From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- sw/source/core/SwNumberTree/SwNodeNum.cxx | 369 + sw/source/core/SwNumberTree/SwNumberTree.cxx | 1182 +++ sw/source/core/access/AccessibilityCheck.cxx | 811 ++ sw/source/core/access/AccessibilityIssue.cxx | 82 + sw/source/core/access/acccell.cxx | 467 + sw/source/core/access/acccell.hxx | 135 + sw/source/core/access/acccontext.cxx | 1526 +++ sw/source/core/access/acccontext.hxx | 360 + sw/source/core/access/accdoc.cxx | 719 ++ sw/source/core/access/accdoc.hxx | 172 + sw/source/core/access/accembedded.cxx | 121 + sw/source/core/access/accembedded.hxx | 72 + sw/source/core/access/accfootnote.cxx | 118 + sw/source/core/access/accfootnote.hxx | 65 + sw/source/core/access/accframe.cxx | 479 + sw/source/core/access/accframe.hxx | 163 + sw/source/core/access/accframebase.cxx | 374 + sw/source/core/access/accframebase.hxx | 65 + sw/source/core/access/accfrmobj.cxx | 408 + sw/source/core/access/accfrmobj.hxx | 89 + sw/source/core/access/accfrmobjmap.cxx | 149 + sw/source/core/access/accfrmobjmap.hxx | 126 + sw/source/core/access/accfrmobjslist.cxx | 163 + sw/source/core/access/accfrmobjslist.hxx | 131 + sw/source/core/access/accgraphic.cxx | 75 + sw/source/core/access/accgraphic.hxx | 57 + sw/source/core/access/accheaderfooter.cxx | 110 + sw/source/core/access/accheaderfooter.hxx | 68 + sw/source/core/access/acchyperlink.cxx | 237 + sw/source/core/access/acchyperlink.hxx | 75 + sw/source/core/access/acchypertextdata.cxx | 47 + sw/source/core/access/acchypertextdata.hxx | 54 + sw/source/core/access/accmap.cxx | 3527 +++++++ sw/source/core/access/accnotextframe.cxx | 316 + sw/source/core/access/accnotextframe.hxx | 125 + sw/source/core/access/accnotexthyperlink.cxx | 234 + sw/source/core/access/accnotexthyperlink.hxx | 65 + sw/source/core/access/accpage.cxx | 160 + sw/source/core/access/accpage.hxx | 77 + sw/source/core/access/accpara.cxx | 3558 +++++++ sw/source/core/access/accpara.hxx | 399 + sw/source/core/access/accportions.cxx | 761 ++ sw/source/core/access/accportions.hxx | 171 + sw/source/core/access/accpreview.cxx | 75 + sw/source/core/access/accpreview.hxx | 68 + sw/source/core/access/accselectionhelper.cxx | 348 + sw/source/core/access/accselectionhelper.hxx | 74 + sw/source/core/access/acctable.cxx | 1739 ++++ sw/source/core/access/acctable.hxx | 280 + sw/source/core/access/acctextframe.cxx | 320 + sw/source/core/access/acctextframe.hxx | 116 + sw/source/core/access/parachangetrackinginfo.cxx | 204 + sw/source/core/access/parachangetrackinginfo.hxx | 51 + sw/source/core/access/textmarkuphelper.cxx | 225 + sw/source/core/access/textmarkuphelper.hxx | 70 + sw/source/core/attr/calbck.cxx | 381 + sw/source/core/attr/cellatr.cxx | 226 + sw/source/core/attr/fmtfollowtextflow.cxx | 30 + sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx | 174 + sw/source/core/attr/format.cxx | 803 ++ sw/source/core/attr/hints.cxx | 270 + sw/source/core/attr/swatrset.cxx | 481 + sw/source/core/bastyp/SwSmartTagMgr.cxx | 70 + sw/source/core/bastyp/bparr.cxx | 505 + sw/source/core/bastyp/breakit.cxx | 183 + sw/source/core/bastyp/calc.cxx | 1498 +++ sw/source/core/bastyp/checkit.cxx | 33 + sw/source/core/bastyp/index.cxx | 391 + sw/source/core/bastyp/init.cxx | 797 ++ sw/source/core/bastyp/proofreadingiterator.cxx | 68 + sw/source/core/bastyp/swcache.cxx | 512 ++ sw/source/core/bastyp/swrect.cxx | 243 + sw/source/core/bastyp/swregion.cxx | 193 + sw/source/core/bastyp/swtypes.cxx | 72 + sw/source/core/bastyp/tabcol.cxx | 92 + sw/source/core/crsr/BlockCursor.cxx | 32 + sw/source/core/crsr/BlockCursor.hxx | 91 + sw/source/core/crsr/DateFormFieldButton.cxx | 97 + sw/source/core/crsr/DropDownFormFieldButton.cxx | 167 + sw/source/core/crsr/FormFieldButton.cxx | 151 + sw/source/core/crsr/annotationmark.cxx | 97 + sw/source/core/crsr/bookmrk.cxx | 1068 +++ sw/source/core/crsr/callnk.cxx | 247 + sw/source/core/crsr/callnk.hxx | 48 + sw/source/core/crsr/crbm.cxx | 318 + sw/source/core/crsr/crossrefbookmark.cxx | 93 + sw/source/core/crsr/crsrsh.cxx | 3814 ++++++++ sw/source/core/crsr/crstrvl.cxx | 2592 ++++++ sw/source/core/crsr/crstrvl1.cxx | 126 + sw/source/core/crsr/findattr.cxx | 1444 +++ sw/source/core/crsr/findcoll.cxx | 113 + sw/source/core/crsr/findfmt.cxx | 97 + sw/source/core/crsr/findtxt.cxx | 1183 +++ sw/source/core/crsr/overlayrangesoutline.cxx | 104 + sw/source/core/crsr/overlayrangesoutline.hxx | 60 + sw/source/core/crsr/pam.cxx | 1171 +++ sw/source/core/crsr/paminit.cxx | 61 + sw/source/core/crsr/swcrsr.cxx | 2569 ++++++ sw/source/core/crsr/trvlcol.cxx | 103 + sw/source/core/crsr/trvlfnfl.cxx | 378 + sw/source/core/crsr/trvlreg.cxx | 280 + sw/source/core/crsr/trvltbl.cxx | 927 ++ sw/source/core/crsr/viscrs.cxx | 941 ++ sw/source/core/doc/CntntIdxStore.cxx | 477 + .../core/doc/DocumentChartDataProviderManager.cxx | 106 + .../core/doc/DocumentContentOperationsManager.cxx | 5138 +++++++++++ sw/source/core/doc/DocumentDeviceManager.cxx | 376 + sw/source/core/doc/DocumentDrawModelManager.cxx | 356 + sw/source/core/doc/DocumentExternalDataManager.cxx | 34 + sw/source/core/doc/DocumentFieldsManager.cxx | 1860 ++++ sw/source/core/doc/DocumentLayoutManager.cxx | 517 ++ .../doc/DocumentLinksAdministrationManager.cxx | 583 ++ sw/source/core/doc/DocumentListItemsManager.cxx | 105 + sw/source/core/doc/DocumentListsManager.cxx | 214 + sw/source/core/doc/DocumentOutlineNodesManager.cxx | 131 + sw/source/core/doc/DocumentRedlineManager.cxx | 3233 +++++++ sw/source/core/doc/DocumentSettingManager.cxx | 964 ++ sw/source/core/doc/DocumentStateManager.cxx | 118 + sw/source/core/doc/DocumentStatisticsManager.cxx | 218 + sw/source/core/doc/DocumentStylePoolManager.cxx | 2655 ++++++ sw/source/core/doc/DocumentTimerManager.cxx | 236 + sw/source/core/doc/SwDocIdle.cxx | 62 + sw/source/core/doc/SwStyleNameMapper.cxx | 772 ++ sw/source/core/doc/acmplwrd.cxx | 402 + sw/source/core/doc/dbgoutsw.cxx | 846 ++ sw/source/core/doc/doc.cxx | 1830 ++++ sw/source/core/doc/docbasic.cxx | 234 + sw/source/core/doc/docbm.cxx | 1862 ++++ sw/source/core/doc/docchart.cxx | 179 + sw/source/core/doc/doccomp.cxx | 2698 ++++++ sw/source/core/doc/doccorr.cxx | 364 + sw/source/core/doc/docdesc.cxx | 922 ++ sw/source/core/doc/docdraw.cxx | 634 ++ sw/source/core/doc/docedt.cxx | 886 ++ sw/source/core/doc/docfld.cxx | 1168 +++ sw/source/core/doc/docfly.cxx | 1159 +++ sw/source/core/doc/docfmt.cxx | 2151 +++++ sw/source/core/doc/docftn.cxx | 547 ++ sw/source/core/doc/docglbl.cxx | 523 ++ sw/source/core/doc/docglos.cxx | 212 + sw/source/core/doc/doclay.cxx | 1682 ++++ sw/source/core/doc/docnew.cxx | 1282 +++ sw/source/core/doc/docnum.cxx | 2622 ++++++ sw/source/core/doc/docredln.cxx | 1906 ++++ sw/source/core/doc/docruby.cxx | 327 + sw/source/core/doc/docsort.cxx | 937 ++ sw/source/core/doc/docstat.cxx | 51 + sw/source/core/doc/doctxm.cxx | 2076 +++++ sw/source/core/doc/docxforms.cxx | 129 + sw/source/core/doc/extinput.cxx | 304 + sw/source/core/doc/fmtcol.cxx | 627 ++ sw/source/core/doc/ftnidx.cxx | 520 ++ sw/source/core/doc/gctable.cxx | 450 + sw/source/core/doc/htmltbl.cxx | 1768 ++++ sw/source/core/doc/lineinfo.cxx | 129 + sw/source/core/doc/list.cxx | 274 + sw/source/core/doc/notxtfrm.cxx | 1549 ++++ sw/source/core/doc/number.cxx | 1475 +++ sw/source/core/doc/poolfmt.cxx | 301 + sw/source/core/doc/rdfhelper.cxx | 264 + sw/source/core/doc/sortopt.cxx | 64 + sw/source/core/doc/swserv.cxx | 320 + sw/source/core/doc/swstylemanager.cxx | 158 + sw/source/core/doc/swstylemanager.hxx | 32 + sw/source/core/doc/tblafmt.cxx | 1251 +++ sw/source/core/doc/tblcpy.cxx | 1042 +++ sw/source/core/doc/tblrwcl.cxx | 3403 +++++++ sw/source/core/doc/textboxhelper.cxx | 774 ++ sw/source/core/doc/visiturl.cxx | 125 + sw/source/core/docnode/cancellablejob.cxx | 33 + sw/source/core/docnode/cancellablejob.hxx | 47 + sw/source/core/docnode/finalthreadmanager.cxx | 429 + sw/source/core/docnode/ndcopy.cxx | 361 + sw/source/core/docnode/ndnotxt.cxx | 293 + sw/source/core/docnode/ndnum.cxx | 96 + sw/source/core/docnode/ndsect.cxx | 1422 +++ sw/source/core/docnode/ndsect.hxx | 32 + sw/source/core/docnode/ndtbl.cxx | 4645 ++++++++++ sw/source/core/docnode/ndtbl1.cxx | 1631 ++++ sw/source/core/docnode/node.cxx | 2160 +++++ sw/source/core/docnode/node2lay.cxx | 467 + sw/source/core/docnode/nodes.cxx | 2335 +++++ sw/source/core/docnode/observablethread.cxx | 64 + sw/source/core/docnode/pausethreadstarting.cxx | 48 + .../core/docnode/retrievedinputstreamdata.cxx | 153 + sw/source/core/docnode/retrieveinputstream.cxx | 83 + .../core/docnode/retrieveinputstreamconsumer.cxx | 63 + sw/source/core/docnode/section.cxx | 1594 ++++ sw/source/core/docnode/swbaslnk.cxx | 359 + sw/source/core/docnode/swthreadjoiner.cxx | 54 + sw/source/core/docnode/swthreadmanager.cxx | 83 + sw/source/core/docnode/threadlistener.cxx | 48 + sw/source/core/docnode/threadmanager.cxx | 253 + sw/source/core/docnode/threadmanager.hxx | 148 + sw/source/core/draw/dcontact.cxx | 2530 +++++ sw/source/core/draw/dflyobj.cxx | 1295 +++ sw/source/core/draw/dobjfac.cxx | 38 + sw/source/core/draw/dpage.cxx | 254 + sw/source/core/draw/drawdoc.cxx | 138 + sw/source/core/draw/dview.cxx | 998 ++ sw/source/core/edit/acorrect.cxx | 693 ++ sw/source/core/edit/autofmt.cxx | 2834 ++++++ sw/source/core/edit/edatmisc.cxx | 190 + sw/source/core/edit/edattr.cxx | 853 ++ sw/source/core/edit/eddel.cxx | 363 + sw/source/core/edit/edfcol.cxx | 2287 +++++ sw/source/core/edit/edfld.cxx | 417 + sw/source/core/edit/edfldexp.cxx | 58 + sw/source/core/edit/edfmt.cxx | 159 + sw/source/core/edit/edglbldc.cxx | 384 + sw/source/core/edit/edglss.cxx | 337 + sw/source/core/edit/editsh.cxx | 1056 +++ sw/source/core/edit/edlingu.cxx | 1712 ++++ sw/source/core/edit/ednumber.cxx | 912 ++ sw/source/core/edit/edredln.cxx | 153 + sw/source/core/edit/edsect.cxx | 421 + sw/source/core/edit/edtab.cxx | 531 ++ sw/source/core/edit/edtox.cxx | 388 + sw/source/core/edit/edundo.cxx | 246 + sw/source/core/edit/edws.cxx | 321 + sw/source/core/fields/authfld.cxx | 668 ++ sw/source/core/fields/cellfml.cxx | 1218 +++ sw/source/core/fields/chpfld.cxx | 301 + sw/source/core/fields/dbfld.cxx | 870 ++ sw/source/core/fields/ddefld.cxx | 377 + sw/source/core/fields/ddetbl.cxx | 207 + sw/source/core/fields/docufld.cxx | 2646 ++++++ sw/source/core/fields/expfld.cxx | 1432 +++ sw/source/core/fields/fldbas.cxx | 824 ++ sw/source/core/fields/flddat.cxx | 232 + sw/source/core/fields/flddropdown.cxx | 217 + sw/source/core/fields/fldlst.cxx | 153 + sw/source/core/fields/macrofld.cxx | 222 + sw/source/core/fields/postithelper.cxx | 253 + sw/source/core/fields/reffld.cxx | 1462 +++ sw/source/core/fields/scrptfld.cxx | 119 + sw/source/core/fields/tblcalc.cxx | 212 + sw/source/core/fields/textapi.cxx | 193 + sw/source/core/fields/usrfld.cxx | 368 + sw/source/core/frmedt/fecopy.cxx | 1626 ++++ sw/source/core/frmedt/fedesc.cxx | 244 + sw/source/core/frmedt/fefly1.cxx | 2110 +++++ sw/source/core/frmedt/feflyole.cxx | 127 + sw/source/core/frmedt/feshview.cxx | 3262 +++++++ sw/source/core/frmedt/fetab.cxx | 2324 +++++ sw/source/core/frmedt/fews.cxx | 1328 +++ sw/source/core/frmedt/tblsel.cxx | 2601 ++++++ sw/source/core/graphic/grfatr.cxx | 338 + sw/source/core/graphic/ndgrf.cxx | 874 ++ sw/source/core/inc/AccessibilityCheck.hxx | 38 + sw/source/core/inc/AccessibilityIssue.hxx | 67 + sw/source/core/inc/DateFormFieldButton.hxx | 45 + .../core/inc/DocumentChartDataProviderManager.hxx | 66 + .../core/inc/DocumentContentOperationsManager.hxx | 192 + sw/source/core/inc/DocumentDeviceManager.hxx | 84 + sw/source/core/inc/DocumentDrawModelManager.hxx | 90 + sw/source/core/inc/DocumentExternalDataManager.hxx | 46 + sw/source/core/inc/DocumentFieldsManager.hxx | 111 + sw/source/core/inc/DocumentLayoutManager.hxx | 76 + .../inc/DocumentLinksAdministrationManager.hxx | 84 + sw/source/core/inc/DocumentListItemsManager.hxx | 71 + sw/source/core/inc/DocumentListsManager.hxx | 73 + sw/source/core/inc/DocumentOutlineNodesManager.hxx | 65 + sw/source/core/inc/DocumentRedlineManager.hxx | 157 + sw/source/core/inc/DocumentSettingManager.hxx | 205 + sw/source/core/inc/DocumentStateManager.hxx | 65 + sw/source/core/inc/DocumentStatisticsManager.hxx | 72 + sw/source/core/inc/DocumentStylePoolManager.hxx | 61 + sw/source/core/inc/DocumentTimerManager.hxx | 89 + sw/source/core/inc/DropDownFormFieldButton.hxx | 40 + sw/source/core/inc/FormFieldButton.hxx | 56 + sw/source/core/inc/GetMetricVal.hxx | 42 + sw/source/core/inc/MarkManager.hxx | 150 + sw/source/core/inc/SwGrammarMarkUp.hxx | 68 + sw/source/core/inc/SwPortionHandler.hxx | 105 + sw/source/core/inc/SwUndoFmt.hxx | 256 + sw/source/core/inc/SwUndoPageDesc.hxx | 86 + sw/source/core/inc/SwUndoTOXChange.hxx | 48 + sw/source/core/inc/SwXMLBlockExport.hxx | 66 + sw/source/core/inc/SwXMLBlockImport.hxx | 131 + sw/source/core/inc/SwXMLTextBlocks.hxx | 96 + sw/source/core/inc/SwXTextDefaults.hxx | 66 + sw/source/core/inc/TextFrameIndex.hxx | 26 + sw/source/core/inc/UndoAttribute.hxx | 257 + sw/source/core/inc/UndoBookmark.hxx | 157 + sw/source/core/inc/UndoCore.hxx | 273 + sw/source/core/inc/UndoDelete.hxx | 106 + sw/source/core/inc/UndoDraw.hxx | 134 + sw/source/core/inc/UndoInsert.hxx | 223 + sw/source/core/inc/UndoManager.hxx | 131 + sw/source/core/inc/UndoNumbering.hxx | 142 + sw/source/core/inc/UndoOverwrite.hxx | 93 + sw/source/core/inc/UndoRedline.hxx | 137 + sw/source/core/inc/UndoSection.hxx | 97 + sw/source/core/inc/UndoSort.hxx | 84 + sw/source/core/inc/UndoSplitMove.hxx | 85 + sw/source/core/inc/UndoTable.hxx | 418 + sw/source/core/inc/acorrect.hxx | 119 + sw/source/core/inc/anchoredobjectposition.hxx | 447 + sw/source/core/inc/annotationmark.hxx | 48 + .../core/inc/ascharanchoredobjectposition.hxx | 160 + sw/source/core/inc/attrhint.hxx | 31 + sw/source/core/inc/bodyfrm.hxx | 41 + sw/source/core/inc/bookmrk.hxx | 356 + sw/source/core/inc/cellfrm.hxx | 70 + sw/source/core/inc/cntfrm.hxx | 125 + sw/source/core/inc/colfrm.hxx | 40 + sw/source/core/inc/crossrefbookmark.hxx | 86 + sw/source/core/inc/dbg_lay.hxx | 105 + sw/source/core/inc/dflyobj.hxx | 148 + sw/source/core/inc/dialoghelp.hxx | 30 + sw/source/core/inc/docedt.hxx | 31 + sw/source/core/inc/docfld.hxx | 179 + sw/source/core/inc/docredln.hxx | 35 + sw/source/core/inc/docsort.hxx | 160 + sw/source/core/inc/doctxm.hxx | 108 + sw/source/core/inc/drawfont.hxx | 597 ++ sw/source/core/inc/dumpfilter.hxx | 59 + sw/source/core/inc/dview.hxx | 126 + sw/source/core/inc/environmentofanchoredobject.hxx | 98 + sw/source/core/inc/fefly.hxx | 31 + sw/source/core/inc/fieldhint.hxx | 43 + sw/source/core/inc/flowfrm.hxx | 279 + sw/source/core/inc/flyfrm.hxx | 284 + sw/source/core/inc/flyfrms.hxx | 241 + sw/source/core/inc/fntcache.hxx | 164 + sw/source/core/inc/fntcap.hxx | 37 + sw/source/core/inc/frame.hxx | 1417 +++ sw/source/core/inc/frminf.hxx | 75 + sw/source/core/inc/frmtool.hxx | 610 ++ sw/source/core/inc/ftnboss.hxx | 130 + sw/source/core/inc/ftnfrm.hxx | 165 + sw/source/core/inc/hffrm.hxx | 58 + sw/source/core/inc/ifinishedthreadlistener.hxx | 46 + sw/source/core/inc/layact.hxx | 213 + sw/source/core/inc/laycache.hxx | 69 + sw/source/core/inc/layfrm.hxx | 226 + sw/source/core/inc/layouter.hxx | 146 + sw/source/core/inc/movedfwdfrmsbyobjpos.hxx | 58 + sw/source/core/inc/mvsave.hxx | 199 + sw/source/core/inc/node2lay.hxx | 81 + sw/source/core/inc/noteurl.hxx | 32 + sw/source/core/inc/notxtfrm.hxx | 103 + sw/source/core/inc/objectformatter.hxx | 174 + sw/source/core/inc/observablethread.hxx | 87 + sw/source/core/inc/pagedeschint.hxx | 40 + sw/source/core/inc/pagefrm.hxx | 443 + sw/source/core/inc/paintfrm.hxx | 36 + sw/source/core/inc/pamtyp.hxx | 115 + sw/source/core/inc/prevwpage.hxx | 53 + sw/source/core/inc/ptqueue.hxx | 57 + sw/source/core/inc/retrievedinputstreamdata.hxx | 89 + sw/source/core/inc/retrieveinputstream.hxx | 57 + sw/source/core/inc/retrieveinputstreamconsumer.hxx | 59 + sw/source/core/inc/rolbck.hxx | 435 + sw/source/core/inc/rootfrm.hxx | 462 + sw/source/core/inc/rowfrm.hxx | 127 + sw/source/core/inc/scriptinfo.hxx | 399 + sw/source/core/inc/sectfrm.hxx | 175 + sw/source/core/inc/sortedobjs.hxx | 100 + sw/source/core/inc/swblocks.hxx | 128 + sw/source/core/inc/swcache.hxx | 265 + sw/source/core/inc/swfntcch.hxx | 76 + sw/source/core/inc/swfont.hxx | 1000 ++ sw/source/core/inc/swselectionlist.hxx | 91 + sw/source/core/inc/swthreadjoiner.hxx | 36 + sw/source/core/inc/swthreadmanager.hxx | 78 + sw/source/core/inc/tabfrm.hxx | 254 + sw/source/core/inc/tblrwcl.hxx | 198 + sw/source/core/inc/textapi.hxx | 65 + sw/source/core/inc/threadlistener.hxx | 55 + .../core/inc/tocntntanchoredobjectposition.hxx | 93 + .../core/inc/tolayoutanchoredobjectposition.hxx | 52 + sw/source/core/inc/txmsrt.hxx | 301 + sw/source/core/inc/txtfly.hxx | 378 + sw/source/core/inc/txtfrm.hxx | 1024 +++ sw/source/core/inc/txttypes.hxx | 93 + sw/source/core/inc/undoflystrattr.hxx | 49 + sw/source/core/inc/unobookmark.hxx | 210 + sw/source/core/inc/unoevent.hxx | 92 + sw/source/core/inc/unofield.hxx | 240 + sw/source/core/inc/unoflatpara.hxx | 146 + sw/source/core/inc/unofldmid.h | 52 + sw/source/core/inc/unofootnote.hxx | 149 + sw/source/core/inc/unofreg.hxx | 38 + sw/source/core/inc/unoidx.hxx | 229 + sw/source/core/inc/unometa.hxx | 270 + sw/source/core/inc/unoparaframeenum.hxx | 77 + sw/source/core/inc/unoport.hxx | 307 + sw/source/core/inc/unorefmark.hxx | 116 + sw/source/core/inc/unosection.hxx | 163 + sw/source/core/inc/unotextmarkup.hxx | 103 + sw/source/core/inc/viewimp.hxx | 307 + sw/source/core/inc/visiturl.hxx | 39 + sw/source/core/inc/wrong.hxx | 417 + sw/source/core/layout/anchoreddrawobject.cxx | 918 ++ sw/source/core/layout/anchoredobject.cxx | 900 ++ sw/source/core/layout/atrfrm.cxx | 3656 ++++++++ sw/source/core/layout/calcmove.cxx | 2232 +++++ sw/source/core/layout/colfrm.cxx | 445 + sw/source/core/layout/dbg_lay.cxx | 923 ++ sw/source/core/layout/dumpfilter.cxx | 163 + sw/source/core/layout/findfrm.cxx | 1827 ++++ sw/source/core/layout/flowfrm.cxx | 2665 ++++++ sw/source/core/layout/fly.cxx | 2917 ++++++ sw/source/core/layout/flycnt.cxx | 1463 +++ sw/source/core/layout/flyincnt.cxx | 278 + sw/source/core/layout/flylay.cxx | 1489 +++ sw/source/core/layout/flypos.cxx | 63 + sw/source/core/layout/frmtool.cxx | 3897 ++++++++ sw/source/core/layout/ftnfrm.cxx | 2968 ++++++ sw/source/core/layout/hffrm.cxx | 767 ++ sw/source/core/layout/layact.cxx | 2344 +++++ sw/source/core/layout/laycache.cxx | 1205 +++ sw/source/core/layout/layhelp.hxx | 217 + sw/source/core/layout/layouter.cxx | 482 + sw/source/core/layout/legacyitem.cxx | 79 + sw/source/core/layout/movedfwdfrmsbyobjpos.cxx | 90 + sw/source/core/layout/newfrm.cxx | 613 ++ sw/source/core/layout/objectformatter.cxx | 478 + sw/source/core/layout/objectformatterlayfrm.cxx | 187 + sw/source/core/layout/objectformatterlayfrm.hxx | 70 + sw/source/core/layout/objectformattertxtfrm.cxx | 807 ++ sw/source/core/layout/objectformattertxtfrm.hxx | 185 + sw/source/core/layout/objstmpconsiderwrapinfl.cxx | 60 + sw/source/core/layout/objstmpconsiderwrapinfl.hxx | 42 + sw/source/core/layout/pagechg.cxx | 2546 +++++ sw/source/core/layout/pagedesc.cxx | 633 ++ sw/source/core/layout/paintfrm.cxx | 7495 +++++++++++++++ sw/source/core/layout/sectfrm.cxx | 2916 ++++++ sw/source/core/layout/softpagebreak.cxx | 154 + sw/source/core/layout/sortedobjs.cxx | 292 + sw/source/core/layout/ssfrm.cxx | 739 ++ sw/source/core/layout/swselectionlist.cxx | 83 + sw/source/core/layout/tabfrm.cxx | 5851 ++++++++++++ sw/source/core/layout/trvlfrm.cxx | 2638 ++++++ sw/source/core/layout/unusedf.cxx | 78 + sw/source/core/layout/virtoutp.cxx | 188 + sw/source/core/layout/virtoutp.hxx | 61 + sw/source/core/layout/wsfrm.cxx | 4602 ++++++++++ .../objectpositioning/anchoredobjectposition.cxx | 1132 +++ .../ascharanchoredobjectposition.cxx | 395 + .../environmentofanchoredobject.cxx | 98 + .../tocntntanchoredobjectposition.cxx | 1202 +++ .../tolayoutanchoredobjectposition.cxx | 226 + sw/source/core/ole/ndole.cxx | 1246 +++ sw/source/core/para/paratr.cxx | 236 + sw/source/core/sw3io/swacorr.cxx | 99 + sw/source/core/swg/BlockListTokens.txt | 7 + sw/source/core/swg/SwXMLBlockExport.cxx | 136 + sw/source/core/swg/SwXMLBlockImport.cxx | 359 + sw/source/core/swg/SwXMLSectionList.cxx | 137 + sw/source/core/swg/SwXMLTextBlocks.cxx | 517 ++ sw/source/core/swg/SwXMLTextBlocks1.cxx | 566 ++ sw/source/core/swg/TextBlockTokens.txt | 5 + sw/source/core/swg/swblocks.cxx | 569 ++ sw/source/core/table/swnewtable.cxx | 2199 +++++ sw/source/core/table/swtable.cxx | 2771 ++++++ sw/source/core/text/EnhancedPDFExportHelper.cxx | 2280 +++++ sw/source/core/text/SwGrammarMarkUp.cxx | 145 + sw/source/core/text/atrhndl.hxx | 121 + sw/source/core/text/atrstck.cxx | 846 ++ sw/source/core/text/frmcrsr.cxx | 1685 ++++ sw/source/core/text/frmform.cxx | 2061 +++++ sw/source/core/text/frminf.cxx | 293 + sw/source/core/text/frmpaint.cxx | 714 ++ sw/source/core/text/guess.cxx | 591 ++ sw/source/core/text/guess.hxx | 64 + sw/source/core/text/inftxt.cxx | 1991 ++++ sw/source/core/text/inftxt.hxx | 788 ++ sw/source/core/text/itradj.cxx | 841 ++ sw/source/core/text/itratr.cxx | 1481 +++ sw/source/core/text/itratr.hxx | 114 + sw/source/core/text/itrcrsr.cxx | 1913 ++++ sw/source/core/text/itrform2.cxx | 2889 ++++++ sw/source/core/text/itrform2.hxx | 245 + sw/source/core/text/itrpaint.cxx | 693 ++ sw/source/core/text/itrpaint.hxx | 65 + sw/source/core/text/itrtxt.cxx | 421 + sw/source/core/text/itrtxt.hxx | 340 + sw/source/core/text/noteurl.cxx | 25 + sw/source/core/text/pordrop.hxx | 106 + sw/source/core/text/porexp.cxx | 250 + sw/source/core/text/porexp.hxx | 74 + sw/source/core/text/porfld.cxx | 1350 +++ sw/source/core/text/porfld.hxx | 254 + sw/source/core/text/porfly.cxx | 405 + sw/source/core/text/porfly.hxx | 91 + sw/source/core/text/porftn.hxx | 102 + sw/source/core/text/porglue.cxx | 260 + sw/source/core/text/porglue.hxx | 88 + sw/source/core/text/porhyph.hxx | 84 + sw/source/core/text/porlay.cxx | 2645 ++++++ sw/source/core/text/porlay.hxx | 327 + sw/source/core/text/porlin.cxx | 320 + sw/source/core/text/porlin.hxx | 204 + sw/source/core/text/pormulti.cxx | 2569 ++++++ sw/source/core/text/pormulti.hxx | 256 + sw/source/core/text/porref.cxx | 75 + sw/source/core/text/porref.hxx | 48 + sw/source/core/text/porrst.cxx | 633 ++ sw/source/core/text/porrst.hxx | 174 + sw/source/core/text/portab.hxx | 114 + sw/source/core/text/portox.cxx | 77 + sw/source/core/text/portox.hxx | 49 + sw/source/core/text/portxt.cxx | 845 ++ sw/source/core/text/portxt.hxx | 103 + sw/source/core/text/possiz.hxx | 69 + sw/source/core/text/redlnitr.cxx | 926 ++ sw/source/core/text/redlnitr.hxx | 136 + sw/source/core/text/txtcache.cxx | 190 + sw/source/core/text/txtcache.hxx | 65 + sw/source/core/text/txtdrop.cxx | 1079 +++ sw/source/core/text/txtfld.cxx | 737 ++ sw/source/core/text/txtfly.cxx | 1407 +++ sw/source/core/text/txtfrm.cxx | 4024 ++++++++ sw/source/core/text/txtftn.cxx | 1553 ++++ sw/source/core/text/txthyph.cxx | 572 ++ sw/source/core/text/txtinit.cxx | 60 + sw/source/core/text/txtpaint.cxx | 113 + sw/source/core/text/txtpaint.hxx | 127 + sw/source/core/text/txttab.cxx | 600 ++ sw/source/core/text/widorp.cxx | 544 ++ sw/source/core/text/widorp.hxx | 84 + sw/source/core/text/wrong.cxx | 937 ++ sw/source/core/text/xmldump.cxx | 567 ++ sw/source/core/tox/ToxLinkProcessor.cxx | 75 + sw/source/core/tox/ToxTabStopTokenHandler.cxx | 128 + sw/source/core/tox/ToxTextGenerator.cxx | 440 + sw/source/core/tox/ToxWhitespaceStripper.cxx | 63 + sw/source/core/tox/tox.cxx | 940 ++ sw/source/core/tox/toxhlp.cxx | 119 + sw/source/core/tox/txmsrt.cxx | 864 ++ sw/source/core/txtnode/SwGrammarContact.cxx | 190 + sw/source/core/txtnode/atrfld.cxx | 732 ++ sw/source/core/txtnode/atrflyin.cxx | 297 + sw/source/core/txtnode/atrftn.cxx | 576 ++ sw/source/core/txtnode/atrref.cxx | 109 + sw/source/core/txtnode/atrtox.cxx | 90 + sw/source/core/txtnode/chrfmt.cxx | 42 + sw/source/core/txtnode/fmtatr2.cxx | 843 ++ sw/source/core/txtnode/fntcache.cxx | 2712 ++++++ sw/source/core/txtnode/fntcap.cxx | 778 ++ sw/source/core/txtnode/modeltoviewhelper.cxx | 337 + sw/source/core/txtnode/ndhints.cxx | 475 + sw/source/core/txtnode/ndtxt.cxx | 5326 +++++++++++ sw/source/core/txtnode/swfntcch.cxx | 75 + sw/source/core/txtnode/swfont.cxx | 1541 ++++ sw/source/core/txtnode/thints.cxx | 3512 +++++++ sw/source/core/txtnode/txatbase.cxx | 163 + sw/source/core/txtnode/txatritr.cxx | 218 + sw/source/core/txtnode/txtatr2.cxx | 314 + sw/source/core/txtnode/txtedt.cxx | 2288 +++++ sw/source/core/undo/SwRewriter.cxx | 71 + sw/source/core/undo/SwUndoField.cxx | 150 + sw/source/core/undo/SwUndoFmt.cxx | 466 + sw/source/core/undo/SwUndoPageDesc.cxx | 341 + sw/source/core/undo/SwUndoTOXChange.cxx | 85 + sw/source/core/undo/docundo.cxx | 735 ++ sw/source/core/undo/rolbck.cxx | 1485 +++ sw/source/core/undo/unattr.cxx | 1055 +++ sw/source/core/undo/unbkmk.cxx | 218 + sw/source/core/undo/undel.cxx | 1317 +++ sw/source/core/undo/undobj.cxx | 1682 ++++ sw/source/core/undo/undobj1.cxx | 714 ++ sw/source/core/undo/undoflystrattr.cxx | 90 + sw/source/core/undo/undraw.cxx | 559 ++ sw/source/core/undo/unfmco.cxx | 87 + sw/source/core/undo/unins.cxx | 1045 +++ sw/source/core/undo/unmove.cxx | 308 + sw/source/core/undo/unnum.cxx | 395 + sw/source/core/undo/unoutl.cxx | 49 + sw/source/core/undo/unovwr.cxx | 474 + sw/source/core/undo/unredln.cxx | 527 ++ sw/source/core/undo/unsect.cxx | 601 ++ sw/source/core/undo/unsort.cxx | 253 + sw/source/core/undo/unspnd.cxx | 197 + sw/source/core/undo/untbl.cxx | 3155 +++++++ sw/source/core/undo/untblk.cxx | 451 + sw/source/core/unocore/SwXTextDefaults.cxx | 235 + sw/source/core/unocore/TextCursorHelper.cxx | 46 + sw/source/core/unocore/XMLRangeHelper.cxx | 387 + sw/source/core/unocore/XMLRangeHelper.hxx | 68 + sw/source/core/unocore/swunohelper.cxx | 336 + sw/source/core/unocore/unobkm.cxx | 734 ++ sw/source/core/unocore/unochart.cxx | 2717 ++++++ sw/source/core/unocore/unocoll.cxx | 1941 ++++ sw/source/core/unocore/unocrsr.cxx | 214 + sw/source/core/unocore/unocrsrhelper.cxx | 1507 +++ sw/source/core/unocore/unodraw.cxx | 2854 ++++++ sw/source/core/unocore/unoevent.cxx | 233 + sw/source/core/unocore/unofield.cxx | 3034 ++++++ sw/source/core/unocore/unoflatpara.cxx | 593 ++ sw/source/core/unocore/unoframe.cxx | 3656 ++++++++ sw/source/core/unocore/unoftn.cxx | 575 ++ sw/source/core/unocore/unoidx.cxx | 3118 +++++++ sw/source/core/unocore/unomap.cxx | 1569 ++++ sw/source/core/unocore/unomap1.cxx | 1665 ++++ sw/source/core/unocore/unomapproperties.hxx | 529 ++ sw/source/core/unocore/unoobj.cxx | 2948 ++++++ sw/source/core/unocore/unoobj2.cxx | 1692 ++++ sw/source/core/unocore/unoparagraph.cxx | 1417 +++ sw/source/core/unocore/unoport.cxx | 825 ++ sw/source/core/unocore/unoportenum.cxx | 1489 +++ sw/source/core/unocore/unoredline.cxx | 587 ++ sw/source/core/unocore/unoredlines.cxx | 168 + sw/source/core/unocore/unorefmk.cxx | 1522 +++ sw/source/core/unocore/unosect.cxx | 1735 ++++ sw/source/core/unocore/unosett.cxx | 2446 +++++ sw/source/core/unocore/unosrch.cxx | 691 ++ sw/source/core/unocore/unostyle.cxx | 5596 +++++++++++ sw/source/core/unocore/unotbl.cxx | 4194 +++++++++ sw/source/core/unocore/unotext.cxx | 2717 ++++++ sw/source/core/unocore/unotextmarkup.cxx | 531 ++ sw/source/core/view/dialoghelp.cxx | 48 + sw/source/core/view/pagepreviewlayout.cxx | 1461 +++ sw/source/core/view/printdata.cxx | 459 + sw/source/core/view/vdraw.cxx | 279 + sw/source/core/view/viewimp.cxx | 457 + sw/source/core/view/viewpg.cxx | 209 + sw/source/core/view/viewsh.cxx | 2619 ++++++ sw/source/core/view/vnew.cxx | 387 + sw/source/core/view/vprint.cxx | 688 ++ sw/source/core/view/vprint.hxx | 33 + sw/source/filter/ascii/ascatr.cxx | 391 + sw/source/filter/ascii/parasc.cxx | 503 + sw/source/filter/ascii/wrtasc.cxx | 211 + sw/source/filter/ascii/wrtasc.hxx | 44 + sw/source/filter/basflt/docfact.cxx | 44 + sw/source/filter/basflt/fltini.cxx | 726 ++ sw/source/filter/basflt/fltshell.cxx | 1131 +++ sw/source/filter/basflt/iodetect.cxx | 379 + sw/source/filter/basflt/shellio.cxx | 927 ++ sw/source/filter/docx/swdocxreader.cxx | 254 + sw/source/filter/docx/swdocxreader.hxx | 42 + sw/source/filter/html/SwAppletImpl.cxx | 193 + sw/source/filter/html/css1atr.cxx | 3754 ++++++++ sw/source/filter/html/css1atr.hxx | 28 + sw/source/filter/html/css1kywd.cxx | 244 + sw/source/filter/html/css1kywd.hxx | 249 + sw/source/filter/html/htmlatr.cxx | 3364 +++++++ sw/source/filter/html/htmlatr.hxx | 21 + sw/source/filter/html/htmlbas.cxx | 331 + sw/source/filter/html/htmlcss1.cxx | 2332 +++++ sw/source/filter/html/htmlctxt.cxx | 775 ++ sw/source/filter/html/htmldrawreader.cxx | 577 ++ sw/source/filter/html/htmldrawwriter.cxx | 287 + sw/source/filter/html/htmlfld.cxx | 649 ++ sw/source/filter/html/htmlfld.hxx | 83 + sw/source/filter/html/htmlfldw.cxx | 582 ++ sw/source/filter/html/htmlfly.cxx | 84 + sw/source/filter/html/htmlfly.hxx | 131 + sw/source/filter/html/htmlflyt.cxx | 505 + sw/source/filter/html/htmlflywriter.cxx | 2133 +++++ sw/source/filter/html/htmlform.cxx | 2483 +++++ sw/source/filter/html/htmlform.hxx | 32 + sw/source/filter/html/htmlforw.cxx | 1369 +++ sw/source/filter/html/htmlftn.cxx | 565 ++ sw/source/filter/html/htmlgrin.cxx | 1533 ++++ sw/source/filter/html/htmlnum.cxx | 48 + sw/source/filter/html/htmlnum.hxx | 125 + sw/source/filter/html/htmlnumreader.cxx | 622 ++ sw/source/filter/html/htmlnumwriter.cxx | 324 + sw/source/filter/html/htmlplug.cxx | 1676 ++++ sw/source/filter/html/htmlreqifreader.cxx | 499 + sw/source/filter/html/htmlreqifreader.hxx | 39 + sw/source/filter/html/htmlsect.cxx | 852 ++ sw/source/filter/html/htmltab.cxx | 5299 +++++++++++ sw/source/filter/html/htmltabw.cxx | 1150 +++ sw/source/filter/html/parcss1.cxx | 1348 +++ sw/source/filter/html/parcss1.hxx | 262 + sw/source/filter/html/svxcss1.cxx | 3183 +++++++ sw/source/filter/html/svxcss1.hxx | 310 + sw/source/filter/html/swcss1.hxx | 206 + sw/source/filter/html/swhtml.cxx | 5646 ++++++++++++ sw/source/filter/html/swhtml.hxx | 1046 +++ sw/source/filter/html/wrthtml.cxx | 1575 ++++ sw/source/filter/html/wrthtml.hxx | 703 ++ sw/source/filter/inc/fltini.hxx | 71 + sw/source/filter/inc/fltshell.hxx | 357 + sw/source/filter/inc/msfilter.hxx | 400 + sw/source/filter/inc/rtf.hxx | 46 + sw/source/filter/inc/wrt_fn.hxx | 56 + sw/source/filter/inc/wrtswtbl.hxx | 309 + sw/source/filter/inc/wwstyles.hxx | 176 + sw/source/filter/rtf/swparrtf.cxx | 204 + sw/source/filter/writer/writer.cxx | 537 ++ sw/source/filter/writer/wrt_fn.cxx | 155 + sw/source/filter/writer/wrtswtbl.cxx | 885 ++ sw/source/filter/ww8/README-rtf.txt | 244 + sw/source/filter/ww8/WW8FFData.cxx | 158 + sw/source/filter/ww8/WW8FFData.hxx | 89 + sw/source/filter/ww8/WW8FibData.cxx | 47 + sw/source/filter/ww8/WW8FibData.hxx | 45 + sw/source/filter/ww8/WW8Sttbf.cxx | 104 + sw/source/filter/ww8/WW8Sttbf.hxx | 124 + sw/source/filter/ww8/WW8TableInfo.cxx | 1462 +++ sw/source/filter/ww8/WW8TableInfo.hxx | 353 + sw/source/filter/ww8/attributeoutputbase.hxx | 698 ++ sw/source/filter/ww8/docxattributeoutput.cxx | 9694 ++++++++++++++++++++ sw/source/filter/ww8/docxattributeoutput.hxx | 1061 +++ sw/source/filter/ww8/docxexport.cxx | 1841 ++++ sw/source/filter/ww8/docxexport.hxx | 306 + sw/source/filter/ww8/docxexportfilter.cxx | 112 + sw/source/filter/ww8/docxexportfilter.hxx | 56 + sw/source/filter/ww8/docxfootnotes.hxx | 91 + sw/source/filter/ww8/docxhelper.hxx | 25 + sw/source/filter/ww8/docxsdrexport.cxx | 1576 ++++ sw/source/filter/ww8/docxsdrexport.hxx | 111 + sw/source/filter/ww8/docxtablestyleexport.cxx | 751 ++ sw/source/filter/ww8/docxtablestyleexport.hxx | 51 + sw/source/filter/ww8/escher.hxx | 181 + sw/source/filter/ww8/fields.cxx | 140 + sw/source/filter/ww8/fields.hxx | 48 + sw/source/filter/ww8/needed_cast.hxx | 54 + sw/source/filter/ww8/rtfattributeoutput.cxx | 4340 +++++++++ sw/source/filter/ww8/rtfattributeoutput.hxx | 670 ++ sw/source/filter/ww8/rtfexport.cxx | 1465 +++ sw/source/filter/ww8/rtfexport.hxx | 235 + sw/source/filter/ww8/rtfexportfilter.cxx | 107 + sw/source/filter/ww8/rtfexportfilter.hxx | 75 + sw/source/filter/ww8/rtfsdrexport.cxx | 762 ++ sw/source/filter/ww8/rtfsdrexport.hxx | 110 + sw/source/filter/ww8/rtfstringbuffer.cxx | 86 + sw/source/filter/ww8/rtfstringbuffer.hxx | 69 + sw/source/filter/ww8/sortedarray.hxx | 106 + sw/source/filter/ww8/sprmids.hxx | 606 ++ sw/source/filter/ww8/styles.cxx | 177 + sw/source/filter/ww8/types.hxx | 45 + sw/source/filter/ww8/writerhelper.cxx | 920 ++ sw/source/filter/ww8/writerhelper.hxx | 768 ++ sw/source/filter/ww8/writerwordglue.cxx | 1081 +++ sw/source/filter/ww8/writerwordglue.hxx | 150 + sw/source/filter/ww8/wrtw8esh.cxx | 3076 +++++++ sw/source/filter/ww8/wrtw8nds.cxx | 3612 ++++++++ sw/source/filter/ww8/wrtw8num.cxx | 670 ++ sw/source/filter/ww8/wrtw8sty.cxx | 2587 ++++++ sw/source/filter/ww8/wrtww8.cxx | 4471 +++++++++ sw/source/filter/ww8/wrtww8.hxx | 1650 ++++ sw/source/filter/ww8/wrtww8gr.cxx | 881 ++ sw/source/filter/ww8/ww8atr.cxx | 5628 ++++++++++++ sw/source/filter/ww8/ww8attributeoutput.hxx | 506 + sw/source/filter/ww8/ww8glsy.cxx | 247 + sw/source/filter/ww8/ww8glsy.hxx | 91 + sw/source/filter/ww8/ww8graf.cxx | 3220 +++++++ sw/source/filter/ww8/ww8graf.hxx | 97 + sw/source/filter/ww8/ww8graf2.cxx | 769 ++ sw/source/filter/ww8/ww8par.cxx | 6697 ++++++++++++++ sw/source/filter/ww8/ww8par.hxx | 1940 ++++ sw/source/filter/ww8/ww8par2.cxx | 4577 +++++++++ sw/source/filter/ww8/ww8par2.hxx | 298 + sw/source/filter/ww8/ww8par3.cxx | 2543 +++++ sw/source/filter/ww8/ww8par4.cxx | 549 ++ sw/source/filter/ww8/ww8par5.cxx | 3740 ++++++++ sw/source/filter/ww8/ww8par6.cxx | 6121 ++++++++++++ sw/source/filter/ww8/ww8scan.cxx | 8480 +++++++++++++++++ sw/source/filter/ww8/ww8scan.hxx | 1874 ++++ sw/source/filter/ww8/ww8struc.hxx | 1149 +++ sw/source/filter/ww8/ww8toolbar.cxx | 1006 ++ sw/source/filter/ww8/ww8toolbar.hxx | 342 + sw/source/filter/xml/XMLRedlineImportHelper.cxx | 744 ++ sw/source/filter/xml/XMLRedlineImportHelper.hxx | 131 + sw/source/filter/xml/swxml.cxx | 1007 ++ sw/source/filter/xml/wrtxml.cxx | 568 ++ sw/source/filter/xml/wrtxml.hxx | 88 + sw/source/filter/xml/xmlbrsh.cxx | 236 + sw/source/filter/xml/xmlbrshe.hxx | 42 + sw/source/filter/xml/xmlbrshi.hxx | 80 + sw/source/filter/xml/xmlexp.cxx | 602 ++ sw/source/filter/xml/xmlexp.hxx | 144 + sw/source/filter/xml/xmlexpit.cxx | 1110 +++ sw/source/filter/xml/xmlexpit.hxx | 100 + sw/source/filter/xml/xmlfmt.cxx | 1135 +++ sw/source/filter/xml/xmlfmte.cxx | 351 + sw/source/filter/xml/xmlfonte.cxx | 92 + sw/source/filter/xml/xmlimp.cxx | 1879 ++++ sw/source/filter/xml/xmlimp.hxx | 195 + sw/source/filter/xml/xmlimpit.cxx | 957 ++ sw/source/filter/xml/xmlimpit.hxx | 87 + sw/source/filter/xml/xmlitem.cxx | 74 + sw/source/filter/xml/xmlitem.hxx | 67 + sw/source/filter/xml/xmliteme.cxx | 221 + sw/source/filter/xml/xmlitemi.cxx | 370 + sw/source/filter/xml/xmlitemm.cxx | 289 + sw/source/filter/xml/xmlithlp.cxx | 333 + sw/source/filter/xml/xmlithlp.hxx | 70 + sw/source/filter/xml/xmlitmap.hxx | 89 + sw/source/filter/xml/xmlitmpr.cxx | 76 + sw/source/filter/xml/xmlmeta.cxx | 171 + sw/source/filter/xml/xmlscript.cxx | 39 + sw/source/filter/xml/xmltble.cxx | 1245 +++ sw/source/filter/xml/xmltbli.cxx | 2875 ++++++ sw/source/filter/xml/xmltbli.hxx | 206 + sw/source/filter/xml/xmltext.cxx | 87 + sw/source/filter/xml/xmltexte.cxx | 583 ++ sw/source/filter/xml/xmltexte.hxx | 68 + sw/source/filter/xml/xmltexti.cxx | 1041 +++ sw/source/filter/xml/xmltexti.hxx | 110 + sw/source/ui/chrdlg/break.cxx | 208 + sw/source/ui/chrdlg/chardlg.cxx | 309 + sw/source/ui/chrdlg/drpcps.cxx | 742 ++ sw/source/ui/chrdlg/numpara.cxx | 340 + sw/source/ui/chrdlg/pardlg.cxx | 246 + sw/source/ui/chrdlg/swuiccoll.cxx | 261 + sw/source/ui/chrdlg/tblnumfm.cxx | 45 + sw/source/ui/config/mailconfigpage.cxx | 484 + sw/source/ui/config/optcomp.cxx | 505 + sw/source/ui/config/optload.cxx | 916 ++ sw/source/ui/config/optpage.cxx | 2200 +++++ sw/source/ui/dbui/addresslistdialog.cxx | 639 ++ sw/source/ui/dbui/addresslistdialog.hxx | 101 + sw/source/ui/dbui/createaddresslistdialog.cxx | 592 ++ sw/source/ui/dbui/createaddresslistdialog.hxx | 109 + sw/source/ui/dbui/customizeaddresslistdialog.cxx | 181 + sw/source/ui/dbui/customizeaddresslistdialog.hxx | 91 + sw/source/ui/dbui/dbinsdlg.cxx | 1751 ++++ sw/source/ui/dbui/dbtablepreviewdialog.cxx | 116 + sw/source/ui/dbui/dbtablepreviewdialog.hxx | 46 + sw/source/ui/dbui/mailmergewizard.cxx | 256 + sw/source/ui/dbui/mmaddressblockpage.cxx | 1568 ++++ sw/source/ui/dbui/mmaddressblockpage.hxx | 284 + sw/source/ui/dbui/mmdocselectpage.cxx | 187 + sw/source/ui/dbui/mmdocselectpage.hxx | 55 + sw/source/ui/dbui/mmgreetingspage.cxx | 421 + sw/source/ui/dbui/mmgreetingspage.hxx | 130 + sw/source/ui/dbui/mmlayoutpage.cxx | 695 ++ sw/source/ui/dbui/mmlayoutpage.hxx | 87 + sw/source/ui/dbui/mmoutputtypepage.cxx | 521 ++ sw/source/ui/dbui/mmoutputtypepage.hxx | 43 + sw/source/ui/dbui/mmresultdialogs.cxx | 1203 +++ sw/source/ui/dbui/selectdbtabledialog.cxx | 150 + sw/source/ui/dbui/selectdbtabledialog.hxx | 44 + sw/source/ui/dialog/abstract.cxx | 43 + sw/source/ui/dialog/addrdlg.cxx | 36 + sw/source/ui/dialog/ascfldlg.cxx | 434 + sw/source/ui/dialog/docstdlg.cxx | 125 + sw/source/ui/dialog/macassgn.cxx | 133 + sw/source/ui/dialog/swdlgfact.cxx | 1258 +++ sw/source/ui/dialog/swdlgfact.hxx | 787 ++ sw/source/ui/dialog/swmessdialog.cxx | 20 + sw/source/ui/dialog/swuiexp.cxx | 41 + sw/source/ui/dialog/uiregionsw.cxx | 2104 +++++ sw/source/ui/dialog/wordcountdialog.cxx | 150 + sw/source/ui/dochdl/selglos.cxx | 42 + sw/source/ui/envelp/envfmt.cxx | 482 + sw/source/ui/envelp/envfmt.hxx | 73 + sw/source/ui/envelp/envlop1.cxx | 340 + sw/source/ui/envelp/envprt.cxx | 190 + sw/source/ui/envelp/envprt.hxx | 72 + sw/source/ui/envelp/label1.cxx | 725 ++ sw/source/ui/envelp/labelexp.cxx | 104 + sw/source/ui/envelp/labfmt.cxx | 609 ++ sw/source/ui/envelp/labfmt.hxx | 142 + sw/source/ui/envelp/labprt.cxx | 156 + sw/source/ui/envelp/labprt.hxx | 63 + sw/source/ui/envelp/mailmrge.cxx | 616 ++ sw/source/ui/envelp/swuilabimp.hxx | 143 + sw/source/ui/fldui/DateFormFieldDialog.cxx | 125 + sw/source/ui/fldui/DropDownFieldDialog.cxx | 142 + sw/source/ui/fldui/DropDownFormFieldDialog.cxx | 199 + sw/source/ui/fldui/changedb.cxx | 255 + sw/source/ui/fldui/flddb.cxx | 531 ++ sw/source/ui/fldui/flddb.hxx | 82 + sw/source/ui/fldui/flddinf.cxx | 461 + sw/source/ui/fldui/flddinf.hxx | 68 + sw/source/ui/fldui/flddok.cxx | 627 ++ sw/source/ui/fldui/flddok.hxx | 72 + sw/source/ui/fldui/fldedt.cxx | 337 + sw/source/ui/fldui/fldfunc.cxx | 607 ++ sw/source/ui/fldui/fldfunc.hxx | 95 + sw/source/ui/fldui/fldpage.cxx | 344 + sw/source/ui/fldui/fldpage.hxx | 89 + sw/source/ui/fldui/fldref.cxx | 1126 +++ sw/source/ui/fldui/fldref.hxx | 93 + sw/source/ui/fldui/fldtdlg.cxx | 292 + sw/source/ui/fldui/fldvar.cxx | 1228 +++ sw/source/ui/fldui/fldvar.hxx | 84 + sw/source/ui/fldui/inpdlg.cxx | 175 + sw/source/ui/fldui/javaedit.cxx | 246 + sw/source/ui/fmtui/tmpdlg.cxx | 522 ++ sw/source/ui/frmdlg/column.cxx | 1387 +++ sw/source/ui/frmdlg/cption.cxx | 532 ++ sw/source/ui/frmdlg/frmdlg.cxx | 194 + sw/source/ui/frmdlg/frmpage.cxx | 3142 +++++++ sw/source/ui/frmdlg/pattern.cxx | 42 + sw/source/ui/frmdlg/uiborder.cxx | 56 + sw/source/ui/frmdlg/wrap.cxx | 627 ++ sw/source/ui/inc/mmresultdialogs.hxx | 189 + sw/source/ui/inc/swuiexp.hxx | 31 + sw/source/ui/index/cntex.cxx | 392 + sw/source/ui/index/cnttab.cxx | 3932 ++++++++ sw/source/ui/index/multmrk.cxx | 66 + sw/source/ui/index/swuiidxmrk.cxx | 1716 ++++ sw/source/ui/misc/bookmark.cxx | 507 + sw/source/ui/misc/docfnote.cxx | 387 + sw/source/ui/misc/glosbib.cxx | 411 + sw/source/ui/misc/glossary.cxx | 971 ++ sw/source/ui/misc/impfnote.hxx | 84 + sw/source/ui/misc/insfnote.cxx | 246 + sw/source/ui/misc/linenum.cxx | 260 + sw/source/ui/misc/num.cxx | 957 ++ sw/source/ui/misc/outline.cxx | 1076 +++ sw/source/ui/misc/pgfnote.cxx | 317 + sw/source/ui/misc/pggrid.cxx | 490 + sw/source/ui/misc/srtdlg.cxx | 428 + sw/source/ui/misc/swmodalredlineacceptdlg.cxx | 79 + sw/source/ui/misc/titlepage.cxx | 308 + sw/source/ui/table/autoformatpreview.cxx | 465 + sw/source/ui/table/colwd.cxx | 75 + sw/source/ui/table/convert.cxx | 202 + sw/source/ui/table/instable.cxx | 275 + sw/source/ui/table/mergetbl.cxx | 43 + sw/source/ui/table/rowht.cxx | 72 + sw/source/ui/table/splittbl.cxx | 48 + sw/source/ui/table/tabledlg.cxx | 1737 ++++ sw/source/ui/table/tautofmt.cxx | 408 + sw/source/ui/uno/swdetect.cxx | 160 + sw/source/ui/uno/swdetect.hxx | 63 + sw/source/ui/utlui/swrenamexnameddlg.cxx | 78 + sw/source/ui/vba/service.cxx | 39 + sw/source/ui/vba/service.hxx | 35 + sw/source/ui/vba/vbaaddin.cxx | 92 + sw/source/ui/vba/vbaaddin.hxx | 52 + sw/source/ui/vba/vbaaddins.cxx | 96 + sw/source/ui/vba/vbaaddins.hxx | 45 + sw/source/ui/vba/vbaapplication.cxx | 776 ++ sw/source/ui/vba/vbaapplication.hxx | 116 + sw/source/ui/vba/vbaautotextentry.cxx | 132 + sw/source/ui/vba/vbaautotextentry.hxx | 69 + sw/source/ui/vba/vbabookmark.cxx | 99 + sw/source/ui/vba/vbabookmark.hxx | 59 + sw/source/ui/vba/vbabookmarks.cxx | 228 + sw/source/ui/vba/vbabookmarks.hxx | 66 + sw/source/ui/vba/vbaborders.cxx | 365 + sw/source/ui/vba/vbaborders.hxx | 54 + sw/source/ui/vba/vbacell.cxx | 101 + sw/source/ui/vba/vbacell.hxx | 58 + sw/source/ui/vba/vbacells.cxx | 213 + sw/source/ui/vba/vbacells.hxx | 63 + sw/source/ui/vba/vbacolumn.cxx | 90 + sw/source/ui/vba/vbacolumn.hxx | 53 + sw/source/ui/vba/vbacolumns.cxx | 147 + sw/source/ui/vba/vbacolumns.hxx | 66 + sw/source/ui/vba/vbadialog.cxx | 73 + sw/source/ui/vba/vbadialog.hxx | 42 + sw/source/ui/vba/vbadialogs.cxx | 51 + sw/source/ui/vba/vbadialogs.hxx | 44 + sw/source/ui/vba/vbadocument.cxx | 723 ++ sw/source/ui/vba/vbadocument.hxx | 120 + sw/source/ui/vba/vbadocumentproperties.cxx | 922 ++ sw/source/ui/vba/vbadocumentproperties.hxx | 58 + sw/source/ui/vba/vbadocuments.cxx | 162 + sw/source/ui/vba/vbadocuments.hxx | 52 + sw/source/ui/vba/vbaeventshelper.cxx | 95 + sw/source/ui/vba/vbaeventshelper.hxx | 42 + sw/source/ui/vba/vbafield.cxx | 536 ++ sw/source/ui/vba/vbafield.hxx | 74 + sw/source/ui/vba/vbafilterpropsfromformat.hxx | 75 + sw/source/ui/vba/vbafind.cxx | 407 + sw/source/ui/vba/vbafind.hxx | 99 + sw/source/ui/vba/vbafont.cxx | 242 + sw/source/ui/vba/vbafont.hxx | 53 + sw/source/ui/vba/vbaframe.cxx | 58 + sw/source/ui/vba/vbaframe.hxx | 48 + sw/source/ui/vba/vbaframes.cxx | 99 + sw/source/ui/vba/vbaframes.hxx | 50 + sw/source/ui/vba/vbaglobals.cxx | 174 + sw/source/ui/vba/vbaglobals.hxx | 68 + sw/source/ui/vba/vbaheaderfooter.cxx | 102 + sw/source/ui/vba/vbaheaderfooter.hxx | 53 + sw/source/ui/vba/vbaheaderfooterhelper.cxx | 176 + sw/source/ui/vba/vbaheaderfooterhelper.hxx | 48 + sw/source/ui/vba/vbaheadersfooters.cxx | 140 + sw/source/ui/vba/vbaheadersfooters.hxx | 51 + sw/source/ui/vba/vbainformationhelper.cxx | 65 + sw/source/ui/vba/vbainformationhelper.hxx | 39 + sw/source/ui/vba/vbalistformat.cxx | 110 + sw/source/ui/vba/vbalistformat.hxx | 49 + sw/source/ui/vba/vbalistgalleries.cxx | 107 + sw/source/ui/vba/vbalistgalleries.hxx | 51 + sw/source/ui/vba/vbalistgallery.cxx | 59 + sw/source/ui/vba/vbalistgallery.hxx | 48 + sw/source/ui/vba/vbalisthelper.cxx | 660 ++ sw/source/ui/vba/vbalisthelper.hxx | 73 + sw/source/ui/vba/vbalistlevel.cxx | 385 + sw/source/ui/vba/vbalistlevel.hxx | 70 + sw/source/ui/vba/vbalistlevels.cxx | 110 + sw/source/ui/vba/vbalistlevels.hxx | 51 + sw/source/ui/vba/vbalisttemplate.cxx | 67 + sw/source/ui/vba/vbalisttemplate.hxx | 51 + sw/source/ui/vba/vbalisttemplates.cxx | 104 + sw/source/ui/vba/vbalisttemplates.hxx | 52 + sw/source/ui/vba/vbamailmerge.cxx | 56 + sw/source/ui/vba/vbamailmerge.hxx | 55 + sw/source/ui/vba/vbaoptions.cxx | 274 + sw/source/ui/vba/vbaoptions.hxx | 78 + sw/source/ui/vba/vbapagesetup.cxx | 260 + sw/source/ui/vba/vbapagesetup.hxx | 62 + sw/source/ui/vba/vbapalette.cxx | 93 + sw/source/ui/vba/vbapalette.hxx | 37 + sw/source/ui/vba/vbapane.cxx | 65 + sw/source/ui/vba/vbapane.hxx | 48 + sw/source/ui/vba/vbapanes.cxx | 118 + sw/source/ui/vba/vbapanes.hxx | 44 + sw/source/ui/vba/vbaparagraph.cxx | 179 + sw/source/ui/vba/vbaparagraph.hxx | 74 + sw/source/ui/vba/vbaparagraphformat.cxx | 565 ++ sw/source/ui/vba/vbaparagraphformat.hxx | 88 + sw/source/ui/vba/vbarange.cxx | 424 + sw/source/ui/vba/vbarange.hxx | 96 + sw/source/ui/vba/vbarangehelper.cxx | 191 + sw/source/ui/vba/vbarangehelper.hxx | 44 + sw/source/ui/vba/vbareplacement.cxx | 66 + sw/source/ui/vba/vbareplacement.hxx | 51 + sw/source/ui/vba/vbarevision.cxx | 95 + sw/source/ui/vba/vbarevision.hxx | 52 + sw/source/ui/vba/vbarevisions.cxx | 184 + sw/source/ui/vba/vbarevisions.hxx | 54 + sw/source/ui/vba/vbarow.cxx | 120 + sw/source/ui/vba/vbarow.hxx | 61 + sw/source/ui/vba/vbarows.cxx | 369 + sw/source/ui/vba/vbarows.hxx | 82 + sw/source/ui/vba/vbasection.cxx | 83 + sw/source/ui/vba/vbasection.hxx | 53 + sw/source/ui/vba/vbasections.cxx | 194 + sw/source/ui/vba/vbasections.hxx | 51 + sw/source/ui/vba/vbaselection.cxx | 1166 +++ sw/source/ui/vba/vbaselection.hxx | 120 + sw/source/ui/vba/vbastyle.cxx | 227 + sw/source/ui/vba/vbastyle.hxx | 78 + sw/source/ui/vba/vbastyles.cxx | 378 + sw/source/ui/vba/vbastyles.hxx | 48 + sw/source/ui/vba/vbasystem.cxx | 273 + sw/source/ui/vba/vbasystem.hxx | 64 + sw/source/ui/vba/vbatable.cxx | 127 + sw/source/ui/vba/vbatable.hxx | 51 + sw/source/ui/vba/vbatablehelper.cxx | 274 + sw/source/ui/vba/vbatablehelper.hxx | 67 + sw/source/ui/vba/vbatableofcontents.cxx | 111 + sw/source/ui/vba/vbatableofcontents.hxx | 61 + sw/source/ui/vba/vbatables.cxx | 236 + sw/source/ui/vba/vbatables.hxx | 48 + sw/source/ui/vba/vbatablesofcontents.cxx | 185 + sw/source/ui/vba/vbatablesofcontents.hxx | 54 + sw/source/ui/vba/vbatabstop.cxx | 49 + sw/source/ui/vba/vbatabstop.hxx | 40 + sw/source/ui/vba/vbatabstops.cxx | 267 + sw/source/ui/vba/vbatabstops.hxx | 53 + sw/source/ui/vba/vbatemplate.cxx | 128 + sw/source/ui/vba/vbatemplate.hxx | 46 + sw/source/ui/vba/vbavariable.cxx | 92 + sw/source/ui/vba/vbavariable.hxx | 53 + sw/source/ui/vba/vbavariables.cxx | 95 + sw/source/ui/vba/vbavariables.hxx | 51 + sw/source/ui/vba/vbaview.cxx | 386 + sw/source/ui/vba/vbaview.hxx | 63 + sw/source/ui/vba/vbawindow.cxx | 178 + sw/source/ui/vba/vbawindow.hxx | 59 + sw/source/ui/vba/vbawrapformat.cxx | 247 + sw/source/ui/vba/vbawrapformat.hxx | 66 + sw/source/ui/vba/wordvbahelper.cxx | 175 + sw/source/ui/vba/wordvbahelper.hxx | 69 + sw/source/uibase/app/appenv.cxx | 493 + sw/source/uibase/app/appenv.hxx | 23 + sw/source/uibase/app/apphdl.cxx | 1077 +++ sw/source/uibase/app/applab.cxx | 393 + sw/source/uibase/app/appopt.cxx | 499 + sw/source/uibase/app/docsh.cxx | 1412 +++ sw/source/uibase/app/docsh2.cxx | 1745 ++++ sw/source/uibase/app/docshdrw.cxx | 100 + sw/source/uibase/app/docshini.cxx | 699 ++ sw/source/uibase/app/docst.cxx | 1547 ++++ sw/source/uibase/app/docstyle.cxx | 3220 +++++++ sw/source/uibase/app/mainwn.cxx | 133 + sw/source/uibase/app/swdll.cxx | 180 + sw/source/uibase/app/swdllimpl.hxx | 45 + sw/source/uibase/app/swmodul1.cxx | 692 ++ sw/source/uibase/app/swmodule.cxx | 403 + sw/source/uibase/app/swwait.cxx | 84 + sw/source/uibase/chrdlg/ccoll.cxx | 160 + sw/source/uibase/config/StoredChapterNumbering.cxx | 476 + sw/source/uibase/config/barcfg.cxx | 124 + sw/source/uibase/config/caption.cxx | 39 + sw/source/uibase/config/cfgitems.cxx | 245 + sw/source/uibase/config/dbconfig.cxx | 99 + sw/source/uibase/config/fontcfg.cxx | 309 + sw/source/uibase/config/modcfg.cxx | 1334 +++ sw/source/uibase/config/prtopt.cxx | 168 + sw/source/uibase/config/uinums.cxx | 260 + sw/source/uibase/config/usrpref.cxx | 599 ++ sw/source/uibase/config/viewopt.cxx | 582 ++ sw/source/uibase/dbui/README | 48 + sw/source/uibase/dbui/dbmgr.cxx | 3366 +++++++ sw/source/uibase/dbui/dbtree.cxx | 435 + sw/source/uibase/dbui/dbui.cxx | 88 + sw/source/uibase/dbui/maildispatcher.cxx | 248 + sw/source/uibase/dbui/mailmergehelper.cxx | 832 ++ sw/source/uibase/dbui/mailmergetoolbarcontrols.cxx | 422 + sw/source/uibase/dbui/mmconfigitem.cxx | 1711 ++++ .../uibase/dialog/SwSpellDialogChildWindow.cxx | 827 ++ sw/source/uibase/dialog/regionsw.cxx | 209 + sw/source/uibase/dialog/swabstdlg.cxx | 55 + sw/source/uibase/dialog/swwrtshitem.cxx | 40 + sw/source/uibase/dialog/watermarkdialog.cxx | 106 + sw/source/uibase/dialog/wordcountwrapper.cxx | 48 + sw/source/uibase/dochdl/gloshdl.cxx | 699 ++ sw/source/uibase/dochdl/swdtflvr.cxx | 4349 +++++++++ sw/source/uibase/docvw/AnchorOverlayObject.cxx | 382 + sw/source/uibase/docvw/AnchorOverlayObject.hxx | 123 + sw/source/uibase/docvw/AnnotationMenuButton.cxx | 216 + sw/source/uibase/docvw/AnnotationMenuButton.hxx | 54 + sw/source/uibase/docvw/AnnotationWin.cxx | 507 + sw/source/uibase/docvw/AnnotationWin2.cxx | 1624 ++++ sw/source/uibase/docvw/DashedLine.cxx | 95 + sw/source/uibase/docvw/FrameControlsManager.cxx | 217 + sw/source/uibase/docvw/HeaderFooterWin.cxx | 521 ++ sw/source/uibase/docvw/OverlayRanges.cxx | 178 + sw/source/uibase/docvw/OverlayRanges.hxx | 75 + sw/source/uibase/docvw/PageBreakWin.cxx | 469 + sw/source/uibase/docvw/PostItMgr.cxx | 2456 +++++ sw/source/uibase/docvw/ShadowOverlayObject.cxx | 237 + sw/source/uibase/docvw/ShadowOverlayObject.hxx | 66 + sw/source/uibase/docvw/SidebarScrollBar.cxx | 73 + sw/source/uibase/docvw/SidebarScrollBar.hxx | 50 + sw/source/uibase/docvw/SidebarTxtControl.cxx | 446 + sw/source/uibase/docvw/SidebarTxtControl.hxx | 77 + sw/source/uibase/docvw/SidebarTxtControlAcc.cxx | 271 + sw/source/uibase/docvw/SidebarTxtControlAcc.hxx | 46 + sw/source/uibase/docvw/SidebarWinAcc.cxx | 144 + sw/source/uibase/docvw/SidebarWinAcc.hxx | 56 + sw/source/uibase/docvw/UnfloatTableButton.cxx | 233 + sw/source/uibase/docvw/edtdd.cxx | 495 + sw/source/uibase/docvw/edtwin.cxx | 6429 +++++++++++++ sw/source/uibase/docvw/edtwin2.cxx | 441 + sw/source/uibase/docvw/edtwin3.cxx | 176 + sw/source/uibase/docvw/frmsidebarwincontainer.cxx | 172 + sw/source/uibase/docvw/frmsidebarwincontainer.hxx | 64 + sw/source/uibase/docvw/romenu.cxx | 343 + sw/source/uibase/docvw/romenu.hxx | 82 + sw/source/uibase/docvw/srcedtw.cxx | 995 ++ sw/source/uibase/envelp/envimg.cxx | 334 + sw/source/uibase/envelp/labelcfg.cxx | 333 + sw/source/uibase/envelp/labimg.cxx | 463 + sw/source/uibase/envelp/syncbtn.cxx | 87 + sw/source/uibase/fldui/fldmgr.cxx | 1850 ++++ sw/source/uibase/fldui/fldwrap.cxx | 131 + sw/source/uibase/fldui/xfldui.cxx | 162 + sw/source/uibase/frmdlg/colex.cxx | 607 ++ sw/source/uibase/frmdlg/colmgr.cxx | 161 + sw/source/uibase/frmdlg/frmmgr.cxx | 613 ++ sw/source/uibase/globdoc/globdoc.cxx | 64 + sw/source/uibase/inc/DashedLine.hxx | 29 + sw/source/uibase/inc/DateFormFieldDialog.hxx | 58 + sw/source/uibase/inc/DropDownFieldDialog.hxx | 66 + sw/source/uibase/inc/DropDownFormFieldDialog.hxx | 68 + sw/source/uibase/inc/FrameControl.hxx | 72 + sw/source/uibase/inc/FrameControlsManager.hxx | 53 + sw/source/uibase/inc/HeaderFooterWin.hxx | 73 + sw/source/uibase/inc/PageBreakWin.hxx | 66 + sw/source/uibase/inc/SidebarWindowsConsts.hxx | 33 + sw/source/uibase/inc/SwSpellDialogChildWindow.hxx | 67 + sw/source/uibase/inc/SwXFilterOptions.hxx | 81 + sw/source/uibase/inc/UnfloatTableButton.hxx | 46 + sw/source/uibase/inc/abstract.hxx | 39 + sw/source/uibase/inc/addrdlg.hxx | 32 + sw/source/uibase/inc/annotsh.hxx | 80 + sw/source/uibase/inc/ascfldlg.hxx | 65 + sw/source/uibase/inc/autoformatpreview.hxx | 82 + sw/source/uibase/inc/barcfg.hxx | 46 + sw/source/uibase/inc/basesh.hxx | 117 + sw/source/uibase/inc/beziersh.hxx | 42 + sw/source/uibase/inc/bmpwin.hxx | 53 + sw/source/uibase/inc/bookctrl.hxx | 45 + sw/source/uibase/inc/bookmark.hxx | 112 + sw/source/uibase/inc/break.hxx | 64 + sw/source/uibase/inc/caption.hxx | 89 + sw/source/uibase/inc/cfgitems.hxx | 163 + sw/source/uibase/inc/changedb.hxx | 60 + sw/source/uibase/inc/chartins.hxx | 37 + sw/source/uibase/inc/chldwrap.hxx | 47 + sw/source/uibase/inc/chrdlg.hxx | 74 + sw/source/uibase/inc/chrdlgmodes.hxx | 20 + sw/source/uibase/inc/cnttab.hxx | 57 + sw/source/uibase/inc/colex.hxx | 107 + sw/source/uibase/inc/colmgr.hxx | 126 + sw/source/uibase/inc/column.hxx | 206 + sw/source/uibase/inc/conarc.hxx | 43 + sw/source/uibase/inc/concustomshape.hxx | 57 + sw/source/uibase/inc/condedit.hxx | 70 + sw/source/uibase/inc/conform.hxx | 38 + sw/source/uibase/inc/conpoly.hxx | 38 + sw/source/uibase/inc/conrect.hxx | 45 + sw/source/uibase/inc/content.hxx | 186 + sw/source/uibase/inc/conttree.hxx | 383 + sw/source/uibase/inc/convert.hxx | 69 + sw/source/uibase/inc/cption.hxx | 83 + sw/source/uibase/inc/dbconfig.hxx | 48 + sw/source/uibase/inc/dbinsdlg.hxx | 161 + sw/source/uibase/inc/dbtree.hxx | 67 + sw/source/uibase/inc/dbui.hxx | 70 + sw/source/uibase/inc/docfnote.hxx | 40 + sw/source/uibase/inc/docstdlg.hxx | 61 + sw/source/uibase/inc/drawbase.hxx | 73 + sw/source/uibase/inc/drawsh.hxx | 56 + sw/source/uibase/inc/drformsh.hxx | 45 + sw/source/uibase/inc/drpcps.hxx | 165 + sw/source/uibase/inc/drwbassh.hxx | 55 + sw/source/uibase/inc/drwtxtsh.hxx | 84 + sw/source/uibase/inc/dselect.hxx | 35 + sw/source/uibase/inc/edtdd.hxx | 19 + sw/source/uibase/inc/edtwin.hxx | 301 + sw/source/uibase/inc/envimg.hxx | 89 + sw/source/uibase/inc/envlop.hxx | 122 + sw/source/uibase/inc/fldedt.hxx | 59 + sw/source/uibase/inc/fldmgr.hxx | 203 + sw/source/uibase/inc/fldtdlg.hxx | 62 + sw/source/uibase/inc/fldwrap.hxx | 57 + sw/source/uibase/inc/fontcfg.hxx | 112 + sw/source/uibase/inc/formatclipboard.hxx | 95 + sw/source/uibase/inc/frmdlg.hxx | 55 + sw/source/uibase/inc/frmmgr.hxx | 174 + sw/source/uibase/inc/frmpage.hxx | 324 + sw/source/uibase/inc/frmsh.hxx | 54 + sw/source/uibase/inc/globals.h | 31 + sw/source/uibase/inc/glosbib.hxx | 81 + sw/source/uibase/inc/glosdoc.hxx | 125 + sw/source/uibase/inc/gloshdl.hxx | 98 + sw/source/uibase/inc/gloslst.hxx | 82 + sw/source/uibase/inc/glossary.hxx | 123 + sw/source/uibase/inc/glshell.hxx | 85 + sw/source/uibase/inc/grfsh.hxx | 56 + sw/source/uibase/inc/hyp.hxx | 54 + sw/source/uibase/inc/idxmrk.hxx | 60 + sw/source/uibase/inc/imaildsplistener.hxx | 61 + sw/source/uibase/inc/initui.hxx | 52 + sw/source/uibase/inc/inpdlg.hxx | 68 + sw/source/uibase/inc/inputwin.hxx | 210 + sw/source/uibase/inc/insfnote.hxx | 85 + sw/source/uibase/inc/instable.hxx | 87 + sw/source/uibase/inc/javaedit.hxx | 79 + sw/source/uibase/inc/label.hxx | 84 + sw/source/uibase/inc/labelcfg.hxx | 61 + sw/source/uibase/inc/labimg.hxx | 125 + sw/source/uibase/inc/labimp.hxx | 36 + sw/source/uibase/inc/labrec.hxx | 57 + sw/source/uibase/inc/langhelper.hxx | 64 + sw/source/uibase/inc/linenum.hxx | 61 + sw/source/uibase/inc/listsh.hxx | 42 + sw/source/uibase/inc/macassgn.hxx | 48 + sw/source/uibase/inc/mailconfigpage.hxx | 69 + sw/source/uibase/inc/maildispatcher.hxx | 159 + sw/source/uibase/inc/mailmergehelper.hxx | 284 + sw/source/uibase/inc/mailmergewizard.hxx | 87 + sw/source/uibase/inc/mailmrge.hxx | 167 + sw/source/uibase/inc/mediash.hxx | 46 + sw/source/uibase/inc/mergetbl.hxx | 39 + sw/source/uibase/inc/mmconfigitem.hxx | 253 + sw/source/uibase/inc/multmrk.hxx | 45 + sw/source/uibase/inc/navicfg.hxx | 104 + sw/source/uibase/inc/navicont.hxx | 55 + sw/source/uibase/inc/navipi.hxx | 177 + sw/source/uibase/inc/navmgr.hxx | 58 + sw/source/uibase/inc/navsh.hxx | 36 + sw/source/uibase/inc/num.hxx | 138 + sw/source/uibase/inc/numberingtypelistbox.hxx | 60 + sw/source/uibase/inc/numfmtlb.hxx | 145 + sw/source/uibase/inc/numpara.hxx | 85 + sw/source/uibase/inc/numprevw.hxx | 67 + sw/source/uibase/inc/olesh.hxx | 39 + sw/source/uibase/inc/olmenu.hxx | 139 + sw/source/uibase/inc/optcomp.hxx | 79 + sw/source/uibase/inc/optload.hxx | 198 + sw/source/uibase/inc/optpage.hxx | 384 + sw/source/uibase/inc/outline.hxx | 123 + sw/source/uibase/inc/pardlg.hxx | 28 + sw/source/uibase/inc/pattern.hxx | 33 + sw/source/uibase/inc/pgfnote.hxx | 69 + sw/source/uibase/inc/pggrid.hxx | 91 + sw/source/uibase/inc/prcntfld.hxx | 85 + sw/source/uibase/inc/pview.hxx | 302 + sw/source/uibase/inc/redlndlg.hxx | 165 + sw/source/uibase/inc/regionsw.hxx | 257 + sw/source/uibase/inc/rowht.hxx | 43 + sw/source/uibase/inc/scroll.hxx | 57 + sw/source/uibase/inc/selglos.hxx | 51 + sw/source/uibase/inc/sharedconnection.hxx | 30 + sw/source/uibase/inc/shdwcrsr.hxx | 53 + sw/source/uibase/inc/splittbl.hxx | 57 + sw/source/uibase/inc/srcedtw.hxx | 148 + sw/source/uibase/inc/srcview.hxx | 89 + sw/source/uibase/inc/srtdlg.hxx | 89 + sw/source/uibase/inc/swcont.hxx | 104 + sw/source/uibase/inc/swdtflvr.hxx | 247 + sw/source/uibase/inc/swmessdialog.hxx | 27 + sw/source/uibase/inc/swmodalredlineacceptdlg.hxx | 41 + sw/source/uibase/inc/swrenamexnameddlg.hxx | 64 + sw/source/uibase/inc/swruler.hxx | 118 + sw/source/uibase/inc/swtablerep.hxx | 84 + sw/source/uibase/inc/swuiccoll.hxx | 73 + sw/source/uibase/inc/swuicnttab.hxx | 441 + sw/source/uibase/inc/swuiidxmrk.hxx | 235 + sw/source/uibase/inc/swuipardlg.hxx | 46 + sw/source/uibase/inc/swwrtshitem.hxx | 42 + sw/source/uibase/inc/syncbtn.hxx | 49 + sw/source/uibase/inc/tabledlg.hxx | 39 + sw/source/uibase/inc/tablemgr.hxx | 76 + sw/source/uibase/inc/tabsh.hxx | 55 + sw/source/uibase/inc/tautofmt.hxx | 89 + sw/source/uibase/inc/tblnumfm.hxx | 35 + sw/source/uibase/inc/textsh.hxx | 91 + sw/source/uibase/inc/titlepage.hxx | 62 + sw/source/uibase/inc/tmpdlg.hxx | 55 + sw/source/uibase/inc/tmplctrl.hxx | 44 + sw/source/uibase/inc/toxmgr.hxx | 271 + sw/source/uibase/inc/uiborder.hxx | 40 + sw/source/uibase/inc/uiitems.hxx | 108 + sw/source/uibase/inc/uinums.hxx | 119 + sw/source/uibase/inc/uiobject.hxx | 65 + sw/source/uibase/inc/uitool.hxx | 113 + sw/source/uibase/inc/uivwimp.hxx | 169 + sw/source/uibase/inc/unoatxt.hxx | 271 + sw/source/uibase/inc/unodispatch.hxx | 127 + sw/source/uibase/inc/unomailmerge.hxx | 170 + sw/source/uibase/inc/unomod.hxx | 138 + sw/source/uibase/inc/unotools.hxx | 84 + sw/source/uibase/inc/unotxvw.hxx | 247 + sw/source/uibase/inc/usrpref.hxx | 273 + sw/source/uibase/inc/viewlayoutctrl.hxx | 47 + sw/source/uibase/inc/watermarkdialog.hxx | 40 + sw/source/uibase/inc/wformsh.hxx | 41 + sw/source/uibase/inc/wfrmsh.hxx | 41 + sw/source/uibase/inc/wgrfsh.hxx | 39 + sw/source/uibase/inc/wlistsh.hxx | 41 + sw/source/uibase/inc/wolesh.hxx | 40 + sw/source/uibase/inc/wordcountctrl.hxx | 37 + sw/source/uibase/inc/wordcountdialog.hxx | 76 + sw/source/uibase/inc/workctrl.hxx | 94 + sw/source/uibase/inc/wrap.hxx | 97 + sw/source/uibase/inc/wrtsh.hxx | 652 ++ sw/source/uibase/inc/wtabsh.hxx | 40 + sw/source/uibase/inc/wtextsh.hxx | 41 + sw/source/uibase/inc/wview.hxx | 45 + sw/source/uibase/inc/zoomctrl.hxx | 43 + sw/source/uibase/index/idxmrk.cxx | 73 + sw/source/uibase/index/toxmgr.cxx | 497 + sw/source/uibase/lingu/hhcwrp.cxx | 693 ++ sw/source/uibase/lingu/hyp.cxx | 124 + sw/source/uibase/lingu/olmenu.cxx | 885 ++ sw/source/uibase/lingu/sdrhhcwrap.cxx | 164 + sw/source/uibase/lingu/sdrhhcwrap.hxx | 56 + sw/source/uibase/misc/glosdoc.cxx | 627 ++ sw/source/uibase/misc/glshell.cxx | 271 + sw/source/uibase/misc/numberingtypelistbox.cxx | 137 + sw/source/uibase/misc/redlndlg.cxx | 1261 +++ sw/source/uibase/misc/swruler.cxx | 371 + sw/source/uibase/ribbar/conarc.cxx | 100 + sw/source/uibase/ribbar/concustomshape.cxx | 186 + sw/source/uibase/ribbar/conform.cxx | 108 + sw/source/uibase/ribbar/conpoly.cxx | 103 + sw/source/uibase/ribbar/conrect.cxx | 214 + sw/source/uibase/ribbar/drawbase.cxx | 550 ++ sw/source/uibase/ribbar/dselect.cxx | 44 + sw/source/uibase/ribbar/inputwin.cxx | 602 ++ sw/source/uibase/ribbar/workctrl.cxx | 1063 +++ sw/source/uibase/shells/annotsh.cxx | 1812 ++++ sw/source/uibase/shells/basesh.cxx | 3039 ++++++ sw/source/uibase/shells/beziersh.cxx | 325 + sw/source/uibase/shells/drawdlg.cxx | 373 + sw/source/uibase/shells/drawsh.cxx | 603 ++ sw/source/uibase/shells/drformsh.cxx | 250 + sw/source/uibase/shells/drwbassh.cxx | 1042 +++ sw/source/uibase/shells/drwtxtex.cxx | 1251 +++ sw/source/uibase/shells/drwtxtsh.cxx | 846 ++ sw/source/uibase/shells/frmsh.cxx | 1458 +++ sw/source/uibase/shells/grfsh.cxx | 1016 ++ sw/source/uibase/shells/grfshex.cxx | 123 + sw/source/uibase/shells/langhelper.cxx | 588 ++ sw/source/uibase/shells/listsh.cxx | 273 + sw/source/uibase/shells/mediash.cxx | 164 + sw/source/uibase/shells/navsh.cxx | 95 + sw/source/uibase/shells/olesh.cxx | 47 + sw/source/uibase/shells/slotadd.cxx | 133 + sw/source/uibase/shells/tabsh.cxx | 1602 ++++ sw/source/uibase/shells/textdrw.cxx | 125 + sw/source/uibase/shells/textfld.cxx | 1096 +++ sw/source/uibase/shells/textglos.cxx | 125 + sw/source/uibase/shells/textidx.cxx | 236 + sw/source/uibase/shells/textsh.cxx | 1049 +++ sw/source/uibase/shells/textsh1.cxx | 2179 +++++ sw/source/uibase/shells/textsh2.cxx | 259 + sw/source/uibase/shells/txtattr.cxx | 845 ++ sw/source/uibase/shells/txtcrsr.cxx | 459 + sw/source/uibase/shells/txtnum.cxx | 310 + sw/source/uibase/sidebar/PageColumnControl.cxx | 122 + sw/source/uibase/sidebar/PageColumnControl.hxx | 56 + sw/source/uibase/sidebar/PageColumnPopup.cxx | 76 + sw/source/uibase/sidebar/PageFooterPanel.cxx | 295 + sw/source/uibase/sidebar/PageFooterPanel.hxx | 110 + sw/source/uibase/sidebar/PageFormatPanel.cxx | 432 + sw/source/uibase/sidebar/PageFormatPanel.hxx | 112 + sw/source/uibase/sidebar/PageHeaderPanel.cxx | 296 + sw/source/uibase/sidebar/PageHeaderPanel.hxx | 110 + sw/source/uibase/sidebar/PageMarginControl.cxx | 586 ++ sw/source/uibase/sidebar/PageMarginControl.hxx | 109 + sw/source/uibase/sidebar/PageMarginPopup.cxx | 75 + sw/source/uibase/sidebar/PageMarginUtils.hxx | 192 + .../uibase/sidebar/PageOrientationControl.cxx | 194 + .../uibase/sidebar/PageOrientationControl.hxx | 60 + sw/source/uibase/sidebar/PageOrientationPopup.cxx | 76 + sw/source/uibase/sidebar/PageSizeControl.cxx | 238 + sw/source/uibase/sidebar/PageSizeControl.hxx | 62 + sw/source/uibase/sidebar/PageSizePopup.cxx | 76 + sw/source/uibase/sidebar/PageStylesPanel.cxx | 604 ++ sw/source/uibase/sidebar/PageStylesPanel.hxx | 123 + sw/source/uibase/sidebar/StylePresetsPanel.cxx | 217 + sw/source/uibase/sidebar/StylePresetsPanel.hxx | 71 + sw/source/uibase/sidebar/SwPanelFactory.cxx | 204 + sw/source/uibase/sidebar/TableEditPanel.cxx | 238 + sw/source/uibase/sidebar/TableEditPanel.hxx | 93 + sw/source/uibase/sidebar/ThemePanel.cxx | 509 + sw/source/uibase/sidebar/ThemePanel.hxx | 63 + sw/source/uibase/sidebar/WrapPropertyPanel.cxx | 189 + sw/source/uibase/sidebar/WrapPropertyPanel.hxx | 88 + sw/source/uibase/table/chartins.cxx | 228 + sw/source/uibase/table/swtablerep.cxx | 150 + sw/source/uibase/table/tablemgr.cxx | 352 + sw/source/uibase/table/tablepg.hxx | 183 + sw/source/uibase/uitest/uiobject.cxx | 172 + sw/source/uibase/uiview/formatclipboard.cxx | 593 ++ sw/source/uibase/uiview/pview.cxx | 1887 ++++ sw/source/uibase/uiview/scroll.cxx | 119 + sw/source/uibase/uiview/srcview.cxx | 849 ++ sw/source/uibase/uiview/swcli.cxx | 167 + sw/source/uibase/uiview/uivwimp.cxx | 322 + sw/source/uibase/uiview/view.cxx | 1871 ++++ sw/source/uibase/uiview/view0.cxx | 648 ++ sw/source/uibase/uiview/view1.cxx | 217 + sw/source/uibase/uiview/view2.cxx | 2573 ++++++ sw/source/uibase/uiview/viewcoll.cxx | 77 + sw/source/uibase/uiview/viewdlg.cxx | 65 + sw/source/uibase/uiview/viewdlg2.cxx | 283 + sw/source/uibase/uiview/viewdraw.cxx | 749 ++ sw/source/uibase/uiview/viewfunc.hxx | 55 + sw/source/uibase/uiview/viewling.cxx | 837 ++ sw/source/uibase/uiview/viewmdi.cxx | 700 ++ sw/source/uibase/uiview/viewport.cxx | 1235 +++ sw/source/uibase/uiview/viewprt.cxx | 350 + sw/source/uibase/uiview/viewsrch.cxx | 869 ++ sw/source/uibase/uiview/viewstat.cxx | 603 ++ sw/source/uibase/uiview/viewtab.cxx | 2532 +++++ sw/source/uibase/uno/SwXDocumentSettings.cxx | 1508 +++ sw/source/uibase/uno/SwXDocumentSettings.hxx | 80 + sw/source/uibase/uno/SwXFilterOptions.cxx | 146 + sw/source/uibase/uno/dlelstnr.cxx | 133 + sw/source/uibase/uno/unoatxt.cxx | 1053 +++ sw/source/uibase/uno/unodefaults.cxx | 52 + sw/source/uibase/uno/unodefaults.hxx | 38 + sw/source/uibase/uno/unodispatch.cxx | 393 + sw/source/uibase/uno/unodoc.cxx | 82 + sw/source/uibase/uno/unofreg.cxx | 89 + sw/source/uibase/uno/unomailmerge.cxx | 1174 +++ sw/source/uibase/uno/unomod.cxx | 970 ++ sw/source/uibase/uno/unomodule.cxx | 150 + sw/source/uibase/uno/unomodule.hxx | 76 + sw/source/uibase/uno/unotxdoc.cxx | 4475 +++++++++ sw/source/uibase/uno/unotxvw.cxx | 1756 ++++ sw/source/uibase/utlui/attrdesc.cxx | 844 ++ sw/source/uibase/utlui/bookctrl.cxx | 141 + sw/source/uibase/utlui/condedit.cxx | 84 + sw/source/uibase/utlui/content.cxx | 4033 ++++++++ sw/source/uibase/utlui/glbltree.cxx | 1088 +++ sw/source/uibase/utlui/gloslst.cxx | 441 + sw/source/uibase/utlui/gotodlg.cxx | 102 + sw/source/uibase/utlui/initui.cxx | 289 + sw/source/uibase/utlui/navicfg.cxx | 128 + sw/source/uibase/utlui/navipi.cxx | 1128 +++ sw/source/uibase/utlui/numfmtlb.cxx | 473 + sw/source/uibase/utlui/prcntfld.cxx | 228 + sw/source/uibase/utlui/shdwcrsr.cxx | 119 + sw/source/uibase/utlui/tmplctrl.cxx | 142 + sw/source/uibase/utlui/uiitems.cxx | 275 + sw/source/uibase/utlui/uitool.cxx | 848 ++ sw/source/uibase/utlui/unotools.cxx | 510 + sw/source/uibase/utlui/viewlayoutctrl.cxx | 197 + sw/source/uibase/utlui/wordcountctrl.cxx | 39 + sw/source/uibase/utlui/zoomctrl.cxx | 65 + sw/source/uibase/web/wdocsh.cxx | 80 + sw/source/uibase/web/wformsh.cxx | 50 + sw/source/uibase/web/wfrmsh.cxx | 50 + sw/source/uibase/web/wgrfsh.cxx | 54 + sw/source/uibase/web/wlistsh.cxx | 49 + sw/source/uibase/web/wolesh.cxx | 48 + sw/source/uibase/web/wtabsh.cxx | 52 + sw/source/uibase/web/wtextsh.cxx | 56 + sw/source/uibase/web/wview.cxx | 292 + sw/source/uibase/wrtsh/delete.cxx | 631 ++ sw/source/uibase/wrtsh/move.cxx | 691 ++ sw/source/uibase/wrtsh/navmgr.cxx | 247 + sw/source/uibase/wrtsh/select.cxx | 993 ++ sw/source/uibase/wrtsh/wrtsh1.cxx | 1990 ++++ sw/source/uibase/wrtsh/wrtsh2.cxx | 606 ++ sw/source/uibase/wrtsh/wrtsh3.cxx | 253 + sw/source/uibase/wrtsh/wrtsh4.cxx | 235 + sw/source/uibase/wrtsh/wrtundo.cxx | 150 + 1504 files changed, 817613 insertions(+) create mode 100644 sw/source/core/SwNumberTree/SwNodeNum.cxx create mode 100644 sw/source/core/SwNumberTree/SwNumberTree.cxx create mode 100644 sw/source/core/access/AccessibilityCheck.cxx create mode 100644 sw/source/core/access/AccessibilityIssue.cxx create mode 100644 sw/source/core/access/acccell.cxx create mode 100644 sw/source/core/access/acccell.hxx create mode 100644 sw/source/core/access/acccontext.cxx create mode 100644 sw/source/core/access/acccontext.hxx create mode 100644 sw/source/core/access/accdoc.cxx create mode 100644 sw/source/core/access/accdoc.hxx create mode 100644 sw/source/core/access/accembedded.cxx create mode 100644 sw/source/core/access/accembedded.hxx create mode 100644 sw/source/core/access/accfootnote.cxx create mode 100644 sw/source/core/access/accfootnote.hxx create mode 100644 sw/source/core/access/accframe.cxx create mode 100644 sw/source/core/access/accframe.hxx create mode 100644 sw/source/core/access/accframebase.cxx create mode 100644 sw/source/core/access/accframebase.hxx create mode 100644 sw/source/core/access/accfrmobj.cxx create mode 100644 sw/source/core/access/accfrmobj.hxx create mode 100644 sw/source/core/access/accfrmobjmap.cxx create mode 100644 sw/source/core/access/accfrmobjmap.hxx create mode 100644 sw/source/core/access/accfrmobjslist.cxx create mode 100644 sw/source/core/access/accfrmobjslist.hxx create mode 100644 sw/source/core/access/accgraphic.cxx create mode 100644 sw/source/core/access/accgraphic.hxx create mode 100644 sw/source/core/access/accheaderfooter.cxx create mode 100644 sw/source/core/access/accheaderfooter.hxx create mode 100644 sw/source/core/access/acchyperlink.cxx create mode 100644 sw/source/core/access/acchyperlink.hxx create mode 100644 sw/source/core/access/acchypertextdata.cxx create mode 100644 sw/source/core/access/acchypertextdata.hxx create mode 100644 sw/source/core/access/accmap.cxx create mode 100644 sw/source/core/access/accnotextframe.cxx create mode 100644 sw/source/core/access/accnotextframe.hxx create mode 100644 sw/source/core/access/accnotexthyperlink.cxx create mode 100644 sw/source/core/access/accnotexthyperlink.hxx create mode 100644 sw/source/core/access/accpage.cxx create mode 100644 sw/source/core/access/accpage.hxx create mode 100644 sw/source/core/access/accpara.cxx create mode 100644 sw/source/core/access/accpara.hxx create mode 100644 sw/source/core/access/accportions.cxx create mode 100644 sw/source/core/access/accportions.hxx create mode 100644 sw/source/core/access/accpreview.cxx create mode 100644 sw/source/core/access/accpreview.hxx create mode 100644 sw/source/core/access/accselectionhelper.cxx create mode 100644 sw/source/core/access/accselectionhelper.hxx create mode 100644 sw/source/core/access/acctable.cxx create mode 100644 sw/source/core/access/acctable.hxx create mode 100644 sw/source/core/access/acctextframe.cxx create mode 100644 sw/source/core/access/acctextframe.hxx create mode 100644 sw/source/core/access/parachangetrackinginfo.cxx create mode 100644 sw/source/core/access/parachangetrackinginfo.hxx create mode 100644 sw/source/core/access/textmarkuphelper.cxx create mode 100644 sw/source/core/access/textmarkuphelper.hxx create mode 100644 sw/source/core/attr/calbck.cxx create mode 100644 sw/source/core/attr/cellatr.cxx create mode 100644 sw/source/core/attr/fmtfollowtextflow.cxx create mode 100644 sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx create mode 100644 sw/source/core/attr/format.cxx create mode 100644 sw/source/core/attr/hints.cxx create mode 100644 sw/source/core/attr/swatrset.cxx create mode 100644 sw/source/core/bastyp/SwSmartTagMgr.cxx create mode 100644 sw/source/core/bastyp/bparr.cxx create mode 100644 sw/source/core/bastyp/breakit.cxx create mode 100644 sw/source/core/bastyp/calc.cxx create mode 100644 sw/source/core/bastyp/checkit.cxx create mode 100644 sw/source/core/bastyp/index.cxx create mode 100644 sw/source/core/bastyp/init.cxx create mode 100644 sw/source/core/bastyp/proofreadingiterator.cxx create mode 100644 sw/source/core/bastyp/swcache.cxx create mode 100644 sw/source/core/bastyp/swrect.cxx create mode 100644 sw/source/core/bastyp/swregion.cxx create mode 100644 sw/source/core/bastyp/swtypes.cxx create mode 100644 sw/source/core/bastyp/tabcol.cxx create mode 100644 sw/source/core/crsr/BlockCursor.cxx create mode 100644 sw/source/core/crsr/BlockCursor.hxx create mode 100644 sw/source/core/crsr/DateFormFieldButton.cxx create mode 100644 sw/source/core/crsr/DropDownFormFieldButton.cxx create mode 100644 sw/source/core/crsr/FormFieldButton.cxx create mode 100644 sw/source/core/crsr/annotationmark.cxx create mode 100644 sw/source/core/crsr/bookmrk.cxx create mode 100644 sw/source/core/crsr/callnk.cxx create mode 100644 sw/source/core/crsr/callnk.hxx create mode 100644 sw/source/core/crsr/crbm.cxx create mode 100644 sw/source/core/crsr/crossrefbookmark.cxx create mode 100644 sw/source/core/crsr/crsrsh.cxx create mode 100644 sw/source/core/crsr/crstrvl.cxx create mode 100644 sw/source/core/crsr/crstrvl1.cxx create mode 100644 sw/source/core/crsr/findattr.cxx create mode 100644 sw/source/core/crsr/findcoll.cxx create mode 100644 sw/source/core/crsr/findfmt.cxx create mode 100644 sw/source/core/crsr/findtxt.cxx create mode 100644 sw/source/core/crsr/overlayrangesoutline.cxx create mode 100644 sw/source/core/crsr/overlayrangesoutline.hxx create mode 100644 sw/source/core/crsr/pam.cxx create mode 100644 sw/source/core/crsr/paminit.cxx create mode 100644 sw/source/core/crsr/swcrsr.cxx create mode 100644 sw/source/core/crsr/trvlcol.cxx create mode 100644 sw/source/core/crsr/trvlfnfl.cxx create mode 100644 sw/source/core/crsr/trvlreg.cxx create mode 100644 sw/source/core/crsr/trvltbl.cxx create mode 100644 sw/source/core/crsr/viscrs.cxx create mode 100644 sw/source/core/doc/CntntIdxStore.cxx create mode 100644 sw/source/core/doc/DocumentChartDataProviderManager.cxx create mode 100644 sw/source/core/doc/DocumentContentOperationsManager.cxx create mode 100644 sw/source/core/doc/DocumentDeviceManager.cxx create mode 100644 sw/source/core/doc/DocumentDrawModelManager.cxx create mode 100644 sw/source/core/doc/DocumentExternalDataManager.cxx create mode 100644 sw/source/core/doc/DocumentFieldsManager.cxx create mode 100644 sw/source/core/doc/DocumentLayoutManager.cxx create mode 100644 sw/source/core/doc/DocumentLinksAdministrationManager.cxx create mode 100644 sw/source/core/doc/DocumentListItemsManager.cxx create mode 100644 sw/source/core/doc/DocumentListsManager.cxx create mode 100644 sw/source/core/doc/DocumentOutlineNodesManager.cxx create mode 100644 sw/source/core/doc/DocumentRedlineManager.cxx create mode 100644 sw/source/core/doc/DocumentSettingManager.cxx create mode 100644 sw/source/core/doc/DocumentStateManager.cxx create mode 100644 sw/source/core/doc/DocumentStatisticsManager.cxx create mode 100644 sw/source/core/doc/DocumentStylePoolManager.cxx create mode 100644 sw/source/core/doc/DocumentTimerManager.cxx create mode 100644 sw/source/core/doc/SwDocIdle.cxx create mode 100644 sw/source/core/doc/SwStyleNameMapper.cxx create mode 100644 sw/source/core/doc/acmplwrd.cxx create mode 100644 sw/source/core/doc/dbgoutsw.cxx create mode 100644 sw/source/core/doc/doc.cxx create mode 100644 sw/source/core/doc/docbasic.cxx create mode 100644 sw/source/core/doc/docbm.cxx create mode 100644 sw/source/core/doc/docchart.cxx create mode 100644 sw/source/core/doc/doccomp.cxx create mode 100644 sw/source/core/doc/doccorr.cxx create mode 100644 sw/source/core/doc/docdesc.cxx create mode 100644 sw/source/core/doc/docdraw.cxx create mode 100644 sw/source/core/doc/docedt.cxx create mode 100644 sw/source/core/doc/docfld.cxx create mode 100644 sw/source/core/doc/docfly.cxx create mode 100644 sw/source/core/doc/docfmt.cxx create mode 100644 sw/source/core/doc/docftn.cxx create mode 100644 sw/source/core/doc/docglbl.cxx create mode 100644 sw/source/core/doc/docglos.cxx create mode 100644 sw/source/core/doc/doclay.cxx create mode 100644 sw/source/core/doc/docnew.cxx create mode 100644 sw/source/core/doc/docnum.cxx create mode 100644 sw/source/core/doc/docredln.cxx create mode 100644 sw/source/core/doc/docruby.cxx create mode 100644 sw/source/core/doc/docsort.cxx create mode 100644 sw/source/core/doc/docstat.cxx create mode 100644 sw/source/core/doc/doctxm.cxx create mode 100644 sw/source/core/doc/docxforms.cxx create mode 100644 sw/source/core/doc/extinput.cxx create mode 100644 sw/source/core/doc/fmtcol.cxx create mode 100644 sw/source/core/doc/ftnidx.cxx create mode 100644 sw/source/core/doc/gctable.cxx create mode 100644 sw/source/core/doc/htmltbl.cxx create mode 100644 sw/source/core/doc/lineinfo.cxx create mode 100644 sw/source/core/doc/list.cxx create mode 100644 sw/source/core/doc/notxtfrm.cxx create mode 100644 sw/source/core/doc/number.cxx create mode 100644 sw/source/core/doc/poolfmt.cxx create mode 100644 sw/source/core/doc/rdfhelper.cxx create mode 100644 sw/source/core/doc/sortopt.cxx create mode 100644 sw/source/core/doc/swserv.cxx create mode 100644 sw/source/core/doc/swstylemanager.cxx create mode 100644 sw/source/core/doc/swstylemanager.hxx create mode 100644 sw/source/core/doc/tblafmt.cxx create mode 100644 sw/source/core/doc/tblcpy.cxx create mode 100644 sw/source/core/doc/tblrwcl.cxx create mode 100644 sw/source/core/doc/textboxhelper.cxx create mode 100644 sw/source/core/doc/visiturl.cxx create mode 100644 sw/source/core/docnode/cancellablejob.cxx create mode 100644 sw/source/core/docnode/cancellablejob.hxx create mode 100644 sw/source/core/docnode/finalthreadmanager.cxx create mode 100644 sw/source/core/docnode/ndcopy.cxx create mode 100644 sw/source/core/docnode/ndnotxt.cxx create mode 100644 sw/source/core/docnode/ndnum.cxx create mode 100644 sw/source/core/docnode/ndsect.cxx create mode 100644 sw/source/core/docnode/ndsect.hxx create mode 100644 sw/source/core/docnode/ndtbl.cxx create mode 100644 sw/source/core/docnode/ndtbl1.cxx create mode 100644 sw/source/core/docnode/node.cxx create mode 100644 sw/source/core/docnode/node2lay.cxx create mode 100644 sw/source/core/docnode/nodes.cxx create mode 100644 sw/source/core/docnode/observablethread.cxx create mode 100644 sw/source/core/docnode/pausethreadstarting.cxx create mode 100644 sw/source/core/docnode/retrievedinputstreamdata.cxx create mode 100644 sw/source/core/docnode/retrieveinputstream.cxx create mode 100644 sw/source/core/docnode/retrieveinputstreamconsumer.cxx create mode 100644 sw/source/core/docnode/section.cxx create mode 100644 sw/source/core/docnode/swbaslnk.cxx create mode 100644 sw/source/core/docnode/swthreadjoiner.cxx create mode 100644 sw/source/core/docnode/swthreadmanager.cxx create mode 100644 sw/source/core/docnode/threadlistener.cxx create mode 100644 sw/source/core/docnode/threadmanager.cxx create mode 100644 sw/source/core/docnode/threadmanager.hxx create mode 100644 sw/source/core/draw/dcontact.cxx create mode 100644 sw/source/core/draw/dflyobj.cxx create mode 100644 sw/source/core/draw/dobjfac.cxx create mode 100644 sw/source/core/draw/dpage.cxx create mode 100644 sw/source/core/draw/drawdoc.cxx create mode 100644 sw/source/core/draw/dview.cxx create mode 100644 sw/source/core/edit/acorrect.cxx create mode 100644 sw/source/core/edit/autofmt.cxx create mode 100644 sw/source/core/edit/edatmisc.cxx create mode 100644 sw/source/core/edit/edattr.cxx create mode 100644 sw/source/core/edit/eddel.cxx create mode 100644 sw/source/core/edit/edfcol.cxx create mode 100644 sw/source/core/edit/edfld.cxx create mode 100644 sw/source/core/edit/edfldexp.cxx create mode 100644 sw/source/core/edit/edfmt.cxx create mode 100644 sw/source/core/edit/edglbldc.cxx create mode 100644 sw/source/core/edit/edglss.cxx create mode 100644 sw/source/core/edit/editsh.cxx create mode 100644 sw/source/core/edit/edlingu.cxx create mode 100644 sw/source/core/edit/ednumber.cxx create mode 100644 sw/source/core/edit/edredln.cxx create mode 100644 sw/source/core/edit/edsect.cxx create mode 100644 sw/source/core/edit/edtab.cxx create mode 100644 sw/source/core/edit/edtox.cxx create mode 100644 sw/source/core/edit/edundo.cxx create mode 100644 sw/source/core/edit/edws.cxx create mode 100644 sw/source/core/fields/authfld.cxx create mode 100644 sw/source/core/fields/cellfml.cxx create mode 100644 sw/source/core/fields/chpfld.cxx create mode 100644 sw/source/core/fields/dbfld.cxx create mode 100644 sw/source/core/fields/ddefld.cxx create mode 100644 sw/source/core/fields/ddetbl.cxx create mode 100644 sw/source/core/fields/docufld.cxx create mode 100644 sw/source/core/fields/expfld.cxx create mode 100644 sw/source/core/fields/fldbas.cxx create mode 100644 sw/source/core/fields/flddat.cxx create mode 100644 sw/source/core/fields/flddropdown.cxx create mode 100644 sw/source/core/fields/fldlst.cxx create mode 100644 sw/source/core/fields/macrofld.cxx create mode 100644 sw/source/core/fields/postithelper.cxx create mode 100644 sw/source/core/fields/reffld.cxx create mode 100644 sw/source/core/fields/scrptfld.cxx create mode 100644 sw/source/core/fields/tblcalc.cxx create mode 100644 sw/source/core/fields/textapi.cxx create mode 100644 sw/source/core/fields/usrfld.cxx create mode 100644 sw/source/core/frmedt/fecopy.cxx create mode 100644 sw/source/core/frmedt/fedesc.cxx create mode 100644 sw/source/core/frmedt/fefly1.cxx create mode 100644 sw/source/core/frmedt/feflyole.cxx create mode 100644 sw/source/core/frmedt/feshview.cxx create mode 100644 sw/source/core/frmedt/fetab.cxx create mode 100644 sw/source/core/frmedt/fews.cxx create mode 100644 sw/source/core/frmedt/tblsel.cxx create mode 100644 sw/source/core/graphic/grfatr.cxx create mode 100644 sw/source/core/graphic/ndgrf.cxx create mode 100644 sw/source/core/inc/AccessibilityCheck.hxx create mode 100644 sw/source/core/inc/AccessibilityIssue.hxx create mode 100644 sw/source/core/inc/DateFormFieldButton.hxx create mode 100644 sw/source/core/inc/DocumentChartDataProviderManager.hxx create mode 100644 sw/source/core/inc/DocumentContentOperationsManager.hxx create mode 100644 sw/source/core/inc/DocumentDeviceManager.hxx create mode 100644 sw/source/core/inc/DocumentDrawModelManager.hxx create mode 100644 sw/source/core/inc/DocumentExternalDataManager.hxx create mode 100644 sw/source/core/inc/DocumentFieldsManager.hxx create mode 100644 sw/source/core/inc/DocumentLayoutManager.hxx create mode 100644 sw/source/core/inc/DocumentLinksAdministrationManager.hxx create mode 100644 sw/source/core/inc/DocumentListItemsManager.hxx create mode 100644 sw/source/core/inc/DocumentListsManager.hxx create mode 100644 sw/source/core/inc/DocumentOutlineNodesManager.hxx create mode 100644 sw/source/core/inc/DocumentRedlineManager.hxx create mode 100644 sw/source/core/inc/DocumentSettingManager.hxx create mode 100644 sw/source/core/inc/DocumentStateManager.hxx create mode 100644 sw/source/core/inc/DocumentStatisticsManager.hxx create mode 100644 sw/source/core/inc/DocumentStylePoolManager.hxx create mode 100644 sw/source/core/inc/DocumentTimerManager.hxx create mode 100644 sw/source/core/inc/DropDownFormFieldButton.hxx create mode 100644 sw/source/core/inc/FormFieldButton.hxx create mode 100644 sw/source/core/inc/GetMetricVal.hxx create mode 100644 sw/source/core/inc/MarkManager.hxx create mode 100644 sw/source/core/inc/SwGrammarMarkUp.hxx create mode 100644 sw/source/core/inc/SwPortionHandler.hxx create mode 100644 sw/source/core/inc/SwUndoFmt.hxx create mode 100644 sw/source/core/inc/SwUndoPageDesc.hxx create mode 100644 sw/source/core/inc/SwUndoTOXChange.hxx create mode 100644 sw/source/core/inc/SwXMLBlockExport.hxx create mode 100644 sw/source/core/inc/SwXMLBlockImport.hxx create mode 100644 sw/source/core/inc/SwXMLTextBlocks.hxx create mode 100644 sw/source/core/inc/SwXTextDefaults.hxx create mode 100644 sw/source/core/inc/TextFrameIndex.hxx create mode 100644 sw/source/core/inc/UndoAttribute.hxx create mode 100644 sw/source/core/inc/UndoBookmark.hxx create mode 100644 sw/source/core/inc/UndoCore.hxx create mode 100644 sw/source/core/inc/UndoDelete.hxx create mode 100644 sw/source/core/inc/UndoDraw.hxx create mode 100644 sw/source/core/inc/UndoInsert.hxx create mode 100644 sw/source/core/inc/UndoManager.hxx create mode 100644 sw/source/core/inc/UndoNumbering.hxx create mode 100644 sw/source/core/inc/UndoOverwrite.hxx create mode 100644 sw/source/core/inc/UndoRedline.hxx create mode 100644 sw/source/core/inc/UndoSection.hxx create mode 100644 sw/source/core/inc/UndoSort.hxx create mode 100644 sw/source/core/inc/UndoSplitMove.hxx create mode 100644 sw/source/core/inc/UndoTable.hxx create mode 100644 sw/source/core/inc/acorrect.hxx create mode 100644 sw/source/core/inc/anchoredobjectposition.hxx create mode 100644 sw/source/core/inc/annotationmark.hxx create mode 100644 sw/source/core/inc/ascharanchoredobjectposition.hxx create mode 100644 sw/source/core/inc/attrhint.hxx create mode 100644 sw/source/core/inc/bodyfrm.hxx create mode 100644 sw/source/core/inc/bookmrk.hxx create mode 100644 sw/source/core/inc/cellfrm.hxx create mode 100644 sw/source/core/inc/cntfrm.hxx create mode 100644 sw/source/core/inc/colfrm.hxx create mode 100644 sw/source/core/inc/crossrefbookmark.hxx create mode 100644 sw/source/core/inc/dbg_lay.hxx create mode 100644 sw/source/core/inc/dflyobj.hxx create mode 100644 sw/source/core/inc/dialoghelp.hxx create mode 100644 sw/source/core/inc/docedt.hxx create mode 100644 sw/source/core/inc/docfld.hxx create mode 100644 sw/source/core/inc/docredln.hxx create mode 100644 sw/source/core/inc/docsort.hxx create mode 100644 sw/source/core/inc/doctxm.hxx create mode 100644 sw/source/core/inc/drawfont.hxx create mode 100644 sw/source/core/inc/dumpfilter.hxx create mode 100644 sw/source/core/inc/dview.hxx create mode 100644 sw/source/core/inc/environmentofanchoredobject.hxx create mode 100644 sw/source/core/inc/fefly.hxx create mode 100644 sw/source/core/inc/fieldhint.hxx create mode 100644 sw/source/core/inc/flowfrm.hxx create mode 100644 sw/source/core/inc/flyfrm.hxx create mode 100644 sw/source/core/inc/flyfrms.hxx create mode 100644 sw/source/core/inc/fntcache.hxx create mode 100644 sw/source/core/inc/fntcap.hxx create mode 100644 sw/source/core/inc/frame.hxx create mode 100644 sw/source/core/inc/frminf.hxx create mode 100644 sw/source/core/inc/frmtool.hxx create mode 100644 sw/source/core/inc/ftnboss.hxx create mode 100644 sw/source/core/inc/ftnfrm.hxx create mode 100644 sw/source/core/inc/hffrm.hxx create mode 100644 sw/source/core/inc/ifinishedthreadlistener.hxx create mode 100644 sw/source/core/inc/layact.hxx create mode 100644 sw/source/core/inc/laycache.hxx create mode 100644 sw/source/core/inc/layfrm.hxx create mode 100644 sw/source/core/inc/layouter.hxx create mode 100644 sw/source/core/inc/movedfwdfrmsbyobjpos.hxx create mode 100644 sw/source/core/inc/mvsave.hxx create mode 100644 sw/source/core/inc/node2lay.hxx create mode 100644 sw/source/core/inc/noteurl.hxx create mode 100644 sw/source/core/inc/notxtfrm.hxx create mode 100644 sw/source/core/inc/objectformatter.hxx create mode 100644 sw/source/core/inc/observablethread.hxx create mode 100644 sw/source/core/inc/pagedeschint.hxx create mode 100644 sw/source/core/inc/pagefrm.hxx create mode 100644 sw/source/core/inc/paintfrm.hxx create mode 100644 sw/source/core/inc/pamtyp.hxx create mode 100644 sw/source/core/inc/prevwpage.hxx create mode 100644 sw/source/core/inc/ptqueue.hxx create mode 100644 sw/source/core/inc/retrievedinputstreamdata.hxx create mode 100644 sw/source/core/inc/retrieveinputstream.hxx create mode 100644 sw/source/core/inc/retrieveinputstreamconsumer.hxx create mode 100644 sw/source/core/inc/rolbck.hxx create mode 100644 sw/source/core/inc/rootfrm.hxx create mode 100644 sw/source/core/inc/rowfrm.hxx create mode 100644 sw/source/core/inc/scriptinfo.hxx create mode 100644 sw/source/core/inc/sectfrm.hxx create mode 100644 sw/source/core/inc/sortedobjs.hxx create mode 100644 sw/source/core/inc/swblocks.hxx create mode 100644 sw/source/core/inc/swcache.hxx create mode 100644 sw/source/core/inc/swfntcch.hxx create mode 100644 sw/source/core/inc/swfont.hxx create mode 100644 sw/source/core/inc/swselectionlist.hxx create mode 100644 sw/source/core/inc/swthreadjoiner.hxx create mode 100644 sw/source/core/inc/swthreadmanager.hxx create mode 100644 sw/source/core/inc/tabfrm.hxx create mode 100644 sw/source/core/inc/tblrwcl.hxx create mode 100644 sw/source/core/inc/textapi.hxx create mode 100644 sw/source/core/inc/threadlistener.hxx create mode 100644 sw/source/core/inc/tocntntanchoredobjectposition.hxx create mode 100644 sw/source/core/inc/tolayoutanchoredobjectposition.hxx create mode 100644 sw/source/core/inc/txmsrt.hxx create mode 100644 sw/source/core/inc/txtfly.hxx create mode 100644 sw/source/core/inc/txtfrm.hxx create mode 100644 sw/source/core/inc/txttypes.hxx create mode 100644 sw/source/core/inc/undoflystrattr.hxx create mode 100644 sw/source/core/inc/unobookmark.hxx create mode 100644 sw/source/core/inc/unoevent.hxx create mode 100644 sw/source/core/inc/unofield.hxx create mode 100644 sw/source/core/inc/unoflatpara.hxx create mode 100644 sw/source/core/inc/unofldmid.h create mode 100644 sw/source/core/inc/unofootnote.hxx create mode 100644 sw/source/core/inc/unofreg.hxx create mode 100644 sw/source/core/inc/unoidx.hxx create mode 100644 sw/source/core/inc/unometa.hxx create mode 100644 sw/source/core/inc/unoparaframeenum.hxx create mode 100644 sw/source/core/inc/unoport.hxx create mode 100644 sw/source/core/inc/unorefmark.hxx create mode 100644 sw/source/core/inc/unosection.hxx create mode 100644 sw/source/core/inc/unotextmarkup.hxx create mode 100644 sw/source/core/inc/viewimp.hxx create mode 100644 sw/source/core/inc/visiturl.hxx create mode 100644 sw/source/core/inc/wrong.hxx create mode 100644 sw/source/core/layout/anchoreddrawobject.cxx create mode 100644 sw/source/core/layout/anchoredobject.cxx create mode 100644 sw/source/core/layout/atrfrm.cxx create mode 100644 sw/source/core/layout/calcmove.cxx create mode 100644 sw/source/core/layout/colfrm.cxx create mode 100644 sw/source/core/layout/dbg_lay.cxx create mode 100644 sw/source/core/layout/dumpfilter.cxx create mode 100644 sw/source/core/layout/findfrm.cxx create mode 100644 sw/source/core/layout/flowfrm.cxx create mode 100644 sw/source/core/layout/fly.cxx create mode 100644 sw/source/core/layout/flycnt.cxx create mode 100644 sw/source/core/layout/flyincnt.cxx create mode 100644 sw/source/core/layout/flylay.cxx create mode 100644 sw/source/core/layout/flypos.cxx create mode 100644 sw/source/core/layout/frmtool.cxx create mode 100644 sw/source/core/layout/ftnfrm.cxx create mode 100644 sw/source/core/layout/hffrm.cxx create mode 100644 sw/source/core/layout/layact.cxx create mode 100644 sw/source/core/layout/laycache.cxx create mode 100644 sw/source/core/layout/layhelp.hxx create mode 100644 sw/source/core/layout/layouter.cxx create mode 100644 sw/source/core/layout/legacyitem.cxx create mode 100644 sw/source/core/layout/movedfwdfrmsbyobjpos.cxx create mode 100644 sw/source/core/layout/newfrm.cxx create mode 100644 sw/source/core/layout/objectformatter.cxx create mode 100644 sw/source/core/layout/objectformatterlayfrm.cxx create mode 100644 sw/source/core/layout/objectformatterlayfrm.hxx create mode 100644 sw/source/core/layout/objectformattertxtfrm.cxx create mode 100644 sw/source/core/layout/objectformattertxtfrm.hxx create mode 100644 sw/source/core/layout/objstmpconsiderwrapinfl.cxx create mode 100644 sw/source/core/layout/objstmpconsiderwrapinfl.hxx create mode 100644 sw/source/core/layout/pagechg.cxx create mode 100644 sw/source/core/layout/pagedesc.cxx create mode 100644 sw/source/core/layout/paintfrm.cxx create mode 100644 sw/source/core/layout/sectfrm.cxx create mode 100644 sw/source/core/layout/softpagebreak.cxx create mode 100644 sw/source/core/layout/sortedobjs.cxx create mode 100644 sw/source/core/layout/ssfrm.cxx create mode 100644 sw/source/core/layout/swselectionlist.cxx create mode 100644 sw/source/core/layout/tabfrm.cxx create mode 100644 sw/source/core/layout/trvlfrm.cxx create mode 100644 sw/source/core/layout/unusedf.cxx create mode 100644 sw/source/core/layout/virtoutp.cxx create mode 100644 sw/source/core/layout/virtoutp.hxx create mode 100644 sw/source/core/layout/wsfrm.cxx create mode 100644 sw/source/core/objectpositioning/anchoredobjectposition.cxx create mode 100644 sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx create mode 100644 sw/source/core/objectpositioning/environmentofanchoredobject.cxx create mode 100644 sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx create mode 100644 sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx create mode 100644 sw/source/core/ole/ndole.cxx create mode 100644 sw/source/core/para/paratr.cxx create mode 100644 sw/source/core/sw3io/swacorr.cxx create mode 100644 sw/source/core/swg/BlockListTokens.txt create mode 100644 sw/source/core/swg/SwXMLBlockExport.cxx create mode 100644 sw/source/core/swg/SwXMLBlockImport.cxx create mode 100644 sw/source/core/swg/SwXMLSectionList.cxx create mode 100644 sw/source/core/swg/SwXMLTextBlocks.cxx create mode 100644 sw/source/core/swg/SwXMLTextBlocks1.cxx create mode 100644 sw/source/core/swg/TextBlockTokens.txt create mode 100644 sw/source/core/swg/swblocks.cxx create mode 100644 sw/source/core/table/swnewtable.cxx create mode 100644 sw/source/core/table/swtable.cxx create mode 100644 sw/source/core/text/EnhancedPDFExportHelper.cxx create mode 100644 sw/source/core/text/SwGrammarMarkUp.cxx create mode 100644 sw/source/core/text/atrhndl.hxx create mode 100644 sw/source/core/text/atrstck.cxx create mode 100644 sw/source/core/text/frmcrsr.cxx create mode 100644 sw/source/core/text/frmform.cxx create mode 100644 sw/source/core/text/frminf.cxx create mode 100644 sw/source/core/text/frmpaint.cxx create mode 100644 sw/source/core/text/guess.cxx create mode 100644 sw/source/core/text/guess.hxx create mode 100644 sw/source/core/text/inftxt.cxx create mode 100644 sw/source/core/text/inftxt.hxx create mode 100644 sw/source/core/text/itradj.cxx create mode 100644 sw/source/core/text/itratr.cxx create mode 100644 sw/source/core/text/itratr.hxx create mode 100644 sw/source/core/text/itrcrsr.cxx create mode 100644 sw/source/core/text/itrform2.cxx create mode 100644 sw/source/core/text/itrform2.hxx create mode 100644 sw/source/core/text/itrpaint.cxx create mode 100644 sw/source/core/text/itrpaint.hxx create mode 100644 sw/source/core/text/itrtxt.cxx create mode 100644 sw/source/core/text/itrtxt.hxx create mode 100644 sw/source/core/text/noteurl.cxx create mode 100644 sw/source/core/text/pordrop.hxx create mode 100644 sw/source/core/text/porexp.cxx create mode 100644 sw/source/core/text/porexp.hxx create mode 100644 sw/source/core/text/porfld.cxx create mode 100644 sw/source/core/text/porfld.hxx create mode 100644 sw/source/core/text/porfly.cxx create mode 100644 sw/source/core/text/porfly.hxx create mode 100644 sw/source/core/text/porftn.hxx create mode 100644 sw/source/core/text/porglue.cxx create mode 100644 sw/source/core/text/porglue.hxx create mode 100644 sw/source/core/text/porhyph.hxx create mode 100644 sw/source/core/text/porlay.cxx create mode 100644 sw/source/core/text/porlay.hxx create mode 100644 sw/source/core/text/porlin.cxx create mode 100644 sw/source/core/text/porlin.hxx create mode 100644 sw/source/core/text/pormulti.cxx create mode 100644 sw/source/core/text/pormulti.hxx create mode 100644 sw/source/core/text/porref.cxx create mode 100644 sw/source/core/text/porref.hxx create mode 100644 sw/source/core/text/porrst.cxx create mode 100644 sw/source/core/text/porrst.hxx create mode 100644 sw/source/core/text/portab.hxx create mode 100644 sw/source/core/text/portox.cxx create mode 100644 sw/source/core/text/portox.hxx create mode 100644 sw/source/core/text/portxt.cxx create mode 100644 sw/source/core/text/portxt.hxx create mode 100644 sw/source/core/text/possiz.hxx create mode 100644 sw/source/core/text/redlnitr.cxx create mode 100644 sw/source/core/text/redlnitr.hxx create mode 100644 sw/source/core/text/txtcache.cxx create mode 100644 sw/source/core/text/txtcache.hxx create mode 100644 sw/source/core/text/txtdrop.cxx create mode 100644 sw/source/core/text/txtfld.cxx create mode 100644 sw/source/core/text/txtfly.cxx create mode 100644 sw/source/core/text/txtfrm.cxx create mode 100644 sw/source/core/text/txtftn.cxx create mode 100644 sw/source/core/text/txthyph.cxx create mode 100644 sw/source/core/text/txtinit.cxx create mode 100644 sw/source/core/text/txtpaint.cxx create mode 100644 sw/source/core/text/txtpaint.hxx create mode 100644 sw/source/core/text/txttab.cxx create mode 100644 sw/source/core/text/widorp.cxx create mode 100644 sw/source/core/text/widorp.hxx create mode 100644 sw/source/core/text/wrong.cxx create mode 100644 sw/source/core/text/xmldump.cxx create mode 100644 sw/source/core/tox/ToxLinkProcessor.cxx create mode 100644 sw/source/core/tox/ToxTabStopTokenHandler.cxx create mode 100644 sw/source/core/tox/ToxTextGenerator.cxx create mode 100644 sw/source/core/tox/ToxWhitespaceStripper.cxx create mode 100644 sw/source/core/tox/tox.cxx create mode 100644 sw/source/core/tox/toxhlp.cxx create mode 100644 sw/source/core/tox/txmsrt.cxx create mode 100644 sw/source/core/txtnode/SwGrammarContact.cxx create mode 100644 sw/source/core/txtnode/atrfld.cxx create mode 100644 sw/source/core/txtnode/atrflyin.cxx create mode 100644 sw/source/core/txtnode/atrftn.cxx create mode 100644 sw/source/core/txtnode/atrref.cxx create mode 100644 sw/source/core/txtnode/atrtox.cxx create mode 100644 sw/source/core/txtnode/chrfmt.cxx create mode 100644 sw/source/core/txtnode/fmtatr2.cxx create mode 100644 sw/source/core/txtnode/fntcache.cxx create mode 100644 sw/source/core/txtnode/fntcap.cxx create mode 100644 sw/source/core/txtnode/modeltoviewhelper.cxx create mode 100644 sw/source/core/txtnode/ndhints.cxx create mode 100644 sw/source/core/txtnode/ndtxt.cxx create mode 100644 sw/source/core/txtnode/swfntcch.cxx create mode 100644 sw/source/core/txtnode/swfont.cxx create mode 100644 sw/source/core/txtnode/thints.cxx create mode 100644 sw/source/core/txtnode/txatbase.cxx create mode 100644 sw/source/core/txtnode/txatritr.cxx create mode 100644 sw/source/core/txtnode/txtatr2.cxx create mode 100644 sw/source/core/txtnode/txtedt.cxx create mode 100644 sw/source/core/undo/SwRewriter.cxx create mode 100644 sw/source/core/undo/SwUndoField.cxx create mode 100644 sw/source/core/undo/SwUndoFmt.cxx create mode 100644 sw/source/core/undo/SwUndoPageDesc.cxx create mode 100644 sw/source/core/undo/SwUndoTOXChange.cxx create mode 100644 sw/source/core/undo/docundo.cxx create mode 100644 sw/source/core/undo/rolbck.cxx create mode 100644 sw/source/core/undo/unattr.cxx create mode 100644 sw/source/core/undo/unbkmk.cxx create mode 100644 sw/source/core/undo/undel.cxx create mode 100644 sw/source/core/undo/undobj.cxx create mode 100644 sw/source/core/undo/undobj1.cxx create mode 100644 sw/source/core/undo/undoflystrattr.cxx create mode 100644 sw/source/core/undo/undraw.cxx create mode 100644 sw/source/core/undo/unfmco.cxx create mode 100644 sw/source/core/undo/unins.cxx create mode 100644 sw/source/core/undo/unmove.cxx create mode 100644 sw/source/core/undo/unnum.cxx create mode 100644 sw/source/core/undo/unoutl.cxx create mode 100644 sw/source/core/undo/unovwr.cxx create mode 100644 sw/source/core/undo/unredln.cxx create mode 100644 sw/source/core/undo/unsect.cxx create mode 100644 sw/source/core/undo/unsort.cxx create mode 100644 sw/source/core/undo/unspnd.cxx create mode 100644 sw/source/core/undo/untbl.cxx create mode 100644 sw/source/core/undo/untblk.cxx create mode 100644 sw/source/core/unocore/SwXTextDefaults.cxx create mode 100644 sw/source/core/unocore/TextCursorHelper.cxx create mode 100644 sw/source/core/unocore/XMLRangeHelper.cxx create mode 100644 sw/source/core/unocore/XMLRangeHelper.hxx create mode 100644 sw/source/core/unocore/swunohelper.cxx create mode 100644 sw/source/core/unocore/unobkm.cxx create mode 100644 sw/source/core/unocore/unochart.cxx create mode 100644 sw/source/core/unocore/unocoll.cxx create mode 100644 sw/source/core/unocore/unocrsr.cxx create mode 100644 sw/source/core/unocore/unocrsrhelper.cxx create mode 100644 sw/source/core/unocore/unodraw.cxx create mode 100644 sw/source/core/unocore/unoevent.cxx create mode 100644 sw/source/core/unocore/unofield.cxx create mode 100644 sw/source/core/unocore/unoflatpara.cxx create mode 100644 sw/source/core/unocore/unoframe.cxx create mode 100644 sw/source/core/unocore/unoftn.cxx create mode 100644 sw/source/core/unocore/unoidx.cxx create mode 100644 sw/source/core/unocore/unomap.cxx create mode 100644 sw/source/core/unocore/unomap1.cxx create mode 100644 sw/source/core/unocore/unomapproperties.hxx create mode 100644 sw/source/core/unocore/unoobj.cxx create mode 100644 sw/source/core/unocore/unoobj2.cxx create mode 100644 sw/source/core/unocore/unoparagraph.cxx create mode 100644 sw/source/core/unocore/unoport.cxx create mode 100644 sw/source/core/unocore/unoportenum.cxx create mode 100644 sw/source/core/unocore/unoredline.cxx create mode 100644 sw/source/core/unocore/unoredlines.cxx create mode 100644 sw/source/core/unocore/unorefmk.cxx create mode 100644 sw/source/core/unocore/unosect.cxx create mode 100644 sw/source/core/unocore/unosett.cxx create mode 100644 sw/source/core/unocore/unosrch.cxx create mode 100644 sw/source/core/unocore/unostyle.cxx create mode 100644 sw/source/core/unocore/unotbl.cxx create mode 100644 sw/source/core/unocore/unotext.cxx create mode 100644 sw/source/core/unocore/unotextmarkup.cxx create mode 100644 sw/source/core/view/dialoghelp.cxx create mode 100644 sw/source/core/view/pagepreviewlayout.cxx create mode 100644 sw/source/core/view/printdata.cxx create mode 100644 sw/source/core/view/vdraw.cxx create mode 100644 sw/source/core/view/viewimp.cxx create mode 100644 sw/source/core/view/viewpg.cxx create mode 100644 sw/source/core/view/viewsh.cxx create mode 100644 sw/source/core/view/vnew.cxx create mode 100644 sw/source/core/view/vprint.cxx create mode 100644 sw/source/core/view/vprint.hxx create mode 100644 sw/source/filter/ascii/ascatr.cxx create mode 100644 sw/source/filter/ascii/parasc.cxx create mode 100644 sw/source/filter/ascii/wrtasc.cxx create mode 100644 sw/source/filter/ascii/wrtasc.hxx create mode 100644 sw/source/filter/basflt/docfact.cxx create mode 100644 sw/source/filter/basflt/fltini.cxx create mode 100644 sw/source/filter/basflt/fltshell.cxx create mode 100644 sw/source/filter/basflt/iodetect.cxx create mode 100644 sw/source/filter/basflt/shellio.cxx create mode 100644 sw/source/filter/docx/swdocxreader.cxx create mode 100644 sw/source/filter/docx/swdocxreader.hxx create mode 100644 sw/source/filter/html/SwAppletImpl.cxx create mode 100644 sw/source/filter/html/css1atr.cxx create mode 100644 sw/source/filter/html/css1atr.hxx create mode 100644 sw/source/filter/html/css1kywd.cxx create mode 100644 sw/source/filter/html/css1kywd.hxx create mode 100644 sw/source/filter/html/htmlatr.cxx create mode 100644 sw/source/filter/html/htmlatr.hxx create mode 100644 sw/source/filter/html/htmlbas.cxx create mode 100644 sw/source/filter/html/htmlcss1.cxx create mode 100644 sw/source/filter/html/htmlctxt.cxx create mode 100644 sw/source/filter/html/htmldrawreader.cxx create mode 100644 sw/source/filter/html/htmldrawwriter.cxx create mode 100644 sw/source/filter/html/htmlfld.cxx create mode 100644 sw/source/filter/html/htmlfld.hxx create mode 100644 sw/source/filter/html/htmlfldw.cxx create mode 100644 sw/source/filter/html/htmlfly.cxx create mode 100644 sw/source/filter/html/htmlfly.hxx create mode 100644 sw/source/filter/html/htmlflyt.cxx create mode 100644 sw/source/filter/html/htmlflywriter.cxx create mode 100644 sw/source/filter/html/htmlform.cxx create mode 100644 sw/source/filter/html/htmlform.hxx create mode 100644 sw/source/filter/html/htmlforw.cxx create mode 100644 sw/source/filter/html/htmlftn.cxx create mode 100644 sw/source/filter/html/htmlgrin.cxx create mode 100644 sw/source/filter/html/htmlnum.cxx create mode 100644 sw/source/filter/html/htmlnum.hxx create mode 100644 sw/source/filter/html/htmlnumreader.cxx create mode 100644 sw/source/filter/html/htmlnumwriter.cxx create mode 100644 sw/source/filter/html/htmlplug.cxx create mode 100644 sw/source/filter/html/htmlreqifreader.cxx create mode 100644 sw/source/filter/html/htmlreqifreader.hxx create mode 100644 sw/source/filter/html/htmlsect.cxx create mode 100644 sw/source/filter/html/htmltab.cxx create mode 100644 sw/source/filter/html/htmltabw.cxx create mode 100644 sw/source/filter/html/parcss1.cxx create mode 100644 sw/source/filter/html/parcss1.hxx create mode 100644 sw/source/filter/html/svxcss1.cxx create mode 100644 sw/source/filter/html/svxcss1.hxx create mode 100644 sw/source/filter/html/swcss1.hxx create mode 100644 sw/source/filter/html/swhtml.cxx create mode 100644 sw/source/filter/html/swhtml.hxx create mode 100644 sw/source/filter/html/wrthtml.cxx create mode 100644 sw/source/filter/html/wrthtml.hxx create mode 100644 sw/source/filter/inc/fltini.hxx create mode 100644 sw/source/filter/inc/fltshell.hxx create mode 100644 sw/source/filter/inc/msfilter.hxx create mode 100644 sw/source/filter/inc/rtf.hxx create mode 100644 sw/source/filter/inc/wrt_fn.hxx create mode 100644 sw/source/filter/inc/wrtswtbl.hxx create mode 100644 sw/source/filter/inc/wwstyles.hxx create mode 100644 sw/source/filter/rtf/swparrtf.cxx create mode 100644 sw/source/filter/writer/writer.cxx create mode 100644 sw/source/filter/writer/wrt_fn.cxx create mode 100644 sw/source/filter/writer/wrtswtbl.cxx create mode 100644 sw/source/filter/ww8/README-rtf.txt create mode 100644 sw/source/filter/ww8/WW8FFData.cxx create mode 100644 sw/source/filter/ww8/WW8FFData.hxx create mode 100644 sw/source/filter/ww8/WW8FibData.cxx create mode 100644 sw/source/filter/ww8/WW8FibData.hxx create mode 100644 sw/source/filter/ww8/WW8Sttbf.cxx create mode 100644 sw/source/filter/ww8/WW8Sttbf.hxx create mode 100644 sw/source/filter/ww8/WW8TableInfo.cxx create mode 100644 sw/source/filter/ww8/WW8TableInfo.hxx create mode 100644 sw/source/filter/ww8/attributeoutputbase.hxx create mode 100644 sw/source/filter/ww8/docxattributeoutput.cxx create mode 100644 sw/source/filter/ww8/docxattributeoutput.hxx create mode 100644 sw/source/filter/ww8/docxexport.cxx create mode 100644 sw/source/filter/ww8/docxexport.hxx create mode 100644 sw/source/filter/ww8/docxexportfilter.cxx create mode 100644 sw/source/filter/ww8/docxexportfilter.hxx create mode 100644 sw/source/filter/ww8/docxfootnotes.hxx create mode 100644 sw/source/filter/ww8/docxhelper.hxx create mode 100644 sw/source/filter/ww8/docxsdrexport.cxx create mode 100644 sw/source/filter/ww8/docxsdrexport.hxx create mode 100644 sw/source/filter/ww8/docxtablestyleexport.cxx create mode 100644 sw/source/filter/ww8/docxtablestyleexport.hxx create mode 100644 sw/source/filter/ww8/escher.hxx create mode 100644 sw/source/filter/ww8/fields.cxx create mode 100644 sw/source/filter/ww8/fields.hxx create mode 100644 sw/source/filter/ww8/needed_cast.hxx create mode 100644 sw/source/filter/ww8/rtfattributeoutput.cxx create mode 100644 sw/source/filter/ww8/rtfattributeoutput.hxx create mode 100644 sw/source/filter/ww8/rtfexport.cxx create mode 100644 sw/source/filter/ww8/rtfexport.hxx create mode 100644 sw/source/filter/ww8/rtfexportfilter.cxx create mode 100644 sw/source/filter/ww8/rtfexportfilter.hxx create mode 100644 sw/source/filter/ww8/rtfsdrexport.cxx create mode 100644 sw/source/filter/ww8/rtfsdrexport.hxx create mode 100644 sw/source/filter/ww8/rtfstringbuffer.cxx create mode 100644 sw/source/filter/ww8/rtfstringbuffer.hxx create mode 100644 sw/source/filter/ww8/sortedarray.hxx create mode 100644 sw/source/filter/ww8/sprmids.hxx create mode 100644 sw/source/filter/ww8/styles.cxx create mode 100644 sw/source/filter/ww8/types.hxx create mode 100644 sw/source/filter/ww8/writerhelper.cxx create mode 100644 sw/source/filter/ww8/writerhelper.hxx create mode 100644 sw/source/filter/ww8/writerwordglue.cxx create mode 100644 sw/source/filter/ww8/writerwordglue.hxx create mode 100644 sw/source/filter/ww8/wrtw8esh.cxx create mode 100644 sw/source/filter/ww8/wrtw8nds.cxx create mode 100644 sw/source/filter/ww8/wrtw8num.cxx create mode 100644 sw/source/filter/ww8/wrtw8sty.cxx create mode 100644 sw/source/filter/ww8/wrtww8.cxx create mode 100644 sw/source/filter/ww8/wrtww8.hxx create mode 100644 sw/source/filter/ww8/wrtww8gr.cxx create mode 100644 sw/source/filter/ww8/ww8atr.cxx create mode 100644 sw/source/filter/ww8/ww8attributeoutput.hxx create mode 100644 sw/source/filter/ww8/ww8glsy.cxx create mode 100644 sw/source/filter/ww8/ww8glsy.hxx create mode 100644 sw/source/filter/ww8/ww8graf.cxx create mode 100644 sw/source/filter/ww8/ww8graf.hxx create mode 100644 sw/source/filter/ww8/ww8graf2.cxx create mode 100644 sw/source/filter/ww8/ww8par.cxx create mode 100644 sw/source/filter/ww8/ww8par.hxx create mode 100644 sw/source/filter/ww8/ww8par2.cxx create mode 100644 sw/source/filter/ww8/ww8par2.hxx create mode 100644 sw/source/filter/ww8/ww8par3.cxx create mode 100644 sw/source/filter/ww8/ww8par4.cxx create mode 100644 sw/source/filter/ww8/ww8par5.cxx create mode 100644 sw/source/filter/ww8/ww8par6.cxx create mode 100644 sw/source/filter/ww8/ww8scan.cxx create mode 100644 sw/source/filter/ww8/ww8scan.hxx create mode 100644 sw/source/filter/ww8/ww8struc.hxx create mode 100644 sw/source/filter/ww8/ww8toolbar.cxx create mode 100644 sw/source/filter/ww8/ww8toolbar.hxx create mode 100644 sw/source/filter/xml/XMLRedlineImportHelper.cxx create mode 100644 sw/source/filter/xml/XMLRedlineImportHelper.hxx create mode 100644 sw/source/filter/xml/swxml.cxx create mode 100644 sw/source/filter/xml/wrtxml.cxx create mode 100644 sw/source/filter/xml/wrtxml.hxx create mode 100644 sw/source/filter/xml/xmlbrsh.cxx create mode 100644 sw/source/filter/xml/xmlbrshe.hxx create mode 100644 sw/source/filter/xml/xmlbrshi.hxx create mode 100644 sw/source/filter/xml/xmlexp.cxx create mode 100644 sw/source/filter/xml/xmlexp.hxx create mode 100644 sw/source/filter/xml/xmlexpit.cxx create mode 100644 sw/source/filter/xml/xmlexpit.hxx create mode 100644 sw/source/filter/xml/xmlfmt.cxx create mode 100644 sw/source/filter/xml/xmlfmte.cxx create mode 100644 sw/source/filter/xml/xmlfonte.cxx create mode 100644 sw/source/filter/xml/xmlimp.cxx create mode 100644 sw/source/filter/xml/xmlimp.hxx create mode 100644 sw/source/filter/xml/xmlimpit.cxx create mode 100644 sw/source/filter/xml/xmlimpit.hxx create mode 100644 sw/source/filter/xml/xmlitem.cxx create mode 100644 sw/source/filter/xml/xmlitem.hxx create mode 100644 sw/source/filter/xml/xmliteme.cxx create mode 100644 sw/source/filter/xml/xmlitemi.cxx create mode 100644 sw/source/filter/xml/xmlitemm.cxx create mode 100644 sw/source/filter/xml/xmlithlp.cxx create mode 100644 sw/source/filter/xml/xmlithlp.hxx create mode 100644 sw/source/filter/xml/xmlitmap.hxx create mode 100644 sw/source/filter/xml/xmlitmpr.cxx create mode 100644 sw/source/filter/xml/xmlmeta.cxx create mode 100644 sw/source/filter/xml/xmlscript.cxx create mode 100644 sw/source/filter/xml/xmltble.cxx create mode 100644 sw/source/filter/xml/xmltbli.cxx create mode 100644 sw/source/filter/xml/xmltbli.hxx create mode 100644 sw/source/filter/xml/xmltext.cxx create mode 100644 sw/source/filter/xml/xmltexte.cxx create mode 100644 sw/source/filter/xml/xmltexte.hxx create mode 100644 sw/source/filter/xml/xmltexti.cxx create mode 100644 sw/source/filter/xml/xmltexti.hxx create mode 100644 sw/source/ui/chrdlg/break.cxx create mode 100644 sw/source/ui/chrdlg/chardlg.cxx create mode 100644 sw/source/ui/chrdlg/drpcps.cxx create mode 100644 sw/source/ui/chrdlg/numpara.cxx create mode 100644 sw/source/ui/chrdlg/pardlg.cxx create mode 100644 sw/source/ui/chrdlg/swuiccoll.cxx create mode 100644 sw/source/ui/chrdlg/tblnumfm.cxx create mode 100644 sw/source/ui/config/mailconfigpage.cxx create mode 100644 sw/source/ui/config/optcomp.cxx create mode 100644 sw/source/ui/config/optload.cxx create mode 100644 sw/source/ui/config/optpage.cxx create mode 100644 sw/source/ui/dbui/addresslistdialog.cxx create mode 100644 sw/source/ui/dbui/addresslistdialog.hxx create mode 100644 sw/source/ui/dbui/createaddresslistdialog.cxx create mode 100644 sw/source/ui/dbui/createaddresslistdialog.hxx create mode 100644 sw/source/ui/dbui/customizeaddresslistdialog.cxx create mode 100644 sw/source/ui/dbui/customizeaddresslistdialog.hxx create mode 100644 sw/source/ui/dbui/dbinsdlg.cxx create mode 100644 sw/source/ui/dbui/dbtablepreviewdialog.cxx create mode 100644 sw/source/ui/dbui/dbtablepreviewdialog.hxx create mode 100644 sw/source/ui/dbui/mailmergewizard.cxx create mode 100644 sw/source/ui/dbui/mmaddressblockpage.cxx create mode 100644 sw/source/ui/dbui/mmaddressblockpage.hxx create mode 100644 sw/source/ui/dbui/mmdocselectpage.cxx create mode 100644 sw/source/ui/dbui/mmdocselectpage.hxx create mode 100644 sw/source/ui/dbui/mmgreetingspage.cxx create mode 100644 sw/source/ui/dbui/mmgreetingspage.hxx create mode 100644 sw/source/ui/dbui/mmlayoutpage.cxx create mode 100644 sw/source/ui/dbui/mmlayoutpage.hxx create mode 100644 sw/source/ui/dbui/mmoutputtypepage.cxx create mode 100644 sw/source/ui/dbui/mmoutputtypepage.hxx create mode 100644 sw/source/ui/dbui/mmresultdialogs.cxx create mode 100644 sw/source/ui/dbui/selectdbtabledialog.cxx create mode 100644 sw/source/ui/dbui/selectdbtabledialog.hxx create mode 100644 sw/source/ui/dialog/abstract.cxx create mode 100644 sw/source/ui/dialog/addrdlg.cxx create mode 100644 sw/source/ui/dialog/ascfldlg.cxx create mode 100644 sw/source/ui/dialog/docstdlg.cxx create mode 100644 sw/source/ui/dialog/macassgn.cxx create mode 100644 sw/source/ui/dialog/swdlgfact.cxx create mode 100644 sw/source/ui/dialog/swdlgfact.hxx create mode 100644 sw/source/ui/dialog/swmessdialog.cxx create mode 100644 sw/source/ui/dialog/swuiexp.cxx create mode 100644 sw/source/ui/dialog/uiregionsw.cxx create mode 100644 sw/source/ui/dialog/wordcountdialog.cxx create mode 100644 sw/source/ui/dochdl/selglos.cxx create mode 100644 sw/source/ui/envelp/envfmt.cxx create mode 100644 sw/source/ui/envelp/envfmt.hxx create mode 100644 sw/source/ui/envelp/envlop1.cxx create mode 100644 sw/source/ui/envelp/envprt.cxx create mode 100644 sw/source/ui/envelp/envprt.hxx create mode 100644 sw/source/ui/envelp/label1.cxx create mode 100644 sw/source/ui/envelp/labelexp.cxx create mode 100644 sw/source/ui/envelp/labfmt.cxx create mode 100644 sw/source/ui/envelp/labfmt.hxx create mode 100644 sw/source/ui/envelp/labprt.cxx create mode 100644 sw/source/ui/envelp/labprt.hxx create mode 100644 sw/source/ui/envelp/mailmrge.cxx create mode 100644 sw/source/ui/envelp/swuilabimp.hxx create mode 100644 sw/source/ui/fldui/DateFormFieldDialog.cxx create mode 100644 sw/source/ui/fldui/DropDownFieldDialog.cxx create mode 100644 sw/source/ui/fldui/DropDownFormFieldDialog.cxx create mode 100644 sw/source/ui/fldui/changedb.cxx create mode 100644 sw/source/ui/fldui/flddb.cxx create mode 100644 sw/source/ui/fldui/flddb.hxx create mode 100644 sw/source/ui/fldui/flddinf.cxx create mode 100644 sw/source/ui/fldui/flddinf.hxx create mode 100644 sw/source/ui/fldui/flddok.cxx create mode 100644 sw/source/ui/fldui/flddok.hxx create mode 100644 sw/source/ui/fldui/fldedt.cxx create mode 100644 sw/source/ui/fldui/fldfunc.cxx create mode 100644 sw/source/ui/fldui/fldfunc.hxx create mode 100644 sw/source/ui/fldui/fldpage.cxx create mode 100644 sw/source/ui/fldui/fldpage.hxx create mode 100644 sw/source/ui/fldui/fldref.cxx create mode 100644 sw/source/ui/fldui/fldref.hxx create mode 100644 sw/source/ui/fldui/fldtdlg.cxx create mode 100644 sw/source/ui/fldui/fldvar.cxx create mode 100644 sw/source/ui/fldui/fldvar.hxx create mode 100644 sw/source/ui/fldui/inpdlg.cxx create mode 100644 sw/source/ui/fldui/javaedit.cxx create mode 100644 sw/source/ui/fmtui/tmpdlg.cxx create mode 100644 sw/source/ui/frmdlg/column.cxx create mode 100644 sw/source/ui/frmdlg/cption.cxx create mode 100644 sw/source/ui/frmdlg/frmdlg.cxx create mode 100644 sw/source/ui/frmdlg/frmpage.cxx create mode 100644 sw/source/ui/frmdlg/pattern.cxx create mode 100644 sw/source/ui/frmdlg/uiborder.cxx create mode 100644 sw/source/ui/frmdlg/wrap.cxx create mode 100644 sw/source/ui/inc/mmresultdialogs.hxx create mode 100644 sw/source/ui/inc/swuiexp.hxx create mode 100644 sw/source/ui/index/cntex.cxx create mode 100644 sw/source/ui/index/cnttab.cxx create mode 100644 sw/source/ui/index/multmrk.cxx create mode 100644 sw/source/ui/index/swuiidxmrk.cxx create mode 100644 sw/source/ui/misc/bookmark.cxx create mode 100644 sw/source/ui/misc/docfnote.cxx create mode 100644 sw/source/ui/misc/glosbib.cxx create mode 100644 sw/source/ui/misc/glossary.cxx create mode 100644 sw/source/ui/misc/impfnote.hxx create mode 100644 sw/source/ui/misc/insfnote.cxx create mode 100644 sw/source/ui/misc/linenum.cxx create mode 100644 sw/source/ui/misc/num.cxx create mode 100644 sw/source/ui/misc/outline.cxx create mode 100644 sw/source/ui/misc/pgfnote.cxx create mode 100644 sw/source/ui/misc/pggrid.cxx create mode 100644 sw/source/ui/misc/srtdlg.cxx create mode 100644 sw/source/ui/misc/swmodalredlineacceptdlg.cxx create mode 100644 sw/source/ui/misc/titlepage.cxx create mode 100644 sw/source/ui/table/autoformatpreview.cxx create mode 100644 sw/source/ui/table/colwd.cxx create mode 100644 sw/source/ui/table/convert.cxx create mode 100644 sw/source/ui/table/instable.cxx create mode 100644 sw/source/ui/table/mergetbl.cxx create mode 100644 sw/source/ui/table/rowht.cxx create mode 100644 sw/source/ui/table/splittbl.cxx create mode 100644 sw/source/ui/table/tabledlg.cxx create mode 100644 sw/source/ui/table/tautofmt.cxx create mode 100644 sw/source/ui/uno/swdetect.cxx create mode 100644 sw/source/ui/uno/swdetect.hxx create mode 100644 sw/source/ui/utlui/swrenamexnameddlg.cxx create mode 100644 sw/source/ui/vba/service.cxx create mode 100644 sw/source/ui/vba/service.hxx create mode 100644 sw/source/ui/vba/vbaaddin.cxx create mode 100644 sw/source/ui/vba/vbaaddin.hxx create mode 100644 sw/source/ui/vba/vbaaddins.cxx create mode 100644 sw/source/ui/vba/vbaaddins.hxx create mode 100644 sw/source/ui/vba/vbaapplication.cxx create mode 100644 sw/source/ui/vba/vbaapplication.hxx create mode 100644 sw/source/ui/vba/vbaautotextentry.cxx create mode 100644 sw/source/ui/vba/vbaautotextentry.hxx create mode 100644 sw/source/ui/vba/vbabookmark.cxx create mode 100644 sw/source/ui/vba/vbabookmark.hxx create mode 100644 sw/source/ui/vba/vbabookmarks.cxx create mode 100644 sw/source/ui/vba/vbabookmarks.hxx create mode 100644 sw/source/ui/vba/vbaborders.cxx create mode 100644 sw/source/ui/vba/vbaborders.hxx create mode 100644 sw/source/ui/vba/vbacell.cxx create mode 100644 sw/source/ui/vba/vbacell.hxx create mode 100644 sw/source/ui/vba/vbacells.cxx create mode 100644 sw/source/ui/vba/vbacells.hxx create mode 100644 sw/source/ui/vba/vbacolumn.cxx create mode 100644 sw/source/ui/vba/vbacolumn.hxx create mode 100644 sw/source/ui/vba/vbacolumns.cxx create mode 100644 sw/source/ui/vba/vbacolumns.hxx create mode 100644 sw/source/ui/vba/vbadialog.cxx create mode 100644 sw/source/ui/vba/vbadialog.hxx create mode 100644 sw/source/ui/vba/vbadialogs.cxx create mode 100644 sw/source/ui/vba/vbadialogs.hxx create mode 100644 sw/source/ui/vba/vbadocument.cxx create mode 100644 sw/source/ui/vba/vbadocument.hxx create mode 100644 sw/source/ui/vba/vbadocumentproperties.cxx create mode 100644 sw/source/ui/vba/vbadocumentproperties.hxx create mode 100644 sw/source/ui/vba/vbadocuments.cxx create mode 100644 sw/source/ui/vba/vbadocuments.hxx create mode 100644 sw/source/ui/vba/vbaeventshelper.cxx create mode 100644 sw/source/ui/vba/vbaeventshelper.hxx create mode 100644 sw/source/ui/vba/vbafield.cxx create mode 100644 sw/source/ui/vba/vbafield.hxx create mode 100644 sw/source/ui/vba/vbafilterpropsfromformat.hxx create mode 100644 sw/source/ui/vba/vbafind.cxx create mode 100644 sw/source/ui/vba/vbafind.hxx create mode 100644 sw/source/ui/vba/vbafont.cxx create mode 100644 sw/source/ui/vba/vbafont.hxx create mode 100644 sw/source/ui/vba/vbaframe.cxx create mode 100644 sw/source/ui/vba/vbaframe.hxx create mode 100644 sw/source/ui/vba/vbaframes.cxx create mode 100644 sw/source/ui/vba/vbaframes.hxx create mode 100644 sw/source/ui/vba/vbaglobals.cxx create mode 100644 sw/source/ui/vba/vbaglobals.hxx create mode 100644 sw/source/ui/vba/vbaheaderfooter.cxx create mode 100644 sw/source/ui/vba/vbaheaderfooter.hxx create mode 100644 sw/source/ui/vba/vbaheaderfooterhelper.cxx create mode 100644 sw/source/ui/vba/vbaheaderfooterhelper.hxx create mode 100644 sw/source/ui/vba/vbaheadersfooters.cxx create mode 100644 sw/source/ui/vba/vbaheadersfooters.hxx create mode 100644 sw/source/ui/vba/vbainformationhelper.cxx create mode 100644 sw/source/ui/vba/vbainformationhelper.hxx create mode 100644 sw/source/ui/vba/vbalistformat.cxx create mode 100644 sw/source/ui/vba/vbalistformat.hxx create mode 100644 sw/source/ui/vba/vbalistgalleries.cxx create mode 100644 sw/source/ui/vba/vbalistgalleries.hxx create mode 100644 sw/source/ui/vba/vbalistgallery.cxx create mode 100644 sw/source/ui/vba/vbalistgallery.hxx create mode 100644 sw/source/ui/vba/vbalisthelper.cxx create mode 100644 sw/source/ui/vba/vbalisthelper.hxx create mode 100644 sw/source/ui/vba/vbalistlevel.cxx create mode 100644 sw/source/ui/vba/vbalistlevel.hxx create mode 100644 sw/source/ui/vba/vbalistlevels.cxx create mode 100644 sw/source/ui/vba/vbalistlevels.hxx create mode 100644 sw/source/ui/vba/vbalisttemplate.cxx create mode 100644 sw/source/ui/vba/vbalisttemplate.hxx create mode 100644 sw/source/ui/vba/vbalisttemplates.cxx create mode 100644 sw/source/ui/vba/vbalisttemplates.hxx create mode 100644 sw/source/ui/vba/vbamailmerge.cxx create mode 100644 sw/source/ui/vba/vbamailmerge.hxx create mode 100644 sw/source/ui/vba/vbaoptions.cxx create mode 100644 sw/source/ui/vba/vbaoptions.hxx create mode 100644 sw/source/ui/vba/vbapagesetup.cxx create mode 100644 sw/source/ui/vba/vbapagesetup.hxx create mode 100644 sw/source/ui/vba/vbapalette.cxx create mode 100644 sw/source/ui/vba/vbapalette.hxx create mode 100644 sw/source/ui/vba/vbapane.cxx create mode 100644 sw/source/ui/vba/vbapane.hxx create mode 100644 sw/source/ui/vba/vbapanes.cxx create mode 100644 sw/source/ui/vba/vbapanes.hxx create mode 100644 sw/source/ui/vba/vbaparagraph.cxx create mode 100644 sw/source/ui/vba/vbaparagraph.hxx create mode 100644 sw/source/ui/vba/vbaparagraphformat.cxx create mode 100644 sw/source/ui/vba/vbaparagraphformat.hxx create mode 100644 sw/source/ui/vba/vbarange.cxx create mode 100644 sw/source/ui/vba/vbarange.hxx create mode 100644 sw/source/ui/vba/vbarangehelper.cxx create mode 100644 sw/source/ui/vba/vbarangehelper.hxx create mode 100644 sw/source/ui/vba/vbareplacement.cxx create mode 100644 sw/source/ui/vba/vbareplacement.hxx create mode 100644 sw/source/ui/vba/vbarevision.cxx create mode 100644 sw/source/ui/vba/vbarevision.hxx create mode 100644 sw/source/ui/vba/vbarevisions.cxx create mode 100644 sw/source/ui/vba/vbarevisions.hxx create mode 100644 sw/source/ui/vba/vbarow.cxx create mode 100644 sw/source/ui/vba/vbarow.hxx create mode 100644 sw/source/ui/vba/vbarows.cxx create mode 100644 sw/source/ui/vba/vbarows.hxx create mode 100644 sw/source/ui/vba/vbasection.cxx create mode 100644 sw/source/ui/vba/vbasection.hxx create mode 100644 sw/source/ui/vba/vbasections.cxx create mode 100644 sw/source/ui/vba/vbasections.hxx create mode 100644 sw/source/ui/vba/vbaselection.cxx create mode 100644 sw/source/ui/vba/vbaselection.hxx create mode 100644 sw/source/ui/vba/vbastyle.cxx create mode 100644 sw/source/ui/vba/vbastyle.hxx create mode 100644 sw/source/ui/vba/vbastyles.cxx create mode 100644 sw/source/ui/vba/vbastyles.hxx create mode 100644 sw/source/ui/vba/vbasystem.cxx create mode 100644 sw/source/ui/vba/vbasystem.hxx create mode 100644 sw/source/ui/vba/vbatable.cxx create mode 100644 sw/source/ui/vba/vbatable.hxx create mode 100644 sw/source/ui/vba/vbatablehelper.cxx create mode 100644 sw/source/ui/vba/vbatablehelper.hxx create mode 100644 sw/source/ui/vba/vbatableofcontents.cxx create mode 100644 sw/source/ui/vba/vbatableofcontents.hxx create mode 100644 sw/source/ui/vba/vbatables.cxx create mode 100644 sw/source/ui/vba/vbatables.hxx create mode 100644 sw/source/ui/vba/vbatablesofcontents.cxx create mode 100644 sw/source/ui/vba/vbatablesofcontents.hxx create mode 100644 sw/source/ui/vba/vbatabstop.cxx create mode 100644 sw/source/ui/vba/vbatabstop.hxx create mode 100644 sw/source/ui/vba/vbatabstops.cxx create mode 100644 sw/source/ui/vba/vbatabstops.hxx create mode 100644 sw/source/ui/vba/vbatemplate.cxx create mode 100644 sw/source/ui/vba/vbatemplate.hxx create mode 100644 sw/source/ui/vba/vbavariable.cxx create mode 100644 sw/source/ui/vba/vbavariable.hxx create mode 100644 sw/source/ui/vba/vbavariables.cxx create mode 100644 sw/source/ui/vba/vbavariables.hxx create mode 100644 sw/source/ui/vba/vbaview.cxx create mode 100644 sw/source/ui/vba/vbaview.hxx create mode 100644 sw/source/ui/vba/vbawindow.cxx create mode 100644 sw/source/ui/vba/vbawindow.hxx create mode 100644 sw/source/ui/vba/vbawrapformat.cxx create mode 100644 sw/source/ui/vba/vbawrapformat.hxx create mode 100644 sw/source/ui/vba/wordvbahelper.cxx create mode 100644 sw/source/ui/vba/wordvbahelper.hxx create mode 100644 sw/source/uibase/app/appenv.cxx create mode 100644 sw/source/uibase/app/appenv.hxx create mode 100644 sw/source/uibase/app/apphdl.cxx create mode 100644 sw/source/uibase/app/applab.cxx create mode 100644 sw/source/uibase/app/appopt.cxx create mode 100644 sw/source/uibase/app/docsh.cxx create mode 100644 sw/source/uibase/app/docsh2.cxx create mode 100644 sw/source/uibase/app/docshdrw.cxx create mode 100644 sw/source/uibase/app/docshini.cxx create mode 100644 sw/source/uibase/app/docst.cxx create mode 100644 sw/source/uibase/app/docstyle.cxx create mode 100644 sw/source/uibase/app/mainwn.cxx create mode 100644 sw/source/uibase/app/swdll.cxx create mode 100644 sw/source/uibase/app/swdllimpl.hxx create mode 100644 sw/source/uibase/app/swmodul1.cxx create mode 100644 sw/source/uibase/app/swmodule.cxx create mode 100644 sw/source/uibase/app/swwait.cxx create mode 100644 sw/source/uibase/chrdlg/ccoll.cxx create mode 100644 sw/source/uibase/config/StoredChapterNumbering.cxx create mode 100644 sw/source/uibase/config/barcfg.cxx create mode 100644 sw/source/uibase/config/caption.cxx create mode 100644 sw/source/uibase/config/cfgitems.cxx create mode 100644 sw/source/uibase/config/dbconfig.cxx create mode 100644 sw/source/uibase/config/fontcfg.cxx create mode 100644 sw/source/uibase/config/modcfg.cxx create mode 100644 sw/source/uibase/config/prtopt.cxx create mode 100644 sw/source/uibase/config/uinums.cxx create mode 100644 sw/source/uibase/config/usrpref.cxx create mode 100644 sw/source/uibase/config/viewopt.cxx create mode 100644 sw/source/uibase/dbui/README create mode 100644 sw/source/uibase/dbui/dbmgr.cxx create mode 100644 sw/source/uibase/dbui/dbtree.cxx create mode 100644 sw/source/uibase/dbui/dbui.cxx create mode 100644 sw/source/uibase/dbui/maildispatcher.cxx create mode 100644 sw/source/uibase/dbui/mailmergehelper.cxx create mode 100644 sw/source/uibase/dbui/mailmergetoolbarcontrols.cxx create mode 100644 sw/source/uibase/dbui/mmconfigitem.cxx create mode 100644 sw/source/uibase/dialog/SwSpellDialogChildWindow.cxx create mode 100644 sw/source/uibase/dialog/regionsw.cxx create mode 100644 sw/source/uibase/dialog/swabstdlg.cxx create mode 100644 sw/source/uibase/dialog/swwrtshitem.cxx create mode 100644 sw/source/uibase/dialog/watermarkdialog.cxx create mode 100644 sw/source/uibase/dialog/wordcountwrapper.cxx create mode 100644 sw/source/uibase/dochdl/gloshdl.cxx create mode 100644 sw/source/uibase/dochdl/swdtflvr.cxx create mode 100644 sw/source/uibase/docvw/AnchorOverlayObject.cxx create mode 100644 sw/source/uibase/docvw/AnchorOverlayObject.hxx create mode 100644 sw/source/uibase/docvw/AnnotationMenuButton.cxx create mode 100644 sw/source/uibase/docvw/AnnotationMenuButton.hxx create mode 100644 sw/source/uibase/docvw/AnnotationWin.cxx create mode 100644 sw/source/uibase/docvw/AnnotationWin2.cxx create mode 100644 sw/source/uibase/docvw/DashedLine.cxx create mode 100644 sw/source/uibase/docvw/FrameControlsManager.cxx create mode 100644 sw/source/uibase/docvw/HeaderFooterWin.cxx create mode 100644 sw/source/uibase/docvw/OverlayRanges.cxx create mode 100644 sw/source/uibase/docvw/OverlayRanges.hxx create mode 100644 sw/source/uibase/docvw/PageBreakWin.cxx create mode 100644 sw/source/uibase/docvw/PostItMgr.cxx create mode 100644 sw/source/uibase/docvw/ShadowOverlayObject.cxx create mode 100644 sw/source/uibase/docvw/ShadowOverlayObject.hxx create mode 100644 sw/source/uibase/docvw/SidebarScrollBar.cxx create mode 100644 sw/source/uibase/docvw/SidebarScrollBar.hxx create mode 100644 sw/source/uibase/docvw/SidebarTxtControl.cxx create mode 100644 sw/source/uibase/docvw/SidebarTxtControl.hxx create mode 100644 sw/source/uibase/docvw/SidebarTxtControlAcc.cxx create mode 100644 sw/source/uibase/docvw/SidebarTxtControlAcc.hxx create mode 100644 sw/source/uibase/docvw/SidebarWinAcc.cxx create mode 100644 sw/source/uibase/docvw/SidebarWinAcc.hxx create mode 100644 sw/source/uibase/docvw/UnfloatTableButton.cxx create mode 100644 sw/source/uibase/docvw/edtdd.cxx create mode 100644 sw/source/uibase/docvw/edtwin.cxx create mode 100644 sw/source/uibase/docvw/edtwin2.cxx create mode 100644 sw/source/uibase/docvw/edtwin3.cxx create mode 100644 sw/source/uibase/docvw/frmsidebarwincontainer.cxx create mode 100644 sw/source/uibase/docvw/frmsidebarwincontainer.hxx create mode 100644 sw/source/uibase/docvw/romenu.cxx create mode 100644 sw/source/uibase/docvw/romenu.hxx create mode 100644 sw/source/uibase/docvw/srcedtw.cxx create mode 100644 sw/source/uibase/envelp/envimg.cxx create mode 100644 sw/source/uibase/envelp/labelcfg.cxx create mode 100644 sw/source/uibase/envelp/labimg.cxx create mode 100644 sw/source/uibase/envelp/syncbtn.cxx create mode 100644 sw/source/uibase/fldui/fldmgr.cxx create mode 100644 sw/source/uibase/fldui/fldwrap.cxx create mode 100644 sw/source/uibase/fldui/xfldui.cxx create mode 100644 sw/source/uibase/frmdlg/colex.cxx create mode 100644 sw/source/uibase/frmdlg/colmgr.cxx create mode 100644 sw/source/uibase/frmdlg/frmmgr.cxx create mode 100644 sw/source/uibase/globdoc/globdoc.cxx create mode 100644 sw/source/uibase/inc/DashedLine.hxx create mode 100644 sw/source/uibase/inc/DateFormFieldDialog.hxx create mode 100644 sw/source/uibase/inc/DropDownFieldDialog.hxx create mode 100644 sw/source/uibase/inc/DropDownFormFieldDialog.hxx create mode 100644 sw/source/uibase/inc/FrameControl.hxx create mode 100644 sw/source/uibase/inc/FrameControlsManager.hxx create mode 100644 sw/source/uibase/inc/HeaderFooterWin.hxx create mode 100644 sw/source/uibase/inc/PageBreakWin.hxx create mode 100644 sw/source/uibase/inc/SidebarWindowsConsts.hxx create mode 100644 sw/source/uibase/inc/SwSpellDialogChildWindow.hxx create mode 100644 sw/source/uibase/inc/SwXFilterOptions.hxx create mode 100644 sw/source/uibase/inc/UnfloatTableButton.hxx create mode 100644 sw/source/uibase/inc/abstract.hxx create mode 100644 sw/source/uibase/inc/addrdlg.hxx create mode 100644 sw/source/uibase/inc/annotsh.hxx create mode 100644 sw/source/uibase/inc/ascfldlg.hxx create mode 100644 sw/source/uibase/inc/autoformatpreview.hxx create mode 100644 sw/source/uibase/inc/barcfg.hxx create mode 100644 sw/source/uibase/inc/basesh.hxx create mode 100644 sw/source/uibase/inc/beziersh.hxx create mode 100644 sw/source/uibase/inc/bmpwin.hxx create mode 100644 sw/source/uibase/inc/bookctrl.hxx create mode 100644 sw/source/uibase/inc/bookmark.hxx create mode 100644 sw/source/uibase/inc/break.hxx create mode 100644 sw/source/uibase/inc/caption.hxx create mode 100644 sw/source/uibase/inc/cfgitems.hxx create mode 100644 sw/source/uibase/inc/changedb.hxx create mode 100644 sw/source/uibase/inc/chartins.hxx create mode 100644 sw/source/uibase/inc/chldwrap.hxx create mode 100644 sw/source/uibase/inc/chrdlg.hxx create mode 100644 sw/source/uibase/inc/chrdlgmodes.hxx create mode 100644 sw/source/uibase/inc/cnttab.hxx create mode 100644 sw/source/uibase/inc/colex.hxx create mode 100644 sw/source/uibase/inc/colmgr.hxx create mode 100644 sw/source/uibase/inc/column.hxx create mode 100644 sw/source/uibase/inc/conarc.hxx create mode 100644 sw/source/uibase/inc/concustomshape.hxx create mode 100644 sw/source/uibase/inc/condedit.hxx create mode 100644 sw/source/uibase/inc/conform.hxx create mode 100644 sw/source/uibase/inc/conpoly.hxx create mode 100644 sw/source/uibase/inc/conrect.hxx create mode 100644 sw/source/uibase/inc/content.hxx create mode 100644 sw/source/uibase/inc/conttree.hxx create mode 100644 sw/source/uibase/inc/convert.hxx create mode 100644 sw/source/uibase/inc/cption.hxx create mode 100644 sw/source/uibase/inc/dbconfig.hxx create mode 100644 sw/source/uibase/inc/dbinsdlg.hxx create mode 100644 sw/source/uibase/inc/dbtree.hxx create mode 100644 sw/source/uibase/inc/dbui.hxx create mode 100644 sw/source/uibase/inc/docfnote.hxx create mode 100644 sw/source/uibase/inc/docstdlg.hxx create mode 100644 sw/source/uibase/inc/drawbase.hxx create mode 100644 sw/source/uibase/inc/drawsh.hxx create mode 100644 sw/source/uibase/inc/drformsh.hxx create mode 100644 sw/source/uibase/inc/drpcps.hxx create mode 100644 sw/source/uibase/inc/drwbassh.hxx create mode 100644 sw/source/uibase/inc/drwtxtsh.hxx create mode 100644 sw/source/uibase/inc/dselect.hxx create mode 100644 sw/source/uibase/inc/edtdd.hxx create mode 100644 sw/source/uibase/inc/edtwin.hxx create mode 100644 sw/source/uibase/inc/envimg.hxx create mode 100644 sw/source/uibase/inc/envlop.hxx create mode 100644 sw/source/uibase/inc/fldedt.hxx create mode 100644 sw/source/uibase/inc/fldmgr.hxx create mode 100644 sw/source/uibase/inc/fldtdlg.hxx create mode 100644 sw/source/uibase/inc/fldwrap.hxx create mode 100644 sw/source/uibase/inc/fontcfg.hxx create mode 100644 sw/source/uibase/inc/formatclipboard.hxx create mode 100644 sw/source/uibase/inc/frmdlg.hxx create mode 100644 sw/source/uibase/inc/frmmgr.hxx create mode 100644 sw/source/uibase/inc/frmpage.hxx create mode 100644 sw/source/uibase/inc/frmsh.hxx create mode 100644 sw/source/uibase/inc/globals.h create mode 100644 sw/source/uibase/inc/glosbib.hxx create mode 100644 sw/source/uibase/inc/glosdoc.hxx create mode 100644 sw/source/uibase/inc/gloshdl.hxx create mode 100644 sw/source/uibase/inc/gloslst.hxx create mode 100644 sw/source/uibase/inc/glossary.hxx create mode 100644 sw/source/uibase/inc/glshell.hxx create mode 100644 sw/source/uibase/inc/grfsh.hxx create mode 100644 sw/source/uibase/inc/hyp.hxx create mode 100644 sw/source/uibase/inc/idxmrk.hxx create mode 100644 sw/source/uibase/inc/imaildsplistener.hxx create mode 100644 sw/source/uibase/inc/initui.hxx create mode 100644 sw/source/uibase/inc/inpdlg.hxx create mode 100644 sw/source/uibase/inc/inputwin.hxx create mode 100644 sw/source/uibase/inc/insfnote.hxx create mode 100644 sw/source/uibase/inc/instable.hxx create mode 100644 sw/source/uibase/inc/javaedit.hxx create mode 100644 sw/source/uibase/inc/label.hxx create mode 100644 sw/source/uibase/inc/labelcfg.hxx create mode 100644 sw/source/uibase/inc/labimg.hxx create mode 100644 sw/source/uibase/inc/labimp.hxx create mode 100644 sw/source/uibase/inc/labrec.hxx create mode 100644 sw/source/uibase/inc/langhelper.hxx create mode 100644 sw/source/uibase/inc/linenum.hxx create mode 100644 sw/source/uibase/inc/listsh.hxx create mode 100644 sw/source/uibase/inc/macassgn.hxx create mode 100644 sw/source/uibase/inc/mailconfigpage.hxx create mode 100644 sw/source/uibase/inc/maildispatcher.hxx create mode 100644 sw/source/uibase/inc/mailmergehelper.hxx create mode 100644 sw/source/uibase/inc/mailmergewizard.hxx create mode 100644 sw/source/uibase/inc/mailmrge.hxx create mode 100644 sw/source/uibase/inc/mediash.hxx create mode 100644 sw/source/uibase/inc/mergetbl.hxx create mode 100644 sw/source/uibase/inc/mmconfigitem.hxx create mode 100644 sw/source/uibase/inc/multmrk.hxx create mode 100644 sw/source/uibase/inc/navicfg.hxx create mode 100644 sw/source/uibase/inc/navicont.hxx create mode 100644 sw/source/uibase/inc/navipi.hxx create mode 100644 sw/source/uibase/inc/navmgr.hxx create mode 100644 sw/source/uibase/inc/navsh.hxx create mode 100644 sw/source/uibase/inc/num.hxx create mode 100644 sw/source/uibase/inc/numberingtypelistbox.hxx create mode 100644 sw/source/uibase/inc/numfmtlb.hxx create mode 100644 sw/source/uibase/inc/numpara.hxx create mode 100644 sw/source/uibase/inc/numprevw.hxx create mode 100644 sw/source/uibase/inc/olesh.hxx create mode 100644 sw/source/uibase/inc/olmenu.hxx create mode 100644 sw/source/uibase/inc/optcomp.hxx create mode 100644 sw/source/uibase/inc/optload.hxx create mode 100644 sw/source/uibase/inc/optpage.hxx create mode 100644 sw/source/uibase/inc/outline.hxx create mode 100644 sw/source/uibase/inc/pardlg.hxx create mode 100644 sw/source/uibase/inc/pattern.hxx create mode 100644 sw/source/uibase/inc/pgfnote.hxx create mode 100644 sw/source/uibase/inc/pggrid.hxx create mode 100644 sw/source/uibase/inc/prcntfld.hxx create mode 100644 sw/source/uibase/inc/pview.hxx create mode 100644 sw/source/uibase/inc/redlndlg.hxx create mode 100644 sw/source/uibase/inc/regionsw.hxx create mode 100644 sw/source/uibase/inc/rowht.hxx create mode 100644 sw/source/uibase/inc/scroll.hxx create mode 100644 sw/source/uibase/inc/selglos.hxx create mode 100644 sw/source/uibase/inc/sharedconnection.hxx create mode 100644 sw/source/uibase/inc/shdwcrsr.hxx create mode 100644 sw/source/uibase/inc/splittbl.hxx create mode 100644 sw/source/uibase/inc/srcedtw.hxx create mode 100644 sw/source/uibase/inc/srcview.hxx create mode 100644 sw/source/uibase/inc/srtdlg.hxx create mode 100644 sw/source/uibase/inc/swcont.hxx create mode 100644 sw/source/uibase/inc/swdtflvr.hxx create mode 100644 sw/source/uibase/inc/swmessdialog.hxx create mode 100644 sw/source/uibase/inc/swmodalredlineacceptdlg.hxx create mode 100644 sw/source/uibase/inc/swrenamexnameddlg.hxx create mode 100644 sw/source/uibase/inc/swruler.hxx create mode 100644 sw/source/uibase/inc/swtablerep.hxx create mode 100644 sw/source/uibase/inc/swuiccoll.hxx create mode 100644 sw/source/uibase/inc/swuicnttab.hxx create mode 100644 sw/source/uibase/inc/swuiidxmrk.hxx create mode 100644 sw/source/uibase/inc/swuipardlg.hxx create mode 100644 sw/source/uibase/inc/swwrtshitem.hxx create mode 100644 sw/source/uibase/inc/syncbtn.hxx create mode 100644 sw/source/uibase/inc/tabledlg.hxx create mode 100644 sw/source/uibase/inc/tablemgr.hxx create mode 100644 sw/source/uibase/inc/tabsh.hxx create mode 100644 sw/source/uibase/inc/tautofmt.hxx create mode 100644 sw/source/uibase/inc/tblnumfm.hxx create mode 100644 sw/source/uibase/inc/textsh.hxx create mode 100644 sw/source/uibase/inc/titlepage.hxx create mode 100644 sw/source/uibase/inc/tmpdlg.hxx create mode 100644 sw/source/uibase/inc/tmplctrl.hxx create mode 100644 sw/source/uibase/inc/toxmgr.hxx create mode 100644 sw/source/uibase/inc/uiborder.hxx create mode 100644 sw/source/uibase/inc/uiitems.hxx create mode 100644 sw/source/uibase/inc/uinums.hxx create mode 100644 sw/source/uibase/inc/uiobject.hxx create mode 100644 sw/source/uibase/inc/uitool.hxx create mode 100644 sw/source/uibase/inc/uivwimp.hxx create mode 100644 sw/source/uibase/inc/unoatxt.hxx create mode 100644 sw/source/uibase/inc/unodispatch.hxx create mode 100644 sw/source/uibase/inc/unomailmerge.hxx create mode 100644 sw/source/uibase/inc/unomod.hxx create mode 100644 sw/source/uibase/inc/unotools.hxx create mode 100644 sw/source/uibase/inc/unotxvw.hxx create mode 100644 sw/source/uibase/inc/usrpref.hxx create mode 100644 sw/source/uibase/inc/viewlayoutctrl.hxx create mode 100644 sw/source/uibase/inc/watermarkdialog.hxx create mode 100644 sw/source/uibase/inc/wformsh.hxx create mode 100644 sw/source/uibase/inc/wfrmsh.hxx create mode 100644 sw/source/uibase/inc/wgrfsh.hxx create mode 100644 sw/source/uibase/inc/wlistsh.hxx create mode 100644 sw/source/uibase/inc/wolesh.hxx create mode 100644 sw/source/uibase/inc/wordcountctrl.hxx create mode 100644 sw/source/uibase/inc/wordcountdialog.hxx create mode 100644 sw/source/uibase/inc/workctrl.hxx create mode 100644 sw/source/uibase/inc/wrap.hxx create mode 100644 sw/source/uibase/inc/wrtsh.hxx create mode 100644 sw/source/uibase/inc/wtabsh.hxx create mode 100644 sw/source/uibase/inc/wtextsh.hxx create mode 100644 sw/source/uibase/inc/wview.hxx create mode 100644 sw/source/uibase/inc/zoomctrl.hxx create mode 100644 sw/source/uibase/index/idxmrk.cxx create mode 100644 sw/source/uibase/index/toxmgr.cxx create mode 100644 sw/source/uibase/lingu/hhcwrp.cxx create mode 100644 sw/source/uibase/lingu/hyp.cxx create mode 100644 sw/source/uibase/lingu/olmenu.cxx create mode 100644 sw/source/uibase/lingu/sdrhhcwrap.cxx create mode 100644 sw/source/uibase/lingu/sdrhhcwrap.hxx create mode 100644 sw/source/uibase/misc/glosdoc.cxx create mode 100644 sw/source/uibase/misc/glshell.cxx create mode 100644 sw/source/uibase/misc/numberingtypelistbox.cxx create mode 100644 sw/source/uibase/misc/redlndlg.cxx create mode 100644 sw/source/uibase/misc/swruler.cxx create mode 100644 sw/source/uibase/ribbar/conarc.cxx create mode 100644 sw/source/uibase/ribbar/concustomshape.cxx create mode 100644 sw/source/uibase/ribbar/conform.cxx create mode 100644 sw/source/uibase/ribbar/conpoly.cxx create mode 100644 sw/source/uibase/ribbar/conrect.cxx create mode 100644 sw/source/uibase/ribbar/drawbase.cxx create mode 100644 sw/source/uibase/ribbar/dselect.cxx create mode 100644 sw/source/uibase/ribbar/inputwin.cxx create mode 100644 sw/source/uibase/ribbar/workctrl.cxx create mode 100644 sw/source/uibase/shells/annotsh.cxx create mode 100644 sw/source/uibase/shells/basesh.cxx create mode 100644 sw/source/uibase/shells/beziersh.cxx create mode 100644 sw/source/uibase/shells/drawdlg.cxx create mode 100644 sw/source/uibase/shells/drawsh.cxx create mode 100644 sw/source/uibase/shells/drformsh.cxx create mode 100644 sw/source/uibase/shells/drwbassh.cxx create mode 100644 sw/source/uibase/shells/drwtxtex.cxx create mode 100644 sw/source/uibase/shells/drwtxtsh.cxx create mode 100644 sw/source/uibase/shells/frmsh.cxx create mode 100644 sw/source/uibase/shells/grfsh.cxx create mode 100644 sw/source/uibase/shells/grfshex.cxx create mode 100644 sw/source/uibase/shells/langhelper.cxx create mode 100644 sw/source/uibase/shells/listsh.cxx create mode 100644 sw/source/uibase/shells/mediash.cxx create mode 100644 sw/source/uibase/shells/navsh.cxx create mode 100644 sw/source/uibase/shells/olesh.cxx create mode 100644 sw/source/uibase/shells/slotadd.cxx create mode 100644 sw/source/uibase/shells/tabsh.cxx create mode 100644 sw/source/uibase/shells/textdrw.cxx create mode 100644 sw/source/uibase/shells/textfld.cxx create mode 100644 sw/source/uibase/shells/textglos.cxx create mode 100644 sw/source/uibase/shells/textidx.cxx create mode 100644 sw/source/uibase/shells/textsh.cxx create mode 100644 sw/source/uibase/shells/textsh1.cxx create mode 100644 sw/source/uibase/shells/textsh2.cxx create mode 100644 sw/source/uibase/shells/txtattr.cxx create mode 100644 sw/source/uibase/shells/txtcrsr.cxx create mode 100644 sw/source/uibase/shells/txtnum.cxx create mode 100644 sw/source/uibase/sidebar/PageColumnControl.cxx create mode 100644 sw/source/uibase/sidebar/PageColumnControl.hxx create mode 100644 sw/source/uibase/sidebar/PageColumnPopup.cxx create mode 100644 sw/source/uibase/sidebar/PageFooterPanel.cxx create mode 100644 sw/source/uibase/sidebar/PageFooterPanel.hxx create mode 100644 sw/source/uibase/sidebar/PageFormatPanel.cxx create mode 100644 sw/source/uibase/sidebar/PageFormatPanel.hxx create mode 100644 sw/source/uibase/sidebar/PageHeaderPanel.cxx create mode 100644 sw/source/uibase/sidebar/PageHeaderPanel.hxx create mode 100644 sw/source/uibase/sidebar/PageMarginControl.cxx create mode 100644 sw/source/uibase/sidebar/PageMarginControl.hxx create mode 100644 sw/source/uibase/sidebar/PageMarginPopup.cxx create mode 100644 sw/source/uibase/sidebar/PageMarginUtils.hxx create mode 100644 sw/source/uibase/sidebar/PageOrientationControl.cxx create mode 100644 sw/source/uibase/sidebar/PageOrientationControl.hxx create mode 100644 sw/source/uibase/sidebar/PageOrientationPopup.cxx create mode 100644 sw/source/uibase/sidebar/PageSizeControl.cxx create mode 100644 sw/source/uibase/sidebar/PageSizeControl.hxx create mode 100644 sw/source/uibase/sidebar/PageSizePopup.cxx create mode 100644 sw/source/uibase/sidebar/PageStylesPanel.cxx create mode 100644 sw/source/uibase/sidebar/PageStylesPanel.hxx create mode 100644 sw/source/uibase/sidebar/StylePresetsPanel.cxx create mode 100644 sw/source/uibase/sidebar/StylePresetsPanel.hxx create mode 100644 sw/source/uibase/sidebar/SwPanelFactory.cxx create mode 100644 sw/source/uibase/sidebar/TableEditPanel.cxx create mode 100644 sw/source/uibase/sidebar/TableEditPanel.hxx create mode 100644 sw/source/uibase/sidebar/ThemePanel.cxx create mode 100644 sw/source/uibase/sidebar/ThemePanel.hxx create mode 100644 sw/source/uibase/sidebar/WrapPropertyPanel.cxx create mode 100644 sw/source/uibase/sidebar/WrapPropertyPanel.hxx create mode 100644 sw/source/uibase/table/chartins.cxx create mode 100644 sw/source/uibase/table/swtablerep.cxx create mode 100644 sw/source/uibase/table/tablemgr.cxx create mode 100644 sw/source/uibase/table/tablepg.hxx create mode 100644 sw/source/uibase/uitest/uiobject.cxx create mode 100644 sw/source/uibase/uiview/formatclipboard.cxx create mode 100644 sw/source/uibase/uiview/pview.cxx create mode 100644 sw/source/uibase/uiview/scroll.cxx create mode 100644 sw/source/uibase/uiview/srcview.cxx create mode 100644 sw/source/uibase/uiview/swcli.cxx create mode 100644 sw/source/uibase/uiview/uivwimp.cxx create mode 100644 sw/source/uibase/uiview/view.cxx create mode 100644 sw/source/uibase/uiview/view0.cxx create mode 100644 sw/source/uibase/uiview/view1.cxx create mode 100644 sw/source/uibase/uiview/view2.cxx create mode 100644 sw/source/uibase/uiview/viewcoll.cxx create mode 100644 sw/source/uibase/uiview/viewdlg.cxx create mode 100644 sw/source/uibase/uiview/viewdlg2.cxx create mode 100644 sw/source/uibase/uiview/viewdraw.cxx create mode 100644 sw/source/uibase/uiview/viewfunc.hxx create mode 100644 sw/source/uibase/uiview/viewling.cxx create mode 100644 sw/source/uibase/uiview/viewmdi.cxx create mode 100644 sw/source/uibase/uiview/viewport.cxx create mode 100644 sw/source/uibase/uiview/viewprt.cxx create mode 100644 sw/source/uibase/uiview/viewsrch.cxx create mode 100644 sw/source/uibase/uiview/viewstat.cxx create mode 100644 sw/source/uibase/uiview/viewtab.cxx create mode 100644 sw/source/uibase/uno/SwXDocumentSettings.cxx create mode 100644 sw/source/uibase/uno/SwXDocumentSettings.hxx create mode 100644 sw/source/uibase/uno/SwXFilterOptions.cxx create mode 100644 sw/source/uibase/uno/dlelstnr.cxx create mode 100644 sw/source/uibase/uno/unoatxt.cxx create mode 100644 sw/source/uibase/uno/unodefaults.cxx create mode 100644 sw/source/uibase/uno/unodefaults.hxx create mode 100644 sw/source/uibase/uno/unodispatch.cxx create mode 100644 sw/source/uibase/uno/unodoc.cxx create mode 100644 sw/source/uibase/uno/unofreg.cxx create mode 100644 sw/source/uibase/uno/unomailmerge.cxx create mode 100644 sw/source/uibase/uno/unomod.cxx create mode 100644 sw/source/uibase/uno/unomodule.cxx create mode 100644 sw/source/uibase/uno/unomodule.hxx create mode 100644 sw/source/uibase/uno/unotxdoc.cxx create mode 100644 sw/source/uibase/uno/unotxvw.cxx create mode 100644 sw/source/uibase/utlui/attrdesc.cxx create mode 100644 sw/source/uibase/utlui/bookctrl.cxx create mode 100644 sw/source/uibase/utlui/condedit.cxx create mode 100644 sw/source/uibase/utlui/content.cxx create mode 100644 sw/source/uibase/utlui/glbltree.cxx create mode 100644 sw/source/uibase/utlui/gloslst.cxx create mode 100644 sw/source/uibase/utlui/gotodlg.cxx create mode 100644 sw/source/uibase/utlui/initui.cxx create mode 100644 sw/source/uibase/utlui/navicfg.cxx create mode 100644 sw/source/uibase/utlui/navipi.cxx create mode 100644 sw/source/uibase/utlui/numfmtlb.cxx create mode 100644 sw/source/uibase/utlui/prcntfld.cxx create mode 100644 sw/source/uibase/utlui/shdwcrsr.cxx create mode 100644 sw/source/uibase/utlui/tmplctrl.cxx create mode 100644 sw/source/uibase/utlui/uiitems.cxx create mode 100644 sw/source/uibase/utlui/uitool.cxx create mode 100644 sw/source/uibase/utlui/unotools.cxx create mode 100644 sw/source/uibase/utlui/viewlayoutctrl.cxx create mode 100644 sw/source/uibase/utlui/wordcountctrl.cxx create mode 100644 sw/source/uibase/utlui/zoomctrl.cxx create mode 100644 sw/source/uibase/web/wdocsh.cxx create mode 100644 sw/source/uibase/web/wformsh.cxx create mode 100644 sw/source/uibase/web/wfrmsh.cxx create mode 100644 sw/source/uibase/web/wgrfsh.cxx create mode 100644 sw/source/uibase/web/wlistsh.cxx create mode 100644 sw/source/uibase/web/wolesh.cxx create mode 100644 sw/source/uibase/web/wtabsh.cxx create mode 100644 sw/source/uibase/web/wtextsh.cxx create mode 100644 sw/source/uibase/web/wview.cxx create mode 100644 sw/source/uibase/wrtsh/delete.cxx create mode 100644 sw/source/uibase/wrtsh/move.cxx create mode 100644 sw/source/uibase/wrtsh/navmgr.cxx create mode 100644 sw/source/uibase/wrtsh/select.cxx create mode 100644 sw/source/uibase/wrtsh/wrtsh1.cxx create mode 100644 sw/source/uibase/wrtsh/wrtsh2.cxx create mode 100644 sw/source/uibase/wrtsh/wrtsh3.cxx create mode 100644 sw/source/uibase/wrtsh/wrtsh4.cxx create mode 100644 sw/source/uibase/wrtsh/wrtundo.cxx (limited to 'sw/source') diff --git a/sw/source/core/SwNumberTree/SwNodeNum.cxx b/sw/source/core/SwNumberTree/SwNodeNum.cxx new file mode 100644 index 000000000..f77b002a6 --- /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 +#include +#include +#include +#include +#include +#include +#include + +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(), + " - 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(), + " - no text node set at instance" ); + return SwPosition(*mpTextNode); +} + +SwNumberTreeNode * SwNodeNum::Create() const +{ + SwNodeNum * pResult = new SwNodeNum( GetNumRule() ); + + return pResult; +} + +void SwNodeNum::PreAdd() +{ + OSL_ENSURE( GetTextNode(), + " - no text node set at instance" ); + if ( !GetNumRule() && GetTextNode() ) + { + mpNumRule = GetTextNode()->GetNumRule(); + } + OSL_ENSURE( GetNumRule(), + " - no list style set at 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(), + " - no text node set at instance" ); + OSL_ENSURE( GetNumRule(), + " - no list style set at instance" ); + + if (!m_isHiddenRedlines && GetTextNode()) + { + GetTextNode()->getIDocumentListItems().removeListItem( *this ); + } + + if ( GetNumRule() ) + { + if (!m_isHiddenRedlines && GetTextNode()) + { + GetNumRule()->RemoveTextNode( *(GetTextNode()) ); + } + mpNumRule = nullptr; + } +} + +bool SwNodeNum::IsNotifiable() const +{ + bool aResult = true; + + if ( GetTextNode() ) + aResult = GetTextNode()->IsNotifiable(); + + return aResult; +} + +bool SwNodeNum::IsNotificationEnabled() const +{ + bool aResult = true; + + if ( GetTextNode() ) + aResult = GetTextNode()->IsNotificationEnabled(); + + return aResult; +} + +bool SwNodeNum::IsContinuous() const +{ + bool aResult = false; + + // #i64311# + if ( GetNumRule() ) + { + aResult = mpNumRule->IsContinusNum(); + } + else if ( GetParent() ) + { + aResult = GetParent()->IsContinuous(); + } + else + { + OSL_FAIL( " - OD debug" ); + } + + return aResult; +} + +bool SwNodeNum::IsCounted() const +{ + bool aResult = false; + + if ( GetTextNode() ) + { + // #i59559# + // 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(pNode) ); + OSL_ENSURE( pChild, " - 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(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( "GetActualListStartValue(); + } + else + { + SwNumRule * pRule = GetNumRule(); + + if (pRule) + { + int nLevel = GetParent() ? GetLevelInListTree() : 0; + + if (nLevel >= 0 && nLevel < MAXLEVEL) + { + const SwNumFormat * pFormat = pRule->GetNumFormat( static_cast(nLevel)); + + if (pFormat) + aResult = pFormat->GetStart(); + } + } + } + + return aResult; +} + +void SwNodeNum::HandleNumberTreeRootNodeDelete( SwNodeNum& rNodeNum ) +{ + SwNodeNum* pRootNode = rNodeNum.GetParent() + ? dynamic_cast(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((*rNodeNum.mChildren.begin())) ); + if ( !pChildNode ) + { + OSL_FAIL( " - unknown number tree node child" ); + ++nAllowedChildCount; + continue; + } + + // Unregistering the last child of a phantom will destroy the phantom. + // Thus will be destroyed and access on has to + // be suppressed. + if ( bIsPhantom && rNodeNum.GetChildCount() == 1 ) + { + bDone = true; + } + + UnregisterMeAndChildrenDueToRootDelete( *pChildNode ); + } + + if ( !bIsPhantom ) + { + SwTextNode* pTextNode( rNodeNum.GetTextNode() ); + if ( pTextNode ) + { + pTextNode->RemoveFromList(); + // --> clear all list attributes and the list style + std::set aResetAttrsArray; + aResetAttrsArray.insert( aResetAttrsArray.end(), RES_PARATR_LIST_ID ); + aResetAttrsArray.insert( aResetAttrsArray.end(), RES_PARATR_LIST_LEVEL ); + aResetAttrsArray.insert( aResetAttrsArray.end(), RES_PARATR_LIST_ISRESTART ); + aResetAttrsArray.insert( aResetAttrsArray.end(), RES_PARATR_LIST_RESTARTVALUE ); + aResetAttrsArray.insert( aResetAttrsArray.end(), RES_PARATR_LIST_ISCOUNTED ); + aResetAttrsArray.insert( aResetAttrsArray.end(), 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(&rTextNode), false/*doesn't matter*/ ); + + pPrecedingNodeNum = dynamic_cast( + 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..2b2ab634b --- /dev/null +++ b/sw/source/core/SwNumberTree/SwNumberTree.cxx @@ -0,0 +1,1182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include + +using std::vector; +using std::find; + +SwNumberTreeNode::SwNumberTreeNode() + : mChildren(), + mpParent( nullptr ), + mnNumber( 0 ), + mbContinueingPreviousSubTree( false ), + mbPhantom( false ), + mItLastValid() +{ + 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(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 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()) + { + (*aIt)->ClearObsoletePhantoms(); + + if ((*aIt)->mChildren.empty()) + { + // #i60652# + // Because could destroy the element, which + // is referenced by , it's needed to adjust + // before erasing . + 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()) + { + OSL_ENSURE((*aValidateIt)->mpParent == this, "wrong parent"); + + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + // --> + // improvement: + // - Only one time checked for . + // - 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 could destroy + // the element, which is referenced by , it's needed to + // adjust before erasing . + 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 could destroy the element, + // which is referenced by , it's needed to adjust + // before erasing . + 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(); + // destroys all existing iterators. + // Thus, 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 ) +{ + /* + 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( " - parameter 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); + } + else + { + --aInsertDeepIt; + (*aInsertDeepIt)->AddChild(pChild, nDepth - 1); + } + + } + else + { + pChild->PreAdd(); + std::pair aResult = + mChildren.insert(pChild); + + if (aResult.second) + { + pChild->mpParent = this; + bool bNotification = pChild->IsNotificationEnabled(); + 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 and + // 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 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 unnessary created phantoms at 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(); + } + NotifyInvalidChildren(); + } + } + } + +#ifdef DBG_UTIL + IsSane(false); +#endif +} + +void SwNumberTreeNode::RemoveChild(SwNumberTreeNode * pChild) +{ + /* + 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(); + } + + // #i60652# + // Because could destroy the element, + // which is referenced by , it's needed to adjust + // before erasing . + if (aItPred != mChildren.end() && (*aItPred)->IsPhantom()) + SetLastValid(mChildren.end()); + else + SetLastValid(aItPred); + + mChildren.erase(aRemoveIt); + + NotifyInvalidChildren(); + } + else + { + OSL_FAIL("RemoveChild: failed!"); + } + + pChild->PostRemove(); +} + +void SwNumberTreeNode::RemoveMe() +{ + if (mpParent) + { + SwNumberTreeNode * pSavedParent = mpParent; + + pSavedParent->RemoveChild(this); + + 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 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(), + " - 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 ) +{ + if ( nLevel < 0 ) + { + OSL_FAIL( " - parameter out of valid range. Serious defect." ); + return; + } + + OSL_ENSURE( GetParent(), + " - can only be called for number tree nodes in a list tree" ); + if ( GetParent() ) + { + if ( nLevel != GetLevelInListTree() ) + { + SwNumberTreeNode* pRootTreeNode = GetRoot(); + OSL_ENSURE( pRootTreeNode, + " - no root tree node found. Serious defect." ); + + RemoveMe(); + pRootTreeNode->AddChild( this, nLevel ); + } + } +} + +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 aParents; + + return IsSane(bRecursive, aParents); +} + +void SwNumberTreeNode::IsSane(bool bRecursive, + vector 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(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() +{ + if (IsNotifiable()) + { + if (! IsPhantom()) + NotifyNode(); + + for (auto& rpChild : mChildren) + rpChild->Notify(); + } +} + +void SwNumberTreeNode::NotifyInvalidChildren() +{ + if (IsNotifiable()) + { + tSwNumberTreeChildren::const_iterator aIt = mItLastValid; + + if (aIt == mChildren.end()) + aIt = mChildren.begin(); + else + ++aIt; + + while (aIt != mChildren.end()) + { + (*aIt)->Notify(); + + ++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(); + } + } + } + + } + + if (IsContinuous() && mpParent) + mpParent->NotifyInvalidChildren(); +} + +void SwNumberTreeNode::NotifyInvalidSiblings() +{ + if (mpParent != nullptr) + mpParent->NotifyInvalidChildren(); +} + +// #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(&rNode) ); + if ( aUpperBoundIt != mChildren.begin() ) + { + --aUpperBoundIt; + pPrecedingNode = (*aUpperBoundIt)->GetPrecedingNodeOf( rNode ); + } + } + + if ( pPrecedingNode == nullptr && GetRoot() ) + { + // node has no children or the given node precedes all its children + // and the node isn't the root node. + // Thus, compare the given node with the node in order to check, + // if the node precedes the given node. + if ( !(rNode.LessThan( *this )) ) + { + pPrecedingNode = this; + } + } + + return pPrecedingNode; +} + +void SwNumberTreeNode::NotifyNodesOnListLevel( const int nListLevel ) +{ + if ( nListLevel < 0 ) + { + OSL_FAIL( " - invalid list level provided" ); + return; + } + + SwNumberTreeNode* pRootNode = GetParent() ? GetRoot() : this; + + pRootNode->NotifyChildrenOnDepth( nListLevel ); +} + +void SwNumberTreeNode::NotifyChildrenOnDepth( const int nDepth ) +{ + OSL_ENSURE( nDepth >= 0, + " - 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..b59596da9 --- /dev/null +++ b/sw/source/core/access/AccessibilityCheck.cxx @@ -0,0 +1,811 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sw +{ +namespace +{ +std::shared_ptr +lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText, + sfx::AccessibilityIssueID eIssue = sfx::AccessibilityIssueID::UNSPECIFIED) +{ + auto pIssue = std::make_shared(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()) + { + 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* pDoc) + { + 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(pDoc); + pIssue->setIssueObject(IssueObject::TABLE); + pIssue->setObjectID(sName); + } + + void checkTableNode(SwTableNode* pTableNode) + { + if (!pTableNode) + return; + + SwTable const& rTable = pTableNode->GetTable(); + SwDoc* pDoc = pTableNode->GetDoc(); + if (rTable.IsTableComplex()) + { + addTableIssue(rTable, pDoc); + } + 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, pDoc); + } + } + } + } + +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> 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()) + { + 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 const& xTextRange) + { + uno::Reference xProperties(xTextRange, uno::UNO_QUERY); + if (xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL")) + { + 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()) + { + SwTextNode* pTextNode = pCurrent->GetTextNode(); + uno::Reference xParagraph + = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode); + if (xParagraph.is()) + { + uno::Reference xRunEnumAccess(xParagraph, + uno::UNO_QUERY); + uno::Reference xRunEnum + = xRunEnumAccess->createEnumeration(); + while (xRunEnum->hasMoreElements()) + { + uno::Reference 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 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 const& xTextRange, + uno::Reference const& xParagraph, SwTextNode* pTextNode) + { + sal_Int32 nParaBackColor = {}; // spurious -Werror=maybe-uninitialized + uno::Reference xParagraphProperties(xParagraph, uno::UNO_QUERY); + if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor)) + { + SAL_WARN("sw.a11y", "ParaBackColor void"); + return; + } + + uno::Reference xProperties(xTextRange, uno::UNO_QUERY); + if (xProperties.is()) + { + // 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(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(XATTR_FILLSTYLE, false)); + Color aPageBackground; + + if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID) + { + const XFillColorItem* rXFillColorItem + = rPageSet.GetItem(XATTR_FILLCOLOR, false); + aPageBackground = rXFillColorItem->GetColorValue(); + } + + sal_Int32 nCharBackColor = {}; // spurious -Werror=maybe-uninitialized + + 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 = Color(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()) + { + SwTextNode* pTextNode = pCurrent->GetTextNode(); + uno::Reference xParagraph; + xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode); + if (xParagraph.is()) + { + uno::Reference xRunEnumAccess(xParagraph, + uno::UNO_QUERY); + uno::Reference xRunEnum + = xRunEnumAccess->createEnumeration(); + while (xRunEnum->hasMoreElements()) + { + uno::Reference 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 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()) + { + 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* pDocument = pTextNode->GetDoc(); + pIssue->setDoc(pDocument); + pIssue->setStart(pTextAttr->GetStart()); + pIssue->setEnd(pTextAttr->GetAnyEnd()); + } + } + void check(SwNode* pCurrent) override + { + if (pCurrent->IsTextNode()) + { + 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 const& xTextRange) + { + uno::Reference 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()) + { + SwTextNode* pTextNode = pCurrent->GetTextNode(); + uno::Reference xParagraph; + xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode); + if (xParagraph.is()) + { + uno::Reference xRunEnumAccess(xParagraph, + uno::UNO_QUERY); + uno::Reference xRunEnum + = xRunEnumAccess->createEnumeration(); + while (xRunEnum->hasMoreElements()) + { + uno::Reference 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()) + { + SwTextNode* pTextNode = pCurrent->GetTextNode(); + SwTextFormatColl* pCollection = pTextNode->GetTextColl(); + int nLevel = pCollection->GetAssignedOutlineStyleLevel(); + if (nLevel < 0) + return; + + if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1) + { + lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER)); + } + m_nPreviousLevel = nLevel; + } + } +}; + +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) + { + const uno::Reference xDPS(pShell->GetModel(), + uno::UNO_QUERY_THROW); + const uno::Reference xDocumentProperties( + xDPS->getDocumentProperties()); + OUString sTitle = xDocumentProperties->getTitle(); + if (sTitle.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; + + if (pObject->GetObjIdentifier() == OBJ_CUSTOMSHAPE || pObject->GetObjIdentifier() == OBJ_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> aDocumentChecks; + aDocumentChecks.push_back(std::make_unique(m_aIssueCollection)); + aDocumentChecks.push_back(std::make_unique(m_aIssueCollection)); + aDocumentChecks.push_back(std::make_unique(m_aIssueCollection)); + + for (std::unique_ptr& rpDocumentCheck : aDocumentChecks) + { + rpDocumentCheck->check(m_pDoc); + } + + std::vector> aNodeChecks; + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + aNodeChecks.push_back(std::make_unique(m_aIssueCollection)); + + auto const& pNodes = m_pDoc->GetNodes(); + SwNode* pNode = nullptr; + for (sal_uLong n = 0; n < pNodes.Count(); ++n) + { + pNode = pNodes[n]; + if (pNode) + { + for (std::unique_ptr& 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..991ec866a --- /dev/null +++ b/sw/source/core/access/AccessibilityIssue.cxx @@ -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/. + * + */ + +#include +#include +#include + +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* pDoc) { m_pDoc = pDoc; } + +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); + } + break; + case IssueObject::TABLE: + { + SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->GotoTable(m_sObjectID); + } + 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(); + } + 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..ee533636e --- /dev/null +++ b/sw/source/core/access/acccell.cxx @@ -0,0 +1,467 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accfrmobj.hxx" +#include "accfrmobjslist.hxx" +#include +#include +#include +#include "acccell.hxx" + +#include + +#include +#include +#include +#include "acctable.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace sw::access; + +const char sImplementationName[] = "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(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( 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 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 xTableReference( + getAccessibleParentImpl()); + css::uno::Reference xContextTable( + xTableReference, css::uno::UNO_QUERY); + SAL_WARN_IF( + (!xContextTable.is() + || xContextTable->getAccessibleRole() != AccessibleRole::TABLE), + "sw.a11y", "bad accessible context"); + m_pAccTable = static_cast(xTableReference.get()); +} + +bool SwAccessibleCell::InvalidateMyCursorPos() +{ + bool bNew = IsSelected(); + bool bOld; + { + osl::MutexGuard 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() +{ + osl::MutexGuard 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::get()) + { + uno::Any aR; + aR <<= uno::Reference(this); + return aR; + } + + if (rType == cppu::UnoType::get()) + { + uno::Any aR; + aR <<= uno::Reference(this); + return aR; + } + if ( rType == ::cppu::UnoType::get() ) + { + uno::Reference 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::get(), + SwAccessibleContext::getTypes() ).getTypes(); +} + +uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleCell::getImplementationId() +{ + return css::uno::Sequence(); +} + +// XAccessibleValue + +SwFrameFormat* SwAccessibleCell::GetTableBoxFormat() const +{ + assert(GetFrame()); + assert(GetFrame()->IsCellFrame()); + + const SwCellFrame* pCellFrame = static_cast( 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); +} + +static OUString ReplaceOneChar(const OUString& oldOUString, const OUString& replacedChar, const OUString& 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,"\\","\\\\"); + aRet = ReplaceOneChar(aRet,";","\\;"); + aRet = ReplaceOneChar(aRet,"=","\\="); + aRet = ReplaceOneChar(aRet,",","\\,"); + aRet = ReplaceOneChar(aRet,":","\\:"); + 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 xAccDoc = getAccessibleParent(); + if (xAccDoc.is()) + { + uno::Reference xComponentDoc(xAccDoc, uno::UNO_QUERY); + if (xComponentDoc.is()) + { + crBack = Color(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 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..4caa25cad --- /dev/null +++ b/sw/source/core/access/acccell.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 "acccontext.hxx" +#include +#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 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 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( ) throw () override + { SwAccessibleContext::acquire(); }; + + virtual void SAL_CALL release( ) throw () 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; + + // XAccessibleValue + + // XAccessibleExtendedAttributes + css::uno::Any SAL_CALL getExtendedAttributes() override ; +private: + SwFrameFormat* GetTableBoxFormat() const; + +public: + 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; + // 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..3b9f0f1e6 --- /dev/null +++ b/sw/source/core/access/acccontext.cxx @@ -0,0 +1,1526 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accfrmobjslist.hxx" +#include "acccontext.hxx" +#include +#include +#include +#include + +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 ) +{ + osl::MutexGuard aGuard( m_Mutex ); + + uno::Reference < XAccessible > xParent( pParent ); + m_xWeakParent = xParent; +} + +uno::Reference< XAccessible > SwAccessibleContext::GetWeakParent() const +{ + osl::MutexGuard 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( 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( 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.IsOver( rNewVisArea ) ) + { + if( aBox.IsOver( rOldVisArea ) ) + { + eAction = Action::SCROLLED_WITHIN; + } + else + { + if ( bVisibleChildrenOnly && + !rLower.AlwaysIncludeAsChild() ) + { + eAction = Action::SCROLLED_IN; + } + else + { + eAction = Action::SCROLLED; + } + } + } + else if( aBox.IsOver( 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(), + " - 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(), + " - 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(), + " - not always included child not considered!" ); + } + } + } + else if ( rLower.GetSwFrame() && + ( !bVisibleChildrenOnly || + aBox.IsOver( rOldVisArea ) || + aBox.IsOver( 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()) ); + { + osl::MutexGuard 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() ) + { + 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 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; + { + osl::MutexGuard aGuard( m_Mutex ); + bRet = m_isEditableState; + } + + return bRet; +} + +void SwAccessibleContext::ThrowIfDisposed() +{ + if (!(GetFrame() && GetMap())) + { + throw lang::DisposedException("object is nonfunctional", + static_cast(this)); + } +} + +SwAccessibleContext::SwAccessibleContext(std::shared_ptr const& pMap, + sal_Int16 const nRole, + const SwFrame *pF ) + : SwAccessibleFrame( pMap->GetVisArea().SVRect(), 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 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> SAL_CALL + SwAccessibleContext::getAccessibleChildren() +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + std::list< sw::access::SwAccessibleChild > aChildren; + GetChildren( *GetMap(), aChildren ); + + std::vector> 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. + { + osl::MutexGuard 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 SAL_CALL + SwAccessibleContext::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + ::utl::AccessibleStateSetHelper *pStateSet = + new ::utl::AccessibleStateSetHelper; + + if (m_isSelectedInDoc) + pStateSet->AddState( AccessibleStateType::SELECTED ); + + uno::Reference xStateSet( pStateSet ); + GetStates( *pStateSet ); + + return xStateSet; +} + +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) + { + 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) +{ + long nDiffX = aPoint.X - aRect.X; + 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(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.SVRect() ).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(this)); + } + if (!pWin) + { + throw uno::RuntimeException("no Window", static_cast(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 GetPreviewPageSize()> + sal_uInt16 nPageNum = + static_cast < const SwPageFrame * >( GetFrame() )->GetPhyPageNum(); + aLogBounds.SSize( GetMap()->GetPreviewPageSize( nPageNum ) ); + } + } + if( !aLogBounds.IsEmpty() ) + { + aPixBounds = GetMap()->CoreToPixel( aLogBounds.SVRect() ); + if( !pParent->IsRootFrame() && bRelative) + { + SwRect aParentLogBounds( GetBounds( *(GetMap()), pParent ) ); // twip rel to doc root + Point aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds.SVRect() ).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(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.get() ); + 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 ) ) + { + 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(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) + { + osl::MutexGuard 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()) ); + { + osl::MutexGuard 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( " - 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( " - not expected to handle dispose of child of type ." ); + } + } + } +} + +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() ) + { + SwViewShell *pVSh = GetMap()->GetShell(); + if( pVSh ) + { + if( _nStates & AccessibleStates::EDITABLE ) + { + bool bIsOldEditableState; + bool bIsNewEditableState = IsEditable( pVSh ); + { + osl::MutexGuard 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 ); + { + osl::MutexGuard 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( pCursorShell) != nullptr + ? static_cast( pCursorShell ) + : nullptr; + // Get rid of activated OLE object + if( pFEShell ) + pFEShell->FinishOLEObj(); + + SwWrtShell* pWrtShell = dynamic_cast( pCursorShell) != nullptr + ? static_cast( pCursorShell ) + : nullptr; + + 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(const char* 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..491530558 --- /dev/null +++ b/sw/source/core/access/acccontext.hxx @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include + +#include + +namespace vcl { class Window; } +class SwCursorShell; +class SdrObject; +class SwPaM; +namespace utl { + class AccessibleStateSetHelper; +} +namespace accessibility { + class AccessibleShape; +} + +const char sAccessibleServiceName[] = "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 ::osl::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 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 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> 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 bSeleted); + bool IsSeletedInDoc() const { return m_isSelectedInDoc; } + + static OUString GetResource(const char* 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..bf2c0e844 --- /dev/null +++ b/sw/source/core/access/accdoc.cxx @@ -0,0 +1,719 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accdoc.hxx" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +const char sServiceName[] = "com.sun.star.text.AccessibleTextDocumentView"; +const char sImplementationName[] = "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 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 instead of + // 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(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(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(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(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(this)); + } + + tools::Rectangle aPixBounds( pWin->GetWindowExtentsRelative( nullptr ) ); + aPixBounds.Move(-aPixBounds.Left(), -aPixBounds.Top()); + + Point aPixPoint( aPoint.X, aPoint.Y ); + return aPixBounds.IsInside( 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(this)); + } + + Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to window + if( mpChildWin->GetWindowExtentsRelative( pWin ).IsInside( 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 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::get() ) + { + uno::Reference aSelect = this; + aRet <<= aSelect; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference 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::get(), + SwAccessibleDocumentBase::getTypes() ).getTypes(); +} + +uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleDocument::getImplementationId() +{ + return css::uno::Sequence(); +} + +// 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 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 anyAtrribute; + SwDoc *pDoc = GetMap() ? GetShell()->GetDoc() : nullptr; + + if (!pDoc) + return anyAtrribute; + SwCursorShell* pCursorShell = GetCursorShell(); + if( !pCursorShell ) + return anyAtrribute; + + SwFEShell* pFEShell = dynamic_cast( pCursorShell) != nullptr + ? static_cast( pCursorShell ) + : nullptr; + OUString sValue; + sal_uInt16 nPage, nLogPage; + OUString sDisplay; + + if( pFEShell ) + { + pFEShell->GetPageNumber(-1,true,nPage,nLogPage,sDisplay); + + sValue = "page-name:" + sDisplay + + ";page-number:" + + OUString::number( nPage ) + + ";total-pages:" + + OUString::number( pCursorShell->GetPageCnt() ) + ";"; + + SwContentFrame* pCurrFrame = pCursorShell->GetCurrFrame(); + SwPageFrame* pCurrPage=static_cast(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(static_cast(pSwFrame)); + } + } + else + { + assert(dynamic_cast(pCurrFrame)); + pCurrTextFrame = static_cast(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(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 != 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(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(pCurrFrame)->FindColFrame(); + + sValue += "column-number:"; + + int nCurrCol = 1; + if(pCurrCol!=nullptr) + { + //SwLayoutFrame* pParent = pCurrCol->GetUpper(); + SwFrame* pCurrPageCol=static_cast(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(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 ) + ";"; + } + + anyAtrribute <<= sValue; + } + return anyAtrribute; +} + +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..452a62a33 --- /dev/null +++ b/sw/source/core/access/accdoc.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include "accselectionhelper.hxx" + +// base class for SwAccessibleDocument (in this same header file) and +// SwAccessiblePreview +class SwAccessibleDocumentBase : public SwAccessibleContext +{ + css::uno::Reference< css::accessibility::XAccessible> mxParent; + + VclPtr mpChildWin; // protected by solar mutex + + using SwAccessibleFrame::SetVisArea; + +protected: + virtual ~SwAccessibleDocumentBase() override; + +public: + SwAccessibleDocumentBase(std::shared_ptr 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 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( ) throw () override + { SwAccessibleContext::acquire(); }; + + virtual void SAL_CALL release( ) throw () 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..58d9a7672 --- /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 +#include +#include +#include +#include +#include "accembedded.hxx" +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; + +const char sImplementationName[] = "com.sun.star.comp.Writer.SwAccessibleEmbeddedObject"; + +SwAccessibleEmbeddedObject::SwAccessibleEmbeddedObject( + std::shared_ptr 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() + throw () +{ + SwAccessibleNoTextFrame::acquire (); +} + +void SAL_CALL + SwAccessibleEmbeddedObject::release() + throw () +{ + 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(); +} + +// 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(pCFrame)->GetNode(); + if( pCNode ) + { + style += static_cast(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..86fdcc9cd --- /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 + +class SwAccessibleEmbeddedObject : public SwAccessibleNoTextFrame + , public css::accessibility::XAccessibleExtendedAttributes + +{ +protected: + virtual ~SwAccessibleEmbeddedObject() override; + +public: + SwAccessibleEmbeddedObject(std::shared_ptr const& pInitMap, + const SwFlyFrame* pFlyFrame ); + + // XInterface + + virtual css::uno::Any SAL_CALL + queryInterface (const css::uno::Type & rType) override; + + virtual void SAL_CALL + acquire() + throw () override; + + virtual void SAL_CALL + release() + throw () 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..eb843ca36 --- /dev/null +++ b/sw/source/core/access/accfootnote.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 +#include +#include +#include +#include +#include +#include +#include +#include "accfootnote.hxx" +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; + +const char sImplementationNameFootnote[] = "com.sun.star.comp.Writer.SwAccessibleFootnoteView"; +const char sImplementationNameEndnote[] = "com.sun.star.comp.Writer.SwAccessibleEndnoteView"; + +SwAccessibleFootnote::SwAccessibleFootnote( + std::shared_ptr const& pInitMap, + bool bIsEndnote, + const SwFootnoteFrame *pFootnoteFrame ) : + SwAccessibleContext( pInitMap, + bIsEndnote ? AccessibleRole::END_NOTE : AccessibleRole::FOOTNOTE, + pFootnoteFrame ) +{ + const char* 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(); + + const char* 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(); +} + +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 +#include "acccontext.hxx" + +class SwAccessibleMap; +class SwFootnoteFrame; + +class SwAccessibleFootnote : public SwAccessibleContext +{ +protected: + virtual ~SwAccessibleFootnote() override; + +public: + SwAccessibleFootnote( std::shared_ptr 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..a3ece547a --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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.SVRect() ) ); + if( aPixBounds.IsInside( 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.SVRect() ) ); + if( aPixBounds.IsInside( 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().GetTransparency() || + 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().GetTransparency() != 0) && + (rBack.GetColor() != COL_TRANSPARENT) + ) + return true; + + if( pFrame->IsSctFrame() ) + { + const SwSection* pSection = static_cast(pFrame)->GetSection(); + if( pSection && ( SectionType::ToxHeader == pSection->GetType() || + SectionType::ToxContent == pSection->GetType() ) && + !pVOpt->IsReadonly() && + SwViewOption::IsIndexShadings() ) + return true; + } + if( pFrame->IsFlyFrame() ) + aFrame = static_cast(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..c3ebd59d4 --- /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 + +#include +#include + +#include +#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 + 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.IsOver( 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..cfbfe9ecf --- /dev/null +++ b/sw/source/core/access/accframebase.cxx @@ -0,0 +1,374 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accframebase.hxx" + +#include +#include +#include +#include +#include + +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(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(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(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 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(pFrameFormat)->GetNotifier()); + + SetName( pFrameFormat->GetName() ); + + m_bIsSelected = IsSelected(); +} + +void SwAccessibleFrameBase::InvalidateCursorPos_() +{ + bool bNewSelected = IsSelected(); + bool bOldSelected; + + { + osl::MutexGuard 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() && bNewSelected ) + FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected ); + if( pWin && pWin->HasFocus() && !bNewSelected ) + FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected ); + if(bNewSelected) + { + uno::Reference< XAccessible > xParent( GetWeakParent() ); + if( xParent.is() ) + { + SwAccessibleContext *pAcc = + static_cast ( 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 ) + { + bool bSelected; + + { + osl::MutexGuard aGuard( m_Mutex ); + bSelected = m_bIsSelected; + } + assert(bSelected && "focus object should be selected"); + + FireStateChangedEvent( AccessibleStateType::FOCUSED, + pWin->HasFocus() && bSelected ); + } +} + +bool SwAccessibleFrameBase::HasCursor() +{ + osl::MutexGuard aGuard( m_Mutex ); + return m_bIsSelected; +} + +SwAccessibleFrameBase::~SwAccessibleFrameBase() +{ +} + +void SwAccessibleFrameBase::Notify(const SfxHint& rHint) +{ + if(rHint.GetId() == SfxHintId::Dying) + { + EndListeningAll(); + } + else if(auto pLegacyModifyHint = dynamic_cast(&rHint)) + { + sal_uInt16 nWhich = pLegacyModifyHint->m_pOld ? pLegacyModifyHint->m_pOld->Which() : pLegacyModifyHint->m_pNew ? pLegacyModifyHint->m_pNew->Which() : 0; + const SwFlyFrame* pFlyFrame = static_cast(GetFrame()); + if(nWhich == RES_NAME_CHANGED && pFlyFrame) + { + const SwFrameFormat* pFrameFormat = pFlyFrame->GetFormat(); + + const OUString sOldName( GetName() ); + assert( !pLegacyModifyHint->m_pOld || + static_cast(pLegacyModifyHint->m_pOld)->GetString() == GetName()); + + SetName( pFrameFormat->GetName() ); + assert( !pLegacyModifyHint->m_pNew || + static_cast(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( pCursorShell) != nullptr + ? static_cast< SwFEShell * >( pCursorShell ) : nullptr; + 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(); + sal_uLong 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(); + sal_uLong nStartIndex = pStart->nNode.GetIndex(); + SwPosition* pEnd = pCursor->End(); + sal_uLong 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( const_cast( 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..c38424a3b --- /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 +#include + +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 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 bSeleted ) 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 +#include "acccontext.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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(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( 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(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(pContact)); + SwPageFrame const*const pPage(const_cast( + 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 + +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 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..f088ff8fd --- /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 +#include "acccontext.hxx" + +#include +#include +#include +#include +#include +#include + +#include + +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 ).IsOver( 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 ).IsOver( 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 ).IsOver( 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 +#include +#include "accfrmobj.hxx" +#include + +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 value_type; + typedef SwAccessibleChildMapKey key_compare; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; + typedef std::map::const_reverse_iterator const_reverse_iterator; + +private: + const SdrLayerID mnHellId; + const SdrLayerID mnControlsId; + std::map 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 + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(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..7eca43a7b --- /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 +#include "acccontext.hxx" + +#include +#include +#include + +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 ).IsOver( 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() ).IsOver( 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 + +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 +#include +#include +#include +#include +#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 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(); +} + +// Return this object's role. +sal_Int16 SAL_CALL SwAccessibleGraphic::getAccessibleRole() +{ + SolarMutexGuard g; + + SwFormatURL aURL( static_cast(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 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..b40bcf58d --- /dev/null +++ b/sw/source/core/access/accheaderfooter.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include "accheaderfooter.hxx" +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +const char sImplementationNameHeader[] = "com.sun.star.comp.Writer.SwAccessibleHeaderView"; +const char sImplementationNameFooter[] = "com.sun.star.comp.Writer.SwAccessibleFooterView"; + +SwAccessibleHeaderFooter::SwAccessibleHeaderFooter( + std::shared_ptr 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 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(); + + const char* 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_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..f948311c7 --- /dev/null +++ b/sw/source/core/access/accheaderfooter.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_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 const& pInitMap, + const SwHeaderFrame* pHdFrame ); + SwAccessibleHeaderFooter( std::shared_ptr 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..74f9ac443 --- /dev/null +++ b/sw/source/core/access/acchyperlink.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(&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(pTextAttr)->SetVisited(true); + const_cast(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() ) + { + ::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()); + OUString sToken = "#"; + sal_Int32 nPos = sText.indexOf(sToken); + 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 +#include +#include +#include + +class SwFormatINetFormat; +class SwAccessibleParagraph; +class SwTextAttr; + +class SwAccessibleHyperlink + : public ::cppu::WeakImplHelper + , 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..c62465d12 --- /dev/null +++ b/sw/source/core/access/acchypertextdata.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 "acchyperlink.hxx" +#include "acchypertextdata.hxx" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +SwAccessibleHyperTextData::SwAccessibleHyperTextData() : + maMap() +{ +} + +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 +#include + +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 value_type; + typedef std::less< const SwTextAttr * > key_compare; + typedef std::map::iterator iterator; +private: + std::map 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 + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(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..dd63845d4 --- /dev/null +++ b/sw/source/core/access/accmap.cxx @@ -0,0 +1,3527 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "acccontext.hxx" +#include "accdoc.hxx" +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::sw::access; + +namespace { + +struct SwFrameFunc +{ + bool operator()( const SwFrame * p1, const SwFrame * p2) const + { + return p1 < p2; + } +}; + +} + +class SwAccessibleContextMap_Impl +{ +public: + typedef const SwFrame * key_type; + typedef uno::WeakReference < XAccessible > mapped_type; + typedef std::pair value_type; + typedef SwFrameFunc key_compare; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; +private: + std::map 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 + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(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::OInterfaceContainerHelper2 maEventListeners; + std::unordered_multimap, 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( &rHint ); + if (pSdrHint->GetObject() && + ( dynamic_cast< const SwFlyDrawObj* >(pSdrHint->GetObject()) != nullptr || + dynamic_cast< const SwVirtFlyDrawObj* >(pSdrHint->GetObject()) != nullptr || + typeid(SdrObject) == typeid(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::OInterfaceIteratorHelper2 aIter( maEventListeners ); + while( aIter.hasMoreElements() ) + { + uno::Reference < document::XEventListener > xListener( aIter.next(), + uno::UNO_QUERY ); + try + { + xListener->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(pSdrHint->GetObject()); + uno::Reference 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; +} + +namespace { + +struct SwShapeFunc +{ + bool operator()( const SdrObject * p1, const SdrObject * p2) const + { + return p1 < p2; + } +}; + +} + +typedef std::pair < const SdrObject *, ::rtl::Reference < ::accessibility::AccessibleShape > > SwAccessibleObjShape_Impl; + +class SwAccessibleShapeMap_Impl +{ +public: + + typedef const SdrObject * key_type; + typedef uno::WeakReference mapped_type; + typedef std::pair value_type; + typedef SwShapeFunc key_compare; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; + +private: + + ::accessibility::AccessibleShapeTreeInfo maInfo; + std::map maMap; + +public: + + explicit SwAccessibleShapeMap_Impl( SwAccessibleMap const *pMap ) + : maMap() + { + maInfo.SetSdrView( pMap->GetShell()->GetDrawView() ); + maInfo.SetDevice( 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 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 + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(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 + SwAccessibleShapeMap_Impl::Copy( + size_t& rSize, const SwFEShell *pFESh, + SwAccessibleObjShape_Impl **pSelStart ) const +{ + std::unique_ptr 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" ); + } + + // only used in method + 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( xTmp.get() ) ); + + return xAccImpl; + } + + const SwRect& GetOldBox() const + { + return maOldBox; + } + // only used in method + void SetOldBox( const SwRect& rOldBox ) + { + maOldBox = rOldBox; + } + + const SwAccessibleChild& GetFrameOrObj() const + { + return maFrameOrObj; + } + + // only used in method + 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 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::iterator begin() { return maEvents.begin(); } + std::list::iterator end() { return maEvents.end(); } + std::list::iterator insert( const std::list::iterator& aIter, + const SwAccessibleEvent_Impl& rEvent ) + { + return maEvents.insert( aIter, rEvent ); + } + std::list::iterator erase( const std::list::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::iterator mapped_type; + typedef std::pair value_type; + typedef SwAccessibleChildFunc key_compare; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; +private: + std::map maMap; +public: + iterator end() { return maMap.end(); } + iterator find(const key_type& key) { return maMap.find(key); } + template + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(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& _rXAccWeakRef1, + const uno::WeakReference& _rXAccWeakRef2 ) const + { + return _rXAccWeakRef1.get() < _rXAccWeakRef2.get(); + } +}; + +} + +class SwAccessibleSelectedParas_Impl +{ +public: + typedef uno::WeakReference < XAccessible > key_type; + typedef SwAccessibleParaSelection mapped_type; + typedef std::pair value_type; + typedef SwXAccWeakRefComp key_compare; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; +private: + std::map maMap; +public: + iterator begin() { return maMap.begin(); } + iterator end() { return maMap.end(); } + iterator find(const key_type& key) { return maMap.find(key); } + template + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(args)...); } + iterator erase(const_iterator const & pos) { return maMap.erase(pos); } +}; + +// helper class that stores preview data +class SwAccPreviewData +{ + typedef std::vector 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>& _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>& _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 , and + // + for ( auto & rpPreviewPage : _rPreviewPages ) + { + aPage = rpPreviewPage->pPage; + + // add preview page rectangle to + tools::Rectangle aPreviewPgRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize ); + maPreviewRects.push_back( aPreviewPgRect ); + + // add logic page rectangle to + 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.IsInside( 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 pShapes; + SwAccessibleObjShape_Impl *pSelShape = nullptr; + size_t nShapes = 0; + + const SwViewShell *pVSh = GetShell(); + const SwFEShell *pFESh = dynamic_cast( pVSh) != nullptr ? + static_cast< const SwFEShell * >( pVSh ) : nullptr; + 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(pNode->getLayoutFrame(pVSh->GetLayout()))); + sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex()); + sal_uLong nLastNode; + if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara()) + { + nLastNode = pMerged->pLastNode->GetIndex(); + } + else + { + nLastNode = nFirstNode; + } + + sal_uLong 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(); + sal_uLong nStartIndex = pStart->nNode.GetIndex(); + SwPosition* pEnd = rTmpCursor.End(); + sal_uLong 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 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 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->IsSeletedInDoc()) + { + 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(&(nStartIndex.GetNode())); + pFrame = SwIterator(*pCNd).First(); + if (mapTemp.find(pFrame) != mapTemp.end()) + { + continue; // sw_redlinehide: once is enough + } + } + else if( nStartIndex.GetNode().IsTableNode() ) + { + SwTableNode * pTable = static_cast(&(nStartIndex.GetNode())); + SwTableFormat* pFormat = pTable->GetTable().GetFrameFormat(); + pFrame = SwIterator(*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 = mpSeletedFrameMap->find( pFrame ); + if(aEraseIter != mpSeletedFrameMap->end()) + mpSeletedFrameMap->erase(aEraseIter); + } + else + { + bMarkChanged = true; + vecAdd.push_back(static_cast< SwAccessibleContext * >(xAcc.get())); + } + + mapTemp.emplace( pFrame, xAcc ); + } + } + } + } + } + } + if( !mpSeletedFrameMap ) + mpSeletedFrameMap.reset( new SwAccessibleContextMap_Impl ); + if( !mpSeletedFrameMap->empty() ) + { + SwAccessibleContextMap_Impl::iterator aIter = mpSeletedFrameMap->begin(); + while( aIter != mpSeletedFrameMap->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; + mpSeletedFrameMap->clear(); + } + + SwAccessibleContextMap_Impl::iterator aIter = mapTemp.begin(); + while( aIter != mapTemp.end() ) + { + mpSeletedFrameMap->emplace( (*aIter).first, (*aIter).second ); + ++aIter; + } + mapTemp.clear(); + + if( bMarkChanged && mpFrameMap) + { + 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 pShapes; + SwAccessibleObjShape_Impl *pSelShape = nullptr; + size_t nShapes = 0; + + const SwViewShell *pVSh = GetShell(); + const SwFEShell *pFESh = dynamic_cast( pVSh) != nullptr ? + static_cast< const SwFEShell * >( pVSh ) : nullptr; + 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(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 = 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); + } + } + } + } +} + +//Merge with DoInvalidateShapeSelection +/* +void SwAccessibleMap::DoInvalidateShapeFocus() +{ + const SwViewShell *pVSh = GetShell(); + const SwFEShell *pFESh = dynamic_cast( pVSh) != nullptr ? + static_cast< const SwFEShell * >( pVSh ) : nullptr; + const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0; + + if( nSelShapes != 1 ) + return; + + SwAccessibleObjShape_Impl *pShapes = nullptr; + SwAccessibleObjShape_Impl *pSelShape = nullptr; + size_t nShapes = 0; + + { + osl::MutexGuard aGuard( maMutex ); + if( mpShapeMap ) + pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape ); + } + + if( pShapes ) + { + vcl::Window *pWin = GetShell()->GetWin(); + bool bFocused = pWin && pWin->HasFocus(); + SwAccessibleObjShape_Impl *pShape = pShapes; + while( nShapes ) + { + if( pShape->second.is() ) + { + if( bFocused && pShape >= pSelShape ) + pShape->second->SetState( AccessibleStateType::FOCUSED ); + else + pShape->second->ResetState( AccessibleStateType::FOCUSED ); + } + + --nShapes; + ++pShape; + } + + delete[] pShapes; + } +} + +*/ + +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(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 SwAccessibleMap::GetDocumentPreview( + const std::vector>& _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 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 ) + { + SwAccessibleContext *pAcc = nullptr; + 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 ) + { + //here get all the sub list. + if (pParentObj->IsGroupObject()) + { + if (xAccParent.is()) + { + uno::Reference < XAccessibleContext > xContext = xAccParent->getAccessibleContext(); + if (xContext.is()) + { + sal_Int32 nChildren = xContext->getAccessibleChildCount(); + for(sal_Int32 i = 0; i 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 = 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 ) + { + SwAccessibleContextMap_Impl::iterator aIter = + mpFrameMap->find( pFrame ); + if( aIter != mpFrameMap->end() ) + { + 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 ) + { + SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj ); + if( aIter != mpShapeMap->end() ) + { + 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)) + { + ::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::Modify. + 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() ) ) + { + ::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() ) ) + { + 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() ); + 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() ) ) + { + 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() ); + 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(pVSh) ) + { + if( pCSh->IsTableMode() ) + { + while( aFrameOrObj.GetSwFrame() && !aFrameOrObj.GetSwFrame()->IsCellFrame() ) + aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper(); + } + else if( auto pFESh = dynamic_cast(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( 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.get()); + 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 xAcc = GetDocumentView_( true ); + if (xAcc) + { + SwAccessiblePreview *pAccPreview = static_cast(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.get() ); + 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() ) ) + { + 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() ) + { + 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() ) ) + { + 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() ) + { + 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(*this), + SwAccessibleChild( &rChild ) ); + } + } + + return nIndex; +} + +void SwAccessibleMap::UpdatePreview( const std::vector>& _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( 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 +{ + MapMode aSrc( MapUnit::MapTwip ); + MapMode aDest( MapUnit::Map100thMM ); + return OutputDevice::LogicToLogic( GetVisArea().SVRect(), aSrc, aDest ); +} + +// Convert a MM100 value relative to the document root into a pixel value +// relative to the screen! +Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const +{ + MapMode aSrc( MapUnit::Map100thMM ); + MapMode aDest( MapUnit::MapTwip ); + + Point aPoint = OutputDevice::LogicToLogic( rPoint, aSrc, aDest ); + 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 +{ + MapMode aSrc( MapUnit::Map100thMM ); + MapMode aDest( MapUnit::MapTwip ); + Size aSize( OutputDevice::LogicToLogic( rSize, aSrc, aDest ) ); + if (const OutputDevice* pWin = GetShell()->GetWin()) + { + 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 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.get() ); + 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()) + { + MapMode aMapMode; + GetMapMode( rPoint, aMapMode ); + aPoint = pWin->PixelToLogic( rPoint, aMapMode ); + } + return aPoint; +} + +static long lcl_CorrectCoarseValue(long aCoarseValue, long aFineValue, + long aRefValue, bool bToLower) +{ + 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 tools::Rectangle& rRect ) const +{ + tools::Rectangle aRect; + if (const OutputDevice* pWin = GetShell()->GetWin()) + { + MapMode aMapMode; + GetMapMode( rRect.TopLeft(), aMapMode ); + aRect = pWin->LogicToPixel( rRect, aMapMode ); + + tools::Rectangle aTmpRect = pWin->PixelToLogic( aRect, aMapMode ); + lcl_CorrectRectangle(aRect, rRect, 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 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 SwAccessibleMap::BuildSelectedParas() +{ + // no accessible contexts, no selection + if ( !mpFrameMap ) + { + return nullptr; + } + + // get cursor as an instance of its base class + SwPaM* pCursor( nullptr ); + { + SwCursorShell* pCursorShell = dynamic_cast(GetShell()); + if ( pCursorShell ) + { + SwFEShell* pFEShell = dynamic_cast(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 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 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 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( xAcc.get() ) ); + if ( xAccImpl.is() && xAccImpl->GetFrame() ) + { + const SwTextFrame* pTextFrame( + dynamic_cast(xAccImpl->GetFrame()) ); + OSL_ENSURE( pTextFrame, + " - 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 ) + { + 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( xAcc.get() ) ); + if ( xAccImpl.is() && xAccImpl->GetFrame() ) + { + const SwTextFrame* pTextFrame( + dynamic_cast(xAccImpl->GetFrame()) ); + OSL_ENSURE( pTextFrame, + " - 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..acc4f0ce3 --- /dev/null +++ b/sw/source/core/access/accnotextframe.cxx @@ -0,0 +1,316 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accnotextframe.hxx" +#include +#include "accnotexthyperlink.hxx" +#include +#include + +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(pFlyFrame->Lower()); + const SwContentNode* pSwContentNode = pContentFrame->GetNode(); + if(pSwContentNode != nullptr) + { + pNd = pSwContentNode->GetNoTextNode(); + } + } + + return pNd; +} + +SwAccessibleNoTextFrame::SwAccessibleNoTextFrame( + std::shared_ptr const& pInitMap, + sal_Int16 nInitRole, + const SwFlyFrame* pFlyFrame ) : + SwAccessibleFrameBase( pInitMap, nInitRole, pFlyFrame ), + msTitle(), + msDesc() +{ + const SwNoTextNode* pNd = GetNoTextNode(); + StartListening(const_cast(pNd)->GetNotifier()); + // #i73249# + // consider new attributes Title and Description + if( pNd ) + { + 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(auto pLegacyModifyHint = dynamic_cast(&rHint)) + { + const sal_uInt16 nWhich = pLegacyModifyHint->m_pOld ? pLegacyModifyHint->m_pOld->Which() : pLegacyModifyHint->m_pNew ? pLegacyModifyHint->m_pNew->Which() : 0; + 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(pLegacyModifyHint->m_pOld); + if(pOldItem) + sOldTitle = pOldItem->GetString(); + const SwStringMsgPoolItem* pNewItem = dynamic_cast(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::get() ) + { + uno::Reference xImage = this; + return uno::Any(xImage); + } + else if ( aType == cppu::UnoType::get()) + { + uno::Reference 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::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(); +} +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(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(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 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..21d3bcbe0 --- /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 +#include +#include + +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 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( ) throw () override + { SwAccessibleContext::acquire(); }; + + virtual void SAL_CALL release( ) throw () 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..5fbe826c9 --- /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 + +#include +#include +#include +#include + +#include "accnotexthyperlink.hxx" + +#include + +#include +#include +#include + +#include + +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) + { + ::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(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 +#include +#include + +#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(static_cast(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..39cf02b4b --- /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 +#include +#include +#include +#include +#include "accpage.hxx" + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +using uno::Sequence; + +const char sImplementationName[] = "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; + + { + osl::MutexGuard 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 ) + { + bool bSelected; + + { + osl::MutexGuard 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 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( GetFrame() )->GetPhyPageNum() ); + SetName( GetResource( STR_ACCESS_PAGE_NAME, &sPage ) ); +} + +SwAccessiblePage::~SwAccessiblePage() +{ +} + +bool SwAccessiblePage::HasCursor() +{ + osl::MutexGuard aGuard( m_Mutex ); + return m_bIsSelected; +} + +OUString SwAccessiblePage::getImplementationName( ) +{ + return sImplementationName; +} + +sal_Bool SwAccessiblePage::supportsService( const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence SwAccessiblePage::getSupportedServiceNames( ) +{ + return { "com.sun.star.text.AccessiblePageView", sAccessibleServiceName }; +} + +Sequence< sal_Int8 > SAL_CALL SwAccessiblePage::getImplementationId() +{ + return css::uno::Sequence(); +} + +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 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..4119275a7 --- /dev/null +++ b/sw/source/core/access/accpara.cxx @@ -0,0 +1,3558 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accpara.hxx" +#include "accportions.hxx" +#include +#include +#include +#include +#include +#include +#include +#include "acchyperlink.hxx" +#include "acchypertextdata.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "textmarkuphelper.hxx" +#include "parachangetrackinginfo.hxx" +#include +#include +#include +#include +#include + +#include +#include +#include "../../uibase/inc/fldmgr.hxx" +#include // 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; +} + +const char sServiceName[] = "com.sun.star.text.AccessibleParagraphView"; +const char sImplementationName[] = "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(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( pCursorShell) != nullptr + ? static_cast< SwFEShell * >( pCursorShell ) : nullptr; + 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(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(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; + { + osl::MutexGuard 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 ) + { + OUString sNewDesc( GetDescription() ); + OUString sOldDesc; + { + osl::MutexGuard 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; + { + osl::MutexGuard 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; + { + osl::MutexGuard 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 const& pInitMap, + const SwTextFrame& rTextFrame ) + : SwAccessibleContext( pInitMap, AccessibleRole::PARAGRAPH, &rTextFrame ) + , m_sDesc() + , 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(rTextFrame)); + m_bIsHeading = IsHeading(); + //Get the real heading level, Heading1 ~ Heading10 + m_nHeadingLevel = GetRealHeadingLevel(); + SetName( OUString() ); // set an empty accessibility name for paragraphs + + // If this object has the focus, then it is remembered by the map itself. + m_nOldCaretPos = GetCaretPos(); +} + +SwAccessibleParagraph::~SwAccessibleParagraph() +{ + SolarMutexGuard aGuard; + + m_pPortionData.reset(); + m_pHyperTextData.reset(); + mpParaChangeTrackInfo.reset(); // #i108125# + EndListeningAll(); +} + +bool SwAccessibleParagraph::HasCursor() +{ + osl::MutexGuard aGuard( m_Mutex ); + return m_nOldCaretPos != -1; +} + +void SwAccessibleParagraph::UpdatePortionData() +{ + // obtain the text frame + OSL_ENSURE( GetFrame() != nullptr, "The text frame has vanished!" ); + OSL_ENSURE( GetFrame()->IsTextFrame(), "The text frame has mutated!" ); + const SwTextFrame* pFrame = static_cast( GetFrame() ); + + // 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 ); +} + +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(GetFrame())); + SwPosition aStartPos(pFrame->MapViewToModelPos(nStart)); + auto pUnoCursor(const_cast(pFrame->GetDoc()).CreateUnoCursor(aStartPos)); + pUnoCursor->SetMark(); + *pUnoCursor->GetMark() = pFrame->MapViewToModelPos(nEnd); + + // create a (dummy) text portion to be returned + uno::Reference aEmpty; + SwXTextPortion* pPortion = + new SwXTextPortion ( pUnoCursor.get(), aEmpty, PORTION_TEXT); + + return pPortion; +} + +// 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(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(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(); + + osl::MutexGuard aGuard2( m_Mutex ); + if( m_sDesc.isEmpty() ) + m_sDesc = GetDescription(); + + return m_sDesc; +} + +lang::Locale SAL_CALL SwAccessibleParagraph::getLocale() +{ + SolarMutexGuard aGuard; + + const SwTextFrame *pTextFrame = dynamic_cast( GetFrame() ); + if( !pTextFrame ) + { + throw uno::RuntimeException("no SwTextFrame", static_cast(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 SAL_CALL SwAccessibleParagraph::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + utl::AccessibleRelationSetHelper* pHelper = new utl::AccessibleRelationSetHelper(); + + const SwTextFrame* pTextFrame = dynamic_cast(GetFrame()); + OSL_ENSURE( pTextFrame, + " - missing text frame"); + if ( pTextFrame ) + { + const SwContentFrame* pPrevContentFrame( pTextFrame->FindPrevCnt() ); + if ( pPrevContentFrame ) + { + uno::Sequence< uno::Reference > 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 > 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( 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* pBackgrdBrush = nullptr; + const Color* pSectionTOXColor = nullptr; + SwRect aDummyRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if ( pFrame && + pFrame->GetBackgroundBrush( aFillAttributes, pBackgrdBrush, pSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false ) ) + { + if ( pSectionTOXColor ) + { + rColor = *pSectionTOXColor; + return true; + } + else + { + rColor = pBackgrdBrush->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::get()) + { + uno::Reference aAccText = static_cast(*this); // resolve ambiguity + aRet <<= aAccText; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccEditText = this; + aRet <<= aAccEditText; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccSel = this; + aRet <<= aAccSel; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccHyp = this; + aRet <<= aAccHyp; + } + // #i63870# + // add interface com::sun:star:accessibility::XAccessibleTextAttributes + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccTextAttr = this; + aRet <<= aAccTextAttr; + } + // #i89175# + // add interface com::sun:star:accessibility::XAccessibleTextMarkup + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccTextMarkup = this; + aRet <<= aAccTextMarkup; + } + // add interface com::sun:star:accessibility::XAccessibleMultiLineText + else if ( rType == cppu::UnoType::get()) + { + uno::Reference aAccMultiLineText = this; + aRet <<= aAccMultiLineText; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference< css::accessibility::XAccessibleTextSelection > aTextExtension = this; + aRet <<= aTextExtension; + } + else if ( rType == cppu::UnoType::get()) + { + uno::Reference 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::get(), + cppu::UnoType::get(), + ::cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + SwAccessibleContext::getTypes() ).getTypes(); +} + +uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleParagraph::getImplementationId() +{ + return css::uno::Sequence(); +} + +// XAccessibleText + +sal_Int32 SwAccessibleParagraph::getCaretPosition() +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + sal_Int32 nRet = GetCaretPos(); + { + osl::MutexGuard 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(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(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(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(this)); + } + + SwRect aTmpRect(0, 0, tabs[0].Position, 0); + + tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aTmpRect.SVRect() )); + SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root + + Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds.SVRect() ).TopLeft() ); + aScreenRect.Move( -aFramePixPos.X(), -aFramePixPos.Y() ); + + tabs[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(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( + static_txtattr_cast(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(pField)->GetSubType(); + break; + case SwFieldIds::GetRef: + { + switch( pField->GetSubType() ) + { + case REF_BOOKMARK: + { + const SwGetRefField* pRefField = dynamic_cast(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(pField)->GetSetRefName(); + break; + } + //Get format string + strTypeName = sEntry; + // GetFormat() >= 0> is always true as 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(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(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(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 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 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 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 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(GetFrame())); + const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps()); + std::unique_ptr pSet; + if ( !bOnlyCharAttrs ) + { + pSet.reset( new SfxItemSet( const_cast(pTextNode->GetDoc()->GetAttrPool()), + svl::Items{} ) ); + } + else + { + pSet.reset( new SfxItemSet( const_cast(pTextNode->GetDoc()->GetAttrPool()), + svl::Items{} ) ); + } + // #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 + if ( !bOnlyCharAttrs ) + { + SfxItemSet aParaSet( const_cast(pTextNode->GetDoc()->GetAttrPool()), + svl::Items{} ); + pTextNode->SwContentNode::GetAttr( aParaSet ); + pSet->Put( aParaSet ); + } + // get default character attributes and merge these into + OSL_ENSURE( pTextNode->GetTextColl(), + " - missing paragraph style. Serious defect!" ); + if ( pTextNode->GetTextColl() ) + { + SfxItemSet aCharSet( const_cast(pTextNode->GetDoc()->GetAttrPool()), + svl::Items{} ); + SetPutRecursive( aCharSet, pTextNode->GetTextColl()->GetAttrSet() ); + pSet->Put( aCharSet ); + } + + // build-up sequence containing the run attributes + tAccParaPropValMap aDefAttrSeq; + { + const SfxItemPropertyMap& rPropMap = + aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap(); + PropertyEntryVector_t aPropertyEntries = rPropMap.getPropertyEntries(); + for ( const auto& rProp : aPropertyEntries ) + { + const SfxPoolItem* pItem = pSet->GetItem( rProp.nWID ); + if ( pItem ) + { + uno::Any aVal; + pItem->QueryValue( aVal, rProp.nMemberId ); + + PropertyValue rPropVal; + rPropVal.Name = rProp.sName; + 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::makeAny( 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(); + 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 ( const SwFlyFrame* pFlyFrame = dynamic_cast(pUpperFrame) ) + { + pUpperFrame = pFlyFrame->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# + static const char sMMToPixelRatio[] = "MMToPixelRatio"; + bool bProvideMMToPixelRatio( !aRequestedAttributes.hasElements() || + (comphelper::findValue(aRequestedAttributes, sMMToPixelRatio) != -1) ); + + uno::Sequence< PropertyValue > aValues( aDefAttrSeq.size() + + ( bProvideMMToPixelRatio ? 1 : 0 ) ); + std::transform(aDefAttrSeq.begin(), aDefAttrSeq.end(), aValues.begin(), + [](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(a100thMMSize.Width())/100)/aPixelSize.Width(); + rPropVal.Value <<= fRatio; + rPropVal.Handle = -1; + rPropVal.State = beans::PropertyState_DEFAULT_VALUE; + aValues[ 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 + std::unique_ptr pPaM; + const TextFrameIndex nCorePos(GetPortionData().GetCoreViewPosition(nIndex)); + SwTextFrame const*const pFrame(static_cast(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.reset(new SwPaM(aModelPos, aEndPos)); + } + + // retrieve character attributes for the created PaM + SfxItemSet aSet( pPaM->GetDoc()->GetAttrPool(), + svl::Items{} ); + // #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 + { + if ( pTextNode->HasSwAttrSet() ) + { + SfxItemSet aAutomaticParaStyleCharAttrs( pPaM->GetDoc()->GetAttrPool(), + svl::Items{} ); + aAutomaticParaStyleCharAttrs.Put( *(pTextNode->GetpSwAttrSet()), false ); + aSet.Put( aAutomaticParaStyleCharAttrs ); + } + } + // get character attributes at and merge these into + { + SfxItemSet aCharAttrsAtPaM( pPaM->GetDoc()->GetAttrPool(), + svl::Items{} ); + SwUnoCursorHelper::GetCursorAttr(*pPaM, aCharAttrsAtPaM, true); + aSet.Put( aCharAttrsAtPaM ); + } + + // build-up sequence containing the run attributes + { + tAccParaPropValMap aRunAttrSeq; + { + tAccParaPropValMap aDefAttrSeq; + uno::Sequence< OUString > aDummy; + _getDefaultAttributesImpl( aDummy, aDefAttrSeq, true ); // #i82637# + + const SfxItemPropertyMap& rPropMap = + aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap(); + PropertyEntryVector_t aPropertyEntries = rPropMap.getPropertyEntries(); + for ( const auto& rProp : aPropertyEntries ) + { + const SfxPoolItem* pItem( nullptr ); + // #i82637# - Found character attributes, whose value equals the value of + // the corresponding default character attributes, are excluded. + if ( aSet.GetItemState( rProp.nWID, true, &pItem ) == SfxItemState::SET ) + { + uno::Any aVal; + pItem->QueryValue( aVal, rProp.nMemberId ); + + PropertyValue rPropVal; + rPropVal.Name = rProp.sName; + 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(GetFrame())); + const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps()); + std::unique_ptr pSet; + pSet.reset( + new SfxItemSet( + const_cast(pTextNode->GetDoc()->GetAttrPool()), + svl::Items< + 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>{})); + + if ( pTextNode->HasBullet() || pTextNode->HasNumber() ) + { + pSet->Put( pTextNode->GetAttr(RES_PARATR_LIST_LEVEL) ); + } + pSet->Put( pTextNode->SwContentNode::GetAttr(RES_UL_SPACE) ); + pSet->Put( pTextNode->SwContentNode::GetAttr(RES_LR_SPACE) ); + pSet->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 = pSet->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(GetFrame())); + TextFrameIndex const nCorePos(GetPortionData().GetCoreViewPosition(nIndex)); + std::pair 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( reinterpret_cast(anyChar.pReserved)); + if (COL_AUTO == Color(crBack)) + { + uno::Reference xComponent(this); + if (xComponent.is()) + { + crBack = static_cast(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( reinterpret_cast(anyChar.pReserved)); + + if( COL_AUTO == Color(crChar) ) + { + uno::Reference xComponent(this); + if (xComponent.is()) + { + Color cr(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( reinterpret_cast(anyChar.pReserved)); + if ( COL_AUTO == Color(crUnderline) ) + { + uno::Reference xComponent(this); + if (xComponent.is()) + { + Color cr(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() ) + { + tabs.realloc(1); + 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[0] = 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(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(this)); + } + + tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCoreRect.SVRect() )); + SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root + + Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds.SVRect() ).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(this)); + } + Point aPoint( rPoint.X, rPoint.Y ); + SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root + Point aPixPos( GetMap()->CoreToPixel( aLogBounds.SVRect() ).TopLeft() ); + aPoint.setX(aPoint.getX() + aPixPos.getX()); + aPoint.setY(aPoint.getY() + aPixPos.getY()); + Point aCorePoint( GetMap()->PixelToCore( aPoint ) ); + if( !aLogBounds.IsInside( 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( GetFrame() ); + // construct SwPosition (where GetModelPositionForViewPoint() will put the result into) + SwTextNode* pNode = const_cast(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(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= aBound.startPos ) + { + while(nexBound.endPos==aBound.endPos&&nIndex(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.SVRect() ).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(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 xRange( + SwXTextRange::CreateXTextRange( + const_cast(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& 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 + uno::Reference xPortion = CreateUnoPortion( nStartIndex, + nEndIndex ); + + // build sorted index array + sal_Int32 nLength = rAttributeSet.getLength(); + const PropertyValue* pPairs = rAttributeSet.getConstArray(); + std::vector 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 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( 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( GetFrame() ); + SwHyperlinkIter_Impl aHIter(*pTextFrame); + SwTextNode const* pNode(nullptr); + SwTextAttr* pHt = const_cast(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(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( 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 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(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(GetFrame())); + sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex()); + sal_uLong 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(); + sal_uLong nStartIndex = pStart->nNode.GetIndex(); + SwPosition* pEnd = rTmpCursor.End(); + sal_uLong 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, nEnd; + /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd ); + return nStart; +} + +sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex ) +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + sal_Int32 nStart, nEnd; + /*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(GetFrame())); + sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex()); + sal_uLong 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(); + sal_uLong nStartIndex = pStart->nNode.GetIndex(); + SwPosition* pEnd = pCursor->End(); + sal_uLong 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(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 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(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 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(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(this)); + } + + tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCursorCoreRect.SVRect() )); + + SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root + Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds.SVRect() ).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(GetFrame())); + sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex()); + sal_uLong 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(); + sal_uLong nStartIndex = pStart->nNode.GetIndex(); + SwPosition* pEnd = rTmpCursor.End(); + sal_uLong 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) + { + OUString headStr = sValue.copy(0, 7); + if (headStr == "Heading") + { + OUString intStr = sValue.copy(8); + sal_Int32 headingLevel = intStr.toInt32(); + 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..ee5288cd2 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "accselectionhelper.hxx" +#include +#include + +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 m_pPortionData; + std::unique_ptr 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 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) + 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 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( ) throw () override + { SwAccessibleContext::acquire(); }; + + virtual void SAL_CALL release( ) throw () 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..b929bc54b --- /dev/null +++ b/sw/source/core/access/accportions.cxx @@ -0,0 +1,761 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include "accportions.hxx" +#include +#include +#include +#include + +// for portion replacement in Special() +#include + +// for GetWordBoundary(...), GetSentenceBoundary(...): +#include +#include + +// for FillSpecialPos(...) +#include + +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 +static size_t FindBreak(const std::vector& rPositions, T nValue); + +/// like FindBreak, but finds the last equal or larger position +template +static size_t FindLastBreak(const std::vector& rPositions, T nValue); + + +SwAccessiblePortionData::SwAccessiblePortionData( + const SwTextFrame *const pTextFrame, + const SwViewOption* pViewOpt ) : + SwPortionHandler(), + m_pTextFrame(pTextFrame), + m_aBuffer(), + m_nViewPosition( 0 ), + m_pViewOptions( pViewOpt ), + m_sAccessibleString(), + m_aLineBreaks(), + m_aAccessiblePositions(), + m_aFieldPosition(), + m_aPortionAttrs(), + 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(std::u16string_view(m_pTextFrame->GetText()).substr(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::Table: 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 ) ) + { + // 'wide' 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 +static size_t FindBreak(const std::vector& 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 +static size_t FindLastBreak(const std::vector& rPositions, T const nValue) +{ + size_t nResult = FindBreak( rPositions, nValue ); + + // skip 'zero-length' portions + // #i70538# consider size of 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 ((nEndPos - nStartPos) > TextFrameIndex(1)) + { + // 'wide' 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 ((nCoreEndPos - nCorePos == TextFrameIndex(1)) && + (m_pTextFrame->GetText()[sal_Int32(nCorePos)] != m_sAccessibleString[nPos])) + { + // case 1: a one-character, 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 = static_cast( 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) +{ + 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..246ed8622 --- /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 +#include +#include +#include +#include + +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 AccessiblePositions; + typedef std::vector 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 m_aPortionAttrs; /// additional portion attributes + + std::unique_ptr 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); + + 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 > 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..3465aae96 --- /dev/null +++ b/sw/source/core/access/accpreview.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 +#include +#include +#include "accpreview.hxx" + +const char sImplementationName[] = "com.sun.star.comp.Writer.SwAccessibleDocumentPageView"; + +using ::com::sun::star::uno::Sequence; + +SwAccessiblePreview::SwAccessiblePreview(std::shared_ptr 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 SwAccessiblePreview::getSupportedServiceNames( ) +{ + return {"com.sun.star.text.AccessibleTextDocumentPageView", + sAccessibleServiceName}; +} + +Sequence< sal_Int8 > SAL_CALL SwAccessiblePreview::getImplementationId() +{ + return css::uno::Sequence(); +} + +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 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..be92d4a2c --- /dev/null +++ b/sw/source/core/access/accselectionhelper.cxx @@ -0,0 +1,348 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "accselectionhelper.hxx" + +#include "acccontext.hxx" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 ) +{ +} + +SwAccessibleSelectionHelper::~SwAccessibleSelectionHelper() +{ +} + +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( 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 + SwFEShell* pFEShell = GetFEShell(); + if( pFEShell != nullptr ) + { + 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 pRStateSet = pRContext->getAccessibleStateSet(); + if( pRStateSet.is() ) + { + Sequence 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; + const SwFEShell* pFEShell = GetFEShell(); + if( pFEShell ) + { + 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 ) + { + 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. + const SwFEShell* pFEShell = GetFEShell(); + if( pFEShell != nullptr ) + { + 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(std::count_if(aChildren.begin(), aChildren.end(), + [this](const SwAccessibleChild& aChild) { return lcl_getSelectedState(aChild, &m_rContext, m_rContext.GetMap()); })); + } + } + return nCount; +} + +Reference 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( nullptr == 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..10d78d996 --- /dev/null +++ b/sw/source/core/access/accselectionhelper.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_ACCESS_ACCSELECTIONHELPER_HXX +#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCSELECTIONHELPER_HXX + +#include +#include + +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 ); + ~SwAccessibleSelectionHelper(); + + // 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..ccdf75eb8 --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "accfrmobjslist.hxx" +#include +#include +#include "acctable.hxx" + +#include + +#include +#include +#include + +#include +#include + +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 SwAccTableSelHander_Impl +{ +public: + virtual void Unselect( sal_Int32 nRowOrCol, sal_Int32 nExt ) = 0; + +protected: + ~SwAccTableSelHander_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, + SwAccTableSelHander_Impl& rSelHdl, + bool bColumns ) const; + + // #i77106# + bool IncludeRow( const SwFrame& rFrame ) const + { + return !mbOnlyTableColumnHeader || + mpTabFrame->IsInHeadline( rFrame ); + } +public: + // #i77106# - add third optional parameter , default value + 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, + SwAccTableSelHander_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, + SwAccTableSelHander_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.IsOver( 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, + SwAccTableSelHander_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 || nRow >= static_cast< sal_Int32 >( maRows.size() ) ) || + ( nCol < 0 || nCol >= static_cast< sal_Int32 >( maColumns.size() ) ) ) + { + uno::Reference < XAccessibleTable > xThis( pThis ); + lang::IndexOutOfBoundsException aExcept( + "row or column index out of range", + xThis ); + throw aExcept; + } +} + +namespace { + +class SwAccSingleTableSelHander_Impl : public SwAccTableSelHander_Impl +{ + bool m_bSelected; + +public: + + inline SwAccSingleTableSelHander_Impl(); + + virtual ~SwAccSingleTableSelHander_Impl() {} + + bool IsSelected() const { return m_bSelected; } + + virtual void Unselect( sal_Int32, sal_Int32 ) override; +}; + +} + +inline SwAccSingleTableSelHander_Impl::SwAccSingleTableSelHander_Impl() : + m_bSelected( true ) +{ +} + +void SwAccSingleTableSelHander_Impl::Unselect( sal_Int32, sal_Int32 ) +{ + m_bSelected = false; +} + +namespace { + +class SwAccAllTableSelHander_Impl : public SwAccTableSelHander_Impl + +{ + std::vector< bool > m_aSelected; + sal_Int32 m_nCount; + +public: + explicit SwAccAllTableSelHander_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 ~SwAccAllTableSelHander_Impl(); +}; + +} + +SwAccAllTableSelHander_Impl::~SwAccAllTableSelHander_Impl() +{ +} + +uno::Sequence < sal_Int32 > SwAccAllTableSelHander_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 SwAccAllTableSelHander_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(this)->getAccessibleChildCount(), "Illegal child index." ); // #i77106# + + const SwTableBox* pBox = nullptr; + + // get table box for 'our' table cell + SwAccessibleChild aCell( GetChild( *const_cast(GetMap()), nChildIndex ) ); + if( aCell.GetSwFrame() ) + { + const SwFrame* pChildFrame = aCell.GetSwFrame(); + if( (pChildFrame != nullptr) && pChildFrame->IsCellFrame() ) + { + const SwCellFrame* pCellFrame = + static_cast( 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( 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(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 const& pInitMap, + const SwTabFrame* pTabFrame ) : + SwAccessibleContext( pInitMap, AccessibleRole::TABLE, pTabFrame ) +{ + const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat(); + if(pFrameFormat) + StartListening(const_cast(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(auto pLegacyHint = dynamic_cast(&rHint)) + { + sal_uInt16 nWhich = pLegacyHint->m_pOld ? pLegacyHint->m_pOld->Which() : pLegacyHint->m_pNew ? pLegacyHint->m_pNew->Which() : 0; + const SwTabFrame* pTabFrame = static_cast(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::get() ) + { + uno::Reference xThis( this ); + aRet <<= xThis; + } + else if ( rType == cppu::UnoType::get() ) + { + uno::Reference xSelection( this ); + aRet <<= xSelection; + } + else if ( rType == cppu::UnoType::get() ) + { + uno::Reference 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::get(), + cppu::UnoType::get(), + SwAccessibleContext::getTypes() ).getTypes(); +} + +uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleTable::getImplementationId() +{ + return css::uno::Sequence(); +} + +// #i77106# +std::unique_ptr SwAccessibleTable::CreateNewTableData() +{ + const SwTabFrame* pTabFrame = static_cast( GetFrame() ); + return std::unique_ptr(new SwAccessibleTableData_Impl( *GetMap(), pTabFrame, IsInPagePreview() )); +} + +void SwAccessibleTable::UpdateTableData() +{ + // #i77106# - usage of new method + 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 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(), + " - 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 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(), + " - 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. + SwAccessibleTableColHeaders* pTableColHeaders = + new SwAccessibleTableColHeaders(GetMap()->shared_from_this(), + static_cast(GetFrame())); + uno::Reference< XAccessibleTable > xTableColumnHeaders( pTableColHeaders ); + if ( pTableColHeaders->getAccessibleChildCount() <= 0 ) + { + return uno::Reference< XAccessibleTable >(); + } + + return xTableColumnHeaders; +} + +uno::Sequence< sal_Int32 > SAL_CALL SwAccessibleTable::getSelectedAccessibleRows() +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + const SwSelBoxes *pSelBoxes = GetSelBoxes(); + if( pSelBoxes ) + { + sal_Int32 nRows = GetTableData().GetRowCount(); + SwAccAllTableSelHander_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(); + SwAccAllTableSelHander_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 ) + { + SwAccSingleTableSelHander_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 ) + { + SwAccSingleTableSelHander_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 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 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 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 xAccDoc = getAccessibleParent(); + if (xAccDoc.is()) + { + uno::Reference xComponentDoc(xAccDoc,uno::UNO_QUERY); + if (xComponentDoc.is()) + { + crBack = Color(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 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 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 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; + + long lColumnCount = getAccessibleColumnCount(); + for(long lCol = 0; lCol < lColumnCount; lCol ++) + { + 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; + + long lRowCount = getAccessibleRowCount(); + + for(long lRow = 0; lRow < lRowCount; lRow ++) + { + long 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( + std::shared_ptr const& pMap, + const SwTabFrame *const pTabFrame) + : SwAccessibleTable(pMap, pTabFrame) +{ + SolarMutexGuard aGuard; + + const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat(); + if(pFrameFormat) + StartListening(const_cast(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 SwAccessibleTableColHeaders::CreateNewTableData() +{ + const SwTabFrame* pTabFrame = static_cast( GetFrame() ); + return std::unique_ptr(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( 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 const char sImplName[] = "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..4da4944ba --- /dev/null +++ b/sw/source/core/access/acctable.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCTABLE_HXX +#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCTABLE_HXX + +#include +#include +#include +#include + +#include +#include + +#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 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 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 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( ) throw () override + { SwAccessibleContext::acquire(); }; + + virtual void SAL_CALL release( ) throw () 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 > > 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 CreateNewTableData() override; + virtual void Notify(const SfxHint&) override; + +public: + SwAccessibleTableColHeaders(std::shared_ptr 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; + +}; +#endif + +/* 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..928a68677 --- /dev/null +++ b/sw/source/core/access/acctextframe.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 const& pInitMap, + const SwFlyFrame& rFlyFrame ) : + SwAccessibleFrameBase( pInitMap, AccessibleRole::TEXT_FRAME, &rFlyFrame ), + msTitle(), + msDesc() +{ + 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(auto pLegacyModifyHint = dynamic_cast(&rHint)) + { + const sal_uInt16 nWhich = pLegacyModifyHint->m_pOld ? pLegacyModifyHint->m_pOld->Which() : pLegacyModifyHint->m_pNew ? pLegacyModifyHint->m_pNew->Which() : 0; + const SwFlyFrame* pFlyFrame = static_cast(GetFrame()); + switch(nWhich) + { + // #i73249# + case RES_TITLE_CHANGED: + { + OUString sOldTitle, sNewTitle; + const SwStringMsgPoolItem *pOldItem = dynamic_cast(pLegacyModifyHint->m_pOld); + if(pOldItem) + sOldTitle = pOldItem->GetString(); + const SwStringMsgPoolItem *pNewItem = dynamic_cast(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() + throw () +{ + SwAccessibleContext::acquire (); +} + +void SAL_CALL + SwAccessibleTextFrame::release() + throw () +{ + SwAccessibleContext::release (); +} + +// XAccessibleSelection + +void SAL_CALL SwAccessibleTextFrame::selectAccessibleChild( sal_Int32 ) +{ + SAL_WARN("sw.a11y", " - missing implementation"); +} + +sal_Bool SAL_CALL SwAccessibleTextFrame::isAccessibleChildSelected( sal_Int32 nChildIndex ) +{ + SolarMutexGuard g; + + uno::Reference xAcc = getAccessibleChild( nChildIndex ); + uno::Reference 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", " - missing implementation"); +} + +void SAL_CALL SwAccessibleTextFrame::selectAllAccessibleChildren( ) +{ + SAL_WARN("sw.a11y", " - 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 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(); +} + +void SAL_CALL SwAccessibleTextFrame::deselectAccessibleChild( sal_Int32 ) +{ + SAL_WARN("sw.a11y", " - 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(); +} + +// XAccessibleRelationSet + +SwFlyFrame* SwAccessibleTextFrame::getFlyFrame() const +{ + SwFlyFrame* pFlyFrame = nullptr; + + const SwFrame* pFrame = GetFrame(); + assert(pFrame); + if( pFrame->IsFlyFrame() ) + { + pFlyFrame = static_cast( const_cast( pFrame ) ); + } + + return pFlyFrame; +} + +AccessibleRelation SwAccessibleTextFrame::makeRelation( sal_Int16 nType, const SwFlyFrame* pFrame ) +{ + uno::Sequence > aSequence { GetMap()->GetContext( pFrame ) }; + return AccessibleRelation( nType, aSequence ); +} + +uno::Reference SAL_CALL SwAccessibleTextFrame::getAccessibleRelationSet( ) +{ + SolarMutexGuard aGuard; + + ThrowIfDisposed(); + + // get the frame, and insert prev/next relations into helper + + 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..a4ee77ad8 --- /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 + +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 const& pInitMap, + const SwFlyFrame& rFlyFrame); + + virtual css::uno::Any SAL_CALL queryInterface( + css::uno::Type const & rType ) override; + virtual void SAL_CALL acquire() throw () override; + virtual void SAL_CALL release() throw () 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..5fcc6f41c --- /dev/null +++ b/sw/source/core/access/parachangetrackinginfo.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace { + void initChangeTrackTextMarkupLists( const SwTextFrame& rTextFrame, + std::unique_ptr& opChangeTrackInsertionTextMarkupList, + std::unique_ptr& opChangeTrackDeletionTextMarkupList, + std::unique_ptr& opChangeTrackFormatChangeTextMarkupList ) + { + opChangeTrackInsertionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) ); + opChangeTrackDeletionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) ); + opChangeTrackFormatChangeTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) ); + + if (!rTextFrame.GetTextNodeFirst()) + { + OSL_FAIL( " 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, + " expected to be NULL." ); + OSL_ENSURE( mpChangeTrackFormatChangeTextMarkupList == nullptr, + " 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( " - 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 +#include + +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 mpChangeTrackInsertionTextMarkupList; + std::unique_ptr mpChangeTrackDeletionTextMarkupList; + std::unique_ptr 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..19bd4d576 --- /dev/null +++ b/sw/source/core/access/textmarkuphelper.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 +#include + +#include +#include +#include +#include + +#include +#include +#include + +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 +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 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( " - missing instance" ); + } + } + + return aTextMarkupSegment; +} + +css::uno::Sequence< css::accessibility::TextSegment > + SwTextMarkupHelper::getTextMarkupAtIndex( const sal_Int32 nCharIndex, + const sal_Int32 nTextMarkupType ) +{ + // assumption: + // value of 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 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 ); + } + } + } + + uno::Sequence< css::accessibility::TextSegment > aTextMarkups( + aTmpTextMarkups.size() ); + std::copy( aTmpTextMarkups.begin(), aTmpTextMarkups.end(), aTextMarkups.begin() ); + + return aTextMarkups; +} + +/* 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 +#include + +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/calbck.cxx b/sw/source/core/attr/calbck.cxx new file mode 100644 index 000000000..cef9b02b0 --- /dev/null +++ b/sw/source/core/attr/calbck.cxx @@ -0,0 +1,381 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sw +{ + bool ListenerEntry::GetInfo(SfxPoolItem& rInfo) const + { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); } + void ListenerEntry::Modify(const SfxPoolItem *const pOldValue, + const SfxPoolItem *const pNewValue) + { + SwClientNotify(*GetRegisteredIn(), sw::LegacyModifyHint(pOldValue, pNewValue)); + } + void ListenerEntry::SwClientNotify(const SwModify& rModify, const SfxHint& rHint) + { + if (auto pLegacyHint = dynamic_cast(&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->SwClientNotifyCall(rModify, rHint); + } + else if (m_pToTell) + m_pToTell->SwClientNotifyCall(rModify, rHint); + } +} + +sw::LegacyModifyHint::~LegacyModifyHint() {} +sw::ModifyChangedHint::~ModifyChangedHint() {} + +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::unique_ptr SwClient::CheckRegistration( const SfxPoolItem* pOld ) +{ + DBG_TESTSOLARMUTEX(); + // this method only handles notification about dying SwModify objects + if( !pOld || pOld->Which() != RES_OBJECTDYING ) + return nullptr; + + assert(dynamic_cast(pOld)); + const SwPtrMsgPoolItem* pDead = static_cast(pOld); + if(pDead->pObject != m_pRegisteredIn) + { + // we should only care received death notes from objects we are following + return nullptr; + } + // 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 std::unique_ptr(new sw::ModifyChangedHint(pAbove)); +} + +void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (auto pLegacyHint = dynamic_cast(&rHint)) + { + Modify(pLegacyHint->m_pOld, pLegacyHint->m_pNew); + } +}; + +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); +} + +void SwClient::Modify(SfxPoolItem const*const pOldValue, SfxPoolItem const*const /*pNewValue*/) +{ + CheckRegistration( pOldValue ); +} + +void SwModify::SetInDocDTOR() +{ + // If the document gets destroyed anyway, just tell clients to + // forget me so that they don't try to get removed from my list + // later when they also get destroyed + SwIterator aIter(*this); + for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next()) + pClient->m_pRegisteredIn = nullptr; + m_pWriterListeners = nullptr; +} + +SwModify::~SwModify() +{ + DBG_TESTSOLARMUTEX(); + OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." ); + + if ( IsInCache() ) + SwFrame::GetCache().Delete( this ); + + if ( IsInSwFntCache() ) + pSwFontCache->Delete( this ); + + // notify all clients that they shall remove themselves + SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this ); + NotifyClients( &aDyObject, &aDyObject ); + + // remove all clients that have not done themselves + // mba: possibly a hotfix for forgotten base class calls?! + while( m_pWriterListeners ) + static_cast(m_pWriterListeners)->CheckRegistration( &aDyObject ); +} + +void SwModify::NotifyClients( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue ) +{ + DBG_TESTSOLARMUTEX(); + if ( IsInCache() || IsInSwFntCache() ) + { + const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() : + pNewValue ? pNewValue->Which() : 0; + CheckCaching( nWhich ); + } + + if ( !m_pWriterListeners || IsModifyLocked() ) + return; + + LockModify(); + + // mba: WTF?! + if( !pOldValue ) + { + m_bLockClientList = true; + } + else + { + switch( pOldValue->Which() ) + { + case RES_OBJECTDYING: + case RES_REMOVE_UNO_OBJECT: + m_bLockClientList = static_cast(pOldValue)->pObject != this; + break; + + default: + m_bLockClientList = true; + } + } + + ModifyBroadcast( pOldValue, pNewValue ); + m_bLockClientList = false; + UnlockModify(); +} + +bool SwModify::GetInfo( SfxPoolItem& rInfo ) const +{ + if(!m_pWriterListeners) + return true; + SwIterator 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(); + OSL_ENSURE( !m_bLockClientList, "Client inserted while in Modify" ); + + if(pDepend->m_pRegisteredIn != this ) + { +#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; +} + +void SwModify::CheckCaching( const sal_uInt16 nWhich ) +{ + if( isCHRATR( nWhich ) ) + { + SetInSwFntCache( false ); + } + else + { + switch( nWhich ) + { + case RES_OBJECTDYING: + case RES_FMT_CHG: + case RES_ATTRSET_CHG: + SetInSwFntCache( false ); + [[fallthrough]]; + 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( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + break; + } + } +} + +sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell) + : m_rToTell(rToTell) +{} + +sw::WriterMultiListener::~WriterMultiListener() +{} + +void sw::WriterMultiListener::StartListening(SwModify* pDepend) +{ + EndListening(nullptr); + m_vDepends.emplace_back(ListenerEntry(&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::CallSwClientNotify( const SfxHint& rHint ) const +{ + SwIterator 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(this)->GetNotifier().Broadcast(rHint); +} +/* 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..a0a459ed8 --- /dev/null +++ b/sw/source/core/attr/cellatr.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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(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(rAttr).GetFormula() && + m_pDefinedIn == static_cast(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 +{ + const SwNode* pRet = nullptr; + if( m_pDefinedIn ) + { + SwTableBox* pBox = SwIterator( *m_pDefinedIn ).First(); + if( pBox ) + pRet = pBox->GetSttNd(); + } + return pRet; +} + +SwTableBox* SwTableBoxFormula::GetTableBox() +{ + SwTableBox* pBox = nullptr; + if( m_pDefinedIn ) + pBox = SwIterator( *m_pDefinedIn ).First(); + return pBox; +} + +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(static_cast(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 ) + { + 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(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..8b04869af --- /dev/null +++ b/sw/source/core/attr/fmtfollowtextflow.cxx @@ -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 . + */ + +#include +#include + +#include + +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..21ceec2f0 --- /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 +#include +#include +#include +#include + +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(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( " - 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 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( " - 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( " - unknown MemberId" ); + } + return bRet; +} + +void SwFormatWrapInfluenceOnObjPos::SetWrapInfluenceOnObjPos( sal_Int16 _nWrapInfluenceOnPosition ) +{ + // #i35017# - constant names have changed and consider new value + if ( _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE || + _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_CONCURRENT || + _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ITERATIVE ) + { + mnWrapInfluenceOnPosition = _nWrapInfluenceOnPosition; + } + else + { + OSL_FAIL( " - invalid attribute value" ); + } +} + +// #i35017# - add parameter <_bIterativeAsOnceConcurrent> to control, if +// value has to be treated as +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatWrapInfluenceOnObjPos")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWrapInfluenceOnPosition"), BAD_CAST(OString::number(mnWrapInfluenceOnPosition).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAllowOverlap"), BAD_CAST(OString::boolean(mbAllowOverlap).getStr())); + 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..a69dbf125 --- /dev/null +++ b/sw/source/core/attr/format.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 +#include //For SwFmt::getIDocumentSettingAccess() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + + +SwFormat::SwFormat( SwAttrPool& rPool, const char* pFormatNm, + const sal_uInt16* 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 sal_uInt16* 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 ) : + 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(); + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + // 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() ) + { + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } + + 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; + ModifyNotification( &aOld, &aNew ); + } + 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 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 + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + // special treatments for some attributes + SwAttrSet* pChgSet = const_cast(&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 ); + ModifyNotification( &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() ) + { + m_bFormatInDTOR = true; + + SwFormat* pParentFormat = DerivedFrom(); + if( !pParentFormat ) + { + SAL_WARN( + "sw.core", + "~SwFormat: parent format missing from: " << GetName() ); + } + else + { + SwFormatChg aOldFormat( this ); + SwFormatChg aNewFormat( pParentFormat ); + SwIterator aIter(*this); + for(SwClient* pClient = aIter.First(); pClient && pParentFormat; pClient = aIter.Next()) + { + SAL_INFO("sw.core", "reparenting " << typeid(*pClient).name() << " at " << pClient << " from " << typeid(*this).name() << " at " << this << " to " << typeid(*pParentFormat).name() << " at " << pParentFormat); + pParentFormat->Add( pClient ); + pClient->ModifyNotification( &aOldFormat, &aNewFormat ); + } + } + } +} + +void SwFormat::Modify( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue ) +{ + bool bContinue = true; // true = pass on to dependent ones + + sal_uInt16 nWhich = pOldValue ? pOldValue->Which() : + pNewValue ? pNewValue->Which() : 0 ; + switch( nWhich ) + { + case 0: break; // Which-Id of 0? + + case RES_OBJECTDYING: + if (pNewValue) + { + // If the dying object is the parent format of this format so + // attach this to the parent of the parent + SwFormat* pFormat = static_cast(static_cast(pNewValue)->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: + if (pOldValue && pNewValue && static_cast(pOldValue)->GetTheChgdSet() != &m_aSet) + { + // pass only those that are not set + SwAttrSetChg aOld( *static_cast(pOldValue) ); + SwAttrSetChg aNew( *static_cast(pNewValue) ); + + aOld.GetChgSet()->Differentiate( m_aSet ); + aNew.GetChgSet()->Differentiate( m_aSet ); + + if( aNew.Count() ) + NotifyClients( &aOld, &aNew ); + bContinue = false; + } + 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 + if ( pOldValue && pNewValue && + static_cast(pOldValue)->pChangedFormat != this && + static_cast(pNewValue)->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 + OSL_ENSURE( RES_PARATR_DROP == nWhich, "Modify was sent without sender" ); + bContinue = false; + } + } + } + + if( bContinue ) + { + // walk over all dependent formats + NotifyClients( pOldValue, pNewValue ); + } +} + +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) + ); + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + pDerFrom->Add( this ); + m_aSet.SetParent( &pDerFrom->m_aSet ); + + SwFormatChg aOldFormat( this ); + SwFormatChg aNewFormat( this ); + ModifyNotification( &aOldFormat, &aNewFormat ); + + 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 aSvxBrushItem; //(std::make_shared(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 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& 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 SfxPoolItem* pItem = nullptr; + SfxItemState eRet = m_aSet.GetItemState(RES_BACKGROUND, true, &pItem); + if (pItem) + rItem.reset(static_cast(pItem->Clone())); + return eRet; +} + +bool SwFormat::SetFormatAttr( const SfxPoolItem& rAttr ) +{ + if ( IsInCache() || IsInSwFntCache() ) + { + const sal_uInt16 nWhich = rAttr.Which(); + CheckCaching( 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)"); + SfxItemSet aTempSet(*m_aSet.GetPool(), svl::Items{}); + const SvxBrushItem& rSource = static_cast< const SvxBrushItem& >(rAttr); + + // 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); + + SwAttrSetChg aChgOld(m_aSet, aOld); + SwAttrSetChg aChgNew(m_aSet, aNew); + + ModifyNotification(&aChgOld, &aChgNew); + } + } + + 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 ); + + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } + } + return bRet; +} + +bool SwFormat::SetFormatAttr( const SfxItemSet& rSet ) +{ + if( !rSet.Count() ) + return false; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + 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()) + { + const SfxPoolItem* pSource = nullptr; + + if(SfxItemState::SET == aTempSet.GetItemState(RES_BACKGROUND, false, &pSource)) + { + // 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 + const SvxBrushItem& rSource(static_cast< const SvxBrushItem& >(*pSource)); + 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); + + SwAttrSetChg aChgOld(m_aSet, aOld); + SwAttrSetChg aChgNew(m_aSet, aNew); + + ModifyNotification(&aChgOld, &aChgNew); + } + } + + 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 ); + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } + } + 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 + + if ( IsInCache() || IsInSwFntCache() ) + { + for( sal_uInt16 n = nWhich1; n < nWhich2; ++n ) + CheckCaching( 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 ) + { + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } + return bRet; +} + +// #i73790# +sal_uInt16 SwFormat::ResetAllFormatAttr() +{ + if( !m_aSet.Count() ) + return 0; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + // 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 ) + { + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } + return aNew.Count(); +} + +void SwFormat::DelDiffs( const SfxItemSet& rSet ) +{ + if( !m_aSet.Count() ) + return; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + // 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 ) + { + SwAttrSetChg aChgOld( m_aSet, aOld ); + SwAttrSetChg aChgNew( m_aSet, aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // send all modified ones + } +} + +/** 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(); +} + +void SwFormat::SetGrabBagItem(const uno::Any& rVal) +{ + if (!m_pGrabBagItem) + m_pGrabBagItem = std::make_shared(); + + m_pGrabBagItem->PutValue(rVal, 0); +} + +std::unique_ptr 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(m_aSet.GetBackground(bInP).Clone()); +} + +drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFormat::getSdrAllFillAttributesHelper() const +{ + return drawinglayer::attribute::SdrAllFillAttributesHelperPtr(); +} + +/* 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..b8d626e1b --- /dev/null +++ b/sw/source/core/attr/hints.cxx @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +SwFormatChg::SwFormatChg( SwFormat* pFormat ) + : SwMsgPoolItem( RES_FMT_CHG ), pChangedFormat( pFormat ) +{ +} + +SwInsText::SwInsText( sal_Int32 nP, sal_Int32 nL ) + : SwMsgPoolItem( RES_INS_TXT ), nPos( nP ), nLen( nL ) +{ +} + +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 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 + +SwCondCollCondChg::SwCondCollCondChg( SwFormat *pFormat ) + : SwMsgPoolItem( RES_CONDCOLL_CONDCHG ), pChangedFormat( pFormat ) +{ +} + +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() ) + { + sal_uLong 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..aa6d9f1ad --- /dev/null +++ b/sw/source/core/attr/swatrset.cxx @@ -0,0 +1,481 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +SwAttrPool::SwAttrPool( SwDoc* pD ) + : SfxItemPool( "SWG", + POOLATTR_BEGIN, POOLATTR_END-1, + aSlotTab, &aAttrTab ), + m_pDoc( pD ) +{ + // create secondary pools immediately + createAndAddSecondaryPools(); +} + +SwAttrPool::~SwAttrPool() +{ + // cleanup secondary pools first + removeAndDeleteSecondaryPools(); +} + +void SwAttrPool::createAndAddSecondaryPools() +{ + const SfxItemPool* pCheckAlreadySet = GetSecondaryPool(); + + if(pCheckAlreadySet) + { + OSL_ENSURE(false, "SwAttrPool already has a secondary pool (!)"); + return; + } + + // 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 + SfxItemPool *pSdrPool = new SdrItemPool(this); + + // #75371# change DefaultItems for the SdrEdgeObj distance items + // to TWIPS. + // 1/100th mm in twips + const long nDefEdgeDist = (500 * 72) / 127; + + pSdrPool->SetPoolDefaultItem(SdrEdgeNode1HorzDistItem(nDefEdgeDist)); + pSdrPool->SetPoolDefaultItem(SdrEdgeNode1VertDistItem(nDefEdgeDist)); + pSdrPool->SetPoolDefaultItem(SdrEdgeNode2HorzDistItem(nDefEdgeDist)); + pSdrPool->SetPoolDefaultItem(SdrEdgeNode2VertDistItem(nDefEdgeDist)); + + // #i33700# // Set shadow distance defaults as PoolDefaultItems + pSdrPool->SetPoolDefaultItem(makeSdrShadowXDistItem((300 * 72) / 127)); + pSdrPool->SetPoolDefaultItem(makeSdrShadowYDistItem((300 * 72) / 127)); + + SfxItemPool *pEEgPool = EditEngine::CreatePool(); + + pSdrPool->SetSecondaryPool(pEEgPool); + + if(!GetFrozenIdRanges()) + { + FreezeIdRanges(); + } + else + { + pSdrPool->FreezeIdRanges(); + } +} + +void SwAttrPool::removeAndDeleteSecondaryPools() +{ + SfxItemPool *pSdrPool = GetSecondaryPool(); + + if(!pSdrPool) + { + OSL_ENSURE(false, "SwAttrPool has no secondary pool, it's missing (!)"); + return; + } + + SfxItemPool *pEEgPool = pSdrPool->GetSecondaryPool(); + + if(!pEEgPool) + { + OSL_ENSURE(false, "i don't accept additional pools"); + return; + } + + // first delete the items, then break the linking + pSdrPool->Delete(); + + SetSecondaryPool(nullptr); + pSdrPool->SetSecondaryPool(nullptr); + + // final cleanup of secondary pool(s) + SfxItemPool::Free(pSdrPool); + SfxItemPool::Free(pEEgPool); +} + +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 sal_uInt16* 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 SwAttrSet::Clone( bool bItems, SfxItemPool *pToPool ) const +{ + if ( pToPool && pToPool != GetPool() ) + { + SwAttrPool* pAttrPool = dynamic_cast< SwAttrPool* >(pToPool); + std::unique_ptr 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( + 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 SwModify* pModify ) +{ + bool bSet = false; + + const SfxPoolItem* pItem; + if( SfxItemState::SET == GetItemState( RES_PAGEDESC, false, &pItem ) && + static_cast(pItem)->GetDefinedIn() != pModify ) + { + const_cast(static_cast(pItem))->ChgDefinedIn( pModify ); + bSet = true; + } + + if( SfxItemState::SET == GetItemState( RES_PARATR_DROP, false, &pItem ) && + static_cast(pItem)->GetDefinedIn() != pModify ) + { + // If CharFormat is set and it is set in different attribute pools then + // the CharFormat has to be copied. + SwCharFormat* pCharFormat = const_cast(static_cast(pItem))->GetCharFormat(); + if( pCharFormat && GetPool() != pCharFormat->GetAttrSet().GetPool() ) + { + pCharFormat = GetDoc()->CopyCharFormat( *pCharFormat ); + const_cast(static_cast(pItem))->SetCharFormat( pCharFormat ); + } + const_cast(static_cast(pItem))->ChgDefinedIn( pModify ); + bSet = true; + } + + if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false, &pItem ) && + static_cast(pItem)->GetDefinedIn() != pModify ) + { + const_cast(static_cast(pItem))->ChgDefinedIn( pModify ); + bSet = true; + } + + return bSet; +} + +void SwAttrSet::CopyToModify( SwModify& rMod ) const +{ + // copy attributes across multiple documents if needed + SwContentNode* pCNd = dynamic_cast( &rMod ); + SwFormat* pFormat = dynamic_cast( &rMod ); + + if( pCNd || pFormat ) + { + if( Count() ) + { + // #i92811# + std::unique_ptr pNewListIdItem; + + const SfxPoolItem* pItem; + const SwDoc *pSrcDoc = GetDoc(); + SwDoc *pDstDoc = pCNd ? pCNd->GetDoc() : pFormat->GetDoc(); + + // Does the NumRule has to be copied? + if( pSrcDoc != pDstDoc && + SfxItemState::SET == GetItemState( RES_PARATR_NUMRULE, false, &pItem ) ) + { + const OUString& rNm = static_cast(pItem)->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 + if ( pSrcDoc != pDstDoc && + pCNd && pCNd->IsTextNode() && + GetItemState( RES_PARATR_LIST_ID, false, &pItem ) == SfxItemState::SET ) + { + auto pStrItem = dynamic_cast(pItem); + assert(pStrItem); + 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 + // could have also created it. + if ( pNewListIdItem == nullptr && + !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) ) + { + // copy list + pDstDoc->getIDocumentListsAccess().createList( sListId, sDefaultListStyleName ); + } + } + } + + std::unique_ptr< SfxItemSet > tmpSet; + + const SwPageDesc* pPgDesc; + if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState( + RES_PAGEDESC, false, &pItem )) + { + pPgDesc = static_cast(pItem)->GetPageDesc(); + if( pPgDesc ) + { + tmpSet.reset(new SfxItemSet(*this)); + + SwPageDesc* pDstPgDesc = pDstDoc->FindPageDesc(pPgDesc->GetName()); + if( !pDstPgDesc ) + { + pDstPgDesc = pDstDoc->MakePageDesc(pPgDesc->GetName()); + pDstDoc->CopyPageDesc( *pPgDesc, *pDstPgDesc ); + } + SwFormatPageDesc aDesc( pDstPgDesc ); + aDesc.SetNumOffset( static_cast(pItem)->GetNumOffset() ); + tmpSet->Put( aDesc ); + } + } + + if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState( RES_ANCHOR, false, &pItem ) + && static_cast< const SwFormatAnchor* >( pItem )->GetContentAnchor() != nullptr ) + { + if( !tmpSet ) + tmpSet.reset( new SfxItemSet( *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 ); + } + + if (pSrcDoc != pDstDoc && + SfxItemState::SET == GetItemState(RES_PARATR_LIST_AUTOFMT, false, &pItem)) + { + SfxItemSet const& rAutoStyle(*static_cast(*pItem).GetStyleHandle()); + std::shared_ptr const pNewSet( + rAutoStyle.SfxItemSet::Clone(true, &pDstDoc->GetAttrPool())); + + // fix up character style, it contains pointers to pSrcDoc + if (SfxItemState::SET == pNewSet->GetItemState(RES_TXTATR_CHARFMT, false, &pItem)) + { + auto const* pChar(static_cast(pItem)); + SwCharFormat *const pCopy(pDstDoc->CopyCharFormat(*pChar->GetCharFormat())); + const_cast(pChar)->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.reset(new SfxItemSet(*this)); + } + tmpSet->Put(item); + } + + if( tmpSet ) + { + if( pCNd ) + { + // #i92811# + if ( pNewListIdItem != nullptr ) + { + tmpSet->Put( *pNewListIdItem ); + } + pCNd->SetAttr( *tmpSet ); + } + else + { + pFormat->SetFormatAttr( *tmpSet ); + } + } + else if( pCNd ) + { + // #i92811# + if ( pNewListIdItem != nullptr ) + { + SfxItemSet aTmpSet( *this ); + aTmpSet.Put( *pNewListIdItem ); + pCNd->SetAttr( aTmpSet ); + } + else + { + pCNd->SetAttr( *this ); + } + } + else + { + pFormat->SetFormatAttr( *this ); + } + } + } +#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 sal_uInt16* pRange, const sal_uInt16 nId ) +{ + while( *pRange ) + { + if( *pRange <= nId && nId <= *(pRange+1) ) + return true; + pRange += 2; + } + 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..096705e8b --- /dev/null +++ b/sw/source/core/bastyp/SwSmartTagMgr.cxx @@ -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 . + */ + +#include + +#include +#include +#include + +using namespace com::sun::star; +using namespace com::sun::star::uno; + +SwSmartTagMgr* SwSmartTagMgr::spTheSwSmartTagMgr = nullptr; + +SwSmartTagMgr& SwSmartTagMgr::Get() +{ + if ( !spTheSwSmartTagMgr ) + { + spTheSwSmartTagMgr = new SwSmartTagMgr( SwDocShell::Factory().GetModuleName() ); + spTheSwSmartTagMgr->Init("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..8158a485a --- /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 + +#include +#include + +/** Resize block management by this constant. + As a result there are approx. 20 * MAXENTRY == 20000 entries available */ +static 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_uLong nSize, sal_uInt16 nCur ) +{ + assert( !nSize || nCur < nBlock ); // BigPtrArray: CurIndex invalid + + sal_uLong 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_uLong from, sal_uLong 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_uLong 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_uLong 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_uLong 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_uLong 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_uLong pos, sal_uLong 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_uLong nElem = n; + while( nElem ) + { + sal_uInt16 nel = p->nElem - sal_uInt16(pos); + if( sal_uLong(nel) > nElem ) + nel = sal_uInt16(nElem); + // move elements if needed + if( ( pos + nel ) < sal_uLong(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_uLong 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 + // represents the "old" and 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 - 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +SwBreakIt* g_pBreakIt = nullptr; + +void SwBreakIt::Create_( const uno::Reference & 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 & 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(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..ffe3d9d9c --- /dev/null +++ b/sw/source/core/bastyp/calc.cxx @@ -0,0 +1,1498 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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"; + +// ATTENTION: sorted list of all operators +struct CalcOp +{ + union{ + const char* pName; + const OUString* pUName; + }; + SwCalcOper eOp; +}; + +CalcOp const aOpTable[] = { +/* 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 +/* COS */ {{sCalc_Cos}, CALC_COS}, // Cosine +/* 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 +/* 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 +/* ROUND */ {{sCalc_Round}, CALC_ROUND}, // Rounding +/* 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(pFirst)->eOp ) + { + if( CALC_NAME == static_cast(pSecond)->eOp ) + nRet = static_cast(pFirst)->pUName->compareTo( + *static_cast(pSecond)->pUName ); + else + nRet = static_cast(pFirst)->pUName->compareToAscii( + static_cast(pSecond)->pName ); + } + else + { + if( CALC_NAME == static_cast(pSecond)->eOp ) + nRet = -1 * static_cast(pSecond)->pUName->compareToAscii( + static_cast(pFirst)->pName ); + else + nRet = strcmp( static_cast(pFirst)->pName, + static_cast(pSecond)->pName ); + } + return nRet; +} +}// extern "C" + +CalcOp* FindOperator( const OUString& rSrch ) +{ + CalcOp aSrch; + aSrch.pUName = &rSrch; + aSrch.eOp = CALC_NAME; + + return static_cast(bsearch( static_cast(&aSrch), + static_cast(aOpTable), + SAL_N_ELEMENTS( aOpTable ), + sizeof( CalcOp ), + OperatorCompare )); +} + +static LanguageType GetDocAppScriptLang( SwDoc const & rDoc ) +{ + return static_cast(rDoc.GetDefault( + GetWhichOfScript( RES_CHRATR_LANGUAGE, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() )) + )).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_pLocaleDataWrapper( m_aSysLocale.GetLocaleDataPtr() ) + , m_pCharClass( &GetAppCharClass() ) + , m_nListPor( 0 ) + , m_eCurrOper( CALC_NAME ) + , m_eCurrListOper( CALC_NAME ) + , m_eError( SwCalcError::NONE ) +{ + m_aErrExpr.aStr = "~C_ERR~"; + LanguageType eLang = GetDocAppScriptLang( m_rDoc ); + + if( eLang != m_pLocaleDataWrapper->getLanguageTag().getLanguageType() || + eLang != m_pCharClass->getLanguageTag().getLanguageType() ) + { + LanguageTag aLanguageTag( eLang ); + m_pCharClass = new CharClass( ::comphelper::getProcessComponentContext(), aLanguageTag ); + m_pLocaleDataWrapper = new LocaleDataWrapper( aLanguageTag ); + } + + m_sCurrSym = comphelper::string::strip(m_pLocaleDataWrapper->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( F_PI ); + m_aVarTable[ aHashValue[ 3 ] ]->nValue.PutDouble( 2.7182818284590452354 ); + + 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_pLocaleDataWrapper != m_aSysLocale.GetLocaleDataPtr() ) + delete m_pLocaleDataWrapper; + 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_pLocaleDataWrapper->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 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(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(static_cast(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; + 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_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 + 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_pLocaleDataWrapper->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 + 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(); +} + +CharClass* SwCalc::GetCharClass() +{ + return m_pCharClass; +} + +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: + 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; + 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 ) + { + OUString aName( m_sCommand.copy( nRealStt, + aRes.EndPos - nRealStt )); + if( 1 == aName.getLength() ) + { + 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 { + if( -1 != ( nFndPos = + m_sCommand.indexOf( ']', nFndPos )) ) + { + // ignore the ] + if ('\\' == m_sCommand[nFndPos-1]) + { + m_aVarName.append(std::u16string_view(m_sCommand).substr(nSttPos, + nFndPos - nSttPos - 1) ); + nSttPos = ++nFndPos; + } + else + break; + } + } while( nFndPos != -1 ); + + if( nFndPos != -1 ) + { + if( nSttPos != nFndPos ) + m_aVarName.append(std::u16string_view(m_sCommand).substr(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 ) + { + OUString aName( m_sCommand.copy( nRealStt, + aRes.EndPos - nRealStt )); + if( !aName.isEmpty() ) + { + 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.getLength() && '=' == aName[1] ) + m_eCurrOper = eTmp2; + else if( 1 != aName.getLength() ) + 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(floor( left.GetDouble() )); + nYear = nYear & 0x0000FFFF; + sal_Int32 nMonth = static_cast(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(floor( left.GetDouble() )); + nYearMonth = nYearMonth & 0x00FFFFFF; + sal_Int32 nDay = static_cast(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(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(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); + break; + case CALC_COS: + SAL_INFO("sw.calc", "cos"); + return StdFunc(&cos, false); + break; + case CALC_TAN: + SAL_INFO("sw.calc", "tan"); + return StdFunc(&tan, false); + break; + case CALC_ATAN: + SAL_INFO("sw.calc", "atan"); + return StdFunc(&atan, false); + break; + case CALC_ASIN: + SAL_INFO("sw.calc", "asin"); + return StdFunc(&asin, true); + break; + case CALC_ACOS: + SAL_INFO("sw.calc", "acos"); + return StdFunc(&acos, true); + break; + 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; + break; + } + case CALC_NUMBER: + { + SAL_INFO("sw.calc", "number: " << m_nNumberValue.GetDouble()); + SwSbxValue nErg; + 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; + break; + } + 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; + break; + } + case CALC_MINUS: + { + SAL_INFO("sw.calc", "-"); + SwSbxValue nErg; + GetToken(); + nErg.PutDouble( -(Prim().GetDouble()) ); + return nErg; + break; + } + 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; + break; + } + case CALC_RP: + // ignore, see tdf#121962 + SAL_INFO("sw.calc", ")"); + break; + case CALC_MEAN: + { + SAL_INFO("sw.calc", "mean"); + m_nListPor = 1; + GetToken(); + SwSbxValue nErg = Expr(); + double aTmp = nErg.GetDouble(); + aTmp /= m_nListPor; + nErg.PutDouble( aTmp ); + return nErg; + break; + } + 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; + break; + } + case CALC_SUM: + case CALC_DATE: + case CALC_MIN: + case CALC_MAX: + { + SAL_INFO("sw.calc", "sum/date/min/max"); + GetToken(); + SwSbxValue nErg = Expr(); + return nErg; + break; + } + case CALC_ENDCALC: + { + SAL_INFO("sw.calc", "endcalc"); + SwSbxValue nErg; + nErg.Clear(); + return nErg; + break; + } + 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(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( 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(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.GetLocaleDataPtr() ); +} + +bool SwCalc::Str2Double( const OUString& rCommand, sal_Int32& rCommandPos, + double& rVal, SwDoc const * const pDoc ) +{ + const SvtSysLocale aSysLocale; + std::unique_ptr 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.GetLocaleDataPtr()); + + 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 +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..69066ff5c --- /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 +#include +#include + +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..073b66563 --- /dev/null +++ b/sw/source/core/bastyp/index.cxx @@ -0,0 +1,391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +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(&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(&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) + { + SwIndex * pIdx = const_cast(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() << ")"; +} + +/* 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..c3a8d48b0 --- /dev/null +++ b/sw/source/core/bastyp/init.cxx @@ -0,0 +1,797 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +// some ranges for sets in collections/ nodes + +// AttrSet range for the 2 break attributes +sal_uInt16 const aBreakSetRange[] = { + RES_PAGEDESC, RES_BREAK, + 0 +}; + +// AttrSet range for TextFormatColl +// list attributes ( RES_PARATR_LIST_BEGIN - RES_PARATR_LIST_END ) are not +// included in the paragraph style's itemset. +sal_uInt16 const aTextFormatCollSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_PARATR_BEGIN, RES_PARATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + + // FillAttribute support + XATTR_FILL_FIRST, XATTR_FILL_LAST, + + 0 +}; + +// AttrSet range for GrfFormatColl +sal_uInt16 const aGrfFormatCollSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_GRFATR_BEGIN, RES_GRFATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 +}; + +// AttrSet range for TextNode +sal_uInt16 const aTextNodeSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_CHRATR_BEGIN, RES_CHRATR_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, + + // FillAttribute support (paragraph FillStyle) + XATTR_FILL_FIRST, XATTR_FILL_LAST, + + 0 +}; + +// AttrSet range for NoTextNode +sal_uInt16 const aNoTextNodeSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_GRFATR_BEGIN, RES_GRFATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 +}; + +sal_uInt16 const aTableSetRange[] = { + RES_FILL_ORDER, RES_FRM_SIZE, + RES_LR_SPACE, RES_BREAK, + RES_BACKGROUND, RES_SHADOW, + RES_HORI_ORIENT, RES_HORI_ORIENT, + RES_KEEP, RES_KEEP, + RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT, + RES_FRAMEDIR, RES_FRAMEDIR, + // #i29550# + RES_COLLAPSING_BORDERS, RES_COLLAPSING_BORDERS, + // <-- collapsing + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG, + 0 +}; + +sal_uInt16 const aTableLineSetRange[] = { + RES_FILL_ORDER, RES_FRM_SIZE, + RES_LR_SPACE, RES_UL_SPACE, + RES_BACKGROUND, RES_SHADOW, + RES_ROW_SPLIT, RES_ROW_SPLIT, + RES_PROTECT, RES_PROTECT, + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG, + 0 +}; + +sal_uInt16 const aTableBoxSetRange[] = { + RES_FILL_ORDER, RES_FRM_SIZE, + RES_LR_SPACE, RES_UL_SPACE, + RES_BACKGROUND, RES_SHADOW, + RES_PROTECT, RES_PROTECT, + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_FRAMEDIR, RES_FRAMEDIR, + RES_BOXATR_BEGIN, RES_BOXATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG, + 0 +}; + +// AttrSet range for SwFrameFormat +sal_uInt16 const aFrameFormatSetRange[] = { + 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, + + 0 +}; + +// AttrSet range for SwCharFormat +sal_uInt16 const aCharFormatSetRange[] = { + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 +}; + +// AttrSet range for character autostyles +sal_uInt16 const aCharAutoFormatSetRange[] = { + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 +}; + +// AttrSet range for SwPageDescFormat +sal_uInt16 const aPgFrameFormatSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 +}; + +// 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_FIELD + { 0, false }, // RES_TXTATR_FLYCNT + { 0, false }, // RES_TXTATR_FTN + { 0, false }, // RES_TXTATR_ANNOTATION + { 0, true }, // RES_TXTATR_DUMMY3 + { 0, true }, // RES_TXTATR_DUMMY1 + { 0, true }, // RES_TXTATR_DUMMY2 + + { 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_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 *pGlobalOLEExcludeList = nullptr; + +SwAutoCompleteWord* SwDoc::s_pAutoCompleteWords = nullptr; + +SwCheckIt* pCheckIt = nullptr; +static CharClass* pAppCharClass = nullptr; + +static CollatorWrapper* pCollator = nullptr, + *pCaseCollator = nullptr; + +salhelper::SingletonRef* s_getCalendarWrapper() +{ + static salhelper::SingletonRef 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, 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_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 ); + +// TextAttr - Dummies + aAttrTab[ RES_TXTATR_DUMMY1 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY1 ); + aAttrTab[ RES_TXTATR_DUMMY2 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY2 ); + aAttrTab[ RES_TXTATR_DUMMY3 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY3 ); + + 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(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(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_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( *static_cast(aAttrTab[ RES_CHRATR_FONT- POOLATTR_BEGIN ]), + *static_cast(aAttrTab[ RES_CHRATR_CJK_FONT - POOLATTR_BEGIN ]), + *static_cast(aAttrTab[ RES_CHRATR_CTL_FONT - POOLATTR_BEGIN ]) ); + + SwBreakIt::Create_( ::comphelper::getProcessComponentContext() ); + pCheckIt = nullptr; + + FrameInit(); + TextInit_(); + + SwSelPaintRects::s_pMapMode = new MapMode; + SwFntObj::pPixMap = new MapMode; + + pGlobalOLEExcludeList = new std::vector; + + 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::pDfltBoxAutoFormat; + + delete SwSelPaintRects::s_pMapMode; + delete SwFntObj::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 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; + } + }; + + class theTransWrp : public rtl::Static {}; +} + +const ::utl::TransliterationWrapper& GetAppCmpStrIgnore() +{ + return theTransWrp::get().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..7d09b1676 --- /dev/null +++ b/sw/source/core/bastyp/proofreadingiterator.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/. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +css::uno::Reference instance; +bool disposed = false; + +void doDispose( + css::uno::Reference const & + inst) +{ + css::uno::Reference comp(inst, css::uno::UNO_QUERY); + if (comp.is()) { + SolarMutexReleaser r; + comp->dispose(); + } +} + +} + +css::uno::Reference +sw::proofreadingiterator::get( + css::uno::Reference const & context) +{ + css::uno::Reference 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 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..222700005 --- /dev/null +++ b/sw/source/core/bastyp/swcache.cxx @@ -0,0 +1,512 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include + +#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_aCacheObjects(), + 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; + if ( nullptr != (pRet = (nIndex < m_aCacheObjects.size()) ? m_aCacheObjects[ nIndex ].get() : nullptr) ) + { + 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; + if ( nullptr != (pObj = Get( pOwner, false )) ) + 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..d443c85d9 --- /dev/null +++ b/sw/source/core/bastyp/swrect.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#ifdef DBG_UTIL +#include +#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); +} + +Point SwRect::Center() const +{ + return Point( Left() + Width() / 2, + Top() + Height() / 2 ); +} + +SwRect& SwRect::Union( const SwRect& rRect ) +{ + if ( Top() > rRect.Top() ) + Top( rRect.Top() ); + if ( Left() > rRect.Left() ) + Left( rRect.Left() ); + 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 ( IsOver( 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() ); + 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() ); + 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; +} + +bool SwRect::IsInside( const SwRect& rRect ) const +{ + const long nRight = Right(); + const long nBottom = Bottom(); + const long nrRight = rRect.Right(); + const long nrBottom= rRect.Bottom(); + return (Left() <= rRect.Left()) && (rRect.Left()<= nRight) && + (Left() <= nrRight) && (nrRight <= nRight) && + (Top() <= rRect.Top()) && (rRect.Top() <= nBottom) && + (Top() <= nrBottom) && (nrBottom <= nBottom); +} + +bool SwRect::IsInside( const Point& rPoint ) const +{ + return (Left() <= rPoint.X()) && + (Top() <= rPoint.Y()) && + (Right() >= rPoint.X()) && + (Bottom()>= rPoint.Y()); +} + +// mouse moving of table borders +bool SwRect::IsNear( const Point& rPoint, long nTolerance ) const +{ + bool bIsNearby = (((Left() - nTolerance) <= rPoint.X()) && + ((Top() - nTolerance) <= rPoint.Y()) && + ((Right() + nTolerance) >= rPoint.X()) && + ((Bottom() + nTolerance) >= rPoint.Y())); + return IsInside(rPoint) || bIsNearby; +} + +bool SwRect::IsOver( const SwRect& rRect ) const +{ + return (Top() <= rRect.Bottom()) && + (Left() <= rRect.Right()) && + (Right() >= rRect.Left()) && + (Bottom()>= rRect.Top()); +} + +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 long nNew ) { m_Size.setWidth(nNew); } +void SwRect::Height_( const long nNew ) { m_Size.setHeight(nNew); } +void SwRect::Left_( const long nLeft ){ m_Size.AdjustWidth(m_Point.getX() - nLeft ); m_Point.setX(nLeft); } +void SwRect::Right_( const long nRight ){ m_Size.setWidth(nRight - m_Point.getX()); } +void SwRect::Top_( const long nTop ){ m_Size.AdjustHeight(m_Point.getY() - nTop ); m_Point.setY(nTop); } +void SwRect::Bottom_( const long nBottom ){ m_Size.setHeight(nBottom - m_Point.getY()); } + +long SwRect::Width_() const{ return m_Size.getWidth(); } +long SwRect::Height_() const{ return m_Size.getHeight(); } +long SwRect::Left_() const{ return m_Point.getX(); } +long SwRect::Right_() const{ return m_Point.getX() + m_Size.getWidth(); } +long SwRect::Top_() const{ return m_Point.getY(); } +long SwRect::Bottom_() const{ return m_Point.getY() + m_Size.getHeight(); } + +void SwRect::AddWidth( const long nAdd ) { m_Size.AdjustWidth(nAdd ); } +void SwRect::AddHeight( const long nAdd ) { m_Size.AdjustHeight(nAdd ); } +void SwRect::AddLeft( const long nAdd ){ m_Size.AdjustWidth(-nAdd ); m_Point.setX(m_Point.getX() + nAdd); } +void SwRect::SubLeft( const long nSub ){ m_Size.AdjustWidth(nSub ); m_Point.setX(m_Point.getX() - nSub); } +void SwRect::AddRight( const long nAdd ){ m_Size.AdjustWidth(nAdd ); } +void SwRect::AddTop( const long nAdd ){ m_Size.AdjustHeight(-nAdd ); m_Point.setY(m_Point.getY() + nAdd); } +void SwRect::SubTop( const long nSub ){ m_Size.AdjustHeight(nSub ); m_Point.setY(m_Point.getY() - nSub); } +void SwRect::AddBottom( const long nAdd ){ m_Size.AdjustHeight(nAdd ); } +void SwRect::SetPosX( const long nNew ){ m_Point.setX(nNew); } +void SwRect::SetPosY( const long nNew ){ m_Point.setY(nNew); } + +Size SwRect::Size_() const { return SSize(); } +Size SwRect::SwappedSize() const { return Size( m_Size.getHeight(), m_Size.getWidth() ); } + +Point SwRect::TopLeft() const { return Pos(); } +Point SwRect::TopRight() const { return Point( m_Point.getX() + m_Size.getWidth(), m_Point.getY() ); } +Point SwRect::BottomLeft() const { return Point( m_Point.getX(), m_Point.getY() + m_Size.getHeight() ); } +Point SwRect::BottomRight() const + { return Point( m_Point.getX() + m_Size.getWidth(), m_Point.getY() + m_Size.getHeight() ); } + +long SwRect::GetLeftDistance( long nLimit ) const { return m_Point.getX() - nLimit; } +long SwRect::GetBottomDistance( long nLim ) const { return nLim - m_Point.getY() - m_Size.getHeight();} +long SwRect::GetTopDistance( long nLimit ) const { return m_Point.getY() - nLimit; } +long SwRect::GetRightDistance( long nLim ) const { return nLim - m_Point.getX() - m_Size.getWidth(); } + +bool SwRect::OverStepLeft( long nLimit ) const + { return nLimit > m_Point.getX() && m_Point.getX() + m_Size.getWidth() > nLimit; } +bool SwRect::OverStepBottom( long nLimit ) const + { return nLimit > m_Point.getY() && m_Point.getY() + m_Size.getHeight() > nLimit; } +bool SwRect::OverStepTop( long nLimit ) const + { return nLimit > m_Point.getY() && m_Point.getY() + m_Size.getHeight() > nLimit; } +bool SwRect::OverStepRight( long nLimit ) const + { return nLimit > m_Point.getX() && m_Point.getX() + m_Size.getWidth() > nLimit; } + +void SwRect::SetLeftAndWidth( long nLeft, long nNew ) +{ + m_Point.setX(nLeft); + m_Size.setWidth(nNew); +} +void SwRect::SetTopAndHeight( long nTop, long nNew ) +{ + m_Point.setY(nTop); + m_Size.setHeight(nNew); +} +void SwRect::SetRightAndWidth( long nRight, long nNew ) +{ + m_Point.setX(nRight - nNew); + m_Size.setWidth(nNew); +} +void SwRect::SetBottomAndHeight( long nBottom, 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 +{ + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("left"), "%li", Left()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("top"), "%li", Top()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("width"), "%li", Width()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("height"), "%li", Height()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("bottom"), "%li", Bottom()); + 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 + +/* 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..e67dac264 --- /dev/null +++ b/sw/source/core/bastyp/swregion.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 +#include +#include + +SwRegionRects::SwRegionRects( const SwRect &rStartRect, sal_uInt16 nInit ) : + SwRects(), + m_aOrigin( rStartRect ) +{ + reserve(nInit); + push_back( m_aOrigin ); +} + +// If is true then this Rect will be overwritten by at +// position . Otherwise 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 ) +{ + bool f = false; + InsertRect( rRect, 0, f ); +} + +/** Delete all overlaps of the Rects in array with the given + + 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.IsOver( (*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. + long nTmp; + if ( 0 < (nTmp = aInter.Top() - aTmp.Top()) ) + { + const 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() ); + if ( 0 < (nTmp = aInter.Left() - aTmp.Left()) ) + { + const 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 SwTwips CalcArea( const SwRect &rRect ) +{ + return rRect.Width() * rRect.Height(); +} + +// combine all adjacent rectangles +void SwRegionRects::Compress() +{ + for (size_type i = 0; i < size(); ) + { + bool bRestart(false); + for ( size_type j = i+1; j < size(); ++j ) + { + // If one rectangle contains a second completely than the latter + // does not need to be stored and can be deleted + if ( (*this)[i].IsInside( (*this)[j] ) ) + { + erase( begin() + j ); + --j; + } + else if ( (*this)[j].IsInside( (*this)[i] ) ) + { + (*this)[i] = (*this)[j]; + erase( begin() + j ); + bRestart = true; + break; + } + else + { + // If two rectangles have the same area of their union minus the + // intersection then one of them can be deleted. + // 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 + const long nFuzzy = 1361513; + SwRect aUnion( (*this)[i] ); + aUnion.Union( (*this)[j] ); + SwRect aInter( (*this)[i] ); + aInter.Intersection( (*this)[j] ); + if ( (::CalcArea( (*this)[i] ) + + ::CalcArea( (*this)[j] ) + nFuzzy) >= + (::CalcArea( aUnion ) - CalcArea( aInter )) ) + { + (*this)[i] = aUnion; + erase( begin() + j ); + bRestart = true; + break; + } + } + } + i = bRestart ? 0 : i+1; + } +} + +/* 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..59c01abb5 --- /dev/null +++ b/sw/source/core/bastyp/swtypes.cxx @@ -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 . + */ + +#include + +#include +#include +#include + +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(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..0ecbb1069 --- /dev/null +++ b/sw/source/core/bastyp/tabcol.cxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +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( long nValue, long nMin, 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( 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 ); + +#if OSL_DEBUG_LEVEL > 1 + for ( const auto& rPos : m_aData ) + { + aEntry = rPos; + } +#endif +} + +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..5c986ab86 --- /dev/null +++ b/sw/source/core/crsr/BlockCursor.cxx @@ -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 . + */ + +#include +#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 +#include + +#include + +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 maStartPt; + std::optional 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 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 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..d5c44f121 --- /dev/null +++ b/sw/source/core/crsr/DateFormFieldButton.cxx @@ -0,0 +1,97 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include + +namespace +{ +class SwDatePickerDialog : public FloatingWindow +{ +private: + VclPtr m_pCalendar; + sw::mark::DateFieldmark* m_pFieldmark; + SvNumberFormatter* m_pNumberFormatter; + + DECL_LINK(ImplSelectHdl, Calendar*, void); + +public: + SwDatePickerDialog(SwEditWin* parent, sw::mark::DateFieldmark* pFieldmark, + SvNumberFormatter* pNumberFormatter); + virtual ~SwDatePickerDialog() override; + virtual void dispose() override; +}; +} + +SwDatePickerDialog::SwDatePickerDialog(SwEditWin* parent, sw::mark::DateFieldmark* pFieldmark, + SvNumberFormatter* pNumberFormatter) + : FloatingWindow(parent, WB_BORDER | WB_SYSTEMWINDOW | WB_NOSHADOW) + , m_pCalendar(VclPtr::Create(this, WB_TABSTOP)) + , m_pFieldmark(pFieldmark) + , m_pNumberFormatter(pNumberFormatter) +{ + if (m_pFieldmark != nullptr) + { + std::pair aResult = m_pFieldmark->GetCurrentDate(); + if (aResult.first) + { + const Date& rNullDate = m_pNumberFormatter->GetNullDate(); + m_pCalendar->SetCurDate(rNullDate + sal_Int32(aResult.second)); + } + } + m_pCalendar->SetSelectHdl(LINK(this, SwDatePickerDialog, ImplSelectHdl)); + m_pCalendar->SetOutputSizePixel(m_pCalendar->CalcWindowSizePixel()); + m_pCalendar->Show(); + SetOutputSizePixel(m_pCalendar->GetSizePixel()); +} + +SwDatePickerDialog::~SwDatePickerDialog() { disposeOnce(); } + +void SwDatePickerDialog::dispose() +{ + m_pCalendar.clear(); + FloatingWindow::dispose(); +} + +IMPL_LINK(SwDatePickerDialog, ImplSelectHdl, Calendar*, pCalendar, void) +{ + if (!pCalendar->IsTravelSelect()) + { + if (m_pFieldmark != nullptr) + { + const Date& rNullDate = m_pNumberFormatter->GetNullDate(); + double dDate = pCalendar->GetFirstSelectedDate() - rNullDate; + m_pFieldmark->SetCurrentDate(dDate); + } + EndPopupMode(); + } +} + +DateFormFieldButton::DateFormFieldButton(SwEditWin* pEditWin, sw::mark::DateFieldmark& rFieldmark, + SvNumberFormatter* pNumberFormatter) + : FormFieldButton(pEditWin, rFieldmark) + , m_pNumberFormatter(pNumberFormatter) +{ +} + +DateFormFieldButton::~DateFormFieldButton() { disposeOnce(); } + +void DateFormFieldButton::InitPopup() +{ + sw::mark::DateFieldmark* pDateFieldmark = dynamic_cast(&m_rFieldmark); + m_pFieldPopup = VclPtr::Create(static_cast(GetParent()), + pDateFieldmark, m_pNumberFormatter); +} + +/* 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..a110ac3f0 --- /dev/null +++ b/sw/source/core/crsr/DropDownFormFieldButton.cxx @@ -0,0 +1,167 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +class SwFieldListBox final : public InterimItemWindow +{ +private: + std::unique_ptr m_xTreeView; + +public: + SwFieldListBox(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/swriter/ui/formdropdown.ui", "FormDropDown") + , m_xTreeView(m_xBuilder->weld_tree_view("list")) + { + } + weld::TreeView& get_widget() { return *m_xTreeView; } + virtual ~SwFieldListBox() override { disposeOnce(); } + virtual void dispose() override + { + m_xTreeView.reset(); + InterimItemWindow::dispose(); + } +}; + +/** + * 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. + */ +class SwFieldDialog : public FloatingWindow +{ +private: + VclPtr m_xListBox; + sw::mark::IFieldmark* m_pFieldmark; + + DECL_LINK(MyListBoxHandler, weld::TreeView&, bool); + +public: + SwFieldDialog(SwEditWin* parent, sw::mark::IFieldmark* fieldBM, long nMinListWidth); + virtual ~SwFieldDialog() override; + virtual void dispose() override; +}; +} + +SwFieldDialog::SwFieldDialog(SwEditWin* parent, sw::mark::IFieldmark* fieldBM, long nMinListWidth) + : FloatingWindow(parent, WB_BORDER | WB_SYSTEMWINDOW) + , m_xListBox(VclPtr::Create(this)) + , m_pFieldmark(fieldBM) +{ + weld::TreeView& rTreeView = m_xListBox->get_widget(); + + if (fieldBM != nullptr) + { + const sw::mark::IFieldmark::parameter_map_t* const pParameters = fieldBM->GetParameters(); + + OUString sListKey = ODF_FORMDROPDOWN_LISTENTRY; + sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries + = pParameters->find(sListKey); + css::uno::Sequence vListEntries; + if (pListEntries != pParameters->end()) + { + pListEntries->second >>= vListEntries; + for (OUString const& i : std::as_const(vListEntries)) + rTreeView.append_text(i); + } + + if (!vListEntries.hasElements()) + { + rTreeView.append_text(SwResId(STR_DROP_DOWN_EMPTY_LIST)); + } + + // Select the current one + OUString sResultKey = ODF_FORMDROPDOWN_RESULT; + sw::mark::IFieldmark::parameter_map_t::const_iterator pResult + = pParameters->find(sResultKey); + if (pResult != pParameters->end()) + { + sal_Int32 nSelection = -1; + pResult->second >>= nSelection; + rTreeView.set_cursor(nSelection); + rTreeView.select(nSelection); + } + } + + auto nHeight = rTreeView.get_height_rows( + std::min(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount(), + rTreeView.n_children())); + rTreeView.set_size_request(-1, nHeight); + Size lbSize(rTreeView.get_preferred_size()); + lbSize.AdjustWidth(4); + lbSize.AdjustHeight(4); + lbSize.setWidth(std::max(lbSize.Width(), nMinListWidth)); + m_xListBox->SetSizePixel(lbSize); + rTreeView.connect_row_activated(LINK(this, SwFieldDialog, MyListBoxHandler)); + m_xListBox->Show(); + + rTreeView.grab_focus(); + + SetSizePixel(lbSize); +} + +SwFieldDialog::~SwFieldDialog() { disposeOnce(); } + +void SwFieldDialog::dispose() +{ + m_xListBox.disposeAndClear(); + FloatingWindow::dispose(); +} + +IMPL_LINK(SwFieldDialog, MyListBoxHandler, weld::TreeView&, rBox, bool) +{ + OUString sSelection = rBox.get_selected_text(); + if (sSelection == SwResId(STR_DROP_DOWN_EMPTY_LIST)) + { + EndPopupMode(); + return true; + } + + sal_Int32 nSelection = rBox.get_selected_index(); + if (nSelection >= 0) + { + OUString sKey = ODF_FORMDROPDOWN_RESULT; + (*m_pFieldmark->GetParameters())[sKey] <<= nSelection; + m_pFieldmark->Invalidate(); + SwView& rView = static_cast(GetParent())->GetView(); + rView.GetDocShell()->SetModified(); + } + + EndPopupMode(); + + return true; +} + +DropDownFormFieldButton::DropDownFormFieldButton(SwEditWin* pEditWin, + sw::mark::DropDownFieldmark& rFieldmark) + : FormFieldButton(pEditWin, rFieldmark) +{ +} + +DropDownFormFieldButton::~DropDownFormFieldButton() { disposeOnce(); } + +void DropDownFormFieldButton::InitPopup() +{ + m_pFieldPopup = VclPtr::Create(static_cast(GetParent()), + &m_rFieldmark, GetSizePixel().Width()); +} + +/* 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..43d8ff6e0 --- /dev/null +++ b/sw/source/core/crsr/FormFieldButton.cxx @@ -0,0 +1,151 @@ +/* -*- 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 +#include +#include +#include +#include +#include + +FormFieldButton::FormFieldButton(SwEditWin* pEditWin, sw::mark::Fieldmark& rFieldmark) + : MenuButton(pEditWin, WB_DIALOGCONTROL) + , m_rFieldmark(rFieldmark) +{ + assert(GetParent()); + assert(dynamic_cast(GetParent())); +} + +FormFieldButton::~FormFieldButton() { disposeOnce(); } + +void FormFieldButton::dispose() +{ + m_pFieldPopup.disposeAndClear(); + MenuButton::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::MouseButtonUp(const MouseEvent&) +{ + assert(GetParent()); + + Point aPixPos = GetPosPixel(); + aPixPos.AdjustY(GetSizePixel().Height()); + + // sets m_pFieldPopup + InitPopup(); + + m_pFieldPopup->SetPopupModeEndHdl(LINK(this, DropDownFormFieldButton, FieldPopupModeEndHdl)); + + tools::Rectangle aRect(GetParent()->OutputToScreenPixel(aPixPos), Size(0, 0)); + m_pFieldPopup->StartPopupMode(aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus); + Invalidate(); +} + +IMPL_LINK_NOARG(FormFieldButton, FieldPopupModeEndHdl, FloatingWindow*, void) +{ + m_pFieldPopup.disposeAndClear(); + 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_pFieldPopup ? 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 = MenuButton::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..0fc6d16da --- /dev/null +++ b/sw/source/core/crsr/annotationmark.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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sw::mark +{ + AnnotationMark::AnnotationMark( + const SwPaM& rPaM, + const OUString& rName ) + : MarkBase( rPaM, rName ) + { + if ( rName.getLength() == 0 ) + { + SetName( MarkBase::GenerateNewName("__Annotation__") ); + } + } + + AnnotationMark::~AnnotationMark() + { + } + + void AnnotationMark::InitDoc(SwDoc* const io_pDoc, + 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(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(pPostItField)->SetName( GetName() ); + } + + if (io_pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + io_pDoc->GetIDocumentUndoRedo().AppendUndo( std::make_unique(*this) ); + } + io_pDoc->getIDocumentState().SetModified(); + } + + const SwFormatField* AnnotationMark::GetAnnotationFormatField() const + { + SwDoc* pDoc = GetMarkPos().GetDoc(); + assert(pDoc != nullptr); + + const auto sName = GetName(); + SwFieldType* pType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::Postit, OUString(), false ); + std::vector vFields; + pType->GatherFields(vFields); + auto ppFound = std::find_if(vFields.begin(), vFields.end(), [&sName](SwFormatField* pF) + { + auto pPF = dynamic_cast(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/bookmrk.cxx b/sw/source/core/crsr/bookmrk.cxx new file mode 100644 index 000000000..bd7e5d48f --- /dev/null +++ b/sw/source/core/crsr/bookmrk.cxx @@ -0,0 +1,1068 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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()); + sal_uLong const nStartNode(rStartPos.nNode.GetIndex()); + sal_uLong const nEndNode(rEndPos.nNode.GetIndex()); + int nFields(0); + std::optional ret; + for (sal_uLong 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()) + { + assert(nStartNode <= pNode->StartOfSectionIndex()); + // fieldmark cannot overlap node section + n = pNode->StartOfSectionIndex(); + } + else + { + assert(pNode->IsNoTextNode()); + } + } + 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(Fieldmark const * const pField, + const sal_Unicode aStartMark, + const sal_Unicode aEndMark) + { + if (aEndMark != CH_TXT_ATR_FORMELEMENT) + { + SwPosition const& rStart(pField->GetMarkStart()); + assert(rStart.nNode.GetNode().GetTextNode()->GetText()[rStart.nContent.GetIndex()] == aStartMark); (void) rStart; (void) aStartMark; + SwPosition const sepPos(sw::mark::FindFieldSep(*pField)); + assert(sepPos.nNode.GetNode().GetTextNode()->GetText()[sepPos.nContent.GetIndex()] == CH_TXT_ATR_FIELDSEP); (void) sepPos; + } + SwPosition const& rEnd(pField->GetMarkEnd()); + assert(rEnd.nNode.GetNode().GetTextNode()->GetText()[rEnd.nContent.GetIndex() - 1] == aEndMark); (void) rEnd; + } + + void lcl_SetFieldMarks(Fieldmark* const pField, + SwDoc* const io_pDoc, + const sal_Unicode aStartMark, + const sal_Unicode aEndMark, + SwPosition const*const pSepPos) + { + io_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr); + OUString startChar(aStartMark); + if (aEndMark != CH_TXT_ATR_FORMELEMENT + && pField->GetMarkStart() == pField->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 = pField->GetMarkStart(); + if (aEndMark != CH_TXT_ATR_FORMELEMENT) + { + SwPaM aStartPaM(start); + io_pDoc->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 + pField->SetMarkStartPos( start ); + SwPosition& rEnd = pField->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_pDoc->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 = pField->GetMarkEnd(); + if (aEndMark && startChar.getLength() == 1) + { + SwPaM aEndPaM(rEnd); + io_pDoc->getIDocumentContentOperations().InsertString(aEndPaM, OUString(aEndMark)); + ++rEnd.nContent; + } + lcl_AssertFieldMarksSet(pField, aStartMark, aEndMark); + + io_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr); + }; + + void lcl_RemoveFieldMarks(Fieldmark const * const pField, + SwDoc* const io_pDoc, + const sal_Unicode aStartMark, + const sal_Unicode aEndMark) + { + io_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr); + + const SwPosition& rStart = pField->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(*pField)); + io_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(rStart, aStartMark); + io_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(sepPos, CH_TXT_ATR_FIELDSEP); + } + + const SwPosition& rEnd = pField->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_pDoc->GetDocumentContentOperationsManager().DeleteDummyChar(aEnd, aEndMark); + + io_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr); + }; + + auto InvalidatePosition(SwPosition const& rPos) -> void + { + SwUpdateAttr const hint(rPos.nContent.GetIndex(), rPos.nContent.GetIndex(), 0); + rPos.nNode.GetNode().GetTextNode()->NotifyClients(nullptr, &hint); + } +} + +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(rNewPos).swap(m_pPos1); + m_pPos1->nContent.SetMark(this); + } + + void MarkBase::SetOtherMarkPos(const SwPosition& rNewPos) + { + std::make_unique(rNewPos).swap(m_pPos2); + m_pPos2->nContent.SetMark(this); + } + + OUString MarkBase::ToString( ) const + { + return "Mark: ( Name, [ Node1, Index1 ] ): ( " + m_aName + ", [ " + + OUString::number( GetMarkPos().nNode.GetIndex( ) ) + ", " + + OUString::number( GetMarkPos().nContent.GetIndex( ) ) + " ] )"; + } + + void MarkBase::dumpAsXml(xmlTextWriterPtr pWriter) const + { + xmlTextWriterStartElement(pWriter, BAD_CAST("MarkBase")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(m_aName.toUtf8().getStr())); + xmlTextWriterStartElement(pWriter, BAD_CAST("markPos")); + GetMarkPos().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + if (IsExpanded()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("otherMarkPos")); + GetOtherMarkPos().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + xmlTextWriterEndElement(pWriter); + } + + MarkBase::~MarkBase() + { } + + OUString MarkBase::GenerateNewName(const OUString& 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::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::Modify( const SfxPoolItem *pOld, const SfxPoolItem *pNew ) + { + NotifyClients(pOld, pNew); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached uno object + SetXBookmark(uno::Reference(nullptr)); + } + } + + auto MarkBase::InvalidateFrames() -> void + { + } + + NavigatorReminder::NavigatorReminder(const SwPaM& rPaM) + : MarkBase(rPaM, MarkBase::GenerateNewName("__NavigatorReminder__")) + { } + + UnoMark::UnoMark(const SwPaM& aPaM) + : MarkBase(aPaM, MarkBase::GenerateNewName("__UnoMark__")) + { } + + DdeBookmark::DdeBookmark(const SwPaM& aPaM) + : MarkBase(aPaM, MarkBase::GenerateNewName("__DdeLink__")) + { } + + void DdeBookmark::SetRefObject(SwServerObject* pObj) + { + m_aRefObj = pObj; + } + + void DdeBookmark::DeregisterFromDoc(SwDoc* const pDoc) + { + if(m_aRefObj.is()) + pDoc->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) + , ::sfx2::Metadatable() + , m_aCode(rCode) + , m_bHidden(false) + { + m_aName = rName; + } + + void Bookmark::InitDoc(SwDoc* const io_pDoc, + sw::mark::InsertMode const, SwPosition const*const) + { + if (io_pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + io_pDoc->GetIDocumentUndoRedo().AppendUndo( + std::make_unique(*this)); + } + io_pDoc->getIDocumentState().SetModified(); + InvalidateFrames(); + } + + void Bookmark::DeregisterFromDoc(SwDoc* const io_pDoc) + { + DdeBookmark::DeregisterFromDoc(io_pDoc); + + if (io_pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + io_pDoc->GetIDocumentUndoRedo().AppendUndo( + std::make_unique(*this)); + } + io_pDoc->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; + InvalidateFrames(); + } + } + + ::sfx2::IXmlIdRegistry& Bookmark::GetRegistry() + { + SwDoc *const pDoc( GetMarkPos().GetDoc() ); + assert(pDoc); + return pDoc->GetXmlIdRegistry(); + } + + bool Bookmark::IsInClipboard() const + { + SwDoc *const pDoc( GetMarkPos().GetDoc() ); + assert(pDoc); + return pDoc->IsClipBoard(); + } + + bool Bookmark::IsInUndo() const + { + return false; + } + + bool Bookmark::IsInContent() const + { + SwDoc *const pDoc( GetMarkPos().GetDoc() ); + assert(pDoc); + return !pDoc->IsInHeaderFooter( GetMarkPos().nNode ); + } + + uno::Reference< rdf::XMetadatable > Bookmark::MakeUnoObject() + { + SwDoc *const pDoc( GetMarkPos().GetDoc() ); + assert(pDoc); + const uno::Reference< rdf::XMetadatable> xMeta( + SwXBookmark::CreateXBookmark(*pDoc, this), uno::UNO_QUERY); + return xMeta; + } + + Fieldmark::Fieldmark(const SwPaM& rPaM) + : MarkBase(rPaM, MarkBase::GenerateNewName("__Fieldmark__")) + { + if(!IsExpanded()) + SetOtherMarkPos(GetMarkPos()); + } + + void Fieldmark::SetMarkStartPos( const SwPosition& rNewStartPos ) + { + if ( GetMarkPos( ) <= GetOtherMarkPos( ) ) + return SetMarkPos( rNewStartPos ); + else + return SetOtherMarkPos( rNewStartPos ); + } + + void Fieldmark::SetMarkEndPos( const SwPosition& rNewEndPos ) + { + if ( GetMarkPos( ) <= GetOtherMarkPos( ) ) + return SetOtherMarkPos( rNewEndPos ); + else + return SetMarkPos( rNewEndPos ); + } + + OUString Fieldmark::ToString( ) const + { + return "Fieldmark: ( Name, Type, [ Nd1, Id1 ], [ Nd2, Id2 ] ): ( " + m_aName + ", " + + m_aFieldname + ", [ " + OUString::number( GetMarkPos().nNode.GetIndex( ) ) + + ", " + OUString::number( GetMarkPos( ).nContent.GetIndex( ) ) + " ], [" + + OUString::number( 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 + { + xmlTextWriterStartElement(pWriter, BAD_CAST("Fieldmark")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldname"), BAD_CAST(m_aFieldname.toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldHelptext"), BAD_CAST(m_aFieldHelptext.toUtf8().getStr())); + MarkBase::dumpAsXml(pWriter); + xmlTextWriterStartElement(pWriter, BAD_CAST("parameters")); + for (auto& rParam : m_vParams) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("parameter")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(rParam.first.toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(comphelper::anyToString(rParam.second).toUtf8().getStr())); + xmlTextWriterEndElement(pWriter); + } + xmlTextWriterEndElement(pWriter); + xmlTextWriterEndElement(pWriter); + } + + TextFieldmark::TextFieldmark(const SwPaM& rPaM, const OUString& rName) + : Fieldmark(rPaM) + { + if ( !rName.isEmpty() ) + m_aName = rName; + } + + void TextFieldmark::InitDoc(SwDoc* const io_pDoc, + sw::mark::InsertMode const eMode, SwPosition const*const pSepPos) + { + if (eMode == sw::mark::InsertMode::New) + { + lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos); + // no need to invalidate text frames here, the insertion of the + // CH_TXT_ATR already invalidates + } + else + { + lcl_AssertFieldMarksSet(this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); + } + } + + void TextFieldmark::ReleaseDoc(SwDoc* const pDoc) + { + IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); + if (rIDUR.DoesUndo()) + { + rIDUR.AppendUndo(std::make_unique(*this)); + } + ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes + lcl_RemoveFieldMarks(this, pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); + } + + NonTextFieldmark::NonTextFieldmark(const SwPaM& rPaM) + : Fieldmark(rPaM) + { } + + void NonTextFieldmark::InitDoc(SwDoc* const io_pDoc, + sw::mark::InsertMode const eMode, SwPosition const*const pSepPos) + { + assert(pSepPos == nullptr); + if (eMode == sw::mark::InsertMode::New) + { + lcl_SetFieldMarks(this, io_pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT, pSepPos); + + // For some reason the end mark is moved from 1 by the Insert: + // we don't want this for checkboxes + SwPosition aNewEndPos = GetMarkEnd(); + aNewEndPos.nContent--; + SetMarkEndPos( aNewEndPos ); + } + else + { + lcl_AssertFieldMarksSet(this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT); + } + } + + void NonTextFieldmark::ReleaseDoc(SwDoc* const pDoc) + { + IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); + if (rIDUR.DoesUndo()) + { + rIDUR.AppendUndo(std::make_unique(*this)); + } + ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes + lcl_RemoveFieldMarks(this, pDoc, + 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 *const pDoc( GetMarkPos().GetDoc() ); + if ( pDoc ) + pDoc->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::HideButton() + { + if(m_pButton) + m_pButton->Show(false); + } + + void FieldmarkWithDropDownButton::RemoveButton() + { + if(m_pButton) + m_pButton.disposeAndClear(); + } + + DropDownFieldmark::DropDownFieldmark(const SwPaM& rPaM) + : FieldmarkWithDropDownButton(rPaM) + { + } + + DropDownFieldmark::~DropDownFieldmark() + { + SendLOKMessage("hide"); + } + + void DropDownFieldmark::ShowButton(SwEditWin* pEditWin) + { + if(pEditWin) + { + if(!m_pButton) + m_pButton = VclPtr::Create(pEditWin, *this); + m_pButton->CalcPosAndSize(m_aPortionPaintArea); + m_pButton->Show(); + SendLOKMessage("show"); + } + } + + void DropDownFieldmark::HideButton() + { + SendLOKMessage("hide"); + FieldmarkWithDropDownButton::HideButton(); + } + + void DropDownFieldmark::RemoveButton() + { + SendLOKMessage("hide"); + FieldmarkWithDropDownButton::RemoveButton(); + } + + void DropDownFieldmark::SetPortionPaintArea(const SwRect& rPortionPaintArea) + { + m_aPortionPaintArea = rPortionPaintArea; + if(m_pButton) + { + m_pButton->Show(); + m_pButton->CalcPosAndSize(m_aPortionPaintArea); + SendLOKMessage("show"); + } + } + + void DropDownFieldmark::SendLOKMessage(const OString& sAction) + { + const SfxViewShell* pViewShell = SfxViewShell::Current(); + if (pViewShell && pViewShell->isLOKMobilePhone()) + { + return; + } + + if (comphelper::LibreOfficeKit::isActive()) + { + if (!m_pButton) + return; + + SwEditWin* pEditWin = dynamic_cast(m_pButton->GetParent()); + if (!pEditWin) + return; + + OStringBuffer sPayload; + if (sAction == "show") + { + if(m_aPortionPaintArea.IsEmpty()) + return; + + sPayload = OStringLiteral("{\"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 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 + OUString sResultKey = ODF_FORMDROPDOWN_RESULT; + auto pSelectedItemIter = pParameters->find(sResultKey); + 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) + "\"}}"); + } + else + { + sPayload = "{\"action\": \"hide\", \"type\": \"drop-down\"}"; + } + if (sPayload.toString() != m_sLastSentLOKMsg) { + m_sLastSentLOKMsg = sPayload.toString(); + pEditWin->GetView().GetWrtShell().GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, m_sLastSentLOKMsg.getStr()); + } + } + } + + DateFieldmark::DateFieldmark(const SwPaM& rPaM) + : FieldmarkWithDropDownButton(rPaM) + , m_pNumberFormatter(nullptr) + , m_pDocumentContentOperationsManager(nullptr) + { + } + + DateFieldmark::~DateFieldmark() + { + } + + void DateFieldmark::InitDoc(SwDoc* const io_pDoc, + sw::mark::InsertMode eMode, SwPosition const*const pSepPos) + { + m_pNumberFormatter = io_pDoc->GetNumberFormatter(); + m_pDocumentContentOperationsManager = &io_pDoc->GetDocumentContentOperationsManager(); + if (eMode == sw::mark::InsertMode::New) + { + lcl_SetFieldMarks(this, io_pDoc, 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* const pDoc) + { + IDocumentUndoRedo & rIDUR(pDoc->GetIDocumentUndoRedo()); + if (rIDUR.DoesUndo()) + { + // TODO does this need a 3rd Undo class? + rIDUR.AppendUndo(std::make_unique(*this)); + } + ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes + lcl_RemoveFieldMarks(this, pDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND); + } + + void DateFieldmark::ShowButton(SwEditWin* pEditWin) + { + if(pEditWin) + { + if(!m_pButton) + m_pButton = VclPtr::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 DateFieldmark::GetCurrentDate() const + { + // Check current date param first + std::pair 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(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) + { + Color* pCol = nullptr; + m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentDate, &pCol, false); + } + return sCurrentDate; + } + + std::pair 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(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) + { + Color* pCol = nullptr; + m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentContent, &pCol, false); + } + return sCurrentContent; + } + + void DateFieldmark::InvalidateCurrentDateParam() + { + std::pair 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..f6c277036 --- /dev/null +++ b/sw/source/core/crsr/callnk.cxx @@ -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 . + */ + +#include +#include +#include +#include "callnk.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + } +} + +static void lcl_notifyRow(const SwContentNode* pNode, SwCursorShell & rShell) +{ + if ( !pNode ) + return; + + SwFrame *const pMyFrame = pNode->getLayoutFrame( rShell.GetLayout() ); + if ( !pMyFrame ) + return; + + // We need to emulated a change of the row height in order + // to have the complete row redrawn + SwRowFrame *const pRow = pMyFrame->FindRowFrame(); + if ( !pRow ) + return; + + const SwTableLine* pLine = pRow->GetTabLine( ); + + if (rShell.IsTableMode() || (rShell.StartsWithTable() && rShell.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; + } + + SwFormatFrameSize aSize = pLine->GetFrameFormat()->GetFrameSize(); + pRow->ModifyNotification(nullptr, &aSize); +} + +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; + + lcl_notifyRow(pCNd, m_rShell); + + const SwDoc *pDoc=m_rShell.GetDoc(); + const SwContentNode *pNode = nullptr; + if ( pDoc && m_nNode < pDoc->GetNodes( ).Count( ) ) + { + pNode = pDoc->GetNodes()[m_nNode]->GetContentNode(); + } + lcl_notifyRow(pNode, m_rShell); + + sal_Int32 nCmp, nCurrentContent = pCurrentCursor->GetPoint()->nContent.GetIndex(); + SwNodeType nNdWhich = pCNd->GetNodeType(); + sal_uLong 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() ) + { + 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() ); + } +} + +long SwCallLink::getLayoutFrame(const SwRootFrame* pRoot, + SwTextNode const & rNd, sal_Int32 nCntPos, bool /*bCalcFrame*/) +{ + SwTextFrame* pFrame = static_cast(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..241491917 --- /dev/null +++ b/sw/source/core/crsr/callnk.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_CRSR_CALLNK_HXX +#define INCLUDED_SW_SOURCE_CORE_CRSR_CALLNK_HXX + +#include +#include + +class SwCursorShell; +class SwTextNode; +class SwRootFrame; + +class SwCallLink +{ +public: + SwCursorShell & m_rShell; + sal_uLong m_nNode; + sal_Int32 m_nContent; + SwNodeType m_nNodeType; + long m_nLeftFramePos; + bool m_bHasSelection; + + explicit SwCallLink( SwCursorShell & rSh ); + ~SwCallLink() COVERITY_NOEXCEPT_FALSE; + + static 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/crbm.cxx b/sw/source/core/crsr/crbm.cxx new file mode 100644 index 000000000..2311cf8d9 --- /dev/null +++ b/sw/source/core/crsr/crbm.cxx @@ -0,0 +1,318 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace +{ + struct CursorStateHelper + { + explicit CursorStateHelper(SwCursorShell const & rShell) + : m_pCursor(rShell.GetSwCursor()) + , 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.IsHideRedlines()) + { + return false; + } + SwTextNode const& rNode(*rMark.GetMarkPos().nNode.GetNode().GetTextNode()); + SwTextFrame const*const pFrame(static_cast( + rNode.getLayoutFrame(&rLayout))); + if (!pFrame) + { + return true; + } + if (rMark.IsExpanded()) + { + SwTextFrame const*const pOtherFrame(static_cast( + rMark.GetOtherMarkPos().nNode.GetNode().GetTextNode()->getLayoutFrame(&rLayout))); + return pFrame == pOtherFrame + && pFrame->MapModelToViewPos(rMark.GetMarkPos()) + == pFrame->MapModelToViewPos(rMark.GetOtherMarkPos()); + } + else + { + if (rMark.GetMarkPos().nContent.GetIndex() == rNode.Len()) + { // at end of node: never deleted (except if node deleted) + return rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden; + } + else + { // check character following mark pos + return pFrame->MapModelToViewPos(rMark.GetMarkPos()) + == pFrame->MapModelToView(&rNode, 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..9fbba6de0 --- /dev/null +++ b/sw/source/core/crsr/crossrefbookmark.cxx @@ -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 . + */ + +#include + +#include + +#include +#include +#include + +namespace +{ + const char CrossRefNumItemBookmark_NamePrefix[] = "__RefNumPara__"; +} + +namespace sw::mark +{ + CrossRefBookmark::CrossRefBookmark(const SwPaM& rPaM, + const vcl::KeyCode& rCode, + const OUString& rName, + const OUString& rPrefix) + : Bookmark( + // ensure that m_pPos2 is null by only passing start to super + SwPaM(*rPaM.Start()), rCode, rName) + { + assert( IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark(rPaM) + && "" + "- 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() && + "" + " - new bookmark position for cross-reference bookmark doesn't mark text node"); + assert(rNewPos.nContent.GetIndex() == 0 && + "" + " - new bookmark position for cross-reference bookmark doesn't mark start of text node"); + MarkBase::SetMarkPos(rNewPos); + } + + SwPosition& CrossRefBookmark::GetOtherMarkPos() const + { + assert(false && + "" + " - 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, IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc") + { } + + bool CrossRefHeadingBookmark::IsLegalName(const OUString& rName) + { + return rName.match(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()); + } + + CrossRefNumItemBookmark::CrossRefNumItemBookmark(const SwPaM& rPaM, + const vcl::KeyCode& rCode, + const OUString& rName) + : CrossRefBookmark(rPaM, rCode, rName, CrossRefNumItemBookmark_NamePrefix) + { } + + bool CrossRefNumItemBookmark::IsLegalName(const OUString& rName) + { + return rName.match(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..7a0c3b881 --- /dev/null +++ b/sw/source/core/crsr/crsrsh.cxx @@ -0,0 +1,3814 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BlockCursor.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "callnk.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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->GetPoint() == pStt ? pCurrentCursor->GetMark() : pCurrentCursor->GetPoint(); + + SwPaM *pTmpDel = nullptr, + *pTmp = pCurrentCursor->GetNext(); + + // Search the complete ring + while( pTmp != pCurrentCursor ) + { + const SwPosition *pTmpStt = pTmp->Start(), + *pTmpEnd = pTmp->GetPoint() == pTmpStt ? + pTmp->GetMark() : pTmp->GetPoint(); + 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()); + + // 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(m_pCurrentCursor->GetNext()); + delete m_pCurrentCursor; + m_pCurrentCursor = dynamic_cast(pNextCursor); + UpdateCursor(); +} + +/** + * Create and return a new shell cursor. + * Simply returns the current shell cursor if there is no selection + * (HasSelection()). + */ +SwPaM & SwCursorShell::CreateNewShellCursor() +{ + if (HasSelection()) + { + (void) CreateCursor(); // n.b. returns old cursor + } + return *GetCursor(); +} + +/** + * Return the current shell cursor + * @return - returns current `SwPaM` shell cursor + */ +SwPaM & SwCursorShell::GetCurrentShellCursor() +{ + return *GetCursor(); +} + +/** + * Return pointer to the current shell cursor + * @return - returns pointer to current `SwPaM` shell cursor + */ +SwPaM* SwCursorShell::GetCursor( bool bMakeTableCursor ) const +{ + if( m_pTableCursor ) + { + if( bMakeTableCursor && m_pTableCursor->IsCursorMovedUpdate() ) + { + //don't re-create 'parked' cursors + const SwContentNode* pCNd; + if( m_pTableCursor->GetPoint()->nNode.GetIndex() && + m_pTableCursor->GetMark()->nNode.GetIndex() ) + { + 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(this)->m_pCurrentCursor = + dynamic_cast(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, const bool DoSetPosX ) +{ + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); + bool bVis = m_bSVCursorVis; + + sal_uInt16 eFlags = SwCursorShell::CHKRANGE; + if ( !DoSetPosX ) + eFlags |= SwCursorShell::UPDOWN; + + + // Idle-formatting? + if( bIdleEnd && Imp()->GetRegion() ) + { + 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; + } + + 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, true ); + --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( + 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 flag is no longer + // reflected in the return value . + const bool bResetOfInFrontOfLabel = SetInFrontOfLabel( false ); + bRet = pShellCursor->LeftRight( bLeft, nCnt, nMode, bVisualAllowed, + bSkipHidden, !IsOverwriteCursor(), + GetLayout()); + if ( !bRet && bLeft && bResetOfInFrontOfLabel ) + { + // undo reset of flag + SetInFrontOfLabel( true ); + } + } + + if( bRet ) + { + UpdateCursor(); + } + + return bRet; +} + +void SwCursorShell::MarkListLevel( const OUString& sListId, + const int nListLevel ) +{ + if ( sListId != m_sMarkedListId || + nListLevel != m_nMarkedListLevel) + { + 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 ) + { + 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(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 ) +{ + SET_CURR_SHELL( 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(eUpdateMode + | SwCursorShell::UPDOWN | SwCursorShell::CHKRANGE); + UpdateCursor( static_cast(eUpdateMode) ); + } + } + return bRet; +} + +bool SwCursorShell::LRMargin( bool bLeft, bool bAPI) +{ + SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed + SET_CURR_SHELL( 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); + + nNode = rNodes.GetEndOfContent(); + SwContentNode* pEnd = SwNodes::GoPrevious(&nNode); + + if (!pStart || !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 + SET_CURR_SHELL( this ); + + SwCursorSaveState aSaveState( *m_pCurrentCursor ); + Point& rPt = m_pCurrentCursor->GetPtPos(); + std::pair 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 tmp(pShellCursor->GetPtPos(), false); + SwContentFrame *const pFrame = pCNode + ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp) + : nullptr; + return !pFrame || (pFrame->IsTextFrame() && static_cast(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 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(*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 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(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 ) +{ + SET_CURR_SHELL( 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().IsInside( rCurrentCursorPt )) + return bRet; + } + else if( aPos.nNode.GetNode().IsContentNode() ) + { + // in the same frame? + std::pair tmp(m_aCharRect.Pos(), false); + SwFrame* pOld = static_cast(aPos.nNode.GetNode()).getLayoutFrame( + GetLayout(), nullptr, &tmp); + tmp.first = aPt; + SwFrame* pNew = static_cast(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().IsInside( 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 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 ) +{ + SET_CURR_SHELL( this ); + + // check if the SPoint is in a table selection + if( m_pTableCursor ) + return m_pTableCursor->IsInside( rPt ); + + SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed + // search position 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 SwPaM *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()->IsHideRedlines()) + { + 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()->IsHideRedlines()) + { + SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()); + if (pNode) + { + SwTextFrame const*const pFrame(static_cast( + 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()->IsHideRedlines()) + { + SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()); + if (pNode) + { + SwTextFrame const*const pFrame(static_cast( + 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 ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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(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() +{ + SET_CURR_SHELL(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(pPg->GetNext()); + } + + sal_uInt16 nPageNo = 0; + while (pPg) + { + if (!pPg->IsEmptyPage()) + ++nPageNo; + pPg = static_cast(pPg->GetPrev()); + } + return nPageNo; +} + +sal_uInt16 SwCursorShell::GetNextPrevPageNum( bool bNext ) +{ + SET_CURR_SHELL( 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(pPg->GetNext()); + } + while( pPg && pPg->getFrameArea().Top() == nPageTop ); + + while( pPg && pPg->IsEmptyPage() ) + pPg = static_cast(pPg->GetNext()); + } + else + { + // go to previous view layout row: + do + { + pPg = static_cast(pPg->GetPrev()); + } + while( pPg && pPg->getFrameArea().Top() == nPageTop ); + + while( pPg && pPg->IsEmptyPage() ) + pPg = static_cast(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() +{ + SET_CURR_SHELL( 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(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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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; +} + +bool SwCursorShell::GoNextPrevCursorSetSearchLabel(const bool bNext) +{ + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + + if( !m_pCurrentCursor->IsMultiSelection() ) + { + if( !m_pCurrentCursor->HasMark() ) + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + + return bNext ? GoNextCursor() : GoPrevCursor(); +} + +void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect) +{ + comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); + SET_CURR_SHELL( 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.IsOver( 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 ) +{ + SET_CURR_SHELL( 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() +{ + SET_CURR_SHELL( 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 ) ); +} + +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(); + } +}; + +} + +void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) +{ + SET_CURR_SHELL( this ); + ClearUpCursors(); + + if (ActionPend()) + { + if ( eFlags & SwCursorShell::READONLY ) + m_bIgnoreReadonly = true; + return; // if not then no update + } + + SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this ); + + 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 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 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 (Imp()->IsAccessible()) + Imp()->InvalidateAccessibleCursorPosition( pTableFrame ); + 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 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 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 (Imp()->IsAccessible()) + Imp()->InvalidateAccessibleCursorPosition( pFrame ); + + // 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() +{ + SwWrtShell* pShell = GetDoc()->GetDocShell()->GetWrtShell(); + if (!pShell) + return; + + SwFrame* pCurrentFrame = GetCurrFrame(); + SelectionType eType = pShell->GetSelectionType(); + + boost::property_tree::ptree aRootTree; + + if (pCurrentFrame && (eType & SelectionType::Table) && pCurrentFrame->IsInTab()) + { + const SwRect& rPageRect = pShell->GetAnyCurRect(CurRectType::Page, nullptr); + + boost::property_tree::ptree aTableColumns; + { + SwTabCols aTabCols; + pShell->GetTabCols(aTabCols); + + const int nColumnOffset = aTabCols.GetLeftMin() + rPageRect.Left(); + + aTableColumns.put("left", aTabCols.GetLeft()); + aTableColumns.put("right", aTabCols.GetRight()); + aTableColumns.put("tableOffset", nColumnOffset); + + boost::property_tree::ptree aEntries; + for (size_t i = 0; i < aTabCols.Count(); ++i) + { + auto const & rEntry = aTabCols.GetEntry(i); + boost::property_tree::ptree aTableColumnEntry; + aTableColumnEntry.put("position", rEntry.nPos); + aTableColumnEntry.put("min", rEntry.nMin); + aTableColumnEntry.put("max", rEntry.nMax); + aTableColumnEntry.put("hidden", rEntry.bHidden); + aEntries.push_back(std::make_pair("", aTableColumnEntry)); + } + aTableColumns.push_back(std::make_pair("entries", aEntries)); + } + + boost::property_tree::ptree aTableRows; + { + SwTabCols aTabRows; + pShell->GetTabRows(aTabRows); + + const int nRowOffset = aTabRows.GetLeftMin() + rPageRect.Top(); + + aTableRows.put("left", aTabRows.GetLeft()); + aTableRows.put("right", aTabRows.GetRight()); + aTableRows.put("tableOffset", nRowOffset); + + boost::property_tree::ptree aEntries; + for (size_t i = 0; i < aTabRows.Count(); ++i) + { + auto const & rEntry = aTabRows.GetEntry(i); + boost::property_tree::ptree aTableRowEntry; + aTableRowEntry.put("position", rEntry.nPos); + aTableRowEntry.put("min", rEntry.nMin); + aTableRowEntry.put("max", rEntry.nMax); + aTableRowEntry.put("hidden", rEntry.bHidden); + aEntries.push_back(std::make_pair("", aTableRowEntry)); + } + aTableRows.push_back(std::make_pair("entries", aEntries)); + } + + aRootTree.add_child("columns", aTableColumns); + aRootTree.add_child("rows", aTableRows); + } + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aRootTree); + GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, aStream.str().c_str()); +} + +void SwCursorShell::RefreshBlockCursor() +{ + assert(m_pBlockCursor); + SwShellCursor &rBlock = m_pBlockCursor->getShellCursor(); + Point aPt = rBlock.GetPtPos(); + std::pair 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 ) ) + { + SwCursor* pNxt = static_cast(m_pCurrentCursor->GetNext()); + while( pNxt != m_pCurrentCursor ) + { + delete pNxt; + pNxt = static_cast(m_pCurrentCursor->GetNext()); + } + + std::list::iterator pStart = aSelList.getStart(); + std::list::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 if there was one on the stack, otherwise +*/ +bool SwCursorShell::Pop(PopMode const eDelete) +{ + SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed + + // 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() ) + { + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 ) + { + m_bSVCursorVis = true; + m_pCurrentCursor->SetShowTextInputFieldOverlay( true ); + + 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); + } + + UpdateCursor(); + } +} + +void SwCursorShell::HideCursor() +{ + if( !m_bBasicHideCursor ) + { + m_bSVCursorVis = false; + // possibly reverse selected areas!! + SET_CURR_SHELL( this ); + m_pCurrentCursor->SetShowTextInputFieldOverlay( 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() +{ + m_bHasFocus = true; + if( !m_bBasicHideCursor && VisArea().Width() ) + { + UpdateCursor( static_cast( SwCursorShell::CHKRANGE ) ); + ShowCursors( m_bSVCursorVis ); + } +} + +/** Get current frame in which the cursor is positioned. */ +SwContentFrame *SwCursorShell::GetCurrFrame( const bool bCalcFrame ) const +{ + SET_CURR_SHELL( const_cast(this) ); + SwContentFrame *pRet = nullptr; + SwContentNode *pNd = m_pCurrentCursor->GetContentNode(); + if ( pNd ) + { + if ( bCalcFrame ) + { + sal_uInt16* pST = const_cast(&mnStartAction); + ++(*pST); + const Size aOldSz( GetDocSize() ); + std::pair const tmp(m_pCurrentCursor->GetPtPos(), true); + pRet = pNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp); + --(*pST); + if( aOldSz != GetDocSize() ) + const_cast(this)->SizeChgNotify(); + } + else + { + std::pair 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + const sal_uInt16 nWhich = pOld ? + pOld->Which() : + pNew ? + pNew->Which() : + sal::static_int_cast(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(); + + if( m_aGrfArrivedLnk.IsSet() && + ( RES_GRAPHIC_ARRIVED == nWhich || RES_GRAPHIC_SWAPIN == nWhich )) + 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()->IsHideRedlines()) + { + SwContentFrame const*const pFrame(GetCurrFrame(false)); + if (FrameContainsNode(*pFrame, m_pCurrentCursor->GetMark()->nNode.GetIndex())) + { + OUStringBuffer buf; + SwPosition const*const pStart(m_pCurrentCursor->Start()); + SwPosition const*const pEnd(m_pCurrentCursor->End()); + for (sal_uLong 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, 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 if SPoint was corrected by the layout. +*/ +bool SwCursorShell::SetVisibleCursor( const Point &rPt ) +{ + SET_CURR_SHELL( 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 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 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->GetPoint() == pStt ? pDelRg->GetMark() : pDelRg->GetPoint(); + + SwPaM *pTmpDel = nullptr, *pTmp = *ppDelRing; + + // search over the whole ring + bool bGoNext; + do { + + if (!pTmp) + break; + + const SwPosition *pTmpStt = pTmp->Start(), + *pTmpEnd = pTmp->GetPoint() == pTmpStt ? + pTmp->GetMark() : pTmp->GetPoint(); + // 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 = 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 + std::unique_ptr pNew( new SwPaM( *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) + pNew->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) + pNew->GetPoint()->nNode = *pNode->EndOfSectionNode()->StartOfSectionNode(); + } + else + pNew->GetPoint()->nNode = *pNode->StartOfSectionNode(); + pNew->SetMark(); + pNew->GetPoint()->nNode = *pNode->EndOfSectionNode(); + + // take care of all shells + for(SwViewShell& rTmp : GetRingContainer()) + { + if( dynamic_cast(&rTmp) != nullptr) + { + SwCursorShell* pSh = static_cast(&rTmp); + if (pSh->m_pStackCursor) + pSh->ParkPams(pNew.get(), &pSh->m_pStackCursor); + + pSh->ParkPams( pNew.get(), &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 = 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 ) + , SwModify() + , 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_sMarkedListId() + , m_nMarkedListLevel( 0 ) + , m_oldColFrame(nullptr) +{ + SET_CURR_SHELL( 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_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 ) + , SwModify() + , 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_sMarkedListId() + , m_nMarkedListLevel( 0 ) + , m_oldColFrame(nullptr) +{ + SET_CURR_SHELL( 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_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() + 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 ); + 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; + sal_uLong 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(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 +{ + 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(); + SwPaM* 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( !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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwCursorShell")); + + SwViewShell::dumpAsXml(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_pCurrentCursor")); + for (const SwPaM& rPaM : m_pCurrentCursor->GetRingContainer()) + rPaM.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + 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 ); + } + } + } + + if ( !rSmartTagTypes.empty() ) + { + rStringKeyMaps = comphelper::containerToSequence(aStringKeyMaps); + } +} + +static void lcl_FillTextRange( uno::Reference& 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 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() ) + { + const SwWrongList *pSmartTagList = pNode->GetSmartTags(); + if ( pSmartTagList ) + { + sal_Int32 nCurrent = aPos.nContent.GetIndex(); + sal_Int32 nBegin = nCurrent; + sal_Int32 nLen = 1; + + if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin)) + { + 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() ) + { + sal_Int32 nBegin = aPos.nContent.GetIndex(); + sal_Int32 nLen = 1; + + if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin)) + { + // 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 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..4dbb5e732 --- /dev/null +++ b/sw/source/core/crsr/crstrvl.cxx @@ -0,0 +1,2592 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "callnk.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +void SwCursorShell::MoveCursorToNum() +{ + SwCallLink aLk( *this ); // watch Cursor-Moves + SwCursorSaveState aSaveState( *m_pCurrentCursor ); + if( ActionPend() ) + return; + SET_CURR_SHELL( this ); + // try to set cursor onto this position, at half of the char- + // SRectangle's height + Point aPt( m_pCurrentCursor->GetPtPos() ); + std::pair 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 ) + { + SET_CURR_SHELL( 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 ); + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 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(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(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() +{ + bool bRet = false; + + SwTOXMarks aMarks; + sal_uInt16 nCnt = SwDoc::GetCurTOXMark( *m_pCurrentCursor->GetPoint(), aMarks ); + if( nCnt ) + { + // Take the 1. and get the index type. Search in its dependency list + // for the actual index + const SwTOXType* pType = aMarks[0]->GetTOXType(); + SwIterator aIter( *pType ); + + for( SwTOXBase* pTOX = aIter.First(); pTOX; pTOX = aIter.Next() ) + { + auto pTOXBaseSection = dynamic_cast( pTOX); + if( !pTOXBaseSection ) + continue; + auto pSectFormat = pTOXBaseSection->GetFormat(); + if( !pSectFormat ) + continue; + const SwSectionNode* pSectNd = pSectFormat->GetSectionNode(); + if (!pSectNd) + continue; + SwNodeIndex aIdx( *pSectNd, 1 ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = GetDoc()->GetNodes().GoNext( &aIdx ); + if( !pCNd ) + continue; + if( pCNd->EndOfSectionIndex() >= pSectNd->EndOfSectionIndex() ) + continue; + const SwContentFrame* pCFrame = pCNd->getLayoutFrame( GetLayout() ); + if( pCFrame && ( IsReadOnlyAvailable() || !pCFrame->IsProtected() ) ) + { + SwCallLink aLk( *this ); // watch Cursor-Moves + SwCursorSaveState aSaveState( *m_pCurrentCursor ); + assert(pCFrame->IsTextFrame()); + *m_pCurrentCursor->GetPoint() = + static_cast(pCFrame) + ->MapViewToModelPos(TextFrameIndex(0)); + bRet = !m_pCurrentCursor->IsInProtectTable() && + !m_pCurrentCursor->IsSelOvr(); + if( bRet ) + UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); + break; + } + } + } +} + +/// 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 = 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 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(pItem); + if( !pFormulaItem ) + continue; + pTBox = pFormulaItem->GetTableBox(); + if( pTBox && + pTBox->GetSttNd() && + pTBox->GetSttNd()->GetNodes().IsDocNodes() && + ( !bOnlyErrors || + !pFormulaItem->HasValidBoxes() ) ) + { + const SwContentFrame* pCFrame; + SwNodeIndex aIdx( *pTBox->GetSttNd() ); + const SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aIdx ); + std::pair const tmp(aPt, false); + if (pCNd) + { + 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 = 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 ) + { + SET_CURR_SHELL( 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 = 0; + SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos ); + + if( rPos.nNode.GetIndex() < GetDoc()->GetNodes().GetEndOfExtras().GetIndex() ) + { + // also at collection use only the first frame + std::pair 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(pItem); + if( !pToxMarkItem ) + continue; + pTextTOX = pToxMarkItem->GetTextTOXMark(); + if( !pTextTOX ) + continue; + pTextNd = &pTextTOX->GetTextNode(); + if( !pTextNd->GetNodes().IsDocNodes() ) + continue; + std::pair 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 = 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 ) + { + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 vFields; + rFieldType.GatherFields(vFields, false); + for(SwFormatField* pFormatField: vFields) + { + SwTextField* pTextField = pFormatField->GetTextField(); + if ( pTextField != nullptr + && ( !bChkInpFlag + || static_cast(pTextField->GetFormatField().GetField())->GetInputFlag() ) ) + { + const SwTextNode& rTextNode = pTextField->GetTextNode(); + std::pair const tmp(aPt, false); + const SwContentFrame* pCFrame = + rTextNode.getLayoutFrame( + rTextNode.GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp); + if ( pCFrame != nullptr + && ( bInReadOnly || !pCFrame->IsProtected() ) ) + { + std::unique_ptr 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 pSrch; + std::unique_ptr 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 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(); + for( size_t i=0; i < nSize; ++i ) + { + pFieldType = rFieldTypes[ i ].get(); + if( 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(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(&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; + + SET_CURR_SHELL( 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::GotoFormatField( const SwFormatField& rField ) +{ + bool bRet = false; + SwTextField const*const pTextField(rField.GetTextField()); + if (pTextField + && (!GetLayout()->IsHideRedlines() + || !sw::IsFieldDeletedInModel( + GetDoc()->getIDocumentRedlineAccess(), *pTextField))) + { + SET_CURR_SHELL( 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(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(pCurField)->PtrToBoxNm( pTableNd ? &pTableNd->GetTable() : nullptr ); + } + + return pCurField; +} + +bool SwCursorShell::CursorInsideInputField() const +{ + for(SwPaM& rCursor : GetCursor()->GetRingContainer()) + { + if (dynamic_cast(GetTextFieldAtCursor(&rCursor, true))) + return true; + } + return false; +} + +bool SwCursorShell::PosInsideInputField( const SwPosition& rPos ) +{ + return dynamic_cast(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(GetTextFieldAtPos( &rPos, true )); + assert(pTextInputField != nullptr + && " - no Input Field at given position"); + return pTextInputField->GetStart(); +} + +sal_Int32 SwCursorShell::EndOfInputFieldAtPos( const SwPosition& rPos ) +{ + const SwTextInputField* pTextInputField = dynamic_cast(GetTextFieldAtPos( &rPos, true )); + assert(pTextInputField != nullptr + && " - no Input Field at given position"); + return *(pTextInputField->End()); +} + +void SwCursorShell::GotoOutline( SwOutlineNodes::size_type nIdx ) +{ + SwCursor* pCursor = getShellCursor( true ); + + SET_CURR_SHELL( 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 ); + + SET_CURR_SHELL( 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 ); + } + + SET_CURR_SHELL( 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 ); + } + SET_CURR_SHELL( 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* pCursor = 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) + { + return nPos; + } + } + return SwOutlineNodes::npos; // no more left +} + +bool 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 false; + + SET_CURR_SHELL( 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); + return bRet; +} + +/// jump to reference marker +bool SwCursorShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType, + sal_uInt16 nSeqNo ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( this ); + bool bRet = false; + + if( !IsTableMode() ) + { + Point aPt( rPt ); + SwPosition aPos( *m_pCurrentCursor->GetPoint() ); + + SwTextNode* pTextNd; + SwContentFrame *pFrame(nullptr); + SwTextAttr* pTextAttr; + 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() ) + { + const SwTextNode* pONd = pTextNd->FindOutlineNodeOfLevel(MAXLEVEL-1, GetLayout()); + if( pONd ) + { + rContentAtPos.eContentAtPos = IsAttrAtPos::Outline; + rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pONd, true, false, ExpandMode::ExpandFootnote); + 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 ) + { + if( !aTmpState.m_bPosCorr ) + { + 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 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 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(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(static_cast(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 && 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 ) + { + SwCallLink aLk( *this ); // watch Cursor-Moves + SwCursorSaveState aSaveState( *m_pCurrentCursor ); + m_pCurrentCursor->GetPoint()->nNode = *static_cast(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 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 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 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 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 ) + { + pTextAttr = pTextNd->GetTextAttrAt( + aPos.nContent.GetIndex(), 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 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 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::TableBoxFml & rContentAtPos.eContentAtPos +#ifdef DBG_UTIL + || IsAttrAtPos::TableBoxValue & rContentAtPos.eContentAtPos +#endif + ) ) + { + const SwTableNode* pTableNd; + const SwTableBox* pBox; + const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode(); + const SfxPoolItem* pItem; + if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) && + nullptr != ( pBox = pTableNd->GetTable().GetTableBox( + pSttNd->GetIndex() )) && +#ifdef DBG_UTIL + ( SfxItemState::SET == pBox->GetFrameFormat()->GetItemState( + RES_BOXATR_FORMULA, false, &pItem ) || + SfxItemState::SET == pBox->GetFrameFormat()->GetItemState( + RES_BOXATR_VALUE, false, &pItem )) +#else + SfxItemState::SET == pBox->GetFrameFormat()->GetItemState( + RES_BOXATR_FORMULA, false, &pItem ) +#endif + ) + { + std::pair tmp(aPt, true); + SwFrame* pF = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp); + if( pF ) + { + // then the CellFrame + pFrame = static_cast(pF); + while( pF && !pF->IsCellFrame() ) + pF = pF->GetUpper(); + } + + if( aTmpState.m_bPosCorr ) + { + if( pF && !pF->getFrameArea().IsInside( 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( RES_BOXATR_VALUE == pItem->Which() ) + rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxValue; + else +#endif + const_cast(static_cast(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(); + SfxItemSet aSet( GetDoc()->GetAttrPool(), svl::Items{} ); + 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( 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.toString(); + } + } + 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(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(pFndTextAttr)->GetpTextNode(); + break; + + case IsAttrAtPos::Ftn: + pNd = &static_cast(pFndTextAttr)->GetTextNode(); + break; + + case IsAttrAtPos::InetAttr: + pNd = static_txtattr_cast(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(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 aIter(*pNd); + SwTextFrame* pTmpFrame = aIter.First(); + while( pTmpFrame ) + { + if ( !pTmpFrame->IsFollow()) + { + bRet = pTmpFrame->IsRightToLeft(); + break; + } + pTmpFrame = aIter.Next(); + } + } + return bRet; +} + +bool SwCursorShell::SelectText( const sal_Int32 nStart, + const sal_Int32 nEnd ) +{ + SET_CURR_SHELL( 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; +} + +bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich, + bool bExpand, + const SwTextAttr* pTextAttr ) +{ + SET_CURR_SHELL( 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 = SelectText( 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 ); + + SET_CURR_SHELL( this ); + SwCallLink aLk( *this ); // watch Cursor-Moves + SwCursorSaveState aSaveState( *pCursor ); + + pCursor->GetPoint()->nNode = *rAttr.GetpTextNode(); + pCursor->GetPoint()->nContent.Assign( const_cast(rAttr.GetpTextNode()), + rAttr.GetStart() ); + bRet = !pCursor->IsSelOvr(); + if( bRet ) + UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); + } + return bRet; +} + +const SwFormatINetFormat* SwCursorShell::FindINetAttr( const OUString& rName ) const +{ + return mxDoc->FindINetAttr( rName ); +} + +bool SwCursorShell::GetShadowCursorPos( const Point& rPt, SwFillMode eFillMode, + SwRect& rRect, sal_Int16& rOrient ) +{ + + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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() )) + { + SfxItemSet aSet( + GetDoc()->GetAttrPool(), + svl::Items< + RES_PARATR_ADJUST, RES_PARATR_ADJUST, + RES_LR_SPACE, RES_LR_SPACE>{}); + SvxLRSpaceItem aLR( static_cast( + pCNd->GetAttr( RES_LR_SPACE ) ) ); + aLR.SetTextLeft( aFPos.nTabCnt ); + aLR.SetTextFirstLineOffset( 0 ); + aSet.Put( aLR ); + + const SvxAdjustItem& rAdj = static_cast(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() ) + { + SET_CURR_SHELL( 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 ); + 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() ) + { + SET_CURR_SHELL( 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 ); + 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() ) + { + SET_CURR_SHELL( 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(); + sal_uLong 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(); + SwContentFrame* pFrame; + std::pair tmp(aPt, true); + if (pCNd) + { + 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 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 ); + SwContentFrame* pFrame; + if (pTextNd->GetIndex() < nBodySttNdIdx) + { + std::pair tmp(aPt, true); + 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((*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 ) + { + SET_CURR_SHELL( 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(this)->SelectObj( pSObj->GetCurrentBoundRect().Center() ); + MakeSelVisible(); + bRet = true; + } + } + else // then is it a fly + { + SwFlyFrame* pFly = pFndFormat->GetFrame(&aPt); + if( pFly ) + { + static_cast(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 +#include + +#include + +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/findattr.cxx b/sw/source/core/crsr/findattr.cxx new file mode 100644 index 000000000..a2486ab29 --- /dev/null +++ b/sw/source/core/crsr/findattr.cxx @@ -0,0 +1,1444 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 static_cast(rItem1).GetFamilyName() == + static_cast(rItem2).GetFamilyName(); + + case RES_CHRATR_COLOR: + return static_cast(rItem1).GetValue().IsRGBEqual( + static_cast(rItem2).GetValue() ); + case RES_PAGEDESC: + ::std::optional const oNumOffset1 = + static_cast(rItem1).GetNumOffset(); + ::std::optional const oNumOffset2 = + static_cast(rItem2).GetNumOffset(); + + if (oNumOffset1 != oNumOffset2) + return false; + + return static_cast(rItem1).GetPageDesc() == static_cast(rItem2).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 if found, 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{} ) + , 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(pFndChar); + m_pStackArr = reinterpret_cast(pStackChar); +} + +SwAttrCheckArr::~SwAttrCheckArr() +{ + delete[] reinterpret_cast(m_pFindArr); + delete[] reinterpret_cast(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 + { + if( RES_TXTATR_END <= (nWhich = pItem->Which() )) + 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::unique_ptr pIter; + 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 ) + { + pIter.reset(new SfxWhichIter( *pSet )); + nWhch = pIter->FirstWhich(); + while( nWhch && + SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) ) + nWhch = pIter->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( pIter ) + { + assert(pSet && "otherwise no pIter"); + nWhch = pIter->NextWhich(); + while( nWhch && + SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) ) + nWhch = pIter->NextWhich(); + if( !nWhch ) + break; + } + else + break; + } + pIter.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::unique_ptr pIter; + 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 ) + { + pIter.reset( new SfxWhichIter( *pSet ) ); + nWhch = pIter->FirstWhich(); + while( nWhch && + SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) ) + nWhch = pIter->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( pIter ) + { + assert(pSet && "otherwise no pIter"); + nWhch = pIter->NextWhich(); + while( nWhch && + SfxItemState::SET != pSet->GetItemState( nWhch, true, &pTmpItem ) ) + nWhch = pIter->NextWhich(); + if( !nWhch ) + break; + } + else + break; + } + pIter.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 + if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) + 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 + if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) + 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 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(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 pPam(sw::MakeRegion(fnMove, rRegion)); + + bool bFound = false; + bool bFirst = true; + const bool bSrchForward = &fnMove == &fnMoveForward; + SwContentNode * pNode; + o3tl::sorted_vector aFormatArr; + + // check which text/char attributes are searched + SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls ); + SfxItemSet aOtherSet( rSearchPam.GetDoc()->GetAttrPool(), + svl::Items{} ); + 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(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 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& xSearchItem) override; + virtual bool IsReplaceMode() const override; +}; + +} + +int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove, + const SwPaM & rRegion, bool bInReadOnly, + std::unique_ptr& 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(rRegion).GetPrev(); + const_cast(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() ); + } + + std::optional 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(&rRegion); + do { + p = pNext; + pNext = p->GetNext(); + p->MoveTo(const_cast(&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* pDoc = GetDoc(); + Link aLnk( pDoc->GetOle2Link() ); + pDoc->SetOle2Link( Link() ); + + bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() || + !rSet.Count() ) ) || + (pReplSet && pReplSet->Count()); + bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace; + if (bStartUndo) + { + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr ); + } + + SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt, + pReplSet, *this, pLayout ); + + sal_uLong nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel ); + pDoc->SetOle2Link( aLnk ); + if( nRet && bReplace ) + pDoc->getIDocumentState().SetModified(); + + if (bStartUndo) + { + pDoc->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..8385e0a4c --- /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 +#include +#include +#include +#include +#include +#include +#include + +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& xSearchItem) override; + virtual bool IsReplaceMode() const override; +}; + +} + +int SwFindParaFormatColl::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove, + const SwPaM & rRegion, bool bInReadOnly, + std::unique_ptr& /*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(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* pDoc = GetDoc(); + Link aLnk( pDoc->GetOle2Link() ); + pDoc->SetOle2Link( Link() ); + + bool const bStartUndo = + pDoc->GetIDocumentUndoRedo().DoesUndo() && pReplFormatColl; + if (bStartUndo) + { + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, rFormatColl.GetName()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, pReplFormatColl->GetName()); + + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE_STYLE, + &aRewriter ); + } + + SwFindParaFormatColl aSwFindParaFormatColl(rFormatColl, pReplFormatColl, pLayout); + + sal_uLong nRet = FindAll( aSwFindParaFormatColl, nStart, nEnd, eFndRngs, bCancel ); + pDoc->SetOle2Link( aLnk ); + + if( nRet && pReplFormatColl ) + pDoc->getIDocumentState().SetModified(); + + if (bStartUndo) + { + pDoc->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 +#include +#include +#include +#include + +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 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(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..1e9ecef2c --- /dev/null +++ b/sw/source/core/crsr/findtxt.cxx @@ -0,0 +1,1183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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) + { +#if BOOST_VERSION < 105600 + m_oMergedIter.reset(*pFrame); +#else + m_oMergedIter.emplace(*pFrame); +#endif + } + } + + 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 &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 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_FTN: + 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(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& xSearchItem) +{ + if( rSearchOpt.searchString.isEmpty() ) + return false; + + std::unique_ptr 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(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> 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(aLoop) == postits.size() + ? nEnd + : postits[aLoop].second; + nTextLen = nEndInside - nStartInside; + } + else + { + nStartInside = static_cast(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(aLoop) != postits.size()) + : (aLoop != 0)) + { + const SwTextAttr *const pTextAttr = bSrchForward + ? postits[aLoop].first + : postits[aLoop - 1].first; + if (pPostItMgr && pPostItMgr->SearchReplace( + static_txtattr_cast(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 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 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()) && + 1 == std::abs(static_cast(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& 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& 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(rRegion).GetPrev(); + const_cast(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() ); + } + + std::optional 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(&rRegion)); + do { + p = pNext; + pNext = p->GetNext(); + p->MoveTo(const_cast(&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* pDoc = GetDoc(); + Link aLnk( pDoc->GetOle2Link() ); + pDoc->SetOle2Link( Link() ); + + bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace; + if (bStartUndo) + { + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr ); + } + + bool bSearchSel = 0 != (rSearchOpt.searchFlag & SearchFlags::REG_NOT_BEGINOFLINE); + if( bSearchSel ) + eFndRngs = static_cast(eFndRngs | FindRanges::InSel); + SwFindParaText aSwFindParaText(rSearchOpt, bSearchInNotes, bReplace, *this, pLayout); + sal_uLong nRet = FindAll( aSwFindParaText, nStart, nEnd, eFndRngs, bCancel ); + pDoc->SetOle2Link( aLnk ); + if( nRet && bReplace ) + pDoc->getIDocumentState().SetModified(); + + if (bStartUndo) + { + SwRewriter rewriter(MakeUndoReplaceRewriter( + nRet, rSearchOpt.searchString, rSearchOpt.replaceString)); + pDoc->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> 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 ReplaceBackReferences(const i18nutil::SearchOptions2& rSearchOpt, + SwPaM *const pPam, SwRootFrame const*const pLayout) +{ + std::optional 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(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.realloc(1); + aResult.endOffset.realloc(1); + aResult.startOffset[0] = 0; + aResult.endOffset[0] = aStr.getLength(); + aSText.ReplaceBackReferences( aReplaceStr, aStr, aResult ); + xRet = aReplaceStr; + } + else + { + OUString const aStr(pLayout + ? pFrame->GetText() + : pTextNode->GetTextNode()->GetText()); + 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()); + } + 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..1ec83370e --- /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 +#include +#include +#include +#include +#include + +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, + const std::vector< basegfx::B2DRange >& rRanges ) + : sdr::overlay::OverlayObject(rColor) + , maRanges(rRanges) + { + // no AA for highlight overlays + allowAntiAliase(false); + } + + OverlayRangesOutline::~OverlayRangesOutline() + { + if( getOverlayManager() ) + { + getOverlayManager()->remove(*this); + } + } + + void OverlayRangesOutline::setRanges(const std::vector< basegfx::B2DRange >& rNew) + { + if(rNew != maRanges) + { + maRanges = 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..3621f7fe6 --- /dev/null +++ b/sw/source/core/crsr/overlayrangesoutline.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_CRSR_OVERLAYRANGESOUTLINE_HXX +#define INCLUDED_SW_SOURCE_CORE_CRSR_OVERLAYRANGESOUTLINE_HXX + +#include +#include + +#include + +namespace sw +{ + namespace 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, + const 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(const std::vector< basegfx::B2DRange >& rNew); + }; + } // end of namespace overlay +} // end of namespace sw + +#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..d1fccd357 --- /dev/null +++ b/sw/source/core/crsr/pam.cxx @@ -0,0 +1,1171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(nNode.GetIndex()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(nContent.GetIndex()).getStr())); + 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( sal_uLong nSttIdx, sal_uLong nEndIdx, const SwNode& rEndNd ) +{ + sal_uLong 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, sal_uLong nStt, sal_uLong 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; + } + + sal_uLong 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 if valid range + */ +bool CheckNodesRange( const SwNodeIndex& rStt, + const SwNodeIndex& rEnd, bool bChkSection ) +{ + const SwNodes& rNds = rStt.GetNodes(); + sal_uLong 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(pNd)->GoNext( pIdx, nMode ); + return false; +} + +bool GoPrevious( SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode ) +{ + if( pNd->IsContentNode() ) + return static_cast(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 && 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 && 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, + long nMarkOffset, long 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, + long nMarkOffset, long 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 MakeRegion(SwMoveFnCollection const & fnMove, + const SwPaM & rOrigRg) +{ + std::unique_ptr pPam; + { + pPam.reset(new SwPaM(rOrigRg, const_cast(&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 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 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 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 ) + { + sal_uLong nSttIdx = GetMark()->nNode.GetIndex(), + nEndIdx = GetPoint()->nNode.GetIndex(); + if( nEndIdx <= nSttIdx ) + { + sal_uLong 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 + 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?" ); + sal_uLong nIdx = rContent.GetContentIdx()->GetIndex(); + if( nSttIdx <= nIdx && nEndIdx >= nIdx && + rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes() ) + { + bRet = true; + break; + } + } + } + } + } + } + + const SwDoc *pDoc = GetDoc(); + // Legacy text/combo/checkbox: never return read-only when inside these form fields. + const IDocumentMarkAccess* pMarksAccess = pDoc->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 (!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 && pDoc->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) + { + // Paragraph Signatures and Classification fields are read-only. + if (pDoc && pDoc->GetEditShell()) + bRet = pDoc->GetEditShell()->IsCursorInParagraphMetadataField(); + } + + if (!bRet && + pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)) + { + if (pDoc->getIDocumentMarkAccess()->isBookmarkDeleted(*this)) + { + return true; + } + } + if (!bRet && + pDoc->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; + } + } + } + } + } + } + + 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 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(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(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(std::u16string_view(aTmpStr).substr(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->CallSwClientNotify(sw::LegacyModifyHint(&aHint, &aHint)); + } + // other node types not invalidated + } +} + +void SwPaM::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwPaM")); + + xmlTextWriterStartElement(pWriter, BAD_CAST("point")); + GetPoint()->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + if (HasMark()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("mark")); + GetMark()->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + + 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..bbc975acf --- /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 +#include +#include + +static const SwMoveFnCollection aFwrd = { + /* fnNd */ &GoNext, + /* fnNds */ &GoNextNds, + /* fnDoc */ &GoEndDoc, + /* fnSections */ &GoEndSection, + /* fnCmpOp */ &SwPosition::operator<, + /* fnGetHint */ &GetFrwrdTextHint, + /* fnSearch */ &utl::TextSearch::SearchForward, + /* fnSection */ &SwNodes::GoStartOfSection +}; + +static 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..e1237d8cf --- /dev/null +++ b/sw/source/core/crsr/swcrsr.cxx @@ -0,0 +1,2569 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::i18n; + +static 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) + { + if( ( bBack = (nStt > nEnd )) ) + { + sal_uLong n = nStt; nStt = nEnd; nEnd = n; + } + ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd ); + } + + explicit PercentHdl( const SwPaM& rPam ) + : pDSh( rPam.GetDoc()->GetDocShell() ) + { + sal_uLong 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 = rPam.GetMark()->nNode.GetIndex(); + nEnd = rPam.GetPoint()->nNode.GetIndex(); + } + nActPos = nStt; + if( ( bBack = (nStt > nEnd )) ) + { + 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_uLong nPos; + if( bNodeIdx ) + nPos = 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* pDoc = GetDoc(); + SwNodes& rNds = pDoc->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 + ( !pDoc->GetDocShell() || !pDoc->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; + if( nullptr == ( pCNd = rPtIdx.GetNode().GetContentNode() ) ) + { + bIsValidPos = false; + nContentPos = 0; + rPtIdx = aIdx; + if( nullptr == ( pCNd = rPtIdx.GetNode().GetContentNode() ) ) + { + // 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) + { + sal_uLong nSttIdx = GetMark()->nNode.GetIndex(), + nEndIdx = GetPoint()->nNode.GetIndex(); + if( nEndIdx <= nSttIdx ) + { + sal_uLong nTmp = nSttIdx; + nSttIdx = nEndIdx; + nEndIdx = nTmp; + } + + const SwSectionFormats& rFormats = pDoc->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?" ); + sal_uLong 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(this) ) + { + const SwContentFrame* pFrame = static_cast(pNd)->getLayoutFrame( pDoc->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(pNd)->getLayoutFrame( pDoc->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(pFrame)); + *GetPoint() = pTextFrame->MapViewToModelPos(TextFrameIndex( + bGoNxt ? 0 : pTextFrame->GetText().getLength())); + } + else + { + assert(pFrame->IsNoTextFrame()); + SwContentNode *const pCNd = const_cast( + static_cast(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 to NULL - see below + pFrame = nullptr; + } + + if ( IsInProtectTable( true ) ) + { + // new position in protected table + // --> trigger restore of saved pos by setting 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(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) + && !dynamic_cast(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 sal_uLong 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 + sal_uLong nSEIdx = pPtNd->EndOfSectionIndex(); + sal_uLong 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; + + if( nullptr != ( pPtNd = pMyNd->FindTableNode() )) + 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(static_cast(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; + if( nullptr == ( pCNd = aCellStt.GetNode().GetContentNode() )) + 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 ); + if( nullptr == ( pCNd = aCellStt.GetNode().GetContentNode() )) + 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 if cursor can be set to this position +bool SwCursor::IsAtValidPos( bool bPoint ) const +{ + const SwDoc* pDoc = GetDoc(); + const SwPosition* pPos = bPoint ? GetPoint() : GetMark(); + const SwNode* pNd = &pPos->nNode.GetNode(); + + if( pNd->IsContentNode() && !static_cast(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) && + !dynamic_cast(this) ) + { + return false; + } + + // #i45129# - in UI-ReadOnly everything is allowed + if( !pDoc->GetDocShell() || !pDoc->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* pDoc = pCurrentCursor->GetDoc(); + bool const bDoesUndo = pDoc->GetIDocumentUndoRedo().DoesUndo(); + int nFndRet = 0; + sal_uLong nFound = 0; + const bool bSrchBkwrd = &fnMove == &fnMoveBackward; + SwPaM *pTmpCursor = pCurrentCursor, *pSaveCursor = pCurrentCursor; + std::unique_ptr xSearchItem; + + // only create progress bar for ShellCursor + bool bIsUnoCursor = dynamic_cast(pCurrentCursor) != nullptr; + std::unique_ptr pPHdl; + sal_uInt16 nCursorCnt = 0; + if( FindRanges::InSel & eFndRngs ) + { + while( pCurrentCursor != ( pTmpCursor = pTmpCursor->GetNext() )) + ++nCursorCnt; + if( nCursorCnt && !bIsUnoCursor ) + pPHdl.reset(new PercentHdl( 0, nCursorCnt, pDoc->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) + && pDoc->GetIDocumentUndoRedo().DoesUndo() + && rParas.IsReplaceMode()) + { + short nRet = pCurrentCursor->MaxReplaceArived(); + if( RET_YES == nRet ) + { + pDoc->GetIDocumentUndoRedo().DelAllUndoObj(); + pDoc->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(); + + pDoc->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 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) + if( 0 == ( nFound = lcl_FindSelection( rParas, this, fnMove, + pFndRing, aRegion, eFndRngs, + bInReadOnly, bCancel ) )) + 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[ sal_uLong(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->IsHideRedlines()) + { + m_pFrame = static_cast(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 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->IsHideRedlines()) || !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, OUString(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; +} + +bool SwCursor::ExpandToSentenceBorders(SwRootFrame const*const pLayout) +{ + bool bRes = false; + SwTextNode* pStartNd = Start()->nNode.GetNode().GetTextNode(); + SwTextNode* pEndNd = End()->nNode.GetNode().GetTextNode(); + if (pStartNd && pEndNd) + { + 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 + bool bChanged = false; + if (nStartPos <= pStartNd->GetText().getLength() && nStartPos >= 0) + { + *GetMark() = SwPosition(*pStartNd, nStartPos); + bChanged = true; + } + if (nEndPos <= pEndNd->GetText().getLength() && nEndPos >= 0) + { + *GetPoint() = SwPosition(*pEndNd, nEndPos); + bChanged = true; + } + if (bChanged && !IsSelOvr()) + bRes = true; + } + return bRes; +} + +bool SwTableCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 /*nMode*/, + bool /*bVisualAllowed*/, bool /*bSkipHidden*/, bool /*bInsertCursor*/, + SwRootFrame const*) +{ + 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 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( + static_cast(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) +{ + // 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(rNode.GetContentNode()->getLayoutFrame(pLayout)); + if (pFrame) + { + while (pFrame->GetPrecede()) + { + pFrame = static_cast(pFrame->GetPrecede()); + } + } + } + + while( nCnt ) + { + SwNodeIndex aOldNodeIdx( GetPoint()->nNode ); + + TextFrameIndex beforeIndex(-1); + if (pFrame) + { + beforeIndex = pFrame->MapModelToViewPos(*GetPoint()); + } + + if ( !Move( fnMove, fnGo ) ) + break; + + if (pFrame) + { + SwTextFrame const* pNewFrame(static_cast( + GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(pLayout))); + if (pNewFrame) + { + while (pNewFrame->GetPrecede()) + { + pNewFrame = static_cast(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 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(), + static_cast(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 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() ) + { + SwTextFrame const* pFrame; + const SwScriptInfo* pSI = + SwScriptInfo::GetScriptInfo( *rNode.GetTextNode(), &pFrame ); + if ( pSI ) + { + SwIndex& rIdx = GetPoint()->nContent; + const sal_Int32 nPos = rIdx.GetIndex(); + + if (nPos && nPos < rNode.GetTextNode()->GetText().getLength()) + { + 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, long nUpDownX, + SwRootFrame & rLayout) +{ + SwTableCursor* pTableCursor = dynamic_cast(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 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 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 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 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 + { + // Jump to beginning or end of line when the cursor at first or last line. + SwNode& rNode = GetPoint()->nNode.GetNode(); + const sal_Int32 nOffset = bUp ? 0 : rNode.GetTextNode()->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 ) + { + GetDoc()->GetEditShell()->UpdateCursor(); + bRet = false; + } + else{ + *GetPoint() = aPos; // just give a new position + bRet = true; + } + + } + + DoSetBidiLevelUpDown(); // calculate cursor bidi level + } + return bRet; +} + +bool SwCursor::LeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI) +{ + Point aPt; + std::pair 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 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(pFrame)->MapModelToViewPos(*aPam.GetPoint()) + == static_cast(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(), + static_cast(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() + + (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). + sal_uLong 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) + { + 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 = 0; + m_nTableMkNd = 0; + m_nTablePtCnt = 0; + m_nTableMkCnt = 0; +} + +SwTableCursor::~SwTableCursor() {} + +static bool +lcl_SeekEntry(const SwSelBoxes& rTmp, SwStartNode const*const pSrch, + size_t & o_rFndPos) +{ + sal_uLong 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(static_cast(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(static_cast(pNd)), pNd ? static_cast(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(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(pNd), pNd ? static_cast(pNd)->Len() : 0); + } + } + return pCurrentCursor; +} + +void SwTableCursor::InsertBox( const SwTableBox& rTableBox ) +{ + SwTableBox* pBox = const_cast(&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..8e1de5ba8 --- /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 +#include +#include +#include +#include +#include "callnk.hxx" + +SwLayoutFrame* GetCurrColumn( const SwLayoutFrame* pLayFrame ) +{ + while( pLayFrame && !pLayFrame->IsColumnFrame() ) + pLayFrame = pLayFrame->GetUpper(); + return const_cast(pLayFrame); +} + +SwLayoutFrame* GetNextColumn( const SwLayoutFrame* pLayFrame ) +{ + SwLayoutFrame* pActCol = GetCurrColumn( pLayFrame ); + return pActCol ? static_cast(pActCol->GetNext()) : nullptr; +} + +SwLayoutFrame* GetPrevColumn( const SwLayoutFrame* pLayFrame ) +{ + SwLayoutFrame* pActCol = GetCurrColumn( pLayFrame ); + return pActCol ? static_cast(pActCol->GetPrev()) : nullptr; +} + +SwContentFrame* GetColumnStt( const SwLayoutFrame* pColFrame ) +{ + return pColFrame ? const_cast(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 ) + { + SwContentFrame* pCnt = (*fnPosCol)( pLayFrame ); + if( pCnt ) + { + SET_CURR_SHELL( 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..e0eb9ca48 --- /dev/null +++ b/sw/source/core/crsr/trvlfnfl.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "callnk.hxx" +#include + +bool SwCursorShell::CallCursorShellFN( FNCursorShell fnCursor ) +{ + 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 ) +{ + 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(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 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 + (pFrame)->ContainsContent(); + if( pCnt ) + { + SwTextFrame const*const pTF( + static_cast(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(pTextFootnote->GetTextNode()); + GetPoint()->nNode = rTNd; + GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() ); + + return !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | + SwCursorSelOverFlags::Toggle ); + } + } + } + return false; +} + +bool SwCursorShell::GotoFootnoteAnchor() +{ + // 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, sal_uLong nNd, sal_Int32 nCnt ) +{ + const sal_uLong nTNd = rFootnote.GetTextNode().GetIndex(); + return nTNd < nNd || ( nTNd == nNd && rFootnote.GetStart() <= nCnt ); +} + +static bool CmpL( const SwTextFootnote& rFootnote, sal_uLong nNd, sal_Int32 nCnt ) +{ + const sal_uLong 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() ) + { + sal_uLong 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(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 + sal_uLong 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(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() +{ + SET_CURR_SHELL( 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().IsInside( 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..1648caf8f --- /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 +#include +#include +#include +#include +#include +#include "callnk.hxx" +#include +#include +#include + +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; + sal_uLong 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; + sal_uLong 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 = 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(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( const OUString& 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( const OUString& 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..cdee8e9c5 --- /dev/null +++ b/sw/source/core/crsr/trvltbl.cxx @@ -0,0 +1,927 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "callnk.hxx" +#include +#include +#include +#include +#include +#include + +/// 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->getRowSpan() > 1 ) + { + if ( !pTableNd ) + pTableNd = IsCursorInTable(); + assert (pTableNd); + pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(), + static_cast(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(this)->StartAllAction(); + bRet = mxDoc->InsertRow( SwTable::SelLineFromBox( pTableBox, aBoxes, false )); + static_cast(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(); + + SET_CURR_SHELL( 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(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 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(pStartFrame), + static_cast(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(); + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 if no suitable cell could be found, otherwise points + to content in a suitable cell and 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; + sal_uLong 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; + sal_uLong 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 = 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(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; + sal_uLong 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(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(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 if( nullptr != ( pSttNd = pPos->nNode.GetNode(). + FindSttNodeByType( SwTableBoxStartNode )) ) + { + pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() ); + } + + // box has more than one paragraph + if( pChkBox && pSttNd->GetIndex() + 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( dynamic_cast(&rSh) != nullptr ) + bRet |= static_cast(&rSh)->CheckTableBoxContent( + static_cast(&rSh)->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..6f3a1e3b6 --- /dev/null +++ b/sw/source/core/crsr/viscrs.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "overlayrangesoutline.hxx" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// 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. + +long SwSelPaintRects::s_nPixPtX = 0; +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().IsOver( 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(aStream.str().c_str()).trim(); +} + +} + +void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell) +{ + SwRect aRect; + long nTmpY = m_pCursorShell->m_aCursorHeight.getY(); + if( 0 > nTmpY ) + { + nTmpY = -nTmpY; + m_aTextCursor.SetOrientation( 900 ); + 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(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 ) + { + 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(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(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(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()); + } + + // 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(); + + // is cursor at a misspelled word ? + bool bIsWrong = false; + 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 = nullptr; + 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(m_pCursorShell)->GetContentAtPos(aRect.Pos(), aContentAtPos)) + { + const SwFormatINetFormat* pItem = static_cast(aContentAtPos.aFnd.pAttr); + sHyperlink = buildHyperlinkJSON(aContentAtPos.sStr, pItem->GetValue()); + } + else if (bIsSelection) + { + SwWrtShell* pShell = m_pCursorShell->GetDoc()->GetDocShell()->GetWrtShell(); + + if (pShell) + { + SfxItemSet aSet(m_pCursorShell->GetSfxViewShell()->GetPool(), + svl::Items{}); + pShell->GetCurAttr(aSet); + if(SfxItemState::SET <= aSet.GetItemState( RES_TXTATR_INETFMT )) + { + sHyperlink = buildHyperlinkJSON(m_pCursorShell->GetSelText(), + aSet.GetItem(RES_TXTATR_INETFMT)->GetValue()); + } + } + } + + if (pViewShell) + { + if (pViewShell == m_pCursorShell->GetSfxViewShell()) + { + SfxLokHelper::notifyVisCursorInvalidation(pViewShell, sRect, bIsWrong, sHyperlink); + } + else + SfxLokHelper::notifyOtherView(m_pCursorShell->GetSfxViewShell(), pViewShell, LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect); + } + else + { + SfxLokHelper::notifyVisCursorInvalidation(m_pCursorShell->GetSfxViewShell(), sRect, bIsWrong, sHyperlink); + SfxLokHelper::notifyOtherViews(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect); + } + } + + if ( !m_pCursorShell->IsCursorReadonly() || m_pCursorShell->GetViewOptions()->IsSelectionInReadonly() ) + { + if ( m_pCursorShell->GetDrawView() ) + const_cast(static_cast(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(); + } +} + +const vcl::Cursor& SwVisibleCursor::GetTextCursor() const +{ + return m_aTextCursor; +} + +SwSelPaintRects::SwSelPaintRects( const SwCursorShell& rCSh ) + : SwRects() + , m_pCursorShell( &rCSh ) +#if HAVE_FEATURE_DESKTOP + , m_bShowTextInputFieldOverlay(true) +#endif +{ +} + +SwSelPaintRects::~SwSelPaintRects() +{ + Hide(); +} + +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); +#endif +} + +void SwSelPaintRects::Hide() +{ +#if HAVE_FEATURE_DESKTOP + m_pCursorOverlay.reset(); + m_pTextInputFieldOverlay.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 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* pSelectionRectangles) +{ + SdrView *const pView = const_cast(m_pCursorShell->GetDrawView()); + + if(pView && pView->PaintWindowCount()) + { + // 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(m_pCursorOverlay.get())->setRanges(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 SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + const Color aHighlight(aSvtOptionsDrawinglayer.getHilightColor()); + + // create correct selection + m_pCursorOverlay.reset( new sdr::overlay::OverlaySelection( + sdr::overlay::OverlayType::Transparent, + aHighlight, + aNewRanges, + true) ); + + xTargetOverlay->add(*m_pCursorOverlay); + } + } + + HighlightInputField(); +#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()) + { + // If pSelectionRectangles is set, we're just collecting the text selections -> don't emit start/end. + if (!empty() && !pSelectionRectangles) + { + // 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); + + if (aStartRect.HasArea()) + { + OString sRect = aStartRect.SVRect().toString(); + GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, sRect.getStr()); + } + if (aEndRect.HasArea()) + { + OString sRect = aEndRect.SVRect().toString(); + GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, sRect.getStr()); + } + } + + std::vector 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) + { + GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRect.getStr()); + SfxLokHelper::notifyOtherViews(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", sRect); + } + else + pSelectionRectangles->push_back(sRect); + } + } +} + +void SwSelPaintRects::HighlightInputField() +{ + std::vector< basegfx::B2DRange > aInputFieldRanges; + + if (m_bShowTextInputFieldOverlay) + { + SwTextInputField* pCurTextInputFieldAtCursor = + dynamic_cast(SwCursorShell::GetTextFieldAtPos( GetShell()->GetCursor()->Start(), false )); + if ( pCurTextInputFieldAtCursor != nullptr ) + { + SwTextNode* pTextNode = pCurTextInputFieldAtCursor->GetpTextNode(); + std::unique_ptr 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 = static_cast(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( aInputFieldRanges ); + } + else + { + SdrView* pView = const_cast(GetShell()->GetDrawView()); + SdrPaintWindow* pCandidate = pView->GetPaintWindow(0); + const rtl::Reference& xTargetOverlay = pCandidate->GetOverlayManager(); + + if (xTargetOverlay.is()) + { + // use system's highlight color with decreased luminance as highlight color + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + Color aHighlight(aSvtOptionsDrawinglayer.getHilightColor()); + aHighlight.DecreaseLuminance( 128 ); + + m_pTextInputFieldOverlay.reset( new sw::overlay::OverlayRangesOutline( + aHighlight, aInputFieldRanges ) ); + xTargetOverlay->add( *m_pTextInputFieldOverlay ); + } + } + } + else + { + m_pTextInputFieldOverlay.reset(); + } +} + +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()) ) + { + 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, + long* pX, long* pY ) +{ + const OutputDevice* pOut = rSh.GetWin(); + 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 aSelectionRectangles; + for(SwPaM& rPaM : GetRingContainer()) + { + SwShellCursor* pShCursor = dynamic_cast(&rPaM); + if(pShCursor) + pShCursor->SwSelPaintRects::Show(&aSelectionRectangles); + } + + if (comphelper::LibreOfficeKit::isActive()) + { + std::vector 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(&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(&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 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 xBuilder(Application::CreateBuilder(pDlg->getDialog(), "modules/swriter/ui/asksearchdialog.ui")); + std::unique_ptr 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(GetShell())->SaveTableBoxContent( pPos ); +} + +bool SwShellCursor::UpDown( bool bUp, sal_uInt16 nCnt ) +{ + return SwCursor::UpDown( bUp, nCnt, + &GetPtPos(), GetShell()->GetUpDownX(), + *GetShell()->GetLayout()); +} + +// if 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( GetShell()->VisArea() ); + if (comphelper::LibreOfficeKit::isActive()) + aReg = GetShell()->getIDocumentLayoutAccess().GetCurrentLayout()->getFrameArea(); + 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 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().IsOver( 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::IsInside( 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 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().IsInside( rPt ) ) + return true; + + for ( SwCellFrame* pCellFrame = static_cast(pFrame); pCellFrame; pCellFrame = pCellFrame->GetFollowCell() ) + { + if( pCellFrame->getFrameArea().IsInside( 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..1fe119f2e --- /dev/null +++ b/sw/source/core/doc/CntntIdxStore.cxx @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + + static const int BEFORE_NODE = 0; // Position before the given node index + static const int BEFORE_SAME_NODE = 1; // Same node index but content index before given content index + static const int SAME_POSITION = 2; // Same node index and samecontent index + static const int BEHIND_SAME_NODE = 3; // Same node index but content index behind given content index + static const int BEHIND_NODE = 4; // Position behind the given node index + + int lcl_RelativePosition( const SwPosition& rPos, sal_uLong nNode, sal_Int32 nContent ) + { + sal_uLong 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 + { + long int m_nIdx; + bool m_bOther; + sal_Int32 m_nContent; +#if 0 +#include + 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(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(m_pNewContentNode), std::min( nContent, static_cast(m_nLen) ) ); + } + else + { + rPos.nContent -= m_nCorrLen; + } + }; + }; + struct ContentIdxStoreImpl : sw::mark::ContentIdxStore + { + std::vector m_aBkmkEntries; + std::vector m_aRedlineEntries; + std::vector m_aFlyEntries; + std::vector m_aUnoCursorEntries; + std::vector m_aShellCursorEntries; + typedef std::function 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* pDoc, sal_uLong nNode, sal_Int32 nContent, bool bSaveFlySplit=false) override + { + SaveBkmks(pDoc, nNode, nContent); + SaveRedlines(pDoc, nNode, nContent); + SaveFlys(pDoc, nNode, nContent, bSaveFlySplit); + SaveUnoCursors(pDoc, nNode, nContent); + SaveShellCursors(pDoc, nNode, nContent); + } + virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = false, RestoreMode eMode = RestoreMode::All) override + { + SwContentNode* pCNd = pDoc->GetNodes()[ nNode ]->GetContentNode(); + updater_t aUpdater = OffsetUpdater(pCNd, nOffset); + if (eMode & RestoreMode::NonFlys) + { + RestoreBkmks(pDoc, aUpdater); + RestoreRedlines(pDoc, aUpdater); + RestoreUnoCursors(aUpdater); + RestoreShellCursors(aUpdater); + } + if (eMode & RestoreMode::Flys) + { + RestoreFlys(pDoc, aUpdater, bAuto); + } + } + virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode eMode = RestoreMode::All) override + { + SwContentNode* pCNd = rNd.GetContentNode(); + SwDoc* pDoc = rNd.GetDoc(); + updater_t aUpdater = LimitUpdater(pCNd, nLen, nCorrLen); + if (eMode & RestoreMode::NonFlys) + { + RestoreBkmks(pDoc, aUpdater); + RestoreRedlines(pDoc, aUpdater); + RestoreUnoCursors(aUpdater); + RestoreShellCursors(aUpdater); + } + if (eMode & RestoreMode::Flys) + { + RestoreFlys(pDoc, aUpdater, false); + } + } + + private: + void SaveBkmks(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent); + void RestoreBkmks(SwDoc* pDoc, updater_t const & rUpdater); + void SaveRedlines(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent); + void RestoreRedlines(SwDoc* pDoc, updater_t const & rUpdater); + void SaveFlys(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent, bool bSaveFlySplit); + void RestoreFlys(SwDoc* pDoc, updater_t const & rUpdater, bool bAuto); + void SaveUnoCursors(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent); + void RestoreUnoCursors(updater_t const & rUpdater); + void SaveShellCursors(SwDoc* pDoc, sal_uLong 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& rPaMEntries, const sal_uLong 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& rPaMEntries, const sal_uLong 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& rPaMEntries, const sal_uLong 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* pEntries) + { + for (MarkEntry& aEntry : *pEntries) + aEntry.Dump(); + } +#endif +} + +void ContentIdxStoreImpl::SaveBkmks(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent) +{ + IDocumentMarkAccess* const pMarkAccess = pDoc->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(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(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().nContent.GetIndex() }; + m_aBkmkEntries.push_back(aEntry); + } + const MarkEntry aEntry = { static_cast(ppBkmk - pMarkAccess->getAllMarksBegin()), true, pBkmk->GetOtherMarkPos().nContent.GetIndex() }; + m_aBkmkEntries.push_back(aEntry); + } + } +} + +void ContentIdxStoreImpl::RestoreBkmks(SwDoc* pDoc, updater_t const & rUpdater) +{ + IDocumentMarkAccess* const pMarkAccess = pDoc->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* pDoc, sal_uLong nNode, sal_Int32 nContent) +{ + SwRedlineTable const & rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + long int 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* pDoc, updater_t const & rUpdater) +{ + const SwRedlineTable& rRedlTable = pDoc->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* pDoc, sal_uLong nNode, sal_Int32 nContent, bool bSaveFlySplit) +{ + SwContentNode *pNode = pDoc->GetNodes()[nNode]->GetContentNode(); + if( !pNode ) + return; + SwFrame* pFrame = pNode->getLayoutFrame( pDoc->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(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 : *pDoc->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* pDoc, updater_t const & rUpdater, bool bAuto) +{ + SwFrameFormats* pSpz = pDoc->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() ) + { + 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 ]; + SfxPoolItem const *pAnchor = &pFrameFormat->GetAnchor(); + pFrameFormat->NotifyClients( pAnchor, pAnchor ); + } + } +} + +void ContentIdxStoreImpl::SaveUnoCursors(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nContent) +{ + pDoc->cleanupUnoCursorTable(); + for (const auto& pWeakUnoCursor : pDoc->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(pUnoCursor.get()); + if( pUnoTableCursor ) + { + for(SwPaM& rPaM : const_cast(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* pDoc, sal_uLong nNode, sal_Int32 nContent) +{ + SwCursorShell* pShell = pDoc->GetEditShell(); + if( !pShell ) + return; + for(SwViewShell& rCurShell : pShell->GetRingContainer()) + { + if( dynamic_cast(&rCurShell) != nullptr ) + { + SwPaM *_pStackCursor = static_cast(&rCurShell)->GetStackCursor(); + if( _pStackCursor ) + for (;;) + { + lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, *_pStackCursor); + if (!_pStackCursor) + break; + _pStackCursor = _pStackCursor->GetNext(); + if (_pStackCursor == static_cast(&rCurShell)->GetStackCursor()) + break; + } + + for(SwPaM& rPaM : static_cast(&rCurShell)->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::Create() + { + return std::make_shared(); + } +} +/* 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..90785725e --- /dev/null +++ b/sw/source/core/doc/DocumentChartDataProviderManager.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace com::sun::star; +using namespace com::sun::star::uno; + +namespace sw { + +DocumentChartDataProviderManager::DocumentChartDataProviderManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), + maChartDataProviderImplRef() +{ + +} + +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) + { + 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..17bbd2b56 --- /dev/null +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -0,0 +1,5138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +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* pDoc, sal_uLong nSttNd, sal_uLong nEndNd, + sal_uLong nInsNd ) + { + const SwFrameFormats& rFrameFormatTable = *pDoc->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( pDoc, pSNd->GetIndex(), + pSNd->EndOfSectionIndex(), nInsNd ) ) + // Do not copy ! + return true; + } + } + + return false; + } + + SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, sal_uLong & rDelCount) + { + SwNodeIndex const& rStart(rSourcePaM.Start()->nNode); + // Special handling for SwDoc::AppendDoc + if (rSourcePaM.GetDoc()->GetNodes().GetEndOfExtras().GetIndex() + 1 + == rStart.GetIndex()) + { + rDelCount = 1; + return SwNodeIndex(rStart, +1); + } + else + { + rDelCount = 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 sal_uLong nNewIdx, sal_uLong& rDelCount ) + { + sal_uLong nStart = rPam.Start()->nNode.GetIndex(); + sal_uLong 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, + sal_uLong nDelCount ) + { + sal_uLong nNdOff = rOrigPos.nNode.GetIndex(); + nNdOff -= rOrigStt.nNode.GetIndex(); + nNdOff -= nDelCount; + sal_Int32 nContentPos = rOrigPos.nContent.GetIndex(); + + // Always adjust at to be changed instance + 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, SwPosition& rCpyPam) + { + const SwDoc* pSrcDoc = rPam.GetDoc(); + SwDoc* pDestDoc = rCpyPam.GetDoc(); + const IDocumentMarkAccess* const pSrcMarkAccess = pSrcDoc->getIDocumentMarkAccess(); + ::sw::UndoGuard const undoGuard(pDestDoc->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. + const bool bIsNotOnBoundary = + pMark->IsExpanded() + ? (rMarkStart != rStt || rMarkEnd != rEnd) // rMarkStart != rMarkEnd + : (rMarkStart != rStt && rMarkEnd != rEnd); // 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... + sal_uLong 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 = pDestDoc->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 + pDestDoc->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()); + } + ::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* pSrcDoc = rPam.GetDoc(); + const SwRedlineTable& rTable = pSrcDoc->getIDocumentRedlineAccess().GetRedlineTable(); + if( !rTable.empty() ) + { + SwDoc* pDestDoc = rCpyPam.GetDoc(); + SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End(); + std::unique_ptr pDelPam; + const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); + // We have to count the "non-copied" nodes + sal_uLong nDelCount; + SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount)); + + SwRedlineTable::size_type n = 0; + pSrcDoc->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->GetNext()->End() == *pDelPam->Start()) + { + *pDelPam->GetNext()->End() = *pDelPam->End(); + pDelPam.reset(pDelPam->GetNext()); + } + } + } + } + } + + if( pDelPam ) + { + RedlineFlags eOld = pDestDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore ); + + ::sw::UndoGuard const undoGuard(pDestDoc->GetIDocumentUndoRedo()); + + do { + pDestDoc->getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() ); + if( !pDelPam->IsMultiSelection() ) + break; + delete pDelPam->GetNext(); + } while( true ); + + pDestDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + } + } + + void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg ) + { + SwDoc* pSrcDoc = rRg.aStart.GetNode().GetDoc(); + if( !pSrcDoc->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> & rBreaks, + SwPaM const & rPam, bool const isOnlyFieldmarks) + { + sal_uLong const nStartNode(rPam.Start()->nNode.GetIndex()); + sal_uLong const nEndNode(rPam.End()->nNode.GetIndex()); + SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes()); + IDocumentMarkAccess const& rIDMA(*rPam.GetDoc()->getIDocumentMarkAccess()); + + std::stack> startedFields; + + for (sal_uLong 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*const pAttr(rTextNode.GetTextAttrForCharAt(i)); + if (pAttr && pAttr->End() && (nEnd < *pAttr->End())) + { + assert(pAttr->HasDummyChar()); + 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 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 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, + bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false) + { + std::vector> Breaks; + + sw::CalcBreaks(Breaks, rPam); + + if (Breaks.empty()) + { + return (rDocumentContentOperations.*pFunc)(rPam, bForceJoinNext); + } + + // 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() ); + sal_uLong 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, bForceJoinNext); + 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, bForceJoinNext); + } + + 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; + sal_uInt32 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->GetMark() == pStt ? pR->GetPoint() : pR->GetMark(); + sal_uInt32 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 = 0; + pRedl->GetPoint()->nContent.Assign( nullptr, 0 ); + pRedl->GetMark()->nNode = 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->GetMark() == pStt ? pR->GetPoint() : pR->GetMark(); + sal_uInt32 nSttIdx = rPos.nNode.GetIndex(); + nStt = pStt->nNode.GetIndex() - nSttIdx; + nSttCnt = pStt->nContent.GetIndex(); + if( nStt == 0 ) + nSttCnt = nSttCnt - rPos.nContent.GetIndex(); + if( pR->HasMark() ) + { + nEnd = pEnd->nNode.GetIndex() - nSttIdx; + nEndCnt = pEnd->nContent.GetIndex(); + if( nEnd == 0 ) + nEndCnt = nEndCnt - rPos.nContent.GetIndex(); + } + + pRedl->GetPoint()->nNode = 0; + pRedl->GetPoint()->nContent.Assign( nullptr, 0 ); + pRedl->GetMark()->nNode = 0; + pRedl->GetMark()->nContent.Assign( nullptr, 0 ); + } + + void SetPos( sal_uInt32 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 == 0 ? aPos.nContent.GetIndex() : 0 ) ); + if( pRedl->HasMark() ) + { + pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd; + pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) ); + } + } + }; + + typedef std::vector< SaveRedline > SaveRedlines_t; + + void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr) + { + SwDoc* pDoc = aPam.GetNode().GetDoc(); + + const SwPosition* pStart = aPam.Start(); + const SwPosition* pEnd = aPam.End(); + + // get first relevant redline + SwRedlineTable::size_type nCurrentRedline; + pDoc->getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline ); + if( nCurrentRedline > 0) + nCurrentRedline--; + + // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->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 = pDoc->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; + pDoc->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; + pDoc->getIDocumentRedlineAccess().AppendRedline( pNewRedline, true ); + } + + // save the current redline + rArr.emplace_back( pCurrent, *pStart ); + } + } + + // restore old redline mode + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + + void lcl_RestoreRedlines(SwDoc* pDoc, const SwPosition& rPos, SaveRedlines_t& rArr) + { + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); + + for(SaveRedline & rSvRedLine : rArr) + { + rSvRedLine.SetPos( rPos ); + pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ); + } + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + + void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr) + { + SwDoc* pDoc = rRg.aStart.GetNode().GetDoc(); + SwRedlineTable::size_type nRedlPos; + SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--; + aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 ); + if( pDoc->getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos ) + --nRedlPos; + else if( nRedlPos >= pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ) + return ; + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); + SwRedlineTable& rRedlTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + + do { + SwRangeRedline* pTmp = rRedlTable[ nRedlPos ]; + + const SwPosition* pRStt = pTmp->Start(), + * pREnd = pTmp->GetMark() == pRStt + ? pTmp->GetPoint() : pTmp->GetMark(); + + 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 ); + pDoc->getIDocumentRedlineAccess().AppendRedline( pTmp, true ); + } + } + else + break; + + } while( ++nRedlPos < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + + void lcl_RestoreRedlines(SwDoc *const pDoc, sal_uInt32 const nInsPos, SaveRedlines_t& rArr) + { + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); + + for(SaveRedline & rSvRedLine : rArr) + { + rSvRedLine.SetPos( nInsPos ); + pDoc->getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ); + if (rSvRedLine.pRedl->GetType() == RedlineType::Delete) + { + UpdateFramesForAddDeleteRedline(*pDoc, *rSvRedLine.pRedl); + } + } + + pDoc->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(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(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(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; + const OUString sPara("\\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->IsHideRedlines() && pTNd) + { + SwTextFrame const*const pTextFrame(static_cast( + 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->IsHideRedlines() + || 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; } + + /// Insert Hints according to content types; + // Is used in SwDoc::Insert(..., SwFormatHint &rHt) + + bool lcl_InsAttr( + SwDoc *const pDoc, + const SwPaM &rRg, + const SfxItemSet& rChgSet, + const SetAttrMode nFlags, + SwUndoAttr *const pUndo, + SwRootFrame const*const pLayout, + const bool bExpandCharToPara, + 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 SfxItemSet( + pDoc->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT, + RES_TXTATR_UNKNOWN_CONTAINER, + RES_TXTATR_UNKNOWN_CONTAINER>{}); + + SfxItemSet* pTmpOtherItemSet = new SfxItemSet( + pDoc->GetAttrPool(), + svl::Items< + RES_PARATR_BEGIN, RES_GRFATR_END - 1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1, + // FillAttribute support: + XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + 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( " - 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(static_cast(nLevel)); + SwCharFormat * pCharFormat = + pDoc->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 ) + { + SfxItemSet aTextSet( pDoc->GetAttrPool(), + svl::Items{} ); + 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 && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() + && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()))) + { + SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1, + pStt->nNode, pStt->nContent.GetIndex() ); + + if( pUndo ) + pUndo->SaveRedlineData( aPam, true ); + + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true); + else + pDoc->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! + SfxItemSet aTextSet( + pDoc->GetAttrPool(), + svl::Items< + RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD, + RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY, + RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD>{}); + + 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 && (pDoc->getIDocumentRedlineAccess().IsRedlineOn() || (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() + && !pDoc->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( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + pDoc->getIDocumentRedlineAccess().AppendRedline( + new SwRangeRedline( + bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ), + true); + else if( bTextIns ) + pDoc->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; + if( SfxItemState::SET == pOtherSet->GetItemState( RES_PAGEDESC, + false, reinterpret_cast(&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->IsHideRedlines()) + { + 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(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() ) && + SfxItemState::SET == pOtherSet->GetItemState( RES_BREAK, + false, reinterpret_cast(&pBreak) ) ) + { + 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(pOtherSet)->ClearItem( RES_BREAK ); + if( !pOtherSet->Count() ) + { + DELETECHARSETS + return bRet; + } + } + + { + // If we have a PoolNumRule, create it if needed + const SwNumRuleItem* pRule; + sal_uInt16 nPoolId=0; + if( SfxItemState::SET == pOtherSet->GetItemState( RES_PARATR_NUMRULE, + false, reinterpret_cast(&pRule) ) && + !pDoc->FindNumRulePtr( pRule->GetValue() ) && + USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(), + SwGetPoolIdFromName::NumRule )) ) + pDoc->getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId ); + } + } + + SfxItemSet firstSet(pDoc->GetAttrPool(), + svl::Items{}); + if (pOtherSet && pOtherSet->Count()) + { // actually only RES_BREAK is possible here... + firstSet.Put(*pOtherSet); + } + SfxItemSet propsSet(pDoc->GetAttrPool(), + svl::Items{}); + 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 ); + } + + // the SwRegHistory inserts the attribute into the TextNode! + SwRegHistory history( pNode, *pNode, pHistory ); + bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr ) + || bRet; + + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos ); + + if( pUndo ) + pUndo->SaveRedlineData( aPam, false ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Format, aPam ), true); + } + } + 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); + + pDoc->CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy); + bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet; + } + + DELETECHARSETS + return bRet; + } + + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() ) + { + if( pUndo ) + pUndo->SaveRedlineData( rRg, false ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Format, rRg ), true); + } + + /* now if range */ + sal_uLong nNodes = 0; + + SwNodeIndex aSt( pDoc->GetNodes() ); + SwNodeIndex aEnd( pDoc->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 ) + { + //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc, + //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that + //current setting attribute set is a character range properties set and comes from a MS Word + //binary file, and the setting range include a paragraph end position (0X0D); + //more specifications, as such property inside the character range properties set recorded in + //MS Word binary file are dealt and inserted into data model (SwDoc) one by one, so we + //only dealing the scenario that the char properties set with 1 item inside; + + if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1 ) + { + SwTextNode* pCurrentNd = pStt->nNode.GetNode().GetTextNode(); + + if (pCurrentNd) + { + pCurrentNd->TryCharSetExpandToNum(*pCharSet); + + } + } + 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); + pDoc->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->IsHideRedlines() + && 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()) + { + SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints() + : pTNd->GetpSwpHints(); + if( pSwpHints ) + pSwpHints->Register( &aRegH ); + + pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags); + if( pSwpHints ) + 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; + } + } + + //The data parameter flag: bExpandCharToPara, comes from the data member of SwDoc, + //which is set in SW MS Word Binary filter WW8ImplRreader. With this flag on, means that + //current setting attribute set is a character range properties set and comes from a MS Word + //binary file, and the setting range include a paragraph end position (0X0D); + //more specifications, as such property inside the character range properties set recorded in + //MS Word binary file are dealt and inserted into data model (SwDoc) one by one, so we + //only dealing the scenario that the char properties set with 1 item inside; + if (bExpandCharToPara && pCharSet && pCharSet->Count() ==1) + { + SwPosition aStartPos (*rRg.Start()); + SwPosition aEndPos (*rRg.End()); + + if (aEndPos.nNode.GetNode().GetTextNode() && aEndPos.nContent != aEndPos.nNode.GetNode().GetTextNode()->Len()) + aEndPos.nNode--; + + sal_uLong nStart = aStartPos.nNode.GetIndex(); + sal_uLong nEnd = aEndPos.nNode.GetIndex(); + for(; nStart <= nEnd; ++nStart) + { + SwNode* pNd = pDoc->GetNodes()[ nStart ]; + if (!pNd || !pNd->IsTextNode()) + continue; + SwTextNode *pCurrentNd = pNd->GetTextNode(); + pCurrentNd->TryCharSetExpandToNum(*pCharSet); + } + } + + DELETECHARSETS + return (nNodes != 0) || bRet; + } +} + +namespace sw +{ + +namespace mark +{ + bool IsFieldmarkOverlap(SwPaM const& rPaM) + { + std::vector> 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* pDoc = rPos.nNode.GetNode().GetDoc(); + bool bColumnSel = pDoc->IsClipBoard() && pDoc->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 (pDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly)) + { + // Correct the Start-/EndNode + sal_uLong 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(pNd)->Len() != pEnd->nContent.GetIndex() ) + { + --nEnd; + --nDiff; + } + if( nDiff && + lcl_ChkFlyFly( pDoc, nStt, nEnd, rPos.nNode.GetIndex() ) ) + { + return false; + } + } + + SwPaM* pRedlineRange = nullptr; + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() || + (!pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) ) + pRedlineRange = new SwPaM( rPos ); + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + + bool bRet = false; + + if( pDoc != &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"); + } + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + if( pRedlineRange ) + { + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlineRange ), true); + else + pDoc->getIDocumentRedlineAccess().SplitRedline( *pRedlineRange ); + delete pRedlineRange; + } + + return bRet; +} + +/// 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(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 + SwNodeIndex aMvStt( aSttIdx, 1 ); + SwDoc::CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), true ); + } + + 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); + + if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() + && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) + { + m_rDoc.getIDocumentRedlineAccess().CompressRedlines(); + } +} + +void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam ) +{ + lcl_DoWithBreaks( *this, rPam, &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(); + sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() - + pNd->StartOfSectionIndex(); + sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex(); + + if ( nSectDiff-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 brakes to the following Node. + bool bSavePageBreak = false, bSavePageDesc = false; + + /* #i9185# This would lead to a segmentation fault if not caught above. */ + sal_uLong 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(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 pUndo(new SwUndoDelete( aDelPam, 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; + } + } + // 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; +} + +// #i100466# Add handling of new optional parameter +bool DocumentContentOperationsManager::DeleteAndJoin( SwPaM & rPam, + const bool bForceJoinNext ) +{ + if ( lcl_StrLenOverflow( rPam ) ) + return false; + + bool const ret = lcl_DoWithBreaks( *this, rPam, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) + ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl + : &DocumentContentOperationsManager::DeleteAndJoinImpl, + bForceJoinNext ); + + 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 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 ore 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 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 restoreFunc( + [&](SwTextNode *const, sw::mark::RestoreMode const eMode) + { + if (!pContentStore->Empty()) + { + pContentStore->Restore(&m_rDoc, pOrigNode->GetIndex()-1, 0, true, 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()-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 has to stay as it is. + // A join previous from its next would more or less delete + 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 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 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 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; +} + +bool 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 ) + { + 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(); + } + } + return bRet; +} + +// 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(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(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(&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 ) + { + SwUpdateAttr aHint(0,0,0); + pNode->ModifyBroadcast(nullptr, &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(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(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(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(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::TransliterateText( + const SwPaM& rPaM, + utl::TransliterationWrapper& rTrans ) +{ + std::unique_ptr pUndo; + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + pUndo.reset(new SwUndoTransliterate( rPaM, rTrans )); + + const SwPosition* pStt = rPaM.Start(), + * pEnd = rPaM.End(); + sal_uLong 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? + { + // set current word as 'area of effect' + + 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 ) + { + nSttCnt = aBndry.startPos; + nEndCnt = aBndry.endPos; + } + } + + if( nSttNd != nEndNd ) // is more than one text node involved? + { + // iterate over all effected 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()); + } + + for( ; aIdx.GetIndex() < nEndNd; ++aIdx ) + { + pTNd = aIdx.GetNode().GetTextNode(); + if (pTNd) + { + pTNd->TransliterateText( + rTrans, 0, pTNd->GetText().getLength(), pUndo.get()); + } + } + + if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() )) + pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get() ); + } + else if( pTNd && nSttCnt < nEndCnt ) + pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get() ); + + 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() ) ) + { + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique(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 = nullptr; + rFlyAttrSet.GetItemState( RES_ANCHOR, false, reinterpret_cast(&pAnchor) ); + 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 ) + { + SwPosition aPos( *rRg.GetNode().FindFlyStartNode() ); + aAnch.SetAnchor( &aPos ); + } + else + { + aAnch.SetAnchor( rRg.GetPoint() ); + if ( eAnchorId == RndStdIds::FLY_AT_PAGE ) + { + eAnchorId = dynamic_cast( &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(pFormat, 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(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() ) + { + sal_uLong nPrevPos = rPos.nNode.GetIndex() - 1; + const SwTableNode* pTableNd; + const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ]; + if( pNd->IsStartNode() && + SwTableBoxStartNode == static_cast(pNd)->GetStartNodeType() && + nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) && + ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() && + SwTableBoxStartNode != static_cast(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(rPos).nNode = pTableNd->GetIndex()-1; + const_cast(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 pContentStore(sw::mark::ContentIdxStore::Create()); + pContentStore->Save( &m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true ); + assert(pNode->IsTextNode()); + std::function restoreFunc( + [&](SwTextNode *const, sw::mark::RestoreMode const eMode) + { + if (!pContentStore->Empty()) + { // move all bookmarks, TOXMarks, FlyAtCnt + pContentStore->Restore(&m_rDoc, rPos.nNode.GetIndex()-1, 0, true, 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( 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> 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() ); + sal_uLong 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) + : DeleteAndJoinImpl(aPam, false); + 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; +} + +///Add a para for the char attribute exp... +bool DocumentContentOperationsManager::InsertPoolItem( + const SwPaM &rRg, + const SfxPoolItem &rHt, + const SetAttrMode nFlags, + SwRootFrame const*const pLayout, + const bool bExpandCharToPara, + SwTextAttr **ppNewTextAttr) +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + + SwDataChanged aTmp( rRg ); + std::unique_ptr 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, bExpandCharToPara, 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 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, /*bExpandCharToPara*/false, /*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 ) + { + 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* 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* pDest = rInsPos.GetNode().GetDoc(); + SwNodeIndex aSavePos( rInsPos ); + + if (rRg.aStart != rRg.aEnd) + { + bool bEndIsEqualEndPos = rInsPos == rRg.aEnd; + bool isRecreateEndNode(false); + --aSavePos; + SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 ); + + // insert behind the already copied start node + m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true ); + aRedlRest.Restore(); + if (bMakeNewFrames) // tdf#130685 only after aRedlRest + { // recreate from previous node (could be merged now) + if (SwTextNode *const pNode = aSavePos.GetNode().GetTextNode()) + { + std::unordered_set frames; + SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode(); + if (pEndNode) + { + SwIterator aIter(*pEndNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + frames.insert(pFrame); + } + } + } + sw::RecreateStartTextFrames(*pNode); + if (!frames.empty()) + { // tdf#132187 check if the end node needs new frames + SwIterator aIter(*pEndNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + 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, + (!isRecreateEndNode || isAtStartOfSection) + ? 0 : +1); + ::MakeFrames(pDest, aSavePos, end); + } + if (bEndIsEqualEndPos) + { + const_cast(rRg.aEnd) = aSavePos; + } + } + +#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 + sal_uInt16 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(pDest->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 ); + + // Also copy all bookmarks + // guess this must be done before the DelDummyNodes below as that + // deletes nodes so would mess up the index arithmetic + if( m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() ) + { + SwPaM aRgTmp( rRg.aStart, rRg.aEnd ); + SwPaM aCpyPaM(aCpyRange.aStart, aCpyRange.aEnd); + 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! + *aCpyPaM.GetPoint() = pCopiedPaM->second; + } + + sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, *aCpyPaM.Start()); + } + + if( bDelRedlines && ( RedlineFlags::DeleteRedlines & pDest->getIDocumentRedlineAccess().GetRedlineFlags() )) + lcl_DeleteRedlines( rRg, aCpyRange ); + + pDest->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 *const pDest = 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; + sal_uLong nSkipAfter = pAPos->nNode.GetIndex(); + sal_uLong 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 . + // 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( " - 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 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( " - 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 + { + long 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( pDest == &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( pDest->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() ) + { + size_t n = 0; + for (const auto& rFlyN : aSet) + { + const SwFrameFormat *pFormatN = rFlyN.GetFormat(); + const SwFormatChain &rChain = pFormatN->GetChain(); + int nCnt = int(nullptr != rChain.GetPrev()); + nCnt += rChain.GetNext() ? 1: 0; + 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]) ); + --nCnt; + } + else if ( rChain.GetNext() == pFormatK ) + { + ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]), + static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) ); + --nCnt; + } + ++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( const SwNodePtr& rpNd, void* pArgs ) +{ + ParaRstFormat* pPara = static_cast(pArgs); + if (pPara->pLayout && pPara->pLayout->IsHideRedlines() + && rpNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden) + { + return true; // skip hidden, since new items aren't applied + } + SwTextNode * pTextNode = rpNd->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() == rpNd ) + 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, const bool ) +{ + 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 redlines; + { + auto pRedline(std::make_unique(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> 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(**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> 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( + *pRedline, SwUndoId::DELETE)); + } + 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(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 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, + const bool bForceJoinNext ) +{ + bool bJoinText, bJoinPrev; + ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev ); + // #i100466# + if ( bForceJoinNext ) + { + bJoinPrev = false; + } + + { + bool const bSuccess( DeleteRangeImpl( rPam ) ); + 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, const bool) +{ + // 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() ); + ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() ); + + bool const bSuccess( DeleteRangeImplImpl( aDelPam ) ); + if (bSuccess) + { // now copy position from temp copy to given PaM + *rPam.GetPoint() = *aDelPam.GetPoint(); + } + + return bSuccess; +} + +bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam) +{ + 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(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( rPam ) ); + } + + 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 + 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 + sal_uInt32 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 + 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 NodesArary + 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 + SfxItemSet aSet( m_rDoc.GetAttrPool(), + svl::Items{} ); + 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 ); + } + + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique( aDelPam, SwUndoId::REPLACE )); + } + m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true); + + *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 = 0; + rPam.GetPoint()->nContent = rIdx; + *rPam.GetMark() = *rPam.GetPoint(); + m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld ); + + *rPam.GetPoint() = pBkmk->GetMarkPos(); + if(pBkmk->IsExpanded()) + *rPam.GetMark() = pBkmk->GetOtherMarkPos(); + 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(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 \ + SfxItemState aNumRuleState = SfxItemState::UNKNOWN; \ + std::shared_ptr aNumRuleItem; \ + SfxItemState aListIdState = SfxItemState::UNKNOWN; \ + std::shared_ptr aListIdItem; \ + +#define PUSH_NUMRULE_STATE \ + lcl_PushNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd ); + +#define POP_NUMRULE_STATE \ + lcl_PopNumruleState( aNumRuleState, aNumRuleItem, aListIdState, aListIdItem, pDestTextNd, rPam ); + +static void lcl_PushNumruleState( + SfxItemState &aNumRuleState, std::shared_ptr& aNumRuleItem, + SfxItemState &aListIdState, std::shared_ptr& aListIdItem, + const SwTextNode *pDestTextNd ) +{ + // Safe numrule item at destination. + // #i86492# - Safe also item of destination. + const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet(); + if (pAttrSet != nullptr) + { + const SfxPoolItem * pItem = nullptr; + aNumRuleState = pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem); + if (SfxItemState::SET == aNumRuleState) + { + aNumRuleItem.reset(static_cast(pItem->Clone())); + } + + aListIdState = pAttrSet->GetItemState(RES_PARATR_LIST_ID, false, &pItem); + if (SfxItemState::SET == aListIdState) + { + aListIdItem.reset(static_cast(pItem->Clone())); + } + } +} + +static void lcl_PopNumruleState( + SfxItemState aNumRuleState, const std::shared_ptr& aNumRuleItem, + SfxItemState aListIdState, const std::shared_ptr& aListIdItem, + SwTextNode *pDestTextNd, const SwPaM& rPam ) +{ + /* If only a part of one paragraph is copied + restore the numrule at the destination. */ + // #i86492# - restore also item + if ( !lcl_MarksWholeNode(rPam) ) + { + if (SfxItemState::SET == aNumRuleState) + { + pDestTextNd->SetAttr(*aNumRuleItem); + } + else + { + pDestTextNd->ResetAttr(RES_PARATR_NUMRULE); + } + if (SfxItemState::SET == aListIdState) + { + pDestTextNd->SetAttr(*aListIdItem); + } + else + { + pDestTextNd->ResetAttr(RES_PARATR_LIST_ID); + } + } +} + +bool DocumentContentOperationsManager::CopyImpl(SwPaM& rPam, SwPosition& rPos, + SwCopyFlags const flags, + SwPaM *const pCopyRange) const +{ + std::vector> 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() ); + sal_uLong 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, ©Range); + 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, ©Range); + 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* pDoc = rPos.nNode.GetNode().GetDoc(); + const bool bColumnSel = pDoc->IsClipBoard() && pDoc->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 + ( pDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd )) + { + return false; + } + + const bool bEndEqualIns = pDoc == &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 const pCopyPam(pDoc->CreateUnoCursor(rPos)); + + SwTableNumFormatMerge aTNFM( m_rDoc, *pDoc ); + std::unique_ptr> pFlys; + std::vector const* pFlysAtInsPos; + + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + pUndo = new SwUndoCpyDoc(*pCopyPam); + pDoc->GetIDocumentUndoRedo().AppendUndo( std::unique_ptr(pUndo) ); + pFlysAtInsPos = pUndo->GetFlysAnchoredAt(); + } + else + { + pFlys = sw::GetFlysAnchoredAt(*pDoc, rPos.nNode.GetIndex()); + pFlysAtInsPos = pFlys.get(); + } + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->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()) > 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 = !pDoc->IsInsOnlyTextGlossary() && + ( (pDestTextNd && !pDestTextNd->GetText().getLength()) || + ( !bOneNode && !rPos.nContent.GetIndex() ) ); + bool bCopyBookmarks = true; + bool bCopyPageSource = false; + int nDeleteTextNodes = 0; + + // #i104585# copy outline num rule to clipboard (for ASCII filter) + if (pDoc->IsClipBoard() && m_rDoc.GetOutlineNumRule()) + { + pDoc->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 value for possible propagation. + OUString aListIdToPropagate; + const SwNumRule* pNumRuleToPropagate = + pDoc->SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true ); + if ( !pNumRuleToPropagate ) + { + pNumRuleToPropagate = + pDoc->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 = pDoc->GetNodes().MakeTextNode( aInsPos, + pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); + else + { + pDestTextNd = pSttTextNd->MakeCopy(pDoc, 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(pDoc->GetIDocumentUndoRedo()); + pDoc->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 = pDoc->GetNodes()[ aInsPos.GetIndex()-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) + { + pSttTextNd->CopyCollFormat( *pDestTextNd ); + 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(pDoc->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(pDoc->GetIDocumentUndoRedo()); + pDoc->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-=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 = pDoc->GetNodes().MakeTextNode( aInsPos, + pDoc->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 )) + { + pEndTextNd->CopyCollFormat( *pDestTextNd ); + if ( bOneNode ) + { + POP_NUMRULE_STATE + } + } + } + + SfxItemSet aBrkSet( pDoc->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 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 = pDoc->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 (pDoc->IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource) + { + 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 (pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes); + } + + if( pCpyRange ) + { + pCpyRange->SetMark(); + *pCpyRange->GetPoint() = *pCopyPam->GetPoint(); + *pCpyRange->GetMark() = *pCopyPam->GetMark(); + } + + if ( pNumRuleToPropagate != nullptr ) + { + // #i86492# - use , because it also handles the + // Don't reset indent attributes, that would mean loss of direct + // formatting. + pDoc->SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr, + aListIdToPropagate, true, /*bResetIndentAttrs=*/false ); + } + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + pDoc->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..18da12493 --- /dev/null +++ b/sw/source/core/doc/DocumentDeviceManager.cxx @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ) + { + 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( + m_rDoc.GetAttrPool(), + svl::Items< + 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>{}); + VclPtr p = VclPtr::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; + } + 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 pNewVir = VclPtr::Create(DeviceFormat::GRAYSCALE); +#else + VclPtr pNewVir = VclPtr::Create(DeviceFormat::BITMASK); +#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(this)->setVirtualDevice( pNewVir ); + return *mpVirDev; +} + +SfxPrinter& DocumentDeviceManager::CreatePrinter_() const +{ + OSL_ENSURE( ! mpPrt, "Do not call CreatePrinter_(), call getPrinter() instead" ); + +#if OSL_DEBUG_LEVEL > 1 + SAL_INFO("sw", "Printer will be created!" ); +#endif + + // We create a default SfxPrinter. + // The ItemSet is deleted by Sfx! + auto pSet = std::make_unique( + m_rDoc.GetAttrPool(), + svl::Items< + 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>{}); + + VclPtr pNewPrt = VclPtr::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(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 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..ef1aa1a29 --- /dev/null +++ b/sw/source/core/doc/DocumentDrawModelManager.cxx @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(); + } + + SdrPage* pMasterPage = mpDrawModel->AllocPage( false ); + mpDrawModel->InsertPage( pMasterPage ); + 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 ) + { + 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; + 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( " - 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( " - given layer ID already an invisible one." ); + nInvisibleLayerId = _nVisibleLayerId; + } + else + { + OSL_FAIL( " - 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(rFrameFormat.FindSdrObject()); + SdrTextObj* pTextObj = dynamic_cast(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 + +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..800c90967 --- /dev/null +++ b/sw/source/core/doc/DocumentFieldsManager.cxx @@ -0,0 +1,1860 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + + 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(pField)->GetValue(pLayout) ); + else + // Extension to calculate with Strings + aValue.PutString( static_cast(pField)->GetExpStr(pLayout) ); + + // set the new value in Calculator + rCalc.VarChange( pField->GetTyp()->GetName(), aValue ); + } + else if( pMgr ) + { + #if !HAVE_FEATURE_DBCONNECTIVITY + (void) rDoc; + #else + switch( nFieldWhich ) + { + case SwFieldIds::DbNumSet: + { + SwDBNumSetField* pDBField = const_cast(static_cast(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(static_cast(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(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 pNew = rFieldTyp.Copy(); + switch( nFieldWhich ) + { + case SwFieldIds::Dde: + static_cast(pNew.get())->SetDoc( &m_rDoc ); + break; + + case SwFieldIds::Database: + case SwFieldIds::Table: + case SwFieldIds::DateTime: + case SwFieldIds::GetExp: + static_cast(pNew.get())->SetDoc( &m_rDoc ); + break; + + case SwFieldIds::User: + case SwFieldIds::SetExp: + static_cast(pNew.get())->SetDoc( &m_rDoc ); + // JP 29.07.96: Optionally prepare FieldList for Calculator: + mpUpdateFields->InsertFieldType( *pNew ); + break; + case SwFieldIds::TableOfAuthorities : + static_cast(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()) + { + 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(pTmp)->SetDeleted( true ); + else if( SwFieldIds::User == nWhich ) + static_cast(pTmp)->SetDeleted( true ); + else + static_cast(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 ) +{ + // Call Modify() for every field type, + // dependent SwTextField get notified ... + + for( auto const & pFieldType : *mpFieldTypes ) + { + switch( pFieldType->Which() ) + { + // Update table fields second to last + // Update references last + case SwFieldIds::GetRef: + case SwFieldIds::Table: + case SwFieldIds::Database: + case SwFieldIds::JumpEdit: + case SwFieldIds::RefPageSet: // are never expanded! + break; + + case SwFieldIds::Dde: + { + SwMsgPoolItem aUpdateDDE( RES_UPDATEDDETBL ); + pFieldType->ModifyNotification( nullptr, &aUpdateDDE ); + break; + } + case SwFieldIds::GetExp: + case SwFieldIds::SetExp: + case SwFieldIds::HiddenText: + case SwFieldIds::HiddenPara: + // Expression fields are treated separately + break; + default: + pFieldType->ModifyNotification ( nullptr, nullptr ); + } + } + + if( !IsExpFieldsLocked() ) + UpdateExpFields( nullptr, false ); // update expression fields + + // Tables + UpdateTableFields(nullptr); + + // References + UpdateRefFields(); + if( bCloseDB ) + { +#if HAVE_FEATURE_DBCONNECTIVITY + 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(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(&rFieldTyp) ); + switch( nFieldWhich ) + { + case SwFieldIds::SetExp: + static_cast(rFieldTyp).SetDeleted( false ); + break; + case SwFieldIds::User: + static_cast(rFieldTyp).SetDeleted( false ); + break; + case SwFieldIds::Dde: + static_cast(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(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(&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( 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()->ModifyNotification(nullptr, &aTableUpdate); + + if (! bUpdateFields) + bTableSelBreak = true; + } + } + break; + + case SwFieldIds::Macro: + if( bUpdateFields && pDstTextField->GetpTextNode() ) + pDstTextField->GetpTextNode()-> + ModifyNotification( nullptr, pDstFormatField ); + break; + + case SwFieldIds::DatabaseName: + case SwFieldIds::DbNextSet: + case SwFieldIds::DbNumSet: + case SwFieldIds::DbSetNumber: + m_rDoc.ChgDBData(static_cast( pNewField)->GetRealDBData()); + pNewField->GetTyp()->UpdateFields(); + + break; + + case SwFieldIds::Database: +#if HAVE_FEATURE_DBCONNECTIVITY + { + // JP 10.02.96: call ChgValue, so that the style change sets the + // ContentString correctly + SwDBField* pDBField = static_cast(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() ) + pFieldType->ModifyNotification( nullptr, nullptr ); +} + +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 vFields; + pFieldType->GatherFields(vFields); + SwTableFormulaUpdate* pUpdateField = nullptr; + if( pHt && RES_TABLEFML_UPDATE == pHt->Which() ) + pUpdateField = static_cast(pHt); + for(auto pFormatField : vFields) + { + SwTableField* pField = static_cast(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( pUpdateField->m_pTable ); + 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(pItem); + if( pBoxFormula && pBoxFormula->GetDefinedIn() ) + { + const_cast(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(pHt)->m_eFlags )) + return ; + + std::unique_ptr> pCalc; + + if( pFieldType ) + { + std::vector 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(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(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 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), pLayout); + else + pFrame = nullptr; + } + } + if( !pFrame ) + { + // create index to determine the TextNode + SwNodeIndex aIdx( rTextNd ); + FieldsToCalc( *pCalc, + SetGetExpField(aIdx, pFormatField->GetTextField()), + 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(dynamic_cast(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(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! + Point aPt; // return the first frame of the layout - Tab.Headline!! + SwNodeIndex aCNdIdx( *pTableNd, +2 ); + SwContentNode* pCNd = aCNdIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = m_rDoc.GetNodes().GoNext( &aCNdIdx ); + + if (pCNd) + { + std::pair 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), + pLayout); + else + pFrame = nullptr; + } + } + } + if( !pFrame ) + { + // create index to determine the TextNode + SwNodeIndex aIdx( *pTableNd ); + FieldsToCalc(*pCalc, SetGetExpField(aIdx), 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(); + SfxItemSet aTmp( m_rDoc.GetAttrPool(), + svl::Items{} ); + + 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 = static_cast(nHashSize); + OSL_ENSURE( nStrFormatCnt == nHashSize, "Downcasting to sal_uInt16 lost information!" ); + SwHashTable 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(static_cast(pFieldType))->Expand(nsSwGetSetExpType::GSE_STRING, 0, LANGUAGE_SYSTEM)); + SwHash* pFnd = aHashStrTable.Find( rNm, &nPos ); + if( pFnd ) + // modify entry in the hash table + static_cast(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 + 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.GetLocaleDataPtr(); + 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; + sal_uLong nContentStart = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionIndex() + 1; + sal_uLong nContentEnd = m_rDoc.GetNodes().GetEndOfContent().GetIndex(); + SwSectionFormats& rSectFormats = m_rDoc.GetSections(); + for( SwSectionFormats::size_type n = 0; nEndOfSectionNode(), 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 SetExpOutlineNodeMap; + + for (std::unique_ptr const& it : *mpUpdateFields->GetSortList()) + { + SwSection* pSect = const_cast(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; + } + + SwTextField* pTextField = const_cast(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(&pTextField->GetFormatField()); + const SwField* pField = pFormatField->GetField(); + + nWhich = pField->GetTyp()->Which(); + switch( nWhich ) + { + case SwFieldIds::HiddenText: + { + SwHiddenTextField* pHField = const_cast(static_cast(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(static_cast(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 + { + const_cast(static_cast(pField))->Evaluate(&m_rDoc); + aCalc.VarChange( sDBNumNm, static_cast(pField)->GetSetNumber()); + pField->ExpandField(m_rDoc.IsClipBoard(), nullptr); + } +#endif + break; + case SwFieldIds::DbNextSet: + case SwFieldIds::DbNumSet: +#if HAVE_FEATURE_DBCONNECTIVITY + { + UpdateDBNumFields( *const_cast(static_cast(pField)), aCalc ); + if( bCanFill ) + bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc ); + } +#endif + break; + case SwFieldIds::Database: + { +#if HAVE_FEATURE_DBCONNECTIVITY + // evaluate field + const_cast(static_cast(pField))->Evaluate(); + + SwDBData aTmpDBData(static_cast(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(static_cast(pField)); + + if( (!pUpdateField || pUpdateField == pTextField ) + && pGField->IsInBodyText() ) + { + OUString aNew = LookString( aHashStrTable, pGField->GetFormula() ); + pGField->ChgExpStr( aNew, pLayout ); + } + } + else + { + SwSetExpField* pSField = const_cast(static_cast(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(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(static_cast(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(static_cast(pField)); + SwSetExpFieldType* pSFieldTyp = static_cast(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(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 + 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(static_cast(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 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->ModifyNotification( nullptr, pMsgHint ); + break; + case SwFieldIds::DocStat: + pFieldType->ModifyNotification( 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, sal_uLong 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; + sal_uLong 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 vFields; + GetSysFieldType(aType)->GatherFields(vFields); + for(auto pFormatField: vFields) + { + if (pFormatField->GetTextField()) + { + bool bChgd = false; + switch( aType ) + { + case SwFieldIds::DocInfo: + if( static_cast(pFormatField->GetField())->IsFixed() ) + { + bChgd = true; + SwDocInfoField* pDocInfField = static_cast(pFormatField->GetField()); + pDocInfField->SetExpansion( static_cast( + pDocInfField->GetTyp())->Expand( + pDocInfField->GetSubType(), + pDocInfField->GetFormat(), + pDocInfField->GetLanguage(), + pDocInfField->GetName() ) ); + } + break; + + case SwFieldIds::Author: + if( static_cast(pFormatField->GetField())->IsFixed() ) + { + bChgd = true; + SwAuthorField* pAuthorField = static_cast(pFormatField->GetField()); + pAuthorField->SetExpansion( SwAuthorFieldType::Expand( pAuthorField->GetFormat() ) ); + } + break; + + case SwFieldIds::ExtUser: + if( static_cast(pFormatField->GetField())->IsFixed() ) + { + bChgd = true; + SwExtUserField* pExtUserField = static_cast(pFormatField->GetField()); + pExtUserField->SetExpansion( SwExtUserFieldType::Expand(pExtUserField->GetSubType()) ); + } + break; + + case SwFieldIds::DateTime: + if( static_cast(pFormatField->GetField())->IsFixed() ) + { + bChgd = true; + static_cast(pFormatField->GetField())->SetDateTime( + DateTime(Date(nDate), tools::Time(nTime)) ); + } + break; + + case SwFieldIds::Filename: + if( static_cast(pFormatField->GetField())->IsFixed() ) + { + bChgd = true; + SwFileNameField* pFileNameField = + static_cast(pFormatField->GetField()); + pFileNameField->SetExpansion( static_cast( + 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 + 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 + pMgr->CloseAll(false); +#endif +} + +void DocumentFieldsManager::FieldsToCalc( SwCalc& rCalc, sal_uLong nLastNd, sal_uInt16 nLastCnt ) +{ + // create the sorted list of all SetFields + mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC ); + mbNewFieldLst = false; + +#if !HAVE_FEATURE_DBCONNECTIVITY + 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 + pMgr->CloseAll(false); +#endif +} + +void DocumentFieldsManager::FieldsToExpand( SwHashTable & 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(static_cast(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 + // for string/text fields + pSField->ChgExpStr(aNew, &rLayout); + + // look up the field's name + aNew = static_cast(pSField->GetTyp())->GetSetRefName(); + // Entry present? + sal_uInt16 nPos; + SwHash* pFnd = rHashTable.Find( aNew, &nPos ); + if( pFnd ) + // modify entry in the hash table + static_cast(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( 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 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 + (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(rDBField).SetCondValid( bPar1 ); + else + static_cast(rDBField).SetCondValid( bPar1 ); + + if( !rDBField.GetRealDBData().sDataSource.isEmpty() ) + { + // Edit a certain database + if( SwFieldIds::DbNextSet == nFieldType ) + static_cast(rDBField).Evaluate(&m_rDoc); + else + static_cast(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..8d5cc79dc --- /dev/null +++ b/sw/source/core/doc/DocumentLayoutManager.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(static_cast( 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(pFormat, 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( 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 aToDeleteFrameFormats; + const sal_uLong 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(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( + pTextNd->GetTextAttrForCharAt( pPos->nContent.GetIndex(), + RES_TXTATR_FLYCNT )); + if ( pAttr && (pAttr->GetFlyCnt().GetFrameFormat() == pFormat) ) + { + // don't delete, set pointer to 0 + const_cast(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(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; + if(bDraw) + { + const auto pCAnchor = rNewAnchor.GetContentAnchor(); + 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())) && + pCAnchor && m_rDoc.IsInHeaderFooter(pCAnchor->nNode); + } + + // 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(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, 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() || 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(); 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(pDest,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(*pDest), m_rDoc)); + + if(pDest->GetAnchor() == rNewAnchor) + { + // Do *not* connect to layout, if a 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(pDest,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 the draw format has a TextBox, then copy its fly format as well. + if (SwFrameFormat* pSourceTextBox = SwTextBoxHelper::getOtherTextBoxFormat(&rSource, RES_DRAWFRMFMT)) + { + SwFormatAnchor boxAnchor(rNewAnchor); + if (RndStdIds::FLY_AS_CHAR == boxAnchor.GetAnchorId()) + { + // AS_CHAR *must not* be set on textbox fly-frame + boxAnchor.SetType(RndStdIds::FLY_AT_CHAR); + } + // presumably these anchors are supported though not sure + assert(RndStdIds::FLY_AT_CHAR == boxAnchor.GetAnchorId() || RndStdIds::FLY_AT_PARA == boxAnchor.GetAnchorId()); + SwFrameFormat* pDestTextBox = CopyLayoutFormat(*pSourceTextBox, + boxAnchor, bSetTextFlyAtt, bMakeFrames); + SwAttrSet aSet(pDest->GetAttrSet()); + SwFormatContent aContent(pDestTextBox->GetContent().GetContentIdx()->GetNode().GetStartNode()); + aSet.Put(aContent); + pDest->SetFormatAttr(aSet); + + // Link FLY and DRAW formats, so it becomes a text box + pDest->SetOtherTextBoxFormat(pDestTextBox); + pDestTextBox->SetOtherTextBoxFormat(pDest); + } + + 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()); + } + + 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..c5ca5b11e --- /dev/null +++ b/sw/source/core/doc/DocumentLinksAdministrationManager.cxx @@ -0,0 +1,583 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(&rLnk) != nullptr) + { + tools::SvRef 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( + 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 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( const OUString& rStr, SwPaM*& rpPam, std::unique_ptr& 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 ) ); + OUString sCmp( sItem.copy( nPos + 1 )); + sItem = rCC.lowercase( sItem ); + + FindItem aPara( sName ); + + if( sCmp == "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, 0, + *aPara.pTableNd->EndOfSectionNode(), 1 )); + return true; + } + } + else if( sCmp == "frame" ) + { + SwNodeIndex* pIdx; + SwNode* pNd; + const SwFlyFrameFormat* pFlyFormat = m_rDoc.FindFlyByName( sName ); + if( pFlyFormat ) + { + pIdx = const_cast(pFlyFormat->GetContent().GetContentIdx()); + if( pIdx ) + { + pNd = &pIdx->GetNode(); + if( !pNd->IsNoTextNode() ) + { + rpRange.reset(new SwNodeRange( *pNd, 1, *pNd->EndOfSectionNode() )); + return true; + } + } + } + } + else if( sCmp == "region" ) + { + sItem = sName; // Is being dealt with further down! + bContinue = true; + } + else if( sCmp == "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, 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(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, 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 + +#include +#include +#include +#include + +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, + " - 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( " - 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..d74e924fd --- /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 +#include +#include +#include + +#include +#include + + +namespace sw +{ + +DocumentListsManager::DocumentListsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), maLists(), maListStyleLists() +{ +} + +SwList* DocumentListsManager::createList( const OUString& rListId, + const OUString& sDefaultListStyleName ) +{ + OUString sListId = rListId; + if ( sListId.isEmpty() ) + { + sListId = CreateUniqueListId(); + } + + if ( getListByName( sListId ) ) + { + OSL_FAIL( " - provided list id already used. Serious defect." ); + return nullptr; + } + + SwNumRule* pDefaultNumRuleForNewList = m_rDoc.FindNumRulePtr( sDefaultListStyleName ); + if ( !pDefaultNumRuleForNewList ) + { + OSL_FAIL( " - 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( " - no list style name provided. Serious defect." ); + return; + } + + if ( getListForListStyle( sListStyleName ) ) + { + OSL_FAIL( " - a list for the provided list style name already exists. Serious defect." ); + return; + } + + SwNumRule* pNumRule = m_rDoc.FindNumRulePtr( sListStyleName ); + if ( !pNumRule ) + { + OSL_FAIL( " - 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, + " - 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, + " - 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 ) +{ + 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::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..25f381476 --- /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 +#include +#include +#include +#include +#include +#include + +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->IsHideRedlines()) + { + SwTextFrame const*const pFrame(static_cast(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 (sal_uLong 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..f3aaa13a6 --- /dev/null +++ b/sw/source/core/doc/DocumentRedlineManager.cxx @@ -0,0 +1,3233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(&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( 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(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) +{ + // 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) + { + SwStartNode *const pTableOrSectionNode( + currentStart.nNode.GetNode().IsTableNode() + ? static_cast(currentStart.nNode.GetNode().GetTableNode()) + : static_cast(currentStart.nNode.GetNode().GetSectionNode())); + assert(pTableOrSectionNode); // known pathology + for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) + { + pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden); + } + for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts()) + { + if (pLayout->IsHideRedlines()) + { + if (pTableOrSectionNode->IsTableNode()) + { + static_cast(pTableOrSectionNode)->DelFrames(pLayout); + } + else + { + static_cast(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 + { + std::vector frames; + SwIterator aIter(*pNode); + for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + frames.push_back(pFrame); + } + } + 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(pFrame->GetMergedPara()->pLastNode); + } + } + SwNodeIndex tmp(*pLast); + // skip over hidden sections! + pNode = static_cast(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) +{ + bool isAppendObjsCalled(false); + rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode); + SwPosition currentStart(*rPam.Start()); + SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode()); + while (!pStartNode) + { + SwStartNode const*const pTableOrSectionNode( + currentStart.nNode.GetNode().IsTableNode() + ? static_cast(currentStart.nNode.GetNode().GetTableNode()) + : static_cast(currentStart.nNode.GetNode().GetSectionNode())); + assert(pTableOrSectionNode); // known pathology + for (sal_uLong j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j) + { + pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None); + } + if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines()) + { + // 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 + { + std::vector frames; + SwIterator aIter(*pNode); + for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + frames.push_back(pFrame); + } + } + 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; + } + + // 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(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! + } + } + 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(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->IsHideRedlines()) + { + 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* pDoc = aPam.GetDoc(); + // using Undo, copy paragraph style + SwTextFormatColl* pFromColl = pFromNode->GetTextColl(); + SwTextFormatColl* pToColl = pToNode->GetTextColl(); + if (bCopy && pFromColl != pToColl) + pDoc->SetTextFormatColl(aPam, pFromColl); + + // using Undo, remove direct paragraph formatting of the "To" paragraph, + // and apply here direct paragraph formatting of the "From" paragraph + SfxItemSet aTmp( + pDoc->GetAttrPool(), + svl::Items< + 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>{}); + 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) + pDoc->getIDocumentContentOperations().InsertItemSet(aPam, aTmp2); + else if (!bCopy && (!bSameSet || pFromColl != pToColl)) + return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 ); + } + return nullptr; + } + + 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: + rArr.DeleteAndDestroy( rPos-- ); + 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 ); + 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 ); + 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 ); + + 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() ); + std::set aResetAttrsArray; + + sal_uInt16 aResetableSetRange[] = { + RES_PARATR_BEGIN, RES_PARATR_END - 1, + RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1, + 0 + }; + + const sal_uInt16 *pUShorts = aResetableSetRange; + while (*pUShorts) + { + for (sal_uInt16 i = pUShorts[0]; i <= pUShorts[1]; ++i) + aResetAttrsArray.insert( aResetAttrsArray.end(), i ); + pUShorts += 2; + } + + 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 = pStt == rPam.GetPoint() ? rPam.GetMark() + : rPam.GetPoint(); + 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 = pStt == rPam.GetPoint() ? rPam.GetMark() + : rPam.GetPoint(); + SwDoc* pDoc = rPam.GetDoc(); + if( !pStt->nContent.GetIndex() && + !pDoc->GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() ) + { + const SwRangeRedline* pRedl = pDoc->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() && + !pDoc->GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() && + pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() ) + { + const SwRangeRedline* pRedl = pDoc->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 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(m_rRedline) = *m_pCursor; + } + }; +} + +namespace sw +{ + +DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc) + : m_rDoc(i_rSwdoc) + , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) + , mpRedlineTable(new SwRedlineTable) + , mpExtraRedlineTable(new SwExtraRedlineTable) + , mbIsRedlineMove(false) + , mnAutoFormatRedlnCommentNo(0) +{ +} + +RedlineFlags DocumentRedlineManager::GetRedlineFlags() const +{ + return meRedlineFlags; +} + +void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode ) +{ + if( meRedlineFlags != eMode ) + { + 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); // 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 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 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 < mpRedlineTable->size(); ++i) + { + SwRangeRedline *const pRedline((*mpRedlineTable)[i]); + (pRedline->*pFnc)(nLoop, i); + while (mpRedlineTable->size() <= i + || (*mpRedlineTable)[i] != pRedline) + { // ensure current position + --i; // a previous redline may have been deleted + } + } + + //SwRangeRedline::MoveFromSection routinely changes + //the keys that mpRedlineTable is sorted by + mpRedlineTable->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 *mpRedlineTable; +} + +SwRedlineTable& DocumentRedlineManager::GetRedlineTable() +{ + return *mpRedlineTable; +} + +const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const +{ + return *mpExtraRedlineTable; +} + +SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() +{ + return *mpExtraRedlineTable; +} + +bool DocumentRedlineManager::HasExtraRedlineTable() const +{ + return mpExtraRedlineTable != nullptr; +} + +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) +{ + bool bMerged = false; + CHECK_REDLINE( *this ) + + if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags)) + { + pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); + + if( m_rDoc.IsAutoFormatRedline() ) + { + pNewRedl->SetAutoFormat(); + if( mpAutoFormatRedlnComment && !mpAutoFormatRedlnComment->isEmpty() ) + { + pNewRedl->SetComment( *mpAutoFormatRedlnComment ); + pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo ); + } + } + + SwPosition* pStt = pNewRedl->Start(), + * pEnd = pStt == pNewRedl->GetPoint() ? pNewRedl->GetMark() + : pNewRedl->GetPoint(); + { + 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 < mpRedlineTable->size(); bDec ? n : ++n ) + { + bDec = false; + + SwRangeRedline* pRedl = (*mpRedlineTable)[ n ]; + SwPosition* pRStt = pRedl->Start(), + * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() + : pRedl->GetPoint(); + + // #i8518# remove empty redlines while we're at it + if( ( *pRStt == *pREnd ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + mpRedlineTable->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 ) ) + { + bool bDelete = false; + + // Merge if applicable? + if( (( SwComparePosition::Behind == eCmpPos && + IsPrevPos( *pREnd, *pStt ) ) || + ( SwComparePosition::CollideStart == eCmpPos ) || + ( SwComparePosition::OverlapBehind == eCmpPos ) ) && + pRedl->CanCombine( *pNewRedl ) && + ( n+1 >= mpRedlineTable->size() || + ( *(*mpRedlineTable)[ n+1 ]->Start() >= *pEnd && + *(*mpRedlineTable)[ n+1 ]->Start() != *pREnd ) ) ) + { + pRedl->SetEnd( *pEnd, pREnd ); + if( !pRedl->HasValidRange() ) + { + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl ); + } + + bMerged = true; + bDelete = true; + } + else if( (( SwComparePosition::Before == eCmpPos && + IsPrevPos( *pEnd, *pRStt ) ) || + ( SwComparePosition::CollideEnd == eCmpPos ) || + ( SwComparePosition::OverlapBefore == eCmpPos ) ) && + pRedl->CanCombine( *pNewRedl ) && + ( !n || + *(*mpRedlineTable)[ n-1 ]->End() != *pRStt )) + { + pRedl->SetStart( *pStt, pRStt ); + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl ); + + bMerged = true; + bDelete = true; + } + else if ( SwComparePosition::Outside == eCmpPos ) + { + // own insert-over-insert redlines: + // just scrap the inside ones + mpRedlineTable->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; + } + } + else if( SwComparePosition::Inside == eCmpPos ) + { + // split up + if( *pEnd != *pREnd ) + { + SwRangeRedline* pCpy = new SwRangeRedline( *pRedl ); + pCpy->SetStart( *pEnd ); + mpRedlineTable->Insert( pCpy ); + } + pRedl->SetEnd( *pStt, pREnd ); + if( ( *pStt == *pRStt ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + mpRedlineTable->DeleteAndDestroy( n ); + bDec = true; + } + else if( !pRedl->HasValidRange() ) + { + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->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 ); + mpRedlineTable->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 ); + mpRedlineTable->Insert( pCpy ); + } + pRedl->SetEnd( *pStt, pREnd ); + if( ( *pStt == *pRStt ) && + ( pRedl->GetContentIdx() == nullptr ) ) + { + mpRedlineTable->DeleteAndDestroy( n ); + bDec = true; + } + else if( !pRedl->HasValidRange() ) + { + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->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 ); + mpRedlineTable->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 + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl, n ); + bDec = true; + break; + + case SwComparePosition::OverlapBehind: + pRedl->SetEnd( *pStt, pREnd ); + if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr ) + { + mpRedlineTable->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 + mpRedlineTable->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 ) + mpRedlineTable->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 ); + mpRedlineTable->DeleteAndDestroy( n ); + bDec = true; + } + else if( SwComparePosition::OverlapBehind == eCmpPos ) + pNewRedl->SetStart( *pREnd, pStt ); + else + pNewRedl->SetEnd( *pRStt, pEnd ); + break; + + case SwComparePosition::CollideStart: + case SwComparePosition::CollideEnd: + 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. + mpRedlineTable->Insert(pNewRedl); + pRedl->Show(0, mpRedlineTable->GetPos(pRedl)); + mpRedlineTable->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; + } + + mpRedlineTable->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 ) ) + { + + // 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; + mpRedlineTable->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; + } + if( !bCallDelete && !bDec && *pEnd == *pREnd ) + { + m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl ); + bCompress = true; + } + else if ( bCallDelete || !bDec ) + { + // delete new redline, except in some cases of fallthrough from previous + // case ::Equal (eg. same portion w:del in w:ins in OOXML import) + delete pNewRedl; + pNewRedl = nullptr; + } + break; + + case SwComparePosition::Outside: + { + mpRedlineTable->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 ) + mpRedlineTable->DeleteAndDestroy( n ); + else + { + pRedl->SetStart( *pEnd, pRStt ); + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->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 ) + { + mpRedlineTable->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, mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 )) + { + mpRedlineTable->Insert(pNewRedl); + pRedl->Hide(0, mpRedlineTable->GetPos(pRedl)); + mpRedlineTable->Remove( pNewRedl ); + } + } + else + { + pNew = new SwRangeRedline( *pRedl ); + pNew->PushData( *pNewRedl ); + pNew->SetEnd( *pEnd ); + pNewRedl->SetEnd( *pRStt, pEnd ); + pRedl->SetStart( *pNew->End(), pRStt ) ; + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl ); + bDec = true; + } + } + break; + + case SwComparePosition::OverlapBehind: + { + if( *pStt == *pRStt ) + { + pRedl->PushData( *pNewRedl ); + pNewRedl->SetStart( *pREnd, pStt ); + if( IsHideChanges( meRedlineFlags )) + { + mpRedlineTable->Insert( pNewRedl ); + pRedl->Hide(0, mpRedlineTable->GetPos(pRedl)); + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl ); + } + } + } + break; + default: + break; + } + + // insert the pNew part (if it exists) + if( pNew ) + { + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 + mpRedlineTable->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 ) ) + mpRedlineTable->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 + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 ); + mpRedlineTable->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 && + *(*mpRedlineTable)[ n-1 ]->End() < *pStt ) + { + // If that's the case we can merge it, meaning + // the new one covers this well + pNewRedl->SetEnd( *pREnd, pEnd ); + mpRedlineTable->DeleteAndDestroy( n ); + bDec = true; + } + break; + case SwComparePosition::CollideStart: + if( pRedl->IsOwnRedline( *pNewRedl ) && + pRedl->CanCombine( *pNewRedl ) && + n+1 < mpRedlineTable->size() && + *(*mpRedlineTable)[ n+1 ]->Start() < *pEnd ) + { + // If that's the case we can merge it, meaning + // the new one covers this well + pNewRedl->SetStart( *pRStt, pStt ); + mpRedlineTable->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. + + SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode(); + SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode(); + SwTextNode* pDelNode = pStt->nNode.GetNode().GetTextNode(); + SwTextNode* pTextNode; + SwNodeIndex aIdx( pEnd->nNode.GetNode() ); + bool bFirst = true; + + while (pDelNode != nullptr && pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex()) + { + pTextNode = pTextNd->GetTextNode(); + if (pTextNode && pDelNode != pTextNode ) + { + 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 xRedlineExtraData; + if (!bFirst) + pExtraData->SetFormatAll(false); + xRedlineExtraData.reset(pExtraData); + pPar->SetExtraData( xRedlineExtraData.get() ); + } + mpRedlineTable->Insert( pPar ); + } + + // modify paragraph formatting + lcl_CopyStyle(*pStt, aPos); + } + pTextNd = SwNodes::GoPrevious( &aIdx ); + + if (bFirst) + bFirst = false; + } + } + } + bool const ret = mpRedlineTable->Insert( pNewRedl ); + assert(ret || !pNewRedl); + if (ret && !pNewRedl) + { + bMerged = true; // treat InsertWithValidRanges as "merge" + } + } + } + + if( bCompress ) + CompressRedlines(); + } + else + { + 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 (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 + + mpExtraRedlineTable->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 + + mpExtraRedlineTable->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) = 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 < mpRedlineTable->size(); ++n ) + { + SwRangeRedline* pPrev = (*mpRedlineTable)[ n-1 ], + * pCur = (*mpRedlineTable)[ n ]; + const SwPosition* pPrevStt = pPrev->Start(), + * pPrevEnd = pPrevStt == pPrev->GetPoint() + ? pPrev->GetMark() : pPrev->GetPoint(); + const SwPosition* pCurStt = pCur->Start(), + * pCurEnd = pCurStt == pCur->GetPoint() + ? pCur->GetMark() : pCur->GetPoint(); + 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() ); + mpRedlineTable->DeleteAndDestroy( n ); + --n; + if( pFnc ) + (pPrev->*pFnc)(0, nPrevIndex); + } + } + 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(); + GetRedline( *pStt, &n ); + for ( ; n < mpRedlineTable->size(); ++n) + { + SwRangeRedline * pRedline = (*mpRedlineTable)[ 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); + mpRedlineTable->DeleteAndDestroy( n-- ); + pRedline = nullptr; + break; + } + if (pRedline && !pRedline->HasValidRange()) + { + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedline, n ); + } + if( pNew ) + mpRedlineTable->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 pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange )); + if( pUndo->GetRedlSaveCount() ) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); + } + } + + const SwPosition* pStt = rRange.Start(), + * pEnd = pStt == rRange.GetPoint() ? rRange.GetMark() + : rRange.GetPoint(); + SwRedlineTable::size_type n = 0; + GetRedline( *pStt, &n ); + for( ; n < mpRedlineTable->size() ; ++n ) + { + SwRangeRedline* pRedl = (*mpRedlineTable)[ n ]; + if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() ) + continue; + + SwPosition* pRStt = pRedl->Start(), + * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() + : pRedl->GetPoint(); + switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) ) + { + case SwComparePosition::Equal: + case SwComparePosition::Outside: + pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove); + mpRedlineTable->DeleteAndDestroy( n-- ); + bChg = true; + break; + + case SwComparePosition::OverlapBefore: + pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove); + pRedl->SetStart( *pEnd, pRStt ); + pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add); + // re-insert + mpRedlineTable->Remove( n ); + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->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 + mpRedlineTable->Remove( n ); + mpRedlineTable->Insert( pRedl ); + --n; + } + if( pCpy ) + mpRedlineTable->Insert( pCpy ); + } + } + break; + + case SwComparePosition::CollideEnd: + case SwComparePosition::Before: + n = mpRedlineTable->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 sal_uLong nNdIdx = rNd.GetIndex(); + for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n ) + { + const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; + sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(), + nMk = pTmp->GetMark()->nNode.GetIndex(); + if( nPt < nMk ) { long 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 ? +} + +const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos, + SwRedlineTable::size_type* pFndPos ) const +{ + SwRedlineTable::size_type nO = mpRedlineTable->size(), nM, nU = 0; + if( nO > 0 ) + { + nO--; + while( nU <= nO ) + { + nM = nU + ( nO - nU ) / 2; + const SwRangeRedline* pRedl = (*mpRedlineTable)[ nM ]; + const SwPosition* pStt = pRedl->Start(); + const SwPosition* pEnd = pStt == pRedl->GetPoint() + ? pRedl->GetMark() + : pRedl->GetPoint(); + if( pEnd == pStt + ? *pStt == rPos + : ( *pStt <= rPos && rPos < *pEnd ) ) + { + while( nM && rPos == *(*mpRedlineTable)[ nM - 1 ]->End() && + rPos == *(*mpRedlineTable)[ nM - 1 ]->Start() ) + { + --nM; + pRedl = (*mpRedlineTable)[ 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 >= *(*mpRedlineTable)[ nM - 1 ]->Start() && + rPos <= *(*mpRedlineTable)[ nM - 1 ]->End() && + ( RedlineType::Insert == (*mpRedlineTable)[ nM - 1 ]->GetType() ) ) + { + --nM; + pRedl = (*mpRedlineTable)[ nM ]; + } + else if( ( nM + 1 ) <= nO && rPos >= *(*mpRedlineTable)[ nM + 1 ]->Start() && + rPos <= *(*mpRedlineTable)[ nM + 1 ]->End() && + ( RedlineType::Insert == (*mpRedlineTable)[ nM + 1 ]->GetType() ) ) + { + ++nM; + pRedl = (*mpRedlineTable)[ 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 = (*mpRedlineTable)[ nPos ]; + 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(*pTmp) ); + } + + bRet |= lcl_AcceptRedline( *mpRedlineTable, nPos, bCallDelete ); + + if( nSeqNo ) + { + if( SwRedlineTable::npos == nPos ) + nPos = 0; + SwRedlineTable::size_type nFndPos = 2 == nLoopCnt + ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos ) + : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ); + if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) && + SwRedlineTable::npos != ( nFndPos = + mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) ) + { + nPos = nFndPos; + pTmp = (*mpRedlineTable)[ 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( aPam )); + } + + int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, *mpRedlineTable, + 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 = pStt == rPam.GetPoint() ? rPam.GetMark() + : rPam.GetPoint(); + + const sal_uLong nSttIdx = pStt->nNode.GetIndex(); + const sal_uLong nEndIdx = pEnd->nNode.GetIndex(); + + for( SwRedlineTable::size_type n = 0; n < mpRedlineTable->size() ; ++n ) + { + const SwRangeRedline* pTmp = (*mpRedlineTable)[ n ]; + sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(), + nMk = pTmp->GetMark()->nNode.GetIndex(); + if( nPt < nMk ) { long 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 = (*mpRedlineTable)[ nPos ]; + 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( *pTmp ) ); + } + + bRet |= lcl_RejectRedline( *mpRedlineTable, nPos, bCallDelete ); + + if( nSeqNo ) + { + if( SwRedlineTable::npos == nPos ) + nPos = 0; + SwRedlineTable::size_type nFndPos = 2 == nLoopCnt + ? mpRedlineTable->FindNextSeqNo( nSeqNo, nPos ) + : mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ); + if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) && + SwRedlineTable::npos != ( nFndPos = + mpRedlineTable->FindPrevSeqNo( nSeqNo, nPos ))) ) + { + nPos = nFndPos; + pTmp = (*mpRedlineTable)[ 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(aPam) ); + } + + int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, *mpRedlineTable, + 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 (mpRedlineTable->size() > 1) + { + { + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, OUString::number(mpRedlineTable->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 (!mpRedlineTable->empty() && bSuccess) + { + if (bAccept) + bSuccess = AcceptRedline(mpRedlineTable->size() - 1, true); + else + bSuccess = RejectRedline(mpRedlineTable->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 < mpRedlineTable->size(); ++n ) + { + pFnd = (*mpRedlineTable)[ 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 < mpRedlineTable->size() ) + { + const SwRangeRedline* pTmp = (*mpRedlineTable)[ 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 < mpRedlineTable->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 = (*mpRedlineTable)[ --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 = (*mpRedlineTable)[ --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 = pStt == rPaM.GetPoint() ? rPaM.GetMark() + : rPaM.GetPoint(); + SwRedlineTable::size_type n = 0; + if( GetRedlineTable().FindAtPosition( *pStt, n ) ) + { + for( ; n < mpRedlineTable->size(); ++n ) + { + bRet = true; + SwRangeRedline* pTmp = (*mpRedlineTable)[ 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 & DocumentRedlineManager::GetRedlinePassword() const +{ + return maRedlinePasswd; +} + +void DocumentRedlineManager::SetRedlinePassword( + /*[in]*/const uno::Sequence & 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 ) + { + mpAutoFormatRedlnComment.reset( new OUString( *pText ) ); + } + else + { + mpAutoFormatRedlnComment.reset(); + } + + mnAutoFormatRedlnCommentNo = nSeqNo; +} + +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..ca68d3e30 --- /dev/null +++ b/sw/source/core/doc/DocumentSettingManager.cxx @@ -0,0 +1,964 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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), + mbTreatSingleColumnBreakAsPageBreak(false), + mbSurroundTextWrapSmall(false), + mbPropLineSpacingShrinksFirstLine(true), + mbSubtractFlys(false), + mApplyParagraphMarkFormatToNumbering(false), + mbLastBrowseMode( false ), + mbDisableOffPagePositioning ( false ), + mbProtectBookmarks(false), + mbProtectFields(false), + mbHeaderSpacingBelowLastPara(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::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; + 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(); + // counting of phantoms depends on + 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::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; + + // COMPATIBILITY FLAGS END + + case DocumentSettingId::BROWSE_MODE: //can be used temporary (load/save) when no SwViewShell is available + 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; + 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& sw::DocumentSettingManager::getForbiddenCharacterTable() +{ + if (!mxForbiddenCharsTable) + mxForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext()); + return mxForbiddenCharsTable; +} + +const std::shared_ptr& 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 ) + { + 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 + // No mbIsGlobalDoc + // No mbGlblDocSaveLinks + // No mbIsLabelDoc + // No mbPurgeOLE + // No mbKernAsianPunctuation + mbParaSpaceMax = rSource.mbParaSpaceMax; + mbParaSpaceMaxAtPages = rSource.mbParaSpaceMaxAtPages; + mbTabCompat = rSource.mbTabCompat; + mbUseVirtualDevice = rSource.mbUseVirtualDevice; + mbAddFlyOffsets = rSource.mbAddFlyOffsets; + // No 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; + // No mbMathBaselineAlignment + // No mbStylesNoDefault + // No 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; + // No mbProtectForm + mbMsWordCompTrailingBlanks = rSource.mbMsWordCompTrailingBlanks; + mbMsWordCompMinLineHeightByFly = rSource.mbMsWordCompMinLineHeightByFly; + // No mbInvertBorderSpacing + mbCollapseEmptyCellPara = rSource.mbCollapseEmptyCellPara; + mbTabAtLeftIndentForParagraphsInList = rSource.mbTabAtLeftIndentForParagraphsInList; + // No mbSmallCapsPercentage66 + // No mbTabOverflow + mbUnbreakableNumberings = rSource.mbUnbreakableNumberings; + // No mbClippedPictures + // No mbBackgroundParaOverDrawings + mbTabOverMargin = rSource.mbTabOverMargin; + // No mbTreatSingleColumnBreakAsPageBreak + // No mbSurroundTextWrapSmall + // No mbPropLineSpacingShrinksFirstLine + mbSubtractFlys = rSource.mbSubtractFlys; + // No mbLastBrowseMode + mbDisableOffPagePositioning = rSource.mbDisableOffPagePositioning; + // No mbEmptyDbFieldHidesPara + mbEmptyDbFieldHidesPara = rSource.mbEmptyDbFieldHidesPara; + mbContinuousEndnotes = rSource.mbContinuousEndnotes; + // No mbProtectBookmarks + // No mbProtectFields + mbHeaderSpacingBelowLastPara = rSource.mbHeaderSpacingBelowLastPara; +} + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("DocumentSettingManager")); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbHTMLMode")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbHTMLMode).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsGlobalDoc")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbIsGlobalDoc).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbGlblDocSaveLinks")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbGlblDocSaveLinks).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsLabelDoc")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbIsLabelDoc).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbPurgeOLE")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbPurgeOLE).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbKernAsianPunctuation")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbKernAsianPunctuation).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMax")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbParaSpaceMax).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMaxAtPages")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbParaSpaceMaxAtPages).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabCompat")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTabCompat).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseVirtualDevice")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUseVirtualDevice).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddFlyOffsets")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbAddFlyOffsets).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddVerticalFlyOffsets")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbAddVerticalFlyOffsets).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddExternalLeading")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbAddExternalLeading).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseHiResolutionVirtualDevice")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUseHiResolutionVirtualDevice).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldLineSpacing")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbOldLineSpacing).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaSpacingToTableCells")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbAddParaSpacingToTableCells).getStr())); + xmlTextWriterEndElement(pWriter); + xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaLineSpacingToTableCells")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbAddParaLineSpacingToTableCells).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerObjectPos")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUseFormerObjectPos).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerTextWrapping")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUseFormerTextWrapping).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbConsiderWrapOnObjPos")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbConsiderWrapOnObjPos).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbMathBaselineAlignment")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbMathBaselineAlignment).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbStylesNoDefault")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbStylesNoDefault).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbFloattableNomargins")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbFloattableNomargins).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldNumbering")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbOldNumbering).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreFirstLineIndentInNumbering")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbIgnoreFirstLineIndentInNumbering).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotJustifyLinesWithManualBreak")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbDoNotJustifyLinesWithManualBreak).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotResetParaAttrsForNumFont")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbDoNotResetParaAttrsForNumFont).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTableRowKeep")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTableRowKeep).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreTabsAndBlanksForLineCalculation")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbIgnoreTabsAndBlanksForLineCalculation).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotCaptureDrawObjsOnPage")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbDoNotCaptureDrawObjsOnPage).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbClipAsCharacterAnchoredWriterFlyFrames")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbClipAsCharacterAnchoredWriterFlyFrames).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnixForceZeroExtLeading")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUnixForceZeroExtLeading).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabRelativeToIndent")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTabRelativeToIndent).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbProtectForm")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbProtectForm).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompTrailingBlanks")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbMsWordCompTrailingBlanks).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompMinLineHeightByFly")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbMsWordCompMinLineHeightByFly).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbInvertBorderSpacing")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbInvertBorderSpacing).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbCollapseEmptyCellPara")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbCollapseEmptyCellPara).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabAtLeftIndentForParagraphsInList")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTabAtLeftIndentForParagraphsInList).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbSmallCapsPercentage66")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbSmallCapsPercentage66).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverflow")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTabOverflow).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnbreakableNumberings")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbUnbreakableNumberings).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbClippedPictures")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbClippedPictures).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbBackgroundParaOverDrawings")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbBackgroundParaOverDrawings).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverMargin")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTabOverMargin).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbTreatSingleColumnBreakAsPageBreak")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbTreatSingleColumnBreakAsPageBreak).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbSurroundTextWrapSmall")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbSurroundTextWrapSmall).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbPropLineSpacingShrinksFirstLine")); + xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbPropLineSpacingShrinksFirstLine).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbSubtractFlys")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbSubtractFlys).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbLastBrowseMode")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbLastBrowseMode).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbDisableOffPagePositioning")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbDisableOffPagePositioning).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbEmptyDbFieldHidesPara")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbEmptyDbFieldHidesPara).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbContinuousEndnotes")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbContinuousEndnotes).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbHeaderSpacingBelowLastPara")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), + BAD_CAST(OString::boolean(mbHeaderSpacingBelowLastPara).getStr())); + xmlTextWriterEndElement(pWriter); + + 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 +#include +#include +#include +#include +#include + +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..9508e6d72 --- /dev/null +++ b/sw/source/core/doc/DocumentStatisticsManager.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sw +{ + +DocumentStatisticsManager::DocumentStatisticsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), + mpDocStat( new SwDocStat ), + mbInitialized( false ), + maStatsUpdateIdle( i_rSwdoc ) + +{ + maStatsUpdateIdle.SetPriority( TaskPriority::LOWEST ); + maStatsUpdateIdle.SetInvokeHandler( LINK( this, DocumentStatisticsManager, DoIdleStatsUpdate ) ); + maStatsUpdateIdle.SetDebugName( "sw::DocumentStatisticsManager maStatsUpdateIdle" ); +} + +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) + { + if (!bCompleteAsync) + { + maStatsUpdateIdle.Stop(); + while (IncrementalDocStatCalculate( + std::numeric_limits::max(), bFields)) {} + } + else if (IncrementalDocStatCalculate(5000, bFields)) + maStatsUpdateIdle.Start(); + else + maStatsUpdateIdle.Stop(); + } +} + +// returns true while there is more to do +bool DocumentStatisticsManager::IncrementalDocStatCalculate(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( sal_uLong i = m_rDoc.GetNodes().Count(); i > 0 && nChars > 0; ) + { + SwNode* pNd = m_rDoc.GetNodes()[ --i ]; + switch( pNd->GetNodeType() ) + { + case SwNodeType::Text: + { + 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 vFields; + pPostits->GatherFields(vFields); + for(auto pFormatField : vFields) + { + const auto pField = static_cast(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); + sal_Int32 n=0; + aStat[n].Name = "TableCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nTable); + aStat[n].Name = "ImageCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nGrf); + aStat[n].Name = "ObjectCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nOLE); + if ( mpDocStat->nPage ) + { + aStat[n].Name = "PageCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nPage); + } + aStat[n].Name = "ParagraphCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nPara); + aStat[n].Name = "WordCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nWord); + aStat[n].Name = "CharacterCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nChar); + aStat[n].Name = "NonWhitespaceCharacterCount"; + aStat[n++].Value <<= static_cast(mpDocStat->nCharExcludingSpaces); + + // For e.g. autotext documents there is no pSwgInfo (#i79945) + SwDocShell* pObjShell(m_rDoc.GetDocShell()); + if (pObjShell) + { + const uno::Reference xDPS( + pObjShell->GetModel(), uno::UNO_QUERY_THROW); + const uno::Reference 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..b3ccc8913 --- /dev/null +++ b/sw/source/core/doc/DocumentStylePoolManager.cxx @@ -0,0 +1,2655 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +{ + static const sal_uInt16 PT_3 = 3 * 20; // 3 pt + static const sal_uInt16 PT_6 = 6 * 20; // 6 pt + static const sal_uInt16 PT_7 = 7 * 20; // 7 pt + static const sal_uInt16 PT_10 = 10 * 20; // 10 pt + static const sal_uInt16 PT_12 = 12 * 20; // 12 pt + static const sal_uInt16 PT_14 = 14 * 20; // 14 pt + static const sal_uInt16 PT_16 = 16 * 20; // 16 pt + static const sal_uInt16 PT_18 = 18 * 20; // 18 pt + static const sal_uInt16 PT_24 = 24 * 20; // 24 pt + static const sal_uInt16 PT_28 = 28 * 20; // 28 pt + + #define HTML_PARSPACE GetMetricVal( CM_05 ) + + static const sal_uInt16 aHeadlineSizes[ 2 * MAXLEVEL ] = { + // we do everything percentual now: + 130, 115, 101, 95, 85, + 85, 80, 80, 75, 75, // normal + PT_24, PT_18, PT_14, PT_12, PT_10, + PT_7, PT_7, PT_7, PT_7, PT_7 // HTML mode + }; + + 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 long nLeft = rLR.GetLeft(); + const long nRight = rLR.GetRight(); + const 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(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(0) }, + { RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_FONT, static_cast(0) }, + { RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_FONT, static_cast(0) } + }; + aArr[0].nFntType = nLatinFntType; + aArr[1].nFntType = nCJKFntType; + aArr[2].nFntType = nCTLFntType; + + for(const auto & n : aArr) + { + LanguageType nLng = static_cast(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* pDoc, 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 = pDoc->GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE); + if( bHTMLMode ) + aHItem.SetHeight( aHeadlineSizes[ MAXLEVEL + nLevel ] ); + else + aHItem.SetHeight( PT_14, 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 = pDoc->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( *pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT )); + } + + void lcl_SetRegister( SwDoc* pDoc, SfxItemSet& rSet, sal_uInt16 nFact, + bool bHeader, bool bTab ) + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + sal_uInt16 nLeft = nFact ? GetMetricVal( CM_05 ) * nFact : 0; + 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 ) + { + long nRightMargin = lcl_GetRightMargin( *pDoc ); + SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + aTStops.Insert( SvxTabStop( nRightMargin - nLeft, + SvxTabAdjust::Right, + cDfltDecimalChar, '.' )); + rSet.Put( aTStops ); + } + } + + void lcl_SetNumBul( SwDoc* pDoc, 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( *pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nNxt )); + } + + void lcl_PutStdPageSizeIntoItemSet( SwDoc* pDoc, SfxItemSet& rSet ) + { + SwPageDesc* pStdPgDsc = pDoc->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 ); + } +} + +static const char* 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 +}; + +static const char* 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 +static const char* 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_JAKETADRESS, + STR_POOLCOLL_SENDADRESS, + STR_POOLCOLL_ENDNOTE, + STR_POOLCOLL_LABEL_DRAWING +}; + +static const char* 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 +}; + +static const char* STR_POOLCOLL_DOC_ARY[] = +{ + // Category Chapter/Document + STR_POOLCOLL_DOC_TITLE, + STR_POOLCOLL_DOC_SUBTITLE, + STR_POOLCOLL_DOC_APPENDIX +}; + +static const char* 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 +}; + +static const char* 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 +}; + +static const char* STR_POOLCHR_HTML_ARY[] = +{ + STR_POOLCHR_HTML_EMPHASIS, + STR_POOLCHR_HTML_CITIATION, + 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 +}; + +static const char* STR_POOLFRM_ARY[] = +{ + STR_POOLFRM_FRAME, + STR_POOLFRM_GRAPHIC, + STR_POOLFRM_OLE, + STR_POOLFRM_FORMEL, + STR_POOLFRM_MARGINAL, + STR_POOLFRM_WATERSIGN, + STR_POOLFRM_LABEL +}; + +static const char* STR_POOLPAGE_ARY[] = +{ + // Page styles + STR_POOLPAGE_STANDARD, + STR_POOLPAGE_FIRST, + STR_POOLPAGE_LEFT, + STR_POOLPAGE_RIGHT, + STR_POOLPAGE_JAKET, + STR_POOLPAGE_REGISTER, + STR_POOLPAGE_HTML, + STR_POOLPAGE_FOOTNOTE, + STR_POOLPAGE_ENDNOTE, + STR_POOLPAGE_LANDSCAPE +}; + +static const char* STR_POOLNUMRULE_NUM_ARY[] = +{ + // Numbering styles + 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 +static const char* 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() ) + { + return pNewColl; + } + + if( pNewColl->IsAssignedToListLevelOfOutlineStyle()) + nOutLvlBits |= ( 1 << pNewColl->GetAssignedOutlineStyleLevel() ); + } + + // Didn't find it until here -> create anew + const char* pResId = nullptr; + 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( GetMetricVal( CM_05 )); + aSet.Put( aLR ); + } + break; + case RES_POOLCOLL_TEXT_NEGIDENT: // Text body neg. indentation + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + aLR.SetTextFirstLineOffset( -static_cast(GetMetricVal( CM_05 ))); + aLR.SetTextLeft( GetMetricVal( CM_1 )); + 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( GetMetricVal( CM_05 )); + aSet.Put( aLR ); + } + break; + + case RES_POOLCOLL_CONFRONTATION: // Text body confrontation + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + aLR.SetTextFirstLineOffset( - short( GetMetricVal( CM_1 ) * 4 + + GetMetricVal( CM_05)) ); + aLR.SetTextLeft( GetMetricVal( CM_1 ) * 5 ); + 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( GetMetricVal( CM_1 ) * 4 ); + 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(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: // Tabele 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 ); + + 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( -static_cast( GetMetricVal( CM_05 ) + GetMetricVal( CM_01 ) ) ); + aLR.SetTextLeft( GetMetricVal( CM_05 ) + GetMetricVal( CM_01 ) ); + 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_JAKETADRESS: // 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_SENDADRESS: // 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( GetMetricVal( CM_1 )); + aLR.SetRight( GetMetricVal( CM_1 )); + aSet.Put( aLR ); + std::unique_ptr 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 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 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 aLR(pNewColl->GetLRSpace().Clone()); + // We indent by 1 cm. The IDs are always 2 away from each other! + aLR->SetLeft( GetMetricVal( CM_1 )); + aSet.Put(std::move(aLR)); + } + break; + case RES_POOLCOLL_HTML_DT: + { + std::unique_ptr 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; + const char* pRCId = nullptr; + sal_uInt16 const * pWhichRange = nullptr; + + 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 ) ); + // i40133: patch submitted by rail: set language to 'none' to prevent spell checking: + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_LANGUAGE ) ); + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_CJK_LANGUAGE ) ); + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_CTL_LANGUAGE ) ); + } + break; + case RES_POOLCHR_INET_VISIT: + { + aSet.Put( SvxColorItem( COL_RED, RES_CHRATR_COLOR ) ); + aSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE ) ); + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_LANGUAGE ) ); + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_CJK_LANGUAGE ) ); + aSet.Put( SvxLanguageItem( LANGUAGE_NONE, RES_CHRATR_CTL_LANGUAGE ) ); + } + 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: + { + 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_CITIATION: + 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, 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, DEF_LINE_WIDTH_0 ); + 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( 114, 114, 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, + GetMetricVal( CM_1 ) * 3 + GetMetricVal( CM_05 ), + MM50 )); + } + 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(GetFormatFromPool( nId )); +} + +SwCharFormat* DocumentStylePoolManager::GetCharFormatFromPool( sal_uInt16 nId ) +{ + return static_cast(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, " - 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( GetMetricVal( CM_1 ) * 2 ); + aLR.SetRight( aLR.GetLeft() ); + } + SvxULSpaceItem aUL( RES_UL_SPACE ); + { + aUL.SetUpper( static_cast(aLR.GetLeft()) ); + aUL.SetLower( static_cast(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_JAKET: // "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( GetMetricVal( CM_1 )); + aUL.SetUpper( static_cast(aLR.GetRight()) ); + aUL.SetLower( static_cast(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 ); + aFormat.SetSuffix( "." ); + + 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) + { + 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 ); + aFormat.SetSuffix( "." ); + + 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) + { + 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 ); + aFormat.SetSuffix( "." ); + + 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) + { + 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 ) + { + 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.SetSuffix( "." ); + 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) + { + + 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.SetSuffix( "." ); + aFormat.SetNumAdjust( SvxAdjust::Right ); + + 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 ); + + 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.SetSuffix(OUString(u')')); + aFormat.SetIncludeUpperLevels( 1 ); + aFormat.SetStart( 3 ); + + 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 = GetMetricVal( CM_01 ) * 4, + nOffs2 = GetMetricVal( CM_1 ) * 2; + + if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aFormat.SetFirstLineOffset( - nOffs ); + } + else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) + { + aFormat.SetFirstLineIndent( - nOffs ); + } + + aFormat.SetSuffix( OUString() ); + for (sal_uInt16 n = 3; n < MAXLEVEL; ++n) + { + aFormat.SetStart( n+1 ); + + if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aFormat.SetAbsLSpace( nOffs2 + ((n-3) * nOffs) ); + } + else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) + { + long nPos = nOffs2 + ((n-3) * static_cast(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 = GetMetricVal( CM_01 ) * 4; + + 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 ) + { + long nPos = ((n & 1) +1) * static_cast(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 +lcl_NewUINameArray(const char** pIds, const size_t nLen, const size_t nSvxIds = 0) +{ + assert(nSvxIds <= nLen); + const size_t nWriterIds = nLen - nSvxIds; + std::vector 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& SwStyleNameMapper::GetTextUINameArray() +{ + static const std::vector s_aTextUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_TEXT_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_TEXT_ARY))); + return s_aTextUINameArray; +} + +const std::vector& SwStyleNameMapper::GetListsUINameArray() +{ + static const std::vector s_aListsUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_LISTS_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_LISTS_ARY))); + return s_aListsUINameArray; +} + +const std::vector& SwStyleNameMapper::GetExtraUINameArray() +{ + static const std::vector s_aExtraUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_EXTRA_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_EXTRA_ARY))); + return s_aExtraUINameArray; +} + +const std::vector& SwStyleNameMapper::GetRegisterUINameArray() +{ + static const std::vector s_aRegisterUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_REGISTER_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_REGISTER_ARY))); + return s_aRegisterUINameArray; +} + +const std::vector& SwStyleNameMapper::GetDocUINameArray() +{ + static const std::vector s_aDocUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_DOC_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_DOC_ARY))); + return s_aDocUINameArray; +} + +const std::vector& SwStyleNameMapper::GetHTMLUINameArray() +{ + static const std::vector s_aHTMLUINameArray( + lcl_NewUINameArray(STR_POOLCOLL_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_HTML_ARY))); + return s_aHTMLUINameArray; +} + +const std::vector& SwStyleNameMapper::GetFrameFormatUINameArray() +{ + static const std::vector s_aFrameFormatUINameArray( + lcl_NewUINameArray(STR_POOLFRM_ARY, SAL_N_ELEMENTS(STR_POOLFRM_ARY))); + return s_aFrameFormatUINameArray; +} + +const std::vector& SwStyleNameMapper::GetChrFormatUINameArray() +{ + static const std::vector s_aChrFormatUINameArray( + lcl_NewUINameArray(STR_POOLCHR_ARY, SAL_N_ELEMENTS(STR_POOLCHR_ARY))); + return s_aChrFormatUINameArray; +} + +const std::vector& SwStyleNameMapper::GetHTMLChrFormatUINameArray() +{ + static const std::vector s_aHTMLChrFormatUINameArray( + lcl_NewUINameArray(STR_POOLCHR_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCHR_HTML_ARY))); + return s_aHTMLChrFormatUINameArray; +} + +const std::vector& SwStyleNameMapper::GetPageDescUINameArray() +{ + static const std::vector s_aPageDescUINameArray( + lcl_NewUINameArray(STR_POOLPAGE_ARY, SAL_N_ELEMENTS(STR_POOLPAGE_ARY))); + return s_aPageDescUINameArray; +} + +const std::vector& SwStyleNameMapper::GetNumRuleUINameArray() +{ + static const std::vector s_aNumRuleUINameArray( + lcl_NewUINameArray(STR_POOLNUMRULE_NUM_ARY, SAL_N_ELEMENTS(STR_POOLNUMRULE_NUM_ARY))); + return s_aNumRuleUINameArray; +} + +const std::vector& SwStyleNameMapper::GetTableStyleUINameArray() +{ + static const std::vector s_aTableStyleUINameArray( + // 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(SAL_N_ELEMENTS(STR_TABSTYLE_ARY) - 1))); + return s_aTableStyleUINameArray; +} + +/* 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..842c1262d --- /dev/null +++ b/sw/source/core/doc/DocumentTimerManager.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sw +{ +DocumentTimerManager::DocumentTimerManager(SwDoc& i_rSwdoc) + : m_rDoc(i_rSwdoc) + , m_nIdleBlockCount(0) + , m_bStartOnUnblock(false) + , m_aDocIdle(i_rSwdoc) + , m_aFireIdleJobsTimer("sw::DocumentTimerManager m_aFireIdleJobsTimer") + , m_bWaitForLokInit(true) +{ + m_aDocIdle.SetPriority(TaskPriority::LOWEST); + m_aDocIdle.SetInvokeHandler(LINK(this, DocumentTimerManager, DoIdleJobs)); + m_aDocIdle.SetDebugName("sw::DocumentTimerManager m_aDocIdle"); + + 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 ); + + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter )->ModifyNotification( 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 (m_rDoc.GetEditShell()) + m_rDoc.GetEditShell()->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(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..24f51c90e --- /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 +#include +#include +#include +#include +#include + +#include +#include + +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 ) + : 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..9f2f88c6d --- /dev/null +++ b/sw/source/core/doc/SwStyleNameMapper.cxx @@ -0,0 +1,772 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#ifdef _NEED_TO_DEBUG_MAPPING +#include +#endif + +namespace +{ + +const OUString & +lcl_GetSpecialExtraName(const OUString& rExtraName, const bool bIsUIName ) +{ + const std::vector& 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 +NameToIdHash HashFromRange(sal_uInt16 nAcc, sal_uInt16 nBegin, sal_uInt16 nEnd, + const std::vector& (*pFunc)(), Rest... rest) +{ + NameToIdHash hash(HashFromRange(nAcc + nEnd - nBegin, rest...)); + sal_uInt16 nIndex, nId; + const std::vector& rStrings = pFunc(); + for (nIndex = 0, nId = nBegin; nId < nEnd; nId++, nIndex++) + hash[rStrings[nIndex]] = nId; + return hash; +} + +template struct TablePair +{ + static const NameToIdHash& getMap(bool bProgName) + { + if (bProgName) + { + static const NameToIdHash s_aProgMap(initFunc(true)); + return s_aProgMap; + } + static const NameToIdHash s_aUIMap(initFunc(false)); + return s_aUIMap; + } +}; + +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::getMap(bProgName); + case SwGetPoolIdFromName::ChrFmt: + return TablePair::getMap(bProgName); + case SwGetPoolIdFromName::FrmFmt: + return TablePair::getMap(bProgName); + case SwGetPoolIdFromName::PageDesc: + return TablePair::getMap(bProgName); + case SwGetPoolIdFromName::NumRule: + return TablePair::getMap(bProgName); + case SwGetPoolIdFromName::TabStyle: + return TablePair::getMap(bProgName); + case SwGetPoolIdFromName::CellStyle: + return TablePair::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* 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& SwStyleNameMapper::GetCellStyleUINameArray() +{ + static const std::vector s_aCellStyleUINameArray; + return s_aCellStyleUINameArray; +} + +const std::vector& SwStyleNameMapper::GetTextProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetListsProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetExtraProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetRegisterProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetDocProgNameArray() +{ + static const std::vector s_aDocProgNameArray = { + "Title", // STR_POCO_PRGM_DOC_TITLE + "Subtitle", + "Appendix", + }; + return s_aDocProgNameArray; +} + +const std::vector& SwStyleNameMapper::GetHTMLProgNameArray() +{ + static const std::vector s_aHTMLProgNameArray = { + "Quotations", + "Preformatted Text", + "Horizontal Line", + "List Contents", + "List Heading", // STR_POCO_PRGM_HTML_DT + }; + return s_aHTMLProgNameArray; +} + +const std::vector& SwStyleNameMapper::GetFrameFormatProgNameArray() +{ + static const std::vector s_aFrameFormatProgNameArray = { + "Frame", // RES_POOLFRM_FRAME + "Graphics", + "OLE", + "Formula", + "Marginalia", + "Watermark", + "Labels", // RES_POOLFRM_LABEL + }; + return s_aFrameFormatProgNameArray; +} + +const std::vector& SwStyleNameMapper::GetChrFormatProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetHTMLChrFormatProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetPageDescProgNameArray() +{ + static const std::vector 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& SwStyleNameMapper::GetNumRuleProgNameArray() +{ + static const std::vector s_aNumRuleProgNameArray = { + "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& 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 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& SwStyleNameMapper::GetCellStyleProgNameArray() +{ + static const std::vector 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..b256c5065 --- /dev/null +++ b/sw/source/core/doc/acmplwrd.cxx @@ -0,0 +1,402 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +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 Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override; +}; + +class SwAutoCompleteWord_Impl +{ + std::vector + 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 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *) +{ + switch( pOld ? pOld->Which() : 0 ) + { + case RES_REMOVE_UNO_OBJECT: + case RES_OBJECTDYING: + if( static_cast(GetRegisteredIn()) == static_cast(pOld)->pObject ) + EndListeningAll(); + m_pAutoCompleteWord->DocumentDying(*m_pDoc); + break; + } +} + +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 + 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(*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()) + { + editeng::SortedAutoCompleteStrings::const_iterator 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 - m_WordList.begin()); + 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(m_WordList[nPos]); + m_WordList.erase(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& 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(m_WordList[nMyPos]); + m_WordList.erase(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 ) + { + // clear LRU array first then delete the string object + for( ; nNewPos < nMyLen; ++nNewPos ) + { + SwAutoCompleteString *const pDel = + dynamic_cast(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(m_WordList[nPos - 1]); + if(pCurrent && pCurrent->RemoveDocument(rDoc) && bDelete) + { + m_WordList.erase(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..3fb9c460b --- /dev/null +++ b/sw/source/core/doc/dbgoutsw.cxx @@ -0,0 +1,846 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static OString aDbgOutResult; +bool bDbgOutStdErr = false; +bool bDbgOutPrintAttrSet = false; + +template +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(const OUString & aStr) +{ + aDbgOutResult = OUStringToOString(aStr, RTL_TEXTENCODING_ASCII_US); + + if (bDbgOutStdErr) + fprintf(stderr, "%s", aDbgOutResult.getStr()); + + return aDbgOutResult.getStr(); +} + +static map & GetItemWhichMap() +{ + static map 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_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_DUMMY3 , "TXTATR_DUMMY3" }, + { RES_TXTATR_DUMMY1 , "TXTATR_DUMMY1" }, + { RES_TXTATR_DUMMY2 , "TXTATR_DUMMY2" }, + { 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(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(rPos.nNode.GetIndex()) + + ", " + + OUString::number(rPos.nContent.GetIndex()) + + ": " + + OUString::number(reinterpret_cast(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 * pDoc = rNode.GetDoc(); + if (pDoc) + { + const SwFrameFormats * pFrameFormats = pDoc->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 = ""; + + const SwTextNode * pTextNode = rNode.GetTextNode(); + + if (rNode.IsTextNode()) + { + const SfxItemSet * pAttrSet = pTextNode->GetpSwAttrSet(); + + aTmpStr += "" + (pTextNode->GetText().getLength() > 10 ? pTextNode->GetText().copy(0, 10) : pTextNode->GetText()) + ""; + + if (rNode.IsTableNode()) + aTmpStr += ""; + + aTmpStr += "" + OUString::number(pTextNode->GetAttrOutlineLevel()-1) + ""; + + const SwNumRule * pNumRule = pTextNode->GetNumRule(); + + if (pNumRule != nullptr) + { + aTmpStr += ""; + if ( pTextNode->GetNum() ) + { + aTmpStr += lcl_dbg_out(*(pTextNode->GetNum())); + } + aTmpStr += "" + + pNumRule->GetName(); + + const SfxPoolItem * pItem = nullptr; + + if (pAttrSet && SfxItemState::SET == + pAttrSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem)) + { + aTmpStr += "(" + + static_cast(pItem)->GetValue() + ")*"; + } + + const SwNumFormat * pNumFormat = nullptr; + aTmpStr += ""; + + if (pTextNode->GetActualListLevel() > 0) + pNumFormat = pNumRule->GetNumFormat( static_cast< sal_uInt16 >(pTextNode->GetActualListLevel()) ); + + if (pNumFormat) + { + aTmpStr += "" + + lcl_dbg_out_NumType(pNumFormat->GetNumberingType()) + ""; + } + } + + if (pTextNode->IsCountedInList()) + aTmpStr += ""; + + SwFormatColl * pColl = pTextNode->GetFormatColl(); + + if (pColl) + { + aTmpStr += "" + pColl->GetName() + "("; + + SwTextFormatColl *pTextColl = static_cast(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 += ")" + ""; + } + + SwFormatColl * pCColl = pTextNode->GetCondFormatColl(); + + if (pCColl) + { + aTmpStr += "" + pCColl->GetName() + ""; + } + + aTmpStr += "" + lcl_AnchoredFrames(rNode) + ""; + + if (bDbgOutPrintAttrSet) + { + aTmpStr += "" + lcl_dbg_out(pTextNode->GetSwAttrSet()) + ""; + } + } + else if (rNode.IsStartNode()) + { + aTmpStr += " (&rNode); + if (pStartNode != nullptr) + aTmpStr += OUString::number(pStartNode->EndOfSectionNode()->GetIndex()); + + aTmpStr += "\"/>"; + } + else if (rNode.IsEndNode()) + aTmpStr += ""; + + aTmpStr += ""; + + 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(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(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..b850b2c8a --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* @@@MAINTAINABILITY-HORROR@@@ + Probably unwanted dependency on SwDocShell +*/ +#include + +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 std::set< sal_Int32 > &rPossiblePages, + sal_uInt16& rVirtPgNo, sal_uInt16& rLineNo ); + + const SwPostItField* GetPostIt() const + { + return static_cast( GetTextField()->GetFormatField().GetField() ); + } +}; + +} + +sal_uInt16 PostItField_::GetPageNo( + const StringRangeEnumerator &rRangeEnum, + const std::set< 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 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 = static_cast(pFrame->GetLineCount( nPos ) + + pFrame->GetAllLines() - pFrame->GetThisLines()); + rVirtPgNo = pFrame->GetVirtPageNum(); + return nPgNo; + } + } + return 0; +} + +bool sw_GetPostIts(IDocumentFieldsAccess const* pIDFA, SetGetExpFields* pSrtLst) +{ + SwFieldType* pFieldType = pIDFA->GetSysFieldType(SwFieldIds::Postit); + assert(pFieldType); + + std::vector vFields; + pFieldType->GatherFields(vFields); + if(pSrtLst) + for(auto pField: vFields) + { + auto pTextField = pField->GetTextField(); + SwNodeIndex aIdx(pTextField->GetTextNode()); + std::unique_ptr 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; + aStr += 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( rLayout.Lower() ); + while (pStPage && nPageNum <= nDocPageCountWithBlank) + { + if ( pStPage->getFrameArea().Height() == 0 ) + { + --nDocPageCount; + if (nPageNum <= nActualPageWithBlank) + --nActualPage; + } + ++nPageNum; + pStPage = static_cast(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(); + std::set< 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( 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(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( 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; + + SET_CURR_SHELL( 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(*(*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(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(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::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(); + std::set< 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( rLayout.Lower() ); + for ( sal_Int32 i = 1; pStPage && i < nDocPageCount; ++i ) + pStPage = static_cast(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( rLayout.Lower() ); + while( pPageFrame && nPageNum < nDocPageCount ) + { + ++nPageNum; + rValidPagesSet.insert( nPageNum ); + validStartFrames[ nPageNum ] = pPageFrame; + pPageFrame = static_cast(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( const OUString& rName ) const +{ + for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK)) + { + auto pFormatRef = dynamic_cast(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(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* pNames ) const +{ + sal_uInt16 nCount = 0; + for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK)) + { + auto pRefMark = dynamic_cast(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( const SwNodePtr& rpNd, void* pArgs ) +{ + SwTextNode *pTextNode = rpNd->GetTextNode(); + bool bOnlyWrong = *static_cast(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( const SwNodePtr& rpNd, void* ) +{ + SwTextNode *pTextNode = rpNd->GetTextNode(); + if( pTextNode ) + { + pTextNode->SetSmartTagDirty( true ); + if( pTextNode->GetSmartTags() ) + { + pTextNode->SetSmartTags( nullptr ); + } + } + return true; +} + +/** + * Re-trigger spelling in the idle handler. + * + * @param bInvalid if , 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 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 ) + { + o3tl::sorted_vector aAllLayouts = GetAllLayouts(); + for( auto aLayout : aAllLayouts ) + aLayout->AllInvalidateAutoCompleteWords(); + for( sal_uLong 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( const OUString& rName ) const +{ + for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT)) + { + auto pFormatItem = dynamic_cast(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* pExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bImpress ) +{ + const SwOutlineNodes& rOutNds = GetNodes().GetOutLineNds(); + if( pExtDoc && !rOutNds.empty() ) + { + ::StartProgress( STR_STATSTR_SUMMARY, 0, rOutNds.size(), GetDocShell() ); + SwNodeIndex aEndOfDoc( pExtDoc->GetNodes().GetEndOfContent(), -1 ); + for( SwOutlineNodes::size_type i = 0; i < rOutNds.size(); ++i ) + { + ::SetProgressState( static_cast(i), GetDocShell() ); + const sal_uLong nIndex = rOutNds[ i ]->GetIndex(); + + const int nLvl = GetNodes()[ nIndex ]->GetTextNode()->GetAttrOutlineLevel()-1; + if( nLvl > nLevel ) + continue; + long nEndOfs = 1; + sal_uInt8 nWish = nPara; + sal_uLong 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 ], 0, *rOutNds[ i ], nEndOfs ); + GetNodes().Copy_( aRange, aEndOfDoc ); + } + const SwTextFormatColls *pColl = pExtDoc->GetTextFormatColls(); + for( SwTextFormatColls::size_type i = 0; i < pColl->size(); ++i ) + (*pColl)[ i ]->ResetFormatAttr( RES_PAGEDESC, RES_BREAK ); + SwNodeIndex aIndx( pExtDoc->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 = static_cast( + !pMyColl->IsAssignedToListLevelOfOutlineStyle() + ? RES_POOLCOLL_HEADLINE2 + : RES_POOLCOLL_HEADLINE1 ); + pMyColl = pExtDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nHeadLine ); + pNd->ChgFormatColl( pMyColl ); + } + if( !pNd->Len() && + pNd->StartOfSectionIndex()+2 < pNd->EndOfSectionIndex() ) + { + bDelete = true; + pExtDoc->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 ((2 == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex()) + || (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(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(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> aHidingFieldTypes; + for (std::unique_ptr const & pType : *getIDocumentFieldsAccess().GetFieldTypes()) + { + if (FieldCanHideParaWeight(pType->Which())) + aHidingFieldTypes.push_back(std::make_unique(pType.get())); + } + for (const auto& pTypeGuard : aHidingFieldTypes) + { + if (const SwFieldType* pType = pTypeGuard->get()) + { + std::vector vFields; + pType->GatherFields(vFields); + for(auto pFormatField: vFields) + bRet |= HandleHidingField(*pFormatField, GetNodes(), getIDocumentContentOperations()); + } + } + } + + // Remove any hidden paragraph (hidden text attribute) + for( sal_uLong n = GetNodes().Count(); n; ) + { + SwTextNode* pTextNd = GetNodes()[ --n ]->GetTextNode(); + if ( pTextNd ) + { + bool bRemoved = false; + if ( pTextNd->HasHiddenCharAttribute( true ) ) + { + bRemoved = true; + bRet = true; + + if (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 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 vFields; + getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara)->GatherFields(vFields); + if(vFields.size()) + return true; + + // Search for any hidden paragraph (hidden text attribute) + for( sal_uLong n = GetNodes().Count()-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 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(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 pUndo(new SwUndoCpyTable(this)); + pUndo->SetTableSttIdx( pTableNd->GetIndex() ); + GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); + } + } + else if( rPam.HasMark() ) + { + std::unique_ptr pUndo(new SwUndoCpyDoc( rPam )); + pUndo->SetInsertRange( rPam, false ); + GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); + } +} + +void SwDoc::ChangeTOX(SwTOXBase & rTOX, const SwTOXBase & rNew) +{ + assert(dynamic_cast(&rTOX)); + SwTOXBaseSection& rTOXSect(static_cast(rTOX)); + + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().AppendUndo( + std::make_unique(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( sal_uLong n = GetNodes().Count(); n; ) + { + SwNode* pNd = GetNodes()[ --n ]; + if ( pNd->IsTextNode() && pNd->GetTextNode()->HasHiddenCharAttribute( false ) ) + return true; + } + + return false; +} + +std::shared_ptr SwDoc::CreateUnoCursor( const SwPosition& rPos, bool bTableCursor ) +{ + std::shared_ptr pNew; + if( bTableCursor ) + pNew = std::make_shared(rPos); + else + pNew = std::make_shared(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(1); + aArgs[0] <<= 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; +}; + + +/* 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..4fb0db964 --- /dev/null +++ b/sw/source/core/doc/docbasic.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; + +static Sequence *lcl_docbasic_convertArgs( SbxArray& rArgs ) +{ + Sequence *pRet = nullptr; + + sal_uInt32 nCount = rArgs.Count32(); + if( nCount > 1 ) + { + nCount--; + pRet = new Sequence( nCount ); + Any *pUnoArgs = pRet->getArray(); + for( sal_uInt32 i=0; iGetType() ) + { + case SbxSTRING: + pUnoArgs[i] <<= pVar->GetOUString(); + break; + case SbxCHAR: + pUnoArgs[i] <<= static_cast(pVar->GetChar()) ; + break; + case SbxUSHORT: + pUnoArgs[i] <<= static_cast(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 > 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(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() ) + { + std::unique_ptr > pUnoArgs(new Sequence()); + + 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(), *pUnoArgs,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..c76e4417c --- /dev/null +++ b/sw/source/core/doc/docbm.cxx @@ -0,0 +1,1862 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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(new std::vector<::sw::mark::MarkBase*>::const_iterator(rIter)) +{ +} + +IDocumentMarkAccess::iterator::iterator(iterator const& rOther) + : m_pIter(new std::vector<::sw::mark::MarkBase*>::const_iterator(*rOther.m_pIter)) +{ +} + +auto IDocumentMarkAccess::iterator::operator=(iterator const& rOther) -> iterator& +{ + m_pIter.reset(new std::vector<::sw::mark::MarkBase*>::const_iterator(*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; +} + +IDocumentMarkAccess::iterator::~iterator() +{ +} + +// ARGH why does it *need* to return const& ? +::sw::mark::IMark* /*const&*/ +IDocumentMarkAccess::iterator::operator*() const +{ + return static_cast(**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(new std::vector<::sw::mark::MarkBase*>::const_iterator()) +{ +} + +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((*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 lcl_PositionFromContentNode( + SwContentNode * const pContentNode, + const bool bAtEnd) + { + std::unique_ptr 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 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(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", + pStPos->nNode.GetIndex() << "," << + pStPos->nContent.GetIndex() << " " << + 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_vAllMarks() + , m_vBookmarks() + , m_vFieldmarks() + , m_vAnnotationMarks() + , m_pDoc(&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 << " " << + pPos1->nNode.GetIndex() << "," << + pPos1->nContent.GetIndex() << " " << + 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(rPaM, rName); + break; + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::BOOKMARK: + pMark = std::make_unique(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + pMark = std::make_unique(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + pMark = std::make_unique(rPaM, vcl::KeyCode(), rName); + break; + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + pMark = std::make_unique(rPaM); + break; + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + pMark = std::make_unique( 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_pDoc, 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; + } + 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_pDoc->GetIDocumentUndoRedo().DoesUndo(); + m_pDoc->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( pMark ); + if (pFieldMark) + pFieldMark->SetFieldname( rType ); + + if (bUndoIsEnabled) + { + m_pDoc->GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); + if (pFieldMark) + m_pDoc->GetIDocumentUndoRedo().AppendUndo(std::make_unique(*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_pDoc->GetIDocumentUndoRedo().DoesUndo(); + m_pDoc->GetIDocumentUndoRedo().DoUndo(false); + + bool bEnableSetModified = m_pDoc->getIDocumentState().IsEnableSetModified(); + m_pDoc->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( pMark ); + if (pFieldMark) + pFieldMark->SetFieldname( rType ); + + if (bUndoIsEnabled) + { + m_pDoc->GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); + if (pFieldMark) + m_pDoc->GetIDocumentUndoRedo().AppendUndo(std::make_unique(*pFieldMark)); + } + + m_pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified); + m_pDoc->getIDocumentState().SetModified(); + + return pFieldMark; + } + + ::sw::mark::IMark* MarkManager::getMarkForTextNode( + const SwTextNode& rTextNode, + const IDocumentMarkAccess::MarkType eType ) + { + SwPosition aPos(rTextNode); + aPos.nContent.Assign(&const_cast(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_pDoc && + "" + " - 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_pDoc && + "" + " - 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_pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + m_pDoc->GetIDocumentUndoRedo().AppendUndo( + std::make_unique(sOldName, rNewName, m_pDoc)); + } + m_pDoc->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* pSaveBkmk, + const SwIndex* pSttIdx, + const SwIndex* pEndIdx ) + { + std::vector 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 > 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)); + } + } // 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 m_pFieldmark; + SwDoc * m_pDoc; + LazyFieldmarkDeleter(Fieldmark* pMark, SwDoc *const pDoc) + : m_pFieldmark(pMark), m_pDoc(pDoc) + { + 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*! + m_pFieldmark->ReleaseDoc(m_pDoc); + } + }; + + } + + std::unique_ptr + MarkManager::deleteMark(const const_iterator_t& ppMark) + { + std::unique_ptr 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 && + " - 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(pMark), m_pDoc)); + } + else + { + assert(false && + " - 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() && + " - 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; + } + DdeBookmark* const pDdeBookmark = dynamic_cast(pMark); + if (pDdeBookmark) + pDdeBookmark->DeregisterFromDoc(m_pDoc); + //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(aI, ppMark.get())); + + 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_pDoc && + "" + " - 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)); + 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(); } + + // 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(*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).IsCoveringPosition(rPos); ++itFieldmark) + { // find the innermost fieldmark + if (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart() + || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd()) + { + pFieldmark = *itFieldmark; + } + } + return dynamic_cast(pFieldmark); + } + + void MarkManager::deleteFieldmarkAt(const SwPosition& rPos) + { + auto const pFieldmark = dynamic_cast(getFieldmarkAt(rPos)); + if (!pFieldmark) + return; + + deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark)); + } + + ::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 aPaM(pFieldmark->GetMarkPos()); + + // Remove the old fieldmark and create a new one with the new type + if(aPaM.GetPoint()->nContent > 0 && (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)) + { + --aPaM.GetPoint()->nContent; + 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(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(*pFieldBM); + pNewActiveFieldmark = &rFormField; + } + else + { + pNewActiveFieldmark = m_pLastActiveFieldmark; + } + } + + if(pNewActiveFieldmark != m_pLastActiveFieldmark) + { + ClearFieldActivation(); + m_pLastActiveFieldmark = pNewActiveFieldmark; + if(pNewActiveFieldmark) + pNewActiveFieldmark->ShowButton(&rEditWin); + } + } + + void MarkManager::ClearFieldActivation() + { + if(m_pLastActiveFieldmark) + m_pLastActiveFieldmark->RemoveButton(); + + m_pLastActiveFieldmark = nullptr; + } + + IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const + { + IFieldmark *pMark = getFieldmarkAt(rPos); + if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN) + return nullptr; + return pMark; + } + + std::vector MarkManager::getDropDownsFor(const SwPaM &rPaM) const + { + std::vector 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(pI); + if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN) + continue; + + aRet.push_back(pMark); + } + + return aRet; + } + + IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos) const + { return dynamic_cast(lcl_getMarkAfter(m_vFieldmarks, rPos)); } + + IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos) const + { return dynamic_cast(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()); + } + + OUString MarkManager::getUniqueMarkName(const OUString& rName) const + { + OSL_ENSURE(rName.getLength(), + " - a name should be proposed"); + if( m_pDoc->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 "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; + while(nCnt < SAL_MAX_INT32) + { + sTmp = rName + 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} + }; + + xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager")); + for (const auto & rContainer : aContainers) + { + if (!rContainer.pContainer->empty()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName)); + for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it) + (*it)->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + } + 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_aShortName() + , m_aCode() + , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk)) +{ + const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk); + if(pBookmark) + { + m_aShortName = pBookmark->GetShortName(); + m_aCode = pBookmark->GetKeyCode(); + + ::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 = ULONG_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(ULONG_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)) + { + ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>( + pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName, + m_eOrigBkmType, sw::mark::InsertMode::New)); + if(pBookmark) + { + pBookmark->SetKeyCode(m_aCode); + pBookmark->SetShortName(m_aShortName); + 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 * pSaveBkmk, + const SwIndex* pSttIdx, + const SwIndex* pEndIdx) +{ + // illegal range ?? + if(rStt.GetIndex() > rEnd.GetIndex() + || (rStt == rEnd && (!pSttIdx || !pEndIdx || pSttIdx->GetIndex() >= pEndIdx->GetIndex()))) + return; + SwDoc* const pDoc = rStt.GetNode().GetDoc(); + + pDoc->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 = pDoc->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 = pDoc->GetNodes().GoNext( &pRStt->nNode ); + if (!pCNd) + { + bStt = false; + pRStt->nNode = rStt; + if( nullptr == ( pCNd = SwNodes::GoPrevious( &pRStt->nNode )) ) + { + 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; + if( nullptr == ( pCNd = pDoc->GetNodes().GoNext( &pREnd->nNode )) ) + { + pREnd->nNode = pRStt->nNode; + pCNd = pREnd->nNode.GetNode().GetContentNode(); + } + } + pREnd->nContent.Assign( pCNd, bStt ? 0 : pCNd->Len() ); + } + } + } +} + +/* 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..4acfb5119 --- /dev/null +++ b/sw/source/core/doc/docchart.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ) + { + 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() ) ) + { + SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD) + pPCD->InvalidateTable( &rTable ); + // 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..1fe455f50 --- /dev/null +++ b/sw/source/core/doc/doccomp.cxx @@ -0,0 +1,2698 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +using std::vector; + +namespace { + +class SwCompareLine +{ + const SwNode& m_rNode; +public: + explicit SwCompareLine( const SwNode& rNd ) : m_rNode( rNd ) {} + + 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& rpInsRing, std::unique_ptr& rpDelRing ) const; + + const SwNode& GetNode() const { return m_rNode; } + + const SwNode& GetEndNode() const; + + // for debugging + OUString GetText() const; +}; + + +class CompareData +{ +protected: + SwDoc& m_rDoc; +private: + std::unique_ptr m_pIndex; + std::unique_ptr m_pChangedFlag; + + std::unique_ptr m_pInsertRing, m_pDelRing; + + static sal_uLong PrevIdx( const SwNode* pNd ); + static sal_uLong 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* pLine ) + { m_aLines.push_back( pLine ); } + + 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; + const SwCompareLine* pLine; + + HashData() + : nNext( 0 ), nHash( 0 ), pLine(nullptr) {} + }; + + std::unique_ptr m_pHashArr; + std::unique_ptr 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 m_pIndex; + std::unique_ptr 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 m_pMemory; + 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 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 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 m_pL1, m_pL2; + std::unique_ptr 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_pDataArr[0].pLine = nullptr; + 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 ) + { + for( size_t n = 0; n < rData.GetLineCount(); ++n ) + { + const SwCompareLine* pLine = rData.GetLine( n ); + OSL_ENSURE( pLine, "where is the line?" ); + sal_uLong nH = pLine->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].pLine = pLine; + *pFound = i; + break; + } + else if( m_pDataArr[i].nHash == nH && + m_pDataArr[i].pLine->Compare( *pLine )) + break; + + rData.SetIndex( n, i ); + } + } +} + +Compare::Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 ) +{ + std::unique_ptr pMD1, pMD2; + // Look for the differing lines + { + std::unique_ptr pDiscard1( new char[ rData1.GetLineCount() ] ); + std::unique_ptr pDiscard2( new char[ rData2.GetLineCount() ] ); + + std::unique_ptr pCount1(new sal_uLong[ nDiff ]); + std::unique_ptr 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 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[ 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 long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */ + const long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */ + const long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */ + const long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */ + + long fmin = fmid, fmax = fmid; /* Limits of top-down search. */ + long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */ + + long c; /* Cost. */ + 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) + { + 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) + { + 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) + { + 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_rNode.GetNodeType() ) + { + case SwNodeType::Text: + nRet = GetTextNodeHashValue( *m_rNode.GetTextNode(), nRet ); + break; + + case SwNodeType::Table: + { + const SwNode* pEndNd = m_rNode.EndOfSectionNode(); + SwNodeIndex aIdx( m_rNode ); + 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_rNode; + switch( m_rNode.GetNodeType() ) + { + case SwNodeType::Table: + pNd = m_rNode.EndOfSectionNode(); + break; + + case SwNodeType::Section: + { + const SwSectionNode& rSNd = static_cast(m_rNode); + const SwSection& rSect = rSNd.GetSection(); + if( SectionType::Content != rSect.GetType() || rSect.IsProtect() ) + pNd = m_rNode.EndOfSectionNode(); + } + break; + default: break; + } + return *pNd; +} + +bool SwCompareLine::Compare( const SwCompareLine& rLine ) const +{ + return CompareNode( m_rNode, rLine.m_rNode ); +} + +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(rSrcNd); + const SwTableNode& rTDstNd = static_cast(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(rSrcNd), + & rSDstNd = static_cast(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_rNode.GetNodeType() ) + { + case SwNodeType::Text: + sRet = m_rNode.GetTextNode()->GetExpandText(nullptr); + break; + + case SwNodeType::Table: + { + sRet = "Tabelle: " + SimpleTableToText(m_rNode); + } + break; + + case SwNodeType::Section: + { + sRet = "Section - Node:"; + + const SwSectionNode& rSNd = static_cast(m_rNode); + const SwSection& rSect = rSNd.GetSection(); + switch( rSect.GetType() ) + { + case SectionType::Content: + if( rSect.IsProtect() ) + sRet += OUString::number( + 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& rpInsRing, std::unique_ptr& rpDelRing ) const +{ + bool bRet = false; + + // Only compare textnodes + if( SwNodeType::Text == m_rNode.GetNodeType() && + SwNodeType::Text == rLine.GetNode().GetNodeType() ) + { + SwTextNode& rDstNd = *const_cast(m_rNode.GetTextNode()); + const SwTextNode& rSrcNd = *rLine.GetNode().GetTextNode(); + SwDoc* pDstDoc = 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 aLcsDst( nMinLen + 1 ); + std::vector aLcsSrc( nMinLen + 1 ); + + if( CmpOptions.eCmpMode == SwCompareMode::ByWord ) + { + std::vector aTmpLcsDst( nMinLen + 1 ); + std::vector 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 = pDstDoc->GetIDocumentUndoRedo().DoesUndo(); + pDstDoc->GetIDocumentUndoRedo().DoUndo( false ); + SwPaM aCpyPam( rSrcNd, nSrcFrom ); + aCpyPam.SetMark(); + aCpyPam.GetPoint()->nContent = nSrcTo; + aCpyPam.GetDoc()->getIDocumentContentOperations().CopyRange( aCpyPam, *aPam.GetPoint(), + SwCopyFlags::CheckPosInFly); + pDstDoc->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; +} + +sal_uLong 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; +} + +sal_uLong 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(); + + sal_uLong nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() ); + sal_uLong nSrcEndIdx = rSrcEndNd.GetIndex(); + + sal_uLong nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() ); + sal_uLong 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( new SwCompareLine( *pNd ) ); + nSrcSttIdx = NextIdx( pNd ); + } + + while( nDstSttIdx <= nDstEndIdx ) + { + const SwNode* pNd = rDstNds[ nDstSttIdx ]; + InsertLine( new 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(), 0, + rData.GetLine( nEnd-1 )->GetEndNode(), 1 ); + + sal_uInt16 nOffset = 0; + const SwCompareLine* pLine = nullptr; + if( nInsPos >= 1 ) + { + if( GetLineCount() == nInsPos ) + { + pLine = GetLine( nInsPos-1 ); + nOffset = 1; + } + else + pLine = GetLine( nInsPos ); + } + + const SwNode* pLineNd; + if( pLine ) + { + if( nOffset ) + pLineNd = &pLine->GetEndNode(); + else + pLineNd = &pLine->GetNode(); + } + else + { + pLineNd = &GetEndOfContent(); + nOffset = 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(), 0, -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 pLcsDst(new int[ nMinLen ]); + std::unique_ptr 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* pDstLn = GetLine( nThisStt + nDstFrom - 1 ); + const SwCompareLine* pSrcLn = rData.GetLine( nStt + nSrcFrom - 1 ); + + // Show differences in detail for lines that + // were matched as only slightly different + if( !pDstLn->ChangesInLine( *pSrcLn, 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 xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference 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( *pTmp, false )); + } + m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( aRedlnData, *pTmp ), true ); + + } while( m_pDelRing.get() != ( pTmp = pTmp->GetNext()) ); + } + + pTmp = m_pInsertRing.get(); + if( pTmp ) + { + 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( *pTmp, true )); + } + } while( m_pInsertRing.get() != ( pTmp = pTmp->GetNext()) ); + } +} + +typedef std::shared_ptr CompareDataPtr; +typedef std::pair CompareDataPtrPair; +typedef std::vector Comparators; + +namespace +{ + Comparators buildComparators(SwDoc &rSrcDoc, SwDoc &rDestDoc) + { + Comparators aComparisons; + //compare main text + aComparisons.emplace_back(std::make_shared(rSrcDoc, true), + std::make_shared(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(rSrcDoc, *pSrcIdx), + std::make_shared(rDestDoc, *pDestIdx)); + } + } + return aComparisons; + } +} + +// Returns (the difference count?) if something is different +long SwDoc::CompareDoc( const SwDoc& rDoc ) +{ + if( &rDoc == this ) + return 0; + + 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(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(static_cast(&rDstNd)), pStt->nContent.GetIndex() ); + pDestRedl = new SwRangeRedline( rSrcRedl.GetRedlineData(), aPos ); + + if( RedlineType::Delete == pDestRedl->GetType() ) + { + // mark the area as deleted + const SwPosition* pEnd = pStt == rSrcRedl.GetPoint() + ? rSrcRedl.GetMark() + : rSrcRedl.GetPoint(); + + 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* pDoc = pDestRedl->GetDoc(); + + if( RedlineType::Insert == pDestRedl->GetType() ) + { + // the part was inserted so copy it from the SourceDoc + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 ); + const sal_Int32 nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex(); + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore); + + pSrcRedl->GetDoc()->getIDocumentContentOperations().CopyRange( + *const_cast(static_cast(pSrcRedl)), + *pDestRedl->GetPoint(), SwCopyFlags::CheckPosInFly); + + pDoc->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( !pDoc->getIDocumentRedlineAccess().GetRedline( *pDStt, &n ) && n ) + --n; + + const SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + for( ; n < rRedlineTable.size(); ++n ) + { + SwRangeRedline* pRedl = rRedlineTable[ n ]; + SwPosition* pRStt = pRedl->Start(), + * pREnd = pRStt == pRedl->GetPoint() ? pRedl->GetMark() + : pRedl->GetPoint(); + 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 pUndo; + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + pUndo.reset(new SwUndoCompDoc( *pCpyRedl )); + + // now modify doc: append redline, undo (and count) + pDoc->getIDocumentRedlineAccess().AppendRedline( pCpyRedl, true ); + if( pUndo ) + { + pDoc->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 pUndo; + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + pUndo.reset(new SwUndoCompDoc( *pDestRedl )); + + // now modify doc: append redline, undo (and count) + IDocumentRedlineAccess::AppendResult const result( + pDoc->getIDocumentRedlineAccess().AppendRedline(pDestRedl, true)); + if( pUndo ) + { + pDoc->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 +long SwDoc::MergeDoc( const SwDoc& rDoc ) +{ + if( &rDoc == this ) + return 0; + + long nRet = 0; + + GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + + SwDoc& rSrcDoc = const_cast(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 vRedlines; + const SwRedlineTable& rSrcRedlTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable(); + sal_uLong nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex(); + sal_uLong nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex(); + for(const SwRangeRedline* pRedl : rSrcRedlTable) + { + sal_uLong 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( 3, std::min( nPar1Len, nPar2Len ) ); + } + + std::set 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 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..41f7b673d --- /dev/null +++ b/sw/source/core/doc/doccorr.cxx @@ -0,0 +1,364 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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.StartOfSectionNode(); + while( ( pStartNode != nullptr ) && + ( pStartNode->StartOfSectionNode() != pStartNode ) && + ( pStartNode->GetStartNodeType() == SwNormalStartNode ) ) + 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(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 *const pDoc = aStart.nNode.GetNode().GetDoc(); + SwCursorShell *const pShell = pDoc->GetEditShell(); + + if( pShell ) + { + for(const SwViewShell& rShell : pShell->GetRingContainer()) + { + if(dynamic_cast(&rShell) == nullptr) + continue; + const SwCursorShell* pCursorShell = static_cast(&rShell); + 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(pCursorShell->GetCursor_())->GetRingContainer()) + { + lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos ); + } + + if( pCursorShell->IsTableMode() ) + lcl_PaMCorrAbs( const_cast(*pCursorShell->GetTableCrs()), aStart, aEnd, aNewPos ); + } + } + + pDoc->cleanupUnoCursorTable(); + for(const auto& pWeakUnoCursor : pDoc->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(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* pDoc = pOldNode->GetDoc(); + + const sal_Int32 nCntIdx = rNewPos.nContent.GetIndex() + nOffset; + + SwCursorShell const* pShell = pDoc->GetEditShell(); + if( pShell ) + { + for(const SwViewShell& rShell : pShell->GetRingContainer()) + { + if(dynamic_cast(&rShell) == nullptr) + continue; + SwCursorShell* pCursorShell = const_cast(static_cast(&rShell)); + 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 ); + } + } + + pDoc->cleanupUnoCursorTable(); + for(const auto& pWeakUnoCursor : pDoc->mvUnoCursorTable) + { + auto pUnoCursor(pWeakUnoCursor.lock()); + if(!pUnoCursor) + continue; + for(SwPaM& rPaM : pUnoCursor->GetRingContainer()) + { + lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx ); + } + + SwUnoTableCursor* pUnoTableCursor = + dynamic_cast(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( dynamic_cast(&rCurrentSh) != nullptr ) + { + return static_cast(&rCurrentSh); + } + } + } + return nullptr; +} + +SwEditShell* SwDoc::GetEditShell() +{ + return const_cast( const_cast( 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..51dc930f0 --- /dev/null +++ b/sw/source/core/doc/docdesc.cxx @@ -0,0 +1,922 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 = GetMetricVal( CM_1 ); + nMinLeft = nMinRight * 2; + } + else if (!utl::ConfigManager::IsFuzzing() && MeasurementSystem::Metric == SvtSysLocale().GetLocaleData().getMeasurementSystemEnum() ) + { + nMinTop = nMinBottom = nMinLeft = nMinRight = 1134; // 2 centimeters + } + else + { + nMinTop = nMinBottom = 1440; // as in MS Word: 1 Inch + nMinLeft = nMinRight = 1800; // 1,25 Inch + } + + // set margins + SvxLRSpaceItem aLR( RES_LR_SPACE ); + SvxULSpaceItem aUL( RES_UL_SPACE ); + + aUL.SetUpper( static_cast(nMinTop) ); + aUL.SetLower( static_cast(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_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 from RES_COL and RES_PAPER_BIN: + case RES_COL: + case RES_PAPER_BIN: + 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(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 SwFrameFormat *pRight = rHead.GetHeaderFormat(); + const SwFormatContent &aRCnt = pRight->GetContent(); + const SwFormatContent &aCnt = rFormatHead.GetHeaderFormat()->GetContent(); + + if (!aCnt.GetContentIdx()) + { + const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst); + rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetHeader() ); + } + else 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(), 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(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 SwFrameFormat *pRight = rFoot.GetFooterFormat(); + const SwFormatContent &aRCnt = pRight->GetContent(); + const SwFormatContent &aLCnt = rFormatFoot.GetFooterFormat()->GetContent(); + if( !aLCnt.GetContentIdx() ) + { + const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst); + rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetFooter() ); + } + else 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(), 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(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()) + { + GetIDocumentUndoRedo().AppendUndo( + std::make_unique(rDesc, rChged, this)); + } + ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo()); + + // Mirror at first if needed. + if ( rChged.GetUseOn() == UseOnPage::Mirror ) + const_cast(rChged).Mirror(); + else + { + // Or else transfer values from Master to Left + ::lcl_DescSetAttr(rChged.GetMaster(), + const_cast(rChged).GetLeft()); + } + ::lcl_DescSetAttr(rChged.GetMaster(), + const_cast(rChged).GetFirstMaster()); + ::lcl_DescSetAttr(rChged.GetLeft(), + const_cast(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() ); + + // #i46909# no undo if header or footer changed + bool bHeaderFooterChanged = false; + + // Synch header. + const SwFormatHeader &rHead = rChged.GetMaster().GetHeader(); + if (undoGuard.UndoWasEnabled()) + { + // #i46909# no undo if header or footer changed + // Did something change in the nodes? + const SwFormatHeader &rOldHead = rDesc.GetMaster().GetHeader(); + bHeaderFooterChanged |= + ( rHead.IsActive() != rOldHead.IsActive() || + rChged.IsHeaderShared() != rDesc.IsHeaderShared() || + rChged.IsFirstShared() != rDesc.IsFirstShared() ); + } + rDesc.GetMaster().SetFormatAttr( rHead ); + CopyMasterHeader(rChged, rHead, rDesc, true, false); // Copy left header + CopyMasterHeader(rChged, rHead, rDesc, false, true); // Copy first master + CopyMasterHeader(rChged, rHead, rDesc, true, true); // Copy first left + rDesc.ChgHeaderShare( rChged.IsHeaderShared() ); + + // Synch Footer. + const SwFormatFooter &rFoot = rChged.GetMaster().GetFooter(); + if (undoGuard.UndoWasEnabled()) + { + // #i46909# no undo if header or footer changed + // Did something change in the Nodes? + const SwFormatFooter &rOldFoot = rDesc.GetMaster().GetFooter(); + bHeaderFooterChanged |= + ( rFoot.IsActive() != rOldFoot.IsActive() || + rChged.IsFooterShared() != rDesc.IsFooterShared() ); + } + rDesc.GetMaster().SetFormatAttr( rFoot ); + CopyMasterFooter(rChged, rFoot, rDesc, true, false); // Copy left footer + CopyMasterFooter(rChged, rFoot, rDesc, false, true); // Copy first master + CopyMasterFooter(rChged, rFoot, rDesc, true, true); // Copy first left + 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(); + + // #i46909# no undo if header or footer changed + if( bHeaderFooterChanged ) + { + GetIDocumentUndoRedo().DelAllUndoObj(); + } + + 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(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 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(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(dynamic_cast( &rShell) != nullptr) + { + pShell = static_cast(&rShell); + 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 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::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(GetEditShell()); + if( pSh ) + { + mbOLEPrtNotifyPending = mbAllOLENotify = false; + + std::unique_ptr pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), true ); + if( pNodes ) + { + ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY, + 0, pNodes->size(), GetDocShell()); + getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction(); + SwMsgPoolItem aMsgHint( RES_UPDATE_ATTR ); + + 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->ModifyNotification( &aMsgHint, &aMsgHint ); + } + } + 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 ( 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..cdc449943 --- /dev/null +++ b/sw/source/core/doc/docdraw.cxx @@ -0,0 +1,634 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 + * and the members of a destroyed group + */ +static void lcl_AdjustPositioningAttr( SwDrawFrameFormat* _pFrameFormat, + const SdrObject& _rSdrObj ) +{ + const SwContact* pContact = GetUserCall( &_rSdrObj ); + OSL_ENSURE( pContact, " - 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(pAnchorFrame)->IsFollow(), + " - 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( " - 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 SwRect 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 . + { + const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( &_rSdrObj ); + if ( auto pAnchoredDrawObj = dynamic_cast( pAnchoredObj) ) + { + const SwRect aObjRect = _rSdrObj.GetSnapRect(); + const_cast(pAnchoredDrawObj) + ->SetLastObjRect( aObjRect.SVRect() ); + } + } +} + +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(GetUserCall(pObj)); + const SwFormatAnchor aAnch( pMyContact->GetFormat()->GetAnchor() ); + + std::unique_ptr pUndo; + if (GetIDocumentUndoRedo().DoesUndo()) + pUndo.reset(new SwUndoDrawGroup( static_cast(rMrkList.GetMarkCount()) , this)); + + // #i53320# + bool bGroupMembersNotPositioned( false ); + { + SwAnchoredDrawObject* pAnchoredDrawObj = + static_cast(pMyContact->GetAnchoredObj( pObj )); + bGroupMembersNotPositioned = pAnchoredDrawObj->NotYetPositioned(); + } + // Destroy ContactObjects and formats. + for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + + // #i53320# +#if OSL_DEBUG_LEVEL > 0 + SwAnchoredDrawObject* pAnchoredDrawObj = + static_cast(pContact->GetAnchoredObj( pObj )); + OSL_ENSURE( bGroupMembersNotPositioned == pAnchoredDrawObj->NotYetPositioned(), + " - group members have different positioning status!" ); +#endif + + pFormat = static_cast(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 ); + + 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; +} + +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 >[]> 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 ( dynamic_cast(pObj) != nullptr ) + { + SwDrawContact *pContact = static_cast(GetUserCall(pObj)); + SwFormatAnchor aAnch( pContact->GetFormat()->GetAnchor() ); + SdrObjList *pLst = static_cast(pObj)->GetSubList(); + + SwUndoDrawUnGroup* pUndo = nullptr; + if( bUndo ) + { + pUndo = new SwUndoDrawUnGroup( static_cast(pObj), this ); + GetIDocumentUndoRedo().AppendUndo(std::unique_ptr(pUndo)); + } + + for ( size_t i2 = 0; i2 < pLst->GetObjCount(); ++i2 ) + { + SdrObject* pSubObj = pLst->GetObj( i2 ); + SwDrawFrameFormat *pFormat = MakeDrawFrameFormat( GetUniqueShapeName(), + GetDfltFrameFormat() ); + pFormat->SetFormatAttr( aAnch ); + // #i36010# - set layout direction of the position + pFormat->SetPositionLayoutDir( + text::PositionLayoutDir::PositionInLayoutDirOfAnchor ); + pFormatsAndObjs[i].emplace_back( pFormat, pSubObj ); + + if( bUndo ) + pUndo->AddObj( static_cast(i2), pFormat ); + } + } + } + } + } + rDrawView.UnGroupMarked(); + // creation of 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(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( dynamic_cast( pObj) != nullptr ) + { + SwFlyFrameFormat* pFrameFormat = + static_cast(pObj)->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( pObj) == nullptr ) + { + SwDrawContact *pC = static_cast(GetUserCall(pObj)); + SwDrawFrameFormat *pFrameFormat = static_cast(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 pUndo; + if (GetIDocumentUndoRedo().DoesUndo()) + pUndo.reset(new SwUndoDrawDelete( static_cast(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(pObj->GetUserCall()); + if( pContact ) // of course not for grouped objects + { + SwDrawFrameFormat *pFormat = static_cast(pContact->GetFormat()); + // before delete of selection is performed, marked + // -objects have to be replaced by its + // reference objects. Thus, assert, if a + // -object is found in the mark list. + if ( dynamic_cast( pObj) != nullptr ) + { + OSL_FAIL( " 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( pField)) + { + // Date field + pInfo->SetRepresentation( + pDateField->GetFormatted( + *GetNumberFormatter(), LANGUAGE_SYSTEM) ); + } + else if (auto pURLField = dynamic_cast( 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( pField)) + { + // Clear measure field + pInfo->SetFieldColor(std::optional()); + } + else if ( auto pTimeField = dynamic_cast( 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(pObj) && + !dynamic_cast(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(pObj) && + !dynamic_cast(pObj) ) + { + SwDrawContact* pDrawContact = + dynamic_cast(::GetUserCall( pObj )); + if ( pDrawContact ) + { + SwAnchoredDrawObject* pAnchoredDrawObj = + dynamic_cast(pDrawContact->GetAnchoredObj( pObj )); + + // error handling + { + if ( !pAnchoredDrawObj ) + { + OSL_FAIL( "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..07902efe3 --- /dev/null +++ b/sw/source/core/doc/docedt.cxx @@ -0,0 +1,886 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 ) +{ + SwPosition aPos(rStartPos); + for(const SaveFly & rSave : rArr) + { + // create new anchor + SwFrameFormat* pFormat = rSave.pFrameFormat; + SwFormatAnchor aAnchor( pFormat->GetAnchor() ); + + if (rSave.isAtInsertNode) + { + if( pInsertPos != nullptr ) + { + if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) + { + aPos.nNode = *pInsertPos; + aPos.nContent.Assign(dynamic_cast(&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(&aPos.nNode.GetNode()), 0); + } + } + else + { + aPos.nNode = rStartPos.nNode.GetIndex() + rSave.nNdDiff; + aPos.nContent.Assign(dynamic_cast(&aPos.nNode.GetNode()), + rSave.nNdDiff == 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 ) +{ + 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)))) + { + 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* pDoc = rMkNdIdx.GetNode().GetDoc(); + SwFrameFormats& rTable = *pDoc->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 (pFormat != rTable[i]) + i = std::distance(rTable.begin(), rTable.find( pFormat )); + } + + pDoc->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* pDest = rNd.GetDoc(); + if( !pDest->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + SwRedlineTable::size_type nFndPos; + const SwPosition* pEnd; + SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetContentNode(), nCnt )); + pDest->getIDocumentRedlineAccess().GetRedline( aSrcPos, &nFndPos ); + const SwRangeRedline* pRedl; + while( nFndPos-- + && *( pEnd = ( pRedl = pDest->getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos ] )->End() ) == aSrcPos + && *pRedl->Start() < aSrcPos ) + { + if( !mpSaveIndex ) + { + mpSaveIndex.reset(new SwNodeIndex( rInsIdx, -1 )); + } + mvSavArr.push_back( const_cast(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 lcl_RangesToVector(const sal_uInt16 * pRanges) +{ + std::vector aResult; + + int i = 0; + while (pRanges[i] != 0) + { + OSL_ENSURE(pRanges[i+1] != 0, "malformed ranges"); + + for (sal_uInt16 j = pRanges[i]; j <= pRanges[i+1]; j++) + aResult.push_back(j); + + i += 2; + } + + return aResult; +} + +void sw_GetJoinFlags( SwPaM& rPam, bool& rJoinText, bool& rJoinPrev ) +{ + rJoinText = false; + rJoinPrev = false; + if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode ) + { + const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End(); + SwTextNode *pSttNd = pStt->nNode.GetNode().GetTextNode(); + if( pSttNd ) + { + SwTextNode *pEndNd = pEnd->nNode.GetNode().GetTextNode(); + rJoinText = nullptr != pEndNd; + if( rJoinText ) + { + 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* pDoc = 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(pDoc->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()) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( + RES_BREAK, false, &pItem ) ) + pTextNd->ResetAttr( RES_BREAK ); + if( pTextNd->HasSwAttrSet() && + SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( + RES_PAGEDESC, false, &pItem ) ) + pTextNd->ResetAttr( RES_PAGEDESC ); + } + + /* The PointNode */ + if( pOldTextNd->HasSwAttrSet() ) + { + const SfxPoolItem* pItem; + SfxItemSet aSet( pDoc->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(pDoc, aOldIdx.GetIndex(), SAL_MAX_INT32); + + SwIndex aAlphaIdx(pTextNd); + pOldTextNd->CutText( pTextNd, aAlphaIdx, SwIndex(pOldTextNd), + pOldTextNd->Len() ); + SwPosition aAlphaPos( aIdx, aAlphaIdx ); + pDoc->CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, true ); + + // move all Bookmarks/TOXMarks + if( !pContentStore->Empty() ) + pContentStore->Restore( pDoc, 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); + } + pDoc->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: + + <-- pTextNd + ccc <-- pDelNd + + and are paragraph + attributes. The attribute stayed if not + overwritten by an attribute in "ccc". Fixed by + first resetting all character attributes in first + paragraph (pTextNd). + */ + std::vector aShorts = + lcl_RangesToVector(aCharFormatSetRange); + pTextNd->ResetAttr(aShorts); + + if( pDelNd->HasSwAttrSet() ) + { + // only copy the character attributes + SfxItemSet aTmpSet( pDoc->GetAttrPool(), aCharFormatSetRange ); + aTmpSet.Put( *pDelNd->GetpSwAttrSet() ); + pTextNd->SetAttr( aTmpSet ); + } + } + + pDoc->CorrRel( aIdx, *rPam.GetPoint(), 0, true ); + // #i100466# adjust given , 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 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 )); + + sal_uLong nCurrNd = pSttPos->nNode.GetIndex(); + sal_uLong 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(pContentFrame)->IsHiddenNow() ) + { + if( pPageCnt && *pPageCnt && pPageSt ) + { + sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum(); + if( !*pPageSt ) + { + *pPageSt = nPageNr; + if( *pPageCnt < *pPageSt ) + *pPageCnt = *pPageSt; + } + 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(pNd)->GetSection().IsProtect() || + static_cast(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; + + sal_uInt32 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( const SwNodePtr& rpNd, void* pArgs ) +{ + // Hyphenate returns true if there is a hyphenation point and sets pPam + SwTextNode *pNode = rpNd->GetTextNode(); + SwHyphArgs *pHyphArgs = static_cast(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(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; + } + long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1 + : nPageNr + *pPageCnt - *pPageSt + 1; + ::SetProgressState( nStat, pNode->GetDoc()->GetDocShell() ); + } + pHyphArgs->SetRange( rpNd ); + if( pNode->Hyphenate( *pHyphArgs ) ) + { + pHyphArgs->SetNode( rpNd ); + 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 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 = pStt == rPaM.GetPoint() ? rPaM.GetMark() + : rPaM.GetPoint(); + + const sal_uLong nSttNd = pStt->nNode.GetIndex(); + const sal_uLong 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..7ea93bb4a --- /dev/null +++ b/sw/source/core/doc/docfld.cxx @@ -0,0 +1,1168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 ) +{ + 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 ) +{ + 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( const SwTableBox& rTBox ) +{ + m_eSetGetExpFieldType = TABLEBOX; + m_CNTNT.pTBox = &rTBox; + + m_nNode = 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(static_cast(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(rFrame).GetTextNodeFirst() + : *static_cast(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_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(); + + if( nullptr != ( pTableNd = pNext->FindTableNode() ) ) + 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 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 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 const & rTable, const OUString& rName ) +{ + HashStr* pFnd = rTable.Find( comphelper::string::strip(rName, ' ') ); + if( pFnd ) + return pFnd->aSetStr; + + return OUString(); +} + +SwDBData const & SwDoc::GetDBData() +{ + return GetDBDesc(); +} + +const SwDBData& SwDoc::GetDBDesc() +{ +#if HAVE_FEATURE_DBCONNECTIVITY + if(maDBData.sDataSource.isEmpty()) + { + const SwFieldTypes::size_type nSize = getIDocumentFieldsAccess().GetFieldTypes()->size(); + for(SwFieldTypes::size_type i = 0; i < nSize && maDBData.sDataSource.isEmpty(); ++i) + { + SwFieldType& rFieldType = *((*getIDocumentFieldsAccess().GetFieldTypes())[i]); + SwFieldIds nWhich = rFieldType.Which(); + if(IsUsed(rFieldType)) + { + switch(nWhich) + { + case SwFieldIds::Database: + case SwFieldIds::DbNextSet: + case SwFieldIds::DbNumSet: + case SwFieldIds::DbSetNumber: + { + std::vector vFields; + rFieldType.GatherFields(vFields); + if(vFields.size()) + { + if(SwFieldIds::Database == nWhich) + maDBData = static_cast(vFields.front()->GetField()->GetTyp())->GetDBData(); + else + maDBData = static_cast (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 + (void) b; +#else + GetDBManager()->SetInitDBFields( b ); +#endif +} + +#if HAVE_FEATURE_DBCONNECTIVITY + +/// 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& rDBNameList, + const std::vector* pAllDBNames ) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) rDBNameList; + (void) pAllDBNames; +#else + std::vector aUsedDBNames; + std::vector 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(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(pField)->GetDBData() )); + break; + + case SwFieldIds::DbSetNumber: + case SwFieldIds::DatabaseName: + AddUsedDBToList( rDBNameList, + lcl_DBDataToString(static_cast(pField)->GetRealDBData() )); + break; + + case SwFieldIds::DbNumSet: + case SwFieldIds::DbNextSet: + AddUsedDBToList( rDBNameList, + lcl_DBDataToString(static_cast(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& rAllDBNames ) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (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& SwDoc::FindUsedDBs( const std::vector& rAllDBNames, + const OUString& rFormula, + std::vector& 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.copy( nPos, nEndPos - nPos )); + } + } + } + return rUsedDBNames; +} + +void SwDoc::AddUsedDBToList( std::vector& rDBNameList, + const std::vector& rUsedDBNames ) +{ + for ( const auto &sName : rUsedDBNames ) + AddUsedDBToList( rDBNameList, sName ); +} + +void SwDoc::AddUsedDBToList( std::vector& rDBNameList, const OUString& rDBName) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) rDBNameList; + (void) rDBName; +#else + if( rDBName.isEmpty() ) + return; + +#ifdef UNX + for( const auto &sName : rDBNameList ) + if( rDBName == sName.getToken(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& rOldNames, + const OUString& rNewName ) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (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 = static_cast(rNewName.getToken(0, DB_DELIM, nIdx).toInt32()); + + 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(static_cast(pItem)); + SwTextField* pTextField = pFormatField->GetTextField(); + if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes()) + continue; + + SwField* pField = pFormatField->GetField(); + bool bExpand = false; + + switch( pField->GetTyp()->Which() ) + { + case SwFieldIds::Database: +#if HAVE_FEATURE_DBCONNECTIVITY + if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast(pField)->GetDBData()))) + { + SwDBFieldType* pOldTyp = static_cast(pField->GetTyp()); + + SwDBFieldType* pTyp = static_cast(getIDocumentFieldsAccess().InsertFieldType( + SwDBFieldType(this, pOldTyp->GetColumnName(), aNewDBData))); + + pFormatField->RegisterToFieldType( *pTyp ); + pField->ChgTyp(pTyp); + + static_cast(pField)->ClearInitialized(); + static_cast(pField)->InitContent(); + + bExpand = true; + } +#endif + break; + + case SwFieldIds::DbSetNumber: + case SwFieldIds::DatabaseName: + if (IsNameInArray(rOldNames, + lcl_DBDataToString(static_cast(pField)->GetRealDBData()))) + { + static_cast(pField)->SetDBData(aNewDBData); + bExpand = true; + } + break; + + case SwFieldIds::DbNumSet: + case SwFieldIds::DbNextSet: + if (IsNameInArray(rOldNames, + lcl_DBDataToString(static_cast(pField)->GetRealDBData()))) + { + static_cast(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& 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& 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(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(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); + + // consider and unhide sections + // with hide condition, only in mode 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 (). + 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 aTmpArr; + std::vector::size_type nArrStt = 0; + SwSectionFormats& rArr = rDoc.GetSections(); + SwSectionNode* pSectNd = nullptr; + sal_uLong 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 ) + { + sal_uLong 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::size_type n = nArrStt; n < aTmpArr.size(); ++n) + { + pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode(); + OSL_ENSURE( pSectNd, "Where is my SectionNode" ); + pSectNd->GetSection().SetCondHidden( false ); + } + for (std::vector::size_type n = 0; n < nArrStt; ++n) + { + pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode(); + OSL_ENSURE( pSectNd, "Where is my SectionNode" ); + pSectNd->GetSection().SetCondHidden( false ); + } + + // add all to the list so that they are sorted + for (const auto &nId : aTmpArr) + { + GetBodyNode( *rDoc.GetNodes()[ nId ]->GetSectionNode() ); + } + } + + const OUString sTrue("TRUE"); + const OUString sFalse("FALSE"); + +#if HAVE_FEATURE_DBCONNECTIVITY + 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(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(static_cast(pField))->SetHidden( false ); + else if (sFormula==sTrue) + const_cast(static_cast(pField))->SetHidden( true ); + else + break; + + sFormula.clear(); + // trigger formatting + const_cast(pFormatField)->ModifyNotification( nullptr, nullptr ); + } + break; + + case SwFieldIds::HiddenText: + if (GETFLD_ALL == eGetMode) + { + sFormula = pField->GetPar1(); + if (sFormula.isEmpty() || sFormula==sFalse) + const_cast(static_cast(pField))->SetValue( true ); + else if (sFormula==sTrue) + const_cast(static_cast(pField))->SetValue( false ); + else + break; + + sFormula.clear(); + + // evaluate field + const_cast(static_cast(pField))->Evaluate(&rDoc); + // trigger formatting + const_cast(pFormatField)->UpdateTextNode(nullptr, nullptr); + } + break; + +#if HAVE_FEATURE_DBCONNECTIVITY + case SwFieldIds::DbNumSet: + { + SwDBData aDBData(const_cast(static_cast(pField))->GetDBData(&rDoc)); + + if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) + && (GETFLD_ALL == eGetMode + || (GETFLD_CALC & eGetMode + && static_cast(pField)->IsCondValid())) + ) + { + sFormula = pField->GetPar1(); + } + } + break; + case SwFieldIds::DbNextSet: + { + SwDBData aDBData(const_cast(static_cast(pField))->GetDBData(&rDoc)); + + if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) + && (GETFLD_ALL == eGetMode + || (GETFLD_CALC & eGetMode + && static_cast(pField)->IsCondValid())) + ) + { + sFormula = pField->GetPar1(); + } + } + break; +#endif + default: break; + } + + if (!sFormula.isEmpty()) + { + GetBodyNode( *pTextField, nWhich ); + } + } + } + m_nFieldListGetMode = eGetMode; + m_nNodes = rDoc.GetNodes().Count(); +} + +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 const tmp(aPt, false); + const SwContentFrame* pFrame = rTextNd.getLayoutFrame( + rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp); + + std::unique_ptr 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 ) + pNew.reset(new SetGetExpField( aIdx, &rTField )); + } + 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 )); + } + + // always set the BodyTextFlag in GetExp or DB fields + if( SwFieldIds::GetExp == nFieldWhich ) + { + SwGetExpField* pGetField = const_cast(static_cast(rTField.GetFormatField().GetField())); + pGetField->ChgBodyTextFlag( bIsInBody ); + } +#if HAVE_FEATURE_DBCONNECTIVITY + else if( SwFieldIds::Database == nFieldWhich ) + { + SwDBField* pDBField = const_cast(static_cast(rTField.GetFormatField().GetField())); + pDBField->ChgBodyTextFlag( bIsInBody ); + } +#endif + if( pNew != nullptr ) + m_pFieldSortList->insert( std::move(pNew) ); +} + +void SwDocUpdateField::GetBodyNode( const SwSectionNode& rSectNd ) +{ + const SwDoc& rDoc = *rSectNd.GetDoc(); + std::unique_ptr pNew; + + if( rSectNd.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex() ) + { + do { // middle check loop + + // we need to get the anchor first + // create index to determine the TextNode + SwPosition aPos( rSectNd ); + SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aPos.nNode ); // to the next ContentNode + + if( !pCNd || !pCNd->IsTextNode() ) + break; + + // always the first! (in tab headline, header-/footer) + Point aPt; + std::pair 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( rSectNd, &aPos )); + + } while( false ); + } + + if( !pNew ) + pNew.reset(new SetGetExpField( rSectNd )); + + m_pFieldSortList->insert( std::move(pNew) ); +} + +void SwDocUpdateField::InsertFieldType( const SwFieldType& rType ) +{ + OUString sFieldName; + switch( rType.Which() ) + { + case SwFieldIds::User : + sFieldName = static_cast(rType).GetName(); + break; + case SwFieldIds::SetExp: + sFieldName = static_cast(rType).GetName(); + break; + default: + OSL_ENSURE( false, "No valid field type" ); + } + + if( !sFieldName.isEmpty() ) + { + 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(rType).GetName(); + break; + case SwFieldIds::SetExp: + sFieldName = static_cast(rType).GetName(); + break; + default: break; + } + + if( !sFieldName.isEmpty() ) + { + 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 ) + { + if (m_FieldTypeTable[n].get() == pFnd) + { + m_FieldTypeTable[n].reset(static_cast(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..988e59c76 --- /dev/null +++ b/sw/source/core/doc/docfly.cxx @@ -0,0 +1,1159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 SwDoc::GetFlyFrameFormats( + FlyCntType const eType, bool const bIgnoreTextBoxes) +{ + SwFrameFormats& rFormats = *GetSpzFrameFormats(); + const size_t nSize = rFormats.size(); + + std::vector 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(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 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(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(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()); + for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i, + pPage =static_cast(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(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 + const SfxPoolItem* pItem; + 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(&rFormat) ); + pNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 ); + } + + if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem )) + { + 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. + if( SfxItemState::SET != rSet.GetItemState( RES_HORI_ORIENT, false, &pItem )) + pItem = nullptr; + + SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() ); + bool bPutOldH(false); + + if( text::HoriOrientation::NONE == aOldH.GetHoriOrient() && ( !pItem || + aOldH.GetPos() == static_cast(pItem)->GetPos() )) + { + SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos(); + nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX(); + + if( pItem ) + { + SwFormatHoriOrient* pH = const_cast(static_cast(pItem)); + aOldH.SetHoriOrient( pH->GetHoriOrient() ); + aOldH.SetRelationOrient( pH->GetRelationOrient() ); + } + aOldH.SetPos( nPos ); + bPutOldH = true; + } + if (nNew == RndStdIds::FLY_AT_PAGE) + { + sal_Int16 nRelOrient(pItem + ? static_cast(pItem)->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 ); + } + + if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false, &pItem )) + pItem = nullptr; + SwFormatVertOrient aOldV( rFormat.GetVertOrient() ); + + // #i28922# - correction: compare + if( text::VertOrientation::NONE == aOldV.GetVertOrient() && (!pItem || + aOldV.GetPos() == static_cast(pItem)->GetPos() ) ) + { + SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos(); + nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY(); + if( pItem ) + { + SwFormatVertOrient* pV = const_cast(static_cast(pItem)); + aOldV.SetVertOrient( pV->GetVertOrient() ); + aOldV.SetRelationOrient( pV->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 pResult; + + switch(pItem->Which()) + { + case XATTR_FILLBITMAP: + { + pResult = static_cast< const XFillBitmapItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_LINEDASH: + { + pResult = static_cast< const XLineDashItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_LINESTART: + { + pResult = static_cast< const XLineStartItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_LINEEND: + { + pResult = static_cast< const XLineEndItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_FILLGRADIENT: + { + pResult = static_cast< const XFillGradientItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_FILLFLOATTRANSPARENCE: + { + pResult = static_cast< const XFillFloatTransparenceItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + case XATTR_FILLHATCH: + { + pResult = static_cast< const XFillHatchItem* >(pItem)->checkForUniqueItem(pDrawModel); + break; + } + } + + if(pResult) + { + rSet.Put(*pResult); + } + } +} + +bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet ) +{ + if( !rSet.Count() ) + return false; + + std::unique_ptr 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( 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( 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(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 + const SfxPoolItem* pItem; + 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(); + if( SfxItemState::SET == pAsk->GetItemState( RES_ANCHOR, false, &pItem ) + && static_cast(pItem)->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( *pItem ); + 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_URL, 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( pObj) == nullptr ) + { + SwDrawContact* pContact = static_cast(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 + bool bNoUserCallExcepted = + dynamic_cast( pObj) != nullptr && + !static_cast(pObj)->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 is kept, because the + // anchor index position could be moved, if the object again is + // anchored as character. + std::unique_ptr 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(pNewAnchorFrame)->IsFollow() ) + { + pNewAnchorFrame = static_cast(pNewAnchorFrame)->FindMaster(); + } + if ( pNewAnchorFrame->IsProtected() ) + { + pNewAnchorFrame = nullptr; + } + else + { + SwPosition aPos( pNewAnchorFrame->IsTextFrame() + ? *static_cast(pNewAnchorFrame)->GetTextNodeForParaProps() + : *static_cast(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, by left-top-corner + std::pair 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(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().IsInside( aPt ) ) + pNewAnchorFrame = pNewAnchorFrame->GetNext(); + if ( !pNewAnchorFrame ) + continue; + + aNewAnch.SetPageNum( static_cast(pNewAnchorFrame)->GetPhyPageNum()); + } + break; + case RndStdIds::FLY_AS_CHAR: + if( _bSameOnly ) // Change of position/size + { + if( !pOldAnchorFrame ) + { + pContact->ConnectToLayout(); + pOldAnchorFrame = pContact->GetAnchorFrame(); + } + const_cast(static_cast(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(pNewAnchorFrame)); + SwPosition aPos( *pFrame->GetTextNodeForParaProps() ); + if ( pNewAnchorFrame->getFrameArea().IsInside( 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 ); + } + 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 ) takes care of the + // invalidation of the object position. + if ( _bPosCorr ) + { + // #i33313# - consider not connected 'virtual' drawing + // objects + if ( dynamic_cast( pObj) != nullptr && + !static_cast(pObj)->IsConnected() ) + { + SwRect aNewObjRect( aObjRect ); + static_cast(pContact->GetAnchoredObj( nullptr )) + ->AdjustPositioningAttr( pNewAnchorFrame, + &aNewObjRect ); + } + else + { + static_cast(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 && " - 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(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 sal_uLong nFlySttNd = pCntIdx->GetIndex(); + if( 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; + sal_uLong nTstSttNd = rAnchor.GetContentAnchor()->nNode.GetIndex(); + if( nFlySttNd <= nTstSttNd && nTstSttNd < nFlySttNd + 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(); + sal_uLong 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(static_cast(rDest)); + + // Attach Follow to the Master. + SwFormatChain aChain = rDestFormat.GetChain(); + aChain.SetPrev( &static_cast(rSource) ); + SetAttr( aChain, rDestFormat ); + + SfxItemSet aSet( GetAttrPool(), svl::Items{} ); + + // Attach Follow to the Master. + aChain.SetPrev( &static_cast(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( 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..7b877a7cd --- /dev/null +++ b/sw/source/core/doc/docfmt.cxx @@ -0,0 +1,2151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( const SwNodePtr& rpNd, void* pArgs ) +{ + const sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast(pArgs); + SwContentNode* pNode = rpNd->GetContentNode(); + if (pPara && pPara->pLayout && pPara->pLayout->IsHideRedlines() + && pNode && pNode->GetRedlineMergeFlag() == SwNode::Merge::Hidden) + { + return true; + } + if( pNode && pNode->HasSwAttrSet() ) + { + const bool bLocked = pNode->IsModifyLocked(); + pNode->LockModify(); + + SwDoc* pDoc = pNode->GetDoc(); + + // remove unused attribute RES_LR_SPACE + // add list attributes + SfxItemSet aSavedAttrsSet( + pDoc->GetAttrPool(), + svl::Items< + RES_PARATR_NUMRULE, RES_PARATR_NUMRULE, + RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1, + RES_PAGEDESC, RES_BREAK>{}); + const SfxItemSet* pAttrSetOfNode = pNode->GetpSwAttrSet(); + + std::vector aClearWhichIds; + // restoring all paragraph list attributes + { + SfxItemSet aListAttrSet( pDoc->GetAttrPool(), svl::Items{} ); + 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 != static_cast(pItem)->GetPageDesc(); + break; + case RES_BREAK: + bSave = SvxBreak::NONE != static_cast(pItem)->GetBreak(); + break; + case RES_PARATR_NUMRULE: + bSave = !static_cast(pItem)->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, + " - 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 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 std::set &rAttrs, + const bool bSendDataChangedEvents, + SwRootFrame const*const pLayout) +{ + SwPaM* pPam = const_cast(&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::unique_ptr< SwDataChanged > xDataChanged; + if ( bSendDataChangedEvents ) + { + xDataChanged.reset( new SwDataChanged( *pPam ) ); + } + SwHistory* pHst = nullptr; + if (GetIDocumentUndoRedo().DoesUndo()) + { + std::unique_ptr pUndo(new SwUndoResetAttr( rRg, + bTextAttr ? sal_uInt16(RES_CONDTXTFMTCOLL) : sal_uInt16(RES_TXTFMTCOLL) )); + if( !rAttrs.empty() ) + { + pUndo->SetAttrs( 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 + SfxItemSet aDelSet(GetAttrPool(), svl::Items{}); + for( std::set::const_reverse_iterator 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(); + + xDataChanged.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 ); + + SfxItemSet aSet(GetAttrPool(), svl::Items{}); + 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(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 ); + } + getIDocumentState().SetModified(); +} + +void SwDoc::ResetAttrAtFormat( const sal_uInt16 nWhichId, + SwFormat& rChangedFormat ) +{ + std::unique_ptr 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; + + SwModify 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 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( aOld, this ) ); + } + + const SfxPoolItem* pTmpItem; + if( ( SfxItemState::SET == + aNew.GetItemState( RES_PARATR_TABSTOP, false, &pTmpItem ) ) && + static_cast(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 = (*static_cast(pTmpItem))[ 0 ].GetTabPos(), + nOldWidth = aOld.Get(RES_PARATR_TABSTOP)[ 0 ].GetTabPos(); + + bool bChg = false; + for (const SfxPoolItem* pItem2 : GetAttrPool().GetItemSurrogates(RES_PARATR_TABSTOP)) + { + auto pTabStopItem = dynamic_cast(pItem2); + if(pTabStopItem) + bChg |= lcl_SetNewDefTabStops( nOldWidth, nNewWidth, + *const_cast(pTabStopItem) ); + } + + aNew.ClearItem( RES_PARATR_TABSTOP ); + aOld.ClearItem( RES_PARATR_TABSTOP ); + if( bChg ) + { + SwFormatChg aChgFormat( mpDfltCharFormat.get() ); + // notify the frames + aCallMod.ModifyNotification( &aChgFormat, &aChgFormat ); + } + } + } + + if( aNew.Count() && aCallMod.HasWriterListeners() ) + { + SwAttrSetChg aChgOld( aOld, aOld ); + SwAttrSetChg aChgNew( aNew, aNew ); + aCallMod.ModifyNotification( &aChgOld, &aChgNew ); // all changed are sent + } + + // remove the default formats from the object again + SwIterator 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(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( pFormat) != nullptr || dynamic_cast( 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(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 static_cast(FindFormatByName( static_cast(*mpFrameFormatTable), 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(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(pDerivedFrom); + pFrameFormat = MakeFrameFormat( rFormatName, pFrameFormat, bBroadcast, bAuto ); + return dynamic_cast(pFrameFormat); +} + +SwCharFormat *SwDoc::MakeCharFormat( const OUString &rFormatName, + SwCharFormat *pDerivedFrom, + bool bBroadcast ) +{ + SwCharFormat *pFormat = new SwCharFormat( GetAttrPool(), rFormatName, pDerivedFrom ); + mpCharFormatTable->push_back( pFormat ); + pFormat->SetAuto(false); + getIDocumentState().SetModified(); + + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().AppendUndo( + std::make_unique(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(pDerivedFrom); + pCharFormat = MakeCharFormat( rFormatName, pCharFormat, bBroadcast ); + return dynamic_cast(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(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(pDerivedFrom); + pTextFormatColl = MakeTextFormatColl( rFormatName, pTextFormatColl, bBroadcast ); + return dynamic_cast(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(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 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( const SwNodePtr& rpNode, void* pArgs ) +{ + SwContentNode* pCNd = rpNode->GetTextNode(); + + if( pCNd == nullptr) + return true; + + sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast(pArgs); + + if (pPara->pLayout && pPara->pLayout->IsHideRedlines()) + { + if (pCNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden) + { + return true; + } + if (pCNd->IsTextNode()) + { + pCNd = sw::GetParaPropsNode(*pPara->pLayout, SwNodeIndex(*pCNd)); + } + } + + SwTextFormatColl* pFormat = static_cast(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(*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 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(&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(CopyFormat( rFormat, *GetFrameFormats(), &SwDoc::MakeFrameFormat_, + *GetDfltFrameFormat() )); +} + +/// copy the char format +SwCharFormat* SwDoc::CopyCharFormat( const SwCharFormat& rFormat ) +{ + return static_cast(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(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(pNewColl)->SetConditions( + static_cast(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 SfxPoolItem* pItem; + if( SfxItemState::SET == pNewColl->GetItemState( RES_PARATR_NUMRULE, + false, &pItem )) + { + const OUString& rName = static_cast(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 = static_cast(FindFormatByName( static_cast(*mpGrfFormatCollTable), rColl.GetName() )); + if( pNewColl ) + return pNewColl; + + // Search for the "parent" first + SwGrfFormatColl* pParent = mpDfltGrfFormatColl.get(); + if( pParent != rColl.DerivedFrom() ) + pParent = CopyGrfColl( *static_cast(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 == FindFormatByName( rDestArr, pSrc->GetName() ) ) + { + if( RES_CONDTXTFMTCOLL == pSrc->Which() ) + MakeCondTextFormatColl( pSrc->GetName(), static_cast(&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 = FindFormatByName( rDestArr, pSrc->GetName() ); + pDest->SetAuto(false); + pDest->DelDiffs( *pSrc ); + + // #i94285#: existing instance, before copying attributes + const SfxPoolItem* pItem; + if( &GetAttrPool() != pSrc->GetAttrSet().GetPool() && + SfxItemState::SET == pSrc->GetAttrSet().GetItemState( + RES_PAGEDESC, false, &pItem ) && + static_cast(pItem)->GetPageDesc() ) + { + SwFormatPageDesc aPageDesc( *static_cast(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( FindFormatByName( rDestArr, + pSrc->DerivedFrom()->GetName() ) ); + if( RES_TXTFMTCOLL == pSrc->Which() || + RES_CONDTXTFMTCOLL == pSrc->Which() ) + { + SwTextFormatColl* pSrcColl = static_cast(pSrc), + * pDstColl = static_cast(pDest); + if( &pSrcColl->GetNextTextFormatColl() != pSrcColl ) + pDstColl->SetNextTextFormatColl( *static_cast(FindFormatByName( + rDestArr, pSrcColl->GetNextTextFormatColl().GetName() ) ) ); + + if(pSrcColl->IsAssignedToListLevelOfOutlineStyle()) + pDstColl->AssignToListLevelOfOutlineStyle(pSrcColl->GetAssignedOutlineStyleLevel()); + +//FEATURE::CONDCOLL + if( RES_CONDTXTFMTCOLL == pSrc->Which() ) + // Copy the conditions, but delete the old ones first! + static_cast(pDstColl)->SetConditions( + static_cast(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 pNewItem(pItem->Clone()); + + SwFrameFormat* pOldFormat; + if( bCpyHeader ) + pOldFormat = static_cast(pNewItem.get())->GetHeaderFormat(); + else + pOldFormat = static_cast(pNewItem.get())->GetFooterFormat(); + + if( pOldFormat ) + { + SwFrameFormat* pNewFormat = new SwFrameFormat( GetAttrPool(), "CpyDesc", + GetDfltFrameFormat() ); + pNewFormat->CopyAttrs( *pOldFormat ); + + if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState( + RES_CNTNT, false, &pItem )) + { + const SwFormatContent* pContent = static_cast(pItem); + 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, 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 ) + static_cast(pNewItem.get())->RegisterToFormat(*pNewFormat); + else + static_cast(pNewItem.get())->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); + } +} + +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(); +} + +SwFormat* SwDoc::FindFormatByName( const SwFormatsBase& rFormatArr, + const OUString& rName ) +{ + SwFormat* pFnd = nullptr; + for( size_t n = 0; n < rFormatArr.GetFormatCount(); ++n ) + { + // Does the Doc already contain the template? + if( rFormatArr.GetFormat(n)->HasName( rName ) ) + { + pFnd = rFormatArr.GetFormat(n); + break; + } + } + return pFnd; +} + +void SwDoc::MoveLeftMargin(const SwPaM& rPam, bool bRight, bool bModulus, + SwRootFrame const*const pLayout) +{ + SwHistory* pHistory = nullptr; + if (GetIDocumentUndoRedo().DoesUndo()) + { + std::unique_ptr 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( static_cast(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(static_cast(nListLevel)); + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + aLS.SetTextLeft( rFormat.GetIndentAt() ); + aLS.SetTextFirstLineOffset( static_cast(rFormat.GetFirstLineIndent()) ); + } + } + } + } + + 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(rPos) ); + } + } + return bRet; +} + +SwTableBoxFormat* SwDoc::MakeTableBoxFormat() +{ + SwTableBoxFormat* pFormat = new SwTableBoxFormat( GetAttrPool(), mpDfltFrameFormat.get() ); + pFormat->SetName("TableBox" + OUString::number(reinterpret_cast(pFormat))); + getIDocumentState().SetModified(); + return pFormat; +} + +SwTableLineFormat* SwDoc::MakeTableLineFormat() +{ + SwTableLineFormat* pFormat = new SwTableLineFormat( GetAttrPool(), mpDfltFrameFormat.get() ); + pFormat->SetName("TableLine" + OUString::number(reinterpret_cast(pFormat))); + getIDocumentState().SetModified(); + return pFormat; +} + +void SwDoc::EnsureNumberFormatter() +{ + comphelper::doubleCheckedInit(mpNumberFormatter, []() + { + LanguageType eLang = LANGUAGE_SYSTEM; + SvNumberFormatter* pRet = new SvNumberFormatter(comphelper::getProcessComponentContext(), eLang); + pRet->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL ); + if (!utl::ConfigManager::IsFuzzing()) + pRet->SetYear2000(static_cast(::utl::MiscCfg().GetYear2000())); + return pRet; + }); +} + +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(rSrc).GetNumberFormatter( false ); + if( pN ) + { + pNFormat = rDest.GetNumberFormatter(); + pNFormat->MergeFormatter( *pN ); + } + } + + if( &rSrc != &rDest ) + static_cast(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(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 whichIds; + SfxItemIter iter(rSet); + for (SfxPoolItem const* pItem = iter.GetCurItem(); pItem; pItem = iter.NextItem()) + { + whichIds.push_back(pItem->Which()); + whichIds.push_back(pItem->Which()); + } + whichIds.push_back(0); + SfxItemSet currentSet(GetAttrPool(), whichIds.data()); + pTNd->GetParaAttr(currentSet, nEnd, nEnd); + for (size_t i = 0; whichIds[i]; i += 2) + { // yuk - want to explicitly set the pool defaults too :-/ + currentSet.Put(currentSet.Get(whichIds[i])); + } + + 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 to + SfxItemSet aSet(rSet); + // remove from all items, which are already set at the format + aSet.Differentiate(rFormat.GetAttrSet()); + // contains now all *new* items for the format + + // copying current format item set to + SfxItemSet aOldSet(rFormat.GetAttrSet()); + // insert new items into + aOldSet.Put(aSet); + // invalidate all new items in 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(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 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)); + } + } + + 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); + xmlTextWriterSetIndentString(pWriter, BAD_CAST(" ")); + xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr); + bOwns = true; + } + xmlTextWriterStartElement(pWriter, BAD_CAST("SwDoc")); + 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); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mbModified")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(getIDocumentState().IsModified()).getStr())); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterEndElement(pWriter); + if (bOwns) + { + xmlTextWriterEndDocument(pWriter); + xmlFreeTextWriter(pWriter); + } +} + +void SwDBData::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwDBData")); + + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sDataSource"), BAD_CAST(sDataSource.toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sCommand"), BAD_CAST(sCommand.toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nCommandType"), BAD_CAST(OString::number(nCommandType).getStr())); + + xmlTextWriterEndElement(pWriter); +} + +std::set SwDoc::GetDocColors() +{ + std::set 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(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( 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( + boost::make_tuple(x->Which(), x->GetName(), x) ); + return m_Array.project<0>( it ); +} + +std::pair +SwFrameFormats::rangeFind( sal_uInt16 type, const OUString& name ) const +{ + return m_TypeAndNameIndex.equal_range( boost::make_tuple(type, name) ); +} + +std::pair +SwFrameFormats::rangeFind( const value_type& x ) const +{ + return rangeFind( x->Which(), x->GetName() ); +} + +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::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(p)) != end(); +} + +bool SwFrameFormats::newDefault( const value_type& x ) +{ + std::pair 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..90899b91a --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( static_cast( + 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( static_cast( + 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( + static_cast(m_bEndNote + ? RES_POOLCHR_ENDNOTE + : RES_POOLCHR_FOOTNOTE), + pFormat, + *this); +} + +SwCharFormat* SwEndNoteInfo::GetAnchorCharFormat(SwDoc& rDoc) const +{ + auto pAnchorFormatFromDoc = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( static_cast( + 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( + static_cast(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::SwClientNotify( const SwModify& rModify, const SfxHint& rHint) +{ + if (auto pLegacyHint = dynamic_cast(&rHint)) + { + const sal_uInt16 nWhich = pLegacyHint->m_pOld ? pLegacyHint->m_pOld->Which() : pLegacyHint->m_pNew ? pLegacyHint->m_pNew->Which() : 0 ; + if (RES_ATTRSET_CHG == nWhich || RES_FMT_CHG == nWhich) + { + auto pFormat = GetCurrentCharFormat(m_pCharFormat == nullptr); + if (!pFormat || !m_aDepends.IsListeningTo(pFormat) || pFormat->IsFormatInDTOR()) + return; + SwDoc* pDoc = pFormat->GetDoc(); + SwFootnoteIdxs& rFootnoteIdxs = pDoc->GetFootnoteIdxs(); + for( size_t nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos ) + { + SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ]; + const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote(); + if ( rFootnote.IsEndNote() == m_bEndNote ) + { + pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr()); + } + } + } + else + CheckRegistration( pLegacyHint->m_pOld ); + } + else if (auto pModifyChangedHint = dynamic_cast(&rHint)) + { + auto pNew = const_cast(pModifyChangedHint->m_pNew); + if(m_pAnchorFormat == &rModify) + m_pAnchorFormat = static_cast(pNew); + else if(m_pCharFormat == &rModify) + m_pCharFormat = static_cast(pNew); + else if(m_pPageDesc == &rModify) + m_pPageDesc = static_cast(pNew); + else if(m_pTextFormatColl == &rModify) + m_pTextFormatColl = static_cast(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() : + SwEndNoteInfo(), + 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(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 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 ) + { + SwFormatChg aOld( pOldChrFormat ); + SwFormatChg aNew( pNewChrFormat ); + mpFootnoteInfo->ModifyNotification( &aOld, &aNew ); + } + + // #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( 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 ) + { + SwFormatChg aOld( pOldChrFormat ); + SwFormatChg aNew( pNewChrFormat ); + mpEndNoteInfo->ModifyNotification( &aOld, &aNew ); + } + + // #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 sal_uLong nSttNd = pStt->nNode.GetIndex(); + const sal_Int32 nSttCnt = pStt->nContent.GetIndex(); + const sal_uLong nEndNd = pEnd->nNode.GetIndex(); + const sal_Int32 nEndCnt = pEnd->nContent.GetIndex(); + + size_t nPos = 0; + rFootnoteArr.SeekEntry( pStt->nNode, &nPos ); + + std::unique_ptr 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++ ]; + sal_uLong 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(rFootnote).SetEndNote( bIsEndNote ); + bTypeChgd = true; + pTextFootnote->CheckCondColl(); + //#i11339# dispose UNO wrapper when a footnote is changed to an endnote or vice versa + const_cast(rFootnote).InvalidateFootnote(); + } + } + } + } + + nPos = nPosSave; // There are more in the front! + while( nPos ) + { + SwTextFootnote* pTextFootnote = rFootnoteArr[ --nPos ]; + sal_uLong 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(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..b933843ff --- /dev/null +++ b/sw/source/core/doc/docglbl.cxx @@ -0,0 +1,523 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 SwNodePtr GetStartNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl ) +{ + for( ; *nOutl < pOutlNds->size(); ++(*nOutl) ) + { + SwNodePtr pNd = (*pOutlNds)[ *nOutl ]; + if( pNd->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel && !pNd->FindTableNode() ) + { + return pNd; + } + } + + return nullptr; +} + +static SwNodePtr GetEndNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl ) +{ + SwNodePtr 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 SwNodePtr GetStartNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl ) +{ + for( ; *nOutl < pOutlNds->size(); ++(*nOutl) ) + { + SwNodePtr pNd = (*pOutlNds)[ *nOutl ]; + if( pNd->GetTextNode()->GetTextColl() == pSplitColl && + !pNd->FindTableNode() ) + { + return pNd; + } + } + return nullptr; +} + +static SwNodePtr GetEndNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl ) +{ + SwNodePtr 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(&GetNodes().GetOutLineNds()); + std::unique_ptr xTmpOutlNds; + SwNodePtr 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 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 pFilter; + switch( eDocType ) + { + case SPLITDOC_TO_HTML: + pFilter = SwIoSystem::GetFilterOfFormat("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 ) + { + SwNodePtr 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(&xDocSh)->GetDoc(); + + uno::Reference xDPS( + static_cast(&xDocSh)->GetModel(), + uno::UNO_QUERY_THROW); + uno::Reference 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, 0, aEndIdx.GetNode() ); + SwNodeIndex aTmpIdx( pDoc->GetNodes().GetEndOfContent() ); + GetNodes().Copy_( aRg, aTmpIdx, false ); + + // Delete the initial TextNode + SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), 2 ); + if( aIdx.GetIndex() + 1 != + pDoc->GetNodes().GetEndOfContent().GetIndex() ) + pDoc->GetNodes().Delete( aIdx ); + + // All Flys in the section + GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRg, nullptr, aIdx); + + // And what's with all the Bookmarks? + // ????? + + 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. + sal_uLong nNodeDiff = aEndIdx.GetIndex() - + pStartNd->GetIndex() - 1; + if( nNodeDiff ) + { + SwPaM aTmp( *pStartNd, aEndIdx.GetNode(), 1, -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, 1, aEndIdx, 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(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..269d00ad8 --- /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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +void SwDoc::ReplaceUserDefinedDocumentProperties( + const uno::Reference& xSourceDocProps) +{ + OSL_ENSURE(xSourceDocProps.is(), "null reference"); + + uno::Reference xDPS( + GetDocShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference xDocProps( + xDPS->getDocumentProperties() ); + OSL_ENSURE(xDocProps.is(), "null reference"); + + uno::Reference xSourceUDSet( + xSourceDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW); + uno::Reference xTargetUD( + xDocProps->getUserDefinedProperties()); + uno::Reference xTargetUDSet(xTargetUD, + uno::UNO_QUERY_THROW); + const uno::Sequence tgtprops + = xTargetUDSet->getPropertySetInfo()->getProperties(); + + for (const auto& rTgtProp : tgtprops) { + try { + xTargetUD->removeProperty(rTgtProp.Name); + } catch (uno::Exception &) { + // ignore + } + } + + uno::Reference xSetInfo + = xSourceUDSet->getPropertySetInfo(); + const uno::Sequence 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 xSourceDPS( + rSource.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference xSourceDocProps( + xSourceDPS->getDocumentProperties() ); + OSL_ENSURE(xSourceDocProps.is(), "null reference"); + + uno::Reference xDPS( + GetDocShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference 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(static_cast(pTableNd)) : *static_cast(pContentNd) ); + aCpyPam.SetMark(); + + // till the nodes array's end + aCpyPam.GetPoint()->nNode = pGDoc->GetNodes().GetEndOfContent().GetIndex()-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(rInsPos.nNode.GetNode(). + FindTableBoxStartNode()); + + if( pBoxSttNd && 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..ce33b8c21 --- /dev/null +++ b/sw/source/core/doc/doclay.cxx @@ -0,0 +1,1682 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 ) + { + pPg = getIDocumentDrawModelAccess().GetDrawModel()->AllocPage( false ); + getIDocumentDrawModelAccess().GetDrawModel()->InsertPage( pPg ); + } + + // 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(pObj)->GetUnoControlModel(); + uno::Any aVal; + uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY); + const OUString sName("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( pObj) == nullptr && + dynamic_cast( pObj) == nullptr && + typeid(SdrObject) != typeid(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; + if( !mbInReading ) + 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(), -1, + GetNodes().GetEndOfAutotext() ); + GetNodes().SectionDown( &aRange, SwFlyStartNode ); + + pFormat->SetFormatAttr( SwFormatContent( rNode.StartOfSectionNode() )); + + const SwFormatAnchor* pAnchor = nullptr; + if( pFlySet ) + { + pFlySet->GetItemState( RES_ANCHOR, false, + reinterpret_cast(&pAnchor) ); + 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()) + { + sal_uLong nNodeIdx = rAnchPos.nNode.GetIndex(); + const sal_Int32 nCntIdx = rAnchPos.nContent.GetIndex(); + GetIDocumentUndoRedo().AppendUndo( + std::make_unique( 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 && SfxItemState::SET == pFlySet->GetItemState( + RES_ANCHOR, false, reinterpret_cast(&pAnch) )) || + ( pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState( + RES_ANCHOR, true, reinterpret_cast(&pAnch) )) ) + { + if ( RndStdIds::FLY_AT_PAGE != pAnch->GetAnchorId() ) + { + pAnchorPos = pAnch->GetContentAnchor(); + } + } + } + + if (pAnchorPos) + { + if( !pFrameFormat ) + pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ); + + sal_uInt16 nCollId = static_cast( + 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((*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, 0, *pTableNd->EndOfSectionNode(), 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(&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(&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 sal_uInt32 nFlyIndex = pFlyPos->nNode.GetIndex(); + const SwPosition* pPaMStart = pTmp->Start(); + const SwPosition* pPaMEnd = pTmp->End(); + const sal_uInt32 nPamStartIndex = pPaMStart->nNode.GetIndex(); + const sal_uInt32 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(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(getIDocumentLayoutAccess().GetCurrentLayout()->GetLower()); + while( pPage ) + { + if( pPage->GetSortedObjs() ) + { + const SwSortedObjs &rObjs = *pPage->GetSortedObjs(); + for(SwAnchoredObject* pAnchoredObj : rObjs) + { + SwFrameFormat *pFly; + if ( dynamic_cast( pAnchoredObj) != 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(pPage->GetPrev()); + while ( !pContentFrame && pPrv ) + { + pContentFrame = pPrv->FindFirstBodyContent(); + pPrv = static_cast(pPrv->GetPrev()); + } + } + if ( pContentFrame ) + { + SwNodeIndex aIdx( pContentFrame->IsTextFrame() + ? *static_cast(pContentFrame)->GetTextNodeFirst() + : *static_cast(pContentFrame)->GetNode() ); + aRetval.insert(std::make_shared(aIdx, pFly, aRetval.size())); + } + } + } + } + pPage = static_cast(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, OUString const& rText, OUString const& rSeparator, + const OUString& rNumberingSeparator, + const bool bBefore, const sal_uInt16 nId, const sal_uLong 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." ); + sal_uLong 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# + // and <description> attributes are lost when calling <DelFrames()>. + // Thus, keep them and restore them after the calling <MakeFrames()> + const bool bIsSwFlyFrameFormatInstance( dynamic_cast<SwFlyFrameFormat*>(pOldFormat) != nullptr ); + const OUString sTitle( bIsSwFlyFrameFormatInstance + ? static_cast<SwFlyFrameFormat*>(pOldFormat)->GetObjTitle() + : OUString() ); + const OUString sDescription( bIsSwFlyFrameFormatInstance + ? static_cast<SwFlyFrameFormat*>(pOldFormat)->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 ( bIsSwFlyFrameFormatInstance ) + { + static_cast<SwFlyFrameFormat*>(pOldFormat)->SetObjTitle( sTitle ); + static_cast<SwFlyFrameFormat*>(pOldFormat)->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.isEmpty() ) + { + 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, sal_uLong 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, const OUString& rName, const OUString& rCmpName) +{ + if (rName.startsWith(rCmpName)) + { + // Only get and set the Flag + const sal_Int32 nNum = rName.copy(nNmLen).toInt32() - 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()) + { + 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* pDoc, const char* pDefStrId, sal_uInt16 eType) +{ + assert(eType >= RES_FMT_BEGIN && eType < RES_FMT_END); + if( pDoc->IsInMailMerge()) + { + OUString newName = "MailMergeFly" + + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US ) + + OUString::number( pDoc->GetSpzFrameFormats()->size() + 1 ); + return newName; + } + + OUString aName(SwResId(pDefStrId)); + sal_Int32 nNmLen = aName.getLength(); + + const SwFrameFormats& rFormats = *pDoc->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() const +{ + return lcl_GetUniqueFlyName(this, STR_GRAPHIC_DEFNAME, RES_FLYFRMFMT); +} + +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, "DrawObject", RES_DRAWFRMFMT); +} + +const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType nNdTyp ) const +{ + auto range = GetSpzFrameFormats()->rangeFind( RES_FLYFRMFMT, rName ); + for( auto it = range.first; it != range.second; it++ ) + { + const SwFrameFormat* pFlyFormat = *it; + if( RES_FLYFRMFMT != pFlyFormat->Which() || pFlyFormat->GetName() != rName ) + continue; + 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 ) +{ + OUString sName( rName ); + if( sName.isEmpty() || FindFlyByName( sName ) ) + { + const char* 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)); + + if( 255 < ( n = GetSpzFrameFormats()->size() )) + 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 = aNm.copy( nLen ).toInt32(); + 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() ) + { + 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*const pFlys(pFlyNd->GetAnchoredFlys()); + bool bFound(false); + for (size_t i = 0; pFlys && i < pFlys->size(); ++i) + { + const SwFrameFormat *const pFormat = (*pFlys)[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..358887b21 --- /dev/null +++ b/sw/source/core/doc/docnew.cxx @@ -0,0 +1,1282 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <o3tl/sorted_vector.hxx> + +#include <doc.hxx> +#include <proofreadingiterator.hxx> +#include <com/sun/star/text/XFlatParagraphIteratorProvider.hpp> +#include <com/sun/star/linguistic2/XProofreadingIterator.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/random.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/XmlIdRegistry.hxx> +#include <sal/log.hxx> + +#include <sfx2/linkmgr.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svl/zforlist.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> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::document; + +/* + * 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)), + 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(), "Character style", 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), + m_pXmlIdRegistry(), + 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->push_back(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 )); + maOLEModifiedIdle.SetDebugName( "sw::SwDoc maOLEModifiedIdle" ); + +#if HAVE_FEATURE_DBCONNECTIVITY + // 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. + { + SfxItemSet aIgnorableParagraphItems( GetAttrPool(), svl::Items<RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1>{}); + 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; + + 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; + } + + // Old - deletion without a Flag is expensive, because we send a Modify + // aTOXTypes.DeleteAndDestroy( 0, aTOXTypes.Count() ); + { + for( auto n = mpTOXTypes->size(); n; ) + { + (*mpTOXTypes)[ --n ]->SetInDocDTOR(); + (*mpTOXTypes)[ n ].reset(); + } + mpTOXTypes->clear(); + } + mpDefTOXBases.reset(); + + // 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() ); + + // 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() ); + mpCharFormatTable->erase( mpCharFormatTable->begin() ); + +#if HAVE_FEATURE_DBCONNECTIVITY + // 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 + + delete mpNumberFormatter.load(); mpNumberFormatter= nullptr; + mpFootnoteInfo.reset(); + mpEndNoteInfo.reset(); + mpLineNumberInfo.reset(); + mpFootnoteIdxs.reset(); + mpTOXTypes.reset(); + mpEmptyPageFormat.reset(); + mpColumnContFormat.reset(); + mpDfltCharFormat.reset(); + mpDfltFrameFormat.reset(); + mpLayoutCache.reset(); + + SfxItemPool::Free(mpAttrPool); +} + +void SwDoc::SetDocShell( SwDocShell* pDSh ) +{ + if( mpDocShell != pDSh ) + { + 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->DeleteAndDestroy(1, mpCharFormatTable->size()); + + 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(); + + delete mpNumberFormatter.load(); 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* pDoc = rTextNode.GetDoc(); + if( !pDoc || pDoc->IsInDtor() ) + return nullptr; + return pDoc->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 + const sal_uInt16 aRangeOfDefaults[] = + { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_CHRATR_BEGIN, RES_CHRATR_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, + XATTR_START, XATTR_END-1, + 0 + }; + + SfxItemSet aNewDefaults(GetAttrPool(), aRangeOfDefaults); + + for (auto nRange = 0; aRangeOfDefaults[nRange] != 0; nRange += 2) + { + for (sal_uInt16 nWhich = aRangeOfDefaults[nRange]; + nWhich <= aRangeOfDefaults[nRange + 1]; ++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() - 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.get(), 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); + + 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, const OUString& 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 inidividual 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 ); + // InsertPageBreak just works on SwTextNode nodes, so make + // sure the last node is one! + bool bIsTextNode = aBreakIdx.GetNode().IsTextNode(); + if ( !bIsTextNode ) + getIDocumentContentOperations().AppendTextNode( aBreakPos ); + const OUString name = pTargetPageDesc->GetName(); + pTargetShell->InsertPageBreak( &name, nStartPageNumber ); + if ( !bIsTextNode ) + { + pTargetShell->SttEndDoc( false ); + --aBreakIdx; + GetNodes().Delete( aBreakIdx ); + } + + // 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" ); + 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(); + sal_uLong const nEndIdx = aPaM.End()->nNode.GetIndex(); + + for (sal_uLong nIdx = aPaM.Start()->nNode.GetIndex(); + nIdx <= nEndIdx; ++nIdx) + { + SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode(); + if ( pTextNode ) + UpdateParRsid( pTextNode ); + } + } + + { + sal_uLong 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..c16ffcdd0 --- /dev/null +++ b/sw/source/core/doc/docnum.cxx @@ -0,0 +1,2622 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lrspitem.hxx> +#include <ftninfo.hxx> +#include <ftnidx.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentListsAccess.hxx> +#include <IDocumentFieldsAccess.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 <tools/datetimeutils.hxx> + +#include <map> +#include <stdlib.h> + + +namespace { + void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam, sal_uInt16 marker, + SwRootFrame const*const pLayout) + { + std::set<sal_uInt16> aResetAttrsArray; + aResetAttrsArray.insert( 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) + { // 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( 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); + + getIDocumentState().SetModified(); +} + +void SwDoc::PropagateOutlineRule() +{ + 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() ) + { + SwNumRule * pMyOutlineRule = GetOutlineNumRule(); + + if (pMyOutlineRule) + { + 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(); + const SwNodePtr pSttNd = &aPam.Start()->nNode.GetNode(); + const SwNodePtr 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(static_cast<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(static_cast<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 = &rStt == rPam.GetPoint() ? *rPam.GetMark() + : *rPam.GetPoint(); + 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 ]; + + sal_uLong 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() + 2 ); + + long nOffs = nNewPos - ( 0 < nOffset ? aEndRg.GetIndex() : aSttRg.GetIndex()); + SwPaM aPam( aSttRg, aEndRg, 0, -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; + OUString sNum = rName.getToken( 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; + OUString sName( rName ); + + while( -1 != nPos ) + { + sal_uInt16 nVal = 0; + for( sal_Int32 n = 0; n < sNum.getLength(); ++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.copy( nPos ); + nPos = 0; + sNum = sName.getToken( 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; + OUString sTempNum = sExpandedText.getToken(0, '.', nPos); + if( sTempNum.isEmpty() || -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) + { + 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) + { + 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 ) + { + 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( 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 ); + } + } + } +} + +bool SwDoc::ReplaceNumRule( const SwPosition& rPos, + const OUString& rOldRule, const OUString& rNewRule ) +{ + bool bRet = false; + SwNumRule *pOldRule = FindNumRulePtr( rOldRule ), + *pNewRule = FindNumRulePtr( rNewRule ); + if( pOldRule && pNewRule && pOldRule != pNewRule ) + { + 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 ); + sal_uInt16 nChgFormatLevel = 0; + for( sal_uInt8 n = 0; n < MAXLEVEL; ++n ) + { + const SwNumFormat& rOldFormat = pOldRule->Get( n ), + & rNewFormat = pNewRule->Get( n ); + + if( rOldFormat.GetAbsLSpace() != rNewFormat.GetAbsLSpace() || + rOldFormat.GetFirstLineOffset() != rNewFormat.GetFirstLineOffset() ) + nChgFormatLevel |= ( 1 << n ); + } + + 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(); + + bRet = true; + } + } + + return bRet; +} + +namespace +{ + struct ListStyleData + { + SwNumRule* pReplaceNumRule; + bool bCreateNewList; + OUString sListId; + + ListStyleData() + : pReplaceNumRule( nullptr ), + bCreateNewList( false ), + sListId() + {} + }; +} + +void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM) +{ + OSL_ENSURE( rPaM.GetDoc() == this, "need same doc" ); + + std::map<SwNumRule *, ListStyleData> aMyNumRuleMap; + + bool bFirst = true; + + const sal_uLong nStt = rPaM.Start()->nNode.GetIndex(); + const sal_uLong nEnd = rPaM.End()->nNode.GetIndex(); + for (sal_uLong 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); + sal_uLong nStt = aPam.Start()->nNode.GetIndex(); + sal_uLong 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->IsHideRedlines()) + { + if (rIndex.GetNode().IsTextNode()) + { + if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None) + { + 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->IsHideRedlines()) + { + 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() ) + { + // If NO_NUMLEVEL is switched on, we search the preceding Node with Numbering + bool bError = false; + do { + sw::GotoPrevLayoutTextFrame(aIdx, pLayout); + if( aIdx.GetNode().IsTextNode() ) + { + pNd = aIdx.GetNode().GetTextNode(); + const SwNumRule* pRule = pNd->GetNumRule(); + + sal_uInt8 nTmpNum; + + if( pRule ) + { + 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() != 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); + sal_uLong nStt = aPam.Start()->nNode.GetIndex(); + sal_uLong const nEnd = aPam.End()->nNode.GetIndex(); + + // -> outline nodes are promoted or demoted differently + bool bOnlyOutline = true; + bool bOnlyNonOutline = true; + for (sal_uLong 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 (sal_uLong 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(sal_uLong 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, long nOffset, bool const bIsOutlMv) +{ + // 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->IsHideRedlines()) + { + 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 < 0) + { + nOffset += rPam.Start()->nNode.GetIndex() - nodes.first->GetIndex(); + if (0 <= nOffset) // hack: there are callers that know what + { // node they want; those should never need + nOffset = -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 (0 < nOffset) + { + nOffset -= nodes.second->GetIndex() - rPam.End()->nNode.GetIndex(); + if (nOffset <= 0) // hack: there are callers that know what + { // node they want; those should never need + nOffset = +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 > 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 < 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, long const nOffset, + bool const bIsOutlMv, SwRootFrame const*const pLayout) +{ + const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); + + sal_uLong nStIdx = pStt->nNode.GetIndex(); + sal_uLong 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() ) + { // 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. + } + + sal_uLong nInStIdx, nInEndIdx; + long nOffs = nOffset; + if( nOffset > 0 ) + { + nInEndIdx = nEndIdx; + nEndIdx += nOffset; + ++nOffs; + } + else + { + // Impossible to move to negative index + if( o3tl::make_unsigned(std::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 > 0 ? pEnd->nNode : pStt->nNode, nOffs ); + SwNodeRange aMvRg( pStt->nNode, 0, pEnd->nNode, +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 )) + { + // 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(); + + /* 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 + + 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 > 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) + { + assert(*aPam.Start() <= *pRedline->Start()); // caller's fault + SwRangeRedline* pNewRedline; + { + SwPaM pam(*pRedline, nullptr); + sal_uLong const nCurrentOffset( + aIdx.GetIndex() + 1 - 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); + + 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); + } + + sal_uLong 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; + sal_uLong 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( const OUString& 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 = static_cast<sal_uInt16>(sNm.copy( nNmLen ).toInt32()); + 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 ) + if( 0xff != ( nTmp = pSetFlags[ n ] )) + { + // 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(); +} + +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..b98e89caf --- /dev/null +++ b/sw/source/core/doc/docredln.cxx @@ -0,0 +1,1906 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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 <view.hxx> +#include <viewsh.hxx> +#include <viscrs.hxx> +#include <rootfrm.hxx> +#include <strings.hrc> +#include <wrtsh.hxx> +#include <txtfld.hxx> + +#include <flowfrm.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 ) + { + SwRedlineTable::size_type nDummy = 0; + const SwRangeRedline* pCurrent = rTable[ n ]; + const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr; + if( pCurrent == pNext ) + ++nDummy; + if( n == nWatch ) + ++nDummy; // Possible debugger breakpoint + } + } + +#endif + + +SwExtraRedlineTable::~SwExtraRedlineTable() +{ + DeleteAndDestroyAll(); +} + +void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos) + { + const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos); + xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name())); + xmlTextWriterEndElement(pWriter); + } + 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* pDoc, const SwTable& rTable, 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(); ) + { + 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 + } + } + } + else + { + const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline); + if (pTableRowRedline) + { + const SwTableLine *pRedTabLine = &pTableRowRedline->GetTableLine(); + const SwTableBoxes &rRedTabBoxes = pRedTabLine->GetTabBoxes(); + const SwTable& rRedTable = rRedTabBoxes[0]->GetSttNd()->FindTableNode()->GetTable(); + if ( &rRedTable == &rTable ) + { + // Redline for this table + 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) + + { + DeleteAndDestroy( nCurRedlinePos ); + bChg = true; + continue; // don't increment position after delete + } + } + } + } + ++nCurRedlinePos; + } + + if( bChg ) + pDoc->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 SwModify& rMod, const SwRootFrame* pLayout, + SwFrameType const nFrameType, const Point* pPoint) +{ + SwIterator<SwFrame, SwModify, 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(); + } + } + } +} + +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.GetPoint() == pStartPos ? rCursor.GetMark() : rCursor.GetPoint(); + + + 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()); +} + +} // 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. + static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr; + if (!comphelper::LibreOfficeKit::isActive() || bDisableRedlineComments) + 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* pDoc = pRedline->GetDoc(); + SwViewShell* pSh; + if( pDoc && !pDoc->IsInDtor() ) + { + pSh = pDoc->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) + { + 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); + p->CallDisplayFunc(nP); + return rv.second; + } + return InsertWithValidRanges( p ); +} + +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); + 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 = pStt == p->GetPoint() ? p->GetMark() : p->GetPoint(); + 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(maVector.size() - 1); + LOKRedlineNotification(RedlineNotification::Remove, pRedline); + delete pRedline; + } +} + +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 const 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 const 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 = pRStt == pTmp->GetPoint() ? pTmp->GetMark() + : pTmp->GetPoint(); + if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos ) + { + if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos ) + { + pFnd = pTmp; + break; + } + } + else + break; + } + } + return pFnd; +} + +void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos) + operator[](nCurRedlinePos)->dumpAsXml(pWriter); + + 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* pDoc = rPam.GetDoc(); + + // What about Undo? Is it turned off? + SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId + ? pDoc->FindTextFormatCollByName( m_sFormatNm ) + : pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId ); + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore)); + + SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() ); + + const SwPosition* pStt = rPam.Start(), + * pEnd = pStt == rPam.GetPoint() ? rPam.GetMark() + : rPam.GetPoint(); + + 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(); + aPam.GetPoint()->nContent.Assign( pNode, pNode->Len() ); + } + 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 ) + pDoc->SetTextFormatColl( aPam, pColl, false ); + + if( m_pSet ) + pDoc->getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet ); + + pDoc->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* pDoc = rPam.GetDoc(); + + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore)); + + // Actually we need to reset the Attribute here! + for( const auto& rWhichId : m_aWhichIds ) + { + pDoc->getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ), + SetAttrMode::DONTEXPAND ); + } + + pDoc->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_eType( eT ), m_bAutoFormat(false), m_nAuthor( nAut ), m_nSeqNo( 0 ) +{ + 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_eType( rCpy.m_eType ) + , m_bAutoFormat(false) + , m_nAuthor( rCpy.m_nAuthor ) + , m_nSeqNo( rCpy.m_nSeqNo ) +{ +} + +// 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_eType(eT), m_bAutoFormat(false), m_nAuthor(nAut), m_nSeqNo(0) +{ +} + +SwRedlineData::~SwRedlineData() +{ + delete m_pExtraData; + delete m_pNext; +} + +bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const +{ + DateTime aTime = GetTimeStamp(); + aTime.SetSec(0); + DateTime aCompareTime = rCmp.GetTimeStamp(); + aCompareTime.SetSec(0); + return m_nAuthor == rCmp.m_nAuthor && + m_eType == rCmp.m_eType && + m_sComment == rCmp.m_sComment && + aTime == aCompareTime && + (( !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; +} + +static const char* 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::m_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( m_nLastId++ ) +{ + m_bDelLastPara = false; + m_bIsVisible = true; + if( !rPam.HasMark() ) + DeleteMark(); +} + +SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam ) + : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ), + m_pRedlineData( new SwRedlineData( rData )), + m_pContentSect( nullptr ), + m_nId( m_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( m_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( m_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* pRedline, SwDoc* pDoc) +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + const SwRedlineTable& rRedTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i) + { + if (rRedTable[i] == pRedline) + { + SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedline); + break; + } + } +} + +void SwRangeRedline::MaybeNotifyRedlinePositionModification(long nTop) +{ + if (!comphelper::LibreOfficeKit::isActive()) + 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) +{ + if( 1 <= nLoop ) + { + SwDoc* pDoc = GetDoc(); + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore); + ::sw::UndoGuard const undoGuard(pDoc->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 = true; + MoveFromSection(nMyPos); + break; + + case RedlineType::Format: // Attributes have been applied + case RedlineType::Table: // Table structure has been modified + InvalidateRange(Invalidation::Add); + break; + default: + break; + } + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } +} + +void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos) +{ + SwDoc* pDoc = GetDoc(); + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore); + ::sw::UndoGuard const undoGuard(pDoc->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; + } + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); +} + +void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos) +{ + SwDoc* pDoc = GetDoc(); + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + SwRedlineData* pCur; + + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore); + ::sw::UndoGuard const undoGuard(pDoc->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; + } + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); +} + +// trigger the Layout +void SwRangeRedline::InvalidateRange(Invalidation const eWhy) +{ + sal_uLong nSttNd = GetMark()->nNode.GetIndex(), + nEndNd = GetPoint()->nNode.GetIndex(); + sal_Int32 nSttCnt = GetMark()->nContent.GetIndex(); + sal_Int32 nEndCnt = GetPoint()->nContent.GetIndex(); + + if( nSttNd > nEndNd || ( nSttNd == nEndNd && nSttCnt > nEndCnt )) + { + sal_uLong nTmp = nSttNd; nSttNd = nEndNd; nEndNd = nTmp; + sal_Int32 nTmp2 = nSttCnt; nSttCnt = nEndCnt; nEndCnt = nTmp2; + } + + SwNodes& rNds = GetDoc()->GetNodes(); + for (sal_uLong 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->ModifyNotification(&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( sal_uLong 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; + } +} + +void SwRangeRedline::MoveToSection() +{ + if( !m_pContentSect ) + { + const SwPosition* pStt = Start(), + * pEnd = pStt == GetPoint() ? GetMark() : GetPoint(); + + SwDoc* pDoc = 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 = pDoc->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 = pDoc->GetNodes(); + if( pCSttNd || pCEndNd ) + { + SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() ) + ? pCSttNd->GetTextNode()->GetTextColl() + : (pCEndNd && pCEndNd->IsTextNode() ) + ? pCEndNd->GetTextNode()->GetTextColl() + : pDoc->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 ) + pDoc->getIDocumentContentOperations().MoveAndJoin( aPam, aPos ); + else + { + if( pCSttNd && !pCEndNd ) + m_bDelLastPara = true; + pDoc->getIDocumentContentOperations().MoveRange( aPam, aPos, + SwMoveFlags::DEFAULT ); + } + } + else + { + pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) ); + + SwPosition aPos( *pSttNd->EndOfSectionNode() ); + pDoc->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 = pStt == GetPoint() ? GetMark() : GetPoint(); + + SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode(); + SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode(); + + SwStartNode* pSttNd; + SwDoc* pDoc = GetDoc(); + SwNodes& rNds = pDoc->GetNodes(); + + bool bSaveCopyFlag = pDoc->IsCopyIsMove(), + bSaveRdlMoveFlg = pDoc->getIDocumentRedlineAccess().IsRedlineMove(); + pDoc->SetCopyIsMove( true ); + + // The IsRedlineMove() flag causes the behaviour of the + // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change, + // which will eventually be called by the CopyRange() below. + pDoc->getIDocumentRedlineAccess().SetRedlineMove(true); + + if( pCSttNd ) + { + SwTextFormatColl* pColl = pCSttNd->IsTextNode() + ? pCSttNd->GetTextNode()->GetTextColl() + : pDoc->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 )); + pDoc->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() ); + pDoc->getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly); + } + else + { + SwNodeIndex aInsPos( *pSttNd->EndOfSectionNode() ); + SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 ); + pDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos); + } + } + m_pContentSect = new SwNodeIndex( *pSttNd ); + + pDoc->SetCopyIsMove( bSaveCopyFlag ); + pDoc->getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg ); + +} + +void SwRangeRedline::DelCopyOfSection(size_t nMyPos) +{ + if( m_pContentSect ) + { + const SwPosition* pStt = Start(), + * pEnd = pStt == GetPoint() ? GetMark() : GetPoint(); + + SwDoc* pDoc = 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 = pDoc->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? + pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/); + } + else if( pCSttNd || pCEndNd ) + { + if( pCSttNd && !pCEndNd ) + m_bDelLastPara = true; + pDoc->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 = pDoc->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(); + pDoc->getIDocumentContentOperations().DelFullPara( aPam ); + } + } + else + { + pDoc->getIDocumentContentOperations().DeleteRange( aPam ); + } + + if( pStt == GetPoint() ) + Exchange(); + + DeleteMark(); + } +} + +void SwRangeRedline::MoveFromSection(size_t nMyPos) +{ + if( m_pContentSect ) + { + SwDoc* pDoc = GetDoc(); + const SwRedlineTable& rTable = pDoc->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(), 1, + ( 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; + + pDoc->getIDocumentContentOperations().AppendTextNode( aPos ); + } + else + { + pDoc->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 ) + { + pDoc->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; +} + +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() +{ + // 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()); + if (const SwTextNode *pTextNode = pPaM->GetNode().GetTextNode()) + { + if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, true )) + { + sDescr = SwResId(STR_START_QUOTE) + + pTextAttr->GetFormatField().GetField()->GetFieldName() + + SwResId(STR_END_QUOTE); + } + } + + // replace $1 in description by description of the redlines text + const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS)); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aTmpStr); + + aResult = aRewriter.Apply(aResult); + + if (bDeletePaM) + delete pPaM; + + return aResult; +} + +void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline")); + + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr())); + 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; + } + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr())); + + SwPaM::dumpAsXml(pWriter); + + 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..f1ce56a0d --- /dev/null +++ b/sw/source/core/doc/docruby.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 <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 = pStt == _pStartCursor->GetPoint() + ? _pStartCursor->GetMark() + : _pStartCursor->GetPoint(); + 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 ); + std::set<sal_uInt16> aDelArr; + aDelArr.insert( 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 = pStt == _pStartCursor->GetPoint() + ? _pStartCursor->GetMark() + : _pStartCursor->GetPoint(); + 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..21ff81b7b --- /dev/null +++ b/sw/source/core/doc/docsort.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 <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 <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 <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; +OUString* SwSortElement::pLastAlgorithm = nullptr; +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; + delete pLastAlgorithm; + pLastAlgorithm = nullptr; + delete pSortCollator; + pSortCollator = nullptr; + delete pLclData; + pLclData = nullptr; + pDoc = nullptr; + pBox = nullptr; +} + +SwSortElement::~SwSortElement() +{ +} + +double SwSortElement::StrToDouble( const OUString& 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* pSrtKey = pOptions->aKeys[ nKey ].get(); + if( pSrtKey->eSortOrder == SwSortOrder::Ascending ) + { + pOrig = this; + pCmp = &rCmp; + } + else + { + pOrig = &rCmp; + pCmp = this; + } + + if( pSrtKey->bIsNumeric ) + { + double n1 = pOrig->GetValue( nKey ); + double n2 = pCmp->GetValue( nKey ); + + nCmp = n1 < n2 ? -1 : n1 == n2 ? 0 : 1; + } + else + { + if( !pLastAlgorithm || *pLastAlgorithm != pSrtKey->sSortType ) + { + if( pLastAlgorithm ) + *pLastAlgorithm = pSrtKey->sSortType; + else + pLastAlgorithm = new OUString( pSrtKey->sSortType ); + pSortCollator->loadCollatorAlgorithm( *pLastAlgorithm, + *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) + if( -1 != ( nStart = rStr.indexOf( nDeli, 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( sal_uLong 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 + { + sal_uLong 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, -1, 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()-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) + sal_uLong 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); + + size_t 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 = static_cast<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 sal_uLong 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(), 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 + sal_uLong nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex(); + + bool bDelFirst = false; + if( nCount == 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(), 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) : + pDoc(pDocPtr), + nRow(0), + nCol(0) +{ // If the array is symmetric + bSym = CheckLineSymmetry(rBoxRef); + if( bSym ) + { + // Determine column/row count + nCols = GetColCount(rBoxRef); + nRows = GetRowCount(rBoxRef); + + // Create linear array + size_t nCount = static_cast<size_t>(nRows) * nCols; + pArr = std::make_unique<FndBox_ const *[]>(nCount); + memset(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 = nRow; + for (const auto & pLine : rLines) + { + // The Boxes of a Line + const FndBoxes_t& rBoxes = pLine->GetBoxes(); + sal_uInt16 nOldCol = 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 = nRow * nCols + nCol; + 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 ) ) + { + auto pSet = std::make_unique<SfxItemSet>( + pDoc->GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + pSet->Put( pFormat->GetAttrSet() ); + if( ppItemSets.empty() ) + { + size_t nCount = static_cast<size_t>(nRows) * nCols; + ppItemSets.resize(nCount); + } + ppItemSets[nOff] = std::move(pSet); + } + + bModRow = true; + } + else + { + // Iterate recursively over the Lines of a Box + FillFlat( *pBox, ( j+1 == rBoxes.size() ) ); + } + nCol++; + } + if(bModRow) + nRow++; + nCol = nOldCol; + } + if(!bLastBox) + nRow = nOldRow; +} + +/// Access a specific Cell +const FndBox_* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const +{ + sal_uInt16 nOff = n_Row * nCols + n_Col; + const FndBox_* pTmp = pArr[nOff]; + + OSL_ENSURE(n_Col < nCols && n_Row < nRows && pTmp, "invalid array access"); + return pTmp; +} + +const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const +{ + OSL_ENSURE( ppItemSets.empty() || ( n_Col < nCols && n_Row < nRows), "invalid array access"); + + return !ppItemSets.empty() ? ppItemSets[unsigned(n_Row * nCols) + n_Col].get() : 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..7ca7103ff --- /dev/null +++ b/sw/source/core/doc/doctxm.cxx @@ -0,0 +1,2076 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <scriptinfo.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 <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 +{ + sal_uLong nNode; + sal_Int32 nContent; +public: + CompareNodeContent( sal_uLong nNd, sal_Int32 nCnt ) + : nNode( nNd ), nContent( nCnt ) {} + + bool operator==( const CompareNodeContent& rCmp ) const + { return nNode == rCmp.nNode && nContent == rCmp.nContent; } + bool operator!=( const CompareNodeContent& rCmp ) const + { return nNode != rCmp.nNode || nContent != rCmp.nContent; } + bool operator< ( const CompareNodeContent& rCmp ) const + { return nNode < rCmp.nNode || + ( nNode == rCmp.nNode && nContent < rCmp.nContent); } + bool operator<=( const CompareNodeContent& rCmp ) const + { return nNode < rCmp.nNode || + ( nNode == rCmp.nNode && nContent <= rCmp.nContent); } + bool operator> ( const CompareNodeContent& rCmp ) const + { return nNode > rCmp.nNode || + ( nNode == rCmp.nNode && nContent > rCmp.nContent); } + bool operator>=( const CompareNodeContent& rCmp ) const + { return nNode > rCmp.nNode || + ( nNode == rCmp.nNode && nContent >= rCmp.nContent); } +}; + +} + +const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark, + SwTOXSearch eDir, bool bInReadOnly ) +{ + const SwTextTOXMark* pMark = rCurTOXMark.GetTextTOXMark(); + OSL_ENSURE(pMark, "pMark==0 invalid TextTOXMark"); + + const SwTextNode *pTOXSrc = pMark->GetpTextNd(); + + CompareNodeContent aAbsIdx( pTOXSrc->GetIndex(), pMark->GetStart() ); + CompareNodeContent aPrevPos( 0, 0 ); + CompareNodeContent aNextPos( ULONG_MAX, SAL_MAX_INT32 ); + CompareNodeContent aMax( 0, 0 ); + CompareNodeContent aMin( ULONG_MAX, SAL_MAX_INT32 ); + + const SwTOXMark* pNew = nullptr; + const SwTOXMark* pMax = &rCurTOXMark; + const SwTOXMark* pMin = &rCurTOXMark; + + const SwTOXType* pType = rCurTOXMark.GetTOXType(); + SwTOXMarks aMarks; + SwTOXMark::InsertTOXMarks( aMarks, *pType ); + + for(SwTOXMark* pTOXMark : aMarks) + { + if ( pTOXMark == &rCurTOXMark ) + continue; + + pMark = pTOXMark->GetTextTOXMark(); + if (!pMark) + continue; + + 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::pair<SwTOXBase const*, sw::RedlineMode> const tmp(&rTOX, + pLayout && pLayout->IsHideRedlines() + ? sw::RedlineMode::Hidden + : sw::RedlineMode::Shown); + 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( sal_uLong nSttNd, sal_uLong 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 ) + { + OSL_ENSURE( dynamic_cast< const SwTOXBaseSection *>( &pSectNd->GetSection()) != nullptr, + "no TOXBaseSection!" ); + SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>( + pSectNd->GetSection()); + return &rTOXSect; + } + pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode(); + } + return nullptr; +} + +const SwAttrSet& SwDoc::GetTOXBaseAttrSet(const SwTOXBase& rTOXBase) +{ + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) != nullptr, "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; + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) != nullptr, "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 = rNm.copy( nNmLen ).toInt32(); + 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) +{ + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) != nullptr, + "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?" ); + } + } + return pNd ? pNd->FindOutlineNodeOfLevel(nLvl, pLayout) : nullptr; +} + +// 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 (!SwTOXBase::GetRegisteredIn()->HasWriterListeners() || + !GetFormat()) + { + return; + } + SwSectionNode const*const pSectNd(GetFormat()->GetSectionNode()); + if (nullptr == pSectNd || + !pSectNd->GetNodes().IsDocNodes() || + IsHiddenFlag() || + (pLayout->IsHideRedlines() && pSectNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)) + { + return; + } + + if ( !mbKeepExpression ) + { + maMSTOCExpression.clear(); + } + + SwDoc* pDoc = const_cast<SwDoc*>(pSectNd->GetDoc()); + + assert(pDoc); //Where is the document? + + if (pAttr && GetFormat()) + pDoc->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 + size_t nPgDescNdIdx = pSectNd->GetIndex() + 1; + size_t* 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 = &pDoc->GetPageDesc( 0 ); + } + } + + pDoc->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 SwTextNode* pOwnChapterNode = IsFromChapter() + ? ::lcl_FindChapterNode( *pSectNd, pLayout ) + : nullptr; + + SwNode2LayoutSaveUpperFrames aN2L(*pSectNd); + const_cast<SwSectionNode*>(pSectNd)->DelFrames(); + + // This would be a good time to update the Numbering + pDoc->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); + { + pDoc->getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, RedlineType::Any ); + + SwNodeIndex aSttIdx( *pSectNd, +1 ); + SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() ); + pFirstEmptyNd = pDoc->GetNodes().MakeTextNode( aEndIdx, + pDoc->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 = pDoc->GetNodes().GoNext( &aNxtIdx ); + assert(pCNd != pFirstEmptyNd); + assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex()); + if( pCNd->HasSwAttrSet() ) + { + SfxItemSet aBrkSet( pDoc->GetAttrPool(), aBreakSetRange ); + aBrkSet.Put( *pCNd->GetpSwAttrSet() ); + if( aBrkSet.Count() ) + pFirstEmptyNd->SetAttr( aBrkSet ); + } + } + + if (pDoc->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 + pDoc->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 ); + + pDoc->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 = pDoc->GetNodes().MakeTextNode( aIdx, + GetTextFormatColl( FORM_TITLE ) ); + pHeadNd->InsertText( GetTitle(), SwIndex( pHeadNd ) ); + + SwSectionData headerData( SectionType::ToxHeader, GetTOXName()+"_Head" ); + + SwNodeIndex aStt( *pHeadNd ); --aIdx; + SwSectionFormat* pSectFormat = pDoc->MakeSectionFormat(); + pDoc->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 ); + SwNodeIndex aInsPos( *pFirstEmptyNd, 1 ); + for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt ) + { + ::SetProgressState( 0, pDoc->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 = pDoc->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, pDoc->GetDocShell() ); + + std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler = + std::make_shared<sw::DefaultToxTabStopTokenHandler>( + pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(), + pDoc->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(), 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 = pDoc->GetNodes().GoNext( &aEndIdx ); + if( pCNd ) // Robust against defect documents, e.g. i60336 + pCNd->SetAttr( *pFirstEmptyNd->GetpSwAttrSet() ); + } + } + + // now create the new Frames + sal_uLong nIdx = pSectNd->GetIndex(); + // don't delete if index is empty + if(nIdx + 2 < pSectNd->EndOfSectionIndex()) + pDoc->GetNodes().Delete( aInsPos ); + + aN2L.RestoreUpperFrames( pDoc->GetNodes(), nIdx, nIdx + 1 ); + o3tl::sorted_vector<SwRootFrame*> aAllLayouts = pDoc->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; +} + +/// Create from Marks +void SwTOXBaseSection::UpdateMarks( const SwTOXInternational& rIntl, + const SwTextNode* pOwnChapterNode, + SwRootFrame const*const pLayout) +{ + const SwTOXType* pType = static_cast<SwTOXType*>( SwTOXBase::GetRegisteredIn() ); + if( !pType->HasWriterListeners() ) + return; + + SwDoc* pDoc = GetFormat()->GetDoc(); + TOXTypes eTOXTyp = GetTOXType()->GetType(); + SwIterator<SwTOXMark,SwTOXType> aIter( *pType ); + + for (SwTOXMark* pMark = aIter.First(); pMark; pMark = aIter.Next()) + { + ::SetProgressState( 0, pDoc->GetDocShell() ); + + if (pMark->GetTOXType()->GetType() == eTOXTyp) + { + SwTextTOXMark *const pTextMark(pMark->GetTextTOXMark()); + if (nullptr == pTextMark) + continue; + const SwTextNode* pTOXSrc = pTextMark->GetpTextNd(); + // Only insert TOXMarks from the Doc, not from the + // UNDO. + + // If selected use marks from the same chapter only + if( pTOXSrc->GetNodes().IsDocNodes() && + pTOXSrc->GetText().getLength() && pTOXSrc->HasWriterListeners() && + pTOXSrc->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) && + (!IsFromChapter() || ::lcl_FindChapterNode(*pTOXSrc, pLayout) == pOwnChapterNode) && + !pTOXSrc->IsHiddenByParaField() && + !SwScriptInfo::IsInHiddenRange(*pTOXSrc, pTextMark->GetStart()) && + (!pLayout || !pLayout->IsHideRedlines() + || !sw::IsMarkHintHidden(*pLayout, *pTOXSrc, *pTextMark))) + { + if(TOX_INDEX == eTOXTyp) + { + // index entry mark + assert(g_pBreakIt); + lang::Locale aLocale = g_pBreakIt->GetLocale(pTOXSrc->GetLang(pTextMark->GetStart())); + + InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, *pTOXSrc, pTextMark, + GetOptions(), FORM_ENTRY, rIntl, aLocale )); + if(GetOptions() & SwTOIOptions::KeyAsEntry && + !pTextMark->GetTOXMark().GetPrimaryKey().isEmpty()) + { + InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, *pTOXSrc, pTextMark, + GetOptions(), FORM_PRIMARY_KEY, rIntl, aLocale )); + if (!pTextMark->GetTOXMark().GetSecondaryKey().isEmpty()) + { + InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, *pTOXSrc, pTextMark, + GetOptions(), FORM_SECONDARY_KEY, rIntl, aLocale )); + } + } + } + else if( TOX_USER == eTOXTyp || + pMark->GetLevel() <= GetLevel()) + { // table of content mark + // also used for user marks + InsertSorted(MakeSwTOXSortTabBase<SwTOXContent>(pLayout, *pTOXSrc, pTextMark, 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->IsHideRedlines() + || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) && + ( !IsFromChapter() || + ::lcl_FindChapterNode(*pTextNd, pLayout) == pOwnChapterNode )) + { + 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->IsHideRedlines() + || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) && + (!IsFromChapter() || pOwnChapterNode == + ::lcl_FindChapterNode(*pTextNd, pLayout))) + { + 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() || + ::lcl_FindChapterNode(rTextNode, pLayout) == pOwnChapterNode) + && (!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 + sal_uLong nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + 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->IsHideRedlines() + || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden) + && ( !IsFromChapter() || + ::lcl_FindChapterNode(*pCNd, pLayout) == pOwnChapterNode )) + { + std::unique_ptr<SwTOXPara> pNew( MakeSwTOXSortTabBase<SwTOXPara>( + pLayout, *pCNd, eMyType, + ( USHRT_MAX != nSetLevel ) + ? static_cast<sal_uInt16>(nSetLevel) + : FORM_ALPHA_DELIMITER ) ); + InsertSorted( std::move(pNew) ); + } + } + + nIdx = pNd->StartOfSectionNode()->EndOfSectionIndex() + 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->IsHideRedlines() + || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden) + && (!IsFromChapter() + || ::lcl_FindChapterNode(*pCNd, pLayout) == pOwnChapterNode)) + { + 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(static_cast<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::unique_ptr< std::vector<sal_uInt16> > xCharStyleIdx(pMainEntryNums ? new std::vector<sal_uInt16> : nullptr); + + OUString sSrchStr = OUStringChar(C_NUM_REPL) + 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 += S_PAGE_DELI; + else + aNumStr += "-"; + + aNumStr += aType.GetNumStr( nBeg + nCount ); + } + + // Create new String + nBeg = rNums[i]; + aNumStr += 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 += 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 += 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()) + { + // eventually the last index must me appended + if (xCharStyleIdx->size()&0x01) + xCharStyleIdx->push_back(aNumStr.getLength()); + + // search by name + SwDoc* pDoc = pNd->GetDoc(); + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( GetMainEntryCharStyle(), SwGetPoolIdFromName::ChrFmt ); + SwCharFormat* pCharFormat = nullptr; + if(USHRT_MAX != nPoolId) + pCharFormat = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(nPoolId); + else + pCharFormat = pDoc->FindCharFormatByName( GetMainEntryCharStyle() ); + if(!pCharFormat) + pCharFormat = pDoc->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 + 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.copy(1); + } + + OSL_ENSURE(rRange.Min() >= 0 && rRange.Max() >= 0, "Min Max < 0"); + + const long nMin = rRange.Min(); + const long nMax = rRange.Max(); + + 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 long nStart = i+1; + const 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 ); +} + +bool SwTOXBase::GetInfo( SfxPoolItem& rInfo ) const +{ + switch( rInfo.Which() ) + { + case RES_CONTENT_VISIBLE: + { + const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this); + if( pSect && pSect->GetFormat() ) + pSect->GetFormat()->GetInfo( rInfo ); + } + return false; + } + return true; +} + +/* 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..ea827d58d --- /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::makeAny; +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, makeAny( 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() ) + { + // 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..d9d22c25f --- /dev/null +++ b/sw/source/core/doc/extinput.cxx @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 *const pDoc = GetDoc(); + if (pDoc->IsInDtor()) { return; /* #i58606# */ } + + SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode(); + if( pTNd ) + { + SwIndex& rIdx = GetPoint()->nContent; + sal_Int32 nSttCnt = rIdx.GetIndex(); + sal_Int32 nEndCnt = GetMark()->nContent.GetIndex(); + if( nEndCnt != nSttCnt ) + { + // Prevent IME edited text being grouped with non-IME edited text. + bool bKeepGroupUndo = pDoc->GetIDocumentUndoRedo().DoesGroupUndo(); + pDoc->GetIDocumentUndoRedo().DoGroupUndo(false); + 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; + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::OVERWRITE, nullptr ); + pDoc->getIDocumentContentOperations().Overwrite( *this, sText.copy( 0, nOWLen ) ); + pDoc->getIDocumentContentOperations().InsertString( *this, sText.copy( nOWLen ) ); + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::OVERWRITE, nullptr ); + } + } + else + { + pTNd->ReplaceText( rIdx, nLen, m_sOverwriteText.copy( 0, nLen )); + if( m_bInsText ) + { + rIdx = nSttCnt; + pDoc->getIDocumentContentOperations().Overwrite( *this, sText ); + } + } + } + else + { + pTNd->EraseText( rIdx, nEndCnt - nSttCnt ); + + if( m_bInsText ) + { + pDoc->getIDocumentContentOperations().InsertString( *this, sText ); + } + } + pDoc->GetIDocumentUndoRedo().DoGroupUndo(bKeepGroupUndo); + if (m_eInputLanguage != LANGUAGE_DONTKNOW) + { + 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; + pDoc->getIDocumentContentOperations().InsertPoolItem(*this, aLangItem ); + } + + } + } + } +} + +void SwExtTextInput::SetInputData( const CommandExtTextInputData& rData ) +{ + SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode(); + if( pTNd ) + { + sal_Int32 nSttCnt = GetPoint()->nContent.GetIndex(); + sal_Int32 nEndCnt = GetMark()->nContent.GetIndex(); + if( nEndCnt < nSttCnt ) + { + std::swap(nSttCnt, nEndCnt); + } + + 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 ); + } + + pTNd->InsertText( rNewStr, aIdx, + SwInsertFlags::EMPTYEXPAND ); + 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) + { + 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() ) + { + 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 ) + { + sal_uLong nNdIdx = rNd.GetIndex(); + SwExtTextInput* pTmp = mpExtInputRing; + do { + sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(), + nMk = pTmp->GetMark()->nNode.GetIndex(); + sal_Int32 nPtCnt = pTmp->GetPoint()->nContent.GetIndex(); + sal_Int32 nMkCnt = pTmp->GetMark()->nContent.GetIndex(); + + if( nPt < nMk || ( nPt == nMk && nPtCnt < nMkCnt )) + { + sal_uLong nTmp = nMk; nMk = nPt; nPt = nTmp; + sal_Int32 nTmp2 = nMkCnt; nMkCnt = nPtCnt; nPtCnt = nTmp2; + } + + if( nMk <= nNdIdx && nNdIdx <= nPt && + ( nContentPos<0 || + ( nMkCnt <= nContentPos && nContentPos <= nPtCnt ))) + { + 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..ab39abf31 --- /dev/null +++ b/sw/source/core/doc/fmtcol.cxx @@ -0,0 +1,627 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/macros.h> +#include <osl/diagnose.h> +#include <hintids.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <doc.hxx> +#include <fmtcol.hxx> +#include <fmtcolfunc.hxx> +#include <hints.hxx> +#include <node.hxx> +#include <numrule.hxx> +#include <paratr.hxx> +#include <calbck.hxx> +#include <svl/intitem.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() ) + { + if (!pNewNumRuleItem) + { + (void)pTextFormatColl->GetItemState(RES_PARATR_NUMRULE, false, reinterpret_cast<const SfxPoolItem**>(&pNewNumRuleItem)); + } + 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(nullptr); + (void)rTextFormatColl.GetItemState(RES_PARATR_NUMRULE, false, reinterpret_cast<const SfxPoolItem**>(&pNumRuleItem)); + 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 + +void SwTextFormatColl::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( GetDoc()->IsInDtor() ) + { + SwFormatColl::Modify( pOld, pNew ); + 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; + + switch( pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ) + { + case RES_ATTRSET_CHG: + // Only recalculate if we're not the sender! + pNewChgSet = static_cast<const SwAttrSetChg*>(pNew); + pOldChgSet = static_cast<const SwAttrSetChg*>(pOld); + pNewChgSet->GetChgSet()->GetItemState( + RES_LR_SPACE, false, reinterpret_cast<const SfxPoolItem**>(&pNewLRSpace) ); + pNewChgSet->GetChgSet()->GetItemState( + RES_UL_SPACE, false, reinterpret_cast<const SfxPoolItem**>(&pNewULSpace) ); + pNewChgSet->GetChgSet()->GetItemState( RES_CHRATR_FONTSIZE, + false, reinterpret_cast<const SfxPoolItem**>(&(aFontSizeArr[0])) ); + pNewChgSet->GetChgSet()->GetItemState( RES_CHRATR_CJK_FONTSIZE, + false, reinterpret_cast<const SfxPoolItem**>(&(aFontSizeArr[1])) ); + pNewChgSet->GetChgSet()->GetItemState( RES_CHRATR_CTL_FONTSIZE, + false, reinterpret_cast<const SfxPoolItem**>(&(aFontSizeArr[2])) ); + // #i70223#, #i84745# + // check, if attribute set is applied to this paragraph style + if ( bAssignedToListLevelOfOutlineStyle && + pNewChgSet->GetTheChgdSet() == &GetAttrSet() ) + { + pNewChgSet->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false, + reinterpret_cast<const SfxPoolItem**>(&pNewNumRuleItem) ); + } + + 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 = static_cast<const SvxLRSpaceItem*>(pNew); + break; + case RES_UL_SPACE: + pNewULSpace = static_cast<const SvxULSpaceItem*>(pNew); + break; + case RES_CHRATR_FONTSIZE: + aFontSizeArr[0] = static_cast<const SvxFontHeightItem*>(pNew); + break; + case RES_CHRATR_CJK_FONTSIZE: + aFontSizeArr[1] = static_cast<const SvxFontHeightItem*>(pNew); + break; + case RES_CHRATR_CTL_FONTSIZE: + aFontSizeArr[2] = static_cast<const SvxFontHeightItem*>(pNew); + break; + // #i70223# + case RES_PARATR_NUMRULE: + if (bAssignedToListLevelOfOutlineStyle) + { + pNewNumRuleItem = static_cast<const SwNumRuleItem*>(pNew); + } + break; + default: + break; + } + + // #i70223# + if ( bAssignedToListLevelOfOutlineStyle && pNewNumRuleItem ) + { + TextFormatCollFunc::CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle( + this, pNewNumRuleItem ); + } + + bool bContinue = true; + + // Check against the own attributes + if( pNewLRSpace && SfxItemState::SET == GetItemState( RES_LR_SPACE, false, + reinterpret_cast<const SfxPoolItem**>(&pOldLRSpace) )) + { + if( pOldLRSpace != pNewLRSpace ) // Avoid recursion (SetAttr!) + { + bool bChg = false; + SvxLRSpaceItem aNew( *pOldLRSpace ); + // We had a relative value -> recalculate + if( 100 != aNew.GetPropLeft() ) + { + 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() ) + { + 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 && SfxItemState::SET == GetItemState( + RES_UL_SPACE, false, reinterpret_cast<const SfxPoolItem**>(&pOldULSpace) ) && + 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::Modify( pOld, pNew ); +} + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + GetAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +void SwTextFormatColls::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColls")); + for (size_t i = 0; i < size(); ++i) + GetFormat(i)->dumpAsXml(pWriter); + 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<SwModify*>(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, + static_cast<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..07250b97e --- /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 +{ + sal_uLong nIdxLHS = SwTextFootnote_GetIndex( lhs ); + sal_uLong 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* pDoc = rStt.GetNode().GetDoc(); + if( pDoc->IsInReading() ) + return ; + SwTextFootnote* pTextFootnote; + + const SwEndNoteInfo& rEndInfo = pDoc->GetEndNoteInfo(); + const SwFootnoteInfo& rFootnoteInfo = pDoc->GetFootnoteInfo(); + IDocumentRedlineAccess const& rIDRA(pDoc->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 = pDoc->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 = pDoc->GetNodes().GetOutLineNds(); + const SwNode *pChapterStartHidden(&pDoc->GetNodes().GetEndOfExtras()); + sal_uLong nChapterStart(pChapterStartHidden->GetIndex()); + sal_uLong nChapterEnd(pDoc->GetNodes().GetEndOfContent().GetIndex()); + sal_uLong 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 ]; + sal_uLong 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; + sal_uLong 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* pDoc = const_cast<SwDoc*>((*this)[ 0 ]->GetTextNode().GetDoc()); + SwTextFootnote* pTextFootnote; + const SwEndNoteInfo& rEndInfo = pDoc->GetEndNoteInfo(); + const SwFootnoteInfo& rFootnoteInfo = pDoc->GetFootnoteInfo(); + IDocumentRedlineAccess const& rIDRA(pDoc->getIDocumentRedlineAccess()); + + SwUpdFootnoteEndNtAtEnd aNumArr; + + SwRootFrame const* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + o3tl::sorted_vector<SwRootFrame*> aAllLayouts = pDoc->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 = pDoc->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 ) + { + sal_uLong 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 +{ + sal_uLong 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; + sal_uLong 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..8a7105c43 --- /dev/null +++ b/sw/source/core/doc/gctable.cxx @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rFormat.GetItemState( RES_BOX, true, &pItem ) ) + { + const SvxBorderLine* pBrd = static_cast<const SvxBoxItem*>(pItem)->GetLeft(); + if( pBrd ) + { + if( *pBrdLn == *pBrd ) + bAnyBorderFnd = 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 SfxPoolItem* pItem; + const SvxBorderLine* pBrd; + const SwTableBox& rBox = rCollTLB.GetBox( rStt, &nPos ); + + if( SfxItemState::SET != rBox.GetFrameFormat()->GetItemState(RES_BOX,true, &pItem ) ) + break; + pBrd = GetLineTB( static_cast<const SvxBoxItem*>(pItem), bTop ); + if( !pBrd || *pBrd != rBrdLn ) + break; + nLastPos = nPos; + } + return nLastPos; +} + +static const SvxBorderLine* lcl_GCBorder_GetBorder( const SwTableBox& rBox, + bool bTop, + const SfxPoolItem** ppItem ) +{ + return SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( RES_BOX, true, ppItem ) + ? GetLineTB( static_cast<const SvxBoxItem*>(*ppItem), bTop ) + : nullptr; +} + +static void lcl_GCBorder_DelBorder( const SwCollectTableLineBoxes& rCollTLB, + size_t& rStt, bool bTop, + const SvxBorderLine& rLine, + const SfxPoolItem* 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( *static_cast<const SvxBoxItem*>(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 SfxPoolItem* pItem; + 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( SfxItemState::SET == pBox->GetFrameFormat()->GetItemState( RES_BOX, true, &pItem ) ) + { + pBrd = static_cast<const SvxBoxItem*>(pItem)->GetRight(); + if( pBrd ) + { + aBPara.SetBorder( *pBrd ); + const SwTableBox* pNextBox = rBoxes[n+1]; + if( lcl_GCBorder_ChkBoxBrd_B( pNextBox, &aBPara ) && + aBPara.IsAnyBorderFound() ) + { + SvxBoxItem aBox( *static_cast<const SvxBoxItem*>(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 SfxPoolItem *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 ); + + // 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..f791e7d34 --- /dev/null +++ b/sw/source/core/doc/htmltbl.cxx @@ -0,0 +1,1768 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> +#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 nRow; // start row + sal_uInt16 nCol; // start column + sal_uInt16 nColSpan; // the column's COLSPAN + + std::unique_ptr<SwHTMLTableLayoutConstraints> pNext; // the next constraint + + sal_uLong nMinNoAlign, 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 nMinNoAlign; } + sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; } + + SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt ); + SwHTMLTableLayoutConstraints* GetNext() const { return pNext.get(); } + + sal_uInt16 GetColSpan() const { return nColSpan; } + sal_uInt16 GetColumn() const { return nCol; } +}; + +} + +SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd, + std::shared_ptr<SwHTMLTableLayout> const& rTab, + bool bNoBrTag, + std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) : + xNext( rNxt ), pBox( nullptr ), xTable( rTab ), pStartNode( pSttNd ), + nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag ) +{} + +const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const +{ + return pBox ? pBox->GetSttNd() : pStartNode; +} + +SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts, + sal_uInt16 nRSpan, sal_uInt16 nCSpan, + sal_uInt16 nWidth, bool bPercentWidth, + bool bNWrapOpt ) : + xContents(rCnts), + nRowSpan( nRSpan ), nColSpan( nCSpan ), + nWidthOption( nWidth ), bPercentWidthOption( bPercentWidth ), + bNoWrapOption( bNWrapOpt ) +{} + +SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth, + bool bRelWidth, + bool bLBorder ) : + nMinNoAlign(MINLAY), nMaxNoAlign(MINLAY), nAbsMinNoAlign(MINLAY), + nMin(0), nMax(0), + nAbsColWidth(0), nRelColWidth(0), + nWidthOption( nWidth ), bRelWidthOption( bRelWidth ), + bLeftBorder( bLBorder ) +{} + +SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints( + sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRw, sal_uInt16 nColumn, sal_uInt16 nColSp ): + nRow( nRw ), nCol( nColumn ), nColSpan( nColSp ), + nMinNoAlign( nMin ), nMaxNoAlign( nMax ) +{} + +SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext( + SwHTMLTableLayoutConstraints *pNxt ) +{ + SwHTMLTableLayoutConstraints *pPrev = nullptr; + SwHTMLTableLayoutConstraints *pConstr = this; + while( pConstr ) + { + if( pConstr->nRow > pNxt->nRow || + pConstr->GetColumn() > pNxt->GetColumn() ) + break; + pPrev = pConstr; + pConstr = pConstr->GetNext(); + } + + if( pPrev ) + { + pNxt->pNext = std::move(pPrev->pNext); + pPrev->pNext.reset( pNxt ); + pConstr = this; + } + else + { + pNxt->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_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 static_cast<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 static_cast<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; + long nRightOffset = 0, + nLeftOffset = 0; + rTabFrame.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset ); + nWidth -= (nLeftOffset + nRightOffset); + + return static_cast<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, sal_uLong 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 *pDoc = pSttNd->GetDoc(); + sal_uLong nIdx = pSttNd->GetIndex(); + while( !(pDoc->GetNodes()[nIdx])->IsEndNode() ) + { + SwTextNode *pTextNd = (pDoc->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 = (pDoc->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 ) + { + 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 = + static_cast<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 = static_cast<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail); + m_nRelRightFill = static_cast<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 = static_cast<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( + static_cast<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) ); + pColumn->SetRelColWidth( + static_cast<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) ); + } + else + { + double nColMinD = nColMin; + pColumn->SetAbsColWidth( + static_cast<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) ); + pColumn->SetRelColWidth( + static_cast<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( + static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); + pColumn->SetRelColWidth( + static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) ); + } + else + { + double nColMinD = nColMin; + pColumn->SetAbsColWidth( + static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) ); + pColumn->SetRelColWidth( + static_cast<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 = static_cast<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 = static_cast<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( static_cast<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( static_cast<sal_uInt16>(nRelColWidth) ); + + nAbs = nAbs + static_cast<sal_uInt16>(nColMax); + nRel = nRel + static_cast<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( + static_cast<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) ); + pColumn->SetRelColWidth( + static_cast<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( + static_cast<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) ); + GetColumn( i )->SetRelColWidth( + static_cast<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 = static_cast<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 = static_cast<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( static_cast<sal_uInt16>(nAbsColWidth) ); + GetColumn( i )->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) ); + nAbs = nAbs + static_cast<sal_uInt16>(nAbsColWidth); + nRel = nRel + static_cast<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) ) + { + // Calculate the width of additional cells we use for + // aligning inner tables. + sal_uInt16 nAbsDist = static_cast<sal_uInt16>(nAbsAvail-nAbsTabWidth); + sal_uInt16 nRelDist = static_cast<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() ) + { + 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 *pDoc = 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( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() ) + { + const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( *pDoc ); + 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..9922e3aa1 --- /dev/null +++ b/sw/source/core/doc/lineinfo.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 <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentState.hxx> +#include <lineinfo.hxx> +#include <charfmt.hxx> +#include <poolfmt.hxx> +#include <rootfrm.hxx> +#include <set> + +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( MM50 ), + 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* /*pNew*/ ) +{ + CheckRegistration( 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..a87570131 --- /dev/null +++ b/sw/source/core/doc/list.cxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vector> +#include <numrule.hxx> +#include <ndarr.hxx> +#include <node.hxx> +#include <pam.hxx> +#include <SwNodeNum.hxx> + +// implementation class for SwList +class SwListImpl +{ + public: + SwListImpl( const OUString& sListId, + SwNumRule& rDefaultListStyle, + const SwNodes& rNodes ); + ~SwListImpl() COVERITY_NOEXCEPT_FALSE; + + const OUString& GetListId() const { return msListId;} + + const OUString& GetDefaultListStyleName() const { return msDefaultListStyleName;} + + void InsertListItem( SwNodeNum& rNodeNum, bool isHiddenRedlines, + const int nLevel ); + static void RemoveListItem( SwNodeNum& rNodeNum ); + + void InvalidateListTree(); + void ValidateListTree(); + + void MarkListLevel( const int nListLevel, + const bool bValue ); + + bool IsListLevelMarked( const int nListLevel ) const; + + // unique identifier of the list + const OUString msListId; + // default list style for the list items, identified by the list style name + OUString msDefaultListStyleName; + + // list trees for certain document ranges + struct tListTreeForRange + { + /// tree always corresponds to document model + std::unique_ptr<SwNodeNum> pRoot; + /// Tree that is missing those nodes that are merged or hidden + /// by delete redlines; this is only used if there is a layout + /// that has IsHideRedlines() enabled. + /// A second tree is needed because not only are the numbers in + /// the nodes different, the structure of the tree may be different + /// as well, if a high-level node is hidden its children go under + /// the previous node on the same level. + /// The nodes of pRootRLHidden are a subset of the nodes of pRoot. + std::unique_ptr<SwNodeNum> pRootRLHidden; + /// top-level SwNodes section + std::unique_ptr<SwPaM> pSection; + tListTreeForRange(std::unique_ptr<SwNodeNum> p1, std::unique_ptr<SwNodeNum> p2, std::unique_ptr<SwPaM> p3) + : pRoot(std::move(p1)), pRootRLHidden(std::move(p2)), pSection(std::move(p3)) {} + }; + std::vector<tListTreeForRange> maListTrees; + + int mnMarkedListLevel; + + void NotifyItemsOnListLevel( const int nLevel ); +}; + +SwListImpl::SwListImpl( const OUString& sListId, + SwNumRule& rDefaultListStyle, + const SwNodes& rNodes ) + : msListId( sListId ), + msDefaultListStyleName( rDefaultListStyle.GetName() ), + maListTrees(), + mnMarkedListLevel( MAXLEVEL ) +{ + // create empty list trees for the document ranges + const SwNode* pNode = rNodes[0]; + do + { + SwPaM aPam( *pNode, *pNode->EndOfSectionNode() ); + + maListTrees.emplace_back( + 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()) + { + sal_uLong nIndex = pNode->GetIndex(); + nIndex++; + pNode = rNodes[nIndex]; + } + } + while ( pNode != &rNodes.GetEndOfContent() ); +} + +SwListImpl::~SwListImpl() COVERITY_NOEXCEPT_FALSE +{ + for ( auto& rNumberTree : maListTrees ) + { + SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRoot)); + SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRootRLHidden)); + } +} + +void SwListImpl::InsertListItem( SwNodeNum& rNodeNum, bool const isHiddenRedlines, + const int nLevel ) +{ + 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(isHiddenRedlines + ? rNumberTree.pRootRLHidden + : rNumberTree.pRoot); + pRoot->AddChild(&rNodeNum, nLevel); + break; + } + } +} + +void SwListImpl::RemoveListItem( SwNodeNum& rNodeNum ) +{ + rNodeNum.RemoveMe(); +} + +void SwListImpl::InvalidateListTree() +{ + for ( const auto& rNumberTree : maListTrees ) + { + rNumberTree.pRoot->InvalidateTree(); + rNumberTree.pRootRLHidden->InvalidateTree(); + } +} + +void SwListImpl::ValidateListTree() +{ + for ( auto& rNumberTree : maListTrees ) + { + rNumberTree.pRoot->NotifyInvalidChildren(); + rNumberTree.pRootRLHidden->NotifyInvalidChildren(); + } +} + +void SwListImpl::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 SwListImpl::IsListLevelMarked( const int nListLevel ) const +{ + return nListLevel == mnMarkedListLevel; +} + +void SwListImpl::NotifyItemsOnListLevel( const int nLevel ) +{ + for ( auto& rNumberTree : maListTrees ) + { + rNumberTree.pRoot->NotifyNodesOnListLevel( nLevel ); + rNumberTree.pRootRLHidden->NotifyNodesOnListLevel( nLevel ); + } +} + +SwList::SwList( const OUString& sListId, + SwNumRule& rDefaultListStyle, + const SwNodes& rNodes ) + : mpListImpl( new SwListImpl( sListId, rDefaultListStyle, rNodes ) ) +{ +} + +SwList::~SwList() +{ +} + +const OUString & SwList::GetListId() const +{ + return mpListImpl->GetListId(); +} + +const OUString & SwList::GetDefaultListStyleName() const +{ + return mpListImpl->GetDefaultListStyleName(); +} + +void SwList::SetDefaultListStyleName(OUString const& rNew) +{ + mpListImpl->msDefaultListStyleName = rNew; +} + +void SwList::InsertListItem( SwNodeNum& rNodeNum, bool const isHiddenRedlines, + const int nLevel ) +{ + mpListImpl->InsertListItem( rNodeNum, isHiddenRedlines, nLevel ); +} + +void SwList::RemoveListItem( SwNodeNum& rNodeNum ) +{ + SwListImpl::RemoveListItem( rNodeNum ); +} + +void SwList::InvalidateListTree() +{ + mpListImpl->InvalidateListTree(); +} + +void SwList::ValidateListTree() +{ + mpListImpl->ValidateListTree(); +} + +void SwList::MarkListLevel( const int nListLevel, + const bool bValue ) +{ + mpListImpl->MarkListLevel( nListLevel, bValue ); +} + +bool SwList::IsListLevelMarked( const int nListLevel ) const +{ + return mpListImpl->IsListLevelMarked( nListLevel ); +} + +/* 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..550f0df50 --- /dev/null +++ b/sw/source/core/doc/notxtfrm.cxx @@ -0,0 +1,1549 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <mdiexp.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 <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> + +// 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("Arial Unicode"); + 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( static_cast<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 ), + // RotateFlyFrame3 + mpTransformableSwFrame(), + // MM02 + mpViewContact() +{ + 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() ) + { + const SvxBrushItem *pItem; + const Color *pCol; + SwRect aOrigRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if ( rFrame.GetBackgroundBrush( aFillAttributes, pItem, pCol, 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( PushFlags::FILLCOLOR|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 ) ) + { + const SwFlyFreeFrame *pFly = dynamic_cast< const SwFlyFreeFrame* >( FindFlyFrame() ); + if( pFly ) + { + bool bGetUnclippedFrame=true; + const SfxPoolItem* pItem; + if( pFly->GetFormat() && SfxItemState::SET == pFly->GetFormat()->GetItemState(RES_BOX, false, &pItem) ) + { + const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem); + if( rBox.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.IsOver( 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. + 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()), long(1) ); + const double nScale = double(aFramePrintArea.Width()) / double(nLeftCrop); + nLeftCrop = long(nScale * -rCrop.GetLeft() ); + nRightCrop = long(nScale * -rCrop.GetRight() ); + } + + // crop values have to be mirrored too + if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both ) + { + 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()), long(1) ); + const double nScale = double(aFramePrintArea.Height()) / double(nTopCrop); + nTopCrop = long(nScale * -rCrop.GetTop() ); + nBottomCrop= long(nScale * -rCrop.GetBottom() ); + } + + // crop values have to be mirrored too + if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both ) + { + 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 ) + { + 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(static_cast< double >(-rSwRotationGrf.GetValue()) * (M_PI/1800.0)); + + return basegfx::normalizeToRange(fRotate, F_2PI); + } + } + + // 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(), -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.IsOver( 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + sal_uInt16 nWhich = pNew ? pNew->Which() : pOld ? pOld->Which() : 0; + + // #i73788# + // no <SwContentFrame::Modify(..)> for RES_LINKED_GRAPHIC_STREAM_ARRIVED + if ( RES_GRAPHIC_PIECE_ARRIVED != nWhich && + RES_GRAPHIC_ARRIVED != nWhich && + RES_GRF_REREAD_AND_INCACHE != nWhich && + RES_LINKED_GRAPHIC_STREAM_ARRIVED != nWhich ) + { + SwContentFrame::Modify( pOld, pNew ); + } + + bool bComplete = true; + + switch( nWhich ) + { + case RES_OBJECTDYING: + break; + + case RES_GRF_REREAD_AND_INCACHE: + if( SwNodeType::Grf == GetNode()->GetNodeType() ) + { + // TODO: Remove - due to GraphicObject refactoring + bComplete = false; + } + 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*>(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; + + case RES_GRAPHIC_PIECE_ARRIVED: + case RES_GRAPHIC_ARRIVED: + // i73788# - handle RES_LINKED_GRAPHIC_STREAM_ARRIVED as RES_GRAPHIC_ARRIVED + case RES_LINKED_GRAPHIC_STREAM_ARRIVED: + if ( GetNode()->GetNodeType() == SwNodeType::Grf ) + { + bComplete = false; + SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode()); + + ClearCache(); + + SwRect aRect( getFrameArea() ); + + SwViewShell *pVSh = pNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pVSh ) + break; + + for(SwViewShell& rShell : pVSh->GetRingContainer()) + { + SET_CURR_SHELL( &rShell ); + if( rShell.IsPreview() ) + { + if( rShell.GetWin() ) + ::RepaintPagePreview( &rShell, aRect ); + } + else if ( rShell.VisArea().IsOver( aRect ) && + OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType() ) + { + // invalidate instead of painting + rShell.GetWin()->Invalidate( aRect.SVRect() ); + } + } + } + break; + + default: + if ( !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, + uno::Sequence< beans::PropertyValue >()); + + // get a primitive processor for rendering + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( + drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rOutputDevice, aViewInformation2D) ); + if(pProcessor2D) + { + // 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()) + { + // tdf#114076: Expand ClipRange to next PixelBound + // Do this by going to basegfx::B2DRange, adding a + // single pixel size and using floor/ceil to go to + // full integer (as needed for pixels). Also need + // to go back to basegfx::B2DPolyPolygon for the + // creation of the needed MaskPrimitive2D. + // The general problem is that Writer is scrolling + // using blitting the unchanged parts, this forces + // this part of the scroll to pixel coordinate steps, + // while the ViewTransformation for paint nowadays has + // a sub-pixel precision. This results in an offset + // up to one pixel in radius. To solve this for now, + // we need to expand to the next outer pixel bound. + // Hopefully in the future we will someday be able to + // stay on the full available precision, but this + // will need a change in the repaint/scroll paradigm. + const basegfx::B2DRange aClipRange(aClip.getB2DRange()); + const basegfx::B2DVector aSinglePixelXY(rOutputDevice.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + const basegfx::B2DRange aExpandedClipRange( + floor(aClipRange.getMinX() - aSinglePixelXY.getX()), + floor(aClipRange.getMinY() - aSinglePixelXY.getY()), + ceil(aClipRange.getMaxX() + aSinglePixelXY.getX()), + ceil(aClipRange.getMaxY() + aSinglePixelXY.getY())); + + // create the enclosing rectangle as polygon + basegfx::B2DPolyPolygon aTarget(basegfx::utils::createPolygonFromRect(aExpandedClipRange)); + + // tdf#124272 the fix above (tdf#114076) was too rough - the + // clip region used may be a PolyPolygon. In that case that + // PolyPolygon would have to be scaled to mentioned PixelBounds. + // Since that is not really possible geometrically (would need + // more some 'grow in outside direction' but with unequal grow + // values in all directions - just maaany problems + // involved), use a graphical trick: The topology of the + // PolyPolygon uses the standard FillRule, so adding the now + // guaranteed to be bigger or equal bounding (enclosing) + // rectangle twice as polygon will expand the BoundRange, but + // not change the geometry visualization at all + if(!rOutputDevice.GetClipRegion().IsRectangle()) + { + // double the outer rectangle range polygon to have it + // included twice + aTarget.append(aTarget.getB2DPolygon(0)); + + // add the original clip 'inside' (due to being smaller + // or equal). That PolyPolygon may have an unknown number + // of polygons (>=1) + aTarget.append(aClip); + } + + drawinglayer::primitive2d::MaskPrimitive2D* pNew( + new drawinglayer::primitive2d::MaskPrimitive2D( + aTarget, + rContent)); + rContent.resize(1); + rContent[0] = pNew; + } + } + + if(!rName.isEmpty() || !rTitle.isEmpty() || !rDescription.isEmpty()) + { + // Embed to ObjectInfoPrimitive2D when we have Name/Title/Description + // information available + drawinglayer::primitive2d::ObjectInfoPrimitive2D* pNew( + new drawinglayer::primitive2d::ObjectInfoPrimitive2D( + rContent, + rName, + rTitle, + rDescription)); + rContent.resize(1); + rContent[0] = pNew; + } + + 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 drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequence( + const sdr::contact::DisplayInfo& rDisplayInfo) 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); +}; + +drawinglayer::primitive2d::Primitive2DContainer ViewObjectContactOfSwNoTextFrame::createPrimitive2DSequence( + const sdr::contact::DisplayInfo& /*rDisplayInfo*/) 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 + drawinglayer::primitive2d::Primitive2DContainer aContent(1); + aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D( + aGraphicTransform, + rGrfObj, + aGraphicAttr); + + return aContent; + } + + return drawinglayer::primitive2d::Primitive2DContainer(); +} + +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 +) +: sdr::contact::ViewContact(), + 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 (pShell->Imp()->GetDrawView()->IsAntiAliasing()) + { + pOut->SetAntialiasing( nFormerAntialiasingAtOutput | AntialiasingFlags::EnableB2dDraw ); + } + + 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(); + } + 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() + : 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 + { + const char* pResId = nullptr; + + 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 ( pShell->Imp()->GetDrawView()->IsAntiAliasing() ) + pOut->SetAntialiasing( nFormerAntialiasingAtOutput ); + } + else // bIsChart || pOLENd + { + // Fix for bug fdo#33781 + const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() ); + if (pShell->Imp()->GetDrawView()->IsAntiAliasing()) + { + AntialiasingFlags nNewAntialiasingAtOutput = nFormerAntialiasingAtOutput | AntialiasingFlags::EnableB2dDraw; + + // #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 (pShell->Imp()->GetDrawView()->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 .. F_2PI] + // to [0 .. 360] and check modulo 90 + const long nRot(static_cast<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( 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..1de08ae9f --- /dev/null +++ b/sw/source/core/doc/number.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 <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> + +using namespace ::com::sun::star; + +sal_uInt16 SwNumRule::mnRefCount = 0; +SwNumFormat* SwNumRule::maBaseFormats[ RULE_END ][ MAXLEVEL ] = { + {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } }; + +SwNumFormat* SwNumRule::maLabelAlignmentBaseFormats[ RULE_END ][ MAXLEVEL ] = { + {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } }; + +const sal_uInt16 SwNumRule::maDefNumIndents[ MAXLEVEL ] = { +//inch: 0,5 1,0 1,5 2,0 2,5 3,0 3,5 4,0 4,5 5,0 + 1440/4, 1440/2, 1440*3/4, 1440, 1440*5/4, 1440*3/2, 1440*7/4, 1440*2, + 1440*9/4, 1440*5/2 +}; + +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 + ? *maBaseFormats[ meRuleType ][ i ] + : *maLabelAlignmentBaseFormats[ 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 ) + { + 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 maDefNumIndents[ nLvl ]; +} + +sal_uInt16 SwNumRule::GetBullIndent( sal_uInt8 nLvl ) +{ + OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" ); + return maDefNumIndents[ 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + // Look for the NumRules object in the Doc where this NumFormat is set. + // The format does not need to exist! + const SwCharFormat* pFormat = nullptr; + sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + switch( nWhich ) + { + case RES_ATTRSET_CHG: + case RES_FMT_CHG: + pFormat = GetCharFormat(); + break; + } + + if( pFormat && !pFormat->GetDoc()->IsInDtor() ) + UpdateNumNodes( const_cast<SwDoc*>(pFormat->GetDoc()) ); + else + CheckRegistration( 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* pDoc ) +{ + bool bDocIsModified = pDoc->getIDocumentState().IsModified(); + bool bFnd = false; + for( SwNumRuleTable::size_type n = pDoc->GetNumRuleTable().size(); !bFnd && n; ) + { + const SwNumRule* pRule = pDoc->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 ) + pDoc->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 ) + : maTextNodeList(), + maParagraphStyleList(), + 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 ), + msDefaultListId() +{ + if( !mnRefCount++ ) // 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->SetSuffix( "." ); + pFormat->SetBulletChar( numfunc::GetBulletChar(n)); + SwNumRule::maBaseFormats[ NUM_RULE ][ n ] = pFormat; + } + // position-and-space mode LABEL_ALIGNMENT + // first line indent of general numbering in inch: -0,25 inch + const long cFirstLineIndent = -1440/4; + // indent values of general numbering in inch: + // 0,5 0,75 1,0 1,25 1,5 + // 1,75 2,0 2,25 2,5 2,75 + const long cIndentAt[ MAXLEVEL ] = { + 1440/2, 1440*3/4, 1440, 1440*5/4, 1440*3/2, + 1440*7/4, 1440*2, 1440*9/4, 1440*5/2, 1440*11/4 }; + 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->SetSuffix( "." ); + pFormat->SetBulletChar( numfunc::GetBulletChar(n)); + SwNumRule::maLabelAlignmentBaseFormats[ 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::maBaseFormats[ 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::maLabelAlignmentBaseFormats[ OUTLINE_RULE ][ n ] = pFormat; + } + } + OSL_ENSURE( !msName.isEmpty(), "NumRule without a name!" ); +} + +SwNumRule::SwNumRule( const SwNumRule& rNumRule ) + : maTextNodeList(), + maParagraphStyleList(), + 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 ) +{ + ++mnRefCount; + 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( !--mnRefCount ) // the last one closes the door (?) + { + // Numbering: + SwNumFormat** ppFormats = &SwNumRule::maBaseFormats[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::maLabelAlignmentBaseFormats[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* pDoc ) +{ + for(auto& rpNumFormat : maFormats) + { + if( rpNumFormat ) + { + SwCharFormat* pFormat = rpNumFormat->GetCharFormat(); + if( pFormat && pFormat->GetDoc() != pDoc ) + { + // copy + SwNumFormat* pNew = new SwNumFormat( *rpNumFormat ); + pNew->SetCharFormat( pDoc->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; +} + +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 bool bOnlyArabic, + 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; + } + + if (nLevel < MAXLEVEL) + { + const SwNumFormat& rMyNFormat = Get( static_cast<sal_uInt16>(nLevel) ); + + { + css::lang::Locale aLocale( LanguageTag::convertToLocale(nLang)); + + if (rMyNFormat.HasListFormat()) + { + OUString sLevelFormat = rMyNFormat.GetListFormat(); + // 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; + if (rNumVector[i]) + { + if (bOnlyArabic) + sReplacement = OUString::number(rNumVector[i]); + else + 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); + } + + // As a fallback: caller code expects nonempty string as a result. + // But if we have empty string (and had no errors before) this is valid result. + // So use classical hack with zero-width-space as a string filling. + if (sLevelFormat.isEmpty()) + sLevelFormat = OUStringChar(CHAR_ZWSP); + + 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]) + { + if (bOnlyArabic) + aStr.append(OUString::number(rNumVector[i])); + else + 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 && !bOnlyArabic && + 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( static_cast<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, false, 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( static_cast<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 ); + } + + 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* pDoc, const SwNumRule& rNumRule ) +{ + for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) + { + Set( n, rNumRule.maFormats[ n ].get() ); + if( maFormats[ n ] && maFormats[ n ]->GetCharFormat() && + !pDoc->GetCharFormats()->IsAlive(maFormats[n]->GetCharFormat())) + { + // If we copy across different Documents, then copy the + // corresponding CharFormat into the new Document. + maFormats[n]->SetCharFormat( pDoc->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 ) + { + SwNumFormat aNumFormat = Get(n); + if(aNumFormat.GetCharFormat()) + aNumFormat.SetCharFormatName(aNumFormat.GetCharFormat()->GetName()); + aRule.SetLevel(n, aNumFormat, 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 long nNewListTab = aTmpNumFormat.GetListtabPos() + nDiff; + aTmpNumFormat.SetListtabPos( nNewListTab ); + } + + const 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 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() +{ + o3tl::sorted_vector< SwList* > aLists; + for ( const SwTextNode* pTextNode : maTextNodeList ) + { + aLists.insert( pTextNode->GetDoc()->getIDocumentListsAccess().getListByName( pTextNode->GetListId() ) ); + } + for ( auto aList : aLists ) + aList->ValidateListTree(); + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRule")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("msName"), BAD_CAST(msName.toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mnPoolFormatId"), BAD_CAST(OString::number(mnPoolFormatId).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAutoRuleFlag"), BAD_CAST(OString::boolean(mbAutoRuleFlag).getStr())); + + for (const auto& pFormat : maFormats) + { + if (!pFormat) + { + continue; + } + + pFormat->dumpAsXml(pWriter); + } + + 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); +} + +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::unique_ptr<vcl::Font> mpFont; + }; + + class theSwDefBulletConfig + : public rtl::Static<SwDefBulletConfig, theSwDefBulletConfig>{}; + } + + SwDefBulletConfig& SwDefBulletConfig::getInstance() + { + return theSwDefBulletConfig::get(); + } + + 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() ) + { + 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.reset( new vcl::Font( 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; + }; + + class theSwNumberingUIBehaviorConfig : public rtl::Static<SwNumberingUIBehaviorConfig, theSwNumberingUIBehaviorConfig>{}; + } + + SwNumberingUIBehaviorConfig& SwNumberingUIBehaviorConfig::getInstance() + { + return theSwNumberingUIBehaviorConfig::get(); + } + + 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() ) + { + 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(); + } + + SvxNumberFormat::SvxNumPositionAndSpaceMode GetDefaultPositionAndSpaceMode() + { + if (utl::ConfigManager::IsFuzzing()) + return SvxNumberFormat::LABEL_ALIGNMENT; + + SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode; + SvtSaveOptions aSaveOptions; + switch (aSaveOptions.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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleTable")); + for (SwNumRule* pNumRule : *this) + pNumRule->dumpAsXml(pWriter); + 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..326e05eaf --- /dev/null +++ b/sw/source/core/doc/poolfmt.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 <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 <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 SwModify& 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 ) +{ + bool bUsed = rRule.GetTextNodeListSize() > 0 || + rRule.GetParagraphStyleListSize() > 0 || + rRule.IsUsedByRedline(); + + 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_JAKETADRESS: + case RES_POOLCOLL_SENDADRESS: + 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..d404b477e --- /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..06ac05856 --- /dev/null +++ b/sw/source/core/doc/sortopt.cxx @@ -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 . + */ + +#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) : + eDirection( rOpt.eDirection ), + cDeli( rOpt.cDeli ), + nLanguage( rOpt.nLanguage ), + bTable( rOpt.bTable ), + bIgnoreCase( rOpt.bIgnoreCase ) +{ + for(auto const & pKey : rOpt.aKeys) + { + aKeys.push_back( std::make_unique<SwSortKey>(*pKey) ); + } +} + +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..95b3982ea --- /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 <bookmrk.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( OUString(), OUString(), xWrt ); + break; + + case SotClipboardFormatId::RTF: + case SotClipboardFormatId::RICHTEXT: + // mba: no BaseURL for data exchange + ::GetRTFWriter( OUString(), 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() ) + { + 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 ) + { + sal_uLong 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() ) + { + 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 +{ + sal_uLong 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_pDoc( rPam.GetDoc() ) +{ + m_nContent = rPam.GetPoint()->nContent.GetIndex(); +} + +SwDataChanged::SwDataChanged( SwDoc* pDc, const SwPosition& rPos ) + : m_pPam( nullptr ), m_pPos( &rPos ), m_pDoc( pDc ) +{ + m_nContent = rPos.nContent.GetIndex(); +} + +SwDataChanged::~SwDataChanged() +{ + // JP 09.04.96: Only if the Layout is available (thus during input) + if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + const ::sfx2::SvLinkSources& rServers = m_pDoc->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() && dynamic_cast<const SwServerObject*>( refObj.get() ) != nullptr) + { + SwServerObject& rObj = *static_cast<SwServerObject*>( refObj.get() ); + if( m_pPos ) + rObj.SendDataChanged( *m_pPos ); + else + rObj.SendDataChanged( *m_pPam ); + } + + // We shouldn't have a connection anymore + if( !refObj->HasDataLinks() ) + { + // Then remove from the list + m_pDoc->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..cfca5ee5b --- /dev/null +++ b/sw/source/core/doc/swstylemanager.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 aAutoCharPool; + StylePool 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 ) + : aAutoCharPool(), + 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 ? aAutoCharPool : 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 ? aAutoCharPool : 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 ? aAutoCharPool : 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 ? aAutoCharPool : 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..b1301b2f1 --- /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..293e87898 --- /dev/null +++ b/sw/source/core/doc/tblafmt.cxx @@ -0,0 +1,1251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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/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::pDfltBoxAutoFormat = nullptr; + +#define AUTOTABLE_FORMAT_NAME "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) : _rStream(rStream), _whereToWriteEndOfBlock(BeginSwBlock(rStream)) + { + } + + ~WriterSpecificAutoFormatBlock() + { + EndSwBlock(_rStream, _whereToWriteEndOfBlock); + } + + private: + WriterSpecificAutoFormatBlock(WriterSpecificAutoFormatBlock const&) = delete; + WriterSpecificAutoFormatBlock& operator=(WriterSpecificAutoFormatBlock const&) = delete; + + SvStream &_rStream; + sal_uInt64 _whereToWriteEndOfBlock; + }; + + /// 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() +: AutoFormatVersions(), + 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() +: AutoFormatBase(), + 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_sNumFormatString(), + m_eSysLanguage(::GetAppLanguage()), + m_eNumFormatLanguage(::GetAppLanguage()), + m_wXObject() +{ + // 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>(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, 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 ), + m_wXObject() +{ +} + +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_aBreak(std::make_shared<SvxFormatBreakItem>(SvxBreak::NONE, RES_BREAK)) + , 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_aBreak() + , m_aKeepWithNextPara() + , 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_aBreak.reset(rNew.m_aBreak->Clone()); + m_aPageDesc = rNew.m_aPageDesc; + 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( !pDfltBoxAutoFormat ) + pDfltBoxAutoFormat = new SwBoxAutoFormat; + return *pDfltBoxAutoFormat; + } +} + +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( !pDfltBoxAutoFormat ) + pDfltBoxAutoFormat = new SwBoxAutoFormat(); + *pFormat = new SwBoxAutoFormat(*pDfltBoxAutoFormat); + } + return **pFormat; +} + +const SwBoxAutoFormat& SwTableAutoFormat::GetDefaultBoxFormat() +{ + if(!pDfltBoxAutoFormat) + pDfltBoxAutoFormat = new SwBoxAutoFormat(); + + return *pDfltBoxAutoFormat; +} + +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 ) + { + 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( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, true, + reinterpret_cast<const SfxPoolItem**>(&pNumFormatItem) ) && pNFormatr && + 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 ) + { + 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 ) + { + 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); + + if ( m_aBreak->GetBreak() != SvxBreak::NONE ) + rSet.Put(*m_aBreak); + rSet.Put(m_aPageDesc); + 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); + + 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 = SwDoc::GetRowSplit(*pShell->getShellCursor(false)); + m_bRowSplit = pRowSplit && pRowSplit->GetValue(); + pRowSplit.reset(); + + const SfxItemSet &rSet = pFormat->GetAttrSet(); + + m_aBreak.reset(rSet.Get(RES_BREAK).Clone()); + m_aPageDesc = rSet.Get(RES_PAGEDESC); + 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)) + { + legacy::SvxFormatBreak::Create(*m_aBreak, rStream, AUTOFORMAT_FILE_VERSION); +//unimplemented READ(m_aPageDesc, SwFormatPageDesc, 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); + + legacy::SvxFormatBreak::Store(*m_aBreak, rStream, legacy::SvxFormatBreak::GetVersion(fileVersion)); +//unimplemented m_aPageDesc.Store(rStream, m_aPageDesc.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( !pDfltBoxAutoFormat ) + pDfltBoxAutoFormat = new SwBoxAutoFormat; + pFormat = pDfltBoxAutoFormat; + } + 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(const OUString& 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, DEF_LINE_WIDTH_5 ); + 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; + long nPos = rStream.Tell(); + rStream.ReadUChar( nCnt ).ReadUChar( nChrSet ); + if( rStream.Tell() != sal_uLong(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.Flush(); + 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(const OUString& 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(const OUString& 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..164a33ae5 --- /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/zforlist.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 <docary.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() ) + { + 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, static_cast<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 = static_cast<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 ) + { + 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 ) + { + const SwTableLines &rLines = rTable.GetTabLines(); + const sal_uInt16 nLineCount = rLines.size(); + if( nLineCount < mnAddLine ) + mnAddLine = nLineCount; + sal_uInt16 nLine = static_cast<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(); + 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(), 1, + *pCpyBox->GetSttNd()->EndOfSectionNode() ) : nullptr ); + + SwNodeIndex aInsIdx( *pDstBox->GetSttNd(), bDelContent ? 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 ) + { + 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( + static_cast<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 ) + { + SfxItemSet aBoxAttrSet( pCpyDoc->GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, + RES_BOXATR_VALUE>{} ); + aBoxAttrSet.Put( pCpyBox->GetFrameFormat()->GetAttrSet() ); + if( aBoxAttrSet.Count() ) + { + const SfxPoolItem* pItem; + SvNumberFormatter* pN = pDoc->GetNumberFormatter( false ); + if( pN && pN->HasMergeFormatTable() && SfxItemState::SET == aBoxAttrSet. + GetItemState( RES_BOXATR_FORMAT, false, &pItem ) ) + { + sal_uLong nOldIdx = static_cast<const SwTableBoxNumFormat*>(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 ); + + if( nullptr == (pTmp = pCpyBox->FindNextBox( rCpyTable, pCpyBox, false ))) + break; // no more Boxes + pCpyBox = pTmp; + + if( nullptr == ( pTmp = pMyBox->FindNextBox( *this, pMyBox, false ))) + 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..5eb2ff999 --- /dev/null +++ b/sw/source/core/doc/tblrwcl.cxx @@ -0,0 +1,3403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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* pDoc; + 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 ) + : pDoc( 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 ), pDoc(rPara.pDoc), 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 ), pDoc(rPara.pDoc), 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 void 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()); + + sal_uInt16 nFndPos; + if( pCpyPara->nCpyCnt ) + { + 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(); + 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->pDoc, 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 void 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; +} + +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* pDoc, 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( pDoc, 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 = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD && nCnt) + pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind ); + pDoc->UpdateCharts( GetFrameFormat()->GetName() ); + + pDoc->GetDocShell()->GetFEShell()->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()) + lcl_CopyRow( *rpFndLine, &aCpyPara ); + } + + // 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() ); + + pDoc->GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + return true; +} + +static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const long nOffset, + bool bFirst, SwShareBoxFormats& rShareFormats ); + +static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const long nOffset, + bool bFirst, SwShareBoxFormats& rShareFormats ) +{ + for ( auto pLine : rLines ) + ::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats ); +} + +static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const 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 < static_cast<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 < static_cast<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 + if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() ) + 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 + if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() ) + 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 + if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() ) + 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 < static_cast<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 < static_cast<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() ) + { + 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* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, + bool bSameHeight ) +{ + OSL_ENSURE( pDoc && !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 + pDoc->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<long[]> pRowHeights; + if ( bSameHeight ) + { + pRowHeights.reset(new 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; + { + sal_uLong nSttNd = pLastBox->GetSttIdx() + 1, + nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex(); + while( nSttNd < nEndNd ) + if( !pDoc->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( pDoc, 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()+2 != pEndNd->GetIndex() ) + { + // Move TextNodes + SwNodeRange aRg( *pLastBox->GetSttNd(), +2, *pEndNd ); + pLastBox = pNewLine->GetTabBoxes()[0]; // reset + SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 ); + pDoc->GetNodes().MoveNodes(aRg, pDoc->GetNodes(), aInsPos, false); + pDoc->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* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt ) +{ + OSL_ENSURE( pDoc && !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 + pDoc->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( pDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat, + pSelBox, nBoxPos + i ); // insert after + + ::InsTableBox( pDoc, 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" ); + + 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 + if( 0 != ( nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox ) ) ) + 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 < static_cast<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()) + { + 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 < static_cast<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 < static_cast<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 long nLineCount = static_cast<long>(rTable.GetTabLines().size()); + long nMaxSpan = nLineCount; + long nMinSpan = 1; + while( nMaxSpan ) + { + SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ]; + for( auto pBox : pLine->GetTabBoxes() ) + { + long 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 + static_cast<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 + static_cast<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 ) + { + 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->pDoc->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->pDoc->GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine, + aFindFrame.pNewFrameFormat, + pCpyPara->pDoc->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 + { + SfxItemSet aBoxAttrSet( pCpyPara->pDoc->GetAttrPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet()); + if( aBoxAttrSet.Count() ) + { + const SfxPoolItem* pItem; + SvNumberFormatter* pN = pCpyPara->pDoc->GetNumberFormatter( false ); + if( pN && pN->HasMergeFormatTable() && SfxItemState::SET == aBoxAttrSet. + GetItemState( RES_BOXATR_FORMAT, false, &pItem ) ) + { + sal_uLong nOldIdx = static_cast<const SwTableBoxNumFormat*>(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(), 1, + *rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() ); + SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 ); + + pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx, nullptr, false); + // Delete the initial TextNode + pCpyPara->pDoc->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->pDoc->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* pInsDoc, const SwPosition& rPos, + const SwSelBoxes& rSelBoxes, + bool bCpyName ) 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 != pInsDoc ) + { + pInsDoc->CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) ); + pInsDoc->CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) ); + } + + SwTable* pNewTable = const_cast<SwTable*>(pInsDoc->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()); + + 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 = pInsDoc->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, *pInsDoc ); + + // 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 != static_cast<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 >= static_cast<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 >= static_cast<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< unsigned long >(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(); + + std::unique_ptr<FndBox_> xFndBox; // for insertion/deletion + 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 )); + + 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( xFndBox ) + { + // Clean up the structure of all Lines + GCLines(); + + // Update Layout + if( !bBigger || xFndBox->AreLinesToRestore( *this ) ) + xFndBox->MakeFrames( *this ); + + // TL_CHART2: it is currently unclear if sth has to be done here. + // The function name hints that nothing needs to be done, on the other + // hand there is a case where sth gets deleted. :-( + + xFndBox.reset(); + } + +#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 = 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(); + + std::unique_ptr<FndBox_> xFndBox; // for insertion/deletion + 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; + xFndBox.reset(); + } + } + } + break; + default: break; + } + + if( xFndBox ) + { + // then clean up the structure of all Lines + GCLines(); + + // Update Layout + if( bBigger || xFndBox->AreLinesToRestore( *this ) ) + xFndBox->MakeFrames( *this ); + + // TL_CHART2: it is currently unclear if sth has to be done here. + + xFndBox.reset(); + } + + CHECKTABLELAYOUT + + return bRet; +} + +SwFrameFormat* SwShareBoxFormat::GetFormat( long nWidth ) const +{ + SwFrameFormat *pRet = nullptr, *pTmp; + for( auto n = aNewFormats.size(); n; ) + if( ( pTmp = 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 = pOldFormat->GetFormatAttr( RES_FRM_SIZE, false ); + for( auto n = aNewFormats.size(); n; ) + if( SfxItemState::SET == ( pTmp = 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 ) +{ + aNewFormats.push_back( &rNew ); +} + +bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat ) +{ + // returns true, if we can delete + if( pOldFormat == &rFormat ) + return true; + + std::vector<SwFrameFormat*>::iterator it = std::find( aNewFormats.begin(), aNewFormats.end(), &rFormat ); + if( aNewFormats.end() != it ) + aNewFormats.erase( it ); + return aNewFormats.empty(); +} + +SwShareBoxFormats::~SwShareBoxFormats() +{ +} + +SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, 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; + SwShareBoxFormat* pEntry; + if( !Seek_Entry( rOld, &nPos )) + { + pEntry = new SwShareBoxFormat( rOld ); + m_ShareArr.insert(m_ShareArr.begin() + nPos, std::unique_ptr<SwShareBoxFormat>(pEntry)); + } + else + pEntry = m_ShareArr[ nPos ].get(); + + pEntry->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..9a2cc95dc --- /dev/null +++ b/sw/source/core/doc/textboxhelper.cxx @@ -0,0 +1,774 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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 <frmfmt.hxx> +#include <frameformats.hxx> + +#include <editeng/unoprnms.hxx> +#include <editeng/memberids.h> +#include <svx/svdoashp.hxx> +#include <svx/svdpage.hxx> +#include <svl/itemiter.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <sal/log.hxx> +#include <svx/anchorid.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/TextContentAnchorType.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/text/WritingMode.hpp> +#include <com/sun/star/text/WritingMode2.hpp> + +using namespace com::sun::star; + +void SwTextBoxHelper::create(SwFrameFormat* pShape) +{ + // If TextBox wasn't enabled previously + if (pShape->GetAttrSet().HasItem(RES_CNTNT)) + return; + + // 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); + try + { + SdrObject* pSourceSDRShape = pShape->FindRealSdrObject(); + uno::Reference<text::XTextContent> XSourceShape(pSourceSDRShape->getUnoShape(), + uno::UNO_QUERY_THROW); + xTextContentAppend->insertTextContentWithProperties( + xTextFrame, uno::Sequence<beans::PropertyValue>(), XSourceShape->getAnchor()); + } + catch (uno::Exception&) + { + 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)); + + pShape->SetOtherTextBoxFormat(pFormat); + pFormat->SetOtherTextBoxFormat(pShape); + + // Initialize properties. + uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY); + uno::Any aEmptyBorder = uno::makeAny(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::makeAny(sal_Int32(100))); + + xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::makeAny(text::SizeType::FIX)); + + xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH)); + + uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY); + xNamed->setName(pShape->GetDoc()->GetUniqueFrameName()); + + // 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); + } + + // Also initialize the properties, which are not constant, but inherited from the shape's ones. + uno::Reference<drawing::XShape> xShape(pShape->FindRealSdrObject()->getUnoShape(), + uno::UNO_QUERY); + syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::makeAny(xShape->getSize())); + + 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)); + syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT)); + syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION)); + syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT)); + syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION)); + syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION)); + syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION)); + syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, + xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT)); + syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, + xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)); + text::WritingMode eMode; + if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode) + syncProperty(pShape, RES_FRAMEDIR, 0, uno::makeAny(sal_Int16(eMode))); +} + +void SwTextBoxHelper::destroy(SwFrameFormat* pShape) +{ + // If a TextBox was enabled previously + if (pShape->GetAttrSet().HasItem(RES_CNTNT)) + { + SwFrameFormat* pFormat = pShape->GetOtherTextBoxFormat(); + + // Unlink the TextBox's text range from the original shape. + pShape->ResetFormatAttr(RES_CNTNT); + + // Delete the associated TextFrame. + if (pFormat) + pShape->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat); + } +} + +bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType) +{ + assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT); + if (!pFormat || pFormat->Which() != nType || !pFormat->GetAttrSet().HasItem(RES_CNTNT)) + return false; + + sal_uInt16 nOtherType = (pFormat->Which() == RES_FLYFRMFMT) ? sal_uInt16(RES_DRAWFRMFMT) + : sal_uInt16(RES_FLYFRMFMT); + SwFrameFormat* pOtherFormat = pFormat->GetOtherTextBoxFormat(); + if (!pOtherFormat) + return false; + + assert(pOtherFormat->Which() == nOtherType); + if (pOtherFormat->Which() != nOtherType) + return false; + + const SwFormatContent& rContent = pFormat->GetContent(); + return pOtherFormat->GetAttrSet().HasItem(RES_CNTNT) && pOtherFormat->GetContent() == rContent; +} + +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* pDoc) +{ + sal_Int32 nRet = 0; + const SwFrameFormats& rSpzFrameFormats = *pDoc->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::makeAny(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) +{ + if (!isTextBox(pFormat, nType)) + return nullptr; + return pFormat->GetOtherTextBoxFormat(); +} + +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); +} + +template <typename T> static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny) +{ + if (SwFrameFormat* pFormat = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + { + 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) +{ + uno::Any aRet; + + if (rType == cppu::UnoType<css::text::XTextAppend>::get()) + { + lcl_queryInterface<text::XTextAppend>(pShape, aRet); + } + else if (rType == cppu::UnoType<css::text::XText>::get()) + { + lcl_queryInterface<text::XText>(pShape, aRet); + } + else if (rType == cppu::UnoType<css::text::XTextRange>::get()) + { + lcl_queryInterface<text::XTextRange>(pShape, aRet); + } + + return aRet; +} + +tools::Rectangle SwTextBoxHelper::getTextRectangle(SwFrameFormat* pShape, bool bAbsolute) +{ + tools::Rectangle aRet; + aRet.SetEmpty(); + auto pSdrShape = pShape->FindRealSdrObject(); + auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pSdrShape); + 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 (pSdrShape) + { + // fallback - get *any* bound rect we can possibly get hold of + aRet = pSdrShape->GetCurrentBoundRect(); + } + + if (!bAbsolute && pSdrShape) + { + // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor. + Point aPoint(pSdrShape->GetSnapRect().Center()); + Size aSize(pSdrShape->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, const OUString& rPropertyName, + const css::uno::Any& rValue) +{ + if (rPropertyName == "CustomShapeGeometry") + { + // CustomShapeGeometry changes the textbox position offset and size, so adjust both. + syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any()); + + SdrObject* pObject = pShape->FindRealSdrObject(); + if (pObject) + { + tools::Rectangle aRectangle(pObject->GetSnapRect()); + syncProperty( + pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION, + uno::makeAny(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left())))); + syncProperty( + pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION, + uno::makeAny(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top())))); + } + + SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT); + 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; + } + + if (nDirection) + { + syncProperty(pShape, RES_FRAMEDIR, 0, uno::makeAny(nDirection)); + } + } + } + else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST) + syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue); + else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT) + syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue); + else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST) + syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue); + else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST) + syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue); + else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST) + syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue); + else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST) + syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue); + else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE) + { + text::WritingMode eMode; + if (rValue >>= eMode) + syncProperty(pShape, RES_FRAMEDIR, 0, uno::makeAny(sal_Int16(eMode))); + } +} + +void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID, + css::uno::Any& rValue) +{ + if (!pShape) + return; + + nMemberID &= ~CONVERT_TWIPS; + + if (SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + { + if (nWID == RES_CHAIN) + { + 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; + } + } + } +} + +void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID, + const css::uno::Any& rValue) +{ + // No shape yet? Then nothing to do, initial properties are set by create(). + if (!pShape) + return; + + uno::Any aValue(rValue); + nMemberID &= ~CONVERT_TWIPS; + + if (SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + { + 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: + aPropertyName = UNO_NAME_HORI_ORIENT_RELATION; + break; + case MID_HORIORIENT_POSITION: + aPropertyName = UNO_NAME_HORI_ORIENT_POSITION; + bAdjustX = true; + 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; + } + break; + } + case RES_VERT_ORIENT: + switch (nMemberID) + { + case MID_VERTORIENT_ORIENT: + aPropertyName = UNO_NAME_VERT_ORIENT; + break; + case MID_VERTORIENT_RELATION: + aPropertyName = UNO_NAME_VERT_ORIENT_RELATION; + break; + case MID_VERTORIENT_POSITION: + aPropertyName = UNO_NAME_VERT_ORIENT_POSITION; + bAdjustY = true; + break; + } + break; + case RES_FRM_SIZE: + switch (nMemberID) + { + 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: + if (aValue.get<text::TextContentAnchorType>() + == text::TextContentAnchorType_AS_CHARACTER) + { + uno::Reference<beans::XPropertySet> const xPropertySet( + SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), + uno::UNO_QUERY); + xPropertySet->setPropertyValue( + UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH)); + return; + } + 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; + } + 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; + } + 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; + } + break; + } + + if (!aPropertyName.isEmpty()) + { + // Position/size should be the text position/size, not the shape one as-is. + if (bAdjustX || bAdjustY || bAdjustSize) + { + tools::Rectangle aRect = getTextRectangle(pShape, /*bAbsolute=*/false); + if (!aRect.IsEmpty()) + { + if (bAdjustX || bAdjustY) + { + sal_Int32 nValue; + if (aValue >>= nValue) + { + if (bAdjustX) + nValue += TWIPS_TO_MM(aRect.getX()); + else if (bAdjustY) + nValue += TWIPS_TO_MM(aRect.getY()); + aValue <<= nValue; + } + } + else if (bAdjustSize) + { + awt::Size aSize(TWIPS_TO_MM(aRect.getWidth()), + TWIPS_TO_MM(aRect.getHeight())); + aValue <<= aSize; + } + } + } + + 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; + } +} + +void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet) +{ + if (SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT)) + { + SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange); + + SfxItemIter aIter(rSet); + const SfxPoolItem* pItem = aIter.GetCurItem(); + do + { + if (rShape.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + SwFormatAnchor pShapeAnch = rShape.GetAnchor(); + aTextBoxSet.Put(pShapeAnch); + } + + switch (pItem->Which()) + { + case RES_VERT_ORIENT: + { + auto& rOrient = static_cast<const SwFormatVertOrient&>(*pItem); + SwFormatVertOrient aOrient(rOrient); + + tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false); + if (!aRect.IsEmpty()) + aOrient.SetPos(aOrient.GetPos() + aRect.getY()); + + if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) + { + 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: + { + auto& rOrient = static_cast<const SwFormatHoriOrient&>(*pItem); + SwFormatHoriOrient aOrient(rOrient); + + tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false); + if (!aRect.IsEmpty()) + aOrient.SetPos(aOrient.GetPos() + aRect.getX()); + + if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) + { + 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 = getTextRectangle(&rShape, /*bAbsolute=*/false); + if (!aRect.IsEmpty()) + { + aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY()); + aTextBoxSet.Put(aVertOrient); + + aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX()); + aTextBoxSet.Put(aHoriOrient); + + aSize.SetWidth(aRect.getWidth()); + aSize.SetHeight(aRect.getHeight()); + aTextBoxSet.Put(aSize); + } + } + 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()) + pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet); + } +} + +/* 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..1e284f1d8 --- /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* pD ) + : pDoc( pD ) +{ + StartListening( *INetURLHistory::GetOrCreate() ); +} + +SwURLStateChanged::~SwURLStateChanged() +{ + EndListening( *INetURLHistory::GetOrCreate() ); +} + +void SwURLStateChanged::Notify( SfxBroadcaster& , const SfxHint& rHint ) +{ + if( dynamic_cast<const INetURLHistoryHint*>(&rHint) && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + // This URL has been changed: + const INetURLObject* pIURL = static_cast<const INetURLHistoryHint&>(rHint).GetObject(); + OUString sURL( pIURL->GetMainURL( INetURLObject::DecodeMechanism::NONE ) ), sBkmk; + + SwEditShell* pESh = pDoc->GetEditShell(); + + if( pDoc->GetDocShell() && pDoc->GetDocShell()->GetMedium() && + // If this is our Doc, we can also have local jumps! + pDoc->GetDocShell()->GetMedium()->GetName() == sURL ) + sBkmk = "#" + pIURL->GetMark(); + + bool bAction = false, bUnLockView = false; + for (const SfxPoolItem* pItem : pDoc->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)->ModifyNotification(&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( const OUString& rURL ) +{ + bool bRet = false; + if( !rURL.isEmpty() ) + { + INetURLHistory *pHist = INetURLHistory::GetOrCreate(); + if( '#' == rURL[0] && mpDocShell && mpDocShell->GetMedium() ) + { + INetURLObject aIObj( mpDocShell->GetMedium()->GetURLObject() ); + aIObj.SetMark( rURL.copy( 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..ecd226b51 --- /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..80ddd75c3 --- /dev/null +++ b/sw/source/core/docnode/finalthreadmanager.cxx @@ -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 . + */ + +#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> + +/** thread to cancel a give list of cancellable jobs + + helper class for FinalThreadManager +*/ +class CancelJobsThread : public osl::Thread +{ + public: + explicit CancelJobsThread( const std::list< css::uno::Reference< css::util::XCancellable > >& rJobs ) + : osl::Thread(), + maMutex(), + maJobs( 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 osl::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 ) +{ + osl::MutexGuard aGuard(maMutex); + + maJobs.insert( maJobs.end(), rJobs.begin(), rJobs.end() ); + mbAllJobsCancelled = !maJobs.empty(); +} + +bool CancelJobsThread::existJobs() const +{ + osl::MutexGuard aGuard(maMutex); + + return !maJobs.empty(); +} + +bool CancelJobsThread::allJobsCancelled() const +{ + osl::MutexGuard aGuard(maMutex); + + return maJobs.empty() && mbAllJobsCancelled; +} + +void CancelJobsThread::stopWhenAllJobsCancelled() +{ + osl::MutexGuard aGuard(maMutex); + + mbStopped = true; +} + +css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob() +{ + css::uno::Reference< css::util::XCancellable > xRet; + + { + osl::MutexGuard aGuard(maMutex); + + if ( !maJobs.empty() ) + { + xRet = maJobs.front(); + maJobs.pop_front(); + } + } + + return xRet; +} + +bool CancelJobsThread::stopped() const +{ + osl::MutexGuard 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 ) + : osl::Thread(), + maMutex(), + 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), + maMutex(), + maThreads(), + 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 >( static_cast< cppu::OWeakObject* >( this ), css::uno::UNO_QUERY ) ); +} + +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() ) + { + osl::MutexGuard aGuard(maMutex); + + if ( mpCancelJobsThread == nullptr ) + { + mpCancelJobsThread.reset(new CancelJobsThread( 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..4d12c2d2e --- /dev/null +++ b/sw/source/core/docnode/ndcopy.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 <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <node.hxx> +#include <frmfmt.hxx> +#include <swtable.hxx> +#include <ndtxt.hxx> +#include <swtblfmt.hxx> +#include <cellatr.hxx> +#include <docary.hxx> +#include <ddefld.hxx> +#include <swddetbl.hxx> +#include <ndindex.hxx> +#include <frameformats.hxx> +#include <vector> +#include <osl/diagnose.h> + + +#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* pDoc, 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( pDoc->IsInsOnlyTextGlossary() ) + { + SwNodeIndex aIdx( rIdx, -1 ); + if( aIdx.GetNode().IsTextNode() ) + { + pCpyAttrNd = aIdx.GetNode().GetTextNode(); + pColl = &pCpyAttrNd->GetTextColl()->GetNextTextFormatColl(); + } + } + if( !pColl ) + pColl = pDoc->CopyTextColl( *GetTextColl() ); + + SwTextNode* pTextNd = pDoc->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_pDoc; + sal_uLong m_nOldTableSttIdx; + MapTableFrameFormats& m_rMapArr; + SwTableLine* m_pInsLine; + SwTableBox* m_pInsBox; + SwTableNode *m_pTableNd; + const SwTable *m_pOldTable; + + CopyTable(SwDoc* pDc, MapTableFrameFormats& rArr, sal_uLong nOldStt, + SwTableNode& rTableNd, const SwTable* pOldTable) + : m_pDoc(pDc), 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 SfxPoolItem* pItem; + if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMULA, false, + &pItem ) && static_cast<const SwTableBoxFormula*>(pItem)->IsIntrnlName() ) + { + const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem))->PtrToBoxNm(pCT->m_pOldTable); + } + + pBoxFormat = pCT->m_pDoc->MakeTableBoxFormat(); + pBoxFormat->CopyAttrs( *pBox->GetFrameFormat() ); + + if( pBox->GetSttIdx() ) + { + SvNumberFormatter* pN = pCT->m_pDoc->GetNumberFormatter(false); + if( pN && pN->HasMergeFormatTable() && SfxItemState::SET == pBoxFormat-> + GetItemState( RES_BOXATR_FORMAT, false, &pItem ) ) + { + sal_uLong nOldIdx = static_cast<const SwTableBoxNumFormat*>(pItem)->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_pDoc->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* pDoc, const SwNodeIndex& rIdx ) const +{ + // In which array are we? Nodes? UndoNodes? + SwNodes& rNds = const_cast<SwNodes&>(GetNodes()); + + { + if( rIdx < pDoc->GetNodes().GetEndOfInserts().GetIndex() && + rIdx >= pDoc->GetNodes().GetEndOfInserts().StartOfSectionIndex() ) + return nullptr; + } + + // Copy the TableFrameFormat + OUString sTableName( GetTable().GetFrameFormat()->GetName() ); + if( !pDoc->IsCopyIsMove() ) + { + const SwFrameFormats& rTableFormats = *pDoc->GetTableFrameFormats(); + for( size_t n = rTableFormats.size(); n; ) + if( rTableFormats[ --n ]->GetName() == sTableName ) + { + sTableName = pDoc->GetUniqueTableName(); + break; + } + } + + SwFrameFormat* pTableFormat = pDoc->MakeTableFrameFormat( sTableName, pDoc->GetDfltFrameFormat() ); + pTableFormat->CopyAttrs( *GetTable().GetFrameFormat() ); + SwTableNode* pTableNd = new SwTableNode( rIdx ); + SwEndNode* pEndNd = new SwEndNode( rIdx, *pTableNd ); + SwNodeIndex aInsPos( *pEndNd ); + + SwTable& rTable = pTableNd->GetTable(); + 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() ) + pDoc->getIDocumentFieldsAccess().InsDeletedFieldType( *pDDEType ); + else + pDDEType = static_cast<SwDDEFieldType*>(pDoc->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, +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() ); + pDoc->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( pDoc, 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 ) +{ + // Copy the formats into the other document: + // Special case for PageBreak/PageDesc/ColBrk + SwDoc* pDestDoc = rDestNd.GetDoc(); + SwAttrSet aPgBrkSet( pDestDoc->GetAttrPool(), aBreakSetRange ); + const SwAttrSet* pSet; + + if( nullptr != ( pSet = rDestNd.GetpSwAttrSet() ) ) + { + // 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 ); + } + + rDestNd.ChgFormatColl( pDestDoc->CopyTextColl( *GetTextColl() )); + if( nullptr != ( pSet = GetpSwAttrSet() ) ) + 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..9029f9348 --- /dev/null +++ b/sw/source/core/docnode/ndnotxt.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 <hintids.hxx> +#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.reset( new tools::PolyPolygon( *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.reset( new tools::PolyPolygon(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 ); + } + } + } + const_cast<SwNoTextNode *>(this)->m_bContourMapModeValid = true; + const_cast<SwNoTextNode *>(this)->m_bPixelContour = false; + } + + return m_pContour.get(); +} + +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.reset( new tools::PolyPolygon( *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..1e8e75256 --- /dev/null +++ b/sw/source/core/docnode/ndnum.cxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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) +{ + SwTextNode * pTextNd = rNd.GetTextNode(); + + if (pTextNd && pTextNd->IsOutlineStateChanged()) + { + 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; + + const SwNodePtr pSrch = const_cast<SwNodePtr>(&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..eeac5d109 --- /dev/null +++ b/sw/source/core/docnode/ndsect.cxx @@ -0,0 +1,1422 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <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 <txtfrm.hxx> +#include <hints.hxx> +#include <memory> +#include "ndsect.hxx" +#include <tools/datetimeutils.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(); + sal_uLong 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::pair<SwTOXBase const*, sw::RedlineMode> 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 ? pTOXBaseAndMode->first : 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, 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(), USHRT_MAX ); + } + 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 ); + sal_uLong 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; + if( SfxItemState::SET != pFormat->GetItemState( + RES_FTN_AT_TXTEND, true, &pFootnoteEndAtTextEnd ) || + SfxItemState::SET != pFormat->GetItemState( + RES_END_AT_TXTEND, true, &pFootnoteEndAtTextEnd )) + pFootnoteEndAtTextEnd = nullptr; + + 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 )); + 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 ; + } + + { + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, pFormat ); + pFormat->ModifyNotification( &aMsgHint, &aMsgHint ); + } + + // 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 + sal_uLong 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(), USHRT_MAX ); + + /// 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, sal_uLong nStt, sal_uLong nEnd ) +{ + SwFootnoteIdxs& rFootnoteArr = pNd->GetDoc()->GetFootnoteIdxs(); + if( !rFootnoteArr.empty() ) + { + 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( 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 + sal_uLong 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().IsHidden() && + 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 + sal_uLong nEnd = pSectNd->EndOfSectionIndex(); + sal_uLong nStart = pSectNd->GetIndex()+1; + sal_uLong nSkipIdx = ULONG_MAX; + for( sal_uLong n = nStart; n < nEnd; ++n ) + { + SwNode* pNd = (*this)[n]; + + // Attach all Sections in the NodeSection underneath the new one + if( ULONG_MAX == nSkipIdx ) + pNd->m_pStartOfSection = pSectNd; + else if( n >= nSkipIdx ) + nSkipIdx = ULONG_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( ULONG_MAX == nSkipIdx ) + nSkipIdx = pNd->EndOfSectionIndex(); + } + } + else if( pNd->IsContentNode() ) + static_cast<SwContentNode*>(pNd)->DelFrames(nullptr); + } + + sw_DeleteFootnote( pSectNd, nStart, nEnd ); + + if( bInsFrame ) + { + if( pNode2Layout ) + { + sal_uLong 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() ) + { + if( GetSection().IsHidden() || 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()->IsHideRedlines() + && !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 ( pNew->IsTextFrame() ) + { + SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pNew->FindPrevCnt()) ); + } + } + 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 ( pNew->IsTextFrame() ) + { + SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pNew->FindPrevCnt()) ); + } + } + 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* pDoc = rNds.GetDoc(); + + *pIdxBehind = *this; + + m_pSection->m_Data.SetHiddenFlag(true); + + if( rNds.IsDocNodes() ) + { + SwNodeIndex *pEnd = pEndIdx ? pEndIdx : + new SwNodeIndex( *EndOfSectionNode(), 1 ); + ::MakeFrames( pDoc, *pIdxBehind, *pEnd ); + if( !pEndIdx ) + delete pEnd; + } +} + +void SwSectionNode::DelFrames(SwRootFrame const*const /*FIXME TODO*/, bool const bForce) +{ + sal_uLong 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) + { + 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* pDoc, const SwNodeIndex& rIdx ) const +{ + // In which array am I: Nodes, UndoNodes? + const SwNodes& rNds = GetNodes(); + + // Copy the SectionFrameFormat + SwSectionFormat* pSectFormat = pDoc->MakeSectionFormat(); + pSectFormat->CopyAttrs( *GetSection().GetFormat() ); + + std::unique_ptr<SwTOXBase> pTOXBase; + if (SectionType::ToxContent == GetSection().GetType()) + { + OSL_ENSURE( dynamic_cast< const SwTOXBaseSection* >( &GetSection() ) != nullptr , "no TOXBaseSection!" ); + SwTOXBaseSection const& rTBS( + dynamic_cast<SwTOXBaseSection const&>(GetSection())); + pTOXBase.reset( new SwTOXBase(rTBS, pDoc) ); + } + + 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() == pDoc && pDoc->IsCopyIsMove() ) + { + pNewSect->SetSectionName( GetSection().GetSectionName() ); + } + else + { + const OUString sSectionName(GetSection().GetSectionName()); + pNewSect->SetSectionName(pDoc->GetUniqueSectionName( &sSectionName )); + } + } + + pNewSect->SetType( GetSection().GetType() ); + pNewSect->SetCondition( GetSection().GetCondition() ); + 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, +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( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ? LinkCreateType::Connect : LinkCreateType::NONE ); + + // If we copy from the Undo as Server, enter it again + if (m_pSection->IsServer() + && pDoc->GetIDocumentUndoRedo().IsUndoNodes(rNds)) + { + pNewSect->SetRefObject( m_pSection->GetObject() ); + pDoc->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 ); + sal_uLong 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::NodesArrChgd() +{ + SwSectionFormat *const pFormat = m_pSection->GetFormat(); + if( !pFormat ) + return; + + SwNodes& rNds = GetNodes(); + SwDoc* pDoc = pFormat->GetDoc(); + + if( !rNds.IsDocNodes() ) + { + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, pFormat ); + pFormat->ModifyNotification( &aMsgHint, &aMsgHint ); + } + + 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 + sal_uLong nStart = GetIndex()+1, nEnd = EndOfSectionIndex(); + for( sal_uLong n = nStart; n < nEnd; ++n ) + // Make up the Format's nesting + if( nullptr != ( pSectNd = rNds[ n ]->GetSectionNode() ) ) + { + 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 = rNm.copy( aName.getLength() ).toInt32(); + 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..70e3ed72f --- /dev/null +++ b/sw/source/core/docnode/ndsect.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_DOCNODE_NDSECT_HXX +#define INCLUDED_SW_SOURCE_CORE_DOCNODE_NDSECT_HXX + +#include <tools/solar.h> + +class SwSectionNode; + +void sw_DeleteFootnote( SwSectionNode *pNd, sal_uLong nStt, sal_uLong 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..5dcbdf865 --- /dev/null +++ b/sw/source/core/docnode/ndtbl.cxx @@ -0,0 +1,4645 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <tools/datetimeutils.hxx> +#include <sal/log.hxx> + +#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 ); + SvxBorderLine aLine( &aCol, DEF_LINE_WIDTH_0 ); + if ( bHTML ) + { + aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + aLine.SetWidth( DEF_LINE_WIDTH_0 ); + } + 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; + sal_uLong nIndex = rIdx.GetIndex(); + do { + SwNode* pNd = GetNodes()[ nIndex ]->StartOfSectionNode(); + if( nullptr != ( pTableNd = pNd->GetTableNode() ) ) + 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 + sal_uLong nIdxPos = 0; + SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr; + if( !pLine->GetTabBoxes().empty() ) + { + if( nInsPos < pLine->GetTabBoxes().size() ) + { + if( nullptr == (pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(), + pLine->GetTabBoxes()[ nInsPos ] ))) + pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() ); + } + else + { + if( nullptr == (pNxtBox = pLine->FindNextBox( pTableNd->GetTable(), + pLine->GetTabBoxes().back() ))) + pNxtBox = pLine->FindNextBox( pTableNd->GetTable() ); + } + } + else if( nullptr == ( pNxtBox = pLine->FindNextBox( pTableNd->GetTable() ))) + 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, static_cast<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(); + const SfxPoolItem *pItem = nullptr; + + if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem ) + && pItem != nullptr) + { + 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(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == pNdSet->GetItemState( RES_PAGEDESC, false, + &pItem ) ) + { + pTableFormat->SetFormatAttr( *pItem ); + pNextNd->ResetAttr( RES_PAGEDESC ); + pNdSet = pNextNd->GetpSwAttrSet(); + } + if( pNdSet && SfxItemState::SET == pNdSet->GetItemState( RES_BREAK, false, + &pItem ) ) + { + 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 ); + } + SfxItemSet aCharSet( GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{} ); + + 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 += 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, 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(); + { + sal_uLong 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, + static_cast<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(); + const SfxPoolItem *pItem = nullptr; + + if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem ) + && pItem != nullptr) + { + 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::unique_ptr< std::vector<SwTableBoxFormat*> > aBoxFormatArr2; + if( bUseBoxFormat ) + { + aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr )); + } + else + { + aBoxFormatArr2.reset(new std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr )); + } + + SfxItemSet aCharSet( GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{} ); + + 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() ) + { + sal_uLong nSttNd = pBox->GetSttIdx()+1; + sal_uLong 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); + } + } + + sal_uLong 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, 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) + { + const SfxPoolItem* pItem; + if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false, &pItem)) + { + if (pTableFormat) + { + pTableFormat->SetFormatAttr(*pItem); + } + rTextNode.ResetAttr(RES_BREAK); + pSet = rTextNode.GetpSwAttrSet(); + } + + if (pSet + && (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false, &pItem)) + && static_cast<SwFormatPageDesc const*>(pItem)->GetPageDesc()) + { + if (pTableFormat) + { + pTableFormat->SetFormatAttr(*pItem); + } + 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* pDoc = 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 += 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( static_cast<sal_uInt16>( + aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) ); + } + } + + aPosArr.push_back( + static_cast<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( pDoc, aSttIdx.GetIndex(), pTextNd->GetText().getLength() ); + + 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)> restoreFunc( + [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode) + { + 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, *pDoc, &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(); + const SfxPoolItem *pItem = nullptr; + + if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem ) + && pItem != nullptr) + { + 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; + } + + sal_uLong nIdx = pTableNd->GetIndex(); + aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 ); + + getIDocumentState().SetEnableSetModified(bEnableSetModified); + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 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* pDoc = 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, *pDoc); + + 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, 0, *pTableNd->EndOfSectionNode() ); + std::unique_ptr<SwUndoTableToText> pUndo; + SwNodeRange* pUndoRg = nullptr; + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().ClearRedo(); + pUndoRg = new SwNodeRange( aRg.aStart, -1, aRg.aEnd, +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* pDoc = pDelPara->rNds.GetDoc(); + SwNodeRange aDelRg( *pBox->GetSttNd(), 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 + sal_uLong 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( *pDoc, 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( pDoc, nNdIdx, pCurTextNd->GetText().getLength() ); + + pDelPara->pLastNd->JoinNext(); + + if( !pContentStore->Empty() ) + pContentStore->Restore( pDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen ); + } + else if( pDelPara->pUndo ) + { + ++aDelRg.aStart; + pDelPara->pUndo->AddBoxPos( *pDoc, nNdIdx, aDelRg.aEnd.GetIndex() ); + } + } + else if( pDelPara->pUndo ) + pDelPara->pUndo->AddBoxPos( *pDoc, 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 SfxPoolItem *pBreak, *pDesc; + if( SfxItemState::SET != rTableSet.GetItemState( RES_PAGEDESC, false, &pDesc )) + pDesc = nullptr; + if( SfxItemState::SET != rTableSet.GetItemState( RES_BREAK, false, &pBreak )) + pBreak = nullptr; + + 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# + sal_uLong 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 ) + { + if( nullptr != ( pCNd = aDelRg.aStart.GetNode().GetContentNode())) + { + 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 if( nullptr != ( pSNd = aDelRg.aStart.GetNode().GetSectionNode())) + { + 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, 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, 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; + + SwEditShell* pESh = GetEditShell(); + if( pESh ) + { + 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 ); + } + + sal_uLong 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. + SwEditShell* pESh = GetEditShell(); + if( pESh ) + { + const SwNode* pNd = rCursor.GetNode().FindTableBoxStartNode(); + pESh->ParkCursor( SwNodeIndex( *pNd ) ); + } + + // Thus delete the Columns + GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr); + DeleteRowCol( aBoxes, true ); + GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr); +} + +bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn ) +{ + if( ::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( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr) + return false; + + ::ClearFEShellTabCols(*this, nullptr); + SwSelBoxes aSelBoxes( rBoxes ); + SwTable &rTable = pTableNd->GetTable(); + long nMin = 0; + long nMax = 0; + if( rTable.IsNewModel() ) + { + if( bColumn ) + rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax ); + else + rTable.FindSuperfluousRows( aSelBoxes ); + } + + // Are we deleting the whole Table? + const sal_uLong nTmpIdx1 = pTableNd->GetIndex(); + const sal_uLong 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 sal_uLong nTableEnd = pTableNd->EndOfSectionIndex() + 1; + const sal_uLong 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; + sal_uLong 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 )); + 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 ); + } + + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 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 (bColumn) + rTable.PrepareDeleteCol( nMin, nMax ); + rTable.FindSuperfluousRows( aSelBoxes ); + if (pUndo) + pUndo->ReNewBoxes( aSelBoxes ); + } + bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true ); + if (bRet) + { + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 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<sal_uLong> 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) + { + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 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, 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()->IsHideRedlines() + && !pNode->IsCreateFrameWhenHidingRedlines()) + { + 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()->IsHideRedlines() + && !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. + { + SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pNew->FindPrevCnt()) ); + } + } + 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. + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) ); + } + } + 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* pDoc = GetDoc(); + if (pDoc) + { + SwTable& rTable = GetTable(); + if ( pDoc->getIDocumentRedlineAccess().HasExtraRedlineTable() ) + pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines( pDoc, rTable, true, RedlineType::Any ); + } +} + +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() ( long s1, long s2 ) const; +}; + +} + +bool FuzzyCompare::operator() ( long s1, 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 long nLeftMin = ( aRectFnSet.IsVert() ? + pTab->GetPrtLeft() - pPage->getFrameArea().Left() : + pTab->GetPrtTop() - pPage->getFrameArea().Top() ); + const long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0; + const long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea()); + const long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX; + + rFill.SetLeftMin( nLeftMin ); + rFill.SetLeft( nLeft ); + rFill.SetRight( nRight ); + rFill.SetRightMax( nRightMax ); + + typedef std::map< long, std::pair< long, long >, FuzzyCompare > BoundaryMap; + BoundaryMap aBoundaries; + BoundaryMap::iterator aIter; + std::pair< long, long > aPair; + + typedef std::map< 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: + long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea()); + 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; + 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: + 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 long nTabTop = aRectFnSet.GetPrtTop(*pTab); + const long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop ); + const std::pair< long, long > aTmpPair = rEntry.second; + const long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop ); + const 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()) ); + 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 long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ]; + const long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ]; + const long nOldRowHeight = nOldRowEnd - nOldRowStart; + + const long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ]; + const long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ]; + const long nNewRowHeight = nNewRowEnd - nNewRowStart; + + const 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 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 long 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 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); + SwClearFntCacheTextGlyphs(); + 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) ); + } + + SwMsgPoolItem aChg( RES_TBLHEADLINECHG ); + rTable.SetRowsToRepeat( nSet ); + rTable.GetFrameFormat()->ModifyNotification( &aChg, &aChg ); + getIDocumentState().SetModified(); +} + +void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd ) +{ + if( pHst ) + pHst->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text ); +} + +void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox ) +{ + aPosArr.push_back(nWidth); + SwTableBox* p = const_cast<SwTableBox*>(&rBox); + m_Boxes.push_back(p); + nWidth = nWidth + static_cast<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth()); +} + +const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox ) +{ + const SwTableBox* pRet = nullptr; + + if( !aPosArr.empty() ) + { + std::vector<sal_uInt16>::size_type n; + for( n = 0; n < aPosArr.size(); ++n ) + if( aPosArr[ n ] == nWidth ) + break; + else if( aPosArr[ n ] > nWidth ) + { + if( n ) + --n; + break; + } + + if( n >= aPosArr.size() ) + --n; + + nWidth = nWidth + static_cast<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth()); + pRet = m_Boxes[ n ]; + } + return pRet; +} + +bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth ) +{ + if( !aPosArr.empty() ) + { + std::vector<sal_uInt16>::size_type n; + for( n = 0; n < aPosArr.size(); ++n ) + { + if( aPosArr[ n ] == nOffset ) + break; + else if( aPosArr[ n ] > nOffset ) + { + if( n ) + --n; + break; + } + } + + aPosArr.erase( aPosArr.begin(), aPosArr.begin() + n ); + m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n); + + size_t nArrSize = aPosArr.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 = nWidth; + nSize *= ( aPosArr[ n ] - nOffset ); + nSize /= nOldWidth; + aPosArr[ n ] = sal_uInt16( nSize ); + } + } + } + return !aPosArr.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 + { + sal_uInt16 const aTableSplitBoxSetRange[] { + RES_LR_SPACE, RES_UL_SPACE, + RES_BACKGROUND, RES_SHADOW, + RES_PROTECT, RES_PROTECT, + RES_VERT_ORIENT, RES_VERT_ORIENT, + 0 }; + + SfxItemSet aTmpSet( pFormat->GetDoc()->GetAttrPool(), + aTableSplitBoxSetRange ); + 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( 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) + */ +bool SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode, + bool bCalcNewSize ) +{ + SwNode* pNd = &rPos.nNode.GetNode(); + SwTableNode* pTNd = pNd->FindTableNode(); + if( !pTNd || pNd->IsTableNode() ) + return false; + + if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr) + return false; + + 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; + } + + { + sal_uLong 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 + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pTNd); + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pNew); + + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 ); + + return nullptr != pNew; +} + +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*> aSrcDestMap; + SwTableNode* pNewTableNd; + SwTable& rOldTable; + +public: + SplitTable_Para( SwTableNode* pNew, SwTable& rOld ) + : aSrcDestMap(), pNewTableNd( pNew ), rOldTable( rOld ) + {} + SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const + { + auto it = aSrcDestMap.find( pSrcFormat ); + return it == aSrcDestMap.end() ? nullptr : it->second; + } + + void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat ) + { aSrcDestMap[ pSrcFormat ] = pDestFormat; } + + void ChgBox( SwTableBox* pBox ) + { + rOldTable.GetTabSortBoxes().erase( pBox ); + pNewTableNd->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; + + sal_uLong 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 ) + { + GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(); + + getIDocumentState().SetModified(); + getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 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 -= 2; + DelNodes( aIdx, 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(); + SfxItemSet aCharSet(pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{}); + 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()) + { + sal_uLong nSttNd = pSetBox->GetSttIdx()+1; + sal_uLong 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, 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 = pFormat->GetName().copy( nNmLen ).toInt32(); + 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 = FindFormatByName( *mpTableFrameFormatTable, rName ); + else + { + // Only the ones set in the Doc + for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n ) + { + const SwFrameFormat* pFormat = (*mpTableFrameFormatTable)[ n ]; + if( !pFormat->IsDefault() && IsUsed( *pFormat ) && + pFormat->GetName() == rName ) + { + pRet = pFormat; + break; + } + } + } + return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet)); +} + +bool 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(); + } + return bRet; +} + +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 SfxPoolItem* pNumFormatItem = nullptr; + if( SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, + false, &pNumFormatItem ) && GetNumberFormatter()->IsTextFormat( + static_cast<const SwTableBoxNumFormat*>(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()); + SfxItemSet aBoxSet( GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + + 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 = static_cast<const SwTableBoxNumFormat*>(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 + const SfxPoolItem* pValueItem = nullptr, *pFormatItem = nullptr; + SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat()); + if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, + false, &pFormatItem ) || + SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, + false, &pValueItem )) + { + 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 ) + { + 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() ) + { + SwTextNode * pTextNode = pNode->GetTextNode(); + if (pTextNode && pTextNode->IsNumbered() + && pTextNode->GetText().isEmpty()) + { + const SfxPoolItem* pFormatItem = nullptr; + SfxItemSet rSet( pTextNode->GetDoc()->GetAttrPool(), + svl::Items<RES_PARATR_BEGIN, RES_PARATR_END - 1>{}); + pTextNode->SwContentNode::GetAttr( rSet ); + if ( SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE , false , &pFormatItem ) ) + { + 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(static_cast<SfxStringItem*>(pFormatItem->Clone())); + pNewItem->SetValue(OUString()); + rSet.Put( std::move(pNewItem) ); + pTextNode->SetAttr( rSet ); + } + } + } +} + +void SwDoc::ClearBoxNumAttrs( const SwNodeIndex& rNode ) +{ + SwStartNode* pSttNd; + if( nullptr != ( pSttNd = rNode.GetNode(). + FindSttNodeByType( SwTableBoxStartNode )) && + 2 == pSttNd->EndOfSectionIndex() - pSttNd->GetIndex() ) + { + SwTableBox* pBox = pSttNd->FindTableNode()->GetTable(). + GetTableBox( pSttNd->GetIndex() ); + + const SfxPoolItem* pFormatItem = nullptr; + const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet(); + if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, false, &pFormatItem ) || + SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) || + SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false )) + { + 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( + static_cast<const SwTableBoxNumFormat*>(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 ) +{ + 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 ); + } + + 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.get(), aPos, rBoxes, true )) + { + xCpyDoc.clear(); + + if( pUndo ) + { + GetIDocumentUndoRedo().DoUndo(bUndo); + } + return false; + } + aPos.nNode -= 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, 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 ) +{ + bool bChgd = false; + SwTableFormat* pFormat = FindTableFormatByName( rName ); + if( pFormat ) + { + 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() ) + { + sal_uLong 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), vAffectedTables, this)); + } + } + + return pReleasedFormat; +} + +void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat) +{ + SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName); + if (pFormat) + { + 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) + GetDocShell()->GetFEShell()->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..6dc3814db --- /dev/null +++ b/sw/source/core/docnode/ndtbl1.cxx @@ -0,0 +1,1631 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; + +// See swtable.cxx too +#define COLFUZZY 20L + +static bool IsSame( long nA, 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 *pOld, *pNew; + sal_Int16 nType; +}; + +} + +SwTableFormatCmp::SwTableFormatCmp( SwFrameFormat *pO, SwFrameFormat *pN, sal_Int16 nT ) + : pOld ( pO ), pNew ( pN ), nType( nT ) +{ + if (pOld) + pOld->Add(this); +} + +SwTableFormatCmp::~SwTableFormatCmp() +{ + if (pOld) + { + pOld->Remove(this); + if (!pOld->HasWriterListeners()) + delete 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->pOld == pOld && pCmp->nType == nType ) + return pCmp->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 ) + { + 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; + if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( rFormatCmp, pLine->GetFrameFormat(), 0 ))) + 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 ) + { + std::vector<SwTableLine*> aRowArr; // For Lines collecting + ::lcl_CollectLines( aRowArr, rCursor, false ); + + if( !aRowArr.empty() ) + { + 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 ) + { + std::vector<SwTableLine*> aRowArr; // For Lines collecting + ::lcl_CollectLines( aRowArr, rCursor, true ); + + if( !aRowArr.empty() ) + { + 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 ) + { + long nHeight = 0; + sal_Int32 nTotalHeight = 0; + for ( auto pLn : aRowArr ) + { + 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 ) + { + std::vector<SwTableLine*> aRowArr; // For Lines collecting + ::lcl_CollectLines( aRowArr, rCursor, true ); + + if( !aRowArr.empty() ) + { + 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 && *rToFill != *aAlternative ) + { + bRet = false; + break; + } + } + } + } + return bRet; +} + +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.IsOver( 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! + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER, false, + reinterpret_cast<const SfxPoolItem**>(&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); + } + + if( SfxItemState::SET == rSet.GetItemState( RES_BOX, false, + reinterpret_cast<const SfxPoolItem**>(&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; + if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), nType ))) + 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() ) + { + SwTable& rTable = pTableNd->GetTable(); + if (GetIDocumentUndoRedo().DoesUndo()) + { + GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd)); + } + + 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()); + + if ( !pBorderLine && bSetLine ) + { + aBox.reset(::GetDfltAttr(RES_BOX)->Clone()); + } + else + { + if ( aBox->GetTop() ) + ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetTop()), + pColor, pBorderLine ); + if ( aBox->GetBottom() ) + ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetBottom()), + pColor, pBorderLine ); + if ( aBox->GetLeft() ) + ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetLeft()), + pColor, pBorderLine ); + if ( aBox->GetRight() ) + ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetRight()), + 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() ) + { + 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 ) ) + { + 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; + if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), 0 ))) + 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 ) +{ + 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 = static_cast<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 static_cast<sal_uInt16>(std::max( long(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 ) + { + long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1]; + 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 long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin(); + nColLeft += nDiff; + nColRight += nDiff; + } + const long nCellLeft = aRectFnSet.GetLeft(pCell->getFrameArea()); + const long nCellRight = aRectFnSet.GetRight(pCell->getFrameArea()); + + // Calculate overlapping value + 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() ) + { + 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 long nCLeft = aRectFnSet.GetLeft(pCell->getFrameArea()); + const long nCRight = aRectFnSet.GetRight(pCell->getFrameArea()); + + bool bNotInCols = true; + + for ( size_t i = 0; i <= rCols.Count(); ++i ) + { + sal_uInt16 nFit = rToFill[i]; + long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1]; + long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i]; + + if ( bRTL ) + { + 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) + long nLeftA = nColLeft; + long nRightA = nColRight; + if ( rCols.GetLeftMin() != sal_uInt16(aRectFnSet.GetLeft(pTab->getFrameArea())) ) + { + const 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]; + } + const sal_uInt16 nEqualWidth = nSelectedWidth / nCols; + // bBalance: Distribute the width evenly + for (sal_uInt16 & rn : aWish) + if ( rn && bBalance ) + rn = nEqualWidth; + } + + const 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]; + + long nTabRight = aTabCols.GetRight() + nDiff; + + // If the Table would become too wide, we restrict the + // adjusted amount to the allowed maximum. + if ( !bBalance && nTabRight > aTabCols.GetRightMax() ) + { + const long nTmpD = nTabRight - aTabCols.GetRightMax(); + nDiff -= nTmpD; + nTabRight -= nTmpD; + } + for ( size_t i2 = i; i2 < aTabCols.Count(); ++i2 ) + aTabCols[i2] += nDiff; + aTabCols.SetRight( nTabRight ); + } + } + } + + const 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..e5b0a4061 --- /dev/null +++ b/sw/source/core/docnode/node.cxx @@ -0,0 +1,2160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <docary.hxx> +#include <node.hxx> +#include <ndindex.hxx> +#include <numrule.hxx> +#include <swtable.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <swcache.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> + +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() ) + { + SwAttrSet aNewSet( *pAttrSet ); + aNewSet.SetParent( pParentSet ); + aNewSet.ClearItem( RES_FRMATR_STYLE_NAME ); + aNewSet.ClearItem( RES_FRMATR_CONDITIONAL_STYLE_NAME ); + OUString sVal; + + if ( pParentFormat ) + { + 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::unique_ptr<SfxItemSet> pStyleNames; + if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) ) + { + pStyleNames.reset(new SfxItemSet( *aNewSet.GetPool(), svl::Items<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>{} )); + 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::unique_ptr<SfxItemSet> pStyleNames; + if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) ) + { + pStyleNames.reset(new SfxItemSet( *aNewSet.GetPool(), svl::Items<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>{} )); + 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() && 0 == m_pStartOfSection->StartOfSectionIndex() ) + return 0; + + sal_uInt16 nLevel; + const SwNode* pNode = IsStartNode() ? this : m_pStartOfSection; + for( nLevel = 1; 0 != pNode->StartOfSectionIndex(); ++nLevel ) + pNode = pNode->m_pStartOfSection; + return IsEndNode() ? nLevel-1 : nLevel; +} + +#ifdef DBG_UTIL +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() ) + { + SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes()); + SwNode* pNd = rNodes[ rWhere.GetIndex() -1 ]; + rNodes.InsertNode( this, rWhere ); + if( nullptr == ( m_pStartOfSection = pNd->GetStartNode()) ) + { + 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, sal_uLong 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 ) + { + SwNode* pNd = rNodes[ nPos - 1 ]; + rNodes.InsertNode( this, nPos ); + if( nullptr == ( m_pStartOfSection = pNd->GetStartNode()) ) + { + m_pStartOfSection = pNd->m_pStartOfSection; + if( pNd->GetEndNode() ) // Skip EndNode ? Section! + { + pNd = m_pStartOfSection; + m_pStartOfSection = pNd->m_pStartOfSection; + } + } + } +} + +SwNode::~SwNode() +{ + assert(!m_pAnchoredFlys || GetDoc()->IsInDtor()); // must all be deleted +} + +/// 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().IsOver( 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; + + if( nullptr != ( pSttNd = FindTableBoxStartNode() ) ) + { + 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(); + } + + if( nullptr != ( pSttNd = FindFootnoteStartNode() ) ) + { + 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( size_t* 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 = static_cast<const SwFormatPageDesc&>(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* pDoc = 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 = *pDoc->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 = &pDoc->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 = pDoc->GetPageDescCnt(); n && !pPgDesc; ) + { + const SwPageDesc& rPgDsc = pDoc->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 = &pDoc->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 = pDoc->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 = &pDoc->GetPageDesc( 0 ); + pNd = nullptr; + } + } + } + + if( pNd ) + { + SwFindNearestNode aInfo( *pNd ); + // Over all Nodes of all PageDescs + for (const SfxPoolItem* pItem : pDoc->GetAttrPool().GetItemSurrogates(RES_PAGEDESC)) + { + auto pPageDescItem = dynamic_cast<const SwFormatPageDesc*>(pItem); + if( pPageDescItem && pPageDescItem->GetDefinedIn() ) + { + const SwModify* 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 ); + } + } + + if( nullptr != ( pNd = aInfo.GetFoundNode() )) + { + if( pNd->IsContentNode() ) + pPgDesc = static_cast<const SwFormatPageDesc&>(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 = &pDoc->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; + } + xmlTextWriterStartElement(pWriter, BAD_CAST(pName)); + + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(GetIndex()).getStr())); + + switch (GetNodeType()) + { + case SwNodeType::Grf: + { + auto pNoTextNode = static_cast<const SwNoTextNode*>(this); + const tools::PolyPolygon* pContour = pNoTextNode->HasContour(); + if (pContour) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("pContour")); + for (sal_uInt16 i = 0; i < pContour->Count(); ++i) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("polygon")); + 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) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("point")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), + BAD_CAST(OString::number(j).getStr())); + const Point& rPoint = rPolygon.GetPoint(j); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"), + BAD_CAST(OString::number(rPoint.X()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"), + BAD_CAST(OString::number(rPoint.Y()).getStr())); + xmlTextWriterEndElement(pWriter); + } + xmlTextWriterEndElement(pWriter); + } + xmlTextWriterEndElement(pWriter); + } + } + break; + default: + break; + } + + xmlTextWriterEndElement(pWriter); + if (GetNodeType() == SwNodeType::End) + 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, sal_uLong 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 ); + sal_uLong 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; + case SwNodeType::Section: + pName = "section"; + 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; + } + + xmlTextWriterStartElement(pWriter, BAD_CAST(pName)); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(GetIndex()).getStr())); + + if (IsTableNode()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("attrset")); + GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + else if (GetStartNodeType() == SwTableBoxStartNode) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("attrset")); + if (SwTableBox* pBox = GetTableBox()) + pBox->GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + + // 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 shoul 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, sal_uLong 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 ); +} + +void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint) +{ + if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + const sal_uInt16 nWhich = pLegacyHint->m_pOld + ? pLegacyHint->m_pOld->Which() + : pLegacyHint->m_pNew + ? pLegacyHint->m_pNew->Which() + : 0 ; + + 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_CONDCOLL_CONDCHG: + if(pLegacyHint->m_pNew + && static_cast<const SwCondCollCondChg*>(pLegacyHint->m_pNew)->pChangedFormat == GetRegisteredIn() + && &GetNodes() == &GetDoc()->GetNodes() ) + ChkCondColl(); + return; // Do not pass through to the base class/Frames + + case RES_ATTRSET_CHG: + if (GetNodes().IsDocNodes() + && IsTextNode() + && pLegacyHint->m_pOld + && SfxItemState::SET == static_cast<const SwAttrSetChg*>(pLegacyHint->m_pOld)->GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false)) + bCalcHidden = true; + break; + + case RES_UPDATE_ATTR: + if (GetNodes().IsDocNodes() + && IsTextNode() + && pLegacyHint->m_pNew + && RES_ATTRSET_CHG == static_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew)->getWhichAttr()) + bCalcHidden = true; + break; + } + if(bSetParent && GetpSwAttrSet()) + AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl); + if(bCalcHidden) + static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags(); + NotifyClients(pLegacyHint->m_pOld, pLegacyHint->m_pNew); + } + else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint)) + { + m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew)); + } +} + +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() ) + { + SwFormatChg aTmp1( pOldColl ); + SwFormatChg aTmp2( pNewColl ); + SwClientNotify( *this, sw::LegacyModifyHint(&aTmp1, &aTmp2) ); + } + } + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + 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()->IsHideRedlines() + && !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 ( pNew->IsTextFrame() ) + { + SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pNew->FindPrevCnt()) ); + } + } + } +} + +/** + * 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 (sal_uLong 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; + } + } + 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 (sal_uLong 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. + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) ); + } + } + + 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( static_cast<const SwFormatPageDesc&>(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 SwModify::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?"); + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + 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 ) + { + SwAttrSetChg aChgOld( *GetpSwAttrSet(), aOld ); + SwAttrSetChg aChgNew( *GetpSwAttrSet(), aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // Send all changed ones + } + } + return bRet; +} + +bool SwContentNode::SetAttr( const SfxItemSet& rSet ) +{ + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + const SfxPoolItem* pFnd = nullptr; + if( SfxItemState::SET == rSet.GetItemState( RES_AUTO_STYLE, false, &pFnd ) ) + { + OSL_ENSURE( rSet.Count() == 1, "SetAutoStyle mixed with other attributes?!" ); + const SwFormatAutoFormat* pTmp = static_cast<const SwFormatAutoFormat*>(pFnd); + + // 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, *pTmp->GetStyleHandle() ); + } + else + { + mpAttrSet = pTmp->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 SfxPoolItem* pNameItem = nullptr; + if ( nullptr != GetCondFormatColl() || + SfxItemState::SET != mpAttrSet->GetItemState( RES_FRMATR_STYLE_NAME, false, &pNameItem ) || + static_cast<const SfxStringItem*>(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 ) + { + // Some special treatment for Attributes + SwAttrSetChg aChgOld( *GetpSwAttrSet(), aOld ); + SwAttrSetChg aChgNew( *GetpSwAttrSet(), aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // Send out all changed ones + } + } + 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; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + // If Modify is locked, do not send out any Modifys + if( IsModifyLocked() ) + { + sal_uInt16 nDel = 0; + if ( !nWhich2 || nWhich2 < nWhich1 ) + { + std::vector<sal_uInt16> aClearWhichIds; + aClearWhichIds.push_back( nWhich1 ); + nDel = ClearItemsFromAttrSet( aClearWhichIds ); + } + 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 ) + { + SwAttrSetChg aChgOld( *GetpSwAttrSet(), aOld ); + SwAttrSetChg aChgNew( *GetpSwAttrSet(), aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // All changed ones are sent + + if( !GetpSwAttrSet()->Count() ) // Empty?, delete it + mpAttrSet.reset(); + } + return bRet; +} + +bool SwContentNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr ) +{ + if( !GetpSwAttrSet() ) + return false; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + // 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 ) + { + SwAttrSetChg aChgOld( *GetpSwAttrSet(), aOld ); + SwAttrSetChg aChgNew( *GetpSwAttrSet(), aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // All changed ones are sent + } + } + if( !GetpSwAttrSet()->Count() ) // Empty?, delete it + mpAttrSet.reset(); + return 0 != nDel ; +} + +sal_uInt16 SwContentNode::ResetAllAttr() +{ + if( !GetpSwAttrSet() ) + return 0; + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + // If Modify is locked, do not send out any Modifys + if( IsModifyLocked() ) + { + std::vector<sal_uInt16> aClearWhichIds; + aClearWhichIds.push_back(0); + sal_uInt16 nDel = ClearItemsFromAttrSet( aClearWhichIds ); + 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 ) + { + SwAttrSetChg aChgOld( *GetpSwAttrSet(), aOld ); + SwAttrSetChg aChgNew( *GetpSwAttrSet(), aNew ); + ModifyNotification( &aChgOld, &aChgNew ); // All changed ones are sent + + 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 (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() ) ) + { + 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()); + NotifyClients(&aTmp1, &aTmp2); + } + if(IsInCache()) + { + SwFrame::GetCache().Delete(this); + SetInCache(false); + } + } +} + +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() +{ + // Check, just to be sure + if( RES_CONDTXTFMTCOLL == GetFormatColl()->Which() ) + { + 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) + { + 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 * pDoc = GetDoc(); + bool bResult = false; + + if (pDoc != nullptr) + bResult = pDoc->getIDocumentRedlineAccess().IsInRedlines(*this); + + return bResult; +} + +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()); + if (!m_pAnchoredFlys) + { + m_pAnchoredFlys.reset(new std::vector<SwFrameFormat*>); + } + m_pAnchoredFlys->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()); + assert(m_pAnchoredFlys); + auto it(std::find(m_pAnchoredFlys->begin(), m_pAnchoredFlys->end(), pFlyFormat)); + assert(it != m_pAnchoredFlys->end()); + m_pAnchoredFlys->erase(it); + if (m_pAnchoredFlys->empty()) + { + m_pAnchoredFlys.reset(); + } +} + +/* 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..a62f55ec6 --- /dev/null +++ b/sw/source/core/docnode/node2lay.cxx @@ -0,0 +1,467 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swtable.hxx> +#include <ftnfrm.hxx> +#include <sectfrm.hxx> +#include <cntfrm.hxx> +#include <tabfrm.hxx> +#include <frmtool.hxx> +#include <section.hxx> +#include <node2lay.hxx> + +/** + * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is + * just the public interface. + */ +class SwNode2LayImpl +{ + std::unique_ptr<SwIterator<SwFrame, SwModify, sw::IteratorMode::UnwrapMulti>> pIter; + SwModify* pMod; + std::vector<SwFrame*> mvUpperFrames; // To collect the Upper + sal_uLong nIndex; // The Index of the to-be-inserted Nodes + bool bMaster : 1; // true => only Master, false => only Frames without Follow + bool bInit : 1; // Did we already call First() at SwClient? + + SwNode2LayImpl(const SwNode2LayImpl&) = delete; + SwNode2LayImpl& operator=(const SwNode2LayImpl&) = delete; + +public: + SwNode2LayImpl( const SwNode& rNode, sal_uLong 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, sal_uLong nStt, sal_uLong nEnd ); + + SwFrame* GetFrame( const Point* pDocPos ) const; +}; + +static SwNode* GoNextWithFrame(const SwNodes& rNodes, SwNodeIndex *pIdx) +{ + if( pIdx->GetIndex() >= rNodes.Count() - 1 ) + return nullptr; + + SwNodeIndex aTmp(*pIdx, +1); + SwNode* pNd = nullptr; + while( aTmp < rNodes.Count()-1 ) + { + pNd = &aTmp.GetNode(); + bool bFound = false; + if ( pNd->IsContentNode() ) + // sw_redlinehide: assume that it's OK to find a node with the same + // frame as the caller's one + bFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First(); + else if ( pNd->IsTableNode() ) + bFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First() ; + else if( pNd->IsEndNode() && !pNd->StartOfSectionNode()->IsSectionNode() ) + { + pNd = nullptr; + break; + } + if ( bFound ) + break; + ++aTmp; + } + + if( aTmp == rNodes.Count()-1 ) + pNd = nullptr; + else if( pNd ) + (*pIdx) = aTmp; + return pNd; +} + +static SwNode* GoPreviousWithFrame(SwNodeIndex *pIdx) +{ + if( !pIdx->GetIndex() ) + return nullptr; + + SwNodeIndex aTmp( *pIdx, -1 ); + SwNode* pNd(nullptr); + while( aTmp.GetIndex() ) + { + pNd = &aTmp.GetNode(); + bool bFound = false; + if ( pNd->IsContentNode() ) + // sw_redlinehide: assume that it's OK to find a node with the same + // frame as the caller's one + bFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First(); + else if ( pNd->IsTableNode() ) + bFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First(); + else if( pNd->IsStartNode() && !pNd->IsSectionNode() ) + { + pNd = nullptr; + break; + } + if ( bFound ) + break; + --aTmp; + } + + if( !aTmp.GetIndex() ) + pNd = nullptr; + else if( pNd ) + (*pIdx) = aTmp; + return pNd; +} + +/** + * The main purpose of this ctor is to find the right SwModify 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, sal_uLong nIdx, bool bSearch ) + : nIndex( nIdx ), bInit( 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() < nIndex ) + { + SwNodeIndex aTmp( *rNode.EndOfSectionNode(), +1 ); + pNd = GoPreviousWithFrame( &aTmp ); + if( pNd && rNode.GetIndex() > pNd->GetIndex() ) + pNd = nullptr; // Do not go over the limits + bMaster = false; + } + else + { + SwNodeIndex aTmp( rNode, -1 ); + pNd = GoNextWithFrame( rNode.GetNodes(), &aTmp ); + bMaster = true; + if( !bSearch && pNd && rNode.EndOfSectionIndex() < pNd->GetIndex() ) + pNd = nullptr; // Do not go over the limits + } + } + else + { + pNd = &rNode; + bMaster = nIndex < rNode.GetIndex(); + } + if( pNd ) + { + if( pNd->IsContentNode() ) + pMod = const_cast<SwModify*>(static_cast<SwModify const *>(pNd->GetContentNode())); + else + { + assert(pNd->IsTableNode()); + pMod = pNd->GetTableNode()->GetTable().GetFrameFormat(); + } + pIter.reset(new SwIterator<SwFrame, SwModify, sw::IteratorMode::UnwrapMulti>(*pMod)); + } + else + { + pIter = nullptr; + pMod = 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( !pIter ) + return nullptr; + if( !bInit ) + { + pRet = pIter->First(); + bInit = true; + } + else + pRet = pIter->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( !bMaster ) + { + 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( bMaster ) + { + if( pNd->GetIndex() >= nIndex ) + pRet = pSct; + } + else if( pNd->EndOfSectionIndex() < nIndex ) + pRet = pSct; + } + } + return pRet; + } + pRet = pIter->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 ); + } + } + pIter.reset(); + pMod = 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 = bMaster ? 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 = bMaster ? 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(), + bMaster ? 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( !bMaster ) + rpFrame = rpFrame->GetNext(); + return pUpper; +} + +void SwNode2LayImpl::RestoreUpperFrames( SwNodes& rNds, sal_uLong nStt, sal_uLong nEnd ) +{ + SwNode* pNd; + SwDoc *pDoc = 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, pDoc, 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 pMod ? ::GetFrameOfModify(nullptr, *pMod, FRM_ALL, nullptr, pDocPos ? &tmp : nullptr) : nullptr; +} + +SwNode2Layout::SwNode2Layout( const SwNode& rNd, sal_uLong nIdx ) + : pImpl( new SwNode2LayImpl( rNd, nIdx, false ) ) +{ +} + +SwNode2LayoutSaveUpperFrames::SwNode2LayoutSaveUpperFrames(const SwNode& rNd) + : pImpl( new SwNode2LayImpl( rNd, rNd.GetIndex(), true ) ) +{ + pImpl->SaveUpperFrames(); +} + +void SwNode2LayoutSaveUpperFrames::RestoreUpperFrames( + SwNodes& rNds, sal_uLong const nStt, sal_uLong const nEnd) +{ + pImpl->RestoreUpperFrames( rNds, nStt, nEnd ); +} + +SwFrame* SwNode2Layout::NextFrame() +{ + return pImpl->NextFrame(); +} + +SwLayoutFrame* SwNode2Layout::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode ) +{ + return pImpl->UpperFrame( rpFrame, rNode ); +} + +SwNode2Layout::~SwNode2Layout() +{ +} + +SwNode2LayoutSaveUpperFrames::~SwNode2LayoutSaveUpperFrames() +{ +} + +SwFrame* SwNode2Layout::GetFrame( const Point* pDocPos ) const +{ + return 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..01febcc78 --- /dev/null +++ b/sw/source/core/docnode/nodes.cxx @@ -0,0 +1,2335 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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 pDocument TODO: provide documentation + */ +SwNodes::SwNodes( SwDoc* pDocument ) + : m_vIndices(nullptr), m_pMyDoc( pDocument ) +{ + m_bInNodesDel = m_bInDelUpdOutline = false; + + assert(m_pMyDoc); + + sal_uLong 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(); +} + +void SwNodes::ChgNode( SwNodeIndex const & rDelPos, sal_uLong nSz, + SwNodeIndex& rInsPos, bool bNewFrames ) +{ + // no need for frames in the UndoArea + SwNodes& rNds = rInsPos.GetNodes(); + const SwNode* pPrevInsNd = rNds[ rInsPos.GetIndex() -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, 0 ); + + // NEVER include nodes from the RedLineArea + sal_uLong nNd = rInsPos.GetIndex(); + bool bInsOutlineIdx = !( + rNds.GetEndOfRedlines().StartOfSectionNode()->GetIndex() < nNd && + nNd < rNds.GetEndOfRedlines().GetIndex() ); + + 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 sal_uLong nDiff = rDelPos.GetIndex() < rInsPos.GetIndex() ? 0 : 1; + + for( sal_uLong 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()) + { + const SwNodePtr pSrch = &rNd; + m_pOutlineNodes->erase( pSrch ); + } + } + + BigPtrArray::Move( aDelIdx.GetIndex(), rInsPos.GetIndex() ); + + if( rNd.IsTextNode() ) + { + SwTextNode& rTextNd = static_cast<SwTextNode&>(rNd); + + rTextNd.AddToList(); + + if (bInsOutlineIdx && rTextNd.IsOutline()) + { + const SwNodePtr 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( sal_uLong 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 + pTextNd->InvalidateNumRule(); + + pTextNd->RemoveFromList(); + } + + RemoveNode( rDelPos.GetIndex(), 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, 0 ); + if( rNds.GetDoc() != GetDoc() ) + rNds.GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 ); + + if( bNewFrames ) + bNewFrames = &GetDoc()->GetNodes() == &rNds && + GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + + if( bNewFrames ) + { + // get the frames: + SwNodeIndex aIdx( *pPrevInsNd, 1 ); + SwNodeIndex aFrameNdIdx( aIdx ); + SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aFrameNdIdx, + rNds[ rInsPos.GetIndex() - 1 ] ); + + if( pFrameNd ) + 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 == 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()-1 >= aRg.aStart.GetIndex() && + aIndex.GetIndex()-1 < aRg.aEnd.GetIndex()) || + ( aIndex.GetIndex()-1 == aRg.aEnd.GetIndex() ) ) + return false; + } + + sal_uLong 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, -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 = 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 + sal_uLong nNd = aIdx.GetIndex(); + bool bInsOutlineIdx = !( rNodes.GetEndOfRedlines(). + StartOfSectionNode()->GetIndex() < nNd && + nNd < rNodes.GetEndOfRedlines().GetIndex() ); + + 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( sal_uLong 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( aMvIdx.GetIndex(), 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( sal_uLong 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(), 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( dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr ) + { + SwDDEFieldType* pTyp = static_cast<SwDDETable&>(pTableNd-> + GetTable()).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 = 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(), 1, false ); // delete EndNode + sal_uLong nSttPos = pSttNd->GetIndex(); + + // this StartNode will be removed later + SwStartNode* pTmpSttNd = new SwStartNode( *this, nSttPos+1 ); + pTmpSttNd->m_pStartOfSection = pSttNd->m_pStartOfSection; + + RemoveNode( nSttPos, 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 = 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( sal_uLong 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 = 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 = 0; + + // remove pointer from node array + RemoveNode( aRg.aEnd.GetIndex(), 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, 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 = 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, 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(), 1, true ); + RemoveNode( pRange->aEnd.GetIndex(), 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, sal_uLong nNodes) +{ + int nLevel = 0; // level counter + SwNode * pCurrentNode; + + sal_uLong nCnt = Count() - rIndex.GetIndex() - 1; + if( nCnt > nNodes ) nCnt = nNodes; + + if( nCnt == 0 ) // no count -> return + return; + + SwNodeRange aRg( rIndex, 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 = 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(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 = 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 = 0; + } + } + else // remove all nodes between start and end node (incl. both) + { + RemoveNode( aRg.aEnd.GetIndex(), nCnt + 2, true ); // delete array + nCnt = 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, 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; + } + pTextNd->InvalidateNumRule(); + } + else if( pCurrentNode->IsContentNode() ) + static_cast<SwContentNode*>(pCurrentNode)->InvalidateNumRule(); + + --aRg.aEnd; + nCnt++; + } + } + + ++aRg.aEnd; + if( nCnt != 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, 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 == 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, sal_uLong nCnt ) +{ + sal_uLong 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; + sal_uLong nEndIdx = (*ppEndNdArr)->GetIndex(); + + if( nSttIdx != nEndIdx ) + RemoveNode( nSttIdx, nEndIdx - nSttIdx, true ); + + ++ppEndNdArr; + } + } + else + { + int bUpdateNum = 0; + for( sal_uLong n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n ) + { + SwNode* pNd = (*this)[ n ]; + + if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline()) + { + // remove the outline indices + SwOutlineNodes::size_type nIdxPos; + if( m_pOutlineNodes->Seek_Entry( pNd, &nIdxPos )) + { + m_pOutlineNodes->erase(nIdxPos); + 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( const SwNodePtr& rpNode, void * pPara ) +{ + HighLevel * pHL = static_cast<HighLevel*>(pPara); + if( rpNode->GetStartNode() ) + pHL->nLevel++; + else if( rpNode->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; + sal_uLong nSttNdIdx = pStt->nNode.GetIndex() + 1; + const sal_uLong 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* const pInsDoc = pDestNd->GetDoc(); + ::sw::UndoGuard const ug(pInsDoc->GetIDocumentUndoRedo()); + pInsDoc->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* const pInsDoc = pDestNd->GetDoc(); + ::sw::UndoGuard const undoGuard(pInsDoc->GetIDocumentUndoRedo()); + pSrcNd->CopyCollFormat( *pDestNd ); + bCopyCollFormat = false; + } + + 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* const pInsDoc = pDestNd->GetDoc(); + ::sw::UndoGuard const ug(pInsDoc->GetIDocumentUndoRedo()); + pInsDoc->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* const pInsDoc = pDestNd->GetDoc(); + ::sw::UndoGuard const ug(pInsDoc->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 NodesArary + const sal_uLong 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* pDoc = rIndex.GetNode().GetDoc(); + + SwNode * pCurrentNode; + if( rIndex == 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(); + int nIsEndOfContent = (aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0; + + if (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() != 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( long nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex(); + nNodeCnt > 0; --nNodeCnt ) + { + pCurrentNode = &aRg.aStart.GetNode(); + switch( pCurrentNode->GetNodeType() ) + { + case SwNodeType::Table: + // Does it copy a table in(to) a footnote? + if( aInsPos < pDoc->GetNodes().GetEndOfInserts().GetIndex() && + pDoc->GetNodes().GetEndOfInserts().StartOfSectionIndex() + < aInsPos.GetIndex() ) + { + const long nDistance = + pCurrentNode->EndOfSectionIndex() - + aRg.aStart.GetIndex(); + if (nDistance < nNodeCnt) + nNodeCnt -= nDistance; + else + nNodeCnt = 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, + 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( pDoc, aInsPos ); + const long nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2; + if (nDistance < nNodeCnt) + nNodeCnt -= nDistance; + else + nNodeCnt = 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( pDoc, aInsPos ); + + const long nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2; + if (nDistance < nNodeCnt) + nNodeCnt -= nDistance; + else + nNodeCnt = 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( 1 == nNodeCnt && 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, 1, aInsPos ); + pDoc->GetNodes().SectionDown( &aTmpRg, + pCurrentNode->m_pStartOfSection->GetStartNodeType() ); + } + break; + + case SwNodeType::Text: + case SwNodeType::Grf: + case SwNodeType::Ole: + { + static_cast<SwContentNode*>(pCurrentNode)->MakeCopy( + pDoc, 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(), 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 > 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: improve documentation +//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 a table node with frames + * + * If no pEnd is given, search is started with FrameIndex; otherwise + * search is started with the one before rFrameIdx and after pEnd. + * + * @param rFrameIdx node with frames to search in + * @param pEnd ??? + * @return result node; 0 (!!!) if not found + */ +SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx, + const SwNode* pEnd ) const +{ + SwNode* pFrameNd = nullptr; + + // no layout -> skip + if( GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + SwNode* pSttNd = &rFrameIdx.GetNode(); + + // move of a hidden section? + SwSectionNode* 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* pTableNd = pSttNd->IsTableNode() + ? pSttNd->StartOfSectionNode()->FindTableNode() + : pSttNd->FindTableNode(); + SwNodeIndex aIdx( rFrameIdx ); + SwNode* pNd; + if( pEnd ) + { + --aIdx; + pNd = &aIdx.GetNode(); + } + else + pNd = pSttNd; + + if( ( pFrameNd = pNd )->IsContentNode() ) + rFrameIdx = aIdx; + + // search forward or backward for a content node + else if( nullptr != ( pFrameNd = GoPrevSection( &aIdx, true, false )) && + ::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()) + ) + { + rFrameIdx = aIdx; + } + else + { + if( pEnd ) + aIdx = pEnd->GetIndex() + 1; + else + aIdx = rFrameIdx; + + // NEVER leave the section when doing this! + if( ( pEnd && ( pFrameNd = &aIdx.GetNode())->IsContentNode() ) || + ( nullptr != ( pFrameNd = GoNextSection( &aIdx, true, false )) && + ::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()) + )) + { + // 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* pTableNode; + if (pSttNd->IsTableNode() && + nullptr != (pTableNode = pFrameNd->FindTableNode()) && + // TABLE IN TABLE: + pTableNode != pSttNd->StartOfSectionNode()->FindTableNode()) + { + pFrameNd = pTableNode; + rFrameIdx = *pFrameNd; + } + else + rFrameIdx = aIdx; + } + else if( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() ) + { + pFrameNd = pNd->StartOfSectionNode(); + rFrameIdx = *pFrameNd; + } + else + { + if( pEnd ) + aIdx = pEnd->GetIndex() + 1; + else + aIdx = rFrameIdx.GetIndex() + 1; + + if( (pFrameNd = &aIdx.GetNode())->IsTableNode() ) + rFrameIdx = aIdx; + else + { + 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(); + } + } + } + } + } + } + return pFrameNd; +} + +void SwNodes::ForEach( sal_uLong nStart, sal_uLong nEnd, + FnForEach_SwNodes fn, void* pArgs ) +{ + if( nEnd > m_nSize ) + nEnd = m_nSize; + + if( nStart < nEnd ) + { + sal_uInt16 cur = Index2Block( nStart ); + BlockInfo** pp = m_ppInf.get() + cur; + BlockInfo* p = *pp; + sal_uInt16 nElem = sal_uInt16( 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( sal_uLong nDelPos, sal_uLong nSz, bool bDel ) +{ +#ifndef NDEBUG + SwNode *const pFirst((*this)[nDelPos]); +#endif + for (sal_uLong 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 + sal_uLong 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 + sal_uLong 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(); + } + } + + sal_uLong nEnd = nDelPos + nSz; + SwNode* pNew = (*this)[ nEnd ]; + + for (SwNodeIndex& rIndex : m_vIndices->GetRingContainer()) + { + sal_uLong const nIdx = rIndex.GetIndex(); + if (nDelPos <= nIdx && nIdx < nEnd) + rIndex = *pNew; + } + + std::vector<BigPtrEntry> aTempEntries; + if( bDel ) + { + sal_uLong 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(nCnt); + + while( nCnt-- ) + { + delete pDel; + pDel = pPrev; + sal_uLong nPrevNdIdx = pPrev->GetPos(); + BigPtrEntry* pTempEntry = &aTempEntries[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 = pDel->GetPos() + 1; + } + + BigPtrArray::Remove( nDelPos, nSz ); +} + +void SwNodes::InsertNode( const SwNodePtr pNode, + const SwNodeIndex& rPos ) +{ + BigPtrEntry* pIns = pNode; + BigPtrArray::Insert( pIns, rPos.GetIndex() ); +} + +void SwNodes::InsertNode( const SwNodePtr pNode, + sal_uLong nPos ) +{ + BigPtrEntry* pIns = pNode; + BigPtrArray::Insert( pIns, nPos ); +} + +// ->#112139# +SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const +{ + if (nullptr != pNode) + { + SwNodeIndex aIdx(*pNode); + + if (aIdx <= (*this)[0]->EndOfSectionIndex()) + pNode = (*this)[0]; + else + { + while ((*this)[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_pMyDoc->GetNodes(); +} + +void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes")); + for (sal_uLong i = 0; i < Count(); ++i) + (*this)[i]->dumpAsXml(pWriter); + 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..273dd45c2 --- /dev/null +++ b/sw/source/core/docnode/observablethread.cxx @@ -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 . + */ + +#include <observablethread.hxx> +#include <ifinishedthreadlistener.hxx> +#include <memory> + +/* class for an observable thread + + #i73788# +*/ +ObservableThread::ObservableThread() + : mnThreadID( 0 ), + mpThreadListener() +{ +} + +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..5d93ea923 --- /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..b41125698 --- /dev/null +++ b/sw/source/core/docnode/retrievedinputstreamdata.cxx @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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::mnNextKeyValue = 1; + +namespace +{ + class theSwRetrievedInputStreamDataManager : + public rtl::Static< SwRetrievedInputStreamDataManager, theSwRetrievedInputStreamDataManager> + { + }; +} + +SwRetrievedInputStreamDataManager& SwRetrievedInputStreamDataManager::GetManager() +{ + return theSwRetrievedInputStreamDataManager::get(); +} + +SwRetrievedInputStreamDataManager::tDataKey SwRetrievedInputStreamDataManager::ReserveData( + std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > const & pThreadConsumer ) +{ + osl::MutexGuard aGuard(maMutex); + + // create empty data container for given thread Consumer + tDataKey nDataKey( mnNextKeyValue ); + tData aNewEntry( pThreadConsumer ); + maInputStreamData[ nDataKey ] = aNewEntry; + + // prepare next data key value + if ( mnNextKeyValue < SAL_MAX_UINT64 ) + { + ++mnNextKeyValue; + } + else + { + mnNextKeyValue = 1; + } + + return nDataKey; +} + +void SwRetrievedInputStreamDataManager::PushData( + const tDataKey nDataKey, + css::uno::Reference<css::io::XInputStream> const & xInputStream, + const bool bIsStreamReadOnly ) +{ + osl::MutexGuard aGuard(maMutex); + + std::map< tDataKey, tData >::iterator aIter = maInputStreamData.find( nDataKey ); + + if ( aIter != maInputStreamData.end() ) + { + // 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 ) +{ + osl::MutexGuard 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_LINK( SwRetrievedInputStreamDataManager, + LinkedInputStreamReady, + void*, p, void ) +{ + SwRetrievedInputStreamDataManager::tDataKey* pDataKey = static_cast<SwRetrievedInputStreamDataManager::tDataKey*>(p); + if ( !pDataKey ) + { + return; + } + + osl::MutexGuard aGuard(maMutex); + + 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..cfe313ae5 --- /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 <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 ) + : ObservableThread(), + mnDataKey( nDataKey ), + mrLinkedURL( rLinkedURL ), + mrReferer( rReferer ) +{ +} + +SwAsyncRetrieveInputStreamThread::~SwAsyncRetrieveInputStreamThread() +{ +} + +void SwAsyncRetrieveInputStreamThread::threadFunction() +{ + osl_setThreadName("SwAsyncRetrieveInputStreamThread"); + + css::uno::Sequence < css::beans::PropertyValue > xProps( 2 ); + xProps[0].Name = "URL"; + xProps[0].Value <<= mrLinkedURL; + xProps[1].Name = "Referer"; + xProps[1].Value <<= 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..c1f078429 --- /dev/null +++ b/sw/source/core/docnode/section.cxx @@ -0,0 +1,1594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <fmtftntx.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& rSectFormat; +public: + SwIntrnlSectRefLink( SwSectionFormat& rFormat, SfxLinkUpdateMode nUpdateType ) + : SwBaseLink( nUpdateType, SotClipboardFormatId::RTF ), + 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( sal_uLong nSttNd, sal_uLong 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 +} + +OUString SwSectionData::CollapseWhiteSpaces(const OUString& sName) +{ + const sal_Int32 nLen = sName.getLength(); + const sal_Unicode cRef = ' '; + OUStringBuffer aBuf(nLen+1); + for (sal_Int32 i = 0; i<nLen; ) + { + const sal_Unicode cCur = sName[i++]; + aBuf.append(cCur); + if (cCur!=cRef) + continue; + while (i<nLen && sName[i]==cRef) + ++i; + } + return aBuf.makeStringAndClear(); +} + +SwSection::SwSection( + SectionType const eType, OUString const& rName, SwSectionFormat & rFormat) + : SwClient(& rFormat) + , m_Data(eType, rName) +{ + SwSection *const pParentSect = GetParent(); + if( pParentSect ) + { + if( pParentSect->IsHiddenFlag() ) + { + SetHidden(); + } + + m_Data.SetProtectFlag( pParentSect->IsProtectFlag() ); + // edit in readonly sections + m_Data.SetEditInReadonlyFlag( pParentSect->IsEditInReadonlyFlag() ); + } + + if (!m_Data.IsProtectFlag()) + { + 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 + + 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 + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, pFormat ); + pFormat->ModifyNotification( &aMsgHint, &aMsgHint ); + 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 ) + { + 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 + SwMsgPoolItem aMsgItem( RES_SECTION_HIDDEN ); + pFormat->ModifyNotification( &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 + SwMsgPoolItem aMsgItem( RES_SECTION_NOT_HIDDEN ); + pFormat->ModifyNotification( &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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + bool bUpdateFootnote = false; + switch( pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ) + { + 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(); + const SfxPoolItem* pItem; + + if( SfxItemState::SET == pNewSet->GetItemState( + RES_PROTECT, false, &pItem ) ) + { + m_Data.SetProtectFlag( static_cast<SvxProtectItem const*>(pItem) + ->IsContentProtected() ); + pNewSet->ClearItem( RES_PROTECT ); + pOldSet->ClearItem( RES_PROTECT ); + } + + // --> edit in readonly sections + if( SfxItemState::SET == pNewSet->GetItemState( + RES_EDIT_IN_READONLY, false, &pItem ) ) + { + m_Data.SetEditInReadonlyFlag( + static_cast<SwFormatEditInReadonly const*>(pItem)->GetValue()); + pNewSet->ClearItem( RES_EDIT_IN_READONLY ); + pOldSet->ClearItem( RES_EDIT_IN_READONLY ); + } + + if( SfxItemState::SET == pNewSet->GetItemState( + RES_FTN_AT_TXTEND, false, &pItem ) || + SfxItemState::SET == pNewSet->GetItemState( + RES_END_AT_TXTEND, false, &pItem )) + { + bUpdateFootnote = true; + } + + if( !pNewSet->Count() ) + return; + } + break; + + case RES_PROTECT: + if( pNew ) + { + bool bNewFlag = + static_cast<const SvxProtectItem*>(pNew)->IsContentProtected(); + if( !bNewFlag ) + { + // Switching off: See if there is protection transferred + // by the Parents + const SwSection* pSect = this; + do { + if( pSect->IsProtect() ) + { + bNewFlag = true; + break; + } + pSect = pSect->GetParent(); + } while (pSect); + } + + 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() ) + { + 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, 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(); + } + + sal_uLong nEnd = pSectNd->EndOfSectionIndex(); + sal_uLong nStart = pSectNd->GetIndex()+1; + sw_DeleteFootnote( pSectNd, nStart, nEnd ); + } + if( pIdx ) + { + // 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->ModifyNotification( &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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + bool bClients = false; + sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + 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 )) + { + ModifyBroadcast( 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 ) ) + { + ModifyBroadcast( 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 )) + { + ModifyBroadcast( &pOldSet->Get( RES_FTN_AT_TXTEND ), pItem ); + pNewSet->ClearItem( RES_FTN_AT_TXTEND ); + pOldSet->ClearItem( RES_FTN_AT_TXTEND ); + } + if( SfxItemState::SET == pNewSet->GetItemState( + RES_END_AT_TXTEND, false, &pItem )) + { + ModifyBroadcast( &pOldSet->Get( RES_END_AT_TXTEND ), 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_FTN_AT_TXTEND: + case RES_END_AT_TXTEND : bClients = true; + [[fallthrough]]; + case RES_SECTION_HIDDEN: + case RES_SECTION_NOT_HIDDEN: + { + SwSection* pSect = GetSection(); + if( pSect && ( bClients || ( RES_SECTION_HIDDEN == nWhich ? + !pSect->IsHiddenFlag() : pSect->IsHiddenFlag() ) ) ) + { + ModifyBroadcast( 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! + if( HasWriterListeners() ) + { + ModifyBroadcast( 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::Modify( pOld, pNew ); // Rewire first! + 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::Modify( pOld, pNew ); // Rewire first! + UpdateParent(); + return; + } + break; + } + SwFrameFormat::Modify( pOld, pNew ); + + 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 SwModify::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() ) + { + 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; + + SwSection* pSection = nullptr; + const SvxProtectItem* pProtect(nullptr); + // edit in readonly sections + const SwFormatEditInReadonly* pEditInReadonly = nullptr; + bool bIsHidden = false; + + SwIterator<SwClient,SwSectionFormat> aIter(*this); + for(SwClient* pLast = aIter.First(); pLast; pLast = aIter.Next()) + { + if( dynamic_cast<const SwSectionFormat*>(pLast) != nullptr ) + { + if( !pSection ) + { + pSection = GetSection(); + if( GetRegisteredIn() ) + { + const SwSection* pPS = GetParentSection(); + pProtect = &pPS->GetFormat()->GetProtect(); + // edit in readonly sections + pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly(); + bIsHidden = pPS->IsHiddenFlag(); + } + else + { + pProtect = &GetProtect(); + // edit in readonly sections + pEditInReadonly = &GetEditInReadonly(); + bIsHidden = pSection->IsHidden(); + } + } + if (!pProtect->IsContentProtected() != + !pSection->IsProtectFlag()) + { + pLast->ModifyNotification( static_cast<SfxPoolItem const *>(pProtect), + static_cast<SfxPoolItem const *>(pProtect) ); + } + + // edit in readonly sections + if (!pEditInReadonly->GetValue() != + !pSection->IsEditInReadonlyFlag()) + { + pLast->ModifyNotification( static_cast<SfxPoolItem const *>(pEditInReadonly), + static_cast<SfxPoolItem const *>(pEditInReadonly) ); + } + + if( bIsHidden == pSection->IsHiddenFlag() ) + { + SwMsgPoolItem aMsgItem( static_cast<sal_uInt16>(bIsHidden + ? RES_SECTION_HIDDEN + : RES_SECTION_NOT_HIDDEN ) ); + pLast->ModifyNotification( &aMsgItem, &aMsgItem ); + } + } + else if( !pSection && + dynamic_cast<const SwSection*>(pLast) != nullptr ) + { + pSection = static_cast<SwSection*>(pLast); + if( GetRegisteredIn() ) + { + const SwSection* pPS = GetParentSection(); + pProtect = &pPS->GetFormat()->GetProtect(); + // edit in readonly sections + pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly(); + bIsHidden = pPS->IsHiddenFlag(); + } + else + { + pProtect = &GetProtect(); + // edit in readonly sections + pEditInReadonly = &GetEditInReadonly(); + bIsHidden = pSection->IsHidden(); + } + } + } +} + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormat")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + GetAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +void SwSectionFormats::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormats")); + for (size_t i = 0; i < size(); ++i) + GetFormat(i)->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +// Method to break section links inside a linked section +static void lcl_BreakSectionLinksInSect( const SwSectionNode& rSectNd ) +{ + if ( !rSectNd.GetDoc() ) + { + OSL_FAIL( "method <lcl_RemoveSectionLinksInSect(..)> - no Doc at SectionNode" ); + return; + } + + 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( SwBaseLink& rUpdLnk, SwSectionNode& rSectNd ) +{ + SwDoc* pDoc = rSectNd.GetDoc(); + SwDocShell* pDShell = pDoc->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 = pDoc->getIDocumentLinksAdministration().GetLinkManager().GetLinks(); + for( auto n = rLnks.size(); n; ) + { + SwBaseLink* pBLink; + + ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]); + if( pLnk != &rUpdLnk && + sfx2::SvBaseLinkObjectType::ClientFile == pLnk->GetObjType() && + dynamic_cast< const SwBaseLink *>( pLnk ) != nullptr && + ( pBLink = static_cast<SwBaseLink*>(pLnk) )->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 = rSectFormat.GetSectionNode(); + SwDoc* pDoc = 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() ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == xDocSh->GetMedium()->GetItemSet()-> + GetItemState( SID_PASSWORD, false, &pItem ) ) + rSection.SetLinkFilePassword( + static_cast<const SfxStringItem*>(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(), 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( 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 = 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 ] == &rSectFormat ) + { + SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + SwEditShell* pESh = pDoc->GetEditShell(); + + if( pESh ) + pESh->StartAllAction(); + else + pSh->StartAction(); + + SwSectionData aSectionData(*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 = 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(SwSectionData::CollapseWhiteSpaces(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() ); +} + +const SwNode* SwIntrnlSectRefLink::GetAnchor() const +{ + return rSectFormat.GetSectionNode(); +} + +bool SwIntrnlSectRefLink::IsInRange( sal_uLong nSttNd, sal_uLong nEndNd ) const +{ + SwStartNode* pSttNd = 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..8b7832937 --- /dev/null +++ b/sw/source/core/docnode/swbaslnk.cxx @@ -0,0 +1,359 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/docfile.hxx> +#include <sfx2/lnkbase.hxx> +#include <sfx2/objsh.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 <DocumentLinksAdministrationManager.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <swevent.hxx> +#include <swbaslnk.hxx> +#include <swserv.hxx> +#include <viewsh.hxx> +#include <ndgrf.hxx> +#include <hints.hxx> +#include <cntfrm.hxx> +#include <htmltbl.hxx> +#include <calbck.hxx> +#include <dialoghelp.hxx> +#include <memory> + +using namespace com::sun::star; + +static bool SetGrfFlySize( const Size& rGrfSz, SwGrfNode* pGrfNd, const Size &rOrigGrfSize ); + + +static void lcl_CallModify( SwGrfNode& rGrfNd, SfxPoolItem& rItem ) +{ + //call first all not SwNoTextFrames, then the SwNoTextFrames. + // The reason is, that in the SwNoTextFrames the Graphic + // after a Paint will be swapped out! So all other "behind" + // them haven't a loaded Graphic. + rGrfNd.LockModify(); + { + SwIterator<SwClient,SwGrfNode> aIter(rGrfNd); + for(SwClient* pLast = aIter.First(); pLast; pLast = aIter.Next()) + if(dynamic_cast<const SwContentFrame*>( pLast) == nullptr) + pLast->ModifyNotification(&rItem, &rItem); + } + { + SwIterator<SwContentFrame,SwGrfNode> aIter(rGrfNd); + for(SwClient* pLast = aIter.First(); pLast; pLast = aIter.Next()) + pLast->ModifyNotification(&rItem, &rItem); + } + rGrfNd.UnlockModify(); +} + +::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* pDoc = m_pContentNode->GetDoc(); + if( pDoc->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 ); + pDoc->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(pDoc); + + if (pDoc->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) + { + if (!SetGrfFlySize(aGrfSz, pSwGrfNode, aOldSz)) + { + SwMsgPoolItem aMsgHint(RES_GRAPHIC_ARRIVED); + lcl_CallModify(*pSwGrfNode, aMsgHint); + return SUCCESS; + } + } + + 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 *pDoc = 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( *pDoc ); + 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( sal_uLong, sal_uLong ) 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..ee5a3144c --- /dev/null +++ b/sw/source/core/docnode/swthreadjoiner.cxx @@ -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 . + */ + +#include <swthreadjoiner.hxx> +#include <com/sun/star/util/JobManager.hpp> +#include <comphelper/processfactory.hxx> +#include <osl/mutex.hxx> +#include <rtl/instance.hxx> + +// Testing + +using namespace ::com::sun::star; + +namespace +{ + class theJoinerMutex : public rtl::Static<osl::Mutex, theJoinerMutex> {}; + + uno::Reference< util::XJobManager > pThreadJoiner; +} + +uno::Reference< util::XJobManager >& SwThreadJoiner::GetThreadJoiner() +{ + osl::MutexGuard aGuard(theJoinerMutex::get()); + + 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..4b646e8b2 --- /dev/null +++ b/sw/source/core/docnode/swthreadmanager.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 <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::mbThreadManagerInstantiated = false; + +SwThreadManager::SwThreadManager() + : mpThreadManagerImpl( new ThreadManager( SwThreadJoiner::GetThreadJoiner() ) ) +{ + mpThreadManagerImpl->Init(); + mbThreadManagerInstantiated = true; +} + +SwThreadManager::~SwThreadManager() +{ +} + +namespace { + +struct InitInstance : public rtl::Static<SwThreadManager, InitInstance> {}; + +} + +SwThreadManager& SwThreadManager::GetThreadManager() +{ + return InitInstance::get(); +} + +bool SwThreadManager::ExistsThreadManager() +{ + return mbThreadManagerInstantiated; +} + +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..5ace4c002 --- /dev/null +++ b/sw/source/core/docnode/threadlistener.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 <threadlistener.hxx> +#include "threadmanager.hxx" + +/** helper class to observe threads + + #i73788# +*/ +ThreadListener::ThreadListener( ThreadManager& rThreadListenerOwner ) + : IFinishedThreadListener(), + 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..c2e7c46ed --- /dev/null +++ b/sw/source/core/docnode/threadmanager.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 "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::mnStartedSize = 10; + +ThreadManager::ThreadManager( uno::Reference< util::XJobManager > const & rThreadJoiner ) + : maMutex(), + mrThreadJoiner( rThreadJoiner ), + mpThreadListener(), + mnThreadIDCounter( 0 ), + maWaitingForStartThreads(), + maStartedThreads(), + 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 ) + +{ + osl::MutexGuard 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() < mnStartedSize && + !StartingOfThreadsSuspended() ) + { + // 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 ---- + osl::MutexGuard 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 + 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) +{ + osl::MutexGuard aGuard(maMutex); + + if ( !StartingOfThreadsSuspended() ) + { + // 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() +{ + osl::MutexGuard aGuard(maMutex); + + mbStartingOfThreadsSuspended = false; + + while ( maStartedThreads.size() < mnStartedSize && + !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..21078e7ab --- /dev/null +++ b/sw/source/core/docnode/threadmanager.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_THREADMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_DOCNODE_THREADMANAGER_HXX + +#include <vcl/idle.hxx> +#include <osl/mutex.hxx> +#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() + { + osl::MutexGuard aGuard(maMutex); + + mbStartingOfThreadsSuspended = true; + } + + /** continues the starting of threads after it has been suspended + */ + void ResumeStartingOfThreads(); + + bool StartingOfThreadsSuspended() + { + osl::MutexGuard 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 mnStartedSize; + + osl::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 ); +}; +#endif + +/* 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..ad672260a --- /dev/null +++ b/sw/source/core/draw/dcontact.cxx @@ -0,0 +1,2530 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lrspitem.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 <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 <doc.hxx> +#include <hints.hxx> +#include <txtfrm.hxx> +#include <frameformats.hxx> +#include <docary.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 ) +{ + SdrTextAniKind eTKind; + return SdrInventor::Default == rObj.GetObjInventor() && + OBJ_TEXT == rObj.GetObjIdentifier() && + ( SdrTextAniKind::Scroll == ( eTKind = static_cast<const SdrTextObj&>(rObj).GetTextAniKind()) + || 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 ) + { + 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) +{ + // 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) +{ + // 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(nullptr); + if(nullptr != (pPg = pContact->GetMaster()->getSdrPageFromSdrObject())) + { + 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)); + // #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 ), + maAnchoredDrawObj(), + 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* pDoc) +{ + for(auto& rpFly : *pDoc->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() +{ + maDrawVirtObjs.push_back( + SwDrawVirtObjPtr( + new SwDrawVirtObj( + GetMaster()->getSdrModelFromSdrObject(), + *GetMaster(), + *this))); + maDrawVirtObjs.back()->AddToDrawingPage(); + 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::NotifyBackgrdOfAllVirtObjs(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() ) + { + // #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() ) + { + 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. + SfxItemSet aResizeSet(pFormat->GetDoc()->GetAttrPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE>{}); + SwFormatFrameSize aSize; + aResizeSet.Put(aSize); + SwTextBoxHelper::syncFlyFrameAttr(*pFormat, aResizeSet); + } +} + +// !!!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. + NotifyBackgrdOfAllVirtObjs( 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 + SwRect 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(); + } + 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"); + } + } + SfxItemSet aSet( GetFormat()->GetDoc()->GetAttrPool(), + svl::Items<RES_VERT_ORIENT, RES_HORI_ORIENT>{} ); + 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.SVRect() ); + } + else if ( aObjRect.SSize() != 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()); + } + } + 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 ) + { + static_cast<const SwAttrSetChg&>(_rItem).GetChgSet()-> + GetItemState( RES_ANCHOR, false, reinterpret_cast<const SfxPoolItem**>(&pAnchorFormat) ); + } + 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 (auto pLegacyHint = dynamic_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); + NotifyBackgrdOfAllVirtObjs(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::ShapeProperty::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); + NotifyBackgrdOfAllVirtObjs(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 ); + NotifyBackgrdOfAllVirtObjs( &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 ( dynamic_cast<const SwDrawVirtObj*>( _pDrawObj) != nullptr ) + { + SwDrawVirtObj* pDrawVirtObj = static_cast<SwDrawVirtObj*>(_pDrawObj); + pDrawVirtObj->RemoveFromWriterLayout(); + pDrawVirtObj->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(..)> + SwModify *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, SwModify, 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 + // (2) drawing object isn't a control object to be anchored + // in header/footer. + bool bAdd = ( !pFrame->IsContentFrame() || + !static_cast<SwContentFrame*>(pFrame)->IsFollow() ) && + ( !::CheckControlLayer( GetMaster() ) || + !pFrame->FindFooterOrHeader() ); + + if (bAdd && RndStdIds::FLY_AT_FLY != pAnch->GetAnchorId()) + { + 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(); + 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( GetMaster()->GetCurrentBoundRect() ); + if ( GetPageFrame() != pPg ) + { + // 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 drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) 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); + } + } + } + } + } + + drawinglayer::primitive2d::Primitive2DContainer VOCOfDrawVirtObj::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) 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 + xRetval = rReferencedObject.GetViewContact().getViewIndependentPrimitive2DContainer(); + } + + if(!xRetval.empty()) + { + // create transform primitive + const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::TransformPrimitive2D(aOffsetMatrix, xRetval)); + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + + return 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 ), + maAnchoredDrawObj(), + mrDrawContact(_rDrawContact) +{ + // #i26791# + maAnchoredDrawObj.SetDrawObj( *this ); + + // #i35635# - set initial position out of sight + NbcMove( Size( -16000, -16000 ) ); +} + +SwDrawVirtObj::~SwDrawVirtObj() +{ +} + +SwDrawVirtObj& SwDrawVirtObj::operator=( const SwDrawVirtObj& rObj ) +{ + SdrVirtObj::operator=(rObj); + // Note: Members <maAnchoredDrawObj> and <mrDrawContact> + // haven't to be considered. + return *this; +} + +SwDrawVirtObj* SwDrawVirtObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + SwDrawVirtObj* pObj = new SwDrawVirtObj( + rTargetModel, + rRefObj, + mrDrawContact); + + pObj->operator=( *this ); + // Note: Member <maAnchoredDrawObj> hasn't to be considered. + + return pObj; +} + +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() +{ + // determine 'master' + SdrObject* pOrgMasterSdrObj = mrDrawContact.GetMaster(); + + // insert 'virtual' drawing object into page, set layer and user call. + SdrPage* pDrawPg; + // #i27030# - apply order number of referenced object + if ( nullptr != ( pDrawPg = pOrgMasterSdrObj->getSdrPageFromSdrObject() ) ) + { + // #i27030# - apply order number of referenced object + pDrawPg->InsertObject( this, GetReferencedObj().GetOrdNum() ); + } + else + { + pDrawPg = getSdrPageFromSdrObject(); + if ( pDrawPg ) + { + pDrawPg->SetObjectOrdNum( GetOrdNumDirect(), + GetReferencedObj().GetOrdNum() ); + } + else + { + SetOrdNum( GetReferencedObj().GetOrdNum() ); + } + } + 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(aOutRect.IsEmpty()) + { + const_cast<SwDrawVirtObj*>(this)->RecalcBoundRect(); + } + + return aOutRect; +} + +const tools::Rectangle& SwDrawVirtObj::GetLastBoundRect() const +{ + return aOutRect; +} + +Point SwDrawVirtObj::GetOffset() const +{ + // do NOT use IsEmpty() here, there is already a useful offset + // in the position + if(aOutRect == tools::Rectangle()) + { + return Point(); + } + else + { + return 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()); + 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); + SetRectsDirty(); +} + +void SwDrawVirtObj::NbcRotate(const Point& rRef, long nAngle, double sn, double cs) +{ + rRefObj.NbcRotate(rRef - GetOffset(), nAngle, sn, cs); + SetRectsDirty(); +} + +void SwDrawVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2) +{ + rRefObj.NbcMirror(rRef1 - GetOffset(), rRef2 - GetOffset()); + SetRectsDirty(); +} + +void SwDrawVirtObj::NbcShear(const Point& rRef, long nAngle, double tn, bool bVShear) +{ + rRefObj.NbcShear(rRef - GetOffset(), nAngle, tn, bVShear); + SetRectsDirty(); +} + +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(pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Resize(rRef - GetOffset(), xFact, yFact, bUnsetRelative); + SetRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); + } +} + +void SwDrawVirtObj::Rotate(const Point& rRef, long nAngle, double sn, double cs) +{ + if(nAngle) + { + tools::Rectangle aBoundRect0; if(pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Rotate(rRef - GetOffset(), nAngle, sn, cs); + SetRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); + } +} + +void SwDrawVirtObj::Mirror(const Point& rRef1, const Point& rRef2) +{ + tools::Rectangle aBoundRect0; if(pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Mirror(rRef1 - GetOffset(), rRef2 - GetOffset()); + SetRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::Shear(const Point& rRef, long nAngle, double tn, bool bVShear) +{ + if(nAngle) + { + tools::Rectangle aBoundRect0; if(pUserCall) aBoundRect0 = GetLastBoundRect(); + rRefObj.Shear(rRef - GetOffset(), nAngle, tn, bVShear); + SetRectsDirty(); + 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(pUserCall) aBoundRect0 = GetLastBoundRect(); + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.SetSnapRect(aR); + SetRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::NbcSetSnapRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aR(rRect); + aR -= GetOffset(); + SetRectsDirty(); + 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(pUserCall) aBoundRect0 = GetLastBoundRect(); + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.SetLogicRect(aR); + SetRectsDirty(); + SendUserCall(SdrUserCallType::Resize, aBoundRect0); +} + +void SwDrawVirtObj::NbcSetLogicRect(const tools::Rectangle& rRect) +{ + tools::Rectangle aR(rRect); + aR -= GetOffset(); + rRefObj.NbcSetLogicRect(aR); + SetRectsDirty(); +} + +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); + SetRectsDirty(); +} + +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..f794887fe --- /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 <tools/mapunit.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/baseprimitive2d.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 drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() const override; + + public: + /// basic constructor, used from SdrObject. + explicit VCOfSwFlyDrawObj(SwFlyDrawObj& rObj) + : ViewContactOfSdrObj(rObj) + { + } + }; + + } + + drawinglayer::primitive2d::Primitive2DContainer VCOfSwFlyDrawObj::createViewIndependentPrimitive2DSequence() const + { + // currently gets not visualized, return empty sequence + return drawinglayer::primitive2d::Primitive2DContainer(); + } + +} // 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; +} + +sal_uInt16 SwFlyDrawObj::GetObjIdentifier() const +{ + return 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) + : BufferedDecompositionPrimitive2D(), + 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()) + { + // 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 + ImplPrimitive2DIDBlock(SwVirtFlyDrawObjPrimitive, 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 drawinglayer::primitive2d::Primitive2DContainer createViewIndependentPrimitive2DSequence() 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 +{ + drawinglayer::primitive2d::Primitive2DContainer VCOfSwVirtFlyDrawObj::createViewIndependentPrimitive2DSequence() const + { + drawinglayer::primitive2d::Primitive2DContainer xRetval; + 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)); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xPrimitive }; + } + } + + return xRetval; + } + +} // 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, long nAngle, 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 (!) + nAngle /= 10; + + while(nAngle < 0) + { + nAngle += 3600; + } + + SwWrtShell *pShForAngle = nAngle ? dynamic_cast<SwWrtShell*>(GetFlyFrame()->getRootFrame()->GetCurrShell()) : nullptr; + if (pShForAngle) + { + // RotGrfFlyFrame: Add transformation to placeholder object + Size aSize; + const sal_uInt16 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize)); + SwFlyFrameAttrMgr aMgr(false, pShForAngle, Frmmgr_Type::NONE, nullptr); + + aMgr.SetRotation(nOldRot, (nOldRot + static_cast<sal_uInt16>(nAngle)) % 3600, aSize); + } + } + else + { + // call parent + SdrVirtObj::Rotate(rRef, nAngle, 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(); + bMovProt = rP.IsPosProtected(); + 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() ) + { + mpOutDev->Push(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() ) + { + bool bDrawObject(true); + + if ( !SwFlyFrame::IsPaint( const_cast<SwVirtFlyDrawObj*>(this), pShell ) ) + { + bDrawObject = false; + } + + if ( bDrawObject ) + { + // 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() ) + { + // 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(), GetFlyFrame()->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)->aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + else + const_cast<SwVirtFlyDrawObj*>(this)->aOutRect = tools::Rectangle(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const +{ + SetRect(); + return 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 aOutRect; +} + +void SwVirtFlyDrawObj::SetSnapRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (pUserCall!=nullptr) + pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp); +} + +void SwVirtFlyDrawObj::NbcSetSnapRect(const tools::Rectangle& ) +{ + SetRect(); +} + +const tools::Rectangle& SwVirtFlyDrawObj::GetLogicRect() const +{ + SetRect(); + return aOutRect; +} + +void SwVirtFlyDrawObj::SetLogicRect(const tools::Rectangle& ) +{ + tools::Rectangle aTmp( GetLastBoundRect() ); + SetRect(); + SetChanged(); + BroadcastObjectChange(); + if (pUserCall!=nullptr) + 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(); + aOutRect = GetFlyFrame()->getFrameArea().SVRect(); + } + + aOutRect.Move( rSiz ); + const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() ); + const Point aNewPos( aOutRect.TopLeft() ); + const SwRect aFlyRect( 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(); + 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; + + 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(); + 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 long nOldWidth(aOldRect.GetWidth()); + const 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 + SfxItemSet aSet( pSh->GetAttrPool(), svl::Items<RES_GRFATR_CROPGRF, RES_GRFATR_CROPGRF>{} ); + 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 (convertMm100ToTwip(nLeftCrop)); + aCrop.SetTop (convertMm100ToTwip(nTopCrop)); + aCrop.SetRight (convertMm100ToTwip(nRightCrop)); + aCrop.SetBottom(convertMm100ToTwip(nBottomCrop)); + pSh->SetAttrItem(aCrop); + + // Set new frame size + SwFrameFormat *pFormat = GetFormat(); + SwFormatFrameSize aSz( pFormat->GetFrameSize() ); + const long aNewWidth(aNewRect.GetWidth() + (aOutRect.GetWidth() - aOldRect.GetWidth())); + const long aNewHeight(aNewRect.GetHeight() + (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 + 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( 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( aOutRect.Right() - aOutRect.Left() + 1, aOutRect.Bottom()- 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(); + 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() ) + { + 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 ? aOutRect.Right() + 1 : aOutRect.Left(), aOutRect.Top()); + + if ( aNewPos != aOldPos ) + { + // 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()); + 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(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 +sal_uInt16 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const +{ + sal_uInt16 nRetval(0); + 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; +} + +long SwVirtFlyDrawObj::GetRotateAngle() const +{ + if(ContainsSwGrfNode()) + { + Size aSize; + return 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()) + { + // 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()) + { + // 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.IsInside( 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.IsInside( 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); +} + +/* 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..31bef2f94 --- /dev/null +++ b/sw/source/core/draw/dobjfac.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 <dobjfac.hxx> +#include <dflyobj.hxx> + +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 == 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..39e5f93e3 --- /dev/null +++ b/sw/source/core/draw/dpage.cxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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), + pDoc(&rNewModel.GetDoc()) +{ +} + +SwDPage::~SwDPage() +{ +} + +void SwDPage::lateInit(const SwDPage& rSrcPage) +{ + FmFormPage::lateInit( rSrcPage ); + + if ( rSrcPage.pGridLst ) + { + pGridLst.reset( new SdrPageGridFrameList ); + for ( sal_uInt16 i = 0; i != rSrcPage.pGridLst->GetCount(); ++i ) + pGridLst->Insert( ( *rSrcPage.pGridLst )[ i ] ); + } +} + +SwDPage* SwDPage::CloneSdrPage(SdrModel& rTargetModel) const +{ + SwDrawModel& rSwDrawModel(static_cast< SwDrawModel& >(rTargetModel)); + 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 ( pGridLst ) + const_cast<SwDPage*>(this)->pGridLst->Clear(); + else + const_cast<SwDPage*>(this)->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().IsOver( aRect ) ) + ::InsertGridFrame( const_cast<SwDPage*>(this)->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)->pGridLst.get(), pPg ); + pPg = pPg->GetNext(); + } while ( pPg && pPg->getFrameArea().IsOver( pSh->VisArea() ) ); + } + } + return pGridLst.get(); +} + +bool SwDPage::RequestHelp( vcl::Window* pWindow, SdrView const * pView, + const HelpEvent& rEvt ) +{ + assert( 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; + if (pDrawObj) + { + SwFlyFrame *pFly = pDrawObj->GetFlyFrame(); + + aPixRect = pWindow->LogicToPixel(pFly->getFrameArea().SVRect()); + + const SwFormatURL &rURL = pFly->GetFormat()->GetURL(); + 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.eEvent == SdrEventKind::ExecuteUrl) + { + sText = aVEvt.pURLField->GetURL(); + aPixRect = pWindow->LogicToPixel(aVEvt.pObj->GetLogicRect()); + } + } + + if (!sText.isEmpty()) + { + // #i80029# + bool bExecHyperlinks = pDoc->GetDocShell()->IsReadOnly(); + if (!bExecHyperlinks) + 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( pDoc ); + + Reference < XInterface > xRet; + SwDocShell* pDocShell = 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..7fa2bd715 --- /dev/null +++ b/sw/source/core/draw/drawdoc.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 <svx/svxids.hrc> + +#include <com/sun/star/frame/XModel.hpp> +#include <svx/drawitem.hxx> +#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 *const pDoc) +: FmFormModel( + &pDoc->GetAttrPool(), + pDoc->GetDocShell()) + , m_pDoc( pDoc ) +{ + SetScaleUnit( MapUnit::MapTwip ); + SetSwapGraphics(); + + // use common InitDrawModelAndDocShell which will set the associations as needed, + // including SvxColorTableItem with WhichID SID_COLOR_TABLE + InitDrawModelAndDocShell(m_pDoc->GetDocShell(), this); + + // copy all the default values to the SdrModel + SfxItemPool* pSdrPool = m_pDoc->GetAttrPool().GetSecondaryPool(); + if( pSdrPool ) + { + const sal_uInt16 aWhichRanges[] = + { + RES_CHRATR_BEGIN, RES_CHRATR_END, + RES_PARATR_BEGIN, RES_PARATR_END, + 0 + }; + + SfxItemPool& rDocPool = m_pDoc->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_pDoc->GetDocumentSettingManager().getForbiddenCharacterTable()); + // Implementation for asian compression + SetCharCompressType( m_pDoc->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. + */ +SdrPage* SwDrawModel::AllocPage(bool bMasterPage) +{ + SwDPage* pPage = new SwDPage(*this, bMasterPage); + pPage->SetName("Controls"); + return pPage; +} + +uno::Reference<embed::XStorage> SwDrawModel::GetDocumentStorage() const +{ + return m_pDoc->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..d02f57e9f --- /dev/null +++ b/sw/source/core/draw/dview.cxx @@ -0,0 +1,998 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <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> +#include <UndoManager.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 ) != nullptr ? + static_cast<const SwVirtFlyDrawObj*>(pObj) : nullptr; + 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(getOptionsDrawinglayer().IsOverlayBuffer_Writer()); + + // #i74769#, #i75172# Use default from the configuration + SetBufferedOutputAllowed(getOptionsDrawinglayer().IsPaintBuffer_Writer()); +} + +// #i99665# +bool SwDrawView::IsAntiAliasing() const +{ + return getOptionsDrawinglayer().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; + if(nullptr == (pAnch = CalcAnchor())) + 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 ); + SwRect aAutoPos = pAnchoredObj->GetLastCharRect(); + if ( aAutoPos.Height() ) + { + aPos = aAutoPos.Pos(); + } + } + + // add anchor handle: + maHdlList.AddHdl( std::make_unique<SwSdrHdl>( aPos, ( pAnch->IsVertical() && !pAnch->IsVertLR() ) || + pAnch->IsRightToLeft() ) ); +} + +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 ) + { + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + const SwFlyFrame *pTmpFlyFrame = static_cast<SwFlyFrame*>(pAnchoredObj); + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true); + m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() ); + } + } + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + const SwFlyFrame *pTmpFlyFrame = static_cast<SwFlyFrame*>(pAnchoredObj); + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true); + m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() ); + } + } + 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 ( dynamic_cast< const SwFlyFrame *>( pMovedAnchoredObj ) != nullptr && + 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 ( dynamic_cast< const SwFlyFrame *>( pMovedAnchoredObj ) != nullptr ) + { + const SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pMovedAnchoredObj); + + // adjustments for accessibility API + m_rImp.DisposeAccessibleFrame( pFlyFrame ); + m_rImp.AddAccessibleFrame( pFlyFrame ); + + 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 ( dynamic_cast< const SwVirtFlyDrawObj *>( pTmpObj ) != nullptr ) + { + const SwFlyFrame *pTmpFlyFrame = + static_cast<SwVirtFlyDrawObj*>(pTmpObj)->GetFlyFrame(); + m_rImp.DisposeAccessibleFrame( pTmpFlyFrame ); + m_rImp.AddAccessibleFrame( pTmpFlyFrame ); + } + else + { + m_rImp.DisposeAccessibleObj(pTmpObj, true); + m_rImp.AddAccessibleObj( pTmpObj ); + } + } + else + { + // adjust loop counter + if ( bMovedForward ) + ++i; + else if (i > 0) + --i; + } + + } while ( ( bMovedForward && i < ( nObjCount - aMovedChildObjs.size() ) ) || + ( !bMovedForward && i > ( nNewPos + aMovedChildObjs.size() ) ) ); + } + else + { + // adjustments for accessibility API + m_rImp.DisposeAccessibleObj(pObj, true); + m_rImp.AddAccessibleObj( pObj ); + } + + 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; + const bool bFly = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) != nullptr; + if ( bFly ) + { + pAnch = static_cast<SwVirtFlyDrawObj*>(pObj)->GetFlyFrame()->GetAnchorFrame(); + aMyRect = static_cast<SwVirtFlyDrawObj*>(pObj)->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 ( !bFly ) + { + 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() ) + { + // 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 ( dynamic_cast< const SwDrawVirtObj *>( pMarkObj ) != nullptr ) + { + SdrObject* pRefObj = &(static_cast<SwDrawVirtObj*>(pMarkObj)->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 (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(); +} + +// support enhanced text edit for draw objects +SdrUndoManager* SwDrawView::getSdrUndoManagerForEnhancedTextEdit() const +{ + SwDoc* pDoc = Imp().GetShell()->GetDoc(); + + return pDoc ? dynamic_cast< SdrUndoManager* >(&(pDoc->GetUndoManager())) : nullptr; +} + +/* 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..f26f23732 --- /dev/null +++ b/sw/source/core/edit/acorrect.cxx @@ -0,0 +1,693 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/i18n/BreakType.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/linguistic2/XHyphenator.hpp> +#include <com/sun/star/linguistic2/XHyphenatedWord.hpp> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> + +#include <editeng/acorrcfg.hxx> + +using namespace ::com::sun::star; + +namespace { + +class PaMIntoCursorShellRing +{ + SwPaM &rDelPam, &rCursor; + SwPaM* pPrevDelPam; + SwPaM* 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 ) + : rDelPam( rPam ), rCursor( rShCursor ) +{ + SwPaM* pShCursor = rCSh.GetCursor_(); + + pPrevDelPam = rDelPam.GetPrev(); + pPrevCursor = rCursor.GetPrev(); + + rDelPam.GetRingContainer().merge( pShCursor->GetRingContainer() ); + rCursor.GetRingContainer().merge( pShCursor->GetRingContainer() ); +} + +PaMIntoCursorShellRing::~PaMIntoCursorShellRing() +{ + // and take out the Pam again: + RemoveFromRing( rDelPam, pPrevDelPam ); + RemoveFromRing( rCursor, 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; + } + } + + 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 + { + if( nSourceLength != rText.getLength() ) + { + 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 + pDoc->getIDocumentContentOperations().Overwrite( *pPam, rText ); + } + + 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))); + + SfxItemSet aSet( m_rEditSh.GetDoc()->GetAttrPool(), + svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{} ); + 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 && 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(":"); + + SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(rSttPos)), + pFrame->MapViewToModelPos(TextFrameIndex(nEndPos + (replaceLastChar ? 1 : 0)))); + + 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 ) +{ + 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() ) + { + // 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()) + { + Color* pColor = nullptr; + Color** ppColor = &pColor; + // Send text as NatNum12 prefix + OUString sPrefix("[NatNum12 " + sDisambiguatedWord.makeStringAndClear() + "]0"); + if (pFormatter->GetPreviewString(sPrefix, 0, sConverted, ppColor, LANGUAGE_USER_HUNGARIAN_ROVAS)) + 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 ) +{ + sal_uLong 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->AddWrtSttException(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 ) + { + const sal_Int32 nStart = rPos.nContent.GetIndex(); + if( nStart == pTextNd->GetText().getLength() ) + pTextNd->FormatToTextAttr( pTextNd ); + + if( pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count() ) + { + 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..7588104d5 --- /dev/null +++ b/sw/source/core/edit/autofmt.cxx @@ -0,0 +1,2834 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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 + sal_uLong 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( m_aNdIdx.GetIndex() + m_nEndNdIdx - m_aEndNdIdx.GetIndex(), + m_pDoc->GetDocShell() ); + + m_pCurTextNd = static_cast<SwTextNode*>(pNewNd); + m_pCurTextFrame = GetFrame( *m_pCurTextNd ); +} + +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() != nullptr) + { + assert(!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, 0.05 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); + aLine.SetWidth( DEF_LINE_WIDTH_0 ); + break; + case 2: // single, 1.0 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); + aLine.SetWidth( DEF_LINE_WIDTH_1 ); + break; + case 3: // double, 1.0 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + aLine.SetWidth( DEF_LINE_WIDTH_1 ); + break; + case 4: // double (thick/thin), 4.0 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP); + aLine.SetWidth( DEF_LINE_WIDTH_3 ); + break; + case 5: // double (thin/thick), 4.0 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP); + aLine.SetWidth( DEF_LINE_WIDTH_3 ); + break; + case 6: // double, 2.5 pt + aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + aLine.SetWidth( DEF_LINE_WIDTH_2 ); + break; + } + SfxItemSet aSet(m_pDoc->GetAttrPool(), + svl::Items<RES_PARATR_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER, + RES_BOX, RES_BOX>{}); + 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( static_cast<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( static_cast<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, OUString(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 = static_cast<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 = static_cast<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 + SfxItemSet aSet( + m_pDoc->GetAttrPool(), + svl::Items< + RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_PARATR_ADJUST, RES_PARATR_ADJUST, + RES_PARATR_TABSTOP, RES_PARATR_DROP, + RES_BACKGROUND, RES_SHADOW>{}); + + 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 + SvxAdjustItem const * pAdj; + if( SfxItemState::SET == aSet.GetItemState( RES_PARATR_ADJUST, + false, reinterpret_cast<const SfxPoolItem**>(&pAdj) )) + { + 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 SfxPoolItem* pItem; + if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) + && SvxBreak::NONE != static_cast<const SvxFormatBreakItem*>(pItem)->GetBreak() ) + return true; + + if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) + && static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc() + && UseOnPage::NONE != static_cast<const SwFormatPageDesc*>(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 ) + { + // 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 ); + + // 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 ); +} + +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 ) + { + // 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_Unicode 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( static_cast<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( static_cast<sal_uInt16>(aPrefix.getToken( 0, u'\x0001', nPrefixIdx ).toInt32())); + 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( static_cast<sal_uInt16>(aPrefix.getToken( 0, u'\x0001', nPrefixIdx ).toInt32() )); + 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( static_cast<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 = OUStringChar( m_aFlags.cBullet ) + 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( static_cast<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<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( static_cast<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + nLvl ), true ); + if( m_aFlags.bAFormatByInput ) + { + SwTextFormatColl& rNxtColl = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetNextTextFormatColl(); + + JoinPrevPara(); + + DeleteLeadingTrailingBlanks( true, false ); + const SwTextFrame *const pNextFrame = GetNextNode(false); + (void)DeleteJoinCurNextPara(pNextFrame, true); + + m_aDelPam.DeleteMark(); + m_aDelPam.GetPoint()->nNode = *GetNextNode(false)->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 ); + 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 + } + + LanguageType eLang = bGetLanguage + ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true) + : LANGUAGE_SYSTEM; + + if( m_aFlags.bTransliterateRTL && eLang == LANGUAGE_HUNGARIAN && + SetRedlineText( STR_AUTOFMTREDL_TRANSLITERATE_RTL ) && + aACorrDoc.TransliterateRTLWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos))) + { + 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 ) + { + 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(), +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, m_aNdIdx.GetIndex(), + 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; + sal_uLong 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); + *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; + if (SfxItemState::SET == m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet(). + GetItemState( RES_LR_SPACE, true, + reinterpret_cast<const SfxPoolItem**>(&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 && + SfxItemState::SET == m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet(). + GetItemState( RES_LR_SPACE, false, + reinterpret_cast<const SfxPoolItem**>(&pLRSpace) ) && + ( 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; + + SET_CURR_SHELL( 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() +{ + SET_CURR_SHELL( 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..06546d81d --- /dev/null +++ b/sw/source/core/edit/edatmisc.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 <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 std::set<sal_uInt16> &attrs, SwPaM* pPaM ) +{ + SET_CURR_SHELL( 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 ); +} + +// tdf#122893 turn off ShowChanges mode to apply paragraph formatting permanently with redlining +// ie. in all directly preceding deleted paragraphs at the actual cursor positions +static void lcl_disableShowChangesIfNeeded( SwDoc *const pDoc, const SwNode& rNode, RedlineFlags &eRedlMode ) +{ + if ( IDocumentRedlineAccess::IsShowChanges(eRedlMode) && + // is there redlining at beginning of the position (possible redline block before the modified node) + pDoc->getIDocumentRedlineAccess().GetRedlinePos( rNode, RedlineType::Any ) < + pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ) + { + eRedlMode = RedlineFlags::ShowInsert | RedlineFlags::Ignore; + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode ); + } +} + +void SwEditShell::SetAttrItem( const SfxPoolItem& rHint, SetAttrMode nFlags, const bool bParagraphSetting ) +{ + SET_CURR_SHELL( this ); + StartAllAction(); + RedlineFlags eRedlMode = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode; + 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() )) + { + if (bParagraphSetting) + lcl_disableShowChangesIfNeeded( GetDoc(), (*rPaM.Start()).nNode.GetNode(), eRedlMode); + + GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, rHint, nFlags, GetLayout()); + } + } + + GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr); + } + else + { + if( !HasSelection() ) + UpdateAttr(); + + if (bParagraphSetting) + lcl_disableShowChangesIfNeeded( GetDoc(), (*pCursor->Start()).nNode.GetNode(), eRedlMode); + + GetDoc()->getIDocumentContentOperations().InsertPoolItem(*pCursor, rHint, nFlags, GetLayout()); + } + EndAllAction(); + GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode ); +} + +void SwEditShell::SetAttrSet( const SfxItemSet& rSet, SetAttrMode nFlags, SwPaM* pPaM, const bool bParagraphSetting ) +{ + SET_CURR_SHELL( this ); + SwPaM* pCursor = pPaM ? pPaM : GetCursor(); + StartAllAction(); + RedlineFlags eRedlMode = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode; + 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() )) + { + if (bParagraphSetting) + lcl_disableShowChangesIfNeeded( GetDoc(), (*rTmpCursor.Start()).nNode.GetNode(), eRedlMode); + + GetDoc()->getIDocumentContentOperations().InsertItemSet(rTmpCursor, rSet, nFlags, GetLayout()); + } + } + + GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr); + } + else + { + if( !HasSelection() ) + UpdateAttr(); + + if (bParagraphSetting) + lcl_disableShowChangesIfNeeded( GetDoc(), (*pCursor->Start()).nNode.GetNode(), eRedlMode); + + GetDoc()->getIDocumentContentOperations().InsertItemSet(*pCursor, rSet, nFlags, GetLayout()); + } + EndAllAction(); + GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode ); +} + +/* 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..20abffc64 --- /dev/null +++ b/sw/source/core/edit/edattr.cxx @@ -0,0 +1,853 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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(static_cast<sal_uInt16>(nListLevel)).GetCharFormatName(); + SwCharFormat * pCharFormat = + GetDoc()->FindCharFormatByName(aCharFormatName); + + if (pCharFormat) + rSet.Put(pCharFormat->GetAttrSet()); + } + } + + continue; + } + + sal_uLong nSttNd = rCurrentPaM.GetMark()->nNode.GetIndex(), + nEndNd = rCurrentPaM.GetPoint()->nNode.GetIndex(); + sal_Int32 nSttCnt = rCurrentPaM.GetMark()->nContent.GetIndex(); + sal_Int32 nEndCnt = rCurrentPaM.GetPoint()->nContent.GetIndex(); + + if( nSttNd > nEndNd || ( nSttNd == nEndNd && nSttCnt > nEndCnt )) + { + std::swap(nSttNd, nEndNd); + std::swap(nSttCnt, nEndCnt); + } + + if( 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( sal_uLong 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()->IsHideRedlines() + || 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 ); +} + +bool 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 + sal_uLong 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( sal_uLong n = nSttNd; n <= nEndNd; ++n ) + { + // get the node + SwNode* pNd = GetDoc()->GetNodes()[ n ]; + + if (GetLayout()->IsHideRedlines() + && 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 false; + } + } + + return true; +} + +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 + sal_uLong nSttNd = rCurrentPaM.GetMark()->nNode.GetIndex(), + nEndNd = rCurrentPaM.GetPoint()->nNode.GetIndex(); + + // reverse start and end if they aren't sorted correctly + if( nSttNd > nEndNd ) + std::swap(nSttNd, nEndNd); + + // for all the nodes in the current Point and Mark + for( sal_uLong 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 + sal_uLong 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( sal_uLong 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; + + const SvxTabStopItem& rTabItem = GetDoc()->GetDefault( RES_PARATR_TABSTOP ); + sal_uInt16 nDefDist = static_cast<sal_uInt16>(rTabItem.Count() ? rTabItem[0].GetTabPos() : 1134); + if( !nDefDist ) + return false; + + for(SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + sal_uLong nSttNd = rPaM.GetMark()->nNode.GetIndex(), + nEndNd = rPaM.GetPoint()->nNode.GetIndex(); + + if( nSttNd > nEndNd ) + std::swap(nSttNd, nEndNd); + + SwContentNode* pCNd; + for( sal_uLong n = nSttNd; bRet && n <= nEndNd; ++n ) + if( nullptr != ( pCNd = GetDoc()->GetNodes()[ n ]->GetTextNode() )) + { + pCNd = sw::GetParaPropsNode(*GetLayout(), *pCNd); + const SvxLRSpaceItem& rLS = static_cast<const SvxLRSpaceItem&>( + pCNd->GetAttr( RES_LR_SPACE )); + if( bRight ) + { + long nNext = rLS.GetTextLeft() + nDefDist; + if( bModulus ) + nNext = ( nNext / nDefDist ) * nDefDist; + SwFrame* pFrame = pCNd->getLayoutFrame( GetLayout() ); + if ( pFrame ) + { + const sal_uInt16 nFrameWidth = static_cast<sal_uInt16>( pFrame->IsVertical() ? + pFrame->getFrameArea().Height() : + pFrame->getFrameArea().Width() ); + bRet = nFrameWidth > ( nNext + MM50 ); + } + 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( static_cast<sal_uInt16>(nListLevel) ); + if( SVX_NUM_BITMAP != rNumFormat.GetNumberingType() ) + { + if ( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() ) + sExp = OUString(rNumFormat.GetBulletChar()); + 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 = pStt == rPaM.GetMark() + ? rPaM.GetPoint() + : rPaM.GetMark(); + 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 + { + sal_uLong 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..660f1f79a --- /dev/null +++ b/sw/source/core/edit/eddel.cxx @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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* 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::unique_ptr<SwPaM> pNewPam; + SwPaM * pPam = &rPam; + if (bSelectAll) + { + assert(dynamic_cast<SwShellCursor*>(&rPam)); // must be corrected pam + pNewPam.reset(new SwPaM(*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.get(); + } + // delete everything + GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam); + SaveTableBoxContent( pPam->GetPoint() ); + } + + // Selection is not needed anymore + rPam.DeleteMark(); +} + +bool SwEditShell::Delete() +{ + SET_CURR_SHELL( 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, &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* pDestShell ) +{ + if( !pDestShell ) + pDestShell = this; + + SET_CURR_SHELL( pDestShell ); + + // 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( pDestShell == this ) + { + // First cursor represents the target position!! + rPaM.DeleteMark(); + pPos = rPaM.GetPoint(); + continue; + } + else + pPos = pDestShell->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( pDestShell->GetDoc() == GetDoc() && + *rPaM.Start() <= *pTmp && *pTmp < *rPaM.End() ) + return false; + } + } + + pDestShell->StartAllAction(); + SwPosition *pPos = nullptr; + bool bRet = false; + bool bFirstMove = true; + SwNodeIndex aSttNdIdx( pDestShell->GetDoc()->GetNodes() ); + sal_Int32 nSttCntIdx = 0; + // For block selection this list is filled with the insert positions + auto pNextInsert = aInsertList.begin(); + + pDestShell->GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + for(SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + if( !pPos ) + { + if( pDestShell == this ) + { + // First cursor represents the target position!! + rPaM.DeleteMark(); + pPos = rPaM.GetPoint(); + continue; + } + else + pPos = pDestShell->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)); + pDestShell->GetDoc()->MakeUniqueNumRules(aInsertPaM); + + bRet = true; + } + + // Maybe nothing has been moved? + if( !bFirstMove ) + { + SwPaM* pCursor = pDestShell->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 + pDestShell->GetCursor()->SetMark(); + pDestShell->GetCursor()->DeleteMark(); + } +#if OSL_DEBUG_LEVEL > 0 + // check if the indices are registered in the correct nodes + { + for(SwPaM& rCmp : pDestShell->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 + pDestShell->GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + pDestShell->EndAllAction(); + + pDestShell->SaveTableBoxContent( pDestShell->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 ) +{ + SET_CURR_SHELL( 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() ) + { + SET_CURR_SHELL( 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..d0154b488 --- /dev/null +++ b/sw/source/core/edit/edfcol.cxx @@ -0,0 +1,2287 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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> + +#define WATERMARK_NAME "PowerPlusWaterMarkObject" +#define WATERMARK_AUTO_SIZE sal_uInt32(1) + +namespace +{ +static const OUString MetaFilename("tscp/bails.rdf"); +static const OUString MetaNS("urn:bails"); +static const OUString ParagraphSignatureRDFNamespace = "urn:bails:loext:paragraph:signature:"; +static const OUString ParagraphSignatureIdRDFName = "urn:bails:loext:paragraph:signature:id"; +static const OUString ParagraphSignatureDigestRDFName = ":digest"; +static const OUString ParagraphSignatureDateRDFName = ":date"; +static const OUString ParagraphSignatureUsageRDFName = ":usage"; +static const OUString ParagraphSignatureLastIdRDFName = "urn:bails:loext:paragraph:signature:lastid"; +static const OUString ParagraphClassificationNameRDFName = "urn:bails:loext:paragraph:classification:name"; +static const OUString ParagraphClassificationValueRDFName = "urn:bails:loext:paragraph:classification:value"; +static const OUString ParagraphClassificationAbbrRDFName = "urn:bails:loext:paragraph:classification:abbreviation"; +static const OUString ParagraphClassificationFieldNamesRDFName = "urn:bails:loext:paragraph:classification:fields"; +static const OUString MetadataFieldServiceName = "com.sun.star.text.textfield.MetadataField"; +static const OUString DocInfoServiceName = "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, const OUString& 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, const OUString& 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, + const OUString& 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, + const OUString& 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.isEmpty() || 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, + const OUString& 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; + + msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.ouSubject + ", " + + 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* pDoc, + 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 = pDoc->GetIDocumentUndoRedo().DoesUndo(); + pDoc->GetIDocumentUndoRedo().DoUndo(false); + comphelper::ScopeGuard const g([pDoc, isUndoEnabled]() { + pDoc->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* pDoc, + 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(pDoc, 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, + const OUString& sKey) +{ + const std::pair<OUString, OUString> rdfPair = lcl_getRDF(xModel, xField, ParagraphClassificationNameRDFName); + return rdfPair.first == ParagraphClassificationNameRDFName && (sKey.isEmpty() || rdfPair.second == sKey); +} + +uno::Reference<text::XTextField> lcl_FindParagraphClassificationField(const uno::Reference<frame::XModel>& xModel, + const uno::Reference<text::XTextContent>& xParagraph, + const OUString& sKey = OUString()) +{ + 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* pDoc, const uno::Reference<text::XTextContent>& xParagraph, const bool updateDontRemove) +{ + SwDocShell* pDocShell = pDoc->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(pDoc, xModel, xParagraph, xField, utf8Text); + } + else if (!lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).first) + { + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr); + pDoc->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoParagraphSigning>(pDoc, xField, xParagraph, false)); + lcl_RemoveParagraphMetadataField(xField); + pDoc->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::makeAny(rsKey)); + uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY); + + rxText->insertTextContent(rxParagraphCursor, xTextContent, false); +} + +static void removeAllClassificationFields(OUString const & 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 || !SfxObjectShell::Current()) + 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 = SfxObjectShell::Current()->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::makeAny(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::makeAny(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::makeAny(awt::FontWeight::BOLD)); + xFooterPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::BOLD)); + } + else + { + xHeaderPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL)); + xFooterPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL)); + } + } + break; + + default: + break; + } + } + } +} + +std::vector<svx::ClassificationResult> SwEditShell::CollectAdvancedClassification() +{ + std::vector<svx::ClassificationResult> aResult; + + SwDocShell* pDocShell = GetDoc()->GetDocShell(); + if (!pDocShell || !SfxObjectShell::Current()) + return aResult; + + const OUString sBlank; + + uno::Reference<document::XDocumentProperties> xDocumentProperties = SfxObjectShell::Current()->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::makeAny(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, 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::makeAny(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::makeAny(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::makeAny(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; + const OUString sBlank(""); + 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 aShapeServiceName = "com.sun.star.drawing.CustomShape"; + OUString sWatermark = ""; + bool bSuccess = false; + uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, 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::makeAny(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::makeAny(static_cast<sal_Int32>(nColor))); + xPropertySet->setPropertyValue(UNO_NAME_FILLSTYLE, uno::makeAny(drawing::FillStyle_SOLID)); + xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::makeAny(nTransparency)); + xPropertySet->setPropertyValue(UNO_NAME_LINESTYLE, uno::makeAny(drawing::LineStyle_NONE)); + xPropertySet->setPropertyValue(UNO_NAME_OPAQUE, uno::makeAny(false)); + xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::makeAny(false)); + xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::makeAny(false)); + xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT, uno::makeAny(nHeight)); + xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH, uno::makeAny(nWidth)); + xPropertySet->setPropertyValue(UNO_NAME_TEXT_WRAP, uno::makeAny(text::WrapTextMode_THROUGH)); + xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, uno::makeAny(text::RelOrientation::PAGE_PRINT_AREA)); + xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, uno::makeAny(text::RelOrientation::PAGE_PRINT_AREA)); + xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME, uno::makeAny(sFont)); + xPropertySet->setPropertyValue(UNO_NAME_CHAR_HEIGHT, uno::makeAny(WATERMARK_AUTO_SIZE)); + xPropertySet->setPropertyValue("Transformation", uno::makeAny(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::makeAny(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::makeAny(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::makeAny(aMatrix)); + + xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT, uno::makeAny(text::HoriOrientation::CENTER)); + xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT, uno::makeAny(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::makeAny(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::makeAny(sal_Int32(11))); + xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::makeAny(nOldValue)); + xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(bDynamicHeight)); + } +} + +SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc* pDoc, + const uno::Reference<text::XTextField>& xField, + const uno::Reference<text::XTextContent>& xParent, + const bool bRemove) + : SwUndo(SwUndoId::PARA_SIGN_ADD, pDoc), + m_pDoc(pDoc), + m_xField(xField), + m_xParent(xParent), + m_bRemove(bRemove) +{ + // Save the metadata and field content to undo/redo. + uno::Reference<frame::XModel> xModel = m_pDoc->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_pDoc->GetIDocumentUndoRedo().DoesUndo(); + m_pDoc->GetIDocumentUndoRedo().DoUndo(false); + + // Prevent validation since this will trigger a premature validation + // upon inserting, but before setting the metadata. + SwEditShell* pEditSh = m_pDoc->GetEditShell(); + const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false); + comphelper::ScopeGuard const g([&] () { + pEditSh->SetParagraphSignatureValidation(bOldValidationFlag); + m_pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled); + }); + + m_xField = lcl_InsertParagraphSignature(m_pDoc->GetDocShell()->GetBaseModel(), m_xParent, m_signature, m_usage); + lcl_DoUpdateParagraphSignatureField(m_pDoc, m_xField, m_display); +} + +void SwUndoParagraphSigning::Remove() +{ + // Disable undo to avoid introducing noise when we edit the metadata field. + const bool isUndoEnabled = m_pDoc->GetIDocumentUndoRedo().DoesUndo(); + m_pDoc->GetIDocumentUndoRedo().DoUndo(false); + + // Prevent validation since this will trigger a premature validation + // upon removing. + SwEditShell* pEditSh = m_pDoc->GetEditShell(); + const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false); + comphelper::ScopeGuard const g([&] () { + pEditSh->SetParagraphSignatureValidation(bOldValidationFlag); + m_pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled); + }); + + lcl_RemoveParagraphMetadataField(m_xField); +} + +void SwEditShell::SignParagraph() +{ + SwDocShell* pDocShell = GetDoc()->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); + + std::vector<css::beans::PropertyValue> vec = comphelper::sequenceToContainer<std::vector<css::beans::PropertyValue>>(aProperties); + auto it = std::find_if(vec.begin(), vec.end(), [](const beans::PropertyValue& rValue) + { + return rValue.Name == "Usage"; + }); + + OUString aUsage; + if (it != vec.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); + }); + + GetDoc()->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); + + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoParagraphSigning>(GetDoc(), xField, xParagraph, true)); + + GetDoc()->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 const OUString sBlank(""); + 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()) + { + // 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); + const OUString sName = (it2 != aStatements.end() ? it->first : sBlank); + const OUString sValue = (it2 != aStatements.end() ? 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(); + + RedlineFlags eRedlMode = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode; + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, pLocal->GetName()); + + GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter); + for(SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + + if ( !rPaM.HasReadonlySel( GetViewOptions()->IsFormView() ) ) + { + // tdf#105413 turn off ShowChanges mode for the next loops to apply styles permanently with redlining, + // ie. in all directly preceding deleted paragraphs at the actual cursor positions + if ( IDocumentRedlineAccess::IsShowChanges(eRedlMode) && + // is there redlining at beginning of the position (possible redline block before the modified node) + GetDoc()->getIDocumentRedlineAccess().GetRedlinePos( (*rPaM.Start()).nNode.GetNode(), RedlineType::Any ) < + GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size() ) + { + eRedlMode = RedlineFlags::ShowInsert | RedlineFlags::Ignore; + GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode ); + } + + // 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()); + } + + } + GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL, &aRewriter); + EndAllAction(); + + GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode ); +} + +SwTextFormatColl* SwEditShell::MakeTextFormatColl(const OUString& rFormatCollName, + SwTextFormatColl* pParent) +{ + SwTextFormatColl *pColl; + if ( pParent == nullptr ) + pParent = &GetTextFormatColl(0); + if ( (pColl=GetDoc()->MakeTextFormatColl(rFormatCollName, pParent)) == 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 ) + { + // 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 SfxPoolItem* pItem; + const SwNumRule* pRule = nullptr; + if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false) + || SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false) + || (SfxItemState::SET == pSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem) + && nullptr != (pRule = GetDoc()->FindNumRulePtr( + static_cast<const SwNumRuleItem*>(pItem)->GetValue())) + && pRule->IsAutoRule())) + { + SfxItemSet aSet( *pSet ); + aSet.ClearItem( RES_BREAK ); + aSet.ClearItem( RES_PAGEDESC ); + + if (pRule + || (SfxItemState::SET == pSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem) + && nullptr != (pRule = GetDoc()->FindNumRulePtr( + static_cast<const SwNumRuleItem*>(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..dd05535e4 --- /dev/null +++ b/sw/source/core/edit/edfld.cxx @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <calbck.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 static_cast<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; + + SET_CURR_SHELL( this ); + StartAllAction(); + StartUndo( SwUndoId::DELETE ); + Push(); + SwPaM* pPaM = GetCursor(); + // TODO: this is really hackish + SwFieldHint aHint(pPaM, GetLayout()); + SwIterator<SwClient,SwFieldType> aIter(*pType); + for( SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next() ) + { + pPaM->DeleteMark(); + pClient->SwClientNotifyCall( *pType, aHint ); + } + + Pop(PopMode::DeleteCurrent); + EndAllAction(); + EndUndo( SwUndoId::DELETE ); +} + +/// add a field at the cursor position +void SwEditShell::Insert2(SwField const & rField, const bool bForceExpandHints) +{ + SET_CURR_SHELL( this ); + StartAllAction(); + SwFormatField aField( rField ); + + const SetAttrMode nInsertFlags = bForceExpandHints + ? SetAttrMode::FORCEHINTEXPAND + : SetAttrMode::DEFAULT; + + for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) // for each PaM + { + const bool bSuccess(GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, aField, nInsertFlags)); + OSL_ENSURE( bSuccess, "Doc->Insert(Field) failed"); + } + + EndAllAction(); +} + +/// Are the PaMs positioned on fields? +static SwTextField* lcl_FindInputField( SwDoc* pDoc, SwField& rField ) +{ + // Search field via its address. For input fields this needs to be done in protected fields. + SwTextField* pTField = nullptr; + if (SwFieldIds::Input == rField.Which() + || (SwFieldIds::SetExp == rField.Which() + && static_cast<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<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) +{ + SET_CURR_SHELL( 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; + + if( nullptr != (pTextField = GetTextFieldAtPos( pCurStt, true )) ) + { + 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(); +} + +const SwDBData& SwEditShell::GetDBDesc() const +{ + return GetDoc()->GetDBDesc(); +} + +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) +{ + SET_CURR_SHELL( this ); + StartAllAction(); + GetDoc()->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); + if (bCloseDB) + { +#if HAVE_FEATURE_DBCONNECTIVITY + GetDoc()->GetDBManager()->CloseAll(); // close all database connections +#endif + } + EndAllAction(); +} + +SwDBManager* SwEditShell::GetDBManager() const +{ +#if HAVE_FEATURE_DBCONNECTIVITY + 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 +{ + 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..c0a8fef2a --- /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> +#include <calbck.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..de6e0a18a --- /dev/null +++ b/sw/source/core/edit/edfmt.cxx @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <docary.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; + SfxItemSet aSet( GetDoc()->GetAttrPool(), svl::Items<RES_TXTATR_CHARFMT, + RES_TXTATR_CHARFMT>{} ); + const SfxPoolItem* pItem; + if( GetCurAttr( aSet ) && SfxItemState::SET == + aSet.GetItemState( RES_TXTATR_CHARFMT, false, &pItem ) ) + pFormat = static_cast<const SwFormatCharFormat*>(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 SwModify& 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..add383a8c --- /dev/null +++ b/sw/source/core/edit/edglbldc.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 <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> +#include <osl/diagnose.h> + +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: + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( pSect) != nullptr, "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; + sal_uLong 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() ) + { + sal_uLong 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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 ]; + sal_uLong 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(); + } + 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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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( sal_uLong nPos ) +{ + eType = GLBLDOC_UNKNOWN; + PTR.pTOX = nullptr; + nDocPos = nPos; +} + +SwGlblDocContent::SwGlblDocContent( const SwTOXBaseSection* pTOX ) +{ + eType = GLBLDOC_TOXBASE; + PTR.pTOX = pTOX; + + const SwSectionNode* pSectNd = pTOX->GetFormat()->GetSectionNode(); + nDocPos = pSectNd ? pSectNd->GetIndex() : 0; +} + +SwGlblDocContent::SwGlblDocContent( const SwSection* pSect ) +{ + eType = GLBLDOC_SECTION; + PTR.pSect = pSect; + + const SwSectionNode* pSectNd = pSect->GetFormat()->GetSectionNode(); + nDocPos = pSectNd ? pSectNd->GetIndex() : 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..5c37ec22a --- /dev/null +++ b/sw/source/core/edit/edglss.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 <sal/config.h> + +#include <o3tl/safeint.hxx> +#include <osl/endian.h> +#include <tools/urlobj.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <pam.hxx> +#include <docary.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* pInsDoc ) +{ + OSL_ENSURE( pInsDoc, "no Ins.Document" ); + + SwNodes& rNds = pInsDoc->GetNodes(); + + SwNodeIndex aIdx( rNds.GetEndOfContent(), -1 ); + SwContentNode *const pContentNode = aIdx.GetNode().GetContentNode(); + SwPosition aPos( aIdx, + SwIndex(pContentNode, pContentNode ? pContentNode->Len() : 0)); + + bool bRet = false; + SET_CURR_SHELL( this ); + + pInsDoc->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 = *pInsDoc->GetTableFrameFormats(); + for( auto n = rTableFormats.size(); n; ) + if( rTableFormats[ --n ]->GetName() == rTableName ) + { + bCpyTableNm = false; + break; + } + } + bRet = pInsDoc->InsCopyOfTable( aPos, aBoxes, nullptr, bCpyTableNm ); + } + else + bRet = false; + } + else + { + bool bColSel = GetCursor_()->IsColumnSelection(); + if( bColSel && pInsDoc->IsClipBoard() ) + pInsDoc->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; + } + aPaM.Start()->nContent.Assign(nullptr, 0); + } + bRet = GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, SwCopyFlags::CheckPosInFly) + || bRet; + } + } + } + } + + pInsDoc->getIDocumentFieldsAccess().UnlockExpFields(); + if( !pInsDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() ) + pInsDoc->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..0b932fba1 --- /dev/null +++ b/sw/source/core/edit/editsh.cxx @@ -0,0 +1,1056 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <docary.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 + 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()->NotifyClients( &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()->NotifyClients( &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( const OUString &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( const OUString& 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 + sal_uLong 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; + sal_uLong 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() ) + { + 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("(").append(aCalc.GetStrResult( aCalc.VarLook( sVar )->nValue )).append(")"); + } + 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 + SET_CURR_SHELL( 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(); + } + 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 ) +{ + 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())) + { + 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() ); +} + +/// 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(); + sal_uLong nCurrNd = pSttPos->nNode.GetIndex(); + sal_uLong 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: + if( nullptr != ( pContentFrame = static_cast<SwTextNode*>(pNd)->getLayoutFrame( GetLayout() )) ) + { + // 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( static_cast<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 = 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()->IsHideRedlines()) + { + if (auto const*const pMerged = pFrame->GetMergedPara()) + { + aStart = *pMerged->pLastNode; + } + } + } + } + return nRet; +} + +long SwEditShell::CompareDoc( const SwDoc& rDoc ) +{ + StartAllAction(); + long nRet = GetDoc()->CompareDoc( rDoc ); + EndAllAction(); + return nRet; +} + +long SwEditShell::MergeDoc( const SwDoc& rDoc ) +{ + StartAllAction(); + long nRet = GetDoc()->MergeDoc( rDoc ); + EndAllAction(); + return nRet; +} + +const SwFootnoteInfo& SwEditShell::GetFootnoteInfo() const +{ + return GetDoc()->GetFootnoteInfo(); +} + +void SwEditShell::SetFootnoteInfo(const SwFootnoteInfo& rInfo) +{ + StartAllAction(); + SET_CURR_SHELL( this ); + GetDoc()->SetFootnoteInfo(rInfo); + CallChgLnk(); + EndAllAction(); +} + +const SwEndNoteInfo& SwEditShell::GetEndNoteInfo() const +{ + return GetDoc()->GetEndNoteInfo(); +} + +void SwEditShell::SetEndNoteInfo(const SwEndNoteInfo& rInfo) +{ + StartAllAction(); + SET_CURR_SHELL( this ); + GetDoc()->SetEndNoteInfo(rInfo); + EndAllAction(); +} + +const SwLineNumberInfo& SwEditShell::GetLineNumberInfo() const +{ + return GetDoc()->GetLineNumberInfo(); +} + +void SwEditShell::SetLineNumberInfo(const SwLineNumberInfo& rInfo) +{ + StartAllAction(); + SET_CURR_SHELL( 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; + SET_CURR_SHELL( 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 ) +{ + const SwPosition& rPos = *GetCursor()->GetPoint(); + SwExtTextInput* pInput = GetDoc()->GetExtTextInput( rPos.nNode.GetNode() ); + if( !pInput ) + return; + + StartAllAction(); + SET_CURR_SHELL( 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( 0 > nDiff ) + Left( -nDiff, CRSR_SKIP_CHARS ); + else if( 0 < nDiff ) + Right( nDiff, CRSR_SKIP_CHARS ); + + 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(); + SET_CURR_SHELL( 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..15e8532f0 --- /dev/null +++ b/sw/source/core/edit/edlingu.cxx @@ -0,0 +1,1712 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/frame/XModel.hpp> +#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 <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> + +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 *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 nCursorCnt; +public: + SwLinguIter(); + + SwEditShell *GetSh() { return 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 nCursorCnt; } + + // 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 > xSpeller; + svx::SpellPortions aLastPortions; + + SpellContentPositions aLastPositions; + bool bBackToStartOfSentence; + + void CreatePortion(uno::Reference< XSpellAlternatives > const & xAlt, + linguistic2::ProofreadingResult* pGrammarResult, + bool bIsField, bool bIsHidden); + + void AddPortion(uno::Reference< XSpellAlternatives > const & xAlt, + linguistic2::ProofreadingResult* pGrammarResult, + const SpellContentPositions& rDeletedRedlines); +public: + SwSpellIter() : + 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 aLastPortions;} + const SpellContentPositions& GetLastPositions() const {return aLastPositions;} +}; + +/// used for text conversion +class SwConvIter : public SwLinguIter +{ + SwConversionArgs &rArgs; +public: + explicit SwConvIter(SwConversionArgs &rConvArgs) + : 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 bOldIdle; + static void DelSoftHyph( SwPaM &rPam ); + +public: + SwHyphIter() : m_pLastNode(nullptr), m_pLastFrame(nullptr), 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() + : pSh(nullptr) + , nCursorCnt(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( pSh ) + return; + + bool bSetCurr; + + pSh = pShell; + + SET_CURR_SHELL( pSh ); + + OSL_ENSURE(!m_pEnd, "SwLinguIter::Start_ without End?"); + + SwPaM *pCursor = pSh->GetCursor(); + + if( pShell->HasSelection() || pCursor != pCursor->GetNext() ) + { + bSetCurr = nullptr != GetCurr(); + nCursorCnt = pSh->GetCursorCnt(); + if( pSh->IsTableMode() ) + pSh->TableCursorToCursor(); + + pSh->Push(); + sal_uInt16 n; + for( n = 0; n < nCursorCnt; ++n ) + { + pSh->Push(); + pSh->DestroyCursor(); + } + pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + bSetCurr = false; + nCursorCnt = 1; + pSh->Push(); + pSh->SetLinguRange( eStart, eEnd ); + } + + pCursor = 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( !pSh ) + return; + + OSL_ENSURE(m_pEnd, "SwLinguIter::End_ without end?"); + if(bRestoreSelection) + { + while( nCursorCnt-- ) + pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + + pSh->KillPams(); + pSh->ClearMark(); + } + m_pStart.reset(); + m_pEnd.reset(); + m_pCurr.reset(); + m_pCurrX.reset(); + + pSh = nullptr; +} + +void SwSpellIter::Start( SwEditShell *pShell, SwDocPositions eStart, + SwDocPositions eEnd ) +{ + if( GetSh() ) + return; + + xSpeller = ::GetSpellChecker(); + if ( xSpeller.is() ) + Start_( pShell, eStart, eEnd ); + aLastPortions.clear(); + 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(), + 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( makeAny( 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(), &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 makeAny( 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) + bOldIdle = pShell->GetViewOptions()->IsIdle(); + pShell->GetViewOptions()->SetIdle( false ); + Start_( pShell, eStart, eEnd ); +} + +// restore selections +void SwHyphIter::End() +{ + if( !GetSh() ) + return; + GetSh()->GetViewOptions()->SetIdle( 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 = GetSwCursor(); + + 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 + 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 "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)); + OUString aWord = aText.replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "") + .replaceAll(OUStringChar(CH_TXTATR_INWORD), ""); + + 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(1); + PropertyValue &rVal = aPropVals.getArray()[0]; + rVal.Name = UPN_MAX_NUMBER_OF_SUGGESTIONS; + rVal.Value <<= sal_Int16(7); + + xSpellAlt = xSpell->spell( aWord, static_cast<sal_uInt16>(eActLang), aPropVals ); + } + } + + if ( xSpellAlt.is() ) // error found? + { + HandleCorrectionError( aText, 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(rResult.aErrors.begin(), rResult.aErrors.end(), + [rErrorPosInText, nLen](const linguistic2::SingleProofreadingError &rError) { + return rError.nErrorStart <= rErrorPosInText + && rErrorPosInText + nLen <= rError.nErrorStart + rError.nErrorLength; }); + if (pError != rResult.aErrors.end()) + { + rSuggestions = pError->aSuggestions; + rErrorIndexInResult = static_cast<sal_Int32>(std::distance(rResult.aErrors.begin(), pError)); + } + } + + if (rResult.aErrors.hasElements()) // error found? + { + HandleCorrectionError( aText, 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; + aLastPortions.clear(); + 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( bBackToStartOfSentence ) + { + pMySh->GoStartSentence(); + bBackToStartOfSentence = false; + } + uno::Any aSpellRet = + pMySh->GetDoc()->Spell(*pCursor, + 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, + 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 = 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() +{ + 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, + 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(pGrammarResult->aProperties.begin(), pGrammarResult->aProperties.end(), + [](const beans::PropertyValue& rProperty) { return rProperty.Name == "DialogTitle"; }); + if (pProperty != pGrammarResult->aProperties.end()) + 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(); + aLastPortions.push_back(aPortion); + aLastPositions.push_back(aPosition); + +} + +void SwSpellIter::AddPortion(uno::Reference< XSpellAlternatives > const & xAlt, + linguistic2::ProofreadingResult* pGrammarResult, + const SpellContentPositions& rDeletedRedlines) +{ + SwEditShell *pMySh = GetSh(); + OUString sText; + pMySh->GetSelectedText( sText ); + if(!sText.isEmpty()) + { + 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..0b96be07b --- /dev/null +++ b/sw/source/core/edit/ednumber.cxx @@ -0,0 +1,912 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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 ); + { + sal_uInt32 nStt = aPam.GetPoint()->nNode.GetIndex(), + nEnd = aPam.GetMark()->nNode.GetIndex(); + if( nStt > nEnd ) + { + sal_uInt32 nTmp = nStt; nStt = nEnd; nEnd = nTmp; + } + for (sal_uInt32 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 ); + { + sal_uInt32 nStt = aPam.GetPoint()->nNode.GetIndex(), + nEnd = aPam.GetMark()->nNode.GetIndex(); + if( nStt > nEnd ) + { + sal_uInt32 nTmp = nStt; nStt = nEnd; nEnd = nTmp; + } + for (sal_uInt32 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( long 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 + long 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 + { + sal_uLong 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() ) + { + sal_uLong 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 = 1; + else + nOffset = nIdx - nStt; + } + else + nOffset = 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(); + SwNodePtr pNd = const_cast<SwNodePtr>(&rNd); + bool bFirst = true; + SwOutlineNodes::size_type nPos; + int nLvl(0); + if( !rOutlNd.Seek_Entry( pNd, &nPos ) && nPos ) + --nPos; + + for( ; nPos < rOutlNd.size(); ++nPos ) + { + SwNodePtr 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::START, 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::END, 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 static_cast<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..f65f6319c --- /dev/null +++ b/sw/source/core/edit/edredln.cxx @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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() ) + { + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( this ); + StartAllAction(); + bool bRet = GetDoc()->getIDocumentRedlineAccess().RejectRedline( nPos, true ); + if( !nPos && !::IsExtraData( GetDoc() ) ) + lcl_InvalidateAll( this ); + EndAllAction(); + return bRet; +} + +bool SwEditShell::AcceptRedlinesInSelection() +{ + SET_CURR_SHELL( this ); + StartAllAction(); + bool bRet = GetDoc()->getIDocumentRedlineAccess().AcceptRedline( *GetCursor(), true ); + EndAllAction(); + return bRet; +} + +bool SwEditShell::RejectRedlinesInSelection() +{ + SET_CURR_SHELL( this ); + StartAllAction(); + bool 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()) ) + { + SET_CURR_SHELL( 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..ba765eee4 --- /dev/null +++ b/sw/source/core/edit/edsect.cxx @@ -0,0 +1,421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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, + // pInntermostNode contain 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 ) + { + StartAllAction(); + + // adjust insert position to insert before start nodes and after end + // nodes + SwNodeIndex aInsertIndex( *pInsertNode, + 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..93036f735 --- /dev/null +++ b/sw/source/core/edit/edtab.cxx @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 ); + bool bRet = false; + SwPaM* pCursor = GetCursor(); + const SwTableNode* pTableNd = + GetDoc()->IsIdxInTable( pCursor->GetPoint()->nNode ); + if( IsTableMode() ) + { + ClearMark(); + pCursor = GetCursor(); + } + else if( !pTableNd || pCursor->GetNext() != pCursor ) + return bRet; + + // 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(); + 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 + sal_uLong nStt = rPaM.GetMark()->nNode.GetIndex(), + nEnd = rPaM.GetPoint()->nNode.GetIndex(); + if( nStt > nEnd ) { sal_uLong n = nStt; nStt = nEnd; nEnd = n; } + + 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 ) +{ + SET_CURR_SHELL( 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; + const SfxPoolItem* pItem; + if( SfxItemState::SET == pBox->GetFrameFormat()->GetAttrSet().GetItemState( + RES_BOXATR_FORMAT, true, &pItem )) + { + nFormat = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue(); + return GetDoc()->GetNumberFormatter()->IsTextFormat( nFormat ); + } + + sal_uLong nNd = pBox->IsValidNumTextNd(); + if( ULONG_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(); + } + + sal_uLong nNd; + if( pBox && ULONG_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..e652551d1 --- /dev/null +++ b/sw/source/core/edit/edtox.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 <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 <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> + +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 ) +{ + SET_CURR_SHELL( 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) +{ + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) != nullptr, "no TOXBaseSection!" ); + const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase); + return rTOXSect.IsProtect(); +} + +void SwEditShell::SetTOXBaseReadonly(const SwTOXBase& rTOXBase, bool bReadonly) +{ + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) != nullptr, "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 ) +{ + SET_CURR_SHELL( 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()) + { + SwDoc* pMyDoc = GetDoc(); + SwDocShell* pDocSh = pMyDoc->GetDocShell(); + + bool bInIndex = &rTOX == GetCurTOX(); + SET_CURR_SHELL( 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 + CalcLayout(); + + // 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 ) + { + OSL_ENSURE( dynamic_cast<const SwTOXBaseSection*>( pSect) != nullptr, "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; + SwTOXMark::InsertTOXMarks( aMarks, *pTOXType ); + 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(); + rtl_TextEncoding 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, + '\\' ); + + while (rStrm.good()) + { + OString aRdLine; + 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..6a093a342 --- /dev/null +++ b/sw/source/core/edit/edundo.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/frame/XModel.hpp> + +#include <editsh.hxx> +#include <fesh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <pam.hxx> +#include <UndoCore.hxx> +#include <swundo.hxx> +#include <flyfrm.hxx> +#include <frmfmt.hxx> +#include <docsh.hxx> +#include <pagefrm.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(); + 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) +{ + SET_CURR_SHELL( 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().Undo() + || 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) +{ + SET_CURR_SHELL( 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) +{ + SET_CURR_SHELL( 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" ); + + if( dynamic_cast<const SwFEShell*>( pShell) != nullptr ) + { + SwFEShell* pFEShell = static_cast<SwFEShell*>( pShell ); + 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..b17b557df --- /dev/null +++ b/sw/source/core/edit/edws.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 <editsh.hxx> + +#include <officecfg/Office/Common.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/window.hxx> + +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentState.hxx> +#include <pam.hxx> +#include <docary.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( dynamic_cast<const SwEditShell *>(&rCurrentShell) != nullptr ) + static_cast<SwEditShell*>(&rCurrentShell)->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 ( 1 != (pNd->GetIndex() - pNd->StartOfSectionIndex()) ) + return true; + + pNd = &rNds.GetEndOfAutotext(); + return 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 ) +{ + SET_CURR_SHELL( 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())); + 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) +{ + SET_CURR_SHELL( 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() +{ + SET_CURR_SHELL(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..48a58c07b --- /dev/null +++ b/sw/source/core/fields/authfld.cxx @@ -0,0 +1,668 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/any.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 <calbck.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); + OSL_FAIL("Field unknown" ); +} + +SwAuthEntry* SwAuthorityFieldType::AddField(const OUString& 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), + rFieldContents.getToken( 0, TOX_STYLE_DELIMITER, nIdx )); + + for (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( + const OUString& 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 array position +sal_uInt16 SwAuthorityFieldType::AppendField( const SwAuthEntry& rInsert ) +{ + for( SwAuthDataArr::size_type nRet = 0; nRet < m_DataArr.size(); ++nRet ) + { + if( *m_DataArr[ nRet ] == rInsert ) + return nRet; + } + + //if it is a new Entry - insert + m_DataArr.push_back(new SwAuthEntry(rInsert)); + return m_DataArr.size()-1; +} + +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()); + SwTOXInternational aIntl(m_eLanguage, SwTOIOptions::NONE, m_sSortAlgorithm); + // 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 = [&aIntl, pTextNode, pFormatField] + (std::vector<std::unique_ptr<SwTOXSortTabBase>> & rSortArr) + { + std::unique_ptr<SwTOXAuthority> pNew( + new SwTOXAuthority(*pTextNode, *pFormatField, aIntl)); + + 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + //re-generate positions of the fields + DelSequenceArray(); + NotifyClients( pOld, pNew ); +} + +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, + const OUString& 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 (sIdentifier.trim().startsWith("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); +} + +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" +}; + +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(const OUString& rFieldName) +{ + for(sal_Int32 i = 0; i < AUTH_FIELD_END; ++i) + if(rFieldName.equalsAscii(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; + comphelper::string::padToLength(sBuf, AUTH_FIELD_ISBN, 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(); + 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..db2375997 --- /dev/null +++ b/sw/source/core/fields/cellfml.cxx @@ -0,0 +1,1218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <docary.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/safeint.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(); + + const SfxPoolItem* pItem; + if( SfxItemState::SET == GetFrameFormat()->GetItemState( + RES_BOXATR_FORMULA, false, &pItem ) ) + { + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + if( !static_cast<const SwTableBoxFormula*>(pItem)->IsValid() ) + { + // calculate + const SwTable* pTmp = rCalcPara.m_pTable; + rCalcPara.m_pTable = &pBox->GetSttNd()->FindTableNode()->GetTable(); + const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem))->Calc( rCalcPara, nRet ); + + if( !rCalcPara.IsStackOverflow() ) + { + SwFrameFormat* pFormat = pBox->ClaimFrameFormat(); + SfxItemSet aTmp( pDoc->GetAttrPool(), + svl::Items<RES_BOXATR_BEGIN,RES_BOXATR_END-1>{} ); + 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( SfxItemState::SET == pBox->GetFrameFormat()->GetItemState( + RES_BOXATR_VALUE, false, &pItem ) ) + { + rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status + nRet = static_cast<const SwTableBoxValue*>(pItem)->GetValue(); + break; + } + + SwTextNode* pTextNd = pDoc->GetNodes()[ m_pStartNode->GetIndex() + 1 ]->GetTextNode(); + if( !pTextNd ) + break; + + sal_Int32 nSttPos = 0; + OUString sText = pTextNd->GetText(); + 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; + } + // ?? 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 ); + + 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 ) + { + if( bDelim ) + rNewStr.append(cListDelim); + bDelim = true; + rNewStr.append(pCalcPara->m_rCalc.GetStrResult( + pTableBox->GetValue( *pCalcPara ) )); + } + } + 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("("); + rNewStr.append(pCalcPara->m_rCalc.GetStrResult( + pSttBox->GetValue( *pCalcPara ) )); + 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(OUString::number(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(OUString::number(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))) + .append(":"); + rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 ); + } + + pBox = rTable.GetTableBox( rFirstBox ); + rNewStr.append(OUString::number(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(std::u16string_view(m_sFormula).substr(nFormula)); + break; + } + + // write beginning + aStr.append(std::u16string_view(m_sFormula).substr(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, const OUString& 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 == pFormat->GetName().getToken(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 = rStr.copy( 0, nPos ).toInt32(); + 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() ) + { + long nBox = SwTable::GetBoxNum( sTmp, true ); + nBox -= SwTable::GetBoxNum( sRefBoxNm, true ); + 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 ) + { + 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 + { + 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()).append("."); // 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).append("."); // keep table name + else + rTableUpd.m_bModified = true; + } + else + rNewStr.append(sTableNm).append("."); // 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()).append("."); + } + else if( !sTableNm.isEmpty() ) + rNewStr.append(sTableNm).append("."); + } + else if( bInNewTable ) + { + rTableUpd.m_bModified = true; + rNewStr.append(*rTableUpd.m_aData.pNewTableNm).append("."); + } + else if( !sTableNm.isEmpty() ) + rNewStr.append(sTableNm).append("."); + } + + if( pLastBox ) + rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pEndBox))).append(":"); + + rNewStr.append(OUString::number(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..9473b086d --- /dev/null +++ b/sw/source/core/fields/chpfld.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 <com/sun/star/text/ChapterFormat.hpp> +#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(const OUString& sIn) +{ + OUStringBuffer aBuf(sIn.replace('\n', ' ')); + 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.sTitle; + case CF_NUM_NOPREPST_TITLE: + return rState.sNumber + 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* pDoc = const_cast<SwDoc*>(pContentNode->GetDoc()); + + const SwTextNode* pTextNode = dynamic_cast<const SwTextNode*>(pContentNode); + if (!pTextNode || !rFrame.IsInDocBody()) + { + SwPosition aDummyPos( pDoc->GetNodes().GetEndOfContent() ); + pTextNode = GetBodyTextNode( *pDoc, 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.sTitle.clear(); + rState.sPost.clear(); + rState.sPre.clear(); + + SwDoc* pDoc = const_cast<SwDoc*>(rTextNd.GetDoc()); + const SwTextNode *pTextNd = rTextNd.FindOutlineNodeOfLevel(rState.nLevel, pLayout); + if( pTextNd ) + { + 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 != pDoc->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(); + } + } + 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..abc9fbd37 --- /dev/null +++ b/sw/source/core/fields/dbfld.cxx @@ -0,0 +1,870 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/zforlist.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 <ndtxt.hxx> +#include <dbfld.hxx> +#include <dbmgr.hxx> +#include <unofldmid.h> +#include <calbck.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) + { + 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) + + rDBName.getToken(1, DB_DELIM) + + OUStringChar(DB_DELIM) + + rDBName.getToken(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(SwDoc const * pDoc) +{ + SwDBManager* pMgr = pDoc->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(SwDoc const * pDoc) +{ + SwDBManager* pMgr = pDoc->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(static_cast<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(SwDoc const * pDoc) +{ + SwDBManager* pMgr = pDoc->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..f03bf0257 --- /dev/null +++ b/sw/source/core/fields/ddefld.cxx @@ -0,0 +1,377 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swbaslnk.hxx> +#include <unofldmid.h> +#include <hints.hxx> +#include <calbck.hxx> + +using namespace ::com::sun::star; + +#define DDE_TXT_ENCODING osl_getThreadTextEncoding() + +namespace { + +class SwIntrnlRefLink : public SwBaseLink +{ + SwDDEFieldType& rFieldType; +public: + SwIntrnlRefLink( SwDDEFieldType& rType, SfxLinkUpdateMode nUpdateType ) + : SwBaseLink( nUpdateType, SotClipboardFormatId::STRING ), + 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( sal_uLong nSttNd, sal_uLong nEndNd ) const override; +}; + +} + +::sfx2::SvBaseLink::UpdateResult SwIntrnlRefLink::DataChanged( const OUString& rMimeType, + const uno::Any & rValue ) +{ + switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) + { + case SotClipboardFormatId::STRING: + if( !IsNoDataFlag() ) + { + uno::Sequence< sal_Int8 > aSeq; + rValue >>= aSeq; + OUString sStr( reinterpret_cast<char const *>(aSeq.getConstArray()), aSeq.getLength(), DDE_TXT_ENCODING ); + + // 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 ); + + rFieldType.SetExpansion( sStr ); + // set Expansion first! (otherwise this flag will be deleted) + rFieldType.SetCRLFDelFlag( bDel ); + } + break; + + // other formats + default: + return SUCCESS; + } + + OSL_ENSURE( rFieldType.GetDoc(), "no pDoc" ); + + // no dependencies left? + if( rFieldType.HasWriterListeners() && !rFieldType.IsModifyLocked() && !ChkNoDataFlag() ) + { + SwViewShell* pSh = rFieldType.GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + SwEditShell* pESh = rFieldType.GetDoc()->GetEditShell(); + + // Search for fields. If no valid found, disconnect. + SwMsgPoolItem aUpdateDDE( RES_UPDATEDDETBL ); + rFieldType.LockModify(); + + std::vector<SwFormatField*> vFields; + rFieldType.GatherFields(vFields, false); + if(vFields.size()) + { + if(pESh) + pESh->StartAllAction(); + else if(pSh) + pSh->StartAction(); + } + + for(auto pFormatField: vFields) + { + // a DDE table or a DDE field attribute in the text + if(pFormatField->GetTextField()) + pFormatField->UpdateTextNode( nullptr, &aUpdateDDE ); + } + + rFieldType.UnlockModify(); + + if(vFields.size()) + { + if(pESh) + pESh->EndAllAction(); + else if(pSh) + pSh->EndAction(); + + if(pSh) + pSh->GetDoc()->getIDocumentState().SetModified(); + } + } + + return SUCCESS; +} + +void SwIntrnlRefLink::Closed() +{ + if( rFieldType.GetDoc() && !rFieldType.GetDoc()->IsInDtor() ) + { + // advise goes, convert all fields into text? + SwViewShell* pSh = rFieldType.GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + SwEditShell* pESh = rFieldType.GetDoc()->GetEditShell(); + if( pESh ) + { + pESh->StartAllAction(); + pESh->FieldToText( &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; + rFieldType.CallSwClientNotify(sw::LinkAnchorSearchHint(rFieldType.GetDoc()->GetNodes(), pNd)); + return pNd; +} + +bool SwIntrnlRefLink::IsInRange( sal_uLong nSttNd, sal_uLong nEndNd ) const +{ + bool bInRange = false; + rFieldType.CallSwClientNotify(sw::InRangeSearchHint( + nSttNd, nEndNd, bInRange)); + return bInRange; +} + +SwDDEFieldType::SwDDEFieldType(const OUString& rName, + const OUString& rCmd, SfxLinkUpdateMode nUpdateType ) + : SwFieldType( SwFieldIds::Dde ), + aName( rName ), pDoc( nullptr ), nRefCnt( 0 ) +{ + bCRLFFlag = bDeleted = false; + refLink = new SwIntrnlRefLink( *this, nUpdateType ); + SetCmd( rCmd ); +} + +SwDDEFieldType::~SwDDEFieldType() +{ + if( pDoc && !pDoc->IsInDtor() ) + pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( refLink.get() ); + refLink->Disconnect(); +} + +std::unique_ptr<SwFieldType> SwDDEFieldType::Copy() const +{ + std::unique_ptr<SwDDEFieldType> pType(new SwDDEFieldType( aName, GetCmd(), GetType() )); + pType->aExpansion = aExpansion; + pType->bCRLFFlag = bCRLFFlag; + pType->bDeleted = bDeleted; + pType->SetDoc( pDoc ); + return pType; +} + +OUString SwDDEFieldType::GetName() const +{ + return aName; +} + +void SwDDEFieldType::SetCmd( const OUString& _aStr ) +{ + OUString aStr = _aStr; + sal_Int32 nIndex = 0; + do + { + aStr = aStr.replaceFirst(" ", " ", &nIndex); + } while (nIndex>=0); + refLink->SetLinkSourceName( aStr ); +} + +OUString const & SwDDEFieldType::GetCmd() const +{ + return refLink->GetLinkSourceName(); +} + +void SwDDEFieldType::SetDoc( SwDoc* pNewDoc ) +{ + if( pNewDoc == pDoc ) + return; + + if( pDoc && refLink.is() ) + { + OSL_ENSURE( !nRefCnt, "How do we get the references?" ); + pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( refLink.get() ); + } + + pDoc = pNewDoc; + if( pDoc && nRefCnt ) + { + refLink->SetVisible( pDoc->getIDocumentLinksAdministration().IsVisibleLinks() ); + pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( refLink.get() ); + } +} + +void SwDDEFieldType::RefCntChgd() +{ + if( nRefCnt ) + { + refLink->SetVisible( pDoc->getIDocumentLinksAdministration().IsVisibleLinks() ); + pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( refLink.get() ); + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + refLink->Update(); + } + else + { + Disconnect(); + pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( 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 <<= 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 >>= aExpansion; + break; + default: + assert(false); + } + if( nPart>=0 ) + { + 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() ); + } +} + +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..5e7f3f7e5 --- /dev/null +++ b/sw/source/core/fields/ddetbl.cxx @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <ddefld.hxx> +#include <ndindex.hxx> +#include <fldupde.hxx> +#include <swtblfmt.hxx> +#include <fieldhint.hxx> +#include <osl/diagnose.h> + +/// 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( pNew && RES_UPDATEDDETBL == pNew->Which() ) + ChangeContent(); + else + SwTable::Modify( pOld, pNew ); +} + +void SwDDETable::SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) +{ + SwClient::SwClientNotify(rModify, rHint); + if(dynamic_cast<const SwFieldHint*>(&rHint)) + // 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 (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, 0 ); +} + +SwDDEFieldType* SwDDETable::GetDDEFieldType() +{ + return m_pDDEType; +} + +bool 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 false; + OSL_ENSURE( !GetTabSortBoxes().empty(), "Table without content?" ); + SwNode* pNd = const_cast<SwNode*>(static_cast<SwNode const *>(GetTabSortBoxes()[0]->GetSttNd())); + if( !pNd->GetNodes().IsDocNodes() ) + return false; + + 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 + + return true; +} + +/* 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..92afe5024 --- /dev/null +++ b/sw/source/core/fields/docufld.cxx @@ -0,0 +1,2646 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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/urihelper.hxx> +#include <unotools/useroptions.hxx> +#include <unotools/syslocale.hxx> +#include <svl/zforlist.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) + { + // 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; + + 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; + + default: + assert(false); + } + return true; +} + +SwFileNameFieldType::SwFileNameFieldType(SwDoc *pDocument) + : SwFieldType( SwFieldIds::Filename ) +{ + m_pDoc = pDocument; +} + +OUString SwFileNameFieldType::Expand(sal_uLong nFormat) const +{ + OUString aRet; + const SwDocShell* pDShell = m_pDoc->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_pDoc); +} + +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 *pDocument) + : SwFieldType( SwFieldIds::TemplateName ) +{ + m_pDoc = pDocument; +} + +OUString SwTemplNameFieldType::Expand(sal_uLong nFormat) const +{ + OSL_ENSURE( nFormat < FF_END, "Expand: no valid Format!" ); + + OUString aRet; + SwDocShell *pDocShell(m_pDoc->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_pDoc); +} + +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* pDocument) + : SwFieldType( SwFieldIds::DocStat ), m_nNumberingType( SVX_NUM_ARABIC ) +{ + m_pDoc = pDocument; +} + +OUString SwDocStatFieldType::Expand(sal_uInt16 nSubType, SvxNumType nFormat) const +{ + sal_uInt32 nVal = 0; + const SwDocStat& rDStat = m_pDoc->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_pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) + const_cast<SwDocStat &>(rDStat).nPage = m_pDoc->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_pDoc); +} + +/** + * @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 ) +{ + long nDate = Date::DateToDays( rDate.Day, rDate.Month, rDate.Year ); + 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* pDoc) +{ + OSL_ENSURE(pDoc, "got no document"); + + if( SwFieldTypesEnum::ConditionalText == m_nSubType ) + { +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) pDoc; +#else + SwDBManager* pMgr = pDoc->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 + if( pMgr) + { + sal_Int32 nIdx{ 0 }; + OUString sDBName( GetDBName( sTmpName, pDoc )); + 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(const OUString& rName, SwDoc *pDoc) +{ + sal_Int32 nPos = rName.indexOf(DB_DELIM); + if( nPos>=0 ) + { + nPos = rName.indexOf(DB_DELIM, nPos + 1); + + if( nPos>=0 ) + return rName.copy(0, nPos); + } + + SwDBData aData = pDoc->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 *pDoc) + : SwFieldType( SwFieldIds::Postit ) + , mpDoc(pDoc) +{} + +std::unique_ptr<SwFieldType> SwPostItFieldType::Copy() const +{ + return std::make_unique<SwPostItFieldType>(mpDoc); +} + +// PostIt field + +sal_uInt32 SwPostItField::m_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 ? m_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( std::make_unique<OutlinerParaObject>(*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::unique_ptr<OutlinerParaObject> pText ) +{ + mpText = std::move(pText); +} + +sal_Int32 SwPostItField::GetNumberOfParagraphs() const +{ + return mpText ? mpText->Count() : 1; +} + +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* pDoc = pGetType->GetDoc(); + auto pObj = std::make_unique<SwTextAPIEditSource>( pDoc ); + 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.get() ); + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwPostItField")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + + SwField::dumpAsXml(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("mpText")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", mpText.get()); + if (mpText) + mpText->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + 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::Modify( const SfxPoolItem*, const SfxPoolItem * ) +{ +} + +// 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* pDc ) + : SwFieldType( SwFieldIds::RefPageGet ), m_pDoc( pDc ), m_nNumberingType( SVX_NUM_ARABIC ) +{ +} + +std::unique_ptr<SwFieldType> SwRefPageGetFieldType::Copy() const +{ + std::unique_ptr<SwRefPageGetFieldType> pNew(new SwRefPageGetFieldType( m_pDoc )); + pNew->m_nNumberingType = m_nNumberingType; + return pNew; +} + +void SwRefPageGetFieldType::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + 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( !pNew && !pOld && HasWriterListeners() ) + { + SwRootFrame const* pLayout(nullptr); + SwRootFrame const* pLayoutRLHidden(nullptr); + for (SwRootFrame const*const pLay : m_pDoc->GetAllLayouts()) + { + if (pLay->IsHideRedlines()) + { + pLayoutRLHidden = pLay; + } + else + { + pLayout = pLay; + } + } + ModifyImpl(pLayout); + if (pLayoutRLHidden) + { + ModifyImpl(pLayoutRLHidden); + } + } + + // forward to text fields, they "expand" the text + NotifyClients( pOld, pNew ); +} + +bool SwRefPageGetFieldType::MakeSetList(SetGetExpFields& rTmpLst, + SwRootFrame const*const pLayout) +{ + IDocumentRedlineAccess const& rIDRA(m_pDoc->getIDocumentRedlineAccess()); + std::vector<SwFormatField*> vFields; + 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_pDoc->GetNodes().GetEndOfPostIts() ); + bool const bResult = GetBodyTextNode( *m_pDoc, 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_pDoc->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()).ModifyNotification( 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* pDoc = pGetType->GetDoc(); + if( pField->GetTextNode().StartOfSectionIndex() > + pDoc->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( pDoc->GetNodes() ) ); + SwTextNode* pTextNode = const_cast<SwTextNode*>(GetBodyTextNode(*pDoc, 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 ) + { + // 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* pD ) + : SwFieldType( SwFieldIds::JumpEdit ), m_pDoc( pD ), m_aDep( *this ) +{ +} + +std::unique_ptr<SwFieldType> SwJumpEditFieldType::Copy() const +{ + return std::make_unique<SwJumpEditFieldType>( m_pDoc ); +} + +SwCharFormat* SwJumpEditFieldType::GetCharFormat() +{ + SwCharFormat* pFormat = m_pDoc->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..2872101dc --- /dev/null +++ b/sw/source/core/fields/expfld.cxx @@ -0,0 +1,1432 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> +#include <calbck.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, OUString(DB_DELIM)); + + if (bWithCommandType) + { + nIndex = sRes.lastIndexOf('.', nIndex); + if (nIndex<0) + { + return sRes; + } + sRes = sRes.replaceAt(nIndex, 1, OUString(DB_DELIM)); + } + + nIndex = sRes.indexOf('.'); + if (nIndex>=0) + { + sRes = sRes.replaceAt(nIndex, 1, OUString(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::Modify( const SfxPoolItem*, const SfxPoolItem* pNew ) +{ + if( pNew && RES_DOCPOS_UPDATE == pNew->Which() ) + NotifyClients( nullptr, pNew ); + // do not expand anything else +} + +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); + SetGetExpField aEndField( aPos.nNode, &rField, &aPos.nContent ); + 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 = static_cast<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( static_cast<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::Modify( const SfxPoolItem*, const SfxPoolItem* ) +{ + // 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 ) + { + SwNumRule * pRule = pTextNd->GetNumRule(); + + if (pRule) + { + // --> 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(static_cast<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()) + { + 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 sal_uInt16 nIds[] = + { + RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_CHRATR_FONT, RES_CHRATR_FONT, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONT, + 0, 0 + }; + SwAttrSet aSet(rDoc.GetAttrPool(), nIds); + rTextNode.GetParaAttr(aSet, nRet, nRet+1); + + if( RTL_TEXTENCODING_SYMBOL != static_cast<const SvxFontItem&>(aSet.Get( + GetWhichOfScript( RES_CHRATR_FONT, nSrcpt )) ).GetCharSet() ) + { + LanguageType eLang = static_cast<const SvxLanguageItem&>(aSet.Get( + GetWhichOfScript( RES_CHRATR_LANGUAGE, nSrcpt )) ).GetLanguage(); + LanguageTag aLanguageTag( eLang); + CharClass aCC( aLanguageTag); + 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(static_cast<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->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; + 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; + 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..382880ecd --- /dev/null +++ b/sw/source/core/fields/fldbas.cxx @@ -0,0 +1,824 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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> + +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 + }; + +} + +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 ) + : SwModify() + , 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::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + std::vector<SwFormatField*> vFields; + GatherFields(vFields); + if(!vFields.size()) + return; + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldType")); + for(const auto pFormatField: vFields) + pFormatField->dumpAsXml(pWriter); + 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() +{ + bool bHasHiddenInformationNotes = false; + CallSwClientNotify(sw::HasHiddenInformationNotesHint(bHasHiddenInformationNotes)); + return bHasHiddenInformationNotes; +} + +void SwFieldType::GatherNodeIndex(std::vector<sal_uLong>& 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 SwFieldTypes::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldTypes")); + sal_uInt16 nCount = size(); + for (sal_uInt16 nType = 0; nType < nCount; ++nType) + (*this)[nType]->dumpAsXml(pWriter); + 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_Cache() + , m_bUseFieldValueCache( bUseFieldValueCache ) + , m_nLang( nLang ) + , m_bIsAutomaticLanguage( true ) + , m_nFormat( nFormat ) + , m_pType( pType ) +{ + 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; + 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; + 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; + 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(); + Color* pCol = nullptr; + + // Bug #60010 + LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, nFormat, *pFormatter ); + + if( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && LANGUAGE_SYSTEM != nFormatLng ) + { + SvNumFormatType nType = SvNumFormatType::DEFINED; + sal_Int32 nDummy; + + 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()); + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwValueField")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_fValue"), BAD_CAST(OString::number(m_fValue).getStr())); + SwField::dumpAsXml(pWriter); + 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; + 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: + return true; + default: break; + } + return false; +} + +void SwField::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwField")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name())); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nFormat"), BAD_CAST(OString::number(m_nFormat).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nLang"), BAD_CAST(OString::number(m_nLang.get()).getStr())); + + 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..f2a6a779a --- /dev/null +++ b/sw/source/core/fields/flddat.cxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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* pDoc, const DateTime& rDT) +{ + SvNumberFormatter* pFormatter = pDoc->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(); + + long nVal = static_cast<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( DateTime::EMPTY ); + aDateTime.SetNanoSec(aDateTimeValue.NanoSeconds); + aDateTime.SetSec(aDateTimeValue.Seconds); + aDateTime.SetMin(aDateTimeValue.Minutes); + aDateTime.SetHour(aDateTimeValue.Hours); + aDateTime.SetDay(aDateTimeValue.Day); + aDateTime.SetMonth(aDateTimeValue.Month); + aDateTime.SetYear(aDateTimeValue.Year); + 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..25c31230a --- /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()), + aValues(rSrc.aValues), aSelectedItem(rSrc.aSelectedItem), + aName(rSrc.aName), aHelp(rSrc.aHelp), aToolTip(rSrc.aToolTip) +{ +} + +SwDropDownField::~SwDropDownField() +{ +} + +OUString SwDropDownField::ExpandImpl(SwRootFrame const*const) const +{ + OUString sSelect = GetSelectedItem(); + if (sSelect.isEmpty()) + { + vector<OUString>::const_iterator aIt = aValues.begin(); + if ( aIt != 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(const vector<OUString> & rItems) +{ + aValues = rItems; + aSelectedItem.clear(); +} + +void SwDropDownField::SetItems(const uno::Sequence<OUString> & rItems) +{ + aValues.clear(); + + comphelper::sequenceToContainer(aValues, rItems); + + aSelectedItem.clear(); +} + +uno::Sequence<OUString> SwDropDownField::GetItemSequence() const +{ + return comphelper::containerToSequence(aValues); +} + + +void SwDropDownField::SetSelectedItem(const OUString & rItem) +{ + vector<OUString>::const_iterator aIt = + std::find(aValues.begin(), aValues.end(), rItem); + + if (aIt != aValues.end()) + aSelectedItem = *aIt; + else + aSelectedItem.clear(); +} + +void SwDropDownField::SetName(const OUString & rName) +{ + aName = rName; +} + +void SwDropDownField::SetHelp(const OUString & rHelp) +{ + aHelp = rHelp; +} + +void SwDropDownField::SetToolTip(const OUString & rToolTip) +{ + aToolTip = rToolTip; +} + +bool SwDropDownField::QueryValue(::uno::Any &rVal, sal_uInt16 nWhich) const +{ + nWhich &= ~CONVERT_TWIPS; + switch( nWhich ) + { + case FIELD_PROP_PAR1: + rVal <<= aSelectedItem; + break; + case FIELD_PROP_PAR2: + rVal <<= aName; + break; + case FIELD_PROP_PAR3: + rVal <<= aHelp; + break; + case FIELD_PROP_PAR4: + rVal <<= 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 >>= aName; + break; + + case FIELD_PROP_PAR3: + rVal >>= aHelp; + break; + + case FIELD_PROP_PAR4: + rVal >>= 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..aa937cb22 --- /dev/null +++ b/sw/source/core/fields/fldlst.cxx @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <editsh.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <docary.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <expfld.hxx> +#include <pam.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..2847ecbb1 --- /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* pDocument) + : SwFieldType( SwFieldIds::Macro ), + m_pDoc(pDocument) +{ +} + +std::unique_ptr<SwFieldType> SwMacroFieldType::Copy() const +{ + return std::make_unique<SwMacroFieldType>(m_pDoc); +} + +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, + const OUString& rMacroName, + const OUString& rLibraryName ) +{ + // concatenate library and name; use dot only if both strings have content + rMacro = rLibraryName; + if ( !rLibraryName.isEmpty() && !rMacroName.isEmpty() ) + 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..614d73cf2 --- /dev/null +++ b/sw/source/core/fields/postithelper.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 <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 <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; +} +} + +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 = 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() ) + aRet = DELETED; + o_rInfo.mRedlineAuthor = pRedline->GetAuthor(); + } + } + } + } + } + + return ( (aRet==VISIBLE) && SwScriptInfo::IsInHiddenRange( *pTextNode , rAnchorPos.nContent.GetIndex()) ) + ? HIDDEN + : aRet; +} + +long SwPostItHelper::getLayoutHeight( const SwRootFrame* pRoot ) +{ + long nRet = pRoot ? pRoot->getFrameArea().Height() : 0; + return nRet; +} + +void SwPostItHelper::setSidebarChanged( SwRootFrame* pRoot, bool bBrowseMode ) +{ + if( pRoot ) + { + pRoot->SetSidebarChanged(); + if( bBrowseMode ) + pRoot->InvalidateBrowseWidth(); + } +} + +unsigned long SwPostItHelper::getPageInfo( SwRect& rPageFrame, const SwRootFrame* pRoot, const Point& rPoint ) +{ + unsigned long 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..db2cdc3bf --- /dev/null +++ b/sw/source/core/fields/reffld.cxx @@ -0,0 +1,1462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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, + const OUString& rReferenceLanguage) +{ + if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != "hu" && rReferenceLanguage != "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) + + LanguageTag aLanguageTag(eLang); + CharClass aCharClass( aLanguageTag ); + 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 OUString sLettersStartingWithVowels = "aefilmnorsuxyAEFILMNORSUXY"; + if (sLettersStartingWithVowels.indexOf(sNumbering[0]) != -1) + { + // 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 OUString sVowels = OUStringLiteral("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 ") ) + { + // lowercase, if rReferenceLanguage == "hu", not "Hu" + OUString sArticle; + + if ( rReferenceLanguage == "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 ); +} + +// #i85090# +OUString SwGetRefField::GetExpandedTextOfReferencedTextNode( + SwRootFrame const& rLayout) const +{ + const SwTextNode* pReferencedTextNode( GetReferencedTextNode() ); + return pReferencedTextNode + ? sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0)) + : OUString(); +} + +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, + OUString const& rSetReferenceLanguage) +{ + // remove all special characters (replace them with blanks) + if (!rText.isEmpty()) + { + 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.isEmpty()) + { + lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage); + } + } +} + +// #i81002# - parameter <pFieldTextAttr> added +void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr ) +{ + m_sText.clear(); + m_sTextRLHidden.clear(); + + SwDoc* pDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + // finding the reference target (the number) + sal_Int32 nNumStart = -1; + sal_Int32 nNumEnd = -1; + SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( + pDoc, 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 : pDoc->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(), *pDoc, 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 < pDoc->GetFootnoteIdxs().size(); ++i ) + { + SwTextFootnote* const pFootnoteIdx = pDoc->GetFootnoteIdxs()[i]; + if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() ) + { + m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(*pDoc, nullptr); + m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(*pDoc, 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(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; + + LanguageTag aLanguageTag( GetLanguage()); + LocaleDataWrapper aLocaleData( aLanguageTag ); + + // 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() && + 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() && + 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) + { + SwDoc* pDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc(); + const OUString rPar1 = GetPar1(); + // don't convert when the name points to an existing field type + if(!pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false)) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl ); + const char* pResId = nullptr; + 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* pDc ) + : SwFieldType( SwFieldIds::GetRef ), m_pDoc( pDc ) +{} + +std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const +{ + return std::make_unique<SwGetRefFieldType>( m_pDoc ); +} + +void SwGetRefFieldType::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + // update to all GetReference fields + if( !pNew && !pOld ) + { + 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() ); + } + } + // forward to text fields, they "expand" the text + NotifyClients( pOld, pNew ); +} + +namespace sw { + +bool IsMarkHintHidden(SwRootFrame const& rLayout, + SwTextNode const& rNode, SwTextAttrEnd const& rHint) +{ + if (!rLayout.IsHideRedlines()) + { + 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 ); + if( nullptr == ( pTextNd = aIdx.GetNode().GetTextNode())) + 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_pDoc ) + { + 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_pDoc, rDestDoc, rRefField, true ); + } + break; + + case REF_FOOTNOTE: + case REF_ENDNOTE: + aFntMap.Check( *m_pDoc, 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..37f2e3dc2 --- /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* pD ) + : SwFieldType( SwFieldIds::Script ), m_pDoc( pD ) +{} + +std::unique_ptr<SwFieldType> SwScriptFieldType::Copy() const +{ + return std::make_unique<SwScriptFieldType>( m_pDoc ); +} + +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..c93d04a5c --- /dev/null +++ b/sw/source/core/fields/tblcalc.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 <sal/config.h> + +#include <o3tl/any.hxx> + +#include <calc.hxx> +#include <calbck.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..08331d40b --- /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, + {OUString("TextField"), EE_FEATURE_FIELD, + cppu::UnoType<text::XTextField>::get(), beans::PropertyAttribute::READONLY, 0 }, + {OUString("TextPortionType"), WID_PORTIONTYPE, + ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + {OUString("TextUserDefinedAttributes"), EE_CHAR_XMLATTRIBS, + cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + {OUString("ParaUserDefinedAttributes"), EE_PARA_XMLATTRIBS, + cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { OUString(), 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 >() ) +, pSource(std::move(p)) +{ +} + +SwTextAPIObject::~SwTextAPIObject() throw() +{ + pSource->Dispose(); + 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 + pImpl = rSource.pImpl; + 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) +: pImpl(new SwTextAPIEditSource_Impl) +{ + pImpl->mpPool = &pDoc->GetDocShell()->GetPool(); + pImpl->mpDoc = pDoc; + pImpl->mnRef = 1; +} + +SwTextAPIEditSource::~SwTextAPIEditSource() +{ + if (!--pImpl->mnRef) + delete pImpl; +} + +void SwTextAPIEditSource::Dispose() +{ + pImpl->mpPool=nullptr; + pImpl->mpDoc=nullptr; + pImpl->mpTextForwarder.reset(); + pImpl->mpOutliner.reset(); +} + +SvxTextForwarder* SwTextAPIEditSource::GetTextForwarder() +{ + if( !pImpl->mpPool ) + return nullptr; // mpPool == 0 can be used to flag this as disposed + + if( !pImpl->mpOutliner ) + { + //init draw model first + pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + pImpl->mpOutliner.reset(new Outliner(pImpl->mpPool, OutlinerMode::TextObject)); + pImpl->mpDoc->SetCalcFieldValueHdl(pImpl->mpOutliner.get()); + } + + if( !pImpl->mpTextForwarder ) + { + pImpl->mpTextForwarder.reset(new SvxOutlinerForwarder(*pImpl->mpOutliner, false)); + } + + return pImpl->mpTextForwarder.get(); +} + +void SwTextAPIEditSource::SetText( OutlinerParaObject const & rText ) +{ + if ( pImpl->mpPool ) + { + if( !pImpl->mpOutliner ) + { + //init draw model first + pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + pImpl->mpOutliner.reset(new Outliner(pImpl->mpPool, OutlinerMode::TextObject)); + pImpl->mpDoc->SetCalcFieldValueHdl(pImpl->mpOutliner.get()); + } + + pImpl->mpOutliner->SetText( rText ); + } +} + +void SwTextAPIEditSource::SetString( const OUString& rText ) +{ + if ( pImpl->mpPool ) + { + if( !pImpl->mpOutliner ) + { + //init draw model first + pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + pImpl->mpOutliner.reset(new Outliner(pImpl->mpPool, OutlinerMode::TextObject)); + pImpl->mpDoc->SetCalcFieldValueHdl(pImpl->mpOutliner.get()); + } + else + pImpl->mpOutliner->Clear(); + pImpl->mpOutliner->Insert( rText ); + } +} + +std::unique_ptr<OutlinerParaObject> SwTextAPIEditSource::CreateText() +{ + if ( pImpl->mpPool && pImpl->mpOutliner ) + return pImpl->mpOutliner->CreateParaObject(); + else + return nullptr; +} + +OUString SwTextAPIEditSource::GetText() const +{ + if ( pImpl->mpPool && pImpl->mpOutliner ) + return 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..642f47c7f --- /dev/null +++ b/sw/source/core/fields/usrfld.cxx @@ -0,0 +1,368 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/zforlist.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()); + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserField")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nSubType"), BAD_CAST(OString::number(m_nSubType).getStr())); + SwValueField::dumpAsXml(pWriter); + 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_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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( !pOld && !pNew ) + m_bValidValue = false; + + NotifyClients( pOld, pNew ); + + // 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. + CharClass* pCharClass = rCalc.GetCharClass(); + LanguageTag aCalcLanguage = pCharClass->getLanguageTag(); + LanguageTag aFieldTypeLanguage(GetFieldTypeLanguage()); + bool bSwitchLanguage = aCalcLanguage != aFieldTypeLanguage; + if (bSwitchLanguage) + pCharClass->setLanguageTag(aFieldTypeLanguage); + + m_nValue = rCalc.Calculate( m_aContent ).GetDouble(); + + if (bSwitchLanguage) + pCharClass->setLanguageTag(aCalcLanguage); + + 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; + 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 ) + { + m_aContent = rStr; + + if (nFormat && nFormat != SAL_MAX_UINT32) + { + double fValue; + + if (GetDoc()->IsNumberFormat(rStr, nFormat, fValue)) + { + SetValue(fValue); + 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; + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserFieldType")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nValue"), BAD_CAST(OString::number(m_nValue).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aContent"), BAD_CAST(m_aContent.toUtf8().getStr())); + SwFieldType::dumpAsXml(pWriter); + 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..67027a4c5 --- /dev/null +++ b/sw/source/core/frmedt/fecopy.cxx @@ -0,0 +1,1626 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/docfile.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 <unotools/streamwrap.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <fmtornt.hxx> +#include <fmtflcnt.hxx> +#include <frmfmt.hxx> +#include <docary.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* pClpDoc, const OUString* pNewClpText ) +{ + OSL_ENSURE( pClpDoc, "No Clipboard document" ); + + pClpDoc->GetIDocumentUndoRedo().DoUndo(false); // always false! + + // delete content if ClpDocument contains content + SwNodeIndex aSttIdx( pClpDoc->GetNodes().GetEndOfExtras(), 2 ); + SwNodeIndex aEndNdIdx( *aSttIdx.GetNode().EndOfSectionNode() ); + SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode(); + if (!pTextNd || !pTextNd->GetText().isEmpty() || + aSttIdx.GetIndex()+1 != pClpDoc->GetNodes().GetEndOfContent().GetIndex() ) + { + pClpDoc->GetNodes().Delete( aSttIdx, + pClpDoc->GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() ); + pTextNd = pClpDoc->GetNodes().MakeTextNode( aSttIdx, + pClpDoc->GetDfltTextFormatColl() ); + --aSttIdx; + } + + // also delete surrounding FlyFrames if any + for( const auto pFly : *pClpDoc->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 ) + { + pClpDoc->getIDocumentLayoutAccess().DelLayoutFormat( pFly ); + } + } + + pClpDoc->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 + } + + pClpDoc->getIDocumentFieldsAccess().LockExpFields(); + pClpDoc->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 = pClpDoc->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 = *pClpDoc->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( pClpDoc->GetAttrPool(), aFrameFormatSetRange ); + + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA ); + aAnchor.SetAnchor( &aPos ); + aSet.Put( aAnchor ); + + SdrObject *const pNew = + pClpDoc->CloneSdrObj( *pObj ); + + SwPaM aTemp(aPos); + pClpDoc->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 ); + } + + pClpDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true ); + } + } + } + else + CopySelToDoc( pClpDoc ); // copy the selections + + pClpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE ); + pClpDoc->getIDocumentFieldsAccess().UnlockExpFields(); + if( !pClpDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() ) + pClpDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); +} + +static const Point &lcl_FindBasePos( const SwFrame *pFrame, const Point &rPt ) +{ + const SwFrame *pF = pFrame; + while ( pF && !pF->getFrameArea().IsInside( 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* pDestShell, 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( !pDestShell->Imp()->GetDrawView() ) + // should create it now + pDestShell->MakeDrawView(); + else if( bSelectInsert ) + pDestShell->Imp()->GetDrawView()->UnmarkAll(); + + SdrPageView *pDestPgView = pDestShell->Imp()->GetPageView(), + *pSrcPgView = Imp()->GetPageView(); + SwDrawView *pDestDrwView = pDestShell->Imp()->GetDrawView(), + *pSrcDrwView = Imp()->GetDrawView(); + SwDoc* pDestDoc = pDestShell->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 == pDestShell ) + { + // 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, + *pDestShell, aAnchor, aNewAnch, false ); + } + else + { + SwPaM *pCursor = pDestShell->GetCursor(); + if( pCursor->GetNode().IsNoTextNode() ) + bRet = false; + else + bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), + pCursor->GetNode(), nullptr, rInsPt, + *pDestShell, aAnchor, + aNewAnch, false ); + } + } + else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() ) + { + aAnchor.SetPageNum( pDestShell->GetPageNumber( rInsPt ) ); + const SwRootFrame* pTmpRoot = pDestShell->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( *pDestShell->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 ( dynamic_cast<const SwDrawFrameFormat*>( pFormat) != nullptr ) + { + static_cast<SwDrawFrameFormat*>(pFormat)->PosAttrSet(); + } + } + if( bSelectInsert ) + pDestDrwView->MarkObj( pNew, pDestPgView ); + } + } + } + } + + if ( bIsMove && bRet ) + { + if( pDestShell == 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* pDestShell, const Point& rSttPt, + const Point& rInsPt, bool bIsMove, bool bSelectInsert ) +{ + bool bRet = false; + + OSL_ENSURE( pDestShell, "Copy without DestShell." ); + OSL_ENSURE( this == pDestShell || !pDestShell->IsObjSelected(), + "Dest-Shell cannot be in Obj-Mode" ); + + SET_CURR_SHELL( pDestShell ); + + pDestShell->StartAllAction(); + pDestShell->GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + + // Shift references + bool bCopyIsMove = mxDoc->IsCopyIsMove(); + if( bIsMove ) + // set a flag in Doc, handled in TextNodes + mxDoc->SetCopyIsMove( true ); + + RedlineFlags eOldRedlMode = pDestShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + pDestShell->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 = pDestShell->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 == pDestShell ) + { + // 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, + *pDestShell, aAnchor, aNewAnch, true ); + } + } + else + { + const SwPaM *pCursor = pDestShell->GetCursor(); + if( pCursor->GetNode().IsNoTextNode() ) + bRet = false; + else + bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), pCursor->GetNode(), + pFly, rInsPt, *pDestShell, aAnchor, + aNewAnch, GetDoc() == pDestShell->GetDoc()); + } + } + else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() ) + { + aAnchor.SetPageNum( pDestShell->GetPageNumber( rInsPt ) ); + const SwRootFrame* pTmpRoot = pDestShell->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 = pDestShell->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( pDestShell->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??? + pDestShell->Imp()->GetDrawView()->UnmarkAll(); + pDestShell->SelectFlyFrame( *pFlyFrame ); + } + } + + if (this != pDestShell && !pDestShell->HasShellFocus()) + pDestShell->Imp()->GetDrawView()->hideMarkHandles(); + } + } + else if ( IsObjSelected() ) + bRet = CopyDrawSel( pDestShell, 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 == pDestShell ) + { + // 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( !pDestShell->GetCursor()->GetNode().IsNoTextNode() ) + { + pDstPos.reset(new SwPosition( *pDestShell->GetCursor()->GetPoint() )); + bRet = true; + } + + if( bRet ) + { + if( GetDoc() == pDestShell->GetDoc() ) + ParkTableCursor(); + + bRet = pDestShell->GetDoc()->InsCopyOfTable( *pDstPos, aBoxes,nullptr, + bIsMove && this == pDestShell && + aBoxes.size() == pTableNd->GetTable(). + GetTabSortBoxes().size(), + this != pDestShell ); + + if( this != pDestShell ) + *pDestShell->GetCursor()->GetPoint() = *pDstPos; + + // create all parked Cursor? + if( GetDoc() == pDestShell->GetDoc() ) + GetCursor(); + + // JP 16.04.99: Bug 64908 - Set InsPos, to assure the parked + // Cursor is positioned at the insert position + if( this == pDestShell ) + GetCursorDocPos() = rInsPt; + } + } + } + else + { + bRet = true; + if( this == pDestShell ) + { + // 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( pDestShell->GetCursor()->GetNode().IsNoTextNode() ) + bRet = false; + + if( bRet ) + bRet = SwEditShell::Copy( pDestShell ); + } + + pDestShell->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; pDestShell->ActionPend(); ++nActCnt ) + pDestShell->EndAllAction(); + + for( ; nActCnt; --nActCnt ) + pDestShell->StartAllAction(); + } + pDestShell->GetDoc()->getIDocumentFieldsAccess().UnlockExpFields(); + pDestShell->GetDoc()->getIDocumentFieldsAccess().UpdateFields(false); + + pDestShell->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; + } +} + +bool SwFEShell::Paste( SwDoc* pClpDoc, bool bNestedTable ) +{ + SET_CURR_SHELL( this ); + OSL_ENSURE( pClpDoc, "no clipboard document" ); + // then till end of the nodes array + SwNodeIndex aIdx( pClpDoc->GetNodes().GetEndOfExtras(), 2 ); + SwPaM aCpyPam( aIdx ); //DocStart + + // 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().GetTableNode(); + if( !pSrcNd ) // table node ? + { // don't skip !! + SwContentNode* pCNd = aCpyPam.GetNode().GetContentNode(); + if( pCNd ) + aCpyPam.GetPoint()->nContent.Assign( pCNd, 0 ); + else if( !aCpyPam.Move( fnMoveForward, GoInNode )) + aCpyPam.Move( fnMoveBackward, GoInNode ); + } + + aCpyPam.SetMark(); + aCpyPam.Move( fnMoveForward, GoInDoc ); + + 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( pClpDoc->IsColumnSelection() && !IsTableMode() ) + { + // Creation of the list of insert positions + std::vector< Insertion > aCopyVector; + // The number of text portions of the rectangular selection + const sal_uInt32 nSelCount = aCpyPam.GetPoint()->nNode.GetIndex() + - aCpyPam.GetMark()->nNode.GetIndex(); + sal_uInt32 nCount = nSelCount; + SwNodeIndex aClpIdx( aIdx ); + SwPaM* pStartCursor = GetCursor(); + SwPaM* pCurrCursor = pStartCursor; + sal_uInt32 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 > 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 > 0; + nCursorCount = 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 = std::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 && 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; + pClpDoc->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 && + // are we at the beginning of the cell? (if not, we will insert a nested table) + // first paragraph of the cell? + rPaM.GetNode().GetIndex() == rPaM.GetNode().FindTableBoxStartNode()->GetIndex()+1 && + // beginning of the paragraph? + !rPaM.GetPoint()->nContent.GetIndex()) + { + 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() && + !pClpDoc->GetSpzFrameFormats()->empty() ) + { + // we need a DrawView + if( !Imp()->GetDrawView() ) + MakeDrawView(); + + for ( auto pCpyFormat : *pClpDoc->GetSpzFrameFormats() ) + { + bool bInsWithFormat = true; + + if( Imp()->GetDrawView()->IsGroupEntered() && + RES_DRAWFRMFMT == pCpyFormat->Which() && + (RndStdIds::FLY_AS_CHAR != pCpyFormat->GetAnchor().GetAnchorId()) ) + { + const SdrObject* pSdrObj = pCpyFormat->FindSdrObject(); + if( pSdrObj ) + { + SdrObject* pNew = GetDoc()->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 ); + } + + Imp()->GetDrawView()->InsertObjectAtView( pNew, *Imp()->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 ); + + bInsWithFormat = false; + } + } + + if( bInsWithFormat ) + { + 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() && + GetDoc()->IsInHeaderFooter( pPos->nNode ) ) + { + const SdrObject *pCpyObj = pCpyFormat->FindSdrObject(); + if (pCpyObj && CheckControlLayer(pCpyObj)) + continue; + } + 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. + continue; + } + + // Ignore TextBoxes, they are already handled in sw::DocumentLayoutManager::CopyLayoutFormat(). + if (SwTextBoxHelper::isTextBox(pCpyFormat, RES_FLYFRMFMT)) + continue; + + aAnchor.SetAnchor( pPos ); + } + else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() ) + { + aAnchor.SetPageNum( GetPhyPageNum() ); + } + else if( RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() ) + { + Point aPt; + (void)lcl_SetAnchor( *rPaM.GetPoint(), rPaM.GetNode(), + nullptr, aPt, *this, aAnchor, aPt, false ); + } + + SwFrameFormat * pNew = GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pCpyFormat, aAnchor, true, true ); + + if( pNew ) + { + if( RES_FLYFRMFMT == pNew->Which() ) + { + const Point aPt( GetCursorDocPos() ); + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pNew)-> + GetFrame( &aPt ); + if( pFlyFrame ) + SelectFlyFrame( *pFlyFrame ); + // always pick the first FlyFrame only; the others + // were copied to the clipboard via Fly in Fly + break; + } + else + { + OSL_ENSURE( RES_DRAWFRMFMT == pNew->Which(), "New format."); + // #i52780# - drawing object has to be made visible on paste. + pNew->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING)); + SdrObject *pObj = pNew->FindSdrObject(); + SwDrawView *pDV = Imp()->GetDrawView(); + pDV->MarkObj( pObj, pDV->GetSdrPageView() ); + // #i47455# - notify draw frame format + // that position attributes are already set. + if ( dynamic_cast<const SwDrawFrameFormat*>( pNew) != nullptr ) + { + static_cast<SwDrawFrameFormat*>(pNew)->PosAttrSet(); + } + } + } + } + } + } + else + { + if( bDelTable && IsTableMode() ) + { + SwEditShell::Delete(); + bDelTable = false; + } + + SwPosition& rInsPos = *rPaM.GetPoint(); + const SwStartNode* pBoxNd = rInsPos.nNode.GetNode(). + FindTableBoxStartNode(); + if( pBoxNd && 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; + + pClpDoc->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(); + sal_uLong const nEndIdx = aPaM.End()->nNode.GetIndex(); + + for (sal_uLong 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 ); + SwPaM aCpyPam( *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* pTableNode = aCpyPam.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); + aCpyPam = aTmp; + } + EndUndo(SwUndoId::INSERT); + } + + MovePage( GetThisFrame, GetLastSub ); + aCpyPam.SetMark(); + *aCpyPam.GetMark() = *GetCursor()->GetPoint(); + + SET_CURR_SHELL( this ); + + StartAllAction(); + GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + SetSelection(aCpyPam); + // copy the text of the selection + SwEditShell::Copy(&rToFill); + + 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::OInterfaceContainerHelper2& 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()); + + MapMode aTmp( MapUnit::MapTwip ); + pVirtDev->SetMapMode( aTmp ); + if( pVirtDev->SetOutputSize( aSz ) ) + { + aGrf.Draw( pVirtDev.get(), 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 ) +{ + SET_CURR_SHELL( 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 + SfxItemSet aFrameSet( mxDoc->GetAttrPool(), + svl::Items<RES_SURROUND, RES_ANCHOR>{} ); + 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( dynamic_cast<const SdrCaptionObj*>( pOldObj) != nullptr) + aNullPt = static_cast<SdrCaptionObj*>(pOldObj)->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( OBJ_GRUP ); + 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) +{ + SET_CURR_SHELL( 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); + + OUString aReferer; + SwDocShell *pDocShell = GetDoc()->GetDocShell(); + if (pDocShell->HasName()) { + aReferer = pDocShell->GetMedium()->GetName(); + } + + // set in all cases - the Clone() will have copied an existing link (!) + pNewGrafObj->SetGraphicLink(rURL, aReferer, OUString()); + + pResult = pNewGrafObj; + } + else + { + pView->AddUndo(std::make_unique<SdrUndoAttrObj>(*pObj)); + + SfxItemSet aSet(pView->GetModel()->GetItemPool(), svl::Items<XATTR_FILLSTYLE, XATTR_FILLBITMAP>{}); + + 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..819fd1d4c --- /dev/null +++ b/sw/source/core/frmedt/fedesc.cxx @@ -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 . + */ + +#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> + +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!"); + + SET_CURR_SHELL( 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(); + SET_CURR_SHELL( 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..4ad007164 --- /dev/null +++ b/sw/source/core/frmedt/fefly1.cxx @@ -0,0 +1,2110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <fmtornt.hxx> +#include <fmturl.hxx> +#include <fmtfsize.hxx> +#include <docary.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 <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 ) +{ + SET_CURR_SHELL( 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() ) + { + 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() +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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); + } + + 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)); + 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, o3tl::default_delete<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(); + } + } + + 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 ) +{ + SET_CURR_SHELL( 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 SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( RES_HORI_ORIENT, false, &pItem ) + && text::HoriOrientation::NONE == static_cast<const SwFormatHoriOrient*>(pItem)->GetHoriOrient() ) + { + bHOriChgd = true; + aOldH.reset(static_cast<SwFormatHoriOrient*>(pItem->Clone())); + const_cast<SfxItemSet&>(rSet).Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT ) ); + } + if( SfxItemState::SET == rSet.GetItemState( RES_VERT_ORIENT, false, &pItem ) + && text::VertOrientation::NONE == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() ) + { + bVOriChgd = true; + aOldV.reset(static_cast<SwFormatVertOrient*>(pItem->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( *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; + SET_CURR_SHELL( this ); + StartAllAction(); + SwShellCursor *pStartCursor = dynamic_cast<SwShellCursor*>(GetSwCursor()); + SwShellCursor *pCursor = pStartCursor; + do + { + if (!pCursor) + break; + + // Has the anchor not been set or been set incompletely? + if( pFlyAttrSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pFlyAttrSet->GetItemState( RES_ANCHOR, false, + &pItem ) ) + { + SwFormatAnchor* pAnchor = const_cast<SwFormatAnchor*>(static_cast<const 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 ) + { + const Point aPt( GetCursorDocPos() ); + SwFlyFrame* pFrame = pFormat->GetFrame( &aPt ); + + if( pFrame ) + { + // 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; + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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 + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false, &pItem ) ) + { + const SwFormatAnchor* pAnchor = static_cast<const SwFormatAnchor*>(pItem); + 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 ) +{ + SET_CURR_SHELL( 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; +} + +SfxItemSet 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. + SfxItemSet aSet(rPool, svl::Items<RES_VERT_ORIENT, RES_ANCHOR>{}); + aSet.Put(rAnchor); + return aSet; +} + +bool SwFEShell::SetDrawingAttr( SfxItemSet& rSet ) +{ + bool bRet = false; + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( this ); + + SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame(); + OSL_ENSURE( pFly, "SetFlyFrameAttr, no Fly selected." ); + if( pFly ) + { + 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 ) + { + StartAllAction(); + SET_CURR_SHELL( this ); + + SwFlyFrameFormat* pFlyFormat = pFly->GetFormat(); + const Point aPt( pFly->getFrameArea().Pos() ); + + std::unique_ptr<SfxItemSet> pSet; + const SfxPoolItem* pItem; + if( SfxItemState::SET == pNewFormat->GetItemState( RES_ANCHOR, false, &pItem )) + { + pSet.reset(new SfxItemSet( GetDoc()->GetAttrPool(), aFrameFormatSetRange )); + pSet->Put( *pItem ); + if( !sw_ChkAndSetNewAnchor( *pFly, *pSet )) + { + pSet.reset(); + } + } + + if( GetDoc()->SetFrameFormatToFly( *pFlyFormat, *pNewFormat, pSet.get(), bKeepOrient )) + { + SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt ); + if( pFrame ) + SelectFlyFrame( *pFrame ); + else + GetLayout()->SetAssertFlyPages(); + } + pSet.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 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 long lXDiff = aPt.getX() - pFly->getFrameArea().Left(); + const 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 = 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 ) +{ + SET_CURR_SHELL( this ); + + const SdrMarkList *pMrkList; + if( Imp()->HasDrawView() && 1 == + ( pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList())->GetMarkCount() ) + { + 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 long nWidth = rBound.Right() - rBound.Left(); + const long nHeight= rBound.Bottom() - rBound.Top(); + aFrameSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, + std::max( nWidth, long(MINFLY) ), + std::max( nHeight, 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() ) + { + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() == 1 ) + { + 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() ) + { + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() == 1 ) + { + 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); + const MapMode aSourceMapMode( MapUnit::Map100thMM ); + const MapMode aTargetMapMode( MapUnit::MapTwip ); + nBaseline = OutputDevice::LogicToLogic( nBaseline, aSourceMapMode.GetMapUnit(), aTargetMapMode.GetMapUnit() ); + + 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..b34fb3f77 --- /dev/null +++ b/sw/source/core/frmedt/feflyole.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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; + sal_uLong 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(); +} + +OUString SwFEShell::GetUniqueShapeName() const +{ + return GetDoc()->GetUniqueShapeName(); +} + +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..9da71abf8 --- /dev/null +++ b/sw/source/core/frmedt/feshview.cxx @@ -0,0 +1,3262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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(const char* pResId, const SdrModel& rModel) +{ + ::basegfx::B2DPolyPolygon aRetval; + XLineEndListRef pLineEndList(rModel.GetLineEndList()); + + if( pLineEndList.is() ) + { + OUString aArrowName( SvxResId(pResId) ); + long nCount = pLineEndList->Count(); + 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; + SET_CURR_SHELL( 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(); + SwFrameFormat* pFormat = GetUserCall(pObject)->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 ( 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( dynamic_cast<const SwFlyFrame*>( pAnchObj) != nullptr ) + { + SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(pAnchObj); + 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, o3tl::default_delete<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() ) + { + 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( dynamic_cast<const SwCursorShell *>(&rSh) != nullptr ) + static_cast<SwCursorShell*>(&rSh)->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]; + if ( dynamic_cast<const SwFlyFrame*>( pAnchoredObj) == nullptr ) + continue; + + SwFlyFrame* pAct = static_cast<SwFlyFrame*>(pAnchoredObj); + SwRect aTmpCalcPnt( pAct->getFramePrintArea() ); + aTmpCalcPnt += pAct->getFrameArea().Pos(); + if ( aRect.IsOver( aTmpCalcPnt ) ) + { + SwContentFrame *pCnt = pAct->ContainsContent(); + while ( pCnt ) + { + aTmpCalcPnt = pCnt->getFramePrintArea(); + aTmpCalcPnt += pCnt->getFrameArea().Pos(); + if ( aRect.IsOver( 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() ); + long nWidth = 100; // (1/100th mm) + + // determine line width and calculate with it the line end width + if( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE ) + { + 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_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 ); + 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 ); + 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() ) + { + 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 ); + } + } + } + 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; + if( nullptr != ( pUserCall = GetUserCall(pObj) ) ) + { + 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 ) +{ + SET_CURR_SHELL(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; + SET_CURR_SHELL(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) +{ + SET_CURR_SHELL(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().IsInside( rPt ) ) + { + const SwTextFrame* pTextFrame = + dynamic_cast<const SwTextFrame*>(pContentFrame); + if ( pTextFrame ) + { + SwPosition aPos(GetDoc()->GetNodes()); + Point aTmpPt( rPt ); + if (pTextFrame->GetKeyCursorOfst(&aPos, aTmpPt)) + { + SwRect aCursorCharRect; + if (pTextFrame->GetCharRect(aCursorCharRect, + aPos)) + { + if ( aCursorCharRect.IsOver( 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().IsInside(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 ) +{ + if( !Imp()->HasDrawView() ) + return nullptr; + + const SdrObject *pBest = nullptr, + *pTop = nullptr; + + const 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; + SvxSearchDialogWrapper::SetSearchLabel( bNext ? SearchLabel::EndWrapped : SearchLabel::StartWrapped ); + } + } + + return pBest; +} + +bool SwFEShell::GotoObj( bool bNext, GotoObjFlags eType ) +{ + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + + const SdrObject* pBest = GetBestObject( bNext, eType ); + + 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( pBest->GetCurrentBoundRect() ); + } + CallChgLnk(); + return true; +} + +bool SwFEShell::BeginCreate( sal_uInt16 /*SdrObjKind ?*/ eSdrObjectKind, const Point &rPos ) +{ + bool bRet = false; + + if ( !Imp()->HasDrawView() ) + Imp()->MakeDrawView(); + + if ( GetPageNumber( rPos ) ) + { + Imp()->GetDrawView()->SetCurrentObj( eSdrObjectKind ); + if ( eSdrObjectKind == OBJ_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( sal_uInt16 /*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() +{ + OSL_ENSURE( Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1, + "New object not selected." ); + + 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() ); + + // alien identifier should end up on defaults + // duplications possible!! + sal_uInt16 nIdent = SdrInventor::Default == rSdrObj.GetObjInventor() + ? rSdrObj.GetObjIdentifier() + : 0xFFFF; + + // 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; + tools::Rectangle aRect( aTmp.SVRect() ); + aRect.AdjustLeft( -(MM50*2) ); + aRect.AdjustTop( -(MM50*2) ); + aRect.AdjustRight(MM50*2 ); + aRect.AdjustBottom(MM50*2 ); + + if( !aRect.IsOver( 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 = 0xFFFF == nIdent; + 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().IsInside( 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 + } + } + + SfxItemSet aSet( GetDoc()->GetAttrPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR>{} ); + 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( OBJ_NONE == nIdent ) + { + // For OBJ_NONE a fly is inserted. + const long nWidth = rBound.Right() - rBound.Left(); + const long nHeight= rBound.Bottom() - rBound.Top(); + aSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, std::max( nWidth, long(MINFLY) ), + std::max( nHeight, 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(); + pPg = pTmpSdrModel->AllocPage( false ); + pTmpSdrModel->InsertPage( pPg ); + } + 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() )) + { + SfxItemSet aHtmlSet( GetDoc()->GetAttrPool(), svl::Items<RES_VERT_ORIENT, RES_HORI_ORIENT>{} ); + // 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.SetName(GetUniqueShapeName()); + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(bRestore); + } + + Point aRelNullPt; + if( OBJ_CAPTION == nIdent ) + aRelNullPt = static_cast<SdrCaptionObj&>(rSdrObj).GetTailPos(); + else + aRelNullPt = rBound.TopLeft(); + + 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() +{ + 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() ) + { + 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() ) + { + 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 ) + { + SET_CURR_SHELL( 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( 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( !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 ) +{ + SET_CURR_SHELL( 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() +{ + SET_CURR_SHELL( 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().IsInside( aPt ) ) + { + if ( aPt.Y() > pPage->getFrameArea().Bottom() ) + pLast = pPage; + pPage = pPage->GetNext(); + } + if ( !pPage ) + pPage = pLast; + OSL_ENSURE( pPage, "Page not found." ); + + // Alien identifier should roll into the default, + // Duplications are possible!! + sal_uInt16 nIdent = + Imp()->GetDrawView()->GetCurrentObjInventor() == SdrInventor::Default ? + Imp()->GetDrawView()->GetCurrentObjIdentifier() : 0xFFFF; + + 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(); + + SfxItemSet aSet( GetAttrPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR>{} ); + aSet.Put( aAnch ); + + Point aRelNullPt; + + if( OBJ_CAPTION == nIdent ) + aRelNullPt = static_cast<SdrCaptionObj*>(pObj)->GetTailPos(); + else + aRelNullPt = rBound.TopLeft(); + + 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(); + } +} + +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( sal_uInt16 /*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(OBJ_CARC == eSdrObjectKind || OBJ_CCUT == 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)); + aAttr.Put(makeSdrCircEndAngleItem(0)); + pObj->SetMergedItemSet(aAttr); + } + else if(dynamic_cast<const SdrPathObj*>( pObj) != nullptr) + { + basegfx::B2DPolyPolygon aPoly; + + switch(eSdrObjectKind) + { + case OBJ_PATHLINE: + case OBJ_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 OBJ_FREELINE: + case OBJ_FREEFILL: + { + 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 OBJ_POLY: + case OBJ_PLIN: + { + 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(OBJ_PLIN == eSdrObjectKind) + { + aInnerPoly.append(basegfx::B2DPoint(aRect.Center().getX(), aRect.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + } + break; + case OBJ_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; + } + + static_cast<SdrPathObj*>(pObj)->SetPathPoly(aPoly); + } + else if(dynamic_cast<const SdrMeasureObj*>( pObj) != nullptr) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + static_cast<SdrMeasureObj*>(pObj)->SetPoint(Point(aStart.X(), nYMiddle), 0); + static_cast<SdrMeasureObj*>(pObj)->SetPoint(Point(aEnd.X(), nYMiddle), 1); + + SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool()); + SetLineEnds(aAttr, *pObj, nSlotId); + pObj->SetMergedItemSet(aAttr); + } + else if(dynamic_cast<const SdrCaptionObj*>( pObj) != nullptr) + { + bool bVerticalText = ( SID_DRAW_TEXT_VERTICAL == nSlotId || + SID_DRAW_CAPTION_VERTICAL == nSlotId ); + static_cast<SdrTextObj*>(pObj)->SetVerticalWriting(bVerticalText); + if(bVerticalText) + { + SfxItemSet aSet(pObj->GetMergedItemSet()); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pObj->SetMergedItemSet(aSet); + } + + static_cast<SdrCaptionObj*>(pObj)->SetLogicRect(aRect); + static_cast<SdrCaptionObj*>(pObj)->SetTailPos( + aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2)); + } + else if(dynamic_cast<const SdrTextObj*>( pObj) != nullptr) + { + SdrTextObj* pText = static_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) + { + SfxItemSet aSet(pDrawModel->GetItemPool(), svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST>{}); + 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::GetShapeBackgrd + 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::GetShapeBackgrd() const +{ + Color aRetColor; + + // check, if a draw view exists + OSL_ENSURE( Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackgrd - 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::GetShapeBackgrd - 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::GetShapeBackgrd - 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->GetDrawBackgrdColor(); + } + } + } + } + } + + 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::GetShapeBackgrd - 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::GetShapeBackgrd - 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::GetShapeBackgrd - 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().IsInside( 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..242c148f8 --- /dev/null +++ b/sw/source/core/frmedt/fetab.cxx @@ -0,0 +1,2324 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swwait.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <frmatr.hxx> +#include <fesh.hxx> +#include <doc.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( long nA, 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 = GetSwCursor(); + + 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; + } + + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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, true ); + 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; + } + + SET_CURR_SHELL( this ); + StartAllAction(); + + // 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 ); + 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 ); + while( pNextBox && + pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox ); + } + + sal_uLong 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 ); + + if( pCNd ) + { + 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 + { + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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 () == static_cast<sal_uInt16>(nLeftMin) && + m_pColumnCache->pLastCols->GetLeft () == static_cast<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) && + m_pColumnCache->pLastCols->GetRight () == static_cast<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&& + m_pColumnCache->pLastCols->GetRightMax() == static_cast<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 long nLeftMin = ( aRectFnSet.IsVert() ? + pTab->GetPrtLeft() - pPage->getFrameArea().Left() : + pTab->GetPrtTop() - pPage->getFrameArea().Top() ); + const long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0; + const long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea()); + const 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; + + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 ) + { + SET_CURR_SHELL( this ); + StartAllAction(); + GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) ); + EndAllActionAndCall(); + } +} + +void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( this ); + if( !bTstOnly ) + StartAllAction(); + bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize ); + if( !bTstOnly ) + EndAllActionAndCall(); + return bRet; +} + +void SwFEShell::SetRowBackground( const SvxBrushItem &rNew ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( this ); + StartAllAction(); + GetDoc()->SetTabBorders( *getShellCursor( false ), rSet ); + EndAllActionAndCall(); +} + +void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine, + const editeng::SvxBorderLine* pBorderLine ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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 ); + + SET_CURR_SHELL( this ); + StartAllAction(); + + GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt ); + + if( !IsCursorReadonly() ) + { + if( IsTableMode() ) + ClearMark(); + ParkCursorInTab(); + } + EndAllActionAndCall(); +} + +// cancel table selection +void SwFEShell::UnProtectCells() +{ + SET_CURR_SHELL( 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() +{ + SET_CURR_SHELL( 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 ); + SET_CURR_SHELL( 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); + if( nullptr != ( pNd = rPos.nNode.GetNode().GetContentNode() )) + 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 ) +{ + SET_CURR_SHELL( 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; +} + +bool SwFEShell::SetTableStyle(const OUString& rStyleName) +{ + // make sure SwDoc has the style + SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName); + if (!pTableFormat) + return false; + + SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode) + return false; + + // set the name & update + return 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() ) + { + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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() ) + { + long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea()); + + const 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 long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + + const 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.IsInside( 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 ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + pFrame = lcl_FindFrame( static_cast<SwFlyFrame*>(pObj), + 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 ) + { + SET_CURR_SHELL( 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 long nX = pFrame->getFrameArea().Left(); + + // get TabCols, only via these we get the position + SwTabCols aTabCols; + GetMouseTabCols( aTabCols, rPt ); + + const 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() ) + { + SET_CURR_SHELL( 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; + + SET_CURR_SHELL( 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); + 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..e98406446 --- /dev/null +++ b/sw/source/core/frmedt/fews.cxx @@ -0,0 +1,1328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 +{ + SET_CURR_SHELL( 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: + if( nullptr == (pFrame = pFrame->FindFooterOrHeader()) ) + 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().IsInside( rPoint ) ) + pPage = pPage->GetNext(); + if ( pPage ) + return static_cast<const SwPageFrame*>(pPage)->GetPhyPageNum(); + else + return 0; +} + +bool SwFEShell::GetPageNumber( 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); + + sal_uLong 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; + + SET_CURR_SHELL( 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 ); + sal_uLong 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(); + if( nullptr != ( pFly = pFrame->FindFlyFrame() ) ) + 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( dynamic_cast<const SwTextFrame*>(pFrame) ); + 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(); + 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 ) + { + 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 ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr && !bEnvironment ) + pRef = static_cast<const SwVirtFlyDrawObj*>(pObj)->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..cdcb4451b --- /dev/null +++ b/sw/source/core/frmedt/tblsel.cxx @@ -0,0 +1,2601 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <docary.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() ); + } + + long X() const { return aPos.X(); } + 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 sal_uLong 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().IsOver( 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(); + + if( nullptr == (pTable = pTable->GetFollow()) ) + 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().IsOver( 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 long nUnionRight = rUnion.Right(); + const long nUnionBottom = rUnion.Bottom(); + const long nFrameRight = rFrameRect.Right(); + const long nFrameBottom = rFrameRect.Bottom(); + + // ignore if FrameRect is outside the union + + const long nXFuzzy = aRectFnSet.IsVert() ? 0 : 20; + const 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; + long nYPos = LONG_MAX; + long nXPos = 0; + 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(); + + if( nullptr == (pTable = pTable->GetFollow()) ) + 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().IsOver( 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; ) + if( USHRT_MAX != ( nWhichId = rBoxes[ --n ] + ->GetTabBox()->IsFormulaOrValueBox() )) + 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().IsOver( 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; ) + if( USHRT_MAX != ( nWhichId = rBoxes[ --n ] + ->GetTabBox()->IsFormulaOrValueBox() )) + 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(); + sal_uLong 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 + 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().IsOver( 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(); + 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() ); + long nLeft = rUnion.Left() - pCell->getFrameArea().Left(); + nLeft = nLeft * aNew.GetWidth() / + pCell->getFrameArea().Width(); + 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 ); + + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pBox->GetFrameFormat()->GetAttrSet() + .GetItemState( RES_BOX, false, &pItem )) + { + SvxBoxItem aBox( *static_cast<const SvxBoxItem*>(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() ); + 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; + 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(), -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, long nWish, + const 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 long nTmpWish = pOrg->GetFormat()->GetFrameSize().GetWidth(); + const 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); + long nSttTop = aRectFnSet.GetTop(pStart->getFrameArea()); + 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 long nStSz = pStart->GetFormat()->GetFrameSize().GetWidth(); + const long nEdSz = pEnd->GetFormat()->GetFrameSize().GetWidth(); + const long nWish = std::max( 1L, pTable->GetFormat()->GetFrameSize().GetWidth() ); + while ( pTable ) + { + SwRectFnSet aRectFnSet(pTable); + const long nOfst = aRectFnSet.GetPrtLeft(*pTable); + const long nPrtWidth = aRectFnSet.GetWidth(pTable->getFramePrintArea()); + long nSt1 = ::lcl_CalcWish( pStart, nWish, nPrtWidth ) + nOfst; + long nEd1 = ::lcl_CalcWish( pEnd, nWish, nPrtWidth ) + nOfst; + + if ( nSt1 <= nEd1 ) + nEd1 += static_cast<long>((nEdSz * nPrtWidth) / nWish) - 1; + else + nSt1 += static_cast<long>((nStSz * nPrtWidth) / nWish) - 1; + + long nSt2; + 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 ) + { + long nTmp = nSt1; + nSt1 = nEd1; + nEd1 = nTmp; + } + if( nSt2 > nEd2 ) + { + 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().IsOver( 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().IsOver( 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; + 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()[static_cast<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; + + for ( sal_uInt16 j = nStPos; j <= nEndPos; ++j ) + ::lcl_InsertRow( *rTable.GetTabLines()[j], + 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 ) ) ) ) + { + 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/grfatr.cxx b/sw/source/core/graphic/grfatr.cxx new file mode 100644 index 000000000..a8e2e63c7 --- /dev/null +++ b/sw/source/core/graphic/grfatr.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 <com/sun/star/drawing/ColorMode.hpp> +#include <o3tl/any.hxx> +#include <vcl/GraphicObject.hxx> +#include <grfatr.hxx> +#include <swunohelper.hxx> +#include <osl/diagnose.h> + +#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==(static_cast<const SfxEnumItem<MirrorGraph>&>(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 ); +} + +sal_Int16 SwRotationGrf::checkAndCorrectValue(sal_Int16 nValue) +{ + if(nValue < 0) + { + // 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 + (nValue % 3600); + } + else if (nValue >= 3600) + { + // bigger range, use modulo + DBG_ASSERT(false, "SwRotationGrf: Value is in 10th degree and *has* to be in [0 .. 3600[ (!)"); + return nValue % 3600; + } + + return nValue; +} + +SwRotationGrf::SwRotationGrf( sal_Int16 nVal, const Size& rSz ) + // tdf#115529 check and evtl. correct value +: SfxUInt16Item( RES_GRFATR_ROTATION, checkAndCorrectValue(nVal) ), + 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(static_cast<sal_uInt16>(checkAndCorrectValue(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_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_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(static_cast<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..bb39141cc --- /dev/null +++ b/sw/source/core/graphic/ndgrf.cxx @@ -0,0 +1,874 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/helpers.hxx> +#include <tools/urlobj.hxx> +#include <tools/fract.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 <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 ), + maGrfObj(), + 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, + const OUString& rGrfName, + const OUString& rFltName, + SwGrfFormatColl *pGrfColl, + SwAttrSet const * pAutoAttr ) : + SwNoTextNode( rWhere, SwNodeType::Grf, pGrfColl, pAutoAttr ), + maGrfObj(), + mbInBaseLinkSwapIn(true), + // #i73788# + mbLinkedInputStreamReady( false ), + mbIsStreamReadOnly( false ) +{ + Graphic aGrf; aGrf.SetDefaultType(); + maGrfObj.SetGraphic( aGrf, rGrfName ); + + 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, OUString(), &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, sURLLink ); + 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, sURLLink ); + + if( mxLink.is() ) + { + if( getLayoutFrame( GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() ) ) + { + SwMsgPoolItem aMsgHint( RES_GRF_REREAD_AND_INCACHE ); + ModifyNotification( &aMsgHint, &aMsgHint ); + } + 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, sURLLink ); + 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, sURLLink ); + 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 ) + { + SwMsgPoolItem aMsgHint( RES_UPDATE_ATTR ); + ModifyNotification( &aMsgHint, &aMsgHint ); + } + + return bReadGrf; +} + +SwGrfNode::~SwGrfNode() +{ + mpReplacementGraphic.reset(); + + // #i73788# + mpThreadConsumer.reset(); + + SwDoc* pDoc = GetDoc(); + if( mxLink.is() ) + { + OSL_ENSURE( !mbInSwapIn, "DTOR: I am still in SwapIn" ); + pDoc->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); +} + +/// 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) + { + 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, OUString()); + onGraphicChanged(); +} + +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 ); + ModifyNotification( &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( const OUString& rGrfName, const OUString& rFltName ) +{ + mxLink = new SwBaseLink( SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::GDIMETAFILE, this ); + + IDocumentLinksAdministration& rIDLA = getIDocumentLinksAdministration(); + if( GetNodes().IsDocNodes() ) + { + mxLink->SetVisible( rIDLA.IsVisibleLinks() ); + if( rFltName == "DDE" ) + { + sal_Int32 nTmp = 0; + const OUString sApp{ rGrfName.getToken( 0, sfx2::cTokenSeparator, nTmp ) }; + const OUString sTopic{ rGrfName.getToken( 0, sfx2::cTokenSeparator, nTmp ) }; + const OUString sItem{ rGrfName.copy( 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() ) + { + 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* pDoc, const SwNodeIndex& rIdx, bool) const +{ + // copy formats into the other document + SwGrfFormatColl* pColl = pDoc->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(); + rGA.SetCrop( convertTwipToMm100( rCrop.GetLeft() ), + convertTwipToMm100( rCrop.GetTop() ), + convertTwipToMm100( rCrop.GetRight() ), + convertTwipToMm100( rCrop.GetBottom() )); + + 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.SetTransparency( 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) + { + 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 ); + ModifyNotification( &aMsgHint, &aMsgHint ); + } + } +} + +void SwGrfNode::UpdateLinkWithInputStream() +{ + // do not work on link, if a <SwapIn> has been triggered. + if ( !mbInSwapIn && IsLinkedFile() ) + { + GetLink()->setStreamToLoadFrom( mxInputStream, mbIsStreamReadOnly ); + GetLink()->Update(); + SwMsgPoolItem aMsgHint( RES_GRAPHIC_ARRIVED ); + ModifyNotification( &aMsgHint, &aMsgHint ); + + // #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..3b5e12c67 --- /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* pDoc); + 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> const& rIssueAdditionalInfo) + { + m_aIssueAdditionalInfo = 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..8d0e4d6c4 --- /dev/null +++ b/sw/source/core/inc/DateFormFieldButton.hxx @@ -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/. + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_DATEFORMEFIELDBUTTO_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_DATEFORMEFIELDBUTTO_HXX + +#include "FormFieldButton.hxx" + +class SwEditWin; +class FloatingWindow; +class SvNumberFormatter; +namespace sw +{ +namespace mark +{ +class DateFieldmark; +} +} // namespace sw + +/** + * 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 : public FormFieldButton +{ +public: + DateFormFieldButton(SwEditWin* pEditWin, sw::mark::DateFieldmark& rFieldMark, + SvNumberFormatter* pNumberFormatter); + virtual ~DateFormFieldButton() override; + + virtual void InitPopup() override; + +private: + SvNumberFormatter* m_pNumberFormatter; +}; + +#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..1759f5b94 --- /dev/null +++ b/sw/source/core/inc/DocumentChartDataProviderManager.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCHARTDATAPROVIDEMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCHARTDATAPROVIDEMANAGER_HXX + +#include <IDocumentChartDataProviderAccess.hxx> +#include <o3tl/deleter.hxx> +#include <rtl/ref.hxx> +#include <memory> + +class SwTable; +class SwChartDataProvider; +class SwChartLockController_Helper; +class SwDoc; + + +namespace sw { + +class DocumentChartDataProviderManager : 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, o3tl::default_delete<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..3df395329 --- /dev/null +++ b/sw/source/core/inc/DocumentContentOperationsManager.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_DOCUMENTCONTENTOPERATIONSMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCONTENTOPERATIONSMANAGER_HXX + +#include <IDocumentContentOperations.hxx> +#include <ndarr.hxx> //Only for lcl_RstTxtAttr + +class SwDoc; +class SwNoTextNode; +class SwFormatColl; +class SwHistory; + +namespace sw +{ + +class DocumentContentOperationsManager : 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; + + // Add optional parameter <bForceJoinNext>, default value <false> + // Needed for hiding of deletion redlines + bool DeleteAndJoin( SwPaM&, + const bool bForceJoinNext = false ) override; + + bool MoveRange(SwPaM&, SwPosition&, SwMoveFlags) override; + + bool MoveNodeRange(SwNodeRange&, SwNodeIndex&, SwMoveFlags) override; + + bool 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 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, + bool bExpandCharToPara = false, + 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( const SwNodePtr& rpNd, void* pArgs ); //originally from docfmt.cxx + + + virtual ~DocumentContentOperationsManager() override; + +private: + SwDoc& m_rDoc; + + bool DeleteAndJoinImpl(SwPaM&, const bool); + bool DeleteAndJoinWithRedlineImpl(SwPaM&, const bool unused = false); + bool DeleteRangeImpl(SwPaM&, const bool unused = false); + bool DeleteRangeImplImpl(SwPaM &); + 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, SwPosition& rTarget); + +void CalcBreaks(std::vector<std::pair<sal_uLong, sal_Int32>> & rBreaks, + SwPaM const & rPam, bool const isOnlyFieldmarks = false); + +} + +#endif // INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCONTENTOPERATIONSMANAGER_HXX + +/* 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..46c682817 --- /dev/null +++ b/sw/source/core/inc/DocumentDeviceManager.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 : 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..2f86e2d73 --- /dev/null +++ b/sw/source/core/inc/DocumentDrawModelManager.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_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 : 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..068d87eed --- /dev/null +++ b/sw/source/core/inc/DocumentExternalDataManager.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_DOCUMENTEXTERNALDATAMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTEXTERNALDATAMANAGER_HXX + +#include <IDocumentExternalData.hxx> + +namespace sw { + +class DocumentExternalDataManager : 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..c73345f66 --- /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 : 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, sal_uLong nLen) override; + virtual void SetFixFields(const DateTime* pNewDateTime) override; + virtual void FieldsToCalc(SwCalc& rCalc, sal_uLong nLastNd, sal_uInt16 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..c9813dbf8 --- /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 : 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..8223591d3 --- /dev/null +++ b/sw/source/core/inc/DocumentLinksAdministrationManager.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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> + +namespace sfx2 { class LinkManager; } +class SwDoc; +class SwPaM; +class SwNodeRange; + +namespace sw +{ + +class DocumentLinksAdministrationManager : 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( const OUString& 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..7508a2b5d --- /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 : 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..2ba7ececf --- /dev/null +++ b/sw/source/core/inc/DocumentListsManager.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_INC_DOCUMENTLISTSMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTSMANAGER_HXX + +#include <IDocumentListsAccess.hxx> +#include <memory> +#include <unordered_map> + +class SwList; +class SwDoc; + +namespace sw +{ + + +class DocumentListsManager : 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; + + // container to hold the lists of the text document + std::unordered_map<OUString, std::unique_ptr<SwList>> 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..97cc23131 --- /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 : 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..a9811827e --- /dev/null +++ b/sw/source/core/inc/DocumentRedlineManager.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_DOCUMENTREDLINEMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTREDLINEMANAGER_HXX + +#include <IDocumentRedlineAccess.hxx> +#include <memory> + +class SwDoc; + +namespace sw +{ + +class SAL_DLLPUBLIC_RTTI DocumentRedlineManager : 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 HasExtraRedlineTable() const 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 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; } + + virtual ~DocumentRedlineManager() override; + +private: + + DocumentRedlineManager(DocumentRedlineManager const&) = delete; + DocumentRedlineManager& operator=(DocumentRedlineManager const&) = delete; + + SwDoc& m_rDoc; + + RedlineFlags meRedlineFlags; //< Current Redline Mode. + std::unique_ptr<SwRedlineTable> mpRedlineTable; //< List of all Ranged Redlines. + std::unique_ptr<SwExtraRedlineTable> mpExtraRedlineTable; //< List of all Extra Redlines. + std::unique_ptr<OUString> mpAutoFormatRedlnComment; //< 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..958a2fcaa --- /dev/null +++ b/sw/source/core/inc/DocumentSettingManager.hxx @@ -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 . + */ +#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 : + 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 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; + +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; + + +// 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..e9b7d09bb --- /dev/null +++ b/sw/source/core/inc/DocumentStateManager.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_DOCUMENTSTATEMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTATEMANAGER_HXX + +#include <IDocumentState.hxx> + +class SwDoc; + + +namespace sw { + +class DocumentStateManager : 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. +}; + +} + +#endif + +/* 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..ab82a767a --- /dev/null +++ b/sw/source/core/inc/DocumentStatisticsManager.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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTATISTICSMANAGER_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTATISTICSMANAGER_HXX + +#include <IDocumentStatistics.hxx> +#include <SwDocIdle.hxx> +#include <memory> + +class SwDoc; +struct SwDocStat; + +namespace sw { + +class DocumentStatisticsManager : 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(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..721ecba22 --- /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 : 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..65346efdb --- /dev/null +++ b/sw/source/core/inc/DocumentTimerManager.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_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 : 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..a19470498 --- /dev/null +++ b/sw/source/core/inc/DropDownFormFieldButton.hxx @@ -0,0 +1,40 @@ +/* -*- 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_DROPDOWNFORMEFIELDBUTTO_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_DROPDOWNFORMEFIELDBUTTO_HXX + +#include "FormFieldButton.hxx" + +class SwEditWin; +class FloatingWindow; +namespace sw +{ +namespace 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 : public FormFieldButton +{ +public: + DropDownFormFieldButton(SwEditWin* pEditWin, sw::mark::DropDownFieldmark& rFieldMark); + virtual ~DropDownFormFieldButton() override; + + virtual void InitPopup() override; +}; + +#endif + +/* 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..987d86d15 --- /dev/null +++ b/sw/source/core/inc/FormFieldButton.hxx @@ -0,0 +1,56 @@ +/* -*- 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_FORMEFIELDBUTTO_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_FORMEFIELDBUTTO_HXX + +#include <vcl/menubtn.hxx> +#include <swrect.hxx> + +class SwEditWin; +class FloatingWindow; +namespace sw +{ +namespace mark +{ +class Fieldmark; +} +} // namespace sw + +/** + * This button is shown when the cursor is on a form field with drop-down capability. + */ +class FormFieldButton : public MenuButton +{ +public: + FormFieldButton(SwEditWin* pEditWin, sw::mark::Fieldmark& rFieldMark); + virtual ~FormFieldButton() override; + virtual void dispose() override; + + void CalcPosAndSize(const SwRect& rPortionPaintArea); + + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + DECL_LINK(FieldPopupModeEndHdl, FloatingWindow*, void); + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual WindowHitTest ImplHitTest(const Point& rFramePos) override; + + virtual void InitPopup() = 0; + +private: + tools::Rectangle m_aFieldFramePixel; + +protected: + sw::mark::Fieldmark& m_rFieldmark; + VclPtr<FloatingWindow> m_pFieldPopup; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/core/inc/GetMetricVal.hxx b/sw/source/core/inc/GetMetricVal.hxx new file mode 100644 index 000000000..9b17975d6 --- /dev/null +++ b/sw/source/core/inc/GetMetricVal.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_GETMETRICVAL_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_GETMETRICVAL_HXX + +#include <sal/types.h> + +#define CM_1 0 // 1 centimeter or 1/2 inch +#define CM_05 1 // 0.5 centimeter or 1/4 inch +#define CM_01 2 // 0.1 centimeter or 1/20 inch + +inline sal_uInt16 GetMetricVal( int n ) +{ + sal_uInt16 nVal = 567; // 1 cm + + if( CM_01 == n ) + nVal /= 10; + else if( CM_05 == n ) + nVal /= 2; + return nVal; +} + +#endif + +/* 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..a9457f916 --- /dev/null +++ b/sw/source/core/inc/MarkManager.hxx @@ -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 . + */ + +#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; + +namespace sw { + namespace mark { + typedef std::unordered_map<OUString, sal_Int32> MarkBasenameMapUniqueOffset_t; + + class FieldmarkWithDropDownButton; + + class MarkManager + : 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) 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 ::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* > getDropDownsFor(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 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; + + 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 * const m_pDoc; + + sw::mark::FieldmarkWithDropDownButton* m_pLastActiveFieldmark; + }; + } // namespace mark +} + +#endif + +/* 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..f37605556 --- /dev/null +++ b/sw/source/core/inc/SwGrammarMarkUp.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_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 : 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 */ + 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..2036b0a66 --- /dev/null +++ b/sw/source/core/inc/SwPortionHandler.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_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: + + SwPortionHandler() {} /// (empty) constructor + + 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..7ba6b1c34 --- /dev/null +++ b/sw/source/core/inc/SwUndoFmt.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 . + */ + +#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> + +class SwDoc; +class SwTextFormatColl; +class SwConditionTextFormatColl; +class SwRewriter; + +class SwUndoFormatCreate : public SwUndo +{ +protected: + SwFormat * m_pNew; + OUString m_sDerivedFrom; + SwDoc * m_pDoc; + mutable OUString m_sNewName; + SfxItemSet * m_pNewSet; + sal_uInt16 m_nId; // FormatId related + bool m_bAuto; + +public: + SwUndoFormatCreate(SwUndoId nUndoId, SwFormat * pNew, SwFormat const * pDerivedFrom, + SwDoc * pDoc); + 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_pDoc; + OUString m_sOldName; + SfxItemSet m_aOldSet; + sal_uInt16 m_nId; // FormatId related + bool m_bAuto; + +public: + SwUndoFormatDelete(SwUndoId nUndoId, SwFormat const * pOld, SwDoc * pDoc); + 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_pDoc; + +public: + SwUndoRenameFormat(SwUndoId nUndoId, const OUString & sOldName, + const OUString & sNewName, + SwDoc * pDoc); + 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 * pDoc); + + 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 * pDoc); + + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; + virtual void Delete(SwFormat * pFormat) override; + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoCondTextFormatCollCreate : public SwUndoTextFormatCollCreate +{ +public: + SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl * pNew, SwTextFormatColl const * pDerivedFrom, SwDoc * pDoc); + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; +}; + +class SwUndoCondTextFormatCollDelete : public SwUndoTextFormatCollDelete +{ +public: + SwUndoCondTextFormatCollDelete(SwTextFormatColl const * pOld, SwDoc * pDoc); + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; +}; + +class SwUndoRenameFormatColl : public SwUndoRenameFormat +{ +public: + SwUndoRenameFormatColl(const OUString & sOldName, + const OUString & sNewName, + SwDoc * pDoc); + + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoCharFormatCreate : public SwUndoFormatCreate +{ +public: + SwUndoCharFormatCreate(SwCharFormat * pNew, SwCharFormat const * pDerivedFrom, + SwDoc * pDoc); + + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; + virtual void Delete() override; + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoCharFormatDelete : public SwUndoFormatDelete +{ +public: + SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc * pDoc); + + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; + virtual void Delete(SwFormat * pFormat) override; + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoRenameCharFormat : public SwUndoRenameFormat +{ +public: + SwUndoRenameCharFormat(const OUString & sOldName, + const OUString & sNewName, + SwDoc * pDoc); + + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoFrameFormatCreate : public SwUndoFormatCreate +{ +public: + SwUndoFrameFormatCreate(SwFrameFormat * pNew, SwFrameFormat const * pDerivedFrom, + SwDoc * pDoc); + + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; + virtual void Delete() override; + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoFrameFormatDelete : public SwUndoFormatDelete +{ +public: + SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc * pDoc); + + virtual SwFormat * Create(SwFormat * pDerivedFrom) override; + virtual void Delete(SwFormat * pFormat) override; + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoRenameFrameFormat : public SwUndoRenameFormat +{ +public: + SwUndoRenameFrameFormat(const OUString & sOldName, + const OUString & sNewName, + SwDoc * pDoc); + + virtual SwFormat * Find(const OUString & rName) const override; +}; + +class SwUndoNumruleCreate : public SwUndo +{ + const SwNumRule * m_pNew; + mutable SwNumRule m_aNew; + SwDoc * m_pDoc; + mutable bool m_bInitialized; + +public: + SwUndoNumruleCreate(const SwNumRule * pNew, SwDoc * pDoc); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + SwRewriter GetRewriter() const override; +}; + +class SwUndoNumruleDelete : public SwUndo +{ + SwNumRule m_aOld; + SwDoc * m_pDoc; + +public: + SwUndoNumruleDelete(const SwNumRule & aRule, SwDoc * pDoc); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + SwRewriter GetRewriter() const override; +}; + +class SwUndoNumruleRename : public SwUndo +{ + OUString m_aOldName, m_aNewName; + SwDoc * m_pDoc; + + public: + SwUndoNumruleRename(const OUString & aOldName, const OUString & aNewName, + SwDoc * pDoc); + + 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..acdaed7cc --- /dev/null +++ b/sw/source/core/inc/SwUndoPageDesc.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_SWUNDOPAGEDESC_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWUNDOPAGEDESC_HXX + +#include <undobj.hxx> +#include <pagedesc.hxx> + +class SwDoc; + +class SwUndoPageDesc : 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 ); + +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 : 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 : 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..a593bfd6d --- /dev/null +++ b/sw/source/core/inc/SwUndoTOXChange.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_SWUNDOTOXCHANGE_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWUNDOTOXCHANGE_HXX + +#include <undobj.hxx> +#include <tox.hxx> + +class SwDoc; +class SwTOXBaseSection; + +class SwUndoTOXChange : public SwUndo +{ +private: + SwTOXBase m_Old; + SwTOXBase m_New; + + sal_uLong const m_nNodeIndex; + +public: + SwUndoTOXChange(const SwDoc* pDoc, 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; +}; + +#endif // INCLUDED_SW_SOURCE_CORE_INC_SWUNDOTOXCHANGE_HXX + +/* 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..d858cfecf --- /dev/null +++ b/sw/source/core/inc/SwXMLBlockExport.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWXMLBLOCKEXPORT_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWXMLBLOCKEXPORT_HXX + +#include <xmloff/xmlexp.hxx> + +class SwXMLTextBlocks; + +class SwXMLBlockListExport : public SvXMLExport +{ +private: + SwXMLTextBlocks &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 : public SvXMLExport +{ +private: + SwXMLTextBlocks &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(const OUString & rText); + void ExportAutoStyles_() override {} + void ExportMasterStyles_ () override {} + void ExportContent_() override {} +}; + +#endif + +/* 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..d58ce4bf1 --- /dev/null +++ b/sw/source/core/inc/SwXMLBlockImport.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_SWXMLBLOCKIMPORT_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWXMLBLOCKIMPORT_HXX + +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <com/sun/star/xml/sax/FastToken.hpp> +#include <sax/fastattribs.hxx> +#include <cppuhelper/implbase.hxx> + +namespace com::sun::star::xml::sax { class XFastTokenHandler; } + +using namespace css::xml::sax; +using namespace xmloff::token; + +class SwXMLTextBlocks; +class SwXMLBlockListImport : public SvXMLImport +{ +private: + SwXMLTextBlocks &rBlockList; + +protected: + // 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 rBlockList; + } + virtual ~SwXMLBlockListImport() + throw() override; +}; + +class SwXMLTextBlockImport : public SvXMLImport +{ +protected: + // 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 bTextOnly; + OUString &m_rText; + SwXMLTextBlockImport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + OUString &rNewText, bool bNewTextOnly ); + + virtual ~SwXMLTextBlockImport() + throw() 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 : + 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 : + 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; +}; + +#endif + +/* 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..3d1feb1ec --- /dev/null +++ b/sw/source/core/inc/SwXMLTextBlocks.hxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_SWXMLTEXTBLOCKS_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWXMLTEXTBLOCKS_HXX + +#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 xDocShellRef; + SwXmlFlags nFlags; + OUString aPackageName; + tools::SvRef<SfxMedium> xMedium; + + void ReadInfo(); + void WriteInfo(); + void InitBlockMode ( const css::uno::Reference < css::embed::XStorage >& rStorage ); + void ResetBlockMode(); + +public: + css::uno::Reference < css::embed::XStorage > xBlkRoot; + css::uno::Reference < css::embed::XStorage > 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 ( const OUString& rShort ); + virtual ~SwXMLTextBlocks() override; + virtual ErrCode Delete( sal_uInt16 ) override; + virtual ErrCode Rename( sal_uInt16, const OUString& ) 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( const OUString& 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( const OUString& rShort, OUString& rText ); + ErrCode PutBlockText( const OUString& rShort, const OUString& rText, const OUString& rPackageName ); + void MakeBlockText( const OUString& rText ); +}; + +#endif + +/* 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..f989600c5 --- /dev/null +++ b/sw/source/core/inc/SwXTextDefaults.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWXTEXTDEFAULTS_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_SWXTEXTDEFAULTS_HXX + +#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 : 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; +}; +#endif + +/* 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..cc73197fd --- /dev/null +++ b/sw/source/core/inc/UndoAttribute.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_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 <set> + +class SvxTabStopItem; +class SwFormat; +class SwFootnoteInfo; +class SwEndNoteInfo; +class SwDoc; + +class SwUndoAttr : 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; + sal_uLong 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 : public SwUndo, private SwUndRng +{ + const std::unique_ptr<SwHistory> m_pHistory; + std::set<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( const std::set<sal_uInt16> &rAttrs ); + + SwHistory& GetHistory() { return *m_pHistory; } +}; + +class SwUndoFormatAttr : public SwUndo +{ + friend class SwUndoDefaultAttr; + OUString m_sFormatName; + std::unique_ptr<SfxItemSet> m_pOldSet; // old attributes + sal_uLong 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( const 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 : 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 : public SwUndo +{ + const sal_uLong 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 : public SwClient +{ + std::unique_ptr<SwUndoFormatAttr> m_pUndo; + const bool m_bSaveDrawPt; + +public: + SwUndoFormatAttrHelper( SwFormat& rFormat, bool bSaveDrawPt = true ); + + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 : 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 : public SwUndo +{ + std::unique_ptr<SfxItemSet> m_pOldSet; // the old attributes + std::unique_ptr<SvxTabStopItem> m_pTabStop; + +public: + // registers at the format and saves old attributes + SwUndoDefaultAttr( const SfxItemSet& rOldSet, const SwDoc* pDoc ); + + virtual ~SwUndoDefaultAttr() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +class SwUndoChangeFootNote : 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 : public SwUndo +{ + std::unique_ptr<SwFootnoteInfo> m_pFootNoteInfo; + +public: + SwUndoFootNoteInfo( const SwFootnoteInfo &rInfo, const SwDoc* pDoc ); + + virtual ~SwUndoFootNoteInfo() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +class SwUndoEndNoteInfo : public SwUndo +{ + std::unique_ptr<SwEndNoteInfo> m_pEndNoteInfo; + +public: + SwUndoEndNoteInfo( const SwEndNoteInfo &rInfo, const SwDoc* pDoc ); + + 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..ebaac8cd1 --- /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> + +class SwHistoryBookmark; +class SwHistoryNoTextFieldmark; +class SwHistoryTextFieldmark; + +namespace sw { + namespace 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 : public SwUndoBookmark +{ +public: + SwUndoInsBookmark( const ::sw::mark::IMark& ); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +class SwUndoDeleteBookmark : public SwUndoBookmark +{ +public: + SwUndoDeleteBookmark( const ::sw::mark::IMark& ); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; +}; + +class SwUndoRenameBookmark : public SwUndo +{ + const OUString m_sOldName; + const OUString m_sNewName; + +public: + SwUndoRenameBookmark( const OUString& rOldName, const OUString& rNewName, const SwDoc* pDoc ); + 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 : 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 : 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 : 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 : 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..a86e30c83 --- /dev/null +++ b/sw/source/core/inc/UndoCore.hxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <o3tl/deleter.hxx> +#include <rtl/ustring.hxx> +#include <redline.hxx> + +#include <memory> +#include <vector> + +class SfxItemSet; +class SwFormatColl; +class SwFormatAnchor; +class SdrMarkList; +class SwUndoDelete; + +namespace sw { + class UndoManager; + class IShellCursorSupplier; +} + +class SwRedlineSaveData: 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; +#endif +}; + +class SwRedlineSaveDatas { +private: + std::vector<std::unique_ptr<SwRedlineSaveData, o3tl::default_delete<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, o3tl::default_delete<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 + : 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; + } + +private: + SwDoc & m_rDoc; + IShellCursorSupplier & m_rCursorSupplier; + SwFrameFormat * m_pSelFormat; + SdrMarkList * m_pMarkList; +}; + +class RepeatContext + : 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 : 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 : public SwUndo, public SwClient +{ + SwFrameFormat* m_pFrameFormat; // saved FlyFormat + const OUString m_DerivedFromFormatName; + const OUString m_NewFormatName; + std::unique_ptr<SfxItemSet> m_pItemSet; // the re-/ set attributes + sal_uLong 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 Modify( const SfxPoolItem*, const SfxPoolItem* ) override; + void GetAnchor( SwFormatAnchor& rAnhor, sal_uLong 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 : 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; +}; + +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 + + 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); + +#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..8a0bdabb5 --- /dev/null +++ b/sw/source/core/inc/UndoDelete.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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; + +namespace sfx2 { + class MetadatableUndo; +} + +class SwUndoDelete + : 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; + + sal_uLong m_nNode; + sal_uLong m_nNdDiff; // difference of Nodes before/after Delete + sal_uLong m_nSectDiff; // diff. of Nodes before/after Move w/ SectionNodes + sal_uLong 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 + + bool SaveContent( const SwPosition* pStt, const SwPosition* pEnd, + SwTextNode* pSttTextNd, SwTextNode* pEndTextNd ); + +public: + SwUndoDelete( + SwPaM&, + 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; } + +}; + +#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..ee8f37ef2 --- /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 : 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* pDoc ); + + virtual ~SwSdrUndo() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + virtual OUString GetComment() const override; +}; + +class SwUndoDrawGroup : public SwUndo +{ + std::unique_ptr<SwUndoGroupObjImpl[]> m_pObjArray; + sal_uInt16 m_nSize; + bool m_bDeleteFormat; + +public: + SwUndoDrawGroup( sal_uInt16 nCnt, const SwDoc* pDoc ); + + 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 : public SwUndo +{ + std::unique_ptr<SwUndoGroupObjImpl[]> m_pObjArray; + sal_uInt16 m_nSize; + bool m_bDeleteFormat; + +public: + SwUndoDrawUnGroup( SdrObjGroup*, const SwDoc* pDoc ); + + virtual ~SwUndoDrawUnGroup() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + void AddObj( sal_uInt16 nPos, SwDrawFrameFormat* ); +}; + +class SwUndoDrawUnGroupConnectToLayout : public SwUndo +{ +private: + std::vector< std::pair< SwDrawFrameFormat*, SdrObject* > > m_aDrawFormatsAndObjs; + +public: + SwUndoDrawUnGroupConnectToLayout(const SwDoc* pDoc); + + virtual ~SwUndoDrawUnGroupConnectToLayout() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + void AddFormatAndObj( SwDrawFrameFormat* pDrawFrameFormat, + SdrObject* pDrawObject ); +}; + +class SwUndoDrawDelete : 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* pDoc ); + + virtual ~SwUndoDrawDelete() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + void AddObj( SwDrawFrameFormat*, const SdrMark& ); +}; + +#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..801997d65 --- /dev/null +++ b/sw/source/core/inc/UndoInsert.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_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; + +class SwUndoInsert: 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; + sal_uLong 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; } +}; + +SwRewriter +MakeUndoReplaceRewriter(sal_uLong const occurrences, + OUString const& sOld, OUString const& sNew); + +class SwUndoReplace + : 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 : public SwUndo +{ + std::unique_ptr<Graphic> mpGraphic; + std::optional<OUString> maNm; + std::optional<OUString> maFltr; + sal_uLong 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 : public SwUndo +{ + union { + struct { + // for NoTextFrames + SwUndoInsLayFormat* pUndoFly; + SwUndoFormatAttr* pUndoAttr; + } OBJECT; + struct { + // for tables or TextFrames + SwUndoDelete* pUndoInsNd; + sal_uLong 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( sal_uLong 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..fda9c734a --- /dev/null +++ b/sw/source/core/inc/UndoManager.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_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 SAL_DLLPUBLIC_RTTI UndoManager + : 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; + + // SfxUndoManager + virtual void AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, + bool bTryMerg = false) override; + virtual bool Undo() override; + virtual bool Redo() override; + + SwUndo * RemoveLastUndo(); + SwUndo * GetLastUndo(); + + SwNodes const& GetUndoNodes() const; + SwNodes & GetUndoNodes(); + void SetDocShell(SwDocShell* pDocShell); + +protected: + virtual void EmptyActionsChanged() override; + +private: + 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); + + // 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..b83a78ec4 --- /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 : 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* pDoc, 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 : public SwUndo, private SwUndRng +{ + struct NodeLevel + { + sal_uLong index; + int level; + NodeLevel(sal_uLong 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 : public SwUndo, private SwUndRng +{ + sal_uLong nNewStt; + long nOffset; + +public: + SwUndoMoveNum( const SwPaM& rPam, long nOffset, bool bIsOutlMv ); + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + virtual void RepeatImpl( ::sw::RepeatContext & ) override; + + void SetStartNode( sal_uLong nValue ) { nNewStt = nValue; } +}; + +class SwUndoNumUpDown : public SwUndo, private SwUndRng +{ + short 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 : public SwUndo +{ + sal_uLong nIdx; + 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 : public SwUndo +{ + sal_uLong nIdx; + sal_uInt16 nOldStt, nNewStt; + bool bSetSttValue : 1; + bool 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..1a21a87a8 --- /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: public SwUndo, private SwUndoSaveContent +{ + OUString aDelStr, aInsStr; + std::unique_ptr<SwRedlineSaveDatas> pRedlSaveData; + sal_uLong nSttNode; + sal_Int32 nSttContent; + bool bInsChar : 1; // no Overwrite, but Insert + bool 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 : public SwUndo, public SwUndRng +{ + std::vector< std::unique_ptr<UndoTransliterate_Data> > aChanges; + TransliterationFlags 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 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..1688e2872 --- /dev/null +++ b/sw/source/core/inc/UndoRedline.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOREDLINE_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_UNDOREDLINE_HXX + +#include <memory> +#include <undobj.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 : public SwUndoRedline +{ + bool m_bCanGroup : 1; + bool m_bIsDelim : 1; + bool m_bIsBackspace : 1; + + OUString m_sRedlineText; + + virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; + virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; + +public: + SwUndoRedlineDelete( const SwPaM& rRange, SwUndoId nUserId ); + virtual SwRewriter GetRewriter() const override; + + bool CanGrouping( const SwUndoRedlineDelete& rPrev ); + + // SwUndoTableCpyTable needs this information: + long NodeDiff() const { return m_nSttNode - m_nEndNode; } + sal_Int32 ContentStart() const { return m_nSttContent; } + + void SetRedlineText(const OUString & rText); +}; + +class SwUndoRedlineSort : public SwUndoRedline +{ + std::unique_ptr<SwSortOptions> m_pOpt; + sal_uLong 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 : public SwUndoRedline +{ +private: + virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; + +public: + SwUndoAcceptRedline( const SwPaM& rRange ); + + virtual void RepeatImpl( ::sw::RepeatContext & ) override; +}; + +class SwUndoRejectRedline : public SwUndoRedline +{ +private: + virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override; + +public: + SwUndoRejectRedline( const SwPaM& rRange ); + + virtual void RepeatImpl( ::sw::RepeatContext & ) override; +}; + +class SwUndoCompDoc : 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..ac7858063 --- /dev/null +++ b/sw/source/core/inc/UndoSection.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX + +#include <o3tl/deleter.hxx> +#include <undobj.hxx> +#include <memory> + +class SfxItemSet; +class SwTextNode; +class SwSectionData; +class SwSectionFormat; +class SwTOXBase; + +namespace sw { + enum class RedlineMode; +}; + +class SwUndoInsSection : public SwUndo, private SwUndRng +{ +private: + const std::unique_ptr<SwSectionData> m_pSectionData; + const std::unique_ptr<std::pair<SwTOXBase *, sw::RedlineMode>> m_pTOXBase; /// 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; + sal_uLong m_nSectionNodePos; + bool m_bSplitAtStart : 1; + bool m_bSplitAtEnd : 1; + bool m_bUpdateFootnote : 1; + + void Join( SwDoc& rDoc, sal_uLong nNode ); + +public: + SwUndoInsSection(SwPaM const&, SwSectionData const&, + SfxItemSet const* pSet, + std::pair<SwTOXBase const*, sw::RedlineMode> 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(sal_uLong 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 : 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; + sal_uLong 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..27f97ceaf --- /dev/null +++ b/sw/source/core/inc/UndoSort.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <memory> +#include <vector> + +struct SwSortOptions; +class SwTableNode; +class SwUndoAttrTable; + +struct SwSortUndoElement +{ + union { + struct { + sal_uLong nID; + sal_uLong 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( sal_uLong nS, sal_uLong nT ) + { + SORT_TXT_TBL.TXT.nSource = nS; + SORT_TXT_TBL.TXT.nTarget = nT; + SORT_TXT_TBL.TXT.nID = 0xffffffff; + } + ~SwSortUndoElement(); +}; + +class SwUndoSort : public SwUndo, private SwUndRng +{ + std::unique_ptr<SwSortOptions> pSortOpt; + std::vector<std::unique_ptr<SwSortUndoElement>> m_SortList; + std::unique_ptr<SwUndoAttrTable> pUndoTableAttr; + sal_uLong nTableNd; + +public: + SwUndoSort( const SwPaM&, const SwSortOptions& ); + SwUndoSort( sal_uLong nStt, sal_uLong 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( sal_uLong nOrgPos, sal_uLong 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..ad779ee44 --- /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: public SwUndo +{ + std::unique_ptr<SwHistory> m_pHistory; + std::unique_ptr<SwRedlineData> pRedlData; + sal_uLong nNode; + sal_Int32 nContent; + bool bTableFlag : 1; + bool bChkTableStt : 1; + sal_uInt32 nParRsid; + +public: + SwUndoSplitNode( SwDoc* pDoc, 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() { bTableFlag = true; } +}; + +class SwUndoMove : 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 + sal_uLong 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* pDoc, 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; } + sal_uLong GetEndNode() const { return m_nEndNode; } + sal_uLong 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..88c3b10db --- /dev/null +++ b/sw/source/core/inc/UndoTable.hxx @@ -0,0 +1,418 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <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 SwUndoInsTable : 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; + sal_uLong 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 : public SwUndo, public SwUndRng +{ + OUString m_sTableName; + SwInsertTableOptions m_aInsertTableOpts; + std::vector<sal_uLong> 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 : 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; + sal_uLong 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, sal_uLong nNdIdx, sal_uLong nEndIdx, + sal_Int32 nContentIdx = SAL_MAX_INT32); +}; + +class SwUndoAttrTable : public SwUndo +{ + sal_uLong 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 : public SwUndo +{ + OUString m_TableStyleName; + sal_uLong 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 : public SwUndo +{ + std::unique_ptr<SaveTable> m_pSaveTable; + std::set<sal_uLong> m_Boxes; + struct BoxMove + { + sal_uLong index; ///< Index of this box. + bool hasMoved; ///< Has this box been moved already. + BoxMove(sal_uLong idx, bool moved=false) : index(idx), hasMoved(moved) {}; + bool operator<(const BoxMove& other) const { return index < other.index; }; + }; + std::unique_ptr< std::set<BoxMove> > m_pNewSttNds; + std::unique_ptr<SwUndoSaveSections> m_pDelSects; + long m_nMin, m_nMax; // for redo of delete column + sal_uLong 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, + long nMn, 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<sal_uLong> &rNodeCnts ); + void SaveSection( SwStartNode* pSttNd ); + void ReNewBoxes( const SwSelBoxes& rBoxes ); + +}; + +class SwUndoMove; + +class SwUndoTableMerge : public SwUndo, private SwUndRng +{ + sal_uLong m_nTableNode; + std::unique_ptr<SaveTable> m_pSaveTable; + std::set<sal_uLong> m_Boxes; + std::vector<sal_uLong> 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* pDoc, SwNodeRange& rRg, SwNodeIndex& rPos ); + + void SetSelBoxes( const SwSelBoxes& rBoxes ); + + void AddNewBox( sal_uLong nSttNdIdx ) + { m_aNewStartNodes.push_back( nSttNdIdx ); } + + void SaveCollection( const SwTableBox& rBox ); +}; + +class SwUndoTableNumFormat : 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; + sal_uLong m_nNode; + sal_uLong 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 : 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, + const SwPosition& rPos, bool& rJoin, bool bRedo ); + +public: + SwUndoTableCpyTable(const SwDoc* pDoc); + + 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 ); +}; + +class SwUndoCpyTable : public SwUndo +{ + std::unique_ptr<SwUndoDelete> m_pDelete; + sal_uLong m_nTableNode; + +public: + SwUndoCpyTable(const SwDoc* pDoc); + + virtual ~SwUndoCpyTable() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + void SetTableSttIdx( sal_uLong nIdx ) { m_nTableNode = nIdx; } +}; + +class SwUndoSplitTable : public SwUndo +{ + sal_uLong 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( sal_uLong nIdx ) { m_nOffset = nIdx - m_nTableNode; } + SwHistory* GetHistory() { return m_pHistory.get(); } + void SaveFormula( SwHistory& rHistory ); +}; + +class SwUndoMergeTable : public SwUndo +{ + OUString m_aName; + sal_uLong 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 : public SwUndo +{ + sal_uLong 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 : public SwUndo +{ + OUString m_sName; + std::unique_ptr<SwTableAutoFormat> m_pAutoFormat; +public: + SwUndoTableStyleMake(const OUString& rName, const SwDoc* pDoc); + + virtual ~SwUndoTableStyleMake() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + virtual SwRewriter GetRewriter() const override; +}; + +class SwUndoTableStyleDelete : public SwUndo +{ + std::unique_ptr<SwTableAutoFormat> m_pAutoFormat; + std::vector<SwTable*> m_rAffectedTables; +public: + SwUndoTableStyleDelete(std::unique_ptr<SwTableAutoFormat> pAutoFormat, const std::vector<SwTable*>& rAffectedTables, const SwDoc* pDoc); + + virtual ~SwUndoTableStyleDelete() override; + + virtual void UndoImpl( ::sw::UndoRedoContext & ) override; + virtual void RedoImpl( ::sw::UndoRedoContext & ) override; + + virtual SwRewriter GetRewriter() const override; +}; + +class SwUndoTableStyleUpdate : public SwUndo +{ + std::unique_ptr<SwTableAutoFormat> m_pOldFormat, m_pNewFormat; +public: + SwUndoTableStyleUpdate(const SwTableAutoFormat& rNewFormat, const SwTableAutoFormat& rOldFormat, const SwDoc* pDoc); + + 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..1ab8f9411 --- /dev/null +++ b/sw/source/core/inc/acorrect.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_ACORRECT_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_ACORRECT_HXX + +#include <memory> + +#include <svl/itemset.hxx> +#include <tools/solar.h> +#include <editeng/svxacorr.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 : 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 ) 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; + ACFlags m_nFlags; + sal_uLong m_nNode; + sal_Int32 m_nContent; + sal_Unicode m_cChar; + LanguageType m_eLanguage; + bool m_bDeleted; + +public: + SwAutoCorrExceptWord(ACFlags nAFlags, sal_uLong nNd, sal_Int32 nContent, + const OUString& rWord, sal_Unicode cChr, + LanguageType eLang) + : m_sWord(rWord), m_nFlags(nAFlags), m_nNode(nNd), 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..b2afe2c47 --- /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" + +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; + + // does the object represents a Writer fly frame + bool mbIsObjFly; + // #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; + // #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 sal_uLong _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..9fe8478e2 --- /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 "bookmrk.hxx" +#include <rtl/ustring.hxx> + +class SwFormatField; + +namespace sw::mark +{ + class AnnotationMark : public MarkBase + { + public: + AnnotationMark( + const SwPaM& rPaM, + const OUString& rName ); + + virtual ~AnnotationMark() override; + + virtual void InitDoc(SwDoc* const 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..6bafb73d1 --- /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 : 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..8a3e86c5f --- /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 : 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..f6b046e9d --- /dev/null +++ b/sw/source/core/inc/bodyfrm.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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: public SwLayoutFrame +{ +protected: + 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/bookmrk.hxx b/sw/source/core/inc/bookmrk.hxx new file mode 100644 index 000000000..27e4171be --- /dev/null +++ b/sw/source/core/inc/bookmrk.hxx @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_BOOKMRK_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_BOOKMRK_HXX + +#include <cppuhelper/weakref.hxx> +#include <sfx2/Metadatable.hxx> +#include <vcl/keycod.hxx> +#include <memory> +#include <rtl/ustring.hxx> +#include <osl/diagnose.h> +#include <tools/ref.hxx> +#include <IMark.hxx> +#include <swrect.hxx> +#include "FormFieldButton.hxx" + +namespace com { + namespace sun { + namespace star { + namespace text { + class XTextContent; + } + } + } +} + +class SwDoc; +class SwEditWin; +class SwServerObject; +class SvNumberFormatter; + +namespace sw { + namespace 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* const, 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 Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) 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(const OUString& rPrefix); + + css::uno::WeakReference< css::text::XTextContent> m_wXBookmark; + }; + + class NavigatorReminder + : public MarkBase + { + public: + NavigatorReminder(const SwPaM& rPaM); + }; + + class UnoMark + : 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* const pDoc); + 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* const io_Doc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override; + + virtual void DeregisterFromDoc(SwDoc* const io_pDoc) 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* const) = 0; + + void SetMarkStartPos( const SwPosition& rNewStartPos ); + void SetMarkEndPos( const SwPosition& rNewEndPos ); + + 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 + : public Fieldmark + { + public: + TextFieldmark(const SwPaM& rPaM, const OUString& rName); + virtual void InitDoc(SwDoc* const io_pDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override; + virtual void ReleaseDoc(SwDoc* const pDoc) 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* const io_pDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override; + virtual void ReleaseDoc(SwDoc* const pDoc) override; + }; + + /// Fieldmark representing a checkbox form field. + class CheckboxFieldmark + : 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 HideButton(); + virtual void RemoveButton(); + + protected: + VclPtr<FormFieldButton> m_pButton; + }; + + /// Fieldmark representing a drop-down form field. + class DropDownFieldmark + : public FieldmarkWithDropDownButton + { + public: + DropDownFieldmark(const SwPaM& rPaM); + virtual ~DropDownFieldmark() override; + + virtual void ShowButton(SwEditWin* pEditWin) override; + virtual void HideButton() 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 SendLOKMessage(const OString& sAction); + + private: + SwRect m_aPortionPaintArea; + OString m_sLastSentLOKMsg; + }; + + /// Fieldmark representing a date form field. + class DateFieldmark + : virtual public IDateFieldmark + , public FieldmarkWithDropDownButton + { + public: + DateFieldmark(const SwPaM& rPaM); + virtual ~DateFieldmark() override; + + virtual void InitDoc(SwDoc* const io_pDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override; + virtual void ReleaseDoc(SwDoc* const pDoc) 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); + } +} +#endif + +/* 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..e98654ae4 --- /dev/null +++ b/sw/source/core/inc/cellfrm.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_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: public SwLayoutFrame +{ + const SwTableBox* m_pTabBox; + + virtual void DestroyImpl() override; + virtual ~SwCellFrame() override; + +protected: + virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; + +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; + long GetLayoutRowSpan() 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..2daec8fdd --- /dev/null +++ b/sw/source/core/inc/cntfrm.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_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* ); + +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*, sal_uInt8 &, + 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 Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 MoveFootnoteCntFwd( bool, SwFootnoteBossFrame* ); // called by MoveFwd if content + + inline SwContentFrame* GetNextContentFrame() const; + inline SwContentFrame* GetPrevContentFrame() const; + static bool CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave, + 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..7c78661e7 --- /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: 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/crossrefbookmark.hxx b/sw/source/core/inc/crossrefbookmark.hxx new file mode 100644 index 000000000..06116bdda --- /dev/null +++ b/sw/source/core/inc/crossrefbookmark.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_CROSSREFBOOKMARK_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_CROSSREFBOOKMARK_HXX + +#include "bookmrk.hxx" +#include <rtl/ustring.hxx> +#include <vcl/keycod.hxx> + +namespace sw { + namespace mark { + class CrossRefBookmark + : public Bookmark + { + public: + CrossRefBookmark(const SwPaM& rPaM, + const vcl::KeyCode& rCode, + const OUString& rName, + const OUString& 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 + : public CrossRefBookmark + { + public: + CrossRefHeadingBookmark(const SwPaM& rPaM, + const vcl::KeyCode& rCode, + const OUString& rName); + static bool IsLegalName(const OUString& rName); + }; + + class CrossRefNumItemBookmark + : public CrossRefBookmark + { + public: + CrossRefNumItemBookmark(const SwPaM& rPaM, + const vcl::KeyCode& rCode, + const OUString& rName); + static bool IsLegalName(const OUString& rName); + }; + } +} + +#endif + +/* 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..4f7348b3b --- /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 nRecord; + static SwImplProtocol* pImpl; + static bool Start() { return bool( PROT::Init & nRecord ); } + +public: + static PROT Record() { return nRecord; } + static void SetRecord( PROT nNew ) { nRecord = nNew; } + static bool Record( PROT nFunc ) { return bool(( nFunc | PROT::Init ) & 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..8da47dafd --- /dev/null +++ b/sw/source/core/inc/dflyobj.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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; + +const sal_uInt16 SwFlyDrawObjIdentifier = 0x0001; + +// DrawObjects for Flys +class SwFlyDrawObj : public SdrObject +{ +private: + virtual std::unique_ptr<sdr::properties::BaseProperties> CreateObjectSpecificProperties() override; + bool mbIsTextBox; + +protected: + // #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 sal_uInt16 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). +class SwVirtFlyDrawObj : public SdrVirtObj +{ +private: + SwFlyFrame *m_pFlyFrame; + + // RotGrfFlyFrame: Helper to access the rotation angle (in 10th degrees, left-handed) + // of a GraphicFrame + sal_uInt16 getPossibleRotationFromFraphicFrame(Size& rSize) const; + +protected: + // 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 long 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, long 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..13ba7c8a8 --- /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..36cf3d86e --- /dev/null +++ b/sw/source/core/inc/docfld.hxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <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 +{ + sal_uLong m_nNode; + sal_Int32 m_nContent; + union { + const SwTextField* pTextField; + const SwSection* pSection; + const SwPosition* pPos; + const SwTextTOXMark* pTextTOX; + const SwTableBox* pTBox; + const SwTextINetFormat* pTextINet; + const SwFlyFrameFormat* pFlyFormat; + } m_CNTNT; + enum SetGetExpFieldType + { + TEXTFIELD, TEXTTOXMARK, SECTIONNODE, CRSRPOS, TABLEBOX, + TEXTINET, FLYFRAME + } m_eSetGetExpFieldType; + +public: + SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextField* pField = nullptr, + const SwIndex* pIdx = nullptr ); + + SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextINetFormat& rINet ); + + SetGetExpField( const SwSectionNode& rSectNode, + const SwPosition* pPos = nullptr ); + + 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; } + const SwTextINetFormat* GetINetFormat() const + { return TEXTINET == m_eSetGetExpFieldType ? m_CNTNT.pTextINet : nullptr; } + const SwFlyFrameFormat* GetFlyFormat() const + { return FLYFRAME == m_eSetGetExpFieldType ? m_CNTNT.pFlyFormat : nullptr; } + + sal_uLong 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 : public SwHash +{ + OUString aSetStr; + HashStr( const OUString& rName, const OUString& rText, HashStr* ); +}; + +struct SwCalcFieldType : 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; + + sal_uLong 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 ); + void GetBodyNode( const SwSectionNode&); + +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..1a65c3540 --- /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..a660ba4cb --- /dev/null +++ b/sw/source/core/inc/docsort.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_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 { + namespace sun { + namespace star { + namespace 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 OUString* pLastAlgorithm; + 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(const OUString& rStr); +private: + int keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const; +}; + +// sort text +struct SwSortTextElement : public SwSortElement +{ + sal_uLong nOrg; + SwNodeIndex aPos; + + SwSortTextElement( const SwNodeIndex& rPos ); + + virtual OUString GetKey( sal_uInt16 nKey ) const override; +}; + +// sort table +struct SwSortBoxElement : 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 bSym; } + sal_uInt16 GetRows() const { return nRows; } + sal_uInt16 GetCols() const { return 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* pDoc; + std::unique_ptr<FndBox_ const *[]> pArr; + std::vector<std::unique_ptr<SfxItemSet>> ppItemSets; + + sal_uInt16 nRows; + sal_uInt16 nCols; + sal_uInt16 nRow; + sal_uInt16 nCol; + + bool bSym; +}; + +inline bool FlatFndBox::HasItemSets() const { return !ppItemSets.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..5c005b37f --- /dev/null +++ b/sw/source/core/inc/doctxm.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <tox.hxx> +#include <section.hxx> + +class SwTOXInternational; +class SwPageDesc; +class SwTextNode; +class SwTextFormatColl; +struct SwPosition; +struct SwTOXSortTabBase; + +class SwTOXBaseSection : 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 ); + +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; +}; + +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..410943c98 --- /dev/null +++ b/sw/source/core/inc/drawfont.hxx @@ -0,0 +1,597 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 "TextFrameIndex.hxx" +#include <swdllapi.h> + +class SwTextFrame; +class SwViewShell; +class SwScriptInfo; +namespace sw { class WrongListIterator; } +class SwFont; +namespace vcl { + class Font; + 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::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; + 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; + long m_nSperren; + long m_nSpace; + 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_bSperr : 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::TextLayoutCache const*const pCachedVclData = nullptr) + : m_pCachedVclData(pCachedVclData) + { + 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_nSperren = 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_bSperr = 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::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; + } + + 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; + } + + long GetSperren() const + { +#ifdef DBG_UTIL + OSL_ENSURE( m_bSperr, "DrawTextInfo: Undefined >Sperren<" ); +#endif + return m_nSperren; + } + + long GetKern() const + { + return m_nKern; + } + + 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 ) + { + m_aText = rNew; + m_pCachedVclData = nullptr; // would any case benefit from save/restore? + } + + 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) + { + m_nIdx = nNew; + } + + void SetLen(TextFrameIndex const nNew) + { + m_nLen = nNew; + } + + void SetOffset( sal_Int32 nNew ) + { + m_nOfst = nNew; +#ifdef DBG_UTIL + m_bOfst = true; +#endif + } + + void SetKanaDiff( 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( long nNew ) + { + m_nKern = nNew; + } + + void SetSpace( long nNew ) + { + if( nNew < 0 ) + { + m_nSperren = -nNew; + m_nSpace = 0; + } + else + { + m_nSpace = nNew; + m_nSperren = 0; + } +#ifdef DBG_UTIL + m_bSpace = true; + m_bSperr = 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( sal_uInt16 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/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..2251efa48 --- /dev/null +++ b/sw/source/core/inc/dview.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_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 : 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; + +protected: + // 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; + + // support enhanced text edit for draw objects + virtual SdrUndoManager* getSdrUndoManagerForEnhancedTextEdit() const 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# + bool IsAntiAliasing() const; + + // 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..aa3d5a2a5 --- /dev/null +++ b/sw/source/core/inc/environmentofanchoredobject.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 . + */ + +#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 ); + + /** destructor + */ + ~SwEnvironmentOfAnchoredObject(); + + /** 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..07b56d5fc --- /dev/null +++ b/sw/source/core/inc/fefly.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_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..929d6ea19 --- /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 : 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..078ef9326 --- /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 m_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 m_bMoveBwdJump; } + static void SetMoveBwdJump( bool bNew ){ m_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..464eadd7e --- /dev/null +++ b/sw/source/core/inc/flyfrm.hxx @@ -0,0 +1,284 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 ); + +/** 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(); // these to methods are called in the + void FinitDrawObj(); // constructors + + void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, sal_uInt8 &, + SwAttrSetChg *pa = nullptr, SwAttrSetChg *pb = nullptr ); + + using SwLayoutFrame::CalcRel; + +protected: + // Predecessor/Successor for chaining with text flow + SwFlyFrame *m_pPrevLink, *m_pNextLink; + +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 + 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 + + 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 bool SetObjTop_( const SwTwips _nTop ) override; + virtual bool SetObjLeft_( const SwTwips _nLeft ) override; + + virtual SwRect GetObjBoundRect() const override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 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); + +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..4c6940c28 --- /dev/null +++ b/sw/source/core/inc/flyfrms.hxx @@ -0,0 +1,241 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 : public SwFlyFreeFrame +{ +public: + // #i28701# + + SwFlyLayFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor ); + +protected: + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; +}; + +// Flys that are bound to Content but not in Content +class SwFlyAtContentFrame : public SwFlyFreeFrame +{ +protected: + 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 Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 : public SwFlyFrame +{ + Point aRef; // relative to this point AbsPos is being calculated + + virtual void DestroyImpl() override; + virtual ~SwFlyInContentFrame() override; + +protected: + virtual void NotifyBackground( SwPageFrame *pPage, + const SwRect& rRect, PrepareHint eHint) override; + virtual void MakeAll(vcl::RenderContext* pRenderContext) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 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( long nOfst ) { aRef.AdjustY( nOfst ); } + + // #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..f908ed62e --- /dev/null +++ b/sw/source/core/inc/fntcache.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <map> + +#include <vcl/font.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/vcllayout.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(); +}; + +/// Clears the pre-calculated text glyphs in all SwFntObj instances. +void SwClearFntCacheTextGlyphs(); + +// Font cache, global variable, created/destroyed in txtinit.cxx +extern SwFntCache *pFntCache; +extern SwFntObj *pLastFont; + +/** + * Defines a substring on a given output device, to be used as an std::map<> + * key. + */ +struct SwTextGlyphsKey +{ + VclPtr<OutputDevice> m_pOutputDevice; + OUString m_aText; + sal_Int32 m_nIndex; + sal_Int32 m_nLength; + +}; +bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r); + +class SwFntObj : 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_nZoom; + bool m_bSymbol : 1; + bool m_bPaintBlank : 1; + + /// Cache of already calculated layout glyphs. + std::map<SwTextGlyphsKey, SalLayoutGlyphs> m_aTextGlyphs; + + static long nPixWidth; + static MapMode *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 ); + + 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; } + std::map<SwTextGlyphsKey, SalLayoutGlyphs>& GetTextGlyphs() { return m_aTextGlyphs; } + + 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 : public SwCacheAccess +{ + SwViewShell const *m_pShell; +protected: + 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..429746737 --- /dev/null +++ b/sw/source/core/inc/frame.hxx @@ -0,0 +1,1417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/baseprimitive2d.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> + +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; + +// 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 mnLastFrameId; + 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); +}; + +/** + * 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 *mpCache; + + 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*, sal_uInt8 & ); + 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; + bool mbForbidDelete : 1; + + 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 + SwModify *GetDep() { return GetRegisteredInNonConst(); } + const SwModify *GetDep() const { return GetRegisteredIn(); } + + SwFrame( SwModify*, 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 Modify( const SfxPoolItem*, const SfxPoolItem* ) 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 *mpCache; } + static SwCache *GetCachePtr() { return mpCache; } + static void SetCache( SwCache *pNew ) { mpCache = 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(); } + // #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, + const Color*& rpColor, + 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; + 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 mbForbidDelete; } + + /// 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 + long GetTopMargin() const; + long GetBottomMargin() const; + long GetLeftMargin() const; + long GetRightMargin() const; + void SetTopBottomMargins( long, long ); + void SetLeftRightMargins( long, long ); + void SetRightLeftMargins( long, long ); + long GetPrtLeft() const; + long GetPrtBottom() const; + long GetPrtRight() const; + long GetPrtTop() const; + bool SetMinLeft( long ); + bool SetMaxBottom( long ); + bool SetMaxRight( 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() { mbForbidDelete = true; } + void AllowDelete() { mbForbidDelete = false; } + + 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 && !pFrame->IsDeleteForbidden()) ? + pFrame : nullptr) + { + if (m_pForbidFrame) + m_pForbidFrame->ForbidDelete(); + } + + SwFrameDeleteGuard(const SwFrameDeleteGuard&) =delete; + + ~SwFrameDeleteGuard() + { + if (m_pForbidFrame) + m_pForbidFrame->AllowDelete(); + } + + SwFrameDeleteGuard& operator=(const SwFrameDeleteGuard&) =delete; +}; + +typedef long (SwFrame:: *SwFrameGet)() const; +typedef bool (SwFrame:: *SwFrameMax)( long ); +typedef void (SwFrame:: *SwFrameMakePos)( const SwFrame*, const SwFrame*, bool ); +typedef long (*SwOperator)( long, long ); +typedef void (SwFrame:: *SwFrameSet)( long, 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)()); + } + + long GetTop (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetTop) (); } + long GetBottom(const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetBottom)(); } + long GetLeft (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetLeft) (); } + long GetRight (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetRight) (); } + long GetWidth (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetWidth) (); } + 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, long nNew) const { (rRect.*m_fnRect->fnSetTop) (nNew); } + void SetBottom(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetBottom)(nNew); } + void SetLeft (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetLeft) (nNew); } + void SetRight (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetRight) (nNew); } + void SetWidth (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetWidth) (nNew); } + void SetHeight(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetHeight)(nNew); } + + void SubTop (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSubTop) (nNew); } + void AddBottom(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddBottom)(nNew); } + void SubLeft (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSubLeft) (nNew); } + void AddRight (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddRight) (nNew); } + void AddWidth (SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddWidth) (nNew); } + void AddHeight(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnAddHeight)(nNew); } + + void SetPosX(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetPosX)(nNew); } + void SetPosY(SwRect& rRect, long nNew) const { (rRect.*m_fnRect->fnSetPosY)(nNew); } + + long GetTopMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetTopMargin) (); } + long GetBottomMargin(const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetBottomMargin)(); } + long GetLeftMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetLeftMargin) (); } + long GetRightMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetRightMargin) (); } + void SetXMargins(SwFrame& rFrame, long nLeft, long nRight) const { (rFrame.*m_fnRect->fnSetXMargins)(nLeft, nRight); } + void SetYMargins(SwFrame& rFrame, long nTop, long nBottom) const { (rFrame.*m_fnRect->fnSetYMargins)(nTop, nBottom); } + long GetPrtTop (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtTop) (); } + long GetPrtBottom (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtBottom) (); } + long GetPrtLeft (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtLeft) (); } + long GetPrtRight (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtRight) (); } + long TopDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnTopDist) (nPos); } + long BottomDist(const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnBottomDist) (nPos); } + long LeftDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnLeftDist) (nPos); } + long RightDist (const SwRect& rRect, long nPos) const { return (rRect.*m_fnRect->fnRightDist) (nPos); } + void SetLimit (SwFrame& rFrame, long nNew) const { (rFrame.*m_fnRect->fnSetLimit) (nNew); } + bool OverStep (const SwRect& rRect, 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); } + long XDiff(long n1, long n2) const { return (m_fnRect->fnXDiff) (n1, n2); } + long YDiff(long n1, long n2) const { return (m_fnRect->fnYDiff) (n1, n2); } + long XInc (long n1, long n2) const { return (m_fnRect->fnXInc) (n1, n2); } + long YInc (long n1, long n2) const { return (m_fnRect->fnYInc) (n1, n2); } + + void SetLeftAndWidth(SwRect& rRect, long nLeft, long nWidth) const { (rRect.*m_fnRect->fnSetLeftAndWidth)(nLeft, nWidth); } + void SetTopAndHeight(SwRect& rRect, long nTop, 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..ab7ce6a5c --- /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 *pFrame; + + // Where does the text (w/o whitespaces) start (document is global!)? + static SwTwips GetLineStart( const SwTextCursor &rLine ); + +public: + SwTextFrameInfo( const SwTextFrame *pTextFrame ) : 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 pFrame; } + SwTextFrameInfo& SetFrame( const SwTextFrame* pNew ) + { 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..f7f4a209b --- /dev/null +++ b/sw/source/core/inc/frmtool.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_FRMTOOL_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_FRMTOOL_HXX + +#include <swtypes.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 +#define BROWSE_HEIGHT (56700L * 10L) // 10 Meters +#define GRFNUM_NO 0 +#define GRFNUM_YES 1 +#define GRFNUM_REPLACE 2 + +void AppendObjs( const SwFrameFormats *pTable, sal_uLong nIndex, + SwFrame *pFrame, SwPageFrame *pPage, SwDoc* doc ); + +void AppendObjsOfNode(SwFrameFormats const* pTable, sal_uLong 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, sal_uLong nIndex, + bool bPages = false, sal_uLong nEndIndex = 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; + +// prevent creation of Flys in InsertCnt_, e.g. for table headlines +extern bool bDontCreateObjects; + +// for FlyCnts, see SwFlyAtContentFrame::MakeAll() +extern bool bSetCompletePaintOnInvalidate; + +// for table settings via keyboard +SwTwips CalcRowRstHeight( SwLayoutFrame *pRow ); +long CalcHeightWithFlys( const SwFrame *pFrame ); + +namespace sw { + +bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 nPageNum); + +} // namespace sw + +SwPageFrame *InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper, + bool isRightPage, bool bFirst, bool bInsertEmpty, bool bFootnote, + SwFrame *pSibling ); + +// 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, + SwModify 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 +{ +protected: + SwFrame *mpFrame; + const SwRect maFrame; + const SwRect maPrt; + SwTwips mnFlyAnchorOfst; + SwTwips mnFlyAnchorOfstNoWrap; + bool mbHadFollow; + bool mbInvaKeep; + bool mbValidSize; + +public: + SwFrameNotify( SwFrame *pFrame ); + ~SwFrameNotify() COVERITY_NOEXCEPT_FALSE; + + const SwRect &getFrameArea() const { return maFrame; } + void SetInvaKeep() { mbInvaKeep = true; } +}; + +class SwLayNotify : public SwFrameNotify +{ + bool m_bLowersComplete; + +public: + SwLayNotify( SwLayoutFrame *pLayFrame ); + ~SwLayNotify(); + + void SetLowersComplete( bool b ) { m_bLowersComplete = b; } + bool IsLowersComplete() const { return m_bLowersComplete; } +}; + +class SwFlyNotify : public SwLayNotify +{ + SwPageFrame *pOldPage; + const SwRect aFrameAndSpace; + +public: + SwFlyNotify( SwFlyFrame *pFlyFrame ); + ~SwFlyNotify(); +}; + +class SwContentNotify : public SwFrameNotify +{ +private: + // #i11859# + bool mbChkHeightOfLastLine; + SwTwips mnHeightOfLastLine; + + // #i25029# + bool mbInvalidatePrevPrtArea; + bool mbBordersJoinedWithPrev; + +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 : 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 SwModify *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; + long CalcLeft( const SwFrame *pCaller ) const; + 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 : public SwCacheAccess +{ + const SwFrame *m_pConstructor; //opt: for passing on to SwBorderAttrs + +protected: + 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 nCnt; + static bool bLocked; + +public: + StackHack() + { + if ( ++StackHack::nCnt > 50 ) + StackHack::bLocked = true; + } + ~StackHack() + { + if ( --StackHack::nCnt < 5 ) + StackHack::bLocked = false; + } + + static bool IsLocked() { return StackHack::bLocked; } + static sal_uInt8 Count() { return StackHack::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 +*/ +void GetSpacingValuesOfFrame( const SwFrame& rFrame, + SwTwips& onLowerSpacing, + SwTwips& onLineSpacing, + bool& obIsLineSpacingProportional ); + +/** 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 SwModify* 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..3ee7859bf --- /dev/null +++ b/sw/source/core/inc/ftnboss.hxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +{ + 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..34dc89bc1 --- /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: 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: 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..7d070b683 --- /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: public SwHeadFootFrame +{ +public: + SwHeaderFrame( SwFrameFormat* pFrame, SwFrame* pSib ) : SwHeadFootFrame(pFrame, pSib, SwFrameType::Header) {}; +}; + +/// Footer in the document layout, inside a page. +class SwFooterFrame: 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..990c0e4b8 --- /dev/null +++ b/sw/source/core/inc/layact.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <vcl/TaskStopwatch.hxx> +#include <tools/color.hxx> + +#include <ctime> +#include <memory> + +#include <swrect.hxx> + +class OutputDevice; +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 + TaskStopwatch* m_pWatch; + + // 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; + + // 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() + + 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_bReschedule; // Call Reschedule depending on Progress? + bool m_bInterrupt; // For termination the layouting + 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; + + // for loop control by disabling in-row splitting within embedded tables + const SwPageFrame *m_pCurPage; + sal_uInt16 m_nTabLevel; // embedding level + sal_uInt32 m_nCallCount; // calling FormatLayoutTab on the same page + + void PaintContent( const SwContentFrame *, const SwPageFrame *, + const SwRect &rOldRect, 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( const 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(); + +public: + SwLayAction(SwRootFrame *pRt, SwViewShellImp *pImp, TaskStopwatch* pWatch = nullptr); + ~SwLayAction(); + + 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 IsReschedule() const { return m_bReschedule; } + bool IsIdle() const { return m_pWatch != nullptr; } + bool IsPaintExtraData() const { return m_bPaintExtraData; } + bool IsInterrupt(); + + // 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 SetCalcLayout ( bool bNew ) { m_bCalcLayout = bNew; } + void SetReschedule ( bool bNew ) { m_bReschedule = bNew; } + void SetWaitAllowed ( bool bNew ) { m_bWaitAllowed = bNew; } + + void SetAgain() { m_bAgain = true; } + 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 +{ + TaskStopwatch m_aWatch; + SwRootFrame *pRoot; + SwViewShellImp *pImp; // The Idler registers and deregisters here + SwContentNode *pContentNode; // The current cursor position is saved here + sal_Int32 nTextPos; + bool bPageValid; // Were we able to evaluate everything on the whole page? +#ifdef DBG_UTIL + bool m_bIndicator; + + void ShowIdle( Color eName ); +#endif + + bool IsInterrupt(); + 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..6f3a5a00d --- /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> pImpl; + sal_uInt16 nLockCount; + +public: + SwLayoutCache(); + ~SwLayoutCache(); + + void Read( SvStream &rStream ); + static void Write( SvStream &rStream, const SwDoc& rDoc ); + + void ClearImpl(); + bool IsLocked() const { return nLockCount > 0; } + sal_uInt16& GetLockCount() { return nLockCount; } + SwLayCacheImpl *LockImpl() + { if( nLockCount & 0x8000 ) return nullptr; + if ( pImpl ) + ++nLockCount; + return pImpl.get(); } + void UnlockImpl() { --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..1a725dc34 --- /dev/null +++ b/sw/source/core/inc/layfrm.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_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; + + 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; } + 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..73e74d7d1 --- /dev/null +++ b/sw/source/core/inc/mvsave.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 . + */ +#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; + vcl::KeyCode m_aCode; + IDocumentMarkAccess::MarkType m_eOrigBkmType; + sal_uLong m_nNode1; + sal_uLong 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* pDoc, sal_uLong nNode, sal_Int32 nContent, bool bSaveFlySplit=false) =0; + virtual void Restore(SwDoc* pDoc, sal_uLong nNode, sal_Int32 nOffset=0, bool bAuto = 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 +{ + sal_uLong nNdDiff; /// relative node difference + sal_Int32 nContentIndex; ///< index in node + SwFrameFormat* pFrameFormat; /// the fly's frame format + bool isAtInsertNode; ///< if true, anchor _at_ insert node index + + SaveFly( sal_uLong nNodeDiff, sal_Int32 const nCntntIdx, SwFrameFormat* pFormat, bool bInsert ) + : nNdDiff(nNodeDiff) + , nContentIndex(nCntntIdx) + , pFrameFormat(pFormat) + , isAtInsertNode(bInsert) + { } +}; + +typedef std::deque< SaveFly > SaveFlyArr; + +void RestFlyInRange( SaveFlyArr& rArr, const SwPosition& rSttIdx, + const SwNodeIndex* pInsPos ); +void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr ); +void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos, + SaveFlyArr& rArr, bool bMoveAllFlys ); + +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_pDoc; + sal_Int32 m_nContent; + +public: + SwDataChanged( const SwPaM& rPam ); + SwDataChanged( SwDoc* pDoc, 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..9faadd574 --- /dev/null +++ b/sw/source/core/inc/node2lay.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_NODE2LAY_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_NODE2LAY_HXX + +#include <tools/solar.h> +#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 SwModify 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> 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, sal_uLong nIdx ); + ~SwNode2Layout(); + SwFrame* NextFrame(); + SwLayoutFrame* UpperFrame( SwFrame* &rpFrame, const SwNode& rNode ); + + SwFrame *GetFrame( const Point* pDocPos ) const; +}; + +class SwNode2LayoutSaveUpperFrames +{ + std::unique_ptr<SwNode2LayImpl> pImpl; +public: + /// Use this ctor for collecting the UpperFrames + SwNode2LayoutSaveUpperFrames( const SwNode& rNd ); + ~SwNode2LayoutSaveUpperFrames(); + + void RestoreUpperFrames( SwNodes& rNds, sal_uLong nStt, sal_uLong nEnd ); +}; + +#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..fd52fffb5 --- /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..c958da594 --- /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: 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; + +protected: + virtual void MakeAll(vcl::RenderContext* pRenderContext) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) 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( 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..adee2319f --- /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 : 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..3c7f29f52 --- /dev/null +++ b/sw/source/core/inc/pagefrm.hxx @@ -0,0 +1,443 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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; + +/// 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: 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 mnShadowPxWidth; + + void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, sal_uInt8 &, + 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; + +protected: + virtual void MakeAll(vcl::RenderContext* pRenderContext) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; + virtual void SwClientNotify(const SwModify&, const SfxHint&) override; + + /// Calculate the content height of a page (without columns). + size_t GetContentHeight(const long nTop, const 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::GetDrawBackgrdColor + + determine the color, that is respectively will be drawn as background + for the page frame. + + @return reference to an instance of class Color + */ + Color GetDrawBackgrdColor() 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 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&); + +#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..b3d4812d6 --- /dev/null +++ b/sw/source/core/inc/paintfrm.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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..48dcce58a --- /dev/null +++ b/sw/source/core/inc/pamtyp.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_PAMTYP_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_PAMTYP_HXX + +#include <unotools/textsearch.hxx> +#include <swdllapi.h> + +#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..04c62873f --- /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..505f18f40 --- /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 <osl/mutex.hxx> +#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_LINK( LinkedInputStreamReady, void*, void ); + + private: + + static tDataKey mnNextKeyValue; + + osl::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..579efb24f --- /dev/null +++ b/sw/source/core/inc/retrieveinputstream.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_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 : public ObservableThread +{ + public: + + static ::rtl::Reference< ObservableThread > createThread( + const SwRetrievedInputStreamDataManager::tDataKey nDataKey, + const OUString& rLinkedURL, const OUString& rReferer ); + + virtual ~SwAsyncRetrieveInputStreamThread() override; + + protected: + + virtual void threadFunction() override; + + private: + + 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..d6cec5075 --- /dev/null +++ b/sw/source/core/inc/rolbck.hxx @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <svl/itemset.hxx> +#include <tools/solar.h> +#include <vcl/keycod.hxx> +#include <tox.hxx> + +#include <IDocumentMarkAccess.hxx> + +#include <memory> +#include <vector> +#include <set> + +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; + +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; +}; + +class SwHistorySetFormat : public SwHistoryHint +{ + std::unique_ptr<SfxPoolItem> m_pAttr; + const sal_uLong m_nNodeIndex; + +public: + SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNode ); + virtual ~SwHistorySetFormat() override; + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + virtual OUString GetDescription() const override; + +}; + +class SwHistoryResetFormat : public SwHistoryHint +{ + const sal_uLong m_nNodeIndex; + const sal_uInt16 m_nWhich; + +public: + SwHistoryResetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNodeIdx ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + +}; + +class SwHistorySetText : public SwHistoryHint +{ + std::unique_ptr<SfxPoolItem> m_pAttr; + const sal_uLong m_nNodeIndex; + const sal_Int32 m_nStart; + const sal_Int32 m_nEnd; + bool m_bFormatIgnoreStart : 1; + bool m_bFormatIgnoreEnd : 1; + +public: + SwHistorySetText( SwTextAttr* pTextHt, sal_uLong nNode ); + virtual ~SwHistorySetText() override; + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + +}; + +class SwHistorySetTextField : 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; + + sal_uLong m_nNodeIndex; + sal_Int32 m_nPos; + SwFieldIds m_nFieldWhich; + +public: + SwHistorySetTextField( const SwTextField* pTextField, sal_uLong nNode ); + virtual ~SwHistorySetTextField() override; + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + + virtual OUString GetDescription() const override; + +}; + +class SwHistorySetRefMark : public SwHistoryHint +{ + const OUString m_RefName; + const sal_uLong m_nNodeIndex; + const sal_Int32 m_nStart; + const sal_Int32 m_nEnd; + +public: + SwHistorySetRefMark( const SwTextRefMark* pTextHt, sal_uLong nNode ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + +}; + +class SwHistorySetTOXMark : public SwHistoryHint +{ + SwTOXMark m_TOXMark; + const OUString m_TOXName; + const TOXTypes m_eTOXTypes; + const sal_uLong m_nNodeIndex; + const sal_Int32 m_nStart; + const sal_Int32 m_nEnd; + +public: + SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, sal_uLong 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 : public SwHistoryHint +{ + const sal_uLong 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, + sal_uLong nNode ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + + sal_uInt16 GetWhich() const { return m_nAttr; } + sal_uLong GetNode() const { return m_nNodeIndex; } + sal_Int32 GetContent() const { return m_nStart; } + +}; + +class SwHistorySetFootnote : public SwHistoryHint +{ + const std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> m_pUndo; + const OUString m_FootnoteNumber; + sal_uLong m_nNodeIndex; + const sal_Int32 m_nStart; + const bool m_bEndNote; + +public: + SwHistorySetFootnote( SwTextFootnote* pTextFootnote, sal_uLong nNode ); + SwHistorySetFootnote( const SwTextFootnote& ); + virtual ~SwHistorySetFootnote() override; + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + + virtual OUString GetDescription() const override; + +}; + +class SwHistoryChangeFormatColl : public SwHistoryHint +{ + SwFormatColl * const m_pColl; + const sal_uLong m_nNodeIndex; + const SwNodeType m_nNodeType; + +public: + SwHistoryChangeFormatColl( SwFormatColl* pColl, sal_uLong nNode, SwNodeType nNodeWhich ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + +}; + +class SwHistoryTextFlyCnt : 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(); } + +}; + +class SwHistoryBookmark : 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; + vcl::KeyCode m_aKeycode; + const sal_uLong m_nNode; + const sal_uLong 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 : public SwHistoryHint +{ + public: + SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark); + virtual void SetInDoc(SwDoc* pDoc, bool) override; + void ResetInDoc(SwDoc* pDoc); + + private: + const OUString m_sType; + const sal_uLong m_nNode; + const sal_Int32 m_nContent; +}; + +/// History object containing all information used during undo / redo +/// of text form field insertion. +class SwHistoryTextFieldmark : public SwHistoryHint +{ + public: + SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark); + virtual void SetInDoc(SwDoc* pDoc, bool) override; + void ResetInDoc(SwDoc* pDoc); + + private: + const OUString m_sName; + const OUString m_sType; + const sal_uLong m_nStartNode; + const sal_Int32 m_nStartContent; + const sal_uLong m_nEndNode; + const sal_Int32 m_nEndContent; + /*const*/ sal_uLong m_nSepNode; + /*const*/ sal_Int32 m_nSepContent; +}; + +class SwHistorySetAttrSet : public SwHistoryHint +{ + SfxItemSet m_OldSet; + std::vector<sal_uInt16> m_ResetArray; + const sal_uLong m_nNodeIndex; + +public: + SwHistorySetAttrSet( const SfxItemSet& rSet, sal_uLong nNode, + const std::set<sal_uInt16> &rSetArr ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; + +}; + +class SwHistoryChangeFlyAnchor : public SwHistoryHint +{ + SwFrameFormat & m_rFormat; + const sal_uLong m_nOldNodeIndex; + const sal_Int32 m_nOldContentIndex; + +public: + SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat ); + virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override; +}; + +class SwHistoryChangeFlyChain : 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 : 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, + sal_uLong nNodeIdx ); + void Add( SwTextAttr* pTextHt, sal_uLong nNodeIdx, bool bNewAttr ); + void Add( SwFormatColl*, sal_uLong 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 sal_uLong nNodeIdx, + const sal_Int32 nStart, + const sal_Int32 nEnd, + const bool bCopyFields ); + + void CopyFormatAttr( const SfxItemSet& rSet, sal_uLong nNodeIdx ); +}; + +class SwRegHistory : public SwClient +{ +private: + std::set<sal_uInt16> m_WhichIdSet; + SwHistory * const m_pHistory; + sal_uLong m_nNodeIndex; + + void MakeSetWhichIds(); + +protected: + virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) override; + +public: + SwRegHistory( SwHistory* pHst ); + SwRegHistory( const SwNode& rNd, SwHistory* pHst ); + SwRegHistory( SwModify* pRegIn, const SwNode& rNd, SwHistory* pHst ); + + /// @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( SwModify* pRegIn, const SwNode& rNd ); + void ChangeNodeIndex( sal_uLong 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..7a53fc1af --- /dev/null +++ b/sw/source/core/inc/rootfrm.hxx @@ -0,0 +1,462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 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 SAL_DLLPUBLIC_RTTI SwRootFrame: 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 + 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 = convertMm100ToTwip(20000); + + 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 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; + + /** + * 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. + */ + 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 SET_CURR_SHELL + * The macro and 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; + +protected: + + 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; } + + /** + * 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 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); +}; + +inline 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..e131730c2 --- /dev/null +++ b/sw/source/core/inc/rowfrm.hxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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: 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; + +protected: + virtual void MakeAll(vcl::RenderContext* pRenderContext) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; + +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; } +}; + +#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..ead531c5c --- /dev/null +++ b/sw/source/core/inc/scriptinfo.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_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 "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 + long Compress( long* 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 pScrArray + The screen 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( long* pKernArray, long* pScrArray, + TextFrameIndex nStt, TextFrameIndex nLen, 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 pScrArray + The screen 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, long* pKernArray, + long* pScrArray, TextFrameIndex nIdx, + TextFrameIndex nLen, + TextFrameIndex nNumberOfBlanks = TextFrameIndex(0), + long nSpaceAdd = 0 ); + + static TextFrameIndex CountCJKCharacters(const OUString &rText, + TextFrameIndex nPos, TextFrameIndex nEnd, LanguageType aLang); + + static void CJKJustify( const OUString& rText, long* pKernArray, + long* pScrArray, TextFrameIndex nStt, + TextFrameIndex nLen, LanguageType aLang, + 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..3b890b385 --- /dev/null +++ b/sw/source/core/inc/sectfrm.hxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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" + +class SwSection; +class SwSectionFormat; +class SwAttrSetChg; +class SwFootnoteContFrame; +class SwLayouter; + +enum class SwFindMode +{ + None = 0, EndNote = 1, LastCnt = 2, MyLast = 4 +}; + +class SwSectionFrame: public SwLayoutFrame, public SwFlowFrame +{ + 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*, sal_uInt8 &, + 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; + +protected: + 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 Modify( const SfxPoolItem*, const SfxPoolItem* ) 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..3cc65c484 --- /dev/null +++ b/sw/source/core/inc/swblocks.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 nHashS, nHashL; // Hash codes for testing +public: + OUString aShort; /// Shortname + OUString aLong; /// Longname + OUString aPackageName; /// Package name + bool bIsOnlyTextFlagInit : 1; /// Is the Flag valid? + bool 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 aShort < r.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 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..92b678c07 --- /dev/null +++ b/sw/source/core/inc/swcache.hxx @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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> + +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; + long m_nAppend; /// number of entries appended + long m_nInsertFree; /// number of entries inserted on freed position + long m_nReplace; /// number of LRU replacements + long m_nGetSuccess; + long m_nGetFail; + long m_nToTop; /// number of reordering (LRU) + long m_nDelete; /// number of explicit deletes + long m_nGetSeek; /// number of gets without index + long m_nAverageSeekCnt; /// number of seeks for all gets without index + long m_nFlushCnt; /// number of flush calls + long m_nFlushedObjects; + long m_nIncreaseMax; /// number of cache size increases + 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..ebe64d841 --- /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 AttributSet/Font-Cache pSwFontCache" +#endif + ) {} + +}; + +// AttributSet/Font-Cache, globale Variable, in FontCache.Cxx angelegt +extern SwFontCache *pSwFontCache; + +class SwFontObj : 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 : public SwCacheAccess +{ + SwViewShell *m_pShell; +protected: + 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..b9e367602 --- /dev/null +++ b/sw/source/core/inc/swfont.hxx @@ -0,0 +1,1000 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +const sal_Unicode CH_PAR = 0xB6; // 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 + +sal_uInt16 UnMapDirection( sal_uInt16 nDir, const bool bVertFormat, const bool bVertFormatLRBT ); + +class SwSubFont : 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 ); + 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 FontAlign 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 sal_uInt16 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::unique_ptr<Color> + m_pBackColor; // 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_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( Color* pNewColor ); + const Color* GetBackColor() const{ return m_pBackColor.get(); } + 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 FontAlign 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(sal_uInt16 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 &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(); } + 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(); } + sal_uInt16 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(); } + 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, + 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, 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 ); } + + 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 FontAlign eAlign ) +{ + m_nFontCacheId = nullptr; + Font::SetAlignment( eAlign ); +} + +inline void SwFont::SetAlign( const FontAlign 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 sal_uInt16 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..0fe63587b --- /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..e22fb90ae --- /dev/null +++ b/sw/source/core/inc/swthreadjoiner.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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..28952683a --- /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 mbThreadManagerInstantiated; + + 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..325515755 --- /dev/null +++ b/sw/source/core/inc/tabfrm.hxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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; + +/// SwTabFrame is one table in the document layout, containing rows (which contain cells). +class SwTabFrame: 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; + + bool m_bSplitRowDisabled : 1; // loop control + + /** + * 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*, sal_uInt8 &, + SwAttrSetChg *pa = nullptr, + SwAttrSetChg *pb = nullptr ); + + virtual bool ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) override; + + virtual void DestroyImpl() override; + virtual ~SwTabFrame() override; + +protected: + virtual void MakeAll(vcl::RenderContext* pRenderContext) override; + virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override; + virtual void Modify( const SfxPoolItem*, const SfxPoolItem* ) override; + // only changes the Framesize, not the PrtArea size + virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override; + +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; + } + bool IsSplitRowDisabled() const + { + return m_bSplitRowDisabled; + } + void SetSplitRowDisabled() + { + m_bSplitRowDisabled = true; + } + + // #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, + long& rLeftOffset, + long& rRightOffset ) 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..8d78bae00 --- /dev/null +++ b/sw/source/core/inc/tblrwcl.hxx @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <memory> +#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* pDoc, 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> aPosArr; + std::vector<SwTableBox*> m_Boxes; + SwHistory* pHst; + SplitTable_HeadlineOption nMode; + sal_uInt16 nWidth; + bool bGetFromTop : 1; + bool bGetValues : 1; + +public: + SwCollectTableLineBoxes( bool bTop, SplitTable_HeadlineOption nMd = SplitTable_HeadlineOption::NONE, SwHistory* pHist=nullptr ) + : + pHst( pHist ), nMode( nMd ), nWidth( 0 ), + bGetFromTop( bTop ), 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 == aPosArr.size()) ? nWidth + : aPosArr[ nPos+1 ]; + return *m_Boxes[ nPos ]; + } + + bool IsGetFromTop() const { return bGetFromTop; } + bool IsGetValues() const { return bGetValues; } + + SplitTable_HeadlineOption GetMode() const { return nMode; } + void SetValues( bool bFlag ) { bGetValues = false; nWidth = 0; + 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< 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 >= static_cast<sal_uInt16>(pLines->size()); } +}; + +class SwGCBorder_BoxBrd +{ + const editeng::SvxBorderLine* pBrdLn; + bool bAnyBorderFnd; +public: + SwGCBorder_BoxBrd() : pBrdLn( nullptr ), bAnyBorderFnd( false ) {} + + void SetBorder( const editeng::SvxBorderLine& rBorderLine ) + { pBrdLn = &rBorderLine; bAnyBorderFnd = 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 bAnyBorderFnd; } +}; + +void sw_GC_Line_Border( const SwTableLine* pLine, SwGCLineBorder* pGCPara ); + +class SwShareBoxFormat +{ + const SwFrameFormat* pOldFormat; + std::vector<SwFrameFormat*> aNewFormats; + +public: + SwShareBoxFormat( const SwFrameFormat& rFormat ) + : pOldFormat( &rFormat ) + {} + + const SwFrameFormat& GetOldFormat() const { return *pOldFormat; } + + SwFrameFormat* GetFormat( 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<std::unique_ptr<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, 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..3362621d7 --- /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 : public SvxEditSource +{ + SwTextAPIEditSource_Impl* 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::unique_ptr<OutlinerParaObject> CreateText(); + OUString GetText() const; +}; + +class SwTextAPIObject : public SvxUnoText +{ + std::unique_ptr<SwTextAPIEditSource> pSource; +public: + SwTextAPIObject( std::unique_ptr<SwTextAPIEditSource> p); + virtual ~SwTextAPIObject() throw() override; + void DisposeEditSource() { pSource->Dispose(); } + std::unique_ptr<OutlinerParaObject> CreateText() { return pSource->CreateText(); } + void SetString( const OUString& rText ) { pSource->SetString( rText ); } + void SetText( OutlinerParaObject const & rText ) { pSource->SetText( rText ); } + OUString GetText() const { return 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..4917296c6 --- /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 : 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..07dd957cd --- /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 : 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..14bda973e --- /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 : 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..b7772a5b1 --- /dev/null +++ b/sw/source/core/inc/txmsrt.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_INC_TXMSRT_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_TXMSRT_HXX + +#include <i18nlangtag/lang.h> +#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; + sal_uLong 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 OUString GetURL() 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 : 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 : 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 : 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 : 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 OUString GetURL() 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 : public SwTOXSortTabBase +{ + SwTOXTable( const SwContentNode& rNd ); + + void SetLevel(sal_uInt16 nSet){nLevel = nSet;} + + virtual sal_uInt16 GetLevel() const override; + + virtual OUString GetURL() const override; +private: + virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override; + + sal_uInt16 nLevel; +}; + +struct SwTOXAuthority : 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; +}; + +#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..0f3c9e8c7 --- /dev/null +++ b/sw/source/core/inc/txtfly.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_INC_TXTFLY_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_TXTFLY_HXX + +#include <editeng/txtrange.hxx> +#include <tools/solar.h> +#include <swtypes.hxx> +#include <swrect.hxx> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <memory> +#include <vector> + +class OutputDevice; +class SwPageFrame; +class SdrObject; +class SwFormat; +class SwAnchoredObject; +class SwTextFrame; +class SwDrawTextInfo; +class SwContourCache; + +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; + long nPntCnt; + SwRect ContourRect( const SwFormat* pFormat, const SdrObject* pObj, + const SwTextFrame* pFrame, const SwRect &rLine, const 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 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 * pPage; + const SwAnchoredObject * mpCurrAnchoredObj; + const SwTextFrame * m_pCurrFrame; + const SwTextFrame * m_pMaster; + std::unique_ptr<SwAnchoredObjList> mpAnchoredObjList; + + long nMinBottom; + long nNextTop; /// Stores the upper edge of the "next" frame + sal_uLong m_nCurrFrameNodeIndex; + + bool bOn : 1; + bool 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; + const SwTextFrame* GetMaster() const; + + // This temporary variable needs to be manipulated in const methods + long GetNextTop() const; + void SetNextTop( 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() +{ + bTopRule = false; +} + +inline bool SwTextFly::IsOn() const +{ + return bOn; +} + +inline bool SwTextFly::Relax( const SwRect &rRect ) +{ + if (bOn) + { + bOn = IsAnyFrame( rRect ); + } + return bOn; +} + +inline bool SwTextFly::Relax() +{ + if (bOn) + { + bOn = IsAnyFrame(); + } + return bOn; +} + +inline SwTwips SwTextFly::GetMinBottom() const +{ + return mpAnchoredObjList ? nMinBottom : CalcMinBottom(); +} + +inline const SwTextFrame* SwTextFly::GetMaster() const +{ + return m_pMaster ? m_pMaster : const_cast<SwTextFly*>(this)->GetMaster_(); +} + +inline long SwTextFly::GetNextTop() const +{ + return nNextTop; +} + +inline void SwTextFly::SetNextTop( long nNew ) const +{ + const_cast<SwTextFly*>(this)->nNextTop = nNew; +} + +inline SwRect SwTextFly::GetFrame( const SwRect &rRect ) const +{ + return 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; +} + +#endif + +/* 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..0f96e6676 --- /dev/null +++ b/sw/source/core/inc/txtfrm.hxx @@ -0,0 +1,1024 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <set> + +namespace com::sun::star::linguistic2 { class XHyphenatedWord; } + +namespace sw::mark { class IMark; } +class SwCharRange; +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; + +#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, sal_uLong 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<sal_uLong> *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); + +} // 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: 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 long nMinPrtLine = 0; // This Line must not be underrun when printing + // Hack for table cells stretching multiple pages + + sal_uLong mnAllLines :24; // Line count for the Paint (including nThisLines) + sal_uLong 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 long = 0); + inline void InvalidateRange( const SwCharRange &, const 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; + +protected: + 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_() and ExecSpellPopup() + SwRect AutoSpell_(SwTextNode &, sal_Int32); + + /// Is called by DoIdleJob_() + SwRect SmartTagScan(SwTextNode &); + + /// Is called by DoIdleJob_() + void CollectAutoCmplWrds(SwTextNode &, sal_Int32); + + /** + * Returns the screen position of rPos. 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 ) 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 + sal_uInt16 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 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 + */ + 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( 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 + */ + long SwitchHorizontalToVertical( 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 + */ + long SwitchVerticalToHorizontal( 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; + 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: +#if BOOST_VERSION < 105600 + sw::MergedPara const* m_pMerged; + SwTextNode const* m_pNode; +#else + sw::MergedPara const*const m_pMerged; + SwTextNode const*const m_pNode; +#endif + 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..16f433e3b --- /dev/null +++ b/sw/source/core/inc/txttypes.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_TXTTYPES_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_TXTTYPES_HXX + +/// @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, + + 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, + + Table = 0x0750, + + TabRight = 0x07d0, + TabCenter = 0x07d1, + TabDecimal = 0x07d2, + + TabLeft = 0x0740, +}; + +#endif // INCLUDED_SW_SOURCE_CORE_INC_TXTTYPES_HXX + +/* 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..4110b91e6 --- /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 : 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..f277748b5 --- /dev/null +++ b/sw/source/core/inc/unobookmark.hxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UNOBOOKMARK_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_UNOBOOKMARK_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 <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; + +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); + /// @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(); + + 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 + : 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 > SwXFieldmark_Base; + +class SwXFieldmark final + : public SwXFieldmark_Base +{ + ::sw::mark::ICheckboxFieldmark* getCheckboxFieldmark(); + bool m_bReplacementObject; + + 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; + 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; + 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; +}; + +#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOBOOKMARK_HXX + +/* 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..6e533a079 --- /dev/null +++ b/sw/source/core/inc/unoevent.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 : public SvDetachedEventDescriptor +{ + //XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; +protected: + 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 : public SvEventDescriptor +{ + SwXFrame& rFrame; + +public: + SwFrameEventDescriptor( SwXTextFrame& rFrameRef ); + SwFrameEventDescriptor( SwXTextGraphicObject& rGraphicRef ); + SwFrameEventDescriptor( SwXTextEmbeddedObject& rObjectRef ); + + virtual ~SwFrameEventDescriptor() override; + + virtual OUString SAL_CALL getImplementationName() override; + +protected: + virtual void setMacroItem(const SvxMacroItem& rItem) override; + virtual const SvxMacroItem& getMacroItem() override; + virtual sal_uInt16 getMacroItemWhich() const override; +}; + +class SwFrameStyleEventDescriptor : public SvEventDescriptor +{ + sw::ICoreFrameStyle& m_rStyle; + +public: + SwFrameStyleEventDescriptor( sw::ICoreFrameStyle& rStyle ); + + virtual ~SwFrameStyleEventDescriptor() override; + + virtual OUString SAL_CALL getImplementationName() override; + +protected: + 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..47f9f0c4d --- /dev/null +++ b/sw/source/core/inc/unofield.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_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 + : 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; + +class SwXTextField + : 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 + : 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..06f68aba4 --- /dev/null +++ b/sw/source/core/inc/unoflatpara.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_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 <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 + : 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: + 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; + + sal_uLong mnCurrentNode; // used for non-automatic mode + sal_uLong 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..2bb13a66f --- /dev/null +++ b/sw/source/core/inc/unofldmid.h @@ -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_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_IS_FIELD_USED 32 +#define FIELD_PROP_IS_FIELD_DISPLAYED 33 + +#define FIELD_PROP_TEXT 34 + +#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..79014ecba --- /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() throw() override { OWeakObject::acquire(); } + virtual void SAL_CALL release() throw() 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/unofreg.hxx b/sw/source/core/inc/unofreg.hxx new file mode 100644 index 000000000..afb8e6dcd --- /dev/null +++ b/sw/source/core/inc/unofreg.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UNOFREG_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_UNOFREG_HXX + +#include <sal/config.h> +#include <sfx2/sfxmodelfactory.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +namespace com::sun::star::lang { class XMultiServiceFactory; } + +// writer documents +css::uno::Sequence< OUString > SwTextDocument_getSupportedServiceNames() throw(); +OUString SwTextDocument_getImplementationName() throw(); +/// @throws css::uno::Exception +css::uno::Reference< css::uno::XInterface > SwTextDocument_createInstance( const css::uno::Reference< css::lang::XMultiServiceFactory > &rSMgr, SfxModelFlags _nCreationFlags ); + +#endif + +/* 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..c87460a3d --- /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 + : 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 + : public SwXDocumentIndexMark_Base +{ + +private: + + class Impl; + ::sw::UnoImplPtr<Impl> m_pImpl; + + virtual ~SwXDocumentIndexMark() override; + + SwXDocumentIndexMark(SwDoc & rDoc, + SwTOXType & rType, 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/unometa.hxx b/sw/source/core/inc/unometa.hxx new file mode 100644 index 000000000..87c351b5d --- /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 + : 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); + +#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..e3712cf28 --- /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 : public SwClient + { + FrameClient(SwModify* 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 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..7817d7d16 --- /dev/null +++ b/sw/source/core/inc/unoport.hxx @@ -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 . + */ +#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 +}; + +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; + 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; + + 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 SfxItemPropertySimpleEntry& 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 SetCollapsed(bool bSet) { m_bIsCollapsed = bSet;} + + SwTextPortionType GetTextPortionType() const { return m_ePortionType; } + + SwUnoCursor& GetCursor() const + { return *m_pUnoCursor; } +}; + +class SwXTextPortionEnumeration + : 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; + +protected: + 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 const & 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 : public SwXTextPortion +{ +private: + SwRangeRedline const& m_rRedline; + + /// @throws css::uno::RuntimeException + void 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( + OUString const& 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..c65b65009 --- /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 + : 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..370ed909f --- /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 + : 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..bfbded136 --- /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: + 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..aff1e7012 --- /dev/null +++ b/sw/source/core/inc/viewimp.hxx @@ -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 . + */ +#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 <vector> +#include <memory> + +class OutputDevice; +class SwViewShell; +class SwFlyFrame; +class SwViewOption; +class SwRegionRects; +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_pRegion; // Collector of Paintrects from the LayAction + + 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 ); + SwRegionRects *GetRegion() { return m_pRegion.get(); } + void DelRegion(); + + /// 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..54f051247 --- /dev/null +++ b/sw/source/core/inc/visiturl.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_INC_VISITURL_HXX +#define INCLUDED_SW_SOURCE_CORE_INC_VISITURL_HXX + +#include <svl/lstner.hxx> + +class SwDoc; + +class SwURLStateChanged : public SfxListener +{ + SwDoc* pDoc; +public: + SwURLStateChanged( SwDoc* pD ); + 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..c5b4df884 --- /dev/null +++ b/sw/source/core/inc/wrong.hxx @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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/util/Color.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/uno/Any.hxx> + +#include <vector> + +#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; + 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()) + { + const OUString colorKey("LineColor"); + css::uno::Any aLineColor = xPropertyBag->getValue(colorKey); + css::util::Color lineColor = 0; + + if (aLineColor >>= lineColor) + { + return Color( 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()) + { + const OUString typeKey("LineType"); + css::uno::Any aLineType = xPropertyBag->getValue(typeKey); + ::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()) + { + const OUString colorKey("LineColor"); + css::uno::Any aLineColor = xPropertyBag->getValue(colorKey); + css::util::Color lineColor = 0; + + if (aLineColor >>= lineColor) + { + return Color( 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()) + { + const OUString typeKey("LineType"); + css::uno::Any aLineType = xPropertyBag->getValue(typeKey); + ::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. + 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 static_cast<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..a908300b0 --- /dev/null +++ b/sw/source/core/layout/anchoreddrawobject.cxx @@ -0,0 +1,918 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + +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 (mpOldPageFrame && mpOldPageFrame->getRootFrame()->IsAnyShellAccessible()) + { + mpOldPageFrame->getRootFrame()->GetCurrShell()->Imp()->MoveAccessible( + nullptr, mpAnchoredDrawObj->GetDrawObj(), maOldObjRect); + } +} + +// --> #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() : + SwAnchoredObject(), + 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() ) + { + 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() ) + { + if ( _pPageFrame->GetUpper() ) + { + // --> #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() ) + { + mbValidPos = false; + // --> #i68520# + InvalidateObjRectWithSpaces(); + + // --> #i44339# - check, if anchor frame exists. + if ( GetAnchorFrame() ) + { + // --> #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 ( dynamic_cast< const SwTextFrame* >(GetAnchorFrame()) != 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 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. + 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(); + + long nTargetWidth = aCurrObjRect.GetWidth( ); + if ( GetDrawObj( )->GetRelativeWidth( ) ) + { + 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(); + } + + long nTargetHeight = aCurrObjRect.GetHeight(); + if (bCheck) + { + 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_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::makeAny(xShape->getSize())); + } + } + + pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified); + } + } + return 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); + SfxItemSet items(GetFrameFormat().GetDoc()->GetAttrPool(), svl::Items<RES_VERT_ORIENT, RES_HORI_ORIENT>()); + 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 ) + { + if ( GetPageFrame() ) + GetPageFrame()->RemoveDrawObjFromPage( *this ); + pPageFrame->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..df9d0cb08 --- /dev/null +++ b/sw/source/core/layout/anchoredobject.cxx @@ -0,0 +1,900 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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 ), + maRelPos(), + maLastCharRect(), + mnLastTopOfLine( 0 ), + mpVertPosOrientFrame( nullptr ), + // --> #i28701# + mbPositioningInProgress( false ), + mbConsiderForTextWrap( false ), + mbPositionLocked( false ), + // --> #i40147# + mbKeepPositionLockedForSection( false ), + mbRestartLayoutProcess( false ), + // --> #i35911# + mbClearedEnvironment( false ), + // --> #i3317# + mbTmpConsiderWrapInfluence( false ), + // --> #i68520# + maObjRectWithSpaces(), + mbObjRectWithSpacesValid( false ), + maLastObjRect() +{ +} + +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 ) + { + // 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( 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() ) + { + const SwFormatAnchor& rAnch = GetFrameFormat().GetAnchor(); + if ( (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) && + rAnch.GetContentAnchor() ) + { + // --> 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 ) + { + // 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() ) ) + { + if ( nTopOfLine != mnLastTopOfLine ) + { + // 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() - long(rUL.GetUpper()), 0L )); + maObjRectWithSpaces.Left( std::max( maObjRectWithSpaces.Left()- rLR.GetLeft(), 0L )); + 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() ) + { + 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(); + // determine start index + 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 ( 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() ); +} + +// --> #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().IsOver( 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; +} + +/* 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..d9f37a669 --- /dev/null +++ b/sw/source/core/layout/atrfrm.cxx @@ -0,0 +1,3656 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/WrapTextMode.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/text/TextGridMode.hpp> +#include <sal/log.hxx> +#include <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <svtools/unoimap.hxx> +#include <vcl/imap.hxx> +#include <vcl/imapobj.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/syslocale.hxx> +#include <frmfmt.hxx> +#include <unocoll.hxx> +#include <unosett.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 <docary.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 <sortedobjs.hxx> +#include <HandleAnchorNodeChg.hxx> +#include <calbck.hxx> +#include <pagedeschint.hxx> +#include <drawdoc.hxx> +#include <hints.hxx> +#include <frameformats.hxx> + +#include <ndtxt.hxx> + +#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx> +#include <svl/itemiter.hxx> +#include <wrtsh.hxx> +#include <txtfld.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 ) + { + // 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(); + sal_uInt32 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(long lMult, 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( convertMm100ToTwip(aTmp.Height()) ); + aTmp.setWidth( convertMm100ToTwip(aTmp.Width()) ); + } + 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 = convertMm100ToTwip(nWd); + 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 = convertMm100ToTwip(nHg); + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFrameSize")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + + std::stringstream aSize; + aSize << GetSize(); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(aSize.str().c_str())); + + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameHeightType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameHeightType)).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameWidthType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameWidthType)).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthPercent"), BAD_CAST(OString::number(m_nWidthPercent).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eWidthPercentRelation"), BAD_CAST(OString::number(m_eWidthPercentRelation).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nHeightPercent"), BAD_CAST(OString::number(m_nHeightPercent).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eHeightPercentRelation"), BAD_CAST(OString::number(m_eHeightPercentRelation).getStr())); + + 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<SwModify*>(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<SwModify*>(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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContent")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("startNode"), BAD_CAST(OString::number(m_pStartNode->GetNode().GetIndex()).getStr())); + 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& rModify, const SfxHint& rHint ) +{ + SwClient::SwClientNotify(rModify, rHint); + const SwPageDescHint* pHint = dynamic_cast<const SwPageDescHint*>(&rHint); + if ( pHint ) + { + // mba: shouldn't that be broadcasted also? + SwFormatPageDesc aDfltDesc( pHint->GetPageDesc() ); + SwPageDesc* pDesc = pHint->GetPageDesc(); + const SwModify* 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 + { + OSL_FAIL( "What kind of SwModify is this?" ); + RegisterToPageDesc( *pDesc ); + } + } + else + // there could be an Undo-copy + RegisterToPageDesc( *pDesc ); + } +} + +void SwFormatPageDesc::RegisterToPageDesc( SwPageDesc& rDesc ) +{ + rDesc.Add( this ); +} + +void SwFormatPageDesc::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( !m_pDefinedIn ) + return; + + const sal_uInt16 nWhichId = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + switch( nWhichId ) + { + case RES_OBJECTDYING: + //The Pagedesc where I'm registered dies, therefore I unregister + //from that format. During this I get deleted! + if( typeid(SwFormat) == typeid( m_pDefinedIn )) + { + bool const bResult = + static_cast<SwFormat*>(m_pDefinedIn)->ResetFormatAttr(RES_PAGEDESC); + OSL_ENSURE( bResult, "FormatPageDesc not deleted" ); + } + else if( typeid(SwContentNode) == typeid( m_pDefinedIn )) + { + bool const bResult = static_cast<SwContentNode*>(m_pDefinedIn) + ->ResetAttr(RES_PAGEDESC); + OSL_ENSURE( bResult, "FormatPageDesc not deleted" ); + } + break; + + default: + /* do nothing */; + } +} + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatPageDesc")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + if (m_oNumOffset) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST(OString::number(*m_oNumOffset).getStr())); + else + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST("none")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("pPageDesc"), "%p", GetPageDesc()); + if (const SwPageDesc* pPageDesc = GetPageDesc()) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(pPageDesc->GetName().toUtf8().getStr())); + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwColumn")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWish"), BAD_CAST(OString::number(m_nWish).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUpper"), BAD_CAST(OString::number(0).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLower"), BAD_CAST(OString::number(0).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLeft"), BAD_CAST(OString::number(m_nLeft).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nRight"), BAD_CAST(OString::number(m_nRight).getStr())); + 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 ) + { + 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); + + //Convert the current width to the requested width. + for (SwColumn &rCol: m_aColumns) + { + long nTmp = rCol.GetWishWidth(); + nTmp *= GetWishWidth(); + 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 = new SwXTextColumns(*this); + 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( static_cast<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 (convertMm100ToTwip(pArray[i].LeftMargin)); + aCol.SetRight(convertMm100ToTwip(pArray[i].RightMargin)); + m_aColumns.insert(m_aColumns.begin() + i, aCol); + } + bRet = true; + m_nWidth = nWidthSum; + m_bOrtho = false; + + auto pSwColums = comphelper::getUnoTunnelImplementation<SwXTextColumns>(xCols); + if(pSwColums) + { + m_bOrtho = pSwColums->IsAutomaticWidth(); + m_nLineWidth = pSwColums->GetSepLineWidth(); + m_aLineColor = pSwColums->GetSepLineColor(); + m_nLineHeight = pSwColums->GetSepLineHeightRelative(); + switch ( pSwColums->GetSepLineStyle() ) + { + default: + case 0: m_eLineStyle = SvxBorderLineStyle::NONE; break; + case 1: m_eLineStyle = SvxBorderLineStyle::SOLID; break; + case 2: m_eLineStyle = SvxBorderLineStyle::DOTTED; break; + case 3: m_eLineStyle = SvxBorderLineStyle::DASHED; break; + } + if(!pSwColums->GetSepLineIsOn()) + m_eAdj = COLADJ_NONE; + else switch(pSwColums->GetSepLineVertAlign()) + { + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatCol")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eLineStyle"), BAD_CAST(OString::number(static_cast<sal_Int16>(m_eLineStyle)).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineWidth"), BAD_CAST(OString::number(m_nLineWidth).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aLineColor"), BAD_CAST(m_aLineColor.AsRGBHexString().toUtf8().getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineHeight"), BAD_CAST(OString::number(m_nLineHeight).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eAdj"), BAD_CAST(OString::number(m_eAdj).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidth"), BAD_CAST(OString::number(m_nWidth).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthAdjustValue"), BAD_CAST(OString::number(m_aWidthAdjustValue).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOrtho"), BAD_CAST(OString::boolean(m_bOrtho).getStr())); + + xmlTextWriterStartElement(pWriter, BAD_CAST("aColumns")); + for (const SwColumn& rColumn : m_aColumns) + rColumn.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatSurround")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + 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); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr())); + + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bAnchorOnly"), BAD_CAST(OString::boolean(m_bAnchorOnly).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bContour"), BAD_CAST(OString::boolean(m_bContour).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOutside"), BAD_CAST(OString::boolean(m_bOutside).getStr())); + + 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 = convertMm100ToTwip(nVal); + SetPos( nVal ); + } + break; + default: + OSL_ENSURE( false, "unknown MemberId" ); + bRet = false; + } + return bRet; +} + +void SwFormatVertOrient::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatVertOrient")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nYPos"), BAD_CAST(OString::number(m_nYPos).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr())); + 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 = convertMm100ToTwip(nVal); + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatHoriOrient")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nXPos"), BAD_CAST(OString::number(m_nXPos).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bPosToggle"), BAD_CAST(OString::boolean(m_bPosToggle).getStr())); + 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( ++m_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( ++m_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 = ++m_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::m_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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatAnchor")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + + if (m_pContentAnchor) + { + std::stringstream aContentAnchor; + aContentAnchor << *m_pContentAnchor; + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_pContentAnchor"), BAD_CAST(aContentAnchor.str().c_str())); + } + else + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pContentAnchor"), "%p", m_pContentAnchor.get()); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eAnchorType"), BAD_CAST(OString::number(static_cast<int>(m_eAnchorId)).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPageNumber"), BAD_CAST(OString::number(m_nPageNumber).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nOrder"), BAD_CAST(OString::number(m_nOrder).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nOrderCounter"), BAD_CAST(OString::number(m_nOrderCounter).getStr())); + OUString aPresentation; + IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag()); + GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr())); + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatNoBalancedColumns")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr())); + 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==( rAttr ) && + 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: + { + sal_Int32 nTmp = 0; + bRet = (rVal >>= nTmp); + if( bRet ) + SetColor( Color(nTmp) ); + } + break; + case MID_GRID_LINES: + { + sal_Int16 nTmp = 0; + bRet = (rVal >>= nTmp); + if( bRet && (nTmp >= 0) ) + SetLines( static_cast<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 = convertMm100ToTwip( nTmp ); + 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( static_cast<sal_uInt16>(nTmp) ); + } + else if( (nMemberId & ~CONVERT_TWIPS) == MID_GRID_BASEWIDTH ) + { + nTmp = std::max<sal_Int32>(nTmp, MIN_TEXTGRID_SIZE); + SetBaseWidth( static_cast<sal_uInt16>(nTmp) ); + } + else + SetRubyHeight( static_cast<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 sal_uInt16* pWhichRange) +: SwFormat(rPool, pFormatNm, (pWhichRange ? pWhichRange : aFrameFormatSetRange), pDrvdFrame, nFormatWhich), + m_wXObject(), + maFillAttributes(), + m_ffList(nullptr), + m_pOtherTextBoxFormat(nullptr) +{ +} + +SwFrameFormat::SwFrameFormat( + SwAttrPool& rPool, + const OUString &rFormatNm, + SwFrameFormat *pDrvdFrame, + sal_uInt16 nFormatWhich, + const sal_uInt16* pWhichRange) +: SwFormat(rPool, rFormatNm, (pWhichRange ? pWhichRange : aFrameFormatSetRange), pDrvdFrame, nFormatWhich), + m_wXObject(), + maFillAttributes(), + m_ffList(nullptr), + m_pOtherTextBoxFormat(nullptr) +{ +} + +SwFrameFormat::~SwFrameFormat() +{ + if( !GetDoc()->IsInDtor()) + { + const SwFormatAnchor& rAnchor = GetAnchor(); + if (rAnchor.GetContentAnchor() != nullptr) + { + rAnchor.GetContentAnchor()->nNode.GetNode().RemoveAnchoredFly(this); + } + } + + if( nullptr != m_pOtherTextBoxFormat ) + { + m_pOtherTextBoxFormat->SetOtherTextBoxFormat( nullptr ); + m_pOtherTextBoxFormat = nullptr; + } +} + +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"); + + 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) { + SwStringMsgPoolItem aNew( RES_NAME_CHANGED, rNewName ); + ModifyNotification( &aOld, &aNew ); + } + } + else + SwFormat::SetName( rNewName, bBroadcast ); +} + +void SwFrameFormat::SetOtherTextBoxFormat( SwFrameFormat *pFormat ) +{ + if( nullptr != pFormat ) + { + assert( (Which() == RES_DRAWFRMFMT && pFormat->Which() == RES_FLYFRMFMT) + || (Which() == RES_FLYFRMFMT && pFormat->Which() == RES_DRAWFRMFMT) ); + assert( nullptr == m_pOtherTextBoxFormat ); + } + else + { + assert( nullptr != m_pOtherTextBoxFormat ); + } + bool bChanged = m_pOtherTextBoxFormat != pFormat; + m_pOtherTextBoxFormat = pFormat; + + SdrObject* pObj = FindSdrObject(); + + if (pObj) + { + SwFlyDrawObj* pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pObj); + + if (pSwFlyDraw) + pSwFlyDraw->SetTextBox(true); + } + + if (m_pOtherTextBoxFormat && bChanged && Which() == RES_DRAWFRMFMT) + { + // This is a shape of a shape+frame pair and my frame has changed. Make sure my content is + // in sync with the frame's content. + if (GetAttrSet().GetContent() != m_pOtherTextBoxFormat->GetAttrSet().GetContent()) + { + SwAttrSet aSet(GetAttrSet()); + SwFormatContent aContent(m_pOtherTextBoxFormat->GetAttrSet().GetContent()); + aSet.Put(aContent); + SetFormatAttr(aSet); + } + } +} + +bool SwFrameFormat::supportsFullDrawingLayerFillAttributeSet() const +{ + return true; +} + +void SwFrameFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if (pNew) + { + SwFormatHeader const *pH = nullptr; + SwFormatFooter const *pF = nullptr; + + const sal_uInt16 nWhich = pNew->Which(); + + if( RES_ATTRSET_CHG == nWhich ) + { + static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( + RES_HEADER, false, reinterpret_cast<const SfxPoolItem**>(&pH) ); + static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( + RES_FOOTER, false, reinterpret_cast<const SfxPoolItem**>(&pF) ); + + // reset fill information + if (maFillAttributes && supportsFullDrawingLayerFillAttributeSet()) + { + SfxItemIter aIter(*static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()); + bool bReset(false); + + 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(); + } + } + } + else if(RES_FMT_CHG == nWhich) + { + // reset fill information on format change (e.g. style changed) + if (maFillAttributes && supportsFullDrawingLayerFillAttributeSet()) + { + maFillAttributes.reset(); + } + } + else if( RES_HEADER == nWhich ) + pH = static_cast<const SwFormatHeader*>(pNew); + else if( RES_FOOTER == nWhich ) + pF = static_cast<const SwFormatFooter*>(pNew); + + 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::Modify( pOld, pNew ); + + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached uno object + SetXObject(uno::Reference<uno::XInterface>(nullptr)); + } + + const SwPosition* oldAnchorPosition = nullptr; + const SwPosition* newAnchorPosition = nullptr; + if( pNew && pNew->Which() == RES_ATTRSET_CHG ) + { + const SfxPoolItem* tmp = nullptr; + static_cast< const SwAttrSetChg* >(pNew)->GetChgSet()->GetItemState( RES_ANCHOR, false, &tmp ); + if( tmp ) + { + newAnchorPosition = static_cast< const SwFormatAnchor* >( tmp )->GetContentAnchor(); + assert(newAnchorPosition == nullptr || // style's set must not contain position! + static_cast<SwAttrSetChg const*>(pNew)->GetTheChgdSet() == &m_aSet); + } + } + if( pNew && pNew->Which() == RES_ANCHOR ) + newAnchorPosition = static_cast< const SwFormatAnchor* >( pNew )->GetContentAnchor(); + if( pOld && pOld->Which() == RES_ATTRSET_CHG ) + { + const SfxPoolItem* tmp = nullptr; + static_cast< const SwAttrSetChg* >(pOld)->GetChgSet()->GetItemState( RES_ANCHOR, false, &tmp ); + if( tmp ) + { + oldAnchorPosition = static_cast< const SwFormatAnchor* >( tmp )->GetContentAnchor(); + assert(oldAnchorPosition == nullptr || // style's set must not contain position! + static_cast<SwAttrSetChg const*>(pOld)->GetTheChgdSet() == &m_aSet); + } + } + if( pOld && pOld->Which() == RES_ANCHOR ) + oldAnchorPosition = static_cast< const SwFormatAnchor* >( pOld )->GetContentAnchor(); + if( oldAnchorPosition != nullptr && ( newAnchorPosition == nullptr || oldAnchorPosition->nNode.GetIndex() != newAnchorPosition->nNode.GetIndex())) + { + oldAnchorPosition->nNode.GetNode().RemoveAnchoredFly(this); + } + if( newAnchorPosition != nullptr && ( oldAnchorPosition == nullptr || oldAnchorPosition->nNode.GetIndex() != newAnchorPosition->nNode.GetIndex())) + { + newAnchorPosition->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( dynamic_cast<const SwSectionFormat*>( this ) != nullptr ) + { + // get the Frame using Node2Layout + const SwSectionNode* pSectNd = static_cast<const SwSectionFormat*>(this)->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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFrameFormat")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + 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) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("which"), BAD_CAST(pWhich)); + + if (m_pOtherTextBoxFormat) + { + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("OtherTextBoxFormat"), "%p", m_pOtherTextBoxFormat); + } + + GetAttrSet().dumpAsXml(pWriter); + + if (const SdrObject* pSdrObject = FindSdrObject()) + pSdrObject->dumpAsXml(pWriter); + + xmlTextWriterEndElement(pWriter); +} + +void SwFrameFormats::dumpAsXml(xmlTextWriterPtr pWriter, const char* pName) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST(pName)); + for (const SwFrameFormat *pFormat : m_PosIndex) + pFormat->dumpAsXml(pWriter); + 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; + + SwModify *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 ) + { + SwIterator<SwFrame, SwModify, 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( dynamic_cast<const SwFlyFrame*>( pObj) != 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 dynamic_cast<SwAnchoredObject*>(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; + } + + if( bBroadcast ) + { + SwStringMsgPoolItem aOld( RES_TITLE_CHANGED, pMasterObject->GetTitle() ); + SwStringMsgPoolItem aNew( RES_TITLE_CHANGED, rTitle ); + pMasterObject->SetTitle( rTitle ); + ModifyNotification( &aOld, &aNew ); + } + else + { + pMasterObject->SetTitle( rTitle ); + } +} + +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::SetObjDescription( const OUString& rDescription, bool bBroadcast ) +{ + SdrObject* pMasterObject = FindSdrObject(); + OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::SetDescription(..)> - missing <SdrObject> instance" ); + msDesc = rDescription; + if ( !pMasterObject ) + { + return; + } + + if( bBroadcast ) + { + SwStringMsgPoolItem aOld( RES_DESCRIPTION_CHANGED, pMasterObject->GetDescription() ); + SwStringMsgPoolItem aNew( RES_DESCRIPTION_CHANGED, rDescription ); + pMasterObject->SetDescription( rDescription ); + ModifyNotification( &aOld, &aNew ); + } + else + { + pMasterObject->SetDescription( rDescription ); + } +} + +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 && + (aBackground->GetColor().GetTransparency() != 0) && + (aBackground->GetColor() != COL_TRANSPARENT) + ) + { + return true; + } + else + { + const GraphicObject *pTmpGrf = aBackground->GetGraphicObject(); + if ( pTmpGrf && + (pTmpGrf->GetAttr().GetTransparency() != 0) + ) + { + 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 && + (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); + } +} + +SwHandleAnchorNodeChg::~SwHandleAnchorNodeChg() COVERITY_NOEXCEPT_FALSE +{ + 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(); + + mpWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); +} + +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 MapMode aSrc ( MapUnit::MapTwip ); + const MapMode aDest( MapUnit::Map100thMM ); + aOrigSz = OutputDevice::LogicToLogic( aOrigSz, aSrc, aDest ); + aActSz = OutputDevice::LogicToLogic( aActSz, aSrc, aDest ); + aPos -= pRef->getFrameArea().Pos(); + aPos -= pRef->getFramePrintArea().Pos(); + aPos = OutputDevice::LogicToLogic( 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; +} + +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()); + sal_uLong const count(rNodes.Count()); + for (sal_uLong i = 0; i != count; ++i) + { + SwNode const*const pNode(rNodes[i]); + std::vector<SwFrameFormat*> const*const pFlys(pNode->GetAnchoredFlys()); + if (pFlys) + { + for (const auto& rpFly : *pFlys) + { + SwFormatAnchor const& rAnchor((*rpFly).GetAnchor(false)); + assert(&rAnchor.GetContentAnchor()->nNode.GetNode() == pNode); + } + } + } + SwFrameFormats const*const pSpzFrameFormats(rDoc.GetSpzFrameFormats()); + if (pSpzFrameFormats) + { + 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*const pFlys(rNode.GetAnchoredFlys()); + assert(std::find(pFlys->begin(), pFlys->end(), *it) != pFlys->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..6fb4ac868 --- /dev/null +++ b/sw/source/core/layout/calcmove.cxx @@ -0,0 +1,2232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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(); + +#if BOOST_VERSION < 105600 + std::list<FlowFrameJoinLockGuard> tabGuard; + std::list<SwFrameDeleteGuard> rowGuard; +#else + std::optional<FlowFrameJoinLockGuard> tabGuard; + std::optional<SwFrameDeleteGuard> rowGuard; +#endif + SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr; + + if ( bTab ) + { +#if BOOST_VERSION < 105600 + tabGuard.emplace_back(static_cast<SwTabFrame*>(this)); // tdf#125741 +#else + tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741 +#endif + pThis = static_cast<SwTabFrame*>(this); + } + else if (IsRowFrame()) + { +#if BOOST_VERSION < 105600 + rowGuard.emplace_back(this); // tdf#125741 keep this alive +#else + rowGuard.emplace(this); // tdf#125741 keep this alive +#endif + } + 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() ) + { + 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, long& rBot) +{ + // And then there can be paragraph anchored frames that sit below their paragraph. + long nMax = 0; + for (SwAnchoredObject* pObj : rSortedObjs) + { + // #i28701# - consider changed type of <SwSortedObjs> + // entries. + long nTmp = 0; + if ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pObj); + 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 long nTop, const 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. + long nBot = getFrameArea().Top() + nTop; + const SwFrame *pFrame = Lower(); + while (pFrame) + { + 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::unique_ptr<SwBorderAttrAccess> pAccess; + 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 (!pAccess) + { + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->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 long nTop = pAttrs->CalcTopLine() + aBorder.Height(); + const long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height(); + + long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0; + const auto nDefWidth = pSh->GetBrowseWidth(); + if (nWidth < nDefWidth) + nWidth = nDefWidth; + nWidth += + 2 * aBorder.Width(); + nWidth = std::max( nWidth, 2L * aBorder.Width() + 4*MM50 ); + + { + 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. + 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) + { + 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::unique_ptr<SwBorderAttrAccess> pAccess; + 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 long nDiff = nPrtWidth - (getFrameArea().*fnRect->fnGetWidth)(); + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + + if( IsNeighbourFrame() && IsRightToLeft() ) + { + (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 ( !pAccess ) + { + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->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()) + { + sal_uLong 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() ) + { + 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 long nLeft = rAttrs.CalcLeft( this ); + const 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()) + 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 = dynamic_cast<const SwFlyFrame*>( pObj) != 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() ); + 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( 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; + } + + auto xDeleteGuard = std::make_unique<SwFrameDeleteGuard>(this); + LockJoin(); + 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::unique_ptr<SwContentNotify, o3tl::default_delete<SwContentNotify>> pNotify(new SwContentNotify( 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) ) ) + { + pNotify->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() ) + { + dynamic_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 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 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 long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper()); + 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 ) + pNotify->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() ) + { + if( !bMoveable && IsInTab() ) + { + long nDiff = -aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) ); + long nReal = GetUpper()->Grow( nDiff ); + if( nReal ) + continue; + } + 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 ) ) + { + 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 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(); + xDeleteGuard.reset(); + if ( bMovedFwd || bMovedBwd ) + pNotify->SetInvaKeep(); + if ( bMovedFwd ) + { + pNotify->SetInvalidatePrevPrtArea(); + } + pNotify.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 ); + + pTmpFrame->RemoveFromLayout(); + pTmpFrame->InsertBefore( pUp, pOldNext ); + pTmpFrame->InvalidateInfFlags(); // restore flags + } + else + { + bRet = pFrame->WouldFit( nSpace, bSplit, false ); + 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 ); + 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..dfb438953 --- /dev/null +++ b/sw/source/core/layout/colfrm.cxx @@ -0,0 +1,445 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 ) + { + 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; + 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 ) + { + 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..f395efeae --- /dev/null +++ b/sw/source/core/layout/dbg_lay.cxx @@ -0,0 +1,923 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <tools/stream.hxx> + +PROT SwProtocol::nRecord = PROT::FileInit; +SwImplProtocol* SwProtocol::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> pStream; // output stream + std::unique_ptr<std::set<sal_uInt16>> pFrameIds; // which FrameIds shall be logged ( NULL == all) + std::vector<long> aVars; // variables + OStringBuffer aLayer; // indentation of output (" " per start/end) + SwFrameType nTypes; // which types shall be logged + sal_uInt16 nLineCount; // printed lines + sal_uInt16 nMaxLines; // max lines to be printed + sal_uInt8 nInitFile; // range (FrameId,FrameType,Record) during reading of the INI file + sal_uInt8 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( 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( !pStream ) NewStream(); } +}; + +/* Through the PROTOCOL_ENTER macro a SwEnterLeave object gets created. If the + * current function should be logged a 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* pFrame; // the frame + PROT nFunction; // the function + DbgAction nAction; // the action if needed + void* pParam; // further parameter +public: + SwImplEnterLeave( const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar ) + : pFrame( pF ), nFunction( nFunct ), nAction( nAct ), pParam( pPar ) {} + virtual ~SwImplEnterLeave() {} + virtual void Enter(); // message when entering + virtual void Leave(); // message when leaving +}; + +namespace { + +class SwSizeEnterLeave : public SwImplEnterLeave +{ + long nFrameHeight; +public: + SwSizeEnterLeave( const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar ) + : SwImplEnterLeave( pF, nFunct, nAct, pPar ), nFrameHeight( pF->getFrameArea().Height() ) {} + + virtual void Leave() override; // resize message +}; + +class SwUpperEnterLeave : public SwImplEnterLeave +{ + sal_uInt16 nFrameId; +public: + SwUpperEnterLeave( const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar ) + : SwImplEnterLeave( pF, nFunct, nAct, pPar ), nFrameId( 0 ) {} + + virtual void Enter() override; // message + virtual void Leave() override; // message of FrameId from upper +}; + +class SwFrameChangesLeave : public SwImplEnterLeave +{ + SwRect aFrame; +public: + SwFrameChangesLeave( const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar ) + : SwImplEnterLeave( pF, nFunct, nAct, pPar ), 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 ) + { + nRecord &= ~nFunction; // Don't log this function any longer + nRecord &= ~PROT::Init; // Always reset PROT::Init + return; + } + nRecord |= nFunction; // Activate logging of this function + nRecord &= ~PROT::Init; // Always reset PROT::Init + if( pImpl ) + pImpl->ChkStream(); + } + if( !pImpl ) // Create Impl object if needed + pImpl = new SwImplProtocol(); + 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() +{ + nRecord = PROT::FileInit; + OUString aName("dbg_lay.go"); + SvFileStream aStream( aName, StreamMode::READ ); + if( aStream.IsOpen() ) + { + pImpl = new SwImplProtocol(); + pImpl->FileInit(); + } + aStream.Close(); +} + +// End of logging + +void SwProtocol::Stop() +{ + if( pImpl ) + { + delete pImpl; + pImpl = nullptr; + if( pFntCache ) + pFntCache->Flush(); + } + nRecord = PROT::FileInit; +} + +SwImplProtocol::SwImplProtocol() + : nTypes( FRM_ALL ), + nLineCount( 0 ), nMaxLines( USHRT_MAX ), nTestMode( 0 ) +{ + NewStream(); +} + +bool SwImplProtocol::NewStream() +{ + OUString aName("dbg_lay.out"); + nLineCount = 0; + pStream.reset( new SvFileStream( aName, StreamMode::WRITE | StreamMode::TRUNC ) ); + if( pStream->GetError() ) + { + pStream.reset(); + } + return nullptr != pStream; +} + +SwImplProtocol::~SwImplProtocol() +{ + if( pStream ) + { + pStream->Close(); + pStream.reset(); + } + pFrameIds.reset(); + 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 + { + OString aTmp = rLine.getToken(0, ']'); + if (aTmp == "[frmid") // section FrameIds + { + nInitFile = 1; + pFrameIds.reset(); // default: log all frames + } + else if (aTmp == "[frmtype")// section types + { + nInitFile = 2; + nTypes = FRM_ALL; // default: log all frame types + } + else if (aTmp == "[record")// section functions + { + nInitFile = 3; + SwProtocol::SetRecord( PROT::FileInit );// default: don't log any function + } + else if (aTmp == "[test")// section functions + { + nInitFile = 4; // default: + nTestMode = 0; // log outside of test formatting + } + else if (aTmp == "[max")// Max number of lines + { + nInitFile = 5; // default: + nMaxLines = USHRT_MAX; + } + else if (aTmp == "[var")// variables + { + nInitFile = 6; + } + else + nInitFile = 0; // oops: unknown section? + rLine = rLine.copy(aTmp.getLength() + 1); + } + + // spaces (or tabs) are the delimiter + sal_Int32 nIndex = 0; + do + { + OString aTok = rLine.getToken( 0, ' ', nIndex ); + bool bNo = false; + if( !aTok.isEmpty() && '!' == aTok[0] ) + { + bNo = true; // remove this function/type + aTok = aTok.copy(1); + } + if( !aTok.isEmpty() ) + { + sal_Int64 nVal = aTok.toInt64(); + switch ( nInitFile ) + { + case 1: InsertFrame( sal_uInt16( nVal ) ); // add FrameId + break; + case 2: { + SwFrameType nNew = static_cast<SwFrameType>(nVal); + if( bNo ) + nTypes &= ~nNew; // remove type + else + 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 ) + nTestMode &= ~nNew; // reset test mode + else + nTestMode |= nNew; // set test mode + } + break; + case 5: nMaxLines = static_cast<sal_uInt16>(nVal); + break; + case 6: aVars.push_back( nVal ); + break; + } + } + } + while ( nIndex >= 0 ); +} + +/// read the file "dbg_lay.ini" in the current directory and evaluate it. +void SwImplProtocol::FileInit() +{ + OUString aName("dbg_lay.ini"); + SvFileStream aStream( aName, StreamMode::READ ); + if( aStream.IsOpen() ) + { + OString aLine; + 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 += OString(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 (sal_Int32(length) < 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 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 SwModify* pModify = pTable->GetRegisteredIn(); + const SwFormat* pFormat = static_cast<const SwFormat*>(pModify); + 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 (sal_uLong 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: pFrameIds.reset(); break; + case 4: pStream.reset(); break; + } + return; + } + if( !pStream && !NewStream() ) + return; // still no stream + + if( pFrameIds && !pFrameIds->count( sal_uInt16(lcl_GetFrameId( pFrame )) ) ) + return; // doesn't belong to the wished FrameIds + + if( !(pFrame->GetType() & nTypes) ) + return; // the type is unwanted + + if( 1 == nTestMode && nFunction != PROT::TestFormat ) + return; // we may only log inside a test formatting + bool bTmp = false; + OStringBuffer aOut(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, 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, 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, aLayer, nAct ); + break; + case PROT::ShrinkTest: + aOut.append("SwFrame::Shrink (test)"); + lcl_Start( aOut, 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, aLayer, nAct ); + if( pParam ) + { + aOut.append(' '); + aOut.append(static_cast<sal_Int64>(*static_cast<long*>(pParam))); + } + break; + case PROT::PrintArea: aOut.append("PROT::PrintArea"); + lcl_Start( aOut, aLayer, nAct ); + break; + case PROT::Size: aOut.append("PROT::Size"); + lcl_Start( aOut, 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, 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, aLayer, nAct ); + if( DbgAction::Start == nAct ) + nTestMode |= 2; + else + 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()); + pStream->WriteOString( aOut.makeStringAndClear() ); + (*pStream) << endl; // output + pStream->Flush(); // to the disk, so we can read it immediately + if( ++nLineCount >= 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( !pFrameIds ) + pFrameIds.reset( new std::set<sal_uInt16> ); + if( pFrameIds->count( nId ) ) + return; + 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( !pFrameIds ) + return; + 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( pFrame, nFunction, DbgAction::Start, pParam ); +} + +void SwImplEnterLeave::Leave() +{ + SwProtocol::Record( pFrame, nFunction, DbgAction::End, pParam ); +} + +void SwSizeEnterLeave::Leave() +{ + nFrameHeight = pFrame->getFrameArea().Height() - nFrameHeight; + SwProtocol::Record( pFrame, nFunction, DbgAction::End, &nFrameHeight ); +} + +void SwUpperEnterLeave::Enter() +{ + nFrameId = pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId( pFrame->GetUpper() )) : 0; + SwProtocol::Record( pFrame, nFunction, DbgAction::Start, &nFrameId ); +} + +void SwUpperEnterLeave::Leave() +{ + nFrameId = pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId( pFrame->GetUpper() )) : 0; + SwProtocol::Record( pFrame, nFunction, DbgAction::End, &nFrameId ); +} + +void SwFrameChangesLeave::Enter() +{ +} + +void SwFrameChangesLeave::Leave() +{ + if( pFrame->getFrameArea() != aFrame ) + SwProtocol::Record( pFrame, PROT::FrmChanges, DbgAction::NONE, &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..2ba71faf7 --- /dev/null +++ b/sw/source/core/layout/dumpfilter.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 <dumpfilter.hxx> + +#include <wrtsh.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <docsh.hxx> +#include <rootfrm.hxx> +#include <unotxdoc.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::getUnoTunnelImplementation<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); + xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr ); + + // TODO This doesn't export the whole XML file, whereas dumpAsXML() does it nicely + pLayout->dumpAsXml( writer ); + + 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..548399e40 --- /dev/null +++ b/sw/source/core/layout/findfrm.cxx @@ -0,0 +1,1827 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <txtfrm.hxx> +#include <bodyfrm.hxx> +#include <calbck.hxx> +#include <viewopt.hxx> +#include <ndtxt.hxx> +#include <sal/log.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; +} + +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(); + while ( pRet && !pRet->IsFootnoteBossFrame() ) + { + 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().IsInside( 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.IsInside(rPt)) || + (pSize && rBoundRect.IsOver(aRect)) ) + { + pRet = static_cast<const SwPageFrame*>(pPage); + } + + pPage = pPage->GetNext(); + } + + return pRet; +} + +bool SwRootFrame::IsBetweenPages(const Point& rPt) const +{ + if (!getFrameArea().IsInside(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().IsInside(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()) + { + // 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 <= MmToTwips(2.0); + } + } + + 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; + if ( nullptr != (pFrame = FindNext_()) ) + { + 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 ) + { + 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; + } + } + 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 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? + 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()->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->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; +} + +/* 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..576e32d90 --- /dev/null +++ b/sw/source/core/layout/flowfrm.cxx @@ -0,0 +1,2665 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> +#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::m_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(); +} + +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) + bool bKeep = bCheckIfLastRowShouldKeep || + ( !m_rThis.IsInFootnote() && + ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) && + rKeep.GetValue() ); + + 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; + sal_uLong nIndex = ULONG_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.IsOver( 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 = dynamic_cast<const SwFlyFrame*>(pObj) ) + { + 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. + sal_uLong 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( ULONG_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 ) + { + if ( nullptr != (pStart->mpPrev = pSibling->GetPrev()) ) + pStart->GetPrev()->mpNext = pStart; + else + pParent->m_pLower = pStart; + pSibling->InvalidatePos_(); + pSibling->InvalidatePrt_(); + } + else + { + if ( nullptr == (pStart->mpPrev = pParent->Lower()) ) + 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; + 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; + auto pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pPrevFrame); + const SwBorderAttrs *pAttrs = pAccess->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::unique_ptr<SwBorderAttrAccess> pAccess; + 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; + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pOwn); + pAttrs = pAccess->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); + + const bool bContextualSpacing = bContextualSpacingThis + && bContextualSpacingPrev + && lcl_IdenticalStyles(pPrevFrame, &m_rThis); + + // 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 + && lcl_IdenticalStyles(pPrevFrame, &m_rThis); + + // 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 + && lcl_IdenticalStyles(pPrevFrame, &m_rThis); + + // 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 bPrevLineSpacingPorportional = false; + GetSpacingValuesOfFrame( (*pPrevFrame), + nPrevLowerSpace, nPrevLineSpacing, + bPrevLineSpacingPorportional ); + 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, 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 ( bPrevLineSpacingPorportional ) + { + nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true ); + } + else + { + nAdd = std::max( nAdd, static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true ) ); + } + } + nUpper += nAdd; + } + } + else + { + nUpper = bContextualSpacing ? 0 : std::max( + bHalfContextualSpacingPrev ? 0 : static_cast<long>(nPrevLowerSpace), + bHalfContextualSpacing ? 0 : static_cast<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, 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 ( bPrevLineSpacingPorportional ) + { + nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true ); + } + else + { + nAdd = std::max( nAdd, 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 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 ); + 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()->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::unique_ptr<SwBorderAttrAccess> pAttrAccess; + if ( !_pAttrs ) + { + pAttrAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), &m_rThis); + _pAttrs = pAttrAccess->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::unique_ptr<SwBorderAttrAccess> pAttrAccess; + if (pFrame && (!_pAttrs || pFrame != &m_rThis)) + { + pAttrAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), pFrame); + _pAttrs = pAttrAccess->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() && + m_rThis.GetUpper()->FindTabFrame()->IsFwdMoveAllowed() ) ) && + 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::unique_ptr<SwFrameDeleteGuard> xDeleteGuard(bMakePage ? new SwFrameDeleteGuard(pOldBoss) : nullptr); + + 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; + + xDeleteGuard.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 ) + { + 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(); + 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(); +#if BOOST_VERSION < 105600 + std::list<SwFrameDeleteGuard> g; +#else + ::std::optional<SwFrameDeleteGuard> g; +#endif + 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) +#if BOOST_VERSION < 105600 + g.emplace_back(m_rThis.GetUpper()->GetUpper()); +#else + g.emplace(m_rThis.GetUpper()->GetUpper()); +#endif + 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..e3c52a035 --- /dev/null +++ b/sw/source/core/layout/fly.cxx @@ -0,0 +1,2917 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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 <editeng/outlobj.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 <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 ), + SwAnchoredObject(), // #i26791# + m_pPrevLink( nullptr ), + m_pNextLink( nullptr ), + m_bInCnt( false ), + m_bAtCnt( false ), + m_bLayout( false ), + m_bAutoPosition( false ), + 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(); + + 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() ) + { + 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() ) + { + const SwFormatContent& rContent = GetFormat()->GetContent(); + OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." ); + sal_uLong 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 ) + { + // 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( 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 ); + } + } + } + + 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 ( dynamic_cast<const SwFlyFrame*>( pAnchoredObj) != nullptr ) + { + SwFrame::DestroyFrame(static_cast<SwFlyFrame*>(pAnchoredObj)); + } + else if ( dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) != nullptr ) + { + // consider 'virtual' drawing objects + SdrObject* pObj = pAnchoredObj->DrawObj(); + if ( dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr ) + { + SwDrawVirtObj* pDrawVirtObj = static_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() +{ + SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat())); + + // 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()) + { + if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell)) + { // tdf#131679 move any cursor out of fly + SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, pFEShell); + 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 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) + SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell(); + if( pSh ) + { + SwRootFrame* pLayout = pMaster->getRootFrame(); + if( pLayout && pLayout->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow ); + } +} + +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." ); + sal_uLong 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) + SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell(); + if( pSh ) + { + SwRootFrame* pLayout = pMaster->getRootFrame(); + if( pLayout && pLayout->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow ); + } +} + +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) +{ + SwFrame::SwClientNotify(rMod, rHint); + 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + + if (pNew && pOld && RES_ATTRSET_CHG == pNew->Which()) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwLayoutFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags == 0 ) + return; + + Invalidate_(); + if ( nInvFlags & 0x01 ) + { + InvalidatePos_(); + // #i68520# + InvalidateObjRectWithSpaces(); + } + if ( nInvFlags & 0x02 ) + { + InvalidateSize_(); + // #i68520# + InvalidateObjRectWithSpaces(); + } + if ( nInvFlags & 0x04 ) + InvalidatePrt_(); + if ( nInvFlags & 0x08 ) + SetNotifyBack(); + if ( nInvFlags & 0x10 ) + SetCompletePaint(); + if ( ( nInvFlags & 0x40 ) && Lower() && Lower()->IsNoTextFrame() ) + ClrContourCache( GetVirtDrawObj() ); + SwRootFrame *pRoot; + if ( nInvFlags & 0x20 && nullptr != (pRoot = getRootFrame()) ) + pRoot->InvalidateBrowseWidth(); + // #i28701# + if ( nInvFlags & 0x80 ) + { + // 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(); + +} + +void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &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 |= 0x09; + } + break; + // #i28701# - consider new option 'wrap influence on position' + case RES_WRAP_INFLUENCE_ON_OBJPOS: + { + rInvFlags |= 0x89; + } + break; + case RES_SURROUND: + { + //#i28701# - invalidate position on change of + // wrapping style. + //rInvFlags |= 0x40; + rInvFlags |= 0x41; + // 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 |= 0x09; + + // 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 |= 0x80; + } + break; + + case RES_PROTECT: + if (pNew) + { + const SvxProtectItem *pP = static_cast<const SvxProtectItem*>(pNew); + GetVirtDrawObj()->SetMoveProtect( pP->IsPosProtected() ); + GetVirtDrawObj()->SetResizeProtect( pP->IsSizeProtected() ); + if( pSh ) + { + SwRootFrame* pLayout = getRootFrame(); + if( pLayout && pLayout->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleEditableState( true, this ); + } + } + 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 |= 0x1A; + } + break; + + case RES_FRM_SIZE: + case RES_FMT_CHG: + { + const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize(); + if ( FrameSizeChg( rNew ) ) + NotifyDrawObj(); + rInvFlags |= 0x7F; + 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() - long(rUL.GetUpper()), 0L ) ); + aOld.AddHeight(rUL.GetLower() ); + const SvxLRSpaceItem &rLR = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetLRSpace(); + aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), 0L ) ); + 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 |= 0x80; + + break; + } + case RES_UL_SPACE: + case RES_LR_SPACE: + { + rInvFlags |= 0x41; + 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() - long(rUL.GetUpper()), 0L ) ); + aOld.AddHeight(rUL.GetLower() ); + } + else + { + const SvxLRSpaceItem &rLR = *static_cast<const SvxLRSpaceItem*>(pNew); + aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), 0L ) ); + aOld.AddWidth(rLR.GetRight() ); + } + } + aNew.Union( aOld ); + NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear ); + } + break; + + case RES_TEXT_VERT_ADJUST: + { + InvalidateContentPos(); + rInvFlags |= 0x10; + } + break; + + case RES_BOX: + case RES_SHADOW: + rInvFlags |= 0x17; + break; + + case RES_FRAMEDIR : + SetDerivedVert( false ); + SetDerivedR2L( false ); + CheckDirChange(); + break; + + case RES_OPAQUE: + { + 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( pSh ) + { + SwRootFrame* pLayout = getRootFrame(); + if( pLayout && pLayout->IsAnyShellAccessible() ) + { + pSh->Imp()->DisposeAccessibleFrame( this ); + pSh->Imp()->AddAccessibleFrame( this ); + } + } + // #i28701# - perform reorder of object lists + // at anchor frame and at page frame. + rInvFlags |= 0x80; + } + 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 ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwLayoutFrame::Modify( 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; + SfxItemSet aSet( pFormat->GetDoc()->GetAttrPool(), + svl::Items<RES_VERT_ORIENT, RES_HORI_ORIENT>{}); + + 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() ) + { + 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(nullptr, 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; + const int nLoopControlMax = 20; + const SwFrame* pLoopControlCond = nullptr; + + SwFrame* pLast; + do + { + pLast = 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; + SwPageFrame* pPageFrame = pFrame->FindPageFrame(); + size_t nCnt = pFrame->GetDrawObjs()->size(); + size_t i = 0; + while ( i < nCnt ) + { + // #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 ) + { + 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 load in the pLay. + // As long as these Frames are loading in pLay, we continue + } 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() ) + { + 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); + 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; + + long nBodyHeight = pBody->getFrameArea().Height(); + long nTableHeight = pTable->getFrameArea().Height(); + 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# + 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 ); + } + } + } + } + + 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. + SwViewShell* pSh = getRootFrame()->GetCurrShell(); + if( pSh ) + { + SwRootFrame* pLayout = getRootFrame(); + if( pLayout && pLayout->IsAnyShellAccessible() ) + { + pSh->Imp()->AddAccessibleObj( _rNewObj.GetDrawObj() ); + } + } + + assert(!m_pDrawObjs || m_pDrawObjs->is_sorted()); +} + +void SwFrame::RemoveDrawObj( SwAnchoredObject& _rToRemoveObj ) +{ + // Notify accessible layout. + SwViewShell* pSh = getRootFrame()->GetCurrShell(); + if( pSh ) + { + SwRootFrame* pLayout = getRootFrame(); + if (pLayout && pLayout->IsAnyShellAccessible()) + pSh->Imp()->DisposeAccessibleObj(_rToRemoveObj.GetDrawObj(), false); + } + + // 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() ) + { + // #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 ( dynamic_cast<const SwFlyFrame*>( pAnchoredObj) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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() ) + { + 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 ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pObj); + + 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 + { + OSL_ENSURE( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr, + "<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->SetRectsDirty(); + 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 + { + 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.GetWidthPercent() == SwFormatFrameSize::SYNCED ) + { + aRet.setWidth( aRet.Width() * ( aRet.Height()) ); + aRet.setWidth( aRet.Width() / ( rSz.GetHeight()) ); + } + else if ( 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; + + while ( pFrame ) + { + 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(); + } + + 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 + { + const SwFlyFreeFrame* pSwFlyFreeFrame(dynamic_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()) + { + const SwFlyFreeFrame* pSwFlyFreeFrame(dynamic_cast< const SwFlyFreeFrame* >(this)); + + if(nullptr != pSwFlyFreeFrame && pSwFlyFreeFrame->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. +} + +/** 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 ( dynamic_cast<const SwFlyFrame*>( pAnchoredObj) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + // 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; +} + +/* 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..71f6dff77 --- /dev/null +++ b/sw/source/core/layout/flycnt.cxx @@ -0,0 +1,1463 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <hints.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> + +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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + const SwFormatAnchor *pAnch = nullptr; + + if (pNew) + { + const sal_uInt16 nWhich = pNew->Which(); + if( RES_ATTRSET_CHG == nWhich && SfxItemState::SET == + static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_ANCHOR, false, + reinterpret_cast<const SfxPoolItem**>(&pAnch) )) + ; // The anchor pointer is set at GetItemState! + + else if( RES_ANCHOR == nWhich ) + { + //Change anchor, I move myself to a new place. + //The anchor type must not change, this is only possible using + //SwFEShell. + pAnch = static_cast<const SwFormatAnchor*>(pNew); + } + } + + if( pAnch ) + { + 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 = dynamic_cast<SwFlyFrame*>(rObj); + 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(); + } + else + SwFlyFrame::Modify( pOld, pNew ); +} + +//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 *pStack1; + static const SwFlyFrame *pStack2; + static const SwFlyFrame *pStack3; + static const SwFlyFrame *pStack4; + static const SwFlyFrame *pStack5; + + const SwFlyFrame *pFly; + std::vector<Point> maObjPositions; + +public: + explicit SwOszControl( const SwFlyFrame *pFrame ); + ~SwOszControl(); + bool ChkOsz(); + static bool IsInProgress( const SwFlyFrame *pFly ); +}; + +} + +const SwFlyFrame *SwOszControl::pStack1 = nullptr; +const SwFlyFrame *SwOszControl::pStack2 = nullptr; +const SwFlyFrame *SwOszControl::pStack3 = nullptr; +const SwFlyFrame *SwOszControl::pStack4 = nullptr; +const SwFlyFrame *SwOszControl::pStack5 = nullptr; + +SwOszControl::SwOszControl( const SwFlyFrame *pFrame ) + : pFly( pFrame ) +{ + if ( !SwOszControl::pStack1 ) + SwOszControl::pStack1 = pFly; + else if ( !SwOszControl::pStack2 ) + SwOszControl::pStack2 = pFly; + else if ( !SwOszControl::pStack3 ) + SwOszControl::pStack3 = pFly; + else if ( !SwOszControl::pStack4 ) + SwOszControl::pStack4 = pFly; + else if ( !SwOszControl::pStack5 ) + SwOszControl::pStack5 = pFly; +} + +SwOszControl::~SwOszControl() +{ + if ( SwOszControl::pStack1 == pFly ) + SwOszControl::pStack1 = nullptr; + else if ( SwOszControl::pStack2 == pFly ) + SwOszControl::pStack2 = nullptr; + else if ( SwOszControl::pStack3 == pFly ) + SwOszControl::pStack3 = nullptr; + else if ( SwOszControl::pStack4 == pFly ) + SwOszControl::pStack4 = nullptr; + else if ( SwOszControl::pStack5 == pFly ) + SwOszControl::pStack5 = nullptr; + // #i3317# + maObjPositions.clear(); +} + +bool SwOszControl::IsInProgress( const SwFlyFrame *pFly ) +{ + if ( SwOszControl::pStack1 && !pFly->IsLowerOf( SwOszControl::pStack1 ) ) + return true; + if ( SwOszControl::pStack2 && !pFly->IsLowerOf( SwOszControl::pStack2 ) ) + return true; + if ( SwOszControl::pStack3 && !pFly->IsLowerOf( SwOszControl::pStack3 ) ) + return true; + if ( SwOszControl::pStack4 && !pFly->IsLowerOf( SwOszControl::pStack4 ) ) + return true; + if ( SwOszControl::pStack5 && !pFly->IsLowerOf( SwOszControl::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 = 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() ) + { + // #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() ) + { + 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 = + dynamic_cast<SwTextFrame&>(*GetAnchorFrameContainingAnchPos()); + // #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 ); + if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( + *this, GetPageFrame()->GetPhyPageNum(), + bAnchoredAtMaster, nToPageNum, bDummy ) ) + { + 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 ) + SwLayouter::RemoveMovedFwdFrame( rDoc, *pAnchorTextFrame ); + else + bInsert = false; + } + if ( bInsert ) + { + 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; + } + } + } + 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 nMain, nSub; + SwDistance() : nMain(0), nSub(0) { } + bool operator<( const SwDistance& rTwo ) const + { return nMain < rTwo.nMain || ( nMain == rTwo.nMain && nSub && + rTwo.nSub && nSub < rTwo.nSub ); } + bool operator<=( const SwDistance& rTwo ) const + { return nMain < rTwo.nMain || ( nMain == rTwo.nMain && ( !nSub || + !rTwo.nSub || nSub <= rTwo.nSub ) ); } +}; + +} + +static const SwFrame * lcl_CalcDownDist( SwDistance &rRet, + const Point &rPt, + const SwContentFrame *pCnt ) +{ + rRet.nSub = 0; + //If the point stays inside the Cnt everything is clear already; the Content + //automatically has a distance of 0. + if ( pCnt->getFrameArea().IsInside( rPt ) ) + { + rRet.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().IsInside( rPt ) ) + { + // <rPt> point is inside environment of given content frame + // #i70582# + if( bVert ) + { + if ( bVertL2R ) + rRet.nMain = rPt.X() - nTopForObjPos; + else + rRet.nMain = nTopForObjPos - rPt.X(); + } + else + rRet.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.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.nMain = rPt.X() - nTopForObjPos; + else + rRet.nMain = nTopForObjPos - rPt.X(); + } + else + rRet.nMain = rPt.Y() - nTopForObjPos; + return pCnt; + } + else + rRet.nMain = LONG_MAX; + } + else + { + rRet.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.nSub = rRet.nMain; + rRet.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().IsInside( 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.nSub += nPrtHeight; + else + rRet.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().IsInside( rPt ) ) + { + SwTwips nDiff = pLay->IsVertical() ? ( pLay->IsVertLR() ? ( rPt.X() - nFrameTop ) : ( nFrameTop - rPt.X() ) ) + : ( rPt.Y() - nFrameTop ); + if( bSct || pSect ) + rRet.nSub += nDiff; + else + rRet.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.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().IsInside( 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().IsInside( 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.nMain = LONG_MAX; + if ( nUp.nMain >= 0 && LONG_MAX != nUp.nMain ) + { + bNegAllowed = false; + if ( nUpLst.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.nMain < 0 ) || ( nUp <= nUpLst ) ) ); + + const SwContentFrame *pDownLst; + const SwContentFrame *pDownFrame = pCnt; + SwDistance nDownLst; + if ( nDown.nMain < 0 ) + nDown.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.nMain < 0 ) + nDown.nMain = LONG_MAX; + //It makes sense to search further, if the distance grows inside + //a table. + if ( pDownLst->IsInTab() && pDownFrame->IsInTab() ) + { + while ( pDownFrame && ( ( nDown.nMain != LONG_MAX && pDownFrame->IsInTab()) || bBody != pDownFrame->IsInDocBody() ) ) + { + pDownFrame = pDownFrame->GetNextContentFrame(); + if ( pDownFrame ) + ::lcl_CalcDownDist( nDown, aNew, pDownFrame ); + if ( nDown.nMain < 0 ) + nDown.nMain = LONG_MAX; + } + } + } + if ( !pDownFrame ) + nDown.nMain = LONG_MAX; + + } while ( pDownFrame && nDown <= nDownLst && + nDown.nMain != LONG_MAX && nDownLst.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.nMain == LONG_MAX && nUpLst.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().IsInside( 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.nMain + aDist.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 ) + { + if ( GetPageFrame() ) + GetPageFrame()->MoveFly( this, pPageFrame ); + else + pPageFrame->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..4823aab29 --- /dev/null +++ b/sw/source/core/layout/flyincnt.cxx @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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 != 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 )); + 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + bool bCallPrepare = false; + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + if (RES_ATTRSET_CHG == nWhich && pNew) + { + if(pOld && + (SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()-> + GetItemState(RES_SURROUND, false) || + SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()-> + GetItemState(RES_FRMMACRO, false)) ) + { + SwAttrSetChg aOld( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNew( *static_cast<const SwAttrSetChg*>(pNew) ); + + aOld.ClearItem( RES_SURROUND ); + aNew.ClearItem( RES_SURROUND ); + aOld.ClearItem( RES_FRMMACRO ); + aNew.ClearItem( RES_FRMMACRO ); + if( aNew.Count() ) + { + SwFlyFrame::Modify( &aOld, &aNew ); + bCallPrepare = true; + } + } + else if( static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->Count()) + { + SwFlyFrame::Modify( pOld, pNew ); + bCallPrepare = true; + } + } + else if( nWhich != RES_SURROUND && RES_FRMMACRO != nWhich ) + { + SwFlyFrame::Modify( pOld, pNew ); + bCallPrepare = true; + } + + if ( bCallPrepare && 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() ) + { + 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..72d673b22 --- /dev/null +++ b/sw/source/core/layout/flylay.cxx @@ -0,0 +1,1489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cntfrm.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <ftnfrm.hxx> +#include <frmatr.hxx> +#include <frmtool.hxx> +#include <hints.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> + +using namespace ::com::sun::star; + +SwFlyFreeFrame::SwFlyFreeFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) +: SwFlyFrame( pFormat, pSib, pAnch ), + // #i34753# + mbNoMakePos( false ), + // #i37068# + mbNoMoveOnCheckClip( false ), + maUnclippedFrame(), + // RotateFlyFrame3 + mpTransformableSwFrame() +{ +} + +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 SfxPoolItem* pItem(nullptr); + + if(GetFormat() && SfxItemState::SET == GetFormat()->GetItemState(RES_BOX, false, &pItem)) + { + const SvxBoxItem& rBox = *static_cast< const SvxBoxItem* >(pItem); + + if(rBox.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 && 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 long nBot = getFrameArea().Top() + getFrameArea().Height(); + const long nRig = getFrameArea().Left() + getFrameArea().Width(); + const long nClipBot = aClip.Top() + aClip.Height(); + const long nClipRig = aClip.Left() + aClip.Width(); + + const bool bBot = nBot > nClipBot; + const bool bRig = nRig > nClipRig; + if ( bBot || bRig ) + { + 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 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 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 ) + { + 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 ) + { + 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 long nPrtHeightDiff = getFrameArea().Height() - getFramePrintArea().Height(); + const long nPrtWidthDiff = getFrameArea().Width() - getFramePrintArea().Width(); + maUnclippedFrame = getFrameArea(); + + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aFrm.Height( aFrameRect.Height() ); + aFrm.Width ( std::max( 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; +} + +// #i28701# + +void SwFlyLayFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + const SwFormatAnchor *pAnch = nullptr; + + if (pNew) + { + const sal_uInt16 nWhich = pNew->Which(); + if( RES_ATTRSET_CHG == nWhich && SfxItemState::SET == + static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_ANCHOR, false, + reinterpret_cast<const SfxPoolItem**>(&pAnch) )) + ; // GetItemState sets the anchor pointer! + + else if( RES_ANCHOR == nWhich ) + { + // Change of anchor. I'm attaching myself to the new place. + // It's not allowed to change the anchor type. This is only + // possible via SwFEShell. + pAnch = static_cast<const SwFormatAnchor*>(pNew); + } + } + + if( pAnch ) + { + OSL_ENSURE( pAnch->GetAnchorId() == + GetFormat()->GetAnchor().GetAnchorId(), + "8-) 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() ) + { + sal_uInt16 nPgNum = pAnch->GetPageNum(); + SwRootFrame *pRoot = getRootFrame(); + SwPageFrame *pTmpPage = static_cast<SwPageFrame*>(pRoot->Lower()); + for ( sal_uInt16 i = 1; (i <= nPgNum) && pTmpPage; ++i, + pTmpPage = static_cast<SwPageFrame*>(pTmpPage->GetNext()) ) + { + if ( i == nPgNum ) + { + // #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(); + } + else + SwFlyFrame::Modify( pOld, pNew ); +} + +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(); + if ( pObj->getSdrPageFromSdrObject() ) + pObj->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(), nNewNum ); + else + pFly->GetVirtDrawObj()->SetOrdNum( nNewNum ); + } + + // 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( GetUpper() && + static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() && + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() ) + { + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp() + ->AddAccessibleFrame( pNew ); + } + } + + // #i28701# - correction: consider also drawing objects + if ( pNew->GetDrawObjs() ) + { + SwSortedObjs &rObjs = *pNew->GetDrawObjs(); + for (SwAnchoredObject* pTmpObj : rObjs) + { + if ( dynamic_cast<const SwFlyFrame*>( pTmpObj) != nullptr ) + { + SwFlyFrame* pTmpFly = static_cast<SwFlyFrame*>(pTmpObj); + // #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( GetUpper() && + static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() && + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() ) + { + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp() + ->DisposeAccessibleFrame( pToRemove, true ); + } + + // #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( GetUpper() && + static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() && + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() ) + { + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp() + ->DisposeAccessibleFrame( pToMove, true ); + } + + // 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( GetUpper() && + static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() && + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() ) + { + static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp() + ->AddAccessibleFrame( pToMove ); + } + + // #i28701# - correction: move lowers of Writer fly frame + if ( pToMove->GetDrawObjs() ) + { + SwSortedObjs &rObjs = *pToMove->GetDrawObjs(); + for (SwAnchoredObject* pObj : rObjs) + { + if ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pObj); + 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().IsInside( pFly->getFrameArea().Pos() ) ) + { + if( pUp->IsFlyFrame() ) + { + const SwFlyFrame *pTmpFly = static_cast<const SwFlyFrame*>(pUp); + while( pTmpFly->GetNextLink() ) + { + pTmpFly = pTmpFly->GetNextLink(); + if( pTmpFly->getFrameArea().IsInside( pFly->getFrameArea().Pos() ) ) + break; + } + pUp = pTmpFly; + } + else if( pUp->IsInFootnote() ) + { + const SwFootnoteFrame *pTmp = pUp->FindFootnoteFrame(); + while( pTmp->GetFollow() ) + { + pTmp = pTmp->GetFollow(); + if( pTmp->getFrameArea().IsInside( 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()) ); + } + long nHeight = (9*aRectFnSet.GetHeight(rRect))/10; + 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 ); + 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); + long nHeight = (9*aRectFnSet.GetHeight(rRect))/10; + long nTop; + const SvxULSpaceItem &rUL = pFormat->GetULSpace(); + SwRect aSnapRect( pSdrObj->GetSnapRect() ); + long nTmpH = 0; + if( bMove ) + { + nTop = aRectFnSet.YInc( aRectFnSet.IsVert() ? pSdrObj->GetAnchorPos().X() : + pSdrObj->GetAnchorPos().Y(), -nHeight ); + 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..e7709204f --- /dev/null +++ b/sw/source/core/layout/flypos.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 <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docary.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..295000c0a --- /dev/null +++ b/sw/source/core/layout/frmtool.cxx @@ -0,0 +1,3897 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <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 <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; + +bool bObjsDirect = true; +bool bDontCreateObjects = false; +bool bSetCompletePaintOnInvalidate = false; + +sal_uInt8 StackHack::nCnt = 0; +bool StackHack::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() COVERITY_NOEXCEPT_FALSE +{ + 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() ) + { + long nOldHeight = aRectFnSet.GetHeight(maFrame); + 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( mpFrame->IsAccessibleFrame() ) + { + SwRootFrame *pRootFrame = mpFrame->getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame ); + } + } + + // 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() && + dynamic_cast<const SwFlyFrame*>( pObj) != 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 ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj); + 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." ); + } + } + } + } + } + else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() ) + { + SwRootFrame *pRootFrame = mpFrame->getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame ); + } + } + + // #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() ) ) + { + // #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() ) + { + 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(); + } +} + +SwLayNotify::~SwLayNotify() +{ + 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; + 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(); +} + +SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) : + SwLayNotify( pFlyFrame ), + // #115759# - keep correct page frame - the page frame + // the Writer fly frame is currently registered at. + pOldPage( pFlyFrame->GetPageFrame() ), + aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() ) +{ +} + +SwFlyNotify::~SwFlyNotify() +{ + 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, pOldPage, aFrameAndSpace, &maPrt ); + // #i35640# - additional notify anchor text frame, + // if Writer fly frame has changed its page + if ( pFly->GetAnchorFrame()->IsTextFrame() && + pFly->GetPageFrame() != 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(); + if ( pNxt ) + { + pNxt->InvalidatePos(); + } + } + + // #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() && + ( dynamic_cast<const SwFlyFreeFrame*>( pFly) == nullptr || + !static_cast<SwFlyFreeFrame*>(pFly)->IsNoMoveOnCheckClip() ) ) + { + // #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() ) + { + // 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(); + } + } +} + +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() ) + { + 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(); + } + } + } +} + +SwContentNotify::~SwContentNotify() +{ + 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 + } + } + //dito 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 *const pDoc = pCnt->IsTextFrame() + ? &static_cast<SwTextFrame*>(pCnt)->GetDoc() + : static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetDoc(); + if ( !pDoc->GetSpzFrameFormats()->empty() && + pDoc->DoesContainAtPageObjWithContentAnchor() && !pDoc->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 = pDoc->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() ) + { + 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() ); + } + } + } + } +} + +// 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 ) + { + 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->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(sal_uLong 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); + 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*const pFlys(rNode.GetAnchoredFlys()); + if (!pFlys) + { + return; + } + for (SwFrameFormat * pFrameFormat : *pFlys) + { + SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor(); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR + || (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR + && RES_DRAWFRMFMT == pFrameFormat->Which())) + { + assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex()); + if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode)) + { + pFrameFormat->DelFrames(); + } + } + } +} + +void AppendObjsOfNode(SwFrameFormats const*const pTable, sal_uLong 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*const pFlys(rNode.GetAnchoredFlys()); + for (size_t it = 0; pFlys && it != pFlys->size(); ) + { + SwFrameFormat *const pFormat = (*pFlys)[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, sal_uLong 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); + sal_uLong const until = iter == pMerged->extents.end() + ? pMerged->pLastNode->GetIndex() + 1 + : iter->pNode->GetIndex(); + for (sal_uLong 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()->IsHideRedlines()) + { + 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, + sal_uLong nIndex, bool bPages, sal_uLong 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, 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 == 0 || nIndex < nEndIndex; ++nIndex) + { + SwNode *pNd = pDoc->GetNodes()[nIndex]; + if ( pNd->IsContentNode() ) + { + SwContentNode* pNode = static_cast<SwContentNode*>(pNd); + if (pLayout->IsHideRedlines() && !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 ( 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) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) ); + // #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(); + } + } + // 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 && !bDontCreateObjects ) + 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->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden); + 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(); + + pFrame = pTableNode->MakeFrame( pLay ); + + 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. + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + // no notification, if <SwViewShell> is in construction + if ( pViewShell && !pViewShell->IsInConstructor() && + pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() && + pFrame->FindPageFrame() != nullptr) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) ); + } + } + 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->IsHideRedlines() && !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#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. + { + SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() ); + // no notification, if <SwViewShell> is in construction + if ( pViewShell && !pViewShell->IsInConstructor() && + pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() && + pFrame->FindPageFrame() != nullptr) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) ); + } + } + 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->IsHideRedlines() && !pNd->IsCreateFrameWhenHidingRedlines()) + { + assert(pNd->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->IsHideRedlines() && !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 && !bDontCreateObjects ) + { + SwFlyFrame* pFly = pLay->FindFlyFrame(); + if( pFly ) + AppendObjs( pTable, nIndex, pFly, pPage, pDoc ); + } + } + else + { + assert(!pLayout->IsHideRedlines() + || 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 ( !bDontCreateObjects ) + 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 ); + sal_uLong nEndIdx = rEndIdx.GetIndex(); + SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp, + pDoc->GetNodes()[ nEndIdx-1 ]); + if ( pNd ) + { + bool bApres = aTmp < rSttIdx; + SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() ); + SwFrame* pFrame; + sw::FrameMode eMode = sw::FrameMode::Existing; + while( nullptr != (pFrame = aNode2Layout.NextFrame()) ) + { + 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() > 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( !bDontCreateObjects ) + { + 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 SwModify *pMod, const SwFrame *pConstructor) + : SwCacheObj(pMod) + , 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 = dynamic_cast<const SwTextFrame*>(pConstructor); + 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<SwModify *>(static_cast<SwModify const *>(m_pOwner))->SetInCache( 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(); + m_bTop = false; +} + +void SwBorderAttrs::CalcBottom_() +{ + m_nBottom = CalcBottomLine() + m_rUL.GetLower(); + m_bBottom = false; +} + +long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const +{ + 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(); + } + + 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 (dynamic_cast<const SwFlyFrame*>(pObject) != nullptr) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pObject); + if (pFly->Lower() && pFly->Lower()->IsTabFrame()) + return true; + } + } + } + return false; +} + +long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const +{ + 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(); + } + + 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 SwModify const* GetCacheOwner(SwFrame const& rFrame) +{ + return rFrame.IsContentFrame() + ? static_cast<SwModify 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<SwModify const*>(static_cast<const SwLayoutFrame&>(rFrame).GetFormat()); +} + +SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) : + SwCacheAccess( rCach, + static_cast<void const *>(GetCacheOwner(*pFrame)), + GetCacheOwner(*pFrame)->IsInCache()), + m_pConstructor( pFrame ) +{ +} + +SwCacheObj *SwBorderAttrAccess::NewObj() +{ + const_cast<SwModify *>(static_cast<SwModify const *>(m_pOwner))->SetInCache( true ); + return new SwBorderAttrs( static_cast<SwModify 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() ) + { + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + 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() ) + { + const SwSortedObjs *pObjs = m_pPage->GetSortedObjs(); + if ( pObjs->size() ) + { + 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 ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj); + + // #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()> + pFlyFrame->GetPageFrame()->RemoveFlyFromPage( pFlyFrame ); + } + } + // #115759# - remove also drawing objects from page + else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr ) + { + if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + if (SwPageFrame *pPg = pObj->GetPageFrame()) + pPg->RemoveDrawObjFromPage( + *static_cast<SwAnchoredDrawObject*>(pObj) ); + } + } + } +} + +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; + if ( nullptr == (pSav = pLay->ContainsAny()) ) + 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 ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj); + if ( dynamic_cast<const SwFlyFreeFrame*>( pObj) != nullptr ) + { + _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 ) +{ + 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() : rDesc.GetRightFormat(); + 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 = dynamic_cast<SwFlyFrame*>(pObj)) + { + // 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.IsOver( 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() ) + { + 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.IsOver( rRect ) ) + pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged ); + } + // #i23129# - only invalidate, if the text frame + // printing area overlaps with the given rectangle. + else if ( aCntPrt.IsOver( rRect ) ) + pCnt->Prepare( eHint, static_cast<void*>(&aCntPrt.Intersection_( rRect )) ); + if ( pCnt->GetDrawObjs() ) + { + const SwSortedObjs &rObjs = *pCnt->GetDrawObjs(); + for (SwAnchoredObject* pObj : rObjs) + { + if ( dynamic_cast<const SwFlyFrame*>( pObj) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pObj); + 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().IsOver(pObj->GetLastBoundRect()) || + pTab->getFrameArea().IsOver(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().IsOver( pObj->GetLastBoundRect() ) || + pCell->getFrameArea().IsOver( 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 ( dynamic_cast<const SwFlyFrame*>( pAnchoredObj) != 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().IsInside( rPos ) ) + { + if( pFrame->IsFootnoteFrame() ) + { + const SwFootnoteFrame* pTmp = static_cast<const SwFootnoteFrame*>(pFrame)->GetFollow(); + while( pTmp ) + { + if( pTmp->getFrameArea().IsInside( rPos ) ) + return pTmp; + pTmp = pTmp->GetFollow(); + } + } + else + { + SwFlyFrame* pTmp = const_cast<SwFlyFrame*>(pFrame->FindFlyFrame()); + while( pTmp ) + { + if( pTmp->getFrameArea().IsInside( 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()) ) + { + long nHeight = 0, nFlyAdd = 0; + do + { + 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( 0L, 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 + { + 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.IsOver( 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* pFrame; + bool bSet; + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; +public: + SwFrameHolder() : pFrame(nullptr), bSet(false) {} + void SetFrame( SwFrame* pHold ); + SwFrame* GetFrame() { return pFrame; } + void Reset(); + bool IsSet() const { return bSet; } +}; + +} + +void SwFrameHolder::SetFrame( SwFrame* pHold ) +{ + bSet = true; + if (pFrame != pHold) + { + if (pFrame) + EndListening(*pFrame); + StartListening(*pHold); + pFrame = pHold; + } +} + +void SwFrameHolder::Reset() +{ + if (pFrame) + EndListening(*pFrame); + bSet = false; + pFrame = nullptr; +} + +void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( rHint.GetId() == SfxHintId::Dying && &rBC == pFrame ) + { + pFrame = nullptr; + } +} + +SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, SwModify 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, SwModify, 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.IsInside(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(); + return rInf.IsPaintLineNumbers() || + rInf.IsCountInFlys() || + (static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()); +} + +// 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 ) +{ + if ( !rFrame.IsFlowFrame() ) + { + onLowerSpacing = 0; + onLineSpacing = 0; + } + else + { + const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace(); + 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, SwModify, 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..8ddd9747f --- /dev/null +++ b/sw/source/core/layout/ftnfrm.cxx @@ -0,0 +1,2968 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 long lcl_Undersize( const SwFrame* pFrame ) +{ + 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() ) + { + 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( 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, 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()); + + 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 ); + } + } + 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() ) + { + SwFrame *pCnt = static_cast<SwLayoutFrame*>(GetNext())->ContainsAny(); + if( pCnt ) + { + 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 ) + { + // 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_(); + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + if ( GetNext() ) + GetNext()->InvalidatePos_(); + 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(); + 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(); + 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 ) + { + if ( pLastInsertedFootnote->GetNext() ) + { + 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 ) + { + ChangeFootnoteRef( pSrc, pAttr, pDest ); + SwFootnoteBossFrame *pDestBoss = pDest->FindFootnoteBossFrame( true ); + OSL_ENSURE( pDestBoss, "+SwPageFrame::MoveFootnotes: no destination boss" ); + if( pDestBoss ) // robust + { + SwFootnoteFrames aFootnoteArr; + SwFootnoteBossFrame::CollectFootnotes_(pDest, pFootnote, aFootnoteArr, nullptr); + if ( !aFootnoteArr.empty() ) + { + 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 ) + { + 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 ) + { + 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->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 && pNewUpper->IsFootnoteContFrame()) + { + 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); + } + pTmpNxt->MoveSubTree( pTmpFootnote, pNewUp->GetNext() ); + } + } + } + } + + MoveSubTree( pNewUp, pNewUp->Lower() ); + + if( !bSameBoss ) + Prepare( PrepareHint::BossChanged ); + } + return bSamePage; +} + +SwSaveFootnoteHeight::SwSaveFootnoteHeight( SwFootnoteBossFrame *pBs, const SwTwips nDeadLine ) : + 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..87d63ebb7 --- /dev/null +++ b/sw/source/core/layout/hffrm.cxx @@ -0,0 +1,767 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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; + sal_uLong 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); + + long 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; + + auto pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + OSL_ENSURE(pAccess, "no border attributes"); + + SwBorderAttrs * pAttrs = pAccess->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) + { + auto pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + OSL_ENSURE(pAccess, "no border attributes"); + + SwBorderAttrs * pAttrs = pAccess->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( SwLayoutFrame const *pFrame, SwPageFrame *pPage ) +{ + size_t i = 0; + while ( pPage->GetSortedObjs() && + pPage->GetSortedObjs()->size() && + i < pPage->GetSortedObjs()->size() ) + { + SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i]; + if ( dynamic_cast< const SwFlyFrame *>( pObj ) != nullptr ) + { + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj); + if ( pFrame->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..7f913c8a0 --- /dev/null +++ b/sw/source/core/layout/layact.cxx @@ -0,0 +1,2344 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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 <sortedobjs.hxx> +#include <objectformatter.hxx> +#include <fntcache.hxx> +#include <vector> +#include <tools/diagnose_ex.h> + +// Save some typing work to avoid accessing destroyed pages. +#define XCHECKPAGE \ + { if ( IsAgain() ) \ + { \ + if( bNoLoop ) \ + rLayoutAccess.GetLayouter()->EndLoopControl(); \ + return; \ + } \ + } + +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 ) ); + } +} + +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 ) + { + SdrObject *pO = rObjs[i]->DrawObj(); + if ( dynamic_cast< const SwVirtFlyDrawObj *>( pO ) == nullptr ) + continue; + + // do not consider invisible objects + const IDocumentDrawModelAccess& rIDDMA = pPage->GetFormat()->getIDocumentDrawModelAccess(); + if ( !rIDDMA.IsVisibleLayerId( pO->GetLayer() ) ) + { + continue; + } + + SwFlyFrame *pFly = static_cast<SwVirtFlyDrawObj*>(pO)->GetFlyFrame(); + + if ( pFly == pSelfFly || !rRect.IsOver( pFly->getFrameArea() ) ) + continue; + + if ( pSelfFly && pSelfFly->IsLowerOf( pFly ) ) + continue; + + if ( pFly->GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() ) + continue; + + if ( pSelfFly ) + { + const SdrObject *pTmp = pSelfFly->GetVirtDrawObj(); + if ( pO->GetLayer() == pTmp->GetLayer() ) + { + if ( pO->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, + 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. + long nOldHeight = aRectFnSet.GetHeight(rOldRect); + 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() ) + { + 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, TaskStopwatch* pWatch) + : m_pRoot(pRt), + m_pImp( pI ), + m_pWatch(pWatch), + m_pOptTab( nullptr ), + m_nPreInvaPage( USHRT_MAX ), + m_nStartTicks( std::clock() ), + m_nEndPage( USHRT_MAX ), + m_nCheckPageNum( USHRT_MAX ), + m_pCurPage( nullptr ), + m_nTabLevel( 0 ), + m_nCallCount( 0 ) +{ + 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_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 +} + +bool SwLayAction::IsInterrupt() +{ + return m_bInterrupt || (m_pWatch && m_pWatch->exceededRuntime()); +} + +void SwLayAction::Reset() +{ + m_pOptTab = nullptr; + m_nStartTicks = std::clock(); + m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX; + m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true; + m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bReschedule = + m_bUpdateExpFields = m_bBrowseActionStop = false; + m_pCurPage = nullptr; +} + +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 = 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::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); + m_bAgain |= RemoveEmptyBrowserPages(); + while ( IsAgain() ) + { + m_bAgain = m_bNextCycle = false; + InternalAction(pRenderContext); + m_bAgain |= RemoveEmptyBrowserPages(); + } + 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(); + bool bNoLoop = pPage && SwLayouter::StartLoopControl( m_pRoot->GetFormat()->GetDoc(), pPage ); + sal_uInt16 nPercentPageNum = 0; + while ((!IsInterrupt() && pPage) || (m_nCheckPageNum != USHRT_MAX)) + { + if (!pPage && m_nCheckPageNum != USHRT_MAX) + { + 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 + if ( !IsIdle() && !IsComplete() && IsShortCut( pPage ) ) + { + m_pRoot->DeleteEmptySct(); + XCHECKPAGE; + if ( !IsInterrupt() && + (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) ) + { + if ( m_pRoot->IsAssertFlyPages() ) + m_pRoot->AssertFlyPages(); + if ( m_pRoot->IsSuperfluous() ) + { + bool bOld = IsAgain(); + m_pRoot->RemoveSuperfluous(); + m_bAgain = bOld; + } + if ( IsAgain() ) + { + if( bNoLoop ) + rLayoutAccess.GetLayouter()->EndLoopControl(); + 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()); + continue; + } + break; + } + else + { + m_pRoot->DeleteEmptySct(); + XCHECKPAGE; + + 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) + XCHECKPAGE; + + // 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 ); + XCHECKPAGE; + } + // change condition + if ( !IsNextCycle() && + ( pPage->IsInvalidContent() || + (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) ) + { + pPage->ValidateFlyInCnt(); + pPage->ValidateContent(); + pPage->ValidateFlyLayout(); + pPage->ValidateFlyContent(); + if ( !FormatContent( pPage ) ) + { + XCHECKPAGE; + 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. + XCHECKPAGE; + 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 ); + } + } + + if ( !pPage && !IsInterrupt() && + (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) ) + { + if ( m_pRoot->IsAssertFlyPages() ) + m_pRoot->AssertFlyPages(); + if ( m_pRoot->IsSuperfluous() ) + { + bool bOld = IsAgain(); + m_pRoot->RemoveSuperfluous(); + m_bAgain = bOld; + } + if ( IsAgain() ) + { + if( bNoLoop ) + rLayoutAccess.GetLayouter()->EndLoopControl(); + 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()); + } + } + + 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; + XCHECKPAGE; + 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 = IsInterrupt(); + 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 ); + + XCHECKPAGE; + + // 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() ) ) + { + XCHECKPAGE; + // 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 ); + XCHECKPAGE; + } + + 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 ) ) + { + XCHECKPAGE; + 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 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() ) ) + bRet = false; + m_pRoot->ResetTurbo(); + } + else + bRet = false; + return bRet; +} + +static bool lcl_IsInvaLay( const SwFrame *pFrame, long nBottom ) +{ + return !pFrame->isFrameAreaDefinitionValid() || + (pFrame->IsCompletePaint() && ( pFrame->getFrameArea().Top() < nBottom ) ); +} + +static const SwFrame *lcl_FindFirstInvaLay( const SwFrame *pFrame, 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; + if ( nullptr != (pTmp = lcl_FindFirstInvaLay( pFrame, nBottom )) ) + return pTmp; + } + pFrame = pFrame->GetNext(); + } + return nullptr; +} + +static const SwFrame *lcl_FindFirstInvaContent( const SwLayoutFrame *pLay, 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 = dynamic_cast< const SwFlyFrame *>( pObj ) ) + { + 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, + long _nBottom ) +{ + OSL_ENSURE( _pPage->GetSortedObjs(), "FindFirstInvaObj, no Objs" ); + + for (SwAnchoredObject* pObj : *_pPage->GetSortedObjs()) + { + if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pObj ) ) + { + 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 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 ) +{ + // save page for loop control + if( pLay->IsPageFrame() && static_cast<SwPageFrame*>(pLay) != m_pCurPage ) + { + m_nCallCount = 0; + m_pCurPage = static_cast<SwPageFrame*>(pLay); + } + 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() ) + { + // loop control for embedded tables + if ( m_nTabLevel > 0 && ++m_nCallCount > 50 ) { + static_cast<SwTabFrame*>(pLow)->SetSplitRowDisabled(); + } + + ++m_nTabLevel; + + // 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 ); + --m_nTabLevel; + } + // Skip the ones already registered for deletion + else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() ) + bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect ); + } + 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 + bool bTabChanged = false; + SwFrame *pLow = pFly->Lower(); + while ( pLow ) + { + if ( pLow->IsLayoutFrame() ) + { + if ( pLow->IsTabFrame() ) + bTabChanged |= FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect ); + else + bChanged |= 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().IsOver( 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( const SwPageFrame *pPage ) +{ + 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; + const SwContentFrame *pContentPrev = pContent->GetPrev() ? pContent->GetPrevContentFrame() : nullptr; + + 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) + { + // 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 ( !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 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() ) + { + // consider interrupt formatting. + if ( IsInterrupt() && !mbFormatContentOnInterrupt ) + return false; + } + if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() && + pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom()) + { + const 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 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() ) + { + // consider interrupt formatting. + if ( IsInterrupt() && !mbFormatContentOnInterrupt ) + return; + } + pContent = pContent->GetNextContentFrame(); + } + CheckWaitCursor(); +} + +bool SwLayIdle::IsInterrupt() +{ + return m_aWatch.exceededRuntime(); +} + +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 = pImp->GetShell(); + if( COMPLETE_STRING == nTextPos ) + { + --nTextPos; + if( dynamic_cast< const SwCursorShell *>( pSh ) != nullptr && !static_cast<SwCursorShell*>(pSh)->IsTableMode() ) + { + SwPaM *pCursor = static_cast<SwCursorShell*>(pSh)->GetCursor(); + if( !pCursor->HasMark() && !pCursor->IsMultiSelection() ) + { + pContentNode = pCursor->GetContentNode(); + nTextPos = pCursor->GetPoint()->nContent.GetIndex(); + } + } + } + sal_Int32 const nPos((pContentNode && pTextNode == pContentNode) + ? nTextPos + : COMPLETE_STRING); + + switch ( eJob ) + { + case ONLINE_SPELLING : + { + SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) ); + // PENDING should stop idle spell checking + bPageValid = bPageValid && (SwTextNode::WrongState::TODO != pTextNode->GetWrongDirty()); + if ( aRepaint.HasArea() ) + pImp->GetShell()->InvalidateWindows( aRepaint ); + if (IsInterrupt()) + 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 (IsInterrupt()) + return true; + break; + case WORD_COUNT : + { + const sal_Int32 nEnd = pTextNode->GetText().getLength(); + SwDocStat aStat; + pTextNode->CountWords( aStat, 0, nEnd ); + if (IsInterrupt()) + return true; + break; + } + case SMART_TAGS : + { + try { + const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) ); + bPageValid = bPageValid && !pTextNode->IsSmartTagDirty(); + if ( aRepaint.HasArea() ) + pImp->GetShell()->InvalidateWindows( aRepaint ); + } catch( const css::uno::RuntimeException&) { + // handle smarttag problems gracefully and provide diagnostics + TOOLS_WARN_EXCEPTION( "sw.core", "SMART_TAGS"); + } + if (IsInterrupt()) + 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 ( dynamic_cast< const SwFlyFrame *>( pObj ) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pObj); + 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 = 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 = pImp->GetFirstVisPage(pViewShell->GetOut()); + else + pPage = static_cast<SwPageFrame*>(pRoot->Lower()); + + pContentNode = nullptr; + nTextPos = COMPLETE_STRING; + + while ( pPage ) + { + bPageValid = true; + const SwContentFrame *pCnt = pPage->ContainsContent(); + while( pCnt && pPage->IsAnLower( pCnt ) ) + { + if ( DoIdleJob_( pCnt, eJob ) ) + 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 = dynamic_cast< const SwFlyFrame *>( pObj ) ) + { + const SwContentFrame *pC = pFly->ContainsContent(); + while( pC ) + { + if ( pC->IsTextFrame() ) + { + if ( DoIdleJob_( pC, eJob ) ) + return true; + } + pC = pC->GetNextContentFrame(); + } + } + } + } + + if( 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().IsOver( pImp->GetShell()->VisArea())) + break; + } + return false; +} + +#if HAVE_FEATURE_DESKTOP && defined DBG_UTIL +void SwLayIdle::ShowIdle( Color eColor ) +{ + if ( !m_bIndicator ) + { + m_bIndicator = true; + vcl::Window *pWin = 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->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pWin->SetFillColor( eColor ); + pWin->SetLineColor(); + pWin->DrawRect( aRect ); + pWin->Pop(); + } + } +} +#define SHOW_IDLE( Color ) ShowIdle( Color ) +#else +#define SHOW_IDLE( Color ) +#endif // DBG_UTIL + +SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) : + pRoot( pRt ), + pImp( pI ) +#ifdef DBG_UTIL + , m_bIndicator( false ) +#endif +{ + SAL_INFO("sw.idle", "SwLayIdle() entry"); + + pImp->m_pIdleAct = this; + + SHOW_IDLE( COL_LIGHTRED ); + + 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 : pImp->GetShell()->GetRingContainer()) + { + ++rSh.mnStartAction; + bool bVis = false; + if ( dynamic_cast<const SwCursorShell*>( &rSh) != nullptr ) + { + bVis = static_cast<SwCursorShell*>(&rSh)->GetCharRect().IsOver(rSh.VisArea()); + } + aBools.push_back( bVis ); + } + + bool bInterrupt(false); + { + SwLayAction aAction(pRoot, pImp, &m_aWatch); + aAction.SetWaitAllowed( false ); + aAction.Action(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 : pImp->GetShell()->GetRingContainer()) + { + --rSh.mnStartAction; + + if ( rSh.Imp()->GetRegion() ) + 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() && dynamic_cast<const SwCursorShell*>( &rSh) != nullptr ) + { + bActions |= aBools[nBoolIdx] != + static_cast<SwCursorShell*>(&rSh)->GetCharRect().IsOver( 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 : pImp->GetShell()->GetRingContainer()) + { + SwCursorShell* pCursorShell = nullptr; + if(dynamic_cast<const SwCursorShell*>( &rSh) != nullptr) + pCursorShell = static_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->GetRegion() ) + { + pViewImp->DelRegion(); + + // 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 = *pImp->GetShell()->GetViewOptions(); + const SwViewShell* pViewShell = 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*>(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 ) + { + pRoot->ResetIdleFormat(); + SfxObjectShell* pDocShell = pImp->GetShell()->GetDoc()->GetDocShell(); + pDocShell->Broadcast( SfxEventHint( SfxEventHintId::SwEventLayoutFinished, SwDocShell::GetEventName(STR_SW_EVENT_LAYOUT_FINISHED), pDocShell ) ); + // Limit lifetime of the text glyphs cache to a single run of the + // layout. + SwClearFntCacheTextGlyphs(); + } + } + + pImp->GetShell()->EnableSmooth( true ); + + if( pImp->IsAccessible() ) + pImp->FireAccessibleEvents(); + + SAL_INFO("sw.idle", "SwLayIdle() return"); + +#ifdef DBG_UTIL + if ( m_bIndicator && 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() +{ + 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..d7c9cc72f --- /dev/null +++ b/sw/source/core/layout/laycache.cxx @@ -0,0 +1,1205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <tools/stream.hxx> +#include <doc.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docstat.hxx> +#include <docary.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> +#include <set> + +using namespace ::com::sun::star; + +SwLayoutCache::SwLayoutCache() : 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( !pImpl ) + { + pImpl.reset( new SwLayCacheImpl ); + if( !pImpl->Read( rStream ) ) + { + pImpl.reset(); + } + } +} + +void SwLayCacheImpl::Insert( sal_uInt16 nType, sal_uLong nIndex, sal_Int32 nOffset ) +{ + aType.push_back( nType ); + mIndices.push_back( nIndex ); + 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 + 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, 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, 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 .. + { + SwLayCacheIoImpl aIo( rStream, true ); + // We want to save the relative index, so we need the index + // of the first content + sal_uLong 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()); + sal_uLong 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( 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) + { + sal_uLong 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( 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 = dynamic_cast<SwFlyFrame*>(pAnchoredObj)) + { + 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( !pImpl ) + return true; + const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout(); + if( pRootFrame ) + { + size_t nIndex = 0; + sal_uLong 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 >= 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()); + sal_uLong nNdIdx = pFrame->GetTextNodeFirst()->GetIndex(); + if( nNdIdx > nStartOfContent ) + { + bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow(); + nNdIdx -= nStartOfContent; + if( pImpl->GetBreakIndex( nIndex ) != nNdIdx || + SW_LAYCACHE_IO_REC_PARA != + pImpl->GetBreakType( nIndex ) || + (bFollow + ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset()) + : COMPLETE_STRING) != 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 + { + sal_uLong nNdIdx = + pTab->GetTable()->GetTableNode()->GetIndex(); + if( nNdIdx > nStartOfContent ) + { + nNdIdx -= nStartOfContent; + if( pImpl->GetBreakIndex( nIndex ) != nNdIdx || + SW_LAYCACHE_IO_REC_TABLE != + pImpl->GetBreakType( nIndex ) || + nOfst != 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() ) + { + pImpl.reset(); + } +} + +SwLayoutCache::~SwLayoutCache() +{ + OSL_ENSURE( !nLockCount, "Deleting a locked SwLayoutCache!?" ); +} + +/// helper class to create not nested section frames for nested sections. +SwActualSection::SwActualSection( SwActualSection *pUp, + SwSectionFrame *pSect, + SwSectionNode *pNd ) : + pUpper( pUp ), + pSectFrame( pSect ), + pSectNode( pNd ) +{ + if ( !pSectNode ) + { + const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx(); + pSectNode = pIndex->GetNode().FindSectionNode(); + } +} + +namespace { + +bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache, + SwNodes const& rNodes, sal_uLong 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, + sal_uLong 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 = USHRT_MAX; + } + } + else + { + mnIndex = std::numeric_limits<size_t>::max(); + mnStartOfContent = ULONG_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_uLong nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara; + if ( nNdCount <= 1 ) + { + //Estimates the number of paragraphs. + sal_uLong nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() - + mpDoc->GetNodes().GetEndOfExtras().GetIndex(); + //Tables have a little overhead... + nTmp -= mpDoc->GetTableFrameFormats()->size() * 25; + //Fly frames, too .. + nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() - + mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / 3 * 5; + if ( nTmp > 0 ) + nNdCount = 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( sal_uLong 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 ) + { + bDontCreateObjects = true; //frmtool + + // 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; + } + + bDontCreateObjects = false; + 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 ) + { + 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 + std::set< 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 + std::set< const SdrObject*, SdrObjectCompare > aFlySet; + for (SwAnchoredObject* pAnchoredObj : rObjs) + { + if (SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj)) // 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() ) + { + std::set< const SdrObject*, SdrObjectCompare >::iterator 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 ) : + pStream( &rStrm ), + nFlagRecEnd ( 0 ), + nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR), + nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR), + bWriteMode( bWrtMd ), + bError( false ) +{ + if( bWriteMode ) + pStream->WriteUInt16( nMajorVersion ) + .WriteUInt16( nMinorVersion ); + + else + pStream->ReadUInt16( nMajorVersion ) + .ReadUInt16( nMinorVersion ); +} + +void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType ) +{ + sal_uInt32 nPos = pStream->Tell(); + if( bWriteMode ) + { + aRecords.emplace_back(cType, nPos ); + pStream->WriteUInt32( 0 ); + } + else + { + sal_uInt32 nVal(0); + pStream->ReadUInt32( nVal ); + sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal); + if (!nVal || cRecTyp != cType || !pStream->good()) + { + OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" ); + OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" ); + aRecords.emplace_back(0, pStream->Tell() ); + bError = true; + } + else + { + sal_uInt32 nSize = nVal >> 8; + aRecords.emplace_back(cRecTyp, nPos+nSize ); + } + } +} + +// Close record +void SwLayCacheIoImpl::CloseRec() +{ + bool bRes = true; + OSL_ENSURE( !aRecords.empty(), "CloseRec: no levels" ); + if( !aRecords.empty() ) + { + sal_uInt32 nPos = pStream->Tell(); + if( bWriteMode ) + { + sal_uInt32 nBgn = aRecords.back().size; + pStream->Seek( nBgn ); + sal_uInt32 nSize = nPos - nBgn; + sal_uInt32 nVal = ( nSize << 8 ) | aRecords.back().type; + pStream->WriteUInt32( nVal ); + pStream->Seek( nPos ); + if( pStream->GetError() != ERRCODE_NONE ) + bRes = false; + } + else + { + sal_uInt32 n = aRecords.back().size; + OSL_ENSURE( n >= nPos, "CloseRec: too much data read" ); + if( n != nPos ) + { + pStream->Seek( n ); + if( n < nPos ) + bRes = false; + } + if( pStream->GetErrorCode() != ERRCODE_NONE ) + bRes = false; + } + aRecords.pop_back(); + } + + if( !bRes ) + bError = true; +} + +sal_uInt32 SwLayCacheIoImpl::BytesLeft() +{ + sal_uInt32 n = 0; + if( !bError && !aRecords.empty() ) + { + sal_uInt32 nEndPos = aRecords.back().size; + sal_uInt32 nPos = pStream->Tell(); + if( nEndPos > nPos ) + n = nEndPos - nPos; + } + return n; +} + +sal_uInt8 SwLayCacheIoImpl::Peek() +{ + sal_uInt8 c(0); + if( !bError ) + { + sal_uInt32 nPos = pStream->Tell(); + pStream->ReadUChar( c ); + pStream->Seek( nPos ); + if( pStream->GetErrorCode() != ERRCODE_NONE ) + { + c = 0; + bError = true; + } + } + return c; +} + +void SwLayCacheIoImpl::SkipRec() +{ + sal_uInt8 c = Peek(); + OpenRec( c ); + pStream->Seek( aRecords.back().size ); + CloseRec(); +} + +sal_uInt8 SwLayCacheIoImpl::OpenFlagRec() +{ + OSL_ENSURE( !bWriteMode, "OpenFlagRec illegal in write mode" ); + sal_uInt8 cFlags(0); + pStream->ReadUChar( cFlags ); + nFlagRecEnd = pStream->Tell() + ( cFlags & 0x0F ); + return (cFlags >> 4); +} + +void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen ) +{ + OSL_ENSURE( 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; + pStream->WriteUChar( cFlags ); + nFlagRecEnd = pStream->Tell() + nLen; +} + +void SwLayCacheIoImpl::CloseFlagRec() +{ + if( bWriteMode ) + { + OSL_ENSURE( pStream->Tell() == nFlagRecEnd, "Wrong amount of data written" ); + } + else + { + OSL_ENSURE( pStream->Tell() <= nFlagRecEnd, "Too many data read" ); + if( pStream->Tell() != nFlagRecEnd ) + pStream->Seek( 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..b4d21a3fd --- /dev/null +++ b/sw/source/core/layout/layhelp.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX +#define INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX + +#include <swrect.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<sal_uLong> mIndices; + /// either a textframe character offset, or a row index inside a table + std::deque<sal_Int32> aOffset; + std::vector<sal_uInt16> aType; + SwPageFlyCache m_FlyCache; + bool bUseFlyCache; + void Insert( sal_uInt16 nType, sal_uLong nIndex, sal_Int32 nOffset ); + +public: + SwLayCacheImpl() : bUseFlyCache(false) {} + + size_t size() const { return mIndices.size(); } + + bool Read( SvStream& rStream ); + + sal_uLong GetBreakIndex( size_t nIdx ) const { return mIndices[ nIdx ]; } + sal_Int32 GetBreakOfst( size_t nIdx ) const { return aOffset[ nIdx ]; } + sal_uInt16 GetBreakType( size_t nIdx ) const { return aType[ nIdx ]; } + + size_t GetFlyCount() const { return m_FlyCache.size(); } + SwFlyCache& GetFlyCache( size_t nIdx ) { return m_FlyCache[ nIdx ]; } + + bool IsUseFlyCache() const { return bUseFlyCache; } +}; + +// Helps to create the sectionframes during the InsertCnt_-function +// by controlling nested sections. +class SwActualSection +{ + SwActualSection *pUpper; + SwSectionFrame *pSectFrame; + SwSectionNode *pSectNode; +public: + SwActualSection( SwActualSection *pUpper, + SwSectionFrame *pSect, + SwSectionNode *pNd ); + + SwSectionFrame *GetSectionFrame() { return pSectFrame; } + void SetSectionFrame( SwSectionFrame *p ) { pSectFrame = p; } + SwSectionNode *GetSectionNode() { return pSectNode;} + void SetUpper(SwActualSection *p) { pUpper = p; } + SwActualSection *GetUpper() { return 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; + sal_uLong 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, + sal_uLong nNodeIndex, bool bCache ); + ~SwLayHelper(); + sal_uLong CalcPageCount(); + bool CheckInsert( sal_uLong 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> aRecords; + + SvStream *pStream; + + sal_uLong nFlagRecEnd; + + sal_uInt16 nMajorVersion; + sal_uInt16 nMinorVersion; + + bool bWriteMode : 1; + bool bError : 1; + +public: + SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ); + + /// Get input or output stream + SvStream& GetStream() const { return *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 bError; } + + sal_uInt16 GetMajorVersion() const { return nMajorVersion; } + sal_uInt16 GetMinorVersion() const { return 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, long nXL, long nYL, long nWL, long nHL ) : + SwRect( nXL, nYL, nWL, nHL ), nOrdNum( nO ), nPageNum( nP ){} +}; + +#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..0ae658570 --- /dev/null +++ b/sw/source/core/layout/layouter.cxx @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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" + +#define LOOP_DETECT 250 + +class SwLooping +{ + sal_uInt16 nMinPage; + sal_uInt16 nMaxPage; + sal_uInt16 nCount; + sal_uInt16 mnLoopControlStage; +public: + explicit SwLooping( SwPageFrame const * pPage ); + void Control( SwPageFrame* pPage ); + void Drastic( SwFrame* pFrame ); + bool IsLoopingLouieLight() const { return nCount > LOOP_DETECT - 30; }; +}; + +class SwEndnoter +{ + SwLayouter* pMaster; + SwSectionFrame* pSect; + std::unique_ptr<SwFootnoteFrames> pEndArr; +public: + explicit SwEndnoter( SwLayouter* pLay ) + : pMaster( pLay ), pSect( nullptr ) {} + void CollectEndnotes( SwSectionFrame* pSct ); + void CollectEndnote( SwFootnoteFrame* pFootnote ); + const SwSectionFrame* GetSect() const { return pSect; } + void InsertEndnotes(); + bool HasEndnotes() const { return pEndArr && !pEndArr->empty(); } +}; + +void SwEndnoter::CollectEndnotes( SwSectionFrame* pSct ) +{ + OSL_ENSURE( pSct, "CollectEndnotes: Which section?" ); + if( !pSect ) + pSect = pSct; + else if( pSct != pSect ) + return; + pSect->CollectEndnotes( pMaster ); +} + +void SwEndnoter::CollectEndnote( SwFootnoteFrame* pFootnote ) +{ + if( pEndArr && pEndArr->end() != std::find( pEndArr->begin(), 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( pEndArr ) + { + for (SwFootnoteFrame* pEndFootnote : *pEndArr) + { + if( pEndFootnote->GetAttr() == pFootnote->GetAttr() ) + { + SwFrame::DestroyFrame(pFootnote); + return; + } + } + } + if( !pEndArr ) + pEndArr.reset( new SwFootnoteFrames ); // deleted from the SwLayouter + pEndArr->push_back( pFootnote ); +} + +void SwEndnoter::InsertEndnotes() +{ + if( !pSect ) + return; + if( !pEndArr || pEndArr->empty() ) + { + pSect = nullptr; + return; + } + OSL_ENSURE( pSect->Lower() && pSect->Lower()->IsFootnoteBossFrame(), + "InsertEndnotes: Where's my column?" ); + SwFrame* pRef = pSect->FindLastContent( SwFindMode::MyLast ); + SwFootnoteBossFrame *pBoss = pRef ? pRef->FindFootnoteBossFrame() + : static_cast<SwFootnoteBossFrame*>(pSect->Lower()); + pBoss->MoveFootnotes_( *pEndArr ); + pEndArr.reset(); + pSect = nullptr; +} + +SwLooping::SwLooping( SwPageFrame const * pPage ) +{ + OSL_ENSURE( pPage, "Where's my page?" ); + nMinPage = pPage->GetPhyPageNum(); + nMaxPage = nMinPage; + nCount = 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 > nMaxPage ) + nMaxPage = nNew; + if( nNew < nMinPage ) + { + nMinPage = nNew; + nMaxPage = nNew; + nCount = 0; + mnLoopControlStage = 0; + } + else if( nNew > nMinPage + 2 ) + { + nMinPage = nNew - 2; + nMaxPage = nNew; + nCount = 0; + mnLoopControlStage = 0; + } + else if( ++nCount > 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 > nMinPage && pPage->GetPrev() ) + Drastic( static_cast<SwPageFrame*>(pPage->GetPrev())->Lower() ); + if( nNew < nMaxPage && pPage->GetNext() ) + Drastic( static_cast<SwPageFrame*>(pPage->GetNext())->Lower() ); + + ++mnLoopControlStage; + nCount = 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..932afa918 --- /dev/null +++ b/sw/source/core/layout/legacyitem.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 <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 SAL_TYPES_SIZEOFLONG == 8 + rStrm.WriteInt64(rItem.GetPos()); +#else + rStrm.WriteInt32(rItem.GetPos()); +#endif + 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..07ba49b7b --- /dev/null +++ b/sw/source/core/layout/newfrm.cxx @@ -0,0 +1,613 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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::mpCache = nullptr; + +static long FirstMinusSecond( long nFirst, long nSecond ) + { return nFirst - nSecond; } +static long SecondMinusFirst( long nFirst, long nSecond ) + { return nSecond - nFirst; } +static long SwIncrement( long nA, long nAdd ) + { return nA + nAdd; } +static long SwDecrement( long nA, 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<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::mnLastFrameId=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 ), + maPagesArea(), + mnViewWidth( -1 ), + mnColumns( 0 ), + mbBookMode( false ), + mbSidebarChanged( false ), + mbNeedGrammarCheck( false ), + mbCheckSuperfluous( false ), + mbIdleFormat( true ), + mbBrowseWidthValid( false ), + mbTurboAllowed( true ), + mbAssertFlyPages( true ), + mbIsVirtPageNum( false ), + mbIsNewLayout( true ), + mbCallbackActionEnabled ( false ), + mbLayoutFreezed ( false ), + mbHideRedlines(pFormat->GetDoc()->GetDocumentRedlineManager().IsHideRedlines()), + 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, this, true, true, false, false, nullptr); + + // 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 ); + //Remove masters that haven't been replaced yet from the list. + RemoveMasterObjs( mpDrawPage ); + 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..e48439a2b --- /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 <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 ( dynamic_cast<const SwFlyFrame*>( &_rAnchoredObj) == nullptr ) + { + // 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 ( dynamic_cast<const SwFlyFrame*>( &_rAnchoredObj) != nullptr ) + { + SwFlyFrame& rFlyFrame = static_cast<SwFlyFrame&>(_rAnchoredObj); + // --> #i34753# - reset flag, which prevents a positioning + if ( rFlyFrame.IsFlyLayFrame() ) + { + static_cast<SwFlyLayFrame&>(rFlyFrame).SetNoMakePos( false ); + } + + // #i81146# new loop control + int nLoopControlRuns = 0; + const int nLoopControlMax = 15; + + do { + if ( mpLayAction ) + { + mpLayAction->FormatLayoutFly( &rFlyFrame ); + // --> consider, if the layout action + // has to be restarted due to a delete of a page frame. + if ( mpLayAction->IsAgain() ) + { + break; + } + } + else + { + FormatLayout_( rFlyFrame ); + } + // --> #i34753# - prevent further positioning, if + // to-page|to-fly anchored Writer fly frame is already clipped. + if ( rFlyFrame.IsFlyLayFrame() && rFlyFrame.IsClipped() ) + { + static_cast<SwFlyLayFrame&>(rFlyFrame).SetNoMakePos( true ); + } + // #i23129#, #i36347# - pass correct page frame + // to the object formatter + SwObjectFormatter::FormatObjsAtFrame( rFlyFrame, + *(rFlyFrame.FindPageFrame()), + mpLayAction ); + if ( mpLayAction ) + { + mpLayAction->FormatFlyContent( &rFlyFrame ); + // --> consider, if the layout action + // has to be restarted due to a delete of a page frame. + if ( mpLayAction->IsAgain() ) + { + break; + } + } + else + { + FormatObjContent( rFlyFrame ); + } + + if ( ++nLoopControlRuns >= nLoopControlMax ) + { + OSL_FAIL( "LoopControl in SwObjectFormatter::FormatObj_: Stage 3!!!" ); + rFlyFrame.ValidateThisAndAllLowers( 2 ); + nLoopControlRuns = 0; + } + + // --> #i57917# + // stop formatting of anchored object, if restart of layout process is requested. + } while ( !rFlyFrame.isFrameAreaDefinitionValid() && + !_rAnchoredObj.RestartLayoutProcess() && + rFlyFrame.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..cc9f33216 --- /dev/null +++ b/sw/source/core/layout/objectformatterlayfrm.cxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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..c8d64cf44 --- /dev/null +++ b/sw/source/core/layout/objectformattertxtfrm.cxx @@ -0,0 +1,807 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <ftnfrm.hxx> + +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 ); + // #i58182# - consider new method signature + if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( nIdx ), + GetPgNumOfCollected( nIdx ), + IsCollectedAnchoredAtMaster( nIdx ), + nToPageNum, bDummy ) ) + { + // #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( _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(); + } + 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 ); + SwAnchoredObject* pObj = nullptr; + if ( !mrAnchorTextFrame.IsFollow() ) + { + pObj = GetFirstObjWithMovedFwdAnchor( + // #i35017# - constant name has changed + text::WrapInfluenceOnPosition::ONCE_CONCURRENT, + nToPageNum, bInFollow ); + } + // #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() ) + SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame ); + else + bInsert = false; + } + if ( bInsert ) + { + 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 ) + { + const SwSortedObjs* pObjs = GetAnchorFrame().GetDrawObjs(); + if ( pObjs ) + { + // 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 ) +{ + // #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 ) + { + // #i26945# - use new method <_CheckMovedFwdCondition(..)> + // #i43913# + // #i58182# - consider new method signature + if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( i ), + GetPgNumOfCollected( i ), + IsCollectedAnchoredAtMaster( i ), + _noToPageNum, _boInFollow ) ) + { + pRetAnchoredObj = pAnchoredObj; + break; + } + } + } + + return pRetAnchoredObj; +} + +// #i58182# +// - replace private method by corresponding static public method +bool SwObjectFormatterTextFrame::CheckMovedFwdCondition( + SwAnchoredObject& _rAnchoredObj, + const sal_uInt32 _nFromPageNum, + const bool _bAnchoredAtMasterBeforeFormatAnchor, + sal_uInt32& _noToPageNum, + bool& _boInFollow ) +{ + 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; + } + } + } + + return bAnchorIsMovedForward; +} + +// #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(); + if (pLowerFrame->IsFootnoteContFrame()) + { + 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; + } + } + 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() ) + { + // 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 ( _rAnchorTextFrame.IsInSct() ) + { + SwFrame* pSectFrame = _rAnchorTextFrame.GetUpper(); + while ( pSectFrame ) + { + if ( pSectFrame->IsSctFrame() || pSectFrame->IsCellFrame() ) + { + break; + } + pSectFrame = pSectFrame->GetUpper(); + } + if ( pSectFrame && 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 ) + { + 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. + SwFrame* pColFrameOfAnchor = _rAnchorTextFrame.FindColFrame(); + if ( pColFrameOfAnchor ) + { + // #i44049# + _rAnchorTextFrame.LockJoin(); + 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..15667b9ea --- /dev/null +++ b/sw/source/core/layout/objectformattertxtfrm.hxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 ); + + /** 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>. + + @return boolean + indicating, if 'anchor is moved forward' + */ + static bool CheckMovedFwdCondition( SwAnchoredObject& _rAnchoredObj, + const sal_uInt32 _nFromPageNum, + const bool _bAnchoredAtMasterBeforeFormatAnchor, + sal_uInt32& _noToPageNum, + bool& _boInFollow ); +}; + +#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..a3188eb2a --- /dev/null +++ b/sw/source/core/layout/pagechg.cxx @@ -0,0 +1,2546 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lok.hxx> +#include <ndole.hxx> +#include <sal/log.hxx> +#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 <docary.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; + long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight(); + SwRectFnSet aRectFnSet(this); + long nSize = aRectFnSet.GetWidth(getFrameArea()); + 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: + 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 ); + 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 the SwEditWin + SwViewShell* pSh = getRootFrame()->GetCurrShell(); + SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( pSh ); + 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(); + // #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 ) + { + 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 ( dynamic_cast< const SwDrawVirtObj *>( pSdrObj ) != nullptr ) + { + SwDrawVirtObj* pDrawVirtObj = static_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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + if ( pSh ) + pSh->SetFirstVisPageInvalid(); + sal_uInt8 nInvFlags = 0; + + if( pNew && RES_ATTRSET_CHG == pNew->Which() ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwLayoutFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags != 0 ) + { + InvalidatePage( this ); + if ( nInvFlags & 0x01 ) + InvalidatePrt_(); + if ( nInvFlags & 0x02 ) + SetCompletePaint(); + if ( nInvFlags & 0x04 && GetNext() ) + GetNext()->InvalidatePos(); + if ( nInvFlags & 0x08 ) + PrepareHeader(); + if ( nInvFlags & 0x10 ) + PrepareFooter(); + if ( nInvFlags & 0x20 ) + CheckGrid( nInvFlags & 0x40 ); + } +} + + +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 + SwFrame::SwClientNotify(rModify, rHint); +} + +void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &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 |= 0x20; + } + + // 2. header and footer: + const SwFormatHeader &rOldH = pOldFormat->GetHeader(); + const SwFormatHeader &rNewH = pNewFormat->GetHeader(); + if( rOldH != rNewH ) + rInvFlags |= 0x08; + + const SwFormatFooter &rOldF = pOldFormat->GetFooter(); + const SwFormatFooter &rNewF = pNewFormat->GetFooter(); + if( rOldF != rNewF ) + rInvFlags |= 0x10; + 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::Modify(..)> 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(), long(MINLAY) ) ); + aFrm.Width ( std::max( rSz.GetWidth(), 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 |= 0x03; + if ( aOldPageFrameRect.Height() != getFrameArea().Height() ) + rInvFlags |= 0x04; + } + 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 |= 0x22; + } + break; + + case RES_HEADER: + rInvFlags |= 0x08; + break; + + case RES_FOOTER: + rInvFlags |= 0x10; + break; + case RES_TEXTGRID: + rInvFlags |= 0x60; + break; + case RES_FRAMEDIR : + CheckDirChange(); + break; + + default: + bClear = false; + } + if ( bClear ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwLayoutFrame::Modify( 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 ( dynamic_cast< const SwFlyAtContentFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFly = static_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() ) + { + for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs()) + { + // #i28701# + if ( dynamic_cast< const SwFlyInContentFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFly = static_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() ) + { + for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs()) + { + // #i28701# + if ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + pFrame = pFly->ContainsContent(); + while ( pFrame ) + { + ::lcl_PrepFlyInCntRegister( pFrame ); + pFrame = pFrame->GetNextContentFrame(); + } + } + } + } +} + +//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. + * + * @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(); + bool bIsEmpty = pPage->IsEmptyPage(); + 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; + } + 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 + } + if ( bIsEmpty ) + { + // 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( !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()); + 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 && !pSibling->IsFootnotePage() && + !pSibling->FindFirstBodyContent() && + (!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(); + long nDocPos = LONG_MAX; + + // Check the corresponding last page if it is empty and stop loop at the last non-empty page. + do + { + bool bExistEssentialObjs = ( nullptr != pPage->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 #108784# - consider that drawing objects in + // header/footer are supported now. + bool bOnlySuperfluosObjs = true; + SwSortedObjs &rObjs = *pPage->GetSortedObjs(); + for ( size_t i = 0; bOnlySuperfluosObjs && i < rObjs.size(); ++i ) + { + // #i28701# + SwAnchoredObject* pAnchoredObj = rObjs[i]; + // OD 2004-01-19 #110582# - do not consider hidden objects + if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( + pAnchoredObj->GetDrawObj()->GetLayer() ) && + !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) + { + bOnlySuperfluosObjs = false; + } + } + bExistEssentialObjs = !bOnlySuperfluosObjs; + } + + // OD 19.06.2003 #108784# - optimization: check first, if essential objects + // exists. + const SwLayoutFrame* pBody = nullptr; + if ( bExistEssentialObjs || + pPage->FindFootnoteCont() || + ( nullptr != ( pBody = pPage->FindBodyCont() ) && + ( pBody->ContainsContent() || + // #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() ) ) ) ) + { + 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); + std::set< sal_uInt16 > neededPages; + + 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); + } + +#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.NotifyClients( nullptr, &rAnch ); + OSL_ENSURE( !pPage->GetSortedObjs() || + nCnt != pPage->GetSortedObjs()->size(), + "Object couldn't be reattached!" ); +#else + rFormat.NotifyClients( 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(); + 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 = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr; + if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width())) + || rFormat.GetFrameSize().GetWidthPercent()) + { + continue; + } + + 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 ( dynamic_cast<const SwCursorShell*>( &rSh) != nullptr ) + static_cast<SwCursorShell*>(&rSh)->StartAction(); + else + rSh.StartAction(); + } +} + +void SwRootFrame::EndAllAction( bool bVirDev ) +{ + if ( GetCurrShell() ) + for(SwViewShell& rSh : GetCurrShell()->GetRingContainer()) + { + const bool bOldEndActionByVirDev = rSh.IsEndActionByVirDev(); + rSh.SetEndActionByVirDev( bVirDev ); + if ( dynamic_cast<const SwCursorShell*>( &rSh) != nullptr ) + { + static_cast<SwCursorShell*>(&rSh)->EndAction(); + static_cast<SwCursorShell*>(&rSh)->CallChgLnk(); + if ( dynamic_cast<const SwFEShell*>( &rSh) != nullptr ) + static_cast<SwFEShell*>(&rSh)->SetChainMarker(); + } + else + rSh.EndAction(); + rSh.SetEndActionByVirDev( bOldEndActionByVirDev ); + } +} + +void SwRootFrame::UnoRemoveAllActions() +{ + if ( GetCurrShell() ) + for(SwViewShell& rSh : GetCurrShell()->GetRingContainer()) + { + // #i84729# + // No end action, if <SwViewShell> instance is currently in its end action. + // Recursives 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() ) + for(SwViewShell& rSh : GetCurrShell()->GetRingContainer()) + { + sal_uInt16 nActions = rSh.GetRestoreActions(); + while( nActions-- ) + { + if ( dynamic_cast<const SwCursorShell*>( &rSh) != nullptr ) + static_cast<SwCursorShell*>(&rSh)->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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFlyFrame( static_cast<SwFlyFrame*>(pAnchoredObj) ); + lcl_MoveAllLowers( pFlyFrame, 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 ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr ) + { + SwAnchoredDrawObject* pAnchoredDrawObj( static_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 ) +{ + const SwRect aFrame( pFrame->getFrameArea() ); + + // 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( pFrame->IsAccessibleFrame() ) + { + SwRootFrame *pRootFrame = pFrame->getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame ); + } + } + + // the move any objects + lcl_MoveAllLowerObjs( pFrame, rOffset ); + + // finally, for layout frames we have to call this function recursively: + if ( dynamic_cast< const SwLayoutFrame *>( pFrame ) != nullptr ) + { + 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 long nBorder = getFrameArea().Pos().getX(); + const long nVisWidth = mnViewWidth - 2 * nBorder; + const 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. + long nWidthRemain = nVisWidth; + + // after one row has been processed, these variables contain + // the width of the row and the maximum of the page heights + long nCurrentRowHeight = 0; + long nCurrentRowWidth = 0; + + // these variables are used to finally set the size of the + // root frame + 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 + long nPageWidth = 0; + 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 + long nSizeDiff = 0; + if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive()) + nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2; + + // adjust positions of pages in current row + long nX = nSizeDiff; + + const long nRowStart = nBorder + nSizeDiff; + const 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 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, aNewPagePos.getX() ); + nMaxPageRight = std::max( nMaxPageRight, 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 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 +{ + long nUpperLimit = 0; + 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.IsInside( 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.IsInside( 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(); + 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 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..c9c242435 --- /dev/null +++ b/sw/source/core/layout/pagedesc.cxx @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> +#include <hints.hxx> + +SwPageDesc::SwPageDesc(const OUString& rName, SwFrameFormat *pFormat, SwDoc *const pDoc) + : SwModify() + , 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_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 ) + : SwModify() + , BroadcasterMixin() + , 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 ) +{ + 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_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() ); + + 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(auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + const sal_uInt16 nWhich = pLegacyHint->m_pOld + ? pLegacyHint->m_pOld->Which() + : pLegacyHint->m_pNew + ? pLegacyHint->m_pNew->Which() + : 0; + NotifyClients(pLegacyHint->m_pOld, pLegacyHint->m_pNew); + 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 SwModify* 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; +} + +// Page styles +static const char* STR_POOLPAGE[] = +{ + STR_POOLPAGE_STANDARD, + STR_POOLPAGE_FIRST, + STR_POOLPAGE_LEFT, + STR_POOLPAGE_RIGHT, + STR_POOLPAGE_JAKET, + STR_POOLPAGE_REGISTER, + STR_POOLPAGE_HTML, + STR_POOLPAGE_FOOTNOTE, + STR_POOLPAGE_ENDNOTE, + STR_POOLPAGE_LANDSCAPE +}; + +SwPageDesc* SwPageDesc::GetByName(SwDoc& rDoc, const OUString& 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDesc")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_StyleName"), "%s", + BAD_CAST(m_StyleName.toUtf8().getStr())); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pFollow"), "%p", m_pFollow); + xmlTextWriterWriteFormatAttribute( + pWriter, BAD_CAST("m_eUse"), "0x%s", + BAD_CAST(OString::number(static_cast<int>(m_eUse), 16).getStr())); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_Master")); + m_Master.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_Left")); + m_Left.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstMaster")); + m_FirstMaster.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstLeft")); + m_FirstLeft.dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDescs")); + + for (const auto& pPageDesc : m_PosIndex) + { + pPageDesc->dumpAsXml(pWriter); + } + + 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..7e3986006 --- /dev/null +++ b/sw/source/core/layout/paintfrm.cxx @@ -0,0 +1,7495 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.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> + +#define COL_NOTES_SIDEPANE Color(230,230,230) +#define COL_NOTES_SIDEPANE_BORDER Color(200,200,200) +#define COL_NOTES_SIDEPANE_SCROLLAREA Color(230,230,220) + +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 aColor; + SvxBorderLineStyle nStyle; + const SwTabFrame *pTab; + SubColFlags nSubColor; //colorize subsidiary lines + bool bPainted; //already painted? + sal_uInt8 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 aColor;} + SvxBorderLineStyle GetStyle() const { return nStyle; } + const SwTabFrame *GetTab() const { return pTab; } + void SetPainted() { bPainted = true; } + void Lock( bool bLock ) { if ( bLock ) + ++nLock; + else if ( nLock ) + --nLock; + } + bool IsPainted() const { return bPainted; } + bool IsLocked() const { return nLock != 0; } + SubColFlags GetSubColor() const { return 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 > 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 nLastCount; //avoid unnecessary cycles in PaintLines + SwLineRects() : 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 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(const drawinglayer::primitive2d::Primitive2DContainer& rContainer); + drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear() + { + drawinglayer::primitive2d::Primitive2DContainer lines; + lines.swap(m_Lines); + return lines; + } +}; + +} + +// Default zoom factor +const static 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 + long nSPixelSzW; + long nSPixelSzH; + long nSHalfPixelSzW; + long nSHalfPixelSzH; + long nSMinDistPixelW; + 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(const drawinglayer::primitive2d::Primitive2DContainer& rContainer) +{ + if(!rContainer.empty()) + { + m_Lines.append(rContainer); + } +} + +SwLineRect::SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyl, + const SwTabFrame *pT, const SubColFlags nSCol ) : + SwRect( rRect ), + nStyle( nStyl ), + pTab( pT ), + nSubColor( nSCol ), + bPainted( false ), + nLock( 0 ) +{ + if ( pCol != nullptr ) + 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 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 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 = aLineRects.rbegin(); it != 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; + } + } + 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 long nAdd = 20; + + std::vector<SwLineRect*> aCheck; + + for (size_t i = 0; i < aLineRects.size(); ++i) + { + SwLineRect &rL1 = aLineRects[i]; + if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() ) + continue; + + aCheck.clear(); + + const bool bVert = rL1.Height() > rL1.Width(); + 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 = aLineRects.begin(); it2 != aLineRects.end(); ++it2) + { + SwLineRect &rL2 = *it2; + if ( rL2.GetTab() != rL1.GetTab() || + rL2.IsPainted() || + rL2.IsLocked() || + (bVert == (rL2.Height() > rL2.Width())) ) + continue; + + 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.IsInside( aIns ) ) + continue; + 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.IsInside( aIns ) ) + continue; + 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 ) + { + aLineRects.erase(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 < 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(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.aLineRects.begin(); itK != rRects.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.IsOver( rLine ) ) + { + if ( bVerticalSubs ) // Vertical? + { + if ( aSubsRect.Left() <= rLine.Right() && + aSubsRect.Right() >= rLine.Left() ) + { + long nTmp = rLine.Top()-(properties.nSPixelSzH+1); + if ( aSubsLineRect.Top() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Bottom( nTmp ); + 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 ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + else // Horizontal + { + if ( aSubsRect.Top() <= rLine.Bottom() && + aSubsRect.Bottom() >= rLine.Top() ) + { + long nTmp = rLine.Left()-(properties.nSPixelSzW+1); + if ( aSubsLineRect.Left() < nTmp ) + { + SwRect aNewSubsRect( aSubsLineRect ); + aNewSubsRect.Right( nTmp ); + 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 ); + aLineRects.emplace_back( aNewSubsRect, nullptr, aSubsLineRect.GetStyle(), nullptr, + aSubsLineRect.GetSubColor() ); + } + aLineRects.erase(aLineRects.begin() + i); + --i; + break; + } + } + } + } + } +} + +void SwLineRects::LockLines( bool bLock ) +{ + for (SwLineRect& rLRect : aLineRects) + rLRect.Lock( bLock ); +} + +static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect ) +{ + long startX = rLRect.Left( ), endX; + long startY = rLRect.Top( ), endY; + + // Discriminate vertically stretched rect from horizontally stretched + // and restrict minimum nHalfLWidth to 1 + long nHalfLWidth = std::max( static_cast<long>(std::min( rLRect.Width( ), rLRect.Height( ) ) / 2), 1L ); + + 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 ( aLineRects.size() == nLastCount ) + return; + + // #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut ); + + pOut->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOut->SetFillColor(); + pOut->SetLineColor(); + ConnectEdges( pOut, properties ); + const Color *pLast = nullptr; + + bool bPaint2nd = false; + size_t nMinCount = aLineRects.size(); + + for ( size_t i = 0; i < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = 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 < aLineRects.size(); ++i ) + { + SwLineRect &rLRect = 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(); + } + } + nLastCount = nMinCount; + pOut->Pop(); + +} + +void SwSubsRects::PaintSubsidiary( OutputDevice *pOut, + const SwLineRects *pRects, + SwPaintProperties const & properties ) +{ + if ( !aLineRects.empty() ) + { + // #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 != aLineRects.size(); ++i) + { + SwLineRect &rLi = aLineRects[i]; + const bool bVerticalSubs = rLi.Height() > rLi.Width(); + + for (size_type k = i + 1; k != aLineRects.size(); ++k) + { + SwLineRect &rLk = aLineRects[k]; + if ( rLi.SSize() == rLk.SSize() ) + { + if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) ) + { + if ( bVerticalSubs ) + { + long nLi = rLi.Right(); + long nLk = rLk.Right(); + if ( rLi.Top() == rLk.Top() && + ((nLi < rLk.Left() && nLi+21 > rLk.Left()) || + (nLk < rLi.Left() && nLk+21 > rLi.Left()))) + { + aLineRects.erase(aLineRects.begin() + i); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + else + { + long nLi = rLi.Bottom(); + long nLk = rLk.Bottom(); + if ( rLi.Left() == rLk.Left() && + ((nLi < rLk.Top() && nLi+21 > rLk.Top()) || + (nLk < rLi.Top() && nLk+21 > rLi.Top()))) + { + aLineRects.erase(aLineRects.begin() + i); + // don't continue with inner loop any more: + // the array may shrink! + --i; + break; + } + } + } + } + } + } + + if ( pRects && (!pRects->aLineRects.empty()) ) + RemoveSuperfluousSubsidiaryLines( *pRects, properties ); + + if ( !aLineRects.empty() ) + { + pOut->Push( PushFlags::FILLCOLOR|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 : 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 = 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 long lcl_AlignWidth( const long nWidth, SwPaintProperties const & properties ) +{ + if ( nWidth ) + { + const long nW = nWidth % properties.nSPixelSzW; + + if ( !nW || nW > properties.nSHalfPixelSzW ) + return std::max(1L, nWidth - properties.nSHalfPixelSzW); + } + return nWidth; +} + +static long lcl_AlignHeight( const long nHeight, SwPaintProperties const & properties ) +{ + if ( nHeight ) + { + const long nH = nHeight % properties.nSPixelSzH; + + if ( !nH || nH > properties.nSHalfPixelSzH ) + return std::max(1L, 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 ) + { + SwTwips nDiff = rBox.GetTop() ? + rBox.CalcLineSpace( SvxBoxItemLine::TOP ) : + 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; + + if (dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr) + continue; + + const SwFlyFrame *pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + + if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.IsOver(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_implDrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + vcl::RenderContext* _pOut, + 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.GetTransparency() != 0 ) + /// background color is transparent --> draw transparent. + { + bDrawTransparent = true; + nTransparencyPercent = (aColor.GetTransparency()*100 + 0x7F)/0xFF; + } + else if ( (_rGraphicObj.GetAttr().GetTransparency() != 0) && + (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) ) + /// graphic is drawn transparent and background color is + /// "no fill"/"auto fill" --> draw transparent + { + bDrawTransparent = true; + nTransparencyPercent = (_rGraphicObj.GetAttr().GetTransparency()*100 + 0x7F)/0xFF; + } + + if ( bDrawTransparent ) + { + /// draw background transparent + if( _pOut->GetFillColor() != aColor.GetRGBColor() ) + _pOut->SetFillColor( aColor.GetRGBColor() ); + tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() ); + _pOut->DrawTransparent( aPoly, nTransparencyPercent ); + } + else + { + /// draw background opaque + if ( _pOut->GetFillColor() != aColor ) + _pOut->SetFillColor( aColor ); + _pOut->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_implDrawGraphicBackgrd(..)> for the intrinsic drawing. + * + * @param _rBackgrdBrush + * background brush contain the color the background has to be drawn. + * + * @param _pOut + * 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_DrawGraphicBackgrd( const SvxBrushItem& _rBackgrdBrush, + OutputDevice* _pOut, + 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_implDrawGraphicBackgrd( _rBackgrdBrush, _pOut, _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_DrawGraphicBackgrd> + * + * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool> + */ +static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext *pOut, + 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, pOut ); + + // Change type from <bool> to <bool>. + const bool bNotInside = !rOut.IsInside( aAlignedGrfRect ); + if ( bNotInside ) + { + pOut->Push( PushFlags::CLIPREGION ); + pOut->IntersectClipRegion( rOut.SVRect() ); + } + + GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject()); + + // Outsource drawing of background with a background color + ::lcl_DrawGraphicBackgrd( rBrush, pOut, 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, *pOut ); + + const basegfx::B2DHomMatrix aGraphicTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix( + aAlignedGrfRect.Width(), aAlignedGrfRect.Height(), + aAlignedGrfRect.Left(), aAlignedGrfRect.Top())); + + paintGraphicUsingPrimitivesHelper( + *pOut, + *pGrf, + pGrf->GetAttr(), + aGraphicTransform, + OUString(), + OUString(), + OUString()); + + if ( bNotInside ) + pOut->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())) + { + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + + // 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(aSvtOptionsDrawinglayer.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, rSequence); + pPrimitives = &primitives; + } + assert(pPrimitives && pPrimitives->size()); + + const drawinglayer::geometry::ViewInformation2D aViewInformation2D( + basegfx::B2DHomMatrix(), + rOut.GetViewTransformation(), + aPaintRange, + nullptr, + 0.0, + uno::Sequence< beans::PropertyValue >()); + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rOut, + aViewInformation2D) ); + if(pProcessor) + { + pProcessor->process(*pPrimitives); + return true; + } + } + } + } + + return false; +} + +void DrawGraphic( + const SvxBrushItem *pBrush, + vcl::RenderContext *pOutDev, + 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.IsInside( 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, pOutDev ); + // draw background color for aligned paint rectangle + lcl_DrawGraphicBackgrd( *pBrush, pOutDev, 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 + pOutDev->Push( PushFlags::CLIPREGION ); + pOutDev->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( pOutDev, + aAlignedPaintRect.SVRect(), + aGrf.SSize(), + Size( aPaintOffset.X(), aPaintOffset.Y() ), + std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) ); + } + // reset clipping at output device + pOutDev->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( !pOutDev, "new Graphic position?" ); + } + + /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of + /// graphic is already drawn or not. + bool bGrfBackgrdAlreadyDrawn = false; + if ( bRetouche ) + { + pOutDev->Push( PushFlags::FILLCOLOR|PushFlags::LINECOLOR ); + pOutDev->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.GetTransparency() != 0) && + (pBrush->GetColor() == COL_TRANSPARENT) + ) + { + bTransparentGrfWithNoFillBackgrd = true; + nGrfTransparency = aGrfAttr.GetTransparency(); + } + } + 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.GetTransparency() != 0) || + bTransparentGrfWithNoFillBackgrd ) ) + { + eDrawStyle = Transparent; + } + + // #i75614# reset draw mode in high contrast mode in order to get fill color set + const DrawModeFlags nOldDrawMode = pOutDev->GetDrawMode(); + if ( gProp.pSGlobalShell->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + pOutDev->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( pOutDev->GetFillColor() != aColor.GetRGBColor() ) + pOutDev->SetFillColor( aColor.GetRGBColor() ); + break; + } + default: + { + if( pOutDev->GetFillColor() != aColor ) + pOutDev->SetFillColor( aColor ); + break; + } + } + + // #i75614# + // restore draw mode + pOutDev->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 : aColor.GetTransparency() + )*100 + 0x7F)/0xFF); + // draw poly-polygon transparent + pOutDev->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 ) + { + pOutDev->DrawRect( aRegion[i].SVRect() ); + } + } + } + pOutDev ->Pop(); + } + + if( bDraw && aGrf.IsOver( rOut ) ) + lcl_DrawGraphic( *pBrush, pOutDev, rSh, aGrf, rOut, bGrfNum, gProp, + bGrfBackgrdAlreadyDrawn ); + + if( bReplaceGrfNum ) + { + const BitmapEx& rBmp = rSh.GetReplacementBitmap(false); + vcl::Font aTmp( pOutDev->GetFont() ); + Graphic::DrawEx(pOutDev, 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; + + svx::frame::Style maAttribute; + + enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 }; + +public: + SwLineEntry( SwTwips nKey, + SwTwips nStartPos, + SwTwips nEndPos, + const svx::frame::Style& rAttribute ); + + OverlapType Overlaps( const SwLineEntry& rComp ) const; +}; + +} + +SwLineEntry::SwLineEntry( SwTwips nKey, + SwTwips nStartPos, + SwTwips nEndPos, + const svx::frame::Style& rAttribute ) + : mnKey( nKey ), + mnStartPos( nStartPos ), + mnEndPos( nEndPos ), + 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; +} + +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( const Point&, + const Point&, + svx::frame::Style*, + bool bHori ) 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 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 ) + { + const SwLayoutFrame* pLowerLayFrame = dynamic_cast<const SwLayoutFrame*>(pLower); + if ( pLowerLayFrame && !pLowerLayFrame->IsTabFrame() ) + 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 ); + 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.IsOver(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( const Point& rStartPoint, + const Point& rEndPoint, + svx::frame::Style* pStyles, + bool bHori ) const +{ + // 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, + + 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; + } + 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 ( 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; + } + } + 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; + } + } +} + +/** + * 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); + + aR.MirrorSelf(); + 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 ); + + SwLineEntry aLeft (nLeft, nTop, nBottom, + bVert ? aB : (bR2L ? aR : aL)); + SwLineEntry aRight (nRight, nTop, nBottom, + bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR)); + SwLineEntry aTop (nTop, nLeft, nRight, + bVert ? aL : (bBottomAsTop ? aB : aT)); + SwLineEntry aBottom(nBottom, nLeft, nRight, + 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; + 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, rOldAttr ); + + // new middle segment + const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rOld.mnEndPos, 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, rOldAttr ); + + // new middle segment + const SwLineEntry aMiddle( nKey, rNew.mnStartPos, rNew.mnEndPos, rCmpAttr ); + + // new right segment + const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, 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, rNewAttr ); + + // new middle segment + const SwLineEntry aMiddle( nKey, rOld.mnStartPos, rNew.mnEndPos, rCmpAttr ); + + // new right segment + const SwLineEntry aRight( nKey, rNew.mnEndPos, rOld.mnEndPos, 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 drawinglayer::primitive2d::Primitive2DContainer createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo) override + { + bool bPaint( true ); + + SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject(); + if ( pObj ) + { + bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell ); + } + + if ( !bPaint ) + { + return drawinglayer::primitive2d::Primitive2DContainer(); + } + + return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, rDisplayInfo ); + } + }; + +} // 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() && !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()->DelRegion(); + } + + 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.IsOver( 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->GetDrawBackgrdColor()); + + 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.IsOver( 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 fnCheck; + long nLimit; +public: + SwShortCut( const SwFrame& rFrame, const SwRect& rRect ); + bool Stop( const SwRect& rRect ) const + { return (rRect.*fnCheck)( 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 ) + { + fnCheck = &SwRect::GetBottomDistance; + nLimit = rRect.Top(); + } + else + { + fnCheck = &SwRect::GetLeftDistance; + nLimit = rRect.Left() + rRect.Width(); + } + } + else if( bVert == rFrame.IsNeighbourFrame() ) + { + fnCheck = &SwRect::GetTopDistance; + nLimit = rRect.Top() + rRect.Height(); + } + else + { + if ( rFrame.IsVertLR() ) + { + fnCheck = &SwRect::GetLeftDistance; + nLimit = rRect.Right(); + } + else + { + fnCheck = &SwRect::GetRightDistance; + 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.IsOver( aPaintRect ) ) + { + if ( bCnt && pFrame->IsCompletePaint() && + !rRect.IsInside( 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 + drawinglayer::primitive2d::PolygonHairlinePrimitive2D * pPlainLine = + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aLinePolygon, aOtherColor ); + + aSeq[0] = drawinglayer::primitive2d::Primitive2DReference( pPlainLine ); + + // Dashed line in twips + aStrokePattern.push_back( 40 ); + aStrokePattern.push_back( 40 ); + + aSeq.resize( 2 ); + } + + // Compute the dashed line primitive + drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D * pLine = + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D ( + basegfx::B2DPolyPolygon( aLinePolygon ), + drawinglayer::attribute::LineAttribute( aColor ), + drawinglayer::attribute::StrokeAttribute( aStrokePattern ) ); + + aSeq[ aSeq.size( ) - 1 ] = drawinglayer::primitive2d::Primitive2DReference( pLine ); + + return aSeq; +} + +void SwPageFrame::PaintBreak( ) const +{ + if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER && + !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() && + !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() && + !gProp.pSGlobalShell->IsPreview() ) + { + 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() ) + { + const SwFrame* pBodyFrame = Lower(); + while ( pBodyFrame && !pBodyFrame->IsBodyFrame() ) + pBodyFrame = pBodyFrame->GetNext(); + + if ( pBodyFrame ) + { + const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent(); + if ( pCnt && pCnt->IsColBreak( true ) ) + { + // 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() ) + { + 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 ); + 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 ); + } + + drawinglayer::primitive2d::TextSimplePortionPrimitive2D * pText = + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, + aBreakText, 0, aBreakText.getLength(), + std::vector< double >(), + aFontAttr, + lang::Locale(), + aLineColor ); + aSeq.push_back( drawinglayer::primitive2d::Primitive2DReference( pText ) ); + + 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 ) + { + SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin(); + + const SwLayoutFrame* pBody = FindBodyCont(); + if ( pBody ) + { + 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 ) ) ) + { + bool bRtl = AllSettings::GetLayoutRTL(); + const SwRect& rVisArea = gProp.pSGlobalShell->VisArea(); + 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; + + 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 ) ) + { + const SwFrame* pFootnoteContFrame = Lower(); + while ( pFootnoteContFrame ) + { + if ( pFootnoteContFrame->IsFootnoteContFrame() ) + aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() ); + pFootnoteContFrame = pFootnoteContFrame->GetNext(); + } + + 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* pBackgrdBrush = nullptr; + const Color* pSectionTOXColor = nullptr; + SwRect aDummyRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if ( GetBackgroundBrush( aFillAttributes, pBackgrdBrush, pSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) ) + { + if ( pSectionTOXColor && + (pSectionTOXColor->GetTransparency() != 0) && + (*pSectionTOXColor != COL_TRANSPARENT) ) + { + bBackgroundTransparent = true; + } + else if(aFillAttributes && aFillAttributes->isUsed()) + { + bBackgroundTransparent = aFillAttributes->isTransparent(); + } + else if ( pBackgrdBrush ) + { + if ( (pBackgrdBrush->GetColor().GetTransparency() != 0) && + (pBackgrdBrush->GetColor() != COL_TRANSPARENT) ) + { + bBackgroundTransparent = true; + } + else + { + const GraphicObject *pTmpGrf = + pBackgrdBrush->GetGraphicObject(); + if ( pTmpGrf && + (pTmpGrf->GetAttr().GetTransparency() != 0) + ) + { + bBackgroundTransparent = true; + } + } + } + } + } + + return bBackgroundTransparent; +}; + +bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh ) +{ + SdrObjUserCall *pUserCall; + + if ( nullptr == ( pUserCall = GetUserCall(pObj) ) ) + 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 ( dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwVirtFlyDrawObj*>(pObj)->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().IsOver( 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().IsOver( 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; +}; + +} + +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.IsOver(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( 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 SwFrameFormat* pSwFrameFormat = dynamic_cast< const SwFrameFormat* >(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 && + ((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( PushFlags::FILLCOLOR|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(); + + // 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(); + + 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 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 long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties ); + const 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.IsOver( 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.IsOver( 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) + : BufferedDecompositionPrimitive2D(), + 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 + ImplPrimitive2DIDBlock(SwBorderRectanglePrimitive2D, 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)) + { + 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(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, + uno::Sequence< beans::PropertyValue >() ); + + 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.IsInside( 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(); + } + + 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); + const svx::frame::Style aStyleRight(pRightBorder, 1.0); + const svx::frame::Style aStyleBottom(pBottomBorder, 1.0); + const svx::frame::Style aStyleLeft(pLeftBorder, 1.0); + drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget; + + aBorderLineTarget.append( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D( + aBorderTransform, + aStyleTop, + aStyleRight, + aStyleBottom, + aStyleLeft))); + gProp.pBLines->AddBorderLines(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.IsInside( 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<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.IsOver( 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() ) ) + { + const SwLayoutFrame* pBody = FindBodyCont(); + if( pBody ) + { + SwRect aGrid( pBody->getFramePrintArea() ); + aGrid += pBody->getFrameArea().Pos(); + + SwRect aInter( aGrid ); + aInter.Intersection( rRect ); + if( aInter.HasArea() ) + { + bool bGrid = pGrid->GetRubyTextBelow(); + bool bCell = GRID_LINES_CHARS == pGrid->GetGridType(); + long nGrid = pGrid->GetBaseHeight(); + const SwDoc* pDoc = GetFormat()->GetDoc(); + long nGridWidth = GetGridWidth(*pGrid, *pDoc); + long nRuby = pGrid->GetRubyHeight(); + 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( aInter.Left(), nY ); + SwTwips nHeight = std::min(nRight, 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( aInter.Left(), nY ); + SwTwips nW = std::min(nRight, 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(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( aInter.Left(), nY ); + SwTwips nW = std::min(nRight, 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() ) + { + // 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::mnShadowPxWidth = 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() ); + + long lShadowAdjustment = mnShadowPxWidth - 1; // TODO: extract this + + _orHorizontalShadowRect.Chg( + Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ), + Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ), + mnShadowPxWidth ) ); + + 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) +{ + // 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); + + long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0; + 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( + new drawinglayer::primitive2d::DiscreteShadow( + vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK, + ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor))); + + drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get(); + static vcl::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj( new BitmapEx ); + static vcl::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj( new BitmapEx ); + 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(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getBottom().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageBottomShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getTop().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageTopShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageTopRightShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getRight().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageRightShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + aFilledSquare.Erase( aShadowColor ); + aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask ); + + aMask = AlphaMask( shadowMask.getLeft().GetBitmap() ); + aFilledSquare = Bitmap( aMask.GetSizePixel(), 24 ); + 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() - mnShadowPxWidth) ) ), + aPageBottomRightShadow ); + pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - mnShadowPxWidth ) ), + aPageTopRightShadow ); + + if (aPagePxRect.Height() > 2 * mnShadowPxWidth) + { + const long nWidth = aPageRightShadow.GetSizePixel().Width(); + const long nHeight = aPagePxRect.Height() - 2 * (mnShadowPxWidth - 1); + if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE) + aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast); + + lcl_paintBitmapExToRect(pOut, + Point(aPaintRect.Right() + mnShadowPxWidth, aPagePxRect.Top() + mnShadowPxWidth - 1), + Size(nWidth, nHeight), + aPageRightShadow, RIGHT); + } + } + + // Left shadows and corners + if(bPaintLeftShadow) + { + const long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width(); + pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, + aPagePxRect.Bottom() + 1 + mnShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow ); + pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - mnShadowPxWidth ) ), aPageTopLeftShadow ); + if (aPagePxRect.Height() > 2 * mnShadowPxWidth) + { + const long nWidth = aPageLeftShadow.GetSizePixel().Width(); + const long nHeight = aPagePxRect.Height() - 2 * (mnShadowPxWidth - 1); + if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE) + aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast); + + lcl_paintBitmapExToRect(pOut, + Point(lLeft, aPagePxRect.Top() + mnShadowPxWidth - 1), + Size(nWidth, nHeight), + aPageLeftShadow, LEFT); + } + } + + // Bottom shadow + const 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 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() - mnShadowPxWidth), + 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 + { + sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight(); + const tools::Rectangle &aVisRect = _pViewShell->VisArea().SVRect(); + //draw border and sidepane + _pViewShell->GetOut()->SetLineColor(); + if (!bRight) + { + _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_BORDER); + _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(COL_NOTES_SIDEPANE); + _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()))) ; + } + else + { + _pViewShell->GetOut()->SetFillColor(COL_NOTES_SIDEPANE_BORDER); + 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(COL_NOTES_SIDEPANE); + SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height())); + _pViewShell->GetOut()->DrawRect(aSidebar.SVRect()); + } + if (pMgr->ShowScrollbar(nPageNum)) + { + // 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.IsOver(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_NOTES_SIDEPANE_SCROLLAREA); + } + _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.IsOver(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_NOTES_SIDEPANE_SCROLLAREA); + } + _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( mnShadowPxWidth + 1 ); + aPagePxRect.AddTop( - mnShadowPxWidth - 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() - mnShadowPxWidth - 1); + if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + mnShadowPxWidth + 1); + + _orBorderAndShadowBoundRect = 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( PushFlags::FILLCOLOR|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; + const 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 ); + //- 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.IsOver( 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 ) + { + aRect = getFrameArea(); + } + 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(pOut && 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 ) + { + 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.IsOver( aBorderRect ) ) + { + SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame ); + const SwBorderAttrs &rTmpAttrs = *aAccess.Get(); + if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.IsOver( 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() ) + { + if ( rRect.HasArea() ) + { + //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().IsOver( 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() ) && + dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + const SwFlyFrame *pFly = + static_cast<const SwFlyFrame*>(pAnchoredObj); + if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().IsOver( 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 long (Point:: *pmfPtGet)() const; + typedef void (Point:: *pmfPtSet)(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 long nP1OthPt = !bHori ? rP1.X() : rP1.Y(); + const tools::Rectangle &rBound = pObj->GetCurrentBoundRect(); + const Point aDrPt( rBound.TopLeft() ); + const long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y(); + const Size aDrSz( rBound.GetSize() ); + const long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height(); + + if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz ) + { + const long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y(); + const 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 ); + + drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine = + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aPolygon, aLineColor ); + aSeq[i] = drawinglayer::primitive2d::Primitive2DReference( pLine ); + } + + 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 ); + + drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine = + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aPolygon, aLineColor ); + aSeq[0] = drawinglayer::primitive2d::Primitive2DReference( pLine ); + + 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 ); + + drawinglayer::primitive2d::PolygonHairlinePrimitive2D* pLine = + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aPolygon, aLineColor ); + aSeq[i] = drawinglayer::primitive2d::Primitive2DReference( pLine ); + } + + return aSeq; +} + +void SwPageFrame::PaintSubsidiaryLines( const SwPageFrame *, + const SwRect & ) const +{ + if ( !gProp.pSGlobalShell->IsHeaderFooterEdit() ) + { + 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.IsOver( 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() ) + { + SwLayoutFrame::RefreshExtraData( aRect ); + + if ( bLineInFly && GetSortedObjs() ) + for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs()) + { + if ( auto pFly = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) ) + { + 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 = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) ) + { + 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::GetDrawBackgrdColor() const +{ + const SvxBrushItem* pBrushItem; + const Color* pDummyColor; + SwRect aDummyRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if ( GetBackgroundBrush( aFillAttributes, pBrushItem, pDummyColor, 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->GetDrawBackgrdColor()); + 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, + const Color*& rpCol, + SwRect &rOrigRect, + bool bLowerMode, + bool bConsiderTextBox ) const +{ + const SwFrame *pFrame = this; + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + const SwViewOption *pOpt = pSh->GetViewOptions(); + rpBrush = nullptr; + rpCol = nullptr; + 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 ) + { + rpCol = &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().GetTransparency() || rBack.GetGraphicPos() != GPOS_NONE || + + // done when direct color is forced + rpCol || + + // 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(); + } + 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* ) +{ + return Graphic(); +} + +Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap ) +{ + 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; + + 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->GetDrawBackgrdColor()); + 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* ) +{ + Graphic aRet; + SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel(); + if ( pMod ) + { + SdrObject *pObj = FindSdrObject(); + std::unique_ptr<SdrView> pView( new SdrView( *pMod ) ); + SdrPageView *pPgView = pView->ShowSdrPage(pView->GetModel()->GetPage(0)); + pView->MarkObj( pObj, pPgView ); + aRet = pView->GetMarkedObjBitmapEx(); + pView->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..2ad9d79e5 --- /dev/null +++ b/sw/source/core/layout/sectfrm.cxx @@ -0,0 +1,2916 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <o3tl/safeint.hxx> +#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) +{ + 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 ) +{ + 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); + 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. + { + SwViewShell* pViewShell( getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(FindNextCnt( true )), + dynamic_cast<SwTextFrame*>(FindPrevCnt()) ); + } + } + 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 ) + { + { + 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 + if ( nullptr != (pFrame = GetPrev()) ) + { 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 ) + { + SwRectFnSet aRectFnSet(this); + SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea()); + if( nFrameHeight > 0 ) + { + 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()) + { + 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( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<const SwSectionFormat*>(pFormat->GetRegisteredIn()); + else + return nullptr; + } + return pFormat; +} + +static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame, + SwFrame* pFrame, bool &rbChkFootnote ) +{ + if( pFrame ) + { + 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); + 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 ) + { + 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 ) + { + nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) ); + if( nDiff < 0 ) + nDeadLine = aRectFnSet.GetTop(getFrameArea()); + const Size aOldSz( getFramePrintArea().SSize() ); + 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() ) + { + 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 ); + } + + long nHeight = aRectFnSet.GetHeight(getFrameArea()); + 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 ) + { + // 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 ); + } + } + SwLayoutFrame* pLayoutFrame = dynamic_cast<SwLayoutFrame*>(&rFrame); + if ( pLayoutFrame != nullptr ) + { + SwFrame* pLowerFrame = pLayoutFrame->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 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 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()); + { + long nBottom = aRectFnSet.GetBottom(getFrameArea()); + nBottom = aRectFnSet.YInc( nBottom, -nDiff ); + 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 ) + { + long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea()); + 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(); + 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); + 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 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); + 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 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 ) + { + OSL_ENSURE( pCol->IsColumnFrame(), "GetIndPrev(): ColumnFrame expected" ); + OSL_ENSURE( pCol->GetLower() && pCol->GetLower()->IsBodyFrame(), + "GetIndPrev(): Where's the body?"); + 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 ) + { + OSL_ENSURE( pCol->IsColumnFrame(), "GetIndNext(): ColumnFrame expected" ); + OSL_ENSURE( pCol->GetLower() && pCol->GetLower()->IsBodyFrame(), + "GetIndNext(): Where's the body?"); + 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( dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) != nullptr ) + pMyFormat = static_cast<const SwSectionFormat*>(pMyFormat->GetRegisteredIn()); + 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( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<SwSectionFormat*>(pFormat->GetRegisteredIn()); + 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( dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) != nullptr ) + pFormat = static_cast<SwSectionFormat*>(pFormat->GetRegisteredIn()); + else + break; + m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd(); + } +} + +void SwSectionFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + + if( pNew && RES_ATTRSET_CHG == pNew->Which() ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwLayoutFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags != 0 ) + { + if ( nInvFlags & 0x01 ) + InvalidateSize(); + if ( nInvFlags & 0x10 ) + SetCompletePaint(); + } +} + +void SwSectionFrame::SwClientNotify( const SwModify& rMod, const SfxHint& rHint ) +{ + SwFrame::SwClientNotify(rMod, rHint); + // #i117863# + if(&rMod != GetDep()) + return; + const auto pHint = dynamic_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint); + if(!pHint) + return; + SwSectionFrame::MoveContentAndDelete( this, pHint->IsSaveContent() ); +} + +void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &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 |= 0x10; + } + rInvFlags |= 0x01; + 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 |= 0x11; + } + } + 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 |= 0x01; + } + } + 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 |= 0x01; + } + } + break; + case RES_COLUMNBALANCE: + rInvFlags |= 0x01; + break; + + case RES_FRAMEDIR : + SetDerivedR2L( false ); + CheckDirChange(); + break; + + case RES_PROTECT: + { + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + if( pSh && pSh->GetLayout()->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleEditableState( true, this ); + } + break; + + default: + bClear = false; + } + if ( bClear ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwLayoutFrame::Modify( 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 ), "ConatainsFootnoteCont: 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 ) + { + 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" ); + if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() ) + { + 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..2e3df1910 --- /dev/null +++ b/sw/source/core/layout/sortedobjs.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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..1a92144a5 --- /dev/null +++ b/sw/source/core/layout/ssfrm.cxx @@ -0,0 +1,739 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <fmtclds.hxx> +#include <viewimp.hxx> +#include <sortedobjs.hxx> +#include <hints.hxx> +#include <frmtool.hxx> +#include <ndtxt.hxx> + + // No inline cause we need the function pointers +long SwFrame::GetTopMargin() const + { return getFramePrintArea().Top(); } +long SwFrame::GetBottomMargin() const + { return getFrameArea().Height() -getFramePrintArea().Height() -getFramePrintArea().Top(); } +long SwFrame::GetLeftMargin() const + { return getFramePrintArea().Left(); } +long SwFrame::GetRightMargin() const + { return getFrameArea().Width() - getFramePrintArea().Width() - getFramePrintArea().Left(); } +long SwFrame::GetPrtLeft() const + { return getFrameArea().Left() + getFramePrintArea().Left(); } +long SwFrame::GetPrtBottom() const + { return getFrameArea().Top() + getFramePrintArea().Height() + getFramePrintArea().Top(); } +long SwFrame::GetPrtRight() const + { return getFrameArea().Left() + getFramePrintArea().Width() + getFramePrintArea().Left(); } +long SwFrame::GetPrtTop() const + { return getFrameArea().Top() + getFramePrintArea().Top(); } + +bool SwFrame::SetMinLeft( 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( 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( 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( long nTop, long nBot ) +{ + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aPrt.Top( nTop ); + aPrt.Height( getFrameArea().Height() - nTop - nBot ); +} + +void SwFrame::SetLeftRightMargins( long nLeft, long nRight) +{ + SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this); + aPrt.Left( nLeft ); + aPrt.Width( getFrameArea().Width() - nLeft - nRight ); +} + +void SwFrame::SetRightLeftMargins( long nRight, 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() ) + { + 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() ) + { + const SwSortedObjs *pObjs = GetDrawObjs(); + const size_t nCnt = pObjs->size(); + for ( size_t i = 0; i < nCnt; ++i ) + { + SwAnchoredObject* pAnchoredObj = (*pObjs)[i]; + if( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + static_cast<SwFlyFrame*>(pAnchoredObj)->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 (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 ); + } + } + } + + if (m_pDrawObjs) + { + for (size_t i = m_pDrawObjs->size(); i; ) + { + SwAnchoredObject* pAnchoredObj = (*m_pDrawObjs)[--i]; + if ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFrame::DestroyFrame(static_cast<SwFlyFrame*>(pAnchoredObj)); + } + 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() ) + { + SwFormatChg aOldFormat( GetFormat() ); + pNew->Add( this ); + SwFormatChg aNewFormat( pNew ); + ModifyNotification( &aOldFormat, &aNewFormat ); + } +} + +SwContentFrame::SwContentFrame( SwContentNode * const pContent, SwFrame* pSib ) : + SwFrame( pContent, pSib ), + SwFlowFrame( static_cast<SwFrame&>(*this) ) +{ + assert(!getRootFrame()->IsHideRedlines() || 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(&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 = dynamic_cast<SwFlyFrame*>(pAnchoredObj)) + { + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFrame::DestroyFrame(static_cast<SwFlyFrame*>(pAnchoredObj)); + 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); + long nRight = (aRect.*fnRect->fnGetRight)(); + long nLeft = (aRect.*fnRect->fnGetLeft)(); + const SwFrame* pTmp = this; + bool bLeft = true; + bool bRight = true; + long nRowSpan = 0; + while( pTmp ) + { + if( pTmp->IsCellFrame() && pTmp->GetUpper() && + pTmp->GetUpper()->IsVertical() != pTmp->IsVertical() ) + nRowSpan = static_cast<const SwCellFrame*>(pTmp)->GetTabBox()->getRowSpan(); + long nTmpRight = (pTmp->getFrameArea().*fnRect->fnGetRight)(); + 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() ) + { + if( bLeft || aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 ) + 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; + long nLeft = (getFrameArea().*fnRect->fnGetLeft)(); + long nWidth = (getFrameArea().*fnRect->fnGetWidth)(); + long nPrtLeft = (getFramePrintArea().*fnRect->fnGetLeft)(); + 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); + 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() ) + { + 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..009099250 --- /dev/null +++ b/sw/source/core/layout/tabfrm.cxx @@ -0,0 +1,5851 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <IDocumentFieldsAccess.hxx> +#include <viewimp.hxx> +#include <fesh.hxx> +#include <swtable.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 <docary.hxx> +#include <sal/log.hxx> +#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) + , m_bSplitRowDisabled(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; + for ( size_t i = 0; i < rLines.size(); ++i ) + { + 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) + , m_bSplitRowDisabled(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, long nBottom ); +static void lcl_RecalcRow( SwRowFrame& rRow, long nBottom ); +static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, long lYStart, bool bInva ); +// #i26945# - add parameter <_bOnlyRowsAndCells> to control +// that only row and cell frames are formatted. +static bool lcl_InnerCalcLayout( SwFrame *pFrame, + 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, 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 (SwAnchoredObject* pAnchoredObj : *pLowerFrame->GetDrawObjs()) + { + // invalidate position of anchored object + pAnchoredObj->SetTmpConsiderWrapInfluence( false ); + pAnchoredObj->SetConsiderForTextWrap( false ); + pAnchoredObj->UnlockPosition(); + pAnchoredObj->InvalidateObjPos(); + + SwFlyFrame *pFly = dynamic_cast<SwFlyFrame*>(pAnchoredObj); + + // 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()->SetRectsDirty(); + 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; + 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, 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 long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan(); + if ( nLayoutRowSpan > 1 ) + { + // calculate height of cell: + const long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan ); + const 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 long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow ) +{ + 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: + 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 = !IsSplitRowDisabled(); + if ( bSplitRowAllowed && !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) + OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" ); + m_pTable->SetRowsToRepeat(0); + return false; + } + 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. + for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount ) + { + // Insert new headlines: + bDontCreateObjects = true; //frmtool + SwRowFrame* pHeadline = new SwRowFrame( + *GetTable()->GetTabLines()[ nRowCount ], this ); + pHeadline->SetRepeatedHeadline( true ); + bDontCreateObjects = false; + pHeadline->InsertBefore( pFoll, nullptr ); + + SwPageFrame *pPage = pHeadline->FindPageFrame(); + const SwFrameFormats *pTable = GetFormat()->GetDoc()->GetSpzFrameFormats(); + if( !pTable->empty() ) + { + sal_uLong 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; +} + +void SwTabFrame::Join() +{ + OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" ); + + SwTabFrame *pFoll = GetFollow(); + + if (pFoll && !pFoll->IsJoinLocked()) + { + 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, 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, 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); + SwCellFrame* pThisCell = dynamic_cast<SwCellFrame*>(pFrame); + if ( pThisCell && 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, + 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 SwModify* 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, + 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() ) ) + { + // #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 + SwCellFrame* pThisCell = dynamic_cast<SwCellFrame*>(pFrame); + if ( pThisCell && 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, 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() && + !GetFollow()->IsJoinLocked() + ) + { + 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(); + + auto pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + const SwBorderAttrs *pAttrs = pAccess->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 && 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 ) + { + pAccess.reset(); + m_bCalcLowers |= pLayout->Resize( + pLayout->GetBrowseWidthByTabFrame( *this ) ); + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + } + + 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. + long n1StLineHeight = 0; + if ( IsFollow() ) + { + SwFrame* pFrame = GetFirstNonHeadlineRow(); + if ( pFrame ) + n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea()); + } + + if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() ) + { + const long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea()); + const long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea()); + const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea()); + Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs ); + + SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout(); + if ( pLayout && + (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth || + aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) ) + { + pAccess.reset(); + m_bCalcLowers |= pLayout->Resize( + pLayout->GetBrowseWidthByTabFrame( *this ) ); + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + } + 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; + if ( MoveBwd( bReformat ) ) + { + aRectFnSet.Refresh(this); + bMovedBwd = true; + aNotify.SetLowersComplete( false ); + if ( bFootnotesInDoc ) + MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true ); + if ( bReformat || bKeep ) + { + long nOldTop = aRectFnSet.GetTop(getFrameArea()); + MakePos(); + if( nOldTop != aRectFnSet.GetTop(getFrameArea()) ) + { + SwHTMLTableLayout *pHTMLLayout = + GetTable()->GetHTMLTableLayout(); + if( pHTMLLayout ) + { + pAccess.reset(); + m_bCalcLowers |= pHTMLLayout->Resize( + pHTMLLayout->GetBrowseWidthByTabFrame( *this ) ); + + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + } + + setFramePrintAreaValid(false); + Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs ); + } + 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()); + 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 + && IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true) + && pLastRow->ShouldRowKeepWithNext()) + { + bFormat = true; + } + } + + if ( bFormat ) + { + pAccess.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 ); + + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + + // 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; + } + } + + const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) ); + 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; + pAccess.reset(); + + GetFollow()->MakeAll(pRenderContext); + + pAccess = std::make_unique<SwBorderAttrAccess>(SwFrame::GetCache(), this); + pAttrs = pAccess->Get(); + + 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; + pAccess.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, + long& rLeftOffset, + long& rRightOffset ) 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); + long nPrtPos = aRectFnSet.GetTop(getFrameArea()); + nPrtPos = aRectFnSet.YInc( nPrtPos, rUpper ); + SwRect aRect( getFrameArea() ); + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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 + pFly->IsFlyAtContentFrame() && + // fly overlaps with corresponding table rectangle + aFlyRect.IsOver( aRect ) && + // fly isn't lower of table and + // anchor character frame of fly isn't lower of table + ( !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 + 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; + bInvalidatePrtArea = true; + } + } + if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() || + css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&& + text::HoriOrientation::LEFT == rHori.GetHoriOrient() ) + { + const 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 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()) ); + } + + 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() ) + { + 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 + long nTmpRight = -1000000, + nLeftOffset = 0; + if( CalcFlyOffsets( nUpper, nLeftOffset, nTmpRight ) ) + { + setFramePrintAreaValid(false); + } + + long nRightOffset = std::max( 0L, 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( 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( 0L, 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( 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( 0L, 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, nLeftOffset ) : + nCenterSpacing ); + nRightSpacing = nRightLine + + ( (nRightOffset > 0) ? + std::max( nCenterSpacing, 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, ( 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, ( 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, ( 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, 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()) + 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() ) + { + 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() ) + { + SwRect aOldFrame( getFrameArea() ); + + //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 ) + { + long nTmp = GetUpper()->Grow( nDist - std::max<long>(nReal, 0), bTst, bInfo ); + + if ( IsRestrictTableGrowth() ) + { + nTmp = std::min( nDist, nReal + nTmp ); + nDist = nTmp < 0 ? 0 : nTmp; + } + } + + if ( !bTst ) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aRectFnSet.AddBottom( aFrm, nDist ); + } + + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame ); + } + } + } + + 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 ? aBack->GetGraphicPos() : GPOS_NONE; + if ( GPOS_NONE != ePos && GPOS_TILED != ePos ) + SetCompletePaint(); + } + + return nDist; +} + +void SwTabFrame::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + bool bAttrSetChg = pNew && RES_ATTRSET_CHG == pNew->Which(); + + if( bAttrSetChg ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwLayoutFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags != 0 ) + { + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + if ( nInvFlags & 0x02 ) + InvalidatePrt_(); + if ( nInvFlags & 0x40 ) + InvalidatePos_(); + SwFrame *pTmp; + if ( nullptr != (pTmp = GetIndNext()) ) + { + if ( nInvFlags & 0x04 ) + { + pTmp->InvalidatePrt_(); + if ( pTmp->IsContentFrame() ) + pTmp->InvalidatePage( pPage ); + } + if ( nInvFlags & 0x10 ) + pTmp->SetCompletePaint(); + } + if ( nInvFlags & 0x08 && nullptr != (pTmp = GetPrev()) ) + { + pTmp->InvalidatePrt_(); + if ( pTmp->IsContentFrame() ) + pTmp->InvalidatePage( pPage ); + } + if ( nInvFlags & 0x20 ) + { + if ( pPage && pPage->GetUpper() && !IsFollow() ) + static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth(); + } + if ( nInvFlags & 0x80 ) + InvalidateNextPos(); + } +} + +void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &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(); + for ( sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx ) + { + bDontCreateObjects = true; //frmtool + SwRowFrame* pHeadline = new SwRowFrame( *GetTable()->GetTabLines()[ nIdx ], this ); + pHeadline->SetRepeatedHeadline( true ); + bDontCreateObjects = false; + pHeadline->Paste( this, pLowerRow ); + } + } + rInvFlags |= 0x02; + break; + + case RES_FRM_SIZE: + case RES_HORI_ORIENT: + rInvFlags |= 0x22; + break; + + case RES_PAGEDESC: //Attribute changes (on/off) + if ( IsInDocBody() ) + { + rInvFlags |= 0x40; + 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 |= 0xC0; + break; + + case RES_LAYOUT_SPLIT: + if ( !IsFollow() ) + rInvFlags |= 0x40; + break; + case RES_FRAMEDIR : + SetDerivedR2L( false ); + CheckDirChange(); + break; + case RES_COLLAPSING_BORDERS : + rInvFlags |= 0x02; + lcl_InvalidateAllLowersPrt( this ); + break; + case RES_UL_SPACE: + rInvFlags |= 0x1C; + [[fallthrough]]; + + default: + bClear = false; + } + if ( bClear ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwLayoutFrame::Modify( 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() ) + { + + long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()); + SwRectFnSet fnRectX(pNewUpper); + 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 ); + } + } + 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 + if ( nullptr != (pFrame = GetPrev()) ) + { 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() ) + { + 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() +{ + SwModify* 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + bool bAttrSetChg = pNew && RES_ATTRSET_CHG == pNew->Which(); + const SfxPoolItem *pItem = nullptr; + + if( bAttrSetChg ) + { + const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pNew)->GetChgSet(); + pChgSet->GetItemState( RES_FRM_SIZE, false, &pItem); + if ( !pItem ) + pChgSet->GetItemState( RES_ROW_SPLIT, false, &pItem); + } + else if (pNew && (RES_FRM_SIZE == pNew->Which() || RES_ROW_SPLIT == pNew->Which())) + pItem = pNew; + + if ( pItem ) + { + SwTabFrame *pTab = FindTabFrame(); + if ( pTab ) + { + const bool bInFirstNonHeadlineRow = pTab->IsFollow() && + this == pTab->GetFirstNonHeadlineRow(); + // #i35063# + // Invalidation required is pRow is last row + if ( bInFirstNonHeadlineRow || !GetNext() ) + { + if ( bInFirstNonHeadlineRow ) + pTab = pTab->FindMaster(); + pTab->InvalidatePos(); + } + } + } + + SwLayoutFrame::Modify( pOld, pNew ); +} + +void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext) +{ + if ( !GetNext() ) + { + setFrameAreaSizeValid(false); + } + + SwLayoutFrame::MakeAll(pRenderContext); +} + +long CalcHeightWithFlys( const SwFrame *pFrame ) +{ + SwRectFnSet aRectFnSet(pFrame); + 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; + if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough) + { + // 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, 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 ) + { + long nFlyAdd = 0; + while ( pLow ) + { + if ( pLow->IsRowFrame() ) + { + // #i26945# + nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow), + _bConsiderObjs ); + } + else + { + long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea()); + nHeight += nLowHeight; + // #i26945# + if ( _bConsiderObjs ) + { + nFlyAdd = std::max( 0L, 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 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() ) + { + //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 ) + { + SwRootFrame *pRootFrame = getRootFrame(); + SwRectFnSet aRectFnSet(this); + SwRect aOldFrame; + + 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 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. + 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 long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea()); + if ( nDiff ) + { + aOldFrame = pToAdjust->getFrameArea(); + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust); + aRectFnSet.AddBottom( aFrm, nDiff ); + pNotify = pToAdjust; + } + + if ( pNotify ) + { + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() ) + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame ); + + 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), + 0L); + + // 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() ) + { + sal_uLong 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() +{ + SwModify* pMod = GetFormat(); + if( pMod ) + { + // At this stage the lower frames aren't destroyed already, + // therefore we have to do a recursive dispose. + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true ); + } + + pMod->Remove( this ); + if( !pMod->HasWriterListeners() ) + delete pMod; + } + + SwLayoutFrame::DestroyImpl(); +} + +SwCellFrame::~SwCellFrame() +{ +} + +static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, long lYStart, bool bInva ) +{ + bool bRet = false; + SwFrame *pFrame = pLay->Lower(); + SwRectFnSet aRectFnSet(pLay); + while ( pFrame ) + { + long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea()); + if( nFrameTop != lYStart ) + { + bRet = true; + const long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop ); + const 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + + // 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()->SetRectsDirty(); + // --> 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# + 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 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 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 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.IsOver( 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 = dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) ) + { + 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; + } + } + } + } + } + + long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()); + if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) || + aRectFnSet.GetTop(Lower()->getFrameArea()) != aRectFnSet.GetPrtTop(*this) ) + { + long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining; + if ( nDiff >= 0 ) + { + 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; + } + } + 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 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + bool bAttrSetChg = pNew && RES_ATTRSET_CHG == pNew->Which(); + const SfxPoolItem *pItem = nullptr; + + if( bAttrSetChg ) + static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_VERT_ORIENT, false, &pItem); + else if (pNew && RES_VERT_ORIENT == pNew->Which()) + pItem = pNew; + + if ( pItem ) + { + bool bInva = true; + if ( text::VertOrientation::NONE == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() && + // OD 04.11.2003 #112910# + Lower() && Lower()->IsContentFrame() ) + { + SwRectFnSet aRectFnSet(this); + const long lYStart = aRectFnSet.GetPrtTop(*this); + bInva = lcl_ArrangeLowers( this, lYStart, false ); + } + if ( bInva ) + { + SetCompletePaint(); + InvalidatePrt(); + } + } + + if ( ( bAttrSetChg && + SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_PROTECT, false ) ) || + ( pNew && RES_PROTECT == pNew->Which()) ) + { + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + if( pSh && pSh->GetLayout()->IsAnyShellAccessible() ) + pSh->Imp()->InvalidateAccessibleEditableState( true, this ); + } + + if ( bAttrSetChg && + SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_FRAMEDIR, false, &pItem ) ) + { + SetDerivedVert( false ); + CheckDirChange(); + } + + // #i29550# + if ( bAttrSetChg && + SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( RES_BOX, false, &pItem ) ) + { + 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::Modify( pOld, pNew ); +} + +long SwCellFrame::GetLayoutRowSpan() const +{ + long nRet = GetTabBox()->getRowSpan(); + 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; +} + +void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const +{ + SwFrame::dumpAsXmlAttributes(pWriter); + if (SwCellFrame* pFollow = GetFollowCell()) + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId()); + + if (SwCellFrame* pPrevious = GetPreviousCell()) + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId()); +} + +// #i103961# +void SwCellFrame::Cut() +{ + // notification for accessibility + { + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() ) + { + SwViewShell* pVSh = pRootFrame->GetCurrShell(); + if ( pVSh && pVSh->Imp() ) + { + pVSh->Imp()->DisposeAccessibleFrame( this ); + } + } + } + + 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() ) + { + nTmpHeight = static_cast<const SwTabFrame*>(pTmp)->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), + 0L); + } + + 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..7ec33dcea --- /dev/null +++ b/sw/source/core/layout/trvlfrm.cxx @@ -0,0 +1,2638 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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.IsInside( 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 *pEntry; + const SwFlyFrame *pStack1; + const SwFlyFrame *pStack2; + + bool ChkOsz( const SwFlyFrame *pFly ) + { + bool bRet = true; + if ( pFly != pStack1 && pFly != pStack2 ) + { + pStack1 = pStack2; + pStack2 = pFly; + bRet = false; + } + return bRet; + } + + void Entry( const SwFlyFrame *pFly ) + { + if ( !pEntry ) + pEntry = pStack1 = pFly; + } + + void Exit( const SwFlyFrame *pFly ) + { + if ( pFly == pEntry ) + pEntry = pStack1 = 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.IsInside( 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().IsInside( 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( ) ); + SwRect rBackRect; + if (pBackFrame) + { + 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.IsOver(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.IsOver(GetPaintArea()) ) + { + bRet = SwLayoutFrame::FillSelection( rList, rRect ); + if( GetSortedObjs() ) + { + const SwSortedObjs &rObjs = *GetSortedObjs(); + for (SwAnchoredObject* pAnchoredObj : rObjs) + { + if( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ) + continue; + const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + if( pFly->FillSelection( rList, rRect ) ) + bRet = true; + } + } + } + return bRet; +} + +bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const +{ + const SwFrame *pPage = Lower(); + const 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().IsInside( 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().IsInside( 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().IsInside( rPoint ) && + getFrameArea().IsInside( 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().IsInside( 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 long nPrtLeft = bRTL ? + aRectFnSet.GetPrtRight(*pTable) : + aRectFnSet.GetPrtLeft(*pTable); + if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0)) + nX = nPrtLeft; + else + { + const 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 ) + { + 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 ); + } + + 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().IsInside( aInsideCell ) ) + { + bEnd = true; + //Get the right Content out of the cell. + if ( !pCnt->getFrameArea().IsInside( aInsideCnt ) ) + { + pCnt = pCell->ContainsContent(); + if ( fnNxtPrv == lcl_GetPrvCnt ) + while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) ) + pCnt = pCnt->GetNextContentFrame(); + } + } + else if ( pCnt->getFrameArea().IsInside( 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 + if( nullptr == (pCnt = (*fnPosPage)(pLayoutFrame)) ) + 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().IsInside( 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.IsInside( 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()); + pContent = pStart->IsInDocBody() + ? pStart->ContainsContent() + : pStart->FindPageFrame()->FindFirstBodyContent(); + } + if ( !pContent ) // Somewhere down the road we have to start with one! + { + pContent = pStart->FindPageFrame()->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! + pContent = pStart->FindPageFrame()->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.IsInside( 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.IsInside( 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 SwModify *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().IsOver( 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.IsOver( 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.GetPoint() == pStartPos ? rCursor.GetMark() : rCursor.GetPoint(); + + 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 + long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion); + nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2); + 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 + long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion); + nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2); + 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 ); + long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion); + nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2); + 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 ); + long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion); + nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2); + 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() ) + { + long nTmpY = aTmpEnd.Y(); + aTmpEnd.setY( aTmpSt.Y() ); + aTmpSt.setY( nTmpY ); + } + if( aTmpSt.X() > aTmpEnd.X() ) + { + 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(); + 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.IsOver( 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) + { + if ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ) + continue; + const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + 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 ) + { + OSL_ENSURE( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) != nullptr, + "<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..fea1c0551 --- /dev/null +++ b/sw/source/core/layout/unusedf.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 <rootfrm.hxx> +#include <cntfrm.hxx> +#include <flyfrm.hxx> + +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 ) +{ + 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..ace9cb283 --- /dev/null +++ b/sw/source/core/layout/virtoutp.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 "virtoutp.hxx" +#include <viewopt.hxx> +#include <rootfrm.hxx> + +/* 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() <= aSize.Width() ) + return true; + if( !pVirDev ) + { + pVirDev = VclPtr<VirtualDevice>::Create(); + pVirDev->SetLineColor(); + if( pOut ) + { + if( pVirDev->GetFillColor() != pOut->GetFillColor() ) + pVirDev->SetFillColor( pOut->GetFillColor() ); + } + } + + if( rNew.Width() > aSize.Width() ) + { + aSize.setWidth( rNew.Width() ); + if( !pVirDev->SetOutputSizePixel( aSize ) ) + { + pVirDev.disposeAndClear(); + 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() ) + { + ++nCount; + return; + } +#endif + + bOn = bOn && !nCount && rRect.HasArea() && pShell->GetWin(); + ++nCount; + if( !bOn ) + return; + + pSh = pShell; + pOut = nullptr; + OutputDevice *pO = pSh->GetOut(); +// We don't cheat on printers or virtual output devices... + if( OUTDEV_WINDOW != pO->GetOutDevType() ) + return; + + pOut = pO; + Size aPixSz( 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( !pSh->GetWin()->IsReallyVisible() || + aTmpRect.GetWidth() <= pSh->GetWin()->GetOutputSizePixel().Width() + 2, + "Paintwidth bigger than visarea?" ); + // Does the rectangle fit in our buffer? + if( !DoesFit( aTmpRect.GetSize() ) ) + { + pOut = nullptr; + return; + } + + aRect = SwRect( pO->PixelToLogic( aTmpRect ) ); + + SetOutDev( pSh, pVirDev ); + + if( pVirDev->GetFillColor() != pOut->GetFillColor() ) + pVirDev->SetFillColor( pOut->GetFillColor() ); + + MapMode aMapMode( pOut->GetMapMode() ); + // use method to set mapping + //aMapMode.SetOrigin( Point(0,0) - aRect.Pos() ); + ::SetMappingForVirtDev( aRect.Pos(), pOut, pVirDev ); + + if( aMapMode != pVirDev->GetMapMode() ) + pVirDev->SetMapMode( aMapMode ); + + // set value of parameter <rRect> + rRect = aRect; + +} + +void SwLayVout::Flush_() +{ + OSL_ENSURE( pVirDev, "SwLayVout::DrawOut: nothing left Toulouse" ); + pOut->DrawOutDev( aRect.Pos(), aRect.SSize(), + aRect.Pos(), aRect.SSize(), *pVirDev ); + SetOutDev( pSh, pOut ); + 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..c120f8cc6 --- /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* pSh; + VclPtr<OutputDevice> pOut; + VclPtr<VirtualDevice> pVirDev; + SwRect aRect; + SwRect aOrgRect; + Size aSize; + sal_uInt16 nCount; + + bool DoesFit( const Size &rOut ); + +public: + SwLayVout() : pSh(nullptr), pOut(nullptr), pVirDev(nullptr), aSize(0, VIRTUALHEIGHT), nCount(0) {} + ~SwLayVout() { pVirDev.disposeAndClear(); } + + /// OD 27.09.2002 #103636# - change 2nd parameter <rRect> - no longer <const> + void Enter( SwViewShell *pShell, SwRect &rRect, bool bOn ); + void Leave() { --nCount; Flush(); } + + void SetOrgRect( SwRect const &rRect ) { aOrgRect = rRect; } + const SwRect& GetOrgRect() const { return aOrgRect; } + + bool IsFlushable() const { return bool(pOut); } + void Flush_(); + void Flush() { if( 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..57db1547a --- /dev/null +++ b/sw/source/core/layout/wsfrm.cxx @@ -0,0 +1,4602 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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 <ndtxt.hxx> +#include <swtable.hxx> + +// RotateFlyFrame3 +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +using namespace ::com::sun::star; + +SwFrameAreaDefinition::SwFrameAreaDefinition() +: maFrameArea(), + maFramePrintArea(), + mbFrameAreaPositionValid(false), + mbFrameAreaSizeValid(false), + mbFramePrintAreaValid(false), + mnFrameId(SwFrameAreaDefinition::mnLastFrameId++) +{ +} + +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()) + { + 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( SwModify *pMod, SwFrame* pSib ) +: SwFrameAreaDefinition(), + SwClient( pMod ), + SfxBroadcaster(), + 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), + mbForbidDelete(false) +{ + 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 SfxPoolItem* pItem; + // 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 && SfxItemState::SET == pFormat->GetItemState( RES_FRAMEDIR, true, &pItem ) ) + { + const SvxFrameDirectionItem* pFrameDirItem = static_cast<const SvxFrameDirectionItem*>(pItem); + 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + + if( pOld && pNew && RES_ATTRSET_CHG == pNew->Which() ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + do + { + UpdateAttrFrame(pOItem, pNItem, nInvFlags); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + } + else + UpdateAttrFrame( pOld, pNew, nInvFlags ); + + if ( nInvFlags != 0 ) + { + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + if ( nInvFlags & 0x01 ) + { + InvalidatePrt_(); + if( !GetPrev() && IsTabFrame() && IsInSct() ) + FindSctFrame()->InvalidatePrt_(); + } + if ( nInvFlags & 0x02 ) + InvalidateSize_(); + if ( nInvFlags & 0x04 ) + InvalidatePos_(); + if ( nInvFlags & 0x08 ) + SetCompletePaint(); + SwFrame *pNxt; + if ( nInvFlags & 0x30 && nullptr != (pNxt = GetNext()) ) + { + pNxt->InvalidatePage( pPage ); + if ( nInvFlags & 0x10 ) + pNxt->InvalidatePos_(); + if ( nInvFlags & 0x20 ) + pNxt->SetCompletePaint(); + } + } +} + +void SwFrame::UpdateAttrFrame( const SfxPoolItem *pOld, const SfxPoolItem *pNew, + sal_uInt8 &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: + rInvFlags |= 0x0B; + break; + + case RES_HEADER_FOOTER_EAT_SPACING: + rInvFlags |= 0x03; + break; + + case RES_BACKGROUND: + rInvFlags |= 0x28; + break; + + case RES_KEEP: + rInvFlags |= 0x04; + break; + + case RES_FRM_SIZE: + ReinitializeFrameSizeAttrFlags(); + rInvFlags |= 0x13; + break; + + case RES_FMT_CHG: + rInvFlags |= 0x0F; + 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 |= 0x28; + } + /* 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() ) + { + 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()) + { + 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)() ); + } + + long nNew = (aNew.*fnRect->fnGetHeight)(); + 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. + if( nullptr != (mpPrev = pBehind->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 + if ( nullptr != (mpNext = pBefore->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. + if( nullptr != (mpPrev = pBehind->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 ) + { + while ( pNxt && pNxt->IsInTab() ) + { + if( nullptr != (pNxt = pNxt->FindTabFrame()) ) + 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() ) + { + if( nullptr != (pNxt = pNxt->FindTabFrame()) ) + pNxt = pNxt->FindNextCnt(); + } + if ( pNxt ) + { + pNxt->InvalidateLineNum_(); + if ( pNxt != GetNext() ) + pNxt->InvalidatePage(); + } + } + + if( nullptr != (pFrame = GetIndNext()) ) + { + // 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 + if ( nullptr != (pFrame = GetPrev()) ) + { 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(); + 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(); + SwTabFrame* pMasterTab = pThisTab && pThisTab->IsFollow() ? pThisTab->FindMaster() : nullptr; + if ( pMasterTab ) + { + pMasterTab->InvalidatePos_(); + pMasterTab->SetRemoveFollowFlowLinePending( true ); + } + } + } + } + //Remove first, then shrink the upper. + SwLayoutFrame *pUp = GetUpper(); + RemoveFromLayout(); + if ( pUp ) + { + 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); + 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)() ) + { + // 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 + { + const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(this); + if ( pThisCell ) + { + 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 + { + const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(this); + if ( pThisCell ) + { + 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. + long nBrowseAdd = 0; + if ( bBrowse && GetUpper()->IsPageFrame() ) // only (Page-)BodyFrames + { + SwViewShell *pViewShell = getRootFrame()->GetCurrShell(); + SwLayoutFrame *pUp = GetUpper(); + long nChg; + const 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, 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 long nTmp = nChg - pBody->getFramePrintArea().Height(); + if ( !bTst ) + { + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pBody); + aFrm.Height(std::max( 0L, aFrm.Height() - nChg )); + } + + pBody->InvalidatePrt_(); + pBody->InvalidateSize_(); + if ( pBody->GetNext() ) + pBody->GetNext()->InvalidatePos_(); + if ( !IsHeaderFrame() ) + pBody->SetCompletePaint(); + } + nChg = nTmp <= 0 ? 0 : nTmp; + } + } + + const 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; + 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. + 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 ? aBack->GetGraphicPos() : GPOS_NONE; + 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 long nOldFrameHeight = getFrameArea().Height(); + const 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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 ) ) + { + 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 || dynamic_cast< const SwFlyFrame *>( this ) != nullptr ) + { + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchObj ) != nullptr ) + static_cast<SwFlyFrame*>(pAnchObj)->ValidateThisAndAllLowers( 2 ); + else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchObj ) != nullptr ) + static_cast<SwAnchoredDrawObject*>(pAnchObj)->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. + 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<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.IsOver( 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem * pNew ) +{ + sal_uInt8 nInvFlags = 0; + + if( pNew && RES_ATTRSET_CHG == pNew->Which() && pOld ) + { + SfxItemIter aNIter( *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet() ); + SfxItemIter aOIter( *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + const SfxPoolItem* pNItem = aNIter.GetCurItem(); + const SfxPoolItem* pOItem = aOIter.GetCurItem(); + SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) ); + SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) ); + do + { + UpdateAttr_(pOItem, pNItem, nInvFlags, &aOldSet, &aNewSet); + pNItem = aNIter.NextItem(); + pOItem = aOIter.NextItem(); + } while (pNItem); + if ( aOldSet.Count() || aNewSet.Count() ) + SwFrame::Modify( &aOldSet, &aNewSet ); + } + else + UpdateAttr_( pOld, pNew, nInvFlags ); + + if ( nInvFlags == 0 ) + return; + + SwPageFrame *pPage = FindPageFrame(); + InvalidatePage( pPage ); + if ( nInvFlags & 0x01 ) + SetCompletePaint(); + if ( nInvFlags & 0x02 ) + InvalidatePos_(); + if ( nInvFlags & 0x04 ) + InvalidateSize_(); + if ( nInvFlags & 0x88 ) + { + if( IsInSct() && !GetPrev() ) + { + SwSectionFrame *pSect = FindSctFrame(); + if( pSect->ContainsAny() == this ) + { + pSect->InvalidatePrt_(); + pSect->InvalidatePage( pPage ); + } + } + InvalidatePrt_(); + } + SwFrame* pNextFrame = GetIndNext(); + if ( pNextFrame && nInvFlags & 0x10) + { + pNextFrame->InvalidatePrt_(); + pNextFrame->InvalidatePage( pPage ); + } + if ( pNextFrame && nInvFlags & 0x80 ) + { + pNextFrame->SetCompletePaint(); + } + if ( nInvFlags & 0x20 ) + { + SwFrame* pPrevFrame = GetPrev(); + if ( pPrevFrame ) + { + pPrevFrame->InvalidatePrt_(); + pPrevFrame->InvalidatePage( pPage ); + } + } + if ( nInvFlags & 0x40 ) + InvalidateNextPos(); + +} + +void SwContentFrame::UpdateAttr_( const SfxPoolItem* pOld, const SfxPoolItem* pNew, + sal_uInt8 &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 = 0xFF; + [[fallthrough]]; + + case RES_PAGEDESC: //attribute changes (on/off) + if ( IsInDocBody() && !IsInTab() ) + { + rInvFlags |= 0x02; + 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 |= 0x80; + [[fallthrough]]; + } + case RES_LR_SPACE: + case RES_BOX: + case RES_SHADOW: + Prepare( PrepareHint::FixSizeChanged ); + SwFrame::Modify( pOld, pNew ); + rInvFlags |= 0x30; + break; + + case RES_BREAK: + { + rInvFlags |= 0x42; + const IDocumentSettingAccess& rIDSA = GetUpper()->GetFormat()->getIDocumentSettingAccess(); + if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) || + rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) ) + { + rInvFlags |= 0x1; + 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 |= 0x01; + 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 |= 0x01; + break; + + case RES_FRM_SIZE: + rInvFlags |= 0x01; + [[fallthrough]]; + + default: + bClear = false; + } + if ( bClear ) + { + if ( pOldSet || pNewSet ) + { + if ( pOldSet ) + pOldSet->ClearItem( nWhich ); + if ( pNewSet ) + pNewSet->ClearItem( nWhich ); + } + else + SwFrame::Modify( 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: + const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(this); + if ( pThisCell && 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()->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 ? aBack->GetGraphicPos() : GPOS_NONE; + if ( GPOS_NONE != ePos && GPOS_TILED != ePos ) + SetCompletePaint(); + } + } + + if( bMoveAccFrame && IsAccessibleFrame() ) + { + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame ); + } + } + 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 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(); + const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(this); + // NEW TABLES + if ( pThisCell && 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( bMoveAccFrame && IsAccessibleFrame() ) + { + SwRootFrame *pRootFrame = getRootFrame(); + if( pRootFrame && pRootFrame->IsAnyShellAccessible() && + pRootFrame->GetCurrShell() ) + { + pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame ); + } + } + 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 ? aBack->GetGraphicPos() : GPOS_NONE; + 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 + pCnt->InvalidatePos(); + } + } + 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() ) + { + // 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 = static_cast<sal_uInt16>(pAttrs->CalcLeft(this)); + const sal_uInt16 nUpper = bHideWhitespace ? 0 : pAttrs->CalcTop(); + + const sal_uInt16 nRight = static_cast<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() ) + { + 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 long nOldLeft = (getFrameArea().*fnRect->fnGetLeft)(); + const 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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 ) + 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 ) ) ; +} + +long SwLayoutFrame::CalcRel( const SwFormatFrameSize &rSz ) const +{ + long nRet = rSz.GetWidth(), + nPercent = rSz.GetWidthPercent(); + + if ( nPercent ) + { + const SwFrame *pRel = GetUpper(); + 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(); + 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 long lcl_CalcMinColDiff( SwLayoutFrame *pLayFrame ) +{ + 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 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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. + + long nMinimum = nMinHeight; + 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 ) ) + { + 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 ) ) ) + { + 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<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. + 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? + { + 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() ); + 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 = dynamic_cast<SwFlyFrame*>(this); + 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 + ::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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame* pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + ::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<sal_uLong> *const pSkipped, + SwFrameFormats & rTable, + SwPageFrame *const pPage, + SwTextNode const*const pNode, + std::vector<sw::Extent>::const_iterator & 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 + if (auto const pFlys = pNode->GetAnchoredFlys()) + { + for (auto const pFly : *pFlys) + { + 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<sal_uLong> *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()) + { + 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); + sal_uLong const until = iter == pMerged->extents.end() + ? pMerged->pLastNode->GetIndex() + 1 + : iter->pNode->GetIndex(); + for (sal_uLong 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<sal_uLong> *const pSkipped) +{ + assert(rEndOfSectionNode.IsEndNode()); + assert(rNodes[rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1]->IsCreateFrameWhenHidingRedlines()); // first node is never hidden + for (sal_uLong 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.IsHideRedlines()) + { + assert(!pFrame->GetMergedPara() || + !rNode.IsCreateFrameWhenHidingRedlines()); + 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::Modify() 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 (sal_uLong 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 + if (auto const pFlys = pNode->GetAnchoredFlys()) + { + for (SwFrameFormat * pFormat : *pFlys) + { + 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()) + { + 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 (sal_uLong j = rNode.GetIndex(); j <= rNode.EndOfSectionIndex(); ++j) + { + rNode.GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden); + } + rNode.GetTableNode()->DelFrames(&rLayout); + } + } + if (!rNode.IsCreateFrameWhenHidingRedlines()) + { + if (rLayout.IsHideRedlines()) + { + 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)); + sal_uLong 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); + assert(!bDontCreateObjects); + bDontCreateObjects = true; // suppress here, to be called once + ::MakeFrames(rLayout.GetFormat()->GetDoc(), start, end); + bDontCreateObjects = false; + i = j - 1; // will be incremented again + } + } + } +} + +static void UnHideRedlinesExtras(SwRootFrame & rLayout, + SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode, + std::set<sal_uLong> *const pSkipped) +{ + assert(rEndOfExtraSectionNode.IsEndNode()); + for (sal_uLong 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 (sal_uLong 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); + } + } +} + +void SwRootFrame::SetHideRedlines(bool const bHideRedlines) +{ + if (bHideRedlines == mbHideRedlines) + { + return; + } + mbHideRedlines = bHideRedlines; + assert(GetCurrShell()->ActionPend()); // tdf#125754 avoid recursive layout + SwDoc & rDoc(*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<sal_uLong> skippedFlys; + UnHideRedlinesExtras(*this, rNodes, rNodes.GetEndOfAutotext(), + // when un-hiding, delay all fly frame creation to AppendAllObjs below + IsHideRedlines() ? &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(*this, rNodes, rNodes.GetEndOfInserts(), nullptr); + UnHideRedlines(*this, rNodes, rNodes.GetEndOfContent(), nullptr); + + if (!IsHideRedlines()) + { // 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(), this); + } + + 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 + if (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, bHideRedlines + ? SwFormatFieldHintWhich::REMOVED + : SwFormatFieldHintWhich::INSERTED) ); + + +// InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer +} + +/* 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..5ac2514bb --- /dev/null +++ b/sw/source/core/objectpositioning/anchoredobjectposition.cxx @@ -0,0 +1,1132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <fmtfsize.hxx> +#include <fmtfollowtextflow.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <ndtxt.hxx> +#include <IDocumentSettingAccess.hxx> +#include <textboxhelper.hxx> +#include <fmtsrnd.hxx> + +using namespace ::com::sun::star; +using namespace objectpositioning; + +SwAnchoredObjectPosition::SwAnchoredObjectPosition( SdrObject& _rDrawObj ) + : mrDrawObj( _rDrawObj ), + mbIsObjFly( false ), + mpAnchoredObj( nullptr ), + mpAnchorFrame( nullptr ), + mpContact( nullptr ), + // #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: + { + 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; + // #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; + + 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) + 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; + } + + // tdf#91260 - allow textboxes extending beyond the page bottom + // tdf#101627 - the patch a4dee94afed9ade6ac50237c8d99a6e49d3bebc1 + // for tdf#91260 causes problems if the textbox + // is anchored in the footer, so exclude this case + if ( !( GetAnchorFrame().GetUpper() && GetAnchorFrame().GetUpper()->IsFooterFrame() ) + && nAdjustedRelPosY < nProposedRelPosY ) + { + const SwFrameFormat* pFormat = &(GetFrameFormat()); + if ( GetObject().IsTextBox() ) + { + // shrink textboxes to extend beyond the page bottom + SwFrameFormat* pFrameFormat = ::FindFrameFormat(&GetObject()); + SwFormatFrameSize aSize(pFormat->GetFrameSize()); + SwTwips nShrinked = aSize.GetHeight() - (nProposedRelPosY - nAdjustedRelPosY); + if (nShrinked >= 0) { + aSize.SetHeight( nShrinked ); + pFrameFormat->SetFormatAttr(aSize); + } + nAdjustedRelPosY = nProposedRelPosY; + } else if ( SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT) ) + // when the shape has a textbox, use only the proposed vertical position + nAdjustedRelPosY = nProposedRelPosY; + } + } + 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; + + 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 ) + { + // 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 ( dynamic_cast<const SwFlyFrame*>( &GetAnchoredObj() ) != nullptr && + ( 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 ( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame() ) == 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 ); + sal_uLong 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 sal_uLong _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 ) + { + sal_uLong 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..ffe9a3682 --- /dev/null +++ b/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <fmtornt.hxx> + + +using namespace ::com::sun::star; +using 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 ), + maAnchorPos ( Point() ), + mnRelPos ( 0 ), + maObjBoundRect ( SwRect() ), + mnLineAlignment ( sw::LineAlign::NONE ) +{} + +/** destructor */ +SwAsCharAnchoredObjectPosition::~SwAsCharAnchoredObjectPosition() +{} + +/** method to cast <SwAnchoredObjectPosition::GetAnchorFrame()> to needed type */ +const SwTextFrame& SwAsCharAnchoredObjectPosition::GetAnchorTextFrame() const +{ + OSL_ENSURE( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame() ) != nullptr, + "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 + OSL_ENSURE( dynamic_cast<const SwAnchoredDrawObject*>( &GetAnchoredObj() ) != nullptr, + "<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# + OSL_ENSURE( dynamic_cast<const SwFlyInContentFrame*>( &GetAnchoredObj()) != nullptr, + "<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..b8fb4f02a --- /dev/null +++ b/sw/source/core/objectpositioning/environmentofanchoredobject.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 <environmentofanchoredobject.hxx> +#include <frame.hxx> +#include <pagefrm.hxx> + +using namespace objectpositioning; + +SwEnvironmentOfAnchoredObject::SwEnvironmentOfAnchoredObject( + const bool _bFollowTextFlow ) + : mbFollowTextFlow( _bFollowTextFlow ) +{} + +SwEnvironmentOfAnchoredObject::~SwEnvironmentOfAnchoredObject() +{} + +/** 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" ); + } + } + + OSL_ENSURE( dynamic_cast< const SwLayoutFrame *>( pHoriEnvironmentLayFrame ) != nullptr, + "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" ); + } + } + + OSL_ENSURE( dynamic_cast< const SwLayoutFrame *>( pVertEnvironmentLayFrame ) != nullptr, + "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..95331da7f --- /dev/null +++ b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx @@ -0,0 +1,1202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <environmentofanchoredobject.hxx> +#include <frmatr.hxx> +#include <fmtwrapinfluenceonobjpos.hxx> +#include <sortedobjs.hxx> +#include <textboxhelper.hxx> + +using namespace objectpositioning; +using namespace ::com::sun::star; + +SwToContentAnchoredObjectPosition::SwToContentAnchoredObjectPosition( SdrObject& _rDrawObj ) + : SwAnchoredObjectPosition ( _rDrawObj ), + mpVertPosOrientFrame( nullptr ), + // #i26791# + maOffsetToFrameAnchorPos( Point() ), + 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 +{ + OSL_ENSURE( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame()) != nullptr , + "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 ); + + // 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: + { + 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 + { + 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 ) + { + 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().IsOver(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..19ad5ac61 --- /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 objectpositioning; +using namespace ::com::sun::star; + +SwToLayoutAnchoredObjectPosition::SwToLayoutAnchoredObjectPosition( SdrObject& _rDrawObj ) + : SwAnchoredObjectPosition( _rDrawObj ), + maRelPos( Point() ), + // #i26791# + maOffsetToFrameAnchorPos( Point() ) +{} + +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 long nAnchorBottom = GetAnchorFrame().getFrameArea().Bottom(); + const 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..1927872ed --- /dev/null +++ b/sw/source/core/ole/ndole.cxx @@ -0,0 +1,1246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + +using namespace utl; +using namespace com::sun::star::uno; +using namespace com::sun::star; + +namespace { + +class SwOLELRUCache + : private utl::ConfigItem +{ +private: + typedef std::deque<SwOLEObj *> OleObjects_t; + OleObjects_t 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 + +class SwEmbedObjectLink : public sfx2::SvBaseLink +{ + SwOLENode* 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 ), + pOleNode(pNode) +{ + SetSynchron( false ); +} + +::sfx2::SvBaseLink::UpdateResult SwEmbedObjectLink::DataChanged( + const OUString&, const uno::Any& ) +{ + if ( !pOleNode->UpdateLinkURL_Impl() ) + { + // the link URL was not changed + uno::Reference< embed::XEmbeddedObject > xObject = 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&) + { + } + } + } + + pOleNode->GetNewReplacement(); + pOleNode->SetChanged(); + + return SUCCESS; +} + +void SwEmbedObjectLink::Closed() +{ + pOleNode->BreakFileLink_Impl(); + SvBaseLink::Closed(); +} + +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(); +} + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLENode")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), + BAD_CAST(OString::number(GetIndex()).getStr())); + + GetOLEObj().dumpAsXml(pWriter); + + 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* pDoc, const SwNodeIndex& rIdx, bool) const +{ + // If there's already a SvPersist instance, we use it + SfxObjectShell* pPersistShell = pDoc->GetPersist(); + if( !pPersistShell ) + { + // TODO/LATER: is EmbeddedObjectContainer not enough? + // the created document will be closed by pDoc ( should use SfxObjectShellLock ) + pPersistShell = new SwDocShell( pDoc, SfxObjectCreateMode::INTERNAL ); + pDoc->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 = pDoc->GetNodes().MakeOLENode( rIdx, aNewName, GetAspect(), + pDoc->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 ); + pDoc->SetOLEPrtNotifyPending(); + + return pOLENd; +} + +bool SwOLENode::IsInGlobalDocSection() const +{ + // Find the "Body Anchor" + sal_uLong 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( 1 ); + aArgs[0].Name = "URL"; + aArgs[0].Value <<= 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 ) + { + uno::Reference< embed::XStorage > xStorage = pPers->GetStorage(); + if ( xStorage.is() ) + { + 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 ) + { + try + { + uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY_THROW ); + if ( xLinkSupport->isLink() ) + { + const OUString aLinkURL = xLinkSupport->getLinkURL(); + if ( !aLinkURL.isEmpty() ) + { + // this is a file link so the model link manager should handle it + mpObjectLink = new SwEmbedObjectLink( this ); + maLinkURL = aLinkURL; + GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink, sfx2::SvBaseLinkObjectType::ClientOle, aLinkURL ); + mpObjectLink->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()) + { + SET_CURR_SHELL(&rShell); + + if(rShell.VisArea().IsOver(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), + maPrimitive2DSequence(), + maRange(), + 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_aPrimitive2DSequence(), + m_aRange() +{ + m_xOLERef.Lock(); + if ( xObj.is() ) + { + m_xListener = new SwOLEListener_Impl( this ); + xObj->addStateChangeListener( m_xListener.get() ); + } +} + +SwOLEObj::SwOLEObj( const OUString &rString, sal_Int64 nAspect ) : + m_pOLENode( nullptr ), + m_aName( rString ), + m_aPrimitive2DSequence(), + m_aRange() +{ + 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.get() ); + 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() ) + { + SwDoc* pDoc = pNode->GetDoc(); + + // If there's already a SvPersist instance, we use it + SfxObjectShell* p = pDoc->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( pDoc, 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(); +} + +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() ); + const MapMode aSrc ( MapUnit::MapTwip ); + const MapMode aDest( MapUnit::Map100thMM ); + aSz = OutputDevice::LogicToLogic( aSz, aSrc, aDest ); + 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.Assign( xObj, m_xOLERef.GetViewAspect() ); + m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), m_aName ); + m_xListener = new SwOLEListener_Impl( this ); + xObj->addStateChangeListener( m_xListener.get() ); + } + + 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* pDoc = m_pOLENode->GetDoc(); + bRet = UnloadObject( m_xOLERef.GetObject(), pDoc, 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() +{ + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLEObj")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + xmlTextWriterStartElement(pWriter, BAD_CAST("m_xOLERef")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), + BAD_CAST(typeid(*m_xOLERef.GetObject()).name())); + xmlTextWriterEndElement(pWriter); + + 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; + OleObjects_t::iterator it = + std::find(m_OleObjects.begin(), m_OleObjects.end(), pObj); + if (it != m_OleObjects.end() && it != m_OleObjects.begin()) + { + // object in cache but is currently not the first in cache + m_OleObjects.erase(it); + it = m_OleObjects.end(); + } + if (it == m_OleObjects.end()) + { + 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 ) +{ + OleObjects_t::iterator 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..e12a71805 --- /dev/null +++ b/sw/source/core/para/paratr.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 <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> + +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 ); +} + +void SwFormatDrop::Modify( const SfxPoolItem*, const SfxPoolItem * ) +{ + if( m_pDefinedIn ) + { + if( dynamic_cast< const SwFormat *>( m_pDefinedIn ) == nullptr) + m_pDefinedIn->ModifyNotification( this, this ); + else if( m_pDefinedIn->HasWriterListeners() && + !m_pDefinedIn->IsModifyLocked() ) + { + // Notify those who are dependent on the format on our own. + // The format itself wouldn't pass on the notify as it does not get past the check. + m_pDefinedIn->ModifyBroadcast( this, 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 = static_cast<sal_Int16>(convertMm100ToTwip(static_cast<sal_Int32>(nVal))); + 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 = convertMm100ToTwip(pDrop->Distance); + } + } + 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleItem")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(GetValue().toUtf8().getStr())); + 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..748f764a3 --- /dev/null +++ b/sw/source/core/sw3io/swacorr.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 <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 ) +{ + if( nullptr == dynamic_cast<const SwDocShell*>( &rObjSh) ) + return false; + + SwDocShell& rDShell = static_cast<SwDocShell&>(rObjSh); + 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() ) + { + rDShell.GetEditShell()->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..ae8d81ca2 --- /dev/null +++ b/sw/source/core/swg/SwXMLBlockExport.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 <SwXMLBlockExport.hxx> +#include <SwXMLTextBlocks.hxx> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.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 ), + 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, + rBlockList.GetName()); + { + SvXMLElementExport aRoot (*this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK_LIST, true, true); + sal_uInt16 nBlocks= rBlockList.GetCount(); + for ( sal_uInt16 i = 0; i < nBlocks; i++) + { + AddAttribute( XML_NAMESPACE_BLOCKLIST, + XML_ABBREVIATED_NAME, + rBlockList.GetShortName(i)); + AddAttribute( XML_NAMESPACE_BLOCKLIST, + XML_PACKAGE_NAME, + rBlockList.GetPackageName(i)); + AddAttribute( XML_NAMESPACE_BLOCKLIST, + XML_NAME, + rBlockList.GetLongName(i)); + AddAttribute( XML_NAMESPACE_BLOCKLIST, + XML_UNFORMATTED_TEXT, + 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 ), + 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(const OUString &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, + 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 ( rText.getToken( 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..ab5519e27 --- /dev/null +++ b/sw/source/core/swg/SwXMLBlockImport.cxx @@ -0,0 +1,359 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 & rLocalRef; + +public: + SwXMLBlockListContext( SwXMLBlockListImport& rImport, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ); + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 ); + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} +}; + +class SwXMLTextBlockDocumentContext : public SvXMLImportContext +{ +private: + SwXMLTextBlockImport & rLocalRef; + +public: + SwXMLTextBlockDocumentContext( SwXMLTextBlockImport& rImport ); + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 & rLocalRef; + +public: + SwXMLTextBlockBodyContext( SwXMLTextBlockImport& rImport ); + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 & rLocalRef; + +public: + SwXMLTextBlockTextContext( SwXMLTextBlockImport& rImport ); + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 & rLocalRef; + +public: + SwXMLTextBlockParContext( SwXMLTextBlockImport & rImport ); + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 ), + 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( 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 ), + 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( rLocalRef ); + return nullptr; +} + +SwXMLTextBlockTextContext::SwXMLTextBlockTextContext( + SwXMLTextBlockImport& rImport) : + SvXMLImportContext ( rImport ), + 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( rLocalRef ); + return nullptr; +} + +SwXMLTextBlockBodyContext::SwXMLTextBlockBodyContext( + SwXMLTextBlockImport& rImport ) : + SvXMLImportContext( rImport ), + 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( rLocalRef ); + else if( Element == SwXMLTextBlockToken::TEXT_P ) + return new SwXMLTextBlockParContext( rLocalRef ); + return nullptr; +} + +SwXMLTextBlockParContext::SwXMLTextBlockParContext( + SwXMLTextBlockImport& rImport ) : + SvXMLImportContext( rImport ), + rLocalRef( rImport ) +{ +} + +void SAL_CALL SwXMLTextBlockParContext::characters( const OUString & aChars ) +{ + rLocalRef.m_rText += aChars; +} + +SwXMLTextBlockParContext::~SwXMLTextBlockParContext() +{ + if (rLocalRef.bTextOnly) + rLocalRef.m_rText += "\015"; + else + { + if (!rLocalRef.m_rText.endsWith( " " )) + rLocalRef.m_rText += " "; + } +} + +// SwXMLBlockListImport ////////////////////////////// +SwXMLBlockListImport::SwXMLBlockListImport( + const uno::Reference< uno::XComponentContext >& rContext, + SwXMLTextBlocks &rBlocks ) +: SvXMLImport( rContext, "", SvXMLImportFlags::NONE ), + rBlockList (rBlocks) +{ +} + +SwXMLBlockListImport::~SwXMLBlockListImport() + throw () +{ +} + +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 ), + bTextOnly ( bNewTextOnly ), + m_rText ( rNewText ) +{ +} + +SwXMLTextBlockImport::~SwXMLTextBlockImport() + throw() +{ +} + +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..aa6c63b8b --- /dev/null +++ b/sw/source/core/swg/SwXMLSectionList.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 <SwXMLSectionList.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.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 void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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 void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + 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() + throw() +{ +} + +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..943cb8c41 --- /dev/null +++ b/sw/source/core/swg/SwXMLTextBlocks.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 <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 <unotools/ucbstreamhelper.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 ) +{ + xBlkRoot = rStorage; + xRoot = nullptr; +} + +void SwXMLTextBlocks::ResetBlockMode ( ) +{ + xBlkRoot = nullptr; + xRoot = nullptr; +} + +SwXMLTextBlocks::SwXMLTextBlocks( const OUString& rFile ) + : SwImpBlocks(rFile) + , nFlags(SwXmlFlags::NONE) +{ + SwDocShell* pDocSh = new SwDocShell ( SfxObjectCreateMode::INTERNAL ); + if( !pDocSh->DoInitNew() ) + return; + m_bReadOnly = true; + m_xDoc = pDocSh->GetDoc(); + 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&) + { + OSL_FAIL("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 ) + , nFlags(SwXmlFlags::NONE) +{ + SwDocShell* pDocSh = new SwDocShell ( SfxObjectCreateMode::INTERNAL ); + if( !pDocSh->DoInitNew() ) + return; + m_bReadOnly = false; + m_xDoc = pDocSh->GetDoc(); + 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(xDocShellRef.is()) + xDocShellRef->DoClose(); + 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 ) +{ + aPackageName = GeneratePackageName( rShort ); + AddName(rShort, rLong, 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->bIsOnlyTextFlagInit = true; + pNew->bIsOnlyText = bOnlyText; + m_aNames.insert( std::move(pNew) ); + m_bInfoChanged = true; +} + +ErrCode SwXMLTextBlocks::Delete( sal_uInt16 n ) +{ + const OUString aPckName (m_aNames[n]->aPackageName); + if ( xBlkRoot.is() && + xBlkRoot->hasByName( aPckName ) && xBlkRoot->isStreamElement( aPckName ) ) + { + try + { + xBlkRoot->removeElement ( aPckName ); + uno::Reference < embed::XTransactedObject > xTrans( 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( xBlkRoot.is(), "No storage set" ); + if(!xBlkRoot.is()) + return ERRCODE_NONE; + OUString aOldName (m_aNames[nIdx]->aPackageName); + m_aShort = rNewShort; + aPackageName = GeneratePackageName( m_aShort ); + + if(aOldName != aPackageName) + { + if (IsOnlyTextBlock ( nIdx ) ) + { + OUString sExt(".xml"); + OUString aOldStreamName = aOldName + sExt; + OUString aNewStreamName = aPackageName + sExt; + + xRoot = xBlkRoot->openStorageElement( aOldName, embed::ElementModes::READWRITE ); + try + { + xRoot->renameElement ( aOldStreamName, aNewStreamName ); + } + catch(const container::ElementExistException&) + { + SAL_WARN("sw", "Couldn't rename " << aOldStreamName << " to " << aNewStreamName); + } + uno::Reference < embed::XTransactedObject > xTrans( xRoot, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + xRoot = nullptr; + } + + try + { + xBlkRoot->renameElement ( aOldName, aPackageName ); + } + catch(const container::ElementExistException&) + { + SAL_WARN("sw", "Couldn't rename " << aOldName << " to " << aPackageName); + } + } + uno::Reference < embed::XTransactedObject > xTrans( 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::StartPutBlock( const OUString& rShort, const OUString& rPackageName ) +{ + OSL_ENSURE( xBlkRoot.is(), "No storage set" ); + if(!xBlkRoot.is()) + return ERRCODE_NONE; + GetIndex ( rShort ); + try + { + xRoot = xBlkRoot->openStorageElement( rPackageName, embed::ElementModes::READWRITE ); + + uno::Reference< beans::XPropertySet > xRootProps( xRoot, uno::UNO_QUERY_THROW ); + OUString aMime( SotExchange::GetFormatMimeType( SotClipboardFormatId::STARWRITER_8 ) ); + xRootProps->setPropertyValue( "MediaType", uno::makeAny( 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; + aPackageName = GeneratePackageName( rShort ); + SetIsTextOnly( rShort, false); + return StartPutBlock (rShort, aPackageName); +} + +ErrCode SwXMLTextBlocks::PutBlock() +{ + ErrCode nRes = ERRCODE_NONE; // dead variable, this always returns 0 + SwXmlFlags nCommitFlags = nFlags; + + WriterRef xWrt; + ::GetXMLWriter ( OUString(), GetBaseURL(), xWrt); + SwWriter aWriter (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 ( xRoot.is() ) + { + std::unique_ptr<SfxMedium> pTmpMedium; + try + { + uno::Reference< embed::XStorage > xTempStorage = + ::comphelper::OStorageHelper::GetTemporaryStorage(); + + 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( xRoot ); + bOK = bTmpOK; + } + catch(const uno::Exception&) + { + } + } + + if( !bOK ) + nRes = ERR_SWG_WRITE_ERROR; + } + + try + { + uno::Reference < embed::XTransactedObject > xTrans( xRoot, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + xRoot = nullptr; + if ( nCommitFlags == SwXmlFlags::NONE ) + { + uno::Reference < embed::XTransactedObject > xTmpTrans( 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( const OUString& 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 ) + { + nFlags |= SwXmlFlags::NoRootCommit; + m_bInPutMuchBlocks = true; + } + } + } + else if( m_bInPutMuchBlocks ) + { + nFlags &= ~SwXmlFlags::NoRootCommit; + if( xBlkRoot.is() ) + { + try + { + uno::Reference < embed::XTransactedObject > xTrans( 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]->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]->bIsOnlyText; + } + return bRet; +} +bool SwXMLTextBlocks::IsOnlyTextBlock( sal_uInt16 nIdx ) const +{ + return m_aNames[nIdx]->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 ( const OUString& 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 ); + aPackageName = GeneratePackageName( rShort ); + ClearDoc(); + nRes = PutBlockText( rShort, rText, aPackageName ); + return nRes; +} + +void SwXMLTextBlocks::MakeBlockText( const OUString& 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( rText.getToken( 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..9995d3c1f --- /dev/null +++ b/sw/source/core/swg/SwXMLTextBlocks1.cxx @@ -0,0 +1,566 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/Parser.hpp> +#include <com/sun/star/xml/sax/FastParser.hpp> +#include <com/sun/star/xml/sax/FastToken.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/xmlnmspe.hxx> +#include <sfx2/event.hxx> +#include <swerror.h> + +const char XMLN_BLOCKLIST[] = "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 + { + xRoot = xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ ); + xMedium = new SfxMedium( xRoot, GetBaseURL(), "writer8" ); + SwReader aReader( *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 ( 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() ) + { + xRoot->copyElementTo( sObjReplacements, xStr, sObjReplacements ); + uno::Reference< embed::XTransactedObject > xTrans( xStr, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + } + } + } + catch( uno::Exception& ) + { + } + + xRoot = nullptr; + } + else + { + OUString aStreamName = aFolderName + ".xml"; + try + { + xRoot = xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ ); + uno::Reference < io::XStream > xStream = xRoot->openStreamElement( aStreamName, embed::ElementModes::READ ); + + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = m_aNames[nIdx]->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& ) + { + } + + 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]->aShort; + m_aLong = m_aNames[nIdx]->aLong; + aPackageName = m_aNames[nIdx]->aPackageName; + + // open stream in proper sub-storage + CloseFile(); + if ( OpenFile() != ERRCODE_NONE ) + return ERR_SWG_READ_ERROR; + + try + { + xRoot = xBlkRoot->openStorageElement( aPackageName, embed::ElementModes::READ ); + bool bOasis = SotStorage::GetVersion( xRoot ) > SOFFICE_FILEFORMAT_60; + + uno::Reference < io::XStream > xDocStream = 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 ParserInputSrouce + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = m_aName; + aParserInput.aInputStream = xInputStream; + + // get service factory + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + // create descriptor and reference to it. Either + // both or neither must be kept because of the + // reference counting! + SvMacroTableEventDescriptor* pDescriptor = + new SvMacroTableEventDescriptor(aAutotextEvents); + uno::Reference<XNameReplace> xReplace = pDescriptor; + Sequence<Any> aFilterArguments( 1 ); + aFilterArguments[0] <<= xReplace; + + // get filter + OUString sFilterComponent = bOasis + ? OUString("com.sun.star.comp.Writer.XMLOasisAutotextEventsImporter") + : OUString("com.sun.star.comp.Writer.XMLAutotextEventsImporter"); + uno::Reference< xml::sax::XFastParser > xFilter( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + sFilterComponent, aFilterArguments, xContext), + UNO_QUERY_THROW ); + + // parse the stream + try + { + xFilter->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( const OUString& rShort, OUString& rText ) +{ + OUString aFolderName = GeneratePackageName ( rShort ); + OUString aStreamName = aFolderName + ".xml"; + rText.clear(); + + try + { + bool bTextOnly = true; + + xRoot = xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ ); + if ( !xRoot->hasByName( aStreamName ) || !xRoot->isStreamElement( aStreamName ) ) + { + bTextOnly = false; + aStreamName = "content.xml"; + } + + uno::Reference < io::XStream > xContents = 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 ? + } + + xRoot = nullptr; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Tried to open non-existent folder or stream!"); + } + + return ERRCODE_NONE; +} + +ErrCode SwXMLTextBlocks::PutBlockText( const OUString& rShort, + const OUString& 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 + { + xRoot = xBlkRoot->openStorageElement( rPackageName, embed::ElementModes::WRITE ); + uno::Reference < io::XStream > xDocStream = 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( xRoot, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + + if (! (nFlags & SwXmlFlags::NoRootCommit) ) + { + uno::Reference < embed::XTransactedObject > xTmpTrans( xBlkRoot, uno::UNO_QUERY ); + if ( xTmpTrans.is() ) + xTmpTrans->commit(); + } + } + catch ( uno::Exception& ) + { + nRes = ERR_SWG_WRITE_ERROR; + } + + 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 ( !xBlkRoot.is() || !xBlkRoot->hasByName( sDocName ) || !xBlkRoot->isStreamElement( sDocName ) ) + return; + + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = sDocName; + + uno::Reference < io::XStream > xDocStream = 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 ( xBlkRoot.is() || ERRCODE_NONE == OpenFile ( false ) ) + { + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext); + OUString sDocName( XMLN_BLOCKLIST ); + + /* + if ( xBlkRoot->IsContained( sDocName) ) + { + xBlkRoot->Remove ( sDocName ); + xBlkRoot->Commit(); + } + */ + + try + { + uno::Reference < io::XStream > xDocStream = xBlkRoot->openStreamElement( sDocName, + 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( 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]->aShort; + m_aLong = m_aNames[nIdx]->aLong; + aPackageName = m_aNames[nIdx]->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 + { + xRoot = xBlkRoot->openStorageElement( aPackageName, embed::ElementModes::WRITE ); + bool bOasis = SotStorage::GetVersion( xRoot ) > SOFFICE_FILEFORMAT_60; + + uno::Reference < io::XStream > xDocStream = 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(2); + aParams[0] <<= xSaxWriter; + aParams[1] <<= 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( xRoot, uno::UNO_QUERY ); + if ( xTmpTrans.is() ) + xTmpTrans->commit(); + + uno::Reference < embed::XTransactedObject > xTrans( xBlkRoot, uno::UNO_QUERY ); + if ( xTrans.is() ) + xTrans->commit(); + + 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..803c1bb0b --- /dev/null +++ b/sw/source/core/swg/swblocks.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 <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 ) + : aShort( rShort ), aLong( rLong ), aPackageName (rShort), + bIsOnlyTextFlagInit( false ), bIsOnlyText( false ) +{ + nHashS = SwImpBlocks::Hash( rShort ); + nHashL = SwImpBlocks::Hash( rLong ); +} + +SwBlockName::SwBlockName( const OUString& rShort, const OUString& rLong, const OUString& rPackageName) + : aShort( rShort ), aLong( rLong ), aPackageName (rPackageName), + bIsOnlyTextFlagInit( false ), bIsOnlyText( false ) +{ + nHashS = SwImpBlocks::Hash( rShort ); + 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( OUString() ); + 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->nHashS == nHash + && pName->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->nHashL == nHash + && pName->aLong == rLong ) + return i; + } + return USHRT_MAX; +} + +OUString SwImpBlocks::GetShortName( sal_uInt16 n ) const +{ + if( n < m_aNames.size() ) + return m_aNames[n]->aShort; + return OUString(); +} + +OUString SwImpBlocks::GetLongName( sal_uInt16 n ) const +{ + if( n < m_aNames.size() ) + return m_aNames[n]->aLong; + return OUString(); +} + +OUString SwImpBlocks::GetPackageName( sal_uInt16 n ) const +{ + if( n < m_aNames.size() ) + return m_aNames[n]->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->bIsOnlyTextFlagInit = true; + pNew->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 ) + 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 ) + { + 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 ]->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(); + } +} + +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 ]->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 ]->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->bIsOnlyTextFlagInit && + !m_pImp->IsFileChanged() && !m_pImp->OpenFile() ) + { + pBlkNm->bIsOnlyText = m_pImp->IsOnlyTextBlock( pBlkNm->aShort ); + pBlkNm->bIsOnlyTextFlagInit = true; + m_pImp->CloseFile(); + } + bRet = pBlkNm->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 ]->bIsOnlyTextFlagInit ) + return m_pImp->m_aNames[ nIdx ]->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..d1e724936 --- /dev/null +++ b/sw/source/core/table/swnewtable.cxx @@ -0,0 +1,2199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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> + +#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; + 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( long& rMin, 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(); + } + + long nNew = 0; // will be the right border of the current box + 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 long lcl_Box2LeftBorder( const SwTableBox& rBox ) +{ + if( !rBox.GetUpper() ) + return 0; + 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( long nLeft, const SwTableLine* pLine ) +{ + if( !pLine ) + return nullptr; + 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 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. + 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 ) + { + long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan(); + long 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; + 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; + long nMid = ( nMin + nMax ) / 2; + + auto pRet(std::make_unique<SwBoxSelection>()); + std::vector< std::pair< SwTableBox*, long > > aNewWidthVector; + size_t nCheckBottom = nBottom; + long nLeftSpan = 0; + long nRightSpan = 0; + long nLeftSpanCnt = 0; + 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; + 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" ); + long nLeft = nRight; + nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth(); + long nRowSpan = pBox->getRowSpan(); + if( nRight <= nMin ) + { + if( nRight == nMin && nLeftSpanCnt ) + bOkay = false; + continue; + } + SwTableBox* pInnerBox = nullptr; + SwTableBox* pLeftBox = nullptr; + SwTableBox* pRightBox = nullptr; + long nDiff = 0; + 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 ) + { + long nTmpSpan = pInnerBox->getRowSpan(); + if( nTmpSpan > 1 ) + nBottom += nTmpSpan - 1; + else if( nTmpSpan < -1 ) + nBottom -= nTmpSpan + 1; + } + SwTableBox* pOuterBox = pLeftBox; + do + { + if( pOuterBox ) + { + 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(); + 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 long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos, + const SwSelBoxes& rBoxes, bool bBehind ) +{ + sal_Int32 nAddWidth = 0; + long nCount = 0; + for (size_t j = 0; j < rBoxes.size(); ++j) + { + SwTableBox *pBox = rBoxes[j]; + SwTableLine* pLine = pBox->GetUpper(); + 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* pDoc, const SwSelBoxes& rBoxes, + sal_uInt16 nCnt, bool bBehind ) +{ + if( m_aLines.empty() || !nCnt ) + return false; + + CHECK_TABLE( *this ) + 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 = long( nAddWidth / nCnt ); // Rounding + nAddWidth = nNewBoxWidth * nCnt; // Rounding + if( !nAddWidth || nAddWidth >= nTableWidth ) + return false; + AdjustWidths( static_cast< long >(nTableWidth), static_cast< 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; + long 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( pDoc, pTableNd, pLine, pBoxFrameFormat, pBox, nInsPos, nCnt ); + long nRowSpan = pBox->getRowSpan(); + 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(); + 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 + long nRowSpan = static_cast<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; + + 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 ) +{ + long nAbsSpan = getRowSpan(); + if( nAbsSpan < 0 ) + nAbsSpan = -nAbsSpan; + if( nAbsSpan == 1 || !nMaxStep ) + return *this; + + if( nMaxStep > --nAbsSpan ) + nMaxStep = static_cast<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; + 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 ); + 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* pDoc, 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(); + long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 ); + if( !nNewHeight ) + ++nNewHeight; + aFSz.SetHeight( nNewHeight ); + pFrameFormat->SetFormatAttr( aFSz ); + } + InsertRow_( pDoc, 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 ) + { + long 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; + long nRowSpan = pBox->getRowSpan(); + if( nRowSpan < 2 ) + nSpan = 0; + else if( nSpan ) + { + sal_uInt16 nEndOfRowSpan = static_cast<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* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, + bool bSameHeight ) +{ + CHECK_TABLE( *this ) + ++nCnt; + FndBox_ aFndBox( nullptr, nullptr ); + aFndBox.SetTableLines( rBoxes, *this ); + + if( bSameHeight && pDoc->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( pDoc, 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( pDoc, 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 ) + { + long 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() ) + { + for (size_t i = 0; i < rBoxes.size(); ++i) + { + SwTableBox* pBox = rBoxes[i]; + long nRowSpan = pBox->getRowSpan(); + if( nRowSpan != 1 && pBox->GetFrameFormat()->GetFrameSize().GetWidth() ) + { + 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, long nMin, long nMax, + SwTableLine& rLine, bool bChkProtected, bool bColumn ) +{ + long nLeft = 0; + long nRight = 0; + 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" ); + 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; + long 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 + long nUpperMin = 0, nUpperMax = 0; + 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 ) + { + long nMinWidth = nUpperMax - nUpperMin; + 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 ); + } + + { + long nMin = std::min(nUpperMin, nLowerMin); + 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, long &rMin, 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(); + long nRight = 0; + for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox ) + { + 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( long nMin, long nMax ) +{ + OSL_ENSURE( m_bNewModel, "Don't call me for old tables" ); + if( m_aLines.empty() || nMax < nMin ) + return; + 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(); + long nRight = 0; + for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox ) + { + long nLeft = nRight; + SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; + nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth(); + if( nRight < nMin ) + continue; + if( nLeft > nMax ) + break; + 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]; + long 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" ); + long 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 ) + { + 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() ) + { + for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) + { + SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol]; + OSL_ENSURE( pBox, "Missing Table Box" ); + long 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 ) + { + long nLeftBorder = lcl_Box2LeftBorder( *pBox ); + SwTableBox* pNext; + do + { + pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] ); + if( pNext ) + { + pBox = pNext; + 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" ); + long nRowSp = pBox->getRowSpan(); + if( nRowSp < 0 ) + nRowSp = -nRowSp; + if( nRowSp > 1 ) + { + lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines), + static_cast<sal_uInt16>(nLastLine), false ); + break; + } + } +} + +#ifdef DBG_UTIL + +namespace { + +struct RowSpanCheck +{ + long 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; + long 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..e3e083127 --- /dev/null +++ b/sw/source/core/table/swtable.cxx @@ -0,0 +1,2771 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <hints.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/colritem.hxx> +#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 <list> +#include <calbck.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, sal_uLong nNdPos ); + +inline const Color* SwTableBox::GetSaveUserColor() const +{ + return mpUserColor.get(); +} + +inline const Color* SwTableBox::GetSaveNumFormatColor() const +{ + return mpNumFormatColor.get(); +} + +inline void SwTableBox::SetSaveUserColor(const Color* p ) +{ + if (p) + mpUserColor.reset(new Color(*p)); + else + mpUserColor.reset(); +} + +inline void SwTableBox::SetSaveNumFormatColor( const Color* p ) +{ + if (p) + mpNumFormatColor.reset(new Color(*p)); + else + mpNumFormatColor.reset(); +} + +long SwTableBox::getRowSpan() const +{ + return mnRowSpan; +} + +void SwTableBox::setRowSpan( long 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, " " ); + for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); ) + if( '\x9' == c ) + rText = rText.replaceAt( n, 1, " " ); + 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* pDoc, 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 = pDoc->GetNodes().GoNext( &aIdx ); + OSL_ENSURE( pCNd, "Box with no content node" ); + + if( pCNd->IsTextNode() ) + { + if( pBox->GetSaveNumFormatColor() && pCNd->GetpSwAttrSet() ) + { + SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() ); + if( pBox->GetSaveUserColor() ) + aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR )); + else + aAttrSet.ClearItem( RES_CHRATR_COLOR ); + pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, + static_cast<SwTextNode*>(pCNd)->GetTextColl(), + &aAttrSet, nInsPos, nCnt ); + } + else + pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, + static_cast<SwTextNode*>(pCNd)->GetTextColl(), + pCNd->GetpSwAttrSet(), + nInsPos, nCnt ); + } + else + pDoc->GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat, + pDoc->GetDfltTextFormatColl(), nullptr, + nInsPos, nCnt ); + + long 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) +{ + return 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 long nOld, + const long nNew, std::vector<SwFormat*>& rFormatArr ); + +static void lcl_ModifyLines( SwTableLines &rLines, const long nOld, + const 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 long nOld, + const 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 *= nNew; + nBox /= 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + // catch SSize changes, to adjust the lines/boxes + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ; + const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr; + + if( RES_ATTRSET_CHG == nWhich ) + { + if (pOld && pNew && SfxItemState::SET == static_cast<const SwAttrSetChg*>(pNew)->GetChgSet()->GetItemState( + RES_FRM_SIZE, false, reinterpret_cast<const SfxPoolItem**>(&pNewSize))) + { + pOldSize = &static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()->GetFrameSize(); + } + } + else if( RES_FRM_SIZE == nWhich ) + { + pOldSize = static_cast<const SwFormatFrameSize*>(pOld); + pNewSize = static_cast<const SwFormatFrameSize*>(pNew); + } + else + CheckRegistration( pOld ); + + if (pOldSize && pNewSize && !m_bModifyLocked) + AdjustWidths( pOldSize->GetWidth(), pNewSize->GetWidth() ); +} + +void SwTable::AdjustWidths( const long nOld, const 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<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 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. + long nPos = 0; + long nLeftMin = 0; + long nRightMax = 0; + if (nWish != 0) //fdo#33012 0 width frmfmt + { + SwTwips nSum = 0; + const SwTableBox *pCur = pBox; + const SwTableLine *pLine = pBox->GetUpper(); + const 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 long nTmp = lcl_MulDiv64<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 ) + { + 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 ) + { + // 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 ); + long nCmp = rToFill[j]; + + if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) && + (nPos <= (nCmp + COLFUZZY)) ) + { + // check if nLeftMin is > old minimum for entry nPos: + const long nOldMin = rEntry.nMin; + if ( nLeftMin > nOldMin ) + rEntry.nMin = nLeftMin; + // check if nRightMin is < old maximum for entry nPos: + const 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 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; + 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 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. + 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<long>( + rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(), + nOldAct, rParm.nOldWish); + } + pCur = pLine->GetUpper(); + pLine = pCur ? pCur->GetUpper() : nullptr; + } + long nLeftDiff = 0; + long nRightDiff = 0; + if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this. + { + // Right edge is left edge plus width. + const long nWidth = lcl_MulDiv64<long>( + pBox->GetFrameFormat()->GetFrameSize().GetWidth(), + nOldAct, rParm.nOldWish); + const 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 long nWidth = lcl_MulDiv64<long>( + pBox->GetFrameFormat()->GetFrameSize().GetWidth(), + nOldAct, rParm.nOldWish); + const 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 < static_cast<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. + long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why? + nLeftDiff *= rParm.nNewWish; + nLeftDiff /= nTmp; + nRightDiff *= rParm.nNewWish; + nRightDiff /= nTmp; + 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 long nDiff, Parm &rParm ); + +static void lcl_AdjustLines( SwTableLines &rLines, const 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 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()) + { + 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 + aOri.SetHoriOrient( text::HoriOrientation::LEFT_AND_WIDTH ); + } + pFormat->SetFormatAttr( aOri ); + } + const long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why? + long nTabDiff = 0; + + if ( rOld.GetLeft() != rNew.GetLeft() ) + { + nTabDiff = rOld.GetLeft() - rNew.GetLeft(); + nTabDiff *= aParm.nOldWish; + nTabDiff /= nAct; + } + if ( rOld.GetRight() != rNew.GetRight() ) + { + 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::list<sal_uInt16> &rSpanPos, ChangeList& rChanges, + SwTableLine* pLine, long nWish, long nWidth, bool bTop ) +{ + if( rChanges.empty() ) + { + rSpanPos.clear(); + return; + } + if( rSpanPos.empty() ) + { + rChanges.clear(); + return; + } + std::list<sal_uInt16> aNewSpanPos; + ChangeList aNewChanges; + ChangeList::iterator pCurr = rChanges.begin(); + aNewChanges.push_back( *pCurr ); // Nullposition + std::list<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 long 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 long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); + const long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft(); + if( nNewWidth < 1 || nOldWidth < 1 ) + return; + for( size_t i = 0; i <= rOld.Count(); ++i ) + { + long nNewPos; + 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<long>(nNewPos, rParm.nNewWish, nNewWidth); + nOldPos = lcl_MulDiv64<long>(nOldPos, rParm.nOldWish, nOldWidth); + if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 ) + { + ColChange aChg( static_cast<sal_uInt16>(nOldPos), static_cast<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::list<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 < static_cast<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 < static_cast<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( const OUString& rStr ) +{ + bool bIsValid = true; + sal_Int32 nLen = rStr.getLength(); + for( sal_Int32 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; + if( (cChar -= 'A') >= 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 = static_cast<sal_uInt16>(rStr.toInt32()); + } + rStr.clear(); + } + else + { + nRet = 0; + const OUString aText( rStr.copy( 0, nPos ) ); + if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) ) + { + nRet = static_cast<sal_uInt16>(aText.toInt32()); + } + 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( sal_uLong 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(); + sal_uLong 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 ) + { + SwModify* pModify = pCNd; + // #144862# Better handling of table in table + if ( pTableNd && pTableNd->GetTable().GetFrameFormat() ) + pModify = pTableNd->GetTable().GetFrameFormat(); + + SwFrame* pFrame = pModify ? SwIterator<SwFrame,SwModify>(*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_aBoxes(), + m_pUpper( pUp ) +{ + 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 + SwModify* 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 ) +{ + SwFrameFormat *pOld = GetFrameFormat(); + SwIterator<SwRowFrame,SwFormat> aIter( *pOld ); + + // First, re-register the Frames. + for( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() ) + { + if( pRow->GetTabLine() == this ) + { + pRow->RegisterToFormat( *pNewFormat ); + + pRow->InvalidateSize(); + pRow->InvalidatePrt_(); + pRow->SetCompletePaint(); + pRow->ReinitializeFrameSizeAttrFlags(); + + // #i35063# + // consider 'split row allowed' attribute + SwTabFrame* pTab = pRow->FindTabFrame(); + bool bInFollowFlowRow = false; + const bool bInFirstNonHeadlineRow = pTab->IsFollow() && + pRow == pTab->GetFirstNonHeadlineRow(); + if ( bInFirstNonHeadlineRow || + !pRow->GetNext() || + ( bInFollowFlowRow = pRow->IsInFollowFlowRow() ) || + nullptr != pRow->IsInSplitTableRow() ) + { + if ( bInFirstNonHeadlineRow || bInFollowFlowRow ) + pTab = pTab->FindMaster(); + + pTab->SetRemoveFollowFlowLinePending( true ); + pTab->InvalidatePos(); + } + } + } + + // 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; +} + +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 + SwModify* 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(); + SwIterator<SwCellFrame,SwFormat> aIter( *pOld ); + + // 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) + for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() ) + { + if( pCell->GetTabBox() == this ) + { + pCell->RegisterToFormat( *pNewFormat ); + pCell->InvalidateSize(); + pCell->InvalidatePrt_(); + pCell->SetCompletePaint(); + pCell->SetDerivedVert( false ); + pCell->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 = pCell->FindTabFrame(); + if ( pTab && pTab->IsCollapsingBorders() ) + { + SwFrame* pRow = pCell->GetUpper(); + pRow->InvalidateSize_(); + pRow->InvalidatePrt_(); + } + } + } + + // 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; + + if( 0 == (nCol = nCol - nCalc) ) + 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); + if( nullptr != ( pBox = pLine->GetUpper()) ) + 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; +} + +sal_uLong SwTableBox::GetSttIdx() const +{ + return m_pStartNode ? m_pStartNode->GetIndex() : 0; +} + + // 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 ) +{ + sal_uLong nNdPos = rBox.IsValidNumTextNd(); + ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos); +} +void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol, + bool bChgAlign,sal_uLong nNdPos ) +{ + + if( ULONG_MAX == nNdPos ) + return; + + SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); + SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); + const SfxPoolItem* pItem; + + // assign adjustment + if( bChgAlign ) + { + 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" + if( !pTNd->GetpSwAttrSet() || SfxItemState::SET != pTNd->GetpSwAttrSet()-> + GetItemState( RES_CHRATR_COLOR, false, &pItem )) + pItem = nullptr; + + const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); + const Color* pNewUserColor = pItem ? &static_cast<const SvxColorItem*>(pItem)->GetValue() : nullptr; + + 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( pItem ) + { + 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 ); + + 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 + if( bChgAlign && + ( SfxItemState::SET != rBox.GetFrameFormat()->GetItemState( + RES_VERT_ORIENT, true, &pItem ) || + text::VertOrientation::TOP == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() )) + { + rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM )); + } + +} + +static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat ) +{ + sal_uLong nNdPos = rBox.IsValidNumTextNd( false ); + if( ULONG_MAX == nNdPos ) + return; + + SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc(); + SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode(); + bool bChgAlign = pDoc->IsInsTableAlignNum(); + const SfxPoolItem* pItem; + + 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 + if( bChgAlign && pAttrSet && SfxItemState::SET == pAttrSet->GetItemState( + RES_PARATR_ADJUST, false, &pItem ) && + SvxAdjust::Right == static_cast<const SvxAdjustItem*>(pItem)->GetAdjust() ) + { + pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) ); + } + + // assign color or save "user color" + if( !pAttrSet || SfxItemState::SET != pAttrSet-> + GetItemState( RES_CHRATR_COLOR, false, &pItem )) + pItem = nullptr; + + const Color* pOldNumFormatColor = rBox.GetSaveNumFormatColor(); + const Color* pNewUserColor = pItem ? &static_cast<const SvxColorItem*>(pItem)->GetValue() : nullptr; + + 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( pItem ) + { + 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 ); + + // assign vertical orientation + if( bChgAlign && + SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( + RES_VERT_ORIENT, false, &pItem ) && + text::VertOrientation::BOTTOM == static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() ) + { + rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP )); + } + +} + +// for detection of modifications (mainly TableBoxAttribute) +void SwTableBoxFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( !IsModifyLocked() && GetDoc() && !GetDoc()->IsInDtor()) + { + const SwTableBoxNumFormat *pNewFormat = nullptr; + const SwTableBoxFormula *pNewFormula = nullptr; + const SwTableBoxValue *pNewVal = nullptr; + sal_uLong nOldFormat = getSwDefaultTextFormat(); + + switch( pNew ? pNew->Which() : 0 ) + { + case RES_ATTRSET_CHG: + { + const SfxItemSet& rSet = *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet(); + if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, + false, reinterpret_cast<const SfxPoolItem**>(&pNewFormat) ) ) + nOldFormat = static_cast<const SwAttrSetChg*>(pOld)-> + GetChgSet()->Get( RES_BOXATR_FORMAT ).GetValue(); + rSet.GetItemState( RES_BOXATR_FORMULA, false, + reinterpret_cast<const SfxPoolItem**>(&pNewFormula) ); + rSet.GetItemState( RES_BOXATR_VALUE, false, + reinterpret_cast<const SfxPoolItem**>(&pNewVal) ); + break; + } + case RES_BOXATR_FORMAT: + pNewFormat = static_cast<const SwTableBoxNumFormat*>(pNew); + nOldFormat = static_cast<const SwTableBoxNumFormat*>(pOld)->GetValue(); + break; + case RES_BOXATR_FORMULA: + pNewFormula = static_cast<const SwTableBoxFormula*>(pNew); + break; + case RES_BOXATR_VALUE: + pNewVal = static_cast<const SwTableBoxValue*>(pNew); + break; + } + + // something changed and some BoxAttribut remained in the set! + if( pNewFormat || pNewFormula || pNewVal ) + { + GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, 0); + + if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMAT, false ) || + SfxItemState::SET == GetItemState( RES_BOXATR_VALUE, false ) || + SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false ) ) + { + // fetch the box + SwIterator<SwTableBox,SwFormat> aIter( *this ); + SwTableBox* pBox = aIter.First(); + if( pBox ) + { + OSL_ENSURE( !aIter.Next(), "zero or more than one box at format" ); + + 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 + (void)GetItemState(RES_BOXATR_FORMAT, false, reinterpret_cast<const SfxPoolItem**>(&pNewFormat)); + nOldFormat = GetTableBoxNumFormat().GetValue(); + nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat; + } + + // is it newer or has the current been removed? + if( pNewVal ) + { + 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( !pNewVal && SfxItemState::SET != GetItemState( + RES_BOXATR_VALUE, false, reinterpret_cast<const SfxPoolItem**>(&pNewVal) )) + { + // so far, no value has been set, so try to evaluate the content + sal_uLong nNdPos = pBox->IsValidNumTextNd(); + if( ULONG_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 = pNewVal->GetValue(); + bIsNumFormat = true; + } + + // format contents with the new value assigned and write to paragraph + Color* pCol = nullptr; + OUString sNewText; + 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 + } + + if( !bChgText ) + { + sNewText.clear(); + } + } + + // across all boxes + ChgTextToNum( *pBox, sNewText, pCol, + GetDoc()->IsInsTableAlignNum() ); + + } + else if( bNewIsTextFormat && nOldFormat != nNewFormat ) + { + ChgNumToText( *pBox, nNewFormat ); + } + } + } + } + } + // call base class + SwFrameFormat::Modify( pOld, pNew ); +} + +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; + sal_uLong nNdPos = IsValidNumTextNd(); + if( ULONG_MAX != nNdPos ) + { + OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() ); + // Keep Tabs + lcl_TabToBlankAtSttEnd( aText ); + rIsEmptyTextNd = aText.isEmpty(); + SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter(); + + const SfxPoolItem* pItem; + if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, &pItem )) + { + rFormatIndex = static_cast<const SwTableBoxNumFormat*>(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; + const SwTableBoxValue *pValue; + + if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_VALUE, false, + reinterpret_cast<const SfxPoolItem**>(&pValue) )) + pValue = nullptr; + if( SfxItemState::SET != GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT, false, + reinterpret_cast<const SfxPoolItem**>(&pNumFormat) )) + pNumFormat = nullptr; + + sal_uLong nNdPos; + if( pNumFormat && pValue && ULONG_MAX != ( nNdPos = IsValidNumTextNd() ) ) + { + OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]-> + GetTextNode()->GetRedlineText() ); + lcl_DelTabsAtSttEnd( sOldText ); + + Color* pCol = nullptr; + GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString( + pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol ); + + bRet = sNewText != sOldText || + !( ( !pCol && !GetSaveNumFormatColor() ) || + ( pCol && GetSaveNumFormatColor() && + *pCol == *GetSaveNumFormatColor() )); + } + } + return bRet; +} + +sal_uLong SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const +{ + sal_uLong nPos = ULONG_MAX; + if( m_pStartNode ) + { + SwNodeIndex aIdx( *m_pStartNode ); + sal_uLong nIndex = aIdx.GetIndex(); + const sal_uLong 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() ) + { + continue; + } + nPos = ULONG_MAX; + break; + } + } + } + } + } + else + nPos = ULONG_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() +{ + const SfxPoolItem *pFormatItem, *pValItem; + SwFrameFormat* pFormat = GetFrameFormat(); + if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMAT, true, &pFormatItem ) + && SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, true, &pValItem )) + { + const sal_uLong nFormatId = static_cast<const SwTableBoxNumFormat*>(pFormatItem)->GetValue(); + sal_uLong nNdPos = ULONG_MAX; + SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter(); + + if( !pNumFormatr->IsTextFormat( nFormatId ) && + ULONG_MAX != (nNdPos = IsValidNumTextNd()) ) + { + double fVal = static_cast<const SwTableBoxValue*>(pValItem)->GetValue(); + 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 SwTableLine::RegisterToFormat( SwFormat& rFormat ) +{ + rFormat.Add( this ); +} + +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..d570a8015 --- /dev/null +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -0,0 +1,2280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <docary.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 <tools/globname.hxx> +#include <svx/svdobj.hxx> + +using namespace ::com::sun::star; + +// Some static data structures + +TableColumnsMap SwEnhancedPDFExportHelper::aTableColumnsMap; +LinkIdMap SwEnhancedPDFExportHelper::aLinkIdMap; +NumListIdMap SwEnhancedPDFExportHelper::aNumListIdMap; +NumListBodyIdMap SwEnhancedPDFExportHelper::aNumListBodyIdMap; +FrameTagIdMap SwEnhancedPDFExportHelper::aFrameTagIdMap; + +LanguageType SwEnhancedPDFExportHelper::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: +const char aDocumentString[] = "Document"; +const char aDivString[] = "Div"; +const char aSectString[] = "Sect"; +const char aHString[] = "H"; +const char aH1String[] = "H1"; +const char aH2String[] = "H2"; +const char aH3String[] = "H3"; +const char aH4String[] = "H4"; +const char aH5String[] = "H5"; +const char aH6String[] = "H6"; +const char aListString[] = "L"; +const char aListItemString[] = "LI"; +const char aListBodyString[] = "LBody"; +const char aBlockQuoteString[] = "BlockQuote"; +const char aCaptionString[] = "Caption"; +const char aIndexString[] = "Index"; +const char aTOCString[] = "TOC"; +const char aTOCIString[] = "TOCI"; +const char aTableString[] = "Table"; +const char aTRString[] = "TR"; +const char aTDString[] = "TD"; +const char aTHString[] = "TH"; +const char aBibEntryString[] = "BibEntry"; +const char aQuoteString[] = "Quote"; +const char aSpanString[] = "Span"; +const char aCodeString[] = "Code"; +const char aFigureString[] = "Figure"; +const char aFormulaString[] = "Formula"; +const char aLinkString[] = "Link"; +const char aNoteString[] = "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* pDoc = rNode.GetDoc(); + const SwNodes& rNodes = pDoc->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, 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 ) + : nEndStructureElement( 0 ), + nRestoreCurrentTag( -1 ), + mpNumInfo( pNumInfo ), + mpFrameInfo( pFrameInfo ), + mpPorInfo( pPorInfo ) +{ + mpPDFExtOutDevData = + dynamic_cast< vcl::PDFExtOutDevData*>( rOut.GetExtOutDevData() ); + + if ( mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF() ) + { +#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() ) + { +#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 + + } +} + +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 ) + { + void* pKey = lcl_GetKeyFromFrame( *pKeyFrame ); + + if ( pKey ) + { + FrameTagIdMap& rFrameTagIdMap = SwEnhancedPDFExportHelper::GetFrameTagIdMap(); + const FrameTagIdMap::const_iterator aIter = rFrameTagIdMap.find( pKey ); + if ( aIter != rFrameTagIdMap.end() ) + nReopenTag = (*aIter).second; + } + } + } + + if ( -1 != nReopenTag ) + { + 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 ( nRestoreCurrentTag != -1 ) + { + const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( 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 ); + ++nEndStructureElement; + +#if OSL_DEBUG_LEVEL > 1 + aStructStack.push_back( static_cast<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 ) +{ + vcl::PDFWriter::StructAttributeValue eVal; + sal_Int32 nVal; + + /* + * ATTRIBUTES FOR BLSE + */ + if ( mpFrameInfo ) + { + 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; + + // 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 : + 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 : + bPlacement = + bWidth = + bHeight = + 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 ); + } + } + + // Formally here bAlternateText was triggered for PDF export, but this + // was moved for more general use to primitives and usage in + // VclMetafileProcessor2D (see processGraphicPrimitive2D). + + 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 ) + { + const SwCellFrame* pThisCell = dynamic_cast<const SwCellFrame*>(pFrame); + if ( pThisCell ) + { + 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 long nLeft = fnRectX.GetLeft(pThisCell->getFrameArea()); + const 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.IsInside(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 ) + { + 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 ) ) + 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 = static_cast<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 long nLeft = aRectFnSet.GetLeft(pCellFrame->getFrameArea()); + rCols.insert( nLeft ); + + while ( pCellFrame ) + { + const 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->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 ( nEndStructureElement > 0 ) + { + EndTag(); + --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::Table : + 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() ); + } + } + + aTableColumnsMap.clear(); + aLinkIdMap.clear(); + aNumListIdMap.clear(); + aNumListBodyIdMap.clear(); + 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; + + 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 ); + long nOrigHeight = pCurrPage->getFrameArea().Height(); + long nNewHeight = nOrigHeight*fScale; + 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( 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; + 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" ); + + 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, aLinkPageNum); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( rLinkRect, nLinkId ); + 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 ); + } + } + } + } + } + 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 SfxPoolItem* pItem; + if ( RES_DRAWFRMFMT != pFrameFormat->Which() && + GetFrameOfModify(mrSh.GetLayout(), *pFrameFormat, SwFrameType::Fly) && + SfxItemState::SET == pFrameFormat->GetAttrSet().GetItemState( RES_URL, true, &pItem ) ) + { + const SwPageFrame* pCurrPage = + static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() ); + + OUString aURL( static_cast<const SwFormatURL*>(pItem)->GetURL() ); + const bool bIntern = !aURL.isEmpty() && '#' == 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 ); + + // 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, 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 ); + } + } + } + } + } + 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 ); + + // 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, aLinkPageNum); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( rLinkRect, nLinkId ); + aLinkIdMap.push_back( aLinkEntry ); + + // Connect Link and Destination: + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + + // #i44368# Links in Header/Footer + if ( bHeaderFooter ) + { + const OUString aDummy; + MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aDummy, true ); + } + } + } + } + } + mrSh.SwCursorShell::ClearMark(); + } + + // 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() ) + { + // Link PageNums + std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( aLinkRect ); + + // 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 ); + + for (sal_Int32 aLinkPageNum : aLinkPageNums) + { + // Link Export + tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, aLinkRect.SVRect())); + const sal_Int32 nLinkId = + pPDFExtOutDevData->CreateLink(aRect, aLinkPageNum); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( aLinkRect, nLinkId ); + aLinkIdMap.push_back( aLinkEntry ); + + if ( -1 != nDestPageNum ) + { + aRect = SwRectToPDFRect(pCurrPage, rDestRect.SVRect()); + // Destination Export + const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(rDestRect.SVRect(), nDestPageNum); + + // Connect Link and Destination: + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + } + } + } + } + + // 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(); +} + +// 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 ) 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, 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..3a007ce05 --- /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; + } +} + +SwGrammarMarkUp* SwGrammarMarkUp::SplitGrammarList( sal_Int32 nSplitPos ) +{ + SwGrammarMarkUp* pNew = static_cast<SwGrammarMarkUp*>(SplitList( nSplitPos )); + 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 = 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..e391b0825 --- /dev/null +++ b/sw/source/core/text/atrhndl.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_ATRHNDL_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_ATRHNDL_HXX + +#define NUM_ATTRIBUTE_STACKS 44 + +#include <memory> +#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::unique_ptr<SwFont> m_pFnt; + + 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_pFnt, "ResetFont without a font"); + if (m_pFnt) + rFnt = *m_pFnt; +}; + +inline const SwFont* SwAttrHandler::GetFont() const +{ + return m_pFnt.get(); +}; + +#endif + +/* 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..aa1b9f67e --- /dev/null +++ b/sw/source/core/text/atrstck.cxx @@ -0,0 +1,846 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/blinkitem.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 +}; + +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 = static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle().get(); + } + else + { + // Get the attributes from the template + const SwCharFormat* pFormat = RES_TXTATR_INETFMT == rAttr.Which() ? + static_cast<const SwFormatINetFormat&>(rAttr).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(); + const SfxPoolItem* pItem; + if (SfxItemState::SET == pTmpFormat->GetItemState(RES_CHRATR_COLOR, true, &pItem)) + *pColor = static_cast<const SvxColorItem*>(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_pFnt) + { + *m_pFnt = rFnt; + } + else + { + m_pFnt.reset(new SwFont(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 ( 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 SfxPoolItem* pTwoLineItem = CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES ); + bTwoLineAct = static_cast<const SvxTwoLinesItem*>(pTwoLineItem)->GetValue(); + } + else + bTwoLineAct = + static_cast<const SvxTwoLinesItem*>(m_pDefaultArray[ nTwoLineStack ])->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 SfxPoolItem* pRotateItem = CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE ); + rFnt.SetVertical( static_cast<const SvxCharRotateItem*>(pRotateItem)->GetValue(), + m_bVertLayout ); + } + else + rFnt.SetVertical( + static_cast<const SvxCharRotateItem*>(m_pDefaultArray[ nRotateStack ])->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( static_cast<const SvxCaseMapItem&>(rItem).GetCaseMap() ); + break; + case RES_CHRATR_COLOR : + rFnt.SetColor( static_cast<const SvxColorItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_CONTOUR : + rFnt.SetOutline( static_cast<const SvxContourItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_CROSSEDOUT : + rFnt.SetStrikeout( static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout() ); + break; + case RES_CHRATR_ESCAPEMENT : + rFnt.SetEscapement( static_cast<const SvxEscapementItem&>(rItem).GetEsc() ); + rFnt.SetProportion( static_cast<const SvxEscapementItem&>(rItem).GetProportionalHeight() ); + break; + case RES_CHRATR_FONT : + rFnt.SetName( static_cast<const SvxFontItem&>(rItem).GetFamilyName(), SwFontScript::Latin ); + rFnt.SetStyleName( static_cast<const SvxFontItem&>(rItem).GetStyleName(), SwFontScript::Latin ); + rFnt.SetFamily( static_cast<const SvxFontItem&>(rItem).GetFamily(), SwFontScript::Latin ); + rFnt.SetPitch( static_cast<const SvxFontItem&>(rItem).GetPitch(), SwFontScript::Latin ); + rFnt.SetCharSet( static_cast<const SvxFontItem&>(rItem).GetCharSet(), SwFontScript::Latin ); + break; + case RES_CHRATR_FONTSIZE : + rFnt.SetSize(Size(0,static_cast<const SvxFontHeightItem&>(rItem).GetHeight() ), SwFontScript::Latin ); + break; + case RES_CHRATR_KERNING : + rFnt.SetFixKerning( static_cast<const SvxKerningItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_LANGUAGE : + rFnt.SetLanguage( static_cast<const SvxLanguageItem&>(rItem).GetLanguage(), SwFontScript::Latin ); + break; + case RES_CHRATR_POSTURE : + rFnt.SetItalic( static_cast<const SvxPostureItem&>(rItem).GetPosture(), SwFontScript::Latin ); + break; + case RES_CHRATR_SHADOWED : + rFnt.SetShadow( static_cast<const SvxShadowedItem&>(rItem).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 && !static_cast<const SvxCharHiddenItem*>(pTmpItem)->GetValue()) ) + { + rFnt.SetUnderline( static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle() ); + rFnt.SetUnderColor( static_cast<const SvxUnderlineItem&>(rItem).GetColor() ); + } + break; + } + case RES_CHRATR_BOX: + { + const SvxBoxItem& aBoxItem = static_cast<const SvxBoxItem&>(rItem); + 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 = static_cast<const SvxShadowItem&>(rItem); + rFnt.SetShadowColor( aShadowItem.GetColor() ); + rFnt.SetShadowWidth( aShadowItem.GetWidth() ); + rFnt.SetShadowLocation( aShadowItem.GetLocation() ); + break; + } + case RES_CHRATR_OVERLINE : + rFnt.SetOverline( static_cast<const SvxOverlineItem&>(rItem).GetLineStyle() ); + rFnt.SetOverColor( static_cast<const SvxOverlineItem&>(rItem).GetColor() ); + break; + case RES_CHRATR_WEIGHT : + rFnt.SetWeight( static_cast<const SvxWeightItem&>(rItem).GetWeight(), SwFontScript::Latin ); + break; + case RES_CHRATR_WORDLINEMODE : + rFnt.SetWordLineMode( static_cast<const SvxWordLineModeItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_AUTOKERN : + if( static_cast<const SvxAutoKernItem&>(rItem).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(new Color( static_cast<const SvxBrushItem&>(rItem).GetColor() ) ); + break; + case RES_CHRATR_HIGHLIGHT : + rFnt.SetHighlightColor( static_cast<const SvxBrushItem&>(rItem).GetColor() ); + break; + case RES_CHRATR_CJK_FONT : + rFnt.SetName( static_cast<const SvxFontItem&>(rItem).GetFamilyName(), SwFontScript::CJK ); + rFnt.SetStyleName( static_cast<const SvxFontItem&>(rItem).GetStyleName(), SwFontScript::CJK ); + rFnt.SetFamily( static_cast<const SvxFontItem&>(rItem).GetFamily(), SwFontScript::CJK ); + rFnt.SetPitch( static_cast<const SvxFontItem&>(rItem).GetPitch(), SwFontScript::CJK ); + rFnt.SetCharSet( static_cast<const SvxFontItem&>(rItem).GetCharSet(), SwFontScript::CJK ); + break; + case RES_CHRATR_CJK_FONTSIZE : + rFnt.SetSize(Size( 0, static_cast<const SvxFontHeightItem&>(rItem).GetHeight()), SwFontScript::CJK); + break; + case RES_CHRATR_CJK_LANGUAGE : + rFnt.SetLanguage( static_cast<const SvxLanguageItem&>(rItem).GetLanguage(), SwFontScript::CJK ); + break; + case RES_CHRATR_CJK_POSTURE : + rFnt.SetItalic( static_cast<const SvxPostureItem&>(rItem).GetPosture(), SwFontScript::CJK ); + break; + case RES_CHRATR_CJK_WEIGHT : + rFnt.SetWeight( static_cast<const SvxWeightItem&>(rItem).GetWeight(), SwFontScript::CJK ); + break; + case RES_CHRATR_CTL_FONT : + rFnt.SetName( static_cast<const SvxFontItem&>(rItem).GetFamilyName(), SwFontScript::CTL ); + rFnt.SetStyleName( static_cast<const SvxFontItem&>(rItem).GetStyleName(), SwFontScript::CTL ); + rFnt.SetFamily( static_cast<const SvxFontItem&>(rItem).GetFamily(), SwFontScript::CTL ); + rFnt.SetPitch( static_cast<const SvxFontItem&>(rItem).GetPitch(), SwFontScript::CTL ); + rFnt.SetCharSet( static_cast<const SvxFontItem&>(rItem).GetCharSet(), SwFontScript::CTL ); + break; + case RES_CHRATR_CTL_FONTSIZE : + rFnt.SetSize(Size(0, static_cast<const SvxFontHeightItem&>(rItem).GetHeight() ), SwFontScript::CTL); + break; + case RES_CHRATR_CTL_LANGUAGE : + rFnt.SetLanguage( static_cast<const SvxLanguageItem&>(rItem).GetLanguage(), SwFontScript::CTL ); + break; + case RES_CHRATR_CTL_POSTURE : + rFnt.SetItalic( static_cast<const SvxPostureItem&>(rItem).GetPosture(), SwFontScript::CTL ); + break; + case RES_CHRATR_CTL_WEIGHT : + rFnt.SetWeight( static_cast<const SvxWeightItem&>(rItem).GetWeight(), SwFontScript::CTL ); + break; + case RES_CHRATR_EMPHASIS_MARK : + rFnt.SetEmphasisMark( + static_cast<const SvxEmphasisMarkItem&>(rItem).GetEmphasisMark() + ); + break; + case RES_CHRATR_SCALEW : + rFnt.SetPropWidth( static_cast<const SvxCharScaleWidthItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_RELIEF : + rFnt.SetRelief( static_cast<const SvxCharReliefItem&>(rItem).GetValue() ); + break; + case RES_CHRATR_HIDDEN : + if (m_pShell && m_pShell->GetWin()) + { + if ( static_cast<const SvxCharHiddenItem&>(rItem).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 SfxPoolItem* pTwoLineItem = CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES ); + bTwoLineAct = static_cast<const SvxTwoLinesItem*>(pTwoLineItem)->GetValue(); + } + else + bTwoLineAct = + static_cast<const SvxTwoLinesItem*>(m_pDefaultArray[ nTwoLineStack ])->GetValue(); + + if ( !bTwoLineAct ) + rFnt.SetVertical( static_cast<const SvxCharRotateItem&>(rItem).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 && static_cast<const SvxTwoLinesItem&>(rItem).GetValue() ) + { + rFnt.SetVertical( 0, 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 SfxPoolItem* pRotateItem = CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE ); + rFnt.SetVertical( static_cast<const SvxCharRotateItem*>(pRotateItem)->GetValue(), + m_bVertLayout ); + } + else + rFnt.SetVertical( + static_cast<const SvxCharRotateItem*>(m_pDefaultArray[ nRotateStack ])->GetValue(), + m_bVertLayout + ); + break; + } + case RES_TXTATR_CJK_RUBY : + rFnt.SetVertical( 0, 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_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_pFnt, "No font available for GetDefaultAscentAndHeight"); + + if (m_pFnt) + { + SwFont aFont( *m_pFnt ); + 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..9fd60520c --- /dev/null +++ b/sw/source/core/text/frmcrsr.cxx @@ -0,0 +1,1685 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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( aRectFnSet.GetPrtBottom(*pFrame), nUpperMaxY ); + else + nMaxY = std::max( aRectFnSet.GetPrtBottom(*pFrame), nUpperMaxY ); + } + else + nMaxY = std::min( 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; } + long X() const { return rPoint.X(); } + long Y() const { return rPoint.Y(); } + long Left() const { return aFrame.Left(); } + long Right() const { return aFrame.Right(); } + 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().IsInside( 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 brakes + 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 bRight; +public: + SwSetToRightMargin() : bRight( false ) { } + ~SwSetToRightMargin() { SwTextCursor::SetRightMargin( bRight ); } + void SetRight( const bool bNew ) { 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 sal_uLong 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 sal_uLong 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; + } + if( nullptr != ( pTmpFollow = GetFollow() ) ) + { // 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 brakes + } + 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, GetLineSpace() ); + nDist += nLineHeight; + nDiff -= nFirst; + + if( nDiff > 0 ) + { + nDiff /= nDist; + rFill.Fill().nParaCnt = static_cast<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() ) + { + const OUString aTmp(" "); + SwDrawTextInfo aDrawInf( pSh, *pOut, aTmp, 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..71e417c3a --- /dev/null +++ b/sw/source/core/text/frmform.cxx @@ -0,0 +1,2061 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 nLevel; +public: + FormatLevel() { ++nLevel; } + ~FormatLevel() { --nLevel; } + static sal_uInt16 GetLevel() { return nLevel; } + static bool LastLevel() { return 10 < nLevel; } +}; + +} + +sal_uInt16 FormatLevel::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() ) + { + 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->GetDelta() = 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->GetDelta() = 0; + } + } + + if ( pPage && !bOldInvaContent ) + pPage->ValidateContent(); + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( pOldUp == GetUpper(), "SwTextFrame::CalcFollow: heavy follow" ); +#endif + + const long nRemaining = + - aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom ); + if ( nRemaining > 0 && !GetUpper()->IsSctFrame() && + 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()) + { + 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() ) ) + { + 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 ) +{ + css::uno::Sequence< css::style::TabStop > tabs(1); + css::style::TabStop ts; + + 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 css::uno::Sequence< css::style::TabStop >(); + } + + // copy tab stop information into a Sequence, which only contains one element. + 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 + } + + tabs[0] = ts; + return tabs; +} + +// 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() ) + { + OSL_FAIL( "+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. + { + SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pFoll->FindNextCnt( true )), + this ); + } + } + 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. + { + SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )), + this ); + } + } + + // 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 out 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->GetDelta() = 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; +} + +// We rewire the footnotes and the character bound objects +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 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->GetDelta() = 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)! + 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; + } + ChangeOffset( GetFollow(), nEnd ); + GetFollow()->ManipOfst( nEnd ); + } + else + { + // 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) || + (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule())) + ) + { + 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 sal_uInt16 nOldAscent = pOldCur->GetAscent(); + const sal_uInt16 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 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 + pPara->GetDelta() -= sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen); + + // 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. + if( rLine.GetStart() + rLine.GetLength() >= nStrLen && + rLine.GetCurr()->GetNext() ) + { + rLine.TruncLines(); + rLine.SetTruncLines( true ); + } + } + + if( !rInf.IsTest() ) + { + // 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() ) + { + 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 ); + 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 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() ) + { + 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->GetDelta() = 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..f42fef695 --- /dev/null +++ b/sw/source/core/text/frminf.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 <sal/config.h> + +#include <o3tl/safeint.hxx> + +#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 = pFrame->GetPara(); + if( !pLay ) + return false; + + // For follows false of course + if( 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 = pFrame->GetPara(); + if( !pLay ) + return false; + + long nWidth = pFrame->getFramePrintArea().Width(); + nWidth *= nPercent; + nWidth /= 100; + return o3tl::make_unsigned(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*>(pFrame) ); + SwTextCursor aLine( const_cast<SwTextFrame*>(pFrame), &aInf ); + return GetLineStart( aLine ) - pFrame->getFrameArea().Left() - pFrame->getFramePrintArea().Left(); +} + +// Calculates the character's position and returns the middle position +SwTwips SwTextFrameInfo::GetCharPos(TextFrameIndex const nChar, bool bCenter) const +{ + SwRectFnSet aRectFnSet(pFrame); + SwFrameSwapper aSwapper( pFrame, true ); + + SwTextSizeInfo aInf( const_cast<SwTextFrame*>(pFrame) ); + SwTextCursor aLine( const_cast<SwTextFrame*>(pFrame), &aInf ); + + SwTwips nStt, nNext; + SwRect aRect; + aLine.GetCharRect( &aRect, nChar ); + if ( aRectFnSet.IsVert() ) + pFrame->SwitchHorizontalToVertical( aRect ); + + nStt = aRectFnSet.GetLeft(aRect); + + if( !bCenter ) + return nStt - aRectFnSet.GetLeft(pFrame->getFrameArea()); + + aLine.GetCharRect( &aRect, nChar + TextFrameIndex(1) ); + if ( aRectFnSet.IsVert() ) + pFrame->SwitchHorizontalToVertical( aRect ); + + nNext = aRectFnSet.GetLeft(aRect); + + return (( nNext + nStt ) / 2 ) - aRectFnSet.GetLeft(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*>(pFrame) ); + SwTextMargin aLine( const_cast<SwTextFrame*>(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*>(pFrame) ); + SwTextMargin aLine( const_cast<SwTextFrame*>(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*>(pFrame) ); + SwTextCursor aLine( const_cast<SwTextFrame*>(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*>(pFrame) ); + SwTextCursor aLine( const_cast<SwTextFrame*>(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() - pFrame->getFrameArea().Left() - 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..ec3fd196d --- /dev/null +++ b/sw/source/core/text/frmpaint.cxx @@ -0,0 +1,714 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swmodule.hxx> +#include <tabfrm.hxx> +#include <numrule.hxx> +#include <wrong.hxx> + +#include <EnhancedPDFExportHelper.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 { return !( m_nLineNr % m_rLineInf.GetCountBy() ); } + bool HasDivider() const { + if( !m_nDivider ) return false; + return !(m_nLineNr % m_rLineInf.GetDividerCountBy()); + } + + void PaintExtra( SwTwips nY, long nAsc, long nMax, bool bRed ); + void PaintRedline( SwTwips nY, 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; + if( bLineNum ) + { + /* 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, pFrame->IsVertical() ); + 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 ) + { + 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, long nAsc, long nMax, bool bRed ) +{ + // Line number is stronger than the divider + const OUString aTmp( HasNumber() ? m_rLineInf.GetNumType().GetNumStr( m_nLineNr ) + : m_rLineInf.GetDivider() ); + + // Get script type of line numbering: + m_pFnt->SetActual( SwScriptInfo::WhichFont(0, aTmp) ); + + 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 ); + 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.IsInside( 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 ) + { + long nDiff = m_bGoLeft ? m_nRedX - m_nX : m_nX - m_nRedX; + if( nDiff > REDLINE_MINDIST ) + PaintRedline( nY, nMax ); + } +} + +void SwExtraPainter::PaintRedline( SwTwips nY, long nMax ) +{ + Point aStart( m_nRedX, nY ); + Point aEnd( m_nRedX, nY + nMax ); + + if( !IsClipChg() ) + { + SwRect aRct( aStart, aEnd ); + if( !m_aRect.IsInside( 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; + + 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; + } + } + + 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(); + } + if( bNoPrtLine ) + { + do + { + if( bNoDummy || !aLine.GetCurr()->IsDummy() ) + { + bool bRed = bRedLine && aLine.GetCurr()->HasRedline(); + if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() ) + { + if( bLineNum && + ( aExtra.HasNumber() || aExtra.HasDivider() ) ) + { + sal_uInt16 nTmpHeight, nTmpAscent; + aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + 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 (!GetMergedPara() && + 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(); + 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; + 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); + } + } + + 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, 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> pClip; + if( IsUndersized() ) + { + pClip.reset(new SwSaveClip( pSh->GetOut() )); + pClip->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 ) + { + const OUString aTmp( CH_PAR ); + SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), aTmp, 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 ); + + 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 ); + 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..0118f7479 --- /dev/null +++ b/sw/source/core/text/guess.cxx @@ -0,0 +1,591 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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 ) +{ + 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, nBreakWidth, nMaxSizeDiff ); + + if ( ( nBreakWidth <= nLineWidth ) || ( bUnbreakableNumberings && rPor.IsNumberPortion() ) ) + { + // portion fits to line + nCutPos = rInf.GetIdx() + nMaxLen; + if( nItalic && + (nCutPos >= TextFrameIndex(rInf.GetText().getLength()) || + // #i48035# Needed for CalcFitToContent + // if first line ends with a manual line break + rInf.GetText()[sal_Int32(nCutPos)] == CH_BREAK)) + nBreakWidth = nBreakWidth + nItalic; + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff ); + + 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 ) + { + nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() ); + + if ( !nHyphPos && rInf.GetIdx() ) + nHyphPos = rInf.GetIdx() - TextFrameIndex(1); + } + else + { + nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() ); + +#if OSL_DEBUG_LEVEL > 1 + if ( TextFrameIndex(COMPLETE_STRING) != nCutPos ) + { + sal_uInt16 nMinSize; + rInf.GetTextSize( &rSI, rInf.GetIdx(), nCutPos - rInf.GetIdx(), + nMaxComp, nMinSize, nMaxSizeDiff ); + OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" ); + } +#endif + } + + if( nCutPos > rInf.GetIdx() + nMaxLen ) + { + // second check if everything fits to line + nCutPos = nBreakPos = rInf.GetIdx() + nMaxLen - TextFrameIndex(1); + rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp, + nBreakWidth, nMaxSizeDiff ); + + // The following comparison should always give true, otherwise + // there likely has been a pixel rounding error in GetTextBreak + if ( nBreakWidth <= nLineWidth ) + { + if (nItalic && (nBreakPos + TextFrameIndex(1)) >= TextFrameIndex(rInf.GetText().getLength())) + nBreakWidth = nBreakWidth + nItalic; + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff ); + + 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() ) + { + nBreakPos = rInf.GetIdx(); + nCutPos = TextFrameIndex(-1); + return false; + } + + TextFrameIndex nPorLen(0); + // do not call the break iterator nCutPos is a blank + sal_Unicode cCutChar = nCutPos < TextFrameIndex(rInf.GetText().getLength()) + ? rInf.GetText()[sal_Int32(nCutPos)] + : 0; + if (IsBlank(cCutChar)) + { + nBreakPos = nCutPos; + TextFrameIndex nX = 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()) < nBreakPos && + IsBlank(rInf.GetChar(--nX))) + --nBreakPos; + } + else // #i20878# + { + while (nX && nBreakPos > rInf.GetLineStart() + TextFrameIndex(1) && + IsBlank(rInf.GetChar(--nX))) + --nBreakPos; + } + + if( nBreakPos > rInf.GetIdx() ) + nPorLen = nBreakPos - rInf.GetIdx(); + while (++nCutPos < TextFrameIndex(rInf.GetText().getLength()) && + IsBlank(rInf.GetChar(nCutPos))) + ; // nothing + + nBreakStart = 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() ) + { + nFieldDiff = TextFrameIndex(aText.getLength() - 1); + nCutPos = nCutPos + nFieldDiff; + nHyphPos = nHyphPos + 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() + 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 ( nCutPos && ! rPor.InFieldGrp() ) + { + const CharClass& rCC = GetAppCharClass(); + + // step back until a non-punctuation character is reached + TextFrameIndex nLangIndex = 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() - 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(nCutPos), aLocale, + sal_Int32(rInf.GetLineStart()), aHyphOpt, aUserOpt ); + + 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 ( nBreakPos < rInf.GetLineStart() && rInf.IsFirstMulti() && + ! rInf.IsFootnoteInside() ) + nBreakPos = rInf.GetLineStart(); + + nBreakStart = nBreakPos; + + bHyph = BreakType::HYPHENATION == aResult.breakType; + + if (bHyph && nBreakPos != TextFrameIndex(COMPLETE_STRING)) + { + // found hyphenation position within line + // nBreakPos is set to the hyphenation position + xHyphWord = aResult.rHyphenatedWord; + nBreakPos += TextFrameIndex(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 = + xHyphWord->getWord().indexOf( CHAR_SOFTHYPHEN ); + + if ( nSoftHyphPos >= 0 && + nBreakStart + TextFrameIndex(nSoftHyphPos) <= nBreakPos && + nBreakPos > rInf.GetLineStart() ) + nBreakPos = rInf.GetIdx() - TextFrameIndex(1); + } + + if( nBreakPos >= rInf.GetIdx() ) + { + nPorLen = nBreakPos - rInf.GetIdx(); + if ('-' == rInf.GetText()[ sal_Int32(nBreakPos) - 1 ]) + xHyphWord = nullptr; + } + } + else if ( !bHyph && nBreakPos >= rInf.GetLineStart() ) + { + OSL_ENSURE(sal_Int32(nBreakPos) != COMPLETE_STRING, "we should have found a break pos"); + + // found break position within line + xHyphWord = nullptr; + + // check, if break position is soft hyphen and an underflow + // has to be triggered + if( nBreakPos > rInf.GetLineStart() && rInf.GetIdx() && + CHAR_SOFTHYPHEN == rInf.GetText()[ sal_Int32(nBreakPos) - 1 ]) + { + 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 = 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 ) ) ) + nBreakPos = nX; + } + if( nBreakPos > rInf.GetIdx() ) + nPorLen = nBreakPos - rInf.GetIdx(); + } + else + { + // no line break found, setting nBreakPos to COMPLETE_STRING + // causes a break cut + nBreakPos = TextFrameIndex(COMPLETE_STRING); + OSL_ENSURE( nCutPos >= rInf.GetIdx(), "Deep cut" ); + nPorLen = nCutPos - rInf.GetIdx(); + } + + if (nBreakPos > nCutPos && nBreakPos != TextFrameIndex(COMPLETE_STRING)) + { + const TextFrameIndex nHangingLen = nBreakPos - nCutPos; + SwPosSize aTmpSize = rInf.GetTextSize( &rSI, nCutPos, nHangingLen ); + aTmpSize.Width(aTmpSize.Width() + nLeftRightBorderSpace); + OSL_ENSURE( !pHanging, "A hanging portion is hanging around" ); + pHanging.reset( new SwHangingPortion( aTmpSize ) ); + pHanging->SetLen( nHangingLen ); + nPorLen = 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 ( nBreakPos < rInf.GetIdx() ) + nBreakPos = nOldIdx - TextFrameIndex(1); + else if (TextFrameIndex(COMPLETE_STRING) != nBreakPos) + { + OSL_ENSURE( nBreakPos >= nFieldDiff, "I've got field trouble!" ); + nBreakPos = nBreakPos - nFieldDiff; + } + + OSL_ENSURE( nCutPos >= rInf.GetIdx() && nCutPos >= nFieldDiff, + "I've got field trouble, part2!" ); + nCutPos = nCutPos - nFieldDiff; + + OUString& rOldText = const_cast<OUString&> (rInf.GetText()); + OUString aReplacement( cFieldChr ); + rOldText = rOldText.replaceAt(sal_Int32(nOldIdx) - 1, sal_Int32(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, nBreakWidth, nMaxSizeDiff, + rInf.GetCachedVclData().get() ); + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff ); + + nBreakWidth += nItalic + nLeftRightBorderSpace; + } + else + nBreakWidth = 0; + + if( pHanging ) + { + nBreakPos = nCutPos; + // Keep following SwBreakPortion in the same line. + if ( CH_BREAK == rInf.GetChar( nBreakPos + 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 ); + nBreakStart = TextFrameIndex(aBound.startPos); + sal_Int32 nWordLen = aBound.endPos - sal_Int32(nBreakStart); + + // if everything else fails, we want to cut at nPos + nCutPos = nPos; + + OUString const aText( rInf.GetText().copy(sal_Int32(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 + xHyphWord = xHyph->queryAlternativeSpelling( aText, + g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), + sal::static_int_cast<sal_Int16>(sal_Int32(nPos - nBreakStart)), + rInf.GetHyphValues() ); + return xHyphWord.is() && 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..5ff7e7474 --- /dev/null +++ b/sw/source/core/text/guess.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_TEXT_GUESS_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_GUESS_HXX +#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 > xHyphWord; + std::unique_ptr<SwHangingPortion> pHanging; // for hanging punctuation + TextFrameIndex nCutPos; // this character doesn't fit + TextFrameIndex nBreakStart; // start index of word containing line break + TextFrameIndex nBreakPos; // start index of break position + TextFrameIndex nFieldDiff; // absolute positions can be wrong if we + // a field in the text has been expanded + sal_uInt16 nBreakWidth; // width of the broken portion +public: + SwTextGuess(): nCutPos(0), nBreakStart(0), + nBreakPos(0), nFieldDiff(0), 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 pHanging.get(); } + SwHangingPortion* ReleaseHangingPortion() { return pHanging.release(); } + sal_uInt16 BreakWidth() const { return nBreakWidth; } + TextFrameIndex CutPos() const { return nCutPos; } + TextFrameIndex BreakStart() const { return nBreakStart; } + TextFrameIndex BreakPos() const {return nBreakPos; } + TextFrameIndex FieldDiff() const {return nFieldDiff; } + const css::uno::Reference< css::linguistic2::XHyphenatedWord >& HyphWord() const + { return xHyphWord; } +}; + +#endif + +/* 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..b7a58b198 --- /dev/null +++ b/sw/source/core/text/inftxt.cxx @@ -0,0 +1,1991 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/brushitem.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 <paintfrm.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> + +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() + : pSpace( nullptr ), + nVertAlign( SvxParaVertAlignItem::Align::Automatic ), + nDefTabStop( 0 ), + bListTabStopIncluded( false ), + nListTabStopPosition( 0 ) +{ +} + +SwLineInfo::~SwLineInfo() +{ +} + +void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet, + const SwTextNode& rTextNode ) +{ + pRuler.reset( new SvxTabStopItem( rAttrSet.GetTabStops() ) ); + if ( rTextNode.GetListTabStopPosition( nListTabStopPosition ) ) + { + bListTabStopIncluded = true; + + // insert the list tab stop into SvxTabItem instance <pRuler> + const SvxTabStop aListTabStop( nListTabStopPosition, + SvxTabAdjust::Left ); + pRuler->Insert( aListTabStop ); + + // remove default tab stops, which are before the inserted list tab stop + for ( sal_uInt16 i = 0; i < pRuler->Count(); i++ ) + { + if ( (*pRuler)[i].GetTabPos() < nListTabStopPosition && + (*pRuler)[i].GetAdjustment() == SvxTabAdjust::Default ) + { + pRuler->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 < pRuler->Count(); i++ ) + { + if ( (*pRuler)[i].GetTabPos() == 0 && + (*pRuler)[i].GetAdjustment() == SvxTabAdjust::Default ) + { + pRuler->Remove(i); + break; + } + } + } + + pSpace = &rAttrSet.GetLineSpacing(); + nVertAlign = rAttrSet.GetParaVertAlign().GetValue(); + 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( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl ); + m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl ); + m_nDirection = DIR_RIGHT2LEFT; + } + else + { + m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong ); + m_pRef->SetLayoutMode( 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::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 = static_cast<sal_uInt16>(aDrawInf.GetKanaDiff()); + nMinSize = aSize.Width(); +} + +TextFrameIndex SwTextSizeInfo::GetTextBreak( const long nLineWidth, + const TextFrameIndex nMaxLen, + const sal_uInt16 nComp, + vcl::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 long nLineWidth, + const TextFrameIndex nMaxLen, + const sal_uInt16 nComp, + TextFrameIndex& rExtraCharPos, + vcl::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) ); + aTextFly.CtorInitTextFly( pFrame ); + aPaintRect = rPaint; + nSpaceIdx = 0; + pSpaceAdd = nullptr; + m_pWrongList = nullptr; + m_pGrammarCheckList = nullptr; + m_pSmartTags = nullptr; + 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() ) + , pSpaceAdd( rInf.GetpSpaceAdd() ), + pBrushItem( rInf.GetBrushItem() ), + aTextFly( rInf.GetTextFly() ), + aPos( rInf.GetPos() ), + aPaintRect( rInf.GetPaintRect() ), + nSpaceIdx( rInf.GetSpaceIdx() ) +{ } + +SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf ) + : SwTextSizeInfo( rInf ) + , m_pWrongList( rInf.GetpWrongList() ) + , m_pGrammarCheckList( rInf.GetGrammarCheckList() ) + , m_pSmartTags( rInf.GetSmartTags() ) + , pSpaceAdd( rInf.GetpSpaceAdd() ), + pBrushItem( rInf.GetBrushItem() ), + aTextFly( rInf.GetTextFly() ), + aPos( rInf.GetPos() ), + aPaintRect( rInf.GetPaintRect() ), + nSpaceIdx( rInf.GetSpaceIdx() ) +{ } + +SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint ) +{ + CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint ); +} + +/// Returns if the current background color is dark. +static bool lcl_IsDarkBackground( const SwTextPaintInfo& rInf ) +{ + const Color* pCol = rInf.GetFont()->GetBackColor(); + if( ! pCol || COL_TRANSPARENT == *pCol ) + { + const SvxBrushItem* pItem; + SwRect aOrigBackRect; + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + // Consider, that [GetBackgroundBrush(...)] can set <pCol> + // See implementation in /core/layout/paintfrm.cxx + // There is a background color, if there is a background brush and + // its color is *not* "no fill"/"auto fill". + if( rInf.GetTextFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false ) ) + { + if ( !pCol ) + pCol = &pItem->GetColor(); + + // Determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it. + if ( *pCol == COL_TRANSPARENT) + pCol = nullptr; + } + else + pCol = nullptr; + } + + if( !pCol ) + pCol = &aGlobalRetoucheColor; + + return pCol->IsDark(); +} + +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 = m_rPaintInf.GetFont()->GetColor().GetTransparency(); + const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); + aVCLGradient.SetStyle(GradientStyle::Linear); + aVCLGradient.SetStartColor(aTransColor); + aVCLGradient.SetEndColor(aTransColor); + aVCLGradient.SetAngle(0); + 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 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(aPos); + if( m_pFnt->GetLeftBorder() && !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()) ) + { + 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().GetTransparency() != 0) + { + 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() ) + { + 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, 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 sal_uInt16 nDir = s_aFnt.GetOrientation( rTextPaintInfo.GetTextFrame()->IsVertical() ); + SwTwips nMaxWidth; + if (nDir == 900 || nDir == 2700) + nMaxWidth = rRect.Height(); + else + { + assert(nDir == 0); //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 ) + { + 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( static_cast<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( 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() ) + { + 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() ) + { + 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; + + lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions ); + } + + 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() ) ) + { + case 0 : + aSize.setWidth( nPostItsWidth ); + aSize.setHeight( nFontHeight ); + aTmp.setX( aPos.X() ); + aTmp.setY( aPos.Y() - nFontAscent ); + break; + case 900 : + aSize.setHeight( nPostItsWidth ); + aSize.setWidth( nFontHeight ); + aTmp.setX( aPos.X() - nFontAscent ); + aTmp.setY( aPos.Y() ); + break; + case 2700 : + aSize.setHeight( nPostItsWidth ); + aSize.setWidth( nFontHeight ); + aTmp.setX( aPos.X() - nFontHeight + + nFontAscent ); + aTmp.setY( aPos.Y() ); + break; + } + + SwRect aTmpRect( aTmp, aSize ); + + if ( GetTextFrame()->IsRightToLeft() ) + GetTextFrame()->SwitchLTRtoRTL( aTmpRect ); + + if ( GetTextFrame()->IsVertical() ) + GetTextFrame()->SwitchHorizontalToVertical( aTmpRect ); + + const tools::Rectangle aRect( aTmpRect.SVRect() ); + SwViewOption::PaintPostIts( const_cast<OutputDevice*>(GetOut()), aRect, bScript ); + +} + +void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const +{ + SwRect aIntersect; + CalcRect( rPor, &aIntersect ); + if ( aIntersect.HasArea() ) + { + if (OnWin() && SwViewOption::IsFieldShadings() && + !GetOpt().IsPagePreview()) + { + OutputDevice* pOut = const_cast<OutputDevice*>(GetOut()); + pOut->Push( PushFlags::LINECOLOR | 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( PushFlags::LINECOLOR | 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 +{ + OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" ); + + SwRect aIntersect; + CalcRect( rPor, nullptr, &aIntersect, true ); + + if ( aIntersect.HasArea() ) + { + OutputDevice* pOut = const_cast<OutputDevice*>(GetOut()); + pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR ); + + // For dark background we do not want to have a filled rectangle + if ( GetVsh() && GetVsh()->GetWin() && lcl_IsDarkBackground( *this ) ) + { + pOut->SetLineColor( SwViewOption::GetFontColor() ); + } + 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( PushFlags::LINECOLOR | PushFlags::FILLCOLOR ); + pOutDev->SetFillColor( SwViewOption::GetFieldShadingsColor() ); + pOutDev->SetLineColor( ); + pOutDev->DrawRect( aIntersect.SVRect() ); + pOutDev->Pop(); + } + } + } + + SwRect aIntersect; + CalcRect( rPor, nullptr, &aIntersect, true ); + + if ( aIntersect.HasArea() ) + { + 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(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( PushFlags::LINECOLOR | 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 +{ + if( OnWin() && !IsMulti() ) + { + 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::ControlChar: + if ( !GetOpt().IsPagePreview() + && !GetOpt().IsReadonly() + && SwViewOption::IsFieldShadings() + && ( PortionType::Number != nWhich + || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615# + { + bDraw = true; + } + 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::Table: + 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 ); + } +} + +static void lcl_InitHyphValues( PropertyValues &rVals, + sal_Int16 nMinLeading, sal_Int16 nMinTrailing, bool bNoCapsHyphenation ) +{ + sal_Int32 nLen = rVals.getLength(); + + if (0 == nLen) // yet to be initialized? + { + rVals.realloc( 3 ); + 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; + } + else if (3 == nLen) // already initialized once? + { + PropertyValue *pVal = rVals.getArray(); + pVal[0].Value <<= nMinLeading; + pVal[1].Value <<= nMinTrailing; + pVal[2].Value <<= bNoCapsHyphenation; + } + else { + OSL_FAIL( "unexpected size of sequence" ); + } +} + +const PropertyValues & SwTextFormatInfo::GetHyphValues() const +{ + OSL_ENSURE( 3 == 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 bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation(); + lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing, bNoCapsHyphenation); + } + 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(); + if( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == 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", g_pBreakIt->GetLocale(eTmp).Language), + 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_ZWNBSP : + 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); + if (!bTabOverMargin) + 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(); + + 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 ) + { + 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 ) + { + 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) + { + 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 ) + { + 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 ) + { + 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..83c173e2d --- /dev/null +++ b/sw/source/core/text/inftxt.hxx @@ -0,0 +1,788 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_INFTXT_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_INFTXT_HXX +#include <memory> +#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> +#include <sal/log.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 + +#ifdef DBG_UTIL +#define OPTDBG( rInf ) (rInf).IsOptDbg() +#else +#define OPTDBG( rInf ) false +#endif + +// Respects the attribute LineSpace when calculating the Height/Ascent +class SwLineInfo +{ + friend class SwTextIter; + + std::unique_ptr<SvxTabStopItem> pRuler; + const SvxLineSpacingItem *pSpace; + SvxParaVertAlignItem::Align nVertAlign; + sal_uInt16 nDefTabStop; + bool bListTabStopIncluded; + long 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, + const SwTwips nRight ) const; + const SvxLineSpacingItem *GetLineSpacing() const { return pSpace; } + sal_uInt16 GetDefTabStop() const { return nDefTabStop; } + void SetDefTabStop( sal_uInt16 nNew ) const + { const_cast<SwLineInfo*>(this)->nDefTabStop = nNew; } + + // vertical alignment + SvxParaVertAlignItem::Align GetVertAlign() const { return nVertAlign; } + bool HasSpecialAlign( bool bVert ) const + { return bVert ? + ( SvxParaVertAlignItem::Align::Baseline != nVertAlign ) : + ( SvxParaVertAlignItem::Align::Baseline != nVertAlign && + SvxParaVertAlignItem::Align::Automatic != nVertAlign ); } + + sal_uInt16 NumberOfTabStops() const; + + bool IsListTabStopIncluded() const + { + return bListTabStopIncluded; + } + long GetListTabStopPosition() const + { + return 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<vcl::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::unique_ptr<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::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 long nLineWidth, + const TextFrameIndex nMaxLen, + const sal_uInt16 nComp, + vcl::TextLayoutCache const*) const; + TextFrameIndex GetTextBreak( const long nLineWidth, + const TextFrameIndex nMaxLen, + const sal_uInt16 nComp, + TextFrameIndex& rExtraCharPos, + vcl::TextLayoutCache const*) const; + + sal_uInt16 GetAscent() 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<vcl::TextLayoutCache>& GetCachedVclData() const + { + return m_pCachedVclData; + } + void SetCachedVclData(std::shared_ptr<vcl::TextLayoutCache> const& pCachedVclData) + { + m_pCachedVclData = pCachedVclData; + } +}; + +class SwTextPaintInfo : public SwTextSizeInfo +{ + sw::WrongListIterator *m_pWrongList; + sw::WrongListIterator *m_pGrammarCheckList; + sw::WrongListIterator *m_pSmartTags; + std::vector<long>* pSpaceAdd; + const SvxBrushItem *pBrushItem; // For the background + SwTextFly aTextFly; // Calculate the FlyFrame + Point aPos; // Paint position + SwRect aPaintRect; // Original paint rect (from Layout paint) + + sal_uInt16 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) + , pSpaceAdd(nullptr) + , pBrushItem(nullptr) + , 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 pBrushItem; } + + SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint ); + + SwTwips X() const { return aPos.X(); } + void X( const long nNew ) { aPos.setX(nNew); } + SwTwips Y() const { return aPos.Y(); } + void Y( const SwTwips nNew ) { aPos.setY(nNew); } + + SwTextFly& GetTextFly() { return aTextFly; } + const SwTextFly& GetTextFly() const { return 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; + void DrawViewOpt( const SwLinePortion &rPor, PortionType nWhich ) 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 aPos; } + void SetPos( const Point &rNew ) { aPos = rNew; } + + const SwRect &GetPaintRect() const { return aPaintRect; } + + // STUFF FOR JUSTIFIED ALIGNMENT + + sal_uInt16 GetSpaceIdx() const { return nSpaceIdx; } + void ResetSpaceIdx(){nSpaceIdx = 0; } + void SetSpaceIdx( sal_uInt16 nNew ) { nSpaceIdx = nNew; } + void IncSpaceIdx() { ++nSpaceIdx; } + void RemoveFirstSpaceAdd() { pSpaceAdd->erase( pSpaceAdd->begin() ); } + long GetSpaceAdd() const + { return ( pSpaceAdd && nSpaceIdx < pSpaceAdd->size() ) + ? (*pSpaceAdd)[nSpaceIdx] : 0; } + + void SetpSpaceAdd( std::vector<long>* pNew ){ pSpaceAdd = pNew; } + std::vector<long>* GetpSpaceAdd() const { return 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<vcl::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 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 ); +} + +#endif + +/* 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..64a53c3c4 --- /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, 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, 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 + { + ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode(); + rInf.GetOut()->SetLayoutMode ( nOldLayout | 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 long nGluePortionWidth, 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, nullptr, nIdx, nNext - nIdx ); + + 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 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 ) + { + 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 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; + 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 ); + + 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 ] = static_cast<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(); + long nDecompress = 0; + + while( pPos ) + { + if ( pPos->InTextGrp() ) + { + const sal_uInt16 nMinWidth = pPos->Width(); + + // 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 ); + pPos->Width( nMinWidth + + ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 ); + nDecompress += pPos->Width() - nMinWidth; + } + else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) + { + pPos->Width( static_cast<sal_uInt16>(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 ) +{ + 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 long nLeftMar = GetLeftMargin(); + SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight, + nRealWidth - nPrtWidth, nLineHeight ); + + SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect ); + while( pFly && 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( 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 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 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..1fadbf708 --- /dev/null +++ b/sw/source/core/text/itratr.cxx @@ -0,0 +1,1481 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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; + + 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 < pHints->Count()) && + ((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 < pHints->Count()) && + (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 < pHints->Count()) && + ((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] = 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: + { + 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: + { + 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> pOut; + SwViewShell const * pSh; + sal_uLong &rMin; + sal_uLong &rAbsMin; + long nRowWidth; + long nWordWidth; + long nWordAdd; + sal_Int32 nNoLineBreak; + SwMinMaxArgs( OutputDevice* pOutI, SwViewShell const * pShI, sal_uLong& rMinI, sal_uLong &rAbsI ) + : pOut( pOutI ), pSh( pShI ), rMin( rMinI ), rAbsMin( rAbsI ), nRowWidth(0), + nWordWidth(0), nWordAdd(0), nNoLineBreak(COMPLETE_STRING) + { } + void Minimum( long nNew ) const { if( static_cast<long>(rMin) < nNew ) rMin = nNew; } + void NewWord() { nWordAdd = 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.nNoLineBreak ) + rArg.NewWord(); + if( nStop == nIdx ) + ++nStop; + if( nStop > nEnd ) + nStop = nEnd; + + SwDrawTextInfo aDrawInf(rArg.pSh, *rArg.pOut, rText, nIdx, nStop - nIdx); + long nCurrentWidth = pFnt->GetTextSize_( aDrawInf ).Width(); + rArg.nRowWidth += nCurrentWidth; + if( bClear ) + rArg.NewWord(); + else + { + rArg.nWordWidth += nCurrentWidth; + if( static_cast<long>(rArg.rAbsMin) < rArg.nWordWidth ) + rArg.rAbsMin = rArg.nWordWidth; + rArg.Minimum( rArg.nWordWidth + rArg.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 nMaxWidth; // sum of all frame widths + long nMinWidth; // biggest frame + long nLeftRest; // space not already covered by frames in the left margin + long nRightRest; // space not already covered by frames in the right margin + long nLeftDiff; // Min/Max-difference of the frame in the left margin + long nRightDiff; // Min/Max-difference of the frame in the right margin + sal_uLong nIndx; // index of the node + void Minimum( long nNew ) { if( nNew > nMinWidth ) nMinWidth = nNew; } +}; + +} + +static void lcl_MinMaxNode( SwFrameFormat* pNd, SwMinMaxNodeArgs* pIn ) +{ + 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 && pIn, "Unexpected NULL arguments"); + if (!pPos || !pIn || pIn->nIndx != pPos->nNode.GetIndex()) + return; + + 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(); + sal_uLong 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(); + + 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() ) + { + pIn->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 ) + { + pIn->nRightRest -= pIn->nRightDiff; + pIn->nRightDiff = nDiff; + } + if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() ) + { + if( pIn->nRightRest > 0 ) + pIn->nRightRest = 0; + } + pIn->nRightRest -= nMin; + break; + } + case text::HoriOrientation::LEFT: + { + if( nDiff ) + { + pIn->nLeftRest -= pIn->nLeftDiff; + pIn->nLeftDiff = nDiff; + } + if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() && + pIn->nLeftRest < 0 ) + pIn->nLeftRest = 0; + pIn->nLeftRest -= nMin; + break; + } + default: + { + pIn->nMaxWidth += nMax; + pIn->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( sal_uLong 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(); + 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(); + 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.nMinWidth = 0; + aNodeArgs.nMaxWidth = 0; + aNodeArgs.nLeftRest = nLROffset; + aNodeArgs.nRightRest = rSpace.GetRight(); + aNodeArgs.nLeftDiff = 0; + aNodeArgs.nRightDiff = 0; + if( nIndex ) + { + SwFrameFormats* pTmp = const_cast<SwFrameFormats*>(GetDoc()->GetSpzFrameFormats()); + if( pTmp ) + { + aNodeArgs.nIndx = nIndex; + for( SwFrameFormat *pFormat : *pTmp ) + lcl_MinMaxNode( pFormat, &aNodeArgs ); + } + } + if( aNodeArgs.nLeftRest < 0 ) + aNodeArgs.Minimum( nLROffset - aNodeArgs.nLeftRest ); + aNodeArgs.nLeftRest -= aNodeArgs.nLeftDiff; + if( aNodeArgs.nLeftRest < 0 ) + aNodeArgs.nMaxWidth -= aNodeArgs.nLeftRest; + + if( aNodeArgs.nRightRest < 0 ) + aNodeArgs.Minimum( rSpace.GetRight() - aNodeArgs.nRightRest ); + aNodeArgs.nRightRest -= aNodeArgs.nRightDiff; + if( aNodeArgs.nRightRest < 0 ) + aNodeArgs.nMaxWidth -= aNodeArgs.nRightRest; + + SwScriptInfo aScriptInfo; + SwAttrIter aIter( *const_cast<SwTextNode*>(this), aScriptInfo ); + TextFrameIndex nIdx(0); + aIter.SeekAndChgAttrIter( nIdx, pOut ); + TextFrameIndex nLen(m_Text.getLength()); + long nCurrentWidth = 0; + 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<long>(rMax) < aArg.nRowWidth ) + rMax = aArg.nRowWidth; + aArg.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.nWordWidth += nCurrentWidth; + aArg.nRowWidth += nCurrentWidth; + if( static_cast<long>(rAbsMin) < aArg.nWordWidth ) + rAbsMin = aArg.nWordWidth; + aArg.Minimum( aArg.nWordWidth + aArg.nWordAdd ); + aArg.nNoLineBreak = sal_Int32(nIdx++); + } + break; + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + { + if( !pHint ) + break; + long nOldWidth = aArg.nWordWidth; + long nOldAdd = aArg.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.nWordAdd = nOldWidth + nOldAdd; + aArg.nWordWidth = nCurrentWidth; + aArg.nRowWidth += nCurrentWidth; + if( static_cast<long>(rAbsMin) < aArg.nWordWidth ) + rAbsMin = aArg.nWordWidth; + aArg.Minimum( aArg.nWordWidth + aArg.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.nWordWidth = nOldWidth; + aArg.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<long>(rMax) < aArg.nRowWidth ) + rMax = aArg.nRowWidth; + + nLROffset += rSpace.GetRight(); + + rAbsMin += nLROffset; + rAbsMin += nAdd; + rMin += nLROffset; + rMin += nAdd; + if( static_cast<long>(rMin) < aNodeArgs.nMinWidth ) + rMin = aNodeArgs.nMinWidth; + if( static_cast<long>(rAbsMin) < aNodeArgs.nMinWidth ) + rAbsMin = aNodeArgs.nMinWidth; + rMax += aNodeArgs.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 + OUString sTmp( CH_BLANK ); + SwDrawTextInfo aDrawInf(pSh, *pOut, sTmp, 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 static_cast<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 static_cast<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..78130e5de --- /dev/null +++ b/sw/source/core/text/itratr.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_TEXT_ITRATR_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_ITRATR_HXX +#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; } +}; + +#endif + +/* 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..cb830f327 --- /dev/null +++ b/sw/source/core/text/itrcrsr.cxx @@ -0,0 +1,1913 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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::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( static_cast<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 + nLeft = 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 + nLeft = m_pFrame->getFrameArea().Left() + + m_pFrame->getFramePrintArea().Left() + + nLMWithNum - + pNode->GetLeftMarginWithNum() - + // #i95907# + // #i111284# + ( bListLevelIndentsApplicableAndLabelAlignmentActive + ? 0 + : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) ); + } + else + { + nLeft = m_pFrame->getFrameArea().Left() + + std::max( long( rSpace.GetTextLeft() + nLMWithNum ), + m_pFrame->getFramePrintArea().Left() ); + } + } + + nRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width(); + + if( nLeft >= nRight && + // #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) ) ) ) + { + nLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left(); + if( nLeft >= nRight ) // e.g. with large paragraph indentations in slim table columns + nRight = nLeft + 1; // einen goennen wir uns immer + } + + if( m_pFrame->IsFollow() && m_pFrame->GetOffset() ) + nFirst = nLeft; + else + { + short nFLOfst = 0; + 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; + + 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: + { + 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() && + nLeft == 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 + nLeft -= nFirstLineOfs; + } + + nFirst = nLeft + nFirstLineOfs; + } + else + { + nFirst = 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 + nFirst += m_pFrame->GetAdditionalFirstLineOffset(); + + if( nFirst >= nRight ) + nFirst = nRight - 1; + } + const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust(); + nAdjust = rAdjust.GetAdjust(); + + // left is left and right is right + if ( m_pFrame->IsRightToLeft() ) + { + if ( SvxAdjust::Left == nAdjust ) + nAdjust = SvxAdjust::Right; + else if ( SvxAdjust::Right == nAdjust ) + nAdjust = 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() +{ + nDropLeft = nDropLines = nDropHeight = nDropDescent = 0; + const SwParaPortion *pPara = GetInfo().GetParaPortion(); + if( pPara ) + { + const SwDropPortion *pPorDrop = pPara->FindDropPortion(); + if ( pPorDrop ) + { + nDropLeft = pPorDrop->GetDropLeft(); + nDropLines = pPorDrop->GetLines(); + nDropHeight = pPorDrop->GetDropHeight(); + nDropDescent = 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 long nMax ) +{ + // 1170: Ambiguity of document positions + 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 ); + 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(); + + long nX = 0; + long nLast = 0; + SwLinePortion *pPor = m_pCurr->GetFirstPortion(); + + sal_uInt16 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() ); + sal_uInt16 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 + { + sal_uInt16 nPorHeight = nTmpHeight; + sal_uInt16 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; + 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 ); + 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 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 bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN); + // 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 ) + { + 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 ); + } + } + 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, sal_uInt16 nWidth30, sal_uInt16 nX) +{ + if (!pPor->GetNextPortion()) + { + return false; + } + + // 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())) + { + return false; + } + + return !pPor->IsBreakPortion(); +} + +// 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; + + sal_uInt16 nX = sal_uInt16( 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; + 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. + + sal_uInt16 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; + } + } + } + + sal_uInt16 nWidth30; + if ( pPor->IsPostItsPortion() ) + nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2; + 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 = 30 + pPor->GetViewWidth( GetInfo() ) / 2; + 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) + { + 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() ) ) + ++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() ) + { + if (pPor->IsPostItsPortion()) + { + // 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; + 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 = static_cast<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!" ); + + SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(), + *aSizeInf.GetOut(), + &pPara->GetScriptInfo(), + aSizeInf.GetText(), + aSizeInf.GetIdx(), + pPor->GetLen() ); + + // 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(0, nX - nSumBorderWidth); + } + // Shift the offset with the left border width + else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() ) + { + nX = std::max(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(); + bool bChgNodeInner = pLower + && (pLower->IsTextFrame() || pLower->IsLayoutFrame()); + Point aTmpPoint( rPoint ); + + if ( m_pFrame->IsRightToLeft() ) + m_pFrame->SwitchLTRtoRTL( aTmpPoint ); + + if ( m_pFrame->IsVertical() ) + m_pFrame->SwitchHorizontalToVertical( aTmpPoint ); + + if( bChgNodeInner && pTmp->getFrameArea().IsInside( 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.IsOver( 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) + { + if( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr ) + continue; + const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pAnchoredObj); + 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..8d0cda953 --- /dev/null +++ b/sw/source/core/text/itrform2.cxx @@ -0,0 +1,2889 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <vector> + +using namespace ::com::sun::star; + +namespace { + //! Calculates and sets optimal repaint offset for the current line + long lcl_CalcOptRepaint( SwTextFormatter &rThis, + SwLineLayout const &rCurr, + TextFrameIndex nOldLineEnd, + const std::vector<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 ); +} + +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( static_cast<sal_uInt16>(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 (pNext == m_pFirstOfBorderMerge) + { + m_pFirstOfBorderMerge = nullptr; + 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() ); + if( m_pCurr->GetAscent() < pPor->GetAscent() ) + m_pCurr->SetAscent( pPor->GetAscent() ); + + 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 inspite 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); + } + + SwLinePortion *pPor = NewPortion( rInf ); + + // 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 = static_cast<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 = static_cast<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 && pGrid->IsSnapToChars() && ! 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 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( static_cast<sal_uInt16>(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 sal_uInt16 nDist = static_cast<sal_uInt16>(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 && pGrid->IsSnapToChars() && 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 sal_uInt16 nKernWidth_1 = static_cast<sal_uInt16>(nKernWidth / 2); + + OSL_ENSURE( nKernWidth <= nRestWidth, + "Not enough space left for adjusting non-asian text in grid mode" ); + + pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 ); + rInf.X( rInf.X() + nKernWidth_1 ); + + if ( ! bFull ) + 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 ); + } + + 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() ); + 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->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 +{ +public: + SwMetaPortion() { SetWhichPor( PortionType::Meta ); } + virtual void Paint( const SwTextPaintInfo &rInf ) const override; +}; + +} + +void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const +{ + if ( Width() ) + { + rInf.DrawViewOpt( *this, PortionType::Meta ); + SwTextPortion::Paint( rInf ); + } +} + +namespace sw::mark { + OUString ExpandFieldmark(IFieldmark* pBM) + { + 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 const sal_Unicode vEnSpaces[ODF_FORMFIELD_DEFAULT_LENGTH] = {8194, 8194, 8194, 8194, 8194}; + return OUString(vEnSpaces, ODF_FORMFIELD_DEFAULT_LENGTH); + } +} + +SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const +{ + SwTextPortion *pPor = nullptr; + if( GetFnt()->IsTox() ) + { + pPor = new SwToxPortion; + } + else if ( GetFnt()->IsInputField() ) + { + pPor = new SwTextInputFieldPortion(); + } + else + { + if( GetFnt()->IsRef() ) + pPor = new SwRefPortion; + else if (GetFnt()->IsMeta()) + { + pPor = new SwMetaPortion; + } + 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 ) +{ + // 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::unique_ptr<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: + pPor = new SwBreakPortion( *rInf.GetLast() ); 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_ZWNBSP : // 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 = static_cast<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(); + + const SfxPoolItem* pItem; + sal_uInt16 nDir = 0; + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_ROTATE, + true, &pItem )) + nDir = static_cast<const SvxCharRotateItem*>(pItem)->GetValue(); + + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion(rInf.GetIdx() + TextFrameIndex(1), + 900 == nDir + ? DIR_BOTTOM2TOP + : DIR_TOP2BOTTOM ); + } + } + } + else if ( pPor->InNumberGrp() ) + { + const SwFont* pNumFnt = static_cast<SwFieldPortion*>(pPor)->GetFont(); + + if ( pNumFnt ) + { + sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTextFrame()->IsVertical() ); + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion(TextFrameIndex(0), 900 == 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() ); + 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 sal_uInt16 nOldHeight = m_pCurr->Height(); + const sal_uInt16 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<long> flyStarts; + + // these are the conditions for a fly position comparison + if ( bOptimizeRepaint && m_pCurr->IsFly() ) + { + SwLinePortion* pPor = m_pCurr->GetFirstPortion(); + 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 ); + 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()))) + { + sal_uInt16 nTmpAscent, nTmpHeight; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + AlignFlyInCntBase( Y() + 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 ) +{ + sal_uInt16 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 ); + 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 = static_cast<sal_uInt16>(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)) + { + 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 = static_cast<sal_uInt16>(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 ); + 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 ); + 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: + { + long nTmp = pSpace->GetPropLineSpace(); + // 50% is the minimum, if 0% we switch to the + // default value 100% ... + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + + nTmp *= nLineHeight; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + nLineHeight = static_cast<sal_uInt16>(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" ); + + long 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 ); + + long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; + pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); + + const sal_uInt16 nTmpHeight = pCurrent->GetRealHeight(); + sal_uInt16 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( 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; + } + + long 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.IsOver( 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.IsOver( 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(); + + long nAscent; + long nTop = Y(); + 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 long nLeftMar = GetLeftMargin(); + const 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 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.IsOver( 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 ); + + 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( static_cast<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( sal_uInt16(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. + 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( sal_uInt16( nH ) ); + } + if( nAscent < pFly->Height() ) + pFly->SetAscent( sal_uInt16(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( sal_uInt16(aInter.Height()) ); + if( nAscent < pFly->Height() ) + pFly->SetAscent( sal_uInt16(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 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 sal_uLong i = nTmpWidth / nGridWidth + 1; + + const long nNewWidth = ( i - 1 ) * nGridWidth - nOfst; + if ( nNewWidth > 0 ) + rInf.Width( static_cast<sal_uInt16>(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() ) + 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 + + long 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. + sal_uInt16 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 = static_cast<sal_uInt16>( 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 ) + { + 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() ) + { + 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 + long lcl_CalcOptRepaint( SwTextFormatter &rThis, + SwLineLayout const &rCurr, + TextFrameIndex const nOldLineEnd, + const std::vector<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 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 + long nPOfst = 0; + size_t nCnt = 0; + 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..c9a14f566 --- /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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_ITRFORM2_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_ITRFORM2_HXX +#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 ); + 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( 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 ); +}; + +#endif + +/* 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..28eb142d6 --- /dev/null +++ b/sw/source/core/text/itrpaint.cxx @@ -0,0 +1,693 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 ); + 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() ) + { + 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( 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( !bPaintDrop ) + { + // 8084: Optimization, less painting + // AMA: By 8084 7538 has been revived + // bDrawInWindow removed, so that DropCaps also can be printed + bPaintDrop = pPor == m_pCurr->GetFirstPortion() + && GetDropLines() >= GetLineNr(); + } + + sal_uInt16 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 ); + 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; + } + + // 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())) + { + const SwTmpEndPortion aEnd( *pEndTempl ); + 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, + 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) + { + const SfxPoolItem* pItem; + if (SfxItemState::SET == e.pNode->GetSwAttrSet().GetItemState( + RES_CHRATR_UNDERLINE, true, &pItem)) + { + const bool bUnderSelect(m_pFont->GetUnderline() == + static_cast<SvxUnderlineItem const*>(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..2256c3928 --- /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 bPaintDrop; + + SwLinePortion *CalcPaintOfst( const SwRect &rPaint ); + void CheckSpecialUnderline( const SwLinePortion* pPor, + long nAdjustBaseLine = 0 ); +protected: + void CtorInitTextPainter( SwTextFrame *pFrame, SwTextPaintInfo *pInf ); + explicit SwTextPainter(SwTextNode const * pTextNode) + : SwTextCursor(pTextNode) + , 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 ) { bPaintDrop = bNew; } + bool IsPaintDrop() const { return 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..b285307a8 --- /dev/null +++ b/sw/source/core/text/itrtxt.cxx @@ -0,0 +1,421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "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( sal_uInt16 &rAscent, sal_uInt16 &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(); + return m_pPrev = pLay; +} + +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; + return m_pCurr = m_pCurr->GetNext(); + } + 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 ) + bRightMargin = false; + bool bPrevious = bRightMargin && m_pCurr->GetLen() && GetPrev() && + GetPrev()->GetLen(); + if (bPrevious && nPosition && CH_BREAK == GetInfo().GetChar(nPosition - TextFrameIndex(1))) + bPrevious = false; + return bPrevious ? PrevLine() : m_pCurr; +} + +sal_uInt16 SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine, + const SwLinePortion* pPor, + sal_uInt16 nPorHeight, sal_uInt16 nPorAscent, + const bool bAutoToCentered ) const +{ + if ( pPor ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + + sal_uInt16 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 + nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent; + break; + } + [[fallthrough]]; + case SvxParaVertAlignItem::Align::Baseline : + // base line + 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..81e67503d --- /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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_ITRTXT_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_ITRTXT_HXX +#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 ); + + sal_uInt16 GetLineHeight() const { return m_pCurr->GetRealHeight(); } + void CalcAscentAndHeight( sal_uInt16 &rAscent, sal_uInt16 &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 nLeft; + SwTwips nRight; + SwTwips nFirst; + sal_uInt16 nDropLeft; + sal_uInt16 nDropHeight; + sal_uInt16 nDropDescent; + sal_uInt16 nDropLines; + SvxAdjust nAdjust; + // #i91133# + SwTwips mnTabLeft; + +protected: + // For FormatQuoVadis + void Right( const SwTwips nNew ) { nRight = nNew; } + + void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf ); + explicit SwTextMargin(SwTextNode const * pTextNode) + : SwTextIter(pTextNode) + , nLeft(0) + , nRight(0) + , nFirst(0) + , nDropLeft(0) + , nDropHeight(0) + , nDropDescent(0) + , nDropLines(0) + , nAdjust(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 nRight; } + SwTwips FirstLeft() const { return nFirst; } + 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 nAdjust; } + sal_uInt16 GetLineWidth() const + { return sal_uInt16( Right() - GetLeftMargin() + 1 ); } + SwTwips GetLeftMin() const { return std::min(nFirst, nLeft); } + bool HasNegFirst() const { return nFirst < nLeft; } + + // #i91133# + SwTwips GetTabLeft() const + { + return mnTabLeft; + } + // DropCaps + sal_uInt16 GetDropLines() const { return nDropLines; } + void SetDropLines( const sal_uInt16 nNew ) { nDropLines = nNew; } + sal_uInt16 GetDropLeft() const { return nDropLeft; } + sal_uInt16 GetDropHeight() const { return nDropHeight; } + void SetDropHeight( const sal_uInt16 nNew ) { nDropHeight = nNew; } + sal_uInt16 GetDropDescent() const { return nDropDescent; } + void SetDropDescent( const sal_uInt16 nNew ) { nDropDescent = 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 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 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 long nMax = 0 ); + void GetEndCharRect(SwRect *, TextFrameIndex, SwCursorMoveState* = nullptr, + const 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 + sal_uInt16 AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor, + sal_uInt16 nPorHeight = 0, sal_uInt16 nAscent = 0, + const bool bAutoToCentered = false ) const; + + static void SetRightMargin( const bool bNew ){ bRightMargin = bNew; } + static bool IsRightMargin() { return 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() ? nFirst : Left(); +} + +inline SwTwips SwTextMargin::Left() const +{ + return (nDropLines >= m_nLineNr && 1 != m_nLineNr) ? nFirst + nDropLeft : nLeft; +} + +#endif + +/* 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..7e1f91493 --- /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..c8948197f --- /dev/null +++ b/sw/source/core/text/pordrop.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_PORDROP_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORDROP_HXX + +#include "portxt.hxx" + +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> pFollow; + std::unique_ptr<SwFont> pFnt; + TextFrameIndex nLen; + sal_uInt16 nWidth; + bool m_bJoinBorderWithNext; + bool m_bJoinBorderWithPrev; + +public: + SwDropPortionPart( SwFont& rFont, const TextFrameIndex nL ) + : pFnt( &rFont ), nLen( nL ), nWidth( 0 ), m_bJoinBorderWithNext(false), m_bJoinBorderWithPrev(false) {}; + ~SwDropPortionPart(); + + SwDropPortionPart* GetFollow() const { return pFollow.get(); }; + void SetFollow( std::unique_ptr<SwDropPortionPart> pNew ) { pFollow = std::move(pNew); }; + SwFont& GetFont() const { return *pFnt; } + TextFrameIndex GetLen() const { return nLen; } + sal_uInt16 GetWidth() const { return nWidth; } + void SetWidth( sal_uInt16 nNew ) { 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; } +}; + +class SwDropPortion : public SwTextPortion +{ + friend class SwDropCapCache; + std::unique_ptr<SwDropPortionPart> pPart; // due to script/attribute changes + sal_uInt16 nLines; // Line count + sal_uInt16 nDropHeight; // Height + sal_uInt16 nDropDescent; // Distance to the next line + sal_uInt16 nDistance; // Distance to the text + sal_uInt16 nFix; // Fixed position + short 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 nLines; } + sal_uInt16 GetDistance() const { return nDistance; } + sal_uInt16 GetDropHeight() const { return nDropHeight; } + sal_uInt16 GetDropDescent() const { return nDropDescent; } + sal_uInt16 GetDropLeft() const { return Width() + nFix; } + + SwDropPortionPart* GetPart() const { return pPart.get(); } + void SetPart( std::unique_ptr<SwDropPortionPart> pNew ) { pPart = std::move(pNew); } + + void SetY( short nNew ) { nY = nNew; } + + SwFont* GetFnt() const { return pPart ? &pPart->GetFont() : nullptr; } + + static void DeleteDropCapCache(); +}; + +#endif + +/* 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..659b5532d --- /dev/null +++ b/sw/source/core/text/porexp.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 <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() ); +} + +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() - nLineLength, true ); + if( nMay ) + { + 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() ); +} + +SwPostItsPortion::SwPostItsPortion( bool bScrpt ) + : bScript( bScrpt ) +{ + nLineLength = 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..418e6ff3c --- /dev/null +++ b/sw/source/core/text/porexp.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_TEXT_POREXP_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_POREXP_HXX + +#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; +}; + +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; +}; + +class SwPostItsPortion : public SwExpandPortion +{ + bool 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 bScript; } +}; + +#endif + +/* 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..fb414dc87 --- /dev/null +++ b/sw/source/core/text/porfld.cxx @@ -0,0 +1,1350 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <bookmrk.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), ""); + m_bFollow = true; +} + +SwFieldPortion::SwFieldPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold ) + : m_aExpand(rExpand), m_pFont(std::move(pFont)), m_nNextOffset(0), m_nNextScriptChg(COMPLETE_STRING), 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_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<vcl::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 ) + { + 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 if (nIdx < TextFrameIndex(pOldText->getLength())) + { + aText = (*pOldText).replaceAt(sal_Int32(nIdx), 1, aText); + } + 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() - TextFrameIndex(1) : 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! + TextFrameIndex nRest; + bool bFull = false; + bool bEOL = false; + TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx(); + { + 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() ? 0 : 1); + + // 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_ZWNBSP : + 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() ) ) + { + // 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() ); +} + +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) ), + nFixWidth(0), + 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(), + 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!!! + nFixWidth = rInf.IsMulti() ? Height() : Width(); + rInf.SetNumDone( !rInf.GetRest() ); + if( rInf.IsNumDone() ) + { +// SetAscent( rInf.GetAscent() ); + OSL_ENSURE( Height() && nAscent, "NumberPortions without Height | Ascent" ); + + 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 < nFixWidth + nMinDist ) + nDiff = nFixWidth + 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( sal_uInt16( nDiff ) ); + } + else if( Width() < nDiff ) + Width( sal_uInt16(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)->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() ) + { + 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( nFixWidth == Width() && ! HasFollow() ) + SwExpandPortion::Paint( rInf ); + else + { + // logical const: reset width + SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this); + bPaintSpace = bPaintSpace && nFixWidth < nOldWidth; + sal_uInt16 nSpaceOffs = nFixWidth; + pThis->Width( nFixWidth ); + + if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) || + ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) ) + SwExpandPortion::Paint( rInf ); + else + { + SwTextPaintInfo aInf( rInf ); + if( nOffset < nMinDist ) + nOffset = 0; + else + { + if( IsCenter() ) + { + /* #110778# a / 2 * 2 == a is not a tautology */ + sal_uInt16 nTmpOffset = nOffset; + nOffset /= 2; + if( nOffset < nMinDist ) + nOffset = nTmpOffset - nMinDist; + } + else + nOffset = nOffset - 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_Unicode cBullet, + const OUString& rBulletFollowedBy, + std::unique_ptr<SwFont> pFont, + const bool bLft, + const bool bCntr, + const sal_uInt16 nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ) + : SwNumberPortion( OUStringChar(cBullet) + 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 ), + pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 ) +{ + SetWhichPor( PortionType::GrfNum ); + SetAnimated( false ); + m_bReplace = false; + if( pGrfBrush ) + { + pBrush.reset(pGrfBrush->Clone()); + const Graphic* pGraph = pGrfBrush->GetGraphic(referer); + if( pGraph ) + SetAnimated( pGraph->IsAnimated() ); + else + m_bReplace = true; + } + if( pGrfOrient ) + { + nYPos = pGrfOrient->GetPos(); + eOrient = pGrfOrient->GetVertOrient(); + } + else + { + nYPos = 0; + eOrient = text::VertOrientation::TOP; + } + Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) ); + nFixWidth = Width(); + nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE; + Height( sal_uInt16(nGrfHeight) ); + m_bNoPaint = false; +} + +SwGrfNumPortion::~SwGrfNumPortion() +{ + if ( IsAnimated() ) + { + Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic()); + if (pGraph) + pGraph->StopAnimation( nullptr, nId ); + } + pBrush.reset(); +} + +void SwGrfNumPortion::StopAnimation( OutputDevice* pOut ) +{ + if ( IsAnimated() ) + { + Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic()); + if (pGraph) + pGraph->StopAnimation( pOut, 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( 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() - static_cast<sal_uInt16>(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(); + 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 < nFixWidth + nMinDist ) + nDiff = nFixWidth + 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( sal_uInt16(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 ); + long nTmpWidth = std::max( long(0), static_cast<long>(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( nFixWidth < Width() && !bTmpLeft ) + { + sal_uInt16 nOffset = Width() - nFixWidth; + if( nOffset < nMinDist ) + nOffset = 0; + else + { + if( IsCenter() ) + { + nOffset /= 2; + if( nOffset < nMinDist ) + nOffset = Width() - nFixWidth - nMinDist; + } + else + nOffset = nOffset - nMinDist; + } + aPos.AdjustX(nOffset ); + } + + if( m_bReplace ) + { + const 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( !nId ) + { + SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) ); + rInf.GetTextFrame()->SetAnimation(); + } + if( aTmp.IsOver( 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*>(pBrush->GetGraphic()); + if (pGraph) + pGraph->StopAnimation(nullptr,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*>(pBrush->GetGraphic()); + if (pGraph) + { + pGraph->StartAnimation( + const_cast<OutputDevice*>(rInf.GetOut()), aPos, aSize, nId ); + } + } + + // pdf export, printing, preview, stop animations... + else + bDraw = true; + } + if( bDraw ) + { + + Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic()); + if (pGraph) + pGraph->StopAnimation( nullptr, 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() ) + { + DrawGraphic( pBrush.get(), const_cast<OutputDevice*>(rInf.GetOut()), + aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES ); + } +} + +void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent, + long nFlyAsc, long nFlyDesc ) +{ + if ( GetOrient() != text::VertOrientation::NONE ) + { + 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( OutputDevice* pOut ) +{ + OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" ); + if( HasPara() ) + { + 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 ) + , nUpPos(0) + , nLowPos(0) + , 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; + } + 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( nProportion ); // a smaller font + SwFontSave aFontSave( rInf, &aTmpFont ); + + Point aOldPos = rInf.GetPos(); + Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row + for( sal_Int32 i = 0 ; i < nCount; ++i ) + { + if( i == nTop ) // change the row + aOutPos.setY( aOldPos.Y() + nLowPos ); // Y of the second row + aOutPos.setX( aOldPos.X() + aPos[i] ); // X position + const SwFontScript nAct = 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( aWidth[ nAct ] ) + { + Size aTmpSz = aTmpFont.GetSize( nAct ); + if( aTmpSz.Width() != aWidth[ nAct ] ) + { + aTmpSz.setWidth( 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 == aScrType[i]; ++i ) + 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( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" ); + if( !aWidth[ aScrType[i] ] ) + { + rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) ); + aWidth[ aScrType[i] ] = + static_cast<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 ); + 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 + { + nProportion -= 5; + aTmpFont.SetProportion( nProportion ); + memset( &aPos, 0, sizeof(aPos) ); + nMaxDescent = 0; + nMaxAscent = 0; + nMaxWidth = 0; + nUpPos = 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 = aScrType[i]; + aTmpFont.SetActual( nScrp ); + if( aWidth[ nScrp ] ) + { + Size aFontSize( aTmpFont.GetSize( nScrp ) ); + aFontSize.setWidth( 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() ); + aPos[ i ] = static_cast<sal_uInt16>(aSize.Width()); + if( i == nTop ) // enter the second line + { + nLowPos = nMaxDescent; + Height( nMaxDescent + nMaxAscent ); + Width( nMaxWidth ); + SetAscent( nMaxAscent ); + nMaxAscent = 0; + nMaxDescent = 0; + nMaxWidth = 0; + } + nMaxWidth = nMaxWidth + aPos[ i ]; + if( nAsc > nMaxAscent ) + nMaxAscent = nAsc; + if( aSize.Height() - nAsc > nMaxDescent ) + nMaxDescent = static_cast<sal_uInt16>(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 ); + nLowPos = nMaxDescent; + } + } + Height( Height() + nMaxDescent + nMaxAscent ); + nUpPos = nMaxAscent; + SetAscent( Height() - nMaxDescent - nLowPos ); + } while( 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: aPos[1] = aPos[0] + nTopDiff; + [[fallthrough]]; + case 2: aPos[nTop-1] = Width() - aPos[nTop-1]; + } + aPos[0] = 0; + switch( nCount ) + { + case 5: aPos[4] = aPos[3] + nBotDiff; + [[fallthrough]]; + case 3: aPos[nTop] = nBotDiff; break; + case 6: aPos[4] = aPos[3] + nBotDiff; + [[fallthrough]]; + case 4: aPos[nTop] = 0; + [[fallthrough]]; + case 2: aPos[nCount-1] = Width() - 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( static_cast<sal_uInt16>( 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..ec70c66d2 --- /dev/null +++ b/sw/source/core/text/porfld.hxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_PORFLD_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORFLD_HXX + +#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; + 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) + + 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 ); + 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; } + + // 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; +}; + +/** + * 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 nFixWidth; // See Glues + sal_uInt16 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_Unicode cCh, + const OUString& 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> pBrush; + long nId; // For StopAnimation + SwTwips nYPos; // _Always_ contains the current RelPos + SwTwips nGrfHeight; + sal_Int16 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( long nLnAscent, long nLnDescent, + long nFlyAscent, long nFlyDescent ); + + void StopAnimation( OutputDevice* pOut ); + + bool IsAnimated() const { return m_bAnimated; } + void SetAnimated( bool bNew ) { m_bAnimated = bNew; } + void SetRelPos( SwTwips nNew ) { nYPos = nNew; } + void SetId( long nNew ) const + { const_cast<SwGrfNumPortion*>(this)->nId = nNew; } + SwTwips GetRelPos() const { return nYPos; } + SwTwips GetGrfHeight() const { return nGrfHeight; } + sal_Int16 GetOrient() const { return 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 aPos[6]; // up to six X positions + o3tl::enumarray<SwFontScript,sal_uInt16> aWidth = {}; // one width for every scripttype + SwFontScript aScrType[6]; // scripttype of every character + sal_uInt16 nUpPos; // the Y position of the upper baseline + sal_uInt16 nLowPos; // the Y position of the lower baseline + sal_uInt8 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; +}; + +#endif + +/* 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..d785a9f3c --- /dev/null +++ b/sw/source/core/text/porfly.cxx @@ -0,0 +1,405 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/log.hxx> +#include <fmtanchr.hxx> +#include <fmtflcnt.hxx> +#include <fmtornt.hxx> +#include <flyfrms.hxx> +#include <txatbase.hxx> +#include "porfly.hxx" +#include "porlay.hxx" +#include "inftxt.hxx" + +#include <sortedobjs.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( static_cast<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 = static_cast<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 = nullptr; + if ( nullptr != (pObjs = GetDrawObjs()) ) + { + 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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + RemoveFly( static_cast<SwFlyFrame*>(pAnchoredObj) ); + pNew->AppendFly( static_cast<SwFlyFrame*>(pAnchoredObj) ); + } + 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().IsOver(aRepaintRect)) && + SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell())) + { + 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); + } + 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_eAlign(sw::LineAlign::NONE) +{ + nLineLength = 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, long nLnAscent, long nLnDescent, long nFlyAsc, 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, long nLnAscent, long nLnDescent, long nFlyAsc, 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, + long nLnAscent, long nLnDescent, + long nFlyAsc, 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(); + } + + SwFrameFormat* pShape = FindFrameFormat(pSdrObj); + const SwFormatAnchor& rAnchor(pShape->GetAnchor()); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR) + { + // This is an inline draw shape, see if it has a textbox. + SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT); + if (pTextBox) + { + // It has, so look up its text rectangle, and adjust the position + // of the textbox accordingly. + // Both rectangles are absolute, SwFormatHori/VertOrient's position + // is relative to the print area of the anchor text frame. + tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pShape); + + SwFormatHoriOrient aHori(pTextBox->GetHoriOrient()); + aHori.SetHoriOrient(css::text::HoriOrientation::NONE); + sal_Int32 nLeft = aTextRectangle.getX() - rFrame.getFrameArea().Left() + - rFrame.getFramePrintArea().Left(); + aHori.SetPos(nLeft); + + SwFormatVertOrient aVert(pTextBox->GetVertOrient()); + aVert.SetVertOrient(css::text::VertOrientation::NONE); + sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top() + - rFrame.getFramePrintArea().Top(); + aVert.SetPos(nTop); + + pTextBox->LockModify(); + pTextBox->SetFormatAttr(aHori); + pTextBox->SetFormatAttr(aVert); + pTextBox->UnlockModify(); + } + } + + 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 ) + { + nAscent = static_cast<sal_uInt16>(-nRelPos); + if( nAscent > Height() ) + Height( nAscent ); + } + else + { + nAscent = 0; + Height( Height() + static_cast<sal_uInt16>(nRelPos) ); + } + } + else + { + Height( 1 ); + nAscent = 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..c01abd499 --- /dev/null +++ b/sw/source/core/text/porfly.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_TEXT_PORFLY_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORFLY_HXX + +#include <ascharanchoredobjectposition.hxx> + +#include "porglue.hxx" +#include <flyfrms.hxx> + +class SwDrawContact; +class SwTextFrame; +struct SwCursorMoveState; + +class SwFlyPortion : public SwFixPortion +{ + sal_uInt16 nBlankWidth; +public: + explicit SwFlyPortion( const SwRect &rFlyRect ) + : SwFixPortion(rFlyRect), nBlankWidth( 0 ) { SetWhichPor( PortionType::Fly ); } + sal_uInt16 GetBlankWidth( ) const { return nBlankWidth; } + void SetBlankWidth( const sal_uInt16 nNew ) { 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 + 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; } + sw::LineAlign GetAlign() const { return m_eAlign; } + void SetAlign(sw::LineAlign eAlign) { m_eAlign = eAlign; } + void SetMax(bool bMax) { m_bMax = bMax; } + void SetBase(const SwTextFrame& rFrame, const Point& rBase, long nLnAscent, long nLnDescent, long nFlyAscent, 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, long nAscent, long nDescent, long nFlyAsc, 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, long nAsc, long nDescent, long nFlyAsc, long nFlyDesc, AsCharFlags nFlags); + virtual void Paint(const SwTextPaintInfo& rInf) const override; + virtual ~DrawFlyCntPortion() override; + }; +} + +#endif + +/* 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..181d77a64 --- /dev/null +++ b/sw/source/core/text/porftn.hxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_PORFTN_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORFTN_HXX + +#include "porfld.hxx" + +class SwTextFootnote; + +class SwFootnotePortion : public SwFieldPortion +{ + SwTextFootnote *pFootnote; + sal_uInt16 nOrigHeight; + // #i98418# + bool mbPreferredScriptTypeSet; + SwFontScript mnPreferredScriptType; +public: + SwFootnotePortion( const OUString &rExpand, SwTextFootnote *pFootnote, + sal_uInt16 nOrig = USHRT_MAX ); + sal_uInt16& Orig() { return 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 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 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 ) { aErgo = rStr; } + const OUString& GetQuoText() const { return m_aExpand; } + const OUString &GetContText() const { return 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, const OUString& 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; +}; + +#endif + +/* 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..0d34ea304 --- /dev/null +++ b/sw/source/core/text/porglue.cxx @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 ) + : nFixWidth( nInitFixWidth ) +{ + PrtWidth( 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; + 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() ) + { + OUStringBuffer aBuf; + comphelper::string::padToLength(aBuf, GetFixWidth() / sal_Int32(GetLen()), ' '); + OUString aText(aBuf.makeStringAndClear()); + SwTextPaintInfo aInf( rInf, &aText ); + aInf.DrawText(*this, TextFrameIndex(aText.getLength()), true); + } + + if( rInf.OnWin() && rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() ) + { +#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 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; +} + +/** + * We're expecting a frame-local SwRect! + */ +SwFixPortion::SwFixPortion( const SwRect &rRect ) + :SwGluePortion( sal_uInt16(rRect.Width()) ), nFix( sal_uInt16(rRect.Left()) ) +{ + Height( sal_uInt16(rRect.Height()) ); + SetWhichPor( PortionType::Fix ); +} + +SwFixPortion::SwFixPortion() + : SwGluePortion(0), nFix(0) +{ + SetWhichPor( PortionType::Fix ); +} + +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..27e9b0dab --- /dev/null +++ b/sw/source/core/text/porglue.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_TEXT_PORGLUE_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORGLUE_HXX + +#include "porlin.hxx" + +class SwRect; +class SwLineLayout; + +class SwGluePortion : public SwLinePortion +{ +private: + sal_uInt16 nFixWidth; +public: + explicit SwGluePortion( const sal_uInt16 nInitFixWidth ); + + void Join( SwGluePortion *pVictim ); + + inline long GetPrtGlue() const; + sal_uInt16 GetFixWidth() const { return nFixWidth; } + void SetFixWidth( const sal_uInt16 nNew ) { nFixWidth = nNew; } + void MoveGlue( SwGluePortion *pTarget, const 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; +}; + +class SwFixPortion : public SwGluePortion +{ + sal_uInt16 nFix; // The width offset in the line +public: + explicit SwFixPortion( const SwRect &rFlyRect ); + SwFixPortion(); + void SetFix( const sal_uInt16 nNewFix ) { nFix = nNewFix; } + sal_uInt16 GetFix() const { return nFix; } +}; + +class SwMarginPortion : public SwGluePortion +{ +public: + explicit SwMarginPortion(); + void AdjustRight( const SwLineLayout* pCurr ); +}; + +inline long SwGluePortion::GetPrtGlue() const +{ return Width() - nFixWidth; } + +// The FixWidth MUST NEVER be larger than the accumulated width! +inline void SwGluePortion::AdjFixWidth() +{ + if( nFixWidth > PrtWidth() ) + nFixWidth = PrtWidth(); +} + +inline void SwGluePortion::MoveAllGlue( SwGluePortion *pTarget ) +{ + MoveGlue( pTarget, GetPrtGlue() ); +} + +inline void SwGluePortion::MoveHalfGlue( SwGluePortion *pTarget ) +{ + MoveGlue( pTarget, GetPrtGlue() / 2 ); +} + +#endif + +/* 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..57f685abe --- /dev/null +++ b/sw/source/core/text/porhyph.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_PORHYPH_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORHYPH_HXX + +#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; +}; + +class SwHyphStrPortion : public SwHyphPortion +{ + OUString aExpand; +public: + explicit SwHyphStrPortion(const OUString &rStr) + : aExpand(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 bExpand; + sal_uInt16 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 ) { bExpand = bNew; } + bool IsExpand() const { return 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( const OUString &rStr ); + virtual void Paint( const SwTextPaintInfo &rInf ) const override; +}; + +#endif + +/* 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..499dac050 --- /dev/null +++ b/sw/source/core/text/porlay.cxx @@ -0,0 +1,2645 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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 <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> + +using namespace ::com::sun::star; +using namespace i18n::ScriptType; + +#include <unicode/ubidi.h> +#include <i18nutil/scripttypedetector.hxx> +#include <i18nutil/unicode.hxx> + +#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 charters 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 charters 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 ( const OUString& 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; + std::vector<SwLineLayout*> aNexts; + SwLineLayout* pNext = m_pNext; + do + { + aNexts.push_back(pNext); + SwLineLayout* pLastNext = pNext; + pNext = pNext->GetNext(); + pLastNext->SetNext(nullptr); + } + while (pNext); + for (auto a : aNexts) + delete a; +} + +// 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 long nInit ) +{ + m_pLLSpaceAdd.reset( new std::vector<long> ); + SetLLSpaceAdd( nInit, 0 ); +} + +// Returns true if there are only blanks in [nStt, nEnd[ +static bool lcl_HasOnlyBlanks(const OUString& 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; + 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; + + if( mpNextPortion ) + { + SetContent( false ); + if( mpNextPortion->IsBreakPortion() ) + { + SetLen( mpNextPortion->GetLen() ); + if( GetLen() ) + bTmpDummy = false; + } + else + { + const sal_uInt16 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() ); + if( !GetAscent() ) + SetAscent( pPos->GetAscent() ); + } + delete pLast->Cut( pPos ); + pPos = pLast->GetNextPortion(); + continue; + } + + TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + nLineLength; + nLineLength += pPos->GetLen(); + AddPrtWidth( pPos->Width() ); + + // #i3952# + if ( bIgnoreBlanksAndTabsForLineHeightCalculation ) + { + 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 + + sal_uInt16 nPosHeight = pPos->Height(); + sal_uInt16 nPosAscent = pPos->GetAscent(); + + SAL_WARN_IF( nPosHeight < nPosAscent, + "sw.core", "SwLineLayout::CalcLine: bad ascent or height" ); + + if( pPos->IsHangingPortion() ) + { + SetHanging(true); + rInf.GetParaPortion()->SetMargin(); + } + + // To prevent that a paragraph-end-character does not change + // the line height through a Descent and thus causing the line + // to reformat. + if ( !pPos->IsBreakPortion() || !Height() ) + { + if (!pPos->IsPostItsPortion()) bOnlyPostIts = false; + + if( bTmpDummy && !nLineLength ) + { + 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 ); + nAscent = nPosAscent; + nMaxDescent = nPosHeight - nPosAscent; + } + } + else if( !pPos->IsFlyPortion() ) + { + if( Height() < nPosHeight ) + { + // Height is set to 0 when Init() is called. + if (bIgnoreBlanksAndTabsForLineHeightCalculation && pPos->GetWhichPor() == PortionType::FlyCnt) + // Compat flag set: take the line height, if it's larger. + Height(std::max(nPosHeight, nLineHeight)); + else + // Just care about the portion height. + Height(nPosHeight); + } + 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( nAscent < nPosAscent ) + nAscent = 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 + nAscent ) + { + if( sw::LineAlign::BOTTOM == pFlyCnt->GetAlign() ) + nAscent = Height() - nMaxDescent; + else if( sw::LineAlign::CENTER == pFlyCnt->GetAlign() ) + nAscent = ( Height() + nAscent - nMaxDescent ) / 2; + } + pFlyCnt->SetAscent( nAscent ); + } + } + + if( bTmpDummy && nFlyHeight ) + { + nAscent = nFlyAscent; + if( nFlyDescent > nFlyHeight - nFlyAscent ) + Height( nFlyHeight + nFlyDescent ); + else + Height( nFlyHeight ); + } + else if( nMaxDescent > Height() - nAscent ) + Height( nMaxDescent + nAscent ); + + if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) ) + { + Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) ); + nAscent = 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 ); + } + + // 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())); + SetRedline( rLine.GetRedln() && + rLine.GetRedln()->CheckLine(start.first->GetIndex(), start.second, + end.first->GetIndex(), end.second) ); +} + +// #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 = static_cast<SwTwips>(pTmpPortion->GetAscent()); + SwTwips nPortionDesc = static_cast<SwTwips>(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::ResetFlags() +{ + m_bFormatAdj = m_bDummy = m_bEndHyph = m_bMidHyph = m_bFly + = m_bRest = m_bBlinking = m_bClipping = m_bContent = m_bRedline + = m_bForcedLeftMargin = m_bHanging = false; +} + +SwLineLayout::SwLineLayout() + : m_pNext( nullptr ), m_nRealHeight( 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.nLen) + { + if (TextFrameIndex(0) == nLen) { + nStart = rRange.nStart; + nLen = rRange.nLen ; + } + else { + if(rRange.nStart + rRange.nLen > nStart + nLen) { + nLen = rRange.nStart + rRange.nLen - nStart; + } + if(rRange.nStart < nStart) { + nLen += nStart - rRange.nStart; + nStart = rRange.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; + } + } + } +} + +// 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, ++iter) + { + if (iter->pNode == pNode) + { + nOffset += TextFrameIndex(iter->nEnd - iter->nStart); + 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; + + while (true) + { + // because of the selectRedLineDeleted call, never overlaps + // extents, must be contained inside one extent + assert(!(iter->nStart <= nStart && nStart < iter->nEnd && iter->nEnd < nEnd)); + assert(!(nStart < iter->nStart && iter->nStart < nEnd && nEnd <= iter->nEnd)); + if (iter->nStart <= nStart && nEnd <= iter->nEnd) + { + if (iter->nStart == nStart && !m_HiddenChg.empty() + && m_HiddenChg.back() == nOffset) + { + // previous one went until end of extent, extend it + m_HiddenChg.back() += TextFrameIndex(nEnd - iter->nStart); + } + else // new one + { + m_HiddenChg.push_back(nOffset + TextFrameIndex(nStart - iter->nStart)); + m_HiddenChg.push_back(nOffset + TextFrameIndex(nEnd - iter->nStart)); + } + break; + } + else + { + nOffset += TextFrameIndex(iter->nEnd - iter->nStart); + ++iter; + // because selectRedLineDeleted, must find it in pNode + assert(iter != pMerged->extents.end()); + assert(iter->pNode == pNode); + } + } + } + nOffset += TextFrameIndex(iter->nEnd - iter->nStart); + } + } + 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; + } + } + + 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; +} + +long SwScriptInfo::Compress(long* 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 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; + + long nSub = 0; + 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; + 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( long* pKernArray, + long* pScrArray, + TextFrameIndex const nStt, + TextFrameIndex const nLen, + 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; + 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; + if ( pScrArray ) + pScrArray[ 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, long* pKernArray, + long* pScrArray, TextFrameIndex const nStt, + TextFrameIndex const nLen, + TextFrameIndex nNumberOfBlanks, + 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; + + 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; + if ( pScrArray ) pScrArray[ 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 SwLineLayout::Init( SwLinePortion* pNextPortion ) +{ + Height( 0 ); + 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 = nAscent; + + 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 SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) && + static_cast<const SvxCharHiddenItem*>(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); + } + } + 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() ) ) + { + 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, long* pKernArray, + long* pScrArray, TextFrameIndex const nStt, + TextFrameIndex const nLen, LanguageType aLang, + long nSpaceAdd, bool bIsSpaceStop ) +{ + assert( pKernArray != nullptr && sal_Int32(nStt) >= 0 ); + if (sal_Int32(nLen) > 0) + { + 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; + if ( pScrArray ) + pScrArray[ 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..ea8220247 --- /dev/null +++ b/sw/source/core/text/porlay.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_PORLAY_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORLAY_HXX + +#include <scriptinfo.hxx> + +#include <swrect.hxx> +#include <swtypes.hxx> +#include "portxt.hxx" + +#include <vector> +#include <deque> + +class SwMarginPortion; +class SwDropPortion; +class SwTextFormatter; + +class SwCharRange +{ +private: + TextFrameIndex nStart; + TextFrameIndex nLen; + +public: + SwCharRange(TextFrameIndex const nInitStart = TextFrameIndex(0), + TextFrameIndex const nInitLen = TextFrameIndex(0)) + : nStart( nInitStart ), nLen(nInitLen) {} + TextFrameIndex & Start() { return nStart; } + TextFrameIndex const& Start() const { return nStart; } + void LeftMove(TextFrameIndex const nNew) + { if ( nNew < nStart ) { nLen += nStart-nNew; nStart = nNew; } } + TextFrameIndex & Len() { return nLen; } + TextFrameIndex const& Len() const { return nLen; } + bool operator<(const SwCharRange &rRange) const + { return nStart < rRange.nStart; } + bool operator>(const SwCharRange &rRange) const + { return nStart + nLen > rRange.nStart + rRange.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 nOfst; + SwTwips nRightOfst; +public: + SwRepaint() : SwRect(), nOfst( 0 ), nRightOfst( 0 ) {} + + SwTwips GetOffset() const { return nOfst; } + void SetOffset( const SwTwips nNew ) { nOfst = nNew; } + SwTwips GetRightOfst() const { return nRightOfst; } + void SetRightOfst( const SwTwips nNew ) { nRightOfst = nNew; } +}; + +/// Collection of SwLinePortion instances, representing one line of text. +/// Typically owned by an SwParaPortion. +class SwLineLayout : public SwTextPortion +{ +private: + SwLineLayout *m_pNext; // The next Line + std::unique_ptr<std::vector<long>> m_pLLSpaceAdd; // Used for justified alignment + std::unique_ptr<std::deque<sal_uInt16>> m_pKanaComp; // Used for Kana compression + sal_uInt16 m_nRealHeight; // The height resulting from line spacing and register + 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_bForcedLeftMargin : 1; // Left adjustment moved by the Fly + bool m_bHanging : 1; // Contains a hanging portion in the margin + bool m_bUnderscore : 1; + + SwTwips GetHangingMargin_() const; + + void DeleteNext(); +public: + // 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 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( sal_uInt16 nNew ) { m_nRealHeight = nNew; } + sal_uInt16 GetRealHeight() const { return m_nRealHeight; } + + // 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 long nInit = 0 ); + void FinishSpaceAdd() { m_pLLSpaceAdd.reset(); } + sal_uInt16 GetLLSpaceAddCount() const { return sal::static_int_cast< sal_uInt16 >(m_pLLSpaceAdd->size()); } + void SetLLSpaceAdd( long nNew, sal_uInt16 nIdx ) + { + if ( nIdx == GetLLSpaceAddCount() ) + m_pLLSpaceAdd->push_back( nNew ); + else + (*m_pLLSpaceAdd)[ nIdx ] = nNew; + } + long GetLLSpaceAdd( sal_uInt16 nIdx ) { return (*m_pLLSpaceAdd)[ nIdx ]; } + void RemoveFirstLLSpaceAdd() { m_pLLSpaceAdd->erase( m_pLLSpaceAdd->begin() ); } + std::vector<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; +}; + +/// 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; + 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; } + long& GetDelta() { return m_nDelta; } + const 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( const OUString &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; +}; + +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; +} + +#endif + +/* 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..a9b057faa --- /dev/null +++ b/sw/source/core/text/porlin.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 <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 ), + nLineLength( 0 ), + nAscent( 0 ), + nWhichPor( 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 + static_cast<sal_uInt16>(pLast->CalcSpacing( rInf.GetSpaceAdd(), rInf )); + + sal_uInt16 nPos; + SwTextPaintInfo aInf( rInf ); + + const bool bBidiPor = rInf.GetTextFrame()->IsRightToLeft() != + bool( ComplexTextLayoutFlags::BiDiRtl & rInf.GetOut()->GetLayoutMode() ); + + sal_uInt16 nDir = bBidiPor ? + 1800 : + 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) + { + 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 = static_cast<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() ); +} + +long SwLinePortion::CalcSpacing( 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() ); +} + +/* 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..615c90022 --- /dev/null +++ b/sw/source/core/text/porlin.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_PORLIN_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORLIN_HXX + +#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 SwLinePortion: public SwPosSize +{ +protected: + // Here we have areas with different attributes + SwLinePortion *mpNextPortion; + // Count of chars and spaces on the line + TextFrameIndex nLineLength; + sal_uInt16 nAscent; // Maximum ascender + + SwLinePortion(); +private: + PortionType nWhichPor; // 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 nLineLength; } + void SetLen(TextFrameIndex const nLen) { nLineLength = nLen; } + void SetNextPortion( SwLinePortion *pNew ){ mpNextPortion = pNew; } + sal_uInt16 &GetAscent() { return nAscent; } + sal_uInt16 GetAscent() const { return nAscent; } + void SetAscent( const sal_uInt16 nNewAsc ) { nAscent = nNewAsc; } + void PrtWidth( sal_uInt16 nNewWidth ) { Width( nNewWidth ); } + sal_uInt16 PrtWidth() const { return Width(); } + void AddPrtWidth( const sal_uInt16 nNew ) { Width( Width() + nNew ); } + void SubPrtWidth( const sal_uInt16 nNew ) { Width( Width() - nNew ); } + + // 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 ) { nWhichPor = nNew; } + PortionType GetWhichPor( ) const { return nWhichPor; } + +// Group queries + bool InTextGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_TXT) != 0; } + bool InGlueGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_GLUE) != 0; } + bool InTabGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_TAB) != 0; } + bool InHyphGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_HYPH) != 0; } + bool InNumberGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_NUMBER) != 0; } + bool InFixGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_FIX) != 0; } + bool InFieldGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_FLD) != 0; } + bool InToxRefGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_TOXREF) != 0; } + bool InToxRefOrFieldGrp() const { return (sal_uInt16(nWhichPor) & ( PORGRP_FLD | PORGRP_TOXREF )) != 0; } + bool InExpGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_EXP) != 0; } + bool InFixMargGrp() const { return (sal_uInt16(nWhichPor) & PORGRP_FIXMARG) != 0; } + bool InSpaceGrp() const { return InTextGrp() || IsMultiPortion(); } +// Individual queries + bool IsGrfNumPortion() const { return nWhichPor == PortionType::GrfNum; } + bool IsFlyCntPortion() const { return nWhichPor == PortionType::FlyCnt; } + bool IsBlankPortion() const { return nWhichPor == PortionType::Blank; } + bool IsBreakPortion() const { return nWhichPor == PortionType::Break; } + bool IsErgoSumPortion() const { return nWhichPor == PortionType::ErgoSum; } + bool IsQuoVadisPortion() const { return nWhichPor == PortionType::QuoVadis; } + bool IsTabLeftPortion() const { return nWhichPor == PortionType::TabLeft; } + bool IsTabRightPortion() const { return nWhichPor == PortionType::TabRight; } + bool IsFootnoteNumPortion() const { return nWhichPor == PortionType::FootnoteNum; } + bool IsFootnotePortion() const { return nWhichPor == PortionType::Footnote; } + bool IsDropPortion() const { return nWhichPor == PortionType::Drop; } + bool IsLayPortion() const { return nWhichPor == PortionType::Lay; } + bool IsParaPortion() const { return nWhichPor == PortionType::Para; } + bool IsMarginPortion() const { return nWhichPor == PortionType::Margin; } + bool IsFlyPortion() const { return nWhichPor == PortionType::Fly; } + bool IsHolePortion() const { return nWhichPor == PortionType::Hole; } + bool IsSoftHyphPortion() const { return nWhichPor == PortionType::SoftHyphen; } + bool IsPostItsPortion() const { return nWhichPor == PortionType::PostIts; } + bool IsCombinedPortion() const { return nWhichPor == PortionType::Combined; } + bool IsTextPortion() const { return nWhichPor == PortionType::Text; } + bool IsHangingPortion() const { return nWhichPor == PortionType::Hanging; } + bool IsKernPortion() const { return nWhichPor == PortionType::Kern; } + bool IsArrowPortion() const { return nWhichPor == PortionType::Arrow; } + bool IsMultiPortion() const { return nWhichPor == PortionType::Multi; } + bool IsNumberPortion() const { return nWhichPor == PortionType::Number; } // #i23726# + bool IsControlCharPortion() const { return nWhichPor == PortionType::ControlChar || nWhichPor == 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 long CalcSpacing( 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; } +}; + +inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion) +{ + *static_cast<SwPosSize*>(this) = rPortion; + nLineLength = rPortion.nLineLength; + nAscent = rPortion.nAscent; + nWhichPor = rPortion.nWhichPor; + m_bJoinBorderWithPrev = rPortion.m_bJoinBorderWithPrev; + m_bJoinBorderWithNext = rPortion.m_bJoinBorderWithNext; + return *this; +} + +inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) : + SwPosSize( rPortion ), + mpNextPortion( nullptr ), + nLineLength( rPortion.nLineLength ), + nAscent( rPortion.nAscent ), + nWhichPor( rPortion.nWhichPor ), + m_bJoinBorderWithPrev( rPortion.m_bJoinBorderWithPrev ), + m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext ) +{ +} + +inline void SwLinePortion::Truncate() +{ + if ( mpNextPortion ) + Truncate_(); +} + +#endif + +/* 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..2154eba1d --- /dev/null +++ b/sw/source/core/text/pormulti.cxx @@ -0,0 +1,2569 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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() ) + { + 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 ); + } +} + +long SwMultiPortion::CalcSpacing( long , const SwTextSizeInfo & ) const +{ + return 0; +} + +bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const +{ + return false; +} + +void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + +// 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 ), nLevel( nLv ) +{ + SetBidi(); + + if ( nLevel % 2 ) + SetDirection( DIR_RIGHT2LEFT ); + else + SetDirection( DIR_LEFT2RIGHT ); +} + +long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTextSizeInfo& rInf ) const +{ + return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt(rInf)) * nSpaceAdd / SPACING_PRECISION_FACTOR; +} + +bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, 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) + , nLineDiff(0) + , nBlank1(0) + , 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) + , pBracket(new SwBracket) + , nLineDiff(0) + , nBlank1(0) + , nBlank2(0) +{ + pBracket->nAscent = 0; + pBracket->nHeight = 0; + pBracket->nPreWidth = 0; + pBracket->nPostWidth = 0; + + SetDouble(); + const SvxTwoLinesItem* pTwo = static_cast<const SvxTwoLinesItem*>(rCreate.pItem); + if( pTwo ) + pBracket->nStart = TextFrameIndex(0); + else + { + const SwTextAttr& rAttr = *rCreate.pAttr; + 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 ) + { + pBracket->cPre = pTwo->GetStartBracket(); + pBracket->cPost = pTwo->GetEndBracket(); + } + else + { + pBracket->cPre = 0; + pBracket->cPost = 0; + } + SwFontScript nTmp = SW_SCRIPTS; + if( pBracket->cPre > 255 ) + { + OUString aText(pBracket->cPre); + nTmp = SwScriptInfo::WhichFont(0, aText); + } + pBracket->nPreScript = nTmp; + nTmp = SW_SCRIPTS; + if( pBracket->cPost > 255 ) + { + OUString aText(pBracket->cPost); + nTmp = SwScriptInfo::WhichFont(0, aText); + } + pBracket->nPostScript = nTmp; + + if( !pBracket->cPre && !pBracket->cPost ) + { + 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, + long nSpaceAdd, + bool bOpen ) const +{ + sal_Unicode cCh = bOpen ? pBracket->cPre : 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( pBracket->nAscent ); + aBlank.Width( nChWidth ); + aBlank.Height( pBracket->nHeight ); + { + std::unique_ptr<SwFont> pTmpFnt( new SwFont( *rInf.GetFont() ) ); + SwFontScript nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript; + if( SW_SCRIPTS > nAct ) + pTmpFnt->SetActual( nAct ); + pTmpFnt->SetProportion( 100 ); + SwFontSave aSave( rInf, pTmpFnt.get() ); + 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.pBracket ) + { + pBracket.reset( new SwBracket ); + pBracket->cPre = rDouble.pBracket->cPre; + pBracket->cPost = rDouble.pBracket->cPost; + pBracket->nPreScript = rDouble.pBracket->nPreScript; + pBracket->nPostScript = rDouble.pBracket->nPostScript; + pBracket->nStart = rDouble.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(); + std::unique_ptr<SwFont> pTmpFnt( new SwFont( *rInf.GetFont() ) ); + pTmpFnt->SetProportion( 100 ); + pBracket->nAscent = 0; + pBracket->nHeight = 0; + if( pBracket->cPre ) + { + OUString aStr( pBracket->cPre ); + SwFontScript nActualScr = pTmpFnt->GetActual(); + if( SW_SCRIPTS > pBracket->nPreScript ) + pTmpFnt->SetActual( pBracket->nPreScript ); + SwFontSave aSave( rInf, pTmpFnt.get() ); + SwPosSize aSize = rInf.GetTextSize( aStr ); + pBracket->nAscent = rInf.GetAscent(); + pBracket->nHeight = aSize.Height(); + pTmpFnt->SetActual( nActualScr ); + if( nMaxWidth > aSize.Width() ) + { + pBracket->nPreWidth = aSize.Width(); + nMaxWidth -= aSize.Width(); + rInf.X( rInf.X() + aSize.Width() ); + } + else + { + pBracket->nPreWidth = 0; + nMaxWidth = 0; + } + } + else + pBracket->nPreWidth = 0; + if( pBracket->cPost ) + { + OUString aStr( pBracket->cPost ); + if( SW_SCRIPTS > pBracket->nPostScript ) + pTmpFnt->SetActual( pBracket->nPostScript ); + SwFontSave aSave( rInf, pTmpFnt.get() ); + SwPosSize aSize = rInf.GetTextSize( aStr ); + const sal_uInt16 nTmpAsc = rInf.GetAscent(); + if( nTmpAsc > pBracket->nAscent ) + { + pBracket->nHeight += nTmpAsc - pBracket->nAscent; + pBracket->nAscent = nTmpAsc; + } + if( aSize.Height() > pBracket->nHeight ) + pBracket->nHeight = aSize.Height(); + if( nMaxWidth > aSize.Width() ) + { + pBracket->nPostWidth = aSize.Width(); + nMaxWidth -= aSize.Width(); + } + else + { + pBracket->nPostWidth = 0; + nMaxWidth = 0; + } + } + else + 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 (nBlank1 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion()) + { + if( pPor->InTextGrp() ) + nBlank1 = nBlank1 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull ); + rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); + if( pPor->InTabGrp() ) + SetTab1( true ); + } + nLineDiff = GetRoot().Width(); + if( GetRoot().GetNext() ) + { + pPor = GetRoot().GetNext()->GetFirstPortion(); + nLineDiff -= GetRoot().GetNext()->Width(); + } + for (nBlank2 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion()) + { + if( pPor->InTextGrp() ) + nBlank2 = nBlank2 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull ); + rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); + if( pPor->InTabGrp() ) + SetTab2( true ); + } + rInf.SetIdx( nStart ); +} + +long SwDoubleLinePortion::CalcSpacing( 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, + 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<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 ) + , nRubyOffset( rRuby.GetRubyOffset() ) + , 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(); + nAdjustment = rRuby.GetAdjustment(); + 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 == nAdjustment ) + nAdjustment = css::text::RubyAdjust_RIGHT; + else if ( css::text::RubyAdjust_RIGHT == nAdjustment ) + 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 ( nAdjustment ) + { + case css::text::RubyAdjust_CENTER: nRight = static_cast<sal_uInt16>(nLineDiff / 2); + [[fallthrough]]; + case css::text::RubyAdjust_RIGHT: nLeft = static_cast<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 = static_cast<sal_uInt16>(nLineDiff / 2); + nLeft = static_cast<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() ) + nRubyOffset = pField->GetNextOffset(); + else + 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(); + 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) + { + 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*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 < m_pMerged->extents.size() && + 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::unique_ptr<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 nullptr; + std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator); + pRet->pItem = nullptr; + pRet->pAttr = nullptr; + pRet->nStartOfAttr = TextFrameIndex(-1); + pRet->nId = SwMultiCreatorId::Bidi; + pRet->nLevel = nCurrLevel + 1; + return pRet; + } + + // a bidi portion can only contain other bidi portions + if ( pMulti ) + return nullptr; + + // need the node that contains input rPos + std::pair<SwTextNode const*, sal_Int32> startPos(m_pFrame->MapViewToModel(rPos)); + const SvxCharRotateItem* pActiveRotateItem(nullptr); + const SfxPoolItem* pNodeRotateItem(nullptr); + const SvxTwoLinesItem* pActiveTwoLinesItem(nullptr); + const SfxPoolItem* 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) && + static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue()) + { + pActiveRotateItem = static_cast<const SvxCharRotateItem*>(pNodeRotateItem); + } + else + { + pNodeRotateItem = nullptr; + } + if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState( + RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) && + static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetValue()) + { + pActiveTwoLinesItem = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem); + } + else + { + pNodeTwoLinesItem = nullptr; + } + } + } + } + if (!pRuby && !pActiveTwoLinesItem && !pActiveRotateItem) + return nullptr; + + 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()); + std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator); + pRet->pItem = nullptr; + pRet->pAttr = pRuby; + pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart()); + pRet->nId = SwMultiCreatorId::Ruby; + pRet->nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0; + return pRet; + } + 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... + std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator); + + // 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) + { + pRet->pItem = nullptr; + pRet->pAttr = pActiveTwoLinesHint; + pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart()); + if (pNodeTwoLinesItem) + { + aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len())); + bOn = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetEndBracket() == + pActiveTwoLinesItem->GetEndBracket() && + static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetStartBracket() == + pActiveTwoLinesItem->GetStartBracket(); + } + else + { + aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End())); + } + } + else + { + pRet->pItem = pNodeTwoLinesItem; + pRet->pAttr = nullptr; + pRet->nStartOfAttr = TextFrameIndex(-1); + aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len())); + } + pRet->nId = SwMultiCreatorId::Double; + pRet->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 = nullptr; + pNode->GetSwAttrSet().GetItemState( + RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem); + 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 pRet; + 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 pRet; + } + 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... + std::unique_ptr<SwMultiCreator> pRet(new SwMultiCreator); + pRet->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 = nullptr; + pNode->GetSwAttrSet().GetItemState( + RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem); + 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) + { + pRet->pItem = nullptr; + pRet->pAttr = pActiveRotateHint; + pRet->nStartOfAttr = m_pFrame->MapModelToView(startPos.first, pRet->pAttr->GetStart()); + if (pNodeRotateItem) + { + aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len())); + bOn = static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue() == + pActiveRotateItem->GetValue(); + } + else + { + aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *pRet->pAttr->End())); + } + } + else + { + pRet->pItem = pNodeRotateItem; + pRet->pAttr = nullptr; + pRet->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 = nullptr; + pNode->GetSwAttrSet().GetItemState( + RES_CHRATR_ROTATE, true, &pNodeRotateItem); + 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 pRet; + } + return nullptr; +} + +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& rInfo; + SwMultiPortion& rMulti; + std::vector<long>* pOldSpaceAdd; + sal_uInt16 nOldSpIdx; + long nSpaceAdd; + bool bSpaceChg; + sal_uInt8 nOldDir; +public: + SwSpaceManipulator( SwTextPaintInfo& rInf, SwMultiPortion& rMult ); + ~SwSpaceManipulator(); + void SecondLine(); + long GetSpaceAdd() const { return nSpaceAdd; } +}; + +} + +SwSpaceManipulator::SwSpaceManipulator( SwTextPaintInfo& rInf, + SwMultiPortion& rMult ) + : rInfo(rInf) + , rMulti(rMult) + , nSpaceAdd(0) +{ + pOldSpaceAdd = rInfo.GetpSpaceAdd(); + nOldSpIdx = rInfo.GetSpaceIdx(); + nOldDir = rInfo.GetDirection(); + rInfo.SetDirection( rMulti.GetDirection() ); + bSpaceChg = false; + + if( rMulti.IsDouble() ) + { + nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ? + rInfo.GetSpaceAdd() : 0; + if( rMulti.GetRoot().IsSpaceAdd() ) + { + rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd ); + } + else if( rMulti.HasTabulator() ) + rInfo.SetpSpaceAdd( nullptr ); + } + else if ( ! rMulti.IsBidi() ) + { + rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + } +} + +void SwSpaceManipulator::SecondLine() +{ + if( bSpaceChg ) + { + rInfo.RemoveFirstSpaceAdd(); + bSpaceChg = false; + } + SwLineLayout *pLay = rMulti.GetRoot().GetNext(); + if( pLay->IsSpaceAdd() ) + { + rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd ); + } + else + { + rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ? + nullptr : pOldSpaceAdd ); + rInfo.SetSpaceIdx( nOldSpIdx); + } +} + +SwSpaceManipulator::~SwSpaceManipulator() +{ + if( bSpaceChg ) + { + rInfo.RemoveFirstSpaceAdd(); + bSpaceChg = false; + } + rInfo.SetpSpaceAdd( pOldSpaceAdd ); + rInfo.SetSpaceIdx( nOldSpIdx); + rInfo.SetDirection( 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, 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, 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; + if( rMulti.IsDouble() ) + { + SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); + if( rMulti.IsDouble() ) + { + SetPropFont( 50 ); + pTmpFnt->SetProportion( GetPropFont() ); + } + xFontSave.reset(new SwFontSave(rInf, pTmpFnt, 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<vcl::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::unique_ptr<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, + sal_uInt16& nX, + TextFrameIndex const nCurrStart, + 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( static_cast<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..8a0d352df --- /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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_PORMULTI_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORMULTI_HXX + +#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 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 long CalcSpacing( long nSpaceAdd, const SwTextSizeInfo &rInf ) const override; + virtual bool ChgSpaceAdd( SwLineLayout* pCurr, 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; +}; + +class SwDoubleLinePortion : public SwMultiPortion +{ + std::unique_ptr<SwBracket> pBracket; // Surrounding brackets + SwTwips nLineDiff; // Difference of the width of the both lines + TextFrameIndex nBlank1; ///< Number of blanks in the first line + TextFrameIndex 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 pBracket.get(); } + void SetBrackets( const SwDoubleLinePortion& rDouble ); + void PaintBracket( SwTextPaintInfo& rInf, long nSpaceAdd, bool bOpen ) const; + void FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxWidth ); + sal_uInt16 PreWidth() const { return pBracket->nPreWidth; }; + sal_uInt16 PostWidth() const { return pBracket->nPostWidth; } + void ClearBrackets() + { pBracket->nPreWidth = pBracket->nPostWidth=0; Width( 0 ); } + sal_uInt16 BracketWidth(){ return PreWidth() + PostWidth(); } + + void CalcBlanks( SwTextFormatInfo &rInf ); + static void ResetSpaceAdd( SwLineLayout* pCurr ); + SwTwips GetLineDiff() const { return nLineDiff; } + TextFrameIndex GetSpaceCnt() const + { return ( nLineDiff < 0 ) ? nBlank2 : nBlank1; } + TextFrameIndex GetSmallerSpaceCnt() const + { return ( nLineDiff < 0 ) ? nBlank1 : nBlank2; } + + virtual long CalcSpacing( long nSpaceAdd, const SwTextSizeInfo &rInf ) const override; + virtual bool ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const override; +}; + +class SwRubyPortion : public SwMultiPortion +{ + TextFrameIndex nRubyOffset; + css::text::RubyAdjust 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(nAdjustment != css::text::RubyAdjust_LEFT && GetRoot().GetNext()) Adjust_(rInf); } + css::text::RubyAdjust GetAdjustment() const { return nAdjustment; } + TextFrameIndex GetRubyOffset() const { return 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 nLevel; + +public: + SwBidiPortion(TextFrameIndex nEnd, sal_uInt8 nLv); + + sal_uInt8 GetLevel() const { return nLevel; } + // Get number of blanks for justified alignment + TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf) const; + // Calculates extra spacing based on number of blanks + virtual long CalcSpacing( long nSpaceAdd, const SwTextSizeInfo &rInf ) const override; + // Manipulate the spacing array at pCurr + virtual bool ChgSpaceAdd( SwLineLayout* pCurr, 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, sal_uInt16& nX, TextFrameIndex nCurrStart, long nSpaceAdd); + ~SwTextCursorSave(); +}; + +inline bool SwMultiPortion::HasBrackets() const +{ + return IsDouble() && nullptr != static_cast<const SwDoubleLinePortion*>(this)->GetBrackets(); +} + +#endif + +/* 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..9ddb232d1 --- /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() : 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( !nViewWidth ) + pThis->nViewWidth = rInf.GetTextSize(OUString(' ')).Width(); + } + else + pThis->nViewWidth = 0; + return 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..0c7dc8503 --- /dev/null +++ b/sw/source/core/text/porref.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_TEXT_PORREF_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORREF_HXX + +#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 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; +}; + +#endif + +/* 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..69183bc58 --- /dev/null +++ b/sw/source/core/text/porrst.cxx @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <IDocumentRedlineAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <crsrsh.hxx> + +SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + SetWhichPor( PortionType::TempEnd ); +} + +void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const +{ + if (rInf.OnWin() && rInf.GetOpt().IsParagraph()) + { + const SwFont* pOldFnt = rInf.GetFont(); + + SwFont aFont(*pOldFnt); + aFont.SetColor(NON_PRINTING_CHARACTER_COLOR); + const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont); + + // draw the pilcrow + rInf.DrawText(OUString(CH_PAR), *this); + + const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt)); + } +} + +SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion ) + : SwLinePortion( rPortion ) +{ + nLineLength = TextFrameIndex(1); + SetWhichPor( PortionType::Break ); +} + +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() ) + rInf.DrawLineBreak( *this ); +} + +bool SwBreakPortion::Format( SwTextFormatInfo &rInf ) +{ + const SwLinePortion *pRoot = rInf.GetRoot(); + Width( 0 ); + Height( pRoot->Height() ); + 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() ); +} + +SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn, + bool bBG, bool bGK ) : + nKern( nKrn ), bBackground( bBG ), bGridKern( bGK ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + nLineLength = TextFrameIndex(0); + SetWhichPor( PortionType::Kern ); + if( nKern > 0 ) + Width( nKern ); + rPortion.Insert( this ); +} + +SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) : + nKern( 0 ), bBackground( false ), bGridKern( true ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + + nLineLength = TextFrameIndex(0); + SetWhichPor( PortionType::Kern ); +} + +void SwKernPortion::Paint( const SwTextPaintInfo &rInf ) const +{ + if( Width() ) + { + // bBackground is set for Kerning Portions between two fields + if ( 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 ( bGridKern ) + return; + + if( rInf.GetLast() == this ) + rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) ); + if( nKern < 0 ) + Width( -nKern ); + else + Width( 0 ); + rInf.GetLast()->FormatEOL( rInf ); +} + +SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) : + bLeft( true ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + nLineLength = TextFrameIndex(0); + SetWhichPor( PortionType::Arrow ); +} + +SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf ) + : bLeft( false ) +{ + Height( static_cast<sal_uInt16>(rInf.GetTextFrame()->getFramePrintArea().Height()) ); + aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() + + rInf.GetTextFrame()->getFramePrintArea().Right() ); + aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() + + rInf.GetTextFrame()->getFramePrintArea().Bottom() ); + SetWhichPor( PortionType::Arrow ); +} + +void SwArrowPortion::Paint( const SwTextPaintInfo &rInf ) const +{ + const_cast<SwArrowPortion*>(this)->aPos = rInf.GetPos(); +} + +SwLinePortion *SwArrowPortion::Compress() { return this; } + +SwTwips SwTextFrame::EmptyHeight() const +{ + if (IsCollapse()) { + SwViewShell *pSh = getRootFrame()->GetCurrShell(); + if ( dynamic_cast<const SwCursorShell*>( pSh ) != nullptr ) { + SwCursorShell *pCrSh = static_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 ); + + 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(); + + 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: + { + long nTmp = rSpace.GetPropLineSpace(); + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + nTmp *= rRegDiff; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + rRegDiff = static_cast<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 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_ZWNBSP || !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 + auto const nFactor = (Height() * 95) / aSize.Height(); + rFont.SetProportion(nFactor); + rFont.SetWeight(WEIGHT_THIN, rFont.GetActual()); + rFont.SetColor(NON_PRINTING_CHARACTER_COLOR); + // 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 + { + 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)) + { + 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())) + { + 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..7c716be8d --- /dev/null +++ b/sw/source/core/text/porrst.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_TEXT_PORRST_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORRST_HXX + +#include <tools/gen.hxx> + +#include <TextFrameIndex.hxx> +#include <txttypes.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 +{ +public: + explicit SwTmpEndPortion( const SwLinePortion &rPortion ); + virtual void Paint( const SwTextPaintInfo &rInf ) const override; +}; + +class SwBreakPortion : public SwLinePortion +{ +public: + explicit SwBreakPortion( const SwLinePortion &rPortion ); + // 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; +}; + +class SwKernPortion : public SwLinePortion +{ + short nKern; + bool bBackground; + bool 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; +}; + +class SwArrowPortion : public SwLinePortion +{ + Point aPos; + bool 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 bLeft; } + const Point& GetPos() const { return 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 nInnerWidth; +public: + explicit SwHangingPortion( SwPosSize aSize ) : nInnerWidth( aSize.Width() ) + { + SetWhichPor( PortionType::Hanging ); + SetLen(TextFrameIndex(1)); + Height( aSize.Height() ); + } + + sal_uInt16 GetInnerWidth() const { return 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; } +}; + +#endif + +/* 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..3c6e7a5df --- /dev/null +++ b/sw/source/core/text/portab.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_TEXT_PORTAB_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORTAB_HXX + +#include "porglue.hxx" + +class SwTabPortion : public SwFixPortion +{ + const sal_uInt16 nTabPos; + const sal_Unicode cFill; + const bool 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 != cFill; } + sal_uInt16 GetTabPos() const { return 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; +}; + +#endif + +/* 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..a746058e9 --- /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() : 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( !nViewWidth ) + pThis->nViewWidth = rInf.GetTextSize(OUString(' ')).Width(); + } + else + pThis->nViewWidth = 0; + return 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..b2c8f23a7 --- /dev/null +++ b/sw/source/core/text/portox.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_TEXT_PORTOX_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORTOX_HXX + +#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 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; +}; + +#endif + +/* 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..65d6a58f3 --- /dev/null +++ b/sw/source/core/text/portxt.cxx @@ -0,0 +1,845 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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, 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, 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 = static_cast<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(); + if (aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING) && + aGuess.BreakPos() != rInf.GetLineStart() && + ( !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; +} + +long SwTextPortion::CalcSpacing( 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() + : SwTextPortion() +{ + 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(PushFlags::LINECOLOR | 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 ) + : 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; + + // #i16816# export stuff only needed for tagged pdf support + if (!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() )); + } + + const OUString aText( ' ' ); + rInf.DrawText(aText, *this, TextFrameIndex(0), TextFrameIndex(1)); + + 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 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..88e81a358 --- /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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_PORTXT_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_PORTXT_HXX + +#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 long CalcSpacing( 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 nBlankWidth; +public: + explicit SwHolePortion( const SwTextPortion &rPor ); + sal_uInt16 GetBlankWidth( ) const { return nBlankWidth; } + void SetBlankWidth( const sal_uInt16 nNew ) { 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; +}; + +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; +}; + +#endif + +/* 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..eaad3597f --- /dev/null +++ b/sw/source/core/text/possiz.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_TEXT_POSSIZ_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_POSSIZ_HXX + +#include <tools/gen.hxx> +#include <sal/types.h> + +// Compared to the SV sizes SwPosSize is always positive +class SwPosSize +{ + sal_uInt16 nWidth; + sal_uInt16 nHeight; +public: + SwPosSize( const sal_uInt16 nW = 0, const sal_uInt16 nH = 0 ) + : nWidth(nW) + , nHeight(nH) + { + } + explicit SwPosSize( const Size &rSize ) + : nWidth(sal_uInt16(rSize.Width())) + ,nHeight(sal_uInt16(rSize.Height())) + { + } +#if defined(__COVERITY__) + ~SwPosSize() COVERITY_NOEXCEPT_FALSE {} +#endif + sal_uInt16 Height() const { return nHeight; } + void Height( const sal_uInt16 nNew ) { nHeight = nNew; } + sal_uInt16 Width() const { return nWidth; } + void Width( const sal_uInt16 nNew ) { nWidth = nNew; } + Size SvLSize() const { return Size( nWidth, nHeight ); } + void SvLSize( const Size &rSize ) + { + nWidth = sal_uInt16(rSize.Width()); + nHeight = sal_uInt16(rSize.Height()); + } + void SvXSize( const Size &rSize ) + { + nHeight = sal_uInt16(rSize.Width()); + nWidth = sal_uInt16(rSize.Height()); + } + SwPosSize& operator=( const Size &rSize ) + { + nWidth = sal_uInt16(rSize.Width()); + nHeight = sal_uInt16(rSize.Height()); + return *this; + } +}; + +#endif + +/* 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..d4be4a1d3 --- /dev/null +++ b/sw/source/core/text/redlnitr.cxx @@ -0,0 +1,926 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + +using namespace ::com::sun::star; + +namespace sw { + +std::unique_ptr<sw::MergedPara> +CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, + FrameMode const eMode) +{ + IDocumentRedlineAccess const& rIDRA = rTextNode.getIDocumentRedlineAccess(); + if (!rFrame.getRootFrame()->IsHideRedlines()) + { + 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 i = rIDRA.GetRedlinePos(rTextNode, RedlineType::Any); + i < rIDRA.GetRedlineTable().size(); ++i) + { + SwRangeRedline const*const pRed = rIDRA.GetRedlineTable()[i]; + + if (pNode->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.GetNode() == &rTextNode && pEnd->nContent.GetIndex() == 0); + continue; // known pathology, ignore it + } + 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(std::u16string_view(pNode->GetText()).substr(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 (sal_uLong 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 (sal_uLong 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(std::u16string_view(pNode->GetText()).substr(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->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* pDoc = 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 = pDoc->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 = pDoc->GetExtTextInput(*pNode); + if (pExtInp) + break; + } + } + } + const bool bShow = IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()) + && pRootFrame && !pRootFrame->IsHideRedlines(); + if (pExtInp || m_pMergedPara || bShow) + { + 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); + assert(SwRedlineTable::npos != nRedlPos || m_pMergedPara->extents.size() <= 1); + } + if (pExtInp || m_pMergedPara || SwRedlineTable::npos != nRedlPos) + { + 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, + m_pMergedPara + ? 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, + sal_uLong 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) + assert(m_eMode == Mode::Hide || m_nNdIdx == nNode); + 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; + + for ( ; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++m_nAct) + { + m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]->CalcStartEnd(m_nNdIdx, m_nStart, m_nEnd); + + if (nNew < m_nEnd) + { + if (nNew >= m_nStart) // only possible candidate + { + m_bOn = true; + const SwRangeRedline *pRed = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]; + + if (m_pSet) + m_pSet->ClearItem(); + else + { + SwAttrPool& rPool = + const_cast<SwDoc&>(m_rDoc).GetAttrPool(); + m_pSet = std::make_unique<SfxItemSet>(rPool, svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{}); + } + + if( 1 < pRed->GetStackCount() ) + FillHints( pRed->GetAuthor( 1 ), pRed->GetType( 1 ) ); + FillHints( pRed->GetAuthor(), pRed->GetType() ); + + SfxWhichIter aIter( *m_pSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + const SfxPoolItem* pItem; + if( ( nWhich < RES_CHRATR_END ) && + ( SfxItemState::SET == m_pSet->GetItemState( nWhich, 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( + sal_uLong const nStartNode, sal_Int32 const nChkStart, + sal_uLong const nEndNode, sal_Int32 nChkEnd) +{ + // 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; + assert(nStartNode == nEndNode); (void) nStartNode; (void) nEndNode; + if( nChkEnd == nChkStart ) // 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 = false; + + for (m_nAct = m_nFirst; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++m_nAct) + { + m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ]->CalcStartEnd( m_nNdIdx, m_nStart, m_nEnd ); + if (nChkEnd < m_nStart) + break; + if (nChkStart <= m_nEnd && (nChkEnd > m_nStart || COMPLETE_STRING == m_nEnd)) + { + bRet = true; + 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::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( new Color( rStyleSettings.GetHighlightColor() ) ); + } + if ( nAttr & ExtTextInputAttr::GrayWaveline ) + rFnt.SetGreyWave( true ); +} + +short SwExtend::Enter(SwFont& rFnt, sal_uLong 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, sal_uLong 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(sal_uLong 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..6fc39e14f --- /dev/null +++ b/sw/source/core/text/redlnitr.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_REDLNITR_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_REDLNITR_HXX + +#include <IDocumentRedlineAccess.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 + sal_uLong 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, sal_uLong 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, + sal_uLong 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, sal_uLong const nNode, sal_Int32 const nNew) + { return m_pFont && Leave_(rFnt, nNode, nNew); } + short Enter(SwFont& rFnt, sal_uLong nNode, sal_Int32 nNew); + sal_Int32 Next(sal_uLong 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 + sal_uLong 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, sal_uLong const nNode, sal_Int32 const nNew) + { + if (m_pExt) return m_pExt->Enter(rFnt, nNode, nNew); + return 0; + } + sal_Int32 NextExtend(sal_uLong 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, sal_uLong 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(sal_uLong nStartNode, sal_Int32 nChkStart, sal_uLong nEndNode, sal_Int32 nChkEnd); + bool LeaveExtend(SwFont& rFnt, sal_uLong 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 ); + } +}; + +#endif + +/* 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..17eedcc17 --- /dev/null +++ b/sw/source/core/text/txtcache.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 "txtcache.hxx" +#include <txtfrm.hxx> +#include "porlay.hxx" + +#include <sfx2/viewsh.hxx> +#include <view.hxx> + +SwTextLine::SwTextLine( SwTextFrame const *pFrame, std::unique_ptr<SwParaPortion> pNew ) : + SwCacheObj( static_cast<void const *>(pFrame) ), + 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>)) + { + ++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..d04a0eb63 --- /dev/null +++ b/sw/source/core/text/txtcache.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_TEXT_TXTCACHE_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_TXTCACHE_HXX + +#include <swcache.hxx> +#include "porlay.hxx" +#include <memory> + +class SwTextFrame; + +class SwTextLine : public SwCacheObj +{ + std::unique_ptr<SwParaPortion> pLine; + + virtual void UpdateCachePos() override; + +public: + SwTextLine( SwTextFrame const *pFrame, std::unique_ptr<SwParaPortion> pNew = nullptr ); + virtual ~SwTextLine() override; + + SwParaPortion *GetPara() { return pLine.get(); } + const SwParaPortion *GetPara() const { return pLine.get(); } + + void SetPara(SwParaPortion* pNew, bool bDelete) + { + if (!bDelete) + (void)pLine.release(); + pLine.reset(pNew); + } +}; + +class SwTextLineAccess : public SwCacheAccess +{ + +protected: + virtual SwCacheObj *NewObj() override; + +public: + explicit SwTextLineAccess( const SwTextFrame *pOwner ); + + SwParaPortion *GetPara(); + + bool IsAvailable() const; +}; + +#endif + +/* 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..b1a3bb121 --- /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; + long nX; + 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() +{ + pFollow.reset(); + pFnt.reset(); +} + +/// SwDropPortion CTor, DTor +SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt, + const sal_uInt16 nDrpHeight, + const sal_uInt16 nDrpDescent, + const sal_uInt16 nDist ) + : nLines( nLineCnt ), + nDropHeight(nDrpHeight), + nDropDescent(nDrpDescent), + nDistance(nDist), + nFix(0), + nY(0) +{ + SetWhichPor( PortionType::Drop ); +} + +SwDropPortion::~SwDropPortion() +{ + 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( nDropHeight && pPart && 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 + nY ); + const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent + 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( ! nDropHeight || ! pPart || 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() + nDropHeight ); + + // for background + const_cast<SwDropPortion*>(this)->Height( nDropHeight + nDropDescent ); + const_cast<SwDropPortion*>(this)->SetAscent( 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( ! nDropHeight || ! pPart || 1 == nLines ) + { + 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, 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; + sal_uInt16 nAscent = 0; + sal_uInt16 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!" ); + sal_uInt16 nAscent = 0; + sal_uInt16 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, 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(); + long nX = 0; + while( pPor && !pPor->IsDropPortion() ) + { + nX = nX + pPor->Width(); + pPor = pPor->GetNextPortion(); + } + Point aLineOrigin( GetTopLeft() ); + + aLineOrigin.AdjustX(nX ); + sal_uInt16 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* aFontCacheId[ DROP_CACHE_SIZE ] = {}; + OUString aText[ DROP_CACHE_SIZE ]; + sal_uInt16 aFactor[ DROP_CACHE_SIZE ]; + sal_uInt16 aWishedHeight[ DROP_CACHE_SIZE ] = {}; + short aDescent[ DROP_CACHE_SIZE ]; + sal_uInt16 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()))); + + long nDescent = 0; + 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 && + ( aText[ nTmpIdx ] != aStr || aFontCacheId[ nTmpIdx ] != nFntCacheId || + 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 ) + { + ++nIndex; + nIndex %= DROP_CACHE_SIZE; + nTmpIdx = nIndex; + + long nWishedHeight = pDrop->GetDropHeight(); + long nAscent = 0; + + // find out biggest font size for initial scaling factor + long nMaxFontHeight = 1; + while ( pCurrPart ) + { + const SwFont& rFnt = pCurrPart->GetFont(); + const long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() ); + if ( nCurrHeight > nMaxFontHeight ) + nMaxFontHeight = nCurrHeight; + + pCurrPart = pCurrPart->GetFollow(); + } + + nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight; + + if ( bUseCache ) + { + // save keys for cache + aFontCacheId[ nTmpIdx ] = nFntCacheId; + aText[ nTmpIdx ] = aStr; + aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight); + // save initial scaling factor + aFactor[ nTmpIdx ] = static_cast<sal_uInt16>(nFactor); + } + + bool bGrow = (pDrop->GetLen() != TextFrameIndex(0)); + + // for growing control + long nMax = USHRT_MAX; + long nMin = 0; +#if OSL_DEBUG_LEVEL > 1 + 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(); + 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 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.setY(aRect.getY() - 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 long nHght = nAscent + nDescent; + if ( nHght ) + { + if ( nHght > nWishedHeight ) + nMax = nFactor; + else + { + if ( bUseCache ) + aFactor[ nTmpIdx ] = static_cast<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 ) + aDescent[ nTmpIdx ] = -short( nDescent ); + } + + pCurrPart = pDrop->GetPart(); + + // did made any new calculations or did we use the cache? + if ( -1 == nFactor ) + { + nFactor = aFactor[ nTmpIdx ]; + nDescent = 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; + nFix = static_cast<sal_uInt16>(rInf.X()); + + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + if( nDropHeight && pPart && nLines!=1 ) + { + if( !pDropCapCache ) + pDropCapCache = new SwDropCapCache; + + // adjust font sizes to fit into the rectangle + pDropCapCache->CalcFontSize( this, rInf ); + + const long nOldX = rInf.X(); + { + SwDropSave aSave( rInf ); + SwDropPortionPart* pCurrPart = 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( static_cast<sal_uInt16>(nTmpWidth) ); + + // Move + rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() ); + rInf.X( rInf.X() + nTmpWidth ); + pCurrPart = pCurrPart->GetFollow(); + } + SetJoinBorderWithNext(false); + SetJoinBorderWithPrev(false); + Width( static_cast<sal_uInt16>(rInf.X() - nOldX) ); + } + + // reset my length + SetLen( rInf.GetLen() ); + + // Quit when Flys are overlapping + if( ! bFull ) + bFull = lcl_IsDropFlyInter( rInf, Width(), nDropHeight ); + + if( bFull ) + { + // FormatText could have caused nHeight to be 0 + if ( !Height() ) + Height( rInf.GetTextHeight() ); + + // And now for another round + nDropHeight = nLines = 0; + 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 ) + nDistance = 0; + else + { + const sal_uInt16 nWant = Width() + GetDistance(); + const sal_uInt16 nRest = static_cast<sal_uInt16>(rInf.Width() - rInf.X()); + if( ( nWant > nRest ) || + lcl_IsDropFlyInter( rInf, Width() + GetDistance(), nDropHeight ) ) + nDistance = 0; + + Width( Width() + 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..4eac15a48 --- /dev/null +++ b/sw/source/core/text/txtfld.cxx @@ -0,0 +1,737 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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_PAGECOOUNTFLD; + 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) + { + pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix); + } + 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 ) + { + const OUString aNothing; + pRet = new SwFieldPortion( aNothing ); + 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(static_cast<SwFormatAutoFormat const&>(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 + && pHint->GetStart() == *pHint->End()) + { + pSet = pHint->GetAutoFormat().GetStyleHandle(); + // When we find an empty hint (start == end) we got what we are looking for. + break; + } + } + } + + // TODO: apparently Word can apply Character Style too, see testParagraphMark + + // Check each item and in case it should be ignored, then clear it. + if (pSet) + { + 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) + && !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 + 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 (pItem->Which() != RES_CHRATR_BACKGROUND) + { + if (SwTextNode::IsIgnoredCharFormatForNumbering(pItem->Which())) + pCleanedSet->ClearItem(pItem->Which()); + else if (pFormat && pFormat->HasItem(pItem->Which())) + pCleanedSet->ClearItem(pItem->Which()); + } + pItem = aIter.NextItem(); + }; + pNumFnt->SetDiffFnt(pCleanedSet.get(), pIDSA); + } +} + +static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode ) +{ + const SwDoc* pDoc = rTextNode.GetDoc(); + SwRedlineTable::size_type nRedlPos = pDoc->getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any ); + + if( SwRedlineTable::npos != nRedlPos ) + { + const sal_uLong nNdIdx = rTextNode.GetIndex(); + for( ; nRedlPos < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos ) + { + const SwRangeRedline* pTmp = pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ 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 ) + return pTmp; + } + } + } + return nullptr; +} + +static void lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, std::unique_ptr<SwFont>& pNumFnt ) +{ + if ( !rInf.GetVsh()->GetLayout()->IsHideRedlines() ) + { + const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode ); + if (pRedlineNum) + { + std::unique_ptr<SfxItemSet> pSet; + + SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool(); + pSet = std::make_unique<SfxItemSet>(rPool, svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{}); + + std::size_t aAuthor = (1 < pRedlineNum->GetStackCount()) + ? pRedlineNum->GetAuthor( 1 ) + : pRedlineNum->GetAuthor(); + + if ( RedlineType::Delete == pRedlineNum->GetType() ) + SW_MOD()->GetDeletedAuthorAttr(aAuthor, *pSet); + else + SW_MOD()->GetInsertAuthorAttr(aAuthor, *pSet); + + const SfxPoolItem* pItem = nullptr; + if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_COLOR, true, &pItem)) + pNumFnt->SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue()); + if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_UNDERLINE, true, &pItem)) + pNumFnt->SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle()); + if (SfxItemState::SET == pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true, &pItem)) + pNumFnt->SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() ); + } + } +} + +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 ); + long nTmpA = rInf.GetLast()->GetAscent(); + 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 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 + { + OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame()) ); + if ( !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 )); + + // #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); + + lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ); + + // 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..52979fde8 --- /dev/null +++ b/sw/source/core/text/txtfly.cxx @@ -0,0 +1,1407 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "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 <pagedesc.hxx> +#include <sortedobjs.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentSettingAccess.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() : + nPntCnt( 0 ) +{ +} + +SwContourCache::~SwContourCache() +{ +} + +void SwContourCache::ClrObject( sal_uInt16 nPos ) +{ + nPntCnt -= 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->nPntCnt = 0; + } +} + +// #i68520# +SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj, + const SwRect &rLine, + const SwTextFrame* pFrame, + const 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 && + ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr || + ( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() && + static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) ) + { + aRet = pAnchoredObj->GetObjRectWithSpaces(); + if( aRet.IsOver( 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 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 ) + { + nPntCnt -= mvItems.back().mxTextRanger->GetPointCount(); + mvItems.pop_back(); + } + ::basegfx::B2DPolyPolygon aPolyPolygon; + std::unique_ptr<::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( static_cast<const SwVirtFlyDrawObj*>(pObj)-> + GetFlyFrame()->getFrameArea().SVRect() ); + aPolyPolygon.clear(); + aPolyPolygon.append(aPoly.getB2DPolyPolygon()); + } + else + { + if( dynamic_cast< const E3dObject *>( pObj ) == nullptr ) + { + aPolyPolygon = pObj->TakeXorPoly(); + } + + ::basegfx::B2DPolyPolygon aContourPoly(pObj->TakeContour()); + pPolyPolygon.reset(new ::basegfx::B2DPolyPolygon(aContourPoly)); + } + 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.get(), 20, + static_cast<sal_uInt16>(rLRSpace.GetLeft()), static_cast<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(); + + nPntCnt += mvItems[0].mxTextRanger->GetPointCount(); + while( nPntCnt > POLY_MAX && mvItems.size() > POLY_MIN ) + { + nPntCnt -= 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); + long nTmpTop = aRectFnSet.GetTop(rLine); + // fnGetBottom is top + height + long nTmpBottom = aRectFnSet.GetBottom(rLine); + + Range aRange( std::min( nTmpTop, nTmpBottom ), std::max( nTmpTop, nTmpBottom ) ); + + std::deque<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() + : pPage(nullptr) + , mpCurrAnchoredObj(nullptr) + , m_pCurrFrame(nullptr) + , m_pMaster(nullptr) + , nMinBottom(0) + , nNextTop(0) + , m_nCurrFrameNodeIndex(0) + , bOn(false) + , bTopRule(false) + , mbIgnoreCurrentFrame(false) + , mbIgnoreContour(false) + , mbIgnoreObjsInHeaderFooter(false) + +{ +} + +SwTextFly::SwTextFly( const SwTextFrame *pFrame ) +{ + CtorInitTextFly( pFrame ); +} + +SwTextFly::SwTextFly( const SwTextFly& rTextFly ) +{ + pPage = rTextFly.pPage; + mpCurrAnchoredObj = rTextFly.mpCurrAnchoredObj; + m_pCurrFrame = rTextFly.m_pCurrFrame; + m_pMaster = rTextFly.m_pMaster; + if( rTextFly.mpAnchoredObjList ) + { + mpAnchoredObjList.reset( new SwAnchoredObjList( *(rTextFly.mpAnchoredObjList) ) ); + } + + bOn = rTextFly.bOn; + bTopRule = rTextFly.bTopRule; + nMinBottom = rTextFly.nMinBottom; + nNextTop = rTextFly.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; + 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(); + bOn = pPage->GetSortedObjs() != nullptr; + bTopRule = true; + nMinBottom = 0; + nNextTop = 0; + m_nCurrFrameNodeIndex = ULONG_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( 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( 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 = 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.IsOver( 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( !bTopRule, "DrawTextOpaque: Wrong TopRule" ); + + // #i68520# + const SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); + if (nCount > 0) + { + const SdrLayerID nHellId = pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId(); + for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i ) + { + // #i68520# + const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i]; + if( dynamic_cast<const SwFlyFrame*>(pTmpAnchoredObj) && + mpCurrAnchoredObj != pTmpAnchoredObj ) + { + // #i68520# + const SwFlyFrame& rFly = dynamic_cast<const SwFlyFrame&>(*pTmpAnchoredObj); + if( aRegion.GetOrigin().IsOver( rFly.getFrameArea() ) ) + { + const SwFrameFormat *pFormat = rFly.GetFormat(); + const SwFormatSurround &rSur = pFormat->GetSurround(); + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + // Only the ones who are opaque and more to the top + if( ! rFly.IsBackgroundTransparent() && + css::text::WrapTextMode_THROUGH == rSur.GetSurround() && + ( !rSur.IsAnchorOnly() || + // #i68520# + GetMaster() == rFly.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 = + rFly.Lower() && rFly.Lower()->IsNoTextFrame() + ? static_cast<const SwNoTextFrame*>(rFly.Lower()) + : nullptr; + if ( !pNoText || + (!pNoText->IsTransparent() && !rSur.IsContour()) ) + { + bOpaque = true; + aRegion -= rFly.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( !bTopRule, "DrawFlyRect: Wrong TopRule" ); + // #i68520# + const SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); + if (nCount > 0) + { + const SdrLayerID nHellId = 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 = dynamic_cast<const SwFlyFrame*>(pAnchoredObjTmp); + 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, 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 ) && 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( dynamic_cast<const SwFlyFrame*>(mpCurrAnchoredObj), 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 ( !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.IsOver( 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. + sal_uLong 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 (ULONG_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 = pPage->GetSortedObjs(); + const size_t nCount = pSorted ? pSorted->size() : 0; + // --> #108724# Page header/footer content doesn't have to wrap around + // floating screen objects + 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 bWrapAllowed = ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) || + (!m_pCurrFrame->IsInFootnote() && !bFooterHeader)); + + 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 long nRight = aRectFnSet.GetRight(aRect) - 1; + const 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() ) ) + { + 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(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() ) + nMinBottom = ( aRectFnSet.IsVert() && nMinBottom ) ? + std::min( nMinBottom, aBound.Left() ) : + std::max( nMinBottom, aRectFnSet.GetBottom(aBound) ); + } + + bOn = true; + } + } + if( nMinBottom ) + { + SwTwips nMax = aRectFnSet.GetPrtBottom(*m_pCurrFrame->GetUpper()); + if( aRectFnSet.YDiff( nMinBottom, nMax ) > 0 ) + 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, aBound.Bottom() ); + } + } + } + SwTwips nMax = m_pCurrFrame->GetUpper()->getFrameArea().Top() + + m_pCurrFrame->GetUpper()->getFramePrintArea().Bottom(); + if( nRet > nMax ) + nRet = nMax; + } + 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(pPage->getFrameArea()) < aRectFnSet.GetHeight(rRect)) + { + // get the doc model description + const SwPageDesc* pPageDesc = 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( 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.IsOver( 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.IsOver( 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().IsOver( 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 long nTmpTop = aRectFnSet.GetTop(aTmp); + if( aRectFnSet.YDiff( nTmpTop, aRectFnSet.GetTop(aLine) ) > 0 ) + { + if( aRectFnSet.YDiff( 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.IsOver( 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().IsOver( 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.IsOver( 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 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 long nCurrLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame); + const long nCurrRight = aRectFnSet.GetPrtRight(*m_pCurrFrame); + const SwRect& aRect( pAnchoredObj->GetObjRectWithSpaces() ); + long nFlyLeft = aRectFnSet.GetLeft(aRect); + long nFlyRight = aRectFnSet.GetRight(aRect); + + if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight ) + eSurroundForTextWrap = css::text::WrapTextMode_PARALLEL; + else + { + long nLeft = nFlyLeft - nCurrLeft; + 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( 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..2d585994e --- /dev/null +++ b/sw/source/core/text/txtfrm.cxx @@ -0,0 +1,4024 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + + +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, sal_uLong const nNodeIndex) + { + if (rFrame.IsTextFrame()) + { + SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(rFrame)); + if (sw::MergedPara const*const pMerged = rTextFrame.GetMergedPara()) + { + sal_uLong const nFirst(pMerged->pFirstNode->GetIndex()); + sal_uLong 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.IsHideRedlines()) + { + 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.IsHideRedlines()) + { + 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->IsHideRedlines()) + { + 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"); + SfxItemSet firstSet(*rFormatSet.GetPool(), + svl::Items<RES_PAGEDESC, RES_BREAK>{}); + 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); + } + SfxItemSet propsSet(*rFormatSet.GetPool(), + svl::Items<RES_PARATR_BEGIN, RES_PAGEDESC, + RES_BREAK+1, RES_FRMATR_END, + XATTR_FILL_FIRST, XATTR_FILL_LAST+1>{}); + 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 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 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 long nPrtWidth = aPrt.Width(); + aPrt.Width( aPrt.Height() ); + aPrt.Height( nPrtWidth ); + } + + { + const 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 + 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 long nWidth = rRect.Width(); + const 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 long nOfstX = rPoint.X() - getFrameArea().Left(); + const 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. + */ +long SwTextFrame::SwitchHorizontalToVertical( 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 +{ + 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() ); + } + + 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 long nWidth = rRect.Height(); + const 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 +{ + 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(); + } + + 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. + */ +long SwTextFrame::SwitchVerticalToHorizontal( 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)); + + 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 ? + ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl : + ComplexTextLayoutFlags::BiDiStrong ); +} + +void SwLayoutModeModifier::SetAuto() +{ + const ComplexTextLayoutFlags nNewLayoutMode = m_nOldLayoutMode & ~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; + sal_uLong 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); + } + } + } + + 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().copy(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().copy(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().copy(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().copy(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().copy(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<SwModify*>( + 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<SwModify*>(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 + } + } +} + +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 long nD) +{ + if ( IsIdxInside( aRange.Start(), aRange.Len() ) ) + InvalidateRange_( aRange, nD ); +} + +void SwTextFrame::InvalidateRange_( const SwCharRange &aRange, const 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->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 ) + { + 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( new SwWrongList( WRONGLIST_SPELL ) ); + pTextNode->GetWrong()->SetInvalid( nPos, nEnd ); + } + if ( !pTextNode->GetSmartTags() && !pTextNode->IsSmartTagDirty() ) + { + pTextNode->SetSmartTags( new 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()) + { + 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 + */ +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; +} + +// 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::Modify() 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); + + if (auto const pHint = dynamic_cast<sw::LegacyModifyHint const*>(&rHint)) + { + pOld = pHint->m_pOld; + pNew = pHint->m_pNew; + } + 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)); + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + + // 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::Modify( 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(std::abs(static_cast<long>(rNode.GetIndex()) - static_cast<long>(pMoveText->pDestNode->GetIndex())) == 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); + 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_SetWrong( *this, rNode, nNPos, nNLen, true ); + lcl_SetScriptInval( *this, nPos ); + bSetFieldsDirty = true; + lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>); + } + 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( isA11yRelevantAttribute( pNewUpdate->getWhichAttr() ) && + hasA11yRelevantAttribute( pNewUpdate->getFmtAttrs() ) ) + { + SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr; + if ( pViewSh ) + { + pViewSh->InvalidateAccessibleParaAttrs( *this ); + } + } + } + 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(); + const SfxPoolItem* pItem = nullptr; + int nClear = 0; + sal_uInt16 nCount = rNewSet.Count(); + + if( SfxItemState::SET == rNewSet.GetItemState( RES_TXTATR_FTN, false, &pItem )) + { + nPos = MapModelToView(&rNode, + static_cast<const SwFormatFootnote*>(pItem)->GetTextFootnote()->GetStart()); + if (IsIdxInside(nPos, TextFrameIndex(1))) + Prepare( PrepareHint::FootnoteInvalidation, pNew ); + nClear = 0x01; + --nCount; + } + + if( SfxItemState::SET == rNewSet.GetItemState( RES_TXTATR_FIELD, false, &pItem )) + { + nPos = MapModelToView(&rNode, + static_cast<const SwFormatField*>(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 ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) != nullptr ) + { + SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj); + 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::Modify( &aOldSet, &aNewSet ); + } + } + else + SwContentFrame::Modify( pOld, pNew ); + } + + if (isA11yRelevantAttribute(nWhich)) + { + SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr; + if ( pViewSh ) + { + pViewSh->InvalidateAccessibleParaAttrs( *this ); + } + } + } + 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; + if ( nullptr != (pNxt = FindNext()) ) + pNxt->InvalidatePrt(); + } + } + } // switch + + if( bSetFieldsDirty ) + GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode, 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? + bFormat = aTextFly.Relax() || IsUndersized(); + } + } + } + } + + 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; + if ( nullptr != ( pNxt = GetIndNext() ) ) + { + 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( 0L , 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 ); +} + +/** + * 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 ) +{ + 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 ); + else + { + // we need the total height including the current line + aLine.Top(); + do + { + rMaxHeight -= aLine.GetLineHeight(); + } while ( aLine.Next() ); + } + + return bRet; +} + +sal_uInt16 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 = static_cast<sal_uInt16>(getFramePrintArea().SSize().Height()); + if( IsUndersized() ) + { + if( IsEmpty() || GetText().isEmpty() ) + nRet = static_cast<sal_uInt16>(EmptyHeight()); + else + ++nRet; + } + return nRet; + } + + // TODO: Refactor and improve code + const SwLineLayout* pLineLayout = GetPara(); + sal_uInt16 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()) + { + int nListLevel = pTextNode->GetActualListLevel(); + + if (nListLevel < 0) + nListLevel = 0; + + if (nListLevel >= MAXLEVEL) + nListLevel = MAXLEVEL - 1; + + const SwNumFormat& rNumFormat = + pTextNode->GetNumRule()->Get( static_cast<sal_uInt16>(nListLevel) ); + if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + // 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 + */ +long SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace ) const +{ + 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(); + + 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() ? static_cast<sal_uInt16>(getFramePrintArea().Width()) : static_cast<sal_uInt16>(getFramePrintArea().Height()); + return USHRT_MAX; + } + const SwParaPortion *pPara = GetPara(); + if ( !pPara ) + return USHRT_MAX; + + return pPara->Height(); +} + +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_uLong nNew = 0; + const SwLineNumberInfo &rInf = GetDoc().GetLineNumberInfo(); + if ( !GetText().isEmpty() && HasPara() ) + { + SwTextSizeInfo aInf( this ); + SwTextMargin aLine( this, &aInf ); + if ( rInf.IsCountBlankLines() ) + { + aLine.Bottom(); + nNew = static_cast<sal_uLong>(aLine.GetLineNr()); + } + else + { + do + { + if( aLine.GetCurr()->HasContent() ) + ++nNew; + } while ( aLine.NextLine() ); + } + } + else if ( rInf.IsCountBlankLines() ) + nNew = 1; + + if ( nNew != mnThisLines ) + { + if (!IsInTab() && GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber().IsCount()) + { + mnAllLines -= mnThisLines; + mnThisLines = nNew; + mnAllLines += mnThisLines; + SwFrame *pNxt = GetNextContentFrame(); + while( pNxt && pNxt->IsInTab() ) + { + if( nullptr != (pNxt = pNxt->FindTabFrame()) ) + 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() ) + { + 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 ) + { + 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 ); + } +} + +/* 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..73aff1cf5 --- /dev/null +++ b/sw/source/core/text/txtftn.cxx @@ -0,0 +1,1553 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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 <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() ); + + if ( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) == nullptr || + static_cast<SwFlyFrame*>(pAnchoredObj)->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() + SwTwips(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); + 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" ); + + if( !m_pFrame->IsFootnoteAllowed() ) + return nullptr; + + SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint); + 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 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() ); + + 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(); + long nDiff = nLastLeft - nQuoWidth; + if( nDiff < 0 ) + { + nLastLeft = pQuo->GetAscent(); + nQuoWidth = static_cast<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 *pInf; + SwFont *pFnt; + std::unique_ptr<SwFont> 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 ) + : pInf( &const_cast<SwTextSizeInfo&>(rInf) ) + , pFnt( nullptr ) +{ + if( pTextFootnote && rInf.GetTextFrame() ) + { + pFnt = const_cast<SwTextSizeInfo&>(rInf).GetFont(); + pOld.reset( new SwFont( *pFnt ) ); + pOld->GetTox() = pFnt->GetTox(); + pFnt->GetTox() = 0; + SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pTextFootnote->GetFootnote()); + const SwDoc *const pDoc = &rInf.GetTextFrame()->GetDoc(); + + // #i98418# + if ( bApplyGivenScriptType ) + { + pFnt->SetActual( nGivenScriptType ); + } + else + { + // examine text and set script + OUString aTmpStr(rFootnote.GetViewNumStr(*pDoc, rInf.GetTextFrame()->getRootFrame())); + 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(); + pFnt->SetDiffFnt( &rSet, &pDoc->getIDocumentSettingAccess() ); + + // we reduce footnote size, if we are inside a double line portion + if ( ! pOld->GetEscapement() && 50 == pOld->GetPropr() ) + { + Size aSize = pFnt->GetSize( pFnt->GetActual() ); + pFnt->SetSize( Size( aSize.Width() / 2, + aSize.Height() / 2 ), + pFnt->GetActual() ); + } + + // set the correct rotation at the footnote font + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_ROTATE, + true, &pItem )) + pFnt->SetVertical( static_cast<const SvxCharRotateItem*>(pItem)->GetValue(), + rInf.GetTextFrame()->IsVertical() ); + + pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() ); + + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_BACKGROUND, + true, &pItem )) + pFnt->SetBackColor( new Color( static_cast<const SvxBrushItem*>(pItem)->GetColor() ) ); + } + else + pFnt = nullptr; +} + +SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE +{ + if( pFnt ) + { + // Put back SwFont + *pFnt = *pOld; + pFnt->GetTox() = pOld->GetTox(); + pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() ); + pOld.reset(); + } +} + +SwFootnotePortion::SwFootnotePortion( const OUString &rExpand, + SwTextFootnote *pFootn, sal_uInt16 nReal ) + : SwFieldPortion( rExpand, nullptr ) + , pFootnote(pFootn) + , 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, 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, 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, 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, aErgo ); +} + +SwQuoVadisPortion::SwQuoVadisPortion( const OUString &rExp, const OUString& rStr ) + : SwFieldPortion( rExp ), 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 += aErgo; + return true; +} + +void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Special( GetLen(), m_aExpand + 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, OUString() ); +} + +SwErgoSumPortion::SwErgoSumPortion(const OUString &rExp, const OUString& 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( const OUString &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..215cc019b --- /dev/null +++ b/sw/source/core/text/txthyph.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 <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() ); +} + +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 = aExpand; + return true; +} + +void SwHyphStrPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Special( GetLen(), aExpand, GetWhichPor() ); +} + +SwLinePortion *SwSoftHyphPortion::Compress() { return this; } + +SwSoftHyphPortion::SwSoftHyphPortion() : + bExpand(false), 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( !nViewWidth ) + const_cast<SwSoftHyphPortion*>(this)->nViewWidth + = rInf.GetTextSize(OUString('-')).Width(); + } + else + const_cast<SwSoftHyphPortion*>(this)->nViewWidth = 0; + return 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() ) + { + 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( const OUString &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..e2292d70b --- /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( pOut && bChg ) + { + if ( pOut->GetConnectMetaFile() ) + pOut->Pop(); + else + { + if( bOn ) + pOut->SetClipRegion( aClip ); + else + pOut->SetClipRegion(); + } + 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 ( !pOut || (!rRect.HasArea() && !pOut->IsClipRegion()) ) + { + const_cast<SwRect&>(rRect) = aOldRect; + return; + } + + if ( !bChg ) + { + if ( pOut->GetConnectMetaFile() ) + pOut->Push(); + else if ( bOn ) + aClip = pOut->GetClipRegion(); + } + + if ( !rRect.HasArea() ) + 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( pOut->IsClipRegion() ) // no && because of Mac + { + if ( aRect == pOut->GetClipRegion().GetBoundRect() ) + { + const_cast<SwRect&>(rRect) = aOldRect; + return; + } + } + + if( SwRootFrame::HasSameRect( rRect ) ) + pOut->SetClipRegion(); + else + { + const vcl::Region aClipRegion( aRect ); + pOut->SetClipRegion( aClipRegion ); + } + } + 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..0c8e0466b --- /dev/null +++ b/sw/source/core/text/txtpaint.hxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_TXTPAINT_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_TXTPAINT_HXX +#include <vcl/outdev.hxx> + +class SwRect; // SwSaveClip +class SwTextFrame; + +class SwSaveClip final +{ + vcl::Region aClip; + const bool bOn; + bool bChg; + + VclPtr<OutputDevice> pOut; + void ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame, + bool bEnlargeRect, + sal_Int32 nEnlargeTop, + sal_Int32 nEnlargeBottom ); +public: + explicit SwSaveClip(OutputDevice* pOutDev) + : bOn(pOutDev && pOutDev->IsClipRegion()) + , bChg(false) + , pOut(pOutDev) + { + } + + ~SwSaveClip(); + void ChgClip( const SwRect &rRect, const SwTextFrame* pFrame = nullptr, + bool bEnlargeRect = false, + sal_Int32 nEnlargeTop = 0, + sal_Int32 nEnlargeBottom = 0) + { if( pOut ) ChgClip_( rRect, pFrame, + bEnlargeRect, nEnlargeTop, nEnlargeBottom ); } + bool IsOn() const { return bOn; } + bool IsChg() const { return 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 + +#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..a38cbe048 --- /dev/null +++ b/sw/source/core/text/txttab.cxx @@ -0,0 +1,600 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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, const SwTwips nRight ) const +{ + for( sal_uInt16 i = 0; i < pRuler->Count(); ++i ) + { + const SvxTabStop &rTabStop = pRuler->operator[](i); + if( rTabStop.GetTabPos() > SwTwips(nRight) ) + return i ? nullptr : &rTabStop; + + if( rTabStop.GetTabPos() > nSearchPos ) + return &rTabStop; + } + return nullptr; +} + +sal_uInt16 SwLineInfo::NumberOfTabStops() const +{ + return pRuler->Count(); +} + +SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const +{ + 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 bool bTabsRelativeToIndent = + m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT); + 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; + + // #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 SvxTabStop* pTabStop = m_aLineInf.GetTabStop( nSearchPos, nMyRight ); + 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; + } + 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 = static_cast<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 + { + 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; + } + } + } + } + + 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 ) + : SwFixPortion(), nTabPos(nTabPosition), cFill(cFillChar), bAutoTabStop( bAutoTab ) +{ + nLineLength = TextFrameIndex(1); + OSL_ENSURE(!IsFilled() || ' ' != cFill, "SwTabPortion::CTOR: blanks ?!"); + SetWhichPor( PortionType::Table ); +} + +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( static_cast<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); + + // 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 sal_uInt16 nDir = rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() ); + + if( ! bFull && 0 == 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 && !bAutoTabStop && GetTabPos() > rInf.Width() ) + { + rInf.SetLastTab( this ); + break; + } + + PrtWidth( static_cast<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() || !bAutoTabStop ) ) || bAtParaEnd ) && + GetTabPos() >= rInf.GetTextFrame()->getFrameArea().Width() ) + { + bFull = false; + if ( bTabOverflow && !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( static_cast<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 ) +{ + const bool bTabOverMargin = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN); + // 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() : 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 && 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 && 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::Table ); + 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; + comphelper::string::padToLength(aBuf, nChar, ' '); + rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0), + TextFrameIndex(nChar), true); + } + } + + // Display fill characters + if( IsFilled() ) + { + // Tabs with filling/filled tabs + const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(cFill)).Width(); + OSL_ENSURE( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" ); + + // Robust: + if( nCharWidth ) + { + // Always with kerning, also on printer! + sal_uInt16 nChar = Width() / nCharWidth; + if ( cFill == '_' ) + ++nChar; // to avoid gaps + OUStringBuffer aBuf; + comphelper::string::padToLength(aBuf, nChar, 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..c38aab287 --- /dev/null +++ b/sw/source/core/text/widorp.cxx @@ -0,0 +1,544 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 (!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 ), nWidLines( 0 ), 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() ) + nWidLines = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetWidows().GetValue(); + } + else + { + const SwAttrSet& rSet = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet(); + const SvxOrphansItem &rOrph = rSet.GetOrphans(); + if ( rOrph.GetValue() > 1 ) + nOrphLines = rOrph.GetValue(); + if ( m_pFrame->IsFollow() ) + nWidLines = rSet.GetWidows().GetValue(); + + } + + if ( m_bKeep || nWidLines || nOrphLines ) + { + 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; + nOrphLines = 0; + 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 = nOrphLines; + if( bHasToFit ) + 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; + } + 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( !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() >= 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() > 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 >= + SwTwips(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 >= SwTwips(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 ) +{ + // 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(); + + while( nMinLines > rLine.GetLineNr() ) + { + if( !rLine.NextLine() ) + return false; + nLineSum += rLine.GetLineHeight(); + } + + // We do not fit + if( !IsInside( rLine ) ) + return false; + + // Check the Widows-rule + if( !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(); + 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 >= 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..9c0e37fc2 --- /dev/null +++ b/sw/source/core/text/widorp.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_TEXT_WIDORP_HXX +#define INCLUDED_SW_SOURCE_CORE_TEXT_WIDORP_HXX +class SwTextFrame; + +#include <swtypes.hxx> +#include "itrtxt.hxx" + +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 nWidLines, nOrphLines; + +public: + WidowsAndOrphans( SwTextFrame *pFrame, const SwTwips nRst = 0, + bool bCheckKeep = true ); + bool FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine ); + sal_uInt16 GetOrphansLines() const + { return nOrphLines; } + void ClrOrphLines(){ nOrphLines = 0; } + + bool FindBreak( SwTextFrame *pFrame, SwTextMargin &rLine, bool bHasToFit ); + bool WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTest ); + // 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() > nOrphLines ) { + return IsBreakNow( rLine ); + } + return false; + } +}; + +#endif + +/* 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..5a70c7be9 --- /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), 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), 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 static_cast<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; +} + +SwWrongList* SwWrongList::SplitList( sal_Int32 nSplitPos ) +{ + SwWrongList *pRet = nullptr; + 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 = new SwGrammarMarkUp(); + else + pRet = 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 >= static_cast<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 = static_cast<sal_uInt16>(std::distance(aDelIter, aIter)); + if( nDel ) + { + auto nDelPos = static_cast<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..b551c2842 --- /dev/null +++ b/sw/source/core/text/xmldump.cxx @@ -0,0 +1,567 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <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> + +namespace { + +class XmlPortionDumper:public SwPortionHandler +{ + private: + xmlTextWriterPtr writer; + TextFrameIndex ofs; + const OUString& m_rText; + OUString m_aLine; + + static const char* getTypeName( 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::FieldMark: return "PortionType::FieldMark"; + case PortionType::FieldFormCheckbox: return "PortionType::FieldFormCheckbox"; + + 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::Table: return "PortionType::Table"; + + 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"; + } + } + + public: + + explicit XmlPortionDumper( xmlTextWriterPtr some_writer, const OUString& rText ):writer( some_writer ), 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 + { + xmlTextWriterStartElement( writer, BAD_CAST( "Text" ) ); + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nLength" ), + "%i", static_cast<int>(static_cast<sal_Int32>(nLength)) ); + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nType" ), + "%s", getTypeName( nType ) ); + if (nHeight > 0) + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("nHeight"), "%i", static_cast<int>(nHeight)); + if (nWidth > 0) + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("nWidth"), "%i", static_cast<int>(nWidth)); + if (nLength > TextFrameIndex(0)) + xmlTextWriterWriteAttribute(writer, BAD_CAST("Portion"), + BAD_CAST(m_rText.copy(sal_Int32(ofs), sal_Int32(nLength)).toUtf8().getStr())); + + xmlTextWriterEndElement( writer ); + m_aLine += m_rText.copy(sal_Int32(ofs), sal_Int32(nLength)); + 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 + { + xmlTextWriterStartElement( writer, BAD_CAST( "Special" ) ); + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nLength" ), + "%i", static_cast<int>(static_cast<sal_Int32>(nLength)) ); + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nType" ), + "%s", getTypeName( nType ) ); + OString sText8 = OUStringToOString( rText, RTL_TEXTENCODING_UTF8 ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rText" ), + "%s", sText8.getStr( ) ); + + if (nHeight > 0) + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("nHeight"), "%i", static_cast<int>(nHeight)); + + if (nWidth > 0) + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("nWidth"), "%i", static_cast<int>(nWidth)); + + if (pFont) + pFont->dumpAsXml(writer); + + xmlTextWriterEndElement( writer ); + m_aLine += rText; + ofs += nLength; + } + + virtual void LineBreak( sal_Int32 nWidth ) override + { + xmlTextWriterStartElement( writer, BAD_CAST( "LineBreak" ) ); + if (nWidth > 0) + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nWidth" ), + "%i", static_cast<int>(nWidth) ); + if (!m_aLine.isEmpty()) + { + xmlTextWriterWriteAttribute(writer, BAD_CAST("Line"), + BAD_CAST(m_aLine.toUtf8().getStr())); + m_aLine.clear(); + } + xmlTextWriterEndElement( writer ); + } + + /** + * @param nLength + * number of 'model string' characters to be skipped + */ + virtual void Skip( TextFrameIndex nLength ) override + { + xmlTextWriterStartElement( writer, BAD_CAST( "Skip" ) ); + xmlTextWriterWriteFormatAttribute( writer, + BAD_CAST( "nLength" ), + "%i", static_cast<int>(static_cast<sal_Int32>(nLength)) ); + xmlTextWriterEndElement( writer ); + ofs += nLength; + } + + virtual void Finish( ) override + { + xmlTextWriterStartElement( writer, BAD_CAST( "Finish" ) ); + xmlTextWriterEndElement( writer ); + } + +}; + + xmlTextWriterPtr lcl_createDefaultWriter() + { + xmlTextWriterPtr writer = xmlNewTextWriterFilename( "layout.xml", 0 ); + xmlTextWriterSetIndent(writer,1); + xmlTextWriterSetIndentString(writer, BAD_CAST(" ")); + xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr ); + return writer; + } + + void lcl_freeWriter( xmlTextWriterPtr writer ) + { + 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 ) + { + xmlTextWriterStartElement( writer, reinterpret_cast<const xmlChar *>(name) ); + + dumpAsXmlAttributes( writer ); + + if (IsRootFrame()) + { + const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(this); + 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>)); + } + xmlTextWriterEndElement(writer); + } + + if (IsPageFrame()) + { + const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(this); + xmlTextWriterStartElement(writer, BAD_CAST("page_status")); + xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyLayout()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyContent()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyInCnt"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyInCnt()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidLayout()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidContent()).getStr())); + xmlTextWriterEndElement(writer); + xmlTextWriterStartElement(writer, BAD_CAST("page_info")); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", pPageFrame->GetPhyPageNum()); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", pPageFrame->GetVirtPageNum()); + OUString aFormatName = pPageFrame->GetPageDesc()->GetName(); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr())); + xmlTextWriterEndElement(writer); + } + + if (IsTextFrame()) + { + const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this); + sw::MergedPara const*const pMerged(pTextFrame->GetMergedPara()); + if (pMerged) + { + xmlTextWriterStartElement( writer, BAD_CAST( "merged" ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIuUINTPTR, pMerged->pParaPropsNode->GetIndex() ); + for (auto const& e : pMerged->extents) + { + xmlTextWriterStartElement( writer, BAD_CAST( "extent" ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIuUINTPTR, e.pNode->GetIndex() ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "start" ), "%" SAL_PRIdINT32, e.nStart ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "end" ), "%" SAL_PRIdINT32, e.nEnd ); + xmlTextWriterEndElement( writer ); + } + xmlTextWriterEndElement( writer ); + } + } + + if (IsCellFrame()) + { + SwCellFrame const* pCellFrame(static_cast<SwCellFrame const*>(this)); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", pCellFrame->GetLayoutRowSpan() ); + } + + xmlTextWriterStartElement( writer, BAD_CAST( "infos" ) ); + dumpInfosAsXml( writer ); + xmlTextWriterEndElement( writer ); + + // Dump Anchored objects if any + const SwSortedObjs* pAnchored = GetDrawObjs(); + if ( pAnchored && pAnchored->size() > 0 ) + { + xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) ); + + for (SwAnchoredObject* pObject : *pAnchored) + { + pObject->dumpAsXml( writer ); + } + + 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 ); + xmlTextWriterWriteString( writer, + reinterpret_cast<const xmlChar *>(aText8.getStr( )) ); + XmlPortionDumper pdumper( writer, aText ); + pTextFrame->VisitPortions( pdumper ); + + } + else + { + dumpChildrenAsXml( writer ); + } + xmlTextWriterEndElement( writer ); + } + + if ( bCreateWriter ) + lcl_freeWriter( writer ); +} + +void SwFrame::dumpInfosAsXml( xmlTextWriterPtr writer ) const +{ + // output the Frame + xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) ); + getFrameArea().dumpAsXmlAttributes(writer); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFixSize"), BAD_CAST(OString::boolean(HasFixSize()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidPos"), BAD_CAST(OString::boolean(isFrameAreaPositionValid()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidSize"), BAD_CAST(OString::boolean(isFrameAreaSizeValid()).getStr())); + xmlTextWriterWriteAttribute(writer, BAD_CAST("mbValidPrtArea"), BAD_CAST(OString::boolean(isFramePrintAreaValid()).getStr())); + xmlTextWriterEndElement( writer ); + + // output the print area + xmlTextWriterStartElement( writer, BAD_CAST( "prtBounds" ) ); + getFramePrintArea().dumpAsXmlAttributes(writer); + xmlTextWriterEndElement( writer ); +} + +// Hack: somehow conversion from "..." to va_list does +// bomb on two string literals in the format. +static const char* const TMP_FORMAT = "%" SAL_PRIuUINTPTR; + +void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const +{ + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "id" ), "%" SAL_PRIuUINT32, GetFrameId() ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "symbol" ), "%s", BAD_CAST( typeid( *this ).name( ) ) ); + if ( GetNext( ) ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "next" ), "%" SAL_PRIuUINT32, GetNext()->GetFrameId() ); + if ( GetPrev( ) ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "prev" ), "%" SAL_PRIuUINT32, GetPrev()->GetFrameId() ); + if ( GetUpper( ) ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "upper" ), "%" SAL_PRIuUINT32, GetUpper()->GetFrameId() ); + if ( GetLower( ) ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "lower" ), "%" SAL_PRIuUINT32, GetLower()->GetFrameId() ); + if (IsFootnoteFrame()) + { + SwFootnoteFrame const*const pFF(static_cast<SwFootnoteFrame const*>(this)); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("ref"), "%" SAL_PRIuUINT32, pFF->GetRef()->GetFrameId() ); + if (pFF->GetMaster()) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("master"), "%" SAL_PRIuUINT32, pFF->GetMaster()->GetFrameId() ); + if (pFF->GetFollow()) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFF->GetFollow()->GetFrameId() ); + } + if ( IsTextFrame( ) ) + { + const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this); + const SwTextNode *pTextNode = pTextFrame->GetTextNodeFirst(); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), TMP_FORMAT, pTextNode->GetIndex() ); + + OString aMode = "Horizontal"; + if (IsVertLRBT()) + { + aMode = "VertBTLR"; + } + else if (IsVertLR()) + { + aMode = "VertLR"; + } + else if (IsVertical()) + { + aMode = "Vertical"; + } + 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(); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtName" ), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr())); + 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(); + + xmlTextWriterStartElement( writer, BAD_CAST( getElementName() ) ); + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this ); + + xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) ); + GetObjBoundRect().dumpAsXmlAttributes(writer); + xmlTextWriterEndElement( writer ); + + if (const SdrObject* pObject = GetDrawObj()) + pObject->dumpAsXml(writer); + + xmlTextWriterEndElement( writer ); + + if ( bCreateWriter ) + lcl_freeWriter( writer ); +} + +void SwFont::dumpAsXml(xmlTextWriterPtr writer) const +{ + xmlTextWriterStartElement(writer, BAD_CAST("SwFont")); + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", this); + // do not use Color::AsRGBHexString() as that omits the transparency + xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("color"), "%08" SAL_PRIxUINT32, sal_uInt32(GetColor())); + xmlTextWriterEndElement(writer); +} + +void SwTextFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const +{ + SwFrame::dumpAsXmlAttributes( writer ); + if ( HasFollow() ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() ); + + if (m_pPrecede != nullptr) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTextFrame*>(m_pPrecede)->GetFrameId() ); +} + +void SwSectionFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const +{ + SwFrame::dumpAsXmlAttributes( writer ); + if ( HasFollow() ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() ); + + if (m_pPrecede != nullptr) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwSectionFrame*>( m_pPrecede )->GetFrameId() ); +} + +void SwTabFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const +{ + SwFrame::dumpAsXmlAttributes( writer ); + if ( HasFollow() ) + xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() ); + + if (m_pPrecede != nullptr) + 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..c86cde923 --- /dev/null +++ b/sw/source/core/tox/ToxLinkProcessor.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/. + */ + +#include <memory> +#include <ToxLinkProcessor.hxx> + +#include <SwStyleNameMapper.hxx> +#include <ndtxt.hxx> +#include <sal/log.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) +{ + if (m_pStartedLink == nullptr) + { + SAL_INFO("sw.core", "ToxLinkProcessor: LE without LS"); + return; + } + + if (url.isEmpty()) { + return; + } + + std::unique_ptr<ClosedLink> pClosedLink( + new ClosedLink(url, 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..e54271c9d --- /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(sal_uInt32 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 = static_cast<const SvxLRSpaceItem&>( targetNode.SwContentNode::GetAttr(RES_LR_SPACE) ); + + 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); + } + 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; +} + +long +DefaultToxTabStopTokenHandler::CalculatePageMarginFromPageDescription(const SwTextNode& targetNode) const +{ + size_t 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(); + 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 = + static_cast<const SwFormatPageDesc&>( 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..cfef4d356 --- /dev/null +++ b/sw/source/core/tox/ToxTextGenerator.cxx @@ -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 . + */ + +#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 <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->IsHideRedlines()) + { // 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) + " "; + retval += 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, 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(); + 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: + mLinkProcessor->CloseLink(rText.getLength(), rBase.GetURL()); + break; + + case TOKEN_AUTHORITY: + { + ToxAuthorityField eField = static_cast<ToxAuthorityField>(aToken.nAuthorityField); + SwIndex aIdx( pTOXNd, rText.getLength() ); + rBase.FillText( *pTOXNd, aIdx, static_cast<sal_uInt16>(eField), pLayout ); + } + break; + case TOKEN_END: break; + } + + if ( !aToken.sCharStyleName.isEmpty() ) + { + SwCharFormat* pCharFormat; + if( USHRT_MAX != aToken.nPoolId ) + pCharFormat = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( aToken.nPoolId ); + else + pCharFormat = pDoc->FindCharFormatByName( aToken.sCharStyleName); + + 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; + 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->IsHideRedlines()) + { + 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 (sal_uLong 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(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..b2fe68727 --- /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.getStr(); +} + + +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..92b3bddcc --- /dev/null +++ b/sw/source/core/tox/tox.cxx @@ -0,0 +1,940 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swtypes.hxx> +#include <ndtxt.hxx> +#include <txttxmrk.hxx> +#include <tox.hxx> +#include <strings.hrc> +#include <doc.hxx> +#include <docary.hxx> +#include <paratr.hxx> +#include <editeng/tstpitem.hxx> +#include <hints.hxx> +#include <calbck.hxx> + +#include <optional> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <algorithm> + + +using namespace std; + + +const sal_Unicode C_NUM_REPL = '@'; +const sal_Unicode C_END_PAGE_NUM = '~'; +const OUString S_PAGE_DELI(", "); + + +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_pTextAttr( nullptr ), m_nLevel( 0 ) + , m_bAutoGenerated(false) + , m_bMainEntry(false) +{ } + +SwTOXMark::SwTOXMark(const SwTOXType* pTyp) + : SfxPoolItem(RES_TXTATR_TOXMARK ) + , m_pTextAttr( nullptr ) + , m_nLevel( 0 ) + , m_bAutoGenerated(false) + , m_bMainEntry(false) +{ + const_cast<SwTOXType*>(pTyp)->Add(this); +} + +SwTOXMark::SwTOXMark(const SwTOXMark& rCopy) + : SfxPoolItem(RES_TXTATR_TOXMARK) + , 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 (auto pRegister = const_cast<SwTOXMark&>(rCopy).GetRegisteredIn()) + pRegister->Add(this); + // Copy AlternativString + m_aAltText = rCopy.m_aAltText; +} + +SwTOXMark::~SwTOXMark() +{ +} + +void SwTOXMark::RegisterToTOXType(SwTOXType& rType) +{ + rType.Add(this); +} + +bool SwTOXMark::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return GetRegisteredIn() == static_cast<const SwTOXMark&>(rAttr).GetRegisteredIn(); +} + +SwTOXMark* SwTOXMark::Clone( SfxItemPool* ) const +{ + return new SwTOXMark( *this ); +} + +void SwTOXMark::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew) +{ + NotifyClients(pOld, pNew); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached uno object + SetXTOXMark(css::uno::Reference<css::text::XDocumentIndexMark>(nullptr)); + } +} + +void SwTOXMark::InvalidateTOXMark() +{ + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, + &static_cast<SwModify&>(*this) ); // cast to base class! + NotifyClients(&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(); +} + +void SwTOXMark::InsertTOXMarks( SwTOXMarks& aMarks, const SwTOXType& rType ) +{ + SwIterator<SwTOXMark,SwTOXType> aIter(rType); + SwTOXMark* pMark = aIter.First(); + while( pMark ) + { + if(pMark->GetTextTOXMark()) + aMarks.push_back(pMark); + pMark = aIter.Next(); + } +} + +// 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 pRegister = const_cast<SwTOXType&>(rCopy).GetRegisteredIn()) + pRegister->Add(this); +} + +static const char* 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 +}; + +static const char* 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 +}; + +static const char* 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 +}; + +static const char* 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 +}; + +static const char* 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 +}; + +static const char* STR_POOLCOLL_TOX_ILLUS_ARY[] = +{ + // Illustrations Index + STR_POOLCOLL_TOX_ILLUSH, + STR_POOLCOLL_TOX_ILLUS1 +}; + +static const char* STR_POOLCOLL_TOX_OBJECT_ARY[] = +{ + // Object Index + STR_POOLCOLL_TOX_OBJECTH, + STR_POOLCOLL_TOX_OBJECT1 +}; + +static const char* STR_POOLCOLL_TOX_TABLES_ARY[] = +{ + // Tables Index + STR_POOLCOLL_TOX_TABLESH, + STR_POOLCOLL_TOX_TABLES1 +}; + +static const char* STR_POOLCOLL_TOX_AUTHORITIES_ARY[] = +{ + // Index of Authorities + STR_POOLCOLL_TOX_AUTHORITIESH, + STR_POOLCOLL_TOX_AUTHORITIES1 +}; + +static const char* 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 char** 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, aTmpTokens ); + SetTemplate(i, SwResId(STR_POOLCOLL_TOX_IDXBREAK)); + } + else + { + SetPattern( i, 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, aAuthTokens); + } + else + SetPattern( i, 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, 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<SwModify*>(static_cast<SwModify 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_aEntryTypeName = rSource.m_aEntryTypeName ; + 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; +} + +void SwTOXBase::SetEntryTypeName(const OUString& sName) +{ + m_aEntryTypeName = sName ; +} + +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_aEntryTypeName = rSource.m_aEntryTypeName ; + 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.copy(0, sToken.getLength()-1) + sData + sToken.copy(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(const OUString & 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( sToken.startsWith( 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 ); + OUString sTmp( sToken.getToken( 0, ',', nIdx )); + if( !sTmp.isEmpty() ) + eRet.nPoolId = static_cast<sal_uInt16>(sTmp.toInt32()); + + switch( eTokenType ) + { +//i53420 + case TOKEN_CHAPTER_INFO: +//i53420 + case TOKEN_ENTRY_NO: + sTmp = sToken.getToken( 0, ',', nIdx ); // token 2 + if( !sTmp.isEmpty() ) + eRet.nChapterFormat = static_cast<sal_uInt16>(sTmp.toInt32()); + sTmp = sToken.getToken( 0, ',', nIdx ); // token 3 + if( !sTmp.isEmpty() ) + eRet.nOutlineLevel = static_cast<sal_uInt16>(sTmp.toInt32()); //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 = sToken.getToken( 0, ',', nIdx ); // token 2 + if( !sTmp.isEmpty() ) + eRet.nTabStopPosition = sTmp.toInt32(); + + sTmp = sToken.getToken( 0, ',', nIdx ); // token 3 + if( !sTmp.isEmpty() ) + eRet.eTabAlign = static_cast<SvxTabAdjust>(sTmp.toInt32()); + + sTmp = sToken.getToken( 0, ',', nIdx ); // token 4 + if( !sTmp.isEmpty() ) + eRet.cTabFillChar = sTmp[0]; + + sTmp = sToken.getToken( 0, ',', nIdx ); // token 5 + if( !sTmp.isEmpty() ) + eRet.bWithTab = 0 != sTmp.toInt32(); + break; + + case TOKEN_AUTHORITY: + eRet.nAuthorityField = static_cast<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, const SwFormTokens& rTokens) +{ + OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX"); + m_aPattern[nLevel] = 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..058d83b8d --- /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 { + 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 = 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 = xIES->getIndexFollowPageWord( bMorePages, 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 = 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, long nOptions ) const +{ + bool bRet = false; + try { + bRet = 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 = 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..cc77ef66e --- /dev/null +++ b/sw/source/core/tox/txmsrt.cxx @@ -0,0 +1,864 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unotools/charclass.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> + +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( static_cast<sal_uInt16>(nTyp) ) + , m_bValidText( false ) +{ + if ( pLocale ) + aLocale = *pLocale; + + if( pNd ) + { + 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; + } + } +} + +OUString SwTOXSortTabBase::GetURL() const +{ + return OUString(); +} + +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) +{ + const SwTOXIndex& rCmp = static_cast<const SwTOXIndex&>(rCmpBase); + + OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword"); + + const TextAndReading aMyTaR(GetText()); + const TextAndReading aOtherTaR(rCmp.GetText()); + + bool bRet = GetLevel() == rCmp.GetLevel() && + pTOXIntl->IsLess( aMyTaR, GetLocale(), + aOtherTaR, rCmp.GetLocale() ); + + // If we don't summarize we need to evaluate the Pos + if( !bRet && !(GetOptions() & SwTOIOptions::SameEntry) ) + { + bRet = pTOXIntl->IsEqual( aMyTaR, GetLocale(), + aOtherTaR, rCmp.GetLocale() ) && + nPos < rCmp.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.copy(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.copy(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()); + } + BOOST_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(0)), + 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" ); + const char* 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->IsHideRedlines()) + { + 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 (sal_uLong 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 = static_cast<sal_uInt16>(nTmp); + } + return nRet; +} + +OUString SwTOXPara::GetURL() const +{ + OUString aText; + const SwContentNode* pNd = aTOXSources[0].pNd; + switch( eType ) + { + case SwTOXElement::Template: + case SwTOXElement::OutlineLevel: + { + const SwTextNode * pTextNd = pNd->GetTextNode(); + + SwDoc* pDoc = 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 = pDoc->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 aText; +} + +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 = + reinterpret_cast<const SwTableNode*>(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; +} + +OUString SwTOXTable::GetURL() const +{ + const SwNode* pNd = aTOXSources[0].pNd; + if (!pNd) + return OUString(); + + pNd = pNd->FindTableNode(); + if (!pNd) + return OUString(); + + const OUString sName = static_cast<const SwTableNode*>(pNd)->GetTable().GetFrameFormat()->GetName(); + if ( sName.isEmpty() ) + return OUString(); + + return "#" + sName + OUStringChar(cMarkSeparator) + "table"; +} + +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()); +} + +void SwTOXAuthority::FillText( SwTextNode& rNd, + const SwIndex& rInsPos, sal_uInt16 nAuthField, + SwRootFrame const*const 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)); + rNd.InsertText( sText, rInsPos ); +} + +bool SwTOXAuthority::equivalent(const SwTOXSortTabBase& rCmp) +{ + return nType == rCmp.nType && + static_cast<SwAuthorityField*>(m_rField.GetField())->GetAuthEntry() == + static_cast<SwAuthorityField*>(static_cast<const SwTOXAuthority&>(rCmp).m_rField.GetField())->GetAuthEntry(); +} + +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..d957bfbcc --- /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 <hints.hxx> +#include <IGrammarContact.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <SwGrammarMarkUp.hxx> +#include <txtfrm.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 : public IGrammarContact, public SwClient +{ + Timer aTimer; + std::unique_ptr<SwGrammarMarkUp> mpProxyList; + bool mbFinished; + SwTextNode* getMyTextNode() { return static_cast<SwTextNode*>(GetRegisteredIn()); } + DECL_LINK( TimerRepaint, Timer *, void ); + +public: + SwGrammarContact(); + virtual ~SwGrammarContact() override { 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; +protected: + // virtual function of SwClient + virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) override; +}; + +} + +SwGrammarContact::SwGrammarContact() : mbFinished( false ) +{ + aTimer.SetTimeout( 2000 ); // Repaint of grammar check after 'setChecked' + aTimer.SetInvokeHandler( LINK(this, SwGrammarContact, TimerRepaint) ); + aTimer.SetDebugName( "sw::SwGrammarContact TimerRepaint" ); +} + +IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer, void ) +{ + if( pTimer ) + { + pTimer->Stop(); + if( GetRegisteredIn() ) + { //Replace the old wrong list by the proxy list and repaint all frames + getMyTextNode()->SetGrammarCheck( mpProxyList.release() ); + SwTextFrame::repaintTextFrames( *getMyTextNode() ); + } + } +} + +/* I'm always a client of the current paragraph */ +void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos ) +{ + SwTextNode* pTextNode = rNewPos.nNode.GetNode().GetTextNode(); + if( pTextNode != GetRegisteredIn() ) // paragraph has been changed + { + aTimer.Stop(); + if( GetRegisteredIn() ) // My last paragraph has been left + { + if( mpProxyList ) + { // replace old list by the proxy list and repaint + getMyTextNode()->SetGrammarCheck( mpProxyList.release() ); + SwTextFrame::repaintTextFrames( *getMyTextNode() ); + } + EndListeningAll(); + } + if( pTextNode ) + pTextNode->Add( this ); // welcome new paragraph + } +} + +/* deliver a grammar check list for the given text node */ +SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) +{ + SwGrammarMarkUp *pRet = nullptr; + if( GetRegisteredIn() == &rTextNode ) // hey, that's my current paragraph! + { // so you will get a proxy list... + if( bCreate ) + { + if( mbFinished ) + { + mpProxyList.reset(); + } + if( !mpProxyList ) + { + if( rTextNode.GetGrammarCheck() ) + mpProxyList.reset( static_cast<SwGrammarMarkUp*>(rTextNode.GetGrammarCheck()->Clone()) ); + else + { + mpProxyList.reset( new SwGrammarMarkUp() ); + mpProxyList->SetInvalid( 0, COMPLETE_STRING ); + } + } + mbFinished = false; + } + pRet = mpProxyList.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( pRet ); + rTextNode.SetGrammarCheckDirty( true ); + } + } + return pRet; +} + +void SwGrammarContact::Modify( const SfxPoolItem* pOld, const SfxPoolItem * ) +{ + if( !pOld || pOld->Which() != RES_OBJECTDYING ) + return; + + const SwPtrMsgPoolItem *pDead = static_cast<const SwPtrMsgPoolItem *>(pOld); + if( pDead->pObject == GetRegisteredIn() ) + { // if my current paragraph dies, I throw the proxy list away + aTimer.Stop(); + EndListeningAll(); + mpProxyList.reset(); + } +} + +void SwGrammarContact::finishGrammarCheck( SwTextNode& rTextNode ) +{ + if( &rTextNode != GetRegisteredIn() ) // not my paragraph + SwTextFrame::repaintTextFrames( rTextNode ); // can be repainted directly + else + { + if( mpProxyList ) + { + mbFinished = true; + aTimer.Start(); // will replace old list and repaint with delay + } + else if( getMyTextNode()->GetGrammarCheck() ) + { // all grammar problems seems to be gone, no delay needed + getMyTextNode()->SetGrammarCheck( nullptr ); + SwTextFrame::repaintTextFrames( *getMyTextNode() ); + } + } +} + +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..b8027bcd5 --- /dev/null +++ b/sw/source/core/txtnode/atrfld.cxx @@ -0,0 +1,732 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 ) + { + 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() ) + { + 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() +{ + SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT, + &static_cast<SwModify&>(*this)); // cast to base class (void*) + CallSwClientNotify(sw::LegacyModifyHint{ &item, &item }); +} + +void SwFormatField::SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) +{ + SwClient::SwClientNotify(rModify, rHint); + if (const auto pFieldHint = dynamic_cast<const SwFieldHint*>( &rHint )) + { + if( !mpTextField ) + return; + + // replace field content by text + SwPaM* pPaM = pFieldHint->m_pPaM; + SwDoc* pDoc = 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(pDoc->IsClipBoard(), pFieldHint->m_pLayout)); + pPaM->SetMark(); + pPaM->Move( fnMoveForward ); + pDoc->getIDocumentContentOperations().DeleteRange( *pPaM ); + pDoc->getIDocumentContentOperations().InsertString( *pPaM, aEntry ); + } else if (const auto pLegacyHint = dynamic_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 = nullptr; + // ??? why does this Modify method not already do this? + NotifyClients(pOld, pNew); + return; + } + + if( !mpTextField ) + 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->ModifyNotification(pNodeOld, pNodeNew); + } + if(bExpand) + { + mpTextField->ExpandTextField( pOld == nullptr && pNew == nullptr ); + } +} + +bool SwFormatField::GetInfo( SfxPoolItem& rInfo ) const +{ + const SwTextNode* pTextNd; + return RES_AUTOFMT_DOCNODE != rInfo.Which() || + !mpTextField || nullptr == ( pTextNd = mpTextField->GetpTextNode() ) || + &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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatField")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mpTextField"), "%p", mpTextField); + + SfxPoolItem::dumpAsXml(pWriter); + mpField->dumpAsXml(pWriter); + + 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->ModifyNotification( 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->ModifyNotification(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()) ) + { + 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* pDoc = static_cast<const SwPostItFieldType*>(pPostItField->GetTyp())->GetDoc(); + assert(pDoc != nullptr); + + IDocumentMarkAccess* pMarksAccess = pDoc->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..4e61e568e --- /dev/null +++ b/sw/source/core/txtnode/atrflyin.cxx @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +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* pDoc ) +{ + 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(pDoc->GetIDocumentUndoRedo()); + SwFormatAnchor aAnchor( pFormat->GetAnchor() ); + if ((RndStdIds::FLY_AT_PAGE != aAnchor.GetAnchorId()) && + (pDoc != 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( pDoc->GetNodes().GetEndOfExtras(), +2 ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = pDoc->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 = pDoc->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* pDoc = 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( pDoc != pFormat->GetDoc() ) + { + // disable undo while copying attribute + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + SwFrameFormat* pNew = pDoc->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::Modify() 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(); + } + + pTextBox->SetFormatAttr(aTextBoxAnchor); + + if (bIsInSplitNode) + { + pOldNode->RemoveAnchoredFly(pTextBox); + aPos.nNode.GetNode().AddAnchoredFly(pTextBox); + pTextBox->UnlockModify(); + } + } + } + + // 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..fd8f65185 --- /dev/null +++ b/sw/source/core/txtnode/atrftn.cxx @@ -0,0 +1,576 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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> + +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 ) + , SwModify() + , 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::Modify(SfxPoolItem const* pOld, SfxPoolItem const* pNew) +{ + NotifyClients(pOld, pNew); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached UNO object + SetXFootnote(css::uno::Reference<css::text::XFootnote>(nullptr)); + } +} + +void SwFormatFootnote::InvalidateFootnote() +{ + SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT, + &static_cast<SwModify&>(*this)); // cast to base class (void*) + NotifyClients(&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() ? + static_cast<sal_uInt16>(RES_END_AT_TXTEND) : + static_cast<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); + SwFormatFootnote const& rFootnote(GetFootnote()); + SwNodes &rNodes = m_pTextNode->GetDoc()->GetNodes(); + m_pTextNode->ModifyNotification( nullptr, &rFootnote ); + if ( m_pStartNode ) + { + // must iterate over all TextNodes because of footnotes on other pages + sal_uLong nSttIdx = m_pStartNode->GetIndex() + 1; + sal_uLong nEndIdx = m_pStartNode->GetNode().EndOfSectionIndex(); + for( ; nSttIdx < nEndIdx; ++nSttIdx ) + { + SwNode* pNd; + if( ( pNd = rNodes[ nSttIdx ] )->IsTextNode() ) + static_cast<SwTextNode*>(pNd)->ModifyNotification( nullptr, &rFootnote ); + } + } +} + +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 *const pDstDoc = rDestNode.GetDoc(); + SwNodes &rDstNodes = pDstDoc->GetNodes(); + + // copy only the content of the section + SwNodeRange aRg( *m_pStartNode, 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() ); + sal_uLong 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; + } + + if( nullptr == (pFormatColl = pInfo->GetFootnoteTextColl() ) ) + 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 ) + { + SwNodeIndex aIdx( *m_pStartNode ); + SwContentNode* pCNd = m_pTextNode->GetNodes().GoNext( &aIdx ); + if( pCNd ) + { + 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* pDoc = m_pTextNode->GetDoc(); + if( pDoc->IsInReading() ) + return; + + std::set<sal_uInt16> aUsedNums; + std::vector<SwTextFootnote*> badRefNums; + ::lcl_FillUsedFootnoteRefNumbers(*pDoc, 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 +} + +/* 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..88292c8a2 --- /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) + , SwModify() + , m_pTextAttr(nullptr) + , m_aRefName(rName) +{ +} + +SwFormatRefMark::SwFormatRefMark( const SwFormatRefMark& rAttr ) + : SfxPoolItem(RES_TXTATR_REFMARK) + , SwModify() + , BroadcasterMixin() + , 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::Modify(SfxPoolItem const* pOld, SfxPoolItem const* pNew) +{ + NotifyClients(pOld, pNew); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached UNO object + SetXRefMark(css::uno::Reference<css::text::XTextContent>(nullptr)); + } +} + +void SwFormatRefMark::InvalidateRefMark() +{ + SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT, + &static_cast<SwModify&>(*this)); // cast to base class (void*) + NotifyClients(&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..de3a782d2 --- /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* pDoc ) +{ + SwTOXMark& rTOX = const_cast<SwTOXMark&>(GetTOXMark()); + TOXTypes eType = rTOX.GetTOXType()->GetType(); + const sal_uInt16 nCount = pDoc->GetTOXTypeCount( eType ); + const SwTOXType* pType = nullptr; + const OUString rNm = rTOX.GetTOXType()->GetTypeName(); + + for(sal_uInt16 i=0; i < nCount; ++i) + { + const SwTOXType* pSrcType = pDoc->GetTOXType(eType, i); + if(pSrcType->GetTypeName() == rNm ) + { + pType = pSrcType; + break; + } + } + + // if the requested tox type does not exist, create it + if(!pType) + { + pDoc->InsertTOXType( SwTOXType( *pDoc, eType, rNm ) ); + pType = pDoc->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/chrfmt.cxx b/sw/source/core/txtnode/chrfmt.cxx new file mode 100644 index 000000000..b5fac1e16 --- /dev/null +++ b/sw/source/core/txtnode/chrfmt.cxx @@ -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 . + */ + +#include <libxml/xmlwriter.h> + +#include <charfmt.hxx> +#include <docary.hxx> + + +void SwCharFormat::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwCharFormat")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr())); + GetAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +void SwCharFormats::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwCharFormats")); + for (size_t i = 0; i < size(); ++i) + GetFormat(i)->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +/* 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..e825a99a8 --- /dev/null +++ b/sw/source/core/txtnode/fmtatr2.cxx @@ -0,0 +1,843 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hints.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 <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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( m_pTextAttribute ) + m_pTextAttribute->ModifyNotification( pOld, pNew ); +} + +// 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatAutoFormat")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + mpHandle->dumpAsXml(pWriter); + 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 + SwHyperlinkEventDescriptor* pEvents = + new SwHyperlinkEventDescriptor(); + pEvents->copyMacrosFromINetFormat(*this); + uno::Reference<container::XNameReplace> xNameReplace(pEvents); + + // all others return a string; so we just set rVal here and exit + rVal <<= xNameReplace; + } + 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::Modify! + 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) + { + 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() + , SwModify() + , 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)); + } +} + +// SwClient +void Meta::Modify( const SfxPoolItem *pOld, const SfxPoolItem *pNew ) +{ + NotifyClients(pOld, pNew); + GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged)); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // 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) +{ + try + { + const uno::Reference<rdf::XMetadatable> xMetaField( MakeUnoObject() ); + OSL_ENSURE(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); + } + } + catch (const uno::Exception&) + { + OSL_FAIL("exception?"); + } +} + +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..decea02c4 --- /dev/null +++ b/sw/source/core/txtnode/fntcache.cxx @@ -0,0 +1,2712 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> + +#include <cstdint> + +#include <i18nlangtag/mslangid.hxx> +#include <vcl/outdev.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/metric.hxx> +#include <vcl/svapp.hxx> +#include <vcl/lazydelete.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 <swmodule.hxx> +#include <accessibilityoptions.hxx> +#include <svtools/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> + +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; + +static constexpr Color gWaveCol(COL_GRAY); + +long SwFntObj::nPixWidth; +MapMode* SwFntObj::pPixMap = nullptr; +static vcl::DeleteOnDeinit< VclPtr<OutputDevice> > s_pFntObjPixOut( new VclPtr<OutputDevice> ); + +namespace +{ + +long EvalGridWidthAdd( const SwTextGridItem *const pGrid, const SwDrawTextInfo &rInf ) +{ + SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell(); + SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool(); + + SfxStyleSheetBase* pStyle = pBasePool->Find(SwResId(STR_POOLCOLL_STANDARD), SfxStyleFamily::Para); + SfxItemSet& aTmpSet = pStyle->GetItemSet(); + const SvxFontHeightItem &aDefaultFontItem = aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE); + + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc); + const sal_uInt32 nFontHeight = aDefaultFontItem.GetHeight(); + const long nGridWidthAdd = nGridWidth > nFontHeight ? nGridWidth - nFontHeight : 0; + if( SwFontScript::Latin == rInf.GetFont()->GetActual() ) + return nGridWidthAdd / 2; + + return nGridWidthAdd; +} + +/** + * Pre-calculates glyph items for the rendered subset of rKey's text, assuming + * outdev state does not change between the outdev calls. + */ +SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, SalLayoutGlyphs& rTextGlyphs) +{ + // Use pre-calculated result. + if (rTextGlyphs.IsValid()) + return &rTextGlyphs; + + if (rKey.m_nIndex >= rKey.m_aText.getLength()) + // Same as in OutputDevice::GetTextArray(). + return nullptr; + + // Calculate glyph items. + std::unique_ptr<SalLayout> pLayout + = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0, + nullptr, SalLayoutFlags::GlyphItemsOnly); + if (!pLayout) + return nullptr; + + const SalLayoutGlyphs* pGlyphs = pLayout->GetGlyphs(); + if (!pGlyphs) + return nullptr; + + // Remember the calculation result. + rTextGlyphs = *pGlyphs; + + return &rTextGlyphs; +} +} + +bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r) +{ + if (l.m_pOutputDevice.get() < r.m_pOutputDevice.get()) + return true; + if (l.m_pOutputDevice.get() > r.m_pOutputDevice.get()) + return false; + if (l.m_nIndex < r.m_nIndex) + return true; + if (l.m_nIndex > r.m_nIndex) + return false; + if (l.m_nLength < r.m_nLength) + return true; + if (l.m_nLength > r.m_nLength) + return false; + + // Comparing strings is expensive, so compare them: + // - only at the end of this function + // - only once + // - only the relevant substring (if the index/length is not out of bounds) + sal_Int32 nRet = 0; + if (l.m_nLength < 0 || l.m_nIndex < 0 || l.m_nIndex + l.m_nLength > l.m_aText.getLength()) + nRet = l.m_aText.compareTo(r.m_aText); + else + nRet = memcmp(l.m_aText.getStr() + l.m_nIndex, r.m_aText.getStr() + r.m_nIndex, + l.m_nLength * sizeof(sal_Unicode)); + if (nRet < 0) + return true; + if (nRet > 0) + return false; + + return false; +}; + +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_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; + long nHalfSpace; + long* pKernArray; + const bool bBidiPor; + + CalcLinePosData( SwDrawTextInfo& _rInf, vcl::Font& _rFont, + TextFrameIndex const _nCnt, const bool _bSwitchH2V, const bool _bSwitchH2VLRBT, const bool _bSwitchL2R, + long _nHalfSpace, long* _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) +{ + long nBlank = 0; + const TextFrameIndex nEnd = nStart + nWrLen; + const 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 sal_uInt16 nDir = rData.bBidiPor ? 1800 + : UnMapDirection(rData.rFont.GetOrientation(), + rData.bSwitchH2V, rData.bSwitchH2VLRBT); + + switch ( nDir ) + { + 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 = static_cast<sal_uInt16>(aOutMet.GetAscent()); + 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 = static_cast<sal_uInt16>(rRefDev.GetTextHeight()); + +#if OSL_DEBUG_LEVEL > 0 + // Check if vcl did not change the meaning of GetTextHeight + const FontMetric aOutMet( rRefDev.GetFontMetric() ); + long nTmpPrtHeight = static_cast<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 = static_cast<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; +} + +// 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 = static_cast<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; + } + + // check zoom factor, e.g. because of PrtOle2 during export + { + // In case the zoom factor of the output device differs from the + // one in the ViewOptions, this Font must not be cached, + // hence set zoom factor to an invalid value + long nTmp; + if( pOut->GetMapMode().GetScaleX().IsValid() && + pOut->GetMapMode().GetScaleY().IsValid() && + pOut->GetMapMode().GetScaleX() == pOut->GetMapMode().GetScaleY() ) + { + nTmp = long(100 * pOut->GetMapMode().GetScaleX()); + } + else + nTmp = 0; + if( nTmp != m_nZoom ) + m_nZoom = USHRT_MAX - 1; + } + + m_nScrAscent = static_cast<sal_uInt16>(pOut->GetFontMetric().GetAscent()); + if ( USHRT_MAX == m_nScrHeight ) + m_nScrHeight = static_cast<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() : + 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). + 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. + 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 + static_cast<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 sal_uInt8 lcl_WhichPunctuation( sal_Unicode cChar ) +{ + if ( ( cChar < 0x3001 || cChar > 0x3002 ) && + ( cChar < 0x3008 || cChar > 0x3011 ) && + ( cChar < 0x3014 || cChar > 0x301F ) && + 0xFF62 != cChar && 0xFF63 != cChar ) + // no punctuation + return SwScriptInfo::NONE; + else if ( 0x3001 == cChar || 0x3002 == cChar || + 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 SwScriptInfo::SPECIAL_RIGHT; + + return SwScriptInfo::SPECIAL_LEFT; +} + +static bool lcl_IsMonoSpaceFont( const vcl::RenderContext& rOut ) +{ + const long nWidth1 = rOut.GetTextWidth( OUString( u'\x3008' ) ); + const 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; + } + + 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) + { + if (WRONGAREA_WAVE == wrongArea->mLineType) + { + vcl::ScopedAntialiasing a(rInf.GetOut(), true); + rInf.GetOut().SetLineColor( wrongArea->mColor ); + rInf.GetOut().DrawWaveLine( aStart, aEnd, 1 ); + } + else if (WRONGAREA_BOLDWAVE == wrongArea->mLineType) + { + vcl::ScopedAntialiasing a(rInf.GetOut(), true); + rInf.GetOut().SetLineColor( wrongArea->mColor ); + rInf.GetOut().DrawWaveLine( aStart, aEnd, 2 ); + } + 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(); +} + +void SwFntObj::DrawText( SwDrawTextInfo &rInf ) +{ + OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" ); + + OutputDevice& rRefDev = rInf.GetShell()->GetRefDev(); + OutputDevice* 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 ComplexTextLayoutFlags nMode = rInf.GetOut().GetLayoutMode(); + const bool bBidiPor = ( bSwitchL2R != + ( ComplexTextLayoutFlags::Default != ( 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() != *pPixMap ) + { + *pPixMap = rInf.GetOut().GetMapMode(); + (*s_pFntObjPixOut.get()) = rInf.GetpOut(); + Size aTmp( 1, 1 ); + nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width(); + } + + aTextOriginPos.AdjustX(rInf.GetFrame()->IsRightToLeft() ? 0 : 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: Do we want to snap asian characters to the grid? + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars()) + { + //for textgrid refactor + //const sal_uInt16 nGridWidth = pGrid->GetBaseHeight(); + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc); + + // kerning array - gives the absolute position of end of each character + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + + if ( m_pPrinter ) + m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + + // Change the average width per character to an appropriate grid width + // basically get the ratio of the avg width to the grid unit width, then + // multiple this ratio to give the new avg width - which in this case + // gives a new grid width unit size + + long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen()); + + const sal_uLong nRatioAvgWidthCharToGridWidth = nAvgWidthPerChar ? + ( nAvgWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nAvgWidthPerChar = nRatioAvgWidthCharToGridWidth * nGridWidth; + + // the absolute end position of the first character is also its width + long nCharWidth = pKernArray[ 0 ]; + sal_uLong nHalfWidth = nAvgWidthPerChar / 2; + + long nNextFix=0; + + // we work out the start position (origin) of the first character, + // and we set the next "fix" offset to half the width of the char. + // The exceptions are for punctuation characters that are not centered + // so in these cases we just add half a regular "average" character width + // to the first characters actual width to allow the next character to + // be centered automatically + // If the character is "special right", then the offset is correct already + // so the fix offset is as normal - half the average character width + + sal_Unicode cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ]; + sal_uInt8 nType = lcl_WhichPunctuation( cChar ); + switch ( nType ) + { + // centre character + case SwScriptInfo::NONE : + aTextOriginPos.AdjustX(( nAvgWidthPerChar - nCharWidth ) / 2 ); + nNextFix = nCharWidth / 2; + break; + case SwScriptInfo::SPECIAL_RIGHT : + nNextFix = nHalfWidth; + break; + // punctuation + default: + aTextOriginPos.AdjustX(nAvgWidthPerChar - nCharWidth ); + nNextFix = nCharWidth - nHalfWidth; + } + + // calculate offsets + for (sal_Int32 j = 1; j < sal_Int32(rInf.GetLen()); ++j) + { + long nCurrentCharWidth = pKernArray[ j ] - pKernArray[ j - 1 ]; + nNextFix += nAvgWidthPerChar; + + // almost the same as getting the offset for the first character: + // punctuation characters are not centered, so just add half an + // average character width minus the characters actual char width + // to get the offset into the centre of the next character + + cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + j ]; + nType = lcl_WhichPunctuation( cChar ); + switch ( nType ) + { + case SwScriptInfo::NONE : + pKernArray[ j - 1 ] = nNextFix - ( nCurrentCharWidth / 2 ); + break; + case SwScriptInfo::SPECIAL_RIGHT : + pKernArray[ j - 1 ] = nNextFix - nHalfWidth; + break; + default: + pKernArray[ j - 1 ] = nNextFix + nHalfWidth - nCurrentCharWidth; + } + } + + // the layout engine requires the total width of the output + pKernArray[sal_Int32(rInf.GetLen()) - 1] = rInf.GetWidth() - + aTextOriginPos.X() + rInf.GetPos().X() ; + + if ( bSwitchH2V ) + rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos ); + + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + + return; + } + } + + // For text grid refactor + // ASIAN LINE AND CHARACTER GRID MODE START: not snap to characters + + if ( rInf.GetFrame() && rInf.SnapToGrid() ) + { + SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame())); + + // ASIAN LINE AND CHARACTER GRID MODE - do not snap to characters + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf ); + + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + + if ( m_pPrinter ) + m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + if ( bSwitchH2V ) + rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos ); + if ( rInf.GetSpace() || rInf.GetKanaComp()) + { + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + if ( rInf.GetFont() && rInf.GetLen() ) + { + bool bSpecialJust = false; + 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.GetpOut()) ) ) + { + pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ) , &aTextOriginPos ); + bSpecialJust = true; + } + ///Asian Justification + if ( ( SwFontScript::CJK == nActual || SwFontScript::Latin == nActual ) && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK ); + if (!MsLangId::isKorean(aLang)) + { + long nSpaceSum = nSpaceAdd; + for (sal_Int32 nI = 0; nI < sal_Int32(rInf.GetLen()); ++nI) + { + pKernArray[ nI ] += nSpaceSum; + nSpaceSum += nSpaceAdd; + } + bSpecialJust = true; + nSpaceAdd = 0; + } + } + long nGridAddSum = nGridWidthAdd; + for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++, nGridAddSum += nGridWidthAdd ) + { + pKernArray[i] += nGridAddSum; + } + long nKernSum = rInf.GetKern(); + if ( bSpecialJust || rInf.GetKern() ) + { + 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; + pKernArray[i] += nKernSum; + } + ///With through/uderstr. Grouped style requires a blank at the end + ///of a text edition special measures: + if( m_bPaintBlank && rInf.GetLen() && (CH_BLANK == + rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen()) - 1])) + { + ///If it concerns a singular, underlined space acts, + ///we must spend two: + if (TextFrameIndex(1) == rInf.GetLen()) + { + pKernArray[0] = rInf.GetWidth() + nSpaceAdd; + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), 1); + } + else + { + pKernArray[sal_Int32(rInf.GetLen()) - 2] += nSpaceAdd; + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), + sal_Int32(rInf.GetLen())); + } + } + else + { + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), + sal_Int32(rInf.GetLen())); + } + } + else + { + sal_Int32 i; + long nSpaceSum = 0; + for (i = 0; i < sal_Int32(rInf.GetLen()); i++) + { + if(CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i]) + nSpaceSum += nSpaceAdd + nKernSum; + + pKernArray[i] += nSpaceSum; + } + + rInf.GetOut().DrawTextArray(aTextOriginPos, + rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), + sal_Int32(rInf.GetLen())); + } + } + } + else + { + //long nKernAdd = rInf.GetKern(); + long nKernAdd = 0; + long nGridAddSum = nGridWidthAdd + nKernAdd; + for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); + i++, nGridAddSum += nGridWidthAdd + nKernAdd) + { + pKernArray[i] += nGridAddSum; + } + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), 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::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + + if( bStretch ) + { + sal_Int32 nZwi = sal_Int32(rInf.GetLen()) - 1; + long nDiff = rInf.GetWidth() - pKernArray[ nZwi ] + - sal_Int32(rInf.GetLen()) * rInf.GetKern(); + long nRest = nDiff % nZwi; + long nAdd; + if( nRest < 0 ) + { + nAdd = -1; + nRest += nZwi; + } + else + { + nAdd = +1; + nRest = nZwi - nRest; + } + nDiff /= nZwi; + long nSum = nDiff; + for( sal_Int32 i = 0; i < nZwi; ) + { + pKernArray[ i ] += nSum; + if( ++i == nRest ) + nDiff += nAdd; + nSum += nDiff; + } + } + + // Modify Array for special justifications + + 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( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + static_cast<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(), pKernArray.get(), nullptr, + 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( pKernArray.get(), nullptr, 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(), pKernArray.get(), nullptr, + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + bSpecialJust = true; + nSpaceAdd = 0; + } + } + } + + 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; + pKernArray[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()) + { + pKernArray[0] = rInf.GetWidth() + nSpaceAdd; + + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 ); + } + else + { + pKernArray[ sal_Int32(rInf.GetLen()) - 2 ] += nSpaceAdd; + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + } + } + else + rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), + pKernArray.get(), 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 = pKernArray[ i ] + nKernSum; + if ( ( ComplexTextLayoutFlags::BiDiStrong | 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 ) + { + 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 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 OUString* pStr = &rInf.GetText(); + + OUString aStr; + OUString aBulletOverlay; + bool bBullet = rInf.GetBullet(); + if( m_bSymbol ) + bBullet = false; + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); + long nScrPos; + + // get screen array + std::unique_ptr<long[]> pScrArray(new long[sal_Int32(rInf.GetLen())]); + SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; + SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); + + // 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 ); + } + aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; + pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); + } + else + { + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + } + + // Modify Printer and ScreenArrays for special justifications + + 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() ) ) + { + Point aTmpPos( aTextOriginPos ); + pSI->Compress( pScrArray.get(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTmpPos ); + pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + static_cast<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(), pKernArray.get(), pScrArray.get(), + 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( pKernArray.get(), pScrArray.get(), 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(), pKernArray.get(), + pScrArray.get(), rInf.GetIdx(), + rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + nSpaceAdd = 0; + } + } + } + + nScrPos = pScrArray[ 0 ]; + + if( bBullet ) + { + // !!! HACK !!! + // The Arabic layout engine requires some context of the string + // which should be painted. + sal_Int32 nCopyStart = sal_Int32(rInf.GetIdx()); + if ( nCopyStart ) + --nCopyStart; + + sal_Int32 nCopyLen = sal_Int32(rInf.GetLen()); + if ( nCopyStart + nCopyLen < rInf.GetText().getLength() ) + ++nCopyLen; + + aStr = rInf.GetText().copy( nCopyStart, nCopyLen ); + pStr = &aStr; + + 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())) || + pKernArray[i + nCopyStart] != pKernArray[ i + nCopyStart + 1]) + { + aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BULLET)); + } + else + { + aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BLANK)); + } + } + else + { + aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(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()); + long nKernSum = rInf.GetKern(); + 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)) + { + pKernArray[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(), + pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 ); + if( bBullet ) + rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(), + rInf.GetIdx() ? 1 : 0, 1 ); + } + else + { + sal_Unicode nCh; + + // In case of Pair Kerning the printer influence on the positioning + // grows + const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3; + const int nDiv = nMul+1; + + // 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. + long nSpaceSum = 0; + // in word line mode and for Arabic, we disable the half space trick: + const long nHalfSpace = m_pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2; + const long nOtherHalf = nSpaceAdd - nHalfSpace; + if ( nSpaceAdd && ( cChPrev == CH_BLANK ) ) + nSpaceSum = nHalfSpace; + for (sal_Int32 i = 1; i < sal_Int32(nCnt); ++i, nKernSum += rInf.GetKern()) + { + nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i]; + + OSL_ENSURE( pScrArray, "Where is the screen array?" ); + long nScr; + nScr = pScrArray[ i ] - pScrArray[ i - 1 ]; + + // If there is an (ex-)Space before us, position optimally, + // i.e., our right margin to the 100% printer position; + // if we _are_ an ex-Space, position us left-aligned to the + // printer position. + if ( nCh == CH_BLANK ) + { + nScrPos = pKernArray[i-1] + nScr; + + if ( cChPrev == CH_BLANK ) + nSpaceSum += nOtherHalf; + if (i + 1 == sal_Int32(nCnt)) + nSpaceSum += nSpaceAdd; + else + nSpaceSum += nHalfSpace; + } + else + { + if ( cChPrev == CH_BLANK ) + { + nScrPos = pKernArray[i-1] + nScr; + // no Pixel is lost: + nSpaceSum += nOtherHalf; + } + else if ( cChPrev == '-' ) + nScrPos = pKernArray[i-1] + nScr; + else + { + nScrPos += nScr; + nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv; + } + } + cChPrev = nCh; + pKernArray[i-1] = nScrPos - nScr + 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 || m_pPrtFont->IsWordLineMode()) && i+1 == sal_Int32(nCnt) && nCh == CH_BLANK) + pKernArray[i-1] = pKernArray[i-1] - nSpaceAdd; + } + + // the layout engine requires the total width of the output + pKernArray[sal_Int32(rInf.GetLen()) - 1] += nKernSum + nSpaceSum; + + if( rInf.GetGreyWave() ) + { + if( rInf.GetLen() ) + { + 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; + long nKernVal = pKernArray[sal_Int32(rInf.GetLen()) - 1]; + + const sal_uInt16 nDir = bBidiPor + ? 1800 + : UnMapDirection(GetFont().GetOrientation(), + bSwitchH2V, bSwitchH2VLRBT); + + switch ( nDir ) + { + 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()) + { + CalcLinePosData aCalcLinePosData(rInf, GetFont(), nCnt, bSwitchH2V, + bSwitchH2VLRBT, bSwitchL2R, nHalfSpace, + pKernArray.get(), 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 ); + + // If we paint bullets instead of spaces, we use a copy of + // the paragraph string. For the layout engine, the copy + // of the string has to be an environment of the range which + // is painted + sal_Int32 nTmpIdx = bBullet + ? (rInf.GetIdx() ? 1 : 0) + : sal_Int32(rInf.GetIdx()); + aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen }; + pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(), + nTmpIdx , 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 ); + long nShift = rInf.GetOut( ).GetFontMetric( ).GetBulletOffset( ); + if ( nShift ) + { + long nAdd = 0; + + if (aBulletOverlay.getLength() > nTmpIdx && + aBulletOverlay[ nTmpIdx ] == CH_BULLET ) + { + if (bSwitchH2V) + aTextOriginPos.AdjustY(nShift ) ; + else + aTextOriginPos.AdjustX(nShift ) ; + nAdd = nShift ; + } + for( sal_Int32 i = 1 ; i < nLen ; ++i ) + { + if ( aBulletOverlay[ i + nTmpIdx ] == CH_BULLET ) + pKernArray [ i - 1 ] += nShift ; + if ( nAdd ) + pKernArray [ i - 1 ] -= nAdd; + } + } + rInf.GetOut().DrawTextArray( aTextOriginPos, aBulletOverlay, pKernArray.get(), + nTmpIdx , nLen ); + pTmpFont->SetColor( aPreviousColor ); + + pTmpFont->SetUnderline(aPreviousUnderline); + pTmpFont->SetOverline(aPreviousOverline); + pTmpFont->SetStrikeout(aPreviousStrikeout); + rInf.GetOut().Pop(); + } + } + } + } +} + +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() && pGrid->IsSnapToChars() ) + { + 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() ) ); + + long nAvgWidthPerChar = aTextSize.Width() / sal_Int32(nLn); + + const sal_uLong i = nAvgWidthPerChar ? + ( nAvgWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + aTextSize.setWidth(i * nGridWidth * sal_Int32(nLn)); + rInf.SetKanaDiff( 0 ); + return aTextSize; + } + } + + //for textgrid refactor + if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() ) + { + SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame())); + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf ); + 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))); + aTextSize.setHeight( pOutDev->GetTextHeight() + + GetFontLeading( rInf.GetShell(), rInf.GetOut() ) ); + aTextSize.AdjustWidth(sal_Int32(nLn) * nGridWidthAdd); + //if ( rInf.GetKern() && nLn ) + // aTextSize.Width() += ( nLn ) * long( rInf.GetKern() ); + + 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) + if ( m_pPrinter && m_pPrinter.get() != rInf.GetpOut() ) + { + if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) ) + m_pPrinter->SetFont(*m_pPrtFont); + aTextSize.setWidth( m_pPrinter->GetTextWidth( rInf.GetText(), + sal_Int32(rInf.GetIdx()), sal_Int32(nLn))); + aTextSize.setHeight( m_pPrinter->GetTextHeight() ); + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(nLn)]); + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); + if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *m_pScrFont ); + long nScrPos; + + m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(nLn)); + if( bCompress ) + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(), + rInf.GetIdx(), nLn, rInf.GetKanaComp(), + static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) ); + else + rInf.SetKanaDiff( 0 ); + + if ( rInf.GetKanaDiff() ) + nScrPos = pKernArray[ sal_Int32(nLn) - 1 ]; + else + { + std::unique_ptr<long[]> pScrArray(new long[sal_Int32(rInf.GetLen())]); + rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + nScrPos = pScrArray[ 0 ]; + TextFrameIndex nCnt(rInf.GetText().getLength()); + if ( nCnt < rInf.GetIdx() ) + nCnt = TextFrameIndex(0); // assert??? + else + nCnt = nCnt - rInf.GetIdx(); + nCnt = std::min(nCnt, nLn); + sal_Unicode nChPrev = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ]; + + sal_Unicode nCh; + + // In case of Pair Kerning the printer influence on the positioning + // grows + const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3; + const int nDiv = nMul+1; + for (sal_Int32 i = 1; i < sal_Int32(nCnt); i++) + { + nCh = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + i ]; + long nScr; + nScr = pScrArray[ i ] - pScrArray[ i - 1 ]; + if ( nCh == CH_BLANK ) + nScrPos = pKernArray[i-1]+nScr; + else + { + if ( nChPrev == CH_BLANK || nChPrev == '-' ) + nScrPos = pKernArray[i-1]+nScr; + else + { + nScrPos += nScr; + nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv; + } + } + nChPrev = nCh; + pKernArray[i-1] = nScrPos - nScr; + } + } + + pKernArray.reset(); + aTextSize.setWidth( nScrPos ); + } + else + { + if( !m_pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *m_pPrtFont ); + if( bCompress ) + { + std::unique_ptr<long[]> pKernArray( new long[sal_Int32(nLn)] ); + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(nLn)); + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(), + rInf.GetIdx(), nLn, rInf.GetKanaComp(), + static_cast<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) ); + aTextSize.setWidth( pKernArray[sal_Int32(nLn) - 1] ); + } + else + { + SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(nLn) }; + SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + aTextSize.setWidth( rInf.GetOut().GetTextWidth( rInf.GetText(), + sal_Int32(rInf.GetIdx()), sal_Int32(nLn), + rInf.GetVclCache(), pGlyphs) ); + rInf.SetKanaDiff( 0 ); + } + + aTextSize.setHeight( rInf.GetOut().GetTextHeight() ); + } + + if ( rInf.GetKern() && nLn ) + aTextSize.AdjustWidth((sal_Int32(nLn) - 1) * 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) +{ + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + const long nSperren = -rInf.GetSperren() / SPACING_PRECISION_FACTOR; + long nKern = rInf.GetKern(); + + if( 0 != nSperren ) + nKern -= nSperren; + + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + + // 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() ); + SwTextGlyphsKey aGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) }; + SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]); + m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs); + } + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + + 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( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + static_cast<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(), pKernArray.get(), nullptr, + 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( pKernArray.get(), nullptr, 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(), pKernArray.get(), nullptr, + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + nSpaceAdd = 0; + } + } + } + + long nLeft = 0; + long nRight = 0; + TextFrameIndex nCnt(0); + long nSpaceSum = 0; + long nKernSum = 0; + + 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() && pGrid->IsSnapToChars() ) + { + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc); + + long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen()); + + sal_uLong i = nAvgWidthPerChar ? + ( nAvgWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nAvgWidthPerChar = i * nGridWidth; + +// stupid CLANG + nCnt = TextFrameIndex(rInf.GetOffset() / nAvgWidthPerChar); + if (2 * (rInf.GetOffset() - sal_Int32(nCnt) * nAvgWidthPerChar) > nAvgWidthPerChar) + ++nCnt; + + return nCnt; + } + } + + //for textgrid refactor + if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() ) + { + SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame())); + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + + const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf ); + + for (TextFrameIndex j(0); j < rInf.GetLen(); j++) + { + long nScr = pKernArray[sal_Int32(j)] + (nSpaceAdd + nGridWidthAdd) * (sal_Int32(j) + 1); + if( nScr >= rInf.GetOffset()) + { + nCnt = j; + break; + } + } + return nCnt; + } + } + + 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 < 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; + + nLeft = nRight; + nRight = pKernArray[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 > 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, 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); + 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() && pGrid->IsSnapToChars() ) + { + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc); + + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())]); + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + + long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen()); + + const sal_uLong i = nAvgWidthPerChar ? + ( nAvgWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nAvgWidthPerChar = i * nGridWidth; + long nCurrPos = nAvgWidthPerChar; + + while( nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos ) + { + nCurrPos += nAvgWidthPerChar; + ++nTextBreak; + } + + return nTextBreak + rInf.GetIdx(); + } + } + + //for text grid enhancement + if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() ) + { + SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame())); + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf ); + + std::unique_ptr<long[]> pKernArray(new long[sal_Int32(rInf.GetLen())] ); + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + long nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd; + while (++nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos) + { + nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd * (sal_Int32(nTextBreak) + 1); + } + 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(), OUString(aSnippet[0]) ); + } + } + + pTmpText = &aTmpText; + nTmpIdx = TextFrameIndex(0); + nTmpLen = TextFrameIndex(aTmpText.getLength()); + bTextReplaced = true; + } + + if( rInf.GetHyphPos() ) { + sal_Int32 nHyphPos = sal_Int32(*rInf.GetHyphPos()); + nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak( + *pTmpText, nTextWidth, + u'-', nHyphPos, + sal_Int32(nTmpIdx), sal_Int32(nTmpLen), + nKern, rInf.GetVclCache())); + *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()); + SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), *pTmpText, sal_Int32(nTmpIdx), sal_Int32(nTmpLen) }; + SalLayoutGlyphs* pGlyphs + = lcl_CreateLayout(aGlyphsKey, aFntAccess.Get()->GetTextGlyphs()[aGlyphsKey]); + 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; + + 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::unique_ptr<long[]> pKernArray( new long[sal_Int32(nLn)] ); + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(), + sal_Int32(rInf.GetIdx()), sal_Int32(nLn)); + if( rInf.GetScriptInfo()->Compress( pKernArray.get(), rInf.GetIdx(), nLn, + rInf.GetKanaComp(), static_cast<sal_uInt16>(GetHeight( m_nActual )), + lcl_IsFullstopCentered( rInf.GetOut() ) ) ) + { + long nKernAdd = nKern; + TextFrameIndex const nTmpBreak = nTextBreak2; + if( nKern && nTextBreak2 ) + nKern *= sal_Int32(nTextBreak2) - 1; + while (nTextBreak2 < nLn && nTextWidth >= pKernArray[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 + const Color* pCol = GetFont() ? GetFont()->GetBackColor() : nullptr; + Color aColor; + 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. + aColor = Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor())); + pCol = &aColor; + } + + // 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 = nullptr; + } + else + pCol = nullptr; + } + + // 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() && + !SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews()) + 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; +} + +void SwClearFntCacheTextGlyphs() +{ + for (SwFntObj* pFntObj = pFntCache->First(); pFntObj; pFntObj = SwFntCache::Next(pFntObj)) + pFntObj->GetTextGlyphs().clear(); +} + +/* 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..8ab9cf830 --- /dev/null +++ b/sw/source/core/txtnode/fntcap.cxx @@ -0,0 +1,778 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 &rInf; + SwCapitalInfo* pCapInf; // refers to additional information + // required by the ::Do function + explicit SwDoCapitals ( SwDrawTextInfo &rInfo ) : rInf( rInfo ), pCapInf( nullptr ) { } + ~SwDoCapitals() {} +public: + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) = 0; + virtual void Do() = 0; + OutputDevice& GetOut() { return rInf.GetOut(); } + SwDrawTextInfo& GetInf() { return rInf; } + SwCapitalInfo* GetCapInf() const { return pCapInf; } + void SetCapInf( SwCapitalInfo& rNew ) { pCapInf = &rNew; } +}; + +namespace { + +class SwDoGetCapitalSize : public SwDoCapitals +{ +protected: + Size 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 aTextSize; } +}; + +} + +void SwDoGetCapitalSize::Init( SwFntObj *, SwFntObj * ) +{ + aTextSize.setHeight( 0 ); + aTextSize.setWidth( 0 ); +} + +void SwDoGetCapitalSize::Do() +{ + aTextSize.AdjustWidth(rInf.GetSize().Width() ); + if( rInf.GetUpper() ) + aTextSize.setHeight( rInf.GetSize().Height() ); +} + +Size SwSubFont::GetCapitalSize( SwDrawTextInfo& rInf ) +{ + // Start: + const 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: + long nTextWidth; + TextFrameIndex m_nBreak; + +public: + SwDoGetCapitalBreak( SwDrawTextInfo &rInfo, long const nWidth) + : SwDoCapitals ( rInfo ) + , 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 ( nTextWidth ) + { + if ( rInf.GetSize().Width() < nTextWidth ) + nTextWidth -= rInf.GetSize().Width(); + else + { + TextFrameIndex nEnd = rInf.GetEnd(); + m_nBreak = TextFrameIndex(GetOut().GetTextBreak( + rInf.GetText(), nTextWidth, sal_Int32(rInf.GetIdx()), + sal_Int32(rInf.GetLen()), 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 != rInf.GetLen() ) + m_nBreak = sw_CalcCaseMap( *rInf.GetFont(), + GetCapInf()->rString, + GetCapInf()->nIdx, + GetCapInf()->nLen, m_nBreak ); + else + m_nBreak = m_nBreak + GetCapInf()->nIdx; + } + + nTextWidth = 0; + } + } +} + +TextFrameIndex SwFont::GetCapitalBreak( SwViewShell const * pSh, const OutputDevice* pOut, + const SwScriptInfo* pScript, const OUString& rText, 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 *pUpperFnt; + SwFntObj *pLowerFnt; +public: + explicit SwDoDrawCapital( SwDrawTextInfo &rInfo ) : + SwDoCapitals( rInfo ), pUpperFnt(nullptr), 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 ) +{ + pUpperFnt = pUpperFont; + pLowerFnt = pLowerFont; +} + +void SwDoDrawCapital::Do() +{ + SV_STAT( nDrawText ); + const sal_uInt16 nOrgWidth = rInf.GetWidth(); + rInf.SetWidth( sal_uInt16(rInf.GetSize().Width()) ); + if ( rInf.GetUpper() ) + pUpperFnt->DrawText( rInf ); + else + { + bool bOldBullet = rInf.GetBullet(); + rInf.SetBullet( false ); + pLowerFnt->DrawText( rInf ); + rInf.SetBullet( bOldBullet ); + } + + OSL_ENSURE( pUpperFnt, "No upper font, dying soon!"); + rInf.Shift( pUpperFnt->GetFont().GetOrientation() ); + rInf.SetWidth( nOrgWidth ); +} + +void SwDoDrawCapital::DrawSpace( Point &rPos ) +{ + long nDiff = rInf.GetPos().X() - rPos.X(); + + Point aPos( rPos ); + const bool bSwitchL2R = rInf.GetFrame()->IsRightToLeft() && + ! rInf.IsIgnoreFrameRTL(); + + if ( bSwitchL2R ) + rInf.GetFrame()->SwitchLTRtoRTL( aPos ); + + const ComplexTextLayoutFlags nMode = rInf.GetpOut()->GetLayoutMode(); + const bool bBidiPor = ( bSwitchL2R != + ( ComplexTextLayoutFlags::Default != ( ComplexTextLayoutFlags::BiDiRtl & nMode ) ) ); + + if ( bBidiPor ) + nDiff = -nDiff; + + if ( rInf.GetFrame()->IsVertical() ) + rInf.GetFrame()->SwitchHorizontalToVertical( aPos ); + + if ( nDiff ) + { + rInf.ApplyAutoColor(); + GetOut().DrawStretchText( aPos, nDiff, + " ", 0, 2 ); + } + rPos.setX( rInf.GetPos().X() + 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 *pUpperFnt; + SwFntObj *pLowerFnt; + TextFrameIndex nCursor; + sal_uInt16 nOfst; +public: + SwDoCapitalCursorOfst( SwDrawTextInfo &rInfo, const sal_uInt16 nOfs ) : + SwDoCapitals( rInfo ), pUpperFnt(nullptr), pLowerFnt(nullptr), nCursor( 0 ), nOfst( nOfs ) + { } + virtual ~SwDoCapitalCursorOfst() {} + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) override; + virtual void Do() override; + + TextFrameIndex GetCursor() const { return nCursor; } +}; + +} + +void SwDoCapitalCursorOfst::Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) +{ + pUpperFnt = pUpperFont; + pLowerFnt = pLowerFont; +} + +void SwDoCapitalCursorOfst::Do() +{ + if ( nOfst ) + { + if ( static_cast<long>(nOfst) > rInf.GetSize().Width() ) + { + nOfst -= rInf.GetSize().Width(); + nCursor = nCursor + rInf.GetLen(); + } + else + { + SwDrawTextInfo aDrawInf( rInf.GetShell(), *rInf.GetpOut(), + rInf.GetScriptInfo(), + rInf.GetText(), + rInf.GetIdx(), + rInf.GetLen(), 0, false ); + aDrawInf.SetOffset( nOfst ); + aDrawInf.SetKern( rInf.GetKern() ); + aDrawInf.SetKanaComp( rInf.GetKanaComp() ); + aDrawInf.SetFrame( rInf.GetFrame() ); + aDrawInf.SetFont( rInf.GetFont() ); + + if ( rInf.GetUpper() ) + { + aDrawInf.SetSpace( 0 ); + nCursor = nCursor + pUpperFnt->GetModelPositionForViewPoint( aDrawInf ); + } + else + { + aDrawInf.SetSpace( rInf.GetSpace() ); + nCursor = nCursor + pLowerFnt->GetModelPositionForViewPoint( aDrawInf ); + } + nOfst = 0; + } + } +} + +TextFrameIndex SwSubFont::GetCapitalCursorOfst( SwDrawTextInfo& rInf ) +{ + const 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 nStrLen; + const sal_uInt16 nCapWidth; + const sal_uInt16 nOrgWidth; +public: + virtual void Do() override; + + SwDoDrawStretchCapital( SwDrawTextInfo &rInfo, const sal_uInt16 nCapitalWidth ) + : SwDoDrawCapital( rInfo ), + nStrLen( rInfo.GetLen() ), + nCapWidth( nCapitalWidth ), + nOrgWidth( rInfo.GetWidth() ) + { } +}; + +} + +void SwDoDrawStretchCapital::Do() +{ + SV_STAT( nDrawStretchText ); + long nPartWidth = rInf.GetSize().Width(); + + if( rInf.GetLen() ) + { + // small caps and kerning + long nDiff = long(nOrgWidth) - long(nCapWidth); + if( nDiff ) + { + nDiff *= sal_Int32(rInf.GetLen()); + nDiff /= sal_Int32(nStrLen); + nDiff += nPartWidth; + if( 0 < nDiff ) + nPartWidth = nDiff; + } + + rInf.ApplyAutoColor(); + + Point aPos( rInf.GetPos() ); + const bool bSwitchL2R = rInf.GetFrame()->IsRightToLeft() && + ! rInf.IsIgnoreFrameRTL(); + + if ( bSwitchL2R ) + rInf.GetFrame()->SwitchLTRtoRTL( aPos ); + + if ( rInf.GetFrame()->IsVertical() ) + rInf.GetFrame()->SwitchHorizontalToVertical( aPos ); + + // Optimise: + if (TextFrameIndex(1) >= rInf.GetLen()) + GetOut().DrawText(aPos, rInf.GetText(), sal_Int32(rInf.GetIdx()), + sal_Int32(rInf.GetLen())); + else + GetOut().DrawStretchText(aPos, nPartWidth, rInf.GetText(), + sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen())); + } + const_cast<Point&>(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 = static_cast<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?!" ); + + 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 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().SetIdx(TextFrameIndex(0)); + rDo.GetInf().SetLen(TextFrameIndex(aNewText.getLength())); + rDo.GetInf().SetText( aNewText ); + } + else + { + rDo.GetInf().SetIdx( nOldPos ); + rDo.GetInf().SetLen( 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 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().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( !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/modeltoviewhelper.cxx b/sw/source/core/txtnode/modeltoviewhelper.cxx new file mode 100644 index 000000000..4675a632a --- /dev/null +++ b/sw/source/core/txtnode/modeltoviewhelper.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 <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 <set> +#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; + } +}; + +} + +typedef std::set<FieldResult, sortfieldresults> FieldResultSet; + +namespace { + +struct block +{ + sal_Int32 m_nStart; + sal_Int32 m_nLen; + bool m_bVisible; + FieldResultSet 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); + + 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_FIELD: + case RES_TXTATR_ANNOTATION: + 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 *pDoc = rNode.GetDoc(); + aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode) + ? OUString(CHAR_ZWSP) + : rFootnote.GetViewNumStr(*pDoc, 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*> aDropDowns = + rNode.GetDoc()->getIDocumentMarkAccess()->getDropDownsFor(aPaM); + + for (sw::mark::IFieldmark *pMark : aDropDowns) + { + const sal_Int32 nDummyCharPos = pMark->GetMarkPos().nContent.GetIndex()-1; + 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, OUString()); + 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..e7f34081c --- /dev/null +++ b/sw/source/core/txtnode/ndhints.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 <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> +#include <set> +#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 +{ + auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart; + std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart); + auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd; + std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd()); + auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart; + std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart()); + m_bStartMapNeedsSorting = false; + m_bEndMapNeedsSorting = false; + m_bWhichMapNeedsSorting = false; +} + +void SwpHints::ResortStartMap() const +{ + auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart; + std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart); + m_bStartMapNeedsSorting = false; +} + +void SwpHints::ResortEndMap() const +{ + auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd; + std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd()); + m_bEndMapNeedsSorting = false; +} + +void SwpHints::ResortWhichMap() const +{ + m_bWhichMapNeedsSorting = false; + auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart; + std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart()); +} + +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..62b817138 --- /dev/null +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -0,0 +1,5326 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <fmtmeta.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 <rootfrm.hxx> +#include <expfld.hxx> +#include <section.hxx> +#include <mvsave.hxx> +#include <swcache.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/intitem.hxx> +#include <list.hxx> +#include <sortedobjs.hxx> +#include <calbck.hxx> +#include <attrhint.hxx> +#include <memory> +#include <unoparagraph.hxx> +#include <wrtsh.hxx> +#include <frameformats.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 ); + + // call method <UpdateOutlineNode(..)> only for the document nodes array + if ( IsDocNodes() ) + UpdateOutlineNode(*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 -= 2; + else + return pNode; + break; + } + } while( true ); +} + +SwTextNode::SwTextNode( const SwNodeIndex &rWhere, SwTextFormatColl *pTextColl, const SfxItemSet* pAutoAttr ) +: SwContentNode( rWhere, SwNodeType::Text, pTextColl ), + m_Text(), + m_pParaIdleData_Impl(nullptr), + m_bContainsHiddenChars(false), + m_bHiddenCharsHidePara(false), + m_bRecalcHiddenCharFlags(false), + m_bLastOutlineState( false ), + m_bNotifiable( false ), + mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ), + mbInSetOrResetAttr( false ), + m_pNumStringCache(), + m_wXParagraph(), + maFillAttributes() +{ + 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(); + } + 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(); +} + +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() ) + { + 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 (sal_uLong 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.NotifyClients(&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)> 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); + } + 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()->IsHideRedlines()) + { + isHide = true; + } + frames.push_back(pFrame); + } + for (SwTextFrame * pFrame : frames) + { + pFrame->RegisterToNode( *pNode ); + if (!pFrame->IsFollow() && pFrame->GetOffset()) + { + pFrame->SetOffset( TextFrameIndex(0) ); + } + } + + if ( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + + 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); + } + if (eOldMergeFlag != SwNode::Merge::None) + { + MoveMergedFlysAndFootnotes(frames, *pNode, *this, true); + } + } + else + { + SwWrongList *pList = GetWrong(); + SetWrong( nullptr, false ); + SetWrongDirty(WrongState::TODO); + + SwGrammarMarkUp *pList3 = GetGrammarCheck(); + SetGrammarCheck( nullptr, false ); + SetGrammarCheckDirty( true ); + + SetWordCountDirty( true ); + + SwWrongList *pList2 = GetSmartTags(); + SetSmartTags( nullptr, false ); + 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( pList, false ); + } + + if( pList3 ) + { + pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) ); + SetGrammarCheck( pList3, false ); + } + + if( pList2 ) + { + pNode->SetSmartTags( pList2->SplitList( nSplitPos ) ); + SetSmartTags( pList2, false ); + } + + if (pContentIndexRestore) + { // call before making frames and before RegisterToNode + (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys); + } + + 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()->IsHideRedlines()) + { + 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 (pFrame->GetText().isEmpty()) + { + // turns out it's empty - 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); + } + + if (bRecreateThis) + { + MoveMergedFlysAndFootnotes(frames, *pNode, *this, true); + } + } + +#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 SfxPoolItem *pItem; + if( HasWriterListeners() && SfxItemState::SET == pNode->GetSwAttrSet(). + GetItemState( RES_PAGEDESC, true, &pItem ) ) + { + pNode->ModifyNotification( 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()->IsHideRedlines()) + { + 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()->IsHideRedlines()) + { + 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() != 0) + { + for (sal_uLong 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()->IsHideRedlines()) + { + 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)); + assert(pFrame->GetMergedPara()); + assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode)); + assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex()); + 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()->IsHideRedlines()) + { + rNode.SetRedlineMergeFlag(SwNode::Merge::None); + break; // checking once is enough + } + } + } +} + +} // namespace + +SwContentNode *SwTextNode::JoinNext() +{ + SwNodes& rNds = GetNodes(); + SwNodeIndex aIdx( *this ); + if( SwContentNode::CanJoinNext( &aIdx ) ) + { + SwDoc* pDoc = rNds.GetDoc(); + const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); + pContentStore->Save(pDoc, aIdx.GetIndex(), SAL_MAX_INT32); + SwTextNode *pTextNode = aIdx.GetNode().GetTextNode(); + sal_Int32 nOldLen = m_Text.getLength(); + + // METADATA: merge + JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len()); + + SwWrongList *pList = GetWrong(); + if( pList ) + { + pList->JoinList( pTextNode->GetWrong(), nOldLen ); + SetWrongDirty(WrongState::TODO); + SetWrong( nullptr, false ); + } + else + { + pList = pTextNode->GetWrong(); + if( pList ) + { + pList->Move( 0, nOldLen ); + SetWrongDirty(WrongState::TODO); + pTextNode->SetWrong( nullptr, false ); + } + } + + SwGrammarMarkUp *pList3 = GetGrammarCheck(); + if( pList3 ) + { + pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen ); + SetGrammarCheckDirty( true ); + SetGrammarCheck( nullptr, false ); + } + else + { + pList3 = pTextNode->GetGrammarCheck(); + if( pList3 ) + { + pList3->MoveGrammar( 0, nOldLen ); + SetGrammarCheckDirty( true ); + pTextNode->SetGrammarCheck( nullptr, false ); + } + } + + SwWrongList *pList2 = GetSmartTags(); + if( pList2 ) + { + pList2->JoinList( pTextNode->GetSmartTags(), nOldLen ); + SetSmartTagDirty( true ); + SetSmartTags( nullptr, false ); + } + else + { + pList2 = pTextNode->GetSmartTags(); + if( pList2 ) + { + pList2->Move( 0, nOldLen ); + SetSmartTagDirty( true ); + pTextNode->SetSmartTags( nullptr, false ); + } + } + + { // scope for SwIndex + pTextNode->CutText( this, SwIndex(pTextNode), pTextNode->Len() ); + } + // move all Bookmarks/TOXMarks + if( !pContentStore->Empty()) + pContentStore->Restore( pDoc, GetIndex(), nOldLen ); + + if( pTextNode->HasAnyIndex() ) + { + // move all ShellCursor/StackCursor/UnoCursor out of delete range + pDoc->CorrAbs( aIdx, SwPosition( *this ), nOldLen, true ); + } + SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag()); + rNds.Delete(aIdx); + SetWrong( pList, false ); + SetGrammarCheck( pList3, false ); + SetSmartTags( pList2, false ); + InvalidateNumRule(); + CheckResetRedlineMergeFlag(*this, eOldMergeFlag == SwNode::Merge::First + ? sw::Recreate::ThisNode + : sw::Recreate::No); + } + else { + OSL_FAIL( "No TextNode." ); + } + + return this; +} + +void SwTextNode::JoinPrev() +{ + SwNodes& rNds = GetNodes(); + SwNodeIndex aIdx( *this ); + if( SwContentNode::CanJoinPrev( &aIdx ) ) + { + SwDoc* pDoc = rNds.GetDoc(); + const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); + pContentStore->Save( pDoc, aIdx.GetIndex(), SAL_MAX_INT32); + SwTextNode *pTextNode = aIdx.GetNode().GetTextNode(); + const sal_Int32 nLen = pTextNode->Len(); + + SwWrongList *pList = pTextNode->GetWrong(); + if( pList ) + { + pList->JoinList( GetWrong(), Len() ); + SetWrongDirty(WrongState::TODO); + pTextNode->SetWrong( nullptr, false ); + SetWrong( nullptr ); + } + else + { + pList = GetWrong(); + if( pList ) + { + pList->Move( 0, nLen ); + SetWrongDirty(WrongState::TODO); + SetWrong( nullptr, false ); + } + } + + SwGrammarMarkUp *pList3 = pTextNode->GetGrammarCheck(); + if( pList3 ) + { + pList3->JoinGrammarList( GetGrammarCheck(), Len() ); + SetGrammarCheckDirty( true ); + pTextNode->SetGrammarCheck( nullptr, false ); + SetGrammarCheck( nullptr ); + } + else + { + pList3 = GetGrammarCheck(); + if( pList3 ) + { + pList3->MoveGrammar( 0, nLen ); + SetGrammarCheckDirty( true ); + SetGrammarCheck( nullptr, false ); + } + } + + SwWrongList *pList2 = pTextNode->GetSmartTags(); + if( pList2 ) + { + pList2->JoinList( GetSmartTags(), Len() ); + SetSmartTagDirty( true ); + pTextNode->SetSmartTags( nullptr, false ); + SetSmartTags( nullptr ); + } + else + { + pList2 = GetSmartTags(); + if( pList2 ) + { + pList2->Move( 0, nLen ); + SetSmartTagDirty( true ); + SetSmartTags( nullptr, false ); + } + } + + { // scope for SwIndex + pTextNode->CutText( this, SwIndex(this), SwIndex(pTextNode), nLen ); + } + // move all Bookmarks/TOXMarks + if( !pContentStore->Empty() ) + pContentStore->Restore( pDoc, GetIndex() ); + + if( pTextNode->HasAnyIndex() ) + { + // move all ShellCursor/StackCursor/UnoCursor out of delete range + pDoc->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( pList, false ); + SetGrammarCheck( pList3, false ); + SetSmartTags( pList2, false ); + 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(); + for (SwRangeRedline* pRedl : rTable) + { + 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 OSL_DEBUG_LEVEL > 0 + std::vector<SwFrameFormat*> checkFormats; + const SwFrameFormats& rFormats = *GetDoc()->GetSpzFrameFormats(); + for (auto& rpFormat : rFormats) + { + const SwFormatAnchor& rAnchor = rpFormat->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. + #if 0 + rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex()); + #endif + checkFormats.push_back( rpFormat ); + } + } + } +#endif + std::vector<SwFrameFormat*> const*const pFlys(GetAnchoredFlys()); + for (size_t i = 0; pFlys && i != pFlys->size(); ++i) + { + SwFrameFormat const*const pFormat = (*pFlys)[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()); +#if OSL_DEBUG_LEVEL > 0 + auto checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat ); + assert( checkPos != checkFormats.end()); + checkFormats.erase( checkPos ); +#endif + } + } + } +#if OSL_DEBUG_LEVEL > 0 + assert( checkFormats.empty()); +#endif + + // 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 + SwContentFrame* pContentFrame = getLayoutFrame(GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()); + SwSortedObjs* pSortedObjs = pContentFrame ? pContentFrame->GetDrawObjs() : nullptr; + if (pSortedObjs) + 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()) + { + 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* pDoc = GetDoc(); + OSL_ENSURE( pDoc, "No Doc?" ); + // 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); + } + if (pDoc) + { + pDoc->GetNodes().UpdateOutlineNode(*this); + } + + SwNodes& rNds = GetNodes(); + // If Level 0 (Chapter), update the footnotes! + if( ( !nNewLevel || !nOldLevel) && pDoc && !pDoc->GetFootnoteIdxs().empty() && + FTNNUM_CHAPTER == pDoc->GetFootnoteInfo().m_eNum && + rNds.IsDocNodes() ) + { + SwNodeIndex aTmpIndex( rNds, GetIndex()); + + pDoc->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_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, const OUString& rName ) +{ + if( !rName.isEmpty() ) + { + 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 (SwModify). + 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* const pDoc = static_txtattr_cast< + const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc(); + if ( pDoc ) + { + const SwCharFormats* pCharFormats = pDoc->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->ModifyNotification( nullptr, &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_pNumStringCache.reset( (nTextStartIdx != 0) + ? new OUString // fdo#49076: numbering only if copy from para start + : new OUString(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())) + { + SfxItemSet aCharSet( + pDest->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>{} ); + 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())) + { + SfxItemSet aCharSet( + pDest->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>{}); + 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 aHint( aPos, nLen ); + NotifyClients( 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.copy(nTextStartIdx, nLen)); + OUString const newText = m_Text.replaceAt(nTextStartIdx, nLen, ""); + 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 + sal_uInt16 nWhich = IsInvalidItem( pItem ) + ? pDest->GetpSwAttrSet()->GetWhichByPos( aIter.GetCurPos() ) + : pItem->Which(); + if( RES_FRMATR_STYLE_NAME != nWhich && + RES_FRMATR_CONDITIONAL_STYLE_NAME != 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()) + { + SfxItemSet aCharSet( + pDest->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>{}); + aCharSet.Put( *GetpSwAttrSet() ); + if( aCharSet.Count() ) + pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen ); + } + else + { + GetpSwAttrSet()->CopyToModify( *pDest ); + } + } + + // notify frames - before moving hints, because footnotes + // want to find their anchor text frame in the follow chain + SwInsText aInsHint( nDestStart, nLen ); + pDest->ModifyNotification( nullptr, &aInsHint ); + sw::MoveText const moveHint(pDest, nDestStart, nTextStartIdx, nLen); + CallSwClientNotify(moveHint); + SwDelText aDelHint( nTextStartIdx, nLen ); + ModifyNotification( 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, ""); + + // 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 ); + NotifyClients( nullptr, &aHint ); + } + else + { + SwDelText aHint( nStartIdx, nCnt ); + NotifyClients( 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); + + NotifyClients( nullptr, &aHint ); + SwFormatChg aNew( GetTextColl() ); + NotifyClients( 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); + } + } + } + + if( IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + SetInSwFntCache( false ); + + // 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()); + NotifyClients( &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 && IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + } + 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 ) + { + std::vector<sal_uInt16> aClearWhichIds; + aClearWhichIds.push_back( RES_PARATR_NUMRULE ); + if ( ClearItemsFromAttrSet( aClearWhichIds ) != 0 && IsInCache() ) + { + SwFrame::GetCache().Delete( this ); + SetInCache( false ); + } + } + } + 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; +} + +namespace +{ + +sal_uInt16 lcl_BoundListLevel(const int nActualLevel) +{ + return static_cast<sal_uInt16>( std::min( std::max(nActualLevel, 0), MAXLEVEL-1 ) ); +} + +} + +// -> #i29560# +bool SwTextNode::HasNumber() const +{ + bool bResult = false; + + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr; + if ( pRule ) + { + const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel()))); + + // #i40041# + bResult = aFormat.IsEnumeration() && + SVX_NUM_NUMBER_NONE != aFormat.GetNumberingType(); + } + + 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) const +{ + if (GetDoc()->IsClipBoard() && m_pNumStringCache) + { + // #i111677# do not expand number strings in clipboard documents + return *m_pNumStringCache; + } + const SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr; + if ( pRule && + IsCountedInList() ) + { + SvxNumberType const& rNumberType( + pRule->Get( lcl_BoundListLevel(GetActualListLevel()) ) ); + if (rNumberType.IsTextFormat() || + + (style::NumberingType::NUMBER_NONE == rNumberType.GetNumberingType())) + { + return pRule->MakeNumString( GetNum(pLayout)->GetNumberVector(), + _bInclPrefixAndSuffixStrings, + false, + _nRestrictToThisLevel, + nullptr, + GetLang(0)); + } + } + + return OUString(); +} + +long SwTextNode::GetLeftMarginWithNum( bool bTextLeft ) const +{ + 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# +long SwTextNode::GetLeftMarginForTabCalculation() const +{ + 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()) + { + 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.copy(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); + + 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* pDoc = GetDoc(); + SwRedlineTable::size_type nRedlPos = pDoc->getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete ); + if( SwRedlineTable::npos != nRedlPos ) + { + // some redline-delete object exists for the node + const sal_uLong nNdIdx = GetIndex(); + for( ; nRedlPos < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos ) + { + const SwRangeRedline* pTmp = pDoc->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.copy(0, 1)); + + ++const_cast<SwIndex&>(rStart); + m_Text = m_Text.replaceAt(rStart.GetIndex(), nLen - 1, ""); + Update( rStart, nLen - 1, true ); + + OUString aTmpText( sInserted.copy(1) ); + m_Text = m_Text.replaceAt(rStart.GetIndex(), 0, aTmpText); + Update( rStart, aTmpText.getLength() ); + } + else + { + m_Text = m_Text.replaceAt(nStartPos, nLen, ""); + Update( rStart, nLen, true ); + + m_Text = m_Text.replaceAt(nStartPos, 0, sInserted); + Update( rStart, sInserted.getLength() ); + } + + SetIgnoreDontExpand( bOldExpFlg ); + SwDelText aDelHint( nStartPos, nDelLen ); + NotifyClients( nullptr, &aDelHint ); + + if (sInserted.getLength()) + { + SwInsText aHint( nStartPos, sInserted.getLength() ); + NotifyClients( nullptr, &aHint ); + } +} + +namespace { + void lcl_ResetParAttrs( SwTextNode &rTextNode ) + { + std::set<sal_uInt16> aAttrs; + aAttrs.insert( aAttrs.end(), RES_PARATR_LIST_ID ); + aAttrs.insert( aAttrs.end(), RES_PARATR_LIST_LEVEL ); + aAttrs.insert( aAttrs.end(), RES_PARATR_LIST_ISRESTART ); + aAttrs.insert( aAttrs.end(), RES_PARATR_LIST_RESTARTVALUE ); + aAttrs.insert( aAttrs.end(), 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 SfxPoolItem* pItem = nullptr; + 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, &pItem ) == + 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." ); + OSL_ENSURE( dynamic_cast<const SwTextFormatColl *>(pNewColl) != nullptr, + "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) ); + } + + GetNodes().UpdateOutlineNode(*this); + + return pOldColl; +} + +const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout) 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() ? mpNodeNumRLHidden.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); + assert(mpNodeNum); + rFunc(*mpNodeNum); + if (pBackup) + { + mpNodeNumRLHidden = std::move(pBackup); + rFunc(*mpNodeNumRLHidden); + } +} + +SwNumberTree::tNumberVector +SwTextNode::GetNumberVector(SwRootFrame const*const pLayout) const +{ + if (SwNodeNum const*const pNum = GetNum(pLayout)) + { + 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 static_cast<const SfxUInt16Item &>(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, + static_cast<sal_uInt16>(nLevel) ) ); + } +} + +// #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() const +{ + assert(!GetNum() || !mpNodeNumRLHidden || // must be in sync + GetNum()->GetLevelInListTree() == mpNodeNumRLHidden->GetLevelInListTree()); + return GetNum() ? GetNum()->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 = + dynamic_cast<const SfxBoolItem&>(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()) + { + // #i87154# + // Correction of #newlistlevelattrs#: + // The numbering type has to be checked for bullet lists. + const SwNumFormat& rFormat = pRule->Get( lcl_BoundListLevel(GetActualListLevel()) ); + 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() ) + { + 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( static_cast<sal_uInt16>(GetAttrListLevel()) ); + if ( pFormat ) + { + nListRestartValue = pFormat->GetStart(); + } + } + } + + return nListRestartValue; +} + +bool SwTextNode::IsNotifiable() const +{ + return m_bNotifiable && IsNotificationEnabled(); +} + +bool SwTextNode::IsNotificationEnabled() const +{ + bool bResult = false; + const SwDoc * pDoc = GetDoc(); + if( pDoc ) + { + bResult = !(pDoc->IsInReading() || pDoc->IsInDtor()); + } + return bResult; +} + +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 = + dynamic_cast<const SfxBoolItem&>(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 + { + assert(!mpNodeNum); + mpNodeNum.reset(new SwNodeNum(this, false)); + pList->InsertListItem(*mpNodeNum, false, GetAttrListLevel()); + // iterate all frames & if there's one with hidden layout... + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this); + for (SwTextFrame* pFrame = iter.First(); pFrame; 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) + { + assert(false); + OSL_FAIL( "<SwTextNode::AddToListRLHidden()> - the text node is already added to a list. Serious defect" ); + return; + } + + SwList *const pList(FindList(this)); + if (pList) + { + assert(!mpNodeNumRLHidden); + mpNodeNumRLHidden.reset(new SwNodeNum(this, true)); + pList->InsertListItem(*mpNodeNumRLHidden, true, GetAttrListLevel()); + } +} + +void SwTextNode::RemoveFromList() +{ + // sw_redlinehide: ensure it's removed from the other half too! + RemoveFromListRLHidden(); + if ( IsInList() ) + { + SwList::RemoveListItem( *mpNodeNum ); + 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); + mpNodeNumRLHidden.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( long& nListTabStopPosition ) const +{ + bool bListTabStopPositionProvided(false); + + const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr; + if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 ) + { + const SwNumFormat& rFormat = pNumRule->Get( static_cast<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( static_cast<sal_uInt16>(GetActualListLevel()) ); + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + switch ( rFormat.GetLabelFollowedBy() ) + { + case SvxNumberFormat::LISTTAB: + { + return "\t"; + } + break; + case SvxNumberFormat::SPACE: + { + return " "; + } + break; + case SvxNumberFormat::NEWLINE: + { + return "\n"; + } + break; + case SvxNumberFormat::NOTHING: + { + // intentionally left blank. + } + break; + default: + { + OSL_FAIL( "<SwTextNode::GetLabelFollowedBy()> - unknown SvxNumberFormat::GetLabelFollowedBy() return value" ); + } + } + } + } + + 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 ) + { + const SfxPoolItem* pItem = nullptr; + // handle RES_PARATR_NUMRULE + if ( rItemSet.GetItemState( RES_PARATR_NUMRULE, false, &pItem ) == SfxItemState::SET ) + { + mrTextNode.RemoveFromList(); + + const SwNumRuleItem* pNumRuleItem = + dynamic_cast<const SwNumRuleItem*>(pItem); + assert(pNumRuleItem); + if ( !pNumRuleItem->GetValue().isEmpty() ) + { + mbAddTextNodeToList = true; + // #i70748# + mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + } + + // handle RES_PARATR_LIST_ID + if ( rItemSet.GetItemState( RES_PARATR_LIST_ID, false, &pItem ) == SfxItemState::SET ) + { + const SfxStringItem* pListIdItem = + dynamic_cast<const SfxStringItem*>(pItem); + const OUString sListIdOfTextNode = mrTextNode.GetListId(); + if ( pListIdItem && + pListIdItem->GetValue() != sListIdOfTextNode ) + { + mbAddTextNodeToList = true; + if ( mrTextNode.IsInList() ) + { + mrTextNode.RemoveFromList(); + } + } + } + + // handle RES_PARATR_LIST_LEVEL + if ( rItemSet.GetItemState( RES_PARATR_LIST_LEVEL, false, &pItem ) == SfxItemState::SET ) + { + const SfxInt16Item* pListLevelItem = + dynamic_cast<const SfxInt16Item*>(pItem); + if (pListLevelItem && pListLevelItem->GetValue() != mrTextNode.GetAttrListLevel()) + { + mbUpdateListLevel = true; + } + } + + // handle RES_PARATR_LIST_ISRESTART + if ( rItemSet.GetItemState( RES_PARATR_LIST_ISRESTART, false, &pItem ) == SfxItemState::SET ) + { + const SfxBoolItem* pListIsRestartItem = + dynamic_cast<const SfxBoolItem*>(pItem); + if (pListIsRestartItem && pListIsRestartItem->GetValue() != mrTextNode.IsListRestart()) + { + mbUpdateListRestart = true; + } + } + + // handle RES_PARATR_LIST_RESTARTVALUE + if ( rItemSet.GetItemState( RES_PARATR_LIST_RESTARTVALUE, false, &pItem ) == SfxItemState::SET ) + { + const SfxInt16Item* pListRestartValueItem = + dynamic_cast<const SfxInt16Item*>(pItem); + if ( !mrTextNode.HasAttrListRestartValue() || (pListRestartValueItem && + pListRestartValueItem->GetValue() != mrTextNode.GetAttrListRestartValue()) ) + { + mbUpdateListRestart = true; + } + } + + // handle RES_PARATR_LIST_ISCOUNTED + if ( rItemSet.GetItemState( RES_PARATR_LIST_ISCOUNTED, false, &pItem ) == SfxItemState::SET ) + { + const SfxBoolItem* pIsCountedInListItem = + dynamic_cast<const SfxBoolItem*>(pItem); + if (pIsCountedInListItem && pIsCountedInListItem->GetValue() != + mrTextNode.IsCountedInList()) + { + mbUpdateListCount = true; + } + } + + // #i70748# + // handle RES_PARATR_OUTLINELEVEL + if ( rItemSet.GetItemState( RES_PARATR_OUTLINELEVEL, false, &pItem ) == SfxItemState::SET ) + { + const SfxUInt16Item* pOutlineLevelItem = + dynamic_cast<const SfxUInt16Item*>(pItem); + if (pOutlineLevelItem && 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()); + mrTextNode.DoNum( + [nLevel](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel); }); + } + + if ( mbUpdateListRestart && mrTextNode.IsInList() ) + { + mrTextNode.DoNum( + [](SwNodeNum & rNum) { + rNum.InvalidateMe(); + rNum.NotifyInvalidSiblings(); + }); + } + + if ( mbUpdateListCount && mrTextNode.IsInList() ) + { + mrTextNode.DoNum( + [](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(); }); + } + } + + // #i70748# + if (mbOutlineLevelSet) + { + mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode); + if (mrTextNode.GetAttrOutlineLevel() == 0) + { + mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + else + { + const SfxPoolItem* pItem = nullptr; + if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE, + true, &pItem ) + != 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; +} + +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 + { + assert(!mrTextNode.GetpSwAttrSet() + || dynamic_cast<const SfxUInt16Item*>( + &mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false))); + if (mrTextNode.GetpSwAttrSet() + && static_cast<const SfxUInt16Item&>( + mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false)).GetValue() > 0) + { + mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr(); + } + } + } + + if ( mrTextNode.IsInList() ) + { + if ( mbUpdateListLevel ) + { + auto const nLevel(mrTextNode.GetAttrListLevel()); + mrTextNode.DoNum( + [nLevel](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel); }); + } + + if ( mbUpdateListRestart ) + { + mrTextNode.DoNum( + [](SwNodeNum & rNum) { + rNum.InvalidateMe(); + rNum.NotifyInvalidSiblings(); + }); + } + + if ( mbUpdateListCount ) + { + mrTextNode.DoNum( + [](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(); }); + } + } + } + // 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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextNode")); + xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(GetIndex()).getStr())); + + OUString sText = GetText(); + for (int i = 0; i < 32; ++i) + sText = sText.replace(i, '*'); + xmlTextWriterStartElement(pWriter, BAD_CAST("m_Text")); + xmlTextWriterWriteString(pWriter, BAD_CAST(sText.toUtf8().getStr())); + xmlTextWriterEndElement(pWriter); + + if (GetFormatColl()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toUtf8().getStr())); + xmlTextWriterEndElement(pWriter); + } + + if (HasSwAttrSet()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("SwAttrSet")); + GetSwAttrSet().dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + + if (HasHints()) + { + xmlTextWriterStartElement(pWriter, BAD_CAST("SwpHints")); + const SwpHints& rHints = GetSwpHints(); + for (size_t i = 0; i < rHints.Count(); ++i) + rHints.Get(i)->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); + } + + if (GetNumRule()) + GetNumRule()->dumpAsXml(pWriter); + + xmlTextWriterEndElement(pWriter); +} + +sal_uInt32 SwTextNode::GetRsid( sal_Int32 nStt, sal_Int32 nEnd ) const +{ + SfxItemSet aSet( const_cast<SfxItemPool&>(static_cast<SfxItemPool const &>(GetDoc()->GetAttrPool())), svl::Items<RES_CHRATR_RSID, RES_CHRATR_RSID>{} ); + 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::SwClientNotify( const SwModify& rModify, const SfxHint& rHint ) +{ + if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + bool bWasNotifiable = m_bNotifiable; + m_bNotifiable = false; + + const auto pOldValue = pLegacyHint->m_pOld; + const auto pNewValue = pLegacyHint->m_pNew; + // 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() ) + { + 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(rModify, rHint); + + SwDoc* pDoc = GetDoc(); + // #125329# - assure that text node is in document nodes array + if ( pDoc && !pDoc->IsInDtor() && &pDoc->GetNodes() == &GetNodes() ) + { + pDoc->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)); + } + } + 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..49783fd25 --- /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( true ); + 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..60d22422c --- /dev/null +++ b/sw/source/core/txtnode/swfont.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 <hintids.hxx> + +#include <com/sun/star/i18n/ScriptType.hpp> +#include <vcl/outdev.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/blinkitem.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( Color* pNewColor ) +{ + m_pBackColor.reset( pNewColor ); + 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)) + { + case 0 : + return m_aTopBorder; + break; + case 900 : + return m_aRightBorder; + break; + case 1800 : + return m_aBottomBorder; + break; + case 2700 : + return m_aLeftBorder; + break; + default : + assert(false); + return m_aTopBorder; + break; + } +} + +const std::optional<editeng::SvxBorderLine>& +SwFont::GetAbsBottomBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const +{ + switch (GetOrientation(bVertLayout, bVertLayoutLRBT)) + { + case 0 : + return m_aBottomBorder; + break; + case 900 : + return m_aLeftBorder; + break; + case 1800 : + return m_aTopBorder; + break; + case 2700 : + return m_aRightBorder; + break; + default : + assert(false); + return m_aBottomBorder; + break; + } +} + +const std::optional<editeng::SvxBorderLine>& +SwFont::GetAbsLeftBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const +{ + switch (GetOrientation(bVertLayout, bVertLayoutLRBT)) + { + case 0 : + return m_aLeftBorder; + break; + case 900 : + return m_aTopBorder; + break; + case 1800 : + return m_aRightBorder; + break; + case 2700 : + return m_aBottomBorder; + break; + default : + assert(false); + return m_aLeftBorder; + break; + } +} + +const std::optional<editeng::SvxBorderLine>& +SwFont::GetAbsRightBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const +{ + switch (GetOrientation(bVertLayout, bVertLayoutLRBT)) + { + case 0 : + return m_aRightBorder; + break; + case 900 : + return m_aBottomBorder; + break; + case 1800 : + return m_aLeftBorder; + break; + case 2700 : + return m_aTopBorder; + break; + default : + assert(false); + return m_aRightBorder; + break; + } +} + +SvxShadowLocation SwFont::GetAbsShadowLocation(const bool bVertLayout, + const bool bVertLayoutLRBT) const +{ + SvxShadowLocation aLocation = SvxShadowLocation::NONE; + switch (GetOrientation(bVertLayout, bVertLayoutLRBT)) + { + 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 sal_uInt16 nOrient = GetOrientation(bVertLayout, bVertLayoutLRBT); + const SvxShadowLocation aLoc = GetAbsShadowLocation(bVertLayout, bVertLayoutLRBT); + switch( nShadow ) + { + case SvxShadowItemSide::TOP: + if(( aLoc == SvxShadowLocation::TopLeft || + aLoc == SvxShadowLocation::TopRight ) && + ( nOrient == 0 || nOrient == 1800 || + ( nOrient == 900 && !bSkipRight ) || + ( nOrient == 2700 && !bSkipLeft ))) + { + nSpace = m_nShadowWidth; + } + break; + + case SvxShadowItemSide::BOTTOM: + if(( aLoc == SvxShadowLocation::BottomLeft || + aLoc == SvxShadowLocation::BottomRight ) && + ( nOrient == 0 || nOrient == 1800 || + ( nOrient == 900 && !bSkipLeft ) || + ( nOrient == 2700 && !bSkipRight ))) + { + nSpace = m_nShadowWidth; + } + break; + + case SvxShadowItemSide::LEFT: + if(( aLoc == SvxShadowLocation::TopLeft || + aLoc == SvxShadowLocation::BottomLeft ) && + ( nOrient == 900 || nOrient == 2700 || + ( nOrient == 0 && !bSkipLeft ) || + ( nOrient == 1800 && !bSkipRight ))) + { + nSpace = m_nShadowWidth; + } + break; + + case SvxShadowItemSide::RIGHT: + if(( aLoc == SvxShadowLocation::TopRight || + aLoc == SvxShadowLocation::BottomRight ) && + ( nOrient == 900 || nOrient == 2700 || + ( nOrient == 0 && !bSkipRight ) || + ( nOrient == 1800 && !bSkipLeft ))) + { + nSpace = m_nShadowWidth; + } + break; + default: + assert(false); + break; + } + + return nSpace; +} + +// maps directions for vertical layout +static sal_uInt16 MapDirection(sal_uInt16 nDir, const bool bVertFormat, const bool bVertFormatLRBT) +{ + if ( bVertFormat ) + { + switch ( nDir ) + { + case 0 : + if (bVertFormatLRBT) + nDir = 900; + else + nDir = 2700; + break; + case 900 : + nDir = 0; + break; + case 2700 : + nDir = 1800; + 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 +sal_uInt16 UnMapDirection(sal_uInt16 nDir, const bool bVertFormat, const bool bVertFormatLRBT) +{ + if (bVertFormatLRBT) + { + switch (nDir) + { + case 900: + nDir = 0; + break; + default: + SAL_WARN("sw.core", "unsupported direction for VertLRBT"); + break; + } + return nDir; + } + + if ( bVertFormat ) + { + switch ( nDir ) + { + case 0 : + nDir = 900; + break; + case 1800 : + nDir = 2700; + break; + case 2700 : + nDir = 0; + break; +#if OSL_DEBUG_LEVEL > 0 + default : + OSL_FAIL( "Unsupported direction" ); + break; +#endif + } + } + return nDir; +} + +sal_uInt16 SwFont::GetOrientation(const bool bVertFormat, const bool bVertFormatLRBT) const +{ + return UnMapDirection(m_aSub[m_nActual].GetOrientation(), bVertFormat, bVertFormatLRBT); +} + +void SwFont::SetVertical(sal_uInt16 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 long nAscent = nOldAscent + + ( static_cast<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 ) +{ + m_pBackColor.reset(); + + if( pAttrSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_FONT, + true, &pItem )) + { + const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_FONTSIZE, + true, &pItem )) + { + const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_POSTURE, + true, &pItem )) + m_aSub[SwFontScript::Latin].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_WEIGHT, + true, &pItem )) + m_aSub[SwFontScript::Latin].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_LANGUAGE, + true, &pItem )) + m_aSub[SwFontScript::Latin].SetLanguage( static_cast<const SvxLanguageItem*>(pItem)->GetLanguage() ); + + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONT, + true, &pItem )) + { + const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONTSIZE, + true, &pItem )) + { + const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_POSTURE, + true, &pItem )) + m_aSub[SwFontScript::CJK].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_WEIGHT, + true, &pItem )) + m_aSub[SwFontScript::CJK].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_LANGUAGE, + true, &pItem )) + { + LanguageType eNewLang = static_cast<const SvxLanguageItem*>(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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONT, + true, &pItem )) + { + const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONTSIZE, + true, &pItem )) + { + const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_POSTURE, + true, &pItem )) + m_aSub[SwFontScript::CTL].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_WEIGHT, + true, &pItem )) + m_aSub[SwFontScript::CTL].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_LANGUAGE, + true, &pItem )) + m_aSub[SwFontScript::CTL].SetLanguage( static_cast<const SvxLanguageItem*>(pItem)->GetLanguage() ); + + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_UNDERLINE, + true, &pItem )) + { + SetUnderline( static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle() ); + SetUnderColor( static_cast<const SvxUnderlineItem*>(pItem)->GetColor() ); + } + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_OVERLINE, + true, &pItem )) + { + SetOverline( static_cast<const SvxOverlineItem*>(pItem)->GetLineStyle() ); + SetOverColor( static_cast<const SvxOverlineItem*>(pItem)->GetColor() ); + } + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CROSSEDOUT, + true, &pItem )) + SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_COLOR, + true, &pItem )) + SetColor( static_cast<const SvxColorItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_EMPHASIS_MARK, + true, &pItem )) + SetEmphasisMark( static_cast<const SvxEmphasisMarkItem*>(pItem)->GetEmphasisMark() ); + + SetTransparent( true ); + SetAlign( ALIGN_BASELINE ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CONTOUR, + true, &pItem )) + SetOutline( static_cast<const SvxContourItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED, + true, &pItem )) + SetShadow( static_cast<const SvxShadowedItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_RELIEF, + true, &pItem )) + SetRelief( static_cast<const SvxCharReliefItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED, + true, &pItem )) + SetPropWidth(static_cast<const SvxShadowedItem*>(pItem)->GetValue() ? 50 : 100 ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_AUTOKERN, + true, &pItem )) + { + if( static_cast<const SvxAutoKernItem*>(pItem)->GetValue() ) + { + SetAutoKern( ( !pIDocumentSettingAccess || + !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ? + FontKerning::FontSpecific : + FontKerning::Asian ); + } + else + SetAutoKern( FontKerning::NONE ); + } + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_WORDLINEMODE, + true, &pItem )) + SetWordLineMode( static_cast<const SvxWordLineModeItem*>(pItem)->GetValue() ); + + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_ESCAPEMENT, + true, &pItem )) + { + const SvxEscapementItem *pEsc = static_cast<const SvxEscapementItem *>(pItem); + SetEscapement( pEsc->GetEsc() ); + if( m_aSub[SwFontScript::Latin].IsEsc() ) + SetProportion( pEsc->GetProportionalHeight() ); + } + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CASEMAP, + true, &pItem )) + SetCaseMap( static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_KERNING, + true, &pItem )) + SetFixKerning( static_cast<const SvxKerningItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_ROTATE, + true, &pItem )) + SetVertical( static_cast<const SvxCharRotateItem*>(pItem)->GetValue() ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND, + true, &pItem )) + m_pBackColor.reset( new Color( static_cast<const SvxBrushItem*>(pItem)->GetColor() ) ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_HIGHLIGHT, + true, &pItem )) + SetHighlightColor(static_cast<const SvxBrushItem*>(pItem)->GetColor()); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BOX, + true, &pItem )) + { + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOW, + true, &pItem )) + { + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + SetShadowColor(pShadowItem->GetColor()); + SetShadowWidth(pShadowItem->GetWidth()); + SetShadowLocation(pShadowItem->GetLocation()); + } + const SfxPoolItem* pTwoLinesItem = nullptr; + if( SfxItemState::SET == + pAttrSet->GetItemState( RES_CHRATR_TWO_LINES, true, &pTwoLinesItem )) + if ( static_cast<const SvxTwoLinesItem*>(pTwoLinesItem)->GetValue() ) + SetVertical( 0 ); + } + 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; + m_pBackColor.reset( rFont.m_pBackColor ? new Color( *rFont.m_pBackColor ) : nullptr ); + 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_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_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() ); + const SfxPoolItem* pItem; + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND, + true, &pItem )) + m_pBackColor.reset( new Color( static_cast<const SvxBrushItem*>(pItem)->GetColor() ) ); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_HIGHLIGHT, + true, &pItem )) + SetHighlightColor(static_cast<const SvxBrushItem*>(pItem)->GetColor()); + else + SetHighlightColor(COL_TRANSPARENT); + if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BOX, + true, &pItem )) + { + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + 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( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOW, + true, &pItem )) + { + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + 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 ); + 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; + m_pBackColor.reset( rFont.m_pBackColor ? new Color( *rFont.m_pBackColor ) : nullptr ); + 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_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() ) + { + long nDescent = nOldHeight - nOldAscent - + ( static_cast<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; +} + +Size SwSubFont::GetTextSize_( SwDrawTextInfo& rInf ) +{ + // Robust: the font is supposed to be set already, but better safe than + // sorry... + if ( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(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 ); + 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<long>(CalcEscHeight( static_cast<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() != reinterpret_cast<const void*>(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.GetSperren() / 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(); + 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.SetText( " " ); + rInf.SetIdx( TextFrameIndex(0) ); + rInf.SetLen( TextFrameIndex(2) ); + SetUnderline( nOldUnder ); + rInf.SetUnderFnt( nullptr ); + + // set position for underline font + rInf.SetPos( pUnderFnt->GetPos() ); + + pUnderFnt->GetFont().DrawStretchText_( rInf ); + + rInf.SetUnderFnt( pUnderFnt ); + rInf.SetText(oldStr); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( 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() != reinterpret_cast<const void*>(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.GetSperren() / 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.SetText( " " ); + rInf.SetIdx( TextFrameIndex(0) ); + rInf.SetLen( TextFrameIndex(2) ); + SetUnderline( nOldUnder ); + rInf.SetUnderFnt( nullptr ); + + // set position for underline font + rInf.SetPos( pUnderFnt->GetPos() ); + + pUnderFnt->GetFont().DrawStretchText_( rInf ); + + rInf.SetUnderFnt( pUnderFnt ); + rInf.SetText(oldStr); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + + rInf.SetPos(aOldPos); +} + +TextFrameIndex SwSubFont::GetModelPositionForViewPoint_( SwDrawTextInfo& rInf ) +{ + if ( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(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(); + 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 ) +{ + long nOfst; + + bool bVert = false; + bool bVertLRBT = false; + if (rInf.GetFrame()) + { + bVert = rInf.GetFrame()->IsVertical(); + bVertLRBT = rInf.GetFrame()->IsVertLRBT(); + } + const sal_uInt16 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 ) + { + 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 ) + { + case 0 : + rPos.AdjustY(nOfst ); + break; + case 900 : + rPos.AdjustX(nOfst ); + break; + case 2700 : + rPos.AdjustX( -nOfst ); + break; + } + + break; + default : + nOfst = (static_cast<long>(m_nOrgHeight) * GetEscapement()) / 100; + + switch ( nDir ) + { + 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( sal_uInt16 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() ) != + ( ComplexTextLayoutFlags::Default != ( ComplexTextLayoutFlags::BiDiRtl & GetpOut()->GetLayoutMode() ) ); + + bool bVert = false; + bool bVertLRBT = false; + if (GetFrame()) + { + bVert = GetFrame()->IsVertical(); + bVertLRBT = GetFrame()->IsVertLRBT(); + } + nDir = bBidiPor ? 1800 : UnMapDirection(nDir, bVert, bVertLRBT); + + switch ( nDir ) + { + 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 +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()); + 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..f31e39713 --- /dev/null +++ b/sw/source/core/txtnode/thints.cxx @@ -0,0 +1,3512 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/xmlcnitm.hxx> +#include <editeng/rsiditem.hxx> +#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 <txtfld.hxx> +#include <txtannotationfld.hxx> +#include <charfmt.hxx> +#include <frmfmt.hxx> +#include <ftnidx.hxx> +#include <fmtruby.hxx> +#include <fmtmeta.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 <unordered_map> + +#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 + } + 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)); + 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)); + 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 + +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. + +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_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<sal_uLong, 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: + + std::set<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(); + + aBounds.insert( nOtherStart ); + aBounds.insert( nOtherEnd ); + } + } + + std::set<sal_Int32>::iterator aStartIter = aBounds.lower_bound( nThisStart ); + std::set<sal_Int32>::iterator 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; + 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 ) + { + 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() ) ) + aNewSet.ClearItem( pItem->Which() ); + } + } + 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: + SfxItemSet aItemSet( rDoc.GetAttrPool(), + svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END>{} ); + 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. + const_cast<SwPostItField&>(dynamic_cast<const SwPostItField&>(*(pNew->GetFormatField().GetField()))).SetName(OUString()); + } + } + 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; + 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 ) + { + // some things need to be done before deleting the formatting attribute + SwDoc* pDoc = GetDoc(); + switch( pAttr->Which() ) + { + case RES_TXTATR_FLYCNT: + { + SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat(); + if( pFormat ) // set to 0 by Undo? + pDoc->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( !pDoc->IsInDtor() ) + { + SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr)); + SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp(); + + //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( !pDoc->getIDocumentFieldsAccess().IsNewFieldLst() && GetNodes().IsDocNodes() ) + pDoc->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 = pDoc->GetDocShell()) + { + static const OUString metaNS("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; + + default: + break; + } + + SwTextAttr::Destroy( pAttr, pDoc->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 = nullptr; + (void)pFormat->GetItemState( RES_ANCHOR, false, + reinterpret_cast<const SfxPoolItem**>(&pAnchor) ); + + 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, ""); + // 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 *pDoc = GetDoc(); + SwNodes &rNodes = pDoc->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, ""); + // 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 + sal_uLong nSttIdx = + static_cast<SwTextFootnote*>(pAttr)->GetStartNode()->GetIndex(); + sal_uLong 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 < pDoc->GetFootnoteIdxs().size(); ++n ) + if( pAttr == pDoc->GetFootnoteIdxs()[n] ) + { + // assign new index by removing and re-inserting + pTextFootnote = pDoc->GetFootnoteIdxs()[n]; + pDoc->GetFootnoteIdxs().erase( pDoc->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 = pDoc->GetFootnoteIdxs().insert(pTextFootnote).second; + OSL_ENSURE( bSuccess, "FootnoteIdx not inserted." ); + } + SwNodeIndex aTmpIndex( *this ); + pDoc->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; + + } + // 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 * const 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; + } + 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() ); + NotifyClients( 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(); + const SfxPoolItem* pItem; + if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) ) + 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() ); + NotifyClients( 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) +{ + return (nWhich == RES_CHRATR_UNDERLINE || nWhich == RES_CHRATR_BACKGROUND + || nWhich == RES_CHRATR_ESCAPEMENT); +} + +//In MS Word, following properties of the paragraph end position won't affect the formatting of bullets, so we ignore them: +//Font underline; +//Font Italic of Western, CJK and CTL; +//Font Bold of Wertern, CJK and CTL; +static bool lcl_IsIgnoredCharFormatForBullets(const sal_uInt16 nWhich) +{ + return (nWhich == RES_CHRATR_UNDERLINE || nWhich == RES_CHRATR_POSTURE || nWhich == RES_CHRATR_WEIGHT + || nWhich == RES_CHRATR_CJK_POSTURE || nWhich == RES_CHRATR_CJK_WEIGHT + || nWhich == RES_CHRATR_CTL_POSTURE || nWhich == RES_CHRATR_CTL_WEIGHT); +} + +//Condition for expanding char set to character style of specified number rule level: +//The item inside the set should not conflict to any exist and non-default item inside paragraph properties set (SwContentNode::SwPAttrSet); +//The node should have applied a number rule; +//The node should be counted in a list, if not, make it to be; +//The item should not conflict to any exist and non-default item inside the character of specified number rule level; +//The item should not be ignored depend on the exact number rule type; +void SwTextNode::TryCharSetExpandToNum(const SfxItemSet& aCharSet) +{ + SfxItemIter aIter( aCharSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + if (!pItem) + return; + const sal_uInt16 nWhich = pItem->Which(); + + const SfxPoolItem& rInnerItem = GetAttr(nWhich,false); + + if (!IsDefaultItem(&rInnerItem) && !IsInvalidItem(&rInnerItem)) + return; + + if (!IsInList() && GetNumRule() && !GetListId().isEmpty()) + { + return; + } + + SwNumRule* pCurrNum = GetNumRule(false); + + int nLevel = GetActualListLevel(); + + if (nLevel != -1 && pCurrNum) + { + const SwNumFormat* pCurrNumFormat = pCurrNum->GetNumFormat(static_cast<sal_uInt16>(nLevel)); + if (pCurrNumFormat) + { + if (pCurrNumFormat->IsItemize() && lcl_IsIgnoredCharFormatForBullets(nWhich)) + return; + if (pCurrNumFormat->IsEnumeration() && SwTextNode::IsIgnoredCharFormatForNumbering(nWhich)) + return; + SwCharFormat* pCurrCharFormat =pCurrNumFormat->GetCharFormat(); + + if (pCurrCharFormat && pCurrCharFormat->GetItemState(nWhich,false) != SfxItemState::SET) + { + pCurrCharFormat->SetFormatAttr(*pItem); + SwNumFormat aNewNumFormat(*pCurrNumFormat); + aNewNumFormat.SetCharFormat(pCurrCharFormat); + pCurrNum->Set(nLevel,aNewNumFormat); + } + } + } +} + +// 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; + SfxItemSet aTextSet( *rSet.GetPool(), svl::Items<RES_TXTATR_BEGIN, RES_TXTATR_END-1>{} ); + + // 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: + const SfxPoolItem* pItem; + const bool bAutoStyle = SfxItemState::SET == aTextSet.GetItemState( RES_TXTATR_AUTOFMT, false, &pItem ); + if ( bAutoStyle ) + { + std::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFormatAutoFormat*>(pItem)->GetStyleHandle(); + const bool bRet = SetAttr( *pAutoStyleSet ); + 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 ) + { + if( ( nWhich < RES_CHRATR_END || + RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) && + ( SfxItemState::SET == pCFSet->GetItemState( nWhich ) ) ) + rSet.Put( pCFSet->Get( nWhich ) ); + 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 ) + { + if( ( nWhich < RES_CHRATR_END || + ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) && + ( SfxItemState::SET == pCFSet->GetItemState( nWhich ) ) ) + rSet.Put( pCFSet->Get( nWhich ) ); + 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() ) + { + const SwNumRule* pRule = rTextNode.GetNumRule(); + if ( pRule && rTextNode.GetActualListLevel() >= 0 ) + { + const SwNumFormat& rFormat = pRule->Get(static_cast<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->IsHideRedlines()) + { + 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::unique_ptr< 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::unique_ptr< SfxItemIter > pItemIter; + const SfxPoolItem* pItem = nullptr; + + if ( RES_TXTATR_AUTOFMT == pHt->Which() ) + { + const SfxItemSet* pAutoSet = CharFormat::GetItemSet( pHt->GetAttr() ); + if ( pAutoSet ) + { + pItemIter.reset( new SfxItemIter( *pAutoSet ) ); + pItem = pItemIter->GetCurItem(); + } + } + else + pItem = &pHt->GetAttr(); + + const sal_Int32 nHintEnd = *pAttrEnd; + + for (; pItem; pItem = pItemIter ? pItemIter->NextItem() : nullptr) + { + const sal_uInt16 nHintWhich = pItem->Which(); + OSL_ENSURE(!isUNKNOWNATR(nHintWhich), + "SwTextNode::GetAttr(): unknown attribute?"); + + if (!pAttrArr) + { + pAttrArr.reset( + new 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 = + static_cast<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)) + { + m_rAttrSet.ClearItem(nWhich); + } + } + } + +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->NotifyClients( &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 ); } +} + +bool SwpHints::MergePortions( SwTextNode& rNode ) +{ + if ( !Count() ) + return false; + + // sort before merging + Resort(); + + bool bRet = false; + typedef std::multimap< int, std::pair<SwTextAttr*, bool> > PortionMap; + PortionMap aPortionMap; + std::unordered_map<int, bool> RsidOnlyAutoFormatFlagMap; + sal_Int32 nLastPorStart = COMPLETE_STRING; + int 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.insert(std::make_pair(nKey, + std::make_pair(pHt, isRsidOnlyAutoFormat))); + RsidOnlyAutoFormatFlagMap[nKey] = isRsidOnlyAutoFormat; + } + + // 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::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i ); + std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j ); + PortionMap::iterator aIter1 = aRange1.first; + PortionMap::iterator aIter2 = aRange2.first; + + enum { MATCH, DIFFER_ONLY_RSID, DIFFER } eMerge(MATCH); + size_t const nAttributesInPor1 = std::distance(aRange1.first, aRange1.second); + size_t const nAttributesInPor2 = std::distance(aRange2.first, aRange2.second); + bool const isRsidOnlyAutoFormat1(RsidOnlyAutoFormatFlagMap[i]); + bool const isRsidOnlyAutoFormat2(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)) + { + // _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->second.first->End() < aIter2->second.first->GetStart()) + { + eMerge = DIFFER; + break; + } + // skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set + if (bSkipRsidOnlyAutoFormat + && aIter1 != aRange1.second && aIter1->second.second) + { + assert(DIFFER != eMerge); + eMerge = DIFFER_ONLY_RSID; + ++aIter1; + continue; + } + if (bSkipRsidOnlyAutoFormat + && aIter2 != aRange2.second && aIter2->second.second) + { + assert(DIFFER != eMerge); + eMerge = DIFFER_ONLY_RSID; + ++aIter2; + continue; + } + assert(aIter1 != aRange1.second && aIter2 != aRange2.second); + SwTextAttr const*const p1 = aIter1->second.first; + SwTextAttr const*const p2 = aIter2->second.first; + if (p1->Which() != p2->Which()) + { + eMerge = DIFFER; + break; + } + 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()) + { + 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(); + pItem1 && pItem2; + pItem1 = iter1.NextItem(), + pItem2 = iter2.NextItem()) + { + if (pItem1->Which() == RES_CHRATR_RSID) + pItem1 = iter1.NextItem(); + if (pItem2->Which() == RES_CHRATR_RSID) + pItem2 = iter2.NextItem(); + if (!pItem1 && !pItem2) + break; + if (!pItem1 || !pItem2) + { + eMerge = DIFFER; + break; + } + if (pItem1 != pItem2) // all are poolable + { + assert(IsInvalidItem(pItem1) || IsInvalidItem(pItem2) || pItem1->Which() != pItem2->Which() || *pItem1 != *pItem2); + eMerge = DIFFER; + break; + } + if (iter1.IsAtEnd() && iter2.IsAtEnd()) + break; + if (iter1.IsAtEnd() || iter2.IsAtEnd()) + { + eMerge = DIFFER; + break; + } + } + if (DIFFER == eMerge) + break; // outer loop too + else + eMerge = DIFFER_ONLY_RSID; + } + else + { + eMerge = DIFFER; + break; + } + } + ++aIter1; + ++aIter2; + } + } + else + { + eMerge = DIFFER; + } + + 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 ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 ) + { + SwTextAttr *const p2 = aIter2->second.first; + 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 = aPortionMap.equal_range( i ); + for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 ) + { + SwTextAttr *const p1 = aIter1->second.first; + 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 (aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1) + { + if (!aIter1->second.second) // already set above, don't change + { + SwTextAttr *const pCurrent(aIter1->second.first); + if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag) + { + NoteInHistory(pCurrent); + pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag); + NoteInHistory(pCurrent, true); + } + } + } + for (aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2) + { + if (!aIter2->second.second) // already set above, don't change + { + SwTextAttr *const pCurrent(aIter2->second.first); + 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; +} + +// 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 :-("); + 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(); + const SfxPoolItem* pItem; + if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN, true, &pItem ) ) + 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 sal_uInt16 *pRanges = pSet->GetRanges(); + while( (*pRanges) != 0 ) + { + const sal_uInt16 nBeg = *pRanges; + ++pRanges; + const sal_uInt16 nEnd = *pRanges; + ++pRanges; + 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* pDoc = rNode.GetDoc(); + const SwField* pField = pTextField->GetFormatField().GetField(); + + if( !pDoc->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 ) + pDoc->getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField); + if( rNode.GetNodes().IsDocNodes() ) + pDoc->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*>( + pDoc->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( pDoc->getIDocumentFieldsAccess().IsNewFieldLst() ) + static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt(); + bInsFieldType = static_cast<SwDDEFieldType*>(pField->GetTyp())->IsDeleted(); + break; + + case SwFieldIds::Postit: + if ( pDoc->GetDocShell() ) + { + pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( + &pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED)); + } + break; + default: break; + } + if( bInsFieldType ) + pDoc->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_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.ModifyNotification(nullptr,&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. + // Special case ruby portion: During import, the ruby attribute is set + // multiple times + // Special case hyperlink: During import, the ruby attribute is set + // multiple times + // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert + // character attributes directly + if ( RES_TXTATR_CHARFMT == nWhich && !bNoHintAdjustMode ) + { + 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() ) + { + SwUpdateAttr aHint(nHtStart, nHintEnd, nWhich, aWhichSublist); + + rNode.ModifyNotification( nullptr, &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() ) + { + 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: + { + cRet = CH_TXTATR_BREAKWORD; + } + 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..4609bd841 --- /dev/null +++ b/sw/source/core/txtnode/txatbase.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 <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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextAttr")); + + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("start"), BAD_CAST(OString::number(m_nStart).getStr())); + if (End()) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("end"), BAD_CAST(OString::number(*End()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + 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) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("which"), BAD_CAST(pWhich)); + if (oValue) + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(oValue->getStr())); + switch (Which()) + { + case RES_TXTATR_AUTOFMT: + GetAutoFormat().dumpAsXml(pWriter); + break; + case RES_TXTATR_FIELD: + GetFormatField().dumpAsXml(pWriter); + break; + default: + break; + } + + 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..b90f1060f --- /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 ) + { + 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..dd036d07e --- /dev/null +++ b/sw/source/core/txtnode/txtatr2.cxx @@ -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 . + */ + +#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::ModifyNotification( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + OSL_ENSURE( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTextCharFormat::Modify(): unknown Modify"); + + if ( m_pTextNode ) + { + SwUpdateAttr aUpdateAttr( + GetStart(), + *GetEnd(), + nWhich); + + m_pTextNode->ModifyNotification( &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* pDoc = GetTextNode().GetDoc(); + if( !IsVisitedValid() ) + { + SetVisited( pDoc->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 bResetMod = !pDoc->getIDocumentState().IsModified(); + Link<bool,void> aOle2Lnk; + if ( bResetMod ) + { + aOle2Lnk = pDoc->GetOle2Link(); + pDoc->SetOle2Link( Link<bool,void>() ); + } + + pRet = IsPoolUserFormat( nId ) + ? pDoc->FindCharFormatByName( rStr ) + : pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId ); + + if ( bResetMod ) + { + pDoc->getIDocumentState().ResetModified(); + pDoc->SetOle2Link( aOle2Lnk ); + } + } + + if ( pRet ) + pRet->Add( this ); + else + EndListeningAll(); + + return pRet; +} + +void SwTextINetFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + OSL_ENSURE( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTextINetFormat::Modify(): unknown Modify"); + + if ( m_pTextNode ) + { + SwUpdateAttr aUpdateAttr( + GetStart(), + *GetEnd(), + nWhich); + + m_pTextNode->ModifyNotification( &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::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew ) +{ + const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + OSL_ENSURE( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTextRuby::Modify(): unknown Modify"); + + if ( m_pTextNode ) + { + SwUpdateAttr aUpdateAttr( + GetStart(), + *GetEnd(), + nWhich); + + m_pTextNode->ModifyNotification( &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* pDoc = GetTextNode().GetDoc(); + const OUString& rStr = rFormat.GetCharFormatName(); + const sal_uInt16 nId = rStr.isEmpty() + ? static_cast<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 = !pDoc->getIDocumentState().IsModified(); + Link<bool,void> aOle2Lnk; + if( bResetMod ) + { + aOle2Lnk = pDoc->GetOle2Link(); + const_cast<SwDoc*>(pDoc)->SetOle2Link( Link<bool,void>() ); + } + + pRet = IsPoolUserFormat( nId ) + ? pDoc->FindCharFormatByName( rStr ) + : const_cast<SwDoc*>(pDoc)->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId ); + + if( bResetMod ) + { + const_cast<SwDoc*>(pDoc)->getIDocumentState().ResetModified(); + const_cast<SwDoc*>(pDoc)->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..cbc5f4f39 --- /dev/null +++ b/sw/source/core/txtnode/txtedt.cxx @@ -0,0 +1,2288 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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* pDoc, + const uno::Reference< XSpellChecker1 >& xSpell, + const LanguageType eActLang ) + { + if( !pDoc ) + return; + + if( xSpell.is() && !xSpell->hasLanguage( eActLang.get() ) ) + pDoc->SetMissingDictionaries( true ); + else + pDoc->SetMissingDictionaries( false ); + } +} + +struct SwParaIdleData_Impl +{ + SwWrongList* pWrong; // for spell checking + SwGrammarMarkUp* pGrammarCheck; // for grammar checking / proof reading + 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() : + pWrong ( nullptr ), + pGrammarCheck ( nullptr ), + pSmartTags ( nullptr ), + nNumberOfWords ( 0 ), + nNumberOfAsianWords ( 0 ), + nNumberOfChars ( 0 ), + nNumberOfCharsExcludingSpaces ( 0 ), + bWordCountDirty ( true ), + eWrongDirty ( SwTextNode::WrongState::TODO ), + bGrammarCheckDirty ( true ), + bSmartTagDirty ( true ), + bAutoComplDirty ( true ) {}; +}; + +/* + * 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( + SwTextFrame & rTextFrame, 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) + { + 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); + + NotifyClients( nullptr, &aHint ); + SwFormatChg aNew( GetFormatColl() ); + NotifyClients( 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; + + CharClass& rCC = GetAppCharClass(); + LanguageTag aOldLanguageTag = rCC.getLanguageTag(); + + 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 ) + { + rCC.setLanguageTag( LanguageTag( g_pBreakIt->GetLocale( m_aCurrentLang )) ); + if ( rCC.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 ) + + rCC.setLanguageTag( aOldLanguageTag ); + + // #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 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())) + { + 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 ) +{ + sal_uInt16 aRanges[] = { + nLangWhichId, nLangWhichId, + nFontWhichId, nFontWhichId, + 0, 0, 0 }; + if (!pFont) + aRanges[2] = aRanges[3] = 0; // clear entries with font WhichId + + SwEditShell *pEditShell = GetDoc()->GetEditShell(); + SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges ); + 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 + 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 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* pDoc = 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( pDoc, 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 >() ) ) + { + sal_Int32 nSmartTagStt = nBegin; + sal_Int32 nDummy = 1; + if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) ) + { + if( !pNode->GetWrong() ) + { + pNode->SetWrong( new 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() ) + { + rACW.InsertWord( rWord, *pDoc ); + } + } + } + } + + // 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 + SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr; + if( pViewSh ) + pViewSh->InvalidateAccessibleParaAttrs( *this ); + } + + 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->SetWrong( nullptr ); + } + 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->SetSmartTags( nullptr ); + + // 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* pDoc = 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, *pDoc ); + } + 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 ) +{ + if (nStt < 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); + } + + 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 + + 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 ) ) ); + + // extend nStt, nEnd to the current sentence boundaries + sal_Int32 nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence( + GetText(), nStt, + g_pBreakIt->GetLocale( GetLang( nStt ) ) ); + 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; + } + + 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()) + { + // 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 (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 ); + NotifyClients( nullptr, &aDelHint ); + + SwInsText aHint( nPos, nTLen ); + NotifyClients( 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 + 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); + const OUString& aExpandText = aConversionMap.getViewText(); + + if (aExpandText.isEmpty() && !bCountNumbering) + { + 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 ) + { + delete m_pParaIdleData_Impl->pWrong; + delete m_pParaIdleData_Impl->pGrammarCheck; + delete m_pParaIdleData_Impl->pSmartTags; + delete m_pParaIdleData_Impl; + m_pParaIdleData_Impl = nullptr; + } +} + +void SwTextNode::SetWrong( SwWrongList* pNew, bool bDelete ) +{ + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pWrong; + } + m_pParaIdleData_Impl->pWrong = pNew; + } +} + +SwWrongList* SwTextNode::GetWrong() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : nullptr; +} + +// #i71360# +const SwWrongList* SwTextNode::GetWrong() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : nullptr; +} + +void SwTextNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete ) +{ + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pGrammarCheck; + } + m_pParaIdleData_Impl->pGrammarCheck = pNew; + } +} + +SwGrammarMarkUp* SwTextNode::GetGrammarCheck() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : nullptr; +} + +SwWrongList const* SwTextNode::GetGrammarCheck() const +{ + return static_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetGrammarCheck()); +} + +void SwTextNode::SetSmartTags( SwWrongList* pNew, bool bDelete ) +{ + OSL_ENSURE( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(), + "Weird - we have a smart tag list without any recognizers?" ); + + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pSmartTags; + } + m_pParaIdleData_Impl->pSmartTags = pNew; + } +} + +SwWrongList* SwTextNode::GetSmartTags() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 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..05566a1bd --- /dev/null +++ b/sw/source/core/undo/SwRewriter.cxx @@ -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 . + */ + +#include <algorithm> +#include <SwRewriter.hxx> + +using namespace std; + +SwRewriter::SwRewriter() +{ +} + +void SwRewriter::AddRule(SwUndoArg eWhat, const OUString & rWith) +{ + SwRewriteRule aRule(eWhat, rWith); + + 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..1eac1fa41 --- /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()) +{ + nNodeIndex = rPos.nNode.GetIndex(); + nOffset = rPos.nContent.GetIndex(); + pDoc = rPos.GetDoc(); +} + +SwUndoField::~SwUndoField() +{ +} + +SwPosition SwUndoField::GetPosition() +{ + SwNode * pNode = pDoc->GetNodes()[nNodeIndex]; + SwNodeIndex aNodeIndex(*pNode); + SwIndex aIndex(pNode->GetContentNode(), nOffset); + SwPosition aResult(aNodeIndex, aIndex); + + return aResult; +} + +SwUndoFieldFromDoc::SwUndoFieldFromDoc(const SwPosition & rPos, + const SwField & rOldField, + const SwField & rNewField, + SwMsgPoolItem * _pHint, bool _bUpdate) + : SwUndoField(rPos) + , pOldField(rOldField.CopyField()) + , pNewField(rNewField.CopyField()) + , pHint(_pHint) + , bUpdate(_bUpdate) +{ + OSL_ENSURE(pOldField, "No old field!"); + OSL_ENSURE(pNewField, "No new field!"); + OSL_ENSURE(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) + { + pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pOldField, pHint, bUpdate); + } +} + +void SwUndoFieldFromDoc::DoImpl() +{ + SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition()); + const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr; + + if (pField) + { + pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *pNewField, pHint, bUpdate); + SwFormatField* pDstFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField()); + + if (pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false) == pDstFormatField->GetField()->GetTyp()) + pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( pDstFormatField, SwFormatFieldHintWhich::INSERTED ) ); + } +} + +void SwUndoFieldFromDoc::RedoImpl(::sw::UndoRedoContext &) +{ + DoImpl(); +} + +void SwUndoFieldFromDoc::RepeatImpl(::sw::RepeatContext &) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + DoImpl(); +} + +SwUndoFieldFromAPI::SwUndoFieldFromAPI(const SwPosition & rPos, + const Any & rOldVal, const Any & rNewVal, + sal_uInt16 _nWhich) + : SwUndoField(rPos), aOldVal(rOldVal), aNewVal(rNewVal), nWhich(_nWhich) +{ +} + +SwUndoFieldFromAPI::~SwUndoFieldFromAPI() +{ +} + +void SwUndoFieldFromAPI::UndoImpl(::sw::UndoRedoContext &) +{ + SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition()); + + if (pField) + pField->PutValue(aOldVal, nWhich); +} + +void SwUndoFieldFromAPI::DoImpl() +{ + SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition()); + + if (pField) + pField->PutValue(aNewVal, 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..d0e3798aa --- /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 * _pDoc) + : SwUndo(nUndoId, _pDoc), m_pNew(_pNew), + m_pDoc(_pDoc), m_pNewSet(nullptr), m_nId(0), m_bAuto(false) +{ + if (_pDerivedFrom) + m_sDerivedFrom = _pDerivedFrom->GetName(); +} + +SwUndoFormatCreate::~SwUndoFormatCreate() +{ +} + +void SwUndoFormatCreate::UndoImpl(::sw::UndoRedoContext &) +{ + if (m_pNew) + { + if (m_sNewName.isEmpty()) + m_sNewName = m_pNew->GetName(); + + if (!m_sNewName.isEmpty()) + m_pNew = Find(m_sNewName); + + if (m_pNew) + { + m_pNewSet = 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_pDoc->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 * _pDoc) + : SwUndo(nUndoId, _pDoc), + m_pDoc(_pDoc), 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_pDoc->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 * _pDoc) + : SwUndo(nUndoId, _pDoc), m_sOldName(_sOldName), + m_sNewName(_sNewName), m_pDoc(_pDoc) +{ +} + +SwUndoRenameFormat::~SwUndoRenameFormat() +{ +} + +void SwUndoRenameFormat::UndoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pFormat = Find(m_sNewName); + + if (pFormat) + { + m_pDoc->RenameFormat(*pFormat, m_sOldName, true); + } +} + +void SwUndoRenameFormat::RedoImpl(::sw::UndoRedoContext &) +{ + SwFormat * pFormat = Find(m_sOldName); + + if (pFormat) + { + m_pDoc->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 * _pDoc) + : SwUndoFormatCreate(SwUndoId::TXTFMTCOL_CREATE, _pNew, _pDerivedFrom, _pDoc) +{ +} + +SwFormat * SwUndoTextFormatCollCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +void SwUndoTextFormatCollCreate::Delete() +{ + m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(m_pNew), true); +} + +SwFormat * SwUndoTextFormatCollCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoTextFormatCollDelete::SwUndoTextFormatCollDelete(SwTextFormatColl const * _pOld, + SwDoc * _pDoc) + : SwUndoFormatDelete(SwUndoId::TXTFMTCOL_DELETE, _pOld, _pDoc) +{ +} + +SwFormat * SwUndoTextFormatCollDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +void SwUndoTextFormatCollDelete::Delete(SwFormat * pOld) +{ + m_pDoc->DelTextFormatColl(static_cast<SwTextFormatColl *>(pOld), true); +} + +SwFormat * SwUndoTextFormatCollDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoCondTextFormatCollCreate::SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl *_pNew, + SwTextFormatColl const *_pDerivedFrom, SwDoc *_pDoc) + : SwUndoTextFormatCollCreate(_pNew, _pDerivedFrom, _pDoc) +{ +} + +SwFormat * SwUndoCondTextFormatCollCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCondTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +SwUndoCondTextFormatCollDelete::SwUndoCondTextFormatCollDelete(SwTextFormatColl const * _pOld, + SwDoc * _pDoc) + : SwUndoTextFormatCollDelete(_pOld, _pDoc) +{ +} + +SwFormat * SwUndoCondTextFormatCollDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCondTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true); +} + +SwUndoRenameFormatColl::SwUndoRenameFormatColl(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * _pDoc) + : SwUndoRenameFormat(SwUndoId::TXTFMTCOL_RENAME, sInitOldName, sInitNewName, _pDoc) +{ +} + +SwFormat * SwUndoRenameFormatColl::Find(const OUString & rName) const +{ + return m_pDoc->FindTextFormatCollByName(rName); +} + +SwUndoCharFormatCreate::SwUndoCharFormatCreate(SwCharFormat * pNewFormat, + SwCharFormat const * pDerivedFrom, + SwDoc * pDocument) + : SwUndoFormatCreate(SwUndoId::CHARFMT_CREATE, pNewFormat, pDerivedFrom, pDocument) +{ +} + +SwFormat * SwUndoCharFormatCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCharFormat(m_sNewName, static_cast<SwCharFormat *>(pDerivedFrom), true); +} + +void SwUndoCharFormatCreate::Delete() +{ + m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(m_pNew), true); +} + +SwFormat * SwUndoCharFormatCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoCharFormatDelete::SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc * pDocument) + : SwUndoFormatDelete(SwUndoId::CHARFMT_DELETE, pOld, pDocument) +{ +} + +SwFormat * SwUndoCharFormatDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeCharFormat(m_sOldName, static_cast<SwCharFormat *>(pDerivedFrom), true); +} + +void SwUndoCharFormatDelete::Delete(SwFormat * pFormat) +{ + m_pDoc->DelCharFormat(static_cast<SwCharFormat *>(pFormat), true); +} + +SwFormat * SwUndoCharFormatDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoRenameCharFormat::SwUndoRenameCharFormat(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * pDocument) + : SwUndoRenameFormat(SwUndoId::CHARFMT_RENAME, sInitOldName, sInitNewName, pDocument) +{ +} + +SwFormat * SwUndoRenameCharFormat::Find(const OUString & rName) const +{ + return m_pDoc->FindCharFormatByName(rName); +} + +SwUndoFrameFormatCreate::SwUndoFrameFormatCreate(SwFrameFormat * pNewFormat, + SwFrameFormat const * pDerivedFrom, + SwDoc * pDocument) + : SwUndoFormatCreate(SwUndoId::FRMFMT_CREATE, pNewFormat, pDerivedFrom, pDocument) +{ +} + +SwFormat * SwUndoFrameFormatCreate::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeFrameFormat(m_sNewName, static_cast<SwFrameFormat *>(pDerivedFrom), true, m_pNew->IsAuto()); +} + +void SwUndoFrameFormatCreate::Delete() +{ + m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(m_pNew), true); +} + +SwFormat * SwUndoFrameFormatCreate::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoFrameFormatDelete::SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc * pDocument) + : SwUndoFormatDelete(SwUndoId::FRMFMT_DELETE, pOld, pDocument) +{ +} + +SwFormat * SwUndoFrameFormatDelete::Create(SwFormat * pDerivedFrom) +{ + return m_pDoc->MakeFrameFormat(m_sOldName, static_cast<SwFrameFormat *>(pDerivedFrom), true); +} + +void SwUndoFrameFormatDelete::Delete(SwFormat * pFormat) +{ + m_pDoc->DelFrameFormat(static_cast<SwFrameFormat *>(pFormat), true); +} + +SwFormat * SwUndoFrameFormatDelete::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoRenameFrameFormat::SwUndoRenameFrameFormat(const OUString & sInitOldName, + const OUString & sInitNewName, + SwDoc * pDocument) + : SwUndoRenameFormat(SwUndoId::FRMFMT_RENAME, sInitOldName, sInitNewName, pDocument) +{ +} + +SwFormat * SwUndoRenameFrameFormat::Find(const OUString & rName) const +{ + return m_pDoc->FindFrameFormatByName(rName); +} + +SwUndoNumruleCreate::SwUndoNumruleCreate(const SwNumRule * _pNew, + SwDoc * _pDoc) + : SwUndo(SwUndoId::NUMRULE_CREATE, _pDoc), m_pNew(_pNew), m_aNew(*_pNew), m_pDoc(_pDoc), + m_bInitialized(false) +{ +} + +void SwUndoNumruleCreate::UndoImpl(::sw::UndoRedoContext &) +{ + if (! m_bInitialized) + { + m_aNew = *m_pNew; + m_bInitialized = true; + } + + m_pDoc->DelNumRule(m_aNew.GetName(), true); +} + +void SwUndoNumruleCreate::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->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 * _pDoc) + : SwUndo(SwUndoId::NUMRULE_DELETE, _pDoc), m_aOld(rRule), m_pDoc(_pDoc) +{ +} + +void SwUndoNumruleDelete::UndoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->MakeNumRule(m_aOld.GetName(), &m_aOld, true); +} + +void SwUndoNumruleDelete::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->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 * _pDoc) + : SwUndo(SwUndoId::NUMRULE_RENAME, _pDoc), m_aOldName(_aOldName), m_aNewName(_aNewName), + m_pDoc(_pDoc) +{ +} + +void SwUndoNumruleRename::UndoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->RenameNumRule(m_aNewName, m_aOldName, true); +} + +void SwUndoNumruleRename::RedoImpl(::sw::UndoRedoContext &) +{ + m_pDoc->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..1ca66f9fb --- /dev/null +++ b/sw/source/core/undo/SwUndoPageDesc.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 <doc.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 ) + { + 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 SfxPoolItem* pItem; + rDest.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + SwFrameFormat* pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->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. + rSource.GetMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + + if( !rDest.IsHeaderShared() ) + { + // Same procedure for unshared header... + const SwFormatHeader& rSourceLeftHead = rSource.GetLeft().GetHeader(); + rDest.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( rSourceLeftHead.GetHeaderFormat()->GetContent() ); + rSource.GetLeft().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + } + if (!rDest.IsFirstShared()) + { + // Same procedure for unshared header... + const SwFormatHeader& rSourceFirstMasterHead = rSource.GetFirstMaster().GetHeader(); + rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->GetHeaderFormat(); + pNewFormat->SetFormatAttr( rSourceFirstMasterHead.GetHeaderFormat()->GetContent() ); + rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_HEADER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatHeader*>(pNewItem.get())->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 SfxPoolItem* pItem; + rDest.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + SwFrameFormat *pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceFoot.GetFooterFormat()->GetContent() ); + + rSource.GetMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + + if( !rDest.IsFooterShared() ) + { + const SwFormatFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter(); + rDest.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceLeftFoot.GetFooterFormat()->GetContent() ); + rSource.GetLeft().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); + } + if (rDest.IsFirstShared()) + return; + + const SwFormatFooter& rSourceFirstMasterFoot = rSource.GetFirstMaster().GetFooter(); + rDest.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( rSourceFirstMasterFoot.GetFooterFormat()->GetContent() ); + rSource.GetFirstMaster().GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ); + pNewItem.reset(pItem->Clone()); + pNewFormat = static_cast<SwFormatFooter*>(pNewItem.get())->GetFooterFormat(); + pNewFormat->SetFormatAttr( SwFormatContent() ); +} + +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); +} + +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); +} + +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..c88182b7a --- /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 +{ + sal_uLong GetSectionNodeIndex(SwTOXBaseSection const& rTOX) + { + const SwSectionNode* pSectNd = rTOX.GetFormat()->GetSectionNode(); + assert(pSectNd); + return pSectNd->GetIndex(); + } +} + +SwUndoTOXChange::SwUndoTOXChange(const SwDoc *pDoc, + SwTOXBaseSection const& rTOX, SwTOXBase const& rNew) + : SwUndo(SwUndoId::TOXCHANGE, pDoc) + , 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, sal_uLong 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..61629e087 --- /dev/null +++ b/sw/source/core/undo/docundo.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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> + +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; +} + +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(); + 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::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 < 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) +{ + 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); + + // 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() +{ + if(isTextEditActive()) + { + return SdrUndoManager::Undo(); + } + else + { + return impl_DoUndoRedo(UndoOrRedoType::Undo); + } +} + +bool UndoManager::Redo() +{ + if(isTextEditActive()) + { + return SdrUndoManager::Redo(); + } + else + { + return impl_DoUndoRedo(UndoOrRedoType::Redo); + } +} + +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..206ad9343 --- /dev/null +++ b/sw/source/core/undo/rolbck.cxx @@ -0,0 +1,1485 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <bookmrk.hxx> +#include <frameformats.hxx> +#include <memory> + +OUString SwHistoryHint::GetDescription() const +{ + return OUString(); +} + +SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong 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::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, sal_uLong 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, sal_uLong 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()->IsAlive(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 ) + { + 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, sal_uLong nNodePos ) + : SwHistoryHint( HSTRY_SETTXTFLDHNT ) + , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) ) +{ + // only copy if not Sys-FieldType + SwDoc* pDoc = 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 || + !pDoc->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, sal_uLong 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, sal_uLong 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() ) +{ + 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, sal_uLong 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, sal_uLong 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* pDoc = const_cast<SwDoc*>(pTextFootnote->GetTextNode().GetDoc()); + SwNode* pSaveNd = pDoc->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, sal_uLong 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() ) + { + 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); +} + +SwHistoryBookmark::SwHistoryBookmark( + const ::sw::mark::IMark& rBkmk, + bool bSavePos, + bool bSaveOtherPos) + : SwHistoryHint(HSTRY_BOOKMARK) + , m_aName(rBkmk.GetName()) + , m_aShortName() + , m_aKeycode() + , m_nNode(bSavePos ? + rBkmk.GetMarkPos().nNode.GetIndex() : 0) + , m_nOtherNode(bSaveOtherPos ? + rBkmk.GetOtherMarkPos().nNode.GetIndex() : 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) + { + m_aKeycode = pBookmark->GetKeyCode(); + m_aShortName = pBookmark->GetShortName(); + + ::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::unique_ptr<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.reset(new SwPaM(*pContentNd, m_nContent)); + } + else + { + pMark = *pMarkAccess->findMark(m_aName); + pPam.reset(new SwPaM(pMark->GetMarkPos())); + } + + if(m_bSaveOtherPos) + { + SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode(); + OSL_ENSURE(pContentNd, + "<SwHistoryBookmark::SetInDoc(..)>" + " - wrong node for a mark"); + + if (pPam != nullptr && 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) + { + 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 ) + { + pBookmark->SetKeyCode(m_aKeycode); + pBookmark->SetShortName(m_aShortName); + 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.GetMarkPos().nNode.GetIndex()) + , m_nContent(rFieldMark.GetMarkPos().nContent.GetIndex()) +{ +} + +void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + std::unique_ptr<SwPaM> pPam; + + const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); + if(pContentNd) + pPam.reset(new SwPaM(*pContentNd, m_nContent)); + + if (pPam) + { + IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); + pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType); + } +} + +void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc* pDoc) +{ + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + SwNodes& rNds = pDoc->GetNodes(); + std::unique_ptr<SwPaM> pPam; + + const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); + if(pContentNd) + pPam.reset(new SwPaM(*pContentNd, m_nContent-1)); + + if (pPam) + { + IDocumentMarkAccess* pMarkAccess = pDoc->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* pDoc) +{ + ::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()); + + SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent); + + IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess()); + rMarksAccess.deleteFieldmarkAt(pos); +} + +SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet, + sal_uLong nNodePos, const std::set<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 + { + 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)) + { + 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_SwpHstry() + , m_nEndDiff( 0 ) +{ +} + +SwHistory::~SwHistory() +{ +} + +void SwHistory::Add( + const SfxPoolItem* pOldValue, + const SfxPoolItem* pNewValue, + sal_uLong 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, sal_uLong 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, sal_uLong 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) ); + + const SwFormatChain* pChainItem; + if( SfxItemState::SET == rFormat.GetItemState( RES_CHAIN, false, + reinterpret_cast<const SfxPoolItem**>(&pChainItem) )) + { + 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, + sal_uLong nNodeIdx) +{ + if(rSet.Count()) + { + SfxItemIter aIter(rSet); + const SfxPoolItem* pItem = aIter.GetCurItem(); + do + { + if(!IsInvalidItem(pItem)) + { + Add(pItem, pItem, nNodeIdx); + } + + pItem = aIter.NextItem(); + + } while(pItem); + } +} + +void SwHistory::CopyAttr( + SwpHints const * pHts, + const sal_uLong 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( ULONG_MAX ) +{ + MakeSetWhichIds(); +} + +SwRegHistory::SwRegHistory( SwModify* 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::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if ( m_pHistory && pNew && pOld != pNew ) + { + if ( pNew->Which() < POOLATTR_END ) + { + if(RES_UPDATE_ATTR == pNew->Which()) + { + // const SfxItemPool& rPool = static_cast< const SwUpdateAttr* >(pNew)->GetSfxItemPool(); + + m_pHistory->Add( + // rPool, + pOld, + pNew, + m_nNodeIndex); + } + else + { + OSL_ENSURE(false, "Unexpected update attribute (!)"); + } + } + else if (pOld && RES_ATTRSET_CHG == pNew->Which()) + { + std::unique_ptr<SwHistoryHint> pNewHstr; + const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(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( SwModify* 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() ) + { + const SfxItemSet* pSet = nullptr; + if( dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) != nullptr ) + { + pSet = static_cast<SwContentNode*>( + GetRegisteredIn())->GetpSwAttrSet(); + } + else if ( dynamic_cast< const SwFormat *>( GetRegisteredIn() ) != nullptr ) + { + pSet = &static_cast<SwFormat*>( + GetRegisteredIn())->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..bccbaba80 --- /dev/null +++ b/sw/source/core/undo/unattr.cxx @@ -0,0 +1,1055 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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_bSaveDrawPt( bSvDrwPt ) +{ +} + +void SwUndoFormatAttrHelper::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) +{ + if( pOld ) { + if ( pOld->Which() == RES_OBJECTDYING ) { + CheckRegistration( pOld ); + } else if ( pNew ) { + const SwDoc& rDoc = *static_cast<SwFormat*>(GetRegisteredInNonConst())->GetDoc(); + if( POOLATTR_END >= pOld->Which() ) { + if ( GetUndo() ) { + m_pUndo->PutAttr( *pOld, rDoc ); + } else { + m_pUndo.reset( new SwUndoFormatAttr( *pOld, + *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) ); + } + } else if ( RES_ATTRSET_CHG == pOld->Which() ) { + if ( GetUndo() ) { + SfxItemIter aIter( + *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet() ); + for (auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + m_pUndo->PutAttr( *pItem, rDoc ); + } + } else { + m_pUndo.reset( new SwUndoFormatAttr( + *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet(), + *static_cast<SwFormat*>(GetRegisteredInNonConst()), m_bSaveDrawPt ) ); + } + } + } + } +} + +SwUndoFormatAttr::SwUndoFormatAttr( const SfxItemSet& rOldSet, + SwFormat& rChgFormat, + bool bSaveDrawPt ) + : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() ) + , m_sFormatName ( rChgFormat.GetName() ) + // #i56253# + , m_pOldSet( new SfxItemSet( rOldSet ) ) + , m_nNodeIndex( 0 ) + , m_nFormatWhich( rChgFormat.Which() ) + , m_bSaveDrawPt( bSaveDrawPt ) +{ + Init( rChgFormat ); +} + +SwUndoFormatAttr::SwUndoFormatAttr( const SfxPoolItem& rItem, SwFormat& rChgFormat, + bool bSaveDrawPt ) + : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() ) + , m_sFormatName(rChgFormat.GetName()) + , m_pOldSet( rChgFormat.GetAttrSet().Clone( false ) ) + , m_nNodeIndex( 0 ) + , m_nFormatWhich( rChgFormat.Which() ) + , m_bSaveDrawPt( bSaveDrawPt ) +{ + m_pOldSet->Put( rItem ); + Init( rChgFormat ); +} + +void SwUndoFormatAttr::Init( const SwFormat & rFormat ) +{ + // tdf#126017 never save SwNodeIndex, it will go stale + m_pOldSet->ClearItem(RES_CNTNT); + // treat change of anchor specially + if ( SfxItemState::SET == m_pOldSet->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 (pDoc->GetSections().ContainsFormat(&rFormat)) { + m_nNodeIndex = rFormat.GetContent().GetContentIdx()->GetIndex(); + } else if ( dynamic_cast< const SwTableBoxFormat* >( &rFormat ) != nullptr ) { + SwTableBox * pTableBox = SwIterator<SwTableBox,SwFormat>( rFormat ).First(); + 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_pOldSet) + 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_pOldSet->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_pOldSet->ClearItem( RES_ANCHOR ); + } + } + + if ( !bAnchorAttrRestored ) { + SwUndoFormatAttrHelper aTmp( *pFormat, m_bSaveDrawPt ); + pFormat->SetFormatAttr( *m_pOldSet ); + if ( aTmp.GetUndo() ) { + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } else { + m_pOldSet->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 SwDoc::FindFormatByName(*rDoc.GetGrfFormatColls(), 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 = SwDoc::FindFormatByName(*rDoc.GetSpzFrameFormats(), m_sFormatName); + if (pFormat) + return pFormat; + pFormat = SwDoc::FindFormatByName(*rDoc.GetFrameFormats(), 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_pOldSet) + 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_pOldSet->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_pOldSet->Put( SwFormatFrameSize( SwFrameSize::Variable, aPt.X(), aPt.Y() ) ); + } + } + + const SwFormatAnchor& rAnchor = + m_pOldSet->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_pOldSet->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_pOldSet->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_pOldSet->Get( RES_FRM_SIZE ); + aDrawSavePt.setX( rOldSize.GetWidth() ); + aDrawSavePt.setY( rOldSize.GetHeight() ); + m_pOldSet->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_pOldSet->Put( aNewAnchor ); + SwUndoFormatAttrHelper aTmp( *pFrameFormat, m_bSaveDrawPt ); + pFrameFormat->SetFormatAttr( *m_pOldSet ); + if ( aTmp.GetUndo() ) { + m_nNodeIndex = aTmp.GetUndo()->m_nNodeIndex; + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } else { + m_pOldSet->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_pOldSet->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( const std::set<sal_uInt16> &rAttrs ) +{ + m_Ids = 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( ULONG_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( ULONG_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* pDoc = rPam.GetDoc(); + if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) { + m_pRedlineData.reset( new SwRedlineData( bIsContent + ? RedlineType::Insert + : RedlineType::Format, + pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + } + + m_pRedlineSaveData.reset( new SwRedlineSaveDatas ); + if ( !FillSaveDataForFormat( rPam, *m_pRedlineSaveData )) + m_pRedlineSaveData.reset(); + + SetRedlineFlags( pDoc->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 ( ULONG_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()) + && (*m_AttrSet.GetRanges() <= 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 ( ULONG_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; + sal_uLong 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* pDoc ) + : SwUndo( SwUndoId::SETDEFTATTR, pDoc ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( RES_PARATR_TABSTOP, false, &pItem ) ) { + // store separately, because it may change! + m_pTabStop.reset( static_cast<SvxTabStopItem*>(pItem->Clone()) ); + if ( 1 != rSet.Count() ) { // are there more attributes? + m_pOldSet.reset( new SfxItemSet( rSet ) ); + } + } else { + m_pOldSet.reset( new SfxItemSet( rSet ) ); + } +} + +SwUndoDefaultAttr::~SwUndoDefaultAttr() +{ +} + +void SwUndoDefaultAttr::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if (m_pOldSet) + { + SwUndoFormatAttrHelper aTmp( + *rDoc.GetDfltTextFormatColl() ); + rDoc.SetDefault( *m_pOldSet ); + m_pOldSet.reset(); + if ( aTmp.GetUndo() ) { + // transfer ownership of helper object's old set + m_pOldSet = std::move(aTmp.GetUndo()->m_pOldSet); + } + } + 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* pDoc ) + : SwUndo( SwUndoId::FTNINFO, pDoc ) + , 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* pDoc ) + : SwUndo( SwUndoId::FTNINFO, pDoc ) + , 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) +{ + 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, 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..16d45a7b7 --- /dev/null +++ b/sw/source/core/undo/unbkmk.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 <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* pDoc ) +{ + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + for ( IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin(); + ppBkmk != pMarkAccess->getAllMarksEnd(); + ++ppBkmk ) + { + if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) ) + { + pMarkAccess->deleteMark( ppBkmk ); + 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* pDoc ) + : SwUndo( SwUndoId::BOOKMARK_RENAME, pDoc ) + , m_sOldName( rOldName ) + , m_sNewName( rNewName ) +{ +} + +SwUndoRenameBookmark::~SwUndoRenameBookmark() +{ +} + +static OUString lcl_QuoteName(const OUString& 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..e1e519fa2 --- /dev/null +++ b/sw/source/core/undo/undel.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 <UndoDelete.hxx> +#include <hintids.hxx> +#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 <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, sal_uLong 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); + sal_uLong nEndOfRedline = 0; + for (size_t i = 0; i < rRedlineSaveData.size(); ++i) + { + auto const& rRedline(rRedlineSaveData[i]); + if (rRedline.m_nSttNode <= rRange.m_nSttNode + && 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 (sal_uLong 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) + { + std::vector<SwTextFrame*> frames; + SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode); + for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) + { + if (pFrame->getRootFrame()->IsHideRedlines()) + { + 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, + 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_bCacheComment = false; + + SwDoc * pDoc = rPam.GetDoc(); + + if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->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.GetPoint() == pStt + ? rPam.GetMark() + : rPam.GetPoint(); + + // 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(pDoc->GetIDocumentUndoRedo()); + DelBookmarks(pStt->nNode, pEnd->nNode); + } + else + { + DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + if (m_nEndNode - m_nSttNode > 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(*pDoc, *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 = pDoc->GetUndoManager().GetUndoNodes(); + SwNodes& rDocNds = pDoc->GetNodes(); + SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff, + rDocNds, m_nEndNode - m_nNdDiff ); + if( !bFullPara && !pEndTextNd && + &aRg.aEnd.GetNode() != &pDoc->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, 0, *pEndTextNd, 1 ); + SwPosition aSplitPos( *pEndTextNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->getIDocumentContentOperations().SplitNode( aSplitPos, false ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd ); + --aRg.aEnd; + } + else + m_nReplaceDummy = 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( 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, 0, *pSttTextNd, 1 ); + SwPosition aSplitPos( *pSttTextNd ); + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + pDoc->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, 0, *pEndTextNd, 1 ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart ); + } + else + { + SwNodeRange aMvRg( *pSttTextNd, 0, *pSttTextNd, 1 ); + rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd ); + } + } + } + if( m_nSectDiff || m_nReplaceDummy ) + lcl_MakeAutoFrames( *pDoc->GetSpzFrameFormats(), + m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() ); + } + else + m_nNode = 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 ) +{ + sal_uLong 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* pDoc, 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.GetPoint() == pStt + ? rDelPam.GetMark() + : rDelPam.GetPoint(); + + 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(*pDoc, *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; + + pDoc->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, OUString(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(const OUString& rStr, sal_Int32 nStart, sal_Int32 nEnd) +{ + 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 + { + aResult = SwResId(STR_START_QUOTE) + + rStr.copy(nStart, nCount) + + SwResId(STR_END_QUOTE); + } + } + + return aResult; +} + +OUString DenoteSpecialCharacters(const OUString & rStr) +{ + 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)); + + nStart = i; + bStart = false; + } + + cLast = rStr[i]; + } + + aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength())); + } + else + aResult = SwRewriter::GetPlaceHolder(UndoArg2); + + return aResult.makeStringAndClear(); +} + +SwRewriter SwUndoDelete::GetRewriter() const +{ + SwRewriter aResult; + + if (m_nNode != 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, SwPosition &rPos, sal_uLong nOldIdx ) +{ + if( !rSpzArr.empty() ) + { + SwFlyFrameFormat* pFormat; + const SwFormatAnchor* pAnchor; + const SwPosition* pAPos; + for( size_t n = 0; n < rSpzArr.size(); ++n ) + { + pFormat = static_cast<SwFlyFrameFormat*>(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(); + + sal_uLong 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 = 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 ) + { + sal_uLong 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()) + { + sal_uLong nOldIdx = aPos.nNode.GetIndex(); + rDoc.getIDocumentContentOperations().SplitNode( aPos, false ); + if( m_bBackSp ) + lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx); + } + else + ++aPos.nNode; + } + } + if( m_nSectDiff ) + { + sal_uLong nMoveIndex = aPos.nNode.GetIndex(); + int 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, 0 - nDiff, aPos.nNode, 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, 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 ) + { + sal_uLong 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, 0, aPos.nNode, 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 ? 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 ); + OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent, + SwInsertFlags::NOHINTEXPAND) ); + 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); + + sal_uLong 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()->IsHideRedlines()) + { + 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 || 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 (0 != m_nNode) + { + // tdf#136453 only if section nodes at the start + if (m_bBackSp && m_nReplaceDummy != 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 == 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 : m_nEndNode); + ::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() ); + 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() ); + 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(); + + 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(); + } + 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 ); +} + +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; +} + +/* 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..db2fee97b --- /dev/null +++ b/sw/source/core/undo/undobj.cxx @@ -0,0 +1,1682 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#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 <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 <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.GetPoint() == pStt + ? rPam.GetMark() + : rPam.GetPoint(); + m_nEndNode = pEnd->nNode.GetIndex(); + m_nEndContent = pEnd->nContent.GetIndex(); + } + else + { + // no selection !! + m_nEndNode = 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 +{ + SwPaM & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() ); + SetPaM( rPaM, bCorrToContent ); + return rPaM; +} + +void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, sal_uLong nSttIdx, + const sal_uLong* 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( sal_uLong 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) +{ + const char *pId = nullptr; + 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::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 +{ +} + +// 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, + sal_uLong* 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(); + + sal_uLong nTmpMvNode = aPos.nNode.GetIndex(); + + if( pCpyNd || pEndNdIdx ) + { + SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 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, sal_uLong nNodeIdx, + SwPosition& rInsPos, + const sal_uLong* 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* pDoc = rMark.nNode.GetNode().GetDoc(); + + // if it's not in the doc array, probably missing some invalidation somewhere + assert(&rPoint.nNode.GetNodes() == &pDoc->GetNodes()); + assert(&rMark.nNode.GetNodes() == &pDoc->GetNodes()); + + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + // 1. Footnotes + if( DelContentType::Ftn & nDelContentType ) + { + SwFootnoteIdxs& rFootnoteArr = pDoc->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 = *pDoc->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, 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) && + // at least for calls from SwUndoDelete, + // this should work - other Undos don't + // remember the order of the cursor + (rPoint.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 ) + { + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + if( pMarkAccess->getAllMarksCount() ) + { + 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); + n--; + } + } + } + } + } +} + +// save a complete section into UndoNodes array +SwUndoSaveSection::SwUndoSaveSection() + : m_nMoveLen( 0 ), m_nStartPos( ULONG_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(); + 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 ); + if( nullptr != ( pCNd = aPam.GetContentNode()) ) + aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + // Keep positions as SwIndex so that this section can be deleted in DTOR + sal_uLong 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( ULONG_MAX != m_nStartPos ) // was there any content? + { + // 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( ULONG_MAX != m_nStartPos ) // was there any content? + { + SwPosition aInsPos( rInsPos ); + sal_uLong 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 = 0; + + if( m_pRedlineSaveData ) + { + SwUndo::SetSaveData( *pDoc, *m_pRedlineSaveData ); + m_pRedlineSaveData.reset(); + } + } +} + +// 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(); +#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, o3tl::default_delete<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, o3tl::default_delete<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 + assert(rSData.empty() || + (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.copy(0, nFrontLen) + + rFillStr + + rStr.copy(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 + return rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex() + || rEnd.nContent != 0 + || rStart.nContent != rStart.nNode.GetNode().GetTextNode()->Len(); +} + +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..2398ea8da --- /dev/null +++ b/sw/source/core/undo/undobj1.cxx @@ -0,0 +1,714 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/itemiter.hxx> +#include <svx/svdundo.hxx> +#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 <docary.hxx> +#include <rootfrm.hxx> +#include <swundo.hxx> +#include <pam.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 (m_pFrameFormat->GetOtherTextBoxFormat()) + { // clear that before delete + m_pFrameFormat->SetOtherTextBoxFormat(nullptr); + } + delete m_pFrameFormat; + } +} + +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( static_cast<sal_uInt16>(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->GetOtherTextBoxFormat()) + { + // 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->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(m_pFrameFormat); + + if (m_pFrameFormat->GetOtherTextBoxFormat()->Which() == RES_DRAWFRMFMT) + { + SdrObject* pSdrObject = m_pFrameFormat->GetOtherTextBoxFormat()->FindSdrObject(); + 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_DRAWFRMFMT) + { + // This is a draw format and we just set the fly format's textbox pointer to this draw + // format. Sync the draw format's content with the fly format's content. + SwFrameFormat* pFlyFormat = m_pFrameFormat->GetOtherTextBoxFormat(); + m_pFrameFormat->SetFormatAttr(pFlyFormat->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->GetOtherTextBoxFormat()) + { // tdf#108867 clear that pointer + m_pFrameFormat->GetOtherTextBoxFormat()->SetOtherTextBoxFormat(nullptr); + } + + // all Uno objects should now log themselves off + { + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, m_pFrameFormat ); + m_pFrameFormat->ModifyNotification( &aMsgHint, &aMsgHint ); + } + + 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 = rAnchor.GetPageNum(); + } + + m_pFrameFormat->ResetFormatAttr( RES_ANCHOR ); // delete anchor + + // delete from array + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( m_pFrameFormat ); +} + +SwUndoInsLayFormat::SwUndoInsLayFormat( SwFrameFormat* pFormat, sal_uLong 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; + switch( m_nRndId ) + { + case RndStdIds::FLY_AT_PAGE: + m_nNodePagePos = rAnchor.GetPageNum(); + break; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_FLY: + m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex(); + break; + case RndStdIds::FLY_AS_CHAR: + case RndStdIds::FLY_AT_CHAR: + { + const SwPosition* pPos = rAnchor.GetContentAnchor(); + m_nContentPos = pPos->nContent.GetIndex(); + m_nNodePagePos = pPos->nNode.GetIndex(); + } + break; + default: + OSL_FAIL( "Which FlyFrame?" ); + } +} + +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()); + bool bRemoveIdx = true; + if( mnCursorSaveIndexPara > 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 ); + SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true ); + bRemoveIdx = false; + } + } + if( bRemoveIdx ) + { + RemoveIdxFromSection( rDoc, rContent.GetContentIdx()->GetIndex() ); + } + } + 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( 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_pItemSet( new SfxItemSet( *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, + sal_uLong 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) + { + if( m_bAnchorChanged ) + m_pFrameFormat->DelFrames(); + + if( m_pFrameFormat->DerivedFrom() != pDerivedFromFrameFormat) + m_pFrameFormat->SetDerivedFrom(pDerivedFromFrameFormat); + + SfxItemIter aIter( *m_pItemSet ); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if( IsInvalidItem( pItem )) + m_pFrameFormat->ResetFormatAttr( m_pItemSet->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) + { + 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_pItemSet->Put( *pItem ); + } + else + m_pItemSet->InvalidateItem( nWhich ); +} + +void SwUndoSetFlyFormat::Modify( const SfxPoolItem* pOld, const SfxPoolItem* ) +{ + if( pOld ) + { + sal_uInt16 nWhich = pOld->Which(); + + if( nWhich < POOLATTR_END ) + PutAttr( nWhich, pOld ); + else if( RES_ATTRSET_CHG == nWhich ) + { + SfxItemIter aIter( *static_cast<const SwAttrSetChg*>(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..b6c8bd521 --- /dev/null +++ b/sw/source/core/undo/undraw.cxx @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <hints.hxx> +#include <fmtanchr.hxx> +#include <fmtflcnt.hxx> +#include <txtflcnt.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <docary.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <UndoCore.hxx> +#include <dcontact.hxx> +#include <viewsh.hxx> +#include <frameformats.hxx> + +struct SwUndoGroupObjImpl +{ + SwDrawFrameFormat* pFormat; + SdrObject* pObj; + sal_uLong 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* pDoc ) + : SwUndo( SwUndoId::DRAWUNDO, pDoc ), 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_SendRemoveToUno( SwFormat& rFormat ) +{ + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, &rFormat ); + rFormat.ModifyNotification( &aMsgHint, &aMsgHint ); +} + +static void lcl_SaveAnchor( SwFrameFormat* pFormat, sal_uLong& 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())) + { + 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, sal_uLong 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())) + { + 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* pDoc ) + : SwUndo( SwUndoId::DRAWGROUP, pDoc ), 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); + + ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // 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 ); + + 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(); + + 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)); + + // object will destroy itself + pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() ); + pObj->SetUserCall( nullptr ); + + ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *rSave.pFormat ); + + 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; + + // #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 ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // 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* pDoc ) + : SwUndo( SwUndoId::DRAWUNGROUP, pDoc ), m_bDeleteFormat( false ) +{ + m_nSize = static_cast<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 ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // 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(); + + // remove from array + for( sal_uInt16 n = 1; n < m_nSize; ++n ) + { + SwUndoGroupObjImpl& rSave = m_pObjArray[n]; + + ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *rSave.pFormat ); + + 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; + + // #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 ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // 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; + + // #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* pDoc) + : SwUndo( SwUndoId::DRAWUNGROUP, pDoc ) +{ +} + +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* pDoc ) + : SwUndo( SwUndoId::DRAWDELETE, pDoc ), 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; + + // #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 ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + 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 ); + + // notify UNO objects to decouple + ::lcl_SendRemoveToUno( *pFormat ); + + // remove from array + SwDoc* pDoc = pFormat->GetDoc(); + SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats(); + rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat )); + + m_pMarkList->InsertEntry( rMark ); +} + +/* 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..118cf98f3 --- /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..6e08fb502 --- /dev/null +++ b/sw/source/core/undo/unins.cxx @@ -0,0 +1,1045 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sot/storage.hxx> +#include <editeng/keepitem.hxx> +#include <svx/svdobj.hxx> + +#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 <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(); + SwPaM *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 + { + sal_uLong 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(); + SwPaM *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!) + sal_uLong 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; +} + +class SwUndoReplace::Impl + : private SwUndoSaveContent +{ + OUString m_sOld; + OUString m_sIns; + sal_uLong 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); + virtual ~Impl() + { + } + + 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(); + + sal_uLong 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(); + sal_uLong 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(); + SwPaM & 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 ) + { + SwPosition aPos(*pNd, pNd->Len()); + 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 = m_nSttCnt; +} + +void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPaM & 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, 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(); + + sal_uLong 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,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..6888d477f --- /dev/null +++ b/sw/source/core/undo/unmove.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 <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* pDoc = rRange.GetDoc(); + SwTextNode* pTextNd = pDoc->GetNodes()[ m_nSttNode ]->GetTextNode(); + SwTextNode* pEndTextNd = pDoc->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* pDoc, const SwNodeRange& rRg, + const SwNodeIndex& rMvPos ) + : SwUndo(SwUndoId::MOVE, pDoc) + , 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? + sal_uLong nContentStt = pDoc->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 ); + if( nullptr != ( pCNd = aMkPos.nNode.GetNode().GetContentNode() )) + 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* pDoc = rRange.GetDoc(); + sal_uLong nContentStt = pDoc->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..a4e4b39c0 --- /dev/null +++ b/sw/source/core/undo/unnum.cxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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* pDoc, + SwUndoId nUndoId) + : SwUndo( nUndoId, pDoc ), + 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 = 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 ) +{ + m_aNodes.reserve( std::min<sal_uLong>(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, long nOff, bool bIsOutlMv ) + : SwUndo( bIsOutlMv ? SwUndoId::OUTLINE_UD : SwUndoId::MOVENUM, rPam.GetDoc() ), + SwUndRng( rPam ), + nNewStt( 0 ), nOffset( nOff ) +{ + // nOffset: Down => 1 + // Up => -1 +} + +void SwUndoMoveNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + sal_uLong nTmpStt = m_nSttNode, nTmpEnd = m_nEndNode; + + if (m_nEndNode || m_nEndContent != COMPLETE_STRING) // section? + { + if( nNewStt < m_nSttNode ) // moved forwards + m_nEndNode = m_nEndNode - ( m_nSttNode - nNewStt ); + else + m_nEndNode = m_nEndNode + ( nNewStt - m_nSttNode ); + } + m_nSttNode = nNewStt; + + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().MoveParagraph( rPam, -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, nOffset, SwUndoId::OUTLINE_UD == GetId()); +} + +void SwUndoMoveNum::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if( SwUndoId::OUTLINE_UD == GetId() ) + { + rDoc.MoveOutlinePara(rContext.GetRepeatPaM(), + 0 < nOffset ? 1 : -1 ); + } + else + { + rDoc.MoveParagraph(rContext.GetRepeatPaM(), nOffset); + } +} + +SwUndoNumUpDown::SwUndoNumUpDown( const SwPaM& rPam, short nOff ) + : SwUndo( nOff > 0 ? SwUndoId::NUMUP : SwUndoId::NUMDOWN, rPam.GetDoc() ), + SwUndRng( rPam ), + nOffset( nOff ) +{ + // nOffset: Down => 1 + // Up => -1 +} + +void SwUndoNumUpDown::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().NumUpDown(rPam, 1 != nOffset ); +} + +void SwUndoNumUpDown::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( AddUndoRedoPaM(rContext) ); + rContext.GetDoc().NumUpDown(rPam, 1 == nOffset); +} + +void SwUndoNumUpDown::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().NumUpDown(rContext.GetRepeatPaM(), 1 == nOffset); +} + +SwUndoNumOrNoNum::SwUndoNumOrNoNum( const SwNodeIndex& rIdx, bool bOldNum, + bool bNewNum) + : SwUndo( SwUndoId::NUMORNONUM, rIdx.GetNode().GetDoc() ), + nIdx( rIdx.GetIndex() ), mbNewNum(bNewNum), + mbOldNum(bOldNum) +{ +} + +// #115901#, #i40034# +void SwUndoNumOrNoNum::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx ); + SwTextNode * pTextNd = aIdx.GetNode().GetTextNode(); + + if (nullptr != pTextNd) + { + pTextNd->SetCountedInList(mbOldNum); + } +} + +// #115901#, #i40034# +void SwUndoNumOrNoNum::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), nIdx ); + 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() ), + nIdx( rPos.nNode.GetIndex() ), nOldStt( USHRT_MAX ), + nNewStt( USHRT_MAX ), bSetSttValue( false ), bFlag( bFlg ) +{ +} + +SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt ) + : SwUndo(SwUndoId::SETNUMRULESTART, rPos.GetDoc()) + , nIdx(rPos.nNode.GetIndex()) + , nOldStt(USHRT_MAX) + , nNewStt(nStt) + , bSetSttValue(true) + , bFlag(false) +{ + SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode(); + if ( pTextNd ) + { + if ( pTextNd->HasAttrListRestartValue() ) + { + nOldStt = static_cast<sal_uInt16>(pTextNd->GetAttrListRestartValue()); + } + else + { + nOldStt = 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()[ nIdx ] ); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart( aPos, nOldStt ); + } + else + { + rDoc.SetNumRuleStart( aPos, !bFlag ); + } +} + +void SwUndoNumRuleStart::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + SwPosition const aPos( *rDoc.GetNodes()[ nIdx ] ); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart( aPos, nNewStt ); + } + else + { + rDoc.SetNumRuleStart( aPos, bFlag ); + } +} + +void SwUndoNumRuleStart::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if( bSetSttValue ) + { + rDoc.SetNodeNumStart(*rContext.GetRepeatPaM().GetPoint(), nNewStt); + } + else + { + rDoc.SetNumRuleStart(*rContext.GetRepeatPaM().GetPoint(), 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..04341d61a --- /dev/null +++ b/sw/source/core/undo/unoutl.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 <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); +} + +/* 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..7361da5a4 --- /dev/null +++ b/sw/source/core/undo/unovwr.cxx @@ -0,0 +1,474 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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* pDoc, SwPosition& rPos, + sal_Unicode cIns ) + : SwUndo(SwUndoId::OVERWRITE, pDoc), + bGroup( false ) +{ + SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode(); + assert(pTextNd); + sal_Int32 const nTextNdLen = pTextNd->GetText().getLength(); + + nSttNode = rPos.nNode.GetIndex(); + nSttContent = rPos.nContent.GetIndex(); + + if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), + rPos.nNode, rPos.nContent.GetIndex()+1 ); + pRedlSaveData.reset( new SwRedlineSaveDatas ); + if( !FillSaveData( aPam, *pRedlSaveData, false )) + { + pRedlSaveData.reset(); + } + if (nSttContent < nTextNdLen) + { + pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any); + } + } + + bInsChar = true; + if( nSttContent < nTextNdLen ) // no pure insert? + { + aDelStr += OUStringChar( pTextNd->GetText()[nSttContent] ); + if( !m_pHistory ) + m_pHistory.reset( new SwHistory ); + SwRegHistory aRHst( *pTextNd, m_pHistory.get() ); + m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), nSttNode, 0, + nTextNdLen, false ); + ++rPos.nContent; + bInsChar = false; + } + + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + pTextNd->InsertText( OUString(cIns), rPos.nContent, + SwInsertFlags::EMPTYEXPAND ); + aInsStr += OUStringChar( cIns ); + + if( !bInsChar ) + { + const SwIndex aTmpIndex( rPos.nContent, -2 ); + pTextNd->EraseText( aTmpIndex, 1 ); + } + pTextNd->SetIgnoreDontExpand( bOldExpFlg ); + + m_bCacheComment = false; +} + +SwUndoOverwrite::~SwUndoOverwrite() +{ +} + +bool SwUndoOverwrite::CanGrouping( SwDoc* pDoc, SwPosition& rPos, + sal_Unicode cIns ) +{ +// What is with only inserted characters? + + // Only deletion of single chars can be combined. + if( rPos.nNode != nSttNode || aInsStr.isEmpty() || + ( !bGroup && 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() != ( nSttContent + 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( aInsStr, aInsStr.getLength()-1 ) ) + return false; + + if (!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 = ( !pRedlSaveData && !bSaved ) || + ( pRedlSaveData && bSaved && + SwUndo::CanRedlineGroup( *pRedlSaveData, aTmpSav, + nSttContent > rPos.nContent.GetIndex() )); + // aTmpSav.DeleteAndDestroyAll(); + if( !bOk ) + return false; + + pDoc->getIDocumentRedlineAccess().DeleteRedline( aPam, false, RedlineType::Any ); + } + + // both 'overwrites' can be combined so 'move' the corresponding character + if( !bInsChar ) + { + if (rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength()) + { + aDelStr += OUStringChar( pDelTextNd->GetText()[rPos.nContent.GetIndex()] ); + ++rPos.nContent; + } + else + 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; + aInsStr += OUStringChar( cIns ); + + if( !bInsChar ) + { + const SwIndex aTmpIndex( rPos.nContent, -2 ); + pDelTextNd->EraseText( aTmpIndex, 1 ); + } + pDelTextNd->SetIgnoreDontExpand( bOldExpFlg ); + + bGroup = true; + return true; +} + +void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pCurrentPam->DeleteMark(); + pCurrentPam->GetPoint()->nNode = nSttNode; + SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode(); + assert(pTextNd); + SwIndex& rIdx = pCurrentPam->GetPoint()->nContent; + rIdx.Assign( pTextNd, nSttContent ); + + SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord(); + if( pACEWord ) + { + if( 1 == aInsStr.getLength() && 1 == aDelStr.getLength() ) + pACEWord->CheckChar( *pCurrentPam->GetPoint(), aDelStr[0] ); + pDoc->SetAutoCorrExceptWord( nullptr ); + } + + // If there was not only an overwrite but also an insert, delete the surplus + if( aInsStr.getLength() > aDelStr.getLength() ) + { + rIdx += aDelStr.getLength(); + pTextNd->EraseText( rIdx, aInsStr.getLength() - aDelStr.getLength() ); + rIdx = nSttContent; + } + + if( !aDelStr.isEmpty() ) + { + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + ++rIdx; + for( sal_Int32 n = 0; n < aDelStr.getLength(); n++ ) + { + // do it individually, to keep the attributes! + OUString aTmpStr(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( pDoc, 0, false ); + } + + if( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent ) + { + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent = nSttContent; + } + + if( pRedlSaveData ) + SetSaveData( *pDoc, *pRedlSaveData ); +} + +void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwPaM *const pCurrentPam = & rContext.GetRepeatPaM(); + if( aInsStr.isEmpty() || pCurrentPam->HasMark() ) + return; + + SwDoc & rDoc = rContext.GetDoc(); + + { + ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); + rDoc.getIDocumentContentOperations().Overwrite(*pCurrentPam, OUString(aInsStr[0])); + } + for( sal_Int32 n = 1; n < aInsStr.getLength(); ++n ) + rDoc.getIDocumentContentOperations().Overwrite( *pCurrentPam, OUString(aInsStr[n]) ); +} + +void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM *const pCurrentPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); + + pCurrentPam->DeleteMark(); + pCurrentPam->GetPoint()->nNode = nSttNode; + SwTextNode* pTextNd = pCurrentPam->GetNode().GetTextNode(); + assert(pTextNd); + SwIndex& rIdx = pCurrentPam->GetPoint()->nContent; + + if( pRedlSaveData ) + { + rIdx.Assign( pTextNd, nSttContent ); + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent += aDelStr.getLength(); + pDoc->getIDocumentRedlineAccess().DeleteRedline( *pCurrentPam, false, RedlineType::Any ); + pCurrentPam->DeleteMark(); + } + rIdx.Assign( pTextNd, !aDelStr.isEmpty() ? nSttContent+1 : nSttContent ); + + bool bOldExpFlg = pTextNd->IsIgnoreDontExpand(); + pTextNd->SetIgnoreDontExpand( true ); + + for( sal_Int32 n = 0; n < aInsStr.getLength(); n++ ) + { + // do it individually, to keep the attributes! + OUString const ins( + pTextNd->InsertText( OUString(aInsStr[n]), rIdx, + SwInsertFlags::EMPTYEXPAND) ); + assert(ins.getLength() == 1); // cannot fail + (void) ins; + if( n < aDelStr.getLength() ) + { + rIdx -= 2; + pTextNd->EraseText( rIdx, 1 ); + rIdx += n+1 < 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( pCurrentPam->GetMark()->nContent.GetIndex() != nSttContent ) + { + pCurrentPam->SetMark(); + pCurrentPam->GetMark()->nContent = nSttContent; + } +} + +SwRewriter SwUndoOverwrite::GetRewriter() const +{ + SwRewriter aResult; + + OUString aString = SwResId(STR_START_QUOTE) + + ShortenString(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; + sal_uLong nNdIdx; + sal_Int32 nStart, nLen; + + UndoTransliterate_Data( sal_uLong 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 ), 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 = aChanges.size() - 1; i >= 0; --i) + 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(), nType ); + rDoc.getIDocumentContentOperations().TransliterateText( rPam, aTrans ); +} + +void SwUndoTransliterate::AddChanges( SwTextNode& rTNd, + sal_Int32 nStart, sal_Int32 nLen, + uno::Sequence <sal_Int32> const & rOffsets ) +{ + long nOffsLen = rOffsets.getLength(); + UndoTransliterate_Data* pNew = new UndoTransliterate_Data( + rTNd.GetIndex(), nStart, static_cast<sal_Int32>(nOffsLen), + rTNd.GetText().copy(nStart, nLen)); + + 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( 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; + 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 < aChanges.size(); ++i) // check all changes but not the current one + { + UndoTransliterate_Data* pD = 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 ) + { + 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..4b98043a6 --- /dev/null +++ b/sw/source/core/undo/unredln.cxx @@ -0,0 +1,527 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <unotools/charclass.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <txtfrm.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() ); + } + + sal_uLong 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)); + + UndoRedlineImpl(rDoc, rPam); + + if( mpRedlSaveData ) + { + sal_uLong 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 (dynamic_cast<SwUndoRedlineDelete*>(this)) + { + 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 ) + { + sal_uLong 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 nUsrId ) + : 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; +} + +// 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); +} + +void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) +{ + if (rPam.GetPoint() != rPam.GetMark()) + { + 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 ); + sal_uLong 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 ); + sal_uLong 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* pDoc = rRg.GetDoc(); + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + RedlineType eTyp = m_bInsert ? RedlineType::Insert : RedlineType::Delete; + m_pRedlineData.reset( new SwRedlineData( eTyp, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->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* pDoc = rRedl.GetDoc(); + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlineData.reset( new SwRedlineData( rRedl.GetRedlineData() ) ); + SetRedlineFlags( pDoc->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, 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, 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..ae2ba60a3 --- /dev/null +++ b/sw/source/core/undo/unsect.cxx @@ -0,0 +1,601 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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::unique_ptr<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::unique_ptr<SfxItemSet> pAttr; + if( rSect.GetFormat() ) + { + sal_uInt16 nCnt = 1; + if( rSect.IsProtect() ) + ++nCnt; + + if( nCnt < rSect.GetFormat()->GetAttrSet().Count() ) + { + pAttr.reset(new SfxItemSet( rSect.GetFormat()->GetAttrSet() )); + pAttr->ClearItem( RES_PROTECT ); + pAttr->ClearItem( RES_CNTNT ); + if( !pAttr->Count() ) + { + pAttr.reset(); + } + } + } + return pAttr; +} + +SwUndoInsSection::SwUndoInsSection( + SwPaM const& rPam, SwSectionData const& rNewData, + SfxItemSet const*const pSet, + std::pair<SwTOXBase const*, sw::RedlineMode> const*const pTOXBase) + : SwUndo( SwUndoId::INSSECTION, rPam.GetDoc() ), SwUndRng( rPam ) + , m_pSectionData(new SwSectionData(rNewData)) + , m_pTOXBase( pTOXBase + ? std::make_unique<std::pair<SwTOXBase *, sw::RedlineMode>>( + new SwTOXBase(*pTOXBase->first), pTOXBase->second) + : nullptr ) + , m_pAttrSet( (pSet && pSet->Count()) ? new SfxItemSet( *pSet ) : nullptr ) + , m_nSectionNodePos(0) + , m_bSplitAtStart(false) + , m_bSplitAtEnd(false) + , m_bUpdateFootnote(false) +{ + 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() ) + { + 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_pTOXBase) + { + SwRootFrame const* pLayout(nullptr); + SwRootFrame * pLayoutToReset(nullptr); + comphelper::ScopeGuard g([&]() { + if (pLayoutToReset) + { + pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Shown); + } + }); + o3tl::sorted_vector<SwRootFrame *> layouts(rDoc.GetAllLayouts()); + for (SwRootFrame const*const p : layouts) + { + if ((m_pTOXBase->second == sw::RedlineMode::Hidden) == p->IsHideRedlines()) + { + pLayout = p; + break; + } + } + if (!pLayout) + { + assert(!layouts.empty()); // must have one layout + pLayoutToReset = *layouts.begin(); + pLayoutToReset->SetHideRedlines(m_pTOXBase->second == sw::RedlineMode::Hidden); + pLayout = pLayoutToReset; + } + pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(), + // don't expand: will be done by SwUndoUpdateIndex::RedoImpl() + *m_pTOXBase->first, 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, 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, 1 ); + rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); + } + + if( pUpdateTOX ) + { + // initiate formatting + SwEditShell* pESh = rDoc.GetEditShell(); + if( pESh ) + pESh->CalcLayout(); + + // insert page numbers + const_cast<SwTOXBaseSection*>(pUpdateTOX)->UpdatePageNum(); + } +} + +void SwUndoInsSection::RepeatImpl(::sw::RepeatContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if (m_pTOXBase) + { + rDoc.InsertTableOf(*rContext.GetRepeatPaM().GetPoint(), + *m_pTOXBase->first, 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, sal_uLong 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::unique_ptr<SfxItemSet> const m_pAttrSet; + std::shared_ptr< ::sfx2::MetadatableUndo > const m_pMetadataUndo; + sal_uLong const m_nStartNode; + sal_uLong 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_pAttrSet( ::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_pAttrSet.get()); + } + else + { + SwNodeIndex aStt( rDoc.GetNodes(), m_nStartNode ); + SwNodeIndex aEnd( rDoc.GetNodes(), m_nEndNode-2 ); + SwSectionFormat* pFormat = rDoc.MakeSectionFormat(); + if (m_pAttrSet) + { + pFormat->SetFormatAttr( *m_pAttrSet ); + } + + /// 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(), USHRT_MAX); + 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::unique_ptr<SfxItemSet> m_pAttrSet; + sal_uLong 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_pAttrSet( ::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::unique_ptr<SfxItemSet> pCur = ::lcl_GetAttrSet( rNdSect ); + if (m_pAttrSet) + { + // The Content and Protect items must persist + const SfxPoolItem* pItem; + m_pAttrSet->Put( pFormat->GetFormatAttr( RES_CNTNT )); + if( SfxItemState::SET == pFormat->GetItemState( RES_PROTECT, true, &pItem )) + { + m_pAttrSet->Put( *pItem ); + } + pFormat->DelDiffs( *m_pAttrSet ); + m_pAttrSet->ClearItem( RES_CNTNT ); + pFormat->SetFormatAttr( *m_pAttrSet ); + } + 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 ); + } + m_pAttrSet = std::move(pCur); + + if (!m_bOnlyAttrChanged) + { + 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..8f0820fb6 --- /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) + , nTableNd(0) +{ + pSortOpt.reset( new SwSortOptions(rOpt) ); +} + +SwUndoSort::SwUndoSort( sal_uLong nStt, sal_uLong nEnd, const SwTableNode& rTableNd, + const SwSortOptions& rOpt, bool bSaveTable ) + : SwUndo(SwUndoId::SORT_TBL, rTableNd.GetDoc()) +{ + m_nSttNode = nStt; + m_nEndNode = nEnd; + nTableNd = rTableNd.GetIndex(); + + pSortOpt.reset( new SwSortOptions(rOpt) ); + if( bSaveTable ) + pUndoTableAttr.reset( new SwUndoAttrTable( rTableNd ) ); +} + +SwUndoSort::~SwUndoSort() +{ + pSortOpt.reset(); + pUndoTableAttr.reset(); +} + +void SwUndoSort::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc & rDoc = rContext.GetDoc(); + if(pSortOpt->bTable) + { + // Undo for Table + RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode ); + + if( pUndoTableAttr ) + { + pUndoTableAttr->UndoImpl(rContext); + } + + SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->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 sal_uLong 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 == m_nSttNode + 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 + i ); + SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 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(pSortOpt->bTable) + { + // Redo for Table + RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode ); + + SwTableNode* pTableNd = rDoc.GetNodes()[ nTableNd ]->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( pUndoTableAttr ) + { + pUndoTableAttr->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 sal_uLong 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 + i); + SwNodeRange aRg( aIdxList[i], 0, aIdxList[i], 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(!pSortOpt->bTable) + { + SwPaM *const pPam = & rContext.GetRepeatPaM(); + SwDoc& rDoc = *pPam->GetDoc(); + + if( !rDoc.IsIdxInTable( pPam->Start()->nNode ) ) + rDoc.SortText(*pPam, *pSortOpt); + } +} + +void SwUndoSort::Insert( const OUString& rOrgPos, const OUString& rNewPos) +{ + m_SortList.push_back(std::make_unique< SwSortUndoElement>(rOrgPos, rNewPos)); +} + +void SwUndoSort::Insert( sal_uLong nOrgPos, sal_uLong 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..112db668d --- /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 <swundo.hxx> +#include <frmfmt.hxx> +#include <UndoCore.hxx> +#include <rolbck.hxx> +#include <redline.hxx> +#include <docary.hxx> +#include <IShellCursorSupplier.hxx> +#include <osl/diagnose.h> + +// SPLITNODE + +SwUndoSplitNode::SwUndoSplitNode( SwDoc* pDoc, const SwPosition& rPos, + bool bChkTable ) + : SwUndo( SwUndoId::SPLITNODE, pDoc ), nNode( rPos.nNode.GetIndex() ), + nContent( rPos.nContent.GetIndex() ), + bTableFlag( false ), bChkTableStt( 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(), nNode, 0, + pTextNd->GetText().getLength(), false ); + if (!m_pHistory->Count()) + { + m_pHistory.reset(); + } + } + // consider Redline + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + pRedlData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() ); + } + + nParRsid = pTextNd->GetParRsid(); +} + +SwUndoSplitNode::~SwUndoSplitNode() +{ + m_pHistory.reset(); + pRedlData.reset(); +} + +void SwUndoSplitNode::UndoImpl(::sw::UndoRedoContext & rContext) +{ + SwDoc *const pDoc = & rContext.GetDoc(); + SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() ); + rPam.DeleteMark(); + if( bTableFlag ) + { + // than a TextNode was added directly before the current table + SwNodeIndex& rIdx = rPam.GetPoint()->nNode; + rIdx = nNode; + SwTextNode* pTNd; + SwNode* pCurrNd = pDoc->GetNodes()[ 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 ) + { + const SfxPoolItem *pItem; + if( SfxItemState::SET == pNdSet->GetItemState( RES_PAGEDESC, false, + &pItem ) ) + pTableFormat->SetFormatAttr( *pItem ); + + if( SfxItemState::SET == pNdSet->GetItemState( RES_BREAK, false, + &pItem ) ) + 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()[ 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( 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, nParRsid ); + } + } + + // also set the cursor onto undo section + rPam.DeleteMark(); + rPam.GetPoint()->nNode = nNode; + rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), nContent ); +} + +void SwUndoSplitNode::RedoImpl(::sw::UndoRedoContext & rContext) +{ + SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() ); + rPam.GetPoint()->nNode = nNode; + SwTextNode * pTNd = rPam.GetNode().GetTextNode(); + OSL_ENSURE(pTNd, "SwUndoSplitNode::RedoImpl(): SwTextNode expected"); + if (pTNd) + { + rPam.GetPoint()->nContent.Assign( pTNd, nContent ); + + SwDoc* pDoc = rPam.GetDoc(); + pDoc->getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), bChkTableStt ); + + if (m_pHistory) + { + m_pHistory->SetTmpEnd(m_pHistory->Count()); + } + + if( ( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) || + ( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )) + { + rPam.SetMark(); + if( rPam.Move( fnMoveBackward )) + { + if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) + { + RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *pRedlData, rPam ), true); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else + pDoc->getIDocumentRedlineAccess().SplitRedline( rPam ); + rPam.Exchange(); + } + rPam.DeleteMark(); + } + } +} + +void SwUndoSplitNode::RepeatImpl(::sw::RepeatContext & rContext) +{ + rContext.GetDoc().getIDocumentContentOperations().SplitNode( + *rContext.GetRepeatPaM().GetPoint(), bChkTableStt ); +} + +/* 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..1cbf5c1bd --- /dev/null +++ b/sw/source/core/undo/untbl.cxx @@ -0,0 +1,3155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UndoTable.hxx> +#include <UndoRedline.hxx> +#include <UndoDelete.hxx> +#include <UndoSplitMove.hxx> +#include <UndoCore.hxx> +#include <fesh.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 <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 <rowfrm.hxx> +#include <cellfrm.hxx> +#include <swcache.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 <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 +{ + sal_uLong 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 ); +}; + +namespace { + +class SaveBox; +class SaveLine; + +} + +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; + +public: + SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt = USHRT_MAX, + bool bSaveFormula = true ); + + sal_uInt16 AddFormat( SwFrameFormat* pFormat, bool bIsLine ); + void NewFrameFormat( const SwTableLine* , 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* pNext; + SaveBox* pBox; + sal_uInt16 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* pNext; + sal_uLong nSttNode; + long nRowSpan; + sal_uInt16 nItemSet; + union + { + SfxItemSets* pContentAttrs; + SaveLine* pLine; + } 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 +{ + sal_uLong m_nSttNd; + sal_uLong 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, sal_uLong nNd, sal_uLong nEndIdx, sal_Int32 nContent ); + +private: + SwTableToTextSave(const SwTableToTextSave&) = delete; + SwTableToTextSave& operator=(const SwTableToTextSave&) = delete; + +}; + +sal_uInt16 const aSave_BoxContentSet[] = { + 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, + 0 }; + +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(); + 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 ); + } + + 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(); + + 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() ); + rDoc.GetEditShell()->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() )) + { + SwPaM aPam( *pTableNode->EndOfSectionNode(), *pTableNode, 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, sal_uLong nNd, sal_uLong 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(); + sal_uLong 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( sal_uLong nSttNd, sal_uLong 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; + { + sal_uLong 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)> restoreFunc( + [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode) + { + 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(), pTextNd->GetText().getLength() ); + } + + 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( sal_uLong 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, sal_uLong nNdIdx, sal_uLong 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(); + + sal_uLong 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. + const SfxPoolItem* pItem; + if( SfxItemState::SET == pSet->GetItemState( RES_BOXATR_FORMULA, true, &pItem )) + { + pSet->ClearItem( RES_BOXATR_VALUE ); + if (m_pSwTable && m_bSaveFormula) + { + SwTableFormulaUpdate aMsgHint(m_pSwTable); + aMsgHint.m_eFlags = TBL_BOXNAME; + SwTableBoxFormula* pFormulaItem = const_cast<SwTableBoxFormula*>(static_cast<const 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 static_cast<sal_uInt16>(nRet); +} + +void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) +{ + m_bModifyBox = bMdfyBox; + + // 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); + + if( pFormat->IsInCache() ) + { + SwFrame::GetCache().Delete( pFormat ); + pFormat->SetInCache( false ); + } + + // 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(); + } + + // 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->pNext ) + { + if( !pLn ) + { + OSL_ENSURE( false, "Number of lines changed" ); + break; + } + + pLn->RestoreAttr( *rTable.GetTabLines()[ n ], *this ); + } + + m_aFrameFormats.clear(); + m_bModifyBox = false; +} + +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); + + if( pFormat->IsInCache() ) + { + SwFrame::GetCache().Delete( pFormat ); + pFormat->SetInCache( false ); + } + + // 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() ); + } +} + +void SaveTable::NewFrameFormat( const SwTableLine* pTableLn, const SwTableBox* pTableBx, + sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat ) +{ + SwDoc* pDoc = pOldFormat->GetDoc(); + + SwFrameFormat* pFormat = m_aFrameFormats[ nFormatPos ]; + if( !pFormat ) + { + if( pTableLn ) + pFormat = pDoc->MakeTableLineFormat(); + else + pFormat = pDoc->MakeTableBoxFormat(); + pFormat->SetFormatAttr(*m_aSets[nFormatPos]); + m_aFrameFormats[nFormatPos] = pFormat; + } + + // first re-assign Frames + SwIterator<SwLayoutFrame,SwFormat> aIter( *pOldFormat ); + for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() ) + { + if( pTableLn ? static_cast<SwRowFrame*>(pLast)->GetTabLine() == pTableLn + : static_cast<SwCellFrame*>(pLast)->GetTabBox() == pTableBx ) + { + pLast->RegisterToFormat(*pFormat); + pLast->InvalidateAll(); + pLast->ReinitializeFrameSizeAttrFlags(); + if ( !pTableLn ) + { + static_cast<SwCellFrame*>(pLast)->SetDerivedVert( false ); + static_cast<SwCellFrame*>(pLast)->CheckDirChange(); + } + } + } + + // than re-assign myself + if ( pTableLn ) + const_cast<SwTableLine*>(pTableLn)->RegisterToFormat( *pFormat ); + else if ( pTableBx ) + const_cast<SwTableBox*>(pTableBx)->RegisterToFormat( *pFormat ); + + if (m_bModifyBox && !pTableLn) + { + const SfxPoolItem& rOld = pOldFormat->GetFormatAttr( RES_BOXATR_FORMAT ), + & rNew = pFormat->GetFormatAttr( RES_BOXATR_FORMAT ); + if( rOld != rNew ) + pFormat->ModifyNotification( &rOld, &rNew ); + } + + if( !pOldFormat->HasWriterListeners() ) + delete pOldFormat; +} + +SaveLine::SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable ) + : pNext( nullptr ) +{ + if( pPrev ) + pPrev->pNext = this; + + nItemSet = rSTable.AddFormat( rLine.GetFrameFormat(), true ); + + pBox = new SaveBox( nullptr, *rLine.GetTabBoxes()[ 0 ], rSTable ); + SaveBox* pBx = pBox; + for( size_t n = 1; n < rLine.GetTabBoxes().size(); ++n ) + pBx = new SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTable ); +} + +SaveLine::~SaveLine() +{ + delete pBox; + delete pNext; +} + +void SaveLine::RestoreAttr( SwTableLine& rLine, SaveTable& rSTable ) +{ + rSTable.NewFrameFormat( &rLine, nullptr, nItemSet, rLine.GetFrameFormat() ); + + SaveBox* pBx = pBox; + for( size_t n = 0; n < rLine.GetTabBoxes().size(); ++n, pBx = pBx->pNext ) + { + if( !pBx ) + { + OSL_ENSURE( false, "Number of boxes changed" ); + break; + } + pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTable ); + } +} + +void SaveLine::SaveContentAttrs( SwDoc* pDoc ) +{ + pBox->SaveContentAttrs( pDoc ); + if( pNext ) + pNext->SaveContentAttrs( pDoc ); +} + +void SaveLine::CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable ) +{ + SwTableLineFormat* pFormat = static_cast<SwTableLineFormat*>(rSTable.m_aFrameFormats[ nItemSet ]); + if( !pFormat ) + { + SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc(); + pFormat = pDoc->MakeTableLineFormat(); + pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]); + rSTable.m_aFrameFormats[nItemSet] = pFormat; + } + SwTableLine* pNew = new SwTableLine( pFormat, 1, &rParent ); + + rParent.GetTabLines().push_back( pNew ); + + pBox->CreateNew( rTable, *pNew, rSTable ); + + if( pNext ) + pNext->CreateNew( rTable, rParent, rSTable ); +} + +SaveBox::SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable ) + : pNext( nullptr ), nSttNode( ULONG_MAX ), nRowSpan(0) +{ + Ptrs.pLine = nullptr; + + if( pPrev ) + pPrev->pNext = this; + + nItemSet = rSTable.AddFormat( rBox.GetFrameFormat(), false ); + + if( rBox.GetSttNd() ) + { + nSttNode = rBox.GetSttIdx(); + nRowSpan = rBox.getRowSpan(); + } + else + { + Ptrs.pLine = new SaveLine( nullptr, *rBox.GetTabLines()[ 0 ], rSTable ); + + SaveLine* pLn = Ptrs.pLine; + for( size_t n = 1; n < rBox.GetTabLines().size(); ++n ) + pLn = new SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTable ); + } +} + +SaveBox::~SaveBox() +{ + if( ULONG_MAX == nSttNode ) // no EndBox + delete Ptrs.pLine; + else + delete Ptrs.pContentAttrs; + delete pNext; +} + +void SaveBox::RestoreAttr( SwTableBox& rBox, SaveTable& rSTable ) +{ + rSTable.NewFrameFormat( nullptr, &rBox, nItemSet, rBox.GetFrameFormat() ); + + if( ULONG_MAX == nSttNode ) // no EndBox + { + if( rBox.GetTabLines().empty() ) + { + OSL_ENSURE( false, "Number of lines changed" ); + } + else + { + SaveLine* pLn = Ptrs.pLine; + for( size_t n = 0; n < rBox.GetTabLines().size(); ++n, pLn = pLn->pNext ) + { + if( !pLn ) + { + OSL_ENSURE( false, "Number of lines changed" ); + break; + } + + pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTable ); + } + } + } + else if( rBox.GetSttNd() && rBox.GetSttIdx() == nSttNode ) + { + if( Ptrs.pContentAttrs ) + { + SwNodes& rNds = rBox.GetFrameFormat()->GetDoc()->GetNodes(); + sal_uInt16 nSet = 0; + sal_uLong nEnd = rBox.GetSttNd()->EndOfSectionIndex(); + for( sal_uLong n = nSttNode + 1; n < nEnd; ++n ) + { + SwContentNode* pCNd = rNds[ n ]->GetContentNode(); + if( pCNd ) + { + std::shared_ptr<SfxItemSet> pSet( (*Ptrs.pContentAttrs)[ nSet++ ] ); + if( pSet ) + { + sal_uInt16 const *pRstAttr = aSave_BoxContentSet; + while( *pRstAttr ) + { + pCNd->ResetAttr( *pRstAttr, *(pRstAttr+1) ); + pRstAttr += 2; + } + pCNd->SetAttr( *pSet ); + } + else + pCNd->ResetAllAttr(); + } + } + } + } + else + { + OSL_ENSURE( false, "Box not anymore at the same node" ); + } +} + +void SaveBox::SaveContentAttrs( SwDoc* pDoc ) +{ + if( ULONG_MAX == nSttNode ) // no EndBox + { + // continue in current line + Ptrs.pLine->SaveContentAttrs( pDoc ); + } + else + { + sal_uLong nEnd = pDoc->GetNodes()[ nSttNode ]->EndOfSectionIndex(); + Ptrs.pContentAttrs = new SfxItemSets; + for( sal_uLong n = nSttNode + 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() ); + } + + Ptrs.pContentAttrs->push_back( pSet ); + } + } + } + if( pNext ) + pNext->SaveContentAttrs( pDoc ); +} + +void SaveBox::CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable ) +{ + SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(rSTable.m_aFrameFormats[ nItemSet ]); + if( !pFormat ) + { + SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc(); + pFormat = pDoc->MakeTableBoxFormat(); + pFormat->SetFormatAttr(*rSTable.m_aSets[nItemSet]); + rSTable.m_aFrameFormats[nItemSet] = pFormat; + } + + if( ULONG_MAX == nSttNode ) // no EndBox + { + SwTableBox* pNew = new SwTableBox( pFormat, 1, &rParent ); + rParent.GetTabBoxes().push_back( pNew ); + + Ptrs.pLine->CreateNew( rTable, *pNew, rSTable ); + } + else + { + // search box for StartNode in old table + SwTableBox* pBox = rTable.GetTableBox( nSttNode ); + if (pBox) + { + SwFrameFormat* pOld = pBox->GetFrameFormat(); + pBox->RegisterToFormat( *pFormat ); + if( !pOld->HasWriterListeners() ) + delete pOld; + + pBox->setRowSpan( 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( pNext ) + 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, + long nMn, 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_pNewSttNds.reset( new std::set<BoxMove> ); + + 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_pNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) ); + } + + for( ; i < rTableBoxes.size(); ++i ) + // new box: insert sorted + m_pNewSttNds->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<sal_uLong> &rNodeCnts ) +{ + const SwTable& rTable = rTableNd.GetTable(); + const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes(); + + OSL_ENSURE( ! IsDelBox(), "wrong Action" ); + m_pNewSttNds.reset( new std::set<BoxMove> ); + + 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; + sal_uLong 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 > nLineDiff ); + m_pNewSttNds->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 *, sal_uLong> > 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_pNewSttNds->empty() ) + { + // Then the nodes have be moved and not deleted! + // But for that we need a temp array. + std::vector<BoxMove> aTmp( m_pNewSttNds->begin(), m_pNewSttNds->end() ); + + // backwards + for (size_t n = aTmp.size(); n > 0 ; ) + { + --n; + // delete box from table structure + sal_uLong 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(), 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; + sal_uLong 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_pNewSttNds->rbegin(); it != m_pNewSttNds->rend(); ++it ) + { + sal_uLong 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 *, sal_uLong> & 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( 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 + sal_uLong 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* pDoc, SwNodeRange& rRg, SwNodeIndex& rPos ) +{ + SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 ); + std::unique_ptr<SwUndoMove> pUndo(new SwUndoMove( pDoc, rRg, rPos )); + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + pDoc->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( sal_uLong(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( ULONG_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 ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMAT, + false, &pItem )) + { + m_bNewFormat = true; + m_nNewFormatIdx = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue(); + } + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_FORMULA, + false, &pItem )) + { + m_bNewFormula = true; + m_aNewFormula = static_cast<const SwTableBoxFormula*>(pItem)->GetFormula(); + } + if( SfxItemState::SET == pNewSet->GetItemState( RES_BOXATR_VALUE, + false, &pItem )) + { + m_bNewValue = true; + m_fNewNum = static_cast<const SwTableBoxValue*>(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( ULONG_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 ) + { + SfxItemSet aBoxSet( rDoc.GetAttrPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + + // 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 ) + { + SfxItemSet aBoxSet( rDoc.GetAttrPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + + 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 ) +{ +} + +SwUndoTableCpyTable::SwUndoTableCpyTable(const SwDoc* pDoc) + : SwUndo( SwUndoId::TBLCPYTBL, pDoc ) +{ +} + +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(); + sal_uLong 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 ); + 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, bDeleteCompleteParagraph, true ); + } + else + { + pUndo = std::make_unique<SwUndoDelete>( aPam, true ); + if( pEntry->pUndo ) + { + pEntry->pUndo->UndoImpl(rContext); + pEntry->pUndo.reset(); + } + } + pEntry->pUndo = std::move(pUndo); + + aInsIdx = rBox.GetSttIdx() + 1; + rDoc.GetNodes().Delete( aInsIdx ); + + SfxItemSet aTmpSet( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + 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<SfxItemSet>( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + 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(); + sal_uLong 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, 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 const& 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 ); + + SfxItemSet aTmpSet( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + 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<SfxItemSet>( + rDoc.GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + 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, true ); + } + + pEntry->pBoxNumAttr = std::make_unique<SfxItemSet>( + pDoc->GetAttrPool(), + svl::Items< + RES_VERT_ORIENT, RES_VERT_ORIENT, + RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + 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, + const 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; + 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, 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; +} + +bool SwUndoTableCpyTable::IsEmpty() const +{ + return !m_pInsRowUndo && m_vArr.empty(); +} + +SwUndoCpyTable::SwUndoCpyTable(const SwDoc* pDoc) + : SwUndo( SwUndoId::CPYTBL, pDoc ), 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(); + 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 ); + } + + SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), 0 , 1 ); + m_pDelete.reset( new SwUndoDelete( aPam, 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* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_CREATE, pDoc), + 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, const std::vector<SwTable*>& rAffectedTables, const SwDoc* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_DELETE, pDoc), + m_pAutoFormat(std::move(pAutoFormat)), + m_rAffectedTables(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* pDoc) + : SwUndo(SwUndoId::TBLSTYLE_UPDATE, pDoc) + , 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..374241404 --- /dev/null +++ b/sw/source/core/undo/untblk.cxx @@ -0,0 +1,451 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IShellCursorSupplier.hxx> +#include <docary.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::unique_ptr<std::vector<SwFrameFormat*>> +GetFlysAnchoredAt(SwDoc & rDoc, sal_uLong const nSttNode) +{ + std::unique_ptr<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.reset( new std::vector<SwFrameFormat*> ); + 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* pDoc = 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(*pDoc, m_nSttNode); + } + // consider Redline + if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) ); + SetRedlineFlags( pDoc->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, + int 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 == 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) + { + // than collect all new Flys + SwDoc* pDoc = rPam.GetDoc(); + const size_t nArrLen = pDoc->GetSpzFrameFormats()->size(); + for( size_t n = 0; n < nArrLen; ++n ) + { + SwFrameFormat* pFormat = (*pDoc->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, 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, + sal_uLong const nStartNode, sal_uLong 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()); +} + +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 = 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()) + { + sal_uLong 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(); + sal_uLong 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 == 0) + { + rPam.Move( fnMoveBackward, GoInContent ); + } + } + } + + SwNodeIndex& rIdx = rPam.GetPoint()->nNode; + SwTextNode* pTextNode = rIdx.GetNode().GetTextNode(); + if( pTextNode ) + { + if( !m_pTextFormatColl ) // if 0 than it's no TextNode -> delete + { + SwNodeIndex aDelIdx( rIdx ); + assert(0 < m_nDeleteTextNodes && m_nDeleteTextNodes < 3); + for (int i = 0; i < m_nDeleteTextNodes; ++i) + { + rPam.Move(fnMoveForward, GoInNode); + } + rPam.DeleteMark(); + + for (int 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 + SwPaM& rPam(rContext.GetCursorSupplier().CreateNewShellCursor()); + SwDoc* pDoc = 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(*pDoc, + rPam.GetPoint()->nNode.GetIndex())); + + const bool bMvBkwrd = MovePtBackward(rPam); + + // re-insert content again (first detach m_pUndoNodeIndex!) + sal_uLong const nMvNd = m_pUndoNodeIndex->GetIndex(); + m_pUndoNodeIndex.reset(); + MoveFromUndoNds(*pDoc, nMvNd, *rPam.GetMark()); + if (m_nDeleteTextNodes != 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 (pDoc->GetTextFormatColls()->IsAlive(m_pTextFormatColl)) + { + SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode(); + if( pTextNd ) + pTextNd->ChgFormatColl( m_pTextFormatColl ); + } + m_pTextFormatColl = pSavTextFormatColl; + + if (m_pLastNodeColl && pDoc->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( pDoc, 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 = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + } + else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) && + !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + pDoc->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..19ceff59d --- /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 SfxItemPropertySimpleEntry *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) + { + SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_PAGEDESC, RES_PAGEDESC>{} ); + 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 SfxItemPropertySimpleEntry *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 SfxItemPropertySimpleEntry *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.begin(), + [this](const OUString& rName) -> PropertyState { return getPropertyState(rName); }); + + return aRet; +} + +void SAL_CALL SwXTextDefaults::setPropertyToDefault( const OUString& rPropertyName ) +{ + if (!m_pDoc) + throw RuntimeException(); + const SfxItemPropertySimpleEntry *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 SfxItemPropertySimpleEntry *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..d2c45acf4 --- /dev/null +++ b/sw/source/core/unocore/TextCursorHelper.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 <TextCursorHelper.hxx> +#include <comphelper/servicehelper.hxx> + +using namespace ::com::sun::star; + +namespace +{ + class theOTextCursorHelperUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theOTextCursorHelperUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & OTextCursorHelper::getUnoTunnelId() +{ + return theOTextCursorHelperUnoTunnelId::get().getSeq(); +} + +//XUnoTunnel +sal_Int64 SAL_CALL OTextCursorHelper::getSomething( + const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<OTextCursorHelper>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + +/* 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..4542469f5 --- /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 <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( + const OUString& 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 = rXMLString.copy( 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 = (aCellStr.copy( i + 1 )).toInt32() - 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..9bfa185af --- /dev/null +++ b/sw/source/core/unocore/swunohelper.cxx @@ -0,0 +1,336 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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::makeAny( true ) ); + bRemoved = true; + } + catch( css::uno::Exception& ) + { + bRemoved = false; + OSL_FAIL( "Exception from executeCommand( delete )" ); + } + return bRemoved; +} + +bool UCB_MoveFile( const OUString& rURL, const OUString& 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& ) + { + OSL_FAIL( "Exception from executeCommand( transfer )" ); + bCopyCompleted = false; + } + return bCopyCompleted; +} + +bool UCB_IsCaseSensitiveFileName( const OUString& 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; + OSL_FAIL( "Exception from 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& ) + { + OSL_FAIL( "Exception caught!" ); + } + } + } + catch( css::uno::Exception& ) + { + OSL_FAIL( "Exception caught!" ); + 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..c8fa27faa --- /dev/null +++ b/sw/source/core/unocore/unobkm.cxx @@ -0,0 +1,734 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/interfacecontainer2.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/mutex.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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 m_EventListeners; + SwDoc* m_pDoc; + ::sw::mark::IMark* m_pRegisteredBookmark; + OUString m_sMarkName; + bool m_bHidden; + OUString m_HideCondition; + + Impl( SwDoc *const pDoc ) + : m_EventListeners(m_Mutex) + , 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); + m_EventListeners.disposeAndClear(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(); +} + +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( + ::sw::UnoTunnelGetImplementation<SwXBookmark>(xUT)); + if (pXBkm && (pDoc == pXBkm->m_pImpl->m_pDoc)) + { + return pXBkm->m_pImpl->m_pRegisteredBookmark; + } + return nullptr; +} + +namespace +{ + class theSwXBookmarkUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXBookmarkUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXBookmark::getUnoTunnelId() +{ + return theSwXBookmarkUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXBookmark::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + return ::sw::UnoTunnelImpl<SwXBookmark>(rId, this); +} + +void SwXBookmark::attachToRangeEx( + const uno::Reference< text::XTextRange > & xTextRange, + IDocumentMarkAccess::MarkType eType) +{ + if (m_pImpl->m_pRegisteredBookmark) + { + throw uno::RuntimeException(); + } + + const uno::Reference<lang::XUnoTunnel> xRangeTunnel( + xTextRange, uno::UNO_QUERY); + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + pCursor = + ::sw::UnoTunnelGetImplementation<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)); + // #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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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) +{ + 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"); +} + +SwXFieldmark::SwXFieldmark(bool _isReplacementObject, SwDoc* pDc) + : SwXFieldmark_Base(pDc) + , m_bReplacementObject(_isReplacementObject) +{ } + +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(); +} + +void SwXFieldmark::attachToRange( const uno::Reference < text::XTextRange >& xTextRange ) +{ + + attachToRangeEx( xTextRange, + ( m_bReplacementObject ? IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK : IDocumentMarkAccess::MarkType::TEXT_FIELDMARK ) ); +} + +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(); + if(fieldType != getFieldType()) + { + if(fieldType == ODF_FORMDROPDOWN || fieldType == ODF_FORMCHECKBOX || fieldType == ODF_FORMDATE) + { + ::sw::mark::IFieldmark* pNewFieldmark = GetIDocumentMarkAccess()->changeFormFieldmarkType(pBkm, fieldType); + if (pNewFieldmark) + { + registerInMark(*this, pNewFieldmark); + return; + } + } + + // We did not generate a new fieldmark, so set the type ID + pBkm->SetFieldname(fieldType); + } +} + +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 + SwXFieldmark* pXBkmk = nullptr; + 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(pXBkmk); + 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 + SwXFieldmark_Base::setPropertyValue( PropertyName, rValue ); +} + +// 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::makeAny( pCheckboxFm->IsChecked() ); + } + return SwXFieldmark_Base::getPropertyValue( rPropertyName ); +} + +/* 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..4b0cf5738 --- /dev/null +++ b/sw/source/core/unocore/unochart.cxx @@ -0,0 +1,2717 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <osl/mutex.hxx> +#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_bIsLocked( false ) +{ + m_aUnlockTimer.SetTimeout( 1500 ); + m_aUnlockTimer.SetInvokeHandler( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts )); + m_aUnlockTimer.SetDebugName( "sw::SwChartLockController_Helper aUnlockTimer" ); +} + +SwChartLockController_Helper::~SwChartLockController_Helper() COVERITY_NOEXCEPT_FALSE +{ + if (m_pDoc) // still connected? + 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 osl::Mutex & GetChartMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + +static void LaunchModifiedEvent( + ::comphelper::OInterfaceContainerHelper2 &rICH, + const uno::Reference< uno::XInterface > &rxI ) +{ + lang::EventObject aEvtObj( rxI ); + comphelper::OInterfaceIteratorHelper2 aIt( rICH ); + while (aIt.hasMoreElements()) + { + uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY ); + if (xRef.is()) + xRef->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, + const OUString &rCellRangeName ) +{ + sal_Int32 nToken = -1 == rCellRangeName.indexOf('.') ? 0 : 1; + OUString aCellRangeNoTableName( rCellRangeName.getToken( nToken, '.' ) ); + OUString aTLName( aCellRangeNoTableName.getToken(0, ':') ); // name of top left cell + OUString aBRName( aCellRangeNoTableName.getToken(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( SwFrameFormat &rTableFormat, SwUnoCursor &rTableCursor ) +{ + OUString aRes; + + //!! see also SwXTextTableCursor::getRangeName + + SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&rTableCursor); + if (!pUnoTableCursor) + return OUString(); + 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( const OUString &rTableName, + const OUString &rStartCell, const OUString &rEndCell, + bool bForceEndCellName ) +{ + OSL_ENSURE( !rTableName.isEmpty(), "table name missing" ); + OSL_ENSURE( !rStartCell.isEmpty(), "cell name missing" ); + OUString aRes = rTableName + "." + rStartCell; + + if (!rEndCell.isEmpty()) + { + aRes += ":" + rEndCell; + } + else if (bForceEndCellName) + { + aRes += ":" + rStartCell; + } + + return aRes; +} + +static bool GetTableAndCellsFromRangeRep( + const OUString &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 aRange; // cell range + OUString aStartCell; // name of top left cell + OUString aEndCell; // name of bottom right cell + sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' ); + if (nIdx >= 0) + { + aTableName = rRangeRepresentation.copy( 0, nIdx ); + aRange = rRangeRepresentation.copy( nIdx + 1 ); + sal_Int32 nPos = aRange.indexOf( ':' ); + if (nPos >= 0) // a cell-range like "Table1.A2:D4" + { + aStartCell = aRange.copy( 0, nPos ); + aEndCell = aRange.copy( 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, const OUString &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, + const OUString &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( const OUString &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( rRangeRepresentation.getToken( 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* pSwDoc ) : + m_aEventListeners( GetChartMutex() ), + m_pDoc( pSwDoc ) +{ + 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( sal_uLong nN = rNodes.Count(); nN--; ) + { + SwNodePtr 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 = pTable->GetTabLines().front()->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; + bool bFirstFound = false; + for (oi = 0; oi < oiEnd; ++oi) + { + // 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) + { + 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 : 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( + const OUString &rCellRangeRepresentation ) +{ + // check that we do not have multiple ranges + if (-1 == rCellRangeRepresentation.indexOf( ';' )) + { + // 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 ); + std::sort( aSortedMapping.begin(), aSortedMapping.end() ); + bool bNeedSequenceMapping = false; + for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i) + { + auto it = std::find( aSortedMapping.begin(), aSortedMapping.end(), + aSequenceMapping[i] ); + aSequenceMapping[i] = std::distance(aSortedMapping.begin(), it); + + if (i != 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); + sal_Int32 nProps = 0; + aResult[nProps ].Name = "FirstCellAsLabel"; + aResult[nProps++].Value <<= bFirstCellIsLabel; + aResult[nProps ].Name = "CellRangeRepresentation"; + aResult[nProps++].Value <<= aSortedCellRanges; + if (!aBrokenCellRangeForExport.isEmpty()) + { + aResult[nProps ].Name = "BrokenCellRangeForExport"; + aResult[nProps++].Value <<= aBrokenCellRangeForExport; + } + if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1) + { + chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ? + chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS; + aResult[nProps ].Name = "DataRowSource"; + aResult[nProps++].Value <<= eDataRowSource; + + if (aSequenceMapping.hasElements()) + { + aResult[nProps ].Name = "SequenceMapping"; + aResult[nProps++].Value <<= aSequenceMapping; + } + } + aResult.realloc( nProps ); + + return aResult; +} + +uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation( + const OUString& 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*/ ) +{ + return uno::Reference<css::chart2::data::XDataSequence>(); +} + +void SAL_CALL SwChartDataProvider::dispose( ) +{ + bool bMustDispose( false ); + { + osl::MutexGuard aGuard( GetChartMutex() ); + bMustDispose = !m_bDisposed; + if (!m_bDisposed) + m_bDisposed = true; + } + if (bMustDispose) + { + // 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( dynamic_cast< chart2::data::XDataProvider * >(this) ); + m_aEventListeners.disposeAndClear( aEvtObj ); + } +} + +void SAL_CALL SwChartDataProvider::addEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEventListeners.addInterface( rxListener ); +} + +void SAL_CALL SwChartDataProvider::removeEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEventListeners.removeInterface( 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 ) +{ + OSL_ENSURE( pTable, "table pointer is NULL" ); + if (pTable) + { + 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 ); + } + } + } +} + +void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox ) +{ + OSL_ENSURE( pTable, "table pointer is NULL" ); + if (pTable) + { + 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) + { + 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) + { + 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) + { + //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::getUnoTunnelImplementation<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_aEvtListeners( GetChartMutex() ), + m_aModifyListeners( GetChartMutex() ), + 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( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); + m_xDataProvider->AddDataSequence( *pTable, xRef ); + m_xDataProvider->addEventListener( dynamic_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_aEvtListeners( GetChartMutex() ), + m_aModifyListeners( GetChartMutex() ), + 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( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); + m_xDataProvider->AddDataSequence( *pTable, xRef ); + m_xDataProvider->addEventListener( dynamic_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() +{ +} + +namespace +{ + class theSwChartDataSequenceUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwChartDataSequenceUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId() +{ + return theSwChartDataSequenceUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId ) +{ + if( isUnoTunnelId<SwChartDataSequence>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + + +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; + OUString aNew; + if (bUseCol) + { + aRplc = "%COLUMNLETTER"; + aNew = aCellName.copy(0, pBuf - aCellName.getStr()); + } + else + { + aRplc = "%ROWNUMBER"; + aNew = OUString(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.begin(), + [] (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.begin(), + [] (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.begin(), + [] (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 (dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + 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, dynamic_cast< XModifyBroadcaster * >(this) ); +} + +void SAL_CALL SwChartDataSequence::addModifyListener( + const uno::Reference< util::XModifyListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aModifyListeners.addInterface( rxListener ); +} + +void SAL_CALL SwChartDataSequence::removeModifyListener( + const uno::Reference< util::XModifyListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aModifyListeners.removeInterface( 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 ); + { + osl::MutexGuard aGuard( GetChartMutex() ); + bMustDispose = !m_bDisposed; + if (!m_bDisposed) + m_bDisposed = true; + } + if (bMustDispose) + { + m_bDisposed = true; + if (m_xDataProvider.is()) + { + const SwTable* pTable = SwTable::FindTable( GetFrameFormat() ); + if (pTable) + { + uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY ); + 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( dynamic_cast< chart2::data::XDataSequence * >(this) ); + m_aModifyListeners.disposeAndClear( aEvtObj ); + m_aEvtListeners.disposeAndClear( aEvtObj ); + } +} + +void SAL_CALL SwChartDataSequence::addEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEvtListeners.addInterface( rxListener ); +} + +void SAL_CALL SwChartDataSequence::removeEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEvtListeners.removeInterface( 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 + FillRangeDescriptor( aDesc, aStartBox + ":" + aEndBox ); + + 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_aEventListeners( GetChartMutex() ), + m_aModifyListeners( GetChartMutex() ) +{ + 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( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY ); + uno::Reference< lang::XEventListener > xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY ); + + // 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, dynamic_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, dynamic_cast< XModifyBroadcaster * >(this) ); + } +} + +uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( ) +{ + SolarMutexGuard aGuard; + if (m_bDisposed) + throw lang::DisposedException(); + + uno::Reference< util::XCloneable > xRes; + + uno::Reference< util::XCloneable > xDataCloneable( m_xData, uno::UNO_QUERY ); + uno::Reference< util::XCloneable > xLabelsCloneable( m_xLabels, uno::UNO_QUERY ); + 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 ); + } + xRes = pRes; + return xRes; +} + +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 ) +{ + osl::MutexGuard 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()) + dispose(); +} + +void SAL_CALL SwChartLabeledDataSequence::modified( + const lang::EventObject& rEvent ) +{ + if (rEvent.Source == m_xData || rEvent.Source == m_xLabels) + { + LaunchModifiedEvent( m_aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) ); + } +} + +void SAL_CALL SwChartLabeledDataSequence::addModifyListener( + const uno::Reference< util::XModifyListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aModifyListeners.addInterface( rxListener ); +} + +void SAL_CALL SwChartLabeledDataSequence::removeModifyListener( + const uno::Reference< util::XModifyListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aModifyListeners.removeInterface( rxListener ); +} + +void SAL_CALL SwChartLabeledDataSequence::dispose( ) +{ + bool bMustDispose( false ); + { + osl::MutexGuard 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( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) ); + m_aModifyListeners.disposeAndClear( aEvtObj ); + m_aEventListeners.disposeAndClear( aEvtObj ); + } +} + +void SAL_CALL SwChartLabeledDataSequence::addEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEventListeners.addInterface( rxListener ); +} + +void SAL_CALL SwChartLabeledDataSequence::removeEventListener( + const uno::Reference< lang::XEventListener >& rxListener ) +{ + osl::MutexGuard aGuard( GetChartMutex() ); + if (!m_bDisposed && rxListener.is()) + m_aEventListeners.removeInterface( 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..9965a5b9c --- /dev/null +++ b/sw/source/core/unocore/unocoll.cxx @@ -0,0 +1,1941 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svtools/unoimap.hxx> +#include <svtools/unoevent.hxx> +#include <unotbl.hxx> +#include <unostyle.hxx> +#include <unofield.hxx> +#include <unoidx.hxx> +#include <unoframe.hxx> +#include <textboxhelper.hxx> +#include <unofootnote.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 <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 ); + 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::makeAny( 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( 2 ); + aArgs[0] <<= uno::Reference< uno::XInterface >(); + aArgs[1] <<= 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::makeAny( 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 } +}; + +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(const OUString& rServiceName) +{ + for(const ProvNamesId_Type & i : aProvNamesId) + { + if (rServiceName.equalsAscii(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 + { + SwVbaObjectForCodeNameProvider* pObjProv = + new SwVbaObjectForCodeNameProvider(rDoc.GetDocShell()); + xRet = static_cast<cppu::OWeakObject*>(pObjProv); + } +#endif + break; + case SwServiceType::VbaCodeNameProvider : +#if HAVE_FEATURE_SCRIPTING + { + if (rDoc.GetDocShell() && ooo::vba::isAlienWordDoc(*rDoc.GetDocShell())) + { + SwVbaCodeNameProvider* pObjProv = new SwVbaCodeNameProvider(rDoc.GetDocShell()); + xRet = static_cast<cppu::OWeakObject*>(pObjProv); + } + } +#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(1); + aArgs[ 0 ] <<= 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 = static_cast<cppu::OWeakObject*>(new SwXTextColumns); + 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; + 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::makeAny(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::makeAny(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::makeAny(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* const pDoc); + + //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* const pDoc) + : m_aFrames() +{ + SolarMutexGuard aGuard; + const SwFrameFormats* const pFormats = pDoc->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 = pDoc->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 makeAny(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(static_cast<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/unocrsr.cxx b/sw/source/core/unocore/unocrsr.cxx new file mode 100644 index 000000000..76bdb939b --- /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* pDoc = GetDoc(); + if( !pDoc->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* pDoc = GetDoc(); + SwNodeIndex aOldIdx( *pDoc->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() && + ( !pDoc->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..a82e7a1c2 --- /dev/null +++ b/sw/source/core/unocore/unocrsrhelper.cxx @@ -0,0 +1,1507 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <docary.hxx> +#include <swtable.hxx> +#include <tox.hxx> +#include <doctxm.hxx> +#include <fchrfmt.hxx> +#include <editeng/editids.hrc> +#include <editeng/flstitem.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( + ::sw::UnoTunnelGetImplementation<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(::sw::UnoTunnelGetImplementation<SwXShape>(xTunnel)); + if (pShape) + { + uno::Reference<uno::XAggregation> const xAgg( + pShape->GetAggregationInterface()); + if (xAgg.is()) + { + SvxShape *const pSvxShape( + ::sw::UnoTunnelGetImplementation<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( + ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xTunnel)); + if (pCursor) + { + if (pCursor->GetDoc() == &rTargetDoc) + { + o_rpPaM = lcl_createPamCopy(*pCursor->GetPaM()); + } + return; + } + + SwXTextRanges* const pRanges( + ::sw::UnoTunnelGetImplementation<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( + ::sw::UnoTunnelGetImplementation<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( + ::sw::UnoTunnelGetImplementation<SwXTextTable>(xTunnel)); + if (pTextTable) + { + SwFrameFormat *const pFrameFormat(pTextTable->GetFrameFormat()); + if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc) + { + o_rTableName = pFrameFormat->GetName(); + } + return; + } + + SwXCell *const pCell( + ::sw::UnoTunnelGetImplementation<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( + ::sw::UnoTunnelGetImplementation<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(); + } + SfxItemSet const& rSet(*pFormat->GetStyleHandle()); + 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& rEntry : rMap.getPropertyEntries()) + { + if (rPropSet.getPropertyState(rEntry, rSet) == PropertyState_DIRECT_VALUE) + { + Any value; + rPropSet.getPropertyValue(rEntry, rSet, value); + props.emplace_back(rEntry.sName, value); + } + } + return uno::makeAny(comphelper::containerToSequence(props)); +} + +// Read the special properties of the cursor +bool getCursorPropertyValue(const SfxItemPropertySimpleEntry& 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())) + { + if( pAny ) + { + 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.GetPoint()->nContent.GetIndex(); + sal_Int32 nPaMEnd = rPam.GetMark() ? rPam.GetMark()->nContent.GetIndex() : nPaMStart; + if(nPaMStart > nPaMEnd) + { + std::swap(nPaMStart, nPaMEnd); + } + 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::getUnoTunnelImplementation<SwXNumberingRules>(xIndexReplace); + if(pSwNum) + { + SwDoc* pDoc = 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 = pDoc->GetCharFormats()->size(); + SwCharFormat* pCharFormat = nullptr; + for(size_t nCharFormat = 0; nCharFormat < nChCount; ++nCharFormat) + { + SwCharFormat& rChFormat = *((*(pDoc->GetCharFormats()))[nCharFormat]); + if(rChFormat.GetName() == pNewCharStyles[i]) + { + pCharFormat = &rChFormat; + break; + } + } + + if(!pCharFormat) + { + SfxStyleSheetBasePool* pPool = pDoc->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* >(pDoc->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(pDoc); + + if( rPam.GetNext() != &rPam ) // Multiple selection? + { + pDoc->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 + pDoc->SetNumRule( aRangeArr.SetPam( n, aPam ), aRule, false ); + } + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + } + else + { + // no start of a new list + pDoc->SetNumRule( rPam, aRule, false ); + } + + } + else if(!pSwNum->GetCreatedNumRuleName().isEmpty()) + { + UnoActionContext aAction( pDoc ); + SwNumRule* pRule = pDoc->FindNumRulePtr( pSwNum->GetCreatedNumRuleName() ); + if ( !pRule ) + throw RuntimeException(); + // no start of a new list + pDoc->SetNumRule( rPam, *pRule, false ); + } + else + { + // #i103817# + // outline numbering + UnoActionContext aAction(pDoc); + SwNumRule* pRule = pDoc->GetOutlineNumRule(); + if(!pRule) + throw RuntimeException(); + pDoc->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 SfxItemPropertySimpleEntry& rEntry, SwPaM& rPam) +{ + SwDoc* pDoc = 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(pDoc); + + if( rPam.GetNext() != &rPam ) // Multiple selection? + { + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + SwPamRanges aRangeArr( rPam ); + SwPaM aPam( *rPam.GetPoint() ); + for( size_t n = 0; n < aRangeArr.Count(); ++n ) + pDoc->SetNodeNumStart( *aRangeArr.SetPam( n, aPam ).GetPoint(), 1 ); + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + } + else + pDoc->SetNodeNumStart( *rPam.GetPoint(), 0 ); + } + + break; + case FN_UNO_NUM_LEVEL : + break; + case FN_UNO_NUM_RULES: + break; + case FN_UNO_CHARFMT_SEQUENCE: + { + std::set<sal_uInt16> aWhichIds; + aWhichIds.insert( RES_TXTATR_CHARFMT); + pDoc->ResetAttrs(rPam, true, aWhichIds); + } + break; + } +} + +void InsertFile(SwUnoCursor* pUnoCursor, const OUString& rURL, + const uno::Sequence< beans::PropertyValue >& rOptions) +{ + std::unique_ptr<SfxMedium> pMed; + SwDoc* pDoc = pUnoCursor->GetDoc(); + SwDocShell* pDocSh = pDoc->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( 2 ); + aArgs[0] <<= xInputStream; + aArgs[1] <<= 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? + { + 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 ) + { + + UnoActionContext aContext(pDoc); + + if(pUnoCursor->HasMark()) + pDoc->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, + const OUString& rRedlineType, + const uno::Sequence< beans::PropertyValue >& rRedlineProperties ) +{ + IDocumentRedlineAccess* pRedlineAccess = &rPaM.GetDoc()->getIDocumentRedlineAccess(); + + RedlineType eType; + if ( rRedlineType == "Insert" ) + eType = RedlineType::Insert; + else if ( rRedlineType == "Delete" ) + eType = RedlineType::Delete; + else if ( rRedlineType == "Format" ) + eType = RedlineType::Format; + else if ( rRedlineType == "TextTable" ) + eType = RedlineType::Table; + else if ( rRedlineType == "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 = 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( 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 *const pDoc = rPaM.GetDoc(); + + // Build set of attributes we want to fetch + std::vector<sal_uInt16> aWhichPairs; + std::vector<SfxItemPropertySimpleEntry 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; + SfxItemPropertySimpleEntry const* pEntry = rPropSet.getPropertyMap().getByName(rPropertyName); + + if (!pEntry) + { + // unknown property + break; + } + else if (pEntry->nFlags & beans::PropertyAttribute::READONLY) + { + break; + } + else if (rPropertyName == "NumberingRules") + { + aWhichPairs.push_back(RES_PARATR_NUMRULE); + aWhichPairs.push_back(RES_PARATR_NUMRULE); + nNumId = aEntries.size(); + } + else + { + // FIXME: we should have some nice way of merging ranges surely ? + aWhichPairs.push_back(pEntry->nWID); + aWhichPairs.push_back(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; + aWhichPairs.push_back(0); // terminate + SfxItemSet aItemSet(pDoc->GetAttrPool(), aWhichPairs.data()); + + for (size_t i = 0; i < aEntries.size(); ++i) + { + SfxItemPropertySimpleEntry const*const pEntry = aEntries[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 = pDoc->FindNumRulePtr( + xNumberingRules->getName()); + if (pRule) + pRule->SetUsedByRedline(true); + } + } + else + { + rPropSet.setPropertyValue(*pEntry, rValue, aItemSet); + if (i == nStyleId) + rValue >>= sParaStyleName; + } + } + + if (eType == RedlineType::ParagraphFormat && sParaStyleName.isEmpty()) + nStylePoolId = RES_POOLCOLL_STANDARD; + + xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( sParaStyleName, nStylePoolId, &aItemSet )); + } + else if (eType == RedlineType::ParagraphFormat) + xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( "", RES_POOLCOLL_STANDARD, nullptr )); + } + } + + SwRangeRedline* pRedline = new SwRangeRedline( aRedlineData, rPaM ); + RedlineFlags nPrevMode = pRedlineAccess->GetRedlineFlags( ); + // xRedlineExtraData is copied here + pRedline->SetExtraData( xRedlineExtraData.get() ); + + pRedlineAccess->SetRedlineFlags_intern(RedlineFlags::On); + auto const result(pRedlineAccess->AppendRedline(pRedline, false)); + pRedlineAccess->SetRedlineFlags_intern( nPrevMode ); + if (IDocumentRedlineAccess::AppendResult::IGNORED == result) + throw lang::IllegalArgumentException(); +} + +void makeTableRowRedline( SwTableLine& rTableLine, + const OUString& rRedlineType, + const uno::Sequence< beans::PropertyValue >& rRedlineProperties ) +{ + IDocumentRedlineAccess* pRedlineAccess = &rTableLine.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess(); + + RedlineType eType; + if ( rRedlineType == "TableRowInsert" ) + { + eType = RedlineType::TableRowInsert; + } + else if ( rRedlineType == "TableRowDelete" ) + { + eType = RedlineType::TableRowDelete; + } + 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 ) ) ); + } + + 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, + const OUString& rRedlineType, + const uno::Sequence< beans::PropertyValue >& rRedlineProperties ) +{ + IDocumentRedlineAccess* pRedlineAccess = &rTableBox.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess(); + + RedlineType eType; + if ( rRedlineType == "TableCellInsert" ) + { + eType = RedlineType::TableCellInsert; + } + else if ( rRedlineType == "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..3c1949063 --- /dev/null +++ b/sw/source/core/unocore/unodraw.cxx @@ -0,0 +1,2854 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <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/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 bOpaque; + uno::Reference< text::XTextRange > 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 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. + , bOpaque(false) + // #i26791# + , m_pFollowTextFlow( new SwFormatFollowTextFlow(false) ) + // #i28701# #i35017# + , m_pWrapInfluenceOnObjPos( new SwFormatWrapInfluenceOnObjPos( + text::WrapInfluenceOnPosition::ONCE_CONCURRENT) ) + // #i28749# + , mnPositionLayoutDir(text::PositionLayoutDir::PositionInLayoutDirOfAnchor) + , 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 xTextRange; + } + bool IsOpaque() const + { + return bOpaque; + } + const bool& GetOpaque() const + { + return 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){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 ), pPageView(nullptr) +{ +} + +SwFmDrawPage::~SwFmDrawPage() throw () +{ + 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(!pPageView) + pPageView = mpView->ShowSdrPage( mpPage ); + return pPageView; +} + +void SwFmDrawPage::RemovePageView() +{ + if(pPageView && mpView) + mpView->HideSdrPage(); + 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 + SwXShape* pShape = nullptr; + if(xShapeTunnel.is()) + pShape = reinterpret_cast< SwXShape * >( + sal::static_int_cast< sal_IntPtr >( xShapeTunnel->getSomething(SwXShape::getUnoTunnelId()) )); + 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); + uno::Reference<beans::XPropertySet> xPrSet = pShape; + xRet.set(xPrSet, uno::UNO_QUERY); + } + const_cast<std::vector<SwXShape*>*>(&m_vShapes)->push_back(pShape); + 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) + : m_aShapes() +{ + 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::makeAny(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) : + pDoc(pDc), + pDrawPage(nullptr) +{ +} + +SwXDrawPage::~SwXDrawPage() +{ + if(xPageAgg.is()) + { + uno::Reference< uno::XInterface > xInt; + xPageAgg->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(!pDoc) + throw uno::RuntimeException(); + if(!pDoc->getIDocumentDrawModelAccess().GetDrawModel()) + return 0; + else + { + GetSvxPage(); + return SwTextBoxHelper::getCount(pDrawPage->GetSdrPage()); + } +} + +uno::Any SwXDrawPage::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + if(!pDoc) + throw uno::RuntimeException(); + if(!pDoc->getIDocumentDrawModelAccess().GetDrawModel()) + throw lang::IndexOutOfBoundsException(); + + GetSvxPage(); + return SwTextBoxHelper::getByIndex(pDrawPage->GetSdrPage(), nIndex); +} + +uno::Type SwXDrawPage::getElementType() +{ + return cppu::UnoType<drawing::XShape>::get(); +} + +sal_Bool SwXDrawPage::hasElements() +{ + SolarMutexGuard aGuard; + if(!pDoc) + throw uno::RuntimeException(); + if(!pDoc->getIDocumentDrawModelAccess().GetDrawModel()) + return false; + else + return GetSvxPage()->hasElements(); +} + +void SwXDrawPage::add(const uno::Reference< drawing::XShape > & xShape) +{ + SolarMutexGuard aGuard; + if(!pDoc) + throw uno::RuntimeException(); + uno::Reference< lang::XUnoTunnel > xShapeTunnel(xShape, uno::UNO_QUERY); + SwXShape* pShape = nullptr; + SvxShape* pSvxShape = nullptr; + if(xShapeTunnel.is()) + { + pShape = reinterpret_cast< SwXShape * >( + sal::static_int_cast< sal_IntPtr >( xShapeTunnel->getSomething(SwXShape::getUnoTunnelId()) )); + pSvxShape = reinterpret_cast< SvxShape * >( + sal::static_int_cast< sal_IntPtr >( xShapeTunnel->getSomething(SvxShape::getUnoTunnelId()) )); + } + + // 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(); + + SfxItemSet aSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END-1>{} ); + 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 = convertMm100ToTwip(aMM100Pos.X); + 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 = convertMm100ToTwip(aMM100Pos.Y); + 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 ? pDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId() : pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId() ); + else + pObj->SetLayer(pDoc->getIDocumentDrawModelAccess().GetInvisibleControlsId()); + + std::unique_ptr<SwPaM> pPam(new SwPaM(pDoc->GetNodes().GetEndOfContent())); + std::unique_ptr<SwUnoInternalPaM> pInternalPam; + uno::Reference< text::XTextRange > xRg; + if( pDesc && (xRg = pDesc->GetTextRange()).is() ) + { + pInternalPam.reset(new SwUnoInternalPaM(*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()) + { + aAnchor.SetAnchor(pInternalPam->Start()); + } + + } + else if ((aAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && pDoc->getIDocumentLayoutAccess().GetCurrentLayout()) + { + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + Point aTmp(convertMm100ToTwip(aMM100Pos.X), convertMm100ToTwip(aMM100Pos.Y)); + 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.get(); + UnoActionContext aAction(pDoc); + pDoc->getIDocumentContentOperations().InsertDrawObj( *pTemp, *pObj, aSet ); + + if (pSvxShape->GetSdrObject()->GetName().isEmpty()) + { + pSvxShape->GetSdrObject()->SetName(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(!pDoc) + throw uno::RuntimeException(); + 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(!pDoc || !xShapes.is()) + throw uno::RuntimeException(); + uno::Reference< drawing::XShapeGroup > xRet; + if(xPageAgg.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 uno::RuntimeException(); // FlyInCnt! + } + } + + UnoActionContext aContext(pDoc); + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + + SwDrawContact* pContact = pDoc->GroupSelection( *pPage->GetDrawView() ); + pDoc->ChgAnchor( + pPage->GetDrawView()->GetMarkedObjectList(), + RndStdIds::FLY_AT_PARA, + true, false ); + + pPage->GetDrawView()->UnmarkAll(); + if(pContact) + xRet = SwFmDrawPage::GetShapeGroup( pContact->GetMaster() ); + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + } + pPage->RemovePageView(); + } + } + return xRet; +} + +void SwXDrawPage::ungroup(const uno::Reference< drawing::XShapeGroup > & rShapeGroup) +{ + SolarMutexGuard aGuard; + if(!pDoc) + throw uno::RuntimeException(); + if(xPageAgg.is()) + { + SwFmDrawPage* pPage = GetSvxPage(); + if(pPage) //TODO: can this be Null? + { + pPage->PreUnGroup(rShapeGroup); + UnoActionContext aContext(pDoc); + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + + pDoc->UnGroupSelection( *pPage->GetDrawView() ); + pDoc->ChgAnchor( pPage->GetDrawView()->GetMarkedObjectList(), + RndStdIds::FLY_AT_PARA, + true, false ); + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + pPage->RemovePageView(); + } + } +} + +SwFmDrawPage* SwXDrawPage::GetSvxPage() +{ + if(!xPageAgg.is() && pDoc) + { + SolarMutexGuard aGuard; + // #i52858# + SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + SdrPage* pPage = pModel->GetPage( 0 ); + + { + // We need a Ref to the object during queryInterface or else + // it will be deleted + pDrawPage = new SwFmDrawPage(pPage); + uno::Reference< drawing::XDrawPage > xPage = pDrawPage; + uno::Any aAgg = xPage->queryInterface(cppu::UnoType<uno::XAggregation>::get()); + aAgg >>= xPageAgg; + } + if( xPageAgg.is() ) + xPageAgg->setDelegator( static_cast<cppu::OWeakObject*>(this) ); + } + return pDrawPage; +} + +/** + * Renamed and outlined to detect where it's called + */ +void SwXDrawPage::InvalidateSwDoc() +{ + pDoc = nullptr; +} + +namespace +{ + class theSwXShapeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXShapeUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXShape::getUnoTunnelId() +{ + return theSwXShapeUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXShape::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXShape>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + + if( xShapeAgg.is() ) + { + const uno::Type& rTunnelType = cppu::UnoType<lang::XUnoTunnel>::get(); + uno::Any aAgg = 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_shared<svx::PropertyValueProvider>( _rShape, "AnchorType" ); + _rObj.getShapePropertyChangeNotifier().registerProvider( svx::ShapeProperty::TextDocAnchor, 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)) + , 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 >>= xShapeAgg; + // #i31698# + if ( xShapeAgg.is() ) + { + 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( xShapeAgg.is() ) + xShapeAgg->setDelegator( static_cast<cppu::OWeakObject*>(this) ); + osl_atomic_decrement(&m_refCount); + + SvxShape* pShape = comphelper::getUnoTunnelImplementation<SvxShape>(xShapeAgg); + + SdrObject* pObj = pShape ? pShape->GetSdrObject() : nullptr; + if(pObj) + { + auto pFormat = ::FindFrameFormat( pObj ); + if(pFormat) + SetFrameFormat(pFormat); + + lcl_addShapePropertyEventFactories( *pObj, *this ); + pImpl->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::getUnoTunnelImplementation<SwXShape>(pCurrent->getWeakUnoShape()); + if ( pSwShape ) + { + if ( pSwShape->m_bDescriptor ) + { + auto pFormat = ::FindFrameFormat( pCurrent ); + if ( pFormat ) + pSwShape->SetFrameFormat(pFormat); + pSwShape->m_bDescriptor = false; + } + + if ( !pSwShape->pImpl->bInitializedPropertyNotifier ) + { + lcl_addShapePropertyEventFactories( *pCurrent, *pSwShape ); + pSwShape->pImpl->bInitializedPropertyNotifier = true; + } + } + } +} + +SwXShape::~SwXShape() +{ + SolarMutexGuard aGuard; + if (xShapeAgg.is()) + { + uno::Reference< uno::XInterface > xRef; + xShapeAgg->setDelegator(xRef); + } + 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 = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType); + 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() && xShapeAgg.is()) + { + if(aType == cppu::UnoType<XShape>::get()) + aRet <<= uno::Reference<XShape>(this); + else + aRet = xShapeAgg->queryAggregation(aType); + } + return aRet; +} + +uno::Sequence< uno::Type > SwXShape::getTypes( ) +{ + uno::Sequence< uno::Type > aRet = SwXShapeBaseClass::getTypes(); + if(xShapeAgg.is()) + { + uno::Any aProv = 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; + uno::Reference< beans::XPropertySetInfo > aRet; + if(xShapeAgg.is()) + { + const uno::Type& rPropSetType = cppu::UnoType<beans::XPropertySet>::get(); + uno::Any aPSet = 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(); + aRet = new SfxExtItemPropertySetInfo( m_pPropertyMapEntries, aPropSeq ); + } + } + if(!aRet.is()) + aRet = m_pPropSet->getPropertySetInfo(); + return aRet; +} + +void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue) +{ + SolarMutexGuard aGuard; + SwFrameFormat* pFormat = GetFrameFormat(); + const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if(xShapeAgg.is()) + { + 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::getUnoTunnelImplementation<SwXFrame>(xFrame); + if(pFrame && pFrame->GetFrameFormat() && + pFrame->GetFrameFormat()->GetDoc() == pDoc) + { + UnoActionContext aCtx(pDoc); + SfxItemSet aItemSet( pDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{} ); + 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 ); + } + else + { + aAnchor.SetAnchor( pInternalPam->GetPoint() ); + aSet.Put(aAnchor); + pFormat->SetFormatAttr(aSet); + } + } + else if (pEntry->nWID == FN_TEXT_BOX) + { + bool bValue(false); + aValue >>= bValue; + if (bValue) + SwTextBoxHelper::create(pFormat); + else + SwTextBoxHelper::destroy(pFormat); + + } + 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); + } + // #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); + } + 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); + } + else + { + SfxPoolItem* pItem = nullptr; + switch(pEntry->nWID) + { + case RES_ANCHOR: + pItem = pImpl->GetAnchor(true); + break; + case RES_HORI_ORIENT: + pItem = pImpl->GetHOrient(true); + break; + case RES_VERT_ORIENT: + pItem = pImpl->GetVOrient(true); + break; + case RES_LR_SPACE: + pItem = pImpl->GetLRSpace(true); + break; + case RES_UL_SPACE: + pItem = pImpl->GetULSpace(true); + break; + case RES_SURROUND: + pItem = pImpl->GetSurround(true); + break; + case FN_TEXT_RANGE: + if(auto tr = o3tl::tryAccess< + uno::Reference<text::XTextRange>>(aValue)) + { + uno::Reference< text::XTextRange > & rRange = pImpl->GetTextRange(); + rRange = *tr; + } + break; + case RES_OPAQUE : + pImpl->SetOpaque(*o3tl::doAccess<bool>(aValue)); + break; + // #i26791# + case RES_FOLLOW_TEXT_FLOW: + { + pItem = pImpl->GetFollowTextFlow( true ); + } + break; + // #i28701# + case RES_WRAP_INFLUENCE_ON_OBJPOS: + { + pItem = pImpl->GetWrapInfluenceOnObjPos( true ); + } + break; + // #i28749# + case FN_SHAPE_POSITION_LAYOUT_DIR : + { + sal_Int16 nPositionLayoutDir = 0; + aValue >>= nPositionLayoutDir; + pImpl->SetPositionLayoutDir( nPositionLayoutDir ); + } + break; + } + if(pItem) + pItem->PutValue(aValue, pEntry->nMemberId); + } + } + else + { + const uno::Type& rPSetType = + cppu::UnoType<beans::XPropertySet>::get(); + uno::Any aPSet = 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); + } + + // #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(xShapeAgg.is()) + { + const SfxItemPropertySimpleEntry* 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) + { + bool bValue = SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT); + aRet <<= bValue; + } + 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 = pImpl->GetAnchor(); + break; + case RES_HORI_ORIENT: + pItem = pImpl->GetHOrient(); + break; + case RES_VERT_ORIENT: + pItem = pImpl->GetVOrient(); + break; + case RES_LR_SPACE: + pItem = pImpl->GetLRSpace(); + break; + case RES_UL_SPACE: + pItem = pImpl->GetULSpace(); + break; + case RES_SURROUND: + pItem = pImpl->GetSurround(); + break; + case FN_TEXT_RANGE : + aRet <<= pImpl->GetTextRange(); + break; + case RES_OPAQUE : + aRet <<= pImpl->GetOpaque(); + break; + case FN_ANCHOR_POSITION : + { + aRet <<= awt::Point(); + } + break; + // #i26791# + case RES_FOLLOW_TEXT_FLOW : + { + pItem = pImpl->GetFollowTextFlow(); + } + break; + // #i28701# + case RES_WRAP_INFLUENCE_ON_OBJPOS: + { + pItem = 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 <<= 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 = 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(!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 SfxItemPropertySimpleEntry* 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)) + 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 = pImpl->GetAnchor(); + break; + case RES_HORI_ORIENT: + pItem = pImpl->GetHOrient(); + break; + case RES_VERT_ORIENT: + pItem = pImpl->GetVOrient(); + break; + case RES_LR_SPACE: + pItem = pImpl->GetLRSpace(); + break; + case RES_UL_SPACE: + pItem = pImpl->GetULSpace(); + break; + case RES_SURROUND: + pItem = pImpl->GetSurround(); + break; + // #i28701# + case RES_WRAP_INFLUENCE_ON_OBJPOS: + { + pItem = 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 = 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(!xShapeAgg.is()) + throw uno::RuntimeException(); + + const SfxItemPropertySimpleEntry* 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: pImpl->RemoveAnchor(); break; + case RES_HORI_ORIENT: pImpl->RemoveHOrient(); break; + case RES_VERT_ORIENT: pImpl->RemoveVOrient(); break; + case RES_LR_SPACE: pImpl->RemoveLRSpace(); break; + case RES_UL_SPACE: pImpl->RemoveULSpace(); break; + case RES_SURROUND: pImpl->RemoveSurround();break; + case RES_OPAQUE : pImpl->SetOpaque(false); break; + case FN_TEXT_RANGE : + break; + // #i26791# + case RES_FOLLOW_TEXT_FLOW: + { + pImpl->RemoveFollowTextFlow(); + } + break; + // #i28701# + case RES_WRAP_INFLUENCE_ON_OBJPOS: + { + pImpl->RemoveWrapInfluenceOnObjPos(); + } + break; + } + } + } + else + { + const uno::Type& rPStateType = cppu::UnoType<XPropertyState>::get(); + uno::Any aPState = 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(!xShapeAgg.is()) + throw uno::RuntimeException(); + + const SfxItemPropertySimpleEntry* 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 = 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 ( !xShapeAgg.is() ) + throw uno::RuntimeException("no shape aggregate", *this ); + + // must be handled by the aggregate + uno::Reference< beans::XPropertySet > xShapeProps; + if ( 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 ( !xShapeAgg.is() ) + throw uno::RuntimeException("no shape aggregate", *this ); + + // must be handled by the aggregate + uno::Reference< beans::XPropertySet > xShapeProps; + if ( 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()) + { + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + SwXTextPortion* pPortion = nullptr; + SwXText* pText = nullptr; + SwXParagraph* pParagraph = nullptr; + + pRange = reinterpret_cast< SwXTextRange * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXTextRange::getUnoTunnelId()) )); + pText = reinterpret_cast< SwXText * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXText::getUnoTunnelId()) )); + pCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( OTextCursorHelper::getUnoTunnelId()) )); + pPortion = reinterpret_cast< SwXTextPortion * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXTextPortion::getUnoTunnelId()) )); + pParagraph = reinterpret_cast< SwXParagraph * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXParagraph::getUnoTunnelId( ) ) ) ); + + if (pRange) + pDoc = &pRange->GetDoc(); + else if (pText) + pDoc = pText->GetDoc(); + else if (pCursor) + pDoc = pCursor->GetDoc(); + else if (pPortion) + pDoc = pPortion->GetCursor().GetDoc(); + else if (pParagraph && pParagraph->GetTextNode()) + pDoc = pParagraph->GetTextNode()->GetDoc(); + + } + + if(!pDoc) + throw uno::RuntimeException(); + if (const SwDocShell* pDocSh = pDoc->GetDocShell()) + { + 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 = 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(xShapeAgg.is()) + { + uno::Any aAgg(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<OUStringLiteral>{ "com.sun.star.drawing.Shape" }); +} + +SvxShape* SwXShape::GetSvxShape() +{ + if(xShapeAgg.is()) + return comphelper::getUnoTunnelImplementation<SvxShape>(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::getUnoTunnelImplementation<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::makeAny(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 + const OUString aHoriPosPropStr("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 + const OUString aHoriOrientPropStr("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 + const OUString aVertPosPropStr("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 + const OUString aVertOrientPropStr("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 : aConvertedPath.Coordinates) + { + for(awt::Point& rPoint : 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(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( ) throw() +{ + SwXShape::acquire(); +} + +void SwXGroupShape::release( ) throw() +{ + 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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XShapes>::get(); + uno::Any aAgg = 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 = nullptr; + if(xShape.is()) + pSwShape = reinterpret_cast< SwXShape * >( + sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething(SwXShape::getUnoTunnelId()) )); + if(pSwShape && pSwShape->m_bDescriptor) + { + SvxShape* pAddShape = reinterpret_cast< SvxShape * >( + sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething(SvxShape::getUnoTunnelId()) )); + 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->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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XShapes>::get(); + uno::Any aAgg = 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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XIndexAccess>::get(); + uno::Any aAgg = 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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XIndexAccess>::get(); + uno::Any aAgg = 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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XIndexAccess>::get(); + uno::Any aAgg = 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( xShapeAgg.is() ) + { + const uno::Type& rType = cppu::UnoType<XIndexAccess>::get(); + uno::Any aAgg = 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..7ca58df40 --- /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), + rFrame(rFrameRef) +{ +} + +SwFrameEventDescriptor::SwFrameEventDescriptor( + SwXTextGraphicObject& rGraphicRef ) : + SvEventDescriptor(static_cast<text::XTextContent&>(rGraphicRef), aGraphicEvents), + rFrame(static_cast<SwXFrame&>(rGraphicRef)) +{ +} + +SwFrameEventDescriptor::SwFrameEventDescriptor( + SwXTextEmbeddedObject& rObjectRef ) : + SvEventDescriptor(static_cast<text::XTextContent&>(rObjectRef), aOLEEvents), + rFrame(static_cast<SwXFrame&>(rObjectRef)) +{ +} + +SwFrameEventDescriptor::~SwFrameEventDescriptor() +{ +} + +void SwFrameEventDescriptor::setMacroItem(const SvxMacroItem& rItem) +{ + rFrame.GetFrameFormat()->SetFormatAttr(rItem); +} + +const SvxMacroItem& SwFrameEventDescriptor::getMacroItem() +{ + return 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); +} + +static 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..d6c9e5a92 --- /dev/null +++ b/sw/source/core/unocore/unofield.cxx @@ -0,0 +1,3034 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swtypes.hxx> +#include <cmdid.h> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentState.hxx> +#include <hints.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/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 <osl/mutex.hxx> +#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) +#define COM_TEXT_FLDMASTER_CC "com.sun.star.text.fieldmaster." + +// note: this thing is indexed as an array, so do not insert/remove entries! +static 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; +}; + +} + +static 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(const OUString& rPropertyName) +{ + if (rPropertyName == UNO_NAME_DDE_COMMAND_TYPE) + return 0; + + if (rPropertyName == UNO_NAME_DDE_COMMAND_FILE) + return 1; + + if (rPropertyName == UNO_NAME_DDE_COMMAND_ELEMENT) + return 2; + + if (rPropertyName == UNO_NAME_IS_AUTOMATIC_UPDATE) + return 3; + + return SAL_MAX_INT32; +} + +static sal_uInt16 GetFieldTypeMId( const OUString& rProperty, const SwFieldType& rTyp ) +{ + sal_uInt16 nId = lcl_GetPropMapIdForFieldType( rTyp.Which() ); + const SfxItemPropertySet* pSet = aSwMapProvider.GetPropertySet( nId ); + if( !pSet ) + nId = USHRT_MAX; + else + { + const SfxItemPropertySimpleEntry* 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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 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_EventListeners(m_Mutex) + , 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_EventListeners(m_Mutex) + , 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; +}; + +namespace +{ + class theSwXFieldMasterUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXFieldMasterUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXFieldMaster::getUnoTunnelId() +{ + return theSwXFieldMasterUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXFieldMaster::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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.begin(), + [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) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.addInterface(xListener); +} + +void SAL_CALL SwXFieldMaster::removeEventListener( + const uno::Reference<lang::XEventListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.removeInterface(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); + m_EventListeners.disposeAndClear(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.copy(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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + SwFieldType* m_pFieldType; + SwFormatField* m_pFormatField; + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 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_EventListeners(m_Mutex) + , 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; +}; + +namespace +{ + class theSwXTextFieldUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextFieldUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextField::getUnoTunnelId() +{ + return theSwXTextFieldUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXTextField::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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() + ? reinterpret_cast<SwXTextField*>( + sal::static_int_cast<sal_IntPtr>( + uno::Reference<lang::XUnoTunnel>(xField, uno::UNO_QUERY_THROW) + ->getSomething(getUnoTunnelId()))) + : 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 (xField.is()) + { + 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 = reinterpret_cast< SwXFieldMaster * >( + sal::static_int_cast< sal_IntPtr >( xMasterTunnel->getSomething( SwXFieldMaster::getUnoTunnelId()) )); + + 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(); + 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 = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = reinterpret_cast< SwXTextRange * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXTextRange::getUnoTunnelId()) )); + pCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( OTextCursorHelper::getUnoTunnelId()) )); + } + + 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: + { + 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)), + OUString())); + 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 + + 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->ClearFieldType(); + 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 ) + { + SwUnoInternalPaM aIntPam( *m_pImpl->m_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( m_pImpl->m_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() ); + m_pImpl->m_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(); + m_pImpl->m_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()), m_pImpl->m_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) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.addInterface(xListener); +} + +void SAL_CALL SwXTextField::removeEventListener( + const uno::Reference<lang::XEventListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.removeInterface(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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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(1); + text::TextContentAnchorType* pArray = aTypes.getArray(); + pArray[0] = 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.get()); + 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()->ModifyNotification(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), ".textfield.docinfo." ); + nIdx = sServiceNameCC.indexOf( aOldNamePart2 ); + if (nIdx >= 0) + sServiceNameCC = sServiceNameCC.replaceAt( nIdx, strlen(aOldNamePart2), ".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); + m_EventListeners.disposeAndClear(ev); +} + +void SwXTextField::Impl::Notify(const SfxHint& rHint) +{ + + if(rHint.GetId() == SfxHintId::Dying) + Invalidate(); + else if (auto pLegacyHint = dynamic_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(RTL_CONSTASCII_LENGTH(COM_TEXT_FLDMASTER_CC)); + + 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::makeAny(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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + ::comphelper::OInterfaceContainerHelper2 m_RefreshListeners; + + Impl() : m_RefreshListeners(m_Mutex) { } +}; + +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)); + m_pImpl->m_RefreshListeners.disposeAndClear(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)); + m_pImpl->m_RefreshListeners.notifyEach( + & util::XRefreshListener::refreshed, event); +} + +void SAL_CALL SwXTextFieldTypes::addRefreshListener( + const uno::Reference<util::XRefreshListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_RefreshListeners.addInterface(xListener); +} + +void SAL_CALL SwXTextFieldTypes::removeRefreshListener( + const uno::Reference<util::XRefreshListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_RefreshListeners.removeInterface(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 ); + } +} + +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..15ffb4441 --- /dev/null +++ b/sw/source/core/unocore/unoflatpara.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 <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 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 }, + { OUString(), -1, css::uno::Type(), 0, 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::makeAny( comphelper::containerToSequence( GetConversionMap().getFieldPositions() ) ); + } + else if (rPropertyName == "FootnotePositions") + { + return uno::makeAny( 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()) + { + 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>(); +} + +namespace +{ + class theSwXFlatParagraphUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXFlatParagraphUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 >& +SwXFlatParagraph::getUnoTunnelId() +{ + return theSwXFlatParagraphUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXFlatParagraph::getSomething( + const uno::Sequence< sal_Int8 >& rId) +{ + return sw::UnoTunnelImpl(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 sal_uLong 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 = 0; + mnEndNode = 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(sw::UnoTunnelGetImplementation<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( sal_uLong 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(sw::UnoTunnelGetImplementation<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( sal_uLong nCurrentNode = pCurrentNode->GetIndex() - 1; nCurrentNode > 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..c96dc6676 --- /dev/null +++ b/sw/source/core/unocore/unoframe.cxx @@ -0,0 +1,3656 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svxids.hrc> +#include <svx/xfillit0.hxx> +#include <svx/xflgrit.hxx> +#include <svx/sdtaitm.hxx> +#include <svx/xflclit.hxx> +#include <tools/globname.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 <osl/mutex.hxx> +#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/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.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 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) +{ + aAnyMap.SetValue( nWID, nMemberId, rVal ); +} + +bool BaseFrameProperties_Impl::GetProperty(sal_uInt16 nWID, sal_uInt8 nMemberId, const uno::Any*& rpAny) +{ + return 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; + awt::Size aSize; + aSize.Width = 2 * MM50; + aSize.Height = 2 * MM50; + ::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() : + SwFrameProperties_Impl(/*aSwMapProvider.GetPropertyMap(PROPERTY_MAP_EMBEDDED_OBJECT)*/ ){} + + 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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 m_EventListeners; + + Impl() : m_EventListeners(m_Mutex) { } +}; + +namespace +{ + class theSwXFrameUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXFrameUnoTunnelId > {}; +} + +const ::uno::Sequence< sal_Int8 > & SwXFrame::getUnoTunnelId() +{ + return theSwXFrameUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXFrame::getSomething( const ::uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXFrame>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + + +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) + , eType(eSet) + , 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(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) + , eType(eSet) + , 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(!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(); + } + } + else if(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(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(); + const ::SfxItemPropertySimpleEntry* 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); + 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 ( ((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(static_cast<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( static_cast<sal_uInt16>(nPoints) ); + for(sal_Int32 j = 0; j < nPoints; j++) + { + Point aPoint(pPoints[j].X, pPoints[j].Y); + aSet.SetPoint(aPoint, static_cast<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); + } + // 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::unique_ptr<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 ) + { + const ::SfxPoolItem* pItem; + if( SfxItemState::SET == pFrameFormat->GetItemState( RES_ANCHOR, false, &pItem )) + { + pSet.reset(new SfxItemSet( pDoc->GetAttrPool(), aFrameFormatSetRange )); + pSet->Put( *pItem ); + if ( pFormat->GetDoc()->GetEditShell() != nullptr + && !sw_ChkAndSetNewAnchor( *pFly, *pSet ) ) + { + pSet.reset(); + } + } + } + } + + pFormat->GetDoc()->SetFrameFormatToFly( *pFormat, *pFrameFormat, pSet.get() ); + } + 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::getUnoTunnelImplementation<SwXFrame>(xFrame); + if(pFrame && this != pFrame && pFrame->GetFrameFormat() && pFrame->GetFrameFormat()->GetDoc() == pDoc) + { + SfxItemSet aSet( pDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{} ); + 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 + SfxItemSet aSet( pDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER, + + // FillAttribute support + XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + 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) + { + const ::SfxPoolItem* pItem; + if( SfxItemState::SET == aSet.GetItemState( RES_ANCHOR, false, &pItem )) + { + 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(); +} + +uno::Any SwXFrame::getPropertyValue(const OUString& rPropertyName) +{ + SolarMutexGuard aGuard; + uno::Any aAny; + SwFrameFormat* pFormat = GetFrameFormat(); + const SfxItemPropertySimpleEntry* 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(5); + text::TextContentAnchorType* pArray = aTypes.getArray(); + pArray[0] = text::TextContentAnchorType_AT_PARAGRAPH; + pArray[1] = text::TextContentAnchorType_AS_CHARACTER; + pArray[2] = text::TextContentAnchorType_AT_PAGE; + pArray[3] = text::TextContentAnchorType_AT_FRAME; + pArray[4] = text::TextContentAnchorType_AT_CHARACTER; + aAny <<= aTypes; + } + else if(pFormat) + { + if( ((eType == FLYCNTTYPE_GRF) || (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 ) + { + 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) + { + const SwEditShell* pEditShell = pDoc->GetEditShell(); + if (pEditShell) + { + 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()); + 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(); + } + // 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(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 + pFormat->GetDoc()->GetEditShell()->CalcLayout(); + + SwFrame* pTmpFrame = SwIterator<SwFrame,SwFormat>( *pFormat ).First(); + if ( pTmpFrame ) + { + OSL_ENSURE( pTmpFrame->isFrameAreaDefinitionValid(), "frame not valid" ); + const SwRect &rRect = pTmpFrame->getFrameArea(); + Size aMM100Size = OutputDevice::LogicToLogic( + Size( rRect.Width(), rRect.Height() ), + MapMode( MapUnit::MapTwip ), MapMode( MapUnit::Map100thMM )); + 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()); + beans::PropertyState* pStates = aStates.getArray(); + SwFrameFormat* pFormat = GetFrameFormat(); + if(pFormat) + { + const OUString* pNames = aPropertyNames.getConstArray(); + const SwAttrSet& rFormatSet = pFormat->GetAttrSet(); + for(int i = 0; i < aPropertyNames.getLength(); i++) + { + const SfxItemPropertySimpleEntry* 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 ((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(aStates.begin(), aStates.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 SfxItemPropertySimpleEntry* 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 ) ); + + bool bNextFrame; + if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID) + { + SwDoc* pDoc = pFormat->GetDoc(); + SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + 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 ( (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(); + SfxItemSet aSet( pDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{} ); + aSet.SetParent(&pFormat->GetAttrSet()); + aSet.ClearItem(pEntry->nWID); + if(rPropertyName != UNO_NAME_ANCHOR_TYPE) + pFormat->SetFormatAttr(aSet); + } + } + else + { + 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 SfxItemPropertySimpleEntry* 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) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.addInterface(xListener); +} + +void SAL_CALL SwXFrame::removeEventListener( + const uno::Reference<lang::XEventListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_EventListeners.removeInterface(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); + m_pImpl->m_EventListeners.disposeAndClear(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) + { + 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() +{ + 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 = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = reinterpret_cast< SwXTextRange * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( SwXTextRange::getUnoTunnelId()) )); + pCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething( OTextCursorHelper::getUnoTunnelId()) )); + } + + 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 ); + static sal_uInt16 const aFrameAttrRange[] = + { + 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, + 0 + }; + static sal_uInt16 const aGrAttrRange[] = + { + RES_GRFATR_BEGIN, RES_GRFATR_END-1, + 0 + }; + SfxItemSet aGrSet(pDoc->GetAttrPool(), aGrAttrRange ); + + SfxItemSet aFrameSet(pDoc->GetAttrPool(), aFrameAttrRange ); + + // 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(); + } + + const SfxPoolItem* pItem; + RndStdIds eAnchorId = RndStdIds::FLY_AT_PARA; + if(SfxItemState::SET == aFrameSet.GetItemState(RES_ANCHOR, false, &pItem) ) + { + eAnchorId = static_cast<const SwFormatAnchor*>(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 == static_cast<const SwFormatAnchor*>(pItem)->GetPageNum() ) + { + SwFormatAnchor aAnchor( *static_cast<const SwFormatAnchor*>(pItem) ); + 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( 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 )); + } + + aPam.DeleteMark(); // mark position node will be deleted! + aIntPam.DeleteMark(); // mark position node will be deleted! + pFormat = pDoc->MakeFlyAndMove( *pCopySource, aFrameSet, + nullptr, + pParentFrameFormat ); + if(pAnchorItem && pFormat) + { + pFormat->DelFrames(); + pAnchorItem->SetAnchor( pCopySource->Start() ); + SfxItemSet aAnchorSet( pDoc->GetAttrPool(), svl::Items<RES_ANCHOR, RES_ANCHOR>{} ); + aAnchorSet.Put( *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( bIsDescriptor ? m_pDoc : GetFrameFormat()->GetDoc() ); + } + else if( 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; + + SwFrameFormat* pFormat; + if(IsDescriptor()) + attachToRange(xTextRange); + else if(nullptr != (pFormat = GetFrameFormat())) + { + SwDoc* pDoc = pFormat->GetDoc(); + SwUnoInternalPaM aIntPam(*pDoc); + if (!::sw::XTextRangeToSwPaM(aIntPam, xTextRange)) + throw lang::IllegalArgumentException(); + + SfxItemSet aSet( pDoc->GetAttrPool(), svl::Items<RES_ANCHOR, RES_ANCHOR>{} ); + 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() +{ + SolarMutexGuard aGuard; + uno::RuntimeException aRuntime; + aRuntime.Message = "position cannot be determined with this method"; + throw aRuntime; +} + +void SwXFrame::setPosition(const awt::Point& /*aPosition*/) +{ + SolarMutexGuard aGuard; + 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 ) : + SwXText(nullptr, CursorType::Frame), + SwXFrame(FLYCNTTYPE_FRM, aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_FRAME), _pDoc ) +{ +} + +SwXTextFrame::SwXTextFrame(SwFrameFormat& rFormat) : + SwXText(rFormat.GetDoc(), CursorType::Frame), + SwXFrame(rFormat, FLYCNTTYPE_FRM, aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_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( )throw() +{ + SwXFrame::acquire(); +} + +void SAL_CALL SwXTextFrame::release( )throw() +{ + 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; + uno::Reference< text::XTextCursor > aRef; + 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; + } + + SwXTextCursor *const pXCursor = new SwXTextCursor( + *pFormat->GetDoc(), this, CursorType::Frame, *aPam.GetPoint()); + aRef = static_cast<text::XWordCursor*>(pXCursor); + + return aRef; +} + +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..f9efe7742 --- /dev/null +++ b/sw/source/core/unocore/unoftn.cxx @@ -0,0 +1,575 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/interfacecontainer2.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svl/listener.hxx> +#include <osl/mutex.hxx> + +#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.begin(), + [](const char* pService) -> OUString { return OUString::createFromAscii(pService); }); + + return ret; +} + +} + +class SwXFootnote::Impl + : public SvtListener +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + + SwXFootnote& m_rThis; + uno::WeakReference<uno::XInterface> m_wThis; + const bool m_bIsEndnote; + ::comphelper::OInterfaceContainerHelper2 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_EventListeners(m_Mutex) + , 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); + m_EventListeners.disposeAndClear(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; +} + +namespace +{ + class theSwXFootnoteUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXFootnoteUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXFootnote::getUnoTunnelId() +{ + return theSwXFootnoteUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXFootnote::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + const sal_Int64 nRet( ::sw::UnoTunnelImpl<SwXFootnote>(rId, this) ); + return nRet ? nRet : SwXText::getSomething(rId); +} + +OUString SAL_CALL +SwXFootnote::getImplementationName() +{ + return "SwXFootnote"; +} + +static 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! +}; + +static const size_t g_nServicesEndnote( SAL_N_ELEMENTS(g_ServicesFootnote) ); + +static 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 = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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() ); + SwXTextCursor *const pXCursor = + new SwXTextCursor(*GetDoc(), this, CursorType::Footnote, aPos); + auto& rUnoCursor(pXCursor->GetCursor()); + rUnoCursor.Move(fnMoveForward, GoInNode); + const uno::Reference< text::XTextCursor > xRet = + static_cast<text::XWordCursor*>(pXCursor); + return xRet; +} + +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..873058e98 --- /dev/null +++ b/sw/source/core/unocore/unoidx.cxx @@ -0,0 +1,3118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cppuhelper/interfacecontainer.h> +#include <comphelper/interfacecontainer2.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/svapp.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <editeng/memberids.h> +#include <hints.hxx> +#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> + +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* pDoc, SwTOXBase& rTOXBase, const OUString& rNewName) +{ + const sal_uInt16 nUserCount = pDoc->GetTOXTypeCount( TOX_USER ); + const SwTOXType* pNewType = nullptr; + for(sal_uInt16 nUser = 0; nUser < nUserCount; nUser++) + { + const SwTOXType* pType = pDoc->GetTOXType( TOX_USER, nUser ); + if (pType->GetTypeName()==rNewName) + { + pNewType = pType; + break; + } + } + if(!pNewType) + { + SwTOXType aNewType(*pDoc, TOX_USER, rNewName); + pNewType = pDoc->InsertTOXType( aNewType ); + } + + rTOXBase.RegisterToTOXType( *const_cast<SwTOXType*>(pNewType) ); +} + +static const char cUserDefined[] = "User-Defined"; +static 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.matchAsciiL(cUserDefined, sizeof(cUserDefined)) && + rTmp.matchAsciiL(cUserSuffix, sizeof(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; + ::cppu::OMultiTypeInterfaceContainerHelper 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(auto pLegacy = dynamic_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; +} + +namespace +{ + class theSwXDocumentIndexUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXDocumentIndexUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXDocumentIndex::getUnoTunnelId() +{ + return theSwXDocumentIndexUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXDocumentIndex::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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; + + SfxItemPropertySimpleEntry 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_INDEX_ENTRY_TYPE: + { + rTOXBase.SetEntryTypeName(lcl_AnyToType<OUString>(rValue)); + nCreate = SwTOXElement::IndexEntryType; + 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; + SfxItemPropertySimpleEntry 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_INDEX_ENTRY_TYPE : + aRet <<= pTOXBase->GetEntryTypeName(); + 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(); + SwTOXMark::InsertTOXMarks( aMarks, *pType ); + 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(); + } + + ::cppu::OInterfaceContainerHelper *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 = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + SwXDocumentIndexMark & m_rThis; + bool m_bInReplaceMark; + +public: + + uno::WeakReference<uno::XInterface> m_wThis; + SfxItemPropertySet const& m_rPropSet; + const TOXTypes m_eTOXType; + ::comphelper::OInterfaceContainerHelper2 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_aEntryTypeName; + 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_EventListeners(m_Mutex) + , 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(SwTOXType & rTOXType, SwTOXMark & rMark, SwPaM & rPam, + SwXTextCursor const*const pTextCursor); + + void ReplaceTOXMark(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)); + m_EventListeners.disposeAndClear(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); + m_EventListeners.disposeAndClear(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, + SwTOXType & rType, 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 +{ + class theSwXDocumentIndexMarkUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXDocumentIndexMarkUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXDocumentIndexMark::getUnoTunnelId() +{ + return theSwXDocumentIndexMarkUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXDocumentIndexMark::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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 = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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( + SwTOXType & rTOXType, SwTOXMark & rMark, SwPaM & rPam, + SwXTextCursor const*const pTextCursor) +{ + SwDoc *const pDoc( rPam.GetDoc() ); + UnoActionContext aAction(pDoc); + 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; + pDoc->getIDocumentContentOperations().InsertPoolItem(rPam, rMark, nInsertFlags, + /*pLayout*/nullptr, /*bExpandCharToPara*/false, &pNewTextAttr); + if (bMark && *rPam.GetPoint() > *rPam.GetMark()) + { + rPam.Exchange(); + } + + if (!pNewTextAttr) + { + throw uno::RuntimeException( + "SwXDocumentIndexMark::InsertTOXMark(): cannot insert attribute", + nullptr); + } + + m_pDoc = pDoc; + 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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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; + + SfxItemPropertySimpleEntry 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_INDEX_ENTRY_TYPE : + aMark.SetEntryTypeName(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_INDEX_ENTRY_TYPE : + { + m_pImpl->m_aEntryTypeName = 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; + SfxItemPropertySimpleEntry 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_INDEX_ENTRY_TYPE : + aRet <<= m_pImpl->m_pTOXMark->GetEntryTypeName(); + 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_INDEX_ENTRY_TYPE : + aRet <<= m_pImpl->m_aEntryTypeName; + 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(), static_cast<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(static_cast<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; +}; + +} + +static 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 = convertMm100ToTwip(nPosition); + 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::ISBN) + { + 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(static_cast<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(static_cast<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/unomap.cxx b/sw/source/core/unocore/unomap.cxx new file mode 100644 index 000000000..8da991f62 --- /dev/null +++ b/sw/source/core/unocore/unomap.cxx @@ -0,0 +1,1569 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/style/VerticalAlignment.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/colritem.hxx> +#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 \ + { OUString(UNO_NAME_IS_FIELD_USED), FIELD_PROP_IS_FIELD_USED, cppu::UnoType<float>::get(), PropertyAttribute::READONLY, 0},\ + { OUString(UNO_NAME_IS_FIELD_DISPLAYED), FIELD_PROP_IS_FIELD_DISPLAYED, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::READONLY, 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 [] = + { + { OUString(UNO_NAME_RUBY_ADJUST), RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ADJUST }, + { OUString(UNO_NAME_RUBY_IS_ABOVE), RES_TXTATR_CJK_RUBY, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ABOVE }, + { OUString(UNO_NAME_RUBY_POSITION), RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_POSITION }, + { OUString(), 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 [] = + { + { OUString(UNO_NAME_NUMBERING_RULES), FN_UNO_NUM_RULES, cppu::UnoType<css::container::XIndexReplace>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_HIDDEN), FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_STYLE_INTEROP_GRAB_BAG), FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + { OUString(), 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[] = + { + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_NUMBER_FORMAT), RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 }, + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_TEXT_SECTION), FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_IS_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), 0, MID_PROTECT_CONTENT}, + { OUString(UNO_NAME_CELL_NAME), FN_UNO_CELL_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY,0}, + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_ROW_SPAN), FN_UNO_CELL_ROW_SPAN, cppu::UnoType<sal_Int32>::get(), 0, 0 }, + { OUString(UNO_NAME_CELL_INTEROP_GRAB_BAG), RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_PARENT_TEXT), FN_UNO_PARENT_TEXT, cppu::UnoType<text::XText>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, + REDLINE_NODE_PROPERTIES + { OUString(), 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[] = + { + { OUString(UNO_NAME_SEARCH_ALL), WID_SEARCH_ALL, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_BACKWARDS), WID_BACKWARDS, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_CASE_SENSITIVE), WID_CASE_SENSITIVE, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_REGULAR_EXPRESSION), WID_REGULAR_EXPRESSION, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_SIMILARITY), WID_SIMILARITY, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_SIMILARITY_ADD), WID_SIMILARITY_ADD, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_SIMILARITY_EXCHANGE), WID_SIMILARITY_EXCHANGE,cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_SIMILARITY_RELAX), WID_SIMILARITY_RELAX, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_SIMILARITY_REMOVE), WID_SIMILARITY_REMOVE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_STYLES), WID_STYLES, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEARCH_WORDS), WID_WORDS, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(), 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[] = + { + { OUString(UNO_NAME_ANCHOR_PAGE_NO), RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_ANCHOR_PAGENUM }, + { OUString(UNO_NAME_ANCHOR_TYPE), RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORTYPE}, + { OUString(UNO_NAME_ANCHOR_FRAME), RES_ANCHOR, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORFRAME}, + { OUString(UNO_NAME_HORI_ORIENT), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_ORIENT }, + { OUString(UNO_NAME_HORI_ORIENT_POSITION), RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_POSITION|CONVERT_TWIPS }, + { OUString(UNO_NAME_HORI_ORIENT_RELATION), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_RELATION }, + { OUString(UNO_NAME_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_SURROUND), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_SURROUND_SURROUNDTYPE }, + { OUString(UNO_NAME_TEXT_WRAP), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, + { OUString(UNO_NAME_SURROUND_ANCHORONLY), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_SURROUND_ANCHORONLY }, + { OUString(UNO_NAME_SURROUND_CONTOUR), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR }, + { OUString(UNO_NAME_CONTOUR_OUTSIDE), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE }, + { OUString(UNO_NAME_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_ORIENT }, + { OUString(UNO_NAME_VERT_ORIENT_POSITION), RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_POSITION|CONVERT_TWIPS }, + { OUString(UNO_NAME_VERT_ORIENT_RELATION), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_RELATION }, + { OUString(UNO_NAME_TEXT_RANGE), FN_TEXT_RANGE, cppu::UnoType<css::text::XTextRange>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_OPAQUE), RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_ANCHOR_POSITION), FN_ANCHOR_POSITION, cppu::UnoType<css::awt::Point>::get(), PropertyAttribute::READONLY, 0}, + // #i26791# + { OUString(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW}, + // #i28701# + { OUString(UNO_NAME_WRAP_INFLUENCE_ON_POSITION), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE}, + { OUString(UNO_NAME_ALLOW_OVERLAP), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP}, + // #i28749# + { OUString(UNO_NAME_TRANSFORMATION_IN_HORI_L2R), + FN_SHAPE_TRANSFORMATION_IN_HORI_L2R, + cppu::UnoType<css::drawing::HomogenMatrix3>::get(), + PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_POSITION_LAYOUT_DIR), + FN_SHAPE_POSITION_LAYOUT_DIR, + cppu::UnoType<sal_Int16>::get(), + PROPERTY_NONE, 0}, + // #i36248# + { OUString(UNO_NAME_STARTPOSITION_IN_HORI_L2R), + FN_SHAPE_STARTPOSITION_IN_HORI_L2R, + cppu::UnoType<css::awt::Point>::get(), + PropertyAttribute::READONLY, 0}, + { OUString(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> + { OUString(UNO_NAME_PAGE_TOGGLE), RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE }, + { OUString(UNO_NAME_RELATIVE_HEIGHT), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT }, + { OUString(UNO_NAME_RELATIVE_HEIGHT_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION }, + { OUString(UNO_NAME_RELATIVE_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, + { OUString(UNO_NAME_RELATIVE_WIDTH_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION }, + { OUString(UNO_NAME_TEXT_BOX), FN_TEXT_BOX, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAIN_NEXT_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NEXTNAME}, + { OUString(UNO_NAME_CHAIN_PREV_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_PREVNAME}, + { OUString(UNO_NAME_CHAIN_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NAME }, + { OUString(), 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_ + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_ALPHABETICAL_SEPARATORS), WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_KEY_AS_ENTRY), WID_USE_KEY_AS_ENTRY , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_COMBINED_ENTRIES), WID_USE_COMBINED_ENTRIES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_CASE_SENSITIVE), WID_IS_CASE_SENSITIVE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_P_P), WID_USE_P_P , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_DASH), WID_USE_DASH , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_UPPER_CASE), WID_USE_UPPER_CASE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_MAIN_ENTRY_CHARACTER_STYLE_NAME), WID_MAIN_ENTRY_CHARACTER_STYLE_NAME , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLESEPARATOR), WID_PARA_SEP, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL2), WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL3), WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_IS_COMMA_SEPARATED), WID_IS_COMMA_SEPARATED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_DOCUMENT_INDEX_MARKS), WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LOCALE), WID_IDX_LOCALE, cppu::UnoType<css::lang::Locale>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SORT_ALGORITHM), WID_IDX_SORT_ALGORITHM, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_INDEX_ENTRY_TYPE), WID_INDEX_ENTRY_TYPE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 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_ + { OUString(UNO_NAME_LEVEL), WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_MARKS), WID_CREATE_FROM_MARKS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS), WID_HIDE_TABLEADER_PAGENUMBERS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TAB_IN_TOC), WID_TAB_IN_TOC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_BOOKMARK), WID_TOC_BOOKMARK, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_NEWLINE), WID_TOC_NEWLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_PARAGRAPH_OUTLINE_LEVEL), WID_TOC_PARAGRAPH_OUTLINE_LEVEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_OUTLINE), WID_CREATE_FROM_OUTLINE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_PARAGRAPH_STYLES), WID_LEVEL_PARAGRAPH_STYLES , cppu::UnoType<css::container::XIndexReplace>::get() , PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), WID_CREATE_FROM_PARAGRAPH_STYLES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL2), WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL3), WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL4), WID_PARA_LEV4, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL5), WID_PARA_LEV5, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL6), WID_PARA_LEV6, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL7), WID_PARA_LEV7, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL8), WID_PARA_LEV8, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL9), WID_PARA_LEV9, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL10), WID_PARA_LEV10, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DOCUMENT_INDEX_MARKS), WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 }, + { OUString(), 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_ + { OUString(UNO_NAME_CREATE_FROM_MARKS), WID_CREATE_FROM_MARKS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS), WID_HIDE_TABLEADER_PAGENUMBERS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TAB_IN_TOC), WID_TAB_IN_TOC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_BOOKMARK), WID_TOC_BOOKMARK, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_NEWLINE), WID_TOC_NEWLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TOC_PARAGRAPH_OUTLINE_LEVEL), WID_TOC_PARAGRAPH_OUTLINE_LEVEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_LEVEL_FROM_SOURCE), WID_USE_LEVEL_FROM_SOURCE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0}, + { OUString(UNO_NAME_LEVEL_PARAGRAPH_STYLES), WID_LEVEL_PARAGRAPH_STYLES , cppu::UnoType<css::container::XIndexReplace>::get() , PropertyAttribute::READONLY,0}, + { OUString(UNO_NAME_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), WID_CREATE_FROM_PARAGRAPH_STYLES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_TABLES), WID_CREATE_FROM_TABLES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_TEXT_FRAMES), WID_CREATE_FROM_TEXT_FRAMES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_GRAPHIC_OBJECTS), WID_CREATE_FROM_GRAPHIC_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_EMBEDDED_OBJECTS), WID_CREATE_FROM_EMBEDDED_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL2), WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL3), WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL4), WID_PARA_LEV4, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL5), WID_PARA_LEV5, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL6), WID_PARA_LEV6, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL7), WID_PARA_LEV7, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL8), WID_PARA_LEV8, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL9), WID_PARA_LEV9, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL10), WID_PARA_LEV10, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_DOCUMENT_INDEX_MARKS), WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USER_INDEX_NAME), WID_USER_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(), 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_ + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_LABELS), WID_CREATE_FROM_LABELS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LABEL_CATEGORY), WID_LABEL_CATEGORY , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LABEL_DISPLAY_TYPE), WID_LABEL_DISPLAY_TYPE , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 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_ + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_ALPHABETICAL_SEPARATORS), WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0}, + { OUString(UNO_NAME_CREATE_FROM_STAR_MATH), WID_CREATE_FROM_STAR_MATH , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_STAR_CHART), WID_CREATE_FROM_STAR_CHART , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_STAR_CALC), WID_CREATE_FROM_STAR_CALC , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_STAR_DRAW), WID_CREATE_FROM_STAR_DRAW , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_OTHER_EMBEDDED_OBJECTS), WID_CREATE_FROM_OTHER_EMBEDDED_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 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_ + { OUString(UNO_NAME_CREATE_FROM_CHAPTER), WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CREATE_FROM_LABELS), WID_CREATE_FROM_LABELS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USE_ALPHABETICAL_SEPARATORS), WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LABEL_CATEGORY), WID_LABEL_CATEGORY , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LABEL_DISPLAY_TYPE), WID_LABEL_DISPLAY_TYPE , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_IS_RELATIVE_TABSTOPS), WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTOXIllustrationsMap_Impl; + } + break; + case PROPERTY_MAP_TEXT_TABLE_ROW: + { + static SfxItemPropertyMapEntry const aTableRowPropertyMap_Impl[] = + { + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_TABLE_COLUMN_SEPARATORS), FN_UNO_TABLE_COLUMN_SEPARATORS, cppu::UnoType< cppu::UnoSequenceType<css::text::TableColumnSeparator> >::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_HEIGHT), FN_UNO_ROW_HEIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,CONVERT_TWIPS }, + { OUString(UNO_NAME_IS_AUTO_HEIGHT), FN_UNO_ROW_AUTO_HEIGHT, cppu::UnoType<bool>::get(), PROPERTY_NONE , 0 }, + { OUString(UNO_NAME_SIZE_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE }, + { OUString(UNO_NAME_WIDTH_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE }, + { OUString(UNO_NAME_IS_SPLIT_ALLOWED), RES_ROW_SPLIT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_ROW_INTEROP_GRAB_BAG), RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, + { OUString(), 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_PARAGRAPH_EXTENSIONS: + { + m_aMapEntriesArr[nPropertyId] = GetParagraphExtensionsPropertyMap(); + } + break; + case PROPERTY_MAP_BIBLIOGRAPHY : + { + static SfxItemPropertyMapEntry const aBibliographyMap_Impl[] = + { + BASE_INDEX_PROPERTIES_ + { OUString(UNO_NAME_IS_PROTECTED), WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_PARA_STYLEHEADING), WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_PARA_STYLELEVEL1), WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0}, + { OUString(UNO_NAME_LEVEL_FORMAT), WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0}, + { OUString(UNO_NAME_LOCALE), WID_IDX_LOCALE, cppu::UnoType<css::lang::Locale>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SORT_ALGORITHM), WID_IDX_SORT_ALGORITHM, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aBibliographyMap_Impl; + } + break; + case PROPERTY_MAP_TEXT_DOCUMENT: + { + static SfxItemPropertyMapEntry const aDocMap_Impl[] = + { + { OUString(UNO_NAME_BASIC_LIBRARIES), WID_DOC_BASIC_LIBRARIES, cppu::UnoType<css::script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_CHAR_FONT_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + { OUString(UNO_NAME_CHAR_FONT_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + { OUString(UNO_NAME_CHAR_FONT_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + { OUString(UNO_NAME_CHAR_LOCALE), RES_CHRATR_LANGUAGE , cppu::UnoType<css::lang::Locale>::get(), PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE }, + { OUString(UNO_NAME_CHARACTER_COUNT), WID_DOC_CHAR_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_DIALOG_LIBRARIES), WID_DOC_DIALOG_LIBRARIES, cppu::UnoType<css::script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_VBA_DOCOBJ), WID_DOC_VBA_DOCOBJ, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_INDEX_AUTO_MARK_FILE_U_R_L), WID_DOC_AUTO_MARK_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PARAGRAPH_COUNT), WID_DOC_PARA_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_RECORD_CHANGES), WID_DOC_CHANGES_RECORD, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SHOW_CHANGES), WID_DOC_CHANGES_SHOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_WORD_COUNT), WID_DOC_WORD_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_IS_TEMPLATE), WID_DOC_ISTEMPLATEID, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_WORD_SEPARATOR), WID_DOC_WORD_SEPARATOR, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_HIDE_FIELD_TIPS), WID_DOC_HIDE_TIPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_REDLINE_DISPLAY_TYPE), WID_DOC_REDLINE_DISPLAY, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_REDLINE_PROTECTION_KEY), WID_DOC_CHANGES_PASSWORD, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_FORBIDDEN_CHARACTERS), WID_DOC_FORBIDDEN_CHARS, cppu::UnoType<css::i18n::XForbiddenCharacters>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TWO_DIGIT_YEAR), WID_DOC_TWO_DIGIT_YEAR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_AUTOMATIC_CONTROL_FOCUS), WID_DOC_AUTOMATIC_CONTROL_FOCUS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_APPLY_FORM_DESIGN_MODE), WID_DOC_APPLY_FORM_DESIGN_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_RUNTIME_UID), WID_DOC_RUNTIME_UID, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_LOCK_UPDATES), WID_DOC_LOCK_UPDATES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString("UndocumentedWriterfilterHack"), WID_DOC_WRITERFILTER, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_HAS_VALID_SIGNATURES), WID_DOC_HAS_VALID_SIGNATURES, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_BUILDID), WID_DOC_BUILDID, cppu::UnoType<OUString>::get(), 0, 0}, + { OUString(UNO_NAME_DOC_INTEROP_GRAB_BAG), WID_DOC_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_DEFAULT_PAGE_MODE), WID_DOC_DEFAULT_PAGE_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocMap_Impl; + } + break; + case PROPERTY_MAP_LINK_TARGET: + { + static SfxItemPropertyMapEntry const aLinkTargetMap_Impl[] = + { + { OUString(UNO_LINK_DISPLAY_BITMAP), 0, cppu::UnoType<css::awt::XBitmap>::get(), PropertyAttribute::READONLY, 0xbf}, + { OUString(UNO_LINK_DISPLAY_NAME), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aLinkTargetMap_Impl; + } + break; + case PROPERTY_MAP_AUTO_TEXT_GROUP : + { + static SfxItemPropertyMapEntry const aAutoTextGroupMap_Impl[] = + { + { OUString(UNO_NAME_FILE_PATH), WID_GROUP_PATH, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_TITLE), WID_GROUP_TITLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 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_TEXT_COLUMS : + { + static SfxItemPropertyMapEntry const aTextColumns_Impl[] = + { + {OUString(UNO_NAME_IS_AUTOMATIC), WID_TXTCOL_IS_AUTOMATIC, cppu::UnoType<bool>::get(),PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_AUTOMATIC_DISTANCE), WID_TXTCOL_AUTO_DISTANCE, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_WIDTH), WID_TXTCOL_LINE_WIDTH, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_COLOR), WID_TXTCOL_LINE_COLOR, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_RELATIVE_HEIGHT), WID_TXTCOL_LINE_REL_HGT, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_VERTIVAL_ALIGNMENT), WID_TXTCOL_LINE_ALIGN, cppu::UnoType<css::style::VerticalAlignment>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_IS_ON), WID_TXTCOL_LINE_IS_ON, cppu::UnoType<bool>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEPARATOR_LINE_STYLE), WID_TXTCOL_LINE_STYLE, cppu::UnoType<sal_Int8>::get(),PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTextColumns_Impl; + } + 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 ) + { + // OUString(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[] = + { + {OUString(UNO_NAME_ADJUST), FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATE_TIME_VALUE), FIELD_PROP_DATE_TIME, cppu::UnoType<css::util::DateTime>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + {OUString(UNO_NAME_IS_DATE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDateTimeFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_USER : + { + static SfxItemPropertyMapEntry const aUserFieldPropMap[] = + { + {OUString(UNO_NAME_IS_SHOW_FORMULA), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_VISIBLE), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + m_aMapEntriesArr[nPropertyId] = aUserFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_SET_EXP : + { + static SfxItemPropertyMapEntry const aSetExpFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_HINT), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBERING_TYPE), FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_INPUT), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + // #i69733# wrong name - OUString(UNO_NAME_IS_INPUT) expanded to "Input" instead of "IsInput" + {OUString(UNO_NAME_INPUT), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_SHOW_FORMULA), FIELD_PROP_BOOL3, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_VISIBLE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEQUENCE_VALUE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SUB_TYPE), FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_VALUE), FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_VARIABLE_NAME), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aSetExpFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_GET_EXP : + { + static SfxItemPropertyMapEntry const aGetExpFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_SHOW_FORMULA), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SUB_TYPE), FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_VALUE), FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_VARIABLE_SUBTYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aGetExpFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_FILE_NAME: + { + static SfxItemPropertyMapEntry const aFileNameFieldPropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_FILE_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aFileNameFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_PAGE_NUM : + { + static SfxItemPropertyMapEntry const aPageNumFieldPropMap [] = + { + {OUString(UNO_NAME_NUMBERING_TYPE), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_OFFSET), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SUB_TYPE), FIELD_PROP_SUBTYPE, cppu::UnoType<css::text::PageNumberType>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_USERTEXT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aPageNumFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_AUTHOR : + { + static SfxItemPropertyMapEntry const aAuthorFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_FULL_NAME),FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aAuthorFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_CHAPTER : + { + static SfxItemPropertyMapEntry const aChapterFieldPropMap [] = + { + {OUString(UNO_NAME_CHAPTER_FORMAT),FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_LEVEL),FIELD_PROP_BYTE1, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aChapterFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_GET_REFERENCE : + { + static SfxItemPropertyMapEntry const aGetRefFieldPropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_REFERENCE_FIELD_PART),FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_REFERENCE_FIELD_SOURCE),FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SEQUENCE_NUMBER), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SOURCE_NAME), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_REFERENCE_FIELD_LANGUAGE), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aGetRefFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_CONDITIONED_TEXT : + { + static SfxItemPropertyMapEntry const aConditionedTextFieldPropMap [] = + { + {OUString(UNO_NAME_CONDITION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_FALSE_CONTENT), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_CONDITION_TRUE) , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_TRUE_CONTENT) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aConditionedTextFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_HIDDEN_TEXT : + { + static SfxItemPropertyMapEntry const aHiddenTextFieldPropMap [] = + { + {OUString(UNO_NAME_CONDITION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONTENT) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_HIDDEN) , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aHiddenTextFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_ANNOTATION : + { + static SfxItemPropertyMapEntry const aAnnotationFieldPropMap [] = + { + {OUString(UNO_NAME_AUTHOR), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INITIALS), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_RESOLVED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATE_TIME_VALUE), FIELD_PROP_DATE_TIME, cppu::UnoType<css::util::DateTime>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATE), FIELD_PROP_DATE, cppu::UnoType<css::util::Date>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_TEXT_RANGE), FIELD_PROP_TEXT, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::READONLY, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aAnnotationFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_INPUT: + { + static SfxItemPropertyMapEntry const aInputFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_HINT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_HELP), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_TOOLTIP), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aInputFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_MACRO : + { + static SfxItemPropertyMapEntry const aMacroFieldPropMap [] = + { + {OUString(UNO_NAME_HINT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_MACRO_NAME),FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_MACRO_LIBRARY),FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(),PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SCRIPT_URL),FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(),PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aMacroFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DDE : + { + static SfxItemPropertyMapEntry const aDDEFieldPropMap [] = + { + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDDEFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DROPDOWN : + { + static SfxItemPropertyMapEntry const aDropDownMap [] = + { + {OUString(UNO_NAME_ITEMS), FIELD_PROP_STRINGS, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SELITEM), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_HELP), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_TOOLTIP), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDropDownMap; + } + break; + case PROPERTY_MAP_FLDTYP_HIDDEN_PARA : + { + static SfxItemPropertyMapEntry const aHiddenParaFieldPropMap [] = + { + {OUString(UNO_NAME_CONDITION),FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_HIDDEN) , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aHiddenParaFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOC_INFO : + { + static SfxItemPropertyMapEntry const aDocInfoFieldPropMap [] = + { + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INFO_FORMAT), FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INFO_TYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_TEMPLATE_NAME : + { + static SfxItemPropertyMapEntry const aTmplNameFieldPropMap [] = + { + {OUString(UNO_NAME_FILE_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTmplNameFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_USER_EXT : + { + static SfxItemPropertyMapEntry const aUsrExtFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_USER_DATA_TYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId]= aUsrExtFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_REF_PAGE_SET : + { + static SfxItemPropertyMapEntry const aRefPgSetFieldPropMap [] = + { + {OUString(UNO_NAME_OFFSET), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_ON), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aRefPgSetFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_REF_PAGE_GET : + { + static SfxItemPropertyMapEntry const aRefPgGetFieldPropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBERING_TYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aRefPgGetFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_JUMP_EDIT : + { + static SfxItemPropertyMapEntry const aJumpEdtFieldPropMap [] = + { + {OUString(UNO_NAME_HINT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_PLACEHOLDER), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_PLACEHOLDER_TYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aJumpEdtFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_SCRIPT : + { + static SfxItemPropertyMapEntry const aScriptFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SCRIPT_TYPE), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_URL_CONTENT), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 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. + + {OUString(UNO_NAME_DATA_BASE_NAME) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_TABLE_NAME) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONDITION) , FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_BASE_URL) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COMMAND_TYPE), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 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. + + {OUString(UNO_NAME_DATA_BASE_NAME) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_TABLE_NAME), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONDITION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_BASE_URL) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COMMAND_TYPE), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SET_NUMBER), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 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. + + {OUString(UNO_NAME_DATA_BASE_NAME) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_TABLE_NAME) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_BASE_URL) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COMMAND_TYPE), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBERING_TYPE), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SET_NUMBER), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_VISIBLE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDBSetNumFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DATABASE : + { + static SfxItemPropertyMapEntry const aDBFieldPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_FIELD_CODE), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_DATA_BASE_FORMAT),FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_VISIBLE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 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. + + {OUString(UNO_NAME_DATA_BASE_NAME) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_TABLE_NAME) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_BASE_URL) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COMMAND_TYPE), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_VISIBLE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDBNameFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCSTAT: + { + static SfxItemPropertyMapEntry const aDocstatFieldPropMap [] = + { + {OUString(UNO_NAME_NUMBERING_TYPE), FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + // {OUString(UNO_NAME_STATISTIC_TYPE_ID),FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocstatFieldPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR: + { + static SfxItemPropertyMapEntry const aDocInfoAuthorPropMap [] = + { + {OUString(UNO_NAME_AUTHOR), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoAuthorPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME: + { + static SfxItemPropertyMapEntry const aDocInfoDateTimePropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATE_TIME_VALUE), FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_IS_DATE), FIELD_PROP_BOOL2, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + {OUString(UNO_NAME_NUMBER_FORMAT),FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoDateTimePropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME : + { + static SfxItemPropertyMapEntry const aDocInfoEditTimePropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATE_TIME_VALUE), FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_NUMBER_FORMAT),FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoEditTimePropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_MISC: + { + static SfxItemPropertyMapEntry const aDocInfoStringContentPropMap [] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoStringContentPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM: + { + static SfxItemPropertyMapEntry const aDocInfoCustomPropMap [] = + { + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED_LANGUAGE), FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoCustomPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DOCINFO_REVISION : + { + static SfxItemPropertyMapEntry const aDocInfoRevisionPropMap [] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_REVISION), FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_FIXED), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDocInfoRevisionPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS: + { + static SfxItemPropertyMapEntry const aCombinedCharactersPropMap[] = + { + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aCombinedCharactersPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_TABLE_FORMULA: + { + static SfxItemPropertyMapEntry const aTableFormulaPropMap[] = + { + {OUString(UNO_NAME_CURRENT_PRESENTATION), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_SHOW_FORMULA), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBER_FORMAT), FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTableFormulaPropMap; + } + break; + case PROPERTY_MAP_FLDTYP_DUMMY_0: + { + static SfxItemPropertyMapEntry const aEmptyPropMap [] = + { + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aEmptyPropMap; + } + break; + case PROPERTY_MAP_FLDMSTR_USER : + { + static SfxItemPropertyMapEntry const aUserFieldTypePropMap[] = + { + {OUString(UNO_NAME_DEPENDENT_TEXT_FIELDS), FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_IS_EXPRESSION), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + {OUString(UNO_NAME_VALUE), FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aUserFieldTypePropMap; + } + break; + case PROPERTY_MAP_FLDMSTR_DDE : + { + static SfxItemPropertyMapEntry const aDDEFieldTypePropMap[] = + { + {OUString(UNO_NAME_DDE_COMMAND_ELEMENT), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DDE_COMMAND_FILE), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DDE_COMMAND_TYPE), FIELD_PROP_SUBTYPE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DEPENDENT_TEXT_FIELDS), FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_IS_AUTOMATIC_UPDATE), FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_CONTENT), FIELD_PROP_PAR5, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDDEFieldTypePropMap; + } + break; + case PROPERTY_MAP_FLDMSTR_SET_EXP : + { + static SfxItemPropertyMapEntry const aSetExpFieldTypePropMap[] = + { + {OUString(UNO_NAME_CHAPTER_NUMBERING_LEVEL),FIELD_PROP_SHORT1, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DEPENDENT_TEXT_FIELDS), FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NUMBERING_SEPARATOR), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SUB_TYPE), FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 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. + + {OUString(UNO_NAME_DATA_BASE_NAME) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_NAME), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + {OUString(UNO_NAME_DATA_TABLE_NAME), FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COLUMN_NAME), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_DATA_BASE_URL) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DATA_COMMAND_TYPE), FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_DEPENDENT_TEXT_FIELDS), FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aDBFieldTypePropMap; + } + break; + case PROPERTY_MAP_FLDMSTR_DUMMY0 : + { + static SfxItemPropertyMapEntry const aStandardFieldMasterMap[] = + { + {OUString(UNO_NAME_DEPENDENT_TEXT_FIELDS), 0, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_NAME), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aStandardFieldMasterMap; + } + break; + case PROPERTY_MAP_FLDTYP_BIBLIOGRAPHY: + { + static SfxItemPropertyMapEntry const aBibliographyFieldMap[] = + { + {OUString(UNO_NAME_FIELDS) , FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(),PROPERTY_NONE, 0}, + COMMON_FLDTYP_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aBibliographyFieldMap; + } + break; + case PROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY: + { + static SfxItemPropertyMapEntry const aBibliographyFieldMasterMap[] = + { + {OUString(UNO_NAME_BRACKET_BEFORE) , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_BRACKET_AFTER) , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_NUMBER_ENTRIES) , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_SORT_BY_POSITION) , FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_LOCALE), FIELD_PROP_LOCALE, cppu::UnoType<css::lang::Locale>::get() , PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SORT_ALGORITHM), FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_SORT_KEYS) , FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValues> >::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_INSTANCE_NAME), FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aBibliographyFieldMasterMap; + } + break; + case PROPERTY_MAP_TEXT : + { + static SfxItemPropertyMapEntry const aTextMap[] = + { + REDLINE_NODE_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTextMap; + } + break; + case PROPERTY_MAP_MAILMERGE : + { + static SfxItemPropertyMapEntry const aMailMergeMap[] = + { + { OUString(UNO_NAME_SELECTION), WID_SELECTION, cppu::UnoType< cppu::UnoSequenceType<css::uno::Any> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_RESULT_SET), WID_RESULT_SET, cppu::UnoType<css::sdbc::XResultSet>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CONNECTION), WID_CONNECTION, cppu::UnoType<css::sdbc::XConnection>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_MODEL), WID_MODEL, cppu::UnoType<css::frame::XModel>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_DATA_SOURCE_NAME), WID_DATA_SOURCE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DAD_COMMAND), WID_DATA_COMMAND, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_FILTER), WID_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DOCUMENT_URL), WID_DOCUMENT_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_OUTPUT_URL), WID_OUTPUT_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DAD_COMMAND_TYPE), WID_DATA_COMMAND_TYPE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_OUTPUT_TYPE), WID_OUTPUT_TYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_ESCAPE_PROCESSING), WID_ESCAPE_PROCESSING, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SINGLE_PRINT_JOBS), WID_SINGLE_PRINT_JOBS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_FILE_NAME_FROM_COLUMN), WID_FILE_NAME_FROM_COLUMN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_FILE_NAME_PREFIX), WID_FILE_NAME_PREFIX, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SUBJECT), WID_MAIL_SUBJECT, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_ADDRESS_FROM_COLUMN), WID_ADDRESS_FROM_COLUMN, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEND_AS_HTML), WID_SEND_AS_HTML, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEND_AS_ATTACHMENT), WID_SEND_AS_ATTACHMENT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_MAIL_BODY), WID_MAIL_BODY, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_ATTACHMENT_NAME), WID_ATTACHMENT_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_ATTACHMENT_FILTER), WID_ATTACHMENT_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PRINT_OPTIONS), WID_PRINT_OPTIONS, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SAVE_AS_SINGLE_FILE), WID_SAVE_AS_SINGLE_FILE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SAVE_FILTER), WID_SAVE_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SAVE_FILTER_OPTIONS), WID_SAVE_FILTER_OPTIONS, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SAVE_FILTER_DATA), WID_SAVE_FILTER_DATA, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_COPIES_TO), WID_COPIES_TO, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_BLIND_COPIES_TO), WID_BLIND_COPIES_TO, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IN_SERVER_PASSWORD), WID_IN_SERVER_PASSWORD, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_OUT_SERVER_PASSWORD), WID_OUT_SERVER_PASSWORD, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aMailMergeMap; + } + break; + case PROPERTY_MAP_TEXT_VIEW : + { + static SfxItemPropertyMapEntry pTextViewMap[] = + { + {OUString(UNO_NAME_PAGE_COUNT), WID_PAGE_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_LINE_COUNT), WID_LINE_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_IS_CONSTANT_SPELLCHECK), WID_IS_CONSTANT_SPELLCHECK, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + {OUString(UNO_NAME_IS_HIDE_SPELL_MARKS), WID_IS_HIDE_SPELL_MARKS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, // deprecated #i91949 + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = pTextViewMap; + } + break; + case PROPERTY_MAP_CHART2_DATA_SEQUENCE : + { + static SfxItemPropertyMapEntry const aChart2DataSequenceMap[] = + { + {OUString(UNO_NAME_ROLE), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aChart2DataSequenceMap; + } + break; + case PROPERTY_MAP_METAFIELD: + { + static SfxItemPropertyMapEntry const aMetaFieldMap[] = + { + { OUString(UNO_NAME_NUMBER_FORMAT), 0, + cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_IS_FIXED_LANGUAGE), 0, + cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aMetaFieldMap; + } + break; + case PROPERTY_MAP_TABLE_STYLE: + { + static SfxItemPropertyMapEntry const aTableStyleMap[] = + { + { OUString(UNO_NAME_TABLE_FIRST_ROW_END_COLUMN), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_TABLE_FIRST_ROW_START_COLUMN), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_TABLE_LAST_ROW_END_COLUMN), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_TABLE_LAST_ROW_START_COLUMN), 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aTableStyleMap; + } + break; + case PROPERTY_MAP_CELL_STYLE: + { + static SfxItemPropertyMapEntry const aCellStyleMap[] = + { + // SvxBrushItem + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 }, + // SvxBoxItem + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + // SwFormatVertOrient + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_VERTORIENT_ORIENT }, + // SvxFrameDirectionItem + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + // SvNumberformat + { OUString(UNO_NAME_NUMBER_FORMAT), RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(),PropertyAttribute::MAYBEVOID, 0 }, + // SvxAdjustItem + { OUString(UNO_NAME_PARA_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST }, + // SvxColorItem + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 }, + // SvxShadowedItem + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + // SvxContouredItem + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + // SvxCrossedOutItem + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::MAYBEVOID, MID_CROSS_OUT }, + // SvxUnderlineItem + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE }, + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE,cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR }, + { OUString(UNO_NAME_CHAR_UNDERLINE_HAS_COLOR), RES_CHRATR_UNDERLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR }, + // standard font + // SvxFontHeightItem + { OUString(UNO_NAME_CHAR_HEIGHT), RES_CHRATR_FONTSIZE, cppu::UnoType<float>::get(),PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS }, + // SvxWeightItem + { OUString(UNO_NAME_CHAR_WEIGHT), RES_CHRATR_WEIGHT, cppu::UnoType<float>::get(),PropertyAttribute::MAYBEVOID, MID_WEIGHT }, + // SvxPostureItem + { OUString(UNO_NAME_CHAR_POSTURE), RES_CHRATR_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(),PropertyAttribute::MAYBEVOID, MID_POSTURE }, + // SvxFontItem + { OUString(UNO_NAME_CHAR_FONT_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + // cjk font + { OUString(UNO_NAME_CHAR_HEIGHT_ASIAN), RES_CHRATR_CJK_FONTSIZE, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_WEIGHT_ASIAN), RES_CHRATR_CJK_WEIGHT, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT }, + { OUString(UNO_NAME_CHAR_POSTURE_ASIAN), RES_CHRATR_CJK_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE }, + { OUString(UNO_NAME_CHAR_FONT_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + // ctl font + { OUString(UNO_NAME_CHAR_HEIGHT_COMPLEX), RES_CHRATR_CTL_FONTSIZE, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_WEIGHT_COMPLEX), RES_CHRATR_CTL_WEIGHT, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT }, + { OUString(UNO_NAME_CHAR_POSTURE_COMPLEX), RES_CHRATR_CTL_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE }, + { OUString(UNO_NAME_CHAR_FONT_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, + { OUString(UNO_NAME_CHAR_FONT_FAMILY_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, + { OUString(UNO_NAME_CHAR_FONT_PITCH_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + m_aMapEntriesArr[nPropertyId] = aCellStyleMap; + } + 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..909f183bb --- /dev/null +++ b/sw/source/core/unocore/unomap1.cxx @@ -0,0 +1,1665 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svxids.hrc> +#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/colritem.hxx> +#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; + } +} + +SwUnoPropertyMapProvider::~SwUnoPropertyMapProvider() +{ +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextCursorPropertyMap() +{ + static SfxItemPropertyMapEntry const aCharAndParaMap_Impl[] = + { + COMPLETE_TEXT_CURSOR_MAP + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aCharAndParaMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAccessibilityTextAttrPropertyMap() +{ + static SfxItemPropertyMapEntry const aAccessibilityTextAttrMap_Impl[] = + { + COMMON_ACCESSIBILITY_TEXT_ATTRIBUTE + { OUString(), 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 + { OUString(UNO_NAME_CHAR_STYLE_NAME), RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(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 + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aParagraphMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAutoParaStylePropertyMap() +{ + static SfxItemPropertyMapEntry const aAutoParaStyleMap [] = + { + { OUString(UNO_NAME_PARA_STYLE_NAME), RES_FRMATR_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_PAGE_STYLE_NAME), FN_UNO_PAGE_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_NUMBERING_IS_NUMBER), FN_UNO_IS_NUMBER, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_NUMBERING_LEVEL), FN_UNO_NUM_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_NUMBERING_START_VALUE), FN_UNO_NUM_START_VALUE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, + { OUString(UNO_NAME_DOCUMENT_INDEX), FN_UNO_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_TEXT_TABLE), FN_UNO_TEXT_TABLE, cppu::UnoType<css::text::XTextTable>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_CELL), FN_UNO_CELL, cppu::UnoType<css::table::XCell>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_TEXT_FRAME), FN_UNO_TEXT_FRAME, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_TEXT_SECTION), FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_PARA_CHAPTER_NUMBERING_LEVEL), FN_UNO_PARA_CHAPTER_NUMBERING_LEVEL,cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_PARA_CONDITIONAL_STYLE_NAME), RES_FRMATR_CONDITIONAL_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_PARA_IS_NUMBERING_RESTART), FN_NUMBER_NEWSTART, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, + // TODO add RES_PARATR_LIST_AUTOFMT? + { OUString(UNO_NAME_OUTLINE_LEVEL), RES_PARATR_OUTLINELEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, + COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN + TABSTOPS_MAP_ENTRY + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(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 + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aAutoParaStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetCharStylePropertyMap() +{ + static SfxItemPropertyMapEntry const aCharStyleMap [] = + { + { OUString(UNO_NAME_CHAR_AUTO_KERNING), RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_BACK_TRANSPARENT), RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_CHAR_BACK_COLOR), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_CHAR_HIGHLIGHT), RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_BACK_COLOR }, + { OUString(UNO_NAME_CHAR_CASE_MAP), RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_TRANSPARENCE), RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA}, + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT}, + { OUString(UNO_NAME_CHAR_CROSSED_OUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC }, + { OUString(UNO_NAME_CHAR_ESCAPEMENT_HEIGHT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT}, + { OUString(UNO_NAME_CHAR_FLASH), RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_HIDDEN), RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + STANDARD_FONT_PROPERTIES + CJK_FONT_PROPERTIES + CTL_FONT_PROPERTIES + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE}, + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR}, + { OUString(UNO_NAME_CHAR_UNDERLINE_HAS_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR}, + { OUString(UNO_NAME_CHAR_OVERLINE), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE}, + { OUString(UNO_NAME_CHAR_OVERLINE_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR}, + { OUString(UNO_NAME_CHAR_OVERLINE_HAS_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR}, + { OUString(UNO_NAME_CHAR_KERNING), RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_CHAR_NO_HYPHENATION), RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_WORD_MODE), RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_HIDDEN), FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_STYLE_INTEROP_GRAB_BAG), FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_CHAR_COMBINE_IS_ON), RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES}, + { OUString(UNO_NAME_CHAR_COMBINE_PREFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET}, + { OUString(UNO_NAME_CHAR_COMBINE_SUFFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET}, + { OUString(UNO_NAME_CHAR_EMPHASIS), RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS}, + PROP_DIFF_FONTHEIGHT + { OUString(UNO_NAME_CHAR_ROTATION), RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE }, + { OUString(UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE), RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE }, + { OUString(UNO_NAME_CHAR_SCALE_WIDTH), RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_CHAR_RELIEF), RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF }, + { OUString(UNO_NAME_CHAR_LEFT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_RIGHT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_TOP_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_LEFT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_TOP_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_SHADOW_FORMAT), RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aCharStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAutoCharStylePropertyMap() +{ + // same as PROPERTY_MAP_TEXTPORTION_EXTENSIONS + static SfxItemPropertyMapEntry const aAutoCharStyleMap [] = + { + { OUString(UNO_NAME_CHAR_AUTO_KERNING), RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_BACK_TRANSPARENT), RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_CHAR_BACK_COLOR), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_CHAR_HIGHLIGHT), RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_BACK_COLOR }, + { OUString(UNO_NAME_CHAR_CASE_MAP), RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_TRANSPARENCE), RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA}, + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT}, + { OUString(UNO_NAME_CHAR_CROSSED_OUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC }, + { OUString(UNO_NAME_CHAR_ESCAPEMENT_HEIGHT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT}, + { OUString(UNO_NAME_CHAR_FLASH), RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_HIDDEN), RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + STANDARD_FONT_PROPERTIES + CJK_FONT_PROPERTIES + CTL_FONT_PROPERTIES + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE}, + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR}, + { OUString(UNO_NAME_CHAR_UNDERLINE_HAS_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR}, + { OUString(UNO_NAME_CHAR_OVERLINE), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE}, + { OUString(UNO_NAME_CHAR_OVERLINE_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR}, + { OUString(UNO_NAME_CHAR_OVERLINE_HAS_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR}, + { OUString(UNO_NAME_CHAR_KERNING), RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_CHAR_NO_HYPHENATION), RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_WORD_MODE), RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES), RES_TXTATR_UNKNOWN_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_CHAR_COMBINE_IS_ON), RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES}, + { OUString(UNO_NAME_CHAR_COMBINE_PREFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET}, + { OUString(UNO_NAME_CHAR_COMBINE_SUFFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET}, + { OUString(UNO_NAME_CHAR_EMPHASIS), RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS}, + { OUString(UNO_NAME_CHAR_ROTATION), RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE }, + { OUString(UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE), RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE }, + { OUString(UNO_NAME_CHAR_SCALE_WIDTH), RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_CHAR_RELIEF), RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF }, + { OUString(UNO_NAME_CHAR_AUTO_STYLE_NAME), RES_TXTATR_AUTOFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_CHAR_SHADING_VALUE), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, + { OUString(UNO_NAME_CHAR_LEFT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_RIGHT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_TOP_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_LEFT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_TOP_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_CHAR_SHADOW_FORMAT), RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(), 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 + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aParaStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetConditionalParaStylePropertyMap() +{ + static SfxItemPropertyMapEntry const aParaStyleMap [] = + { + COMMON_PARA_STYLE_PROPERTIES + { OUString(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 + + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aParaStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFrameStylePropertyMap() +{ + static SfxItemPropertyMapEntry const aFrameStyleMap [] = + { + { OUString(UNO_NAME_ANCHOR_PAGE_NO), RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ANCHOR_PAGENUM }, + { OUString(UNO_NAME_ANCHOR_TYPE), RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE, MID_ANCHOR_ANCHORTYPE}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_COLOR_R_G_B), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR_R_G_B}, + { OUString(UNO_NAME_BACK_COLOR_TRANSPARENCY), RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_BACK_COLOR_TRANSPARENCY}, + { OUString(UNO_NAME_FRAME_INTEROP_GRAB_BAG), RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, + // { OUString(UNO_NAME_CHAIN_NEXT_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_CHAIN_NEXTNAME}, + // { OUString(UNO_NAME_CHAIN_PREV_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_CHAIN_PREVNAME}, + /*not impl*/ { OUString(UNO_NAME_CLIENT_MAP), RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_CLIENTMAP }, + { OUString(UNO_NAME_CONTENT_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_CONTENT }, + { OUString(UNO_NAME_EDIT_IN_READONLY), RES_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(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 + { OUString(UNO_NAME_BACK_GRAPHIC_TRANSPARENCY), RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENCY}, + { OUString(UNO_NAME_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_HORI_ORIENT), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT }, + { OUString(UNO_NAME_HORI_ORIENT_POSITION), RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_HORIORIENT_POSITION|CONVERT_TWIPS }, + { OUString(UNO_NAME_HORI_ORIENT_RELATION), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_RELATION }, + { OUString(UNO_NAME_HYPER_LINK_U_R_L), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_URL}, + { OUString(UNO_NAME_HYPER_LINK_TARGET), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_TARGET}, + { OUString(UNO_NAME_HYPER_LINK_NAME), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_HYPERLINKNAME }, + { OUString(UNO_NAME_OPAQUE), RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PAGE_TOGGLE), RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE }, + { OUString(UNO_NAME_POSITION_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_POSITION}, + { OUString(UNO_NAME_PRINT), RES_PRINT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEIGHT), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_RELATIVE_HEIGHT), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT }, + { OUString(UNO_NAME_RELATIVE_HEIGHT_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION }, + { OUString(UNO_NAME_RELATIVE_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, + { OUString(UNO_NAME_RELATIVE_WIDTH_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION }, + { OUString(UNO_NAME_SIZE_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE }, + { OUString(UNO_NAME_WIDTH_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE }, + { OUString(UNO_NAME_SIZE), RES_FRM_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_FRMSIZE_SIZE|CONVERT_TWIPS}, + { OUString(UNO_NAME_IS_SYNC_WIDTH_TO_HEIGHT), RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT }, + { OUString(UNO_NAME_IS_SYNC_HEIGHT_TO_WIDTH), RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH }, + // { OUString(UNO_NAME_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH }, + { OUString(UNO_NAME_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_SHADOW_TRANSPARENCE), RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE}, + { OUString(UNO_NAME_SERVER_MAP), RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_SERVERMAP }, + { OUString(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 + { OUString(UNO_NAME_SURROUND), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, + { OUString(UNO_NAME_TEXT_WRAP), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, + { OUString(UNO_NAME_SURROUND_ANCHORONLY), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_ANCHORONLY }, + { OUString(UNO_NAME_SURROUND_CONTOUR), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR }, + { OUString(UNO_NAME_CONTOUR_OUTSIDE), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE }, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, + { OUString(UNO_NAME_VERT_ORIENT_POSITION), RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_VERTORIENT_POSITION|CONVERT_TWIPS }, + { OUString(UNO_NAME_VERT_ORIENT_RELATION), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_RELATION }, + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_IS_AUTO_UPDATE), FN_UNO_IS_AUTO_UPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + // #i18732# + { OUString(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW}, + // #i28701# + { OUString(UNO_NAME_WRAP_INFLUENCE_ON_POSITION), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE}, + { OUString(UNO_NAME_ALLOW_OVERLAP), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP}, + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_HIDDEN), FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(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 + + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aFrameStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetPageStylePropertyMap() +{ + static SfxItemPropertyMapEntry const aPageStyleMap [] = + { + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(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 + { OUString(UNO_NAME_HEADER_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_HEADER_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_HEADER_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_HEADER_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_HEADER_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_HEADER_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_HEADER_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_HEADER_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_HEADER_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_HEADER_BODY_DISTANCE), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_LO_MARGIN|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT), SID_ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_HEADER_IS_SHARED), SID_ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_HEADER_HEIGHT), SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_SIZE_HEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_HEADER_IS_ON), SID_ATTR_PAGE_ON, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_HEADER_DYNAMIC_SPACING), RES_HEADER_FOOTER_EAT_SPACING, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID ,0 }, + + + { OUString(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 + { OUString(UNO_NAME_FOOTER_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_FOOTER_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_FOOTER_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_FOOTER_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_FOOTER_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_FOOTER_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_FOOTER_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_FOOTER_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_FOOTER_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, + { OUString(UNO_NAME_FOOTER_BODY_DISTANCE), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_UP_MARGIN|CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_IS_DYNAMIC_HEIGHT), SID_ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_FOOTER_IS_SHARED), SID_ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_FOOTER_HEIGHT), SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_SIZE_HEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTER_IS_ON), SID_ATTR_PAGE_ON, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 }, + { OUString(UNO_NAME_FOOTER_DYNAMIC_SPACING), RES_HEADER_FOOTER_EAT_SPACING, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID ,0 }, + + { OUString(UNO_NAME_IS_LANDSCAPE), SID_ATTR_PAGE, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_PAGE_ORIENTATION }, + { OUString(UNO_NAME_NUMBERING_TYPE), SID_ATTR_PAGE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_PAGE_NUMTYPE }, + { OUString(UNO_NAME_PAGE_STYLE_LAYOUT), SID_ATTR_PAGE, cppu::UnoType<css::style::PageStyleLayout>::get(), PROPERTY_NONE ,MID_PAGE_LAYOUT }, + { OUString(UNO_NAME_PRINTER_PAPER_TRAY), RES_PAPER_BIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE , 0 }, +// { OUString(UNO_NAME_REGISTER_MODE_ACTIVE), SID_SWREGISTER_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE , 0 }, + { OUString(UNO_NAME_REGISTER_PARAGRAPH_STYLE), SID_SWREGISTER_COLLECTION, cppu::UnoType<OUString>::get(), PROPERTY_NONE , 0 }, + { OUString(UNO_NAME_SIZE), SID_ATTR_PAGE_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_SIZE_SIZE|CONVERT_TWIPS}, + { OUString(UNO_NAME_WIDTH), SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_SIZE_WIDTH|CONVERT_TWIPS}, + { OUString(UNO_NAME_HEIGHT), SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_SIZE_HEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_TEXT_VERT_ADJUST), RES_TEXT_VERT_ADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_HEADER_TEXT), FN_UNO_HEADER, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_HEADER_TEXT_LEFT), FN_UNO_HEADER_LEFT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_HEADER_TEXT_RIGHT), FN_UNO_HEADER_RIGHT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_HEADER_TEXT_FIRST), FN_UNO_HEADER_FIRST, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOOTER_TEXT), FN_UNO_FOOTER, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOOTER_TEXT_LEFT), FN_UNO_FOOTER_LEFT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOOTER_TEXT_RIGHT), FN_UNO_FOOTER_RIGHT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOOTER_TEXT_FIRST), FN_UNO_FOOTER_FIRST, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOLLOW_STYLE), FN_UNO_FOLLOW_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_FOOTNOTE_HEIGHT), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_FTN_HEIGHT|CONVERT_TWIPS}, + { OUString(UNO_NAME_FOOTNOTE_LINE_WEIGHT), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_LINE_WEIGHT|CONVERT_TWIPS}, + { OUString(UNO_NAME_FOOTNOTE_LINE_COLOR), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_COLOR}, + { OUString(UNO_NAME_FOOTNOTE_LINE_STYLE), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE , MID_FTN_LINE_STYLE}, + { OUString(UNO_NAME_FOOTNOTE_LINE_RELATIVE_WIDTH), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE , MID_LINE_RELWIDTH }, + { OUString(UNO_NAME_FOOTNOTE_LINE_ADJUST), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_LINE_ADJUST }, + { OUString(UNO_NAME_FOOTNOTE_LINE_TEXT_DISTANCE), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_TEXT_DIST |CONVERT_TWIPS }, + { OUString(UNO_NAME_FOOTNOTE_LINE_DISTANCE), FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_FOOTNOTE_DIST|CONVERT_TWIPS}, + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + // writing grid + { OUString(UNO_NAME_GRID_COLOR), RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_COLOR}, + { OUString(UNO_NAME_GRID_LINES), RES_TEXTGRID, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_GRID_LINES}, + { OUString(UNO_NAME_GRID_BASE_HEIGHT), RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_BASEHEIGHT|CONVERT_TWIPS}, + { OUString(UNO_NAME_GRID_RUBY_HEIGHT), RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_RUBYHEIGHT|CONVERT_TWIPS}, + { OUString(UNO_NAME_GRID_MODE), RES_TEXTGRID, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_GRID_TYPE}, + { OUString(UNO_NAME_GRID_RUBY_BELOW), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_RUBY_BELOW}, + { OUString(UNO_NAME_GRID_PRINT), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_PRINT}, + { OUString(UNO_NAME_GRID_DISPLAY), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_DISPLAY}, + { OUString(UNO_NAME_GRID_BASE_WIDTH), RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_BASEWIDTH|CONVERT_TWIPS}, + { OUString(UNO_NAME_GRID_SNAP_TO_CHARS), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_SNAPTOCHARS}, + { OUString(UNO_NAME_GRID_STANDARD_PAGE_MODE), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_STANDARD_MODE}, + { OUString(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 + + // 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' + { OUString(UNO_NAME_HEADER_FILLBMP_LOGICAL_SIZE), XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_OFFSET_X), XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_OFFSET_Y), XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_POSITION_OFFSET_X), XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_POSITION_OFFSET_Y), XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_RECTANGLE_POINT), XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_SIZE_X), XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM}, + { OUString(UNO_NAME_HEADER_FILLBMP_SIZE_Y), XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM}, + { OUString(UNO_NAME_HEADER_FILLBMP_STRETCH), XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_TILE), XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBMP_MODE), OWN_ATTR_FILLBMP_MODE, cppu::UnoType<css::drawing::BitmapMode>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILLCOLOR), XATTR_FILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBACKGROUND), XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILLBITMAP), XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP}, + { OUString(UNO_NAME_HEADER_FILLBITMAPNAME), XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_HEADER_FILLGRADIENTSTEPCOUNT), XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILLGRADIENT), XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, + { OUString(UNO_NAME_HEADER_FILLGRADIENTNAME), XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_HEADER_FILLHATCH), XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH}, + { OUString(UNO_NAME_HEADER_FILLHATCHNAME), XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_HEADER_FILLSTYLE), XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILL_TRANSPARENCE), XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_HEADER_FILLTRANSPARENCEGRADIENT), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, + { OUString(UNO_NAME_HEADER_FILLTRANSPARENCEGRADIENTNAME), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(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) + { OUString(UNO_NAME_FOOTER_FILLBMP_LOGICAL_SIZE), XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_OFFSET_X), XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_OFFSET_Y), XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_POSITION_OFFSET_X), XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_POSITION_OFFSET_Y), XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_RECTANGLE_POINT), XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_SIZE_X), XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM}, + { OUString(UNO_NAME_FOOTER_FILLBMP_SIZE_Y), XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM}, + { OUString(UNO_NAME_FOOTER_FILLBMP_STRETCH), XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_TILE), XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get() , 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBMP_MODE), OWN_ATTR_FILLBMP_MODE, cppu::UnoType<css::drawing::BitmapMode>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLCOLOR), XATTR_FILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBACKGROUND), XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLBITMAP), XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP}, + { OUString(UNO_NAME_FOOTER_FILLBITMAPNAME), XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_FOOTER_FILLGRADIENTSTEPCOUNT), XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLGRADIENT), XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, + { OUString(UNO_NAME_FOOTER_FILLGRADIENTNAME), XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_FOOTER_FILLHATCH), XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH}, + { OUString(UNO_NAME_FOOTER_FILLHATCHNAME), XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_FOOTER_FILLSTYLE), XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILL_TRANSPARENCE), XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_FOOTER_FILLTRANSPARENCEGRADIENT), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, + { OUString(UNO_NAME_FOOTER_FILLTRANSPARENCEGRADIENTNAME), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME }, + { OUString(UNO_NAME_FOOTER_FILLCOLOR_2), XATTR_SECONDARYFILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0}, + + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aPageStyleMap; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTablePropertyMap() +{ + static SfxItemPropertyMapEntry const aTablePropertyMap_Impl[] = + { + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE,MID_BACK_COLOR }, + { OUString(UNO_NAME_BREAK_TYPE), RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_HORI_ORIENT), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT }, + { OUString(UNO_NAME_KEEP_TOGETHER), RES_KEEP, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SPLIT), RES_LAYOUT_SPLIT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PAGE_NUMBER_OFFSET), RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET}, + { OUString(UNO_NAME_PAGE_DESC_NAME), RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0xbf}, + { OUString(UNO_NAME_RELATIVE_WIDTH), FN_TABLE_RELATIVE_WIDTH,cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0xbf }, + { OUString(UNO_NAME_REPEAT_HEADLINE), FN_TABLE_HEADLINE_REPEAT,cppu::UnoType<bool>::get(), PROPERTY_NONE, 0xbf}, + { OUString(UNO_NAME_HEADER_ROW_COUNT), FN_TABLE_HEADLINE_COUNT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0xbf}, + { OUString(UNO_NAME_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SHADOW_TRANSPARENCE), RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE}, + { OUString(UNO_NAME_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_WIDTH), FN_TABLE_WIDTH, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, 0xbf}, + { OUString(UNO_NAME_IS_WIDTH_RELATIVE), FN_TABLE_IS_RELATIVE_WIDTH, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0xbf}, + { OUString(UNO_NAME_CHART_ROW_AS_LABEL), FN_UNO_RANGE_ROW_LABEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHART_COLUMN_AS_LABEL), FN_UNO_RANGE_COL_LABEL, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TABLE_BORDER), FN_UNO_TABLE_BORDER, cppu::UnoType<css::table::TableBorder>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, + { OUString(UNO_NAME_TABLE_BORDER2), FN_UNO_TABLE_BORDER2, cppu::UnoType<css::table::TableBorder2>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, + { OUString(UNO_NAME_TABLE_BORDER_DISTANCES), FN_UNO_TABLE_BORDER_DISTANCES, cppu::UnoType<css::table::TableBorderDistances>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, + { OUString(UNO_NAME_TABLE_COLUMN_SEPARATORS), FN_UNO_TABLE_COLUMN_SEPARATORS, cppu::UnoType< cppu::UnoSequenceType<css::text::TableColumnSeparator> >::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_TABLE_COLUMN_RELATIVE_SUM), FN_UNO_TABLE_COLUMN_RELATIVE_SUM, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::READONLY, 0 }, + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(UNO_LINK_DISPLAY_NAME), FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_TEXT_SECTION), FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_TABLE_NAME), FN_UNO_TABLE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_PAGE_STYLE_NAME), RES_PAGEDESC, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TABLE_TEMPLATE_NAME), FN_UNO_TABLE_TEMPLATE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + // #i29550# + { OUString(UNO_NAME_COLLAPSING_BORDERS), RES_COLLAPSING_BORDERS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + REDLINE_NODE_PROPERTIES + { OUString(UNO_NAME_TABLE_INTEROP_GRAB_BAG), RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, + { OUString(), 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 + { OUString(UNO_NAME_BACK_COLOR), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_TRANSPARENT), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_NUMBER_FORMAT), RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 }, + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, + { OUString(UNO_NAME_CHART_ROW_AS_LABEL), FN_UNO_RANGE_ROW_LABEL, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_CHART_COLUMN_AS_LABEL), FN_UNO_RANGE_COL_LABEL, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aRangePropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetSectionPropertyMap() +{ + static SfxItemPropertyMapEntry const aSectionPropertyMap_Impl[] = + { + { OUString(UNO_NAME_CONDITION), WID_SECT_CONDITION, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DDE_COMMAND_FILE), WID_SECT_DDE_TYPE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DDE_COMMAND_TYPE), WID_SECT_DDE_FILE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DDE_COMMAND_ELEMENT), WID_SECT_DDE_ELEMENT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_AUTOMATIC_UPDATE), WID_SECT_DDE_AUTOUPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_FILE_LINK), WID_SECT_LINK , cppu::UnoType<css::text::SectionFileLink>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_VISIBLE), WID_SECT_VISIBLE , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_PROTECTED), WID_SECT_PROTECTED, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_EDIT_IN_READONLY), WID_SECT_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LINK_REGION), WID_SECT_REGION , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_LINK_DISPLAY_NAME), FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_FOOTNOTE_IS_COLLECT_AT_TEXT_END), RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_COLLECT }, + { OUString(UNO_NAME_FOOTNOTE_IS_RESTART_NUMBERING), RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_RESTART_NUM }, + { OUString(UNO_NAME_FOOTNOTE_RESTART_NUMBERING_AT), RES_FTN_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_START_AT}, + { OUString(UNO_NAME_FOOTNOTE_IS_OWN_NUMBERING), RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_OWN_NUM }, + { OUString(UNO_NAME_FOOTNOTE_NUMBERING_TYPE), RES_FTN_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_TYPE }, + { OUString(UNO_NAME_FOOTNOTE_NUMBERING_PREFIX), RES_FTN_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_PREFIX }, + { OUString(UNO_NAME_FOOTNOTE_NUMBERING_SUFFIX), RES_FTN_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_SUFFIX }, + { OUString(UNO_NAME_ENDNOTE_IS_COLLECT_AT_TEXT_END), RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_COLLECT }, + { OUString(UNO_NAME_ENDNOTE_IS_RESTART_NUMBERING), RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_RESTART_NUM }, + { OUString(UNO_NAME_ENDNOTE_RESTART_NUMBERING_AT), RES_END_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_START_AT }, + { OUString(UNO_NAME_ENDNOTE_IS_OWN_NUMBERING), RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_OWN_NUM }, + { OUString(UNO_NAME_ENDNOTE_NUMBERING_TYPE), RES_END_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_NUM_TYPE }, + { OUString(UNO_NAME_ENDNOTE_NUMBERING_PREFIX), RES_END_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_PREFIX }, + { OUString(UNO_NAME_ENDNOTE_NUMBERING_SUFFIX), RES_END_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_SUFFIX }, + { OUString(UNO_NAME_DOCUMENT_INDEX), WID_SECT_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::READONLY | PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_GLOBAL_DOCUMENT_SECTION), WID_SECT_IS_GLOBAL_DOC_SECTION, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0 }, + { OUString(UNO_NAME_PROTECTION_KEY), WID_SECT_PASSWORD, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_DONT_BALANCE_TEXT_COLUMNS), RES_COLUMNBALANCE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + COMMON_TEXT_CONTENT_PROPERTIES + REDLINE_NODE_PROPERTIES + { OUString(UNO_NAME_IS_CURRENTLY_VISIBLE), WID_SECT_CURRENTLY_VISIBLE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_SECT_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_L_MARGIN|CONVERT_TWIPS}, + { OUString(UNO_NAME_SECT_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS}, + { OUString(), 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 + { OUString(UNO_NAME_CHAIN_NEXT_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NEXTNAME}, + { OUString(UNO_NAME_CHAIN_PREV_NAME), RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_PREVNAME}, + /*not impl*/ { OUString(UNO_NAME_CLIENT_MAP), RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_CLIENTMAP }, + { OUString(UNO_NAME_EDIT_IN_READONLY), RES_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_COLUMNS), RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS}, + //next elements are part of the service description + { OUString(UNO_NAME_FRAME_HEIGHT_ABSOLUTE), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS }, + { OUString(UNO_NAME_FRAME_HEIGHT_PERCENT), RES_FRM_SIZE, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT }, + { OUString(UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT), RES_FRM_SIZE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FRMSIZE_IS_AUTO_HEIGHT }, + { OUString(UNO_NAME_FRAME_WIDTH_ABSOLUTE), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS }, + { OUString(UNO_NAME_FRAME_WIDTH_PERCENT), RES_FRM_SIZE, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, + { OUString(UNO_NAME_SIZE_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE }, + { OUString(UNO_NAME_WIDTH_TYPE), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE }, + { OUString(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 + + { OUString(), 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 + { OUString(UNO_NAME_SURROUND_CONTOUR), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR }, + { OUString(UNO_NAME_CONTOUR_OUTSIDE), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE }, + { OUString(UNO_NAME_GRAPHIC_CROP), RES_GRFATR_CROPGRF, cppu::UnoType<css::text::GraphicCrop>::get(), PROPERTY_NONE, CONVERT_TWIPS }, + { OUString(UNO_NAME_HORI_MIRRORED_ON_EVEN_PAGES), RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_HORZ_EVEN_PAGES }, + { OUString(UNO_NAME_HORI_MIRRORED_ON_ODD_PAGES), RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_HORZ_ODD_PAGES }, + { OUString(UNO_NAME_VERT_MIRRORED), RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_VERT }, + { OUString(UNO_NAME_REPLACEMENT_GRAPHIC), FN_UNO_REPLACEMENT_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 }, + { OUString(UNO_NAME_GRAPHIC_FILTER), FN_UNO_GRAPHIC_FILTER, cppu::UnoType<OUString>::get(), 0, 0 }, + { OUString(UNO_NAME_GRAPHIC), FN_UNO_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 }, + { OUString(UNO_NAME_GRAPHIC_URL), FN_UNO_GRAPHIC_URL, cppu::UnoType<css::uno::Any>::get(), 0, 0 }, + { OUString(UNO_NAME_TRANSFORMED_GRAPHIC), FN_UNO_TRANSFORMED_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 }, + { OUString(UNO_NAME_ACTUAL_SIZE), FN_UNO_ACTUAL_SIZE, cppu::UnoType<css::awt::Size>::get(), PropertyAttribute::READONLY, CONVERT_TWIPS}, + { OUString(UNO_NAME_CONTOUR_POLY_POLYGON), FN_PARAM_CONTOUR_PP, cppu::UnoType<css::drawing::PointSequenceSequence>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PIXEL_CONTOUR), FN_UNO_IS_PIXEL_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_IS_AUTOMATIC_CONTOUR), FN_UNO_IS_AUTOMATIC_CONTOUR , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_GRAPHIC_ROTATION), RES_GRFATR_ROTATION, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_ADJUST_LUMINANCE), RES_GRFATR_LUMINANCE, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_ADJUST_CONTRAST), RES_GRFATR_CONTRAST, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_ADJUST_RED), RES_GRFATR_CHANNELR, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_ADJUST_GREEN), RES_GRFATR_CHANNELG, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_ADJUST_BLUE), RES_GRFATR_CHANNELB, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(UNO_NAME_GAMMA), RES_GRFATR_GAMMA, cppu::UnoType<double>::get(), 0, 0}, + { OUString(UNO_NAME_GRAPHIC_IS_INVERTED), RES_GRFATR_INVERT, cppu::UnoType<bool>::get(), 0, 0}, + { OUString(UNO_NAME_TRANSPARENCY), RES_GRFATR_TRANSPARENCY, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString(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 + + { OUString(), 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 + { OUString(UNO_NAME_SURROUND_CONTOUR), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR }, + { OUString(UNO_NAME_CONTOUR_OUTSIDE), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE}, + { OUString(UNO_NAME_CONTOUR_POLY_POLYGON), FN_PARAM_CONTOUR_PP, cppu::UnoType<css::drawing::PointSequenceSequence>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_IS_PIXEL_CONTOUR), FN_UNO_IS_PIXEL_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_IS_AUTOMATIC_CONTOUR), FN_UNO_IS_AUTOMATIC_CONTOUR , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_CLSID), FN_UNO_CLSID, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_STREAM_NAME), FN_UNO_STREAM_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_MODEL), FN_UNO_MODEL, cppu::UnoType<css::frame::XModel>::get(), PropertyAttribute::READONLY|PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_GRAPHIC_URL), FN_UNO_REPLACEMENT_GRAPHIC_URL, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_GRAPHIC), FN_UNO_REPLACEMENT_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_COMPONENT),FN_UNO_COMPONENT, cppu::UnoType<css::lang::XComponent>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_EMBEDDED_OBJECT),FN_EMBEDDED_OBJECT, cppu::UnoType<css::embed::XEmbeddedObject>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DRAW_ASPECT),FN_UNO_DRAW_ASPECT, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_VISIBLE_AREA_WIDTH),FN_UNO_VISIBLE_AREA_WIDTH, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(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 + + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aEmbeddedPropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetIndexMarkPropertyMap() +{ + static SfxItemPropertyMapEntry const aIdxMarkMap_Impl[] = + { + { OUString(UNO_NAME_ALTERNATIVE_TEXT), WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PRIMARY_KEY), WID_PRIMARY_KEY, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SECONDARY_KEY), WID_SECONDARY_KEY, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_TEXT_READING), WID_TEXT_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PRIMARY_KEY_READING), WID_PRIMARY_KEY_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SECONDARY_KEY_READING), WID_SECONDARY_KEY_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_MAIN_ENTRY), WID_MAIN_ENTRY, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aIdxMarkMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentMarkPropertyMap() +{ + static SfxItemPropertyMapEntry const aContentMarkMap_Impl[] = + { + { OUString(UNO_NAME_ALTERNATIVE_TEXT), WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL), WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aContentMarkMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetUserMarkPropertyMap() +{ + static SfxItemPropertyMapEntry const aUserMarkMap_Impl[] = + { + { OUString(UNO_NAME_ALTERNATIVE_TEXT), WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_LEVEL), WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_USER_INDEX_NAME), WID_USER_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0}, + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(), 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: + { OUString(UNO_NAME_BACK_COLOR), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_BACK_COLOR }, + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, + { OUString(UNO_NAME_NUMBER_FORMAT), RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 }, + { OUString(UNO_NAME_BACK_TRANSPARENT), FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_GRAPHIC_TRANSPARENT }, + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, + { OUString(UNO_NAME_TEXT_SECTION), FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + { OUString(UNO_NAME_IS_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), 0, MID_PROTECT_CONTENT}, + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aTableCursorPropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetBookmarkPropertyMap() +{ + static SfxItemPropertyMapEntry const aBookmarkPropertyMap_Impl [] = + { + { OUString(UNO_LINK_DISPLAY_NAME), FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(UNO_NAME_BOOKMARK_HIDDEN), FN_BOOKMARK_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + { OUString(UNO_NAME_BOOKMARK_CONDITION), FN_BOOKMARK_CONDITION, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aBookmarkPropertyMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetParagraphExtensionsPropertyMap() +{ + static SfxItemPropertyMapEntry const aParagraphExtensionsMap_Impl[] = + { + COMMON_TEXT_CONTENT_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aParagraphExtensionsMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextPortionExtensionPropertyMap() +{ + static SfxItemPropertyMapEntry const aTextPortionExtensionMap_Impl[] = + { + COMPLETE_TEXT_CURSOR_MAP + {OUString(UNO_NAME_BOOKMARK), FN_UNO_BOOKMARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + {OUString(UNO_NAME_CONTROL_CHARACTER), FN_UNO_CONTROL_CHARACTER, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, MID_HYPHEN_MIN_LEAD }, + {OUString(UNO_NAME_IS_COLLAPSED), FN_UNO_IS_COLLAPSED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 }, + {OUString(UNO_NAME_IS_START), FN_UNO_IS_START, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 }, + //REDLINE_PROPERTIES + {OUString(UNO_NAME_TEXT_PORTION_TYPE), FN_UNO_TEXT_PORTION_TYPE, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_META), FN_UNO_META, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aTextPortionExtensionMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFootnotePropertyMap() +{ + static SfxItemPropertyMapEntry const aFootnoteMap_Impl[] = + { + {OUString(UNO_NAME_REFERENCE_ID), 0, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::READONLY|PropertyAttribute::MAYBEVOID, 0}, + COMMON_TEXT_CONTENT_PROPERTIES + REDLINE_NODE_PROPERTIES + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aFootnoteMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetRedlinePropertyMap() +{ + static SfxItemPropertyMapEntry const aRedlineMap_Impl[] = + { + REDLINE_PROPERTIES + REDLINE_NODE_PROPERTIES + {OUString(UNO_NAME_REDLINE_START), 0, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::READONLY, 0}, + {OUString(UNO_NAME_REDLINE_END), 0, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aRedlineMap_Impl; +} + +SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextDefaultPropertyMap() +{ + static SfxItemPropertyMapEntry aTextDefaultMap_Impl[] = + { + { OUString(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 + { OUString(UNO_NAME_CHAR_STYLE_NAME), RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, + { OUString(UNO_NAME_IS_SPLIT_ALLOWED), RES_ROW_SPLIT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, + // #i29550# + { OUString(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 + { OUString(UNO_NAME_GRID_STANDARD_PAGE_MODE), RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_STANDARD_MODE}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + + return aTextDefaultMap_Impl; +} + +const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetRedlinePortionPropertyMap() +{ + static SfxItemPropertyMapEntry const aRedlinePortionMap_Impl[] = + { + COMPLETE_TEXT_CURSOR_MAP + {OUString(UNO_NAME_BOOKMARK), FN_UNO_BOOKMARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, + {OUString(UNO_NAME_CONTROL_CHARACTER), FN_UNO_CONTROL_CHARACTER, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, MID_HYPHEN_MIN_LEAD }, + {OUString(UNO_NAME_IS_COLLAPSED), FN_UNO_IS_COLLAPSED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 }, + {OUString(UNO_NAME_IS_START), FN_UNO_IS_START, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 }, + REDLINE_PROPERTIES + {OUString(UNO_NAME_TEXT_PORTION_TYPE), FN_UNO_TEXT_PORTION_TYPE, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 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_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_TEXT_COLUMS : + { + static SfxItemPropertySet aPROPERTY_MAP_TEXT_COLUMS(pEntries); + m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_COLUMS; + } + 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; + } + } + 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..0b211f392 --- /dev/null +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -0,0 +1,529 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 \ + { OUString(UNO_NAME_CHAR_HEIGHT), RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_CHAR_WEIGHT), RES_CHRATR_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \ + { OUString(UNO_NAME_CHAR_FONT_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_FAMILY), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \ + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \ + { OUString(UNO_NAME_CHAR_FONT_PITCH), RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \ + { OUString(UNO_NAME_CHAR_POSTURE), RES_CHRATR_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \ + { OUString(UNO_NAME_RSID), RES_CHRATR_RSID, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_LOCALE), RES_CHRATR_LANGUAGE, cppu::UnoType<css::lang::Locale>::get(), PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE }, \ + { OUString(UNO_NAME_CHAR_INTEROP_GRAB_BAG), RES_CHRATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, \ + +#define CJK_FONT_PROPERTIES \ + { OUString(UNO_NAME_CHAR_HEIGHT_ASIAN), RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_CHAR_WEIGHT_ASIAN), RES_CHRATR_CJK_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \ + { OUString(UNO_NAME_CHAR_FONT_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_FAMILY_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \ + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \ + { OUString(UNO_NAME_CHAR_FONT_PITCH_ASIAN), RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \ + { OUString(UNO_NAME_CHAR_POSTURE_ASIAN), RES_CHRATR_CJK_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \ + { OUString(UNO_NAME_CHAR_LOCALE_ASIAN), RES_CHRATR_CJK_LANGUAGE , cppu::UnoType<css::lang::Locale>::get() , PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE }, + +#define CTL_FONT_PROPERTIES \ + { OUString(UNO_NAME_CHAR_HEIGHT_COMPLEX), RES_CHRATR_CTL_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_WEIGHT_COMPLEX), RES_CHRATR_CTL_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \ + { OUString(UNO_NAME_CHAR_FONT_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \ + { OUString(UNO_NAME_CHAR_FONT_FAMILY_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \ + { OUString(UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \ + { OUString(UNO_NAME_CHAR_FONT_PITCH_COMPLEX), RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \ + { OUString(UNO_NAME_CHAR_POSTURE_COMPLEX), RES_CHRATR_CTL_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \ + { OUString(UNO_NAME_CHAR_LOCALE_COMPLEX), RES_CHRATR_CTL_LANGUAGE , cppu::UnoType<css::lang::Locale>::get() , PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE }, + +#define REDLINE_NODE_PROPERTIES \ + { OUString(UNO_NAME_START_REDLINE), FN_UNO_REDLINE_NODE_START , cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0xbf }, \ + { OUString(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 \ + {OUString(UNO_NAME_REDLINE_AUTHOR), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_DATE_TIME), 0, cppu::UnoType<css::util::DateTime>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_COMMENT), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_DESCRIPTION), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0}, \ + {OUString(UNO_NAME_REDLINE_TYPE), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_SUCCESSOR_DATA), 0, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_IDENTIFIER), 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_IS_IN_HEADER_FOOTER), 0, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_REDLINE_TEXT), 0, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + {OUString(UNO_NAME_MERGE_LAST_PARA), 0, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, + +#define COMMON_CRSR_PARA_PROPERTIES_FN_ONLY \ + { OUString(UNO_NAME_PARA_STYLE_NAME), FN_UNO_PARA_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_PAGE_STYLE_NAME), FN_UNO_PAGE_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, \ + { OUString(UNO_NAME_NUMBERING_IS_NUMBER), FN_UNO_IS_NUMBER, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_NUMBERING_LEVEL), FN_UNO_NUM_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_NUMBERING_RULES), FN_UNO_NUM_RULES, cppu::UnoType<css::container::XIndexReplace>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + { OUString(UNO_NAME_NUMBERING_START_VALUE), FN_UNO_NUM_START_VALUE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + { OUString(UNO_NAME_DOCUMENT_INDEX), FN_UNO_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_TEXT_TABLE), FN_UNO_TEXT_TABLE, cppu::UnoType<css::text::XTextTable>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_CELL), FN_UNO_CELL, cppu::UnoType<css::table::XCell>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_TEXT_FRAME), FN_UNO_TEXT_FRAME, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_TEXT_SECTION), FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_TEXT_PARAGRAPH), FN_UNO_TEXT_PARAGRAPH, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \ + { OUString(UNO_NAME_PARA_CHAPTER_NUMBERING_LEVEL), FN_UNO_PARA_CHAPTER_NUMBERING_LEVEL,cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_PARA_CONDITIONAL_STYLE_NAME), FN_UNO_PARA_CONDITIONAL_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, \ + { OUString(UNO_NAME_LIST_ID), FN_UNO_LIST_ID, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_PARA_IS_NUMBERING_RESTART), FN_NUMBER_NEWSTART, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE), FN_UNO_PARA_CONT_PREV_SUBTREE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0 }, \ + { OUString(UNO_NAME_PARA_LIST_LABEL_STRING), FN_UNO_PARA_NUM_STRING, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 }, \ + { OUString(UNO_NAME_PARA_LIST_AUTO_FORMAT), FN_UNO_PARA_NUM_AUTO_FORMAT, cppu::UnoType<cppu::UnoSequenceType<css::beans::NamedValue>>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_OUTLINE_LEVEL), RES_PARATR_OUTLINELEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, + +#define COMMON_HYPERLINK_PROPERTIES \ + { OUString(UNO_NAME_HYPER_LINK_U_R_L), RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_URL}, \ + { OUString(UNO_NAME_HYPER_LINK_TARGET), RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_TARGET}, \ + { OUString(UNO_NAME_HYPER_LINK_NAME), RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_HYPERLINKNAME }, \ + { OUString(UNO_NAME_UNVISITED_CHAR_STYLE_NAME), RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_UNVISITED_FMT }, \ + { OUString(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 +// OUString(UNO_NAME_BREAK_TYPE) and OUString(UNO_NAME_PAGE_DESC_NAME) which can not be used +// by the SwXTextTableCursor +#define COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01 \ + { OUString(UNO_NAME_PARRSID), RES_PARATR_RSID, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_IS_HYPHENATION), RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN }, \ + { OUString(UNO_NAME_PARA_HYPHENATION_NO_CAPS), RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS }, \ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD }, \ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL }, \ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS }, \ + { OUString(UNO_NAME_CHAR_AUTO_KERNING), RES_CHRATR_AUTOKERN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_BACK_COLOR), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \ + { OUString(UNO_NAME_CHAR_HIGHLIGHT), RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \ + { OUString(UNO_NAME_PARA_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \ + { OUString(UNO_NAME_CHAR_CASE_MAP), RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_TRANSPARENCE), RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_COLOR_ALPHA }, \ + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT }, \ + { OUString(UNO_NAME_CHAR_CROSSED_OUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_CROSSED_OUT }, \ + { OUString(UNO_NAME_CHAR_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_ESC }, \ + { OUString(UNO_NAME_CHAR_ESCAPEMENT_HEIGHT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, MID_ESC_HEIGHT }, \ + { OUString(UNO_NAME_CHAR_AUTO_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_AUTO_ESC }, \ + { OUString(UNO_NAME_CHAR_FLASH), RES_CHRATR_BLINK, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_HIDDEN), RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE }, \ + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR }, \ + { OUString(UNO_NAME_CHAR_UNDERLINE_HAS_COLOR), RES_CHRATR_UNDERLINE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TL_HASCOLOR }, \ + { OUString(UNO_NAME_CHAR_OVERLINE), RES_CHRATR_OVERLINE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE }, \ + { OUString(UNO_NAME_CHAR_OVERLINE_COLOR), RES_CHRATR_OVERLINE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR }, \ + { OUString(UNO_NAME_CHAR_OVERLINE_HAS_COLOR), RES_CHRATR_OVERLINE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TL_HASCOLOR }, \ + { OUString(UNO_NAME_PARA_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_URL }, \ + { OUString(UNO_NAME_PARA_GRAPHIC), RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC }, \ + { OUString(UNO_NAME_PARA_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_FILTER }, \ + { OUString(UNO_NAME_PARA_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_POSITION }, \ + { OUString(UNO_NAME_PARA_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TXT_LMARGIN | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT), RES_LR_SPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_AUTO }, \ + { OUString(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 \ + { OUString(UNO_NAME_CHAR_KERNING), RES_CHRATR_KERNING, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_NO_HYPHENATION), RES_CHRATR_NOHYPHEN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_DROP_CAP_FORMAT), RES_PARATR_DROP, cppu::UnoType<css::style::DropCapFormat>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_FORMAT | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_DROP_CAP_WHOLE_WORD), RES_PARATR_DROP, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_WHOLE_WORD }, \ + { OUString(UNO_NAME_DROP_CAP_CHAR_STYLE_NAME), RES_PARATR_DROP, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_CHAR_STYLE_NAME }, \ + { OUString(UNO_NAME_PARA_KEEP_TOGETHER), RES_KEEP, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_SPLIT), RES_PARATR_SPLIT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_WIDOWS), RES_PARATR_WIDOWS, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_ORPHANS), RES_PARATR_ORPHANS, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PAGE_NUMBER_OFFSET), RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET }, \ + { OUString(UNO_NAME_PARA_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST }, \ + { OUString(UNO_NAME_PARA_EXPAND_SINGLE_WORD), RES_PARATR_ADJUST, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_EXPAND_SINGLE }, \ + { OUString(UNO_NAME_PARA_LAST_LINE_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_LAST_LINE_ADJUST }, \ + { OUString(UNO_NAME_PARA_LINE_NUMBER_COUNT), RES_LINENUMBER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_COUNT }, \ + { OUString(UNO_NAME_PARA_LINE_NUMBER_START_VALUE), RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_STARTVALUE }, \ + { OUString(UNO_NAME_PARA_LINE_SPACING), RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_REGISTER_MODE_ACTIVE), RES_PARATR_REGISTER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_UP_MARGIN | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LO_MARGIN | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_CONTEXT_MARGIN), RES_UL_SPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_CTX_MARGIN }, \ + { OUString(UNO_NAME_CHAR_BACK_TRANSPARENT), RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT }, \ + { OUString(UNO_NAME_PARA_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT }, \ + { OUString(UNO_NAME_NUMBERING_STYLE_NAME), RES_PARATR_NUMRULE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_WORD_MODE), RES_CHRATR_WORDLINEMODE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_LEFT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_RIGHT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_TOP_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_LEFT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_TOP_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_SHADOW_FORMAT), RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \ + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, \ + { OUString(UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES), RES_TXTATR_UNKNOWN_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS }, \ + { OUString(UNO_NAME_CHAR_COMBINE_IS_ON), RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TWOLINES }, \ + { OUString(UNO_NAME_CHAR_COMBINE_PREFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_START_BRACKET }, \ + { OUString(UNO_NAME_CHAR_COMBINE_SUFFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_END_BRACKET }, \ + { OUString(UNO_NAME_CHAR_EMPHASIS), RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_EMPHASIS }, \ + { OUString(UNO_NAME_PARA_IS_HANGING_PUNCTUATION), RES_PARATR_HANGINGPUNCTUATION, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_IS_CHARACTER_DISTANCE), RES_PARATR_SCRIPTSPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_IS_FORBIDDEN_RULES), RES_PARATR_FORBIDDEN_RULES, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_VERT_ALIGNMENT), RES_PARATR_VERTALIGN, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_CHAR_ROTATION), RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_ROTATE }, \ + { OUString(UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE), RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_FITTOLINE }, \ + { OUString(UNO_NAME_CHAR_SCALE_WIDTH), RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_RUBY_TEXT), RES_TXTATR_CJK_RUBY, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_TEXT }, \ + { OUString(UNO_NAME_RUBY_ADJUST), RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ADJUST }, \ + { OUString(UNO_NAME_RUBY_CHAR_STYLE_NAME), RES_TXTATR_CJK_RUBY, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_CHARSTYLE }, \ + { OUString(UNO_NAME_RUBY_POSITION), RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_POSITION}, \ + { OUString(UNO_NAME_RUBY_IS_ABOVE), RES_TXTATR_CJK_RUBY, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ABOVE }, \ + { OUString(UNO_NAME_CHAR_RELIEF), RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RELIEF }, \ + { OUString(UNO_NAME_SNAP_TO_GRID), RES_PARATR_SNAPTOGRID, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_PARA_IS_CONNECT_BORDER), RES_PARATR_CONNECT_BORDER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, \ + { OUString(UNO_NAME_CHAR_SHADING_VALUE), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, \ + { OUString(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 \ + { OUString(UNO_NAME_BREAK_TYPE), RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_PAGE_DESC_NAME), RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGEDESCNAME }, + +#define TABSTOPS_MAP_ENTRY { OUString(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 \ + { OUString(UNO_NAME_CHAR_STYLE_NAME), RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\ + { OUString(UNO_NAME_CHAR_STYLE_NAMES), FN_UNO_CHARFMT_SEQUENCE, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_CHAR_AUTO_STYLE_NAME), RES_TXTATR_AUTOFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\ + { OUString(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\ + { OUString(UNO_NAME_DOCUMENT_INDEX_MARK), FN_UNO_DOCUMENT_INDEX_MARK, cppu::UnoType<css::text::XDocumentIndexMark>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\ + { OUString(UNO_NAME_TEXT_FIELD), FN_UNO_TEXT_FIELD, cppu::UnoType<css::text::XTextField>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\ + { OUString(UNO_NAME_REFERENCE_MARK), FN_UNO_REFERENCE_MARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },\ + { OUString(UNO_NAME_FOOTNOTE), FN_UNO_FOOTNOTE, cppu::UnoType<css::text::XFootnote>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\ + { OUString(UNO_NAME_ENDNOTE), FN_UNO_ENDNOTE, cppu::UnoType<css::text::XFootnote>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\ + { OUString(UNO_NAME_HYPER_LINK_EVENTS), RES_TXTATR_INETFMT, cppu::UnoType<css::container::XNameReplace>::get(), PropertyAttribute::MAYBEVOID, MID_URL_HYPERLINKEVENTS},\ + { OUString(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_\ + { OUString(UNO_NAME_TITLE), WID_IDX_TITLE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_NAME), WID_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CONTENT_SECTION), WID_IDX_CONTENT_SECTION, cppu::UnoType<css::text::XTextSection>::get() , PropertyAttribute::READONLY, 0},\ + { OUString(UNO_NAME_HEADER_SECTION), WID_IDX_HEADER_SECTION, cppu::UnoType<css::text::XTextSection>::get() , PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\ + +#define ANCHOR_TYPES_PROPERTY { OUString(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 \ + { OUString(UNO_NAME_ANCHOR_PAGE_NO), RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ANCHOR_PAGENUM }, \ + { OUString(UNO_NAME_ANCHOR_TYPE), RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE, MID_ANCHOR_ANCHORTYPE}, \ + { OUString(UNO_NAME_ANCHOR_FRAME), RES_ANCHOR, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORFRAME}, \ + ANCHOR_TYPES_PROPERTY\ + { OUString(UNO_NAME_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, \ + { OUString(UNO_NAME_BACK_COLOR_R_G_B), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR_R_G_B}, \ + { OUString(UNO_NAME_BACK_COLOR_TRANSPARENCY), RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_BACK_COLOR_TRANSPARENCY}, \ + { OUString(UNO_NAME_FRAME_INTEROP_GRAB_BAG), RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_CONTENT_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_CONTENT }, \ + { OUString(UNO_NAME_FRAME_STYLE_NAME), FN_UNO_FRAME_STYLE_NAME,cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_BACK_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, \ + { OUString(UNO_NAME_BACK_GRAPHIC), RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, \ + { OUString(UNO_NAME_BACK_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, \ + { OUString(UNO_NAME_BACK_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, \ + { OUString(UNO_NAME_BACK_GRAPHIC_TRANSPARENCY), RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENCY}, \ + { OUString(UNO_NAME_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS},\ + { OUString(UNO_NAME_HEIGHT), RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS},\ + { OUString(UNO_NAME_HORI_ORIENT), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT }, \ + { OUString(UNO_NAME_HORI_ORIENT_POSITION), RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_HORIORIENT_POSITION|CONVERT_TWIPS }, \ + { OUString(UNO_NAME_HORI_ORIENT_RELATION), RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_RELATION }, \ + { OUString(UNO_NAME_HYPER_LINK_U_R_L), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_URL}, \ + { OUString(UNO_NAME_HYPER_LINK_TARGET), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_TARGET}, \ + { OUString(UNO_NAME_HYPER_LINK_NAME), RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_HYPERLINKNAME }, \ + { OUString(UNO_NAME_OPAQUE), RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_PAGE_TOGGLE), RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE }, \ + { OUString(UNO_NAME_POSITION_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_POSITION}, \ + { OUString(UNO_NAME_PRINT), RES_PRINT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_RELATIVE_HEIGHT), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT }, \ + { OUString(UNO_NAME_RELATIVE_HEIGHT_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION }, \ + { OUString(UNO_NAME_RELATIVE_WIDTH), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, \ + { OUString(UNO_NAME_RELATIVE_WIDTH_RELATION), RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION }, \ + { OUString(UNO_NAME_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, \ + { OUString(UNO_NAME_SHADOW_TRANSPARENCE), RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE}, \ + { OUString(UNO_NAME_IMAGE_MAP), RES_URL, cppu::UnoType<css::container::XIndexContainer>::get(), PROPERTY_NONE, MID_URL_CLIENTMAP}, \ + { OUString(UNO_NAME_SERVER_MAP), RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_SERVERMAP }, \ + { OUString(UNO_NAME_SIZE), RES_FRM_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_FRMSIZE_SIZE|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_SIZE_PROTECTED), RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_SIZE }, \ + { OUString(UNO_NAME_IS_SYNC_WIDTH_TO_HEIGHT), RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT }, \ + { OUString(UNO_NAME_IS_SYNC_HEIGHT_TO_WIDTH), RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH }, \ + { OUString(UNO_NAME_TEXT_WRAP), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, \ + { OUString(UNO_NAME_SURROUND), RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, \ + { OUString(UNO_NAME_SURROUND_ANCHORONLY), RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_ANCHORONLY }, \ + { OUString(UNO_NAME_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, \ + { OUString(UNO_NAME_VERT_ORIENT), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, \ + { OUString(UNO_NAME_VERT_ORIENT_POSITION), RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_VERTORIENT_POSITION|CONVERT_TWIPS }, \ + { OUString(UNO_NAME_VERT_ORIENT_RELATION), RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_RELATION }, \ + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS }, \ + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, \ + { OUString(UNO_LINK_DISPLAY_NAME), FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, \ + { OUString(UNO_NAME_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },\ + { OUString(UNO_NAME_Z_ORDER), FN_UNO_Z_ORDER, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW}, \ + { OUString(UNO_NAME_PARENT_TEXT), FN_UNO_PARENT_TEXT, cppu::UnoType<text::XText>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, \ + { OUString(UNO_NAME_WRAP_INFLUENCE_ON_POSITION), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE}, \ + { OUString(UNO_NAME_ALLOW_OVERLAP), RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP}, \ + { OUString(UNO_NAME_TITLE), FN_UNO_TITLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_DESCRIPTION), FN_UNO_DESCRIPTION, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_LAYOUT_SIZE), WID_LAYOUT_SIZE, cppu::UnoType<css::awt::Size>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, \ + { OUString(UNO_NAME_LINE_STYLE), RES_BOX, cppu::UnoType<css::drawing::LineStyle>::get(), 0, LINE_STYLE }, \ + { OUString(UNO_NAME_LINE_WIDTH), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LINE_WIDTH |CONVERT_TWIPS }, \ + { OUString(UNO_NAME_TEXT_VERT_ADJUST), RES_TEXT_VERT_ADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), PROPERTY_NONE ,0}, + +#define COMMON_TEXT_CONTENT_PROPERTIES \ + { OUString(UNO_NAME_ANCHOR_TYPE), FN_UNO_ANCHOR_TYPE, cppu::UnoType<css::text::TextContentAnchorType>::get(), PropertyAttribute::READONLY, MID_ANCHOR_ANCHORTYPE},\ + ANCHOR_TYPES_PROPERTY\ + { OUString(UNO_NAME_TEXT_WRAP), FN_UNO_TEXT_WRAP, cppu::UnoType<css::text::WrapTextMode>::get(), PropertyAttribute::READONLY, MID_SURROUND_SURROUNDTYPE }, + +#define PROP_DIFF_FONTHEIGHT \ + { OUString(UNO_NAME_CHAR_PROP_HEIGHT), RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\ + { OUString(UNO_NAME_CHAR_DIFF_HEIGHT), RES_CHRATR_FONTSIZE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_FONTHEIGHT_DIFF|CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_PROP_HEIGHT_ASIAN), RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\ + { OUString(UNO_NAME_CHAR_DIFF_HEIGHT_ASIAN), RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_FONTHEIGHT_DIFF|CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_PROP_HEIGHT_COMPLEX), RES_CHRATR_CTL_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\ + { OUString(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 \ + { OUString(UNO_NAME_BREAK_TYPE), RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_PAGE_DESC_NAME), RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGEDESCNAME },\ + { OUString(UNO_NAME_PAGE_NUMBER_OFFSET), RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET},\ + { OUString(UNO_NAME_CHAR_AUTO_KERNING), RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_BACK_TRANSPARENT), RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },\ + { OUString(UNO_NAME_CHAR_BACK_COLOR), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\ + { OUString(UNO_NAME_CHAR_HIGHLIGHT), RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\ + { OUString(UNO_NAME_PARA_BACK_COLOR), RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\ + { OUString(UNO_NAME_PARA_BACK_TRANSPARENT), RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },\ + { OUString(UNO_NAME_PARA_GRAPHIC_URL), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },\ + { OUString(UNO_NAME_PARA_GRAPHIC), RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PROPERTY_NONE ,MID_GRAPHIC },\ + { OUString(UNO_NAME_PARA_GRAPHIC_FILTER), RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },\ + { OUString(UNO_NAME_PARA_GRAPHIC_LOCATION), RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, \ + { OUString(UNO_NAME_CHAR_CASE_MAP), RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_TRANSPARENCE), RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA},\ + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT},\ + { OUString(UNO_NAME_CHAR_CROSSED_OUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC },\ + { OUString(UNO_NAME_CHAR_ESCAPEMENT_HEIGHT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT},\ + { OUString(UNO_NAME_CHAR_FLASH), RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_HIDDEN), RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + STANDARD_FONT_PROPERTIES\ + CJK_FONT_PROPERTIES\ + CTL_FONT_PROPERTIES\ + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},\ + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},\ + { OUString(UNO_NAME_CHAR_UNDERLINE_HAS_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},\ + { OUString(UNO_NAME_CHAR_OVERLINE), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},\ + { OUString(UNO_NAME_CHAR_OVERLINE_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},\ + { OUString(UNO_NAME_CHAR_OVERLINE_HAS_COLOR), RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},\ + { OUString(UNO_NAME_PARA_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TXT_LMARGIN|CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_LEFT_MARGIN_RELATIVE), RES_LR_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_L_REL_MARGIN},\ + { OUString(UNO_NAME_PARA_RIGHT_MARGIN_RELATIVE), RES_LR_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_R_REL_MARGIN},\ + { OUString(UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT), RES_LR_SPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FIRST_AUTO},\ + { OUString(UNO_NAME_PARA_FIRST_LINE_INDENT), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FIRST_LINE_INDENT|CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_FIRST_LINE_INDENT_RELATIVE), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FIRST_LINE_REL_INDENT|CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_KERNING), RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_NO_HYPHENATION), RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_DROP_CAP_FORMAT), RES_PARATR_DROP, cppu::UnoType<css::style::DropCapFormat>::get() , PROPERTY_NONE, MID_DROPCAP_FORMAT|CONVERT_TWIPS },\ + { OUString(UNO_NAME_DROP_CAP_WHOLE_WORD), RES_PARATR_DROP, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_DROPCAP_WHOLE_WORD },\ + { OUString(UNO_NAME_DROP_CAP_CHAR_STYLE_NAME), RES_PARATR_DROP, cppu::UnoType<OUString>::get() , PropertyAttribute::MAYBEVOID, MID_DROPCAP_CHAR_STYLE_NAME },\ + { OUString(UNO_NAME_PARA_KEEP_TOGETHER), RES_KEEP, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_PARA_SPLIT), RES_PARATR_SPLIT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_PARA_WIDOWS), RES_PARATR_WIDOWS, cppu::UnoType<sal_Int8>::get(),PropertyAttribute::MAYBEVOID, 0},\ + { OUString(UNO_NAME_PARA_ORPHANS), RES_PARATR_ORPHANS, cppu::UnoType<sal_Int8>::get(),PropertyAttribute::MAYBEVOID, 0},\ + { OUString(UNO_NAME_PARA_EXPAND_SINGLE_WORD), RES_PARATR_ADJUST, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_EXPAND_SINGLE },\ + { OUString(UNO_NAME_PARA_LAST_LINE_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LAST_LINE_ADJUST},\ + { OUString(UNO_NAME_PARA_LINE_NUMBER_COUNT), RES_LINENUMBER, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_LINENUMBER_COUNT },\ + { OUString(UNO_NAME_PARA_LINE_NUMBER_START_VALUE), RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_LINENUMBER_STARTVALUE},\ + { OUString(UNO_NAME_PARA_LINE_SPACING), RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(),PROPERTY_NONE, CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_PARA_ADJUST},\ + { OUString(UNO_NAME_PARA_REGISTER_MODE_ACTIVE), RES_PARATR_REGISTER, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_PARA_TOP_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},\ + { OUString(UNO_NAME_PARA_CONTEXT_MARGIN), RES_UL_SPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_CTX_MARGIN},\ + { OUString(UNO_NAME_PARA_TOP_MARGIN_RELATIVE), RES_UL_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_UP_REL_MARGIN},\ + { OUString(UNO_NAME_PARA_BOTTOM_MARGIN_RELATIVE), RES_UL_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LO_REL_MARGIN},\ + TABSTOPS_MAP_ENTRY\ + { OUString(UNO_NAME_CHAR_WORD_MODE), RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_CHAR_SHADING_VALUE), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, \ + { OUString(UNO_NAME_CHAR_LEFT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_RIGHT_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_TOP_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER), RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_LEFT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_TOP_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE), RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },\ + { OUString(UNO_NAME_CHAR_SHADOW_FORMAT), RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},\ + { OUString(UNO_NAME_LEFT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_RIGHT_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_TOP_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },\ + { OUString(UNO_NAME_BOTTOM_BORDER), RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },\ + { OUString(UNO_NAME_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS },\ + { OUString(UNO_NAME_LEFT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_RIGHT_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_TOP_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },\ + { OUString(UNO_NAME_BOTTOM_BORDER_DISTANCE), RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },\ + { OUString(UNO_NAME_PARA_IS_HYPHENATION), RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN },\ + { OUString(UNO_NAME_PARA_HYPHENATION_NO_CAPS), RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS },\ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD },\ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL },\ + { OUString(UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS), RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS},\ + { OUString(UNO_NAME_NUMBERING_STYLE_NAME), RES_PARATR_NUMRULE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\ + { OUString(UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES), RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },\ + { OUString(UNO_NAME_PARA_SHADOW_FORMAT), RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},\ + { OUString(UNO_NAME_CHAR_COMBINE_IS_ON), RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES},\ + { OUString(UNO_NAME_CHAR_COMBINE_PREFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET},\ + { OUString(UNO_NAME_CHAR_COMBINE_SUFFIX), RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET},\ + { OUString(UNO_NAME_CHAR_EMPHASIS), RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS},\ + { OUString(UNO_NAME_PARA_IS_HANGING_PUNCTUATION), RES_PARATR_HANGINGPUNCTUATION, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\ + { OUString(UNO_NAME_PARA_IS_CHARACTER_DISTANCE), RES_PARATR_SCRIPTSPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\ + { OUString(UNO_NAME_PARA_IS_FORBIDDEN_RULES), RES_PARATR_FORBIDDEN_RULES, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\ + { OUString(UNO_NAME_PARA_VERT_ALIGNMENT), RES_PARATR_VERTALIGN, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , 0 },\ + { OUString(UNO_NAME_CHAR_ROTATION), RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE },\ + { OUString(UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE), RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE },\ + { OUString(UNO_NAME_CHAR_SCALE_WIDTH), RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },\ + { OUString(UNO_NAME_CHAR_RELIEF), RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF },\ + PROP_DIFF_FONTHEIGHT\ + { OUString(UNO_NAME_FOLLOW_STYLE), FN_UNO_FOLLOW_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_IS_PHYSICAL), FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},\ + { OUString(UNO_NAME_IS_AUTO_UPDATE), FN_UNO_IS_AUTO_UPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},\ + { OUString(UNO_NAME_DISPLAY_NAME), FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},\ + { OUString(UNO_NAME_CATEGORY), FN_UNO_CATEGORY, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , 0 },\ + { OUString(UNO_NAME_WRITING_MODE), RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },\ + { OUString(UNO_NAME_PARA_IS_CONNECT_BORDER), RES_PARATR_CONNECT_BORDER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0},\ + { OUString(UNO_NAME_SNAP_TO_GRID), RES_PARATR_SNAPTOGRID, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \ + { OUString(UNO_NAME_OUTLINE_LEVEL), RES_PARATR_OUTLINELEVEL,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_HIDDEN), FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_STYLE_INTEROP_GRAB_BAG), FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \ + { OUString(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 \ + { OUString(UNO_NAME_CHAR_BACK_COLOR), RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, \ + { OUString(UNO_NAME_CHAR_COLOR), RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_CHAR_TRANSPARENCE), RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA }, \ + { OUString(UNO_NAME_CHAR_CONTOURED), RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_CHAR_EMPHASIS), RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS}, \ + { OUString(UNO_NAME_CHAR_ESCAPEMENT), RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC }, \ + { OUString(UNO_NAME_CHAR_FONT_NAME), RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \ + { OUString(UNO_NAME_CHAR_HEIGHT), RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_CHAR_POSTURE), RES_CHRATR_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \ + { OUString(UNO_NAME_CHAR_SHADOWED), RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, \ + { OUString(UNO_NAME_CHAR_STRIKEOUT), RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT}, \ + { OUString(UNO_NAME_CHAR_UNDERLINE_COLOR), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR}, \ + { OUString(UNO_NAME_CHAR_WEIGHT), RES_CHRATR_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \ + { OUString(UNO_NAME_NUMBERING_LEVEL), RES_PARATR_LIST_LEVEL,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \ + { OUString(UNO_NAME_CHAR_UNDERLINE), RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE}, \ + { OUString(UNO_NAME_NUMBERING_RULES), RES_PARATR_NUMRULE,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + { OUString(UNO_NAME_PARA_ADJUST), RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST}, \ + { OUString(UNO_NAME_PARA_BOTTOM_MARGIN), RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LO_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_PARA_FIRST_LINE_INDENT), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_LINE_INDENT|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_PARA_LEFT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TXT_LMARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_PARA_LINE_SPACING), RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + { OUString(UNO_NAME_PARA_RIGHT_MARGIN), RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS}, \ + { OUString(UNO_NAME_TABSTOPS), RES_PARATR_TABSTOP, cppu::UnoType< cppu::UnoSequenceType<css::style::TabStop> >::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + +#define FILL_PROPERTIES_SW_BMP \ + { OUString(UNO_NAME_SW_FILLBMP_LOGICAL_SIZE), XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_OFFSET_X), XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_OFFSET_Y), XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_POSITION_OFFSET_X), XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_POSITION_OFFSET_Y), XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_RECTANGLE_POINT), XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_SIZE_X), XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM}, \ + { OUString(UNO_NAME_SW_FILLBMP_SIZE_Y), XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM}, \ + { OUString(UNO_NAME_SW_FILLBMP_STRETCH), XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBMP_TILE), XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get(), 0, 0},\ + { OUString(UNO_NAME_SW_FILLBMP_MODE), OWN_ATTR_FILLBMP_MODE, cppu::UnoType<drawing::BitmapMode>::get(), 0, 0}, \ + +#define FILL_PROPERTIES_SW_DEFAULTS \ + { OUString(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 \ + { OUString(UNO_NAME_SW_FILLBACKGROUND), XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLBITMAP), XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP}, \ + { OUString(UNO_NAME_SW_FILLBITMAPURL), XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_BITMAP }, \ + { OUString(UNO_NAME_SW_FILLBITMAPNAME), XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \ + { OUString(UNO_NAME_SW_FILLGRADIENTSTEPCOUNT), XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLGRADIENT), XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, \ + { OUString(UNO_NAME_SW_FILLGRADIENTNAME), XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \ + { OUString(UNO_NAME_SW_FILLHATCH), XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH}, \ + { OUString(UNO_NAME_SW_FILLHATCHNAME), XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \ + { OUString(UNO_NAME_SW_FILLSTYLE), XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILL_TRANSPARENCE), XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0}, \ + { OUString(UNO_NAME_SW_FILLTRANSPARENCEGRADIENT), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, \ + { OUString(UNO_NAME_SW_FILLTRANSPARENCEGRADIENTNAME), XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \ + { OUString(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..a70466608 --- /dev/null +++ b/sw/source/core/unocore/unoobj.cxx @@ -0,0 +1,2948 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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/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_bExportPargraphNumbering = 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 *const pDoc, const uno::Any & rValue, SfxItemSet & rSet) +{ + SwDocShell *const pDocSh = pDoc->GetDocShell(); + if(pDocSh) + { + 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); +}; + +void +SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM) +{ + SwDoc *const pDoc = rPaM.GetDoc(); + SwDocShell *const pDocSh = pDoc->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(pDoc); + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + SwPaM *pTmpCursor = &rPaM; + do { + pDoc->SetTextFormatColl(*pTmpCursor, pLocal); + pTmpCursor = pTmpCursor->GetNext(); + } while ( pTmpCursor != &rPaM ); + pDoc->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; + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet.GetItemState( RES_PAGEDESC, true, &pItem ) ) + { + pNewDesc.reset(new SwFormatPageDesc( + *static_cast<const 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(*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 : static_cast<sal_uInt16>(nTmp)); + SwDoc* pDoc = rCursor.GetDoc(); + UnoActionContext aAction(pDoc); + + if( rCursor.GetNext() != &rCursor ) // MultiSelection? + { + pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); + SwPamRanges aRangeArr( rCursor ); + SwPaM aPam( *rCursor.GetPoint() ); + for( size_t n = 0; n < aRangeArr.Count(); ++n ) + { + pDoc->SetNumRuleStart(*aRangeArr.SetPam( n, aPam ).GetPoint()); + pDoc->SetNodeNumStart(*aRangeArr.SetPam( n, aPam ).GetPoint(), + nStt ); + } + pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr ); + } + else + { + pDoc->SetNumRuleStart( *rCursor.GetPoint()); + pDoc->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 + SfxItemSet aSet(rPam.GetDoc()->GetAttrPool(), + svl::Items<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT>{}); + 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 *const pDoc = rPam.GetDoc(); + //default character style must not be set as default format + SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>( + pDoc->GetDocShell() + ->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char)); + if (!pStyle || pStyle->GetCharFormat() == pDoc->GetDfltCharFormat()) + { + throw lang::IllegalArgumentException(); + } + std::unique_ptr<SwFormatDrop> pDrop; + SfxPoolItem const* pItem(nullptr); + if (SfxItemState::SET == + rItemSet.GetItemState(RES_PARATR_DROP, true, &pItem)) + { + pDrop.reset(new SwFormatDrop(*static_cast<const SwFormatDrop*>(pItem))); + } + if (!pDrop) + { + pDrop.reset(new SwFormatDrop); + } + const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*pStyle)); + pDrop->SetCharFormat(xStyle->GetCharFormat()); + rItemSet.Put(*pDrop); +} + +static void +lcl_setRubyCharstyle(SfxItemSet & rItemSet, uno::Any const& rValue) +{ + OUString sTmp; + if (!(rValue >>= sTmp)) + { + throw lang::IllegalArgumentException(); + } + + std::unique_ptr<SwFormatRuby> pRuby; + const SfxPoolItem* pItem; + if (SfxItemState::SET == + rItemSet.GetItemState(RES_TXTATR_CJK_RUBY, true, &pItem)) + { + pRuby.reset(new SwFormatRuby(*static_cast<const 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(*pRuby); +} + +bool +SwUnoCursorHelper::SetCursorPropertyValue( + SfxItemPropertySimpleEntry 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: + 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()); + SfxItemSet items( rPam.GetDoc()->GetAttrPool(), + svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>{} ); + + for (beans::NamedValue const & prop : std::as_const(props)) + { + SfxItemPropertySimpleEntry 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); + } + + SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT); + // TODO: for ODF export we'd need to add it to the autostyle pool + // 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(std::make_shared<SfxItemSet>(items)); + 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 sal_uLong nSttNd = pTmpCursor->Start()->nNode.GetIndex(); + const sal_uLong nEndNd = pTmpCursor->End()->nNode.GetIndex(); + + if( nEndNd - nSttNd >= nMaxLookup ) + { + pFormat = nullptr; + break; + } + + const SwNodes& rNds = rPaM.GetDoc()->GetNodes(); + for( sal_uLong 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; +} + +class SwXTextCursor::Impl +{ + +public: + const SfxItemPropertySet & m_rPropSet; + const CursorType m_eType; + const uno::Reference< text::XText > m_xParentText; + sw::UnoCursorPointer m_pUnoCursor; + + Impl( SwDoc & rDoc, + const CursorType eType, + uno::Reference<text::XText> const & xParent, + SwPosition const& rPoint, SwPosition const*const pMark) + : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR)) + , m_eType(eType) + , m_xParentText(xParent) + , m_pUnoCursor(rDoc.CreateUnoCursor(rPoint)) + { + if (pMark) + { + m_pUnoCursor->SetMark(); + *m_pUnoCursor->GetMark() = *pMark; + } + } + + SwUnoCursor& GetCursorOrThrow() { + if(!m_pUnoCursor) + throw uno::RuntimeException("SwXTextCursor: disposed or invalid", nullptr); + return *m_pUnoCursor; + } +}; + +SwUnoCursor& SwXTextCursor::GetCursor() + { return *m_pImpl->m_pUnoCursor; } + +SwPaM const* SwXTextCursor::GetPaM() const + { return m_pImpl->m_pUnoCursor.get(); } + +SwPaM* SwXTextCursor::GetPaM() + { return m_pImpl->m_pUnoCursor.get(); } + +SwDoc const* SwXTextCursor::GetDoc() const + { return m_pImpl->m_pUnoCursor ? m_pImpl->m_pUnoCursor->GetDoc() : nullptr; } + +SwDoc* SwXTextCursor::GetDoc() + { return m_pImpl->m_pUnoCursor ? m_pImpl->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_pImpl( new Impl(rDoc, eType, xParent, rPos, pMark) ) +{ +} + +SwXTextCursor::SwXTextCursor(uno::Reference< text::XText > const& xParent, + SwPaM const& rSourceCursor, const CursorType eType) + : m_pImpl( new Impl(*rSourceCursor.GetDoc(), eType, + xParent, *rSourceCursor.GetPoint(), + rSourceCursor.HasMark() ? rSourceCursor.GetMark() : nullptr) ) +{ +} + +SwXTextCursor::~SwXTextCursor() +{ +} + +void SwXTextCursor::DeleteAndInsert(const OUString& rText, + const bool bForceExpandHints) +{ + auto pUnoCursor = static_cast<SwCursor*>(m_pImpl->m_pUnoCursor.get()); + if (pUnoCursor) + { + // Start/EndAction + SwDoc* pDoc = pUnoCursor->GetDoc(); + UnoActionContext aAction(pDoc); + const sal_Int32 nTextLen = rText.getLength(); + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr); + auto pCurrent = pUnoCursor; + do + { + if (pCurrent->HasMark()) + { + pDoc->getIDocumentContentOperations().DeleteAndJoin(*pCurrent); + } + if(nTextLen) + { + const bool bSuccess( + SwUnoCursorHelper::DocInsertStringSplitCR( + *pDoc, *pCurrent, rText, bForceExpandHints ) ); + OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." ); + + SwUnoCursorHelper::SelectPam(*pUnoCursor, true); + pCurrent->Left(rText.getLength()); + } + pCurrent = pCurrent->GetNext(); + } while (pCurrent != pUnoCursor); + pDoc->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_pImpl->m_eType) + { + auto pCursor( m_pImpl->m_pUnoCursor ); + SwXMeta const*const pXMeta( + dynamic_cast<SwXMeta*>(m_pImpl->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; +} + +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" + }; +} + +namespace +{ + class theSwXTextCursorUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextCursorUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextCursor::getUnoTunnelId() +{ + return theSwXTextCursorUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXTextCursor::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + const sal_Int64 nRet( ::sw::UnoTunnelImpl<SwXTextCursor>(rId, this) ); + return nRet ? nRet : OTextCursorHelper::getSomething(rId); +} + +void SAL_CALL SwXTextCursor::collapseToStart() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + if (rUnoCursor.HasMark()) + { + if (*rUnoCursor.GetPoint() > *rUnoCursor.GetMark()) + { + rUnoCursor.Exchange(); + } + rUnoCursor.DeleteMark(); + } +} + +void SAL_CALL SwXTextCursor::collapseToEnd() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + SwUnoCursorHelper::SelectPam(rUnoCursor, Expand); + bool bRet = rUnoCursor.Left( nCount); + if (CursorType::Meta == m_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::goRight(sal_Int16 nCount, sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + SwUnoCursorHelper::SelectPam(rUnoCursor, Expand); + bool bRet = rUnoCursor.Right(nCount); + if (CursorType::Meta == m_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +void SAL_CALL +SwXTextCursor::gotoStart(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("gotoStart"); + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + SwUnoCursorHelper::SelectPam(rUnoCursor, Expand); + if (CursorType::Body == m_pImpl->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_pImpl->m_eType) + || (CursorType::TableText == m_pImpl->m_eType) + || (CursorType::Header == m_pImpl->m_eType) + || (CursorType::Footer == m_pImpl->m_eType) + || (CursorType::Footnote== m_pImpl->m_eType) + || (CursorType::Redline == m_pImpl->m_eType)) + { + rUnoCursor.MoveSection(GoCurrSection, fnSectionStart); + } + else if (CursorType::Meta == m_pImpl->m_eType) + { + lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, META_INIT_START); + } +} + +void SAL_CALL +SwXTextCursor::gotoEnd(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("gotoEnd"); + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + SwUnoCursorHelper::SelectPam(rUnoCursor, Expand); + if (CursorType::Body == m_pImpl->m_eType) + { + rUnoCursor.Move( fnMoveForward, GoInDoc ); + } + else if ( (CursorType::Frame == m_pImpl->m_eType) + || (CursorType::TableText == m_pImpl->m_eType) + || (CursorType::Header == m_pImpl->m_eType) + || (CursorType::Footer == m_pImpl->m_eType) + || (CursorType::Footnote== m_pImpl->m_eType) + || (CursorType::Redline == m_pImpl->m_eType)) + { + rUnoCursor.MoveSection( GoCurrSection, fnSectionEnd); + } + else if (CursorType::Meta == m_pImpl->m_eType) + { + lcl_ForceIntoMeta(rUnoCursor, m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY); + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + pCursor = + ::sw::UnoTunnelGetImplementation<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_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: + ; + } + + 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_pImpl->m_eType) + { + SwPaM CopyPam(*pPam->GetMark(), *pPam->GetPoint()); + const bool bNotForced( lcl_ForceIntoMeta( + CopyPam, m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + const bool bRet = + rUnoCursor.IsStartWordWT( i18n::WordType::DICTIONARY_WORD ); + return bRet; +} + +sal_Bool SAL_CALL SwXTextCursor::isEndOfWord() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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( m_pImpl->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_pImpl->m_eType)) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH); + } + + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoPreviousWord(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType)) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH); + } + + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoEndOfWord(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH); + } + + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoStartOfWord(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH); + } + + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::isStartOfSentence() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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( m_pImpl->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( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoPreviousSentence(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoStartOfSentence(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoEndOfSentence(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->m_eType) + { + bRet = lcl_ForceIntoMeta(rUnoCursor, m_pImpl->m_xParentText, + META_CHECK_BOTH) + && bRet; + } + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::isStartOfParagraph() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + const bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor); + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::isEndOfParagraph() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + const bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor); + return bRet; +} + +sal_Bool SAL_CALL +SwXTextCursor::gotoStartOfParagraph(sal_Bool Expand) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + if (CursorType::Meta == m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + if (CursorType::Meta == m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + if (CursorType::Meta == m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + if (CursorType::Meta == m_pImpl->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_pImpl->m_xParentText; +} + +uno::Reference< text::XTextRange > SAL_CALL +SwXTextCursor::getStart() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + uno::Reference< text::XTextRange > xRet; + SwPaM aPam(*rUnoCursor.Start()); + const uno::Reference< text::XText > xParent = getText(); + if (CursorType::Meta == m_pImpl->m_eType) + { + // return cursor to prevent modifying SwXTextRange for META + SwXTextCursor * const pXCursor( + new SwXTextCursor(*rUnoCursor.GetDoc(), xParent, CursorType::Meta, + *rUnoCursor.GetPoint()) ); + pXCursor->gotoStart(false); + xRet = static_cast<text::XWordCursor*>(pXCursor); + } + else + { + xRet = new SwXTextRange(aPam, xParent); + } + return xRet; +} + +uno::Reference< text::XTextRange > SAL_CALL +SwXTextCursor::getEnd() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + uno::Reference< text::XTextRange > xRet; + SwPaM aPam(*rUnoCursor.End()); + const uno::Reference< text::XText > xParent = getText(); + if (CursorType::Meta == m_pImpl->m_eType) + { + // return cursor to prevent modifying SwXTextRange for META + SwXTextCursor * const pXCursor( + new SwXTextCursor(*rUnoCursor.GetDoc(), xParent, CursorType::Meta, + *rUnoCursor.GetPoint()) ); + pXCursor->gotoEnd(false); + xRet = static_cast<text::XWordCursor*>(pXCursor); + } + else + { + xRet = new SwXTextRange(aPam, xParent); + } + return xRet; +} + +OUString SAL_CALL SwXTextCursor::getString() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + OUString aText; + SwUnoCursorHelper::GetTextFromPam(rUnoCursor, aText); + return aText; +} + +void SAL_CALL +SwXTextCursor::setString(const OUString& aString) +{ + SolarMutexGuard aGuard; + + m_pImpl->GetCursorOrThrow(); // just to check if valid + + const bool bForceExpandHints( (CursorType::Meta == m_pImpl->m_eType) + && dynamic_cast<SwXMeta*>(m_pImpl->m_xParentText.get()) + ->CheckForOwnMemberMeta(*GetPaM(), true) ); + DeleteAndInsert(aString, bForceExpandHints); +} + +uno::Any SwUnoCursorHelper::GetPropertyValue( + SwPaM& rPaM, const SfxItemPropertySet& rPropSet, + const OUString& rPropertyName) +{ + uno::Any aAny; + SfxItemPropertySimpleEntry const*const pEntry = + rPropSet.getPropertyMap().getByName(rPropertyName); + + if (!pEntry) + { + throw beans::UnknownPropertyException( + "Unknown property: " + rPropertyName, + static_cast<cppu::OWeakObject *>(nullptr)); + } + + beans::PropertyState eTemp; + const bool bDone = SwUnoCursorHelper::getCursorPropertyValue( + *pEntry, rPaM, &aAny, eTemp ); + + if (!bDone) + { + SfxItemSet aSet( + rPaM.GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>{}); + 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(1); + aValues[0].Name = rPropertyName; + aValues[0].Value = 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 *const pDoc = rPaM.GetDoc(); + OUString aUnknownExMsg, aPropertyVetoExMsg; + + // Build set of attributes we want to fetch + const sal_uInt16 zero = 0; + SfxItemSet aItemSet(pDoc->GetAttrPool(), &zero); + std::vector<std::pair<const SfxItemPropertySimpleEntry*, const uno::Any&>> aEntries; + aEntries.reserve(rPropertyValues.getLength()); + for (const auto& rPropVal : rPropertyValues) + { + const OUString &rPropertyName = rPropVal.Name; + + SfxItemPropertySimpleEntry 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; + } + aItemSet.MergeRange(pEntry->nWID, pEntry->nWID); + aEntries.emplace_back(pEntry, rPropVal.Value); + } + + if (!aEntries.empty()) + { + // Fetch, overwrite, and re-set the attributes from the core + + bool bPreviousPropertyCausesSideEffectsInNodes = false; + for (size_t i = 0; i < aEntries.size(); ++i) + { + SfxItemPropertySimpleEntry 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::unique_ptr<SfxItemSet> pSet; + std::unique_ptr<SfxItemSet> pSetParent; + + for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++) + { + SfxItemPropertySimpleEntry 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) + { + 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 (!pSet) + { + switch ( eCaller ) + { + case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT: + case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION: + pSet.reset( + new SfxItemSet( rPaM.GetDoc()->GetAttrPool(), + svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END>{} )); + break; + case SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY: + pSet.reset( + new SfxItemSet( rPaM.GetDoc()->GetAttrPool(), + {{pEntry->nWID, pEntry->nWID}} )); + break; + default: + pSet.reset( new SfxItemSet( + rPaM.GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_CONTAINER, + RES_UNKNOWNATR_CONTAINER>{})); + } + // #i63870# + SwUnoCursorHelper::GetCursorAttr( rPaM, *pSet ); + } + + pStates[i] = ( pSet->Count() ) + ? rPropSet.getPropertyState( *pEntry, *pSet ) + : beans::PropertyState_DEFAULT_VALUE; + + //try again to find out if a value has been inherited + if( beans::PropertyState_DIRECT_VALUE == pStates[i] ) + { + if (!pSetParent) + { + pSetParent = pSet->Clone( false ); + // #i63870# + SwUnoCursorHelper::GetCursorAttr( + rPaM, *pSetParent, true, false ); + } + + pStates[i] = ( pSetParent->Count() ) + ? rPropSet.getPropertyState( *pEntry, *pSetParent ) + : 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, + std::set<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, + const OUString& rPropertyName) +{ + SwDoc & rDoc = *rPaM.GetDoc(); + SfxItemPropertySimpleEntry const*const pEntry = + rPropSet.getPropertyMap().getByName(rPropertyName); + if (!pEntry) + { + throw beans::UnknownPropertyException( + "Unknown property: " + rPropertyName, + static_cast<cppu::OWeakObject *>(nullptr)); + } + + if (pEntry->nFlags & beans::PropertyAttribute::READONLY) + { + throw uno::RuntimeException( + "setPropertyToDefault: property is read-only: " + + rPropertyName, nullptr); + } + + if (pEntry->nWID < RES_FRMATR_END) + { + std::set<sal_uInt16> aWhichIds; + aWhichIds.insert( 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, + const OUString& rPropertyName) +{ + SfxItemPropertySimpleEntry const*const pEntry = + rPropSet.getPropertyMap().getByName(rPropertyName); + if (!pEntry) + { + throw beans::UnknownPropertyException( + "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[] = + { + { OUString(UNO_NAME_IS_SKIP_HIDDEN_TEXT), FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_SKIP_PROTECTED_TEXT), FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + const uno::Reference< beans::XPropertySetInfo > xInfo = + m_pImpl->m_rPropSet.getPropertySetInfo(); + // extend PropertySetInfo! + const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties(); + return new SfxExtItemPropertySetInfo( + aCursorExtMap_Impl, + aPropSeq ); + }(); + return xRef; +} + +void SAL_CALL +SwXTextCursor::setPropertyValue( + const OUString& rPropertyName, const uno::Any& rValue) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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 + { + SwUnoCursorHelper::SetPropertyValue(rUnoCursor, + m_pImpl->m_rPropSet, rPropertyName, rValue); + } +} + +uno::Any SAL_CALL +SwXTextCursor::getPropertyValue(const OUString& rPropertyName) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + const beans::PropertyState eRet = SwUnoCursorHelper::GetPropertyState( + rUnoCursor, m_pImpl->m_rPropSet, rPropertyName); + return eRet; +} + +uno::Sequence< beans::PropertyState > SAL_CALL +SwXTextCursor::getPropertyStates( + const uno::Sequence< OUString >& rPropertyNames) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + return SwUnoCursorHelper::GetPropertyStates( + rUnoCursor, m_pImpl->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( m_pImpl->GetCursorOrThrow() ); + + // a little lame to have to copy into this. + uno::Sequence< beans::PropertyValue > aPropertyValues( aValues.getLength() ); + 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(); + } + aPropertyValues[ i ].Name = aPropertyNames[ i ]; + aPropertyValues[ i ].Value = aValues[ i ]; + } + try + { + SwUnoCursorHelper::SetPropertyValues( rUnoCursor, m_pImpl->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.begin(), + [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, std::set<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( rWhichIds.end(), nId ); + } + } +} + +void SAL_CALL +SwXTextCursor::setAllPropertiesToDefault() +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + std::set<sal_uInt16> aParaWhichIds; + std::set<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( m_pImpl->GetCursorOrThrow() ); + + if ( rPropertyNames.hasElements() ) + { + SwDoc & rDoc = *rUnoCursor.GetDoc(); + std::set<sal_uInt16> aWhichIds; + std::set<sal_uInt16> aParaWhichIds; + for (const OUString& rName : rPropertyNames) + { + SfxItemPropertySimpleEntry const*const pEntry = + m_pImpl->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( m_pImpl->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++) + { + SfxItemPropertySimpleEntry const*const pEntry = + m_pImpl->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) + { + 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( m_pImpl->GetCursorOrThrow() ); + + SwNode& node = rUnoCursor.GetNode(); + + SwTextNode* txtNode = node.GetTextNode(); + + if (txtNode == nullptr) return; + + if ( text::TextMarkupType::SPELLCHECK == nType ) + { + txtNode->SetWrongDirty(SwTextNode::WrongState::TODO); + txtNode->SetWrong(nullptr); + } + else if( text::TextMarkupType::PROOFREADING == nType ) + { + txtNode->SetGrammarCheckDirty(true); + txtNode->SetGrammarCheck(nullptr); + } + else if ( text::TextMarkupType::SMARTTAG == nType ) + { + txtNode->SetSmartTagDirty(true); + txtNode->SetSmartTags(nullptr); + } + else return; + + SwFormatColl* fmtColl=txtNode->GetFormatColl(); + + if (fmtColl == nullptr) return; + + SwFormatChg aNew( fmtColl ); + txtNode->NotifyClients( nullptr, &aNew ); +} + +void SAL_CALL +SwXTextCursor::makeRedline( + const OUString& rRedlineType, + const uno::Sequence< beans::PropertyValue >& rRedlineProperties) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + SwUnoCursorHelper::makeRedline(rUnoCursor, rRedlineType, rRedlineProperties); +} + +void SAL_CALL SwXTextCursor::insertDocumentFromURL(const OUString& rURL, + const uno::Sequence< beans::PropertyValue >& rOptions) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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); + + uno::Sequence< table::TableSortField > aFields(3); + table::TableSortField* pFields = aFields.getArray(); + + 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]; + } + + pFields[0].Field = 1; + pFields[0].IsAscending = true; + pFields[0].IsCaseSensitive = false; + pFields[0].FieldType = table::TableSortFieldType_ALPHANUMERIC; + pFields[0].CollatorLocale = aLang; + pFields[0].CollatorAlgorithm = aCollAlg; + + pFields[1].Field = 1; + pFields[1].IsAscending = true; + pFields[1].IsCaseSensitive = false; + pFields[1].FieldType = table::TableSortFieldType_ALPHANUMERIC; + pFields[1].CollatorLocale = aLang; + pFields[1].CollatorAlgorithm = aCollAlg; + + pFields[2].Field = 1; + pFields[2].IsAscending = true; + pFields[2].IsCaseSensitive = false; + pFields[2].FieldType = table::TableSortFieldType_ALPHANUMERIC; + pFields[2].CollatorLocale = aLang; + pFields[2].CollatorAlgorithm = 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 !! + + std::unique_ptr<SwSortKey> pKey1(new SwSortKey); + pKey1->nColumnId = USHRT_MAX; + pKey1->bIsNumeric = true; + pKey1->eSortOrder = SwSortOrder::Ascending; + + std::unique_ptr<SwSortKey> pKey2(new SwSortKey); + pKey2->nColumnId = USHRT_MAX; + pKey2->bIsNumeric = true; + pKey2->eSortOrder = SwSortOrder::Ascending; + + std::unique_ptr<SwSortKey> pKey3(new SwSortKey); + pKey3->nColumnId = USHRT_MAX; + pKey3->bIsNumeric = true; + pKey3->eSortOrder = SwSortOrder::Ascending; + SwSortKey* aKeys[3] = {pKey1.get(), pKey2.get(), pKey3.get()}; + + 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 = + static_cast<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 (pKey1->nColumnId != USHRT_MAX) + { + rSortOpt.aKeys.push_back(std::move(pKey1)); + } + if (pKey2->nColumnId != USHRT_MAX) + { + rSortOpt.aKeys.push_back(std::move(pKey2)); + } + if (pKey3->nColumnId != USHRT_MAX) + { + rSortOpt.aKeys.push_back(std::move(pKey3)); + } + + return bRet && !rSortOpt.aKeys.empty(); +} + +void SAL_CALL +SwXTextCursor::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor) +{ + SolarMutexGuard aGuard; + + SwUnoCursor & rUnoCursor( m_pImpl->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 sal_uLong 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( m_pImpl->GetCursorOrThrow() ); + return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_TEXTRANGE); +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwXTextCursor::createEnumeration() +{ + SolarMutexGuard g; + + SwUnoCursor & rUnoCursor( m_pImpl->GetCursorOrThrow() ); + + SwXText* pParentText = comphelper::getUnoTunnelImplementation<SwXText>(m_pImpl->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_pImpl->m_eType) + ? CursorType::SelectionInTable : CursorType::Selection; + SwTableNode const*const pStartNode( (CursorType::TableText == m_pImpl->m_eType) + ? rUnoCursor.GetPoint()->nNode.GetNode().FindTableNode() + : nullptr); + SwTable const*const pTable( + pStartNode ? & pStartNode->GetTable() : nullptr ); + return SwXParagraphEnumeration::Create(pParentText, pNewCursor, eSetType, pStartNode, pTable); +} + +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..9f0dc5d8e --- /dev/null +++ b/sw/source/core/unocore/unoobj2.cxx @@ -0,0 +1,1692 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <docary.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) + { + 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* pDoc = rIdx.GetNode().GetDoc(); + + const auto nChkType = bAtCharAnchoredObjs ? RndStdIds::FLY_AT_CHAR : RndStdIds::FLY_AT_PARA; + const SwContentFrame* pCFrame; + const SwContentNode* pCNd; + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && + nullptr != (pCNd = rIdx.GetNode().GetContentNode()) && + nullptr != (pCFrame = pCNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout())) ) + { + lcl_CollectFrameAtNodeWithLayout(pCFrame, rFrames, nChkType); + } + else + { + const SwFrameFormats& rFormats = *pDoc->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* pDoc = rPam.GetDoc(); + //StartEndAction + UnoActionContext aAction(pDoc); + if (rPam.GetNext() != &rPam) // Ring of Cursors + { + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr); + + for(SwPaM& rCurrent : rPam.GetRingContainer()) + { + if (rCurrent.HasMark() && + ( bTableMode || + (*rCurrent.GetPoint() != *rCurrent.GetMark()) )) + { + pDoc->getIDocumentContentOperations().InsertItemSet(rCurrent, rSet, nFlags); + } + } + + pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr); + } + else + { + pDoc->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 sal_uLong nSttNd = rStart.nNode.GetIndex(); + const sal_uLong nEndNd = rEnd .nNode.GetIndex(); + + if (nEndNd - nSttNd >= 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 (sal_uLong 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 sal_uLong 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?"); + OSL_ENSURE( !((CursorType::SelectionInTable == eType) || + (CursorType::TableText == eType)) + || (m_pOwnTable && m_pOwnStartNode), + "SwXParagraphEnumeration: table type but no start node or table?"); + + 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() throw () 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); +}; + +} + +SwXParagraphEnumeration* SwXParagraphEnumeration::Create( + uno::Reference< text::XText > const& xParent, + const std::shared_ptr<SwUnoCursor>& pCursor, + const CursorType eType, + SwStartNode const*const pStartNode, + SwTable const*const pTable) +{ + 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 paragaph 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_pTableFormat; + const ::sw::mark::IMark* m_pMark; + + Impl(SwDoc& rDoc, const enum RangePosition eRange, + SwFrameFormat* const pTableFormat, + 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_pTableFormat(pTableFormat) + , m_pMark(nullptr) + { + m_pTableFormat && StartListening(pTableFormat->GetNotifier()); + } + + 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_pTableFormat = nullptr; + EndListeningAll(); + } + + const ::sw::mark::IMark* GetBookmark() const { return m_pMark; } + void SetMark(::sw::mark::IMark& rMark) + { + EndListeningAll(); + m_pTableFormat = 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_pTableFormat = 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(SwFrameFormat& 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() +{ +} + +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); +} + +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(); + } + + const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent()); + SwCursor aCursor(aPos, nullptr); + if (GetPositions(aCursor)) + { + UnoActionContext aAction(& m_pImpl->m_rDoc); + 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); + } +} + +namespace +{ + class theSwXTextRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangeUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextRange::getUnoTunnelId() +{ + return theSwXTextRangeUnoTunnelId::get().getSeq(); +} + +// XUnoTunnel +sal_Int64 SAL_CALL +SwXTextRange::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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()) + { + if (m_pImpl->m_eRangePosition == RANGE_IS_TABLE && + m_pImpl->m_pTableFormat) + { + SwTable const*const pTable = SwTable::FindTable( m_pImpl->m_pTableFormat ); + SwTableNode const*const pTableNode = pTable->GetTableNode(); + const SwPosition aPosition( *pTableNode ); + m_pImpl->m_xParentText = + ::sw::CreateParentXText(m_pImpl->m_rDoc, aPosition); + } + } + 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 + { + throw uno::RuntimeException(); + } + 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 + { + throw uno::RuntimeException(); + } + 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) && 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) const +{ + ::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) +{ + bool bRet = false; + + uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY); + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + SwXTextPortion* pPortion = nullptr; + SwXText* pText = nullptr; + SwXParagraph* pPara = nullptr; + if(xRangeTunnel.is()) + { + pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + pCursor = + ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel); + pPortion= + ::sw::UnoTunnelGetImplementation<SwXTextPortion>(xRangeTunnel); + pText = ::sw::UnoTunnelGetImplementation<SwXText>(xRangeTunnel); + pPara = ::sw::UnoTunnelGetImplementation<SwXParagraph>(xRangeTunnel); + } + + // 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 (pText) + { + xTextCursor.set( pText->CreateCursor() ); + xTextCursor->gotoEnd(true); + pCursor = + comphelper::getUnoTunnelImplementation<OTextCursorHelper>(xTextCursor); + } + if(pRange && &pRange->GetDoc() == rToFill.GetDoc()) + { + bRet = pRange->GetPositions(rToFill); + } + 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(); + 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(); + } + + if (!m_pImpl->GetBookmark()) + { + throw uno::RuntimeException(); + } + const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent()); + const auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos)); + if (!GetPositions(*pNewCursor)) + { + throw uno::RuntimeException(); + } + + return SwXParaFrameEnumeration::Create(*pNewCursor, PARAFRAME_PORTION_TEXTRANGE); +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwXTextRange::createEnumeration() +{ + SolarMutexGuard g; + + if (!m_pImpl->GetBookmark()) + { + throw uno::RuntimeException(); + } + const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent()); + auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos)); + if (!GetPositions(*pNewCursor)) + { + throw uno::RuntimeException(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); + } + 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() throw () 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()) + { + 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); + } + } + } +} + +SwXTextRanges* SwXTextRanges::Create(SwPaM *const pPaM) + { return new SwXTextRangesImpl(pPaM); } + +namespace +{ + class theSwXTextRangesUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangesUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextRanges::getUnoTunnelId() + { return theSwXTextRangesUnoTunnelId::get().getSeq(); } + +sal_Int64 SAL_CALL +SwXTextRangesImpl::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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 *const pDoc = rCursor.GetDoc(); + UnoActionContext aAction(pDoc); + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr); + if (rCursor.HasMark()) + { + pDoc->getIDocumentContentOperations().DeleteAndJoin(rCursor); + } + if (!rString.isEmpty()) + { + const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR( + *pDoc, rCursor, rString, false ) ); + OSL_ENSURE( bSuccess, "DocInsertStringSplitCR" ); + SwUnoCursorHelper::SelectPam(rCursor, true); + rCursor.Left(rString.getLength()); + } + pDoc->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() throw () 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; +}; + +} + +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..3fef41218 --- /dev/null +++ b/sw/source/core/unocore/unoparagraph.cxx @@ -0,0 +1,1417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/interfacecontainer2.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.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 <osl/mutex.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 SfxItemPropertySimpleEntry& rEntry, + bool &rAttrSetFetched ); + +class SwXParagraph::Impl + : public SvtListener +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + SwXParagraph& m_rThis; + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 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_EventListeners(m_Mutex) + , 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 SfxItemPropertySimpleEntry& 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); + m_EventListeners.disposeAndClear(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; +} + +namespace +{ + class theSwXParagraphUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXParagraphUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXParagraph::getUnoTunnelId() +{ + return theSwXParagraphUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXParagraph::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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) + { + 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; + uno::Sequence<OUString> aPropertyNames { rPropertyName }; + uno::Sequence<uno::Any> aValues(1); + aValues.getArray()[0] = rValue; + m_pImpl->SetPropertyValues_Impl( aPropertyNames, aValues ); +} + +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() ); + for (sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++) + { + SfxItemPropertySimpleEntry 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)); + } + aValues[nProp].Name = pPropertyNames[nProp]; + aValues[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 SfxItemPropertySimpleEntry& 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) + { + // 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) + { + 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++) + { + SfxItemPropertySimpleEntry 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]; + + SfxItemPropertySimpleEntry 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 + OSL_FAIL( "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; + + 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.begin()); + 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; + + SfxItemPropertySimpleEntry 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 + OSL_FAIL( "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, const OUString& rPropertyName, sal_uInt16 nWID) +{ + if(!nWID) + { + if(rPropertyName == UNO_NAME_ANCHOR_TYPE) + nWID = FN_UNO_ANCHOR_TYPE; + else if(rPropertyName == UNO_NAME_ANCHOR_TYPES) + nWID = FN_UNO_ANCHOR_TYPES; + else if(rPropertyName == 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(1); + text::TextContentAnchorType* pArray = aTypes.getArray(); + pArray[0] = 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 SfxItemPropertySimpleEntry& 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_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; + SfxItemPropertySimpleEntry 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) + { + SfxItemPropertySimpleEntry 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 ); + SfxItemPropertySimpleEntry 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) + { + std::set<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; + } + + SfxItemPropertySimpleEntry 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*/) +{ + SolarMutexGuard aGuard; + // 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)); + m_pImpl->m_EventListeners.disposeAndClear(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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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..07af7a238 --- /dev/null +++ b/sw/source/core/unocore/unoport.cxx @@ -0,0 +1,825 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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) + , m_bIsCollapsed(false) +{ + 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) +{ + 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) +{ + 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 ); + } + 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 SfxItemPropertySimpleEntry& 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; + 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_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 = SwUnoCursorHelper::getCursorPropertyValue( + rEntry, *pUnoCursor, &rVal, eTemp ); + if(!bDone) + { + if(!pSet) + { + pSet = std::make_unique<SfxItemSet>( + pUnoCursor->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_CONTAINER, + RES_UNKNOWNATR_CONTAINER>{}); + 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 SfxItemPropertySimpleEntry* 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() ); + for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++) + { + const SfxItemPropertySimpleEntry* 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 ) ); + + aValues[nProp].Name = pPropertyNames[nProp]; + aValues[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 SfxItemPropertySimpleEntry* 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 + OSL_FAIL( "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 SwXTextPortion::getPropertyValuesTolerant( + const uno::Sequence< OUString >& rPropertyNames ) +{ + SolarMutexGuard aGuard; + + 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.begin()); + 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 = + 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 SfxItemPropertySimpleEntry* 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 + OSL_FAIL( "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); +} + +namespace +{ + class theSwXTextPortionUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextPortionUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextPortion::getUnoTunnelId() +{ + return theSwXTextPortionUnoTunnelId::get().getSeq(); +} + +sal_Int64 SwXTextPortion::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextPortion>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + +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..8b968bb82 --- /dev/null +++ b/sw/source/core/unocore/unoportenum.cxx @@ -0,0 +1,1489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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) + { + 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(); + if (!pTextNode) + { + // no need to consider marks starting after aEndOfPara + SwPosition aEndOfPara(*rUnoCursor.GetPoint()); + aEndOfPara.nContent = aEndOfPara.nNode.GetNode().GetTextNode()->Len(); + const IDocumentMarkAccess::const_iterator_t pCandidatesEnd = + pMarkAccess->findFirstBookmarkStartsAfter(aEndOfPara); + + // search for all bookmarks that start or end in this paragraph + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin(); + ppMark != pCandidatesEnd; + ++ppMark) + { + ::sw::mark::IMark* const pBkmk = *ppMark; + lcl_FillBookmark(pBkmk, nOwnNode, rDoc, rBkmArr); + } + } + else + { + // 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); + } + } + } + + class theSwXTextPortionEnumerationUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextPortionEnumerationUnoTunnelId > {}; + 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() +{ + return theSwXTextPortionEnumerationUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXTextPortionEnumeration::getSomething( + const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextPortionEnumeration>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ) ); + } + return 0; +} + +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_Portions() +{ + 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 const & rPortions ) + : m_Portions( rPortions ) +{ + m_pUnoCursor = rParaCursor.GetDoc()->CreateUnoCursor(*rParaCursor.GetPoint()); +} + +SwXTextPortionEnumeration::~SwXTextPortionEnumeration() +{ + SolarMutexGuard aGuard; + 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* pDoc = 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; + if (pDoc) + { + pFieldmark = pDoc->getIDocumentMarkAccess()-> + getFieldmarkAt(*pUnoCursor->GetMark()); + } + SwXTextPortion* pPortion = new SwXTextPortion( + pUnoCursor, i_xParentText, PORTION_FIELD_START); + xRef = pPortion; + if (pFieldmark && pDoc) + { + pPortion->SetBookmark( + SwXFieldmark::CreateXFieldmark(*pDoc, pFieldmark)); + } + } + else if (CH_TXT_ATR_FIELDSEP == Char) + { + // TODO how to get the field? + SwXTextPortion* pPortion = new SwXTextPortion( + pUnoCursor, i_xParentText, PORTION_FIELD_SEP); + xRef = pPortion; + } + else if (CH_TXT_ATR_FIELDEND == Char) + { + ::sw::mark::IFieldmark* pFieldmark = nullptr; + if (pDoc) + { + pFieldmark = pDoc->getIDocumentMarkAccess()-> + getFieldmarkAt(*pUnoCursor->GetMark()); + } + SwXTextPortion* pPortion = new SwXTextPortion( + pUnoCursor, i_xParentText, PORTION_FIELD_END); + xRef = pPortion; + if (pFieldmark && pDoc) + { + pPortion->SetBookmark( + SwXFieldmark::CreateXFieldmark(*pDoc, pFieldmark)); + } + } + else if (CH_TXT_ATR_FORMELEMENT == Char) + { + ::sw::mark::IFieldmark* pFieldmark = nullptr; + if (pDoc) + { + pFieldmark = pDoc->getIDocumentMarkAccess()->getFieldmarkAt(*pUnoCursor->GetMark()); + } + SwXTextPortion* pPortion = new SwXTextPortion( + pUnoCursor, i_xParentText, PORTION_FIELD_START_END); + xRef = pPortion; + if (pFieldmark && pDoc) + { + pPortion->SetBookmark( + SwXFieldmark::CreateXFieldmark(*pDoc, 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* pDoc = pUnoCursor->GetDoc(); + SwFormatRefMark& rRefMark = const_cast<SwFormatRefMark&>( + static_cast<const SwFormatRefMark&>(rAttr.GetAttr())); + Reference<XTextContent> xContent; + if (!xContent.is()) + { + xContent = SwXReferenceMark::CreateXReferenceMark(*pDoc, &rRefMark); + } + + SwXTextPortion* pPortion = nullptr; + 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) +{ + 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* pDoc = pUnoCursor->GetDoc(); + SwTOXMark & rTOXMark = static_cast<SwTOXMark&>(rAttr.GetAttr()); + + const Reference<XTextContent> xContent = + SwXDocumentIndexMark::CreateXDocumentIndexMark(*pDoc, & rTOXMark); + + SwXTextPortion* pPortion = nullptr; + 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))); + SwXTextPortion * pPortion(nullptr); + 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; +} + +/** + * 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 std::set<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 + 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) + { + 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 ( SwSoftPageBreakList::iterator aIter = rBreakArr.begin(), + aEnd = rBreakArr.end(); + aIter != aEnd; ) + { + 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) + // otherwise, the portion for the attribute is inserted into rPortions! + Reference<XTextRange> xRef; + SwDoc* pDoc = pUnoCursor->GetDoc(); + //search for special text attributes - first some ends + size_t nEndIndex = 0; + sal_Int32 nNextEnd = 0; + while(nEndIndex < pHints->Count() && + (!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; + } + } + } + nEndIndex++; + } + + // then some starts + size_t nStartIndex = 0; + sal_Int32 nNextStart = 0; + while(nStartIndex < pHints->Count() && + 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; + SwXTextPortion* pPortion; + xRef = pPortion = + new SwXTextPortion( + pUnoCursor, xParent, PORTION_FIELD); + Reference<XTextField> const xField = + SwXTextField::CreateXTextField(pDoc, + &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 ) + { + SwXTextPortion* pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION_END ); + pPortion->SetBookmark(SwXBookmark::CreateXBookmark( + *pDoc, pAnnotationMark)); + xRef = pPortion; + } + else + { + SwXTextPortion* pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION ); + Reference<XTextField> xField = + SwXTextField::CreateXTextField(pDoc, + &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; + SwXTextPortion* pPortion = + new SwXTextPortion( pUnoCursor, xParent, PORTION_FIELD); + xRef = pPortion; + Reference<XTextField> xField = + SwXTextField::CreateXTextField(pDoc, + &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; + SwXTextPortion* pPortion; + xRef = pPortion = new SwXTextPortion( + pUnoCursor, xParent, PORTION_FOOTNOTE); + Reference<XFootnote> xContent = + SwXFootnotes::GetObject(*pDoc, 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: + 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_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 ) + { + const SwPosition* pStart = rUnoCursor.GetPoint(); + const SwNodeIndex nOwnNode = pStart->nNode; + + for(size_t nRed = 0; 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 std::set<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 std::set<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) + { + 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, + std::set<sal_Int32>& rFramePositions) +{ + for (const auto& rFrame : rFrames) + { + if (rFrame.nIndex < nCurrentIndex) + continue; + + if (rFrame.nIndex > nCurrentIndex) + break; + + const SwModify* pFrame = rFrame.pFrameClient->GetRegisteredIn(); + if (!pFrame) + continue; + + auto& rFormat = *static_cast<SwFrameFormat*>(const_cast<SwModify*>(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 + { + const SwModify * const pFrame = + i_rFrames.front().pFrameClient->GetRegisteredIn(); + if (pFrame) // Frame could be disposed + { + SwXTextPortion* pPortion = new SwXTextPortion(i_pUnoCursor, i_xParent, + *static_cast<SwFrameFormat*>( const_cast<SwModify*>( 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 * const pDoc = pUnoCursor->GetDoc(); + + std::deque<sal_Int32> FieldMarks; + lcl_FillFieldMarkArray(FieldMarks, *pUnoCursor, i_nStartPos); + + SwXBookmarkPortion_ImplList Bookmarks; + lcl_FillBookmarkArray(*pDoc, *pUnoCursor, Bookmarks); + + SwXRedlinePortion_ImplList Redlines; + lcl_FillRedlineArray(*pDoc, *pUnoCursor, Redlines); + + SwSoftPageBreakList SoftPageBreaks; + lcl_FillSoftPageBreakArray(*pUnoCursor, SoftPageBreaks); + + SwAnnotationStartPortion_ImplList AnnotationStarts; + lcl_FillAnnotationStartArray( *pDoc, *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. + std::set<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); + } + + 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..615b1b04c --- /dev/null +++ b/sw/source/core/unocore/unoredline.cxx @@ -0,0 +1,587 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <IDocumentStylePoolAccess.hxx> +#include <docary.hxx> + +using namespace ::com::sun::star; + +SwXRedlineText::SwXRedlineText(SwDoc* _pDoc, const SwNodeIndex& aIndex) : + SwXText(_pDoc, CursorType::Redline), + aNodeIndex(aIndex) +{ +} + +const SwStartNode* SwXRedlineText::GetStartNode() const +{ + return 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::createTextCursor() +{ + SolarMutexGuard aGuard; + + SwPosition aPos(aNodeIndex); + SwXTextCursor *const 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); +} + +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(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) +{ + uno::Sequence<beans::PropertyValue> aValues(4); + + const SwRedlineData* pNext = rRedline.GetRedlineData().Next(); + if(pNext) + { + beans::PropertyValue* pValues = aValues.getArray(); + pValues[0].Name = UNO_NAME_REDLINE_AUTHOR; + // GetAuthorString(n) walks the SwRedlineData* chain; + // here we always need element 1 + pValues[0].Value <<= rRedline.GetAuthorString(1); + pValues[1].Name = UNO_NAME_REDLINE_DATE_TIME; + pValues[1].Value <<= pNext->GetTimeStamp().GetUNODateTime(); + pValues[2].Name = UNO_NAME_REDLINE_COMMENT; + pValues[2].Value <<= pNext->GetComment(); + pValues[3].Name = UNO_NAME_REDLINE_TYPE; + pValues[3].Value <<= SwRedlineTypeToOUString(pNext->GetType()); + } + return aValues; +} + +uno::Any SwXRedlinePortion::getPropertyValue( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + Validate(); + uno::Any aRet; + if(rPropertyName == UNO_NAME_REDLINE_TEXT) + { + SwNodeIndex* pNodeIdx = m_rRedline.GetContentIdx(); + if(pNodeIdx ) + { + if ( 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; +} + +void SwXRedlinePortion::Validate() +{ + SwUnoCursor& rUnoCursor = GetCursor(); + //search for the redline + SwDoc* pDoc = rUnoCursor.GetDoc(); + const SwRedlineTable& rRedTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + bool bFound = false; + for(size_t nRed = 0; nRed < rRedTable.size() && !bFound; nRed++) + bFound = &m_rRedline == rRedTable[nRed]; + if(!bFound) + throw uno::RuntimeException(); +} + +uno::Sequence< sal_Int8 > SAL_CALL SwXRedlinePortion::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +uno::Any SwXRedlinePortion::GetPropertyValue( const OUString& rPropertyName, const SwRangeRedline& rRedline ) +{ + uno::Any aRet; + if(rPropertyName == UNO_NAME_REDLINE_AUTHOR) + aRet <<= rRedline.GetAuthorString(); + else if(rPropertyName == UNO_NAME_REDLINE_DATE_TIME) + { + aRet <<= rRedline.GetTimeStamp().GetUNODateTime(); + } + else if(rPropertyName == UNO_NAME_REDLINE_COMMENT) + aRet <<= rRedline.GetComment(); + else if(rPropertyName == UNO_NAME_REDLINE_DESCRIPTION) + aRet <<= const_cast<SwRangeRedline&>(rRedline).GetDescr(); + else if(rPropertyName == UNO_NAME_REDLINE_TYPE) + { + aRet <<= SwRedlineTypeToOUString(rRedline.GetType()); + } + else if(rPropertyName == UNO_NAME_REDLINE_SUCCESSOR_DATA) + { + if(rRedline.GetRedlineData().Next()) + aRet <<= lcl_GetSuccessorProperties(rRedline); + } + else if (rPropertyName == UNO_NAME_REDLINE_IDENTIFIER) + { + aRet <<= OUString::number( + sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(&rRedline) ) ); + } + else if (rPropertyName == UNO_NAME_IS_IN_HEADER_FOOTER) + { + aRet <<= rRedline.GetDoc()->IsInHeaderFooter( rRedline.GetPoint()->nNode ); + } + else if (rPropertyName == 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 ( 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), + pDoc(&rDoc), + pRedline(&rRedline) +{ + StartListening(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(!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; + 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(!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 = &pRedline->GetNode(); + if(!bStart && pRedline->HasMark()) + pNode = &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 || !pRedline->HasMark()) + pPoint = pRedline->GetPoint(); + else + pPoint = pRedline->GetMark(); + const uno::Reference<text::XTextRange> xRange = + SwXTextRange::CreateXTextRange(*pDoc, *pPoint, nullptr); + xRet = xRange.get(); + } + break; + default: + OSL_FAIL("illegal node type"); + } + aRet <<= xRet; + } + else if(rPropertyName == UNO_NAME_REDLINE_TEXT) + { + SwNodeIndex* pNodeIdx = pRedline->GetContentIdx(); + if( pNodeIdx ) + { + if ( 1 < ( pNodeIdx->GetNode().EndOfSectionIndex() - pNodeIdx->GetNode().GetIndex() ) ) + { + uno::Reference<text::XText> xRet = new SwXRedlineText(pDoc, *pNodeIdx); + aRet <<= xRet; + } + else { + OSL_FAIL("Empty section in redline portion! (end node immediately follows start node)"); + } + } + } + else + aRet = SwXRedlinePortion::GetPropertyValue(rPropertyName, *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) + { + pDoc = nullptr; + 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(!pDoc) + throw uno::RuntimeException(); + + SwNodeIndex* pNodeIndex = 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(!pDoc) + throw uno::RuntimeException(); + return nullptr != pRedline->GetContentIdx(); +} + +uno::Reference< text::XTextCursor > SwXRedline::createTextCursor() +{ + SolarMutexGuard aGuard; + if(!pDoc) + throw uno::RuntimeException(); + + uno::Reference< text::XTextCursor > xRet; + SwNodeIndex* pNodeIndex = pRedline->GetContentIdx(); + if(!pNodeIndex) + { + throw uno::RuntimeException(); + } + + SwPosition aPos(*pNodeIndex); + SwXTextCursor *const pXCursor = + new SwXTextCursor(*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); + xRet = static_cast<text::XWordCursor*>(pXCursor); + + return xRet; +} + +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..bbab06ed6 --- /dev/null +++ b/sw/source/core/unocore/unoredlines.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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) : + pDoc(&rDoc), + nCurrentIndex(0) +{ + StartListening(pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier()); +} + +SwXRedlineEnumeration::~SwXRedlineEnumeration() +{ +} + +sal_Bool SwXRedlineEnumeration::hasMoreElements() +{ + if(!pDoc) + throw uno::RuntimeException(); + return pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() > nCurrentIndex; +} + +uno::Any SwXRedlineEnumeration::nextElement() +{ + if(!pDoc) + throw uno::RuntimeException(); + const SwRedlineTable& rRedTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + if( rRedTable.size() <= nCurrentIndex ) + throw container::NoSuchElementException(); + uno::Reference <beans::XPropertySet> xRet = SwXRedlines::GetObject( *rRedTable[nCurrentIndex++], *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) + 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..a69fd2cab --- /dev/null +++ b/sw/source/core/unocore/unorefmk.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 <memory> +#include <utility> + +#include <comphelper/interfacecontainer2.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/mutex.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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 m_EventListeners; + bool m_bIsDescriptor; + SwDoc* m_pDoc; + const SwFormatRefMark* m_pMarkFormat; + OUString m_sMarkName; + + Impl(SwDoc* const pDoc, SwFormatRefMark* const pRefMark) + : m_EventListeners(m_Mutex) + , 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); + m_EventListeners.disposeAndClear(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()) + { + SwXReferenceMark *const pMark(new SwXReferenceMark(&rDoc, pMarkFormat)); + xMark.set(pMark); + if (pMarkFormat) + { + pMarkFormat->SetXRefMark(xMark); + } + // need a permanent Reference to initialize m_wThis + pMark->m_pImpl->m_wThis = xMark; + } + return xMark; +} + +namespace +{ + class theSwXReferenceMarkUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXReferenceMarkUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXReferenceMark::getUnoTunnelId() +{ + return theSwXReferenceMarkUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXReferenceMark::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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 *pDoc2 = rPam.GetDoc(); + + UnoActionContext aCont(pDoc2); + 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); + } + + pDoc2->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 = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + pCursor = + ::sw::UnoTunnelGetImplementation<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(); + const std::unique_ptr<SwPaM> pPam( (pTextMark->End()) + ? new SwPaM( rTextNode, *pTextMark->End(), + rTextNode, pTextMark->GetStart()) + : new SwPaM( 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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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() throw() override { cppu::OWeakObject::acquire(); } + virtual void SAL_CALL release() throw() 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 +class SwXMeta::Impl : public SvtListener +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 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_EventListeners(m_Mutex) + , 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; +} + +// SwModify +void SwXMeta::Impl::Notify(const SfxHint& rHint) +{ + m_pTextPortions.reset(); // throw away cache (SwTextNode changed) + if(rHint.GetId() == SfxHintId::Dying || rHint.GetId() == SfxHintId::Deinitializing) + { + 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); + m_EventListeners.disposeAndClear(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::getUnoTunnelImplementation<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; +} + +namespace +{ + class theSwXMetaUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXMetaUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXMeta::getUnoTunnelId() +{ + return theSwXMetaUnoTunnelId::get().getSeq(); +} + +// XUnoTunnel +sal_Int64 SAL_CALL +SwXMeta::getSomething( const uno::Sequence< sal_Int8 > & i_rId ) +{ + return ::sw::UnoTunnelImpl<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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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)); + m_pImpl->m_EventListeners.disposeAndClear(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 * const pDoc( pTextNode->GetDoc() ); + pDoc->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( + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel)); + OTextCursorHelper *const pCursor( pRange ? nullptr : + ::sw::UnoTunnelGetImplementation<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, *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)) +{ + OSL_ENSURE(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 bool bPrefix) +{ + 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); + return bPrefix ? xOdfPrefix : xOdfSuffix; +} + +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) +{ + 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(true)); + } + if (o_pSuffix) + { + *o_pSuffix = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(false)); + } + } 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); + 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..831f0bda3 --- /dev/null +++ b/sw/source/core/unocore/unosect.cxx @@ -0,0 +1,1735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/interfacecontainer2.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/mutex.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 <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> + +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 +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + SwXTextSection & m_rThis; + uno::WeakReference<uno::XInterface> m_wThis; + const SfxItemPropertySet & m_rPropSet; + ::comphelper::OInterfaceContainerHelper2 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) + : SvtListener() + , m_rThis(rThis) + , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_SECTION)) + , m_EventListeners(m_Mutex) + , 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); + m_EventListeners.disposeAndClear(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() ) + { + SwXTextSection *const 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() +{ +} + +namespace +{ + class theSwXTextSectionUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextSectionUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextSection::getUnoTunnelId() +{ + return theSwXTextSectionUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXTextSection::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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 = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + pCursor = + ::sw::UnoTunnelGetImplementation<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); + + SfxItemSet aSet( + pDoc->GetAttrPool(), + svl::Items< + 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>{}); + 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() ) + { + SwPaM aPaM(*pIdx); + aPaM.Move( fnMoveForward, GoInContent ); + + const SwEndNode* pEndNode = pIdx->GetNode().EndOfSectionNode(); + SwPaM aEnd(*pEndNode); + aEnd.Move( fnMoveBackward, GoInContent ); + 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 + m_pImpl->m_EventListeners.addInterface(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 + m_pImpl->m_EventListeners.removeInterface(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::unique_ptr<SfxItemSet> const& pItemSet, + bool const bLinkModeChanged, bool const bLinkUpdateAlways = true) +{ + if (pFormat) + { + 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, pItemSet.get(), + 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::unique_ptr<SfxItemSet> pItemSet; + bool bLinkModeChanged = false; + bool bLinkMode = false; + + for (sal_Int32 nProperty = 0; nProperty < rPropertyNames.getLength(); + nProperty++) + { + SfxItemPropertySimpleEntry 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) + + pSectionData->GetLinkFileName().getToken(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(); + pItemSet.reset( new SfxItemSet(*rOldAttrSet.GetPool(), {{pEntry->nWID, pEntry->nWID}})); + pItemSet->Put(rOldAttrSet); + m_rPropSet.setPropertyValue(*pEntry, + pValues[nProperty], *pItemSet); + } + 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, pItemSet, 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; + + uno::Sequence< OUString > aPropertyNames { rPropertyName }; + uno::Sequence< uno::Any > aValues(1); + aValues.getArray()[0] = rValue; + m_pImpl->SetPropertyValues_Impl( aPropertyNames, aValues ); +} + +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++) + { + SfxItemPropertySimpleEntry 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], OUString(), 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; + SfxItemPropertySimpleEntry 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(); + } + + SfxItemPropertySimpleEntry 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::unique_ptr<SfxItemSet> pNewAttrSet; + 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(); + pNewAttrSet.reset( new SfxItemSet(*rOldAttrSet.GetPool(), {{pEntry->nWID, pEntry->nWID}})); + pNewAttrSet->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, pNewAttrSet, bLinkModeChanged); +} + +uno::Any SAL_CALL +SwXTextSection::getPropertyDefault(const OUString& rPropertyName) +{ + SolarMutexGuard aGuard; + + uno::Any aRet; + SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat(); + SfxItemPropertySimpleEntry 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, OUString(), 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..81f1a6a2e --- /dev/null +++ b/sw/source/core/unocore/unosett.cxx @@ -0,0 +1,2446 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <unostyle.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 <fmtclds.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 <com/sun/star/style/VerticalAlignment.hpp> +#include <o3tl/any.hxx> +#include <o3tl/enumarray.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> +#include <numeric> + +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(); + } +} +// Constants for the css::text::ColumnSeparatorStyle +#define API_COL_LINE_NONE 0 +#define API_COL_LINE_SOLID 1 +#define API_COL_LINE_DOTTED 2 +#define API_COL_LINE_DASHED 3 + +#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[] = + { + { OUString(UNO_NAME_ANCHOR_CHAR_STYLE_NAME),WID_ANCHOR_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_BEGIN_NOTICE), WID_BEGIN_NOTICE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_STYLE_NAME), WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_END_NOTICE), WID_END_NOTICE , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_FOOTNOTE_COUNTING), WID_FOOTNOTE_COUNTING, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_NUMBERING_TYPE), WID_NUMBERING_TYPE, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PAGE_STYLE_NAME), WID_PAGE_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PARA_STYLE_NAME), WID_PARAGRAPH_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_POSITION_END_OF_DOC), WID_POSITION_END_OF_DOC,cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PREFIX), WID_PREFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_START_AT), WID_START_AT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SUFFIX), WID_SUFFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 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[] = + { + { OUString(UNO_NAME_ANCHOR_CHAR_STYLE_NAME),WID_ANCHOR_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_CHAR_STYLE_NAME), WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_NUMBERING_TYPE), WID_NUMBERING_TYPE, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PAGE_STYLE_NAME), WID_PAGE_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PARA_STYLE_NAME), WID_PARAGRAPH_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_PREFIX), WID_PREFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_START_AT), WID_START_AT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SUFFIX), WID_SUFFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(), 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[] = + { + { OUString(UNO_NAME_IS_ABSOLUTE_MARGINS), WID_IS_ABS_MARGINS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_AUTOMATIC), WID_IS_AUTOMATIC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_CONTINUOUS_NUMBERING), WID_CONTINUOUS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_NAME), WID_RULE_NAME , ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(UNO_NAME_NUMBERING_IS_OUTLINE), WID_IS_OUTLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DEFAULT_LIST_ID), WID_DEFAULT_LIST_ID, ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { OUString(), 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[] = + { + { OUString(UNO_NAME_CHAR_STYLE_NAME), WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_COUNT_EMPTY_LINES), WID_COUNT_EMPTY_LINES , cppu::UnoType<bool>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_COUNT_LINES_IN_FRAMES), WID_COUNT_LINES_IN_FRAMES, cppu::UnoType<bool>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_DISTANCE), WID_DISTANCE , ::cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_IS_ON), WID_NUM_ON, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_INTERVAL), WID_INTERVAL , ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEPARATOR_TEXT), WID_SEPARATOR_TEXT, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, + { OUString(UNO_NAME_NUMBER_POSITION), WID_NUMBER_POSITION, ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_NUMBERING_TYPE), WID_NUMBERING_TYPE , ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0}, + { OUString(UNO_NAME_RESTART_AT_EACH_PAGE), WID_RESTART_AT_EACH_PAGE, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, + { OUString(UNO_NAME_SEPARATOR_INTERVAL), WID_SEPARATOR_INTERVAL, ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0}, + { OUString(), 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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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) + { + const SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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 = convertMm100ToTwip(nVal); + if (nTmp > SAL_MAX_UINT16) + nTmp = SAL_MAX_UINT16; + aFontMetric.SetPosFromLeft( static_cast< sal_uInt16 >(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 SfxItemPropertySimpleEntry* 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"); +} + +static const char aInvalidStyle[] = "__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(const OUString &rName) +{ + return rName == aInvalidStyle; +} + +namespace +{ + class theSwXNumberingRulesUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXNumberingRulesUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXNumberingRules::getUnoTunnelId() +{ + return theSwXNumberingRulesUnoTunnelId::get().getSeq(); +} + +// return implementation specific data +sal_Int64 SwXNumberingRules::getSomething( const uno::Sequence< sal_Int8 > & rId ) +{ + if( isUnoTunnelId<SwXNumberingRules>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + +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 + 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(); + } + 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; +} + +static const char* 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( static_cast<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()) + { + //BulletId + nINT16 = rFormat.GetBulletChar(); + aPropertyValues.push_back(comphelper::makePropertyValue("BulletId", nINT16)); + + const vcl::Font* pFont = rFormat.GetBulletFont(); + + //BulletChar + aUString = OUString(rFormat.GetBulletChar()); + 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( static_cast<sal_uInt16>(nIndex) )); + + OUString sHeadingStyleName; + OUString sParagraphStyleName; + + SetPropertiesToNumFormat(aFormat, m_sNewCharStyleNames[nIndex], + &m_sNewBulletFontNames[nIndex], + &sHeadingStyleName, &sParagraphStyleName, + m_pDoc, 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(static_cast<sal_uInt16>(nIndex), aFormat); +} + +void SwXNumberingRules::SetPropertiesToNumFormat( + SwNumFormat & aFormat, + OUString & rCharStyleName, OUString *const pBulletFontName, + OUString *const pHeadingStyleName, + OUString *const pParagraphStyleName, + SwDoc *const pDoc, + const uno::Sequence<beans::PropertyValue>& rProperties) +{ + 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 ); + if (sCharFormatName == UNO_NAME_CHARACTER_FORMAT_NONE) + { + rCharStyleName = aInvalidStyle; + aFormat.SetCharFormat(nullptr); + } + else if(pDoc) + { + const SwCharFormats* pFormats = pDoc->GetCharFormats(); + const size_t nChCount = pFormats->size(); + + SwCharFormat* pCharFormat = nullptr; + if (!sCharFormatName.isEmpty()) + { + for(size_t j = 0; j< nChCount; ++j) + { + SwCharFormat* pTmp = (*pFormats)[j]; + if(pTmp->GetName() == sCharFormatName) + { + pCharFormat = pTmp; + break; + } + } + if(!pCharFormat) + { + + SfxStyleSheetBase* pBase; + SfxStyleSheetBasePool* pPool = pDoc->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(convertMm100ToTwip(nValue)); + } + else if (rProp.Name == UNO_NAME_SYMBOL_TEXT_DISTANCE) + { + sal_Int32 nValue = 0; + rProp.Value >>= nValue; + if (nValue >= 0) + aFormat.SetCharTextDistance(static_cast<short>(convertMm100ToTwip(nValue))); + 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 = convertMm100ToTwip(nValue); + 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 = convertMm100ToTwip(nValue); + 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 = convertMm100ToTwip(nValue); + aFormat.SetFirstLineIndent( nValue ); + } + else if (rProp.Name == UNO_NAME_INDENT_AT) + { + sal_Int32 nValue = 0; + rProp.Value >>= nValue; + nValue = convertMm100ToTwip(nValue); + 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* pLclDocShell = nullptr; + if( !sBulletFontName.isEmpty() && pDoc && (pLclDocShell = pDoc->GetDocShell()) ) + { + 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.getLength() == 1) + { + aFormat.SetBulletChar(aChar.toChar()); + } + else if(aChar.isEmpty()) + { + // If w:lvlText's value is null - set bullet char to zero + aFormat.SetBulletChar(u'\0'); + } + 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) + { + size.Width = convertMm100ToTwip(size.Width); + size.Height = convertMm100ToTwip(size.Height); + pSetSize->setWidth( size.Width ); + pSetSize->setHeight( size.Height ); + } + 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) + { + 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(); + } +} + +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() +{ +} + +OUString SwXTextColumns::getImplementationName() +{ + return "SwXTextColumns"; +} + +sal_Bool SwXTextColumns::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXTextColumns::getSupportedServiceNames() +{ + Sequence<OUString> aRet { "com.sun.star.text.TextColumns" }; + return aRet; +} + +SwXTextColumns::SwXTextColumns() : + m_nReference(0), + m_bIsAutomaticWidth(true), + m_nAutoDistance(0), + m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_COLUMS)), + m_nSepLineWidth(0), + m_nSepLineColor(0), //black + m_nSepLineHeightRelative(100),//full height + m_nSepLineVertAlign(style::VerticalAlignment_MIDDLE), + m_bSepLineIsOn(false), + m_nSepLineStyle(API_COL_LINE_NONE) // None +{ +} + +SwXTextColumns::SwXTextColumns(const SwFormatCol& rFormatCol) : + m_nReference(0), + m_aTextColumns(rFormatCol.GetNumCols()), + m_bIsAutomaticWidth(rFormatCol.IsOrtho()), + m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_COLUMS)) +{ + const sal_uInt16 nItemGutterWidth = rFormatCol.GetGutterWidth(); + m_nAutoDistance = m_bIsAutomaticWidth ? + USHRT_MAX == nItemGutterWidth ? DEF_GUTTER_WIDTH : static_cast<sal_Int32>(nItemGutterWidth) + : 0; + m_nAutoDistance = convertTwipToMm100(m_nAutoDistance); + + TextColumn* pColumns = m_aTextColumns.getArray(); + const SwColumns& rCols = rFormatCol.GetColumns(); + for(sal_Int32 i = 0; i < m_aTextColumns.getLength(); ++i) + { + const SwColumn* pCol = &rCols[i]; + + pColumns[i].Width = pCol->GetWishWidth(); + m_nReference += pColumns[i].Width; + pColumns[i].LeftMargin = convertTwipToMm100(pCol->GetLeft ()); + pColumns[i].RightMargin = convertTwipToMm100(pCol->GetRight()); + } + if(!m_aTextColumns.hasElements()) + m_nReference = USHRT_MAX; + + m_nSepLineWidth = rFormatCol.GetLineWidth(); + m_nSepLineColor = rFormatCol.GetLineColor(); + m_nSepLineHeightRelative = rFormatCol.GetLineHeight(); + m_bSepLineIsOn = rFormatCol.GetLineAdj() != COLADJ_NONE; + sal_Int8 nStyle = API_COL_LINE_NONE; + switch (rFormatCol.GetLineStyle()) + { + case SvxBorderLineStyle::SOLID: nStyle = API_COL_LINE_SOLID; break; + case SvxBorderLineStyle::DOTTED: nStyle= API_COL_LINE_DOTTED; break; + case SvxBorderLineStyle::DASHED: nStyle= API_COL_LINE_DASHED; break; + default: break; + } + m_nSepLineStyle = nStyle; + switch(rFormatCol.GetLineAdj()) + { + case COLADJ_TOP: m_nSepLineVertAlign = style::VerticalAlignment_TOP; break; + case COLADJ_BOTTOM: m_nSepLineVertAlign = style::VerticalAlignment_BOTTOM; break; + case COLADJ_CENTER: + case COLADJ_NONE: m_nSepLineVertAlign = style::VerticalAlignment_MIDDLE; + } +} + +SwXTextColumns::~SwXTextColumns() +{ +} + +sal_Int32 SwXTextColumns::getReferenceValue() +{ + SolarMutexGuard aGuard; + return m_nReference; +} + +sal_Int16 SwXTextColumns::getColumnCount() +{ + SolarMutexGuard aGuard; + return static_cast< sal_Int16>( m_aTextColumns.getLength() ); +} + +void SwXTextColumns::setColumnCount(sal_Int16 nColumns) +{ + SolarMutexGuard aGuard; + if(nColumns <= 0) + throw uno::RuntimeException(); + m_bIsAutomaticWidth = true; + m_aTextColumns.realloc(nColumns); + TextColumn* pCols = m_aTextColumns.getArray(); + m_nReference = USHRT_MAX; + sal_Int32 nWidth = m_nReference / nColumns; + sal_Int32 nDiff = m_nReference - nWidth * nColumns; + sal_Int32 nDist = m_nAutoDistance / 2; + for(sal_Int16 i = 0; i < nColumns; i++) + { + pCols[i].Width = nWidth; + pCols[i].LeftMargin = i == 0 ? 0 : nDist; + pCols[i].RightMargin = i == nColumns - 1 ? 0 : nDist; + } + pCols[nColumns - 1].Width += nDiff; +} + +uno::Sequence< TextColumn > SwXTextColumns::getColumns() +{ + SolarMutexGuard aGuard; + return m_aTextColumns; +} + +void SwXTextColumns::setColumns(const uno::Sequence< TextColumn >& rColumns) +{ + SolarMutexGuard aGuard; + sal_Int32 nReferenceTemp = std::accumulate(rColumns.begin(), rColumns.end(), sal_Int32(0), + [](const sal_Int32 nSum, const TextColumn& rCol) { return nSum + rCol.Width; }); + m_bIsAutomaticWidth = false; + m_nReference = !nReferenceTemp ? USHRT_MAX : nReferenceTemp; + m_aTextColumns = rColumns; +} + +uno::Reference< XPropertySetInfo > SwXTextColumns::getPropertySetInfo( ) +{ + static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropSet->getPropertySetInfo(); + return aRef; +} + +void SwXTextColumns::setPropertyValue( const OUString& rPropertyName, const Any& aValue ) +{ + const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->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 ) ); + + switch(pEntry->nWID) + { + case WID_TXTCOL_LINE_WIDTH: + { + sal_Int32 nTmp = 0; + aValue >>= nTmp; + if(nTmp < 0) + throw IllegalArgumentException(); + m_nSepLineWidth = convertMm100ToTwip(nTmp); + } + break; + case WID_TXTCOL_LINE_COLOR: + aValue >>= m_nSepLineColor; + break; + case WID_TXTCOL_LINE_STYLE: + { + aValue >>= m_nSepLineStyle; + } + break; + case WID_TXTCOL_LINE_REL_HGT: + { + sal_Int8 nTmp = 0; + aValue >>= nTmp; + if(nTmp < 0) + throw IllegalArgumentException(); + m_nSepLineHeightRelative = nTmp; + } + break; + case WID_TXTCOL_LINE_ALIGN: + { + style::VerticalAlignment eAlign; + if(!(aValue >>= eAlign) ) + { + sal_Int8 nTmp = 0; + if (! ( aValue >>= nTmp ) ) + throw IllegalArgumentException(); + m_nSepLineVertAlign = static_cast<style::VerticalAlignment>(nTmp); + } + else + m_nSepLineVertAlign = eAlign; + } + break; + case WID_TXTCOL_LINE_IS_ON: + m_bSepLineIsOn = *o3tl::doAccess<bool>(aValue); + break; + case WID_TXTCOL_AUTO_DISTANCE: + { + sal_Int32 nTmp = 0; + aValue >>= nTmp; + if(nTmp < 0 || nTmp >= m_nReference) + throw IllegalArgumentException(); + m_nAutoDistance = nTmp; + sal_Int32 nColumns = m_aTextColumns.getLength(); + TextColumn* pCols = m_aTextColumns.getArray(); + sal_Int32 nDist = m_nAutoDistance / 2; + for(sal_Int32 i = 0; i < nColumns; i++) + { + pCols[i].LeftMargin = i == 0 ? 0 : nDist; + pCols[i].RightMargin = i == nColumns - 1 ? 0 : nDist; + } + } + break; + } +} + +Any SwXTextColumns::getPropertyValue( const OUString& rPropertyName ) +{ + const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pEntry) + throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) ); + + Any aRet; + switch(pEntry->nWID) + { + case WID_TXTCOL_LINE_WIDTH: + aRet <<= static_cast < sal_Int32 >(convertTwipToMm100(m_nSepLineWidth)); + break; + case WID_TXTCOL_LINE_COLOR: + aRet <<= m_nSepLineColor; + break; + case WID_TXTCOL_LINE_STYLE: + aRet <<= m_nSepLineStyle; + break; + case WID_TXTCOL_LINE_REL_HGT: + aRet <<= m_nSepLineHeightRelative; + break; + case WID_TXTCOL_LINE_ALIGN: + aRet <<= m_nSepLineVertAlign; + break; + case WID_TXTCOL_LINE_IS_ON: + aRet <<= m_bSepLineIsOn; + break; + case WID_TXTCOL_IS_AUTOMATIC : + aRet <<= m_bIsAutomaticWidth; + break; + case WID_TXTCOL_AUTO_DISTANCE: + aRet <<= m_nAutoDistance; + break; + } + return aRet; +} + +void SwXTextColumns::addPropertyChangeListener( + const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ ) +{ +} + +void SwXTextColumns::removePropertyChangeListener( + const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ ) +{ +} + +void SwXTextColumns::addVetoableChangeListener( + const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ ) +{ +} + +void SwXTextColumns::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ ) +{ +} + +namespace +{ + class theSwXTextColumnsUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextColumnsUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextColumns::getUnoTunnelId() +{ + return theSwXTextColumnsUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXTextColumns::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextColumns>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + +/* 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..a4656104a --- /dev/null +++ b/sw/source/core/unocore/unosrch.cxx @@ -0,0 +1,691 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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> + +using namespace ::com::sun::star; + +class SwSearchProperties_Impl +{ + std::unique_ptr<std::unique_ptr<beans::PropertyValue>[]> pValueArr; + const PropertyEntryVector_t aPropertyEntries; + + 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() : + aPropertyEntries( aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR)->getPropertyMap().getPropertyEntries() ) +{ + size_t nArrLen = aPropertyEntries.size(); + pValueArr.reset( new std::unique_ptr<beans::PropertyValue>[nArrLen] ); +} + +void SwSearchProperties_Impl::SetProperties(const uno::Sequence< beans::PropertyValue >& aSearchAttribs) +{ + //delete all existing values + for(size_t i = 0; i < aPropertyEntries.size(); ++i) + { + pValueArr[i].reset(); + } + + for(const beans::PropertyValue& rSearchAttrib : aSearchAttribs) + { + const OUString& sName = rSearchAttrib.Name; + auto aIt = std::find_if(aPropertyEntries.begin(), aPropertyEntries.end(), + [&sName](const SfxItemPropertyNamedEntry& rProp) { return rProp.sName == sName; }); + if( aIt == aPropertyEntries.end() ) + throw beans::UnknownPropertyException(sName); + auto nIndex = static_cast<sal_uInt32>(std::distance(aPropertyEntries.begin(), aIt)); + pValueArr[nIndex].reset( new beans::PropertyValue(rSearchAttrib) ); + } +} + +uno::Sequence< beans::PropertyValue > SwSearchProperties_Impl::GetProperties() const +{ + sal_uInt32 nPropCount = 0; + for( size_t i = 0; i < aPropertyEntries.size(); i++) + if(pValueArr[i]) + nPropCount++; + + uno::Sequence< beans::PropertyValue > aRet(nPropCount); + beans::PropertyValue* pProps = aRet.getArray(); + nPropCount = 0; + for(size_t i = 0; i < aPropertyEntries.size(); i++) + { + if(pValueArr[i]) + { + pProps[nPropCount++] = *(pValueArr[i]); + } + } + 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 ; + + PropertyEntryVector_t::const_iterator aIt = aPropertyEntries.begin(); + for(size_t i = 0; i < aPropertyEntries.size(); i++, ++aIt) + { + if(pValueArr[i]) + { + SfxPoolItem* pTempItem = nullptr; + switch(aIt->nWID) + { + case RES_BOX: + if(!pBoxItem) + pBoxItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pBoxItem.get(); + break; + case RES_CHRATR_BOX: + if(!pCharBoxItem) + pCharBoxItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCharBoxItem.get(); + break; + case RES_BREAK: + if(!pBreakItem) + pBreakItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pBreakItem.get(); + break; + case RES_CHRATR_AUTOKERN: + if(!pAutoKernItem) + pAutoKernItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pAutoKernItem.get(); + break; + case RES_CHRATR_BACKGROUND: + if(!pBrushItem) + pBrushItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pBrushItem.get(); + break; + case RES_CHRATR_CASEMAP: + if(!pCasemapItem) + pCasemapItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCasemapItem.get(); + break; + case RES_CHRATR_COLOR: + if(!pCharColorItem) + pCharColorItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCharColorItem.get(); + break; + case RES_CHRATR_CONTOUR: + if(!pContourItem) + pContourItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pContourItem.get(); + break; + case RES_CHRATR_CROSSEDOUT: + if(!pCrossedOutItem) + pCrossedOutItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCrossedOutItem.get(); + break; + case RES_CHRATR_ESCAPEMENT: + if(!pEscItem) + pEscItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pEscItem.get(); + break; + case RES_CHRATR_BLINK: + if(!pBlinkItem) + pBlinkItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pBlinkItem.get(); + break; + case RES_CHRATR_FONT: + if(!pFontItem) + pFontItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontItem.get(); + break; + case RES_CHRATR_FONTSIZE: + if(!pFontSizeItem) + pFontSizeItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontSizeItem.get(); + break; + case RES_CHRATR_KERNING: + if(!pKernItem) + pKernItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pKernItem.get(); + break; + case RES_CHRATR_LANGUAGE: + if(!pLangItem) + pLangItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pLangItem.get(); + break; + case RES_CHRATR_NOHYPHEN: + if(!pNHyphItem) + pNHyphItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pNHyphItem.get(); + break; + case RES_CHRATR_POSTURE: + if(!pPostItem) + pPostItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pPostItem.get(); + break; + case RES_CHRATR_SHADOWED: + if(!pShadItem) + pShadItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pShadItem.get(); + break; + case RES_TXTATR_CHARFMT: + if(!pCharFormatItem) + pCharFormatItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCharFormatItem.get(); + break; + case RES_CHRATR_UNDERLINE: + if(!pULineItem) + pULineItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pULineItem.get(); + break; + case RES_CHRATR_OVERLINE: + if(!pOLineItem) + pOLineItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pOLineItem.get(); + break; + case RES_CHRATR_WEIGHT: + if(!pWeightItem) + pWeightItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pWeightItem.get(); + break; + case RES_PARATR_DROP: + if(!pDropItem) + pDropItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pDropItem.get(); + break; + case RES_TXTATR_INETFMT: + if(!pInetItem) + pInetItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pInetItem.get(); + break; + case RES_PAGEDESC: + if(!pDescItem) + pDescItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pDescItem.get(); + break; + case RES_PARATR_ADJUST: + if(!pAdjItem) + pAdjItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pAdjItem.get(); + break; + case RES_BACKGROUND: + if(!pBackItem) + pBackItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pBackItem.get(); + break; + case RES_UL_SPACE: + if(!pULItem) + pULItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pULItem.get(); + break; + case RES_LR_SPACE: + if(!pLRItem) + pLRItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pLRItem.get(); + break; + case RES_KEEP: + if(!pKeepItem) + pKeepItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pKeepItem.get(); + break; + case RES_LINENUMBER: + if(!pLineNumItem) + pLineNumItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pLineNumItem.get(); + break; + case RES_PARATR_LINESPACING: + if(!pLineSpaceItem) + pLineSpaceItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pLineSpaceItem.get(); + break; + case RES_PARATR_REGISTER: + if(!pRegItem) + pRegItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pRegItem.get(); + break; + case RES_PARATR_SPLIT: + if(!pSplitItem) + pSplitItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pSplitItem.get(); + break; + case RES_PARATR_TABSTOP: + if(!pTabItem) + pTabItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pTabItem.get(); + break; + case RES_CHRATR_WORDLINEMODE: + if(!pWLineItem) + pWLineItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pWLineItem.get(); + break; + case RES_CHRATR_CJK_FONT: + if(!pFontCJKItem ) + pFontCJKItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontCJKItem.get(); + break; + case RES_CHRATR_CJK_FONTSIZE: + if(!pFontSizeCJKItem ) + pFontSizeCJKItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontSizeCJKItem.get(); + break; + case RES_CHRATR_CJK_LANGUAGE: + if(!pCJKLangItem ) + pCJKLangItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCJKLangItem.get(); + break; + case RES_CHRATR_CJK_POSTURE: + if(!pCJKPostureItem ) + pCJKPostureItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCJKPostureItem.get(); + break; + case RES_CHRATR_CJK_WEIGHT: + if(!pCJKWeightItem ) + pCJKWeightItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCJKWeightItem.get(); + break; + case RES_CHRATR_CTL_FONT: + if(!pFontCTLItem ) + pFontCTLItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontCTLItem.get(); + break; + case RES_CHRATR_CTL_FONTSIZE: + if(!pFontSizeCTLItem ) + pFontSizeCTLItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pFontSizeCTLItem.get(); + break; + case RES_CHRATR_CTL_LANGUAGE: + if(!pCTLLangItem ) + pCTLLangItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCTLLangItem.get(); + break; + case RES_CHRATR_CTL_POSTURE: + if(!pCTLPostureItem ) + pCTLPostureItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCTLPostureItem.get(); + break; + case RES_CHRATR_CTL_WEIGHT: + if(!pCTLWeightItem ) + pCTLWeightItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pCTLWeightItem.get(); + break; + case RES_CHRATR_SHADOW: + if(!pShadowItem ) + pShadowItem.reset(rSet.GetPool()->GetDefaultItem(aIt->nWID).Clone()); + pTempItem = pShadowItem.get(); + break; + } + if(pTempItem) + { + if(bIsValueSearch) + { + pTempItem->PutValue(pValueArr[i]->Value, aIt->nMemberId); + rSet.Put(*pTempItem); + } + else + rSet.InvalidateItem( pTempItem->Which() ); + } + } + } +} + +bool SwSearchProperties_Impl::HasAttributes() const +{ + for(size_t i = 0; i < aPropertyEntries.size(); i++) + if(pValueArr[i]) + return true; + return false; +} + +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 +{ + class theSwXTextSearchUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextSearchUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextSearch::getUnoTunnelId() +{ + return theSwXTextSearchUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXTextSearch::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextSearch>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + +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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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..fedbf6ece --- /dev/null +++ b/sw/source/core/unocore/unostyle.cxx @@ -0,0 +1,5596 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svxids.hrc> +#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/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 <GetMetricVal.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 <cassert> +#include <memory> +#include <set> +#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; + const char* 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, const char* 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) + { } + }; + static 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) + { } + }; + static 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(const OUString& 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase); + uno::Any GetStyleProperty_Impl(const SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& rBase); + void PutItemToSet(const SvxSetItem* pSetItem, const SfxItemPropertySet& rPropSet, const SfxItemPropertySimpleEntry& 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( ) throw() override {SwXStyle::acquire();} + virtual void SAL_CALL release( ) throw() 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.begin(), [] (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 || nIndex >= static_cast<sal_Int32>(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::makeAny(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) + aOpt.SetInputStream(xInputStream); + else + throw IllegalArgumentException("Parameter 'InputStream' could not be converted to " + "type 'com::sun::star::io::XInputStream'", + nullptr, 0); + } + } + const ErrCode nErr = m_pDocShell->LoadStylesFromFile( rURL, aOpt, true ); + if(nErr) + throw io::IOException(); +} + +uno::Sequence< beans::PropertyValue > SwXStyleFamilies::getStyleLoaderOptions() +{ + SolarMutexGuard aGuard; + uno::Sequence< beans::PropertyValue > aSeq(5); + beans::PropertyValue* pArray = aSeq.getArray(); + const uno::Any aVal(true); + pArray[0] = beans::PropertyValue(UNO_NAME_LOAD_TEXT_STYLES, -1, aVal, beans::PropertyState_DIRECT_VALUE); + pArray[1] = beans::PropertyValue(UNO_NAME_LOAD_FRAME_STYLES, -1, aVal, beans::PropertyState_DIRECT_VALUE); + pArray[2] = beans::PropertyValue(UNO_NAME_LOAD_PAGE_STYLES, -1, aVal, beans::PropertyState_DIRECT_VALUE); + pArray[3] = beans::PropertyValue(UNO_NAME_LOAD_NUMBERING_STYLES, -1, aVal, beans::PropertyState_DIRECT_VALUE); + pArray[4] = beans::PropertyValue(UNO_NAME_OVERWRITE_STYLES, -1, aVal, beans::PropertyState_DIRECT_VALUE); + return aSeq; +} + +static bool lcl_GetHeaderFooterItem( + SfxItemSet const& rSet, OUString const& rPropName, bool const bFooter, + SvxSetItem const*& o_rpItem) +{ + SfxItemState eState = rSet.GetItemState( + bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, + false, reinterpret_cast<const SfxPoolItem**>(&o_rpItem)); + if (SfxItemState::SET != eState && + rPropName == UNO_NAME_FIRST_IS_SHARED) + { // fdo#79269 header may not exist, check footer then + eState = rSet.GetItemState( + (!bFooter) ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, + false, reinterpret_cast<const SfxPoolItem**>(&o_rpItem)); + } + return SfxItemState::SET == eState; +} + +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(); + 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::makeAny(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 = nullptr; + if(xStyleTunnel.is()) + { + pNewStyle = reinterpret_cast< SwXStyle * >( + sal::static_int_cast< sal_IntPtr >( xStyleTunnel->getSomething( SwXStyle::getUnoTunnelId()) )); + } + + 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::getUnoTunnelImplementation<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::makeAny(SwResId(m_rEntry.m_pResId)); +} + + +SwXStyle* XStyleFamily::FindStyle(const OUString& 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 PropertyEntryVector_t aPropertyEntries; + std::map<OUString, uno::Any> m_vPropertyValues; +public: + explicit SwStyleProperties_Impl(const SfxItemPropertyMap& rMap) + : aPropertyEntries(rMap.getPropertyEntries()) + { } + + bool AllowsKey(const OUString& rName) + { + return std::any_of(aPropertyEntries.begin(), aPropertyEntries.end(), + [rName] (const SfxItemPropertyNamedEntry& rEntry) {return rName == rEntry.sName;} ); + } + 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 +{ + class theSwXStyleUnoTunnelId : public rtl::Static<UnoTunnelIdInit, theSwXStyleUnoTunnelId> {}; +} + +const uno::Sequence<sal_Int8>& SwXStyle::getUnoTunnelId() +{ + return theSwXStyleUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXStyle::getSomething(const uno::Sequence<sal_Int8>& rId) +{ + if(isUnoTunnelId<SwXStyle>(rId)) + { + return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this)); + } + return 0; +} + + +uno::Sequence< OUString > SwXStyle::getSupportedServiceNames() +{ + 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 char* STR_POOLPAGE_ARY[] = + { + // Page styles + STR_POOLPAGE_STANDARD, + STR_POOLPAGE_FIRST, + STR_POOLPAGE_LEFT, + STR_POOLPAGE_RIGHT, + STR_POOLPAGE_JAKET, + 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry& 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::makeAny(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 SfxItemPropertySimpleEntry&, 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 = reinterpret_cast<SwXNumberingRules*>(sal::static_int_cast<sal_IntPtr>(xNumberTunnel->getSomething(SwXNumberingRules::getUnoTunnelId()))); + 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry&, 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<sal_uInt16(RES_PAGEDESC)>(const SfxItemPropertySimpleEntry& 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; + const SfxPoolItem* pItem; + if(SfxItemState::SET == rStyleSet.GetItemState(RES_PAGEDESC, true, &pItem)) + pNewDesc.reset(new SwFormatPageDesc(*static_cast<const 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(*pNewDesc); + } +} +template<> +void SwXStyle::SetPropertyValue<sal_uInt16(RES_TEXT_VERT_ADJUST)>(const SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase) +{ + if(!o_rStyleBase.getNewBase()->IsUserDefined() || !rValue.has<paragraphstyle_t>()) + throw lang::IllegalArgumentException(); + static std::unique_ptr<std::map<paragraphstyle_t, SfxStyleSearchBits>> pUnoToCore; + if(!pUnoToCore) + { + pUnoToCore.reset(new std::map<paragraphstyle_t, SfxStyleSearchBits>); + 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 SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry& 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; + const SfxPoolItem* pItem; + if(SfxItemState::SET == rStyleSet.GetItemState(RES_TXTATR_CJK_RUBY, true, &pItem)) + pRuby.reset(new SwFormatRuby(*static_cast<const SwFormatRuby*>(pItem))); + 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(*pRuby); + SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase); +} +template<> +void SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_DROP)>(const SfxItemPropertySimpleEntry& 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; + const SfxPoolItem* pItem; + if(SfxItemState::SET == rStyleSet.GetItemState(RES_PARATR_DROP, true, &pItem)) + pDrop.reset(new SwFormatDrop(*static_cast<const SwFormatDrop*>(pItem))); + 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(*pDrop); +} +template<> +void SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_NUMRULE)>(const SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, const uno::Any&, SwStyleBase_Impl&)>; + static std::unique_ptr<std::map<propertytype_t, coresetter_t>> pUnoToCore; + if(!pUnoToCore) + { + pUnoToCore.reset(new 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>) }, + { 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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase); +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_IS_PHYSICAL>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&) +{ + SfxStyleSheetBase* pBase(GetStyleSheetBase()); + if(!pBase) + return uno::makeAny(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::makeAny<bool>(bPhys); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_HIDDEN>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&) +{ + SfxStyleSheetBase* pBase(GetStyleSheetBase()); + if(!pBase) + return uno::makeAny(false); + rtl::Reference<SwDocStyleSheet> xBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase))); + return uno::makeAny(xBase->IsHidden()); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_STYLE_INTEROP_GRAB_BAG>(const SfxItemPropertySimpleEntry&, 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 SfxItemPropertySimpleEntry& 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::makeAny<OUString>("[From printer settings]"); + SfxPrinter* pPrinter = GetDoc()->getIDocumentDeviceAccess().getPrinter(false); + if(!pPrinter) + return uno::Any(); + return uno::makeAny(pPrinter->GetPaperBinName(nBin)); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_NUM_RULES>(const SfxItemPropertySimpleEntry&, 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::makeAny<uno::Reference<container::XIndexReplace>>(xRules); +} +template<> +uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_PARATR_OUTLINELEVEL)>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + SAL_WARN_IF(SfxStyleFamily::Para != GetFamily(), "sw.uno", "only paras"); + return uno::makeAny<sal_Int16>(rBase.getNewBase()->GetCollection()->GetAttrOutlineLevel()); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_FOLLOW_STYLE>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + OUString aString; + SwStyleNameMapper::FillProgName(rBase.getNewBase()->GetFollow(), aString, lcl_GetSwEnumFromSfxEnum(GetFamily())); + return uno::makeAny(aString); +} +template<> +uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_PAGEDESC)>(const SfxItemPropertySimpleEntry& 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 SfxPoolItem* pItem; + if(SfxItemState::SET != rBase.GetItemSet().GetItemState(RES_PAGEDESC, true, &pItem)) + return uno::Any(); + const SwPageDesc* pDesc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc(); + if(!pDesc) + return uno::Any(); + OUString aString; + SwStyleNameMapper::FillProgName(pDesc->GetName(), aString, SwGetPoolIdFromName::PageDesc); + return uno::makeAny(aString); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_IS_AUTO_UPDATE>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + switch(GetFamily()) + { + case SfxStyleFamily::Para : return uno::makeAny<bool>(rBase.getNewBase()->GetCollection()->IsAutoUpdateFormat()); + case SfxStyleFamily::Frame: return uno::makeAny<bool>(rBase.getNewBase()->GetFrameFormat()->IsAutoUpdateFormat()); + default: return uno::Any(); + } +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_DISPLAY_NAME>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + return uno::makeAny(rBase.getNewBase()->GetName()); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_PARA_STYLE_CONDITIONS>(const SfxItemPropertySimpleEntry&, 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 : 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::makeAny(aSeq); +} +template<> +uno::Any SwXStyle::GetStyleProperty<FN_UNO_CATEGORY>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + static std::unique_ptr<std::map<collectionbits_t, paragraphstyle_t>> pUnoToCore; + if(!pUnoToCore) + { + pUnoToCore.reset(new std::map<collectionbits_t, paragraphstyle_t>); + 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::makeAny<sal_Int16>(-1); + return uno::makeAny(pUnoToCoreIt->second); +} +template<> +uno::Any SwXStyle::GetStyleProperty<SID_SWREGISTER_COLLECTION>(const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + const SwPageDesc *pPageDesc = rBase.getNewBase()->GetPageDesc(); + if(!pPageDesc) + return uno::makeAny(OUString()); + const SwTextFormatColl* pCol = pPageDesc->GetRegisterFormatColl(); + if(!pCol) + return uno::makeAny(OUString()); + OUString aName; + SwStyleNameMapper::FillProgName(pCol->GetName(), aName, SwGetPoolIdFromName::TxtColl); + return uno::makeAny(aName); +} +template<> +uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_BACKGROUND)>(const SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase) +{ + PrepareStyleBase(rBase); + const SfxItemSet& rSet = rBase.GetItemSet(); + if (rSet.Get(XATTR_FILLBMP_TILE).GetValue()) + return uno::makeAny(drawing::BitmapMode_REPEAT); + if (rSet.Get(XATTR_FILLBMP_STRETCH).GetValue()) + return uno::makeAny(drawing::BitmapMode_STRETCH); + return uno::makeAny(drawing::BitmapMode_NO_REPEAT); +} +template<> +uno::Any SwXStyle::GetStyleProperty<HINT_BEGIN>(const SfxItemPropertySimpleEntry& 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 SfxItemPropertySimpleEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase) +{ + using propertytype_t = decltype(rEntry.nWID); + using coresetter_t = std::function<uno::Any(SwXStyle&, const SfxItemPropertySimpleEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&)>; + static std::unique_ptr<std::map<propertytype_t, coresetter_t>> pUnoToCore; + if(!pUnoToCore) + { + pUnoToCore.reset(new 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>) }, + { 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 SfxItemPropertySimpleEntry* 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()); + // workaround for bad designed API + try + { + for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp) + aValues[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, const OUString& rPropertyName) +{ + if(eFamily != SfxStyleFamily::Page) + return &rSet; + const bool isFooter = rPropertyName.startsWith("Footer"); + if(!isFooter && !rPropertyName.startsWith("Header") && rPropertyName != 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 SfxItemPropertySimpleEntry* 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) + { + // 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 SfxItemPropertySimpleEntry* 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_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(); + SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + 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(); + + SvxLRSpaceItem aLR(RES_LR_SPACE); + sal_Int32 nSize = GetMetricVal(CM_1) * 2; + aLR.SetLeft(nSize); + aLR.SetLeft(nSize); + SvxULSpaceItem aUL(RES_UL_SPACE); + aUL.SetUpper(static_cast<sal_uInt16>(nSize)); + aUL.SetLower(static_cast<sal_uInt16>(nSize)); + pPageFormat->SetFormatAttr(aLR); + pPageFormat->SetFormatAttr(aUL); + 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; + 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 SfxItemPropertySimpleEntry* 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, aRet[i]); + } + else if(pEntry->nWID != rSet.GetPool()->GetSlotId(pEntry->nWID)) + { + const SfxPoolItem& rItem = rSet.GetPool()->GetDefaultItem(pEntry->nWID); + rItem.QueryValue(aRet[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 SfxItemPropertySimpleEntry& rEntry, const uno::Any& rVal, SwStyleBase_Impl& rBaseImpl) +{ + // create a new SvxSetItem and get it's ItemSet as new target + const 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(*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 SfxItemPropertySimpleEntry* 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 + if (SfxItemState::SET == aBaseImpl.GetItemSet().GetItemState( + bFooter ? SID_ATTR_PAGE_HEADERSET : SID_ATTR_PAGE_FOOTERSET, + false, reinterpret_cast<const SfxPoolItem**>(&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 + SfxItemSet aTempSet(*aBaseImpl.GetItemSet().GetPool(), + svl::Items<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>{}); + + // set correct parent to get the XFILL_NONE FillStyle as needed + aTempSet.SetParent(&GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_ON, true)); + aTempSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(MM50, MM50))); + 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 = nullptr; + + if(SfxItemState::SET == aBaseImpl.GetItemSet().GetItemState(bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, false, reinterpret_cast<const SfxPoolItem**>(&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 paret at ItemSet from SetItem + rSetSet.SetParent(nullptr); + + // set the new SvxSetItem at the real target and delete it + aBaseImpl.GetItemSet().Put(*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 SfxPoolItem& 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); + 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, aRet[nProp]); + else + aRet[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 SfxItemPropertySimpleEntry* 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)); + aRet[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 + aRet[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; + if(SfxItemState::SET == rSet.GetItemState(bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, false, reinterpret_cast<const SfxPoolItem**>(&pSetItem))) + { + // set at SfxItemSet of the corresponding SfxSetItem + const SfxItemSet& rSetSet = pSetItem->GetItemSet(); + { + SwStyleBase_Impl::ItemSetOverrider o(aBase, &const_cast<SfxItemSet&>(rSetSet)); + aRet[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()) + aRet[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(aRet[nProp], pEntry->nMemberId); + } + break; + default: + aRet[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); + 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::makeAny(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 aIter; + SwDoc* pDoc; + IStyleAccess::SwAutoStyleFamily eFamily; +public: + SwAutoStylesEnumImpl( SwDoc* pInitDoc, IStyleAccess::SwAutoStyleFamily eFam ); + bool hasMoreElements() { return aIter != mAutoStyles.end(); } + std::shared_ptr<SfxItemSet> const & nextElement() { return *(aIter++); } + IStyleAccess::SwAutoStyleFamily getFamily() const { return eFamily; } + SwDoc* getDoc() const { return pDoc; } +}; + +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(); + } + + const sal_uInt16* pRange = nullptr; + 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 = nullptr;//aTextNodeSetRange; + 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 SfxItemPropertySimpleEntry* 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* pInitDoc, IStyleAccess::SwAutoStyleFamily eFam ) +: pDoc( pInitDoc ), 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 = pDoc->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<SfxItemSet>( rAttrPool, svl::Items<RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY>{} ); + pItemSet->Put( *pRubyItem ); + mAutoStyles.push_back( pItemSet ); + } + } + } + else + { + pDoc->GetIStyleAccess().getAllStyles( mAutoStyles, eFamily ); + } + + aIter = mAutoStyles.begin(); +} + +SwXAutoStylesEnumerator::SwXAutoStylesEnumerator( SwDoc* pDoc, IStyleAccess::SwAutoStyleFamily eFam ) +: m_pImpl( new SwAutoStylesEnumImpl( pDoc, eFam ) ) +{ + // Register ourselves as a listener to the document (via the page descriptor) + StartListening(pDoc->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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* 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*/ ) +{ + uno::Sequence< uno::Any > aRet(0); + return aRet; +} + +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(); + PropertyEntryVector_t aPropVector = rMap.getPropertyEntries(); + + 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& rProp : aPropVector ) + { + if ( rProp.nWID == nWID ) + { + beans::PropertyValue aPropertyValue; + aPropertyValue.Name = rProp.sName; + pItem->QueryValue( aPropertyValue.Value, rProp.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, const OUString& 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::makeAny(m_pTableAutoFormat->GetName()); + else + throw css::beans::UnknownPropertyException(rPropertyName); + + return uno::makeAny(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() +{ + SolarMutexGuard aGuard; + return comphelper::mapKeysToSequence(GetCellStyleNameMap()); +} + +sal_Bool SAL_CALL SwXTextTableStyle::hasByName(const OUString& rName) +{ + SolarMutexGuard aGuard; + 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, const OUString& sName, OUString* pParentName) +{ + if (sName.isEmpty()) + return nullptr; + + SwBoxAutoFormat* pBoxAutoFormat = pDocShell->GetDoc()->GetCellStyles().GetBoxFormat(sName); + if (!pBoxAutoFormat) + { + sal_Int32 nSeparatorIndex, nTemplateIndex; + OUString sParentName, sCellSubName; + + nSeparatorIndex = sName.lastIndexOf('.'); + if (0 >= nSeparatorIndex) + return nullptr; + + sParentName = sName.copy(0, nSeparatorIndex); + sCellSubName = sName.copy(nSeparatorIndex+1); + nTemplateIndex = sCellSubName.toInt32()-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*/) +{ + SolarMutexGuard aGuard; + // 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 SfxItemPropertySimpleEntry *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 SfxItemPropertySimpleEntry *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 SfxItemPropertySimpleEntry* 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 SfxItemPropertySimpleEntry* pEntry = rMap.getByName(rPropertyName); + if(pEntry) + { + 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..762dcf73d --- /dev/null +++ b/sw/source/core/unocore/unotbl.cxx @@ -0,0 +1,4194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <svx/svxids.hrc> +#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 <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <shellres.hxx> +#include <docary.hxx> +#include <ndole.hxx> +#include <ndtxt.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/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 <osl/mutex.hxx> +#include <SwStyleNameMapper.hxx> +#include <frmatr.hxx> +#include <sortopt.hxx> +#include <rtl/math.hxx> +#include <sal/log.hxx> +#include <editeng/frmdiritem.hxx> +#include <comphelper/interfacecontainer2.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 <poolfmt.hxx> +#include <frameformats.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 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( + uno::Reference<uno::XInterface> const& xSource, + ::comphelper::OInterfaceContainerHelper2& rListeners) + { + rListeners.notifyEach( + &chart::XChartDataChangeEventListener::chartDataChanged, + createChartEvent(xSource)); + } + + void lcl_SendChartEvent( + uno::Reference<uno::XInterface> const& xSource, + ::cppu::OMultiTypeInterfaceContainerHelper const& rListeners) + { + auto pContainer(rListeners.getContainer(cppu::UnoType<chart::XChartDataChangeEventListener>::get())); + if (pContainer) + pContainer->notifyEach( + &chart::XChartDataChangeEventListener::chartDataChanged, + createChartEvent(xSource)); + } +} + +#define UNO_TABLE_COLUMN_SUM 10000 + + +static bool lcl_LineToSvxLine(const table::BorderLine& rLine, SvxBorderLine& rSvxLine) +{ + rSvxLine.SetColor(Color(rLine.Color)); + + rSvxLine.GuessLinesWidths( SvxBorderLineStyle::NONE, + convertMm100ToTwip( rLine.OuterLineWidth ), + convertMm100ToTwip( rLine.InnerLineWidth ), + convertMm100ToTwip( rLine.LineDistance ) ); + + return rLine.InnerLineWidth > 0 || rLine.OuterLineWidth > 0; +} + +/// @throws lang::IllegalArgumentException +/// @throws uno::RuntimeException +static void lcl_SetSpecialProperty(SwFrameFormat* pFormat, + const SfxItemPropertySimpleEntry* 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, static_cast<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 ( convertMm100ToTwip ( nWidth ) ); + } + 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 SfxItemPropertySimpleEntry* 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::makeAny<bool>(nRepeat > 0); + return uno::makeAny<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(); + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet.GetItemState(RES_PAGEDESC, false, &pItem)) + { + const SwPageDesc* pDsc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc(); + if(pDsc) + return uno::makeAny<OUString>(SwStyleNameMapper::GetProgName(pDsc->GetName(), SwGetPoolIdFromName::PageDesc )); + } + return uno::makeAny(OUString()); + } + + case RES_ANCHOR: + return uno::makeAny(text::TextContentAnchorType_AT_PARAGRAPH); + + case FN_UNO_ANCHOR_TYPES: + { + uno::Sequence<text::TextContentAnchorType> aTypes{text::TextContentAnchorType_AT_PARAGRAPH}; + return uno::makeAny(aTypes); + } + + case FN_UNO_WRAP : + return uno::makeAny(text::WrapTextMode_NONE); + + case FN_PARAM_LINK_DISPLAY_NAME : + return uno::makeAny(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::makeAny(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) + { + 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 = rCellName.copy(nRowPos).toInt32() - 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 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(); + 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 + sal_uLong nNdPos = rCell.m_pBox->IsValidNumTextNd(); + if(ULONG_MAX != nNdPos) + sw_setString( rCell, OUString(), true ); // true == keep number format + SwDoc* pDoc = rCell.GetDoc(); + UnoActionContext aAction(pDoc); + SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat(); + SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{}); + const SfxPoolItem* pItem; + + //!! 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 + if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem) + || pDoc->GetNumberFormatter()->IsTextFormat(static_cast<const SwTableBoxNumFormat*>(pItem)->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(); +} + +namespace +{ + class theSwXCellUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXCell::getUnoTunnelId() +{ + return theSwXCellUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXCell::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXCell>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + else + return SwXText::getSomething(rId); +} + +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( ) throw() +{ + SwXCellBaseClass::acquire(); +} + +void SAL_CALL SwXCell::release( ) throw() +{ + 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 + sal_uInt32 nNdPos = m_pBox->IsValidNumTextNd(); + if(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); + SfxItemSet aSet(pMyDoc->GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_FORMULA>{}); + const SfxPoolItem* pItem; + SwFrameFormat* pBoxFormat = m_pBox->GetFrameFormat(); + if(SfxItemState::SET != pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem) + || pMyDoc->GetNumberFormatter()->IsTextFormat(static_cast<const SwTableBoxNumFormat*>(pItem)->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 + double fRet; + if(IsValid() && !getString().isEmpty()) + fRet = m_pBox->GetFrameFormat()->GetTableBoxValue().GetValue(); + else + ::rtl::math::setNan( &fRet ); + return fRet; +} + +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); + SwXTextCursor* const pXCursor = + new SwXTextCursor(*GetDoc(), this, CursorType::TableText, aPos); + auto& rUnoCursor(pXCursor->GetCursor()); + rUnoCursor.Move(fnMoveForward, GoInNode); + return static_cast<text::XWordCursor*>(pXCursor); +} + +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::makeAny(m_pBox->getRowSpan()); + break; + 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::makeAny(SwXTextSections::GetObject(*rSect.GetFormat())); + } + break; + case FN_UNO_CELL_NAME: + return uno::makeAny(m_pBox->GetName()); + break; + 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* pDoc = aPos.GetDoc(); + if (!pDoc) + return uno::Any(); + + m_xParentText = sw::CreateParentXText(*pDoc, aPos); + } + + return uno::makeAny(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) + SwTable const*const pTable(&pSttNd->FindTableNode()->GetTable()); + return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::TableText, pSttNd, pTable); +} + +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; + } +} + +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 : 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 + const SfxPoolItem* pItem; + auto pBoxFormat(GetTableBox()->GetFrameFormat()); + SfxItemState eState = pBoxFormat->GetAttrSet().GetItemState(RES_BOXATR_FORMAT, true, &pItem); + + if (eState == SfxItemState::SET) + { + // please note that the language of the numberformat + // is implicitly coded into the below value as well + nFIndex = static_cast<const SwTableBoxNumFormat*>(pItem)->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)) + ::rtl::math::setNan(&fTmp); + 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::makeAny(getValue()) : uno::makeAny(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), + 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, pLine); + if(pLn) + { + // 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 SfxItemPropertySimpleEntry* 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( convertMm100ToTwip(nHeight) ); + 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, 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, pLine); + if(pLn) + { + const SfxItemPropertySimpleEntry* 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, 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 == 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() throw() +{ + SwXTextTableCursor_Base::acquire(); +} + +void SwXTextTableCursor::release() throw() +{ + 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* pDoc = 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); + pDoc->SetBoxAttr(rUnoCursor, *aBrush); + + } + break; + case RES_BOXATR_FORMAT: + { + SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT); + aNumberFormat.PutValue(aValue, 0); + pDoc->SetBoxAttr(rUnoCursor, aNumberFormat); + } + break; + case FN_UNO_PARA_STYLE: + SwUnoCursorHelper::SetTextFormatColl(aValue, rUnoCursor); + break; + default: + { + SfxItemSet aItemSet(pDoc->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: + { + SfxItemSet aSet(rTableCursor.GetDoc()->GetAttrPool(), + svl::Items<RES_CHRATR_BEGIN, RES_FRMATR_END-1, + RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>{}); + 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 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) + { aAnyMap.SetValue( nWhichId, nMemberId, rVal ); } + +bool SwTableProperties_Impl::GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny ) + { return 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(*aItem); + } +} +void SwTableProperties_Impl::ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc) +{ + SfxItemSet aSet( + rDoc.GetAttrPool(), + svl::Items< + 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>{}); + 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; + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::cppu::OMultiTypeInterfaceContainerHelper m_Listeners; + + 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_Listeners(m_Mutex) + , 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; + +}; + +namespace +{ + class theSwXTextTableUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextTableUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextTable::getUnoTunnelId() + { return theSwXTextTableUnoTunnelId::get().getSeq(); } + +sal_Int64 SAL_CALL SwXTextTable::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if(isUnoTunnelId<SwXTextTable>(rId)) + { + return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this)); + } + return 0; +} + + +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 = static_cast<sal_uInt16>(nR); + m_pImpl->m_nColumns = static_cast<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(nullptr); + OTextCursorHelper* pCursor(nullptr); + if(xRangeTunnel.is()) + { + pRange = reinterpret_cast<SwXTextRange*>( + sal::static_int_cast<sal_IntPtr>(xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId()))); + pCursor = reinterpret_cast<OTextCursorHelper*>( + sal::static_int_cast<sal_IntPtr>(xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId()))); + } + 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; + SwFrameFormat* pFormat = 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); +} + +void SAL_CALL SwXTextTable::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 SwXTextTable::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); +} + +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).get(); +} + +} // 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 + lcl_SendChartEvent(*this, m_pImpl->m_Listeners); +} + +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 + m_pImpl->m_Listeners.addInterface( + cppu::UnoType<chart::XChartDataChangeEventListener>::get(), 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 + m_pImpl->m_Listeners.removeInterface( + cppu::UnoType<chart::XChartDataChangeEventListener>::get(), 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)) + { + 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 SfxItemPropertySimpleEntry* 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) + { + lcl_SendChartEvent(*this, m_pImpl->m_Listeners); + m_pImpl->m_bFirstRowAsLabel = bTmp; + } + } + break; + + case FN_UNO_RANGE_COL_LABEL: + { + bool bTmp = *o3tl::doAccess<bool>(aValue); + if (m_pImpl->m_bFirstColumnAsLabel != bTmp) + { + lcl_SendChartEvent(*this, m_pImpl->m_Listeners); + 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(); + + SfxItemSet aSet(pDoc->GetAttrPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + + 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(static_cast<sal_uInt16>(convertMm100ToTwip(aBorder.Distance))); + 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 = convertMm100ToTwip(aTableBorderDistances.LeftDistance); + const sal_uInt16 nRightDistance = convertMm100ToTwip(aTableBorderDistances.RightDistance); + const sal_uInt16 nTopDistance = convertMm100ToTwip(aTableBorderDistances.TopDistance); + const sal_uInt16 nBottomDistance = convertMm100ToTwip(aTableBorderDistances.BottomDistance); + 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(); + pDoc->GetDocShell()->GetFEShell()->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 SfxItemPropertySimpleEntry* 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, OUString(), 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(); + + SfxItemSet aSet(pDoc->GetAttrPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + 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 notfied 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); + m_Listeners.disposeAndClear(ev); + } + else + { + lcl_SendChartEvent(xThis.get(), m_Listeners); + } + } +} + +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: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + SwFrameFormat* m_pFrameFormat; + +public: + uno::WeakReference<uno::XInterface> m_wThis; + ::comphelper::OInterfaceContainerHelper2 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_ChartListeners(m_Mutex) + , 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 +{ + class theSwXCellRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXCellRangeUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXCellRange::getUnoTunnelId() +{ + return theSwXCellRangeUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXCellRange::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXCellRange>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return 0; +} + + +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) +{ + SwXCellRange *const pCellRange(new SwXCellRange(pCursor, rFrameFormat, rDesc)); + uno::Reference<table::XCellRange> xCellRange(pCellRange); + // need a permanent Reference to initialize m_wThis + pCellRange->m_pImpl->m_wThis = xCellRange; + 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 ) + { + 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) + { + const SfxItemPropertySimpleEntry *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 *const pDoc = 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); + pDoc->SetBoxAttr(*m_pImpl->m_pTableCursor, *aBrush); + + } + break; + case RES_BOX : + { + SfxItemSet aSet(pDoc->GetAttrPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + 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); + pDoc->SetTabBorders(*m_pImpl->m_pTableCursor, aSet); + } + break; + case RES_BOXATR_FORMAT: + { + SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT); + static_cast<SfxPoolItem&>(aNumberFormat).PutValue(aValue, 0); + pDoc->SetBoxAttr(rCursor, aNumberFormat); + } + break; + case FN_UNO_RANGE_ROW_LABEL: + { + bool bTmp = *o3tl::doAccess<bool>(aValue); + if (m_pImpl->m_bFirstRowAsLabel != bTmp) + { + lcl_SendChartEvent(*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) + { + lcl_SendChartEvent(*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) + pDoc->SetBoxAlign( rCursor, nAlign ); + } + break; + default: + { + SfxItemSet aItemSet( pDoc->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 SfxItemPropertySimpleEntry *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 *const pDoc = m_pImpl->m_pTableCursor->GetDoc(); + SfxItemSet aSet(pDoc->GetAttrPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + 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: + { + SfxItemSet aSet( + m_pImpl->m_pTableCursor->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_CONTAINER, + RES_UNKNOWNATR_CONTAINER>{}); + // 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 : aRowSeq) + { + rRow = uno::Sequence< uno::Any >(nColCount); + for(auto& rCellAny : 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 : vRows) + { + rRow = uno::Sequence<double>(nColCount); + for(auto& rValue : 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.begin(), + [](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 + m_pImpl->m_ChartListeners.addInterface(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 + m_pImpl->m_ChartListeners.removeInterface(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) + lcl_SendChartEvent(xThis.get(), m_ChartListeners); + else + m_ChartListeners.disposeAndClear(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); + return uno::makeAny(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, static_cast<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::makeAny(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, static_cast<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..394e8ae7d --- /dev/null +++ b/sw/source/core/unocore/unotext.cxx @@ -0,0 +1,2717 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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 <IDocumentUndoRedo.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> + +using namespace ::com::sun::star; + +const char cInvalidObject[] = "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 = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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 = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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) + { + const uno::Reference<lang::XUnoTunnel> xRangeTunnel( + xTextRange, uno::UNO_QUERY); + SwXTextRange *const pRange = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel); + OTextCursorHelper *const pCursor = + ::sw::UnoTunnelGetImplementation<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 = + ::sw::UnoTunnelGetImplementation<SwXDocumentIndexMark>(xContentTunnel); + SwXTextSection *const pSection = + ::sw::UnoTunnelGetImplementation<SwXTextSection>(xContentTunnel); + SwXBookmark *const pBookmark = + ::sw::UnoTunnelGetImplementation<SwXBookmark>(xContentTunnel); + SwXReferenceMark *const pReferenceMark = + ::sw::UnoTunnelGetImplementation<SwXReferenceMark>(xContentTunnel); + SwXMeta *const pMeta = + ::sw::UnoTunnelGetImplementation<SwXMeta>(xContentTunnel); + SwXTextField* pTextField = + ::sw::UnoTunnelGetImplementation<SwXTextField>(xContentTunnel); + if (pTextField && pTextField->GetServiceId() != SwServiceType::FieldTypeAnnotation) + pTextField = nullptr; + + const bool bAttribute = pBookmark || pDocumentIndexMark + || pSection || pReferenceMark || pMeta || 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::getUnoTunnelImplementation<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 = + ::sw::UnoTunnelGetImplementation<SwXTextSection>(xSuccTunnel); + SwXTextTable *const pXTable = + ::sw::UnoTunnelGetImplementation<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::getUnoTunnelImplementation<SwXParagraph>(xNewContent); + if(!pPara || !pPara->IsDescriptor() || !xPredecessor.is()) + { + throw lang::IllegalArgumentException(); + } + + const uno::Reference<lang::XUnoTunnel> xPredTunnel(xPredecessor, + uno::UNO_QUERY); + SwXTextSection *const pXSection = + ::sw::UnoTunnelGetImplementation<SwXTextSection>(xPredTunnel); + SwXTextTable *const pXTable = + ::sw::UnoTunnelGetImplementation<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 = + ::sw::UnoTunnelGetImplementation<SwXTextSection>(xSuccTunnel); + SwXTextTable *const pXTable = + ::sw::UnoTunnelGetImplementation<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 = + ::sw::UnoTunnelGetImplementation<SwXTextSection>(xPredTunnel); + SwXTextTable *const pXTable = + ::sw::UnoTunnelGetImplementation<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::getUnoTunnelImplementation<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(); + } + + SfxItemPropertySimpleEntry 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 sal_uLong 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 +{ + class theSwXTextUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXText::getUnoTunnelId() +{ + return theSwXTextUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL +SwXText::getSomething(const uno::Sequence< sal_Int8 >& rId) +{ + return ::sw::UnoTunnelImpl<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 = + ::sw::UnoTunnelGetImplementation<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(); + } + uno::Reference< text::XTextContent > xRet; + std::unique_ptr<SwUnoInternalPaM> pTempStartPam(new SwUnoInternalPaM(*GetDoc())); + std::unique_ptr< SwUnoInternalPaM > pEndPam(new SwUnoInternalPaM(*GetDoc())); + if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart) || + !::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::getUnoTunnelImplementation<SwXTextRange>(xStart); + SwXTextRange *const pEndRange = + comphelper::getUnoTunnelImplementation<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; + 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; + } + 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(); + if ( !isGraphicNode(pFrameFormat) && + (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() || RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) && + pStartPam->Start()->nNode.GetIndex() <= rAnchor.GetContentAnchor()->nNode.GetIndex() && + pStartPam->End()->nNode.GetIndex() >= rAnchor.GetContentAnchor()->nNode.GetIndex()) + { + if (pFrameFormat->GetName().isEmpty()) + { + aAnchoredObjectsByPtr.insert(pFrameFormat->FindSdrObject()); + } + else + { + aAnchoredObjectsByName.insert(pFrameFormat->GetName()); + } + } + } + + 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()); + rNewFrame.setName(m_pImpl->m_pDoc->GetUniqueFrameName()); + } + + 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); + } + } + } + } + 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::getUnoTunnelImplementation<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 + 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 sal_uLong nStartCellNodeIndex = + aStartCellPam.Start()->nNode.GetIndex(); + const sal_uLong 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); + sal_uLong 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! +} + +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& e) + { + SAL_WARN( "sw.uno", "Exception when getting PropertyState: " + + rName + ". Message: " + e.Message ); + } + } + } +} + +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::makeAny(nCellCount--)); + nCellCount*=-1; + for(auto pxPSet = aMergedCell.aCells.begin()+1; nCellCount<0; ++pxPSet, ++nCellCount) + (*pxPSet)->setPropertyValue(UNO_NAME_ROW_SPAN, uno::makeAny(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& e) + { + SAL_WARN( "sw.uno", "Exception when setting property: " + + rTableProperty.Name + ". Message: " + e.Message ); + } + } + + //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( xSourceTunnel.is() + ? ::sw::UnoTunnelGetImplementation<SwXText>(xSourceTunnel) + : nullptr); + + 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 = + ::sw::UnoTunnelGetImplementation<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(), +1, -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; +} + +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()) ); + 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()) + { + const auto pXHFT(new SwXHeadFootText(rHeadFootFormat, bIsHeader)); + xText.set(pXHFT); + 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> SAL_CALL +SwXHeadFootText::createTextCursor() +{ + SolarMutexGuard aGuard; + + SwFrameFormat & rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() ); + + const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent(); + const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode(); + SwPosition aPos(rNode); + SwXTextCursor *const 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); + // 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); +} + +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..f20b47e15 --- /dev/null +++ b/sw/source/core/unocore/unotextmarkup.cxx @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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() +{ + SolarMutexGuard aGuard; + + uno::Reference< container::XStringKeyMap > xProp = new SwXStringKeyMap; + return xProp; +} + +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; + + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + + if(xRangeTunnel.is()) + { + pRange = reinterpret_cast<SwXTextRange*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId()))); + pCursor = reinterpret_cast<OTextCursorHelper*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId()))); + } + + if (pRange) + { + 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 (pCursor) + { + 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( 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( new 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( 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( new 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..cdb476944 --- /dev/null +++ b/sw/source/core/view/dialoghelp.cxx @@ -0,0 +1,48 @@ +/* -*- 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 <vcl/window.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..b3b08c25a --- /dev/null +++ b/sw/source/core/view/pagepreviewlayout.cxx @@ -0,0 +1,1461 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <pagepreviewlayout.hxx> +#include <prevwpage.hxx> + +#include <algorithm> +#include <tools/fract.hxx> +#include <vcl/window.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 ); + 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 = + static_cast<sal_uInt16>(rProposedStartPos.X() / mnColWidth) + 1; + const sal_uInt16 nRowOfProposed = + static_cast<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( mnPreviewLayoutWidth, + maPreviewDocRect.GetWidth() - aTopLeftPos.X() ) ); + else + aSize.setWidth( std::min( maPreviewDocRect.GetWidth() - aTopLeftPos.X(), + maWinSize.Width() - maAdditionalPaintOffset.X() ) ); + if ( mbDoesLayoutRowsFitIntoWindow ) + aSize.setHeight( std::min( 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( 0L, 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.IsInside( 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(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.IsOver( 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( aPageRect, &mrParentViewShell, true, false, true ); + } + else + { + const bool bIsLeftShadowed = pPage->IsLeftShadowNeeded(); + const bool bIsRightShadowed = pPage->IsRightShadowNeeded(); + + mrParentViewShell.maVisArea = 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( 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 ( !mbNewLayoutDuringPaint ) + { + // update at accessibility interface + mrParentViewShell.Imp()->UpdateAccessiblePreview( + maPreviewPages, + aMapMode.GetScaleX(), + mrLayoutRootFrame.GetPageByPageNum( mnSelectedPageNum ), + maWinSize ); + } + + 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.IsOver( 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..4001314a9 --- /dev/null +++ b/sw/source/core/view/printdata.cxx @@ -0,0 +1,459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/languageoptions.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 *pDoc, const SwViewOption *pViewOpt, OutputDevice *pOutDev ) +{ + DeletePostItData(); + m_pPostItFields.reset(new SetGetExpFields); + sw_GetPostIts( &pDoc->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 + SvtLanguageOptions aLangOpt; + bool bRTL = aLangOpt.IsCJKFontEnabled() || aLangOpt.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 + const OUString aPrintRangeName( "PrintContent" ); + uno::Sequence< OUString > aChoices( 3 ); + uno::Sequence< sal_Bool > aChoicesDisabled( 3 ); + uno::Sequence< OUString > aHelpIds( 3 ); + uno::Sequence< OUString > aWidgetIds( 3 ); + aChoices[0] = SwResId( STR_PRINTOPTUI_PRINTALLPAGES ); + aChoicesDisabled[0] = false; + aHelpIds[0] = ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0"; + aWidgetIds[0] = "rbAllPages"; + aChoices[1] = SwResId( STR_PRINTOPTUI_PRINTPAGES ); + aChoicesDisabled[1] = false; + aHelpIds[1] = ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1"; + aWidgetIds[1] = "rbRangePages"; + aChoices[2] = SwResId( STR_PRINTOPTUI_PRINTSELECTION ); + aChoicesDisabled[2] = !bHasSelection; + aHelpIds[2] = ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:2"; + aWidgetIds[2] = "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.realloc( 5 ); + aChoices[0] = SwResId( STR_PRINTOPTUI_NONE); + aChoices[1] = SwResId( STR_PRINTOPTUI_COMMENTS_ONLY); + aChoices[2] = SwResId( STR_PRINTOPTUI_PLACE_END); + aChoices[3] = SwResId( STR_PRINTOPTUI_PLACE_PAGE); + aChoices[4] = SwResId( STR_PRINTOPTUI_PLACE_MARGINS); + aHelpIds.realloc( 2 ); + aHelpIds[0] = ".HelpID:vcl:PrintDialog:PrintAnnotationMode:FixedText"; + aHelpIds[1] = ".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(); + const OUString aBrochurePropertyName( "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( 2 ); + aBRTLChoices[0] = SwResId( STR_PRINTOPTUI_LEFT_SCRIPT); + aBRTLChoices[1] = 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::getUnoTunnelImplementation<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..b59e43275 --- /dev/null +++ b/sw/source/core/view/vdraw.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 <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <swmodule.hxx> +#include <svtools/accessibilityoptions.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 <basegfx/range/b2irectangle.hxx> + +#include <IDocumentDrawModelAccess.hxx> + +void SwViewShellImp::StartAction() +{ + if ( HasDrawView() ) + { + SET_CURR_SHELL( GetShell() ); + if ( dynamic_cast<const SwFEShell*>( m_pShell) != nullptr ) + static_cast<SwFEShell*>(m_pShell)->HideChainMarker(); // might have changed + } +} + +void SwViewShellImp::EndAction() +{ + if ( HasDrawView() ) + { + SET_CURR_SHELL( GetShell() ); + if ( dynamic_cast<const SwFEShell*>(m_pShell) != nullptr ) + static_cast<SwFEShell*>(m_pShell)->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()||SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews())) + { + 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 aOldOutlinerBackgrdColor; + // 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 ) + { + aOldOutlinerBackgrdColor = + 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( 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( aOldOutlinerBackgrdColor ); + 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.IsInside( 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 || dynamic_cast<const SwDrawContact*>( pCont) == nullptr ) + continue; + + const SwFrame *pAnchor = static_cast<const SwDrawContact*>(pCont)->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.IsInside( 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..317480500 --- /dev/null +++ b/sw/source/core/view/viewimp.cxx @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <pagefrm.hxx> +#include <viewimp.hxx> +#include <viewopt.hxx> +#include <flyfrm.hxx> +#include <layact.hxx> +#include <swregion.hxx> +#include <dview.hxx> +#include <swmodule.hxx> +#include <svx/svdpage.hxx> +#include <accmap.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() +{ + m_pAccessibleMap.reset(); + + m_pPagePreviewLayout.reset(); + + // Make sure HideSdrPage is also executed after ShowSdrPage. + if( m_pDrawView ) + m_pDrawView->HideSdrPage(); + + m_pDrawView.reset(); + + DelRegion(); + + OSL_ENSURE( !m_pLayAction, "Have action for the rest of your life." ); + OSL_ENSURE( !m_pIdleAct,"Be idle for the rest of your life." ); +} + +void SwViewShellImp::DelRegion() +{ + m_pRegion.reset(); +} + +bool SwViewShellImp::AddPaintRect( const SwRect &rRect ) +{ + // In case of tiled rendering the visual area is the last painted tile -> not interesting. + if ( rRect.IsOver( m_pShell->VisArea() ) || comphelper::LibreOfficeKit::isActive() ) + { + if ( !m_pRegion ) + { + // 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_pRegion.reset(new SwRegionRects(rArea)); + } + (*m_pRegion) -= rRect; + return true; + } + return false; +} + +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.IsOver( 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) + OutputDevice* pOutDevForDrawView = GetShell()->GetWin(); + + 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() && + !SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews()) + 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()) ) ); +} + +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(); +} + +/* 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..07df8ba6b --- /dev/null +++ b/sw/source/core/view/viewpg.cxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/fract.hxx> +#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 ) +{ + Imp()->InvalidateAccessiblePreviewSelection( nSelPage ); +} + +// 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 ); + + SET_CURR_SHELL( &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 ); + long nTmp = static_cast<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..ef26275ab --- /dev/null +++ b/sw/source/core/view/viewsh.cxx @@ -0,0 +1,2619 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/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 <cntfrm.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 <pagedesc.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 <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/sdr/overlay/overlaymanager.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/svdpagv.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#if !HAVE_FEATURE_DESKTOP +#include <vcl/sysdata.hxx> +#endif + +bool SwViewShell::mbLstAct = false; +ShellResource *SwViewShell::mpShellRes = nullptr; +vcl::DeleteOnDeinit<std::shared_ptr<weld::Window>> SwViewShell::mpCareDialog(new std::shared_ptr<weld::Window>); + +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 +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwViewShell")); + 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(): 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 (IsPreview() && Imp()->IsAccessible()) + { + Imp()->FireAccessibleEvents(); + } + return; + } + + mbInEndAction = true; + //will this put the EndAction of the last shell in the sequence? + + SwViewShell::mbLstAct = true; + for(SwViewShell& rShell : GetRingContainer()) + { + if(&rShell != this && rShell.ActionPend()) + { + SwViewShell::mbLstAct = false; + break; + } + } + + const bool bIsShellForCheckViewLayout = ( this == GetLayout()->GetCurrShell() ); + + SET_CURR_SHELL( this ); + if ( Imp()->HasDrawView() && !Imp()->GetDrawView()->areMarkHandlesHidden() ) + Imp()->StartAction(); + + if ( Imp()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea() ) + Imp()->DelRegion(); + + const bool bExtraData = ::IsExtraData( GetDoc() ); + + if ( !bIdleEnd ) + { + SwLayAction aAction( GetLayout(), Imp() ); + aAction.SetComplete( false ); + if ( mnLockPaint ) + aAction.SetPaint( false ); + aAction.Action(GetWin()); + } + + 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()->GetRegion() || + 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 = std::move(Imp()->m_pRegion); + + //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(); + + //First Invert then Compress, never the other way round! + pRegion->Invert(); + + pRegion->Compress(); + + 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( 0L, aTmp1.Left() - 1 ) ); + if ( aTmp2.Top() > aRect.Top() ) + aTmp1.SetTop( std::max( 0L, 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.SVRect()); + 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 BeginDarwLayers() defines the clip region for DrawingLayer paint, + // transparent objects in the single rectangles will indeed be painted multiple times. + DLPrePaint2(vcl::Region(aRect.SVRect())); + + if ( bPaintsFromSystem ) + PaintDesktop(*GetOut(), aRect); + if (!comphelper::LibreOfficeKit::isActive()) + pCurrentLayout->PaintSwFrame( *mpOut, aRect ); + else + pCurrentLayout->GetCurrShell()->InvalidateWindows(aRect.SVRect()); + + // #i75172# end DrawingLayer paint + DLPostPaint2(true); + } + } + + lcl_PaintTransparentFormControls(*this, aRect); // i#107365 + } + } + if( bShowCursor ) + static_cast<SwCursorShell*>(this)->ShowCursors( true ); + } + else + { + Imp()->DelRegion(); + mbPaintWorks = true; + } + } + else + mbPaintWorks = true; + + mbInEndAction = false; + SwViewShell::mbLstAct = 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( Imp()->IsAccessible() ) + Imp()->FireAccessibleEvents(); +} + +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 ) +{ + SET_CURR_SHELL( 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() ) + { + 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().IsOver( rRect ) || comphelper::LibreOfficeKit::isActive() ) + rSh.GetWin()->Invalidate( rRect.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().IsInside( rRect ) || IsScrollMDI( this, rRect ) || GetCareDialog(*this) ) + { + if ( !IsViewLocked() ) + { + if( mpWin ) + { + const SwFrame* pRoot = GetLayout(); + int nLoopCnt = 3; + 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) +{ + SET_CURR_SHELL( this ); + + bool bCursor = dynamic_cast<const SwCursorShell*>( this ) != nullptr; + if ( bCursor ) + static_cast<SwCursorShell*>(this)->StartAction(); + else + StartAction(); + + GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB); + + if ( bCursor ) + static_cast<SwCursorShell*>(this)->EndAction(); + else + EndAction(); +} + +/** update all charts for which any table exists */ +void SwViewShell::UpdateAllCharts() +{ + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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 ) +{ + bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr; + if ( bCursor ) + static_cast<SwCursorShell&>(rSh).StartAction(); + else + rSh.StartAction(); + rSh.GetLayout()->InvalidateAllContent( nInv ); + if ( bCursor ) + static_cast<SwCursorShell&>(rSh).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 ) +{ + const bool bIsCursorShell = dynamic_cast<const SwCursorShell*>( &_rSh) != nullptr; + if ( bIsCursorShell ) + static_cast<SwCursorShell&>(_rSh).StartAction(); + else + _rSh.StartAction(); + + _rSh.GetLayout()->InvalidateAllObjPos(); + + if ( bIsCursorShell ) + static_cast<SwCursorShell&>(_rSh).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) + { + 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->ModifyNotification(nullptr, nullptr); + } + } + 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); + + SET_CURR_SHELL( 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 ) + { + 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 ) + { + const SwFrame *pCnt = static_cast<SwCursorShell*>(this)->GetCurrFrame( false ); + const SwPageFrame *pPage; + if ( pCnt && nullptr != (pPage = pCnt->FindPageFrame()) ) + { + const sal_uInt16 nVirtNum = pPage->GetVirtPageNum(); + const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType(); + OUString sDisplay = rNum.GetNumStr( nVirtNum ); + PageNumNotify( this, pCnt->GetPhyPageNum(), nVirtNum, sDisplay ); + + 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::getUnoTunnelImplementation<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()); + + 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()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea() ) + Imp()->DelRegion(); + + SET_CURR_SHELL( this ); + + bool bScrolled = false; + + SwPostItMgr* pPostItMgr = GetPostItMgr(); + + if ( bFull ) + GetWin()->Invalidate(); + else + { + //Calculate amount to be scrolled. + const long nXDiff = aPrevArea.Left() - VisArea().Left(); + const 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()) ); + if ( bBookMode ) + { + const SwPageFrame& rFormatPage = pPage->GetFormatPage(); + aPageRect.SSize( rFormatPage.GetBoundRect(GetWin()).SSize() ); + } + + // #i9719# - consider new border and shadow width + if ( aPageRect.IsOver( 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 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( 0L, 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().IsOver( 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().IsOver( 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() ); + Imp()->GetDrawView()->SetActualWin( GetWin() ); + } + GetWin()->PaintImmediately(); + + if ( pPostItMgr ) // #i88070# + { + pPostItMgr->Rescale(); + pPostItMgr->CalcRects(); + pPostItMgr->LayoutPostIts(); + } + + if ( !bScrolled && pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ) + pPostItMgr->CorrectPositions(); + + if( Imp()->IsAccessible() ) + Imp()->UpdateAccessible(); +} + +bool SwViewShell::SmoothScroll( long lXDiff, 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(); + 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() ); + pVout->SetLineColor( GetWin()->GetLineColor() ); + pVout->SetFillColor( GetWin()->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( 0L, 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::mbLstAct = true; + GetLayout()->PaintSwFrame( *GetOut(), aRect ); + SwViewShell::mbLstAct = 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? + // ?????????????????????? + long lMaDelta = aPixSz.Height(); + if ( std::abs(lYDiff) > ( maVisArea.Height() / 3 ) ) + lMaDelta *= 6; + else + lMaDelta *= 2; + + lMaDelta *= lMult; + + if ( lYDiff < 0 ) + lMaDelta = -lMaDelta; + + long lDiff = lYDiff; + while ( lDiff ) + { + 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() ); + + 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(vcl::RenderContext& rRenderContext, const SwRect &rRect) +{ + if ( !GetWin() && !GetOut()->GetConnectMetaFile() ) + return; //for the printer we don't do anything here. + + //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().IsOver( 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 ) + { + 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.IsOver( 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( PushFlags::FILLCOLOR|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()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea()) + Imp()->DelRegion(); + + 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()); + --mnStartAction; + + SwRegionRects *pRegion = Imp()->GetRegion(); + 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.IsOver( VisArea() ); + if ( !bStop ) + break; + } + if ( bStop ) + { + Imp()->DelRegion(); + pRegion = nullptr; + } + } + + if ( pRegion ) + { + //First Invert then Compress, never the other way round! + pRegion->Invert(); + pRegion->Compress(); + bRet = false; + if ( !pRegion->empty() ) + { + SwRegionRects aRegion( rRect ); + for ( size_t i = 0; i < pRegion->size(); ++i ) + { const SwRect &rTmp = (*pRegion)[i]; + if ( !rRect.IsInside( rTmp ) ) + { + InvalidateWindows( rTmp ); + if ( rTmp.IsOver( 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; + Imp()->DelRegion(); + } + 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()) + { + SdrView* pDrawView(pShell->Imp()->GetDrawView()); + + if (nullptr != pDrawView) + { + SdrPageView* pSdrPageView(pDrawView->GetSdrPageView()); + + if (nullptr != pSdrPageView) + { + m_pPatchedPageWindow = pSdrPageView->FindPageWindow(*pShell->GetWin()); + + 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.IsInside( 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; + SET_CURR_SHELL( 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.IsInside( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::mbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::mbLstAct = 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.IsInside( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::mbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::mbLstAct = 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(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() ) + { + // #i68597# + const vcl::Region aDLRegion(rRect); + DLPrePaint2(aDLRegion); + + rRenderContext.Push( PushFlags::FILLCOLOR|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, long tileWidth, 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. + Fraction scaleX = Fraction(contextWidth, 96) * Fraction(1440) / Fraction(tileWidth); + Fraction scaleY = Fraction(contextHeight, 96) * Fraction(1440) / Fraction(tileHeight); + 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(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; + + SET_CURR_SHELL( 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(); + 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(); +} + +void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt ) +{ + if (*mpOpt == rOpt) + return; + + vcl::Window *pMyWin = GetWin(); + if( !pMyWin ) + { + OSL_ENSURE( pMyWin, "SwViewShell::ApplyViewOptions: no window" ); + return; + } + + SET_CURR_SHELL( 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() ) + { + SwMsgPoolItem aHint( RES_HIDDENPARA_PRINT ); + pFieldType->ModifyNotification( &aHint, nullptr); + } + bReformat = true; + } + if ( !bReformat && mpOpt->IsShowHiddenChar() != rOpt.IsShowHiddenChar() ) + { + bReformat = GetDoc()->ContainsHiddenChars(); + } + + // 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 ... + bReformat = bReformat || mpOpt->IsFieldName() != rOpt.IsFieldName(); + + // 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( bOnlineSpellChgd ) + { + 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() ) + { + // 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( Imp()->IsAccessible() ) + Imp()->InvalidateAccessibleEditableState( false ); + } +} + +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(); +} + +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() && !rAccessibilityOptions.GetIsForPagePreviews()) + { + 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()); + } +} + +ShellResource* SwViewShell::GetShellRes() +{ + return mpShellRes; +} + +void SwViewShell::SetCareDialog(const std::shared_ptr<weld::Window>& rNew) +{ + (*mpCareDialog.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; +} + +// #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..9c55d1265 --- /dev/null +++ b/sw/source/core/view/vnew.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 <sfx2/printer.hxx> +#include <sal/log.hxx> +#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() ); + GetWin()->SetFillColor(); + GetWin()->SetBackground(); + GetWin()->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, + long nFlags ) + : + maBrowseBorder(), + mpSfxViewShell( nullptr ), + mpImp( new SwViewShellImp( this ) ), + mpWin( pWindow ), + mpOut( pOutput ? pOutput + : pWindow ? static_cast<OutputDevice*>(pWindow) + : 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), + maPrePostMapMode() +{ + // 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(); + } + + SET_CURR_SHELL( 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, long const nFlags) + : Ring( &rShell ) , + maBrowseBorder( rShell.maBrowseBorder ), + mpSfxViewShell( nullptr ), + mpImp( new SwViewShellImp( this ) ), + mpWin( pWindow ), + mpOut( pOutput ? pOutput + : pWindow ? static_cast<OutputDevice*>(pWindow) + : 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), + maPrePostMapMode() +{ + // 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; + + SET_CURR_SHELL( 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; + + { + SET_CURR_SHELL( 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..5dbf21b71 --- /dev/null +++ b/sw/source/core/view/vprint.cxx @@ -0,0 +1,688 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <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; + if (nullptr != (pPt = s_pPaintQueue)) + { + 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) + { + SwQueuedPaint *pPt = s_pPaintQueue; + do + { SwViewShell *pSh = pPt->pSh; + SET_CURR_SHELL( 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; + if (nullptr != (pPt = s_pPaintQueue)) + { + 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" ); + SET_CURR_SHELL( 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" ); + SET_CURR_SHELL( 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 ) +{ + SET_CURR_SHELL( 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 *pPrtDoc, const SfxPrinter* pPrt) +{ + OSL_ENSURE( dynamic_cast<const SwFEShell*>( this) != nullptr,"SwViewShell::Prt for FEShell only"); + SwFEShell* pFESh = static_cast<SwFEShell*>(this); + pPrtDoc->getIDocumentFieldsAccess().LockExpFields(); + + // use given printer + //! Make a copy of it since it gets destroyed with the temporary document + //! used for PDF export + if (pPrt) + pPrtDoc->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 ) + pPrtDoc->GetAttrPool().SetPoolDefaultItem( *pCpyItem ); + } + + // JP 29.07.99 - Bug 67951 - set all Styles from the SourceDoc into + // the PrintDoc - will be replaced! + pPrtDoc->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 ? pPrtDoc->FindPageDesc( + pPage->GetPageDesc()->GetName() ) : &pPrtDoc->GetPageDesc( 0 ); + + if( !pFESh->IsTableMode() && pActCursor && pActCursor->HasMark() ) + { // Tweak paragraph attributes of last paragraph + SwNodeIndex aNodeIdx( *pPrtDoc->GetNodes().GetEndOfContent().StartOfSectionNode() ); + SwTextNode* pTextNd = pPrtDoc->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( pPrtDoc ); + + // set the page style at the first paragraph + { + SwNodeIndex aNodeIdx( *pPrtDoc->GetNodes().GetEndOfContent().StartOfSectionNode() ); + SwContentNode* pCNd = pPrtDoc->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 + + SET_CURR_SHELL( 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; + long nOrigHeight = pStPage->getFrameArea().Height(); + long nNewHeight = nOrigHeight*fScale; + 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)); + + { + SET_CURR_SHELL( 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( 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() ) + { + 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..fc8e35c99 --- /dev/null +++ b/sw/source/core/view/vprint.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_CORE_VIEW_VPRINT_HXX +#define INCLUDED_SW_SOURCE_CORE_VIEW_VPRINT_HXX + +#include <sal/types.h> + +class SwRootFrame; +class SwPageFrame; + +SwPageFrame const* sw_getPage(SwRootFrame const& rLayout, sal_Int32 const nPage); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/sw/source/filter/ascii/ascatr.cxx b/sw/source/filter/ascii/ascatr.cxx new file mode 100644 index 000000000..3445d7fcd --- /dev/null +++ b/sw/source/filter/ascii/ascatr.cxx @@ -0,0 +1,391 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/stream.hxx> +#include <comphelper/string.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <redline.hxx> +#include "wrtasc.hxx" +#include <txatbase.hxx> +#include <txtfld.hxx> +#include <fmtftn.hxx> +#include <fmtfld.hxx> +#include <fldbas.hxx> +#include <ftninfo.hxx> +#include <numrule.hxx> + +#include <algorithm> + +/* + * This file contains all output functions of the ASCII-Writer; + * For all nodes, attributes, formats and chars. + */ + +namespace { + +class SwASC_AttrIter +{ + SwASCWriter& rWrt; + const SwTextNode& rNd; + sal_Int32 nCurrentSwPos; + + sal_Int32 SearchNext( sal_Int32 nStartPos ); + +public: + SwASC_AttrIter( SwASCWriter& rWrt, const SwTextNode& rNd, sal_Int32 nStt ); + + void NextPos() + { + nCurrentSwPos = SearchNext( nCurrentSwPos + 1 ); + } + + sal_Int32 WhereNext() const + { + return nCurrentSwPos; + } + + bool OutAttr( sal_Int32 nSwPos ); +}; + +} + +SwASC_AttrIter::SwASC_AttrIter( + SwASCWriter& rWr, + const SwTextNode& rTextNd, + sal_Int32 nStt ) + : rWrt( rWr ) + , rNd( rTextNd ) + , nCurrentSwPos( 0 ) +{ + nCurrentSwPos = SearchNext( nStt + 1 ); +} + +sal_Int32 SwASC_AttrIter::SearchNext( sal_Int32 nStartPos ) +{ + sal_Int32 nMinPos = SAL_MAX_INT32; + const SwpHints* pTextAttrs = rNd.GetpSwpHints(); + if( pTextAttrs ) + { + // TODO: This can be optimized, if we make use of the fact that the TextAttrs + // are sorted by starting position. We would need to remember two indices, however. + for ( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if ( pHt->HasDummyChar() ) + { + sal_Int32 nPos = pHt->GetStart(); + + if( nPos >= nStartPos && nPos <= nMinPos ) + nMinPos = nPos; + + if( ( ++nPos ) >= nStartPos && nPos < nMinPos ) + nMinPos = nPos; + } + else if ( pHt->HasContent() ) + { + const sal_Int32 nHintStart = pHt->GetStart(); + if ( nHintStart >= nStartPos && nHintStart <= nMinPos ) + { + nMinPos = nHintStart; + } + + const sal_Int32 nHintEnd = pHt->End() ? *pHt->End() : COMPLETE_STRING; + if ( nHintEnd >= nStartPos && nHintEnd < nMinPos ) + { + nMinPos = nHintEnd; + } + } + } + } + return nMinPos; +} + +bool SwASC_AttrIter::OutAttr( sal_Int32 nSwPos ) +{ + bool bRet = false; + const SwpHints* pTextAttrs = rNd.GetpSwpHints(); + if( pTextAttrs ) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if ( ( pHt->HasDummyChar() + || pHt->HasContent() ) + && nSwPos == pHt->GetStart() ) + { + bRet = true; + OUString sOut; + switch( pHt->Which() ) + { + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + sOut = static_txtattr_cast<SwTextField const*>(pHt) + ->GetFormatField().GetField()->ExpandField(true, nullptr); + break; + + case RES_TXTATR_FTN: + { + const SwFormatFootnote& rFootnote = pHt->GetFootnote(); + if( !rFootnote.GetNumStr().isEmpty() ) + sOut = rFootnote.GetNumStr(); + else if( rFootnote.IsEndNote() ) + sOut = rWrt.m_pDoc->GetEndNoteInfo().m_aFormat. + GetNumStr( rFootnote.GetNumber() ); + else + sOut = rWrt.m_pDoc->GetFootnoteInfo().m_aFormat. + GetNumStr( rFootnote.GetNumber() ); + } + break; + } + if( !sOut.isEmpty() ) + rWrt.Strm().WriteUnicodeOrByteText( sOut ); + } + else if( nSwPos < pHt->GetStart() ) + break; + } + } + return bRet; +} + +namespace { + +class SwASC_RedlineIter +{ +private: + SwTextNode const& m_rNode; + IDocumentRedlineAccess const& m_rIDRA; + SwRedlineTable::size_type m_nextRedline; + +public: + SwASC_RedlineIter(SwASCWriter const& rWriter, SwTextNode const& rNode) + : m_rNode(rNode) + , m_rIDRA(rNode.GetDoc()->getIDocumentRedlineAccess()) + , m_nextRedline(rWriter.m_bHideDeleteRedlines + ? m_rIDRA.GetRedlinePos(m_rNode, RedlineType::Delete) + : SwRedlineTable::npos) + { + } + + bool CheckNodeDeleted() + { + if (m_nextRedline == SwRedlineTable::npos) + { + return false; + } + SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]); + return pRedline->Start()->nNode.GetIndex() < m_rNode.GetIndex() + && m_rNode.GetIndex() < pRedline->End()->nNode.GetIndex(); + } + + std::pair<sal_Int32, sal_Int32> GetNextRedlineSkip() + { + sal_Int32 nRedlineStart(COMPLETE_STRING); + sal_Int32 nRedlineEnd(COMPLETE_STRING); + for ( ; m_nextRedline < m_rIDRA.GetRedlineTable().size(); ++m_nextRedline) + { + SwRangeRedline const*const pRedline(m_rIDRA.GetRedlineTable()[m_nextRedline]); + if (pRedline->GetType() != RedlineType::Delete) + { + continue; + } + SwPosition const*const pStart(pRedline->Start()); + SwPosition const*const pEnd(pRedline->End()); + if (m_rNode.GetIndex() < pStart->nNode.GetIndex()) + { + m_nextRedline = SwRedlineTable::npos; + break; // done + } + if (nRedlineStart == COMPLETE_STRING) + { + nRedlineStart = pStart->nNode.GetIndex() == m_rNode.GetIndex() + ? pStart->nContent.GetIndex() + : 0; + } + else + { + if (pStart->nContent.GetIndex() != nRedlineEnd) + { + assert(nRedlineEnd < pStart->nContent.GetIndex()); + break; // no increment, revisit it next call + } + } + nRedlineEnd = pEnd->nNode.GetIndex() == m_rNode.GetIndex() + ? pEnd->nContent.GetIndex() + : COMPLETE_STRING; + } + return std::make_pair(nRedlineStart, nRedlineEnd); + } +}; + +} + +// Output of the node + +static Writer& OutASC_SwTextNode( Writer& rWrt, SwContentNode& rNode ) +{ + const SwTextNode& rNd = static_cast<SwTextNode&>(rNode); + + sal_Int32 nStrPos = rWrt.m_pCurrentPam->GetPoint()->nContent.GetIndex(); + const sal_Int32 nNodeEnd = rNd.Len(); + sal_Int32 nEnd = nNodeEnd; + bool bLastNd = rWrt.m_pCurrentPam->GetPoint()->nNode == rWrt.m_pCurrentPam->GetMark()->nNode; + if( bLastNd ) + nEnd = rWrt.m_pCurrentPam->GetMark()->nContent.GetIndex(); + + bool bIsOneParagraph = rWrt.m_pOrigPam->Start()->nNode == rWrt.m_pOrigPam->End()->nNode; + + SwASC_AttrIter aAttrIter( static_cast<SwASCWriter&>(rWrt), rNd, nStrPos ); + SwASC_RedlineIter redlineIter(static_cast<SwASCWriter&>(rWrt), rNd); + + if (redlineIter.CheckNodeDeleted()) + { + return rWrt; + } + + const SwNumRule* pNumRule = rNd.GetNumRule(); + if (pNumRule && !nStrPos && rWrt.m_bExportPargraphNumbering && !bIsOneParagraph) + { + bool bIsOutlineNumRule = pNumRule == rNd.GetDoc()->GetOutlineNumRule(); + + // indent each numbering level by 4 spaces + OUString level; + if (!bIsOutlineNumRule) + { + for (int i = 0; i <= rNd.GetActualListLevel(); ++i) + level += " "; + } + + // set up bullets or numbering + OUString numString(rNd.GetNumString()); + if (numString.isEmpty() && !bIsOutlineNumRule) + { + if (rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet()) + numString = " "; + else if (rNd.HasBullet()) + numString = OUString(numfunc::GetBulletChar(rNd.GetActualListLevel())); + else if (!rNd.HasBullet() && !rNd.HasVisibleNumberingOrBullet()) + numString = " "; + } + + if (!level.isEmpty() || !numString.isEmpty()) + rWrt.Strm().WriteUnicodeOrByteText(level + numString + " "); + } + + OUString aStr( rNd.GetText() ); + if( rWrt.m_bASCII_ParaAsBlank ) + aStr = aStr.replace(0x0A, ' '); + + const bool bExportSoftHyphens = RTL_TEXTENCODING_UCS2 == rWrt.GetAsciiOptions().GetCharSet() || + RTL_TEXTENCODING_UTF8 == rWrt.GetAsciiOptions().GetCharSet(); + + std::pair<sal_Int32, sal_Int32> curRedline(redlineIter.GetNextRedlineSkip()); + for (;;) { + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + + bool isOutAttr(false); + if (nStrPos < curRedline.first || curRedline.second <= nStrPos) + { + isOutAttr = aAttrIter.OutAttr(nStrPos); + } + + if (!isOutAttr) + { + OUStringBuffer buf; + while (true) + { + if (nNextAttr <= curRedline.first) + { + buf.append(aStr.copy(nStrPos, nNextAttr - nStrPos)); + break; + } + else if (nStrPos < curRedline.second) + { + if (nStrPos < curRedline.first) + { + buf.append(aStr.copy(nStrPos, curRedline.first - nStrPos)); + } + if (curRedline.second <= nNextAttr) + { + nStrPos = curRedline.second; + curRedline = redlineIter.GetNextRedlineSkip(); + } + else + { + nStrPos = nNextAttr; + break; + } + } + else + { + curRedline = redlineIter.GetNextRedlineSkip(); + } + } + OUString aOutStr(buf.makeStringAndClear()); + if ( !bExportSoftHyphens ) + aOutStr = aOutStr.replaceAll(OUStringChar(CHAR_SOFTHYPHEN), ""); + + // all INWORD/BREAKWORD should be already removed by OutAttr + // but the field-marks are not attributes so filter those + static sal_Unicode const forbidden [] = { + CH_TXT_ATR_INPUTFIELDSTART, + CH_TXT_ATR_INPUTFIELDEND, + CH_TXT_ATR_FORMELEMENT, + CH_TXT_ATR_FIELDSTART, + CH_TXT_ATR_FIELDSEP, + CH_TXT_ATR_FIELDEND, + 0 + }; + aOutStr = comphelper::string::removeAny(aOutStr, forbidden); + + rWrt.Strm().WriteUnicodeOrByteText( aOutStr ); + } + nStrPos = nNextAttr; + if (nStrPos >= nEnd) + { + break; + } + aAttrIter.NextPos(); + } + + if( !bLastNd || + ( ( !rWrt.m_bWriteClipboardDoc && !rWrt.m_bASCII_NoLastLineEnd ) + && !nStrPos && nEnd == nNodeEnd ) ) + rWrt.Strm().WriteUnicodeOrByteText( static_cast<SwASCWriter&>(rWrt).GetLineEnd()); + + return rWrt; +} + +/* + * Create the table for the ASCII function pointers to the output + * function. + * There are local structures that only need to be known to the ASCII DLL. + */ + +SwNodeFnTab aASCNodeFnTab = { +/* RES_TXTNODE */ OutASC_SwTextNode, +/* RES_GRFNODE */ nullptr, +/* RES_OLENODE */ nullptr +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/parasc.cxx b/sw/source/filter/ascii/parasc.cxx new file mode 100644 index 000000000..162459ff1 --- /dev/null +++ b/sw/source/filter/ascii/parasc.cxx @@ -0,0 +1,503 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <tools/stream.hxx> +#include <hintids.hxx> +#include <sfx2/printer.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <svl/languageoptions.hxx> +#include <shellio.hxx> +#include <doc.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pam.hxx> +#include <breakit.hxx> +#include <swerror.h> +#include <strings.hrc> +#include <mdiexp.hxx> +#include <poolfmt.hxx> +#include <iodetect.hxx> + +#include <vcl/metric.hxx> +#include <osl/diagnose.h> + +#define ASC_BUFFLEN 4096 + +namespace { + +class SwASCIIParser +{ + SwDoc* pDoc; + std::unique_ptr<SwPaM> pPam; + SvStream& rInput; + std::unique_ptr<char[]> pArr; + const SwAsciiOptions& rOpt; + std::unique_ptr<SfxItemSet> pItemSet; + long nFileSize; + SvtScriptType nScript; + bool bNewDoc; + + ErrCode ReadChars(); + void InsertText( const OUString& rStr ); + + SwASCIIParser(const SwASCIIParser&) = delete; + SwASCIIParser& operator=(const SwASCIIParser&) = delete; + +public: + SwASCIIParser( SwDoc* pD, const SwPaM& rCursor, SvStream& rIn, + bool bReadNewDoc, const SwAsciiOptions& rOpts ); + + ErrCode CallParser(); +}; + +} + +// Call for the general reader interface +ErrCode AsciiReader::Read( SwDoc &rDoc, const OUString&, SwPaM &rPam, const OUString & ) +{ + if( !m_pStream ) + { + OSL_ENSURE( false, "ASCII read without a stream" ); + return ERR_SWG_READ_ERROR; + } + + std::unique_ptr<SwASCIIParser> pParser(new SwASCIIParser( &rDoc, rPam, *m_pStream, + !m_bInsertMode, m_aOption.GetASCIIOpts() )); + ErrCode nRet = pParser->CallParser(); + + pParser.reset(); + // after Read reset the options + m_aOption.ResetASCIIOpts(); + return nRet; +} + +SwASCIIParser::SwASCIIParser(SwDoc* pD, const SwPaM& rCursor, SvStream& rIn, + bool bReadNewDoc, const SwAsciiOptions& rOpts) + : pDoc(pD), rInput(rIn), rOpt(rOpts), nFileSize(0), nScript(SvtScriptType::NONE) + , bNewDoc(bReadNewDoc) +{ + pPam.reset( new SwPaM( *rCursor.GetPoint() ) ); + pArr.reset( new char [ ASC_BUFFLEN + 2 ] ); + + pItemSet = std::make_unique<SfxItemSet>( pDoc->GetAttrPool(), + svl::Items<RES_CHRATR_FONT, RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_LANGUAGE>{} ); + + // set defaults from the options + if( rOpt.GetLanguage() ) + { + SvxLanguageItem aLang( rOpt.GetLanguage(), RES_CHRATR_LANGUAGE ); + pItemSet->Put( aLang ); + aLang.SetWhich(RES_CHRATR_CJK_LANGUAGE); + pItemSet->Put( aLang ); + aLang.SetWhich(RES_CHRATR_CTL_LANGUAGE); + pItemSet->Put( aLang ); + } + if( !rOpt.GetFontName().isEmpty() ) + { + vcl::Font aTextFont( rOpt.GetFontName(), Size( 0, 10 ) ); + if( pDoc->getIDocumentDeviceAccess().getPrinter( false ) ) + aTextFont = pDoc->getIDocumentDeviceAccess().getPrinter( false )->GetFontMetric( aTextFont ); + SvxFontItem aFont( aTextFont.GetFamilyType(), aTextFont.GetFamilyName(), + OUString(), aTextFont.GetPitch(), aTextFont.GetCharSet(), RES_CHRATR_FONT ); + pItemSet->Put( aFont ); + aFont.SetWhich(RES_CHRATR_CJK_FONT); + pItemSet->Put( aFont ); + aFont.SetWhich(RES_CHRATR_CTL_FONT); + pItemSet->Put( aFont ); + } +} + +// Calling the parser +ErrCode SwASCIIParser::CallParser() +{ + rInput.ResetError(); + nFileSize = rInput.TellEnd(); + rInput.Seek(STREAM_SEEK_TO_BEGIN); + rInput.ResetError(); + + ::StartProgress( STR_STATSTR_W4WREAD, 0, nFileSize, pDoc->GetDocShell() ); + + std::unique_ptr<SwPaM> pInsPam; + sal_Int32 nSttContent = 0; + if (!bNewDoc) + { + const SwNodeIndex& rTmp = pPam->GetPoint()->nNode; + pInsPam.reset(new SwPaM( rTmp, rTmp, 0, -1 )); + nSttContent = pPam->GetPoint()->nContent.GetIndex(); + } + + SwTextFormatColl *pColl = nullptr; + + if (bNewDoc) + { + pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_PRE, false); + if (!pColl) + pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,false); + if (pColl) + pDoc->SetTextFormatColl(*pPam, pColl); + } + + ErrCode nError = ReadChars(); + + if( pItemSet ) + { + // set only the attribute, for scanned scripts. + if( !( SvtScriptType::LATIN & nScript )) + { + pItemSet->ClearItem( RES_CHRATR_FONT ); + pItemSet->ClearItem( RES_CHRATR_LANGUAGE ); + } + if( !( SvtScriptType::ASIAN & nScript )) + { + pItemSet->ClearItem( RES_CHRATR_CJK_FONT ); + pItemSet->ClearItem( RES_CHRATR_CJK_LANGUAGE ); + } + if( !( SvtScriptType::COMPLEX & nScript )) + { + pItemSet->ClearItem( RES_CHRATR_CTL_FONT ); + pItemSet->ClearItem( RES_CHRATR_CTL_LANGUAGE ); + } + if( pItemSet->Count() ) + { + if( bNewDoc ) + { + if (pColl) + { + // Using the pool defaults for the font causes significant + // trouble for the HTML filter, because it is not able + // to export the pool defaults (or to be more precise: + // the HTML filter is not able to detect whether a pool + // default has changed or not. Even a comparison with the + // HTML template does not work, because the defaults are + // not copied when a new doc is created. The result of + // comparing pool defaults therefore would be that the + // defaults are exported always if the have changed for + // text documents in general. That's not sensible, as well + // as it is not sensible to export them always. + sal_uInt16 aWhichIds[4] = + { + RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT, 0 + }; + sal_uInt16 *pWhichIds = aWhichIds; + while (*pWhichIds) + { + const SfxPoolItem *pItem; + if (SfxItemState::SET == pItemSet->GetItemState(*pWhichIds, + false, &pItem)) + { + pColl->SetFormatAttr( *pItem ); + pItemSet->ClearItem( *pWhichIds ); + } + ++pWhichIds; + } + } + if (pItemSet->Count()) + pDoc->SetDefault(*pItemSet); + } + else if( pInsPam ) + { + // then set over the insert range the defined attributes + *pInsPam->GetMark() = *pPam->GetPoint(); + ++pInsPam->GetPoint()->nNode; + pInsPam->GetPoint()->nContent.Assign( + pInsPam->GetContentNode(), nSttContent ); + + // !!!!! + OSL_ENSURE( false, "Have to change - hard attr. to para. style" ); + pDoc->getIDocumentContentOperations().InsertItemSet( *pInsPam, *pItemSet ); + } + } + pItemSet.reset(); + } + + pInsPam.reset(); + + ::EndProgress( pDoc->GetDocShell() ); + return nError; +} + +ErrCode SwASCIIParser::ReadChars() +{ + sal_Unicode *pStt = nullptr, *pEnd = nullptr, *pLastStt = nullptr; + long nReadCnt = 0, nLineLen = 0; + sal_Unicode cLastCR = 0; + bool bSwapUnicode = false; + + const SwAsciiOptions *pUseMe=&rOpt; + SwAsciiOptions aEmpty; + if (nFileSize >= 2 && + aEmpty.GetFontName() == rOpt.GetFontName() && + aEmpty.GetCharSet() == rOpt.GetCharSet() && + aEmpty.GetLanguage() == rOpt.GetLanguage() && + aEmpty.GetParaFlags() == rOpt.GetParaFlags()) + { + sal_uLong nLen, nOrig; + nOrig = nLen = rInput.ReadBytes(pArr.get(), ASC_BUFFLEN); + rtl_TextEncoding eCharSet; + LineEnd eLineEnd; + const bool bRet + = SwIoSystem::IsDetectableText(pArr.get(), nLen, &eCharSet, &bSwapUnicode, &eLineEnd); + if (!bRet) + return ERRCODE_IO_BROKENPACKAGE; + + OSL_ENSURE(bRet, "Autodetect of text import without nag dialog must have failed"); + if (bRet && eCharSet != RTL_TEXTENCODING_DONTKNOW) + { + aEmpty.SetCharSet(eCharSet); + aEmpty.SetParaFlags(eLineEnd); + rInput.SeekRel(-(long(nLen))); + } + else + rInput.SeekRel(-(long(nOrig))); + pUseMe=&aEmpty; + } + + rtl_TextToUnicodeConverter hConverter=nullptr; + rtl_TextToUnicodeContext hContext=nullptr; + rtl_TextEncoding currentCharSet = pUseMe->GetCharSet(); + if (RTL_TEXTENCODING_UCS2 != currentCharSet) + { + if( currentCharSet == RTL_TEXTENCODING_DONTKNOW ) + currentCharSet = RTL_TEXTENCODING_ASCII_US; + hConverter = rtl_createTextToUnicodeConverter( currentCharSet ); + OSL_ENSURE( hConverter, "no string convert available" ); + if (!hConverter) + return ErrCode(ErrCodeArea::Sw, ErrCodeClass::Read, 0); + bSwapUnicode = false; + hContext = rtl_createTextToUnicodeContext( hConverter ); + } + else if (pUseMe != &aEmpty) //Already successfully figured out type + { + rInput.StartReadingUnicodeText( currentCharSet ); + bSwapUnicode = rInput.IsEndianSwap(); + } + + std::unique_ptr<sal_Unicode[]> aWork; + sal_uLong nArrOffset = 0; + + do { + if( pStt >= pEnd ) + { + if( pLastStt != pStt ) + InsertText( OUString( pLastStt )); + + // Read a new block + sal_uLong lGCount; + if( ERRCODE_NONE != rInput.GetError() || 0 == (lGCount = + rInput.ReadBytes( pArr.get() + nArrOffset, + ASC_BUFFLEN - nArrOffset ))) + break; // break from the while loop + + /* + If there was some unconverted bytes on the last cycle then they + were put at the beginning of the array, so total bytes available + to convert this cycle includes them. If we found 0 following bytes + then we ignore the previous partial character. + */ + lGCount+=nArrOffset; + + if( hConverter ) + { + sal_uInt32 nInfo; + sal_Size nNewLen = lGCount, nCntBytes; + aWork.reset(new sal_Unicode[nNewLen + 1]); // add 1 for '\0' + sal_Unicode* pBuf = aWork.get(); + pBuf[nNewLen] = 0; // ensure '\0' + + nNewLen = rtl_convertTextToUnicode( hConverter, hContext, + pArr.get(), lGCount, pBuf, nNewLen, + ( + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE + ), + &nInfo, + &nCntBytes ); + if( 0 != ( nArrOffset = lGCount - nCntBytes ) ) + memmove( pArr.get(), pArr.get() + nCntBytes, nArrOffset ); + + pStt = pLastStt = aWork.get(); + pEnd = pStt + nNewLen; + } + else + { + pStt = pLastStt = reinterpret_cast<sal_Unicode*>(pArr.get()); + auto nChars = lGCount / 2; + pEnd = pStt + nChars; + + if( bSwapUnicode ) + { + char* pF = pArr.get(), *pN = pArr.get() + 1; + for( sal_uLong n = 0; n < nChars; ++n, pF += 2, pN += 2 ) + { + char c = *pF; + *pF = *pN; + *pN = c; + } + } + } + + *pEnd = 0; + nReadCnt += lGCount; + + ::SetProgressState( nReadCnt, pDoc->GetDocShell() ); + + if( cLastCR ) + { + if( 0x0a == *pStt && 0x0d == cLastCR ) + pLastStt = ++pStt; + cLastCR = 0; + nLineLen = 0; + // We skip the last one at the end + if( !rInput.eof() || !(pEnd == pStt || + ( !*pEnd && pEnd == pStt+1 ) ) ) + pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false ); + } + } + + bool bIns = true, bSplitNode = false; + switch( *pStt ) + { + + case 0x0a: if( LINEEND_LF == pUseMe->GetParaFlags() ) + { + bIns = false; + *pStt = 0; + ++pStt; + + // We skip the last one at the end + if( !rInput.eof() || pEnd != pStt ) + bSplitNode = true; + } + break; + + case 0x0d: if( LINEEND_LF != pUseMe->GetParaFlags() ) + { + bIns = false; + *pStt = 0; + ++pStt; + + bool bChkSplit = true; + if( LINEEND_CRLF == pUseMe->GetParaFlags() ) + { + if( pStt == pEnd ) + { + cLastCR = 0x0d; + bChkSplit = false; + } + else if( 0x0a == *pStt ) + ++pStt; + } + + // We skip the last one at the end + if( bChkSplit && ( !rInput.eof() || pEnd != pStt )) + bSplitNode = true; + } + break; + + case 0x0c: + { + // Insert a hard page break + *pStt++ = 0; + if( nLineLen ) + { + InsertText( OUString( pLastStt )); + } + pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false ); + pDoc->getIDocumentContentOperations().InsertPoolItem( + *pPam, SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) ); + pLastStt = pStt; + nLineLen = 0; + bIns = false; + } + break; + + case 0x1a: + if( nReadCnt == nFileSize && pStt+1 == pEnd ) + *pStt = 0; + else + *pStt = '#'; // Replacement visualisation + break; + + case '\t': break; + + default: + if( ' ' > *pStt ) + // Found control char, replace with '#' + *pStt = '#'; + break; + } + + if( bIns ) + { + if( ( nLineLen >= MAX_ASCII_PARA - 100 ) && + ( ( *pStt == ' ' ) || ( nLineLen >= MAX_ASCII_PARA - 1 ) ) ) + { + sal_Unicode c = *pStt; + *pStt = 0; + InsertText( OUString( pLastStt )); + pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false ); + pLastStt = pStt; + nLineLen = 0; + *pStt = c; + } + ++pStt; + ++nLineLen; + } + else if( bSplitNode ) + { + // We found a CR/LF, thus save the text + InsertText( OUString( pLastStt )); + if(bNewDoc) + pDoc->getIDocumentContentOperations().AppendTextNode( *pPam->GetPoint() ); + else + pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false ); + pLastStt = pStt; + nLineLen = 0; + } + } while(true); + + if( hConverter ) + { + rtl_destroyTextToUnicodeContext( hConverter, hContext ); + rtl_destroyTextToUnicodeConverter( hConverter ); + } + return ERRCODE_NONE; +} + +void SwASCIIParser::InsertText( const OUString& rStr ) +{ + pDoc->getIDocumentContentOperations().InsertString( *pPam, rStr ); + + if( pItemSet && g_pBreakIt && nScript != ( SvtScriptType::LATIN | + SvtScriptType::ASIAN | + SvtScriptType::COMPLEX ) ) + nScript |= g_pBreakIt->GetAllScriptsOfText( rStr ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/wrtasc.cxx b/sw/source/filter/ascii/wrtasc.cxx new file mode 100644 index 000000000..fa99ce7c6 --- /dev/null +++ b/sw/source/filter/ascii/wrtasc.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 <osl/endian.h> +#include <tools/stream.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <mdiexp.hxx> +#include <docary.hxx> +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> +#include "wrtasc.hxx" +#include <frameformats.hxx> + +#include <strings.hrc> + +SwASCWriter::SwASCWriter( const OUString& rFltNm ) +{ + SwAsciiOptions aNewOpts; + + switch( 5 <= rFltNm.getLength() ? rFltNm[4] : 0 ) + { + case 'D': + aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 ); + aNewOpts.SetParaFlags( LINEEND_CRLF ); + if( 5 < rFltNm.getLength() ) + switch( rFltNm.copy( 5 ).toInt32() ) + { + case 437: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_437 ); break; + case 850: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_850 ); break; + case 860: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_860 ); break; + case 861: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_861 ); break; + case 863: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_863 ); break; + case 865: aNewOpts.SetCharSet( RTL_TEXTENCODING_IBM_865 ); break; + } + break; + + case 'A': +#ifndef _WIN32 + aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 ); + aNewOpts.SetParaFlags( LINEEND_CRLF ); +#endif + break; + + case 'M': + aNewOpts.SetCharSet( RTL_TEXTENCODING_APPLE_ROMAN ); + aNewOpts.SetParaFlags( LINEEND_CR ); + break; + + case 'X': +#ifdef _WIN32 + aNewOpts.SetCharSet( RTL_TEXTENCODING_MS_1252 ); + aNewOpts.SetParaFlags( LINEEND_LF ); +#endif + break; + + default: + if( rFltNm.getLength() >= 4 && rFltNm.copy( 4 )=="_DLG" ) + { + // use the options + aNewOpts = GetAsciiOptions(); + } + } + SetAsciiOptions( aNewOpts ); +} + +SwASCWriter::~SwASCWriter() {} + +ErrCode SwASCWriter::WriteStream() +{ + bool bIncludeBOM = GetAsciiOptions().GetIncludeBOM(); + + if( m_bASCII_ParaAsCR ) // If predefined + m_sLineEnd = "\015"; + else if( m_bASCII_ParaAsBlank ) + m_sLineEnd = " "; + else + switch( GetAsciiOptions().GetParaFlags() ) + { + case LINEEND_CR: m_sLineEnd = "\015"; break; + case LINEEND_LF: m_sLineEnd = "\012"; break; + case LINEEND_CRLF: m_sLineEnd = "\015\012"; break; + } + + long nMaxNode = m_pDoc->GetNodes().Count(); + + if( m_bShowProgress ) + ::StartProgress( STR_STATSTR_W4WWRITE, 0, nMaxNode, m_pDoc->GetDocShell() ); + + SwPaM* pPam = m_pOrigPam; + + bool bWriteSttTag = m_bUCS2_WithStartChar && + (RTL_TEXTENCODING_UCS2 == GetAsciiOptions().GetCharSet() || + RTL_TEXTENCODING_UTF8 == GetAsciiOptions().GetCharSet()); + + rtl_TextEncoding eOld = Strm().GetStreamCharSet(); + Strm().SetStreamCharSet( GetAsciiOptions().GetCharSet() ); + + // Output all areas of the pam into the ASC file + do { + bool bTstFly = true; + while( m_pCurrentPam->GetPoint()->nNode.GetIndex() < m_pCurrentPam->GetMark()->nNode.GetIndex() || + (m_pCurrentPam->GetPoint()->nNode.GetIndex() == m_pCurrentPam->GetMark()->nNode.GetIndex() && + m_pCurrentPam->GetPoint()->nContent.GetIndex() <= m_pCurrentPam->GetMark()->nContent.GetIndex()) ) + { + SwTextNode* pNd = m_pCurrentPam->GetPoint()->nNode.GetNode().GetTextNode(); + if( pNd ) + { + // Should we have frames only? + // That's possible, if we put a frame selection into the clipboard + if( bTstFly && m_bWriteAll && + pNd->GetText().isEmpty() && + // Frame exists + !m_pDoc->GetSpzFrameFormats()->empty() && + // Only one node in the array + m_pDoc->GetNodes().GetEndOfExtras().GetIndex() + 3 == + m_pDoc->GetNodes().GetEndOfContent().GetIndex() && + // And exactly this one is selected + m_pDoc->GetNodes().GetEndOfContent().GetIndex() - 1 == + m_pCurrentPam->GetPoint()->nNode.GetIndex() ) + { + // Print the frame's content. + // It is always at position 0! + const SwFrameFormat* pFormat = (*m_pDoc->GetSpzFrameFormats())[ 0 ]; + const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx(); + if( pIdx ) + { + m_pCurrentPam = NewUnoCursor(*m_pDoc, pIdx->GetIndex(), + pIdx->GetNode().EndOfSectionIndex() ); + m_pCurrentPam->Exchange(); + continue; // reset while loop! + } + } + else + { + if (bWriteSttTag) + { + switch(GetAsciiOptions().GetCharSet()) + { + case RTL_TEXTENCODING_UTF8: + if( bIncludeBOM ) + { + Strm().WriteUChar( 0xEF ).WriteUChar( 0xBB ).WriteUChar( 0xBF ); + } + + break; + case RTL_TEXTENCODING_UCS2: +#ifdef OSL_LITENDIAN + Strm().SetEndian(SvStreamEndian::LITTLE); + if( bIncludeBOM ) + { + Strm().WriteUChar( 0xFF ).WriteUChar( 0xFE ); + } +#else + Strm().SetEndian(SvStreamEndian::BIG); + if( bIncludeBOM ) + { + Strm().WriteUChar( 0xFE ).WriteUChar( 0xFF ); + } +#endif + break; + + } + bWriteSttTag = false; + } + Out( aASCNodeFnTab, *pNd, *this ); + } + bTstFly = false; // Testing once is enough + } + + if( !m_pCurrentPam->Move( fnMoveForward, GoInNode ) ) + break; + + if( m_bShowProgress ) + ::SetProgressState( m_pCurrentPam->GetPoint()->nNode.GetIndex(), + m_pDoc->GetDocShell() ); // How far? + + } + } while( CopyNextPam( &pPam ) ); // Until all pams are processed + + Strm().SetStreamCharSet( eOld ); + + if( m_bShowProgress ) + ::EndProgress( m_pDoc->GetDocShell() ); + + return ERRCODE_NONE; +} + +void GetASCWriter( const OUString& rFltNm, const OUString& /*rBaseURL*/, WriterRef& xRet ) +{ + xRet = new SwASCWriter( rFltNm ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ascii/wrtasc.hxx b/sw/source/filter/ascii/wrtasc.hxx new file mode 100644 index 000000000..cb0fdc191 --- /dev/null +++ b/sw/source/filter/ascii/wrtasc.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_FILTER_ASCII_WRTASC_HXX +#define INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX + +#include <shellio.hxx> +#include <wrt_fn.hxx> + +extern SwNodeFnTab aASCNodeFnTab; + +// The ASC writer + +class SwASCWriter : public Writer +{ + OUString m_sLineEnd; + + virtual ErrCode WriteStream() override; + +public: + SwASCWriter( const OUString& rFilterName ); + virtual ~SwASCWriter() override; + + const OUString& GetLineEnd() const { return m_sLineEnd; } +}; + +#endif // _ INCLUDED_SW_SOURCE_FILTER_ASCII_WRTASC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/basflt/docfact.cxx b/sw/source/filter/basflt/docfact.cxx new file mode 100644 index 000000000..f2302eb92 --- /dev/null +++ b/sw/source/filter/basflt/docfact.cxx @@ -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 . + */ + +#include <doc.hxx> +#include <docfac.hxx> + + +SwDocFac::SwDocFac( SwDoc *pDc ) + : mxDoc( pDc ) +{ +} + + +SwDocFac::~SwDocFac() COVERITY_NOEXCEPT_FALSE +{ +} + + +SwDoc *SwDocFac::GetDoc() +{ + if( !mxDoc.is() ) + { + mxDoc = new SwDoc; + } + return mxDoc.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/basflt/fltini.cxx b/sw/source/filter/basflt/fltini.cxx new file mode 100644 index 000000000..a36bc9ae0 --- /dev/null +++ b/sw/source/filter/basflt/fltini.cxx @@ -0,0 +1,726 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lang.h> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/any.hxx> +#include <tools/svlibrary.h> +#include <sot/storage.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <shellio.hxx> +#include <fltini.hxx> +#include <init.hxx> +#include <fmtfsize.hxx> +#include <swtable.hxx> +#include <fmtcntnt.hxx> +#include <editeng/boxitem.hxx> +#include <ndtxt.hxx> +#include <swfltopt.hxx> +#include <swdll.hxx> +#include <iodetect.hxx> +#include <osl/module.hxx> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +using namespace utl; +using namespace com::sun::star::uno; +using namespace com::sun::star; + +Reader *ReadAscii = nullptr, *ReadHTML = nullptr, *ReadXML = nullptr; + +static Reader* GetRTFReader(); +static Reader* GetWW8Reader(); +static Reader* GetDOCXReader(); + +// Note: if editing, please don't forget to modify also the enum +// ReaderWriterEnum and aFilterDetect in iodetect.hxx & iodetect.cxx +static SwReaderWriterEntry aReaderWriter[] = +{ + SwReaderWriterEntry( &::GetRTFReader, &::GetRTFWriter, true ), + SwReaderWriterEntry( nullptr, &::GetASCWriter, false ), + SwReaderWriterEntry( &::GetWW8Reader, nullptr, true ), + SwReaderWriterEntry( &::GetWW8Reader, &::GetWW8Writer, true ), + SwReaderWriterEntry( &::GetRTFReader, &::GetRTFWriter, true ), + SwReaderWriterEntry( nullptr, &::GetHTMLWriter, true ), + SwReaderWriterEntry( &::GetWW8Reader, nullptr, true ), + SwReaderWriterEntry( nullptr, &::GetXMLWriter, true ), + SwReaderWriterEntry( nullptr, &::GetASCWriter, false ), + SwReaderWriterEntry( nullptr, &::GetASCWriter, true ), + SwReaderWriterEntry( &::GetDOCXReader, nullptr, true ) +}; + +Reader* SwReaderWriterEntry::GetReader() +{ + if ( pReader ) + return pReader; + else if ( fnGetReader ) + { + pReader = (*fnGetReader)(); + return pReader; + } + return nullptr; +} + +void SwReaderWriterEntry::GetWriter( const OUString& rNm, const OUString& rBaseURL, WriterRef& xWrt ) const +{ + if ( fnGetWriter ) + (*fnGetWriter)( rNm, rBaseURL, xWrt ); + else + xWrt = WriterRef(nullptr); +} + +Reader* SwGetReaderXML() // SW_DLLPUBLIC +{ + return ReadXML; +} + +static void SetFltPtr( sal_uInt16 rPos, Reader* pReader ) +{ + aReaderWriter[ rPos ].pReader = pReader; +} + +namespace sw { + +Filters::Filters() +{ + ReadAscii = new AsciiReader; + ReadHTML = new HTMLReader; + ReadXML = new XMLReader; + SetFltPtr( READER_WRITER_BAS, ReadAscii ); + SetFltPtr( READER_WRITER_HTML, ReadHTML ); + SetFltPtr( READER_WRITER_XML, ReadXML ); + SetFltPtr( READER_WRITER_TEXT_DLG, ReadAscii ); + SetFltPtr( READER_WRITER_TEXT, ReadAscii ); +} + +Filters::~Filters() +{ + // kill Readers + for(SwReaderWriterEntry & rEntry : aReaderWriter) + { + if( rEntry.bDelReader && rEntry.pReader ) + { + delete rEntry.pReader; + rEntry.pReader = nullptr; + } + } +} + +#ifndef DISABLE_DYNLOADING + +oslGenericFunction Filters::GetMswordLibSymbol( const char *pSymbol ) +{ + if (!msword_.is()) + { + OUString url("$LO_LIB_DIR/" SVLIBRARY("msword")); + rtl::Bootstrap::expandMacros(url); + bool ok = msword_.load( url, SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY ); + SAL_WARN_IF(!ok, "sw", "failed to load msword library"); + } + if (msword_.is()) + return msword_.getFunctionSymbol( OUString::createFromAscii( pSymbol ) ); + return nullptr; +} + +#endif + +} + +namespace SwReaderWriter { + +Reader* GetRtfReader() +{ + return aReaderWriter[READER_WRITER_RTF].GetReader(); +} + +Reader* GetDOCXReader() +{ + return aReaderWriter[READER_WRITER_DOCX].GetReader(); +} + +void GetWriter( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet ) +{ + for( int n = 0; n < MAXFILTER; ++n ) + if ( aFilterDetect[n].IsFilter( rFltName ) ) + { + aReaderWriter[n].GetWriter( rFltName, rBaseURL, xRet ); + break; + } +} + +Reader* GetReader( const OUString& rFltName ) +{ + Reader* pRead = nullptr; + for( int n = 0; n < MAXFILTER; ++n ) + { + if ( aFilterDetect[n].IsFilter( rFltName ) ) + { + pRead = aReaderWriter[n].GetReader(); + // add special treatment for some readers + if ( pRead ) + pRead->SetFltName( rFltName ); + break; + } + } + return pRead; +} + +} // namespace SwReaderWriter + +bool Writer::IsStgWriter() const { return false; } + +bool StgWriter::IsStgWriter() const { return true; } + +// Read Filter Flags; used by WW8 / W4W / EXCEL / LOTUS + +/* +<FilterFlags> + <Excel_Lotus> + <MinRow cfg:type="long">0</MinRow> + <MaxRow cfg:type="long">0</MaxRow> + <MinCol cfg:type="long">0</MinCol> + <MaxCol cfg:type="long">0</MaxCol> + </Excel_Lotus> + <W4W> + <W4WHD cfg:type="long">0</W4WHD> + <W4WFT cfg:type="long">0</W4WFT> + <W4W000 cfg:type="long">0</W4W000> + </W4W> + <WinWord> + <WW1F cfg:type="long">0</WW1F> + <WW cfg:type="long">0</WW> + <WW8 cfg:type="long">0</WW8> + <WWF cfg:type="long">0</WWF> + <WWFA0 cfg:type="long">0</WWFA0> + <WWFA1 cfg:type="long">0</WWFA1> + <WWFA2 cfg:type="long">0</WWFA2> + <WWFB0 cfg:type="long">0</WWFB0> + <WWFB1 cfg:type="long">0</WWFB1> + <WWFB2 cfg:type="long">0</WWFB2> + <WWFLX cfg:type="long">0</WWFLX> + <WWFLY cfg:type="long">0</WWFLY> + <WWFT cfg:type="long">0</WWFT> + <WWWR cfg:type="long">0</WWWR> + </WinWord> + <Writer> + <SW3Imp cfg:type="long">0</SW3Imp> + </Writer> +</FilterFlags> +*/ + +SwFilterOptions::SwFilterOptions( sal_uInt16 nCnt, const char** ppNames, + sal_uInt64* pValues ) + : ConfigItem( "Office.Writer/FilterFlags" ) +{ + GetValues( nCnt, ppNames, pValues ); +} + +void SwFilterOptions::GetValues( sal_uInt16 nCnt, const char** ppNames, + sal_uInt64* pValues ) +{ + Sequence<OUString> aNames( nCnt ); + OUString* pNames = aNames.getArray(); + sal_uInt16 n; + + for( n = 0; n < nCnt; ++n ) + pNames[ n ] = OUString::createFromAscii( ppNames[ n ] ); + Sequence<Any> aValues = GetProperties( aNames ); + + if( nCnt == aValues.getLength() ) + { + const Any* pAnyValues = aValues.getConstArray(); + for( n = 0; n < nCnt; ++n ) + pValues[ n ] = pAnyValues[ n ].hasValue() + ? *o3tl::doAccess<sal_uInt64>(pAnyValues[ n ]) + : 0; + } + else + { + for( n = 0; n < nCnt; ++n ) + pValues[ n ] = 0; + } +} + +void SwFilterOptions::ImplCommit() {} +void SwFilterOptions::Notify( const css::uno::Sequence< OUString >& ) {} + +void StgReader::SetFltName( const OUString& rFltNm ) +{ + if( SwReaderType::Storage & GetReaderType() ) + m_aFltName = rFltNm; +} + +void CalculateFlySize(SfxItemSet& rFlySet, const SwNodeIndex& rAnchor, + SwTwips nPageWidth) +{ + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET != rFlySet.GetItemState( RES_FRM_SIZE, true, &pItem ) || + MINFLY > static_cast<const SwFormatFrameSize*>(pItem)->GetWidth() ) + { + std::unique_ptr<SwFormatFrameSize> aSz(rFlySet.Get(RES_FRM_SIZE).Clone()); + if (pItem) + aSz.reset(static_cast<SwFormatFrameSize*>(pItem->Clone())); + + SwTwips nWidth; + // determine the width; if there is a table use the width of the table; + // otherwise use the width of the page + const SwTableNode* pTableNd = rAnchor.GetNode().FindTableNode(); + if( pTableNd ) + nWidth = pTableNd->GetTable().GetFrameFormat()->GetFrameSize().GetWidth(); + else + nWidth = nPageWidth; + + const SwNodeIndex* pSttNd = rFlySet.Get( RES_CNTNT ).GetContentIdx(); + if( pSttNd ) + { + bool bOnlyOneNode = true; + sal_uLong nMinFrame = 0; + sal_uLong nMaxFrame = 0; + SwTextNode* pFirstTextNd = nullptr; + SwNodeIndex aIdx( *pSttNd, 1 ); + SwNodeIndex aEnd( *pSttNd->GetNode().EndOfSectionNode() ); + while( aIdx < aEnd ) + { + SwTextNode *pTextNd = aIdx.GetNode().GetTextNode(); + if( pTextNd ) + { + if( !pFirstTextNd ) + pFirstTextNd = pTextNd; + else if( pFirstTextNd != pTextNd ) + { + // forget it + bOnlyOneNode = false; + break; + } + + sal_uLong nAbsMinCnts; + pTextNd->GetMinMaxSize( aIdx.GetIndex(), nMinFrame, nMaxFrame, nAbsMinCnts ); + } + ++aIdx; + } + + if( bOnlyOneNode ) + { + if( nMinFrame < MINLAY && pFirstTextNd ) + { + // if the first node don't contained any content, then + // insert one char in it calc again and delete once again + SwIndex aNdIdx( pFirstTextNd ); + pFirstTextNd->InsertText("MM", aNdIdx); + sal_uLong nAbsMinCnts; + pFirstTextNd->GetMinMaxSize( pFirstTextNd->GetIndex(), + nMinFrame, nMaxFrame, nAbsMinCnts ); + aNdIdx -= 2; + pFirstTextNd->EraseText( aNdIdx, 2 ); + } + + // consider border and distance to content + const SvxBoxItem& rBoxItem = rFlySet.Get( RES_BOX ); + SvxBoxItemLine nLine = SvxBoxItemLine::LEFT; + for( int i = 0; i < 2; ++i ) + { + const editeng::SvxBorderLine* pLn = rBoxItem.GetLine( nLine ); + if( pLn ) + { + sal_uInt16 nWidthTmp = pLn->GetOutWidth() + pLn->GetInWidth(); + nWidthTmp = nWidthTmp + rBoxItem.GetDistance( nLine ); + nMinFrame += nWidthTmp; + nMaxFrame += nWidthTmp; + } + nLine = SvxBoxItemLine::RIGHT; + } + + // enforce minimum width for contents + if( nMinFrame < MINLAY ) + nMinFrame = MINLAY; + if( nMaxFrame < MINLAY ) + nMaxFrame = MINLAY; + + if( nWidth > static_cast<sal_uInt16>(nMaxFrame) ) + nWidth = nMaxFrame; + else if( nWidth > static_cast<sal_uInt16>(nMinFrame) ) + nWidth = nMinFrame; + } + } + + if( MINFLY > nWidth ) + nWidth = MINFLY; + + aSz->SetWidth( nWidth ); + if( MINFLY > aSz->GetHeight() ) + aSz->SetHeight( MINFLY ); + rFlySet.Put( *aSz ); + } + else if( MINFLY > static_cast<const SwFormatFrameSize*>(pItem)->GetHeight() ) + { + std::unique_ptr<SwFormatFrameSize> aSz(static_cast<SwFormatFrameSize*>(pItem->Clone())); + aSz->SetHeight( MINFLY ); + rFlySet.Put( std::move(aSz) ); + } +} + +namespace +{ + +struct CharSetNameMap +{ + rtl_TextEncoding eCode; + const char* pName; +}; + +const CharSetNameMap *GetCharSetNameMap() +{ + static const CharSetNameMap aMapArr[] = + { +# define IMPLENTRY(X) { RTL_TEXTENCODING_##X, #X } + IMPLENTRY(DONTKNOW), + IMPLENTRY(MS_1252), + IMPLENTRY(APPLE_ROMAN), + IMPLENTRY(IBM_437), + IMPLENTRY(IBM_850), + IMPLENTRY(IBM_860), + IMPLENTRY(IBM_861), + IMPLENTRY(IBM_863), + IMPLENTRY(IBM_865), + IMPLENTRY(SYMBOL), + IMPLENTRY(ASCII_US), + IMPLENTRY(ISO_8859_1), + IMPLENTRY(ISO_8859_2), + IMPLENTRY(ISO_8859_3), + IMPLENTRY(ISO_8859_4), + IMPLENTRY(ISO_8859_5), + IMPLENTRY(ISO_8859_6), + IMPLENTRY(ISO_8859_7), + IMPLENTRY(ISO_8859_8), + IMPLENTRY(ISO_8859_9), + IMPLENTRY(ISO_8859_14), + IMPLENTRY(ISO_8859_15), + IMPLENTRY(IBM_737), + IMPLENTRY(IBM_775), + IMPLENTRY(IBM_852), + IMPLENTRY(IBM_855), + IMPLENTRY(IBM_857), + IMPLENTRY(IBM_862), + IMPLENTRY(IBM_864), + IMPLENTRY(IBM_866), + IMPLENTRY(IBM_869), + IMPLENTRY(MS_874), + IMPLENTRY(MS_1250), + IMPLENTRY(MS_1251), + IMPLENTRY(MS_1253), + IMPLENTRY(MS_1254), + IMPLENTRY(MS_1255), + IMPLENTRY(MS_1256), + IMPLENTRY(MS_1257), + IMPLENTRY(MS_1258), + IMPLENTRY(APPLE_ARABIC), + IMPLENTRY(APPLE_CENTEURO), + IMPLENTRY(APPLE_CROATIAN), + IMPLENTRY(APPLE_CYRILLIC), + IMPLENTRY(APPLE_DEVANAGARI), + IMPLENTRY(APPLE_FARSI), + IMPLENTRY(APPLE_GREEK), + IMPLENTRY(APPLE_GUJARATI), + IMPLENTRY(APPLE_GURMUKHI), + IMPLENTRY(APPLE_HEBREW), + IMPLENTRY(APPLE_ICELAND), + IMPLENTRY(APPLE_ROMANIAN), + IMPLENTRY(APPLE_THAI), + IMPLENTRY(APPLE_TURKISH), + IMPLENTRY(APPLE_UKRAINIAN), + IMPLENTRY(APPLE_CHINSIMP), + IMPLENTRY(APPLE_CHINTRAD), + IMPLENTRY(APPLE_JAPANESE), + IMPLENTRY(APPLE_KOREAN), + IMPLENTRY(MS_932), + IMPLENTRY(MS_936), + IMPLENTRY(MS_949), + IMPLENTRY(MS_950), + IMPLENTRY(SHIFT_JIS), + IMPLENTRY(GB_2312), + IMPLENTRY(GBT_12345), + IMPLENTRY(GBK), + IMPLENTRY(BIG5), + IMPLENTRY(EUC_JP), + IMPLENTRY(EUC_CN), + IMPLENTRY(EUC_TW), + IMPLENTRY(ISO_2022_JP), + IMPLENTRY(ISO_2022_CN), + IMPLENTRY(KOI8_R), + IMPLENTRY(KOI8_U), + IMPLENTRY(UTF7), + IMPLENTRY(UTF8), + IMPLENTRY(ISO_8859_10), + IMPLENTRY(ISO_8859_13), + IMPLENTRY(EUC_KR), + IMPLENTRY(ISO_2022_KR), + IMPLENTRY(JIS_X_0201), + IMPLENTRY(JIS_X_0208), + IMPLENTRY(JIS_X_0212), + IMPLENTRY(MS_1361), + IMPLENTRY(GB_18030), + IMPLENTRY(BIG5_HKSCS), + IMPLENTRY(TIS_620), + IMPLENTRY(PT154), + IMPLENTRY(UCS4), + IMPLENTRY(UCS2), + IMPLENTRY(UNICODE), + {0,nullptr} //Last + }; + return &aMapArr[0]; +} + +/* + Get a rtl_TextEncoding from its name + */ +rtl_TextEncoding CharSetFromName(const OUString& rChrSetStr) +{ + const CharSetNameMap *pStart = GetCharSetNameMap(); + rtl_TextEncoding nRet = pStart->eCode; + + for(const CharSetNameMap *pMap = pStart; pMap->pName; ++pMap) + { + if(rChrSetStr.equalsIgnoreAsciiCaseAscii(pMap->pName)) + { + nRet = pMap->eCode; + break; + } + } + + OSL_ENSURE(nRet != pStart->eCode, "TXT: That was an unknown language!"); + + return nRet; +} + +/* + Get the String name of an rtl_TextEncoding + */ +OUString NameFromCharSet(rtl_TextEncoding nChrSet) +{ + const CharSetNameMap *pStart = GetCharSetNameMap(); + const char *pRet = pStart->pName; + + for(const CharSetNameMap *pMap = pStart; pMap->pName; ++pMap) + { + if (nChrSet == pMap->eCode) + { + pRet = pMap->pName; + break; + } + } + + OSL_ENSURE(pRet != pStart->pName, "TXT: That was an unknown language!"); + + return OUString::createFromAscii(pRet); +} + +} + +// for the automatic conversion (mail/news/...) +// The user data contains the options for the ascii import/export filter. +// The format is: +// 1. CharSet - as ascii chars +// 2. LineEnd - as CR/LF/CRLF +// 3. Fontname +// 4. Language +// 5. Whether to include byte-order-mark - as true/false +// the delimiter character is "," + +void SwAsciiOptions::ReadUserData( const OUString& rStr ) +{ + sal_Int32 nToken = 0; + OUString sToken = rStr.getToken(0, ',', nToken); // 1. Charset name + if (!sToken.isEmpty()) + m_eCharSet = CharSetFromName(sToken); + if (nToken >= 0 && !(sToken = rStr.getToken(0, ',', nToken)).isEmpty()) // 2. Line ending type + { + if (sToken.equalsIgnoreAsciiCase("CRLF")) + m_eCRLF_Flag = LINEEND_CRLF; + else if (sToken.equalsIgnoreAsciiCase("LF")) + m_eCRLF_Flag = LINEEND_LF; + else + m_eCRLF_Flag = LINEEND_CR; + } + if (nToken >= 0 && !(sToken = rStr.getToken(0, ',', nToken)).isEmpty()) // 3. Font name + m_sFont = sToken; + if (nToken >= 0 && !(sToken = rStr.getToken(0, ',', nToken)).isEmpty()) // 4. Language tag + m_nLanguage = LanguageTag::convertToLanguageTypeWithFallback(sToken); + if (nToken >= 0 && !(sToken = rStr.getToken(0, ',', nToken)).isEmpty()) // 5. Include BOM? + m_bIncludeBOM = !(sToken.equalsIgnoreAsciiCase("FALSE")); +} + +void SwAsciiOptions::WriteUserData(OUString& rStr) +{ + // 1. charset + rStr = NameFromCharSet(m_eCharSet) + ","; + + // 2. LineEnd + switch(m_eCRLF_Flag) + { + case LINEEND_CRLF: + rStr += "CRLF"; + break; + case LINEEND_CR: + rStr += "CR"; + break; + case LINEEND_LF: + rStr += "LF"; + break; + } + rStr += ","; + + // 3. Fontname + rStr += m_sFont + ","; + + // 4. Language + if (m_nLanguage) + { + rStr += LanguageTag::convertToBcp47(m_nLanguage); + } + rStr += ","; + + // 5. Whether to include byte-order-mark + if( m_bIncludeBOM ) + { + rStr += "true"; + } + else + { + rStr += "false"; + } + rStr += ","; +} + +#ifdef DISABLE_DYNLOADING + +extern "C" { + Reader *ImportRTF(); + void ExportRTF( const OUString&, const OUString& rBaseURL, WriterRef& ); + Reader *ImportDOC(); + void ExportDOC( const OUString&, const OUString& rBaseURL, WriterRef& ); + Reader *ImportDOCX(); + sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell&, SotStorage&, sal_Bool, const OUString& ); + sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell& ); +} + +#endif + +Reader* GetRTFReader() +{ +#ifndef DISABLE_DYNLOADING + + FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportRTF" ) ); + + if ( pFunction ) + return (*pFunction)(); + + return nullptr; +#else + return ImportRTF(); +#endif + +} + +void GetRTFWriter( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet ) +{ +#ifndef DISABLE_DYNLOADING + FnGetWriter pFunction = reinterpret_cast<FnGetWriter>( SwGlobals::getFilters().GetMswordLibSymbol( "ExportRTF" ) ); + + if ( pFunction ) + (*pFunction)( rFltName, rBaseURL, xRet ); + else + xRet = WriterRef(nullptr); +#else + ExportRTF( rFltName, rBaseURL, xRet ); +#endif +} + +Reader* GetWW8Reader() +{ +#ifndef DISABLE_DYNLOADING + FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportDOC" ) ); + + if ( pFunction ) + return (*pFunction)(); + + return nullptr; +#else + return ImportDOC(); +#endif +} + +void GetWW8Writer( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet ) +{ +#ifndef DISABLE_DYNLOADING + FnGetWriter pFunction = reinterpret_cast<FnGetWriter>( SwGlobals::getFilters().GetMswordLibSymbol( "ExportDOC" ) ); + + if ( pFunction ) + (*pFunction)( rFltName, rBaseURL, xRet ); + else + xRet = WriterRef(nullptr); +#else + ExportDOC( rFltName, rBaseURL, xRet ); +#endif +} + +Reader* GetDOCXReader() +{ +#ifndef DISABLE_DYNLOADING + FnGetReader pFunction = reinterpret_cast<FnGetReader>( SwGlobals::getFilters().GetMswordLibSymbol( "ImportDOCX" ) ); + + if ( pFunction ) + return (*pFunction)(); + + return nullptr; +#else + return ImportDOCX(); +#endif +} + +typedef sal_uInt32 ( *SaveOrDel )( SfxObjectShell&, SotStorage&, sal_Bool, const OUString& ); +typedef sal_uInt32 ( *GetSaveWarning )( SfxObjectShell& ); + +ErrCode SaveOrDelMSVBAStorage( SfxObjectShell& rDoc, SotStorage& rStor, bool bSaveInto, const OUString& rStorageName ) +{ +#ifndef DISABLE_DYNLOADING + SaveOrDel pFunction = reinterpret_cast<SaveOrDel>( SwGlobals::getFilters().GetMswordLibSymbol( "SaveOrDelMSVBAStorage_ww8" ) ); + if( pFunction ) + return ErrCode(pFunction( rDoc, rStor, bSaveInto, rStorageName )); + return ERRCODE_NONE; +#else + return ErrCode(SaveOrDelMSVBAStorage_ww8( rDoc, rStor, bSaveInto, rStorageName )); +#endif +} + +ErrCode GetSaveWarningOfMSVBAStorage( SfxObjectShell &rDocS ) +{ +#ifndef DISABLE_DYNLOADING + GetSaveWarning pFunction = reinterpret_cast<GetSaveWarning>( SwGlobals::getFilters().GetMswordLibSymbol( "GetSaveWarningOfMSVBAStorage_ww8" ) ); + if( pFunction ) + return ErrCode(pFunction( rDocS )); + return ERRCODE_NONE; +#else + return ErrCode(GetSaveWarningOfMSVBAStorage_ww8( rDocS )); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/basflt/fltshell.cxx b/sw/source/filter/basflt/fltshell.cxx new file mode 100644 index 000000000..8486eb3ce --- /dev/null +++ b/sw/source/filter/basflt/fltshell.cxx @@ -0,0 +1,1131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> + +#include <cstddef> + +#include <hintids.hxx> +#include <hints.hxx> + +#include <svl/cintitem.hxx> +#include <svl/stritem.hxx> +#include <fmtanchr.hxx> +#include <fmtfld.hxx> +#include <redline.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <ndtxt.hxx> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <txtfld.hxx> +#include <tox.hxx> +#include <expfld.hxx> +#include <bookmrk.hxx> +#include <fltshell.hxx> +#include <rdfhelper.hxx> + +using namespace com::sun::star; + +static SwContentNode* GetContentNode(SwDoc* pDoc, SwNodeIndex& rIdx, bool bNext) +{ + SwContentNode * pCNd = rIdx.GetNode().GetContentNode(); + if(!pCNd && nullptr == (pCNd = bNext ? pDoc->GetNodes().GoNext(&rIdx) + : SwNodes::GoPrevious(&rIdx))) + { + pCNd = bNext ? SwNodes::GoPrevious(&rIdx) + : pDoc->GetNodes().GoNext(&rIdx); + OSL_ENSURE(pCNd, "no ContentNode found"); + } + return pCNd; +} + +static OUString lcl_getTypePath(OUString& rType) +{ + OUString aRet; + if (rType.startsWith("urn:bails")) + { + rType = "urn:bails"; + aRet = "tscp/bails.rdf"; + } + return aRet; +} + +// Stack entry for all text attributes +SwFltStackEntry::SwFltStackEntry(const SwPosition& rStartPos, std::unique_ptr<SfxPoolItem> pHt) + : m_aMkPos(rStartPos) + , m_aPtPos(rStartPos) + , pAttr( std::move(pHt) ) + , m_isAnnotationOnEnd(false) + , mnStartCP(-1) + , mnEndCP(-1) + , bIsParaEnd(false) +{ + bOld = false; // used for marking Attributes *before* skipping field results + bOpen = true; // lock the attribute --> may first + bConsumedByField = false; +} + +SwFltStackEntry::~SwFltStackEntry() +{ + // Although attribute got passed as pointer, it gets deleted here +} + +void SwFltStackEntry::SetEndPos(const SwPosition& rEndPos) +{ + // Release attribute and keep track of end + // Everything with sal_uInt16s, lest the inserting of new text at + // the cursor position moves the attribute's range + // That's not the desired behavior! + bOpen = false; // release and remember END + m_aPtPos.SetPos(rEndPos); +} + +bool SwFltStackEntry::MakeRegion(SwDoc* pDoc, SwPaM& rRegion, RegionMode const eCheck, + const SwFltPosition &rMkPos, const SwFltPosition &rPtPos, bool bIsParaEnd, + sal_uInt16 nWhich) +{ + // does this range actually contain something? + // empty range is allowed if at start of empty paragraph + // fields are special: never have range, so leave them + + // The only position of 0x0D will not be able to make region in the old logic + // because it is beyond the length of para...need special consideration here. + sal_uLong nMk = rMkPos.m_nNode.GetIndex() + 1; + const SwNodes& rMkNodes = rMkPos.m_nNode.GetNodes(); + if (nMk >= rMkNodes.Count()) + return false; + SwContentNode *const pContentNode(rMkNodes[nMk]->GetContentNode()); + if (rMkPos == rPtPos && + ((0 != rPtPos.m_nContent) || (pContentNode && (0 != pContentNode->Len()))) + && ( RES_TXTATR_FIELD != nWhich + && RES_TXTATR_ANNOTATION != nWhich + && RES_TXTATR_INPUTFIELD != nWhich ) + && !(bIsParaEnd && pContentNode && pContentNode->IsTextNode() && 0 != pContentNode->Len() )) + { + return false; + } + // The content indices always apply to the node! + rRegion.GetPoint()->nNode = rMkPos.m_nNode.GetIndex() + 1; + SwContentNode* pCNd = GetContentNode(pDoc, rRegion.GetPoint()->nNode, true); + + SAL_WARN_IF(pCNd->Len() < rMkPos.m_nContent, "sw.ww8", + "invalid content index " << rMkPos.m_nContent << " but text node has only " << pCNd->Len()); + rRegion.GetPoint()->nContent.Assign(pCNd, + std::min<sal_Int32>(rMkPos.m_nContent, pCNd->Len())); + rRegion.SetMark(); + if (rMkPos.m_nNode != rPtPos.m_nNode) + { + sal_uLong n = rPtPos.m_nNode.GetIndex() + 1; + SwNodes& rNodes = rRegion.GetPoint()->nNode.GetNodes(); + if (n >= rNodes.Count()) + return false; + rRegion.GetPoint()->nNode = n; + pCNd = GetContentNode(pDoc, rRegion.GetPoint()->nNode, false); + } + SAL_WARN_IF(pCNd->Len() < rPtPos.m_nContent, "sw.ww8", + "invalid content index " << rPtPos.m_nContent << " but text node has only " << pCNd->Len()); + rRegion.GetPoint()->nContent.Assign(pCNd, + std::min<sal_Int32>(rPtPos.m_nContent, pCNd->Len())); + OSL_ENSURE( CheckNodesRange( rRegion.Start()->nNode, + rRegion.End()->nNode, true ), + "attribute or similar crosses section-boundaries" ); + bool bRet = true; + if (eCheck & RegionMode::CheckNodes) + { + bRet &= CheckNodesRange(rRegion.Start()->nNode, + rRegion.End()->nNode, true); + } + if (eCheck & RegionMode::CheckFieldmark) + { + bRet &= !sw::mark::IsFieldmarkOverlap(rRegion); + } + return bRet; +} + +bool SwFltStackEntry::MakeRegion(SwDoc* pDoc, SwPaM& rRegion, RegionMode eCheck) const +{ + return MakeRegion(pDoc, rRegion, eCheck, m_aMkPos, m_aPtPos, bIsParaEnd, + pAttr->Which()); +} + +SwFltControlStack::SwFltControlStack(SwDoc* pDo, sal_uLong nFieldFl) + : nFieldFlags(nFieldFl),bHasSdOD(true), bSdODChecked(false), pDoc(pDo), bIsEndStack(false) +{ +} + +SwFltControlStack::~SwFltControlStack() +{ + OSL_ENSURE(m_Entries.empty(), "There are still Attributes on the stack"); +} + +// MoveAttrs() is meant to address the following problem: +// When a field like "set variable" is set through the stack, the text +// is shifted by one \xff character, which makes all subsequent +// attribute positions invalid. +// After setting the attribute in the doc, MoveAttrs() needs to be +// called in order to push all attribute positions to the right in the +// same paragraph further out by one character. +void SwFltControlStack::MoveAttrs(const SwPosition& rPos, MoveAttrsMode eMode) +{ + size_t nCnt = m_Entries.size(); + sal_uLong nPosNd = rPos.nNode.GetIndex(); + sal_uInt16 nPosCt = rPos.nContent.GetIndex() - 1; + + for (size_t i=0; i < nCnt; ++i) + { + SwFltStackEntry& rEntry = *m_Entries[i]; + if ( + (rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) && + (rEntry.m_aMkPos.m_nContent >= nPosCt) + ) + { + rEntry.m_aMkPos.m_nContent++; + OSL_ENSURE( rEntry.m_aMkPos.m_nContent + <= pDoc->GetNodes()[nPosNd]->GetContentNode()->Len(), + "Attribute ends after end of line" ); + } + if ( + (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) && + (rEntry.m_aPtPos.m_nContent >= nPosCt) + ) + { + if ( !rEntry.m_isAnnotationOnEnd + || rEntry.m_aPtPos.m_nContent > nPosCt) + { + assert(!(rEntry.m_isAnnotationOnEnd && rEntry.m_aPtPos.m_nContent > nPosCt)); + if ( eMode == MoveAttrsMode::POSTIT_INSERTED + && rEntry.m_aPtPos.m_nContent == nPosCt + && rEntry.pAttr->Which() == RES_FLTR_ANNOTATIONMARK) + { + rEntry.m_isAnnotationOnEnd = true; + eMode = MoveAttrsMode::DEFAULT; // only set 1 flag + } + rEntry.m_aPtPos.m_nContent++; + OSL_ENSURE( rEntry.m_aPtPos.m_nContent + <= pDoc->GetNodes()[nPosNd]->GetContentNode()->Len(), + "Attribute ends after end of line" ); + } + } + } +} + +void SwFltControlStack::MarkAllAttrsOld() +{ + size_t nCnt = m_Entries.size(); + for (size_t i=0; i < nCnt; ++i) + m_Entries[i]->bOld = true; +} + +namespace +{ + bool couldExtendEntry(const SwFltStackEntry *pExtendCandidate, + const SfxPoolItem& rAttr) + { + return (pExtendCandidate && + !pExtendCandidate->bConsumedByField && + //if we bring character attributes into the fold we need to both + //a) consider RES_CHRATR_FONTSIZE and RES_CHRATR_FONT wrt Word's CJK/CTL variants + //b) consider crossing table cell boundaries (tdf#102334) + isPARATR_LIST(rAttr.Which()) && + *(pExtendCandidate->pAttr) == rAttr); + } +} + +void SwFltControlStack::NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr) +{ + sal_uInt16 nWhich = rAttr.Which(); + // Set end position of potentially equal attributes on stack, so + // as to avoid having them accumulate + SwFltStackEntry *pExtendCandidate = SetAttr(rPos, nWhich); + if (couldExtendEntry(pExtendCandidate, rAttr)) + { + //Here we optimize by seeing if there is an attribute uncommitted + //to the document which + + //a) has the same value as this attribute + //b) is already open, or ends at the same place as where we're starting + //from. If so we merge it with this one and elide adding another + //to the stack + pExtendCandidate->SetEndPos(rPos); + pExtendCandidate->bOpen=true; + } + else + { + SwFltStackEntry *pTmp = new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone()) ); + pTmp->SetStartCP(GetCurrAttrCP()); + m_Entries.push_back(std::unique_ptr<SwFltStackEntry>(pTmp)); + } +} + +void SwFltControlStack::DeleteAndDestroy(Entries::size_type nCnt) +{ + OSL_ENSURE(nCnt < m_Entries.size(), "Out of range!"); + if (nCnt < m_Entries.size()) + { + auto aElement = m_Entries.begin() + nCnt; + m_Entries.erase(aElement); + } + //Clear the para end position recorded in reader intermittently for the least impact on loading performance + //Because the attributes handled based on the unit of para + if ( empty() ) + { + ClearParaEndPosition(); + bHasSdOD = true; + bSdODChecked = false; + } +} + +// SwFltControlStack::StealAttr() removes attributes of the given type +// from the stack. Allowed as nAttrId: 0 meaning any, or a specific +// type. This makes them disappear from the doc structure. Only +// attributes from the same paragraph as rPos are removed. Used for +// graphic apos -> images. +void SwFltControlStack::StealAttr(const SwNodeIndex& rNode) +{ + size_t nCnt = m_Entries.size(); + + while (nCnt) + { + nCnt --; + SwFltStackEntry& rEntry = *m_Entries[nCnt]; + if (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == rNode.GetIndex()) + { + DeleteAndDestroy(nCnt); // delete from the stack + } + } +} + +// SwFltControlStack::KillUnlockedAttr() removes all attributes from +// the stack, which are assigned to an rPos. This makes them disappear +// from the doc structure. Used in WW import for ignoring attributes +// assigned to the 0x0c section break symbol. +void SwFltControlStack::KillUnlockedAttrs(const SwPosition& rPos) +{ + SwFltPosition aFltPos(rPos); + + size_t nCnt = m_Entries.size(); + while( nCnt ) + { + nCnt --; + SwFltStackEntry& rEntry = *m_Entries[nCnt]; + if( !rEntry.bOld + && !rEntry.bOpen + && (rEntry.m_aMkPos == aFltPos) + && (rEntry.m_aPtPos == aFltPos)) + { + DeleteAndDestroy( nCnt ); // remove from stack + } + } +} + +// Unlock all locked attributes and move to the end, all others will +// be applied to the document and removed from the stack. +// Returns if there were any selected attributes on the stack +SwFltStackEntry* SwFltControlStack::SetAttr(const SwPosition& rPos, + sal_uInt16 nAttrId, bool bTstEnd, long nHand, + bool consumedByField) +{ + SwFltStackEntry *pRet = nullptr; + + SwFltPosition aFltPos(rPos); + + OSL_ENSURE(!nAttrId || + (POOLATTR_BEGIN <= nAttrId && POOLATTR_END > nAttrId) || + (RES_FLTRATTR_BEGIN <= nAttrId && RES_FLTRATTR_END > nAttrId), + "Wrong id for attribute"); + + auto aI = m_Entries.begin(); + while (aI != m_Entries.end()) + { + bool bLastEntry = aI == m_Entries.end() - 1; + + SwFltStackEntry& rEntry = **aI; + if (rEntry.bOpen) + { + // set end of attribute + bool bF = false; + if (!nAttrId ) + { + bF = true; + } + else if (nAttrId == rEntry.pAttr->Which()) + { + if( nAttrId != RES_FLTR_BOOKMARK && nAttrId != RES_FLTR_ANNOTATIONMARK && nAttrId != RES_FLTR_RDFMARK ) + { + // query handle + bF = true; + } + else if (nAttrId == RES_FLTR_BOOKMARK && nHand == static_cast<SwFltBookmark*>(rEntry.pAttr.get())->GetHandle()) + { + bF = true; + } + else if (nAttrId == RES_FLTR_ANNOTATIONMARK && nHand == static_cast<CntUInt16Item*>(rEntry.pAttr.get())->GetValue()) + { + bF = true; + } + else if (nAttrId == RES_FLTR_RDFMARK && nHand == static_cast<SwFltRDFMark*>(rEntry.pAttr.get())->GetHandle()) + { + bF = true; + } + } + if (bF) + { + rEntry.bConsumedByField = consumedByField; + rEntry.SetEndPos(rPos); + rEntry.SetEndCP(GetCurrAttrCP()); + if (bLastEntry && nAttrId == rEntry.pAttr->Which()) + { + //potential candidate for merging with an identical + //property beginning at rPos + pRet = &rEntry; + } + } + ++aI; + continue; + } + + // if the end position is equal to the cursor position, then + // refrain from applying it; there needs to be following text, + // except at the very end. (attribute expansion !!) + // Never apply end stack except at document ending + if (bTstEnd) + { + if (bIsEndStack) + { + ++aI; + continue; + } + + //defer inserting this attribute into the document until + //we advance to the next node, or finish processing the document + if (rEntry.m_aPtPos.m_nNode.GetIndex() == aFltPos.m_nNode.GetIndex()) + { + if (bLastEntry && nAttrId == rEntry.pAttr->Which() && + rEntry.m_aPtPos.m_nContent == aFltPos.m_nContent) + { + //potential candidate for merging with an identical + //property beginning at rPos + pRet = &rEntry; + } + + ++aI; + continue; + } + } + SetAttrInDoc(rPos, rEntry); + aI = m_Entries.erase(aI); + } + + return pRet; +} + +static bool MakePoint(const SwFltStackEntry& rEntry, SwDoc* pDoc, + SwPaM& rRegion) +{ + // the anchor is the Pam's Point. It's modified when inserting + // text, etc.; therefore it is kept on the stack. Only the + // attribute's format needs to be set. + rRegion.DeleteMark(); + + sal_uLong nMk = rEntry.m_aMkPos.m_nNode.GetIndex() + 1; + const SwNodes& rMkNodes = rEntry.m_aMkPos.m_nNode.GetNodes(); + if (nMk >= rMkNodes.Count()) + return false; + + rRegion.GetPoint()->nNode = nMk; + SwContentNode* pCNd = GetContentNode(pDoc, rRegion.GetPoint()->nNode, true); + rRegion.GetPoint()->nContent.Assign(pCNd, rEntry.m_aMkPos.m_nContent); + return true; +} + +// MakeBookRegionOrPoint() behaves like MakeRegionOrPoint, except that +// it adheres to certain restrictions on bookmarks in tables (cannot +// span more than one cell) +static bool MakeBookRegionOrPoint(const SwFltStackEntry& rEntry, SwDoc* pDoc, + SwPaM& rRegion ) +{ + if (rEntry.MakeRegion(pDoc, rRegion, SwFltStackEntry::RegionMode::CheckNodes)) + { + if (rRegion.GetPoint()->nNode.GetNode().FindTableBoxStartNode() + != rRegion.GetMark()->nNode.GetNode().FindTableBoxStartNode()) + { + rRegion.Exchange(); // invalid range + rRegion.DeleteMark(); // -> both to mark + } + return true; + } + return MakePoint(rEntry, pDoc, rRegion); +} + +// IterateNumrulePiece() looks for the first range valid for Numrules +// between rTmpStart and rEnd. + +// rNds denotes the doc nodes +// rEnd denotes the range end, +// rTmpStart is an in/out parameter: in: start of range to be searched, +// out: start of valid range +// rTmpEnd is an out parameter +// Returns true for valid range +static bool IterateNumrulePiece( const SwNodeIndex& rEnd, + SwNodeIndex& rTmpStart, SwNodeIndex& rTmpEnd ) +{ + while( ( rTmpStart <= rEnd ) + && !( rTmpStart.GetNode().IsTextNode() ) ) // look for valid start + ++rTmpStart; + + rTmpEnd = rTmpStart; + while( ( rTmpEnd <= rEnd ) + && ( rTmpEnd.GetNode().IsTextNode() ) ) // look for valid end + 1 + ++rTmpEnd; + + --rTmpEnd; // valid end + + return rTmpStart <= rTmpEnd; // valid ? +} + +//***This function will check whether there is existing individual attribute position for 0x0D***/ +//The check will happen only once for a paragraph during loading +bool SwFltControlStack::HasSdOD() +{ + bool bRet = false; + + for (auto const& it : m_Entries) + { + SwFltStackEntry& rEntry = *it; + if ( rEntry.mnStartCP == rEntry.mnEndCP ) + { + if ( CheckSdOD(rEntry.mnStartCP,rEntry.mnEndCP) ) + { + bRet = true; + break; + } + } + } + + return bRet; +} + +void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, + SwFltStackEntry& rEntry) +{ + SwPaM aRegion( rTmpPos ); + + switch(rEntry.pAttr->Which()) + { + case RES_FLTR_ANCHOR: + { + SwFrameFormat* pFormat = static_cast<SwFltAnchor*>(rEntry.pAttr.get())->GetFrameFormat(); + if (pFormat != nullptr) + { + MakePoint(rEntry, pDoc, aRegion); + SwFormatAnchor aAnchor(pFormat->GetAnchor()); + aAnchor.SetAnchor(aRegion.GetPoint()); + pFormat->SetFormatAttr(aAnchor); + // So the frames will be created when inserting into + // existing doc (after setting the anchor!): + if(pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() + && (RndStdIds::FLY_AT_PARA == pFormat->GetAnchor().GetAnchorId())) + { + pFormat->MakeFrames(); + } + } + } + break; + + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + break; + + case RES_TXTATR_TOXMARK: + break; + + case RES_FLTR_NUMRULE: // insert Numrule + { + const OUString& rNumNm = static_cast<SfxStringItem*>(rEntry.pAttr.get())->GetValue(); + SwNumRule* pNumRule = pDoc->FindNumRulePtr( rNumNm ); + if( pNumRule ) + { + if (rEntry.MakeRegion(pDoc, aRegion, SwFltStackEntry::RegionMode::CheckNodes)) + { + SwNodeIndex aTmpStart( aRegion.Start()->nNode ); + SwNodeIndex aTmpEnd( aTmpStart ); + SwNodeIndex& rRegEndNd = aRegion.End()->nNode; + while( IterateNumrulePiece( rRegEndNd, + aTmpStart, aTmpEnd ) ) + { + SwPaM aTmpPam( aTmpStart, aTmpEnd ); + // no start of a new list + pDoc->SetNumRule( aTmpPam, *pNumRule, false ); + + aTmpStart = aTmpEnd; // here starts the next range + ++aTmpStart; + } + } + else + pDoc->DelNumRule( rNumNm ); + } + } + break; + + case RES_FLTR_BOOKMARK: + { + SwFltBookmark* pB = static_cast<SwFltBookmark*>(rEntry.pAttr.get()); + const OUString& rName = static_cast<SwFltBookmark*>(rEntry.pAttr.get())->GetName(); + + if (IsFlagSet(BOOK_TO_VAR_REF)) + { + SwFieldType* pFT = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rName, false); + if (!pFT) + { + SwSetExpFieldType aS(pDoc, rName, nsSwGetSetExpType::GSE_STRING); + pFT = pDoc->getIDocumentFieldsAccess().InsertFieldType(aS); + } + SwSetExpField aField(static_cast<SwSetExpFieldType*>(pFT), pB->GetValSys()); + aField.SetSubType( nsSwExtendedSubType::SUB_INVISIBLE ); + MakePoint(rEntry, pDoc, aRegion); + pDoc->getIDocumentContentOperations().InsertPoolItem(aRegion, SwFormatField(aField)); + MoveAttrs( *(aRegion.GetPoint()) ); + } + if ( ( !IsFlagSet(HYPO) || IsFlagSet(BOOK_AND_REF) ) && + !rEntry.bConsumedByField ) + { + MakeBookRegionOrPoint(rEntry, pDoc, aRegion); + // #i120879# - create a cross reference heading bookmark if appropriate. + const IDocumentMarkAccess::MarkType eBookmarkType = + ( pB->IsTOCBookmark() && + IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aRegion ) ) + ? IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK + : IDocumentMarkAccess::MarkType::BOOKMARK; + pDoc->getIDocumentMarkAccess()->makeMark(aRegion, rName, eBookmarkType, sw::mark::InsertMode::New); + } + } + break; + case RES_FLTR_ANNOTATIONMARK: + { + if (MakeBookRegionOrPoint(rEntry, pDoc, aRegion)) + { + SwTextNode const*const pTextNode( + aRegion.End()->nNode.GetNode().GetTextNode()); + assert(pTextNode); + SwTextField const*const pField(pTextNode->GetFieldTextAttrAt( + aRegion.End()->nContent.GetIndex() - 1, true)); + if (pField) + { + SwPostItField const*const pPostIt( + dynamic_cast<SwPostItField const*>(pField->GetFormatField().GetField())); + if (pPostIt) + { + assert(pPostIt->GetName().isEmpty()); + + if (!aRegion.HasMark()) + { + // Annotation range was found in the file, but start/end is the same, + // pointing after the postit placeholder (see assert above). + // Adjust the start of the range to actually cover the comment, similar + // to what the UI and the UNO API does. + aRegion.SetMark(); + --aRegion.Start()->nContent; + } + + pDoc->getIDocumentMarkAccess()->makeAnnotationMark(aRegion, OUString()); + } + else + { + SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: unexpected field"); + } + } + else + { + SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: missing field"); + } + } + else + SAL_WARN("sw", "failed to make book region or point"); + } + break; + case RES_FLTR_RDFMARK: + { + if (MakeBookRegionOrPoint(rEntry, pDoc, aRegion)) + { + SwFltRDFMark* pMark = static_cast<SwFltRDFMark*>(rEntry.pAttr.get()); + if (aRegion.GetNode().IsTextNode()) + { + SwTextNode& rTextNode = *aRegion.GetNode().GetTextNode(); + + for (const std::pair<OUString, OUString>& rAttribute : pMark->GetAttributes()) + { + OUString aTypeNS = rAttribute.first; + OUString aMetadataFilePath = lcl_getTypePath(aTypeNS); + if (aMetadataFilePath.isEmpty()) + continue; + + SwRDFHelper::addTextNodeStatement(aTypeNS, aMetadataFilePath, rTextNode, rAttribute.first, rAttribute.second); + } + } + } + else + SAL_WARN("sw", "failed to make book region or point"); + } + break; + case RES_FLTR_TOX: + { + MakePoint(rEntry, pDoc, aRegion); + + SwPosition* pPoint = aRegion.GetPoint(); + + SwFltTOX* pTOXAttr = static_cast<SwFltTOX*>(rEntry.pAttr.get()); + + // test if on this node there had been a pagebreak BEFORE the + // tox attribute was put on the stack + SfxItemSet aBkSet( pDoc->GetAttrPool(), svl::Items<RES_PAGEDESC, RES_BREAK>{} ); + SwContentNode* pNd = nullptr; + if( !pTOXAttr->HadBreakItem() || !pTOXAttr->HadPageDescItem() ) + { + pNd = pPoint->nNode.GetNode().GetContentNode(); + if( pNd ) + { + const SfxItemSet* pSet = pNd->GetpSwAttrSet(); + const SfxPoolItem* pItem; + if( pSet ) + { + if( !pTOXAttr->HadBreakItem() + && SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) ) + { + aBkSet.Put( *pItem ); + pNd->ResetAttr( RES_BREAK ); + } + if( !pTOXAttr->HadPageDescItem() + && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) ) + { + aBkSet.Put( *pItem ); + pNd->ResetAttr( RES_PAGEDESC ); + } + } + } + } + + // set (above saved and removed) the break item at the node following the TOX + if (pNd && aBkSet.Count()) + pNd->SetAttr(aBkSet); + } + break; + case RES_FLTR_REDLINE: + { + if (rEntry.MakeRegion(pDoc, aRegion, + SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark)) + { + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On + | RedlineFlags::ShowInsert + | RedlineFlags::ShowDelete ); + SwFltRedline& rFltRedline = *static_cast<SwFltRedline*>(rEntry.pAttr.get()); + + SwRedlineData aData(rFltRedline.eType, + rFltRedline.nAutorNo, + rFltRedline.aStamp, + OUString(), + nullptr + ); + pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(aData, aRegion), true ); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::NONE + | RedlineFlags::ShowInsert + | RedlineFlags::ShowDelete ); + } + } + break; + default: + { + // Revised for more complex situations should be considered + if ( !bSdODChecked ) + { + bHasSdOD = HasSdOD(); + bSdODChecked = true; + } + sal_Int32 nStart = rEntry.GetStartCP(); + sal_Int32 nEnd = rEntry.GetEndCP(); + if (nStart != -1 && nEnd != -1 && nEnd >= nStart ) + { + rEntry.SetIsParaEnd( IsParaEndInCPs(nStart,nEnd,bHasSdOD) ); + } + if (rEntry.MakeRegion(pDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck)) + { + if (rEntry.IsParaEnd()) + { + pDoc->getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.pAttr, SetAttrMode::DEFAULT, nullptr, true); + } + else + { + pDoc->getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.pAttr); + } + } + } + break; + } +} + +bool SwFltControlStack::IsParaEndInCPs(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/,bool /*bSdOD*/) const +{ + return false; +} + +bool SwFltControlStack::CheckSdOD(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/) +{ + return false; +} + +SfxPoolItem* SwFltControlStack::GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos) +{ + size_t nSize = m_Entries.size(); + + while (nSize) + { + // is it the looked-for attribute ? (only applies to locked, meaning + // currently set attributes!!) + SwFltStackEntry &rEntry = *m_Entries[--nSize]; + if (rEntry.bOpen && rEntry.pAttr->Which() == nWhich) + { + if (pPos) + *pPos = nSize; + return rEntry.pAttr.get(); // Ok, so stop + } + } + return nullptr; +} + +const SfxPoolItem* SwFltControlStack::GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich) +{ + SwFltPosition aFltPos(rPos); + + size_t nSize = m_Entries.size(); + + while (nSize) + { + SwFltStackEntry &rEntry = *m_Entries[--nSize]; + if (rEntry.bOpen && rEntry.pAttr->Which() == nWhich && rEntry.m_aMkPos == aFltPos) + { + return rEntry.pAttr.get(); + } + } + return nullptr; +} + +void SwFltControlStack::Delete(const SwPaM &rPam) +{ + const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); + + if( !rPam.HasMark() || *pStt >= *pEnd ) + return; + + SwNodeIndex aStartNode(pStt->nNode, -1); + const sal_Int32 nStartIdx = pStt->nContent.GetIndex(); + SwNodeIndex aEndNode(pEnd->nNode, -1); + const sal_Int32 nEndIdx = pEnd->nContent.GetIndex(); + + // We don't support deleting content that is over one node, or removing a node. + OSL_ENSURE(aEndNode == aStartNode, "nodes must be the same, or this method extended"); + if (aEndNode != aStartNode) + return; + + for (size_t nSize = m_Entries.size(); nSize > 0;) + { + SwFltStackEntry& rEntry = *m_Entries[--nSize]; + + bool bEntryStartAfterSelStart = + (rEntry.m_aMkPos.m_nNode == aStartNode && + rEntry.m_aMkPos.m_nContent >= nStartIdx); + + bool bEntryStartBeforeSelEnd = + (rEntry.m_aMkPos.m_nNode == aEndNode && + rEntry.m_aMkPos.m_nContent <= nEndIdx); + + bool bEntryEndAfterSelStart = false; + bool bEntryEndBeforeSelEnd = false; + if (!rEntry.bOpen) + { + bEntryEndAfterSelStart = + (rEntry.m_aPtPos.m_nNode == aStartNode && + rEntry.m_aPtPos.m_nContent >= nStartIdx); + + bEntryEndBeforeSelEnd = + (rEntry.m_aPtPos.m_nNode == aEndNode && + rEntry.m_aPtPos.m_nContent <= nEndIdx); + } + + bool bTotallyContained = false; + if ( + bEntryStartAfterSelStart && bEntryStartBeforeSelEnd && + bEntryEndAfterSelStart && bEntryEndBeforeSelEnd + ) + { + bTotallyContained = true; + } + + if (bTotallyContained) + { + // after start, before end, delete + DeleteAndDestroy(nSize); + continue; + } + + const sal_Int32 nContentDiff = nEndIdx - nStartIdx; + + // to be adjusted + if (bEntryStartAfterSelStart) + { + if (bEntryStartBeforeSelEnd) + { + // move start to new start + rEntry.m_aMkPos.SetPos(aStartNode, nStartIdx); + } + else + rEntry.m_aMkPos.m_nContent -= nContentDiff; + } + + if (bEntryEndAfterSelStart) + { + if (bEntryEndBeforeSelEnd) + rEntry.m_aPtPos.SetPos(aStartNode, nStartIdx); + else + rEntry.m_aPtPos.m_nContent -= nContentDiff; + } + + //That's what Open is, end equal to start, and nPtContent is invalid + if (rEntry.bOpen) + rEntry.m_aPtPos = rEntry.m_aMkPos; + } +} + +// methods of SwFltAnchor follow +SwFltAnchor::SwFltAnchor(SwFrameFormat* pFormat) : + SfxPoolItem(RES_FLTR_ANCHOR), pFrameFormat(pFormat) +{ + pListener.reset(new SwFltAnchorListener(this)); + pListener->StartListening(pFrameFormat->GetNotifier()); +} + +SwFltAnchor::SwFltAnchor(const SwFltAnchor& rCpy) : + SfxPoolItem(RES_FLTR_ANCHOR), pFrameFormat(rCpy.pFrameFormat) +{ + pListener.reset(new SwFltAnchorListener(this)); + pListener->StartListening(pFrameFormat->GetNotifier()); +} + +SwFltAnchor::~SwFltAnchor() +{ +} + +void SwFltAnchor::SetFrameFormat(SwFrameFormat * _pFrameFormat) +{ + pFrameFormat = _pFrameFormat; +} + + +bool SwFltAnchor::operator==(const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) && + pFrameFormat == static_cast<const SwFltAnchor&>(rItem).pFrameFormat; +} + +SwFltAnchor* SwFltAnchor::Clone(SfxItemPool*) const +{ + return new SwFltAnchor(*this); +} + +SwFltAnchorListener::SwFltAnchorListener(SwFltAnchor* pFltAnchor) + : m_pFltAnchor(pFltAnchor) +{ } + +void SwFltAnchorListener::Notify(const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + m_pFltAnchor->SetFrameFormat(nullptr); + else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint)) + { + if (pDrawFrameFormatHint->m_eId != sw::DrawFrameFormatHintId::DYING) + return; + m_pFltAnchor->SetFrameFormat(nullptr); + } + else if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) + { + if(pLegacyHint->m_pNew->Which() != RES_FMT_CHG) + return; + auto pFormatChg = dynamic_cast<const SwFormatChg*>(pLegacyHint->m_pNew); + auto pFrameFormat = pFormatChg ? dynamic_cast<SwFrameFormat*>(pFormatChg->pChangedFormat) : nullptr; + if(pFrameFormat) + m_pFltAnchor->SetFrameFormat(pFrameFormat); + } +} + +// methods of SwFltRedline follow +bool SwFltRedline::operator==(const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) && + this == &rItem; +} + +SwFltRedline* SwFltRedline::Clone( SfxItemPool* ) const +{ + return new SwFltRedline(*this); +} + +// methods of SwFltBookmark follow +SwFltBookmark::SwFltBookmark( const OUString& rNa, const OUString& rVa, + long nHand, const bool bIsTOCBookmark ) + : SfxPoolItem( RES_FLTR_BOOKMARK ) + , mnHandle( nHand ) + , maName( rNa ) + , maVal( rVa ) + , mbIsTOCBookmark( bIsTOCBookmark ) +{ + // eSrc: CHARSET_DONTKNOW for no transform at operator << + // Upcase is always done. + // Transform is never done at XXXStack.NewAttr(...). + // otherwise: Src Charset from argument for aName + // Src Charset from filter for aVal ( Text ) + + if ( IsTOCBookmark() && ! rNa.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) ) + { + maName = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix(); + maName += rNa; + } +} + +bool SwFltBookmark::operator==(const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) + && maName == static_cast<const SwFltBookmark&>(rItem).maName + && mnHandle == static_cast<const SwFltBookmark&>(rItem).mnHandle; +} + +SwFltBookmark* SwFltBookmark::Clone(SfxItemPool*) const +{ + return new SwFltBookmark(*this); +} + +SwFltRDFMark::SwFltRDFMark() + : SfxPoolItem(RES_FLTR_RDFMARK), + m_nHandle(0) +{ +} + +bool SwFltRDFMark::operator==(const SfxPoolItem& rItem) const +{ + if (!SfxPoolItem::operator==(rItem)) + return false; + + const SwFltRDFMark& rMark = static_cast<const SwFltRDFMark&>(rItem); + + return m_nHandle == rMark.m_nHandle && m_aAttributes == rMark.m_aAttributes; +} + +SwFltRDFMark* SwFltRDFMark::Clone(SfxItemPool*) const +{ + return new SwFltRDFMark(*this); +} + +void SwFltRDFMark::SetHandle(long nHandle) +{ + m_nHandle = nHandle; +} + +long SwFltRDFMark::GetHandle() const +{ + return m_nHandle; +} + +void SwFltRDFMark::SetAttributes(const std::vector< std::pair<OUString, OUString> >& rAttributes) +{ + m_aAttributes = rAttributes; +} + +const std::vector< std::pair<OUString, OUString> >& SwFltRDFMark::GetAttributes() const +{ + return m_aAttributes; +} + +// methods of SwFltTOX follow +SwFltTOX::SwFltTOX(std::shared_ptr<SwTOXBase> xBase) + : SfxPoolItem(RES_FLTR_TOX), m_xTOXBase(std::move(xBase)), + bHadBreakItem( false ), bHadPageDescItem( false ) +{ +} + +bool SwFltTOX::operator==(const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) && + m_xTOXBase.get() == static_cast<const SwFltTOX&>(rItem).m_xTOXBase.get(); +} + +SwFltTOX* SwFltTOX::Clone(SfxItemPool*) const +{ + return new SwFltTOX(*this); +} + +// UpdatePageDescs needs to be called at end of parsing to make Writer actually +// accept Pagedescs contents +void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset) +{ + // Update document page descriptors (only this way also left pages + // get adjusted) + + // PageDesc "Standard" + rDoc.ChgPageDesc(0, rDoc.GetPageDesc(0)); + + // PageDescs "Convert..." + for (size_t i = nInPageDescOffset; i < rDoc.GetPageDescCnt(); ++i) + rDoc.ChgPageDesc(i, rDoc.GetPageDesc(i)); +} + +FrameDeleteWatch::FrameDeleteWatch(SwFrameFormat* pFormat) + : m_pFormat(pFormat) +{ + if(m_pFormat) + StartListening(pFormat->GetNotifier()); +} + +void FrameDeleteWatch::Notify(const SfxHint& rHint) +{ + bool bDying = false; + if (rHint.GetId() == SfxHintId::Dying) + bDying = true; + else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint)) + bDying = pDrawFrameFormatHint->m_eId == sw::DrawFrameFormatHintId::DYING; + if (bDying) + { + m_pFormat = nullptr; + EndListeningAll(); + } +} + +FrameDeleteWatch::~FrameDeleteWatch() +{ + m_pFormat = nullptr; + EndListeningAll(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/basflt/iodetect.cxx b/sw/source/filter/basflt/iodetect.cxx new file mode 100644 index 000000000..5f9929e8f --- /dev/null +++ b/sw/source/filter/basflt/iodetect.cxx @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <iodetect.hxx> +#include <memory> +#include <osl/endian.h> +#include <sot/storage.hxx> +#include <tools/urlobj.hxx> +#include <unotools/moduleoptions.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfile.hxx> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/embed/XStorage.hpp> + +using namespace ::com::sun::star; + +static bool IsDocShellRegistered() +{ + return SvtModuleOptions().IsWriter(); +} + +SwIoDetect aFilterDetect[] = +{ + SwIoDetect( FILTER_RTF ), + SwIoDetect( FILTER_BAS ), + SwIoDetect( sWW6 ), + SwIoDetect( FILTER_WW8 ), + SwIoDetect( sRtfWH ), + SwIoDetect( sHTML ), + SwIoDetect( sWW5 ), + SwIoDetect( FILTER_XML ), + SwIoDetect( FILTER_TEXT_DLG ), + SwIoDetect( FILTER_TEXT ), + SwIoDetect( FILTER_DOCX ) +}; + +OUString SwIoSystem::GetSubStorageName( const SfxFilter& rFltr ) +{ + // for StorageFilters also set the SubStorageName + const OUString& rUserData = rFltr.GetUserData(); + if (rUserData == FILTER_XML || + rUserData == FILTER_XMLV || + rUserData == FILTER_XMLVW) + return "content.xml"; + if (rUserData == sWW6 || rUserData == FILTER_WW8) + return "WordDocument"; + return OUString(); +} + +std::shared_ptr<const SfxFilter> SwIoSystem::GetFilterOfFormat(const OUString& rFormatNm, + const SfxFilterContainer* pCnt) +{ + SfxFilterContainer aCntSw( sSWRITER ); + SfxFilterContainer aCntSwWeb( sSWRITERWEB ); + const SfxFilterContainer* pFltCnt = pCnt ? pCnt : ( IsDocShellRegistered() ? &aCntSw : &aCntSwWeb ); + + do { + if( pFltCnt ) + { + SfxFilterMatcher aMatcher( pFltCnt->GetName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFilter = aIter.First(); + while ( pFilter ) + { + if( pFilter->GetUserData() == rFormatNm ) + return pFilter; + pFilter = aIter.Next(); + } + } + if( pCnt || pFltCnt == &aCntSwWeb ) + break; + pFltCnt = &aCntSwWeb; + } while( true ); + return nullptr; +} + +bool SwIoSystem::IsValidStgFilter( const css::uno::Reference < css::embed::XStorage >& rStg, const SfxFilter& rFilter) +{ + bool bRet = false; + try + { + SotClipboardFormatId nStgFormatId = SotStorage::GetFormatID( rStg ); + bRet = rStg->isStreamElement( "content.xml" ); + if ( bRet ) + bRet = ( nStgFormatId != SotClipboardFormatId::NONE && ( rFilter.GetFormat() == nStgFormatId ) ); + } + catch (const css::uno::Exception& ) + { + } + + return bRet; +} + +bool SwIoSystem::IsValidStgFilter(SotStorage& rStg, const SfxFilter& rFilter) +{ + SotClipboardFormatId nStgFormatId = rStg.GetFormat(); + /*#i8409# We cannot trust the clipboard id anymore :-(*/ + if (rFilter.GetUserData() == FILTER_WW8 || rFilter.GetUserData() == sWW6) + nStgFormatId = SotClipboardFormatId::NONE; + + bool bRet = ERRCODE_NONE == rStg.GetError() && + ( nStgFormatId == SotClipboardFormatId::NONE || rFilter.GetFormat() == nStgFormatId ) && + ( rStg.IsContained( SwIoSystem::GetSubStorageName( rFilter )) ); + if( bRet ) + { + /* Bug 53445 - there are Excel Docs w/o ClipBoardId! */ + /* Bug 62703 - and also WinWord Docs w/o ClipBoardId! */ + if (rFilter.GetUserData() == FILTER_WW8 || rFilter.GetUserData() == sWW6) + { + bRet = (rStg.IsContained("0Table") + || rStg.IsContained("1Table")) + == (rFilter.GetUserData() == FILTER_WW8); + if (bRet && !rFilter.IsAllowedAsTemplate()) + { + tools::SvRef<SotStorageStream> xRef = + rStg.OpenSotStream("WordDocument", + StreamMode::STD_READ ); + xRef->Seek(10); + sal_uInt8 nByte; + xRef->ReadUChar( nByte ); + bRet = !(nByte & 1); + } + } + } + return bRet; +} + +// Check the type of the stream (file) by searching for corresponding set of bytes. +// If no known type is found, return ASCII for now! +// Returns the internal FilterName. +std::shared_ptr<const SfxFilter> SwIoSystem::GetFileFilter(const OUString& rFileName) +{ + SfxFilterContainer aCntSw( sSWRITER ); + SfxFilterContainer aCntSwWeb( sSWRITERWEB ); + const SfxFilterContainer* pFCntnr = IsDocShellRegistered() ? &aCntSw : &aCntSwWeb; + + SfxFilterMatcher aMatcher( pFCntnr->GetName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFilter = aIter.First(); + if ( !pFilter ) + return nullptr; + + if (SotStorage::IsStorageFile(rFileName)) + { + // package storage or OLEStorage based format + tools::SvRef<SotStorage> xStg; + INetURLObject aObj; + aObj.SetSmartProtocol( INetProtocol::File ); + aObj.SetSmartURL( rFileName ); + SfxMedium aMedium(aObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::STD_READ); + + // templates should not get precedence over "normal" filters (#i35508, #i33168) + std::shared_ptr<const SfxFilter> pTemplateFilter; + if (aMedium.IsStorage()) + { + uno::Reference<embed::XStorage> const xStor = aMedium.GetStorage(); + if ( xStor.is() ) + { + while ( pFilter ) + { + if (pFilter->GetUserData().startsWith("C") && IsValidStgFilter(xStor, *pFilter )) + { + if (pFilter->IsOwnTemplateFormat()) + { + // found template filter; maybe there's a "normal" one also + pTemplateFilter = pFilter; + } + else + return pFilter; + } + + pFilter = aIter.Next(); + } + + // there's only a template filter that could be found + if ( pTemplateFilter ) + pFilter = pTemplateFilter; + } + } + else + { + try + { + SvStream *const pStream = aMedium.GetInStream(); + if ( pStream && SotStorage::IsStorageFile(pStream) ) + xStg = new SotStorage( pStream, false ); + } + catch (const css::ucb::ContentCreationException &) + { + } + + if( xStg.is() && ( xStg->GetError() == ERRCODE_NONE ) ) + { + while ( pFilter ) + { + if (pFilter->GetUserData().startsWith("C") && IsValidStgFilter(*xStg, *pFilter)) + { + if (pFilter->IsOwnTemplateFormat()) + { + // found template filter; maybe there's a "normal" one also + pTemplateFilter = pFilter; + } + else + return pFilter; + } + + pFilter = aIter.Next(); + } + + // there's only a template filter that could be found + if ( pTemplateFilter ) + pFilter = pTemplateFilter; + + } + } + + return pFilter; + } + + return SwIoSystem::GetFilterOfFormat(FILTER_TEXT); +} + +bool SwIoSystem::IsDetectableText(const char* pBuf, sal_uLong &rLen, + rtl_TextEncoding *pCharSet, bool *pSwap, LineEnd *pLineEnd) +{ + bool bSwap = false; + rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW; + bool bLE = true; + /*See if it's a known unicode type*/ + if (rLen >= 2) + { + sal_uLong nHead=0; + if (rLen > 2 && sal_uInt8(pBuf[0]) == 0xEF && sal_uInt8(pBuf[1]) == 0xBB && + sal_uInt8(pBuf[2]) == 0xBF) + { + eCharSet = RTL_TEXTENCODING_UTF8; + nHead = 3; + } + else if (sal_uInt8(pBuf[0]) == 0xFE && sal_uInt8(pBuf[1]) == 0xFF) + { + eCharSet = RTL_TEXTENCODING_UCS2; + bLE = false; + nHead = 2; + } + else if (sal_uInt8(pBuf[1]) == 0xFE && sal_uInt8(pBuf[0]) == 0xFF) + { + eCharSet = RTL_TEXTENCODING_UCS2; + nHead = 2; + } + pBuf+=nHead; + rLen-=nHead; + } + + bool bCR = false, bLF = false, bIsBareUnicode = false; + + if (eCharSet != RTL_TEXTENCODING_DONTKNOW) + { + std::unique_ptr<sal_Unicode[]> aWork(new sal_Unicode[rLen+1]); + sal_Unicode *pNewBuf = aWork.get(); + std::size_t nNewLen; + if (eCharSet != RTL_TEXTENCODING_UCS2) + { + nNewLen = rLen; + rtl_TextToUnicodeConverter hConverter = + rtl_createTextToUnicodeConverter(eCharSet); + rtl_TextToUnicodeContext hContext = + rtl_createTextToUnicodeContext(hConverter); + + sal_Size nCntBytes; + sal_uInt32 nInfo; + nNewLen = rtl_convertTextToUnicode( hConverter, hContext, pBuf, + rLen, pNewBuf, nNewLen, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT), &nInfo, &nCntBytes); + + rtl_destroyTextToUnicodeContext(hConverter, hContext); + rtl_destroyTextToUnicodeConverter(hConverter); + } + else + { + nNewLen = rLen/2; + memcpy(pNewBuf, pBuf, rLen); +#ifdef OSL_LITENDIAN + bool const bNativeLE = true; +#else + bool const bNativeLE = false; +#endif + if (bLE != bNativeLE) + { + bSwap = true; + char* pF = reinterpret_cast<char*>(pNewBuf); + char* pN = pF+1; + for(sal_uLong n = 0; n < nNewLen; ++n, pF+=2, pN+=2 ) + { + char c = *pF; + *pF = *pN; + *pN = c; + } + } + } + + for (sal_uLong nCnt = 0; nCnt < nNewLen; ++nCnt, ++pNewBuf) + { + switch (*pNewBuf) + { + case 0xA: + bLF = true; + break; + case 0xD: + bCR = true; + break; + default: + break; + } + } + } + else + { + for( sal_uLong nCnt = 0; nCnt < rLen; ++nCnt, ++pBuf ) + { + switch (*pBuf) + { + case 0x0: + if( nCnt + 1 < rLen && !*(pBuf+1) ) + return false; + bIsBareUnicode = true; + break; + case 0xA: + bLF = true; + break; + case 0xD: + bCR = true; + break; + case 0xC: + case 0x1A: + case 0x9: + break; + default: + break; + } + } + } + + LineEnd eSysLE = GetSystemLineEnd(); + LineEnd eLineEnd; + if (!bCR && !bLF) + eLineEnd = eSysLE; + else + eLineEnd = bCR ? ( bLF ? LINEEND_CRLF : LINEEND_CR ) : LINEEND_LF; + + if (pCharSet) + *pCharSet = eCharSet; + if (pSwap) + *pSwap = bSwap; + if (pLineEnd) + *pLineEnd = eLineEnd; + + return !bIsBareUnicode; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/basflt/shellio.cxx b/sw/source/filter/basflt/shellio.cxx new file mode 100644 index 000000000..74b314277 --- /dev/null +++ b/sw/source/filter/basflt/shellio.cxx @@ -0,0 +1,927 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/date.hxx> +#include <tools/time.hxx> +#include <svl/fstathelper.hxx> +#include <unotools/moduleoptions.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/paperinf.hxx> +#include <node.hxx> +#include <docary.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtpdsc.hxx> +#include <shellio.hxx> +#include <iodetect.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pam.hxx> +#include <editsh.hxx> +#include <undobj.hxx> +#include <swundo.hxx> +#include <swtable.hxx> +#include <tblafmt.hxx> +#include <tblsel.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> +#include <fltini.hxx> +#include <docsh.hxx> +#include <ndole.hxx> +#include <ndtxt.hxx> +#include <redline.hxx> +#include <swerror.h> +#include <pausethreadstarting.hxx> +#include <frameformats.hxx> + +using namespace ::com::sun::star; + +static bool sw_MergePortions(SwNode *const& pNode, void *) +{ + if (pNode->IsTextNode()) + { + pNode->GetTextNode()->FileLoadedInitHints(); + } + return true; +} + +ErrCode SwReader::Read( const Reader& rOptions ) +{ + // copy variables + Reader* po = const_cast<Reader*>(&rOptions); + po->m_pStream = mpStrm; + po->m_pStorage = mpStg; + po->m_xStorage = mxStg; + po->m_bInsertMode = nullptr != mpCursor; + po->m_bSkipImages = mbSkipImages; + + // if a Medium is selected, get its Stream + if( nullptr != (po->m_pMedium = mpMedium ) && + !po->SetStrmStgPtr() ) + { + po->SetReadUTF8( false ); + po->SetBlockMode( false ); + po->SetOrganizerMode( false ); + po->SetIgnoreHTMLComments( false ); + return ERR_SWG_FILE_FORMAT_ERROR; + } + + ErrCode nError = ERRCODE_NONE; + + GetDoc(); + + // while reading, do not call OLE-Modified + Link<bool,void> aOLELink( mxDoc->GetOle2Link() ); + mxDoc->SetOle2Link( Link<bool,void>() ); + + mxDoc->SetInReading( true ); + mxDoc->SetInXMLImport( dynamic_cast< XMLReader* >(po) != nullptr ); + mxDoc->SetInWriterfilterImport(mpMedium && mpMedium->GetFilter() + && (mpMedium->GetFilter()->GetUserData() == FILTER_RTF + || mpMedium->GetFilter()->GetUserData() == sRtfWH + || mpMedium->GetFilter()->GetUserData() == FILTER_DOCX)); + + SwPaM *pPam; + if( mpCursor ) + pPam = mpCursor; + else + { + // if the Reader was not called by a Shell, create a PaM ourselves + SwNodeIndex nNode( mxDoc->GetNodes().GetEndOfContent(), -1 ); + pPam = new SwPaM( nNode ); + // For Web documents the default template was set already by InitNew, + // unless the filter is not HTML, + // or a SetTemplateName was called in ConvertFrom. + if( !mxDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) || ReadHTML != po || !po->mxTemplate.is() ) + po->SetTemplate( *mxDoc ); + } + + // Pams are connected like rings; stop when we return to the 1st element + SwPaM *pEnd = pPam; + + bool bReadPageDescs = false; + bool const bDocUndo = mxDoc->GetIDocumentUndoRedo().DoesUndo(); + bool bSaveUndo = bDocUndo && mpCursor; + if( bSaveUndo ) + { + // the reading of the page template cannot be undone! + bReadPageDescs = po->m_aOption.IsPageDescs(); + if( bReadPageDescs ) + { + bSaveUndo = false; + mxDoc->GetIDocumentUndoRedo().DelAllUndoObj(); + } + else + { + mxDoc->GetIDocumentUndoRedo().ClearRedo(); + mxDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSDOKUMENT, nullptr ); + } + } + mxDoc->GetIDocumentUndoRedo().DoUndo(false); + + RedlineFlags eOld = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + RedlineFlags ePostReadRedlineFlags( RedlineFlags::Ignore ); + + // Array of FlyFormats + SwFrameFormatsV aFlyFrameArr; + // only read templates? then ignore multi selection! + bool bFormatsOnly = po->m_aOption.IsFormatsOnly(); + + while( true ) + { + std::unique_ptr<SwUndoInsDoc> pUndo; + if( bSaveUndo ) + pUndo.reset(new SwUndoInsDoc( *pPam )); + + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + + std::unique_ptr<SwPaM> pUndoPam; + if( bDocUndo || mpCursor ) + { + // set Pam to the previous node, so that it is not also moved + const SwNodeIndex& rTmp = pPam->GetPoint()->nNode; + pUndoPam.reset(new SwPaM( rTmp, rTmp, 0, -1 )); + } + + // store for now all Fly's + if( mpCursor ) + { + std::copy(mxDoc->GetSpzFrameFormats()->begin(), + mxDoc->GetSpzFrameFormats()->end(), std::back_inserter(aFlyFrameArr)); + } + + const sal_Int32 nSttContent = pPam->GetPoint()->nContent.GetIndex(); + + // make sure the End position is correct for all Readers + SwContentNode* pCNd = pPam->GetContentNode(); + sal_Int32 nEndContent = pCNd ? pCNd->Len() - nSttContent : 0; + SwNodeIndex aEndPos( pPam->GetPoint()->nNode, 1 ); + + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + + nError = po->Read( *mxDoc, msBaseURL, *pPam, maFileName ); + + // an ODF document may contain redline mode in settings.xml; save it! + ePostReadRedlineFlags = mxDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + + if( ! nError.IsError() ) // set the End position already + { + --aEndPos; + pCNd = aEndPos.GetNode().GetContentNode(); + if( !pCNd && nullptr == ( pCNd = SwNodes::GoPrevious( &aEndPos ) )) + pCNd = mxDoc->GetNodes().GoNext( &aEndPos ); + + pPam->GetPoint()->nNode = aEndPos; + const sal_Int32 nLen = pCNd->Len(); + if( nLen < nEndContent ) + nEndContent = 0; + else + nEndContent = nLen - nEndContent; + pPam->GetPoint()->nContent.Assign( pCNd, nEndContent ); + + const SwStartNode* pTableBoxStart = pCNd->FindTableBoxStartNode(); + if ( pTableBoxStart ) + { + SwTableBox* pBox = pTableBoxStart->GetTableBox(); + if ( pBox ) + { + mxDoc->ChkBoxNumFormat( *pBox, true ); + } + } + } + + if( mpCursor ) + { + *pUndoPam->GetMark() = *pPam->GetPoint(); + ++pUndoPam->GetPoint()->nNode; + SwNode& rNd = pUndoPam->GetNode(); + if( rNd.IsContentNode() ) + pUndoPam->GetPoint()->nContent.Assign( + static_cast<SwContentNode*>(&rNd), nSttContent ); + else + pUndoPam->GetPoint()->nContent.Assign( nullptr, 0 ); + + bool bChkHeaderFooter = rNd.FindHeaderStartNode() || + rNd.FindFooterStartNode(); + + // search all new Fly's, and store them as individual Undo Objects + for( SwFrameFormats::size_type n = 0; n < mxDoc->GetSpzFrameFormats()->size(); ++n ) + { + SwFrameFormat* pFrameFormat = (*mxDoc->GetSpzFrameFormats())[ n ]; + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + // ok, here IsAlive is a misnomer... + if (!aFlyFrameArr.IsAlive(pFrameFormat)) + { + if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) + // TODO: why is this not handled via SetInsertRange? + || SwUndoInserts::IsCreateUndoForNewFly(rAnchor, + pUndoPam->GetPoint()->nNode.GetIndex(), + pUndoPam->GetMark()->nNode.GetIndex())) + { + if( bChkHeaderFooter && + (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) && + RES_DRAWFRMFMT == pFrameFormat->Which() ) + { + // DrawObjects are not allowed in Headers/Footers! + pFrameFormat->DelFrames(); + mxDoc->DelFrameFormat( pFrameFormat ); + --n; + } + else + { + if( bSaveUndo ) + { + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + // UGLY: temp. enable undo + mxDoc->GetIDocumentUndoRedo().DoUndo(true); + mxDoc->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoInsLayFormat>( pFrameFormat,0,0 ) ); + mxDoc->GetIDocumentUndoRedo().DoUndo(false); + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + } + if( pFrameFormat->HasWriterListeners() ) + { + // Draw-Objects create a Frame when being inserted; thus delete them + pFrameFormat->DelFrames(); + } + + if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) + { + if( !rAnchor.GetContentAnchor() ) + { + pFrameFormat->MakeFrames(); + } + else if( mpCursor ) + { + mxDoc->SetContainsAtPageObjWithContentAnchor( true ); + } + } + else + pFrameFormat->MakeFrames(); + } + } + } + } + aFlyFrameArr.clear(); + + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + if( mxDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + mxDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pUndoPam ), true); + else + mxDoc->getIDocumentRedlineAccess().SplitRedline( *pUndoPam ); + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + } + if( bSaveUndo ) + { + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + pUndo->SetInsertRange( *pUndoPam, false ); + // UGLY: temp. enable undo + mxDoc->GetIDocumentUndoRedo().DoUndo(true); + mxDoc->GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) ); + mxDoc->GetIDocumentUndoRedo().DoUndo(false); + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + } + + pUndoPam.reset(); + + pPam = pPam->GetNext(); + if( pPam == pEnd ) + break; + + // only read templates? then ignore multi selection! Bug 68593 + if( bFormatsOnly ) + break; + + /* + * !!! The Status of the Stream has to be reset directly. !!! + * When Seeking, the current Status-, EOF- and bad-Bit is set; + * nobody knows why + */ + if( mpStrm ) + { + mpStrm->Seek(0); + mpStrm->ResetError(); + } + } + + // fdo#52028: ODF file import does not result in MergePortions being called + // for every attribute, since that would be inefficient. So call it here. + // This is only necessary for formats that may contain RSIDs (ODF,MSO). + // It's too hard to figure out which nodes were inserted in Insert->File + // case (redlines, flys, footnotes, header/footer) so just do every node. + mxDoc->GetNodes().ForEach(&sw_MergePortions); + + mxDoc->SetInReading( false ); + mxDoc->SetInXMLImport( false ); + mxDoc->SetInWriterfilterImport(false); + + mxDoc->InvalidateNumRules(); + mxDoc->UpdateNumRule(); + mxDoc->ChkCondColls(); + mxDoc->SetAllUniqueFlyNames(); + // Clear unassigned cell styles, because they aren't needed anymore. + mxDoc->GetCellStyles().clear(); + + mxDoc->GetIDocumentUndoRedo().DoUndo(bDocUndo); + if (!bReadPageDescs && bSaveUndo ) + { + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + mxDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSDOKUMENT, nullptr ); + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::Ignore ); + } + + // delete Pam if it was created only for reading + if( !mpCursor ) + { + delete pPam; // open a new one + + // #i42634# Moved common code of SwReader::Read() and + // SwDocShell::UpdateLinks() to new SwDoc::UpdateLinks(): + // ATM still with Update + mxDoc->getIDocumentLinksAdministration().UpdateLinks(); + + // not insert: set the redline mode read from settings.xml + eOld = ePostReadRedlineFlags & ~RedlineFlags::Ignore; + + mxDoc->getIDocumentFieldsAccess().SetFieldsDirty(false, nullptr, 0); + } + + mxDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); + mxDoc->SetOle2Link( aOLELink ); + + if( mpCursor ) // the document is now modified + mxDoc->getIDocumentState().SetModified(); + // #i38810# - If links have been updated, the document + // have to be modified. During update of links the OLE link at the document + // isn't set. Thus, the document's modified state has to be set again after + // the OLE link is restored - see above <mxDoc->SetOle2Link( aOLELink )>. + if ( mxDoc->getIDocumentLinksAdministration().LinksUpdated() ) + { + mxDoc->getIDocumentState().SetModified(); + } + + po->SetReadUTF8( false ); + po->SetBlockMode( false ); + po->SetOrganizerMode( false ); + po->SetIgnoreHTMLComments( false ); + + return nError; +} + + +SwReader::SwReader(SfxMedium& rMedium, const OUString& rFileName, SwDoc *pDocument) + : SwDocFac(pDocument), mpStrm(nullptr), mpMedium(&rMedium), mpCursor(nullptr), + maFileName(rFileName), mbSkipImages(false) +{ + SetBaseURL( rMedium.GetBaseURL() ); + SetSkipImages( rMedium.IsSkipImages() ); +} + + +// Read into an existing document +SwReader::SwReader(SvStream& rStrm, const OUString& rFileName, const OUString& rBaseURL, SwPaM& rPam) + : SwDocFac(rPam.GetDoc()), mpStrm(&rStrm), mpMedium(nullptr), mpCursor(&rPam), + maFileName(rFileName), mbSkipImages(false) +{ + SetBaseURL( rBaseURL ); +} + +SwReader::SwReader(SfxMedium& rMedium, const OUString& rFileName, SwPaM& rPam) + : SwDocFac(rPam.GetDoc()), mpStrm(nullptr), mpMedium(&rMedium), + mpCursor(&rPam), maFileName(rFileName), mbSkipImages(false) +{ + SetBaseURL( rMedium.GetBaseURL() ); +} + +SwReader::SwReader( const uno::Reference < embed::XStorage > &rStg, const OUString& rFilename, SwPaM &rPam ) + : SwDocFac(rPam.GetDoc()), mpStrm(nullptr), mxStg( rStg ), mpMedium(nullptr), mpCursor(&rPam), maFileName(rFilename), mbSkipImages(false) +{ +} + +Reader::Reader() + : m_aDateStamp( Date::EMPTY ), + m_aTimeStamp( tools::Time::EMPTY ), + m_aCheckDateTime( DateTime::EMPTY ), + m_pStream(nullptr), m_pMedium(nullptr), m_bInsertMode(false), + m_bTemplateBrowseMode(false), m_bReadUTF8(false), m_bBlockMode(false), m_bOrganizerMode(false), + m_bHasAskTemplateName(false), m_bIgnoreHTMLComments(false), m_bSkipImages(false) +{ +} + +Reader::~Reader() +{ +} + +OUString Reader::GetTemplateName(SwDoc& /*rDoc*/) const +{ + return OUString(); +} + +// load the Filter template, set and release +SwDoc* Reader::GetTemplateDoc(SwDoc& rDoc) +{ + if( !m_bHasAskTemplateName ) + { + SetTemplateName( GetTemplateName(rDoc) ); + m_bHasAskTemplateName = true; + } + + if( m_aTemplateName.isEmpty() ) + ClearTemplate(); + else + { + INetURLObject aTDir( m_aTemplateName ); + const OUString aFileName = aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + OSL_ENSURE( !aTDir.HasError(), "No absolute path for template name!" ); + DateTime aCurrDateTime( DateTime::SYSTEM ); + bool bLoad = false; + + // if the template is already loaded, check once-a-minute if it has changed + if( !mxTemplate.is() || aCurrDateTime >= m_aCheckDateTime ) + { + Date aTstDate( Date::EMPTY ); + tools::Time aTstTime( tools::Time::EMPTY ); + if( FStatHelper::GetModifiedDateTimeOfFile( + aTDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + &aTstDate, &aTstTime ) && + ( !mxTemplate.is() || m_aDateStamp != aTstDate || m_aTimeStamp != aTstTime )) + { + bLoad = true; + m_aDateStamp = aTstDate; + m_aTimeStamp = aTstTime; + } + + // only one minute later check if it has changed + m_aCheckDateTime = aCurrDateTime; + m_aCheckDateTime += tools::Time( 0, 1 ); + } + + if (bLoad) + { + ClearTemplate(); + OSL_ENSURE( !mxTemplate.is(), "Who holds the template doc?" ); + + // If the writer module is not installed, + // we cannot create a SwDocShell. We could create a + // SwWebDocShell however, because this exists always + // for the help. + SvtModuleOptions aModuleOptions; + if (aModuleOptions.IsWriter()) + { + SwDocShell *pDocSh = new SwDocShell(SfxObjectCreateMode::INTERNAL); + SfxObjectShellLock xDocSh = pDocSh; + if (pDocSh->DoInitNew()) + { + mxTemplate = pDocSh->GetDoc(); + mxTemplate->SetOle2Link( Link<bool,void>() ); + // always FALSE + mxTemplate->GetIDocumentUndoRedo().DoUndo( false ); + mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode ); + mxTemplate->RemoveAllFormatLanguageDependencies(); + + ReadXML->SetOrganizerMode( true ); + SfxMedium aMedium( aFileName, StreamMode::NONE ); + SwReader aRdr( aMedium, OUString(), mxTemplate.get() ); + aRdr.Read( *ReadXML ); + ReadXML->SetOrganizerMode( false ); + } + } + } + + OSL_ENSURE( !mxTemplate.is() || FStatHelper::IsDocument( aFileName ) || m_aTemplateName=="$$Dummy$$", + "TemplatePtr but no template exist!" ); + } + + return mxTemplate.get(); +} + +bool Reader::SetTemplate( SwDoc& rDoc ) +{ + bool bRet = false; + + GetTemplateDoc(rDoc); + if( mxTemplate.is() ) + { + rDoc.RemoveAllFormatLanguageDependencies(); + rDoc.ReplaceStyles( *mxTemplate ); + rDoc.getIDocumentFieldsAccess().SetFixFields(nullptr); + bRet = true; + } + + return bRet; +} + +void Reader::ClearTemplate() +{ + mxTemplate.clear(); +} + +void Reader::SetTemplateName( const OUString& rDir ) +{ + if( !rDir.isEmpty() && m_aTemplateName != rDir ) + { + ClearTemplate(); + m_aTemplateName = rDir; + } +} + +void Reader::MakeHTMLDummyTemplateDoc() +{ + ClearTemplate(); + mxTemplate = new SwDoc; + mxTemplate->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, m_bTemplateBrowseMode ); + mxTemplate->getIDocumentDeviceAccess().getPrinter( true ); + mxTemplate->RemoveAllFormatLanguageDependencies(); + m_aCheckDateTime = Date( 1, 1, 2300 ); // year 2300 should be sufficient + m_aTemplateName = "$$Dummy$$"; +} + +// Users that do not need to open these Streams / Storages, +// have to override this method +bool Reader::SetStrmStgPtr() +{ + OSL_ENSURE( m_pMedium, "Where is the Media??" ); + + if( m_pMedium->IsStorage() ) + { + if( SwReaderType::Storage & GetReaderType() ) + { + m_xStorage = m_pMedium->GetStorage(); + return true; + } + } + else + { + m_pStream = m_pMedium->GetInStream(); + if ( m_pStream && SotStorage::IsStorageFile(m_pStream) && (SwReaderType::Storage & GetReaderType()) ) + { + m_pStorage = new SotStorage( *m_pStream ); + m_pStream = nullptr; + } + else if ( !(SwReaderType::Stream & GetReaderType()) ) + { + m_pStream = nullptr; + return false; + } + + return true; + } + return false; +} + +SwReaderType Reader::GetReaderType() +{ + return SwReaderType::Stream; +} + +void Reader::SetFltName( const OUString& ) +{ +} + +void Reader::ResetFrameFormatAttrs( SfxItemSet &rFrameSet ) +{ + rFrameSet.Put( SvxLRSpaceItem(RES_LR_SPACE) ); + rFrameSet.Put( SvxULSpaceItem(RES_UL_SPACE) ); + rFrameSet.Put( SvxBoxItem(RES_BOX) ); +} + +void Reader::ResetFrameFormats( SwDoc& rDoc ) +{ + sal_uInt16 const s_ids[3] = { + RES_POOLFRM_FRAME, RES_POOLFRM_GRAPHIC, RES_POOLFRM_OLE + }; + for (sal_uInt16 i : s_ids) + { + SwFrameFormat *const pFrameFormat = rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( i ); + + pFrameFormat->ResetFormatAttr( RES_LR_SPACE ); + pFrameFormat->ResetFormatAttr( RES_UL_SPACE ); + pFrameFormat->ResetFormatAttr( RES_BOX ); + } +} + + // read the sections of the document, which is equal to the medium. + // returns the count of it +size_t Reader::GetSectionList(SfxMedium&, std::vector<OUString>&) const +{ + return 0; +} + +bool SwReader::HasGlossaries( const Reader& rOptions ) +{ + // copy variables + Reader* po = const_cast<Reader*>(&rOptions); + po->m_pStream = mpStrm; + po->m_pStorage = mpStg; + po->m_bInsertMode = false; + + // if a Medium is selected, get its Stream + bool bRet = false; + if( nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() ) + bRet = po->HasGlossaries(); + return bRet; +} + +bool SwReader::ReadGlossaries( const Reader& rOptions, + SwTextBlocks& rBlocks, bool bSaveRelFiles ) +{ + // copy variables + Reader* po = const_cast<Reader*>(&rOptions); + po->m_pStream = mpStrm; + po->m_pStorage = mpStg; + po->m_bInsertMode = false; + + // if a Medium is selected, get its Stream + bool bRet = false; + if( nullptr == (po->m_pMedium = mpMedium ) || po->SetStrmStgPtr() ) + bRet = po->ReadGlossaries( rBlocks, bSaveRelFiles ); + return bRet; +} + +bool Reader::HasGlossaries() const +{ + return false; +} + +bool Reader::ReadGlossaries( SwTextBlocks&, bool ) const +{ + return false; +} + +SwReaderType StgReader::GetReaderType() +{ + return SwReaderType::Storage; +} + +/* + * Writer + */ + +/* + * Constructors, Destructors are inline (inc/shellio.hxx). + */ + +SwWriter::SwWriter(SvStream& rStrm, SwCursorShell &rShell, bool bInWriteAll) + : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(&rShell), + m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll) +{ +} + +SwWriter::SwWriter(SvStream& rStrm,SwDoc &rDocument) + : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument), + m_bWriteAll(true) +{ +} + +SwWriter::SwWriter(SvStream& rStrm, SwPaM& rPam, bool bInWriteAll) + : m_pStrm(&rStrm), m_pMedium(nullptr), m_pOutPam(&rPam), m_pShell(nullptr), + m_rDoc(*rPam.GetDoc()), m_bWriteAll(bInWriteAll) +{ +} + +SwWriter::SwWriter( const uno::Reference < embed::XStorage >& rStg, SwDoc &rDocument) + : m_pStrm(nullptr), m_xStg( rStg ), m_pMedium(nullptr), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument), m_bWriteAll(true) +{ +} + +SwWriter::SwWriter(SfxMedium& rMedium, SwCursorShell &rShell, bool bInWriteAll) + : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(&rShell), + m_rDoc(*rShell.GetDoc()), m_bWriteAll(bInWriteAll) +{ +} + +SwWriter::SwWriter(SfxMedium& rMedium, SwDoc &rDocument) + : m_pStrm(nullptr), m_pMedium(&rMedium), m_pOutPam(nullptr), m_pShell(nullptr), m_rDoc(rDocument), + m_bWriteAll(true) +{ +} + +ErrCode SwWriter::Write( WriterRef const & rxWriter, const OUString* pRealFileName ) +{ + // #i73788# + SwPauseThreadStarting aPauseThreadStarting; + + bool bHasMark = false; + std::shared_ptr<SwUnoCursor> pTempCursor; + SwPaM * pPam; + + rtl::Reference<SwDoc> xDoc; + + if ( m_pShell && !m_bWriteAll && m_pShell->IsTableMode() ) + { + m_bWriteAll = true; + xDoc = new SwDoc; + + // Copy parts of a table: + // Create a table with the width of the original and copy the selected cells. + // The sizes are corrected by ratio. + + // search the layout for cells + SwSelBoxes aBoxes; + GetTableSel( *m_pShell, aBoxes ); + const SwTableNode* pTableNd = static_cast<const SwTableNode*>(aBoxes[0]->GetSttNd()->StartOfSectionNode()); + SwNodeIndex aIdx( xDoc->GetNodes().GetEndOfExtras(), 2 ); + SwContentNode *pNd = aIdx.GetNode().GetContentNode(); + OSL_ENSURE( pNd, "Node not found" ); + SwPosition aPos( aIdx, SwIndex( pNd ) ); + pTableNd->GetTable().MakeCopy( xDoc.get(), aPos, aBoxes ); + } + + if( !m_bWriteAll && ( m_pShell || m_pOutPam )) + { + if( m_pShell ) + pPam = m_pShell->GetCursor(); + else + pPam = m_pOutPam; + + SwPaM *pEnd = pPam; + + // 1st round: Check if there is a selection + while(true) + { + bHasMark = bHasMark || pPam->HasMark(); + pPam = pPam->GetNext(); + if(bHasMark || pPam == pEnd) + break; + } + + // if there is no selection, select the whole document + if(!bHasMark) + { + if( m_pShell ) + { + m_pShell->Push(); + m_pShell->SttEndDoc(true); + m_pShell->SetMark(); + m_pShell->SttEndDoc(false); + } + else + { + pPam = new SwPaM( *pPam, pPam ); + pPam->Move( fnMoveBackward, GoInDoc ); + pPam->SetMark(); + pPam->Move( fnMoveForward, GoInDoc ); + } + } + // pPam is still the current Cursor !! + } + else + { + // no Shell or write-everything -> create a Pam + SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc; + pTempCursor = pOutDoc->CreateUnoCursor( + SwPosition(pOutDoc->GetNodes().GetEndOfContent()), false); + pPam = pTempCursor.get(); + if( pOutDoc->IsClipBoard() ) + { + pPam->Move( fnMoveBackward, GoInDoc ); + pPam->SetMark(); + pPam->Move( fnMoveForward, GoInDoc ); + } + else + { + pPam->SetMark(); + pPam->Move( fnMoveBackward, GoInDoc ); + } + } + + rxWriter->m_bWriteAll = m_bWriteAll; + SwDoc* pOutDoc = xDoc.is() ? xDoc.get() : &m_rDoc; + + // If the default PageDesc has still the initial value, + // (e.g. if no printer was set) then set it to DIN A4. + // #i37248# - Modifications are only allowed at a new document. + // <pOutDoc> contains a new document, if <xDoc> is set - see above. + if ( xDoc.is() && !pOutDoc->getIDocumentDeviceAccess().getPrinter( false ) ) + { + const SwPageDesc& rPgDsc = pOutDoc->GetPageDesc( 0 ); + //const SwPageDesc& rPgDsc = *pOutDoc->GetPageDescFromPool( RES_POOLPAGE_STANDARD ); + const SwFormatFrameSize& rSz = rPgDsc.GetMaster().GetFrameSize(); + // Clipboard-Document is always created w/o printer; thus the + // default PageDesc is always aug LONG_MAX !! Set then to DIN A4 + if( LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth() ) + { + SwPageDesc aNew( rPgDsc ); + SwFormatFrameSize aNewSz( rSz ); + Size a4(SvxPaperInfo::GetPaperSize( PAPER_A4 )); + aNewSz.SetHeight( a4.Width() ); + aNewSz.SetWidth( a4.Height() ); + aNew.GetMaster().SetFormatAttr( aNewSz ); + pOutDoc->ChgPageDesc( 0, aNew ); + } + } + + bool bLockedView(false); + SwEditShell* pESh = pOutDoc->GetEditShell(); + if( pESh ) + { + bLockedView = pESh->IsViewLocked(); + pESh->LockView( true ); //lock visible section + pESh->StartAllAction(); + } + + auto xGuard = std::make_unique<PurgeGuard>(*pOutDoc); + + pOutDoc->SetInWriting(true); + ErrCode nError = ERRCODE_NONE; + if( m_pMedium ) + nError = rxWriter->Write( *pPam, *m_pMedium, pRealFileName ); + else if( m_pStrm ) + nError = rxWriter->Write( *pPam, *m_pStrm, pRealFileName ); + else if( m_xStg.is() ) + nError = rxWriter->Write( *pPam, m_xStg, pRealFileName ); + pOutDoc->SetInWriting(false); + + xGuard.reset(); + + if( pESh ) + { + pESh->EndAllAction(); + pESh->LockView( bLockedView ); + } + + // If the selection was only created for printing, reset the old cursor before returning + if( !m_bWriteAll && ( m_pShell || m_pOutPam )) + { + if(!bHasMark) + { + if( m_pShell ) + m_pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + else + delete pPam; + } + } + else + { + // Everything was written successfully? Tell the document! + if ( !nError.IsError() && !xDoc.is() ) + { + m_rDoc.getIDocumentState().ResetModified(); + // #i38810# - reset also flag, that indicates updated links + m_rDoc.getIDocumentLinksAdministration().SetLinksUpdated( false ); + } + } + + if ( xDoc.is() ) + { + pTempCursor.reset(); + xDoc.clear(); + m_bWriteAll = false; + } + + return nError; +} + +bool SetHTMLTemplate( SwDoc & rDoc ) +{ + // get template name of the Sfx-HTML-Filter !!! + if( !ReadHTML->GetTemplateDoc(rDoc) ) + ReadHTML->MakeHTMLDummyTemplateDoc(); + + bool bRet = ReadHTML->SetTemplate( rDoc ); + + SwNodes& rNds = rDoc.GetNodes(); + SwNodeIndex aIdx( rNds.GetEndOfExtras(), 1 ); + SwContentNode* pCNd = rNds.GoNext( &aIdx ); + if( pCNd ) + { + pCNd->SetAttr + ( SwFormatPageDesc(rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_HTML, false) ) ); + pCNd->ChgFormatColl( rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false )); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/docx/swdocxreader.cxx b/sw/source/filter/docx/swdocxreader.cxx new file mode 100644 index 000000000..cb1aa9133 --- /dev/null +++ b/sw/source/filter/docx/swdocxreader.cxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "swdocxreader.hxx" + +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <doc.hxx> +#include <docsh.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <ndtxt.hxx> +#include <poolfmt.hxx> +#include <swerror.h> +#include <unotools/streamwrap.hxx> +#include <unotextrange.hxx> +#include <sfx2/docfile.hxx> +#include <tools/diagnose_ex.h> + +#define AUTOTEXT_GALLERY "autoTxt" + +using namespace css; + +extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOCX() +{ + return new SwDOCXReader; +} + +ErrCode SwDOCXReader::Read(SwDoc& rDoc, const OUString& /* rBaseURL */, SwPaM& rPam, const OUString& /* FileName */ ) +{ + if (!m_pMedium->GetInStream()) + return ERR_SWG_READ_ERROR; + + // We want to work in an empty paragraph. + const SwPosition* pPos = rPam.GetPoint(); + rDoc.getIDocumentContentOperations().SplitNode(*pPos, false); + rDoc.SetTextFormatColl(rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false)); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter"), uno::UNO_SET_THROW); + + SwDocShell* pDocShell(rDoc.GetDocShell()); + uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + xImporter->setTargetDocument(xDstDoc); + + const uno::Reference<text::XTextRange> xInsertTextRange = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr); + uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*m_pMedium->GetInStream())); + + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + pDocShell->SetLoading(SfxLoadedFlags::NONE); + + uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "InsertMode", uno::Any(true) }, + { "TextInsertModeRange", uno::Any(xInsertTextRange) } + })); + + ErrCode ret = ERRCODE_NONE; + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + try + { + xFilter->filter(aDescriptor); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.docx", "SwDOCXReader::Read()"); + ret = ERR_SWG_READ_ERROR; + } + pDocShell->SetLoading(SfxLoadedFlags::ALL); + + return ret; +} + +SwReaderType SwDOCXReader::GetReaderType() +{ + return SwReaderType::Storage | SwReaderType::Stream; +} + +bool SwDOCXReader::HasGlossaries() const +{ + // TODO + return true; +} + +bool SwDOCXReader::ReadGlossaries( SwTextBlocks& rBlocks, bool /* bSaveRelFiles */ ) const +{ + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory( + comphelper::getProcessServiceFactory() ); + + uno::Reference<uno::XInterface> xInterface( + xMultiServiceFactory->createInstance( "com.sun.star.comp.Writer.WriterFilter" ), + uno::UNO_SET_THROW ); + + uno::Reference<document::XFilter> xFilter( xInterface, uno::UNO_QUERY_THROW ); + uno::Reference<document::XImporter> xImporter( xFilter, uno::UNO_QUERY_THROW ); + + SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL ) ); + if( xDocSh->DoInitNew() ) + { + uno::Reference<lang::XComponent> xDstDoc( xDocSh->GetModel(), uno::UNO_QUERY_THROW ); + xImporter->setTargetDocument( xDstDoc ); + + uno::Reference<io::XStream> xStream( new utl::OStreamWrapper( *m_pMedium->GetInStream() ) ); + + uno::Sequence<beans::PropertyValue> aDescriptor( comphelper::InitPropertySequence({ + { "InputStream", uno::Any(xStream) }, + { "ReadGlossaries", uno::Any(true) } + })); + + if( xFilter->filter( aDescriptor ) ) + { + if (rBlocks.StartPutMuchBlockEntries()) + { + bool bRet = MakeEntries(static_cast<SwDocShell*>(&xDocSh)->GetDoc(), rBlocks); + rBlocks.EndPutMuchBlockEntries(); + return bRet; + } + } + } + + return false; +} + +bool SwDOCXReader::MakeEntries( SwDoc *pD, SwTextBlocks &rBlocks ) +{ + const OUString aOldURL( rBlocks.GetBaseURL() ); + rBlocks.SetBaseURL( OUString() ); + + bool bRet = false; + + SwNodeIndex aDocEnd( pD->GetNodes().GetEndOfContent() ); + SwNodeIndex aStart( *aDocEnd.GetNode().StartOfSectionNode(), 1 ); + bool bIsAutoText = false; + + if( aStart < aDocEnd && ( aDocEnd.GetIndex() - aStart.GetIndex() > 2 ) ) + { + SwTextFormatColl* pColl = pD->getIDocumentStylePoolAccess().GetTextCollFromPool + (RES_POOLCOLL_STANDARD, false); + SwContentNode* pCNd = nullptr; + bRet = true; + do { + // Get name - first paragraph + OUString aLNm; + { + SwPaM aPam( aStart ); + SwNodeIndex& rIdx = aPam.GetPoint()->nNode; + ++rIdx; + aLNm = aPam.GetNode().GetTextNode()->GetText(); + + // is AutoText? + bIsAutoText = aLNm.startsWith(AUTOTEXT_GALLERY); + aLNm = aLNm.copy(strlen(AUTOTEXT_GALLERY) + 1); + } + + // Do not copy name + aStart++; + + // Get content + SwPaM aPam( aStart ); + { + SwNodeIndex& rIdx = aPam.GetPoint()->nNode; + ++rIdx; + if( nullptr == ( pCNd = rIdx.GetNode().GetTextNode() ) ) + { + pCNd = pD->GetNodes().MakeTextNode( rIdx, pColl ); + rIdx = *pCNd; + } + } + + aPam.GetPoint()->nContent.Assign( pCNd, 0 ); + aPam.SetMark(); + { + SwNodeIndex& rIdx = aPam.GetPoint()->nNode; + rIdx = aStart.GetNode().EndOfSectionIndex() - 1; + // don't add extra empty text node if exist (.dotx but not .dotm) + if( rIdx.GetNode().GetTextNode() && + rIdx.GetNode().GetTextNode()->GetText().isEmpty() ) + rIdx = aStart.GetNode().EndOfSectionIndex() - 2; + if( nullptr == ( pCNd = rIdx.GetNode().GetContentNode() ) ) + { + ++rIdx; + pCNd = pD->GetNodes().MakeTextNode( rIdx, pColl ); + rIdx = *pCNd; + } + } + aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + if( bIsAutoText ) + { + // Now we have the right selection for one entry + rBlocks.ClearDoc(); + + OUString sShortcut = aLNm; + + // Need to check make sure the shortcut is not already being used + sal_Int32 nStart = 0; + sal_uInt16 nCurPos = rBlocks.GetIndex( sShortcut ); + + while( sal_uInt16(-1) != nCurPos ) + { + // add a Number to it + sShortcut = aLNm + OUString::number( ++nStart ); + nCurPos = rBlocks.GetIndex( sShortcut ); + } + + if( rBlocks.BeginPutDoc( sShortcut, sShortcut ) ) + { + SwDoc* pGlDoc = rBlocks.GetDoc(); + SwNodeIndex aIdx( pGlDoc->GetNodes().GetEndOfContent(), -1 ); + pCNd = aIdx.GetNode().GetContentNode(); + SwPosition aPos( aIdx, SwIndex( pCNd, pCNd ? pCNd->Len() : 0 ) ); + pD->getIDocumentContentOperations().CopyRange(aPam, aPos, SwCopyFlags::CheckPosInFly); + rBlocks.PutDoc(); + } + else + { + bRet = false; + } + } + + aStart = aStart.GetNode().EndOfSectionIndex() + 1; + } while( aStart < aDocEnd && aStart.GetNode().IsStartNode() ); + } + + rBlocks.SetBaseURL( aOldURL ); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/docx/swdocxreader.hxx b/sw/source/filter/docx/swdocxreader.hxx new file mode 100644 index 000000000..214d2742d --- /dev/null +++ b/sw/source/filter/docx/swdocxreader.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_FILTER_DOCX_SWDOCXREADER_HXX +#define INCLUDED_SW_SOURCE_FILTER_DOCX_SWDOCXREADER_HXX + +#include <shellio.hxx> + +/// Wrapper for the UNO DOCX import filter (in writerfilter) for autotext purposes. +class SwDOCXReader : public StgReader +{ +public: + virtual SwReaderType GetReaderType() override; + + virtual bool HasGlossaries() const override; + virtual bool ReadGlossaries( SwTextBlocks& rBlocks, bool bSaveRelFiles ) const override; + +private: + virtual ErrCode Read( SwDoc&, const OUString&, SwPaM&, const OUString& ) override; + + static bool MakeEntries( SwDoc *pD, SwTextBlocks &rBlocks ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/SwAppletImpl.cxx b/sw/source/filter/html/SwAppletImpl.cxx new file mode 100644 index 000000000..fe79b0d26 --- /dev/null +++ b/sw/source/filter/html/SwAppletImpl.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 <SwAppletImpl.hxx> +#include <svtools/htmlkywd.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <comphelper/embeddedobjectcontainer.hxx> +#include <comphelper/classids.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <svtools/embedhlp.hxx> +#include <tools/globname.hxx> +#include <tools/urlobj.hxx> +#include <hintids.hxx> + +using namespace com::sun::star; + +SwHtmlOptType SwApplet_Impl::GetOptionType( const OUString& rName, bool bApplet ) +{ + SwHtmlOptType nType = bApplet ? SwHtmlOptType::PARAM : SwHtmlOptType::TAG; + + switch( rName.toChar() ) + { + case 'A': + case 'a': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_align ) || + rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_alt ) ) + nType = SwHtmlOptType::IGNORE; + else if( bApplet && + (rName == "ARCHIVE" || rName == "ARCHIVES" ) ) + nType = SwHtmlOptType::TAG; + break; + case 'C': + case 'c': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_class ) || + (bApplet && (rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_code ) || + rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_codebase ))) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'H': + case 'h': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_height ) ) + nType = SwHtmlOptType::SIZE; + else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_hspace ) || + (!bApplet && rName.equalsIgnoreAsciiCase( OOO_STRING_SW_HTML_O_Hidden )) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'I': + case 'i': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_id ) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'M': + case 'm': + if( bApplet && rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_mayscript ) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'N': + case 'n': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_name ) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'O': + case 'o': + if( bApplet && rName == "OBJECT" ) + nType = SwHtmlOptType::TAG; + break; + case 'S': + case 's': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_style ) || + (!bApplet && rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_src )) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'T': + case 't': + if( !bApplet && rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_type ) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'V': + case 'v': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_vspace ) ) + nType = SwHtmlOptType::IGNORE; + break; + case 'W': + case 'w': + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_width ) ) + nType = SwHtmlOptType::SIZE; + break; + } + + return nType; +} +SwApplet_Impl::SwApplet_Impl( SfxItemPool& rPool ) : + aItemSet( rPool, svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ) +{ +} + +void SwApplet_Impl::CreateApplet( const OUString& rCode, const OUString& rName, + bool bMayScript, const OUString& rCodeBase, + const OUString& rDocumentBaseURL ) +{ + comphelper::EmbeddedObjectContainer aCnt; + OUString aName; + + // create Applet; it will be in running state + xApplet = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_APPLET_CLASSID ).GetByteSequence(), aName ); + ::svt::EmbeddedObjectRef::TryRunningState( xApplet ); + + INetURLObject aUrlBase(rDocumentBaseURL); + aUrlBase.removeSegment(); + + OUString sDocBase = aUrlBase.GetMainURL(INetURLObject::DecodeMechanism::NONE); + uno::Reference < beans::XPropertySet > xSet( xApplet->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + xSet->setPropertyValue("AppletCode", uno::makeAny( rCode ) ); + xSet->setPropertyValue("AppletName", uno::makeAny( rName ) ); + xSet->setPropertyValue("AppletIsScript", uno::makeAny( bMayScript ) ); + xSet->setPropertyValue("AppletDocBase", uno::makeAny( sDocBase ) ); + if ( !rCodeBase.isEmpty() ) + xSet->setPropertyValue("AppletCodeBase", uno::makeAny( rCodeBase ) ); + else + xSet->setPropertyValue("AppletCodeBase", uno::makeAny( sDocBase ) ); + } +} +#if HAVE_FEATURE_JAVA +bool SwApplet_Impl::CreateApplet( const OUString& rBaseURL ) +{ + OUString aCode, aName, aCodeBase; + bool bMayScript = false; + + size_t nArgCount = aCommandList.size(); + for( size_t i = 0; i < nArgCount; i++ ) + { + const SvCommand& rArg = aCommandList[i]; + const OUString& rName = rArg.GetCommand(); + if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_code ) ) + aCode = rArg.GetArgument(); + else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_codebase ) ) + aCodeBase = INetURLObject::GetAbsURL( rBaseURL, rArg.GetArgument() ); + else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_name ) ) + aName = rArg.GetArgument(); + else if( rName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_mayscript ) ) + bMayScript = true; + } + + if( aCode.isEmpty() ) + return false; + CreateApplet( aCode, aName, bMayScript, aCodeBase, rBaseURL ); + return true; +} +#endif + +SwApplet_Impl::~SwApplet_Impl() +{ +} +void SwApplet_Impl::FinishApplet() +{ + uno::Reference < beans::XPropertySet > xSet( xApplet->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + uno::Sequence < beans::PropertyValue > aProps; + aCommandList.FillSequence( aProps ); + xSet->setPropertyValue("AppletCommands", uno::makeAny( aProps ) ); + } +} + +#if HAVE_FEATURE_JAVA +void SwApplet_Impl::AppendParam( const OUString& rName, const OUString& rValue ) +{ + aCommandList.Append( rName, rValue ); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx new file mode 100644 index 000000000..5445115a6 --- /dev/null +++ b/sw/source/filter/html/css1atr.cxx @@ -0,0 +1,3754 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/svapp.hxx> +#include <svl/whiter.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <svx/xoutbmp.hxx> +#include <svx/svdobj.hxx> +#include <editeng/langitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> +#include <svl/urihelper.hxx> +#include <unotools/charclass.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <charfmt.hxx> +#include <fmtclds.hxx> +#include <fmtcol.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtpdsc.hxx> +#include <fmtlsplt.hxx> +#include <pagedesc.hxx> +#include <fmtanchr.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <viewsh.hxx> +#include <viewopt.hxx> +#include <swtable.hxx> +// NOTES +#include <ftninfo.hxx> +#include <ftnidx.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +// FOOTNOTES +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <swerror.h> +#include <paratr.hxx> +#include <frmatr.hxx> +#include <poolfmt.hxx> +#include "css1kywd.hxx" +#include "wrthtml.hxx" +#include "htmlnum.hxx" +#include "css1atr.hxx" + +#include <IDocumentStylePoolAccess.hxx> +#include <numrule.hxx> +#include <o3tl/typed_flags_set.hxx> + +#include <rtl/strbuf.hxx> + +using namespace css; +using editeng::SvxBorderLine; + +#define HTML_HEADSPACE (12*20) + +namespace { + +enum class Css1Background { + Attr = 1, + Page = 2, + Table = 3, + Fly = 4, + Section = 5 +}; + +enum class Css1FrameSize { + NONE = 0x00, + Width = 0x01, + MinHeight = 0x02, + FixHeight = 0x04, + AnyHeight = 0x06, + Pixel = 0x10, +}; + +} + +namespace o3tl { + template<> struct typed_flags<Css1FrameSize> : is_typed_flags<Css1FrameSize, 0x17> {}; +} + +#define DOT_LEADERS_MAX_WIDTH 18 + +static Writer& OutCSS1_SwFormat( Writer& rWrt, const SwFormat& rFormat, + IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate ); +static Writer& OutCSS1_SwPageDesc( Writer& rWrt, const SwPageDesc& rFormat, + IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate, + sal_uInt16 nRefPoolId, bool bExtRef, + bool bPseudo=true ); +static Writer& OutCSS1_SwFootnoteInfo( Writer& rWrt, const SwEndNoteInfo& rInfo, + SwDoc *pDoc, bool bHasNotes, bool bEndNote ); +static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt, + const SwFormatDrop& rDrop, + const SfxItemSet *pCharFormatItemSet=nullptr ); +static Writer& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( Writer& rWrt, + const SvxUnderlineItem *pUItem, + const SvxOverlineItem *pOItem, + const SvxCrossedOutItem *pCOItem, + const SvxBlinkItem *pBItem ); +static Writer& OutCSS1_SvxFontWeight( Writer& rWrt, const SfxPoolItem& rHt ); +static Writer& OutCSS1_SvxPosture( Writer& rWrt, const SfxPoolItem& rHt ); +static Writer& OutCSS1_SvxULSpace( Writer& rWrt, const SfxPoolItem& rHt ); +static Writer& OutCSS1_SvxLRSpace( Writer& rWrt, const SfxPoolItem& rHt ); +static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt, + const SvxULSpaceItem *pULSpace, + const SvxLRSpaceItem *pLRSpace ); +static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt, + const SfxItemSet& rItemSet ); +static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt, + Css1Background nMode, + const OUString *pGraphicName ); +static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt ); +static Writer& OutCSS1_SwFormatFrameSize( Writer& rWrt, const SfxPoolItem& rHt, + Css1FrameSize nMode ); +static Writer& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( Writer& rWrt, + const SfxItemSet& rItemSet, + bool bDeep ); +static Writer& OutCSS1_SwFormatLayoutSplit( Writer& rWrt, const SfxPoolItem& rHt ); + +namespace +{ + +const char sCSS1_rule_end[] = " }"; +const char sCSS1_span_tag_end[] = "\">"; +const char cCSS1_style_opt_end = '\"'; + +const char* const sHTML_FTN_fontheight = "57%"; + +OString lclConvToHex(sal_uInt16 nHex) +{ + char aNToABuf[] = "00"; + + // set pointer to end of buffer + char *pStr = aNToABuf + (sizeof(aNToABuf)-1); + for( sal_uInt8 n = 0; n < 2; ++n ) + { + *(--pStr) = static_cast<char>(nHex & 0xf ) + 48; + if( *pStr > '9' ) + *pStr += 39; + nHex >>= 4; + } + + return OString(aNToABuf, 2); +} +} + +/// Determines if rProperty has to be suppressed due to ReqIF mode. +bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty) +{ + if (!bReqIF) + return false; + + // Only allow these two keys, nothing else in ReqIF mode. + if (rProperty == sCSS1_P_text_decoration) + return false; + + if (rProperty == sCSS1_P_color) + return false; + + return true; +} + +OString GetCSS1_Color(const Color& rColor) +{ + return "#" + lclConvToHex(rColor.GetRed()) + lclConvToHex(rColor.GetGreen()) + lclConvToHex(rColor.GetBlue()); +} + +namespace { + +class SwCSS1OutMode +{ + SwHTMLWriter& rWrt; + sal_uInt16 nOldMode; + +public: + + SwCSS1OutMode( SwHTMLWriter& rHWrt, sal_uInt16 nMode, + const OUString *pSelector ) : + rWrt( rHWrt ), + nOldMode( rHWrt.m_nCSS1OutMode ) + { + rWrt.m_nCSS1OutMode = nMode; + rWrt.m_bFirstCSS1Property = true; + if( pSelector ) + rWrt.m_aCSS1Selector = *pSelector; + } + + ~SwCSS1OutMode() + { + rWrt.m_nCSS1OutMode = nOldMode; + } +}; + +} + +void SwHTMLWriter::OutCSS1_Property( const char *pProp, + const char *pVal, + const OUString *pSVal ) +{ + if (IgnorePropertyForReqIF(mbReqIF, pProp)) + return; + + OStringBuffer sOut; + + if( m_bFirstCSS1Rule && (m_nCSS1OutMode & CSS1_OUTMODE_RULE_ON)!=0 ) + { + m_bFirstCSS1Rule = false; + OutNewLine(); + sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_style " " + OOO_STRING_SVTOOLS_HTML_O_type "=\"text/css\">"); + // Optional CSS2 code for dot leaders (dotted line between the Table of Contents titles and page numbers): + // (More information: http://www.w3.org/Style/Examples/007/leaders.en.html) + // + // p.leaders { + // /* FIXME: + // (1) dots line up vertically only in the paragraphs with the same alignment/level + // (2) max-width = 18 cm instead of 80em; possible improvement with the new CSS3 calc() */ + // max-width: 18cm; /* note: need to overwrite max-width with max-width - border-left_of_the_actual_paragraph */ + // padding: 0; + // overflow-x: hidden; + // line-height: 120%; /* note: avoid HTML scrollbars and missing descenders of the letters */ + // } + // p.leaders:after { + // float: left; + // width: 0; + // white-space: nowrap; + // content: ". . . . . . . . . . . . . . . . . . ..."; + // } + // p.leaders span:first-child { + // padding-right: 0.33em; + // background: white; + // } + // p.leaders span + span { + // float: right; + // padding-left: 0.33em; + // background: white; + // position: relative; + // z-index: 1 + // } + + if (m_bCfgPrintLayout) { + sOut.append( + "p." sCSS2_P_CLASS_leaders "{max-width:" + OString::number(DOT_LEADERS_MAX_WIDTH) + + "cm;padding:0;overflow-x:hidden;line-height:120%}" + "p." sCSS2_P_CLASS_leaders ":after{float:left;width:0;white-space:nowrap;content:\""); + for (int i = 0; i < 100; i++ ) + sOut.append(". "); + sOut.append( + "\"}p." sCSS2_P_CLASS_leaders " span:first-child{padding-right:0.33em;background:white}" + "p." sCSS2_P_CLASS_leaders " span+span{float:right;padding-left:0.33em;" + "background:white;position:relative;z-index:1}"); + } + Strm().WriteOString( sOut.makeStringAndClear() ); + + IncIndentLevel(); + } + + if( m_bFirstCSS1Property ) + { + switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON ) + { + case CSS1_OUTMODE_SPAN_TAG_ON: + case CSS1_OUTMODE_SPAN_TAG1_ON: + if( m_bTagOn ) + { + sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_span + " " OOO_STRING_SVTOOLS_HTML_O_style "=\""); + } + else + { + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false ); + return; + } + break; + + case CSS1_OUTMODE_RULE_ON: + { + OutNewLine(); + sOut.append(OUStringToOString(m_aCSS1Selector, m_eDestEnc) + " { "); + } + break; + + case CSS1_OUTMODE_STYLE_OPT_ON: + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\""); + break; + } + m_bFirstCSS1Property = false; + } + else + { + sOut.append("; "); + } + + sOut.append(pProp + OStringLiteral(": ")); + if( m_nCSS1OutMode & CSS1_OUTMODE_ENCODE ) + { + // for STYLE-Option encode string + Strm().WriteOString( sOut.makeStringAndClear() ); + if( pVal ) + HTMLOutFuncs::Out_String( Strm(), OUString::createFromAscii(pVal), + m_eDestEnc, &m_aNonConvertableCharacters ); + else if( pSVal ) + HTMLOutFuncs::Out_String( Strm(), *pSVal, m_eDestEnc, &m_aNonConvertableCharacters ); + } + else + { + // for STYLE-Tag print string directly + if( pVal ) + sOut.append(pVal); + else if( pSVal ) + sOut.append(OUStringToOString(*pSVal, m_eDestEnc)); + } + + if (!sOut.isEmpty()) + Strm().WriteOString( sOut.makeStringAndClear() ); +} + +static void AddUnitPropertyValue(OStringBuffer &rOut, long nVal, + FieldUnit eUnit) +{ + if( nVal < 0 ) + { + // special-case sign symbol + nVal = -nVal; + rOut.append('-'); + } + + // the recalculated unit results from (x * nMul)/(nDiv*nFac*10) + long nMul = 1000; + long nDiv = 1; + long nFac = 100; + const char *pUnit; + switch( eUnit ) + { + case FieldUnit::MM_100TH: + OSL_ENSURE( FieldUnit::MM == eUnit, "Measuring unit not supported" ); + [[fallthrough]]; + case FieldUnit::MM: + // 0.01mm = 0.57twip + nMul = 25400; // 25.4 * 1000 + nDiv = 1440; // 72 * 20; + nFac = 100; + pUnit = sCSS1_UNIT_mm; + break; + + case FieldUnit::M: + case FieldUnit::KM: + OSL_ENSURE( FieldUnit::CM == eUnit, "Measuring unit not supported" ); + [[fallthrough]]; + case FieldUnit::CM: + // 0.01cm = 5.7twip (not exact, but the UI is also not exact) + nMul = 2540; // 2.54 * 1000 + nDiv = 1440; // 72 * 20; + nFac = 100; + pUnit = sCSS1_UNIT_cm; + break; + + case FieldUnit::TWIP: + OSL_ENSURE( FieldUnit::POINT == eUnit, "Measuring unit not supported" ); + [[fallthrough]]; + case FieldUnit::POINT: + // 0.1pt = 2.0twip (not exact, but the UI is also not exact) + nMul = 100; + nDiv = 20; + nFac = 10; + pUnit = sCSS1_UNIT_pt; + break; + + case FieldUnit::PICA: + // 0.01pc = 2.40twip (not exact, but the UI is also not exact) + nMul = 1000; + nDiv = 240; // 12 * 20; + nFac = 100; + pUnit = sCSS1_UNIT_pc; + break; + + case FieldUnit::NONE: + case FieldUnit::FOOT: + case FieldUnit::MILE: + case FieldUnit::CUSTOM: + case FieldUnit::PERCENT: + case FieldUnit::INCH: + default: + OSL_ENSURE( FieldUnit::INCH == eUnit, "Measuring unit not supported" ); + // 0.01in = 14.4twip (not exact, but the UI is also not exact) + nMul = 1000; + nDiv = 1440; // 72 * 20; + nFac = 100; + pUnit = sCSS1_UNIT_inch; + break; + } + + long nLongVal = 0; + bool bOutLongVal = true; + if( nVal > LONG_MAX / nMul ) + { + sal_Int64 nBigVal( nVal ); + nBigVal *= nMul; + nBigVal /= nDiv; + nBigVal += 5; + nBigVal /= 10; + + if( nBigVal <= LONG_MAX ) + { + // a long is sufficient + nLongVal = static_cast<long>(nBigVal); + } + else + { + rOut.append(nBigVal / static_cast<sal_Int64>(nFac)); + if( (nBigVal % static_cast<sal_Int64>(nFac)) != 0 ) + { + rOut.append('.'); + while( nFac > 1 && (nBigVal % static_cast<sal_Int64>(nFac)) != 0 ) + { + nFac /= 10; + rOut.append((nBigVal / static_cast<sal_Int64>(nFac)) % sal_Int64(10)); + } + } + bOutLongVal = false; + } + } + else + { + nLongVal = nVal * nMul; + nLongVal /= nDiv; + nLongVal += 5; + nLongVal /= 10; + } + + if( bOutLongVal ) + { + rOut.append(OString::number(nLongVal/nFac)); + if( (nLongVal % nFac) != 0 ) + { + rOut.append('.'); + while( nFac > 1 && (nLongVal % nFac) != 0 ) + { + nFac /= 10; + rOut.append(OString::number((nLongVal / nFac) % 10)); + } + } + } + + rOut.append(pUnit); +} + +void SwHTMLWriter::OutCSS1_UnitProperty( const char *pProp, long nVal ) +{ + OStringBuffer sOut; + AddUnitPropertyValue(sOut, nVal, m_eCSS1Unit); + OutCSS1_PropertyAscii(pProp, sOut.makeStringAndClear()); +} + +void SwHTMLWriter::OutCSS1_PixelProperty( const char *pProp, long nVal, + bool bVert ) +{ + OString sOut(OString::number(ToPixel(nVal,bVert)) + sCSS1_UNIT_px); + OutCSS1_PropertyAscii(pProp, sOut); +} + +void SwHTMLWriter::OutStyleSheet( const SwPageDesc& rPageDesc ) +{ + m_bFirstCSS1Rule = true; + +// Feature: PrintExt + if( IsHTMLMode(HTMLMODE_PRINT_EXT) ) + { + const SwPageDesc *pFirstPageDesc = nullptr; + sal_uInt16 nFirstRefPoolId = RES_POOLPAGE_HTML; + m_bCSS1IgnoreFirstPageDesc = true; + + // First we try to guess how the document is constructed. + // Allowed are only the templates: HTML, 1st page, left page, and right page. + // A first page is only exported, if it matches the template "1st page". + // Left and right pages are only exported, if their templates are linked. + // If other templates are used, only very simple cases are exported. + const SwPageDesc *pPageDesc = &rPageDesc; + const SwPageDesc *pFollow = rPageDesc.GetFollow(); + if( RES_POOLPAGE_FIRST == pPageDesc->GetPoolFormatId() && + pFollow != pPageDesc && + !IsPoolUserFormat( pFollow->GetPoolFormatId() ) ) + { + // the document has a first page + pFirstPageDesc = pPageDesc; + pPageDesc = pFollow; + pFollow = pPageDesc->GetFollow(); + } + + IDocumentStylePoolAccess* pStylePoolAccess = &getIDocumentStylePoolAccess(); + if( pPageDesc == pFollow ) + { + // The document is one-sided; no matter what page, we do not create a 2-sided doc. + // The attribute is exported relative to the HTML page template. + OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(), + RES_POOLPAGE_HTML, true, false ); + nFirstRefPoolId = pFollow->GetPoolFormatId(); + } + else if( (RES_POOLPAGE_LEFT == pPageDesc->GetPoolFormatId() && + RES_POOLPAGE_RIGHT == pFollow->GetPoolFormatId()) || + (RES_POOLPAGE_RIGHT == pPageDesc->GetPoolFormatId() && + RES_POOLPAGE_LEFT == pFollow->GetPoolFormatId()) ) + { + // the document is double-sided + OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(), + RES_POOLPAGE_HTML, true ); + OutCSS1_SwPageDesc( *this, *pFollow, pStylePoolAccess, m_xTemplate.get(), + RES_POOLPAGE_HTML, true ); + nFirstRefPoolId = RES_POOLPAGE_RIGHT; + m_bCSS1IgnoreFirstPageDesc = false; + } + // other cases we miss + + if( pFirstPageDesc ) + OutCSS1_SwPageDesc( *this, *pFirstPageDesc, pStylePoolAccess, m_xTemplate.get(), + nFirstRefPoolId, false ); + } + + // The text body style has to be exported always (if it is changed compared + // to the template), because it is used as reference for any style + // that maps to <P>, and that's especially the standard style + getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false ); + + // the Default-TextStyle is not also exported !! + // 0-Style is the Default; is never exported !! + const size_t nTextFormats = m_pDoc->GetTextFormatColls()->size(); + for( size_t i = 1; i < nTextFormats; ++i ) + { + const SwTextFormatColl* pColl = (*m_pDoc->GetTextFormatColls())[i]; + sal_uInt16 nPoolId = pColl->GetPoolFormatId(); + if( nPoolId == RES_POOLCOLL_TEXT || m_pDoc->IsUsed( *pColl ) ) + OutCSS1_SwFormat( *this, *pColl, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() ); + } + + // the Default-TextStyle is not also exported !! + const size_t nCharFormats = m_pDoc->GetCharFormats()->size(); + for( size_t i = 1; i < nCharFormats; ++i ) + { + const SwCharFormat *pCFormat = (*m_pDoc->GetCharFormats())[i]; + sal_uInt16 nPoolId = pCFormat->GetPoolFormatId(); + if( nPoolId == RES_POOLCHR_INET_NORMAL || + nPoolId == RES_POOLCHR_INET_VISIT || + m_pDoc->IsUsed( *pCFormat ) ) + OutCSS1_SwFormat( *this, *pCFormat, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() ); + } + + bool bHasEndNotes {false}; + bool bHasFootNotes {false}; + const SwFootnoteIdxs& rIdxs = m_pDoc->GetFootnoteIdxs(); + for( auto pIdx : rIdxs ) + { + if( pIdx->GetFootnote().IsEndNote() ) + { + bHasEndNotes = true; + if (bHasFootNotes) + break; + } + else + { + bHasFootNotes = true; + if (bHasEndNotes) + break; + } + } + OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetFootnoteInfo(), m_pDoc, bHasFootNotes, false ); + OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetEndNoteInfo(), m_pDoc, bHasEndNotes, true ); + + if( !m_bFirstCSS1Rule ) + { + DecIndentLevel(); + + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_style, false ); + } + else + { + m_bFirstCSS1Rule = false; + } + + m_nDfltTopMargin = 0; + m_nDfltBottomMargin = 0; +} + +// if pPseudo is set, Styles-Sheets will be exported; +// otherwise we only search for Token and Class for a Format +sal_uInt16 SwHTMLWriter::GetCSS1Selector( const SwFormat *pFormat, OString& rToken, + OUString& rClass, sal_uInt16& rRefPoolId, + OUString *pPseudo ) +{ + sal_uInt16 nDeep = 0; + rToken.clear(); + rClass.clear(); + rRefPoolId = 0; + if( pPseudo ) + pPseudo->clear(); + + bool bChrFormat = RES_CHRFMT==pFormat->Which(); + + // search formats above for the nearest standard or HTML-Tag template + const SwFormat *pPFormat = pFormat; + while( pPFormat && !pPFormat->IsDefault() ) + { + bool bStop = false; + sal_uInt16 nPoolId = pPFormat->GetPoolFormatId(); + if( USER_FMT & nPoolId ) + { + // user templates + const OUString& aNm(pPFormat->GetName()); + + if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_blockquote) + { + rRefPoolId = RES_POOLCOLL_HTML_BLOCKQUOTE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_blockquote); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_citiation) + { + rRefPoolId = RES_POOLCHR_HTML_CITIATION; + rToken = OString(OOO_STRING_SVTOOLS_HTML_citiation); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_code) + { + rRefPoolId = RES_POOLCHR_HTML_CODE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_code); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_definstance) + { + rRefPoolId = RES_POOLCHR_HTML_DEFINSTANCE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_definstance); + } + else if (!bChrFormat && (aNm == OOO_STRING_SVTOOLS_HTML_dd || + aNm == OOO_STRING_SVTOOLS_HTML_dt)) + { + sal_uInt16 nDefListLvl = GetDefListLvl(aNm, nPoolId); + // Export the templates DD 1/DT 1, + // but none of their derived templates, + // also not DD 2/DT 2 etc. + if (nDefListLvl) + { + if (pPseudo && (nDeep || (nDefListLvl & 0x0fff) > 1)) + { + bStop = true; + } + else if (nDefListLvl & HTML_DLCOLL_DD) + { + rRefPoolId = RES_POOLCOLL_HTML_DD; + rToken = OString(OOO_STRING_SVTOOLS_HTML_dd); + } + else + { + rRefPoolId = RES_POOLCOLL_HTML_DT; + rToken = OString(OOO_STRING_SVTOOLS_HTML_dt); + } + } + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_emphasis) + { + rRefPoolId = RES_POOLCHR_HTML_EMPHASIS; + rToken = OString(OOO_STRING_SVTOOLS_HTML_emphasis); + } + else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_horzrule) + { + // do not export HR ! + bStop = (nDeep==0); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_keyboard) + { + rRefPoolId = RES_POOLCHR_HTML_KEYBOARD; + rToken = OString(OOO_STRING_SVTOOLS_HTML_keyboard); + } + else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_listing) + { + // Export Listings as PRE or PRE-derived template + rToken = OString(OOO_STRING_SVTOOLS_HTML_preformtxt); + rRefPoolId = RES_POOLCOLL_HTML_PRE; + nDeep = CSS1_FMT_CMPREF; + } + else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_preformtxt) + { + rRefPoolId = RES_POOLCOLL_HTML_PRE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_preformtxt); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_sample) + { + rRefPoolId = RES_POOLCHR_HTML_SAMPLE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_sample); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_strong) + { + rRefPoolId = RES_POOLCHR_HTML_STRONG; + rToken = OString(OOO_STRING_SVTOOLS_HTML_strong); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_teletype) + { + rRefPoolId = RES_POOLCHR_HTML_TELETYPE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_teletype); + } + else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_variable) + { + rRefPoolId = RES_POOLCHR_HTML_VARIABLE; + rToken = OString(OOO_STRING_SVTOOLS_HTML_variable); + } + else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_xmp) + { + // export XMP as PRE (but not the template as Style) + rToken = OString(OOO_STRING_SVTOOLS_HTML_preformtxt); + rRefPoolId = RES_POOLCOLL_HTML_PRE; + nDeep = CSS1_FMT_CMPREF; + } + + // if a PoolId is set, the Name of the template is that of the related Token + OSL_ENSURE( (rRefPoolId != 0) == (!rToken.isEmpty()), + "Token missing" ); + } + else + { + // Pool templates + switch( nPoolId ) + { + // paragraph templates + case RES_POOLCOLL_HEADLINE_BASE: + case RES_POOLCOLL_STANDARD: + // do not export this template + bStop = (nDeep==0); + break; + case RES_POOLCOLL_TEXT: + rToken = OString(OOO_STRING_SVTOOLS_HTML_parabreak); + break; + case RES_POOLCOLL_HEADLINE1: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head1); + break; + case RES_POOLCOLL_HEADLINE2: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head2); + break; + case RES_POOLCOLL_HEADLINE3: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head3); + break; + case RES_POOLCOLL_HEADLINE4: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head4); + break; + case RES_POOLCOLL_HEADLINE5: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head5); + break; + case RES_POOLCOLL_HEADLINE6: + rToken = OString(OOO_STRING_SVTOOLS_HTML_head6); + break; + case RES_POOLCOLL_SENDADRESS: + rToken = OString(OOO_STRING_SVTOOLS_HTML_address); + break; + case RES_POOLCOLL_HTML_BLOCKQUOTE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_blockquote); + break; + case RES_POOLCOLL_HTML_PRE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_preformtxt); + break; + + case RES_POOLCOLL_HTML_DD: + rToken = OString(OOO_STRING_SVTOOLS_HTML_dd); + break; + case RES_POOLCOLL_HTML_DT: + rToken = OString(OOO_STRING_SVTOOLS_HTML_dt); + break; + + case RES_POOLCOLL_TABLE: + if( pPseudo ) + { + rToken = OOO_STRING_SVTOOLS_HTML_tabledata " " + OOO_STRING_SVTOOLS_HTML_parabreak; + } + else + rToken = OOO_STRING_SVTOOLS_HTML_parabreak; + break; + case RES_POOLCOLL_TABLE_HDLN: + if( pPseudo ) + { + rToken = OOO_STRING_SVTOOLS_HTML_tableheader " " + OOO_STRING_SVTOOLS_HTML_parabreak; + } + else + rToken = OString(OOO_STRING_SVTOOLS_HTML_parabreak); + break; + case RES_POOLCOLL_HTML_HR: + // do not export HR ! + bStop = (nDeep==0); + break; + case RES_POOLCOLL_FOOTNOTE: + if( !nDeep ) + { + rToken = OString(OOO_STRING_SVTOOLS_HTML_parabreak); + rClass = OOO_STRING_SVTOOLS_HTML_sdfootnote; + rRefPoolId = RES_POOLCOLL_TEXT; + nDeep = CSS1_FMT_CMPREF; + } + break; + case RES_POOLCOLL_ENDNOTE: + if( !nDeep ) + { + rToken = OString(OOO_STRING_SVTOOLS_HTML_parabreak); + rClass = OOO_STRING_SVTOOLS_HTML_sdendnote; + rRefPoolId = RES_POOLCOLL_TEXT; + nDeep = CSS1_FMT_CMPREF; + } + break; + + // character templates + case RES_POOLCHR_HTML_EMPHASIS: + rToken = OString(OOO_STRING_SVTOOLS_HTML_emphasis); + break; + case RES_POOLCHR_HTML_CITIATION: + rToken = OString(OOO_STRING_SVTOOLS_HTML_citiation); + break; + case RES_POOLCHR_HTML_STRONG: + rToken = OString(OOO_STRING_SVTOOLS_HTML_strong); + break; + case RES_POOLCHR_HTML_CODE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_code); + break; + case RES_POOLCHR_HTML_SAMPLE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_sample); + break; + case RES_POOLCHR_HTML_KEYBOARD: + rToken = OString(OOO_STRING_SVTOOLS_HTML_keyboard); + break; + case RES_POOLCHR_HTML_VARIABLE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_variable); + break; + case RES_POOLCHR_HTML_DEFINSTANCE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_definstance); + break; + case RES_POOLCHR_HTML_TELETYPE: + rToken = OString(OOO_STRING_SVTOOLS_HTML_teletype); + break; + + case RES_POOLCHR_INET_NORMAL: + if( pPseudo ) + { + rToken = OString(OOO_STRING_SVTOOLS_HTML_anchor); + *pPseudo = OStringToOUString( sCSS1_link, RTL_TEXTENCODING_ASCII_US ); + } + break; + case RES_POOLCHR_INET_VISIT: + if( pPseudo ) + { + rToken = OString(OOO_STRING_SVTOOLS_HTML_anchor); + *pPseudo = OStringToOUString( sCSS1_visited, RTL_TEXTENCODING_ASCII_US ); + } + break; + } + + // if a token is set, PoolId contains the related template + if( !rToken.isEmpty() && !rRefPoolId ) + rRefPoolId = nPoolId; + } + + if( !rToken.isEmpty() || bStop ) + { + // stop if a HTML-Tag template was found + break; + } + else + { + // continue otherwise + nDeep++; + pPFormat = pPFormat->DerivedFrom(); + } + } + + if( !rToken.isEmpty() ) + { + // this is a HTML-Tag template + if( !nDeep ) + nDeep = CSS1_FMT_ISTAG; + } + else + { + // this is not a HTML-Tag template nor derived from one + nDeep = 0; + } + if( nDeep > 0 && nDeep < CSS1_FMT_SPECIAL ) + { + // If the template is derived from a HTML template, + // we export it as <TOKEN>.<CLASS>, otherwise as .<CLASS>. + // <CLASS> is the name of the template after removing all characters + // before and including the first '.' + rClass = pFormat->GetName(); + sal_Int32 nPos = rClass.indexOf( '.' ); + if( nPos >= 0 && rClass.getLength() > nPos+1 ) + { + rClass = rClass.replaceAt( 0, nPos+1, "" ); + } + + rClass = GetAppCharClass().lowercase( rClass ); + rClass = rClass.replaceAll( ".", "-" ); + rClass = rClass.replaceAll( " ", "-" ); + rClass = rClass.replaceAll( "_", "-" ); + } + + return nDeep; +} + +static sal_uInt16 GetCSS1Selector( const SwFormat *pFormat, OUString& rSelector, + sal_uInt16& rRefPoolId ) +{ + OString aToken; + OUString aClass; + OUString aPseudo; + + sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass, + rRefPoolId, &aPseudo ); + if( nDeep ) + { + if( !aToken.isEmpty() ) + rSelector = OStringToOUString(aToken, RTL_TEXTENCODING_ASCII_US); + else + rSelector.clear(); + + if( !aClass.isEmpty() ) + rSelector += "." + aClass; + if( !aPseudo.isEmpty() ) + rSelector += ":" + aPseudo; + } + + return nDeep; +} + +const SwFormat *SwHTMLWriter::GetTemplateFormat( sal_uInt16 nPoolFormatId, + IDocumentStylePoolAccess* pTemplate /*SwDoc *pTemplate*/) +{ + const SwFormat *pRefFormat = nullptr; + + if( pTemplate ) + { + OSL_ENSURE( !(USER_FMT & nPoolFormatId), + "No user templates found" ); + if( POOLGRP_NOCOLLID & nPoolFormatId ) + pRefFormat = pTemplate->GetCharFormatFromPool( nPoolFormatId ); + else + pRefFormat = pTemplate->GetTextCollFromPool( nPoolFormatId, false ); + } + + return pRefFormat; +} + +const SwFormat *SwHTMLWriter::GetParentFormat( const SwFormat& rFormat, sal_uInt16 nDeep ) +{ + OSL_ENSURE( nDeep != USHRT_MAX, "Called GetParent for HTML-template!" ); + const SwFormat *pRefFormat = nullptr; + + if( nDeep > 0 ) + { + // get the pointer for the HTML-Tag template, from which the template is derived + pRefFormat = &rFormat; + for( sal_uInt16 i=nDeep; i>0; i-- ) + pRefFormat = pRefFormat->DerivedFrom(); + + if( pRefFormat && pRefFormat->IsDefault() ) + pRefFormat = nullptr; + } + + return pRefFormat; +} + +bool swhtml_css1atr_equalFontItems( const SfxPoolItem& r1, const SfxPoolItem& r2 ) +{ + return static_cast<const SvxFontItem &>(r1).GetFamilyName() == + static_cast<const SvxFontItem &>(r2).GetFamilyName() && + static_cast<const SvxFontItem &>(r1).GetFamily() == + static_cast<const SvxFontItem &>(r2).GetFamily(); +} + +void SwHTMLWriter::SubtractItemSet( SfxItemSet& rItemSet, + const SfxItemSet& rRefItemSet, + bool bSetDefaults, + bool bClearSame, + const SfxItemSet *pRefScriptItemSet ) +{ + OSL_ENSURE( bSetDefaults || bClearSame, + "SwHTMLWriter::SubtractItemSet: No action for this Flag" ); + SfxItemSet aRefItemSet( *rRefItemSet.GetPool(), rRefItemSet.GetRanges() ); + aRefItemSet.Set( rRefItemSet ); + + // compare with the Attr-Set of the template + SfxWhichIter aIter( rItemSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + const SfxPoolItem *pRefItem, *pItem; + bool bItemSet = ( SfxItemState::SET == + rItemSet.GetItemState( nWhich, false, &pItem) ); + bool bRefItemSet; + + if( pRefScriptItemSet ) + { + switch( nWhich ) + { + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + case RES_CHRATR_CJK_FONT: + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + bRefItemSet = ( SfxItemState::SET == + pRefScriptItemSet->GetItemState( nWhich, true, &pRefItem) ); + break; + default: + bRefItemSet = ( SfxItemState::SET == + aRefItemSet.GetItemState( nWhich, false, &pRefItem) ); + break; + } + } + else + { + bRefItemSet = ( SfxItemState::SET == + aRefItemSet.GetItemState( nWhich, false, &pRefItem) ); + } + + if( bItemSet ) + { + if( (bClearSame || pRefScriptItemSet) && bRefItemSet && + ( *pItem == *pRefItem || + ((RES_CHRATR_FONT == nWhich || + RES_CHRATR_CJK_FONT == nWhich || + RES_CHRATR_CTL_FONT == nWhich) && + swhtml_css1atr_equalFontItems( *pItem, *pRefItem )) ) ) + { + // the Attribute is in both templates with the same value + // and does not need to be exported + rItemSet.ClearItem( nWhich ); + } + } + else + { + if( (bSetDefaults || pRefScriptItemSet) && bRefItemSet ) + { + // the Attribute exists only in the reference; the default + // might have to be exported + rItemSet.Put( rItemSet.GetPool()->GetDefaultItem(nWhich) ); + } + } + + nWhich = aIter.NextWhich(); + } +} + +void SwHTMLWriter::PrepareFontList( const SvxFontItem& rFontItem, + OUString& rNames, + sal_Unicode cQuote, bool bGeneric ) +{ + rNames.clear(); + const OUString& rName = rFontItem.GetFamilyName(); + bool bContainsKeyword = false; + if( !rName.isEmpty() ) + { + sal_Int32 nStrPos = 0; + while( nStrPos != -1 ) + { + OUString aName = rName.getToken( 0, ';', nStrPos ); + aName = comphelper::string::strip(aName, ' '); + if( aName.isEmpty() ) + continue; + + bool bIsKeyword = false; + switch( aName[0] ) + { + case 'c': + case 'C': + bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_cursive ); + break; + + case 'f': + case 'F': + bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_fantasy ); + break; + + case 'm': + case 'M': + bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_monospace ); + break; + + case 's': + case 'S': + bIsKeyword = + aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_serif ) || + aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_sans_serif ); + break; + } + + bContainsKeyword |= bIsKeyword; + + if( !rNames.isEmpty() ) + rNames += ", "; + if( cQuote && !bIsKeyword ) + rNames += OUStringChar( cQuote ); + rNames += aName; + if( cQuote && !bIsKeyword ) + rNames += OUStringChar( cQuote ); + } + } + + if( !bContainsKeyword && bGeneric ) + { + const char *pStr = nullptr; + switch( rFontItem.GetFamily() ) + { + case FAMILY_ROMAN: pStr = sCSS1_PV_serif; break; + case FAMILY_SWISS: pStr = sCSS1_PV_sans_serif; break; + case FAMILY_SCRIPT: pStr = sCSS1_PV_cursive; break; + case FAMILY_DECORATIVE: pStr = sCSS1_PV_fantasy; break; + case FAMILY_MODERN: pStr = sCSS1_PV_monospace; break; + default: + ; + } + + if( pStr ) + { + if( !rNames.isEmpty() ) + rNames += ", "; + rNames += OStringToOUString( pStr, RTL_TEXTENCODING_ASCII_US ); + } + } +} + +bool SwHTMLWriter::HasScriptDependentItems( const SfxItemSet& rItemSet, + bool bCheckDropCap ) +{ + static const sal_uInt16 aWhichIds[] = + { + RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT, + RES_CHRATR_FONTSIZE, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CTL_FONTSIZE, + RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, + RES_CHRATR_POSTURE, RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_POSTURE, + RES_CHRATR_WEIGHT, RES_CHRATR_CJK_WEIGHT, RES_CHRATR_CTL_WEIGHT, + 0, 0, 0 + }; + + for( int i=0; aWhichIds[i]; i += 3 ) + { + const SfxPoolItem *pItem = nullptr, *pItemCJK = nullptr, *pItemCTL = nullptr, *pTmp; + int nItemCount = 0; + if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i], false, + &pTmp ) ) + { + pItem = pTmp; + nItemCount++; + } + if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+1], false, + &pTmp ) ) + { + pItemCJK = pTmp; + nItemCount++; + } + if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+2], false, + &pTmp ) ) + { + pItemCTL = pTmp; + nItemCount++; + } + + // If some of the items are set, but not all, we need script dependent + // styles + if( nItemCount > 0 && nItemCount < 3 ) + return true; + + if( 3 == nItemCount ) + { + // If all items are set, but some of them have different values, + // we need script dependent styles, too. For font items, we have + // to take care about their special HTML/CSS1 representation. + if( RES_CHRATR_FONT == aWhichIds[i] ) + { + if( !swhtml_css1atr_equalFontItems( *pItem, *pItemCJK ) || + !swhtml_css1atr_equalFontItems( *pItem, *pItemCTL ) || + !swhtml_css1atr_equalFontItems( *pItemCJK, *pItemCTL ) ) + return true; + } + else + { + if( *pItem != *pItemCJK || + *pItem != *pItemCTL || + *pItemCJK != *pItemCTL ) + return true; + } + } + } + + const SfxPoolItem *pItem; + if( bCheckDropCap && + SfxItemState::SET == rItemSet.GetItemState( RES_PARATR_DROP, true, + &pItem ) ) + { + const SwFormatDrop *pDrop = static_cast<const SwFormatDrop *>(pItem); + const SwCharFormat *pDCCharFormat = pDrop->GetCharFormat(); + if( pDCCharFormat ) + { + //sequence of (start, end) property ranges we want to + //query + SfxItemSet aTstItemSet( + *pDCCharFormat->GetAttrSet().GetPool(), + svl::Items< + RES_CHRATR_FONT, RES_CHRATR_FONT, + RES_CHRATR_POSTURE, RES_CHRATR_POSTURE, + RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_FONT, + RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT>{}); + aTstItemSet.Set( pDCCharFormat->GetAttrSet() ); + return HasScriptDependentItems( aTstItemSet, false ); + } + } + + return false; +} + +static bool OutCSS1Rule( SwHTMLWriter& rHTMLWrt, const OUString& rSelector, + const SfxItemSet& rItemSet, bool bHasClass, + bool bCheckForPseudo ) +{ + bool bScriptDependent = false; + if( SwHTMLWriter::HasScriptDependentItems( rItemSet, bHasClass ) ) + { + bScriptDependent = true; + OUString aSelector( rSelector ); + + OUString aPseudo; + if( bCheckForPseudo ) + { + sal_Int32 nPos = aSelector.lastIndexOf( ':' ); + if( nPos >= 0 ) + { + aPseudo = aSelector.copy( nPos ); + aSelector =aSelector.copy( 0, nPos ); + } + } + + if( !bHasClass ) + { + // If we are exporting styles for a tag we have to export a tag + // rule for all properties that aren't style dependent and + // some class rule for the additional style dependen properties + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &rSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + } + + //sequence of (start, end) property ranges we want to + //query + SfxItemSet aScriptItemSet( *rItemSet.GetPool(), + svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE, + RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>{} ); + aScriptItemSet.Put( rItemSet ); + + OUString aNewSelector = aSelector + ".western" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( aScriptItemSet, false ); + } + + aNewSelector = aSelector + ".cjk" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( aScriptItemSet, false ); + } + + aNewSelector = aSelector + ".ctl" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( aScriptItemSet, false ); + } + } + else + { + // If there are script dependencies and we are derived from a tag, + // when we have to export a style dependent class for all + // scripts + OUString aNewSelector = aSelector + "-western" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + } + + aNewSelector = aSelector + "-cjk" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + } + + aNewSelector = aSelector + "-ctl" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aNewSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + } + } + } + else + { + // If there are no script dependencies, when all items are + // exported in one step. For hyperlinks only, a script information + // must be there, because these two chr formats don't support + // script dependencies by now. + SwCSS1OutMode aMode( rHTMLWrt, + rHTMLWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &rSelector ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + } + + return bScriptDependent; +} + +static void OutCSS1DropCapRule( + SwHTMLWriter& rHTMLWrt, const OUString& rSelector, + const SwFormatDrop& rDrop, bool bHasClass, + bool bHasScriptDependencies ) +{ + const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat(); + if( (bHasScriptDependencies && bHasClass) || + (pDCCharFormat && SwHTMLWriter::HasScriptDependentItems( pDCCharFormat->GetAttrSet(), false ) ) ) + { + OUString aSelector( rSelector ); + + OUString aPseudo; + sal_Int32 nPos = aSelector.lastIndexOf( ':' ); + if( nPos >= 0 ) + { + aPseudo = aSelector.copy( nPos ); + aSelector = aSelector.copy( 0, nPos ); + } + + if( !bHasClass ) + { + // If we are exporting styles for a tag we have to export a tag + // rule for all properties that aren't style dependent and + // some class rule for the additional style dependen properties + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &rSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop ); + } + + SfxItemSet aScriptItemSet( rHTMLWrt.m_pDoc->GetAttrPool(), + svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE, + RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>{} ); + if( pDCCharFormat ) + aScriptItemSet.Set( pDCCharFormat->GetAttrSet() ); + + OUString aNewSelector = aSelector + ".western" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop, &aScriptItemSet ); + } + + aNewSelector = aSelector + ".cjk" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop, &aScriptItemSet ); + } + + aNewSelector = aSelector + ".ctl" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop, &aScriptItemSet ); + } + } + else + { + // If there are script dependencies and we are derived from a tag, + // when we have to export a style dependent class for all + // scripts + OUString aNewSelector = aSelector + "-western" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop ); + } + + aNewSelector = aSelector + "-cjk" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop ); + } + + aNewSelector = aSelector + "-ctl" + aPseudo; + { + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &aNewSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop ); + } + } + } + else + { + // If there are no script dependencies, when all items are + // exported in one step. For hyperlinks only, a script information + // must be there, because these two chr formats don't support + // script dependencies by now. + SwCSS1OutMode aMode( rHTMLWrt, + rHTMLWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP, + &rSelector ); + OutCSS1_SwFormatDropAttrs( rHTMLWrt, rDrop ); + } +} + +static Writer& OutCSS1_SwFormat( Writer& rWrt, const SwFormat& rFormat, + IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + bool bCharFormat = false; + switch( rFormat.Which() ) + { + case RES_CHRFMT: + bCharFormat = true; + break; + + case RES_TXTFMTCOLL: + case RES_CONDTXTFMTCOLL: + // these template-types can be exported + break; + + default: + // but not these + return rWrt; + } + + // determine Selector and the to-be-exported Attr-Set-depth + OUString aSelector; + sal_uInt16 nRefPoolId = 0; + sal_uInt16 nDeep = GetCSS1Selector( &rFormat, aSelector, nRefPoolId ); + if( !nDeep ) + return rWrt; // not derived from a HTML-template + + sal_uInt16 nPoolFormatId = rFormat.GetPoolFormatId(); + + // Determine the to-be-exported Attr-Set. We have to distinguish 3 cases: + // - HTML-Tag templates (nDeep==USHRT_MAX): + // Export Attrs... + // - that are set in the template, but not in the original of the HTML template + // - the Default-Attrs for the Attrs, that are set in the Original of the + // HTML template, but not in the current template. + // - templates directly derived from HTML templates (nDeep==1): + // Export only Attributes of the template Item-Set w/o its parents. + // - templates in-directly derived from HTML templates (nDeep>1): + // Export Attributes of the template Item-Set incl. its Parents, + // but w/o Attributes that are set in the HTML-Tag template. + + // create Item-Set with all Attributes from the template + // (all but for nDeep==1) + const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet(); + SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() ); + aItemSet.Set( rFormatItemSet ); // Was nDeep!=1 that is not working + // for script dependent items but should + // not make a difference for any other + + bool bSetDefaults = true, bClearSame = true; + const SwFormat *pRefFormat = nullptr; + const SwFormat *pRefFormatScript = nullptr; + switch( nDeep ) + { + case CSS1_FMT_ISTAG: + pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() ); + break; + case CSS1_FMT_CMPREF: + pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pDoc ); + pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() ); + bClearSame = false; + break; + default: + pRefFormat = SwHTMLWriter::GetParentFormat( rFormat, nDeep ); + pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() ); + bSetDefaults = false; + break; + } + + if( pRefFormat ) + { + // subtract Item-Set of the Reference template (incl. its Parents) + SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(), + bSetDefaults, bClearSame, + pRefFormatScript + ? &pRefFormatScript->GetAttrSet() + : nullptr ); + + if( !bCharFormat ) + { + const SvxULSpaceItem& rULItem = pRefFormat->GetULSpace(); + rHTMLWrt.m_nDfltTopMargin = rULItem.GetUpper(); + rHTMLWrt.m_nDfltBottomMargin = rULItem.GetLower(); + } + } + else if( CSS1_FMT_ISTAG==nDeep && !bCharFormat ) + { + // set Default-distance above and below (for the + // case that there is no reference template) + rHTMLWrt.m_nDfltTopMargin = 0; + rHTMLWrt.m_nDfltBottomMargin = HTML_PARSPACE; + if( USER_FMT & nPoolFormatId ) + { + // user templates + const OUString& aNm(rFormat.GetName()); + + if (aNm == "DD 1" || aNm == "DT 1") + rHTMLWrt.m_nDfltBottomMargin = 0; + else if (aNm == OOO_STRING_SVTOOLS_HTML_listing) + rHTMLWrt.m_nDfltBottomMargin = 0; + else if (aNm == OOO_STRING_SVTOOLS_HTML_preformtxt) + rHTMLWrt.m_nDfltBottomMargin = 0; + else if (aNm == OOO_STRING_SVTOOLS_HTML_xmp) + rHTMLWrt.m_nDfltBottomMargin = 0; + } + else + { + // Pool templates + switch( nPoolFormatId ) + { + case RES_POOLCOLL_HEADLINE1: + case RES_POOLCOLL_HEADLINE2: + case RES_POOLCOLL_HEADLINE3: + case RES_POOLCOLL_HEADLINE4: + case RES_POOLCOLL_HEADLINE5: + case RES_POOLCOLL_HEADLINE6: + rHTMLWrt.m_nDfltTopMargin = HTML_HEADSPACE; + break; + case RES_POOLCOLL_SENDADRESS: + case RES_POOLCOLL_HTML_DT: + case RES_POOLCOLL_HTML_DD: + case RES_POOLCOLL_HTML_PRE: + rHTMLWrt.m_nDfltBottomMargin = 0; + break; + } + } + } + + // if nothing is to be exported ... + if( !aItemSet.Count() ) + return rWrt; + + // There is no support for script dependent hyperlinks by now. + bool bCheckForPseudo = false; + if( bCharFormat && + (RES_POOLCHR_INET_NORMAL==nRefPoolId || + RES_POOLCHR_INET_VISIT==nRefPoolId) ) + bCheckForPseudo = true; + + // export now the Attributes (incl. selector) + bool bHasScriptDependencies = false; + if( OutCSS1Rule( rHTMLWrt, aSelector, aItemSet, CSS1_FMT_ISTAG != nDeep, + bCheckForPseudo ) ) + { + if( bCharFormat ) + rHTMLWrt.m_aScriptTextStyles.insert( rFormat.GetName() ); + else + { + if( nPoolFormatId==RES_POOLCOLL_TEXT ) + rHTMLWrt.m_aScriptParaStyles.insert( pDoc->GetTextCollFromPool( RES_POOLCOLL_STANDARD, false )->GetName() ); + rHTMLWrt.m_aScriptParaStyles.insert( rFormat.GetName() ); + } + bHasScriptDependencies = true; + } + + // export Drop-Caps + const SfxPoolItem *pItem; + if( SfxItemState::SET==aItemSet.GetItemState( RES_PARATR_DROP, false, &pItem )) + { + OUString sOut = aSelector + + ":" + OStringToOUString( sCSS1_first_letter, RTL_TEXTENCODING_ASCII_US ); + const SwFormatDrop *pDrop = static_cast<const SwFormatDrop *>(pItem); + OutCSS1DropCapRule( rHTMLWrt, sOut, *pDrop, CSS1_FMT_ISTAG != nDeep, bHasScriptDependencies ); + } + + return rWrt; +} + +static Writer& OutCSS1_SwPageDesc( Writer& rWrt, const SwPageDesc& rPageDesc, + IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate, + sal_uInt16 nRefPoolId, bool bExtRef, + bool bPseudo ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwPageDesc* pRefPageDesc = nullptr; + if( !bExtRef ) + pRefPageDesc = pDoc->GetPageDescFromPool( nRefPoolId, false ); + else if( pTemplate ) + pRefPageDesc = pTemplate->getIDocumentStylePoolAccess().GetPageDescFromPool( nRefPoolId, false ); + + OUString aSelector = "@" + OStringToOUString( sCSS1_page, RTL_TEXTENCODING_ASCII_US ); + + if( bPseudo ) + { + const char *pPseudo = nullptr; + switch( rPageDesc.GetPoolFormatId() ) + { + case RES_POOLPAGE_FIRST: pPseudo = sCSS1_first; break; + case RES_POOLPAGE_LEFT: pPseudo = sCSS1_left; break; + case RES_POOLPAGE_RIGHT: pPseudo = sCSS1_right; break; + } + if( pPseudo ) + aSelector += ":" + OStringToOUString( pPseudo, RTL_TEXTENCODING_ASCII_US ); + } + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_RULE_ON|CSS1_OUTMODE_TEMPLATE, + &aSelector ); + + // Size: If the only difference is the Landscape-Flag, + // only export Portrait or Landscape. Otherwise export size. + bool bRefLandscape = pRefPageDesc && pRefPageDesc->GetLandscape(); + Size aRefSz; + const Size& rSz = rPageDesc.GetMaster().GetFrameSize().GetSize(); + if( pRefPageDesc ) + { + aRefSz = pRefPageDesc->GetMaster().GetFrameSize().GetSize(); + if( bRefLandscape != rPageDesc.GetLandscape() ) + { + long nTmp = aRefSz.Height(); + aRefSz.setHeight( aRefSz.Width() ); + aRefSz.setWidth( nTmp ); + } + } + + // TODO: Bad Hack: On the Page-Tabpage there are small rounding errors + // for the page size. Partially because of bug 25535, we stupidly still + // use the Size-Item from Dialog, even if nothing changed. + // Thus: once one visited the Page-Dialog and left it with OK, we get a + // new page size that then gets exported here. To avoid that, we allow + // here small deviations. + if( std::abs( rSz.Width() - aRefSz.Width() ) <= 2 && + std::abs( rSz.Height() - aRefSz.Height() ) <= 2 ) + { + if( bRefLandscape != rPageDesc.GetLandscape() ) + { + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_size, + rPageDesc.GetLandscape() ? sCSS1_PV_landscape + : sCSS1_PV_portrait ); + } + } + else + { + OStringBuffer sVal; + AddUnitPropertyValue(sVal, rSz.Width(), rHTMLWrt.GetCSS1Unit()); + sVal.append(' '); + AddUnitPropertyValue(sVal, rSz.Height(), rHTMLWrt.GetCSS1Unit()); + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_size, sVal.makeStringAndClear()); + } + + // Export the distance-Attributes as normally + const SwFrameFormat &rMaster = rPageDesc.GetMaster(); + SfxItemSet aItemSet( *rMaster.GetAttrSet().GetPool(), + svl::Items<RES_LR_SPACE, RES_UL_SPACE>{} ); + aItemSet.Set( rMaster.GetAttrSet() ); + + if( pRefPageDesc ) + { + SwHTMLWriter::SubtractItemSet( aItemSet, + pRefPageDesc->GetMaster().GetAttrSet(), + true ); + } + + OutCSS1_SvxULSpace_SvxLRSpace( rWrt, aItemSet ); + + // If for a Pseudo-Selector no Property had been set, we still + // have to export something, so that the corresponding template is + // created on the next import. + if( rHTMLWrt.m_bFirstCSS1Property && bPseudo ) + { + rHTMLWrt.OutNewLine(); + OString sTmp(OUStringToOString(aSelector, rHTMLWrt.m_eDestEnc)); + rWrt.Strm().WriteOString( sTmp ).WriteCharPtr( " {" ); + rHTMLWrt.m_bFirstCSS1Property = false; + } + + if( !rHTMLWrt.m_bFirstCSS1Property ) + rWrt.Strm().WriteCharPtr( sCSS1_rule_end ); + + return rWrt; +} + +static Writer& OutCSS1_SwFootnoteInfo( Writer& rWrt, const SwEndNoteInfo& rInfo, + SwDoc *pDoc, bool bHasNotes, bool bEndNote ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OUString aSelector; + + if( bHasNotes ) + { + aSelector = OOO_STRING_SVTOOLS_HTML_anchor "." + + ( bEndNote ? OUStringLiteral(OOO_STRING_SVTOOLS_HTML_sdendnote_anc) + : OUStringLiteral(OOO_STRING_SVTOOLS_HTML_sdfootnote_anc) ); + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE, + &aSelector ); + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_font_size, + sHTML_FTN_fontheight ); + rHTMLWrt.Strm().WriteCharPtr( sCSS1_rule_end ); + } + + const SwCharFormat *pSymCharFormat = rInfo.GetCharFormat( *pDoc ); + if( pSymCharFormat ) + { + const SfxItemSet& rFormatItemSet = pSymCharFormat->GetAttrSet(); + SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() ); + aItemSet.Set( rFormatItemSet ); + + // If there are footnotes or endnotes, then all Attributes have to be + // exported, so that Netscape displays the document correctly. + // Otherwise it is sufficient, to export the differences to the + // footnote and endnote template. + if( !bHasNotes && rHTMLWrt.m_xTemplate.is() ) + { + SwFormat *pRefFormat = rHTMLWrt.m_xTemplate->getIDocumentStylePoolAccess().GetCharFormatFromPool( + static_cast< sal_uInt16 >(bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE) ); + if( pRefFormat ) + SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(), + true ); + } + if( aItemSet.Count() ) + { + aSelector = OOO_STRING_SVTOOLS_HTML_anchor "." + + ( bEndNote ? OUStringLiteral(OOO_STRING_SVTOOLS_HTML_sdendnote_sym) + : OUStringLiteral(OOO_STRING_SVTOOLS_HTML_sdfootnote_sym)); + if( OutCSS1Rule( rHTMLWrt, aSelector, aItemSet, true, false )) + rHTMLWrt.m_aScriptTextStyles.insert( pSymCharFormat->GetName() ); + } + } + + return rWrt; +} + +Writer& OutCSS1_BodyTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_BODY, nullptr ); + + // Only export the attributes of the page template. + // The attributes of the default paragraph template were + // considered already when exporting the paragraph template. + + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + OUString rEmbeddedGraphicName; + OutCSS1_SvxBrush( rWrt, *pItem, Css1Background::Page, &rEmbeddedGraphicName ); + } + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false, + &pItem )) + { + OutCSS1_SvxBox( rWrt, *pItem ); + } + + if( !rHTMLWrt.m_bFirstCSS1Property ) + { + // if a Property was exported as part of a Style-Option, + // the Option still needs to be finished + rWrt.Strm().WriteChar( '\"' ); + } + + return rWrt; +} + +Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, rHTMLWrt.m_nCSS1Script|CSS1_OUTMODE_STYLE_OPT | + CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr ); + rHTMLWrt.OutCSS1_SfxItemSet( rItemSet, false ); + + return rWrt; +} + +// Wrapper for Table background +Writer& OutCSS1_TableBGStyleOpt( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_TABLEBOX, nullptr ); + OutCSS1_SvxBrush( rWrt, rHt, Css1Background::Table, nullptr ); + + if( !rHTMLWrt.m_bFirstCSS1Property ) + rWrt.Strm().WriteChar( '\"' ); + + return rWrt; +} + +Writer& OutCSS1_NumberBulletListStyleOpt( Writer& rWrt, const SwNumRule& rNumRule, + sal_uInt8 nLevel ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_STYLE_OPT | + CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr ); + + const SwNumFormat& rNumFormat = rNumRule.Get( nLevel ); + + long nLSpace = rNumFormat.GetAbsLSpace(); + long nFirstLineOffset = rNumFormat.GetFirstLineOffset(); + long nDfltFirstLineOffset = HTML_NUMBER_BULLET_INDENT; + if( nLevel > 0 ) + { + const SwNumFormat& rPrevNumFormat = rNumRule.Get( nLevel-1 ); + nLSpace -= rPrevNumFormat.GetAbsLSpace(); + nDfltFirstLineOffset = rPrevNumFormat.GetFirstLineOffset(); + } + + if( rHTMLWrt.IsHTMLMode(HTMLMODE_LSPACE_IN_NUMBER_BULLET) && + nLSpace != HTML_NUMBER_BULLET_MARGINLEFT ) + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLSpace ); + + if( rHTMLWrt.IsHTMLMode(HTMLMODE_FRSTLINE_IN_NUMBER_BULLET) && + nFirstLineOffset != nDfltFirstLineOffset ) + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent, nFirstLineOffset ); + + if( !rHTMLWrt.m_bFirstCSS1Property ) + rWrt.Strm().WriteChar( '\"' ); + + return rWrt; +} + +void SwHTMLWriter::OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat, + HtmlFrmOpts nFrameOpts, + const SdrObject *pSdrObj, + const SfxItemSet *pItemSet ) +{ + SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_FRAME, nullptr ); + + const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); + SvxLRSpaceItem aLRItem( rFrameFormat.GetLRSpace() ); + SvxULSpaceItem aULItem( rFrameFormat.GetULSpace() ); + if( nFrameOpts & HtmlFrmOpts::SAlign ) + { + const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor(); + switch( rAnchor.GetAnchorId() ) + { + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: + if( text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() || + text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() ) + { + if( !(nFrameOpts & HtmlFrmOpts::Align) ) + { + // float + const char *pStr = text::HoriOrientation::RIGHT==rHoriOri.GetHoriOrient() + ? sCSS1_PV_right + : sCSS1_PV_left; + OutCSS1_PropertyAscii( sCSS1_P_float, pStr ); + } + break; + } + [[fallthrough]]; + + case RndStdIds::FLY_AT_PAGE: + case RndStdIds::FLY_AT_FLY: + { + // position + OutCSS1_PropertyAscii( sCSS1_P_position, sCSS1_PV_absolute ); + + // For top/left we need to subtract the distance to the frame + // from the position, as in CSS1 it is added to the position. + // This works also for automatically aligned frames, even that + // in this case Writer also adds the distance; because in this + // case the Orient-Attribute contains the correct position. + + // top + long nXPos=0, nYPos=0; + bool bOutXPos = false, bOutYPos = false; + if( RES_DRAWFRMFMT == rFrameFormat.Which() ) + { + OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" ); + if( !pSdrObj ) + pSdrObj = rFrameFormat.FindSdrObject(); + OSL_ENSURE( pSdrObj, "Where is the SdrObject" ); + if( pSdrObj ) + { + Point aPos( pSdrObj->GetRelativePos() ); + nXPos = aPos.X(); + nYPos = aPos.Y(); + } + bOutXPos = bOutYPos = true; + } + else + { + bOutXPos = text::RelOrientation::CHAR != rHoriOri.GetRelationOrient(); + nXPos = text::HoriOrientation::NONE == rHoriOri.GetHoriOrient() + ? rHoriOri.GetPos() : 0; + + const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient(); + bOutYPos = text::RelOrientation::CHAR != rVertOri.GetRelationOrient(); + nYPos = text::VertOrientation::NONE == rVertOri.GetVertOrient() + ? rVertOri.GetPos() : 0; + } + + if( bOutYPos ) + { + if( IsHTMLMode( HTMLMODE_FLY_MARGINS) ) + { + nYPos -= aULItem.GetUpper(); + if( nYPos < 0 ) + { + aULItem.SetUpper( static_cast<sal_uInt16>(aULItem.GetUpper() + nYPos) ); + nYPos = 0; + } + } + + OutCSS1_UnitProperty( sCSS1_P_top, nYPos ); + } + + if( bOutXPos ) + { + // left + if( IsHTMLMode( HTMLMODE_FLY_MARGINS) ) + { + nXPos -= aLRItem.GetLeft(); + if( nXPos < 0 ) + { + aLRItem.SetLeft( static_cast<sal_uInt16>(aLRItem.GetLeft() + nXPos) ); + nXPos = 0; + } + } + + OutCSS1_UnitProperty( sCSS1_P_left, nXPos ); + } + } + break; + + default: + ; + } + } + + // width/height + if( nFrameOpts & HtmlFrmOpts::SSize ) + { + if( RES_DRAWFRMFMT == rFrameFormat.Which() ) + { + OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" ); + if( !pSdrObj ) + pSdrObj = rFrameFormat.FindSdrObject(); + OSL_ENSURE( pSdrObj, "Where is the SdrObject" ); + if( pSdrObj ) + { + Size aTwipSz( pSdrObj->GetLogicRect().GetSize() ); + if( nFrameOpts & HtmlFrmOpts::SWidth ) + { + if( nFrameOpts & HtmlFrmOpts::SPixSize ) + OutCSS1_PixelProperty( sCSS1_P_width, aTwipSz.Width(), + false ); + else + OutCSS1_UnitProperty( sCSS1_P_width, aTwipSz.Width() ); + } + if( nFrameOpts & HtmlFrmOpts::SHeight ) + { + if( nFrameOpts & HtmlFrmOpts::SPixSize ) + OutCSS1_PixelProperty( sCSS1_P_height, aTwipSz.Height(), + true ); + else + OutCSS1_UnitProperty( sCSS1_P_height, aTwipSz.Height() ); + } + } + } + else + { + OSL_ENSURE( HtmlFrmOpts::AbsSize & nFrameOpts, + "Export absolute size" ); + OSL_ENSURE( HtmlFrmOpts::AnySize & nFrameOpts, + "Export every size" ); + Css1FrameSize nMode = Css1FrameSize::NONE; + if( nFrameOpts & HtmlFrmOpts::SWidth ) + nMode |= Css1FrameSize::Width; + if( nFrameOpts & HtmlFrmOpts::SHeight ) + nMode |= Css1FrameSize::MinHeight|Css1FrameSize::FixHeight; + if( nFrameOpts & HtmlFrmOpts::SPixSize ) + nMode |= Css1FrameSize::Pixel; + + OutCSS1_SwFormatFrameSize( *this, rFrameFormat.GetFrameSize(), nMode ); + } + } + + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + // margin-* + if( (nFrameOpts & HtmlFrmOpts::SSpace) && + IsHTMLMode( HTMLMODE_FLY_MARGINS) ) + { + const SvxLRSpaceItem *pLRItem = nullptr; + const SvxULSpaceItem *pULItem = nullptr; + if( SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE ) ) + pLRItem = &aLRItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE ) ) + pULItem = &aULItem; + if( pLRItem || pULItem ) + OutCSS1_SvxULSpace_SvxLRSpace( *this, pULItem, pLRItem ); + } + + // border + if( nFrameOpts & HtmlFrmOpts::SBorder ) + { + const SfxPoolItem* pItem; + if( nFrameOpts & HtmlFrmOpts::SNoBorder ) + OutCSS1_SvxBox( *this, rFrameFormat.GetBox() ); + else if( SfxItemState::SET==rItemSet.GetItemState( RES_BOX, true, &pItem ) ) + OutCSS1_SvxBox( *this, *pItem ); + } + + // background (if, then the color must be set also) + if( nFrameOpts & HtmlFrmOpts::SBackground ) + OutCSS1_FrameFormatBackground( rFrameFormat ); + + if( pItemSet ) + OutCSS1_SfxItemSet( *pItemSet, false ); + + if( !m_bFirstCSS1Property ) + Strm().WriteChar( '\"' ); +} + +void SwHTMLWriter::OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat ) +{ + SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_TABLE, nullptr ); + + const SfxPoolItem *pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + OutCSS1_SvxBrush( *this, *pItem, Css1Background::Table, nullptr ); + + if( IsHTMLMode( HTMLMODE_PRINT_EXT ) ) + OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, false ); + + if( SfxItemState::SET==rItemSet.GetItemState( RES_LAYOUT_SPLIT, false, &pItem ) ) + OutCSS1_SwFormatLayoutSplit( *this, *pItem ); + + if( !m_bFirstCSS1Property ) + Strm().WriteChar( '\"' ); +} + +void SwHTMLWriter::OutCSS1_TableCellBorderHack(SwFrameFormat const& rFrameFormat) +{ + SwCSS1OutMode const aMode( *this, + CSS1_OUTMODE_STYLE_OPT_ON|CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_TABLEBOX, nullptr ); + OutCSS1_SvxBox(*this, rFrameFormat.GetBox()); + if (!m_bFirstCSS1Property) + { + Strm().WriteChar( cCSS1_style_opt_end ); + } +} + +void SwHTMLWriter::OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol ) +{ + SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_SECTION, nullptr ); + + const SfxPoolItem *pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + OutCSS1_SvxBrush( *this, *pItem, Css1Background::Section, nullptr ); + + if (pCol) + { + OString sColumnCount(OString::number(static_cast<sal_Int32>(pCol->GetNumCols()))); + OutCSS1_PropertyAscii(sCSS1_P_column_count, sColumnCount); + } + + if( !m_bFirstCSS1Property ) + Strm().WriteChar( '\"' ); +} + +static bool OutCSS1_FrameFormatBrush( SwHTMLWriter& rWrt, + const SvxBrushItem& rBrushItem ) +{ + bool bWritten = false; + /// output brush of frame format, if its background color is not "no fill"/"auto fill" + /// or it has a background graphic. + if( rBrushItem.GetColor() != COL_TRANSPARENT || + !rBrushItem.GetGraphicLink().isEmpty() || + 0 != rBrushItem.GetGraphicPos() ) + { + OutCSS1_SvxBrush( rWrt, rBrushItem, Css1Background::Fly, nullptr ); + bWritten = true; + } + return bWritten; +} + +void SwHTMLWriter::OutCSS1_FrameFormatBackground( const SwFrameFormat& rFrameFormat ) +{ + // If the frame itself has a background, then export. + if( OutCSS1_FrameFormatBrush( *this, *rFrameFormat.makeBackgroundBrushItem() ) ) + return; + + // If the frame is not linked to a page, we use the background of the anchor. + const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor(); + RndStdIds eAnchorId = rAnchor.GetAnchorId(); + const SwPosition *pAnchorPos = rAnchor.GetContentAnchor(); + if (RndStdIds::FLY_AT_PAGE != eAnchorId && pAnchorPos) + { + const SwNode& rNode = pAnchorPos->nNode.GetNode(); + if( rNode.IsContentNode() ) + { + // If the frame is linked to a content-node, + // we take the background of the content-node, if it has one. + if( OutCSS1_FrameFormatBrush( *this, + rNode.GetContentNode()->GetSwAttrSet().GetBackground()) ) + return; + + // Otherwise we also could be in a table + const SwTableNode *pTableNd = rNode.FindTableNode(); + if( pTableNd ) + { + const SwStartNode *pBoxSttNd = rNode.FindTableBoxStartNode(); + const SwTableBox *pBox = + pTableNd->GetTable().GetTableBox( pBoxSttNd->GetIndex() ); + + // If the box has a background, we take it. + if( OutCSS1_FrameFormatBrush( *this, + *pBox->GetFrameFormat()->makeBackgroundBrushItem() ) ) + return; + + // Otherwise we use that of the lines + const SwTableLine *pLine = pBox->GetUpper(); + while( pLine ) + { + if( OutCSS1_FrameFormatBrush( *this, + *pLine->GetFrameFormat()->makeBackgroundBrushItem() ) ) + return; + pBox = pLine->GetUpper(); + pLine = pBox ? pBox->GetUpper() : nullptr; + } + + // If there was none either, we use the background of the table. + if( OutCSS1_FrameFormatBrush( *this, + *pTableNd->GetTable().GetFrameFormat()->makeBackgroundBrushItem() ) ) + return; + } + + } + + // If the anchor is again in a Fly-Frame, use the background of the Fly-Frame. + const SwFrameFormat *pFrameFormat = rNode.GetFlyFormat(); + if( pFrameFormat ) + { + OutCSS1_FrameFormatBackground( *pFrameFormat ); + return; + } + } + + // At last there is the background of the page, and as the final rescue + // the value of the Config. + OSL_ENSURE( m_pCurrPageDesc, "no page template found" ); + if( !OutCSS1_FrameFormatBrush( *this, + *m_pCurrPageDesc->GetMaster().makeBackgroundBrushItem() ) ) + { + Color aColor( COL_WHITE ); + + // The background color is normally only used in Browse-Mode. + // We always use it for a HTML document, but for a text document + // only if viewed in Browse-Mode. + if( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) || + m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) + { + SwViewShell *pVSh = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if ( pVSh && + COL_TRANSPARENT != pVSh->GetViewOptions()->GetRetoucheColor()) + aColor = pVSh->GetViewOptions()->GetRetoucheColor(); + } + + OutCSS1_PropertyAscii(sCSS1_P_background, GetCSS1_Color(aColor)); + } +} + +static Writer& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( Writer& rWrt, + const SvxUnderlineItem *pUItem, + const SvxOverlineItem *pOItem, + const SvxCrossedOutItem *pCOItem, + const SvxBlinkItem *pBItem ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + bool bNone = false; + + const char *pUStr = nullptr; + if( pUItem ) + { + switch( pUItem->GetLineStyle() ) + { + case LINESTYLE_NONE: + bNone = true; + break; + case LINESTYLE_DONTKNOW: + break; + default: + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rHTMLWrt.mbReqIF, + "write underline as Hint?" ); + pUStr = sCSS1_PV_underline; + } + break; + } + } + + const char *pOStr = nullptr; + if( pOItem ) + { + switch( pOItem->GetLineStyle() ) + { + case LINESTYLE_NONE: + bNone = true; + break; + case LINESTYLE_DONTKNOW: + break; + default: + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write overline as Hint?" ); + pOStr = sCSS1_PV_overline; + } + break; + } + } + + const char *pCOStr = nullptr; + if( pCOItem ) + { + switch( pCOItem->GetStrikeout() ) + { + case STRIKEOUT_NONE: + bNone = true; + break; + case STRIKEOUT_DONTKNOW: + break; + default: + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rHTMLWrt.mbReqIF, + "write crossedOut as Hint?" ); + pCOStr = sCSS1_PV_line_through; + } + break; + } + } + + const char *pBStr = nullptr; + if( pBItem ) + { + if( !pBItem->GetValue() ) + { + bNone = true; + } + else if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write blink as Hint?" ); + pBStr = sCSS1_PV_blink; + } + } + + OStringBuffer sOut; + if( pUStr ) + sOut.append(pUStr); + + if( pOStr ) + { + if (!sOut.isEmpty()) + sOut.append(' '); + sOut.append(pOStr); + } + + if( pCOStr ) + { + if (!sOut.isEmpty()) + sOut.append(' '); + sOut.append(pCOStr); + } + + if( pBStr ) + { + if (!sOut.isEmpty()) + sOut.append(' '); + sOut.append(pBStr); + } + + if (!sOut.isEmpty()) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sOut.makeStringAndClear() ); + else if( bNone ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sCSS1_PV_none ); + + return rWrt; +} + +static Writer& OutCSS1_SvxCaseMap( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + switch( static_cast<const SvxCaseMapItem&>(rHt).GetCaseMap() ) + { + case SvxCaseMap::NotMapped: + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_normal ); + break; + case SvxCaseMap::SmallCaps: + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_small_caps ); + break; + case SvxCaseMap::Uppercase: + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_uppercase ); + break; + case SvxCaseMap::Lowercase: + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_lowercase ); + break; + case SvxCaseMap::Capitalize: + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_capitalize ); + break; + default: + ; + } + + return rWrt; +} + +static Writer& OutCSS1_SvxColor( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Colors do not need to be exported for Style-Option. + if( rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) && + !rHTMLWrt.m_bCfgPreferStyles ) + return rWrt; + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write color as Hint?" ); + + Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() ); + if( COL_AUTO == aColor ) + aColor = COL_BLACK; + + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_color, GetCSS1_Color(aColor)); + + return rWrt; +} + +static Writer& OutCSS1_SvxCrossedOut( Writer& rWrt, const SfxPoolItem& rHt ) +{ + // This function only exports Hints! + // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly. + + if( static_cast<SwHTMLWriter&>(rWrt).IsCSS1Source(CSS1_OUTMODE_HINT) ) + OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt, + nullptr, nullptr, static_cast<const SvxCrossedOutItem *>(&rHt), nullptr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFont( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // No need to export Fonts for the Style-Option. + if( rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + return rWrt; + + sal_uInt16 nScript = CSS1_OUTMODE_WESTERN; + switch( rHt.Which() ) + { + case RES_CHRATR_CJK_FONT: nScript = CSS1_OUTMODE_CJK; break; + case RES_CHRATR_CTL_FONT: nScript = CSS1_OUTMODE_CTL; break; + } + if( !rHTMLWrt.IsCSS1Script( nScript ) ) + return rWrt; + + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write Font as Hint?" ); + + OUString sOut; + // MS IE3b1 has problems with single quotes + sal_uInt16 nMode = rHTMLWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON; + sal_Unicode cQuote = nMode == CSS1_OUTMODE_RULE_ON ? '\"' : '\''; + SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), sOut, cQuote, + true ); + + rHTMLWrt.OutCSS1_Property( sCSS1_P_font_family, sOut ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFontHeight( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Font-Height need not be exported in the Style-Option. + // For Drop-Caps another Font-Size is exported. + if( rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) || + rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_DROPCAP ) ) + return rWrt; + + sal_uInt16 nScript = CSS1_OUTMODE_WESTERN; + switch( rHt.Which() ) + { + case RES_CHRATR_CJK_FONTSIZE: nScript = CSS1_OUTMODE_CJK; break; + case RES_CHRATR_CTL_FONTSIZE: nScript = CSS1_OUTMODE_CTL; break; + } + if( !rHTMLWrt.IsCSS1Script( nScript ) ) + return rWrt; + + sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight(); + OString sHeight(OString::number(nHeight/20) + sCSS1_UNIT_pt); + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sHeight); + + return rWrt; +} + +static Writer& OutCSS1_SvxPosture( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + sal_uInt16 nScript = CSS1_OUTMODE_WESTERN; + switch( rHt.Which() ) + { + case RES_CHRATR_CJK_POSTURE: nScript = CSS1_OUTMODE_CJK; break; + case RES_CHRATR_CTL_POSTURE: nScript = CSS1_OUTMODE_CTL; break; + } + if( !rHTMLWrt.IsCSS1Script( nScript ) ) + return rWrt; + + const char *pStr = nullptr; + switch( static_cast<const SvxPostureItem&>(rHt).GetPosture() ) + { + case ITALIC_NONE: pStr = sCSS1_PV_normal; break; + case ITALIC_OBLIQUE: pStr = sCSS1_PV_oblique; break; + case ITALIC_NORMAL: + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write italic as Hint?" ); + pStr = sCSS1_PV_italic; + } + break; + default: + ; + } + + if( pStr ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_font_style, pStr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxKerning( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + sal_Int16 nValue = static_cast<const SvxKerningItem&>(rHt).GetValue(); + if( nValue ) + { + OStringBuffer sOut; + if( nValue < 0 ) + { + sOut.append('-'); + nValue = -nValue; + } + + // Width as n.n pt + nValue = (nValue + 1) / 2; // 1/10pt + sOut.append(OString::number(nValue / 10) + "." + OString::number(nValue % 10) + + sCSS1_UNIT_pt); + + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_letter_spacing, + sOut.makeStringAndClear()); + } + else + { + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_letter_spacing, + sCSS1_PV_normal ); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxLanguage( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Only export Language rules + if( rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + return rWrt; + + sal_uInt16 nScript = CSS1_OUTMODE_WESTERN; + switch( rHt.Which() ) + { + case RES_CHRATR_CJK_LANGUAGE: nScript = CSS1_OUTMODE_CJK; break; + case RES_CHRATR_CTL_LANGUAGE: nScript = CSS1_OUTMODE_CTL; break; + } + if( !rHTMLWrt.IsCSS1Script( nScript ) ) + return rWrt; + + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write Language as Hint?" ); + + LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage(); + if( LANGUAGE_DONTKNOW == eLang ) + return rWrt; + + OUString sOut = LanguageTag::convertToBcp47( eLang ); + + rHTMLWrt.OutCSS1_Property( sCSS1_P_so_language, sOut ); + + return rWrt; +} + +static Writer& OutCSS1_SvxUnderline( Writer& rWrt, const SfxPoolItem& rHt ) +{ + // This function only exports Hints! + // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly. + + if( static_cast<SwHTMLWriter&>(rWrt).IsCSS1Source(CSS1_OUTMODE_HINT) ) + OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt, + static_cast<const SvxUnderlineItem *>(&rHt), nullptr, nullptr, nullptr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxOverline( Writer& rWrt, const SfxPoolItem& rHt ) +{ + // This function only exports Hints! + // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly. + + if( static_cast<SwHTMLWriter&>(rWrt).IsCSS1Source(CSS1_OUTMODE_HINT) ) + OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt, + nullptr, static_cast<const SvxOverlineItem *>(&rHt), nullptr, nullptr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxHidden( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if ( static_cast<const SvxCharHiddenItem&>(rHt).GetValue() ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_display, sCSS1_PV_none ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFontWeight( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + sal_uInt16 nScript = CSS1_OUTMODE_WESTERN; + switch( rHt.Which() ) + { + case RES_CHRATR_CJK_WEIGHT: nScript = CSS1_OUTMODE_CJK; break; + case RES_CHRATR_CTL_WEIGHT: nScript = CSS1_OUTMODE_CTL; break; + } + if( !rHTMLWrt.IsCSS1Script( nScript ) ) + return rWrt; + + const char *pStr = nullptr; + switch( static_cast<const SvxWeightItem&>(rHt).GetWeight() ) + { + case WEIGHT_ULTRALIGHT: pStr = sCSS1_PV_extra_light; break; + case WEIGHT_LIGHT: pStr = sCSS1_PV_light; break; + case WEIGHT_SEMILIGHT: pStr = sCSS1_PV_demi_light; break; + case WEIGHT_NORMAL: pStr = sCSS1_PV_normal; break; + case WEIGHT_SEMIBOLD: pStr = sCSS1_PV_demi_bold; break; + case WEIGHT_BOLD: + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + { + // this also works in HTML does not need to be written as + // a STYLE-Options, and must not be written as Hint + OSL_ENSURE( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT), + "write bold as Hint?" ); + pStr = sCSS1_PV_bold; + } + break; + case WEIGHT_ULTRABOLD: pStr = sCSS1_PV_extra_bold; break; + default: + pStr = sCSS1_PV_normal; + } + + if( pStr ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_font_weight, pStr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxBlink( Writer& rWrt, const SfxPoolItem& rHt ) +{ + // This function only exports Hints! + // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly. + + if( static_cast<SwHTMLWriter&>(rWrt).IsCSS1Source(CSS1_OUTMODE_HINT) ) + OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt, + nullptr, nullptr, nullptr, static_cast<const SvxBlinkItem *>(&rHt) ); + + return rWrt; +} + +static Writer& OutCSS1_SvxLineSpacing( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Netscape4 has big problems with cell heights if the line spacing is + // changed within a table and the width of the table is not calculated + // automatically (== if there is a WIDTH-Option) + if( rHTMLWrt.m_bOutTable && rHTMLWrt.m_bCfgNetscape4 ) + return rWrt; + + const SvxLineSpacingItem& rLSItem = static_cast<const SvxLineSpacingItem&>(rHt); + + sal_uInt16 nHeight = 0; + sal_uInt16 nPercentHeight = 0; + SvxLineSpaceRule eLineSpace = rLSItem.GetLineSpaceRule(); + switch( rLSItem.GetInterLineSpaceRule() ) + { + case SvxInterLineSpaceRule::Off: + case SvxInterLineSpaceRule::Fix: + { + switch( eLineSpace ) + { + case SvxLineSpaceRule::Min: + case SvxLineSpaceRule::Fix: + nHeight = rLSItem.GetLineHeight(); + break; + case SvxLineSpaceRule::Auto: + nPercentHeight = 100; + break; + default: + ; + } + } + break; + case SvxInterLineSpaceRule::Prop: + nPercentHeight = rLSItem.GetPropLineSpace(); + break; + + default: + ; + } + + if( nHeight ) + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_line_height, static_cast<long>(nHeight) ); + else if( nPercentHeight && + !(nPercentHeight < 115 && rHTMLWrt.m_bParaDotLeaders )) // avoid HTML scrollbars and missing descenders + { + OString sHeight(OString::number(nPercentHeight) + "%"); + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_line_height, sHeight); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxAdjust( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Export Alignment in Style-Option only if the Tag does not allow ALIGN=xxx + if( rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) && + !rHTMLWrt.m_bNoAlign) + return rWrt; + + const char* pStr = nullptr; + switch( static_cast<const SvxAdjustItem&>(rHt).GetAdjust() ) + { + case SvxAdjust::Left: pStr = sCSS1_PV_left; break; + case SvxAdjust::Right: pStr = sCSS1_PV_right; break; + case SvxAdjust::Block: pStr = sCSS1_PV_justify; break; + case SvxAdjust::Center: pStr = sCSS1_PV_center; break; + default: + ; + } + + if( pStr ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_text_align, pStr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFormatSplit( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const char *pStr = static_cast<const SvxFormatSplitItem&>(rHt).GetValue() + ? sCSS1_PV_auto + : sCSS1_PV_avoid; + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr ); + + return rWrt; +} + +static Writer& OutCSS1_SwFormatLayoutSplit( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const char *pStr = static_cast<const SwFormatLayoutSplit&>(rHt).GetValue() + ? sCSS1_PV_auto + : sCSS1_PV_avoid; + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxWidows( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OString aStr(OString::number(static_cast<const SvxWidowsItem&>(rHt).GetValue())); + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_widows, aStr ); + + return rWrt; +} + +static Writer& OutCSS1_SvxOrphans( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OString aStr(OString::number(static_cast<const SvxOrphansItem&>(rHt).GetValue())); + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_orphans, aStr ); + + return rWrt; +} + +static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt, + const SwFormatDrop& rDrop, + const SfxItemSet *pCharFormatItemSet ) +{ + // Text flows around on right side + rHWrt.OutCSS1_PropertyAscii( sCSS1_P_float, sCSS1_PV_left ); + + // number of lines -> use % for Font-Height! + OString sOut(OString::number(rDrop.GetLines()*100) + "%"); + rHWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sOut); + + // distance to Text = right margin + sal_uInt16 nDistance = rDrop.GetDistance(); + if( nDistance > 0 ) + rHWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, nDistance ); + + const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat(); + if( pCharFormatItemSet ) + rHWrt.OutCSS1_SfxItemSet( *pCharFormatItemSet ); + else if( pDCCharFormat ) + rHWrt.OutCSS1_SfxItemSet( pDCCharFormat->GetAttrSet() ); + else if( (rHWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF) == CSS1_OUTMODE_RULE_OFF ) + rHWrt.Strm().WriteCharPtr( sCSS1_rule_end ); + +} + +static Writer& OutCSS1_SwFormatDrop( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // never export as an Option of a paragraph, but only as Hints + if( !rHTMLWrt.IsCSS1Source(CSS1_OUTMODE_HINT) ) + return rWrt; + + if( rHTMLWrt.m_bTagOn ) + { + SwCSS1OutMode aMode( rHTMLWrt, + rHTMLWrt.m_nCSS1Script|CSS1_OUTMODE_SPAN_TAG1_ON|CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_DROPCAP, nullptr ); + + OutCSS1_SwFormatDropAttrs( rHTMLWrt, static_cast<const SwFormatDrop&>(rHt) ); + // A "> is already printed by the calling OutCSS1_HintAsSpanTag. + } + else + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false ); + } + + return rWrt; +} + +static Writer& OutCSS1_SwFormatFrameSize( Writer& rWrt, const SfxPoolItem& rHt, + Css1FrameSize nMode ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwFormatFrameSize& rFSItem = static_cast<const SwFormatFrameSize&>(rHt); + + if( nMode & Css1FrameSize::Width ) + { + sal_uInt8 nPercentWidth = rFSItem.GetWidthPercent(); + if( nPercentWidth ) + { + OString sOut(OString::number(nPercentWidth) + "%"); + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_width, sOut); + } + else if( nMode & Css1FrameSize::Pixel ) + { + rHTMLWrt.OutCSS1_PixelProperty( sCSS1_P_width, + rFSItem.GetSize().Width(), false ); + } + else + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_width, + rFSItem.GetSize().Width() ); + } + } + + if( nMode & Css1FrameSize::AnyHeight ) + { + bool bOutHeight = false; + switch( rFSItem.GetHeightSizeType() ) + { + case SwFrameSize::Fixed: + bOutHeight = bool(nMode & Css1FrameSize::FixHeight); + break; + case SwFrameSize::Minimum: + bOutHeight = bool(nMode & Css1FrameSize::MinHeight); + break; + default: + OSL_ENSURE( bOutHeight, "Height will not be exported" ); + break; + } + + if( bOutHeight ) + { + sal_uInt8 nPercentHeight = rFSItem.GetHeightPercent(); + if( nPercentHeight ) + { + OString sOut(OString::number(nPercentHeight) + "%"); + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_height, sOut); + } + else if( nMode & Css1FrameSize::Pixel ) + { + rHTMLWrt.OutCSS1_PixelProperty( sCSS1_P_height, + rFSItem.GetSize().Height(), + true ); + } + else + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_height, + rFSItem.GetSize().Height() ); + } + } + } + + return rWrt; +} + +static Writer& OutCSS1_SvxLRSpace( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SvxLRSpaceItem& rLRItem = static_cast<const SvxLRSpaceItem&>(rHt); + + // No Export of a firm attribute is needed if the new values + // match that of the current template + + // A left margin can exist because of a list nearby + long nLeftMargin = rLRItem.GetTextLeft() - rHTMLWrt.m_nLeftMargin; + if( rHTMLWrt.m_nDfltLeftMargin != nLeftMargin ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLeftMargin ); + + // max-width = max-width - margin-left for TOC paragraphs with dot leaders + if( rHTMLWrt.m_bParaDotLeaders ) + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_max_width, long(DOT_LEADERS_MAX_WIDTH/2.54*72*20) - nLeftMargin ); + + } + + if( rHTMLWrt.m_nDfltRightMargin != rLRItem.GetRight() ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, rLRItem.GetRight() ); + } + + // The LineIndent of the first line might contain the room for numbering + long nFirstLineIndent = static_cast<long>(rLRItem.GetTextFirstLineOffset()) - + rHTMLWrt.m_nFirstLineIndent; + if( rHTMLWrt.m_nDfltFirstLineIndent != nFirstLineIndent ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent, + nFirstLineIndent ); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxULSpace( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SvxULSpaceItem& rULItem = static_cast<const SvxULSpaceItem&>(rHt); + + if( rHTMLWrt.m_nDfltTopMargin != rULItem.GetUpper() ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin_top, + static_cast<long>(rULItem.GetUpper()) ); + } + + if( rHTMLWrt.m_nDfltBottomMargin != rULItem.GetLower() ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin_bottom, + static_cast<long>(rULItem.GetLower()) ); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt, + const SvxULSpaceItem *pULItem, + const SvxLRSpaceItem *pLRItem ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if( pLRItem && pULItem && + pLRItem->GetLeft() == pLRItem->GetRight() && + pLRItem->GetLeft() == pULItem->GetUpper() && + pLRItem->GetLeft() == pULItem->GetLower() && + pLRItem->GetLeft() != rHTMLWrt.m_nDfltLeftMargin && + pLRItem->GetRight() != rHTMLWrt.m_nDfltRightMargin && + pULItem->GetUpper() != rHTMLWrt.m_nDfltTopMargin && + pULItem->GetLower() != rHTMLWrt.m_nDfltBottomMargin ) + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_margin, pLRItem->GetLeft() ); + } + else + { + if( pLRItem ) + OutCSS1_SvxLRSpace( rWrt, *pLRItem ); + if( pULItem ) + OutCSS1_SvxULSpace( rWrt, *pULItem ); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxULSpace_SvxLRSpace( Writer& rWrt, + const SfxItemSet& rItemSet ) +{ + const SvxULSpaceItem *pULSpace = nullptr; + const SvxLRSpaceItem *pLRSpace = nullptr; + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, false/*bDeep*/, &pItem ) ) + pLRSpace = static_cast<const SvxLRSpaceItem *>(pItem); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE, false/*bDeep*/, &pItem ) ) + pULSpace = static_cast<const SvxULSpaceItem *>(pItem); + + if( pLRSpace || pULSpace ) + OutCSS1_SvxULSpace_SvxLRSpace( rWrt, pULSpace, pLRSpace ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( Writer& rWrt, + const SvxFormatBreakItem *pBreakItem, + const SwFormatPageDesc *pPDescItem, + const SvxFormatKeepItem *pKeepItem ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if( !rHTMLWrt.IsHTMLMode(HTMLMODE_PRINT_EXT) ) + return rWrt; + + const char *pBreakBefore = nullptr; + const char *pBreakAfter = nullptr; + + if( pKeepItem ) + { + pBreakAfter = pKeepItem->GetValue() ? sCSS1_PV_avoid : sCSS1_PV_auto; + } + if( pBreakItem ) + { + switch( pBreakItem->GetBreak() ) + { + case SvxBreak::NONE: + pBreakBefore = sCSS1_PV_auto; + if( !pBreakAfter ) + pBreakAfter = sCSS1_PV_auto; + break; + + case SvxBreak::PageBefore: + pBreakBefore = sCSS1_PV_always; + break; + + case SvxBreak::PageAfter: + pBreakAfter= sCSS1_PV_always; + break; + + default: + ; + } + } + if( pPDescItem ) + { + const SwPageDesc *pPDesc = pPDescItem->GetPageDesc(); + if( pPDesc ) + { + switch( pPDesc->GetPoolFormatId() ) + { + case RES_POOLPAGE_LEFT: pBreakBefore = sCSS1_PV_left; break; + case RES_POOLPAGE_RIGHT: pBreakBefore = sCSS1_PV_right; break; + default: pBreakBefore = sCSS1_PV_always; break; + } + } + else if( !pBreakBefore ) + { + pBreakBefore = sCSS1_PV_auto; + } + } + + if (rHTMLWrt.mbSkipHeaderFooter) + // No page break when writing only a fragment. + return rWrt; + + if( pBreakBefore ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_before, + pBreakBefore ); + if( pBreakAfter ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_after, + pBreakAfter ); + + return rWrt; +} + +static Writer& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( Writer& rWrt, + const SfxItemSet& rItemSet, + bool bDeep ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + const SfxPoolItem *pItem; + const SvxFormatBreakItem *pBreakItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_BREAK, bDeep, &pItem )) + pBreakItem = static_cast<const SvxFormatBreakItem *>(pItem); + + const SwFormatPageDesc *pPDescItem = nullptr; + if( ( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) || + !rHTMLWrt.m_bCSS1IgnoreFirstPageDesc || + rHTMLWrt.m_pStartNdIdx->GetIndex() != + rHTMLWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() ) && + SfxItemState::SET==rItemSet.GetItemState( RES_PAGEDESC, bDeep, &pItem )) + pPDescItem = static_cast<const SwFormatPageDesc*>(pItem); + + const SvxFormatKeepItem *pKeepItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_KEEP, bDeep, &pItem )) + pKeepItem = static_cast<const SvxFormatKeepItem *>(pItem); + + if( pBreakItem || pPDescItem || pKeepItem ) + OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( rWrt, pBreakItem, + pPDescItem, pKeepItem ); + + return rWrt; +} + +// Wrapper for OutCSS1_SfxItemSet etc. +static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt ) +{ + OutCSS1_SvxBrush( rWrt, rHt, Css1Background::Attr, nullptr ); + return rWrt; +} + +static Writer& OutCSS1_SvxBrush( Writer& rWrt, const SfxPoolItem& rHt, + Css1Background nMode, + const OUString* pGraphicName) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // The Character-Attribute is skipped, if we are about to + // exporting options + if( rHt.Which() < RES_CHRATR_END && + rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + return rWrt; + + // start getting a few values +// const Brush &rBrush = static_cast<const SvxBrushItem &>(rHt).GetBrush(); + const Color & rColor = static_cast<const SvxBrushItem &>(rHt).GetColor(); + OUString aLink = pGraphicName ? *pGraphicName + : static_cast<const SvxBrushItem &>(rHt).GetGraphicLink(); + SvxGraphicPosition ePos = static_cast<const SvxBrushItem &>(rHt).GetGraphicPos(); + if( Css1Background::Page == nMode && !rHTMLWrt.mbEmbedImages ) + { + // page style images are exported if not tiled + if( aLink.isEmpty() || GPOS_TILED==ePos ) + return rWrt; + } + + // get the color + bool bColor = false; + /// set <bTransparent> to true, if color is "no fill"/"auto fill" + bool bTransparent = (rColor == COL_TRANSPARENT); + Color aColor; + if( !bTransparent ) + { + aColor = rColor; + bColor = true; + } + + // and now the Graphic + OUString aGraphicInBase64; + OUString aGraphicAsLink; + + // Embedded Graphic -> export WriteEmbedded + const Graphic* pGrf = nullptr; + if( rHTMLWrt.mbEmbedImages || aLink.isEmpty()) + { + pGrf = static_cast<const SvxBrushItem &>(rHt).GetGraphic(); + if( pGrf ) + { + if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) ) + { + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + } + } + aLink.clear(); + } + else if( !pGraphicName && rHTMLWrt.m_bCfgCpyLinkedGrfs ) + { + aGraphicAsLink = aLink; + rWrt.CopyLocalFileToINet( aGraphicAsLink ); + aLink = aGraphicAsLink; + } + // In tables we only export something if there is a Graphic + if( Css1Background::Table==nMode && !pGrf && !aLink.isEmpty()) + return rWrt; + + // if necessary, add the orientation of the Graphic + const char *pRepeat = nullptr, *pHori = nullptr, *pVert = nullptr; + if( pGrf || !aLink.isEmpty() ) + { + if( GPOS_TILED==ePos ) + { + pRepeat = sCSS1_PV_repeat; + } + else + { + switch( ePos ) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + pHori = sCSS1_PV_top; + break; + + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + pHori = sCSS1_PV_middle; + break; + + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + pHori = sCSS1_PV_bottom; + break; + + default: + ; + } + + switch( ePos ) + { + case GPOS_LT: + case GPOS_LM: + case GPOS_LB: + pVert = sCSS1_PV_left; + break; + + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + pVert = sCSS1_PV_center; + break; + + case GPOS_RT: + case GPOS_RM: + case GPOS_RB: + pVert = sCSS1_PV_right; + break; + + default: + ; + } + + if( pHori || pVert ) + pRepeat = sCSS1_PV_no_repeat; + } + } + + // now build the string + OUString sOut; + if( !pGrf && aLink.isEmpty() && !bColor ) + { + // no color and no Link, but a transparent Brush + if( bTransparent && Css1Background::Fly != nMode ) + sOut += OStringToOUString(sCSS1_PV_transparent, RTL_TEXTENCODING_ASCII_US); + } + else + { + if( bColor ) + { + OString sTmp(GetCSS1_Color(aColor)); + sOut += OStringToOUString(sTmp, RTL_TEXTENCODING_ASCII_US); + } + + if( pGrf || !aLink.isEmpty() ) + { + if( bColor ) + sOut += " "; + + if(pGrf) + { + sOut += OStringToOUString(sCSS1_url, RTL_TEXTENCODING_ASCII_US) + + "(\'" OOO_STRING_SVTOOLS_HTML_O_data ":" + aGraphicInBase64 + "\')"; + } + else + { + sOut += OStringToOUString(sCSS1_url, RTL_TEXTENCODING_ASCII_US)+ + "(" + URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), + aLink) + ")"; + } + + if( pRepeat ) + { + sOut += " " + OStringToOUString(pRepeat, RTL_TEXTENCODING_ASCII_US); + } + + if( pHori ) + { + sOut += " " + OStringToOUString(pHori, RTL_TEXTENCODING_ASCII_US); + } + if( pVert ) + { + sOut += " " + OStringToOUString(pVert, RTL_TEXTENCODING_ASCII_US); + } + + sOut += " " + OStringToOUString(sCSS1_PV_scroll, RTL_TEXTENCODING_ASCII_US) + " "; + } + } + + if( !sOut.isEmpty() ) + rHTMLWrt.OutCSS1_Property( sCSS1_P_background, sOut ); + + return rWrt; +} + +static void OutCSS1_SvxBorderLine( SwHTMLWriter& rHTMLWrt, + const char *pProperty, + const SvxBorderLine *pLine ) +{ + if( !pLine || pLine->isEmpty() ) + { + rHTMLWrt.OutCSS1_PropertyAscii( pProperty, sCSS1_PV_none ); + return; + } + + sal_Int32 nWidth = pLine->GetWidth(); + + OStringBuffer sOut; + if( Application::GetDefaultDevice() && + nWidth <= Application::GetDefaultDevice()->PixelToLogic( + Size( 1, 1 ), MapMode( MapUnit::MapTwip) ).Width() ) + { + // If the width is smaller than one pixel, then export as 1px + // so that Netscape and IE show the line. + sOut.append("1px"); + } + else + { + nWidth *= 5; // 1/100pt + + // width in n.nn pt + sOut.append(OString::number(nWidth / 100) + "." + OString::number((nWidth/10) % 10) + + OString::number(nWidth % 10) + sCSS1_UNIT_pt); + } + + // Line-Style: solid or double + sOut.append(' '); + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + sOut.append(sCSS1_PV_solid); + break; + case SvxBorderLineStyle::DOTTED: + sOut.append(sCSS1_PV_dotted); + break; + case SvxBorderLineStyle::DASHED: + sOut.append(sCSS1_PV_dashed); + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + sOut.append(sCSS1_PV_double); + break; + case SvxBorderLineStyle::EMBOSSED: + sOut.append(sCSS1_PV_ridge); + break; + case SvxBorderLineStyle::ENGRAVED: + sOut.append(sCSS1_PV_groove); + break; + case SvxBorderLineStyle::INSET: + sOut.append(sCSS1_PV_inset); + break; + case SvxBorderLineStyle::OUTSET: + sOut.append(sCSS1_PV_outset); + break; + default: + sOut.append(sCSS1_PV_none); + } + sOut.append(' '); + + // and also the color + sOut.append(GetCSS1_Color(pLine->GetColor())); + + rHTMLWrt.OutCSS1_PropertyAscii(pProperty, sOut.makeStringAndClear()); +} + +Writer& OutCSS1_SvxBox( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // Avoid interference between character and paragraph attributes + if( rHt.Which() < RES_CHRATR_END && + rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ) + return rWrt; + + if( rHt.Which() == RES_CHRATR_BOX ) + { + if( rHTMLWrt.m_bTagOn ) + { + // Inline-block to make the line height changing correspond to the character border + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_display, "inline-block"); + } + else + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false ); + return rWrt; + } + } + + const SvxBoxItem& rBoxItem = static_cast<const SvxBoxItem&>(rHt); + const SvxBorderLine *pTop = rBoxItem.GetTop(); + const SvxBorderLine *pBottom = rBoxItem.GetBottom(); + const SvxBorderLine *pLeft = rBoxItem.GetLeft(); + const SvxBorderLine *pRight = rBoxItem.GetRight(); + + if( (pTop && pBottom && pLeft && pRight && + *pTop == *pBottom && *pTop == *pLeft && *pTop == *pRight) || + (!pTop && !pBottom && !pLeft && !pRight) ) + { + // all Lines are set and equal, or all Lines are not set + // => border : ... + OutCSS1_SvxBorderLine( rHTMLWrt, sCSS1_P_border, pTop ); + } + else + { + // otherwise export all Lines separately + OutCSS1_SvxBorderLine( rHTMLWrt, sCSS1_P_border_top, pTop ); + OutCSS1_SvxBorderLine( rHTMLWrt, sCSS1_P_border_bottom, pBottom ); + OutCSS1_SvxBorderLine( rHTMLWrt, sCSS1_P_border_left, pLeft ); + OutCSS1_SvxBorderLine( rHTMLWrt, sCSS1_P_border_right, pRight ); + } + + long nTopDist = pTop ? rBoxItem.GetDistance( SvxBoxItemLine::TOP ) : 0; + long nBottomDist = pBottom ? rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) : 0; + long nLeftDist = pLeft ? rBoxItem.GetDistance( SvxBoxItemLine::LEFT ) : 0; + long nRightDist = pRight ? rBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) : 0; + + if( nTopDist == nBottomDist && nLeftDist == nRightDist ) + { + OStringBuffer sVal; + AddUnitPropertyValue(sVal, nTopDist, rHTMLWrt.GetCSS1Unit()); + if( nTopDist != nLeftDist ) + { + sVal.append(' '); + AddUnitPropertyValue(sVal, nLeftDist, rHTMLWrt.GetCSS1Unit()); + } + rHTMLWrt.OutCSS1_PropertyAscii(sCSS1_P_padding, sVal.makeStringAndClear()); + } + else + { + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_padding_top, nTopDist ); + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_padding_bottom, nBottomDist ); + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_padding_left, nLeftDist ); + rHTMLWrt.OutCSS1_UnitProperty( sCSS1_P_padding_right, nRightDist ); + } + + return rWrt; +} + +static Writer& OutCSS1_SvxFrameDirection( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast< SwHTMLWriter& >( rWrt ); + + // Language will be exported rules only + if( !rHTMLWrt.IsCSS1Source( CSS1_OUTMODE_TEMPLATE ) ) + return rWrt; + + SvxFrameDirection nDir = + static_cast< const SvxFrameDirectionItem& >( rHt ).GetValue(); + const char* pStr = nullptr; + switch( nDir ) + { + case SvxFrameDirection::Horizontal_LR_TB: + case SvxFrameDirection::Vertical_LR_TB: + pStr = sCSS1_PV_ltr; + break; + case SvxFrameDirection::Horizontal_RL_TB: + case SvxFrameDirection::Vertical_RL_TB: + pStr = sCSS1_PV_rtl; + break; + case SvxFrameDirection::Environment: + pStr = sCSS1_PV_inherit; + break; + default: break; + } + + if( pStr ) + rHTMLWrt.OutCSS1_PropertyAscii( sCSS1_P_direction, pStr ); + + return rWrt; +} + +/* + * Place here the table for the HTML-Function-Pointer to the + * Export-Functions. + * They are local structures, only needed within the HTML-DLL. + */ + +static SwAttrFnTab const aCSS1AttrFnTab = { +/* RES_CHRATR_CASEMAP */ OutCSS1_SvxCaseMap, +/* RES_CHRATR_CHARSETCOLOR */ nullptr, +/* RES_CHRATR_COLOR */ OutCSS1_SvxColor, +/* RES_CHRATR_CONTOUR */ nullptr, +/* RES_CHRATR_CROSSEDOUT */ OutCSS1_SvxCrossedOut, +/* RES_CHRATR_ESCAPEMENT */ nullptr, +/* RES_CHRATR_FONT */ OutCSS1_SvxFont, +/* RES_CHRATR_FONTSIZE */ OutCSS1_SvxFontHeight, +/* RES_CHRATR_KERNING */ OutCSS1_SvxKerning, +/* RES_CHRATR_LANGUAGE */ OutCSS1_SvxLanguage, +/* RES_CHRATR_POSTURE */ OutCSS1_SvxPosture, +/* RES_CHRATR_UNUSED1*/ nullptr, +/* RES_CHRATR_SHADOWED */ nullptr, +/* RES_CHRATR_UNDERLINE */ OutCSS1_SvxUnderline, +/* RES_CHRATR_WEIGHT */ OutCSS1_SvxFontWeight, +/* RES_CHRATR_WORDLINEMODE */ nullptr, +/* RES_CHRATR_AUTOKERN */ nullptr, +/* RES_CHRATR_BLINK */ OutCSS1_SvxBlink, +/* RES_CHRATR_NOHYPHEN */ nullptr, // new: don't separate +/* RES_CHRATR_UNUSED2 */ nullptr, +/* RES_CHRATR_BACKGROUND */ OutCSS1_SvxBrush, // new: character background +/* RES_CHRATR_CJK_FONT */ OutCSS1_SvxFont, +/* RES_CHRATR_CJK_FONTSIZE */ OutCSS1_SvxFontHeight, +/* RES_CHRATR_CJK_LANGUAGE */ OutCSS1_SvxLanguage, +/* RES_CHRATR_CJK_POSTURE */ OutCSS1_SvxPosture, +/* RES_CHRATR_CJK_WEIGHT */ OutCSS1_SvxFontWeight, +/* RES_CHRATR_CTL_FONT */ OutCSS1_SvxFont, +/* RES_CHRATR_CTL_FONTSIZE */ OutCSS1_SvxFontHeight, +/* RES_CHRATR_CTL_LANGUAGE */ OutCSS1_SvxLanguage, +/* RES_CHRATR_CTL_POSTURE */ OutCSS1_SvxPosture, +/* RES_CHRATR_CTL_WEIGHT */ OutCSS1_SvxFontWeight, +/* RES_CHRATR_ROTATE */ nullptr, +/* RES_CHRATR_EMPHASIS_MARK */ nullptr, +/* RES_CHRATR_TWO_LINES */ nullptr, +/* RES_CHRATR_SCALEW */ nullptr, +/* RES_CHRATR_RELIEF */ nullptr, +/* RES_CHRATR_HIDDEN */ OutCSS1_SvxHidden, +/* RES_CHRATR_OVERLINE */ OutCSS1_SvxOverline, +/* RES_CHRATR_RSID */ nullptr, +/* RES_CHRATR_BOX */ OutCSS1_SvxBox, +/* RES_CHRATR_SHADOW */ nullptr, +/* RES_CHRATR_HIGHLIGHT */ nullptr, +/* RES_CHRATR_GRABBAG */ nullptr, +/* RES_CHRATR_BIDIRTL */ nullptr, +/* RES_CHRATR_IDCTHINT */ nullptr, + +/* RES_TXTATR_REFMARK */ nullptr, +/* RES_TXTATR_TOXMARK */ nullptr, +/* RES_TXTATR_META */ nullptr, +/* RES_TXTATR_METAFIELD */ nullptr, +/* RES_TXTATR_AUTOFMT */ nullptr, +/* RES_TXTATR_INETFMT */ nullptr, +/* RES_TXTATR_CHARFMT */ nullptr, +/* RES_TXTATR_CJK_RUBY */ nullptr, +/* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr, +/* RES_TXTATR_INPUTFIELD */ nullptr, + +/* RES_TXTATR_FIELD */ nullptr, +/* RES_TXTATR_FLYCNT */ nullptr, +/* RES_TXTATR_FTN */ nullptr, +/* RES_TXTATR_ANNOTATION */ nullptr, +/* RES_TXTATR_DUMMY3 */ nullptr, +/* RES_TXTATR_DUMMY1 */ nullptr, // Dummy: +/* RES_TXTATR_DUMMY2 */ nullptr, // Dummy: + +/* RES_PARATR_LINESPACING */ OutCSS1_SvxLineSpacing, +/* RES_PARATR_ADJUST */ OutCSS1_SvxAdjust, +/* RES_PARATR_SPLIT */ OutCSS1_SvxFormatSplit, +/* RES_PARATR_ORPHANS */ OutCSS1_SvxOrphans, +/* RES_PARATR_WIDOWS */ OutCSS1_SvxWidows, +/* RES_PARATR_TABSTOP */ nullptr, +/* RES_PARATR_HYPHENZONE*/ nullptr, +/* RES_PARATR_DROP */ OutCSS1_SwFormatDrop, +/* RES_PARATR_REGISTER */ nullptr, // new: register-true +/* RES_PARATR_NUMRULE */ nullptr, +/* RES_PARATR_SCRIPTSPACE */ nullptr, +/* RES_PARATR_HANGINGPUNCTUATION */ nullptr, +/* RES_PARATR_FORBIDDEN_RULES */ nullptr, // new +/* RES_PARATR_VERTALIGN */ nullptr, // new +/* RES_PARATR_SNAPTOGRID*/ nullptr, // new +/* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new +/* RES_PARATR_OUTLINELEVEL */ nullptr, // new since cws outlinelevel +/* RES_PARATR_RSID */ nullptr, // new +/* RES_PARATR_GRABBAG */ nullptr, + +/* RES_PARATR_LIST_ID */ nullptr, // new +/* RES_PARATR_LIST_LEVEL */ nullptr, // new +/* RES_PARATR_LIST_ISRESTART */ nullptr, // new +/* RES_PARATR_LIST_RESTARTVALUE */ nullptr, // new +/* RES_PARATR_LIST_ISCOUNTED */ nullptr, // new +/* RES_PARATR_LIST_AUTOFMT */ nullptr, // new + +/* RES_FILL_ORDER */ nullptr, +/* RES_FRM_SIZE */ nullptr, +/* RES_PAPER_BIN */ nullptr, +/* RES_LR_SPACE */ OutCSS1_SvxLRSpace, +/* RES_UL_SPACE */ OutCSS1_SvxULSpace, +/* RES_PAGEDESC */ nullptr, +/* RES_BREAK */ nullptr, +/* RES_CNTNT */ nullptr, +/* RES_HEADER */ nullptr, +/* RES_FOOTER */ nullptr, +/* RES_PRINT */ nullptr, +/* RES_OPAQUE */ nullptr, +/* RES_PROTECT */ nullptr, +/* RES_SURROUND */ nullptr, +/* RES_VERT_ORIENT */ nullptr, +/* RES_HORI_ORIENT */ nullptr, +/* RES_ANCHOR */ nullptr, +/* RES_BACKGROUND */ OutCSS1_SvxBrush, +/* RES_BOX */ OutCSS1_SvxBox, +/* RES_SHADOW */ nullptr, +/* RES_FRMMACRO */ nullptr, +/* RES_COL */ nullptr, +/* RES_KEEP */ nullptr, +/* RES_URL */ nullptr, +/* RES_EDIT_IN_READONLY */ nullptr, +/* RES_LAYOUT_SPLIT */ nullptr, +/* RES_CHAIN */ nullptr, +/* RES_TEXTGRID */ nullptr, +/* RES_LINENUMBER */ nullptr, +/* RES_FTN_AT_TXTEND */ nullptr, +/* RES_END_AT_TXTEND */ nullptr, +/* RES_COLUMNBALANCE */ nullptr, +/* RES_FRAMEDIR */ OutCSS1_SvxFrameDirection, +/* RES_HEADER_FOOTER_EAT_SPACING */ nullptr, +/* RES_ROW_SPLIT */ nullptr, +/* RES_FOLLOW_TEXT_FLOW */ nullptr, +/* RES_COLLAPSING_BORDERS */ nullptr, +/* RES_WRAP_INFLUENCE_ON_OBJPOS */ nullptr, +/* RES_AUTO_STYLE */ nullptr, +/* RES_FRMATR_STYLE_NAME */ nullptr, +/* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr, +/* RES_FRMATR_GRABBAG */ nullptr, +/* RES_TEXT_VERT_ADJUST */ nullptr, + +/* RES_GRFATR_MIRRORGRF */ nullptr, +/* RES_GRFATR_CROPGRF */ nullptr, +/* RES_GRFATR_ROTATION */ nullptr, +/* RES_GRFATR_LUMINANCE */ nullptr, +/* RES_GRFATR_CONTRAST */ nullptr, +/* RES_GRFATR_CHANNELR */ nullptr, +/* RES_GRFATR_CHANNELG */ nullptr, +/* RES_GRFATR_CHANNELB */ nullptr, +/* RES_GRFATR_GAMMA */ nullptr, +/* RES_GRFATR_INVERT */ nullptr, +/* RES_GRFATR_TRANSPARENCY */ nullptr, +/* RES_GRFATR_DRWAMODE */ nullptr, +/* RES_GRFATR_DUMMY1 */ nullptr, +/* RES_GRFATR_DUMMY2 */ nullptr, +/* RES_GRFATR_DUMMY3 */ nullptr, +/* RES_GRFATR_DUMMY4 */ nullptr, +/* RES_GRFATR_DUMMY5 */ nullptr, + +/* RES_BOXATR_FORMAT */ nullptr, +/* RES_BOXATR_FORMULA */ nullptr, +/* RES_BOXATR_VALUE */ nullptr +}; + +static_assert(SAL_N_ELEMENTS(aCSS1AttrFnTab) == RES_BOXATR_END); + +void SwHTMLWriter::OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, + bool bDeep ) +{ + // print ItemSet, including all attributes + Out_SfxItemSet( aCSS1AttrFnTab, *this, rItemSet, bDeep ); + + // some Attributes require special treatment + const SfxPoolItem *pItem = nullptr; + + // Underline, Overline, CrossedOut and Blink form together a CSS1-Property + // (doesn't work of course for Hints) + if( !IsCSS1Source(CSS1_OUTMODE_HINT) ) + { + const SvxUnderlineItem *pUnderlineItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_CHRATR_UNDERLINE, bDeep, &pItem )) + pUnderlineItem = static_cast<const SvxUnderlineItem *>(pItem); + + const SvxOverlineItem *pOverlineItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_CHRATR_OVERLINE, bDeep, &pItem )) + pOverlineItem = static_cast<const SvxOverlineItem *>(pItem); + + const SvxCrossedOutItem *pCrossedOutItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_CHRATR_CROSSEDOUT, bDeep, &pItem )) + pCrossedOutItem = static_cast<const SvxCrossedOutItem *>(pItem); + + const SvxBlinkItem *pBlinkItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_CHRATR_BLINK, bDeep, &pItem )) + pBlinkItem = static_cast<const SvxBlinkItem *>(pItem); + + if( pUnderlineItem || pOverlineItem || pCrossedOutItem || pBlinkItem ) + OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( *this, pUnderlineItem, + pOverlineItem, + pCrossedOutItem, + pBlinkItem ); + + OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, bDeep ); + } + + if( !m_bFirstCSS1Property ) + { + // if a Property was exported as part of a Style-Option, + // the Option still needs to be finished + OStringBuffer sOut; + switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF ) + { + case CSS1_OUTMODE_SPAN_TAG_OFF: + sOut.append(sCSS1_span_tag_end); + break; + + case CSS1_OUTMODE_STYLE_OPT_OFF: + sOut.append(cCSS1_style_opt_end); + break; + + case CSS1_OUTMODE_RULE_OFF: + sOut.append(sCSS1_rule_end); + break; + } + if (!sOut.isEmpty()) + Strm().WriteOString( sOut.makeStringAndClear() ); + } +} + +Writer& OutCSS1_HintSpanTag( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_SPAN_TAG | + CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_HINT, nullptr ); + + Out( aCSS1AttrFnTab, rHt, rWrt ); + + if( !rHTMLWrt.m_bFirstCSS1Property && rHTMLWrt.m_bTagOn ) + rWrt.Strm().WriteCharPtr( sCSS1_span_tag_end ); + + return rWrt; +} + +Writer& OutCSS1_HintStyleOpt( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwCSS1OutMode aMode( rHTMLWrt, CSS1_OUTMODE_STYLE_OPT_ON | + CSS1_OUTMODE_ENCODE| + CSS1_OUTMODE_HINT, nullptr ); + + Out( aCSS1AttrFnTab, rHt, rWrt ); + + if( !rHTMLWrt.m_bFirstCSS1Property ) + rWrt.Strm().WriteChar( '\"' ); + + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1atr.hxx b/sw/source/filter/html/css1atr.hxx new file mode 100644 index 000000000..ea07dec5c --- /dev/null +++ b/sw/source/filter/html/css1atr.hxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_CSS1ATR_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_CSS1ATR_HXX + +bool swhtml_css1atr_equalFontItems( const SfxPoolItem& r1, const SfxPoolItem& r2 ); + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1kywd.cxx b/sw/source/filter/html/css1kywd.cxx new file mode 100644 index 000000000..f8914dedb --- /dev/null +++ b/sw/source/filter/html/css1kywd.cxx @@ -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 . + */ + +#include "css1kywd.hxx" + +const char* const sCSS_mimetype = "text/css"; + +const char* const sCSS1_import = "import"; + +const char* const sCSS1_page = "page"; +//const char* const sCSS1_media = "media"; + +const char* const sCSS1_important = "important"; + +const char* const sCSS1_link = "link"; +const char* const sCSS1_visited = "visited"; +const char* const sCSS1_first_letter = "first-letter"; + +const char* const sCSS1_left = "left"; +const char* const sCSS1_right = "right"; +const char* const sCSS1_first = "first"; + +const char* const sCSS1_url = "url"; +const char* const sCSS1_rgb = "rgb"; + +const char* const sCSS1_UNIT_pt = "pt"; +const char* const sCSS1_UNIT_mm = "mm"; +const char* const sCSS1_UNIT_cm = "cm"; +const char* const sCSS1_UNIT_pc = "pc"; +const char* const sCSS1_UNIT_inch = "in"; +const char* const sCSS1_UNIT_px = "px"; +const char* const sCSS1_UNIT_em = "em"; +const char* const sCSS1_UNIT_ex = "ex"; + +// Strings for font properties + +const char* const sCSS1_P_font_family = "font-family"; + +const char* const sCSS1_PV_serif = "serif"; +const char* const sCSS1_PV_sans_serif = "sans-serif"; +const char* const sCSS1_PV_cursive = "cursive"; +const char* const sCSS1_PV_fantasy = "fantasy"; +const char* const sCSS1_PV_monospace = "monospace"; + +const char* const sCSS1_P_font_style = "font-style"; + +const char* const sCSS1_PV_normal = "normal"; +const char* const sCSS1_PV_italic = "italic"; +const char* const sCSS1_PV_oblique = "oblique"; + +const char* const sCSS1_P_font_variant = "font-variant"; + +//const char* const sCSS1_PV_normal = "normal"; +const char* const sCSS1_PV_small_caps = "small-caps"; + +const char* const sCSS1_P_font_weight = "font-weight"; + +const char* const sCSS1_PV_extra_light = "extra-light"; +const char* const sCSS1_PV_light = "light"; +const char* const sCSS1_PV_demi_light = "demi-light"; +//const char* const sCSS1_PV_medium = "medium"; +const char* const sCSS1_PV_demi_bold = "demi-bold"; +const char* const sCSS1_PV_bold = "bold"; +const char* const sCSS1_PV_extra_bold = "extra-bold"; +const char* const sCSS1_PV_lighter = "lighter"; +const char* const sCSS1_PV_bolder = "bolder"; + +const char* const sCSS1_P_text_transform = "text-transform"; + +const char* const sCSS1_PV_capitalize = "capitalize"; +const char* const sCSS1_PV_uppercase = "uppercase"; +const char* const sCSS1_PV_lowercase = "lowercase"; + +const char* const sCSS1_P_font_size = "font-size"; + +const char* const sCSS1_PV_xx_small = "xx-small"; +const char* const sCSS1_PV_x_small = "x-small"; +const char* const sCSS1_PV_small = "small"; +const char* const sCSS1_PV_medium = "medium"; +const char* const sCSS1_PV_large = "large"; +const char* const sCSS1_PV_x_large = "x-large"; +const char* const sCSS1_PV_xx_large = "xx-large"; + +const char* const sCSS1_PV_larger = "larger"; +const char* const sCSS1_PV_smaller = "smaller"; + +const char* const sCSS1_P_font = "font"; + +// Strings for color and background properties + +const char* const sCSS1_P_color = "color"; + +const char* const sCSS1_P_background = "background"; +const char* const sCSS1_P_background_color = "background-color"; + +const char* const sCSS1_PV_transparent = "transparent"; + +const char* const sCSS1_PV_repeat = "repeat"; +const char* const sCSS1_PV_repeat_x = "repeat-x"; +const char* const sCSS1_PV_repeat_y = "repeat-y"; +const char* const sCSS1_PV_no_repeat = "no-repeat"; + +const char* const sCSS1_PV_top = "top"; +const char* const sCSS1_PV_middle = "middle"; +const char* const sCSS1_PV_bottom = "bottom"; + +const char* const sCSS1_PV_scroll = "scroll"; + +// Strings for text properties + +const char* const sCSS1_P_letter_spacing = "letter-spacing"; + +const char* const sCSS1_P_text_decoration = "text-decoration"; + +const char* const sCSS1_PV_none = "none"; +const char* const sCSS1_PV_underline = "underline"; +const char* const sCSS1_PV_overline = "overline"; +const char* const sCSS1_PV_line_through = "line-through"; +const char* const sCSS1_PV_blink = "blink"; + +const char* const sCSS1_P_text_align = "text-align"; + +const char* const sCSS1_PV_left = "left"; +const char* const sCSS1_PV_center = "center"; +const char* const sCSS1_PV_right = "right"; +const char* const sCSS1_PV_justify = "justify"; + +const char* const sCSS1_P_text_indent = "text-indent"; + +const char* const sCSS1_P_line_height = "line-height"; +const char* const sCSS1_P_list_style_type = "list-style-type"; + +// Strings for box properties + +const char* const sCSS1_P_margin_left = "margin-left"; +const char* const sCSS1_P_margin_right = "margin-right"; +const char* const sCSS1_P_margin_top = "margin-top"; +const char* const sCSS1_P_margin_bottom = "margin-bottom"; +const char* const sCSS1_P_margin = "margin"; + +const char* const sCSS1_P_padding_top = "padding-top"; +const char* const sCSS1_P_padding_bottom = "padding-bottom"; +const char* const sCSS1_P_padding_left = "padding-left"; +const char* const sCSS1_P_padding_right = "padding-right"; +const char* const sCSS1_P_padding = "padding"; + +const char* const sCSS1_PV_auto = "auto"; + +const char* const sCSS1_P_border_left_width = "border-left-width"; +const char* const sCSS1_P_border_right_width = "border-right-width"; +const char* const sCSS1_P_border_top_width = "border-top-width"; +const char* const sCSS1_P_border_bottom_width = "border-bottom-width"; +const char* const sCSS1_P_border_width = "border-width"; +const char* const sCSS1_P_border_color = "border-color"; +const char* const sCSS1_P_border_style = "border-style"; +const char* const sCSS1_P_border_left = "border-left"; +const char* const sCSS1_P_border_right = "border-right"; +const char* const sCSS1_P_border_top = "border-top"; +const char* const sCSS1_P_border_bottom = "border-bottom"; +const char* const sCSS1_P_border = "border"; + +const char* const sCSS1_PV_thin = "thin"; +//const char* const sCSS1_PV_medium = "medium"; +const char* const sCSS1_PV_thick = "thick"; + +//const char* const sCSS1_PV_none = "none"; +const char* const sCSS1_PV_dotted = "dotted"; +const char* const sCSS1_PV_dashed = "dashed"; +const char* const sCSS1_PV_solid = "solid"; +const char* const sCSS1_PV_double = "double"; +const char* const sCSS1_PV_groove = "groove"; +const char* const sCSS1_PV_ridge = "ridge"; +const char* const sCSS1_PV_inset = "inset"; +const char* const sCSS1_PV_outset = "outset"; + +const char* const sCSS1_P_width = "width"; +const char* const sCSS1_P_max_width = "max-width"; + +const char* const sCSS1_P_height = "height"; + +const char* const sCSS1_P_float = "float"; + +const char* const sCSS1_P_column_count = "column-count"; + +// Strings for positioning + +const char* const sCSS1_P_position = "position"; + +const char* const sCSS1_PV_absolute = "absolute"; +const char* const sCSS1_PV_relative = "relative"; +const char* const sCSS1_PV_static = "static"; + +const char* const sCSS1_P_left = "left"; + +const char* const sCSS1_P_top = "top"; + +// Strings for printing extensions + +const char* const sCSS1_P_page_break_before = "page-break-before"; +const char* const sCSS1_P_page_break_after = "page-break-after"; +const char* const sCSS1_P_page_break_inside = "page-break-inside"; +const char* const sCSS1_P_size = "size"; +const char* const sCSS1_P_widows = "widows"; +const char* const sCSS1_P_visibility = "visibility"; +const char* const sCSS1_P_orphans = "orphans"; +//const char* const sCSS1_P_marks = "marks"; + +const char* const sCSS1_PV_always = "always"; +const char* const sCSS1_PV_avoid = "avoid"; + +const char* const sCSS1_PV_portrait = "portrait"; +const char* const sCSS1_PV_landscape = "landscape"; + +//const char* const sCSS1_PV_crop = "crop"; +//const char* const sCSS1_PV_cross = "cross"; + +const char* const sCSS1_class_abs_pos = "sd-abs-pos"; + +const char* const sCSS1_P_so_language = "so-language"; + +const char* const sCSS1_P_direction = "direction"; +const char* const sCSS1_PV_ltr = "ltr"; +const char* const sCSS1_PV_rtl = "rtl"; +const char* const sCSS1_PV_inherit = "inherit"; + +const char* const sCSS1_P_display = "display"; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1kywd.hxx b/sw/source/filter/html/css1kywd.hxx new file mode 100644 index 000000000..7c0e326fd --- /dev/null +++ b/sw/source/filter/html/css1kywd.hxx @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_CSS1KYWD_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_CSS1KYWD_HXX + +#include <sal/types.h> + +extern const char* const sCSS_mimetype; + +extern const char* const sCSS1_import; + +extern const char* const sCSS1_page; + +extern const char* const sCSS1_important; + +extern const char* const sCSS1_link; +extern const char* const sCSS1_visited; +extern const char* const sCSS1_first_letter; + +extern const char* const sCSS1_left; +extern const char* const sCSS1_right; +extern const char* const sCSS1_first; + +extern const char* const sCSS1_url; +extern const char* const sCSS1_rgb; + +extern const char* const sCSS1_UNIT_pt; +extern const char* const sCSS1_UNIT_mm; +extern const char* const sCSS1_UNIT_cm; +extern const char* const sCSS1_UNIT_pc; +extern const char* const sCSS1_UNIT_inch; +extern const char* const sCSS1_UNIT_px; +extern const char* const sCSS1_UNIT_em; +extern const char* const sCSS1_UNIT_ex; + +// Strings for font properties + +extern const char* const sCSS1_P_font_family; + +extern const char* const sCSS1_PV_serif; +extern const char* const sCSS1_PV_sans_serif; +extern const char* const sCSS1_PV_cursive; +extern const char* const sCSS1_PV_fantasy; +extern const char* const sCSS1_PV_monospace; + +extern const char* const sCSS1_P_font_style; + +extern const char* const sCSS1_PV_normal; +extern const char* const sCSS1_PV_italic; +extern const char* const sCSS1_PV_oblique; + +extern const char* const sCSS1_P_font_variant; + +//extern const char* const sCSS1_PV_normal; +extern const char* const sCSS1_PV_small_caps; + +extern const char* const sCSS1_P_text_transform; + +extern const char* const sCSS1_PV_capitalize; +extern const char* const sCSS1_PV_uppercase; +extern const char* const sCSS1_PV_lowercase; + +extern const char* const sCSS1_P_font_weight; + +extern const char* const sCSS1_PV_extra_light; +extern const char* const sCSS1_PV_light; +extern const char* const sCSS1_PV_demi_light; +//extern const char* const sCSS1_PV_medium; +extern const char* const sCSS1_PV_demi_bold; +extern const char* const sCSS1_PV_bold; +extern const char* const sCSS1_PV_extra_bold; +extern const char* const sCSS1_PV_lighter; +extern const char* const sCSS1_PV_bolder; + +extern const char* const sCSS1_P_font_size; + +extern const char* const sCSS1_PV_xx_small; +extern const char* const sCSS1_PV_x_small; +extern const char* const sCSS1_PV_small; +extern const char* const sCSS1_PV_medium; +extern const char* const sCSS1_PV_large; +extern const char* const sCSS1_PV_x_large; +extern const char* const sCSS1_PV_xx_large; + +extern const char* const sCSS1_PV_larger; +extern const char* const sCSS1_PV_smaller; + +extern const char* const sCSS1_P_font; + +// Strings for color and background properties + +extern const char* const sCSS1_P_color; + +extern const char* const sCSS1_P_background; +extern const char* const sCSS1_P_background_color; + +extern const char* const sCSS1_PV_transparent; + +extern const char* const sCSS1_PV_repeat; +extern const char* const sCSS1_PV_repeat_x; +extern const char* const sCSS1_PV_repeat_y; +extern const char* const sCSS1_PV_no_repeat; + +extern const char* const sCSS1_PV_top; +extern const char* const sCSS1_PV_middle; +extern const char* const sCSS1_PV_bottom; + +extern const char* const sCSS1_PV_scroll; + +// Strings for text properties + +extern const char* const sCSS1_P_letter_spacing; + +extern const char* const sCSS1_P_text_decoration; + +extern const char* const sCSS1_PV_none; +extern const char* const sCSS1_PV_underline; +extern const char* const sCSS1_PV_overline; +extern const char* const sCSS1_PV_line_through; +extern const char* const sCSS1_PV_blink; + +extern const char* const sCSS1_P_text_align; + +extern const char* const sCSS1_PV_left; +extern const char* const sCSS1_PV_center; +extern const char* const sCSS1_PV_right; +extern const char* const sCSS1_PV_justify; + +extern const char* const sCSS1_P_text_indent; + +extern const char* const sCSS1_P_line_height; + +extern const char* const sCSS1_P_list_style_type; + +// Strings for box properties + +extern const char* const sCSS1_P_margin_left; +extern const char* const sCSS1_P_margin_right; +extern const char* const sCSS1_P_margin_top; +extern const char* const sCSS1_P_margin_bottom; +extern const char* const sCSS1_P_margin; + +extern const char* const sCSS1_P_padding_top; +extern const char* const sCSS1_P_padding_bottom; +extern const char* const sCSS1_P_padding_left; +extern const char* const sCSS1_P_padding_right; +extern const char* const sCSS1_P_padding; + +extern const char* const sCSS1_PV_auto; + +extern const char* const sCSS1_P_border_left_width; +extern const char* const sCSS1_P_border_right_width; +extern const char* const sCSS1_P_border_top_width; +extern const char* const sCSS1_P_border_bottom_width; +extern const char* const sCSS1_P_border_width; +extern const char* const sCSS1_P_border_color; +extern const char* const sCSS1_P_border_style; +extern const char* const sCSS1_P_border_left; +extern const char* const sCSS1_P_border_right; +extern const char* const sCSS1_P_border_top; +extern const char* const sCSS1_P_border_bottom; +extern const char* const sCSS1_P_border; + +extern const char* const sCSS1_PV_thin; +//extern const char* const sCSS1_PV_medium; +extern const char* const sCSS1_PV_thick; + +//extern const char* const sCSS1_PV_none; +extern const char* const sCSS1_PV_dotted; +extern const char* const sCSS1_PV_dashed; +extern const char* const sCSS1_PV_solid; +extern const char* const sCSS1_PV_double; +extern const char* const sCSS1_PV_groove; +extern const char* const sCSS1_PV_ridge; +extern const char* const sCSS1_PV_inset; +extern const char* const sCSS1_PV_outset; + +extern const char* const sCSS1_P_width; +extern const char* const sCSS1_P_max_width; + +extern const char* const sCSS1_P_height; + +extern const char* const sCSS1_P_float; + +extern const char* const sCSS1_P_column_count; + +// Strings for positioning + +extern const char* const sCSS1_P_position; + +extern const char* const sCSS1_PV_absolute; +extern const char* const sCSS1_PV_relative; +extern const char* const sCSS1_PV_static; + +extern const char* const sCSS1_P_left; + +extern const char* const sCSS1_P_top; + +// Strings for printing extensions + +extern const char* const sCSS1_P_page_break_before; +extern const char* const sCSS1_P_page_break_after; +extern const char* const sCSS1_P_page_break_inside; +extern const char* const sCSS1_P_size; +extern const char* const sCSS1_P_widows; +extern const char* const sCSS1_P_visibility; +extern const char* const sCSS1_P_orphans; +//extern const char* const sCSS1_P_marks; + +extern const char* const sCSS1_PV_always; +extern const char* const sCSS1_PV_avoid; + +extern const char* const sCSS1_PV_portrait; +extern const char* const sCSS1_PV_landscape; + +//extern const char* const sCSS1_PV_crop; +//extern const char* const sCSS1_PV_cross; + + +extern const char* const sCSS1_class_abs_pos; + +extern const char* const sCSS1_P_so_language; +extern const char* const sCSS1_P_direction; +extern const char* const sCSS1_PV_ltr; +extern const char* const sCSS1_PV_rtl; +extern const char* const sCSS1_PV_inherit; + +extern const char* const sCSS1_P_display; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx new file mode 100644 index 000000000..83a02160b --- /dev/null +++ b/sw/source/filter/html/htmlatr.cxx @@ -0,0 +1,3364 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmltokn.h> +#include <svl/whiter.hxx> +#include <sfx2/event.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <fmtfsize.hxx> +#include <fmtclds.hxx> +#include <fmtpdsc.hxx> +#include <fmtflcnt.hxx> +#include <fmtinfmt.hxx> +#include <txatbase.hxx> +#include <frmatr.hxx> +#include <charfmt.hxx> +#include <fmtfld.hxx> +#include <doc.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <paratr.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <swtable.hxx> +#include <fldbas.hxx> +#include <breakit.hxx> +#include "htmlatr.hxx" +#include "htmlnum.hxx" +#include "wrthtml.hxx" +#include "htmlfly.hxx" +#include <numrule.hxx> +#include <rtl/character.hxx> +#include <osl/diagnose.h> +#include <deque> + +#include <svtools/HtmlWriter.hxx> + +#include <memory> +#include <algorithm> + +using namespace css; + +HTMLOutEvent const aAnchorEventTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_O_SDonclick, OOO_STRING_SVTOOLS_HTML_O_onclick, SvMacroItemId::OnClick }, + { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver }, + { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut }, + { nullptr, nullptr, SvMacroItemId::NONE } +}; + +static Writer& OutHTML_SvxAdjust( Writer& rWrt, const SfxPoolItem& rHt ); + +sal_uInt16 SwHTMLWriter::GetDefListLvl( const OUString& rNm, sal_uInt16 nPoolId ) +{ + if( nPoolId == RES_POOLCOLL_HTML_DD ) + { + return 1 | HTML_DLCOLL_DD; + } + else if( nPoolId == RES_POOLCOLL_HTML_DT ) + { + return 1 | HTML_DLCOLL_DT; + } + + OUString sDTDD = OOO_STRING_SVTOOLS_HTML_dt " "; + if( rNm.startsWith(sDTDD) ) + // DefinitionList - term + return static_cast<sal_uInt16>(rNm.copy( sDTDD.getLength() ).toInt32()) | HTML_DLCOLL_DT; + + sDTDD = OOO_STRING_SVTOOLS_HTML_dd " "; + if( rNm.startsWith(sDTDD) ) + // DefinitionList - definition + return static_cast<sal_uInt16>(rNm.copy( sDTDD.getLength() ).toInt32()) | HTML_DLCOLL_DD; + + return 0; +} + +void SwHTMLWriter::OutAndSetDefList( sal_uInt16 nNewLvl ) +{ + // possibly, we first need to start a new list + if( m_nDefListLvl < nNewLvl ) + { + // output </pre> for the previous(!) paragraph, if required. + // Preferable, the <pre> is exported by OutHTML_SwFormatOff for the + // previous paragraph already, but that's not possible, because a very + // deep look at the next paragraph (this one) is required to figure + // out that a def list starts here. + + ChangeParaToken( HtmlTokenId::NONE ); + + // write according to the level difference + for( sal_uInt16 i=m_nDefListLvl; i<nNewLvl; i++ ) + { + if( m_bLFPossible ) + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist ); + IncIndentLevel(); + m_bLFPossible = true; + } + } + else if( m_nDefListLvl > nNewLvl ) + { + for( sal_uInt16 i=nNewLvl ; i < m_nDefListLvl; i++ ) + { + DecIndentLevel(); + if( m_bLFPossible ) + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_deflist, false ); + m_bLFPossible = true; + } + } + + m_nDefListLvl = nNewLvl; +} + +void SwHTMLWriter::ChangeParaToken( HtmlTokenId nNew ) +{ + if( nNew != m_nLastParaToken && HtmlTokenId::PREFORMTXT_ON == m_nLastParaToken ) + { + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_preformtxt, false ); + m_bLFPossible = true; + } + m_nLastParaToken = nNew; +} + +sal_uInt16 SwHTMLWriter::GetCSS1ScriptForScriptType( sal_uInt16 nScriptType ) +{ + sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT; + + switch( nScriptType ) + { + case i18n::ScriptType::LATIN: + nRet = CSS1_OUTMODE_WESTERN; + break; + case i18n::ScriptType::ASIAN: + nRet = CSS1_OUTMODE_CJK; + break; + case i18n::ScriptType::COMPLEX: + nRet = CSS1_OUTMODE_CTL; + break; + } + + return nRet; +} + +// a single output function should be enough for all formats +/* + * Output the formats as follows + * - output the tag for formats for which a corresponding HTML tag exist + * - for all the other formats, output a paragraph tag <P> and set bUserFormat + * - if a paragraph alignment is set for the supplied ItemSet of the node or + * for the ItemSet of the format, output an ALIGN=xxx if HTML allows it + * - In all cases, hard attribute is written as STYLE option. + * If bUserFormat is not set, only the supplied ItemSet is considered. + * Otherwise, attributes of the format are output as well. + */ + +namespace { + +struct SwHTMLTextCollOutputInfo +{ + OString aToken; // End token to be output + std::unique_ptr<SfxItemSet> pItemSet; // hard attribute + + bool bInNumberBulletList; // in an enumerated list; + bool bParaPossible; // a </P> may be output additionally + bool bOutPara; // a </P> is supposed to be output + bool bOutDiv; // write a </DIV> + + SwHTMLTextCollOutputInfo() : + bInNumberBulletList( false ), + bParaPossible( false ), + bOutPara( false ), + bOutDiv( false ) + {} + + bool HasParaToken() const { return aToken.getLength()==1 && aToken[0]=='P'; } + bool ShouldOutputToken() const { return bOutPara || !HasParaToken(); } +}; + +} + +SwHTMLFormatInfo::SwHTMLFormatInfo( const SwFormat *pF, SwDoc *pDoc, SwDoc *pTemplate, + bool bOutStyles, + LanguageType eDfltLang, + sal_uInt16 nCSS1Script ) + : pFormat(pF) + , nLeftMargin(0) + , nRightMargin(0) + , nFirstLineIndent(0) + , nTopMargin(0) + , nBottomMargin(0) + , bScriptDependent( false ) +{ + sal_uInt16 nRefPoolId = 0; + // Get the selector of the format + sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass, + nRefPoolId ); + OSL_ENSURE( nDeep ? !aToken.isEmpty() : aToken.isEmpty(), + "Something seems to be wrong with this token!" ); + OSL_ENSURE( nDeep ? nRefPoolId != 0 : nRefPoolId == 0, + "Something seems to be wrong with the comparison style!" ); + + bool bTextColl = pFormat->Which() == RES_TXTFMTCOLL || + pFormat->Which() == RES_CONDTXTFMTCOLL; + + const SwFormat *pReferenceFormat = nullptr; // Comparison format + if( nDeep != 0 ) + { + // It's an HTML-tag style or this style is derived from such + // a style. + if( !bOutStyles ) + { + // if no styles are exported, it may be necessary to additionally + // write hard attribute + switch( nDeep ) + { + case CSS1_FMT_ISTAG: + case CSS1_FMT_CMPREF: + // for HTML-tag styles the differences to the original + // (if available) + pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, + &pTemplate->getIDocumentStylePoolAccess() ); + break; + + default: + // otherwise, the differences to the HTML-tag style of the + // original or the ones to the current document, if it the + // HTML-tag style is not available + if( pTemplate ) + pReferenceFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, + &pTemplate->getIDocumentStylePoolAccess() ); + else + pReferenceFormat = SwHTMLWriter::GetParentFormat( *pFormat, nDeep ); + break; + } + } + } + else if( bTextColl ) + { + // HTML-tag styles that are not derived from a paragraph style + // must be exported as hard attribute relative to the text-body + // style. For a 'not-styles' export, the one of the HTML style + // should be used as a reference + if( !bOutStyles && pTemplate ) + pReferenceFormat = pTemplate->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false ); + else + pReferenceFormat = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false ); + } + + if( pReferenceFormat || nDeep==0 ) + { + pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(), + pFormat->GetAttrSet().GetRanges() ) ); + // if the differences to a different style are supposed to be + // written, hard attribute is necessary. This is always true + // for styles that are not derived from HTML-tag styles. + + pItemSet->Set( pFormat->GetAttrSet() ); + + if( pReferenceFormat ) + SwHTMLWriter::SubtractItemSet( *pItemSet, pReferenceFormat->GetAttrSet(), true ); + + // delete ItemSet that is empty straight away. This will save work + // later on + if( !pItemSet->Count() ) + { + pItemSet.reset(); + } + } + + if( !bTextColl ) + return; + + if( bOutStyles ) + { + // We have to add hard attributes for any script dependent + // item that is not accessed by the style + static const sal_uInt16 aWhichIds[3][4] = + { + { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT }, + { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT }, + { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, + RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT } + }; + + sal_uInt16 nRef = 0; + sal_uInt16 aSets[2] = {0,0}; + switch( nCSS1Script ) + { + case CSS1_OUTMODE_WESTERN: + nRef = 0; + aSets[0] = 1; + aSets[1] = 2; + break; + case CSS1_OUTMODE_CJK: + nRef = 1; + aSets[0] = 0; + aSets[1] = 2; + break; + case CSS1_OUTMODE_CTL: + nRef = 2; + aSets[0] = 0; + aSets[1] = 1; + break; + } + for( int i=0; i<4; ++i ) + { + const SfxPoolItem& rRef = pFormat->GetFormatAttr( aWhichIds[nRef][i] ); + for(sal_uInt16 nSet : aSets) + { + const SfxPoolItem& rSet = pFormat->GetFormatAttr( aWhichIds[nSet][i] ); + if( rSet != rRef ) + { + if( !pItemSet ) + pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(), + pFormat->GetAttrSet().GetRanges() ) ); + pItemSet->Put( rSet ); + } + } + } + } + + // remember all the different default spacings from the style or + // the comparison style. + const SvxLRSpaceItem &rLRSpace = + (pReferenceFormat ? pReferenceFormat : pFormat)->GetLRSpace(); + nLeftMargin = rLRSpace.GetTextLeft(); + nRightMargin = rLRSpace.GetRight(); + nFirstLineIndent = rLRSpace.GetTextFirstLineOffset(); + + const SvxULSpaceItem &rULSpace = + (pReferenceFormat ? pReferenceFormat : pFormat)->GetULSpace(); + nTopMargin = rULSpace.GetUpper(); + nBottomMargin = rULSpace.GetLower(); + + // export language if it differs from the default language + sal_uInt16 nWhichId = + SwHTMLWriter::GetLangWhichIdFromScript( nCSS1Script ); + const SvxLanguageItem& rLang = + static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr( nWhichId )); + LanguageType eLang = rLang.GetLanguage(); + if( eLang != eDfltLang ) + { + if( !pItemSet ) + pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(), + pFormat->GetAttrSet().GetRanges() ) ); + pItemSet->Put( rLang ); + } + + static const sal_uInt16 aWhichIds[3] = + { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE }; + for(sal_uInt16 i : aWhichIds) + { + if( i != nWhichId ) + { + const SvxLanguageItem& rTmpLang = + static_cast<const SvxLanguageItem&>(pFormat->GetFormatAttr(i)); + if( rTmpLang.GetLanguage() != eLang ) + { + if( !pItemSet ) + pItemSet.reset( new SfxItemSet( *pFormat->GetAttrSet().GetPool(), + pFormat->GetAttrSet().GetRanges() ) ); + pItemSet->Put( rTmpLang ); + } + } + } + +} + +SwHTMLFormatInfo::~SwHTMLFormatInfo() +{ +} + +static void OutHTML_SwFormat( Writer& rWrt, const SwFormat& rFormat, + const SfxItemSet *pNodeItemSet, + SwHTMLTextCollOutputInfo& rInfo ) +{ + OSL_ENSURE( RES_CONDTXTFMTCOLL==rFormat.Which() || RES_TXTFMTCOLL==rFormat.Which(), + "not a paragraph style" ); + + SwHTMLWriter & rHWrt = static_cast<SwHTMLWriter&>(rWrt); + + // First, some flags + sal_uInt16 nNewDefListLvl = 0; + sal_uInt16 nNumStart = USHRT_MAX; + bool bForceDL = false; + bool bDT = false; + rInfo.bInNumberBulletList = false; // Are we in a list? + bool bNumbered = false; // The current paragraph is numbered + bool bPara = false; // the current token is <P> + rInfo.bParaPossible = false; // a <P> may be additionally output + bool bNoEndTag = false; // don't output an end tag + + rHWrt.m_bNoAlign = false; // no ALIGN=... possible + + if (rHWrt.mbXHTML) + { + rHWrt.m_bNoAlign = true; + } + + sal_uInt8 nBulletGrfLvl = 255; // The bullet graphic we want to output + + // Are we in a bulleted or numbered list? + const SwTextNode* pTextNd = rWrt.m_pCurrentPam->GetNode().GetTextNode(); + + SwHTMLNumRuleInfo aNumInfo; + if( rHWrt.GetNextNumInfo() ) + { + aNumInfo = *rHWrt.GetNextNumInfo(); + rHWrt.ClearNextNumInfo(); + } + else + { + aNumInfo.Set( *pTextNd ); + } + + if( aNumInfo.GetNumRule() ) + { + rInfo.bInNumberBulletList = true; + nNewDefListLvl = 0; + + // is the current paragraph numbered? + bNumbered = aNumInfo.IsNumbered(); + sal_uInt8 nLvl = aNumInfo.GetLevel(); + + OSL_ENSURE( pTextNd->GetActualListLevel() == nLvl, + "Remembered Num level is wrong" ); + OSL_ENSURE( bNumbered == pTextNd->IsCountedInList(), + "Remembered numbering state is wrong" ); + + if( bNumbered ) + { + nBulletGrfLvl = nLvl; // only temporarily!!! + // #i57919# + // correction of re-factoring done by cws swnumtree: + // - <nNumStart> has to contain the restart value, if the + // numbering is restarted at this text node. Value <USHRT_MAX> + // indicates, that no additional restart value has to be written. + if ( pTextNd->IsListRestart() ) + { + nNumStart = static_cast< sal_uInt16 >(pTextNd->GetActualListStartValue()); + } + OSL_ENSURE( rHWrt.m_nLastParaToken == HtmlTokenId::NONE, + "<PRE> was not closed before <LI>." ); + } + } + + // Now, we're getting the token and, if necessary, the class + std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat)); + SwHTMLFormatInfo *pFormatInfo; + SwHTMLFormatInfos::iterator it = rHWrt.m_TextCollInfos.find( pTmpInfo ); + if (it != rHWrt.m_TextCollInfos.end()) + { + pFormatInfo = it->get(); + } + else + { + pFormatInfo = new SwHTMLFormatInfo( &rFormat, rWrt.m_pDoc, rHWrt.m_xTemplate.get(), + rHWrt.m_bCfgOutStyles, rHWrt.m_eLang, + rHWrt.m_nCSS1Script ); + rHWrt.m_TextCollInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo)); + if( rHWrt.m_aScriptParaStyles.count( rFormat.GetName() ) ) + pFormatInfo->bScriptDependent = true; + } + + // Now, we define what is possible due to the token + HtmlTokenId nToken = HtmlTokenId::NONE; // token for tag change + bool bOutNewLine = false; // only output a single LF? + if( !pFormatInfo->aToken.isEmpty() ) + { + // It is an HTML-tag style or the style is derived from such a + // style. + rInfo.aToken = pFormatInfo->aToken; + + if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_address) + { + rInfo.bParaPossible = true; + rHWrt.m_bNoAlign = true; + } + else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_blockquote) + { + rInfo.bParaPossible = true; + rHWrt.m_bNoAlign = true; + } + else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_parabreak) + { + bPara = true; + } + else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_preformtxt) + { + if (HtmlTokenId::PREFORMTXT_ON == rHWrt.m_nLastParaToken) + { + bOutNewLine = true; + } + else + { + nToken = HtmlTokenId::PREFORMTXT_ON; + rHWrt.m_bNoAlign = true; + bNoEndTag = true; + } + } + else if (rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt || rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dd) + { + bDT = rInfo.aToken == OOO_STRING_SVTOOLS_HTML_dt; + rInfo.bParaPossible = !bDT; + rHWrt.m_bNoAlign = true; + bForceDL = true; + } + } + else + { + // all styles that do not correspond to an HTML tag, or that are + // not derived from it, are exported as <P> + + rInfo.aToken = OOO_STRING_SVTOOLS_HTML_parabreak; + bPara = true; + } + + // If necessary, take the hard attribute from the style + if( pFormatInfo->pItemSet ) + { + OSL_ENSURE(!rInfo.pItemSet, "Where does this ItemSet come from?"); + rInfo.pItemSet.reset(new SfxItemSet( *pFormatInfo->pItemSet )); + } + + // additionally, add the hard attribute from the paragraph + if( pNodeItemSet ) + { + if (rInfo.pItemSet) + rInfo.pItemSet->Put( *pNodeItemSet ); + else + rInfo.pItemSet.reset(new SfxItemSet( *pNodeItemSet )); + } + + // we will need the lower spacing of the paragraph later on + const SvxULSpaceItem& rULSpace = + pNodeItemSet ? pNodeItemSet->Get(RES_UL_SPACE) + : rFormat.GetULSpace(); + + if( (rHWrt.m_bOutHeader && + rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() == + rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex()) || + rHWrt.m_bOutFooter ) + { + if( rHWrt.m_bCfgOutStyles ) + { + SvxULSpaceItem aULSpaceItem( rULSpace ); + if( rHWrt.m_bOutHeader ) + aULSpaceItem.SetLower( rHWrt.m_nHeaderFooterSpace ); + else + aULSpaceItem.SetUpper( rHWrt.m_nHeaderFooterSpace ); + + if (!rInfo.pItemSet) + { + rInfo.pItemSet.reset(new SfxItemSet(*rFormat.GetAttrSet().GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>{})); + } + rInfo.pItemSet->Put( aULSpaceItem ); + } + rHWrt.m_bOutHeader = false; + rHWrt.m_bOutFooter = false; + } + + if( bOutNewLine ) + { + // output a line break (without indentation) at the beginning of the + // paragraph, only + rInfo.aToken.clear(); // don't output an end tag + rWrt.Strm().WriteCharPtr( SAL_NEWLINE_STRING ); + + return; + } + + // should an ALIGN=... be written? + const SfxPoolItem* pAdjItem = nullptr; + const SfxPoolItem* pItem; + + if( rInfo.pItemSet && + SfxItemState::SET == rInfo.pItemSet->GetItemState( RES_PARATR_ADJUST, + false, &pItem ) ) + { + pAdjItem = pItem; + } + + // Consider the lower spacing of the paragraph? (never in the last + // paragraph of tables) + bool bUseParSpace = !rHWrt.m_bOutTable || + (rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() != + rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex()); + // If styles are exported, indented paragraphs become definition lists + const SvxLRSpaceItem& rLRSpace = + pNodeItemSet ? pNodeItemSet->Get(RES_LR_SPACE) + : rFormat.GetLRSpace(); + if( (!rHWrt.m_bCfgOutStyles || bForceDL) && !rInfo.bInNumberBulletList ) + { + sal_Int32 nLeftMargin; + if( bForceDL ) + nLeftMargin = rLRSpace.GetTextLeft(); + else + nLeftMargin = rLRSpace.GetTextLeft() > pFormatInfo->nLeftMargin + ? rLRSpace.GetTextLeft() - pFormatInfo->nLeftMargin + : 0; + + if( nLeftMargin > 0 && rHWrt.m_nDefListMargin > 0 ) + { + nNewDefListLvl = static_cast< sal_uInt16 >((nLeftMargin + (rHWrt.m_nDefListMargin/2)) / + rHWrt.m_nDefListMargin); + if( nNewDefListLvl == 0 && bForceDL && !bDT ) + nNewDefListLvl = 1; + } + else + { + // If the left margin is 0 or negative, emulating indent + // with <dd> does not work. We then set a def list only if + // the dd style is used. + nNewDefListLvl = (bForceDL&& !bDT) ? 1 : 0; + } + + bool bIsNextTextNode = + rWrt.m_pDoc->GetNodes()[rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex()+1] + ->IsTextNode(); + + if( bForceDL && bDT ) + { + // Instead of a DD we must use a DT from the level above this one. + nNewDefListLvl++; + } + else if( !nNewDefListLvl && !rHWrt.m_bCfgOutStyles && bPara && + rULSpace.GetLower()==0 && + ((bUseParSpace && bIsNextTextNode) || rHWrt.m_nDefListLvl==1) && + (!pAdjItem || SvxAdjust::Left== + static_cast<const SvxAdjustItem *>(pAdjItem)->GetAdjust()) ) + { + // Export paragraphs without a lower spacing as DT + nNewDefListLvl = 1; + bDT = true; + rInfo.bParaPossible = false; + rHWrt.m_bNoAlign = true; + } + } + + if( nNewDefListLvl != rHWrt.m_nDefListLvl ) + rHWrt.OutAndSetDefList( nNewDefListLvl ); + + // if necessary, start a bulleted or numbered list + if( rInfo.bInNumberBulletList ) + { + OSL_ENSURE( !rHWrt.m_nDefListLvl, "DL cannot be inside OL!" ); + OutHTML_NumberBulletListStart( rHWrt, aNumInfo ); + + if( bNumbered ) + { + if( !rHWrt.m_aBulletGrfs[nBulletGrfLvl].isEmpty() ) + bNumbered = false; + else + nBulletGrfLvl = 255; + } + } + + // Take the defaults of the style, because they don't need to be + // exported + rHWrt.m_nDfltLeftMargin = pFormatInfo->nLeftMargin; + rHWrt.m_nDfltRightMargin = pFormatInfo->nRightMargin; + rHWrt.m_nDfltFirstLineIndent = pFormatInfo->nFirstLineIndent; + + if( rInfo.bInNumberBulletList ) + { + if( !rHWrt.IsHTMLMode( HTMLMODE_LSPACE_IN_NUMBER_BULLET ) ) + rHWrt.m_nDfltLeftMargin = rLRSpace.GetTextLeft(); + + // In numbered lists, don't output a first line indent. + rHWrt.m_nFirstLineIndent = rLRSpace.GetTextFirstLineOffset(); + } + + if( rInfo.bInNumberBulletList && bNumbered && bPara && !rHWrt.m_bCfgOutStyles ) + { + // a single LI doesn't have spacing + rHWrt.m_nDfltTopMargin = 0; + rHWrt.m_nDfltBottomMargin = 0; + } + else if( rHWrt.m_nDefListLvl && bPara ) + { + // a single DD doesn't have spacing, as well + rHWrt.m_nDfltTopMargin = 0; + rHWrt.m_nDfltBottomMargin = 0; + } + else + { + rHWrt.m_nDfltTopMargin = pFormatInfo->nTopMargin; + // if in the last paragraph of a table the lower paragraph spacing + // is changed, Netscape doesn't get it. That's why we don't + // export anything here for now, by setting this spacing to the + // default value. + if( rHWrt.m_bCfgNetscape4 && !bUseParSpace ) + rHWrt.m_nDfltBottomMargin = rULSpace.GetLower(); + else + rHWrt.m_nDfltBottomMargin = pFormatInfo->nBottomMargin; + } + + if( rHWrt.m_nDefListLvl ) + { + rHWrt.m_nLeftMargin = + (rHWrt.m_nDefListLvl-1) * rHWrt.m_nDefListMargin; + } + + if( rHWrt.m_bLFPossible && !rHWrt.m_bFirstLine ) + rHWrt.OutNewLine(); // paragraph tag on a new line + rInfo.bOutPara = false; + + // this is now our new token + rHWrt.ChangeParaToken( nToken ); + + bool bHasParSpace = bUseParSpace && rULSpace.GetLower() > 0; + + // if necessary, start a new list item + if( rInfo.bInNumberBulletList && bNumbered ) + { + HtmlWriter html(rWrt.Strm(), rHWrt.maNamespace); + html.start(OOO_STRING_SVTOOLS_HTML_li); + if( USHRT_MAX != nNumStart ) + html.attribute(OOO_STRING_SVTOOLS_HTML_O_value, OString::number(nNumStart)); + // Finish the opening element, but don't close it. + html.characters(OString()); + } + + if( rHWrt.m_nDefListLvl > 0 && !bForceDL ) + { + OString aTag = bDT ? OOO_STRING_SVTOOLS_HTML_dt : OOO_STRING_SVTOOLS_HTML_dd; + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHWrt.GetNamespace() + aTag ); + } + + if( pAdjItem && + rHWrt.IsHTMLMode( HTMLMODE_NO_CONTROL_CENTERING ) && + rHWrt.HasControls() ) + { + // The align=... attribute does behave strange in netscape + // if there are controls in a paragraph, because the control and + // all text behind the control does not recognize this attribute. + OString sOut = "<" + rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division; + rWrt.Strm().WriteOString( sOut ); + + rHWrt.m_bTextAttr = false; + rHWrt.m_bOutOpts = true; + OutHTML_SvxAdjust( rWrt, *pAdjItem ); + rWrt.Strm().WriteChar( '>' ); + pAdjItem = nullptr; + rHWrt.m_bNoAlign = false; + rInfo.bOutDiv = true; + rHWrt.IncIndentLevel(); + rHWrt.m_bLFPossible = true; + rHWrt.OutNewLine(); + } + + // for BLOCKQUOTE, ADDRESS and DD we output another paragraph token, if + // - no styles are written and + // - a lower spacing or a paragraph alignment exists + // Also, XHTML does not allow character children in this context. + OString aToken = rInfo.aToken; + if( (!rHWrt.m_bCfgOutStyles || rHWrt.mbXHTML) && rInfo.bParaPossible && !bPara && + (bHasParSpace || pAdjItem) ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHWrt.GetNamespace() + rInfo.aToken ); + aToken = OOO_STRING_SVTOOLS_HTML_parabreak; + bPara = true; + rHWrt.m_bNoAlign = false; + } + + LanguageType eLang; + if (rInfo.pItemSet) + eLang = static_cast<const SvxLanguageItem&>(rInfo.pItemSet->Get(SwHTMLWriter::GetLangWhichIdFromScript(rHWrt.m_nCSS1Script))).GetLanguage(); + else + eLang = rHWrt.m_eLang; + + if( rInfo.pItemSet ) + { + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE }; + + for(sal_uInt16 i : aWhichIds) + { + // export language if it differs from the default language only. + const SfxPoolItem *pTmpItem; + if( SfxItemState::SET == rInfo.pItemSet->GetItemState( i, + true, &pTmpItem ) && + static_cast<const SvxLanguageItem *>(pTmpItem)->GetLanguage() == eLang ) + rInfo.pItemSet->ClearItem( i ); + } + } + + // and the text direction + SvxFrameDirection nDir = rHWrt.GetHTMLDirection( + (pNodeItemSet ? pNodeItemSet->Get( RES_FRAMEDIR ) + : rFormat.GetFrameDir() ).GetValue() ); + + // We only write a <P>, if + // - we are not inside OL/UL/DL, or + // - the paragraph of an OL/UL is not numbered or + // - styles are not exported and + // - a lower spacing, or + // - a paragraph alignment exists, or + // - styles are exported and + // - the text body style was changed, or + // - a user format is exported, or + // - a paragraph attribute exists + if( !bPara || + (!rInfo.bInNumberBulletList && !rHWrt.m_nDefListLvl) || + (rInfo.bInNumberBulletList && !bNumbered) || + (!rHWrt.m_bCfgOutStyles && + (bHasParSpace || pAdjItem || + (eLang != LANGUAGE_DONTKNOW && eLang != rHWrt.m_eLang))) || + nDir != rHWrt.m_nDirection || + rHWrt.m_bCfgOutStyles ) + { + // now, options are output + rHWrt.m_bTextAttr = false; + rHWrt.m_bOutOpts = true; + + OString sOut = "<" + rHWrt.GetNamespace() + aToken; + + if( eLang != LANGUAGE_DONTKNOW && eLang != rHWrt.m_eLang ) + { + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + rHWrt.OutLanguage( eLang ); + } + + if( nDir != rHWrt.m_nDirection ) + { + if( !sOut.isEmpty() ) + { + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + } + rHWrt.OutDirection( nDir ); + } + + if( rHWrt.m_bCfgOutStyles && + (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""; + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + OUString aClass( pFormatInfo->aClass ); + if( pFormatInfo->bScriptDependent ) + { + if( !aClass.isEmpty() ) + aClass += "-"; + switch( rHWrt.m_nCSS1Script ) + { + case CSS1_OUTMODE_WESTERN: + aClass += "western"; + break; + case CSS1_OUTMODE_CJK: + aClass += "cjk"; + break; + case CSS1_OUTMODE_CTL: + aClass += "ctl"; + break; + } + } + HTMLOutFuncs::Out_String( rWrt.Strm(), aClass, + rHWrt.m_eDestEnc, &rHWrt.m_aNonConvertableCharacters ); + sOut += "\""; + } + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + + // if necessary, output alignment + if( !rHWrt.m_bNoAlign && pAdjItem ) + OutHTML_SvxAdjust( rWrt, *pAdjItem ); + + rHWrt.m_bParaDotLeaders = bPara && rHWrt.m_bCfgPrintLayout && rHWrt.indexOfDotLeaders( + pTextNd->GetAnyFormatColl().GetPoolFormatId(), pTextNd->GetText()) > -1; + + // and now, if necessary, the STYLE options + if (rHWrt.m_bCfgOutStyles && rInfo.pItemSet) + { + OutCSS1_ParaTagStyleOpt( rWrt, *rInfo.pItemSet ); + } + + if (rHWrt.m_bParaDotLeaders) { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\"" + sCSS2_P_CLASS_leaders "\"><" + OOO_STRING_SVTOOLS_HTML_O_span; + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + } + + rWrt.Strm().WriteChar( '>' ); + + // is a </P> supposed to be written? + rInfo.bOutPara = + bPara && + ( rHWrt.m_bCfgOutStyles || bHasParSpace ); + + // if no end tag is supposed to be written, delete it + if( bNoEndTag ) + rInfo.aToken.clear(); + } + + if( nBulletGrfLvl != 255 ) + { + OSL_ENSURE( aNumInfo.GetNumRule(), "Where is the numbering gone???" ); + OSL_ENSURE( nBulletGrfLvl < MAXLEVEL, "There are not this many layers." ); + const SwNumFormat& rNumFormat = aNumInfo.GetNumRule()->Get(nBulletGrfLvl); + OutHTML_BulletImage( rWrt, OOO_STRING_SVTOOLS_HTML_image, rNumFormat.GetBrush(), + rHWrt.m_aBulletGrfs[nBulletGrfLvl]); + } + + rHWrt.GetNumInfo() = aNumInfo; + + // reset the defaults + rHWrt.m_nDfltLeftMargin = 0; + rHWrt.m_nDfltRightMargin = 0; + rHWrt.m_nDfltFirstLineIndent = 0; + rHWrt.m_nDfltTopMargin = 0; + rHWrt.m_nDfltBottomMargin = 0; + rHWrt.m_nLeftMargin = 0; + rHWrt.m_nFirstLineIndent = 0; +} + +static void OutHTML_SwFormatOff( Writer& rWrt, const SwHTMLTextCollOutputInfo& rInfo ) +{ + SwHTMLWriter & rHWrt = static_cast<SwHTMLWriter&>(rWrt); + + // if there is no token, we don't need to output anything + if( rInfo.aToken.isEmpty() ) + { + rHWrt.FillNextNumInfo(); + const SwHTMLNumRuleInfo& rNextInfo = *rHWrt.GetNextNumInfo(); + // a bulleted list must be closed in PRE as well + if( rInfo.bInNumberBulletList ) + { + + const SwHTMLNumRuleInfo& rNRInfo = rHWrt.GetNumInfo(); + if( rNextInfo.GetNumRule() != rNRInfo.GetNumRule() || + rNextInfo.GetDepth() != rNRInfo.GetDepth() || + rNextInfo.IsNumbered() || rNextInfo.IsRestart() ) + rHWrt.ChangeParaToken( HtmlTokenId::NONE ); + OutHTML_NumberBulletListEnd( rHWrt, rNextInfo ); + } + else if( rNextInfo.GetNumRule() != nullptr ) + rHWrt.ChangeParaToken( HtmlTokenId::NONE ); + + return; + } + + if( rInfo.ShouldOutputToken() ) + { + if( rHWrt.m_bLFPossible ) + rHWrt.OutNewLine( true ); + + // if necessary, for BLOCKQUOTE, ADDRESS and DD another paragraph token + // is output, if + // - no styles are written and + // - a lower spacing exists + if( rInfo.bParaPossible && rInfo.bOutPara ) + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_parabreak, false ); + + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHWrt.GetNamespace() + rInfo.aToken, false ); + rHWrt.m_bLFPossible = + rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dt && + rInfo.aToken != OOO_STRING_SVTOOLS_HTML_dd && + rInfo.aToken != OOO_STRING_SVTOOLS_HTML_li; + } + if( rInfo.bOutDiv ) + { + rHWrt.DecIndentLevel(); + if( rHWrt.m_bLFPossible ) + rHWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false ); + rHWrt.m_bLFPossible = true; + } + + // if necessary, close the list item, then close a bulleted or numbered list + if( rInfo.bInNumberBulletList ) + { + rHWrt.FillNextNumInfo(); + OutHTML_NumberBulletListEnd( rHWrt, *rHWrt.GetNextNumInfo() ); + } +} + +namespace { + +class HTMLStartEndPos +{ + sal_Int32 nStart; + sal_Int32 nEnd; + std::unique_ptr<SfxPoolItem> pItem; + +public: + + HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE ); + + const SfxPoolItem *GetItem() const { return pItem.get(); } + + void SetStart( sal_Int32 nStt ) { nStart = nStt; } + sal_Int32 GetStart() const { return nStart; } + + sal_Int32 GetEnd() const { return nEnd; } + void SetEnd( sal_Int32 nE ) { nEnd = nE; } +}; + +} + +HTMLStartEndPos::HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, + sal_Int32 nE ) : + nStart( nStt ), + nEnd( nE ), + pItem( rItem.Clone() ) +{} + +typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions; + +namespace { + +enum HTMLOnOffState { HTML_NOT_SUPPORTED, // unsupported Attribute + HTML_REAL_VALUE, // Attribute with value + HTML_ON_VALUE, // Attribute is On-Tag + HTML_OFF_VALUE, // Attribute is Off-Tag + HTML_CHRFMT_VALUE, // Attribute for character format + HTML_COLOR_VALUE, // Attribute for foreground color + HTML_STYLE_VALUE, // Attribute must be exported as style + HTML_DROPCAP_VALUE, // DropCap-Attribute + HTML_AUTOFMT_VALUE }; // Attribute for automatic character styles + +class HTMLEndPosLst +{ + HTMLStartEndPositions aStartLst; // list, sorted for start positions + HTMLStartEndPositions aEndLst; // list, sorted for end positions + std::deque<sal_Int32> aScriptChgLst; // positions where script changes + // 0 is not contained in this list, + // but the text length + // the script that is valid up to the position + // contained in aScriptChgList at the same index + std::vector<sal_uInt16> aScriptLst; + + SwDoc *pDoc; // the current document + SwDoc* pTemplate; // the HTML template (or 0) + std::optional<Color> xDfltColor;// the default foreground colors + std::set<OUString>& rScriptTextStyles; + + sal_uLong nHTMLMode; + bool bOutStyles : 1; // are styles exported + + // Insert/remove a SttEndPos in/from the Start and End lists. + // The end position is known. + void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos ); + void RemoveItem_( HTMLStartEndPositions::size_type nEndPos ); + + // determine the 'type' of the attribute + HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem ); + + // does a specific OnTag item exist + bool ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos ); + + // does an item exist that can be used to disable an attribute that + // is exported the same way as the supplied item in the same range? + bool ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos, + sal_Int32 nEndPos ); + + // adapt the end of a split item + void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd, + HTMLStartEndPositions::size_type nStartPos ); + + // insert an attribute in the lists and, if necessary, split it + void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart, + sal_Int32 nEnd ); + + // split an already existing attribute + void SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, + sal_Int32 nEnd ); + + // Insert without taking care of script + void InsertNoScript( const SfxPoolItem& rItem, sal_Int32 nStart, + sal_Int32 nEnd, SwHTMLFormatInfos& rFormatInfos, + bool bParaAttrs ); + + const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat, + SwHTMLFormatInfos& rFormatInfos ); + +public: + + HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor, + bool bOutStyles, sal_uLong nHTMLMode, + const OUString& rText, std::set<OUString>& rStyles ); + ~HTMLEndPosLst(); + + // insert an attribute + void Insert( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd, + SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs=false ); + void Insert( const SfxItemSet& rItemSet, sal_Int32 nStart, sal_Int32 nEnd, + SwHTMLFormatInfos& rFormatInfos, bool bDeep, + bool bParaAttrs=false ); + void Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos, + SwHTMLFormatInfos& rFormatInfos ); + + sal_uInt16 GetScriptAtPos( sal_Int32 nPos, + sal_uInt16 nWeak ); + + void OutStartAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos, + HTMLOutContext *pContext = nullptr ); + void OutEndAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos, + HTMLOutContext *pContext ); + + bool IsHTMLMode( sal_uLong nMode ) const { return (nHTMLMode & nMode) != 0; } +}; + +} + +void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos ) +{ + // Insert the attribute in the Start list behind all attributes that + // were started before, or at the same position. + sal_Int32 nStart = pPos->GetStart(); + HTMLStartEndPositions::size_type i {0}; + + while( i < aStartLst.size() && aStartLst[i]->GetStart() <= nStart ) + ++i; + aStartLst.insert( aStartLst.begin() + i, pPos ); + + // the position in the End list was supplied + aEndLst.insert( aEndLst.begin() + nEndPos, pPos ); +} + +void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos ) +{ + HTMLStartEndPos *pPos = aEndLst[nEndPos]; + + // now, we are looking for it in the Start list + HTMLStartEndPositions::iterator it = + std::find(aStartLst.begin(), aStartLst.end(), pPos ); + OSL_ENSURE(it != aStartLst.end(), "Item not found in Start List!"); + if( it != aStartLst.end() ) + aStartLst.erase( it ); + + aEndLst.erase( aEndLst.begin() + nEndPos ); + + delete pPos; +} + +HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem ) +{ + HTMLOnOffState eState = HTML_NOT_SUPPORTED; + switch( rItem.Which() ) + { + case RES_CHRATR_POSTURE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CTL_POSTURE: + switch( static_cast<const SvxPostureItem&>(rItem).GetPosture() ) + { + case ITALIC_NORMAL: + eState = HTML_ON_VALUE; + break; + case ITALIC_NONE: + eState = HTML_OFF_VALUE; + break; + default: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + } + break; + + case RES_CHRATR_CROSSEDOUT: + switch( static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout() ) + { + case STRIKEOUT_SINGLE: + case STRIKEOUT_DOUBLE: + eState = HTML_ON_VALUE; + break; + case STRIKEOUT_NONE: + eState = HTML_OFF_VALUE; + break; + default: + ; + } + break; + + case RES_CHRATR_ESCAPEMENT: + switch( static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rItem).GetEnumValue()) ) + { + case SvxEscapement::Superscript: + case SvxEscapement::Subscript: + eState = HTML_ON_VALUE; + break; + case SvxEscapement::Off: + eState = HTML_OFF_VALUE; + break; + default: + ; + } + break; + + case RES_CHRATR_UNDERLINE: + switch( static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle() ) + { + case LINESTYLE_SINGLE: + eState = HTML_ON_VALUE; + break; + case LINESTYLE_NONE: + eState = HTML_OFF_VALUE; + break; + default: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + } + break; + + case RES_CHRATR_OVERLINE: + case RES_CHRATR_HIDDEN: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + + case RES_CHRATR_WEIGHT: + case RES_CHRATR_CJK_WEIGHT: + case RES_CHRATR_CTL_WEIGHT: + switch( static_cast<const SvxWeightItem&>(rItem).GetWeight() ) + { + case WEIGHT_BOLD: + eState = HTML_ON_VALUE; + break; + case WEIGHT_NORMAL: + eState = HTML_OFF_VALUE; + break; + default: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + } + break; + + case RES_CHRATR_BLINK: + eState = static_cast<const SvxBlinkItem&>(rItem).GetValue() ? HTML_ON_VALUE + : HTML_OFF_VALUE; + break; + + case RES_CHRATR_COLOR: + eState = HTML_COLOR_VALUE; + break; + + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_CJK_FONT: + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_TXTATR_INETFMT: + eState = HTML_REAL_VALUE; + break; + + case RES_TXTATR_CHARFMT: + eState = HTML_CHRFMT_VALUE; + break; + + case RES_TXTATR_AUTOFMT: + eState = HTML_AUTOFMT_VALUE; + break; + + case RES_CHRATR_CASEMAP: + eState = HTML_STYLE_VALUE; + break; + + case RES_CHRATR_KERNING: + eState = HTML_STYLE_VALUE; + break; + + case RES_CHRATR_BACKGROUND: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + + case RES_PARATR_DROP: + eState = HTML_DROPCAP_VALUE; + break; + + case RES_CHRATR_BOX: + if( IsHTMLMode(HTMLMODE_SOME_STYLES) ) + eState = HTML_STYLE_VALUE; + break; + } + + return eState; +} + +bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos ) +{ + for( auto pTest : aStartLst ) + { + if( pTest->GetStart() > nPos ) + { + // this attribute, and all attributes that follow, start later + break; + } + else if( pTest->GetEnd() > nPos ) + { + // the attribute starts before, or at, the current position and + // ends after it + const SfxPoolItem *pItem = pTest->GetItem(); + if( pItem->Which() == nWhich && + HTML_ON_VALUE == GetHTMLItemState(*pItem) ) + { + // an OnTag attribute was found + return true; + } + } + } + + return false; +} + +bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos, + sal_Int32 nEndPos ) +{ + if( nWhich != RES_CHRATR_CROSSEDOUT && + nWhich != RES_CHRATR_UNDERLINE && + nWhich != RES_CHRATR_BLINK ) + { + return false; + } + + for( auto pTest : aStartLst ) + { + if( pTest->GetStart() > nStartPos ) + { + // this attribute, and all attributes that follow, start later + break; + } + else if( pTest->GetStart()==nStartPos && + pTest->GetEnd()==nEndPos ) + { + // the attribute starts before or at the current position and + // ends after it + const SfxPoolItem *pItem = pTest->GetItem(); + sal_uInt16 nTstWhich = pItem->Which(); + if( (nTstWhich == RES_CHRATR_CROSSEDOUT || + nTstWhich == RES_CHRATR_UNDERLINE || + nTstWhich == RES_CHRATR_BLINK) && + HTML_OFF_VALUE == GetHTMLItemState(*pItem) ) + { + // an OffTag attribute was found that is exported the same + // way as the current item + return true; + } + } + } + + return false; +} + +void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd, + HTMLStartEndPositions::size_type nStartPos ) +{ + // fix the end position accordingly + pPos->SetEnd( nNewEnd ); + + // remove the item from the End list + HTMLStartEndPositions::iterator it = + std::find(aEndLst.begin(), aEndLst.end(), pPos ); + OSL_ENSURE(it != aEndLst.end(), "Item not found in End List!" ); + if( it != aEndLst.end() ) + aEndLst.erase( it ); + + // from now on, it is closed as the last one at the corresponding position + HTMLStartEndPositions::size_type nEndPos {0}; + while( nEndPos < aEndLst.size() && aEndLst[nEndPos]->GetEnd() <= nNewEnd ) + ++nEndPos; + aEndLst.insert( aEndLst.begin() + nEndPos, pPos ); + + // now, adjust the attributes that got started afterwards + for( HTMLStartEndPositions::size_type i = nStartPos+1; i<aStartLst.size(); ++i ) + { + HTMLStartEndPos *pTest = aStartLst[i]; + sal_Int32 nTestEnd = pTest->GetEnd(); + if( pTest->GetStart() >= nNewEnd ) + { + // the Test attribute and all the following ones start, after the + // split attribute ends + break; + } + else if( nTestEnd > nNewEnd ) + { + // the Test attribute starts before the split attribute + // ends, and ends afterwards, i.e., it must be split, as well + + // set the new end + pTest->SetEnd( nNewEnd ); + + // remove the attribute from the End list + it = std::find(aEndLst.begin(), aEndLst.end(), pTest ); + OSL_ENSURE(it != aEndLst.end(), "Item not found in End List!" ); + if( it != aEndLst.end() ) + aEndLst.erase( it ); + + // it now ends as the first attribute in the respective position. + // We already know this position in the End list. + aEndLst.insert( aEndLst.begin() + nEndPos, pTest ); + + // insert the 'rest' of the attribute + InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd ); + } + } +} + +void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart, + sal_Int32 nEnd ) +{ + HTMLStartEndPositions::size_type i; + for( i = 0; i < aEndLst.size(); i++ ) + { + HTMLStartEndPos *pTest = aEndLst[i]; + sal_Int32 nTestEnd = pTest->GetEnd(); + if( nTestEnd <= nStart ) + { + // the Test attribute ends, before the new one starts + continue; + } + else if( nTestEnd < nEnd ) + { + if( pTest->GetStart() < nStart ) + { + // the Test attribute ends, before the new one ends. Thus, the + // new attribute must be split. + InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i ); + nStart = nTestEnd; + } + } + else + { + // the Test attribute (and all that follow) ends, before the new + // one ends + break; + } + } + + // one attribute must still be inserted + InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i ); +} + +void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, + sal_Int32 nEnd ) +{ + sal_uInt16 nWhich = rItem.Which(); + + // first, we must search for the old items by using the start list and + // determine the new item range + + for( HTMLStartEndPositions::size_type i=0; i<aStartLst.size(); ++i ) + { + HTMLStartEndPos *pTest = aStartLst[i]; + sal_Int32 nTestStart = pTest->GetStart(); + sal_Int32 nTestEnd = pTest->GetEnd(); + + if( nTestStart >= nEnd ) + { + // this attribute, and all that follow, start later + break; + } + else if( nTestEnd > nStart ) + { + // the Test attribute ends in the range that must be deleted + const SfxPoolItem *pItem = pTest->GetItem(); + + // only the corresponding OnTag attributes have to be considered + if( pItem->Which() == nWhich && + HTML_ON_VALUE == GetHTMLItemState( *pItem ) ) + { + bool bDelete = true; + + if( nTestStart < nStart ) + { + // the start of the new attribute corresponds to the new + // end of the attribute + FixSplittedItem( pTest, nStart, i ); + bDelete = false; + } + else + { + // the Test item only starts after the new end of the + // attribute. Therefore, it can be completely erased. + aStartLst.erase( aStartLst.begin() + i ); + i--; + + HTMLStartEndPositions::iterator it = + std::find(aEndLst.begin(), aEndLst.end(), pTest ); + OSL_ENSURE(it != aEndLst.end(), "Item not found in End List!" ); + if( it != aEndLst.end() ) + aEndLst.erase( it ); + } + + // if necessary, insert the second part of the split + // attribute + if( nTestEnd > nEnd ) + { + InsertItem( *pTest->GetItem(), nEnd, nTestEnd ); + } + + if( bDelete ) + delete pTest; + } + } + } +} + +const SwHTMLFormatInfo *HTMLEndPosLst::GetFormatInfo( const SwFormat& rFormat, + SwHTMLFormatInfos& rFormatInfos ) +{ + SwHTMLFormatInfo *pFormatInfo; + std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(&rFormat)); + SwHTMLFormatInfos::iterator it = rFormatInfos.find( pTmpInfo ); + if (it != rFormatInfos.end()) + { + pFormatInfo = it->get(); + } + else + { + pFormatInfo = new SwHTMLFormatInfo( &rFormat, pDoc, pTemplate, + bOutStyles ); + rFormatInfos.insert(std::unique_ptr<SwHTMLFormatInfo>(pFormatInfo)); + if ( rScriptTextStyles.count( rFormat.GetName() ) ) + pFormatInfo->bScriptDependent = true; + } + + return pFormatInfo; +} + +HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfltCol, + bool bStyles, sal_uLong nMode, const OUString& rText, + std::set<OUString>& rStyles) + : pDoc(pD) + , pTemplate(pTempl) + , xDfltColor(std::move(xDfltCol)) + , rScriptTextStyles(rStyles) + , nHTMLMode(nMode) + , bOutStyles(bStyles) +{ + sal_Int32 nEndPos = rText.getLength(); + sal_Int32 nPos = 0; + while( nPos < nEndPos ) + { + sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( rText, nPos ); + nPos = g_pBreakIt->GetBreakIter()->endOfScript( rText, nPos, nScript ); + aScriptChgLst.push_back( nPos ); + aScriptLst.push_back( nScript ); + } +} + +HTMLEndPosLst::~HTMLEndPosLst() +{ + OSL_ENSURE(aStartLst.empty(), "Start List not empty in destructor"); + OSL_ENSURE(aEndLst.empty(), "End List not empty in destructor"); +} + +void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem, + sal_Int32 nStart, sal_Int32 nEnd, + SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs ) +{ + // no range ?? in that case, don't take it, it will never take effect !! + if( nStart != nEnd ) + { + bool bSet = false, bSplit = false; + switch( GetHTMLItemState(rItem) ) + { + case HTML_ON_VALUE: + // output the attribute, if it isn't 'on', already + if( !ExistsOnTagItem( rItem.Which(), nStart ) ) + bSet = true; + break; + + case HTML_OFF_VALUE: + // If the corresponding attribute is 'on', split it. + // Additionally, output it as Style, if it is not set for the + // whole paragraph, because in that case it was already output + // together with the paragraph tag. + if( ExistsOnTagItem( rItem.Which(), nStart ) ) + bSplit = true; + bSet = bOutStyles && !bParaAttrs && + !ExistsOffTagItem( rItem.Which(), nStart, nEnd ); + break; + + case HTML_REAL_VALUE: + // we can always output the attribute + bSet = true; + break; + + case HTML_STYLE_VALUE: + // We can only output the attribute as CSS1. If it is set for + // the paragraph, it was already output with the paragraph tag. + // The only exception is the character-background attribute. This + // attribute must always be handled like a Hint. + bSet = bOutStyles && + (!bParaAttrs + || rItem.Which()==RES_CHRATR_BACKGROUND + || rItem.Which()==RES_CHRATR_BOX + || rItem.Which()==RES_CHRATR_OVERLINE); + break; + + case HTML_CHRFMT_VALUE: + { + OSL_ENSURE( RES_TXTATR_CHARFMT == rItem.Which(), + "Not a character style after all" ); + const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rItem); + const SwCharFormat* pFormat = rChrFormat.GetCharFormat(); + + const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos ); + if( !pFormatInfo->aToken.isEmpty() ) + { + // output the character style tag before the hard + // attributes + InsertItem( rItem, nStart, nEnd ); + } + if( pFormatInfo->pItemSet ) + { + Insert( *pFormatInfo->pItemSet, nStart, nEnd, + rFormatInfos, true, bParaAttrs ); + } + } + break; + + case HTML_AUTOFMT_VALUE: + { + const SwFormatAutoFormat& rAutoFormat = static_cast<const SwFormatAutoFormat&>(rItem); + const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle(); + if( pSet ) + Insert( *pSet, nStart, nEnd, rFormatInfos, true, bParaAttrs ); + } + break; + + case HTML_COLOR_VALUE: + // A foreground color as a paragraph attribute is only exported if + // it is not the same as the default color. + { + OSL_ENSURE( RES_CHRATR_COLOR == rItem.Which(), + "Not a foreground color, after all" ); + Color aColor( static_cast<const SvxColorItem&>(rItem).GetValue() ); + if( COL_AUTO == aColor ) + aColor = COL_BLACK; + bSet = !bParaAttrs || !xDfltColor || + !xDfltColor->IsRGBEqual( aColor ); + } + break; + + case HTML_DROPCAP_VALUE: + { + OSL_ENSURE( RES_PARATR_DROP == rItem.Which(), + "Not a drop cap, after all" ); + const SwFormatDrop& rDrop = static_cast<const SwFormatDrop&>(rItem); + nEnd = nStart + rDrop.GetChars(); + if( !bOutStyles ) + { + // At least use the attributes of the character style + const SwCharFormat *pCharFormat = rDrop.GetCharFormat(); + if( pCharFormat ) + { + Insert( pCharFormat->GetAttrSet(), nStart, nEnd, + rFormatInfos, true, bParaAttrs ); + } + } + else + { + bSet = true; + } + } + break; + default: + ; + } + + if( bSet ) + InsertItem( rItem, nStart, nEnd ); + if( bSplit ) + SplitItem( rItem, nStart, nEnd ); + } +} + +void HTMLEndPosLst::Insert( const SfxPoolItem& rItem, + sal_Int32 nStart, sal_Int32 nEnd, + SwHTMLFormatInfos& rFormatInfos, bool bParaAttrs ) +{ + bool bDependsOnScript = false, bDependsOnAnyScript = false; + sal_uInt16 nScript = i18n::ScriptType::LATIN; + switch( rItem.Which() ) + { + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + bDependsOnScript = true; + nScript = i18n::ScriptType::LATIN; + break; + + case RES_CHRATR_CJK_FONT: + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + bDependsOnScript = true; + nScript = i18n::ScriptType::ASIAN; + break; + + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + bDependsOnScript = true; + nScript = i18n::ScriptType::COMPLEX; + break; + case RES_TXTATR_CHARFMT: + { + const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rItem); + const SwCharFormat* pFormat = rChrFormat.GetCharFormat(); + const SwHTMLFormatInfo *pFormatInfo = GetFormatInfo( *pFormat, rFormatInfos ); + if( pFormatInfo->bScriptDependent ) + { + bDependsOnScript = true; + bDependsOnAnyScript = true; + } + } + break; + case RES_TXTATR_INETFMT: + { + if( GetFormatInfo( *pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( + RES_POOLCHR_INET_NORMAL), rFormatInfos )->bScriptDependent || + GetFormatInfo( *pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( + RES_POOLCHR_INET_VISIT), rFormatInfos )->bScriptDependent ) + { + bDependsOnScript = true; + bDependsOnAnyScript = true; + } + } + break; + } + + if( bDependsOnScript ) + { + sal_Int32 nPos = nStart; + for( size_t i=0; i < aScriptChgLst.size(); i++ ) + { + sal_Int32 nChgPos = aScriptChgLst[i]; + if( nPos >= nChgPos ) + { + // the hint starts behind or at the next script change, + // so we may continue with this position. + continue; + } + if( nEnd <= nChgPos ) + { + // the (rest of) the hint ends before or at the next script + // change, so we can insert it, but only if it belongs + // to the current script. + if( bDependsOnAnyScript || nScript == aScriptLst[i] ) + InsertNoScript( rItem, nPos, nEnd, rFormatInfos, + bParaAttrs ); + break; + } + + // the hint starts before the next script change and ends behind + // it, so we can insert a hint up to the next script change and + // continue with the rest of the hint. + if( bDependsOnAnyScript || nScript == aScriptLst[i] ) + InsertNoScript( rItem, nPos, nChgPos, rFormatInfos, bParaAttrs ); + nPos = nChgPos; + } + } + else + { + InsertNoScript( rItem, nStart, nEnd, rFormatInfos, bParaAttrs ); + } +} + +void HTMLEndPosLst::Insert( const SfxItemSet& rItemSet, + sal_Int32 nStart, sal_Int32 nEnd, + SwHTMLFormatInfos& rFormatInfos, + bool bDeep, bool bParaAttrs ) +{ + SfxWhichIter aIter( rItemSet ); + + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( nWhich, bDeep, &pItem ) ) + { + Insert( *pItem, nStart, nEnd, rFormatInfos, bParaAttrs ); + } + + nWhich = aIter.NextWhich(); + } +} + +void HTMLEndPosLst::Insert( const SwDrawFrameFormat& rFormat, sal_Int32 nPos, + SwHTMLFormatInfos& rFormatInfos ) +{ + const SdrObject* pTextObj = SwHTMLWriter::GetMarqueeTextObj( rFormat ); + + if( pTextObj ) + { + // get the edit engine attributes of the object as SW attributes and + // insert them as hints. Because of the amount of Hints the styles + // are not considered! + const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet(); + SfxItemSet aItemSet( *rFormatItemSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, + RES_CHRATR_END>{} ); + SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, pTextObj ); + bool bOutStylesOld = bOutStyles; + bOutStyles = false; + Insert( aItemSet, nPos, nPos+1, rFormatInfos, false ); + bOutStyles = bOutStylesOld; + } +} + +sal_uInt16 HTMLEndPosLst::GetScriptAtPos( sal_Int32 nPos, sal_uInt16 nWeak ) +{ + sal_uInt16 nRet = CSS1_OUTMODE_ANY_SCRIPT; + + size_t nScriptChgs = aScriptChgLst.size(); + size_t i=0; + while( i < nScriptChgs && nPos >= aScriptChgLst[i] ) + i++; + OSL_ENSURE( i < nScriptChgs, "script list is too short" ); + if( i < nScriptChgs ) + { + if( i18n::ScriptType::WEAK == aScriptLst[i] ) + nRet = nWeak; + else + nRet = SwHTMLWriter::GetCSS1ScriptForScriptType( aScriptLst[i] ); + } + + return nRet; +} + +void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos, + HTMLOutContext *pContext ) +{ + rHWrt.m_bTagOn = true; + + // Character border attribute must be the first which is written out + // because of border merge. + HTMLStartEndPositions::size_type nCharBoxIndex = 0; + while( nCharBoxIndex < aStartLst.size() && + aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX ) + { + ++nCharBoxIndex; + } + + // the attributes of the start list are sorted in ascending order + for( HTMLStartEndPositions::size_type i=0; i< aStartLst.size(); ++i ) + { + HTMLStartEndPos *pPos = nullptr; + if( nCharBoxIndex < aStartLst.size() ) + { + if( i == 0 ) + pPos = aStartLst[nCharBoxIndex]; + else if( i == nCharBoxIndex ) + pPos = aStartLst[0]; + else + pPos = aStartLst[i]; + } + else + pPos = aStartLst[i]; + + sal_Int32 nStart = pPos->GetStart(); + if( nStart > nPos ) + { + // this attribute, and all that follow, will be opened later on + break; + } + else if( nStart == nPos ) + { + // output the attribute + sal_uInt16 nCSS1Script = rHWrt.m_nCSS1Script; + sal_uInt16 nWhich = pPos->GetItem()->Which(); + if( RES_TXTATR_CHARFMT == nWhich || + RES_TXTATR_INETFMT == nWhich || + RES_PARATR_DROP == nWhich ) + { + rHWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script ); + } + if( pContext ) + { + HTMLOutFuncs::FlushToAscii( rHWrt.Strm(), *pContext ); + pContext = nullptr; // one time only + } + Out( aHTMLAttrFnTab, *pPos->GetItem(), rHWrt ); + rHWrt.maStartedAttributes[pPos->GetItem()->Which()]++; + rHWrt.m_nCSS1Script = nCSS1Script; + } + } +} + +void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rHWrt, sal_Int32 nPos, + HTMLOutContext *pContext = nullptr ) +{ + rHWrt.m_bTagOn = false; + + // the attributes in the End list are sorted in ascending order + HTMLStartEndPositions::size_type i {0}; + while( i < aEndLst.size() ) + { + HTMLStartEndPos *pPos = aEndLst[i]; + sal_Int32 nEnd = pPos->GetEnd(); + + if( SAL_MAX_INT32 == nPos || nEnd == nPos ) + { + if( pContext ) + { + HTMLOutFuncs::FlushToAscii( rHWrt.Strm(), *pContext ); + pContext = nullptr; // one time only + } + // Skip closing span if next character span has the same border (border merge) + bool bSkipOut = false; + if( pPos->GetItem()->Which() == RES_CHRATR_BOX ) + { + HTMLStartEndPositions::iterator it = + std::find(aStartLst.begin(), aStartLst.end(), pPos ); + OSL_ENSURE(it != aStartLst.end(), "Item not found in Start List!" ); + if (it != aStartLst.end()) + ++it; + while(it != aStartLst.end() ) + { + HTMLStartEndPos *pEndPos = *it; + if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX && + *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) == + *static_cast<const SvxBoxItem*>(pPos->GetItem()) ) + { + pEndPos->SetStart(pPos->GetStart()); + bSkipOut = true; + break; + } + ++it; + } + } + if( !bSkipOut ) + { + Out( aHTMLAttrFnTab, *pPos->GetItem(), rHWrt ); + rHWrt.maStartedAttributes[pPos->GetItem()->Which()]--; + } + RemoveItem_( i ); + } + else if( nEnd > nPos ) + { + // this attribute, and all that follow, are closed later on + break; + } + else + { + // The attribute is closed before the current position. This + // is not allowed, but we can handle it anyway. + OSL_ENSURE( nEnd >= nPos, + "The attribute should've been closed a long time ago" ); + i++; + } + } +} + +/* Output of the nodes*/ +Writer& OutHTML_SwTextNode( Writer& rWrt, const SwContentNode& rNode ) +{ + const SwTextNode * pNd = &static_cast<const SwTextNode&>(rNode); + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const OUString& rStr = pNd->GetText(); + sal_Int32 nEnd = rStr.getLength(); + + // special case: empty node and HR style (horizontal rule) + // output a <HR>, only + sal_uInt16 nPoolId = pNd->GetAnyFormatColl().GetPoolFormatId(); + + // Handle horizontal rule <hr> + if (!nEnd && + (RES_POOLCOLL_HTML_HR==nPoolId || pNd->GetAnyFormatColl().GetName() == OOO_STRING_SVTOOLS_HTML_horzrule)) + { + // then, the paragraph-anchored graphics/OLE objects in the paragraph + // MIB 8.7.97: We enclose the line in a <PRE>. This means that the + // spacings are wrong, but otherwise we get an empty paragraph + // after the <HR> which is even uglier. + rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE ); + + // Output all the nodes that are anchored to a frame + rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any ); + + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); // paragraph tag on a new line + + rHTMLWrt.m_bLFPossible = true; + + HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace); + aHtml.start(OOO_STRING_SVTOOLS_HTML_horzrule); + + const SfxItemSet* pItemSet = pNd->GetpSwAttrSet(); + if( !pItemSet ) + { + aHtml.endAttribute(); + return rHTMLWrt; + } + const SfxPoolItem* pItem; + if( SfxItemState::SET == pItemSet->GetItemState( RES_LR_SPACE, false, &pItem )) + { + sal_Int32 nLeft = static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft(); + sal_Int32 nRight = static_cast<const SvxLRSpaceItem*>(pItem)->GetRight(); + if( nLeft || nRight ) + { + const SwFrameFormat& rPgFormat = + rHTMLWrt.m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool + ( RES_POOLPAGE_HTML, false )->GetMaster(); + const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize(); + const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace(); + const SwFormatCol& rCol = rPgFormat.GetCol(); + + long nPageWidth = rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight(); + + if( 1 < rCol.GetNumCols() ) + nPageWidth /= rCol.GetNumCols(); + + const SwTableNode* pTableNd = pNd->FindTableNode(); + if( pTableNd ) + { + const SwTableBox* pBox = pTableNd->GetTable().GetTableBox( + pNd->StartOfSectionIndex() ); + if( pBox ) + nPageWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); + } + + OString sWidth = OString::number(SwHTMLWriter::ToPixel(nPageWidth - nLeft - nRight, false)); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth); + + if( !nLeft ) + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_left); + else if( !nRight ) + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_right); + else + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, OOO_STRING_SVTOOLS_HTML_AL_center); + } + } + + if( SfxItemState::SET == pItemSet->GetItemState( RES_BOX, false, &pItem )) + { + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + const editeng::SvxBorderLine* pBorderLine = pBoxItem->GetBottom(); + if( pBorderLine ) + { + sal_uInt16 nWidth = pBorderLine->GetScaledWidth(); + OString sWidth = OString::number(SwHTMLWriter::ToPixel(nWidth, false)); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_size, sWidth); + + const Color& rBorderColor = pBorderLine->GetColor(); + if( !rBorderColor.IsRGBEqual( COL_GRAY ) ) + { + HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, rBorderColor); + } + + if( !pBorderLine->GetInWidth() ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_noshade, OOO_STRING_SVTOOLS_HTML_O_noshade); + } + } + } + aHtml.end(); + return rHTMLWrt; + } + + // Do not export the empty nodes with 2pt fonts and standard style that + // are inserted before tables and sections, but do export bookmarks + // and paragraph anchored frames. + if( !nEnd && (nPoolId == RES_POOLCOLL_STANDARD || + nPoolId == RES_POOLCOLL_TABLE || + nPoolId == RES_POOLCOLL_TABLE_HDLN) ) + { + // The current node is empty and contains the standard style ... + const SfxPoolItem* pItem; + const SfxItemSet* pItemSet = pNd->GetpSwAttrSet(); + if( pItemSet && pItemSet->Count() && + SfxItemState::SET == pItemSet->GetItemState( RES_CHRATR_FONTSIZE, false, &pItem ) && + 40 == static_cast<const SvxFontHeightItem *>(pItem)->GetHeight() ) + { + // ... moreover, the 2pt font is set ... + sal_uLong nNdPos = rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex(); + const SwNode *pNextNd = rWrt.m_pDoc->GetNodes()[nNdPos+1]; + const SwNode *pPrevNd = rWrt.m_pDoc->GetNodes()[nNdPos-1]; + bool bStdColl = nPoolId == RES_POOLCOLL_STANDARD; + if( ( bStdColl && (pNextNd->IsTableNode() || pNextNd->IsSectionNode()) ) || + ( !bStdColl && + pNextNd->IsEndNode() && + pPrevNd->IsStartNode() && + SwTableBoxStartNode == pPrevNd->GetStartNode()->GetStartNodeType() ) ) + { + // ... and it is located before a table or a section + rHTMLWrt.OutBookmarks(); + rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE; + + // Output all frames that are anchored to this node + rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Any ); + rHTMLWrt.m_bLFPossible = false; + + return rWrt; + } + } + } + + // catch PageBreaks and PageDescs + bool bPageBreakBehind = false; + if( rHTMLWrt.m_bCfgFormFeed && + !(rHTMLWrt.m_bOutTable || rHTMLWrt.m_bOutFlyFrame) && + rHTMLWrt.m_pStartNdIdx->GetIndex() != rHTMLWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() ) + { + bool bPageBreakBefore = false; + const SfxPoolItem* pItem; + const SfxItemSet* pItemSet = pNd->GetpSwAttrSet(); + + if( pItemSet ) + { + if( SfxItemState::SET == pItemSet->GetItemState( RES_PAGEDESC, true, &pItem ) && + static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc() ) + { + bPageBreakBefore = true; + } + else if( SfxItemState::SET == pItemSet->GetItemState( RES_BREAK, true, &pItem ) ) + { + switch( static_cast<const SvxFormatBreakItem *>(pItem)->GetBreak() ) + { + case SvxBreak::PageBefore: + bPageBreakBefore = true; + break; + case SvxBreak::PageAfter: + bPageBreakBehind = true; + break; + case SvxBreak::PageBoth: + bPageBreakBefore = true; + bPageBreakBehind = true; + break; + default: + break; + } + } + } + + if( bPageBreakBefore ) + rWrt.Strm().WriteChar( '\f' ); + } + + // if necessary, open a form + rHTMLWrt.OutForm(); + + // Output the page-anchored frames that are 'anchored' to this node + bool bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Prefix ); + + // Output all frames that are anchored to this node that are supposed to + // be written before the paragraph tag. + if( bFlysLeft ) + { + bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), 0, HtmlPosition::Before ); + } + + if( rHTMLWrt.m_pCurrentPam->GetPoint()->nNode == rHTMLWrt.m_pCurrentPam->GetMark()->nNode ) + { + nEnd = rHTMLWrt.m_pCurrentPam->GetMark()->nContent.GetIndex(); + } + + // are there any hard attributes that must be written as options? + rHTMLWrt.m_bTagOn = true; + + // now, output the tag of the paragraph + const SwFormat& rFormat = pNd->GetAnyFormatColl(); + SwHTMLTextCollOutputInfo aFormatInfo; + bool bOldLFPossible = rHTMLWrt.m_bLFPossible; + OutHTML_SwFormat( rWrt, rFormat, pNd->GetpSwAttrSet(), aFormatInfo ); + + // If we didn't open a new line before the paragraph tag, we do that now + rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE; + if( !bOldLFPossible && rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + + // then, the bookmarks (including end tag) + rHTMLWrt.m_bOutOpts = false; + rHTMLWrt.OutBookmarks(); + + // now it's a good opportunity again for an LF - if it is still allowed + if( rHTMLWrt.m_bLFPossible && + rHTMLWrt.GetLineLen() >= rHTMLWrt.m_nWhishLineLen ) + { + rHTMLWrt.OutNewLine(); + } + rHTMLWrt.m_bLFPossible = false; + + // find text that originates from an outline numbering + sal_Int32 nOffset = 0; + OUString aOutlineText; + OUString aFullText; + + // export numbering string as plain text only for the outline numbering, + // because the outline numbering isn't exported as a numbering - see <SwHTMLNumRuleInfo::Set(..)> + if ( pNd->IsOutline() && + pNd->GetNumRule() == pNd->GetDoc()->GetOutlineNumRule() ) + { + aOutlineText = pNd->GetNumString(); + nOffset = nOffset + aOutlineText.getLength(); + aFullText = aOutlineText; + } + OUString aFootEndNoteSym; + if( rHTMLWrt.m_pFormatFootnote ) + { + aFootEndNoteSym = rHTMLWrt.GetFootEndNoteSym( *rHTMLWrt.m_pFormatFootnote ); + nOffset = nOffset + aFootEndNoteSym.getLength(); + aFullText += aFootEndNoteSym; + } + + // Table of Contents or other paragraph with dot leaders? + sal_Int32 nIndexTab = rHTMLWrt.indexOfDotLeaders( nPoolId, rStr ); + if (nIndexTab > -1) + // skip part after the tabulator (page number) + nEnd = nIndexTab; + + // are there any hard attributes that must be written as tags? + aFullText += rStr; + HTMLEndPosLst aEndPosLst( rWrt.m_pDoc, rHTMLWrt.m_xTemplate.get(), + rHTMLWrt.m_xDfltColor, rHTMLWrt.m_bCfgOutStyles, + rHTMLWrt.GetHTMLMode(), aFullText, + rHTMLWrt.m_aScriptTextStyles ); + if( aFormatInfo.pItemSet ) + { + aEndPosLst.Insert( *aFormatInfo.pItemSet, 0, nEnd + nOffset, + rHTMLWrt.m_CharFormatInfos, false, true ); + } + + if( !aOutlineText.isEmpty() || rHTMLWrt.m_pFormatFootnote ) + { + // output paragraph attributes, so that the text gets the attributes of + // the paragraph. + aEndPosLst.OutStartAttrs( rHTMLWrt, 0 ); + + // Theoretically, we would have to consider the character style of + // the numbering. Because it cannot be set via the UI, let's ignore + // it for now. + + if( !aOutlineText.isEmpty() ) + HTMLOutFuncs::Out_String( rWrt.Strm(), aOutlineText, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters); + + if( rHTMLWrt.m_pFormatFootnote ) + { + rHTMLWrt.OutFootEndNoteSym( *rHTMLWrt.m_pFormatFootnote, aFootEndNoteSym, + aEndPosLst.GetScriptAtPos( aOutlineText.getLength(), rHTMLWrt.m_nCSS1Script ) ); + rHTMLWrt.m_pFormatFootnote = nullptr; + } + } + + // for now, correct the start. I.e., if we only output part of the sentence, + // the attributes must be correct there, as well!! + rHTMLWrt.m_bTextAttr = true; + + size_t nAttrPos = 0; + sal_Int32 nStrPos = rHTMLWrt.m_pCurrentPam->GetPoint()->nContent.GetIndex(); + const SwTextAttr * pHt = nullptr; + const size_t nCntAttr = pNd->HasHints() ? pNd->GetSwpHints().Count() : 0; + if( nCntAttr && nStrPos > ( pHt = pNd->GetSwpHints().Get(0) )->GetStart() ) + { + // Ok, there are earlier attributes that we must output + do { + aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset ); + + nAttrPos++; + if( pHt->Which() == RES_TXTATR_FIELD + || pHt->Which() == RES_TXTATR_ANNOTATION ) + continue; + + if ( pHt->End() && !pHt->HasDummyChar() ) + { + const sal_Int32 nHtEnd = *pHt->End(), + nHtStt = pHt->GetStart(); + if( !rHTMLWrt.m_bWriteAll && nHtEnd <= nStrPos ) + continue; + + // don't consider empty hints at the beginning - or should we ?? + if( nHtEnd == nHtStt ) + continue; + + // add attribute to the list + if( rHTMLWrt.m_bWriteAll ) + aEndPosLst.Insert( pHt->GetAttr(), nHtStt + nOffset, + nHtEnd + nOffset, + rHTMLWrt.m_CharFormatInfos ); + else + { + sal_Int32 nTmpStt = nHtStt < nStrPos ? nStrPos : nHtStt; + sal_Int32 nTmpEnd = std::min(nHtEnd, nEnd); + aEndPosLst.Insert( pHt->GetAttr(), nTmpStt + nOffset, + nTmpEnd + nOffset, + rHTMLWrt.m_CharFormatInfos ); + } + continue; + // but don't output it, that will be done later !! + } + + } while( nAttrPos < nCntAttr && nStrPos > + ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() ); + + // so, let's output all collected attributes from the string pos on + aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset ); + aEndPosLst.OutStartAttrs( rHTMLWrt, nStrPos + nOffset ); + } + + bool bWriteBreak = (HtmlTokenId::PREFORMTXT_ON != rHTMLWrt.m_nLastParaToken); + if( bWriteBreak && pNd->GetNumRule() ) + bWriteBreak = false; + + { + HTMLOutContext aContext( rHTMLWrt.m_eDestEnc ); + + for( ; nStrPos < nEnd; nStrPos++ ) + { + // output the frames that are anchored to the current position + if( bFlysLeft ) + { + aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset, &aContext ); + bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), + nStrPos, HtmlPosition::Inside, + &aContext ); + } + + bool bOutChar = true; + const SwTextAttr * pTextHt = nullptr; + if (nAttrPos < nCntAttr && pHt->GetStart() == nStrPos) + { + do { + if ( pHt->End() && !pHt->HasDummyChar() ) + { + if( *pHt->End() != nStrPos ) + { + // insert hints with end, if they don't start + // an empty range (hints that don't start a range + // are ignored) + aEndPosLst.Insert( pHt->GetAttr(), nStrPos + nOffset, + *pHt->End() + nOffset, + rHTMLWrt.m_CharFormatInfos ); + } + } + else + { + // hints without an end are output last + OSL_ENSURE( !pTextHt, "Why is there already an attribute without an end?" ); + if( rHTMLWrt.m_nTextAttrsToIgnore>0 ) + { + rHTMLWrt.m_nTextAttrsToIgnore--; + } + else + { + pTextHt = pHt; + SwFieldIds nFieldWhich; + if( RES_TXTATR_FIELD != pHt->Which() + || ( SwFieldIds::Postit != (nFieldWhich = static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->Which()) + && SwFieldIds::Script != nFieldWhich ) ) + { + bWriteBreak = false; + } + } + bOutChar = false; // don't output 255 + } + } while( ++nAttrPos < nCntAttr && nStrPos == + ( pHt = pNd->GetSwpHints().Get( nAttrPos ) )->GetStart() ); + } + + // Additionally, some draw formats can bring attributes + if( pTextHt && RES_TXTATR_FLYCNT == pTextHt->Which() ) + { + const SwFrameFormat* pFrameFormat = + static_cast<const SwFormatFlyCnt &>(pTextHt->GetAttr()).GetFrameFormat(); + + if( RES_DRAWFRMFMT == pFrameFormat->Which() ) + aEndPosLst.Insert( *static_cast<const SwDrawFrameFormat *>(pFrameFormat), + nStrPos + nOffset, + rHTMLWrt.m_CharFormatInfos ); + } + + aEndPosLst.OutEndAttrs( rHTMLWrt, nStrPos + nOffset, &aContext ); + aEndPosLst.OutStartAttrs( rHTMLWrt, nStrPos + nOffset, &aContext ); + + if( pTextHt ) + { + rHTMLWrt.m_bLFPossible = rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && + nStrPos > 0 && + rStr[nStrPos-1] == ' '; + sal_uInt16 nCSS1Script = rHTMLWrt.m_nCSS1Script; + rHTMLWrt.m_nCSS1Script = aEndPosLst.GetScriptAtPos( + nStrPos + nOffset, nCSS1Script ); + HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext ); + Out( aHTMLAttrFnTab, pTextHt->GetAttr(), rHTMLWrt ); + rHTMLWrt.m_nCSS1Script = nCSS1Script; + rHTMLWrt.m_bLFPossible = false; + } + + if( bOutChar ) + { + sal_uInt32 c = rStr[nStrPos]; + if( rtl::isHighSurrogate(c) && nStrPos < nEnd - 1 ) + { + const sal_Unicode d = rStr[nStrPos + 1]; + if( rtl::isLowSurrogate(d) ) + { + c = rtl::combineSurrogates(c, d); + nStrPos++; + } + } + + // try to split a line after about 255 characters + // at a space character unless in a PRE-context + if( ' ' == c && rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE ) + { + sal_Int32 nLineLen; + nLineLen = rHTMLWrt.GetLineLen(); + + sal_Int32 nWordLen = rStr.indexOf( ' ', nStrPos+1 ); + if( nWordLen == -1 ) + nWordLen = nEnd; + nWordLen -= nStrPos; + + if( nLineLen >= rHTMLWrt.m_nWhishLineLen || + (nLineLen+nWordLen) >= rHTMLWrt.m_nWhishLineLen ) + { + HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext ); + rHTMLWrt.OutNewLine(); + bOutChar = false; + } + } + + if( bOutChar ) + { + if( 0x0a == c ) + { + HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext ); + HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace); + aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak); + } + else if (c == CH_TXT_ATR_FORMELEMENT) + { + // Placeholder for a single-point fieldmark. + + SwPosition aMarkPos = *rWrt.m_pCurrentPam->GetPoint(); + aMarkPos.nContent += nStrPos - aMarkPos.nContent.GetIndex(); + rHTMLWrt.OutPointFieldmarks(aMarkPos); + } + else + HTMLOutFuncs::Out_Char( rWrt.Strm(), c, aContext, &rHTMLWrt.m_aNonConvertableCharacters ); + + // if a paragraph's last character is a hard line break + // then we need to add an extra <br> + // because browsers like Mozilla wouldn't add a line for the next paragraph + bWriteBreak = (0x0a == c) && + (HtmlTokenId::PREFORMTXT_ON != rHTMLWrt.m_nLastParaToken); + } + } + } + HTMLOutFuncs::FlushToAscii( rWrt.Strm(), aContext ); + } + + aEndPosLst.OutEndAttrs( rHTMLWrt, SAL_MAX_INT32 ); + + // Output the frames that are anchored to the last position + if( bFlysLeft ) + bFlysLeft = rHTMLWrt.OutFlyFrame( rNode.GetIndex(), + nEnd, HtmlPosition::Inside ); + OSL_ENSURE( !bFlysLeft, "Not all frames were saved!" ); + + rHTMLWrt.m_bTextAttr = false; + + if( bWriteBreak ) + { + bool bEndOfCell = rHTMLWrt.m_bOutTable && + rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() == + rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex(); + + if( bEndOfCell && !nEnd && + rHTMLWrt.IsHTMLMode(HTMLMODE_NBSP_IN_TABLES) ) + { + // If the last paragraph of a table cell is empty and we export + // for the MS-IE, we write a   instead of a <BR> + rWrt.Strm().WriteChar( '&' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_S_nbsp ).WriteChar( ';' ); + } + else + { + HtmlWriter aHtml(rHTMLWrt.Strm(), rHTMLWrt.maNamespace); + aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak); + const SvxULSpaceItem& rULSpace = pNd->GetSwAttrSet().Get(RES_UL_SPACE); + if (rULSpace.GetLower() > 0 && !bEndOfCell) + { + aHtml.single(OOO_STRING_SVTOOLS_HTML_linebreak); + } + rHTMLWrt.m_bLFPossible = true; + } + } + + if( rHTMLWrt.m_bClearLeft || rHTMLWrt.m_bClearRight ) + { + const char* pString; + if( rHTMLWrt.m_bClearLeft ) + { + if( rHTMLWrt.m_bClearRight ) + pString = OOO_STRING_SVTOOLS_HTML_AL_all; + else + pString = OOO_STRING_SVTOOLS_HTML_AL_left; + } + else + { + pString = OOO_STRING_SVTOOLS_HTML_AL_right; + } + + HtmlWriter aHtml(rHTMLWrt.Strm(), rHTMLWrt.maNamespace); + aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pString); + aHtml.end(); + + rHTMLWrt.m_bClearLeft = false; + rHTMLWrt.m_bClearRight = false; + + rHTMLWrt.m_bLFPossible = true; + } + + // if an LF is not allowed already, it is allowed once the paragraphs + // ends with a ' ' + if( !rHTMLWrt.m_bLFPossible && + rHTMLWrt.m_nLastParaToken == HtmlTokenId::NONE && + nEnd > 0 && ' ' == rStr[nEnd-1] ) + rHTMLWrt.m_bLFPossible = true; + + // dot leaders: print the skipped page number in a different span element + if (nIndexTab > -1) { + OString sOut = OUStringToOString(rStr.copy(nIndexTab + 1), RTL_TEXTENCODING_ASCII_US); + rWrt.Strm().WriteOString( "</span><span>" + sOut + "</span>" ); + } + + rHTMLWrt.m_bTagOn = false; + OutHTML_SwFormatOff( rWrt, aFormatInfo ); + + // if necessary, close a form + rHTMLWrt.OutForm( false ); + + if( bPageBreakBehind ) + rWrt.Strm().WriteChar( '\f' ); + + return rHTMLWrt; +} + +sal_uInt32 SwHTMLWriter::ToPixel( sal_uInt32 nVal, const bool bVert ) +{ + if( Application::GetDefaultDevice() && nVal ) + { + Size aSz( bVert ? 0 : nVal, bVert ? nVal : 0 ); + aSz = Application::GetDefaultDevice()->LogicToPixel(aSz, MapMode( MapUnit::MapTwip )); + nVal = bVert ? aSz.Height() : aSz.Width(); + if( !nVal ) // if there is a Twip, there should be a pixel as well + nVal = 1; + } + return nVal; +} + +static Writer& OutHTML_CSS1Attr( Writer& rWrt, const SfxPoolItem& rHt ) +{ + // if hints are currently written, we try to write the hint as an + // CSS1 attribute + + if( static_cast<SwHTMLWriter&>(rWrt).m_bCfgOutStyles && static_cast<SwHTMLWriter&>(rWrt).m_bTextAttr ) + OutCSS1_HintSpanTag( rWrt, rHt ); + + return rWrt; +} + +/* File CHRATR.HXX: */ + +static Writer& OutHTML_SvxColor( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + if( !rHTMLWrt.m_bTextAttr && rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bCfgPreferStyles ) + { + // don't write the font color as a tag, if styles are preferred to + // normal tags + return rWrt; + } + + if( rHTMLWrt.m_bTagOn ) + { + Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() ); + if( COL_AUTO == aColor ) + aColor = COL_BLACK; + + if (rHTMLWrt.mbXHTML) + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span + " " OOO_STRING_SVTOOLS_HTML_O_style "="; + rWrt.Strm().WriteOString(sOut); + HTMLOutFuncs::Out_Color(rWrt.Strm(), aColor, /*bXHTML=*/true).WriteChar('>'); + } + else + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " " + OOO_STRING_SVTOOLS_HTML_O_color "="; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_Color( rWrt.Strm(), aColor ).WriteChar( '>' ); + } + } + else + { + if (rHTMLWrt.mbXHTML) + HTMLOutFuncs::Out_AsciiTag( + rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false); + else + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font, false ); + } + + return rWrt; +} + +static Writer& OutHTML_SwPosture( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const FontItalic nPosture = static_cast<const SvxPostureItem&>(rHt).GetPosture(); + if( ITALIC_NORMAL == nPosture ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_italic, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +static Writer& OutHTML_SvxFont( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + if (IgnorePropertyForReqIF(rHTMLWrt.mbReqIF, "font-family")) + { + return rWrt; + } + + if( rHTMLWrt.m_bTagOn ) + { + OUString aNames; + SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), aNames, 0, + rHTMLWrt.IsHTMLMode(HTMLMODE_FONT_GENERIC) ); + if (rHTMLWrt.mbXHTML) + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span + " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-family: "; + rWrt.Strm().WriteOString(sOut); + HTMLOutFuncs::Out_String(rWrt.Strm(), aNames, rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters) + .WriteCharPtr("\">"); + } + else + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font " " + OOO_STRING_SVTOOLS_HTML_O_face "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aNames, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ) + .WriteCharPtr( "\">" ); + } + } + else + { + if (rHTMLWrt.mbXHTML) + HTMLOutFuncs::Out_AsciiTag( + rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false); + else + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font, false ); + } + + return rWrt; +} + +static Writer& OutHTML_SvxFontHeight( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + if (IgnorePropertyForReqIF(rHTMLWrt.mbReqIF, "font-size")) + { + return rWrt; + } + + if( rHTMLWrt.m_bTagOn ) + { + if (rHTMLWrt.mbXHTML) + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span; + + sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight(); + // Twips -> points. + sal_uInt16 nSize = nHeight / 20; + sOut += " " OOO_STRING_SVTOOLS_HTML_O_style "=\"font-size: " + + OString::number(static_cast<sal_Int32>(nSize)) + "pt\""; + rWrt.Strm().WriteOString(sOut); + } + else + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font; + + sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight(); + sal_uInt16 nSize = rHTMLWrt.GetHTMLFontSize( nHeight ); + sOut += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" + + OString::number(static_cast<sal_Int32>(nSize)) + "\""; + rWrt.Strm().WriteOString( sOut ); + + if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // always export font size as CSS option, too + OutCSS1_HintStyleOpt( rWrt, rHt ); + } + } + rWrt.Strm().WriteChar( '>' ); + } + else + { + if (rHTMLWrt.mbXHTML) + HTMLOutFuncs::Out_AsciiTag( + rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false); + else + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_font, false ); + } + + return rWrt; +} + +static Writer& OutHTML_SvxLanguage( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage(); + if( LANGUAGE_DONTKNOW == eLang ) + return rWrt; + + if( rHTMLWrt.m_bTagOn ) + { + OString sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span; + rWrt.Strm().WriteOString( sOut ); + rHTMLWrt.OutLanguage( static_cast<const SvxLanguageItem &>(rHt).GetLanguage() ); + rWrt.Strm().WriteChar( '>' ); + } + else + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false ); + } + + return rWrt; +} +static Writer& OutHTML_SwWeight( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const FontWeight nBold = static_cast<const SvxWeightItem&>(rHt).GetWeight(); + if( WEIGHT_BOLD == nBold ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_bold, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute ? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +static Writer& OutHTML_SwCrossedOut( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + // Because of Netscape, we output STRIKE and not S! + const FontStrikeout nStrike = static_cast<const SvxCrossedOutItem&>(rHt).GetStrikeout(); + if( STRIKEOUT_NONE != nStrike && !rHTMLWrt.mbReqIF ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_strike, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +static Writer& OutHTML_SvxEscapement( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const SvxEscapement eEscape = + static_cast<SvxEscapement>(static_cast<const SvxEscapementItem&>(rHt).GetEnumValue()); + OString aTag; + switch( eEscape ) + { + case SvxEscapement::Superscript: aTag = OOO_STRING_SVTOOLS_HTML_superscript; break; + case SvxEscapement::Subscript: aTag = OOO_STRING_SVTOOLS_HTML_subscript; break; + default: + ; + } + + if( !aTag.isEmpty() ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + aTag, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +static Writer& OutHTML_SwUnderline( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const FontLineStyle eUnder = static_cast<const SvxUnderlineItem&>(rHt).GetLineStyle(); + if( LINESTYLE_NONE != eUnder && !rHTMLWrt.mbReqIF ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_underline, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +static Writer& OutHTML_SwFlyCnt( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + const SwFormatFlyCnt& rFlyCnt = static_cast<const SwFormatFlyCnt&>(rHt); + + const SwFrameFormat& rFormat = *rFlyCnt.GetFrameFormat(); + const SdrObject *pSdrObj = nullptr; + + SwHTMLFrameType eType = + static_cast<SwHTMLFrameType>(rHTMLWrt.GuessFrameType( rFormat, pSdrObj )); + AllHtmlFlags nMode = aHTMLOutFrameAsCharTable[eType][rHTMLWrt.m_nExportMode]; + rHTMLWrt.OutFrameFormat( nMode, rFormat, pSdrObj ); + return rWrt; +} + +// This is now our Blink item. Blinking is activated by setting the item to +// true! +static Writer& OutHTML_SwBlink( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + if( static_cast<const SvxBlinkItem&>(rHt).GetValue() ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_blink, rHTMLWrt.m_bTagOn ); + } + else if( rHTMLWrt.m_bCfgOutStyles && rHTMLWrt.m_bTextAttr ) + { + // maybe as CSS1 attribute? + OutCSS1_HintSpanTag( rWrt, rHt ); + } + + return rWrt; +} + +Writer& OutHTML_INetFormat( Writer& rWrt, const SwFormatINetFormat& rINetFormat, bool bOn ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OUString aURL( rINetFormat.GetValue() ); + const SvxMacroTableDtor *pMacTable = rINetFormat.GetMacroTable(); + bool bEvents = pMacTable != nullptr && !pMacTable->empty(); + + // Anything to output at all? + if( aURL.isEmpty() && !bEvents && rINetFormat.GetName().isEmpty() ) + return rWrt; + + // bOn controls if we are writing the opening or closing tag + if( !bOn ) + { + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor, false ); + return rWrt; + } + + OString sOut("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor); + + bool bScriptDependent = false; + { + const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( + RES_POOLCHR_INET_NORMAL ); + std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat)); + auto const it = rHTMLWrt.m_CharFormatInfos.find( pFormatInfo ); + if (it != rHTMLWrt.m_CharFormatInfos.end()) + { + bScriptDependent = (*it)->bScriptDependent; + } + } + if( !bScriptDependent ) + { + const SwCharFormat* pFormat = rWrt.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( + RES_POOLCHR_INET_VISIT ); + std::unique_ptr<SwHTMLFormatInfo> pFormatInfo(new SwHTMLFormatInfo(pFormat)); + auto const it = rHTMLWrt.m_CharFormatInfos.find( pFormatInfo ); + if (it != rHTMLWrt.m_CharFormatInfos.end()) + { + bScriptDependent = (*it)->bScriptDependent; + } + } + + if( bScriptDependent ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""; + const char* pStr = nullptr; + switch( rHTMLWrt.m_nCSS1Script ) + { + case CSS1_OUTMODE_WESTERN: + pStr = "western"; + break; + case CSS1_OUTMODE_CJK: + pStr = "cjk"; + break; + case CSS1_OUTMODE_CTL: + pStr = "ctl"; + break; + } + sOut += pStr + OStringLiteral("\""); + } + + rWrt.Strm().WriteOString( sOut ); + sOut = ""; + + OUString sRel; + + if( !aURL.isEmpty() || bEvents ) + { + OUString sTmp( aURL.toAsciiUpperCase() ); + sal_Int32 nPos = sTmp.indexOf( "\" REL=" ); + if( nPos >= 0 ) + { + sRel = aURL.copy( nPos+1 ); + aURL = aURL.copy( 0, nPos); + } + aURL = comphelper::string::strip(aURL, ' '); + + sOut += " " OOO_STRING_SVTOOLS_HTML_O_href "=\""; + rWrt.Strm().WriteOString( sOut ); + rHTMLWrt.OutHyperlinkHRefValue( aURL ); + sOut = "\""; + } + + if( !rINetFormat.GetName().isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rINetFormat.GetName(), + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + + const OUString& rTarget = rINetFormat.GetTargetFrame(); + if( !rTarget.isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rTarget, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + + if( !sRel.isEmpty() ) + sOut += OUStringToOString(sRel, RTL_TEXTENCODING_ASCII_US); + + if( !sOut.isEmpty() ) + rWrt.Strm().WriteOString( sOut ); + + if( bEvents ) + HTMLOutFuncs::Out_Events( rWrt.Strm(), *pMacTable, aAnchorEventTable, + rHTMLWrt.m_bCfgStarBasic, rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters ); + rWrt.Strm().WriteCharPtr( ">" ); + + return rWrt; +} + +static Writer& OutHTML_SwFormatINetFormat( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const SwFormatINetFormat& rINetFormat = static_cast<const SwFormatINetFormat&>(rHt); + + if( rHTMLWrt.m_bTagOn ) + { + // if necessary, temporarily close an attribute that is still open + if( !rHTMLWrt.m_aINetFormats.empty() ) + { + SwFormatINetFormat *pINetFormat = + rHTMLWrt.m_aINetFormats.back(); + OutHTML_INetFormat( rWrt, *pINetFormat, false ); + } + + // now, open the new one + OutHTML_INetFormat( rWrt, rINetFormat, true ); + + // and remember it + SwFormatINetFormat *pINetFormat = new SwFormatINetFormat( rINetFormat ); + rHTMLWrt.m_aINetFormats.push_back( pINetFormat ); + } + else + { + OutHTML_INetFormat( rWrt, rINetFormat, false ); + + OSL_ENSURE( rHTMLWrt.m_aINetFormats.size(), "there must be a URL attribute missing" ); + if( !rHTMLWrt.m_aINetFormats.empty() ) + { + // get its own attribute from the stack + SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back(); + rHTMLWrt.m_aINetFormats.pop_back(); + delete pINetFormat; + } + + if( !rHTMLWrt.m_aINetFormats.empty() ) + { + // there is still an attribute on the stack that must be reopened + SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back(); + OutHTML_INetFormat( rWrt, *pINetFormat, true ); + } + } + + return rWrt; +} + +static Writer& OutHTML_SwTextCharFormat( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( rHTMLWrt.m_bOutOpts ) + return rWrt; + + const SwFormatCharFormat& rChrFormat = static_cast<const SwFormatCharFormat&>(rHt); + const SwCharFormat* pFormat = rChrFormat.GetCharFormat(); + + if( !pFormat ) + { + return rWrt; + } + + std::unique_ptr<SwHTMLFormatInfo> pTmpInfo(new SwHTMLFormatInfo(pFormat)); + SwHTMLFormatInfos::const_iterator it = rHTMLWrt.m_CharFormatInfos.find(pTmpInfo); + if (it == rHTMLWrt.m_CharFormatInfos.end()) + return rWrt; + + const SwHTMLFormatInfo *pFormatInfo = it->get(); + OSL_ENSURE( pFormatInfo, "Why is there no information about the character style?" ); + + if( rHTMLWrt.m_bTagOn ) + { + OString sOut = "<" + rHTMLWrt.GetNamespace(); + if( !pFormatInfo->aToken.isEmpty() ) + sOut += pFormatInfo->aToken; + else + sOut += OString(OOO_STRING_SVTOOLS_HTML_span); + + if( rHTMLWrt.m_bCfgOutStyles && + (!pFormatInfo->aClass.isEmpty() || pFormatInfo->bScriptDependent) ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_class "=\""; + rWrt.Strm().WriteOString( sOut ); + OUString aClass( pFormatInfo->aClass ); + if( pFormatInfo->bScriptDependent ) + { + if( !aClass.isEmpty() ) + aClass += "-"; + switch( rHTMLWrt.m_nCSS1Script ) + { + case CSS1_OUTMODE_WESTERN: + aClass += "western"; + break; + case CSS1_OUTMODE_CJK: + aClass += "cjk"; + break; + case CSS1_OUTMODE_CTL: + aClass += "ctl"; + break; + } + } + HTMLOutFuncs::Out_String( rWrt.Strm(), aClass, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + sOut += ">"; + rWrt.Strm().WriteOString( sOut ); + } + else + { + OString aTag = !pFormatInfo->aToken.isEmpty() ? pFormatInfo->aToken.getStr() + : OOO_STRING_SVTOOLS_HTML_span; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rHTMLWrt.GetNamespace() + aTag, false); + } + + return rWrt; +} + +static Writer& OutHTML_SvxAdjust( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + if( !rHTMLWrt.m_bOutOpts || !rHTMLWrt.m_bTagOn ) + return rWrt; + + const SvxAdjustItem& rAdjust = static_cast<const SvxAdjustItem&>(rHt); + const char* pStr = nullptr; + switch( rAdjust.GetAdjust() ) + { + case SvxAdjust::Center: pStr = OOO_STRING_SVTOOLS_HTML_AL_center; break; + case SvxAdjust::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break; + case SvxAdjust::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break; + case SvxAdjust::Block: pStr = OOO_STRING_SVTOOLS_HTML_AL_justify; break; + default: + ; + } + if( pStr ) + { + OString sOut = OStringLiteral(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") + + pStr + "\""; + rWrt.Strm().WriteOString( sOut ); + } + + return rWrt; +} + +/* + * here, define the table for the HTML function pointers to the output + * functions. + */ + +SwAttrFnTab aHTMLAttrFnTab = { +/* RES_CHRATR_CASEMAP */ OutHTML_CSS1Attr, +/* RES_CHRATR_CHARSETCOLOR */ nullptr, +/* RES_CHRATR_COLOR */ OutHTML_SvxColor, +/* RES_CHRATR_CONTOUR */ nullptr, +/* RES_CHRATR_CROSSEDOUT */ OutHTML_SwCrossedOut, +/* RES_CHRATR_ESCAPEMENT */ OutHTML_SvxEscapement, +/* RES_CHRATR_FONT */ OutHTML_SvxFont, +/* RES_CHRATR_FONTSIZE */ OutHTML_SvxFontHeight, +/* RES_CHRATR_KERNING */ OutHTML_CSS1Attr, +/* RES_CHRATR_LANGUAGE */ OutHTML_SvxLanguage, +/* RES_CHRATR_POSTURE */ OutHTML_SwPosture, +/* RES_CHRATR_UNUSED1*/ nullptr, +/* RES_CHRATR_SHADOWED */ nullptr, +/* RES_CHRATR_UNDERLINE */ OutHTML_SwUnderline, +/* RES_CHRATR_WEIGHT */ OutHTML_SwWeight, +/* RES_CHRATR_WORDLINEMODE */ nullptr, +/* RES_CHRATR_AUTOKERN */ nullptr, +/* RES_CHRATR_BLINK */ OutHTML_SwBlink, +/* RES_CHRATR_NOHYPHEN */ nullptr, // New: don't hyphenate +/* RES_CHRATR_UNUSED2 */ nullptr, +/* RES_CHRATR_BACKGROUND */ OutHTML_CSS1Attr, // New: character background +/* RES_CHRATR_CJK_FONT */ OutHTML_SvxFont, +/* RES_CHRATR_CJK_FONTSIZE */ OutHTML_SvxFontHeight, +/* RES_CHRATR_CJK_LANGUAGE */ OutHTML_SvxLanguage, +/* RES_CHRATR_CJK_POSTURE */ OutHTML_SwPosture, +/* RES_CHRATR_CJK_WEIGHT */ OutHTML_SwWeight, +/* RES_CHRATR_CTL_FONT */ OutHTML_SvxFont, +/* RES_CHRATR_CTL_FONTSIZE */ OutHTML_SvxFontHeight, +/* RES_CHRATR_CTL_LANGUAGE */ OutHTML_SvxLanguage, +/* RES_CHRATR_CTL_POSTURE */ OutHTML_SwPosture, +/* RES_CHRATR_CTL_WEIGHT */ OutHTML_SwWeight, +/* RES_CHRATR_ROTATE */ nullptr, +/* RES_CHRATR_EMPHASIS_MARK */ nullptr, +/* RES_CHRATR_TWO_LINES */ nullptr, +/* RES_CHRATR_SCALEW */ nullptr, +/* RES_CHRATR_RELIEF */ nullptr, +/* RES_CHRATR_HIDDEN */ OutHTML_CSS1Attr, +/* RES_CHRATR_OVERLINE */ OutHTML_CSS1Attr, +/* RES_CHRATR_RSID */ nullptr, +/* RES_CHRATR_BOX */ OutHTML_CSS1Attr, +/* RES_CHRATR_SHADOW */ nullptr, +/* RES_CHRATR_HIGHLIGHT */ nullptr, +/* RES_CHRATR_GRABBAG */ nullptr, +/* RES_CHRATR_BIDIRTL */ nullptr, +/* RES_CHRATR_IDCTHINT */ nullptr, + +/* RES_TXTATR_REFMARK */ nullptr, +/* RES_TXTATR_TOXMARK */ nullptr, +/* RES_TXTATR_META */ nullptr, +/* RES_TXTATR_METAFIELD */ nullptr, +/* RES_TXTATR_AUTOFMT */ nullptr, +/* RES_TXTATR_INETFMT */ OutHTML_SwFormatINetFormat, +/* RES_TXTATR_CHARFMT */ OutHTML_SwTextCharFormat, +/* RES_TXTATR_CJK_RUBY */ nullptr, +/* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr, +/* RES_TXTATR_INPUTFIELD */ OutHTML_SwFormatField, + +/* RES_TXTATR_FIELD */ OutHTML_SwFormatField, +/* RES_TXTATR_FLYCNT */ OutHTML_SwFlyCnt, +/* RES_TXTATR_FTN */ OutHTML_SwFormatFootnote, +/* RES_TXTATR_ANNOTATION */ OutHTML_SwFormatField, +/* RES_TXTATR_DUMMY3 */ nullptr, +/* RES_TXTATR_DUMMY1 */ nullptr, // Dummy: +/* RES_TXTATR_DUMMY2 */ nullptr, // Dummy: + +/* RES_PARATR_LINESPACING */ nullptr, +/* RES_PARATR_ADJUST */ OutHTML_SvxAdjust, +/* RES_PARATR_SPLIT */ nullptr, +/* RES_PARATR_WIDOWS */ nullptr, +/* RES_PARATR_ORPHANS */ nullptr, +/* RES_PARATR_TABSTOP */ nullptr, +/* RES_PARATR_HYPHENZONE*/ nullptr, +/* RES_PARATR_DROP */ OutHTML_CSS1Attr, +/* RES_PARATR_REGISTER */ nullptr, // new: register-true +/* RES_PARATR_NUMRULE */ nullptr, // Dummy: +/* RES_PARATR_SCRIPTSPACE */ nullptr, // Dummy: +/* RES_PARATR_HANGINGPUNCTUATION */ nullptr, // Dummy: +/* RES_PARATR_FORBIDDEN_RULES */ nullptr, // new +/* RES_PARATR_VERTALIGN */ nullptr, // new +/* RES_PARATR_SNAPTOGRID*/ nullptr, // new +/* RES_PARATR_CONNECT_TO_BORDER */ nullptr, // new +/* RES_PARATR_OUTLINELEVEL */ nullptr, +/* RES_PARATR_RSID */ nullptr, +/* RES_PARATR_GRABBAG */ nullptr, + +/* RES_PARATR_LIST_ID */ nullptr, // new +/* RES_PARATR_LIST_LEVEL */ nullptr, // new +/* RES_PARATR_LIST_ISRESTART */ nullptr, // new +/* RES_PARATR_LIST_RESTARTVALUE */ nullptr, // new +/* RES_PARATR_LIST_ISCOUNTED */ nullptr, // new + +/* RES_FILL_ORDER */ nullptr, +/* RES_FRM_SIZE */ nullptr, +/* RES_PAPER_BIN */ nullptr, +/* RES_LR_SPACE */ nullptr, +/* RES_UL_SPACE */ nullptr, +/* RES_PAGEDESC */ nullptr, +/* RES_BREAK */ nullptr, +/* RES_CNTNT */ nullptr, +/* RES_HEADER */ nullptr, +/* RES_FOOTER */ nullptr, +/* RES_PRINT */ nullptr, +/* RES_OPAQUE */ nullptr, +/* RES_PROTECT */ nullptr, +/* RES_SURROUND */ nullptr, +/* RES_VERT_ORIENT */ nullptr, +/* RES_HORI_ORIENT */ nullptr, +/* RES_ANCHOR */ nullptr, +/* RES_BACKGROUND */ nullptr, +/* RES_BOX */ nullptr, +/* RES_SHADOW */ nullptr, +/* RES_FRMMACRO */ nullptr, +/* RES_COL */ nullptr, +/* RES_KEEP */ nullptr, +/* RES_URL */ nullptr, +/* RES_EDIT_IN_READONLY */ nullptr, +/* RES_LAYOUT_SPLIT */ nullptr, +/* RES_CHAIN */ nullptr, +/* RES_TEXTGRID */ nullptr, +/* RES_LINENUMBER */ nullptr, +/* RES_FTN_AT_TXTEND */ nullptr, +/* RES_END_AT_TXTEND */ nullptr, +/* RES_COLUMNBALANCE */ nullptr, +/* RES_FRAMEDIR */ nullptr, +/* RES_HEADER_FOOTER_EAT_SPACING */ nullptr, +/* RES_ROW_SPLIT */ nullptr, +/* RES_FOLLOW_TEXT_FLOW */ nullptr, +/* RES_COLLAPSING_BORDERS */ nullptr, +/* RES_WRAP_INFLUENCE_ON_OBJPOS */ nullptr, +/* RES_AUTO_STYLE */ nullptr, +/* RES_FRMATR_STYLE_NAME */ nullptr, +/* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr, +/* RES_FRMATR_GRABBAG */ nullptr, +/* RES_TEXT_VERT_ADJUST */ nullptr, + +/* RES_GRFATR_MIRRORGRF */ nullptr, +/* RES_GRFATR_CROPGRF */ nullptr, +/* RES_GRFATR_ROTATION */ nullptr, +/* RES_GRFATR_LUMINANCE */ nullptr, +/* RES_GRFATR_CONTRAST */ nullptr, +/* RES_GRFATR_CHANNELR */ nullptr, +/* RES_GRFATR_CHANNELG */ nullptr, +/* RES_GRFATR_CHANNELB */ nullptr, +/* RES_GRFATR_GAMMA */ nullptr, +/* RES_GRFATR_INVERT */ nullptr, +/* RES_GRFATR_TRANSPARENCY */ nullptr, +/* RES_GRFATR_DRWAMODE */ nullptr, +/* RES_GRFATR_DUMMY1 */ nullptr, +/* RES_GRFATR_DUMMY2 */ nullptr, +/* RES_GRFATR_DUMMY3 */ nullptr, +/* RES_GRFATR_DUMMY4 */ nullptr, +/* RES_GRFATR_DUMMY5 */ nullptr, + +/* RES_BOXATR_FORMAT */ nullptr, +/* RES_BOXATR_FORMULA */ nullptr, +/* RES_BOXATR_VALUE */ nullptr +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlatr.hxx b/sw/source/filter/html/htmlatr.hxx new file mode 100644 index 000000000..642a7c048 --- /dev/null +++ b/sw/source/filter/html/htmlatr.hxx @@ -0,0 +1,21 @@ +/* -*- 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_FILTER_HTML_HTMLATR_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLATR_HXX + +#include <sal/config.h> + +struct HTMLOutEvent; + +extern HTMLOutEvent const aAnchorEventTable[]; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/filter/html/htmlbas.cxx b/sw/source/filter/html/htmlbas.cxx new file mode 100644 index 000000000..8071e419c --- /dev/null +++ b/sw/source/filter/html/htmlbas.cxx @@ -0,0 +1,331 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <osl/diagnose.h> +#include <basic/basmgr.hxx> +#include <basic/sbmod.hxx> +#include <sfx2/evntconf.hxx> +#include <sfx2/app.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> + +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/container/XNameContainer.hpp> + +#include <fmtfld.hxx> + +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <docsh.hxx> +#include <docufld.hxx> +#include "wrthtml.hxx" +#include "swhtml.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + +static HTMLOutEvent const aBodyEventTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OpenDoc }, + { OOO_STRING_SVTOOLS_HTML_O_SDonunload, OOO_STRING_SVTOOLS_HTML_O_onunload, SvMacroItemId::PrepareCloseDoc }, + { OOO_STRING_SVTOOLS_HTML_O_SDonfocus, OOO_STRING_SVTOOLS_HTML_O_onfocus, SvMacroItemId::ActivateDoc }, + { OOO_STRING_SVTOOLS_HTML_O_SDonblur, OOO_STRING_SVTOOLS_HTML_O_onblur, SvMacroItemId::DeactivateDoc }, + { nullptr, nullptr, SvMacroItemId::NONE } +}; + +void SwHTMLParser::NewScript() +{ + ParseScriptOptions( m_aScriptType, m_sBaseURL, m_eScriptLang, m_aScriptURL, + m_aBasicLib, m_aBasicModule ); + + if( !m_aScriptURL.isEmpty() ) + { + // Ignore the script tag + m_bIgnoreRawData = true; + } +} + +void SwHTMLParser::EndScript() +{ + bool bInsIntoBasic = false, + bInsSrcIntoField = false; + + switch( m_eScriptLang ) + { + case HTMLScriptLanguage::StarBasic: + bInsIntoBasic = true; + break; + default: + bInsSrcIntoField = true; + break; + } + + m_bIgnoreRawData = false; + m_aScriptSource = convertLineEnd(m_aScriptSource, GetSystemLineEnd()); + + // Except for StarBasic and unused JavaScript, save each script or module name in a field + if( bInsSrcIntoField && !m_bIgnoreHTMLComments ) + { + SwScriptFieldType *pType = + static_cast<SwScriptFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Script )); + + SwScriptField aField( pType, m_aScriptType, + !m_aScriptURL.isEmpty() ? m_aScriptURL : m_aScriptSource, + !m_aScriptURL.isEmpty() ); + InsertAttr( SwFormatField( aField ), false ); + } + + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + if( !m_aScriptSource.isEmpty() && pDocSh && + bInsIntoBasic && IsNewDoc() ) + { + // Create a Basic module for javascript and StarBasic. + + // The Basic does still not remove SGML comments + RemoveSGMLComment( m_aScriptSource ); + + // get library name + OUString aLibName; + if( !m_aBasicLib.isEmpty() ) + aLibName = m_aBasicLib; + else + aLibName = "Standard"; + + // get module library container + Reference< script::XLibraryContainer > xModLibContainer = pDocSh->GetBasicContainer(); + + if ( xModLibContainer.is() ) + { + Reference< container::XNameContainer > xModLib; + if ( xModLibContainer->hasByName( aLibName ) ) + { + // get module library + Any aElement = xModLibContainer->getByName( aLibName ); + aElement >>= xModLib; + } + else + { + // create module library + xModLib = xModLibContainer->createLibrary( aLibName ); + } + + if ( xModLib.is() ) + { + if( m_aBasicModule.isEmpty() ) + { + // create module name + bool bFound = true; + while( bFound ) + { + m_aBasicModule = "Modul" + OUString::number( static_cast<sal_Int32>(++m_nSBModuleCnt) ); + bFound = xModLib->hasByName( m_aBasicModule ); + } + } + + // create module + OUString aModName( m_aBasicModule ); + if ( !xModLib->hasByName( aModName ) ) + { + Any aElement; + aElement <<= m_aScriptSource; + xModLib->insertByName( aModName , aElement ); + } + } + } + + // get dialog library container + Reference< script::XLibraryContainer > xDlgLibContainer = pDocSh->GetDialogContainer(); + + if ( xDlgLibContainer.is() ) + { + if ( !xDlgLibContainer->hasByName( aLibName ) ) + { + // create dialog library + xDlgLibContainer->createLibrary( aLibName ); + } + } + } + + m_aScriptSource.clear(); + m_aScriptType.clear(); + m_aScriptURL.clear(); + + m_aBasicLib.clear(); + m_aBasicModule.clear(); +} + +void SwHTMLParser::AddScriptSource() +{ + // We'll just remember a few strings here + if( aToken.getLength() > 2 && + (HTMLScriptLanguage::StarBasic==m_eScriptLang && aToken[ 0 ] == '\'') ) + { + sal_Int32 nPos = -1; + if( m_aBasicLib.isEmpty() ) + { + nPos = aToken.indexOf( OOO_STRING_SVTOOLS_HTML_SB_library ); + if( nPos != -1 ) + { + m_aBasicLib = + aToken.copy( nPos + sizeof(OOO_STRING_SVTOOLS_HTML_SB_library) - 1 ); + m_aBasicLib = comphelper::string::strip(m_aBasicLib, ' '); + } + } + + if( m_aBasicModule.isEmpty() && nPos == -1 ) + { + nPos = aToken.indexOf( OOO_STRING_SVTOOLS_HTML_SB_module ); + if( nPos != -1 ) + { + m_aBasicModule = + aToken.copy( nPos + sizeof(OOO_STRING_SVTOOLS_HTML_SB_module) - 1 ); + m_aBasicModule = comphelper::string::strip(m_aBasicModule, ' '); + } + } + + if( nPos == -1 ) + { + if( !m_aScriptSource.isEmpty() ) + m_aScriptSource += "\n"; + m_aScriptSource += aToken; + } + } + else if( !m_aScriptSource.isEmpty() || !aToken.isEmpty() ) + { + // Empty lines are ignored on the beginning + if( !m_aScriptSource.isEmpty() ) + { + m_aScriptSource += "\n"; + } + m_aScriptSource += aToken; + } +} + +void SwHTMLParser::InsertBasicDocEvent( const OUString& aEvent, const OUString& rName, + ScriptType eScrType, + const OUString& rScrType ) +{ + OSL_ENSURE( !rName.isEmpty(), "InsertBasicDocEvent() called without macro" ); + if( rName.isEmpty() ) + return; + + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + OSL_ENSURE( pDocSh, "Where is the DocShell?" ); + if( !pDocSh ) + return; + + OUString sEvent(convertLineEnd(rName, GetSystemLineEnd())); + OUString sScriptType; + if( EXTENDED_STYPE == eScrType ) + sScriptType = rScrType; + + SfxEventConfiguration::ConfigureEvent( aEvent, SvxMacro( sEvent, sScriptType, eScrType ), + pDocSh ); +} + +void SwHTMLWriter::OutBasic(SwHTMLWriter & rHTMLWrt) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) rHTMLWrt; +#else + if( !m_bCfgStarBasic ) + return; + + BasicManager *pBasicMan = m_pDoc->GetDocShell()->GetBasicManager(); + OSL_ENSURE( pBasicMan, "Where is the Basic-Manager?" ); + // Only write DocumentBasic + if( !pBasicMan || pBasicMan == SfxApplication::GetBasicManager() ) + { + return; + } + + bool bFirst=true; + // Now write all StarBasic and unused Javascript modules + for( sal_uInt16 i=0; i<pBasicMan->GetLibCount(); i++ ) + { + StarBASIC *pBasic = pBasicMan->GetLib( i ); + const OUString& rLibName = pBasic->GetName(); + for( const auto& pModule: pBasic->GetModules() ) + { + OUString sLang(SVX_MACRO_LANGUAGE_STARBASIC); + + if( bFirst ) + { + bFirst = false; + OutNewLine(); + OString sOut = + "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_meta + " " OOO_STRING_SVTOOLS_HTML_O_httpequiv + "=\"" + OOO_STRING_SVTOOLS_HTML_META_content_script_type + "\" " OOO_STRING_SVTOOLS_HTML_O_content + "=\"text/x-"; + Strm().WriteOString( sOut ); + // Entities aren't welcome here + Strm().WriteOString( OUStringToOString(sLang, m_eDestEnc) ) + .WriteCharPtr( "\">" ); + } + + const OUString& rModName = pModule->GetName(); + Strm().WriteCharPtr( SAL_NEWLINE_STRING ); // don't indent! + HTMLOutFuncs::OutScript( Strm(), GetBaseURL(), pModule->GetSource32(), + sLang, STARBASIC, OUString(), + &rLibName, &rModName, + m_eDestEnc, &m_aNonConvertableCharacters ); + } + } +#endif +} + +static const char* aEventNames[] = +{ + "OnLoad", "OnPrepareUnload", "OnFocus", "OnUnfocus" +}; + +void SwHTMLWriter::OutBasicBodyEvents() +{ + SwDocShell *pDocSh = m_pDoc->GetDocShell(); + if( !pDocSh ) + return; + + SvxMacroTableDtor aDocTable; + + uno::Reference< document::XEventsSupplier > xSup( pDocSh->GetModel(), uno::UNO_QUERY ); + uno::Reference < container::XNameReplace > xEvents = xSup->getEvents(); + for ( sal_Int32 i=0; i<4; i++ ) + { + std::unique_ptr<SvxMacro> pMacro = SfxEventConfiguration::ConvertToMacro( xEvents->getByName( OUString::createFromAscii(aEventNames[i]) ), pDocSh ); + if ( pMacro ) + { + aDocTable.Insert( aBodyEventTable[i].nEvent, *pMacro ); + } + } + + if( !aDocTable.empty() ) + HTMLOutFuncs::Out_Events( Strm(), aDocTable, aBodyEventTable, + m_bCfgStarBasic, m_eDestEnc, &m_aNonConvertableCharacters ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlcss1.cxx b/sw/source/filter/html/htmlcss1.cxx new file mode 100644 index 000000000..ce608edc9 --- /dev/null +++ b/sw/source/filter/html/htmlcss1.cxx @@ -0,0 +1,2332 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/urihelper.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <sfx2/docfile.hxx> +#include <editeng/editids.hrc> +#include <editeng/fhgtitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <fmtpdsc.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmtsrnd.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <charfmt.hxx> +#include <docary.hxx> +#include <osl/diagnose.h> + +#include <doc.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pam.hxx> +#include <poolfmt.hxx> +#include <docsh.hxx> +#include <paratr.hxx> +#include <pagedesc.hxx> +#include "css1kywd.hxx" +#include "swcss1.hxx" +#include "htmlnum.hxx" +#include "swhtml.hxx" +#include <numrule.hxx> +#include "css1atr.hxx" + +using namespace ::com::sun::star; + +// How many rows/characters are allowed for DropCaps? +// (Are there maybe somewhere else corresponding values?) +#define MAX_DROPCAP_LINES 9 +#define MAX_DROPCAP_CHARS 9 + +static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc ); + +// Implementation of SwCSS1Parsers (actually swcss1.cxx) +static const sal_uInt16 aItemIds[] = +{ + RES_BREAK, + RES_PAGEDESC, + RES_KEEP, +}; + +void SwCSS1Parser::ChgPageDesc( const SwPageDesc *pPageDesc, + const SwPageDesc& rNewPageDesc ) +{ + size_t pos; + bool found = m_pDoc->ContainsPageDesc( pPageDesc, &pos ); + OSL_ENSURE( found, "style not found" ); + if (found) + m_pDoc->ChgPageDesc( pos, rNewPageDesc ); +} + +SwCSS1Parser::SwCSS1Parser(SwDoc *const pDoc, SwHTMLParser const& rParser, + const sal_uInt32 aFHeights[7], const OUString& rBaseURL, bool const bNewDoc) + : SvxCSS1Parser(pDoc->GetAttrPool(), rBaseURL, + aItemIds, SAL_N_ELEMENTS(aItemIds)) + , m_pDoc( pDoc ) + , m_rHTMLParser(rParser) + , m_nDropCapCnt( 0 ), + m_bIsNewDoc( bNewDoc ), + m_bBodyBGColorSet( false ), + m_bBodyBackgroundSet( false ), + m_bBodyTextSet( false ), + m_bBodyLinkSet( false ), + m_bBodyVLinkSet( false ), + m_bSetFirstPageDesc( false ), + m_bSetRightPageDesc( false ), + m_bTableHeaderTextCollSet( false ), + m_bTableTextCollSet( false ), + m_bLinkCharFormatsSet( false ) +{ + m_aFontHeights[0] = aFHeights[0]; + m_aFontHeights[1] = aFHeights[1]; + m_aFontHeights[2] = aFHeights[2]; + m_aFontHeights[3] = aFHeights[3]; + m_aFontHeights[4] = aFHeights[4]; + m_aFontHeights[5] = aFHeights[5]; + m_aFontHeights[6] = aFHeights[6]; +} + +SwCSS1Parser::~SwCSS1Parser() +{ +} + +// Feature: PrintExt +bool SwCSS1Parser::SetFormatBreak( SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rPropInfo ) +{ + SvxBreak eBreak = SvxBreak::NONE; + bool bKeep = false; + bool bSetKeep = false, bSetBreak = false, bSetPageDesc = false; + const SwPageDesc *pPageDesc = nullptr; + switch( rPropInfo.m_ePageBreakBefore ) + { + case SVX_CSS1_PBREAK_ALWAYS: + eBreak = SvxBreak::PageBefore; + bSetBreak = true; + break; + case SVX_CSS1_PBREAK_LEFT: + pPageDesc = GetLeftPageDesc( true ); + bSetPageDesc = true; + break; + case SVX_CSS1_PBREAK_RIGHT: + pPageDesc = GetRightPageDesc( true ); + bSetPageDesc = true; + break; + case SVX_CSS1_PBREAK_AUTO: + bSetBreak = bSetPageDesc = true; + break; + default: + ; + } + switch( rPropInfo.m_ePageBreakAfter ) + { + case SVX_CSS1_PBREAK_ALWAYS: + case SVX_CSS1_PBREAK_LEFT: + case SVX_CSS1_PBREAK_RIGHT: + // LEFT/RIGHT also could be set in the previous paragraph + eBreak = SvxBreak::PageAfter; + bSetBreak = true; + break; + case SVX_CSS1_PBREAK_AUTO: + bSetBreak = bSetKeep = bSetPageDesc = true; + break; + case SVX_CSS1_PBREAK_AVOID: + bKeep = bSetKeep = true; + break; + default: + ; + } + + if( bSetBreak ) + rItemSet.Put( SvxFormatBreakItem( eBreak, RES_BREAK ) ); + if( bSetPageDesc ) + rItemSet.Put( SwFormatPageDesc( pPageDesc ) ); + if( bSetKeep ) + rItemSet.Put( SvxFormatKeepItem( bKeep, RES_KEEP ) ); + + return bSetBreak; +} + +static void SetCharFormatAttrs( SwCharFormat *pCharFormat, SfxItemSet& rItemSet ) +{ + const SfxPoolItem *pItem; + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CTL_FONTSIZE }; + for(sal_uInt16 i : aWhichIds) + { + if( SfxItemState::SET == rItemSet.GetItemState( i, false, + &pItem ) && + static_cast<const SvxFontHeightItem *>(pItem)->GetProp() != 100) + { + // percentage values at the FontHeight item aren't supported + rItemSet.ClearItem( i ); + } + } + + pCharFormat->SetFormatAttr( rItemSet ); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + { + // A Brush-Item with RES_BACKGROUND must be converted to one + // with RES_CHRATR_BACKGROUND + + SvxBrushItem aBrushItem( *static_cast<const SvxBrushItem *>(pItem) ); + aBrushItem.SetWhich( RES_CHRATR_BACKGROUND ); + pCharFormat->SetFormatAttr( aBrushItem ); + } + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false, &pItem ) ) + { + SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem *>(pItem) ); + aBoxItem.SetWhich( RES_CHRATR_BOX ); + pCharFormat->SetFormatAttr( aBoxItem ); + } +} + +void SwCSS1Parser::SetLinkCharFormats() +{ + OSL_ENSURE( !m_bLinkCharFormatsSet, "Call SetLinkCharFormats unnecessary" ); + + SvxCSS1MapEntry *pStyleEntry = + GetTag( OOO_STRING_SVTOOLS_HTML_anchor ); + SwCharFormat *pUnvisited = nullptr, *pVisited = nullptr; + if( pStyleEntry ) + { + SfxItemSet& rItemSet = pStyleEntry->GetItemSet(); + bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR, + false)); + pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ); + SetCharFormatAttrs( pUnvisited, rItemSet ); + m_bBodyLinkSet |= bColorSet; + + pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ); + SetCharFormatAttrs( pVisited, rItemSet ); + m_bBodyVLinkSet |= bColorSet; + } + + OUString sTmp = OOO_STRING_SVTOOLS_HTML_anchor ":link"; + + pStyleEntry = GetTag( sTmp ); + if( pStyleEntry ) + { + SfxItemSet& rItemSet = pStyleEntry->GetItemSet(); + bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR, + false)); + if( !pUnvisited ) + pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ); + SetCharFormatAttrs( pUnvisited, rItemSet ); + m_bBodyLinkSet |= bColorSet; + } + + sTmp = OOO_STRING_SVTOOLS_HTML_anchor ":visited"; + + pStyleEntry = GetTag( sTmp ); + if( pStyleEntry ) + { + SfxItemSet& rItemSet = pStyleEntry->GetItemSet(); + bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR, + false)); + if( !pVisited ) + pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ); + SetCharFormatAttrs( pVisited, rItemSet ); + m_bBodyVLinkSet |= bColorSet; + } + + m_bLinkCharFormatsSet = true; +} + +static void SetTextCollAttrs( SwTextFormatColl *pColl, SfxItemSet& rItemSet, + SvxCSS1PropertyInfo const & rPropInfo, + SwCSS1Parser *pCSS1Parser ) +{ + const SfxItemSet& rCollItemSet = pColl->GetAttrSet(); + const SfxPoolItem *pCollItem, *pItem; + + // left, right border and first line indentation + if( (rPropInfo.m_bLeftMargin || rPropInfo.m_bRightMargin || + rPropInfo.m_bTextIndent) && + (!rPropInfo.m_bLeftMargin || !rPropInfo.m_bRightMargin || + !rPropInfo.m_bTextIndent) && + SfxItemState::SET == rCollItemSet.GetItemState(RES_LR_SPACE,true,&pCollItem) && + SfxItemState::SET == rItemSet.GetItemState(RES_LR_SPACE,false,&pItem) ) + { + const SvxLRSpaceItem *pLRItem = static_cast<const SvxLRSpaceItem *>(pItem); + + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem *>(pCollItem) ); + if( rPropInfo.m_bLeftMargin ) + aLRItem.SetTextLeft( pLRItem->GetTextLeft() ); + if( rPropInfo.m_bRightMargin ) + aLRItem.SetRight( pLRItem->GetRight() ); + if( rPropInfo.m_bTextIndent ) + aLRItem.SetTextFirstLineOffset( pLRItem->GetTextFirstLineOffset() ); + + rItemSet.Put( aLRItem ); + } + + // top and bottom border + if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) && + (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) && + SfxItemState::SET == rCollItemSet.GetItemState(RES_UL_SPACE,true, + &pCollItem) && + SfxItemState::SET == rItemSet.GetItemState(RES_UL_SPACE,false,&pItem) ) + { + const SvxULSpaceItem *pULItem = static_cast<const SvxULSpaceItem *>(pItem); + + SvxULSpaceItem aULItem( *static_cast<const SvxULSpaceItem *>(pCollItem) ); + if( rPropInfo.m_bTopMargin ) + aULItem.SetUpper( pULItem->GetUpper() ); + if( rPropInfo.m_bBottomMargin ) + aULItem.SetLower( pULItem->GetLower() ); + + rItemSet.Put( aULItem ); + } + + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CTL_FONTSIZE }; + for(sal_uInt16 i : aWhichIds) + { + if( SfxItemState::SET == rItemSet.GetItemState( i, false, + &pItem ) && + static_cast<const SvxFontHeightItem *>(pItem)->GetProp() != 100) + { + // percentage values at the FontHeight item aren't supported + rItemSet.ClearItem( i ); + } + } + + pCSS1Parser->SetFormatBreak( rItemSet, rPropInfo ); + + pColl->SetFormatAttr( rItemSet ); +} + +void SwCSS1Parser::SetTableTextColl( bool bHeader ) +{ + OSL_ENSURE( !(bHeader ? m_bTableHeaderTextCollSet : m_bTableTextCollSet), + "Call SetTableTextColl unnecessary" ); + + sal_uInt16 nPoolId; + OUString sTag; + if( bHeader ) + { + nPoolId = RES_POOLCOLL_TABLE_HDLN; + sTag = OOO_STRING_SVTOOLS_HTML_tableheader; + } + else + { + nPoolId = RES_POOLCOLL_TABLE; + sTag = OOO_STRING_SVTOOLS_HTML_tabledata; + } + + SwTextFormatColl *pColl = nullptr; + + // The following entries will never be used again and may be changed. + SvxCSS1MapEntry *pStyleEntry = GetTag( sTag ); + if( pStyleEntry ) + { + pColl = GetTextFormatColl(nPoolId, OUString()); + SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(), + pStyleEntry->GetPropertyInfo(), this ); + } + + OUString sTmp = sTag + " " OOO_STRING_SVTOOLS_HTML_parabreak; + pStyleEntry = GetTag( sTmp ); + if( pStyleEntry ) + { + if( !pColl ) + pColl = GetTextFormatColl(nPoolId, OUString()); + SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(), + pStyleEntry->GetPropertyInfo(), this ); + } + + if( bHeader ) + m_bTableHeaderTextCollSet = true; + else + m_bTableTextCollSet = true; +} + +void SwCSS1Parser::SetPageDescAttrs( const SvxBrushItem *pBrush, + SfxItemSet *pItemSet2 ) +{ + std::shared_ptr<SvxBrushItem> aBrushItem(std::make_shared<SvxBrushItem>(RES_BACKGROUND)); + std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(RES_BOX)); + std::shared_ptr<SvxFrameDirectionItem> aFrameDirItem(std::make_shared<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)); + bool bSetBrush = pBrush!=nullptr, bSetBox = false, bSetFrameDir = false; + if( pBrush ) + aBrushItem.reset(pBrush->Clone()); + + if( pItemSet2 ) + { + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET == pItemSet2->GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + // set a background + aBrushItem.reset(static_cast<SvxBrushItem*>(pItem->Clone())); + pItemSet2->ClearItem( RES_BACKGROUND ); + bSetBrush = true; + } + + if( SfxItemState::SET == pItemSet2->GetItemState( RES_BOX, false, &pItem ) ) + { + // set a border + aBoxItem.reset(static_cast<SvxBoxItem*>(pItem->Clone())); + pItemSet2->ClearItem( RES_BOX ); + bSetBox = true; + } + + if( SfxItemState::SET == pItemSet2->GetItemState( RES_FRAMEDIR, false, &pItem ) ) + { + // set a frame + aFrameDirItem.reset(static_cast<SvxFrameDirectionItem*>(pItem->Clone())); + pItemSet2->ClearItem( RES_FRAMEDIR ); + bSetFrameDir = true; + } + } + + if( bSetBrush || bSetBox || bSetFrameDir ) + { + static sal_uInt16 aPoolIds[] = { RES_POOLPAGE_HTML, RES_POOLPAGE_FIRST, + RES_POOLPAGE_LEFT, RES_POOLPAGE_RIGHT }; + for(sal_uInt16 i : aPoolIds) + { + const SwPageDesc *pPageDesc = GetPageDesc( i, false ); + if( pPageDesc ) + { + SwPageDesc aNewPageDesc( *pPageDesc ); + SwFrameFormat &rMaster = aNewPageDesc.GetMaster(); + if( bSetBrush ) + rMaster.SetFormatAttr( *aBrushItem ); + if( bSetBox ) + rMaster.SetFormatAttr( *aBoxItem ); + if( bSetFrameDir ) + rMaster.SetFormatAttr( *aFrameDirItem ); + + ChgPageDesc( pPageDesc, aNewPageDesc ); + } + } + } +} + +// Feature: PrintExt +void SwCSS1Parser::SetPageDescAttrs( const SwPageDesc *pPageDesc, + SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rPropInfo ) +{ + if( !pPageDesc ) + return; + + SwPageDesc aNewPageDesc( *pPageDesc ); + SwFrameFormat &rMaster = aNewPageDesc.GetMaster(); + const SfxItemSet& rPageItemSet = rMaster.GetAttrSet(); + const SfxPoolItem *pPageItem, *pItem; + bool bChanged = false; + + // left, right border and first line indentation + if( (rPropInfo.m_bLeftMargin || rPropInfo.m_bRightMargin) && + SfxItemState::SET == rItemSet.GetItemState(RES_LR_SPACE,false,&pItem) ) + { + if( (!rPropInfo.m_bLeftMargin || !rPropInfo.m_bRightMargin) && + SfxItemState::SET == rPageItemSet.GetItemState(RES_LR_SPACE, + true,&pPageItem) ) + { + const SvxLRSpaceItem *pLRItem = static_cast<const SvxLRSpaceItem *>(pItem); + + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem *>(pPageItem) ); + if( rPropInfo.m_bLeftMargin ) + aLRItem.SetLeft( pLRItem->GetLeft() ); + if( rPropInfo.m_bRightMargin ) + aLRItem.SetRight( pLRItem->GetRight() ); + + rMaster.SetFormatAttr( aLRItem ); + } + else + { + rMaster.SetFormatAttr( *pItem ); + } + bChanged = true; + } + + // top and bottom border + if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) && + SfxItemState::SET == rItemSet.GetItemState(RES_UL_SPACE,false,&pItem) ) + { + if( (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) && + SfxItemState::SET == rPageItemSet.GetItemState(RES_UL_SPACE, + true,&pPageItem) ) + { + const SvxULSpaceItem *pULItem = static_cast<const SvxULSpaceItem *>(pItem); + + SvxULSpaceItem aULItem( *static_cast<const SvxULSpaceItem *>(pPageItem) ); + if( rPropInfo.m_bTopMargin ) + aULItem.SetUpper( pULItem->GetUpper() ); + if( rPropInfo.m_bBottomMargin ) + aULItem.SetLower( pULItem->GetLower() ); + + rMaster.SetFormatAttr( aULItem ); + } + else + { + rMaster.SetFormatAttr( *pItem ); + } + bChanged = true; + } + + // the size + if( rPropInfo.m_eSizeType != SVX_CSS1_STYPE_NONE ) + { + if( rPropInfo.m_eSizeType == SVX_CSS1_STYPE_TWIP ) + { + rMaster.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed, rPropInfo.m_nWidth, + rPropInfo.m_nHeight ) ); + bChanged = true; + } + else + { + // With "size: auto|portrait|landscape" the current size + // of the style remains. If "landscape" and "portrait" then + // the landscape flag will be set and maybe the width/height + // are swapped. + SwFormatFrameSize aFrameSz( rMaster.GetFrameSize() ); + bool bLandscape = aNewPageDesc.GetLandscape(); + if( ( bLandscape && + rPropInfo.m_eSizeType == SVX_CSS1_STYPE_PORTRAIT ) || + ( !bLandscape && + rPropInfo.m_eSizeType == SVX_CSS1_STYPE_LANDSCAPE ) ) + { + SwTwips nTmp = aFrameSz.GetHeight(); + aFrameSz.SetHeight( aFrameSz.GetWidth() ); + aFrameSz.SetWidth( nTmp ); + rMaster.SetFormatAttr( aFrameSz ); + aNewPageDesc.SetLandscape( !bLandscape ); + bChanged = true; + } + } + } + + // Is that possible? + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + { + // set a background + rMaster.SetFormatAttr( *pItem ); + rItemSet.ClearItem( RES_BACKGROUND ); + bChanged = true; + } + + if( bChanged ) + ChgPageDesc( pPageDesc, aNewPageDesc ); +} + +std::unique_ptr<SvxBrushItem> SwCSS1Parser::makePageDescBackground() const +{ + return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false ) + ->GetMaster().makeBackgroundBrushItem(); +} + +Css1ScriptFlags SwCSS1Parser::GetScriptFromClass( OUString& rClass, + bool bSubClassOnly ) +{ + Css1ScriptFlags nScriptFlags = Css1ScriptFlags::AllMask; + sal_Int32 nLen = rClass.getLength(); + sal_Int32 nPos = nLen > 4 ? rClass.lastIndexOf( '-' ) : -1; + + if( nPos == -1 ) + { + if( bSubClassOnly ) + return nScriptFlags; + nPos = 0; + } + else + { + nPos++; + nLen = nLen - nPos; + } + + switch( nLen ) + { + case 3: + if( rClass.matchIgnoreAsciiCase( "cjk", nPos ) ) + { + nScriptFlags = Css1ScriptFlags::CJK; + } + else if( rClass.matchIgnoreAsciiCase( "ctl", nPos ) ) + { + nScriptFlags = Css1ScriptFlags::CTL; + } + break; + case 7: + if( rClass.matchIgnoreAsciiCase( "western", nPos ) ) + { + nScriptFlags = Css1ScriptFlags::Western; + } + break; + } + if( Css1ScriptFlags::AllMask != nScriptFlags ) + { + if( nPos ) + { + rClass = rClass.copy( 0, nPos-1 ); + } + else + { + rClass.clear(); + } + } + + return nScriptFlags; +} + +static CSS1SelectorType GetTokenAndClass( const CSS1Selector *pSelector, + OUString& rToken, OUString& rClass, + Css1ScriptFlags& rScriptFlags ) +{ + rToken = pSelector->GetString(); + rClass.clear(); + rScriptFlags = Css1ScriptFlags::AllMask; + + CSS1SelectorType eType = pSelector->GetType(); + if( CSS1_SELTYPE_ELEM_CLASS==eType ) + { + sal_Int32 nPos = rToken.indexOf( '.' ); + OSL_ENSURE( nPos >= 0, "No dot in Class-Selector???" ); + if( nPos >= 0 ) + { + rClass = rToken.copy( nPos+1 ); + rToken = rToken.copy( 0, nPos ); + + rScriptFlags = SwCSS1Parser::GetScriptFromClass( rClass, false ); + if( rClass.isEmpty() ) + eType = CSS1_SELTYPE_ELEMENT; + } + } + + rToken = rToken.toAsciiLowerCase(); + return eType; +} + +static void RemoveScriptItems( SfxItemSet& rItemSet, Css1ScriptFlags nScript, + const SfxItemSet *pParentItemSet = nullptr ) +{ + static const sal_uInt16 aWhichIds[3][5] = + { + { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, RES_CHRATR_LANGUAGE, + RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT }, + { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT }, + { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, RES_CHRATR_CTL_LANGUAGE, + RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT } + }; + + bool aClearItems[3] = { false, false, false }; + switch( nScript ) + { + case Css1ScriptFlags::Western: + aClearItems[1] = aClearItems[2] = true; + break; + case Css1ScriptFlags::CJK: + aClearItems[0] = aClearItems[2] = true; + break; + case Css1ScriptFlags::CTL: + aClearItems[0] = aClearItems[1] = true; + break; + case Css1ScriptFlags::AllMask: + break; + default: + OSL_ENSURE( aClearItems[0], "unknown script type" ); + break; + } + + for( size_t j=0; j < SAL_N_ELEMENTS(aWhichIds); ++j ) + { + for( size_t i=0; i < SAL_N_ELEMENTS(aWhichIds[0]); ++i ) + { + sal_uInt16 nWhich = aWhichIds[j][i]; + const SfxPoolItem *pItem; + if( aClearItems[j] || + (pParentItemSet && + SfxItemState::SET == rItemSet.GetItemState( nWhich, false, &pItem ) && + (0==i ? swhtml_css1atr_equalFontItems( *pItem, pParentItemSet->Get(nWhich ) ) + : *pItem == pParentItemSet->Get(nWhich ) ) ) ) + { + rItemSet.ClearItem( nWhich ); + } + } + } +} + +void SwCSS1Parser::StyleParsed( const CSS1Selector *pSelector, + SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo ) +{ + if( !m_bIsNewDoc ) + return; + + CSS1SelectorType eSelType = pSelector->GetType(); + const CSS1Selector *pNext = pSelector->GetNext(); + + if( CSS1_SELTYPE_ID==eSelType && !pNext ) + { + InsertId( pSelector->GetString(), rItemSet, rPropInfo ); + } + else if( CSS1_SELTYPE_CLASS==eSelType && !pNext ) + { + OUString aClass( pSelector->GetString() ); + Css1ScriptFlags nScript = GetScriptFromClass( aClass ); + if( Css1ScriptFlags::AllMask != nScript ) + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript ); + InsertClass( aClass, aScriptItemSet, rPropInfo ); + } + else + { + InsertClass( aClass, rItemSet, rPropInfo ); + } + } + else if( CSS1_SELTYPE_PAGE==eSelType ) + { + if( !pNext || + (CSS1_SELTYPE_PSEUDO == pNext->GetType() && + (pNext->GetString().equalsIgnoreAsciiCase( "left" ) || + pNext->GetString().equalsIgnoreAsciiCase( "right" ) || + pNext->GetString().equalsIgnoreAsciiCase( "first" ) ) ) ) + { + OUString aName; + if( pNext ) + aName = pNext->GetString(); + InsertPage( aName, + pNext != nullptr, + rItemSet, rPropInfo ); + } + } + + if( CSS1_SELTYPE_ELEMENT != eSelType && + CSS1_SELTYPE_ELEM_CLASS != eSelType) + return; + + // get token and class of selector + OUString aToken2; + OUString aClass; + Css1ScriptFlags nScript; + eSelType = GetTokenAndClass( pSelector, aToken2, aClass, nScript ); + HtmlTokenId nToken2 = GetHTMLToken( aToken2 ); + + // and also some information of the next element + CSS1SelectorType eNextType = pNext ? pNext->GetType() + : CSS1_SELTYPE_ELEMENT; + + // first some special cases + if( CSS1_SELTYPE_ELEMENT==eSelType ) + { + switch( nToken2 ) + { + case HtmlTokenId::ANCHOR_ON: + if( !pNext ) + { + InsertTag( aToken2, rItemSet, rPropInfo ); + return; + } + else if (CSS1_SELTYPE_PSEUDO == eNextType) + { + // maybe A:visited or A:link + + OUString aPseudo( pNext->GetString() ); + aPseudo = aPseudo.toAsciiLowerCase(); + bool bInsert = false; + switch( aPseudo[0] ) + { + case 'l': + if( aPseudo == "link" ) + { + bInsert = true; + } + break; + case 'v': + if( aPseudo == "visited" ) + { + bInsert = true; + } + break; + } + if( bInsert ) + { + OUString sTmp = aToken2 + ":" + aPseudo; + if( Css1ScriptFlags::AllMask != nScript ) + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript ); + InsertTag( sTmp, aScriptItemSet, rPropInfo ); + } + else + { + InsertTag( sTmp, rItemSet, rPropInfo ); + } + return; + } + } + break; + case HtmlTokenId::BODY_ON: + if( !pNext ) + { + // BODY + + // We must test the background before setting, because + // in SetPageDescAttrs it will be deleted. + const SfxPoolItem *pItem; + if( SfxItemState::SET==rItemSet.GetItemState(RES_BACKGROUND,false,&pItem) ) + { + const SvxBrushItem *pBrushItem = + static_cast<const SvxBrushItem *>(pItem); + + /// Body has a background color, if it is not "no fill"/"auto fill" + if( pBrushItem->GetColor() != COL_TRANSPARENT ) + m_bBodyBGColorSet = true; + if( GPOS_NONE != pBrushItem->GetGraphicPos() ) + m_bBodyBackgroundSet = true; + } + + // Border and Padding + rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST ); + + // Some attributes must be set at the style, the ones which + // aren't inherited + SetPageDescAttrs( nullptr, &rItemSet ); + + // all remaining options can be set at the default style, + // then they're the default + if( SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,false) ) + m_bBodyTextSet = true; + SetTextCollAttrs( + GetTextCollFromPool( RES_POOLCOLL_STANDARD ), + rItemSet, rPropInfo, this ); + + return; + } + break; + default: break; + } + } + else if( CSS1_SELTYPE_ELEM_CLASS==eSelType && HtmlTokenId::ANCHOR_ON==nToken2 && + !pNext && aClass.getLength() >= 9 && + ('s' == aClass[0] || 'S' == aClass[0]) ) + { + sal_uInt16 nPoolFormatId = 0; + if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote_sym) ) + nPoolFormatId = RES_POOLCHR_ENDNOTE; + else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote_sym) ) + nPoolFormatId = RES_POOLCHR_FOOTNOTE; + if( nPoolFormatId ) + { + if( Css1ScriptFlags::AllMask == nScript ) + { + SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId), rItemSet ); + } + else + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript ); + SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId), + aScriptItemSet); + } + return; + } + } + + // Now the selectors are processed which belong to a paragraph style + sal_uInt16 nPoolCollId = 0; + switch( nToken2 ) + { + case HtmlTokenId::HEAD1_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE1; + break; + case HtmlTokenId::HEAD2_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE2; + break; + case HtmlTokenId::HEAD3_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE3; + break; + case HtmlTokenId::HEAD4_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE4; + break; + case HtmlTokenId::HEAD5_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE5; + break; + case HtmlTokenId::HEAD6_ON: + nPoolCollId = RES_POOLCOLL_HEADLINE6; + break; + case HtmlTokenId::PARABREAK_ON: + if( aClass.getLength() >= 9 && + ('s' == aClass[0] || 'S' == aClass[0]) ) + { + if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) ) + nPoolCollId = RES_POOLCOLL_ENDNOTE; + else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) ) + nPoolCollId = RES_POOLCOLL_FOOTNOTE; + + if( nPoolCollId ) + aClass.clear(); + else + nPoolCollId = RES_POOLCOLL_TEXT; + } + else + { + nPoolCollId = RES_POOLCOLL_TEXT; + } + break; + case HtmlTokenId::ADDRESS_ON: + nPoolCollId = RES_POOLCOLL_SENDADRESS; + break; + case HtmlTokenId::BLOCKQUOTE_ON: + nPoolCollId = RES_POOLCOLL_HTML_BLOCKQUOTE; + break; + case HtmlTokenId::DT_ON: + nPoolCollId = RES_POOLCOLL_HTML_DT; + break; + case HtmlTokenId::DD_ON: + nPoolCollId = RES_POOLCOLL_HTML_DD; + break; + case HtmlTokenId::PREFORMTXT_ON: + nPoolCollId = RES_POOLCOLL_HTML_PRE; + break; + case HtmlTokenId::TABLEHEADER_ON: + case HtmlTokenId::TABLEDATA_ON: + if( CSS1_SELTYPE_ELEMENT==eSelType && !pNext ) + { + InsertTag( aToken2, rItemSet, rPropInfo ); + return; + } + else if( CSS1_SELTYPE_ELEMENT==eSelType && pNext && + (CSS1_SELTYPE_ELEMENT==eNextType || + CSS1_SELTYPE_ELEM_CLASS==eNextType) ) + { + // not TH and TD, but TH P and TD P + OUString aSubToken, aSubClass; + GetTokenAndClass( pNext, aSubToken, aSubClass, nScript ); + if( HtmlTokenId::PARABREAK_ON == GetHTMLToken( aSubToken ) ) + { + aClass = aSubClass; + pNext = pNext->GetNext(); + eNextType = pNext ? pNext->GetType() : CSS1_SELTYPE_ELEMENT; + + if( !aClass.isEmpty() || pNext ) + { + nPoolCollId = static_cast< sal_uInt16 >( + HtmlTokenId::TABLEHEADER_ON == nToken2 ? RES_POOLCOLL_TABLE_HDLN + : RES_POOLCOLL_TABLE ); + } + else + { + OUString sTmp = aToken2 + " " OOO_STRING_SVTOOLS_HTML_parabreak; + + if( Css1ScriptFlags::AllMask == nScript ) + { + InsertTag( sTmp, rItemSet, rPropInfo ); + } + else + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript ); + InsertTag( sTmp, aScriptItemSet, rPropInfo ); + } + + return; + } + } + } + break; + + default: + ; + } + + if( nPoolCollId ) + { + if( !pNext || + (CSS1_SELTYPE_PSEUDO==eNextType && + pNext->GetString().equalsIgnoreAsciiCase( "first-letter" ) && + SvxAdjust::Left == rPropInfo.m_eFloat) ) + { + // either not a composed selector or a X:first-line { float: left; ... } + + // search resp. create the style + SwTextFormatColl* pColl = GetTextFormatColl(nPoolCollId, OUString()); + SwTextFormatColl* pParentColl = nullptr; + if( !aClass.isEmpty() ) + { + OUString aName( pColl->GetName() ); + AddClassName( aName, aClass ); + + pParentColl = pColl; + pColl = m_pDoc->FindTextFormatCollByName( aName ); + if( !pColl ) + pColl = m_pDoc->MakeTextFormatColl( aName, pParentColl ); + } + if( !pNext ) + { + // set only the attributes at the style + const SfxPoolItem *pItem; + const SvxBoxItem *pBoxItem = nullptr; + if( SfxItemState::SET == + pColl->GetAttrSet().GetItemState(RES_BOX,true,&pItem) ) + pBoxItem = static_cast<const SvxBoxItem *>(pItem); + rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST, pBoxItem ); + if( Css1ScriptFlags::AllMask == nScript && !pParentColl ) + { + SetTextCollAttrs( pColl, rItemSet, rPropInfo, this ); + } + else + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript, + pParentColl ? &pParentColl->GetAttrSet() : nullptr ); + SetTextCollAttrs( pColl, aScriptItemSet, rPropInfo, this ); + } + } + else + { + // create a DropCap attribute + SwFormatDrop aDrop( pColl->GetDrop() ); + aDrop.GetChars() = 1; + + // set the attributes of the DropCap attribute + if( Css1ScriptFlags::AllMask == nScript ) + { + OUString sName(pColl->GetName()); + FillDropCap( aDrop, rItemSet, &sName ); + } + else + { + SfxItemSet aScriptItemSet( rItemSet ); + if( Css1ScriptFlags::Western != nScript ) + { + aScriptItemSet.ClearItem( RES_CHRATR_FONT ); + aScriptItemSet.ClearItem( RES_CHRATR_LANGUAGE ); + aScriptItemSet.ClearItem( RES_CHRATR_POSTURE ); + aScriptItemSet.ClearItem( RES_CHRATR_WEIGHT ); + } + if( Css1ScriptFlags::CJK != nScript ) + { + aScriptItemSet.ClearItem( RES_CHRATR_CJK_FONT ); + aScriptItemSet.ClearItem( RES_CHRATR_CJK_LANGUAGE ); + aScriptItemSet.ClearItem( RES_CHRATR_CJK_POSTURE ); + aScriptItemSet.ClearItem( RES_CHRATR_CJK_WEIGHT ); + } + if( Css1ScriptFlags::CTL != nScript ) + { + aScriptItemSet.ClearItem( RES_CHRATR_CTL_FONT ); + aScriptItemSet.ClearItem( RES_CHRATR_CTL_LANGUAGE ); + aScriptItemSet.ClearItem( RES_CHRATR_CTL_POSTURE ); + aScriptItemSet.ClearItem( RES_CHRATR_CTL_WEIGHT ); + } + OUString sName(pColl->GetName()); + FillDropCap( aDrop, aScriptItemSet, &sName ); + } + + // Only set the attribute if "float: left" is specified and + // the Initial is over several lines. Otherwise the maybe + // created character style will be later searched and set + // via name. + if( aDrop.GetLines() > 1 && + (SvxAdjust::Left == rPropInfo.m_eFloat || + Css1ScriptFlags::AllMask == nScript) ) + { + pColl->SetFormatAttr( aDrop ); + } + } + } + + return; + } + + // Now the selectors are processed which are belonging to the character + // template. There are no composed ones here. + if( pNext ) + return; + + SwCharFormat* pCFormat = GetChrFormat(nToken2, OUString()); + if( pCFormat ) + { + SwCharFormat *pParentCFormat = nullptr; + if( !aClass.isEmpty() ) + { + OUString aName( pCFormat->GetName() ); + AddClassName( aName, aClass ); + pParentCFormat = pCFormat; + + pCFormat = m_pDoc->FindCharFormatByName( aName ); + if( !pCFormat ) + { + pCFormat = m_pDoc->MakeCharFormat( aName, pParentCFormat ); + pCFormat->SetAuto(false); + } + } + + if( Css1ScriptFlags::AllMask == nScript && !pParentCFormat ) + { + SetCharFormatAttrs( pCFormat, rItemSet ); + } + else + { + SfxItemSet aScriptItemSet( rItemSet ); + RemoveScriptItems( aScriptItemSet, nScript, + pParentCFormat ? &pParentCFormat->GetAttrSet() : nullptr ); + SetCharFormatAttrs( pCFormat, aScriptItemSet ); + } + } +} + +sal_uInt32 SwCSS1Parser::GetFontHeight( sal_uInt16 nSize ) const +{ + return m_aFontHeights[ std::min<sal_uInt16>(nSize,6) ]; +} + +const FontList *SwCSS1Parser::GetFontList() const +{ + const FontList *pFList = nullptr; + SwDocShell *pDocSh = m_pDoc->GetDocShell(); + if( pDocSh ) + { + const SvxFontListItem *pFListItem = + static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST)); + if( pFListItem ) + pFList = pFListItem->GetFontList(); + } + + return pFList; +} + +SwCharFormat* SwCSS1Parser::GetChrFormat( HtmlTokenId nToken2, const OUString& rClass ) const +{ + // search the corresponding style + sal_uInt16 nPoolId = 0; + const char* sName = nullptr; + switch( nToken2 ) + { + case HtmlTokenId::EMPHASIS_ON: nPoolId = RES_POOLCHR_HTML_EMPHASIS; break; + case HtmlTokenId::CITIATION_ON: nPoolId = RES_POOLCHR_HTML_CITIATION; break; + case HtmlTokenId::STRONG_ON: nPoolId = RES_POOLCHR_HTML_STRONG; break; + case HtmlTokenId::CODE_ON: nPoolId = RES_POOLCHR_HTML_CODE; break; + case HtmlTokenId::SAMPLE_ON: nPoolId = RES_POOLCHR_HTML_SAMPLE; break; + case HtmlTokenId::KEYBOARD_ON: nPoolId = RES_POOLCHR_HTML_KEYBOARD; break; + case HtmlTokenId::VARIABLE_ON: nPoolId = RES_POOLCHR_HTML_VARIABLE; break; + case HtmlTokenId::DEFINSTANCE_ON: nPoolId = RES_POOLCHR_HTML_DEFINSTANCE; break; + case HtmlTokenId::TELETYPE_ON: nPoolId = RES_POOLCHR_HTML_TELETYPE; break; + + case HtmlTokenId::SHORTQUOTE_ON: sName = OOO_STRING_SVTOOLS_HTML_shortquote; break; + case HtmlTokenId::LANGUAGE_ON: sName = OOO_STRING_SVTOOLS_HTML_language; break; + case HtmlTokenId::AUTHOR_ON: sName = OOO_STRING_SVTOOLS_HTML_author; break; + case HtmlTokenId::PERSON_ON: sName = OOO_STRING_SVTOOLS_HTML_person; break; + case HtmlTokenId::ACRONYM_ON: sName = OOO_STRING_SVTOOLS_HTML_acronym; break; + case HtmlTokenId::ABBREVIATION_ON: sName = OOO_STRING_SVTOOLS_HTML_abbreviation; break; + case HtmlTokenId::INSERTEDTEXT_ON: sName = OOO_STRING_SVTOOLS_HTML_insertedtext; break; + case HtmlTokenId::DELETEDTEXT_ON: sName = OOO_STRING_SVTOOLS_HTML_deletedtext; break; + default: break; + } + + // search or create the style (only possible with name) + if( !nPoolId && !sName ) + return nullptr; + + // search or create style (without class) + SwCharFormat *pCFormat = nullptr; + if( nPoolId ) + { + pCFormat = GetCharFormatFromPool( nPoolId ); + } + else + { + OUString sCName( OUString::createFromAscii(sName) ); + pCFormat = m_pDoc->FindCharFormatByName( sCName ); + if( !pCFormat ) + { + pCFormat = m_pDoc->MakeCharFormat( sCName, m_pDoc->GetDfltCharFormat() ); + pCFormat->SetAuto(false); + } + } + + OSL_ENSURE( pCFormat, "No character style???" ); + + // If a class exists, then search for the class style but don't + // create one. + OUString aClass( rClass ); + GetScriptFromClass( aClass, false ); + if( !aClass.isEmpty() ) + { + OUString aTmp( pCFormat->GetName() ); + AddClassName( aTmp, aClass ); + SwCharFormat *pClassCFormat = m_pDoc->FindCharFormatByName( aTmp ); + if( pClassCFormat ) + { + pCFormat = pClassCFormat; + } + else + { + const SvxCSS1MapEntry *pClass = GetClass( aClass ); + if( pClass ) + { + pCFormat = m_pDoc->MakeCharFormat( aTmp, pCFormat ); + pCFormat->SetAuto(false); + SfxItemSet aItemSet( pClass->GetItemSet() ); + SetCharFormatAttrs( pCFormat, aItemSet ); + } + } + } + + return pCFormat; +} + +SwTextFormatColl *SwCSS1Parser::GetTextCollFromPool( sal_uInt16 nPoolId ) const +{ + const SwTextFormatColls::size_type nOldArrLen = m_pDoc->GetTextFormatColls()->size(); + + SwTextFormatColl *pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId, false ); + + if( m_bIsNewDoc ) + { + const SwTextFormatColls::size_type nArrLen = m_pDoc->GetTextFormatColls()->size(); + for( SwTextFormatColls::size_type i=nOldArrLen; i<nArrLen; ++i ) + lcl_swcss1_setEncoding( *(*m_pDoc->GetTextFormatColls())[i], + GetDfltEncoding() ); + } + + return pColl; +} + +SwCharFormat *SwCSS1Parser::GetCharFormatFromPool( sal_uInt16 nPoolId ) const +{ + const SwCharFormats::size_type nOldArrLen = m_pDoc->GetCharFormats()->size(); + + SwCharFormat *pCharFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nPoolId ); + + if( m_bIsNewDoc ) + { + const SwCharFormats::size_type nArrLen = m_pDoc->GetCharFormats()->size(); + + for( SwCharFormats::size_type i=nOldArrLen; i<nArrLen; i++ ) + lcl_swcss1_setEncoding( *(*m_pDoc->GetCharFormats())[i], + GetDfltEncoding() ); + } + + return pCharFormat; +} + +SwTextFormatColl *SwCSS1Parser::GetTextFormatColl( sal_uInt16 nTextColl, + const OUString& rClass ) +{ + SwTextFormatColl* pColl = nullptr; + + OUString aClass( rClass ); + GetScriptFromClass( aClass, false ); + if( RES_POOLCOLL_TEXT == nTextColl && aClass.getLength() >= 9 && + ('s' == aClass[0] || 'S' == aClass[0] ) ) + { + if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) ) + { + nTextColl = RES_POOLCOLL_ENDNOTE; + aClass.clear(); + } + else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) ) + { + nTextColl = RES_POOLCOLL_FOOTNOTE; + aClass.clear(); + } + } + + if( USER_FMT & nTextColl ) // one created by Reader + { + OSL_ENSURE( false, "Where does the user style comes from?" ); + pColl = GetTextCollFromPool( RES_POOLCOLL_STANDARD ); + } + else + { + pColl = GetTextCollFromPool( nTextColl ); + } + + OSL_ENSURE( pColl, "No paragraph style???" ); + if( !aClass.isEmpty() ) + { + OUString aTmp( pColl->GetName() ); + AddClassName( aTmp, aClass ); + SwTextFormatColl* pClassColl = m_pDoc->FindTextFormatCollByName( aTmp ); + + if( !pClassColl && + (nTextColl==RES_POOLCOLL_TABLE || + nTextColl==RES_POOLCOLL_TABLE_HDLN) ) + { + // In this case there was a <TD><P CLASS=foo>, but no TD.foo + // style was found. The we must use P.foo, if available. + SwTextFormatColl* pCollText = + GetTextCollFromPool( RES_POOLCOLL_TEXT ); + aTmp = pCollText->GetName(); + AddClassName( aTmp, aClass ); + pClassColl = m_pDoc->FindTextFormatCollByName( aTmp ); + } + + if( pClassColl ) + { + pColl = pClassColl; + } + else + { + const SvxCSS1MapEntry *pClass = GetClass( aClass ); + if( pClass ) + { + pColl = m_pDoc->MakeTextFormatColl( aTmp, pColl ); + SfxItemSet aItemSet( pClass->GetItemSet() ); + SvxCSS1PropertyInfo aPropInfo( pClass->GetPropertyInfo() ); + aPropInfo.SetBoxItem( aItemSet, MIN_BORDER_DIST ); + bool bPositioned = MayBePositioned( pClass->GetPropertyInfo() ); + if( bPositioned ) + aItemSet.ClearItem( RES_BACKGROUND ); + SetTextCollAttrs( pColl, aItemSet, aPropInfo, + this ); + } + } + + } + + if( pColl ) + lcl_swcss1_setEncoding( *pColl, GetDfltEncoding() ); + + return pColl; +} + +SwPageDesc *SwCSS1Parser::GetMasterPageDesc() +{ + return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false ); +} + +static SwPageDesc *FindPageDesc(SwDoc *pDoc, sal_uInt16 nPoolId) +{ + size_t nPageDescs = pDoc->GetPageDescCnt(); + size_t nPage; + for (nPage=0; nPage < nPageDescs && + pDoc->GetPageDesc(nPage).GetPoolFormatId() != nPoolId; ++nPage) + ; + + return nPage < nPageDescs ? &pDoc->GetPageDesc(nPage) : nullptr; +} + +const SwPageDesc *SwCSS1Parser::GetPageDesc( sal_uInt16 nPoolId, bool bCreate ) +{ + if( RES_POOLPAGE_HTML == nPoolId ) + return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false ); + + const SwPageDesc *pPageDesc = FindPageDesc(m_pDoc, nPoolId); + if( !pPageDesc && bCreate ) + { + if (m_rHTMLParser.IsReadingHeaderOrFooter()) + { // (there should be only one definition of header/footer in HTML) + SAL_WARN("sw.html", "no creating PageDesc while reading header/footer"); + return nullptr; + } + + // The first page is created from the right page, if there is one. + SwPageDesc *pMasterPageDesc = nullptr; + if( RES_POOLPAGE_FIRST == nPoolId ) + pMasterPageDesc = FindPageDesc(m_pDoc, RES_POOLPAGE_RIGHT); + if( !pMasterPageDesc ) + pMasterPageDesc = m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false ); + + // The new page style is created by copying from master + SwPageDesc *pNewPageDesc = m_pDoc-> + getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false ); + + // therefore we also need the number of the new style + OSL_ENSURE(pNewPageDesc == FindPageDesc(m_pDoc, nPoolId), "page style not found"); + + m_pDoc->CopyPageDesc( *pMasterPageDesc, *pNewPageDesc, false ); + + // Modify the styles for their new purpose. + const SwPageDesc *pFollow = nullptr; + bool bSetFollowFollow = false; + switch( nPoolId ) + { + case RES_POOLPAGE_FIRST: + // If there is already a left page, then is it the follow-up + // style, else it is the HTML style. + pFollow = GetLeftPageDesc(); + if( !pFollow ) + pFollow = pMasterPageDesc; + break; + + case RES_POOLPAGE_RIGHT: + // If the left style is already created, nothing will happen here. + // Otherwise the left style is created and ensures the link with + // the right style. + GetLeftPageDesc( true ); + break; + + case RES_POOLPAGE_LEFT: + // The right style is created if none exists. No links are created. + // If there is already a first page style, then the left style becomes + // follow-up style of the first page. + pFollow = GetRightPageDesc( true ); + bSetFollowFollow = true; + { + const SwPageDesc *pFirstPageDesc = GetFirstPageDesc(); + if( pFirstPageDesc ) + { + SwPageDesc aNewFirstPageDesc( *pFirstPageDesc ); + aNewFirstPageDesc.SetFollow( pNewPageDesc ); + ChgPageDesc( pFirstPageDesc, aNewFirstPageDesc ); + } + } + break; + } + + if( pFollow ) + { + SwPageDesc aNewPageDesc( *pNewPageDesc ); + aNewPageDesc.SetFollow( pFollow ); + ChgPageDesc( pNewPageDesc, aNewPageDesc ); + + if( bSetFollowFollow ) + { + SwPageDesc aNewFollowPageDesc( *pFollow ); + aNewFollowPageDesc.SetFollow( pNewPageDesc ); + ChgPageDesc( pFollow, aNewFollowPageDesc ); + } + } + pPageDesc = pNewPageDesc; + } + + return pPageDesc; +} + +bool SwCSS1Parser::MayBePositioned( const SvxCSS1PropertyInfo& rPropInfo, + bool bAutoWidth ) +{ + if (!rPropInfo.m_bVisible) + { + // Don't create a textframe for this div if it's hidden. + return false; + } + + // abs-pos + // left/top none auto twip perc + + // none Z Z - - + // auto Z Z - - + // twip Z Z S/R - + // perc - - - - + + // - the tag will be positioned absolutely and left/top are both + // present and don't contain a percentage value, or + // - the tag should flow, and + // - a width was specified (needed in both cases) + return ( ( SVX_CSS1_POS_ABSOLUTE == rPropInfo.m_ePosition && + SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eLeftType && + SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eTopType && + (SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType || + SVX_CSS1_LTYPE_TWIP != rPropInfo.m_eTopType) ) || + ( SvxAdjust::End != rPropInfo.m_eFloat ) ) && + ( bAutoWidth || + SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eWidthType || + SVX_CSS1_LTYPE_PERCENTAGE == rPropInfo.m_eWidthType ); +} + +void SwCSS1Parser::AddClassName( OUString& rFormatName, const OUString& rClass ) +{ + OSL_ENSURE( !rClass.isEmpty(), "Style class without length?" ); + + rFormatName += "." + rClass; +} + +void SwCSS1Parser::FillDropCap( SwFormatDrop& rDrop, + SfxItemSet& rItemSet, + const OUString *pName ) +{ + // the number of lines matches somehow a percentage value + // for the height (what happens with absolute heights???) + sal_uInt8 nLines = rDrop.GetLines(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_CHRATR_FONTSIZE, false, &pItem ) ) + { + sal_uInt16 nProp = static_cast<const SvxFontHeightItem *>(pItem)->GetProp(); + nLines = static_cast<sal_uInt8>((nProp + 50) / 100); + if( nLines < 1 ) + nLines = 1; + else if( nLines > MAX_DROPCAP_LINES ) + nLines = MAX_DROPCAP_LINES; + + // Only when nLines>1, then the attribute also is set. Then + // we don't need the font height in the character style. + if( nLines > 1 ) + { + rItemSet.ClearItem( RES_CHRATR_FONTSIZE ); + rItemSet.ClearItem( RES_CHRATR_CJK_FONTSIZE ); + rItemSet.ClearItem( RES_CHRATR_CTL_FONTSIZE ); + } + } + + // In case of hard attribution (pName==0) we can stop, if the Initial is + // only one line. + if( nLines<=1 ) + return; + + rDrop.GetLines() = nLines; + + // a right border becomes the spacing to text! + if( SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, false, &pItem ) ) + { + rDrop.GetDistance() = static_cast< sal_uInt16 >( + static_cast<const SvxLRSpaceItem *>(pItem)->GetRight() ); + rItemSet.ClearItem( RES_LR_SPACE ); + } + + // for every other attribute create a character style + if( rItemSet.Count() ) + { + SwCharFormat *pCFormat = nullptr; + OUString aName; + if( pName ) + { + aName = *pName + ".FL"; // first letter + pCFormat = m_pDoc->FindCharFormatByName( aName ); + } + else + { + do + { + aName = "first-letter " + OUString::number( static_cast<sal_Int32>(++m_nDropCapCnt) ); + } + while( m_pDoc->FindCharFormatByName(aName) ); + } + + if( !pCFormat ) + { + pCFormat = m_pDoc->MakeCharFormat( aName, m_pDoc->GetDfltCharFormat() ); + pCFormat->SetAuto(false); + } + SetCharFormatAttrs( pCFormat, rItemSet ); + + // The character style needs only be set in the attribute, when + // the attribute also is set. + if( nLines > 1 ) + rDrop.SetCharFormat( pCFormat ); + } +} + +// specific CSS1 of SwHTMLParsers + +HTMLAttr **SwHTMLParser::GetAttrTabEntry( sal_uInt16 nWhich ) +{ + // find the table entry of the item ... + HTMLAttr **ppAttr = nullptr; + switch( nWhich ) + { + case RES_CHRATR_BLINK: + ppAttr = &m_xAttrTab->pBlink; + break; + case RES_CHRATR_CASEMAP: + ppAttr = &m_xAttrTab->pCaseMap; + break; + case RES_CHRATR_COLOR: + ppAttr = &m_xAttrTab->pFontColor; + break; + case RES_CHRATR_CROSSEDOUT: + ppAttr = &m_xAttrTab->pStrike; + break; + case RES_CHRATR_ESCAPEMENT: + ppAttr = &m_xAttrTab->pEscapement; + break; + case RES_CHRATR_FONT: + ppAttr = &m_xAttrTab->pFont; + break; + case RES_CHRATR_CJK_FONT: + ppAttr = &m_xAttrTab->pFontCJK; + break; + case RES_CHRATR_CTL_FONT: + ppAttr = &m_xAttrTab->pFontCTL; + break; + case RES_CHRATR_FONTSIZE: + ppAttr = &m_xAttrTab->pFontHeight; + break; + case RES_CHRATR_CJK_FONTSIZE: + ppAttr = &m_xAttrTab->pFontHeightCJK; + break; + case RES_CHRATR_CTL_FONTSIZE: + ppAttr = &m_xAttrTab->pFontHeightCTL; + break; + case RES_CHRATR_KERNING: + ppAttr = &m_xAttrTab->pKerning; + break; + case RES_CHRATR_POSTURE: + ppAttr = &m_xAttrTab->pItalic; + break; + case RES_CHRATR_CJK_POSTURE: + ppAttr = &m_xAttrTab->pItalicCJK; + break; + case RES_CHRATR_CTL_POSTURE: + ppAttr = &m_xAttrTab->pItalicCTL; + break; + case RES_CHRATR_UNDERLINE: + ppAttr = &m_xAttrTab->pUnderline; + break; + case RES_CHRATR_WEIGHT: + ppAttr = &m_xAttrTab->pBold; + break; + case RES_CHRATR_CJK_WEIGHT: + ppAttr = &m_xAttrTab->pBoldCJK; + break; + case RES_CHRATR_CTL_WEIGHT: + ppAttr = &m_xAttrTab->pBoldCTL; + break; + case RES_CHRATR_BACKGROUND: + ppAttr = &m_xAttrTab->pCharBrush; + break; + case RES_CHRATR_BOX: + ppAttr = &m_xAttrTab->pCharBox; + break; + + case RES_PARATR_LINESPACING: + ppAttr = &m_xAttrTab->pLineSpacing; + break; + case RES_PARATR_ADJUST: + ppAttr = &m_xAttrTab->pAdjust; + break; + + case RES_LR_SPACE: + ppAttr = &m_xAttrTab->pLRSpace; + break; + case RES_UL_SPACE: + ppAttr = &m_xAttrTab->pULSpace; + break; + case RES_BOX: + ppAttr = &m_xAttrTab->pBox; + break; + case RES_BACKGROUND: + ppAttr = &m_xAttrTab->pBrush; + break; + case RES_BREAK: + ppAttr = &m_xAttrTab->pBreak; + break; + case RES_PAGEDESC: + ppAttr = &m_xAttrTab->pPageDesc; + break; + case RES_PARATR_SPLIT: + ppAttr = &m_xAttrTab->pSplit; + break; + case RES_PARATR_WIDOWS: + ppAttr = &m_xAttrTab->pWidows; + break; + case RES_PARATR_ORPHANS: + ppAttr = &m_xAttrTab->pOrphans; + break; + case RES_KEEP: + ppAttr = &m_xAttrTab->pKeep; + break; + + case RES_CHRATR_LANGUAGE: + ppAttr = &m_xAttrTab->pLanguage; + break; + case RES_CHRATR_CJK_LANGUAGE: + ppAttr = &m_xAttrTab->pLanguageCJK; + break; + case RES_CHRATR_CTL_LANGUAGE: + ppAttr = &m_xAttrTab->pLanguageCTL; + break; + + case RES_FRAMEDIR: + ppAttr = &m_xAttrTab->pDirection; + break; + } + + return ppAttr; +} + +void SwHTMLParser::NewStyle() +{ + OUString sType; + + const HTMLOptions& rOptions2 = GetOptions(); + for (size_t i = rOptions2.size(); i; ) + { + const HTMLOption& rOption = rOptions2[--i]; + if( HtmlOptionId::TYPE == rOption.GetToken() ) + sType = rOption.GetString(); + } + + m_bIgnoreRawData = sType.getLength() && + !sType.getToken(0,';').equalsAscii(sCSS_mimetype); +} + +void SwHTMLParser::EndStyle() +{ + m_bIgnoreRawData = false; + + if( !m_aStyleSource.isEmpty() ) + { + m_pCSS1Parser->ParseStyleSheet( m_aStyleSource ); + m_aStyleSource.clear(); + } +} + +bool SwHTMLParser::FileDownload( const OUString& rURL, + OUString& rStr ) +{ + // depose view (because of reschedule) + SwViewShell *pOldVSh = CallEndAction(); + + SfxMedium aDLMedium( rURL, StreamMode::READ | StreamMode::SHARE_DENYWRITE ); + + SvStream* pStream = aDLMedium.GetInStream(); + if( pStream ) + { + SvMemoryStream aStream; + aStream.WriteStream( *pStream ); + + rStr = OUString(static_cast<const char *>(aStream.GetData()), aStream.TellEnd(), + GetSrcEncoding()); + } + + // was aborted? + if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + || 1 == m_xDoc->getReferenceCount() ) + { + // was the import aborted from SFX? + eState = SvParserState::Error; + pStream = nullptr; + } + + // recreate View + SwViewShell *const pVSh = CallStartAction( pOldVSh ); + OSL_ENSURE( pOldVSh == pVSh, "FileDownload: SwViewShell changed on us" ); + + return pStream!=nullptr; +} + +void SwHTMLParser::InsertLink() +{ + bool bFinishDownload = false; + if( !m_vPendingStack.empty() ) + { + OSL_ENSURE( ShouldFinishFileDownload(), + "Pending-Stack without File-Download?" ); + + m_vPendingStack.pop_back(); + assert( m_vPendingStack.empty() && "Where does the Pending-Stack come from?" ); + + bFinishDownload = true; + } + else + { + OUString sRel, sHRef, sType; + + const HTMLOptions& rOptions2 = GetOptions(); + for (size_t i = rOptions2.size(); i; ) + { + const HTMLOption& rOption = rOptions2[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::REL: + sRel = rOption.GetString(); + break; + case HtmlOptionId::HREF: + sHRef = URIHelper::SmartRel2Abs( INetURLObject( m_sBaseURL ), rOption.GetString(), Link<OUString *, bool>(), false ); + break; + case HtmlOptionId::TYPE: + sType = rOption.GetString(); + break; + default: break; + } + } + + if( !sHRef.isEmpty() && sRel.equalsIgnoreAsciiCase( "STYLESHEET" ) && + ( sType.isEmpty() || + sType.getToken(0,';').equalsAscii(sCSS_mimetype) ) ) + { + if( GetMedium() ) + { + // start download of style source + StartFileDownload(sHRef); + if( IsParserWorking() ) + { + // The style was loaded synchronously and we can call it directly. + bFinishDownload = true; + } + else + { + // The style was load asynchronously and is only available + // on the next continue call. Therefore we must create a + // Pending stack, so that we will return to here. + m_vPendingStack.emplace_back( HtmlTokenId::LINK ); + } + } + else + { + // load file synchronous + OUString sSource; + if( FileDownload( sHRef, sSource ) ) + m_pCSS1Parser->ParseStyleSheet( sSource ); + } + } + } + + if( bFinishDownload ) + { + OUString sSource; + if( FinishFileDownload( sSource ) && !sSource.isEmpty() ) + m_pCSS1Parser->ParseStyleSheet( sSource ); + } +} + +bool SwCSS1Parser::ParseStyleSheet( const OUString& rIn ) +{ + if( !SvxCSS1Parser::ParseStyleSheet( rIn ) ) + return false; + + SwPageDesc *pMasterPageDesc = + m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false ); + + SvxCSS1MapEntry* pPageEntry = GetPage(OUString(), false); + if( pPageEntry ) + { + // @page (affects all already existing pages) + + SetPageDescAttrs( pMasterPageDesc, pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + + // For all other already existing page styles the attributes + // must also be set + + SetPageDescAttrs( GetFirstPageDesc(), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + SetPageDescAttrs( GetLeftPageDesc(), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + SetPageDescAttrs( GetRightPageDesc(), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + + } + + pPageEntry = GetPage( "first", true ); + if( pPageEntry ) + { + SetPageDescAttrs( GetFirstPageDesc(true), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + m_bSetFirstPageDesc = true; + } + + pPageEntry = GetPage( "right", true ); + if( pPageEntry ) + { + SetPageDescAttrs( GetRightPageDesc(true), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + m_bSetRightPageDesc = true; + } + + pPageEntry = GetPage( "left", true ); + if( pPageEntry ) + SetPageDescAttrs( GetLeftPageDesc(true), pPageEntry->GetItemSet(), + pPageEntry->GetPropertyInfo() ); + + return true; +} + +bool SwHTMLParser::ParseStyleOptions( const OUString &rStyle, + const OUString &rId, + const OUString &rClass, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + const OUString *pLang, + const OUString *pDir ) +{ + bool bRet = false; + + if( !rClass.isEmpty() ) + { + OUString aClass( rClass ); + SwCSS1Parser::GetScriptFromClass( aClass ); + const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass ); + if( pClass ) + { + SvxCSS1Parser::MergeStyles( pClass->GetItemSet(), + pClass->GetPropertyInfo(), + rItemSet, rPropInfo, false ); + bRet = true; + } + } + + if( !rId.isEmpty() ) + { + const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId ); + if( pId ) + SvxCSS1Parser::MergeStyles( pId->GetItemSet(), + pId->GetPropertyInfo(), + rItemSet, rPropInfo, !rClass.isEmpty() ); + rPropInfo.m_aId = rId; + bRet = true; + } + + if( !rStyle.isEmpty() ) + { + m_pCSS1Parser->ParseStyleOption( rStyle, rItemSet, rPropInfo ); + bRet = true; + } + + if( bRet ) + rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST ); + + if( pLang && !pLang->isEmpty() ) + { + LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( *pLang ); + if( LANGUAGE_DONTKNOW != eLang ) + { + SvxLanguageItem aLang( eLang, RES_CHRATR_LANGUAGE ); + rItemSet.Put( aLang ); + aLang.SetWhich( RES_CHRATR_CJK_LANGUAGE ); + rItemSet.Put( aLang ); + aLang.SetWhich( RES_CHRATR_CTL_LANGUAGE ); + rItemSet.Put( aLang ); + + bRet = true; + } + } + if( pDir && !pDir->isEmpty() ) + { + OUString aValue( *pDir ); + SvxFrameDirection eDir = SvxFrameDirection::Environment; + if (aValue.equalsIgnoreAsciiCase("LTR")) + eDir = SvxFrameDirection::Horizontal_LR_TB; + else if (aValue.equalsIgnoreAsciiCase("RTL")) + eDir = SvxFrameDirection::Horizontal_RL_TB; + + if( SvxFrameDirection::Environment != eDir ) + { + SvxFrameDirectionItem aDir( eDir, RES_FRAMEDIR ); + rItemSet.Put( aDir ); + + bRet = true; + } + } + + return bRet; +} + +void SwHTMLParser::SetAnchorAndAdjustment( const SvxCSS1PropertyInfo &rPropInfo, + SfxItemSet &rFrameItemSet ) +{ + SwFormatAnchor aAnchor; + + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + sal_Int16 eVertOri = text::VertOrientation::NONE; + sal_Int16 eHoriRel = text::RelOrientation::FRAME; + sal_Int16 eVertRel = text::RelOrientation::FRAME; + SwTwips nHoriPos = 0, nVertPos = 0; + css::text::WrapTextMode eSurround = css::text::WrapTextMode_THROUGH; + if( SVX_CSS1_POS_ABSOLUTE == rPropInfo.m_ePosition ) + { + if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType && + SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eTopType ) + { + // Absolute positioned objects are page-bound, when they + // aren't in a frame and otherwise frame-bound. + const SwStartNode *pFlySttNd = + m_pPam->GetPoint()->nNode.GetNode().FindFlyStartNode(); + if( pFlySttNd ) + { + aAnchor.SetType( RndStdIds::FLY_AT_FLY ); + SwPosition aPos( *pFlySttNd ); + aAnchor.SetAnchor( &aPos ); + } + else + { + aAnchor.SetType( RndStdIds::FLY_AT_PAGE ); + aAnchor.SetPageNum( 1 ); + } + nHoriPos = rPropInfo.m_nLeft; + nVertPos = rPropInfo.m_nTop; + } + else + { + aAnchor.SetType( RndStdIds::FLY_AT_PARA ); + aAnchor.SetAnchor( m_pPam->GetPoint() ); + eVertOri = text::VertOrientation::TOP; + eVertRel = text::RelOrientation::CHAR; + if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType ) + { + eHoriOri = text::HoriOrientation::NONE; + eHoriRel = text::RelOrientation::PAGE_FRAME; + nHoriPos = rPropInfo.m_nLeft; + } + else + { + eHoriOri = text::HoriOrientation::LEFT; + eHoriRel = text::RelOrientation::FRAME; // to be changed later + } + } + } + else + { + // Flowing object are inserted as paragraph-bound, when the paragraph is + // still empty and otherwise auto-bound. + // Auto-bound frames for the time being inserted at the previous position + // and later moved. + const sal_Int32 nContent = m_pPam->GetPoint()->nContent.GetIndex(); + if( nContent ) + { + aAnchor.SetType( RndStdIds::FLY_AT_CHAR ); + m_pPam->Move( fnMoveBackward ); + eVertOri = text::VertOrientation::CHAR_BOTTOM; + eVertRel = text::RelOrientation::CHAR; + } + else + { + aAnchor.SetType( RndStdIds::FLY_AT_PARA ); + eVertOri = text::VertOrientation::TOP; + eVertRel = text::RelOrientation::PRINT_AREA; + } + + aAnchor.SetAnchor( m_pPam->GetPoint() ); + + if( nContent ) + m_pPam->Move( fnMoveForward ); + + sal_uInt16 nLeftSpace = 0, nRightSpace = 0; + short nIndent = 0; + GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent ); + + if( SvxAdjust::Right==rPropInfo.m_eFloat ) + { + eHoriOri = text::HoriOrientation::RIGHT; + eHoriRel = nRightSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_LEFT; + } + else + { + eHoriOri = text::HoriOrientation::LEFT; + eHoriRel = nLeftSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_RIGHT; + } + } + rFrameItemSet.Put( aAnchor ); + + // positioned absolutely with wrap + rFrameItemSet.Put( SwFormatHoriOrient( nHoriPos, eHoriOri, eHoriRel ) ); + rFrameItemSet.Put( SwFormatVertOrient( nVertPos, eVertOri, eVertRel ) ); + rFrameItemSet.Put( SwFormatSurround( eSurround ) ); +} + +void SwHTMLParser::SetVarSize( SvxCSS1PropertyInfo const &rPropInfo, + SfxItemSet &rFrameItemSet, + SwTwips nDfltWidth, sal_uInt8 nDfltPrcWidth ) +{ + SwTwips nWidth = nDfltWidth, nHeight = MINFLY; + sal_uInt8 nPercentWidth = nDfltPrcWidth, nPercentHeight = 0; + switch( rPropInfo.m_eWidthType ) + { + case SVX_CSS1_LTYPE_PERCENTAGE: + nPercentWidth = rPropInfo.m_nWidth > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nWidth) : 1; + nWidth = MINFLY; + break; + case SVX_CSS1_LTYPE_TWIP: + nWidth = std::max<long>(rPropInfo.m_nWidth, MINFLY); + nPercentWidth = 0; + break; + default: + ; + } + switch( rPropInfo.m_eHeightType ) + { + case SVX_CSS1_LTYPE_PERCENTAGE: + nPercentHeight = rPropInfo.m_nHeight > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nHeight) : 1; + break; + case SVX_CSS1_LTYPE_TWIP: + // Netscape and MS-IE interpreting the height incorrectly as minimum height, + // therefore we are doing the same. + nHeight = std::max<long>(rPropInfo.m_nHeight, MINFLY); + break; + default: + ; + } + + SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, nWidth, nHeight ); + aFrameSize.SetWidthPercent( nPercentWidth ); + aFrameSize.SetHeightPercent( nPercentHeight ); + rFrameItemSet.Put( aFrameSize ); +} + +void SwHTMLParser::SetFrameFormatAttrs( SfxItemSet &rItemSet, + HtmlFrameFormatFlags nFlags, + SfxItemSet &rFrameItemSet ) +{ + const SfxPoolItem *pItem; + if( (nFlags & HtmlFrameFormatFlags::Box) && + SfxItemState::SET==rItemSet.GetItemState( RES_BOX, true, &pItem ) ) + { + if( nFlags & HtmlFrameFormatFlags::Padding ) + { + SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem *>(pItem) ); + // reset all 4 sides to 0 + aBoxItem.SetAllDistances(0); + rFrameItemSet.Put( aBoxItem ); + } + else + { + rFrameItemSet.Put( *pItem ); + } + rItemSet.ClearItem( RES_BOX ); + } + + if( (nFlags & HtmlFrameFormatFlags::Background) && + SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, true, &pItem ) ) + { + rFrameItemSet.Put( *pItem ); + rItemSet.ClearItem( RES_BACKGROUND ); + } + + if( (nFlags & HtmlFrameFormatFlags::Direction) && + SfxItemState::SET==rItemSet.GetItemState( RES_FRAMEDIR, true, &pItem ) ) + { + rFrameItemSet.Put( *pItem ); + rItemSet.ClearItem( RES_FRAMEDIR ); + } +} + +std::unique_ptr<HTMLAttrContext> SwHTMLParser::PopContext( HtmlTokenId nToken ) +{ + std::unique_ptr<HTMLAttrContext> xCntxt; + + HTMLAttrContexts::size_type nPos = m_aContexts.size(); + if( nPos <= m_nContextStMin ) + return nullptr; + + bool bFound = HtmlTokenId::NONE == nToken; + if( nToken != HtmlTokenId::NONE ) + { + // search for stack entry of token ... + while( nPos > m_nContextStMin ) + { + HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken(); + if( nCntxtToken == nToken ) + { + bFound = true; + break; + } + else if( nCntxtToken == HtmlTokenId::NONE ) // zero as token doesn't occur + { + break; + } + } + } + else + { + nPos--; + } + + if( bFound ) + { + xCntxt = std::move(m_aContexts[nPos]); + m_aContexts.erase( m_aContexts.begin() + nPos ); + } + + return xCntxt; +} + +void SwHTMLParser::GetMarginsFromContext( sal_uInt16& nLeft, + sal_uInt16& nRight, + short& nIndent, + bool bIgnoreTopContext ) const +{ + HTMLAttrContexts::size_type nPos = m_aContexts.size(); + if( bIgnoreTopContext ) + { + if( !nPos ) + return; + else + nPos--; + } + + while( nPos > m_nContextStAttrMin ) + { + const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get(); + if( pCntxt->IsLRSpaceChanged() ) + { + pCntxt->GetMargins( nLeft, nRight, nIndent ); + return; + } + } +} + +void SwHTMLParser::GetMarginsFromContextWithNumberBullet( sal_uInt16& nLeft, + sal_uInt16& nRight, + short& nIndent ) const +{ + GetMarginsFromContext( nLeft, nRight, nIndent ); + const SwHTMLNumRuleInfo& rInfo = const_cast<SwHTMLParser*>(this)->GetNumInfo(); + if( rInfo.GetDepth() ) + { + sal_uInt8 nLevel = static_cast<sal_uInt8>( (rInfo.GetDepth() <= MAXLEVEL ? rInfo.GetDepth() + : MAXLEVEL) - 1 ); + const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get(nLevel); + nLeft = nLeft + rNumFormat.GetAbsLSpace(); //TODO: overflow + nIndent = rNumFormat.GetFirstLineOffset(); //TODO: overflow + } +} + +void SwHTMLParser::GetULSpaceFromContext( sal_uInt16& nUpper, + sal_uInt16& nLower ) const +{ + sal_uInt16 nDfltColl = 0; + OUString aDfltClass; + + HTMLAttrContexts::size_type nPos = m_aContexts.size(); + while( nPos > m_nContextStAttrMin ) + { + const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get(); + if( pCntxt->IsULSpaceChanged() ) + { + pCntxt->GetULSpace( nUpper, nLower ); + return; + } + else if( !nDfltColl ) + { + nDfltColl = pCntxt->GetDfltTextFormatColl(); + if( nDfltColl ) + aDfltClass = pCntxt->GetClass(); + } + } + + if( !nDfltColl ) + nDfltColl = RES_POOLCOLL_TEXT; + + const SwTextFormatColl *pColl = + m_pCSS1Parser->GetTextFormatColl( nDfltColl, aDfltClass ); + const SvxULSpaceItem& rULSpace = pColl->GetULSpace(); + nUpper = rULSpace.GetUpper(); + nLower = rULSpace.GetLower(); +} + +void SwHTMLParser::EndContextAttrs( HTMLAttrContext *pContext ) +{ + HTMLAttrs &rAttrs = pContext->GetAttrs(); + for( auto pAttr : rAttrs ) + { + if( RES_PARATR_DROP==pAttr->GetItem().Which() ) + { + // Set the number of characters for DropCaps. If it's zero at the + // end, the attribute is set to invalid and then isn't set from SetAttr. + sal_Int32 nChars = m_pPam->GetPoint()->nContent.GetIndex(); + if( nChars < 1 ) + pAttr->Invalidate(); + else if( nChars > MAX_DROPCAP_CHARS ) + nChars = MAX_DROPCAP_CHARS; + static_cast<SwFormatDrop&>(pAttr->GetItem()).GetChars() = static_cast<sal_uInt8>(nChars); + } + + EndAttr( pAttr ); + } +} + +void SwHTMLParser::InsertParaAttrs( const SfxItemSet& rItemSet ) +{ + SfxItemIter aIter( rItemSet ); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + // search for the table entry of the item... + sal_uInt16 nWhich = pItem->Which(); + HTMLAttr **ppAttr = GetAttrTabEntry( nWhich ); + + if( ppAttr ) + { + NewAttr(m_xAttrTab, ppAttr, *pItem); + if( RES_PARATR_BEGIN > nWhich ) + (*ppAttr)->SetLikePara(); + m_aParaAttrs.push_back( *ppAttr ); + bool bSuccess = EndAttr( *ppAttr, false ); + if (!bSuccess) + m_aParaAttrs.pop_back(); + } + } +} + +static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc ) +{ + if( RTL_TEXTENCODING_DONTKNOW == eEnc ) + return; + + const SfxItemSet& rItemSet = rFormat.GetAttrSet(); + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT }; + const SfxPoolItem *pItem; + for(sal_uInt16 i : aWhichIds) + { + if( SfxItemState::SET == rItemSet.GetItemState( i, false,&pItem ) ) + { + const SvxFontItem& rFont = *static_cast<const SvxFontItem *>(pItem); + if( RTL_TEXTENCODING_SYMBOL != rFont.GetCharSet() ) + { + SvxFontItem aFont( rFont.GetFamily(), rFont.GetFamilyName(), + rFont.GetStyleName(), rFont.GetPitch(), + eEnc, i); + rFormat.SetFormatAttr( aFont ); + } + } + } +} + +void SwCSS1Parser::SetDfltEncoding( rtl_TextEncoding eEnc ) +{ + if( eEnc != GetDfltEncoding() ) + { + if( m_bIsNewDoc ) + { + // Set new encoding as pool default + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT }; + for(sal_uInt16 i : aWhichIds) + { + const SvxFontItem& rDfltFont = + static_cast<const SvxFontItem&>(m_pDoc->GetDefault( i)); + SvxFontItem aFont( rDfltFont.GetFamily(), + rDfltFont.GetFamilyName(), + rDfltFont.GetStyleName(), + rDfltFont.GetPitch(), + eEnc, i ); + m_pDoc->SetDefault( aFont ); + } + + // Change all paragraph styles that do specify a font. + for( auto pTextFormatColl : *m_pDoc->GetTextFormatColls() ) + lcl_swcss1_setEncoding( *pTextFormatColl, eEnc ); + + // Change all character styles that do specify a font. + for( auto pCharFormat : *m_pDoc->GetCharFormats() ) + lcl_swcss1_setEncoding( *pCharFormat, eEnc ); + } + + SvxCSS1Parser::SetDfltEncoding( eEnc ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlctxt.cxx b/sw/source/filter/html/htmlctxt.cxx new file mode 100644 index 000000000..6be3cab72 --- /dev/null +++ b/sw/source/filter/html/htmlctxt.cxx @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> + +#include <hintids.hxx> +#include <svl/itemiter.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <svtools/htmltokn.h> +#include <editeng/boxitem.hxx> +#include <osl/diagnose.h> + +#include <doc.hxx> +#include <pam.hxx> +#include <shellio.hxx> +#include <paratr.hxx> +#include "htmlnum.hxx" +#include "swcss1.hxx" +#include "swhtml.hxx" + +#include <memory> + +using namespace ::com::sun::star; + +class HTMLAttrContext_SaveDoc +{ + SwHTMLNumRuleInfo aNumRuleInfo; // Numbering for this environment + std::unique_ptr<SwPosition> + pPos; // Jump back to here when leaving context + std::shared_ptr<HTMLAttrTable> + xAttrTab; // Valid attributes for the environment, + // if attributes shouldn't be preserved + + size_t nContextStMin; // Stack lower bound for the environment + // if stack needs to be protected + size_t nContextStAttrMin; // Stack lower bound for the environment + // if the attributes shouldn't be preserved + bool bStripTrailingPara : 1; + bool bKeepNumRules : 1; + bool bFixHeaderDist : 1; + bool bFixFooterDist : 1; + +public: + + HTMLAttrContext_SaveDoc() : + nContextStMin( SIZE_MAX ), nContextStAttrMin( SIZE_MAX ), + bStripTrailingPara( false ), bKeepNumRules( false ), + bFixHeaderDist( false ), bFixFooterDist( false ) + {} + + // The position is ours, so we need to create and delete it + void SetPos( const SwPosition& rPos ) { pPos.reset( new SwPosition(rPos) ); } + const SwPosition *GetPos() const { return pPos.get(); } + + // The index isn't ours. So no creation or deletion + void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { aNumRuleInfo.Set(rInf); } + const SwHTMLNumRuleInfo& GetNumInfo() const { return aNumRuleInfo; } + + std::shared_ptr<HTMLAttrTable> const & GetAttrTab(bool bCreate = false); + + void SetContextStMin( size_t nMin ) { nContextStMin = nMin; } + size_t GetContextStMin() const { return nContextStMin; } + + void SetContextStAttrMin( size_t nMin ) { nContextStAttrMin = nMin; } + size_t GetContextStAttrMin() const { return nContextStAttrMin; } + + void SetStripTrailingPara( bool bSet ) { bStripTrailingPara = bSet; } + bool GetStripTrailingPara() const { return bStripTrailingPara; } + + void SetKeepNumRules( bool bSet ) { bKeepNumRules = bSet; } + bool GetKeepNumRules() const { return bKeepNumRules; } + + void SetFixHeaderDist( bool bSet ) { bFixHeaderDist = bSet; } + bool GetFixHeaderDist() const { return bFixHeaderDist; } + + void SetFixFooterDist( bool bSet ) { bFixFooterDist = bSet; } + bool GetFixFooterDist() const { return bFixFooterDist; } +}; + +std::shared_ptr<HTMLAttrTable> const & HTMLAttrContext_SaveDoc::GetAttrTab( bool bCreate ) +{ + if (!xAttrTab && bCreate) + { + xAttrTab = std::make_shared<HTMLAttrTable>(); + memset(xAttrTab.get(), 0, sizeof(HTMLAttrTable)); + } + return xAttrTab; +} + +HTMLAttrContext_SaveDoc *HTMLAttrContext::GetSaveDocContext( bool bCreate ) +{ + if( !m_pSaveDocContext && bCreate ) + m_pSaveDocContext.reset(new HTMLAttrContext_SaveDoc); + + return m_pSaveDocContext.get(); +} + +HTMLAttrContext::HTMLAttrContext( HtmlTokenId nTokn, sal_uInt16 nPoolId, const OUString& rClass, + bool bDfltColl ) : + m_aClass( rClass ), + m_nToken( nTokn ), + m_nTextFormatColl( nPoolId ), + m_nLeftMargin( 0 ), + m_nRightMargin( 0 ), + m_nFirstLineIndent( 0 ), + m_nUpperSpace( 0 ), + m_nLowerSpace( 0 ), + m_eAppend( AM_NONE ), + m_bLRSpaceChanged( false ), + m_bULSpaceChanged( false ), + m_bDefaultTextFormatColl( bDfltColl ), + m_bSpansSection( false ), + m_bPopStack( false ), + m_bFinishPREListingXMP( false ), + m_bRestartPRE( false ), + m_bRestartXMP( false ), + m_bRestartListing( false ), + m_bHeaderOrFooter( false ) +{} + +HTMLAttrContext::HTMLAttrContext( HtmlTokenId nTokn ) : + m_nToken( nTokn ), + m_nTextFormatColl( 0 ), + m_nLeftMargin( 0 ), + m_nRightMargin( 0 ), + m_nFirstLineIndent( 0 ), + m_nUpperSpace( 0 ), + m_nLowerSpace( 0 ), + m_eAppend( AM_NONE ), + m_bLRSpaceChanged( false ), + m_bULSpaceChanged( false ), + m_bDefaultTextFormatColl( false ), + m_bSpansSection( false ), + m_bPopStack( false ), + m_bFinishPREListingXMP( false ), + m_bRestartPRE( false ), + m_bRestartXMP( false ), + m_bRestartListing( false ), + m_bHeaderOrFooter( false ) +{} + +HTMLAttrContext::~HTMLAttrContext() +{ + m_pSaveDocContext.reset(); +} + +void HTMLAttrContext::ClearSaveDocContext() +{ + m_pSaveDocContext.reset(); +} + +void SwHTMLParser::SplitAttrTab( const SwPosition& rNewPos ) +{ + // preliminary paragraph attributes are not allowed here, they could + // be set here and then the pointers become invalid! + OSL_ENSURE(m_aParaAttrs.empty(), + "Danger: there are non-final paragraph attributes"); + m_aParaAttrs.clear(); + + const SwNodeIndex* pOldEndPara = &m_pPam->GetPoint()->nNode; +#ifndef NDEBUG + auto const nOld(pOldEndPara->GetIndex()); +#endif + sal_Int32 nOldEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + + const SwNodeIndex& rNewSttPara = rNewPos.nNode; + sal_Int32 nNewSttCnt = rNewPos.nContent.GetIndex(); + + bool bMoveBack = false; + + // close all open attributes and re-open them after the table + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes) + { + HTMLAttr *pAttr = *pHTMLAttributes; + while( pAttr ) + { + HTMLAttr *pNext = pAttr->GetNext(); + HTMLAttr *pPrev = pAttr->GetPrev(); + + sal_uInt16 nWhich = pAttr->m_pItem->Which(); + if( !nOldEndCnt && RES_PARATR_BEGIN <= nWhich && + pAttr->GetSttParaIdx() < pOldEndPara->GetIndex() ) + { + // The attribute needs to be closed one content position beforehand + if( !bMoveBack ) + { + bMoveBack = m_pPam->Move( fnMoveBackward ); + nOldEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + } + } + else if( bMoveBack ) + { + m_pPam->Move( fnMoveForward ); + nOldEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + bMoveBack = false; + } + + if( (RES_PARATR_BEGIN <= nWhich && bMoveBack) || + pAttr->GetSttParaIdx() < pOldEndPara->GetIndex() || + (pAttr->GetSttPara() == *pOldEndPara && + pAttr->GetSttCnt() != nOldEndCnt) ) + { + // The attribute needs to be set. Because we still need the original, since + // pointers to the attribute still exists in the contexts, we need to clone it. + // The next-list gets lost but the previous-list is preserved + HTMLAttr *pSetAttr = pAttr->Clone( *pOldEndPara, nOldEndCnt ); + + if( pNext ) + pNext->InsertPrev( pSetAttr ); + else + { + if (pSetAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pSetAttr ); + else + m_aSetAttrTab.push_back( pSetAttr ); + } + } + else if( pPrev ) + { + // The previous attributes still need to be set, even if the current attribute + // doesn't need to be set before the table + if( pNext ) + pNext->InsertPrev( pPrev ); + else + { + if (pPrev->m_bInsAtStart) + m_aSetAttrTab.push_front( pPrev ); + else + m_aSetAttrTab.push_back( pPrev ); + } + } + + // Set the start of the attribute + pAttr->m_nStartPara = rNewSttPara; + pAttr->m_nEndPara = rNewSttPara; + pAttr->m_nStartContent = nNewSttCnt; + pAttr->m_nEndContent = nNewSttCnt; + pAttr->m_pPrev = nullptr; + + pAttr = pNext; + } + } + + if( bMoveBack ) + m_pPam->Move( fnMoveForward ); + + assert(m_pPam->GetPoint()->nNode.GetIndex() == nOld); +} + +void SwHTMLParser::SaveDocContext( HTMLAttrContext *pCntxt, + HtmlContextFlags nFlags, + const SwPosition *pNewPos ) +{ + HTMLAttrContext_SaveDoc *pSave = pCntxt->GetSaveDocContext( true ); + pSave->SetStripTrailingPara( bool(HtmlContextFlags::StripPara & nFlags) ); + pSave->SetKeepNumRules( bool(HtmlContextFlags::KeepNumrule & nFlags) ); + pSave->SetFixHeaderDist( bool(HtmlContextFlags::HeaderDist & nFlags) ); + pSave->SetFixFooterDist( bool(HtmlContextFlags::FooterDist & nFlags) ); + + if( pNewPos ) + { + // If the PaM needs to be set to a different position, we need to preserve numbering + if( !pSave->GetKeepNumRules() ) + { + // Numbering shall not be preserved. So we need to preserve the current state + // and turn off numbering afterwards + pSave->SetNumInfo( GetNumInfo() ); + GetNumInfo().Clear(); + } + + if( HtmlContextFlags::KeepAttrs & nFlags ) + { + // Close attribute on current position and start on new one + SplitAttrTab( *pNewPos ); + } + else + { + std::shared_ptr<HTMLAttrTable> xSaveAttrTab = pSave->GetAttrTab(true); + SaveAttrTab(xSaveAttrTab); + } + + pSave->SetPos( *m_pPam->GetPoint() ); + *m_pPam->GetPoint() = *pNewPos; + } + + // Settings nContextStMin automatically means, that no + // currently open lists (DL/OL/UL) can be closed + if( HtmlContextFlags::ProtectStack & nFlags ) + { + pSave->SetContextStMin( m_nContextStMin ); + m_nContextStMin = m_aContexts.size(); + + if( HtmlContextFlags::KeepAttrs & nFlags ) + { + pSave->SetContextStAttrMin( m_nContextStAttrMin ); + m_nContextStAttrMin = m_aContexts.size(); + } + } +} + +void SwHTMLParser::RestoreDocContext( HTMLAttrContext *pCntxt ) +{ + HTMLAttrContext_SaveDoc *pSave = pCntxt->GetSaveDocContext(); + if( !pSave ) + return; + + if( pSave->GetStripTrailingPara() ) + StripTrailingPara(); + + if( pSave->GetPos() ) + { + if( pSave->GetFixHeaderDist() || pSave->GetFixFooterDist() ) + FixHeaderFooterDistance( pSave->GetFixHeaderDist(), + pSave->GetPos() ); + + std::shared_ptr<HTMLAttrTable> xSaveAttrTab = pSave->GetAttrTab(); + if (!xSaveAttrTab) + { + // Close attribute on current position and start on the old one + SplitAttrTab( *pSave->GetPos() ); + } + else + { + RestoreAttrTab(xSaveAttrTab); + } + + *m_pPam->GetPoint() = *pSave->GetPos(); + + // We can already set the attributes so far + SetAttr(); + } + + if( SIZE_MAX != pSave->GetContextStMin() ) + { + m_nContextStMin = pSave->GetContextStMin(); + if( SIZE_MAX != pSave->GetContextStAttrMin() ) + m_nContextStAttrMin = pSave->GetContextStAttrMin(); + } + + if( !pSave->GetKeepNumRules() ) + { + // Set the preserved numbering back + GetNumInfo().Set( pSave->GetNumInfo() ); + } + + pCntxt->ClearSaveDocContext(); +} + +void SwHTMLParser::EndContext( HTMLAttrContext *pContext ) +{ + if( pContext->GetPopStack() ) + { + // Close all still open contexts. Our own context needs to be deleted already! + while( m_aContexts.size() > m_nContextStMin ) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + OSL_ENSURE(xCntxt.get() != pContext, + "Context still on the stack" ); + if (xCntxt.get() == pContext) + break; + + EndContext(xCntxt.get()); + } + } + + // Close all still open attributes + if( pContext->HasAttrs() ) + EndContextAttrs( pContext ); + + // If a section has been opened, end it. Since sections can be part of absolute-positioned + // objects, this needs to be done before restoring document context + if( pContext->GetSpansSection() ) + EndSection(); + + // Leave borders and other special sections + if( pContext->HasSaveDocContext() ) + RestoreDocContext( pContext ); + + // Add a paragraph break if needed + if( AM_NONE != pContext->GetAppendMode() && + m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( pContext->GetAppendMode() ); + + // Restart PRE, LISTING and XMP environments + if( pContext->IsFinishPREListingXMP() ) + FinishPREListingXMP(); + + if( pContext->IsRestartPRE() ) + StartPRE(); + + if( pContext->IsRestartXMP() ) + StartXMP(); + + if( pContext->IsRestartListing() ) + StartListing(); +} + +void SwHTMLParser::ClearContext( HTMLAttrContext *pContext ) +{ + HTMLAttrs &rAttrs = pContext->GetAttrs(); + for( auto pAttr : rAttrs ) + { + // Simple deletion doesn't to the job, since the attribute + // needs to be deregistered with its list. + // In theory, you could delete the list and its attributes separately + // but if you get that wrong, quite a lot is messed up + DeleteAttr( pAttr ); + } + rAttrs.clear(); + + OSL_ENSURE( !pContext->GetSpansSection(), + "Area can no longer be exited" ); + + OSL_ENSURE( !pContext->HasSaveDocContext(), + "Frame can no longer be exited" ); + + // like RestoreDocContext reset enough of this to not catastrophically + // fail if we still have a SaveDocContext here + if (HTMLAttrContext_SaveDoc *pSave = pContext->GetSaveDocContext()) + { + if (SIZE_MAX != pSave->GetContextStMin()) + { + m_nContextStMin = pSave->GetContextStMin(); + if (SIZE_MAX != pSave->GetContextStAttrMin()) + m_nContextStAttrMin = pSave->GetContextStAttrMin(); + } + + pContext->ClearSaveDocContext(); + } + + // Restart PRE/LISTING/XMP environments + if( pContext->IsFinishPREListingXMP() ) + FinishPREListingXMP(); + + if( pContext->IsRestartPRE() ) + StartPRE(); + + if( pContext->IsRestartXMP() ) + StartXMP(); + + if( pContext->IsRestartListing() ) + StartListing(); +} + +bool SwHTMLParser::DoPositioning( SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + HTMLAttrContext *pContext ) +{ + bool bRet = false; + + // A border is opened on the following conditions + // - the tag is absolute-positioned AND left/top are both known AND don't contain a % property + // OR + // - the tag should be floating AND + // - there's a given width + if( SwCSS1Parser::MayBePositioned( rPropInfo ) ) + { + SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs(aFrameItemSet ); + + SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, rPropInfo, + aFrameItemSet ); + + SetVarSize( rPropInfo, aFrameItemSet ); + + SetSpace( Size(0,0), rItemSet, rPropInfo, aFrameItemSet ); + + SetFrameFormatAttrs( rItemSet, + HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Padding|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Direction, + aFrameItemSet ); + + InsertFlyFrame(aFrameItemSet, pContext, rPropInfo.m_aId); + pContext->SetPopStack( true ); + rPropInfo.m_aId.clear(); + bRet = true; + } + + return bRet; +} + +bool SwHTMLParser::CreateContainer( const OUString& rClass, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + HTMLAttrContext *pContext ) +{ + bool bRet = false; + if( rClass.equalsIgnoreAsciiCase( "sd-abs-pos" ) && + SwCSS1Parser::MayBePositioned( rPropInfo ) ) + { + // Container class + SfxItemSet *pFrameItemSet = pContext->GetFrameItemSet( m_xDoc.get() ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( *pFrameItemSet ); + + SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, + rPropInfo, *pFrameItemSet ); + Size aDummy(0,0); + SetFixSize( aDummy, aDummy, false, false, rPropInfo, *pFrameItemSet ); + SetSpace( aDummy, rItemSet, rPropInfo, *pFrameItemSet ); + SetFrameFormatAttrs( rItemSet, HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Direction, + *pFrameItemSet ); + + bRet = true; + } + + return bRet; +} + +void SwHTMLParser::InsertAttrs( SfxItemSet &rItemSet, + SvxCSS1PropertyInfo const &rPropInfo, + HTMLAttrContext *pContext, + bool bCharLvl ) +{ + // Put together a DropCap attribute, if a "float:left" is before the first character + if( bCharLvl && !m_pPam->GetPoint()->nContent.GetIndex() && + SvxAdjust::Left == rPropInfo.m_eFloat ) + { + SwFormatDrop aDrop; + aDrop.GetChars() = 1; + + m_pCSS1Parser->FillDropCap( aDrop, rItemSet ); + + // We only set the DropCap attribute if the initial spans multiple lines + if( aDrop.GetLines() > 1 ) + { + NewAttr(m_xAttrTab, &m_xAttrTab->pDropCap, aDrop); + + HTMLAttrs &rAttrs = pContext->GetAttrs(); + rAttrs.push_back( m_xAttrTab->pDropCap ); + + return; + } + } + + if( !bCharLvl ) + m_pCSS1Parser->SetFormatBreak( rItemSet, rPropInfo ); + + OSL_ENSURE(m_aContexts.size() <= m_nContextStAttrMin || + m_aContexts.back().get() != pContext, + "SwHTMLParser::InsertAttrs: Context already on the Stack"); + + SfxItemIter aIter( rItemSet ); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + HTMLAttr **ppAttr = nullptr; + + switch( pItem->Which() ) + { + case RES_LR_SPACE: + { + // Paragraph indents need to be added and are generated for each paragraphs + // (here for the first paragraph only, all the following in SetTextCollAttrs) + const SvxLRSpaceItem *pLRItem = + static_cast<const SvxLRSpaceItem *>(pItem); + + // Get old paragraph indents without the top context (that's the one we're editing) + sal_uInt16 nOldLeft = 0, nOldRight = 0; + short nOldIndent = 0; + bool bIgnoreTop = m_aContexts.size() > m_nContextStMin && + m_aContexts.back().get() == pContext; + GetMarginsFromContext( nOldLeft, nOldRight, nOldIndent, + bIgnoreTop ); + + // ... and the currently valid ones + sal_uInt16 nLeft = nOldLeft, nRight = nOldRight; + short nIndent = nOldIndent; + pContext->GetMargins( nLeft, nRight, nIndent ); + + // ... and add the new indents to the old ones + // Here, we don't get the ones from the item but the separately remembered ones, + // since they could be negative. Accessing those via the item still works, since + // the item (with value 0) will be added + if( rPropInfo.m_bLeftMargin ) + { + OSL_ENSURE( rPropInfo.m_nLeftMargin < 0 || + rPropInfo.m_nLeftMargin == pLRItem->GetTextLeft(), + "left margin does not match with item" ); + if( rPropInfo.m_nLeftMargin < 0 && + -rPropInfo.m_nLeftMargin > nOldLeft ) + nLeft = 0; + else + nLeft = nOldLeft + static_cast< sal_uInt16 >(rPropInfo.m_nLeftMargin); + } + if( rPropInfo.m_bRightMargin ) + { + OSL_ENSURE( rPropInfo.m_nRightMargin < 0 || + rPropInfo.m_nRightMargin == pLRItem->GetRight(), + "right margin does not match with item" ); + if( rPropInfo.m_nRightMargin < 0 && + -rPropInfo.m_nRightMargin > nOldRight ) + nRight = 0; + else + nRight = nOldRight + static_cast< sal_uInt16 >(rPropInfo.m_nRightMargin); + } + if( rPropInfo.m_bTextIndent ) + nIndent = pLRItem->GetTextFirstLineOffset(); + + // Remember the value for the following paragraphs + pContext->SetMargins( nLeft, nRight, nIndent ); + + // Set the attribute on the current paragraph + SvxLRSpaceItem aLRItem( *pLRItem ); + aLRItem.SetTextFirstLineOffset( nIndent ); + aLRItem.SetTextLeft( nLeft ); + aLRItem.SetRight( nRight ); + NewAttr(m_xAttrTab, &m_xAttrTab->pLRSpace, aLRItem); + EndAttr( m_xAttrTab->pLRSpace, false ); + } + break; + + case RES_UL_SPACE: + if( !rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin ) + { + sal_uInt16 nUpper = 0, nLower = 0; + GetULSpaceFromContext( nUpper, nLower ); + SvxULSpaceItem aULSpace( *static_cast<const SvxULSpaceItem *>(pItem) ); + if( !rPropInfo.m_bTopMargin ) + aULSpace.SetUpper( nUpper ); + if( !rPropInfo.m_bBottomMargin ) + aULSpace.SetLower( nLower ); + + NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, aULSpace); + + // save context information + HTMLAttrs &rAttrs = pContext->GetAttrs(); + rAttrs.push_back( m_xAttrTab->pULSpace ); + + pContext->SetULSpace( aULSpace.GetUpper(), aULSpace.GetLower() ); + } + else + { + ppAttr = &m_xAttrTab->pULSpace; + } + break; + case RES_CHRATR_FONTSIZE: + // don't set attributes with a % property + if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 ) + ppAttr = &m_xAttrTab->pFontHeight; + break; + case RES_CHRATR_CJK_FONTSIZE: + // don't set attributes with a % property + if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 ) + ppAttr = &m_xAttrTab->pFontHeightCJK; + break; + case RES_CHRATR_CTL_FONTSIZE: + // don't set attributes with a % property + if( static_cast<const SvxFontHeightItem *>(pItem)->GetProp() == 100 ) + ppAttr = &m_xAttrTab->pFontHeightCTL; + break; + + case RES_BACKGROUND: + if( bCharLvl ) + { + // Convert the Frame attribute to a Char attribute (if needed) + SvxBrushItem aBrushItem( *static_cast<const SvxBrushItem *>(pItem) ); + aBrushItem.SetWhich( RES_CHRATR_BACKGROUND ); + + // Set the attribute + NewAttr(m_xAttrTab, &m_xAttrTab->pCharBrush, aBrushItem); + + // and save context information + HTMLAttrs &rAttrs = pContext->GetAttrs(); + rAttrs.push_back( m_xAttrTab->pCharBrush ); + } + else if( pContext->GetToken() != HtmlTokenId::TABLEHEADER_ON && + pContext->GetToken() != HtmlTokenId::TABLEDATA_ON ) + { + ppAttr = &m_xAttrTab->pBrush; + } + break; + + case RES_BOX: + if( bCharLvl ) + { + SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem *>(pItem) ); + aBoxItem.SetWhich( RES_CHRATR_BOX ); + + NewAttr(m_xAttrTab, &m_xAttrTab->pCharBox, aBoxItem); + + HTMLAttrs &rAttrs = pContext->GetAttrs(); + rAttrs.push_back( m_xAttrTab->pCharBox ); + } + else + { + ppAttr = &m_xAttrTab->pBox; + } + break; + + default: + ppAttr = GetAttrTabEntry( pItem->Which() ); + break; + } + + if( ppAttr ) + { + // Set the attribute + NewAttr(m_xAttrTab, ppAttr, *pItem); + + // and save context information + HTMLAttrs &rAttrs = pContext->GetAttrs(); + rAttrs.push_back( *ppAttr ); + } + } + + if( !rPropInfo.m_aId.isEmpty() ) + InsertBookmark( rPropInfo.m_aId ); +} + +void SwHTMLParser::InsertAttr( HTMLAttr **ppAttr, const SfxPoolItem & rItem, + HTMLAttrContext *pCntxt ) +{ + if( !ppAttr ) + { + ppAttr = GetAttrTabEntry( rItem.Which() ); + if( !ppAttr ) + return; + } + + // Set the attribute + NewAttr(m_xAttrTab, ppAttr, rItem); + + // save context information + HTMLAttrs &rAttrs = pCntxt->GetAttrs(); + rAttrs.push_back( *ppAttr ); +} + +void SwHTMLParser::SplitPREListingXMP( HTMLAttrContext *pCntxt ) +{ + // PRE/Listing/XMP need to be finished when finishing context + pCntxt->SetFinishPREListingXMP( true ); + + // And set all now valid flags + if( IsReadPRE() ) + pCntxt->SetRestartPRE( true ); + if( IsReadXMP() ) + pCntxt->SetRestartXMP( true ); + if( IsReadListing() ) + pCntxt->SetRestartListing( true ); + + FinishPREListingXMP(); +} + +SfxItemSet *HTMLAttrContext::GetFrameItemSet( SwDoc *pCreateDoc ) +{ + if( !m_pFrameItemSet && pCreateDoc ) + m_pFrameItemSet = std::make_unique<SfxItemSet>( pCreateDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + return m_pFrameItemSet.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmldrawreader.cxx b/sw/source/filter/html/htmldrawreader.cxx new file mode 100644 index 000000000..c1ed88829 --- /dev/null +++ b/sw/source/filter/html/htmldrawreader.cxx @@ -0,0 +1,577 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/svdpage.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdotext.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtacitm.hxx> +#include <svx/sdtayitm.hxx> +#include <svx/sdtaaitm.hxx> +#include <svx/sdtaiitm.hxx> +#include <svx/sdtmfitm.hxx> +#include <editeng/eeitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <svl/itemiter.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> + +#include <charatr.hxx> +#include <drawdoc.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmtsrnd.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <poolfmt.hxx> +#include "swcss1.hxx" +#include "swhtml.hxx" +#include <shellio.hxx> + +using namespace css; + +static HTMLOptionEnum<SdrTextAniKind> const aHTMLMarqBehaviorTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_BEHAV_scroll, SdrTextAniKind::Scroll }, + { OOO_STRING_SVTOOLS_HTML_BEHAV_alternate, SdrTextAniKind::Alternate }, + { OOO_STRING_SVTOOLS_HTML_BEHAV_slide, SdrTextAniKind::Slide }, + { nullptr, SdrTextAniKind(0) } +}; + +static HTMLOptionEnum<SdrTextAniDirection> const aHTMLMarqDirectionTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_AL_left, SdrTextAniDirection::Left }, + { OOO_STRING_SVTOOLS_HTML_AL_right, SdrTextAniDirection::Right }, + { nullptr, SdrTextAniDirection(0) } +}; + +void SwHTMLParser::InsertDrawObject( SdrObject* pNewDrawObj, + const Size& rPixSpace, + sal_Int16 eVertOri, + sal_Int16 eHoriOri, + SfxItemSet& rCSS1ItemSet, + SvxCSS1PropertyInfo& rCSS1PropInfo ) +{ + // always on top of text. + // but in invisible layer. <ConnectToLayout> will move the object + // to the visible layer. + pNewDrawObj->SetLayer( m_xDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId() ); + + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + sal_uInt16 nLeftSpace = 0, nRightSpace = 0, nUpperSpace = 0, nLowerSpace = 0; + if( (rPixSpace.Width() || rPixSpace.Height()) && Application::GetDefaultDevice() ) + { + Size aTwipSpc( rPixSpace.Width(), rPixSpace.Height() ); + aTwipSpc = + Application::GetDefaultDevice()->PixelToLogic( aTwipSpc, + MapMode(MapUnit::MapTwip) ); + nLeftSpace = nRightSpace = static_cast<sal_uInt16>(aTwipSpc.Width()); + nUpperSpace = nLowerSpace = static_cast<sal_uInt16>(aTwipSpc.Height()); + } + + // set left/right border + const SfxPoolItem *pItem; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_LR_SPACE, true, &pItem ) ) + { + // maybe flatten the first line indentation + const SvxLRSpaceItem *pLRItem = static_cast<const SvxLRSpaceItem *>(pItem); + SvxLRSpaceItem aLRItem( *pLRItem ); + aLRItem.SetTextFirstLineOffset( 0 ); + if( rCSS1PropInfo.m_bLeftMargin ) + { + nLeftSpace = static_cast< sal_uInt16 >(aLRItem.GetLeft()); + rCSS1PropInfo.m_bLeftMargin = false; + } + if( rCSS1PropInfo.m_bRightMargin ) + { + nRightSpace = static_cast< sal_uInt16 >(aLRItem.GetRight()); + rCSS1PropInfo.m_bRightMargin = false; + } + rCSS1ItemSet.ClearItem( RES_LR_SPACE ); + } + if( nLeftSpace || nRightSpace ) + { + SvxLRSpaceItem aLRItem( RES_LR_SPACE ); + aLRItem.SetLeft( nLeftSpace ); + aLRItem.SetRight( nRightSpace ); + aFrameSet.Put( aLRItem ); + } + + // set top/bottom border + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_UL_SPACE, true, &pItem ) ) + { + // maybe flatten the first line indentation + const SvxULSpaceItem *pULItem = static_cast<const SvxULSpaceItem *>(pItem); + if( rCSS1PropInfo.m_bTopMargin ) + { + nUpperSpace = pULItem->GetUpper(); + rCSS1PropInfo.m_bTopMargin = false; + } + if( rCSS1PropInfo.m_bBottomMargin ) + { + nLowerSpace = pULItem->GetLower(); + rCSS1PropInfo.m_bBottomMargin = false; + } + + rCSS1ItemSet.ClearItem( RES_UL_SPACE ); + } + if( nUpperSpace || nLowerSpace ) + { + SvxULSpaceItem aULItem( RES_UL_SPACE ); + aULItem.SetUpper( nUpperSpace ); + aULItem.SetLower( nLowerSpace ); + aFrameSet.Put( aULItem ); + } + + SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR ); + if( SVX_CSS1_POS_ABSOLUTE == rCSS1PropInfo.m_ePosition && + SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eLeftType && + SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eTopType ) + { + const SwStartNode *pFlySttNd = + m_pPam->GetPoint()->nNode.GetNode().FindFlyStartNode(); + + if( pFlySttNd ) + { + aAnchor.SetType( RndStdIds::FLY_AT_FLY ); + SwPosition aPos( *pFlySttNd ); + aAnchor.SetAnchor( &aPos ); + } + else + { + aAnchor.SetType( RndStdIds::FLY_AT_PAGE ); + } + // #i26791# - direct positioning for <SwDoc::Insert(..)> + pNewDrawObj->SetRelativePos( Point(rCSS1PropInfo.m_nLeft + nLeftSpace, + rCSS1PropInfo.m_nTop + nUpperSpace) ); + aFrameSet.Put( SwFormatSurround(css::text::WrapTextMode_THROUGH) ); + } + else if( SvxAdjust::Left == rCSS1PropInfo.m_eFloat || + text::HoriOrientation::LEFT == eHoriOri ) + { + aAnchor.SetType( RndStdIds::FLY_AT_PARA ); + aFrameSet.Put( SwFormatSurround(css::text::WrapTextMode_RIGHT) ); + // #i26791# - direct positioning for <SwDoc::Insert(..)> + pNewDrawObj->SetRelativePos( Point(nLeftSpace, nUpperSpace) ); + } + else if( text::VertOrientation::NONE != eVertOri ) + { + aFrameSet.Put( SwFormatVertOrient( 0, eVertOri ) ); + } + + if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) + { + aAnchor.SetPageNum( 1 ); + } + else if( RndStdIds::FLY_AT_FLY != aAnchor.GetAnchorId() ) + { + aAnchor.SetAnchor( m_pPam->GetPoint() ); + } + aFrameSet.Put( aAnchor ); + + m_xDoc->getIDocumentContentOperations().InsertDrawObj( *m_pPam, *pNewDrawObj, aFrameSet ); +} + +static void PutEEPoolItem( SfxItemSet &rEEItemSet, + const SfxPoolItem& rSwItem ) +{ + + sal_uInt16 nEEWhich = 0; + + switch( rSwItem.Which() ) + { + case RES_CHRATR_COLOR: nEEWhich = EE_CHAR_COLOR; break; + case RES_CHRATR_CROSSEDOUT: nEEWhich = EE_CHAR_STRIKEOUT; break; + case RES_CHRATR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT; break; + case RES_CHRATR_FONT: nEEWhich = EE_CHAR_FONTINFO; break; + case RES_CHRATR_CJK_FONT: nEEWhich = EE_CHAR_FONTINFO_CJK; break; + case RES_CHRATR_CTL_FONT: nEEWhich = EE_CHAR_FONTINFO_CTL; break; + case RES_CHRATR_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT; break; + case RES_CHRATR_CJK_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT_CJK; break; + case RES_CHRATR_CTL_FONTSIZE: nEEWhich = EE_CHAR_FONTHEIGHT_CTL; break; + case RES_CHRATR_KERNING: nEEWhich = EE_CHAR_KERNING; break; + case RES_CHRATR_POSTURE: nEEWhich = EE_CHAR_ITALIC; break; + case RES_CHRATR_CJK_POSTURE: nEEWhich = EE_CHAR_ITALIC_CJK; break; + case RES_CHRATR_CTL_POSTURE: nEEWhich = EE_CHAR_ITALIC_CTL; break; + case RES_CHRATR_UNDERLINE: nEEWhich = EE_CHAR_UNDERLINE; break; + case RES_CHRATR_WEIGHT: nEEWhich = EE_CHAR_WEIGHT; break; + case RES_CHRATR_CJK_WEIGHT: nEEWhich = EE_CHAR_WEIGHT_CJK; break; + case RES_CHRATR_CTL_WEIGHT: nEEWhich = EE_CHAR_WEIGHT_CTL; break; + case RES_BACKGROUND: + case RES_CHRATR_BACKGROUND: + { + const SvxBrushItem& rBrushItem = static_cast<const SvxBrushItem&>(rSwItem); + rEEItemSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) ); + rEEItemSet.Put(XFillColorItem(OUString(), + rBrushItem.GetColor()) ); + } + break; + } + + if( nEEWhich ) + rEEItemSet.Put( rSwItem.CloneSetWhich(nEEWhich) ); +} + +void SwHTMLParser::NewMarquee( HTMLTable *pCurTable ) +{ + + OSL_ENSURE( !m_pMarquee, "Marquee in Marquee???" ); + m_aContents.clear(); + + OUString aId, aStyle, aClass; + + long nWidth=0, nHeight=0; + bool bPercentWidth = false, bDirection = false, bBGColor = false; + Size aSpace( 0, 0 ); + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + SdrTextAniKind eAniKind = SdrTextAniKind::Scroll; + SdrTextAniDirection eAniDir = SdrTextAniDirection::Left; + sal_uInt16 nCount = 0, nDelay = 60; + sal_Int16 nAmount = -6; + Color aBGColor; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (const auto & rOption : rHTMLOptions) + { + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + + case HtmlOptionId::BEHAVIOR: + eAniKind = rOption.GetEnum( aHTMLMarqBehaviorTable, eAniKind ); + break; + + case HtmlOptionId::BGCOLOR: + rOption.GetColor( aBGColor ); + bBGColor = true; + break; + + case HtmlOptionId::DIRECTION: + eAniDir = rOption.GetEnum( aHTMLMarqDirectionTable, eAniDir ); + bDirection = true; + break; + + case HtmlOptionId::LOOP: + if (rOption.GetString(). + equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_LOOP_infinite)) + { + nCount = 0; + } + else + { + const sal_Int32 nLoop = rOption.GetSNumber(); + nCount = std::max<sal_Int32>(nLoop, 0); + } + break; + + case HtmlOptionId::SCROLLAMOUNT: + nAmount = - static_cast<sal_Int16>(rOption.GetNumber()); + break; + + case HtmlOptionId::SCROLLDELAY: + nDelay = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + + case HtmlOptionId::WIDTH: + // first only save as pixel value! + nWidth = rOption.GetNumber(); + bPercentWidth = rOption.GetString().indexOf('%') != -1; + if( bPercentWidth && nWidth>100 ) + nWidth = 100; + break; + + case HtmlOptionId::HEIGHT: + // first only save as pixel value! + nHeight = rOption.GetNumber(); + if( rOption.GetString().indexOf('%') != -1 ) + nHeight = 0; + break; + + case HtmlOptionId::HSPACE: + // first only save as pixel value! + aSpace.setHeight( rOption.GetNumber() ); + break; + + case HtmlOptionId::VSPACE: + // first only save as pixel value! + aSpace.setWidth( rOption.GetNumber() ); + break; + + case HtmlOptionId::ALIGN: + eVertOri = + rOption.GetEnum( aHTMLImgVAlignTable, + text::VertOrientation::TOP ); + eHoriOri = + rOption.GetEnum( aHTMLImgHAlignTable ); + break; + default: break; + } + } + + // create a DrawTextobj + // #i52858# - method name changed + SwDrawModel* pModel = m_xDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + SdrPage* pPg = pModel->GetPage( 0 ); + m_pMarquee = SdrObjFactory::MakeNewObject( + *pModel, + SdrInventor::Default, + OBJ_TEXT); + + if( !m_pMarquee ) + return; + + pPg->InsertObject( m_pMarquee ); + + if( !aId.isEmpty() ) + InsertBookmark( aId ); + + // (only) Alternate runs from left to right as default + if( SdrTextAniKind::Alternate==eAniKind && !bDirection ) + eAniDir = SdrTextAniDirection::Right; + + // re set the attributes needed for scrolling + sal_uInt16 const aWhichMap[] { XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + EE_CHAR_START, EE_CHAR_END, + 0 }; + SfxItemSet aItemSet( pModel->GetItemPool(), aWhichMap ); + aItemSet.Put( makeSdrTextAutoGrowWidthItem( false ) ); + aItemSet.Put( makeSdrTextAutoGrowHeightItem( true ) ); + aItemSet.Put( SdrTextAniKindItem( eAniKind ) ); + aItemSet.Put( SdrTextAniDirectionItem( eAniDir ) ); + aItemSet.Put( SdrTextAniCountItem( nCount ) ); + aItemSet.Put( SdrTextAniDelayItem( nDelay ) ); + aItemSet.Put( SdrTextAniAmountItem( nAmount ) ); + if( SdrTextAniKind::Alternate==eAniKind ) + { + // (only) Alternate starts and ends Inside as default + aItemSet.Put( SdrTextAniStartInsideItem(true) ); + aItemSet.Put( SdrTextAniStopInsideItem(true) ); + if( SdrTextAniDirection::Left==eAniDir ) + aItemSet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT) ); + } + + // set the default colour (from the default template), so that a meaningful + // colour is set at all + const Color& rDfltColor = + m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD ) + ->GetColor().GetValue(); + aItemSet.Put( SvxColorItem( rDfltColor, EE_CHAR_COLOR ) ); + + // set the attributes of the current paragraph style + sal_uInt16 nWhichIds[] = + { + RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT, RES_CHRATR_ESCAPEMENT, + RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, RES_CHRATR_KERNING, + RES_CHRATR_POSTURE, RES_CHRATR_UNDERLINE, RES_CHRATR_WEIGHT, + RES_CHRATR_BACKGROUND, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, + RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT, + 0 + }; + SwTextNode const*const pTextNd = + m_pPam->GetPoint()->nNode.GetNode().GetTextNode(); + if( pTextNd ) + { + const SfxItemSet& rItemSet = pTextNd->GetAnyFormatColl().GetAttrSet(); + const SfxPoolItem *pItem; + for( int i=0; nWhichIds[i]; ++i ) + { + if( SfxItemState::SET == rItemSet.GetItemState( nWhichIds[i], true, &pItem ) ) + PutEEPoolItem( aItemSet, *pItem ); + } + } + + // set attribute of environment at the Draw object + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes) + { + HTMLAttr *pAttr = *pHTMLAttributes; + if( pAttr ) + PutEEPoolItem( aItemSet, pAttr->GetItem() ); + } + + if( bBGColor ) + { + aItemSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) ); + aItemSet.Put(XFillColorItem(OUString(), aBGColor)); + } + + // parse styles (is here only possible for attributes, which also + // can be set at character object) + SfxItemSet aStyleItemSet( m_xDoc->GetAttrPool(), + m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) && + ParseStyleOptions( aStyle, aId, aClass, aStyleItemSet, aPropInfo ) ) + { + SfxItemIter aIter( aStyleItemSet ); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + PutEEPoolItem( aItemSet, *pItem ); + } + } + + // now set the size + Size aTwipSz( bPercentWidth ? 0 : nWidth, nHeight ); + if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() ) + { + aTwipSz = Application::GetDefaultDevice() + ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) ); + } + + if( SVX_CSS1_LTYPE_TWIP== aPropInfo.m_eWidthType ) + { + aTwipSz.setWidth( aPropInfo.m_nWidth ); + nWidth = 1; // != 0; + bPercentWidth = false; + } + if( SVX_CSS1_LTYPE_TWIP== aPropInfo.m_eHeightType ) + aTwipSz.setHeight( aPropInfo.m_nHeight ); + + m_bFixMarqueeWidth = false; + if( !nWidth || bPercentWidth ) + { + if( m_xTable ) + { + if( !pCurTable ) + { + // The marquee is in a table, but not in a cell. Since now no + // reasonable mapping to a cell is possible, we adjust here the + // width to the content of the marquee. + m_bFixMarqueeWidth = true; + } + else if( !nWidth ) + { + // Because we know in which cell the marquee is, we also can + // adjust the width. No width specification is treated as + // 100 percent. + nWidth = 100; + bPercentWidth = true; + } + aTwipSz.setWidth( MINLAY ); + } + else + { + long nBrowseWidth = GetCurrentBrowseWidth(); + aTwipSz.setWidth( !nWidth ? nBrowseWidth + : (nWidth*nBrowseWidth) / 100 ); + } + } + + // The height is only minimum height + if( aTwipSz.Height() < MINFLY ) + aTwipSz.setHeight( MINFLY ); + aItemSet.Put( makeSdrTextMinFrameHeightItem( aTwipSz.Height() ) ); + + m_pMarquee->SetMergedItemSetAndBroadcast(aItemSet); + + if( aTwipSz.Width() < MINFLY ) + aTwipSz.setWidth( MINFLY ); + m_pMarquee->SetLogicRect( tools::Rectangle( 0, 0, aTwipSz.Width(), aTwipSz.Height() ) ); + + // and insert the object into the document + InsertDrawObject( m_pMarquee, aSpace, eVertOri, eHoriOri, aStyleItemSet, + aPropInfo ); + + // Register the drawing object at the table. Is a little bit complicated, + // because it is done via the parser, although the table is known, but + // otherwise the table would have to be public and that also isn't pretty. + // The global pTable also can't be used, because the marquee can also be + // in a sub-table. + if( pCurTable && bPercentWidth) + RegisterDrawObjectToTable( pCurTable, m_pMarquee, static_cast<sal_uInt8>(nWidth) ); +} + +void SwHTMLParser::EndMarquee() +{ + OSL_ENSURE( m_pMarquee && OBJ_TEXT==m_pMarquee->GetObjIdentifier(), + "no marquee or wrong type" ); + + if( m_bFixMarqueeWidth ) + { + // Because there is no fixed height make the text object wider then + // the text, so that there is no line break. + const tools::Rectangle& rOldRect = m_pMarquee->GetLogicRect(); + m_pMarquee->SetLogicRect( tools::Rectangle( rOldRect.TopLeft(), + Size( USHRT_MAX, 240 ) ) ); + } + + // insert the collected text + static_cast<SdrTextObj*>(m_pMarquee)->SetText( m_aContents ); + m_pMarquee->SetMergedItemSetAndBroadcast( m_pMarquee->GetMergedItemSet() ); + + if( m_bFixMarqueeWidth ) + { + // adjust the size to the text + static_cast<SdrTextObj*>(m_pMarquee)->FitFrameToTextSize(); + } + + m_aContents.clear(); + m_pMarquee = nullptr; +} + +void SwHTMLParser::InsertMarqueeText() +{ + OSL_ENSURE( m_pMarquee && OBJ_TEXT==m_pMarquee->GetObjIdentifier(), + "no marquee or wrong type" ); + + // append the current text part to the text + m_aContents += aToken; +} + +void SwHTMLParser::ResizeDrawObject( SdrObject* pObj, SwTwips nWidth ) +{ + OSL_ENSURE( OBJ_TEXT==pObj->GetObjIdentifier(), + "no marquee or wrong type" ); + + if( OBJ_TEXT!=pObj->GetObjIdentifier() ) + return; + + // the old size + const tools::Rectangle& rOldRect = pObj->GetLogicRect(); + Size aNewSz( nWidth, rOldRect.GetSize().Height() ); + pObj->SetLogicRect( tools::Rectangle( rOldRect.TopLeft(), aNewSz ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmldrawwriter.cxx b/sw/source/filter/html/htmldrawwriter.cxx new file mode 100644 index 000000000..a1b6f6aa0 --- /dev/null +++ b/sw/source/filter/html/htmldrawwriter.cxx @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/svdobj.hxx> +#include <svx/svdotext.hxx> +#include <svx/sdtacitm.hxx> +#include <svx/sdtayitm.hxx> +#include <svx/sdtaaitm.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outliner.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <editeng/colritem.hxx> +#include <svl/whiter.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> + +#include <rtl/strbuf.hxx> + +#include <IDocumentDrawModelAccess.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <dcontact.hxx> + +#include "wrthtml.hxx" + + +using namespace css; + +const HtmlFrmOpts HTML_FRMOPTS_MARQUEE = + HtmlFrmOpts::Align | + HtmlFrmOpts::Space; + +const HtmlFrmOpts HTML_FRMOPTS_MARQUEE_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSpace; + +const SdrObject *SwHTMLWriter::GetMarqueeTextObj( const SwDrawFrameFormat& rFormat ) +{ + const SdrObject* pObj = rFormat.FindSdrObject(); + return (pObj && ::IsMarqueeTextObj( *pObj )) ? pObj : nullptr; +} + +void SwHTMLWriter::GetEEAttrsFromDrwObj( SfxItemSet& rItemSet, + const SdrObject *pObj ) +{ + // get the edit script::Engine attributes from object + const SfxItemSet& rObjItemSet = pObj->GetMergedItemSet(); + + // iterate over Edit script::Engine attributes and convert them + // into SW-Attrs resp. set default + SfxWhichIter aIter( rObjItemSet ); + sal_uInt16 nEEWhich = aIter.FirstWhich(); + while( nEEWhich ) + { + const SfxPoolItem *pEEItem; + bool bSet = SfxItemState::SET == rObjItemSet.GetItemState( nEEWhich, false, + &pEEItem ); + + sal_uInt16 nSwWhich = 0; + switch( nEEWhich ) + { + case EE_CHAR_COLOR: nSwWhich = RES_CHRATR_COLOR; break; + case EE_CHAR_STRIKEOUT: nSwWhich = RES_CHRATR_CROSSEDOUT; break; + case EE_CHAR_ESCAPEMENT: nSwWhich = RES_CHRATR_ESCAPEMENT; break; + case EE_CHAR_FONTINFO: nSwWhich = RES_CHRATR_FONT; break; + case EE_CHAR_FONTINFO_CJK: nSwWhich = RES_CHRATR_CJK_FONT; break; + case EE_CHAR_FONTINFO_CTL: nSwWhich = RES_CHRATR_CTL_FONT; break; + case EE_CHAR_FONTHEIGHT: nSwWhich = RES_CHRATR_FONTSIZE; break; + case EE_CHAR_FONTHEIGHT_CJK:nSwWhich = RES_CHRATR_CJK_FONTSIZE; break; + case EE_CHAR_FONTHEIGHT_CTL:nSwWhich = RES_CHRATR_CTL_FONTSIZE; break; + case EE_CHAR_KERNING: nSwWhich = RES_CHRATR_KERNING; break; + case EE_CHAR_ITALIC: nSwWhich = RES_CHRATR_POSTURE; break; + case EE_CHAR_ITALIC_CJK: nSwWhich = RES_CHRATR_CJK_POSTURE; break; + case EE_CHAR_ITALIC_CTL: nSwWhich = RES_CHRATR_CTL_POSTURE; break; + case EE_CHAR_UNDERLINE: nSwWhich = RES_CHRATR_UNDERLINE; break; + case EE_CHAR_WEIGHT: nSwWhich = RES_CHRATR_WEIGHT; break; + case EE_CHAR_WEIGHT_CJK: nSwWhich = RES_CHRATR_CJK_WEIGHT; break; + case EE_CHAR_WEIGHT_CTL: nSwWhich = RES_CHRATR_CTL_WEIGHT; break; + } + + if( nSwWhich ) + { + // if the item isn't set we maybe take the default item + if( !bSet ) + pEEItem = &rObjItemSet.GetPool()->GetDefaultItem(nEEWhich); + + // now we clone the item with the which id of the writer + rItemSet.Put( pEEItem->CloneSetWhich(nSwWhich) ); + } + + nEEWhich = aIter.NextWhich(); + } +} + +Writer& OutHTML_DrawFrameFormatAsMarquee( Writer& rWrt, + const SwDrawFrameFormat& rFormat, + const SdrObject& rSdrObject ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OSL_ENSURE( rWrt.m_pDoc->getIDocumentDrawModelAccess().GetDrawModel(), + "There is a Draw-Obj with no Draw-Model?" ); + const SdrTextObj *pTextObj = static_cast<const SdrTextObj *>(&rSdrObject); + + // Is there text to output + const OutlinerParaObject *pOutlinerParaObj = + pTextObj->GetOutlinerParaObject(); + if( !pOutlinerParaObj ) + return rWrt; + + OStringBuffer sOut; + sOut.append('<').append(OOO_STRING_SVTOOLS_HTML_marquee); + + // get attributes of the object + const SfxItemSet& rItemSet = pTextObj->GetMergedItemSet(); + + // BEHAVIOUR + SdrTextAniKind eAniKind = pTextObj->GetTextAniKind(); + OSL_ENSURE( SdrTextAniKind::Scroll==eAniKind || + SdrTextAniKind::Alternate==eAniKind || + SdrTextAniKind::Slide==eAniKind, + "Text-Draw-Object not suitable for marquee" ); + + const char *pStr = nullptr; + switch( eAniKind ) + { + case SdrTextAniKind::Scroll: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_scroll; break; + case SdrTextAniKind::Slide: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_slide; break; + case SdrTextAniKind::Alternate: pStr = OOO_STRING_SVTOOLS_HTML_BEHAV_alternate; break; + default: + ; + } + + if( pStr ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_behavior). + append("=\"").append(pStr).append("\""); + } + + // DIRECTION + pStr = nullptr; + SdrTextAniDirection eAniDir = pTextObj->GetTextAniDirection(); + switch( eAniDir ) + { + case SdrTextAniDirection::Left: pStr = OOO_STRING_SVTOOLS_HTML_AL_left; break; + case SdrTextAniDirection::Right: pStr = OOO_STRING_SVTOOLS_HTML_AL_right; break; + default: + ; + } + + if( pStr ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_direction). + append("=\"").append(pStr).append("\""); + } + + // LOOP + sal_Int32 nCount = rItemSet.Get( SDRATTR_TEXT_ANICOUNT ).GetValue(); + if( 0==nCount ) + nCount = SdrTextAniKind::Slide==eAniKind ? 1 : -1; + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_loop).append("=\""). + append(nCount).append("\""); + + // SCROLLDELAY + sal_uInt16 nDelay = rItemSet.Get( SDRATTR_TEXT_ANIDELAY ).GetValue(); + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_scrolldelay). + append("=\"").append(static_cast<sal_Int32>(nDelay)).append("\""); + + // SCROLLAMOUNT + sal_Int16 nAmount = rItemSet.Get( SDRATTR_TEXT_ANIAMOUNT ).GetValue(); + if( nAmount < 0 ) + { + nAmount = -nAmount; + } + else if( nAmount && Application::GetDefaultDevice() ) + { + nAmount = Application::GetDefaultDevice() + ->LogicToPixel( Size(nAmount,0), + MapMode(MapUnit::MapTwip) ).Width(); + } + if( nAmount ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_scrollamount). + append("=\"").append(static_cast<sal_Int32>(nAmount)).append("\""); + } + + Size aTwipSz( pTextObj->GetLogicRect().GetSize() ); + if( pTextObj->IsAutoGrowWidth() ) + aTwipSz.setWidth( 0 ); + // The height is at MS a minimum height, therefore we output the minimum + // height, if they exist. Because a minimum height MINFLY is coming with + // high probability from import, we aren't outputting it. You can't + // do anything wrong, because every font is higher. + if( pTextObj->IsAutoGrowHeight() ) + { + aTwipSz.setHeight( pTextObj->GetMinTextFrameHeight() ); + if( MINFLY==aTwipSz.Height() ) + aTwipSz.setHeight( 0 ); + } + + if( (aTwipSz.Width() || aTwipSz.Height()) && + Application::GetDefaultDevice() ) + { + Size aPixelSz = + Application::GetDefaultDevice()->LogicToPixel( aTwipSz, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSz.Width() && aTwipSz.Width() ) + aPixelSz.setWidth( 1 ); + if( !aPixelSz.Height() && aTwipSz.Height() ) + aPixelSz.setHeight( 1 ); + + if( aPixelSz.Width() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width). + append("=\"").append(static_cast<sal_Int32>(aPixelSz.Width())).append("\""); + } + + if( aPixelSz.Height() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_height). + append("=\"").append(static_cast<sal_Int32>(aPixelSz.Height())).append("\""); + } + } + + // BGCOLOR + drawing::FillStyle eFillStyle = + rItemSet.Get(XATTR_FILLSTYLE).GetValue(); + if( drawing::FillStyle_SOLID==eFillStyle ) + { + const Color& rFillColor = + rItemSet.Get(XATTR_FILLCOLOR).GetColorValue(); + + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_bgcolor).append("="); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_Color( rWrt.Strm(), rFillColor ); + } + + if (!sOut.isEmpty()) + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + // and now ALIGN, HSPACE and VSPACE + HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MARQUEE; + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) ) + nFrameFlags |= HTML_FRMOPTS_MARQUEE_CSS1; + OString aEndTags = rHTMLWrt.OutFrameFormatOptions(rFormat, OUString(), nFrameFlags); + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) ) + rHTMLWrt.OutCSS1_FrameFormatOptions( rFormat, nFrameFlags, &rSdrObject ); + + rWrt.Strm().WriteChar( '>' ); + + // What follows now is the counterpart of SdrTextObject::SetText() + Outliner aOutliner(nullptr, OutlinerMode::TextObject); + aOutliner.SetUpdateMode( false ); + aOutliner.SetText( *pOutlinerParaObj ); + OUString aText( aOutliner.GetText( aOutliner.GetParagraph(0), + aOutliner.GetParagraphCount() ) ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aText, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_marquee, false ); + + if( !aEndTags.isEmpty() ) + rWrt.Strm().WriteOString( aEndTags ); + + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlfld.cxx b/sw/source/filter/html/htmlfld.cxx new file mode 100644 index 000000000..068968178 --- /dev/null +++ b/sw/source/filter/html/htmlfld.cxx @@ -0,0 +1,649 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/frame/XModel.hpp> +#include <osl/diagnose.h> +#include <docsh.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <svtools/htmltokn.h> +#include <svl/zforlist.hxx> +#include <unotools/useroptions.hxx> +#include <fmtfld.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <flddat.hxx> +#include "htmlfld.hxx" +#include "swhtml.hxx" + +using namespace nsSwDocInfoSubType; +using namespace ::com::sun::star; + +namespace { + +struct HTMLNumFormatTableEntry +{ + const char *pName; + NfIndexTableOffset eFormat; +}; + +} + +static HTMLOptionEnum<SwFieldIds> const aHTMLFieldTypeTable[] = +{ + { OOO_STRING_SW_HTML_FT_author, SwFieldIds::Author }, + { OOO_STRING_SW_HTML_FT_sender, SwFieldIds::ExtUser }, + { "DATE", SwFieldIds::Date }, + { "TIME", SwFieldIds::Time }, + { OOO_STRING_SW_HTML_FT_datetime, SwFieldIds::DateTime }, + { OOO_STRING_SW_HTML_FT_page, SwFieldIds::PageNumber }, + { OOO_STRING_SW_HTML_FT_docinfo, SwFieldIds::DocInfo }, + { OOO_STRING_SW_HTML_FT_docstat, SwFieldIds::DocStat }, + { OOO_STRING_SW_HTML_FT_filename, SwFieldIds::Filename }, + { nullptr, SwFieldIds(0) } +}; + +static HTMLNumFormatTableEntry const aHTMLDateFieldFormatTable[] = +{ + { "SSYS", NF_DATE_SYSTEM_SHORT }, + { "LSYS", NF_DATE_SYSTEM_LONG }, + { "DMY", NF_DATE_SYS_DDMMYY, }, + { "DMYY", NF_DATE_SYS_DDMMYYYY, }, + { "DMMY", NF_DATE_SYS_DMMMYY, }, + { "DMMYY", NF_DATE_SYS_DMMMYYYY, }, + { "DMMMY", NF_DATE_DIN_DMMMMYYYY }, + { "DMMMYY", NF_DATE_DIN_DMMMMYYYY }, + { "DDMMY", NF_DATE_SYS_NNDMMMYY }, + { "DDMMMY", NF_DATE_SYS_NNDMMMMYYYY }, + { "DDMMMYY", NF_DATE_SYS_NNDMMMMYYYY }, + { "DDDMMMY", NF_DATE_SYS_NNNNDMMMMYYYY }, + { "DDDMMMYY", NF_DATE_SYS_NNNNDMMMMYYYY }, + { "MY", NF_DATE_SYS_MMYY }, + { "MD", NF_DATE_DIN_MMDD }, + { "YMD", NF_DATE_DIN_YYMMDD }, + { "YYMD", NF_DATE_DIN_YYYYMMDD }, + { nullptr, NF_NUMERIC_START } +}; + +static HTMLNumFormatTableEntry const aHTMLTimeFieldFormatTable[] = +{ + { "SYS", NF_TIME_HHMMSS }, + { "SSMM24", NF_TIME_HHMM }, + { "SSMM12", NF_TIME_HHMMAMPM }, + { nullptr, NF_NUMERIC_START } +}; + +static HTMLOptionEnum<SvxNumType> const aHTMLPageNumFieldFormatTable[] = +{ + { OOO_STRING_SW_HTML_FF_uletter, SVX_NUM_CHARS_UPPER_LETTER }, + { OOO_STRING_SW_HTML_FF_lletter, SVX_NUM_CHARS_LOWER_LETTER }, + { OOO_STRING_SW_HTML_FF_uroman, SVX_NUM_ROMAN_UPPER }, + { OOO_STRING_SW_HTML_FF_lroman, SVX_NUM_ROMAN_LOWER }, + { OOO_STRING_SW_HTML_FF_arabic, SVX_NUM_ARABIC }, + { OOO_STRING_SW_HTML_FF_none, SVX_NUM_NUMBER_NONE }, + { OOO_STRING_SW_HTML_FF_char, SVX_NUM_CHAR_SPECIAL }, + { OOO_STRING_SW_HTML_FF_page, SVX_NUM_PAGEDESC }, + { OOO_STRING_SW_HTML_FF_ulettern, SVX_NUM_CHARS_UPPER_LETTER_N }, + { OOO_STRING_SW_HTML_FF_llettern, SVX_NUM_CHARS_LOWER_LETTER_N }, + { nullptr, SvxNumType(0) } +}; + +static HTMLOptionEnum<SwExtUserSubType> const aHTMLExtUsrFieldSubTable[] = +{ + { OOO_STRING_SW_HTML_FS_company, EU_COMPANY }, + { OOO_STRING_SW_HTML_FS_firstname, EU_FIRSTNAME }, + { OOO_STRING_SW_HTML_FS_name, EU_NAME }, + { OOO_STRING_SW_HTML_FS_shortcut, EU_SHORTCUT }, + { OOO_STRING_SW_HTML_FS_street, EU_STREET }, + { OOO_STRING_SW_HTML_FS_country, EU_COUNTRY }, + { OOO_STRING_SW_HTML_FS_zip, EU_ZIP }, + { OOO_STRING_SW_HTML_FS_city, EU_CITY }, + { OOO_STRING_SW_HTML_FS_title, EU_TITLE }, + { OOO_STRING_SW_HTML_FS_position, EU_POSITION }, + { OOO_STRING_SW_HTML_FS_pphone, EU_PHONE_PRIVATE }, + { OOO_STRING_SW_HTML_FS_cphone, EU_PHONE_COMPANY }, + { OOO_STRING_SW_HTML_FS_fax, EU_FAX }, + { OOO_STRING_SW_HTML_FS_email, EU_EMAIL }, + { OOO_STRING_SW_HTML_FS_state, EU_STATE }, + { nullptr, SwExtUserSubType(0) } +}; + +static HTMLOptionEnum<SwAuthorFormat> const aHTMLAuthorFieldFormatTable[] = +{ + { OOO_STRING_SW_HTML_FF_name, AF_NAME }, + { OOO_STRING_SW_HTML_FF_shortcut, AF_SHORTCUT }, + { nullptr, SwAuthorFormat(0) } +}; + +static HTMLOptionEnum<SwPageNumSubType> const aHTMLPageNumFieldSubTable[] = +{ + { OOO_STRING_SW_HTML_FS_random, PG_RANDOM }, + { OOO_STRING_SW_HTML_FS_next, PG_NEXT }, + { OOO_STRING_SW_HTML_FS_prev, PG_PREV }, + { nullptr, SwPageNumSubType(0) } +}; + +// UGLY: these are extensions of nsSwDocInfoSubType (in inc/docufld.hxx) +// these are necessary for importing document info fields written by +// older versions of OOo (< 3.0) which did not have DI_CUSTOM fields + const SwDocInfoSubType DI_INFO1 = DI_SUBTYPE_END + 1; + const SwDocInfoSubType DI_INFO2 = DI_SUBTYPE_END + 2; + const SwDocInfoSubType DI_INFO3 = DI_SUBTYPE_END + 3; + const SwDocInfoSubType DI_INFO4 = DI_SUBTYPE_END + 4; + +static HTMLOptionEnum<sal_uInt16> const aHTMLDocInfoFieldSubTable[] = +{ + { OOO_STRING_SW_HTML_FS_title, DI_TITLE }, + { OOO_STRING_SW_HTML_FS_theme, DI_SUBJECT }, + { OOO_STRING_SW_HTML_FS_keys, DI_KEYS }, + { OOO_STRING_SW_HTML_FS_comment, DI_COMMENT }, + { "INFO1", DI_INFO1 }, + { "INFO2", DI_INFO2 }, + { "INFO3", DI_INFO3 }, + { "INFO4", DI_INFO4 }, + { OOO_STRING_SW_HTML_FS_custom, DI_CUSTOM }, + { OOO_STRING_SW_HTML_FS_create, DI_CREATE }, + { OOO_STRING_SW_HTML_FS_change, DI_CHANGE }, + { nullptr, 0 } +}; + +static HTMLOptionEnum<sal_uInt16> const aHTMLDocInfoFieldFormatTable[] = +{ + { OOO_STRING_SW_HTML_FF_author, DI_SUB_AUTHOR }, + { OOO_STRING_SW_HTML_FF_time, DI_SUB_TIME }, + { OOO_STRING_SW_HTML_FF_date, DI_SUB_DATE }, + { nullptr, 0 } +}; + +static HTMLOptionEnum<SwDocStatSubType> const aHTMLDocStatFieldSubTable[] = +{ + { OOO_STRING_SW_HTML_FS_page, DS_PAGE }, + { OOO_STRING_SW_HTML_FS_para, DS_PARA }, + { OOO_STRING_SW_HTML_FS_word, DS_WORD }, + { OOO_STRING_SW_HTML_FS_char, DS_CHAR }, + { OOO_STRING_SW_HTML_FS_tbl, DS_TBL }, + { OOO_STRING_SW_HTML_FS_grf, DS_GRF }, + { OOO_STRING_SW_HTML_FS_ole, DS_OLE }, + { nullptr, SwDocStatSubType(0) } +}; + +static HTMLOptionEnum<SwFileNameFormat> const aHTMLFileNameFieldFormatTable[] = +{ + { OOO_STRING_SW_HTML_FF_name, FF_NAME }, + { OOO_STRING_SW_HTML_FF_pathname, FF_PATHNAME }, + { OOO_STRING_SW_HTML_FF_path, FF_PATH }, + { OOO_STRING_SW_HTML_FF_name_noext, FF_NAME_NOEXT }, + { nullptr, SwFileNameFormat(0) } +}; + +SvxNumType SwHTMLParser::GetNumType( const OUString& rStr, SvxNumType nDfltType ) +{ + const HTMLOptionEnum<SvxNumType> *pOptEnums = aHTMLPageNumFieldFormatTable; + while( pOptEnums->pName ) + { + if( rStr.equalsIgnoreAsciiCaseAscii( pOptEnums->pName ) ) + return pOptEnums->nValue; + pOptEnums++; + } + return nDfltType; +} + +void SwHTMLParser::NewField() +{ + bool bKnownType = false, bFixed = false, + bHasNumFormat = false, bHasNumValue = false; + SwFieldIds nType = SwFieldIds::Database; + OUString aValue, aNumFormat, aNumValue, aName; + const HTMLOption *pSubOption=nullptr, *pFormatOption=nullptr; + + const HTMLOptions& rHTMLOptions = GetOptions(); + size_t i; + + for ( i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::TYPE: + bKnownType = rOption.GetEnum( nType, aHTMLFieldTypeTable ); + break; + case HtmlOptionId::SUBTYPE: + pSubOption = &rOption; + break; + case HtmlOptionId::FORMAT: + pFormatOption = &rOption; + break; + case HtmlOptionId::NAME: + aName = rOption.GetString(); + break; + case HtmlOptionId::VALUE: + aValue = rOption.GetString(); + break; + case HtmlOptionId::SDNUM: + aNumFormat = rOption.GetString(); + bHasNumFormat = true; + break; + case HtmlOptionId::SDVAL: + aNumValue = rOption.GetString(); + bHasNumValue = true; + break; + case HtmlOptionId::SDFIXED: + bFixed = true; + break; + default: break; + } + } + + if( !bKnownType ) + return; + + // Author and sender are only inserted as a variable field if the document + // was last changed by ourself or nobody changed it and it was created + // by ourself. Otherwise it will be a fixed field. + if( !bFixed && + (SwFieldIds::ExtUser == nType || + SwFieldIds::Author == nType) ) + { + SvtUserOptions aOpt; + const OUString& rUser = aOpt.GetFullName(); + SwDocShell *pDocShell(m_xDoc->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"); + const OUString& rChanged = xDocProps->getModifiedBy(); + const OUString& rCreated = xDocProps->getAuthor(); + if( rUser.isEmpty() || + (!rChanged.isEmpty() ? rUser != rChanged : rUser != rCreated) ) + bFixed = true; + } + } + + SwFieldIds nWhich = nType; + if( SwFieldIds::Date==nType || SwFieldIds::Time==nType ) + nWhich = SwFieldIds::DateTime; + + SwFieldType* pType = m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( nWhich ); + std::unique_ptr<SwField> xNewField; + bool bInsOnEndTag = false; + + switch( nType ) + { + case SwFieldIds::ExtUser: + if( pSubOption ) + { + SwExtUserSubType nSub; + sal_uLong nFormat = 0; + if( bFixed ) + { + nFormat |= AF_FIXED; + bInsOnEndTag = true; + } + if( pSubOption->GetEnum( nSub, aHTMLExtUsrFieldSubTable ) ) + xNewField.reset(new SwExtUserField(static_cast<SwExtUserFieldType*>(pType), nSub, nFormat)); + } + break; + + case SwFieldIds::Author: + { + SwAuthorFormat nFormat = AF_NAME; + if( pFormatOption ) + pFormatOption->GetEnum( nFormat, aHTMLAuthorFieldFormatTable ); + if( bFixed ) + { + nFormat = static_cast<SwAuthorFormat>(static_cast<int>(nFormat) | AF_FIXED); + bInsOnEndTag = true; + } + + xNewField.reset(new SwAuthorField(static_cast<SwAuthorFieldType*>(pType), nFormat)); + } + break; + + case SwFieldIds::Date: + case SwFieldIds::Time: + { + sal_uInt32 nNumFormat = 0; + DateTime aDateTime( DateTime::SYSTEM ); + sal_Int64 nTime = aDateTime.GetTime(); + sal_Int32 nDate = aDateTime.GetDate(); + sal_uInt16 nSub = 0; + bool bValidFormat = false; + HTMLNumFormatTableEntry const * pFormatTable; + + if( SwFieldIds::Date==nType ) + { + nSub = DATEFLD; + pFormatTable = aHTMLDateFieldFormatTable; + if( !aValue.isEmpty() ) + nDate = aValue.toInt32(); + } + else + { + nSub = TIMEFLD; + pFormatTable = aHTMLTimeFieldFormatTable; + if( !aValue.isEmpty() ) + nTime = static_cast<sal_uLong>(aValue.toInt32()); + } + if( !aValue.isEmpty() ) + nSub |= FIXEDFLD; + + SvNumberFormatter *pFormatter = m_xDoc->GetNumberFormatter(); + if( pFormatOption ) + { + const OUString& rFormat = pFormatOption->GetString(); + for( int k = 0; pFormatTable[k].pName; ++k ) + { + if( rFormat.equalsIgnoreAsciiCaseAscii( pFormatTable[k].pName ) ) + { + nNumFormat = pFormatter->GetFormatIndex( + pFormatTable[k].eFormat, LANGUAGE_SYSTEM); + bValidFormat = true; + break; + } + } + } + if( !bValidFormat ) + nNumFormat = pFormatter->GetFormatIndex( pFormatTable[i].eFormat, + LANGUAGE_SYSTEM); + + xNewField.reset(new SwDateTimeField(static_cast<SwDateTimeFieldType *>(pType), nSub, nNumFormat)); + + if (nSub & FIXEDFLD) + static_cast<SwDateTimeField *>(xNewField.get())->SetDateTime(DateTime(Date(nDate), tools::Time(nTime))); + } + break; + + case SwFieldIds::DateTime: + if( bHasNumFormat ) + { + sal_uInt16 nSub = 0; + + SvNumberFormatter *pFormatter = m_xDoc->GetNumberFormatter(); + sal_uInt32 nNumFormat; + LanguageType eLang; + double dValue = GetTableDataOptionsValNum( + nNumFormat, eLang, aNumValue, aNumFormat, + *m_xDoc->GetNumberFormatter() ); + SvNumFormatType nFormatType = pFormatter->GetType( nNumFormat ); + switch( nFormatType ) + { + case SvNumFormatType::DATE: nSub = DATEFLD; break; + case SvNumFormatType::TIME: nSub = TIMEFLD; break; + default: break; + } + + if( nSub ) + { + if( bHasNumValue ) + nSub |= FIXEDFLD; + + xNewField.reset(new SwDateTimeField(static_cast<SwDateTimeFieldType *>(pType), nSub, nNumFormat)); + if (bHasNumValue) + static_cast<SwDateTimeField*>(xNewField.get())->SetValue(dValue); + } + } + break; + + case SwFieldIds::PageNumber: + if( pSubOption ) + { + SwPageNumSubType nSub; + if( pSubOption->GetEnum( nSub, aHTMLPageNumFieldSubTable ) ) + { + SvxNumType nFormat = SVX_NUM_PAGEDESC; + if( pFormatOption ) + pFormatOption->GetEnum( nFormat, aHTMLPageNumFieldFormatTable ); + + short nOff = 0; + + if( nFormat!=SVX_NUM_CHAR_SPECIAL && !aValue.isEmpty() ) + nOff = static_cast<short>(aValue.toInt32()); + else if( nSub == PG_NEXT ) + nOff = 1; + else if( nSub == PG_PREV ) + nOff = -1; + + if( nFormat==SVX_NUM_CHAR_SPECIAL && + nSub==PG_RANDOM ) + nFormat = SVX_NUM_PAGEDESC; + + xNewField.reset(new SwPageNumberField(static_cast<SwPageNumberFieldType*>(pType), nSub, nFormat, nOff)); + if (nFormat == SVX_NUM_CHAR_SPECIAL) + static_cast<SwPageNumberField*>(xNewField.get())->SetUserString(aValue); + } + } + break; + + case SwFieldIds::DocInfo: + if( pSubOption ) + { + sal_uInt16 nSub; + if( pSubOption->GetEnum( nSub, aHTMLDocInfoFieldSubTable ) ) + { + sal_uInt16 nExtSub = 0; + if( DI_CREATE==static_cast<SwDocInfoSubType>(nSub) || + DI_CHANGE==static_cast<SwDocInfoSubType>(nSub) ) + { + nExtSub = DI_SUB_AUTHOR; + if( pFormatOption ) + pFormatOption->GetEnum( nExtSub, aHTMLDocInfoFieldFormatTable ); + nSub |= nExtSub; + } + + sal_uInt32 nNumFormat = 0; + double dValue = 0; + if( bHasNumFormat && (DI_SUB_DATE==nExtSub || DI_SUB_TIME==nExtSub) ) + { + LanguageType eLang; + dValue = GetTableDataOptionsValNum( + nNumFormat, eLang, aNumValue, aNumFormat, + *m_xDoc->GetNumberFormatter() ); + bFixed &= bHasNumValue; + } + else + bHasNumValue = false; + + if( nSub >= DI_INFO1 && nSub <= DI_INFO4 && aName.isEmpty() ) + { + // backward compatibility for OOo 2: + // map to names stored in AddMetaUserDefined + aName = m_InfoNames[nSub - DI_INFO1]; + nSub = DI_CUSTOM; + } + + if( bFixed ) + { + nSub |= DI_SUB_FIXED; + bInsOnEndTag = true; + } + + xNewField.reset(new SwDocInfoField(static_cast<SwDocInfoFieldType *>(pType), nSub, aName, nNumFormat)); + if (bHasNumValue) + static_cast<SwDocInfoField*>(xNewField.get())->SetValue(dValue); + } + } + break; + + case SwFieldIds::DocStat: + if( pSubOption ) + { + SwDocStatSubType nSub; + if( pSubOption->GetEnum( nSub, aHTMLDocStatFieldSubTable ) ) + { + SvxNumType nFormat = SVX_NUM_ARABIC; + if( pFormatOption ) + pFormatOption->GetEnum( nFormat, aHTMLPageNumFieldFormatTable ); + xNewField.reset(new SwDocStatField(static_cast<SwDocStatFieldType*>(pType), nSub, nFormat)); + m_bUpdateDocStat |= (DS_PAGE != nSub); + } + } + break; + + case SwFieldIds::Filename: + { + SwFileNameFormat nFormat = FF_NAME; + if( pFormatOption ) + pFormatOption->GetEnum( nFormat, aHTMLFileNameFieldFormatTable ); + if( bFixed ) + { + nFormat = static_cast<SwFileNameFormat>(static_cast<int>(nFormat) | FF_FIXED); + bInsOnEndTag = true; + } + + xNewField.reset(new SwFileNameField(static_cast<SwFileNameFieldType*>(pType), nFormat)); + } + break; + default: + ; + } + + if (xNewField) + { + if (bInsOnEndTag) + { + m_xField = std::move(xNewField); + } + else + { + m_xDoc->getIDocumentContentOperations().InsertPoolItem(*m_pPam, SwFormatField(*xNewField)); + xNewField.reset(); + } + m_bInField = true; + } +} + +void SwHTMLParser::EndField() +{ + if (m_xField) + { + switch (m_xField->Which()) + { + case SwFieldIds::DocInfo: + OSL_ENSURE( static_cast<SwDocInfoField*>(m_xField.get())->IsFixed(), + "Field DocInfo should not have been saved" ); + static_cast<SwDocInfoField*>(m_xField.get())->SetExpansion( m_aContents ); + break; + + case SwFieldIds::ExtUser: + OSL_ENSURE( static_cast<SwExtUserField*>(m_xField.get())->IsFixed(), + "Field ExtUser should not have been saved" ); + static_cast<SwExtUserField*>(m_xField.get())->SetExpansion( m_aContents ); + break; + + case SwFieldIds::Author: + OSL_ENSURE( static_cast<SwAuthorField*>(m_xField.get())->IsFixed(), + "Field Author should not have been saved" ); + static_cast<SwAuthorField*>(m_xField.get())->SetExpansion( m_aContents ); + break; + + case SwFieldIds::Filename: + OSL_ENSURE( static_cast<SwFileNameField*>(m_xField.get())->IsFixed(), + "Field FileName should not have been saved" ); + static_cast<SwFileNameField*>(m_xField.get())->SetExpansion( m_aContents ); + break; + default: break; + } + + m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, SwFormatField(*m_xField) ); + m_xField.reset(); + } + + m_bInField = false; + m_aContents.clear(); +} + +void SwHTMLParser::InsertFieldText() +{ + if (m_xField) + { + // append the current text part to the text + m_aContents += aToken; + } +} + +void SwHTMLParser::InsertCommentText( const char *pTag ) +{ + bool bEmpty = m_aContents.isEmpty(); + if( !bEmpty ) + m_aContents += "\n"; + + m_aContents += aToken; + if( bEmpty && pTag ) + { + m_aContents = OUStringLiteral("HTML: <") + OUStringChar(*pTag) + ">" + m_aContents; + } +} + +void SwHTMLParser::InsertComment( const OUString& rComment, const char *pTag ) +{ + OUString aComment( rComment ); + if( pTag ) + { + aComment += "</" + + OUString::createFromAscii(pTag) + + ">"; + } + + // MIB 24.06.97: If a PostIt should be insert after a space, we + // will insert before the space. Then there are less problems + // during formatting. (bug #40483#) + const sal_Int32 nPos = m_pPam->GetPoint()->nContent.GetIndex(); + SwTextNode *pTextNd = m_pPam->GetNode().GetTextNode(); + bool bMoveFwd = false; + if (nPos>0 && pTextNd && (' ' == pTextNd->GetText()[nPos-1])) + { + bMoveFwd = true; + + sal_uLong nNodeIdx = m_pPam->GetPoint()->nNode.GetIndex(); + const sal_Int32 nIdx = m_pPam->GetPoint()->nContent.GetIndex(); + for( auto i = m_aSetAttrTab.size(); i > 0; ) + { + HTMLAttr *pAttr = m_aSetAttrTab[--i]; + if( pAttr->GetSttParaIdx() != nNodeIdx || + pAttr->GetSttCnt() != nIdx ) + break; + + if( RES_TXTATR_FIELD == pAttr->m_pItem->Which() && + SwFieldIds::Script == static_cast<const SwFormatField *>(pAttr->m_pItem.get())->GetField()->GetTyp()->Which() ) + { + bMoveFwd = false; + break; + } + } + + if( bMoveFwd ) + m_pPam->Move( fnMoveBackward ); + } + + SwPostItField aPostItField( + static_cast<SwPostItFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Postit )), + OUString(), aComment, OUString(), OUString(), DateTime(DateTime::SYSTEM)); + InsertAttr( SwFormatField( aPostItField ), false ); + + if( bMoveFwd ) + m_pPam->Move( fnMoveForward ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlfld.hxx b/sw/source/filter/html/htmlfld.hxx new file mode 100644 index 000000000..36cfc9e07 --- /dev/null +++ b/sw/source/filter/html/htmlfld.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFLD_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFLD_HXX + + +#define OOO_STRING_SW_HTML_FT_author "AUTHOR" +#define OOO_STRING_SW_HTML_FT_sender "SENDER" +#define OOO_STRING_SW_HTML_FT_datetime "DATETIME" +#define OOO_STRING_SW_HTML_FT_page "PAGE" +#define OOO_STRING_SW_HTML_FT_docinfo "DOCINFO" +#define OOO_STRING_SW_HTML_FT_docstat "DOCSTAT" +#define OOO_STRING_SW_HTML_FT_filename "FILENAME" +#define OOO_STRING_SW_HTML_FS_company "COMPANY" +#define OOO_STRING_SW_HTML_FS_firstname "FIRSTNAME" +#define OOO_STRING_SW_HTML_FS_name "NAME" +#define OOO_STRING_SW_HTML_FS_shortcut "SHORTCUT" +#define OOO_STRING_SW_HTML_FS_street "STREET" +#define OOO_STRING_SW_HTML_FS_country "COUNTRY" +#define OOO_STRING_SW_HTML_FS_zip "ZIP" +#define OOO_STRING_SW_HTML_FS_city "CITY" +#define OOO_STRING_SW_HTML_FS_title "TITLE" +#define OOO_STRING_SW_HTML_FS_position "POSITION" +#define OOO_STRING_SW_HTML_FS_pphone "PPHONE" +#define OOO_STRING_SW_HTML_FS_cphone "CPHONE" +#define OOO_STRING_SW_HTML_FS_fax "FAX" +#define OOO_STRING_SW_HTML_FS_email "EMAIL" +#define OOO_STRING_SW_HTML_FS_state "STATE" +#define OOO_STRING_SW_HTML_FS_random "RANDOM" +#define OOO_STRING_SW_HTML_FS_next "NEXT" +#define OOO_STRING_SW_HTML_FS_prev "PREV" +#define OOO_STRING_SW_HTML_FS_theme "THEME" +#define OOO_STRING_SW_HTML_FS_keys "KEYS" +#define OOO_STRING_SW_HTML_FS_comment "COMMENT" +#define OOO_STRING_SW_HTML_FS_custom "CUSTOM" +#define OOO_STRING_SW_HTML_FS_create "CREATE" +#define OOO_STRING_SW_HTML_FS_change "CHANGE" +#define OOO_STRING_SW_HTML_FS_page "PAGE" +#define OOO_STRING_SW_HTML_FS_para "PARAGRAPH" +#define OOO_STRING_SW_HTML_FS_word "WORD" +#define OOO_STRING_SW_HTML_FS_char "CHAR" +#define OOO_STRING_SW_HTML_FS_tbl "TABLE" +#define OOO_STRING_SW_HTML_FS_grf "GRAPHIC" +#define OOO_STRING_SW_HTML_FS_ole "OLE" +#define OOO_STRING_SW_HTML_FF_name "NAME" +#define OOO_STRING_SW_HTML_FF_shortcut "SHORTCUT" +#define OOO_STRING_SW_HTML_FF_uletter "ULETTER" +#define OOO_STRING_SW_HTML_FF_lletter "LLETTER" +#define OOO_STRING_SW_HTML_FF_uroman "UROMAN" +#define OOO_STRING_SW_HTML_FF_lroman "LROMAN" +#define OOO_STRING_SW_HTML_FF_arabic "ARABIC" +#define OOO_STRING_SW_HTML_FF_none "NONE" +#define OOO_STRING_SW_HTML_FF_char "CHAR" +#define OOO_STRING_SW_HTML_FF_page "PAGE" +#define OOO_STRING_SW_HTML_FF_ulettern "ULETTERN" +#define OOO_STRING_SW_HTML_FF_llettern "LLETTERN" +#define OOO_STRING_SW_HTML_FF_author "AUTHOR" +#define OOO_STRING_SW_HTML_FF_time "TIME" +#define OOO_STRING_SW_HTML_FF_date "DATE" +#define OOO_STRING_SW_HTML_FF_pathname "PATHNAME" +#define OOO_STRING_SW_HTML_FF_path "PATH" +#define OOO_STRING_SW_HTML_FF_name_noext "NAME-NOEXT" + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlfldw.cxx b/sw/source/filter/html/htmlfldw.cxx new file mode 100644 index 000000000..825d563a9 --- /dev/null +++ b/sw/source/filter/html/htmlfldw.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 <com/sun/star/i18n/XBreakIterator.hpp> +#include <comphelper/string.hxx> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmlout.hxx> +#include <osl/diagnose.h> +#include <fmtfld.hxx> +#include <doc.hxx> +#include <breakit.hxx> +#include <ndtxt.hxx> +#include <txtfld.hxx> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <flddat.hxx> +#include <viewopt.hxx> +#include "htmlfld.hxx" +#include "wrthtml.hxx" +#include <rtl/strbuf.hxx> +#include "css1atr.hxx" +#include "css1kywd.hxx" + +using namespace nsSwDocInfoSubType; + +const char *SwHTMLWriter::GetNumFormat( sal_uInt16 nFormat ) +{ + const char *pFormatStr = nullptr; + + switch( static_cast<SvxNumType>(nFormat) ) + { + case SVX_NUM_CHARS_UPPER_LETTER: pFormatStr = OOO_STRING_SW_HTML_FF_uletter; break; + case SVX_NUM_CHARS_LOWER_LETTER: pFormatStr = OOO_STRING_SW_HTML_FF_lletter; break; + case SVX_NUM_ROMAN_UPPER: pFormatStr = OOO_STRING_SW_HTML_FF_uroman; break; + case SVX_NUM_ROMAN_LOWER: pFormatStr = OOO_STRING_SW_HTML_FF_lroman; break; + case SVX_NUM_ARABIC: pFormatStr = OOO_STRING_SW_HTML_FF_arabic; break; + case SVX_NUM_NUMBER_NONE: pFormatStr = OOO_STRING_SW_HTML_FF_none; break; + case SVX_NUM_CHAR_SPECIAL: pFormatStr = OOO_STRING_SW_HTML_FF_char; break; + case SVX_NUM_PAGEDESC: pFormatStr = OOO_STRING_SW_HTML_FF_page; break; + case SVX_NUM_CHARS_UPPER_LETTER_N: pFormatStr = OOO_STRING_SW_HTML_FF_ulettern; break; + case SVX_NUM_CHARS_LOWER_LETTER_N: pFormatStr = OOO_STRING_SW_HTML_FF_llettern; break; + default: + ; + } + + return pFormatStr; +} + +static Writer& OutHTML_SwField( Writer& rWrt, const SwField* pField, + const SwTextNode& rTextNd, sal_Int32 nFieldPos ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwFieldType* pFieldTyp = pField->GetTyp(); + SwFieldIds nField = pFieldTyp->Which(); + sal_uLong nFormat = pField->GetFormat(); + + const char *pTypeStr=nullptr, // TYPE + *pSubStr=nullptr, // SUBTYPE + *pFormatStr=nullptr; // FORMAT (SW) + OUString aValue; // VALUE (SW) + bool bNumFormat=false; // SDNUM (Number-Formatter-Format) + bool bNumValue=false; // SDVAL (Number-Formatter-Value) + double dNumValue = 0.0; // SDVAL (Number-Formatter-Value) + bool bFixed=false; // SDFIXED + OUString aName; // NAME (CUSTOM) + + switch( nField ) + { + case SwFieldIds::ExtUser: + pTypeStr = OOO_STRING_SW_HTML_FT_sender; + switch( static_cast<SwExtUserSubType>(pField->GetSubType()) ) + { + case EU_COMPANY: pSubStr = OOO_STRING_SW_HTML_FS_company; break; + case EU_FIRSTNAME: pSubStr = OOO_STRING_SW_HTML_FS_firstname; break; + case EU_NAME: pSubStr = OOO_STRING_SW_HTML_FS_name; break; + case EU_SHORTCUT: pSubStr = OOO_STRING_SW_HTML_FS_shortcut; break; + case EU_STREET: pSubStr = OOO_STRING_SW_HTML_FS_street; break; + case EU_COUNTRY: pSubStr = OOO_STRING_SW_HTML_FS_country; break; + case EU_ZIP: pSubStr = OOO_STRING_SW_HTML_FS_zip; break; + case EU_CITY: pSubStr = OOO_STRING_SW_HTML_FS_city; break; + case EU_TITLE: pSubStr = OOO_STRING_SW_HTML_FS_title; break; + case EU_POSITION: pSubStr = OOO_STRING_SW_HTML_FS_position; break; + case EU_PHONE_PRIVATE: pSubStr = OOO_STRING_SW_HTML_FS_pphone; break; + case EU_PHONE_COMPANY: pSubStr = OOO_STRING_SW_HTML_FS_cphone; break; + case EU_FAX: pSubStr = OOO_STRING_SW_HTML_FS_fax; break; + case EU_EMAIL: pSubStr = OOO_STRING_SW_HTML_FS_email; break; + case EU_STATE: pSubStr = OOO_STRING_SW_HTML_FS_state; break; + default: + ; + } + OSL_ENSURE( pSubStr, "unknown sub type for SwExtUserField" ); + bFixed = static_cast<const SwExtUserField*>(pField)->IsFixed(); + break; + + case SwFieldIds::Author: + pTypeStr = OOO_STRING_SW_HTML_FT_author; + switch( static_cast<SwAuthorFormat>(nFormat) & 0xff) + { + case AF_NAME: pFormatStr = OOO_STRING_SW_HTML_FF_name; break; + case AF_SHORTCUT: pFormatStr = OOO_STRING_SW_HTML_FF_shortcut; break; + } + OSL_ENSURE( pFormatStr, "unknown format for SwAuthorField" ); + bFixed = static_cast<const SwAuthorField*>(pField)->IsFixed(); + break; + + case SwFieldIds::DateTime: + pTypeStr = OOO_STRING_SW_HTML_FT_datetime; + bNumFormat = true; + if( static_cast<const SwDateTimeField*>(pField)->IsFixed() ) + { + bNumValue = true; + dNumValue = static_cast<const SwDateTimeField*>(pField)->GetValue(); + } + break; + + case SwFieldIds::PageNumber: + { + pTypeStr = OOO_STRING_SW_HTML_FT_page; + SwPageNumSubType eSubType = static_cast<SwPageNumSubType>(pField->GetSubType()); + switch( eSubType ) + { + case PG_RANDOM: pSubStr = OOO_STRING_SW_HTML_FS_random; break; + case PG_NEXT: pSubStr = OOO_STRING_SW_HTML_FS_next; break; + case PG_PREV: pSubStr = OOO_STRING_SW_HTML_FS_prev; break; + } + OSL_ENSURE( pSubStr, "unknown sub type for SwPageNumberField" ); + pFormatStr = SwHTMLWriter::GetNumFormat( static_cast< sal_uInt16 >(nFormat) ); + + if( static_cast<SvxNumType>(nFormat)==SVX_NUM_CHAR_SPECIAL ) + { + aValue = static_cast<const SwPageNumberField *>(pField)->GetUserString(); + } + else + { + const OUString& rValue = pField->GetPar2(); + short nValue = static_cast<short>(rValue.toInt32()); + if( (eSubType == PG_NEXT && nValue!=1) || + (eSubType == PG_PREV && nValue!=-1) || + (eSubType == PG_RANDOM && nValue!=0) ) + { + aValue = rValue; + } + } + } + break; + case SwFieldIds::DocInfo: + { + sal_uInt16 nSubType = pField->GetSubType(); + pTypeStr = OOO_STRING_SW_HTML_FT_docinfo; + sal_uInt16 nExtSubType = nSubType & 0x0f00; + nSubType &= 0x00ff; + + switch( nSubType ) + { + case DI_TITLE: pSubStr = OOO_STRING_SW_HTML_FS_title; break; + case DI_SUBJECT: pSubStr = OOO_STRING_SW_HTML_FS_theme; break; + case DI_KEYS: pSubStr = OOO_STRING_SW_HTML_FS_keys; break; + case DI_COMMENT: pSubStr = OOO_STRING_SW_HTML_FS_comment; break; + case DI_CREATE: pSubStr = OOO_STRING_SW_HTML_FS_create; break; + case DI_CHANGE: pSubStr = OOO_STRING_SW_HTML_FS_change; break; + case DI_CUSTOM: pSubStr = OOO_STRING_SW_HTML_FS_custom; break; + default: pTypeStr = nullptr; break; + } + + if( DI_CUSTOM == nSubType ) { + aName = static_cast<const SwDocInfoField*>(pField)->GetName(); + } + + if( DI_CREATE == nSubType || DI_CHANGE == nSubType ) + { + switch( nExtSubType ) + { + case DI_SUB_AUTHOR: + pFormatStr = OOO_STRING_SW_HTML_FF_author; + break; + case DI_SUB_TIME: + pFormatStr = OOO_STRING_SW_HTML_FF_time; + bNumFormat = true; + break; + case DI_SUB_DATE: + pFormatStr = OOO_STRING_SW_HTML_FF_date; + bNumFormat = true; + break; + } + } + bFixed = static_cast<const SwDocInfoField*>(pField)->IsFixed(); + if( bNumFormat ) + { + if( bFixed ) + { + // For a fixed field output the num value too. + // Fixed fields without number format shouldn't + // exist. See below for OSL_ENSURE(). + dNumValue = static_cast<const SwDocInfoField*>(pField)->GetValue(); + bNumValue = true; + } + else if( !nFormat ) + { + // Non-fixed fields may not have a number format, when + // they come from a 4.0-document. + bNumFormat = false; + } + } + } + break; + + case SwFieldIds::DocStat: + { + pTypeStr = OOO_STRING_SW_HTML_FT_docstat; + sal_uInt16 nSubType = pField->GetSubType(); + switch( nSubType ) + { + case DS_PAGE: pSubStr = OOO_STRING_SW_HTML_FS_page; break; + case DS_PARA: pSubStr = OOO_STRING_SW_HTML_FS_para; break; + case DS_WORD: pSubStr = OOO_STRING_SW_HTML_FS_word; break; + case DS_CHAR: pSubStr = OOO_STRING_SW_HTML_FS_char; break; + case DS_TBL: pSubStr = OOO_STRING_SW_HTML_FS_tbl; break; + case DS_GRF: pSubStr = OOO_STRING_SW_HTML_FS_grf; break; + case DS_OLE: pSubStr = OOO_STRING_SW_HTML_FS_ole; break; + default: pTypeStr = nullptr; break; + } + pFormatStr = SwHTMLWriter::GetNumFormat( static_cast< sal_uInt16 >(nFormat) ); + } + break; + + case SwFieldIds::Filename: + pTypeStr = OOO_STRING_SW_HTML_FT_filename; + switch( static_cast<SwFileNameFormat>(nFormat & ~FF_FIXED) ) + { + case FF_NAME: pFormatStr = OOO_STRING_SW_HTML_FF_name; break; + case FF_PATHNAME: pFormatStr = OOO_STRING_SW_HTML_FF_pathname; break; + case FF_PATH: pFormatStr = OOO_STRING_SW_HTML_FF_path; break; + case FF_NAME_NOEXT: pFormatStr = OOO_STRING_SW_HTML_FF_name_noext; break; + default: + ; + } + bFixed = static_cast<const SwFileNameField*>(pField)->IsFixed(); + OSL_ENSURE( pFormatStr, "unknown format for SwFileNameField" ); + break; + default: break; + } + + // ReqIF-XHTML doesn't allow <sdfield>. + if (rHTMLWrt.mbReqIF && pTypeStr) + { + pTypeStr = nullptr; + } + + // Output the <sdfield> tag. + if( pTypeStr ) + { + OStringBuffer sOut; + sOut.append('<'); + sOut.append(rHTMLWrt.GetNamespace()); + sOut.append(OOO_STRING_SVTOOLS_HTML_sdfield).append(' '). + append(OOO_STRING_SVTOOLS_HTML_O_type).append('='). + append(pTypeStr); + if( pSubStr ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_subtype). + append('=').append(pSubStr); + } + if( pFormatStr ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_format). + append('=').append(pFormatStr); + } + if( !aName.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_name). + append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + if( !aValue.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_value). + append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aValue, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + if( bNumFormat ) + { + OSL_ENSURE( nFormat, "number format is 0" ); + sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum( + bNumValue, dNumValue, nFormat, + *rHTMLWrt.m_pDoc->GetNumberFormatter(), rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters)); + } + if( bFixed ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_sdfixed); + } + sOut.append('>'); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + } + + // output content of the field + OUString const sExpand( pField->ExpandField(true, nullptr) ); + bool bNeedsCJKProcessing = false; + if( !sExpand.isEmpty() ) + { + sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType( sExpand, 0 ); + sal_Int32 nPos = g_pBreakIt->GetBreakIter()->endOfScript( sExpand, 0, + nScriptType ); + + sal_uInt16 nScript = + SwHTMLWriter::GetCSS1ScriptForScriptType( nScriptType ); + if( (nPos < sExpand.getLength() && nPos >= 0) || nScript != rHTMLWrt.m_nCSS1Script ) + { + bNeedsCJKProcessing = true; + } + } + + if( bNeedsCJKProcessing ) + { + //sequence of (start, end) property ranges we want to + //query + SfxItemSet aScriptItemSet( rWrt.m_pDoc->GetAttrPool(), + svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_POSTURE, RES_CHRATR_POSTURE, + RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>{} ); + rTextNd.GetParaAttr(aScriptItemSet, nFieldPos, nFieldPos+1); + + sal_uInt16 aWesternWhichIds[4] = + { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, + RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT }; + sal_uInt16 aCJKWhichIds[4] = + { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT }; + sal_uInt16 aCTLWhichIds[4] = + { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, + RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }; + + sal_uInt16 *pRefWhichIds = nullptr; + switch( rHTMLWrt.m_nCSS1Script ) + { + case CSS1_OUTMODE_WESTERN: + pRefWhichIds = aWesternWhichIds; + break; + case CSS1_OUTMODE_CJK: + pRefWhichIds = aCJKWhichIds; + break; + case CSS1_OUTMODE_CTL: + pRefWhichIds = aCTLWhichIds; + break; + } + + sal_Int32 nPos = 0; + do + { + sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType( sExpand, nPos ); + sal_uInt16 nScript = + SwHTMLWriter::GetCSS1ScriptForScriptType( nScriptType ); + sal_Int32 nEndPos = g_pBreakIt->GetBreakIter()->endOfScript( + sExpand, nPos, nScriptType ); + sal_Int32 nChunkLen = nEndPos - nPos; + if( nScript != CSS1_OUTMODE_ANY_SCRIPT && + /* #108791# */ nScript != rHTMLWrt.m_nCSS1Script ) + { + sal_uInt16 *pWhichIds = nullptr; + switch( nScript ) + { + case CSS1_OUTMODE_WESTERN: pWhichIds = aWesternWhichIds; break; + case CSS1_OUTMODE_CJK: pWhichIds = aCJKWhichIds; break; + case CSS1_OUTMODE_CTL: pWhichIds = aCTLWhichIds; break; + } + + rHTMLWrt.m_bTagOn = true; + + const SfxPoolItem *aItems[5]; + int nItems = 0; + + assert(pWhichIds && pRefWhichIds); + if (pWhichIds && pRefWhichIds) + { + for( int i=0; i<4; i++ ) + { + const SfxPoolItem *pRefItem = + aScriptItemSet.GetItem( pRefWhichIds[i] ); + const SfxPoolItem *pItem = + aScriptItemSet.GetItem( pWhichIds[i] ); + if( pRefItem && pItem && + !(0==i ? swhtml_css1atr_equalFontItems( *pRefItem, *pItem ) + : *pRefItem == *pItem) ) + { + Out( aHTMLAttrFnTab, *pItem, rHTMLWrt ); + aItems[nItems++] = pItem; + } + } + } + + HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand.copy( nPos, nChunkLen ), + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + + rHTMLWrt.m_bTagOn = false; + while( nItems ) + Out( aHTMLAttrFnTab, *aItems[--nItems], rHTMLWrt ); + + } + else + { + HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand.copy( nPos, nChunkLen ), + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + nPos = nEndPos; + } + while( nPos < sExpand.getLength() ); + } + else + { + HTMLOutFuncs::Out_String( rWrt.Strm(), sExpand, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + + // Output the closing tag. + if( pTypeStr ) + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_sdfield, false ); + + return rWrt; +} + +Writer& OutHTML_SwFormatField( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + const SwFormatField & rField = static_cast<const SwFormatField&>(rHt); + const SwField* pField = rField.GetField(); + const SwFieldType* pFieldTyp = pField->GetTyp(); + + if( SwFieldIds::SetExp == pFieldTyp->Which() && + (nsSwGetSetExpType::GSE_STRING & pField->GetSubType()) ) + { + const bool bOn = pFieldTyp->GetName() == "HTML_ON"; + if (!bOn && pFieldTyp->GetName() != "HTML_OFF") + return rWrt; + + OUString rText(comphelper::string::strip(pField->GetPar2(), ' ')); + rWrt.Strm().WriteChar( '<' ); + if( !bOn ) + rWrt.Strm().WriteChar( '/' ); + // TODO: HTML-Tags are written without entities, that for, characters + // not contained in the destination encoding are lost! + OString sTmp(OUStringToOString(rText, + static_cast<SwHTMLWriter&>(rWrt).m_eDestEnc)); + rWrt.Strm().WriteOString( sTmp ).WriteChar( '>' ); + } + else if( SwFieldIds::Postit == pFieldTyp->Which() ) + { + // Comments will be written in ANSI character set, but with system + // line breaks. + const OUString& rComment = pField->GetPar2(); + bool bWritten = false; + + if( (rComment.getLength() >= 6 && rComment.startsWith("<") && rComment.endsWith(">") && + rComment.copy( 1, 4 ).equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_meta) ) || + (rComment.getLength() >= 7 && + rComment.startsWith( "<!--" ) && + rComment.endsWith( "-->" )) ) + { + // directly output META tags + OUString sComment(convertLineEnd(rComment, GetSystemLineEnd())); + // TODO: HTML-Tags are written without entities, that for, + // characters not contained in the destination encoding are lost! + OString sTmp(OUStringToOString(sComment, + static_cast<SwHTMLWriter&>(rWrt).m_eDestEnc)); + rWrt.Strm().WriteOString( sTmp ); + bWritten = true; + } + else if( rComment.getLength() >= 7 && + rComment.endsWith(">") && + rComment.startsWithIgnoreAsciiCase( "HTML:" ) ) + { + OUString sComment(comphelper::string::stripStart(rComment.copy(5), ' ')); + if( '<' == sComment[0] ) + { + sComment = convertLineEnd(sComment, GetSystemLineEnd()); + // TODO: HTML-Tags are written without entities, that for, + // characters not contained in the destination encoding are + // lost! + OString sTmp(OUStringToOString(sComment, + static_cast<SwHTMLWriter&>(rWrt).m_eDestEnc)); + rWrt.Strm().WriteOString( sTmp ); + bWritten = true; + } + + } + + if( !bWritten ) + { + OUString sComment(convertLineEnd(rComment, GetSystemLineEnd())); + // TODO: ??? + OString sOut = + "<" OOO_STRING_SVTOOLS_HTML_comment + " " + + OUStringToOString(sComment, static_cast<SwHTMLWriter&>(rWrt).m_eDestEnc) + + " -->"; + rWrt.Strm().WriteOString( sOut ); + } + } + else if( SwFieldIds::Script == pFieldTyp->Which() ) + { + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + + bool bURL = static_cast<const SwScriptField *>(pField)->IsCodeURL(); + const OUString& rType = pField->GetPar1(); + OUString aContents, aURL; + if(bURL) + aURL = pField->GetPar2(); + else + aContents = pField->GetPar2(); + + // otherwise is the script content itself. Since only JavaScript + // is in fields, it must be JavaScript ...:) + HTMLOutFuncs::OutScript( rWrt.Strm(), rWrt.GetBaseURL(), aContents, rType, JAVASCRIPT, + aURL, nullptr, nullptr, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + } + else + { + const SwTextField *pTextField = rField.GetTextField(); + OSL_ENSURE( pTextField, "Where is the txt fld?" ); + if( pTextField ) + { + // ReqIF-XHTML doesn't allow specifying a background color. + bool bFieldShadings = SwViewOption::IsFieldShadings() && !rHTMLWrt.mbReqIF; + if (bFieldShadings) + { + // If there is a text portion background started already, that should have priority. + auto it = rHTMLWrt.maStartedAttributes.find(RES_CHRATR_BACKGROUND); + if (it != rHTMLWrt.maStartedAttributes.end()) + bFieldShadings = it->second <= 0; + } + + if (bFieldShadings) + { + OStringBuffer sOut; + sOut.append("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span); + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\""); + sOut.append(sCSS1_P_background); + sOut.append(": "); + + Color& rColor = SwViewOption::GetFieldShadingsColor(); + sOut.append(GetCSS1_Color(rColor)); + sOut.append("\">"); + rWrt.Strm().WriteOString(sOut.makeStringAndClear()); + } + + OutHTML_SwField( rWrt, pField, pTextField->GetTextNode(), + pTextField->GetStart() ); + + if (bFieldShadings) + HTMLOutFuncs::Out_AsciiTag( + rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false); + } + } + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlfly.cxx b/sw/source/filter/html/htmlfly.cxx new file mode 100644 index 000000000..4a333ed3d --- /dev/null +++ b/sw/source/filter/html/htmlfly.cxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "htmlfly.hxx" + +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <flypos.hxx> + +#include <frmfmt.hxx> +#include <ndindex.hxx> +#include <pam.hxx> +#include <osl/diagnose.h> + +using namespace css; + +SwHTMLPosFlyFrame::SwHTMLPosFlyFrame( const SwPosFlyFrame& rPosFly, + const SdrObject *pSdrObj, + AllHtmlFlags nFlags ) : + pFrameFormat( &rPosFly.GetFormat() ), + pSdrObject( pSdrObj ), + pNdIdx( new SwNodeIndex( rPosFly.GetNdIndex() ) ), + nOrdNum( rPosFly.GetOrdNum() ), + nContentIdx( 0 ), + nAllFlags( nFlags ) +{ + const SwFormatAnchor& rAnchor = rPosFly.GetFormat().GetAnchor(); + if ((RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) && + HtmlPosition::Inside == GetOutPos() ) + { + // Output of auto-bound frames will be a character farther back, + // because then the position aligns with Netscape. + OSL_ENSURE( rAnchor.GetContentAnchor(), "No anchor position?" ); + if( rAnchor.GetContentAnchor() ) + { + nContentIdx = rAnchor.GetContentAnchor()->nContent.GetIndex(); + sal_Int16 eHoriRel = rPosFly.GetFormat().GetHoriOrient(). + GetRelationOrient(); + if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel ) + { + const SwContentNode *pCNd = pNdIdx->GetNode().GetContentNode(); + OSL_ENSURE( pCNd, "No Content-Node at PaM position" ); + if( pCNd && nContentIdx < pCNd->Len() ) + nContentIdx++; + } + } + } +} + +bool SwHTMLPosFlyFrame::operator<( const SwHTMLPosFlyFrame& rFrame ) const +{ + if( pNdIdx->GetIndex() == rFrame.pNdIdx->GetIndex() ) + { + if( nContentIdx == rFrame.nContentIdx ) + { + if( GetOutPos() == rFrame.GetOutPos() ) + return nOrdNum < rFrame.nOrdNum; + else + return GetOutPos() < rFrame.GetOutPos(); + } + else + return nContentIdx < rFrame.nContentIdx; + } + else + return pNdIdx->GetIndex() < rFrame.pNdIdx->GetIndex(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlfly.hxx b/sw/source/filter/html/htmlfly.hxx new file mode 100644 index 000000000..32b9d80aa --- /dev/null +++ b/sw/source/filter/html/htmlfly.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_FILTER_HTML_HTMLFLY_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFLY_HXX + +#include <o3tl/sorted_vector.hxx> +#include <o3tl/typed_flags_set.hxx> + +class SdrObject; +class SwFrameFormat; +class SwNodeIndex; +class SwPosFlyFrame; + +// ATTENTION: The values of this enum are used directly in the output table!!! +enum SwHTMLFrameType +{ + HTML_FRMTYPE_TABLE, + HTML_FRMTYPE_TABLE_CAP, + HTML_FRMTYPE_MULTICOL, + HTML_FRMTYPE_EMPTY, + HTML_FRMTYPE_TEXT, + HTML_FRMTYPE_GRF, + HTML_FRMTYPE_PLUGIN, + HTML_FRMTYPE_APPLET, + HTML_FRMTYPE_IFRAME, + HTML_FRMTYPE_OLE, + HTML_FRMTYPE_MARQUEE, + HTML_FRMTYPE_CONTROL, + HTML_FRMTYPE_DRAW, + HTML_FRMTYPE_END +}; + +enum class HtmlOut { + TableNode, + GraphicNode, + OleNode, + Div, + MultiCol, + Spacer, + Control, + AMarquee, + Marquee, + GraphicFrame, + OleGraphic, + Span +}; + +enum class HtmlPosition { + Prefix, + Before, + Inside, + Any +}; + +enum class HtmlContainerFlags { + NONE = 0x00, + Span = 0x01, + Div = 0x02, +}; +namespace o3tl { + template<> struct typed_flags<HtmlContainerFlags> : is_typed_flags<HtmlContainerFlags, 0x03> {}; +} + +const sal_uInt16 MAX_FRMTYPES = HTML_FRMTYPE_END; +const sal_uInt16 MAX_BROWSERS = 4; + +struct AllHtmlFlags { + HtmlOut nOut; + HtmlPosition nPosition; + HtmlContainerFlags nContainer; +}; +extern AllHtmlFlags const aHTMLOutFramePageFlyTable[MAX_FRMTYPES][MAX_BROWSERS]; +extern AllHtmlFlags const aHTMLOutFrameParaFrameTable[MAX_FRMTYPES][MAX_BROWSERS]; +extern AllHtmlFlags const aHTMLOutFrameParaPrtAreaTable[MAX_FRMTYPES][MAX_BROWSERS]; +extern AllHtmlFlags const aHTMLOutFrameParaOtherTable[MAX_FRMTYPES][MAX_BROWSERS]; +extern AllHtmlFlags const aHTMLOutFrameAsCharTable[MAX_FRMTYPES][MAX_BROWSERS]; + +class SwHTMLPosFlyFrame +{ + const SwFrameFormat *pFrameFormat; // the frame + const SdrObject *pSdrObject; // maybe Sdr-Object + SwNodeIndex *pNdIdx; // Node-Index + sal_uInt32 nOrdNum; // from SwPosFlyFrame + sal_Int32 nContentIdx; // its position in content + AllHtmlFlags nAllFlags; + + SwHTMLPosFlyFrame(const SwHTMLPosFlyFrame&) = delete; + SwHTMLPosFlyFrame& operator=(const SwHTMLPosFlyFrame&) = delete; + +public: + + SwHTMLPosFlyFrame( const SwPosFlyFrame& rPosFly, + const SdrObject *pSdrObj, AllHtmlFlags nAllFlags ); + + bool operator<( const SwHTMLPosFlyFrame& ) const; + + const SwFrameFormat& GetFormat() const { return *pFrameFormat; } + const SdrObject* GetSdrObject() const { return pSdrObject; } + const SwNodeIndex& GetNdIndex() const { return *pNdIdx; } + sal_Int32 GetContentIndex() const { return nContentIdx; } + AllHtmlFlags const & GetOutMode() const { return nAllFlags; } + HtmlOut GetOutFn() const { return nAllFlags.nOut; } + HtmlPosition GetOutPos() const { return nAllFlags.nPosition; } +}; + +class SwHTMLPosFlyFrames + : public o3tl::sorted_vector<std::unique_ptr<SwHTMLPosFlyFrame>, + o3tl::less_uniqueptr_to<SwHTMLPosFlyFrame>, + o3tl::find_partialorder_ptrequals> +{}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlflyt.cxx b/sw/source/filter/html/htmlflyt.cxx new file mode 100644 index 000000000..9e4596ebc --- /dev/null +++ b/sw/source/filter/html/htmlflyt.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 "htmlfly.hxx" + +#define TE(t,p,c) { HtmlOut::t, HtmlPosition::p, HtmlContainerFlags::c } + +AllHtmlFlags const aHTMLOutFramePageFlyTable[MAX_FRMTYPES][MAX_BROWSERS] = +{ + { + // text frame with table + TE(TableNode, Before, NONE), // HTML 3.2 + TE(Div, Prefix, NONE), // IE 4 + TE(Div, Prefix, NONE), // SW + TE(Div, Prefix, NONE) // Netscape 4! + }, + { + // text frame with table and headline + TE(TableNode, Before, NONE), // HTML 3.2 + TE(Div, Prefix, NONE), // IE 4 + TE(Div, Prefix, NONE), // SW + TE(Div, Prefix, NONE) // Netscape 4 + }, + { + // column frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Prefix, NONE), // IE 4 + TE(MultiCol, Prefix, NONE), // SW + TE(MultiCol, Prefix, Div) // Netscape 4 + }, + { + // empty text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Div, Prefix, NONE), // IE 4 + TE(Div, Prefix, NONE), // SW + TE(Div, Prefix, NONE) // Netscape 4 + }, + { + // other text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Div, Prefix, NONE), // IE 4 + TE(Div, Prefix, NONE), // SW + TE(Div, Prefix, NONE) // Netscape 4 + }, + { + // graphic node + TE(GraphicNode, Inside, NONE), // HTML 3.2 + TE(GraphicNode, Prefix, NONE), // IE 4 + TE(GraphicNode, Prefix, NONE), // SW + TE(GraphicNode, Prefix, Span) // Netscape 4 + }, + { + // plug-in + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Prefix, NONE), // IE 4 + TE(OleNode, Prefix, NONE), // SW + TE(OleNode, Prefix, Span) // Netscape 4 + }, + { + // applet + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Prefix, NONE), // IE 4 + TE(OleNode, Prefix, NONE), // SW + TE(OleNode, Prefix, Span) // Netscape 4 + }, + { + // floating frame + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleNode, Prefix, NONE), // IE 4 + TE(OleNode, Prefix, NONE), // SW + TE(OleGraphic, Prefix, Span) // Netscape 4 + }, + { + // other OLE objects + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleGraphic, Prefix, NONE), // IE 4 + TE(OleGraphic, Prefix, NONE), // SW + TE(OleGraphic, Prefix, Span) // Netscape 4 + }, + { + // marquee + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(AMarquee, Prefix, NONE), // IE 4 + TE(AMarquee, Prefix, NONE), // SW + TE(GraphicFrame, Prefix, Span) // Netscape 4 + }, + { + // controls + TE(Control, Inside, NONE), // HTML 3.2 + TE(Control, Prefix, NONE), // IE 4 + TE(Control, Prefix, NONE), // SW + // Netscape disables FROM at controls in absolute position span. + TE(Control, Inside, NONE) // Netscape 4 + }, + { + // other character objects + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Prefix, NONE), // IE 4 + TE(GraphicFrame, Prefix, NONE), // SW + TE(GraphicFrame, Prefix, Span) // Netscape 4 + } +}; + +AllHtmlFlags const aHTMLOutFrameParaFrameTable[MAX_FRMTYPES][MAX_BROWSERS] = +{ + { + // text frame with table + TE(TableNode, Before, NONE), // HTML 3.2 + TE(TableNode, Before, NONE), // IE 4 + TE(TableNode, Before, NONE), // SW + TE(TableNode, Before, NONE) // Netscape 4 + }, + { + // text frame with table and headline + TE(TableNode, Before, NONE), // HTML 3.2 + TE(Div, Before, NONE), // IE 4 + TE(Div, Before, NONE), // SW + TE(TableNode, Before, NONE) // Netscape 4 + }, + { + // column frame + TE(GraphicFrame, Before, NONE), // HTML 3.2 + TE(GraphicFrame, Before, NONE), // IE 4 + TE(MultiCol, Before, NONE), // SW + TE(MultiCol, Before, Div) // Netscape 4 + }, + { + // empty text frame + TE(GraphicFrame, Before, NONE), // HTML 3.2 + TE(Div, Before, NONE), // IE 4 + TE(Spacer, Before, NONE), // SW + TE(Spacer, Before, NONE) // Netscape 4 + }, + { + // other text frame + TE(GraphicFrame, Before, NONE), // HTML 3.2 + TE(Div, Before, NONE), // IE 4 + TE(Div, Before, NONE), // SW + TE(Div, Before, NONE) // Netscape 4 + }, + { + // graphic node + TE(GraphicNode, Before, NONE), // HTML 3.2 + TE(GraphicNode, Before, NONE), // IE 4 + TE(GraphicNode, Before, NONE), // SW + TE(GraphicNode, Before, NONE) // Netscape 4 + }, + { + // plug-in + TE(OleNode, Before, NONE), // HTML 3.2 + TE(OleNode, Before, NONE), // IE 4 + TE(OleNode, Before, NONE), // SW + TE(OleNode, Before, NONE) // Netscape 4 + }, + { + // applet + TE(OleNode, Before, NONE), // HTML 3.2 + TE(OleNode, Before, NONE), // IE 4 + TE(OleNode, Before, NONE), // SW + TE(OleNode, Before, NONE) // Netscape 4 + }, + { + // floating frame + TE(OleGraphic, Before, NONE), // HTML 3.2 + TE(OleNode, Before, NONE), // IE 4 + TE(OleNode, Before, NONE), // SW + TE(OleGraphic, Before, NONE) // Netscape 4 + }, + { + // other OLE objects + TE(OleGraphic, Before, NONE), // HTML 3.2 + TE(OleGraphic, Before, NONE), // IE 4 + TE(OleGraphic, Before, NONE), // SW + TE(OleGraphic, Before, NONE) // Netscape 4 + }, + { + // marquee (for Netscape 4 in container, so that + // the marquee appears at the right spot) + TE(GraphicFrame, Before, NONE), // HTML 3.2 + TE(AMarquee, Before, NONE), // IE 4 + TE(AMarquee, Before, NONE), // SW + TE(GraphicFrame, Before, NONE) // Netscape 4 + }, + { + // controls + TE(Control, Inside, NONE), // HTML 3.2 + TE(Control, Before, NONE), // IE 4 + TE(Control, Before, NONE), // SW + // here you could make container out if it (import is missing) + TE(Control, Before, NONE) // Netscape 4 + }, + { + // other character objects + TE(GraphicFrame, Before, NONE), // HTML 3.2 + TE(GraphicFrame, Before, NONE), // IE 4 + TE(GraphicFrame, Before, NONE), // SW + TE(GraphicFrame, Before, NONE) // Netscape 4 + } +}; + +AllHtmlFlags const aHTMLOutFrameParaPrtAreaTable[MAX_FRMTYPES][MAX_BROWSERS] = +{ + { + // text frame with table + TE(TableNode, Inside, NONE), // HTML 3.2 + TE(TableNode, Inside, NONE), // IE 4 + TE(TableNode, Inside, NONE), // SW + TE(TableNode, Inside, NONE) // Netscape 4 + }, + { + // text frame with table and headline + TE(TableNode, Inside, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // column frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(MultiCol, Inside, NONE), // SW + TE(MultiCol, Inside, Span) // Netscape 4 + }, + { + // empty text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Spacer, Inside, NONE), // SW + TE(Spacer, Inside, NONE) // Netscape 4 + }, + { + // other text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // graphic node + TE(GraphicNode, Inside, NONE), // HTML 3.2 + TE(GraphicNode, Inside, NONE), // IE 4 + TE(GraphicNode, Inside, NONE), // SW + TE(GraphicNode, Inside, NONE) // Netscape 4 + }, + { + // plug-in + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, NONE) // Netscape 4 + }, + { + // applet + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, NONE) // Netscape 4 + }, + { + // floating frame + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleGraphic, Inside, NONE) // Netscape 4 + }, + { + // other OLE objects + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleGraphic, Inside, NONE), // IE 4 + TE(OleGraphic, Inside, NONE), // SW + TE(OleGraphic, Inside, NONE) // Netscape 4 + }, + { + // marquee + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(AMarquee, Inside, NONE), // IE 4 + TE(AMarquee, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + }, + { + // controls + TE(Control, Inside, NONE), // HTML 3.2 + TE(Control, Inside, NONE), // IE 4 + TE(Control, Inside, NONE), // SW + // here you could make container out if it (import is missing) + TE(Control, Inside, NONE) // Netscape 4 + }, + { + // other character objects + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + } +}; + +AllHtmlFlags const aHTMLOutFrameParaOtherTable[MAX_FRMTYPES][MAX_BROWSERS] = +{ + { + // text frame with table + TE(TableNode, Before, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // text frame with table and headline + TE(TableNode, Before, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // column frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(MultiCol, Inside, NONE), // SW + TE(MultiCol, Inside, Span) // Netscape 4 + }, + { + // empty text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // other text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(Span, Inside, NONE), // IE 4 + TE(Span, Inside, NONE), // SW + TE(Span, Inside, NONE) // Netscape 4 + }, + { + // graphic node + TE(GraphicNode, Inside, NONE), // HTML 3.2 + TE(GraphicNode, Inside, NONE), // IE 4 + TE(GraphicNode, Inside, NONE), // SW + TE(GraphicNode, Inside, Span) // Netscape 4 + }, + { + // plug-in + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, Span) // Netscape 4 + }, + { + // applet + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, Span) // Netscape 4 + }, + { + // floating frame + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleGraphic, Inside, Span) // Netscape 4 + }, + { + // other OLE objects + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleGraphic, Inside, NONE), // IE 4 + TE(OleGraphic, Inside, NONE), // SW + TE(OleGraphic, Inside, Span) // Netscape 4 + }, + { + // marquee + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(AMarquee, Inside, NONE), // IE 4 + TE(AMarquee, Inside, NONE), // SW + TE(GraphicFrame, Inside, Span) // Netscape 4 + }, + { + // controls + TE(Control, Inside, NONE), // HTML 3.2 + TE(Control, Inside, NONE), // IE 4 + TE(Control, Inside, NONE), // SW + // Netscape disables FROM at controls in absolute position span. + TE(Control, Inside, NONE) // Netscape 4 + }, + { + // other character objects + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, Span) // Netscape 4 + } +}; + +AllHtmlFlags const aHTMLOutFrameAsCharTable[MAX_FRMTYPES][MAX_BROWSERS] = +{ + { + // text frame with table + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + }, + { + // text frame with table and headline + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + }, + { + // column frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(MultiCol, Inside, NONE), // SW + TE(MultiCol, Inside, NONE) // Netscape 4 + }, + { + // empty text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(Spacer, Inside, NONE), // SW + TE(Spacer, Inside, NONE) // Netscape 4 + }, + { + // other text frame + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + }, + { + // graphic node + TE(GraphicNode, Inside, NONE), // HTML 3.2 + TE(GraphicNode, Inside, NONE), // IE 4 + TE(GraphicNode, Inside, NONE), // SW + TE(GraphicNode, Inside, NONE) // Netscape 4 + }, + { + // plug-in + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, NONE) // Netscape 4 + }, + { + // applet + TE(OleNode, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleNode, Inside, NONE) // Netscape 4 + }, + { + // floating frame + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleNode, Inside, NONE), // IE 4 + TE(OleNode, Inside, NONE), // SW + TE(OleGraphic, Inside, NONE) // Netscape 4 + }, + { + // other OLE objects + TE(OleGraphic, Inside, NONE), // HTML 3.2 + TE(OleGraphic, Inside, NONE), // IE 4 + TE(OleGraphic, Inside, NONE), // SW + TE(OleGraphic, Inside, NONE) // Netscape 4 + }, + { + // marquee (can always exported as marquee, because + // the content shows up at the right spot + TE(Marquee, Inside, NONE), // HTML 3.2 + TE(Marquee, Inside, NONE), // IE 4 + TE(Marquee, Inside, NONE), // SW + TE(Marquee, Inside, NONE) // Netscape 4 + }, + { + // controls + TE(Control, Inside, NONE), // HTML 3.2 + TE(Control, Inside, NONE), // IE 4 + TE(Control, Inside, NONE), // SW + TE(Control, Inside, NONE) // Netscape 4 + }, + { + // other character objects + TE(GraphicFrame, Inside, NONE), // HTML 3.2 + TE(GraphicFrame, Inside, NONE), // IE 4 + TE(GraphicFrame, Inside, NONE), // SW + TE(GraphicFrame, Inside, NONE) // Netscape 4 + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlflywriter.cxx b/sw/source/filter/html/htmlflywriter.cxx new file mode 100644 index 000000000..184c91195 --- /dev/null +++ b/sw/source/filter/html/htmlflywriter.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 <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <hintids.hxx> +#include <tools/fract.hxx> +#include <svl/urihelper.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/event.hxx> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmltokn.h> +#include <vcl/imap.hxx> +#include <vcl/imapobj.hxx> +#include <svtools/htmlcfg.hxx> +#include <svtools/HtmlWriter.hxx> +#include <svx/svdouno.hxx> +#include <svx/xoutbmp.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brushitem.hxx> +#include <sal/log.hxx> + +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmturl.hxx> +#include <fmtfsize.hxx> +#include <fmtclds.hxx> +#include <fmtcntnt.hxx> +#include <fmtsrnd.hxx> +#include <fmtinfmt.hxx> +#include <txtinet.hxx> +#include <frmatr.hxx> +#include <grfatr.hxx> +#include <flypos.hxx> +#include <ndgrf.hxx> + +#include <doc.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <swerror.h> +#include <frmfmt.hxx> +#include "wrthtml.hxx" +#include "htmlatr.hxx" +#include "htmlfly.hxx" +#include "htmlreqifreader.hxx" + +using namespace css; + +const HtmlFrmOpts HTML_FRMOPTS_IMG_ALL = + HtmlFrmOpts::Alt | + HtmlFrmOpts::Size | + HtmlFrmOpts::AnySize | + HtmlFrmOpts::Border | + HtmlFrmOpts::Name; +const HtmlFrmOpts HTML_FRMOPTS_IMG_CNTNR = + HTML_FRMOPTS_IMG_ALL | + HtmlFrmOpts::AbsSize; +const HtmlFrmOpts HTML_FRMOPTS_IMG = + HTML_FRMOPTS_IMG_ALL | + HtmlFrmOpts::Align | + HtmlFrmOpts::Space | + HtmlFrmOpts::BrClear; +const HtmlFrmOpts HTML_FRMOPTS_IMG_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSpace; + +const HtmlFrmOpts HTML_FRMOPTS_DIV = + HtmlFrmOpts::Id | + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSize | + HtmlFrmOpts::AnySize | + HtmlFrmOpts::AbsSize | + HtmlFrmOpts::SSpace | + HtmlFrmOpts::SBorder | + HtmlFrmOpts::SBackground | + HtmlFrmOpts::BrClear | + HtmlFrmOpts::Dir; + +const HtmlFrmOpts HTML_FRMOPTS_MULTICOL = + HtmlFrmOpts::Id | + HtmlFrmOpts::Width | + HtmlFrmOpts::AnySize | + HtmlFrmOpts::AbsSize | + HtmlFrmOpts::Dir; + +const HtmlFrmOpts HTML_FRMOPTS_MULTICOL_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSize | + HtmlFrmOpts::SSpace | + HtmlFrmOpts::SBorder| + HtmlFrmOpts::SBackground; + +const HtmlFrmOpts HTML_FRMOPTS_SPACER = + HtmlFrmOpts::Align | + HtmlFrmOpts::Size | + HtmlFrmOpts::AnySize | + HtmlFrmOpts::BrClear | + HtmlFrmOpts::MarginSize | + HtmlFrmOpts::AbsSize; + +const HtmlFrmOpts HTML_FRMOPTS_CNTNR = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSpace | + HtmlFrmOpts::SWidth | + HtmlFrmOpts::AnySize | + HtmlFrmOpts::AbsSize | + HtmlFrmOpts::SPixSize; + +static Writer& OutHTML_FrameFormatTableNode( Writer& rWrt, const SwFrameFormat& rFrameFormat ); +static Writer& OutHTML_FrameFormatAsMulticol( Writer& rWrt, const SwFrameFormat& rFormat, + bool bInCntnr ); +static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& rFormat ); +static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt, + const SwFrameFormat& rFrameFormat, bool bSpan ); +static Writer& OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFormat ); + +static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFormat, + bool bInCntnr ); + +static Writer& OutHTML_FrameFormatAsMarquee( Writer& rWrt, const SwFrameFormat& rFrameFormat, + const SdrObject& rSdrObj ); + +static HTMLOutEvent const aImageEventTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OnImageLoadDone }, + { OOO_STRING_SVTOOLS_HTML_O_SDonabort, OOO_STRING_SVTOOLS_HTML_O_onabort, SvMacroItemId::OnImageLoadCancel }, + { OOO_STRING_SVTOOLS_HTML_O_SDonerror, OOO_STRING_SVTOOLS_HTML_O_onerror, SvMacroItemId::OnImageLoadError }, + { nullptr, nullptr, SvMacroItemId::NONE } +}; + +static HTMLOutEvent const aIMapEventTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver }, + { OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut }, + { nullptr, nullptr, SvMacroItemId::NONE } +}; + +sal_uInt16 SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat, + const SdrObject*& rpSdrObj ) +{ + SwHTMLFrameType eType; + + if( RES_DRAWFRMFMT == rFrameFormat.Which() ) + { + // use an arbitrary draw object as the default value + eType = HTML_FRMTYPE_DRAW; + + const SdrObject *pObj = + SwHTMLWriter::GetMarqueeTextObj( static_cast<const SwDrawFrameFormat &>(rFrameFormat) ); + if( pObj ) + { + // scrolling text + rpSdrObj = pObj; + eType = HTML_FRMTYPE_MARQUEE; + } + else + { + pObj = GetHTMLControl( static_cast<const SwDrawFrameFormat &>(rFrameFormat) ); + + if( pObj ) + { + // Form control + rpSdrObj = pObj; + eType = HTML_FRMTYPE_CONTROL; + } + } + } + else + { + // use a text frame as the default value + eType = HTML_FRMTYPE_TEXT; + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1; + const SwNode* pNd = m_pDoc->GetNodes()[ nStt ]; + + if( pNd->IsGrfNode() ) + { + // graphic node + eType = HTML_FRMTYPE_GRF; + } + else if( pNd->IsOLENode() ) + { + // applet, plugin, floating frame + eType = static_cast<SwHTMLFrameType>(GuessOLENodeFrameType( *pNd )); + } + else + { + sal_uLong nEnd = m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex(); + + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + if( SfxItemState::SET == rItemSet.GetItemState( RES_COL, + true, &pItem ) && + static_cast<const SwFormatCol *>(pItem)->GetNumCols() > 1 ) + { + // frame with columns + eType = HTML_FRMTYPE_MULTICOL; + } + else if( pNd->IsTableNode() ) + { + const SwTableNode *pTableNd = pNd->GetTableNode(); + sal_uLong nTableEnd = pTableNd->EndOfSectionIndex(); + + if( nTableEnd+1 == nEnd ) + { + // table + eType = HTML_FRMTYPE_TABLE; + } + else if( nTableEnd+2 == nEnd ) + { + // table with caption + eType = HTML_FRMTYPE_TABLE_CAP; + } + } + else if( pNd->IsTextNode() ) + { + const SwTextNode *pTextNd = pNd->GetTextNode(); + + bool bEmpty = false; + if( nStt==nEnd-1 && !pTextNd->Len() ) + { + // empty frame? Only if no frame is + // anchored to the text or start node. + bEmpty = true; + if( m_pHTMLPosFlyFrames ) + { + for( auto & pHTMLPosFlyFrame : *m_pHTMLPosFlyFrames ) + { + sal_uLong nIdx = pHTMLPosFlyFrame->GetNdIndex().GetIndex(); + bEmpty = (nIdx != nStt) && (nIdx != nStt-1); + if( !bEmpty || nIdx > nStt ) + break; + } + } + } + if( bEmpty ) + { + std::unique_ptr<SvxBrushItem> aBrush = rFrameFormat.makeBackgroundBrushItem(); + /// background is not empty, if it has a background graphic + /// or its background color is not "no fill"/"auto fill". + if( aBrush && + (GPOS_NONE != aBrush->GetGraphicPos() || + aBrush->GetColor() != COL_TRANSPARENT )) + { + bEmpty = false; + } + } + if( bEmpty ) + { + // empty frame + eType = HTML_FRMTYPE_EMPTY; + } + else if( m_pDoc->GetNodes()[nStt+1]->IsTableNode() ) + { + const SwTableNode *pTableNd = + m_pDoc->GetNodes()[nStt+1]->GetTableNode(); + if( pTableNd->EndOfSectionIndex()+1 == nEnd ) + { + // table with heading + eType = HTML_FRMTYPE_TABLE_CAP; + } + } + } + } + } + + return static_cast< sal_uInt16 >(eType); +} + +void SwHTMLWriter::CollectFlyFrames() +{ + OSL_ENSURE( HTML_CFG_MAX+1 == MAX_BROWSERS, + "number of browser configurations has changed" ); + + SwPosFlyFrames aFlyPos( + m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true)); + + for(const auto& rpItem : aFlyPos) + { + const SwFrameFormat& rFrameFormat = rpItem->GetFormat(); + const SdrObject *pSdrObj = nullptr; + const SwPosition *pAPos; + const SwContentNode *pACNd; + SwHTMLFrameType eType = static_cast<SwHTMLFrameType>(GuessFrameType( rFrameFormat, pSdrObj )); + + AllHtmlFlags nMode; + const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor(); + sal_Int16 eHoriRel = rFrameFormat.GetHoriOrient().GetRelationOrient(); + switch( rAnchor.GetAnchorId() ) + { + case RndStdIds::FLY_AT_PAGE: + case RndStdIds::FLY_AT_FLY: + nMode = aHTMLOutFramePageFlyTable[eType][m_nExportMode]; + break; + + case RndStdIds::FLY_AT_PARA: + // frames that are anchored to a paragraph are only placed + // before the paragraph, if the paragraph has a + // spacing. + if( text::RelOrientation::FRAME == eHoriRel && + (pAPos = rAnchor.GetContentAnchor()) != nullptr && + (pACNd = pAPos->nNode.GetNode().GetContentNode()) != nullptr ) + { + const SvxLRSpaceItem& rLRItem = + static_cast<const SvxLRSpaceItem&>(pACNd->GetAttr(RES_LR_SPACE)); + if( rLRItem.GetTextLeft() || rLRItem.GetRight() ) + { + nMode = aHTMLOutFrameParaFrameTable[eType][m_nExportMode]; + break; + } + } + nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode]; + break; + + case RndStdIds::FLY_AT_CHAR: + if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel ) + nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode]; + else + nMode = aHTMLOutFrameParaOtherTable[eType][m_nExportMode]; + break; + + default: + nMode = aHTMLOutFrameParaPrtAreaTable[eType][m_nExportMode]; + break; + } + + if( !m_pHTMLPosFlyFrames ) + m_pHTMLPosFlyFrames.reset(new SwHTMLPosFlyFrames); + + m_pHTMLPosFlyFrames->insert( std::make_unique<SwHTMLPosFlyFrame>(*rpItem, pSdrObj, nMode) ); + } +} + +bool SwHTMLWriter::OutFlyFrame( sal_uLong nNdIdx, sal_Int32 nContentIdx, HtmlPosition nPos, + HTMLOutContext *pContext ) +{ + bool bFlysLeft = false; // Are there still Flys left at the current node position? + + // OutFlyFrame can be called recursively. Thus, sometimes it is + // necessary to start over after a Fly was returned. + bool bRestart = true; + while( m_pHTMLPosFlyFrames && bRestart ) + { + bFlysLeft = bRestart = false; + + // search for the beginning of the FlyFrames + size_t i {0}; + + for( ; i < m_pHTMLPosFlyFrames->size() && + (*m_pHTMLPosFlyFrames)[i]->GetNdIndex().GetIndex() < nNdIdx; i++ ) + ; + for( ; !bRestart && i < m_pHTMLPosFlyFrames->size() && + (*m_pHTMLPosFlyFrames)[i]->GetNdIndex().GetIndex() == nNdIdx; i++ ) + { + SwHTMLPosFlyFrame *pPosFly = (*m_pHTMLPosFlyFrames)[i].get(); + if( ( HtmlPosition::Any == nPos || + pPosFly->GetOutPos() == nPos ) && + pPosFly->GetContentIndex() == nContentIdx ) + { + // It is important to remove it first, because additional + // elements or the whole array could be deleted on + // deeper recursion levels. + std::unique_ptr<SwHTMLPosFlyFrame> flyHolder = m_pHTMLPosFlyFrames->erase_extract(i); + i--; + if( m_pHTMLPosFlyFrames->empty() ) + { + m_pHTMLPosFlyFrames.reset(); + bRestart = true; // not really, only exit the loop + } + + if( pContext ) + { + HTMLOutFuncs::FlushToAscii(Strm(), *pContext ); + pContext = nullptr; // one time only + } + + OutFrameFormat( pPosFly->GetOutMode(), pPosFly->GetFormat(), + pPosFly->GetSdrObject() ); + switch( pPosFly->GetOutFn() ) + { + case HtmlOut::Div: + case HtmlOut::Span: + case HtmlOut::MultiCol: + case HtmlOut::TableNode: + bRestart = true; // It could become recursive here + break; + default: break; + } + } + else + { + bFlysLeft = true; + } + } + } + + return bFlysLeft; +} + +void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFrameFormat, + const SdrObject *pSdrObject ) +{ + HtmlContainerFlags nCntnrMode = nMode.nContainer; + HtmlOut nOutMode = nMode.nOut; + OString aContainerStr; + if( HtmlContainerFlags::NONE != nCntnrMode ) + { + + if( m_bLFPossible && HtmlContainerFlags::Div == nCntnrMode ) + OutNewLine(); + + OStringBuffer sOut; + aContainerStr = (HtmlContainerFlags::Div == nCntnrMode) + ? OOO_STRING_SVTOOLS_HTML_division + : OOO_STRING_SVTOOLS_HTML_span; + sOut.append('<').append(GetNamespace() + aContainerStr).append(' ') + .append(OOO_STRING_SVTOOLS_HTML_O_class).append("=\"") + .append("sd-abs-pos").append('\"'); + Strm().WriteOString( sOut.makeStringAndClear() ); + + // Output a width for non-draw objects + HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_CNTNR; + + // For frames with columns we can also output the background + if( HtmlOut::MultiCol == nOutMode ) + nFrameFlags |= HtmlFrmOpts::SBackground|HtmlFrmOpts::SBorder; + + if( IsHTMLMode( HTMLMODE_BORDER_NONE ) ) + nFrameFlags |= HtmlFrmOpts::SNoBorder; + OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags, pSdrObject ); + Strm().WriteChar( '>' ); + + if( HtmlContainerFlags::Div == nCntnrMode ) + { + IncIndentLevel(); + m_bLFPossible = true; + } + } + + switch( nOutMode ) + { + case HtmlOut::TableNode: // OK + OSL_ENSURE( aContainerStr.isEmpty(), "Table: Container is not supposed to be here" ); + OutHTML_FrameFormatTableNode( *this, rFrameFormat ); + break; + case HtmlOut::GraphicNode: // OK + OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty() ); + break; + case HtmlOut::OleNode: // OK + OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() ); + break; + case HtmlOut::OleGraphic: // OK + OutHTML_FrameFormatOLENodeGrf( *this, rFrameFormat, !aContainerStr.isEmpty() ); + break; + case HtmlOut::Div: + case HtmlOut::Span: + OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" ); + OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode ); + break; + case HtmlOut::MultiCol: // OK + OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() ); + break; + case HtmlOut::Spacer: // OK + OSL_ENSURE( aContainerStr.isEmpty(), "Spacer: Container is not supposed to be here" ); + OutHTML_FrameFormatAsSpacer( *this, rFrameFormat ); + break; + case HtmlOut::Control: // OK + OutHTML_DrawFrameFormatAsControl( *this, + static_cast<const SwDrawFrameFormat &>(rFrameFormat), dynamic_cast<const SdrUnoObj&>(*pSdrObject), + !aContainerStr.isEmpty() ); + break; + case HtmlOut::AMarquee: + OutHTML_FrameFormatAsMarquee( *this, rFrameFormat, *pSdrObject ); + break; + case HtmlOut::Marquee: + OSL_ENSURE( aContainerStr.isEmpty(), "Marquee: Container is not supposed to be here" ); + OutHTML_DrawFrameFormatAsMarquee( *this, + static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject ); + break; + case HtmlOut::GraphicFrame: + OutHTML_FrameFormatAsImage( *this, rFrameFormat ); + break; + } + + if( HtmlContainerFlags::Div == nCntnrMode ) + { + DecIndentLevel(); + if( m_bLFPossible ) + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false ); + m_bLFPossible = true; + } + else if( HtmlContainerFlags::Span == nCntnrMode ) + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false ); +} + +OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat, + const OUString& rAlternateText, + HtmlFrmOpts nFrameOpts ) +{ + OString sRetEndTags; + OStringBuffer sOut; + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + + // Name + if( (nFrameOpts & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) && + !rFrameFormat.GetName().isEmpty() ) + { + const char *pStr = + (nFrameOpts & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name; + sOut.append(' ').append(pStr). + append("=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), rFrameFormat.GetName(), m_eDestEnc, &m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + // Name + if( nFrameOpts & HtmlFrmOpts::Dir ) + { + SvxFrameDirection nDir = GetHTMLDirection( rItemSet ); + Strm().WriteOString( sOut.makeStringAndClear() ); + OutDirection( nDir ); + } + + // ALT + if( (nFrameOpts & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_alt). + append("=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), rAlternateText, m_eDestEnc, &m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + // ALIGN + const char *pStr = nullptr; + RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId(); + if( (nFrameOpts & HtmlFrmOpts::Align) && + ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) ) + { + // MIB 12.3.98: Wouldn't it be more clever to left-align frames that + // are anchored to a paragraph if necessary, instead of inserting them + // as being anchored to characters? + const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); + if( !(nFrameOpts & HtmlFrmOpts::SAlign) || + text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() || + text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() ) + { + pStr = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient() + ? OOO_STRING_SVTOOLS_HTML_AL_right + : OOO_STRING_SVTOOLS_HTML_AL_left; + } + } + if( (nFrameOpts & HtmlFrmOpts::Align) && !pStr && + ( !(nFrameOpts & HtmlFrmOpts::SAlign) || + (RndStdIds::FLY_AS_CHAR == eAnchorId) ) && + SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, true, &pItem )) + { + switch( static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() ) + { + case text::VertOrientation::LINE_TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_top; break; + case text::VertOrientation::CHAR_TOP: + case text::VertOrientation::BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_texttop; break; // not possible + case text::VertOrientation::LINE_CENTER: + case text::VertOrientation::CHAR_CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break; // not possible + case text::VertOrientation::CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_middle; break; + case text::VertOrientation::LINE_BOTTOM: + case text::VertOrientation::CHAR_BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break; // not possible + case text::VertOrientation::TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_bottom; break; + case text::VertOrientation::NONE: break; + } + } + if( pStr ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).append("=\""). + append(pStr).append("\""); + } + + // HSPACE and VSPACE + Size aTwipSpc( 0, 0 ); + if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) && + SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, true, &pItem )) + { + aTwipSpc.setWidth( + ( static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft() + + static_cast<const SvxLRSpaceItem*>(pItem)->GetRight() ) / 2 ); + m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width(); + } + if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) && + SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE, true, &pItem )) + { + aTwipSpc.setHeight( + ( static_cast<const SvxULSpaceItem*>(pItem)->GetUpper() + + static_cast<const SvxULSpaceItem*>(pItem)->GetLower() ) / 2 ); + m_nDfltTopMargin = m_nDfltBottomMargin = static_cast<sal_uInt16>(aTwipSpc.Height()); + } + + if( (nFrameOpts & HtmlFrmOpts::Space) && + (aTwipSpc.Width() || aTwipSpc.Height()) && + Application::GetDefaultDevice() ) + { + Size aPixelSpc = + Application::GetDefaultDevice()->LogicToPixel( aTwipSpc, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSpc.Width() && aTwipSpc.Width() ) + aPixelSpc.setWidth( 1 ); + if( !aPixelSpc.Height() && aTwipSpc.Height() ) + aPixelSpc.setHeight( 1 ); + + if( aPixelSpc.Width() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_hspace). + append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Width())).append("\""); + } + + if( aPixelSpc.Height() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_vspace). + append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Height())).append("\""); + } + } + + // The spacing must be considered for the size, if the corresponding flag + // is set. + if( nFrameOpts & HtmlFrmOpts::MarginSize ) + { + aTwipSpc.setWidth( aTwipSpc.Width() * -2 ); + aTwipSpc.setHeight( aTwipSpc.Height() * -2 ); + } + else + { + aTwipSpc.setWidth( 0 ); + aTwipSpc.setHeight( 0 ); + } + + if( !(nFrameOpts & HtmlFrmOpts::AbsSize) && + SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem )) + { + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + + aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) ); + aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) ); + aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) ); + aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) ); + } + + // WIDTH and/or HEIGHT + // Output SwFrameSize::Variable/SwFrameSize::Minimum only, if ANYSIZE is set + if( (nFrameOpts & HtmlFrmOpts::Size) && + SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ) && + ( (nFrameOpts & HtmlFrmOpts::AnySize) || + SwFrameSize::Fixed == static_cast<const SwFormatFrameSize *>(pItem)->GetHeightSizeType()) ) + { + const SwFormatFrameSize *pFSItem = static_cast<const SwFormatFrameSize *>(pItem); + sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent(); + sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent(); + + // Size of the object in Twips without margins + Size aTwipSz( (nPercentWidth ? 0 + : pFSItem->GetWidth()-aTwipSpc.Width()), + (nPercentHeight ? 0 + : pFSItem->GetHeight()-aTwipSpc.Height()) ); + + OSL_ENSURE( !aTwipSz.IsEmpty(), "Frame size minus spacing < 0!!!???" ); + if( aTwipSz.Width() < 0 ) + aTwipSz.setWidth( 0 ); + if( aTwipSz.Height() < 0 ) + aTwipSz.setHeight( 0 ); + + Size aPixelSz( 0, 0 ); + if( (aTwipSz.Width() || aTwipSz.Height()) && + Application::GetDefaultDevice() ) + { + aPixelSz = + Application::GetDefaultDevice()->LogicToPixel( aTwipSz, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSz.Width() && aTwipSz.Width() ) + aPixelSz.setWidth( 1 ); + if( !aPixelSz.Height() && aTwipSz.Height() ) + aPixelSz.setHeight( 1 ); + } + + if( (nFrameOpts & HtmlFrmOpts::Width) && + ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width). + append("=\""); + if( nPercentWidth ) + sOut.append(static_cast<sal_Int32>(nPercentWidth)).append('%'); + else + sOut.append(static_cast<sal_Int32>(aPixelSz.Width())); + sOut.append("\""); + } + + if( (nFrameOpts & HtmlFrmOpts::Height) && + ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_height). + append("=\""); + if( nPercentHeight ) + sOut.append(static_cast<sal_Int32>(nPercentHeight)).append('%'); + else + sOut.append(static_cast<sal_Int32>(aPixelSz.Height())); + sOut.append("\""); + } + } + + if (!sOut.isEmpty()) + Strm().WriteOString( sOut.makeStringAndClear() ); + + // Insert wrap for graphics that are anchored to a paragraph as + // <BR CLEAR=...> in the string + if( (nFrameOpts & HtmlFrmOpts::BrClear) && + ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) && + SfxItemState::SET == rItemSet.GetItemState( RES_SURROUND, true, &pItem )) + { + const SwFormatSurround* pSurround = static_cast<const SwFormatSurround*>(pItem); + sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient(); + pStr = nullptr; + css::text::WrapTextMode eSurround = pSurround->GetSurround(); + bool bAnchorOnly = pSurround->IsAnchorOnly(); + switch( eHoriOri ) + { + case text::HoriOrientation::RIGHT: + { + switch( eSurround ) + { + case css::text::WrapTextMode_NONE: + case css::text::WrapTextMode_RIGHT: + pStr = OOO_STRING_SVTOOLS_HTML_AL_right; + break; + case css::text::WrapTextMode_LEFT: + case css::text::WrapTextMode_PARALLEL: + if( bAnchorOnly ) + m_bClearRight = true; + break; + default: + ; + } + } + break; + + default: + // If a frame is centered, it gets left aligned. This + // should be taken into account here, too. + { + switch( eSurround ) + { + case css::text::WrapTextMode_NONE: + case css::text::WrapTextMode_LEFT: + pStr = OOO_STRING_SVTOOLS_HTML_AL_left; + break; + case css::text::WrapTextMode_RIGHT: + case css::text::WrapTextMode_PARALLEL: + if( bAnchorOnly ) + m_bClearLeft = true; + break; + default: + ; + } + } + break; + + } + + if( pStr ) + { + sOut.append('<').append(OOO_STRING_SVTOOLS_HTML_linebreak). + append(' ').append(OOO_STRING_SVTOOLS_HTML_O_clear). + append("=\"").append(pStr).append("\">"); + sRetEndTags = sOut.makeStringAndClear(); + } + } + return sRetEndTags; +} + +void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAlternateText, HtmlFrmOpts nFrameOptions) +{ + bool bReplacement = (nFrameOptions & HtmlFrmOpts::Replacement) || mbReqIF; + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + + // Name + if( (nFrameOptions & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) && + !rFrameFormat.GetName().isEmpty() && !bReplacement) + { + const char* pAttributeName = (nFrameOptions & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name; + aHtml.attribute(pAttributeName, rFrameFormat.GetName()); + } + + // Name + if (nFrameOptions & HtmlFrmOpts::Dir) + { + SvxFrameDirection nCurrentDirection = GetHTMLDirection(rItemSet); + OString sDirection = convertDirection(nCurrentDirection); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_dir, sDirection); + } + + // alt + if( (nFrameOptions & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() && !bReplacement ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_alt, rAlternateText); + } + + // align + const char* pAlignString = nullptr; + RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId(); + if( (nFrameOptions & HtmlFrmOpts::Align) && + ((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) && !bReplacement) + { + const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); + if( !(nFrameOptions & HtmlFrmOpts::SAlign) || + text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() || + text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() ) + { + pAlignString = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient() + ? OOO_STRING_SVTOOLS_HTML_AL_right + : OOO_STRING_SVTOOLS_HTML_AL_left; + } + } + if( (nFrameOptions & HtmlFrmOpts::Align) && !pAlignString && + ( !(nFrameOptions & HtmlFrmOpts::SAlign) || + (RndStdIds::FLY_AS_CHAR == eAnchorId) ) && + SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, true, &pItem )) + { + switch( static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient() ) + { + case text::VertOrientation::LINE_TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_top; break; + case text::VertOrientation::CHAR_TOP: + case text::VertOrientation::BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_texttop; break; + case text::VertOrientation::LINE_CENTER: + case text::VertOrientation::CHAR_CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break; + case text::VertOrientation::CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_middle; break; + case text::VertOrientation::LINE_BOTTOM: + case text::VertOrientation::CHAR_BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break; + case text::VertOrientation::TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_bottom; break; + case text::VertOrientation::NONE: break; + } + } + if (pAlignString && !bReplacement) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, pAlignString); + } + + // hspace and vspace + Size aTwipSpc( 0, 0 ); + if( (nFrameOptions & (HtmlFrmOpts::Space | HtmlFrmOpts::MarginSize)) && + SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE, true, &pItem )) + { + aTwipSpc.setWidth( + ( static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft() + + static_cast<const SvxLRSpaceItem*>(pItem)->GetRight() ) / 2 ); + m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width(); + } + if( (nFrameOptions & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) && + SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE, true, &pItem )) + { + aTwipSpc.setHeight( + ( static_cast<const SvxULSpaceItem*>(pItem)->GetUpper() + + static_cast<const SvxULSpaceItem*>(pItem)->GetLower() ) / 2 ); + m_nDfltTopMargin = m_nDfltBottomMargin = static_cast<sal_uInt16>(aTwipSpc.Height()); + } + + if( (nFrameOptions & HtmlFrmOpts::Space) && + (aTwipSpc.Width() || aTwipSpc.Height()) && + Application::GetDefaultDevice() ) + { + Size aPixelSpc = + Application::GetDefaultDevice()->LogicToPixel( aTwipSpc, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSpc.Width() && aTwipSpc.Width() ) + aPixelSpc.setWidth( 1 ); + if( !aPixelSpc.Height() && aTwipSpc.Height() ) + aPixelSpc.setHeight( 1 ); + + if (aPixelSpc.Width()) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_hspace, static_cast<sal_Int32>(aPixelSpc.Width())); + } + + if (aPixelSpc.Height()) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_vspace, static_cast<sal_Int32>(aPixelSpc.Height())); + } + } + + // The spacing must be considered for the size, if the corresponding flag + // is set. + if( nFrameOptions & HtmlFrmOpts::MarginSize ) + { + aTwipSpc.setWidth( aTwipSpc.Width() * -2 ); + aTwipSpc.setHeight( aTwipSpc.Height() * -2 ); + } + else + { + aTwipSpc.setWidth( 0 ); + aTwipSpc.setHeight( 0 ); + } + + if( !(nFrameOptions & HtmlFrmOpts::AbsSize) && + SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem )) + { + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + + aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) ); + aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) ); + aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) ); + aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) ); + } + + // "width" and/or "height" + // Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set + if( (nFrameOptions & HtmlFrmOpts::Size) && + SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem ) && + ( (nFrameOptions & HtmlFrmOpts::AnySize) || + SwFrameSize::Fixed == static_cast<const SwFormatFrameSize *>(pItem)->GetHeightSizeType()) ) + { + const SwFormatFrameSize *pFSItem = static_cast<const SwFormatFrameSize *>(pItem); + sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent(); + sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent(); + + // Size of the object in Twips without margins + Size aTwipSz( (nPercentWidth ? 0 + : pFSItem->GetWidth()-aTwipSpc.Width()), + (nPercentHeight ? 0 + : pFSItem->GetHeight()-aTwipSpc.Height()) ); + + OSL_ENSURE( !aTwipSz.IsEmpty(), "Frame size minus spacing < 0!!!???" ); + if( aTwipSz.Width() < 0 ) + aTwipSz.setWidth( 0 ); + if( aTwipSz.Height() < 0 ) + aTwipSz.setHeight( 0 ); + + Size aPixelSz( 0, 0 ); + if( (aTwipSz.Width() || aTwipSz.Height()) && + Application::GetDefaultDevice() ) + { + aPixelSz = + Application::GetDefaultDevice()->LogicToPixel( aTwipSz, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSz.Width() && aTwipSz.Width() ) + aPixelSz.setWidth( 1 ); + if( !aPixelSz.Height() && aTwipSz.Height() ) + aPixelSz.setHeight( 1 ); + } + + if( (nFrameOptions & HtmlFrmOpts::Width) && + ((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) ) + { + OString sWidth; + if (nPercentWidth) + sWidth = OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%"; + else + sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width())); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth); + } + + if( (nFrameOptions & HtmlFrmOpts::Height) && + ((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) ) + { + OString sHeight; + if (nPercentHeight) + sHeight = OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%"; + else + sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height())); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_height, sHeight); + } + } + + // Insert wrap for graphics that are anchored to a paragraph as + // <BR CLEAR=...> in the string + + if( (nFrameOptions & HtmlFrmOpts::BrClear) && + ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) && + SfxItemState::SET == rItemSet.GetItemState( RES_SURROUND, true, &pItem )) + { + const char* pSurroundString = nullptr; + + const SwFormatSurround* pSurround = static_cast<const SwFormatSurround*>(pItem); + sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient(); + css::text::WrapTextMode eSurround = pSurround->GetSurround(); + bool bAnchorOnly = pSurround->IsAnchorOnly(); + switch( eHoriOri ) + { + case text::HoriOrientation::RIGHT: + { + switch( eSurround ) + { + case css::text::WrapTextMode_NONE: + case css::text::WrapTextMode_RIGHT: + pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_right; + break; + case css::text::WrapTextMode_LEFT: + case css::text::WrapTextMode_PARALLEL: + if( bAnchorOnly ) + m_bClearRight = true; + break; + default: + ; + } + } + break; + + default: + // If a frame is centered, it gets left aligned. This + // should be taken into account here, too. + { + switch( eSurround ) + { + case css::text::WrapTextMode_NONE: + case css::text::WrapTextMode_LEFT: + pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_left; + break; + case css::text::WrapTextMode_RIGHT: + case css::text::WrapTextMode_PARALLEL: + if( bAnchorOnly ) + m_bClearLeft = true; + break; + default: + break; + } + } + break; + } + + if (pSurroundString) + { + aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pSurroundString); + aHtml.end(); + } + } +} + +namespace +{ + +OUString lclWriteOutImap(SwHTMLWriter& rHTMLWrt, const SfxItemSet& rItemSet, const SwFrameFormat& rFrameFormat, + const Size& rRealSize, const ImageMap* pAltImgMap, const SwFormatURL*& pURLItem) +{ + OUString aIMapName; + + const SfxPoolItem* pItem; + + // Only consider the URL attribute if no ImageMap was supplied + if (!pAltImgMap && SfxItemState::SET == rItemSet.GetItemState( RES_URL, true, &pItem)) + { + pURLItem = static_cast<const SwFormatURL*>( pItem); + } + + // write ImageMap + const ImageMap* pIMap = pAltImgMap; + if( !pIMap && pURLItem ) + { + pIMap = pURLItem->GetMap(); + } + + if (pIMap) + { + // make the name unique + aIMapName = pIMap->GetName(); + OUString aNameBase; + if (!aIMapName.isEmpty()) + aNameBase = aIMapName; + else + aNameBase = OOO_STRING_SVTOOLS_HTML_map; + + if (aIMapName.isEmpty()) + aIMapName = aNameBase + OUString::number(rHTMLWrt.m_nImgMapCnt); + + bool bFound; + do + { + bFound = false; + for (const OUString & rImgMapName : rHTMLWrt.m_aImgMapNames) + { + // TODO: Unicode: Comparison is case insensitive for ASCII + // characters only now! + if (aIMapName.equalsIgnoreAsciiCase(rImgMapName)) + { + bFound = true; + break; + } + } + if (bFound) + { + rHTMLWrt.m_nImgMapCnt++; + aIMapName = aNameBase + OUString::number( rHTMLWrt.m_nImgMapCnt ); + } + } while (bFound); + + bool bScale = false; + Fraction aScaleX(1, 1); + Fraction aScaleY(1, 1); + + const SwFormatFrameSize& rFrameSize = rFrameFormat.GetFrameSize(); + const SvxBoxItem& rBox = rFrameFormat.GetBox(); + + if (!rFrameSize.GetWidthPercent() && rRealSize.Width()) + { + SwTwips nWidth = rFrameSize.GetWidth(); + nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT); + + OSL_ENSURE( nWidth > 0, "Are there any graphics that are 0 twip wide!?" ); + if (nWidth <= 0) // should not happen + nWidth = 1; + + if (rRealSize.Width() != nWidth) + { + aScaleX = Fraction(nWidth, rRealSize.Width()); + bScale = true; + } + } + + if (!rFrameSize.GetHeightPercent() && rRealSize.Height()) + { + SwTwips nHeight = rFrameSize.GetHeight(); + + nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM); + + OSL_ENSURE( nHeight > 0, "Are there any graphics that are 0 twip high!?" ); + if (nHeight <= 0) + nHeight = 1; + + if (rRealSize.Height() != nHeight) + { + aScaleY = Fraction(nHeight, rRealSize.Height()); + bScale = true; + } + } + + rHTMLWrt.m_aImgMapNames.push_back(aIMapName); + + OString aIndMap, aIndArea; + const char *pIndArea = nullptr, *pIndMap = nullptr; + + if (rHTMLWrt.m_bLFPossible) + { + rHTMLWrt.OutNewLine( true ); + aIndMap = rHTMLWrt.GetIndentString(); + aIndArea = rHTMLWrt.GetIndentString(1); + pIndArea = aIndArea.getStr(); + pIndMap = aIndMap.getStr(); + } + + if (bScale) + { + ImageMap aScaledIMap(*pIMap); + aScaledIMap.Scale(aScaleX, aScaleY); + HTMLOutFuncs::Out_ImageMap( rHTMLWrt.Strm(), rHTMLWrt.GetBaseURL(), aScaledIMap, aIMapName, + aIMapEventTable, + rHTMLWrt.m_bCfgStarBasic, + SAL_NEWLINE_STRING, pIndArea, pIndMap, + rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters ); + } + else + { + HTMLOutFuncs::Out_ImageMap( rHTMLWrt.Strm(), rHTMLWrt.GetBaseURL(), *pIMap, aIMapName, + aIMapEventTable, + rHTMLWrt.m_bCfgStarBasic, + SAL_NEWLINE_STRING, pIndArea, pIndMap, + rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters ); + } + } + return aIMapName; +} + +} + +Writer& OutHTML_Image( Writer& rWrt, const SwFrameFormat &rFrameFormat, + const OUString& rGraphicURL, + Graphic const & rGraphic, const OUString& rAlternateText, + const Size &rRealSize, HtmlFrmOpts nFrameOpts, + const char *pMarkType, + const ImageMap *pAltImgMap, + const OUString& rMimeType ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + // <object data="..."> instead of <img src="..."> + bool bReplacement = (nFrameOpts & HtmlFrmOpts::Replacement) || rHTMLWrt.mbReqIF; + + if (rHTMLWrt.mbSkipImages) + return rHTMLWrt; + + // if necessary, temporarily close an open attribute + if( !rHTMLWrt.m_aINetFormats.empty() ) + { + SwFormatINetFormat* pINetFormat = rHTMLWrt.m_aINetFormats.back(); + OutHTML_INetFormat( rWrt, *pINetFormat, false ); + } + + OUString aGraphicURL( rGraphicURL ); + if( !rHTMLWrt.mbEmbedImages && !HTMLOutFuncs::PrivateURLToInternalImg(aGraphicURL) && !rHTMLWrt.mpTempBaseURL ) + aGraphicURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aGraphicURL); + + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + + const SwFormatURL* pURLItem = nullptr; + OUString aIMapName = lclWriteOutImap(rHTMLWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem); + + // put img into new line + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + + HtmlWriter aHtml(rWrt.Strm(), rHTMLWrt.maNamespace); + + // <a name=...></a>...<img ...> + if( pMarkType && !rFrameFormat.GetName().isEmpty() ) + { + rHTMLWrt.OutImplicitMark( rFrameFormat.GetName(), pMarkType ); + } + + // URL -> <a>...<img ... >...</a> + const SvxMacroItem *pMacItem = nullptr; + if (SfxItemState::SET == rItemSet.GetItemState(RES_FRMMACRO, true, &pItem)) + { + pMacItem = static_cast<const SvxMacroItem *>(pItem); + } + + if (pURLItem || pMacItem) + { + OUString aMapURL; + OUString aName; + OUString aTarget; + + if(pURLItem) + { + aMapURL = pURLItem->GetURL(); + aName = pURLItem->GetName(); + aTarget = pURLItem->GetTargetFrameName(); + } + + bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty(); + + if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents ) + { + aHtml.start(OOO_STRING_SVTOOLS_HTML_anchor); + + // Output "href" element if a link or macro exists + if( !aMapURL.isEmpty() || bEvents ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, OUStringToOString(rHTMLWrt.convertHyperlinkHRefValue(aMapURL), RTL_TEXTENCODING_UTF8)); + } + + if( !aName.isEmpty() ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, OUStringToOString(aName, RTL_TEXTENCODING_UTF8)); + } + + if( !aTarget.isEmpty() ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, OUStringToOString(aTarget, RTL_TEXTENCODING_UTF8)); + } + + if( pMacItem ) + { + const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable(); + if (!rMacTable.empty()) + { + HtmlWriterHelper::applyEvents(aHtml, rMacTable, aAnchorEventTable, rHTMLWrt.m_bCfgStarBasic); + } + } + } + } + + // <font color = ...>...<img ... >...</font> + sal_uInt16 nBorderWidth = 0; + if( (nFrameOpts & HtmlFrmOpts::Border) && + SfxItemState::SET == rItemSet.GetItemState( RES_BOX, true, &pItem )) + { + Size aTwipBorder( 0, 0 ); + const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem); + + const ::editeng::SvxBorderLine *pColBorderLine = nullptr; + const ::editeng::SvxBorderLine *pBorderLine = pBoxItem->GetLeft(); + if( pBorderLine ) + { + pColBorderLine = pBorderLine; + aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() ); + } + + pBorderLine = pBoxItem->GetRight(); + if( pBorderLine ) + { + pColBorderLine = pBorderLine; + aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() ); + } + + pBorderLine = pBoxItem->GetTop(); + if( pBorderLine ) + { + pColBorderLine = pBorderLine; + aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() ); + } + + pBorderLine = pBoxItem->GetBottom(); + if( pBorderLine ) + { + pColBorderLine = pBorderLine; + aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() ); + } + + aTwipBorder.setWidth( aTwipBorder.Width() / 2 ); + aTwipBorder.setHeight( aTwipBorder.Height() / 2 ); + + if( (aTwipBorder.Width() || aTwipBorder.Height()) && + Application::GetDefaultDevice() ) + { + Size aPixelBorder = + Application::GetDefaultDevice()->LogicToPixel( aTwipBorder, + MapMode(MapUnit::MapTwip) ); + if( !aPixelBorder.Width() && aTwipBorder.Width() ) + aPixelBorder.setWidth( 1 ); + if( !aPixelBorder.Height() && aTwipBorder.Height() ) + aPixelBorder.setHeight( 1 ); + + if( aPixelBorder.Width() ) + aPixelBorder.setHeight( 0 ); + + nBorderWidth = + static_cast<sal_uInt16>(aPixelBorder.Width() + aPixelBorder.Height()); + } + + if( pColBorderLine ) + { + aHtml.start(OOO_STRING_SVTOOLS_HTML_font); + HtmlWriterHelper::applyColor(aHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor()); + } + } + + OString aTag(OOO_STRING_SVTOOLS_HTML_image); + if (bReplacement) + // Write replacement graphic of OLE object as <object>. + aTag = OOO_STRING_SVTOOLS_HTML_object; + aHtml.start(aTag); + + OStringBuffer sBuffer; + if(rHTMLWrt.mbEmbedImages) + { + OUString aGraphicInBase64; + if (XOutBitmap::GraphicToBase64(rGraphic, aGraphicInBase64)) + { + sBuffer.append(OOO_STRING_SVTOOLS_HTML_O_data); + sBuffer.append(":"); + sBuffer.append(OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8)); + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer.makeStringAndClear().getStr()); + } + else + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + } + else + { + sBuffer.append(OUStringToOString(aGraphicURL, RTL_TEXTENCODING_UTF8)); + OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src); + if (bReplacement) + aAttribute = OOO_STRING_SVTOOLS_HTML_O_data; + aHtml.attribute(aAttribute, sBuffer.makeStringAndClear().getStr()); + } + + if (bReplacement) + { + // Handle XHTML type attribute for OLE replacement images. + if (!rMimeType.isEmpty()) + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType.toUtf8()); + } + + // Events + if (SfxItemState::SET == rItemSet.GetItemState(RES_FRMMACRO, true, &pItem)) + { + const SvxMacroTableDtor& rMacTable = static_cast<const SvxMacroItem *>(pItem)->GetMacroTable(); + if (!rMacTable.empty()) + { + HtmlWriterHelper::applyEvents(aHtml, rMacTable, aImageEventTable, rHTMLWrt.m_bCfgStarBasic); + } + } + + // alt, align, width, height, hspace, vspace + rHTMLWrt.writeFrameFormatOptions(aHtml, rFrameFormat, rAlternateText, nFrameOpts); + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) ) + rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts ); + + if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth); + } + + if( pURLItem && pURLItem->IsServerMap() ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap); + } + + if( !aIMapName.isEmpty() ) + { + aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, "#" + aIMapName); + } + + if (bReplacement) + { + // XHTML object replacement image's alternate text doesn't use the + // "alt" attribute. + if (rAlternateText.isEmpty()) + // Empty alternate text is not valid. + aHtml.characters(" "); + else + aHtml.characters(rAlternateText.toUtf8()); + } + + aHtml.flushStack(); + + if( !rHTMLWrt.m_aINetFormats.empty() ) + { + // There is still an attribute on the stack that has to be reopened + SwFormatINetFormat *pINetFormat = rHTMLWrt.m_aINetFormats.back(); + OutHTML_INetFormat( rWrt, *pINetFormat, true ); + } + + return rHTMLWrt; +} + +Writer& OutHTML_BulletImage( Writer& rWrt, + const char *pTag, + const SvxBrushItem* pBrush, + const OUString &rGraphicURL) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OUString aGraphicInBase64; + OUString aLink; + if( pBrush ) + { + aLink = pBrush->GetGraphicLink(); + if(rHTMLWrt.mbEmbedImages || aLink.isEmpty()) + { + const Graphic* pGrf = pBrush->GetGraphic(); + if( pGrf ) + { + if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) ) + { + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + } + } + } + else if(!aLink.isEmpty()) + { + if( rHTMLWrt.m_bCfgCpyLinkedGrfs ) + { + rHTMLWrt.CopyLocalFileToINet( aLink ); + } + + } + } + else if(!rHTMLWrt.mbEmbedImages) + { + aLink = rGraphicURL; + } + if(!aLink.isEmpty()) + { + if( !HTMLOutFuncs::PrivateURLToInternalImg(aLink) ) + aLink = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), aLink); + } + + OStringBuffer sOut; + if( pTag ) + sOut.append('<').append(pTag); + + sOut.append(' '); + sOut.append(OOO_STRING_SVTOOLS_HTML_O_style).append("=\""); + if(!aLink.isEmpty()) + { + sOut.append(OOO_STRING_SVTOOLS_HTML_O_src).append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aLink, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + else + { + sOut.append("list-style-image: ").append("url("). + append(OOO_STRING_SVTOOLS_HTML_O_data).append(":"); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aGraphicInBase64, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append(");"); + } + sOut.append('\"'); + + if (pTag) + sOut.append('>'); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + return rWrt; +} + +static Writer& OutHTML_FrameFormatTableNode( Writer& rWrt, const SwFrameFormat& rFrameFormat ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1; + sal_uLong nEnd = rHTMLWrt.m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex(); + + OUString aCaption; + bool bTopCaption = false; + + // Not const, because GetTable won't be const sometime later + SwNode *pNd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ]; + SwTableNode *pTableNd = pNd->GetTableNode(); + const SwTextNode *pTextNd = pNd->GetTextNode(); + if( !pTableNd && pTextNd ) + { + // Table with heading + bTopCaption = true; + pTableNd = rHTMLWrt.m_pDoc->GetNodes()[nStt+1]->GetTableNode(); + } + OSL_ENSURE( pTableNd, "Frame does not contain a table" ); + if( pTableNd ) + { + sal_uLong nTableEnd = pTableNd->EndOfSectionIndex(); + OSL_ENSURE( nTableEnd == nEnd - 1 || + (nTableEnd == nEnd - 2 && !bTopCaption), + "Invalid frame content for a table" ); + + if( nTableEnd == nEnd - 2 ) + pTextNd = rHTMLWrt.m_pDoc->GetNodes()[nTableEnd+1]->GetTextNode(); + } + if( pTextNd ) + aCaption = pTextNd->GetText(); + + if( pTableNd ) + { + HTMLSaveData aSaveData( rHTMLWrt, pTableNd->GetIndex()+1, + pTableNd->EndOfSectionIndex(), + true, &rFrameFormat ); + rHTMLWrt.m_bOutFlyFrame = true; + OutHTML_SwTableNode( rHTMLWrt, *pTableNd, &rFrameFormat, &aCaption, + bTopCaption ); + } + + return rWrt; +} + +static Writer & OutHTML_FrameFormatAsMulticol( Writer& rWrt, + const SwFrameFormat& rFrameFormat, + bool bInCntnr ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE ); + + // Close the current <DL>! + rHTMLWrt.OutAndSetDefList( 0 ); + + // output as Multicol + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + + OStringBuffer sOut; + sOut.append('<').append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol); + + const SwFormatCol& rFormatCol = rFrameFormat.GetCol(); + + // output the number of columns as COLS + sal_uInt16 nCols = rFormatCol.GetNumCols(); + if( nCols ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_cols). + append("=\"").append(static_cast<sal_Int32>(nCols)).append("\""); + } + + // the Gutter width (minimum value) as GUTTER + sal_uInt16 nGutter = rFormatCol.GetGutterWidth( true ); + if( nGutter!=USHRT_MAX ) + { + if( nGutter && Application::GetDefaultDevice() ) + { + nGutter = static_cast<sal_uInt16>(Application::GetDefaultDevice() + ->LogicToPixel( Size(nGutter,0), + MapMode(MapUnit::MapTwip) ).Width()); + } + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_gutter). + append("=\"").append(static_cast<sal_Int32>(nGutter)).append("\""); + } + + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + // WIDTH + HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MULTICOL; + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr ) + nFrameFlags |= HTML_FRMOPTS_MULTICOL_CSS1; + rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags); + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr ) + rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags ); + + rWrt.Strm().WriteChar( '>' ); + + rHTMLWrt.m_bLFPossible = true; + rHTMLWrt.IncIndentLevel(); // indent the content of Multicol + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex(); + const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode(); + OSL_ENSURE( pSttNd, "Where is the start node" ); + + { + // in a block, so that the old state can be restored in time + // before the end + HTMLSaveData aSaveData( rHTMLWrt, nStt+1, + pSttNd->EndOfSectionIndex(), + true, &rFrameFormat ); + rHTMLWrt.m_bOutFlyFrame = true; + rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() ); + } + + rHTMLWrt.DecIndentLevel(); // indent the content of Multicol; + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol, false ); + rHTMLWrt.m_bLFPossible = true; + + return rWrt; +} + +static Writer& OutHTML_FrameFormatAsSpacer( Writer& rWrt, const SwFrameFormat& rFrameFormat ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // if possible, output a line break before the graphic + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + + OString sOut = + "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_spacer " " + OOO_STRING_SVTOOLS_HTML_O_type "=\"" + OOO_STRING_SVTOOLS_HTML_SPTYPE_block "\""; + rWrt.Strm().WriteOString( sOut ); + + // ALIGN, WIDTH, HEIGHT + OString aEndTags = rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), HTML_FRMOPTS_SPACER); + + rWrt.Strm().WriteChar( '>' ); + if( !aEndTags.isEmpty() ) + rWrt.Strm().WriteOString( aEndTags ); + + return rWrt; +} + +static Writer& OutHTML_FrameFormatAsDivOrSpan( Writer& rWrt, + const SwFrameFormat& rFrameFormat, bool bSpan) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OString aTag; + if( !bSpan ) + { + rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE ); + + // Close the current <DL>! + rHTMLWrt.OutAndSetDefList( 0 ); + aTag = OOO_STRING_SVTOOLS_HTML_division; + } + else + aTag = OOO_STRING_SVTOOLS_HTML_span; + + // output as DIV + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + + OStringBuffer sOut; + sOut.append('<').append(rHTMLWrt.GetNamespace() + aTag); + + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_DIV; + if( rHTMLWrt.IsHTMLMode( HTMLMODE_BORDER_NONE ) ) + nFrameFlags |= HtmlFrmOpts::SNoBorder; + OString aEndTags = rHTMLWrt.OutFrameFormatOptions(rFrameFormat, OUString(), nFrameFlags); + rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags ); + rWrt.Strm().WriteChar( '>' ); + + rHTMLWrt.IncIndentLevel(); // indent the content + rHTMLWrt.m_bLFPossible = true; + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex(); + + // Output frame-anchored frames that are anchored to the start node + rHTMLWrt.OutFlyFrame( nStt, 0, HtmlPosition::Any ); + + const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode(); + OSL_ENSURE( pSttNd, "Where is the start node" ); + + { + // in a block, so that the old state can be restored in time + // before the end + HTMLSaveData aSaveData( rHTMLWrt, nStt+1, + pSttNd->EndOfSectionIndex(), + true, &rFrameFormat ); + rHTMLWrt.m_bOutFlyFrame = true; + rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() ); + } + + rHTMLWrt.DecIndentLevel(); // indent the content of Multicol; + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + aTag, false ); + + if( !aEndTags.isEmpty() ) + rWrt.Strm().WriteOString( aEndTags ); + + return rWrt; +} + +static Writer & OutHTML_FrameFormatAsImage( Writer& rWrt, const SwFrameFormat& rFrameFormat ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if (rHTMLWrt.mbSkipImages) + return rWrt; + + ImageMap aIMap; + Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap ) ); + Size aSz( 0, 0 ); + OUString GraphicURL; + if(!rHTMLWrt.mbEmbedImages) + { + if( rHTMLWrt.GetOrigFileName() ) + GraphicURL = *rHTMLWrt.GetOrigFileName(); + if( aGraphic.GetType() == GraphicType::NONE || + XOutBitmap::WriteGraphic( aGraphic, GraphicURL, + "JPG", + (XOutFlags::UseGifIfPossible| + XOutFlags::UseNativeIfPossible) ) != ERRCODE_NONE ) + { + // empty or incorrect, because there is nothing to output + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + return rWrt; + } + + GraphicURL = URIHelper::SmartRel2Abs( + INetURLObject(rWrt.GetBaseURL()), GraphicURL, + URIHelper::GetMaybeFileHdl() ); + + } + OutHTML_Image( rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz, + HtmlFrmOpts::GenImgMask, "frame", + aIMap.GetIMapObjectCount() ? &aIMap : nullptr ); + + return rWrt; +} + +static Writer& OutHTML_FrameFormatGrfNode( Writer& rWrt, const SwFrameFormat& rFrameFormat, + bool bInCntnr ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + if (rHTMLWrt.mbSkipImages) + return rWrt; + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1; + SwGrfNode *pGrfNd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ]->GetGrfNode(); + OSL_ENSURE( pGrfNd, "Grf node expected" ); + if( !pGrfNd ) + return rWrt; + + HtmlFrmOpts nFrameFlags = bInCntnr ? HTML_FRMOPTS_IMG_CNTNR : HTML_FRMOPTS_IMG; + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr ) + nFrameFlags |= HTML_FRMOPTS_IMG_CSS1; + + Graphic aGraphic = pGrfNd->GetGraphic(); + OUString aGraphicURL; + OUString aMimeType; + if(!rHTMLWrt.mbEmbedImages) + { + const SwMirrorGrf& rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf(); + + if( !pGrfNd->IsLinkedFile() || MirrorGraph::Dont != rMirror.GetValue() ) + { + // create a (mirrored) jpeg file + if( rHTMLWrt.GetOrigFileName() ) + aGraphicURL = *rHTMLWrt.GetOrigFileName(); + else + aGraphicURL = rHTMLWrt.GetBaseURL(); + pGrfNd->GetGrf( true ); + + XOutFlags nFlags = XOutFlags::UseGifIfSensible | + XOutFlags::UseNativeIfPossible; + switch( rMirror.GetValue() ) + { + case MirrorGraph::Vertical: nFlags = XOutFlags::MirrorHorz; break; + case MirrorGraph::Horizontal: nFlags = XOutFlags::MirrorVert; break; + case MirrorGraph::Both: + nFlags = XOutFlags::MirrorVert | XOutFlags::MirrorHorz; + break; + default: break; + } + + Size aMM100Size; + const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize(); + aMM100Size = OutputDevice::LogicToLogic( rSize.GetSize(), + MapMode( MapUnit::MapTwip ), MapMode( MapUnit::Map100thMM )); + + OUString aFilterName(""); + + if (rHTMLWrt.mbReqIF) + { + // Writing image without fallback PNG in ReqIF mode: force PNG + // output. + aFilterName = "PNG"; + nFlags &= ~XOutFlags::UseNativeIfPossible; + nFlags &= ~XOutFlags::UseGifIfSensible; + aMimeType = "image/png"; + } + + const Graphic& rGraphic = pGrfNd->GetGrf(); + + // So that Graphic::IsTransparent() can report true. + if (!rGraphic.isAvailable()) + const_cast<Graphic&>(rGraphic).makeAvailable(); + + ErrCode nErr = XOutBitmap::WriteGraphic( rGraphic, aGraphicURL, + aFilterName, nFlags, &aMM100Size ); + if( nErr ) + { + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + return rWrt; + } + aGraphicURL = URIHelper::SmartRel2Abs( + INetURLObject(rWrt.GetBaseURL()), aGraphicURL, + URIHelper::GetMaybeFileHdl() ); + } + else + { + pGrfNd->GetFileFilterNms( &aGraphicURL, nullptr ); + if( rHTMLWrt.m_bCfgCpyLinkedGrfs ) + rWrt.CopyLocalFileToINet( aGraphicURL ); + } + + } + uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY); + if (xGraphic.is() && aMimeType.isEmpty()) + xGraphic->getPropertyValue("MimeType") >>= aMimeType; + + if (rHTMLWrt.mbReqIF) + { + // Write the original image as an RTF fragment. + OUString aFileName; + if (rHTMLWrt.GetOrigFileName()) + aFileName = *rHTMLWrt.GetOrigFileName(); + INetURLObject aURL(aFileName); + OUString aName = aURL.getBase() + "_" + + aURL.getExtension() + "_" + + OUString::number(aGraphic.GetChecksum(), 16); + aURL.setBase(aName); + aURL.setExtension("ole"); + aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + SvFileStream aOutStream(aFileName, StreamMode::WRITE); + if (!SwReqIfReader::WrapGraphicInRtf(aGraphic, pGrfNd->GetTwipSize(), aOutStream)) + SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed"); + + // Refer to this data. + aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName); + rWrt.Strm().WriteOString("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object); + rWrt.Strm().WriteOString(" data=\"" + aFileName.toUtf8() + "\""); + rWrt.Strm().WriteOString(" type=\"text/rtf\""); + rWrt.Strm().WriteOString(">"); + rHTMLWrt.OutNewLine(); + } + + OutHTML_Image( rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(), + pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType ); + + if (rHTMLWrt.mbReqIF) + rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"); + + return rWrt; +} + +static Writer& OutHTML_FrameFormatAsMarquee( Writer& rWrt, const SwFrameFormat& rFrameFormat, + const SdrObject& rSdrObj ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // get the edit engine attributes of the object as SW attributes and + // sort them as Hints + const SfxItemSet& rFormatItemSet = rFrameFormat.GetAttrSet(); + SfxItemSet aItemSet( *rFormatItemSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, + RES_CHRATR_END>{} ); + SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, &rSdrObj ); + bool bCfgOutStylesOld = rHTMLWrt.m_bCfgOutStyles; + rHTMLWrt.m_bCfgOutStyles = false; + rHTMLWrt.m_bTextAttr = true; + rHTMLWrt.m_bTagOn = true; + Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false ); + rHTMLWrt.m_bTextAttr = false; + + OutHTML_DrawFrameFormatAsMarquee( rHTMLWrt, + static_cast<const SwDrawFrameFormat &>(rFrameFormat), + rSdrObj ); + rHTMLWrt.m_bTextAttr = true; + rHTMLWrt.m_bTagOn = false; + Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false ); + rHTMLWrt.m_bTextAttr = false; + rHTMLWrt.m_bCfgOutStyles = bCfgOutStylesOld; + + return rWrt; +} + +Writer& OutHTML_HeaderFooter( Writer& rWrt, const SwFrameFormat& rFrameFormat, + bool bHeader ) +{ + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // output as Multicol + rHTMLWrt.OutNewLine(); + OStringBuffer sOut; + sOut.append(OOO_STRING_SVTOOLS_HTML_division).append(' ') + .append(OOO_STRING_SVTOOLS_HTML_O_title).append("=\"") + .append( bHeader ? "header" : "footer" ).append("\""); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + sOut.makeStringAndClear().getStr() ); + + rHTMLWrt.IncIndentLevel(); // indent the content of Multicol; + + // Piece a spacer for the spacing together. Because the + // <DL> or </DL> always produces a space between paragraphs, it is + // subtracted if necessary. + const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace(); + sal_uInt16 nSize = bHeader ? rULSpace.GetLower() : rULSpace.GetUpper(); + rHTMLWrt.m_nHeaderFooterSpace = nSize; + + OString aSpacer; + if( rHTMLWrt.IsHTMLMode(HTMLMODE_VERT_SPACER) && + nSize > HTML_PARSPACE && Application::GetDefaultDevice() ) + { + nSize -= HTML_PARSPACE; + nSize = static_cast<sal_Int16>(Application::GetDefaultDevice() + ->LogicToPixel( Size(nSize,0), MapMode(MapUnit::MapTwip) ).Width()); + + aSpacer = OStringBuffer(OOO_STRING_SVTOOLS_HTML_spacer). + append(' ').append(OOO_STRING_SVTOOLS_HTML_O_type). + append("=\"").append(OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical).append("\""). + append(' ').append(OOO_STRING_SVTOOLS_HTML_O_size). + append("=\"").append(static_cast<sal_Int32>(nSize)).append("\""). + makeStringAndClear(); + } + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex(); + const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode(); + OSL_ENSURE( pSttNd, "Where is the start node" ); + + if( !bHeader && !aSpacer.isEmpty() ) + { + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + aSpacer.getStr() ); + } + + { + // in a block, so that the old state can be restored in time + // before the end. pFlyFormat doesn't need to be set here, because + // PageDesc attributes cannot occur here + HTMLSaveData aSaveData( rHTMLWrt, nStt+1, + pSttNd->EndOfSectionIndex() ); + + if( bHeader ) + rHTMLWrt.m_bOutHeader = true; + else + rHTMLWrt.m_bOutFooter = true; + + rHTMLWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() ); + } + + if( bHeader && !aSpacer.isEmpty() ) + { + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + aSpacer.getStr() ); + } + + rHTMLWrt.DecIndentLevel(); // indent the content of Multicol; + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false ); + + rHTMLWrt.m_nHeaderFooterSpace = 0; + + return rWrt; +} + +void SwHTMLWriter::AddLinkTarget( const OUString& rURL ) +{ + if( rURL.isEmpty() || rURL[0] != '#' ) + return; + + // There might be a '|' as delimiter (if the link has been inserted + // freshly) or a '%7c' or a '%7C' if the document has been saved and + // loaded already. + sal_Int32 nPos = rURL.getLength(); + bool bFound = false, bEncoded = false; + while( !bFound && nPos > 0 ) + { + sal_Unicode c = rURL[ --nPos ]; + switch( c ) + { + case cMarkSeparator: + bFound = true; + break; + case '%': + bFound = (rURL.getLength() - nPos) >=3 && rURL[ nPos+1 ] == '7'; + if(bFound) + { + c = rURL[ nPos+2 ]; + bFound = (c == 'C' || c == 'c'); + } + if( bFound ) + bEncoded = true; + } + } + if( !bFound || nPos < 2 ) // at least "#a|..." + return; + + OUString aURL( rURL.copy( 1 ) ); + + // nPos-1+1/3 (-1 because of Erase) + OUString sCmp = aURL.copy(bEncoded ? nPos+2 : nPos).replaceAll(" ",""); + if( sCmp.isEmpty() ) + return; + + sCmp = sCmp.toAsciiLowerCase(); + + if( sCmp == "region" || + sCmp == "frame" || + sCmp == "graphic" || + sCmp == "ole" || + sCmp == "table" ) + { + // Just remember it in a sorted array + if( bEncoded ) + { + aURL = aURL.replaceAt( nPos - 1, 3, OUString(cMarkSeparator) ); + } + m_aImplicitMarks.insert( aURL ); + } + else if( sCmp == "outline" ) + { + // Here, we need position and name. That's why we sort a + // sal_uInt16 and a string array ourselves. + OUString aOutline( aURL.copy( 0, nPos-1 ) ); + SwPosition aPos( *m_pCurrentPam->GetPoint() ); + if( m_pDoc->GotoOutline( aPos, aOutline ) ) + { + sal_uInt32 nIdx = aPos.nNode.GetIndex(); + + decltype(m_aOutlineMarkPoss)::size_type nIns=0; + while( nIns < m_aOutlineMarkPoss.size() && + m_aOutlineMarkPoss[nIns] < nIdx ) + nIns++; + + m_aOutlineMarkPoss.insert( m_aOutlineMarkPoss.begin()+nIns, nIdx ); + if( bEncoded ) + { + aURL = aURL.replaceAt( nPos - 1, 3, OUString(cMarkSeparator) ); + } + m_aOutlineMarks.insert( m_aOutlineMarks.begin()+nIns, aURL ); + } + } +} + +void SwHTMLWriter::CollectLinkTargets() +{ + const SwTextINetFormat* pTextAttr; + + for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT)) + { + auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem); + const SwTextNode* pTextNd; + + if( pINetFormat && + nullptr != ( pTextAttr = pINetFormat->GetTextINetFormat()) && + nullptr != ( pTextNd = pTextAttr->GetpTextNode() ) && + pTextNd->GetNodes().IsDocNodes() ) + { + AddLinkTarget( pINetFormat->GetValue() ); + } + } + + for (const SfxPoolItem* pItem : m_pDoc->GetAttrPool().GetItemSurrogates(RES_URL)) + { + auto pURL = dynamic_cast<const SwFormatURL*>(pItem); + if( pURL ) + { + AddLinkTarget( pURL->GetURL() ); + const ImageMap *pIMap = pURL->GetMap(); + if( pIMap ) + { + for( size_t i=0; i<pIMap->GetIMapObjectCount(); ++i ) + { + const IMapObject* pObj = pIMap->GetIMapObject( i ); + if( pObj ) + { + AddLinkTarget( pObj->GetURL() ); + } + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlform.cxx b/sw/source/filter/html/htmlform.cxx new file mode 100644 index 000000000..dd0c0a81f --- /dev/null +++ b/sw/source/filter/html/htmlform.cxx @@ -0,0 +1,2483 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/documentinfo.hxx> +#include <comphelper/string.hxx> +#include <vcl/svapp.hxx> + +#include <vcl/unohelp.hxx> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmltokn.h> +#include <svl/urihelper.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/event.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <svx/svdouno.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/form/ListSourceType.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/awt/XTextLayoutConstrains.hpp> +#include <com/sun/star/awt/XLayoutConstrains.hpp> +#include <com/sun/star/awt/XImageConsumer.hpp> +#include <com/sun/star/awt/ImageStatus.hpp> +#include <com/sun/star/form/XImageProducerSupplier.hpp> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <fmtanchr.hxx> +#include <htmltbl.hxx> +#include <docsh.hxx> +#include <viewsh.hxx> +#include <unodraw.hxx> +#include <unotextrange.hxx> + +#include "swcss1.hxx" +#include "swhtml.hxx" +#include "htmlform.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::form; + +const sal_uInt16 TABINDEX_MIN = 0; +const sal_uInt16 TABINDEX_MAX = 32767; + +static HTMLOptionEnum<FormSubmitMethod> const aHTMLFormMethodTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_METHOD_get, FormSubmitMethod_GET }, + { OOO_STRING_SVTOOLS_HTML_METHOD_post, FormSubmitMethod_POST }, + { nullptr, FormSubmitMethod(0) } +}; + +static HTMLOptionEnum<FormSubmitEncoding> const aHTMLFormEncTypeTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_ET_url, FormSubmitEncoding_URL }, + { OOO_STRING_SVTOOLS_HTML_ET_multipart, FormSubmitEncoding_MULTIPART }, + { OOO_STRING_SVTOOLS_HTML_ET_text, FormSubmitEncoding_TEXT }, + { nullptr, FormSubmitEncoding(0) } +}; + +namespace { + +enum HTMLWordWrapMode { HTML_WM_OFF, HTML_WM_HARD, HTML_WM_SOFT }; + +} + +static HTMLOptionEnum<HTMLWordWrapMode> const aHTMLTextAreaWrapTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_WW_off, HTML_WM_OFF }, + { OOO_STRING_SVTOOLS_HTML_WW_hard, HTML_WM_HARD }, + { OOO_STRING_SVTOOLS_HTML_WW_soft, HTML_WM_SOFT }, + { OOO_STRING_SVTOOLS_HTML_WW_physical, HTML_WM_HARD }, + { OOO_STRING_SVTOOLS_HTML_WW_virtual, HTML_WM_SOFT }, + { nullptr, HTMLWordWrapMode(0) } +}; + +static SvMacroItemId aEventTypeTable[] = +{ + SvMacroItemId::HtmlOnSubmitForm, + SvMacroItemId::HtmlOnResetForm, + SvMacroItemId::HtmlOnGetFocus, + SvMacroItemId::HtmlOnLoseFocus, + SvMacroItemId::HtmlOnClick, + SvMacroItemId::HtmlOnClickItem, + SvMacroItemId::HtmlOnChange, + SvMacroItemId::HtmlOnSelect, + SvMacroItemId::NONE +}; + +const char * aEventListenerTable[] = +{ + "XSubmitListener", + "XResetListener", + "XFocusListener", + "XFocusListener", + "XApproveActionListener", + "XItemListener", + "XChangeListener", + "" +}; + +const char * aEventMethodTable[] = +{ + "approveSubmit", + "approveReset", + "focusGained", + "focusLost", + "approveAction", + "itemStateChanged", + "changed", + "" +}; + +const char * aEventSDOptionTable[] = +{ + OOO_STRING_SVTOOLS_HTML_O_SDonsubmit, + OOO_STRING_SVTOOLS_HTML_O_SDonreset, + OOO_STRING_SVTOOLS_HTML_O_SDonfocus, + OOO_STRING_SVTOOLS_HTML_O_SDonblur, + OOO_STRING_SVTOOLS_HTML_O_SDonclick, + OOO_STRING_SVTOOLS_HTML_O_SDonclick, + OOO_STRING_SVTOOLS_HTML_O_SDonchange, + nullptr +}; + +const char * aEventOptionTable[] = +{ + OOO_STRING_SVTOOLS_HTML_O_onsubmit, + OOO_STRING_SVTOOLS_HTML_O_onreset, + OOO_STRING_SVTOOLS_HTML_O_onfocus, + OOO_STRING_SVTOOLS_HTML_O_onblur, + OOO_STRING_SVTOOLS_HTML_O_onclick, + OOO_STRING_SVTOOLS_HTML_O_onclick, + OOO_STRING_SVTOOLS_HTML_O_onchange, + nullptr +}; + +class SwHTMLForm_Impl +{ + SwDocShell *m_pDocShell; + + SvKeyValueIterator *m_pHeaderAttrs; + + // Cached interfaces + uno::Reference< drawing::XDrawPage > m_xDrawPage; + uno::Reference< container::XIndexContainer > m_xForms; + uno::Reference< drawing::XShapes > m_xShapes; + uno::Reference< XMultiServiceFactory > m_xServiceFactory; + + uno::Reference< script::XEventAttacherManager > m_xControlEventManager; + uno::Reference< script::XEventAttacherManager > m_xFormEventManager; + + // Context information + uno::Reference< container::XIndexContainer > m_xFormComps; + uno::Reference< beans::XPropertySet > m_xFCompPropertySet; + uno::Reference< drawing::XShape > m_xShape; + + OUString m_sText; + std::vector<OUString> m_aStringList; + std::vector<OUString> m_aValueList; + std::vector<sal_uInt16> m_aSelectedList; + +public: + explicit SwHTMLForm_Impl( SwDocShell *pDSh ) : + m_pDocShell( pDSh ), + m_pHeaderAttrs( pDSh ? pDSh->GetHeaderAttributes() : nullptr ) + { + OSL_ENSURE( m_pDocShell, "No DocShell, no Controls" ); + } + + const uno::Reference< XMultiServiceFactory >& GetServiceFactory(); + void GetDrawPage(); + const uno::Reference< drawing::XShapes >& GetShapes(); + const uno::Reference< script::XEventAttacherManager >& GetControlEventManager(); + const uno::Reference< script::XEventAttacherManager >& GetFormEventManager(); + const uno::Reference< container::XIndexContainer >& GetForms(); + + const uno::Reference< container::XIndexContainer >& GetFormComps() const + { + return m_xFormComps; + } + + void SetFormComps( const uno::Reference< container::XIndexContainer >& r ) + { + m_xFormComps = r; + } + + void ReleaseFormComps() { m_xFormComps = nullptr; m_xControlEventManager = nullptr; } + + const uno::Reference< beans::XPropertySet >& GetFCompPropSet() const + { + return m_xFCompPropertySet; + } + + void SetFCompPropSet( const uno::Reference< beans::XPropertySet >& r ) + { + m_xFCompPropertySet = r; + } + + void ReleaseFCompPropSet() { m_xFCompPropertySet = nullptr; } + + const uno::Reference< drawing::XShape >& GetShape() const { return m_xShape; } + void SetShape( const uno::Reference< drawing::XShape >& r ) { m_xShape = r; } + + OUString& GetText() { return m_sText; } + void EraseText() { m_sText.clear(); } + + std::vector<OUString>& GetStringList() { return m_aStringList; } + void EraseStringList() + { + m_aStringList.clear(); + } + + std::vector<OUString>& GetValueList() { return m_aValueList; } + void EraseValueList() + { + m_aValueList.clear(); + } + + std::vector<sal_uInt16>& GetSelectedList() { return m_aSelectedList; } + void EraseSelectedList() + { + m_aSelectedList.clear(); + } + + SvKeyValueIterator *GetHeaderAttrs() const { return m_pHeaderAttrs; } +}; + +const uno::Reference< XMultiServiceFactory >& SwHTMLForm_Impl::GetServiceFactory() +{ + if( !m_xServiceFactory.is() && m_pDocShell ) + { + m_xServiceFactory = + uno::Reference< XMultiServiceFactory >( m_pDocShell->GetBaseModel(), + UNO_QUERY ); + OSL_ENSURE( m_xServiceFactory.is(), + "XServiceFactory not received from model" ); + } + return m_xServiceFactory; +} + +void SwHTMLForm_Impl::GetDrawPage() +{ + if( !m_xDrawPage.is() && m_pDocShell ) + { + uno::Reference< drawing::XDrawPageSupplier > xTextDoc( m_pDocShell->GetBaseModel(), + UNO_QUERY ); + OSL_ENSURE( xTextDoc.is(), + "drawing::XDrawPageSupplier not received from model" ); + m_xDrawPage = xTextDoc->getDrawPage(); + OSL_ENSURE( m_xDrawPage.is(), "drawing::XDrawPage not received" ); + } +} + +const uno::Reference< container::XIndexContainer >& SwHTMLForm_Impl::GetForms() +{ + if( !m_xForms.is() ) + { + GetDrawPage(); + if( m_xDrawPage.is() ) + { + uno::Reference< XFormsSupplier > xFormsSupplier( m_xDrawPage, UNO_QUERY ); + OSL_ENSURE( xFormsSupplier.is(), + "XFormsSupplier not received from drawing::XDrawPage" ); + + uno::Reference< container::XNameContainer > xNameCont = + xFormsSupplier->getForms(); + m_xForms.set( xNameCont, UNO_QUERY ); + + OSL_ENSURE( m_xForms.is(), "XForms not received" ); + } + } + return m_xForms; +} + +const uno::Reference< drawing::XShapes > & SwHTMLForm_Impl::GetShapes() +{ + if( !m_xShapes.is() ) + { + GetDrawPage(); + if( m_xDrawPage.is() ) + { + m_xShapes = m_xDrawPage; + OSL_ENSURE( m_xShapes.is(), + "XShapes not received from drawing::XDrawPage" ); + } + } + return m_xShapes; +} + +const uno::Reference< script::XEventAttacherManager >& + SwHTMLForm_Impl::GetControlEventManager() +{ + if( !m_xControlEventManager.is() && m_xFormComps.is() ) + { + m_xControlEventManager = + uno::Reference< script::XEventAttacherManager >( m_xFormComps, UNO_QUERY ); + OSL_ENSURE( m_xControlEventManager.is(), + "uno::Reference< XEventAttacherManager > not received from xFormComps" ); + } + + return m_xControlEventManager; +} + +const uno::Reference< script::XEventAttacherManager >& + SwHTMLForm_Impl::GetFormEventManager() +{ + if( !m_xFormEventManager.is() ) + { + GetForms(); + if( m_xForms.is() ) + { + m_xFormEventManager = + uno::Reference< script::XEventAttacherManager >( m_xForms, UNO_QUERY ); + OSL_ENSURE( m_xFormEventManager.is(), + "uno::Reference< XEventAttacherManager > not received from xForms" ); + } + } + + return m_xFormEventManager; +} + +namespace { + +class SwHTMLImageWatcher : + public cppu::WeakImplHelper< awt::XImageConsumer, XEventListener > +{ + uno::Reference< drawing::XShape > xShape; // the control + uno::Reference< XImageProducerSupplier > xSrc; + uno::Reference< awt::XImageConsumer > xThis; // reference to self + bool bSetWidth; + bool bSetHeight; + + void clear(); + +public: + SwHTMLImageWatcher( const uno::Reference< drawing::XShape > & rShape, + bool bWidth, bool bHeight ); + + // startProduction can not be called in the constructor because it can + // destruct itself, hence a separate method. + void start() { xSrc->getImageProducer()->startProduction(); } + + // UNO binding + + // XImageConsumer + virtual void SAL_CALL init( sal_Int32 Width, sal_Int32 Height) override; + virtual void SAL_CALL setColorModel( + sal_Int16 BitCount, const uno::Sequence< sal_Int32 >& RGBAPal, + sal_Int32 RedMask, sal_Int32 GreenMask, sal_Int32 BlueMask, + sal_Int32 AlphaMask) override; + virtual void SAL_CALL setPixelsByBytes( + sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, + const uno::Sequence< sal_Int8 >& ProducerData, + sal_Int32 Offset, sal_Int32 Scansize) override; + virtual void SAL_CALL setPixelsByLongs( + sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, + const uno::Sequence< sal_Int32 >& ProducerData, + sal_Int32 Offset, sal_Int32 Scansize) override; + virtual void SAL_CALL complete( + sal_Int32 Status, + const uno::Reference< awt::XImageProducer > & Producer) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& Source ) override; +}; + +} + +SwHTMLImageWatcher::SwHTMLImageWatcher( + const uno::Reference< drawing::XShape >& rShape, + bool bWidth, bool bHeight ) : + xShape( rShape ), + bSetWidth( bWidth ), bSetHeight( bHeight ) +{ + // Remember the source of the image + uno::Reference< drawing::XControlShape > xControlShape( xShape, UNO_QUERY ); + uno::Reference< awt::XControlModel > xControlModel( + xControlShape->getControl() ); + xSrc.set( xControlModel, UNO_QUERY ); + OSL_ENSURE( xSrc.is(), "No XImageProducerSupplier" ); + + // Register as Event-Listener on the shape to be able to release it on dispose. + uno::Reference< XEventListener > xEvtLstnr = static_cast<XEventListener *>(this); + uno::Reference< XComponent > xComp( xShape, UNO_QUERY ); + xComp->addEventListener( xEvtLstnr ); + + // Lastly we keep a reference to ourselves so we are not destroyed + // (should not be necessary since we're still registered elsewhere) + xThis = static_cast<awt::XImageConsumer *>(this); + + // Register at ImageProducer to retrieve the size... + xSrc->getImageProducer()->addConsumer( xThis ); +} + +void SwHTMLImageWatcher::clear() +{ + // Unregister on Shape + uno::Reference< XEventListener > xEvtLstnr = static_cast<XEventListener *>(this); + uno::Reference< XComponent > xComp( xShape, UNO_QUERY ); + xComp->removeEventListener( xEvtLstnr ); + + // Unregister on ImageProducer + uno::Reference<awt::XImageProducer> xProd = xSrc->getImageProducer(); + if( xProd.is() ) + xProd->removeConsumer( xThis ); +} + +void SwHTMLImageWatcher::init( sal_Int32 Width, sal_Int32 Height ) +{ + OSL_ENSURE( bSetWidth || bSetHeight, + "Width or height has to be adjusted" ); + + // If no width or height is given, it is initialized to those of + // the empty graphic that is available before the stream of a graphic + // that is to be displayed asynchronous is available. + if( !Width && !Height ) + return; + + awt::Size aNewSz; + aNewSz.Width = Width; + aNewSz.Height = Height; + if( Application::GetDefaultDevice() ) + { + Size aTmp(aNewSz.Width, aNewSz.Height); + aTmp = Application::GetDefaultDevice() + ->PixelToLogic( aTmp, MapMode( MapUnit::Map100thMM ) ); + aNewSz.Width = aTmp.Width(); + aNewSz.Height = aTmp.Height(); + } + + if( !bSetWidth || !bSetHeight ) + { + awt::Size aSz( xShape->getSize() ); + if( bSetWidth && aNewSz.Height ) + { + aNewSz.Width *= aSz.Height; + aNewSz.Width /= aNewSz.Height; + aNewSz.Height = aSz.Height; + } + if( bSetHeight && aNewSz.Width ) + { + aNewSz.Height *= aSz.Width; + aNewSz.Height /= aNewSz.Width; + aNewSz.Width = aSz.Width; + } + } + if( aNewSz.Width < MINFLY ) + aNewSz.Width = MINFLY; + if( aNewSz.Height < MINFLY ) + aNewSz.Height = MINFLY; + + xShape->setSize( aNewSz ); + if( bSetWidth ) + { + // If the control is anchored to a table, the column have to be recalculated + + // To get to the SwXShape* we need an interface that is implemented by SwXShape + + uno::Reference< beans::XPropertySet > xPropSet( xShape, UNO_QUERY ); + SwXShape *pSwShape = comphelper::getUnoTunnelImplementation<SwXShape>(xPropSet); + + OSL_ENSURE( pSwShape, "Where is SW-Shape?" ); + if( pSwShape ) + { + SwFrameFormat *pFrameFormat = pSwShape->GetFrameFormat(); + + const SwDoc *pDoc = pFrameFormat->GetDoc(); + const SwPosition* pAPos = pFrameFormat->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( *pDoc ); + + if ( nBrowseWidth ) + { + pLayout->Resize( nBrowseWidth, true, true, + bLastGrf ? HTMLTABLE_RESIZE_NOW + : 500 ); + } + } + } + } + } + + // unregister and delete self + clear(); + xThis = nullptr; +} + +void SwHTMLImageWatcher::setColorModel( + sal_Int16, const Sequence< sal_Int32 >&, sal_Int32, sal_Int32, + sal_Int32, sal_Int32 ) +{ +} + +void SwHTMLImageWatcher::setPixelsByBytes( + sal_Int32, sal_Int32, sal_Int32, sal_Int32, + const Sequence< sal_Int8 >&, sal_Int32, sal_Int32 ) +{ +} + +void SwHTMLImageWatcher::setPixelsByLongs( + sal_Int32, sal_Int32, sal_Int32, sal_Int32, + const Sequence< sal_Int32 >&, sal_Int32, sal_Int32 ) +{ +} + +void SwHTMLImageWatcher::complete( sal_Int32 Status, + const uno::Reference< awt::XImageProducer >& ) +{ + if( awt::ImageStatus::IMAGESTATUS_ERROR == Status || awt::ImageStatus::IMAGESTATUS_ABORTED == Status ) + { + // unregister and delete self + clear(); + xThis = nullptr; + } +} + +void SwHTMLImageWatcher::disposing(const lang::EventObject& evt) +{ + uno::Reference< awt::XImageConsumer > xTmp; + + // We need to release the shape if it is disposed of + if( evt.Source == xShape ) + { + clear(); + xTmp = static_cast<awt::XImageConsumer*>(this); + xThis = nullptr; + } +} + +void SwHTMLParser::DeleteFormImpl() +{ + delete m_pFormImpl; + m_pFormImpl = nullptr; +} + +static void lcl_html_setFixedFontProperty( + const uno::Reference< beans::XPropertySet >& rPropSet ) +{ + vcl::Font aFixedFont( OutputDevice::GetDefaultFont( + DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, + GetDefaultFontFlags::OnlyOne ) ); + Any aTmp; + aTmp <<= aFixedFont.GetFamilyName(); + rPropSet->setPropertyValue("FontName", aTmp ); + + aTmp <<= aFixedFont.GetStyleName(); + rPropSet->setPropertyValue("FontStyleName", + aTmp ); + + aTmp <<= static_cast<sal_Int16>(aFixedFont.GetFamilyType()); + rPropSet->setPropertyValue("FontFamily", aTmp ); + + aTmp <<= static_cast<sal_Int16>(aFixedFont.GetCharSet()); + rPropSet->setPropertyValue("FontCharset", + aTmp ); + + aTmp <<= static_cast<sal_Int16>(aFixedFont.GetPitch()); + rPropSet->setPropertyValue("FontPitch", aTmp ); + + aTmp <<= float(10.0); + rPropSet->setPropertyValue("FontHeight", aTmp ); +} + +void SwHTMLParser::SetControlSize( const uno::Reference< drawing::XShape >& rShape, + const Size& rTextSz, + bool bMinWidth, + bool bMinHeight ) +{ + if( !rTextSz.Width() && !rTextSz.Height() && !bMinWidth && !bMinHeight ) + return; + + // To get to SwXShape* we need an interface that is implemented by SwXShape + + uno::Reference< beans::XPropertySet > xPropSet( rShape, UNO_QUERY ); + + SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pVSh && !m_nEventId ) + { + // If there is no view shell by now and the doc shell is an internal + // one, no view shell will be created. That for, we have to do that of + // our own. This happens if a linked section is inserted or refreshed. + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + if( pDocSh ) + { + if ( pDocSh->GetMedium() ) + { + // if there is no hidden property in the MediaDescriptor it should be removed after loading + const SfxBoolItem* pHiddenItem = SfxItemSet::GetItem<SfxBoolItem>(pDocSh->GetMedium()->GetItemSet(), SID_HIDDEN, false); + m_bRemoveHidden = ( pHiddenItem == nullptr || !pHiddenItem->GetValue() ); + } + + m_pTempViewFrame = SfxViewFrame::LoadHiddenDocument( *pDocSh, SFX_INTERFACE_NONE ); + CallStartAction(); + pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + // this ridiculous hack also enables Undo, so turn it off again + m_xDoc->GetIDocumentUndoRedo().DoUndo(false); + } + } + + SwXShape *pSwShape = comphelper::getUnoTunnelImplementation<SwXShape>(xPropSet); + + OSL_ENSURE( pSwShape, "Where is SW-Shape?" ); + + // has to be a Draw-Format + SwFrameFormat *pFrameFormat = pSwShape ? pSwShape->GetFrameFormat() : nullptr ; + OSL_ENSURE( pFrameFormat && RES_DRAWFRMFMT == pFrameFormat->Which(), "No DrawFrameFormat" ); + + // look if a SdrObject exists for it + const SdrObject *pObj = pFrameFormat ? pFrameFormat->FindSdrObject() : nullptr; + OSL_ENSURE( pObj, "SdrObject not found" ); + OSL_ENSURE( pObj && SdrInventor::FmForm == pObj->GetObjInventor(), "wrong Inventor" ); + + const SdrView* pDrawView = pVSh ? pVSh->GetDrawView() : nullptr; + + const SdrUnoObj *pFormObj = dynamic_cast<const SdrUnoObj*>( pObj ); + uno::Reference< awt::XControl > xControl; + if ( pDrawView && pVSh->GetWin() && pFormObj ) + xControl = pFormObj->GetUnoControl( *pDrawView, *pVSh->GetWin() ); + + awt::Size aSz( rShape->getSize() ); + awt::Size aNewSz( 0, 0 ); + + // #i71248# ensure we got a XControl before applying corrections + if(xControl.is()) + { + if( bMinWidth || bMinHeight ) + { + uno::Reference< awt::XLayoutConstrains > xLC( xControl, UNO_QUERY ); + awt::Size aTmpSz( xLC->getPreferredSize() ); + if( bMinWidth ) + aNewSz.Width = aTmpSz.Width; + if( bMinHeight ) + aNewSz.Height = aTmpSz.Height; + } + if( rTextSz.Width() || rTextSz.Height()) + { + uno::Reference< awt::XTextLayoutConstrains > xLC( xControl, UNO_QUERY ); + OSL_ENSURE( xLC.is(), "no XTextLayoutConstrains" ); + if( xLC.is() ) + { + awt::Size aTmpSz( rTextSz.Width(), rTextSz.Height() ); + if( -1 == rTextSz.Width() ) + { + aTmpSz.Width = 0; + aTmpSz.Height = m_nSelectEntryCnt; + } + aTmpSz = xLC->getMinimumSize( static_cast< sal_Int16 >(aTmpSz.Width), static_cast< sal_Int16 >(aTmpSz.Height) ); + if( rTextSz.Width() ) + aNewSz.Width = aTmpSz.Width; + if( rTextSz.Height() ) + aNewSz.Height = aTmpSz.Height; + } + } + } + + if( Application::GetDefaultDevice() ) + { + Size aTmpSz( aNewSz.Width, aNewSz.Height ); + aTmpSz = Application::GetDefaultDevice() + ->PixelToLogic( aTmpSz, MapMode( MapUnit::Map100thMM ) ); + aNewSz.Width = aTmpSz.Width(); + aNewSz.Height = aTmpSz.Height(); + } + if( aNewSz.Width ) + { + if( aNewSz.Width < MINLAY ) + aNewSz.Width = MINLAY; + aSz.Width = aNewSz.Width; + } + if( aNewSz.Height ) + { + if( aNewSz.Height < MINLAY ) + aNewSz.Height = MINLAY; + aSz.Height = aNewSz.Height; + } + + rShape->setSize( aSz ); +} + +static bool lcl_html_setEvents( + const uno::Reference< script::XEventAttacherManager > & rEvtMn, + sal_uInt32 nPos, const SvxMacroTableDtor& rMacroTable, + const std::vector<OUString>& rUnoMacroTable, + const std::vector<OUString>& rUnoMacroParamTable, + const OUString& rType ) +{ + // First the number of events has to be determined + sal_Int32 nEvents = 0; + + for( int i = 0; SvMacroItemId::NONE != aEventTypeTable[i]; ++i ) + { + const SvxMacro *pMacro = rMacroTable.Get( aEventTypeTable[i] ); + // As long as not all events are implemented the table also holds empty strings + if( pMacro && aEventListenerTable[i] ) + nEvents++; + } + for( const auto &rStr : rUnoMacroTable ) + { + sal_Int32 nIndex = 0; + if( rStr.getToken( 0, '-', nIndex ).isEmpty() || -1 == nIndex ) + continue; + if( rStr.getToken( 0, '-', nIndex ).isEmpty() || -1 == nIndex ) + continue; + if( nIndex < rStr.getLength() ) + nEvents++; + } + + if( 0==nEvents ) + return false; + + Sequence<script::ScriptEventDescriptor> aDescs( nEvents ); + script::ScriptEventDescriptor* pDescs = aDescs.getArray(); + sal_Int32 nEvent = 0; + + for( int i=0; SvMacroItemId::NONE != aEventTypeTable[i]; ++i ) + { + const SvxMacro *pMacro = rMacroTable.Get( aEventTypeTable[i] ); + if( pMacro && aEventListenerTable[i] ) + { + script::ScriptEventDescriptor& rDesc = pDescs[nEvent++]; + rDesc.ListenerType = + OUString::createFromAscii(aEventListenerTable[i]); + rDesc.EventMethod = OUString::createFromAscii(aEventMethodTable[i]); + rDesc.ScriptType = pMacro->GetLanguage(); + rDesc.ScriptCode = pMacro->GetMacName(); + } + } + + for( const auto &rStr : rUnoMacroTable ) + { + sal_Int32 nIndex = 0; + OUString sListener( rStr.getToken( 0, '-', nIndex ) ); + if( sListener.isEmpty() || -1 == nIndex ) + continue; + + OUString sMethod( rStr.getToken( 0, '-', nIndex ) ); + if( sMethod.isEmpty() || -1 == nIndex ) + continue; + + OUString sCode( rStr.copy( nIndex ) ); + if( sCode.isEmpty() ) + continue; + + script::ScriptEventDescriptor& rDesc = pDescs[nEvent++]; + rDesc.ListenerType = sListener; + rDesc.EventMethod = sMethod; + rDesc.ScriptType = rType; + rDesc.ScriptCode = sCode; + rDesc.AddListenerParam.clear(); + + if(!rUnoMacroParamTable.empty()) + { + OUString sSearch = sListener + "-" +sMethod + "-"; + sal_Int32 nLen = sSearch.getLength(); + for(const auto & rParam : rUnoMacroParamTable) + { + if( rParam.startsWith( sSearch ) && rParam.getLength() > nLen ) + { + rDesc.AddListenerParam = rParam.copy(nLen); + break; + } + } + } + } + rEvtMn->registerScriptEvents( nPos, aDescs ); + return true; +} + +static void lcl_html_getEvents( const OUString& rOption, const OUString& rValue, + std::vector<OUString>& rUnoMacroTable, + std::vector<OUString>& rUnoMacroParamTable ) +{ + if( rOption.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_sdevent ) ) + { + OUString aEvent = rOption.copy( strlen( OOO_STRING_SVTOOLS_HTML_O_sdevent ) ) + + "-" + rValue; + rUnoMacroTable.push_back(aEvent); + } + else if( rOption.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_O_sdaddparam ) ) + { + OUString aParam = rOption.copy( strlen( OOO_STRING_SVTOOLS_HTML_O_sdaddparam ) ) + + "-" + rValue; + rUnoMacroParamTable.push_back(aParam); + } +} + +uno::Reference< drawing::XShape > SwHTMLParser::InsertControl( + const uno::Reference< XFormComponent > & rFComp, + const uno::Reference< beans::XPropertySet > & rFCompPropSet, + const Size& rSize, sal_Int16 eVertOri, sal_Int16 eHoriOri, + SfxItemSet& rCSS1ItemSet, SvxCSS1PropertyInfo& rCSS1PropInfo, + const SvxMacroTableDtor& rMacroTable, const std::vector<OUString>& rUnoMacroTable, + const std::vector<OUString>& rUnoMacroParamTable, bool bSetFCompPropSet, + bool bHidden ) +{ + uno::Reference< drawing::XShape > xShape; + + const uno::Reference< container::XIndexContainer > & rFormComps = + m_pFormImpl->GetFormComps(); + Any aAny( &rFComp, cppu::UnoType<XFormComponent>::get()); + rFormComps->insertByIndex( rFormComps->getCount(), aAny ); + + if( !bHidden ) + { + Any aTmp; + sal_Int32 nLeftSpace = 0; + sal_Int32 nRightSpace = 0; + sal_Int32 nUpperSpace = 0; + sal_Int32 nLowerSpace = 0; + + const uno::Reference< XMultiServiceFactory > & rServiceFactory = + m_pFormImpl->GetServiceFactory(); + if( !rServiceFactory.is() ) + return xShape; + + uno::Reference< XInterface > xCreate = rServiceFactory->createInstance( "com.sun.star.drawing.ControlShape" ); + if( !xCreate.is() ) + return xShape; + + xShape.set( xCreate, UNO_QUERY ); + + OSL_ENSURE( xShape.is(), "XShape not received" ); + awt::Size aTmpSz; + aTmpSz.Width = rSize.Width(); + aTmpSz.Height = rSize.Height(); + xShape->setSize( aTmpSz ); + + uno::Reference< beans::XPropertySet > xShapePropSet( xCreate, UNO_QUERY ); + + // set left/right border + const SfxPoolItem *pItem; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_LR_SPACE, true, + &pItem ) ) + { + // Flatten first line indent + const SvxLRSpaceItem *pLRItem = static_cast<const SvxLRSpaceItem *>(pItem); + SvxLRSpaceItem aLRItem( *pLRItem ); + aLRItem.SetTextFirstLineOffset( 0 ); + if( rCSS1PropInfo.m_bLeftMargin ) + { + nLeftSpace = convertTwipToMm100( aLRItem.GetLeft() ); + rCSS1PropInfo.m_bLeftMargin = false; + } + if( rCSS1PropInfo.m_bRightMargin ) + { + nRightSpace = convertTwipToMm100( aLRItem.GetRight() ); + rCSS1PropInfo.m_bRightMargin = false; + } + rCSS1ItemSet.ClearItem( RES_LR_SPACE ); + } + if( nLeftSpace || nRightSpace ) + { + Any aAny2; + aAny2 <<= nLeftSpace; + xShapePropSet->setPropertyValue("LeftMargin", aAny2 ); + + aAny2 <<= nRightSpace; + xShapePropSet->setPropertyValue("RightMargin", aAny2 ); + } + + // set upper/lower border + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_UL_SPACE, true, + &pItem ) ) + { + // Flatten first line indent + const SvxULSpaceItem *pULItem = static_cast<const SvxULSpaceItem *>(pItem); + if( rCSS1PropInfo.m_bTopMargin ) + { + nUpperSpace = convertTwipToMm100( pULItem->GetUpper() ); + rCSS1PropInfo.m_bTopMargin = false; + } + if( rCSS1PropInfo.m_bBottomMargin ) + { + nLowerSpace = convertTwipToMm100( pULItem->GetLower() ); + rCSS1PropInfo.m_bBottomMargin = false; + } + + rCSS1ItemSet.ClearItem( RES_UL_SPACE ); + } + if( nUpperSpace || nLowerSpace ) + { + uno::Any aAny2; + aAny2 <<= nUpperSpace; + xShapePropSet->setPropertyValue("TopMargin", aAny2 ); + + aAny2 <<= nLowerSpace; + xShapePropSet->setPropertyValue("BottomMargin", aAny2 ); + } + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = + rFCompPropSet->getPropertySetInfo(); + OUString sPropName = "BackgroundColor"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_BACKGROUND, true, + &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + const Color &rColor = static_cast<const SvxBrushItem *>(pItem)->GetColor(); + /// copy color, if color is not "no fill"/"auto fill" + if( rColor != COL_TRANSPARENT ) + { + /// copy complete color with transparency + aTmp <<= rColor; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + } + + sPropName = "TextColor"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_COLOR, true, + &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int32>(static_cast<const SvxColorItem *>(pItem)->GetValue() + .GetRGBColor()); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + sPropName = "FontHeight"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_FONTSIZE, + true, &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + float fVal = static_cast< float >( + (static_cast<const SvxFontHeightItem *>(pItem)->GetHeight()) / 20.0 ); + aTmp <<= fVal; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_FONT, true, + &pItem ) ) + { + const SvxFontItem *pFontItem = static_cast<const SvxFontItem *>(pItem); + sPropName = "FontName"; + if( xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= pFontItem->GetFamilyName(); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + sPropName = "FontStyleName"; + if( xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= pFontItem->GetStyleName(); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + sPropName = "FontFamily"; + if( xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily()) ; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + sPropName = "FontCharset"; + if( xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet()) ; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + sPropName = "FontPitch"; + if( xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch()) ; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + } + + sPropName = "FontWeight"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_WEIGHT, + true, &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + float fVal = vcl::unohelper::ConvertFontWeight( + static_cast<const SvxWeightItem *>(pItem)->GetWeight() ); + aTmp <<= fVal; + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + sPropName = "FontSlant"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_POSTURE, + true, &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(static_cast<const SvxPostureItem *>(pItem)->GetPosture()); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + sPropName = "FontUnderline"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_UNDERLINE, + true, &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(static_cast<const SvxUnderlineItem *>(pItem)->GetLineStyle()); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + sPropName = "FontStrikeout"; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_CHRATR_CROSSEDOUT, + true, &pItem ) && + xPropSetInfo->hasPropertyByName( sPropName ) ) + { + aTmp <<= static_cast<sal_Int16>(static_cast<const SvxCrossedOutItem *>(pItem)->GetStrikeout()); + rFCompPropSet->setPropertyValue( sPropName, aTmp ); + } + + uno::Reference< text::XTextRange > xTextRg; + text::TextContentAnchorType nAnchorType = text::TextContentAnchorType_AS_CHARACTER; + bool bSetPos = false, bSetSurround = false; + sal_Int32 nXPos = 0, nYPos = 0; + text::WrapTextMode nSurround = text::WrapTextMode_NONE; + if( SVX_CSS1_POS_ABSOLUTE == rCSS1PropInfo.m_ePosition && + SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eLeftType && + SVX_CSS1_LTYPE_TWIP == rCSS1PropInfo.m_eTopType ) + { + const SwStartNode *pFlySttNd = + m_pPam->GetPoint()->nNode.GetNode().FindFlyStartNode(); + + if( pFlySttNd ) + { + nAnchorType = text::TextContentAnchorType_AT_FRAME; + SwPaM aPaM( *pFlySttNd ); + + uno::Reference< text::XText > xDummyTextRef; // dirty, but works according to OS... + xTextRg = new SwXTextRange( aPaM, xDummyTextRef ); + } + else + { + nAnchorType = text::TextContentAnchorType_AT_PAGE; + } + nXPos = convertTwipToMm100( rCSS1PropInfo.m_nLeft ) + nLeftSpace; + nYPos = convertTwipToMm100( rCSS1PropInfo.m_nTop ) + nUpperSpace; + bSetPos = true; + + nSurround = text::WrapTextMode_THROUGH; + bSetSurround = true; + } + else if( SvxAdjust::Left == rCSS1PropInfo.m_eFloat || + text::HoriOrientation::LEFT == eHoriOri ) + { + nAnchorType = text::TextContentAnchorType_AT_PARAGRAPH; + nXPos = nLeftSpace; + nYPos = nUpperSpace; + bSetPos = true; + nSurround = text::WrapTextMode_RIGHT; + bSetSurround = true; + } + else if( text::VertOrientation::NONE != eVertOri ) + { + sal_Int16 nVertOri = text::VertOrientation::NONE; + switch( eVertOri ) + { + case text::VertOrientation::TOP: + nVertOri = text::VertOrientation::TOP; + break; + case text::VertOrientation::CENTER: + nVertOri = text::VertOrientation::CENTER; + break; + case text::VertOrientation::BOTTOM: + nVertOri = text::VertOrientation::BOTTOM; + break; + case text::VertOrientation::CHAR_TOP: + nVertOri = text::VertOrientation::CHAR_TOP; + break; + case text::VertOrientation::CHAR_CENTER: + nVertOri = text::VertOrientation::CHAR_CENTER; + break; + case text::VertOrientation::CHAR_BOTTOM: + nVertOri = text::VertOrientation::CHAR_BOTTOM; + break; + case text::VertOrientation::LINE_TOP: + nVertOri = text::VertOrientation::LINE_TOP; + break; + case text::VertOrientation::LINE_CENTER: + nVertOri = text::VertOrientation::LINE_CENTER; + break; + case text::VertOrientation::LINE_BOTTOM: + nVertOri = text::VertOrientation::LINE_BOTTOM; + break; + // coverity[dead_error_begin] - following conditions exist to avoid compiler warning + case text::VertOrientation::NONE: + nVertOri = text::VertOrientation::NONE; + break; + } + aTmp <<= nVertOri ; + xShapePropSet->setPropertyValue("VertOrient", aTmp ); + } + + aTmp <<= nAnchorType ; + xShapePropSet->setPropertyValue("AnchorType", aTmp ); + + if( text::TextContentAnchorType_AT_PAGE == nAnchorType ) + { + aTmp <<= sal_Int16(1) ; + xShapePropSet->setPropertyValue("AnchorPageNo", aTmp ); + } + else + { + if( !xTextRg.is() ) + { + uno::Reference< text::XText > xDummyTextRef; // dirty but works according to OS... + xTextRg = new SwXTextRange( *m_pPam, xDummyTextRef ); + } + + aTmp <<= xTextRg; + xShapePropSet->setPropertyValue("TextRange", aTmp ); + } + + if( bSetPos ) + { + aTmp <<= sal_Int16(text::HoriOrientation::NONE); + xShapePropSet->setPropertyValue("HoriOrient", aTmp ); + aTmp <<= nXPos ; + xShapePropSet->setPropertyValue("HoriOrientPosition", aTmp ); + + aTmp <<= sal_Int16(text::VertOrientation::NONE); + xShapePropSet->setPropertyValue("VertOrient", aTmp ); + aTmp <<= nYPos ; + xShapePropSet->setPropertyValue("VertOrientPosition", aTmp ); + } + if( bSetSurround ) + { + aTmp <<= nSurround ; + xShapePropSet->setPropertyValue("Surround", aTmp ); + } + + m_pFormImpl->GetShapes()->add(xShape); + + // Set ControlModel to ControlShape + uno::Reference< drawing::XControlShape > xControlShape( xShape, UNO_QUERY ); + uno::Reference< awt::XControlModel > xControlModel( rFComp, UNO_QUERY ); + xControlShape->setControl( xControlModel ); + } + + // Since the focus is set at insertion of the controls, focus events will be sent + // To prevent previous JavaScript-Events from being called, these events will only be set retroactively + if( !rMacroTable.empty() || !rUnoMacroTable.empty() ) + { + bool bHasEvents = lcl_html_setEvents( m_pFormImpl->GetControlEventManager(), + rFormComps->getCount() - 1, + rMacroTable, rUnoMacroTable, rUnoMacroParamTable, + GetScriptTypeString(m_pFormImpl->GetHeaderAttrs()) ); + if (bHasEvents) + NotifyMacroEventRead(); + } + + if( bSetFCompPropSet ) + { + m_pFormImpl->SetFCompPropSet( rFCompPropSet ); + } + + return xShape; +} + +void SwHTMLParser::NewForm( bool bAppend ) +{ + // Does a form already exist? + if( m_pFormImpl && m_pFormImpl->GetFormComps().is() ) + return; + + if( bAppend ) + { + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + } + + if( !m_pFormImpl ) + m_pFormImpl = new SwHTMLForm_Impl( m_xDoc->GetDocShell() ); + + OUString aAction( m_sBaseURL ); + OUString sName, sTarget; + FormSubmitEncoding nEncType = FormSubmitEncoding_URL; + FormSubmitMethod nMethod = FormSubmitMethod_GET; + SvxMacroTableDtor aMacroTable; + std::vector<OUString> aUnoMacroTable; + std::vector<OUString> aUnoMacroParamTable; + SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs(); + ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs ); + const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + ScriptType eScriptType2 = eDfltScriptType; + SvMacroItemId nEvent = SvMacroItemId::NONE; + bool bSetEvent = false; + + switch( rOption.GetToken() ) + { + case HtmlOptionId::ACTION: + aAction = rOption.GetString(); + break; + case HtmlOptionId::METHOD: + nMethod = rOption.GetEnum( aHTMLFormMethodTable, nMethod ); + break; + case HtmlOptionId::ENCTYPE: + nEncType = rOption.GetEnum( aHTMLFormEncTypeTable, nEncType ); + break; + case HtmlOptionId::TARGET: + sTarget = rOption.GetString(); + break; + case HtmlOptionId::NAME: + sName = rOption.GetString(); + break; + + case HtmlOptionId::SDONSUBMIT: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONSUBMIT: + nEvent = SvMacroItemId::HtmlOnSubmitForm; + bSetEvent = true; + break; + + case HtmlOptionId::SDONRESET: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONRESET: + nEvent = SvMacroItemId::HtmlOnResetForm; + bSetEvent = true; + break; + + default: + lcl_html_getEvents( rOption.GetTokenString(), + rOption.GetString(), + aUnoMacroTable, aUnoMacroParamTable ); + break; + } + + if( bSetEvent ) + { + OUString sEvent( rOption.GetString() ); + if( !sEvent.isEmpty() ) + { + sEvent = convertLineEnd(sEvent, GetSystemLineEnd()); + OUString aScriptType2; + if( EXTENDED_STYPE==eScriptType2 ) + aScriptType2 = rDfltScriptType; + aMacroTable.Insert( nEvent, SvxMacro( sEvent, aScriptType2, eScriptType2 ) ); + } + } + } + + const uno::Reference< XMultiServiceFactory > & rSrvcMgr = + m_pFormImpl->GetServiceFactory(); + if( !rSrvcMgr.is() ) + return; + + uno::Reference< XInterface > xInt; + try + { + xInt = rSrvcMgr->createInstance("com.sun.star.form.component.Form"); + } + catch (const css::lang::ServiceNotRegisteredException&) + { + } + if( !xInt.is() ) + return; + + uno::Reference< XForm > xForm( xInt, UNO_QUERY ); + OSL_ENSURE( xForm.is(), "no Form?" ); + + uno::Reference< container::XIndexContainer > xFormComps( xForm, UNO_QUERY ); + m_pFormImpl->SetFormComps( xFormComps ); + + uno::Reference< beans::XPropertySet > xFormPropSet( xForm, UNO_QUERY ); + + Any aTmp; + aTmp <<= sName; + xFormPropSet->setPropertyValue("Name", aTmp ); + + if( !aAction.isEmpty() ) + { + aAction = URIHelper::SmartRel2Abs(INetURLObject(m_sBaseURL), aAction, Link<OUString *, bool>(), false); + } + else + { + // use directory at empty URL + INetURLObject aURLObj( m_aPathToFile ); + aAction = aURLObj.GetPartBeforeLastName(); + } + aTmp <<= aAction; + xFormPropSet->setPropertyValue("TargetURL", + aTmp ); + + aTmp <<= nMethod; + xFormPropSet->setPropertyValue("SubmitMethod", + aTmp ); + + aTmp <<= nEncType; + xFormPropSet->setPropertyValue("SubmitEncoding", aTmp ); + + if( !sTarget.isEmpty() ) + { + aTmp <<= sTarget; + xFormPropSet->setPropertyValue( "TargetFrame", aTmp ); + } + + const uno::Reference< container::XIndexContainer > & rForms = + m_pFormImpl->GetForms(); + Any aAny( &xForm, cppu::UnoType<XForm>::get()); + rForms->insertByIndex( rForms->getCount(), aAny ); + if( !aMacroTable.empty() ) + { + bool bHasEvents = lcl_html_setEvents( m_pFormImpl->GetFormEventManager(), + rForms->getCount() - 1, + aMacroTable, aUnoMacroTable, aUnoMacroParamTable, + rDfltScriptType ); + if (bHasEvents) + NotifyMacroEventRead(); + } +} + +void SwHTMLParser::EndForm( bool bAppend ) +{ + if( m_pFormImpl && m_pFormImpl->GetFormComps().is() ) + { + if( bAppend ) + { + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + } + + m_pFormImpl->ReleaseFormComps(); + } +} + +void SwHTMLParser::InsertInput() +{ + assert(m_vPendingStack.empty()); + + if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() ) + return; + + OUString sImgSrc, aId, aClass, aStyle, sName; + OUString sText; + SvxMacroTableDtor aMacroTable; + std::vector<OUString> aUnoMacroTable; + std::vector<OUString> aUnoMacroParamTable; + sal_uInt16 nSize = 0; + sal_Int16 nMaxLen = 0; + sal_Int16 nChecked = TRISTATE_FALSE; + sal_Int32 nTabIndex = TABINDEX_MAX + 1; + HTMLInputType eType = HTMLInputType::Text; + bool bDisabled = false, bValue = false; + bool bSetGrfWidth = false, bSetGrfHeight = false; + bool bHidden = false; + long nWidth=0, nHeight=0; + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs(); + ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs ); + const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs ); + + HtmlOptionId nKeepCRLFToken = HtmlOptionId::VALUE; + const HTMLOptions& rHTMLOptions = GetOptions( &nKeepCRLFToken ); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + ScriptType eScriptType2 = eDfltScriptType; + SvMacroItemId nEvent = SvMacroItemId::NONE; + bool bSetEvent = false; + + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::TYPE: + eType = rOption.GetInputType(); + break; + case HtmlOptionId::NAME: + sName = rOption.GetString(); + break; + case HtmlOptionId::VALUE: + sText = rOption.GetString(); + bValue = true; + break; + case HtmlOptionId::CHECKED: + nChecked = TRISTATE_TRUE; + break; + case HtmlOptionId::DISABLED: + bDisabled = true; + break; + case HtmlOptionId::MAXLENGTH: + nMaxLen = static_cast<sal_Int16>(rOption.GetNumber()); + break; + case HtmlOptionId::SIZE: + nSize = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::SRC: + sImgSrc = rOption.GetString(); + break; + case HtmlOptionId::WIDTH: + // only save pixel values at first! + nWidth = rOption.GetNumber(); + break; + case HtmlOptionId::HEIGHT: + // only save pixel values at first! + nHeight = rOption.GetNumber(); + break; + case HtmlOptionId::ALIGN: + eVertOri = + rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + eHoriOri = + rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri ); + break; + case HtmlOptionId::TABINDEX: + // only save pixel values at first! + nTabIndex = rOption.GetNumber(); + break; + + case HtmlOptionId::SDONFOCUS: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONFOCUS: + nEvent = SvMacroItemId::HtmlOnGetFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONBLUR: // actually only EDIT + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONBLUR: + nEvent = SvMacroItemId::HtmlOnLoseFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCLICK: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCLICK: + nEvent = SvMacroItemId::HtmlOnClick; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCHANGE: // actually only EDIT + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCHANGE: + nEvent = SvMacroItemId::HtmlOnChange; + bSetEvent = true; + break; + + case HtmlOptionId::SDONSELECT: // actually only EDIT + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONSELECT: + nEvent = SvMacroItemId::HtmlOnSelect; + bSetEvent = true; + break; + + default: + lcl_html_getEvents( rOption.GetTokenString(), + rOption.GetString(), + aUnoMacroTable, aUnoMacroParamTable ); + break; + } + + if( bSetEvent ) + { + OUString sEvent( rOption.GetString() ); + if( !sEvent.isEmpty() ) + { + sEvent = convertLineEnd(sEvent, GetSystemLineEnd()); + OUString aScriptType2; + if( EXTENDED_STYPE==eScriptType2 ) + aScriptType2 = rDfltScriptType; + aMacroTable.Insert( nEvent, SvxMacro( sEvent, aScriptType2, eScriptType2 ) ); + } + } + } + + if( HTMLInputType::Image==eType ) + { + // Image controls without image URL are ignored (same as MS) + if( sImgSrc.isEmpty() ) + return; + } + else + { + // evaluation of ALIGN for all controls is not a good idea as long as + // paragraph bound controls do not influence the height of the cells of a table + eVertOri = text::VertOrientation::TOP; + eHoriOri = text::HoriOrientation::NONE; + } + + // Default is HTMLInputType::Text + const char *pType = "TextField"; + bool bKeepCRLFInValue = false; + switch( eType ) + { + case HTMLInputType::Checkbox: + pType = "CheckBox"; + bKeepCRLFInValue = true; + break; + + case HTMLInputType::Radio: + pType = "RadioButton"; + bKeepCRLFInValue = true; + break; + + case HTMLInputType::Password: + bKeepCRLFInValue = true; + break; + + case HTMLInputType::Button: + bKeepCRLFInValue = true; + [[fallthrough]]; + case HTMLInputType::Submit: + case HTMLInputType::Reset: + pType = "CommandButton"; + break; + + case HTMLInputType::Image: + pType = "ImageButton"; + break; + + case HTMLInputType::File: + pType = "FileControl"; + break; + + case HTMLInputType::Hidden: + pType = "HiddenControl"; + bKeepCRLFInValue = true; + break; + default: + ; + } + + // For some controls CR/LF has to be deleted from VALUE + if( !bKeepCRLFInValue ) + { + sText = sText.replaceAll("\r", "").replaceAll("\n", ""); + } + + const uno::Reference< XMultiServiceFactory > & rServiceFactory = + m_pFormImpl->GetServiceFactory(); + if( !rServiceFactory.is() ) + return; + + OUString sServiceName = "com.sun.star.form.component." + + OUString::createFromAscii(pType); + uno::Reference< XInterface > xInt = + rServiceFactory->createInstance( sServiceName ); + if( !xInt.is() ) + return; + + uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY ); + if( !xFComp.is() ) + return; + + uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY ); + + Any aTmp; + aTmp <<= sName; + xPropSet->setPropertyValue("Name", aTmp ); + + if( HTMLInputType::Hidden != eType ) + { + if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX ) + { + aTmp <<= static_cast<sal_Int16>(nTabIndex) ; + xPropSet->setPropertyValue("TabIndex", aTmp ); + } + + if( bDisabled ) + { + xPropSet->setPropertyValue("Enabled", makeAny(false) ); + } + } + + aTmp <<= sText; + + Size aSz( 0, 0 ); // defaults + Size aTextSz( 0, 0 ); // Text size + bool bMinWidth = false, bMinHeight = false; + bool bUseSize = false; + switch( eType ) + { + case HTMLInputType::Checkbox: + case HTMLInputType::Radio: + { + if( !bValue ) + aTmp <<= OUString( OOO_STRING_SVTOOLS_HTML_on ); + xPropSet->setPropertyValue("RefValue", + aTmp ); + aTmp <<= OUString(); + xPropSet->setPropertyValue("Label", + aTmp ); + // RadioButton: The DefaultChecked property should only be set + // if the control has been created and activateTabOrder has been called + // because otherwise it would still belong to the previous group. + if( HTMLInputType::Checkbox == eType ) + { + aTmp <<= nChecked ; + xPropSet->setPropertyValue("DefaultState", aTmp ); + } + + const SvxMacro* pMacro = aMacroTable.Get( SvMacroItemId::HtmlOnClick ); + if( pMacro ) + { + aMacroTable.Insert( SvMacroItemId::HtmlOnClickItem, *pMacro ); + aMacroTable.Erase( SvMacroItemId::HtmlOnClick ); + } + // evaluating SIZE shouldn't be necessary here? + bMinWidth = bMinHeight = true; + } + break; + + case HTMLInputType::Image: + { + // SIZE = WIDTH + aSz.setWidth( nSize ? nSize : nWidth ); + aSz.setWidth( nWidth ); + aSz.setHeight( nHeight ); + if( (aSz.Width() || aSz.Height()) && Application::GetDefaultDevice() ) + { + aSz = Application::GetDefaultDevice() + ->PixelToLogic( aSz, MapMode( MapUnit::Map100thMM ) ); + } + aTmp <<= FormButtonType_SUBMIT; + xPropSet->setPropertyValue("ButtonType", aTmp ); + + aTmp <<= sal_Int16(0) ; + xPropSet->setPropertyValue("Border", + aTmp ); + } + break; + + case HTMLInputType::Button: + case HTMLInputType::Submit: + case HTMLInputType::Reset: + { + FormButtonType eButtonType; + switch( eType ) + { + case HTMLInputType::Button: + eButtonType = FormButtonType_PUSH; + break; + case HTMLInputType::Submit: + eButtonType = FormButtonType_SUBMIT; + if (sText.isEmpty()) + sText = OOO_STRING_SVTOOLS_HTML_IT_submit; + break; + case HTMLInputType::Reset: + eButtonType = FormButtonType_RESET; + if (sText.isEmpty()) + sText = OOO_STRING_SVTOOLS_HTML_IT_reset; + break; + default: + ; + } + aTmp <<= sText; + xPropSet->setPropertyValue("Label", + aTmp ); + + aTmp <<= eButtonType; + xPropSet->setPropertyValue("ButtonType", aTmp ); + + bMinWidth = bMinHeight = true; + bUseSize = true; + } + break; + + case HTMLInputType::Password: + case HTMLInputType::Text: + case HTMLInputType::File: + if( HTMLInputType::File != eType ) + { + // The VALUE of file control will be ignored for security reasons + xPropSet->setPropertyValue("DefaultText", aTmp ); + if( nMaxLen != 0 ) + { + aTmp <<= nMaxLen ; + xPropSet->setPropertyValue("MaxTextLen", aTmp ); + } + } + + if( HTMLInputType::Password == eType ) + { + aTmp <<= sal_Int16('*') ; + xPropSet->setPropertyValue("EchoChar", aTmp ); + } + + lcl_html_setFixedFontProperty( xPropSet ); + + if( !nSize ) + nSize = 20; + aTextSz.setWidth( nSize ); + bMinHeight = true; + break; + + case HTMLInputType::Hidden: + xPropSet->setPropertyValue("HiddenValue", aTmp ); + bHidden = true; + break; + default: + ; + } + + if( bUseSize && nSize>0 ) + { + if( Application::GetDefaultDevice() ) + { + Size aNewSz( nSize, 0 ); + aNewSz = Application::GetDefaultDevice() + ->PixelToLogic( aNewSz, MapMode( MapUnit::Map100thMM ) ); + aSz.setWidth( aNewSz.Width() ); + OSL_ENSURE( !aTextSz.Width(), "text width is present" ); + bMinWidth = false; + } + } + + SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aCSS1PropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + { + ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo ); + if( !aId.isEmpty() ) + InsertBookmark( aId ); + } + + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType ) + { + aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) ); + aTextSz.setWidth( 0 ); + bMinWidth = false; + } + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType ) + { + aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) ); + aTextSz.setHeight( 0 ); + bMinHeight = false; + } + + // Set sensible default values if the image button has no valid size + if( HTMLInputType::Image== eType ) + { + if( !aSz.Width() ) + { + aSz.setWidth( HTML_DFLT_IMG_WIDTH ); + bSetGrfWidth = true; + if (m_xTable) + IncGrfsThatResizeTable(); + } + if( !aSz.Height() ) + { + aSz.setHeight( HTML_DFLT_IMG_HEIGHT ); + bSetGrfHeight = true; + } + } + if( aSz.Width() < MINFLY ) + aSz.setWidth( MINFLY ); + if( aSz.Height() < MINFLY ) + aSz.setHeight( MINFLY ); + + uno::Reference< drawing::XShape > xShape = InsertControl( + xFComp, xPropSet, aSz, + eVertOri, eHoriOri, + aCSS1ItemSet, aCSS1PropInfo, + aMacroTable, aUnoMacroTable, + aUnoMacroParamTable, false, + bHidden ); + if( aTextSz.Width() || aTextSz.Height() || bMinWidth || bMinHeight ) + { + OSL_ENSURE( !(bSetGrfWidth || bSetGrfHeight), "Adjust graphic size???" ); + SetControlSize( xShape, aTextSz, bMinWidth, bMinHeight ); + } + + if( HTMLInputType::Radio == eType ) + { + aTmp <<= nChecked ; + xPropSet->setPropertyValue("DefaultState", aTmp ); + } + + if( HTMLInputType::Image == eType ) + { + // Set the URL after inserting the graphic because the Download can + // only register with XModel after the control has been inserted. + aTmp <<= URIHelper::SmartRel2Abs(INetURLObject(m_sBaseURL), sImgSrc, Link<OUString *, bool>(), false); + xPropSet->setPropertyValue("ImageURL", + aTmp ); + } + + if( bSetGrfWidth || bSetGrfHeight ) + { + rtl::Reference<SwHTMLImageWatcher> pWatcher = + new SwHTMLImageWatcher( xShape, bSetGrfWidth, bSetGrfHeight ); + pWatcher->start(); + } +} + +void SwHTMLParser::NewTextArea() +{ + assert(m_vPendingStack.empty()); + + OSL_ENSURE( !m_bTextArea, "TextArea in TextArea?" ); + OSL_ENSURE( !m_pFormImpl || !m_pFormImpl->GetFCompPropSet().is(), + "TextArea in Control?" ); + + if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() ) + { + // Close special treatment for TextArea in the parser + FinishTextArea(); + return; + } + + OUString aId, aClass, aStyle; + OUString sName; + sal_Int32 nTabIndex = TABINDEX_MAX + 1; + SvxMacroTableDtor aMacroTable; + std::vector<OUString> aUnoMacroTable; + std::vector<OUString> aUnoMacroParamTable; + sal_uInt16 nRows = 0, nCols = 0; + HTMLWordWrapMode nWrap = HTML_WM_OFF; + bool bDisabled = false; + SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs(); + ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs ); + const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + ScriptType eScriptType2 = eDfltScriptType; + SvMacroItemId nEvent = SvMacroItemId::NONE; + bool bSetEvent = false; + + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::NAME: + sName = rOption.GetString(); + break; + case HtmlOptionId::DISABLED: + bDisabled = true; + break; + case HtmlOptionId::ROWS: + nRows = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::COLS: + nCols = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::WRAP: + nWrap = rOption.GetEnum( aHTMLTextAreaWrapTable, nWrap ); + break; + + case HtmlOptionId::TABINDEX: + nTabIndex = rOption.GetSNumber(); + break; + + case HtmlOptionId::SDONFOCUS: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONFOCUS: + nEvent = SvMacroItemId::HtmlOnGetFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONBLUR: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONBLUR: + nEvent = SvMacroItemId::HtmlOnLoseFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCLICK: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCLICK: + nEvent = SvMacroItemId::HtmlOnClick; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCHANGE: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCHANGE: + nEvent = SvMacroItemId::HtmlOnChange; + bSetEvent = true; + break; + + case HtmlOptionId::SDONSELECT: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONSELECT: + nEvent = SvMacroItemId::HtmlOnSelect; + bSetEvent = true; + break; + + default: + lcl_html_getEvents( rOption.GetTokenString(), + rOption.GetString(), + aUnoMacroTable, aUnoMacroParamTable ); + break; + } + + if( bSetEvent ) + { + OUString sEvent( rOption.GetString() ); + if( !sEvent.isEmpty() ) + { + sEvent = convertLineEnd(sEvent, GetSystemLineEnd()); + if( EXTENDED_STYPE==eScriptType2 ) + m_aScriptType = rDfltScriptType; + aMacroTable.Insert( nEvent, SvxMacro( sEvent, m_aScriptType, eScriptType2 ) ); + } + } + } + + const uno::Reference< lang::XMultiServiceFactory > & rSrvcMgr = + m_pFormImpl->GetServiceFactory(); + if( !rSrvcMgr.is() ) + { + FinishTextArea(); + return; + } + uno::Reference< uno::XInterface > xInt = rSrvcMgr->createInstance( + "com.sun.star.form.component.TextField" ); + if( !xInt.is() ) + { + FinishTextArea(); + return; + } + + uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY ); + OSL_ENSURE( xFComp.is(), "no FormComponent?" ); + + uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY ); + + Any aTmp; + aTmp <<= sName; + xPropSet->setPropertyValue("Name", aTmp ); + + aTmp <<= true; + xPropSet->setPropertyValue("MultiLine", aTmp ); + xPropSet->setPropertyValue("VScroll", aTmp ); + if( HTML_WM_OFF == nWrap ) + xPropSet->setPropertyValue("HScroll", aTmp ); + if( HTML_WM_HARD == nWrap ) + xPropSet->setPropertyValue("HardLineBreaks", aTmp ); + + if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX ) + { + aTmp <<= static_cast<sal_Int16>(nTabIndex) ; + xPropSet->setPropertyValue("TabIndex", aTmp ); + } + + lcl_html_setFixedFontProperty( xPropSet ); + + if( bDisabled ) + { + xPropSet->setPropertyValue("Enabled", makeAny(false) ); + } + + OSL_ENSURE( m_pFormImpl->GetText().isEmpty(), "Text is not empty!" ); + + if( !nCols ) + nCols = 20; + if( !nRows ) + nRows = 1; + + Size aTextSz( nCols, nRows ); + + SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aCSS1PropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + { + ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo ); + if( !aId.isEmpty() ) + InsertBookmark( aId ); + } + + Size aSz( MINFLY, MINFLY ); + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType ) + { + aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) ); + aTextSz.setWidth( 0 ); + } + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType ) + { + aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) ); + aTextSz.setHeight( 0 ); + } + if( aSz.Width() < MINFLY ) + aSz.setWidth( MINFLY ); + if( aSz.Height() < MINFLY ) + aSz.setHeight( MINFLY ); + + uno::Reference< drawing::XShape > xShape = InsertControl( xFComp, xPropSet, aSz, + text::VertOrientation::TOP, text::HoriOrientation::NONE, + aCSS1ItemSet, aCSS1PropInfo, + aMacroTable, aUnoMacroTable, + aUnoMacroParamTable ); + if( aTextSz.Width() || aTextSz.Height() ) + SetControlSize( xShape, aTextSz, false, false ); + + // create new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::TEXTAREA_ON)); + + // temporarily disable PRE/Listing/XMP + SplitPREListingXMP(xCntxt.get()); + PushContext(xCntxt); + + m_bTextArea = true; + m_bTAIgnoreNewPara = true; +} + +void SwHTMLParser::EndTextArea() +{ + OSL_ENSURE( m_bTextArea, "no TextArea or wrong type" ); + OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(), + "TextArea missing" ); + + const uno::Reference< beans::XPropertySet > & rPropSet = + m_pFormImpl->GetFCompPropSet(); + + Any aTmp; + aTmp <<= m_pFormImpl->GetText(); + rPropSet->setPropertyValue("DefaultText", aTmp ); + m_pFormImpl->EraseText(); + + m_pFormImpl->ReleaseFCompPropSet(); + + // get context + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::TEXTAREA_ON)); + if (xCntxt) + { + // end attributes + EndContext(xCntxt.get()); + } + + m_bTextArea = false; +} + +void SwHTMLParser::InsertTextAreaText( HtmlTokenId nToken ) +{ + OSL_ENSURE( m_bTextArea, "no TextArea or wrong type" ); + OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(), + "TextArea missing" ); + + OUString& rText = m_pFormImpl->GetText(); + switch( nToken) + { + case HtmlTokenId::TEXTTOKEN: + rText += aToken; + break; + case HtmlTokenId::NEWPARA: + if( !m_bTAIgnoreNewPara ) + rText += "\n"; + break; + default: + rText += "<"; + rText += sSaveToken; + if( !aToken.isEmpty() ) + { + rText += " "; + rText += aToken; + } + rText += ">"; + } + + m_bTAIgnoreNewPara = false; +} + +void SwHTMLParser::NewSelect() +{ + assert(m_vPendingStack.empty()); + + OSL_ENSURE( !m_bSelect, "Select in Select?" ); + OSL_ENSURE( !m_pFormImpl || !m_pFormImpl->GetFCompPropSet().is(), + "Select in Control?" ); + + if( !m_pFormImpl || !m_pFormImpl->GetFormComps().is() ) + return; + + OUString aId, aClass, aStyle; + OUString sName; + sal_Int32 nTabIndex = TABINDEX_MAX + 1; + SvxMacroTableDtor aMacroTable; + std::vector<OUString> aUnoMacroTable; + std::vector<OUString> aUnoMacroParamTable; + bool bMultiple = false; + bool bDisabled = false; + m_nSelectEntryCnt = 1; + SvKeyValueIterator *pHeaderAttrs = m_pFormImpl->GetHeaderAttrs(); + ScriptType eDfltScriptType = GetScriptType( pHeaderAttrs ); + const OUString& rDfltScriptType = GetScriptTypeString( pHeaderAttrs ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + ScriptType eScriptType2 = eDfltScriptType; + SvMacroItemId nEvent = SvMacroItemId::NONE; + bool bSetEvent = false; + + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::NAME: + sName = rOption.GetString(); + break; + case HtmlOptionId::MULTIPLE: + bMultiple = true; + break; + case HtmlOptionId::DISABLED: + bDisabled = true; + break; + case HtmlOptionId::SIZE: + m_nSelectEntryCnt = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + + case HtmlOptionId::TABINDEX: + nTabIndex = rOption.GetSNumber(); + break; + + case HtmlOptionId::SDONFOCUS: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONFOCUS: + nEvent = SvMacroItemId::HtmlOnGetFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONBLUR: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONBLUR: + nEvent = SvMacroItemId::HtmlOnLoseFocus; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCLICK: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCLICK: + nEvent = SvMacroItemId::HtmlOnClick; + bSetEvent = true; + break; + + case HtmlOptionId::SDONCHANGE: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCHANGE: + nEvent = SvMacroItemId::HtmlOnChange; + bSetEvent = true; + break; + + default: + lcl_html_getEvents( rOption.GetTokenString(), + rOption.GetString(), + aUnoMacroTable, aUnoMacroParamTable ); + break; + } + + if( bSetEvent ) + { + OUString sEvent( rOption.GetString() ); + if( !sEvent.isEmpty() ) + { + sEvent = convertLineEnd(sEvent, GetSystemLineEnd()); + if( EXTENDED_STYPE==eScriptType2 ) + m_aScriptType = rDfltScriptType; + aMacroTable.Insert( nEvent, SvxMacro( sEvent, m_aScriptType, eScriptType2 ) ); + } + } + } + + const uno::Reference< lang::XMultiServiceFactory > & rSrvcMgr = + m_pFormImpl->GetServiceFactory(); + if( !rSrvcMgr.is() ) + { + FinishTextArea(); + return; + } + uno::Reference< uno::XInterface > xInt = rSrvcMgr->createInstance( + "com.sun.star.form.component.ListBox" ); + if( !xInt.is() ) + { + FinishTextArea(); + return; + } + + uno::Reference< XFormComponent > xFComp( xInt, UNO_QUERY ); + OSL_ENSURE(xFComp.is(), "no FormComponent?"); + + uno::Reference< beans::XPropertySet > xPropSet( xFComp, UNO_QUERY ); + + Any aTmp; + aTmp <<= sName; + xPropSet->setPropertyValue("Name", aTmp ); + + if( nTabIndex >= TABINDEX_MIN && nTabIndex <= TABINDEX_MAX ) + { + aTmp <<= static_cast<sal_Int16>(nTabIndex) ; + xPropSet->setPropertyValue("TabIndex", aTmp ); + } + + if( bDisabled ) + { + xPropSet->setPropertyValue("Enabled", makeAny(false) ); + } + + Size aTextSz( 0, 0 ); + bool bMinWidth = true, bMinHeight = true; + if( !bMultiple && 1==m_nSelectEntryCnt ) + { + xPropSet->setPropertyValue("Dropdown", makeAny(true) ); + } + else + { + if( m_nSelectEntryCnt <= 1 ) // 4 lines is default + m_nSelectEntryCnt = 4; + + if( bMultiple ) + { + xPropSet->setPropertyValue("MultiSelection", makeAny(true) ); + } + aTextSz.setHeight( m_nSelectEntryCnt ); + bMinHeight = false; + } + + SfxItemSet aCSS1ItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aCSS1PropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + { + ParseStyleOptions( aStyle, aId, aClass, aCSS1ItemSet, aCSS1PropInfo ); + if( !aId.isEmpty() ) + InsertBookmark( aId ); + } + + Size aSz( MINFLY, MINFLY ); + m_bFixSelectWidth = true; + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eWidthType ) + { + aSz.setWidth( convertTwipToMm100( aCSS1PropInfo.m_nWidth ) ); + m_bFixSelectWidth = false; + bMinWidth = false; + } + if( SVX_CSS1_LTYPE_TWIP== aCSS1PropInfo.m_eHeightType ) + { + aSz.setHeight( convertTwipToMm100( aCSS1PropInfo.m_nHeight ) ); + aTextSz.setHeight( 0 ); + bMinHeight = false; + } + if( aSz.Width() < MINFLY ) + aSz.setWidth( MINFLY ); + if( aSz.Height() < MINFLY ) + aSz.setHeight( MINFLY ); + + uno::Reference< drawing::XShape > xShape = InsertControl( xFComp, xPropSet, aSz, + text::VertOrientation::TOP, text::HoriOrientation::NONE, + aCSS1ItemSet, aCSS1PropInfo, + aMacroTable, aUnoMacroTable, + aUnoMacroParamTable ); + if( m_bFixSelectWidth ) + m_pFormImpl->SetShape( xShape ); + if( aTextSz.Height() || bMinWidth || bMinHeight ) + SetControlSize( xShape, aTextSz, bMinWidth, bMinHeight ); + + // create new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::SELECT_ON)); + + // temporarily disable PRE/Listing/XMP + SplitPREListingXMP(xCntxt.get()); + PushContext(xCntxt); + + m_bSelect = true; +} + +void SwHTMLParser::EndSelect() +{ + assert(m_vPendingStack.empty()); + + OSL_ENSURE( m_bSelect, "no Select" ); + OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(), + "no select control" ); + + const uno::Reference< beans::XPropertySet > & rPropSet = + m_pFormImpl->GetFCompPropSet(); + + size_t nEntryCnt = m_pFormImpl->GetStringList().size(); + if(!m_pFormImpl->GetStringList().empty()) + { + Sequence<OUString> aList( static_cast<sal_Int32>(nEntryCnt) ); + Sequence<OUString> aValueList( static_cast<sal_Int32>(nEntryCnt) ); + OUString *pStrings = aList.getArray(); + OUString *pValues = aValueList.getArray(); + + for(size_t i = 0; i < nEntryCnt; ++i) + { + OUString sText(m_pFormImpl->GetStringList()[i]); + sText = comphelper::string::stripEnd(sText, ' '); + pStrings[i] = sText; + + sText = m_pFormImpl->GetValueList()[i]; + pValues[i] = sText; + } + + rPropSet->setPropertyValue("StringItemList", Any(aList) ); + + rPropSet->setPropertyValue("ListSourceType", Any(ListSourceType_VALUELIST) ); + + rPropSet->setPropertyValue("ListSource", Any(aValueList) ); + + size_t nSelCnt = m_pFormImpl->GetSelectedList().size(); + if( !nSelCnt && 1 == m_nSelectEntryCnt && nEntryCnt ) + { + // In a dropdown list an entry should always be selected. + m_pFormImpl->GetSelectedList().insert( m_pFormImpl->GetSelectedList().begin(), 0 ); + nSelCnt = 1; + } + Sequence<sal_Int16> aSelList( static_cast<sal_Int32>(nSelCnt) ); + sal_Int16 *pSels = aSelList.getArray(); + for(size_t i = 0; i < nSelCnt; ++i) + { + pSels[i] = static_cast<sal_Int16>(m_pFormImpl->GetSelectedList()[i]); + } + rPropSet->setPropertyValue("DefaultSelection", Any(aSelList) ); + + m_pFormImpl->EraseStringList(); + m_pFormImpl->EraseValueList(); + } + + m_pFormImpl->EraseSelectedList(); + + if( m_bFixSelectWidth ) + { + OSL_ENSURE( m_pFormImpl->GetShape().is(), "Shape not saved" ); + Size aTextSz( -1, 0 ); + SetControlSize( m_pFormImpl->GetShape(), aTextSz, false, false ); + } + + m_pFormImpl->ReleaseFCompPropSet(); + + // get context + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::SELECT_ON)); + if (xCntxt) + { + // close attributes + EndContext(xCntxt.get()); + } + + m_bSelect = false; +} + +void SwHTMLParser::InsertSelectOption() +{ + OSL_ENSURE( m_bSelect, "no Select" ); + OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(), + "no Select-Control" ); + + m_bLBEntrySelected = false; + OUString aValue; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + // leave out for now + break; + case HtmlOptionId::SELECTED: + m_bLBEntrySelected = true; + break; + case HtmlOptionId::VALUE: + aValue = rOption.GetString(); + if( aValue.isEmpty() ) + aValue = "$$$empty$$$"; + break; + default: break; + } + } + + sal_uInt16 nEntryCnt = m_pFormImpl->GetStringList().size(); + m_pFormImpl->GetStringList().push_back(OUString()); + m_pFormImpl->GetValueList().push_back(aValue); + if( m_bLBEntrySelected ) + { + m_pFormImpl->GetSelectedList().push_back( nEntryCnt ); + } +} + +void SwHTMLParser::InsertSelectText() +{ + OSL_ENSURE( m_bSelect, "no select" ); + OSL_ENSURE( m_pFormImpl && m_pFormImpl->GetFCompPropSet().is(), + "no select control" ); + + if(!m_pFormImpl->GetStringList().empty()) + { + OUString& rText = m_pFormImpl->GetStringList().back(); + + if( !aToken.isEmpty() && ' '==aToken[ 0 ] ) + { + sal_Int32 nLen = rText.getLength(); + if( !nLen || ' '==rText[nLen-1]) + aToken = aToken.replaceAt( 0, 1, "" ); + } + if( !aToken.isEmpty() ) + rText += aToken; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlform.hxx b/sw/source/filter/html/htmlform.hxx new file mode 100644 index 000000000..5564a7d29 --- /dev/null +++ b/sw/source/filter/html/htmlform.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_FILTER_HTML_HTMLFORM_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLFORM_HXX + +#include <sal/types.h> + +extern const char * aEventListenerTable[]; +extern const char * aEventMethodTable[]; +extern const char * aEventSDOptionTable[]; +extern const char * aEventOptionTable[]; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlforw.cxx b/sw/source/filter/html/htmlforw.cxx new file mode 100644 index 000000000..ab41378d1 --- /dev/null +++ b/sw/source/filter/html/htmlforw.cxx @@ -0,0 +1,1369 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/form/FormSubmitEncoding.hpp> +#include <com/sun/star/form/FormSubmitMethod.hpp> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/awt/XTextLayoutConstrains.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <hintids.hxx> +#include <o3tl/any.hxx> +#include <rtl/math.hxx> +#include <vcl/svapp.hxx> +#include <svl/macitem.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> +#include <svl/urihelper.hxx> +#include <vcl/unohelp.hxx> +#include <svx/svdouno.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <docsh.hxx> +#include <fmtanchr.hxx> +#include <docary.hxx> +#include <viewsh.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include "wrthtml.hxx" +#include "htmlfly.hxx" +#include "htmlform.hxx" +#include <frmfmt.hxx> +#include <frameformats.hxx> +#include <memory> + +using namespace ::com::sun::star; + +const HtmlFrmOpts HTML_FRMOPTS_CONTROL = + HtmlFrmOpts::NONE; +const HtmlFrmOpts HTML_FRMOPTS_CONTROL_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSize | + HtmlFrmOpts::SSpace | + HtmlFrmOpts::BrClear; +const HtmlFrmOpts HTML_FRMOPTS_IMG_CONTROL = + HtmlFrmOpts::Align | + HtmlFrmOpts::BrClear; +const HtmlFrmOpts HTML_FRMOPTS_IMG_CONTROL_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSpace; + +static void lcl_html_outEvents( SvStream& rStrm, + const uno::Reference< form::XFormComponent >& rFormComp, + bool bCfgStarBasic, + rtl_TextEncoding eDestEnc, + OUString *pNonConvertableChars ) +{ + uno::Reference< uno::XInterface > xParentIfc = rFormComp->getParent(); + OSL_ENSURE( xParentIfc.is(), "lcl_html_outEvents: no parent interface" ); + if( !xParentIfc.is() ) + return; + uno::Reference< container::XIndexAccess > xIndexAcc( xParentIfc, uno::UNO_QUERY ); + uno::Reference< script::XEventAttacherManager > xEventManager( xParentIfc, + uno::UNO_QUERY ); + if( !xIndexAcc.is() || !xEventManager.is() ) + return; + + // and search for the position of the ControlModel within + sal_Int32 nCount = xIndexAcc->getCount(), nPos; + for( nPos = 0 ; nPos < nCount; nPos++ ) + { + uno::Any aTmp = xIndexAcc->getByIndex(nPos); + if( auto x1 = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp) ) + + { + if( rFormComp == *x1 ) + break; + } + else if( auto x2 = o3tl::tryAccess<uno::Reference<form::XForm>>(aTmp) ) + { + if( rFormComp == *x2 ) + break; + } + else + { + OSL_ENSURE( false, "lcl_html_outEvents: wrong reflection" ); + } + } + + if( nPos == nCount ) + return; + + const uno::Sequence< script::ScriptEventDescriptor > aDescs = + xEventManager->getScriptEvents( nPos ); + if( !aDescs.hasElements() ) + return; + + for( const script::ScriptEventDescriptor& rDesc : aDescs ) + { + ScriptType eScriptType = EXTENDED_STYPE; + OUString aScriptType( rDesc.ScriptType ); + if( aScriptType.equalsIgnoreAsciiCase(SVX_MACRO_LANGUAGE_JAVASCRIPT) ) + eScriptType = JAVASCRIPT; + else if( aScriptType.equalsIgnoreAsciiCase(SVX_MACRO_LANGUAGE_STARBASIC ) ) + eScriptType = STARBASIC; + if( JAVASCRIPT != eScriptType && !bCfgStarBasic ) + continue; + + OUString sListener( rDesc.ListenerType ); + if (!sListener.isEmpty()) + { + const sal_Int32 nIdx { sListener.lastIndexOf('.')+1 }; + if (nIdx>0) + { + if (nIdx<sListener.getLength()) + { + sListener = sListener.copy(nIdx); + } + else + { + sListener.clear(); + } + } + } + OUString sMethod( rDesc.EventMethod ); + + const char *pOpt = nullptr; + for( int j=0; aEventListenerTable[j]; j++ ) + { + if( sListener.equalsAscii( aEventListenerTable[j] ) && + sMethod.equalsAscii( aEventMethodTable[j] ) ) + { + pOpt = (STARBASIC==eScriptType ? aEventSDOptionTable + : aEventOptionTable)[j]; + break; + } + } + + OString sOut = " "; + if( pOpt && (EXTENDED_STYPE != eScriptType || + rDesc.AddListenerParam.isEmpty()) ) + sOut += OString(pOpt); + else + { + sOut += OOO_STRING_SVTOOLS_HTML_O_sdevent + + OUStringToOString(sListener, RTL_TEXTENCODING_ASCII_US) + "-" + + OUStringToOString(sMethod, RTL_TEXTENCODING_ASCII_US); + } + sOut += "=\""; + rStrm.WriteOString( sOut ); + HTMLOutFuncs::Out_String( rStrm, rDesc.ScriptCode, eDestEnc, pNonConvertableChars ); + rStrm.WriteChar( '\"' ); + if( EXTENDED_STYPE == eScriptType && + !rDesc.AddListenerParam.isEmpty() ) + { + sOut = " " OOO_STRING_SVTOOLS_HTML_O_sdaddparam + + OUStringToOString(sListener, RTL_TEXTENCODING_ASCII_US) + "-" + + OUStringToOString(sMethod, RTL_TEXTENCODING_ASCII_US) + "=\""; + rStrm.WriteOString( sOut ); + HTMLOutFuncs::Out_String( rStrm, rDesc.AddListenerParam, + eDestEnc, pNonConvertableChars ); + rStrm.WriteChar( '\"' ); + } + } +} + +static bool lcl_html_isHTMLControl( sal_Int16 nClassId ) +{ + bool bRet = false; + + switch( nClassId ) + { + case form::FormComponentType::TEXTFIELD: + case form::FormComponentType::COMMANDBUTTON: + case form::FormComponentType::RADIOBUTTON: + case form::FormComponentType::CHECKBOX: + case form::FormComponentType::LISTBOX: + case form::FormComponentType::IMAGEBUTTON: + case form::FormComponentType::FILECONTROL: + bRet = true; + break; + } + + return bRet; +} + +bool SwHTMLWriter::HasControls() const +{ + sal_uInt32 nStartIdx = m_pCurrentPam->GetPoint()->nNode.GetIndex(); + size_t i = 0; + + // Skip all controls in front of the current paragraph + while ( i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx < nStartIdx ) + ++i; + + return i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx == nStartIdx; +} + +void SwHTMLWriter::OutForm( bool bTag_On, const SwStartNode *pStartNd ) +{ + if( m_bPreserveForm ) // we are in a table or an area with form spanned over it + return; + + if( !bTag_On ) + { + // end the form when all controls are output + if( mxFormComps.is() && + mxFormComps->getCount() == m_nFormCntrlCnt ) + { + OutForm( false, mxFormComps ); + mxFormComps.clear(); + } + return; + } + + uno::Reference< container::XIndexContainer > xNewFormComps; + sal_uInt32 nStartIdx = pStartNd ? pStartNd->GetIndex() + : m_pCurrentPam->GetPoint()->nNode.GetIndex(); + + // skip controls before the interesting area + size_t i = 0; + while ( i < m_aHTMLControls.size() && m_aHTMLControls[i]->nNdIdx < nStartIdx ) + ++i; + + if( !pStartNd ) + { + // Check for a single node: there it's only interesting, if there is + // a control for the node and to which form it belongs. + if( i < m_aHTMLControls.size() && + m_aHTMLControls[i]->nNdIdx == nStartIdx ) + xNewFormComps = m_aHTMLControls[i]->xFormComps; + } + else + { + // we iterate over a table/an area: we're interested in: + // - if there are controls with different start nodes + // - if there is a form, with controls which aren't all in the table/area + + uno::Reference< container::XIndexContainer > xCurrentFormComps;// current form in table + const SwStartNode *pCurrentStNd = nullptr; // and the start node of a Control + sal_Int32 nCurrentCtrls = 0; // and the found controls in it + sal_uInt32 nEndIdx = pStartNd->EndOfSectionIndex(); + for( ; i < m_aHTMLControls.size() && + m_aHTMLControls[i]->nNdIdx <= nEndIdx; i++ ) + { + const SwStartNode *pCntrlStNd = + m_pDoc->GetNodes()[m_aHTMLControls[i]->nNdIdx]->StartOfSectionNode(); + + if( xCurrentFormComps.is() ) + { + // already inside a form ... + if( xCurrentFormComps==m_aHTMLControls[i]->xFormComps ) + { + // ... and the control is also inside ... + if( pCurrentStNd!=pCntrlStNd ) + { + // ... but it's inside another cell: + // Then open a form above the table + xNewFormComps = xCurrentFormComps; + break; + } + nCurrentCtrls = nCurrentCtrls + m_aHTMLControls[i]->nCount; + } + else + { + // ... but the Control is in another cell: + // There we act as if we open a new from and continue searching. + xCurrentFormComps = m_aHTMLControls[i]->xFormComps; + pCurrentStNd = pCntrlStNd; + nCurrentCtrls = m_aHTMLControls[i]->nCount; + } + } + else + { + // We aren't in a form: + // There we act as if we open a form. + xCurrentFormComps = m_aHTMLControls[i]->xFormComps; + pCurrentStNd = pCntrlStNd; + nCurrentCtrls = m_aHTMLControls[i]->nCount; + } + } + if( !xNewFormComps.is() && xCurrentFormComps.is() && + nCurrentCtrls != xCurrentFormComps->getCount() ) + { + // A form should be opened in the table/area which isn't completely + // inside the table. Then we must also now open the form. + xNewFormComps = xCurrentFormComps; + } + } + + if( xNewFormComps.is() && + (!mxFormComps.is() || xNewFormComps != mxFormComps) ) + { + // A form should be opened ... + if( mxFormComps.is() ) + { + // ... but a form is still open: That is in every case an error, + // but we'll close the old form nevertheless. + OutForm( false, mxFormComps ); + + //!!!nWarn = 1; // Control will be assigned to wrong form + } + + mxFormComps = xNewFormComps; + + OutForm( true, mxFormComps ); + uno::Reference< beans::XPropertySet > xTmp; + OutHiddenControls( mxFormComps, xTmp ); + } +} + +void SwHTMLWriter::OutHiddenForms() +{ + // Without DrawModel there can't be controls. Then you also can't access the + // document via UNO, because otherwise a DrawModel would be created. + if( !m_pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + return; + + SwDocShell *pDocSh = m_pDoc->GetDocShell(); + if( !pDocSh ) + return; + + uno::Reference< drawing::XDrawPageSupplier > xDPSupp( pDocSh->GetBaseModel(), + uno::UNO_QUERY ); + OSL_ENSURE( xDPSupp.is(), "XTextDocument not received from XModel" ); + uno::Reference< drawing::XDrawPage > xDrawPage = xDPSupp->getDrawPage(); + + OSL_ENSURE( xDrawPage.is(), "XDrawPage not received" ); + if( !xDrawPage.is() ) + return; + + uno::Reference< form::XFormsSupplier > xFormsSupplier( xDrawPage, uno::UNO_QUERY ); + OSL_ENSURE( xFormsSupplier.is(), + "XFormsSupplier not received from XDrawPage" ); + + uno::Reference< container::XNameContainer > xTmp = xFormsSupplier->getForms(); + OSL_ENSURE( xTmp.is(), "XForms not received" ); + uno::Reference< container::XIndexContainer > xForms( xTmp, uno::UNO_QUERY ); + OSL_ENSURE( xForms.is(), "XForms without container::XIndexContainer?" ); + + sal_Int32 nCount = xForms->getCount(); + for( sal_Int32 i=0; i<nCount; i++) + { + uno::Any aTmp = xForms->getByIndex( i ); + if( auto x = o3tl::tryAccess<uno::Reference<form::XForm>>(aTmp) ) + OutHiddenForm( *x ); + else + { + OSL_ENSURE( false, "OutHiddenForms: wrong reflection" ); + } + } +} + +void SwHTMLWriter::OutHiddenForm( const uno::Reference< form::XForm > & rForm ) +{ + uno::Reference< container::XIndexContainer > xFormComps( rForm, uno::UNO_QUERY ); + if( !xFormComps.is() ) + return; + + sal_Int32 nCount = xFormComps->getCount(); + bool bHiddenOnly = nCount > 0, bHidden = false; + for( sal_Int32 i=0; i<nCount; i++ ) + { + uno::Any aTmp = xFormComps->getByIndex( i ); + auto xFormComp = o3tl::tryAccess<uno::Reference<form::XFormComponent>>( + aTmp); + OSL_ENSURE( xFormComp, "OutHiddenForm: wrong reflection" ); + if( !xFormComp ) + continue; + + uno::Reference< form::XForm > xForm( *xFormComp, uno::UNO_QUERY ); + if( xForm.is() ) + OutHiddenForm( xForm ); + + if( bHiddenOnly ) + { + uno::Reference< beans::XPropertySet > xPropSet( *xFormComp, uno::UNO_QUERY ); + OUString sPropName("ClassId"); + if( xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) ) + { + uno::Any aAny2 = xPropSet->getPropertyValue( sPropName ); + if( auto n = o3tl::tryAccess<sal_Int16>(aAny2) ) + { + if( form::FormComponentType::HIDDENCONTROL == *n ) + bHidden = true; + else if( lcl_html_isHTMLControl( *n ) ) + bHiddenOnly = false; + } + } + } + } + + if( bHidden && bHiddenOnly ) + { + OutForm( true, xFormComps ); + uno::Reference< beans::XPropertySet > xTmp; + OutHiddenControls( xFormComps, xTmp ); + OutForm( false, xFormComps ); + } +} + +void SwHTMLWriter::OutForm( bool bOn, + const uno::Reference< container::XIndexContainer > & rFormComps ) +{ + m_nFormCntrlCnt = 0; + + if( !bOn ) + { + DecIndentLevel(); // indent content of form + if( m_bLFPossible ) + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_form, false ); + m_bLFPossible = true; + + return; + } + + // the new form is opened + if( m_bLFPossible ) + OutNewLine(); + OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_form; + + uno::Reference< beans::XPropertySet > xFormPropSet( rFormComps, uno::UNO_QUERY ); + + uno::Any aTmp = xFormPropSet->getPropertyValue( "Name" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\""; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( Strm(), *s, + m_eDestEnc, &m_aNonConvertableCharacters ); + sOut = "\""; + } + } + + aTmp = xFormPropSet->getPropertyValue( "TargetURL" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if ( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_action "=\""; + Strm().WriteOString( sOut ); + OUString aURL + = URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), *s); + HTMLOutFuncs::Out_String( Strm(), aURL, m_eDestEnc, &m_aNonConvertableCharacters ); + sOut = "\""; + } + } + + aTmp = xFormPropSet->getPropertyValue( "SubmitMethod" ); + if( auto eMethod = o3tl::tryAccess<form::FormSubmitMethod>(aTmp) ) + { + if( form::FormSubmitMethod_POST==*eMethod ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_method "=\"" + OOO_STRING_SVTOOLS_HTML_METHOD_post "\""; + } + } + aTmp = xFormPropSet->getPropertyValue( "SubmitEncoding" ); + if( auto eEncType = o3tl::tryAccess<form::FormSubmitEncoding>(aTmp) ) + { + const char *pStr = nullptr; + switch( *eEncType ) + { + case form::FormSubmitEncoding_MULTIPART: + pStr = OOO_STRING_SVTOOLS_HTML_ET_multipart; + break; + case form::FormSubmitEncoding_TEXT: + pStr = OOO_STRING_SVTOOLS_HTML_ET_text; + break; + default: + ; + } + + if( pStr ) + { + sOut += OStringLiteral(" " OOO_STRING_SVTOOLS_HTML_O_enctype "=\"") + + pStr + "\""; + } + } + + aTmp = xFormPropSet->getPropertyValue( "TargetFrame" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if (!s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_target "=\""; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( Strm(), *s, + m_eDestEnc, &m_aNonConvertableCharacters ); + sOut = "\""; + } + } + + Strm().WriteOString( sOut ); + uno::Reference< form::XFormComponent > xFormComp( rFormComps, uno::UNO_QUERY ); + lcl_html_outEvents( Strm(), xFormComp, m_bCfgStarBasic, m_eDestEnc, &m_aNonConvertableCharacters ); + Strm().WriteChar( '>' ); + + IncIndentLevel(); // indent content of form + m_bLFPossible = true; +} + +void SwHTMLWriter::OutHiddenControls( + const uno::Reference< container::XIndexContainer > & rFormComps, + const uno::Reference< beans::XPropertySet > & rPropSet ) +{ + sal_Int32 nCount = rFormComps->getCount(); + sal_Int32 nPos = 0; + if( rPropSet.is() ) + { + bool bDone = false; + + uno::Reference< form::XFormComponent > xFC( rPropSet, uno::UNO_QUERY ); + for( nPos=0; !bDone && nPos < nCount; nPos++ ) + { + uno::Any aTmp = rFormComps->getByIndex( nPos ); + auto x = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp); + OSL_ENSURE( x, + "OutHiddenControls: wrong reflection" ); + bDone = x && *x == xFC; + } + } + + for( ; nPos < nCount; nPos++ ) + { + uno::Any aTmp = rFormComps->getByIndex( nPos ); + auto xFC = o3tl::tryAccess<uno::Reference<form::XFormComponent>>(aTmp); + OSL_ENSURE( xFC, + "OutHiddenControls: wrong reflection" ); + if( !xFC ) + continue; + uno::Reference< beans::XPropertySet > xPropSet( *xFC, uno::UNO_QUERY ); + + OUString sPropName = "ClassId"; + if( !xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) ) + continue; + + aTmp = xPropSet->getPropertyValue( sPropName ); + auto n = o3tl::tryAccess<sal_Int16>(aTmp); + if( !n ) + continue; + + if( form::FormComponentType::HIDDENCONTROL == *n ) + { + if( m_bLFPossible ) + OutNewLine( true ); + OString sOut = "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_input " " + OOO_STRING_SVTOOLS_HTML_O_type "=\"" + OOO_STRING_SVTOOLS_HTML_IT_hidden "\""; + + aTmp = xPropSet->getPropertyValue( "Name" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\""; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( Strm(), *s, + m_eDestEnc, &m_aNonConvertableCharacters ); + sOut = "\""; + } + } + aTmp = xPropSet->getPropertyValue( "HiddenValue" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\""; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( Strm(), *s, + m_eDestEnc, &m_aNonConvertableCharacters ); + sOut = "\""; + } + } + sOut += ">"; + Strm().WriteOString( sOut ); + + m_nFormCntrlCnt++; + } + else if( lcl_html_isHTMLControl( *n ) ) + { + break; + } + } +} + +// here are the output routines, thus the form::Forms are bundled: + +const SdrObject *SwHTMLWriter::GetHTMLControl( const SwDrawFrameFormat& rFormat ) +{ + // it must be a Draw-Format + OSL_ENSURE( RES_DRAWFRMFMT == rFormat.Which(), + "GetHTMLControl only allow for Draw-Formats" ); + + // Look if a SdrObject exists for it + const SdrObject *pObj = rFormat.FindSdrObject(); + if( !pObj || SdrInventor::FmForm != pObj->GetObjInventor() ) + return nullptr; + + const SdrUnoObj& rFormObj = dynamic_cast<const SdrUnoObj&>(*pObj); + const uno::Reference< awt::XControlModel >& xControlModel = + rFormObj.GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without model" ); + if( !xControlModel.is() ) + return nullptr; + + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + + OUString sPropName("ClassId"); + if( !xPropSet->getPropertySetInfo()->hasPropertyByName( sPropName ) ) + return nullptr; + + uno::Any aTmp = xPropSet->getPropertyValue( sPropName ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + if( lcl_html_isHTMLControl( *n ) ) + { + return pObj; + } + } + + return nullptr; +} + +static void GetControlSize(const SdrUnoObj& rFormObj, Size& rSz, SwDoc *pDoc) +{ + SwViewShell *pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pVSh ) + return; + + uno::Reference< awt::XControl > xControl; + SdrView* pDrawView = pVSh->GetDrawView(); + OSL_ENSURE( pDrawView && pVSh->GetWin(), "no DrawView or window!" ); + if ( pDrawView && pVSh->GetWin() ) + xControl = rFormObj.GetUnoControl( *pDrawView, *pVSh->GetWin() ); + uno::Reference< awt::XTextLayoutConstrains > xLC( xControl, uno::UNO_QUERY ); + OSL_ENSURE( xLC.is(), "no XTextLayoutConstrains" ); + if( !xLC.is() ) + return; + + sal_Int16 nCols=0, nLines=0; + xLC->getColumnsAndLines( nCols, nLines ); + rSz.setWidth( nCols ); + rSz.setHeight( nLines ); +} + +Writer& OutHTML_DrawFrameFormatAsControl( Writer& rWrt, + const SwDrawFrameFormat& rFormat, + const SdrUnoObj& rFormObj, + bool bInCntnr ) +{ + const uno::Reference< awt::XControlModel >& xControlModel = + rFormObj.GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without model" ); + if( !xControlModel.is() ) + return rWrt; + + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = + xPropSet->getPropertySetInfo(); + + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + rHTMLWrt.m_nFormCntrlCnt++; + + enum Tag { TAG_INPUT, TAG_SELECT, TAG_TEXTAREA, TAG_NONE }; + static char const * const TagNames[] = { + OOO_STRING_SVTOOLS_HTML_input, OOO_STRING_SVTOOLS_HTML_select, + OOO_STRING_SVTOOLS_HTML_textarea }; + Tag eTag = TAG_INPUT; + enum Type { + TYPE_TEXT, TYPE_PASSWORD, TYPE_CHECKBOX, TYPE_RADIO, TYPE_FILE, + TYPE_SUBMIT, TYPE_IMAGE, TYPE_RESET, TYPE_BUTTON, TYPE_NONE }; + static char const * const TypeNames[] = { + OOO_STRING_SVTOOLS_HTML_IT_text, OOO_STRING_SVTOOLS_HTML_IT_password, + OOO_STRING_SVTOOLS_HTML_IT_checkbox, OOO_STRING_SVTOOLS_HTML_IT_radio, + OOO_STRING_SVTOOLS_HTML_IT_file, OOO_STRING_SVTOOLS_HTML_IT_submit, + OOO_STRING_SVTOOLS_HTML_IT_image, OOO_STRING_SVTOOLS_HTML_IT_reset, + OOO_STRING_SVTOOLS_HTML_IT_button }; + Type eType = TYPE_NONE; + OUString sValue; + OString sOptions; + bool bEmptyValue = false; + uno::Any aTmp = xPropSet->getPropertyValue( "ClassId" ); + sal_Int16 nClassId = *o3tl::doAccess<sal_Int16>(aTmp); + HtmlFrmOpts nFrameOpts = HTML_FRMOPTS_CONTROL; + switch( nClassId ) + { + case form::FormComponentType::CHECKBOX: + case form::FormComponentType::RADIOBUTTON: + eType = (form::FormComponentType::CHECKBOX == nClassId + ? TYPE_CHECKBOX : TYPE_RADIO); + aTmp = xPropSet->getPropertyValue( "DefaultState" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + if ( TRISTATE_FALSE != *n ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_checked "=\"" + OOO_STRING_SVTOOLS_HTML_O_checked + "\""; + } + } + + aTmp = xPropSet->getPropertyValue( "RefValue" ); + if( auto rVal = o3tl::tryAccess<OUString>(aTmp) ) + + { + if( rVal->isEmpty() ) + bEmptyValue = true; + else if( *rVal != OOO_STRING_SVTOOLS_HTML_on ) + sValue = *rVal; + } + break; + + case form::FormComponentType::COMMANDBUTTON: + { + form::FormButtonType eButtonType = form::FormButtonType_PUSH; + aTmp = xPropSet->getPropertyValue( "ButtonType" ); + if( auto t = o3tl::tryAccess<form::FormButtonType>(aTmp) ) + eButtonType = *t; + + switch( eButtonType ) + { + case form::FormButtonType_RESET: + eType = TYPE_RESET; + break; + case form::FormButtonType_SUBMIT: + eType = TYPE_SUBMIT; + break; + case form::FormButtonType_PUSH: + default: + eType = TYPE_BUTTON; + } + + aTmp = xPropSet->getPropertyValue( "Label" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sValue = *s; + } + } + } + break; + + case form::FormComponentType::LISTBOX: + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + eTag = TAG_SELECT; + aTmp = xPropSet->getPropertyValue( "Dropdown" ); + if( auto b1 = o3tl::tryAccess<bool>(aTmp) ) + { + if( !*b1 ) + { + Size aSz( 0, 0 ); + GetControlSize( rFormObj, aSz, rWrt.m_pDoc ); + + // How many are visible ?? + if( aSz.Height() ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" + + OString::number(static_cast<sal_Int32>(aSz.Height())) + "\""; + } + + auto aTmp2 = xPropSet->getPropertyValue( "MultiSelection" ); + if( auto b2 = o3tl::tryAccess<bool>(aTmp2) ) + { + if ( *b2 ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_multiple; + } + } + } + } + break; + + case form::FormComponentType::TEXTFIELD: + { + Size aSz( 0, 0 ); + GetControlSize( rFormObj, aSz, rWrt.m_pDoc ); + + bool bMultiLine = false; + OUString sMultiLine("MultiLine"); + if( xPropSetInfo->hasPropertyByName( sMultiLine ) ) + { + aTmp = xPropSet->getPropertyValue( sMultiLine ); + auto b = o3tl::tryAccess<bool>(aTmp); + bMultiLine = b && *b; + } + + if( bMultiLine ) + { + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + eTag = TAG_TEXTAREA; + + if( aSz.Height() ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_rows "=\"" + + OString::number(static_cast<sal_Int32>(aSz.Height())) + "\""; + } + if( aSz.Width() ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_cols "=\"" + + OString::number(static_cast<sal_Int32>(aSz.Width())) + "\""; + } + + aTmp = xPropSet->getPropertyValue( "HScroll" ); + if( aTmp.getValueType() == cppu::UnoType<void>::get() || + (aTmp.getValueType() == cppu::UnoType<bool>::get() && + !*o3tl::forceAccess<bool>(aTmp)) ) + { + const char *pWrapStr = nullptr; + auto aTmp2 = xPropSet->getPropertyValue( "HardLineBreaks" ); + auto b = o3tl::tryAccess<bool>(aTmp2); + pWrapStr = (b && *b) ? OOO_STRING_SVTOOLS_HTML_WW_hard + : OOO_STRING_SVTOOLS_HTML_WW_soft; + sOptions += OStringLiteral(" " OOO_STRING_SVTOOLS_HTML_O_wrap "=\"") + + pWrapStr + "\""; + } + } + else + { + eType = TYPE_TEXT; + OUString sEchoChar("EchoChar"); + if( xPropSetInfo->hasPropertyByName( sEchoChar ) ) + { + aTmp = xPropSet->getPropertyValue( sEchoChar ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + if( *n != 0 ) + eType = TYPE_PASSWORD; + } + } + + if( aSz.Width() ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" + + OString::number(static_cast<sal_Int32>(aSz.Width())) + "\""; + } + + aTmp = xPropSet->getPropertyValue( "MaxTextLen" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + if( *n != 0 ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_maxlength "=\"" + + OString::number(static_cast<sal_Int32>(*n)) + "\""; + } + } + + if( xPropSetInfo->hasPropertyByName( "DefaultText" ) ) + { + aTmp = xPropSet->getPropertyValue( "DefaultText" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sValue = *s; + } + } + } + } + } + break; + + case form::FormComponentType::FILECONTROL: + { + Size aSz( 0, 0 ); + GetControlSize( rFormObj, aSz, rWrt.m_pDoc ); + eType = TYPE_FILE; + + if( aSz.Width() ) + { + sOptions += " " OOO_STRING_SVTOOLS_HTML_O_size "=\"" + + OString::number(static_cast<sal_Int32>(aSz.Width())) + "\""; + } + + // VALUE vim form: don't export because of security reasons + } + break; + + case form::FormComponentType::IMAGEBUTTON: + eType = TYPE_IMAGE; + nFrameOpts = HTML_FRMOPTS_IMG_CONTROL; + break; + + default: // doesn't know HTML + eTag = TAG_NONE; // therefore skip it + break; + } + + if( eTag == TAG_NONE ) + return rWrt; + + OString sOut = OStringLiteral("<") + TagNames[eTag]; + if( eType != TYPE_NONE ) + { + sOut += OStringLiteral(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"") + + TypeNames[eType] + "\""; + } + + aTmp = xPropSet->getPropertyValue("Name"); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_name "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), *s, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + } + + aTmp = xPropSet->getPropertyValue("Enabled"); + if( auto b = o3tl::tryAccess<bool>(aTmp) ) + { + if( !*b ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_disabled; + } + } + + if( !sValue.isEmpty() || bEmptyValue ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sValue, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + + sOut += " " + sOptions; + + if( TYPE_IMAGE == eType ) + { + aTmp = xPropSet->getPropertyValue( "ImageURL" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_src "=\""; + rWrt.Strm().WriteOString( sOut ); + + HTMLOutFuncs::Out_String( rWrt.Strm(), + URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), *s), + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + } + + Size aTwipSz( rFormObj.GetLogicRect().GetSize() ); + Size aPixelSz( 0, 0 ); + if( (aTwipSz.Width() || aTwipSz.Height()) && + Application::GetDefaultDevice() ) + { + aPixelSz = + Application::GetDefaultDevice()->LogicToPixel( aTwipSz, + MapMode(MapUnit::MapTwip) ); + if( !aPixelSz.Width() && aTwipSz.Width() ) + aPixelSz.setWidth( 1 ); + if( !aPixelSz.Height() && aTwipSz.Height() ) + aPixelSz.setHeight( 1 ); + } + + if( aPixelSz.Width() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_width "=\"" + + OString::number(static_cast<sal_Int32>(aPixelSz.Width())) + "\""; + } + + if( aPixelSz.Height() ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_height "=\"" + + OString::number(static_cast<sal_Int32>(aPixelSz.Height())) + "\""; + } + } + + aTmp = xPropSet->getPropertyValue( "TabIndex" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + sal_Int16 nTabIndex = *n; + if( nTabIndex > 0 ) + { + if( nTabIndex >= 32767 ) + nTabIndex = 32767; + + sOut += " " OOO_STRING_SVTOOLS_HTML_O_tabindex "=\"" + + OString::number(static_cast<sal_Int32>(nTabIndex)) + "\""; + } + } + + if( !sOut.isEmpty() ) + rWrt.Strm().WriteOString( sOut ); + + OSL_ENSURE( !bInCntnr, "Container is not supported for Controls" ); + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_DRAW ) && !bInCntnr ) + { + // If Character-Objects can't be positioned absolutely, + // then delete the corresponding flag. + nFrameOpts |= (TYPE_IMAGE == eType + ? HTML_FRMOPTS_IMG_CONTROL_CSS1 + : HTML_FRMOPTS_CONTROL_CSS1); + } + OString aEndTags; + if( nFrameOpts != HtmlFrmOpts::NONE ) + aEndTags = rHTMLWrt.OutFrameFormatOptions(rFormat, OUString(), nFrameOpts); + + if( rHTMLWrt.m_bCfgOutStyles ) + { + bool bEdit = TAG_TEXTAREA == eTag || TYPE_FILE == eType || + TYPE_TEXT == eType; + + SfxItemSet aItemSet( rHTMLWrt.m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, + RES_CHRATR_END>{} ); + if( xPropSetInfo->hasPropertyByName( "BackgroundColor" ) ) + { + aTmp = xPropSet->getPropertyValue( "BackgroundColor" ); + if( auto n = o3tl::tryAccess<sal_Int32>(aTmp) ) + { + Color aCol(*n); + aItemSet.Put( SvxBrushItem( aCol, RES_CHRATR_BACKGROUND ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "TextColor" ) ) + { + aTmp = xPropSet->getPropertyValue( "TextColor" ); + if( auto n = o3tl::tryAccess<sal_Int32>(aTmp) ) + { + Color aColor( *n ); + aItemSet.Put( SvxColorItem( aColor, RES_CHRATR_COLOR ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "FontHeight" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontHeight" ); + if( auto nHeight = o3tl::tryAccess<float>(aTmp) ) + + { + if( *nHeight > 0 && (!bEdit || !rtl::math::approxEqual(*nHeight, 10.0)) ) + aItemSet.Put( SvxFontHeightItem( sal_Int16(*nHeight * 20.), 100, RES_CHRATR_FONTSIZE ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "FontName" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontName" ); + if( auto aFName = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !aFName->isEmpty() ) + { + vcl::Font aFixedFont( OutputDevice::GetDefaultFont( + DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, + GetDefaultFontFlags::OnlyOne ) ); + if( !bEdit || *aFName != aFixedFont.GetFamilyName() ) + { + FontFamily eFamily = FAMILY_DONTKNOW; + if( xPropSetInfo->hasPropertyByName( "FontFamily" ) ) + { + auto aTmp2 = xPropSet->getPropertyValue( "FontFamily" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp2) ) + eFamily = static_cast<FontFamily>(*n); + } + SvxFontItem aItem(eFamily, *aFName, OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT); + aItemSet.Put( aItem ); + } + } + } + } + if( xPropSetInfo->hasPropertyByName( "FontWeight" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontWeight" ); + if( auto x = o3tl::tryAccess<float>(aTmp) ) + { + FontWeight eWeight = + vcl::unohelper::ConvertFontWeight( *x ); + if( eWeight != WEIGHT_DONTKNOW && eWeight != WEIGHT_NORMAL ) + aItemSet.Put( SvxWeightItem( eWeight, RES_CHRATR_WEIGHT ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "FontSlant" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontSlant" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + FontItalic eItalic = static_cast<FontItalic>(*n); + if( eItalic != ITALIC_DONTKNOW && eItalic != ITALIC_NONE ) + aItemSet.Put( SvxPostureItem( eItalic, RES_CHRATR_POSTURE ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "FontLineStyle" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontLineStyle" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + FontLineStyle eUnderline = static_cast<FontLineStyle>(*n); + if( eUnderline != LINESTYLE_DONTKNOW && + eUnderline != LINESTYLE_NONE ) + aItemSet.Put( SvxUnderlineItem( eUnderline, RES_CHRATR_UNDERLINE ) ); + } + } + if( xPropSetInfo->hasPropertyByName( "FontStrikeout" ) ) + { + aTmp = xPropSet->getPropertyValue( "FontStrikeout" ); + if( auto n = o3tl::tryAccess<sal_Int16>(aTmp) ) + { + FontStrikeout eStrikeout = static_cast<FontStrikeout>(*n); + if( eStrikeout != STRIKEOUT_DONTKNOW && + eStrikeout != STRIKEOUT_NONE ) + aItemSet.Put( SvxCrossedOutItem( eStrikeout, RES_CHRATR_CROSSEDOUT ) ); + } + } + + rHTMLWrt.OutCSS1_FrameFormatOptions( rFormat, nFrameOpts, &rFormObj, + &aItemSet ); + } + + uno::Reference< form::XFormComponent > xFormComp( xControlModel, uno::UNO_QUERY ); + lcl_html_outEvents( rWrt.Strm(), xFormComp, rHTMLWrt.m_bCfgStarBasic, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + + rWrt.Strm().WriteChar( '>' ); + + if( TAG_SELECT == eTag ) + { + aTmp = xPropSet->getPropertyValue( "StringItemList" ); + if( auto aList = o3tl::tryAccess<uno::Sequence<OUString>>(aTmp) ) + { + rHTMLWrt.IncIndentLevel(); // the content of Select can be indented + sal_Int32 nCnt = aList->getLength(); + const OUString *pStrings = aList->getConstArray(); + + const OUString *pValues = nullptr; + sal_Int32 nValCnt = 0; + auto aTmp2 = xPropSet->getPropertyValue( "ListSource" ); + uno::Sequence<OUString> aValList; + if( auto s = o3tl::tryAccess<uno::Sequence<OUString>>(aTmp2) ) + { + aValList = *s; + nValCnt = aValList.getLength(); + pValues = aValList.getConstArray(); + } + + uno::Any aSelTmp = xPropSet->getPropertyValue( "DefaultSelection" ); + const sal_Int16 *pSels = nullptr; + sal_Int32 nSel = 0; + sal_Int32 nSelCnt = 0; + uno::Sequence<sal_Int16> aSelList; + if( auto s = o3tl::tryAccess<uno::Sequence<sal_Int16>>(aSelTmp) ) + { + aSelList = *s; + nSelCnt = aSelList.getLength(); + pSels = aSelList.getConstArray(); + } + + for( sal_Int32 i = 0; i < nCnt; i++ ) + { + OUString sVal; + bool bSelected = false, bEmptyVal = false; + if( i < nValCnt ) + { + const OUString& rVal = pValues[i]; + if( rVal == "$$$empty$$$" ) + bEmptyVal = true; + else + sVal = rVal; + } + + bSelected = (nSel < nSelCnt) && pSels[nSel] == i; + if( bSelected ) + nSel++; + + rHTMLWrt.OutNewLine(); // every Option gets its own line + sOut = "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_option; + if( !sVal.isEmpty() || bEmptyVal ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_value "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sVal, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut = "\""; + } + if( bSelected ) + sOut += " " OOO_STRING_SVTOOLS_HTML_O_selected; + + sOut += ">"; + rWrt.Strm().WriteOString( sOut ); + + HTMLOutFuncs::Out_String( rWrt.Strm(), pStrings[i], + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_option, false ); + + rHTMLWrt.DecIndentLevel(); + rHTMLWrt.OutNewLine();// the </SELECT> gets its own line + } + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_select, false ); + } + else if( TAG_TEXTAREA == eTag ) + { + // In TextAreas no additional spaces or LF may be exported! + OUString sVal; + aTmp = xPropSet->getPropertyValue( "DefaultText" ); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + sVal = *s; + } + } + if( !sVal.isEmpty() ) + { + sVal = convertLineEnd(sVal, LINEEND_LF); + sal_Int32 nPos = 0; + while ( nPos != -1 ) + { + if( nPos ) + rWrt.Strm().WriteCharPtr( SAL_NEWLINE_STRING ); + OUString aLine = sVal.getToken( 0, 0x0A, nPos ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aLine, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + } + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_textarea, false ); + } + else if( TYPE_CHECKBOX == eType || TYPE_RADIO == eType ) + { + aTmp = xPropSet->getPropertyValue("Label"); + if( auto s = o3tl::tryAccess<OUString>(aTmp) ) + { + if( !s->isEmpty() ) + { + HTMLOutFuncs::Out_String( rWrt.Strm(), *s, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ).WriteChar( ' ' ); + } + } + } + + if( !aEndTags.isEmpty() ) + rWrt.Strm().WriteOString( aEndTags ); + + // Controls aren't bound to a paragraph, therefore don't output LF anymore! + rHTMLWrt.m_bLFPossible = false; + + if( rHTMLWrt.mxFormComps.is() ) + rHTMLWrt.OutHiddenControls( rHTMLWrt.mxFormComps, xPropSet ); + return rWrt; +} + +/** + * Find out if a format belongs to a control and if yes return its form. + */ +static void AddControl( HTMLControls& rControls, + const SdrUnoObj& rFormObj, + sal_uInt32 nNodeIdx ) +{ + const uno::Reference< awt::XControlModel >& xControlModel = + rFormObj.GetUnoControlModel(); + if( !xControlModel.is() ) + return; + + uno::Reference< form::XFormComponent > xFormComp( xControlModel, uno::UNO_QUERY ); + uno::Reference< uno::XInterface > xIfc = xFormComp->getParent(); + uno::Reference< form::XForm > xForm(xIfc, uno::UNO_QUERY); + + OSL_ENSURE( xForm.is(), "Where is the form?" ); + if( xForm.is() ) + { + uno::Reference< container::XIndexContainer > xFormComps( xForm, uno::UNO_QUERY ); + std::unique_ptr<HTMLControl> pHCntrl(new HTMLControl( xFormComps, nNodeIdx )); + auto itPair = rControls.insert( std::move(pHCntrl) ); + if (!itPair.second ) + { + if( (*itPair.first)->xFormComps==xFormComps ) + (*itPair.first)->nCount++; + } + } +} + +void SwHTMLWriter::GetControls() +{ + // Idea: first off collect the paragraph- and character-bound controls. + // In the process for every control the paragraph position and VCForm are + // saved in an array. + // With that array it's possible to find out where form::Forms must be + // opened and closed. + + if( m_pHTMLPosFlyFrames ) + { + // collect the paragraph-bound controls + for( size_t i=0; i<m_pHTMLPosFlyFrames->size(); i++ ) + { + const SwHTMLPosFlyFrame* pPosFlyFrame = (*m_pHTMLPosFlyFrames)[ i ].get(); + if( HtmlOut::Control != pPosFlyFrame->GetOutFn() ) + continue; + + const SdrObject *pSdrObj = pPosFlyFrame->GetSdrObject(); + OSL_ENSURE( pSdrObj, "Where is the SdrObject?" ); + if( !pSdrObj ) + continue; + + AddControl( m_aHTMLControls, dynamic_cast<const SdrUnoObj&>(*pSdrObj), + pPosFlyFrame->GetNdIndex().GetIndex() ); + } + } + + // and now the ones in a character-bound frame + const SwFrameFormats* pSpzFrameFormats = m_pDoc->GetSpzFrameFormats(); + for( size_t i=0; i<pSpzFrameFormats->size(); i++ ) + { + const SwFrameFormat *pFrameFormat = (*pSpzFrameFormats)[i]; + if( RES_DRAWFRMFMT != pFrameFormat->Which() ) + continue; + + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + const SwPosition *pPos = rAnchor.GetContentAnchor(); + if ((RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId()) || !pPos) + continue; + + const SdrObject *pSdrObj = + SwHTMLWriter::GetHTMLControl( *static_cast<const SwDrawFrameFormat*>(pFrameFormat) ); + if( !pSdrObj ) + continue; + + AddControl( m_aHTMLControls, dynamic_cast<const SdrUnoObj&>(*pSdrObj), pPos->nNode.GetIndex() ); + } +} + +HTMLControl::HTMLControl( + const uno::Reference< container::XIndexContainer > & rFormComps, + sal_uInt32 nIdx ) : + xFormComps( rFormComps ), nNdIdx( nIdx ), nCount( 1 ) +{} + +HTMLControl::~HTMLControl() +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlftn.cxx b/sw/source/filter/html/htmlftn.cxx new file mode 100644 index 000000000..a808f1284 --- /dev/null +++ b/sw/source/filter/html/htmlftn.cxx @@ -0,0 +1,565 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> +#include <rtl/strbuf.hxx> +#include <ndindex.hxx> +#include <fmtftn.hxx> +#include <txtftn.hxx> +#include <ftninfo.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <charfmt.hxx> + +#include "swhtml.hxx" +#include "wrthtml.hxx" + +static sal_Int32 lcl_html_getNextPart( OUString& rPart, const OUString& rContent, + sal_Int32 nPos ) +{ + rPart.clear(); + sal_Int32 nLen = rContent.getLength(); + if( nPos >= nLen ) + { + nPos = -1; + } + else + { + bool bQuoted = false, bDone = false; + for( ; nPos < nLen && !bDone; nPos++ ) + { + sal_Unicode c = rContent[nPos]; + switch( c ) + { + case '\\': + if( bQuoted ) + rPart += OUStringChar( c ); + bQuoted = !bQuoted; + break; + + case ';': + if( bQuoted ) + rPart += OUStringChar( c ); + else + bDone = true; + bQuoted = false; + break; + + default: + rPart += OUStringChar( c ); + bQuoted = false; + break; + } + } + } + + return nPos; +} + +static sal_Int32 lcl_html_getEndNoteInfo( SwEndNoteInfo& rInfo, + const OUString& rContent, + bool bEndNote ) +{ + sal_Int32 nStrPos = 0; + for( int nPart = 0; nPart < 4; ++nPart ) + { + OUString aPart; + if( -1 != nStrPos ) + nStrPos = lcl_html_getNextPart( aPart, rContent, nStrPos ); + + switch( nPart ) + { + case 0: + rInfo.m_aFormat.SetNumberingType( bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC ); + if( !aPart.isEmpty() ) + rInfo.m_aFormat.SetNumberingType(SwHTMLParser::GetNumType( aPart, + rInfo.m_aFormat.GetNumberingType() )); + break; + + case 1: + rInfo.m_nFootnoteOffset = aPart.isEmpty() ? 0 : static_cast<sal_uInt16>(aPart.toInt32()); + break; + + case 2: + rInfo.SetPrefix( aPart ); + break; + + case 3: + rInfo.SetSuffix( aPart ); + break; + } + } + + return nStrPos; +} + +void SwHTMLParser::FillEndNoteInfo( const OUString& rContent ) +{ + SwEndNoteInfo aInfo( m_xDoc->GetEndNoteInfo() ); + lcl_html_getEndNoteInfo( aInfo, rContent, true ); + m_xDoc->SetEndNoteInfo( aInfo ); +} + +void SwHTMLParser::FillFootNoteInfo( const OUString& rContent ) +{ + SwFootnoteInfo aInfo( m_xDoc->GetFootnoteInfo() ); + + sal_Int32 nStrPos = lcl_html_getEndNoteInfo( aInfo, rContent, false ); + + for( int nPart = 4; nPart < 8; ++nPart ) + { + OUString aPart; + if( -1 != nStrPos ) + nStrPos = lcl_html_getNextPart( aPart, rContent, nStrPos ); + + switch( nPart ) + { + case 4: + aInfo.m_eNum = FTNNUM_DOC; + if( !aPart.isEmpty() ) + { + switch( aPart[0] ) + { + case 'D': aInfo.m_eNum = FTNNUM_DOC; break; + case 'C': aInfo.m_eNum = FTNNUM_CHAPTER; break; + case 'P': aInfo.m_eNum = FTNNUM_PAGE; break; + } + } + break; + + case 5: + aInfo.m_ePos = FTNPOS_PAGE; + if( !aPart.isEmpty() ) + { + switch( aPart[0] ) + { + case 'C': aInfo.m_ePos = FTNPOS_CHAPTER; break; + case 'P': aInfo.m_ePos = FTNPOS_PAGE; break; + } + } + break; + + case 6: + aInfo.m_aQuoVadis = aPart; + break; + + case 7: + aInfo.m_aErgoSum = aPart; + break; + } + } + + m_xDoc->SetFootnoteInfo( aInfo ); +} + +void SwHTMLParser::InsertFootEndNote( const OUString& rName, bool bEndNote, + bool bFixed ) +{ + if( !m_pFootEndNoteImpl ) + m_pFootEndNoteImpl.reset(new SwHTMLFootEndNote_Impl); + + m_pFootEndNoteImpl->sName = rName; + if( m_pFootEndNoteImpl->sName.getLength() > 3 ) + m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.copy( 0, m_pFootEndNoteImpl->sName.getLength() - 3 ); + m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.toAsciiUpperCase(); + m_pFootEndNoteImpl->bEndNote = bEndNote; + m_pFootEndNoteImpl->bFixed = bFixed; + m_pFootEndNoteImpl->sContent.clear(); +} + +void SwHTMLParser::FinishFootEndNote() +{ + if( !m_pFootEndNoteImpl ) + return; + + SwFormatFootnote aFootnote( m_pFootEndNoteImpl->bEndNote ); + if( m_pFootEndNoteImpl->bFixed ) + aFootnote.SetNumStr( m_pFootEndNoteImpl->sContent ); + + m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, aFootnote ); + SwTextFootnote * const pTextFootnote = static_cast<SwTextFootnote *>( + m_pPam->GetNode().GetTextNode()->GetTextAttrForCharAt( + m_pPam->GetPoint()->nContent.GetIndex() - 1, RES_TXTATR_FTN ) ); + // In header and footer no footnotes can be inserted. + if (pTextFootnote) + m_pFootEndNoteImpl->aTextFootnotes.push_back(SwHTMLTextFootnote(m_pFootEndNoteImpl->sName,pTextFootnote)); + m_pFootEndNoteImpl->sName.clear(); + m_pFootEndNoteImpl->sContent.clear(); + m_pFootEndNoteImpl->bFixed = false; +} + +void SwHTMLParser::InsertFootEndNoteText() +{ + if( m_pFootEndNoteImpl && m_pFootEndNoteImpl->bFixed ) + m_pFootEndNoteImpl->sContent += aToken; +} + +SwNodeIndex *SwHTMLParser::GetFootEndNoteSection( const OUString& rName ) +{ + SwNodeIndex *pStartNodeIdx = nullptr; + + if (m_pFootEndNoteImpl) + { + OUString aName(rName.toAsciiUpperCase()); + + size_t nCount = m_pFootEndNoteImpl->aTextFootnotes.size(); + for(size_t i = 0; i < nCount; ++i) + { + if (m_pFootEndNoteImpl->aTextFootnotes[i].sName == aName) + { + pStartNodeIdx = m_pFootEndNoteImpl->aTextFootnotes[i].pTextFootnote->GetStartNode(); + m_pFootEndNoteImpl->aTextFootnotes.erase( m_pFootEndNoteImpl->aTextFootnotes.begin() + i ); + if (m_pFootEndNoteImpl->aTextFootnotes.empty()) + { + m_pFootEndNoteImpl.reset(); + } + + break; + } + } + } + + return pStartNodeIdx; +} + +Writer& OutHTML_SwFormatFootnote( Writer& rWrt, const SfxPoolItem& rHt ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + SwFormatFootnote& rFormatFootnote = const_cast<SwFormatFootnote&>(static_cast<const SwFormatFootnote&>(rHt)); + SwTextFootnote *pTextFootnote = rFormatFootnote.GetTextFootnote(); + if( !pTextFootnote ) + return rWrt; + + OUString sFootnoteName, sClass; + size_t nPos; + if( rFormatFootnote.IsEndNote() ) + { + nPos = rHTMLWrt.m_pFootEndNotes ? rHTMLWrt.m_pFootEndNotes->size() : 0; + OSL_ENSURE( nPos == static_cast<size_t>(rHTMLWrt.m_nFootNote + rHTMLWrt.m_nEndNote), + "OutHTML_SwFormatFootnote: wrong position" ); + sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_anc; + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number( static_cast<sal_Int32>(++rHTMLWrt.m_nEndNote) ); + } + else + { + nPos = rHTMLWrt.m_nFootNote; + sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_anc; + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number( static_cast<sal_Int32>(++rHTMLWrt.m_nFootNote)); + } + + if( !rHTMLWrt.m_pFootEndNotes ) + rHTMLWrt.m_pFootEndNotes.reset(new std::vector<SwTextFootnote*>); + rHTMLWrt.m_pFootEndNotes->insert( rHTMLWrt.m_pFootEndNotes->begin() + nPos, pTextFootnote ); + + OStringBuffer sOut; + OString aTag = rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor; + sOut.append("<" + aTag + " " OOO_STRING_SVTOOLS_HTML_O_class "=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sClass, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_name "=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor "\" " + OOO_STRING_SVTOOLS_HTML_O_href "=\"#"); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol "\""); + if( !rFormatFootnote.GetNumStr().isEmpty() ) + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_sdfixed); + sOut.append(">"); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript ); + + HTMLOutFuncs::Out_String( rWrt.Strm(), rFormatFootnote.GetViewNumStr(*rWrt.m_pDoc, nullptr), + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript, false ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor, false ); + + return rWrt; +} + +void SwHTMLWriter::OutFootEndNotes() +{ + OSL_ENSURE( m_pFootEndNotes, + "SwHTMLWriter::OutFootEndNotes(): unnecessary call" ); + if( !m_pFootEndNotes ) + return; + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt16 nFootnote = m_nFootNote, nEn = m_nEndNote; +#endif + m_nFootNote = 0; + m_nEndNote = 0; + + for( auto *pTextFootnote : *m_pFootEndNotes ) + { + m_pFormatFootnote = &pTextFootnote->GetFootnote(); + + OUString sFootnoteName; + if( m_pFormatFootnote->IsEndNote() ) + { + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number(static_cast<sal_Int32>(++m_nEndNote)); + } + else + { + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number(static_cast<sal_Int32>(++m_nFootNote)); + } + + if( m_bLFPossible ) + OutNewLine(); + OString sOut = + "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division + " " OOO_STRING_SVTOOLS_HTML_O_id "=\""; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( Strm(), sFootnoteName, m_eDestEnc, &m_aNonConvertableCharacters ); + Strm().WriteCharPtr( "\">" ); + + m_bLFPossible = true; + IncIndentLevel(); // indent content of <DIV> + + OSL_ENSURE( pTextFootnote, "SwHTMLWriter::OutFootEndNotes: SwTextFootnote is missing" ); + SwNodeIndex *pSttNdIdx = pTextFootnote->GetStartNode(); + OSL_ENSURE( pSttNdIdx, + "SwHTMLWriter::OutFootEndNotes: StartNode-Index is missing" ); + if( pSttNdIdx ) + { + HTMLSaveData aSaveData( *this, pSttNdIdx->GetIndex()+1, + pSttNdIdx->GetNode().EndOfSectionIndex(), false ); + Out_SwDoc( m_pCurrentPam.get() ); + } + + DecIndentLevel(); // indent content of <DIV> + if( m_bLFPossible ) + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false ); + m_bLFPossible = true; + + OSL_ENSURE( !m_pFormatFootnote, + "SwHTMLWriter::OutFootEndNotes: Footnote was not output" ); + if( m_pFormatFootnote ) + { + if( m_pFormatFootnote->IsEndNote() ) + m_nEndNote++; + else + m_nFootNote++; + + m_pFormatFootnote = nullptr; + } + } + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( nFootnote == m_nFootNote, + "SwHTMLWriter::OutFootEndNotes: Number of footnotes does not match" ); + OSL_ENSURE( nEn == m_nEndNote, + "SwHTMLWriter::OutFootEndNotes: Number of endnotes does not match" ); +#endif + + m_pFootEndNotes.reset(); + m_nFootNote = m_nEndNote = 0; +} + +OUString SwHTMLWriter::GetFootEndNoteSym( const SwFormatFootnote& rFormatFootnote ) +{ + const SwEndNoteInfo * pInfo = nullptr; + if( rFormatFootnote.GetNumStr().isEmpty() ) + pInfo = rFormatFootnote.IsEndNote() ? &m_pDoc->GetEndNoteInfo() + : &m_pDoc->GetFootnoteInfo(); + + OUString sRet; + if( pInfo ) + sRet = pInfo->GetPrefix(); + sRet += rFormatFootnote.GetViewNumStr(*m_pDoc, nullptr); + if( pInfo ) + sRet += pInfo->GetSuffix(); + + return sRet; +} + +void SwHTMLWriter::OutFootEndNoteSym( const SwFormatFootnote& rFormatFootnote, + const OUString& rNum, + sal_uInt16 nScript ) +{ + const SwEndNoteInfo *pInfo; + + OUString sFootnoteName, sClass; + if( rFormatFootnote.IsEndNote() ) + { + sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_sym; + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + + OUString::number(static_cast<sal_Int32>(m_nEndNote)); + pInfo = &m_pDoc->GetEndNoteInfo(); + } + else + { + sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_sym; + sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + + OUString::number(static_cast<sal_Int32>(m_nFootNote)); + pInfo = &m_pDoc->GetFootnoteInfo(); + } + + const SwCharFormat *pSymCharFormat = pInfo->GetCharFormat( *m_pDoc ); + if( pSymCharFormat && 0 != m_aScriptTextStyles.count( pSymCharFormat->GetName() ) ) + { + switch( nScript ) + { + case CSS1_OUTMODE_WESTERN: + sClass += "-western"; + break; + case CSS1_OUTMODE_CJK: + sClass += "-cjk"; + break; + case CSS1_OUTMODE_CTL: + sClass += "-ctl"; + break; + } + } + + OStringBuffer sOut; + sOut.append('<').append(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor).append(' ') + .append(OOO_STRING_SVTOOLS_HTML_O_class).append("=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), sClass, m_eDestEnc, &m_aNonConvertableCharacters ); + sOut.append("\" ").append(OOO_STRING_SVTOOLS_HTML_O_name).append("=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), sFootnoteName, m_eDestEnc, &m_aNonConvertableCharacters ); + sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol).append("\" ") + .append(OOO_STRING_SVTOOLS_HTML_O_href).append("=\"#"); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), sFootnoteName, m_eDestEnc, &m_aNonConvertableCharacters ); + sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor).append("\">"); + Strm().WriteOString( sOut.makeStringAndClear() ); + + HTMLOutFuncs::Out_String( Strm(), rNum, m_eDestEnc, &m_aNonConvertableCharacters ); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor, false ); +} + +static int lcl_html_fillEndNoteInfo( const SwEndNoteInfo& rInfo, + OUString *pParts, + bool bEndNote ) +{ + int nParts = 0; + sal_Int16 eFormat = rInfo.m_aFormat.GetNumberingType(); + if( (bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC) != eFormat ) + { + const char *pStr = SwHTMLWriter::GetNumFormat( eFormat ); + if( pStr ) + { + pParts[0] = OUString::createFromAscii( pStr ); + nParts = 1; + } + } + if( rInfo.m_nFootnoteOffset > 0 ) + { + pParts[1] = OUString::number(rInfo.m_nFootnoteOffset); + nParts = 2; + } + if( !rInfo.GetPrefix().isEmpty() ) + { + pParts[2] = rInfo.GetPrefix(); + nParts = 3; + } + if( !rInfo.GetSuffix().isEmpty() ) + { + pParts[3] = rInfo.GetSuffix(); + nParts = 4; + } + + return nParts; +} + +static void lcl_html_outFootEndNoteInfo( Writer& rWrt, OUString const *pParts, + int nParts, const char *pName ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + OUStringBuffer aContent; + for( int i=0; i<nParts; ++i ) + { + OUString aTmp( pParts[i] ); + aTmp = aTmp.replaceAll( "\\", "\\\\" ); + aTmp = aTmp.replaceAll( ";", "\\;" ); + if( i > 0 ) + aContent.append(";"); + aContent.append(aTmp); + } + + rHTMLWrt.OutNewLine(); + OString sOut = + "<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_meta " " + OOO_STRING_SVTOOLS_HTML_O_name "=\"" + rtl::OStringView(pName) + + "\" " OOO_STRING_SVTOOLS_HTML_O_content "=\""; + rWrt.Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aContent.makeStringAndClear(), rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + rWrt.Strm().WriteCharPtr( "\">" ); +} + +void SwHTMLWriter::OutFootEndNoteInfo() +{ + // Number type (1 or i) + // Offset (0) + // Before it + // Behind it + // Doc/Page/Chap (D) + // Position (S) + // Next page + // Beginning + + { + const SwFootnoteInfo& rInfo = m_pDoc->GetFootnoteInfo(); + OUString aParts[8]; + int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, false ); + if( rInfo.m_eNum != FTNNUM_DOC ) + { + aParts[4] = rInfo.m_eNum == FTNNUM_CHAPTER ? OUStringLiteral( "C" ) : OUStringLiteral( "P" ); + nParts = 5; + } + if( rInfo.m_ePos != FTNPOS_PAGE) + { + aParts[5] = "C"; + nParts = 6; + } + if( !rInfo.m_aQuoVadis.isEmpty() ) + { + aParts[6] = rInfo.m_aQuoVadis; + nParts = 7; + } + if( !rInfo.m_aErgoSum.isEmpty() ) + { + aParts[7] = rInfo.m_aErgoSum; + nParts = 8; + } + if( nParts > 0 ) + lcl_html_outFootEndNoteInfo( *this, aParts, nParts, + OOO_STRING_SVTOOLS_HTML_META_sdfootnote ); + } + + { + const SwEndNoteInfo& rInfo = m_pDoc->GetEndNoteInfo(); + OUString aParts[4]; + const int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, true ); + if( nParts > 0 ) + lcl_html_outFootEndNoteInfo( *this, aParts, nParts, + OOO_STRING_SVTOOLS_HTML_META_sdendnote ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlgrin.cxx b/sw/source/filter/html/htmlgrin.cxx new file mode 100644 index 000000000..11ac33e69 --- /dev/null +++ b/sw/source/filter/html/htmlgrin.cxx @@ -0,0 +1,1533 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <comphelper/documentinfo.hxx> +#include <vcl/svapp.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <svl/stritem.hxx> +#include <svl/urihelper.hxx> +#include <svl/languageoptions.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/langitem.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/event.hxx> +#include <vcl/imap.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <unotools/eventcfg.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <fmtornt.hxx> +#include <fmturl.hxx> +#include <fmtsrnd.hxx> +#include <fmtinfmt.hxx> +#include <fmtcntnt.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <charatr.hxx> +#include <frmfmt.hxx> +#include <charfmt.hxx> +#include <docary.hxx> +#include <docsh.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <shellio.hxx> +#include <poolfmt.hxx> +#include <IMark.hxx> +#include <ndgrf.hxx> +#include "htmlnum.hxx" +#include "swcss1.hxx" +#include "swhtml.hxx" +#include <numrule.hxx> +#include <IDocumentMarkAccess.hxx> +#include <frameformats.hxx> + +#include <vcl/graphicfilter.hxx> +#include <tools/urlobj.hxx> + +using namespace ::com::sun::star; + +HTMLOptionEnum<sal_Int16> const aHTMLImgHAlignTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_AL_left, text::HoriOrientation::LEFT }, + { OOO_STRING_SVTOOLS_HTML_AL_right, text::HoriOrientation::RIGHT }, + { nullptr, 0 } +}; + +HTMLOptionEnum<sal_Int16> const aHTMLImgVAlignTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::LINE_TOP }, + { OOO_STRING_SVTOOLS_HTML_VA_texttop, text::VertOrientation::CHAR_TOP }, + { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER }, + { OOO_STRING_SVTOOLS_HTML_AL_center, text::VertOrientation::CENTER }, + { OOO_STRING_SVTOOLS_HTML_VA_absmiddle, text::VertOrientation::LINE_CENTER }, + { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::TOP }, + { OOO_STRING_SVTOOLS_HTML_VA_baseline, text::VertOrientation::TOP }, + { OOO_STRING_SVTOOLS_HTML_VA_absbottom, text::VertOrientation::LINE_BOTTOM }, + { nullptr, 0 } +}; + +ImageMap *SwHTMLParser::FindImageMap( const OUString& rName ) const +{ + OSL_ENSURE( rName[0] != '#', "FindImageMap: name begins with '#'!" ); + + if (m_pImageMaps) + { + for (const auto &rpIMap : *m_pImageMaps) + { + if (rName.equalsIgnoreAsciiCase(rpIMap->GetName())) + { + return rpIMap.get(); + } + } + } + return nullptr; +} + +void SwHTMLParser::ConnectImageMaps() +{ + SwNodes& rNds = m_xDoc->GetNodes(); + // on the first node of section #1 + sal_uLong nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + 1; + sal_uLong nEndIdx = rNds.GetEndOfAutotext().GetIndex(); + + SwGrfNode* pGrfNd; + while( m_nMissingImgMaps > 0 && nIdx < nEndIdx ) + { + SwNode *pNd = rNds[nIdx + 1]; + if( nullptr != (pGrfNd = pNd->GetGrfNode()) ) + { + SwFrameFormat *pFormat = pGrfNd->GetFlyFormat(); + SwFormatURL aURL( pFormat->GetURL() ); + const ImageMap *pIMap = aURL.GetMap(); + if( pIMap && pIMap->GetIMapObjectCount()==0 ) + { + // The (empty) image map of the node will be either + // replaced with found image map or deleted. + ImageMap *pNewIMap = + FindImageMap( pIMap->GetName() ); + aURL.SetMap( pNewIMap ); + pFormat->SetFormatAttr( aURL ); + if( !pGrfNd->IsScaleImageMap() ) + { + // meanwhile the graphic size is known or the + // graphic don't need scaling + pGrfNd->ScaleImageMap(); + } + m_nMissingImgMaps--; // search a map less + } + } + nIdx = rNds[nIdx]->EndOfSectionIndex() + 1; + } +} + +void SwHTMLParser::SetAnchorAndAdjustment( sal_Int16 eVertOri, + sal_Int16 eHoriOri, + const SvxCSS1PropertyInfo &rCSS1PropInfo, + SfxItemSet& rFrameItemSet ) +{ + const SfxItemSet *pCntnrItemSet = nullptr; + auto i = m_aContexts.size(); + while( !pCntnrItemSet && i > m_nContextStMin ) + pCntnrItemSet = m_aContexts[--i]->GetFrameItemSet(); + + if( pCntnrItemSet ) + { + // If we are in a container then the anchoring of the container is used. + rFrameItemSet.Put( *pCntnrItemSet ); + } + else if( SwCSS1Parser::MayBePositioned( rCSS1PropInfo, true ) ) + { + // If the alignment can be set via CSS1 options we use them. + SetAnchorAndAdjustment( rCSS1PropInfo, rFrameItemSet ); + } + else + { + // Otherwise the alignment is set correspondingly the normal HTML options. + SetAnchorAndAdjustment( eVertOri, eHoriOri, rFrameItemSet ); + } +} + +void SwHTMLParser::SetAnchorAndAdjustment( sal_Int16 eVertOri, + sal_Int16 eHoriOri, + SfxItemSet& rFrameSet, + bool bDontAppend ) +{ + bool bMoveBackward = false; + SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR ); + sal_Int16 eVertRel = text::RelOrientation::FRAME; + + if( text::HoriOrientation::NONE != eHoriOri ) + { + // determine paragraph indent + sal_uInt16 nLeftSpace = 0, nRightSpace = 0; + short nIndent = 0; + GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent ); + + // determine horizontal alignment and wrapping + sal_Int16 eHoriRel; + css::text::WrapTextMode eSurround; + switch( eHoriOri ) + { + case text::HoriOrientation::LEFT: + eHoriRel = nLeftSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_RIGHT; + break; + case text::HoriOrientation::RIGHT: + eHoriRel = nRightSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_LEFT; + break; + case text::HoriOrientation::CENTER: // for tables + eHoriRel = text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_NONE; + break; + default: + eHoriRel = text::RelOrientation::FRAME; + eSurround = css::text::WrapTextMode_PARALLEL; + break; + } + + // Create a new paragraph, if the current one has frames + // anchored at paragraph/at char without wrapping. + if( !bDontAppend && HasCurrentParaFlys( true ) ) + { + // When the paragraph only contains graphics then there + // is no need for bottom margin. Since here also with use of + // styles no margin should be created, set attributes to + // override! + sal_uInt16 nUpper=0, nLower=0; + GetULSpaceFromContext( nUpper, nLower ); + InsertAttr( SvxULSpaceItem( nUpper, 0, RES_UL_SPACE ), true ); + + AppendTextNode( AM_NOSPACE ); + + if( nUpper ) + { + NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, SvxULSpaceItem(0, nLower, RES_UL_SPACE)); + m_aParaAttrs.push_back( m_xAttrTab->pULSpace ); + EndAttr( m_xAttrTab->pULSpace, false ); + } + } + + // determine vertical alignment and anchoring + const sal_Int32 nContent = m_pPam->GetPoint()->nContent.GetIndex(); + if( nContent ) + { + aAnchor.SetType( RndStdIds::FLY_AT_CHAR ); + bMoveBackward = true; + eVertOri = text::VertOrientation::CHAR_BOTTOM; + eVertRel = text::RelOrientation::CHAR; + } + else + { + aAnchor.SetType( RndStdIds::FLY_AT_PARA ); + eVertOri = text::VertOrientation::TOP; + eVertRel = text::RelOrientation::PRINT_AREA; + } + + rFrameSet.Put( SwFormatHoriOrient( 0, eHoriOri, eHoriRel) ); + + rFrameSet.Put( SwFormatSurround( eSurround ) ); + } + rFrameSet.Put( SwFormatVertOrient( 0, eVertOri, eVertRel) ); + + if( bMoveBackward ) + m_pPam->Move( fnMoveBackward ); + + if (aAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR && !m_pPam->GetNode().GetTextNode()) + { + eState = SvParserState::Error; + return; + } + + aAnchor.SetAnchor( m_pPam->GetPoint() ); + + if( bMoveBackward ) + m_pPam->Move( fnMoveForward ); + + rFrameSet.Put( aAnchor ); +} + +void SwHTMLParser::RegisterFlyFrame( SwFrameFormat *pFlyFormat ) +{ + // automatically anchored frames must be moved forward by one position + if( RES_DRAWFRMFMT != pFlyFormat->Which() && + (RndStdIds::FLY_AT_PARA == pFlyFormat->GetAnchor().GetAnchorId()) && + css::text::WrapTextMode_THROUGH == pFlyFormat->GetSurround().GetSurround() ) + { + m_aMoveFlyFrames.emplace_back(std::make_unique<SwHTMLFrameFormatListener>(pFlyFormat)); + m_aMoveFlyCnts.push_back( m_pPam->GetPoint()->nContent.GetIndex() ); + } +} + +/* */ + +void SwHTMLParser::GetDefaultScriptType( ScriptType& rType, + OUString& rTypeStr ) const +{ + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + SvKeyValueIterator* pHeaderAttrs = pDocSh ? pDocSh->GetHeaderAttributes() + : nullptr; + rType = GetScriptType( pHeaderAttrs ); + rTypeStr = GetScriptTypeString( pHeaderAttrs ); +} + +namespace +{ + bool allowAccessLink(const SwDoc& rDoc) + { + OUString sReferer; + SfxObjectShell * sh = rDoc.GetPersist(); + if (sh != nullptr && sh->HasName()) + { + sReferer = sh->GetMedium()->GetName(); + } + return !SvtSecurityOptions().isUntrustedReferer(sReferer); + } +} + +/* */ + +void SwHTMLParser::InsertImage() +{ + // and now analyze + OUString sAltNm, aId, aClass, aStyle, aMap, sHTMLGrfName; + OUString sGrfNm; + OUString aGraphicData; + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + bool bWidthProvided=false, bHeightProvided=false; + long nWidth=0, nHeight=0; + long nVSpace=0, nHSpace=0; + + sal_uInt16 nBorder = (m_xAttrTab->pINetFormat ? 1 : 0); + bool bIsMap = false; + bool bPercentWidth = false; + bool bPercentHeight = false; + OUString sWidthAsString, sHeightAsString; + SvxMacroItem aMacroItem(RES_FRMMACRO); + + ScriptType eDfltScriptType; + OUString sDfltScriptType; + GetDefaultScriptType( eDfltScriptType, sDfltScriptType ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + SvMacroItemId nEvent = SvMacroItemId::NONE; + ScriptType eScriptType2 = eDfltScriptType; + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::SRC: + sGrfNm = rOption.GetString(); + if( !InternalImgToPrivateURL(sGrfNm) ) + sGrfNm = INetURLObject::GetAbsURL( m_sBaseURL, sGrfNm ); + break; + case HtmlOptionId::DATA: + aGraphicData = rOption.GetString(); + if (!InternalImgToPrivateURL(aGraphicData)) + aGraphicData = INetURLObject::GetAbsURL( + m_sBaseURL, SwHTMLParser::StripQueryFromPath(m_sBaseURL, aGraphicData)); + break; + case HtmlOptionId::ALIGN: + eVertOri = + rOption.GetEnum( aHTMLImgVAlignTable, + text::VertOrientation::TOP ); + eHoriOri = + rOption.GetEnum( aHTMLImgHAlignTable ); + break; + case HtmlOptionId::WIDTH: + // for now only store as pixel value! + nWidth = rOption.GetNumber(); + sWidthAsString = rOption.GetString(); + bPercentWidth = (sWidthAsString.indexOf('%') != -1); + if( bPercentWidth && nWidth>100 ) + nWidth = 100; + // width|height = "auto" means viewing app decides the size + // i.e. proceed as if no particular size was provided + bWidthProvided = (sWidthAsString != "auto"); + break; + case HtmlOptionId::HEIGHT: + // for now only store as pixel value! + nHeight = rOption.GetNumber(); + sHeightAsString = rOption.GetString(); + bPercentHeight = (sHeightAsString.indexOf('%') != -1); + if( bPercentHeight && nHeight>100 ) + nHeight = 100; + // the same as above w/ HtmlOptionId::WIDTH + bHeightProvided = (sHeightAsString != "auto"); + break; + case HtmlOptionId::VSPACE: + nVSpace = rOption.GetNumber(); + break; + case HtmlOptionId::HSPACE: + nHSpace = rOption.GetNumber(); + break; + case HtmlOptionId::ALT: + sAltNm = rOption.GetString(); + break; + case HtmlOptionId::BORDER: + nBorder = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::ISMAP: + bIsMap = true; + break; + case HtmlOptionId::USEMAP: + aMap = rOption.GetString(); + break; + case HtmlOptionId::NAME: + sHTMLGrfName = rOption.GetString(); + break; + + case HtmlOptionId::SDONLOAD: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONLOAD: + nEvent = SvMacroItemId::OnImageLoadDone; + goto IMAGE_SETEVENT; + + case HtmlOptionId::SDONABORT: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONABORT: + nEvent = SvMacroItemId::OnImageLoadCancel; + goto IMAGE_SETEVENT; + + case HtmlOptionId::SDONERROR: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONERROR: + nEvent = SvMacroItemId::OnImageLoadError; + goto IMAGE_SETEVENT; +IMAGE_SETEVENT: + { + OUString sTmp( rOption.GetString() ); + if( !sTmp.isEmpty() ) + { + sTmp = convertLineEnd(sTmp, GetSystemLineEnd()); + OUString sScriptType; + if( EXTENDED_STYPE == eScriptType2 ) + sScriptType = sDfltScriptType; + aMacroItem.SetMacro( nEvent, + SvxMacro( sTmp, sScriptType, eScriptType2 )); + } + } + break; + default: break; + } + } + + if (sGrfNm.isEmpty() && !aGraphicData.isEmpty()) + sGrfNm = aGraphicData; + + if( sGrfNm.isEmpty() ) + return; + + // When we are in an ordered list and the paragraph is still empty and not + // numbered, it may be a graphic for a bullet list. + if( !m_pPam->GetPoint()->nContent.GetIndex() && + GetNumInfo().GetDepth() > 0 && GetNumInfo().GetDepth() <= MAXLEVEL && + !m_aBulletGrfs[GetNumInfo().GetDepth()-1].isEmpty() && + m_aBulletGrfs[GetNumInfo().GetDepth()-1]==sGrfNm ) + { + SwTextNode* pTextNode = m_pPam->GetNode().GetTextNode(); + + if( pTextNode && ! pTextNode->IsCountedInList()) + { + OSL_ENSURE( pTextNode->GetActualListLevel() == GetNumInfo().GetLevel(), + "Numbering level is wrong" ); + + pTextNode->SetCountedInList( true ); + + // It's necessary to invalidate the rule, because between the reading + // of LI and the graphic an EndAction could be called. + if( GetNumInfo().GetNumRule() ) + GetNumInfo().GetNumRule()->SetInvalidRule( true ); + + // Set the style again, so that indent of the first line is correct. + SetTextCollAttrs(); + + return; + } + } + + Graphic aGraphic; + INetURLObject aGraphicURL( sGrfNm ); + if( aGraphicURL.GetProtocol() == INetProtocol::Data ) + { + std::unique_ptr<SvMemoryStream> const pStream(aGraphicURL.getData()); + if (pStream) + { + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + aGraphic = rFilter.ImportUnloadedGraphic(*pStream); + sGrfNm.clear(); + + if (!sGrfNm.isEmpty()) + { + if (ERRCODE_NONE == rFilter.ImportGraphic(aGraphic, "", *pStream)) + sGrfNm.clear(); + } + } + } + else if (m_sBaseURL.isEmpty() || !aGraphicData.isEmpty()) + { + // sBaseURL is empty if the source is clipboard + // aGraphicData is non-empty for <object data="..."> -> not a linked graphic. + if (ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, aGraphicURL)) + sGrfNm.clear(); + } + + if (!sGrfNm.isEmpty()) + { + aGraphic.SetDefaultType(); + } + + if (!nHeight || !nWidth) + { + Size aPixelSize = aGraphic.GetSizePixel(Application::GetDefaultDevice()); + if (!bWidthProvided) + nWidth = aPixelSize.Width(); + if (!bHeightProvided) + nHeight = aPixelSize.Height(); + } + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ); + + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + // set the border + long nHBorderWidth = 0, nVBorderWidth = 0; + if( nBorder ) + { + nHBorderWidth = static_cast<long>(nBorder); + nVBorderWidth = static_cast<long>(nBorder); + SvxCSS1Parser::PixelToTwip( nVBorderWidth, nHBorderWidth ); + + ::editeng::SvxBorderLine aHBorderLine( nullptr, nHBorderWidth ); + ::editeng::SvxBorderLine aVBorderLine( nullptr, nVBorderWidth ); + + if( m_xAttrTab->pINetFormat ) + { + const OUString& rURL = + static_cast<const SwFormatINetFormat&>(m_xAttrTab->pINetFormat->GetItem()).GetValue(); + + m_pCSS1Parser->SetATagStyles(); + sal_uInt16 nPoolId = static_cast< sal_uInt16 >(m_xDoc->IsVisitedURL( rURL ) + ? RES_POOLCHR_INET_VISIT + : RES_POOLCHR_INET_NORMAL); + const SwCharFormat *pCharFormat = m_pCSS1Parser->GetCharFormatFromPool( nPoolId ); + aHBorderLine.SetColor( pCharFormat->GetColor().GetValue() ); + aVBorderLine.SetColor( aHBorderLine.GetColor() ); + } + else + { + const SvxColorItem& rColorItem = m_xAttrTab->pFontColor ? + static_cast<const SvxColorItem &>(m_xAttrTab->pFontColor->GetItem()) : + m_xDoc->GetDefault(RES_CHRATR_COLOR); + aHBorderLine.SetColor( rColorItem.GetValue() ); + aVBorderLine.SetColor( aHBorderLine.GetColor() ); + } + + SvxBoxItem aBoxItem( RES_BOX ); + aBoxItem.SetLine( &aHBorderLine, SvxBoxItemLine::TOP ); + aBoxItem.SetLine( &aHBorderLine, SvxBoxItemLine::BOTTOM ); + aBoxItem.SetLine( &aVBorderLine, SvxBoxItemLine::LEFT ); + aBoxItem.SetLine( &aVBorderLine, SvxBoxItemLine::RIGHT ); + aFrameSet.Put( aBoxItem ); + } + + SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet ); + + SetSpace( Size( nHSpace, nVSpace), aItemSet, aPropInfo, aFrameSet ); + + // set other CSS1 attributes + SetFrameFormatAttrs( aItemSet, HtmlFrameFormatFlags::Box, aFrameSet ); + + Size aTwipSz( bPercentWidth ? 0 : nWidth, bPercentHeight ? 0 : nHeight ); + if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() ) + { + if (bWidthProvided || bHeightProvided || // attributes imply pixel! + aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + { + aTwipSz = Application::GetDefaultDevice() + ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) ); + } + else + { // some bitmaps may have a size in metric units (e.g. PNG); use that + assert(aGraphic.GetPrefMapMode().GetMapUnit() < MapUnit::MapPixel); + aTwipSz = OutputDevice::LogicToLogic(aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + } + } + + // convert CSS1 size to "normal" size + switch( aPropInfo.m_eWidthType ) + { + case SVX_CSS1_LTYPE_TWIP: + aTwipSz.setWidth( aPropInfo.m_nWidth ); + nWidth = 1; // != 0 + bPercentWidth = false; + break; + case SVX_CSS1_LTYPE_PERCENTAGE: + aTwipSz.setWidth( 0 ); + nWidth = aPropInfo.m_nWidth; + bPercentWidth = true; + break; + default: + ; + } + switch( aPropInfo.m_eHeightType ) + { + case SVX_CSS1_LTYPE_TWIP: + aTwipSz.setHeight( aPropInfo.m_nHeight ); + nHeight = 1; // != 0 + bPercentHeight = false; + break; + case SVX_CSS1_LTYPE_PERCENTAGE: + aTwipSz.setHeight( 0 ); + nHeight = aPropInfo.m_nHeight; + bPercentHeight = true; + break; + default: + ; + } + + Size aGrfSz( 0, 0 ); + bool bSetTwipSize = true; // Set Twip-Size on Node? + bool bChangeFrameSize = false; // Change frame format later? + bool bRequestGrfNow = false; + bool bSetScaleImageMap = false; + sal_uInt8 nPercentWidth = 0, nPercentHeight = 0; + + // bPercentWidth / bPercentHeight means we have a percent size. If that's not the case and we have no + // size from nWidth / nHeight either, then inspect the image header. + if ((!bPercentWidth && !nWidth) && (!bPercentHeight && !nHeight) && allowAccessLink(*m_xDoc)) + { + GraphicDescriptor aDescriptor(aGraphicURL); + if (aDescriptor.Detect(/*bExtendedInfo=*/true)) + { + // Try to use size info from the image header before defaulting to + // HTML_DFLT_IMG_WIDTH/HEIGHT. + aTwipSz = Application::GetDefaultDevice()->PixelToLogic(aDescriptor.GetSizePixel(), + MapMode(MapUnit::MapTwip)); + nWidth = aTwipSz.getWidth(); + nHeight = aTwipSz.getHeight(); + } + } + + if( !nWidth || !nHeight ) + { + // When the graphic is in a table, it will be requested immediately, + // so that it is available before the table is layouted. + if (m_xTable && !nWidth) + { + bRequestGrfNow = true; + IncGrfsThatResizeTable(); + } + + // The frame size is set later + bChangeFrameSize = true; + aGrfSz = aTwipSz; + if( !nWidth && !nHeight ) + { + aTwipSz.setWidth( HTML_DFLT_IMG_WIDTH ); + aTwipSz.setHeight( HTML_DFLT_IMG_HEIGHT ); + } + else if( nWidth ) + { + // a percentage value + if( bPercentWidth ) + { + nPercentWidth = static_cast<sal_uInt8>(nWidth); + nPercentHeight = 255; + } + else + { + aTwipSz.setHeight( HTML_DFLT_IMG_HEIGHT ); + } + } + else if( nHeight ) + { + if( bPercentHeight ) + { + nPercentHeight = static_cast<sal_uInt8>(nHeight); + nPercentWidth = 255; + } + else + { + aTwipSz.setWidth( HTML_DFLT_IMG_WIDTH ); + } + } + } + else + { + // Width and height were given and don't need to be set + bSetTwipSize = false; + + if( bPercentWidth ) + nPercentWidth = static_cast<sal_uInt8>(nWidth); + + if( bPercentHeight ) + nPercentHeight = static_cast<sal_uInt8>(nHeight); + } + + // set image map + aMap = comphelper::string::stripEnd(aMap, ' '); + if( !aMap.isEmpty() ) + { + // Since we only know local image maps we just use everything + // after # as name + sal_Int32 nPos = aMap.indexOf( '#' ); + OUString aName; + if ( -1 == nPos ) + aName = aMap ; + else + aName = aMap.copy(nPos+1); + + ImageMap *pImgMap = FindImageMap( aName ); + if( pImgMap ) + { + SwFormatURL aURL; aURL.SetMap( pImgMap );// is copied + + bSetScaleImageMap = !nPercentWidth || !nPercentHeight; + aFrameSet.Put( aURL ); + } + else + { + ImageMap aEmptyImgMap( aName ); + SwFormatURL aURL; aURL.SetMap( &aEmptyImgMap );// is copied + aFrameSet.Put( aURL ); + m_nMissingImgMaps++; // image maps are missing + + // the graphic has to scaled during SetTwipSize, if we didn't + // set a size on the node or the size doesn't match the graphic size. + bSetScaleImageMap = true; + } + } + + // observe minimum values !! + if( nPercentWidth ) + { + OSL_ENSURE( !aTwipSz.Width(), + "Why is a width set if we already have percentage value?" ); + aTwipSz.setWidth( aGrfSz.Width() ? aGrfSz.Width() + : HTML_DFLT_IMG_WIDTH ); + } + else + { + aTwipSz.AdjustWidth(2*nVBorderWidth ); + if( aTwipSz.Width() < MINFLY ) + aTwipSz.setWidth( MINFLY ); + } + if( nPercentHeight ) + { + OSL_ENSURE( !aTwipSz.Height(), + "Why is a height set if we already have percentage value?" ); + aTwipSz.setHeight( aGrfSz.Height() ? aGrfSz.Height() + : HTML_DFLT_IMG_HEIGHT ); + } + else + { + aTwipSz.AdjustHeight(2*nHBorderWidth ); + if( aTwipSz.Height() < MINFLY ) + aTwipSz.setHeight( MINFLY ); + } + + SwFormatFrameSize aFrameSize( SwFrameSize::Fixed, aTwipSz.Width(), aTwipSz.Height() ); + aFrameSize.SetWidthPercent( nPercentWidth ); + aFrameSize.SetHeightPercent( nPercentHeight ); + aFrameSet.Put( aFrameSize ); + + const SwNodeType eNodeType = m_pPam->GetNode().GetNodeType(); + if (eNodeType != SwNodeType::Text && eNodeType != SwNodeType::Table) + return; + + // passing empty sGrfNm here, means we don't want the graphic to be linked + SwFrameFormat *const pFlyFormat = + m_xDoc->getIDocumentContentOperations().InsertGraphic( + *m_pPam, sGrfNm, OUString(), &aGraphic, + &aFrameSet, nullptr, nullptr); + SwGrfNode *pGrfNd = m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx() + ->GetIndex()+1 ]->GetGrfNode(); + + if( !sHTMLGrfName.isEmpty() ) + { + pFlyFormat->SetName( sHTMLGrfName ); + + // maybe jump to graphic + if( JumpToMarks::Graphic == m_eJumpTo && sHTMLGrfName == m_sJmpMark ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + } + + if (pGrfNd) + { + if( !sAltNm.isEmpty() ) + pGrfNd->SetTitle( sAltNm ); + + if( bSetTwipSize ) + pGrfNd->SetTwipSize( aGrfSz ); + + pGrfNd->SetChgTwipSize( bChangeFrameSize ); + + if( bSetScaleImageMap ) + pGrfNd->SetScaleImageMap( true ); + } + + if( m_xAttrTab->pINetFormat ) + { + const SwFormatINetFormat &rINetFormat = + static_cast<const SwFormatINetFormat&>(m_xAttrTab->pINetFormat->GetItem()); + + SwFormatURL aURL( pFlyFormat->GetURL() ); + + aURL.SetURL( rINetFormat.GetValue(), bIsMap ); + aURL.SetTargetFrameName( rINetFormat.GetTargetFrame() ); + aURL.SetName( rINetFormat.GetName() ); + pFlyFormat->SetFormatAttr( aURL ); + + { + static const SvMacroItemId aEvents[] = { + SvMacroItemId::OnMouseOver, + SvMacroItemId::OnClick, + SvMacroItemId::OnMouseOut }; + + for( SvMacroItemId id : aEvents ) + { + const SvxMacro *pMacro = rINetFormat.GetMacro( id ); + if( nullptr != pMacro ) + aMacroItem.SetMacro( id, *pMacro ); + } + } + + if ((RndStdIds::FLY_AS_CHAR == pFlyFormat->GetAnchor().GetAnchorId()) && + m_xAttrTab->pINetFormat->GetSttPara() == + m_pPam->GetPoint()->nNode && + m_xAttrTab->pINetFormat->GetSttCnt() == + m_pPam->GetPoint()->nContent.GetIndex() - 1 ) + { + // the attribute was insert right before as-character anchored + // graphic, therefore we move it + m_xAttrTab->pINetFormat->SetStart( *m_pPam->GetPoint() ); + + // When the attribute is also an anchor, we'll insert + // a bookmark before the graphic, because SwFormatURL + // isn't an anchor. + if( !rINetFormat.GetName().isEmpty() ) + { + m_pPam->Move( fnMoveBackward ); + InsertBookmark( rINetFormat.GetName() ); + m_pPam->Move( fnMoveForward ); + } + } + + } + + if( !aMacroItem.GetMacroTable().empty() ) + { + NotifyMacroEventRead(); + pFlyFormat->SetFormatAttr( aMacroItem ); + } + + // tdf#87083 If the graphic has not been loaded yet, then load it now. + // Otherwise it may be loaded during the first paint of the object and it + // will be too late to adapt the size of the graphic at that point. + if (bRequestGrfNow && pGrfNd) + { + Size aUpdatedSize = pGrfNd->GetTwipSize(); //trigger a swap-in + SAL_WARN_IF(!aUpdatedSize.Width() || !aUpdatedSize.Height(), "sw.html", "html image with no width or height"); + } + + // maybe create frames and register auto bound frames + RegisterFlyFrame( pFlyFormat ); + + if( !aId.isEmpty() ) + InsertBookmark( aId ); +} + +/* */ + +void SwHTMLParser::InsertBodyOptions() +{ + m_xDoc->SetTextFormatColl( *m_pPam, + m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT ) ); + + OUString aBackGround, aId, aStyle, aLang, aDir; + Color aBGColor, aTextColor, aLinkColor, aVLinkColor; + bool bBGColor=false, bTextColor=false; + bool bLinkColor=false, bVLinkColor=false; + + ScriptType eDfltScriptType; + OUString sDfltScriptType; + GetDefaultScriptType( eDfltScriptType, sDfltScriptType ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + ScriptType eScriptType2 = eDfltScriptType; + OUString aEvent; + bool bSetEvent = false; + + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::BACKGROUND: + aBackGround = rOption.GetString(); + break; + case HtmlOptionId::BGCOLOR: + rOption.GetColor( aBGColor ); + bBGColor = true; + break; + case HtmlOptionId::TEXT: + rOption.GetColor( aTextColor ); + bTextColor = true; + break; + case HtmlOptionId::LINK: + rOption.GetColor( aLinkColor ); + bLinkColor = true; + break; + case HtmlOptionId::VLINK: + rOption.GetColor( aVLinkColor ); + bVLinkColor = true; + break; + + case HtmlOptionId::SDONLOAD: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONLOAD: + aEvent = GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC ); + bSetEvent = true; + break; + + case HtmlOptionId::SDONUNLOAD: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONUNLOAD: + aEvent = GlobalEventConfig::GetEventName( GlobalEventId::PREPARECLOSEDOC ); + bSetEvent = true; + break; + + case HtmlOptionId::SDONFOCUS: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONFOCUS: + aEvent = GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ); + bSetEvent = true; + break; + + case HtmlOptionId::SDONBLUR: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONBLUR: + aEvent = GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ); + bSetEvent = true; + break; + + case HtmlOptionId::ONERROR: + break; + + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + bTextColor = true; + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + + if( bSetEvent ) + { + const OUString& rEvent = rOption.GetString(); + if( !rEvent.isEmpty() ) + InsertBasicDocEvent( aEvent, rEvent, eScriptType2, + sDfltScriptType ); + } + } + + if( bTextColor && !m_pCSS1Parser->IsBodyTextSet() ) + { + // The font colour is set in the default style + m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD ) + ->SetFormatAttr( SvxColorItem(aTextColor, RES_CHRATR_COLOR) ); + m_pCSS1Parser->SetBodyTextSet(); + } + + // Prepare the items for the page style (background, frame) + // If BrushItem already set values must remain! + std::unique_ptr<SvxBrushItem> aBrushItem( m_pCSS1Parser->makePageDescBackground() ); + bool bSetBrush = false; + + if( bBGColor && !m_pCSS1Parser->IsBodyBGColorSet() ) + { + // background colour from "BGCOLOR" + OUString aLink; + if( !aBrushItem->GetGraphicLink().isEmpty() ) + aLink = aBrushItem->GetGraphicLink(); + SvxGraphicPosition ePos = aBrushItem->GetGraphicPos(); + + aBrushItem->SetColor( aBGColor ); + + if( !aLink.isEmpty() ) + { + aBrushItem->SetGraphicLink( aLink ); + aBrushItem->SetGraphicPos( ePos ); + } + bSetBrush = true; + m_pCSS1Parser->SetBodyBGColorSet(); + } + + if( !aBackGround.isEmpty() && !m_pCSS1Parser->IsBodyBackgroundSet() ) + { + // background graphic from "BACKGROUND" + aBrushItem->SetGraphicLink( INetURLObject::GetAbsURL( m_sBaseURL, aBackGround ) ); + aBrushItem->SetGraphicPos( GPOS_TILED ); + bSetBrush = true; + m_pCSS1Parser->SetBodyBackgroundSet(); + } + + if( !aStyle.isEmpty() || !aDir.isEmpty() ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + OUString aDummy; + ParseStyleOptions( aStyle, aDummy, aDummy, aItemSet, aPropInfo, nullptr, &aDir ); + + // Some attributes have to set on the page style, in fact the ones + // which aren't inherited + m_pCSS1Parser->SetPageDescAttrs( bSetBrush ? aBrushItem.get() : nullptr, + &aItemSet ); + + const SfxPoolItem *pItem; + static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONTSIZE, + RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CTL_FONTSIZE }; + for(sal_uInt16 i : aWhichIds) + { + if( SfxItemState::SET == aItemSet.GetItemState( i, false, + &pItem ) && + static_cast <const SvxFontHeightItem * >(pItem)->GetProp() != 100) + { + sal_uInt32 nHeight = + ( m_aFontHeights[2] * + static_cast <const SvxFontHeightItem * >(pItem)->GetProp() ) / 100; + SvxFontHeightItem aNewItem( nHeight, 100, i ); + aItemSet.Put( aNewItem ); + } + } + + // all remaining options can be set on the default style + m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD ) + ->SetFormatAttr( aItemSet ); + } + else if( bSetBrush ) + { + m_pCSS1Parser->SetPageDescAttrs( aBrushItem.get() ); + } + + if( bLinkColor && !m_pCSS1Parser->IsBodyLinkSet() ) + { + SwCharFormat *pCharFormat = + m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_INET_NORMAL); + pCharFormat->SetFormatAttr( SvxColorItem(aLinkColor, RES_CHRATR_COLOR) ); + m_pCSS1Parser->SetBodyLinkSet(); + } + if( bVLinkColor && !m_pCSS1Parser->IsBodyVLinkSet() ) + { + SwCharFormat *pCharFormat = + m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_INET_VISIT); + pCharFormat->SetFormatAttr( SvxColorItem(aVLinkColor, RES_CHRATR_COLOR) ); + m_pCSS1Parser->SetBodyVLinkSet(); + } + if( !aLang.isEmpty() ) + { + LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( aLang ); + if( LANGUAGE_DONTKNOW != eLang ) + { + sal_uInt16 nWhich = 0; + switch( SvtLanguageOptions::GetScriptTypeOfLanguage( eLang ) ) + { + case SvtScriptType::LATIN: + nWhich = RES_CHRATR_LANGUAGE; + break; + case SvtScriptType::ASIAN: + nWhich = RES_CHRATR_CJK_LANGUAGE; + break; + case SvtScriptType::COMPLEX: + nWhich = RES_CHRATR_CTL_LANGUAGE; + break; + default: break; + } + if( nWhich ) + { + SvxLanguageItem aLanguage( eLang, nWhich ); + aLanguage.SetWhich( nWhich ); + m_xDoc->SetDefault( aLanguage ); + } + } + } + + if( !aId.isEmpty() ) + InsertBookmark( aId ); +} + +/* */ + +void SwHTMLParser::NewAnchor() +{ + // end previous link if there was one + std::unique_ptr<HTMLAttrContext> xOldCntxt(PopContext(HtmlTokenId::ANCHOR_ON)); + if (xOldCntxt) + { + // and maybe end attributes + EndContext(xOldCntxt.get()); + } + + SvxMacroTableDtor aMacroTable; + OUString sHRef, aName, sTarget; + OUString aId, aStyle, aClass, aLang, aDir; + bool bHasHRef = false, bFixed = false; + + ScriptType eDfltScriptType; + OUString sDfltScriptType; + GetDefaultScriptType( eDfltScriptType, sDfltScriptType ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + SvMacroItemId nEvent = SvMacroItemId::NONE; + ScriptType eScriptType2 = eDfltScriptType; + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::NAME: + aName = rOption.GetString(); + break; + + case HtmlOptionId::HREF: + sHRef = rOption.GetString(); + bHasHRef = true; + break; + case HtmlOptionId::TARGET: + sTarget = rOption.GetString(); + break; + + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::SDFIXED: + bFixed = true; + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + + case HtmlOptionId::SDONCLICK: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONCLICK: + nEvent = SvMacroItemId::OnClick; + goto ANCHOR_SETEVENT; + + case HtmlOptionId::SDONMOUSEOVER: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONMOUSEOVER: + nEvent = SvMacroItemId::OnMouseOver; + goto ANCHOR_SETEVENT; + + case HtmlOptionId::SDONMOUSEOUT: + eScriptType2 = STARBASIC; + [[fallthrough]]; + case HtmlOptionId::ONMOUSEOUT: + nEvent = SvMacroItemId::OnMouseOut; + goto ANCHOR_SETEVENT; +ANCHOR_SETEVENT: + { + OUString sTmp( rOption.GetString() ); + if( !sTmp.isEmpty() ) + { + sTmp = convertLineEnd(sTmp, GetSystemLineEnd()); + OUString sScriptType; + if( EXTENDED_STYPE == eScriptType2 ) + sScriptType = sDfltScriptType; + aMacroTable.Insert( nEvent, SvxMacro( sTmp, sScriptType, eScriptType2 )); + } + } + break; + default: break; + } + } + + // Jump targets, which match our implicit targets, + // here we throw out rigorously. + if( !aName.isEmpty() ) + { + OUString sDecoded( INetURLObject::decode( aName, + INetURLObject::DecodeMechanism::Unambiguous )); + sal_Int32 nPos = sDecoded.lastIndexOf( cMarkSeparator ); + if( nPos != -1 ) + { + OUString sCmp= sDecoded.copy(nPos+1).replaceAll(" ",""); + if( !sCmp.isEmpty() ) + { + sCmp = sCmp.toAsciiLowerCase(); + if( sCmp == "region" || + sCmp == "frame" || + sCmp == "graphic" || + sCmp == "ole" || + sCmp == "table" || + sCmp == "outline" || + sCmp == "text" ) + { + aName.clear(); + } + } + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::ANCHOR_ON)); + + bool bEnAnchor = false, bFootnoteAnchor = false, bFootnoteEnSymbol = false; + OUString aFootnoteName; + OUString aStrippedClass( aClass ); + SwCSS1Parser::GetScriptFromClass( aStrippedClass, false ); + if( aStrippedClass.getLength() >=9 && bHasHRef && sHRef.getLength() > 1 && + ('s' == aStrippedClass[0] || 'S' == aStrippedClass[0]) && + ('d' == aStrippedClass[1] || 'D' == aStrippedClass[1]) ) + { + if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote_anc ) ) + bEnAnchor = true; + else if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote_anc ) ) + bFootnoteAnchor = true; + else if( aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote_sym ) || + aStrippedClass.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote_sym ) ) + bFootnoteEnSymbol = true; + if( bEnAnchor || bFootnoteAnchor || bFootnoteEnSymbol ) + { + aFootnoteName = sHRef.copy( 1 ); + aClass.clear(); + aStrippedClass.clear(); + aName.clear(); + bHasHRef = false; + } + } + + // Styles parsen + if( HasStyleOptions( aStyle, aId, aStrippedClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + { + DoPositioning(aItemSet, aPropInfo, xCntxt.get()); + InsertAttrs(aItemSet, aPropInfo, xCntxt.get(), true); + } + } + + if( bHasHRef ) + { + if( !sHRef.isEmpty() ) + { + sHRef = URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), sHRef, Link<OUString *, bool>(), false ); + } + else + { + // use directory if empty URL + INetURLObject aURLObj( m_aPathToFile ); + sHRef = aURLObj.GetPartBeforeLastName(); + } + + m_pCSS1Parser->SetATagStyles(); + SwFormatINetFormat aINetFormat( sHRef, sTarget ); + aINetFormat.SetName( aName ); + + if( !aMacroTable.empty() ) + { + NotifyMacroEventRead(); + aINetFormat.SetMacroTable( &aMacroTable ); + } + + // set the default attribute + InsertAttr(&m_xAttrTab->pINetFormat, aINetFormat, xCntxt.get()); + } + else if( !aName.isEmpty() ) + { + InsertBookmark( aName ); + } + + if( bEnAnchor || bFootnoteAnchor ) + { + InsertFootEndNote( aFootnoteName, bEnAnchor, bFixed ); + m_bInFootEndNoteAnchor = m_bCallNextToken = true; + } + else if( bFootnoteEnSymbol ) + { + m_bInFootEndNoteSymbol = m_bCallNextToken = true; + } + + // save context + PushContext(xCntxt); +} + +void SwHTMLParser::EndAnchor() +{ + if( m_bInFootEndNoteAnchor ) + { + FinishFootEndNote(); + m_bInFootEndNoteAnchor = false; + } + else if( m_bInFootEndNoteSymbol ) + { + m_bInFootEndNoteSymbol = false; + } + + EndTag( HtmlTokenId::ANCHOR_OFF ); +} + +/* */ + +void SwHTMLParser::InsertBookmark( const OUString& rName ) +{ + HTMLAttr* pTmp = new HTMLAttr( *m_pPam->GetPoint(), + SfxStringItem(RES_FLTR_BOOKMARK, rName), nullptr, std::shared_ptr<HTMLAttrTable>()); + m_aSetAttrTab.push_back( pTmp ); +} + +bool SwHTMLParser::HasCurrentParaBookmarks( bool bIgnoreStack ) const +{ + bool bHasMarks = false; + sal_uLong nNodeIdx = m_pPam->GetPoint()->nNode.GetIndex(); + + // first step: are there still bookmark in the attribute-stack? + // bookmarks are added to the end of the stack - thus we only have + // to check the last bookmark + if( !bIgnoreStack ) + { + for( auto i = m_aSetAttrTab.size(); i; ) + { + HTMLAttr* pAttr = m_aSetAttrTab[ --i ]; + if( RES_FLTR_BOOKMARK == pAttr->m_pItem->Which() ) + { + if( pAttr->GetSttParaIdx() == nNodeIdx ) + bHasMarks = true; + break; + } + } + } + + if( !bHasMarks ) + { + // second step: when we didn't find a bookmark, check if there is one set already + IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); + ppMark != pMarkAccess->getAllMarksEnd(); + ++ppMark) + { + const ::sw::mark::IMark* pBookmark = *ppMark; + + const sal_uLong nBookNdIdx = pBookmark->GetMarkPos().nNode.GetIndex(); + if( nBookNdIdx==nNodeIdx ) + { + bHasMarks = true; + break; + } + else if( nBookNdIdx > nNodeIdx ) + break; + } + } + + return bHasMarks; +} + +/* */ + +void SwHTMLParser::StripTrailingPara() +{ + bool bSetSmallFont = false; + + SwContentNode* pCNd = m_pPam->GetContentNode(); + sal_uLong nNodeIdx = m_pPam->GetPoint()->nNode.GetIndex(); + if( !m_pPam->GetPoint()->nContent.GetIndex() ) + { + if( pCNd && pCNd->StartOfSectionIndex() + 2 < + pCNd->EndOfSectionIndex() && CanRemoveNode(nNodeIdx)) + { + const SwFrameFormats& rFrameFormatTable = *m_xDoc->GetSpzFrameFormats(); + + for( auto pFormat : rFrameFormatTable ) + { + 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())) && + pAPos->nNode == nNodeIdx ) + + return; // we can't delete the node + } + + SetAttr( false ); // the still open attributes must be + // closed before the node is deleted, + // otherwise the last index is dangling + + if( pCNd->Len() && pCNd->IsTextNode() ) + { + // fields were inserted into the node, now they have + // to be moved + SwTextNode *pPrvNd = m_xDoc->GetNodes()[nNodeIdx-1]->GetTextNode(); + if( pPrvNd ) + { + SwIndex aSrc( pCNd, 0 ); + pCNd->GetTextNode()->CutText( pPrvNd, aSrc, pCNd->Len() ); + } + } + + // now we have to move maybe existing bookmarks + IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); + ppMark != pMarkAccess->getAllMarksEnd(); + ++ppMark) + { + ::sw::mark::IMark* pMark = *ppMark; + + sal_uLong nBookNdIdx = pMark->GetMarkPos().nNode.GetIndex(); + if(nBookNdIdx==nNodeIdx) + { + SwNodeIndex nNewNdIdx(m_pPam->GetPoint()->nNode); + SwContentNode* pNd = SwNodes::GoPrevious(&nNewNdIdx); + if(!pNd) + { + OSL_ENSURE(false, "Oops, where is my predecessor node?"); + return; + } + // #i81002# - refactoring + // Do not directly manipulate member of <SwBookmark> + { + SwPosition aNewPos(*pNd); + aNewPos.nContent.Assign(pNd, pNd->Len()); + const SwPaM aPaM(aNewPos); + pMarkAccess->repositionMark(*ppMark, aPaM); + } + } + else if( nBookNdIdx > nNodeIdx ) + break; + } + + m_pPam->GetPoint()->nContent.Assign( nullptr, 0 ); + m_pPam->SetMark(); + m_pPam->DeleteMark(); + m_xDoc->GetNodes().Delete( m_pPam->GetPoint()->nNode ); + m_pPam->Move( fnMoveBackward, GoInNode ); + } + else if (pCNd && pCNd->IsTextNode() && m_xTable) + { + // In empty cells we set a small font, so that the cell doesn't + // get higher than the graphic resp. as low as possible. + bSetSmallFont = true; + } + } + else if( pCNd && pCNd->IsTextNode() && m_xTable && + pCNd->StartOfSectionIndex()+2 == + pCNd->EndOfSectionIndex() ) + { + // When the cell contains only as-character anchored graphics/frames, + // then we also set a small font. + bSetSmallFont = true; + SwTextNode* pTextNd = pCNd->GetTextNode(); + + sal_Int32 nPos = m_pPam->GetPoint()->nContent.GetIndex(); + while( bSetSmallFont && nPos>0 ) + { + --nPos; + bSetSmallFont = + (CH_TXTATR_BREAKWORD == pTextNd->GetText()[nPos]) && + (nullptr != pTextNd->GetTextAttrForCharAt( nPos, RES_TXTATR_FLYCNT )); + } + } + + if( bSetSmallFont ) + { + // Added default to CJK and CTL + SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE ); + pCNd->SetAttr( aFontHeight ); + SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE ); + pCNd->SetAttr( aFontHeightCJK ); + SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE ); + pCNd->SetAttr( aFontHeightCTL ); + } +} + +void SwHTMLParser::NotifyMacroEventRead() +{ + if (m_bNotifyMacroEventRead) + return; + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + if (!pDocSh) + return; + uno::Reference<frame::XModel> const xModel(pDocSh->GetBaseModel()); + comphelper::DocumentInfo::notifyMacroEventRead(xModel); + m_bNotifyMacroEventRead = true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlnum.cxx b/sw/source/filter/html/htmlnum.cxx new file mode 100644 index 000000000..840fd61e8 --- /dev/null +++ b/sw/source/filter/html/htmlnum.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 "htmlnum.hxx" +#include <ndtxt.hxx> +#include <doc.hxx> + +void SwHTMLNumRuleInfo::Set( const SwTextNode& rTextNd ) +{ + const SwNumRule* pTextNdNumRule( rTextNd.GetNumRule() ); + if ( pTextNdNumRule && + pTextNdNumRule != rTextNd.GetDoc()->GetOutlineNumRule() ) + { + pNumRule = const_cast<SwNumRule*>(pTextNdNumRule); + nDeep = static_cast< sal_uInt16 >(pNumRule ? rTextNd.GetActualListLevel() + 1 : 0); + bNumbered = rTextNd.IsCountedInList(); + // #i57919# - correction of refactoring done by cws swnumtree: + // <bRestart> has to be set to <true>, if numbering is restarted at this + // text node and the start value equals <USHRT_MAX>. + // Start value <USHRT_MAX> indicates, that at this text node the numbering + // is restarted with the value given at the corresponding level. + bRestart = rTextNd.IsListRestart() && !rTextNd.HasAttrListRestartValue(); + } + else + { + pNumRule = nullptr; + nDeep = 0; + bNumbered = bRestart = false; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlnum.hxx b/sw/source/filter/html/htmlnum.hxx new file mode 100644 index 000000000..917c0514a --- /dev/null +++ b/sw/source/filter/html/htmlnum.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_FILTER_HTML_HTMLNUM_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLNUM_HXX + +#include <swtypes.hxx> +#include <string.h> + +#define HTML_NUMBER_BULLET_MARGINLEFT (MM50*2 + MM50/2) +#define HTML_NUMBER_BULLET_INDENT (-MM50) + +class SwTextNode; +class SwNumRule; + +// TODO: Unicode: Are these characters the correct ones? +#define HTML_BULLETCHAR_DISC (0xe008) +#define HTML_BULLETCHAR_CIRCLE (0xe009) +#define HTML_BULLETCHAR_SQUARE (0xe00b) + +class SwHTMLNumRuleInfo +{ + sal_uInt16 aNumStarts[MAXLEVEL]; + SwNumRule * pNumRule; // current numbering + sal_uInt16 nDeep; // current numbering depth (1, 2, 3, ...) + bool bRestart : 1; // Export: restart numbering + bool bNumbered : 1; // Export: paragraph is numbered + +public: + + inline void Set( const SwHTMLNumRuleInfo& rInf ); + void Set( const SwTextNode& rTextNd ); + + SwHTMLNumRuleInfo() : + pNumRule( nullptr ), nDeep( 0 ), + bRestart( false ), bNumbered( false ) + { + memset( &aNumStarts, 0xff, sizeof( aNumStarts ) ); + } + + SwHTMLNumRuleInfo( const SwHTMLNumRuleInfo& rInf ) : + pNumRule( rInf.pNumRule ), nDeep( rInf.nDeep ), + bRestart( rInf.bRestart ), bNumbered( rInf.bNumbered ) + { + memcpy( &aNumStarts, &rInf.aNumStarts, sizeof( aNumStarts ) ); + } + + explicit SwHTMLNumRuleInfo( const SwTextNode& rTextNd ) { Set( rTextNd ); } + inline SwHTMLNumRuleInfo& operator=( const SwHTMLNumRuleInfo& rInf ); + + inline void Clear(); + + void SetNumRule( const SwNumRule *pRule ) { pNumRule = const_cast<SwNumRule *>(pRule); } + SwNumRule *GetNumRule() { return pNumRule; } + const SwNumRule *GetNumRule() const { return pNumRule; } + + void SetDepth( sal_uInt16 nDepth ) { nDeep = nDepth; } + sal_uInt16 GetDepth() const { return nDeep; } + void IncDepth() { ++nDeep; } + void DecDepth() { if (nDeep!=0) --nDeep; } + inline sal_uInt8 GetLevel() const; + + bool IsRestart() const { return bRestart; } + + bool IsNumbered() const { return bNumbered; } + + inline void SetNodeStartValue( sal_uInt8 nLvl, sal_uInt16 nVal=USHRT_MAX ); + sal_uInt16 GetNodeStartValue( sal_uInt8 nLvl ) const { return aNumStarts[nLvl]; } +}; + +inline SwHTMLNumRuleInfo& SwHTMLNumRuleInfo::operator=( + const SwHTMLNumRuleInfo& rInf ) +{ + Set( rInf ); + return *this; +} + +inline void SwHTMLNumRuleInfo::Set( const SwHTMLNumRuleInfo& rInf ) +{ + pNumRule = rInf.pNumRule; + nDeep = rInf.nDeep; + bRestart = rInf.bRestart; + bNumbered = rInf.bNumbered; + memcpy( &aNumStarts, &rInf.aNumStarts, sizeof( aNumStarts ) ); +} + +inline void SwHTMLNumRuleInfo::Clear() +{ + pNumRule = nullptr; + nDeep = 0; + bRestart = bNumbered = false; + memset( &aNumStarts, 0xff, sizeof( aNumStarts ) ); +} + +inline sal_uInt8 SwHTMLNumRuleInfo::GetLevel() const +{ + return + static_cast<sal_uInt8>( pNumRule!=nullptr && nDeep != 0 + ? ( nDeep<=MAXLEVEL ? nDeep-1 : MAXLEVEL - 1 ) + : 0 ); +} + +inline void SwHTMLNumRuleInfo::SetNodeStartValue( sal_uInt8 nLvl, sal_uInt16 nVal ) +{ + aNumStarts[nLvl] = nVal; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlnumreader.cxx b/sw/source/filter/html/htmlnumreader.cxx new file mode 100644 index 000000000..95ce01d8b --- /dev/null +++ b/sw/source/filter/html/htmlnumreader.cxx @@ -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 . + */ + +#include <com/sun/star/text/VertOrientation.hpp> +#include <hintids.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <svl/urihelper.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/lrspitem.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <numrule.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <poolfmt.hxx> +#include <ndtxt.hxx> +#include <paratr.hxx> + +#include "htmlnum.hxx" +#include "swcss1.hxx" +#include "swhtml.hxx" + +using namespace css; + +// <UL TYPE=...> +static HTMLOptionEnum<sal_Unicode> const aHTMLULTypeTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_ULTYPE_disc, HTML_BULLETCHAR_DISC }, + { OOO_STRING_SVTOOLS_HTML_ULTYPE_circle, HTML_BULLETCHAR_CIRCLE }, + { OOO_STRING_SVTOOLS_HTML_ULTYPE_square, HTML_BULLETCHAR_SQUARE }, + { nullptr, 0 } +}; + + +void SwHTMLParser::NewNumberBulletList( HtmlTokenId nToken ) +{ + SwHTMLNumRuleInfo& rInfo = GetNumInfo(); + + // Create a new paragraph + bool bSpace = (rInfo.GetDepth() + m_nDefListDeep) == 0; + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( bSpace ? AM_SPACE : AM_NOSPACE, false ); + else if( bSpace ) + AddParSpace(); + + // Increment the numbering depth + rInfo.IncDepth(); + sal_uInt8 nLevel = static_cast<sal_uInt8>( (rInfo.GetDepth() <= MAXLEVEL ? rInfo.GetDepth() + : MAXLEVEL) - 1 ); + + // Create rules if needed + if( !rInfo.GetNumRule() ) + { + sal_uInt16 nPos = m_xDoc->MakeNumRule( m_xDoc->GetUniqueNumRuleName() ); + rInfo.SetNumRule( m_xDoc->GetNumRuleTable()[nPos] ); + } + + // Change the format for this level if that hasn't happened yet for this level + bool bNewNumFormat = rInfo.GetNumRule()->GetNumFormat( nLevel ) == nullptr; + bool bChangeNumFormat = false; + + // Create the default numbering format + SwNumFormat aNumFormat( rInfo.GetNumRule()->Get(nLevel) ); + rInfo.SetNodeStartValue( nLevel ); + if( bNewNumFormat ) + { + sal_uInt16 nChrFormatPoolId = 0; + if( HtmlTokenId::ORDERLIST_ON == nToken ) + { + aNumFormat.SetNumberingType(SVX_NUM_ARABIC); + nChrFormatPoolId = RES_POOLCHR_NUM_LEVEL; + } + else + { + // We'll set a default style because the UI does the same. This meant a 9pt font, which + // was not the case in Netscape. That didn't bother anyone so far + // #i63395# - Only apply user defined default bullet font + if ( numfunc::IsDefBulletFontUserDefined() ) + { + aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() ); + } + aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + aNumFormat.SetBulletChar( cBulletChar ); + nChrFormatPoolId = RES_POOLCHR_BULLET_LEVEL; + } + + sal_Int32 nAbsLSpace = HTML_NUMBER_BULLET_MARGINLEFT; + + sal_Int32 nFirstLineIndent = HTML_NUMBER_BULLET_INDENT; + if( nLevel > 0 ) + { + const SwNumFormat& rPrevNumFormat = rInfo.GetNumRule()->Get( nLevel-1 ); + nAbsLSpace = nAbsLSpace + rPrevNumFormat.GetAbsLSpace(); + nFirstLineIndent = rPrevNumFormat.GetFirstLineOffset(); + } + aNumFormat.SetAbsLSpace( nAbsLSpace ); + aNumFormat.SetFirstLineOffset( nFirstLineIndent ); + aNumFormat.SetCharFormat( m_pCSS1Parser->GetCharFormatFromPool(nChrFormatPoolId) ); + + bChangeNumFormat = true; + } + else if( 1 != aNumFormat.GetStart() ) + { + // If the layer has already been used, the start value may need to be set hard to the paragraph. + rInfo.SetNodeStartValue( nLevel, 1 ); + } + + // and set that in the options + OUString aId, aStyle, aClass, aLang, aDir; + OUString aBulletSrc; + sal_Int16 eVertOri = text::VertOrientation::NONE; + sal_uInt16 nWidth=USHRT_MAX, nHeight=USHRT_MAX; + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::TYPE: + if( bNewNumFormat && !rOption.GetString().isEmpty() ) + { + switch( nToken ) + { + case HtmlTokenId::ORDERLIST_ON: + bChangeNumFormat = true; + switch( rOption.GetString()[0] ) + { + case 'A': aNumFormat.SetNumberingType(SVX_NUM_CHARS_UPPER_LETTER); break; + case 'a': aNumFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER); break; + case 'I': aNumFormat.SetNumberingType(SVX_NUM_ROMAN_UPPER); break; + case 'i': aNumFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER); break; + default: bChangeNumFormat = false; + } + break; + + case HtmlTokenId::UNORDERLIST_ON: + aNumFormat.SetBulletChar( rOption.GetEnum( + aHTMLULTypeTable,aNumFormat.GetBulletChar() ) ); + bChangeNumFormat = true; + break; + default: break; + } + } + break; + case HtmlOptionId::START: + { + sal_uInt16 nStart = static_cast<sal_uInt16>(rOption.GetNumber()); + if( bNewNumFormat ) + { + aNumFormat.SetStart( nStart ); + bChangeNumFormat = true; + } + else + { + rInfo.SetNodeStartValue( nLevel, nStart ); + } + } + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + case HtmlOptionId::SRC: + if( bNewNumFormat ) + { + aBulletSrc = rOption.GetString(); + if( !InternalImgToPrivateURL(aBulletSrc) ) + aBulletSrc = URIHelper::SmartRel2Abs( INetURLObject( m_sBaseURL ), aBulletSrc, Link<OUString *, bool>(), false ); + } + break; + case HtmlOptionId::WIDTH: + nWidth = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::HEIGHT: + nHeight = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::ALIGN: + eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + break; + default: break; + } + } + + if( !aBulletSrc.isEmpty() ) + { + // A bullet list with graphics + aNumFormat.SetNumberingType(SVX_NUM_BITMAP); + + // Create the graphic as a brush + SvxBrushItem aBrushItem( RES_BACKGROUND ); + aBrushItem.SetGraphicLink( aBulletSrc ); + aBrushItem.SetGraphicPos( GPOS_AREA ); + + // Only set size if given a width and a height + Size aTwipSz( nWidth, nHeight), *pTwipSz=nullptr; + if( nWidth!=USHRT_MAX && nHeight!=USHRT_MAX ) + { + aTwipSz = + Application::GetDefaultDevice()->PixelToLogic( aTwipSz, + MapMode(MapUnit::MapTwip) ); + pTwipSz = &aTwipSz; + } + + // Only set orientation if given one + aNumFormat.SetGraphicBrush( &aBrushItem, pTwipSz, + text::VertOrientation::NONE!=eVertOri ? &eVertOri : nullptr); + + // Remember the graphic to not put it into the paragraph + m_aBulletGrfs[nLevel] = aBulletSrc; + bChangeNumFormat = true; + } + else + m_aBulletGrfs[nLevel].clear(); + + // don't number the current paragraph (for now) + { + sal_uInt8 nLvl = nLevel; + SetNodeNum( nLvl ); + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + // Parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + { + if( bNewNumFormat ) + { + if( aPropInfo.m_bLeftMargin ) + { + // Default indent has already been added + long nAbsLSpace = + aNumFormat.GetAbsLSpace() - HTML_NUMBER_BULLET_MARGINLEFT; + if( aPropInfo.m_nLeftMargin < 0 && + nAbsLSpace < -aPropInfo.m_nLeftMargin ) + nAbsLSpace = 0U; + else if( aPropInfo.m_nLeftMargin > SHRT_MAX || + nAbsLSpace + aPropInfo.m_nLeftMargin > SHRT_MAX ) + nAbsLSpace = SHRT_MAX; + else + nAbsLSpace = nAbsLSpace + aPropInfo.m_nLeftMargin; + + aNumFormat.SetAbsLSpace( nAbsLSpace ); + bChangeNumFormat = true; + } + if( aPropInfo.m_bTextIndent ) + { + short nTextIndent = + aItemSet.Get( RES_LR_SPACE ).GetTextFirstLineOffset(); + aNumFormat.SetFirstLineOffset( nTextIndent ); + bChangeNumFormat = true; + } + if( aPropInfo.m_bNumbering ) + { + aNumFormat.SetNumberingType(aPropInfo.m_nNumberingType); + bChangeNumFormat = true; + } + if( aPropInfo.m_bBullet ) + { + aNumFormat.SetBulletChar( aPropInfo.m_cBulletChar ); + bChangeNumFormat = true; + } + } + aPropInfo.m_bLeftMargin = aPropInfo.m_bTextIndent = false; + if( !aPropInfo.m_bRightMargin ) + aItemSet.ClearItem( RES_LR_SPACE ); + + // #i89812# - Perform change to list style before calling <DoPositioning(..)>, + // because <DoPositioning(..)> may open a new context and thus may + // clear the <SwHTMLNumRuleInfo> instance hold by local variable <rInfo>. + if( bChangeNumFormat ) + { + rInfo.GetNumRule()->Set( nLevel, aNumFormat ); + m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() ); + bChangeNumFormat = false; + } + + DoPositioning(aItemSet, aPropInfo, xCntxt.get()); + + InsertAttrs(aItemSet, aPropInfo, xCntxt.get()); + } + } + + if( bChangeNumFormat ) + { + rInfo.GetNumRule()->Set( nLevel, aNumFormat ); + m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() ); + } + + PushContext(xCntxt); + + // set attributes to the current template + SetTextCollAttrs(m_aContexts.back().get()); +} + +void SwHTMLParser::EndNumberBulletList( HtmlTokenId nToken ) +{ + SwHTMLNumRuleInfo& rInfo = GetNumInfo(); + + // A new paragraph needs to be created, when + // - the current one isn't empty (it contains text or paragraph-bound objects) + // - the current one is numbered + bool bAppend = m_pPam->GetPoint()->nContent.GetIndex() > 0; + if( !bAppend ) + { + SwTextNode* pTextNode = m_pPam->GetNode().GetTextNode(); + + bAppend = (pTextNode && ! pTextNode->IsOutline() && pTextNode->IsCountedInList()) || + + HasCurrentParaFlys(); + } + + bool bSpace = (rInfo.GetDepth() + m_nDefListDeep) == 1; + if( bAppend ) + AppendTextNode( bSpace ? AM_SPACE : AM_NOSPACE, false ); + else if( bSpace ) + AddParSpace(); + + // get current context from stack + std::unique_ptr<HTMLAttrContext> xCntxt(nToken != HtmlTokenId::NONE ? PopContext(getOnToken(nToken)) : nullptr); + + // Don't end a list because of a token, if the context wasn't created or mustn't be ended + if( rInfo.GetDepth()>0 && (nToken == HtmlTokenId::NONE || xCntxt) ) + { + rInfo.DecDepth(); + if( !rInfo.GetDepth() ) // was that the last level? + { + // The formats not yet modified are now modified, to ease editing + const SwNumFormat *pRefNumFormat = nullptr; + bool bChanged = false; + for( sal_uInt16 i=0; i<MAXLEVEL; i++ ) + { + const SwNumFormat *pNumFormat = rInfo.GetNumRule()->GetNumFormat(i); + if( pNumFormat ) + { + pRefNumFormat = pNumFormat; + } + else if( pRefNumFormat ) + { + SwNumFormat aNumFormat( rInfo.GetNumRule()->Get(i) ); + aNumFormat.SetNumberingType(pRefNumFormat->GetNumberingType() != SVX_NUM_BITMAP + ? pRefNumFormat->GetNumberingType() : SVX_NUM_CHAR_SPECIAL); + if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() ) + { + // #i63395# - Only apply user defined default bullet font + if ( numfunc::IsDefBulletFontUserDefined() ) + { + aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() ); + } + aNumFormat.SetBulletChar( cBulletChar ); + } + aNumFormat.SetAbsLSpace( (i+1) * HTML_NUMBER_BULLET_MARGINLEFT ); + aNumFormat.SetFirstLineOffset( HTML_NUMBER_BULLET_INDENT ); + aNumFormat.SetCharFormat( pRefNumFormat->GetCharFormat() ); + rInfo.GetNumRule()->Set( i, aNumFormat ); + bChanged = true; + } + } + if( bChanged ) + m_xDoc->ChgNumRuleFormats( *rInfo.GetNumRule() ); + + // On the last append, the NumRule item and NodeNum object were copied. + // Now we need to delete them. ResetAttr deletes the NodeNum object as well + if (SwTextNode *pTextNode = m_pPam->GetNode().GetTextNode()) + pTextNode->ResetAttr(RES_PARATR_NUMRULE); + + rInfo.Clear(); + } + else + { + // the next paragraph not numbered first + SetNodeNum( rInfo.GetLevel() ); + } + } + + // end attributes + bool bSetAttrs = false; + if (xCntxt) + { + EndContext(xCntxt.get()); + xCntxt.reset(); + bSetAttrs = true; + } + + if( nToken != HtmlTokenId::NONE ) + SetTextCollAttrs(); + + if( bSetAttrs ) + SetAttr(); // Set paragraph attributes asap because of Javascript + +} + +void SwHTMLParser::NewNumberBulletListItem( HtmlTokenId nToken ) +{ + sal_uInt8 nLevel = GetNumInfo().GetLevel(); + OUString aId, aStyle, aClass, aLang, aDir; + sal_uInt16 nStart = HtmlTokenId::LISTHEADER_ON != nToken + ? GetNumInfo().GetNodeStartValue( nLevel ) + : USHRT_MAX; + if( USHRT_MAX != nStart ) + GetNumInfo().SetNodeStartValue( nLevel ); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::VALUE: + nStart = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // create a new paragraph + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_NOSPACE, false ); + m_bNoParSpace = false; // no space in <LI>! + + SwTextNode* pTextNode = m_pPam->GetNode().GetTextNode(); + if (!pTextNode) + { + SAL_WARN("sw.html", "No Text-Node at PaM-Position"); + return; + } + + const bool bCountedInList = nToken != HtmlTokenId::LISTHEADER_ON; + + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + OUString aNumRuleName; + if( GetNumInfo().GetNumRule() ) + { + aNumRuleName = GetNumInfo().GetNumRule()->GetName(); + } + else + { + aNumRuleName = m_xDoc->GetUniqueNumRuleName(); + SwNumRule aNumRule( aNumRuleName, + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ); + SwNumFormat aNumFormat( aNumRule.Get( 0 ) ); + // #i63395# - Only apply user defined default bullet font + if ( numfunc::IsDefBulletFontUserDefined() ) + { + aNumFormat.SetBulletFont( &numfunc::GetDefBulletFont() ); + } + aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + aNumFormat.SetBulletChar( cBulletChar ); // the bullet character !! + aNumFormat.SetCharFormat( m_pCSS1Parser->GetCharFormatFromPool(RES_POOLCHR_BULLET_LEVEL) ); + aNumFormat.SetFirstLineOffset( HTML_NUMBER_BULLET_INDENT ); + aNumRule.Set( 0, aNumFormat ); + + m_xDoc->MakeNumRule( aNumRuleName, &aNumRule ); + + OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE, + "Now an open paragraph element is lost" ); + // We'll act like we're in a paragraph. On the next paragraph, at least numbering is gone, + // that's gonna be taken over by the next AppendTextNode + m_nOpenParaToken = nToken; + } + + static_cast<SwContentNode *>(pTextNode)->SetAttr( SwNumRuleItem(aNumRuleName) ); + pTextNode->SetAttrListLevel(nLevel); + // #i57656# - <IsCounted()> state of text node has to be adjusted accordingly. + if ( nLevel < MAXLEVEL ) + { + pTextNode->SetCountedInList( bCountedInList ); + } + // #i57919# + // correction of refactoring done by cws swnumtree + // - <nStart> contains the start value, if the numbering has to be restarted + // at this text node. Value <USHRT_MAX> indicates, that numbering isn't + // restarted at this text node + if ( nStart != USHRT_MAX ) + { + pTextNode->SetListRestart( true ); + pTextNode->SetAttrListRestartValue( nStart ); + } + + if( GetNumInfo().GetNumRule() ) + GetNumInfo().GetNumRule()->SetInvalidRule( true ); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + { + DoPositioning(aItemSet, aPropInfo, xCntxt.get()); + InsertAttrs(aItemSet, aPropInfo, xCntxt.get()); + } + } + + PushContext(xCntxt); + + // set the new template + SetTextCollAttrs(m_aContexts.back().get()); + + // Refresh scroll bar + ShowStatline(); +} + +void SwHTMLParser::EndNumberBulletListItem( HtmlTokenId nToken, bool bSetColl ) +{ + // Create a new paragraph + if( nToken == HtmlTokenId::NONE && m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_NOSPACE ); + + // Get context to that token and pop it from stack + std::unique_ptr<HTMLAttrContext> xCntxt; + auto nPos = m_aContexts.size(); + nToken = getOnToken(nToken); + while (!xCntxt && nPos>m_nContextStMin) + { + HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken(); + switch( nCntxtToken ) + { + case HtmlTokenId::LI_ON: + case HtmlTokenId::LISTHEADER_ON: + if( nToken == HtmlTokenId::NONE || nToken == nCntxtToken ) + { + xCntxt = std::move(m_aContexts[nPos]); + m_aContexts.erase( m_aContexts.begin() + nPos ); + } + break; + case HtmlTokenId::ORDERLIST_ON: + case HtmlTokenId::UNORDERLIST_ON: + case HtmlTokenId::MENULIST_ON: + case HtmlTokenId::DIRLIST_ON: + // Don't care about LI/LH outside the current list + nPos = m_nContextStMin; + break; + default: break; + } + } + + // end attributes + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // set paragraph attributes asap because of Javascript + xCntxt.reset(); + } + + // set current template + if( bSetColl ) + SetTextCollAttrs(); +} + +void SwHTMLParser::SetNodeNum( sal_uInt8 nLevel ) +{ + SwTextNode* pTextNode = m_pPam->GetNode().GetTextNode(); + if (!pTextNode) + { + SAL_WARN("sw.html", "No Text-Node at PaM-Position"); + return; + } + + OSL_ENSURE( GetNumInfo().GetNumRule(), "No numbering rule" ); + const OUString& rName = GetNumInfo().GetNumRule()->GetName(); + static_cast<SwContentNode *>(pTextNode)->SetAttr( SwNumRuleItem(rName) ); + + pTextNode->SetAttrListLevel( nLevel ); + pTextNode->SetCountedInList( false ); + + // Invalidate NumRule, it may have been set valid because of an EndAction + GetNumInfo().GetNumRule()->SetInvalidRule( false ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlnumwriter.cxx b/sw/source/filter/html/htmlnumwriter.cxx new file mode 100644 index 000000000..ea3c949c3 --- /dev/null +++ b/sw/source/filter/html/htmlnumwriter.cxx @@ -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 . + */ + +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmlout.hxx> +#include <numrule.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> + +#include "htmlnum.hxx" +#include "wrthtml.hxx" + +#include <osl/diagnose.h> + +using namespace css; + + +void SwHTMLWriter::FillNextNumInfo() +{ + m_pNextNumRuleInfo = nullptr; + + sal_uLong nPos = m_pCurrentPam->GetPoint()->nNode.GetIndex() + 1; + + bool bTable = false; + do + { + const SwNode* pNd = m_pDoc->GetNodes()[nPos]; + if( pNd->IsTextNode() ) + { + m_pNextNumRuleInfo.reset( new SwHTMLNumRuleInfo( *pNd->GetTextNode() ) ); + + // Before a table we keep the old level if the same numbering is + // continued after the table and no new numbering is started. + // The table will get the indentation that corresponds to its + // numbering level during import. + if( bTable && + m_pNextNumRuleInfo->GetNumRule()==GetNumInfo().GetNumRule() && + !m_pNextNumRuleInfo->IsRestart() ) + { + m_pNextNumRuleInfo->SetDepth( GetNumInfo().GetDepth() ); + } + } + else if( pNd->IsTableNode() ) + { + // A table is skipped so the node after table is viewed. + nPos = pNd->EndOfSectionIndex() + 1; + bTable = true; + } + else + { + // In all other case the numbering is over. + m_pNextNumRuleInfo.reset(new SwHTMLNumRuleInfo); + } + } + while( !m_pNextNumRuleInfo ); +} + +void SwHTMLWriter::ClearNextNumInfo() +{ + m_pNextNumRuleInfo.reset(); +} + +void SwHTMLWriter::SetNextNumInfo( std::unique_ptr<SwHTMLNumRuleInfo> pNxt ) +{ + m_pNextNumRuleInfo = std::move(pNxt); +} + +Writer& OutHTML_NumberBulletListStart( SwHTMLWriter& rWrt, + const SwHTMLNumRuleInfo& rInfo ) +{ + SwHTMLNumRuleInfo& rPrevInfo = rWrt.GetNumInfo(); + bool bSameRule = rPrevInfo.GetNumRule() == rInfo.GetNumRule(); + if( bSameRule && rPrevInfo.GetDepth() >= rInfo.GetDepth() && + !rInfo.IsRestart() ) + { + return rWrt; + } + + bool bStartValue = false; + if( !bSameRule && rInfo.GetDepth() ) + { + OUString aName( rInfo.GetNumRule()->GetName() ); + if( 0 != rWrt.m_aNumRuleNames.count( aName ) ) + { + // The rule has been applied before + sal_Int16 eType = rInfo.GetNumRule() + ->Get( rInfo.GetDepth()-1 ).GetNumberingType(); + if( SVX_NUM_CHAR_SPECIAL != eType && SVX_NUM_BITMAP != eType ) + { + // If it's a numbering rule, the current number should be + // exported as start value, but only if there are no nodes + // within the numbering that have a lower level + bStartValue = true; + if( rInfo.GetDepth() > 1 ) + { + sal_uLong nPos = + rWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex() + 1; + do + { + const SwNode* pNd = rWrt.m_pDoc->GetNodes()[nPos]; + if( pNd->IsTextNode() ) + { + const SwTextNode *pTextNd = pNd->GetTextNode(); + if( !pTextNd->GetNumRule() ) + { + // node isn't numbered => check completed + break; + } + + OSL_ENSURE(! pTextNd->IsOutline(), + "outline not expected"); + + if( pTextNd->GetActualListLevel() + 1 < + rInfo.GetDepth() ) + { + if (rPrevInfo.GetDepth() == 0) + // previous node had no numbering => write start value + bStartValue = true; + else + // node is numbered, but level is lower + bStartValue = false; + // => check completed + break; + } + nPos++; + } + else if( pNd->IsTableNode() ) + { + // skip table + nPos = pNd->EndOfSectionIndex() + 1; + } + else + { + // end node or sections start node -> check + // completed + break; + } + } + while( true ); + } + } + } + else + { + rWrt.m_aNumRuleNames.insert( aName ); + } + } + + OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE, + "<PRE> was not closed before <OL>." ); + sal_uInt16 nPrevDepth = + (bSameRule && !rInfo.IsRestart()) ? rPrevInfo.GetDepth() : 0; + + for( sal_uInt16 i=nPrevDepth; i<rInfo.GetDepth(); i++ ) + { + rWrt.OutNewLine(); // <OL>/<UL> in a new row + + rWrt.m_aBulletGrfs[i].clear(); + OString sOut = "<" + rWrt.GetNamespace(); + const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get( i ); + sal_Int16 eType = rNumFormat.GetNumberingType(); + if( SVX_NUM_CHAR_SPECIAL == eType ) + { + // ordered list: <OL> + sOut += OString(OOO_STRING_SVTOOLS_HTML_unorderlist); + + // determine the type by the bullet character + const char *pStr = nullptr; + switch( rNumFormat.GetBulletChar() ) + { + case HTML_BULLETCHAR_DISC: + pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_disc; + break; + case HTML_BULLETCHAR_CIRCLE: + pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_circle; + break; + case HTML_BULLETCHAR_SQUARE: + pStr = OOO_STRING_SVTOOLS_HTML_ULTYPE_square; + break; + } + + if( pStr ) + { + sOut += OStringLiteral(" " OOO_STRING_SVTOOLS_HTML_O_type "=\"") + pStr + "\""; + } + } + else if( SVX_NUM_BITMAP == eType ) + { + // Unordered list: <UL> + sOut += OString(OOO_STRING_SVTOOLS_HTML_unorderlist); + rWrt.Strm().WriteOString( sOut ); + OutHTML_BulletImage( rWrt, + nullptr, + rNumFormat.GetBrush(), + rWrt.m_aBulletGrfs[i]); + } + else + { + // Ordered list: <OL> + sOut += OString(OOO_STRING_SVTOOLS_HTML_orderlist); + + // determine the type by the format + char cType = 0; + switch( eType ) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + cType = 'A'; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + cType = 'a'; + break; + case SVX_NUM_ROMAN_UPPER: + cType = 'I'; + break; + case SVX_NUM_ROMAN_LOWER: + cType = 'i'; + break; + } + if( cType ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_type "=\"" + OStringChar(cType) + "\""; + } + + sal_uInt16 nStartVal = rNumFormat.GetStart(); + if( bStartValue && 1 == nStartVal && i == rInfo.GetDepth()-1 ) + { + if ( rWrt.m_pCurrentPam->GetNode().GetTextNode()->GetNum() ) + { + nStartVal = static_cast< sal_uInt16 >( rWrt.m_pCurrentPam->GetNode() + .GetTextNode()->GetNumberVector()[i] ); + } + else + { + OSL_FAIL( "<OutHTML_NumberBulletListStart(..) - text node has no number." ); + } + } + if( nStartVal != 1 ) + { + sOut += " " OOO_STRING_SVTOOLS_HTML_O_start "=\"" + OString::number(static_cast<sal_Int32>(nStartVal)) + "\""; + } + } + + if (!sOut.isEmpty() && SVX_NUM_BITMAP != eType) // second condition to avoid adding extra ul, already done before. + rWrt.Strm().WriteOString( sOut ); + + if( rWrt.m_bCfgOutStyles ) + OutCSS1_NumberBulletListStyleOpt( rWrt, *rInfo.GetNumRule(), static_cast<sal_uInt8>(i) ); + + rWrt.Strm().WriteChar( '>' ); + + rWrt.IncIndentLevel(); // indent content of <OL> + } + + return rWrt; +} + +Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt, + const SwHTMLNumRuleInfo& rNextInfo ) +{ + SwHTMLNumRuleInfo& rInfo = rWrt.GetNumInfo(); + bool bSameRule = rNextInfo.GetNumRule() == rInfo.GetNumRule(); + bool bListEnd = !bSameRule || rNextInfo.GetDepth() < rInfo.GetDepth() || rNextInfo.IsRestart(); + + if (rWrt.mbXHTML) + { + if (bListEnd || (!bListEnd && rNextInfo.IsNumbered())) + { + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_li, false); + } + } + + if (!bListEnd) + { + return rWrt; + } + + OSL_ENSURE( rWrt.m_nLastParaToken == HtmlTokenId::NONE, + "<PRE> was not closed before </OL>." ); + sal_uInt16 nNextDepth = + (bSameRule && !rNextInfo.IsRestart()) ? rNextInfo.GetDepth() : 0; + + // MIB 23.7.97: We must loop backwards, to get the right order of </OL>/</UL> + for( sal_uInt16 i=rInfo.GetDepth(); i>nNextDepth; i-- ) + { + rWrt.DecIndentLevel(); // indent content of <OL> + if( rWrt.m_bLFPossible ) + rWrt.OutNewLine(); // </OL>/</UL> in a new line + + // a list is started or ended: + sal_Int16 eType = rInfo.GetNumRule()->Get( i-1 ).GetNumberingType(); + OString aTag; + if( SVX_NUM_CHAR_SPECIAL == eType || SVX_NUM_BITMAP == eType) + aTag = OOO_STRING_SVTOOLS_HTML_unorderlist; + else + aTag = OOO_STRING_SVTOOLS_HTML_orderlist; + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + aTag, false ); + rWrt.m_bLFPossible = true; + } + + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlplug.cxx b/sw/source/filter/html/htmlplug.cxx new file mode 100644 index 000000000..525b3b0ea --- /dev/null +++ b/sw/source/filter/html/htmlplug.cxx @@ -0,0 +1,1676 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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_java.h> + +#include <hintids.hxx> +#include <rtl/strbuf.hxx> +#include <sal/log.hxx> +#include <svl/urihelper.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/frmhtml.hxx> +#include <sfx2/frmhtmlw.hxx> +#include <sfx2/frmdescr.hxx> +#include <sot/storage.hxx> +#include <svx/xoutbmp.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svtools/htmlout.hxx> +#include <svtools/htmlkywd.hxx> +#include <svtools/htmltokn.h> +#include <tools/diagnose_ex.h> +#include <IDocumentContentOperations.hxx> +#include <SwAppletImpl.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <fmtsrnd.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> + +#include <svl/ownlist.hxx> +#include <unotools/mediadescriptor.hxx> +#include <unotools/streamwrap.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <swerror.h> +#include <ndole.hxx> +#include <docsh.hxx> +#include "swhtml.hxx" +#include "wrthtml.hxx" +#include "htmlfly.hxx" +#include "swcss1.hxx" +#include "htmlreqifreader.hxx" +#include <unoframe.hxx> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <com/sun/star/embed/XEmbedPersist2.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <comphelper/embeddedobjectcontainer.hxx> +#include <comphelper/classids.hxx> +#include <rtl/uri.hxx> +#include <comphelper/storagehelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <comphelper/propertysequence.hxx> +#include <filter/msfilter/msoleexp.hxx> +#include <comphelper/fileurl.hxx> +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> +#include <comphelper/propertyvalue.hxx> + +using namespace com::sun::star; + +#define HTML_DFLT_EMBED_WIDTH ((MM50*5)/2) +#define HTML_DFLT_EMBED_HEIGHT ((MM50*5)/2) + +#define HTML_DFLT_APPLET_WIDTH ((MM50*5)/2) +#define HTML_DFLT_APPLET_HEIGHT ((MM50*5)/2) + + +const HtmlFrmOpts HTML_FRMOPTS_EMBED_ALL = + HtmlFrmOpts::Alt | + HtmlFrmOpts::Size | + HtmlFrmOpts::Name; +const HtmlFrmOpts HTML_FRMOPTS_EMBED_CNTNR = + HTML_FRMOPTS_EMBED_ALL | + HtmlFrmOpts::AbsSize; +const HtmlFrmOpts HTML_FRMOPTS_EMBED = + HTML_FRMOPTS_EMBED_ALL | + HtmlFrmOpts::Align | + HtmlFrmOpts::Space | + HtmlFrmOpts::BrClear | + HtmlFrmOpts::Name; +const HtmlFrmOpts HTML_FRMOPTS_HIDDEN_EMBED = + HtmlFrmOpts::Alt | + HtmlFrmOpts::Name; + +const HtmlFrmOpts HTML_FRMOPTS_APPLET_ALL = + HtmlFrmOpts::Alt | + HtmlFrmOpts::Size; +const HtmlFrmOpts HTML_FRMOPTS_APPLET_CNTNR = + HTML_FRMOPTS_APPLET_ALL | + HtmlFrmOpts::AbsSize; +const HtmlFrmOpts HTML_FRMOPTS_APPLET = + HTML_FRMOPTS_APPLET_ALL | + HtmlFrmOpts::Align | + HtmlFrmOpts::Space | + HtmlFrmOpts::BrClear; + +const HtmlFrmOpts HTML_FRMOPTS_IFRAME_ALL = + HtmlFrmOpts::Alt | + HtmlFrmOpts::Size; +const HtmlFrmOpts HTML_FRMOPTS_IFRAME_CNTNR = + HTML_FRMOPTS_IFRAME_ALL | + HtmlFrmOpts::AbsSize; +const HtmlFrmOpts HTML_FRMOPTS_IFRAME = + HTML_FRMOPTS_IFRAME_ALL | + HtmlFrmOpts::Align | + HtmlFrmOpts::Space | + HtmlFrmOpts::Border | + HtmlFrmOpts::BrClear; + +const HtmlFrmOpts HTML_FRMOPTS_OLE_CSS1 = + HtmlFrmOpts::SAlign | + HtmlFrmOpts::SSpace; + +namespace +{ +/** + * Calculates a filename for an image, provided the HTML file name, the image + * itself and a wanted extension. + */ +OUString lcl_CalculateFileName(const OUString* pOrigFileName, const Graphic& rGraphic, + const OUString& rExtension) +{ + OUString aFileName; + + if (pOrigFileName) + aFileName = *pOrigFileName; + INetURLObject aURL(aFileName); + OUString aName = aURL.getBase() + "_" + + aURL.getExtension() + "_" + + OUString::number(rGraphic.GetChecksum(), 16); + aURL.setBase(aName); + aURL.setExtension(rExtension); + aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + + return aFileName; +} +} + +void SwHTMLParser::SetFixSize( const Size& rPixSize, + const Size& rTwipDfltSize, + bool bPercentWidth, bool bPercentHeight, + SvxCSS1PropertyInfo const & rCSS1PropInfo, + SfxItemSet& rFlyItemSet ) +{ + // convert absolute size values into Twip + sal_uInt8 nPercentWidth = 0, nPercentHeight = 0; + Size aTwipSz( bPercentWidth || USHRT_MAX==rPixSize.Width() ? 0 : rPixSize.Width(), + bPercentHeight || USHRT_MAX==rPixSize.Height() ? 0 : rPixSize.Height() ); + if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() ) + { + aTwipSz = + Application::GetDefaultDevice()->PixelToLogic( aTwipSz, + MapMode(MapUnit::MapTwip) ); + } + + // process width + if( SVX_CSS1_LTYPE_PERCENTAGE == rCSS1PropInfo.m_eWidthType ) + { + nPercentWidth = static_cast<sal_uInt8>(rCSS1PropInfo.m_nWidth); + aTwipSz.setWidth( rTwipDfltSize.Width() ); + } + else if( SVX_CSS1_LTYPE_TWIP== rCSS1PropInfo.m_eWidthType ) + { + aTwipSz.setWidth( rCSS1PropInfo.m_nWidth ); + } + else if( bPercentWidth && rPixSize.Width() ) + { + nPercentWidth = static_cast<sal_uInt8>(rPixSize.Width()); + if( nPercentWidth > 100 ) + nPercentWidth = 100; + + aTwipSz.setWidth( rTwipDfltSize.Width() ); + } + else if( USHRT_MAX==rPixSize.Width() ) + { + aTwipSz.setWidth( rTwipDfltSize.Width() ); + } + if( aTwipSz.Width() < MINFLY ) + { + aTwipSz.setWidth( MINFLY ); + } + + // process height + if( SVX_CSS1_LTYPE_PERCENTAGE == rCSS1PropInfo.m_eHeightType ) + { + nPercentHeight = static_cast<sal_uInt8>(rCSS1PropInfo.m_nHeight); + aTwipSz.setHeight( rTwipDfltSize.Height() ); + } + else if( SVX_CSS1_LTYPE_TWIP== rCSS1PropInfo.m_eHeightType ) + { + aTwipSz.setHeight( rCSS1PropInfo.m_nHeight ); + } + else if( bPercentHeight && rPixSize.Height() ) + { + nPercentHeight = static_cast<sal_uInt8>(rPixSize.Height()); + if( nPercentHeight > 100 ) + nPercentHeight = 100; + + aTwipSz.setHeight( rTwipDfltSize.Height() ); + } + else if( USHRT_MAX==rPixSize.Height() ) + { + aTwipSz.setHeight( rTwipDfltSize.Height() ); + } + if( aTwipSz.Height() < MINFLY ) + { + aTwipSz.setHeight( MINFLY ); + } + + // set size + SwFormatFrameSize aFrameSize( SwFrameSize::Fixed, aTwipSz.Width(), aTwipSz.Height() ); + aFrameSize.SetWidthPercent( nPercentWidth ); + aFrameSize.SetHeightPercent( nPercentHeight ); + rFlyItemSet.Put( aFrameSize ); +} + +void SwHTMLParser::SetSpace( const Size& rPixSpace, + SfxItemSet& rCSS1ItemSet, + SvxCSS1PropertyInfo& rCSS1PropInfo, + SfxItemSet& rFlyItemSet ) +{ + sal_Int32 nLeftSpace = 0, nRightSpace = 0; + sal_uInt16 nUpperSpace = 0, nLowerSpace = 0; + if( (rPixSpace.Width() || rPixSpace.Height()) && Application::GetDefaultDevice() ) + { + Size aTwipSpc( rPixSpace.Width(), rPixSpace.Height() ); + aTwipSpc = + Application::GetDefaultDevice()->PixelToLogic( aTwipSpc, + MapMode(MapUnit::MapTwip) ); + nLeftSpace = nRightSpace = aTwipSpc.Width(); + nUpperSpace = nLowerSpace = static_cast<sal_uInt16>(aTwipSpc.Height()); + } + + // set left/right margin + const SfxPoolItem *pItem; + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_LR_SPACE, true, &pItem ) ) + { + // if applicable remove the first line indent + const SvxLRSpaceItem *pLRItem = static_cast<const SvxLRSpaceItem *>(pItem); + SvxLRSpaceItem aLRItem( *pLRItem ); + aLRItem.SetTextFirstLineOffset( 0 ); + if( rCSS1PropInfo.m_bLeftMargin ) + { + nLeftSpace = aLRItem.GetLeft(); + rCSS1PropInfo.m_bLeftMargin = false; + } + if( rCSS1PropInfo.m_bRightMargin ) + { + nRightSpace = aLRItem.GetRight(); + rCSS1PropInfo.m_bRightMargin = false; + } + rCSS1ItemSet.ClearItem( RES_LR_SPACE ); + } + if( nLeftSpace > 0 || nRightSpace > 0 ) + { + SvxLRSpaceItem aLRItem( RES_LR_SPACE ); + aLRItem.SetLeft( std::max<sal_Int32>(nLeftSpace, 0) ); + aLRItem.SetRight( std::max<sal_Int32>(nRightSpace, 0) ); + rFlyItemSet.Put( aLRItem ); + if( nLeftSpace ) + { + const SwFormatHoriOrient& rHoriOri = + rFlyItemSet.Get( RES_HORI_ORIENT ); + if( text::HoriOrientation::NONE == rHoriOri.GetHoriOrient() ) + { + SwFormatHoriOrient aHoriOri( rHoriOri ); + aHoriOri.SetPos( aHoriOri.GetPos() + nLeftSpace ); + rFlyItemSet.Put( aHoriOri ); + } + } + } + + // set top/bottom margin + if( SfxItemState::SET==rCSS1ItemSet.GetItemState( RES_UL_SPACE, true, &pItem ) ) + { + // if applicable remove the first line indent + const SvxULSpaceItem *pULItem = static_cast<const SvxULSpaceItem *>(pItem); + if( rCSS1PropInfo.m_bTopMargin ) + { + nUpperSpace = pULItem->GetUpper(); + rCSS1PropInfo.m_bTopMargin = false; + } + if( rCSS1PropInfo.m_bBottomMargin ) + { + nLowerSpace = pULItem->GetLower(); + rCSS1PropInfo.m_bBottomMargin = false; + } + rCSS1ItemSet.ClearItem( RES_UL_SPACE ); + } + if( nUpperSpace || nLowerSpace ) + { + SvxULSpaceItem aULItem( RES_UL_SPACE ); + aULItem.SetUpper( nUpperSpace ); + aULItem.SetLower( nLowerSpace ); + rFlyItemSet.Put( aULItem ); + if( nUpperSpace ) + { + const SwFormatVertOrient& rVertOri = + rFlyItemSet.Get( RES_VERT_ORIENT ); + if( text::VertOrientation::NONE == rVertOri.GetVertOrient() ) + { + SwFormatVertOrient aVertOri( rVertOri ); + aVertOri.SetPos( aVertOri.GetPos() + nUpperSpace ); + rFlyItemSet.Put( aVertOri ); + } + } + } +} + +OUString SwHTMLParser::StripQueryFromPath(const OUString& rBase, const OUString& rPath) +{ + if (!comphelper::isFileUrl(rBase)) + return rPath; + + sal_Int32 nIndex = rPath.indexOf('?'); + + if (nIndex != -1) + return rPath.copy(0, nIndex); + + return rPath; +} + +bool SwHTMLParser::InsertEmbed() +{ + OUString aURL, aType, aName, aAlt, aId, aStyle, aClass; + OUString aData; + Size aSize( USHRT_MAX, USHRT_MAX ); + Size aSpace( USHRT_MAX, USHRT_MAX ); + bool bPercentWidth = false, bPercentHeight = false, bHidden = false; + sal_Int16 eVertOri = text::VertOrientation::NONE; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + SvCommandList aCmdLst; + const HTMLOptions& rHTMLOptions = GetOptions(); + + // The options are read forwards, because the plug-ins expect them in this + // order. Still only the first value of an option may be regarded. + for (const auto & rOption : rHTMLOptions) + { + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::NAME: + aName = rOption.GetString(); + break; + case HtmlOptionId::SRC: + if( aURL.isEmpty() ) + aURL = rOption.GetString(); + break; + case HtmlOptionId::ALT: + aAlt = rOption.GetString(); + break; + case HtmlOptionId::TYPE: + if( aType.isEmpty() ) + aType = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + if( eVertOri==text::VertOrientation::NONE && eHoriOri==text::HoriOrientation::NONE ) + { + eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri ); + } + break; + case HtmlOptionId::WIDTH: + if( USHRT_MAX==aSize.Width() ) + { + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + aSize.setWidth( static_cast<long>(rOption.GetNumber()) ); + } + break; + case HtmlOptionId::HEIGHT: + if( USHRT_MAX==aSize.Height() ) + { + bPercentHeight = (rOption.GetString().indexOf('%') != -1); + aSize.setHeight( static_cast<long>(rOption.GetNumber()) ); + } + break; + case HtmlOptionId::HSPACE: + if( USHRT_MAX==aSpace.Width() ) + aSpace.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::VSPACE: + if( USHRT_MAX==aSpace.Height() ) + aSpace.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::DATA: + if (m_bXHTML && aURL.isEmpty()) + aData = rOption.GetString(); + break; + case HtmlOptionId::UNKNOWN: + if (rOption.GetTokenString().equalsIgnoreAsciiCase( + OOO_STRING_SW_HTML_O_Hidden)) + { + bHidden = !rOption.GetString().equalsIgnoreAsciiCase( + "FALSE"); + } + break; + default: break; + } + + // All parameters are passed to the plug-in. + aCmdLst.Append( rOption.GetTokenString(), rOption.GetString() ); + } + + if (aType == "image/png" && m_aEmbeds.empty()) + // Toplevel <object> for PNG -> that's an image, not an OLE object. + return false; + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ); + + // Convert the default values (except height/width, which is done by SetFrameSize()) + if( eVertOri==text::VertOrientation::NONE && eHoriOri==text::HoriOrientation::NONE ) + eVertOri = text::VertOrientation::TOP; + if( USHRT_MAX==aSpace.Width() ) + aSpace.setWidth( 0 ); + if( USHRT_MAX==aSpace.Height() ) + aSpace.setHeight( 0 ); + if( bHidden ) + { + // Size (0,0) will be changed to (MINFLY, MINFLY) in SetFrameSize() + aSize.setWidth( 0 ); aSize.setHeight( 0 ); + aSpace.setWidth( 0 ); aSpace.setHeight( 0 ); + bPercentWidth = bPercentHeight = false; + } + + // prepare the URL + INetURLObject aURLObj; + bool bHasURL = !aURL.isEmpty() && + aURLObj.SetURL( + URIHelper::SmartRel2Abs( + INetURLObject(m_sBaseURL), aURL, + URIHelper::GetMaybeFileHdl()) ); + bool bHasData = !aData.isEmpty(); + + try + { + // Strip query and everything after that for file:// URLs, browsers do + // the same. + aURLObj.SetURL(rtl::Uri::convertRelToAbs( + m_sBaseURL, SwHTMLParser::StripQueryFromPath(m_sBaseURL, aData))); + } + catch (const rtl::MalformedUriException& /*rException*/) + { + bHasData = false; + } + + // do not insert plugin if it has neither URL nor type + bool bHasType = !aType.isEmpty(); + if( !bHasURL && !bHasType && !bHasData ) + return true; + + if (!m_aEmbeds.empty()) + { + // Nested XHTML <object> element: points to replacement graphic. + SwOLENode* pOLENode = m_aEmbeds.top(); + svt::EmbeddedObjectRef& rObj = pOLENode->GetOLEObj().GetObject(); + Graphic aGraphic; + if (GraphicFilter::GetGraphicFilter().ImportGraphic(aGraphic, aURLObj) != ERRCODE_NONE) + return true; + + rObj.SetGraphic(aGraphic, aType); + + // Set the size of the OLE frame to the size of the graphic. + OutputDevice* pDevice = Application::GetDefaultDevice(); + if (aSize.getHeight() == USHRT_MAX || aSize.getWidth() == USHRT_MAX) + { + Size aPixelSize = aGraphic.GetSizePixel(pDevice); + if (aSize.getWidth() == USHRT_MAX) + aSize.setWidth(aPixelSize.getWidth()); + if (aSize.getHeight() == USHRT_MAX) + aSize.setHeight(aPixelSize.getHeight()); + } + + SwFrameFormat* pFormat = pOLENode->GetFlyFormat(); + if (!pFormat) + return true; + + SwAttrSet aAttrSet(pFormat->GetAttrSet()); + aAttrSet.ClearItem(RES_CNTNT); + Size aTwipSize(pDevice->PixelToLogic(aSize, MapMode(MapUnit::MapTwip))); + SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, aTwipSize.Width(), aTwipSize.Height()); + aAttrSet.Put(aFrameSize); + pOLENode->GetDoc()->SetFlyFrameAttr(*pFormat, aAttrSet); + return true; + } + + // create the plug-in + comphelper::EmbeddedObjectContainer aCnt; + OUString aObjName; + uno::Reference < embed::XEmbeddedObject > xObj; + if (!bHasData) + { + xObj = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_PLUGIN_CLASSID ).GetByteSequence(), aObjName ); + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + if( bHasURL ) + xSet->setPropertyValue("PluginURL", uno::makeAny( aURL ) ); + if( bHasType ) + xSet->setPropertyValue("PluginMimeType", uno::makeAny( aType ) ); + + uno::Sequence < beans::PropertyValue > aProps; + aCmdLst.FillSequence( aProps ); + xSet->setPropertyValue("PluginCommands", uno::makeAny( aProps ) ); + + } + } + } + else if (SwDocShell* pDocSh = m_xDoc->GetDocShell()) + { + // Has non-empty data attribute in XHTML: map that to an OLE object. + uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage(); + aCnt.SwitchPersistence(xStorage); + aObjName = aCnt.CreateUniqueObjectName(); + { + SvFileStream aFileStream(aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), + StreamMode::READ); + uno::Reference<io::XInputStream> xInStream; + SvMemoryStream aMemoryStream; + + // Allow any MIME type that starts with magic, unless a set of allowed types are + // specified. + auto it = m_aAllowedRTFOLEMimeTypes.find(aType); + if (m_aAllowedRTFOLEMimeTypes.empty() || it != m_aAllowedRTFOLEMimeTypes.end()) + { + OString aMagic("{\\object"); + OString aHeader(read_uInt8s_ToOString(aFileStream, aMagic.getLength())); + aFileStream.Seek(0); + if (aHeader == aMagic) + { + // OLE2 wrapped in RTF: either own format or real OLE2 embedding. + bool bOwnFormat = false; + if (SwReqIfReader::ExtractOleFromRtf(aFileStream, aMemoryStream, bOwnFormat)) + { + xInStream.set(new utl::OStreamWrapper(aMemoryStream)); + } + + if (bOwnFormat) + { + uno::Sequence<beans::PropertyValue> aMedium = comphelper::InitPropertySequence( + { { "InputStream", uno::makeAny(xInStream) }, + { "URL", uno::makeAny(OUString("private:stream")) }, + { "DocumentBaseURL", uno::makeAny(m_sBaseURL) } }); + xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &m_sBaseURL); + } + else + { + // The type is now an OLE2 container, not the original XHTML type. + aType = "application/vnd.sun.star.oleobject"; + } + } + } + + if (!xInStream.is()) + // Non-RTF case. + xInStream.set(new utl::OStreamWrapper(aFileStream)); + + if (!xObj.is()) + { + uno::Reference<io::XStream> xOutStream + = xStorage->openStreamElement(aObjName, embed::ElementModes::READWRITE); + if (aFileStream.IsOpen()) + comphelper::OStorageHelper::CopyInputToOutput(xInStream, + xOutStream->getOutputStream()); + + if (!aType.isEmpty()) + { + // Set media type of the native data. + uno::Reference<beans::XPropertySet> xOutStreamProps(xOutStream, uno::UNO_QUERY); + if (xOutStreamProps.is()) + xOutStreamProps->setPropertyValue("MediaType", uno::makeAny(aType)); + } + } + xObj = aCnt.GetEmbeddedObject(aObjName); + } + } + + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + // set the anchor + if( !bHidden ) + { + SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet ); + } + else + { + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA ); + aAnchor.SetAnchor( m_pPam->GetPoint() ); + aFrameSet.Put( aAnchor ); + aFrameSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT, text::RelOrientation::FRAME) ); + aFrameSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) ); + aFrameSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::PRINT_AREA ) ); + } + + // and the size of the frame + Size aDfltSz( HTML_DFLT_EMBED_WIDTH, HTML_DFLT_EMBED_HEIGHT ); + SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, aFrameSet ); + SetSpace( aSpace, aItemSet, aPropInfo, aFrameSet ); + + // and insert into the document + uno::Reference<lang::XInitialization> xObjInitialization(xObj, uno::UNO_QUERY); + if (xObjInitialization.is()) + { + // Request that the native data of the embedded object is not modified + // during parsing. + uno::Sequence<beans::PropertyValue> aValues{ comphelper::makePropertyValue("StreamReadOnly", + true) }; + uno::Sequence<uno::Any> aArguments{ uno::makeAny(aValues) }; + xObjInitialization->initialize(aArguments); + } + SwFrameFormat* pFlyFormat = + m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam, + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aFrameSet); + if (xObjInitialization.is()) + { + uno::Sequence<beans::PropertyValue> aValues{ comphelper::makePropertyValue("StreamReadOnly", + false) }; + uno::Sequence<uno::Any> aArguments{ uno::makeAny(aValues) }; + xObjInitialization->initialize(aArguments); + } + + // set name at FrameFormat + if( !aName.isEmpty() ) + pFlyFormat->SetName( aName ); + + // set the alternative text + SwNoTextNode *pNoTextNd = + m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx() + ->GetIndex()+1 ]->GetNoTextNode(); + pNoTextNd->SetTitle( aAlt ); + + // if applicable create frames and register auto-bound frames + if( !bHidden ) + { + // HIDDEN plug-ins should stay paragraph-bound. Since RegisterFlyFrame() + // will change paragraph-bound frames with wrap-through into a + // character-bound frame, here we must create the frames by hand. + RegisterFlyFrame( pFlyFormat ); + } + + if (!bHasData) + return true; + + SwOLENode* pOLENode = pNoTextNd->GetOLENode(); + if (!pOLENode) + return true; + + m_aEmbeds.push(pOLENode); + + return true; +} + +#if HAVE_FEATURE_JAVA +void SwHTMLParser::NewObject() +{ + OUString aClassID; + OUString aStandBy, aId, aStyle, aClass; + Size aSize( USHRT_MAX, USHRT_MAX ); + Size aSpace( 0, 0 ); + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + + bool bPercentWidth = false, bPercentHeight = false, + bDeclare = false; + // create a new Command list + m_pAppletImpl.reset(new SwApplet_Impl( m_xDoc->GetAttrPool() )); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::DECLARE: + bDeclare = true; + break; + case HtmlOptionId::CLASSID: + aClassID = rOption.GetString(); + break; + case HtmlOptionId::CODEBASE: + break; + case HtmlOptionId::DATA: + break; + case HtmlOptionId::TYPE: + break; + case HtmlOptionId::CODETYPE: + break; + case HtmlOptionId::ARCHIVE: + case HtmlOptionId::UNKNOWN: + break; + case HtmlOptionId::STANDBY: + aStandBy = rOption.GetString(); + break; + case HtmlOptionId::WIDTH: + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + aSize.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HEIGHT: + bPercentHeight = (rOption.GetString().indexOf('%') != -1); + aSize.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::ALIGN: + eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri ); + break; + case HtmlOptionId::USEMAP: + break; + case HtmlOptionId::NAME: + break; + case HtmlOptionId::HSPACE: + aSpace.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::VSPACE: + aSpace.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::BORDER: + break; + + case HtmlOptionId::SDONCLICK: + case HtmlOptionId::ONCLICK: + case HtmlOptionId::SDONMOUSEOVER: + case HtmlOptionId::ONMOUSEOVER: + case HtmlOptionId::SDONMOUSEOUT: + case HtmlOptionId::ONMOUSEOUT: + break; + default: break; + } + // All parameters are passed to the applet. + m_pAppletImpl->AppendParam( rOption.GetTokenString(), + rOption.GetString() ); + + } + + // Objects that are declared only are not evaluated. Moreover, only + // Java applets are supported. + bool bIsApplet = false; + + if( !bDeclare && aClassID.getLength() == 42 && + aClassID.startsWith("clsid:") ) + { + aClassID = aClassID.copy(6); + SvGlobalName aCID; + if( aCID.MakeId( aClassID ) ) + { + SvGlobalName aJavaCID( 0x8AD9C840UL, 0x044EU, 0x11D1U, 0xB3U, 0xE9U, + 0x00U, 0x80U, 0x5FU, 0x49U, 0x9DU, 0x93U ); + + bIsApplet = aJavaCID == aCID; + } + } + + if( !bIsApplet ) + { + m_pAppletImpl.reset(); + return; + } + + m_pAppletImpl->SetAltText( aStandBy ); + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ); + + SfxItemSet& rFrameSet = m_pAppletImpl->GetItemSet(); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( rFrameSet ); + + // set the anchor and the adjustment + SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, rFrameSet ); + + // and still the size of the frame + Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT ); + SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, rFrameSet ); + SetSpace( aSpace, aItemSet, aPropInfo, rFrameSet ); +} +#endif + +void SwHTMLParser::EndObject() +{ +#if HAVE_FEATURE_JAVA + if( !m_pAppletImpl ) + return; + if( m_pAppletImpl->CreateApplet( m_sBaseURL ) ) + { + m_pAppletImpl->FinishApplet(); + + // and insert into the document + SwFrameFormat* pFlyFormat = + m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam, + ::svt::EmbeddedObjectRef( m_pAppletImpl->GetApplet(), embed::Aspects::MSOLE_CONTENT ), + &m_pAppletImpl->GetItemSet() ); + + // set the alternative name + SwNoTextNode *pNoTextNd = + m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx() + ->GetIndex()+1 ]->GetNoTextNode(); + pNoTextNd->SetTitle( m_pAppletImpl->GetAltText() ); + + // if applicable create frames and register auto-bound frames + RegisterFlyFrame( pFlyFormat ); + + m_pAppletImpl.reset(); + } +#else + (void) this; // Silence loplugin:staticmethods +#endif +} + +#if HAVE_FEATURE_JAVA +void SwHTMLParser::InsertApplet() +{ + OUString aCodeBase, aCode, aName, aAlt, aId, aStyle, aClass; + Size aSize( USHRT_MAX, USHRT_MAX ); + Size aSpace( 0, 0 ); + bool bPercentWidth = false, bPercentHeight = false, bMayScript = false; + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + + // create a new Command list + m_pAppletImpl.reset(new SwApplet_Impl( m_xDoc->GetAttrPool() )); + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::CODEBASE: + aCodeBase = rOption.GetString(); + break; + case HtmlOptionId::CODE: + aCode = rOption.GetString(); + break; + case HtmlOptionId::NAME: + aName = rOption.GetString(); + break; + case HtmlOptionId::ALT: + aAlt = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri ); + break; + case HtmlOptionId::WIDTH: + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + aSize.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HEIGHT: + bPercentHeight = (rOption.GetString().indexOf('%') != -1); + aSize.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HSPACE: + aSpace.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::VSPACE: + aSpace.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::MAYSCRIPT: + bMayScript = true; + break; + default: break; + } + + // All parameters are passed to the applet. + m_pAppletImpl->AppendParam( rOption.GetTokenString(), + rOption.GetString() ); + } + + if( aCode.isEmpty() ) + { + m_pAppletImpl.reset(); + return; + } + + if ( !aCodeBase.isEmpty() ) + aCodeBase = INetURLObject::GetAbsURL( m_sBaseURL, aCodeBase ); + m_pAppletImpl->CreateApplet( aCode, aName, bMayScript, aCodeBase, m_sBaseURL );//, aAlt ); + m_pAppletImpl->SetAltText( aAlt ); + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ); + + SfxItemSet& rFrameSet = m_pAppletImpl->GetItemSet(); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( rFrameSet ); + + // set the anchor and the adjustment + SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, rFrameSet ); + + // and still the size or the frame + Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT ); + SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, rFrameSet ); + SetSpace( aSpace, aItemSet, aPropInfo, rFrameSet ); +} +#endif + +void SwHTMLParser::EndApplet() +{ +#if HAVE_FEATURE_JAVA + if( !m_pAppletImpl ) + return; + + m_pAppletImpl->FinishApplet(); + + // and insert into the document + SwFrameFormat* pFlyFormat = + m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam, + ::svt::EmbeddedObjectRef( m_pAppletImpl->GetApplet(), embed::Aspects::MSOLE_CONTENT ), + &m_pAppletImpl->GetItemSet()); + + // set the alternative name + SwNoTextNode *pNoTextNd = + m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx() + ->GetIndex()+1 ]->GetNoTextNode(); + pNoTextNd->SetTitle( m_pAppletImpl->GetAltText() ); + + // if applicable create frames and register auto-bound frames + RegisterFlyFrame( pFlyFormat ); + + m_pAppletImpl.reset(); +#else + (void) this; +#endif +} + +void SwHTMLParser::InsertParam() +{ +#if HAVE_FEATURE_JAVA + if( !m_pAppletImpl ) + return; + + OUString aName, aValue; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::NAME: + aName = rOption.GetString(); + break; + case HtmlOptionId::VALUE: + aValue = rOption.GetString(); + break; + default: break; + } + } + + if( aName.isEmpty() ) + return; + + m_pAppletImpl->AppendParam( aName, aValue ); +#else + (void) this; +#endif +} + +void SwHTMLParser::InsertFloatingFrame() +{ + OUString aAlt, aId, aStyle, aClass; + Size aSize( USHRT_MAX, USHRT_MAX ); + Size aSpace( 0, 0 ); + bool bPercentWidth = false, bPercentHeight = false; + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + + const HTMLOptions& rHTMLOptions = GetOptions(); + + // First fetch the options of the Writer-Frame-Format + for (const auto & rOption : rHTMLOptions) + { + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::ALT: + aAlt = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + eVertOri = rOption.GetEnum( aHTMLImgVAlignTable, eVertOri ); + eHoriOri = rOption.GetEnum( aHTMLImgHAlignTable, eHoriOri ); + break; + case HtmlOptionId::WIDTH: + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + aSize.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HEIGHT: + bPercentHeight = (rOption.GetString().indexOf('%') != -1); + aSize.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HSPACE: + aSpace.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::VSPACE: + aSpace.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + default: break; + } + } + + // and now the ones for the SfxFrame + SfxFrameDescriptor aFrameDesc; + + SfxFrameHTMLParser::ParseFrameOptions( &aFrameDesc, rHTMLOptions, m_sBaseURL ); + + // create a floating frame + comphelper::EmbeddedObjectContainer aCnt; + OUString aObjName; + uno::Reference < embed::XEmbeddedObject > xObj = aCnt.CreateEmbeddedObject( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence(), aObjName ); + + try + { + // TODO/MBA: testing + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + const OUString& aName = aFrameDesc.GetName(); + ScrollingMode eScroll = aFrameDesc.GetScrollingMode(); + bool bHasBorder = aFrameDesc.HasFrameBorder(); + Size aMargin = aFrameDesc.GetMargin(); + + xSet->setPropertyValue("FrameURL", uno::makeAny( aFrameDesc.GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); + xSet->setPropertyValue("FrameName", uno::makeAny( aName ) ); + + if ( eScroll == ScrollingMode::Auto ) + xSet->setPropertyValue("FrameIsAutoScroll", + uno::makeAny( true ) ); + else + xSet->setPropertyValue("FrameIsScrollingMode", + uno::makeAny( eScroll == ScrollingMode::Yes ) ); + + xSet->setPropertyValue("FrameIsBorder", + uno::makeAny( bHasBorder ) ); + + xSet->setPropertyValue("FrameMarginWidth", + uno::makeAny( sal_Int32( aMargin.Width() ) ) ); + + xSet->setPropertyValue("FrameMarginHeight", + uno::makeAny( sal_Int32( aMargin.Height() ) ) ); + } + } + } + catch ( uno::Exception& ) + { + } + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass ) ) + ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ); + + // fetch the ItemSet + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + // set the anchor and the adjustment + SetAnchorAndAdjustment( eVertOri, eHoriOri, aPropInfo, aFrameSet ); + + // and still the size of the frame + Size aDfltSz( HTML_DFLT_APPLET_WIDTH, HTML_DFLT_APPLET_HEIGHT ); + SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, aPropInfo, aFrameSet ); + SetSpace( aSpace, aItemSet, aPropInfo, aFrameSet ); + + // and insert into the document + SwFrameFormat* pFlyFormat = + m_xDoc->getIDocumentContentOperations().InsertEmbObject(*m_pPam, + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aFrameSet); + + // set the alternative name + SwNoTextNode *pNoTextNd = + m_xDoc->GetNodes()[ pFlyFormat->GetContent().GetContentIdx() + ->GetIndex()+1 ]->GetNoTextNode(); + pNoTextNd->SetTitle( aAlt ); + + // if applicable create frames and register auto-bound frames + RegisterFlyFrame( pFlyFormat ); + + m_bInFloatingFrame = true; +} + +sal_uInt16 SwHTMLWriter::GuessOLENodeFrameType( const SwNode& rNode ) +{ + SwOLEObj& rObj = const_cast<SwOLENode*>(rNode.GetOLENode())->GetOLEObj(); + + SwHTMLFrameType eType = HTML_FRMTYPE_OLE; + + uno::Reference < embed::XClassifiedObject > xClass = rObj.GetOleRef(); + SvGlobalName aClass( xClass->getClassID() ); + if( aClass == SvGlobalName( SO3_PLUGIN_CLASSID ) ) + { + eType = HTML_FRMTYPE_PLUGIN; + } + else if( aClass == SvGlobalName( SO3_IFRAME_CLASSID ) ) + { + eType = HTML_FRMTYPE_IFRAME; + } +#if HAVE_FEATURE_JAVA + else if( aClass == SvGlobalName( SO3_APPLET_CLASSID ) ) + { + eType = HTML_FRMTYPE_APPLET; + } +#endif + + return static_cast< sal_uInt16 >(eType); +} + +Writer& OutHTML_FrameFormatOLENode( Writer& rWrt, const SwFrameFormat& rFrameFormat, + bool bInCntnr ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1; + SwOLENode *pOLENd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ]->GetOLENode(); + + OSL_ENSURE( pOLENd, "OLE-Node expected" ); + if( !pOLENd ) + return rWrt; + + SwOLEObj &rObj = pOLENd->GetOLEObj(); + + uno::Reference < embed::XEmbeddedObject > xObj( rObj.GetOleRef() ); + if ( !svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + return rWrt; + + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + bool bHiddenEmbed = false; + + if( !xSet.is() ) + { + OSL_FAIL("Unknown Object" ); + return rWrt; + } + + HtmlFrmOpts nFrameOpts; + + // if possible output a line break before the "object" + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine( true ); + + if( !rFrameFormat.GetName().isEmpty() ) + rHTMLWrt.OutImplicitMark( rFrameFormat.GetName(), + "ole" ); + uno::Any aAny; + SvGlobalName aGlobName( xObj->getClassID() ); + OStringBuffer sOut; + sOut.append('<'); + if( aGlobName == SvGlobalName( SO3_PLUGIN_CLASSID ) ) + { + // first the plug-in specifics + sOut.append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_embed); + + OUString aStr; + OUString aURL; + aAny = xSet->getPropertyValue("PluginURL"); + if( (aAny >>= aStr) && !aStr.isEmpty() ) + { + aURL = URIHelper::simpleNormalizedMakeRelative( rWrt.GetBaseURL(), + aStr); + } + + if( !aURL.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_src) + .append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aURL, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + OUString aType; + aAny = xSet->getPropertyValue("PluginMimeType"); + if( (aAny >>= aType) && !aType.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_type) + .append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aType, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + if ((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) && + css::text::WrapTextMode_THROUGH == rFrameFormat.GetSurround().GetSurround() ) + { + // A HIDDEN plug-in + sOut.append(' ').append(OOO_STRING_SW_HTML_O_Hidden); + nFrameOpts = HTML_FRMOPTS_HIDDEN_EMBED; + bHiddenEmbed = true; + } + else + { + nFrameOpts = bInCntnr ? HTML_FRMOPTS_EMBED_CNTNR + : HTML_FRMOPTS_EMBED; + } + } + else if( aGlobName == SvGlobalName( SO3_APPLET_CLASSID ) ) + { + // or the applet specifics + + sOut.append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_applet); + + // CODEBASE + OUString aCd; + aAny = xSet->getPropertyValue("AppletCodeBase"); + if( (aAny >>= aCd) && !aCd.isEmpty() ) + { + OUString sCodeBase( URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aCd) ); + if( !sCodeBase.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_codebase) + .append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), sCodeBase, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + } + + // CODE + OUString aClass; + aAny = xSet->getPropertyValue("AppletCode"); + aAny >>= aClass; + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_code) + .append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aClass, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + + // NAME + OUString aAppletName; + aAny = xSet->getPropertyValue("AppletName"); + aAny >>= aAppletName; + if( !aAppletName.isEmpty() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_name) + .append("=\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), aAppletName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + bool bScript = false; + aAny = xSet->getPropertyValue("AppletIsScript"); + aAny >>= bScript; + if( bScript ) + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_mayscript); + + nFrameOpts = bInCntnr ? HTML_FRMOPTS_APPLET_CNTNR + : HTML_FRMOPTS_APPLET; + } + else + { + // or the Floating-Frame specifics + + sOut.append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_iframe); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + SfxFrameHTMLWriter::Out_FrameDescriptor( rWrt.Strm(), rWrt.GetBaseURL(), + xSet, + rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters ); + + nFrameOpts = bInCntnr ? HTML_FRMOPTS_IFRAME_CNTNR + : HTML_FRMOPTS_IFRAME; + } + + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + // ALT, WIDTH, HEIGHT, HSPACE, VSPACE, ALIGN + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bHiddenEmbed ) + nFrameOpts |= HTML_FRMOPTS_OLE_CSS1; + OString aEndTags = rHTMLWrt.OutFrameFormatOptions( rFrameFormat, pOLENd->GetTitle(), nFrameOpts ); + if( rHTMLWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bHiddenEmbed ) + rHTMLWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts ); + + if( aGlobName == SvGlobalName( SO3_APPLET_CLASSID ) ) + { + // output the parameters of applets as separate tags + // and write a </APPLET> + + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("AppletCommands"); + aAny >>= aProps; + + SvCommandList aCommands; + aCommands.FillFromSequence( aProps ); + std::vector<sal_uLong> aParams; + size_t i = aCommands.size(); + while( i > 0 ) + { + const SvCommand& rCommand = aCommands[ --i ]; + const OUString& rName = rCommand.GetCommand(); + SwHtmlOptType nType = SwApplet_Impl::GetOptionType( rName, true ); + if( SwHtmlOptType::TAG == nType ) + { + const OUString& rValue = rCommand.GetArgument(); + rWrt.Strm().WriteChar( ' ' ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + rWrt.Strm().WriteCharPtr( "=\"" ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rValue, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ).WriteChar( '\"' ); + } + else if( SwHtmlOptType::PARAM == nType ) + { + aParams.push_back( i ); + } + } + + rHTMLWrt.Strm().WriteChar( '>' ); + + rHTMLWrt.IncIndentLevel(); // indent the applet content + + size_t ii = aParams.size(); + while( ii > 0 ) + { + const SvCommand& rCommand = aCommands[ aParams[--ii] ]; + const OUString& rName = rCommand.GetCommand(); + const OUString& rValue = rCommand.GetArgument(); + rHTMLWrt.OutNewLine(); + OStringBuffer sBuf; + sBuf.append('<').append(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_param) + .append(' ').append(OOO_STRING_SVTOOLS_HTML_O_name) + .append("=\""); + rWrt.Strm().WriteOString( sBuf.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sBuf.append("\" ").append(OOO_STRING_SVTOOLS_HTML_O_value) + .append("=\""); + rWrt.Strm().WriteOString( sBuf.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rValue, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ).WriteCharPtr( "\">" ); + } + + rHTMLWrt.DecIndentLevel(); // indent the applet content + if( aCommands.size() ) + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_applet, false ); + } + else if( aGlobName == SvGlobalName( SO3_PLUGIN_CLASSID ) ) + { + // write plug-ins parameters as options + + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("PluginCommands"); + aAny >>= aProps; + + SvCommandList aCommands; + aCommands.FillFromSequence( aProps ); + for( size_t i = 0; i < aCommands.size(); i++ ) + { + const SvCommand& rCommand = aCommands[ i ]; + const OUString& rName = rCommand.GetCommand(); + + if( SwApplet_Impl::GetOptionType( rName, false ) == SwHtmlOptType::TAG ) + { + const OUString& rValue = rCommand.GetArgument(); + rWrt.Strm().WriteChar( ' ' ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + rWrt.Strm().WriteCharPtr( "=\"" ); + HTMLOutFuncs::Out_String( rWrt.Strm(), rValue, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ).WriteChar( '\"' ); + } + } + rHTMLWrt.Strm().WriteChar( '>' ); + } + else + { + // and for Floating-Frames just output another </IFRAME> + + rHTMLWrt.Strm().WriteChar( '>' ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_iframe, false ); + } + + if( !aEndTags.isEmpty() ) + rWrt.Strm().WriteOString( aEndTags ); + + return rWrt; +} + +Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFrameFormat, + bool bInCntnr ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + const SwFormatContent& rFlyContent = rFrameFormat.GetContent(); + sal_uLong nStt = rFlyContent.GetContentIdx()->GetIndex()+1; + SwOLENode *pOLENd = rHTMLWrt.m_pDoc->GetNodes()[ nStt ]->GetOLENode(); + + OSL_ENSURE( pOLENd, "OLE-Node expected" ); + if( !pOLENd ) + return rWrt; + + if (rHTMLWrt.mbSkipImages) + { + // If we skip images, embedded objects would be completely lost. + // Instead, try to use the HTML export of the embedded object. + uno::Reference<text::XTextContent> xTextContent = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(*rHTMLWrt.m_pDoc, const_cast<SwFrameFormat*>(&rFrameFormat)); + uno::Reference<document::XEmbeddedObjectSupplier2> xEmbeddedObjectSupplier(xTextContent, uno::UNO_QUERY); + uno::Reference<frame::XStorable> xStorable(xEmbeddedObjectSupplier->getEmbeddedObject(), uno::UNO_QUERY); + SAL_WARN_IF(!xStorable.is(), "sw.html", "OutHTML_FrameFormatOLENodeGrf: no embedded object"); + + // Figure out what is the filter name of the embedded object. + uno::Reference<lang::XServiceInfo> xServiceInfo(xStorable, uno::UNO_QUERY); + OUString aFilter; + if (xServiceInfo.is()) + { + if (xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument")) + aFilter = "HTML (StarCalc)"; + else if (xServiceInfo->supportsService("com.sun.star.text.TextDocument")) + aFilter = "HTML (StarWriter)"; + } + + if (xStorable.is() && !aFilter.isEmpty()) + { + try + { + // FIXME: exception for the simplest test document, too + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= aFilter; + aMediaDescriptor["FilterOptions"] <<= OUString("SkipHeaderFooter"); + aMediaDescriptor["OutputStream"] <<= xOutputStream; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + SAL_WARN_IF(aStream.GetSize()>=o3tl::make_unsigned(SAL_MAX_INT32), "sw.html", "Stream can't fit in OString"); + OString aData(static_cast<const char*>(aStream.GetData()), static_cast<sal_Int32>(aStream.GetSize())); + // Wrap output in a <span> tag to avoid 'HTML parser error: Unexpected end tag: p' + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span); + rWrt.Strm().WriteOString(aData); + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span, false); + } + catch ( uno::Exception& ) + { + } + } + + return rWrt; + } + + if ( !pOLENd->GetGraphic() ) + { + SAL_WARN("sw.html", "Unexpected missing OLE fallback graphic"); + return rWrt; + } + + Graphic aGraphic( *pOLENd->GetGraphic() ); + + SwDocShell* pDocSh = rHTMLWrt.m_pDoc->GetDocShell(); + bool bObjectOpened = false; + OUString aRTFType = "text/rtf"; + if (!rHTMLWrt.m_aRTFOLEMimeType.isEmpty()) + { + aRTFType = rHTMLWrt.m_aRTFOLEMimeType; + } + + if (rHTMLWrt.mbXHTML && pDocSh) + { + // Map native data to an outer <object> element. + + // Calculate the file name, which is meant to be the same as the + // replacement image, just with a .ole extension. + OUString aFileName = lcl_CalculateFileName(rHTMLWrt.GetOrigFileName(), aGraphic, "ole"); + + // Write the data. + SwOLEObj& rOLEObj = pOLENd->GetOLEObj(); + uno::Reference<embed::XEmbeddedObject> xEmbeddedObject = rOLEObj.GetOleRef(); + OUString aFileType; + SvFileStream aOutStream(aFileName, StreamMode::WRITE); + uno::Reference<io::XActiveDataStreamer> xStreamProvider; + uno::Reference<embed::XEmbedPersist2> xOwnEmbedded; + if (xEmbeddedObject.is()) + { + xStreamProvider.set(xEmbeddedObject, uno::UNO_QUERY); + xOwnEmbedded.set(xEmbeddedObject, uno::UNO_QUERY); + } + if (xStreamProvider.is()) + { + // Real OLE2 case: OleEmbeddedObject. + uno::Reference<io::XInputStream> xStream(xStreamProvider->getStream(), uno::UNO_QUERY); + if (xStream.is()) + { + std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xStream)); + if (SwReqIfReader::WrapOleInRtf(*pStream, aOutStream, *pOLENd)) + { + // Data always wrapped in RTF. + aFileType = aRTFType; + } + } + } + else if (xOwnEmbedded.is()) + { + // Our own embedded object: OCommonEmbeddedObject. + SvxMSExportOLEObjects aOLEExp(0); + // Trigger the load of the OLE object if needed, otherwise we can't + // export it. + pOLENd->GetTwipSize(); + SvMemoryStream aMemory; + tools::SvRef<SotStorage> pStorage = new SotStorage(aMemory); + aOLEExp.ExportOLEObject(rOLEObj.GetObject(), *pStorage); + pStorage->Commit(); + aMemory.Seek(0); + if (SwReqIfReader::WrapOleInRtf(aMemory, aOutStream, *pOLENd)) + { + // Data always wrapped in RTF. + aFileType = aRTFType; + } + } + else + { + // Otherwise the native data is just a grab-bag: ODummyEmbeddedObject. + const OUString& aStreamName = rOLEObj.GetCurrentPersistName(); + uno::Reference<embed::XStorage> xStorage = pDocSh->GetStorage(); + uno::Reference<io::XStream> xInStream; + try + { + // Even the native data may be missing. + xInStream = xStorage->openStreamElement(aStreamName, embed::ElementModes::READ); + } catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.html", "OutHTML_FrameFormatOLENodeGrf: failed to open stream element"); + } + if (xInStream.is()) + { + uno::Reference<io::XStream> xOutStream(new utl::OStreamWrapper(aOutStream)); + comphelper::OStorageHelper::CopyInputToOutput(xInStream->getInputStream(), + xOutStream->getOutputStream()); + } + + uno::Reference<beans::XPropertySet> xOutStreamProps(xInStream, uno::UNO_QUERY); + if (xOutStreamProps.is()) + xOutStreamProps->getPropertyValue("MediaType") >>= aFileType; + if (!aRTFType.isEmpty()) + { + aFileType = aRTFType; + } + } + aFileName = URIHelper::simpleNormalizedMakeRelative(rWrt.GetBaseURL(), aFileName); + + // Refer to this data. + if (rHTMLWrt.m_bLFPossible) + rHTMLWrt.OutNewLine(); + rWrt.Strm().WriteOString("<" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object); + rWrt.Strm().WriteOString(" data=\"" + aFileName.toUtf8() + "\""); + if (!aFileType.isEmpty()) + rWrt.Strm().WriteOString(" type=\"" + aFileType.toUtf8() + "\""); + rWrt.Strm().WriteOString(">"); + bObjectOpened = true; + rHTMLWrt.m_bLFPossible = true; + } + + OUString aGraphicURL; + OUString aMimeType; + if(!rHTMLWrt.mbEmbedImages) + { + const OUString* pTempFileName = rHTMLWrt.GetOrigFileName(); + if(pTempFileName) + aGraphicURL = *pTempFileName; + + OUString aFilterName("JPG"); + XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible; + + if (bObjectOpened) + { + aFilterName = "PNG"; + nFlags = XOutFlags::NONE; + aMimeType = "image/png"; + + if (aGraphic.GetType() == GraphicType::NONE) + { + // The OLE Object has no replacement image, write a stub. + aGraphicURL = lcl_CalculateFileName(rHTMLWrt.GetOrigFileName(), aGraphic, "png"); + osl::File aFile(aGraphicURL); + aFile.open(osl_File_OpenFlag_Create); + aFile.close(); + } + } + + ErrCode nErr = XOutBitmap::WriteGraphic( aGraphic, aGraphicURL, + aFilterName, + nFlags ); + if( nErr ) // error, don't write anything + { + rHTMLWrt.m_nWarn = WARN_SWG_POOR_LOAD; + return rWrt; + } + aGraphicURL = URIHelper::SmartRel2Abs( + INetURLObject(rWrt.GetBaseURL()), aGraphicURL, + URIHelper::GetMaybeFileHdl() ); + + } + HtmlFrmOpts nFlags = bInCntnr ? HtmlFrmOpts::GenImgAllMask + : HtmlFrmOpts::GenImgMask; + if (bObjectOpened) + nFlags |= HtmlFrmOpts::Replacement; + OutHTML_Image( rWrt, rFrameFormat, aGraphicURL, aGraphic, + pOLENd->GetTitle(), pOLENd->GetTwipSize(), + nFlags, "ole", nullptr, aMimeType ); + + if (bObjectOpened) + // Close native data. + rWrt.Strm().WriteOString("</" + rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object + ">"); + + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlreqifreader.cxx b/sw/source/filter/html/htmlreqifreader.cxx new file mode 100644 index 000000000..523bc973c --- /dev/null +++ b/sw/source/filter/html/htmlreqifreader.cxx @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "htmlreqifreader.hxx" + +#include <comphelper/scopeguard.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <rtl/strbuf.hxx> +#include <sot/storage.hxx> +#include <svtools/parrtf.hxx> +#include <svtools/rtfkeywd.hxx> +#include <svtools/rtftoken.h> +#include <tools/stream.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <vcl/cvtgrf.hxx> +#include <ndole.hxx> + +namespace +{ +/// RTF parser that just extracts a single OLE2 object from a file. +class ReqIfRtfReader : public SvRTFParser +{ +public: + ReqIfRtfReader(SvStream& rStream); + void NextToken(int nToken) override; + bool WriteObjectData(SvStream& rOLE); + +private: + bool m_bInObjData = false; + OStringBuffer m_aHex; +}; + +ReqIfRtfReader::ReqIfRtfReader(SvStream& rStream) + : SvRTFParser(rStream) +{ +} + +void ReqIfRtfReader::NextToken(int nToken) +{ + switch (nToken) + { + case '}': + m_bInObjData = false; + break; + case RTF_TEXTTOKEN: + if (m_bInObjData) + m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US)); + break; + case RTF_OBJDATA: + m_bInObjData = true; + break; + } +} + +bool ReqIfRtfReader::WriteObjectData(SvStream& rOLE) +{ + return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex.makeStringAndClear(), rOLE); +} + +/// Looks up what OLE1 calls the ClassName, see [MS-OLEDS] 2.3.8 CompObjStream. +OString ExtractOLEClassName(const tools::SvRef<SotStorage>& xStorage) +{ + OString aRet; + + SotStorageStream* pCompObj = xStorage->OpenSotStream("\1CompObj"); + if (!pCompObj) + return aRet; + + pCompObj->Seek(0); + pCompObj->SeekRel(28); // Header + if (!pCompObj->good()) + return aRet; + + sal_uInt32 nData; + pCompObj->ReadUInt32(nData); // AnsiUserType + pCompObj->SeekRel(nData); + if (!pCompObj->good()) + return aRet; + + pCompObj->ReadUInt32(nData); // AnsiClipboardFormat + pCompObj->SeekRel(nData); + if (!pCompObj->good()) + return aRet; + + pCompObj->ReadUInt32(nData); // Reserved1 + return read_uInt8s_ToOString(*pCompObj, nData - 1); // -1 because it is null-terminated +} + +/// Parses the presentation stream of an OLE2 storage. +bool ParseOLE2Presentation(SvStream& rOle2, sal_uInt32& nWidth, sal_uInt32& nHeight, + SvStream& rPresentationData) +{ + // See [MS-OLEDS] 2.3.4, OLEPresentationStream + rOle2.Seek(0); + tools::SvRef<SotStorage> pStorage = new SotStorage(rOle2); + tools::SvRef<SotStorageStream> xOle2Presentation + = pStorage->OpenSotStream("\002OlePres000", StreamMode::STD_READ); + + // Read AnsiClipboardFormat. + sal_uInt32 nMarkerOrLength = 0; + xOle2Presentation->ReadUInt32(nMarkerOrLength); + if (nMarkerOrLength != 0xffffffff) + // FormatOrAnsiString is not present + return false; + sal_uInt32 nFormatOrAnsiLength = 0; + xOle2Presentation->ReadUInt32(nFormatOrAnsiLength); + if (nFormatOrAnsiLength != 0x00000003) // CF_METAFILEPICT + return false; + + // Read TargetDeviceSize. + sal_uInt32 nTargetDeviceSize = 0; + xOle2Presentation->ReadUInt32(nTargetDeviceSize); + if (nTargetDeviceSize != 0x00000004) + // TargetDevice is present + return false; + + sal_uInt32 nAspect = 0; + xOle2Presentation->ReadUInt32(nAspect); + sal_uInt32 nLindex = 0; + xOle2Presentation->ReadUInt32(nLindex); + sal_uInt32 nAdvf = 0; + xOle2Presentation->ReadUInt32(nAdvf); + sal_uInt32 nReserved1 = 0; + xOle2Presentation->ReadUInt32(nReserved1); + xOle2Presentation->ReadUInt32(nWidth); + xOle2Presentation->ReadUInt32(nHeight); + sal_uInt32 nSize = 0; + xOle2Presentation->ReadUInt32(nSize); + + // Read Data. + if (nSize > xOle2Presentation->remainingSize()) + return false; + std::vector<char> aBuffer(nSize); + xOle2Presentation->ReadBytes(aBuffer.data(), aBuffer.size()); + rPresentationData.WriteBytes(aBuffer.data(), aBuffer.size()); + + return true; +} + +/** + * Inserts an OLE1 header before an OLE2 storage, assuming that the storage has an Ole10Native + * stream. + */ +OString InsertOLE1HeaderFromOle10NativeStream(tools::SvRef<SotStorage>& xStorage, + SwOLENode& rOLENode, SvStream& rOle1) +{ + tools::SvRef<SotStorageStream> xOle1Stream + = xStorage->OpenSotStream("\1Ole10Native", StreamMode::STD_READ); + sal_uInt32 nOle1Size = 0; + xOle1Stream->ReadUInt32(nOle1Size); + + OString aClassName("Package"); + + // Write ObjectHeader, see [MS-OLEDS] 2.2.4. + rOle1.Seek(0); + // OLEVersion. + rOle1.WriteUInt32(0x00000501); + + // FormatID is EmbeddedObject. + rOle1.WriteUInt32(0x00000002); + + // ClassName + rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1); + if (!aClassName.isEmpty()) + { + rOle1.WriteOString(aClassName); + // Null terminated pascal string. + rOle1.WriteChar(0); + } + + // TopicName. + rOle1.WriteUInt32(0); + + // ItemName. + rOle1.WriteUInt32(0); + + // NativeDataSize + rOle1.WriteUInt32(nOle1Size); + + // Write the actual native data. + rOle1.WriteStream(*xOle1Stream, nOle1Size); + + // Write Presentation. + if (!rOLENode.GetGraphic()) + { + return aClassName; + } + + const Graphic& rGraphic = *rOLENode.GetGraphic(); + Size aSize = rOLENode.GetTwipSize(); + SvMemoryStream aGraphicStream; + if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE) + { + return aClassName; + } + + auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData()); + sal_uInt64 nPresentationData = aGraphicStream.TellEnd(); + msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nPresentationData); + + // OLEVersion. + rOle1.WriteUInt32(0x00000501); + // FormatID: constant means the ClassName field is present. + rOle1.WriteUInt32(0x00000005); + // ClassName: null terminated pascal string. + OString aPresentationClassName("METAFILEPICT"); + rOle1.WriteUInt32(aPresentationClassName.getLength() + 1); + rOle1.WriteOString(aPresentationClassName); + rOle1.WriteChar(0); + // Width. + rOle1.WriteUInt32(aSize.getWidth()); + // Height. + rOle1.WriteUInt32(aSize.getHeight() * -1); + // PresentationDataSize + rOle1.WriteUInt32(8 + nPresentationData); + // Reserved1-4. + rOle1.WriteUInt16(0x0008); + rOle1.WriteUInt16(0x31b1); + rOle1.WriteUInt16(0x1dd9); + rOle1.WriteUInt16(0x0000); + rOle1.WriteBytes(pGraphicAry, nPresentationData); + + return aClassName; +} + +/// Inserts an OLE1 header before an OLE2 storage. +OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, sal_uInt32& nHeight, + SwOLENode& rOLENode) +{ + rOle2.Seek(0); + tools::SvRef<SotStorage> xStorage(new SotStorage(rOle2)); + if (xStorage->GetError() != ERRCODE_NONE) + return OString(); + + if (xStorage->IsStream("\1Ole10Native")) + { + return InsertOLE1HeaderFromOle10NativeStream(xStorage, rOLENode, rOle1); + } + + OString aClassName = ExtractOLEClassName(xStorage); + + // Write ObjectHeader, see [MS-OLEDS] 2.2.4. + rOle1.Seek(0); + // OLEVersion. + rOle1.WriteUInt32(0x00000501); + + // FormatID is EmbeddedObject. + rOle1.WriteUInt32(0x00000002); + + // ClassName + rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1); + if (!aClassName.isEmpty()) + { + rOle1.WriteOString(aClassName); + // Null terminated pascal string. + rOle1.WriteChar(0); + } + + // TopicName. + rOle1.WriteUInt32(0); + + // ItemName. + rOle1.WriteUInt32(0); + + // NativeDataSize + rOle1.WriteUInt32(rOle2.TellEnd()); + + // Write the actual native data. + rOle2.Seek(0); + rOle1.WriteStream(rOle2); + + // Write Presentation. + SvMemoryStream aPresentationData; + if (ParseOLE2Presentation(rOle2, nWidth, nHeight, aPresentationData)) + { + // OLEVersion. + rOle1.WriteUInt32(0x00000501); + // FormatID: constant means the ClassName field is present. + rOle1.WriteUInt32(0x00000005); + // ClassName: null terminated pascal string. + OString aPresentationClassName("METAFILEPICT"); + rOle1.WriteUInt32(aPresentationClassName.getLength() + 1); + rOle1.WriteOString(aPresentationClassName); + rOle1.WriteChar(0); + // Width. + rOle1.WriteUInt32(nWidth); + // Height. + rOle1.WriteUInt32(nHeight * -1); + // PresentationDataSize + sal_uInt32 nPresentationData = aPresentationData.Tell(); + rOle1.WriteUInt32(8 + nPresentationData); + // Reserved1-4. + rOle1.WriteUInt16(0x0008); + rOle1.WriteUInt16(0x31b1); + rOle1.WriteUInt16(0x1dd9); + rOle1.WriteUInt16(0x0000); + aPresentationData.Seek(0); + rOle1.WriteStream(aPresentationData, nPresentationData); + } + + return aClassName; +} + +/// Writes rGraphic with size from rOLENode to rRtf as an RTF hexdump. +void WrapOleGraphicInRtf(SvStream& rRtf, const SwOLENode& rOLENode, const Graphic& rGraphic) +{ + // Start result. + rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_RESULT); + + // Start pict. + rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PICT); + + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8"); + Size aSize(rOLENode.GetTwipSize()); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICW); + rRtf.WriteOString(OString::number(aSize.getWidth())); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICH); + rRtf.WriteOString(OString::number(aSize.getHeight())); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICWGOAL); + rRtf.WriteOString(OString::number(aSize.getWidth())); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICHGOAL); + rRtf.WriteOString(OString::number(aSize.getHeight())); + SvMemoryStream aGraphicStream; + if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE) + { + auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData()); + sal_uInt64 nSize = aGraphicStream.TellEnd(); + msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize); + rRtf.WriteCharPtr(SAL_NEWLINE_STRING); + msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &rRtf); + } + + // End pict. + rRtf.WriteCharPtr("}"); + + // End result. + rRtf.WriteCharPtr("}"); +} +} + +namespace SwReqIfReader +{ +bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat) +{ + // Add missing header/footer. + SvMemoryStream aRtf; + aRtf.WriteOString("{\\rtf1"); + aRtf.WriteStream(rRtf); + aRtf.WriteOString("}"); + aRtf.Seek(0); + + // Read the RTF markup. + tools::SvRef<ReqIfRtfReader> xReader(new ReqIfRtfReader(aRtf)); + SvParserState eState = xReader->CallParser(); + if (eState == SvParserState::Error) + return false; + + // Write the OLE2 data. + if (!xReader->WriteObjectData(rOle)) + return false; + + tools::SvRef<SotStorage> pStorage = new SotStorage(rOle); + OUString aFilterName = SvxMSDffManager::GetFilterNameFromClassID(pStorage->GetClassName()); + bOwnFormat = !aFilterName.isEmpty(); + if (!bOwnFormat) + { + // Real OLE2 data, we're done. + rOle.Seek(0); + return true; + } + + // ODF-in-OLE2 case, extract actual data. + SvMemoryStream aMemory; + SvxMSDffManager::ExtractOwnStream(*pStorage, aMemory); + rOle.Seek(0); + aMemory.Seek(0); + rOle.WriteStream(aMemory); + // Stream length is current position + 1. + rOle.SetStreamSize(aMemory.GetSize() + 1); + rOle.Seek(0); + return true; +} + +bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode) +{ + sal_uInt64 nPos = rOle2.Tell(); + comphelper::ScopeGuard g([&rOle2, nPos] { rOle2.Seek(nPos); }); + + // Write OLE1 header, then the RTF wrapper. + SvMemoryStream aOLE1; + sal_uInt32 nWidth = 0; + sal_uInt32 nHeight = 0; + OString aClassName = InsertOLE1Header(rOle2, aOLE1, nWidth, nHeight, rOLENode); + + // Start object. + rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_OBJECT); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_OBJEMB); + + // Start objclass. + rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS " "); + rRtf.WriteOString(aClassName); + // End objclass. + rRtf.WriteCharPtr("}"); + + // Object size. + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_OBJW); + rRtf.WriteOString(OString::number(nWidth)); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_OBJH); + rRtf.WriteOString(OString::number(nHeight)); + + // Start objdata. + rRtf.WriteCharPtr( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA SAL_NEWLINE_STRING); + msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOLE1.GetData()), aOLE1.GetSize(), + &rRtf); + // End objdata. + rRtf.WriteCharPtr("}"); + + if (const Graphic* pGraphic = rOLENode.GetGraphic()) + WrapOleGraphicInRtf(rRtf, rOLENode, *pGraphic); + + // End object. + rRtf.WriteCharPtr("}"); + + return true; +} + +bool WrapGraphicInRtf(const Graphic& rGraphic, const Size& rLogicSize, SvStream& rRtf) +{ + rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PICT); + + GfxLink aLink = rGraphic.GetGfxLink(); + const sal_uInt8* pGraphicAry = aLink.GetData(); + sal_uInt64 nSize = aLink.GetDataSize(); + OString aBlipType; + bool bIsWMF = false; + switch (aLink.GetType()) + { + case GfxLinkType::NativeBmp: + aBlipType = OOO_STRING_SVTOOLS_RTF_WBITMAP; + break; + case GfxLinkType::NativeJpg: + aBlipType = OOO_STRING_SVTOOLS_RTF_JPEGBLIP; + break; + case GfxLinkType::NativePng: + aBlipType = OOO_STRING_SVTOOLS_RTF_PNGBLIP; + break; + case GfxLinkType::NativeWmf: + if (aLink.IsEMF()) + aBlipType = OOO_STRING_SVTOOLS_RTF_EMFBLIP; + else + { + aBlipType = OOO_STRING_SVTOOLS_RTF_WMETAFILE; + bIsWMF = true; + } + break; + default: + break; + } + + if (aBlipType.isEmpty()) + return false; + + rRtf.WriteOString(aBlipType); + + Size aMapped(rGraphic.GetPrefSize()); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICW); + rRtf.WriteOString(OString::number(aMapped.Width())); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICH); + rRtf.WriteOString(OString::number(aMapped.Height())); + + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICWGOAL); + rRtf.WriteOString(OString::number(rLogicSize.Width())); + rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICHGOAL); + rRtf.WriteOString(OString::number(rLogicSize.Height())); + + if (bIsWMF) + { + rRtf.WriteOString(OString::number(8)); + msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize); + } + rRtf.WriteOString(SAL_NEWLINE_STRING); + + msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &rRtf); + rRtf.WriteOString(SAL_NEWLINE_STRING); + + // End pict. + rRtf.WriteCharPtr("}"); + return true; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlreqifreader.hxx b/sw/source/filter/html/htmlreqifreader.hxx new file mode 100644 index 000000000..3d0816739 --- /dev/null +++ b/sw/source/filter/html/htmlreqifreader.hxx @@ -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/. + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX + +class Graphic; +class Size; +class SvStream; +class SwOLENode; + +namespace SwReqIfReader +{ +/** + * Extracts an OLE2 container binary from an RTF fragment. + * + * @param bOwnFormat if the extracted data has an ODF class ID or not. + */ +bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat); + +/// Wraps an OLE2 container binary in an RTF fragment. +bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode); + +/** + * Wraps an image in an RTF fragment. + * + * @param rLogicSize the size used in the document model (not pixel size) + */ +bool WrapGraphicInRtf(const Graphic& rGraphic, const Size& rLogicSize, SvStream& rRtf); +} + +#endif // INCLUDED_SW_SOURCE_FILTER_HTML_HTMLREQIFREADER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmlsect.cxx b/sw/source/filter/html/htmlsect.cxx new file mode 100644 index 000000000..024464355 --- /dev/null +++ b/sw/source/filter/html/htmlsect.cxx @@ -0,0 +1,852 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <rtl/uri.hxx> + +#include <svl/urihelper.hxx> +#include <vcl/svapp.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <sfx2/linkmgr.hxx> +#include <osl/diagnose.h> + +#include <hintids.hxx> +#include <fmthdft.hxx> +#include <fmtcntnt.hxx> +#include <fmtclds.hxx> +#include <fmtanchr.hxx> +#include <fmtpdsc.hxx> +#include <frmatr.hxx> +#include <doc.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <shellio.hxx> +#include <section.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <swtable.hxx> +#include "swcss1.hxx" +#include "swhtml.hxx" + + +using namespace ::com::sun::star; + +void SwHTMLParser::NewDivision( HtmlTokenId nToken ) +{ + OUString aId, aHRef; + OUString aStyle, aLang, aDir; + OUString aClass; + SvxAdjust eAdjust = HtmlTokenId::CENTER_ON==nToken ? SvxAdjust::Center + : SvxAdjust::End; + + bool bHeader=false, bFooter=false; + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + if( HtmlTokenId::DIVISION_ON==nToken ) + eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust ); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + case HtmlOptionId::HREF: + aHRef = rOption.GetString(); + break; + case HtmlOptionId::TITLE: + { + const OUString& rType = rOption.GetString(); + if( rType.equalsIgnoreAsciiCase("header") ) + bHeader = true; + else if( rType.equalsIgnoreAsciiCase("footer") ) + bFooter = true; + } + break; + default: break; + } + } + + bool bAppended = false; + if( m_pPam->GetPoint()->nContent.GetIndex() ) + { + AppendTextNode( bHeader||bFooter||!aId.isEmpty()|| !aHRef.isEmpty() ? AM_NORMAL + : AM_NOSPACE ); + bAppended = true; + } + + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + bool bStyleParsed = false, bPositioned = false; + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + bStyleParsed = ParseStyleOptions( aStyle, aId, aClass, + aItemSet, aPropInfo, &aLang, &aDir ); + if( bStyleParsed ) + { + if ( aPropInfo.m_nColumnCount >= 2 ) + { + xCntxt.reset(); + NewMultiCol( aPropInfo.m_nColumnCount ); + return; + } + bPositioned = HtmlTokenId::DIVISION_ON == nToken && !aClass.isEmpty() && + CreateContainer(aClass, aItemSet, aPropInfo, + xCntxt.get()); + if( !bPositioned ) + { + if (aPropInfo.m_bVisible && m_aContexts.size()) + { + const std::unique_ptr<HTMLAttrContext>& pParent + = m_aContexts[m_aContexts.size() - 1]; + if (!pParent->IsVisible()) + { + // If the parent context is hidden, we are not visible, either. + aPropInfo.m_bVisible = false; + } + } + bPositioned = DoPositioning(aItemSet, aPropInfo, xCntxt.get()); + } + } + } + + if (!bPositioned && (bHeader || bFooter) && IsNewDoc() && !m_bReadingHeaderOrFooter) + { + m_bReadingHeaderOrFooter = true; + xCntxt->SetHeaderOrFooter(true); + + SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc(); + SwFrameFormat& rPageFormat = pPageDesc->GetMaster(); + + SwFrameFormat *pHdFtFormat; + bool bNew = false; + HtmlContextFlags nFlags = HtmlContextFlags::MultiColMask; + if( bHeader ) + { + pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat()); + if( !pHdFtFormat ) + { + // still no header, then create one + rPageFormat.SetFormatAttr( SwFormatHeader( true )); + pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat()); + bNew = true; + } + nFlags |= HtmlContextFlags::HeaderDist; + } + else + { + pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat()); + if( !pHdFtFormat ) + { + // still no footer, then create one + rPageFormat.SetFormatAttr( SwFormatFooter( true )); + pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat()); + bNew = true; + } + nFlags |= HtmlContextFlags::FooterDist; + } + + const SwFormatContent& rFlyContent = pHdFtFormat->GetContent(); + const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx(); + SwContentNode *pCNd; + + if( bNew ) + { + pCNd = m_xDoc->GetNodes()[rContentStIdx.GetIndex()+1] + ->GetContentNode(); + } + else + { + // Create a new node at the beginning of the section + SwNodeIndex aSttIdx( rContentStIdx, 1 ); + pCNd = m_xDoc->GetNodes().MakeTextNode( aSttIdx, + m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_TEXT)); + + // delete the current content of the section + SwPaM aDelPam( aSttIdx ); + aDelPam.SetMark(); + + const SwStartNode *pStNd = + static_cast<const SwStartNode *>( &rContentStIdx.GetNode() ); + aDelPam.GetPoint()->nNode = pStNd->EndOfSectionIndex() - 1; + + if (!PendingObjectsInPaM(aDelPam)) + { + ClearFootnotesMarksInRange(aDelPam.GetMark()->nNode, aDelPam.GetPoint()->nNode); + m_xDoc->getIDocumentContentOperations().DelFullPara(aDelPam); + } + + // update page style + for( size_t i=0; i < m_xDoc->GetPageDescCnt(); i++ ) + { + if( RES_POOLPAGE_HTML == m_xDoc->GetPageDesc(i).GetPoolFormatId() ) + { + m_xDoc->ChgPageDesc( i, *pPageDesc ); + break; + } + } + } + + SwPosition aNewPos( SwNodeIndex( rContentStIdx, 1 ), SwIndex( pCNd, 0 ) ); + SaveDocContext(xCntxt.get(), nFlags, &aNewPos); + } + else if( !bPositioned && aId.getLength() > 9 && + (aId[0] == 's' || aId[0] == 'S' ) && + (aId[1] == 'd' || aId[1] == 'D' ) ) + { + bool bEndNote = false, bFootNote = false; + if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote ) ) + bEndNote = true; + else if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote ) ) + bFootNote = true; + if( bFootNote || bEndNote ) + { + SwNodeIndex *pStartNdIdx = GetFootEndNoteSection( aId ); + if( pStartNdIdx ) + { + SwContentNode *pCNd = + m_xDoc->GetNodes()[pStartNdIdx->GetIndex()+1]->GetContentNode(); + SwNodeIndex aTmpSwNodeIndex(*pCNd); + SwPosition aNewPos( aTmpSwNodeIndex, SwIndex( pCNd, 0 ) ); + SaveDocContext(xCntxt.get(), HtmlContextFlags::MultiColMask, &aNewPos); + aId.clear(); + aPropInfo.m_aId.clear(); + } + } + } + + // We only insert sections into frames if the section is linked. + if( (!aId.isEmpty() && !bPositioned) || !aHRef.isEmpty() ) + { + // Insert section (has to be done before setting of attributes, + // because the section is inserted before the PaM position. + + // If we are in the first node of a section, we insert the section + // before the current section and not in the current section. + // Therefore we have to add a node and delete it again! + if( !bAppended ) + { + SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->nNode, -1 ); + if (aPrvNdIdx.GetNode().IsSectionNode()) + { + AppendTextNode(); + bAppended = true; + } + } + std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>); + SetAttr( true, true, pPostIts.get() ); + + // make name of section unique + const OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) ); + + if( !aHRef.isEmpty() ) + { + sal_Unicode cDelim = 255U; + sal_Int32 nPos = aHRef.lastIndexOf( cDelim ); + sal_Int32 nPos2 = -1; + if( nPos != -1 ) + { + nPos2 = aHRef.lastIndexOf( cDelim, nPos ); + if( nPos2 != -1 ) + { + sal_Int32 nTmp = nPos; + nPos = nPos2; + nPos2 = nTmp; + } + } + OUString aURL; + if( nPos == -1 ) + { + aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef, Link<OUString *, bool>(), false); + } + else + { + aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef.copy( 0, nPos ), Link<OUString *, bool>(), false ) + + OUStringChar(sfx2::cTokenSeparator); + if( nPos2 == -1 ) + { + aURL += aHRef.copy( nPos+1 ); + } + else + { + aURL += aHRef.copy( nPos+1, nPos2 - (nPos+1) ) + + OUStringChar(sfx2::cTokenSeparator) + + rtl::Uri::decode( aHRef.copy( nPos2+1 ), + rtl_UriDecodeWithCharset, + RTL_TEXTENCODING_ISO_8859_1 ); + } + } + aHRef = aURL; + } + + SwSectionData aSection( (!aHRef.isEmpty()) ? SectionType::FileLink + : SectionType::Content, aName ); + if( !aHRef.isEmpty() ) + { + aSection.SetLinkFileName( aHRef ); + aSection.SetProtectFlag(true); + } + + SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs(aFrameItemSet ); + + const SfxPoolItem *pItem; + if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + aFrameItemSet.Put( *pItem ); + aItemSet.ClearItem( RES_BACKGROUND ); + } + if( SfxItemState::SET == aItemSet.GetItemState( RES_FRAMEDIR, false, + &pItem ) ) + { + aFrameItemSet.Put( *pItem ); + aItemSet.ClearItem( RES_FRAMEDIR ); + } + + m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false ); + + // maybe jump to section + if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + + SwTextNode* pOldTextNd = + bAppended ? nullptr : m_pPam->GetPoint()->nNode.GetNode().GetTextNode(); + + m_pPam->Move( fnMoveBackward ); + + // move PageDesc and SwFormatBreak attribute from current node into + // (first) node of the section + if( pOldTextNd ) + MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->nNode.GetIndex(), + true ); + + if( pPostIts ) + { + // move still existing PostIts in the first paragraph of the table + InsertAttrs( std::move(*pPostIts) ); + pPostIts.reset(); + } + + xCntxt->SetSpansSection( true ); + + // don't insert Bookmarks with same name as sections + if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName ) + aPropInfo.m_aId.clear(); + } + else + { + xCntxt->SetAppendMode( AM_NOSPACE ); + } + + if( SvxAdjust::End != eAdjust ) + { + InsertAttr(&m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST), xCntxt.get()); + } + + // parse style + if( bStyleParsed ) + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + + xCntxt->SetVisible(aPropInfo.m_bVisible); + PushContext(xCntxt); +} + +void SwHTMLParser::EndDivision() +{ + // search for the stack entry of the token (because we still have the div stack + // we don't make a difference between DIV and CENTER) + std::unique_ptr<HTMLAttrContext> xCntxt; + auto nPos = m_aContexts.size(); + while (!xCntxt && nPos>m_nContextStMin) + { + switch( m_aContexts[--nPos]->GetToken() ) + { + case HtmlTokenId::CENTER_ON: + case HtmlTokenId::DIVISION_ON: + xCntxt = std::move(m_aContexts[nPos]); + m_aContexts.erase( m_aContexts.begin() + nPos ); + break; + default: break; + } + } + + if (xCntxt) + { + // close attribute + EndContext(xCntxt.get()); + SetAttr(); // set paragraph attributes really fast because of JavaScript + if (xCntxt->IsHeaderOrFooter()) + m_bReadingHeaderOrFooter = false; + } +} + +void SwHTMLParser::FixHeaderFooterDistance( bool bHeader, + const SwPosition *pOldPos ) +{ + SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc(); + SwFrameFormat& rPageFormat = pPageDesc->GetMaster(); + + SwFrameFormat *pHdFtFormat = + bHeader ? const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat()) + : const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat()); + OSL_ENSURE( pHdFtFormat, "No header or footer" ); + + const SwFormatContent& rFlyContent = pHdFtFormat->GetContent(); + const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx(); + + sal_uLong nPrvNxtIdx; + if( bHeader ) + { + nPrvNxtIdx = rContentStIdx.GetNode().EndOfSectionIndex()-1; + } + else + { + nPrvNxtIdx = pOldPos->nNode.GetIndex() - 1; + } + + sal_uInt16 nSpace = 0; + SwTextNode *pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx]->GetTextNode(); + if( pTextNode ) + { + const SvxULSpaceItem& rULSpace = + static_cast<const SvxULSpaceItem&>(pTextNode + ->SwContentNode::GetAttr( RES_UL_SPACE )); + + // The bottom paragraph padding becomes the padding + // to header or footer + nSpace = rULSpace.GetLower(); + + // and afterwards set to a valid value + const SvxULSpaceItem& rCollULSpace = + pTextNode->GetAnyFormatColl().GetULSpace(); + if( rCollULSpace.GetUpper() == rULSpace.GetUpper() ) + pTextNode->ResetAttr( RES_UL_SPACE ); + else + pTextNode->SetAttr( + SvxULSpaceItem( rULSpace.GetUpper(), + rCollULSpace.GetLower(), RES_UL_SPACE ) ); + } + + if( bHeader ) + { + nPrvNxtIdx = pOldPos->nNode.GetIndex(); + } + else + { + nPrvNxtIdx = rContentStIdx.GetIndex() + 1; + } + + pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx] + ->GetTextNode(); + if( pTextNode ) + { + const SvxULSpaceItem& rULSpace = + static_cast<const SvxULSpaceItem&>(pTextNode + ->SwContentNode::GetAttr( RES_UL_SPACE )); + + // The top paragraph padding becomes the padding + // to headline or footer if it is greater than the + // bottom padding of the paragraph beforehand + if( rULSpace.GetUpper() > nSpace ) + nSpace = rULSpace.GetUpper(); + + // and afterwards set to a valid value + const SvxULSpaceItem& rCollULSpace = + pTextNode->GetAnyFormatColl().GetULSpace(); + if( rCollULSpace.GetLower() == rULSpace.GetLower() ) + pTextNode->ResetAttr( RES_UL_SPACE ); + else + pTextNode->SetAttr( + SvxULSpaceItem( rCollULSpace.GetUpper(), + rULSpace.GetLower(), RES_UL_SPACE ) ); + } + + SvxULSpaceItem aULSpace( RES_UL_SPACE ); + if( bHeader ) + aULSpace.SetLower( nSpace ); + else + aULSpace.SetUpper( nSpace ); + + pHdFtFormat->SetFormatAttr( aULSpace ); +} + +bool SwHTMLParser::EndSection( bool bLFStripped ) +{ + SwEndNode *pEndNd = m_xDoc->GetNodes()[m_pPam->GetPoint()->nNode.GetIndex()+1] + ->GetEndNode(); + if( pEndNd && pEndNd->StartOfSectionNode()->IsSectionNode() ) + { + // close the section + if( !bLFStripped ) + StripTrailingPara(); + m_pPam->Move( fnMoveForward ); + return true; + } + + OSL_ENSURE( false, "Wrong PaM position at end of section" ); + + return false; +} + +bool SwHTMLParser::EndSections( bool bLFStripped ) +{ + bool bSectionClosed = false; + auto nPos = m_aContexts.size(); + while( nPos>m_nContextStMin ) + { + HTMLAttrContext *pCntxt = m_aContexts[--nPos].get(); + if( pCntxt->GetSpansSection() && EndSection( bLFStripped ) ) + { + bSectionClosed = true; + pCntxt->SetSpansSection( false ); + bLFStripped = false; + } + } + + return bSectionClosed; +} + +void SwHTMLParser::NewMultiCol( sal_uInt16 columnsFromCss ) +{ + OUString aId; + OUString aStyle, aClass, aLang, aDir; + long nWidth = 100; + sal_uInt16 nCols = columnsFromCss, nGutter = 10; + bool bPercentWidth = true; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + case HtmlOptionId::COLS: + nCols = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::WIDTH: + nWidth = rOption.GetNumber(); + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + if( bPercentWidth && nWidth>100 ) + nWidth = 100; + break; + case HtmlOptionId::GUTTER: + nGutter = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + default: break; + } + } + + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::MULTICOL_ON)); + + //.is the multicol element contained in a container? That may be the + // case for 5.0 documents. + bool bInCntnr = false; + auto i = m_aContexts.size(); + while( !bInCntnr && i > m_nContextStMin ) + bInCntnr = nullptr != m_aContexts[--i]->GetFrameItemSet(); + + // Parse style sheets, but don't position anything by now. + bool bStyleParsed = false; + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + bStyleParsed = ParseStyleOptions( aStyle, aId, aClass, + aItemSet, aPropInfo, &aLang, &aDir ); + + // Calculate width. + sal_uInt8 nPercentWidth = bPercentWidth ? static_cast<sal_uInt8>(nWidth) : 0; + SwTwips nTwipWidth = 0; + if( !bPercentWidth && nWidth && Application::GetDefaultDevice() ) + { + nTwipWidth = Application::GetDefaultDevice() + ->PixelToLogic( Size(nWidth, 0), + MapMode(MapUnit::MapTwip) ).Width(); + } + + if( !nPercentWidth && nTwipWidth < MINFLY ) + nTwipWidth = MINFLY; + + // Do positioning. + bool bPositioned = false; + if( bInCntnr || SwCSS1Parser::MayBePositioned( aPropInfo, true ) ) + { + SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs(aFrameItemSet ); + + SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, aPropInfo, + aFrameItemSet ); + + // The width is either the WIDTH attribute's value or contained + // in some style option. + SetVarSize( aPropInfo, aFrameItemSet, nTwipWidth, nPercentWidth ); + + SetSpace( Size(0,0), aItemSet, aPropInfo, aFrameItemSet ); + + // Set some other frame attributes. If the background is set, its + // it will be cleared here. That for, it won't be set at the section, + // too. + SetFrameFormatAttrs( aItemSet, + HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Padding|HtmlFrameFormatFlags::Direction, + aFrameItemSet ); + + // Insert fly frame. If the are columns, the fly frame's name is not + // the sections name but a generated one. + OUString aFlyName; + if( nCols < 2 ) + { + aFlyName = aId; + aPropInfo.m_aId.clear(); + } + + InsertFlyFrame(aFrameItemSet, xCntxt.get(), aFlyName); + + xCntxt->SetPopStack( true ); + bPositioned = true; + } + + bool bAppended = false; + if( !bPositioned ) + { + if( m_pPam->GetPoint()->nContent.GetIndex() ) + { + AppendTextNode( AM_SPACE ); + bAppended = true; + } + else + { + AddParSpace(); + } + } + + // If there are less than 2 columns, no section is inserted. + if( nCols >= 2 ) + { + if( !bAppended ) + { + // If the pam is at the start of a section, an additional text + // node must be inserted. Otherwise, the new section will be + // inserted in front of the old one. + SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->nNode, -1 ); + if (aPrvNdIdx.GetNode().IsSectionNode()) + { + AppendTextNode(); + bAppended = true; + } + } + std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>); + SetAttr( true, true, pPostIts.get() ); + + // Make section name unique. + OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) ); + SwSectionData aSection( SectionType::Content, aName ); + + SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs(aFrameItemSet ); + + if( nGutter && Application::GetDefaultDevice() ) + { + nGutter = static_cast<sal_uInt16>(Application::GetDefaultDevice() + ->PixelToLogic( Size(nGutter, 0), + MapMode(MapUnit::MapTwip) ).Width()); + } + + SwFormatCol aFormatCol; + + aFormatCol.Init( nCols, nGutter, USHRT_MAX ); + aFrameItemSet.Put( aFormatCol ); + + const SfxPoolItem *pItem; + if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + aFrameItemSet.Put( *pItem ); + aItemSet.ClearItem( RES_BACKGROUND ); + } + if( SfxItemState::SET == aItemSet.GetItemState( RES_FRAMEDIR, false, + &pItem ) ) + { + aFrameItemSet.Put( *pItem ); + aItemSet.ClearItem( RES_FRAMEDIR ); + } + m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false ); + + // Jump to section, if this is requested. + if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + + SwTextNode* pOldTextNd = + bAppended ? nullptr : m_pPam->GetPoint()->nNode.GetNode().GetTextNode(); + + m_pPam->Move( fnMoveBackward ); + + // Move PageDesc and SwFormatBreak attributes of the current node + // to the section's first node. + if( pOldTextNd ) + MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->nNode.GetIndex(), + true ); + + if( pPostIts ) + { + // Move pending PostIts into the section. + InsertAttrs( std::move(*pPostIts) ); + pPostIts.reset(); + } + + xCntxt->SetSpansSection( true ); + + // Insert a bookmark if its name differs from the section's name only. + if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName ) + aPropInfo.m_aId.clear(); + } + + // Additional attributes must be set as hard ones. + if( bStyleParsed ) + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + + PushContext(xCntxt); +} + +void SwHTMLParser::InsertFlyFrame( const SfxItemSet& rItemSet, + HTMLAttrContext *pCntxt, + const OUString& rName ) +{ + RndStdIds eAnchorId = + rItemSet.Get( RES_ANCHOR ).GetAnchorId(); + + // create frame + SwFlyFrameFormat* pFlyFormat = m_xDoc->MakeFlySection( eAnchorId, m_pPam->GetPoint(), + &rItemSet ); + if( !rName.isEmpty() ) + pFlyFormat->SetName( rName ); + + RegisterFlyFrame( pFlyFormat ); + + const SwFormatContent& rFlyContent = pFlyFormat->GetContent(); + const SwNodeIndex& rFlyCntIdx = *rFlyContent.GetContentIdx(); + SwContentNode *pCNd = m_xDoc->GetNodes()[rFlyCntIdx.GetIndex()+1] + ->GetContentNode(); + + SwPosition aNewPos( SwNodeIndex( rFlyCntIdx, 1 ), SwIndex( pCNd, 0 ) ); + const HtmlContextFlags nFlags = HtmlContextFlags::ProtectStack|HtmlContextFlags::StripPara; + SaveDocContext( pCntxt, nFlags, &aNewPos ); +} + +void SwHTMLParser::MovePageDescAttrs( SwNode *pSrcNd, + sal_uLong nDestIdx, + bool bFormatBreak ) +{ + SwContentNode* pDestContentNd = + m_xDoc->GetNodes()[nDestIdx]->GetContentNode(); + + OSL_ENSURE( pDestContentNd, "Why is the target not a Content-Node?" ); + + if( pSrcNd->IsContentNode() ) + { + SwContentNode* pSrcContentNd = pSrcNd->GetContentNode(); + + const SfxPoolItem* pItem; + if( SfxItemState::SET == pSrcContentNd->GetSwAttrSet() + .GetItemState( RES_PAGEDESC, false, &pItem ) && + static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc() ) + { + pDestContentNd->SetAttr( *pItem ); + pSrcContentNd->ResetAttr( RES_PAGEDESC ); + } + if( SfxItemState::SET == pSrcContentNd->GetSwAttrSet() + .GetItemState( RES_BREAK, false, &pItem ) ) + { + switch( static_cast<const SvxFormatBreakItem *>(pItem)->GetBreak() ) + { + case SvxBreak::PageBefore: + case SvxBreak::PageAfter: + case SvxBreak::PageBoth: + if( bFormatBreak ) + pDestContentNd->SetAttr( *pItem ); + pSrcContentNd->ResetAttr( RES_BREAK ); + break; + default: + break; + } + } + } + else if( pSrcNd->IsTableNode() ) + { + SwFrameFormat *pFrameFormat = pSrcNd->GetTableNode()->GetTable().GetFrameFormat(); + + const SfxPoolItem* pItem; + if( SfxItemState::SET == pFrameFormat->GetAttrSet(). + GetItemState( RES_PAGEDESC, false, &pItem ) ) + { + if (pDestContentNd) + pDestContentNd->SetAttr(*pItem); + pFrameFormat->ResetFormatAttr( RES_PAGEDESC ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmltab.cxx b/sw/source/filter/html/htmltab.cxx new file mode 100644 index 000000000..87f8858c9 --- /dev/null +++ b/sw/source/filter/html/htmltab.cxx @@ -0,0 +1,5299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/flagguard.hxx> +#include <vcl/svapp.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/spltitem.hxx> +#include <unotools/configmgr.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <svl/urihelper.hxx> +#include <svl/listener.hxx> +#include <sal/log.hxx> + +#include <dcontact.hxx> +#include <fmtornt.hxx> +#include <frmfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtsrnd.hxx> +#include <fmtpdsc.hxx> +#include <fmtcntnt.hxx> +#include <fmtanchr.hxx> +#include <fmtlsplt.hxx> +#include <frmatr.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <ndtxt.hxx> +#include <shellio.hxx> +#include <poolfmt.hxx> +#include <swtable.hxx> +#include <cellatr.hxx> +#include <htmltbl.hxx> +#include <swtblfmt.hxx> +#include "htmlnum.hxx" +#include "swhtml.hxx" +#include "swcss1.hxx" +#include <txtftn.hxx> +#include <itabenum.hxx> +#include <tblafmt.hxx> +#include <SwStyleNameMapper.hxx> +#include <frameformats.hxx> + +#define NETSCAPE_DFLT_BORDER 1 +#define NETSCAPE_DFLT_CELLSPACING 2 + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; + +static HTMLOptionEnum<sal_Int16> const aHTMLTableVAlignTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::NONE }, + { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER }, + { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::BOTTOM }, + { nullptr, 0 } +}; + +// table tags options + +namespace { + +struct HTMLTableOptions +{ + sal_uInt16 nCols; + sal_uInt16 nWidth; + sal_uInt16 nHeight; + sal_uInt16 nCellPadding; + sal_uInt16 nCellSpacing; + sal_uInt16 nBorder; + sal_uInt16 nHSpace; + sal_uInt16 nVSpace; + + SvxAdjust eAdjust; + sal_Int16 eVertOri; + HTMLTableFrame eFrame; + HTMLTableRules eRules; + + bool bPercentWidth : 1; + bool bTableAdjust : 1; + bool bBGColor : 1; + + Color aBorderColor; + Color aBGColor; + + OUString aBGImage, aStyle, aId, aClass, aDir; + + HTMLTableOptions( const HTMLOptions& rOptions, SvxAdjust eParentAdjust ); +}; + +class HTMLTableContext +{ + SwHTMLNumRuleInfo aNumRuleInfo; // Numbering valid before the table + + SwTableNode *pTableNd; // table node + SwFrameFormat *pFrameFormat; // the Fly frame::Frame, containing the table + std::unique_ptr<SwPosition> pPos; // position behind the table + + size_t nContextStAttrMin; + size_t nContextStMin; + + bool bRestartPRE : 1; + bool bRestartXMP : 1; + bool bRestartListing : 1; + + HTMLTableContext(const HTMLTableContext&) = delete; + HTMLTableContext& operator=(const HTMLTableContext&) = delete; + +public: + + std::shared_ptr<HTMLAttrTable> xAttrTab; // attributes + + HTMLTableContext( SwPosition *pPs, size_t nCntxtStMin, + size_t nCntxtStAttrMin ) : + pTableNd( nullptr ), + pFrameFormat( nullptr ), + pPos( pPs ), + nContextStAttrMin( nCntxtStAttrMin ), + nContextStMin( nCntxtStMin ), + bRestartPRE( false ), + bRestartXMP( false ), + bRestartListing( false ), + xAttrTab(std::make_shared<HTMLAttrTable>()) + { + memset(xAttrTab.get(), 0, sizeof(HTMLAttrTable)); + } + + void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { aNumRuleInfo.Set(rInf); } + const SwHTMLNumRuleInfo& GetNumInfo() const { return aNumRuleInfo; }; + + void SavePREListingXMP( SwHTMLParser& rParser ); + void RestorePREListingXMP( SwHTMLParser& rParser ); + + SwPosition *GetPos() const { return pPos.get(); } + + void SetTableNode( SwTableNode *pNd ) { pTableNd = pNd; } + SwTableNode *GetTableNode() const { return pTableNd; } + + void SetFrameFormat( SwFrameFormat *pFormat ) { pFrameFormat = pFormat; } + SwFrameFormat *GetFrameFormat() const { return pFrameFormat; } + + size_t GetContextStMin() const { return nContextStMin; } + size_t GetContextStAttrMin() const { return nContextStAttrMin; } +}; + +} + +// Cell content is a linked list with SwStartNodes and +// HTMLTables. + +class HTMLTableCnts +{ + std::unique_ptr<HTMLTableCnts> m_pNext; // next content + + // Only one of the next two pointers must be set! + const SwStartNode *m_pStartNode; // a paragraph + std::shared_ptr<HTMLTable> m_xTable; // a table + + std::shared_ptr<SwHTMLTableLayoutCnts> m_xLayoutInfo; + + bool m_bNoBreak; + + void InitCtor(); + +public: + + explicit HTMLTableCnts(const SwStartNode* pStNd); + explicit HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab); + + ~HTMLTableCnts(); // only allowed in ~HTMLTableCell + + // Determine SwStartNode and HTMLTable respectively + const SwStartNode *GetStartNode() const { return m_pStartNode; } + const std::shared_ptr<HTMLTable>& GetTable() const { return m_xTable; } + std::shared_ptr<HTMLTable>& GetTable() { return m_xTable; } + + // Add a new node at the end of the list + void Add( std::unique_ptr<HTMLTableCnts> pNewCnts ); + + // Determine next node + const HTMLTableCnts *Next() const { return m_pNext.get(); } + HTMLTableCnts *Next() { return m_pNext.get(); } + + inline void SetTableBox( SwTableBox *pBox ); + + void SetNoBreak() { m_bNoBreak = true; } + + const std::shared_ptr<SwHTMLTableLayoutCnts>& CreateLayoutInfo(); +}; + +namespace { + +// Cell of a HTML table +class HTMLTableCell +{ + std::shared_ptr<HTMLTableCnts> m_xContents; // cell content + std::shared_ptr<SvxBrushItem> m_xBGBrush; // cell background + std::shared_ptr<SvxBoxItem> m_xBoxItem; + + double m_nValue; + sal_uInt32 m_nNumFormat; + sal_uInt16 m_nRowSpan; // cell ROWSPAN + sal_uInt16 m_nColSpan; // cell COLSPAN + sal_uInt16 m_nWidth; // cell WIDTH + sal_Int16 m_eVertOrient; // vertical alignment of the cell + bool m_bProtected : 1; // cell must not filled + bool m_bRelWidth : 1; // nWidth is given in % + bool m_bHasNumFormat : 1; + bool m_bHasValue : 1; + bool m_bNoWrap : 1; + bool mbCovered : 1; + +public: + + HTMLTableCell(); // new cells always empty + + // Fill a not empty cell + void Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan, + sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrush, + std::shared_ptr<SvxBoxItem> const& rBoxItem, + bool bHasNumFormat, sal_uInt32 nNumFormat, + bool bHasValue, double nValue, bool bNoWrap, bool bCovered ); + + // Protect an empty 1x1 cell + void SetProtected(); + + // Set/Get cell content + void SetContents(std::shared_ptr<HTMLTableCnts> const& rCnts) { m_xContents = rCnts; } + const std::shared_ptr<HTMLTableCnts>& GetContents() const { return m_xContents; } + + // Set/Get cell ROWSPAN/COLSPAN + void SetRowSpan( sal_uInt16 nRSpan ) { m_nRowSpan = nRSpan; } + sal_uInt16 GetRowSpan() const { return m_nRowSpan; } + + void SetColSpan( sal_uInt16 nCSpan ) { m_nColSpan = nCSpan; } + sal_uInt16 GetColSpan() const { return m_nColSpan; } + + inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth ); + + const std::shared_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; } + const std::shared_ptr<SvxBoxItem>& GetBoxItem() const { return m_xBoxItem; } + + inline bool GetNumFormat( sal_uInt32& rNumFormat ) const; + inline bool GetValue( double& rValue ) const; + + sal_Int16 GetVertOri() const { return m_eVertOrient; } + + // Is the cell filled or protected ? + bool IsUsed() const { return m_xContents || m_bProtected; } + + std::unique_ptr<SwHTMLTableLayoutCell> CreateLayoutInfo(); + + bool IsCovered() const { return mbCovered; } +}; + +} + +// Row of a HTML table +typedef std::vector<HTMLTableCell> HTMLTableCells; + +namespace { + +class HTMLTableRow +{ + HTMLTableCells m_aCells; ///< cells of the row + std::unique_ptr<SvxBrushItem> xBGBrush; // background of cell from STYLE + + SvxAdjust eAdjust; + sal_uInt16 nHeight; // options of <TR>/<TD> + sal_uInt16 nEmptyRows; // number of empty rows are following + sal_Int16 eVertOri; + bool bIsEndOfGroup : 1; + bool bBottomBorder : 1; // Is there a line after the row? + +public: + + explicit HTMLTableRow( sal_uInt16 nCells ); // cells of the row are empty + + void SetBottomBorder(bool bIn) { bBottomBorder = bIn; } + bool GetBottomBorder() const { return bBottomBorder; } + + inline void SetHeight( sal_uInt16 nHeight ); + sal_uInt16 GetHeight() const { return nHeight; } + + const HTMLTableCell& GetCell(sal_uInt16 nCell) const; + HTMLTableCell& GetCell(sal_uInt16 nCell) + { + return const_cast<HTMLTableCell&>(const_cast<const HTMLTableRow&>(*this).GetCell(nCell)); + } + + void SetAdjust( SvxAdjust eAdj ) { eAdjust = eAdj; } + SvxAdjust GetAdjust() const { return eAdjust; } + + void SetVertOri( sal_Int16 eV) { eVertOri = eV; } + sal_Int16 GetVertOri() const { return eVertOri; } + + void SetBGBrush(std::unique_ptr<SvxBrushItem>& rBrush ) { xBGBrush = std::move(rBrush); } + const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return xBGBrush; } + + void SetEndOfGroup() { bIsEndOfGroup = true; } + bool IsEndOfGroup() const { return bIsEndOfGroup; } + + void IncEmptyRows() { nEmptyRows++; } + sal_uInt16 GetEmptyRows() const { return nEmptyRows; } + + // Expand row by adding empty cells + void Expand( sal_uInt16 nCells, bool bOneCell=false ); + + // Shrink row by deleting empty cells + void Shrink( sal_uInt16 nCells ); +}; + +// Column of a HTML table +class HTMLTableColumn +{ + bool bIsEndOfGroup; + + sal_uInt16 nWidth; // options of <COL> + bool bRelWidth; + + SvxAdjust eAdjust; + sal_Int16 eVertOri; + + SwFrameFormat *aFrameFormats[6]; + + static inline sal_uInt16 GetFrameFormatIdx( bool bBorderLine, + sal_Int16 eVertOri ); + +public: + + bool bLeftBorder; // is there a line before the column + + HTMLTableColumn(); + + inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth); + + void SetAdjust( SvxAdjust eAdj ) { eAdjust = eAdj; } + SvxAdjust GetAdjust() const { return eAdjust; } + + void SetVertOri( sal_Int16 eV) { eVertOri = eV; } + sal_Int16 GetVertOri() const { return eVertOri; } + + void SetEndOfGroup() { bIsEndOfGroup = true; } + bool IsEndOfGroup() const { return bIsEndOfGroup; } + + inline void SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine, + sal_Int16 eVertOri ); + inline SwFrameFormat *GetFrameFormat( bool bBorderLine, + sal_Int16 eVertOri ) const; + + std::unique_ptr<SwHTMLTableLayoutColumn> CreateLayoutInfo(); +}; + +} + +// HTML table +typedef std::vector<HTMLTableRow> HTMLTableRows; + +typedef std::vector<HTMLTableColumn> HTMLTableColumns; + +typedef std::vector<SdrObject *> SdrObjects; + +class HTMLTable +{ + OUString m_aId; + OUString m_aStyle; + OUString m_aClass; + OUString m_aDir; + + std::unique_ptr<SdrObjects> m_pResizeDrawObjects;// SDR objects + std::unique_ptr<std::vector<sal_uInt16>> m_pDrawObjectPercentWidths; // column of draw object and its rel. width + + HTMLTableRows m_aRows; ///< table rows + HTMLTableColumns m_aColumns; ///< table columns + + sal_uInt16 m_nRows; // number of rows + sal_uInt16 m_nCols; // number of columns + sal_uInt16 m_nFilledColumns; // number of filled columns + + sal_uInt16 m_nCurrentRow; // current Row + sal_uInt16 m_nCurrentColumn; // current Column + + sal_uInt16 m_nLeftMargin; // Space to the left margin (from paragraph edge) + sal_uInt16 m_nRightMargin; // Space to the right margin (from paragraph edge) + + sal_uInt16 m_nCellPadding; // Space from border to Text + sal_uInt16 m_nCellSpacing; // Space between two cells + sal_uInt16 m_nHSpace; + sal_uInt16 m_nVSpace; + + sal_uInt16 m_nBoxes; // number of boxes in the table + + const SwStartNode *m_pPrevStartNode; // the Table-Node or the Start-Node of the section before + const SwTable *m_pSwTable; // SW-Table (only on Top-Level) +public: + std::unique_ptr<SwTableBox> m_xBox1; // TableBox, generated when the Top-Level-Table was build +private: + SwTableBoxFormat *m_pBoxFormat; // frame::Frame-Format from SwTableBox + SwTableLineFormat *m_pLineFormat; // frame::Frame-Format from SwTableLine + SwTableLineFormat *m_pLineFrameFormatNoHeight; + std::unique_ptr<SvxBrushItem> m_xBackgroundBrush; // background of the table + std::unique_ptr<SvxBrushItem> m_xInheritedBackgroundBrush; // "inherited" background of the table + const SwStartNode *m_pCaptionStartNode; // Start-Node of the table-caption + //lines for the border + SvxBorderLine m_aTopBorderLine; + SvxBorderLine m_aBottomBorderLine; + SvxBorderLine m_aLeftBorderLine; + SvxBorderLine m_aRightBorderLine; + SvxBorderLine m_aBorderLine; + SvxBorderLine m_aInheritedLeftBorderLine; + SvxBorderLine m_aInheritedRightBorderLine; + bool m_bTopBorder; // is there a line on the top of the table + bool m_bRightBorder; // is there a line on the top right of the table + bool m_bTopAllowed; // is it allowed to set the border? + bool m_bRightAllowed; + bool m_bFillerTopBorder; // gets the left/right filler-cell a border on the + bool m_bFillerBottomBorder; // top or in the bottom + bool m_bInheritedLeftBorder; + bool m_bInheritedRightBorder; + bool m_bBordersSet; // the border is set already + bool m_bForceFrame; + bool m_bTableAdjustOfTag; // comes nTableAdjust from <TABLE>? + sal_uInt32 m_nHeadlineRepeat; // repeating rows + bool m_bIsParentHead; + bool m_bHasParentSection; + bool m_bHasToFly; + bool m_bFixedCols; + bool m_bColSpec; // where there COL(GROUP)-elements? + bool m_bPercentWidth; // width is declared in % + + SwHTMLParser *m_pParser; // the current parser + std::unique_ptr<HTMLTableCnts> m_xParentContents; + + std::unique_ptr<HTMLTableContext> m_pContext; // the context of the table + + std::shared_ptr<SwHTMLTableLayout> m_xLayoutInfo; + + // the following parameters are from the <TABLE>-Tag + sal_uInt16 m_nWidth; // width of the table + sal_uInt16 m_nHeight; // absolute height of the table + SvxAdjust m_eTableAdjust; // drawing::Alignment of the table + sal_Int16 m_eVertOrientation; // Default vertical direction of the cells + sal_uInt16 m_nBorder; // width of the external border + HTMLTableFrame m_eFrame; // frame around the table + HTMLTableRules m_eRules; // frame in the table + bool m_bTopCaption; // Caption of the table + + void InitCtor(const HTMLTableOptions& rOptions); + + // Correction of the Row-Spans for all cells above the chosen cell and the cell itself for the indicated content. The chosen cell gets the Row-Span 1 + void FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, const HTMLTableCnts *pCnts ); + + // Protects the chosen cell and the cells among + void ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan ); + + // Looking for the SwStartNodes of the box ahead + // If nRow==nCell==USHRT_MAX, return the last Start-Node of the table. + const SwStartNode* GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCell ) const; + + sal_uInt16 GetTopCellSpace( sal_uInt16 nRow ) const; + sal_uInt16 GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const; + + // Conforming of the frame::Frame-Format of the box + void FixFrameFormat( SwTableBox *pBox, sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + bool bFirstPara=true, bool bLastPara=true ) const; + + // Create a table with the content (lines/boxes) + void MakeTable_( SwTableBox *pUpper ); + + // Generate a new SwTableBox, which contains a SwStartNode + SwTableBox *NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ) const; + + // Generate a SwTableLine from the cells of the rectangle + // (nTopRow/nLeftCol) inclusive to (nBottomRow/nRightRow) exclusive + SwTableLine *MakeTableLine( SwTableBox *pUpper, + sal_uInt16 nTopRow, sal_uInt16 nLeftCol, + sal_uInt16 nBottomRow, sal_uInt16 nRightCol ); + + // Generate a SwTableBox from the content of the cell + SwTableBox *MakeTableBox( SwTableLine *pUpper, + HTMLTableCnts *pCnts, + sal_uInt16 nTopRow, sal_uInt16 nLeftCol, + sal_uInt16 nBootomRow, sal_uInt16 nRightCol ); + + // Autolayout-Algorithm + + // Setting the border with the help of guidelines of the Parent-Table + void InheritBorders( const HTMLTable *pParent, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, + bool bFirstPara, bool bLastPara ); + + // Inherit the left and the right border of the surrounding table + void InheritVertBorders( const HTMLTable *pParent, + sal_uInt16 nCol, sal_uInt16 nColSpan ); + + // Set the border with the help of the information from the user + void SetBorders(); + + // is the border already set? + bool BordersSet() const { return m_bBordersSet; } + + const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBackgroundBrush; } + const std::unique_ptr<SvxBrushItem>& GetInhBGBrush() const { return m_xInheritedBackgroundBrush; } + + sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine, + bool bWithDistance=false ) const; + +public: + + bool m_bFirstCell; // is there a cell created already? + + HTMLTable(SwHTMLParser* pPars, + bool bParHead, bool bHasParentSec, + bool bHasToFly, + const HTMLTableOptions& rOptions); + + ~HTMLTable(); + + // Identifying of a cell + const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const; + HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) + { + return const_cast<HTMLTableCell&>(const_cast<const HTMLTable&>(*this).GetCell(nRow, nCell)); + } + + // set/determine caption + inline void SetCaption( const SwStartNode *pStNd, bool bTop ); + const SwStartNode *GetCaptionStartNode() const { return m_pCaptionStartNode; } + bool IsTopCaption() const { return m_bTopCaption; } + + SvxAdjust GetTableAdjust( bool bAny ) const + { + return (m_bTableAdjustOfTag || bAny) ? m_eTableAdjust : SvxAdjust::End; + } + + sal_uInt16 GetHSpace() const { return m_nHSpace; } + sal_uInt16 GetVSpace() const { return m_nVSpace; } + + // get inherited drawing::Alignment of rows and column + SvxAdjust GetInheritedAdjust() const; + sal_Int16 GetInheritedVertOri() const; + + // Insert a cell on the current position + void InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + sal_uInt16 nWidth, bool bRelWidth, sal_uInt16 nHeight, + sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrushItem, + std::shared_ptr<SvxBoxItem> const& rBoxItem, + bool bHasNumFormat, sal_uInt32 nNumFormat, + bool bHasValue, double nValue, bool bNoWrap ); + + // announce the start/end of a new row + void OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOri, std::unique_ptr<SvxBrushItem>& rBGBrush); + void CloseRow( bool bEmpty ); + + // announce the end of a new section + inline void CloseSection( bool bHead ); + + // announce the end of a column-group + inline void CloseColGroup( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth, + SvxAdjust eAdjust, sal_Int16 eVertOri ); + + // insert a new column + void InsertCol( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth, + SvxAdjust eAdjust, sal_Int16 eVertOri ); + + // End a table definition (needs to be called for every table) + void CloseTable(); + + // Construct a SwTable (including child tables) + void MakeTable( SwTableBox *pUpper, sal_uInt16 nAbsAvail, + sal_uInt16 nRelAvail=0, sal_uInt16 nAbsLeftSpace=0, + sal_uInt16 nAbsRightSpace=0, sal_uInt16 nInhAbsSpace=0 ); + + bool IsNewDoc() const { return m_pParser->IsNewDoc(); } + + void SetHasParentSection( bool bSet ) { m_bHasParentSection = bSet; } + bool HasParentSection() const { return m_bHasParentSection; } + + void SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts) { m_xParentContents = std::move(pCnts); } + std::unique_ptr<HTMLTableCnts>& GetParentContents() { return m_xParentContents; } + + void MakeParentContents(); + + bool HasToFly() const { return m_bHasToFly; } + + void SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt, + sal_uInt16 nLeft, sal_uInt16 nRight, + const SwTable *pSwTab=nullptr, bool bFrcFrame=false ); + + HTMLTableContext *GetContext() const { return m_pContext.get(); } + + const std::shared_ptr<SwHTMLTableLayout>& CreateLayoutInfo(); + + bool HasColTags() const { return m_bColSpec; } + + sal_uInt16 IncGrfsThatResize() { return m_pSwTable ? const_cast<SwTable *>(m_pSwTable)->IncGrfsThatResize() : 0; } + + void RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth ); + + const SwTable *GetSwTable() const { return m_pSwTable; } + + void SetBGBrush(const SvxBrushItem& rBrush) { m_xBackgroundBrush.reset(new SvxBrushItem(rBrush)); } + + const OUString& GetId() const { return m_aId; } + const OUString& GetClass() const { return m_aClass; } + const OUString& GetStyle() const { return m_aStyle; } + const OUString& GetDirection() const { return m_aDir; } + + void IncBoxCount() { m_nBoxes++; } + bool IsOverflowing() const { return m_nBoxes > 64000; } + + bool PendingDrawObjectsInPaM(SwPaM& rPam) const; +}; + +void HTMLTableCnts::InitCtor() +{ + m_pNext = nullptr; + m_xLayoutInfo.reset(); + m_bNoBreak = false; +} + +HTMLTableCnts::HTMLTableCnts(const SwStartNode* pStNd) + : m_pStartNode(pStNd) +{ + InitCtor(); +} + +HTMLTableCnts::HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab) + : m_pStartNode(nullptr) + , m_xTable(rTab) +{ + InitCtor(); +} + +HTMLTableCnts::~HTMLTableCnts() +{ + m_xTable.reset(); // we don't need the tables anymore + m_pNext.reset(); +} + +void HTMLTableCnts::Add( std::unique_ptr<HTMLTableCnts> pNewCnts ) +{ + HTMLTableCnts *pCnts = this; + + while( pCnts->m_pNext ) + pCnts = pCnts->m_pNext.get(); + + pCnts->m_pNext = std::move(pNewCnts); +} + +inline void HTMLTableCnts::SetTableBox( SwTableBox *pBox ) +{ + OSL_ENSURE(m_xLayoutInfo.get(), "There is no layout info"); + if (m_xLayoutInfo) + m_xLayoutInfo->SetTableBox(pBox); +} + +const std::shared_ptr<SwHTMLTableLayoutCnts>& HTMLTableCnts::CreateLayoutInfo() +{ + if (!m_xLayoutInfo) + { + std::shared_ptr<SwHTMLTableLayoutCnts> xNextInfo; + if (m_pNext) + xNextInfo = m_pNext->CreateLayoutInfo(); + std::shared_ptr<SwHTMLTableLayout> xTableInfo; + if (m_xTable) + xTableInfo = m_xTable->CreateLayoutInfo(); + m_xLayoutInfo = std::make_shared<SwHTMLTableLayoutCnts>(m_pStartNode, xTableInfo, m_bNoBreak, xNextInfo); + } + + return m_xLayoutInfo; +} + +HTMLTableCell::HTMLTableCell(): + m_nValue(0), + m_nNumFormat(0), + m_nRowSpan(1), + m_nColSpan(1), + m_nWidth( 0 ), + m_eVertOrient( text::VertOrientation::NONE ), + m_bProtected(false), + m_bRelWidth( false ), + m_bHasNumFormat(false), + m_bHasValue(false), + m_bNoWrap(false), + mbCovered(false) +{} + +void HTMLTableCell::Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan, + sal_Int16 eVert, std::shared_ptr<SvxBrushItem> const& rBrush, + std::shared_ptr<SvxBoxItem> const& rBoxItem, + bool bHasNF, sal_uInt32 nNF, bool bHasV, double nVal, + bool bNWrap, bool bCovered ) +{ + m_xContents = rCnts; + m_nRowSpan = nRSpan; + m_nColSpan = nCSpan; + m_bProtected = false; + m_eVertOrient = eVert; + m_xBGBrush = rBrush; + m_xBoxItem = rBoxItem; + + m_bHasNumFormat = bHasNF; + m_bHasValue = bHasV; + m_nNumFormat = nNF; + m_nValue = nVal; + + m_bNoWrap = bNWrap; + mbCovered = bCovered; +} + +inline void HTMLTableCell::SetWidth( sal_uInt16 nWdth, bool bRelWdth ) +{ + m_nWidth = nWdth; + m_bRelWidth = bRelWdth; +} + +void HTMLTableCell::SetProtected() +{ + // The content of this cell doesn't have to be anchored anywhere else, + // since they're not gonna be deleted + + m_xContents.reset(); + + // Copy background color + if (m_xBGBrush) + m_xBGBrush = std::make_shared<SvxBrushItem>(*m_xBGBrush); + + m_nRowSpan = 1; + m_nColSpan = 1; + m_bProtected = true; +} + +inline bool HTMLTableCell::GetNumFormat( sal_uInt32& rNumFormat ) const +{ + rNumFormat = m_nNumFormat; + return m_bHasNumFormat; +} + +inline bool HTMLTableCell::GetValue( double& rValue ) const +{ + rValue = m_nValue; + return m_bHasValue; +} + +std::unique_ptr<SwHTMLTableLayoutCell> HTMLTableCell::CreateLayoutInfo() +{ + std::shared_ptr<SwHTMLTableLayoutCnts> xCntInfo; + if (m_xContents) + xCntInfo = m_xContents->CreateLayoutInfo(); + return std::unique_ptr<SwHTMLTableLayoutCell>(new SwHTMLTableLayoutCell(xCntInfo, m_nRowSpan, m_nColSpan, m_nWidth, + m_bRelWidth, m_bNoWrap)); +} + +HTMLTableRow::HTMLTableRow(sal_uInt16 const nCells) + : m_aCells(nCells) + , eAdjust(SvxAdjust::End) + , nHeight(0) + , nEmptyRows(0) + , eVertOri(text::VertOrientation::TOP) + , bIsEndOfGroup(false) + , bBottomBorder(false) +{ + assert(nCells == m_aCells.size() && + "wrong Cell count in new HTML table row"); +} + +inline void HTMLTableRow::SetHeight( sal_uInt16 nHght ) +{ + if( nHght > nHeight ) + nHeight = nHght; +} + +const HTMLTableCell& HTMLTableRow::GetCell(sal_uInt16 nCell) const +{ + OSL_ENSURE( nCell < m_aCells.size(), + "invalid cell index in HTML table row" ); + return m_aCells.at(nCell); +} + +void HTMLTableRow::Expand( sal_uInt16 nCells, bool bOneCell ) +{ + // This row will be filled with a single cell if bOneCell is set. + // This will only work for rows that don't allow adding cells! + + sal_uInt16 nColSpan = nCells - m_aCells.size(); + for (sal_uInt16 i = m_aCells.size(); i < nCells; ++i) + { + m_aCells.emplace_back(); + if (bOneCell) + m_aCells.back().SetColSpan(nColSpan); + --nColSpan; + } + + OSL_ENSURE(nCells == m_aCells.size(), + "wrong Cell count in expanded HTML table row"); +} + +void HTMLTableRow::Shrink( sal_uInt16 nCells ) +{ + OSL_ENSURE(nCells < m_aCells.size(), "number of cells too large"); + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt16 const nEnd = m_aCells.size(); +#endif + // The colspan of empty cells at the end has to be fixed to the new + // number of cells. + sal_uInt16 i=nCells; + while( i ) + { + HTMLTableCell& rCell = m_aCells[--i]; + if (!rCell.GetContents()) + { +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( rCell.GetColSpan() == nEnd - i, + "invalid col span for empty cell at row end" ); +#endif + rCell.SetColSpan( nCells-i); + } + else + break; + } +#if OSL_DEBUG_LEVEL > 0 + for( i=nCells; i<nEnd; i++ ) + { + HTMLTableCell& rCell = m_aCells[i]; + OSL_ENSURE( rCell.GetRowSpan() == 1, + "RowSpan of to be deleted cell is wrong" ); + OSL_ENSURE( rCell.GetColSpan() == nEnd - i, + "ColSpan of to be deleted cell is wrong" ); + OSL_ENSURE( !rCell.GetContents(), "To be deleted cell has content" ); + } +#endif + + m_aCells.erase(m_aCells.begin() + nCells, m_aCells.end()); +} + +HTMLTableColumn::HTMLTableColumn(): + bIsEndOfGroup(false), + nWidth(0), bRelWidth(false), + eAdjust(SvxAdjust::End), eVertOri(text::VertOrientation::TOP), + bLeftBorder(false) +{ + for(SwFrameFormat* & rp : aFrameFormats) + rp = nullptr; +} + +inline void HTMLTableColumn::SetWidth( sal_uInt16 nWdth, bool bRelWdth ) +{ + if( bRelWidth==bRelWdth ) + { + if( nWdth > nWidth ) + nWidth = nWdth; + } + else + nWidth = nWdth; + bRelWidth = bRelWdth; +} + +inline std::unique_ptr<SwHTMLTableLayoutColumn> HTMLTableColumn::CreateLayoutInfo() +{ + return std::unique_ptr<SwHTMLTableLayoutColumn>(new SwHTMLTableLayoutColumn( nWidth, bRelWidth, bLeftBorder )); +} + +inline sal_uInt16 HTMLTableColumn::GetFrameFormatIdx( bool bBorderLine, + sal_Int16 eVertOrient ) +{ + OSL_ENSURE( text::VertOrientation::TOP != eVertOrient, "Top is not allowed" ); + sal_uInt16 n = bBorderLine ? 3 : 0; + switch( eVertOrient ) + { + case text::VertOrientation::CENTER: n+=1; break; + case text::VertOrientation::BOTTOM: n+=2; break; + default: + ; + } + return n; +} + +inline void HTMLTableColumn::SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine, + sal_Int16 eVertOrient ) +{ + aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)] = pFormat; +} + +inline SwFrameFormat *HTMLTableColumn::GetFrameFormat( bool bBorderLine, + sal_Int16 eVertOrient ) const +{ + return aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)]; +} + +void HTMLTable::InitCtor(const HTMLTableOptions& rOptions) +{ + m_nRows = 0; + m_nCurrentRow = 0; m_nCurrentColumn = 0; + + m_pBoxFormat = nullptr; m_pLineFormat = nullptr; + m_pLineFrameFormatNoHeight = nullptr; + m_xInheritedBackgroundBrush.reset(); + + m_pPrevStartNode = nullptr; + m_pSwTable = nullptr; + + m_bTopBorder = false; m_bRightBorder = false; + m_bTopAllowed = true; m_bRightAllowed = true; + m_bFillerTopBorder = false; m_bFillerBottomBorder = false; + m_bInheritedLeftBorder = false; m_bInheritedRightBorder = false; + m_bBordersSet = false; + m_bForceFrame = false; + m_nHeadlineRepeat = 0; + + m_nLeftMargin = 0; + m_nRightMargin = 0; + + const Color& rBorderColor = rOptions.aBorderColor; + + long nBorderOpt = static_cast<long>(rOptions.nBorder); + long nPWidth = nBorderOpt==USHRT_MAX ? NETSCAPE_DFLT_BORDER + : nBorderOpt; + long nPHeight = nBorderOpt==USHRT_MAX ? 0 : nBorderOpt; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + + // nBorder tells the width of the border as it's used in the width calculation of NetScape + // If pOption->nBorder == USHRT_MAX, there wasn't a BORDER option given + // Nonetheless, a 1 pixel wide border will be used for width calculation + m_nBorder = static_cast<sal_uInt16>(nPWidth); + if( nBorderOpt==USHRT_MAX ) + nPWidth = 0; + + if ( rOptions.nCellSpacing != 0 ) + { + m_aTopBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + } + m_aTopBorderLine.SetWidth( nPHeight ); + m_aTopBorderLine.SetColor( rBorderColor ); + m_aBottomBorderLine = m_aTopBorderLine; + + if( nPWidth == nPHeight ) + { + m_aLeftBorderLine = m_aTopBorderLine; + } + else + { + if ( rOptions.nCellSpacing != 0 ) + { + m_aLeftBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + } + m_aLeftBorderLine.SetWidth( nPWidth ); + m_aLeftBorderLine.SetColor( rBorderColor ); + } + m_aRightBorderLine = m_aLeftBorderLine; + + if( rOptions.nCellSpacing != 0 ) + { + m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + } + else + { + m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + } + m_aBorderLine.SetColor( rBorderColor ); + + if( m_nCellPadding ) + { + if( m_nCellPadding==USHRT_MAX ) + m_nCellPadding = MIN_BORDER_DIST; // default + else + { + m_nCellPadding = SwHTMLParser::ToTwips( m_nCellPadding ); + if( m_nCellPadding<MIN_BORDER_DIST ) + m_nCellPadding = MIN_BORDER_DIST; + } + } + if( m_nCellSpacing ) + { + if( m_nCellSpacing==USHRT_MAX ) + m_nCellSpacing = NETSCAPE_DFLT_CELLSPACING; + m_nCellSpacing = SwHTMLParser::ToTwips( m_nCellSpacing ); + } + + nPWidth = rOptions.nHSpace; + nPHeight = rOptions.nVSpace; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + m_nHSpace = static_cast<sal_uInt16>(nPWidth); + m_nVSpace = static_cast<sal_uInt16>(nPHeight); + + m_bColSpec = false; + + m_xBackgroundBrush.reset(m_pParser->CreateBrushItem( + rOptions.bBGColor ? &(rOptions.aBGColor) : nullptr, + rOptions.aBGImage, OUString(), OUString(), OUString())); + + m_pContext = nullptr; + m_xParentContents.reset(); + + m_aId = rOptions.aId; + m_aClass = rOptions.aClass; + m_aStyle = rOptions.aStyle; + m_aDir = rOptions.aDir; +} + +HTMLTable::HTMLTable(SwHTMLParser* pPars, + bool bParHead, + bool bHasParentSec, bool bHasToFlw, + const HTMLTableOptions& rOptions) : + m_aColumns(rOptions.nCols), + m_nCols(rOptions.nCols), + m_nFilledColumns( 0 ), + m_nCellPadding(rOptions.nCellPadding), + m_nCellSpacing(rOptions.nCellSpacing), + m_nBoxes( 1 ), + m_pCaptionStartNode( nullptr ), + m_bTableAdjustOfTag( rOptions.bTableAdjust ), + m_bIsParentHead( bParHead ), + m_bHasParentSection( bHasParentSec ), + m_bHasToFly( bHasToFlw ), + m_bFixedCols( rOptions.nCols>0 ), + m_bPercentWidth( rOptions.bPercentWidth ), + m_pParser( pPars ), + m_nWidth( rOptions.nWidth ), + m_nHeight( rOptions.nHeight ), + m_eTableAdjust( rOptions.eAdjust ), + m_eVertOrientation( rOptions.eVertOri ), + m_eFrame( rOptions.eFrame ), + m_eRules( rOptions.eRules ), + m_bTopCaption( false ), + m_bFirstCell(true) +{ + InitCtor(rOptions); + m_pParser->RegisterHTMLTable(this); +} + +void SwHTMLParser::DeregisterHTMLTable(HTMLTable* pOld) +{ + if (pOld->m_xBox1) + m_aOrphanedTableBoxes.emplace_back(std::move(pOld->m_xBox1)); + m_aTables.erase(std::remove(m_aTables.begin(), m_aTables.end(), pOld)); +} + +SwDoc* SwHTMLParser::GetDoc() const +{ + return m_xDoc.get(); +} + +bool SwHTMLParser::IsReqIF() const +{ + return m_bReqIF; +} + +HTMLTable::~HTMLTable() +{ + m_pParser->DeregisterHTMLTable(this); + + m_pResizeDrawObjects.reset(); + m_pDrawObjectPercentWidths.reset(); + + m_pContext.reset(); + + // pLayoutInfo has either already been deleted or is now owned by SwTable +} + +const std::shared_ptr<SwHTMLTableLayout>& HTMLTable::CreateLayoutInfo() +{ + sal_uInt16 nW = m_bPercentWidth ? m_nWidth : SwHTMLParser::ToTwips( m_nWidth ); + + sal_uInt16 nBorderWidth = GetBorderWidth( m_aBorderLine, true ); + sal_uInt16 nLeftBorderWidth = + m_aColumns[0].bLeftBorder ? GetBorderWidth(m_aLeftBorderLine, true) : 0; + sal_uInt16 nRightBorderWidth = + m_bRightBorder ? GetBorderWidth( m_aRightBorderLine, true ) : 0; + + m_xLayoutInfo = std::make_shared<SwHTMLTableLayout>( + m_pSwTable, + m_nRows, m_nCols, m_bFixedCols, m_bColSpec, + nW, m_bPercentWidth, m_nBorder, m_nCellPadding, + m_nCellSpacing, m_eTableAdjust, + m_nLeftMargin, m_nRightMargin, + nBorderWidth, nLeftBorderWidth, nRightBorderWidth); + + bool bExportable = true; + sal_uInt16 i; + for( i=0; i<m_nRows; i++ ) + { + HTMLTableRow& rRow = m_aRows[i]; + for( sal_uInt16 j=0; j<m_nCols; j++ ) + { + m_xLayoutInfo->SetCell(rRow.GetCell(j).CreateLayoutInfo(), i, j); + SwHTMLTableLayoutCell* pLayoutCell = m_xLayoutInfo->GetCell(i, j ); + + if( bExportable ) + { + const std::shared_ptr<SwHTMLTableLayoutCnts>& rLayoutCnts = + pLayoutCell->GetContents(); + bExportable = !rLayoutCnts || + (rLayoutCnts->GetStartNode() && !rLayoutCnts->GetNext()); + } + } + } + + m_xLayoutInfo->SetExportable( bExportable ); + + for( i=0; i<m_nCols; i++ ) + m_xLayoutInfo->SetColumn(m_aColumns[i].CreateLayoutInfo(), i); + + return m_xLayoutInfo; +} + +inline void HTMLTable::SetCaption( const SwStartNode *pStNd, bool bTop ) +{ + m_pCaptionStartNode = pStNd; + m_bTopCaption = bTop; +} + +void HTMLTable::FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, + const HTMLTableCnts *pCnts ) +{ + sal_uInt16 nRowSpan=1; + while (true) + { + HTMLTableCell& rCell = GetCell(nRow, nCol); + if (rCell.GetContents().get() != pCnts) + break; + rCell.SetRowSpan(nRowSpan); + if (m_xLayoutInfo) + m_xLayoutInfo->GetCell(nRow,nCol)->SetRowSpan(nRowSpan); + + if( !nRow ) break; + nRowSpan++; nRow--; + } +} + +void HTMLTable::ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan ) +{ + for( sal_uInt16 i=0; i<nRowSpan; i++ ) + { + GetCell(nRow+i,nCol).SetProtected(); + if (m_xLayoutInfo) + m_xLayoutInfo->GetCell(nRow+i,nCol)->SetProtected(); + } +} + +// Search the SwStartNode of the last used predecessor box +const SwStartNode* HTMLTable::GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCol ) const +{ + const HTMLTableCnts *pPrevCnts = nullptr; + + if( 0==nRow ) + { + // always the predecessor cell + if( nCol>0 ) + pPrevCnts = GetCell(0, nCol - 1).GetContents().get(); + else + return m_pPrevStartNode; + } + else if( USHRT_MAX==nRow && USHRT_MAX==nCol ) + // contents of preceding cell + pPrevCnts = GetCell(m_nRows - 1, m_nCols - 1).GetContents().get(); + else + { + sal_uInt16 i; + const HTMLTableRow& rPrevRow = m_aRows[nRow-1]; + + // maybe a cell in the current row + i = nCol; + while( i ) + { + i--; + if( 1 == rPrevRow.GetCell(i).GetRowSpan() ) + { + pPrevCnts = GetCell(nRow, i).GetContents().get(); + break; + } + } + + // otherwise the last filled cell of the row before + if( !pPrevCnts ) + { + i = m_nCols; + while( !pPrevCnts && i ) + { + i--; + pPrevCnts = rPrevRow.GetCell(i).GetContents().get(); + } + } + } + OSL_ENSURE( pPrevCnts, "No previous filled cell found" ); + if( !pPrevCnts ) + { + pPrevCnts = GetCell(0, 0).GetContents().get(); + if( !pPrevCnts ) + return m_pPrevStartNode; + } + + while( pPrevCnts->Next() ) + pPrevCnts = pPrevCnts->Next(); + + const SwStartNode* pRet = pPrevCnts->GetStartNode(); + if (pRet) + return pRet; + HTMLTable* pTable = pPrevCnts->GetTable().get(); + if (!pTable) + return nullptr; + return pTable->GetPrevBoxStartNode(USHRT_MAX, USHRT_MAX); +} + +static bool IsBoxEmpty( const SwTableBox *pBox ) +{ + const SwStartNode *pSttNd = pBox->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; +} + +sal_uInt16 HTMLTable::GetTopCellSpace( sal_uInt16 nRow ) const +{ + sal_uInt16 nSpace = m_nCellPadding; + + if( nRow == 0 ) + { + nSpace += m_nBorder + m_nCellSpacing; + } + + return nSpace; +} + +sal_uInt16 HTMLTable::GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const +{ + sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding; + + if( nRow+nRowSpan == m_nRows ) + { + nSpace = nSpace + m_nBorder; + } + + return nSpace; +} + +void HTMLTable::FixFrameFormat( SwTableBox *pBox, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + bool bFirstPara, bool bLastPara ) const +{ + SwFrameFormat *pFrameFormat = nullptr; // frame::Frame format + sal_Int16 eVOri = text::VertOrientation::NONE; + const SvxBrushItem *pBGBrushItem = nullptr; // background + std::shared_ptr<SvxBoxItem> pBoxItem; + bool bTopLine = false, bBottomLine = false, bLastBottomLine = false; + bool bReUsable = false; // Format reusable? + sal_uInt16 nEmptyRows = 0; + bool bHasNumFormat = false; + bool bHasValue = false; + sal_uInt32 nNumFormat = 0; + double nValue = 0.0; + + const HTMLTableColumn& rColumn = m_aColumns[nCol]; + + if( pBox->GetSttNd() ) + { + // Determine background color/graphic + const HTMLTableCell& rCell = GetCell(nRow, nCol); + pBoxItem = rCell.GetBoxItem(); + pBGBrushItem = rCell.GetBGBrush().get(); + if( !pBGBrushItem ) + { + // If a cell spans multiple rows, a background to that row should be copied to the cell. + if (nRowSpan > 1) + { + pBGBrushItem = m_aRows[nRow].GetBGBrush().get(); + } + } + + bTopLine = 0==nRow && m_bTopBorder && bFirstPara; + if (m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara) + { + nEmptyRows = m_aRows[nRow+nRowSpan-1].GetEmptyRows(); + if( nRow+nRowSpan == m_nRows ) + bLastBottomLine = true; + else + bBottomLine = true; + } + + eVOri = rCell.GetVertOri(); + bHasNumFormat = rCell.GetNumFormat( nNumFormat ); + if( bHasNumFormat ) + bHasValue = rCell.GetValue( nValue ); + + if( nColSpan==1 && !bTopLine && !bLastBottomLine && !nEmptyRows && + !pBGBrushItem && !bHasNumFormat && !pBoxItem) + { + pFrameFormat = rColumn.GetFrameFormat( bBottomLine, eVOri ); + bReUsable = !pFrameFormat; + } + } + + if( !pFrameFormat ) + { + pFrameFormat = pBox->ClaimFrameFormat(); + + // calculate width of the box + SwTwips nFrameWidth = static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol) + ->GetRelColWidth()); + for( sal_uInt16 i=1; i<nColSpan; i++ ) + nFrameWidth += static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol+i) + ->GetRelColWidth()); + + // Only set the border on edit boxes. + // On setting the upper and lower border, keep in mind if + // it's the first or the last paragraph of the cell + if( pBox->GetSttNd() ) + { + bool bSet = (m_nCellPadding > 0); + + SvxBoxItem aBoxItem( RES_BOX ); + long nInnerFrameWidth = nFrameWidth; + + if( bTopLine ) + { + aBoxItem.SetLine( &m_aTopBorderLine, SvxBoxItemLine::TOP ); + bSet = true; + } + if( bLastBottomLine ) + { + aBoxItem.SetLine( &m_aBottomBorderLine, SvxBoxItemLine::BOTTOM ); + bSet = true; + } + else if( bBottomLine ) + { + if( nEmptyRows && !m_aBorderLine.GetInWidth() ) + { + // For now, empty rows can only be emulated by thick lines, if it's a single line + SvxBorderLine aThickBorderLine( m_aBorderLine ); + + sal_uInt16 nBorderWidth = m_aBorderLine.GetOutWidth(); + nBorderWidth *= (nEmptyRows + 1); + aThickBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); + aThickBorderLine.SetWidth( nBorderWidth ); + aBoxItem.SetLine( &aThickBorderLine, SvxBoxItemLine::BOTTOM ); + } + else + { + aBoxItem.SetLine( &m_aBorderLine, SvxBoxItemLine::BOTTOM ); + } + bSet = true; + } + if (m_aColumns[nCol].bLeftBorder) + { + const SvxBorderLine& rBorderLine = + 0==nCol ? m_aLeftBorderLine : m_aBorderLine; + aBoxItem.SetLine( &rBorderLine, SvxBoxItemLine::LEFT ); + nInnerFrameWidth -= GetBorderWidth( rBorderLine ); + bSet = true; + } + if( m_bRightBorder ) + { + aBoxItem.SetLine( &m_aRightBorderLine, SvxBoxItemLine::RIGHT ); + nInnerFrameWidth -= GetBorderWidth( m_aRightBorderLine ); + bSet = true; + } + + if (pBoxItem) + { + pFrameFormat->SetFormatAttr( *pBoxItem ); + } + else if (bSet) + { + // BorderDist is not part of a cell with fixed width + sal_uInt16 nBDist = static_cast< sal_uInt16 >( + (2*m_nCellPadding <= nInnerFrameWidth) ? m_nCellPadding + : (nInnerFrameWidth / 2) ); + // We only set the item if there's a border or a border distance + // If the latter is missing, there's gonna be a border and we'll have to set the distance + aBoxItem.SetAllDistances(nBDist ? nBDist : MIN_BORDER_DIST); + pFrameFormat->SetFormatAttr( aBoxItem ); + } + else + pFrameFormat->ResetFormatAttr( RES_BOX ); + + if( pBGBrushItem ) + { + pFrameFormat->SetFormatAttr( *pBGBrushItem ); + } + else + pFrameFormat->ResetFormatAttr( RES_BACKGROUND ); + + // Only set format if there's a value or the box is empty + if( bHasNumFormat && (bHasValue || IsBoxEmpty(pBox)) ) + { + bool bLock = pFrameFormat->GetDoc()->GetNumberFormatter() + ->IsTextFormat( nNumFormat ); + SfxItemSet aItemSet( *pFrameFormat->GetAttrSet().GetPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} ); + SvxAdjust eAdjust = SvxAdjust::End; + SwContentNode *pCNd = nullptr; + if( !bLock ) + { + const SwStartNode *pSttNd = pBox->GetSttNd(); + pCNd = pSttNd->GetNodes()[pSttNd->GetIndex()+1] + ->GetContentNode(); + const SfxPoolItem *pItem; + if( pCNd && pCNd->HasSwAttrSet() && + SfxItemState::SET==pCNd->GetpSwAttrSet()->GetItemState( + RES_PARATR_ADJUST, false, &pItem ) ) + { + eAdjust = static_cast<const SvxAdjustItem *>(pItem) + ->GetAdjust(); + } + } + aItemSet.Put( SwTableBoxNumFormat(nNumFormat) ); + if( bHasValue ) + aItemSet.Put( SwTableBoxValue(nValue) ); + + if( bLock ) + pFrameFormat->LockModify(); + pFrameFormat->SetFormatAttr( aItemSet ); + if( bLock ) + pFrameFormat->UnlockModify(); + else if( pCNd && SvxAdjust::End != eAdjust ) + { + SvxAdjustItem aAdjItem( eAdjust, RES_PARATR_ADJUST ); + pCNd->SetAttr( aAdjItem ); + } + } + else + pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT ); + + OSL_ENSURE( eVOri != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" ); + if( text::VertOrientation::NONE != eVOri ) + { + pFrameFormat->SetFormatAttr( SwFormatVertOrient( 0, eVOri ) ); + } + else + pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT ); + + if( bReUsable ) + const_cast<HTMLTableColumn&>(rColumn).SetFrameFormat(pFrameFormat, bBottomLine, eVOri); + } + else + { + pFrameFormat->ResetFormatAttr( RES_BOX ); + pFrameFormat->ResetFormatAttr( RES_BACKGROUND ); + pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT ); + pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT ); + } + + if (m_pParser->IsReqIF()) + { + // ReqIF case, cells would have no formatting. Apply the default + // table autoformat on them, so imported and UI-created tables look + // the same. + SwTableAutoFormatTable& rTable = m_pParser->GetDoc()->GetTableStyles(); + SwTableAutoFormat* pTableFormat = rTable.FindAutoFormat( + SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_DEFAULT, OUString())); + if (pTableFormat) + { + sal_uInt8 nPos = SwTableAutoFormat::CountPos(nCol, m_nCols, nRow, m_nRows); + pTableFormat->UpdateToSet(nPos, m_nRows==1, m_nCols==1, + const_cast<SfxItemSet&>(static_cast<SfxItemSet const&>( + pFrameFormat->GetAttrSet())), + SwTableAutoFormatUpdateFlags::Box, + pFrameFormat->GetDoc()->GetNumberFormatter()); + } + } + } + else + { + OSL_ENSURE( pBox->GetSttNd() || + SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState( + RES_VERT_ORIENT, false ), + "Box without content has vertical orientation" ); + pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFrameFormat) ); + } + +} + +SwTableBox *HTMLTable::NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ) const +{ + SwTableBox *pBox; + + if (m_xBox1 && m_xBox1->GetSttNd() == pStNd) + { + // If the StartNode is the StartNode of the initially created box, we take that box + pBox = const_cast<HTMLTable*>(this)->m_xBox1.release(); + pBox->SetUpper(pUpper); + } + else + pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper ); + + return pBox; +} + +static void ResetLineFrameFormatAttrs( SwFrameFormat *pFrameFormat ) +{ + pFrameFormat->ResetFormatAttr( RES_FRM_SIZE ); + pFrameFormat->ResetFormatAttr( RES_BACKGROUND ); + OSL_ENSURE( SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState( + RES_VERT_ORIENT, false ), + "Cell has vertical orientation" ); +} + +// !!! could be simplified +SwTableLine *HTMLTable::MakeTableLine( SwTableBox *pUpper, + sal_uInt16 nTopRow, sal_uInt16 nLeftCol, + sal_uInt16 nBottomRow, sal_uInt16 nRightCol ) +{ + SwTableLine *pLine; + if (!pUpper && 0 == nTopRow) + pLine = (m_pSwTable->GetTabLines())[0]; + else + pLine = new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight + : m_pLineFormat, + 0, pUpper ); + + const HTMLTableRow& rTopRow = m_aRows[nTopRow]; + sal_uInt16 nRowHeight = rTopRow.GetHeight(); + const SvxBrushItem *pBGBrushItem = nullptr; + if (nTopRow > 0 || nBottomRow < m_nRows) + { + // It doesn't make sense to set a color on a line, + // if it's the outermost and simultaneously sole line of a table in a table + pBGBrushItem = rTopRow.GetBGBrush().get(); + } + if( nTopRow==nBottomRow-1 && (nRowHeight || pBGBrushItem) ) + { + SwTableLineFormat *pFrameFormat = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat()); + ResetLineFrameFormatAttrs( pFrameFormat ); + + if( nRowHeight ) + { + // set table height. Since it's a minimum height it can be calculated like in Netscape, + // so without considering the actual border width + nRowHeight += GetTopCellSpace( nTopRow ) + + GetBottomCellSpace( nTopRow, 1 ); + + pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, nRowHeight ) ); + } + + if( pBGBrushItem ) + { + pFrameFormat->SetFormatAttr( *pBGBrushItem ); + } + + } + else if( !m_pLineFrameFormatNoHeight ) + { + // else, we'll have to remove the height from the attribute and remember the format + m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat()); + + ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight ); + } + + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + + sal_uInt16 nStartCol = nLeftCol; + while( nStartCol<nRightCol ) + { + sal_uInt16 nCol = nStartCol; + sal_uInt16 nSplitCol = nRightCol; + bool bSplitted = false; + while( !bSplitted ) + { + OSL_ENSURE( nCol < nRightCol, "Gone too far" ); + + HTMLTableCell& rCell = GetCell(nTopRow,nCol); + const bool bSplit = 1 == rCell.GetColSpan(); + + OSL_ENSURE((nCol != nRightCol-1) || bSplit, "Split-Flag wrong"); + if( bSplit ) + { + SwTableBox* pBox = nullptr; + HTMLTableCell& rCell2 = GetCell(nTopRow, nStartCol); + if (rCell2.GetColSpan() == (nCol+1-nStartCol)) + { + // The HTML tables represent a box. So we need to split behind that box + nSplitCol = nCol + 1; + + long nBoxRowSpan = rCell2.GetRowSpan(); + if (!rCell2.GetContents() || rCell2.IsCovered()) + { + if (rCell2.IsCovered()) + nBoxRowSpan = -1 * nBoxRowSpan; + + const SwStartNode* pPrevStartNd = + GetPrevBoxStartNode( nTopRow, nStartCol ); + auto xCnts = std::make_shared<HTMLTableCnts>( + m_pParser->InsertTableSection(pPrevStartNd)); + const std::shared_ptr<SwHTMLTableLayoutCnts> xCntsLayoutInfo = + xCnts->CreateLayoutInfo(); + + rCell2.SetContents(xCnts); + SwHTMLTableLayoutCell *pCurrCell = m_xLayoutInfo->GetCell(nTopRow, nStartCol); + pCurrCell->SetContents(xCntsLayoutInfo); + if( nBoxRowSpan < 0 ) + pCurrCell->SetRowSpan( 0 ); + + // check COLSPAN if needed + for( sal_uInt16 j=nStartCol+1; j<nSplitCol; j++ ) + { + GetCell(nTopRow, j).SetContents(xCnts); + m_xLayoutInfo->GetCell(nTopRow, j) + ->SetContents(xCntsLayoutInfo); + } + } + + pBox = MakeTableBox(pLine, rCell2.GetContents().get(), + nTopRow, nStartCol, + nBottomRow, nSplitCol); + + if (1 != nBoxRowSpan && pBox) + pBox->setRowSpan( nBoxRowSpan ); + + bSplitted = true; + } + + OSL_ENSURE( pBox, "Colspan trouble" ); + + if( pBox ) + rBoxes.push_back( pBox ); + } + nCol++; + } + nStartCol = nSplitCol; + } + + return pLine; +} + +SwTableBox *HTMLTable::MakeTableBox( SwTableLine *pUpper, + HTMLTableCnts *pCnts, + sal_uInt16 nTopRow, sal_uInt16 nLeftCol, + sal_uInt16 nBottomRow, sal_uInt16 nRightCol ) +{ + SwTableBox *pBox; + sal_uInt16 nColSpan = nRightCol - nLeftCol; + sal_uInt16 nRowSpan = nBottomRow - nTopRow; + + if( !pCnts->Next() ) + { + // just one content section + if( pCnts->GetStartNode() ) + { + // ... that's not a table + pBox = NewTableBox( pCnts->GetStartNode(), pUpper ); + pCnts->SetTableBox( pBox ); + } + else if (HTMLTable* pTable = pCnts->GetTable().get()) + { + pTable->InheritVertBorders( this, nLeftCol, + nRightCol-nLeftCol ); + // ... that's a table. We'll build a new box and put the rows of the table + // in the rows of the box + pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + sal_uInt16 nAbs, nRel; + m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel ); + sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol, nColSpan ); + sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol, nColSpan ); + sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan ); + pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace, nRSpace, + nInhSpace ); + } + else + { + return nullptr; + } + } + else + { + // multiple content sections: we'll build a box with rows + pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + SwTableLines& rLines = pBox->GetTabLines(); + bool bFirstPara = true; + + while( pCnts ) + { + if( pCnts->GetStartNode() ) + { + // normal paragraphs are gonna be boxes in a row + SwTableLine *pLine = + new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight + : m_pLineFormat, 0, pBox ); + if( !m_pLineFrameFormatNoHeight ) + { + // If there's no line format without height yet, we can use that one + m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat()); + + ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight ); + } + + SwTableBox* pCntBox = NewTableBox( pCnts->GetStartNode(), + pLine ); + pCnts->SetTableBox( pCntBox ); + FixFrameFormat( pCntBox, nTopRow, nLeftCol, nRowSpan, nColSpan, + bFirstPara, nullptr==pCnts->Next() ); + pLine->GetTabBoxes().push_back( pCntBox ); + + rLines.push_back( pLine ); + } + else + { + pCnts->GetTable()->InheritVertBorders( this, nLeftCol, + nRightCol-nLeftCol ); + // Tables are entered directly + sal_uInt16 nAbs, nRel; + m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel ); + sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol, + nColSpan ); + sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol, + nColSpan ); + sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan ); + pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace, + nRSpace, nInhSpace ); + } + + pCnts = pCnts->Next(); + bFirstPara = false; + } + } + + FixFrameFormat( pBox, nTopRow, nLeftCol, nRowSpan, nColSpan ); + + return pBox; +} + +void HTMLTable::InheritBorders( const HTMLTable *pParent, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, + bool bFirstPara, bool bLastPara ) +{ + OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows, + "Was CloseTable not called?" ); + + // The child table needs a border, if the surrounding cell has a margin on that side. + // The upper/lower border is only set if the table is the first/last paragraph in that cell + // It can't be determined if a border for that table is needed or possible for the left or right side, + // since that's depending on if filler cells are gonna be added. We'll only collect info for now + + if( 0==nRow && pParent->m_bTopBorder && bFirstPara ) + { + m_bTopBorder = true; + m_bFillerTopBorder = true; // fillers get a border too + m_aTopBorderLine = pParent->m_aTopBorderLine; + } + if (pParent->m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara) + { + m_aRows[m_nRows-1].SetBottomBorder(true); + m_bFillerBottomBorder = true; // fillers get a border too + m_aBottomBorderLine = + nRow+nRowSpan==pParent->m_nRows ? pParent->m_aBottomBorderLine + : pParent->m_aBorderLine; + } + + // The child table mustn't get an upper or lower border, if that's already done by the surrounding table + // It can get an upper border if the table is not the first paragraph in that cell + m_bTopAllowed = ( !bFirstPara || (pParent->m_bTopAllowed && + (0==nRow || !pParent->m_aRows[nRow-1].GetBottomBorder())) ); + + // The child table has to inherit the color of the cell it's contained in, if it doesn't have one + const SvxBrushItem *pInhBG = pParent->GetCell(nRow, nCol).GetBGBrush().get(); + if( !pInhBG && pParent != this && + pParent->GetCell(nRow,nCol).GetRowSpan() == pParent->m_nRows ) + { + // the whole surrounding table is a table in a table and consists only of a single line + // that's gonna be GC-ed (correctly). That's why the background of that line is copied. + pInhBG = pParent->m_aRows[nRow].GetBGBrush().get(); + if( !pInhBG ) + pInhBG = pParent->GetBGBrush().get(); + if( !pInhBG ) + pInhBG = pParent->GetInhBGBrush().get(); + } + if( pInhBG ) + m_xInheritedBackgroundBrush.reset(new SvxBrushItem(*pInhBG)); +} + +void HTMLTable::InheritVertBorders( const HTMLTable *pParent, + sal_uInt16 nCol, sal_uInt16 nColSpan ) +{ + sal_uInt16 nInhLeftBorderWidth = 0; + sal_uInt16 nInhRightBorderWidth = 0; + + if( nCol+nColSpan==pParent->m_nCols && pParent->m_bRightBorder ) + { + m_bInheritedRightBorder = true; // just remember for now + m_aInheritedRightBorderLine = pParent->m_aRightBorderLine; + nInhRightBorderWidth = + GetBorderWidth( m_aInheritedRightBorderLine, true ) + MIN_BORDER_DIST; + } + + if (pParent->m_aColumns[nCol].bLeftBorder) + { + m_bInheritedLeftBorder = true; // just remember for now + m_aInheritedLeftBorderLine = 0==nCol ? pParent->m_aLeftBorderLine + : pParent->m_aBorderLine; + nInhLeftBorderWidth = + GetBorderWidth( m_aInheritedLeftBorderLine, true ) + MIN_BORDER_DIST; + } + + if( !m_bInheritedLeftBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) ) + nInhLeftBorderWidth = 2 * MIN_BORDER_DIST; + if( !m_bInheritedRightBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) ) + nInhRightBorderWidth = 2 * MIN_BORDER_DIST; + m_xLayoutInfo->SetInhBorderWidths( nInhLeftBorderWidth, + nInhRightBorderWidth ); + + m_bRightAllowed = ( pParent->m_bRightAllowed && + (nCol+nColSpan==pParent->m_nCols || + !pParent->m_aColumns[nCol+nColSpan].bLeftBorder) ); +} + +void HTMLTable::SetBorders() +{ + sal_uInt16 i; + for( i=1; i<m_nCols; i++ ) + if( HTMLTableRules::All==m_eRules || HTMLTableRules::Cols==m_eRules || + ((HTMLTableRules::Rows==m_eRules || HTMLTableRules::Groups==m_eRules) && + m_aColumns[i-1].IsEndOfGroup())) + { + m_aColumns[i].bLeftBorder = true; + } + + for( i=0; i<m_nRows-1; i++ ) + if( HTMLTableRules::All==m_eRules || HTMLTableRules::Rows==m_eRules || + ((HTMLTableRules::Cols==m_eRules || HTMLTableRules::Groups==m_eRules) && + m_aRows[i].IsEndOfGroup())) + { + m_aRows[i].SetBottomBorder(true); + } + + if( m_bTopAllowed && (HTMLTableFrame::Above==m_eFrame || HTMLTableFrame::HSides==m_eFrame || + HTMLTableFrame::Box==m_eFrame) ) + m_bTopBorder = true; + if( HTMLTableFrame::Below==m_eFrame || HTMLTableFrame::HSides==m_eFrame || + HTMLTableFrame::Box==m_eFrame ) + { + m_aRows[m_nRows-1].SetBottomBorder(true); + } + if( HTMLTableFrame::RHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame || + HTMLTableFrame::Box==m_eFrame ) + m_bRightBorder = true; + if( HTMLTableFrame::LHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame || HTMLTableFrame::Box==m_eFrame ) + { + m_aColumns[0].bLeftBorder = true; + } + + for( i=0; i<m_nRows; i++ ) + { + HTMLTableRow& rRow = m_aRows[i]; + for (sal_uInt16 j=0; j<m_nCols; ++j) + { + HTMLTableCell& rCell = rRow.GetCell(j); + if (rCell.GetContents()) + { + HTMLTableCnts *pCnts = rCell.GetContents().get(); + bool bFirstPara = true; + while( pCnts ) + { + HTMLTable *pTable = pCnts->GetTable().get(); + if( pTable && !pTable->BordersSet() ) + { + pTable->InheritBorders(this, i, j, + rCell.GetRowSpan(), + bFirstPara, + nullptr==pCnts->Next()); + pTable->SetBorders(); + } + bFirstPara = false; + pCnts = pCnts->Next(); + } + } + } + } + + m_bBordersSet = true; +} + +sal_uInt16 HTMLTable::GetBorderWidth( const SvxBorderLine& rBLine, + bool bWithDistance ) const +{ + sal_uInt16 nBorderWidth = rBLine.GetWidth(); + if( bWithDistance ) + { + if( m_nCellPadding ) + nBorderWidth = nBorderWidth + m_nCellPadding; + else if( nBorderWidth ) + nBorderWidth = nBorderWidth + MIN_BORDER_DIST; + } + + return nBorderWidth; +} + +const HTMLTableCell& HTMLTable::GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const +{ + OSL_ENSURE(nRow < m_aRows.size(), "invalid row index in HTML table"); + return m_aRows[nRow].GetCell(nCell); +} + +SvxAdjust HTMLTable::GetInheritedAdjust() const +{ + SvxAdjust eAdjust = (m_nCurrentColumn<m_nCols ? m_aColumns[m_nCurrentColumn].GetAdjust() + : SvxAdjust::End ); + if( SvxAdjust::End==eAdjust ) + eAdjust = m_aRows[m_nCurrentRow].GetAdjust(); + + return eAdjust; +} + +sal_Int16 HTMLTable::GetInheritedVertOri() const +{ + // text::VertOrientation::TOP is default! + sal_Int16 eVOri = m_aRows[m_nCurrentRow].GetVertOri(); + if( text::VertOrientation::TOP==eVOri && m_nCurrentColumn<m_nCols ) + eVOri = m_aColumns[m_nCurrentColumn].GetVertOri(); + if( text::VertOrientation::TOP==eVOri ) + eVOri = m_eVertOrientation; + + OSL_ENSURE( m_eVertOrientation != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" ); + return eVOri; +} + +void HTMLTable::InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + sal_uInt16 nCellWidth, bool bRelWidth, sal_uInt16 nCellHeight, + sal_Int16 eVertOrient, std::shared_ptr<SvxBrushItem> const& rBGBrushItem, + std::shared_ptr<SvxBoxItem> const& rBoxItem, + bool bHasNumFormat, sal_uInt32 nNumFormat, + bool bHasValue, double nValue, bool bNoWrap ) +{ + if( !nRowSpan || static_cast<sal_uInt32>(m_nCurrentRow) + nRowSpan > USHRT_MAX ) + nRowSpan = 1; + + if( !nColSpan || static_cast<sal_uInt32>(m_nCurrentColumn) + nColSpan > USHRT_MAX ) + nColSpan = 1; + + sal_uInt16 nColsReq = m_nCurrentColumn + nColSpan; + sal_uInt16 nRowsReq = m_nCurrentRow + nRowSpan; + sal_uInt16 i, j; + + // if we need more columns than we currently have, we need to add cells for all rows + if( m_nCols < nColsReq ) + { + m_aColumns.resize(nColsReq); + for( i=0; i<m_nRows; i++ ) + m_aRows[i].Expand( nColsReq, i<m_nCurrentRow ); + m_nCols = nColsReq; + OSL_ENSURE(m_aColumns.size() == m_nCols, + "wrong number of columns after expanding"); + } + if( nColsReq > m_nFilledColumns ) + m_nFilledColumns = nColsReq; + + // if we need more rows than we currently have, we need to add cells + if( m_nRows < nRowsReq ) + { + for( i=m_nRows; i<nRowsReq; i++ ) + m_aRows.emplace_back(m_nCols); + m_nRows = nRowsReq; + OSL_ENSURE(m_nRows == m_aRows.size(), "wrong number of rows in Insert"); + } + + // Check if we have an overlap and could remove that + sal_uInt16 nSpanedCols = 0; + if( m_nCurrentRow>0 ) + { + HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow]; + for( i=m_nCurrentColumn; i<nColsReq; i++ ) + { + HTMLTableCell& rCell = rCurRow.GetCell(i); + if (rCell.GetContents()) + { + // A cell from a row further above overlaps this one. + // Content and colors are coming from that cell and can be overwritten + // or deleted (content) or copied (color) by ProtectRowSpan + nSpanedCols = i + rCell.GetColSpan(); + FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() ); + if (rCell.GetRowSpan() > nRowSpan) + ProtectRowSpan( nRowsReq, i, + rCell.GetRowSpan()-nRowSpan ); + } + } + for( i=nColsReq; i<nSpanedCols; i++ ) + { + // These contents are anchored in the row above in any case + HTMLTableCell& rCell = rCurRow.GetCell(i); + FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() ); + ProtectRowSpan( m_nCurrentRow, i, rCell.GetRowSpan() ); + } + } + + // Fill the cells + for( i=nColSpan; i>0; i-- ) + { + for( j=nRowSpan; j>0; j-- ) + { + const bool bCovered = i != nColSpan || j != nRowSpan; + GetCell( nRowsReq-j, nColsReq-i ) + .Set( rCnts, j, i, eVertOrient, rBGBrushItem, rBoxItem, + bHasNumFormat, nNumFormat, bHasValue, nValue, bNoWrap, bCovered ); + } + } + + Size aTwipSz( bRelWidth ? 0 : nCellWidth, nCellHeight ); + if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() ) + { + aTwipSz = Application::GetDefaultDevice() + ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) ); + } + + // Only set width on the first cell! + if( nCellWidth ) + { + sal_uInt16 nTmp = bRelWidth ? nCellWidth : static_cast<sal_uInt16>(aTwipSz.Width()); + GetCell( m_nCurrentRow, m_nCurrentColumn ).SetWidth( nTmp, bRelWidth ); + } + + // Remember height + if( nCellHeight && 1==nRowSpan ) + { + m_aRows[m_nCurrentRow].SetHeight(static_cast<sal_uInt16>(aTwipSz.Height())); + } + + // Set the column counter behind the new cells + m_nCurrentColumn = nColsReq; + if( nSpanedCols > m_nCurrentColumn ) + m_nCurrentColumn = nSpanedCols; + + // and search for the next free cell + while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() ) + m_nCurrentColumn++; +} + +inline void HTMLTable::CloseSection( bool bHead ) +{ + // Close the preceding sections if there's already a row + OSL_ENSURE( m_nCurrentRow<=m_nRows, "invalid current row" ); + if( m_nCurrentRow>0 && m_nCurrentRow<=m_nRows ) + m_aRows[m_nCurrentRow-1].SetEndOfGroup(); + if( bHead ) + m_nHeadlineRepeat = m_nCurrentRow; +} + +void HTMLTable::OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOrient, + std::unique_ptr<SvxBrushItem>& rBGBrushItem) +{ + sal_uInt16 nRowsReq = m_nCurrentRow+1; + + // create the next row if it's not there already + if( m_nRows<nRowsReq ) + { + for( sal_uInt16 i=m_nRows; i<nRowsReq; i++ ) + m_aRows.emplace_back(m_nCols); + m_nRows = nRowsReq; + OSL_ENSURE( m_nRows == m_aRows.size(), + "Row number in OpenRow is wrong" ); + } + + HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow]; + rCurRow.SetAdjust(eAdjust); + rCurRow.SetVertOri(eVertOrient); + if (rBGBrushItem) + m_aRows[m_nCurrentRow].SetBGBrush(rBGBrushItem); + + // reset the column counter + m_nCurrentColumn=0; + + // and search for the next free cell + while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() ) + m_nCurrentColumn++; +} + +void HTMLTable::CloseRow( bool bEmpty ) +{ + OSL_ENSURE( m_nCurrentRow<m_nRows, "current row after table end" ); + + // empty cells just get a slightly thicker lower border! + if( bEmpty ) + { + if( m_nCurrentRow > 0 ) + m_aRows[m_nCurrentRow-1].IncEmptyRows(); + return; + } + + HTMLTableRow& rRow = m_aRows[m_nCurrentRow]; + + // modify the COLSPAN of all empty cells at the row end in a way, that they're forming a single cell + // that can be done here (and not earlier) since there's no more cells in that row + sal_uInt16 i=m_nCols; + while( i ) + { + HTMLTableCell& rCell = rRow.GetCell(--i); + if (!rCell.GetContents()) + { + sal_uInt16 nColSpan = m_nCols-i; + if( nColSpan > 1 ) + rCell.SetColSpan(nColSpan); + } + else + break; + } + + m_nCurrentRow++; +} + +inline void HTMLTable::CloseColGroup( sal_uInt16 nSpan, sal_uInt16 _nWidth, + bool bRelWidth, SvxAdjust eAdjust, + sal_Int16 eVertOrient ) +{ + if( nSpan ) + InsertCol( nSpan, _nWidth, bRelWidth, eAdjust, eVertOrient ); + + OSL_ENSURE( m_nCurrentColumn<=m_nCols, "invalid column" ); + if( m_nCurrentColumn>0 && m_nCurrentColumn<=m_nCols ) + m_aColumns[m_nCurrentColumn-1].SetEndOfGroup(); +} + +void HTMLTable::InsertCol( sal_uInt16 nSpan, sal_uInt16 nColWidth, bool bRelWidth, + SvxAdjust eAdjust, sal_Int16 eVertOrient ) +{ + // #i35143# - no columns, if rows already exist. + if ( m_nRows > 0 ) + return; + + sal_uInt16 i; + + if( !nSpan ) + nSpan = 1; + + sal_uInt16 nColsReq = m_nCurrentColumn + nSpan; + + if( m_nCols < nColsReq ) + { + m_aColumns.resize(nColsReq); + m_nCols = nColsReq; + } + + Size aTwipSz( bRelWidth ? 0 : nColWidth, 0 ); + if( aTwipSz.Width() && Application::GetDefaultDevice() ) + { + aTwipSz = Application::GetDefaultDevice() + ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) ); + } + + for( i=m_nCurrentColumn; i<nColsReq; i++ ) + { + HTMLTableColumn& rCol = m_aColumns[i]; + sal_uInt16 nTmp = bRelWidth ? nColWidth : static_cast<sal_uInt16>(aTwipSz.Width()); + rCol.SetWidth( nTmp, bRelWidth ); + rCol.SetAdjust( eAdjust ); + rCol.SetVertOri( eVertOrient ); + } + + m_bColSpec = true; + + m_nCurrentColumn = nColsReq; +} + +void HTMLTable::CloseTable() +{ + sal_uInt16 i; + + // The number of table rows is only dependent on the <TR> elements (i.e. nCurRow). + // Rows that are spanned via ROWSPAN behind nCurRow need to be deleted + // and we need to adjust the ROWSPAN in the rows above + if( m_nRows>m_nCurrentRow ) + { + HTMLTableRow& rPrevRow = m_aRows[m_nCurrentRow-1]; + for( i=0; i<m_nCols; i++ ) + { + HTMLTableCell& rCell = rPrevRow.GetCell(i); + if (rCell.GetRowSpan() > 1) + { + FixRowSpan(m_nCurrentRow-1, i, rCell.GetContents().get()); + ProtectRowSpan(m_nCurrentRow, i, m_aRows[m_nCurrentRow].GetCell(i).GetRowSpan()); + } + } + for( i=m_nRows-1; i>=m_nCurrentRow; i-- ) + m_aRows.erase(m_aRows.begin() + i); + m_nRows = m_nCurrentRow; + } + + // if the table has no column, we need to add one + if( 0==m_nCols ) + { + m_aColumns.resize(1); + for( i=0; i<m_nRows; i++ ) + m_aRows[i].Expand(1); + m_nCols = 1; + m_nFilledColumns = 1; + } + + // if the table has no row, we need to add one + if( 0==m_nRows ) + { + m_aRows.emplace_back(m_nCols); + m_nRows = 1; + m_nCurrentRow = 1; + } + + if( m_nFilledColumns < m_nCols ) + { + m_aColumns.erase(m_aColumns.begin() + m_nFilledColumns, m_aColumns.begin() + m_nCols); + for( i=0; i<m_nRows; i++ ) + m_aRows[i].Shrink( m_nFilledColumns ); + m_nCols = m_nFilledColumns; + } +} + +void HTMLTable::MakeTable_( SwTableBox *pBox ) +{ + SwTableLines& rLines = (pBox ? pBox->GetTabLines() + : const_cast<SwTable *>(m_pSwTable)->GetTabLines() ); + + for( sal_uInt16 i=0; i<m_nRows; i++ ) + { + SwTableLine *pLine = MakeTableLine( pBox, i, 0, i+1, m_nCols ); + if( pBox || i > 0 ) + rLines.push_back( pLine ); + } +} + +/* How are tables aligned? + +first row: without paragraph indents +second row: with paragraph indents + +ALIGN= LEFT RIGHT CENTER - +------------------------------------------------------------------------- +xxx for tables with WIDTH=nn% the percentage value is important: +xxx nn = 100 text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL % +xxx text::HoriOrientation::NONE text::HoriOrientation::NONE text::HoriOrientation::NONE % text::HoriOrientation::NONE % +xxx nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT % +xxx frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE % + +for tables with WIDTH=nn% the percentage value is important: +nn = 100 text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT % + text::HoriOrientation::LEFT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT_AND % +nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT % + frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE % + +otherwise the calculated width w +w = avail* text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT + HORI_LEDT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT_AND +w < avail frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::LEFT + frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::NONE + +xxx *) if for the table no size was specified, always +xxx text::HoriOrientation::FULL is taken + +*/ + +void HTMLTable::MakeTable( SwTableBox *pBox, sal_uInt16 nAbsAvail, + sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace, + sal_uInt16 nAbsRightSpace, sal_uInt16 nInhAbsSpace ) +{ + OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows, + "Was CloseTable not called?" ); + + OSL_ENSURE(m_xLayoutInfo == nullptr, "Table already has layout info"); + + // Calculate borders of the table and all contained tables + SetBorders(); + + // Step 1: needed layout structures are created (including tables in tables) + CreateLayoutInfo(); + + // Step 2: the minimal and maximal column width is calculated + // (including tables in tables). Since we don't have boxes yet, + // we'll work on the start nodes + m_xLayoutInfo->AutoLayoutPass1(); + + // Step 3: the actual column widths of this table are calculated (not tables in tables) + // We need this now to decide if we need filler cells + // (Pass1 was needed because of this as well) + m_xLayoutInfo->AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, + nAbsRightSpace, nInhAbsSpace ); + + // Set adjustment for the top table + sal_Int16 eHoriOri; + if (m_bForceFrame) + { + // The table should go in a text frame and it's narrower than the + // available space and not 100% wide. So it gets a border + eHoriOri = m_bPercentWidth ? text::HoriOrientation::FULL : text::HoriOrientation::LEFT; + } + else switch (m_eTableAdjust) + { + // The table either fits the page but shouldn't get a text frame, + // or it's wider than the page so it doesn't need a text frame + + case SvxAdjust::Right: + // Don't be considerate of the right margin in right-adjusted tables + eHoriOri = text::HoriOrientation::RIGHT; + break; + case SvxAdjust::Center: + // Centred tables are not considerate of margins + eHoriOri = text::HoriOrientation::CENTER; + break; + case SvxAdjust::Left: + default: + // left-adjusted tables are only considerate of the left margin + eHoriOri = m_nLeftMargin ? text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::LEFT; + break; + } + + if (!m_pSwTable) + { + SAL_WARN("sw.html", "no table"); + return; + } + + // get the table format and adapt it + SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat(); + pFrameFormat->SetFormatAttr( SwFormatHoriOrient(0, eHoriOri) ); + if (text::HoriOrientation::LEFT_AND_WIDTH == eHoriOri) + { + OSL_ENSURE( m_nLeftMargin || m_nRightMargin, + "There are still leftovers from relative margins" ); + + // The right margin will be ignored anyway. + SvxLRSpaceItem aLRItem( m_pSwTable->GetFrameFormat()->GetLRSpace() ); + aLRItem.SetLeft( m_nLeftMargin ); + aLRItem.SetRight( m_nRightMargin ); + pFrameFormat->SetFormatAttr( aLRItem ); + } + + if (m_bPercentWidth && text::HoriOrientation::FULL != eHoriOri) + { + pFrameFormat->LockModify(); + SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() ); + aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidth) ); + pFrameFormat->SetFormatAttr( aFrameSize ); + pFrameFormat->UnlockModify(); + } + + // get the default line and box format + // remember the first box and unlist it from the first row + SwTableLine *pLine1 = (m_pSwTable->GetTabLines())[0]; + m_xBox1.reset((pLine1->GetTabBoxes())[0]); + pLine1->GetTabBoxes().erase(pLine1->GetTabBoxes().begin()); + + m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat()); + m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_xBox1->GetFrameFormat()); + + MakeTable_( pBox ); + + // Finally, we'll do a garbage collection for the top level table + + if( 1==m_nRows && m_nHeight && 1==m_pSwTable->GetTabLines().size() ) + { + // Set height of a one-row table as the minimum width of the row + // Was originally a fixed height, but that made problems + // and is not Netscape 4.0 compliant + m_nHeight = SwHTMLParser::ToTwips( m_nHeight ); + if( m_nHeight < MINLAY ) + m_nHeight = MINLAY; + + (m_pSwTable->GetTabLines())[0]->ClaimFrameFormat(); + (m_pSwTable->GetTabLines())[0]->GetFrameFormat() + ->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, m_nHeight ) ); + } + + if( GetBGBrush() ) + m_pSwTable->GetFrameFormat()->SetFormatAttr( *GetBGBrush() ); + + const_cast<SwTable *>(m_pSwTable)->SetRowsToRepeat( static_cast< sal_uInt16 >(m_nHeadlineRepeat) ); + const_cast<SwTable *>(m_pSwTable)->GCLines(); + + bool bIsInFlyFrame = m_pContext && m_pContext->GetFrameFormat(); + if( bIsInFlyFrame && !m_nWidth ) + { + SvxAdjust eAdjust = GetTableAdjust(false); + if (eAdjust != SvxAdjust::Left && + eAdjust != SvxAdjust::Right) + { + // If a table with a width attribute isn't flowed around left or right + // we'll stack it with a border of 100% width, so its size will + // be adapted. That text frame mustn't be modified + OSL_ENSURE( HasToFly(), "Why is the table in a frame?" ); + sal_uInt32 nMin = m_xLayoutInfo->GetMin(); + if( nMin > USHRT_MAX ) + nMin = USHRT_MAX; + SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMin), MINLAY ); + aFlyFrameSize.SetWidthPercent( 100 ); + m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize ); + bIsInFlyFrame = false; + } + else + { + // left or right adjusted table without width mustn't be adjusted in width + // as they would only shrink but never grow + m_xLayoutInfo->SetMustNotRecalc( true ); + if( m_pContext->GetFrameFormat()->GetAnchor().GetContentAnchor() + ->nNode.GetNode().FindTableNode() ) + { + sal_uInt32 nMax = m_xLayoutInfo->GetMax(); + if( nMax > USHRT_MAX ) + nMax = USHRT_MAX; + SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMax), MINLAY ); + m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize ); + bIsInFlyFrame = false; + } + else + { + m_xLayoutInfo->SetMustNotResize( true ); + } + } + } + m_xLayoutInfo->SetMayBeInFlyFrame( bIsInFlyFrame ); + + // Only tables with relative width or without width should be modified + m_xLayoutInfo->SetMustResize( m_bPercentWidth || !m_nWidth ); + + if (!pLine1->GetTabBoxes().empty()) + m_xLayoutInfo->SetWidths(); + else + SAL_WARN("sw.html", "no table box"); + + const_cast<SwTable *>(m_pSwTable)->SetHTMLTableLayout(m_xLayoutInfo); + + if( m_pResizeDrawObjects ) + { + sal_uInt16 nCount = m_pResizeDrawObjects->size(); + for( sal_uInt16 i=0; i<nCount; i++ ) + { + SdrObject *pObj = (*m_pResizeDrawObjects)[i]; + sal_uInt16 nRow = (*m_pDrawObjectPercentWidths)[3*i]; + sal_uInt16 nCol = (*m_pDrawObjectPercentWidths)[3*i+1]; + sal_uInt8 nPercentWidth = static_cast<sal_uInt8>((*m_pDrawObjectPercentWidths)[3*i+2]); + + SwHTMLTableLayoutCell *pLayoutCell = + m_xLayoutInfo->GetCell( nRow, nCol ); + sal_uInt16 nColSpan = pLayoutCell->GetColSpan(); + + sal_uInt16 nWidth2, nDummy; + m_xLayoutInfo->GetAvail( nCol, nColSpan, nWidth2, nDummy ); + nWidth2 = static_cast< sal_uInt16 >((static_cast<long>(m_nWidth) * nPercentWidth) / 100); + + SwHTMLParser::ResizeDrawObject( pObj, nWidth2 ); + } + } + +} + +void HTMLTable::SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt, + sal_uInt16 nLeft, sal_uInt16 nRight, + const SwTable *pSwTab, bool bFrcFrame ) +{ + m_pPrevStartNode = pStNd; + m_pSwTable = pSwTab; + m_pContext = std::move(pCntxt); + + m_nLeftMargin = nLeft; + m_nRightMargin = nRight; + + m_bForceFrame = bFrcFrame; +} + +void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth ) +{ + if( !m_pResizeDrawObjects ) + m_pResizeDrawObjects.reset(new SdrObjects); + m_pResizeDrawObjects->push_back( pObj ); + + if( !m_pDrawObjectPercentWidths ) + m_pDrawObjectPercentWidths.reset(new std::vector<sal_uInt16>); + m_pDrawObjectPercentWidths->push_back( m_nCurrentRow ); + m_pDrawObjectPercentWidths->push_back( m_nCurrentColumn ); + m_pDrawObjectPercentWidths->push_back( static_cast<sal_uInt16>(nPercentWidth) ); +} + +void HTMLTable::MakeParentContents() +{ + if( !GetContext() && !HasParentSection() ) + { + SetParentContents( + m_pParser->InsertTableContents( m_bIsParentHead ) ); + + SetHasParentSection( true ); + } +} + +void HTMLTableContext::SavePREListingXMP( SwHTMLParser& rParser ) +{ + bRestartPRE = rParser.IsReadPRE(); + bRestartXMP = rParser.IsReadXMP(); + bRestartListing = rParser.IsReadListing(); + rParser.FinishPREListingXMP(); +} + +void HTMLTableContext::RestorePREListingXMP( SwHTMLParser& rParser ) +{ + rParser.FinishPREListingXMP(); + + if( bRestartPRE ) + rParser.StartPRE(); + + if( bRestartXMP ) + rParser.StartXMP(); + + if( bRestartListing ) + rParser.StartListing(); +} + +const SwStartNode *SwHTMLParser::InsertTableSection + ( const SwStartNode *pPrevStNd ) +{ + OSL_ENSURE( pPrevStNd, "Start-Node is NULL" ); + + m_pCSS1Parser->SetTDTagStyles(); + SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TABLE ); + + const SwStartNode *pStNd; + if (m_xTable->m_bFirstCell ) + { + SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode(); + pNd->GetTextNode()->ChgFormatColl( pColl ); + pStNd = pNd->FindTableBoxStartNode(); + m_xTable->m_bFirstCell = false; + } + else if (pPrevStNd) + { + const SwNode* pNd; + if( pPrevStNd->IsTableNode() ) + pNd = pPrevStNd; + else + pNd = pPrevStNd->EndOfSectionNode(); + SwNodeIndex nIdx( *pNd, 1 ); + pStNd = m_xDoc->GetNodes().MakeTextSection( nIdx, SwTableBoxStartNode, + pColl ); + m_xTable->IncBoxCount(); + } + else + { + eState = SvParserState::Error; + return nullptr; + } + + //Added defaults to CJK and CTL + SwContentNode *pCNd = m_xDoc->GetNodes()[pStNd->GetIndex()+1] ->GetContentNode(); + SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE ); + pCNd->SetAttr( aFontHeight ); + SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE ); + pCNd->SetAttr( aFontHeightCJK ); + SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE ); + pCNd->SetAttr( aFontHeightCTL ); + + return pStNd; +} + +const SwStartNode *SwHTMLParser::InsertTableSection( sal_uInt16 nPoolId ) +{ + switch( nPoolId ) + { + case RES_POOLCOLL_TABLE_HDLN: + m_pCSS1Parser->SetTHTagStyles(); + break; + case RES_POOLCOLL_TABLE: + m_pCSS1Parser->SetTDTagStyles(); + break; + } + + SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( nPoolId ); + + SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode(); + const SwStartNode *pStNd; + if (m_xTable->m_bFirstCell) + { + SwTextNode* pTextNd = pNd->GetTextNode(); + if (!pTextNd) + { + eState = SvParserState::Error; + return nullptr; + } + pTextNd->ChgFormatColl(pColl); + m_xTable->m_bFirstCell = false; + pStNd = pNd->FindTableBoxStartNode(); + } + else + { + SwTableNode *pTableNd = pNd->FindTableNode(); + if (!pTableNd) + { + eState = SvParserState::Error; + return nullptr; + } + if( pTableNd->GetTable().GetHTMLTableLayout() ) + { // if there is already a HTMTableLayout, this table is already finished + // and we have to look for the right table in the environment + SwTableNode *pOutTable = pTableNd; + do { + pTableNd = pOutTable; + pOutTable = pOutTable->StartOfSectionNode()->FindTableNode(); + } while( pOutTable && pTableNd->GetTable().GetHTMLTableLayout() ); + } + SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() ); + pStNd = m_xDoc->GetNodes().MakeTextSection( aIdx, SwTableBoxStartNode, + pColl ); + + m_pPam->GetPoint()->nNode = pStNd->GetIndex() + 1; + SwTextNode *pTextNd = m_pPam->GetPoint()->nNode.GetNode().GetTextNode(); + m_pPam->GetPoint()->nContent.Assign( pTextNd, 0 ); + m_xTable->IncBoxCount(); + } + + if (!pStNd) + { + eState = SvParserState::Error; + } + + return pStNd; +} + +SwStartNode *SwHTMLParser::InsertTempTableCaptionSection() +{ + SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT ); + SwNodeIndex& rIdx = m_pPam->GetPoint()->nNode; + rIdx = m_xDoc->GetNodes().GetEndOfExtras(); + SwStartNode *pStNd = m_xDoc->GetNodes().MakeTextSection( rIdx, + SwNormalStartNode, pColl ); + + rIdx = pStNd->GetIndex() + 1; + m_pPam->GetPoint()->nContent.Assign( rIdx.GetNode().GetTextNode(), 0 ); + + return pStNd; +} + +sal_Int32 SwHTMLParser::StripTrailingLF() +{ + sal_Int32 nStripped = 0; + + const sal_Int32 nLen = m_pPam->GetPoint()->nContent.GetIndex(); + if( nLen ) + { + SwTextNode* pTextNd = m_pPam->GetPoint()->nNode.GetNode().GetTextNode(); + // careful, when comments aren't ignored!!! + if( pTextNd ) + { + sal_Int32 nPos = nLen; + sal_Int32 nLFCount = 0; + while (nPos && ('\x0a' == pTextNd->GetText()[--nPos])) + nLFCount++; + + if( nLFCount ) + { + if( nLFCount > 2 ) + { + // On Netscape, a paragraph end matches 2 LFs + // (1 is just a newline, 2 creates a blank line) + // We already have this space with the lower paragraph gap + // If there's a paragraph after the <BR>, we take the maximum + // of the gap that results from the <BR> and <P> + // That's why we need to delete 2 respectively all if less than 2 + nLFCount = 2; + } + + nPos = nLen - nLFCount; + SwIndex nIdx( pTextNd, nPos ); + pTextNd->EraseText( nIdx, nLFCount ); + nStripped = nLFCount; + } + } + } + + return nStripped; +} + +SvxBrushItem* SwHTMLParser::CreateBrushItem( const Color *pColor, + const OUString& rImageURL, + const OUString& rStyle, + const OUString& rId, + const OUString& rClass ) +{ + SvxBrushItem *pBrushItem = nullptr; + + if( !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), svl::Items<RES_BACKGROUND, + RES_BACKGROUND>{} ); + SvxCSS1PropertyInfo aPropInfo; + + if( !rClass.isEmpty() ) + { + OUString aClass( rClass ); + SwCSS1Parser::GetScriptFromClass( aClass ); + const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass ); + if( pClass ) + aItemSet.Put( pClass->GetItemSet() ); + } + + if( !rId.isEmpty() ) + { + const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId ); + if( pId ) + aItemSet.Put( pId->GetItemSet() ); + } + + m_pCSS1Parser->ParseStyleOption( rStyle, aItemSet, aPropInfo ); + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + pBrushItem = new SvxBrushItem( *static_cast<const SvxBrushItem *>(pItem) ); + } + } + + if( !pBrushItem && (pColor || !rImageURL.isEmpty()) ) + { + pBrushItem = new SvxBrushItem(RES_BACKGROUND); + + if( pColor ) + pBrushItem->SetColor(*pColor); + + if( !rImageURL.isEmpty() ) + { + pBrushItem->SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), rImageURL, Link<OUString *, bool>(), false) ); + pBrushItem->SetGraphicPos( GPOS_TILED ); + } + } + + return pBrushItem; +} + +class SectionSaveStruct : public SwPendingData +{ + sal_uInt16 m_nBaseFontStMinSave, m_nFontStMinSave, m_nFontStHeadStartSave; + sal_uInt16 m_nDefListDeepSave; + size_t m_nContextStMinSave; + size_t m_nContextStAttrMinSave; + +public: + + std::shared_ptr<HTMLTable> m_xTable; + + explicit SectionSaveStruct( SwHTMLParser& rParser ); + +#if OSL_DEBUG_LEVEL > 0 + size_t GetContextStAttrMin() const { return m_nContextStAttrMinSave; } +#endif + void Restore( SwHTMLParser& rParser ); +}; + +SectionSaveStruct::SectionSaveStruct( SwHTMLParser& rParser ) : + m_nBaseFontStMinSave(rParser.m_nBaseFontStMin), + m_nFontStMinSave(rParser.m_nFontStMin), + m_nFontStHeadStartSave(rParser.m_nFontStHeadStart), + m_nDefListDeepSave(rParser.m_nDefListDeep), + m_nContextStMinSave(rParser.m_nContextStMin), + m_nContextStAttrMinSave(rParser.m_nContextStAttrMin) +{ + // Freeze font stacks + rParser.m_nBaseFontStMin = rParser.m_aBaseFontStack.size(); + + rParser.m_nFontStMin = rParser.m_aFontStack.size(); + + // Freeze context stack + rParser.m_nContextStMin = rParser.m_aContexts.size(); + rParser.m_nContextStAttrMin = rParser.m_nContextStMin; + + // And remember a few counters + rParser.m_nDefListDeep = 0; +} + +void SectionSaveStruct::Restore( SwHTMLParser& rParser ) +{ + // Unfreeze font stacks + sal_uInt16 nMin = rParser.m_nBaseFontStMin; + if( rParser.m_aBaseFontStack.size() > nMin ) + rParser.m_aBaseFontStack.erase( rParser.m_aBaseFontStack.begin() + nMin, + rParser.m_aBaseFontStack.end() ); + rParser.m_nBaseFontStMin = m_nBaseFontStMinSave; + + nMin = rParser.m_nFontStMin; + if( rParser.m_aFontStack.size() > nMin ) + rParser.m_aFontStack.erase( rParser.m_aFontStack.begin() + nMin, + rParser.m_aFontStack.end() ); + rParser.m_nFontStMin = m_nFontStMinSave; + rParser.m_nFontStHeadStart = m_nFontStHeadStartSave; + + OSL_ENSURE( rParser.m_aContexts.size() == rParser.m_nContextStMin && + rParser.m_aContexts.size() == rParser.m_nContextStAttrMin, + "The Context Stack was not cleaned up" ); + rParser.m_nContextStMin = m_nContextStMinSave; + rParser.m_nContextStAttrMin = m_nContextStAttrMinSave; + + // Reconstruct a few counters + rParser.m_nDefListDeep = m_nDefListDeepSave; + + // Reset a few flags + rParser.m_bNoParSpace = false; + rParser.m_nOpenParaToken = HtmlTokenId::NONE; + + rParser.m_aParaAttrs.clear(); +} + +class CellSaveStruct : public SectionSaveStruct +{ + OUString m_aStyle, m_aId, m_aClass; + OUString m_aBGImage; + Color m_aBGColor; + std::shared_ptr<SvxBoxItem> m_xBoxItem; + + std::shared_ptr<HTMLTableCnts> m_xCnts; // List of all contents + HTMLTableCnts* m_pCurrCnts; // current content or 0 + std::unique_ptr<SwNodeIndex> m_pNoBreakEndNodeIndex; // Paragraph index of a <NOBR> + + double m_nValue; + + sal_uInt32 m_nNumFormat; + + sal_uInt16 m_nRowSpan, m_nColSpan, m_nWidth, m_nHeight; + sal_Int32 m_nNoBreakEndContentPos; // Character index of a <NOBR> + + sal_Int16 m_eVertOri; + + bool m_bHead : 1; + bool m_bPercentWidth : 1; + bool m_bHasNumFormat : 1; + bool m_bHasValue : 1; + bool m_bBGColor : 1; + bool m_bNoWrap : 1; // NOWRAP option + bool m_bNoBreak : 1; // NOBREAK tag + +public: + + CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, bool bHd, + bool bReadOpt ); + + void AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts ); + bool HasFirstContents() const { return bool(m_xCnts); } + + void ClearIsInSection() { m_pCurrCnts = nullptr; } + bool IsInSection() const { return m_pCurrCnts!=nullptr; } + + void InsertCell( SwHTMLParser& rParser, HTMLTable *pCurTable ); + + bool IsHeaderCell() const { return m_bHead; } + + void StartNoBreak( const SwPosition& rPos ); + void EndNoBreak( const SwPosition& rPos ); + void CheckNoBreak( const SwPosition& rPos ); +}; + +CellSaveStruct::CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, + bool bHd, bool bReadOpt ) : + SectionSaveStruct( rParser ), + m_pCurrCnts( nullptr ), + m_nValue( 0.0 ), + m_nNumFormat( 0 ), + m_nRowSpan( 1 ), + m_nColSpan( 1 ), + m_nWidth( 0 ), + m_nHeight( 0 ), + m_nNoBreakEndContentPos( 0 ), + m_eVertOri( pCurTable->GetInheritedVertOri() ), + m_bHead( bHd ), + m_bPercentWidth( false ), + m_bHasNumFormat( false ), + m_bHasValue( false ), + m_bBGColor( false ), + m_bNoWrap( false ), + m_bNoBreak( false ) +{ + OUString aNumFormat, aValue, aDir, aLang; + SvxAdjust eAdjust( pCurTable->GetInheritedAdjust() ); + + if( bReadOpt ) + { + const HTMLOptions& rOptions = rParser.GetOptions(); + for (size_t i = rOptions.size(); i; ) + { + const HTMLOption& rOption = rOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + m_aId = rOption.GetString(); + break; + case HtmlOptionId::COLSPAN: + m_nColSpan = static_cast<sal_uInt16>(rOption.GetNumber()); + if (m_nColSpan > 256) + { + SAL_INFO("sw.html", "ignoring huge COLSPAN " << m_nColSpan); + m_nColSpan = 1; + } + break; + case HtmlOptionId::ROWSPAN: + m_nRowSpan = static_cast<sal_uInt16>(rOption.GetNumber()); + if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing())) + { + SAL_INFO("sw.html", "ignoring huge ROWSPAN " << m_nRowSpan); + m_nRowSpan = 1; + } + break; + case HtmlOptionId::ALIGN: + eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust ); + break; + case HtmlOptionId::VALIGN: + m_eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, m_eVertOri ); + break; + case HtmlOptionId::WIDTH: + m_nWidth = static_cast<sal_uInt16>(rOption.GetNumber()); // Just for Netscape + m_bPercentWidth = (rOption.GetString().indexOf('%') != -1); + if( m_bPercentWidth && m_nWidth>100 ) + m_nWidth = 100; + break; + case HtmlOptionId::HEIGHT: + m_nHeight = static_cast<sal_uInt16>(rOption.GetNumber()); // Just for Netscape + if( rOption.GetString().indexOf('%') != -1) + m_nHeight = 0; // don't consider % attributes + break; + case HtmlOptionId::BGCOLOR: + // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape + // *really* not on other tags + if( !rOption.GetString().isEmpty() ) + { + rOption.GetColor( m_aBGColor ); + m_bBGColor = true; + } + break; + case HtmlOptionId::BACKGROUND: + m_aBGImage = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + m_aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + m_aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + case HtmlOptionId::SDNUM: + aNumFormat = rOption.GetString(); + m_bHasNumFormat = true; + break; + case HtmlOptionId::SDVAL: + m_bHasValue = true; + aValue = rOption.GetString(); + break; + case HtmlOptionId::NOWRAP: + m_bNoWrap = true; + break; + default: break; + } + } + + if( !m_aId.isEmpty() ) + rParser.InsertBookmark( m_aId ); + } + + if( m_bHasNumFormat ) + { + LanguageType eLang; + m_nValue = SfxHTMLParser::GetTableDataOptionsValNum( + m_nNumFormat, eLang, aValue, aNumFormat, + *rParser.m_xDoc->GetNumberFormatter() ); + } + + // Create a new context but don't anchor the drawing::Alignment attribute there, + // since there's no section yet + HtmlTokenId nToken; + sal_uInt16 nColl; + if( m_bHead ) + { + nToken = HtmlTokenId::TABLEHEADER_ON; + nColl = RES_POOLCOLL_TABLE_HDLN; + } + else + { + nToken = HtmlTokenId::TABLEDATA_ON; + nColl = RES_POOLCOLL_TABLE; + } + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, OUString(), true)); + if( SvxAdjust::End != eAdjust ) + rParser.InsertAttr(&rParser.m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST), + xCntxt.get()); + + if( SwHTMLParser::HasStyleOptions( m_aStyle, m_aId, m_aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( rParser.m_xDoc->GetAttrPool(), + rParser.m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( rParser.ParseStyleOptions( m_aStyle, m_aId, m_aClass, aItemSet, + aPropInfo, &aLang, &aDir ) ) + { + SfxPoolItem const* pItem; + if (SfxItemState::SET == aItemSet.GetItemState(RES_BOX, false, &pItem)) + { // fdo#41796: steal box item to set it in FixFrameFormat later! + m_xBoxItem.reset(dynamic_cast<SvxBoxItem *>(pItem->Clone())); + aItemSet.ClearItem(RES_BOX); + } + rParser.InsertAttrs(aItemSet, aPropInfo, xCntxt.get()); + } + } + + rParser.SplitPREListingXMP(xCntxt.get()); + + rParser.PushContext(xCntxt); +} + +void CellSaveStruct::AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts ) +{ + m_pCurrCnts = pNewCnts.get(); + + if (m_xCnts) + m_xCnts->Add( std::move(pNewCnts) ); + else + m_xCnts = std::move(pNewCnts); +} + +void CellSaveStruct::InsertCell( SwHTMLParser& rParser, + HTMLTable *pCurTable ) +{ +#if OSL_DEBUG_LEVEL > 0 + // The attributes need to have been removed when tidying up the context stack, + // Otherwise something's wrong. Let's check that... + + // MIB 8.1.98: When attributes were opened outside of a cell, + // they're still in the attribute table and will only be deleted at the end + // through the CleanContext calls in BuildTable. We don't check that there + // so that we get no assert [violations, by translator] + // We can see this on nContextStAttrMin: the remembered value of nContextStAttrMinSave + // is the value that nContextStAttrMin had at the start of the table. And the + // current value of nContextStAttrMin corresponds to the number of contexts + // we found at the start of the cell. If the values differ, contexts + // were created and we don't check anything. + + if( rParser.m_nContextStAttrMin == GetContextStAttrMin() ) + { + HTMLAttr** pTable = reinterpret_cast<HTMLAttr**>(rParser.m_xAttrTab.get()); + + for( auto nCnt = sizeof( HTMLAttrTable ) / sizeof( HTMLAttr* ); + nCnt--; ++pTable ) + { + OSL_ENSURE( !*pTable, "The attribute table isn't empty" ); + } + } +#endif + + // we need to add the cell on the current position + std::shared_ptr<SvxBrushItem> xBrushItem( + rParser.CreateBrushItem(m_bBGColor ? &m_aBGColor : nullptr, m_aBGImage, + m_aStyle, m_aId, m_aClass)); + pCurTable->InsertCell( m_xCnts, m_nRowSpan, m_nColSpan, m_nWidth, + m_bPercentWidth, m_nHeight, m_eVertOri, xBrushItem, m_xBoxItem, + m_bHasNumFormat, m_nNumFormat, m_bHasValue, m_nValue, + m_bNoWrap ); + Restore( rParser ); +} + +void CellSaveStruct::StartNoBreak( const SwPosition& rPos ) +{ + if( !m_xCnts || + (!rPos.nContent.GetIndex() && m_pCurrCnts == m_xCnts.get() && + m_xCnts->GetStartNode() && + m_xCnts->GetStartNode()->GetIndex() + 1 == + rPos.nNode.GetIndex()) ) + { + m_bNoBreak = true; + } +} + +void CellSaveStruct::EndNoBreak( const SwPosition& rPos ) +{ + if( m_bNoBreak ) + { + m_pNoBreakEndNodeIndex.reset( new SwNodeIndex( rPos.nNode ) ); + m_nNoBreakEndContentPos = rPos.nContent.GetIndex(); + m_bNoBreak = false; + } +} + +void CellSaveStruct::CheckNoBreak( const SwPosition& rPos ) +{ + if (m_xCnts && m_pCurrCnts == m_xCnts.get()) + { + if( m_bNoBreak ) + { + // <NOBR> wasn't closed + m_xCnts->SetNoBreak(); + } + else if( m_pNoBreakEndNodeIndex && + m_pNoBreakEndNodeIndex->GetIndex() == rPos.nNode.GetIndex() ) + { + if( m_nNoBreakEndContentPos == rPos.nContent.GetIndex() ) + { + // <NOBR> was closed immediately before the cell end + m_xCnts->SetNoBreak(); + } + else if( m_nNoBreakEndContentPos + 1 == rPos.nContent.GetIndex() ) + { + SwTextNode const*const pTextNd(rPos.nNode.GetNode().GetTextNode()); + if( pTextNd ) + { + sal_Unicode const cLast = + pTextNd->GetText()[m_nNoBreakEndContentPos]; + if( ' '==cLast || '\x0a'==cLast ) + { + // There's just a blank or a newline between the <NOBR> and the cell end + m_xCnts->SetNoBreak(); + } + } + } + } + } +} + +std::unique_ptr<HTMLTableCnts> SwHTMLParser::InsertTableContents( + bool bHead ) +{ + // create a new section, the PaM is gonna be there + const SwStartNode *pStNd = + InsertTableSection( static_cast< sal_uInt16 >(bHead ? RES_POOLCOLL_TABLE_HDLN + : RES_POOLCOLL_TABLE) ); + + if( GetNumInfo().GetNumRule() ) + { + // Set the first paragraph to non-enumerated + sal_uInt8 nLvl = GetNumInfo().GetLevel(); + + SetNodeNum( nLvl ); + } + + // Reset attributation start + const SwNodeIndex& rSttPara = m_pPam->GetPoint()->nNode; + sal_Int32 nSttCnt = m_pPam->GetPoint()->nContent.GetIndex(); + + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + for (sal_uInt16 nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes) + { + HTMLAttr *pAttr = *pHTMLAttributes; + while( pAttr ) + { + OSL_ENSURE( !pAttr->GetPrev(), "Attribute has previous list" ); + pAttr->m_nStartPara = rSttPara; + pAttr->m_nEndPara = rSttPara; + pAttr->m_nStartContent = nSttCnt; + pAttr->m_nEndContent = nSttCnt; + + pAttr = pAttr->GetNext(); + } + } + + return std::make_unique<HTMLTableCnts>( pStNd ); +} + +sal_uInt16 SwHTMLParser::IncGrfsThatResizeTable() +{ + return m_xTable ? m_xTable->IncGrfsThatResize() : 0; +} + +void SwHTMLParser::RegisterDrawObjectToTable( HTMLTable *pCurTable, + SdrObject *pObj, sal_uInt8 nPercentWidth ) +{ + pCurTable->RegisterDrawObject( pObj, nPercentWidth ); +} + +void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions, + bool bHead ) +{ + if( !IsParserWorking() && m_vPendingStack.empty() ) + return; + + ::comphelper::FlagRestorationGuard g(m_isInTableStructure, false); + std::unique_ptr<CellSaveStruct> xSaveStruct; + + HtmlTokenId nToken = HtmlTokenId::NONE; + bool bPending = false; + if( !m_vPendingStack.empty() ) + { + xSaveStruct.reset(static_cast<CellSaveStruct*>(m_vPendingStack.back().pData.release())); + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + bPending = SvParserState::Error == eState && !m_vPendingStack.empty(); + + SaveState( nToken ); + } + else + { + // <TH> resp. <TD> were already read + if (m_xTable->IsOverflowing()) + { + SaveState( HtmlTokenId::NONE ); + return; + } + + if( !pCurTable->GetContext() ) + { + bool bTopTable = m_xTable.get() == pCurTable; + + // the table has no content yet, this means the actual table needs + // to be created first + + static sal_uInt16 aWhichIds[] = + { + RES_PARATR_SPLIT, RES_PARATR_SPLIT, + RES_PAGEDESC, RES_PAGEDESC, + RES_BREAK, RES_BREAK, + RES_BACKGROUND, RES_BACKGROUND, + RES_KEEP, RES_KEEP, + RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT, + RES_FRAMEDIR, RES_FRAMEDIR, + 0 + }; + + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), aWhichIds ); + SvxCSS1PropertyInfo aPropInfo; + + bool bStyleParsed = ParseStyleOptions( pCurTable->GetStyle(), + pCurTable->GetId(), + pCurTable->GetClass(), + aItemSet, aPropInfo, + nullptr, &pCurTable->GetDirection() ); + const SfxPoolItem *pItem = nullptr; + if( bStyleParsed ) + { + if( SfxItemState::SET == aItemSet.GetItemState( + RES_BACKGROUND, false, &pItem ) ) + { + pCurTable->SetBGBrush( *static_cast<const SvxBrushItem *>(pItem) ); + aItemSet.ClearItem( RES_BACKGROUND ); + } + if( SfxItemState::SET == aItemSet.GetItemState( + RES_PARATR_SPLIT, false, &pItem ) ) + { + aItemSet.Put( + SwFormatLayoutSplit( static_cast<const SvxFormatSplitItem *>(pItem) + ->GetValue() ) ); + aItemSet.ClearItem( RES_PARATR_SPLIT ); + } + } + + sal_uInt16 nLeftSpace = 0; + sal_uInt16 nRightSpace = 0; + short nIndent; + GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent ); + + // save the current position we'll get back to some time + SwPosition *pSavePos = nullptr; + bool bForceFrame = false; + bool bAppended = false; + bool bParentLFStripped = false; + if( bTopTable ) + { + SvxAdjust eTableAdjust = m_xTable->GetTableAdjust(false); + + // If the table is left or right adjusted or should be in a text frame, + // it'll get one + bForceFrame = eTableAdjust == SvxAdjust::Left || + eTableAdjust == SvxAdjust::Right || + pCurTable->HasToFly(); + + // The table either shouldn't get in a text frame and isn't in one + // (it gets simulated through cells), + // or there's already content at that position + OSL_ENSURE( !bForceFrame || pCurTable->HasParentSection(), + "table in frame has no parent!" ); + + bool bAppend = false; + if( bForceFrame ) + { + // If the table gets in a border, we only need to open a new + //paragraph if the paragraph has text frames that don't fly + bAppend = HasCurrentParaFlys(true); + } + else + { + // Otherwise, we need to open a new paragraph if the paragraph + // is empty or contains text frames or bookmarks + bAppend = + m_pPam->GetPoint()->nContent.GetIndex() || + HasCurrentParaFlys() || + HasCurrentParaBookmarks(); + } + if( bAppend ) + { + if( !m_pPam->GetPoint()->nContent.GetIndex() ) + { + //Set default to CJK and CTL + m_xDoc->SetTextFormatColl( *m_pPam, + m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_STANDARD) ); + SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE ); + + HTMLAttr* pTmp = + new HTMLAttr( *m_pPam->GetPoint(), aFontHeight, nullptr, std::shared_ptr<HTMLAttrTable>() ); + m_aSetAttrTab.push_back( pTmp ); + + SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE ); + pTmp = + new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCJK, nullptr, std::shared_ptr<HTMLAttrTable>() ); + m_aSetAttrTab.push_back( pTmp ); + + SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE ); + pTmp = + new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCTL, nullptr, std::shared_ptr<HTMLAttrTable>() ); + m_aSetAttrTab.push_back( pTmp ); + + pTmp = new HTMLAttr( *m_pPam->GetPoint(), + SvxULSpaceItem( 0, 0, RES_UL_SPACE ), nullptr, std::shared_ptr<HTMLAttrTable>() ); + m_aSetAttrTab.push_front( pTmp ); // Position 0, since + // something can be set by + // the table end before + } + AppendTextNode( AM_NOSPACE ); + bAppended = true; + } + else if( !m_aParaAttrs.empty() ) + { + if( !bForceFrame ) + { + // The paragraph will be moved right behind the table. + // That's why we remove all hard attributes of that paragraph + + for(HTMLAttr* i : m_aParaAttrs) + i->Invalidate(); + } + + m_aParaAttrs.clear(); + } + + pSavePos = new SwPosition( *m_pPam->GetPoint() ); + } + else if( pCurTable->HasParentSection() ) + { + bParentLFStripped = StripTrailingLF() > 0; + + // Close paragraph resp. headers + m_nOpenParaToken = HtmlTokenId::NONE; + m_nFontStHeadStart = m_nFontStMin; + + // The hard attributes on that paragraph are never gonna be invalid anymore + m_aParaAttrs.clear(); + } + + // create a table context + std::unique_ptr<HTMLTableContext> pTCntxt( + new HTMLTableContext( pSavePos, m_nContextStMin, + m_nContextStAttrMin ) ); + + // end all open attributes and open them again behind the table + std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts; + if( !bForceFrame && (bTopTable || pCurTable->HasParentSection()) ) + { + SplitAttrTab(pTCntxt->xAttrTab, bTopTable); + // If we reuse an already existing paragraph, we can't add + // PostIts since the paragraph gets behind that table. + // They're gonna be moved into the first paragraph of the table + // If we have tables in tables, we also can't add PostIts to a + // still empty paragraph, since it's not gonna be deleted that way + if( (bTopTable && !bAppended) || + (!bTopTable && !bParentLFStripped && + !m_pPam->GetPoint()->nContent.GetIndex()) ) + pPostIts.reset(new std::deque<std::unique_ptr<HTMLAttr>>); + SetAttr( bTopTable, bTopTable, pPostIts.get() ); + } + else + { + SaveAttrTab(pTCntxt->xAttrTab); + if( bTopTable && !bAppended ) + { + pPostIts.reset(new std::deque<std::unique_ptr<HTMLAttr>>); + SetAttr( true, true, pPostIts.get() ); + } + } + m_bNoParSpace = false; + + // Save current numbering and turn it off + pTCntxt->SetNumInfo( GetNumInfo() ); + GetNumInfo().Clear(); + pTCntxt->SavePREListingXMP( *this ); + + if( bTopTable ) + { + if( bForceFrame ) + { + // the table should be put in a text frame + + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !pCurTable->IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE; + sal_Int16 eHori; + + switch( pCurTable->GetTableAdjust(true) ) + { + case SvxAdjust::Right: + eHori = text::HoriOrientation::RIGHT; + eSurround = css::text::WrapTextMode_LEFT; + break; + case SvxAdjust::Center: + eHori = text::HoriOrientation::CENTER; + break; + case SvxAdjust::Left: + eSurround = css::text::WrapTextMode_RIGHT; + [[fallthrough]]; + default: + eHori = text::HoriOrientation::LEFT; + break; + } + SetAnchorAndAdjustment( text::VertOrientation::NONE, eHori, aFrameSet, + true ); + aFrameSet.Put( SwFormatSurround(eSurround) ); + + SwFormatFrameSize aFrameSize( SwFrameSize::Variable, 20*MM50, MINLAY ); + aFrameSize.SetWidthPercent( 100 ); + aFrameSet.Put( aFrameSize ); + + sal_uInt16 nSpace = pCurTable->GetHSpace(); + if( nSpace ) + aFrameSet.Put( SvxLRSpaceItem(nSpace,nSpace, 0, 0, RES_LR_SPACE) ); + nSpace = pCurTable->GetVSpace(); + if( nSpace ) + aFrameSet.Put( SvxULSpaceItem(nSpace,nSpace, RES_UL_SPACE) ); + + RndStdIds eAnchorId = aFrameSet. + Get( RES_ANCHOR ). + GetAnchorId(); + SwFrameFormat *pFrameFormat = m_xDoc->MakeFlySection( + eAnchorId, m_pPam->GetPoint(), &aFrameSet ); + + pTCntxt->SetFrameFormat( pFrameFormat ); + const SwFormatContent& rFlyContent = pFrameFormat->GetContent(); + m_pPam->GetPoint()->nNode = *rFlyContent.GetContentIdx(); + SwContentNode *pCNd = + m_xDoc->GetNodes().GoNext( &(m_pPam->GetPoint()->nNode) ); + m_pPam->GetPoint()->nContent.Assign( pCNd, 0 ); + + } + + // create a SwTable with a box and set the PaM to the content of + // the box section (the adjustment parameter is a dummy for now + // and will be corrected later) + OSL_ENSURE( !m_pPam->GetPoint()->nContent.GetIndex(), + "The paragraph after the table is not empty!" ); + const SwTable* pSwTable = m_xDoc->InsertTable( + SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ), + *m_pPam->GetPoint(), 1, 1, text::HoriOrientation::LEFT ); + SwFrameFormat *pFrameFormat = pSwTable ? pSwTable->GetFrameFormat() : nullptr; + + if( bForceFrame ) + { + SwNodeIndex aDstIdx( m_pPam->GetPoint()->nNode ); + m_pPam->Move( fnMoveBackward ); + m_xDoc->GetNodes().Delete( aDstIdx ); + } + else + { + if (bStyleParsed && pFrameFormat) + { + m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo ); + pFrameFormat->SetFormatAttr( aItemSet ); + } + m_pPam->Move( fnMoveBackward ); + } + + SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode(); + SwTextNode *const pOldTextNd = (!bAppended && !bForceFrame) ? + pSavePos->nNode.GetNode().GetTextNode() : nullptr; + + if (pFrameFormat && pOldTextNd) + { + const SfxPoolItem* pItem2; + if( SfxItemState::SET == pOldTextNd->GetSwAttrSet() + .GetItemState( RES_PAGEDESC, false, &pItem2 ) && + static_cast<const SwFormatPageDesc *>(pItem2)->GetPageDesc() ) + { + pFrameFormat->SetFormatAttr( *pItem2 ); + pOldTextNd->ResetAttr( RES_PAGEDESC ); + } + if( SfxItemState::SET == pOldTextNd->GetSwAttrSet() + .GetItemState( RES_BREAK, true, &pItem2 ) ) + { + switch( static_cast<const SvxFormatBreakItem *>(pItem2)->GetBreak() ) + { + case SvxBreak::PageBefore: + case SvxBreak::PageAfter: + case SvxBreak::PageBoth: + pFrameFormat->SetFormatAttr( *pItem2 ); + pOldTextNd->ResetAttr( RES_BREAK ); + break; + default: + break; + } + } + } + + if( !bAppended && pPostIts ) + { + // set still-existing PostIts to the first paragraph of the table + InsertAttrs( std::move(*pPostIts) ); + pPostIts.reset(); + } + + pTCntxt->SetTableNode( const_cast<SwTableNode *>(pNd->FindTableNode()) ); + + auto pTableNode = pTCntxt->GetTableNode(); + pCurTable->SetTable( pTableNode, std::move(pTCntxt), + nLeftSpace, nRightSpace, + pSwTable, bForceFrame ); + + OSL_ENSURE( !pPostIts, "unused PostIts" ); + } + else + { + // still open sections need to be deleted + if( EndSections( bParentLFStripped ) ) + bParentLFStripped = false; + + if( pCurTable->HasParentSection() ) + { + // after that, we remove a possibly redundant empty paragraph, + // but only if it was empty before we stripped the LFs + if( !bParentLFStripped ) + StripTrailingPara(); + + if( pPostIts ) + { + // move still existing PostIts to the end of the current paragraph + InsertAttrs( std::move(*pPostIts) ); + pPostIts.reset(); + } + } + + SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode(); + const SwStartNode *pStNd = (m_xTable->m_bFirstCell ? pNd->FindTableNode() + : pNd->FindTableBoxStartNode() ); + + pCurTable->SetTable( pStNd, std::move(pTCntxt), nLeftSpace, nRightSpace ); + } + + // Freeze the context stack, since there could be attributes set + // outside of cells. Can't happen earlier, since there may be + // searches in the stack + m_nContextStMin = m_aContexts.size(); + m_nContextStAttrMin = m_nContextStMin; + } + + xSaveStruct.reset(new CellSaveStruct(*this, pCurTable, bHead, bReadOptions)); + + // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning. + SaveState( HtmlTokenId::NONE ); + } + + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); // Token after <TABLE> + + bool bDone = false; + while( (IsParserWorking() && !bDone) || bPending ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || xSaveStruct->IsInSection(), + "Where is the section??" ); + if( m_vPendingStack.empty() && m_bCallNextToken && xSaveStruct->IsInSection() ) + { + // Call NextToken directly (e.g. ignore the content of floating frames or applets) + NextToken( nToken ); + } + else switch( nToken ) + { + case HtmlTokenId::TABLEHEADER_ON: + case HtmlTokenId::TABLEDATA_ON: + case HtmlTokenId::TABLEROW_ON: + case HtmlTokenId::TABLEROW_OFF: + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::THEAD_OFF: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TFOOT_OFF: + case HtmlTokenId::TBODY_ON: + case HtmlTokenId::TBODY_OFF: + case HtmlTokenId::TABLE_OFF: + SkipToken(); + [[fallthrough]]; + case HtmlTokenId::TABLEHEADER_OFF: + case HtmlTokenId::TABLEDATA_OFF: + bDone = true; + break; + case HtmlTokenId::TABLE_ON: + { + bool bHasToFly = false; + SvxAdjust eTabAdjust = SvxAdjust::End; + if( m_vPendingStack.empty() ) + { + // only if we create a new table, but not if we're still + // reading in the table after a Pending + xSaveStruct->m_xTable = m_xTable; + + // HACK: create a section for a table that goes in a text frame + if( !xSaveStruct->IsInSection() ) + { + // The loop needs to be forward, since the + // first option always wins + bool bNeedsSection = false; + const HTMLOptions& rHTMLOptions = GetOptions(); + for (const auto & rOption : rHTMLOptions) + { + if( HtmlOptionId::ALIGN==rOption.GetToken() ) + { + SvxAdjust eAdjust = rOption.GetEnum( aHTMLPAlignTable, SvxAdjust::End ); + bNeedsSection = SvxAdjust::Left == eAdjust || + SvxAdjust::Right == eAdjust; + break; + } + } + if( bNeedsSection ) + { + xSaveStruct->AddContents( + InsertTableContents(bHead ) ); + } + } + else + { + // If Flys are anchored in the current paragraph, + // the table needs to get in a text frame + bHasToFly = HasCurrentParaFlys(false,true); + } + + // There could be a section in the cell + eTabAdjust = m_xAttrTab->pAdjust + ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()). + GetAdjust() + : SvxAdjust::End; + } + + std::shared_ptr<HTMLTable> xSubTable = BuildTable(eTabAdjust, + bHead, + xSaveStruct->IsInSection(), + bHasToFly); + if( SvParserState::Pending != GetStatus() ) + { + // Only if the table is really complete + if (xSubTable) + { + OSL_ENSURE( xSubTable->GetTableAdjust(false)!= SvxAdjust::Left && + xSubTable->GetTableAdjust(false)!= SvxAdjust::Right, + "left or right aligned tables belong in frames" ); + + auto& rParentContents = xSubTable->GetParentContents(); + if (rParentContents) + { + OSL_ENSURE( !xSaveStruct->IsInSection(), + "Where is the section" ); + + // If there's no table coming, we have a section + xSaveStruct->AddContents(std::move(rParentContents)); + } + + const SwStartNode *pCapStNd = + xSubTable->GetCaptionStartNode(); + + if (xSubTable->GetContext()) + { + OSL_ENSURE( !xSubTable->GetContext()->GetFrameFormat(), + "table in frame" ); + + if( pCapStNd && xSubTable->IsTopCaption() ) + { + xSaveStruct->AddContents( + std::make_unique<HTMLTableCnts>(pCapStNd) ); + } + + xSaveStruct->AddContents( + std::make_unique<HTMLTableCnts>(xSubTable) ); + + if( pCapStNd && !xSubTable->IsTopCaption() ) + { + xSaveStruct->AddContents( + std::make_unique<HTMLTableCnts>(pCapStNd) ); + } + + // We don't have a section anymore + xSaveStruct->ClearIsInSection(); + } + else if( pCapStNd ) + { + // Since we can't delete this section (it might + // belong to the first box), we'll add it + xSaveStruct->AddContents( + std::make_unique<HTMLTableCnts>(pCapStNd) ); + + // We don't have a section anymore + xSaveStruct->ClearIsInSection(); + } + } + + m_xTable = xSaveStruct->m_xTable; + } + } + break; + + case HtmlTokenId::NOBR_ON: + // HACK for MS: Is the <NOBR> at the start of the cell? + xSaveStruct->StartNoBreak( *m_pPam->GetPoint() ); + break; + + case HtmlTokenId::NOBR_OFF: + xSaveStruct->EndNoBreak( *m_pPam->GetPoint() ); + break; + + case HtmlTokenId::COMMENT: + // Spaces are not gonna be deleted with comment fields, + // and we don't want a new cell for a comment + NextToken( nToken ); + break; + + case HtmlTokenId::MARQUEE_ON: + if( !xSaveStruct->IsInSection() ) + { + // create a new section, the PaM is gonna be there + xSaveStruct->AddContents( + InsertTableContents( bHead ) ); + } + m_bCallNextToken = true; + NewMarquee( pCurTable ); + break; + + case HtmlTokenId::TEXTTOKEN: + // Don't add a section for an empty string + if( !xSaveStruct->IsInSection() && 1==aToken.getLength() && + ' '==aToken[0] ) + break; + [[fallthrough]]; + default: + if( !xSaveStruct->IsInSection() ) + { + // add a new section, the PaM's gonna be there + xSaveStruct->AddContents( + InsertTableContents( bHead ) ); + } + + if( IsParserWorking() || bPending ) + NextToken( nToken ); + break; + } + + OSL_ENSURE( !bPending || m_vPendingStack.empty(), + "SwHTMLParser::BuildTableCell: There is a PendStack again" ); + bPending = false; + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending == GetStatus() ) + { + m_vPendingStack.emplace_back( bHead ? HtmlTokenId::TABLEHEADER_ON + : HtmlTokenId::TABLEDATA_ON ); + m_vPendingStack.back().pData = std::move(xSaveStruct); + + return; + } + + // If the content of the cell was empty, we need to create an empty content + // We also create an empty content if the cell ended with a table and had no + // COL tags. Otherwise, it was probably exported by us and we don't + // want to have an additional paragraph + if( !xSaveStruct->HasFirstContents() || + (!xSaveStruct->IsInSection() && !pCurTable->HasColTags()) ) + { + OSL_ENSURE( xSaveStruct->HasFirstContents() || + !xSaveStruct->IsInSection(), + "Section or not, that is the question here" ); + const SwStartNode *pStNd = + InsertTableSection( static_cast< sal_uInt16 >(xSaveStruct->IsHeaderCell() + ? RES_POOLCOLL_TABLE_HDLN + : RES_POOLCOLL_TABLE )); + + if (!pStNd) + eState = SvParserState::Error; + else + { + const SwEndNode *pEndNd = pStNd->EndOfSectionNode(); + SwContentNode *pCNd = m_xDoc->GetNodes()[pEndNd->GetIndex()-1] ->GetContentNode(); + if (!pCNd) + eState = SvParserState::Error; + else + { + //Added defaults to CJK and CTL + SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE ); + pCNd->SetAttr( aFontHeight ); + SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE ); + pCNd->SetAttr( aFontHeightCJK ); + SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE ); + pCNd->SetAttr( aFontHeightCTL ); + } + } + + xSaveStruct->AddContents( std::make_unique<HTMLTableCnts>(pStNd) ); + xSaveStruct->ClearIsInSection(); + } + + if( xSaveStruct->IsInSection() ) + { + xSaveStruct->CheckNoBreak( *m_pPam->GetPoint() ); + + // End all open contexts. We'll take AttrMin because nContextStMin might + // have been modified. Since it's gonna be restored by EndContext, it's okay + while( m_aContexts.size() > m_nContextStAttrMin+1 ) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + EndContext(xCntxt.get()); + } + + // Remove LFs at the paragraph end + if (StripTrailingLF() == 0 && !m_pPam->GetPoint()->nContent.GetIndex()) + { + HTMLTableContext* pTableContext = m_xTable ? m_xTable->GetContext() : nullptr; + SwPosition* pSavedPos = pTableContext ? pTableContext->GetPos() : nullptr; + const bool bDeleteSafe = !pSavedPos || pSavedPos->nNode != m_pPam->GetPoint()->nNode; + if (bDeleteSafe) + StripTrailingPara(); + } + + // If there was an adjustment set for the cell, we need to close it + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + if (xCntxt) + EndContext(xCntxt.get()); + } + else + { + // Close all still open contexts + while( m_aContexts.size() > m_nContextStAttrMin ) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + if (!xCntxt) + break; + ClearContext(xCntxt.get()); + } + } + + // end an enumeration + GetNumInfo().Clear(); + + SetAttr( false ); + + xSaveStruct->InsertCell( *this, pCurTable ); + + // we're probably before a <TH>, <TD>, <TR> or </TABLE> + xSaveStruct.reset(); +} + +namespace { + +class RowSaveStruct : public SwPendingData +{ +public: + SvxAdjust eAdjust; + sal_Int16 eVertOri; + bool bHasCells; + + RowSaveStruct() : + eAdjust( SvxAdjust::End ), eVertOri( text::VertOrientation::TOP ), bHasCells( false ) + {} +}; + +} + +void SwHTMLParser::BuildTableRow( HTMLTable *pCurTable, bool bReadOptions, + SvxAdjust eGrpAdjust, + sal_Int16 eGrpVertOri ) +{ + // <TR> was already read + + if( !IsParserWorking() && m_vPendingStack.empty() ) + return; + + HtmlTokenId nToken = HtmlTokenId::NONE; + std::unique_ptr<RowSaveStruct> xSaveStruct; + + bool bPending = false; + if( !m_vPendingStack.empty() ) + { + xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release())); + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + bPending = SvParserState::Error == eState && !m_vPendingStack.empty(); + + SaveState( nToken ); + } + else + { + SvxAdjust eAdjust = eGrpAdjust; + sal_Int16 eVertOri = eGrpVertOri; + Color aBGColor; + OUString aBGImage, aStyle, aId, aClass; + bool bBGColor = false; + xSaveStruct.reset(new RowSaveStruct); + + if( bReadOptions ) + { + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust ); + break; + case HtmlOptionId::VALIGN: + eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri ); + break; + case HtmlOptionId::BGCOLOR: + // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/>TH> like Netscape + // *really* not on other tags + if( !rOption.GetString().isEmpty() ) + { + rOption.GetColor( aBGColor ); + bBGColor = true; + } + break; + case HtmlOptionId::BACKGROUND: + aBGImage = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass= rOption.GetString(); + break; + default: break; + } + } + } + + if( !aId.isEmpty() ) + InsertBookmark( aId ); + + std::unique_ptr<SvxBrushItem> xBrushItem( + CreateBrushItem( bBGColor ? &aBGColor : nullptr, aBGImage, aStyle, + aId, aClass )); + pCurTable->OpenRow(eAdjust, eVertOri, xBrushItem); + // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning. + SaveState( HtmlTokenId::NONE ); + } + + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); + + bool bDone = false; + while( (IsParserWorking() && !bDone) || bPending ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || + pCurTable->GetContext() || pCurTable->HasParentSection(), + "Where is the section??" ); + if( m_vPendingStack.empty() && m_bCallNextToken && + (pCurTable->GetContext() || pCurTable->HasParentSection()) ) + { + /// Call NextToken directly (e.g. ignore the content of floating frames or applets) + NextToken( nToken ); + } + else switch( nToken ) + { + case HtmlTokenId::TABLE_ON: + if( !pCurTable->GetContext() ) + { + SkipToken(); + bDone = true; + } + + break; + case HtmlTokenId::TABLEROW_ON: + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::THEAD_OFF: + case HtmlTokenId::TBODY_ON: + case HtmlTokenId::TBODY_OFF: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TFOOT_OFF: + case HtmlTokenId::TABLE_OFF: + SkipToken(); + [[fallthrough]]; + case HtmlTokenId::TABLEROW_OFF: + bDone = true; + break; + case HtmlTokenId::TABLEHEADER_ON: + case HtmlTokenId::TABLEDATA_ON: + BuildTableCell( pCurTable, true, HtmlTokenId::TABLEHEADER_ON==nToken ); + if( SvParserState::Pending != GetStatus() ) + { + xSaveStruct->bHasCells = true; + bDone = m_xTable->IsOverflowing(); + } + break; + case HtmlTokenId::CAPTION_ON: + BuildTableCaption( pCurTable ); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::CAPTION_OFF: + case HtmlTokenId::TABLEHEADER_OFF: + case HtmlTokenId::TABLEDATA_OFF: + case HtmlTokenId::COLGROUP_ON: + case HtmlTokenId::COLGROUP_OFF: + case HtmlTokenId::COL_ON: + case HtmlTokenId::COL_OFF: + // Where no cell started, there can't be a cell ending + // all the other tokens are bogus anyway and only break the table + break; + case HtmlTokenId::MULTICOL_ON: + // we can't add columned text frames here + break; + case HtmlTokenId::FORM_ON: + NewForm( false ); // don't create a new paragraph + break; + case HtmlTokenId::FORM_OFF: + EndForm( false ); // don't create a new paragraph + break; + case HtmlTokenId::COMMENT: + NextToken( nToken ); + break; + case HtmlTokenId::MAP_ON: + // an image map doesn't add anything, so we can parse it without a cell + NextToken( nToken ); + break; + case HtmlTokenId::TEXTTOKEN: + if( (pCurTable->GetContext() || + !pCurTable->HasParentSection()) && + 1==aToken.getLength() && ' '==aToken[0] ) + break; + [[fallthrough]]; + default: + pCurTable->MakeParentContents(); + NextToken( nToken ); + break; + } + + OSL_ENSURE( !bPending || m_vPendingStack.empty(), + "SwHTMLParser::BuildTableRow: There is a PendStack again" ); + bPending = false; + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending == GetStatus() ) + { + m_vPendingStack.emplace_back( HtmlTokenId::TABLEROW_ON ); + m_vPendingStack.back().pData = std::move(xSaveStruct); + } + else + { + pCurTable->CloseRow(!xSaveStruct->bHasCells); + xSaveStruct.reset(); + } + + // we're probably before <TR> or </TABLE> +} + +void SwHTMLParser::BuildTableSection( HTMLTable *pCurTable, + bool bReadOptions, + bool bHead ) +{ + // <THEAD>, <TBODY> resp. <TFOOT> were read already + if( !IsParserWorking() && m_vPendingStack.empty() ) + return; + + HtmlTokenId nToken = HtmlTokenId::NONE; + bool bPending = false; + std::unique_ptr<RowSaveStruct> xSaveStruct; + + if( !m_vPendingStack.empty() ) + { + xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release())); + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + bPending = SvParserState::Error == eState && !m_vPendingStack.empty(); + + SaveState( nToken ); + } + else + { + xSaveStruct.reset(new RowSaveStruct); + + if( bReadOptions ) + { + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + InsertBookmark( rOption.GetString() ); + break; + case HtmlOptionId::ALIGN: + xSaveStruct->eAdjust = + rOption.GetEnum( aHTMLPAlignTable, xSaveStruct->eAdjust ); + break; + case HtmlOptionId::VALIGN: + xSaveStruct->eVertOri = + rOption.GetEnum( aHTMLTableVAlignTable, + xSaveStruct->eVertOri ); + break; + default: break; + } + } + } + + // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning. + SaveState( HtmlTokenId::NONE ); + } + + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); + + bool bDone = false; + while( (IsParserWorking() && !bDone) || bPending ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || + pCurTable->GetContext() || pCurTable->HasParentSection(), + "Where is the section?" ); + if( m_vPendingStack.empty() && m_bCallNextToken && + (pCurTable->GetContext() || pCurTable->HasParentSection()) ) + { + // Call NextToken directly (e.g. ignore the content of floating frames or applets) + NextToken( nToken ); + } + else switch( nToken ) + { + case HtmlTokenId::TABLE_ON: + if( !pCurTable->GetContext() ) + { + SkipToken(); + bDone = true; + } + + break; + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TBODY_ON: + case HtmlTokenId::TABLE_OFF: + SkipToken(); + [[fallthrough]]; + case HtmlTokenId::THEAD_OFF: + case HtmlTokenId::TBODY_OFF: + case HtmlTokenId::TFOOT_OFF: + bDone = true; + break; + case HtmlTokenId::CAPTION_ON: + BuildTableCaption( pCurTable ); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::CAPTION_OFF: + break; + case HtmlTokenId::TABLEHEADER_ON: + case HtmlTokenId::TABLEDATA_ON: + SkipToken(); + BuildTableRow( pCurTable, false, xSaveStruct->eAdjust, + xSaveStruct->eVertOri ); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::TABLEROW_ON: + BuildTableRow( pCurTable, true, xSaveStruct->eAdjust, + xSaveStruct->eVertOri ); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::MULTICOL_ON: + // we can't add columned text frames here + break; + case HtmlTokenId::FORM_ON: + NewForm( false ); // don't create a new paragraph + break; + case HtmlTokenId::FORM_OFF: + EndForm( false ); // don't create a new paragraph + break; + case HtmlTokenId::TEXTTOKEN: + // blank strings may be a series of CR+LF and no text + if( (pCurTable->GetContext() || + !pCurTable->HasParentSection()) && + 1==aToken.getLength() && ' ' == aToken[0] ) + break; + [[fallthrough]]; + default: + pCurTable->MakeParentContents(); + NextToken( nToken ); + } + + OSL_ENSURE( !bPending || m_vPendingStack.empty(), + "SwHTMLParser::BuildTableSection: There is a PendStack again" ); + bPending = false; + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending == GetStatus() ) + { + m_vPendingStack.emplace_back( bHead ? HtmlTokenId::THEAD_ON + : HtmlTokenId::TBODY_ON ); + m_vPendingStack.back().pData = std::move(xSaveStruct); + } + else + { + pCurTable->CloseSection( bHead ); + xSaveStruct.reset(); + } + + // now we stand (perhaps) in front of <TBODY>,... or </TABLE> +} + +namespace { + +struct TableColGrpSaveStruct : public SwPendingData +{ + sal_uInt16 nColGrpSpan; + sal_uInt16 nColGrpWidth; + bool bRelColGrpWidth; + SvxAdjust eColGrpAdjust; + sal_Int16 eColGrpVertOri; + + inline TableColGrpSaveStruct(); + + inline void CloseColGroup( HTMLTable *pTable ); +}; + +} + +inline TableColGrpSaveStruct::TableColGrpSaveStruct() : + nColGrpSpan( 1 ), nColGrpWidth( 0 ), + bRelColGrpWidth( false ), eColGrpAdjust( SvxAdjust::End ), + eColGrpVertOri( text::VertOrientation::TOP ) +{} + +inline void TableColGrpSaveStruct::CloseColGroup( HTMLTable *pTable ) +{ + pTable->CloseColGroup( nColGrpSpan, nColGrpWidth, + bRelColGrpWidth, eColGrpAdjust, eColGrpVertOri ); +} + +void SwHTMLParser::BuildTableColGroup( HTMLTable *pCurTable, + bool bReadOptions ) +{ + // <COLGROUP> was read already if bReadOptions is set + + if( !IsParserWorking() && m_vPendingStack.empty() ) + return; + + HtmlTokenId nToken = HtmlTokenId::NONE; + bool bPending = false; + std::unique_ptr<TableColGrpSaveStruct> pSaveStruct; + + if( !m_vPendingStack.empty() ) + { + pSaveStruct.reset(static_cast<TableColGrpSaveStruct*>(m_vPendingStack.back().pData.release())); + + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + bPending = SvParserState::Error == eState && !m_vPendingStack.empty(); + + SaveState( nToken ); + } + else + { + + pSaveStruct.reset(new TableColGrpSaveStruct); + if( bReadOptions ) + { + const HTMLOptions& rColGrpOptions = GetOptions(); + for (size_t i = rColGrpOptions.size(); i; ) + { + const HTMLOption& rOption = rColGrpOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + InsertBookmark( rOption.GetString() ); + break; + case HtmlOptionId::SPAN: + pSaveStruct->nColGrpSpan = static_cast<sal_uInt16>(rOption.GetNumber()); + if (pSaveStruct->nColGrpSpan > 256) + { + SAL_INFO("sw.html", "ignoring huge SPAN " << pSaveStruct->nColGrpSpan); + pSaveStruct->nColGrpSpan = 1; + } + break; + case HtmlOptionId::WIDTH: + pSaveStruct->nColGrpWidth = static_cast<sal_uInt16>(rOption.GetNumber()); + pSaveStruct->bRelColGrpWidth = + (rOption.GetString().indexOf('*') != -1); + break; + case HtmlOptionId::ALIGN: + pSaveStruct->eColGrpAdjust = + rOption.GetEnum( aHTMLPAlignTable, pSaveStruct->eColGrpAdjust ); + break; + case HtmlOptionId::VALIGN: + pSaveStruct->eColGrpVertOri = + rOption.GetEnum( aHTMLTableVAlignTable, + pSaveStruct->eColGrpVertOri ); + break; + default: break; + } + } + } + // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning. + SaveState( HtmlTokenId::NONE ); + } + + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); // naechstes Token + + bool bDone = false; + while( (IsParserWorking() && !bDone) || bPending ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || + pCurTable->GetContext() || pCurTable->HasParentSection(), + "Where is the section?" ); + if( m_vPendingStack.empty() && m_bCallNextToken && + (pCurTable->GetContext() || pCurTable->HasParentSection()) ) + { + // Call NextToken directly (e.g. ignore the content of floating frames or applets) + NextToken( nToken ); + } + else switch( nToken ) + { + case HtmlTokenId::TABLE_ON: + if( !pCurTable->GetContext() ) + { + SkipToken(); + bDone = true; + } + + break; + case HtmlTokenId::COLGROUP_ON: + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TBODY_ON: + case HtmlTokenId::TABLEROW_ON: + case HtmlTokenId::TABLE_OFF: + SkipToken(); + [[fallthrough]]; + case HtmlTokenId::COLGROUP_OFF: + bDone = true; + break; + case HtmlTokenId::COL_ON: + { + sal_uInt16 nColSpan = 1; + sal_uInt16 nColWidth = pSaveStruct->nColGrpWidth; + bool bRelColWidth = pSaveStruct->bRelColGrpWidth; + SvxAdjust eColAdjust = pSaveStruct->eColGrpAdjust; + sal_Int16 eColVertOri = pSaveStruct->eColGrpVertOri; + + const HTMLOptions& rColOptions = GetOptions(); + for (size_t i = rColOptions.size(); i; ) + { + const HTMLOption& rOption = rColOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + InsertBookmark( rOption.GetString() ); + break; + case HtmlOptionId::SPAN: + nColSpan = static_cast<sal_uInt16>(rOption.GetNumber()); + if (nColSpan > 256) + { + SAL_INFO("sw.html", "ignoring huge SPAN " << nColSpan); + nColSpan = 1; + } + break; + case HtmlOptionId::WIDTH: + nColWidth = static_cast<sal_uInt16>(rOption.GetNumber()); + bRelColWidth = + (rOption.GetString().indexOf('*') != -1); + break; + case HtmlOptionId::ALIGN: + eColAdjust = rOption.GetEnum( aHTMLPAlignTable, eColAdjust ); + break; + case HtmlOptionId::VALIGN: + eColVertOri = + rOption.GetEnum( aHTMLTableVAlignTable, eColVertOri ); + break; + default: break; + } + } + pCurTable->InsertCol( nColSpan, nColWidth, bRelColWidth, + eColAdjust, eColVertOri ); + + // the attributes in <COLGRP> should be ignored, if there are <COL> elements + pSaveStruct->nColGrpSpan = 0; + } + break; + case HtmlTokenId::COL_OFF: + break; // Ignore + case HtmlTokenId::MULTICOL_ON: + // we can't add columned text frames here + break; + case HtmlTokenId::TEXTTOKEN: + if( (pCurTable->GetContext() || + !pCurTable->HasParentSection()) && + 1==aToken.getLength() && ' '==aToken[0] ) + break; + [[fallthrough]]; + default: + pCurTable->MakeParentContents(); + NextToken( nToken ); + } + + OSL_ENSURE( !bPending || m_vPendingStack.empty(), + "SwHTMLParser::BuildTableColGrp: There is a PendStack again" ); + bPending = false; + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending == GetStatus() ) + { + m_vPendingStack.emplace_back( HtmlTokenId::COL_ON ); + m_vPendingStack.back().pData = std::move(pSaveStruct); + } + else + { + pSaveStruct->CloseColGroup( pCurTable ); + } +} + +class CaptionSaveStruct : public SectionSaveStruct +{ + SwPosition aSavePos; + SwHTMLNumRuleInfo aNumRuleInfo; // valid numbering + +public: + + std::shared_ptr<HTMLAttrTable> xAttrTab; // attributes + + CaptionSaveStruct( SwHTMLParser& rParser, const SwPosition& rPos ) : + SectionSaveStruct( rParser ), aSavePos( rPos ), + xAttrTab(std::make_shared<HTMLAttrTable>()) + { + rParser.SaveAttrTab(xAttrTab); + + // The current numbering was remembered and just needs to be closed + aNumRuleInfo.Set( rParser.GetNumInfo() ); + rParser.GetNumInfo().Clear(); + } + + const SwPosition& GetPos() const { return aSavePos; } + + void RestoreAll( SwHTMLParser& rParser ) + { + // Recover the old stack + Restore( rParser ); + + // Recover the old attribute tables + rParser.RestoreAttrTab(xAttrTab); + + // Re-open the old numbering + rParser.GetNumInfo().Set( aNumRuleInfo ); + } +}; + +void SwHTMLParser::BuildTableCaption( HTMLTable *pCurTable ) +{ + // <CAPTION> was read already + + if( !IsParserWorking() && m_vPendingStack.empty() ) + return; + + HtmlTokenId nToken = HtmlTokenId::NONE; + std::unique_ptr<CaptionSaveStruct> xSaveStruct; + + if( !m_vPendingStack.empty() ) + { + xSaveStruct.reset(static_cast<CaptionSaveStruct*>(m_vPendingStack.back().pData.release())); + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + OSL_ENSURE( m_vPendingStack.empty(), "Where does a PendStack coming from?" ); + + SaveState( nToken ); + } + else + { + if (m_xTable->IsOverflowing()) + { + SaveState( HtmlTokenId::NONE ); + return; + } + + bool bTop = true; + const HTMLOptions& rHTMLOptions = GetOptions(); + for ( size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + if( HtmlOptionId::ALIGN == rOption.GetToken() ) + { + if (rOption.GetString().equalsIgnoreAsciiCase( + OOO_STRING_SVTOOLS_HTML_VA_bottom)) + { + bTop = false; + } + } + } + + // Remember old PaM position + xSaveStruct.reset(new CaptionSaveStruct(*this, *m_pPam->GetPoint())); + + // Add a text section in the icon section as a container for the header + // and set the PaM there + const SwStartNode *pStNd; + if (m_xTable.get() == pCurTable) + pStNd = InsertTempTableCaptionSection(); + else + pStNd = InsertTableSection( RES_POOLCOLL_TEXT ); + + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::CAPTION_ON)); + + // Table headers are always centered + NewAttr(m_xAttrTab, &m_xAttrTab->pAdjust, SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST)); + + HTMLAttrs &rAttrs = xCntxt->GetAttrs(); + rAttrs.push_back( m_xAttrTab->pAdjust ); + + PushContext(xCntxt); + + // Remember the start node of the section at the table + pCurTable->SetCaption( pStNd, bTop ); + + // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning. + SaveState( HtmlTokenId::NONE ); + } + + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); + + // </CAPTION> is needed according to DTD + bool bDone = false; + while( IsParserWorking() && !bDone ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + switch( nToken ) + { + case HtmlTokenId::TABLE_ON: + if( m_vPendingStack.empty() ) + { + xSaveStruct->m_xTable = m_xTable; + bool bHasToFly = xSaveStruct->m_xTable.get() != pCurTable; + BuildTable( pCurTable->GetTableAdjust( true ), + false, true, bHasToFly ); + } + else + { + BuildTable( SvxAdjust::End ); + } + if( SvParserState::Pending != GetStatus() ) + { + m_xTable = xSaveStruct->m_xTable; + } + break; + case HtmlTokenId::TABLE_OFF: + case HtmlTokenId::COLGROUP_ON: + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TBODY_ON: + case HtmlTokenId::TABLEROW_ON: + SkipToken(); + bDone = true; + break; + + case HtmlTokenId::CAPTION_OFF: + bDone = true; + break; + default: + if( !m_vPendingStack.empty() ) + { + m_vPendingStack.pop_back(); + OSL_ENSURE( m_vPendingStack.empty(), "Further it can't go!" ); + } + + if( IsParserWorking() ) + NextToken( nToken ); + break; + } + + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending==GetStatus() ) + { + m_vPendingStack.emplace_back( HtmlTokenId::CAPTION_ON ); + m_vPendingStack.back().pData = std::move(xSaveStruct); + return; + } + + // end all still open contexts + while( m_aContexts.size() > m_nContextStAttrMin+1 ) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + EndContext(xCntxt.get()); + } + + bool bLFStripped = StripTrailingLF() > 0; + + if (m_xTable.get() == pCurTable) + { + // On moving the caption later, the last paragraph isn't moved as well. + // That means, there has to be an empty paragraph at the end of the section + if( m_pPam->GetPoint()->nContent.GetIndex() || bLFStripped ) + AppendTextNode( AM_NOSPACE ); + } + else + { + // Strip LFs at the end of the paragraph + if( !m_pPam->GetPoint()->nContent.GetIndex() && !bLFStripped ) + StripTrailingPara(); + } + + // If there's an adjustment for the cell, we need to close it + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + if (xCntxt) + { + EndContext(xCntxt.get()); + xCntxt.reset(); + } + + SetAttr( false ); + + // Recover stack and attribute table + xSaveStruct->RestoreAll(*this); + + // Recover PaM + *m_pPam->GetPoint() = xSaveStruct->GetPos(); +} + +namespace { + +class TableSaveStruct : public SwPendingData +{ +public: + std::shared_ptr<HTMLTable> m_xCurrentTable; + + explicit TableSaveStruct(const std::shared_ptr<HTMLTable>& rCurTable) + : m_xCurrentTable(rCurTable) + { + } + + // Initiate creation of the table and put the table in a text frame if + // needed. If it returns true, we need to insert a paragraph. + void MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc ); +}; + +} + +void TableSaveStruct::MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc ) +{ + m_xCurrentTable->MakeTable(nullptr, nWidth); + + HTMLTableContext *pTCntxt = m_xCurrentTable->GetContext(); + OSL_ENSURE( pTCntxt, "Where is the table context" ); + + SwTableNode *pTableNd = pTCntxt->GetTableNode(); + OSL_ENSURE( pTableNd, "Where is the table node" ); + + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pTableNd ) + { + // If there's already a layout, the BoxFrames need to be regenerated at this table + + if( pTCntxt->GetFrameFormat() ) + { + pTCntxt->GetFrameFormat()->DelFrames(); + pTableNd->DelFrames(); + pTCntxt->GetFrameFormat()->MakeFrames(); + } + else + { + pTableNd->DelFrames(); + SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 ); + OSL_ENSURE( aIdx.GetIndex() <= pTCntxt->GetPos()->nNode.GetIndex(), + "unexpected node for table layout" ); + pTableNd->MakeOwnFrames(&aIdx); + } + } + + rPos = *pTCntxt->GetPos(); +} + +HTMLTableOptions::HTMLTableOptions( const HTMLOptions& rOptions, + SvxAdjust eParentAdjust ) : + nCols( 0 ), + nWidth( 0 ), nHeight( 0 ), + nCellPadding( USHRT_MAX ), nCellSpacing( USHRT_MAX ), + nBorder( USHRT_MAX ), + nHSpace( 0 ), nVSpace( 0 ), + eAdjust( eParentAdjust ), eVertOri( text::VertOrientation::CENTER ), + eFrame( HTMLTableFrame::Void ), eRules( HTMLTableRules::NONE ), + bPercentWidth( false ), + bTableAdjust( false ), + bBGColor( false ), + aBorderColor( COL_GRAY ) +{ + bool bBorderColor = false; + bool bHasFrame = false, bHasRules = false; + + for (size_t i = rOptions.size(); i; ) + { + const HTMLOption& rOption = rOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::COLS: + nCols = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::WIDTH: + nWidth = static_cast<sal_uInt16>(rOption.GetNumber()); + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + if( bPercentWidth && nWidth>100 ) + nWidth = 100; + break; + case HtmlOptionId::HEIGHT: + nHeight = static_cast<sal_uInt16>(rOption.GetNumber()); + if( rOption.GetString().indexOf('%') != -1 ) + nHeight = 0; // don't use % attributes + break; + case HtmlOptionId::CELLPADDING: + nCellPadding = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::CELLSPACING: + nCellSpacing = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::ALIGN: + { + if( rOption.GetEnum( eAdjust, aHTMLPAlignTable ) ) + { + bTableAdjust = true; + } + } + break; + case HtmlOptionId::VALIGN: + eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri ); + break; + case HtmlOptionId::BORDER: + // Handle BORDER and BORDER=BORDER like BORDER=1 + if (!rOption.GetString().isEmpty() && + !rOption.GetString().equalsIgnoreAsciiCase( + OOO_STRING_SVTOOLS_HTML_O_border)) + { + nBorder = static_cast<sal_uInt16>(rOption.GetNumber()); + } + else + nBorder = 1; + + if( !bHasFrame ) + eFrame = ( nBorder ? HTMLTableFrame::Box : HTMLTableFrame::Void ); + if( !bHasRules ) + eRules = ( nBorder ? HTMLTableRules::All : HTMLTableRules::NONE ); + break; + case HtmlOptionId::FRAME: + eFrame = rOption.GetTableFrame(); + bHasFrame = true; + break; + case HtmlOptionId::RULES: + eRules = rOption.GetTableRules(); + bHasRules = true; + break; + case HtmlOptionId::BGCOLOR: + // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape + // *really* not on other tags + if( !rOption.GetString().isEmpty() ) + { + rOption.GetColor( aBGColor ); + bBGColor = true; + } + break; + case HtmlOptionId::BACKGROUND: + aBGImage = rOption.GetString(); + break; + case HtmlOptionId::BORDERCOLOR: + rOption.GetColor( aBorderColor ); + bBorderColor = true; + break; + case HtmlOptionId::BORDERCOLORDARK: + if( !bBorderColor ) + rOption.GetColor( aBorderColor ); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + case HtmlOptionId::HSPACE: + nHSpace = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::VSPACE: + nVSpace = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + default: break; + } + } + + if( nCols && !nWidth ) + { + nWidth = 100; + bPercentWidth = true; + } + + // If BORDER=0 or no BORDER given, then there shouldn't be a border + if( 0==nBorder || USHRT_MAX==nBorder ) + { + eFrame = HTMLTableFrame::Void; + eRules = HTMLTableRules::NONE; + } +} + +namespace +{ + class IndexInRange + { + private: + SwNodeIndex maStart; + SwNodeIndex maEnd; + public: + explicit IndexInRange(const SwNodeIndex& rStart, const SwNodeIndex& rEnd) + : maStart(rStart) + , maEnd(rEnd) + { + } + bool operator()(const SwHTMLTextFootnote& rTextFootnote) const + { + const SwNodeIndex aTextIdx(rTextFootnote.pTextFootnote->GetTextNode()); + return aTextIdx >= maStart && aTextIdx <= maEnd; + } + }; +} + +void SwHTMLParser::ClearFootnotesMarksInRange(const SwNodeIndex& rMkNdIdx, const SwNodeIndex& rPtNdIdx) +{ + //similarly for footnotes + if (m_pFootEndNoteImpl) + { + m_pFootEndNoteImpl->aTextFootnotes.erase(std::remove_if(m_pFootEndNoteImpl->aTextFootnotes.begin(), + m_pFootEndNoteImpl->aTextFootnotes.end(), IndexInRange(rMkNdIdx, rPtNdIdx)), m_pFootEndNoteImpl->aTextFootnotes.end()); + if (m_pFootEndNoteImpl->aTextFootnotes.empty()) + { + m_pFootEndNoteImpl.reset(); + } + } + + //follow DelFlyInRange pattern here + assert(rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex()); + + SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc(); + + //ofz#9733 drop bookmarks in this range + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + pMarkAccess->deleteMarks(rMkNdIdx, SwNodeIndex(rPtNdIdx, 1), nullptr, nullptr, nullptr); + + SwFrameFormats& rTable = *pDoc->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) || + (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) && + ( rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx )) + { + if( rPtNdIdx != pAPos->nNode ) + { + // 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) + { + ClearFootnotesMarksInRange(*rContent.GetContentIdx(), + SwNodeIndex(*rContent.GetContentIdx()->GetNode().EndOfSectionNode())); + } + } + } + } +} + +void SwHTMLParser::DeleteSection(SwStartNode* pSttNd) +{ + //if section to be deleted contains a pending m_pMarquee, it will be deleted + //so clear m_pMarquee pointer if that's the case + SwFrameFormat* pObjectFormat = m_pMarquee ? ::FindFrameFormat(m_pMarquee) : nullptr; + FrameDeleteWatch aWatch(pObjectFormat); + + //similarly for footnotes + SwNodeIndex aSttIdx(*pSttNd), aEndIdx(*pSttNd->EndOfSectionNode()); + ClearFootnotesMarksInRange(aSttIdx, aEndIdx); + + m_xDoc->getIDocumentContentOperations().DeleteSection(pSttNd); + + if (pObjectFormat) + { + if (aWatch.WasDeleted()) + m_pMarquee = nullptr; + else + aWatch.EndListeningAll(); + } +} + +std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust, + bool bIsParentHead, + bool bHasParentSection, + bool bHasToFly) +{ + TableDepthGuard aGuard(*this); + if (aGuard.TooDeep()) + eState = SvParserState::Error; + + if (!IsParserWorking() && m_vPendingStack.empty()) + return std::shared_ptr<HTMLTable>(); + + ::comphelper::FlagRestorationGuard g(m_isInTableStructure, true); + HtmlTokenId nToken = HtmlTokenId::NONE; + bool bPending = false; + std::unique_ptr<TableSaveStruct> xSaveStruct; + + if( !m_vPendingStack.empty() ) + { + xSaveStruct.reset(static_cast<TableSaveStruct*>(m_vPendingStack.back().pData.release())); + + m_vPendingStack.pop_back(); + nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken(); + bPending = SvParserState::Error == eState && !m_vPendingStack.empty(); + + SaveState( nToken ); + } + else + { + m_xTable.reset(); + HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust); + + if (!aTableOptions.aId.isEmpty()) + InsertBookmark(aTableOptions.aId); + + std::shared_ptr<HTMLTable> xCurTable(std::make_shared<HTMLTable>(this, + bIsParentHead, + bHasParentSection, + bHasToFly, + aTableOptions)); + m_xTable = xCurTable; + + xSaveStruct.reset(new TableSaveStruct(xCurTable)); + + // Is pending on the first GetNextToken, needs to be re-read on each construction + SaveState( HtmlTokenId::NONE ); + } + + std::shared_ptr<HTMLTable> xCurTable = xSaveStruct->m_xCurrentTable; + + // </TABLE> is needed according to DTD + if( nToken == HtmlTokenId::NONE ) + nToken = GetNextToken(); + + bool bDone = false; + while( (IsParserWorking() && !bDone) || bPending ) + { + SaveState( nToken ); + + nToken = FilterToken( nToken ); + + OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || + xCurTable->GetContext() || xCurTable->HasParentSection(), + "Where is the section?" ); + if( m_vPendingStack.empty() && m_bCallNextToken && + (xCurTable->GetContext() || xCurTable->HasParentSection()) ) + { + /// Call NextToken directly (e.g. ignore the content of floating frames or applets) + NextToken( nToken ); + } + else switch( nToken ) + { + case HtmlTokenId::TABLE_ON: + if( !xCurTable->GetContext() ) + { + // If there's no table added, read the next table' + SkipToken(); + bDone = true; + } + + break; + case HtmlTokenId::TABLE_OFF: + bDone = true; + break; + case HtmlTokenId::CAPTION_ON: + BuildTableCaption(xCurTable.get()); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::COL_ON: + SkipToken(); + BuildTableColGroup(xCurTable.get(), false); + break; + case HtmlTokenId::COLGROUP_ON: + BuildTableColGroup(xCurTable.get(), true); + break; + case HtmlTokenId::TABLEROW_ON: + case HtmlTokenId::TABLEHEADER_ON: + case HtmlTokenId::TABLEDATA_ON: + SkipToken(); + BuildTableSection(xCurTable.get(), false, false); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::THEAD_ON: + case HtmlTokenId::TFOOT_ON: + case HtmlTokenId::TBODY_ON: + BuildTableSection(xCurTable.get(), true, HtmlTokenId::THEAD_ON==nToken); + bDone = m_xTable->IsOverflowing(); + break; + case HtmlTokenId::MULTICOL_ON: + // we can't add columned text frames here + break; + case HtmlTokenId::FORM_ON: + NewForm( false ); // don't add a new paragraph + break; + case HtmlTokenId::FORM_OFF: + EndForm( false ); // don't add a new paragraph + break; + case HtmlTokenId::TEXTTOKEN: + // blank strings may be a series of CR+LF and no text + if( (xCurTable->GetContext() || + !xCurTable->HasParentSection()) && + 1==aToken.getLength() && ' '==aToken[0] ) + break; + [[fallthrough]]; + default: + xCurTable->MakeParentContents(); + NextToken( nToken ); + break; + } + + OSL_ENSURE( !bPending || m_vPendingStack.empty(), + "SwHTMLParser::BuildTable: There is a PendStack again" ); + bPending = false; + if( IsParserWorking() ) + SaveState( HtmlTokenId::NONE ); + + if( !bDone ) + nToken = GetNextToken(); + } + + if( SvParserState::Pending == GetStatus() ) + { + m_vPendingStack.emplace_back( HtmlTokenId::TABLE_ON ); + m_vPendingStack.back().pData = std::move(xSaveStruct); + return std::shared_ptr<HTMLTable>(); + } + + HTMLTableContext *pTCntxt = xCurTable->GetContext(); + if( pTCntxt ) + { + + // Modify table structure + xCurTable->CloseTable(); + + // end contexts that began out of cells. Needs to exist before (!) we move the table, + // since the current one doesn't exist anymore afterwards + while( m_aContexts.size() > m_nContextStAttrMin ) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + if (!xCntxt) + break; + ClearContext(xCntxt.get()); + } + + m_nContextStMin = pTCntxt->GetContextStMin(); + m_nContextStAttrMin = pTCntxt->GetContextStAttrMin(); + + if (m_xTable == xCurTable) + { + // Set table caption + const SwStartNode *pCapStNd = m_xTable->GetCaptionStartNode(); + if( pCapStNd ) + { + // The last paragraph of the section is never part of the copy. + // That's why the section needs to contain at least two paragraphs + + if( pCapStNd->EndOfSectionIndex() - pCapStNd->GetIndex() > 2 ) + { + // Don't copy start node and the last paragraph + SwNodeRange aSrcRg( *pCapStNd, 1, + *pCapStNd->EndOfSectionNode(), -1 ); + + bool bTop = m_xTable->IsTopCaption(); + SwStartNode *pTableStNd = pTCntxt->GetTableNode(); + + OSL_ENSURE( pTableStNd, "Where is the table node" ); + OSL_ENSURE( pTableStNd==m_pPam->GetNode().FindTableNode(), + "Are we in the wrong table?" ); + + SwNode* pNd; + if( bTop ) + pNd = pTableStNd; + else + pNd = pTableStNd->EndOfSectionNode(); + SwNodeIndex aDstIdx( *pNd, bTop ? 0 : 1 ); + + m_xDoc->getIDocumentContentOperations().MoveNodeRange( aSrcRg, aDstIdx, + SwMoveFlags::DEFAULT ); + + // If the caption was added before the table, a page style on that table + // needs to be moved to the first paragraph of the header. + // Additionally, all remembered indices that point to the table node + // need to be moved + if( bTop ) + { + MovePageDescAttrs( pTableStNd, aSrcRg.aStart.GetIndex(), + false ); + } + } + + // The section isn't needed anymore + m_pPam->SetMark(); + m_pPam->DeleteMark(); + DeleteSection(const_cast<SwStartNode*>(pCapStNd)); + m_xTable->SetCaption( nullptr, false ); + } + + // Process SwTable + sal_uInt16 nBrowseWidth = static_cast<sal_uInt16>(GetCurrentBrowseWidth()); + xSaveStruct->MakeTable(nBrowseWidth, *m_pPam->GetPoint(), m_xDoc.get()); + } + + GetNumInfo().Set( pTCntxt->GetNumInfo() ); + pTCntxt->RestorePREListingXMP( *this ); + RestoreAttrTab(pTCntxt->xAttrTab); + + if (m_xTable == xCurTable) + { + // Set upper paragraph spacing + m_bUpperSpace = true; + SetTextCollAttrs(); + + SwTableNode* pTableNode = pTCntxt->GetTableNode(); + size_t nTableBoxSize = pTableNode ? pTableNode->GetTable().GetTabSortBoxes().size() : 0; + m_nParaCnt = m_nParaCnt - std::min(m_nParaCnt, nTableBoxSize); + + // Jump to a table if needed + if( JumpToMarks::Table == m_eJumpTo && m_xTable->GetSwTable() && + m_xTable->GetSwTable()->GetFrameFormat()->GetName() == m_sJmpMark ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + + // If the import was canceled, don't call Show again here since + // the SwViewShell was already deleted + // That's not enough. Even in the ACCEPTING_STATE, a Show mustn't be called + // because otherwise the parser's gonna be destroyed on the reschedule, + // if there's still a DataAvailable link coming. So: only in the WORKING state + if( !m_nParaCnt && SvParserState::Working == GetStatus() ) + Show(); + } + } + else if (m_xTable == xCurTable) + { + // There was no table read + + // We maybe need to delete a read caption + const SwStartNode *pCapStNd = xCurTable->GetCaptionStartNode(); + if( pCapStNd ) + { + m_pPam->SetMark(); + m_pPam->DeleteMark(); + DeleteSection(const_cast<SwStartNode*>(pCapStNd)); + xCurTable->SetCaption( nullptr, false ); + } + } + + if (m_xTable == xCurTable) + { + xSaveStruct->m_xCurrentTable.reset(); + m_xTable.reset(); + } + + std::shared_ptr<HTMLTable> xRetTable = xSaveStruct->m_xCurrentTable; + xSaveStruct.reset(); + + return xRetTable; +} + +bool HTMLTable::PendingDrawObjectsInPaM(SwPaM& rPam) const +{ + if (!m_pResizeDrawObjects) + return false; + + bool bRet = false; + + sal_uInt16 nCount = m_pResizeDrawObjects->size(); + for (sal_uInt16 i = 0; i < nCount && !bRet; ++i) + { + SdrObject *pObj = (*m_pResizeDrawObjects)[i]; + SwFrameFormat* pObjectFormat = ::FindFrameFormat(pObj); + if (!pObjectFormat) + continue; + const SwFormatAnchor& rAnch = pObjectFormat->GetAnchor(); + if (const SwPosition* pPos = rAnch.GetContentAnchor()) + { + SwNodeIndex aObjNodeIndex(pPos->nNode); + bRet = (aObjNodeIndex >= rPam.Start()->nNode && aObjNodeIndex <= rPam.End()->nNode); + } + } + + return bRet; +} + +bool SwHTMLParser::PendingObjectsInPaM(SwPaM& rPam) const +{ + bool bRet = false; + for (const auto& a : m_aTables) + { + bRet = a->PendingDrawObjectsInPaM(rPam); + if (bRet) + break; + const SwTable *pTable = a->GetSwTable(); + if (!pTable) + continue; + const SwTableNode* pTableNode = pTable->GetTableNode(); + if (!pTableNode) + continue; + SwNodeIndex aTableNodeIndex(*pTableNode); + bRet = (aTableNodeIndex >= rPam.Start()->nNode && aTableNodeIndex <= rPam.End()->nNode); + if (bRet) + break; + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/htmltabw.cxx b/sw/source/filter/html/htmltabw.cxx new file mode 100644 index 000000000..f7d5951ae --- /dev/null +++ b/sw/source/filter/html/htmltabw.cxx @@ -0,0 +1,1150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svtools/htmlout.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <svtools/HtmlWriter.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <fmtornt.hxx> +#include <frmfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtsrnd.hxx> +#include <frmatr.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <swrect.hxx> +#include <cellatr.hxx> +#include <poolfmt.hxx> +#include <swtable.hxx> +#include <htmltbl.hxx> +#include "htmlnum.hxx" +#include "wrthtml.hxx" +#include <wrtswtbl.hxx> +#ifdef DBG_UTIL +#include <viewsh.hxx> +#include <viewopt.hxx> +#endif +#include <rtl/strbuf.hxx> +#include <sal/types.h> +#include <osl/diagnose.h> + +#define MAX_DEPTH (3) + +using namespace ::com::sun::star; + +namespace { + +class SwHTMLWrtTable : public SwWriteTable +{ + static void Pixelize( sal_uInt16& rValue ); + void PixelizeBorders(); + + void OutTableCell( SwHTMLWriter& rWrt, const SwWriteTableCell *pCell, + bool bOutVAlign ) const; + + void OutTableCells( SwHTMLWriter& rWrt, + const SwWriteTableCells& rCells, + const SvxBrushItem *pBrushItem ) const; + + virtual bool ShouldExpandSub( const SwTableBox *pBox, + bool bExpandedBefore, sal_uInt16 nDepth ) const override; + + static bool HasTabBackground( const SwTableLine& rLine, + bool bTop, bool bBottom, bool bLeft, bool bRight ); + static bool HasTabBackground( const SwTableBox& rBox, + bool bTop, bool bBottom, bool bLeft, bool bRight ); + +public: + SwHTMLWrtTable( const SwTableLines& rLines, long nWidth, sal_uInt32 nBWidth, + bool bRel, sal_uInt16 nLeftSub, sal_uInt16 nRightSub, + sal_uInt16 nNumOfRowsToRepeat ); + explicit SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo ); + + void Write( SwHTMLWriter& rWrt, sal_Int16 eAlign=text::HoriOrientation::NONE, + bool bTHead=false, const SwFrameFormat *pFrameFormat=nullptr, + const OUString *pCaption=nullptr, bool bTopCaption=false, + sal_uInt16 nHSpace=0, sal_uInt16 nVSpace=0 ) const; +}; + +} + +SwHTMLWrtTable::SwHTMLWrtTable( const SwTableLines& rLines, long nWidth, + sal_uInt32 nBWidth, bool bRel, + sal_uInt16 nLSub, sal_uInt16 nRSub, + sal_uInt16 nNumOfRowsToRepeat ) + : SwWriteTable(nullptr, rLines, nWidth, nBWidth, bRel, MAX_DEPTH, nLSub, nRSub, nNumOfRowsToRepeat) +{ + PixelizeBorders(); +} + +SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo ) + : SwWriteTable(nullptr, pLayoutInfo) +{ + // Adjust some Twip values to pixel limits + if( m_bCollectBorderWidth ) + PixelizeBorders(); +} + +void SwHTMLWrtTable::Pixelize( sal_uInt16& rValue ) +{ + if( rValue && Application::GetDefaultDevice() ) + { + Size aSz( rValue, 0 ); + aSz = Application::GetDefaultDevice()->LogicToPixel( aSz, MapMode(MapUnit::MapTwip) ); + if( !aSz.Width() ) + aSz.setWidth( 1 ); + aSz = Application::GetDefaultDevice()->PixelToLogic( aSz, MapMode(MapUnit::MapTwip) ); + rValue = static_cast<sal_uInt16>(aSz.Width()); + } +} + +void SwHTMLWrtTable::PixelizeBorders() +{ + Pixelize( m_nBorder ); + Pixelize( m_nCellSpacing ); + Pixelize( m_nCellPadding ); +} + +bool SwHTMLWrtTable::HasTabBackground( const SwTableBox& rBox, + bool bTop, bool bBottom, bool bLeft, bool bRight ) +{ + OSL_ENSURE( bTop || bBottom || bLeft || bRight, + "HasTabBackground: cannot be called" ); + + bool bRet = false; + if( rBox.GetSttNd() ) + { + std::unique_ptr<SvxBrushItem> aBrushItem = + rBox.GetFrameFormat()->makeBackgroundBrushItem(); + + /// The table box has a background, if its background color is not "no fill"/ + /// "auto fill" or it has a background graphic. + bRet = aBrushItem && (aBrushItem->GetColor() != COL_TRANSPARENT || + !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic()); + } + else + { + const SwTableLines& rLines = rBox.GetTabLines(); + const SwTableLines::size_type nCount = rLines.size(); + bool bLeftRight = bLeft || bRight; + for( SwTableLines::size_type i=0; !bRet && i<nCount; ++i ) + { + bool bT = bTop && 0 == i; + bool bB = bBottom && nCount-1 == i; + if( bT || bB || bLeftRight ) + bRet = HasTabBackground( *rLines[i], bT, bB, bLeft, bRight); + } + } + + return bRet; +} + +bool SwHTMLWrtTable::HasTabBackground( const SwTableLine& rLine, + bool bTop, bool bBottom, bool bLeft, bool bRight ) +{ + OSL_ENSURE( bTop || bBottom || bLeft || bRight, + "HasTabBackground: cannot be called" ); + + std::unique_ptr<SvxBrushItem> aBrushItem = rLine.GetFrameFormat()->makeBackgroundBrushItem(); + /// The table line has a background, if its background color is not "no fill"/ + /// "auto fill" or it has a background graphic. + bool bRet = aBrushItem && (aBrushItem->GetColor() != COL_TRANSPARENT || + !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic()); + + if( !bRet ) + { + const SwTableBoxes& rBoxes = rLine.GetTabBoxes(); + const SwTableBoxes::size_type nCount = rBoxes.size(); + bool bTopBottom = bTop || bBottom; + for( SwTableBoxes::size_type i=0; !bRet && i<nCount; ++i ) + { + bool bL = bLeft && 0 == i; + bool bR = bRight && nCount-1 == i; + if( bTopBottom || bL || bR ) + bRet = HasTabBackground( *rBoxes[i], bTop, bBottom, bL, bR ); + } + } + + return bRet; +} + +static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders ); + +static bool lcl_TableBox_HasTabBorders( const SwTableBox* pBox, bool *pBorders ) +{ + if( *pBorders ) + return false; + + if( !pBox->GetSttNd() ) + { + for( const auto& rpLine : pBox->GetTabLines() ) + { + if ( lcl_TableLine_HasTabBorders( rpLine, pBorders ) ) + break; + } + } + else + { + const SvxBoxItem& rBoxItem = + pBox->GetFrameFormat()->GetFormatAttr( RES_BOX ); + + *pBorders = rBoxItem.GetTop() || rBoxItem.GetBottom() || + rBoxItem.GetLeft() || rBoxItem.GetRight(); + } + + return !*pBorders; +} + +static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders ) +{ + if( *pBorders ) + return false; + + for( const auto& rpBox : pLine->GetTabBoxes() ) + { + if ( lcl_TableBox_HasTabBorders( rpBox, pBorders ) ) + break; + } + return !*pBorders; +} + +bool SwHTMLWrtTable::ShouldExpandSub( const SwTableBox *pBox, + bool bExpandedBefore, + sal_uInt16 nDepth ) const +{ + bool bExpand = !pBox->GetSttNd() && nDepth>0; + if( bExpand && bExpandedBefore ) + { + // MIB 30.6.97: If a box was already expanded, another one is only + // expanded when it has a border. + bool bBorders = false; + lcl_TableBox_HasTabBorders( pBox, &bBorders ); + if( !bBorders ) + bBorders = HasTabBackground( *pBox, true, true, true, true ); + bExpand = bBorders; + } + + return bExpand; +} + +// Write a box as single cell +void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt, + const SwWriteTableCell *pCell, + bool bOutVAlign ) const +{ + const SwTableBox *pBox = pCell->GetBox(); + sal_uInt16 nRow = pCell->GetRow(); + sal_uInt16 nCol = pCell->GetCol(); + sal_uInt16 nRowSpan = pCell->GetRowSpan(); + sal_uInt16 nColSpan = pCell->GetColSpan(); + + if ( !nRowSpan ) + return; + + const SwStartNode* pSttNd = pBox->GetSttNd(); + bool bHead = false; + if( pSttNd ) + { + sal_uLong nNdPos = pSttNd->GetIndex()+1; + + // determine the type of cell (TD/TH) + SwNode* pNd; + while( !( pNd = rWrt.m_pDoc->GetNodes()[nNdPos])->IsEndNode() ) + { + if( pNd->IsTextNode() ) + { + // The only paragraphs relevant for the distinction are those + // where the style is one of the two table related styles + // or inherits from one of these. + const SwFormat *pFormat = &static_cast<SwTextNode*>(pNd)->GetAnyFormatColl(); + sal_uInt16 nPoolId = pFormat->GetPoolFormatId(); + while( !pFormat->IsDefault() && + RES_POOLCOLL_TABLE_HDLN!=nPoolId && + RES_POOLCOLL_TABLE!=nPoolId ) + { + pFormat = pFormat->DerivedFrom(); + nPoolId = pFormat->GetPoolFormatId(); + } + + if( !pFormat->IsDefault() ) + { + bHead = (RES_POOLCOLL_TABLE_HDLN==nPoolId); + break; + } + } + nNdPos++; + } + } + + rWrt.OutNewLine(); // <TH>/<TD> in new line + OStringBuffer sOut; + sOut.append('<'); + OString aTag(bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata); + sOut.append(rWrt.GetNamespace() + aTag); + + // output ROW- and COLSPAN + if( nRowSpan>1 ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_rowspan). + append("=\"").append(static_cast<sal_Int32>(nRowSpan)).append("\""); + } + if( nColSpan > 1 ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_colspan). + append("=\"").append(static_cast<sal_Int32>(nColSpan)).append("\""); + } + + long nWidth = 0; + bool bOutWidth = true; + sal_uInt32 nPercentWidth = SAL_MAX_UINT32; + + if( m_bLayoutExport ) + { + if( pCell->HasPercentWidthOpt() ) + { + nPercentWidth = pCell->GetWidthOpt(); + } + else + { + nWidth = pCell->GetWidthOpt(); + if( !nWidth ) + bOutWidth = false; + } + } + else + { + if( HasRelWidths() ) + nPercentWidth = GetPercentWidth(nCol, nColSpan); + else + nWidth = GetAbsWidth( nCol, nColSpan ); + } + + if (rWrt.mbReqIF) + // ReqIF implies strict XHTML: no width for <td>. + bOutWidth = false; + + long nHeight = pCell->GetHeight() > 0 + ? GetAbsHeight( pCell->GetHeight(), nRow, nRowSpan ) + : 0; + Size aPixelSz( nWidth, nHeight ); + + // output WIDTH (Argh: only for Netscape) + if( (aPixelSz.Width() || aPixelSz.Height()) && Application::GetDefaultDevice() ) + { + Size aOldSz( aPixelSz ); + aPixelSz = Application::GetDefaultDevice()->LogicToPixel( aPixelSz, + MapMode(MapUnit::MapTwip) ); + if( aOldSz.Width() && !aPixelSz.Width() ) + aPixelSz.setWidth( 1 ); + if( aOldSz.Height() && !aPixelSz.Height() ) + aPixelSz.setHeight( 1 ); + } + + // output WIDTH: from layout or calculated + if( bOutWidth ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width). + append("=\""); + if( nPercentWidth != SAL_MAX_UINT32 ) + { + sOut.append(static_cast<sal_Int32>(nPercentWidth)).append('%'); + } + else + { + sOut.append(static_cast<sal_Int32>(aPixelSz.Width())); + } + sOut.append("\""); + } + + if( nHeight ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_height) + .append("=\"").append(static_cast<sal_Int32>(aPixelSz.Height())).append("\""); + } + + const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet(); + const SfxPoolItem *pItem; + + // ALIGN is only outputted at the paragraphs from now on + + // output VALIGN + if( bOutVAlign ) + { + sal_Int16 eVertOri = pCell->GetVertOri(); + if( text::VertOrientation::TOP==eVertOri || text::VertOrientation::BOTTOM==eVertOri ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign) + .append("=\"").append(text::VertOrientation::TOP==eVertOri ? + OOO_STRING_SVTOOLS_HTML_VA_top : + OOO_STRING_SVTOOLS_HTML_VA_bottom) + .append("\""); + } + } + + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + rWrt.m_bTextAttr = false; + rWrt.m_bOutOpts = true; + const SvxBrushItem *pBrushItem = nullptr; + if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + { + pBrushItem = static_cast<const SvxBrushItem *>(pItem); + } + if( !pBrushItem ) + pBrushItem = pCell->GetBackground(); + + if( pBrushItem ) + { + // output background + if (!rWrt.mbReqIF) + // Avoid non-CSS version in the ReqIF case. + rWrt.OutBackground( pBrushItem, false ); + + if( rWrt.m_bCfgOutStyles ) + OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem ); + } + + rWrt.OutCSS1_TableCellBorderHack(*pBox->GetFrameFormat()); + + sal_uInt32 nNumFormat = 0; + double nValue = 0.0; + bool bNumFormat = false, bValue = false; + if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_FORMAT, false, &pItem ) ) + { + nNumFormat = static_cast<const SwTableBoxNumFormat *>(pItem)->GetValue(); + bNumFormat = true; + } + if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_VALUE, false, &pItem ) ) + { + nValue = static_cast<const SwTableBoxValue *>(pItem)->GetValue(); + bValue = true; + if( !bNumFormat ) + nNumFormat = pBox->GetFrameFormat()->GetTableBoxNumFormat().GetValue(); + } + + if( bNumFormat || bValue ) + { + sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValue, nValue, + nNumFormat, *rWrt.m_pDoc->GetNumberFormatter(), rWrt.m_eDestEnc, + &rWrt.m_aNonConvertableCharacters)); + } + sOut.append('>'); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + rWrt.m_bLFPossible = true; + + rWrt.IncIndentLevel(); // indent the content of <TD>...</TD> + + if( pSttNd ) + { + HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1, + pSttNd->EndOfSectionIndex() ); + rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() ); + } + else + { + sal_uInt16 nTWidth; + sal_uInt32 nBWidth; + sal_uInt16 nLSub, nRSub; + if( HasRelWidths() ) + { + nTWidth = 100; + nBWidth = GetRawWidth( nCol, nColSpan ); + nLSub = 0; + nRSub = 0; + } + else + { + nTWidth = GetAbsWidth( nCol, nColSpan ); + nBWidth = nTWidth; + nLSub = GetLeftSpace( nCol ); + nRSub = GetRightSpace( nCol, nColSpan ); + } + + SwHTMLWrtTable aTableWrt( pBox->GetTabLines(), nTWidth, + nBWidth, HasRelWidths(), nLSub, nRSub, /*nNumOfRowsToRepeat*/0 ); + aTableWrt.Write( rWrt ); + } + + rWrt.DecIndentLevel(); // indent the content of <TD>...</TD> + + if( rWrt.m_bLFPossible ) + rWrt.OutNewLine(); + aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false); + rWrt.m_bLFPossible = true; +} + +// output a line as lines +void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt, + const SwWriteTableCells& rCells, + const SvxBrushItem *pBrushItem ) const +{ + // If the line contains more the one cell and all cells have the same + // alignment, then output the VALIGN at the line instead of the cell. + sal_Int16 eRowVertOri = text::VertOrientation::NONE; + if( rCells.size() > 1 ) + { + for (SwWriteTableCells::size_type nCell = 0; nCell < rCells.size(); ++nCell) + { + sal_Int16 eCellVertOri = rCells[nCell]->GetVertOri(); + if( 0==nCell ) + { + eRowVertOri = eCellVertOri; + } + else if( eRowVertOri != eCellVertOri ) + { + eRowVertOri = text::VertOrientation::NONE; + break; + } + } + } + + rWrt.OutNewLine(); // <TR> in new line + rWrt.Strm().WriteChar( '<' ).WriteOString( rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow ); + if( pBrushItem ) + { + rWrt.OutBackground( pBrushItem, false ); + + rWrt.m_bTextAttr = false; + rWrt.m_bOutOpts = true; + if( rWrt.m_bCfgOutStyles ) + OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem ); + } + + if( text::VertOrientation::TOP==eRowVertOri || text::VertOrientation::BOTTOM==eRowVertOri ) + { + OStringBuffer sOut; + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign) + .append("=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom) + .append("\""); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + } + + rWrt.Strm().WriteChar( '>' ); + + rWrt.IncIndentLevel(); // indent content of <TR>...</TR> + + for (const auto &rpCell : rCells) + { + OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri); + } + + rWrt.DecIndentLevel(); // indent content of <TR>...</TR> + + rWrt.OutNewLine(); // </TR> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow, false ); +} + +void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign, + bool bTHead, const SwFrameFormat *pFrameFormat, + const OUString *pCaption, bool bTopCaption, + sal_uInt16 nHSpace, sal_uInt16 nVSpace ) const +{ + // determine value of RULES + bool bRowsHaveBorder = false; + bool bRowsHaveBorderOnly = true; + SwWriteTableRow *pRow = m_aRows[0].get(); + for( SwWriteTableRows::size_type nRow=1; nRow < m_aRows.size(); ++nRow ) + { + SwWriteTableRow *pNextRow = m_aRows[nRow].get(); + bool bBorder = ( pRow->bBottomBorder || pNextRow->bTopBorder ); + bRowsHaveBorder |= bBorder; + bRowsHaveBorderOnly &= bBorder; + + sal_uInt16 nBorder2 = pRow->bBottomBorder ? pRow->nBottomBorder : USHRT_MAX; + if( pNextRow->bTopBorder && pNextRow->nTopBorder < nBorder2 ) + nBorder2 = pNextRow->nTopBorder; + + pRow->bBottomBorder = bBorder; + pRow->nBottomBorder = nBorder2; + + pNextRow->bTopBorder = bBorder; + pNextRow->nTopBorder = nBorder2; + + pRow = pNextRow; + } + + bool bColsHaveBorder = false; + bool bColsHaveBorderOnly = true; + SwWriteTableCol *pCol = m_aCols[0].get(); + for( SwWriteTableCols::size_type nCol=1; nCol<m_aCols.size(); ++nCol ) + { + SwWriteTableCol *pNextCol = m_aCols[nCol].get(); + bool bBorder = ( pCol->bRightBorder || pNextCol->bLeftBorder ); + bColsHaveBorder |= bBorder; + bColsHaveBorderOnly &= bBorder; + pCol->bRightBorder = bBorder; + pNextCol->bLeftBorder = bBorder; + pCol = pNextCol; + } + + // close previous numbering, etc + rWrt.ChangeParaToken( HtmlTokenId::NONE ); + + if( rWrt.m_bLFPossible ) + rWrt.OutNewLine(); // <TABLE> in new line + OStringBuffer sOut; + sOut.append('<').append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table); + + const SvxFrameDirection nOldDirection = rWrt.m_nDirection; + if( pFrameFormat ) + rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() ); + if( rWrt.m_bOutFlyFrame || nOldDirection != rWrt.m_nDirection ) + { + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + rWrt.OutDirection( rWrt.m_nDirection ); + } + + // output ALIGN= + if( text::HoriOrientation::RIGHT == eAlign ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align). + append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_right).append("\""); + } + else if( text::HoriOrientation::CENTER == eAlign ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align). + append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_center).append("\""); + } + else if( text::HoriOrientation::LEFT == eAlign ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align). + append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_left).append("\""); + } + + // output WIDTH: from layout or calculated + if( m_nTabWidth ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width). + append("=\""); + if( HasRelWidths() ) + sOut.append(static_cast<sal_Int32>(m_nTabWidth)).append('%'); + else if( Application::GetDefaultDevice() ) + { + sal_Int32 nPixWidth = Application::GetDefaultDevice()->LogicToPixel( + Size(m_nTabWidth,0), MapMode(MapUnit::MapTwip) ).Width(); + if( !nPixWidth ) + nPixWidth = 1; + + sOut.append(nPixWidth); + } + else + { + OSL_ENSURE( Application::GetDefaultDevice(), "no Application-Window!?" ); + sOut.append("100%"); + } + sOut.append("\""); + } + + if( (nHSpace || nVSpace) && Application::GetDefaultDevice()) + { + Size aPixelSpc = + Application::GetDefaultDevice()->LogicToPixel( Size(nHSpace,nVSpace), + MapMode(MapUnit::MapTwip) ); + if( !aPixelSpc.Width() && nHSpace ) + aPixelSpc.setWidth( 1 ); + if( !aPixelSpc.Height() && nVSpace ) + aPixelSpc.setHeight( 1 ); + + if( aPixelSpc.Width() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_hspace). + append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Width())).append("\""); + } + + if( aPixelSpc.Height() ) + { + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_vspace). + append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Height())).append("\""); + } + } + + // output CELLPADDING: from layout or calculated + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_cellpadding). + append("=\"").append(static_cast<sal_Int32>(SwHTMLWriter::ToPixel(m_nCellPadding,false))).append("\""); + + // output CELLSPACING: from layout or calculated + sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_cellspacing). + append("=\"").append(static_cast<sal_Int32>(SwHTMLWriter::ToPixel(m_nCellSpacing,false))).append("\""); + + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + // output background + if( pFrameFormat ) + { + rWrt.OutBackground( pFrameFormat->GetAttrSet(), false ); + + if (rWrt.m_bCfgOutStyles) + rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat ); + } + + sOut.append('>'); + rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + rWrt.IncIndentLevel(); // indent content of table + + // output caption + if( pCaption && !pCaption->isEmpty() ) + { + rWrt.OutNewLine(); // <CAPTION> in new line + OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption); + sOutStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).append("=\"") + .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom) + .append("\""); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + sOutStr.getStr() ); + HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption, rWrt.m_eDestEnc, &rWrt.m_aNonConvertableCharacters ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption, false ); + } + + const SwWriteTableCols::size_type nCols = m_aCols.size(); + + // output <COLGRP>/<COL>: If exporting via layout only when during import + // some were there, otherwise always. + bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly); + if( m_bColTags ) + { + if( bColGroups ) + { + rWrt.OutNewLine(); // <COLGRP> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup ); + + rWrt.IncIndentLevel(); // indent content of <COLGRP> + } + + for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol ) + { + rWrt.OutNewLine(); // </COL> in new line + + const SwWriteTableCol *pColumn = m_aCols[nCol].get(); + + HtmlWriter html(rWrt.Strm(), rWrt.maNamespace); + html.start(OOO_STRING_SVTOOLS_HTML_col); + + sal_uInt32 nWidth; + bool bRel; + if( m_bLayoutExport ) + { + bRel = pColumn->HasRelWidthOpt(); + nWidth = pColumn->GetWidthOpt(); + } + else + { + bRel = HasRelWidths(); + nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1); + } + + if( bRel ) + html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(nWidth) + "*"); + else + html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth,false))); + html.end(); + + if( bColGroups && pColumn->bRightBorder && nCol<nCols-1 ) + { + rWrt.DecIndentLevel(); // indent content of <COLGRP> + rWrt.OutNewLine(); // </COLGRP> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup, + false ); + rWrt.OutNewLine(); // <COLGRP> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup ); + rWrt.IncIndentLevel(); // indent content of <COLGRP> + } + } + if( bColGroups ) + { + rWrt.DecIndentLevel(); // indent content of <COLGRP> + + rWrt.OutNewLine(); // </COLGRP> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup, + false ); + } + } + + // output the lines as table lines + + // Output <TBODY>? + bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly); + bool bTBody = bTSections; + + // If sections must be outputted, then a THEAD around the first line only + // can be outputted if there is a line below the cell. + if( bTHead && + (bTSections || bColGroups) && + m_nHeadEndRow<m_aRows.size()-1 && !m_aRows[m_nHeadEndRow]->bBottomBorder ) + bTHead = false; + + // Output <TBODY> only if <THEAD> is outputted. + bTSections |= bTHead; + + if( bTSections ) + { + rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line + OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag); + + rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA> + } + + for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow ) + { + const SwWriteTableRow *pRow2 = m_aRows[nRow].get(); + + OutTableCells( rWrt, pRow2->GetCells(), pRow2->GetBackground() ); + if( !m_nCellSpacing && nRow < m_aRows.size()-1 && pRow2->bBottomBorder && + pRow2->nBottomBorder > DEF_LINE_WIDTH_1 ) + { + for( auto nCnt = (pRow2->nBottomBorder / DEF_LINE_WIDTH_1) - 1; nCnt; --nCnt ) + { + rWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow ); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow, + false ); + } + } + if( ( (bTHead && nRow==m_nHeadEndRow) || + (bTBody && pRow2->bBottomBorder) ) && + nRow < m_aRows.size()-1 ) + { + rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA> + rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line + OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false); + rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line + + if( bTHead && nRow==m_nHeadEndRow ) + bTHead = false; + + aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag); + rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA> + } + } + + if( bTSections ) + { + rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA> + + rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line + OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false); + } + + rWrt.DecIndentLevel(); // indent content of <TABLE> + + rWrt.OutNewLine(); // </TABLE> in new line + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table, false ); + + rWrt.m_nDirection = nOldDirection; +} + +Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode, + const SwFrameFormat *pFlyFrameFormat, + const OUString *pCaption, bool bTopCaption ) +{ + + SwTable& rTable = rNode.GetTable(); + + SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + rHTMLWrt.m_bOutTable = true; + + // The horizontal alignment of the frame (if exists) has priority. + // NONE means that no horizontal alignment was outputted. + sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE; + css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE; + sal_uInt8 nFlyPercentWidth = 0; + long nFlyWidth = 0; + sal_uInt16 nFlyHSpace = 0; + sal_uInt16 nFlyVSpace = 0; + if( pFlyFrameFormat ) + { + eSurround = pFlyFrameFormat->GetSurround().GetSurround(); + const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize(); + nFlyPercentWidth = rFrameSize.GetWidthPercent(); + nFlyWidth = rFrameSize.GetSize().Width(); + + eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient(); + if( text::HoriOrientation::NONE == eFlyHoriOri ) + eFlyHoriOri = text::HoriOrientation::LEFT; + + const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace(); + nFlyHSpace = static_cast< sal_uInt16 >((rLRSpace.GetLeft() + rLRSpace.GetRight()) / 2); + + const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace(); + nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2; + } + + // maybe open a FORM + bool bPreserveForm = false; + if( !rHTMLWrt.m_bPreserveForm ) + { + rHTMLWrt.OutForm( true, &rNode ); + bPreserveForm = rHTMLWrt.mxFormComps.is(); + rHTMLWrt.m_bPreserveForm = bPreserveForm; + } + + SwFrameFormat *pFormat = rTable.GetFrameFormat(); + + const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize(); + long nWidth = rFrameSize.GetSize().Width(); + sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent(); + sal_uInt16 nBaseWidth = static_cast<sal_uInt16>(nWidth); + + sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient(); + + // text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths + sal_uInt16 nNewDefListLvl = 0; + bool bRelWidths = false; + bool bCheckDefList = false; + switch( eTabHoriOri ) + { + case text::HoriOrientation::FULL: + // Tables with automatic alignment become tables with 100% width. + bRelWidths = true; + nWidth = 100; + eTabHoriOri = text::HoriOrientation::LEFT; + break; + case text::HoriOrientation::NONE: + { + const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace(); + if( aLRItem.GetRight() ) + { + // The table width is defined on the basis of the left and + // right margin. Therefore we try to define the actual + // width of the table. If that's not possible we transform + // it to a table with width 100%. + nWidth = pFormat->FindLayoutRect(true).Width(); + if( !nWidth ) + { + bRelWidths = true; + nWidth = 100; + } + + } + else if( nPercentWidth ) + { + // Without a right border the %-width is maintained. + nWidth = nPercentWidth; + bRelWidths = true; + } + else + { + // Without a right margin also an absolute width is maintained. + // We still try to define the actual width via the layout. + long nRealWidth = pFormat->FindLayoutRect(true).Width(); + if( nRealWidth ) + nWidth = nRealWidth; + } + bCheckDefList = true; + } + break; + case text::HoriOrientation::LEFT_AND_WIDTH: + eTabHoriOri = text::HoriOrientation::LEFT; + bCheckDefList = true; + [[fallthrough]]; + default: + // In all other case it's possible to use directly an absolute + // or relative width. + if( nPercentWidth ) + { + bRelWidths = true; + nWidth = nPercentWidth; + } + break; + } + + if( bCheckDefList ) + { + OSL_ENSURE( !rHTMLWrt.GetNumInfo().GetNumRule() || + rHTMLWrt.GetNextNumInfo(), + "NumInfo for next paragraph is missing!" ); + const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace(); + if( aLRItem.GetLeft() > 0 && rHTMLWrt.m_nDefListMargin > 0 && + ( !rHTMLWrt.GetNumInfo().GetNumRule() || + ( rHTMLWrt.GetNextNumInfo() && + (rHTMLWrt.GetNextNumInfo()->IsRestart() || + rHTMLWrt.GetNumInfo().GetNumRule() != + rHTMLWrt.GetNextNumInfo()->GetNumRule()) ) ) ) + { + // If the paragraph before the table is not numbered or the + // paragraph after the table starts with a new numbering or with + // a different rule, we can maintain the indentation with a DL. + // Otherwise we keep the indentation of the numbering. + nNewDefListLvl = static_cast< sal_uInt16 >( + (aLRItem.GetLeft() + (rHTMLWrt.m_nDefListMargin/2)) / + rHTMLWrt.m_nDefListMargin ); + } + } + + if( !pFlyFrameFormat && nNewDefListLvl != rHTMLWrt.m_nDefListLvl ) + rHTMLWrt.OutAndSetDefList( nNewDefListLvl ); + + if( nNewDefListLvl ) + { + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd ); + } + + // eFlyHoriOri and eTabHoriOri now only contain the values of + // LEFT/CENTER and RIGHT! + if( eFlyHoriOri!=text::HoriOrientation::NONE ) + { + eTabHoriOri = eFlyHoriOri; + // MIB 4.7.97: If the table has a relative width, then the width is + // adjusted to the width of the frame, therefore we export its width. + // If fixed width, the table width is relevant. Whoever puts tables with + // relative width <100% into frames is to blame when the result looks bad. + if( bRelWidths ) + { + nWidth = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth; + bRelWidths = nFlyPercentWidth > 0; + } + } + + sal_Int16 eDivHoriOri = text::HoriOrientation::NONE; + switch( eTabHoriOri ) + { + case text::HoriOrientation::LEFT: + // If a left-aligned table has no right sided flow, then we don't need + // an ALIGN=LEFT in the table. + if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT ) + eTabHoriOri = text::HoriOrientation::NONE; + break; + case text::HoriOrientation::RIGHT: + // Something like that also applies to right-aligned tables, + // here we use a <DIV ALIGN=RIGHT> instead. + if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT ) + { + eDivHoriOri = text::HoriOrientation::RIGHT; + eTabHoriOri = text::HoriOrientation::NONE; + } + break; + case text::HoriOrientation::CENTER: + // Almost nobody understands ALIGN=CENTER, therefore we abstain + // from it and use a <CENTER>. + eDivHoriOri = text::HoriOrientation::CENTER; + eTabHoriOri = text::HoriOrientation::NONE; + break; + default: + ; + } + if( text::HoriOrientation::NONE==eTabHoriOri ) + nFlyHSpace = nFlyVSpace = 0; + + if( !pFormat->GetName().isEmpty() ) + rHTMLWrt.OutImplicitMark( pFormat->GetName(), "table" ); + + if( text::HoriOrientation::NONE!=eDivHoriOri ) + { + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); // <CENTER> in new line + if( text::HoriOrientation::CENTER==eDivHoriOri ) + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center ); + else + { + OString sOut = OOO_STRING_SVTOOLS_HTML_division + " " OOO_STRING_SVTOOLS_HTML_O_align "=\"" + OOO_STRING_SVTOOLS_HTML_AL_right "\""; + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + sOut.getStr() ); + } + rHTMLWrt.IncIndentLevel(); // indent content of <CENTER> + rHTMLWrt.m_bLFPossible = true; + } + + // If the table isn't in a frame, then you always can output a LF. + if( text::HoriOrientation::NONE==eTabHoriOri ) + rHTMLWrt.m_bLFPossible = true; + + const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout(); + +#ifdef DBG_UTIL + { + SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if ( pSh && pSh->GetViewOptions()->IsTest1() ) + pLayout = nullptr; + } +#endif + + if( pLayout && pLayout->IsExportable() ) + { + SwHTMLWrtTable aTableWrt( pLayout ); + aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0, + pFormat, pCaption, bTopCaption, + nFlyHSpace, nFlyVSpace ); + } + else + { + SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth, + nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() ); + aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0, + pFormat, pCaption, bTopCaption, + nFlyHSpace, nFlyVSpace ); + } + + // If the table wasn't in a frame, then you always can output a LF. + if( text::HoriOrientation::NONE==eTabHoriOri ) + rHTMLWrt.m_bLFPossible = true; + + if( text::HoriOrientation::NONE!=eDivHoriOri ) + { + rHTMLWrt.DecIndentLevel(); // indent content of <CENTER> + rHTMLWrt.OutNewLine(); // </CENTER> in new line + OString aTag = text::HoriOrientation::CENTER == eDivHoriOri + ? OOO_STRING_SVTOOLS_HTML_center + : OOO_STRING_SVTOOLS_HTML_division; + HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rHTMLWrt.GetNamespace() + aTag, false); + rHTMLWrt.m_bLFPossible = true; + } + + // move Pam behind the table + rHTMLWrt.m_pCurrentPam->GetPoint()->nNode = *rNode.EndOfSectionNode(); + + if( bPreserveForm ) + { + rHTMLWrt.m_bPreserveForm = false; + rHTMLWrt.OutForm( false ); + } + + rHTMLWrt.m_bOutTable = false; + + if( rHTMLWrt.GetNextNumInfo() && + !rHTMLWrt.GetNextNumInfo()->IsRestart() && + rHTMLWrt.GetNextNumInfo()->GetNumRule() == + rHTMLWrt.GetNumInfo().GetNumRule() ) + { + // If the paragraph after the table is numbered with the same rule as the + // one before, then the NumInfo of the next paragraph holds the level of + // paragraph before the table. Therefore NumInfo must be fetched again + // to maybe close the Num list. + rHTMLWrt.ClearNextNumInfo(); + rHTMLWrt.FillNextNumInfo(); + OutHTML_NumberBulletListEnd( rHTMLWrt, *rHTMLWrt.GetNextNumInfo() ); + } + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/parcss1.cxx b/sw/source/filter/html/parcss1.cxx new file mode 100644 index 000000000..852741052 --- /dev/null +++ b/sw/source/filter/html/parcss1.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 <osl/diagnose.h> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/color.hxx> +#include <tools/solar.h> +#include <svtools/htmltokn.h> +#include <comphelper/string.hxx> +#include "parcss1.hxx" + +// Loop-Check: Used to avoid infinite loops, is checked after every +// loop, if there is progress of the input position +#define LOOP_CHECK + +#ifdef LOOP_CHECK + +#define LOOP_CHECK_DECL \ + sal_Int32 nOldInPos = SAL_MAX_INT32; +#define LOOP_CHECK_RESTART \ + nOldInPos = SAL_MAX_INT32; +#define LOOP_CHECK_CHECK( where ) \ + OSL_ENSURE( nOldInPos!=m_nInPos || m_cNextCh==sal_Unicode(EOF), where ); \ + if( nOldInPos==m_nInPos && m_cNextCh!=sal_Unicode(EOF) ) \ + break; \ + else \ + nOldInPos = m_nInPos; + +#else + +#define LOOP_CHECK_DECL +#define LOOP_CHECK_RESTART +#define LOOP_CHECK_CHECK( where ) + +#endif + +const sal_Int32 MAX_LEN = 1024; + +void CSS1Parser::InitRead( const OUString& rIn ) +{ + m_nlLineNr = 0; + m_nlLinePos = 0; + + m_bWhiteSpace = true; // if nothing was read it's like there was WS + m_bEOF = false; + m_eState = CSS1_PAR_WORKING; + m_nValue = 0.; + + m_aIn = rIn; + m_nInPos = 0; + m_cNextCh = GetNextChar(); + m_nToken = GetNextToken(); +} + +sal_Unicode CSS1Parser::GetNextChar() +{ + if( m_nInPos >= m_aIn.getLength() ) + { + m_bEOF = true; + return sal_Unicode(EOF); + } + + sal_Unicode c = m_aIn[m_nInPos]; + m_nInPos++; + + if( c == '\n' ) + { + ++m_nlLineNr; + m_nlLinePos = 1; + } + else + ++m_nlLinePos; + + return c; +} + +// This function implements the scanner described in + +// http://www.w3.org/pub/WWW/TR/WD-css1.html +// resp. http://www.w3.org/pub/WWW/TR/WD-css1-960220.html + +// for CSS1. It's a direct implementation of the +// described Lex grammar. + +CSS1Token CSS1Parser::GetNextToken() +{ + CSS1Token nRet = CSS1_NULL; + m_aToken.clear(); + + do { + // remember if white space was read + bool bPrevWhiteSpace = m_bWhiteSpace; + m_bWhiteSpace = false; + + bool bNextCh = true; + switch( m_cNextCh ) + { + case '/': // COMMENT | '/' + { + m_cNextCh = GetNextChar(); + if( '*' == m_cNextCh ) + { + // COMMENT + m_cNextCh = GetNextChar(); + + bool bAsterisk = false; + while( !(bAsterisk && '/'==m_cNextCh) && !IsEOF() ) + { + bAsterisk = ('*'==m_cNextCh); + m_cNextCh = GetNextChar(); + } + } + else + { + // '/' + bNextCh = false; + nRet = CSS1_SLASH; + } + } + break; + + case '@': // '@import' | '@XXX' + { + m_cNextCh = GetNextChar(); + if (rtl::isAsciiAlpha(m_cNextCh)) + { + // scan the next identifier + OUStringBuffer sTmpBuffer(32); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( (rtl::isAsciiAlphanumeric(m_cNextCh) || + '-' == m_cNextCh) && !IsEOF() ); + + m_aToken += sTmpBuffer; + + // check if we know it + switch( m_aToken[0] ) + { + case 'i': + case 'I': + if( m_aToken.equalsIgnoreAsciiCase( "import" ) ) + nRet = CSS1_IMPORT_SYM; + break; + case 'p': + case 'P': + if( m_aToken.equalsIgnoreAsciiCase( "page" ) ) + nRet = CSS1_PAGE_SYM; + break; + } + + // error handling: ignore '@indent' and the rest until + // semicolon at end of the next block + if( CSS1_NULL==nRet ) + { + m_aToken.clear(); + int nBlockLvl = 0; + sal_Unicode cQuoteCh = 0; + bool bDone = false, bEscape = false; + while( !bDone && !IsEOF() ) + { + bool bOldEscape = bEscape; + bEscape = false; + switch( m_cNextCh ) + { + case '{': + if( !cQuoteCh && !bOldEscape ) + nBlockLvl++; + break; + case ';': + if( !cQuoteCh && !bOldEscape ) + bDone = nBlockLvl==0; + break; + case '}': + if( !cQuoteCh && !bOldEscape ) + bDone = --nBlockLvl==0; + break; + case '\"': + case '\'': + if( !bOldEscape ) + { + if( cQuoteCh ) + { + if( cQuoteCh == m_cNextCh ) + cQuoteCh = 0; + } + else + { + cQuoteCh = m_cNextCh; + } + } + break; + case '\\': + if( !bOldEscape ) + bEscape = true; + break; + } + m_cNextCh = GetNextChar(); + } + } + + bNextCh = false; + } + } + break; + + case '!': // '!' 'legal' | '!' 'important' | syntax error + { + // ignore white space + m_cNextCh = GetNextChar(); + while( ( ' ' == m_cNextCh || + (m_cNextCh >= 0x09 && m_cNextCh <= 0x0d) ) && !IsEOF() ) + { + m_bWhiteSpace = true; + m_cNextCh = GetNextChar(); + } + + if( 'i'==m_cNextCh || 'I'==m_cNextCh) + { + // scan next identifier + OUStringBuffer sTmpBuffer(32); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( (rtl::isAsciiAlphanumeric(m_cNextCh) || + '-' == m_cNextCh) && !IsEOF() ); + + m_aToken += sTmpBuffer; + + if( ( 'i'==m_aToken[0] || 'I'==m_aToken[0] ) && + m_aToken.equalsIgnoreAsciiCase( "important" ) ) + { + // '!' 'important' + nRet = CSS1_IMPORTANT_SYM; + } + else + { + // error handling: ignore '!', not IDENT + nRet = CSS1_IDENT; + } + + m_bWhiteSpace = false; + bNextCh = false; + } + else + { + // error handling: ignore '!' + bNextCh = false; + } + } + break; + + case '\"': + case '\'': // STRING + { + // \... isn't possible yet!!! + sal_Unicode cQuoteChar = m_cNextCh; + m_cNextCh = GetNextChar(); + + OUStringBuffer sTmpBuffer( MAX_LEN ); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( cQuoteChar != m_cNextCh && !IsEOF() ); + + m_aToken += sTmpBuffer; + + nRet = CSS1_STRING; + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': // NUMBER | PERCENTAGE | LENGTH + { + // save current position + std::size_t nInPosSave = m_nInPos; + sal_Unicode cNextChSave = m_cNextCh; + sal_uInt32 nlLineNrSave = m_nlLineNr; + sal_uInt32 nlLinePosSave = m_nlLinePos; + bool bEOFSave = m_bEOF; + + // first try to parse a hex digit + OUStringBuffer sTmpBuffer( 16 ); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( sTmpBuffer.getLength() < 7 && + ( ('0'<=m_cNextCh && '9'>=m_cNextCh) || + ('A'<=m_cNextCh && 'F'>=m_cNextCh) || + ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) && + !IsEOF() ); + + if( sTmpBuffer.getLength()==6 ) + { + // we found a color in hex + m_aToken += sTmpBuffer; + nRet = CSS1_HEXCOLOR; + bNextCh = false; + + break; + } + + // otherwise we try a number + m_nInPos = nInPosSave; + m_cNextCh = cNextChSave; + m_nlLineNr = nlLineNrSave; + m_nlLinePos = nlLinePosSave; + m_bEOF = bEOFSave; + + // first parse the number + sTmpBuffer.setLength( 0 ); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( (('0'<=m_cNextCh && '9'>=m_cNextCh) || '.'==m_cNextCh) && + !IsEOF() ); + + m_aToken += sTmpBuffer; + m_nValue = m_aToken.toDouble(); + + // ignore white space + while( ( ' ' == m_cNextCh || + (m_cNextCh >= 0x09 && m_cNextCh <= 0x0d) ) && !IsEOF() ) + { + m_bWhiteSpace = true; + m_cNextCh = GetNextChar(); + } + + // check now, of there is a unit + switch( m_cNextCh ) + { + case '%': // PERCENTAGE + m_bWhiteSpace = false; + nRet = CSS1_PERCENTAGE; + break; + + case 'c': + case 'C': // LENGTH cm | LENGTH IDENT + case 'e': + case 'E': // LENGTH (em | ex) | LENGTH IDENT + case 'i': + case 'I': // LENGTH inch | LENGTH IDENT + case 'p': + case 'P': // LENGTH (pt | px | pc) | LENGTH IDENT + case 'm': + case 'M': // LENGTH mm | LENGTH IDENT + { + // save current position + sal_Int32 nInPosOld = m_nInPos; + sal_Unicode cNextChOld = m_cNextCh; + sal_uLong nlLineNrOld = m_nlLineNr; + sal_uLong nlLinePosOld = m_nlLinePos; + bool bEOFOld = m_bEOF; + + // parse the next identifier + OUString aIdent; + OUStringBuffer sTmpBuffer2(64); + do { + sTmpBuffer2.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( (rtl::isAsciiAlphanumeric(m_cNextCh) || + '-' == m_cNextCh) && !IsEOF() ); + + aIdent += sTmpBuffer2; + + // Is it a unit? + const char *pCmp1 = nullptr, *pCmp2 = nullptr, *pCmp3 = nullptr; + double nScale1 = 1., nScale2 = 1.; + CSS1Token nToken1 = CSS1_LENGTH, + nToken2 = CSS1_LENGTH, + nToken3 = CSS1_LENGTH; + switch( aIdent[0] ) + { + case 'c': + case 'C': + pCmp1 = "cm"; + nScale1 = (72.*20.)/2.54; // twip + break; + case 'e': + case 'E': + pCmp1 = "em"; + nToken1 = CSS1_EMS; + + pCmp2 = "ex"; + nToken2 = CSS1_EMX; + break; + case 'i': + case 'I': + pCmp1 = "in"; + nScale1 = 72.*20.; // twip + break; + case 'm': + case 'M': + pCmp1 = "mm"; + nScale1 = (72.*20.)/25.4; // twip + break; + case 'p': + case 'P': + pCmp1 = "pt"; + nScale1 = 20.; // twip + + pCmp2 = "pc"; + nScale2 = 12.*20.; // twip + + pCmp3 = "px"; + nToken3 = CSS1_PIXLENGTH; + break; + } + + double nScale = 0.0; + OSL_ENSURE( pCmp1, "Where does the first digit come from?" ); + if( aIdent.equalsIgnoreAsciiCaseAscii( pCmp1 ) ) + { + nScale = nScale1; + nRet = nToken1; + } + else if( pCmp2 && + aIdent.equalsIgnoreAsciiCaseAscii( pCmp2 ) ) + { + nScale = nScale2; + nRet = nToken2; + } + else if( pCmp3 && + aIdent.equalsIgnoreAsciiCaseAscii( pCmp3 ) ) + { + nScale = 1.; // nScale3 + nRet = nToken3; + } + else + { + nRet = CSS1_NUMBER; + } + + if( CSS1_LENGTH==nRet && nScale!=1.0 ) + m_nValue *= nScale; + + if( nRet == CSS1_NUMBER ) + { + m_nInPos = nInPosOld; + m_cNextCh = cNextChOld; + m_nlLineNr = nlLineNrOld; + m_nlLinePos = nlLinePosOld; + m_bEOF = bEOFOld; + } + else + { + m_bWhiteSpace = false; + } + bNextCh = false; + } + break; + default: // NUMBER IDENT + bNextCh = false; + nRet = CSS1_NUMBER; + break; + } + } + break; + + case ':': // ':' + // catch link/visited/active !!! + nRet = CSS1_COLON; + break; + + case '.': // DOT_W_WS | DOT_WO_WS + nRet = bPrevWhiteSpace ? CSS1_DOT_W_WS : CSS1_DOT_WO_WS; + break; + + case '+': // '+' + nRet = CSS1_PLUS; + break; + + case '-': // '-' + nRet = CSS1_MINUS; + break; + + case '{': // '{' + nRet = CSS1_OBRACE; + break; + + case '}': // '}' + nRet = CSS1_CBRACE; + break; + + case ';': // ';' + nRet = CSS1_SEMICOLON; + break; + + case ',': // ',' + nRet = CSS1_COMMA; + break; + + case '#': // '#' + m_cNextCh = GetNextChar(); + if( ('0'<=m_cNextCh && '9'>=m_cNextCh) || + ('a'<=m_cNextCh && 'f'>=m_cNextCh) || + ('A'<=m_cNextCh && 'F'>=m_cNextCh) ) + { + // save current position + sal_Int32 nInPosSave = m_nInPos; + sal_Unicode cNextChSave = m_cNextCh; + sal_uLong nlLineNrSave = m_nlLineNr; + sal_uLong nlLinePosSave = m_nlLinePos; + bool bEOFSave = m_bEOF; + + // first try to parse a hex digit + OUStringBuffer sTmpBuffer(6); + do { + sTmpBuffer.append( m_cNextCh ); + m_cNextCh = GetNextChar(); + } while( sTmpBuffer.getLength() < 7 && + ( ('0'<=m_cNextCh && '9'>=m_cNextCh) || + ('A'<=m_cNextCh && 'F'>=m_cNextCh) || + ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) && + !IsEOF() ); + + if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 ) + { + // we found a color in hex + m_aToken += sTmpBuffer; + nRet = CSS1_HEXCOLOR; + bNextCh = false; + + break; + } + + // otherwise we try a number + m_nInPos = nInPosSave; + m_cNextCh = cNextChSave; + m_nlLineNr = nlLineNrSave; + m_nlLinePos = nlLinePosSave; + m_bEOF = bEOFSave; + } + + nRet = CSS1_HASH; + bNextCh = false; + break; + + case ' ': + case '\t': + case '\r': + case '\n': // White-Space + m_bWhiteSpace = true; + break; + + case sal_Unicode(EOF): + if( IsEOF() ) + { + m_eState = CSS1_PAR_ACCEPTED; + bNextCh = false; + break; + } + [[fallthrough]]; + + default: // IDENT | syntax error + if (rtl::isAsciiAlpha(m_cNextCh)) + { + // IDENT + + bool bHexColor = true; + + // parse the next identifier + OUStringBuffer sTmpBuffer(64); + do { + sTmpBuffer.append( m_cNextCh ); + if( bHexColor ) + { + bHexColor = sTmpBuffer.getLength()<7 && + ( ('0'<=m_cNextCh && '9'>=m_cNextCh) || + ('A'<=m_cNextCh && 'F'>=m_cNextCh) || + ('a'<=m_cNextCh && 'f'>=m_cNextCh) ); + } + m_cNextCh = GetNextChar(); + } while( (rtl::isAsciiAlphanumeric(m_cNextCh) || + '-' == m_cNextCh) && !IsEOF() ); + + m_aToken += sTmpBuffer; + + if( bHexColor && sTmpBuffer.getLength()==6 ) + { + bNextCh = false; + nRet = CSS1_HEXCOLOR; + + break; + } + if( '('==m_cNextCh && + ( (('u'==m_aToken[0] || 'U'==m_aToken[0]) && + m_aToken.equalsIgnoreAsciiCase( "url" )) || + (('r'==m_aToken[0] || 'R'==m_aToken[0]) && + m_aToken.equalsIgnoreAsciiCase( "rgb" )) ) ) + { + int nNestCnt = 0; + OUStringBuffer sTmpBuffer2(64); + do { + sTmpBuffer2.append( m_cNextCh ); + switch( m_cNextCh ) + { + case '(': nNestCnt++; break; + case ')': nNestCnt--; break; + } + m_cNextCh = GetNextChar(); + } while( (nNestCnt>1 || ')'!=m_cNextCh) && !IsEOF() ); + sTmpBuffer2.append( m_cNextCh ); + m_aToken += sTmpBuffer2; + bNextCh = true; + nRet = 'u'==m_aToken[0] || 'U'==m_aToken[0] + ? CSS1_URL + : CSS1_RGB; + } + else + { + bNextCh = false; + nRet = CSS1_IDENT; + } + } + // error handling: ignore digit + break; + } + if( bNextCh ) + m_cNextCh = GetNextChar(); + + } while( CSS1_NULL==nRet && IsParserWorking() ); + + return nRet; +} + +// These functions implement the parser described in + +// http://www.w3.org/pub/WWW/TR/WD-css1.html +// resp. http://www.w3.org/pub/WWW/TR/WD-css1-960220.html + +// for CSS1. It's a direct implementation of the +// described Lex grammar. + +// stylesheet +// : import* rule* + +// import +// : IMPORT_SYM url + +// url +// : STRING + +void CSS1Parser::ParseStyleSheet() +{ + LOOP_CHECK_DECL + + // import* + bool bDone = false; + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseStyleSheet()/import *" ) + + switch( m_nToken ) + { + case CSS1_IMPORT_SYM: + // IMPORT_SYM url + // URL are skipped without checks + m_nToken = GetNextToken(); + break; + case CSS1_IDENT: // Look-Aheads + case CSS1_DOT_W_WS: + case CSS1_HASH: + case CSS1_PAGE_SYM: + // rule + bDone = true; + break; + default: + // error handling: ignore + break; + } + + if( !bDone ) + m_nToken = GetNextToken(); + } + + LOOP_CHECK_RESTART + + // rule * + while( IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseStyleSheet()/rule *" ) + + switch( m_nToken ) + { + case CSS1_IDENT: // Look-Aheads + case CSS1_DOT_W_WS: + case CSS1_HASH: + case CSS1_PAGE_SYM: + // rule + ParseRule(); + break; + default: + // error handling: ignore + m_nToken = GetNextToken(); + break; + } + } +} + +// rule +// : selector [ ',' selector ]* +// '{' declaration [ ';' declaration ]* '}' + +void CSS1Parser::ParseRule() +{ + // selector + std::unique_ptr<CSS1Selector> pSelector = ParseSelector(); + if( !pSelector ) + return; + + // process selector + SelectorParsed( std::move(pSelector), true ); + + LOOP_CHECK_DECL + + // [ ',' selector ]* + while( CSS1_COMMA==m_nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseRule()/selector *" ) + + // ignore ',' + m_nToken = GetNextToken(); + + // selector + pSelector = ParseSelector(); + if( !pSelector ) + return; + + // process selector + SelectorParsed( std::move(pSelector), false ); + } + + // '{' + if( CSS1_OBRACE != m_nToken ) + return; + m_nToken = GetNextToken(); + + // declaration + OUString aProperty; + std::unique_ptr<CSS1Expression> pExpr = ParseDeclaration( aProperty ); + if( !pExpr ) + return; + + // process expression + DeclarationParsed( aProperty, std::move(pExpr) ); + + LOOP_CHECK_RESTART + + // [ ';' declaration ]* + while( CSS1_SEMICOLON==m_nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseRule()/declaration *" ) + + // ';' + m_nToken = GetNextToken(); + + // declaration + if( CSS1_IDENT == m_nToken ) + { + std::unique_ptr<CSS1Expression> pExp = ParseDeclaration( aProperty ); + if( pExp ) + { + // process expression + DeclarationParsed( aProperty, std::move(pExp)); + } + } + } + + // '}' + if( CSS1_CBRACE == m_nToken ) + m_nToken = GetNextToken(); +} + +// selector +// : simple_selector+ [ ':' pseudo_element ]? + +// simple_selector +// : element_name [ DOT_WO_WS class ]? +// | DOT_W_WS class +// | id_selector + +// element_name +// : IDENT + +// class +// : IDENT + +// id_selector +// : '#' IDENT + +// pseudo_element +// : IDENT + +std::unique_ptr<CSS1Selector> CSS1Parser::ParseSelector() +{ + std::unique_ptr<CSS1Selector> pRoot; + CSS1Selector *pLast = nullptr; + + bool bDone = false; + CSS1Selector *pNew = nullptr; + + LOOP_CHECK_DECL + + // simple_selector+ + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseSelector()" ) + + bool bNextToken = true; + + switch( m_nToken ) + { + case CSS1_IDENT: + { + // element_name [ DOT_WO_WS class ]? + + // element_name + OUString aElement = m_aToken; + CSS1SelectorType eType = CSS1_SELTYPE_ELEMENT; + m_nToken = GetNextToken(); + + if( CSS1_DOT_WO_WS == m_nToken ) + { + // DOT_WO_WS + m_nToken = GetNextToken(); + + // class + if( CSS1_IDENT == m_nToken ) + { + aElement += "." + m_aToken; + eType = CSS1_SELTYPE_ELEM_CLASS; + } + else + { + // missing class + return pRoot; + } + } + else + { + // that was a look-ahead + bNextToken = false; + } + pNew = new CSS1Selector( eType, aElement ); + } + break; + case CSS1_DOT_W_WS: + // DOT_W_WS class + + // DOT_W_WS + m_nToken = GetNextToken(); + + if( CSS1_IDENT==m_nToken ) + { + // class + pNew = new CSS1Selector( CSS1_SELTYPE_CLASS, m_aToken ); + } + else + { + // missing class + return pRoot; + } + break; + case CSS1_HASH: + // '#' id_selector + + // '#' + m_nToken = GetNextToken(); + + if( CSS1_IDENT==m_nToken ) + { + // id_selector + pNew = new CSS1Selector( CSS1_SELTYPE_ID, m_aToken ); + } + else + { + // missing id_selector + return pRoot; + } + break; + + case CSS1_PAGE_SYM: + { + // @page + pNew = new CSS1Selector( CSS1_SELTYPE_PAGE, m_aToken ); + } + break; + + default: + // stop because we don't know what's next + bDone = true; + break; + } + + // if created a new selector then save it + if( pNew ) + { + OSL_ENSURE( (pRoot!=nullptr) == (pLast!=nullptr), + "Root-Selector, but no Last" ); + if( pLast ) + pLast->SetNext( pNew ); + else + pRoot.reset(pNew); + + pLast = pNew; + pNew = nullptr; + } + + if( bNextToken && !bDone ) + m_nToken = GetNextToken(); + } + + if( !pRoot ) + { + // missing simple_selector + return pRoot; + } + + // [ ':' pseudo_element ]? + if( CSS1_COLON==m_nToken && IsParserWorking() ) + { + // ':' pseudo element + m_nToken = GetNextToken(); + if( CSS1_IDENT==m_nToken ) + { + if (pLast) + pLast->SetNext( new CSS1Selector(CSS1_SELTYPE_PSEUDO,m_aToken) ); + m_nToken = GetNextToken(); + } + else + { + // missing pseudo_element + return pRoot; + } + } + + return pRoot; +} + +// declaration +// : property ':' expr prio? +// | /* empty */ + +// expression +// : term [ operator term ]* + +// term +// : unary_operator? +// [ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS | IDENT | +// HEXCOLOR | URL | RGB ] + +// operator +// : '/' | ',' | /* empty */ + +// unary_operator +// : '-' | '+' + +// property +// : ident + +// the sign is only used for numeric values (except PERCENTAGE) +// and it's applied on nValue! +std::unique_ptr<CSS1Expression> CSS1Parser::ParseDeclaration( OUString& rProperty ) +{ + std::unique_ptr<CSS1Expression> pRoot; + CSS1Expression *pLast = nullptr; + + // property + if( CSS1_IDENT != m_nToken ) + { + // missing property + return pRoot; + } + rProperty = m_aToken; + + m_nToken = GetNextToken(); + + // ':' + if( CSS1_COLON != m_nToken ) + { + // missing ':' + return pRoot; + } + m_nToken = GetNextToken(); + + // term [operator term]* + // here we're pretty lax regarding the syntax, but this shouldn't + // be a problem + bool bDone = false; + sal_Unicode cSign = 0, cOp = 0; + CSS1Expression *pNew = nullptr; + + LOOP_CHECK_DECL + + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseDeclaration()" ) + + switch( m_nToken ) + { + case CSS1_MINUS: + cSign = '-'; + break; + + case CSS1_PLUS: + cSign = '+'; + break; + + case CSS1_NUMBER: + case CSS1_LENGTH: + case CSS1_PIXLENGTH: + case CSS1_EMS: + case CSS1_EMX: + if( '-'==cSign ) + m_nValue = -m_nValue; + [[fallthrough]]; + case CSS1_STRING: + case CSS1_PERCENTAGE: + case CSS1_IDENT: + case CSS1_URL: + case CSS1_RGB: + case CSS1_HEXCOLOR: + pNew = new CSS1Expression( m_nToken, m_aToken, m_nValue, cOp ); + m_nValue = 0; // otherwise this also is applied to next ident + cSign = 0; + cOp = 0; + break; + + case CSS1_SLASH: + cOp = '/'; + cSign = 0; + break; + + case CSS1_COMMA: + cOp = ','; + cSign = 0; + break; + + default: + bDone = true; + break; + } + + // if created a new expression save it + if( pNew ) + { + OSL_ENSURE( (pRoot!=nullptr) == (pLast!=nullptr), + "Root-Selector, but no Last" ); + if( pLast ) + pLast->SetNext( pNew ); + else + pRoot.reset(pNew); + + pLast = pNew; + pNew = nullptr; + } + + if( !bDone ) + m_nToken = GetNextToken(); + } + + if( !pRoot ) + { + // missing term + return pRoot; + } + + // prio? + if( CSS1_IMPORTANT_SYM==m_nToken ) + { + // IMPORTANT_SYM + m_nToken = GetNextToken(); + } + + return pRoot; +} + +CSS1Parser::CSS1Parser() + : m_bWhiteSpace(false) + , m_bEOF(false) + , m_cNextCh(0) + , m_nInPos(0) + , m_nlLineNr(0) + , m_nlLinePos(0) + , m_nValue(0) + , m_eState(CSS1_PAR_ACCEPTED) + , m_nToken(CSS1_NULL) +{ +} + +CSS1Parser::~CSS1Parser() +{ +} + +void CSS1Parser::ParseStyleSheet( const OUString& rIn ) +{ + OUString aTmp( rIn ); + + sal_Unicode c; + while( !aTmp.isEmpty() && + ( ' '==(c=aTmp[0]) || '\t'==c || '\r'==c || '\n'==c ) ) + aTmp = aTmp.copy( 1 ); + + while( !aTmp.isEmpty() && ( ' '==(c=aTmp[aTmp.getLength()-1]) + || '\t'==c || '\r'==c || '\n'==c ) ) + aTmp = aTmp.copy( 0, aTmp.getLength()-1 ); + + // remove SGML comments + if( aTmp.getLength() >= 4 && + aTmp.startsWith( "<!--" ) ) + aTmp = aTmp.copy( 4 ); + + if( aTmp.getLength() >=3 && + aTmp.endsWith("-->") ) + aTmp = aTmp.copy( 0, aTmp.getLength() - 3 ); + + if( aTmp.isEmpty() ) + return; + + InitRead( aTmp ); + + ParseStyleSheet(); +} + +void CSS1Parser::ParseStyleOption( const OUString& rIn ) +{ + if( rIn.isEmpty() ) + return; + + InitRead( rIn ); + + // fdo#41796: skip over spurious semicolons + while (CSS1_SEMICOLON == m_nToken) + { + m_nToken = GetNextToken(); + } + + OUString aProperty; + std::unique_ptr<CSS1Expression> pExpr = ParseDeclaration( aProperty ); + if( !pExpr ) + return; + + // process expression + DeclarationParsed( aProperty, std::move(pExpr) ); + + LOOP_CHECK_DECL + + // [ ';' declaration ]* + while( CSS1_SEMICOLON==m_nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Infinite loop in ParseStyleOption()" ) + + m_nToken = GetNextToken(); + if( CSS1_IDENT==m_nToken ) + { + std::unique_ptr<CSS1Expression> pExp = ParseDeclaration( aProperty ); + if( pExp ) + { + // process expression + DeclarationParsed( aProperty, std::move(pExp) ); + } + } + } +} + +void CSS1Parser::SelectorParsed( std::unique_ptr<CSS1Selector> /* pSelector */, bool /*bFirst*/ ) +{ +} + +void CSS1Parser::DeclarationParsed( const OUString& /*rProperty*/, + std::unique_ptr<CSS1Expression> /* pExpr */ ) +{ +} + +CSS1Selector::~CSS1Selector() +{ + delete pNext; +} + +CSS1Expression::~CSS1Expression() +{ + delete pNext; +} + +void CSS1Expression::GetURL( OUString& rURL ) const +{ + OSL_ENSURE( CSS1_URL==eType, "CSS1-Expression is not URL" ); + + OSL_ENSURE( aValue.startsWithIgnoreAsciiCase( "url" ) && + aValue.getLength() > 5 && + '(' == aValue[3] && + ')' == aValue[aValue.getLength()-1], + "no valid URL(...)" ); + + if( aValue.getLength() > 5 ) + { + rURL = aValue.copy( 4, aValue.getLength() - 5 ); + + // tdf#94088 original stripped only spaces, but there may also be + // double quotes in CSS style URLs, so be prepared to spaces followed + // by a single quote followed by spaces + const sal_Unicode aSpace(' '); + const sal_Unicode aSingleQuote('\''); + + rURL = comphelper::string::strip(rURL, aSpace); + rURL = comphelper::string::strip(rURL, aSingleQuote); + rURL = comphelper::string::strip(rURL, aSpace); + } +} + +bool CSS1Expression::GetColor( Color &rColor ) const +{ + OSL_ENSURE( CSS1_IDENT==eType || CSS1_RGB==eType || + CSS1_HEXCOLOR==eType || CSS1_STRING==eType, + "CSS1-Expression cannot be colour" ); + + bool bRet = false; + sal_uInt32 nColor = SAL_MAX_UINT32; + + switch( eType ) + { + case CSS1_RGB: + { + sal_uInt8 aColors[3] = { 0, 0, 0 }; + + if (!aValue.startsWithIgnoreAsciiCase( "rgb" ) || aValue.getLength() < 6 || + aValue[3] != '(' || aValue[aValue.getLength()-1] != ')') + { + break; + } + + sal_Int32 nPos = 4; // start after "rgb(" + for ( int nCol = 0; nCol < 3 && nPos > 0; ++nCol ) + { + const OUString aNumber = aValue.getToken(0, ',', nPos); + + sal_Int32 nNumber = aNumber.toInt32(); + if( nNumber<0 ) + { + nNumber = 0; + } + else if( aNumber.indexOf('%') >= 0 ) + { + if( nNumber > 100 ) + nNumber = 100; + nNumber *= 255; + nNumber /= 100; + } + else if( nNumber > 255 ) + nNumber = 255; + + aColors[nCol] = static_cast<sal_uInt8>(nNumber); + } + + rColor.SetRed( aColors[0] ); + rColor.SetGreen( aColors[1] ); + rColor.SetBlue( aColors[2] ); + + bRet = true; // something different than a colour isn't possible + } + break; + + case CSS1_IDENT: + case CSS1_STRING: + { + OUString aTmp( aValue.toAsciiUpperCase() ); + nColor = GetHTMLColor( aTmp ); + bRet = nColor != SAL_MAX_UINT32; + } + if( bRet || CSS1_STRING != eType || aValue.isEmpty() || + aValue[0] != '#' ) + break; + [[fallthrough]]; + case CSS1_HEXCOLOR: + { + // MS-IE hack: colour can also be a string + sal_Int32 nOffset = CSS1_STRING==eType ? 1 : 0; + bool bDouble = aValue.getLength()-nOffset == 3; + sal_Int32 i = nOffset, nEnd = (bDouble ? 3 : 6) + nOffset; + + nColor = 0; + for( ; i<nEnd; i++ ) + { + sal_Unicode c = (i<aValue.getLength() ? aValue[i] + : '0' ); + if( c >= '0' && c <= '9' ) + c -= 48; + else if( c >= 'A' && c <= 'F' ) + c -= 55; + else if( c >= 'a' && c <= 'f' ) + c -= 87; + else + c = 16; + + nColor *= 16; + if( c<16 ) + nColor += c; + if( bDouble ) + { + nColor *= 16; + if( c<16 ) + nColor += c; + } + } + bRet = true; + } + break; + default: + ; + } + + if( bRet && nColor!=SAL_MAX_UINT32 ) + { + rColor.SetRed( static_cast<sal_uInt8>((nColor & 0x00ff0000UL) >> 16) ); + rColor.SetGreen( static_cast<sal_uInt8>((nColor & 0x0000ff00UL) >> 8) ); + rColor.SetBlue( static_cast<sal_uInt8>(nColor & 0x000000ffUL) ); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/parcss1.hxx b/sw/source/filter/html/parcss1.hxx new file mode 100644 index 000000000..6e2d50426 --- /dev/null +++ b/sw/source/filter/html/parcss1.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_HTML_PARCSS1_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_PARCSS1_HXX + +#include <rtl/ustring.hxx> +#include <tools/color.hxx> + +#include <memory> + +// tokens of the CSS1 parser +enum CSS1Token +{ + CSS1_NULL, + + CSS1_IDENT, + CSS1_STRING, + CSS1_NUMBER, + CSS1_PERCENTAGE, + CSS1_LENGTH, // absolute length in 1/100 MM + CSS1_PIXLENGTH, // length in pixels + CSS1_EMS, + CSS1_EMX, + CSS1_HEXCOLOR, + + CSS1_DOT_W_WS, + CSS1_DOT_WO_WS, + CSS1_COLON, + CSS1_SLASH, + CSS1_PLUS, + CSS1_MINUS, + CSS1_OBRACE, + CSS1_CBRACE, + CSS1_SEMICOLON, + CSS1_COMMA, + CSS1_HASH, + + CSS1_IMPORT_SYM, + CSS1_PAGE_SYM, // Feature: PrintExt + + CSS1_IMPORTANT_SYM, + + CSS1_URL, + CSS1_RGB +}; + +enum CSS1ParserState +{ + CSS1_PAR_ACCEPTED = 0, + CSS1_PAR_WORKING +}; + +enum CSS1SelectorType +{ + CSS1_SELTYPE_ELEMENT, + CSS1_SELTYPE_ELEM_CLASS, + CSS1_SELTYPE_CLASS, + CSS1_SELTYPE_ID, + CSS1_SELTYPE_PSEUDO, + CSS1_SELTYPE_PAGE // Feature: PrintExt +}; + +/** A simple selector + * + * This class represents a simple selector, e.g. + * - a HTML element name + * - a HTML element name with a class (separated by a dot) + * - a class (without a dot) + * - an ID (set with ID=xxx) + * - a pseudo element + * + * These simple selectors are chained in a list to complete selectors + */ +class CSS1Selector +{ + CSS1SelectorType eType; // the type + OUString aSelector; // the selector itself + CSS1Selector *pNext; // the following component + +public: + CSS1Selector( CSS1SelectorType eTyp, const OUString &rSel ) + : eType(eTyp), aSelector( rSel ), pNext( nullptr ) + {} + + ~CSS1Selector(); + + CSS1SelectorType GetType() const { return eType; } + const OUString& GetString() const { return aSelector; } + + void SetNext( CSS1Selector *pNxt ) { pNext = pNxt; } + const CSS1Selector *GetNext() const { return pNext; } +}; + +/** a subexpression of a CSS1 declaration + * + * It contains + * - the type of this expression (= token) + * - the value as string (and/or double, with algebraic sign for NUMBER and LENGTH) + * - the operator with that it is connected with the *predecessor* expression + */ +struct CSS1Expression +{ + sal_Unicode cOp; // type of the link with its predecessor + CSS1Token eType; // type of the expression + OUString aValue; // value as string + double nValue; // value as number (TWIPs for LENGTH) + CSS1Expression *pNext; // the following component + +public: + CSS1Expression( CSS1Token eTyp, const OUString &rVal, + double nVal, sal_Unicode cO = 0 ) + : cOp(cO), eType(eTyp), aValue(rVal), nValue(nVal), pNext(nullptr) + {} + + ~CSS1Expression(); + + inline void Set( CSS1Token eTyp, const OUString &rVal, double nVal ); + + CSS1Token GetType() const { return eType; } + const OUString& GetString() const { return aValue; } + double GetNumber() const { return nValue; } + inline sal_uInt32 GetULength() const; + inline sal_Int32 GetSLength() const; + sal_Unicode GetOp() const { return cOp; } + + void GetURL( OUString& rURL ) const; + bool GetColor( Color &rRGB ) const; + + void SetNext( CSS1Expression *pNxt ) { pNext = pNxt; } + const CSS1Expression *GetNext() const { return pNext; } +}; + +inline void CSS1Expression::Set( CSS1Token eTyp, const OUString &rVal, + double nVal ) +{ + cOp = 0; eType = eTyp; aValue = rVal; nValue = nVal; pNext = nullptr; +} + +inline sal_uInt32 CSS1Expression::GetULength() const +{ + return nValue < 0. ? 0UL : static_cast<sal_uInt32>(nValue + .5); +} + +inline sal_Int32 CSS1Expression::GetSLength() const +{ + return static_cast<sal_Int32>(nValue + (nValue < 0. ? -.5 : .5 )); +} + +/** Parser of a style element/option + * + * This class parses the content of a style element or a style option and preprocesses it. + * + * The result of the parser is forwarded to derived parsers by the methods SelectorParsed() + * and DeclarationParsed(). Example: + * H1, H2 { font-weight: bold; text-align: right } + * | | | | + * | | | DeclP( 'text-align', 'right' ) + * | | DeclP( 'font-weight', 'bold' ) + * | SelP( 'H2', false ) + * SelP( 'H1', true ) + */ +class CSS1Parser +{ + bool m_bWhiteSpace : 1; // read a whitespace? + bool m_bEOF : 1; // is end of "file"? + + sal_Unicode m_cNextCh; // next character + + sal_Int32 m_nInPos; // current position in the input string + + sal_uInt32 m_nlLineNr; // current row number + sal_uInt32 m_nlLinePos; // current column number + + double m_nValue; // value of the token as number + + CSS1ParserState m_eState; // current state of the parser + CSS1Token m_nToken; // the current token + + OUString m_aIn; // the string to parse + OUString m_aToken; // token as string + + /// prepare parsing + void InitRead( const OUString& rIn ); + + /// @returns the next character to parse + sal_Unicode GetNextChar(); + + /// @returns the next token to parse + CSS1Token GetNextToken(); + + /// Is the parser still working? + bool IsParserWorking() const { return CSS1_PAR_WORKING == m_eState; } + + bool IsEOF() const { return m_bEOF; } + + // parse parts of the grammar + void ParseRule(); + std::unique_ptr<CSS1Selector> ParseSelector(); + std::unique_ptr<CSS1Expression> ParseDeclaration( OUString& rProperty ); + +protected: + void ParseStyleSheet(); + + /** parse the content of a HTML style element + * + * For each selector and each declaration the methods SelectorParsed() + * or DeclarationParsed() need to be called afterwards + * + * @param rIn the style element as string + */ + void ParseStyleSheet( const OUString& rIn ); + + /** parse the content of a HTML style option + * + * For each selector and each declaration the methods SelectorParsed() + * or DeclarationParsed() need to be called afterwards. + * + * @param rIn the style option as string + * @return true if ??? + */ + void ParseStyleOption( const OUString& rIn ); + + /** Called after a selector was parsed. + * + * @param pSelector The selector that was parsed + * @param bFirst if true, a new declaration starts with this selector + */ + virtual void SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst ); + + /** Called after a declaration or property was parsed + * + * @param rProperty The declaration/property + * @param pExpr ??? + */ + virtual void DeclarationParsed( const OUString& rProperty, + std::unique_ptr<CSS1Expression> pExpr ); + +public: + CSS1Parser(); + virtual ~CSS1Parser(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/svxcss1.cxx b/sw/source/filter/html/svxcss1.cxx new file mode 100644 index 000000000..f64acdb4d --- /dev/null +++ b/sw/source/filter/html/svxcss1.cxx @@ -0,0 +1,3183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmath> +#include <memory> +#include <stdlib.h> + +#include <svx/svxids.hrc> +#include <i18nlangtag/languagetag.hxx> +#include <svtools/ctrltool.hxx> +#include <svl/urihelper.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/langitem.hxx> +#include <svl/itempool.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/orphitem.hxx> +#include <svtools/svparser.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include "css1kywd.hxx" +#include "svxcss1.hxx" +#include "htmlnum.hxx" + +#include <utility> + +using namespace ::com::sun::star; + +/// type of functions to parse CSS1 properties +typedef void (*FnParseCSS1Prop)( const CSS1Expression *pExpr, + SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ); + +static CSS1PropertyEnum const aFontSizeTable[] = +{ + { "xx-small", 0 }, + { "x-small", 1 }, + { "small", 2 }, + { "medium", 3 }, + { "large", 4 }, + { "x-large", 5 }, + { "xx-large", 6 }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aFontWeightTable[] = +{ + { "extra-light", WEIGHT_NORMAL }, // WEIGHT_ULTRALIGHT (OBS) + { "light", WEIGHT_NORMAL }, // WEIGHT_LIGHT (OBSOLETE) + { "demi-light", WEIGHT_NORMAL }, // WEIGHT_SEMILIGHT (OBS) + { "medium", WEIGHT_NORMAL }, // WEIGHT_MEDIUM (OBS) + { "normal", WEIGHT_NORMAL }, // WEIGHT_MEDIUM + { "demi-bold", WEIGHT_NORMAL }, // WEIGHT_SEMIBOLD (OBS) + { "bold", WEIGHT_BOLD }, // WEIGHT_BOLD (OBSOLETE) + { "extra-bold", WEIGHT_BOLD }, // WEIGHT_ULTRABOLD (OBS) + { "bolder", WEIGHT_BOLD }, + { "lighter", WEIGHT_NORMAL }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aFontStyleTable[] = +{ + { "normal", ITALIC_NONE }, + { "italic", ITALIC_NORMAL }, + { "oblique", ITALIC_NORMAL }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aFontVariantTable[] = +{ + { "normal", sal_uInt16(SvxCaseMap::NotMapped) }, + { "small-caps", sal_uInt16(SvxCaseMap::SmallCaps) }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aTextTransformTable[] = +{ + { "uppercase", sal_uInt16(SvxCaseMap::Uppercase) }, + { "lowercase", sal_uInt16(SvxCaseMap::Lowercase) }, + { "capitalize", sal_uInt16(SvxCaseMap::Capitalize) }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aDirectionTable[] = +{ + { "ltr", sal_uInt16(SvxFrameDirection::Horizontal_LR_TB) }, + { "rtl", sal_uInt16(SvxFrameDirection::Horizontal_RL_TB) }, + { "inherit", sal_uInt16(SvxFrameDirection::Environment) }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aBGRepeatTable[] = +{ + { "repeat", GPOS_TILED }, + { "repeat-x", GPOS_TILED }, + { "repeat-y", GPOS_TILED }, + { "no-repeat", GPOS_NONE }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aBGHoriPosTable[] = +{ + { "left", GPOS_LT }, + { "center", GPOS_MT }, + { "right", GPOS_RT }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aBGVertPosTable[] = +{ + { "top", GPOS_LT }, + { "middle", GPOS_LM }, + { "bottom", GPOS_LB }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aTextAlignTable[] = +{ + { "left", sal_uInt16(SvxAdjust::Left) }, + { "center", sal_uInt16(SvxAdjust::Center) }, + { "right", sal_uInt16(SvxAdjust::Right) }, + { "justify", sal_uInt16(SvxAdjust::Block) }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aBorderWidthTable[] = +{ + { "thin", 0 }, // DEF_LINE_WIDTH_0 / DEF_DOUBLE_LINE0 + { "medium", 1 }, // DEF_LINE_WIDTH_1 / DEF_DOUBLE_LINE1 + { "thick", 2 }, // DEF_LINE_WIDTH_2 / DEF_DOUBLE_LINE2 + { nullptr, 0 } +}; + +namespace { + +enum CSS1BorderStyle { CSS1_BS_NONE, CSS1_BS_SINGLE, CSS1_BS_DOUBLE, CSS1_BS_DOTTED, CSS1_BS_DASHED, CSS1_BS_GROOVE, CSS1_BS_RIDGE, CSS1_BS_INSET, CSS1_BS_OUTSET }; + +} + +static CSS1PropertyEnum const aBorderStyleTable[] = +{ + { "none", CSS1_BS_NONE }, + { "dotted", CSS1_BS_DOTTED }, + { "dashed", CSS1_BS_DASHED }, + { "solid", CSS1_BS_SINGLE }, + { "double", CSS1_BS_DOUBLE }, + { "groove", CSS1_BS_GROOVE }, + { "ridge", CSS1_BS_RIDGE }, + { "inset", CSS1_BS_INSET }, + { "outset", CSS1_BS_OUTSET }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aFloatTable[] = +{ + { "left", sal_uInt16(SvxAdjust::Left) }, + { "right", sal_uInt16(SvxAdjust::Right) }, + { "none", sal_uInt16(SvxAdjust::End) }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aPositionTable[] = +{ + { "absolute", SVX_CSS1_POS_ABSOLUTE }, + { "relative", SVX_CSS1_POS_RELATIVE }, + { "static", SVX_CSS1_POS_STATIC }, + { nullptr, 0 } +}; + +// Feature: PrintExt +static CSS1PropertyEnum const aSizeTable[] = +{ + { "auto", SVX_CSS1_STYPE_AUTO }, + { "landscape", SVX_CSS1_STYPE_LANDSCAPE }, + { "portrait", SVX_CSS1_STYPE_PORTRAIT }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aPageBreakTable[] = +{ + { "auto", SVX_CSS1_PBREAK_AUTO }, + { "always", SVX_CSS1_PBREAK_ALWAYS }, + { "avoid", SVX_CSS1_PBREAK_AVOID }, + { "left", SVX_CSS1_PBREAK_LEFT }, + { "right", SVX_CSS1_PBREAK_RIGHT }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aNumberStyleTable[] = +{ + { "decimal", SVX_NUM_ARABIC }, + { "lower-alpha", SVX_NUM_CHARS_LOWER_LETTER }, + { "lower-latin", SVX_NUM_CHARS_LOWER_LETTER }, + { "lower-roman", SVX_NUM_ROMAN_LOWER }, + { "upper-alpha", SVX_NUM_CHARS_UPPER_LETTER }, + { "upper-latin", SVX_NUM_CHARS_UPPER_LETTER }, + { "upper-roman", SVX_NUM_ROMAN_UPPER }, + { nullptr, 0 } +}; + +static CSS1PropertyEnum const aBulletStyleTable[] = +{ + { "circle", HTML_BULLETCHAR_CIRCLE }, + { "disc", HTML_BULLETCHAR_DISC }, + { "square", HTML_BULLETCHAR_SQUARE }, + { nullptr, 0 } +}; + + +static sal_uInt16 const aBorderWidths[] = +{ + DEF_LINE_WIDTH_0, + DEF_LINE_WIDTH_5, + DEF_LINE_WIDTH_1 +}; + +#undef SBORDER_ENTRY +#undef DBORDER_ENTRY + +namespace { + +struct SvxCSS1ItemIds +{ + sal_uInt16 nFont; + sal_uInt16 nFontCJK; + sal_uInt16 nFontCTL; + sal_uInt16 nPosture; + sal_uInt16 nPostureCJK; + sal_uInt16 nPostureCTL; + sal_uInt16 nWeight; + sal_uInt16 nWeightCJK; + sal_uInt16 nWeightCTL; + sal_uInt16 nFontHeight; + sal_uInt16 nFontHeightCJK; + sal_uInt16 nFontHeightCTL; + sal_uInt16 nUnderline; + sal_uInt16 nOverline; + sal_uInt16 nCrossedOut; + sal_uInt16 nColor; + sal_uInt16 nKerning; + sal_uInt16 nCaseMap; + sal_uInt16 nBlink; + + sal_uInt16 nLineSpacing; + sal_uInt16 nAdjust; + sal_uInt16 nWidows; + sal_uInt16 nOrphans; + sal_uInt16 nFormatSplit; + + sal_uInt16 nLRSpace; + sal_uInt16 nULSpace; + sal_uInt16 nBox; + sal_uInt16 nBrush; + + sal_uInt16 nLanguage; + sal_uInt16 nLanguageCJK; + sal_uInt16 nLanguageCTL; + sal_uInt16 nDirection; +}; + +} + +static SvxCSS1ItemIds aItemIds; + +struct SvxCSS1BorderInfo +{ + Color aColor; + sal_uInt16 nAbsWidth; + sal_uInt16 nNamedWidth; + CSS1BorderStyle eStyle; + + SvxCSS1BorderInfo() : + aColor( COL_BLACK ), nAbsWidth( USHRT_MAX ), + nNamedWidth( USHRT_MAX ), eStyle( CSS1_BS_NONE ) + {} + + void SetBorderLine( SvxBoxItemLine nLine, SvxBoxItem &rBoxItem ) const; +}; + +void SvxCSS1BorderInfo::SetBorderLine( SvxBoxItemLine nLine, SvxBoxItem &rBoxItem ) const +{ + if( CSS1_BS_NONE==eStyle || nAbsWidth==0 || + (nAbsWidth==USHRT_MAX && nNamedWidth==USHRT_MAX) ) + { + rBoxItem.SetLine( nullptr, nLine ); + return; + } + + ::editeng::SvxBorderLine aBorderLine( &aColor ); + + // Line style double or single? + switch ( eStyle ) + { + case CSS1_BS_SINGLE: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); + break; + case CSS1_BS_DOUBLE: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + break; + case CSS1_BS_DOTTED: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOTTED); + break; + case CSS1_BS_DASHED: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DASHED); + break; + case CSS1_BS_GROOVE: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::ENGRAVED); + break; + case CSS1_BS_RIDGE: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::EMBOSSED); + break; + case CSS1_BS_INSET: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::INSET); + break; + case CSS1_BS_OUTSET: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::OUTSET); + break; + default: + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::NONE); + break; + } + + // convert named width, if no absolute is given + if( nAbsWidth==USHRT_MAX ) + aBorderLine.SetWidth( aBorderWidths[ nNamedWidth ] ); + else + aBorderLine.SetWidth( nAbsWidth ); + + rBoxItem.SetLine( &aBorderLine, nLine ); +} + +SvxCSS1PropertyInfo::SvxCSS1PropertyInfo() +{ + Clear(); +} + +SvxCSS1PropertyInfo::SvxCSS1PropertyInfo( const SvxCSS1PropertyInfo& rProp ) : + m_aId( rProp.m_aId ), + m_bTopMargin( rProp.m_bTopMargin ), + m_bBottomMargin( rProp.m_bBottomMargin ), + m_bLeftMargin( rProp.m_bLeftMargin ), + m_bRightMargin( rProp.m_bRightMargin ), + m_bTextIndent( rProp.m_bTextIndent ), + m_bNumbering ( rProp.m_bNumbering ), + m_bBullet ( rProp.m_bBullet ), + m_eFloat( rProp.m_eFloat ), + m_ePosition( rProp.m_ePosition ), + m_nTopBorderDistance( rProp.m_nTopBorderDistance ), + m_nBottomBorderDistance( rProp.m_nBottomBorderDistance ), + m_nLeftBorderDistance( rProp.m_nLeftBorderDistance ), + m_nRightBorderDistance( rProp.m_nRightBorderDistance ), + m_nNumberingType ( rProp.m_nNumberingType ), + m_cBulletChar( rProp.m_cBulletChar ), + m_nColumnCount( rProp.m_nColumnCount ), + m_nLeft( rProp.m_nLeft ), + m_nTop( rProp.m_nTop ), + m_nWidth( rProp.m_nWidth ), + m_nHeight( rProp.m_nHeight ), + m_nLeftMargin( rProp.m_nLeftMargin ), + m_nRightMargin( rProp.m_nRightMargin ), + m_eLeftType( rProp.m_eLeftType ), + m_eTopType( rProp.m_eTopType ), + m_eWidthType( rProp.m_eWidthType ), + m_eHeightType( rProp.m_eHeightType ), + m_eSizeType( rProp.m_eSizeType ), + m_ePageBreakBefore( rProp.m_ePageBreakBefore ), + m_ePageBreakAfter( rProp.m_ePageBreakAfter ) +{ + for( size_t i=0; i<m_aBorderInfos.size(); ++i ) + if (rProp.m_aBorderInfos[i]) + m_aBorderInfos[i].reset( new SvxCSS1BorderInfo( *rProp.m_aBorderInfos[i] ) ); +} + +SvxCSS1PropertyInfo::~SvxCSS1PropertyInfo() +{ +} + +void SvxCSS1PropertyInfo::DestroyBorderInfos() +{ + for(auto & rp : m_aBorderInfos) + rp.reset(); +} + +void SvxCSS1PropertyInfo::Clear() +{ + m_aId.clear(); + m_bTopMargin = m_bBottomMargin = false; + m_bLeftMargin = m_bRightMargin = m_bTextIndent = false; + m_bNumbering = m_bBullet = false; + m_nLeftMargin = m_nRightMargin = 0; + m_eFloat = SvxAdjust::End; + + m_ePosition = SVX_CSS1_POS_NONE; + m_nTopBorderDistance = m_nBottomBorderDistance = + m_nLeftBorderDistance = m_nRightBorderDistance = UNSET_BORDER_DISTANCE; + + m_nNumberingType = SVX_NUM_CHARS_UPPER_LETTER; + m_cBulletChar = ' '; + + m_nColumnCount = 0; + + m_nLeft = m_nTop = m_nWidth = m_nHeight = 0; + m_eLeftType = m_eTopType = m_eWidthType = m_eHeightType = SVX_CSS1_LTYPE_NONE; + +// Feature: PrintExt + m_eSizeType = SVX_CSS1_STYPE_NONE; + m_ePageBreakBefore = SVX_CSS1_PBREAK_NONE; + m_ePageBreakAfter = SVX_CSS1_PBREAK_NONE; + + DestroyBorderInfos(); +} + +void SvxCSS1PropertyInfo::Merge( const SvxCSS1PropertyInfo& rProp ) +{ + if( rProp.m_bTopMargin ) + m_bTopMargin = true; + if( rProp.m_bBottomMargin ) + m_bBottomMargin = true; + + if( rProp.m_bLeftMargin ) + { + m_bLeftMargin = true; + m_nLeftMargin = rProp.m_nLeftMargin; + } + if( rProp.m_bRightMargin ) + { + m_bRightMargin = true; + m_nRightMargin = rProp.m_nRightMargin; + } + if( rProp.m_bTextIndent ) + m_bTextIndent = true; + + for( size_t i=0; i<m_aBorderInfos.size(); ++i ) + { + if( rProp.m_aBorderInfos[i] ) + m_aBorderInfos[i].reset( new SvxCSS1BorderInfo( *rProp.m_aBorderInfos[i] ) ); + } + + if( UNSET_BORDER_DISTANCE != rProp.m_nTopBorderDistance ) + m_nTopBorderDistance = rProp.m_nTopBorderDistance; + if( UNSET_BORDER_DISTANCE != rProp.m_nBottomBorderDistance ) + m_nBottomBorderDistance = rProp.m_nBottomBorderDistance; + if( UNSET_BORDER_DISTANCE != rProp.m_nLeftBorderDistance ) + m_nLeftBorderDistance = rProp.m_nLeftBorderDistance; + if( UNSET_BORDER_DISTANCE != rProp.m_nRightBorderDistance ) + m_nRightBorderDistance = rProp.m_nRightBorderDistance; + + m_nColumnCount = rProp.m_nColumnCount; + + if( rProp.m_eFloat != SvxAdjust::End ) + m_eFloat = rProp.m_eFloat; + + if( rProp.m_ePosition != SVX_CSS1_POS_NONE ) + m_ePosition = rProp.m_ePosition; + +// Feature: PrintExt + if( rProp.m_eSizeType != SVX_CSS1_STYPE_NONE ) + { + m_eSizeType = rProp.m_eSizeType; + m_nWidth = rProp.m_nWidth; + m_nHeight = rProp.m_nHeight; + } + + if( rProp.m_ePageBreakBefore != SVX_CSS1_PBREAK_NONE ) + m_ePageBreakBefore = rProp.m_ePageBreakBefore; + + if( rProp.m_ePageBreakAfter != SVX_CSS1_PBREAK_NONE ) + m_ePageBreakAfter = rProp.m_ePageBreakAfter; + + if( rProp.m_eLeftType != SVX_CSS1_LTYPE_NONE ) + { + m_eLeftType = rProp.m_eLeftType; + m_nLeft = rProp.m_nLeft; + } + + if( rProp.m_eTopType != SVX_CSS1_LTYPE_NONE ) + { + m_eTopType = rProp.m_eTopType; + m_nTop = rProp.m_nTop; + } + + if( rProp.m_eWidthType != SVX_CSS1_LTYPE_NONE ) + { + m_eWidthType = rProp.m_eWidthType; + m_nWidth = rProp.m_nWidth; + } + + if( rProp.m_eHeightType != SVX_CSS1_LTYPE_NONE ) + { + m_eHeightType = rProp.m_eHeightType; + m_nHeight = rProp.m_nHeight; + } +} + +SvxCSS1BorderInfo *SvxCSS1PropertyInfo::GetBorderInfo( SvxBoxItemLine nLine, bool bCreate ) +{ + sal_uInt16 nPos = 0; + switch( nLine ) + { + case SvxBoxItemLine::TOP: nPos = 0; break; + case SvxBoxItemLine::BOTTOM: nPos = 1; break; + case SvxBoxItemLine::LEFT: nPos = 2; break; + case SvxBoxItemLine::RIGHT: nPos = 3; break; + } + + if( !m_aBorderInfos[nPos] && bCreate ) + m_aBorderInfos[nPos].reset( new SvxCSS1BorderInfo ); + + return m_aBorderInfos[nPos].get(); +} + +void SvxCSS1PropertyInfo::CopyBorderInfo( SvxBoxItemLine nSrcLine, SvxBoxItemLine nDstLine, + sal_uInt16 nWhat ) +{ + SvxCSS1BorderInfo *pSrcInfo = GetBorderInfo( nSrcLine, false ); + if( !pSrcInfo ) + return; + + SvxCSS1BorderInfo *pDstInfo = GetBorderInfo( nDstLine ); + if( (nWhat & SVX_CSS1_BORDERINFO_WIDTH) != 0 ) + { + pDstInfo->nAbsWidth = pSrcInfo->nAbsWidth; + pDstInfo->nNamedWidth = pSrcInfo->nNamedWidth; + } + + if( (nWhat & SVX_CSS1_BORDERINFO_COLOR) != 0 ) + pDstInfo->aColor = pSrcInfo->aColor; + + if( (nWhat & SVX_CSS1_BORDERINFO_STYLE) != 0 ) + pDstInfo->eStyle = pSrcInfo->eStyle; +} + +void SvxCSS1PropertyInfo::CopyBorderInfo( sal_uInt16 nCount, sal_uInt16 nWhat ) +{ + if( nCount==0 ) + { + CopyBorderInfo( SvxBoxItemLine::BOTTOM, SvxBoxItemLine::TOP, nWhat ); + CopyBorderInfo( SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, nWhat ); + } + if( nCount<=1 ) + { + CopyBorderInfo( SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT, nWhat ); + } +} + +void SvxCSS1PropertyInfo::SetBoxItem( SfxItemSet& rItemSet, + sal_uInt16 nMinBorderDist, + const SvxBoxItem *pDfltItem ) +{ + bool bChg = m_nTopBorderDistance != UNSET_BORDER_DISTANCE || + m_nBottomBorderDistance != UNSET_BORDER_DISTANCE || + m_nLeftBorderDistance != UNSET_BORDER_DISTANCE || + m_nRightBorderDistance != UNSET_BORDER_DISTANCE; + + for( size_t i=0; !bChg && i<m_aBorderInfos.size(); ++i ) + bChg = m_aBorderInfos[i]!=nullptr; + + if( !bChg ) + return; + + std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(aItemIds.nBox)); + if( pDfltItem ) + aBoxItem.reset(pDfltItem->Clone()); + + SvxCSS1BorderInfo *pInfo = GetBorderInfo( SvxBoxItemLine::TOP, false ); + if( pInfo ) + pInfo->SetBorderLine( SvxBoxItemLine::TOP, *aBoxItem ); + + pInfo = GetBorderInfo( SvxBoxItemLine::BOTTOM, false ); + if( pInfo ) + pInfo->SetBorderLine( SvxBoxItemLine::BOTTOM, *aBoxItem ); + + pInfo = GetBorderInfo( SvxBoxItemLine::LEFT, false ); + if( pInfo ) + pInfo->SetBorderLine( SvxBoxItemLine::LEFT, *aBoxItem ); + + pInfo = GetBorderInfo( SvxBoxItemLine::RIGHT, false ); + if( pInfo ) + pInfo->SetBorderLine( SvxBoxItemLine::RIGHT, *aBoxItem ); + + for( size_t i=0; i<m_aBorderInfos.size(); ++i ) + { + SvxBoxItemLine nLine = SvxBoxItemLine::TOP; + sal_uInt16 nDist = 0; + switch( i ) + { + case 0: nLine = SvxBoxItemLine::TOP; + nDist = m_nTopBorderDistance; + m_nTopBorderDistance = UNSET_BORDER_DISTANCE; + break; + case 1: nLine = SvxBoxItemLine::BOTTOM; + nDist = m_nBottomBorderDistance; + m_nBottomBorderDistance = UNSET_BORDER_DISTANCE; + break; + case 2: nLine = SvxBoxItemLine::LEFT; + nDist = m_nLeftBorderDistance; + m_nLeftBorderDistance = UNSET_BORDER_DISTANCE; + break; + case 3: nLine = SvxBoxItemLine::RIGHT; + nDist = m_nRightBorderDistance; + m_nRightBorderDistance = UNSET_BORDER_DISTANCE; + break; + } + + if( aBoxItem->GetLine( nLine ) ) + { + if( UNSET_BORDER_DISTANCE == nDist ) + nDist = aBoxItem->GetDistance( nLine ); + + if( nDist < nMinBorderDist ) + nDist = nMinBorderDist; + } + else + { + nDist = 0U; + } + + aBoxItem->SetDistance( nDist, nLine ); + } + + rItemSet.Put( *aBoxItem ); + + DestroyBorderInfos(); +} + +SvxCSS1MapEntry::SvxCSS1MapEntry( const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ) : + aItemSet( rItemSet ), + aPropInfo( rProp ) +{} + +void SvxCSS1Parser::StyleParsed( const CSS1Selector * /*pSelector*/, + SfxItemSet& /*rItemSet*/, + SvxCSS1PropertyInfo& /*rPropInfo*/ ) +{ + // you see nothing is happening here +} + +void SvxCSS1Parser::SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst ) +{ + if( bFirst ) + { + OSL_ENSURE( pSheetItemSet, "Where is the Item-Set for Style-Sheets?" ); + + for (const std::unique_ptr<CSS1Selector> & rpSelection : m_Selectors) + { + StyleParsed(rpSelection.get(), *pSheetItemSet, *pSheetPropInfo); + } + pSheetItemSet->ClearItem(); + pSheetPropInfo->Clear(); + + // prepare the next rule + m_Selectors.clear(); + } + + m_Selectors.push_back(std::move(pSelector)); +} + +SvxCSS1Parser::SvxCSS1Parser( SfxItemPool& rPool, const OUString& rBaseURL, + sal_uInt16 const *pWhichIds, sal_uInt16 nWhichIds ) : + CSS1Parser(), + sBaseURL( rBaseURL ), + pItemSet(nullptr), + pPropInfo( nullptr ), + eDfltEnc( RTL_TEXTENCODING_DONTKNOW ), + bIgnoreFontFamily( false ) +{ + // also initialize item IDs + aItemIds.nFont = rPool.GetTrueWhich( SID_ATTR_CHAR_FONT, false ); + aItemIds.nFontCJK = rPool.GetTrueWhich( SID_ATTR_CHAR_CJK_FONT, false ); + aItemIds.nFontCTL = rPool.GetTrueWhich( SID_ATTR_CHAR_CTL_FONT, false ); + aItemIds.nPosture = rPool.GetTrueWhich( SID_ATTR_CHAR_POSTURE, false ); + aItemIds.nPostureCJK = rPool.GetTrueWhich( SID_ATTR_CHAR_CJK_POSTURE, false ); + aItemIds.nPostureCTL = rPool.GetTrueWhich( SID_ATTR_CHAR_CTL_POSTURE, false ); + aItemIds.nWeight = rPool.GetTrueWhich( SID_ATTR_CHAR_WEIGHT, false ); + aItemIds.nWeightCJK = rPool.GetTrueWhich( SID_ATTR_CHAR_CJK_WEIGHT, false ); + aItemIds.nWeightCTL = rPool.GetTrueWhich( SID_ATTR_CHAR_CTL_WEIGHT, false ); + aItemIds.nFontHeight = rPool.GetTrueWhich( SID_ATTR_CHAR_FONTHEIGHT, false ); + aItemIds.nFontHeightCJK = rPool.GetTrueWhich( SID_ATTR_CHAR_CJK_FONTHEIGHT, false ); + aItemIds.nFontHeightCTL = rPool.GetTrueWhich( SID_ATTR_CHAR_CTL_FONTHEIGHT, false ); + aItemIds.nUnderline = rPool.GetTrueWhich( SID_ATTR_CHAR_UNDERLINE, false ); + aItemIds.nOverline = rPool.GetTrueWhich( SID_ATTR_CHAR_OVERLINE, false ); + aItemIds.nCrossedOut = rPool.GetTrueWhich( SID_ATTR_CHAR_STRIKEOUT, false ); + aItemIds.nColor = rPool.GetTrueWhich( SID_ATTR_CHAR_COLOR, false ); + aItemIds.nKerning = rPool.GetTrueWhich( SID_ATTR_CHAR_KERNING, false ); + aItemIds.nCaseMap = rPool.GetTrueWhich( SID_ATTR_CHAR_CASEMAP, false ); + aItemIds.nBlink = rPool.GetTrueWhich( SID_ATTR_FLASH, false ); + + aItemIds.nLineSpacing = rPool.GetTrueWhich( SID_ATTR_PARA_LINESPACE, false ); + aItemIds.nAdjust = rPool.GetTrueWhich( SID_ATTR_PARA_ADJUST, false ); + aItemIds.nWidows = rPool.GetTrueWhich( SID_ATTR_PARA_WIDOWS, false ); + aItemIds.nOrphans = rPool.GetTrueWhich( SID_ATTR_PARA_ORPHANS, false ); + aItemIds.nFormatSplit = rPool.GetTrueWhich( SID_ATTR_PARA_SPLIT, false ); + + aItemIds.nLRSpace = rPool.GetTrueWhich( SID_ATTR_LRSPACE, false ); + aItemIds.nULSpace = rPool.GetTrueWhich( SID_ATTR_ULSPACE, false ); + aItemIds.nBox = rPool.GetTrueWhich( SID_ATTR_BORDER_OUTER, false ); + aItemIds.nBrush = rPool.GetTrueWhich( SID_ATTR_BRUSH, false ); + + aItemIds.nLanguage = rPool.GetTrueWhich( SID_ATTR_CHAR_LANGUAGE, false ); + aItemIds.nLanguageCJK = rPool.GetTrueWhich( SID_ATTR_CHAR_CJK_LANGUAGE, false ); + aItemIds.nLanguageCTL = rPool.GetTrueWhich( SID_ATTR_CHAR_CTL_LANGUAGE, false ); + aItemIds.nDirection = rPool.GetTrueWhich( SID_ATTR_FRAMEDIRECTION, false ); + + aWhichMap.insert( aWhichMap.begin(), 0 ); + BuildWhichTable( aWhichMap, reinterpret_cast<sal_uInt16 *>(&aItemIds), + sizeof(aItemIds) / sizeof(sal_uInt16) ); + if( pWhichIds && nWhichIds ) + BuildWhichTable( aWhichMap, pWhichIds, nWhichIds ); + + pSheetItemSet.reset( new SfxItemSet( rPool, aWhichMap.data() ) ); + pSheetPropInfo.reset( new SvxCSS1PropertyInfo ); +} + +SvxCSS1Parser::~SvxCSS1Parser() +{ + pSheetItemSet.reset(); + pSheetPropInfo.reset(); +} + +void SvxCSS1Parser::InsertId( const OUString& rId, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ) +{ + InsertMapEntry( rId, rItemSet, rProp, m_Ids ); +} + +const SvxCSS1MapEntry* SvxCSS1Parser::GetId( const OUString& rId ) const +{ + CSS1Map::const_iterator itr = m_Ids.find(rId); + return itr == m_Ids.end() ? nullptr : itr->second.get(); +} + +void SvxCSS1Parser::InsertClass( const OUString& rClass, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ) +{ + InsertMapEntry( rClass, rItemSet, rProp, m_Classes ); +} + +const SvxCSS1MapEntry* SvxCSS1Parser::GetClass( const OUString& rClass ) const +{ + CSS1Map::const_iterator itr = m_Classes.find(rClass); + return itr == m_Classes.end() ? nullptr : itr->second.get(); +} + +void SvxCSS1Parser::InsertPage( const OUString& rPage, + bool bPseudo, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ) +{ + OUString aKey( rPage ); + if( bPseudo ) + aKey = ":" + aKey; + InsertMapEntry( aKey, rItemSet, rProp, m_Pages ); +} + +SvxCSS1MapEntry* SvxCSS1Parser::GetPage( const OUString& rPage, bool bPseudo ) +{ + OUString aKey( rPage ); + if( bPseudo ) + aKey = ":" + aKey; + + CSS1Map::iterator itr = m_Pages.find(aKey); + return itr == m_Pages.end() ? nullptr : itr->second.get(); +} + +void SvxCSS1Parser::InsertTag( const OUString& rTag, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ) +{ + InsertMapEntry( rTag, rItemSet, rProp, m_Tags ); +} + +SvxCSS1MapEntry* SvxCSS1Parser::GetTag( const OUString& rTag ) +{ + CSS1Map::iterator itr = m_Tags.find(rTag); + return itr == m_Tags.end() ? nullptr : itr->second.get(); +} + +bool SvxCSS1Parser::ParseStyleSheet( const OUString& rIn ) +{ + pItemSet = pSheetItemSet.get(); + pPropInfo = pSheetPropInfo.get(); + + CSS1Parser::ParseStyleSheet( rIn ); + + for (const std::unique_ptr<CSS1Selector> & rpSelector : m_Selectors) + { + StyleParsed(rpSelector.get(), *pSheetItemSet, *pSheetPropInfo); + } + + // and clean up a little bit + m_Selectors.clear(); + pSheetItemSet->ClearItem(); + pSheetPropInfo->Clear(); + + pItemSet = nullptr; + pPropInfo = nullptr; + + return true; +} + +void SvxCSS1Parser::ParseStyleOption( const OUString& rIn, + SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo ) +{ + pItemSet = &rItemSet; + pPropInfo = &rPropInfo; + + CSS1Parser::ParseStyleOption( rIn ); + rItemSet.ClearItem( aItemIds.nDirection ); + + pItemSet = nullptr; + pPropInfo = nullptr; +} + +bool SvxCSS1Parser::GetEnum( const CSS1PropertyEnum *pPropTable, + const OUString &rValue, sal_uInt16& rEnum ) +{ + while( pPropTable->pName ) + { + if( !rValue.equalsIgnoreAsciiCaseAscii( pPropTable->pName ) ) + pPropTable++; + else + break; + } + + if( pPropTable->pName ) + rEnum = pPropTable->nEnum; + + return (pPropTable->pName != nullptr); +} + +void SvxCSS1Parser::PixelToTwip( long &rWidth, long &rHeight ) +{ + if( Application::GetDefaultDevice() ) + { + Size aTwipSz( rWidth, rHeight ); + aTwipSz = Application::GetDefaultDevice()->PixelToLogic( aTwipSz, + MapMode(MapUnit::MapTwip) ); + + rWidth = aTwipSz.Width(); + rHeight = aTwipSz.Height(); + } +} + +sal_uInt32 SvxCSS1Parser::GetFontHeight( sal_uInt16 nSize ) const +{ + sal_uInt16 nHeight; + + switch( nSize ) + { + case 0: nHeight = 8*20; break; + case 1: nHeight = 10*20; break; + case 2: nHeight = 11*20; break; + case 3: nHeight = 12*20; break; + case 4: nHeight = 17*20; break; + case 5: nHeight = 20*20; break; + case 6: + default: nHeight = 32*20; break; + } + + return nHeight; +} + +const FontList *SvxCSS1Parser::GetFontList() const +{ + return nullptr; +} + +void SvxCSS1Parser::InsertMapEntry( const OUString& rKey, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp, + CSS1Map& rMap ) +{ + CSS1Map::iterator itr = rMap.find(rKey); + if (itr == rMap.end()) + { + rMap.insert(std::make_pair(rKey, std::make_unique<SvxCSS1MapEntry>(rItemSet, rProp))); + } + else + { + SvxCSS1MapEntry *const p = itr->second.get(); + MergeStyles( rItemSet, rProp, + p->GetItemSet(), p->GetPropertyInfo(), true ); + } +} + +void SvxCSS1Parser::MergeStyles( const SfxItemSet& rSrcSet, + const SvxCSS1PropertyInfo& rSrcInfo, + SfxItemSet& rTargetSet, + SvxCSS1PropertyInfo& rTargetInfo, + bool bSmart ) +{ + if( !bSmart ) + { + rTargetSet.Put( rSrcSet ); + } + else + { + SvxLRSpaceItem aLRSpace( static_cast<const SvxLRSpaceItem&>(rTargetSet.Get(aItemIds.nLRSpace)) ); + SvxULSpaceItem aULSpace( static_cast<const SvxULSpaceItem&>(rTargetSet.Get(aItemIds.nULSpace)) ); + + rTargetSet.Put( rSrcSet ); + + if( rSrcInfo.m_bLeftMargin || rSrcInfo.m_bRightMargin || + rSrcInfo.m_bTextIndent ) + { + const SvxLRSpaceItem& rNewLRSpace = + static_cast<const SvxLRSpaceItem&>(rSrcSet.Get( aItemIds.nLRSpace )); + + if( rSrcInfo.m_bLeftMargin ) + aLRSpace.SetLeft( rNewLRSpace.GetLeft() ); + if( rSrcInfo.m_bRightMargin ) + aLRSpace.SetRight( rNewLRSpace.GetRight() ); + if( rSrcInfo.m_bTextIndent ) + aLRSpace.SetTextFirstLineOffset( rNewLRSpace.GetTextFirstLineOffset() ); + + rTargetSet.Put( aLRSpace ); + } + + if( rSrcInfo.m_bTopMargin || rSrcInfo.m_bBottomMargin ) + { + const SvxULSpaceItem& rNewULSpace = + static_cast<const SvxULSpaceItem&>(rSrcSet.Get( aItemIds.nULSpace )); + + if( rSrcInfo.m_bTopMargin ) + aULSpace.SetUpper( rNewULSpace.GetUpper() ); + if( rSrcInfo.m_bBottomMargin ) + aULSpace.SetLower( rNewULSpace.GetLower() ); + + rTargetSet.Put( aULSpace ); + } + } + + rTargetInfo.Merge( rSrcInfo ); +} + +void SvxCSS1Parser::SetDfltEncoding( rtl_TextEncoding eEnc ) +{ + eDfltEnc = eEnc; +} + +static void ParseCSS1_font_size( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& rParser ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + sal_uLong nHeight = 0; + sal_uInt16 nPropHeight = 100; + + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + nHeight = pExpr->GetULength(); + break; + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPHeight = static_cast<long>(fHeight); + long nPWidth = 0; + SvxCSS1Parser::PixelToTwip(nPWidth, nPHeight); + nHeight = static_cast<sal_uLong>(nPHeight); + } + else + { + SAL_WARN("sw.html", "out-of-size pxlength: " << fHeight); + } + } + break; + case CSS1_PERCENTAGE: + // only for drop caps! + nPropHeight = static_cast<sal_uInt16>(pExpr->GetNumber()); + break; + case CSS1_IDENT: + { + sal_uInt16 nSize; + + if( SvxCSS1Parser::GetEnum( aFontSizeTable, pExpr->GetString(), + nSize ) ) + { + nHeight = rParser.GetFontHeight( nSize ); + } + } + break; + + default: + ; + } + + if( nHeight || nPropHeight!=100 ) + { + SvxFontHeightItem aFontHeight( nHeight, nPropHeight, + aItemIds.nFontHeight ); + rItemSet.Put( aFontHeight ); + aFontHeight.SetWhich( aItemIds.nFontHeightCJK ); + rItemSet.Put( aFontHeight ); + aFontHeight.SetWhich( aItemIds.nFontHeightCTL ); + rItemSet.Put( aFontHeight ); + } +} + +static void ParseCSS1_font_family( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& rParser ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + OUStringBuffer aName; + rtl_TextEncoding eEnc = rParser.GetDfltEncoding(); + const FontList *pFList = rParser.GetFontList(); + bool bFirst = true; + bool bFound = false; + while( pExpr && (bFirst || ','==pExpr->GetOp() || !pExpr->GetOp()) ) + { + CSS1Token eType = pExpr->GetType(); + if( CSS1_IDENT==eType || CSS1_STRING==eType ) + { + OUString aIdent( pExpr->GetString() ); + + if( CSS1_IDENT==eType ) + { + // Collect all following IDs and append them with a space + const CSS1Expression *pNext = pExpr->GetNext(); + while( pNext && !pNext->GetOp() && + CSS1_IDENT==pNext->GetType() ) + { + aIdent += " " + pNext->GetString(); + pExpr = pNext; + pNext = pExpr->GetNext(); + } + } + if( !aIdent.isEmpty() ) + { + if( !bFound && pFList ) + { + sal_Handle hFont = pFList->GetFirstFontMetric( aIdent ); + if( nullptr != hFont ) + { + const FontMetric& rFMetric = FontList::GetFontMetric( hFont ); + if( RTL_TEXTENCODING_DONTKNOW != rFMetric.GetCharSet() ) + { + bFound = true; + if( RTL_TEXTENCODING_SYMBOL == rFMetric.GetCharSet() ) + eEnc = RTL_TEXTENCODING_SYMBOL; + } + } + } + if( !bFirst ) + aName.append(";"); + aName.append(aIdent); + } + } + + pExpr = pExpr->GetNext(); + bFirst = false; + } + + if( !aName.isEmpty() && !rParser.IsIgnoreFontFamily() ) + { + SvxFontItem aFont( FAMILY_DONTKNOW, aName.makeStringAndClear(), OUString(), PITCH_DONTKNOW, + eEnc, aItemIds.nFont ); + rItemSet.Put( aFont ); + aFont.SetWhich( aItemIds.nFontCJK ); + rItemSet.Put( aFont ); + aFont.SetWhich( aItemIds.nFontCTL ); + rItemSet.Put( aFont ); + } +} + +static void ParseCSS1_font_weight( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + case CSS1_STRING: // MS-IE, what else + { + sal_uInt16 nWeight; + if( SvxCSS1Parser::GetEnum( aFontWeightTable, pExpr->GetString(), + nWeight ) ) + { + SvxWeightItem aWeight( static_cast<FontWeight>(nWeight), aItemIds.nWeight ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCJK ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCTL ); + rItemSet.Put( aWeight ); + } + } + break; + case CSS1_NUMBER: + { + sal_uInt16 nWeight = static_cast<sal_uInt16>(pExpr->GetNumber()); + SvxWeightItem aWeight( nWeight>400 ? WEIGHT_BOLD : WEIGHT_NORMAL, + aItemIds.nWeight ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCJK ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCTL ); + rItemSet.Put( aWeight ); + } + break; + + default: + ; + } +} + +static void ParseCSS1_font_style( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + bool bPosture = false; + bool bCaseMap = false; + FontItalic eItalic = ITALIC_NONE; + SvxCaseMap eCaseMap = SvxCaseMap::NotMapped; + + // normal | italic || small-caps | oblique || small-caps | small-caps + // (only normal, italic and oblique are valid) + + // the value can have two values! + for( int i=0; pExpr && i<2; ++i ) + { + // also here MS-IE parser leaves traces + if( (CSS1_IDENT==pExpr->GetType() || CSS1_STRING==pExpr->GetType()) && + !pExpr->GetOp() ) + { + const OUString& rValue = pExpr->GetString(); + // first check if the value is italic or 'normal' + sal_uInt16 nItalic; + if( SvxCSS1Parser::GetEnum( aFontStyleTable, rValue, nItalic ) ) + { + eItalic = static_cast<FontItalic>(nItalic); + if( !bCaseMap && ITALIC_NONE==eItalic ) + { + // for 'normal' we must also exclude case-map + eCaseMap = SvxCaseMap::NotMapped; + bCaseMap = true; + } + bPosture = true; + } + else if( !bCaseMap && + rValue.equalsIgnoreAsciiCase( "small-caps" ) ) + { + eCaseMap = SvxCaseMap::SmallCaps; + bCaseMap = true; + } + } + + // fetch next expression + pExpr = pExpr->GetNext(); + } + + if( bPosture ) + { + SvxPostureItem aPosture( eItalic, aItemIds.nPosture ); + rItemSet.Put( aPosture ); + aPosture.SetWhich( aItemIds.nPostureCJK ); + rItemSet.Put( aPosture ); + aPosture.SetWhich( aItemIds.nPostureCTL ); + rItemSet.Put( aPosture ); + } + + if( bCaseMap ) + rItemSet.Put( SvxCaseMapItem( eCaseMap, aItemIds.nCaseMap ) ); +} + +static void ParseCSS1_font_variant( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + assert(pExpr && "no expression"); + + // normal | small-caps + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + { + sal_uInt16 nCaseMap; + if( SvxCSS1Parser::GetEnum( aFontVariantTable, pExpr->GetString(), + nCaseMap ) ) + { + rItemSet.Put( SvxCaseMapItem( static_cast<SvxCaseMap>(nCaseMap), + aItemIds.nCaseMap ) ); + } + break; + } + default: + break; + } +} + +static void ParseCSS1_text_transform( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + // none | capitalize | uppercase | lowercase + + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + { + sal_uInt16 nCaseMap; + if( SvxCSS1Parser::GetEnum( aTextTransformTable, pExpr->GetString(), + nCaseMap ) ) + { + rItemSet.Put( SvxCaseMapItem( static_cast<SvxCaseMap>(nCaseMap), + aItemIds.nCaseMap ) ); + } + break; + } + default: + break; + } +} + +static void ParseCSS1_color( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + case CSS1_RGB: + case CSS1_HEXCOLOR: + case CSS1_STRING: // because MS-IE + { + Color aColor; + if( pExpr->GetColor( aColor ) ) + rItemSet.Put( SvxColorItem( aColor, aItemIds.nColor ) ); + } + break; + default: + ; + } +} + +static void ParseCSS1_column_count( const CSS1Expression *pExpr, + SfxItemSet& /*rItemSet*/, + SvxCSS1PropertyInfo &rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + assert(pExpr && "no expression"); + + if ( pExpr->GetType() == CSS1_NUMBER ) + { + double columnCount = pExpr->GetNumber(); + if ( columnCount >= 2 ) + { + rPropInfo.m_nColumnCount = columnCount; + } + } +} + +static void ParseCSS1_direction( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + assert(pExpr && "no expression"); + + sal_uInt16 nDir; + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + case CSS1_STRING: + if( SvxCSS1Parser::GetEnum( aDirectionTable, pExpr->GetString(), + nDir ) ) + { + rItemSet.Put( SvxFrameDirectionItem( + static_cast < SvxFrameDirection >( nDir ), + aItemIds.nDirection ) ); + } + break; + default: + ; + } +} + +static void MergeHori( SvxGraphicPosition& ePos, SvxGraphicPosition eHori ) +{ + OSL_ENSURE( GPOS_LT==eHori || GPOS_MT==eHori || GPOS_RT==eHori, + "vertical position not at the top" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + ePos = eHori; + break; + + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + ePos = GPOS_LT==eHori ? GPOS_LM : (GPOS_MT==eHori ? GPOS_MM : GPOS_RM); + break; + + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + ePos = GPOS_LT==eHori ? GPOS_LB : (GPOS_MT==eHori ? GPOS_MB : GPOS_RB); + break; + + default: + ; + } +} + +static void MergeVert( SvxGraphicPosition& ePos, SvxGraphicPosition eVert ) +{ + OSL_ENSURE( GPOS_LT==eVert || GPOS_LM==eVert || GPOS_LB==eVert, + "horizontal position not on the left side" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_LM: + case GPOS_LB: + ePos = eVert; + break; + + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + ePos = GPOS_LT==eVert ? GPOS_MT : (GPOS_LM==eVert ? GPOS_MM : GPOS_MB); + break; + + case GPOS_RT: + case GPOS_RM: + case GPOS_RB: + ePos = GPOS_LT==eVert ? GPOS_RT : (GPOS_LM==eVert ? GPOS_RM : GPOS_RB); + break; + + default: + ; + } +} + +static void ParseCSS1_background( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& rParser ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + Color aColor; + OUString aURL; + + bool bColor = false, bTransparent = false; + SvxGraphicPosition eRepeat = GPOS_TILED; + SvxGraphicPosition ePos = GPOS_LT; + bool bHori = false, bVert = false; + + while( pExpr && !pExpr->GetOp() ) + { + switch( pExpr->GetType() ) + { + case CSS1_URL: + pExpr->GetURL( aURL ); + break; + + case CSS1_RGB: + bColor = pExpr->GetColor( aColor ); + break; + + case CSS1_LENGTH: + case CSS1_PIXLENGTH: + { + // since we don't know any absolute position, we + // only distinguish between 0 and !0. Therefore pixel + // can be handled like all other units. + + bool nonZero = std::trunc(pExpr->GetNumber()) != 0.0; + if( !bHori ) + { + ePos = nonZero ? GPOS_MM : GPOS_LT; + bHori = true; + } + else if( !bVert ) + { + MergeVert( ePos, (nonZero ? GPOS_LM : GPOS_LT) ); + bVert = true; + } + } + break; + + case CSS1_PERCENTAGE: + { + // the percentage is converted to an enum + + sal_uInt16 nPerc = static_cast<sal_uInt16>(pExpr->GetNumber()); + if( !bHori ) + { + ePos = nPerc < 25 ? GPOS_LT + : (nPerc < 75 ? GPOS_MM + : GPOS_RB); + } + else if( !bVert ) + { + SvxGraphicPosition eVert = + nPerc < 25 ? GPOS_LT: (nPerc < 75 ? GPOS_LM + : GPOS_LB); + MergeVert( ePos, eVert ); + } + } + break; + + case CSS1_IDENT: + case CSS1_HEXCOLOR: + case CSS1_STRING: // because of MS-IE + { + sal_uInt16 nEnum; + const OUString &rValue = pExpr->GetString(); + if( rValue.equalsIgnoreAsciiCase( "transparent" ) ) + { + bTransparent = true; + } + if( SvxCSS1Parser::GetEnum( aBGRepeatTable, rValue, nEnum ) ) + { + eRepeat = static_cast<SvxGraphicPosition>(nEnum); + } + else if( SvxCSS1Parser::GetEnum( aBGHoriPosTable, rValue, nEnum ) ) + { + // <position>, horizontal + MergeHori( ePos, static_cast<SvxGraphicPosition>(nEnum) ); + } + else if( SvxCSS1Parser::GetEnum( aBGVertPosTable, rValue, nEnum ) ) + { + // <position>, vertical + MergeVert( ePos, static_cast<SvxGraphicPosition>(nEnum) ); + } + else if( !bColor ) + { + // <color> + bColor = pExpr->GetColor( aColor ); + } + // <scroll> we don't know + } + break; + + default: + ; + } + + pExpr = pExpr->GetNext(); + } + + // transparent beats everything + if( bTransparent ) + { + bColor = false; + aURL.clear(); + } + + // repeat has priority over a position + if( GPOS_NONE == eRepeat ) + eRepeat = ePos; + + if( bTransparent || bColor || !aURL.isEmpty() ) + { + SvxBrushItem aBrushItem( aItemIds.nBrush ); + + if( bTransparent ) + aBrushItem.SetColor( COL_TRANSPARENT); + else if( bColor ) + aBrushItem.SetColor( aColor ); + + if( !aURL.isEmpty() ) + { + aBrushItem.SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject( rParser.GetBaseURL()), aURL, Link<OUString *, bool>(), false ) ); + aBrushItem.SetGraphicPos( eRepeat ); + } + + rItemSet.Put( aBrushItem ); + } +} + +static void ParseCSS1_background_color( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + Color aColor; + + bool bColor = false, bTransparent = false; + + switch( pExpr->GetType() ) + { + case CSS1_RGB: + bColor = pExpr->GetColor( aColor ); + break; + case CSS1_IDENT: + case CSS1_HEXCOLOR: + case CSS1_STRING: // because of MS-IE + if( pExpr->GetString().equalsIgnoreAsciiCase( "transparent" ) ) + { + bTransparent = true; + } + else + { + // <color> + bColor = pExpr->GetColor( aColor ); + } + break; + default: + ; + } + + if( bTransparent || bColor ) + { + SvxBrushItem aBrushItem( aItemIds.nBrush ); + + if( bTransparent ) + aBrushItem.SetColor( COL_TRANSPARENT ); + else if( bColor ) + aBrushItem.SetColor( aColor); + + rItemSet.Put( aBrushItem ); + } +} + +static void ParseCSS1_line_height( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + sal_uInt16 nHeight = 0; + sal_uInt16 nPropHeight = 0; + + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + nHeight = static_cast<sal_uInt16>(pExpr->GetULength()); + break; + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPHeight = static_cast<long>(fHeight); + long nPWidth = 0; + SvxCSS1Parser::PixelToTwip(nPWidth, nPHeight); + nHeight = static_cast<sal_uInt16>(nPHeight); + } + } + break; + case CSS1_PERCENTAGE: + { + nPropHeight = static_cast<sal_uInt16>(pExpr->GetNumber()); + } + break; + case CSS1_NUMBER: + { + nPropHeight = static_cast<sal_uInt16>(pExpr->GetNumber() * 100); + } + break; + default: + ; + } + + if( nHeight ) + { + if( nHeight < SvxCSS1Parser::GetMinFixLineSpace() ) + nHeight = SvxCSS1Parser::GetMinFixLineSpace(); + SvxLineSpacingItem aLSItem( nHeight, aItemIds.nLineSpacing ); + aLSItem.SetLineHeight( nHeight ); + // interpret <line-height> attribute as minimum line height + aLSItem.SetLineSpaceRule( SvxLineSpaceRule::Min ); + aLSItem.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off ); + rItemSet.Put( aLSItem ); + } + else if( nPropHeight ) + { + SvxLineSpacingItem aLSItem( nPropHeight, aItemIds.nLineSpacing ); + aLSItem.SetLineSpaceRule( SvxLineSpaceRule::Auto ); + if( 100 == nPropHeight ) + aLSItem.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off ); + else + aLSItem.SetPropLineSpace( nPropHeight ); + rItemSet.Put( aLSItem ); + } + +} + +static void ParseCSS1_list_style_type( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + if( pExpr->GetType() == CSS1_IDENT ) + { + const OUString& rValue = pExpr->GetString(); + + // values are context-dependent, so fill both + sal_uInt16 nEnum; + if( SvxCSS1Parser::GetEnum( aNumberStyleTable, rValue, nEnum ) ) + { + rPropInfo.m_bNumbering = true; + rPropInfo.m_nNumberingType = static_cast<SvxNumType>(nEnum); + } + if( SvxCSS1Parser::GetEnum( aBulletStyleTable, rValue, nEnum ) ) + { + rPropInfo.m_bBullet = true; + rPropInfo.m_cBulletChar = nEnum; + } + } +} + +static void ParseCSS1_font( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + FontItalic eItalic = ITALIC_NONE; + SvxCaseMap eCaseMap = SvxCaseMap::NotMapped; + FontWeight eWeight = WEIGHT_NORMAL; + + // [ <font-style> || <font-variant> || <font-weight> ] ? + while( pExpr && !pExpr->GetOp() && + (CSS1_IDENT==pExpr->GetType() || + CSS1_STRING==pExpr->GetType() || + CSS1_NUMBER==pExpr->GetType()) ) + { + if( CSS1_IDENT==pExpr->GetType() || + CSS1_STRING==pExpr->GetType() ) + { + const OUString& rValue = pExpr->GetString(); + + sal_uInt16 nEnum; + + if( SvxCSS1Parser::GetEnum( aFontStyleTable, rValue, nEnum ) ) + { + eItalic = static_cast<FontItalic>(nEnum); + } + else if( SvxCSS1Parser::GetEnum( aFontVariantTable, rValue, nEnum ) ) + { + eCaseMap = static_cast<SvxCaseMap>(nEnum); + } + else if( SvxCSS1Parser::GetEnum( aFontWeightTable, rValue, nEnum ) ) + { + eWeight = static_cast<FontWeight>(nEnum); + } + } + else + { + eWeight = static_cast<sal_uInt16>(pExpr->GetNumber()) > 400 ? WEIGHT_BOLD + : WEIGHT_NORMAL; + } + + pExpr = pExpr->GetNext(); + } + + if( !pExpr || pExpr->GetOp() ) + return; + + // Since "font" resets all values for which nothing is specified, + // we do it here. + SvxPostureItem aPosture( eItalic, aItemIds.nPosture ); + rItemSet.Put( aPosture ); + aPosture.SetWhich( aItemIds.nPostureCJK ); + rItemSet.Put( aPosture ); + aPosture.SetWhich( aItemIds.nPostureCTL ); + rItemSet.Put( aPosture ); + + rItemSet.Put( SvxCaseMapItem( eCaseMap, aItemIds.nCaseMap ) ); + + SvxWeightItem aWeight( eWeight, aItemIds.nWeight ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCJK ); + rItemSet.Put( aWeight ); + aWeight.SetWhich( aItemIds.nWeightCTL ); + rItemSet.Put( aWeight ); + + // font-size + CSS1Expression aExpr( pExpr->GetType(), pExpr->GetString(), + pExpr->GetNumber() ); + ParseCSS1_font_size( &aExpr, rItemSet, rPropInfo, rParser ); + pExpr = pExpr->GetNext(); + + if( !pExpr ) + return; + + // [ '/' line-height ]? + if( '/' == pExpr->GetOp() ) + { + // '/' line-height + aExpr.Set( pExpr->GetType(), pExpr->GetString(), pExpr->GetNumber() ); + ParseCSS1_line_height( &aExpr, rItemSet, rPropInfo, rParser ); + + pExpr = pExpr->GetNext(); + } + + if( !pExpr || pExpr->GetOp() ) + return; + + // font-family + ParseCSS1_font_family( pExpr, rItemSet, rPropInfo, rParser ); +} + +static void ParseCSS1_letter_spacing( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + rItemSet.Put( SvxKerningItem( static_cast<short>(pExpr->GetSLength()), + aItemIds.nKerning ) ); + break; + + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPWidth = static_cast<long>(fHeight); + long nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + rItemSet.Put( SvxKerningItem( static_cast<short>(nPWidth), aItemIds.nKerning ) ); + } + } + break; + + case CSS1_NUMBER: + if( pExpr->GetNumber() == 0 ) + { + // normally unnecessary, but we are tolerant + rItemSet.Put( SvxKerningItem( short(0), aItemIds.nKerning ) ); + } + break; + + case CSS1_IDENT: + case CSS1_STRING: // As a precaution also MS-IE + if( pExpr->GetString().equalsIgnoreAsciiCase( "normal" ) ) + { + rItemSet.Put( SvxKerningItem( short(0), aItemIds.nKerning ) ); + } + break; + default: + ; + } +} + +static void ParseCSS1_text_decoration( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + bool bUnderline = false; + bool bOverline = false; + bool bCrossedOut = false; + bool bBlink = false; + bool bBlinkOn = false; + FontLineStyle eUnderline = LINESTYLE_NONE; + FontLineStyle eOverline = LINESTYLE_NONE; + FontStrikeout eCrossedOut = STRIKEOUT_NONE; + + // the value can contain two values! And MS-IE also strings + while( pExpr && (pExpr->GetType() == CSS1_IDENT || + pExpr->GetType() == CSS1_STRING) && !pExpr->GetOp() ) + { + OUString aValue = pExpr->GetString().toAsciiLowerCase(); + bool bKnown = false; + + switch( aValue[0] ) + { + case 'n': + if( aValue == "none" ) + { + bUnderline = true; + eUnderline = LINESTYLE_NONE; + + bOverline = true; + eOverline = LINESTYLE_NONE; + + bCrossedOut = true; + eCrossedOut = STRIKEOUT_NONE; + + bBlink = true; + bBlinkOn = false; + + bKnown = true; + } + break; + + case 'u': + if( aValue == "underline" ) + { + bUnderline = true; + eUnderline = LINESTYLE_SINGLE; + + bKnown = true; + } + break; + + case 'o': + if( aValue == "overline" ) + { + bOverline = true; + eOverline = LINESTYLE_SINGLE; + + bKnown = true; + } + break; + + case 'l': + if( aValue == "line-through" ) + { + bCrossedOut = true; + eCrossedOut = STRIKEOUT_SINGLE; + + bKnown = true; + } + break; + + case 'b': + if( aValue == "blink" ) + { + bBlink = true; + bBlinkOn = true; + + bKnown = true; + } + break; + } + + if( !bKnown ) + { + bUnderline = true; + eUnderline = LINESTYLE_SINGLE; + } + + pExpr = pExpr->GetNext(); + } + + if( bUnderline ) + rItemSet.Put( SvxUnderlineItem( eUnderline, aItemIds.nUnderline ) ); + + if( bOverline ) + rItemSet.Put( SvxOverlineItem( eOverline, aItemIds.nOverline ) ); + + if( bCrossedOut ) + rItemSet.Put( SvxCrossedOutItem( eCrossedOut, aItemIds.nCrossedOut ) ); + + if( bBlink ) + rItemSet.Put( SvxBlinkItem( bBlinkOn, aItemIds.nBlink ) ); +} + +static void ParseCSS1_text_align( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + if( CSS1_IDENT==pExpr->GetType() || + CSS1_STRING==pExpr->GetType() ) // MS-IE, again + { + sal_uInt16 nAdjust; + if( SvxCSS1Parser::GetEnum( aTextAlignTable, pExpr->GetString(), + nAdjust ) ) + { + rItemSet.Put( SvxAdjustItem( static_cast<SvxAdjust>(nAdjust), + aItemIds.nAdjust ) ); + } + } +} + +static void ParseCSS1_text_indent( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + short nIndent = 0; + bool bSet = false; + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + nIndent = static_cast<short>(pExpr->GetSLength()); + bSet = true; + break; + case CSS1_PIXLENGTH: + { + double fWidth = pExpr->GetNumber(); + if (fWidth < SAL_MAX_INT32/2.0 && fWidth > SAL_MIN_INT32/2.0) + { + long nPWidth = static_cast<long>(fWidth); + long nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + nIndent = static_cast<short>(nPWidth); + bSet = true; + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nLRSpace, false, + &pItem ) ) + { + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem*>(pItem) ); + aLRItem.SetTextFirstLineOffset( nIndent ); + rItemSet.Put( aLRItem ); + } + else + { + SvxLRSpaceItem aLRItem( aItemIds.nLRSpace ); + aLRItem.SetTextFirstLineOffset( nIndent ); + rItemSet.Put( aLRItem ); + } + rPropInfo.m_bTextIndent = true; + } +} + +static void ParseCSS1_margin_left( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + long nLeft = 0; + bool bSet = false; + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + nLeft = pExpr->GetSLength(); + bSet = true; + } + break; + case CSS1_PIXLENGTH: + { + double fLeft = pExpr->GetNumber(); + if (fLeft < SAL_MAX_INT32/2.0 && fLeft > SAL_MIN_INT32/2.0) + { + nLeft = static_cast<long>(fLeft); + long nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nLeft, nPHeight ); + bSet = true; + } + else + { + SAL_WARN("sw.html", "out-of-size pxlength: " << fLeft); + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + rPropInfo.m_nLeftMargin = nLeft; + if( nLeft < 0 ) + nLeft = 0; + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nLRSpace, false, + &pItem ) ) + { + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem*>(pItem) ); + aLRItem.SetTextLeft( static_cast<sal_uInt16>(nLeft) ); + rItemSet.Put( aLRItem ); + } + else + { + SvxLRSpaceItem aLRItem( aItemIds.nLRSpace ); + aLRItem.SetTextLeft( static_cast<sal_uInt16>(nLeft) ); + rItemSet.Put( aLRItem ); + } + rPropInfo.m_bLeftMargin = true; + } +} + +static void ParseCSS1_margin_right( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + long nRight = 0; + bool bSet = false; + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + nRight = pExpr->GetSLength(); + bSet = true; + } + break; + case CSS1_PIXLENGTH: + { + double fRight = pExpr->GetNumber(); + if (fRight < SAL_MAX_INT32/2.0 && fRight > SAL_MIN_INT32/2.0) + { + nRight = static_cast<long>(fRight); + long nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nRight, nPHeight ); + bSet = true; + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + rPropInfo.m_nRightMargin = nRight; + if( nRight < 0 ) + nRight = 0; + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nLRSpace, false, + &pItem ) ) + { + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem*>(pItem) ); + aLRItem.SetRight( static_cast<sal_uInt16>(nRight) ); + rItemSet.Put( aLRItem ); + } + else + { + SvxLRSpaceItem aLRItem( aItemIds.nLRSpace ); + aLRItem.SetRight( static_cast<sal_uInt16>(nRight) ); + rItemSet.Put( aLRItem ); + } + rPropInfo.m_bRightMargin = true; + } +} + +static void ParseCSS1_margin_top( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + assert(pExpr && "no expression"); + + sal_uInt16 nUpper = 0; + bool bSet = false; + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + long nTmp = pExpr->GetSLength(); + if( nTmp < 0 ) + nTmp = 0; + nUpper = static_cast<sal_uInt16>(nTmp); + bSet = true; + } + break; + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPWidth = 0; + long nPHeight = static_cast<long>(fHeight); + if( nPHeight < 0 ) + nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + nUpper = static_cast<sal_uInt16>(nPHeight); + bSet = true; + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nULSpace, false, + &pItem ) ) + { + SvxULSpaceItem aULItem( *static_cast<const SvxULSpaceItem*>(pItem) ); + aULItem.SetUpper( nUpper ); + rItemSet.Put( aULItem ); + } + else + { + SvxULSpaceItem aULItem( aItemIds.nULSpace ); + aULItem.SetUpper( nUpper ); + rItemSet.Put( aULItem ); + } + rPropInfo.m_bTopMargin = true; + } +} + +static void ParseCSS1_margin_bottom( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + sal_uInt16 nLower = 0; + bool bSet = false; + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + long nTmp = pExpr->GetSLength(); + if( nTmp < 0 ) + nTmp = 0; + nLower = static_cast<sal_uInt16>(nTmp); + bSet = true; + } + break; + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPWidth = 0; + long nPHeight = static_cast<long>(fHeight); + if( nPHeight < 0 ) + nPHeight = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + nLower = static_cast<sal_uInt16>(nPHeight); + bSet = true; + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nULSpace, false, + &pItem ) ) + { + SvxULSpaceItem aULItem( *static_cast<const SvxULSpaceItem*>(pItem) ); + aULItem.SetLower( nLower ); + rItemSet.Put( aULItem ); + } + else + { + SvxULSpaceItem aULItem( aItemIds.nULSpace ); + aULItem.SetLower( nLower ); + rItemSet.Put( aULItem ); + } + rPropInfo.m_bBottomMargin = true; + } +} + +static void ParseCSS1_margin( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + long nMargins[4] = { 0, 0, 0, 0 }; + bool bSetMargins[4] = { false, false, false, false }; + + for( int i=0; pExpr && i<4 && !pExpr->GetOp(); ++i ) + { + bool bSetThis = false; + long nMargin = 0; + + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + nMargin = pExpr->GetSLength(); + bSetThis = true; + } + break; + case CSS1_PIXLENGTH: + { + double fMargin = pExpr->GetNumber(); + if (fMargin < SAL_MAX_INT32/2.0 && fMargin > SAL_MIN_INT32/2.0) + { + nMargin = static_cast<long>(fMargin); + long nPWidth = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nMargin ); + bSetThis = true; + } + else + { + SAL_WARN("sw.html", "out-of-size pxlength: " << fMargin); + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSetThis ) + { + // 0 = top + // 1 = right + // 2 = bottom + // 3 = left + switch( i ) + { + case 0: + nMargins[0] = nMargins[1] =nMargins[2] = nMargins[3] = nMargin; + bSetMargins[0] = bSetMargins[1] = + bSetMargins[2] = bSetMargins[3] = true; + break; + case 1: + nMargins[1] = nMargins[3] = nMargin; // right + left + bSetMargins[1] = bSetMargins[3] = true; + break; + case 2: + nMargins[2] = nMargin; // bottom + bSetMargins[2] = true; + break; + case 3: + nMargins[3] = nMargin; // left + bSetMargins[3] = true; + break; + } + } + pExpr = pExpr->GetNext(); + } + + if( bSetMargins[3] || bSetMargins[1] ) + { + if( bSetMargins[3] ) + { + rPropInfo.m_bLeftMargin = true; + rPropInfo.m_nLeftMargin = nMargins[3]; + if( nMargins[3] < 0 ) + nMargins[3] = 0; + } + if( bSetMargins[1] ) + { + rPropInfo.m_bRightMargin = true; + rPropInfo.m_nRightMargin = nMargins[1]; + if( nMargins[1] < 0 ) + nMargins[1] = 0; + } + + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nLRSpace, false, + &pItem ) ) + { + SvxLRSpaceItem aLRItem( *static_cast<const SvxLRSpaceItem*>(pItem) ); + if( bSetMargins[3] ) + aLRItem.SetLeft( static_cast<sal_uInt16>(nMargins[3]) ); + if( bSetMargins[1] ) + aLRItem.SetRight( static_cast<sal_uInt16>(nMargins[1]) ); + rItemSet.Put( aLRItem ); + } + else + { + SvxLRSpaceItem aLRItem( aItemIds.nLRSpace ); + if( bSetMargins[3] ) + aLRItem.SetLeft( static_cast<sal_uInt16>(nMargins[3]) ); + if( bSetMargins[1] ) + aLRItem.SetRight( static_cast<sal_uInt16>(nMargins[1]) ); + rItemSet.Put( aLRItem ); + } + } + + if( bSetMargins[0] || bSetMargins[2] ) + { + if( nMargins[0] < 0 ) + nMargins[0] = 0; + if( nMargins[2] < 0 ) + nMargins[2] = 0; + + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( aItemIds.nULSpace, false, + &pItem ) ) + { + SvxULSpaceItem aULItem( *static_cast<const SvxULSpaceItem*>(pItem) ); + if( bSetMargins[0] ) + aULItem.SetUpper( static_cast<sal_uInt16>(nMargins[0]) ); + if( bSetMargins[2] ) + aULItem.SetLower( static_cast<sal_uInt16>(nMargins[2]) ); + rItemSet.Put( aULItem ); + } + else + { + SvxULSpaceItem aULItem( aItemIds.nULSpace ); + if( bSetMargins[0] ) + aULItem.SetUpper( static_cast<sal_uInt16>(nMargins[0]) ); + if( bSetMargins[2] ) + aULItem.SetLower( static_cast<sal_uInt16>(nMargins[2]) ); + rItemSet.Put( aULItem ); + } + + rPropInfo.m_bTopMargin |= bSetMargins[0]; + rPropInfo.m_bBottomMargin |= bSetMargins[2]; + } +} + +static bool ParseCSS1_padding_xxx( const CSS1Expression *pExpr, + SvxCSS1PropertyInfo& rPropInfo, + SvxBoxItemLine nWhichLine ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + bool bSet = false; + sal_uInt16 nDist = 0; + + switch( pExpr->GetType() ) + { + case CSS1_LENGTH: + { + long nTmp = pExpr->GetSLength(); + if( nTmp < 0 ) + nTmp = 0; + else if( nTmp > SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1 ) + nTmp = SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1; + nDist = static_cast<sal_uInt16>(nTmp); + bSet = true; + } + break; + case CSS1_PIXLENGTH: + { + double fWidth = pExpr->GetNumber(); + if (fWidth < SAL_MAX_INT32/2.0 && fWidth > SAL_MIN_INT32/2.0) + { + long nPWidth = static_cast<long>(fWidth); + long nPHeight = 0; + if( nPWidth < 0 ) + nPWidth = 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + if( nPWidth > SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1 ) + nPWidth = SvxCSS1PropertyInfo::UNSET_BORDER_DISTANCE-1; + nDist = static_cast<sal_uInt16>(nPWidth); + bSet = true; + } + } + break; + case CSS1_PERCENTAGE: + // we aren't able + break; + default: + ; + } + + if( bSet ) + { + switch( nWhichLine ) + { + case SvxBoxItemLine::TOP: rPropInfo.m_nTopBorderDistance = nDist; break; + case SvxBoxItemLine::BOTTOM: rPropInfo.m_nBottomBorderDistance = nDist;break; + case SvxBoxItemLine::LEFT: rPropInfo.m_nLeftBorderDistance = nDist; break; + case SvxBoxItemLine::RIGHT: rPropInfo.m_nRightBorderDistance = nDist; break; + } + } + + return bSet; +} + +static void ParseCSS1_padding_top( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::TOP ); +} + +static void ParseCSS1_padding_bottom( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::BOTTOM ); +} + +static void ParseCSS1_padding_left( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::LEFT ); +} + +static void ParseCSS1_padding_right( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_padding_xxx( pExpr, rPropInfo, SvxBoxItemLine::RIGHT ); +} + +static void ParseCSS1_padding( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + int n=0; + while( n<4 && pExpr && !pExpr->GetOp() ) + { + SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT; + if( ParseCSS1_padding_xxx( pExpr, rPropInfo, nLine ) ) + { + if( n==0 ) + { + rPropInfo.m_nTopBorderDistance = rPropInfo.m_nBottomBorderDistance; + rPropInfo.m_nLeftBorderDistance = rPropInfo.m_nTopBorderDistance; + } + if( n <= 1 ) + rPropInfo.m_nRightBorderDistance = rPropInfo.m_nLeftBorderDistance; + } + + pExpr = pExpr->GetNext(); + n++; + } +} + +static void ParseCSS1_border_xxx( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/, + SvxBoxItemLine nWhichLine, bool bAll ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + sal_uInt16 nWidth = USHRT_MAX; // line thickness + sal_uInt16 nNWidth = 1; // named line thickness (and default) + CSS1BorderStyle eStyle = CSS1_BS_NONE; // line style + Color aColor; + bool bColor = false; + + while( pExpr && !pExpr->GetOp() ) + { + switch( pExpr->GetType() ) + { + case CSS1_RGB: + case CSS1_HEXCOLOR: + if( pExpr->GetColor( aColor ) ) + bColor = true; + break; + + case CSS1_IDENT: + { + const OUString& rValue = pExpr->GetString(); + sal_uInt16 nValue; + if( SvxCSS1Parser::GetEnum( aBorderWidthTable, rValue, nValue ) ) + { + nNWidth = nValue; + } + else if( SvxCSS1Parser::GetEnum( aBorderStyleTable, rValue, nValue ) ) + { + eStyle = static_cast<CSS1BorderStyle>(nValue); + } + else if( pExpr->GetColor( aColor ) ) + { + bColor = true; + } + } + break; + + case CSS1_LENGTH: + nWidth = static_cast<sal_uInt16>(pExpr->GetULength()); + break; + + case CSS1_PIXLENGTH: + { + // One Pixel becomes a hairline (is prettier) + double fWidth = pExpr->GetNumber(); + if (fWidth > 1.0 && fWidth < SAL_MAX_INT32/2.0) + { + bool bHori = nWhichLine == SvxBoxItemLine::TOP || + nWhichLine == SvxBoxItemLine::BOTTOM; + + long nPWidth = bHori ? 0 : fWidth; + long nPHeight = bHori ? fWidth : 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + nWidth = static_cast<sal_uInt16>(bHori ? nPHeight : nPWidth); + } + else + nWidth = 1; + } + break; + + default: + ; + } + + pExpr = pExpr->GetNext(); + } + + for( int i=0; i<4; ++i ) + { + SvxBoxItemLine nLine = SvxBoxItemLine::TOP; + switch( i ) + { + case 0: nLine = SvxBoxItemLine::TOP; break; + case 1: nLine = SvxBoxItemLine::BOTTOM; break; + case 2: nLine = SvxBoxItemLine::LEFT; break; + case 3: nLine = SvxBoxItemLine::RIGHT; break; + } + + if( bAll || nLine == nWhichLine ) + { + SvxCSS1BorderInfo *pInfo = rPropInfo.GetBorderInfo( nLine ); + pInfo->eStyle = eStyle; + pInfo->nAbsWidth = nWidth; + pInfo->nNamedWidth = nNWidth; + if( bColor ) + pInfo->aColor = aColor; + } + } +} + +static void ParseCSS1_border_xxx_width( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/, + SvxBoxItemLine nWhichLine ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + sal_uInt16 nWidth = USHRT_MAX; // line thickness + sal_uInt16 nNWidth = 1; // named line thickness (and default) + + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + { + sal_uInt16 nValue; + if( SvxCSS1Parser::GetEnum( aBorderWidthTable, pExpr->GetString(), nValue ) ) + { + nNWidth = nValue; + } + } + break; + + case CSS1_LENGTH: + nWidth = static_cast<sal_uInt16>(pExpr->GetULength()); + break; + + case CSS1_PIXLENGTH: + { + double fLength = pExpr->GetNumber(); + if (fLength < SAL_MAX_INT32/2.0 && fLength > SAL_MIN_INT32/2.0) + { + long nWidthL = static_cast<long>(fLength); + + bool bHori = nWhichLine == SvxBoxItemLine::TOP || + nWhichLine == SvxBoxItemLine::BOTTOM; + + long nPWidth = bHori ? 0 : nWidthL; + long nPHeight = bHori ? nWidthL : 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + nWidth = static_cast<sal_uInt16>(bHori ? nPHeight : nPWidth); + } + } + break; + + default: + ; + } + + SvxCSS1BorderInfo *pInfo = rPropInfo.GetBorderInfo( nWhichLine ); + pInfo->nAbsWidth = nWidth; + pInfo->nNamedWidth = nNWidth; +} + +static void ParseCSS1_border_top_width( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP ); +} + +static void ParseCSS1_border_right_width( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::RIGHT ); +} + +static void ParseCSS1_border_bottom_width( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::BOTTOM ); +} + +static void ParseCSS1_border_left_width( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::LEFT ); +} + +static void ParseCSS1_border_width( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + sal_uInt16 n=0; + while( n<4 && pExpr && !pExpr->GetOp() ) + { + SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT; + ParseCSS1_border_xxx_width( pExpr, rItemSet, rPropInfo, rParser, nLine ); + rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_WIDTH ); + + pExpr = pExpr->GetNext(); + n++; + } +} + +static void ParseCSS1_border_color( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + sal_uInt16 n=0; + while( n<4 && pExpr && !pExpr->GetOp() ) + { + SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT; + Color aColor; + switch( pExpr->GetType() ) + { + case CSS1_RGB: + case CSS1_HEXCOLOR: + case CSS1_IDENT: + if( pExpr->GetColor( aColor ) ) + rPropInfo.GetBorderInfo( nLine )->aColor = aColor; + break; + default: + ; + } + rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_COLOR ); + + pExpr = pExpr->GetNext(); + n++; + } +} + +static void ParseCSS1_border_style( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + sal_uInt16 n=0; + while( n<4 && pExpr && !pExpr->GetOp() ) + { + SvxBoxItemLine nLine = n==0 || n==2 ? SvxBoxItemLine::BOTTOM : SvxBoxItemLine::LEFT; + sal_uInt16 nValue = 0; + if( CSS1_IDENT==pExpr->GetType() && + SvxCSS1Parser::GetEnum( aBorderStyleTable, pExpr->GetString(), + nValue ) ) + { + rPropInfo.GetBorderInfo( nLine )->eStyle = static_cast<CSS1BorderStyle>(nValue); + } + rPropInfo.CopyBorderInfo( n, SVX_CSS1_BORDERINFO_STYLE ); + + pExpr = pExpr->GetNext(); + n++; + } +} + +static void ParseCSS1_border_top( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP, false ); +} + +static void ParseCSS1_border_right( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::RIGHT, false ); +} + +static void ParseCSS1_border_bottom( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::BOTTOM, false ); +} + +static void ParseCSS1_border_left( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::LEFT, false ); +} + +static void ParseCSS1_border( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& rParser ) +{ + ParseCSS1_border_xxx( pExpr, rItemSet, rPropInfo, rParser, SvxBoxItemLine::TOP, true ); +} + +static void ParseCSS1_float( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + if( CSS1_IDENT==pExpr->GetType() ) + { + sal_uInt16 nFloat; + if( SvxCSS1Parser::GetEnum( aFloatTable, pExpr->GetString(), nFloat ) ) + rPropInfo.m_eFloat = static_cast<SvxAdjust>(nFloat); + } +} + +static void ParseCSS1_position( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + OSL_ENSURE( pExpr, "no expression" ); + + if( CSS1_IDENT==pExpr->GetType() ) + { + sal_uInt16 nPos; + if( SvxCSS1Parser::GetEnum( aPositionTable, pExpr->GetString(), nPos ) ) + rPropInfo.m_ePosition = static_cast<SvxCSS1Position>(nPos); + } +} + +static void ParseCSS1_length( const CSS1Expression *pExpr, + long& rLength, + SvxCSS1LengthType& rLengthType, + bool bHori ) +{ + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + if( pExpr->GetString().equalsIgnoreAsciiCase( "auto" ) ) + { + rLength = 0; + rLengthType = SVX_CSS1_LTYPE_AUTO; + } + break; + + case CSS1_LENGTH: + rLength = pExpr->GetSLength(); + rLengthType = SVX_CSS1_LTYPE_TWIP; + break; + + case CSS1_PIXLENGTH: + case CSS1_NUMBER: // because of Netscape and IE + { + double fLength = pExpr->GetNumber(); + if (fLength < SAL_MAX_INT32/2.0 && fLength > SAL_MIN_INT32/2.0) + { + long nWidthL = static_cast<long>(fLength); + long nPWidth = bHori ? 0 : nWidthL; + long nPHeight = bHori ? nWidthL : 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + rLength = (bHori ? nPHeight : nPWidth); + rLengthType = SVX_CSS1_LTYPE_TWIP; + } + } + break; + + case CSS1_PERCENTAGE: + rLength = static_cast<long>(pExpr->GetNumber()); + if( rLength > 100 ) + rLength = 100; + rLengthType = SVX_CSS1_LTYPE_PERCENTAGE; + break; + + default: + ; + } +} + +static void ParseCSS1_width( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_length( pExpr, rPropInfo.m_nWidth, rPropInfo.m_eWidthType, true ); +} + +static void ParseCSS1_height( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_length( pExpr, rPropInfo.m_nHeight, rPropInfo.m_eHeightType, false ); +} + +static void ParseCSS1_left( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_length( pExpr, rPropInfo.m_nLeft, rPropInfo.m_eLeftType, true ); +} + +static void ParseCSS1_top( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_length( pExpr, rPropInfo.m_nTop, rPropInfo.m_eTopType, false ); +} + +// Feature: PrintExt +static void ParseCSS1_size( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + int n=0; + while( n<2 && pExpr && !pExpr->GetOp() ) + { + switch( pExpr->GetType() ) + { + case CSS1_IDENT: + { + sal_uInt16 nValue; + if( SvxCSS1Parser::GetEnum( aSizeTable, pExpr->GetString(), + nValue ) ) + { + rPropInfo.m_eSizeType = static_cast<SvxCSS1SizeType>(nValue); + } + } + break; + + case CSS1_LENGTH: + rPropInfo.m_nHeight = pExpr->GetSLength(); + if( n==0 ) + rPropInfo.m_nWidth = rPropInfo.m_nHeight; + rPropInfo.m_eSizeType = SVX_CSS1_STYPE_TWIP; + break; + + case CSS1_PIXLENGTH: + { + double fHeight = pExpr->GetNumber(); + if (fHeight < SAL_MAX_INT32/2.0 && fHeight > SAL_MIN_INT32/2.0) + { + long nPHeight = static_cast<long>(fHeight); + long nPWidth = n==0 ? nPHeight : 0; + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + rPropInfo.m_nHeight = nPHeight; + if( n==0 ) + rPropInfo.m_nWidth = nPWidth; + rPropInfo.m_eSizeType = SVX_CSS1_STYPE_TWIP; + } + break; + } + default: + ; + } + + pExpr = pExpr->GetNext(); + n++; + } +} + +static void ParseCSS1_page_break_xxx( const CSS1Expression *pExpr, + SvxCSS1PageBreak& rPBreak ) +{ + if( CSS1_IDENT == pExpr->GetType() ) + { + sal_uInt16 nValue; + if( SvxCSS1Parser::GetEnum( aPageBreakTable, pExpr->GetString(), + nValue ) ) + { + rPBreak = static_cast<SvxCSS1PageBreak>(nValue); + } + } +} + +static void ParseCSS1_page_break_before( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_page_break_xxx( pExpr, rPropInfo.m_ePageBreakBefore ); +} + +static void ParseCSS1_page_break_after( const CSS1Expression *pExpr, + SfxItemSet & /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, + const SvxCSS1Parser& /*rParser*/ ) +{ + ParseCSS1_page_break_xxx( pExpr, rPropInfo.m_ePageBreakAfter ); +} + +static void ParseCSS1_page_break_inside( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + SvxCSS1PageBreak eBreak(SVX_CSS1_PBREAK_NONE); + ParseCSS1_page_break_xxx( pExpr, eBreak ); + + bool bSetSplit = false, bSplit = true; + switch( eBreak ) + { + case SVX_CSS1_PBREAK_AUTO: + bSetSplit = true; + break; + case SVX_CSS1_PBREAK_AVOID: + bSplit = false; + bSetSplit = true; + break; + default: + ; + } + + if( bSetSplit ) + rItemSet.Put( SvxFormatSplitItem( bSplit, aItemIds.nFormatSplit ) ); +} + +static void ParseCSS1_widows( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + if( CSS1_NUMBER == pExpr->GetType() ) + { + sal_uInt8 nVal = pExpr->GetNumber() <= 255 + ? static_cast<sal_uInt8>(pExpr->GetNumber()) + : 255; + SvxWidowsItem aWidowsItem( nVal, aItemIds.nWidows ); + rItemSet.Put( aWidowsItem ); + } +} + +static void ParseCSS1_orphans( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + if( CSS1_NUMBER == pExpr->GetType() ) + { + sal_uInt8 nVal = pExpr->GetNumber() <= 255 + ? static_cast<sal_uInt8>(pExpr->GetNumber()) + : 255; + SvxOrphansItem aOrphansItem( nVal, aItemIds.nOrphans ); + rItemSet.Put( aOrphansItem ); + } +} + +static void ParseCSS1_so_language( const CSS1Expression *pExpr, + SfxItemSet &rItemSet, + SvxCSS1PropertyInfo& /*rPropInfo*/, + const SvxCSS1Parser& /*rParser*/ ) +{ + if( CSS1_IDENT == pExpr->GetType() || + CSS1_STRING == pExpr->GetType() ) + { + LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( pExpr->GetString() ); + if( LANGUAGE_DONTKNOW != eLang ) + { + SvxLanguageItem aLang( eLang, aItemIds.nLanguage ); + rItemSet.Put( aLang ); + aLang.SetWhich( aItemIds.nLanguageCJK ); + rItemSet.Put( aLang ); + aLang.SetWhich( aItemIds.nLanguageCTL ); + rItemSet.Put( aLang ); + } + } +} + +static void ParseCSS1_visibility(const CSS1Expression* pExpr, SfxItemSet& /*rItemSet*/, + SvxCSS1PropertyInfo& rPropInfo, const SvxCSS1Parser& /*rParser*/) +{ + if (pExpr->GetType() != CSS1_IDENT) + return; + + rPropInfo.m_bVisible = pExpr->GetString() != "hidden"; +} + +namespace { + +// the assignment of property to parsing function +struct CSS1PropEntry +{ + const char * pName; + FnParseCSS1Prop pFunc; +}; + +} + +#define CSS1_PROP_ENTRY(p) \ + { sCSS1_P_##p, ParseCSS1_##p } + +// the table with assignments +static CSS1PropEntry const aCSS1PropFnTab[] = +{ + CSS1_PROP_ENTRY(background), + CSS1_PROP_ENTRY(background_color), + CSS1_PROP_ENTRY(border), + CSS1_PROP_ENTRY(border_bottom), + CSS1_PROP_ENTRY(border_bottom_width), + CSS1_PROP_ENTRY(border_color), + CSS1_PROP_ENTRY(border_left), + CSS1_PROP_ENTRY(border_left_width), + CSS1_PROP_ENTRY(border_right), + CSS1_PROP_ENTRY(border_right_width), + CSS1_PROP_ENTRY(border_style), + CSS1_PROP_ENTRY(border_top), + CSS1_PROP_ENTRY(border_top_width), + CSS1_PROP_ENTRY(border_width), + CSS1_PROP_ENTRY(color), + CSS1_PROP_ENTRY(column_count), + CSS1_PROP_ENTRY(direction), + CSS1_PROP_ENTRY(float), + CSS1_PROP_ENTRY(font), + CSS1_PROP_ENTRY(font_family), + CSS1_PROP_ENTRY(font_size), + CSS1_PROP_ENTRY(font_style), + CSS1_PROP_ENTRY(font_variant), + CSS1_PROP_ENTRY(font_weight), + CSS1_PROP_ENTRY(height), + CSS1_PROP_ENTRY(left), + CSS1_PROP_ENTRY(letter_spacing), + CSS1_PROP_ENTRY(line_height), + CSS1_PROP_ENTRY(list_style_type), + CSS1_PROP_ENTRY(margin), + CSS1_PROP_ENTRY(margin_bottom), + CSS1_PROP_ENTRY(margin_left), + CSS1_PROP_ENTRY(margin_right), + CSS1_PROP_ENTRY(margin_top), + CSS1_PROP_ENTRY(orphans), + CSS1_PROP_ENTRY(padding), + CSS1_PROP_ENTRY(padding_bottom), + CSS1_PROP_ENTRY(padding_left), + CSS1_PROP_ENTRY(padding_right), + CSS1_PROP_ENTRY(padding_top), + CSS1_PROP_ENTRY(page_break_after), + CSS1_PROP_ENTRY(page_break_before), + CSS1_PROP_ENTRY(page_break_inside), + CSS1_PROP_ENTRY(position), + CSS1_PROP_ENTRY(size), + CSS1_PROP_ENTRY(so_language), + CSS1_PROP_ENTRY(text_align), + CSS1_PROP_ENTRY(text_decoration), + CSS1_PROP_ENTRY(text_indent), + CSS1_PROP_ENTRY(text_transform), + CSS1_PROP_ENTRY(top), + CSS1_PROP_ENTRY(visibility), + CSS1_PROP_ENTRY(widows), + CSS1_PROP_ENTRY(width), +}; + +#if !defined NDEBUG +static bool CSS1PropEntryCompare( const CSS1PropEntry &lhs, const CSS1PropEntry &rhs) +{ + return strcmp(lhs.pName, rhs.pName) < 0; +} +#endif +static bool CSS1PropEntryFindCompare(CSS1PropEntry const & lhs, OUString const & s) +{ + return s.compareToIgnoreAsciiCaseAscii(lhs.pName) > 0; +} + +void SvxCSS1Parser::DeclarationParsed( const OUString& rProperty, + std::unique_ptr<CSS1Expression> pExpr ) +{ + OSL_ENSURE( pItemSet, "DeclarationParsed() without ItemSet" ); + + static bool bSortedPropFns = false; + + if( !bSortedPropFns ) + { + assert( std::is_sorted( std::begin(aCSS1PropFnTab), std::end(aCSS1PropFnTab), + CSS1PropEntryCompare ) ); + bSortedPropFns = true; + } + + auto it = std::lower_bound( std::begin(aCSS1PropFnTab), std::end(aCSS1PropFnTab), rProperty, + CSS1PropEntryFindCompare ); + if( it != std::end(aCSS1PropFnTab) && !CSS1PropEntryFindCompare(*it,rProperty) ) + { + it->pFunc( pExpr.get(), *pItemSet, *pPropInfo, *this ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/svxcss1.hxx b/sw/source/filter/html/svxcss1.hxx new file mode 100644 index 000000000..8bc1f47b3 --- /dev/null +++ b/sw/source/filter/html/svxcss1.hxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_SVXCSS1_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_SVXCSS1_HXX + +#include <svl/itemset.hxx> +#include <svx/flagsdef.hxx> +#include <editeng/svxenum.hxx> +#include <rtl/textenc.h> +#include "parcss1.hxx" +#include <o3tl/typed_flags_set.hxx> + +#include <array> +#include <map> +#include <memory> +#include <vector> + +class SfxItemPool; +class SvxBoxItem; +class FontList; +enum class SvxBoxItemLine; + +enum SvxCSS1Position +{ + SVX_CSS1_POS_NONE, // nothing specified + SVX_CSS1_POS_STATIC, // normal + SVX_CSS1_POS_ABSOLUTE, // absolute + SVX_CSS1_POS_RELATIVE, // relative +}; + +enum SvxCSS1LengthType +{ + SVX_CSS1_LTYPE_NONE, // nothing specified + SVX_CSS1_LTYPE_AUTO, // automatic + SVX_CSS1_LTYPE_TWIP, // twip + SVX_CSS1_LTYPE_PERCENTAGE, // percentage value +}; + +// Feature: PrintExt +enum SvxCSS1SizeType +{ + SVX_CSS1_STYPE_NONE, // nothing specified + SVX_CSS1_STYPE_AUTO, // automatic + SVX_CSS1_STYPE_TWIP, // twip + SVX_CSS1_STYPE_LANDSCAPE, // landscape + SVX_CSS1_STYPE_PORTRAIT, // portrait +}; + +enum SvxCSS1PageBreak +{ + SVX_CSS1_PBREAK_NONE, // nothing specified + SVX_CSS1_PBREAK_AUTO, // automatic + SVX_CSS1_PBREAK_ALWAYS, // always + SVX_CSS1_PBREAK_AVOID, // never + SVX_CSS1_PBREAK_LEFT, // next page is a left one + SVX_CSS1_PBREAK_RIGHT, // next page is a right one +}; + + +enum class Css1ScriptFlags { + Western = 0x01, + CJK = 0x02, + CTL = 0x04, + AllMask = Western | CJK | CTL, +}; +namespace o3tl { + template<> struct typed_flags<Css1ScriptFlags> : is_typed_flags<Css1ScriptFlags, 0x07> {}; +} + +struct CSS1PropertyEnum +{ + const char *pName; // property value + sal_uInt16 nEnum; // and the corresponding value of enum +}; + +namespace editeng { class SvxBorderLine; } + +#define SVX_CSS1_BORDERINFO_WIDTH 1 +#define SVX_CSS1_BORDERINFO_COLOR 2 +#define SVX_CSS1_BORDERINFO_STYLE 4 + +struct SvxCSS1BorderInfo; +class SvxCSS1PropertyInfo +{ + std::array<std::unique_ptr<SvxCSS1BorderInfo>,4> m_aBorderInfos; + + void DestroyBorderInfos(); + +public: + static constexpr sal_uInt16 UNSET_BORDER_DISTANCE = SAL_MAX_UINT16; + + OUString m_aId; // ID for bookmarks, frame, and so + + bool m_bTopMargin : 1; + bool m_bBottomMargin : 1; + + bool m_bLeftMargin : 1; + bool m_bRightMargin : 1; + bool m_bTextIndent : 1; + bool m_bNumbering : 1; + bool m_bBullet : 1; + + SvxAdjust m_eFloat; + + SvxCSS1Position m_ePosition; + + sal_uInt16 m_nTopBorderDistance; + sal_uInt16 m_nBottomBorderDistance; + sal_uInt16 m_nLeftBorderDistance; + sal_uInt16 m_nRightBorderDistance; + + SvxNumType m_nNumberingType; + sal_Unicode m_cBulletChar; + + sal_uInt16 m_nColumnCount; + + long m_nLeft, m_nTop; + long m_nWidth, m_nHeight; + long m_nLeftMargin, m_nRightMargin; + + SvxCSS1LengthType m_eLeftType, m_eTopType; + SvxCSS1LengthType m_eWidthType, m_eHeightType; + + SvxCSS1SizeType m_eSizeType; + + SvxCSS1PageBreak m_ePageBreakBefore; + SvxCSS1PageBreak m_ePageBreakAfter; + + bool m_bVisible = true; + + SvxCSS1PropertyInfo(); + SvxCSS1PropertyInfo( const SvxCSS1PropertyInfo& rProp ); + ~SvxCSS1PropertyInfo(); + + void Merge( const SvxCSS1PropertyInfo& rProp ); + + void Clear(); + + SvxCSS1BorderInfo *GetBorderInfo( SvxBoxItemLine nLine, bool bCreate=true ); + void CopyBorderInfo( SvxBoxItemLine nSrcLine, SvxBoxItemLine nDstLine, sal_uInt16 nWhat ); + void CopyBorderInfo( sal_uInt16 nCount, sal_uInt16 nWhat ); + + void SetBoxItem( SfxItemSet& rItemSet, sal_uInt16 nMinBorderDist, + const SvxBoxItem* pDflt=nullptr ); + +}; + +class SvxCSS1MapEntry +{ + SfxItemSet aItemSet; + SvxCSS1PropertyInfo aPropInfo; + +public: + SvxCSS1MapEntry( const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ); + + const SfxItemSet& GetItemSet() const { return aItemSet; } + SfxItemSet& GetItemSet() { return aItemSet; } + + const SvxCSS1PropertyInfo& GetPropertyInfo() const { return aPropInfo; } + SvxCSS1PropertyInfo& GetPropertyInfo() { return aPropInfo; } +}; + +// Class is processing the CSS1-Parser output by converting the CSS1 properties +// into SvxItem(Set). Also the selectors together with associated ItemSet are +// saved. +// A derived parser can suppress this for certain selectors by overriding +// the method StyleParsed. + +class SvxCSS1Parser : public CSS1Parser +{ + typedef std::vector<std::unique_ptr<CSS1Selector>> CSS1Selectors; + typedef std::map<OUString, std::unique_ptr<SvxCSS1MapEntry>> CSS1Map; + CSS1Selectors m_Selectors; // List of "open" Selectors + + CSS1Map m_Ids; + CSS1Map m_Classes; + CSS1Map m_Pages; + CSS1Map m_Tags; + + OUString sBaseURL; + + std::unique_ptr<SfxItemSet> pSheetItemSet; // item set of Style-Sheet + SfxItemSet *pItemSet; // current item set + + std::unique_ptr<SvxCSS1PropertyInfo> pSheetPropInfo; + SvxCSS1PropertyInfo *pPropInfo; + + static constexpr sal_uInt16 gnMinFixLineSpace = MM50/2; // minimum spacing for fixed line spacing + + rtl_TextEncoding eDfltEnc; + bool bIgnoreFontFamily; + std::vector<sal_uInt16> aWhichMap; // Which-Map of Parser + + using CSS1Parser::ParseStyleOption; + +protected: + + using CSS1Parser::ParseStyleSheet; + + // This method is called for every selector with according item set. + // For a selector multiple calls are possible. + // If true is returned then the item set resp. the selector isn't saved anymore! + // The ItemSet may be modified accordingly! + // The implementation returns false. + virtual void StyleParsed( const CSS1Selector *pSelector, + SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo ); + + /// Will be called when a Selector is parsed. If bFirst is true, + /// the content of the aItemSet will be copied into all recently + /// created Styles. + /// Derived classes should not override this method! + virtual void SelectorParsed( std::unique_ptr<CSS1Selector> pSelector, bool bFirst ) override; + + /// Will be called for every parsed Property. Adds the item to the + /// pItemSet. + /// Derived classes should not override this method! + virtual void DeclarationParsed( const OUString& rProperty, + std::unique_ptr<CSS1Expression> pExpr ) override; + +public: + + SvxCSS1Parser( SfxItemPool& rPool, + const OUString& rBaseURL, + sal_uInt16 const *pWhichIds, sal_uInt16 nWhichIds ); + virtual ~SvxCSS1Parser() override; + + bool IsIgnoreFontFamily() const { return bIgnoreFontFamily; } + void SetIgnoreFontFamily( bool bSet ) { bIgnoreFontFamily = bSet; } + + // Parse a style sheet. For every found selector a StyleParsed with + // according item set is called. + virtual bool ParseStyleSheet( const OUString& rIn ); + + // Parse style option. Here only the item set is filled. + void ParseStyleOption( const OUString& rIn, SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo ); + + // convert a string to enum value + static bool GetEnum( const CSS1PropertyEnum *pPropTable, + const OUString& rValue, sal_uInt16 &rEnum ); + + static void PixelToTwip( long &nWidth, long &nHeight ); + + // determine the font height of a certain font size (0-6) + virtual sal_uInt32 GetFontHeight( sal_uInt16 nSize ) const; + + virtual const FontList *GetFontList() const; + + const sal_uInt16 *GetWhichMap() const { return aWhichMap.data(); } + + static void InsertMapEntry( const OUString& rKey, const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp, CSS1Map& rMap ); + + void InsertId( const OUString& rId, const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ); + + const SvxCSS1MapEntry* GetId( const OUString& rId ) const; + + void InsertClass( const OUString& rClass, const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ); + + const SvxCSS1MapEntry* GetClass( const OUString& rClass ) const; + + void InsertPage( const OUString& rPage, bool bPseudo, + const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ); + + SvxCSS1MapEntry* GetPage( const OUString& rPage, bool bPseudo ); + + void InsertTag( const OUString& rTag, const SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rProp ); + + SvxCSS1MapEntry* GetTag( const OUString& rTag ); + + static void MergeStyles( const SfxItemSet& rSrcSet, + const SvxCSS1PropertyInfo& rSrcInfo, + SfxItemSet& rTargetSet, + SvxCSS1PropertyInfo& rTargetInfo, + bool bSmart ); + + static sal_uInt16 GetMinFixLineSpace() { return gnMinFixLineSpace; } + + virtual void SetDfltEncoding( rtl_TextEncoding eEnc ); + rtl_TextEncoding GetDfltEncoding() const { return eDfltEnc; } + + const OUString& GetBaseURL() const { return sBaseURL;} + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/swcss1.hxx b/sw/source/filter/html/swcss1.hxx new file mode 100644 index 000000000..b9f127c1e --- /dev/null +++ b/sw/source/filter/html/swcss1.hxx @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_SWCSS1_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_SWCSS1_HXX + +#include <svtools/htmltokn.h> +#include <tools/solar.h> + +#include <poolfmt.hxx> + +#include "svxcss1.hxx" + +class SwDoc; +class SwCharFormat; +class SwTextFormatColl; +class SvxBrushItem; +class SwFormatDrop; +class SwPageDesc; +class SwHTMLParser; + +// This header looks harmless, but includes still quite +// inconspicuous one or the other! On the other hand this class +// is rarely needed. Therefore its own header. + +class SwCSS1Parser : public SvxCSS1Parser +{ + SwDoc *m_pDoc; + SwHTMLParser const& m_rHTMLParser; + + sal_uLong m_aFontHeights[7]; + + sal_uInt16 m_nDropCapCnt; + + bool m_bIsNewDoc : 1; + + bool m_bBodyBGColorSet : 1; + bool m_bBodyBackgroundSet : 1; + bool m_bBodyTextSet : 1; + bool m_bBodyLinkSet : 1; + bool m_bBodyVLinkSet : 1; + + bool m_bSetFirstPageDesc : 1; + bool m_bSetRightPageDesc : 1; + + bool m_bTableHeaderTextCollSet : 1; + bool m_bTableTextCollSet : 1; + + bool m_bLinkCharFormatsSet : 1; + + const SwPageDesc* GetPageDesc( sal_uInt16 nPoolId, bool bCreate ); + + void SetTableTextColl( bool bHeader ); + void SetLinkCharFormats(); + +protected: + virtual void StyleParsed( const CSS1Selector *pSelector, + SfxItemSet& rItemSet, + SvxCSS1PropertyInfo& rPropInfo ) override; + + using CSS1Parser::ParseStyleSheet; + +public: + SwCSS1Parser( SwDoc *pDoc, SwHTMLParser const& rParser, + sal_uInt32 const aFHeight[7], const OUString& rBaseURL, bool bNewDoc); + virtual ~SwCSS1Parser() override; + + virtual bool ParseStyleSheet( const OUString& rIn ) override; + + // determine font height for a certain font size (0-6) + virtual sal_uInt32 GetFontHeight( sal_uInt16 nSize ) const override; + + // fetch current font list (also zero is allowed) + virtual const FontList *GetFontList() const override; + + // determine the character format of a token and a maybe empty class + SwCharFormat* GetChrFormat( HtmlTokenId nToken, const OUString& rClass ) const; + + // determine a TextFormatColl of a Pool-Id + SwTextFormatColl *GetTextFormatColl( sal_uInt16 nTextColl, const OUString& rClass ); + + // This methods do the same as the one of SwDoc, but change the + // encoding if required. + SwTextFormatColl *GetTextCollFromPool( sal_uInt16 nPoolId ) const; + SwCharFormat *GetCharFormatFromPool( sal_uInt16 nPoolId ) const; + + // Fetch the left or right page style. In documents with only + // one style there is only a right page. + // Otherwise the right page is the HTML pool style and the left + // page a user style which is created on-demand if bCreate is set. + SwPageDesc* GetMasterPageDesc(); + inline const SwPageDesc* GetFirstPageDesc( bool bCreate=false ); + inline const SwPageDesc* GetRightPageDesc( bool bCreate=false ); + inline const SwPageDesc* GetLeftPageDesc( bool bCreate=false ); + + // Set attributes on the HTML page style (set attributes are + // deleted from the Item-Set). Is called for the BODY tag. + void SetPageDescAttrs( const SvxBrushItem *pBrush, + SfxItemSet *pItemSet=nullptr ); + + void ChgPageDesc( const SwPageDesc *pPageDesc, + const SwPageDesc& rNewPageDesc ); + + // Is called for @page + void SetPageDescAttrs( const SwPageDesc *pPageDesc, SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rPropInfo ); + + // Fill a DropCap attribute + void FillDropCap( SwFormatDrop& rDrop, SfxItemSet& rItemSet, + const OUString *pName=nullptr ); + + bool SetFormatBreak( SfxItemSet& rItemSet, + const SvxCSS1PropertyInfo& rPropInfo ); + + static void AddClassName( OUString& rFormatName, const OUString& rClass ); + + static bool MayBePositioned( const SvxCSS1PropertyInfo& rPropInfo, + bool bAutoWidth=false ); + + static Css1ScriptFlags GetScriptFromClass( OUString& rClass, + bool bSubClassOnly = true ); + + bool IsBodyBGColorSet() const { return m_bBodyBGColorSet; } + bool IsBodyBackgroundSet() const { return m_bBodyBackgroundSet; } + bool IsBodyTextSet() const { return m_bBodyTextSet; } + bool IsBodyLinkSet() const { return m_bBodyLinkSet; } + bool IsBodyVLinkSet() const { return m_bBodyVLinkSet; } + + bool IsSetFirstPageDesc() const { return m_bSetFirstPageDesc; } + bool IsSetRightPageDesc() const { return m_bSetRightPageDesc; } + + void SetBodyBGColorSet() { m_bBodyBGColorSet = true; } + void SetBodyBackgroundSet() { m_bBodyBackgroundSet = true; } + void SetBodyTextSet() { m_bBodyTextSet = true; } + void SetBodyLinkSet() { m_bBodyLinkSet = true; } + void SetBodyVLinkSet() { m_bBodyVLinkSet = true; } + + std::unique_ptr<SvxBrushItem> makePageDescBackground() const; + + inline void SetTHTagStyles(); + inline void SetTDTagStyles(); + inline void SetATagStyles(); + inline void SetDelayedStyles(); + + virtual void SetDfltEncoding( rtl_TextEncoding eEnc ) override; +}; + +inline const SwPageDesc* SwCSS1Parser::GetFirstPageDesc( bool bCreate ) +{ + return GetPageDesc( RES_POOLPAGE_FIRST, bCreate ); +} + +inline const SwPageDesc* SwCSS1Parser::GetRightPageDesc( bool bCreate ) +{ + return GetPageDesc( RES_POOLPAGE_RIGHT, bCreate ); +} + +inline const SwPageDesc* SwCSS1Parser::GetLeftPageDesc( bool bCreate ) +{ + return GetPageDesc( RES_POOLPAGE_LEFT, bCreate ); +} + +inline void SwCSS1Parser::SetTHTagStyles() +{ + if( !m_bTableHeaderTextCollSet ) + SetTableTextColl( true ); +} + +inline void SwCSS1Parser::SetTDTagStyles() +{ + if( !m_bTableTextCollSet ) + SetTableTextColl( false ); +} + +inline void SwCSS1Parser::SetATagStyles() +{ + if( !m_bLinkCharFormatsSet ) + SetLinkCharFormats(); +} + +inline void SwCSS1Parser::SetDelayedStyles() +{ + SetTHTagStyles(); + SetTDTagStyles(); + SetATagStyles(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/swhtml.cxx b/sw/source/filter/html/swhtml.cxx new file mode 100644 index 000000000..8503c342c --- /dev/null +++ b/sw/source/filter/html/swhtml.cxx @@ -0,0 +1,5646 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <config_java.h> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <rtl/ustrbuf.hxx> +#include <svx/svxids.hrc> +#if OSL_DEBUG_LEVEL > 0 +#include <stdlib.h> +#endif +#include <hintids.hxx> + +#include <vcl/errinf.hxx> +#include <svl/stritem.hxx> +#include <vcl/imap.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <svtools/ctrltool.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/pathoptions.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/event.hxx> +#include <sfx2/docfile.hxx> + +#include <svtools/htmlcfg.hxx> +#include <sfx2/linkmgr.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/flstitem.hxx> +#include <svx/unobrushitemhelper.hxx> + +#include <frmatr.hxx> +#include <charatr.hxx> +#include <fmtfld.hxx> +#include <fmtpdsc.hxx> +#include <fmtanchr.hxx> +#include <fmtsrnd.hxx> +#include <fmtfsize.hxx> +#include <fmtclds.hxx> +#include <fchrfmt.hxx> +#include <fmtinfmt.hxx> +#include <fmtfollowtextflow.hxx> +#include <fmtornt.hxx> +#include <docary.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentState.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <mdiexp.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <IMark.hxx> +#include <docsh.hxx> +#include <editsh.hxx> +#include <docufld.hxx> +#include "swcss1.hxx" +#include <fltini.hxx> +#include <htmltbl.hxx> +#include "htmlnum.hxx" +#include "swhtml.hxx" +#include "wrthtml.hxx" +#include <linkenum.hxx> +#include <breakit.hxx> +#include <SwAppletImpl.hxx> +#include <swdll.hxx> +#include <txatbase.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svx/svdobj.hxx> +#include <officecfg/Office/Writer.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/sequence.hxx> + +#include <swerror.h> +#include <ndole.hxx> +#include <unoframe.hxx> +#include "css1atr.hxx" +#include <frameformats.hxx> + +#define FONTSIZE_MASK 7 + +#define HTML_ESC_PROP 80 +#define HTML_ESC_SUPER DFLT_ESC_SUPER +#define HTML_ESC_SUB DFLT_ESC_SUB + +#define HTML_SPTYPE_BLOCK 1 +#define HTML_SPTYPE_HORI 2 +#define HTML_SPTYPE_VERT 3 + +using editeng::SvxBorderLine; +using namespace ::com::sun::star; + +// <P ALIGN=xxx>, <Hn ALIGN=xxx>, <TD ALIGN=xxx> etc. +HTMLOptionEnum<SvxAdjust> const aHTMLPAlignTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_AL_left, SvxAdjust::Left }, + { OOO_STRING_SVTOOLS_HTML_AL_center, SvxAdjust::Center }, + { OOO_STRING_SVTOOLS_HTML_AL_middle, SvxAdjust::Center }, // Netscape + { OOO_STRING_SVTOOLS_HTML_AL_right, SvxAdjust::Right }, + { OOO_STRING_SVTOOLS_HTML_AL_justify, SvxAdjust::Block }, + { OOO_STRING_SVTOOLS_HTML_AL_char, SvxAdjust::Left }, + { nullptr, SvxAdjust(0) } +}; + +// <SPACER TYPE=...> +static HTMLOptionEnum<sal_uInt16> const aHTMLSpacerTypeTable[] = +{ + { OOO_STRING_SVTOOLS_HTML_SPTYPE_block, HTML_SPTYPE_BLOCK }, + { OOO_STRING_SVTOOLS_HTML_SPTYPE_horizontal, HTML_SPTYPE_HORI }, + { OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical, HTML_SPTYPE_VERT }, + { nullptr, 0 } +}; + +HTMLReader::HTMLReader() +{ + m_bTemplateBrowseMode = true; +} + +OUString HTMLReader::GetTemplateName(SwDoc& rDoc) const +{ + if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) + // HTML import into Writer, avoid loading the Writer/Web template. + return OUString(); + + const OUString sTemplateWithoutExt("internal/html"); + SvtPathOptions aPathOpt; + + // first search for OpenDocument Writer/Web template + // OpenDocument Writer/Web template (extension .oth) + OUString sTemplate( sTemplateWithoutExt + ".oth" ); + if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::PATH_TEMPLATE )) + return sTemplate; + + // no OpenDocument Writer/Web template found. + // search for OpenOffice.org Writer/Web template + sTemplate = sTemplateWithoutExt + ".stw"; + if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::PATH_TEMPLATE )) + return sTemplate; + + OSL_ENSURE( false, "The default HTML template cannot be found in the defined template directories!"); + + return OUString(); +} + +bool HTMLReader::SetStrmStgPtr() +{ + OSL_ENSURE( m_pMedium, "Where is the medium??" ); + + if( m_pMedium->IsRemote() || !m_pMedium->IsStorage() ) + { + m_pStream = m_pMedium->GetInStream(); + return true; + } + return false; + +} + +// Call for the general Reader-Interface +ErrCode HTMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPam, const OUString & rName ) +{ + SetupFilterOptions(); + + if( !m_pStream ) + { + OSL_ENSURE( m_pStream, "HTML-Read without stream" ); + return ERR_SWG_READ_ERROR; + } + + if( !m_bInsertMode ) + { + Reader::ResetFrameFormats( rDoc ); + + // Set the HTML page style, when it isn't a HTML document, + // otherwise it's already set. + if( !rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && m_aNamespace != "reqif-xhtml" ) + { + rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, SwFormatPageDesc( + rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false )) ); + } + } + + // so nobody steals the document! + rtl::Reference<SwDoc> xHoldAlive(&rDoc); + ErrCode nRet = ERRCODE_NONE; + tools::SvRef<SwHTMLParser> xParser = new SwHTMLParser( &rDoc, rPam, *m_pStream, + rName, rBaseURL, !m_bInsertMode, m_pMedium, + IsReadUTF8(), + m_bIgnoreHTMLComments, m_aNamespace ); + + SvParserState eState = xParser->CallParser(); + + if( SvParserState::Pending == eState ) + m_pStream->ResetError(); + else if( SvParserState::Accepted != eState ) + { + const OUString sErr(OUString::number(static_cast<sal_Int32>(xParser->GetLineNr())) + + "," + OUString::number(static_cast<sal_Int32>(xParser->GetLinePos()))); + + // use the stream as transport for error number + nRet = *new StringErrorInfo( ERR_FORMAT_ROWCOL, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + + return nRet; +} + +SwHTMLParser::SwHTMLParser( SwDoc* pD, SwPaM& rCursor, SvStream& rIn, + const OUString& rPath, + const OUString& rBaseURL, + bool bReadNewDoc, + SfxMedium* pMed, bool bReadUTF8, + bool bNoHTMLComments, + const OUString& rNamespace ) + : SfxHTMLParser( rIn, bReadNewDoc, pMed ), + m_aPathToFile( rPath ), + m_sBaseURL( rBaseURL ), + m_xAttrTab(std::make_shared<HTMLAttrTable>()), + m_pNumRuleInfo( new SwHTMLNumRuleInfo ), + m_xDoc( pD ), + m_pActionViewShell( nullptr ), + m_pSttNdIdx( nullptr ), + m_pFormImpl( nullptr ), + m_pMarquee( nullptr ), + m_pImageMap( nullptr ), + m_nBaseFontStMin( 0 ), + m_nFontStMin( 0 ), + m_nDefListDeep( 0 ), + m_nFontStHeadStart( 0 ), + m_nSBModuleCnt( 0 ), + m_nMissingImgMaps( 0 ), + m_nParaCnt( 5 ), + // #i83625# + m_nContextStMin( 0 ), + m_nContextStAttrMin( 0 ), + m_nSelectEntryCnt( 0 ), + m_nOpenParaToken( HtmlTokenId::NONE ), + m_eJumpTo( JumpToMarks::NONE ), +#ifdef DBG_UTIL + m_nContinue( 0 ), +#endif + m_eParaAdjust( SvxAdjust::End ), + m_bDocInitalized( false ), + m_bSetModEnabled( false ), + m_bInFloatingFrame( false ), + m_bInField( false ), + m_bCallNextToken( false ), + m_bIgnoreRawData( false ), + m_bLBEntrySelected ( false ), + m_bTAIgnoreNewPara ( false ), + m_bFixMarqueeWidth ( false ), + m_bNoParSpace( false ), + m_bInNoEmbed( false ), + m_bInTitle( false ), + m_bUpdateDocStat( false ), + m_bFixSelectWidth( false ), + m_bTextArea( false ), + m_bSelect( false ), + m_bInFootEndNoteAnchor( false ), + m_bInFootEndNoteSymbol( false ), + m_bIgnoreHTMLComments( bNoHTMLComments ), + m_bRemoveHidden( false ), + m_bBodySeen( false ), + m_bReadingHeaderOrFooter( false ), + m_bNotifyMacroEventRead( false ), + m_isInTableStructure(false), + m_nTableDepth( 0 ), + m_pTempViewFrame(nullptr) +{ + // If requested explicitly, then force ignoring of comments (don't create postits for them). + if (!utl::ConfigManager::IsFuzzing() && officecfg::Office::Writer::Filter::Import::HTML::IgnoreComments::get()) + m_bIgnoreHTMLComments = true; + + m_nEventId = nullptr; + m_bUpperSpace = m_bViewCreated = m_bChkJumpMark = false; + + m_eScriptLang = HTMLScriptLanguage::Unknown; + + rCursor.DeleteMark(); + m_pPam = &rCursor; // re-use existing cursor: avoids spurious ~SwIndexReg assert + memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable)); + + // Read the font sizes 1-7 from the INI file + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + m_aFontHeights[0] = rHtmlOptions.GetFontSize( 0 ) * 20; + m_aFontHeights[1] = rHtmlOptions.GetFontSize( 1 ) * 20; + m_aFontHeights[2] = rHtmlOptions.GetFontSize( 2 ) * 20; + m_aFontHeights[3] = rHtmlOptions.GetFontSize( 3 ) * 20; + m_aFontHeights[4] = rHtmlOptions.GetFontSize( 4 ) * 20; + m_aFontHeights[5] = rHtmlOptions.GetFontSize( 5 ) * 20; + m_aFontHeights[6] = rHtmlOptions.GetFontSize( 6 ) * 20; + + m_bKeepUnknown = rHtmlOptions.IsImportUnknown(); + + if(bReadNewDoc) + { + //CJK has different defaults, so a different object should be used for this + //RES_CHARTR_CJK_FONTSIZE is a valid value + SvxFontHeightItem aFontHeight(m_aFontHeights[2], 100, RES_CHRATR_FONTSIZE); + m_xDoc->SetDefault( aFontHeight ); + SvxFontHeightItem aFontHeightCJK(m_aFontHeights[2], 100, RES_CHRATR_CJK_FONTSIZE); + m_xDoc->SetDefault( aFontHeightCJK ); + SvxFontHeightItem aFontHeightCTL(m_aFontHeights[2], 100, RES_CHRATR_CTL_FONTSIZE); + m_xDoc->SetDefault( aFontHeightCTL ); + + // #i18732# - adjust default of option 'FollowTextFlow' + // TODO: not sure what the appropriate default for HTML should be? + m_xDoc->SetDefault( SwFormatFollowTextFlow(true) ); + } + + // Change to HTML mode during the import, so that the right styles are created + m_bOldIsHTMLMode = m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE); + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true); + + m_pCSS1Parser.reset(new SwCSS1Parser(m_xDoc.get(), *this, m_aFontHeights, m_sBaseURL, IsNewDoc())); + m_pCSS1Parser->SetIgnoreFontFamily( rHtmlOptions.IsIgnoreFontFamily() ); + + if( bReadUTF8 ) + { + SetSrcEncoding( RTL_TEXTENCODING_UTF8 ); + } + else + { + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + SvKeyValueIterator *pHeaderAttrs = + pDocSh->GetHeaderAttributes(); + if( pHeaderAttrs ) + SetEncodingByHTTPHeader( pHeaderAttrs ); + } + m_pCSS1Parser->SetDfltEncoding( osl_getThreadTextEncoding() ); + + SwDocShell* pDocSh = m_xDoc->GetDocShell(); + if( pDocSh ) + { + m_bViewCreated = true; // not, load synchronous + + // a jump mark is present + + if( pMed ) + { + m_sJmpMark = pMed->GetURLObject().GetMark(); + if( !m_sJmpMark.isEmpty() ) + { + m_eJumpTo = JumpToMarks::Mark; + sal_Int32 nLastPos = m_sJmpMark.lastIndexOf( cMarkSeparator ); + sal_Int32 nPos = nLastPos != -1 ? nLastPos : 0; + + OUString sCmp; + if (nPos) + { + sCmp = m_sJmpMark.copy(nPos + 1).replaceAll(" ", ""); + } + + if( !sCmp.isEmpty() ) + { + sCmp = sCmp.toAsciiLowerCase(); + if( sCmp == "region" ) + m_eJumpTo = JumpToMarks::Region; + else if( sCmp == "table" ) + m_eJumpTo = JumpToMarks::Table; + else if( sCmp == "graphic" ) + m_eJumpTo = JumpToMarks::Graphic; + else if( sCmp == "outline" || + sCmp == "text" || + sCmp == "frame" ) + m_eJumpTo = JumpToMarks::NONE; // this is nothing valid! + else + // otherwise this is a normal (book)mark + nPos = -1; + } + else + nPos = -1; + + if( nPos != -1 ) + m_sJmpMark = m_sJmpMark.copy( 0, nPos ); + if( m_sJmpMark.isEmpty() ) + m_eJumpTo = JumpToMarks::NONE; + } + } + } + + if (!rNamespace.isEmpty()) + { + SetNamespace(rNamespace); + m_bXHTML = true; + if (rNamespace == "reqif-xhtml") + m_bReqIF = true; + } + + // Extract load parameters which are specific to this filter. + if (!pMed) + { + return; + } + + comphelper::SequenceAsHashMap aLoadMap(pMed->GetArgs()); + auto it = aLoadMap.find("AllowedRTFOLEMimeTypes"); + if (it == aLoadMap.end()) + { + return; + } + + uno::Sequence<OUString> aTypes; + it->second >>= aTypes; + m_aAllowedRTFOLEMimeTypes = comphelper::sequenceToContainer<std::set<OUString>>(aTypes); +} + +SwHTMLParser::~SwHTMLParser() +{ +#ifdef DBG_UTIL + OSL_ENSURE( !m_nContinue, "DTOR in continue!" ); +#endif + + OSL_ENSURE(m_aContexts.empty(), "There are still contexts on the stack"); + OSL_ENSURE(!m_nContextStMin, "There are protected contexts"); + m_nContextStMin = 0; + while (!m_aContexts.empty()) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + ClearContext(xCntxt.get()); + } + + bool bAsync = m_xDoc->IsInLoadAsynchron(); + m_xDoc->SetInLoadAsynchron( false ); + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, m_bOldIsHTMLMode); + + if( m_xDoc->GetDocShell() && m_nEventId ) + Application::RemoveUserEvent( m_nEventId ); + + // the DocumentDetected maybe can delete the DocShells, therefore fetch again + if( m_xDoc->GetDocShell() ) + { + // update linked sections + sal_uInt16 nLinkMode = m_xDoc->getIDocumentSettingAccess().getLinkUpdateMode( true ); + if( nLinkMode != NEVER && bAsync && + SfxObjectCreateMode::INTERNAL!=m_xDoc->GetDocShell()->GetCreateMode() ) + m_xDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( nLinkMode == MANUAL, false, nullptr ); + + if ( m_xDoc->GetDocShell()->IsLoading() ) + { + // #i59688# + m_xDoc->GetDocShell()->LoadingFinished(); + } + } + + delete m_pSttNdIdx; + + if( !m_aSetAttrTab.empty() ) + { + OSL_ENSURE( m_aSetAttrTab.empty(),"There are still attributes on the stack" ); + for ( const auto& rpAttr : m_aSetAttrTab ) + delete rpAttr; + m_aSetAttrTab.clear(); + } + + m_pCSS1Parser.reset(); + m_pNumRuleInfo.reset(); + DeleteFormImpl(); + m_pFootEndNoteImpl.reset(); + + OSL_ENSURE(!m_xTable, "It exists still an open table"); + m_pImageMaps.reset(); + + OSL_ENSURE( m_vPendingStack.empty(), + "SwHTMLParser::~SwHTMLParser: Here should not be Pending-Stack anymore" ); + m_vPendingStack.clear(); + + m_xDoc.clear(); + + if ( m_pTempViewFrame ) + { + m_pTempViewFrame->DoClose(); + + // the temporary view frame is hidden, so the hidden flag might need to be removed + if ( m_bRemoveHidden && m_xDoc.is() && m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->GetMedium() ) + m_xDoc->GetDocShell()->GetMedium()->GetItemSet()->ClearItem( SID_HIDDEN ); + } +} + +IMPL_LINK_NOARG( SwHTMLParser, AsyncCallback, void*, void ) +{ + m_nEventId=nullptr; + + // #i47907# - If the document has already been destructed, + // the parser should be aware of this: + if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + || 1 == m_xDoc->getReferenceCount() ) + { + // was the import aborted by SFX? + eState = SvParserState::Error; + } + + GetAsynchCallLink().Call(nullptr); +} + +SvParserState SwHTMLParser::CallParser() +{ + // create temporary index on position 0, so it won't be moved! + m_pSttNdIdx = new SwNodeIndex( m_xDoc->GetNodes() ); + if( !IsNewDoc() ) // insert into existing document ? + { + const SwPosition* pPos = m_pPam->GetPoint(); + + m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + + *m_pSttNdIdx = pPos->nNode.GetIndex()-1; + m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + + SwPaM aInsertionRangePam( *pPos ); + + m_pPam->Move( fnMoveBackward ); + + // split any redline over the insertion point + aInsertionRangePam.SetMark(); + *aInsertionRangePam.GetPoint() = *m_pPam->GetPoint(); + aInsertionRangePam.Move( fnMoveBackward ); + m_xDoc->getIDocumentRedlineAccess().SplitRedline( aInsertionRangePam ); + + m_xDoc->SetTextFormatColl( *m_pPam, + m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD )); + } + + if( GetMedium() ) + { + if( !m_bViewCreated ) + { + m_nEventId = Application::PostUserEvent( LINK( this, SwHTMLParser, AsyncCallback ) ); + } + else + { + m_bViewCreated = true; + m_nEventId = nullptr; + } + } + else // show progress bar + { + rInput.Seek(STREAM_SEEK_TO_END); + rInput.ResetError(); + + m_xProgress.reset(new ImportProgress(m_xDoc->GetDocShell(), 0, rInput.Tell())); + + rInput.Seek(STREAM_SEEK_TO_BEGIN); + rInput.ResetError(); + } + + StartListening(m_xDoc->GetPageDesc( 0 ).GetNotifier()); + + SvParserState eRet = HTMLParser::CallParser(); + return eRet; +} + +bool SwHTMLParser::CanRemoveNode(sal_uLong nNodeIdx) const +{ + const SwNode *pPrev = m_xDoc->GetNodes()[nNodeIdx - 1]; + return pPrev->IsContentNode() || (pPrev->IsEndNode() && pPrev->StartOfSectionNode()->IsSectionNode()); +} + +void SwHTMLParser::Continue( HtmlTokenId nToken ) +{ +#ifdef DBG_UTIL + OSL_ENSURE(!m_nContinue, "Continue in Continue - not supposed to happen"); + m_nContinue++; +#endif + + // When the import (of SFX) is aborted, an error will be set but + // we still continue, so that we clean up properly. + OSL_ENSURE( SvParserState::Error!=eState, + "SwHTMLParser::Continue: already set an error" ); + if( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + eState = SvParserState::Error; + + // Fetch SwViewShell from document, save it and set as current. + SwViewShell *pInitVSh = CallStartAction(); + + if( SvParserState::Error != eState && GetMedium() && !m_bViewCreated ) + { + // At first call first return, show document and wait for callback + // time. + // At this point in CallParser only one digit was read and + // a SaveState(0) was called. + eState = SvParserState::Pending; + m_bViewCreated = true; + m_xDoc->SetInLoadAsynchron( true ); + +#ifdef DBG_UTIL + m_nContinue--; +#endif + + return; + } + + m_bSetModEnabled = false; + if( m_xDoc->GetDocShell() ) + { + m_bSetModEnabled = m_xDoc->GetDocShell()->IsEnableSetModified(); + if( m_bSetModEnabled ) + { + m_xDoc->GetDocShell()->EnableSetModified( false ); + } + } + + // during import don't call OLE-Modified + Link<bool,void> aOLELink( m_xDoc->GetOle2Link() ); + m_xDoc->SetOle2Link( Link<bool,void>() ); + + bool bModified = m_xDoc->getIDocumentState().IsModified(); + bool const bWasUndo = m_xDoc->GetIDocumentUndoRedo().DoesUndo(); + m_xDoc->GetIDocumentUndoRedo().DoUndo(false); + + // When the import will be aborted, don't call Continue anymore. + // If a Pending-Stack exists make sure the stack is ended with a call + // of NextToken. + if( SvParserState::Error == eState ) + { + OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE, + "SwHTMLParser::Continue: Pending-Stack without Token" ); + if( !m_vPendingStack.empty() && m_vPendingStack.back().nToken != HtmlTokenId::NONE ) + NextToken( m_vPendingStack.back().nToken ); + OSL_ENSURE( m_vPendingStack.empty(), + "SwHTMLParser::Continue: There is again a Pending-Stack" ); + } + else + { + HTMLParser::Continue( !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : nToken ); + } + + // disable progress bar again + m_xProgress.reset(); + + bool bLFStripped = false; + if( SvParserState::Pending != GetStatus() ) + { + // set the last attributes yet + { + if( !m_aScriptSource.isEmpty() ) + { + SwScriptFieldType *pType = + static_cast<SwScriptFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Script )); + + SwScriptField aField( pType, m_aScriptType, m_aScriptSource, + false ); + InsertAttr( SwFormatField( aField ), false ); + } + + if( m_pAppletImpl ) + { + if( m_pAppletImpl->GetApplet().is() ) + EndApplet(); + else + EndObject(); + } + + // maybe remove an existing LF after the last paragraph + if( IsNewDoc() ) + bLFStripped = StripTrailingLF() > 0; + + // close still open numbering + while( GetNumInfo().GetNumRule() ) + EndNumberBulletList(); + + OSL_ENSURE( !m_nContextStMin, "There are protected contexts" ); + // try this twice, first normally to let m_nContextStMin decrease + // naturally and get contexts popped in desired order, and if that + // fails force it + for (int i = 0; i < 2; ++i) + { + while (m_aContexts.size() > m_nContextStMin) + { + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext()); + if (xCntxt) + EndContext(xCntxt.get()); + } + if (!m_nContextStMin) + break; + OSL_ENSURE(!m_nContextStMin, "There are still protected contexts"); + m_nContextStMin = 0; + } + + m_aParaAttrs.clear(); + + SetAttr( false ); + + // set the first delayed styles + m_pCSS1Parser->SetDelayedStyles(); + } + + // again correct the start + if( !IsNewDoc() && m_pSttNdIdx->GetIndex() ) + { + SwTextNode* pTextNode = m_pSttNdIdx->GetNode().GetTextNode(); + SwNodeIndex aNxtIdx( *m_pSttNdIdx ); + if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx )) + { + const sal_Int32 nStt = pTextNode->GetText().getLength(); + // when the cursor is still in the node, then set him at the end + if( m_pPam->GetPoint()->nNode == aNxtIdx ) + { + m_pPam->GetPoint()->nNode = *m_pSttNdIdx; + m_pPam->GetPoint()->nContent.Assign( pTextNode, nStt ); + } + +#if OSL_DEBUG_LEVEL > 0 +// !!! shouldn't be possible, or ?? + OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound().nNode.GetIndex(), + "Pam.Bound1 is still in the node" ); + OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound( false ).nNode.GetIndex(), + "Pam.Bound2 is still in the node" ); + + if( m_pSttNdIdx->GetIndex()+1 == m_pPam->GetBound().nNode.GetIndex() ) + { + const sal_Int32 nCntPos = m_pPam->GetBound().nContent.GetIndex(); + m_pPam->GetBound().nContent.Assign( pTextNode, + pTextNode->GetText().getLength() + nCntPos ); + } + if( m_pSttNdIdx->GetIndex()+1 == m_pPam->GetBound( false ).nNode.GetIndex() ) + { + const sal_Int32 nCntPos = m_pPam->GetBound( false ).nContent.GetIndex(); + m_pPam->GetBound( false ).nContent.Assign( pTextNode, + pTextNode->GetText().getLength() + nCntPos ); + } +#endif + // Keep character attribute! + SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode(); + if (pTextNode->GetText().getLength()) + pDelNd->FormatToTextAttr( pTextNode ); + else + pTextNode->ChgFormatColl( pDelNd->GetTextColl() ); + pTextNode->JoinNext(); + } + } + } + + if( SvParserState::Accepted == eState ) + { + if( m_nMissingImgMaps ) + { + // Some Image-Map relations are still missing. + // Maybe now the Image-Maps are there? + ConnectImageMaps(); + } + + // now remove the last useless paragraph + SwPosition* pPos = m_pPam->GetPoint(); + if( !pPos->nContent.GetIndex() && !bLFStripped ) + { + SwTextNode* pCurrentNd; + sal_uLong nNodeIdx = pPos->nNode.GetIndex(); + + bool bHasFlysOrMarks = + HasCurrentParaFlys() || HasCurrentParaBookmarks( true ); + + if( IsNewDoc() ) + { + if (!m_pPam->GetPoint()->nContent.GetIndex() && CanRemoveNode(nNodeIdx)) + { + SwContentNode* pCNd = m_pPam->GetContentNode(); + if( pCNd && pCNd->StartOfSectionIndex()+2 < + pCNd->EndOfSectionIndex() && !bHasFlysOrMarks ) + { + SwViewShell *pVSh = CheckActionViewShell(); + SwCursorShell *pCursorSh = dynamic_cast<SwCursorShell *>( pVSh ); + if( pCursorSh && + pCursorSh->GetCursor()->GetPoint() + ->nNode.GetIndex() == nNodeIdx ) + { + pCursorSh->MovePara(GoPrevPara, fnParaEnd ); + pCursorSh->SetMark(); + pCursorSh->ClearMark(); + } + m_pPam->GetBound().nContent.Assign( nullptr, 0 ); + m_pPam->GetBound(false).nContent.Assign( nullptr, 0 ); + m_xDoc->GetNodes().Delete( m_pPam->GetPoint()->nNode ); + } + } + } + else if( nullptr != ( pCurrentNd = m_xDoc->GetNodes()[ nNodeIdx ]->GetTextNode()) && !bHasFlysOrMarks ) + { + if( pCurrentNd->CanJoinNext( &pPos->nNode )) + { + SwTextNode* pNextNd = pPos->nNode.GetNode().GetTextNode(); + pPos->nContent.Assign( pNextNd, 0 ); + m_pPam->SetMark(); m_pPam->DeleteMark(); + pNextNd->JoinPrev(); + } + else if (pCurrentNd->GetText().isEmpty()) + { + pPos->nContent.Assign( nullptr, 0 ); + m_pPam->SetMark(); m_pPam->DeleteMark(); + m_xDoc->GetNodes().Delete( pPos->nNode ); + m_pPam->Move( fnMoveBackward ); + } + } + } + + // annul the SplitNode from the beginning + else if( !IsNewDoc() ) + { + if( pPos->nContent.GetIndex() ) // then there was no <p> at the end + m_pPam->Move( fnMoveForward, GoInNode ); // therefore to the next + SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode(); + SwNodeIndex aPrvIdx( pPos->nNode ); + if( pTextNode && pTextNode->CanJoinPrev( &aPrvIdx ) && + *m_pSttNdIdx <= aPrvIdx ) + { + // Normally here should take place a JoinNext, but all cursors and + // so are registered in pTextNode, so that it MUST remain. + + // Convert paragraph to character attribute, from Prev adopt + // the paragraph attribute and the template! + SwTextNode* pPrev = aPrvIdx.GetNode().GetTextNode(); + pTextNode->ChgFormatColl( pPrev->GetTextColl() ); + pTextNode->FormatToTextAttr( pPrev ); + pTextNode->ResetAllAttr(); + + if( pPrev->HasSwAttrSet() ) + pTextNode->SetAttr( *pPrev->GetpSwAttrSet() ); + + if( &m_pPam->GetBound().nNode.GetNode() == pPrev ) + m_pPam->GetBound().nContent.Assign( pTextNode, 0 ); + if( &m_pPam->GetBound(false).nNode.GetNode() == pPrev ) + m_pPam->GetBound(false).nContent.Assign( pTextNode, 0 ); + + pTextNode->JoinPrev(); + } + } + + // adjust AutoLoad in DocumentProperties + if (!utl::ConfigManager::IsFuzzing() && IsNewDoc()) + { + SwDocShell *pDocShell(m_xDoc->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(), "DocumentProperties is null"); + if ( xDocProps.is() && (xDocProps->getAutoloadSecs() > 0) && + (xDocProps->getAutoloadURL().isEmpty()) ) + { + xDocProps->setAutoloadURL(m_aPathToFile); + } + } + } + + if( m_bUpdateDocStat ) + { + m_xDoc->getIDocumentStatistics().UpdateDocStat( false, true ); + } + } + + if( SvParserState::Pending != GetStatus() ) + { + delete m_pSttNdIdx; + m_pSttNdIdx = nullptr; + } + + // should the parser be the last one who hold the document, then nothing + // has to be done anymore, document will be destroyed shortly! + if( 1 < m_xDoc->getReferenceCount() ) + { + if( bWasUndo ) + { + m_xDoc->GetIDocumentUndoRedo().DelAllUndoObj(); + m_xDoc->GetIDocumentUndoRedo().DoUndo(true); + } + else if( !pInitVSh ) + { + // When at the beginning of Continue no Shell was available, + // it's possible in the meantime one was created. + // In that case the bWasUndo flag is wrong and we must + // enable Undo. + SwViewShell *pTmpVSh = CheckActionViewShell(); + if( pTmpVSh ) + { + m_xDoc->GetIDocumentUndoRedo().DoUndo(true); + } + } + + m_xDoc->SetOle2Link( aOLELink ); + if( !bModified ) + m_xDoc->getIDocumentState().ResetModified(); + if( m_bSetModEnabled && m_xDoc->GetDocShell() ) + { + m_xDoc->GetDocShell()->EnableSetModified(); + m_bSetModEnabled = false; // this is unnecessary here + } + } + + // When the Document-SwVievShell still exists and an Action is open + // (doesn't have to be by abort), end the Action, disconnect from Shell + // and finally reconstruct the old Shell. + CallEndAction( true ); + +#ifdef DBG_UTIL + m_nContinue--; +#endif +} + +void SwHTMLParser::Notify(const SfxHint& rHint) +{ + if(rHint.GetId() == SfxHintId::Dying) + { + EndListeningAll(); + ReleaseRef(); + } +} + +void SwHTMLParser::DocumentDetected() +{ + OSL_ENSURE( !m_bDocInitalized, "DocumentDetected called multiple times" ); + m_bDocInitalized = true; + if( IsNewDoc() ) + { + if( IsInHeader() ) + FinishHeader(); + + CallEndAction( true ); + + m_xDoc->GetIDocumentUndoRedo().DoUndo(false); + // For DocumentDetected in general a SwViewShell is created. + // But it also can be created later, in case the UI is captured. + CallStartAction(); + } +} + +// is called for every token that is recognised in CallParser +void SwHTMLParser::NextToken( HtmlTokenId nToken ) +{ + if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + || 1 == m_xDoc->getReferenceCount() ) + { + // Was the import cancelled by SFX? If a pending stack + // exists, clean it. + eState = SvParserState::Error; + OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE, + "SwHTMLParser::NextToken: Pending-Stack without token" ); + if( 1 == m_xDoc->getReferenceCount() || m_vPendingStack.empty() ) + return ; + } + +#if OSL_DEBUG_LEVEL > 0 + if( !m_vPendingStack.empty() ) + { + switch( nToken ) + { + // tables are read by recursive method calls + case HtmlTokenId::TABLE_ON: + // For CSS declarations we might have to wait + // for a file download to finish + case HtmlTokenId::LINK: + // For controls we might have to set the size. + case HtmlTokenId::INPUT: + case HtmlTokenId::TEXTAREA_ON: + case HtmlTokenId::SELECT_ON: + case HtmlTokenId::SELECT_OFF: + break; + default: + OSL_ENSURE( m_vPendingStack.empty(), "Unknown token for Pending-Stack" ); + break; + } + } +#endif + + // The following special cases have to be treated before the + // filter detection, because Netscape doesn't reference the content + // of the title for filter detection either. + if( m_vPendingStack.empty() ) + { + if( m_bInTitle ) + { + switch( nToken ) + { + case HtmlTokenId::TITLE_OFF: + { + OUString sTitle = m_sTitle.makeStringAndClear(); + if( IsNewDoc() && !sTitle.isEmpty() ) + { + if( m_xDoc->GetDocShell() ) { + uno::Reference<document::XDocumentPropertiesSupplier> + xDPS(m_xDoc->GetDocShell()->GetModel(), + uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocProps.is(), "no DocumentProperties"); + if (xDocProps.is()) { + xDocProps->setTitle(sTitle); + } + + m_xDoc->GetDocShell()->SetTitle(sTitle); + } + } + m_bInTitle = false; + break; + } + + case HtmlTokenId::NONBREAKSPACE: + m_sTitle.append(" "); + break; + + case HtmlTokenId::SOFTHYPH: + m_sTitle.append("-"); + break; + + case HtmlTokenId::TEXTTOKEN: + m_sTitle.append(aToken); + break; + + default: + m_sTitle.append("<"); + if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) ) + m_sTitle.append("/"); + m_sTitle.append(sSaveToken); + if( !aToken.isEmpty() ) + { + m_sTitle.append(" "); + m_sTitle.append(aToken); + } + m_sTitle.append(">"); + break; + } + + return; + } + } + + // Find out what type of document it is if we don't know already. + // For Controls this has to be finished before the control is inserted + // because for inserting a View is needed. + if( !m_bDocInitalized ) + DocumentDetected(); + + bool bGetIDOption = false, bInsertUnknown = false; + bool bUpperSpaceSave = m_bUpperSpace; + m_bUpperSpace = false; + + // The following special cases may or have to be treated after the + // filter detection + if( m_vPendingStack.empty() ) + { + if( m_bInFloatingFrame ) + { + // <SCRIPT> is ignored here (from us), because it is ignored in + // Applets as well + if( HtmlTokenId::IFRAME_OFF == nToken ) + { + m_bCallNextToken = false; + m_bInFloatingFrame = false; + } + + return; + } + else if( m_bInNoEmbed ) + { + switch( nToken ) + { + case HtmlTokenId::NOEMBED_OFF: + m_aContents = convertLineEnd(m_aContents, GetSystemLineEnd()); + InsertComment( m_aContents, OOO_STRING_SVTOOLS_HTML_noembed ); + m_aContents.clear(); + m_bCallNextToken = false; + m_bInNoEmbed = false; + break; + + case HtmlTokenId::RAWDATA: + InsertCommentText( OOO_STRING_SVTOOLS_HTML_noembed ); + break; + + default: + OSL_ENSURE( false, "SwHTMLParser::NextToken: invalid tag" ); + break; + } + + return; + } + else if( m_pAppletImpl ) + { + // in an applet only <PARAM> tags and the </APPLET> tag + // are of interest for us (for the moment) + // <SCRIPT> is ignored here (from Netscape)! + + switch( nToken ) + { + case HtmlTokenId::APPLET_OFF: + m_bCallNextToken = false; + EndApplet(); + break; + case HtmlTokenId::OBJECT_OFF: + m_bCallNextToken = false; + EndObject(); + break; + case HtmlTokenId::PARAM: + InsertParam(); + break; + default: break; + } + + return; + } + else if( m_bTextArea ) + { + // in a TextArea everything up to </TEXTAREA> is inserted as text. + // <SCRIPT> is ignored here (from Netscape)! + + switch( nToken ) + { + case HtmlTokenId::TEXTAREA_OFF: + m_bCallNextToken = false; + EndTextArea(); + break; + + default: + InsertTextAreaText( nToken ); + break; + } + + return; + } + else if( m_bSelect ) + { + // HAS to be treated after bNoScript! + switch( nToken ) + { + case HtmlTokenId::SELECT_OFF: + m_bCallNextToken = false; + EndSelect(); + return; + + case HtmlTokenId::OPTION: + InsertSelectOption(); + return; + + case HtmlTokenId::TEXTTOKEN: + InsertSelectText(); + return; + + case HtmlTokenId::INPUT: + case HtmlTokenId::SCRIPT_ON: + case HtmlTokenId::SCRIPT_OFF: + case HtmlTokenId::NOSCRIPT_ON: + case HtmlTokenId::NOSCRIPT_OFF: + case HtmlTokenId::RAWDATA: + // treat in normal switch + break; + + default: + // ignore + return; + } + } + else if( m_pMarquee ) + { + // in a TextArea everything up to </TEXTAREA> is inserted as text. + // The <SCRIPT> tags are ignored from MS-IE, we ignore the whole + // script. + switch( nToken ) + { + case HtmlTokenId::MARQUEE_OFF: + m_bCallNextToken = false; + EndMarquee(); + break; + + case HtmlTokenId::TEXTTOKEN: + InsertMarqueeText(); + break; + default: break; + } + + return; + } + else if( m_bInField ) + { + switch( nToken ) + { + case HtmlTokenId::SDFIELD_OFF: + m_bCallNextToken = false; + EndField(); + break; + + case HtmlTokenId::TEXTTOKEN: + InsertFieldText(); + break; + default: break; + } + + return; + } + else if( m_bInFootEndNoteAnchor || m_bInFootEndNoteSymbol ) + { + switch( nToken ) + { + case HtmlTokenId::ANCHOR_OFF: + EndAnchor(); + m_bCallNextToken = false; + break; + + case HtmlTokenId::TEXTTOKEN: + InsertFootEndNoteText(); + break; + default: break; + } + return; + } + else if( !m_aUnknownToken.isEmpty() ) + { + // Paste content of unknown tags. + // (but surely if we are not in the header section) fdo#36080 fdo#34666 + if (!aToken.isEmpty() && !IsInHeader() ) + { + if( !m_bDocInitalized ) + DocumentDetected(); + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken ); + + // if there are temporary paragraph attributes and the + // paragraph isn't empty then the paragraph attributes + // are final. + m_aParaAttrs.clear(); + + SetAttr(); + } + + // Unknown token in the header are only closed by a matching + // end-token, </HEAD> or <BODY>. Text inside is ignored. + switch( nToken ) + { + case HtmlTokenId::UNKNOWNCONTROL_OFF: + if( m_aUnknownToken != sSaveToken ) + return; + [[fallthrough]]; + case HtmlTokenId::FRAMESET_ON: + case HtmlTokenId::HEAD_OFF: + case HtmlTokenId::BODY_ON: + case HtmlTokenId::IMAGE: // Don't know why Netscape acts this way. + m_aUnknownToken.clear(); + break; + case HtmlTokenId::TEXTTOKEN: + return; + default: + m_aUnknownToken.clear(); + break; + } + } + } + + switch( nToken ) + { + case HtmlTokenId::BODY_ON: + if (!m_bBodySeen) + { + m_bBodySeen = true; + if( !m_aStyleSource.isEmpty() ) + { + m_pCSS1Parser->ParseStyleSheet( m_aStyleSource ); + m_aStyleSource.clear(); + } + if( IsNewDoc() ) + { + InsertBodyOptions(); + // If there is a template for the first or the right page, + // it is set here. + const SwPageDesc *pPageDesc = nullptr; + if( m_pCSS1Parser->IsSetFirstPageDesc() ) + pPageDesc = m_pCSS1Parser->GetFirstPageDesc(); + else if( m_pCSS1Parser->IsSetRightPageDesc() ) + pPageDesc = m_pCSS1Parser->GetRightPageDesc(); + + if( pPageDesc ) + { + m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, SwFormatPageDesc( pPageDesc ) ); + } + } + } + break; + + case HtmlTokenId::LINK: + InsertLink(); + break; + + case HtmlTokenId::BASE: + { + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::HREF: + m_sBaseURL = rOption.GetString(); + break; + case HtmlOptionId::TARGET: + if( IsNewDoc() ) + { + SwDocShell *pDocShell(m_xDoc->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(),"no DocumentProperties"); + if (xDocProps.is()) { + xDocProps->setDefaultTarget( + rOption.GetString()); + } + } + } + break; + default: break; + } + } + } + break; + + case HtmlTokenId::META: + { + SvKeyValueIterator *pHTTPHeader = nullptr; + if( IsNewDoc() ) + { + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + if( pDocSh ) + pHTTPHeader = pDocSh->GetHeaderAttributes(); + } + SwDocShell *pDocShell(m_xDoc->GetDocShell()); + OSL_ENSURE(pDocShell, "no SwDocShell"); + if (pDocShell) + { + uno::Reference<document::XDocumentProperties> xDocProps; + if (IsNewDoc()) + { + const uno::Reference<document::XDocumentPropertiesSupplier> + xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW ); + xDocProps = xDPS->getDocumentProperties(); + OSL_ENSURE(xDocProps.is(), "DocumentProperties is null"); + } + ParseMetaOptions( xDocProps, pHTTPHeader ); + } + } + break; + + case HtmlTokenId::TITLE_ON: + m_bInTitle = true; + break; + + case HtmlTokenId::SCRIPT_ON: + NewScript(); + break; + + case HtmlTokenId::SCRIPT_OFF: + EndScript(); + break; + + case HtmlTokenId::NOSCRIPT_ON: + case HtmlTokenId::NOSCRIPT_OFF: + bInsertUnknown = true; + break; + + case HtmlTokenId::STYLE_ON: + NewStyle(); + break; + + case HtmlTokenId::STYLE_OFF: + EndStyle(); + break; + + case HtmlTokenId::RAWDATA: + if( !m_bIgnoreRawData ) + { + if( IsReadScript() ) + { + AddScriptSource(); + } + else if( IsReadStyle() ) + { + if( !m_aStyleSource.isEmpty() ) + m_aStyleSource += "\n"; + m_aStyleSource += aToken; + } + } + break; + + case HtmlTokenId::OBJECT_ON: + if (m_bXHTML) + { + if (!InsertEmbed()) + InsertImage(); + break; + } +#if HAVE_FEATURE_JAVA + NewObject(); + m_bCallNextToken = m_pAppletImpl!=nullptr && m_xTable; +#endif + break; + + case HtmlTokenId::OBJECT_OFF: + if (!m_aEmbeds.empty()) + m_aEmbeds.pop(); + break; + + case HtmlTokenId::APPLET_ON: +#if HAVE_FEATURE_JAVA + InsertApplet(); + m_bCallNextToken = m_pAppletImpl!=nullptr && m_xTable; +#endif + break; + + case HtmlTokenId::IFRAME_ON: + InsertFloatingFrame(); + m_bCallNextToken = m_bInFloatingFrame && m_xTable; + break; + + case HtmlTokenId::LINEBREAK: + if( !IsReadPRE() ) + { + InsertLineBreak(); + break; + } + else + bGetIDOption = true; + // <BR>s in <PRE> resemble true LFs, hence no break + [[fallthrough]]; + + case HtmlTokenId::NEWPARA: + // CR in PRE/LISTING/XMP + { + if( HtmlTokenId::NEWPARA==nToken || + m_pPam->GetPoint()->nContent.GetIndex() ) + { + AppendTextNode(); // there is no LF at this place + // therefore it will cause no problems + SetTextCollAttrs(); + } + // progress bar + if (m_xProgress) + m_xProgress->Update(rInput.Tell()); + } + break; + + case HtmlTokenId::NONBREAKSPACE: + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_HARDBLANK) ); + break; + + case HtmlTokenId::SOFTHYPH: + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_SOFTHYPHEN) ); + break; + + case HtmlTokenId::LINEFEEDCHAR: + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode(); + if (!m_xTable && !m_xDoc->IsInHeaderFooter(m_pPam->GetPoint()->nNode)) + { + NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK)); + EndAttr( m_xAttrTab->pBreak, false ); + } + break; + + case HtmlTokenId::TEXTTOKEN: + // insert string without spanning attributes at the end. + if( !aToken.isEmpty() && ' '==aToken[0] && !IsReadPRE() ) + { + sal_Int32 nPos = m_pPam->GetPoint()->nContent.GetIndex(); + const SwTextNode* pTextNode = nPos ? m_pPam->GetPoint()->nNode.GetNode().GetTextNode() : nullptr; + if (pTextNode) + { + const OUString& rText = pTextNode->GetText(); + sal_Unicode cLast = rText[--nPos]; + if( ' ' == cLast || '\x0a' == cLast) + aToken = aToken.copy(1); + } + else + aToken = aToken.copy(1); + + if( aToken.isEmpty() ) + { + m_bUpperSpace = bUpperSpaceSave; + break; + } + } + + if( !aToken.isEmpty() ) + { + if( !m_bDocInitalized ) + DocumentDetected(); + + if (!m_aEmbeds.empty()) + { + // The text token is inside an OLE object, which means + // alternate text. + SwOLENode* pOLENode = m_aEmbeds.top(); + if (SwFlyFrameFormat* pFormat + = dynamic_cast<SwFlyFrameFormat*>(pOLENode->GetFlyFormat())) + { + if (SdrObject* pObject = SwXFrame::GetOrCreateSdrObject(*pFormat)) + { + pObject->SetTitle(pObject->GetTitle() + aToken); + break; + } + } + } + + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken ); + + // if there are temporary paragraph attributes and the + // paragraph isn't empty then the paragraph attributes + // are final. + m_aParaAttrs.clear(); + + SetAttr(); + } + break; + + case HtmlTokenId::HORZRULE: + InsertHorzRule(); + break; + + case HtmlTokenId::IMAGE: + InsertImage(); + // if only the parser references the doc, we can break and set + // an error code + if( 1 == m_xDoc->getReferenceCount() ) + { + eState = SvParserState::Error; + } + break; + + case HtmlTokenId::SPACER: + InsertSpacer(); + break; + + case HtmlTokenId::EMBED: + InsertEmbed(); + break; + + case HtmlTokenId::NOEMBED_ON: + m_bInNoEmbed = true; + m_bCallNextToken = bool(m_xTable); + ReadRawData( OOO_STRING_SVTOOLS_HTML_noembed ); + break; + + case HtmlTokenId::DEFLIST_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewDefList(); + break; + case HtmlTokenId::DEFLIST_OFF: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndDefListItem( HtmlTokenId::NONE ); + EndDefList(); + break; + + case HtmlTokenId::DD_ON: + case HtmlTokenId::DT_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndDefListItem();// close <DD>/<DT> and set no template + NewDefListItem( nToken ); + break; + + case HtmlTokenId::DD_OFF: + case HtmlTokenId::DT_OFF: + // c.f. HtmlTokenId::LI_OFF + // Actually we should close a DD/DT now. + // But neither Netscape nor Microsoft do this and so don't we. + EndDefListItem( nToken ); + break; + + // divisions + case HtmlTokenId::DIVISION_ON: + case HtmlTokenId::CENTER_ON: + if (!m_isInTableStructure) + { + if (m_nOpenParaToken != HtmlTokenId::NONE) + { + if (IsReadPRE()) + m_nOpenParaToken = HtmlTokenId::NONE; + else + EndPara(); + } + NewDivision( nToken ); + } + break; + + case HtmlTokenId::DIVISION_OFF: + case HtmlTokenId::CENTER_OFF: + if (!m_isInTableStructure) + { + if (m_nOpenParaToken != HtmlTokenId::NONE) + { + if (IsReadPRE()) + m_nOpenParaToken = HtmlTokenId::NONE; + else + EndPara(); + } + EndDivision(); + } + break; + + case HtmlTokenId::MULTICOL_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewMultiCol(); + break; + + case HtmlTokenId::MULTICOL_OFF: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndTag( HtmlTokenId::MULTICOL_ON ); + break; + + case HtmlTokenId::MARQUEE_ON: + NewMarquee(); + m_bCallNextToken = m_pMarquee!=nullptr && m_xTable; + break; + + case HtmlTokenId::FORM_ON: + NewForm(); + break; + case HtmlTokenId::FORM_OFF: + EndForm(); + break; + + // templates + case HtmlTokenId::PARABREAK_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara( true ); + NewPara(); + break; + + case HtmlTokenId::PARABREAK_OFF: + EndPara( true ); + break; + + case HtmlTokenId::ADDRESS_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewTextFormatColl( HtmlTokenId::ADDRESS_ON, RES_POOLCOLL_SENDADRESS ); + break; + + case HtmlTokenId::ADDRESS_OFF: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndTextFormatColl( HtmlTokenId::ADDRESS_OFF ); + break; + + case HtmlTokenId::BLOCKQUOTE_ON: + case HtmlTokenId::BLOCKQUOTE30_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON, RES_POOLCOLL_HTML_BLOCKQUOTE ); + break; + + case HtmlTokenId::BLOCKQUOTE_OFF: + case HtmlTokenId::BLOCKQUOTE30_OFF: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON ); + break; + + case HtmlTokenId::PREFORMTXT_ON: + case HtmlTokenId::LISTING_ON: + case HtmlTokenId::XMP_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewTextFormatColl( nToken, RES_POOLCOLL_HTML_PRE ); + break; + + case HtmlTokenId::PREFORMTXT_OFF: + m_bNoParSpace = true; // the last PRE-paragraph gets a spacing + EndTextFormatColl( HtmlTokenId::PREFORMTXT_OFF ); + break; + + case HtmlTokenId::LISTING_OFF: + case HtmlTokenId::XMP_OFF: + EndTextFormatColl( nToken ); + break; + + case HtmlTokenId::HEAD1_ON: + case HtmlTokenId::HEAD2_ON: + case HtmlTokenId::HEAD3_ON: + case HtmlTokenId::HEAD4_ON: + case HtmlTokenId::HEAD5_ON: + case HtmlTokenId::HEAD6_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + { + if( IsReadPRE() ) + m_nOpenParaToken = HtmlTokenId::NONE; + else + EndPara(); + } + NewHeading( nToken ); + break; + + case HtmlTokenId::HEAD1_OFF: + case HtmlTokenId::HEAD2_OFF: + case HtmlTokenId::HEAD3_OFF: + case HtmlTokenId::HEAD4_OFF: + case HtmlTokenId::HEAD5_OFF: + case HtmlTokenId::HEAD6_OFF: + EndHeading(); + break; + + case HtmlTokenId::TABLE_ON: + if( !m_vPendingStack.empty() ) + BuildTable( SvxAdjust::End ); + else + { + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + OSL_ENSURE(!m_xTable, "table in table not allowed here"); + if( !m_xTable && (IsNewDoc() || !m_pPam->GetNode().FindTableNode()) && + (m_pPam->GetPoint()->nNode.GetIndex() > + m_xDoc->GetNodes().GetEndOfExtras().GetIndex() || + !m_pPam->GetNode().FindFootnoteStartNode() ) ) + { + if ( m_nParaCnt < 5 ) + Show(); // show what we have up to here + + SvxAdjust eAdjust = m_xAttrTab->pAdjust + ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()). + GetAdjust() + : SvxAdjust::End; + BuildTable( eAdjust ); + } + else + bInsertUnknown = m_bKeepUnknown; + } + break; + + // lists + case HtmlTokenId::DIRLIST_ON: + case HtmlTokenId::MENULIST_ON: + case HtmlTokenId::ORDERLIST_ON: + case HtmlTokenId::UNORDERLIST_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + NewNumberBulletList( nToken ); + break; + + case HtmlTokenId::DIRLIST_OFF: + case HtmlTokenId::MENULIST_OFF: + case HtmlTokenId::ORDERLIST_OFF: + case HtmlTokenId::UNORDERLIST_OFF: + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + EndNumberBulletListItem( HtmlTokenId::NONE, true ); + EndNumberBulletList( nToken ); + break; + + case HtmlTokenId::LI_ON: + case HtmlTokenId::LISTHEADER_ON: + if( m_nOpenParaToken != HtmlTokenId::NONE && + (m_pPam->GetPoint()->nContent.GetIndex() + || HtmlTokenId::PARABREAK_ON==m_nOpenParaToken) ) + { + // only finish paragraph for <P><LI>, not for <DD><LI> + EndPara(); + } + + EndNumberBulletListItem( HtmlTokenId::NONE, false );// close <LI>/<LH> and don't set a template + NewNumberBulletListItem( nToken ); + break; + + case HtmlTokenId::LI_OFF: + case HtmlTokenId::LISTHEADER_OFF: + EndNumberBulletListItem( nToken, false ); + break; + + // Attribute : + case HtmlTokenId::ITALIC_ON: + { + SvxPostureItem aPosture( ITALIC_NORMAL, RES_CHRATR_POSTURE ); + SvxPostureItem aPostureCJK( ITALIC_NORMAL, RES_CHRATR_CJK_POSTURE ); + SvxPostureItem aPostureCTL( ITALIC_NORMAL, RES_CHRATR_CTL_POSTURE ); + NewStdAttr( HtmlTokenId::ITALIC_ON, + &m_xAttrTab->pItalic, aPosture, + &m_xAttrTab->pItalicCJK, &aPostureCJK, + &m_xAttrTab->pItalicCTL, &aPostureCTL ); + } + break; + + case HtmlTokenId::BOLD_ON: + { + SvxWeightItem aWeight( WEIGHT_BOLD, RES_CHRATR_WEIGHT ); + SvxWeightItem aWeightCJK( WEIGHT_BOLD, RES_CHRATR_CJK_WEIGHT ); + SvxWeightItem aWeightCTL( WEIGHT_BOLD, RES_CHRATR_CTL_WEIGHT ); + NewStdAttr( HtmlTokenId::BOLD_ON, + &m_xAttrTab->pBold, aWeight, + &m_xAttrTab->pBoldCJK, &aWeightCJK, + &m_xAttrTab->pBoldCTL, &aWeightCTL ); + } + break; + + case HtmlTokenId::STRIKE_ON: + case HtmlTokenId::STRIKETHROUGH_ON: + { + NewStdAttr( HtmlTokenId::STRIKE_ON, &m_xAttrTab->pStrike, + SvxCrossedOutItem(STRIKEOUT_SINGLE, RES_CHRATR_CROSSEDOUT) ); + } + break; + + case HtmlTokenId::UNDERLINE_ON: + { + NewStdAttr( HtmlTokenId::UNDERLINE_ON, &m_xAttrTab->pUnderline, + SvxUnderlineItem(LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE) ); + } + break; + + case HtmlTokenId::SUPERSCRIPT_ON: + { + NewStdAttr( HtmlTokenId::SUPERSCRIPT_ON, &m_xAttrTab->pEscapement, + SvxEscapementItem(HTML_ESC_SUPER,HTML_ESC_PROP, RES_CHRATR_ESCAPEMENT) ); + } + break; + + case HtmlTokenId::SUBSCRIPT_ON: + { + NewStdAttr( HtmlTokenId::SUBSCRIPT_ON, &m_xAttrTab->pEscapement, + SvxEscapementItem(HTML_ESC_SUB,HTML_ESC_PROP, RES_CHRATR_ESCAPEMENT) ); + } + break; + + case HtmlTokenId::BLINK_ON: + { + NewStdAttr( HtmlTokenId::BLINK_ON, &m_xAttrTab->pBlink, + SvxBlinkItem( true, RES_CHRATR_BLINK ) ); + } + break; + + case HtmlTokenId::SPAN_ON: + NewStdAttr( HtmlTokenId::SPAN_ON ); + break; + + case HtmlTokenId::ITALIC_OFF: + case HtmlTokenId::BOLD_OFF: + case HtmlTokenId::STRIKE_OFF: + case HtmlTokenId::UNDERLINE_OFF: + case HtmlTokenId::SUPERSCRIPT_OFF: + case HtmlTokenId::SUBSCRIPT_OFF: + case HtmlTokenId::BLINK_OFF: + case HtmlTokenId::SPAN_OFF: + EndTag( nToken ); + break; + + case HtmlTokenId::STRIKETHROUGH_OFF: + EndTag( HtmlTokenId::STRIKE_OFF ); + break; + + case HtmlTokenId::BASEFONT_ON: + NewBasefontAttr(); + break; + case HtmlTokenId::BASEFONT_OFF: + EndBasefontAttr(); + break; + case HtmlTokenId::FONT_ON: + case HtmlTokenId::BIGPRINT_ON: + case HtmlTokenId::SMALLPRINT_ON: + NewFontAttr( nToken ); + break; + case HtmlTokenId::FONT_OFF: + case HtmlTokenId::BIGPRINT_OFF: + case HtmlTokenId::SMALLPRINT_OFF: + EndFontAttr( nToken ); + break; + + case HtmlTokenId::EMPHASIS_ON: + case HtmlTokenId::CITIATION_ON: + case HtmlTokenId::STRONG_ON: + case HtmlTokenId::CODE_ON: + case HtmlTokenId::SAMPLE_ON: + case HtmlTokenId::KEYBOARD_ON: + case HtmlTokenId::VARIABLE_ON: + case HtmlTokenId::DEFINSTANCE_ON: + case HtmlTokenId::SHORTQUOTE_ON: + case HtmlTokenId::LANGUAGE_ON: + case HtmlTokenId::AUTHOR_ON: + case HtmlTokenId::PERSON_ON: + case HtmlTokenId::ACRONYM_ON: + case HtmlTokenId::ABBREVIATION_ON: + case HtmlTokenId::INSERTEDTEXT_ON: + case HtmlTokenId::DELETEDTEXT_ON: + + case HtmlTokenId::TELETYPE_ON: + NewCharFormat( nToken ); + break; + + case HtmlTokenId::SDFIELD_ON: + NewField(); + m_bCallNextToken = m_bInField && m_xTable; + break; + + case HtmlTokenId::EMPHASIS_OFF: + case HtmlTokenId::CITIATION_OFF: + case HtmlTokenId::STRONG_OFF: + case HtmlTokenId::CODE_OFF: + case HtmlTokenId::SAMPLE_OFF: + case HtmlTokenId::KEYBOARD_OFF: + case HtmlTokenId::VARIABLE_OFF: + case HtmlTokenId::DEFINSTANCE_OFF: + case HtmlTokenId::SHORTQUOTE_OFF: + case HtmlTokenId::LANGUAGE_OFF: + case HtmlTokenId::AUTHOR_OFF: + case HtmlTokenId::PERSON_OFF: + case HtmlTokenId::ACRONYM_OFF: + case HtmlTokenId::ABBREVIATION_OFF: + case HtmlTokenId::INSERTEDTEXT_OFF: + case HtmlTokenId::DELETEDTEXT_OFF: + + case HtmlTokenId::TELETYPE_OFF: + EndTag( nToken ); + break; + + case HtmlTokenId::HEAD_OFF: + if( !m_aStyleSource.isEmpty() ) + { + m_pCSS1Parser->ParseStyleSheet( m_aStyleSource ); + m_aStyleSource.clear(); + } + break; + + case HtmlTokenId::DOCTYPE: + case HtmlTokenId::BODY_OFF: + case HtmlTokenId::HTML_OFF: + case HtmlTokenId::HEAD_ON: + case HtmlTokenId::TITLE_OFF: + break; // don't evaluate further??? + case HtmlTokenId::HTML_ON: + { + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + if( HtmlOptionId::DIR == rOption.GetToken() ) + { + const OUString& rDir = rOption.GetString(); + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), + m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + OUString aDummy; + ParseStyleOptions( aDummy, aDummy, aDummy, aItemSet, + aPropInfo, nullptr, &rDir ); + + m_pCSS1Parser->SetPageDescAttrs( nullptr, &aItemSet ); + break; + } + } + } + break; + + case HtmlTokenId::INPUT: + InsertInput(); + break; + + case HtmlTokenId::TEXTAREA_ON: + NewTextArea(); + m_bCallNextToken = m_bTextArea && m_xTable; + break; + + case HtmlTokenId::SELECT_ON: + NewSelect(); + m_bCallNextToken = m_bSelect && m_xTable; + break; + + case HtmlTokenId::ANCHOR_ON: + NewAnchor(); + break; + + case HtmlTokenId::ANCHOR_OFF: + EndAnchor(); + break; + + case HtmlTokenId::COMMENT: + if( ( aToken.getLength() > 5 ) && ( ! m_bIgnoreHTMLComments ) ) + { + // insert as Post-It + // If there are no space characters right behind + // the <!-- and on front of the -->, leave the comment untouched. + if( ' ' == aToken[ 3 ] && + ' ' == aToken[ aToken.getLength()-3 ] ) + { + OUString aComment( aToken.copy( 3, aToken.getLength()-5 ) ); + InsertComment(comphelper::string::strip(aComment, ' ')); + } + else + { + OUString aComment = "<" + aToken + ">"; + InsertComment( aComment ); + } + } + break; + + case HtmlTokenId::MAP_ON: + // Image Maps are read asynchronously: At first only an image map is created + // Areas are processed later. Nevertheless the + // ImageMap is inserted into the IMap-Array, because it might be used + // already. + m_pImageMap = new ImageMap; + if( ParseMapOptions( m_pImageMap) ) + { + if (!m_pImageMaps) + m_pImageMaps.reset( new ImageMaps ); + m_pImageMaps->push_back(std::unique_ptr<ImageMap>(m_pImageMap)); + } + else + { + delete m_pImageMap; + m_pImageMap = nullptr; + } + break; + + case HtmlTokenId::MAP_OFF: + // there is no ImageMap anymore (don't delete IMap, because it's + // already contained in the array!) + m_pImageMap = nullptr; + break; + + case HtmlTokenId::AREA: + if( m_pImageMap ) + ParseAreaOptions( m_pImageMap, m_sBaseURL, SvMacroItemId::OnMouseOver, + SvMacroItemId::OnMouseOut ); + break; + + case HtmlTokenId::FRAMESET_ON: + bInsertUnknown = m_bKeepUnknown; + break; + + case HtmlTokenId::NOFRAMES_ON: + if( IsInHeader() ) + FinishHeader(); + bInsertUnknown = m_bKeepUnknown; + break; + + case HtmlTokenId::UNKNOWNCONTROL_ON: + // Ignore content of unknown token in the header, if the token + // does not start with a '!'. + // (but judging from the code, also if does not start with a '%') + // (and also if we're not somewhere we consider PRE) + if( IsInHeader() && !IsReadPRE() && m_aUnknownToken.isEmpty() && + !sSaveToken.isEmpty() && '!' != sSaveToken[0] && + '%' != sSaveToken[0] ) + m_aUnknownToken = sSaveToken; + [[fallthrough]]; + + default: + bInsertUnknown = m_bKeepUnknown; + break; + } + + if( bGetIDOption ) + InsertIDOption(); + + if( bInsertUnknown ) + { + OUStringBuffer aComment("HTML: <"); + if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) ) + aComment.append("/"); + aComment.append(sSaveToken); + if( !aToken.isEmpty() ) + { + UnescapeToken(); + aComment.append(" ").append(aToken); + } + aComment.append(">"); + InsertComment( aComment.makeStringAndClear() ); + } + + // if there are temporary paragraph attributes and the + // paragraph isn't empty then the paragraph attributes are final. + if( !m_aParaAttrs.empty() && m_pPam->GetPoint()->nContent.GetIndex() ) + m_aParaAttrs.clear(); +} + +static void lcl_swhtml_getItemInfo( const HTMLAttr& rAttr, + bool& rScriptDependent, + sal_uInt16& rScriptType ) +{ + switch( rAttr.GetItem().Which() ) + { + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + rScriptType = i18n::ScriptType::LATIN; + rScriptDependent = true; + break; + case RES_CHRATR_CJK_FONT: + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + rScriptType = i18n::ScriptType::ASIAN; + rScriptDependent = true; + break; + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + rScriptType = i18n::ScriptType::COMPLEX; + rScriptDependent = true; + break; + default: + rScriptDependent = false; + break; + } +} + +bool SwHTMLParser::AppendTextNode( SwHTMLAppendMode eMode, bool bUpdateNum ) +{ + // A hard line break at the end always must be removed. + // A second one we replace with paragraph spacing. + sal_Int32 nLFStripped = StripTrailingLF(); + if( (AM_NOSPACE==eMode || AM_SOFTNOSPACE==eMode) && nLFStripped > 1 ) + eMode = AM_SPACE; + + // the hard attributes of this paragraph will never be invalid again + m_aParaAttrs.clear(); + + SwTextNode *pTextNode = (AM_SPACE==eMode || AM_NOSPACE==eMode) ? + m_pPam->GetPoint()->nNode.GetNode().GetTextNode() : nullptr; + + if (pTextNode) + { + const SvxULSpaceItem& rULSpace = + static_cast<const SvxULSpaceItem&>(pTextNode->SwContentNode::GetAttr( RES_UL_SPACE )); + + bool bChange = AM_NOSPACE==eMode ? rULSpace.GetLower() > 0 + : rULSpace.GetLower() == 0; + + if( bChange ) + { + const SvxULSpaceItem& rCollULSpace = + pTextNode->GetAnyFormatColl().GetULSpace(); + + bool bMayReset = AM_NOSPACE==eMode ? rCollULSpace.GetLower() == 0 + : rCollULSpace.GetLower() > 0; + + if( bMayReset && + rCollULSpace.GetUpper() == rULSpace.GetUpper() ) + { + pTextNode->ResetAttr( RES_UL_SPACE ); + } + else + { + pTextNode->SetAttr( + SvxULSpaceItem( rULSpace.GetUpper(), + AM_NOSPACE==eMode ? 0 : HTML_PARSPACE, RES_UL_SPACE ) ); + } + } + } + m_bNoParSpace = AM_NOSPACE==eMode || AM_SOFTNOSPACE==eMode; + + SwPosition aOldPos( *m_pPam->GetPoint() ); + + bool bRet = m_xDoc->getIDocumentContentOperations().AppendTextNode( *m_pPam->GetPoint() ); + + // split character attributes and maybe set none, + // which are set for the whole paragraph + const SwNodeIndex& rEndIdx = aOldPos.nNode; + const sal_Int32 nEndCnt = aOldPos.nContent.GetIndex(); + const SwPosition& rPos = *m_pPam->GetPoint(); + + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes) + { + HTMLAttr *pAttr = *pHTMLAttributes; + if( pAttr && pAttr->GetItem().Which() < RES_PARATR_BEGIN ) + { + bool bWholePara = false; + + while( pAttr ) + { + HTMLAttr *pNext = pAttr->GetNext(); + if( pAttr->GetSttParaIdx() < rEndIdx.GetIndex() || + (!bWholePara && + pAttr->GetSttPara() == rEndIdx && + pAttr->GetSttCnt() != nEndCnt) ) + { + bWholePara = + pAttr->GetSttPara() == rEndIdx && + pAttr->GetSttCnt() == 0; + + sal_Int32 nStt = pAttr->m_nStartContent; + bool bScript = false; + sal_uInt16 nScriptItem; + bool bInsert = true; + lcl_swhtml_getItemInfo( *pAttr, bScript, + nScriptItem ); + // set previous part + if( bScript ) + { + const SwTextNode *pTextNd = + pAttr->GetSttPara().GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "No text node" ); + if( pTextNd ) + { + const OUString& rText = pTextNd->GetText(); + sal_uInt16 nScriptText = + g_pBreakIt->GetBreakIter()->getScriptType( + rText, pAttr->GetSttCnt() ); + sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter() + ->endOfScript( rText, nStt, nScriptText ); + while (nScriptEnd < nEndCnt && nScriptEnd != -1) + { + if( nScriptItem == nScriptText ) + { + HTMLAttr *pSetAttr = + pAttr->Clone( rEndIdx, nScriptEnd ); + pSetAttr->m_nStartContent = nStt; + pSetAttr->ClearPrev(); + if( !pNext || bWholePara ) + { + if (pSetAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pSetAttr ); + else + m_aSetAttrTab.push_back( pSetAttr ); + } + else + pNext->InsertPrev( pSetAttr ); + } + nStt = nScriptEnd; + nScriptText = g_pBreakIt->GetBreakIter()->getScriptType( + rText, nStt ); + nScriptEnd = g_pBreakIt->GetBreakIter() + ->endOfScript( rText, nStt, nScriptText ); + } + bInsert = nScriptItem == nScriptText; + } + } + if( bInsert ) + { + HTMLAttr *pSetAttr = + pAttr->Clone( rEndIdx, nEndCnt ); + pSetAttr->m_nStartContent = nStt; + + // When the attribute is for the whole paragraph, the outer + // attributes aren't effective anymore. Hence it may not be inserted + // in the Prev-List of an outer attribute, because that won't be + // set. That leads to shifting when fields are used. + if( !pNext || bWholePara ) + { + if (pSetAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pSetAttr ); + else + m_aSetAttrTab.push_back( pSetAttr ); + } + else + pNext->InsertPrev( pSetAttr ); + } + else + { + HTMLAttr *pPrev = pAttr->GetPrev(); + if( pPrev ) + { + // the previous attributes must be set anyway + if( !pNext || bWholePara ) + { + if (pPrev->m_bInsAtStart) + m_aSetAttrTab.push_front( pPrev ); + else + m_aSetAttrTab.push_back( pPrev ); + } + else + pNext->InsertPrev( pPrev ); + } + } + pAttr->ClearPrev(); + } + + pAttr->SetStart( rPos ); + pAttr = pNext; + } + } + } + + if( bUpdateNum ) + { + if( GetNumInfo().GetDepth() ) + { + sal_uInt8 nLvl = GetNumInfo().GetLevel(); + SetNodeNum( nLvl ); + } + else + m_pPam->GetNode().GetTextNode()->ResetAttr( RES_PARATR_NUMRULE ); + } + + // We must set the attribute of the paragraph before now (because of JavaScript) + SetAttr(); + + // Now it is time to get rid of all script dependent hints that are + // equal to the settings in the style + SwTextNode *pTextNd = rEndIdx.GetNode().GetTextNode(); + OSL_ENSURE( pTextNd, "There is the txt node" ); + size_t nCntAttr = (pTextNd && pTextNd->GetpSwpHints()) + ? pTextNd->GetSwpHints().Count() : 0; + if( nCntAttr ) + { + // These are the end position of all script dependent hints. + // If we find a hint that starts before the current end position, + // we have to set it. If we find a hint that start behind or at + // that position, we have to take the hint value into account. + // If it is equal to the style, or in fact the paragraph value + // for that hint, the hint is removed. Otherwise its end position + // is remembered. + sal_Int32 aEndPos[15] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + SwpHints& rHints = pTextNd->GetSwpHints(); + for( size_t i=0; i < nCntAttr; i++ ) + { + SwTextAttr *pHt = rHints.Get( i ); + sal_uInt16 nWhich = pHt->Which(); + sal_Int16 nIdx = 0; + bool bFont = false; + switch( nWhich ) + { + case RES_CHRATR_FONT: + nIdx = 0; + bFont = true; + break; + case RES_CHRATR_FONTSIZE: + nIdx = 1; + break; + case RES_CHRATR_LANGUAGE: + nIdx = 2; + break; + case RES_CHRATR_POSTURE: + nIdx = 3; + break; + case RES_CHRATR_WEIGHT: + nIdx = 4; + break; + case RES_CHRATR_CJK_FONT: + nIdx = 5; + bFont = true; + break; + case RES_CHRATR_CJK_FONTSIZE: + nIdx = 6; + break; + case RES_CHRATR_CJK_LANGUAGE: + nIdx = 7; + break; + case RES_CHRATR_CJK_POSTURE: + nIdx = 8; + break; + case RES_CHRATR_CJK_WEIGHT: + nIdx = 9; + break; + case RES_CHRATR_CTL_FONT: + nIdx = 10; + bFont = true; + break; + case RES_CHRATR_CTL_FONTSIZE: + nIdx = 11; + break; + case RES_CHRATR_CTL_LANGUAGE: + nIdx = 12; + break; + case RES_CHRATR_CTL_POSTURE: + nIdx = 13; + break; + case RES_CHRATR_CTL_WEIGHT: + nIdx = 14; + break; + default: + // Skip to next attribute + continue; + } + const sal_Int32 nStt = pHt->GetStart(); + if( nStt >= aEndPos[nIdx] ) + { + const SfxPoolItem& rItem = + static_cast<const SwContentNode *>(pTextNd)->GetAttr( nWhich ); + if( bFont ? swhtml_css1atr_equalFontItems(rItem,pHt->GetAttr()) + : rItem == pHt->GetAttr() ) + { + // The hint is the same as set in the paragraph and + // therefore, it can be deleted + // CAUTION!!! This WILL delete the hint and it MAY + // also delete the SwpHints!!! To avoid any trouble + // we leave the loop immediately if this is the last + // hint. + pTextNd->DeleteAttribute( pHt ); + if( 1 == nCntAttr ) + break; + i--; + nCntAttr--; + } + else + { + // The hint is different. Therefore all hints within that + // hint have to be ignored. + aEndPos[nIdx] = pHt->GetEnd() ? *pHt->GetEnd() : nStt; + } + } + else + { + // The hint starts before another one ends. + // The hint in this case is not deleted + OSL_ENSURE( pHt->GetEnd() && *pHt->GetEnd() <= aEndPos[nIdx], + "hints aren't nested properly!" ); + } + } + } + + if (!m_xTable && !--m_nParaCnt) + Show(); + + return bRet; +} + +void SwHTMLParser::AddParSpace() +{ + //If it already has ParSpace, return + if( !m_bNoParSpace ) + return; + + m_bNoParSpace = false; + + sal_uLong nNdIdx = m_pPam->GetPoint()->nNode.GetIndex() - 1; + + SwTextNode *pTextNode = m_xDoc->GetNodes()[nNdIdx]->GetTextNode(); + if( !pTextNode ) + return; + + SvxULSpaceItem rULSpace = + static_cast<const SvxULSpaceItem&>(pTextNode->SwContentNode::GetAttr( RES_UL_SPACE )); + if( !rULSpace.GetLower() ) + { + const SvxULSpaceItem& rCollULSpace = + pTextNode->GetAnyFormatColl().GetULSpace(); + if( rCollULSpace.GetLower() && + rCollULSpace.GetUpper() == rULSpace.GetUpper() ) + { + pTextNode->ResetAttr( RES_UL_SPACE ); + } + else + { + //What I do here, is that I examine the attributes, and if + //I find out, that it's CJK/CTL, then I set the paragraph space + //to the value set in HTML_CJK_PARSPACE/HTML_CTL_PARSPACE. + + bool bIsCJK = false; + bool bIsCTL = false; + + const size_t nCntAttr = pTextNode->GetpSwpHints() + ? pTextNode->GetSwpHints().Count() : 0; + + for(size_t i = 0; i < nCntAttr; ++i) + { + SwTextAttr *const pHt = pTextNode->GetSwpHints().Get(i); + sal_uInt16 const nWhich = pHt->Which(); + if (RES_CHRATR_CJK_FONT == nWhich || + RES_CHRATR_CJK_FONTSIZE == nWhich || + RES_CHRATR_CJK_LANGUAGE == nWhich || + RES_CHRATR_CJK_POSTURE == nWhich || + RES_CHRATR_CJK_WEIGHT == nWhich) + { + bIsCJK = true; + break; + } + if (RES_CHRATR_CTL_FONT == nWhich || + RES_CHRATR_CTL_FONTSIZE == nWhich || + RES_CHRATR_CTL_LANGUAGE == nWhich || + RES_CHRATR_CTL_POSTURE == nWhich || + RES_CHRATR_CTL_WEIGHT == nWhich) + { + bIsCTL = true; + break; + } + } + + if( bIsCTL ) + { + pTextNode->SetAttr( + SvxULSpaceItem( rULSpace.GetUpper(), HTML_CTL_PARSPACE, RES_UL_SPACE ) ); + } + else if( bIsCJK ) + { + pTextNode->SetAttr( + SvxULSpaceItem( rULSpace.GetUpper(), HTML_CJK_PARSPACE, RES_UL_SPACE ) ); + } else { + pTextNode->SetAttr( + SvxULSpaceItem( rULSpace.GetUpper(), HTML_PARSPACE, RES_UL_SPACE ) ); + } + } + } +} + +void SwHTMLParser::Show() +{ + // Here + // - a EndAction is called, so the document is formatted + // - a Reschedule is called, + // - the own View-Shell is set again + // - and a StartAction is called + + OSL_ENSURE( SvParserState::Working==eState, "Show not in working state - That can go wrong" ); + SwViewShell *pOldVSh = CallEndAction(); + + Application::Reschedule(); + + if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + || 1 == m_xDoc->getReferenceCount() ) + { + // was the import aborted by SFX? + eState = SvParserState::Error; + } + + // Fetch the SwViewShell again, as it could be destroyed in Reschedule. + SwViewShell *pVSh = CallStartAction( pOldVSh ); + + // is the current node not visible anymore, then we use a bigger increment + if( pVSh ) + { + m_nParaCnt = (m_pPam->GetPoint()->nNode.GetNode().IsInVisibleArea(pVSh)) + ? 5 : 50; + } +} + +void SwHTMLParser::ShowStatline() +{ + // Here + // - a Reschedule is called, so it can be scrolled + // - the own View-Shell is set again + // - a StartAction/EndAction is called, when there was scrolling. + + OSL_ENSURE( SvParserState::Working==eState, "ShowStatLine not in working state - That can go wrong" ); + + // scroll bar + if (m_xProgress) + { + m_xProgress->Update(rInput.Tell()); + CheckActionViewShell(); + } + else + { + Application::Reschedule(); + + if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() ) + || 1 == m_xDoc->getReferenceCount() ) + // was the import aborted by SFX? + eState = SvParserState::Error; + + SwViewShell *pVSh = CheckActionViewShell(); + if( pVSh && pVSh->HasInvalidRect() ) + { + CallEndAction( false, false ); + CallStartAction( pVSh, false ); + } + } +} + +SwViewShell *SwHTMLParser::CallStartAction( SwViewShell *pVSh, bool bChkPtr ) +{ + OSL_ENSURE( !m_pActionViewShell, "CallStartAction: SwViewShell already set" ); + + if( !pVSh || bChkPtr ) + { +#if OSL_DEBUG_LEVEL > 0 + SwViewShell *pOldVSh = pVSh; +#endif + pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( !pVSh || !pOldVSh || pOldVSh == pVSh, "CallStartAction: Who swapped the SwViewShell?" ); + if( pOldVSh && !pVSh ) + pVSh = nullptr; +#endif + } + m_pActionViewShell = pVSh; + + if( m_pActionViewShell ) + { + if( dynamic_cast< const SwEditShell *>( m_pActionViewShell ) != nullptr ) + static_cast<SwEditShell*>(m_pActionViewShell)->StartAction(); + else + m_pActionViewShell->StartAction(); + } + + return m_pActionViewShell; +} + +SwViewShell *SwHTMLParser::CallEndAction( bool bChkAction, bool bChkPtr ) +{ + if( bChkPtr ) + { + SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh, + "CallEndAction: Who swapped the SwViewShell?" ); +#if OSL_DEBUG_LEVEL > 0 + if( m_pActionViewShell && !pVSh ) + pVSh = nullptr; +#endif + if( pVSh != m_pActionViewShell ) + m_pActionViewShell = nullptr; + } + + if( !m_pActionViewShell || (bChkAction && !m_pActionViewShell->ActionPend()) ) + return m_pActionViewShell; + + if( dynamic_cast< const SwEditShell *>( m_pActionViewShell ) != nullptr ) + { + // Already scrolled?, then make sure that the view doesn't move! + const bool bOldLock = m_pActionViewShell->IsViewLocked(); + m_pActionViewShell->LockView( true ); + const bool bOldEndActionByVirDev = m_pActionViewShell->IsEndActionByVirDev(); + m_pActionViewShell->SetEndActionByVirDev( true ); + static_cast<SwEditShell*>(m_pActionViewShell)->EndAction(); + m_pActionViewShell->SetEndActionByVirDev( bOldEndActionByVirDev ); + m_pActionViewShell->LockView( bOldLock ); + + // bChkJumpMark is only set when the object was also found + if( m_bChkJumpMark ) + { + const Point aVisSttPos( DOCUMENTBORDER, DOCUMENTBORDER ); + if( GetMedium() && aVisSttPos == m_pActionViewShell->VisArea().Pos() ) + ::JumpToSwMark( m_pActionViewShell, + GetMedium()->GetURLObject().GetMark() ); + m_bChkJumpMark = false; + } + } + else + m_pActionViewShell->EndAction(); + + // if the parser holds the last reference to the document, then we can + // abort here and set an error. + if( 1 == m_xDoc->getReferenceCount() ) + { + eState = SvParserState::Error; + } + + SwViewShell *pVSh = m_pActionViewShell; + m_pActionViewShell = nullptr; + + return pVSh; +} + +SwViewShell *SwHTMLParser::CheckActionViewShell() +{ + SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh, + "CheckActionViewShell: Who has swapped SwViewShell?" ); +#if OSL_DEBUG_LEVEL > 0 + if( m_pActionViewShell && !pVSh ) + pVSh = nullptr; +#endif + if( pVSh != m_pActionViewShell ) + m_pActionViewShell = nullptr; + + return m_pActionViewShell; +} + +SwHTMLFrameFormatListener::SwHTMLFrameFormatListener(SwFrameFormat* pFrameFormat) + : m_pFrameFormat(pFrameFormat) +{ + StartListening(m_pFrameFormat->GetNotifier()); +} + +void SwHTMLFrameFormatListener::Notify(const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + m_pFrameFormat = nullptr; +} + +void SwHTMLParser::SetAttr_( bool bChkEnd, bool bBeforeTable, + std::deque<std::unique_ptr<HTMLAttr>> *pPostIts ) +{ + std::unique_ptr<SwPaM> pAttrPam( new SwPaM( *m_pPam->GetPoint() ) ); + const SwNodeIndex& rEndIdx = m_pPam->GetPoint()->nNode; + const sal_Int32 nEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + HTMLAttr* pAttr; + SwContentNode* pCNd; + + std::vector<std::unique_ptr<HTMLAttr>> aFields; + + for( auto n = m_aSetAttrTab.size(); n; ) + { + pAttr = m_aSetAttrTab[ --n ]; + sal_uInt16 nWhich = pAttr->m_pItem->Which(); + + sal_uLong nEndParaIdx = pAttr->GetEndParaIdx(); + bool bSetAttr; + if( bChkEnd ) + { + // Set character attribute with end early on, so set them still in + // the current paragraph (because of JavaScript and various "chats"(?)). + // This shouldn't be done for attributes which are used for + // the whole paragraph, because they could be from a paragraph style + // which can't be set. Because the attributes are inserted with + // SETATTR_DONTREPLACE, they should be able to be set later. + bSetAttr = ( nEndParaIdx < rEndIdx.GetIndex() && + (RES_LR_SPACE != nWhich || !GetNumInfo().GetNumRule()) ) || + ( !pAttr->IsLikePara() && + nEndParaIdx == rEndIdx.GetIndex() && + pAttr->GetEndCnt() < nEndCnt && + (isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)) ) || + ( bBeforeTable && + nEndParaIdx == rEndIdx.GetIndex() && + !pAttr->GetEndCnt() ); + } + else + { + // Attributes in body nodes array section shouldn't be set if we are in a + // special nodes array section, but vice versa it's possible. + sal_uLong nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex(); + bSetAttr = nEndParaIdx < rEndIdx.GetIndex() || + rEndIdx.GetIndex() > nEndOfIcons || + nEndParaIdx <= nEndOfIcons; + } + + if( bSetAttr ) + { + // The attribute shouldn't be in the list of temporary paragraph + // attributes, because then it would be deleted. + while( !m_aParaAttrs.empty() ) + { + OSL_ENSURE( pAttr != m_aParaAttrs.back(), + "SetAttr: Attribute must not yet be set" ); + m_aParaAttrs.pop_back(); + } + + // then set it + m_aSetAttrTab.erase( m_aSetAttrTab.begin() + n ); + + while( pAttr ) + { + HTMLAttr *pPrev = pAttr->GetPrev(); + if( !pAttr->m_bValid ) + { + // invalid attributes can be deleted + delete pAttr; + pAttr = pPrev; + continue; + } + + pCNd = pAttr->m_nStartPara.GetNode().GetContentNode(); + if( !pCNd ) + { + // because of the awful deleting of nodes an index can also + // point to an end node :-( + if ( (pAttr->GetSttPara() == pAttr->GetEndPara()) && + !isTXTATR_NOEND(nWhich) ) + { + // when the end index also points to the node, we don't + // need to set attributes anymore, except if it's a text attribute. + delete pAttr; + pAttr = pPrev; + continue; + } + pCNd = m_xDoc->GetNodes().GoNext( &(pAttr->m_nStartPara) ); + if( pCNd ) + pAttr->m_nStartContent = 0; + else + { + OSL_ENSURE( false, "SetAttr: GoNext() failed!" ); + delete pAttr; + pAttr = pPrev; + continue; + } + } + pAttrPam->GetPoint()->nNode = pAttr->m_nStartPara; + + // because of the deleting of BRs the start index can also + // point behind the end the text + if( pAttr->m_nStartContent > pCNd->Len() ) + pAttr->m_nStartContent = pCNd->Len(); + pAttrPam->GetPoint()->nContent.Assign( pCNd, pAttr->m_nStartContent ); + + pAttrPam->SetMark(); + if ( (pAttr->GetSttPara() != pAttr->GetEndPara()) && + !isTXTATR_NOEND(nWhich) ) + { + pCNd = pAttr->m_nEndPara.GetNode().GetContentNode(); + if( !pCNd ) + { + pCNd = SwNodes::GoPrevious( &(pAttr->m_nEndPara) ); + if( pCNd ) + pAttr->m_nEndContent = pCNd->Len(); + else + { + OSL_ENSURE( false, "SetAttr: GoPrevious() failed!" ); + pAttrPam->DeleteMark(); + delete pAttr; + pAttr = pPrev; + continue; + } + } + + pAttrPam->GetPoint()->nNode = pAttr->m_nEndPara; + } + else if( pAttr->IsLikePara() ) + { + pAttr->m_nEndContent = pCNd->Len(); + } + + // because of the deleting of BRs the start index can also + // point behind the end the text + if( pAttr->m_nEndContent > pCNd->Len() ) + pAttr->m_nEndContent = pCNd->Len(); + + pAttrPam->GetPoint()->nContent.Assign( pCNd, pAttr->m_nEndContent ); + if( bBeforeTable && + pAttrPam->GetPoint()->nNode.GetIndex() == + rEndIdx.GetIndex() ) + { + // If we're before inserting a table and the attribute ends + // in the current node, then we must end it in the previous + // node or discard it, if it starts in that node. + if( nWhich != RES_BREAK && nWhich != RES_PAGEDESC && + !isTXTATR_NOEND(nWhich) ) + { + if( pAttrPam->GetMark()->nNode.GetIndex() != + rEndIdx.GetIndex() ) + { + OSL_ENSURE( !pAttrPam->GetPoint()->nContent.GetIndex(), + "Content-Position before table not 0???" ); + pAttrPam->Move( fnMoveBackward ); + } + else + { + pAttrPam->DeleteMark(); + delete pAttr; + pAttr = pPrev; + continue; + } + } + } + + switch( nWhich ) + { + case RES_FLTR_BOOKMARK: // insert bookmark + { + const OUString sName( static_cast<SfxStringItem*>(pAttr->m_pItem.get())->GetValue() ); + IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark( sName ); + if( ppBkmk != pMarkAccess->getAllMarksEnd() && + (*ppBkmk)->GetMarkStart() == *pAttrPam->GetPoint() ) + break; // do not generate duplicates on this position + pAttrPam->DeleteMark(); + const ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark( + *pAttrPam, + sName, + IDocumentMarkAccess::MarkType::BOOKMARK, + ::sw::mark::InsertMode::New); + + // jump to bookmark + if( JumpToMarks::Mark == m_eJumpTo && pNewMark->GetName() == m_sJmpMark ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + } + break; + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + { + SwFieldIds nFieldWhich = + pPostIts + ? static_cast<const SwFormatField *>(pAttr->m_pItem.get())->GetField()->GetTyp()->Which() + : SwFieldIds::Database; + if( pPostIts && (SwFieldIds::Postit == nFieldWhich || + SwFieldIds::Script == nFieldWhich) ) + { + pPostIts->emplace_front( pAttr ); + } + else + { + aFields.emplace_back( pAttr); + } + } + pAttrPam->DeleteMark(); + pAttr = pPrev; + continue; + + case RES_LR_SPACE: + if( pAttrPam->GetPoint()->nNode.GetIndex() == + pAttrPam->GetMark()->nNode.GetIndex()) + { + // because of numbering set this attribute directly at node + pCNd->SetAttr( *pAttr->m_pItem ); + break; + } + OSL_ENSURE( false, + "LRSpace set over multiple paragraphs!" ); + [[fallthrough]]; // (shouldn't reach this point anyway) + + // tdf#94088 expand RES_BACKGROUND to the new fill attribute + // definitions in the range [XATTR_FILL_FIRST .. XATTR_FILL_LAST]. + // This is the right place in the future if the adapted fill attributes + // may be handled more directly in HTML import to handle them. + case RES_BACKGROUND: + { + const SvxBrushItem& rBrush = static_cast< SvxBrushItem& >(*pAttr->m_pItem); + SfxItemSet aNewSet(m_xDoc->GetAttrPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + setSvxBrushItemAsFillAttributesToTargetSet(rBrush, aNewSet); + m_xDoc->getIDocumentContentOperations().InsertItemSet(*pAttrPam, aNewSet, SetAttrMode::DONTREPLACE); + break; + } + default: + + // maybe jump to a bookmark + if( RES_TXTATR_INETFMT == nWhich && + JumpToMarks::Mark == m_eJumpTo && + m_sJmpMark == static_cast<SwFormatINetFormat*>(pAttr->m_pItem.get())->GetName() ) + { + m_bChkJumpMark = true; + m_eJumpTo = JumpToMarks::NONE; + } + + m_xDoc->getIDocumentContentOperations().InsertPoolItem( *pAttrPam, *pAttr->m_pItem, SetAttrMode::DONTREPLACE ); + } + pAttrPam->DeleteMark(); + + delete pAttr; + pAttr = pPrev; + } + } + } + + for( auto n = m_aMoveFlyFrames.size(); n; ) + { + SwFrameFormat *pFrameFormat = m_aMoveFlyFrames[--n]->GetFrameFormat(); + if (!pFrameFormat) + { + SAL_WARN("sw.html", "SwFrameFormat deleted during import"); + m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n ); + m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n ); + continue; + } + + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + OSL_ENSURE( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId(), + "Only At-Para flys need special handling" ); + const SwPosition *pFlyPos = rAnchor.GetContentAnchor(); + sal_uLong nFlyParaIdx = pFlyPos->nNode.GetIndex(); + bool bMoveFly; + if( bChkEnd ) + { + bMoveFly = nFlyParaIdx < rEndIdx.GetIndex() || + ( nFlyParaIdx == rEndIdx.GetIndex() && + m_aMoveFlyCnts[n] < nEndCnt ); + } + else + { + sal_uLong nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex(); + bMoveFly = nFlyParaIdx < rEndIdx.GetIndex() || + rEndIdx.GetIndex() > nEndOfIcons || + nFlyParaIdx <= nEndOfIcons; + } + if( bMoveFly ) + { + pFrameFormat->DelFrames(); + *pAttrPam->GetPoint() = *pFlyPos; + pAttrPam->GetPoint()->nContent.Assign( pAttrPam->GetContentNode(), + m_aMoveFlyCnts[n] ); + SwFormatAnchor aAnchor( rAnchor ); + aAnchor.SetType( RndStdIds::FLY_AT_CHAR ); + aAnchor.SetAnchor( pAttrPam->GetPoint() ); + pFrameFormat->SetFormatAttr( aAnchor ); + + const SwFormatHoriOrient& rHoriOri = pFrameFormat->GetHoriOrient(); + if( text::HoriOrientation::LEFT == rHoriOri.GetHoriOrient() ) + { + SwFormatHoriOrient aHoriOri( rHoriOri ); + aHoriOri.SetRelationOrient( text::RelOrientation::CHAR ); + pFrameFormat->SetFormatAttr( aHoriOri ); + } + const SwFormatVertOrient& rVertOri = pFrameFormat->GetVertOrient(); + if( text::VertOrientation::TOP == rVertOri.GetVertOrient() ) + { + SwFormatVertOrient aVertOri( rVertOri ); + aVertOri.SetRelationOrient( text::RelOrientation::CHAR ); + pFrameFormat->SetFormatAttr( aVertOri ); + } + + pFrameFormat->MakeFrames(); + m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n ); + m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n ); + } + } + for (auto & field : aFields) + { + pCNd = field->m_nStartPara.GetNode().GetContentNode(); + pAttrPam->GetPoint()->nNode = field->m_nStartPara; + pAttrPam->GetPoint()->nContent.Assign( pCNd, field->m_nStartContent ); + + if( bBeforeTable && + pAttrPam->GetPoint()->nNode.GetIndex() == rEndIdx.GetIndex() ) + { + OSL_ENSURE( !bBeforeTable, "Aha, the case does occur" ); + OSL_ENSURE( !pAttrPam->GetPoint()->nContent.GetIndex(), + "Content-Position before table not 0???" ); + // !!! + pAttrPam->Move( fnMoveBackward ); + } + + m_xDoc->getIDocumentContentOperations().InsertPoolItem( *pAttrPam, *field->m_pItem ); + + field.reset(); + } + aFields.clear(); +} + +void SwHTMLParser::NewAttr(const std::shared_ptr<HTMLAttrTable>& rAttrTable, HTMLAttr **ppAttr, const SfxPoolItem& rItem ) +{ + // Font height and font colour as well as escape attributes may not be + // combined. Therefore they're saved in a list and in it the last opened + // attribute is at the beginning and count is always one. For all other + // attributes count is just incremented. + if( *ppAttr ) + { + HTMLAttr *pAttr = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable); + pAttr->InsertNext( *ppAttr ); + (*ppAttr) = pAttr; + } + else + (*ppAttr) = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable); +} + +bool SwHTMLParser::EndAttr( HTMLAttr* pAttr, bool bChkEmpty ) +{ + bool bRet = true; + + // The list header is saved in the attribute. + HTMLAttr **ppHead = pAttr->m_ppHead; + + OSL_ENSURE( ppHead, "No list header attribute found!" ); + + // save the current position as end position + const SwNodeIndex* pEndIdx = &m_pPam->GetPoint()->nNode; + sal_Int32 nEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + + // Is the last started or an earlier started attribute being ended? + HTMLAttr *pLast = nullptr; + if( ppHead && pAttr != *ppHead ) + { + // The last started attribute isn't being ended + + // Then we look for attribute which was started immediately afterwards, + // which has also not yet been ended (otherwise it would no longer be + // in the list). + pLast = *ppHead; + while( pLast && pLast->GetNext() != pAttr ) + pLast = pLast->GetNext(); + + OSL_ENSURE( pLast, "Attribute not found in own list!" ); + } + + bool bMoveBack = false; + sal_uInt16 nWhich = pAttr->m_pItem->Which(); + if( !nEndCnt && RES_PARATR_BEGIN <= nWhich && + *pEndIdx != pAttr->GetSttPara() ) + { + // Then move back one position in the content! + bMoveBack = m_pPam->Move( fnMoveBackward ); + nEndCnt = m_pPam->GetPoint()->nContent.GetIndex(); + } + + // now end the attribute + HTMLAttr *pNext = pAttr->GetNext(); + + bool bInsert; + sal_uInt16 nScriptItem = 0; + bool bScript = false; + // does it have a non-empty range? + if( !bChkEmpty || (RES_PARATR_BEGIN <= nWhich && bMoveBack) || + RES_PAGEDESC == nWhich || RES_BREAK == nWhich || + *pEndIdx != pAttr->GetSttPara() || + nEndCnt != pAttr->GetSttCnt() ) + { + bInsert = true; + // We do some optimization for script dependent attributes here. + if( *pEndIdx == pAttr->GetSttPara() ) + { + lcl_swhtml_getItemInfo( *pAttr, bScript, nScriptItem ); + } + } + else + { + bInsert = false; + } + + const SwTextNode *pTextNd = (bInsert && bScript) ? + pAttr->GetSttPara().GetNode().GetTextNode() : + nullptr; + + if (pTextNd) + { + const OUString& rText = pTextNd->GetText(); + sal_uInt16 nScriptText = g_pBreakIt->GetBreakIter()->getScriptType( + rText, pAttr->GetSttCnt() ); + sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter() + ->endOfScript( rText, pAttr->GetSttCnt(), nScriptText ); + while (nScriptEnd < nEndCnt && nScriptEnd != -1) + { + if( nScriptItem == nScriptText ) + { + HTMLAttr *pSetAttr = pAttr->Clone( *pEndIdx, nScriptEnd ); + pSetAttr->ClearPrev(); + if( pNext ) + pNext->InsertPrev( pSetAttr ); + else + { + if (pSetAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pSetAttr ); + else + m_aSetAttrTab.push_back( pSetAttr ); + } + } + pAttr->m_nStartContent = nScriptEnd; + nScriptText = g_pBreakIt->GetBreakIter()->getScriptType( + rText, nScriptEnd ); + nScriptEnd = g_pBreakIt->GetBreakIter() + ->endOfScript( rText, nScriptEnd, nScriptText ); + } + bInsert = nScriptItem == nScriptText; + } + if( bInsert ) + { + pAttr->m_nEndPara = *pEndIdx; + pAttr->m_nEndContent = nEndCnt; + pAttr->m_bInsAtStart = RES_TXTATR_INETFMT != nWhich && + RES_TXTATR_CHARFMT != nWhich; + + if( !pNext ) + { + // No open attributes of that type exists any longer, so all + // can be set. Except they depend on another attribute, then + // they're appended there. + if (pAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pAttr ); + else + m_aSetAttrTab.push_back( pAttr ); + } + else + { + // There are other open attributes of that type, + // therefore the setting must be postponed. + // Hence the current attribute is added at the end + // of the Prev-List of the successor. + pNext->InsertPrev( pAttr ); + } + } + else + { + // Then don't insert, but delete. Because of the "faking" of styles + // by hard attributing there can be also other empty attributes in the + // Prev-List, which must be set anyway. + HTMLAttr *pPrev = pAttr->GetPrev(); + bRet = false; + delete pAttr; + + if( pPrev ) + { + // The previous attributes must be set anyway. + if( pNext ) + pNext->InsertPrev( pPrev ); + else + { + if (pPrev->m_bInsAtStart) + m_aSetAttrTab.push_front( pPrev ); + else + m_aSetAttrTab.push_back( pPrev ); + } + } + + } + + // If the first attribute of the list was set, then the list header + // must be corrected as well. + if( pLast ) + pLast->m_pNext = pNext; + else if( ppHead ) + *ppHead = pNext; + + if( bMoveBack ) + m_pPam->Move( fnMoveForward ); + + return bRet; +} + +void SwHTMLParser::DeleteAttr( HTMLAttr* pAttr ) +{ + // preliminary paragraph attributes are not allowed here, they could + // be set here and then the pointers become invalid! + OSL_ENSURE(m_aParaAttrs.empty(), + "Danger: there are non-final paragraph attributes"); + m_aParaAttrs.clear(); + + // The list header is saved in the attribute + HTMLAttr **ppHead = pAttr->m_ppHead; + + OSL_ENSURE( ppHead, "no list header attribute found!" ); + + // Is the last started or an earlier started attribute being removed? + HTMLAttr *pLast = nullptr; + if( ppHead && pAttr != *ppHead ) + { + // The last started attribute isn't being ended + + // Then we look for attribute which was started immediately afterwards, + // which has also not yet been ended (otherwise it would no longer be + // in the list). + pLast = *ppHead; + while( pLast && pLast->GetNext() != pAttr ) + pLast = pLast->GetNext(); + + OSL_ENSURE( pLast, "Attribute not found in own list!" ); + } + + // now delete the attribute + HTMLAttr *pNext = pAttr->GetNext(); + HTMLAttr *pPrev = pAttr->GetPrev(); + //hold ref to xAttrTab until end of scope to ensure *ppHead validity + std::shared_ptr<HTMLAttrTable> xKeepAlive(pAttr->m_xAttrTab); + delete pAttr; + + if( pPrev ) + { + // The previous attributes must be set anyway. + if( pNext ) + pNext->InsertPrev( pPrev ); + else + { + if (pPrev->m_bInsAtStart) + m_aSetAttrTab.push_front( pPrev ); + else + m_aSetAttrTab.push_back( pPrev ); + } + } + + // If the first attribute of the list was deleted, then the list header + // must be corrected as well. + if( pLast ) + pLast->m_pNext = pNext; + else if( ppHead ) + *ppHead = pNext; +} + +void SwHTMLParser::SaveAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab) +{ + // preliminary paragraph attributes are not allowed here, they could + // be set here and then the pointers become invalid! + OSL_ENSURE(m_aParaAttrs.empty(), + "Danger: there are non-final paragraph attributes"); + m_aParaAttrs.clear(); + + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get()); + + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes) + { + *pSaveAttributes = *pHTMLAttributes; + + HTMLAttr *pAttr = *pSaveAttributes; + while (pAttr) + { + pAttr->SetHead(pSaveAttributes, rNewAttrTab); + pAttr = pAttr->GetNext(); + } + + *pHTMLAttributes = nullptr; + } +} + +void SwHTMLParser::SplitAttrTab( std::shared_ptr<HTMLAttrTable> const & rNewAttrTab, + bool bMoveEndBack ) +{ + // preliminary paragraph attributes are not allowed here, they could + // be set here and then the pointers become invalid! + OSL_ENSURE(m_aParaAttrs.empty(), + "Danger: there are non-final paragraph attributes"); + m_aParaAttrs.clear(); + + const SwNodeIndex& nSttIdx = m_pPam->GetPoint()->nNode; + SwNodeIndex nEndIdx( nSttIdx ); + + // close all still open attributes and re-open them after the table + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get()); + bool bSetAttr = true; + const sal_Int32 nSttCnt = m_pPam->GetPoint()->nContent.GetIndex(); + sal_Int32 nEndCnt = nSttCnt; + + if( bMoveEndBack ) + { + sal_uLong nOldEnd = nEndIdx.GetIndex(); + sal_uLong nTmpIdx; + if( ( nTmpIdx = m_xDoc->GetNodes().GetEndOfExtras().GetIndex()) >= nOldEnd || + ( nTmpIdx = m_xDoc->GetNodes().GetEndOfAutotext().GetIndex()) >= nOldEnd ) + { + nTmpIdx = m_xDoc->GetNodes().GetEndOfInserts().GetIndex(); + } + SwContentNode* pCNd = SwNodes::GoPrevious(&nEndIdx); + + // Don't set attributes, when the PaM was moved outside of the content area. + bSetAttr = pCNd && nTmpIdx < nEndIdx.GetIndex(); + + nEndCnt = (bSetAttr ? pCNd->Len() : 0); + } + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; (++pHTMLAttributes, ++pSaveAttributes)) + { + HTMLAttr *pAttr = *pHTMLAttributes; + *pSaveAttributes = nullptr; + while( pAttr ) + { + HTMLAttr *pNext = pAttr->GetNext(); + HTMLAttr *pPrev = pAttr->GetPrev(); + + if( bSetAttr && + ( pAttr->GetSttParaIdx() < nEndIdx.GetIndex() || + (pAttr->GetSttPara() == nEndIdx && + pAttr->GetSttCnt() != nEndCnt) ) ) + { + // The attribute must be set before the list. We need the + // original and therefore we clone it, because pointer to the + // attribute exist in the other contexts. The Next-List is lost + // in doing so, but the Previous-List is preserved. + HTMLAttr *pSetAttr = pAttr->Clone( nEndIdx, nEndCnt ); + + if( pNext ) + pNext->InsertPrev( pSetAttr ); + else + { + if (pSetAttr->m_bInsAtStart) + m_aSetAttrTab.push_front( pSetAttr ); + else + m_aSetAttrTab.push_back( pSetAttr ); + } + } + else if( pPrev ) + { + // If the attribute doesn't need to be set before the table, then + // the previous attributes must still be set. + if( pNext ) + pNext->InsertPrev( pPrev ); + else + { + if (pPrev->m_bInsAtStart) + m_aSetAttrTab.push_front( pPrev ); + else + m_aSetAttrTab.push_back( pPrev ); + } + } + + // set the start of the attribute anew and break link + pAttr->Reset(nSttIdx, nSttCnt, pSaveAttributes, rNewAttrTab); + + if (*pSaveAttributes) + { + HTMLAttr *pSAttr = *pSaveAttributes; + while( pSAttr->GetNext() ) + pSAttr = pSAttr->GetNext(); + pSAttr->InsertNext( pAttr ); + } + else + *pSaveAttributes = pAttr; + + pAttr = pNext; + } + + *pHTMLAttributes = nullptr; + } +} + +void SwHTMLParser::RestoreAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab) +{ + // preliminary paragraph attributes are not allowed here, they could + // be set here and then the pointers become invalid! + OSL_ENSURE(m_aParaAttrs.empty(), + "Danger: there are non-final paragraph attributes"); + m_aParaAttrs.clear(); + + HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get()); + HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get()); + + for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes) + { + OSL_ENSURE(!*pHTMLAttributes, "The attribute table is not empty!"); + + *pHTMLAttributes = *pSaveAttributes; + + HTMLAttr *pAttr = *pHTMLAttributes; + while (pAttr) + { + OSL_ENSURE( !pAttr->GetPrev() || !pAttr->GetPrev()->m_ppHead, + "Previous attribute has still a header" ); + pAttr->SetHead(pHTMLAttributes, m_xAttrTab); + pAttr = pAttr->GetNext(); + } + + *pSaveAttributes = nullptr; + } +} + +void SwHTMLParser::InsertAttr( const SfxPoolItem& rItem, bool bInsAtStart ) +{ + HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), rItem, nullptr, std::shared_ptr<HTMLAttrTable>()); + if (bInsAtStart) + m_aSetAttrTab.push_front( pTmp ); + else + m_aSetAttrTab.push_back( pTmp ); +} + +void SwHTMLParser::InsertAttrs( std::deque<std::unique_ptr<HTMLAttr>> rAttrs ) +{ + while( !rAttrs.empty() ) + { + std::unique_ptr<HTMLAttr> pAttr = std::move(rAttrs.front()); + InsertAttr( pAttr->GetItem(), false ); + rAttrs.pop_front(); + } +} + +void SwHTMLParser::NewStdAttr( HtmlTokenId nToken ) +{ + OUString aId, aStyle, aLang, aDir; + OUString aClass; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + { + if( HtmlTokenId::SPAN_ON != nToken || aClass.isEmpty() || + !CreateContainer( aClass, aItemSet, aPropInfo, xCntxt.get() ) ) + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + } + } + + // save the context + PushContext(xCntxt); +} + +void SwHTMLParser::NewStdAttr( HtmlTokenId nToken, + HTMLAttr **ppAttr, const SfxPoolItem & rItem, + HTMLAttr **ppAttr2, const SfxPoolItem *pItem2, + HTMLAttr **ppAttr3, const SfxPoolItem *pItem3 ) +{ + OUString aId, aStyle, aClass, aLang, aDir; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + aItemSet.Put( rItem ); + if( pItem2 ) + aItemSet.Put( *pItem2 ); + if( pItem3 ) + aItemSet.Put( *pItem3 ); + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + } + else + { + InsertAttr( ppAttr ,rItem, xCntxt.get() ); + if( pItem2 ) + { + OSL_ENSURE( ppAttr2, "missing table entry for item2" ); + InsertAttr( ppAttr2, *pItem2, xCntxt.get() ); + } + if( pItem3 ) + { + OSL_ENSURE( ppAttr3, "missing table entry for item3" ); + InsertAttr( ppAttr3, *pItem3, xCntxt.get() ); + } + } + + // save the context + PushContext(xCntxt); +} + +void SwHTMLParser::EndTag( HtmlTokenId nToken ) +{ + // fetch context + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken))); + if (xCntxt) + { + // and maybe end the attributes + EndContext(xCntxt.get()); + } +} + +void SwHTMLParser::NewBasefontAttr() +{ + OUString aId, aStyle, aClass, aLang, aDir; + sal_uInt16 nSize = 3; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::SIZE: + nSize = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + if( nSize < 1 ) + nSize = 1; + + if( nSize > 7 ) + nSize = 7; + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::BASEFONT_ON)); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + //CJK has different defaults + SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE ); + aItemSet.Put( aFontHeight ); + SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE ); + aItemSet.Put( aFontHeightCJK ); + //Complex type can contain so many types of letters, + //that it's not really worthy to bother, IMO. + //Still, I have set a default. + SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE ); + aItemSet.Put( aFontHeightCTL ); + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + } + else + { + SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() ); + SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeightCJK, aFontHeightCJK, xCntxt.get() ); + SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeightCTL, aFontHeightCTL, xCntxt.get() ); + } + + // save the context + PushContext(xCntxt); + + // save the font size + m_aBaseFontStack.push_back( nSize ); +} + +void SwHTMLParser::EndBasefontAttr() +{ + EndTag( HtmlTokenId::BASEFONT_ON ); + + // avoid stack underflow in tables + if( m_aBaseFontStack.size() > m_nBaseFontStMin ) + m_aBaseFontStack.erase( m_aBaseFontStack.begin() + m_aBaseFontStack.size() - 1 ); +} + +void SwHTMLParser::NewFontAttr( HtmlTokenId nToken ) +{ + sal_uInt16 nBaseSize = + ( m_aBaseFontStack.size() > m_nBaseFontStMin + ? (m_aBaseFontStack[m_aBaseFontStack.size()-1] & FONTSIZE_MASK) + : 3 ); + sal_uInt16 nFontSize = + ( m_aFontStack.size() > m_nFontStMin + ? (m_aFontStack[m_aFontStack.size()-1] & FONTSIZE_MASK) + : nBaseSize ); + + OUString aFace, aId, aStyle, aClass, aLang, aDir; + Color aColor; + sal_uLong nFontHeight = 0; // actual font height to set + sal_uInt16 nSize = 0; // font height in Netscape notation (1-7) + bool bColor = false; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::SIZE: + if( HtmlTokenId::FONT_ON==nToken && !rOption.GetString().isEmpty() ) + { + sal_Int32 nSSize; + if( '+' == rOption.GetString()[0] || + '-' == rOption.GetString()[0] ) + nSSize = o3tl::saturating_add<sal_Int32>(nBaseSize, rOption.GetSNumber()); + else + nSSize = static_cast<sal_Int32>(rOption.GetNumber()); + + if( nSSize < 1 ) + nSSize = 1; + else if( nSSize > 7 ) + nSSize = 7; + + nSize = static_cast<sal_uInt16>(nSSize); + nFontHeight = m_aFontHeights[nSize-1]; + } + break; + case HtmlOptionId::COLOR: + if( HtmlTokenId::FONT_ON==nToken ) + { + rOption.GetColor( aColor ); + bColor = true; + } + break; + case HtmlOptionId::FACE: + if( HtmlTokenId::FONT_ON==nToken ) + aFace = rOption.GetString(); + break; + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + if( HtmlTokenId::FONT_ON != nToken ) + { + // HTML_BIGPRINT_ON or HTML_SMALLPRINT_ON + + // In headings the current heading sets the font height + // and not BASEFONT. + const SwFormatColl *pColl = GetCurrFormatColl(); + sal_uInt16 nPoolId = pColl ? pColl->GetPoolFormatId() : 0; + if( nPoolId>=RES_POOLCOLL_HEADLINE1 && + nPoolId<=RES_POOLCOLL_HEADLINE6 ) + { + // If the font height in the heading wasn't changed yet, + // then take the one from the style. + if( m_nFontStHeadStart==m_aFontStack.size() ) + nFontSize = static_cast< sal_uInt16 >(6 - (nPoolId - RES_POOLCOLL_HEADLINE1)); + } + else + nPoolId = 0; + + if( HtmlTokenId::BIGPRINT_ON == nToken ) + nSize = ( nFontSize<7 ? nFontSize+1 : 7 ); + else + nSize = ( nFontSize>1 ? nFontSize-1 : 1 ); + + // If possible in headlines we fetch the new font height + // from the style. + if( nPoolId && nSize>=1 && nSize <=6 ) + nFontHeight = + m_pCSS1Parser->GetTextCollFromPool( + RES_POOLCOLL_HEADLINE1+6-nSize )->GetSize().GetHeight(); + else + nFontHeight = m_aFontHeights[nSize-1]; + } + + OSL_ENSURE( !nSize == !nFontHeight, "HTML-Font-Size != Font-Height" ); + + OUString aFontName, aStyleName; + FontFamily eFamily = FAMILY_DONTKNOW; // family and pitch, + FontPitch ePitch = PITCH_DONTKNOW; // if not found + rtl_TextEncoding eEnc = osl_getThreadTextEncoding(); + + if( !aFace.isEmpty() && !m_pCSS1Parser->IsIgnoreFontFamily() ) + { + const FontList *pFList = nullptr; + SwDocShell *pDocSh = m_xDoc->GetDocShell(); + if( pDocSh ) + { + const SvxFontListItem *pFListItem = + static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST)); + if( pFListItem ) + pFList = pFListItem->GetFontList(); + } + + bool bFound = false; + sal_Int32 nStrPos = 0; + while( nStrPos!= -1 ) + { + OUString aFName = aFace.getToken( 0, ',', nStrPos ); + aFName = comphelper::string::strip(aFName, ' '); + if( !aFName.isEmpty() ) + { + if( !bFound && pFList ) + { + sal_Handle hFont = pFList->GetFirstFontMetric( aFName ); + if( nullptr != hFont ) + { + const FontMetric& rFMetric = FontList::GetFontMetric( hFont ); + if( RTL_TEXTENCODING_DONTKNOW != rFMetric.GetCharSet() ) + { + bFound = true; + if( RTL_TEXTENCODING_SYMBOL == rFMetric.GetCharSet() ) + eEnc = RTL_TEXTENCODING_SYMBOL; + } + } + } + if( !aFontName.isEmpty() ) + aFontName += ";"; + aFontName += aFName; + } + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( nFontHeight ) + { + SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE ); + aItemSet.Put( aFontHeight ); + SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE ); + aItemSet.Put( aFontHeightCJK ); + SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE ); + aItemSet.Put( aFontHeightCTL ); + } + if( bColor ) + aItemSet.Put( SvxColorItem(aColor, RES_CHRATR_COLOR) ); + if( !aFontName.isEmpty() ) + { + SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT ); + aItemSet.Put( aFont ); + SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT ); + aItemSet.Put( aFontCJK ); + SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT ); + aItemSet.Put( aFontCTL ); + } + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + } + else + { + if( nFontHeight ) + { + SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() ); + SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCJK, xCntxt.get() ); + SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE ); + InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCTL, xCntxt.get() ); + } + if( bColor ) + InsertAttr( &m_xAttrTab->pFontColor, SvxColorItem(aColor, RES_CHRATR_COLOR), xCntxt.get() ); + if( !aFontName.isEmpty() ) + { + SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT ); + InsertAttr( &m_xAttrTab->pFont, aFont, xCntxt.get() ); + SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT ); + InsertAttr( &m_xAttrTab->pFont, aFontCJK, xCntxt.get() ); + SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT ); + InsertAttr( &m_xAttrTab->pFont, aFontCTL, xCntxt.get() ); + } + } + + // save the context + PushContext(xCntxt); + + m_aFontStack.push_back( nSize ); +} + +void SwHTMLParser::EndFontAttr( HtmlTokenId nToken ) +{ + EndTag( nToken ); + + // avoid stack underflow in tables + if( m_aFontStack.size() > m_nFontStMin ) + m_aFontStack.erase( m_aFontStack.begin() + m_aFontStack.size() - 1 ); +} + +void SwHTMLParser::NewPara() +{ + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + + m_eParaAdjust = SvxAdjust::End; + OUString aId, aStyle, aClass, aLang, aDir; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + m_eParaAdjust = rOption.GetEnum( aHTMLPAlignTable, m_eParaAdjust ); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt( + !aClass.isEmpty() ? new HTMLAttrContext( HtmlTokenId::PARABREAK_ON, + RES_POOLCOLL_TEXT, aClass ) + : new HTMLAttrContext( HtmlTokenId::PARABREAK_ON )); + + // parse styles (Don't consider class. This is only possible as long as none of + // the CSS1 properties of the class must be formatted hard!!!) + if (HasStyleOptions(aStyle, aId, OUString(), &aLang, &aDir)) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir)) + { + OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ), + "Class is not considered" ); + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get() ); + } + } + + if( SvxAdjust::End != m_eParaAdjust ) + InsertAttr( &m_xAttrTab->pAdjust, SvxAdjustItem(m_eParaAdjust, RES_PARATR_ADJUST), xCntxt.get() ); + + // and push on stack + PushContext( xCntxt ); + + // set the current style or its attributes + SetTextCollAttrs( !aClass.isEmpty() ? m_aContexts.back().get() : nullptr ); + + // progress bar + ShowStatline(); + + OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE, "Now an open paragraph element will be lost." ); + m_nOpenParaToken = HtmlTokenId::PARABREAK_ON; +} + +void SwHTMLParser::EndPara( bool bReal ) +{ + if (HtmlTokenId::LI_ON==m_nOpenParaToken && m_xTable) + { +#if OSL_DEBUG_LEVEL > 0 + const SwNumRule *pNumRule = m_pPam->GetNode().GetTextNode()->GetNumRule(); + OSL_ENSURE( pNumRule, "Where is the NumRule" ); +#endif + } + + // Netscape skips empty paragraphs, we do the same. + if( bReal ) + { + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + } + + // If a DD or DT was open, it's an implied definition list, + // which must be closed now. + if( (m_nOpenParaToken == HtmlTokenId::DT_ON || m_nOpenParaToken == HtmlTokenId::DD_ON) && + m_nDefListDeep) + { + m_nDefListDeep--; + } + + // Pop the context of the stack. It can also be from an + // implied opened definition list. + std::unique_ptr<HTMLAttrContext> xCntxt( + PopContext( m_nOpenParaToken != HtmlTokenId::NONE ? getOnToken(m_nOpenParaToken) : HtmlTokenId::PARABREAK_ON )); + + // close attribute + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // because of JavaScript set paragraph attributes as fast as possible + xCntxt.reset(); + } + + // reset the existing style + if( bReal ) + SetTextCollAttrs(); + + m_nOpenParaToken = HtmlTokenId::NONE; +} + +void SwHTMLParser::NewHeading( HtmlTokenId nToken ) +{ + m_eParaAdjust = SvxAdjust::End; + + OUString aId, aStyle, aClass, aLang, aDir; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::ALIGN: + m_eParaAdjust = rOption.GetEnum( aHTMLPAlignTable, m_eParaAdjust ); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // open a new paragraph + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + + // search for the matching style + sal_uInt16 nTextColl; + switch( nToken ) + { + case HtmlTokenId::HEAD1_ON: nTextColl = RES_POOLCOLL_HEADLINE1; break; + case HtmlTokenId::HEAD2_ON: nTextColl = RES_POOLCOLL_HEADLINE2; break; + case HtmlTokenId::HEAD3_ON: nTextColl = RES_POOLCOLL_HEADLINE3; break; + case HtmlTokenId::HEAD4_ON: nTextColl = RES_POOLCOLL_HEADLINE4; break; + case HtmlTokenId::HEAD5_ON: nTextColl = RES_POOLCOLL_HEADLINE5; break; + case HtmlTokenId::HEAD6_ON: nTextColl = RES_POOLCOLL_HEADLINE6; break; + default: nTextColl = RES_POOLCOLL_STANDARD; break; + } + + // create the context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nTextColl, aClass)); + + // parse styles (regarding class see also NewPara) + if (HasStyleOptions(aStyle, aId, OUString(), &aLang, &aDir)) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir)) + { + OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ), + "Class is not considered" ); + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get() ); + } + } + + if( SvxAdjust::End != m_eParaAdjust ) + InsertAttr( &m_xAttrTab->pAdjust, SvxAdjustItem(m_eParaAdjust, RES_PARATR_ADJUST), xCntxt.get() ); + + // and push on stack + PushContext(xCntxt); + + // set the current style or its attributes + SetTextCollAttrs(m_aContexts.back().get()); + + m_nFontStHeadStart = m_aFontStack.size(); + + // progress bar + ShowStatline(); +} + +void SwHTMLParser::EndHeading() +{ + // open a new paragraph + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SPACE ); + else + AddParSpace(); + + // search context matching the token and fetch it from stack + std::unique_ptr<HTMLAttrContext> xCntxt; + auto nPos = m_aContexts.size(); + while( !xCntxt && nPos>m_nContextStMin ) + { + switch( m_aContexts[--nPos]->GetToken() ) + { + case HtmlTokenId::HEAD1_ON: + case HtmlTokenId::HEAD2_ON: + case HtmlTokenId::HEAD3_ON: + case HtmlTokenId::HEAD4_ON: + case HtmlTokenId::HEAD5_ON: + case HtmlTokenId::HEAD6_ON: + xCntxt = std::move(m_aContexts[nPos]); + m_aContexts.erase( m_aContexts.begin() + nPos ); + break; + default: break; + } + } + + // and now end attributes + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // because of JavaScript set paragraph attributes as fast as possible + xCntxt.reset(); + } + + // reset existing style + SetTextCollAttrs(); + + m_nFontStHeadStart = m_nFontStMin; +} + +void SwHTMLParser::NewTextFormatColl( HtmlTokenId nToken, sal_uInt16 nColl ) +{ + OUString aId, aStyle, aClass, aLang, aDir; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // open a new paragraph + SwHTMLAppendMode eMode = AM_NORMAL; + switch( nToken ) + { + case HtmlTokenId::LISTING_ON: + case HtmlTokenId::XMP_ON: + // These both tags will be mapped to the PRE style. For the case that a + // a CLASS exists we will delete it so that we don't get the CLASS of + // the PRE style. + aClass.clear(); + [[fallthrough]]; + case HtmlTokenId::BLOCKQUOTE_ON: + case HtmlTokenId::BLOCKQUOTE30_ON: + case HtmlTokenId::PREFORMTXT_ON: + eMode = AM_SPACE; + break; + case HtmlTokenId::ADDRESS_ON: + eMode = AM_NOSPACE; // ADDRESS can follow on a <P> without </P> + break; + case HtmlTokenId::DT_ON: + case HtmlTokenId::DD_ON: + eMode = AM_SOFTNOSPACE; + break; + default: + OSL_ENSURE( false, "unknown style" ); + break; + } + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( eMode ); + else if( AM_SPACE==eMode ) + AddParSpace(); + + // ... and save in a context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, aClass)); + + // parse styles (regarding class see also NewPara) + if (HasStyleOptions(aStyle, aId, OUString(), &aLang, &aDir)) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir)) + { + OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ), + "Class is not considered" ); + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get() ); + } + } + + PushContext(xCntxt); + + // set the new style + SetTextCollAttrs(m_aContexts.back().get()); + + // update progress bar + ShowStatline(); +} + +void SwHTMLParser::EndTextFormatColl( HtmlTokenId nToken ) +{ + SwHTMLAppendMode eMode = AM_NORMAL; + switch( getOnToken(nToken) ) + { + case HtmlTokenId::BLOCKQUOTE_ON: + case HtmlTokenId::BLOCKQUOTE30_ON: + case HtmlTokenId::PREFORMTXT_ON: + case HtmlTokenId::LISTING_ON: + case HtmlTokenId::XMP_ON: + eMode = AM_SPACE; + break; + case HtmlTokenId::ADDRESS_ON: + case HtmlTokenId::DT_ON: + case HtmlTokenId::DD_ON: + eMode = AM_SOFTNOSPACE; + break; + default: + OSL_ENSURE( false, "unknown style" ); + break; + } + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( eMode ); + else if( AM_SPACE==eMode ) + AddParSpace(); + + // pop current context of stack + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken))); + + // and now end attributes + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // because of JavaScript set paragraph attributes as fast as possible + xCntxt.reset(); + } + + // reset existing style + SetTextCollAttrs(); +} + +void SwHTMLParser::NewDefList() +{ + OUString aId, aStyle, aClass, aLang, aDir; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // open a new paragraph + bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 0; + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( bSpace ? AM_SPACE : AM_SOFTNOSPACE ); + else if( bSpace ) + AddParSpace(); + + // one level more + m_nDefListDeep++; + + bool bInDD = false, bNotInDD = false; + auto nPos = m_aContexts.size(); + while( !bInDD && !bNotInDD && nPos>m_nContextStMin ) + { + HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken(); + switch( nCntxtToken ) + { + case HtmlTokenId::DEFLIST_ON: + case HtmlTokenId::DIRLIST_ON: + case HtmlTokenId::MENULIST_ON: + case HtmlTokenId::ORDERLIST_ON: + case HtmlTokenId::UNORDERLIST_ON: + bNotInDD = true; + break; + case HtmlTokenId::DD_ON: + bInDD = true; + break; + default: break; + } + } + + // ... and save in a context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::DEFLIST_ON)); + + // in it save also the margins + sal_uInt16 nLeft=0, nRight=0; + short nIndent=0; + GetMarginsFromContext( nLeft, nRight, nIndent ); + + // The indentation, which already results from a DL, correlates with a DT + // on the current level and this correlates to a DD from the previous level. + // For a level >=2 we must add DD distance. + if( !bInDD && m_nDefListDeep > 1 ) + { + + // and the one of the DT-style of the current level + SvxLRSpaceItem rLRSpace = + m_pCSS1Parser->GetTextFormatColl(RES_POOLCOLL_HTML_DD, OUString()) + ->GetLRSpace(); + nLeft = nLeft + static_cast< sal_uInt16 >(rLRSpace.GetTextLeft()); + } + + xCntxt->SetMargins( nLeft, nRight, nIndent ); + + // parse styles + if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) ) + { + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get() ); + } + } + + PushContext(xCntxt); + + // set the attributes of the new style + if( m_nDefListDeep > 1 ) + SetTextCollAttrs(m_aContexts.back().get()); +} + +void SwHTMLParser::EndDefList() +{ + bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 1; + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( bSpace ? AM_SPACE : AM_SOFTNOSPACE ); + else if( bSpace ) + AddParSpace(); + + // one level less + if( m_nDefListDeep > 0 ) + m_nDefListDeep--; + + // pop current context of stack + std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::DEFLIST_ON)); + + // and now end attributes + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // because of JavaScript set paragraph attributes as fast as possible + xCntxt.reset(); + } + + // and set style + SetTextCollAttrs(); +} + +void SwHTMLParser::NewDefListItem( HtmlTokenId nToken ) +{ + // determine if the DD/DT exist in a DL + bool bInDefList = false, bNotInDefList = false; + auto nPos = m_aContexts.size(); + while( !bInDefList && !bNotInDefList && nPos>m_nContextStMin ) + { + HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken(); + switch( nCntxtToken ) + { + case HtmlTokenId::DEFLIST_ON: + bInDefList = true; + break; + case HtmlTokenId::DIRLIST_ON: + case HtmlTokenId::MENULIST_ON: + case HtmlTokenId::ORDERLIST_ON: + case HtmlTokenId::UNORDERLIST_ON: + bNotInDefList = true; + break; + default: break; + } + } + + // if not, then implicitly open a new DL + if( !bInDefList ) + { + m_nDefListDeep++; + OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE, + "Now an open paragraph element will be lost." ); + m_nOpenParaToken = nToken; + } + + NewTextFormatColl( nToken, static_cast< sal_uInt16 >(nToken==HtmlTokenId::DD_ON ? RES_POOLCOLL_HTML_DD + : RES_POOLCOLL_HTML_DT) ); +} + +void SwHTMLParser::EndDefListItem( HtmlTokenId nToken ) +{ + // open a new paragraph + if( nToken == HtmlTokenId::NONE && m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_SOFTNOSPACE ); + + // search context matching the token and fetch it from stack + nToken = getOnToken(nToken); + std::unique_ptr<HTMLAttrContext> xCntxt; + auto nPos = m_aContexts.size(); + while( !xCntxt && nPos>m_nContextStMin ) + { + HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken(); + switch( nCntxtToken ) + { + case HtmlTokenId::DD_ON: + case HtmlTokenId::DT_ON: + if( nToken == HtmlTokenId::NONE || nToken == nCntxtToken ) + { + xCntxt = std::move(m_aContexts[nPos]); + m_aContexts.erase( m_aContexts.begin() + nPos ); + } + break; + case HtmlTokenId::DEFLIST_ON: + // don't look at DD/DT outside the current DefList + case HtmlTokenId::DIRLIST_ON: + case HtmlTokenId::MENULIST_ON: + case HtmlTokenId::ORDERLIST_ON: + case HtmlTokenId::UNORDERLIST_ON: + // and also not outside another list + nPos = m_nContextStMin; + break; + default: break; + } + } + + // and now end attributes + if (xCntxt) + { + EndContext(xCntxt.get()); + SetAttr(); // because of JavaScript set paragraph attributes as fast as possible + } +} + +/** + * + * @param bNoSurroundOnly The paragraph contains at least one frame + * without wrapping. + * @param bSurroundOnly The paragraph contains at least one frame + * with wrapping, but none without wrapping. + * + * Otherwise the paragraph contains any frame. + */ +bool SwHTMLParser::HasCurrentParaFlys( bool bNoSurroundOnly, + bool bSurroundOnly ) const +{ + SwNodeIndex& rNodeIdx = m_pPam->GetPoint()->nNode; + + const SwFrameFormats& rFrameFormatTable = *m_xDoc->GetSpzFrameFormats(); + + bool bFound = false; + for ( size_t i=0; i<rFrameFormatTable.size(); i++ ) + { + const SwFrameFormat *const pFormat = rFrameFormatTable[i]; + SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); + // A frame was found, when + // - it is paragraph-bound, and + // - is anchored in current paragraph, and + // - every paragraph-bound frame counts, or + // - (only frames without wrapping count and) the frame doesn't have + // a wrapping + SwPosition const*const pAPos = pAnchor->GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) && + pAPos->nNode == rNodeIdx ) + { + if( !(bNoSurroundOnly || bSurroundOnly) ) + { + bFound = true; + break; + } + else + { + // When looking for frames with wrapping, also disregard + // ones with wrap-through. In this case it's (still) HIDDEN-Controls, + // and you don't want to evade those when positioning. + css::text::WrapTextMode eSurround = pFormat->GetSurround().GetSurround(); + if( bNoSurroundOnly ) + { + if( css::text::WrapTextMode_NONE==eSurround ) + { + bFound = true; + break; + } + } + if( bSurroundOnly ) + { + if( css::text::WrapTextMode_NONE==eSurround ) + { + bFound = false; + break; + } + else if( css::text::WrapTextMode_THROUGH!=eSurround ) + { + bFound = true; + // Continue searching: It's possible that some without + // wrapping will follow... + } + } + } + } + } + + return bFound; +} + +// the special methods for inserting of objects + +const SwFormatColl *SwHTMLParser::GetCurrFormatColl() const +{ + const SwContentNode* pCNd = m_pPam->GetContentNode(); + return pCNd ? &pCNd->GetAnyFormatColl() : nullptr; +} + +void SwHTMLParser::SetTextCollAttrs( HTMLAttrContext *pContext ) +{ + SwTextFormatColl *pCollToSet = nullptr; // the style to set + SfxItemSet *pItemSet = nullptr; // set of hard attributes + sal_uInt16 nTopColl = pContext ? pContext->GetTextFormatColl() : 0; + const OUString rTopClass = pContext ? pContext->GetClass() : OUString(); + sal_uInt16 nDfltColl = RES_POOLCOLL_TEXT; + + bool bInPRE=false; // some context info + + sal_uInt16 nLeftMargin = 0, nRightMargin = 0; // the margins and + short nFirstLineIndent = 0; // indentations + + for( auto i = m_nContextStAttrMin; i < m_aContexts.size(); ++i ) + { + const HTMLAttrContext *pCntxt = m_aContexts[i].get(); + + sal_uInt16 nColl = pCntxt->GetTextFormatColl(); + if( nColl ) + { + // There is a style to set. Then at first we must decide, + // if the style can be set. + bool bSetThis = true; + switch( nColl ) + { + case RES_POOLCOLL_HTML_PRE: + bInPRE = true; + break; + case RES_POOLCOLL_TEXT: + // <TD><P CLASS=xxx> must become TD.xxx + if( nDfltColl==RES_POOLCOLL_TABLE || + nDfltColl==RES_POOLCOLL_TABLE_HDLN ) + nColl = nDfltColl; + break; + case RES_POOLCOLL_HTML_HR: + // also <HR> in <PRE> set as style, otherwise it can't + // be exported anymore + break; + default: + if( bInPRE ) + bSetThis = false; + break; + } + + SwTextFormatColl *pNewColl = + m_pCSS1Parser->GetTextFormatColl( nColl, pCntxt->GetClass() ); + + if( bSetThis ) + { + // If now a different style should be set as previously, the + // previous style must be replaced by hard attribution. + + if( pCollToSet ) + { + // insert the attributes hard, which previous style sets + if( !pItemSet ) + pItemSet = new SfxItemSet( pCollToSet->GetAttrSet() ); + else + { + const SfxItemSet& rCollSet = pCollToSet->GetAttrSet(); + SfxItemSet aItemSet( *rCollSet.GetPool(), + rCollSet.GetRanges() ); + aItemSet.Set( rCollSet ); + pItemSet->Put( aItemSet ); + } + // but remove the attributes, which the current style sets, + // because otherwise they will be overwritten later + pItemSet->Differentiate( pNewColl->GetAttrSet() ); + } + + pCollToSet = pNewColl; + } + else + { + // hard attribution + if( !pItemSet ) + pItemSet = new SfxItemSet( pNewColl->GetAttrSet() ); + else + { + const SfxItemSet& rCollSet = pNewColl->GetAttrSet(); + SfxItemSet aItemSet( *rCollSet.GetPool(), + rCollSet.GetRanges() ); + aItemSet.Set( rCollSet ); + pItemSet->Put( aItemSet ); + } + } + } + else + { + // Maybe a default style exists? + nColl = pCntxt->GetDfltTextFormatColl(); + if( nColl ) + nDfltColl = nColl; + } + + // if applicable fetch new paragraph indents + if( pCntxt->IsLRSpaceChanged() ) + { + sal_uInt16 nLeft=0, nRight=0; + + pCntxt->GetMargins( nLeft, nRight, nFirstLineIndent ); + nLeftMargin = nLeft; + nRightMargin = nRight; + } + } + + // If in current context a new style should be set, + // its paragraph margins must be inserted in the context. + if( pContext && nTopColl ) + { + // <TD><P CLASS=xxx> must become TD.xxx + if( nTopColl==RES_POOLCOLL_TEXT && + (nDfltColl==RES_POOLCOLL_TABLE || + nDfltColl==RES_POOLCOLL_TABLE_HDLN) ) + nTopColl = nDfltColl; + + const SwTextFormatColl *pTopColl = + m_pCSS1Parser->GetTextFormatColl( nTopColl, rTopClass ); + const SfxItemSet& rItemSet = pTopColl->GetAttrSet(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState(RES_LR_SPACE,true, &pItem) ) + { + const SvxLRSpaceItem *pLRItem = + static_cast<const SvxLRSpaceItem *>(pItem); + + sal_Int32 nLeft = pLRItem->GetTextLeft(); + sal_Int32 nRight = pLRItem->GetRight(); + nFirstLineIndent = pLRItem->GetTextFirstLineOffset(); + + // In Definition lists the margins also contain the margins from the previous levels + if( RES_POOLCOLL_HTML_DD == nTopColl ) + { + const SvxLRSpaceItem& rDTLRSpace = m_pCSS1Parser + ->GetTextFormatColl(RES_POOLCOLL_HTML_DT, OUString()) + ->GetLRSpace(); + nLeft -= rDTLRSpace.GetTextLeft(); + nRight -= rDTLRSpace.GetRight(); + } + else if( RES_POOLCOLL_HTML_DT == nTopColl ) + { + nLeft = 0; + nRight = 0; + } + + // the paragraph margins add up + nLeftMargin = nLeftMargin + static_cast< sal_uInt16 >(nLeft); + nRightMargin = nRightMargin + static_cast< sal_uInt16 >(nRight); + + pContext->SetMargins( nLeftMargin, nRightMargin, + nFirstLineIndent ); + } + if( SfxItemState::SET == rItemSet.GetItemState(RES_UL_SPACE,true, &pItem) ) + { + const SvxULSpaceItem *pULItem = + static_cast<const SvxULSpaceItem *>(pItem); + pContext->SetULSpace( pULItem->GetUpper(), pULItem->GetLower() ); + } + } + + // If no style is set in the context use the text body. + if( !pCollToSet ) + { + pCollToSet = m_pCSS1Parser->GetTextCollFromPool( nDfltColl ); + const SvxLRSpaceItem& rLRItem = pCollToSet->GetLRSpace(); + if( !nLeftMargin ) + nLeftMargin = static_cast< sal_uInt16 >(rLRItem.GetTextLeft()); + if( !nRightMargin ) + nRightMargin = static_cast< sal_uInt16 >(rLRItem.GetRight()); + if( !nFirstLineIndent ) + nFirstLineIndent = rLRItem.GetTextFirstLineOffset(); + } + + // remove previous hard attribution of paragraph + for( auto pParaAttr : m_aParaAttrs ) + pParaAttr->Invalidate(); + m_aParaAttrs.clear(); + + // set the style + m_xDoc->SetTextFormatColl( *m_pPam, pCollToSet ); + + // if applicable correct the paragraph indent + const SvxLRSpaceItem& rLRItem = pCollToSet->GetLRSpace(); + bool bSetLRSpace = nLeftMargin != rLRItem.GetTextLeft() || + nFirstLineIndent != rLRItem.GetTextFirstLineOffset() || + nRightMargin != rLRItem.GetRight(); + + if( bSetLRSpace ) + { + SvxLRSpaceItem aLRItem( rLRItem ); + aLRItem.SetTextLeft( nLeftMargin ); + aLRItem.SetRight( nRightMargin ); + aLRItem.SetTextFirstLineOffset( nFirstLineIndent ); + if( pItemSet ) + pItemSet->Put( aLRItem ); + else + { + NewAttr(m_xAttrTab, &m_xAttrTab->pLRSpace, aLRItem); + m_xAttrTab->pLRSpace->SetLikePara(); + m_aParaAttrs.push_back( m_xAttrTab->pLRSpace ); + EndAttr( m_xAttrTab->pLRSpace, false ); + } + } + + // and now set the attributes + if( pItemSet ) + { + InsertParaAttrs( *pItemSet ); + delete pItemSet; + } +} + +void SwHTMLParser::NewCharFormat( HtmlTokenId nToken ) +{ + OUString aId, aStyle, aLang, aDir; + OUString aClass; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + case HtmlOptionId::LANG: + aLang = rOption.GetString(); + break; + case HtmlOptionId::DIR: + aDir = rOption.GetString(); + break; + default: break; + } + } + + // create a new context + std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken)); + + // set the style and save it in the context + SwCharFormat* pCFormat = m_pCSS1Parser->GetChrFormat( nToken, aClass ); + OSL_ENSURE( pCFormat, "No character format found for token" ); + + // parse styles (regarding class see also NewPara) + if (HasStyleOptions(aStyle, aId, OUString(), &aLang, &aDir)) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir)) + { + OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ), + "Class is not considered" ); + DoPositioning( aItemSet, aPropInfo, xCntxt.get() ); + InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true ); + } + } + + // Character formats are stored in their own stack and can never be inserted + // by styles. Therefore the attribute doesn't exist in CSS1-Which-Range. + if( pCFormat ) + InsertAttr( &m_xAttrTab->pCharFormats, SwFormatCharFormat( pCFormat ), xCntxt.get() ); + + // save the context + PushContext(xCntxt); +} + +void SwHTMLParser::InsertSpacer() +{ + // and if applicable change it via the options + sal_Int16 eVertOri = text::VertOrientation::TOP; + sal_Int16 eHoriOri = text::HoriOrientation::NONE; + Size aSize( 0, 0); + long nSize = 0; + bool bPercentWidth = false; + bool bPercentHeight = false; + sal_uInt16 nType = HTML_SPTYPE_HORI; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::TYPE: + rOption.GetEnum( nType, aHTMLSpacerTypeTable ); + break; + case HtmlOptionId::ALIGN: + eVertOri = + rOption.GetEnum( aHTMLImgVAlignTable, + eVertOri ); + eHoriOri = + rOption.GetEnum( aHTMLImgHAlignTable, + eHoriOri ); + break; + case HtmlOptionId::WIDTH: + // First only save as pixel value! + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + aSize.setWidth( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::HEIGHT: + // First only save as pixel value! + bPercentHeight = (rOption.GetString().indexOf('%') != -1); + aSize.setHeight( static_cast<long>(rOption.GetNumber()) ); + break; + case HtmlOptionId::SIZE: + // First only save as pixel value! + nSize = rOption.GetNumber(); + break; + default: break; + } + } + + switch( nType ) + { + case HTML_SPTYPE_BLOCK: + { + // create an empty text frame + + // fetch the ItemSet + SfxItemSet aFrameSet( m_xDoc->GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if( !IsNewDoc() ) + Reader::ResetFrameFormatAttrs( aFrameSet ); + + // set the anchor and the adjustment + SetAnchorAndAdjustment( eVertOri, eHoriOri, aFrameSet ); + + // and the size of the frame + Size aDfltSz( MINFLY, MINFLY ); + Size aSpace( 0, 0 ); + SfxItemSet aDummyItemSet( m_xDoc->GetAttrPool(), + m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aDummyPropInfo; + + SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight, + aDummyPropInfo, aFrameSet ); + SetSpace( aSpace, aDummyItemSet, aDummyPropInfo, aFrameSet ); + + // protect the content + SvxProtectItem aProtectItem( RES_PROTECT) ; + aProtectItem.SetContentProtect( true ); + aFrameSet.Put( aProtectItem ); + + // create the frame + RndStdIds eAnchorId = + aFrameSet.Get(RES_ANCHOR).GetAnchorId(); + SwFrameFormat *pFlyFormat = m_xDoc->MakeFlySection( eAnchorId, + m_pPam->GetPoint(), &aFrameSet ); + // Possibly create frames and register auto-bound frames. + RegisterFlyFrame( pFlyFormat ); + } + break; + case HTML_SPTYPE_VERT: + if( nSize > 0 ) + { + if (Application::GetDefaultDevice()) + { + nSize = Application::GetDefaultDevice() + ->PixelToLogic( Size(0,nSize), + MapMode(MapUnit::MapTwip) ).Height(); + } + + // set a paragraph margin + SwTextNode *pTextNode = nullptr; + if( !m_pPam->GetPoint()->nContent.GetIndex() ) + { + // if possible change the bottom paragraph margin + // of previous node + + SetAttr(); // set still open paragraph attributes + + pTextNode = m_xDoc->GetNodes()[m_pPam->GetPoint()->nNode.GetIndex()-1] + ->GetTextNode(); + + // If the previous paragraph isn't a text node, then now an + // empty paragraph is created, which already generates a single + // line of spacing. + if( !pTextNode ) + nSize = nSize>HTML_PARSPACE ? nSize-HTML_PARSPACE : 0; + } + + if( pTextNode ) + { + SvxULSpaceItem aULSpace( static_cast<const SvxULSpaceItem&>(pTextNode + ->SwContentNode::GetAttr( RES_UL_SPACE )) ); + aULSpace.SetLower( aULSpace.GetLower() + static_cast<sal_uInt16>(nSize) ); + pTextNode->SetAttr( aULSpace ); + } + else + { + NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, SvxULSpaceItem(0, static_cast<sal_uInt16>(nSize), RES_UL_SPACE)); + EndAttr( m_xAttrTab->pULSpace, false ); + + AppendTextNode(); // Don't change spacing! + } + } + break; + case HTML_SPTYPE_HORI: + if( nSize > 0 ) + { + // If the paragraph is still empty, set first line + // indentation, otherwise apply letter spacing over a space. + + if (Application::GetDefaultDevice()) + { + nSize = Application::GetDefaultDevice() + ->PixelToLogic( Size(nSize,0), + MapMode(MapUnit::MapTwip) ).Width(); + } + + if( !m_pPam->GetPoint()->nContent.GetIndex() ) + { + sal_uInt16 nLeft=0, nRight=0; + short nIndent = 0; + + GetMarginsFromContextWithNumberBullet( nLeft, nRight, nIndent ); + nIndent = nIndent + static_cast<short>(nSize); + + SvxLRSpaceItem aLRItem( RES_LR_SPACE ); + aLRItem.SetTextLeft( nLeft ); + aLRItem.SetRight( nRight ); + aLRItem.SetTextFirstLineOffset( nIndent ); + + NewAttr(m_xAttrTab, &m_xAttrTab->pLRSpace, aLRItem); + EndAttr( m_xAttrTab->pLRSpace, false ); + } + else + { + NewAttr(m_xAttrTab, &m_xAttrTab->pKerning, SvxKerningItem( static_cast<short>(nSize), RES_CHRATR_KERNING )); + OUString aTmp( ' ' ); + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aTmp ); + EndAttr( m_xAttrTab->pKerning ); + } + } + } +} + +sal_uInt16 SwHTMLParser::ToTwips( sal_uInt16 nPixel ) +{ + if( nPixel && Application::GetDefaultDevice() ) + { + long nTwips = Application::GetDefaultDevice()->PixelToLogic( + Size( nPixel, nPixel ), MapMode( MapUnit::MapTwip ) ).Width(); + return static_cast<sal_uInt16>(std::min(nTwips, SwTwips(SAL_MAX_UINT16))); + } + else + return nPixel; +} + +SwTwips SwHTMLParser::GetCurrentBrowseWidth() +{ + const SwTwips nWidth = SwHTMLTableLayout::GetBrowseWidth( *m_xDoc ); + if( nWidth ) + return nWidth; + + if( !m_aHTMLPageSize.Width() ) + { + const SwFrameFormat& rPgFormat = m_pCSS1Parser->GetMasterPageDesc()->GetMaster(); + + const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize(); + const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace(); + const SvxULSpaceItem& rUL = rPgFormat.GetULSpace(); + const SwFormatCol& rCol = rPgFormat.GetCol(); + + m_aHTMLPageSize.setWidth( rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight() ); + m_aHTMLPageSize.setHeight( rSz.GetHeight() - rUL.GetUpper() - rUL.GetLower() ); + + if( 1 < rCol.GetNumCols() ) + m_aHTMLPageSize.setWidth( m_aHTMLPageSize.Width() / ( rCol.GetNumCols()) ); + } + + return m_aHTMLPageSize.Width(); +} + +void SwHTMLParser::InsertIDOption() +{ + OUString aId; + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + if( HtmlOptionId::ID==rOption.GetToken() ) + { + aId = rOption.GetString(); + break; + } + } + + if( !aId.isEmpty() ) + InsertBookmark( aId ); +} + +void SwHTMLParser::InsertLineBreak() +{ + // <BR CLEAR=xxx> is handled as: + // 1.) Only regard the paragraph-bound frames anchored in current paragraph. + // 2.) For left-justified aligned frames, CLEAR=LEFT or ALL, and for right- + // justified aligned frames, CLEAR=RIGHT or ALL, the wrap-through is + // changed as following: + // 3.) If the paragraph contains no text, then the frames don't get a wrapping + // 4.) otherwise a left aligned frame gets a right "only anchor" wrapping + // and a right aligned frame gets a left "only anchor" wrapping. + // 5.) if in a non-empty paragraph the wrapping of a frame is changed, + // then a new paragraph is opened + // 6.) If no wrappings of frames are changed, a hard line break is inserted. + + OUString aId, aStyle, aClass; // the id of bookmark + bool bClearLeft = false, bClearRight = false; + bool bCleared = false; // Was a CLEAR executed? + + // then we fetch the options + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::CLEAR: + { + const OUString &rClear = rOption.GetString(); + if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_all ) ) + { + bClearLeft = true; + bClearRight = true; + } + else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) ) + bClearLeft = true; + else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) ) + bClearRight = true; + } + break; + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::STYLE: + aStyle = rOption.GetString(); + break; + case HtmlOptionId::CLASS: + aClass = rOption.GetString(); + break; + default: break; + } + } + + // CLEAR is only supported for the current paragraph + if( bClearLeft || bClearRight ) + { + SwNodeIndex& rNodeIdx = m_pPam->GetPoint()->nNode; + SwTextNode* pTextNd = rNodeIdx.GetNode().GetTextNode(); + if( pTextNd ) + { + const SwFrameFormats& rFrameFormatTable = *m_xDoc->GetSpzFrameFormats(); + + for( size_t i=0; i<rFrameFormatTable.size(); i++ ) + { + SwFrameFormat *const pFormat = rFrameFormatTable[i]; + 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())) && + pAPos->nNode == rNodeIdx && + pFormat->GetSurround().GetSurround() != css::text::WrapTextMode_NONE ) + { + sal_Int16 eHori = RES_DRAWFRMFMT == pFormat->Which() + ? text::HoriOrientation::LEFT + : pFormat->GetHoriOrient().GetHoriOrient(); + + css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL; + if( m_pPam->GetPoint()->nContent.GetIndex() ) + { + if( bClearLeft && text::HoriOrientation::LEFT==eHori ) + eSurround = css::text::WrapTextMode_RIGHT; + else if( bClearRight && text::HoriOrientation::RIGHT==eHori ) + eSurround = css::text::WrapTextMode_LEFT; + } + else if( (bClearLeft && text::HoriOrientation::LEFT==eHori) || + (bClearRight && text::HoriOrientation::RIGHT==eHori) ) + { + eSurround = css::text::WrapTextMode_NONE; + } + + if( css::text::WrapTextMode_PARALLEL != eSurround ) + { + SwFormatSurround aSurround( eSurround ); + if( css::text::WrapTextMode_NONE != eSurround ) + aSurround.SetAnchorOnly( true ); + pFormat->SetFormatAttr( aSurround ); + bCleared = true; + } + } + } + } + } + + // parse styles + std::shared_ptr<SvxFormatBreakItem> aBreakItem(std::make_shared<SvxFormatBreakItem>(SvxBreak::NONE, RES_BREAK)); + bool bBreakItem = false; + if( HasStyleOptions( aStyle, aId, aClass ) ) + { + SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() ); + SvxCSS1PropertyInfo aPropInfo; + + if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ) ) + { + if( m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo ) ) + { + aBreakItem.reset(aItemSet.Get(RES_BREAK).Clone()); + bBreakItem = true; + } + if( !aPropInfo.m_aId.isEmpty() ) + InsertBookmark( aPropInfo.m_aId ); + } + } + + if( bBreakItem && SvxBreak::PageAfter == aBreakItem->GetBreak() ) + { + NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem); + EndAttr( m_xAttrTab->pBreak, false ); + } + + if( !bCleared && !bBreakItem ) + { + // If no CLEAR could or should be executed, a line break will be inserted + OUString sTmp( u'\x000a' ); // make the Mac happy :-) + m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, sTmp ); + } + else if( m_pPam->GetPoint()->nContent.GetIndex() ) + { + // If a CLEAR is executed in a non-empty paragraph, then after it + // a new paragraph has to be opened. + // MIB 21.02.97: Here actually we should change the bottom paragraph + // margin to zero. This will fail for something like this <BR ..><P> + // (>Netscape). That's why we don't do it. + AppendTextNode( AM_NOSPACE ); + } + if( bBreakItem && SvxBreak::PageBefore == aBreakItem->GetBreak() ) + { + NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem); + EndAttr( m_xAttrTab->pBreak, false ); + } +} + +void SwHTMLParser::InsertHorzRule() +{ + sal_uInt16 nSize = 0; + sal_uInt16 nWidth = 0; + + SvxAdjust eAdjust = SvxAdjust::End; + + bool bPercentWidth = false; + bool bNoShade = false; + bool bColor = false; + + Color aColor; + OUString aId; + + // let's fetch the options + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::ID: + aId = rOption.GetString(); + break; + case HtmlOptionId::SIZE: + nSize = static_cast<sal_uInt16>(rOption.GetNumber()); + break; + case HtmlOptionId::WIDTH: + bPercentWidth = (rOption.GetString().indexOf('%') != -1); + nWidth = static_cast<sal_uInt16>(rOption.GetNumber()); + if( bPercentWidth && nWidth>=100 ) + { + // the default case are 100% lines (no attributes necessary) + nWidth = 0; + bPercentWidth = false; + } + break; + case HtmlOptionId::ALIGN: + eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust ); + break; + case HtmlOptionId::NOSHADE: + bNoShade = true; + break; + case HtmlOptionId::COLOR: + rOption.GetColor( aColor ); + bColor = true; + break; + default: break; + } + } + + if( m_pPam->GetPoint()->nContent.GetIndex() ) + AppendTextNode( AM_NOSPACE ); + if( m_nOpenParaToken != HtmlTokenId::NONE ) + EndPara(); + AppendTextNode(); + m_pPam->Move( fnMoveBackward ); + + // ...and save in a context + std::unique_ptr<HTMLAttrContext> xCntxt( + new HTMLAttrContext(HtmlTokenId::HORZRULE, RES_POOLCOLL_HTML_HR, OUString())); + + PushContext(xCntxt); + + // set the new style + SetTextCollAttrs(m_aContexts.back().get()); + + // the hard attributes of the current paragraph will never become invalid + m_aParaAttrs.clear(); + + if( nSize>0 || bColor || bNoShade ) + { + // set line colour and/or width + if( !bColor ) + aColor = COL_GRAY; + + SvxBorderLine aBorderLine( &aColor ); + if( nSize ) + { + long nPWidth = 0; + long nPHeight = static_cast<long>(nSize); + SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight ); + if ( !bNoShade ) + { + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + } + aBorderLine.SetWidth( nPHeight ); + } + else if( bNoShade ) + { + aBorderLine.SetWidth( DEF_LINE_WIDTH_2 ); + } + else + { + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE); + aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + } + + SvxBoxItem aBoxItem(RES_BOX); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::BOTTOM ); + HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), aBoxItem, nullptr, std::shared_ptr<HTMLAttrTable>()); + m_aSetAttrTab.push_back( pTmp ); + } + if( nWidth ) + { + // If we aren't in a table, then the width value will be "faked" with + // paragraph indents. That makes little sense in a table. In order to + // avoid that the line is considered during the width calculation, it + // still gets an appropriate LRSpace-Item. + if (!m_xTable) + { + // fake length and alignment of line above paragraph indents + long nBrowseWidth = GetCurrentBrowseWidth(); + nWidth = bPercentWidth ? static_cast<sal_uInt16>((nWidth*nBrowseWidth) / 100) + : ToTwips( static_cast<sal_uInt16>(nBrowseWidth) ); + if( nWidth < MINLAY ) + nWidth = MINLAY; + + const SwFormatColl *pColl = (static_cast<long>(nWidth) < nBrowseWidth) ? GetCurrFormatColl() : nullptr; + if (pColl) + { + SvxLRSpaceItem aLRItem( pColl->GetLRSpace() ); + long nDist = nBrowseWidth - nWidth; + + switch( eAdjust ) + { + case SvxAdjust::Right: + aLRItem.SetTextLeft( static_cast<sal_uInt16>(nDist) ); + break; + case SvxAdjust::Left: + aLRItem.SetRight( static_cast<sal_uInt16>(nDist) ); + break; + case SvxAdjust::Center: + default: + nDist /= 2; + aLRItem.SetTextLeft( static_cast<sal_uInt16>(nDist) ); + aLRItem.SetRight( static_cast<sal_uInt16>(nDist) ); + break; + } + + HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), aLRItem, nullptr, std::shared_ptr<HTMLAttrTable>()); + m_aSetAttrTab.push_back( pTmp ); + } + } + } + + // it's not possible to insert bookmarks in links + if( !aId.isEmpty() ) + InsertBookmark( aId ); + + // pop current context of stack + std::unique_ptr<HTMLAttrContext> xPoppedContext(PopContext(HtmlTokenId::HORZRULE)); + xPoppedContext.reset(); + + m_pPam->Move( fnMoveForward ); + + // and set the current style in the next paragraph + SetTextCollAttrs(); +} + +void SwHTMLParser::ParseMoreMetaOptions() +{ + OUString aName, aContent; + bool bHTTPEquiv = false; + + const HTMLOptions& rHTMLOptions = GetOptions(); + for (size_t i = rHTMLOptions.size(); i; ) + { + const HTMLOption& rOption = rHTMLOptions[--i]; + switch( rOption.GetToken() ) + { + case HtmlOptionId::NAME: + aName = rOption.GetString(); + bHTTPEquiv = false; + break; + case HtmlOptionId::HTTPEQUIV: + aName = rOption.GetString(); + bHTTPEquiv = true; + break; + case HtmlOptionId::CONTENT: + aContent = rOption.GetString(); + break; + default: break; + } + } + + // Here things get a little tricky: We know for sure, that the Doc-Info + // wasn't changed. Therefore it's enough to query for Generator and Refresh + // to find a not processed Token. These are the only ones which won't change + // the Doc-Info. + if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_generator ) || + aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_refresh ) || + aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_content_type ) || + aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_content_script_type ) ) + return; + + aContent = aContent.replaceAll("\r", "").replaceAll("\n", ""); + + if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_sdendnote ) ) + { + FillEndNoteInfo( aContent ); + return; + } + + if( aName.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_sdfootnote ) ) + { + FillFootNoteInfo( aContent ); + return; + } + + OUStringBuffer sText; + sText.append("HTML: <"); + sText.append(OOO_STRING_SVTOOLS_HTML_meta); + sText.append(' '); + if( bHTTPEquiv ) + sText.append(OOO_STRING_SVTOOLS_HTML_O_httpequiv); + else + sText.append(OOO_STRING_SVTOOLS_HTML_O_name); + sText.append("=\""); + sText.append(aName); + sText.append("\" "); + sText.append(OOO_STRING_SVTOOLS_HTML_O_content); + sText.append("=\""); + sText.append(aContent); + sText.append("\">"); + + SwPostItField aPostItField( + static_cast<SwPostItFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Postit )), + OUString(), sText.makeStringAndClear(), OUString(), OUString(), DateTime(DateTime::SYSTEM)); + SwFormatField aFormatField( aPostItField ); + InsertAttr( aFormatField, false ); +} + +HTMLAttr::HTMLAttr( const SwPosition& rPos, const SfxPoolItem& rItem, + HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab ) : + m_nStartPara( rPos.nNode ), + m_nEndPara( rPos.nNode ), + m_nStartContent( rPos.nContent.GetIndex() ), + m_nEndContent(rPos.nContent.GetIndex() ), + m_bInsAtStart( true ), + m_bLikePara( false ), + m_bValid( true ), + m_pItem( rItem.Clone() ), + m_xAttrTab( rAttrTab ), + m_pNext( nullptr ), + m_pPrev( nullptr ), + m_ppHead( ppHd ) +{ +} + +HTMLAttr::HTMLAttr( const HTMLAttr &rAttr, const SwNodeIndex &rEndPara, + sal_Int32 nEndCnt, HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab ) : + m_nStartPara( rAttr.m_nStartPara ), + m_nEndPara( rEndPara ), + m_nStartContent( rAttr.m_nStartContent ), + m_nEndContent( nEndCnt ), + m_bInsAtStart( rAttr.m_bInsAtStart ), + m_bLikePara( rAttr.m_bLikePara ), + m_bValid( rAttr.m_bValid ), + m_pItem( rAttr.m_pItem->Clone() ), + m_xAttrTab( rAttrTab ), + m_pNext( nullptr ), + m_pPrev( nullptr ), + m_ppHead( ppHd ) +{ +} + +HTMLAttr::~HTMLAttr() +{ +} + +HTMLAttr *HTMLAttr::Clone(const SwNodeIndex& rEndPara, sal_Int32 nEndCnt) const +{ + // create the attribute anew with old start position + HTMLAttr *pNew = new HTMLAttr( *this, rEndPara, nEndCnt, m_ppHead, m_xAttrTab ); + + // The Previous-List must be taken over, the Next-List not! + pNew->m_pPrev = m_pPrev; + + return pNew; +} + +void HTMLAttr::Reset(const SwNodeIndex& rSttPara, sal_Int32 nSttCnt, + HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab) +{ + // reset the start (and the end) + m_nStartPara = rSttPara; + m_nStartContent = nSttCnt; + m_nEndPara = rSttPara; + m_nEndContent = nSttCnt; + + // correct the head and nullify link + m_pNext = nullptr; + m_pPrev = nullptr; + m_ppHead = ppHd; + m_xAttrTab = rAttrTab; +} + +void HTMLAttr::InsertPrev( HTMLAttr *pPrv ) +{ + OSL_ENSURE( !pPrv->m_pNext || pPrv->m_pNext == this, + "HTMLAttr::InsertPrev: pNext wrong" ); + pPrv->m_pNext = nullptr; + + OSL_ENSURE( nullptr == pPrv->m_ppHead || m_ppHead == pPrv->m_ppHead, + "HTMLAttr::InsertPrev: ppHead wrong" ); + pPrv->m_ppHead = nullptr; + + HTMLAttr *pAttr = this; + while( pAttr->GetPrev() ) + pAttr = pAttr->GetPrev(); + + pAttr->m_pPrev = pPrv; +} + +bool SwHTMLParser::ParseMetaOptions( + const uno::Reference<document::XDocumentProperties> & i_xDocProps, + SvKeyValueIterator *i_pHeader ) +{ + // always call base ParseMetaOptions, it sets the encoding (#i96700#) + bool ret( HTMLParser::ParseMetaOptions(i_xDocProps, i_pHeader) ); + if (!ret && IsNewDoc()) + { + ParseMoreMetaOptions(); + } + return ret; +} + +// override so we can parse DOCINFO field subtypes INFO[1-4] +void SwHTMLParser::AddMetaUserDefined( OUString const & i_rMetaName ) +{ + // unless we already have 4 names, append the argument to m_InfoNames + OUString* pName // the first empty string in m_InfoNames + (m_InfoNames[0].isEmpty() ? &m_InfoNames[0] : + (m_InfoNames[1].isEmpty() ? &m_InfoNames[1] : + (m_InfoNames[2].isEmpty() ? &m_InfoNames[2] : + (m_InfoNames[3].isEmpty() ? &m_InfoNames[3] : nullptr )))); + if (pName) + { + (*pName) = i_rMetaName; + } +} + +void HTMLReader::SetupFilterOptions() +{ + // Reset state from previous Read() invocation. + m_aNamespace.clear(); + + if (!m_pMedium) + return; + + const SfxItemSet* pItemSet = m_pMedium->GetItemSet(); + if (!pItemSet) + return; + + auto pItem = pItemSet->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS); + if (!pItem) + return; + + OUString aFilterOptions = pItem->GetValue(); + const OUString aXhtmlNsKey("xhtmlns="); + if (aFilterOptions.startsWith(aXhtmlNsKey)) + { + OUString aNamespace = aFilterOptions.copy(aXhtmlNsKey.getLength()); + m_aNamespace = aNamespace; + } +} + +namespace +{ + class FontCacheGuard + { + public: + ~FontCacheGuard() + { + FlushFontCache(); + } + }; +} + +bool TestImportHTML(SvStream &rStream) +{ + FontCacheGuard aFontCacheGuard; + std::unique_ptr<Reader> xReader(new HTMLReader); + xReader->m_pStream = &rStream; + + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc(); + + SwNodeIndex aIdx(pD->GetNodes().GetEndOfContent(), -1); + SwPaM aPaM(aIdx); + pD->SetInReading(true); + bool bRet = false; + try + { + bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE; + } + catch (const std::runtime_error&) + { + } + catch (const std::out_of_range&) + { + } + pD->SetInReading(false); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/swhtml.hxx b/sw/source/filter/html/swhtml.hxx new file mode 100644 index 000000000..9fb7dbfce --- /dev/null +++ b/sw/source/filter/html/swhtml.hxx @@ -0,0 +1,1046 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_SWHTML_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_SWHTML_HXX + +#include <config_java.h> + +#include <sfx2/sfxhtml.hxx> +#include <svl/listener.hxx> +#include <svl/macitem.hxx> +#include <svtools/htmltokn.h> +#include <editeng/svxenum.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <fltshell.hxx> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/form/XFormComponent.hpp> + +#include <memory> +#include <vector> +#include <deque> +#include <stack> +#include <set> + +class SfxMedium; +class SfxViewFrame; +class SdrObject; +class SvxMacroTableDtor; +class SwDoc; +class SwPaM; +class SwViewShell; +class SwStartNode; +class SwFormatColl; +class SwField; +class SwHTMLForm_Impl; +class SwApplet_Impl; +struct SwHTMLFootEndNote_Impl; +class HTMLTableCnts; +struct SwPending; +class SvxCSS1PropertyInfo; +struct ImplSVEvent; + +#define HTML_CJK_PARSPACE (MM50/2) +#define HTML_CTL_PARSPACE (MM50/2) + +#define HTML_DFLT_IMG_WIDTH (MM50*4) +#define HTML_DFLT_IMG_HEIGHT (MM50*2) + +// some things you often need +extern HTMLOptionEnum<SvxAdjust> const aHTMLPAlignTable[]; +extern HTMLOptionEnum<sal_Int16> const aHTMLImgHAlignTable[]; +extern HTMLOptionEnum<sal_Int16> const aHTMLImgVAlignTable[]; + +// attribute stack: + +class HTMLAttr; +typedef std::deque<HTMLAttr *> HTMLAttrs; + +// Table of attributes: The order here is important: The attributes in the +// beginning of the table will set first in EndAllAttrs. +struct HTMLAttrTable +{ + HTMLAttr + *pKeep, // frame attributes + *pBox, + *pBrush, + *pBreak, + *pPageDesc, + + *pLRSpace, // paragraph attributes + *pULSpace, + *pLineSpacing, + *pAdjust, + *pDropCap, + *pSplit, + *pWidows, + *pOrphans, + *pDirection, + + *pCharFormats, // text attributes + *pINetFormat, + + *pBold, // character attributes + *pBoldCJK, + *pBoldCTL, + *pItalic, + *pItalicCJK, + *pItalicCTL, + *pStrike, + *pUnderline, + *pBlink, + *pFont, + *pFontCJK, + *pFontCTL, + *pFontHeight, + *pFontHeightCJK, + *pFontHeightCTL, + *pFontColor, + *pEscapement, + *pCaseMap, + *pKerning, // (only for SPACER) + *pCharBrush, // character background + *pLanguage, + *pLanguageCJK, + *pLanguageCTL, + *pCharBox + ; +}; + +class HTMLAttr +{ + friend class SwHTMLParser; + friend class CellSaveStruct; + + SwNodeIndex m_nStartPara, m_nEndPara; + sal_Int32 m_nStartContent, m_nEndContent; + bool m_bInsAtStart : 1; + bool m_bLikePara : 1; // set attribute above the whole paragraph + bool m_bValid : 1; // is the attribute valid? + + std::unique_ptr<SfxPoolItem> m_pItem; + std::shared_ptr<HTMLAttrTable> m_xAttrTab; + HTMLAttr *m_pNext; // still to close attributes with different values + HTMLAttr *m_pPrev; // already closed but not set attributes + HTMLAttr **m_ppHead; // list head + + HTMLAttr( const SwPosition& rPos, const SfxPoolItem& rItem, + HTMLAttr **pHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab ); + + HTMLAttr( const HTMLAttr &rAttr, const SwNodeIndex &rEndPara, + sal_Int32 nEndCnt, HTMLAttr **pHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab ); + +public: + + ~HTMLAttr(); + + HTMLAttr *Clone( const SwNodeIndex& rEndPara, sal_Int32 nEndCnt ) const; + void Reset( const SwNodeIndex& rSttPara, sal_Int32 nSttCnt, + HTMLAttr **pHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab ); + inline void SetStart( const SwPosition& rPos ); + + sal_uInt32 GetSttParaIdx() const { return m_nStartPara.GetIndex(); } + sal_uInt32 GetEndParaIdx() const { return m_nEndPara.GetIndex(); } + + const SwNodeIndex& GetSttPara() const { return m_nStartPara; } + const SwNodeIndex& GetEndPara() const { return m_nEndPara; } + + sal_Int32 GetSttCnt() const { return m_nStartContent; } + sal_Int32 GetEndCnt() const { return m_nEndContent; } + + bool IsLikePara() const { return m_bLikePara; } + void SetLikePara() { m_bLikePara = true; } + + SfxPoolItem& GetItem() { return *m_pItem; } + const SfxPoolItem& GetItem() const { return *m_pItem; } + + HTMLAttr *GetNext() const { return m_pNext; } + void InsertNext( HTMLAttr *pNxt ) { m_pNext = pNxt; } + + HTMLAttr *GetPrev() const { return m_pPrev; } + void InsertPrev( HTMLAttr *pPrv ); + void ClearPrev() { m_pPrev = nullptr; } + + void SetHead(HTMLAttr **ppHd, const std::shared_ptr<HTMLAttrTable>& rAttrTab) + { + m_ppHead = ppHd; + m_xAttrTab = rAttrTab; + } + + // During setting attributes from styles it can happen that these + // shouldn't be set anymore. To delete them would be very expensive, because + // you don't know all the places where they are linked in. Therefore they're + // made invalid and deleted at the next call of SetAttr_(). + void Invalidate() { m_bValid = false; } +}; + +class HTMLAttrContext_SaveDoc; + +enum SwHTMLAppendMode { + AM_NORMAL, // no paragraph spacing handling + AM_NOSPACE, // set spacing hard to 0cm + AM_SPACE, // set spacing hard to 0.5cm + AM_SOFTNOSPACE, // don't set spacing, but save 0cm + AM_NONE // no append +}; + +class HTMLAttrContext +{ + HTMLAttrs m_aAttrs; // the attributes created in the context + + OUString m_aClass; // context class + + std::unique_ptr<HTMLAttrContext_SaveDoc> m_pSaveDocContext; + std::unique_ptr<SfxItemSet> m_pFrameItemSet; + + HtmlTokenId m_nToken; // the token of the context + + sal_uInt16 m_nTextFormatColl; // a style created in the context or zero + + sal_uInt16 m_nLeftMargin; // a changed left border + sal_uInt16 m_nRightMargin; // a changed right border + sal_uInt16 m_nFirstLineIndent; // a changed first line indent + + sal_uInt16 m_nUpperSpace; + sal_uInt16 m_nLowerSpace; + + SwHTMLAppendMode m_eAppend; + + bool m_bLRSpaceChanged : 1; // left/right border, changed indent? + bool m_bULSpaceChanged : 1; // top/bottom border changed? + bool m_bDefaultTextFormatColl : 1;// nTextFormatColl is only default + bool m_bSpansSection : 1; // the context opens a SwSection + bool m_bPopStack : 1; // delete above stack elements + bool m_bFinishPREListingXMP : 1; + bool m_bRestartPRE : 1; + bool m_bRestartXMP : 1; + bool m_bRestartListing : 1; + bool m_bHeaderOrFooter : 1; + + bool m_bVisible = true; + +public: + void ClearSaveDocContext(); + + HTMLAttrContext( HtmlTokenId nTokn, sal_uInt16 nPoolId, const OUString& rClass, + bool bDfltColl=false ); + explicit HTMLAttrContext( HtmlTokenId nTokn ); + ~HTMLAttrContext(); + + HtmlTokenId GetToken() const { return m_nToken; } + + sal_uInt16 GetTextFormatColl() const { return m_bDefaultTextFormatColl ? 0 : m_nTextFormatColl; } + sal_uInt16 GetDfltTextFormatColl() const { return m_bDefaultTextFormatColl ? m_nTextFormatColl : 0; } + + const OUString& GetClass() const { return m_aClass; } + + inline void SetMargins( sal_uInt16 nLeft, sal_uInt16 nRight, short nIndent ); + + bool IsLRSpaceChanged() const { return m_bLRSpaceChanged; } + inline void GetMargins( sal_uInt16& nLeft, sal_uInt16& nRight, + short &nIndent ) const; + + inline void SetULSpace( sal_uInt16 nUpper, sal_uInt16 nLower ); + bool IsULSpaceChanged() const { return m_bULSpaceChanged; } + inline void GetULSpace( sal_uInt16& rUpper, sal_uInt16& rLower ) const; + + bool HasAttrs() const { return !m_aAttrs.empty(); } + const HTMLAttrs& GetAttrs() const { return m_aAttrs; } + HTMLAttrs& GetAttrs() { return m_aAttrs; } + + void SetSpansSection( bool bSet ) { m_bSpansSection = bSet; } + bool GetSpansSection() const { return m_bSpansSection; } + + void SetPopStack( bool bSet ) { m_bPopStack = bSet; } + bool GetPopStack() const { return m_bPopStack; } + + bool HasSaveDocContext() const { return m_pSaveDocContext!=nullptr; } + HTMLAttrContext_SaveDoc *GetSaveDocContext( bool bCreate=false ); + + const SfxItemSet *GetFrameItemSet() const { return m_pFrameItemSet.get(); } + SfxItemSet *GetFrameItemSet( SwDoc *pCreateDoc ); + + void SetFinishPREListingXMP( bool bSet ) { m_bFinishPREListingXMP = bSet; } + bool IsFinishPREListingXMP() const { return m_bFinishPREListingXMP; } + + void SetRestartPRE( bool bSet ) { m_bRestartPRE = bSet; } + bool IsRestartPRE() const { return m_bRestartPRE; } + + void SetRestartXMP( bool bSet ) { m_bRestartXMP = bSet; } + bool IsRestartXMP() const { return m_bRestartXMP; } + + void SetRestartListing( bool bSet ) { m_bRestartListing = bSet; } + bool IsRestartListing() const { return m_bRestartListing; } + + void SetHeaderOrFooter( bool bSet ) { m_bHeaderOrFooter = bSet; } + bool IsHeaderOrFooter() const { return m_bHeaderOrFooter; } + + void SetAppendMode( SwHTMLAppendMode eMode ) { m_eAppend = eMode; } + SwHTMLAppendMode GetAppendMode() const { return m_eAppend; } + + void SetVisible(bool bVisible) { m_bVisible = bVisible; } + bool IsVisible() const { return m_bVisible; } +}; + +typedef std::vector<std::unique_ptr<HTMLAttrContext>> HTMLAttrContexts; + +class HTMLTable; +class SwCSS1Parser; +class SwHTMLNumRuleInfo; + +typedef std::vector<std::unique_ptr<ImageMap>> ImageMaps; + +enum class HtmlContextFlags { + ProtectStack = 0x0001, + StripPara = 0x0002, + KeepNumrule = 0x0004, + HeaderDist = 0x0008, + FooterDist = 0x0010, + KeepAttrs = 0x0020, + MultiColMask = StripPara | KeepNumrule | KeepAttrs // for headers, footers or footnotes +}; +namespace o3tl +{ + template<> struct typed_flags<HtmlContextFlags> : is_typed_flags<HtmlContextFlags, 0x03f> {}; +} + +enum class HtmlFrameFormatFlags { + Box = 0x0001, + Background = 0x0002, + Padding = 0x0004, + Direction = 0x0008, +}; +namespace o3tl +{ + template<> struct typed_flags<HtmlFrameFormatFlags> : is_typed_flags<HtmlFrameFormatFlags, 0x0f> {}; +} + +class SwHTMLFrameFormatListener : public SvtListener +{ + SwFrameFormat* m_pFrameFormat; +public: + SwHTMLFrameFormatListener(SwFrameFormat* pFrameFormat); + SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; } + virtual void Notify(const SfxHint&) override; +}; + +class SwHTMLParser : public SfxHTMLParser, public SvtListener +{ + friend class SectionSaveStruct; + friend class CellSaveStruct; + friend class CaptionSaveStruct; + + /* + Progress bar + */ + std::unique_ptr<ImportProgress> m_xProgress; + + OUString m_aPathToFile; + OUString m_sBaseURL; + OUString m_aBasicLib; + OUString m_aBasicModule; + OUString m_aScriptSource; // content of the current script block + OUString m_aScriptType; // type of read script (StarBasic/VB/JAVA) + OUString m_aScriptURL; // script URL + OUString m_aStyleSource; // content of current style sheet + OUString m_aContents; // text of current marquee, field and so + OUStringBuffer m_sTitle; + OUString m_aUnknownToken; // a started unknown token + OUString m_aBulletGrfs[MAXLEVEL]; + OUString m_sJmpMark; + + std::vector<sal_uInt16> m_aBaseFontStack; // stack for <BASEFONT> + // Bit 0-2: font size (1-7) + std::vector<sal_uInt16> m_aFontStack; // stack for <FONT>, <BIG>, <SMALL> + // Bit 0-2: font size (1-7) + // Bit 15: font colour was set + + HTMLAttrs m_aSetAttrTab;// "closed", not set attributes + HTMLAttrs m_aParaAttrs; // temporary paragraph attributes + std::shared_ptr<HTMLAttrTable> m_xAttrTab; // "open" attributes + HTMLAttrContexts m_aContexts;// the current context of attribute/token + std::vector<std::unique_ptr<SwHTMLFrameFormatListener>> m_aMoveFlyFrames;// Fly-Frames, the anchor is moved + std::deque<sal_Int32> m_aMoveFlyCnts;// and the Content-Positions + //stray SwTableBoxes which need to be deleted to avoid leaking, but hold + //onto them until parsing is done + std::vector<std::unique_ptr<SwTableBox>> m_aOrphanedTableBoxes; + + std::unique_ptr<SwApplet_Impl> m_pAppletImpl; // current applet + + std::unique_ptr<SwCSS1Parser> m_pCSS1Parser; // Style-Sheet-Parser + std::unique_ptr<SwHTMLNumRuleInfo> m_pNumRuleInfo; + std::vector<SwPending> m_vPendingStack; + + rtl::Reference<SwDoc> m_xDoc; + SwPaM *m_pPam; // SwPosition should be enough, or ?? + SwViewShell *m_pActionViewShell; // SwViewShell, where StartAction was called + SwNodeIndex *m_pSttNdIdx; + + std::vector<HTMLTable*> m_aTables; + std::shared_ptr<HTMLTable> m_xTable; // current "outermost" table + SwHTMLForm_Impl* m_pFormImpl; // current form + SdrObject *m_pMarquee; // current marquee + std::unique_ptr<SwField> m_xField; // current field + ImageMap *m_pImageMap; // current image map + std::unique_ptr<ImageMaps> m_pImageMaps; ///< all Image-Maps that have been read + std::unique_ptr<SwHTMLFootEndNote_Impl> m_pFootEndNoteImpl; + + Size m_aHTMLPageSize; // page size of HTML template + + sal_uInt32 m_aFontHeights[7]; // font heights 1-7 + ImplSVEvent * m_nEventId; + + sal_uInt16 m_nBaseFontStMin; + sal_uInt16 m_nFontStMin; + sal_uInt16 m_nDefListDeep; + sal_uInt16 m_nFontStHeadStart; // elements in font stack at <Hn> + sal_uInt16 m_nSBModuleCnt; // counter for basic modules + sal_uInt16 m_nMissingImgMaps; // How many image maps are still missing? + size_t m_nParaCnt; + size_t m_nContextStMin; // lower limit of PopContext + size_t m_nContextStAttrMin; // lower limit of attributes + sal_uInt16 m_nSelectEntryCnt; // Number of entries in the actual listbox + HtmlTokenId m_nOpenParaToken; // opened paragraph element + + enum class JumpToMarks { NONE, Mark, Table, Region, Graphic }; + JumpToMarks m_eJumpTo; + +#ifdef DBG_UTIL + sal_uInt16 m_nContinue; // depth of Continue calls +#endif + + SvxAdjust m_eParaAdjust; // adjustment of current paragraph + HTMLScriptLanguage m_eScriptLang; // current script language + + bool m_bOldIsHTMLMode : 1; // Was it a HTML document? + + bool m_bDocInitalized : 1; // document resp. shell was initialize + // flag to prevent double init via recursion + bool m_bViewCreated : 1; // the view was already created (asynchronous) + bool m_bSetModEnabled : 1; + + bool m_bInFloatingFrame : 1; // We are in a floating frame + bool m_bInField : 1; + bool m_bKeepUnknown : 1; // handle unknown/not supported tokens + // 8 + bool m_bCallNextToken : 1; // In tables: call NextToken in any case + bool m_bIgnoreRawData : 1; // ignore content of script/style + bool m_bLBEntrySelected : 1; // Is the current option selected? + bool m_bTAIgnoreNewPara : 1; // ignore next LF in text area? + bool m_bFixMarqueeWidth : 1; // Change size of marquee? + + bool m_bUpperSpace : 1; // top paragraph spacing is needed + bool m_bNoParSpace : 1; + // 16 + + bool m_bInNoEmbed : 1; // we are in a NOEMBED area + + bool m_bInTitle : 1; // we are in title + + bool m_bChkJumpMark : 1; // maybe jump to predetermined mark + bool m_bUpdateDocStat : 1; + bool m_bFixSelectWidth : 1; // Set new width of select? + bool m_bTextArea : 1; + // 24 + bool m_bSelect : 1; + bool m_bInFootEndNoteAnchor : 1; + bool m_bInFootEndNoteSymbol : 1; + bool m_bIgnoreHTMLComments : 1; + bool m_bRemoveHidden : 1; // the filter implementation might set the hidden flag + + bool m_bBodySeen : 1; + bool m_bReadingHeaderOrFooter : 1; + bool m_bNotifyMacroEventRead : 1; + bool m_isInTableStructure; + + sal_Int32 m_nTableDepth; + + /// the names corresponding to the DOCINFO field subtypes INFO[1-4] + OUString m_InfoNames[4]; + + SfxViewFrame* m_pTempViewFrame; + + bool m_bXHTML = false; + bool m_bReqIF = false; + + /** + * Non-owning pointers to already inserted OLE nodes, matching opened + * <object> XHTML elements. + */ + std::stack<SwOLENode*> m_aEmbeds; + + std::set<OUString> m_aAllowedRTFOLEMimeTypes; + + void DeleteFormImpl(); + + void DocumentDetected(); + void Show(); + void ShowStatline(); + SwViewShell *CallStartAction( SwViewShell *pVSh = nullptr, bool bChkPtr = true ); + SwViewShell *CallEndAction( bool bChkAction = false, bool bChkPtr = true ); + SwViewShell *CheckActionViewShell(); + + DECL_LINK( AsyncCallback, void*, void ); + + // set attribute on document + void SetAttr_( bool bChkEnd, bool bBeforeTable, std::deque<std::unique_ptr<HTMLAttr>> *pPostIts ); + void SetAttr( bool bChkEnd = true, bool bBeforeTable = false, + std::deque<std::unique_ptr<HTMLAttr>> *pPostIts = nullptr ) + { + if( !m_aSetAttrTab.empty() || !m_aMoveFlyFrames.empty() ) + SetAttr_( bChkEnd, bBeforeTable, pPostIts ); + } + + HTMLAttr **GetAttrTabEntry( sal_uInt16 nWhich ); + + // create a new text node on PaM position + bool AppendTextNode( SwHTMLAppendMode eMode=AM_NORMAL, bool bUpdateNum=true ); + void AddParSpace(); + + // start/end an attribute + // ppDepAttr indicated an attribute table entry, which attribute has to be + // set, before the attribute is closed + void NewAttr(const std::shared_ptr<HTMLAttrTable>& rAttrTab, HTMLAttr **ppAttr, const SfxPoolItem& rItem); + bool EndAttr( HTMLAttr *pAttr, bool bChkEmpty=true ); + void DeleteAttr( HTMLAttr* pAttr ); + + void EndContextAttrs( HTMLAttrContext *pContext ); + void SaveAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab); + void SplitAttrTab( const SwPosition& rNewPos ); + void SplitAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab, bool bMoveEndBack); + void RestoreAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab); + void InsertAttr( const SfxPoolItem& rItem, bool bInsAtStart ); + void InsertAttrs( std::deque<std::unique_ptr<HTMLAttr>> rAttrs ); + + bool DoPositioning( SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + HTMLAttrContext *pContext ); + bool CreateContainer( const OUString& rClass, SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + HTMLAttrContext *pContext ); + bool EndSection( bool bLFStripped=false ); + + void InsertAttrs( SfxItemSet &rItemSet, SvxCSS1PropertyInfo const &rPropInfo, + HTMLAttrContext *pContext, bool bCharLvl=false ); + void InsertAttr( HTMLAttr **ppAttr, const SfxPoolItem & rItem, + HTMLAttrContext *pCntxt ); + void SplitPREListingXMP( HTMLAttrContext *pCntxt ); + void FixHeaderFooterDistance( bool bHeader, const SwPosition *pOldPos ); + + void EndContext( HTMLAttrContext *pContext ); + void ClearContext( HTMLAttrContext *pContext ); + + const SwFormatColl *GetCurrFormatColl() const; + + SwTwips GetCurrentBrowseWidth(); + + SwHTMLNumRuleInfo& GetNumInfo() { return *m_pNumRuleInfo; } + // add parameter <bCountedInList> + void SetNodeNum( sal_uInt8 nLevel ); + + // Manage paragraph styles + + // set the style resp. its attributes on the stack + void SetTextCollAttrs( HTMLAttrContext *pContext = nullptr ); + + void InsertParaAttrs( const SfxItemSet& rItemSet ); + + // Manage attribute context + + // save current context + void PushContext(std::unique_ptr<HTMLAttrContext>& rCntxt) + { + m_aContexts.push_back(std::move(rCntxt)); + } + + // Fetch top/specified context but not outside the context with token + // nLimit. If bRemove set then remove it. + std::unique_ptr<HTMLAttrContext> PopContext(HtmlTokenId nToken = HtmlTokenId::NONE); + + void GetMarginsFromContext( sal_uInt16 &nLeft, sal_uInt16 &nRight, short& nIndent, + bool bIgnoreCurrent=false ) const; + void GetMarginsFromContextWithNumberBullet( sal_uInt16 &nLeft, sal_uInt16 &nRight, + short& nIndent ) const; + void GetULSpaceFromContext( sal_uInt16 &rUpper, sal_uInt16 &rLower ) const; + + void MovePageDescAttrs( SwNode *pSrcNd, sal_uLong nDestIdx, bool bFormatBreak ); + + // Handling of tags at paragraph level + + // <P> and <H1> to <H6> + void NewPara(); + void EndPara( bool bReal = false ); + void NewHeading( HtmlTokenId nToken ); + void EndHeading(); + + // <ADDRESS>, <BLOCKQUOTE> and <PRE> + void NewTextFormatColl( HtmlTokenId nToken, sal_uInt16 nPoolId ); + void EndTextFormatColl( HtmlTokenId nToken ); + + // <DIV> and <CENTER> + void NewDivision( HtmlTokenId nToken ); + void EndDivision(); + + // insert/close Fly-Frames + void InsertFlyFrame( const SfxItemSet& rItemSet, HTMLAttrContext *pCntxt, + const OUString& rId ); + + void SaveDocContext( HTMLAttrContext *pCntxt, HtmlContextFlags nFlags, + const SwPosition *pNewPos ); + void RestoreDocContext( HTMLAttrContext *pCntxt ); + + // end all opened <DIV> areas + bool EndSections( bool bLFStripped ); + + // <MULTICOL> + void NewMultiCol( sal_uInt16 columnsFromCss=0 ); + + // <MARQUEE> + void NewMarquee( HTMLTable *pCurTable=nullptr ); + void EndMarquee(); + void InsertMarqueeText(); + + // Handling of lists + + // order list <OL> and unordered list <UL> with <LI> + void NewNumberBulletList( HtmlTokenId nToken ); + void EndNumberBulletList( HtmlTokenId nToken = HtmlTokenId::NONE ); + void NewNumberBulletListItem( HtmlTokenId nToken ); + void EndNumberBulletListItem( HtmlTokenId nToken, bool bSetColl); + + // definitions lists <DL> with <DD>, <DT> + void NewDefList(); + void EndDefList(); + void NewDefListItem( HtmlTokenId nToken ); + void EndDefListItem( HtmlTokenId nToken = HtmlTokenId::NONE ); + + // Handling of tags on character level + + // handle tags like <B>, <I> and so, which enable/disable a certain + // attribute or like <SPAN> get attributes from styles + void NewStdAttr( HtmlTokenId nToken ); + void NewStdAttr( HtmlTokenId nToken, + HTMLAttr **ppAttr, const SfxPoolItem & rItem, + HTMLAttr **ppAttr2=nullptr, const SfxPoolItem *pItem2=nullptr, + HTMLAttr **ppAttr3=nullptr, const SfxPoolItem *pItem3=nullptr ); + void EndTag( HtmlTokenId nToken ); + + // handle font attributes + void NewBasefontAttr(); // for <BASEFONT> + void EndBasefontAttr(); + void NewFontAttr( HtmlTokenId nToken ); // for <FONT>, <BIG> and <SMALL> + void EndFontAttr( HtmlTokenId nToken ); + + // tags realized via character styles + void NewCharFormat( HtmlTokenId nToken ); + + void ClearFootnotesMarksInRange(const SwNodeIndex& rSttIdx, const SwNodeIndex& rEndIdx); + + void DeleteSection(SwStartNode* pSttNd); + + // <SDFIELD> +public: + static SvxNumType GetNumType( const OUString& rStr, SvxNumType eDfltType ); +private: + void NewField(); + void EndField(); + void InsertFieldText(); + + // <SPACER> + void InsertSpacer(); + + // Inserting graphics, plug-ins and applets + + // search image maps and link with graphic nodes + ImageMap *FindImageMap( const OUString& rURL ) const; + void ConnectImageMaps(); + + // find anchor of Fly-Frames and set corresponding attributes + // in Attrset (htmlgrin.cxx) + void SetAnchorAndAdjustment( sal_Int16 eVertOri, + sal_Int16 eHoriOri, + const SvxCSS1PropertyInfo &rPropInfo, + SfxItemSet& rFrameSet ); + void SetAnchorAndAdjustment( sal_Int16 eVertOri, + sal_Int16 eHoriOri, + SfxItemSet& rFrameSet, + bool bDontAppend=false ); + void SetAnchorAndAdjustment( const SvxCSS1PropertyInfo &rPropInfo, + SfxItemSet &rFrameItemSet ); + + static void SetFrameFormatAttrs( SfxItemSet &rItemSet, + HtmlFrameFormatFlags nFlags, SfxItemSet &rFrameItemSet ); + + // create frames and register auto bound frames + void RegisterFlyFrame( SwFrameFormat *pFlyFrame ); + + // Adjust the size of the Fly-Frames to requirements and conditions + // (not for graphics, therefore htmlplug.cxx) + static void SetFixSize( const Size& rPixSize, const Size& rTwipDfltSize, + bool bPercentWidth, bool bPercentHeight, + SvxCSS1PropertyInfo const &rPropInfo, + SfxItemSet& rFlyItemSet ); + static void SetVarSize( SvxCSS1PropertyInfo const &rPropInfo, + SfxItemSet& rFlyItemSet, SwTwips nDfltWidth=MINLAY, + sal_uInt8 nDefaultPercentWidth=0 ); + static void SetSpace( const Size& rPixSpace, SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, SfxItemSet& rFlyItemSet ); + + sal_uInt16 IncGrfsThatResizeTable(); + + void GetDefaultScriptType( ScriptType& rType, + OUString& rTypeStr ) const; + + // the actual insert methods for <IMG>, <EMBED>, <APPLET> and <PARAM> + void InsertImage(); // htmlgrin.cxx + bool InsertEmbed(); // htmlplug.cxx + +#if HAVE_FEATURE_JAVA + void NewObject(); // htmlplug.cxx +#endif + void EndObject(); // link CommandLine with applet (htmlplug.cxx) +#if HAVE_FEATURE_JAVA + void InsertApplet(); // htmlplug.cxx +#endif + void EndApplet(); // link CommandLine with applet (htmlplug.cxx) + void InsertParam(); // htmlplug.cxx + + void InsertFloatingFrame(); + + // parse <BODY>-tag: set background graphic and background colour (htmlgrin.cxx) + void InsertBodyOptions(); + + // Inserting links and bookmarks (htmlgrin.cxx) + + // parse <A>-tag: insert a link resp. bookmark + void NewAnchor(); + void EndAnchor(); + + // insert bookmark + void InsertBookmark( const OUString& rName ); + + void InsertCommentText( const char *pTag ); + void InsertComment( const OUString& rName, const char *pTag = nullptr ); + + // Has the current paragraph bookmarks? + bool HasCurrentParaBookmarks( bool bIgnoreStack=false ) const; + + // Inserting script/basic elements + + // parse the last read basic module (htmlbas.cxx) + void NewScript(); + void EndScript(); + + void AddScriptSource(); + + // insert event in SFX configuration (htmlbas.cxx) + void InsertBasicDocEvent( const OUString& aEventName, const OUString& rName, + ScriptType eScrType, const OUString& rScrType ); + + // Inserting styles + + // <STYLE> + void NewStyle(); + void EndStyle(); + + static inline bool HasStyleOptions( const OUString &rStyle, const OUString &rId, + const OUString &rClass, const OUString *pLang=nullptr, + const OUString *pDir=nullptr ); + bool ParseStyleOptions( const OUString &rStyle, const OUString &rId, + const OUString &rClass, SfxItemSet &rItemSet, + SvxCSS1PropertyInfo &rPropInfo, + const OUString *pLang=nullptr, const OUString *pDir=nullptr ); + + // Inserting Controls and Forms (htmlform.cxx) + + // Insert draw object into document + void InsertDrawObject( SdrObject* pNewDrawObj, const Size& rSpace, + sal_Int16 eVertOri, + sal_Int16 eHoriOri, + SfxItemSet& rCSS1ItemSet, + SvxCSS1PropertyInfo& rCSS1PropInfo ); + css::uno::Reference< css::drawing::XShape > InsertControl( + const css::uno::Reference< css::form::XFormComponent > & rFormComp, + const css::uno::Reference< css::beans::XPropertySet > & rFCompPropSet, + const Size& rSize, + sal_Int16 eVertOri, + sal_Int16 eHoriOri, + SfxItemSet& rCSS1ItemSet, + SvxCSS1PropertyInfo& rCSS1PropInfo, + const SvxMacroTableDtor& rMacroTable, + const std::vector<OUString>& rUnoMacroTable, + const std::vector<OUString>& rUnoMacroParamTable, + bool bSetPropSet = true, + bool bHidden = false ); + void SetControlSize( const css::uno::Reference< css::drawing::XShape > & rShape, const Size& rTextSz, + bool bMinWidth, bool bMinHeight ); + +public: + static void ResizeDrawObject( SdrObject* pObj, SwTwips nWidth ); +private: + static void RegisterDrawObjectToTable( HTMLTable *pCurTable, SdrObject* pObj, + sal_uInt8 nWidth ); + + void NewForm( bool bAppend=true ); + void EndForm( bool bAppend=true ); + + // Insert methods for <INPUT>, <TEXTAREA> and <SELECT> + void InsertInput(); + + void NewTextArea(); + void InsertTextAreaText( HtmlTokenId nToken ); + void EndTextArea(); + + void NewSelect(); + void InsertSelectOption(); + void InsertSelectText(); + void EndSelect(); + + // Inserting tables (htmltab.cxx) +public: + + // Insert box content after the given node + const SwStartNode *InsertTableSection( const SwStartNode *pPrevStNd ); + + // Insert box content at the end of the table containing the PaM + // and move the PaM into the cell + const SwStartNode *InsertTableSection( sal_uInt16 nPoolId ); + + // Insert methods for various table tags + std::unique_ptr<HTMLTableCnts> InsertTableContents( bool bHead ); + +private: + // Create a section for the temporary storage of the table caption + SwStartNode *InsertTempTableCaptionSection(); + + void BuildTableCell( HTMLTable *pTable, bool bReadOptions, bool bHead ); + void BuildTableRow( HTMLTable *pTable, bool bReadOptions, + SvxAdjust eGrpAdjust, sal_Int16 eVertOri ); + void BuildTableSection( HTMLTable *pTable, bool bReadOptions, bool bHead ); + void BuildTableColGroup( HTMLTable *pTable, bool bReadOptions ); + void BuildTableCaption( HTMLTable *pTable ); + std::shared_ptr<HTMLTable> BuildTable(SvxAdjust eCellAdjust, + bool bIsParentHead = false, + bool bHasParentSection=true, + bool bHasToFlow = false); + + // misc ... + + void ParseMoreMetaOptions(); + + bool FileDownload( const OUString& rURL, OUString& rStr ); + void InsertLink(); + + void InsertIDOption(); + void InsertLineBreak(); + void InsertHorzRule(); + + void FillEndNoteInfo( const OUString& rContent ); + void FillFootNoteInfo( const OUString& rContent ); + void InsertFootEndNote( const OUString& rName, bool bEndNote, bool bFixed ); + void FinishFootEndNote(); + void InsertFootEndNoteText(); + SwNodeIndex *GetFootEndNoteSection( const OUString& rName ); + + sal_Int32 StripTrailingLF(); + + // Remove empty paragraph at the PaM position + void StripTrailingPara(); + // If removing an empty node would corrupt the document + bool CanRemoveNode(sal_uLong nNodeIdx) const; + + // Are there fly frames in the current paragraph? + bool HasCurrentParaFlys( bool bNoSurroundOnly = false, + bool bSurroundOnly = false ) const; + + bool PendingObjectsInPaM(SwPaM& rPam) const; + + class TableDepthGuard + { + private: + SwHTMLParser& m_rParser; + public: + TableDepthGuard(SwHTMLParser& rParser) + : m_rParser(rParser) + { + ++m_rParser.m_nTableDepth; + } + bool TooDeep() const { return m_rParser.m_nTableDepth > 1024; } + ~TableDepthGuard() + { + --m_rParser.m_nTableDepth; + } + }; + +public: // used in tables + + // Create brush item (with new) or 0 + SvxBrushItem* CreateBrushItem( const Color *pColor, + const OUString &rImageURL, + const OUString &rStyle, + const OUString &rId, + const OUString &rClass ); + +protected: + // Executed for each token recognized by CallParser + virtual void NextToken( HtmlTokenId nToken ) override; + virtual ~SwHTMLParser() override; + + // If the document is removed, remove the parser as well + virtual void Notify(const SfxHint&) override; + + virtual void AddMetaUserDefined( OUString const & i_rMetaName ) override; + +public: + + SwHTMLParser( SwDoc* pD, SwPaM & rCursor, SvStream& rIn, + const OUString& rFileName, + const OUString& rBaseURL, + bool bReadNewDoc, + SfxMedium* pMed, bool bReadUTF8, + bool bIgnoreHTMLComments, + const OUString& rNamespace); + + virtual SvParserState CallParser() override; + + static sal_uInt16 ToTwips( sal_uInt16 nPixel ); + + // for reading asynchronously from SvStream + virtual void Continue( HtmlTokenId nToken ) override; + + virtual bool ParseMetaOptions( const css::uno::Reference<css::document::XDocumentProperties>&, + SvKeyValueIterator* ) override; + + + void RegisterHTMLTable(HTMLTable* pNew) + { + m_aTables.push_back(pNew); + } + + void DeregisterHTMLTable(HTMLTable* pOld); + + SwDoc* GetDoc() const; + + bool IsReqIF() const; + + bool IsReadingHeaderOrFooter() const { return m_bReadingHeaderOrFooter; } + + void NotifyMacroEventRead(); + + /// Strips query and fragment from a URL path if base URL is a file:// one. + static OUString StripQueryFromPath(const OUString& rBase, const OUString& rPath); +}; + +struct SwPendingData +{ + virtual ~SwPendingData() {} +}; + +struct SwPending +{ + HtmlTokenId nToken; + std::unique_ptr<SwPendingData> pData; + + SwPending( HtmlTokenId nTkn ) + : nToken( nTkn ) + {} +}; + +inline void HTMLAttr::SetStart( const SwPosition& rPos ) +{ + m_nStartPara = rPos.nNode; + m_nStartContent = rPos.nContent.GetIndex(); + m_nEndPara = m_nStartPara; + m_nEndContent = m_nStartContent; +} + +inline void HTMLAttrContext::SetMargins( sal_uInt16 nLeft, sal_uInt16 nRight, + short nIndent ) +{ + m_nLeftMargin = nLeft; + m_nRightMargin = nRight; + m_nFirstLineIndent = nIndent; + m_bLRSpaceChanged = true; +} + +inline void HTMLAttrContext::GetMargins( sal_uInt16& nLeft, + sal_uInt16& nRight, + short& nIndent ) const +{ + if( m_bLRSpaceChanged ) + { + nLeft = m_nLeftMargin; + nRight = m_nRightMargin; + nIndent = m_nFirstLineIndent; + } +} + +inline void HTMLAttrContext::SetULSpace( sal_uInt16 nUpper, sal_uInt16 nLower ) +{ + m_nUpperSpace = nUpper; + m_nLowerSpace = nLower; + m_bULSpaceChanged = true; +} + +inline void HTMLAttrContext::GetULSpace( sal_uInt16& rUpper, + sal_uInt16& rLower ) const +{ + if( m_bULSpaceChanged ) + { + rUpper = m_nUpperSpace; + rLower = m_nLowerSpace; + } +} + +inline bool SwHTMLParser::HasStyleOptions( const OUString &rStyle, + const OUString &rId, + const OUString &rClass, + const OUString *pLang, + const OUString *pDir ) +{ + return !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() || + (pLang && !pLang->isEmpty()) || (pDir && !pDir->isEmpty()); +} + +class SwTextFootnote; + +struct SwHTMLTextFootnote +{ + OUString sName; + SwTextFootnote* pTextFootnote; + SwHTMLTextFootnote(const OUString &rName, SwTextFootnote* pInTextFootnote) + : sName(rName) + , pTextFootnote(pInTextFootnote) + { + } +}; + +struct SwHTMLFootEndNote_Impl +{ + std::vector<SwHTMLTextFootnote> aTextFootnotes; + + OUString sName; + OUString sContent; // information for the last footnote + bool bEndNote; + bool bFixed; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx new file mode 100644 index 000000000..2f83734ae --- /dev/null +++ b/sw/source/filter/html/wrthtml.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 <stdlib.h> +#include <hintids.hxx> +#include <comphelper/string.hxx> +#include <svl/urihelper.hxx> +#include <svl/languageoptions.hxx> +#include <rtl/tencinfo.h> +#include <sfx2/linkmgr.hxx> +#include <sfx2/docfile.hxx> + +#include <svtools/htmlcfg.hxx> +#include <svtools/htmltokn.h> +#include <svtools/htmlkywd.hxx> +#include <vcl/svapp.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <sfx2/frmhtmlw.hxx> +#include <svx/xoutbmp.hxx> +#include <svx/unobrushitemhelper.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/langitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/frmdiritem.hxx> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <fmthdft.hxx> +#include <fmtfld.hxx> +#include <fmtpdsc.hxx> +#include <txatbase.hxx> +#include <frmatr.hxx> +#include <charfmt.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <ndtxt.hxx> +#include <mdiexp.hxx> +#include <fltini.hxx> +#include <viewopt.hxx> +#include <IMark.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <section.hxx> +#include <swtable.hxx> +#include <fldbas.hxx> +#include <fmtclds.hxx> +#include <docsh.hxx> +#include "wrthtml.hxx" +#include "htmlnum.hxx" +#include "htmlfly.hxx" +#include <swmodule.hxx> +#include <strings.hrc> +#include <swerror.h> +#include <rtl/strbuf.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <xmloff/odffields.hxx> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> +#include <comphelper/scopeguard.hxx> +#include <unotools/tempfile.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#define MAX_INDENT_LEVEL 20 + +using namespace css; + +static char sIndentTabs[MAX_INDENT_LEVEL+2] = + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions ) + : m_pNumRuleInfo(new SwHTMLNumRuleInfo) + , m_nHTMLMode(0) + , m_eCSS1Unit(FieldUnit::NONE) + , mxFormComps() + , m_pStartNdIdx(nullptr) + , m_pCurrPageDesc(nullptr) + , m_pFormatFootnote(nullptr) + , m_nWarn(0) + , m_nLastLFPos(0) + , m_nLastParaToken(HtmlTokenId::NONE) + , m_nBkmkTabPos(-1) + , m_nImgMapCnt(1) + , m_nFormCntrlCnt(0) + , m_nEndNote(0) + , m_nFootNote(0) + , m_nLeftMargin(0) + , m_nDfltLeftMargin(0) + , m_nDfltRightMargin(0) + , m_nFirstLineIndent(0) + , m_nDfltFirstLineIndent(0) + , m_nDfltTopMargin(0) + , m_nDfltBottomMargin(0) + , m_nIndentLvl(0) + , m_nWhishLineLen(0) + , m_nDefListLvl(0) + , m_nDefListMargin(0) + , m_nHeaderFooterSpace(0) + , m_nTextAttrsToIgnore(0) + , m_nExportMode(0) + , m_nCSS1OutMode(0) + , m_nCSS1Script(CSS1_OUTMODE_WESTERN) + , m_nDirection(SvxFrameDirection::Horizontal_LR_TB) + , m_eDestEnc(RTL_TEXTENCODING_MS_1252) + , m_eLang(LANGUAGE_DONTKNOW) + , m_bCfgOutStyles( false ) + , m_bCfgPreferStyles( false ) + , m_bCfgFormFeed( false ) + , m_bCfgStarBasic( false ) + , m_bCfgCpyLinkedGrfs( false ) + , m_bFirstLine(true) + , m_bTagOn( false ) + , m_bTextAttr( false ) + , m_bOutOpts( false ) + , m_bOutTable( false ) + , m_bOutHeader( false ) + , m_bOutFooter( false ) + , m_bOutFlyFrame( false ) + , m_bFirstCSS1Rule( false ) + , m_bFirstCSS1Property( false ) + , m_bCSS1IgnoreFirstPageDesc( false ) + , m_bNoAlign( false ) + , m_bClearLeft( false ) + , m_bClearRight( false ) + , m_bLFPossible( false ) + , m_bPreserveForm( false ) + , m_bCfgNetscape4( false ) + , mbSkipImages(false) + , mbSkipHeaderFooter(false) + , mbEmbedImages(false) + , m_bCfgPrintLayout( false ) + , m_bParaDotLeaders( false ) +{ + SetBaseURL(rBaseURL); + + if (rBaseURL.isEmpty()) + { + // Paste: set base URL to a tempfile, so images are not lost. + mpTempBaseURL.reset(new utl::TempFile()); + mpTempBaseURL->EnableKillingFile(); + SetBaseURL(mpTempBaseURL->GetURL()); + } + + SetupFilterOptions(rFilterOptions); + + if (mbXHTML) + { + m_bNoAlign = true; + } +} + +SwHTMLWriter::~SwHTMLWriter() +{ +} + +std::unique_ptr<SwHTMLNumRuleInfo> SwHTMLWriter::ReleaseNextNumInfo() +{ + return std::move(m_pNextNumRuleInfo); +} + +void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium) +{ + const SfxItemSet* pSet = rMedium.GetItemSet(); + if (pSet == nullptr) + return; + + const SfxPoolItem* pItem; + if (pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) != SfxItemState::SET) + return; + + + const OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue(); + SetupFilterOptions(sFilterOptions); + + comphelper::SequenceAsHashMap aStoreMap(rMedium.GetArgs()); + auto it = aStoreMap.find("RTFOLEMimeType"); + if (it == aStoreMap.end()) + { + return; + } + + it->second >>= m_aRTFOLEMimeType; +} + +void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions) +{ + if (rFilterOptions == "SkipImages") + { + mbSkipImages = true; + } + else if (rFilterOptions == "SkipHeaderFooter") + { + mbSkipHeaderFooter = true; + } + else if (rFilterOptions == "EmbedImages") + { + mbEmbedImages = true; + } + + const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(rFilterOptions); + const OUString aXhtmlNsKey("xhtmlns="); + for (const auto& rOption : aOptionSeq) + { + if (rOption == "XHTML") + mbXHTML = true; + else if (rOption.startsWith(aXhtmlNsKey)) + { + maNamespace = rOption.copy(aXhtmlNsKey.getLength()).toUtf8(); + if (maNamespace == "reqif-xhtml") + { + mbReqIF = true; + // XHTML is always just a fragment inside ReqIF. + mbSkipHeaderFooter = true; + } + // XHTML namespace implies XHTML. + mbXHTML = true; + } + } +} + +ErrCode SwHTMLWriter::WriteStream() +{ + // Intercept paste output if requested. + char* pPasteEnv = getenv("SW_DEBUG_HTML_PASTE_TO"); + std::unique_ptr<SvStream> pPasteStream; + SvStream* pOldPasteStream = nullptr; + if (pPasteEnv) + { + OUString aPasteStr; + if (osl::FileBase::getFileURLFromSystemPath(OUString::fromUtf8(pPasteEnv), aPasteStr) + == osl::FileBase::E_None) + { + pPasteStream.reset(new SvFileStream(aPasteStr, StreamMode::WRITE)); + pOldPasteStream = &Strm(); + SetStream(pPasteStream.get()); + } + } + comphelper::ScopeGuard g([this, pOldPasteStream] { this->SetStream(pOldPasteStream); }); + + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + + // font heights 1-7 + m_aFontHeights[0] = rHtmlOptions.GetFontSize( 0 ) * 20; + m_aFontHeights[1] = rHtmlOptions.GetFontSize( 1 ) * 20; + m_aFontHeights[2] = rHtmlOptions.GetFontSize( 2 ) * 20; + m_aFontHeights[3] = rHtmlOptions.GetFontSize( 3 ) * 20; + m_aFontHeights[4] = rHtmlOptions.GetFontSize( 4 ) * 20; + m_aFontHeights[5] = rHtmlOptions.GetFontSize( 5 ) * 20; + m_aFontHeights[6] = rHtmlOptions.GetFontSize( 6 ) * 20; + + // output styles anyway + // (then also top and bottom paragraph spacing) + m_nExportMode = rHtmlOptions.GetExportMode(); + m_nHTMLMode = GetHtmlMode(nullptr); + + if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode ) + m_nHTMLMode |= HTMLMODE_BLOCK_SPACER; + + if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_MSIE == m_nExportMode ) + m_nHTMLMode |= (HTMLMODE_FLOAT_FRAME | HTMLMODE_LSPACE_IN_NUMBER_BULLET); + + if( HTML_CFG_MSIE == m_nExportMode ) + m_nHTMLMode |= HTMLMODE_NBSP_IN_TABLES; + + if( HTML_CFG_WRITER == m_nExportMode || HTML_CFG_NS40 == m_nExportMode || HTML_CFG_MSIE == m_nExportMode ) + m_nHTMLMode |= HTMLMODE_ABS_POS_FLY | HTMLMODE_ABS_POS_DRAW; + + if( HTML_CFG_WRITER == m_nExportMode ) + m_nHTMLMode |= HTMLMODE_FLY_MARGINS; + + if( HTML_CFG_NS40 == m_nExportMode ) + m_nHTMLMode |= HTMLMODE_BORDER_NONE; + + m_nHTMLMode |= HTMLMODE_FONT_GENERIC; + + if( HTML_CFG_NS40==m_nExportMode ) + m_nHTMLMode |= HTMLMODE_NO_CONTROL_CENTERING; + + m_bCfgOutStyles = IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES); + m_bCfgNetscape4 = (HTML_CFG_NS40 == m_nExportMode); + + if( IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES) ) + m_nHTMLMode |= HTMLMODE_PRINT_EXT; + + m_eCSS1Unit = SW_MOD()->GetMetric( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ); + + bool bWriteUTF8 = m_bWriteClipboardDoc; + m_eDestEnc = bWriteUTF8 ? RTL_TEXTENCODING_UTF8 : rHtmlOptions.GetTextEncoding(); + const char *pCharSet = rtl_getBestMimeCharsetFromTextEncoding( m_eDestEnc ); + m_eDestEnc = rtl_getTextEncodingFromMimeCharset( pCharSet ); + + // Only for the MS-IE we favour the export of styles. + m_bCfgPreferStyles = HTML_CFG_MSIE == m_nExportMode; + + m_bCfgStarBasic = rHtmlOptions.IsStarBasic(); + + m_bCfgFormFeed = !IsHTMLMode(HTMLMODE_PRINT_EXT); + m_bCfgCpyLinkedGrfs = rHtmlOptions.IsSaveGraphicsLocal(); + + m_bCfgPrintLayout = rHtmlOptions.IsPrintLayoutExtension(); + + // get HTML template + bool bOldHTMLMode = false; + SwTextFormatColls::size_type nOldTextFormatCollCnt = 0; + SwCharFormats::size_type nOldCharFormatCnt = 0; + + OSL_ENSURE( !m_xTemplate.is(), "Where is the HTML template coming from?" ); + m_xTemplate = static_cast<HTMLReader*>(ReadHTML)->GetTemplateDoc(*m_pDoc); + if( m_xTemplate.is() ) + { + bOldHTMLMode = m_xTemplate->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE); + m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true); + + nOldTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size(); + nOldCharFormatCnt = m_xTemplate->GetCharFormats()->size(); + } + + if( m_bShowProgress ) + ::StartProgress( STR_STATSTR_W4WWRITE, 0, m_pDoc->GetNodes().Count(), + m_pDoc->GetDocShell()); + + m_xDfltColor.reset(); + m_pFootEndNotes = nullptr; + m_pFormatFootnote = nullptr; + m_bOutTable = m_bOutHeader = m_bOutFooter = m_bOutFlyFrame = false; + mxFormComps.clear(); + m_nFormCntrlCnt = 0; + m_bPreserveForm = false; + m_bClearLeft = m_bClearRight = false; + m_bLFPossible = false; + + m_nLeftMargin = m_nDfltLeftMargin = m_nDfltRightMargin = 0; + m_nDfltTopMargin = m_nDfltBottomMargin = 0; + m_nFirstLineIndent = m_nDfltFirstLineIndent = 0; + m_bFirstCSS1Property = m_bFirstCSS1Rule = false; + m_bCSS1IgnoreFirstPageDesc = false; + m_nIndentLvl = 0; + m_nWhishLineLen = 70; + m_nLastLFPos = 0; + m_nDefListLvl = 0; + m_nDefListMargin = ((m_xTemplate.is() && !m_bCfgOutStyles) ? m_xTemplate.get() : m_pDoc) + ->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_HTML_DD, false ) + ->GetLRSpace().GetTextLeft(); + m_nHeaderFooterSpace = 0; + m_nTextAttrsToIgnore = 0; + m_nCSS1OutMode = 0; + SvtScriptType nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( GetAppLanguage() ); + switch( nScript ) + { + case SvtScriptType::ASIAN: + m_nCSS1Script = CSS1_OUTMODE_CJK; + break; + case SvtScriptType::COMPLEX: + m_nCSS1Script = CSS1_OUTMODE_CTL; + break; + default: + m_nCSS1Script = CSS1_OUTMODE_WESTERN; + break; + } + m_eLang = static_cast<const SvxLanguageItem&>(m_pDoc + ->GetDefault(GetLangWhichIdFromScript(m_nCSS1Script))).GetLanguage(); + + m_nFootNote = m_nEndNote = 0; + + m_nWarn = ERRCODE_NONE; + GetNumInfo().Clear(); + m_pNextNumRuleInfo = nullptr; + + OString aStartTags; + + // respect table and section at document beginning + { + SwTableNode * pTNd = m_pCurrentPam->GetNode().FindTableNode(); + if( pTNd && m_bWriteAll ) + { + // start with table node !! + m_pCurrentPam->GetPoint()->nNode = *pTNd; + + if( m_bWriteOnlyFirstTable ) + m_pCurrentPam->GetMark()->nNode = *pTNd->EndOfSectionNode(); + } + + // first node (with can contain a page break) + m_pStartNdIdx = new SwNodeIndex( m_pCurrentPam->GetPoint()->nNode ); + + SwSectionNode * pSNd = m_pCurrentPam->GetNode().FindSectionNode(); + while( pSNd ) + { + if( m_bWriteAll ) + { + // start with section node !! + m_pCurrentPam->GetPoint()->nNode = *pSNd; + } + else + { + OSL_ENSURE( SectionType::FileLink != pSNd->GetSection().GetType(), + "Export linked areas at document beginning is not implemented" ); + + // save only the tag of section + OString aName = HTMLOutFuncs::ConvertStringToHTML( + pSNd->GetSection().GetSectionName(), m_eDestEnc, + &m_aNonConvertableCharacters ); + + aStartTags = + "<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division + " " OOO_STRING_SVTOOLS_HTML_O_id + "=\"" + aName + "\">" + + aStartTags; + } + // FindSectionNode() on a SectionNode return the same! + pSNd = pSNd->StartOfSectionNode()->FindSectionNode(); + } + } + + // create table of the floating frames, but only when the whole + // document is saved + m_pHTMLPosFlyFrames = nullptr; + CollectFlyFrames(); + m_nLastParaToken = HtmlTokenId::NONE; + GetControls(); + CollectLinkTargets(); + + sal_uInt16 nHeaderAttrs = 0; + m_pCurrPageDesc = MakeHeader( nHeaderAttrs ); + + m_bLFPossible = true; + + // output forms which contain only HiddenControls + OutHiddenForms(); + + if( !aStartTags.isEmpty() ) + Strm().WriteOString( aStartTags ); + + const SfxPoolItem *pItem; + const SfxItemSet& rPageItemSet = m_pCurrPageDesc->GetMaster().GetAttrSet(); + if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() && + (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && + !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) && + SfxItemState::SET == rPageItemSet.GetItemState( RES_HEADER, true, &pItem) ) + { + const SwFrameFormat *pHeaderFormat = + static_cast<const SwFormatHeader *>(pItem)->GetHeaderFormat(); + if( pHeaderFormat ) + OutHTML_HeaderFooter( *this, *pHeaderFormat, true ); + } + + m_nTextAttrsToIgnore = nHeaderAttrs; + Out_SwDoc( m_pOrigPam ); + m_nTextAttrsToIgnore = 0; + + if( mxFormComps.is() ) + OutForm( false, mxFormComps ); + + if( m_pFootEndNotes ) + OutFootEndNotes(); + + if( !m_bWriteClipboardDoc && m_pDoc->GetDocShell() && + (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) && + SfxItemState::SET == rPageItemSet.GetItemState( RES_FOOTER, true, &pItem) ) + { + const SwFrameFormat *pFooterFormat = + static_cast<const SwFormatFooter *>(pItem)->GetFooterFormat(); + if( pFooterFormat ) + OutHTML_HeaderFooter( *this, *pFooterFormat, false ); + } + + if( m_bLFPossible ) + OutNewLine(); + if (!mbSkipHeaderFooter) + { + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_body, false ); + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_html, false ); + } + else if (mbReqIF) + // ReqIF: end xhtml.BlkStruct.class. + HTMLOutFuncs::Out_AsciiTag(Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false); + + // delete the table with floating frames + OSL_ENSURE( !m_pHTMLPosFlyFrames, "Were not all frames output?" ); + m_pHTMLPosFlyFrames.reset(); + + m_aHTMLControls.clear(); + + m_CharFormatInfos.clear(); + m_TextCollInfos.clear(); + m_aImgMapNames.clear(); + m_aImplicitMarks.clear(); + m_aOutlineMarks.clear(); + m_aOutlineMarkPoss.clear(); + m_aNumRuleNames.clear(); + m_aScriptParaStyles.clear(); + m_aScriptTextStyles.clear(); + + m_xDfltColor.reset(); + + delete m_pStartNdIdx; + m_pStartNdIdx = nullptr; + + mxFormComps.clear(); + + OSL_ENSURE( !m_pFootEndNotes, + "SwHTMLWriter::Write: Footnotes not deleted by OutFootEndNotes" ); + + m_pCurrPageDesc = nullptr; + + ClearNextNumInfo(); + + for(OUString & s : m_aBulletGrfs) + s.clear(); + + m_aNonConvertableCharacters.clear(); + + if( m_bShowProgress ) + ::EndProgress( m_pDoc->GetDocShell() ); + + if( m_xTemplate.is() ) + { + // delete character and paragraph templates created during export + auto nTextFormatCollCnt = m_xTemplate->GetTextFormatColls()->size(); + while( nTextFormatCollCnt > nOldTextFormatCollCnt ) + m_xTemplate->DelTextFormatColl( --nTextFormatCollCnt ); + OSL_ENSURE( m_xTemplate->GetTextFormatColls()->size() == nOldTextFormatCollCnt, + "wrong number of TextFormatColls deleted" ); + + auto nCharFormatCnt = m_xTemplate->GetCharFormats()->size(); + while( nCharFormatCnt > nOldCharFormatCnt ) + m_xTemplate->DelCharFormat( --nCharFormatCnt ); + OSL_ENSURE( m_xTemplate->GetCharFormats()->size() == nOldCharFormatCnt, + "wrong number of CharFormats deleted" ); + + // restore HTML mode + m_xTemplate->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, bOldHTMLMode); + + m_xTemplate.clear(); + } + + return m_nWarn; +} + +static const SwFormatCol *lcl_html_GetFormatCol( const SwSection& rSection, + const SwSectionFormat& rFormat ) +{ + const SwFormatCol *pCol = nullptr; + + const SfxPoolItem* pItem; + if( SectionType::FileLink != rSection.GetType() && + SfxItemState::SET == rFormat.GetAttrSet().GetItemState(RES_COL,false,&pItem) && + static_cast<const SwFormatCol *>(pItem)->GetNumCols() > 1 ) + { + pCol = static_cast<const SwFormatCol *>(pItem); + } + + return pCol; +} + +static bool lcl_html_IsMultiColStart( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex ) +{ + bool bRet = false; + const SwSectionNode *pSectNd = + rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetSectionNode(); + if( pSectNd ) + { + const SwSection& rSection = pSectNd->GetSection(); + const SwSectionFormat *pFormat = rSection.GetFormat(); + if( pFormat && lcl_html_GetFormatCol( rSection, *pFormat ) ) + bRet = true; + } + + return bRet; +} + +static bool lcl_html_IsMultiColEnd( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex ) +{ + bool bRet = false; + const SwEndNode *pEndNd = rHTMLWrt.m_pDoc->GetNodes()[nIndex]->GetEndNode(); + if( pEndNd ) + bRet = lcl_html_IsMultiColStart( rHTMLWrt, + pEndNd->StartOfSectionIndex() ); + + return bRet; +} + +static void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt, + const SwSection& rSection, + const SwSectionFormat& rFormat, + const SwFormatCol *pCol, + bool bContinued=false ) +{ + OSL_ENSURE( pCol || !bContinued, "Continuation of DIV" ); + + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + + OStringBuffer sOut; + sOut.append('<').append(OOO_STRING_SVTOOLS_HTML_division); + + const OUString& rName = rSection.GetSectionName(); + if( !rName.isEmpty() && !bContinued ) + { + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_id "=\""); + rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), rName, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + sOut.append('\"'); + } + + SvxFrameDirection nDir = rHTMLWrt.GetHTMLDirection( rFormat.GetAttrSet() ); + rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + rHTMLWrt.OutDirection( nDir ); + + if( SectionType::FileLink == rSection.GetType() ) + { + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_href "=\""); + rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + + const OUString& aFName = rSection.GetLinkFileName(); + sal_Int32 nIdx{ 0 }; + OUString aURL( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) ); + OUString aFilter( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) ); + OUString aSection( aFName.getToken(0, sfx2::cTokenSeparator, nIdx) ); + + OUString aEncURL( URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aURL ) ); + sal_Unicode cDelim = 255U; + bool bURLContainsDelim = (-1 != aEncURL.indexOf( cDelim ) ); + + HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aEncURL, + rHTMLWrt.m_eDestEnc, + &rHTMLWrt.m_aNonConvertableCharacters ); + const char* const pDelim = "ÿ"; + if( !aFilter.isEmpty() || !aSection.isEmpty() || bURLContainsDelim ) + rHTMLWrt.Strm().WriteCharPtr( pDelim ); + if( !aFilter.isEmpty() ) + HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aFilter, rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + if( !aSection.isEmpty() || bURLContainsDelim ) + rHTMLWrt.Strm().WriteCharPtr( pDelim ); + if( !aSection.isEmpty() ) + { + sal_Int32 nPos = aSection.indexOf( '%' ); + while( nPos != -1 ) + { + aSection = aSection.replaceAt(nPos, 1, "%25"); + nPos = aSection.indexOf( '%', nPos+3 ); + } + nPos = aSection.indexOf( cDelim ); + while( nPos != -1 ) + { + aSection = aSection.replaceAt(nPos, 1, "%FF" ); + nPos = aSection.indexOf( cDelim, nPos+3 ); + } + HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aSection, + rHTMLWrt.m_eDestEnc, &rHTMLWrt.m_aNonConvertableCharacters ); + } + sOut.append('\"'); + } + else if( pCol ) + { + // minimum gutter width + sal_uInt16 nGutter = pCol->GetGutterWidth( true ); + if( nGutter!=USHRT_MAX ) + { + if( nGutter && Application::GetDefaultDevice() ) + { + nGutter = static_cast<sal_uInt16>(Application::GetDefaultDevice() + ->LogicToPixel( Size(nGutter, 0), MapMode(MapUnit::MapTwip) ).Width()); + } + sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter "=\"" + OString::number(nGutter) + "\""); + } + } + + rHTMLWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + if( rHTMLWrt.IsHTMLMode( rHTMLWrt.m_bCfgOutStyles ? HTMLMODE_ON : 0 ) ) + rHTMLWrt.OutCSS1_SectionFormatOptions( rFormat, pCol ); + + rHTMLWrt.Strm().WriteChar( '>' ); + + rHTMLWrt.m_bLFPossible = true; + if( !rName.isEmpty() && !bContinued ) + rHTMLWrt.OutImplicitMark( rName, "region" ); + + rHTMLWrt.IncIndentLevel(); +} + +static void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt ) +{ + rHTMLWrt.DecIndentLevel(); + if( rHTMLWrt.m_bLFPossible ) + rHTMLWrt.OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division, false ); + rHTMLWrt.m_bLFPossible = true; +} + +static Writer& OutHTML_Section( Writer& rWrt, const SwSectionNode& rSectNd ) +{ + SwHTMLWriter& rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt); + + // End <PRE> and any <DL>, because a definition list's level may + // change inside the section. + rHTMLWrt.ChangeParaToken( HtmlTokenId::NONE ); + rHTMLWrt.OutAndSetDefList( 0 ); + + const SwSection& rSection = rSectNd.GetSection(); + const SwSectionFormat *pFormat = rSection.GetFormat(); + OSL_ENSURE( pFormat, "Section without a format?" ); + + bool bStartTag = true; + bool bEndTag = true; + const SwSectionFormat *pSurrFormat = nullptr; + const SwSectionNode *pSurrSectNd = nullptr; + const SwSection *pSurrSection = nullptr; + const SwFormatCol *pSurrCol = nullptr; + + sal_uInt32 nSectSttIdx = rSectNd.GetIndex(); + sal_uInt32 nSectEndIdx = rSectNd.EndOfSectionIndex(); + const SwFormatCol *pCol = lcl_html_GetFormatCol( rSection, *pFormat ); + if( pCol ) + { + // If the next node is a columned section node, too, don't export + // an empty section. + if( lcl_html_IsMultiColStart( rHTMLWrt, nSectSttIdx+1 ) ) + bStartTag = false; + + // The same applies if the section end with another columned section. + if( lcl_html_IsMultiColEnd( rHTMLWrt, nSectEndIdx-1 ) ) + bEndTag = false; + + // is there a columned section around this one? + const SwStartNode *pSttNd = rSectNd.StartOfSectionNode(); + if( pSttNd ) + { + pSurrSectNd = pSttNd->FindSectionNode(); + if( pSurrSectNd ) + { + const SwStartNode *pBoxSttNd = pSttNd->FindTableBoxStartNode(); + if( !pBoxSttNd || + pBoxSttNd->GetIndex() < pSurrSectNd->GetIndex() ) + { + pSurrSection = &pSurrSectNd->GetSection(); + pSurrFormat = pSurrSection->GetFormat(); + if( pSurrFormat ) + pSurrCol = lcl_html_GetFormatCol( *pSurrSection, + *pSurrFormat ); + } + } + } + } + + // The surrounding section must be closed before the current one is + // opened, except that it start immediately before the current one or + // another end immediately before the current one + if( pSurrCol && nSectSttIdx - pSurrSectNd->GetIndex() > 1 && + !lcl_html_IsMultiColEnd( rHTMLWrt, nSectSttIdx-1 ) ) + lcl_html_OutSectionEndTag( rHTMLWrt ); + + if( bStartTag ) + lcl_html_OutSectionStartTag( rHTMLWrt, rSection, *pFormat, pCol ); + + { + HTMLSaveData aSaveData( rHTMLWrt, + rHTMLWrt.m_pCurrentPam->GetPoint()->nNode.GetIndex()+1, + rSectNd.EndOfSectionIndex(), + false, pFormat ); + rHTMLWrt.Out_SwDoc( rHTMLWrt.m_pCurrentPam.get() ); + } + + rHTMLWrt.m_pCurrentPam->GetPoint()->nNode = *rSectNd.EndOfSectionNode(); + + if( bEndTag ) + lcl_html_OutSectionEndTag( rHTMLWrt ); + + // The surrounding section must be started again, except that it ends + // immediately behind the current one. + if( pSurrCol && + pSurrSectNd->EndOfSectionIndex() - nSectEndIdx > 1 && + !lcl_html_IsMultiColStart( rHTMLWrt, nSectEndIdx+1 ) ) + lcl_html_OutSectionStartTag( rHTMLWrt, *pSurrSection, *pSurrFormat, + pSurrCol, true ); + + return rWrt; +} + +void SwHTMLWriter::Out_SwDoc( SwPaM* pPam ) +{ + bool bSaveWriteAll = m_bWriteAll; // save + + // search next text::Bookmark position from text::Bookmark table + m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1; + + // output all areas of PaM's in the HTML file + do { + m_bWriteAll = bSaveWriteAll; + m_bFirstLine = true; + + // search for first on PaM created FlyFrame + // still missing: + + while( m_pCurrentPam->GetPoint()->nNode.GetIndex() < m_pCurrentPam->GetMark()->nNode.GetIndex() || + (m_pCurrentPam->GetPoint()->nNode.GetIndex() == m_pCurrentPam->GetMark()->nNode.GetIndex() && + m_pCurrentPam->GetPoint()->nContent.GetIndex() <= m_pCurrentPam->GetMark()->nContent.GetIndex()) ) + { + SwNode& rNd = m_pCurrentPam->GetNode(); + + OSL_ENSURE( !(rNd.IsGrfNode() || rNd.IsOLENode()), + "Unexpected Grf- or OLE-Node here" ); + if( rNd.IsTextNode() ) + { + SwTextNode* pTextNd = rNd.GetTextNode(); + + if( !m_bFirstLine ) + m_pCurrentPam->GetPoint()->nContent.Assign( pTextNd, 0 ); + + OutHTML_SwTextNode( *this, *pTextNd ); + } + else if( rNd.IsTableNode() ) + { + OutHTML_SwTableNode( *this, *rNd.GetTableNode(), nullptr ); + m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1; + } + else if( rNd.IsSectionNode() ) + { + OutHTML_Section( *this, *rNd.GetSectionNode() ); + m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1; + } + else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() ) + break; + + ++m_pCurrentPam->GetPoint()->nNode; // move + sal_uInt32 nPos = m_pCurrentPam->GetPoint()->nNode.GetIndex(); + + if( m_bShowProgress ) + ::SetProgressState( nPos, m_pDoc->GetDocShell() ); // How far ? + + /* If only the selected area should be saved, so only the complete + * nodes should be saved, this means the first and n-th node + * partly, the 2nd till n-1 node complete. (complete means with + * all formats!) + */ + m_bWriteAll = bSaveWriteAll || + nPos != m_pCurrentPam->GetMark()->nNode.GetIndex(); + m_bFirstLine = false; + m_bOutFooter = false; // after one node no footer anymore + } + + ChangeParaToken( HtmlTokenId::NONE ); // MIB 8.7.97: We're doing it here and not at the caller + OutAndSetDefList( 0 ); + + } while( CopyNextPam( &pPam ) ); // until all PaM's processed + + m_bWriteAll = bSaveWriteAll; // reset to old values +} + +// write the StyleTable, general data, header/footer/footnotes +static void OutBodyColor( const char* pTag, const SwFormat *pFormat, + SwHTMLWriter& rHWrt ) +{ + const SwFormat *pRefFormat = nullptr; + + if( rHWrt.m_xTemplate.is() ) + pRefFormat = SwHTMLWriter::GetTemplateFormat( pFormat->GetPoolFormatId(), + &rHWrt.m_xTemplate->getIDocumentStylePoolAccess() ); + + const SvxColorItem *pColorItem = nullptr; + + const SfxItemSet& rItemSet = pFormat->GetAttrSet(); + const SfxPoolItem *pRefItem = nullptr, *pItem = nullptr; + bool bItemSet = SfxItemState::SET == rItemSet.GetItemState( RES_CHRATR_COLOR, + true, &pItem); + bool bRefItemSet = pRefFormat && + SfxItemState::SET == pRefFormat->GetAttrSet().GetItemState( RES_CHRATR_COLOR, + true, &pRefItem); + if( bItemSet ) + { + // only when the item is set in the template of the current document + // or has a different value as the in HTML template, it will be set + const SvxColorItem *pCItem = static_cast<const SvxColorItem*>(pItem); + + if( !bRefItemSet ) + { + pColorItem = pCItem; + } + else + { + Color aColor( pCItem->GetValue() ); + if( COL_AUTO == aColor ) + aColor = COL_BLACK; + + Color aRefColor( static_cast<const SvxColorItem*>(pRefItem)->GetValue() ); + if( COL_AUTO == aRefColor ) + aRefColor = COL_BLACK; + + if( !aColor.IsRGBEqual( aRefColor ) ) + pColorItem = pCItem; + } + } + else if( bRefItemSet ) + { + // The item was still set in the HTML template so we output the default + pColorItem = &rItemSet.GetPool()->GetDefaultItem( RES_CHRATR_COLOR ); + } + + if( pColorItem ) + { + OStringBuffer sOut; + sOut.append(OStringLiteral(" ") + pTag + "="); + rHWrt.Strm().WriteOString( sOut.makeStringAndClear() ); + Color aColor( pColorItem->GetValue() ); + if( COL_AUTO == aColor ) + aColor = COL_BLACK; + HTMLOutFuncs::Out_Color( rHWrt.Strm(), aColor ); + if( RES_POOLCOLL_STANDARD==pFormat->GetPoolFormatId() ) + rHWrt.m_xDfltColor = aColor; + } +} + +sal_uInt16 SwHTMLWriter::OutHeaderAttrs() +{ + sal_uLong nIdx = m_pCurrentPam->GetPoint()->nNode.GetIndex(); + sal_uLong nEndIdx = m_pCurrentPam->GetMark()->nNode.GetIndex(); + + SwTextNode *pTextNd = nullptr; + while( nIdx<=nEndIdx && + nullptr==(pTextNd=m_pDoc->GetNodes()[nIdx]->GetTextNode()) ) + nIdx++; + + OSL_ENSURE( pTextNd, "No Text-Node found" ); + if( !pTextNd || !pTextNd->HasHints() ) + return 0; + + sal_uInt16 nAttrs = 0; + const size_t nCntAttr = pTextNd->GetSwpHints().Count(); + sal_Int32 nOldPos = 0; + for( size_t i=0; i<nCntAttr; ++i ) + { + const SwTextAttr *pHt = pTextNd->GetSwpHints().Get(i); + if( !pHt->End() ) + { + sal_Int32 nPos = pHt->GetStart(); + if( nPos-nOldPos > 1 + || ( pHt->Which() != RES_TXTATR_FIELD + && pHt->Which() != RES_TXTATR_ANNOTATION ) ) + break; + + const SwFieldIds nFieldWhich = + static_cast<const SwFormatField&>(pHt->GetAttr()).GetField()->GetTyp()->Which(); + if( SwFieldIds::Postit!=nFieldWhich && + SwFieldIds::Script!=nFieldWhich ) + break; + + OutNewLine(); + OutHTML_SwFormatField( *this, pHt->GetAttr() ); + nOldPos = nPos; + OSL_ENSURE( nAttrs<SAL_MAX_UINT16, "Too many attributes" ); + nAttrs++; + } + } + + return nAttrs; +} + +const SwPageDesc *SwHTMLWriter::MakeHeader( sal_uInt16 &rHeaderAttrs ) +{ + OStringBuffer sOut; + if (!mbSkipHeaderFooter) + { + if (mbXHTML) + sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_XHTML_doctype11); + else + sOut.append(OOO_STRING_SVTOOLS_HTML_doctype " " OOO_STRING_SVTOOLS_HTML_doctype40); + HTMLOutFuncs::Out_AsciiTag( Strm(), sOut.makeStringAndClear().getStr() ); // No GetNamespace() here. + + // build prelude + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_html ); + + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_head ); + + IncIndentLevel(); // indent content of <HEAD> + + // DocumentInfo + OString sIndent = GetIndentString(); + + uno::Reference<document::XDocumentProperties> xDocProps; + SwDocShell *pDocShell(m_pDoc->GetDocShell()); + if (pDocShell) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + xDocProps.set(xDPS->getDocumentProperties()); + } + + // xDocProps may be null here (when copying) + SfxFrameHTMLWriter::Out_DocInfo( Strm(), GetBaseURL(), xDocProps, + sIndent.getStr(), m_eDestEnc, + &m_aNonConvertableCharacters ); + + // comments and meta-tags of first paragraph + rHeaderAttrs = OutHeaderAttrs(); + + OutFootEndNoteInfo(); + } + + const SwPageDesc *pPageDesc = nullptr; + + // In none HTML documents the first set template will be exported + // and if none is set the default template + sal_uLong nNodeIdx = m_pCurrentPam->GetPoint()->nNode.GetIndex(); + + while( nNodeIdx < m_pDoc->GetNodes().Count() ) + { + SwNode *pNd = m_pDoc->GetNodes()[ nNodeIdx ]; + if( pNd->IsContentNode() ) + { + pPageDesc = static_cast<const SwFormatPageDesc &>(pNd->GetContentNode() + ->GetAttr(RES_PAGEDESC)).GetPageDesc(); + break; + } + else if( pNd->IsTableNode() ) + { + pPageDesc = pNd->GetTableNode()->GetTable().GetFrameFormat() + ->GetPageDesc().GetPageDesc(); + break; + } + + nNodeIdx++; + } + + if( !pPageDesc ) + pPageDesc = &m_pDoc->GetPageDesc( 0 ); + + if (!mbSkipHeaderFooter) + { + // and now ... the style sheet!!! + if( m_bCfgOutStyles ) + { + OutStyleSheet( *pPageDesc ); + } + + // and now ... the BASIC and JavaScript! + if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell + OutBasic(*this); + + DecIndentLevel(); // indent content of <HEAD> + OutNewLine(); + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_head, false ); + + // the body won't be indented, because then everything would be indented! + OutNewLine(); + sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_body); + Strm().WriteOString( sOut.makeStringAndClear() ); + + // language + OutLanguage( m_eLang ); + + // output text colour, when it was set in the default template or was changed + OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_text, + m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ), + *this ); + + // colour of (un)visited links + OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link, + m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL ), + *this ); + OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink, + m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_INET_VISIT ), + *this ); + + const SfxItemSet& rItemSet = pPageDesc->GetMaster().GetAttrSet(); + + // fdo#86857 page styles now contain the XATTR_*, not RES_BACKGROUND + std::unique_ptr<SvxBrushItem> const aBrushItem(getSvxBrushItemFromSourceSet(rItemSet, RES_BACKGROUND)); + OutBackground(aBrushItem.get(), true); + + m_nDirection = GetHTMLDirection( rItemSet ); + OutDirection( m_nDirection ); + + if( m_bCfgOutStyles ) + { + OutCSS1_BodyTagStyleOpt( *this, rItemSet ); + } + // append events + if( m_pDoc->GetDocShell() ) // BASIC is possible only in case we have a DocShell + OutBasicBodyEvents(); + + Strm().WriteChar( '>' ); + } + else if (mbReqIF) + // ReqIF: start xhtml.BlkStruct.class. + HTMLOutFuncs::Out_AsciiTag(Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_division); + + return pPageDesc; +} + +void SwHTMLWriter::OutAnchor( const OUString& rName ) +{ + OStringBuffer sOut; + sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " "); + if (!mbXHTML) + { + sOut.append(OOO_STRING_SVTOOLS_HTML_O_name "=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), rName, m_eDestEnc, &m_aNonConvertableCharacters ).WriteCharPtr( "\">" ); + } + else + { + // XHTML wants 'id' instead of 'name', also the value can't contain + // spaces. + sOut.append(OOO_STRING_SVTOOLS_HTML_O_id "=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), rName.replace(' ', '_'), m_eDestEnc, &m_aNonConvertableCharacters ).WriteCharPtr( "\">" ); + } + HTMLOutFuncs::Out_AsciiTag( Strm(), GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor, false ); +} + +void SwHTMLWriter::OutBookmarks() +{ + // fetch current bookmark + const ::sw::mark::IMark* pBookmark = nullptr; + IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + if(m_nBkmkTabPos != -1) + pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos]; + // Output all bookmarks in this paragraph. The content position + // for the moment isn't considered! + sal_uInt32 nNode = m_pCurrentPam->GetPoint()->nNode.GetIndex(); + while( m_nBkmkTabPos != -1 + && pBookmark->GetMarkPos().nNode.GetIndex() == nNode ) + { + // The area of bookmarks is first ignored, because it's not read. + + // first the SWG specific data: + if ( dynamic_cast< const ::sw::mark::IBookmark* >(pBookmark) && !pBookmark->GetName().isEmpty() ) + { + OutAnchor( pBookmark->GetName() ); + } + + if( ++m_nBkmkTabPos >= pMarkAccess->getAllMarksCount() ) + m_nBkmkTabPos = -1; + else + pBookmark = pMarkAccess->getAllMarksBegin()[m_nBkmkTabPos]; + } + + decltype(m_aOutlineMarkPoss)::size_type nPos; + for( nPos = 0; nPos < m_aOutlineMarkPoss.size() && + m_aOutlineMarkPoss[nPos] < nNode; nPos++ ) + ; + + while( nPos < m_aOutlineMarkPoss.size() && m_aOutlineMarkPoss[nPos] == nNode ) + { + OUString sMark( m_aOutlineMarks[nPos] ); + OutAnchor( sMark.replace('?', '_') ); // '?' causes problems in IE/Netscape 5 + m_aOutlineMarkPoss.erase( m_aOutlineMarkPoss.begin()+nPos ); + m_aOutlineMarks.erase( m_aOutlineMarks.begin() + nPos ); + } +} + +void SwHTMLWriter::OutPointFieldmarks( const SwPosition& rPos ) +{ + // "point" fieldmarks that occupy single character space, as opposed to + // range fieldmarks that are associated with start and end points. + + const IDocumentMarkAccess* pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + if (!pMarkAccess) + return; + + const sw::mark::IFieldmark* pMark = pMarkAccess->getFieldmarkAt(rPos); + if (!pMark) + return; + + if (pMark->GetFieldname() == ODF_FORMCHECKBOX) + { + const sw::mark::ICheckboxFieldmark* pCheckBox = + dynamic_cast<const sw::mark::ICheckboxFieldmark*>(pMark); + + if (pCheckBox) + { + OString aOut("<" + OOO_STRING_SVTOOLS_HTML_input + " " + OOO_STRING_SVTOOLS_HTML_O_type + "=\"" + OOO_STRING_SVTOOLS_HTML_IT_checkbox + "\""); + + if (pCheckBox->IsChecked()) + { + aOut += " " + OOO_STRING_SVTOOLS_HTML_O_checked + "=\"" + OOO_STRING_SVTOOLS_HTML_O_checked + "\""; + } + + aOut += "/>"; + Strm().WriteOString(aOut); + } + } + + // TODO : Handle other single-point fieldmark types here (if any). +} + +void SwHTMLWriter::OutImplicitMark( const OUString& rMark, + const char *pMarkType ) +{ + if( !rMark.isEmpty() && !m_aImplicitMarks.empty() ) + { + OUString sMark(rMark + OUStringChar(cMarkSeparator) + OUString::createFromAscii(pMarkType)); + if( 0 != m_aImplicitMarks.erase( sMark ) ) + { + OutAnchor(sMark.replace('?', '_')); // '?' causes problems in IE/Netscape 5 + } + } +} + +OUString SwHTMLWriter::convertHyperlinkHRefValue(const OUString& rURL) +{ + OUString sURL(rURL); + sal_Int32 nPos = sURL.lastIndexOf(cMarkSeparator); + if (nPos != -1) + { + OUString sCompare = sURL.copy(nPos + 1).replaceAll(" ", ""); + if (!sCompare.isEmpty()) + { + sCompare = sCompare.toAsciiLowerCase(); + if( sCompare == "region" || sCompare == "frame" || + sCompare == "graphic" || sCompare == "ole" || + sCompare == "table" || sCompare == "outline" || + sCompare == "text" ) + { + sURL = sURL.replace( '?', '_' ); // '?' causes problems in IE/Netscape 5 + } + } + } + else if (!sURL.isEmpty() && sURL[0] != '#') + { + // Link is not started from "#", so looks like external link. Encode this URL. + INetURLObject aURL(sURL); + sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + } + return URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), sURL ); +} + +void SwHTMLWriter::OutHyperlinkHRefValue( const OUString& rURL ) +{ + OUString sURL = convertHyperlinkHRefValue(rURL); + HTMLOutFuncs::Out_String( Strm(), sURL, m_eDestEnc, &m_aNonConvertableCharacters ); +} + +void SwHTMLWriter::OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic ) +{ + const Color &rBackColor = pBrushItem->GetColor(); + /// check, if background color is not "no fill"/"auto fill", instead of + /// only checking, if transparency is not set. + if( rBackColor != COL_TRANSPARENT ) + { + OString sOut = + " " OOO_STRING_SVTOOLS_HTML_O_bgcolor "="; + Strm().WriteOString( sOut ); + HTMLOutFuncs::Out_Color( Strm(), rBackColor); + } + + if( !bGraphic ) + return; + + OUString aGraphicInBase64; + const Graphic* pGrf = pBrushItem->GetGraphic(); + OUString GraphicURL = pBrushItem->GetGraphicLink(); + if( mbEmbedImages || GraphicURL.isEmpty()) + { + if( pGrf ) + { + if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) ) + { + m_nWarn = WARN_SWG_POOR_LOAD; + } + Strm().WriteCharPtr( " " OOO_STRING_SVTOOLS_HTML_O_background "=\"" ); + Strm().WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_data ":" ); + HTMLOutFuncs::Out_String( Strm(), aGraphicInBase64, m_eDestEnc, &m_aNonConvertableCharacters ).WriteChar( '\"' ); + } + } + else + { + if( m_bCfgCpyLinkedGrfs ) + { + CopyLocalFileToINet( GraphicURL ); + } + OUString s( URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), GraphicURL)); + Strm().WriteCharPtr(" " OOO_STRING_SVTOOLS_HTML_O_background "=\"" ); + HTMLOutFuncs::Out_String( Strm(), s, m_eDestEnc, &m_aNonConvertableCharacters ); + Strm().WriteCharPtr("\""); + + } +} + +void SwHTMLWriter::OutBackground( const SfxItemSet& rItemSet, bool bGraphic ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, + &pItem )) + { + OutBackground( static_cast<const SvxBrushItem*>(pItem), bGraphic ); + } +} + +sal_uInt16 SwHTMLWriter::GetLangWhichIdFromScript( sal_uInt16 nScript ) +{ + sal_uInt16 nWhichId; + switch( nScript ) + { + case CSS1_OUTMODE_CJK: + nWhichId = RES_CHRATR_CJK_LANGUAGE; + break; + case CSS1_OUTMODE_CTL: + nWhichId = RES_CHRATR_CJK_LANGUAGE; + break; + default: + nWhichId = RES_CHRATR_LANGUAGE; + break; + } + return nWhichId; +} + +void SwHTMLWriter::OutLanguage( LanguageType nLang ) +{ + // ReqIF mode: consumers would ignore language anyway. + if (LANGUAGE_DONTKNOW != nLang && !mbReqIF) + { + OStringBuffer sOut; + sOut.append(' '); + if (mbXHTML) + sOut.append(OOO_STRING_SVTOOLS_XHTML_O_lang); + else + sOut.append(OOO_STRING_SVTOOLS_HTML_O_lang); + sOut.append("=\""); + Strm().WriteOString( sOut.makeStringAndClear() ); + HTMLOutFuncs::Out_String( Strm(), LanguageTag::convertToBcp47(nLang), + m_eDestEnc, &m_aNonConvertableCharacters ).WriteChar( '"' ); + } +} + +SvxFrameDirection SwHTMLWriter::GetHTMLDirection( const SfxItemSet& rItemSet ) const +{ + return GetHTMLDirection( rItemSet.Get( RES_FRAMEDIR ).GetValue() ); +} + +SvxFrameDirection SwHTMLWriter::GetHTMLDirection( SvxFrameDirection nDir ) const +{ + switch( nDir ) + { + case SvxFrameDirection::Vertical_LR_TB: + nDir = SvxFrameDirection::Horizontal_LR_TB; + break; + case SvxFrameDirection::Vertical_RL_TB: + nDir = SvxFrameDirection::Horizontal_RL_TB; + break; + case SvxFrameDirection::Environment: + nDir = m_nDirection; + break; + default: break; + } + + return nDir; +} + +void SwHTMLWriter::OutDirection( SvxFrameDirection nDir ) +{ + OString sConverted = convertDirection(nDir); + if (!sConverted.isEmpty()) + { + OString sOut = + " " OOO_STRING_SVTOOLS_HTML_O_dir + "=\"" + sConverted + "\""; + Strm().WriteOString( sOut ); + } +} + +OString SwHTMLWriter::convertDirection(SvxFrameDirection nDir) +{ + OString sConverted; + switch (nDir) + { + case SvxFrameDirection::Horizontal_LR_TB: + case SvxFrameDirection::Vertical_LR_TB: + sConverted = "ltr"; + break; + case SvxFrameDirection::Horizontal_RL_TB: + case SvxFrameDirection::Vertical_RL_TB: + sConverted = "rtl"; + break; + default: break; + } + return sConverted; +} + +OString SwHTMLWriter::GetIndentString(sal_uInt16 nIncLvl) +{ + OString sRet; + + // somewhat cumbersome, but we have only one indent string! + sal_uInt16 nLevel = m_nIndentLvl + nIncLvl; + + if( nLevel && nLevel <= MAX_INDENT_LEVEL) + { + sIndentTabs[nLevel] = 0; + sRet = sIndentTabs; + sIndentTabs[nLevel] = '\t'; + } + + return sRet; +} + +void SwHTMLWriter::OutNewLine( bool bCheck ) +{ + if( !bCheck || (Strm().Tell()-m_nLastLFPos) > m_nIndentLvl ) + { + Strm().WriteCharPtr( SAL_NEWLINE_STRING ); + m_nLastLFPos = Strm().Tell(); + } + + if( m_nIndentLvl && m_nIndentLvl <= MAX_INDENT_LEVEL) + { + sIndentTabs[m_nIndentLvl] = 0; + Strm().WriteCharPtr( sIndentTabs ); + sIndentTabs[m_nIndentLvl] = '\t'; + } +} + +sal_uInt16 SwHTMLWriter::GetHTMLFontSize( sal_uInt32 nHeight ) const +{ + sal_uInt16 nSize = 1; + for( sal_uInt16 i=6; i>0; i-- ) + { + if( nHeight > (m_aFontHeights[i] + m_aFontHeights[i-1])/2 ) + { + nSize = i+1; + break; + } + } + + return nSize; +} + +// Paragraphs with Table of Contents and other index styles will be typeset with +// dot leaders at the position of the last tabulator in PrintLayout (CSS2) mode +sal_Int32 SwHTMLWriter::indexOfDotLeaders( sal_uInt16 nPoolId, const OUString& rStr ) +{ + if (m_bCfgPrintLayout && ((nPoolId >= RES_POOLCOLL_TOX_CNTNT1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT5) || + (nPoolId >= RES_POOLCOLL_TOX_IDX1 && nPoolId <= RES_POOLCOLL_TOX_IDX3) || + (nPoolId >= RES_POOLCOLL_TOX_USER1 && nPoolId <= RES_POOLCOLL_TOX_CNTNT10) || + nPoolId == RES_POOLCOLL_TOX_ILLUS1 || nPoolId == RES_POOLCOLL_TOX_TABLES1 || + nPoolId == RES_POOLCOLL_TOX_OBJECT1 || + (nPoolId >= RES_POOLCOLL_TOX_AUTHORITIES1 && nPoolId <= RES_POOLCOLL_TOX_USER10))) { + sal_Int32 i = rStr.lastIndexOf('\t'); + // there are only ASCII (Latin-1) characters after the tabulator + if (i > -1 && OUStringToOString(rStr.copy(i + 1), RTL_TEXTENCODING_ASCII_US).indexOf('?') == -1) + return i; + } + return -1; +} + +OString SwHTMLWriter::GetNamespace() const +{ + if (maNamespace.isEmpty()) + return OString(); + + return maNamespace + ":"; +} + +// Structure caches the current data of the writer to output a +// other part of the document, like e.g. header/footer +HTMLSaveData::HTMLSaveData(SwHTMLWriter& rWriter, sal_uLong nStt, + sal_uLong nEnd, bool bSaveNum, + const SwFrameFormat *pFrameFormat) + : rWrt(rWriter) + , pOldPam(rWrt.m_pCurrentPam) + , pOldEnd(rWrt.GetEndPaM()) + , nOldDefListLvl(rWrt.m_nDefListLvl) + , nOldDirection(rWrt.m_nDirection) + , bOldOutHeader(rWrt.m_bOutHeader) + , bOldOutFooter(rWrt.m_bOutFooter) + , bOldOutFlyFrame(rWrt.m_bOutFlyFrame) +{ + bOldWriteAll = rWrt.m_bWriteAll; + + rWrt.m_pCurrentPam = Writer::NewUnoCursor(*rWrt.m_pDoc, nStt, nEnd); + + // recognize table in special areas + if( nStt != rWrt.m_pCurrentPam->GetMark()->nNode.GetIndex() ) + { + const SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ]; + if( pNd->IsTableNode() || pNd->IsSectionNode() ) + rWrt.m_pCurrentPam->GetMark()->nNode = nStt; + } + + rWrt.SetEndPaM( rWrt.m_pCurrentPam.get() ); + rWrt.m_pCurrentPam->Exchange( ); + rWrt.m_bWriteAll = true; + rWrt.m_nDefListLvl = 0; + rWrt.m_bOutHeader = rWrt.m_bOutFooter = false; + + // Maybe save the current numbering information, so that it can be started again. + // Only then also the numbering information of the next paragraph will be valid. + if( bSaveNum ) + { + pOldNumRuleInfo.reset( new SwHTMLNumRuleInfo( rWrt.GetNumInfo() ) ); + pOldNextNumRuleInfo = rWrt.ReleaseNextNumInfo(); + } + else + { + rWrt.ClearNextNumInfo(); + } + + // The numbering will be in any case interrupted. + rWrt.GetNumInfo().Clear(); + + if( pFrameFormat ) + rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() ); +} + +HTMLSaveData::~HTMLSaveData() +{ + rWrt.m_pCurrentPam.reset(); // delete PaM again + + rWrt.m_pCurrentPam = pOldPam; + rWrt.SetEndPaM( pOldEnd ); + rWrt.m_bWriteAll = bOldWriteAll; + rWrt.m_nBkmkTabPos = bOldWriteAll ? rWrt.FindPos_Bkmk( *pOldPam->GetPoint() ) : -1; + rWrt.m_nLastParaToken = HtmlTokenId::NONE; + rWrt.m_nDefListLvl = nOldDefListLvl; + rWrt.m_nDirection = nOldDirection; + rWrt.m_bOutHeader = bOldOutHeader; + rWrt.m_bOutFooter = bOldOutFooter; + rWrt.m_bOutFlyFrame = bOldOutFlyFrame; + + // Maybe continue the numbering from before section. The numbering + // of the next paragraph will be invalid in any case. + if( pOldNumRuleInfo ) + { + rWrt.GetNumInfo().Set( *pOldNumRuleInfo ); + pOldNumRuleInfo.reset(); + rWrt.SetNextNumInfo( std::move(pOldNextNumRuleInfo) ); + } + else + { + rWrt.GetNumInfo().Clear(); + rWrt.ClearNextNumInfo(); + } +} + +void GetHTMLWriter( const OUString& rFilterOptions, const OUString& rBaseURL, WriterRef& xRet ) +{ + xRet = new SwHTMLWriter( rBaseURL, rFilterOptions ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx new file mode 100644 index 000000000..fe50f5149 --- /dev/null +++ b/sw/source/filter/html/wrthtml.hxx @@ -0,0 +1,703 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_HTML_WRTHTML_HXX +#define INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX + +#include <memory> +#include <vector> +#include <set> +#include <map> + +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/form/XForm.hpp> +#include <i18nlangtag/lang.h> +#include <comphelper/stl_types.hxx> +#include <o3tl/sorted_vector.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <rtl/ref.hxx> +#include <svtools/htmlout.hxx> +#include <tools/fldunit.hxx> + +#include <shellio.hxx> +#include <wrt_fn.hxx> +#include "htmlfly.hxx" + +// some forward declarations +class Color; +class SwFrameFormat; +class SwFlyFrameFormat; +class SwDrawFrameFormat; +class SwFormatINetFormat; +class SwFormatVertOrient; +class SwFormatFootnote; +class SwStartNode; +class SwTableNode; +class SwPageDesc; +class SwNodeIndex; +class ImageMap; +class SwNumRule; +class SdrObject; +class SdrUnoObj; +class SvxBrushItem; +class SvxFontItem; +class SwHTMLNumRuleInfo; +class SwHTMLPosFlyFrames; +class SwTextFootnote; +enum class HtmlPosition; +enum class HtmlTokenId : sal_Int16; +namespace utl { class TempFile; } + +extern SwAttrFnTab aHTMLAttrFnTab; + +#define HTML_PARSPACE (MM50) + +// flags for the output of any kind of frames +// BORDER only possible if OutHTML_Image +// ANYSIZE indicates, if also VAR_SIZE and MIN_SIZE values should be exported +// ABSSIZE indicates, if spacing and framing should be ignored +enum class HtmlFrmOpts { + NONE = 0, + Align = 1<<0, + SAlign = 1<<1, + + Width = 1<<2, + Height = 1<<3, + Size = Width | Height, + SWidth = 1<<4, + SHeight = 1<<5, + SSize = SWidth | SHeight, + AnySize = 1<<6, + AbsSize = 1<<7, + MarginSize = 1<<8, + + Space = 1<<9, + SSpace = 1<<10, + + Border = 1<<11, + SBorder = 1<<12, + SNoBorder = 1<<13, + + SBackground = 1<<14, + + Name = 1<<15, + Alt = 1<<16, + BrClear = 1<<17, + SPixSize = 1<<18, + Id = 1<<19, + Dir = 1<<20, + /// The graphic frame is a replacement image of an OLE object. + Replacement = 1<<21, + + GenImgAllMask = Alt | Size | AbsSize | Name, + GenImgMask = GenImgAllMask | Align | Space | BrClear +}; +namespace o3tl { + template<> struct typed_flags<HtmlFrmOpts> : is_typed_flags<HtmlFrmOpts, ((1<<22)-1)> {}; +} + +#define HTMLMODE_BLOCK_SPACER 0x00010000 +#define HTMLMODE_FLOAT_FRAME 0x00020000 +#define HTMLMODE_VERT_SPACER 0x00040000 +#define HTMLMODE_NBSP_IN_TABLES 0x00080000 +#define HTMLMODE_LSPACE_IN_NUMBER_BULLET 0x00100000 +//was HTMLMODE_NO_BR_AT_PAREND 0x00200000 +#define HTMLMODE_PRINT_EXT 0x00400000 +#define HTMLMODE_ABS_POS_FLY 0x00800000 +#define HTMLMODE_ABS_POS_DRAW 0x01000000 +#define HTMLMODE_FLY_MARGINS 0x02000000 +#define HTMLMODE_BORDER_NONE 0x04000000 +#define HTMLMODE_FONT_GENERIC 0x08000000 +#define HTMLMODE_FRSTLINE_IN_NUMBER_BULLET 0x10000000 +#define HTMLMODE_NO_CONTROL_CENTERING 0x20000000 + +#define HTML_DLCOLL_DD 0x4000 +#define HTML_DLCOLL_DT 0x8000 + +#define CSS1_FMT_ISTAG (USHRT_MAX) +#define CSS1_FMT_CMPREF (USHRT_MAX-1) +#define CSS1_FMT_SPECIAL (USHRT_MAX-1) + +// the following flags only specify which descriptors, tags, options, +// and so on should be outputted +// bit 0,1,2 +#define CSS1_OUTMODE_SPAN_NO_ON 0x0000U +#define CSS1_OUTMODE_SPAN_TAG_ON 0x0001U +#define CSS1_OUTMODE_STYLE_OPT_ON 0x0002U +#define CSS1_OUTMODE_RULE_ON 0x0003U +#define CSS1_OUTMODE_SPAN_TAG1_ON 0x0004U +#define CSS1_OUTMODE_ANY_ON 0x0007U + +// bit 3,4,5 +#define CSS1_OUTMODE_SPAN_NO_OFF 0x0000U +#define CSS1_OUTMODE_SPAN_TAG_OFF (sal_uInt16(0x0001U << 3)) +#define CSS1_OUTMODE_STYLE_OPT_OFF (sal_uInt16(0x0002U << 3)) +#define CSS1_OUTMODE_RULE_OFF (sal_uInt16(0x0003U << 3)) +#define CSS1_OUTMODE_ANY_OFF (sal_uInt16(0x0007U << 3)) + +#define CSS1_OUTMODE_ONOFF(a) (CSS1_OUTMODE_##a##_ON|CSS1_OUTMODE_##a##_OFF) +#define CSS1_OUTMODE_SPAN_TAG CSS1_OUTMODE_ONOFF(SPAN_TAG) +#define CSS1_OUTMODE_STYLE_OPT CSS1_OUTMODE_ONOFF(STYLE_OPT) +#define CSS1_OUTMODE_RULE CSS1_OUTMODE_ONOFF(RULE) + +// the following flags specify what should be outputted +// bit 6,7,8,9 +#define CSS1_OUTMODE_TEMPLATE 0x0000U +#define CSS1_OUTMODE_BODY (sal_uInt16(0x0001U << 6)) +#define CSS1_OUTMODE_PARA (sal_uInt16(0x0002U << 6)) +#define CSS1_OUTMODE_HINT (sal_uInt16(0x0003U << 6)) +#define CSS1_OUTMODE_FRAME (sal_uInt16(0x0004U << 6)) +#define CSS1_OUTMODE_TABLE (sal_uInt16(0x0005U << 6)) +#define CSS1_OUTMODE_TABLEBOX (sal_uInt16(0x0006U << 6)) +#define CSS1_OUTMODE_DROPCAP (sal_uInt16(0x0007U << 6)) +#define CSS1_OUTMODE_SECTION (sal_uInt16(0x0008U << 6)) +#define CSS1_OUTMODE_SOURCE (sal_uInt16(0x000fU << 6)) + +// bit 10 +#define CSS1_OUTMODE_ENCODE (sal_uInt16(0x0001U << 10)) + +// bit 11,12,13 +// don't care about script +#define CSS1_OUTMODE_ANY_SCRIPT 0x0000U +// no cjk or ctl items +#define CSS1_OUTMODE_WESTERN (sal_uInt16(0x0001U << 11)) +// no western or ctl items +#define CSS1_OUTMODE_CJK (sal_uInt16(0x0002U << 11)) +// no western or cjk items +#define CSS1_OUTMODE_CTL (sal_uInt16(0x0003U << 11)) +// no western, cjk or ctl items +#define CSS1_OUTMODE_NO_SCRIPT (sal_uInt16(0x0004U << 11)) +#define CSS1_OUTMODE_SCRIPT (sal_uInt16(0x0007U << 11)) + +// the HTML writer +struct HTMLControl +{ + // the form to which the control belongs + css::uno::Reference<css::container::XIndexContainer> xFormComps; + sal_uLong nNdIdx; // the node in which it's anchored + sal_Int32 nCount; // how many controls are on the node + + HTMLControl( const css::uno::Reference<css::container::XIndexContainer>& rForm, sal_uInt32 nIdx ); + ~HTMLControl(); + + // operators for the sort array + bool operator<( const HTMLControl& rCtrl ) const + { + return nNdIdx < rCtrl.nNdIdx; + } +}; + +class HTMLControls : public o3tl::sorted_vector<std::unique_ptr<HTMLControl>, o3tl::less_uniqueptr_to<HTMLControl> > { +}; + +struct SwHTMLFormatInfo +{ + const SwFormat *pFormat; // the format itself + + OString aToken; // the token to output + OUString aClass; // the class to output + + std::unique_ptr<SfxItemSet> pItemSet; // the attribute set to output + + sal_Int32 nLeftMargin; // some default values for + sal_Int32 nRightMargin; // paragraph styles + short nFirstLineIndent; + + sal_uInt16 nTopMargin; + sal_uInt16 nBottomMargin; + + bool bScriptDependent; + + // ctor for a dummy to search + explicit SwHTMLFormatInfo( const SwFormat *pF ) : + pFormat( pF ), + nLeftMargin( 0 ), + nRightMargin( 0 ), + nFirstLineIndent(0), + nTopMargin( 0 ), + nBottomMargin( 0 ), + bScriptDependent(false) + {} + + // ctor for creating of the format information + SwHTMLFormatInfo( const SwFormat *pFormat, SwDoc *pDoc, SwDoc *pTemplate, + bool bOutStyles, LanguageType eDfltLang=LANGUAGE_DONTKNOW, + sal_uInt16 nScript=CSS1_OUTMODE_ANY_SCRIPT ); + ~SwHTMLFormatInfo(); + + friend bool operator<( const SwHTMLFormatInfo& rInfo1, + const SwHTMLFormatInfo& rInfo2 ) + { + return reinterpret_cast<sal_IntPtr>(rInfo1.pFormat) < reinterpret_cast<sal_IntPtr>(rInfo2.pFormat); + } + +}; + +typedef std::set<std::unique_ptr<SwHTMLFormatInfo>, + comphelper::UniquePtrValueLess<SwHTMLFormatInfo>> SwHTMLFormatInfos; + +class IDocumentStylePoolAccess; + +class SW_DLLPUBLIC SwHTMLWriter : public Writer +{ + std::unique_ptr<SwHTMLPosFlyFrames> m_pHTMLPosFlyFrames; + std::unique_ptr<SwHTMLNumRuleInfo> m_pNumRuleInfo;// current numbering + std::unique_ptr<SwHTMLNumRuleInfo> m_pNextNumRuleInfo; + sal_uInt32 m_nHTMLMode; // description of export configuration + + FieldUnit m_eCSS1Unit; + + sal_uInt16 OutHeaderAttrs(); + const SwPageDesc *MakeHeader( sal_uInt16& rHeaderAtrs ); + void GetControls(); + + void AddLinkTarget( const OUString& rURL ); + void CollectLinkTargets(); + + void SetupFilterOptions(const OUString& rFilterOptions); + +protected: + ErrCode WriteStream() override; + void SetupFilterOptions(SfxMedium& rMedium) override; + +public: + std::vector<OUString> m_aImgMapNames; // written image maps + std::set<OUString> m_aImplicitMarks; // implicit jump marks + std::set<OUString> m_aNumRuleNames; // names of exported num rules + std::set<OUString> m_aScriptParaStyles; // script dependent para styles + std::set<OUString> m_aScriptTextStyles; // script dependent text styles + std::vector<OUString> m_aOutlineMarks; + std::vector<sal_uInt32> m_aOutlineMarkPoss; + HTMLControls m_aHTMLControls; // the forms to be written + SwHTMLFormatInfos m_CharFormatInfos; + SwHTMLFormatInfos m_TextCollInfos; + std::vector<SwFormatINetFormat*> m_aINetFormats; // the "open" INet attributes + std::unique_ptr<std::vector<SwTextFootnote*>> m_pFootEndNotes; + + OUString m_aCSS1Selector; // style selector + OUString m_aNonConvertableCharacters; + OUString m_aBulletGrfs[MAXLEVEL]; // list graphics + + css::uno::Reference<css::container::XIndexContainer> mxFormComps; // current form + + rtl::Reference<SwDoc> m_xTemplate; // HTML template + std::optional<Color> m_xDfltColor; // default colour + SwNodeIndex *m_pStartNdIdx; // index of first paragraph + const SwPageDesc *m_pCurrPageDesc;// current page style + const SwFormatFootnote *m_pFormatFootnote; + + sal_uInt32 m_aFontHeights[7]; // font heights 1-7 + + ErrCode m_nWarn; // warning code + sal_uInt32 m_nLastLFPos; // last position of LF + + HtmlTokenId m_nLastParaToken; // to hold paragraphs together + sal_Int32 m_nBkmkTabPos; // current position in bookmark table + sal_uInt16 m_nImgMapCnt; + sal_uInt16 m_nFormCntrlCnt; + sal_uInt16 m_nEndNote; + sal_uInt16 m_nFootNote; + sal_Int32 m_nLeftMargin; // left indent (e.g. from lists) + sal_Int32 m_nDfltLeftMargin; // defaults which doesn't have to be + sal_Int32 m_nDfltRightMargin; // written (from template) + short m_nFirstLineIndent; // first line indent (from lists) + short m_nDfltFirstLineIndent; // not to write default + sal_uInt16 m_nDfltTopMargin; // defaults which doesn't have to be + sal_uInt16 m_nDfltBottomMargin; // written (from template) + sal_uInt16 m_nIndentLvl; // How far is it indented? + sal_Int32 m_nWhishLineLen; // How long can a line be? + sal_uInt16 m_nDefListLvl; // which DL level exists now + sal_Int32 m_nDefListMargin; // How far is the indentation in DL + sal_uInt16 m_nHeaderFooterSpace; + sal_uInt16 m_nTextAttrsToIgnore; + sal_uInt16 m_nExportMode; + sal_uInt16 m_nCSS1OutMode; + sal_uInt16 m_nCSS1Script; // contains default script (that's the one + // that is not contained in class names) + SvxFrameDirection m_nDirection; // the current direction + + rtl_TextEncoding m_eDestEnc; + LanguageType m_eLang; + + // description of the export configuration + // 0 + bool m_bCfgOutStyles : 1; // export styles + bool m_bCfgPreferStyles : 1; // prefer styles instead of usual tags + bool m_bCfgFormFeed : 1; // export form feeds + bool m_bCfgStarBasic : 1; // export StarBasic + bool m_bCfgCpyLinkedGrfs : 1; + + // description of what will be exported + + bool m_bFirstLine : 1; // is the first line outputted? + bool m_bTagOn : 1; // tag on or off i.e. Attr-Start or Attr-End + + // The following two flags specify how attributes are exported: + // bTextAttr bOutOpts + // 0 0 style sheets + // 1 0 Hints: Every attribute will be written as its own tag + // and an end tag exists + // 0 1 (paragraph)attribute: The Attribute will be exported as option + // of an already written tag. There is no end tag. + bool m_bTextAttr : 1; + // 8 + bool m_bOutOpts : 1; + + bool m_bOutTable : 1; // Will the table content be written? + bool m_bOutHeader : 1; + bool m_bOutFooter : 1; + bool m_bOutFlyFrame : 1; + + // flags for style export + + bool m_bFirstCSS1Rule : 1; // was a property already written + bool m_bFirstCSS1Property : 1; // was a property already written + + // 16 + bool m_bCSS1IgnoreFirstPageDesc : 1; + + // what must/can/may not be written? + + bool m_bNoAlign : 1; // HTML tag doesn't allow ALIGN=... + bool m_bClearLeft : 1; // <BR CLEAR=LEFT> write at end of paragraph + bool m_bClearRight : 1; // <BR CLEAR=RIGHT> write at end of paragraph + bool m_bLFPossible : 1; // a line break can be inserted + + // others + + bool m_bPreserveForm : 1; // preserve the current form + + bool m_bCfgNetscape4 : 1; // Netscape4 hacks + + bool mbSkipImages : 1; + /// If HTML header and footer should be written as well, or just the content itself. + bool mbSkipHeaderFooter : 1; + bool mbEmbedImages : 1; + /// Temporary base URL for paste of images. + std::unique_ptr<utl::TempFile> mpTempBaseURL; + /// If XHTML markup should be written instead of HTML. + bool mbXHTML = false; + /// XML namespace, in case of XHTML. + OString maNamespace; + /// If the ReqIF subset of XHTML should be written. + bool mbReqIF = false; + +#define sCSS2_P_CLASS_leaders "leaders" + bool m_bCfgPrintLayout : 1; // PrintLayout option for TOC dot leaders + bool m_bParaDotLeaders : 1; // for TOC dot leaders + // 25 + + /// Tracks which text portion attributes are currently open: a which id -> open count map. + std::map<sal_uInt16, int> maStartedAttributes; + + OUString m_aRTFOLEMimeType; + + /// Construct an instance of SwHTMLWriter and optionally give it + /// the filter options directly, which can also be set via SetupFilterOptions(). + explicit SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions = "" ); + virtual ~SwHTMLWriter() override; + + void Out_SwDoc( SwPaM* ); // write the marked range + + // output all bookmarks of current paragraph + void OutAnchor( const OUString& rName ); + void OutBookmarks(); + void OutPointFieldmarks( const SwPosition& rPos ); + void OutImplicitMark( const OUString& rMark, const char *pMarkType ); + + OUString convertHyperlinkHRefValue(const OUString& rURL); + + void OutHyperlinkHRefValue( const OUString& rURL ); + + // output the FlyFrame anchored at current position + bool OutFlyFrame( sal_uLong nNdIdx, sal_Int32 nContentIdx, + HtmlPosition nPos, HTMLOutContext *pContext = nullptr ); + void OutFrameFormat( AllHtmlFlags nType, const SwFrameFormat& rFormat, + const SdrObject *pSdrObj ); + + void OutForm( bool bTagOn=true, const SwStartNode *pStNd=nullptr ); + void OutHiddenForms(); + void OutHiddenForm( const css::uno::Reference<css::form::XForm>& rForm ); + + void OutForm( bool bOn, const css::uno::Reference<css::container::XIndexContainer>& rFormComps ); + void OutHiddenControls( const css::uno::Reference<css::container::XIndexContainer>& rFormComps, + const css::uno::Reference<css::beans::XPropertySet>& rPropSet ); + bool HasControls() const; + + void OutFootEndNoteInfo(); + void OutFootEndNotes(); + OUString GetFootEndNoteSym( const SwFormatFootnote& rFormatFootnote ); + void OutFootEndNoteSym( const SwFormatFootnote& rFormatFootnote, const OUString& rNum, + sal_uInt16 nScript ); + + void OutBasic(SwHTMLWriter& rHTMLWrt); + + void OutAndSetDefList( sal_uInt16 nNewLvl ); + + void OutStyleSheet( const SwPageDesc& rPageDesc ); + + inline void OutCSS1_PropertyAscii( const char *pProp, + const char *pVal ); + inline void OutCSS1_PropertyAscii( const char *pProp, + const OString& rVal ); + inline void OutCSS1_Property( const char *pProp, const OUString& rVal ); + void OutCSS1_Property( const char *pProp, const char *pVal, + const OUString *pSVal ); + void OutCSS1_UnitProperty( const char *pProp, long nVal ); + void OutCSS1_PixelProperty( const char *pProp, long nVal, bool bVert ); + void OutCSS1_SfxItemSet( const SfxItemSet& rItemSet, bool bDeep=true ); + + // events of BODY tag from SFX configuration + void OutBasicBodyEvents(); + + // BACKGROUND/BGCOLOR option + void OutBackground( const SvxBrushItem *pBrushItem, bool bGraphic ); + void OutBackground( const SfxItemSet& rItemSet, bool bGraphic ); + + void OutLanguage( LanguageType eLang ); + SvxFrameDirection GetHTMLDirection( SvxFrameDirection nDir ) const; + SvxFrameDirection GetHTMLDirection( const SfxItemSet& rItemSet ) const; + void OutDirection( SvxFrameDirection nDir ); + static OString convertDirection(SvxFrameDirection nDirection); + + // ALT/ALIGN/WIDTH/HEIGHT/HSPACE/VSPACE option of current + // frame format output and maybe add a <BR CLEAR=...> at the + // beginning of rEndTags + OString OutFrameFormatOptions( const SwFrameFormat& rFrameFormat, const OUString& rAltText, + HtmlFrmOpts nFrameOpts ); + + void writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAltText, HtmlFrmOpts nFrameOpts); + + void OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat ); + void OutCSS1_TableCellBorderHack(const SwFrameFormat& rFrameFormat); + void OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol ); + void OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat, HtmlFrmOpts nFrameOpts, + const SdrObject *pSdrObj=nullptr, + const SfxItemSet *pItemSet=nullptr ); + void OutCSS1_FrameFormatBackground( const SwFrameFormat& rFrameFormat ); + + void ChangeParaToken( HtmlTokenId nNew ); + + void IncIndentLevel() + { + m_nIndentLvl++; + } + void DecIndentLevel() + { + if ( m_nIndentLvl ) m_nIndentLvl--; + } + OString GetIndentString(sal_uInt16 nIncLvl = 0); + + sal_Int32 GetLineLen() + { + return static_cast<sal_Int32>(Strm().Tell()-m_nLastLFPos); + } + void OutNewLine( bool bCheck=false ); + + // for HTMLSaveData + SwPaM* GetEndPaM() { return m_pOrigPam; } + void SetEndPaM( SwPaM* pPam ) { m_pOrigPam = pPam; } + + static sal_uInt32 ToPixel( sal_uInt32 nVal, const bool bVert ); + + sal_uInt16 GuessFrameType( const SwFrameFormat& rFrameFormat, + const SdrObject*& rpStrObj ); + static sal_uInt16 GuessOLENodeFrameType( const SwNode& rNd ); + + void CollectFlyFrames(); + + sal_uInt16 GetHTMLFontSize( sal_uInt32 nFontHeight ) const; + + // Fetch current numbering information. + SwHTMLNumRuleInfo& GetNumInfo() { return *m_pNumRuleInfo; } + + // Fetch current numbering information of next paragraph. They + // don't have to exist yet! + SwHTMLNumRuleInfo *GetNextNumInfo() { return m_pNextNumRuleInfo.get(); } + std::unique_ptr<SwHTMLNumRuleInfo> ReleaseNextNumInfo(); + + // Set the numbering information of next paragraph. + void SetNextNumInfo( std::unique_ptr<SwHTMLNumRuleInfo> pNxt ); + + // Fill the numbering information of next paragraph. + void FillNextNumInfo(); + + // Clear numbering information of next paragraph. + void ClearNextNumInfo(); + + static const SdrObject* GetHTMLControl( const SwDrawFrameFormat& rFormat ); + static const SdrObject* GetMarqueeTextObj( const SwDrawFrameFormat& rFormat ); + static sal_uInt16 GetCSS1Selector( const SwFormat *pFormat, OString& rToken, + OUString& rClass, sal_uInt16& rRefPoolId, + OUString *pPseudo=nullptr ); + + static const SwFormat *GetTemplateFormat( sal_uInt16 nPoolId, IDocumentStylePoolAccess* /*SwDoc*/ pTemplate ); + static const SwFormat *GetParentFormat( const SwFormat& rFormat, sal_uInt16 nDeep ); + + static void SubtractItemSet( SfxItemSet& rItemSet, + const SfxItemSet& rRefItemSet, + bool bSetDefaults, + bool bClearSame = true, + const SfxItemSet *pRefScriptItemSet=nullptr ); + static bool HasScriptDependentItems( const SfxItemSet& rItemSet, + bool bCheckDropCap ); + + static void GetEEAttrsFromDrwObj( SfxItemSet& rItemSet, + const SdrObject *pObj ); + + static sal_uInt16 GetDefListLvl( const OUString& rNm, sal_uInt16 nPoolId ); + + sal_uInt32 GetHTMLMode() const + { + return m_nHTMLMode; + } + bool IsHTMLMode( sal_uInt32 nMode ) const + { + return (m_nHTMLMode & nMode) != 0; + } + + inline bool IsCSS1Source( sal_uInt16 n ) const; + inline bool IsCSS1Script( sal_uInt16 n ) const; + + static const char *GetNumFormat( sal_uInt16 nFormat ); + static void PrepareFontList( const SvxFontItem& rFontItem, OUString& rNames, + sal_Unicode cQuote, bool bGeneric ); + static sal_uInt16 GetCSS1ScriptForScriptType( sal_uInt16 nScriptType ); + static sal_uInt16 GetLangWhichIdFromScript( sal_uInt16 nScript ); + + FieldUnit GetCSS1Unit() const { return m_eCSS1Unit; } + + sal_Int32 indexOfDotLeaders( sal_uInt16 nPoolId, const OUString& rText ); + + /// Determines the prefix string needed to respect the requested namespace alias. + OString GetNamespace() const; +}; + +inline bool SwHTMLWriter::IsCSS1Source( sal_uInt16 n ) const +{ + return n == (m_nCSS1OutMode & CSS1_OUTMODE_SOURCE); +} + +inline bool SwHTMLWriter::IsCSS1Script( sal_uInt16 n ) const +{ + sal_uInt16 nScript = (m_nCSS1OutMode & CSS1_OUTMODE_SCRIPT); + return CSS1_OUTMODE_ANY_SCRIPT == nScript || n == nScript; +} + +inline void SwHTMLWriter::OutCSS1_PropertyAscii( const char *pProp, + const char *pVal ) +{ + OutCSS1_Property( pProp, pVal, nullptr ); +} + +inline void SwHTMLWriter::OutCSS1_PropertyAscii( const char *pProp, + const OString& rVal ) +{ + OutCSS1_Property( pProp, rVal.getStr(), nullptr ); +} + +inline void SwHTMLWriter::OutCSS1_Property( const char *pProp, + const OUString& rVal ) +{ + OutCSS1_Property( pProp, nullptr, &rVal ); +} + + +// Structure caches the current data of the writer to output +// another part of the document, like e.g. header/footer +// With the two USHORTs in the ctor a new PaM is created and sets the +// positions in the document. +// In dtor all data is restored and the created PaM is deleted again. + +struct HTMLSaveData +{ + SwHTMLWriter& rWrt; + std::shared_ptr<SwUnoCursor> pOldPam; + SwPaM *pOldEnd; + std::unique_ptr<SwHTMLNumRuleInfo> pOldNumRuleInfo; // Owner = this + std::unique_ptr<SwHTMLNumRuleInfo> pOldNextNumRuleInfo; + sal_uInt16 nOldDefListLvl; + SvxFrameDirection nOldDirection; + bool bOldWriteAll : 1; + bool bOldOutHeader : 1; + bool bOldOutFooter : 1; + bool bOldOutFlyFrame : 1; + + HTMLSaveData( SwHTMLWriter&, sal_uLong nStt, sal_uLong nEnd, + bool bSaveNum=true, + const SwFrameFormat *pFrameFormat=nullptr ); + ~HTMLSaveData(); +}; + +// some function prototypes +Writer& OutHTML_FrameFormatOLENode( Writer& rWrt, const SwFrameFormat& rFormat, + bool bInCntnr ); +Writer& OutHTML_FrameFormatOLENodeGrf( Writer& rWrt, const SwFrameFormat& rFormat, + bool bInCntnr ); + +Writer& OutHTML_SwTextNode( Writer&, const SwContentNode& ); +Writer& OutHTML_SwTableNode( Writer& , SwTableNode &, const SwFrameFormat *, + const OUString* pCaption=nullptr, bool bTopCaption=false ); + +Writer& OutHTML_DrawFrameFormatAsControl( Writer& rWrt, const SwDrawFrameFormat& rFormat, + const SdrUnoObj& rSdrObj, bool bInCntnr ); +Writer& OutHTML_DrawFrameFormatAsMarquee( Writer& rWrt, const SwDrawFrameFormat& rFormat, + const SdrObject& rSdrObj ); + +Writer& OutHTML_HeaderFooter( Writer& rWrt, const SwFrameFormat& rFrameFormat, + bool bHeader ); + +Writer& OutHTML_Image( Writer&, const SwFrameFormat& rFormat, + const OUString& rGraphicURL, + Graphic const & rGraphic, const OUString& rAlternateText, + const Size& rRealSize, HtmlFrmOpts nFrameOpts, + const char *pMarkType, + const ImageMap *pGenImgMap, + const OUString& rMimeType = OUString() ); + +Writer& OutHTML_BulletImage( Writer& rWrt, const char *pTag, + const SvxBrushItem* pBrush, + const OUString& rGraphicURL); + +Writer& OutHTML_SwFormatField( Writer& rWrt, const SfxPoolItem& rHt ); +Writer& OutHTML_SwFormatFootnote( Writer& rWrt, const SfxPoolItem& rHt ); +Writer& OutHTML_INetFormat( Writer&, const SwFormatINetFormat& rINetFormat, bool bOn ); + +Writer& OutCSS1_BodyTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ); +Writer& OutCSS1_ParaTagStyleOpt( Writer& rWrt, const SfxItemSet& rItemSet ); + +Writer& OutCSS1_HintSpanTag( Writer& rWrt, const SfxPoolItem& rHt ); +Writer& OutCSS1_HintStyleOpt( Writer& rWrt, const SfxPoolItem& rHt ); + +Writer& OutCSS1_TableBGStyleOpt( Writer& rWrt, const SfxPoolItem& rHt ); +Writer& OutCSS1_NumberBulletListStyleOpt( Writer& rWrt, const SwNumRule& rNumRule, + sal_uInt8 nLevel ); + +Writer& OutHTML_NumberBulletListStart( SwHTMLWriter& rWrt, + const SwHTMLNumRuleInfo& rInfo ); +Writer& OutHTML_NumberBulletListEnd( SwHTMLWriter& rWrt, + const SwHTMLNumRuleInfo& rNextInfo ); + +Writer& OutCSS1_SvxBox( Writer& rWrt, const SfxPoolItem& rHt ); + +OString GetCSS1_Color(const Color& rColor); +bool IgnorePropertyForReqIF(bool bReqIF, const OString& rProperty); + +#endif // INCLUDED_SW_SOURCE_FILTER_HTML_WRTHTML_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/fltini.hxx b/sw/source/filter/inc/fltini.hxx new file mode 100644 index 000000000..54ff7cf07 --- /dev/null +++ b/sw/source/filter/inc/fltini.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_FILTER_INC_FLTINI_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_FLTINI_HXX + +#include <shellio.hxx> + +class SwNumRuleTable; +class SwDoc; +class SwTextNode; +class SwNumRule; +class SwNodeIndex; + +// the special readers + +class HTMLReader: public Reader +{ + // we don't want to have the streams/storages open + virtual bool SetStrmStgPtr() override; + virtual ErrCode Read(SwDoc &, const OUString& rBaseURL, SwPaM &, const OUString &) override; + virtual OUString GetTemplateName(SwDoc& rDoc) const override; + + /// Parse FilterOptions passed to the importer. + void SetupFilterOptions(); + + OUString m_aNamespace; +public: + HTMLReader(); +}; + +class XMLReader : public Reader +{ + virtual ErrCode Read(SwDoc &, const OUString& rBaseURL, SwPaM &, const OUString &) override; +public: + virtual SwReaderType GetReaderType() override; + + XMLReader(); + + // read the sections of the document, which is equal to the medium. + // returns the count of it + virtual size_t GetSectionList( SfxMedium& rMedium, + std::vector<OUString>& rStrings) const override; +}; + +// the special writers + +void GetWW8Writer( const OUString&, const OUString&, WriterRef& ); + +// Get size of fly (if 'automatic' in WW) and check if not too small +SW_DLLPUBLIC void CalculateFlySize(SfxItemSet& rFlySet, const SwNodeIndex& rAnchor, + SwTwips nPageWidth); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/fltshell.hxx b/sw/source/filter/inc/fltshell.hxx new file mode 100644 index 000000000..186ae1578 --- /dev/null +++ b/sw/source/filter/inc/fltshell.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_INC_FLTSHELL_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_FLTSHELL_HXX + +#include <hintids.hxx> +#include <svl/listener.hxx> +#include <tools/datetime.hxx> +#include <mdiexp.hxx> +#include <ndindex.hxx> +#include <pam.hxx> +#include <strings.hrc> +#include <IDocumentRedlineAccess.hxx> + +#include <cstddef> +#include <limits> +#include <memory> + +class SwTOXBase; +class SwField; +class SwFieldType; +class Graphic; +class SwTableBox; +class SwDoc; +class SwPaM; + +inline bool SwFltGetFlag(sal_uLong nFieldFlags, int no) + { return (nFieldFlags & (sal_uLong(1) << no)) != 0; } + +//Subvert the Node/Content system to get positions which don't update as +//content is appended to them +struct SW_DLLPUBLIC SwFltPosition +{ +public: + SwNodeIndex m_nNode; + sal_Int32 m_nContent; +public: + bool operator==(const SwFltPosition &rOther) const + { + return (m_nContent == rOther.m_nContent && + m_nNode == rOther.m_nNode); + } + void SetPos(SwNodeIndex const &rNode, sal_uInt16 nIdx) + { + m_nNode = rNode; + m_nContent = nIdx; + } + //operators with SwPosition, where the node is hacked to the previous one, + //and the offset to content is de-dynamic-ified + SwFltPosition(const SwPosition &rPos) + : m_nNode(rPos.nNode, -1) + , m_nContent(rPos.nContent.GetIndex()) + { + } + void SetPos(const SwPosition &rPos) + { + m_nNode = rPos.nNode.GetIndex()-1; + m_nContent = rPos.nContent.GetIndex(); + } +}; + +// Stack entry for the attributes. It is always pointers to new attributes that are passed. +class SwFltStackEntry +{ +private: + SwFltStackEntry(SwFltStackEntry const&) = delete; + SwFltStackEntry& operator=(SwFltStackEntry const&) = delete; + +public: + SwFltPosition m_aMkPos; + SwFltPosition m_aPtPos; + + std::unique_ptr<SfxPoolItem> pAttr;// Format Attribute + + bool bOld; // to mark Attributes *before* skipping field results + bool bOpen; //Entry open, awaiting being closed + bool bConsumedByField; + bool m_isAnnotationOnEnd; ///< annotation already moved onto its end pos. + + sal_Int32 mnStartCP; + sal_Int32 mnEndCP; + bool bIsParaEnd; + + SW_DLLPUBLIC SwFltStackEntry(const SwPosition & rStartPos, std::unique_ptr<SfxPoolItem> pHt ); + SW_DLLPUBLIC ~SwFltStackEntry(); + + enum class RegionMode { NoCheck = 0, CheckNodes = 1<<0, CheckFieldmark = 1<<1 }; + SW_DLLPUBLIC void SetEndPos( const SwPosition & rEndPos); + SW_DLLPUBLIC bool MakeRegion(SwDoc* pDoc, SwPaM& rRegion, RegionMode eCheck) const; + SW_DLLPUBLIC static bool MakeRegion(SwDoc* pDoc, SwPaM& rRegion, + RegionMode eCheck, const SwFltPosition &rMkPos, const SwFltPosition &rPtPos, bool bIsParaEnd=false, + sal_uInt16 nWhich=0); + + void SetStartCP(sal_Int32 nCP) {mnStartCP = nCP;} + void SetEndCP(sal_Int32 nCP) {mnEndCP = nCP;} + sal_Int32 GetStartCP() const {return mnStartCP;} + sal_Int32 GetEndCP() const {return mnEndCP;} + bool IsParaEnd() const { return bIsParaEnd;} + void SetIsParaEnd(bool bArg){ bIsParaEnd = bArg;} +}; + +template<> struct o3tl::typed_flags<SwFltStackEntry::RegionMode>: o3tl::is_typed_flags<SwFltStackEntry::RegionMode, 0x03> {}; + +class SW_DLLPUBLIC SwFltControlStack +{ +private: + SwFltControlStack(SwFltControlStack const&) = delete; + SwFltControlStack& operator=(SwFltControlStack const&) = delete; + + typedef std::vector<std::unique_ptr<SwFltStackEntry>> Entries; + Entries m_Entries; + + sal_uLong nFieldFlags; + + bool bHasSdOD; + bool bSdODChecked; + +protected: + SwDoc* pDoc; + bool bIsEndStack; + + virtual void SetAttrInDoc(const SwPosition& rTmpPos, SwFltStackEntry& rEntry); + virtual sal_Int32 GetCurrAttrCP() const {return -1;} + virtual bool IsParaEndInCPs(sal_Int32 nStart,sal_Int32 nEnd,bool bSdOD) const; + + //Clear the para end position recorded in reader intermittently for the least impact on loading performance + virtual void ClearParaEndPosition(){}; + virtual bool CheckSdOD(sal_Int32 nStart,sal_Int32 nEnd); + bool HasSdOD(); + +public: + enum class MoveAttrsMode { DEFAULT, POSTIT_INSERTED }; + void MoveAttrs(const SwPosition& rPos, MoveAttrsMode = MoveAttrsMode::DEFAULT); + enum Flags + { + HYPO, + TAGS_DO_ID, + TAGS_VISIBLE, + BOOK_TO_VAR_REF, + BOOK_AND_REF, + TAGS_IN_TEXT, + ALLOW_FLD_CR + }; + + SwFltControlStack(SwDoc* pDo, sal_uLong nFieldFl); + virtual ~SwFltControlStack(); + + bool IsFlagSet(Flags no) const { return ::SwFltGetFlag(nFieldFlags, no);} + + void NewAttr(const SwPosition& rPos, const SfxPoolItem & rAttr ); + + virtual SwFltStackEntry* SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, bool bTstEnd=true, long nHand = LONG_MAX, bool consumedByField=false); + + void StealAttr(const SwNodeIndex& rNode); + void MarkAllAttrsOld(); + void KillUnlockedAttrs(const SwPosition& pPos); + SfxPoolItem* GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos); + const SfxPoolItem* GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich); + void Delete(const SwPaM &rPam); + + bool empty() const { return m_Entries.empty(); } + Entries::size_type size() const { return m_Entries.size(); } + SwFltStackEntry& operator[](Entries::size_type nIndex) + { return *m_Entries[nIndex]; } + void DeleteAndDestroy(Entries::size_type nCnt); +}; + +class SwFltAnchorListener; + +class SW_DLLPUBLIC SwFltAnchor: public SfxPoolItem +{ + SwFrameFormat* pFrameFormat; + std::unique_ptr<SwFltAnchorListener> pListener; + +public: + SwFltAnchor(SwFrameFormat* pFlyFormat); + SwFltAnchor(const SwFltAnchor&); + virtual ~SwFltAnchor() override; + + // "purely virtual methods" of SfxPoolItem + virtual bool operator==(const SfxPoolItem&) const override; + virtual SwFltAnchor* Clone(SfxItemPool* = nullptr) const override; + void SetFrameFormat(SwFrameFormat* _pFrameFormat); + const SwFrameFormat* GetFrameFormat() const { return pFrameFormat; } + SwFrameFormat* GetFrameFormat() { return pFrameFormat; } +}; + +class SwFltAnchorListener : public SvtListener +{ + SwFltAnchor* m_pFltAnchor; + public: + SwFltAnchorListener(SwFltAnchor* pFltAnchor); + virtual void Notify(const SfxHint&) override; +}; + +class SW_DLLPUBLIC SwFltRedline : public SfxPoolItem +{ +public: + DateTime aStamp; + RedlineType eType; + std::size_t nAutorNo; + + SwFltRedline(RedlineType eType_, + std::size_t nAutorNo_, + const DateTime& rStamp_) + : SfxPoolItem(RES_FLTR_REDLINE), aStamp(rStamp_), + eType(eType_), + nAutorNo(nAutorNo_) + { + } + + // "purely virtual methods" of SfxPoolItem + virtual bool operator==(const SfxPoolItem& rItem) const override; + virtual SwFltRedline* Clone(SfxItemPool* = nullptr) const override; +}; + +class SW_DLLPUBLIC SwFltBookmark : public SfxPoolItem +{ +private: + + long mnHandle; + OUString maName; + OUString maVal; + bool mbIsTOCBookmark; + +public: + SwFltBookmark( const OUString& rNa, + const OUString& rVa, + long nHand, + const bool bIsTOCBookmark = false ); + + // "purely virtual methods" of SfxPoolItem + virtual bool operator==(const SfxPoolItem&) const override; + virtual SwFltBookmark* Clone(SfxItemPool* = nullptr) const override; + + long GetHandle() const { return mnHandle; } + const OUString& GetName() const { return maName; } + const OUString& GetValSys() const { return maVal; } + bool IsTOCBookmark() const + { + return mbIsTOCBookmark; + } +}; + +/// Stores RDF statements on a paragraph (key-value pairs where the subject is the paragraph). +class SW_DLLPUBLIC SwFltRDFMark : public SfxPoolItem +{ + long m_nHandle; + std::vector< std::pair<OUString, OUString> > m_aAttributes; + +public: + SwFltRDFMark(); + + virtual bool operator==(const SfxPoolItem&) const override; + virtual SwFltRDFMark* Clone(SfxItemPool* = nullptr) const override; + + void SetHandle(long nHandle); + long GetHandle() const; + void SetAttributes(const std::vector< std::pair<OUString, OUString> >& rAttributes); + const std::vector< std::pair<OUString, OUString> >& GetAttributes() const; +}; + +class SW_DLLPUBLIC SwFltTOX : public SfxPoolItem +{ + std::shared_ptr<SwTOXBase> m_xTOXBase; + bool bHadBreakItem; // there was a break item BEFORE insertion of the TOX + bool bHadPageDescItem; +public: + SwFltTOX(std::shared_ptr<SwTOXBase> xBase); + // "purely virtual methods" of SfxPoolItem + virtual bool operator==(const SfxPoolItem&) const override; + virtual SwFltTOX* Clone(SfxItemPool* = nullptr) const override; + const SwTOXBase& GetBase() const { return *m_xTOXBase; } + void SetHadBreakItem( bool bVal ) { bHadBreakItem = bVal; } + void SetHadPageDescItem( bool bVal ) { bHadPageDescItem = bVal; } + bool HadBreakItem() const { return bHadBreakItem; } + bool HadPageDescItem() const { return bHadPageDescItem; } +}; + +// The WWEndStack behaves like the WWControlStack, except that the attributes +// on it are hoarded to the end of the document if they need to be accessed +// (e.g., book/RefMarks, index, etc.). +class SwFltEndStack : public SwFltControlStack +{ +public: + SwFltEndStack(SwDoc* pDo, sal_uLong nFieldFl) + :SwFltControlStack(pDo, nFieldFl) + { + bIsEndStack = true; + } +}; + +SW_DLLPUBLIC void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset); + +class ImportProgress +{ +private: + SwDocShell *m_pDocShell; +public: + ImportProgress(SwDocShell *pDocShell, long nStartVal, long nEndVal) + : m_pDocShell(pDocShell) + { + ::StartProgress(STR_STATSTR_W4WREAD, nStartVal, nEndVal, m_pDocShell); + } + + void Update(sal_uInt16 nProgress) + { + ::SetProgressState(nProgress, m_pDocShell); // Update + } + + ~ImportProgress() + { + ::EndProgress(m_pDocShell); + } +}; + +// detect if the SwFrameFormat it is watching was deleted +class SW_DLLPUBLIC FrameDeleteWatch final: public SvtListener +{ + SwFrameFormat* m_pFormat; +public: + FrameDeleteWatch(SwFrameFormat* pFormat); + + virtual void Notify(const SfxHint& rHint) override; + + SwFrameFormat* GetFormat() + { + return m_pFormat; + } + + bool WasDeleted() const + { + return !m_pFormat; + } + + virtual ~FrameDeleteWatch() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/msfilter.hxx b/sw/source/filter/inc/msfilter.hxx new file mode 100644 index 000000000..78948385c --- /dev/null +++ b/sw/source/filter/inc/msfilter.hxx @@ -0,0 +1,400 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_INC_MSFILTER_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_MSFILTER_HXX + +#include <sal/config.h> + +#include <cstddef> +#include <map> +#include <vector> +#include <memory> +#include <swtypes.hxx> +#include "wwstyles.hxx" +#include <rtl/textenc.h> +#include "fltshell.hxx" +#include <shellio.hxx> +#include <svl/zforlist.hxx> +#include <svl/listener.hxx> + +class SwDoc; +class SwPaM; +class SwTableNode; +class SwNodeIndex; +class SwNoTextNode; +class SwTextNode; +class WW8TabDesc; + +namespace myImplHelpers +{ +template<class C> class StyleMapperImpl; +} + +class SwTextFormatColl; +class SwCharFormat; + +namespace sw +{ + namespace ms + { + /** MSOffice appears to set the charset of unicode fonts to MS 932 + + But we do "default", whatever that means. + + @param eTextEncoding + the OOo encoding to convert from + + @return + a msoffice equivalent charset identifier + */ + sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding); + + /** MSOffice appears to set the charset of unicode fonts to MS 932 + + Arial Unicode MS for example is a unicode font, but word sets + exported uses of it to the MS 932 charset + + */ + sal_uInt8 rtl_TextEncodingToWinCharsetRTF(OUString const& rFontName, + OUString const& rAltName, rtl_TextEncoding eTextEncoding); + + /** Convert from DTTM to Writer's DateTime + + */ + sal_uInt32 DateTime2DTTM( const DateTime& rDT ); + + /** Convert from Word Date/Time field str to Writer's Date Time str + + */ + sal_uLong MSDateTimeFormatToSwFormat(OUString& rParams, SvNumberFormatter *pFormatter, LanguageType &rLang, bool bHijri, LanguageType nDocLang); + + /*Used to identify if the previous token is AM time field*/ + bool IsPreviousAM(OUString const & rParams, sal_Int32 nPos); + + /*Used to identify if the next token is PM time field*/ + bool IsNextPM(OUString const & rParams, sal_Int32 nPos); + + /** Used by MSDateTimeFormatToSwFormat to identify AM time fields + + */ + bool IsNotAM(OUString const & rParams, sal_Int32 nPos); + + /** Another function used by MSDateTimeFormatToSwFormat + + */ + void SwapQuotesInField(OUString &rFormat); + + } + + namespace util + { + /// Redlining Authors, map word author key to writer author value + typedef std::map<sal_uInt16, std::size_t> AuthorInfos; + + /** Clips a value to MAX/MIN 16bit value to make it safe for use + as a position value to give to writer. i.e. +-57.8cm. Sometimes + we see ridiculous values for positioning in rtf and word document, + this captures such ones and clips them to values which are + still outside the document, but of a value that doesn't cause + problems for writer's layout, e.g. see + https://bz.apache.org/ooo/show_bug.cgi?id=i9245 + + @param nIn + + @return nIn clipped to min/max 16bit value + */ + SwTwips MakeSafePositioningValue(SwTwips nIn); + + /** Knows which writer style a given word style should be imported as + + Mapping a word style to a writer style has to consider mapping + the word builtin styles like "Normal" as the default root style + to our default root style which is called "Default" in english, + and "Normal" in german. + + Additionally it then has to avoid name collisions such as + + a) styles "Normal" and "Default" in a single document, where + we can use the original distinct names "Normal" and "Default" and... + b) styles "Normal" and "Default" in a single document, where + we can not use the original names, and must come up with an + alternative name for one of them... + + And it needs to report to the importer if the style being mapped to + was already in existence, for the cut and paste/insert file mode we + should not modify the returned style if it is already in use as it + is does not belong to us to change. + */ + class ParaStyleMapper + { + private: + //I hate these things stupid pImpl things, but it's warranted here + std::unique_ptr<::myImplHelpers::StyleMapperImpl<SwTextFormatColl> > mpImpl; + public: + explicit ParaStyleMapper(SwDoc &rDoc); + ~ParaStyleMapper(); + + /** StyleResult + StyleResult is a std::pair of a pointer to a style and a flag + which is true if the style existed previously in the document. + */ + typedef std::pair<SwTextFormatColl*, bool> StyleResult; + + /** Get the writer style which the word style should map to + + @param rName + The name of the word style + + @param eSti + The style id of the word style, we are really only interested + in knowing if the style has either a builtin standard id, or is + a user defined style. + + @return + The equivalent writer style packaged as a StyleResult to use + for this word style. + + It will only return a failure in the pathological case of + catastrophic failure elsewhere of there exist already styles + rName and WW-rName[0..SAL_MAX_INT32], which is both unlikely + and impossible. + */ + StyleResult GetStyle(const OUString& rName, ww::sti eSti); + }; + + /** Knows which writer style a given word style should be imported as + + Mapping a word style to a writer style has to consider mapping + the word builtin styles like "Normal" as the default root style + to our default root style which is called "Default" in english, + and "Normal" in german. + + Additionally it then has to avoid name collisions such as + + a) styles "Normal" and "Default" in a single document, where + we can use the original distinct names "Normal" and "Default" and... + b) styles "Normal" and "Default" in a single document, where + we can not use the original names, and must come up with an + alternative name for one of them... + + And it needs to report to the importer if the style being mapped to + was already in existence, for the cut and paste/insert file mode we + should not modify the returned style if it is already in use as it + is does not belong to us to change. + */ + class CharStyleMapper + { + private: + //I hate these things stupid pImpl things, but it's warranted here + std::unique_ptr<::myImplHelpers::StyleMapperImpl<SwCharFormat>> mpImpl; + public: + explicit CharStyleMapper(SwDoc &rDoc); + ~CharStyleMapper(); + + /** StyleResult + StyleResult is a std::pair of a pointer to a style and a flag + which is true if the style existed previously in the document. + */ + typedef std::pair<SwCharFormat*, bool> StyleResult; + + /** Get the writer style which the word style should map to + + @param rName + The name of the word style + + @param eSti + The style id of the word style, we are really only interested + in knowing if the style has either a builtin standard id, or is + a user defined style. + + @return + The equivalent writer style packaged as a StyleResult to use + for this word style. + + It will only return a failure in the pathological case of + catastrophic failure elsewhere of there exist already styles + rName and WW-rName[0..SAL_MAX_INT32], which is both unlikely + and impossible. + */ + StyleResult GetStyle(const OUString& rName, ww::sti eSti); + }; + + /** Find suitable names for exporting this font + + Given a fontname description find the best primary and secondary + fallback font to use from MSWord's persp font + + @see #i10242#/#i19164# for examples + */ + class FontMapExport + { + public: + OUString msPrimary; + OUString msSecondary; + explicit FontMapExport(const OUString &rFontDescription); + }; + + class InsertedTableListener: public SvtListener + { + SwTableNode* m_pTableNode; + public: + explicit InsertedTableListener(SwTableNode& rNode); + SwTableNode* GetTableNode(); + virtual void Notify(const SfxHint&) override; + }; + + /** Handle requirements for table formatting in insert->file mode. + + When inserting a table into a document which already has been + formatted and laid out (e.g using insert->file) then tables + must be handled in a special way, (or so previous comments and + code in the filters leads me to believe). + + Before the document is finalized the new tables need to have + their layout frms deleted and recalculated. This TableManager + detects the necessity to do this, and all tables inserted into + a document should be registered with this manager with + InsertTable, and before finalization DelAndMakeTableFrames should + be called. + + @see #i25782# for examples + */ + class InsertedTablesManager + { + public: + void DelAndMakeTableFrames(); + void InsertTable(SwTableNode &rTableNode, SwPaM &rPaM); + explicit InsertedTablesManager(const SwDoc &rDoc); + private: + bool mbHasRoot; + std::map<std::unique_ptr<InsertedTableListener>, SwNodeIndex*> maTables; + }; + + class RedlineStack + { + private: + std::vector<std::unique_ptr<SwFltStackEntry>> maStack; + SwDoc &mrDoc; + + RedlineStack(RedlineStack const&) = delete; + RedlineStack& operator=(RedlineStack const&) = delete; + + public: + explicit RedlineStack(SwDoc &rDoc) : mrDoc(rDoc) {} + void MoveAttrsFieldmarkInserted(const SwPosition& rPos); + void open(const SwPosition& rPos, const SfxPoolItem& rAttr); + bool close(const SwPosition& rPos, RedlineType eType); + void close(const SwPosition& rPos, RedlineType eType, + WW8TabDesc* pTabDesc ); + void closeall(const SwPosition& rPos); + ~RedlineStack(); + }; + + class SetInDocAndDelete + { + private: + SwDoc &mrDoc; + public: + explicit SetInDocAndDelete(SwDoc &rDoc) : mrDoc(rDoc) {} + void operator()(std::unique_ptr<SwFltStackEntry> & pEntry); + private: + SetInDocAndDelete& operator=(const SetInDocAndDelete&) = delete; + }; + + class SetEndIfOpen //Subclass from something ? + { + private: + const SwPosition &mrPos; + public: + explicit SetEndIfOpen(const SwPosition &rPos) : mrPos(rPos) {} + void operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const + { + if (pEntry->bOpen) + pEntry->SetEndPos(mrPos); + } + private: + SetEndIfOpen& operator=(const SetEndIfOpen&) = delete; + }; + + class CompareRedlines + { + public: + bool operator()(const std::unique_ptr<SwFltStackEntry> & pOneE, const std::unique_ptr<SwFltStackEntry> & pTwoE) + const; + }; + + class WrtRedlineAuthor + { + protected: + std::vector<OUString> maAuthors; // Array of Sw - Bookmarknames + + private: + WrtRedlineAuthor(WrtRedlineAuthor const&) = delete; + WrtRedlineAuthor& operator=(WrtRedlineAuthor const&) = delete; + + public: + WrtRedlineAuthor() = default; + virtual ~WrtRedlineAuthor() {} + + sal_uInt16 AddName( const OUString& rNm ); + virtual void Write(Writer &rWrt) = 0; + }; + + struct CharRunEntry + { + sal_Int32 mnEndPos; + sal_uInt16 mnScript; + rtl_TextEncoding meCharSet; + bool mbRTL; + CharRunEntry(sal_Int32 nEndPos, sal_uInt16 nScript, + rtl_TextEncoding eCharSet, bool bRTL) + : mnEndPos(nEndPos), mnScript(nScript), meCharSet(eCharSet), + mbRTL(bRTL) + { + } + }; + + typedef std::vector<CharRunEntry> CharRuns; + + /** Collect the ranges of Text which share + + Word generally requires characters which share the same direction, + the same script, and occasionally (depending on the format) the + same charset to be exported in independent chunks. + + So this function finds these ranges and returns a STL container + of CharRuns + + @param rTextNd + The TextNode we want to ranges from + + @return STL container of CharRuns which describe the shared + direction, script and optionally script of the contiguous sequences + of characters + + @see #i22537# for example + */ + CharRuns GetPseudoCharRuns(const SwTextNode& rTextNd); + } +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/rtf.hxx b/sw/source/filter/inc/rtf.hxx new file mode 100644 index 000000000..d1e4d3fa9 --- /dev/null +++ b/sw/source/filter/inc/rtf.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_FILTER_INC_RTF_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_RTF_HXX + +#include <sal/types.h> + +class RTFSurround +{ + union { + struct { + sal_uInt8 nGoldCut : 1; // should ideally be bool + sal_uInt8 nOrder : 4; + sal_uInt8 nJunk : 3; + } Flags; + sal_uInt8 nVal; + } Value; +public: + RTFSurround( bool bGoldCut, sal_uInt8 nOrder ) { + Value.Flags.nGoldCut = sal_uInt8(bGoldCut); + Value.Flags.nOrder = nOrder; + Value.Flags.nJunk = 0; + } + + sal_uInt16 GetValue() const { return Value.nVal; } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_INC_RTF_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/wrt_fn.hxx b/sw/source/filter/inc/wrt_fn.hxx new file mode 100644 index 000000000..179fe1f80 --- /dev/null +++ b/sw/source/filter/inc/wrt_fn.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_INC_WRT_FN_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_WRT_FN_HXX +#include <hintids.hxx> + +// some forward declarations +class SwNode; +class SwContentNode; +class Writer; +class SfxPoolItem; +class SfxItemSet; + +/* function pointers to the attribute-write functions */ +typedef Writer& (*FnAttrOut)( Writer&, const SfxPoolItem& ); +typedef FnAttrOut SwAttrFnTab[ POOLATTR_END - POOLATTR_BEGIN ]; + +Writer& Out( const SwAttrFnTab, const SfxPoolItem&, Writer& ); +Writer& Out_SfxItemSet( const SwAttrFnTab, Writer&, const SfxItemSet&, + bool bDeep ); + +/* function pointers to the node-write functions */ + +enum RES_NODE +{ +RES_NODE_BEGIN = 0, + RES_TXTNODE = RES_NODE_BEGIN, + RES_GRFNODE, + RES_OLENODE, +RES_NODE_END +}; + +typedef Writer& (*FnNodeOut)( Writer&, SwContentNode& ); +typedef FnNodeOut SwNodeFnTab[ RES_NODE_END - RES_NODE_BEGIN ]; + +Writer& Out( const SwNodeFnTab, SwNode&, Writer & rWrt ); + +#endif // INCLUDED_SW_SOURCE_FILTER_INC_WRT_FN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/wrtswtbl.hxx b/sw/source/filter/inc/wrtswtbl.hxx new file mode 100644 index 000000000..27710a11b --- /dev/null +++ b/sw/source/filter/inc/wrtswtbl.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_FILTER_INC_WRTSWTBL_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_WRTSWTBL_HXX + +#include <tools/color.hxx> +#include <o3tl/sorted_vector.hxx> + +#include <swdllapi.h> + +#include <memory> +#include <vector> +#include <climits> + +class SwTableBox; +class SwTableLine; +class SwTableLines; +class SwHTMLTableLayout; +class SvxBrushItem; + +namespace editeng { class SvxBorderLine; } + +// Code from the HTML filter for writing of tables + +#define COLFUZZY 20 +#define ROWFUZZY 20 +#define COL_DFLT_WIDTH ((2*COLFUZZY)+1) +#define ROW_DFLT_HEIGHT (2*ROWFUZZY)+1 + +class SW_DLLPUBLIC SwWriteTableCell +{ + const SwTableBox *pBox; // SwTableBox of the cell + const SvxBrushItem *pBackground; // inherited background of a row + + long nHeight; // fix/minimum height of a row + + sal_uInt32 nWidthOpt; // width from option; + + sal_uInt16 nRow; // start row + sal_uInt16 nCol; // start column + + sal_uInt16 nRowSpan; // spanned rows + sal_uInt16 nColSpan; // spanned columns + + bool bPercentWidthOpt; + +public: + + SwWriteTableCell(const SwTableBox *pB, sal_uInt16 nR, sal_uInt16 nC, sal_uInt16 nRSpan, + sal_uInt16 nCSpan, long nHght, const SvxBrushItem *pBGround) + : pBox( pB ), pBackground( pBGround ), nHeight( nHght ), nWidthOpt( 0 ), + nRow( nR ), nCol( nC ), nRowSpan( nRSpan ), nColSpan( nCSpan ), + bPercentWidthOpt( false ) + {} + + const SwTableBox *GetBox() const { return pBox; } + + sal_uInt16 GetRow() const { return nRow; } + sal_uInt16 GetCol() const { return nCol; } + + sal_uInt16 GetRowSpan() const { return nRowSpan; } + sal_uInt16 GetColSpan() const { return nColSpan; } + + long GetHeight() const { return nHeight; } + sal_Int16 GetVertOri() const; + + const SvxBrushItem *GetBackground() const { return pBackground; } + + void SetWidthOpt( sal_uInt16 nWidth, bool bPercent ) + { + nWidthOpt = nWidth; bPercentWidthOpt = bPercent; + } + + sal_uInt32 GetWidthOpt() const { return nWidthOpt; } + bool HasPercentWidthOpt() const { return bPercentWidthOpt; } +}; + +typedef std::vector<std::unique_ptr<SwWriteTableCell>> SwWriteTableCells; + +class SwWriteTableRow final +{ + SwWriteTableCells m_Cells; ///< all cells of the rows + const SvxBrushItem *pBackground; // background + + long nPos; // end position (twips) of the row + bool mbUseLayoutHeights; + + SwWriteTableRow & operator= (const SwWriteTableRow &) = delete; + + // GCC >= 3.4 needs accessible T (const T&) to pass T as const T& argument. + SwWriteTableRow( const SwWriteTableRow & ); + +public: + + sal_uInt16 nTopBorder; // thickness of upper/lower border + sal_uInt16 nBottomBorder; + + bool bTopBorder : 1; // which borders are there? + bool bBottomBorder : 1; + + SwWriteTableRow( long nPos, bool bUseLayoutHeights ); + + SwWriteTableCell *AddCell( const SwTableBox *pBox, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + long nHeight, + const SvxBrushItem *pBackground ); + + void SetBackground( const SvxBrushItem *pBGround ) + { + pBackground = pBGround; + } + const SvxBrushItem *GetBackground() const { return pBackground; } + + bool HasTopBorder() const { return bTopBorder; } + bool HasBottomBorder() const { return bBottomBorder; } + + const SwWriteTableCells& GetCells() const { return m_Cells; } + + inline bool operator==( const SwWriteTableRow& rRow ) const; + inline bool operator<( const SwWriteTableRow& rRow2 ) const; +}; + +inline bool SwWriteTableRow::operator==( const SwWriteTableRow& rRow ) const +{ + // allow for some fuzzyness + return (nPos >= rRow.nPos ? nPos - rRow.nPos : rRow.nPos - nPos ) <= + (mbUseLayoutHeights ? 0 : ROWFUZZY); +} + +inline bool SwWriteTableRow::operator<( const SwWriteTableRow& rRow ) const +{ + // Since we only know the degrees of truth of 0 and 1 here, we also prefer to + // not let x==y and x<y at the same time ;-) + return nPos < rRow.nPos - (mbUseLayoutHeights ? 0 : ROWFUZZY); +} + +using SwWriteTableRows + = o3tl::sorted_vector< std::unique_ptr<SwWriteTableRow>, o3tl::less_uniqueptr_to<SwWriteTableRow> >; + +class SwWriteTableCol +{ + sal_uInt32 nPos; // end position of the column + + sal_uInt32 nWidthOpt; + + bool bRelWidthOpt : 1; + +public: + bool bLeftBorder : 1; // which borders are there? + bool bRightBorder : 1; + + SwWriteTableCol( sal_uInt32 nPosition ); + + sal_uInt32 GetPos() const { return nPos; } + + bool HasLeftBorder() const { return bLeftBorder; } + + bool HasRightBorder() const { return bRightBorder; } + + inline bool operator==( const SwWriteTableCol& rCol ) const; + inline bool operator<( const SwWriteTableCol& rCol ) const; + + void SetWidthOpt( sal_uInt32 nWidth, bool bRel ) + { + nWidthOpt = nWidth; bRelWidthOpt = bRel; + } + sal_uInt32 GetWidthOpt() const { return nWidthOpt; } + bool HasRelWidthOpt() const { return bRelWidthOpt; } +}; + +inline bool SwWriteTableCol::operator==( const SwWriteTableCol& rCol ) const +{ + // allow for some fuzzyness + return (nPos >= rCol.nPos ? nPos - rCol.nPos + : rCol.nPos - nPos ) <= COLFUZZY; +} + +inline bool SwWriteTableCol::operator<( const SwWriteTableCol& rCol ) const +{ + // Since we only know the degrees of truth of 0 and 1 here, we also prefer to + // not let x==y and x<y at the same time ;-) + return nPos + COLFUZZY < rCol.nPos; +} + +struct SwWriteTableColLess { + bool operator()(std::unique_ptr<SwWriteTableCol> const & lhs, std::unique_ptr<SwWriteTableCol> const & rhs) { + return lhs->GetPos() < rhs->GetPos(); + } +}; + +class SwWriteTableCols : public o3tl::sorted_vector<std::unique_ptr<SwWriteTableCol>, SwWriteTableColLess> { +}; + +class SwTable; + +class SW_DLLPUBLIC SwWriteTable +{ +private: + const SwTable* m_pTable; +protected: + SwWriteTableCols m_aCols; // all columns + SwWriteTableRows m_aRows; // all rows + + Color m_nBorderColor; // border color + + sal_uInt16 m_nCellSpacing; // thickness of the inner border + sal_uInt16 m_nCellPadding; // distance of border to content + + sal_uInt16 m_nBorder; // thickness of the outer border + sal_uInt16 m_nInnerBorder; // thickness of the inner border + sal_uInt32 m_nBaseWidth; // reference value for SwFormatFrameSize width + + sal_uInt16 m_nHeadEndRow; // last row of the table heading + + sal_uInt16 m_nLeftSub; + sal_uInt16 m_nRightSub; + + sal_uInt32 m_nTabWidth; // absolute/relative width of the table + + bool m_bRelWidths : 1; // generate relative widths? + bool m_bUseLayoutHeights : 1; // use layout to determine the height? +#ifdef DBG_UTIL + bool m_bGetLineHeightCalled : 1; +#endif + + bool m_bColTags : 1; + bool m_bLayoutExport : 1; + bool m_bCollectBorderWidth : 1; + + virtual bool ShouldExpandSub( const SwTableBox *pBox, + bool bExpandedBefore, sal_uInt16 nDepth ) const; + + void CollectTableRowsCols( long nStartRPos, sal_uInt32 nStartCPos, + long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + sal_uInt16 nDepth ); + + void FillTableRowsCols( long nStartRPos, sal_uInt16 nStartRow, + sal_uInt32 nStartCPos, sal_uInt16 nStartCol, + long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + const SvxBrushItem* pLineBrush, + sal_uInt16 nDepth, + sal_uInt16 nNumOfHeaderRows ); + + void MergeBorders( const editeng::SvxBorderLine* pBorderLine, bool bTable ); + + sal_uInt16 MergeBoxBorders(const SwTableBox *pBox, size_t nRow, size_t nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + sal_uInt16 &rTopBorder, sal_uInt16 &rBottomBorder ); + + sal_uInt32 GetBaseWidth() const { return m_nBaseWidth; } + + bool HasRelWidths() const { return m_bRelWidths; } + +public: + static sal_uInt32 GetBoxWidth( const SwTableBox *pBox ); + + sal_uInt32 GetRawWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const; + sal_uInt16 GetAbsWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const; + sal_uInt16 GetRelWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const; + sal_uInt16 GetPercentWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const; + + long GetAbsHeight(long nRawWidth, size_t nRow, sal_uInt16 nRowSpan) const; + + double GetAbsWidthRatio() const { return m_nTabWidth == m_nBaseWidth ? 1.0 : double(m_nTabWidth) / m_nBaseWidth; } +protected: + long GetLineHeight( const SwTableLine *pLine ); + static long GetLineHeight( const SwTableBox *pBox ); + static const SvxBrushItem *GetLineBrush( const SwTableBox *pBox, + SwWriteTableRow *pRow ); + + sal_uInt16 GetLeftSpace( sal_uInt16 nCol ) const; + sal_uInt16 GetRightSpace(size_t nCol, sal_uInt16 nColSpan) const; + +public: + SwWriteTable(const SwTable* pTable, const SwTableLines& rLines, long nWidth, sal_uInt32 nBWidth, + bool bRel, sal_uInt16 nMaxDepth = USHRT_MAX, + sal_uInt16 nLeftSub=0, sal_uInt16 nRightSub=0, sal_uInt32 nNumOfRowsToRepeat=0); + SwWriteTable(const SwTable* pTable, const SwHTMLTableLayout *pLayoutInfo); + virtual ~SwWriteTable(); + + const SwWriteTableRows& GetRows() const { return m_aRows; } + + const SwTable* GetTable() const { return m_pTable; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/inc/wwstyles.hxx b/sw/source/filter/inc/wwstyles.hxx new file mode 100644 index 000000000..19faeaad1 --- /dev/null +++ b/sw/source/filter/inc/wwstyles.hxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_INC_WWSTYLES_HXX +#define INCLUDED_SW_SOURCE_FILTER_INC_WWSTYLES_HXX + +#include <sal/types.h> + +namespace ww +{ + enum sti + { + stiNormal = 0, // 0x0000 + stiLev1 = 1, // 0x0001 + stiLev2 = 2, // 0x0002 + stiLev3 = 3, // 0x0003 + stiLev4 = 4, // 0x0004 + stiLev5 = 5, // 0x0005 + stiLev6 = 6, // 0x0006 + stiLev7 = 7, // 0x0007 + stiLev8 = 8, // 0x0008 + stiLev9 = 9, // 0x0009 + stiLevFirst = stiLev1, + stiLevLast = stiLev9, + stiIndex1 = 10, // 0x000A + stiIndex2 = 11, // 0x000B + stiIndex3 = 12, // 0x000C + stiIndex4 = 13, // 0x000D + stiIndex5 = 14, // 0x000E + stiIndex6 = 15, // 0x000F + stiIndex7 = 16, // 0x0010 + stiIndex8 = 17, // 0x0011 + stiIndex9 = 18, // 0x0012 + stiIndexFirst = stiIndex1, + stiIndexLast = stiIndex9, + stiToc1 = 19, // 0x0013 + stiToc2 = 20, // 0x0014 + stiToc3 = 21, // 0x0015 + stiToc4 = 22, // 0x0016 + stiToc5 = 23, // 0x0017 + stiToc6 = 24, // 0x0018 + stiToc7 = 25, // 0x0019 + stiToc8 = 26, // 0x001A + stiToc9 = 27, // 0x001B + stiTocFirst = stiToc1, + stiTocLast = stiToc9, + stiNormIndent = 28, // 0x001C + stiFootnoteText = 29, // 0x001D + stiAtnText = 30, // 0x001E + stiHeader = 31, // 0x001F + stiFooter = 32, // 0x0020 + stiIndexHeading = 33, // 0x0021 + stiCaption = 34, // 0x0022 + stiToCaption = 35, // 0x0023 + stiEnvAddr = 36, // 0x0024 + stiEnvRet = 37, // 0x0025 + stiFootnoteRef = 38, // 0x0026 char style + stiAtnRef = 39, // 0x0027 char style + stiLnn = 40, // 0x0028 char style + stiPgn = 41, // 0x0029 char style + stiEdnRef = 42, // 0x002A char style + stiEdnText = 43, // 0x002B + stiToa = 44, // 0x002C + stiMacro = 45, // 0x002D + stiToaHeading = 46, // 0x002E + stiList = 47, // 0x002F + stiListBullet = 48, // 0x0030 + stiListNumber = 49, // 0x0031 + stiList2 = 50, // 0x0032 + stiList3 = 51, // 0x0033 + stiList4 = 52, // 0x0034 + stiList5 = 53, // 0x0035 + stiListBullet2 = 54, // 0x0036 + stiListBullet3 = 55, // 0x0037 + stiListBullet4 = 56, // 0x0038 + stiListBullet5 = 57, // 0x0039 + stiListNumber2 = 58, // 0x003A + stiListNumber3 = 59, // 0x003B + stiListNumber4 = 60, // 0x003C + stiListNumber5 = 61, // 0x003D + stiTitle = 62, // 0x003E + stiClosing = 63, // 0x003F + stiSignature = 64, // 0x0040 + stiNormalChar = 65, // 0x0041 char style + stiBodyText = 66, // 0x0042 + /* + stiBodyTextInd1 was orig stiBodyText2 in documentation, but that + collides with the other stiBodyText2 and this seems more reasonable. + cmc@openoffice.org + */ + stiBodyTextInd1 = 67, // 0x0043 + stiListCont = 68, // 0x0044 + stiListCont2 = 69, // 0x0045 + stiListCont3 = 70, // 0x0046 + stiListCont4 = 71, // 0x0047 + stiListCont5 = 72, // 0x0048 + stiMsgHeader = 73, // 0x0049 + stiSubtitle = 74, // 0x004A + stiSalutation = 75, // 0x004B + stiDate = 76, // 0X004C + stiBodyText1I = 77, // 0x004D + stiBodyText1I2 = 78, // 0x004E + stiNoteHeading = 79, // 0x004F + stiBodyText2 = 80, // 0x0050 + stiBodyText3 = 81, // 0x0051 + stiBodyTextInd2 = 82, // 0x0052 + stiBodyTextInd3 = 83, // 0x0053 + stiBlockQuote = 84, // 0x0054 + stiHyperlink = 85, // 0x0055 char style + stiHyperlinkFollowed = 86, // 0x0056 char style + stiStrong = 87, // 0x0057 char style + stiEmphasis = 88, // 0x0058 char style + stiNavPane = 89, // 0x0059 char style + stiPlainText = 90, // 0x005A + stiMax = 91, // number of defined sti's + stiUser = 0x0ffe, // user styles are distinguished by name + stiNil = 0x0fff // max for 12 bits + }; + + /** Find the WinWord sti index of an old <= Word2 stc (style code) + + When importing a Word 2 document we would like to treat styles as + similar to how word 8 does as possible, to this end word will treat + some styles with special codes as inbuilt styles, and some as user + defined styles. + + @param + stc the Style code to test to see what winword sti word would give + such a code + + @return the sti that word would give it. stiUser if word would treat + it as a user defined style. + */ + sti GetCanonicalStiFromStc(sal_uInt8 stc) throw(); + + /** Find the WinWord english name from a sti index + + Map the word style index to its english name + + @param + sti the Style index + + @return the name word would give it if it's an inbuilt name, otherwise + NULL + */ + const char* GetEnglishNameFromSti(sti eSti) throw(); + + /** Determine if the WinWord sti is standard Character Style + + @param + sti the Style index + + @return true if a known inbuild character style + */ + bool StandardStiIsCharStyle(sti eSti) throw(); +} // namespace ww + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/rtf/swparrtf.cxx b/sw/source/filter/rtf/swparrtf.cxx new file mode 100644 index 000000000..b6ae62bf6 --- /dev/null +++ b/sw/source/filter/rtf/swparrtf.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <shellio.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <docsh.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <swdll.hxx> +#include <swerror.h> + +#include <unotextrange.hxx> + +#include <unotools/streamwrap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XImporter.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +using namespace ::com::sun::star; + +namespace +{ +/// Glue class to call RtfImport as an internal filter, needed by copy&paste support. +class SwRTFReader : public Reader +{ + ErrCode Read(SwDoc& rDoc, const OUString& rBaseURL, SwPaM& rPam, + const OUString& rFileName) override; +}; +} + +ErrCode SwRTFReader::Read(SwDoc& rDoc, const OUString& /*rBaseURL*/, SwPaM& rPam, + const OUString& /*rFileName*/) +{ + if (!m_pStream) + return ERR_SWG_READ_ERROR; + + // We want to work in an empty paragraph. + // Step 1: XTextRange will be updated when content is inserted, so we know + // the end position. + const uno::Reference<text::XTextRange> xInsertPosition + = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr); + auto pSttNdIdx = std::make_shared<SwNodeIndex>(rDoc.GetNodes()); + const SwPosition* pPos = rPam.GetPoint(); + + // Step 2: Split once and remember the node that has been split. + rDoc.getIDocumentContentOperations().SplitNode(*pPos, false); + *pSttNdIdx = pPos->nNode.GetIndex() - 1; + + // Step 3: Split again. + rDoc.getIDocumentContentOperations().SplitNode(*pPos, false); + auto pSttNdIdx2 = std::make_shared<SwNodeIndex>(rDoc.GetNodes()); + *pSttNdIdx2 = pPos->nNode.GetIndex(); + + // Step 4: Insert all content into the new node + rPam.Move(fnMoveBackward); + rDoc.SetTextFormatColl( + rPam, rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false)); + + SwDocShell* pDocShell(rDoc.GetDocShell()); + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory( + comphelper::getProcessServiceFactory()); + uno::Reference<uno::XInterface> xInterface( + xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"), + uno::UNO_SET_THROW); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Reference<lang::XComponent> xDstDoc(pDocShell->GetModel(), uno::UNO_QUERY_THROW); + xImporter->setTargetDocument(xDstDoc); + + const uno::Reference<text::XTextRange> xInsertTextRange + = SwXTextRange::CreateXTextRange(rDoc, *rPam.GetPoint(), nullptr); + + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence( + { { "InputStream", + uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(*m_pStream))) }, + { "InsertMode", uno::Any(true) }, + { "TextInsertModeRange", uno::Any(xInsertTextRange) } })); + auto ret = ERRCODE_NONE; + try + { + xFilter->filter(aDescriptor); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.rtf", "SwRTFReader::Read()"); + ret = ERR_SWG_READ_ERROR; + } + + // Clean up the fake paragraphs. + SwUnoInternalPaM aPam(rDoc); + ::sw::XTextRangeToSwPaM(aPam, xInsertPosition); + if (pSttNdIdx->GetIndex()) + { + // If we are in insert mode, join the split node that is in front + // of the new content with the first new node. Or in other words: + // Revert the first split node. + SwTextNode* pTextNode = pSttNdIdx->GetNode().GetTextNode(); + SwNodeIndex aNxtIdx(*pSttNdIdx); + if (pTextNode && pTextNode->CanJoinNext(&aNxtIdx) + && pSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex()) + { + // If the PaM points to the first new node, move the PaM to the + // end of the previous node. + if (aPam.GetPoint()->nNode == aNxtIdx) + { + aPam.GetPoint()->nNode = *pSttNdIdx; + aPam.GetPoint()->nContent.Assign(pTextNode, pTextNode->GetText().getLength()); + } + // If the first new node isn't empty, convert the node's text + // attributes into hints. Otherwise, set the new node's + // paragraph style at the previous (empty) node. + SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode(); + if (pTextNode->GetText().getLength()) + pDelNd->FormatToTextAttr(pTextNode); + else + pTextNode->ChgFormatColl(pDelNd->GetTextColl()); + pTextNode->JoinNext(); + } + } + + if (pSttNdIdx2->GetIndex()) + { + // If we are in insert mode, join the split node that is after + // the new content with the last new node. Or in other words: + // Revert the second split node. + SwTextNode* pTextNode = pSttNdIdx2->GetNode().GetTextNode(); + SwNodeIndex aPrevIdx(*pSttNdIdx2); + if (pTextNode && pTextNode->CanJoinPrev(&aPrevIdx) + && pSttNdIdx2->GetIndex() - 1 == aPrevIdx.GetIndex()) + { + // If the last new node isn't empty, convert the node's text + // attributes into hints. Otherwise, set the new node's + // paragraph style at the next (empty) node. + SwTextNode* pDelNd = aPrevIdx.GetNode().GetTextNode(); + if (pTextNode->GetText().getLength()) + pDelNd->FormatToTextAttr(pTextNode); + else + pTextNode->ChgFormatColl(pDelNd->GetTextColl()); + pTextNode->JoinPrev(); + } + } + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportRTF() { return new SwRTFReader; } + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportRTF(SvStream& rStream) +{ + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory( + comphelper::getProcessServiceFactory()); + uno::Reference<uno::XInterface> xInterface( + xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.RtfFilter"), + uno::UNO_SET_THROW); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Reference<lang::XComponent> xDstDoc(xDocSh->GetModel(), uno::UNO_QUERY_THROW); + xImporter->setTargetDocument(xDstDoc); + + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence( + { { "InputStream", + uno::Any(uno::Reference<io::XStream>(new utl::OStreamWrapper(rStream))) } })); + bool bRet = true; + try + { + xFilter->filter(aDescriptor); + } + catch (...) + { + bRet = false; + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/writer/writer.cxx b/sw/source/filter/writer/writer.cxx new file mode 100644 index 000000000..501bd8099 --- /dev/null +++ b/sw/source/filter/writer/writer.cxx @@ -0,0 +1,537 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sot/storage.hxx> +#include <sfx2/docfile.hxx> +#include <tools/diagnose_ex.h> +#include <editeng/fontitem.hxx> +#include <editeng/eeitem.hxx> +#include <osl/diagnose.h> +#include <shellio.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <IMark.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <numrule.hxx> +#include <swerror.h> +#include <com/sun/star/ucb/ContentCreationException.hpp> + +using namespace css; + +namespace +{ + SvStream& lcl_OutLongExt( SvStream& rStrm, sal_uLong nVal, bool bNeg ) + { + char aBuf[28]; + + int i = SAL_N_ELEMENTS(aBuf); + aBuf[--i] = 0; + do + { + aBuf[--i] = '0' + static_cast<char>(nVal % 10); + nVal /= 10; + } while (nVal); + + if (bNeg) + aBuf[--i] = '-'; + + return rStrm.WriteCharPtr( &aBuf[i] ); + } +} + +typedef std::multimap<sal_uLong, const ::sw::mark::IMark*> SwBookmarkNodeTable; + +struct Writer_Impl +{ + SvStream * m_pStream; + + std::unique_ptr< std::map<OUString, OUString> > pFileNameMap; + std::vector<const SvxFontItem*> aFontRemoveLst; + SwBookmarkNodeTable aBkmkNodePos; + + Writer_Impl(); + + void RemoveFontList( SwDoc& rDoc ); + void InsertBkmk( const ::sw::mark::IMark& rBkmk ); +}; + +Writer_Impl::Writer_Impl() + : m_pStream(nullptr) +{ +} + +void Writer_Impl::RemoveFontList( SwDoc& rDoc ) +{ + for( const auto& rpFontItem : aFontRemoveLst ) + { + rDoc.GetAttrPool().Remove( *rpFontItem ); + } +} + +void Writer_Impl::InsertBkmk(const ::sw::mark::IMark& rBkmk) +{ + sal_uLong nNd = rBkmk.GetMarkPos().nNode.GetIndex(); + + aBkmkNodePos.emplace( nNd, &rBkmk ); + + if(rBkmk.IsExpanded() && rBkmk.GetOtherMarkPos().nNode != nNd) + { + nNd = rBkmk.GetOtherMarkPos().nNode.GetIndex(); + aBkmkNodePos.emplace( nNd, &rBkmk ); + } +} + +/* + * This module is the central collection point for all writer-filters + * and is a DLL ! + * + * So that the Writer can work with different writers, the output-functions + * of the content carrying objects have to be mapped to the various + * output-functions. + * + * For that, to inquire its output function, every object can be gripped + * via the which-value in a table. + * These functions are available in the corresponding Writer-DLL's. + */ + +Writer::Writer() + : m_pImpl(std::make_unique<Writer_Impl>()) + , m_pOrigFileName(nullptr), m_pDoc(nullptr), m_pOrigPam(nullptr) + , m_bHideDeleteRedlines(false) +{ + m_bWriteAll = m_bShowProgress = m_bUCS2_WithStartChar = true; + m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR = + m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock = + m_bOrganizerMode = false; + m_bExportPargraphNumbering = true; +} + +Writer::~Writer() +{ +} + +/* + * Document Interface Access + */ +IDocumentSettingAccess& Writer::getIDocumentSettingAccess() { return m_pDoc->getIDocumentSettingAccess(); } +const IDocumentSettingAccess& Writer::getIDocumentSettingAccess() const { return m_pDoc->getIDocumentSettingAccess(); } +IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() { return m_pDoc->getIDocumentStylePoolAccess(); } +const IDocumentStylePoolAccess& Writer::getIDocumentStylePoolAccess() const { return m_pDoc->getIDocumentStylePoolAccess(); } + +void Writer::ResetWriter() +{ + m_pImpl->RemoveFontList( *m_pDoc ); + m_pImpl.reset(new Writer_Impl); + + if( m_pCurrentPam ) + { + while (m_pCurrentPam->GetNext() != m_pCurrentPam.get()) + delete m_pCurrentPam->GetNext(); + m_pCurrentPam.reset(); + } + m_pCurrentPam = nullptr; + m_pOrigFileName = nullptr; + m_pDoc = nullptr; + + m_bShowProgress = m_bUCS2_WithStartChar = true; + m_bASCII_NoLastLineEnd = m_bASCII_ParaAsBlank = m_bASCII_ParaAsCR = + m_bWriteClipboardDoc = m_bWriteOnlyFirstTable = m_bBlock = + m_bOrganizerMode = false; +} + +bool Writer::CopyNextPam( SwPaM ** ppPam ) +{ + if( (*ppPam)->GetNext() == m_pOrigPam ) + { + *ppPam = m_pOrigPam; // set back to the beginning pam + return false; // end of the ring + } + + // otherwise copy the next value from the next Pam + *ppPam = (*ppPam)->GetNext(); + + *m_pCurrentPam->GetPoint() = *(*ppPam)->Start(); + *m_pCurrentPam->GetMark() = *(*ppPam)->End(); + + return true; +} + +// search the next Bookmark-Position from the Bookmark-Table + +sal_Int32 Writer::FindPos_Bkmk(const SwPosition& rPos) const +{ + const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + const IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findFirstBookmarkStartsAfter(rPos); + if(ppBkmk != pMarkAccess->getBookmarksEnd()) + return ppBkmk - pMarkAccess->getBookmarksBegin(); + return -1; +} + +std::shared_ptr<SwUnoCursor> +Writer::NewUnoCursor(SwDoc & rDoc, sal_uLong const nStartIdx, sal_uLong const nEndIdx) +{ + SwNodes *const pNds = &rDoc.GetNodes(); + + SwNodeIndex aStt( *pNds, nStartIdx ); + SwContentNode* pCNode = aStt.GetNode().GetContentNode(); + if( !pCNode && nullptr == pNds->GoNext( &aStt ) ) + { + OSL_FAIL( "No more ContentNode at StartPos" ); + } + + auto const pNew = rDoc.CreateUnoCursor(SwPosition(aStt), false); + pNew->SetMark(); + aStt = nEndIdx; + pCNode = aStt.GetNode().GetContentNode(); + if (!pCNode) + pCNode = SwNodes::GoPrevious(&aStt); + assert(pCNode && "No more ContentNode at StartPos"); + pCNode->MakeEndIndex( &pNew->GetPoint()->nContent ); + pNew->GetPoint()->nNode = aStt; + return pNew; +} + +// Stream-specific +SvStream& Writer::Strm() +{ + assert(m_pImpl->m_pStream && "Oh-oh. Writer with no Stream!"); + return *m_pImpl->m_pStream; +} + +void Writer::SetStream(SvStream *const pStream) +{ + m_pImpl->m_pStream = pStream; +} + +SvStream& Writer::OutLong( SvStream& rStrm, long nVal ) +{ + const bool bNeg = nVal < 0; + if (bNeg) + nVal = -nVal; + + return lcl_OutLongExt(rStrm, static_cast<sal_uLong>(nVal), bNeg); +} + +SvStream& Writer::OutULong( SvStream& rStrm, sal_uLong nVal ) +{ + return lcl_OutLongExt(rStrm, nVal, false); +} + +ErrCode Writer::Write( SwPaM& rPaM, SvStream& rStrm, const OUString* pFName ) +{ + if ( IsStgWriter() ) + { + ErrCode nResult = ERRCODE_ABORT; + try + { + tools::SvRef<SotStorage> aRef = new SotStorage( rStrm ); + nResult = Write( rPaM, *aRef, pFName ); + if ( nResult == ERRCODE_NONE ) + aRef->Commit(); + } + catch (const css::ucb::ContentCreationException &) + { + TOOLS_WARN_EXCEPTION("sw", "Writer::Write caught"); + } + return nResult; + } + + m_pDoc = rPaM.GetDoc(); + m_pOrigFileName = pFName; + m_pImpl->m_pStream = &rStrm; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCode nRet = WriteStream(); + + ResetWriter(); + + return nRet; +} + +void Writer::SetupFilterOptions(SfxMedium& /*rMedium*/) +{} + +ErrCode Writer::Write( SwPaM& rPam, SfxMedium& rMedium, const OUString* pFileName ) +{ + SetupFilterOptions(rMedium); + // This method must be overridden in SwXMLWriter a storage from medium will be used there. + // The microsoft format can write to storage but the storage will be based on the stream. + return Write( rPam, *rMedium.GetOutStream(), pFileName ); +} + +ErrCode Writer::Write( SwPaM& /*rPam*/, SotStorage&, const OUString* ) +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +ErrCode Writer::Write( SwPaM&, const uno::Reference < embed::XStorage >&, const OUString*, SfxMedium* ) +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +bool Writer::CopyLocalFileToINet( OUString& rFileNm ) +{ + if( !m_pOrigFileName ) // can be happen, by example if we + return false; // write into the clipboard + + bool bRet = false; + INetURLObject aFileUrl( rFileNm ), aTargetUrl( *m_pOrigFileName ); + +// this is our old without the Mail-Export + if( ! ( INetProtocol::File == aFileUrl.GetProtocol() && + INetProtocol::File != aTargetUrl.GetProtocol() && + INetProtocol::Ftp <= aTargetUrl.GetProtocol() && + INetProtocol::VndSunStarWebdav >= aTargetUrl.GetProtocol() ) ) + return bRet; + + if (m_pImpl->pFileNameMap) + { + // has the file been moved? + std::map<OUString, OUString>::iterator it = m_pImpl->pFileNameMap->find( rFileNm ); + if ( it != m_pImpl->pFileNameMap->end() ) + { + rFileNm = it->second; + return true; + } + } + else + { + m_pImpl->pFileNameMap.reset( new std::map<OUString, OUString> ); + } + + OUString aSrc = rFileNm; + OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName(); + + SfxMedium aSrcFile( aSrc, StreamMode::READ ); + SfxMedium aDstFile( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE ); + + aDstFile.GetOutStream()->WriteStream( *aSrcFile.GetInStream() ); + + aSrcFile.Close(); + aDstFile.Commit(); + + bRet = ERRCODE_NONE == aDstFile.GetError(); + + if( bRet ) + { + m_pImpl->pFileNameMap->insert( std::make_pair( aSrc, aDest ) ); + rFileNm = aDest; + } + + return bRet; +} + +void Writer::PutNumFormatFontsInAttrPool() +{ + // then there are a few fonts in the NumRules + // These put into the Pool. After this does they have a RefCount > 1 + // it can be removed - it is already in the Pool + SfxItemPool& rPool = m_pDoc->GetAttrPool(); + const SwNumRuleTable& rListTable = m_pDoc->GetNumRuleTable(); + const SwNumRule* pRule; + const SwNumFormat* pFormat; + const vcl::Font* pFont; + const vcl::Font* pDefFont = &numfunc::GetDefBulletFont(); + bool bCheck = false; + + for( size_t nGet = rListTable.size(); nGet; ) + if( SwDoc::IsUsed( *(pRule = rListTable[ --nGet ] ))) + for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl ) + if( SVX_NUM_CHAR_SPECIAL == (pFormat = &pRule->Get( nLvl ))->GetNumberingType() || + SVX_NUM_BITMAP == pFormat->GetNumberingType() ) + { + if( nullptr == ( pFont = pFormat->GetBulletFont() ) ) + pFont = pDefFont; + + if( bCheck ) + { + if( *pFont == *pDefFont ) + continue; + } + else if( *pFont == *pDefFont ) + bCheck = true; + + AddFontItem( rPool, SvxFontItem( pFont->GetFamilyType(), + pFont->GetFamilyName(), pFont->GetStyleName(), + pFont->GetPitch(), pFont->GetCharSet(), RES_CHRATR_FONT )); + } +} + +void Writer::PutEditEngFontsInAttrPool() +{ + SfxItemPool& rPool = m_pDoc->GetAttrPool(); + if( rPool.GetSecondaryPool() ) + { + AddFontItems_( rPool, EE_CHAR_FONTINFO ); + AddFontItems_( rPool, EE_CHAR_FONTINFO_CJK ); + AddFontItems_( rPool, EE_CHAR_FONTINFO_CTL ); + } +} + +void Writer::AddFontItems_( SfxItemPool& rPool, sal_uInt16 nW ) +{ + const SvxFontItem* pFont = static_cast<const SvxFontItem*>(&rPool.GetDefaultItem( nW )); + AddFontItem( rPool, *pFont ); + + if( nullptr != ( pFont = static_cast<const SvxFontItem*>(rPool.GetPoolDefaultItem( nW ))) ) + AddFontItem( rPool, *pFont ); + + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nW)) + AddFontItem( rPool, *static_cast<const SvxFontItem*>(pItem) ); +} + +void Writer::AddFontItem( SfxItemPool& rPool, const SvxFontItem& rFont ) +{ + const SvxFontItem* pItem; + if( RES_CHRATR_FONT != rFont.Which() ) + { + SvxFontItem aFont( rFont ); + aFont.SetWhich( RES_CHRATR_FONT ); + pItem = &rPool.Put( aFont ); + } + else + pItem = &rPool.Put( rFont ); + + if( 1 < pItem->GetRefCount() ) + rPool.Remove( *pItem ); + else + { + m_pImpl->aFontRemoveLst.push_back( pItem ); + } +} + +// build a bookmark table, which is sort by the node position. The +// OtherPos of the bookmarks also inserted. +void Writer::CreateBookmarkTable() +{ + const IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getBookmarksBegin(); + ppBkmk != pMarkAccess->getBookmarksEnd(); + ++ppBkmk) + { + m_pImpl->InsertBkmk(**ppBkmk); + } +} + +// search all Bookmarks in the range and return it in the Array +bool Writer::GetBookmarks(const SwContentNode& rNd, sal_Int32 nStt, + sal_Int32 nEnd, std::vector< const ::sw::mark::IMark* >& rArr) +{ + OSL_ENSURE( rArr.empty(), "there are still entries available" ); + + sal_uLong nNd = rNd.GetIndex(); + std::pair<SwBookmarkNodeTable::const_iterator, SwBookmarkNodeTable::const_iterator> aIterPair + = m_pImpl->aBkmkNodePos.equal_range( nNd ); + if( aIterPair.first != aIterPair.second ) + { + // there exist some bookmarks, search now all which is in the range + if( !nStt && nEnd == rNd.Len() ) + // all + for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it ) + rArr.push_back( it->second ); + else + { + for( SwBookmarkNodeTable::const_iterator it = aIterPair.first; it != aIterPair.second; ++it ) + { + const ::sw::mark::IMark& rBkmk = *(it->second); + sal_Int32 nContent; + if( rBkmk.GetMarkPos().nNode == nNd && + (nContent = rBkmk.GetMarkPos().nContent.GetIndex() ) >= nStt && + nContent < nEnd ) + { + rArr.push_back( &rBkmk ); + } + else if( rBkmk.IsExpanded() && nNd == + rBkmk.GetOtherMarkPos().nNode.GetIndex() && (nContent = + rBkmk.GetOtherMarkPos().nContent.GetIndex() ) >= nStt && + nContent < nEnd ) + { + rArr.push_back( &rBkmk ); + } + } + } + } + return !rArr.empty(); +} + +// Storage-specific +ErrCode StgWriter::WriteStream() +{ + OSL_ENSURE( false, "Write in Storages on a stream?" ); + return ERR_SWG_WRITE_ERROR; +} + +ErrCode StgWriter::Write( SwPaM& rPaM, SotStorage& rStg, const OUString* pFName ) +{ + SetStream(nullptr); + m_pStg = &rStg; + m_pDoc = rPaM.GetDoc(); + m_pOrigFileName = pFName; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCode nRet = WriteStorage(); + + m_pStg = nullptr; + ResetWriter(); + + return nRet; +} + +ErrCode StgWriter::Write( SwPaM& rPaM, const uno::Reference < embed::XStorage >& rStg, const OUString* pFName, SfxMedium* pMedium ) +{ + SetStream(nullptr); + m_pStg = nullptr; + m_xStg = rStg; + m_pDoc = rPaM.GetDoc(); + m_pOrigFileName = pFName; + + // Copy PaM, so that it can be modified + m_pCurrentPam = m_pDoc->CreateUnoCursor(*rPaM.End(), false); + m_pCurrentPam->SetMark(); + *m_pCurrentPam->GetPoint() = *rPaM.Start(); + // for comparison secure to the current Pam + m_pOrigPam = &rPaM; + + ErrCode nRet = pMedium ? WriteMedium( *pMedium ) : WriteStorage(); + + m_pStg = nullptr; + ResetWriter(); + + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/writer/wrt_fn.cxx b/sw/source/filter/writer/wrt_fn.cxx new file mode 100644 index 000000000..ded9420df --- /dev/null +++ b/sw/source/filter/writer/wrt_fn.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/itemiter.hxx> +#include <svl/whiter.hxx> +#include <osl/diagnose.h> + +// tdf#94088 SdrAllFillAttributesHelper needed +#include <svx/unobrushitemhelper.hxx> + +#include <shellio.hxx> +#include <wrt_fn.hxx> +#include <node.hxx> + +Writer& Out( const SwAttrFnTab pTab, const SfxPoolItem& rHt, Writer & rWrt ) +{ + sal_uInt16 nId = rHt.Which(); + OSL_ENSURE( nId < POOLATTR_END && nId >= POOLATTR_BEGIN, "SwAttrFnTab::Out()" ); + FnAttrOut pOut; + if( nullptr != ( pOut = pTab[ nId - RES_CHRATR_BEGIN] )) + (*pOut)( rWrt, rHt ); + return rWrt; + +} + +Writer& Out_SfxItemSet( const SwAttrFnTab pTab, Writer& rWrt, + const SfxItemSet& rSet, bool bDeep ) +{ + // at first give the own attributes out + const SfxItemPool& rPool = *rSet.GetPool(); + const SfxItemSet* pSet = &rSet; + if( !pSet->Count() ) // Optimizing - empty Sets + { + if( !bDeep ) + return rWrt; + while( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() ) + ; + if( !pSet ) + return rWrt; + } + const SfxPoolItem* pItem(nullptr); + FnAttrOut pOut; + + // tdf#94088 check if any FillAttribute is used [XATTR_FILL_FIRST .. XATTR_FILL_LAST] + // while processing the items + bool bFillItemUsed = false; + + if( !bDeep || !pSet->GetParent() ) + { + OSL_ENSURE( rSet.Count(), "It has been handled already, right?" ); + SfxItemIter aIter( *pSet ); + pItem = aIter.GetCurItem(); + do { + // pTab only covers POOLATTR_BEGIN..POOLATTR_END. + if( pItem->Which() <= POOLATTR_END ) + { + if( nullptr != ( pOut = pTab[ pItem->Which() - RES_CHRATR_BEGIN]) ) + { + (*pOut)( rWrt, *pItem ); + } + } + else if(XATTR_FILLSTYLE == pItem->Which()) + { + bFillItemUsed = true; + } + } while ((pItem = aIter.NextItem())); + } + else + { + SfxWhichIter aIter( *pSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + if( SfxItemState::SET == pSet->GetItemState( nWhich, bDeep, &pItem ) && + ( *pItem != rPool.GetDefaultItem( nWhich ) + || ( pSet->GetParent() && + *pItem != pSet->GetParent()->Get( nWhich )) + )) + { + if( nullptr != ( pOut = pTab[ nWhich - RES_CHRATR_BEGIN] )) + { + (*pOut)( rWrt, *pItem ); + } + else if(XATTR_FILLSTYLE == pItem->Which()) + { + bFillItemUsed = true; + } + } + nWhich = aIter.NextWhich(); + } + } + + if(bFillItemUsed) + { + // tdf#94088 if used, construct a SvxBrushItem and export it using the + // existing mechanisms. + // This is the right place in the future if the adapted fill attributes + // may be handled more directly in HTML export to handle them. + const std::unique_ptr<SvxBrushItem> aSvxBrushItem(getSvxBrushItemFromSourceSet(*pSet, RES_BACKGROUND, bDeep)); + + if( nullptr != ( pOut = pTab[RES_BACKGROUND - RES_CHRATR_BEGIN] )) + { + (*pOut)( rWrt, *aSvxBrushItem ); + } + } + + return rWrt; +} + +Writer& Out( const SwNodeFnTab pTab, SwNode& rNode, Writer & rWrt ) +{ + // It must be a ContentNode! + SwContentNode * pCNd = rNode.GetContentNode(); + if( !pCNd ) + return rWrt; + + sal_uInt16 nId = RES_TXTNODE; + switch (pCNd->GetNodeType()) + { + case SwNodeType::Text: + nId = RES_TXTNODE; + break; + case SwNodeType::Grf: + nId = RES_GRFNODE; + break; + case SwNodeType::Ole: + nId = RES_OLENODE; + break; + default: + OSL_FAIL("What kind of node is it now?"); + break; + } + FnNodeOut pOut; + if( nullptr != ( pOut = pTab[ nId - RES_NODE_BEGIN ] )) + (*pOut)( rWrt, *pCNd ); + return rWrt; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/writer/wrtswtbl.cxx b/sw/source/filter/writer/wrtswtbl.cxx new file mode 100644 index 000000000..5135ee64e --- /dev/null +++ b/sw/source/filter/writer/wrtswtbl.cxx @@ -0,0 +1,885 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <tools/fract.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <wrtswtbl.hxx> +#include <swtable.hxx> +#include <frmfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <htmltbl.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; + +sal_Int16 SwWriteTableCell::GetVertOri() const +{ + sal_Int16 eCellVertOri = text::VertOrientation::TOP; + if( pBox->GetSttNd() ) + { + const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, false, &pItem ) ) + { + sal_Int16 eBoxVertOri = + static_cast<const SwFormatVertOrient *>(pItem)->GetVertOrient(); + if( text::VertOrientation::CENTER==eBoxVertOri || text::VertOrientation::BOTTOM==eBoxVertOri) + eCellVertOri = eBoxVertOri; + } + } + + return eCellVertOri; +} + +SwWriteTableRow::SwWriteTableRow( long nPosition, bool bUseLayoutHeights ) + : pBackground(nullptr), nPos(nPosition), mbUseLayoutHeights(bUseLayoutHeights), + nTopBorder(USHRT_MAX), nBottomBorder(USHRT_MAX), bTopBorder(true), + bBottomBorder(true) +{ +} + +SwWriteTableCell *SwWriteTableRow::AddCell( const SwTableBox *pBox, + sal_uInt16 nRow, sal_uInt16 nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + long nHeight, + const SvxBrushItem *pBackgroundBrush ) +{ + SwWriteTableCell *pCell = + new SwWriteTableCell( pBox, nRow, nCol, nRowSpan, nColSpan, + nHeight, pBackgroundBrush ); + m_Cells.push_back(std::unique_ptr<SwWriteTableCell>(pCell)); + + return pCell; +} + +SwWriteTableCol::SwWriteTableCol(sal_uInt32 nPosition) + : nPos(nPosition), nWidthOpt(0), bRelWidthOpt(false), + bLeftBorder(true), bRightBorder(true) +{ +} + +sal_uInt32 SwWriteTable::GetBoxWidth( const SwTableBox *pBox ) +{ + const SwFrameFormat *pFormat = pBox->GetFrameFormat(); + const SwFormatFrameSize& aFrameSize= + pFormat->GetFormatAttr( RES_FRM_SIZE ); + + return sal::static_int_cast<sal_uInt32>(aFrameSize.GetSize().Width()); +} + +long SwWriteTable::GetLineHeight( const SwTableLine *pLine ) +{ +#ifdef DBG_UTIL + bool bOldGetLineHeightCalled = m_bGetLineHeightCalled; + m_bGetLineHeightCalled = true; +#endif + + long nHeight = 0; + if( m_bUseLayoutHeights ) + { + // At first we try to get the height of the layout. + bool bLayoutAvailable = false; + nHeight = pLine->GetTableLineHeight(bLayoutAvailable); + if( nHeight > 0 ) + return nHeight; + + // If no layout is found, we assume that the heights are fixed. + // #i60390# - in some cases we still want to continue + // to use the layout heights even if one of the rows has a height of 0 + // ('hidden' rows) + m_bUseLayoutHeights = bLayoutAvailable; + +#ifdef DBG_UTIL + SAL_WARN_IF( !bLayoutAvailable && bOldGetLineHeightCalled, "sw", "Layout invalid?" ); +#endif + } + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + for( auto pBox : rBoxes ) + { + if( pBox->GetSttNd() ) + { + if( nHeight < ROW_DFLT_HEIGHT ) + nHeight = ROW_DFLT_HEIGHT; + } + else + { + long nTmp = 0; + const SwTableLines &rLines = pBox->GetTabLines(); + for( size_t nLine=0; nLine<rLines.size(); nLine++ ) + { + nTmp += GetLineHeight( rLines[nLine] ); + } + if( nHeight < nTmp ) + nHeight = nTmp; + } + } + + return nHeight; +} + +long SwWriteTable::GetLineHeight( const SwTableBox *pBox ) +{ + const SwTableLine *pLine = pBox->GetUpper(); + + if( !pLine ) + return 0; + + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + long nHeight = 0; + if( SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem )) + nHeight = static_cast<const SwFormatFrameSize*>(pItem)->GetHeight(); + + return nHeight; +} + +const SvxBrushItem *SwWriteTable::GetLineBrush( const SwTableBox *pBox, + SwWriteTableRow *pRow ) +{ + const SwTableLine *pLine = pBox->GetUpper(); + + while( pLine ) + { + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + if( !pLine->GetUpper() ) + { + if( !pRow->GetBackground() ) + pRow->SetBackground( static_cast<const SvxBrushItem *>(pItem) ); + pItem = nullptr; + } + + return static_cast<const SvxBrushItem *>(pItem); + } + + pBox = pLine->GetUpper(); + pLine = pBox ? pBox->GetUpper() : nullptr; + } + + return nullptr; +} + +void SwWriteTable::MergeBorders( const SvxBorderLine* pBorderLine, + bool bTable ) +{ + if( Color(0xffffffff) == m_nBorderColor ) + { + if( !pBorderLine->GetColor().IsRGBEqual( COL_GRAY ) ) + m_nBorderColor = pBorderLine->GetColor(); + } + + if( !m_bCollectBorderWidth ) + return; + + const sal_uInt16 nOutWidth = pBorderLine->GetOutWidth(); + if( bTable ) + { + if( nOutWidth && (!m_nBorder || nOutWidth < m_nBorder) ) + m_nBorder = nOutWidth; + } + else + { + if( nOutWidth && (!m_nInnerBorder || nOutWidth < m_nInnerBorder) ) + m_nInnerBorder = nOutWidth; + } + + const sal_uInt16 nDist = pBorderLine->GetInWidth() ? pBorderLine->GetDistance() + : 0; + if( nDist && (!m_nCellSpacing || nDist < m_nCellSpacing) ) + m_nCellSpacing = nDist; +} + +sal_uInt16 SwWriteTable::MergeBoxBorders( const SwTableBox *pBox, + size_t const nRow, size_t const nCol, + sal_uInt16 nRowSpan, sal_uInt16 nColSpan, + sal_uInt16& rTopBorder, + sal_uInt16 &rBottomBorder ) +{ + sal_uInt16 nBorderMask = 0; + + const SwFrameFormat *pFrameFormat = pBox->GetFrameFormat(); + const SvxBoxItem& rBoxItem = pFrameFormat->GetFormatAttr( RES_BOX ); + + if( rBoxItem.GetTop() ) + { + nBorderMask |= 1; + MergeBorders( rBoxItem.GetTop(), nRow==0 ); + rTopBorder = rBoxItem.GetTop()->GetOutWidth(); + } + + if( rBoxItem.GetLeft() ) + { + nBorderMask |= 4; + MergeBorders( rBoxItem.GetLeft(), nCol==0 ); + } + + if( rBoxItem.GetBottom() ) + { + nBorderMask |= 2; + MergeBorders( rBoxItem.GetBottom(), nRow+nRowSpan==m_aRows.size() ); + rBottomBorder = rBoxItem.GetBottom()->GetOutWidth(); + } + + if( rBoxItem.GetRight() ) + { + nBorderMask |= 8; + MergeBorders( rBoxItem.GetRight(), nCol+nColSpan==m_aCols.size() ); + } + + // If any distance is set, the smallest one is used. This holds for + // the four distance of a box as well as for the distances of different + // boxes. + if( m_bCollectBorderWidth ) + { + sal_uInt16 nDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::LEFT ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + nDist = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT ); + if( nDist && (!m_nCellPadding || nDist < m_nCellPadding) ) + m_nCellPadding = nDist; + } + + return nBorderMask; +} + +sal_uInt32 SwWriteTable::GetRawWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + sal_uInt32 nWidth = m_aCols[nCol+nColSpan-1]->GetPos(); + if( nCol > 0 ) + nWidth = nWidth - m_aCols[nCol-1]->GetPos(); + + return nWidth; +} + +sal_uInt16 SwWriteTable::GetLeftSpace( sal_uInt16 nCol ) const +{ + sal_uInt16 nSpace = m_nCellPadding + m_nCellSpacing; + + // Additional subtract the line thickness in the first column. + if( nCol==0 ) + { + nSpace = nSpace + m_nLeftSub; + + const SwWriteTableCol *pCol = m_aCols[nCol].get(); + if( pCol->HasLeftBorder() ) + nSpace = nSpace + m_nBorder; + } + + return nSpace; +} + +sal_uInt16 +SwWriteTable::GetRightSpace(size_t const nCol, sal_uInt16 nColSpan) const +{ + sal_uInt16 nSpace = m_nCellPadding; + + // Additional subtract in the last column CELLSPACING and + // line thickness once again. + if( nCol+nColSpan==m_aCols.size() ) + { + nSpace += (m_nCellSpacing + m_nRightSub); + + const SwWriteTableCol *pCol = m_aCols[nCol+nColSpan-1].get(); + if( pCol->HasRightBorder() ) + nSpace = nSpace + m_nBorder; + } + + return nSpace; +} + +sal_uInt16 SwWriteTable::GetAbsWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + sal_uInt32 nWidth = GetRawWidth( nCol, nColSpan ); + if( m_nBaseWidth != m_nTabWidth ) + { + nWidth *= m_nTabWidth; + nWidth /= m_nBaseWidth; + } + + nWidth -= GetLeftSpace( nCol ) + GetRightSpace( nCol, nColSpan ); + + OSL_ENSURE( nWidth > 0, "Column Width <= 0. OK?" ); + return nWidth > 0 ? static_cast<sal_uInt16>(nWidth) : 0; +} + +sal_uInt16 SwWriteTable::GetRelWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + long nWidth = GetRawWidth( nCol, nColSpan ); + + return static_cast<sal_uInt16>(static_cast<long>(Fraction( nWidth*256 + GetBaseWidth()/2, + GetBaseWidth() ))); +} + +sal_uInt16 SwWriteTable::GetPercentWidth( sal_uInt16 nCol, sal_uInt16 nColSpan ) const +{ + long nWidth = GetRawWidth( nCol, nColSpan ); + + // Looks funny, but is nothing more than + // [(100 * nWidth) + .5] without rounding errors + return static_cast<sal_uInt16>(static_cast<long>(Fraction( nWidth*100 + GetBaseWidth()/2, + GetBaseWidth() ))); +} + +long SwWriteTable::GetAbsHeight(long nRawHeight, size_t const nRow, + sal_uInt16 nRowSpan ) const +{ + nRawHeight -= (2*m_nCellPadding + m_nCellSpacing); + + // Additional subtract in the first column CELLSPACING and + // line thickness once again. + const SwWriteTableRow *pRow = nullptr; + if( nRow==0 ) + { + nRawHeight -= m_nCellSpacing; + pRow = m_aRows[nRow].get(); + if( pRow->HasTopBorder() ) + nRawHeight -= m_nBorder; + } + + // Subtract the line thickness in the last column + if( nRow+nRowSpan==m_aRows.size() ) + { + if( !pRow || nRowSpan > 1 ) + pRow = m_aRows[nRow+nRowSpan-1].get(); + if( pRow->HasBottomBorder() ) + nRawHeight -= m_nBorder; + } + + OSL_ENSURE( nRawHeight > 0, "Row Height <= 0. OK?" ); + return std::max<long>(nRawHeight, 0); +} + +bool SwWriteTable::ShouldExpandSub(const SwTableBox *pBox, bool /*bExpandedBefore*/, + sal_uInt16 nDepth) const +{ + return !pBox->GetSttNd() && nDepth > 0; +} + +// FIXME: the degree of coupling between this method and +// FillTableRowsCols which is called immediately afterwards +// is -extremely- unpleasant and potentially problematic. + +void SwWriteTable::CollectTableRowsCols( long nStartRPos, + sal_uInt32 nStartCPos, + long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + sal_uInt16 nDepth ) +{ + bool bSubExpanded = false; + const SwTableLines::size_type nLines = rLines.size(); + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nEndCPos = 0; +#endif + + long nRPos = nStartRPos; + for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine ) + { + /*const*/ SwTableLine *pLine = rLines[nLine]; + + long nOldRPos = nRPos; + + if( nLine < nLines-1 || nParentLineHeight==0 ) + { + long nLineHeight = GetLineHeight( pLine ); + nRPos += nLineHeight; + if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos ) + { + /* If you have corrupt line height information, e.g. breaking rows in complex table + layout, you may run into this robust code. + It's not allowed that subrows leaves their parentrow. If this would happen the line + height of subrow is reduced to a part of the remaining height */ + OSL_FAIL( "Corrupt line height I" ); + nRPos -= nLineHeight; + nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height + nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows + nRPos += nLineHeight; + } + std::unique_ptr<SwWriteTableRow> pRow(new SwWriteTableRow( nRPos, m_bUseLayoutHeights)); + m_aRows.insert( std::move(pRow) ); + } + else + { +#if OSL_DEBUG_LEVEL > 0 + long nCheckPos = nRPos + GetLineHeight( pLine ); +#endif + nRPos = nStartRPos + nParentLineHeight; +#if OSL_DEBUG_LEVEL > 0 + SwWriteTableRow aSrchRow( nRPos, m_bUseLayoutHeights ); + OSL_ENSURE( std::find_if(m_aRows.begin(), m_aRows.end(), + [&](std::unique_ptr<SwWriteTableRow> const & p) + { return *p == aSrchRow; }) != m_aRows.end(), "Parent-Row not found" ); + SwWriteTableRow aRowCheckPos(nCheckPos,m_bUseLayoutHeights); + SwWriteTableRow aRowRPos(nRPos,m_bUseLayoutHeights); + OSL_ENSURE( !m_bUseLayoutHeights || + aRowCheckPos == aRowRPos, + "Height of the rows does not correspond with the parent" ); +#endif + } + + // If necessary insert a column for all boxes of the row + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const SwTableBoxes::size_type nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = nStartCPos; + for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + sal_uInt32 nOldCPos = nCPos; + + if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) ) + { + nCPos = nCPos + GetBoxWidth( pBox ); + std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nCPos )); + + m_aCols.insert( std::move(pCol) ); + + if( nBox==nBoxes-1 ) + { + OSL_ENSURE( nLine==0 && nParentLineWidth==0, + "Now the parent width will be flattened!" ); + nParentLineWidth = nCPos-nStartCPos; + } + } + else + { +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nCheckPos = nCPos + GetBoxWidth( pBox ); + if( !nEndCPos ) + { + nEndCPos = nCheckPos; + } + else + { + OSL_ENSURE( SwWriteTableCol(nCheckPos) == + SwWriteTableCol(nEndCPos), + "Cell includes rows of different widths" ); + } +#endif + nCPos = nStartCPos + nParentLineWidth; + +#if OSL_DEBUG_LEVEL > 0 + SwWriteTableCol aSrchCol( nCPos ); + OSL_ENSURE( m_aCols.find( &aSrchCol ) != m_aCols.end(), + "Parent-Cell not found" ); + OSL_ENSURE( SwWriteTableCol(nCheckPos) == + SwWriteTableCol(nCPos), + "Width of the cells does not correspond with the parent" ); +#endif + } + + if( ShouldExpandSub( pBox, bSubExpanded, nDepth ) ) + { + CollectTableRowsCols( nOldRPos, nOldCPos, + nRPos - nOldRPos, + nCPos - nOldCPos, + pBox->GetTabLines(), + nDepth-1 ); + bSubExpanded = true; + } + } + } +} + +void SwWriteTable::FillTableRowsCols( long nStartRPos, sal_uInt16 nStartRow, + sal_uInt32 nStartCPos, sal_uInt16 nStartCol, + long nParentLineHeight, + sal_uInt32 nParentLineWidth, + const SwTableLines& rLines, + const SvxBrushItem* pParentBrush, + sal_uInt16 nDepth, + sal_uInt16 nNumOfHeaderRows ) +{ + const SwTableLines::size_type nLines = rLines.size(); + bool bSubExpanded = false; + + // Specifying the border + long nRPos = nStartRPos; + sal_uInt16 nRow = nStartRow; + + for( SwTableLines::size_type nLine = 0; nLine < nLines; ++nLine ) + { + const SwTableLine *pLine = rLines[nLine]; + + // Determine the position of the last covered row + long nOldRPos = nRPos; + if( nLine < nLines-1 || nParentLineHeight==0 ) + { + long nLineHeight = GetLineHeight( pLine ); + nRPos += nLineHeight; + if( nParentLineHeight && nStartRPos + nParentLineHeight <= nRPos ) + { + /* See comment in CollectTableRowCols */ + OSL_FAIL( "Corrupt line height II" ); + nRPos -= nLineHeight; + nLineHeight = nStartRPos + nParentLineHeight - nRPos; // remaining parent height + nLineHeight /= nLines - nLine; // divided through the number of remaining sub rows + nRPos += nLineHeight; + } + } + else + nRPos = nStartRPos + nParentLineHeight; + + // And their index + sal_uInt16 nOldRow = nRow; + SwWriteTableRow aSrchRow( nRPos,m_bUseLayoutHeights ); + SwWriteTableRows::const_iterator it2 = std::find_if(m_aRows.begin(), m_aRows.end(), + [&](std::unique_ptr<SwWriteTableRow> const &p) + { return *p == aSrchRow; }); + + // coupled methods out of sync ... + assert( it2 != m_aRows.end() ); + nRow = it2 - m_aRows.begin(); + + OSL_ENSURE( nOldRow <= nRow, "Don't look back!" ); + if( nOldRow > nRow ) + { + nOldRow = nRow; + if( nOldRow ) + --nOldRow; + } + + SwWriteTableRow *pRow = m_aRows[nOldRow].get(); + SwWriteTableRow *pEndRow = m_aRows[nRow].get(); + if( nLine+1==nNumOfHeaderRows && nParentLineHeight==0 ) + m_nHeadEndRow = nRow; + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + + const SwFrameFormat *pLineFrameFormat = pLine->GetFrameFormat(); + const SfxPoolItem* pItem; + const SfxItemSet& rItemSet = pLineFrameFormat->GetAttrSet(); + + long nHeight = 0; + if( SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, true, &pItem )) + nHeight = static_cast<const SwFormatFrameSize*>(pItem)->GetHeight(); + + const SvxBrushItem *pBrushItem, *pLineBrush = pParentBrush; + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + pLineBrush = static_cast<const SvxBrushItem *>(pItem); + + // If the row spans the entire table, we can + // print out the background to the row. Otherwise + // we have to print out into the cell. + bool bOutAtRow = !nParentLineWidth; + if( !bOutAtRow && nStartCPos==0 ) + { + SwWriteTableCol aCol( nParentLineWidth ); + bOutAtRow = m_aCols.find( &aCol ) == (m_aCols.end() - 1); + } + if( bOutAtRow ) + { + pRow->SetBackground( pLineBrush ); + pBrushItem = nullptr; + } + else + pBrushItem = pLineBrush; + } + else + { + pRow->SetBackground( pLineBrush ); + pBrushItem = nullptr; + } + + const SwTableBoxes::size_type nBoxes = rBoxes.size(); + sal_uInt32 nCPos = nStartCPos; + sal_uInt16 nCol = nStartCol; + + for( SwTableBoxes::size_type nBox=0; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + // Determine the position of the last covered column + sal_uInt32 nOldCPos = nCPos; + if( nBox < nBoxes-1 || (nParentLineWidth==0 && nLine==0) ) + { + nCPos = nCPos + GetBoxWidth( pBox ); + if( nBox==nBoxes-1 ) + nParentLineWidth = nCPos - nStartCPos; + } + else + nCPos = nStartCPos + nParentLineWidth; + + // And their index + sal_uInt16 nOldCol = nCol; + SwWriteTableCol aSrchCol( nCPos ); + SwWriteTableCols::const_iterator it = m_aCols.find( &aSrchCol ); + OSL_ENSURE( it != m_aCols.end(), "missing column" ); + if(it != m_aCols.end()) + { + // if find fails for some nCPos value then it used to set nCol value with size of aCols. + nCol = it - m_aCols.begin(); + } + + if( !ShouldExpandSub( pBox, bSubExpanded, nDepth ) ) + { + sal_uInt16 nRowSpan = nRow - nOldRow + 1; + + // The new table model may have true row span attributes + const long nAttrRowSpan = pBox->getRowSpan(); + if ( 1 < nAttrRowSpan ) + nRowSpan = static_cast<sal_uInt16>(nAttrRowSpan); + else if ( nAttrRowSpan < 1 ) + nRowSpan = 0; + + SAL_WARN_IF(nCol < nOldCol, "sw.filter", "unexpected " << nCol << " < " << nOldCol); + sal_uInt16 nColSpan = nCol >= nOldCol ? nCol - nOldCol + 1 : 1; + pRow->AddCell( pBox, nOldRow, nOldCol, + nRowSpan, nColSpan, nHeight, + pBrushItem ); + nHeight = 0; // The height requires only to be written once + + if( pBox->GetSttNd() ) + { + sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX; + sal_uInt16 nBorderMask = MergeBoxBorders(pBox, nOldRow, nOldCol, + nRowSpan, nColSpan, nTopBorder, nBottomBorder); + + // #i30094# add a sanity check here to ensure that + // we don't access an invalid aCols[] as &nCol + // above can be changed. + if (!(nBorderMask & 4) && nOldCol < m_aCols.size()) + { + SwWriteTableCol *pCol = m_aCols[nOldCol].get(); + OSL_ENSURE(pCol, "No TableCol found, panic!"); + if (pCol) + pCol->bLeftBorder = false; + } + + if (!(nBorderMask & 8)) + { + SwWriteTableCol *pCol = m_aCols[nCol].get(); + OSL_ENSURE(pCol, "No TableCol found, panic!"); + if (pCol) + pCol->bRightBorder = false; + } + + if (!(nBorderMask & 1)) + pRow->bTopBorder = false; + else if (!pRow->nTopBorder || nTopBorder < pRow->nTopBorder) + pRow->nTopBorder = nTopBorder; + + if (!(nBorderMask & 2)) + pEndRow->bBottomBorder = false; + else if ( + !pEndRow->nBottomBorder || + nBottomBorder < pEndRow->nBottomBorder + ) + { + pEndRow->nBottomBorder = nBottomBorder; + } + } + } + else + { + FillTableRowsCols( nOldRPos, nOldRow, nOldCPos, nOldCol, + nRPos-nOldRPos, nCPos-nOldCPos, + pBox->GetTabLines(), + pLineBrush, nDepth-1, + nNumOfHeaderRows ); + bSubExpanded = true; + } + + nCol++; // The next cell begins in the next column + } + + nRow++; + } +} + +SwWriteTable::SwWriteTable(const SwTable* pTable, const SwTableLines& rLines, long nWidth, + sal_uInt32 nBWidth, bool bRel, sal_uInt16 nMaxDepth, sal_uInt16 nLSub, sal_uInt16 nRSub, sal_uInt32 nNumOfRowsToRepeat) + : m_pTable(pTable), m_nBorderColor(sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0), + m_nInnerBorder(0), m_nBaseWidth(nBWidth), m_nHeadEndRow(USHRT_MAX), + m_nLeftSub(nLSub), m_nRightSub(nRSub), m_nTabWidth(nWidth), m_bRelWidths(bRel), + m_bUseLayoutHeights(true), +#ifdef DBG_UTIL + m_bGetLineHeightCalled(false), +#endif + m_bColTags(true), m_bLayoutExport(false), + m_bCollectBorderWidth(true) +{ + sal_uInt32 nParentWidth = m_nBaseWidth + m_nLeftSub + m_nRightSub; + + // First the table structure set. Behind the table is in each + // case the end of a column + std::unique_ptr<SwWriteTableCol> pCol(new SwWriteTableCol( nParentWidth )); + m_aCols.insert( std::move(pCol) ); + m_bUseLayoutHeights = true; + CollectTableRowsCols( 0, 0, 0, nParentWidth, rLines, nMaxDepth - 1 ); + + // FIXME: awfully GetLineHeight writes to this in its first call + // and proceeds to return a rather odd number fdo#62336, we have to + // behave identically since the code in FillTableRowsCols duplicates + // and is highly coupled to CollectTableRowsCols - sadly. + m_bUseLayoutHeights = true; + // And now fill with life + FillTableRowsCols( 0, 0, 0, 0, 0, nParentWidth, rLines, nullptr, nMaxDepth - 1, static_cast< sal_uInt16 >(nNumOfRowsToRepeat) ); + + // Adjust some Twip values to pixel boundaries + if( !m_nBorder ) + m_nBorder = m_nInnerBorder; +} + +SwWriteTable::SwWriteTable(const SwTable* pTable, const SwHTMLTableLayout *pLayoutInfo) + : m_pTable(pTable), m_nBorderColor(sal_uInt32(-1)), m_nCellSpacing(0), m_nCellPadding(0), m_nBorder(0), + m_nInnerBorder(0), m_nBaseWidth(pLayoutInfo->GetWidthOption()), m_nHeadEndRow(0), + m_nLeftSub(0), m_nRightSub(0), m_nTabWidth(pLayoutInfo->GetWidthOption()), + m_bRelWidths(pLayoutInfo->HasPercentWidthOption()), m_bUseLayoutHeights(false), +#ifdef DBG_UTIL + m_bGetLineHeightCalled(false), +#endif + m_bColTags(pLayoutInfo->HasColTags()), m_bLayoutExport(true), + m_bCollectBorderWidth(pLayoutInfo->HaveBordersChanged()) +{ + if( !m_bCollectBorderWidth ) + { + m_nBorder = pLayoutInfo->GetBorder(); + m_nCellPadding = pLayoutInfo->GetCellPadding(); + m_nCellSpacing = pLayoutInfo->GetCellSpacing(); + } + + const sal_uInt16 nCols = pLayoutInfo->GetColCount(); + const sal_uInt16 nRows = pLayoutInfo->GetRowCount(); + + // First set the table structure. + for( sal_uInt16 nCol=0; nCol<nCols; ++nCol ) + { + std::unique_ptr<SwWriteTableCol> pCol( + new SwWriteTableCol( (nCol+1)*COL_DFLT_WIDTH )); + + if( m_bColTags ) + { + const SwHTMLTableLayoutColumn *pLayoutCol = + pLayoutInfo->GetColumn( nCol ); + pCol->SetWidthOpt( pLayoutCol->GetWidthOption(), + pLayoutCol->IsRelWidthOption() ); + } + + m_aCols.insert( std::move(pCol) ); + } + + for( sal_uInt16 nRow=0; nRow<nRows; ++nRow ) + { + std::unique_ptr<SwWriteTableRow> pRow( + new SwWriteTableRow( (nRow+1)*ROW_DFLT_HEIGHT, m_bUseLayoutHeights )); + pRow->nTopBorder = 0; + pRow->nBottomBorder = 0; + m_aRows.insert( std::move(pRow) ); + } + + // And now fill with life + for( sal_uInt16 nRow=0; nRow<nRows; ++nRow ) + { + SwWriteTableRow *pRow = m_aRows[nRow].get(); + + bool bHeightExported = false; + for( sal_uInt16 nCol=0; nCol<nCols; nCol++ ) + { + const SwHTMLTableLayoutCell *pLayoutCell = + pLayoutInfo->GetCell( nRow, nCol ); + + const SwHTMLTableLayoutCnts *pLayoutCnts = + pLayoutCell->GetContents().get(); + + // The cell begins actually a row above or further forward? + if( ( nRow>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow-1,nCol) + ->GetContents().get() ) || + ( nCol>0 && pLayoutCnts == pLayoutInfo->GetCell(nRow,nCol-1) + ->GetContents().get() ) ) + { + continue; + } + + const sal_uInt16 nRowSpan = pLayoutCell->GetRowSpan(); + const sal_uInt16 nColSpan = pLayoutCell->GetColSpan(); + const SwTableBox *pBox = pLayoutCnts->GetTableBox(); + OSL_ENSURE( pBox, + "Table in Table can not be exported over layout" ); + + long nHeight = bHeightExported ? 0 : GetLineHeight( pBox ); + const SvxBrushItem *pBrushItem = GetLineBrush( pBox, pRow ); + + SwWriteTableCell *pCell = + pRow->AddCell( pBox, nRow, nCol, nRowSpan, nColSpan, + nHeight, pBrushItem ); + pCell->SetWidthOpt( pLayoutCell->GetWidthOption(), + pLayoutCell->IsPercentWidthOption() ); + + sal_uInt16 nTopBorder = USHRT_MAX, nBottomBorder = USHRT_MAX; + sal_uInt16 nBorderMask = + MergeBoxBorders( pBox, nRow, nCol, nRowSpan, nColSpan, + nTopBorder, nBottomBorder ); + + SwWriteTableCol *pCol = m_aCols[nCol].get(); + if( !(nBorderMask & 4) ) + pCol->bLeftBorder = false; + + pCol = m_aCols[nCol+nColSpan-1].get(); + if( !(nBorderMask & 8) ) + pCol->bRightBorder = false; + + if( !(nBorderMask & 1) ) + pRow->bTopBorder = false; + + SwWriteTableRow *pEndRow = m_aRows[nRow+nRowSpan-1].get(); + if( !(nBorderMask & 2) ) + pEndRow->bBottomBorder = false; + + // The height requires only to be written once + if( nHeight ) + bHeightExported = true; + } + } + + // Adjust some Twip values to pixel boundaries + if( m_bCollectBorderWidth && !m_nBorder ) + m_nBorder = m_nInnerBorder; +} + +SwWriteTable::~SwWriteTable() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/README-rtf.txt b/sw/source/filter/ww8/README-rtf.txt new file mode 100644 index 000000000..d92cf587f --- /dev/null +++ b/sw/source/filter/ww8/README-rtf.txt @@ -0,0 +1,244 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +--------------------------------------------------------------------- + +Summary of new features in RtfExport + +--------------------------------------------------------------------- + +Miklos Vajna + +<vmiklos@vmiklos.hu> +--------------------------------------------------------------------- + +Table of Contents + +1. Introduction + + 1.1. Terminology + 1.2. General + +2. List if fixed bugs +3. List of new features + + 3.1. Nested tables + 3.2. Character properties + 3.3. Sections + 3.4. Graphics + 3.5. Bookmarks + 3.6. Fields + 3.7. Drawing + 3.8. Form fields + 3.9. OLE objects + +4. Changes in the source code outside RTF + + +--------------------------------------------------------------------- + +1.�Introduction + +--------------------------------------------------------------------- + +The biggest difference is that the new exporter is a UNO component, +and it?s based on the MSWord base classes, the vision here is that +this way much less code can achieve the same set of features, +reducing the amount of duplicated code. + + +1.1.�Terminology + +-------------- + + * The "MSO OK, OOo KO" and similar abbreviations describe if the + given new feature is supported by the OOo RTF importer or it can + be tested using Microsoft Office. + * RtfExport refers to the new UNO-based exporter, RtfWriter refers + to the old built-in one. + + +1.2.�General + +-------------- + +RtfWriter sometimes created documents where the first { is closed in +the middle of the document. MSO ignores this problem, but OOo stops +parsing the rest of the document if this happens, in other words +everything after such a bug is ignored. This can be reproduced by for +example parprops.odt, but it?s triggered in several other cases as +well. RtfExport has no automatic prevention for this, either - but +during development I primarily test the output with OOo, so hopefully +the bug will pop up less frequently. + + +--------------------------------------------------------------------- + +2.�List if fixed bugs + +--------------------------------------------------------------------- + + * https://bz.apache.org/ooo/show_bug.cgi?id=51469 postit + fields + * https://bz.apache.org/ooo/show_bug.cgi?id=66619 page + margins + * https://bz.apache.org/ooo/show_bug.cgi?id=69856 page + numbers + * https://bz.apache.org/ooo/show_bug.cgi?id=81569 { and } in + document title + * https://bz.apache.org/ooo/show_bug.cgi?id=84703 redlines + * https://bz.apache.org/ooo/show_bug.cgi?id=91166 russian + chars + * https://bz.apache.org/ooo/show_bug.cgi?id=92673 bookmarks + across tables + * https://bz.apache.org/ooo/show_bug.cgi?id=100507 ole + object export + * https://bz.apache.org/ooo/show_bug.cgi?id=103993 same as # + 81569 just for doc comments + * https://bz.apache.org/ooo/show_bug.cgi?id=106677 + listoverride index starts at zero + * https://bz.apache.org/ooo/show_bug.cgi?id=38344 enhanced + character space + + +--------------------------------------------------------------------- + +3.�List of new features + +--------------------------------------------------------------------- + + +3.1.�Nested tables + +-------------- + +This was new in Word2000 and it?s now supported by RtfExport (MSO OK, +OOo KO) + + +3.2.�Character properties + +-------------- + +The following are now supported: + + * blinking (MSO OK, OOo KO) + * expanded spacing (MSO OK, OOo OK) + * pair kerning (MSO OK, OOo OK) + + +3.3.�Sections + +-------------- + +RtfExport writes: + + * column breaks (MSO OK, OOo OK) + * special breaks (when the next page should be an odd or an even + page; MSO OK, OOo KO) + * the write-protected property of sections is exported properly + (MSO OK, OOo KO) + * better page numbers (inherited type from page styles, restarts; + MSO OK, OOo KO) + * line numbering (MSO OK, OOo KO) + + +3.4.�Graphics + +-------------- + +PNG graphics are exported in WMF format as well, so that not only MSO +and OOo can display graphics from the output document, but Wordpad as +well. + + +3.5.�Bookmarks + +-------------- + +Implicit bookmarks like reference to a footnote did not work in OOo +(one got an Error: Reference source not found message when opening +the result), this now works as expected. (MSO OK - the importer +previously autocorrected this as well, OO OK) + + +3.6.�Fields + +-------------- + + * Table of contents is now written as a field, so it?s properly + read-only (MSO OK, OOo KO) + * Postit comments are now exported. (MSO OK, OOo KO) + + +3.7.�Drawing + +-------------- + +Drawing objects for Word 97 through Word 2007 (shapes) are now +implemented: + + * basic shapes (rectangle, ellipse, etc.) + * lines, including free-form ones + * texts, including vertical ones and their (paragraph and + character) formatting + +(MSO OK, OOo KO) + + +3.8.�Form fields + +-------------- + +All types supported by the RTF format are exported, namely: + + * text boxes + * check boxes + * list boxes + +(MSO OK, OOo KO) + + +3.9.�OLE objects + +-------------- + +Their result is exported as a picture - RtfWriter did not export +anything. (MSO OK, OOo OK) + +For math, the native data is written as well, so you can edit the +object, too. (MSO OK, OOo KO) + + +--------------------------------------------------------------------- + +4.�Changes in the source code outside RTF + +--------------------------------------------------------------------- + +These are refactorings I needed for RTF. To my best knowledge they do +not change the output of other filters from a user?s point of view. + + * The code that splits runs according to bookmarks is moved from + DocxExport to MSWordExportBase + * WW8_SdrAttrIter has been refactored to MSWord_SdrAttrIter + * MSWordExportBase::SubstituteBullet can avoid replacing bullets + * wwFontHelper::InitFontTable can really load all fonts + * An obvious typo in WW8AttributeOutput::CharTwoLines has been + fixed + diff --git a/sw/source/filter/ww8/WW8FFData.cxx b/sw/source/filter/ww8/WW8FFData.cxx new file mode 100644 index 000000000..291cd845d --- /dev/null +++ b/sw/source/filter/ww8/WW8FFData.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "WW8FFData.hxx" +#include <tools/stream.hxx> +#include "writerwordglue.hxx" +#include "wrtww8.hxx" + +namespace sw +{ + +using sw::types::msword_cast; + +WW8FFData::WW8FFData() + : + mnType(0), + mnResult(0), + mbOwnHelp(false), + mbOwnStat(false), + mbProtected(false), + mbSize(false), + mnTextType(0), + mbRecalc(false), + mbListBox(false), + mnMaxLen(0), + mnCheckboxHeight(0), + mnDefault(0) +{ +} + +WW8FFData::~WW8FFData() +{ +} + +void WW8FFData::setHelp(const OUString & rHelp) +{ + msHelp = rHelp; + mbOwnHelp = true; +} + +void WW8FFData::setStatus(const OUString & rStatus) +{ + msStatus = rStatus; + mbOwnStat = true; +} + +void WW8FFData::addListboxEntry(const OUString & rEntry) +{ + mbListBox = true; + msListEntries.push_back(rEntry); +} + +void WW8FFData::WriteOUString(SvStream * pDataStrm, const OUString & rStr, + bool bAddZero) +{ + sal_uInt16 nStrLen = msword_cast<sal_uInt16>(rStr.getLength()); + pDataStrm->WriteUInt16( nStrLen ); + SwWW8Writer::WriteString16(*pDataStrm, rStr, bAddZero); +} + +void WW8FFData::Write(SvStream * pDataStrm) +{ + sal_uLong nDataStt = pDataStrm->Tell(); + + static const sal_uInt8 aHeader[] = + { + 0,0,0,0, // len of struct + 0x44,0, // the start of "next" data + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // PIC + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0 + }; + + pDataStrm->WriteBytes(aHeader, sizeof(aHeader)); + + sal_uInt8 aData[10] = { + 0xff, 0xff, 0xff, 0xff, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }; + + aData[4] = mnType | (mnResult << 2); + + if (mbOwnHelp) + aData[4] |= (1 << 7); + + aData[5] = (mnTextType << 3); + + if (mbOwnStat) + aData[5] |= 1; + + if (mbProtected) + aData[5] |= (1 << 1); + + if (mbSize) + aData[5] |= (1 << 2); + + if (mbRecalc) + aData[5] |= (1 << 6); + + if (mbListBox) + aData[5] |= (1 << 7); + + aData[6] = ::sal::static_int_cast<sal_uInt8>(mnMaxLen & 0xffff); + aData[7] = ::sal::static_int_cast<sal_uInt8>(mnMaxLen >> 8); + aData[8] = ::sal::static_int_cast<sal_uInt8>(mnCheckboxHeight & 0xffff); + aData[9] = ::sal::static_int_cast<sal_uInt8>(mnCheckboxHeight >> 8); + + pDataStrm->WriteBytes(aData, sizeof(aData)); + + WriteOUString(pDataStrm, msName, true); + + if (mnType == 0) + WriteOUString(pDataStrm, msDefault, true); + else + pDataStrm->WriteUInt16( mnDefault ); + + WriteOUString(pDataStrm, msFormat, true); + WriteOUString(pDataStrm, msHelp, true); + WriteOUString(pDataStrm, msStatus, true); + WriteOUString(pDataStrm, msMacroEnter, true); + WriteOUString(pDataStrm, msMacroExit, true); + + if (mnType == 2) + { + sal_uInt8 aData1[2] = { 0xff, 0xff }; + pDataStrm->WriteBytes(aData1, sizeof(aData1)); + + sal_uInt32 nListboxEntries = msListEntries.size(); + pDataStrm->WriteUInt32( nListboxEntries ); + + for (const OUString & rEntry : msListEntries) + WriteOUString(pDataStrm, rEntry, false); + } + + SwWW8Writer::WriteLong( *pDataStrm, nDataStt, + pDataStrm->Tell() - nDataStt ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8FFData.hxx b/sw/source/filter/ww8/WW8FFData.hxx new file mode 100644 index 000000000..e1ea582b5 --- /dev/null +++ b/sw/source/filter/ww8/WW8FFData.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_FILTER_WW8_WW8FFDATA_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8FFDATA_HXX + +#include <vector> +#include <rtl/ustring.hxx> + +class SvStream; + +namespace sw +{ + +class WW8FFData final +{ +private: + // offset 0x4 + sal_uInt8 mnType; // :2 0x3 + sal_uInt8 mnResult; // :5 0x7c + bool mbOwnHelp; // :1 0x80 + + // offset 5 + bool mbOwnStat; // :1 0x01 + bool mbProtected; // :1 0x02 + bool mbSize; // :1 0x04 + sal_uInt8 mnTextType; // :3 0x38 + bool mbRecalc; // :1 0x4 + bool mbListBox; // :1 0x80 + + // offset 6 + sal_uInt16 mnMaxLen; // :15 0x7fff maximum length of text field, 0 <=> no limit + + // offset 8 + sal_uInt16 mnCheckboxHeight; + + // offset 10 and beyond + OUString msName; + OUString msDefault; // only for type == 0 + sal_uInt16 mnDefault; // only for type != 0 + OUString msFormat; + OUString msHelp; + OUString msStatus; + OUString msMacroEnter; + OUString msMacroExit; + + std::vector< OUString > msListEntries; + + static void WriteOUString(SvStream * pStream, const OUString & rStr, bool bAddZero); + +public: + WW8FFData(); + ~WW8FFData(); + + void setType(sal_uInt8 nType) { mnType = nType; } + + void setResult(sal_uInt8 nResult) { mnResult = nResult; } + + void setName(const OUString & rName) { msName = rName; } + + void setHelp(const OUString & rHelp); + + void setStatus(const OUString & rStatus); + + void addListboxEntry(const OUString & rEntry); + + void Write(SvStream * pDataStrm); +}; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8FFDATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8FibData.cxx b/sw/source/filter/ww8/WW8FibData.cxx new file mode 100644 index 000000000..2c94015a1 --- /dev/null +++ b/sw/source/filter/ww8/WW8FibData.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 "WW8FibData.hxx" + +namespace ww8 +{ +WW8FibData::WW8FibData() + : m_bReadOnlyRecommended(false), + m_bWriteReservation(false) +{ +} + +WW8FibData::~WW8FibData() +{ +} + +void WW8FibData::setReadOnlyRecommended(bool bReadOnlyRecommended) +{ + m_bReadOnlyRecommended = bReadOnlyRecommended; +} + +void WW8FibData::setWriteReservation(bool bWriteReservation) +{ + m_bWriteReservation = bWriteReservation; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8FibData.hxx b/sw/source/filter/ww8/WW8FibData.hxx new file mode 100644 index 000000000..b47cdf6de --- /dev/null +++ b/sw/source/filter/ww8/WW8FibData.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX +#include <IDocumentExternalData.hxx> + +namespace ww8 +{ +class WW8FibData : public ::sw::ExternalData +{ + bool m_bReadOnlyRecommended; + bool m_bWriteReservation; + +public: + WW8FibData(); + virtual ~WW8FibData() override; + + void setReadOnlyRecommended(bool bReadOnlyRecommended); + void setWriteReservation(bool bWriteReservation); + + bool getReadOnlyRecommended() const { return m_bReadOnlyRecommended;} + bool getWriteReservation() const { return m_bWriteReservation;} +}; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8FIBDATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8Sttbf.cxx b/sw/source/filter/ww8/WW8Sttbf.cxx new file mode 100644 index 000000000..1e23c8ca8 --- /dev/null +++ b/sw/source/filter/ww8/WW8Sttbf.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 <sal/config.h> + +#include <algorithm> +#include <iostream> +#include "WW8Sttbf.hxx" +#include <osl/endian.h> +#include <o3tl/make_shared.hxx> +#include <tools/stream.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#ifdef OSL_BIGENDIAN +#include <rtl/ustrbuf.hxx> +#endif + +namespace ww8 +{ + WW8Struct::WW8Struct(SvStream& rSt, sal_uInt32 nPos, sal_uInt32 nSize) + : mn_offset(0), mn_size(0) + { + if (checkSeek(rSt, nPos)) + { + std::size_t nRemainingSize = rSt.remainingSize(); + nSize = std::min<sal_uInt32>(nRemainingSize, nSize); + m_pData = o3tl::make_shared_array<sal_uInt8>(nSize); + mn_size = rSt.ReadBytes(m_pData.get(), nSize); + } + OSL_ENSURE(mn_size == nSize, "short read in WW8Struct::WW8Struct"); + } + + WW8Struct::WW8Struct(WW8Struct const * pStruct, sal_uInt32 nPos, sal_uInt32 nSize) + : m_pData(pStruct->m_pData), mn_offset(pStruct->mn_offset + nPos) + , mn_size(nSize) + { + } + + WW8Struct::~WW8Struct() + { + } + + sal_uInt8 WW8Struct::getU8(sal_uInt32 nOffset) + { + sal_uInt8 nResult = 0; + + if (nOffset < mn_size) + { + nResult = m_pData.get()[mn_offset + nOffset]; + } + + return nResult; + } + + OUString WW8Struct::getUString(sal_uInt32 nOffset, + sal_uInt32 nCount) + { + OUString aResult; + + if (nCount > 0) + { + //clip to available + sal_uInt32 nStartOff = mn_offset + nOffset; + if (nStartOff >= mn_size) + return aResult; + sal_uInt32 nAvailable = (mn_size - nStartOff)/sizeof(sal_Unicode); + if (nCount > nAvailable) + nCount = nAvailable; +#if defined OSL_LITENDIAN + aResult = OUString(reinterpret_cast<const sal_Unicode *>( + m_pData.get() + nStartOff), nCount); +#else + OUStringBuffer aBuf; + for (sal_uInt32 i = 0; i < nCount; ++i) + aBuf.append(static_cast<sal_Unicode>(getU16(nStartOff+i*2))); + aResult = aBuf.makeStringAndClear(); +#endif + } + + SAL_INFO( "sw.ww8.level2", "<WW8Struct-getUString offset=\"" << nOffset + << "\" count=\"" << nCount << "\">" << aResult << "</WW8Struct-getUString>" ); + + return aResult; + + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8Sttbf.hxx b/sw/source/filter/ww8/WW8Sttbf.hxx new file mode 100644 index 000000000..89ec4113b --- /dev/null +++ b/sw/source/filter/ww8/WW8Sttbf.hxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_WW8STTBF_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8STTBF_HXX + +#include <memory> +#include <vector> + +#include <rtl/ustring.hxx> +#include <IDocumentExternalData.hxx> + +class SvStream; + +namespace ww8 +{ + + class WW8Struct : public ::sw::ExternalData + { + std::shared_ptr<sal_uInt8> m_pData; + sal_uInt32 mn_offset; + sal_uInt32 mn_size; + + public: + WW8Struct(SvStream& rSt, sal_uInt32 nPos, sal_uInt32 nSize); + WW8Struct(WW8Struct const * pStruct, sal_uInt32 nPos, sal_uInt32 nSize); + virtual ~WW8Struct() override; + + sal_uInt8 getU8(sal_uInt32 nOffset); + + sal_uInt16 getU16(sal_uInt32 nOffset) + { return getU8(nOffset) + (getU8(nOffset + 1) << 8); } + + OUString getUString(sal_uInt32 nOffset, sal_uInt32 nCount); + }; + + template <class T> + class WW8Sttb : public WW8Struct + { + typedef std::shared_ptr< void > ExtraPointer_t; + bool bDoubleByteCharacters; + std::vector<OUString> m_Strings; + std::vector< ExtraPointer_t > m_Extras; + + public: + WW8Sttb(SvStream& rSt, sal_Int32 nPos, sal_uInt32 nSize); + virtual ~WW8Sttb() override; + + std::vector<OUString> & getStrings() + { + return m_Strings; + } + }; + + template <class T> + WW8Sttb<T>::WW8Sttb(SvStream& rSt, sal_Int32 nPos, sal_uInt32 nSize) + : WW8Struct(rSt, nPos, nSize), bDoubleByteCharacters(false) + { + sal_uInt32 nOffset = 0; + + if (getU16(nOffset) == 0xffff) + { + bDoubleByteCharacters = true; + nOffset += 2; + } + + sal_uInt16 nCount = getU16(nOffset); + sal_uInt16 ncbExtra = getU16(nOffset + 2); + + nOffset += 4; + for (sal_uInt16 i = 0; i < nCount; i++) + { + if (bDoubleByteCharacters) + { + sal_uInt16 nStrLen = getU16(nOffset); + + m_Strings.push_back(getUString(nOffset +2, nStrLen)); + + nOffset += 2 + 2 * nStrLen; + } + else + { + sal_uInt8 nStrLen = getU8(nOffset); + + m_Strings.push_back(getUString(nOffset, nStrLen)); + + nOffset += 1 + nStrLen; + } + + if (ncbExtra > 0) + { + ExtraPointer_t pExtra = std::make_shared<T>(this, nOffset, ncbExtra); + m_Extras.push_back(pExtra); + + nOffset += ncbExtra; + } + } + } + + template <class T> + WW8Sttb<T>::~WW8Sttb() + { + } +} + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8STTBF_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8TableInfo.cxx b/sw/source/filter/ww8/WW8TableInfo.cxx new file mode 100644 index 000000000..4d97fd7f6 --- /dev/null +++ b/sw/source/filter/ww8/WW8TableInfo.cxx @@ -0,0 +1,1462 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <iostream> +#include <stdio.h> +#include "WW8TableInfo.hxx" +#include <fmtfsize.hxx> +#include "attributeoutputbase.hxx" +#include <swtable.hxx> +#include <frmfmt.hxx> +#include <pam.hxx> +#include <dbgoutsw.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +namespace ww8 +{ + +WW8TableNodeInfoInner::WW8TableNodeInfoInner(WW8TableNodeInfo * pParent) +: mpParent(pParent) +, mnDepth(0) +, mnCell(0) +, mnRow(0) +, mnShadowsBefore(0) +, mnShadowsAfter(0) +, mbEndOfLine(false) +, mbFinalEndOfLine(false) +, mbEndOfCell(false) +, mbFirstInTable(false) +, mbVertMerge(false) +, mpTableBox(nullptr) +, mpTable(nullptr) +{ +} + +WW8TableNodeInfoInner::~WW8TableNodeInfoInner() +{ +} + +void WW8TableNodeInfoInner::setDepth(sal_uInt32 nDepth) +{ + mnDepth = nDepth; +} + +void WW8TableNodeInfoInner::setCell(sal_uInt32 nCell) +{ + mnCell = nCell; +} + +void WW8TableNodeInfoInner::setRow(sal_uInt32 nRow) +{ + mnRow = nRow; +} + +void WW8TableNodeInfoInner::setShadowsBefore(sal_uInt32 nShadowsBefore) +{ + mnShadowsBefore = nShadowsBefore; +} + +void WW8TableNodeInfoInner::setShadowsAfter(sal_uInt32 nShadowsAfter) +{ + mnShadowsAfter = nShadowsAfter; +} + +void WW8TableNodeInfoInner::setEndOfLine(bool bEndOfLine) +{ + mbEndOfLine = bEndOfLine; +} + +void WW8TableNodeInfoInner::setFinalEndOfLine(bool bFinalEndOfLine) +{ + mbFinalEndOfLine = bFinalEndOfLine; +} + +void WW8TableNodeInfoInner::setEndOfCell(bool bEndOfCell) +{ + mbEndOfCell = bEndOfCell; +} + +void WW8TableNodeInfoInner::setFirstInTable(bool bFirstInTable) +{ + mbFirstInTable = bFirstInTable; +} + +void WW8TableNodeInfoInner::setVertMerge(bool bVertMerge) + +{ + mbVertMerge = bVertMerge; +} + +void WW8TableNodeInfoInner::setTableBox(const SwTableBox * pTableBox) +{ + mpTableBox = pTableBox; +} + +void WW8TableNodeInfoInner::setTable(const SwTable * pTable) +{ + mpTable = pTable; +} + +void WW8TableNodeInfoInner::setRect(const SwRect & rRect) +{ + maRect = rRect; +} + +const SwNode * WW8TableNodeInfoInner::getNode() const +{ + const SwNode * pResult = nullptr; + + if (mpParent != nullptr) + pResult = mpParent->getNode(); + + return pResult; +} + +TableBoxVectorPtr WW8TableNodeInfoInner::getTableBoxesOfRow() const +{ + TableBoxVectorPtr pResult = std::make_shared<TableBoxVector>(); + + WW8TableCellGrid::Pointer_t pCellGrid = + mpParent->getParent()->getCellGridForTable(getTable(), false); + + if (pCellGrid.get() == nullptr) + { + const SwTableLine * pTabLine = getTableBox()->GetUpper(); + const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes(); + + sal_uInt8 nBoxes = rTableBoxes.size(); + if (nBoxes > MAXTABLECELLS) + nBoxes = MAXTABLECELLS; + for ( sal_uInt8 n = 0; n < nBoxes; n++ ) + { + pResult->push_back(rTableBoxes[n]); + } + } + else + pResult = pCellGrid->getTableBoxesOfRow(this); + + return pResult; +} + +GridColsPtr WW8TableNodeInfoInner::getGridColsOfRow(AttributeOutputBase & rBase, bool calculateColumnsFromAllRows) +{ + GridColsPtr pResult = std::make_shared<GridCols>(); + WidthsPtr pWidths; + + // Check which columns should be checked - only the current row, + // or all the rows together + if (calculateColumnsFromAllRows) + { + // Calculate the width of all the columns based on ALL the rows. + // The difference is that this kind of draws vertical lines, + // so that if the rows look like this: + // + // ------------------------ + // | | | + // ------------------------ + // | | | + // ------------------------ + // | | | + // ------------------------ + + // then the actual column widths will be broken down like this: + // + // ------------------------ + // | | | | | + // ------------------------ + + // See the example at + // http://officeopenxml.com/WPtableGrid.php + // Under "Word 2007 Example" + pWidths = getColumnWidthsBasedOnAllRows(); + } + else + { + // Calculate the width of all the columns based on the current row + pWidths = getWidthsOfRow(); + } + + const SwFrameFormat *pFormat = getTable()->GetFrameFormat(); + OSL_ENSURE(pFormat,"Impossible"); + if (!pFormat) + return pResult; + + const SwFormatFrameSize &rSize = pFormat->GetFrameSize(); + unsigned long nTableSz = static_cast<unsigned long>(rSize.GetWidth()); + + long nPageSize = 0; + bool bRelBoxSize = false; + + rBase.GetTablePageSize( this, nPageSize, bRelBoxSize ); + + SwTwips nSz = 0; + for (const auto& rWidth : *pWidths) + { + nSz += rWidth; + SwTwips nCalc = nSz; + if ( bRelBoxSize ) + nCalc = ( nCalc * nPageSize ) / nTableSz; + + pResult->push_back( nCalc ); + } + + return pResult; +} + +WidthsPtr WW8TableNodeInfoInner::getColumnWidthsBasedOnAllRows() const +{ + WidthsPtr pWidths; + + WW8TableCellGrid::Pointer_t pCellGrid = + mpParent->getParent()->getCellGridForTable(getTable(), false); + + if (pCellGrid.get() == nullptr) + { + const SwTable * pTable = getTable(); + const SwTableLines& rTableLines = pTable->GetTabLines(); + const size_t nNumOfLines = rTableLines.size(); + + // Go over all the rows - and for each row - calculate where + // there is a separator between columns + WidthsPtr pSeparators = std::make_shared<Widths>(); + for ( size_t nLineIndex = 0; nLineIndex < nNumOfLines; ++nLineIndex ) + { + const SwTableLine *pCurrentLine = rTableLines[nLineIndex]; + const SwTableBoxes & rTabBoxes = pCurrentLine->GetTabBoxes(); + size_t nBoxes = rTabBoxes.size(); + if (nBoxes > MAXTABLECELLS) + nBoxes = MAXTABLECELLS; + + sal_uInt32 nSeparatorPosition = 0; + for (size_t nBoxIndex = 0; nBoxIndex < nBoxes; ++nBoxIndex) + { + const SwFrameFormat* pBoxFormat = rTabBoxes[ nBoxIndex ]->GetFrameFormat(); + const SwFormatFrameSize& rLSz = pBoxFormat->GetFrameSize(); + nSeparatorPosition += rLSz.GetWidth(); + pSeparators->push_back(nSeparatorPosition); + } + } + + // Sort the separator positions and remove any duplicates + std::sort(pSeparators->begin(), pSeparators->end()); + std::vector<sal_uInt32>::iterator it = std::unique(pSeparators->begin(), pSeparators->end()); + pSeparators->erase(it, pSeparators->end()); + + // Calculate the widths based on the position of the unique & sorted + // column separators + pWidths = std::make_shared<Widths>(); + sal_uInt32 nPreviousWidth = 0; + for (const sal_uInt32 nCurrentWidth : *pSeparators) + { + pWidths->push_back(nCurrentWidth - nPreviousWidth); + nPreviousWidth = nCurrentWidth; + } + } + else + { + pWidths = pCellGrid->getWidthsOfRow(this); + } + + return pWidths; +} + +WidthsPtr WW8TableNodeInfoInner::getWidthsOfRow() const +{ + WidthsPtr pWidths; + + WW8TableCellGrid::Pointer_t pCellGrid = + mpParent->getParent()->getCellGridForTable(getTable(), false); + + if (pCellGrid.get() == nullptr) + { + const SwTableBox * pTabBox = getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes(); + + pWidths = std::make_shared<Widths>(); + // number of cell written + sal_uInt32 nBoxes = rTabBoxes.size(); + if (nBoxes > MAXTABLECELLS) + nBoxes = MAXTABLECELLS; + + for (sal_uInt32 n = 0; n < nBoxes; n++) + { + const SwFrameFormat* pBoxFormat = rTabBoxes[ n ]->GetFrameFormat(); + const SwFormatFrameSize& rLSz = pBoxFormat->GetFrameSize(); + + pWidths->push_back(rLSz.GetWidth()); + } + } + else + pWidths = pCellGrid->getWidthsOfRow(this); + + return pWidths; +} + +RowSpansPtr WW8TableNodeInfoInner::getRowSpansOfRow() const +{ + RowSpansPtr pResult = std::make_shared<RowSpans>(); + + WW8TableCellGrid::Pointer_t pCellGrid = + mpParent->getParent()->getCellGridForTable(getTable(), false); + + if (pCellGrid.get() == nullptr) + { + const SwTableBox * pTabBox = getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes(); + + sal_uInt32 nBoxes = rTabBoxes.size(); + if (nBoxes > MAXTABLECELLS) + nBoxes = MAXTABLECELLS; + + for (sal_uInt32 n = 0; n < nBoxes; ++n) + { + pResult->push_back(rTabBoxes[n]->getRowSpan()); + } + } + else + pResult = pCellGrid->getRowSpansOfRow(this); + + return pResult; + } + + +#ifdef DBG_UTIL +std::string WW8TableNodeInfoInner::toString() const +{ + static char buffer[256]; + snprintf(buffer, sizeof(buffer), + "<tableinner depth=\"%" SAL_PRIuUINT32 "\"" + " cell=\"%" SAL_PRIuUINT32 "\"" + " row=\"%" SAL_PRIuUINT32 "\"" + " endOfCell=\"%s\"" + " endOfLine=\"%s\"" + " shadowsBefore=\"%" SAL_PRIuUINT32 "\"" + " shadowsAfter=\"%" SAL_PRIuUINT32 "\"" + " vertMerge=\"%s\"/>", + mnDepth, mnCell, mnRow, + mbEndOfCell ? "yes" : "no", + mbEndOfLine ? "yes" : "no", + mnShadowsBefore, + mnShadowsAfter, + mbVertMerge ? "yes" : "no"); + + return std::string(buffer); +} +#endif + +WW8TableNodeInfo::WW8TableNodeInfo(WW8TableInfo * pParent, + const SwNode * pNode) +: mpParent(pParent), + mnDepth(0), + mpNode(pNode), + mpNext(nullptr), + mpNextNode(nullptr) +{ +} + +WW8TableNodeInfo::~WW8TableNodeInfo() +{ +} + +#ifdef DBG_UTIL +std::string WW8TableNodeInfo::toString() const +{ + static char buffer[1024]; + snprintf(buffer, sizeof(buffer), + "<tableNodeInfo p=\"%p\" depth=\"%" SAL_PRIuUINT32 "\">" + ,this, getDepth()); + + std::string sResult(buffer); + + for (const auto& rInner : mInners) + { + WW8TableNodeInfoInner::Pointer_t pInner = rInner.second; + sResult += pInner->toString(); + } + sResult += dbg_out(*mpNode); + sResult += "</tableNodeInfo>"; + + return sResult; +} +#endif + +void WW8TableNodeInfo::setDepth(sal_uInt32 nDepth) +{ + mnDepth = nDepth; + + Inners_t::iterator aIt = mInners.find(mnDepth); + + if (aIt == mInners.end()) + mInners[mnDepth] = std::make_shared<ww8::WW8TableNodeInfoInner>(this); + + mInners[mnDepth]->setDepth(mnDepth); +} + +void WW8TableNodeInfo::setEndOfLine(bool bEndOfLine) +{ + WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth); + pInner->setEndOfLine(bEndOfLine); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<endOfLine depth=\"" << mnDepth << "\">" << toString() << "</endOfLine>" ); +#endif +} + +void WW8TableNodeInfo::setEndOfCell(bool bEndOfCell) +{ + WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth); + pInner->setEndOfCell(bEndOfCell); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<endOfCell depth=\"" << mnDepth << "\">" << toString() << "</endOfCell>" ); +#endif +} + +void WW8TableNodeInfo::setFirstInTable(bool bFirstInTable) +{ + WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth); + + pInner->setFirstInTable(bFirstInTable); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<firstInTable depth=\"" << mnDepth << "\">" << toString() << "</firstInTable>" ); +#endif +} + +void WW8TableNodeInfo::setVertMerge(bool bVertMerge) +{ + WW8TableNodeInfoInner::Pointer_t pInner = getInnerForDepth(mnDepth); + + pInner->setVertMerge(bVertMerge); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<vertMerge depth=\"" << mnDepth << "\">" << toString() << "</vertMerge>" ); +#endif +} + +void WW8TableNodeInfo::setTableBox(const SwTableBox * pTableBox) +{ + getInnerForDepth(mnDepth)->setTableBox(pTableBox); +} + +void WW8TableNodeInfo::setTable(const SwTable * pTable) +{ + getInnerForDepth(mnDepth)->setTable(pTable); +} + +void WW8TableNodeInfo::setNext(WW8TableNodeInfo * pNext) +{ + mpNext = pNext; + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<setnext><from>" << toString() << "</from><to>" << pNext->toString() << "</to></setnext>" ); +#endif +} + +void WW8TableNodeInfo::setNextNode(const SwNode * pNode) +{ + mpNextNode = pNode; +} + +void WW8TableNodeInfo::setRect(const SwRect & rRect) +{ + getInnerForDepth(mnDepth)->setRect(rRect); +} + +void WW8TableNodeInfo::setCell(sal_uInt32 nCell) +{ + getInnerForDepth(mnDepth)->setCell(nCell); +} + +void WW8TableNodeInfo::setRow(sal_uInt32 nRow) +{ + getInnerForDepth(mnDepth)->setRow(nRow); +} + +void WW8TableNodeInfo::setShadowsBefore(sal_uInt32 nShadowsBefore) +{ + getInnerForDepth(mnDepth)->setShadowsBefore(nShadowsBefore); +} + +void WW8TableNodeInfo::setShadowsAfter(sal_uInt32 nShadowsAfter) +{ + getInnerForDepth(mnDepth)->setShadowsAfter(nShadowsAfter); +} + + +sal_uInt32 WW8TableNodeInfo::getDepth() const +{ + if (!mInners.empty()) + return mInners.begin()->second->getDepth(); + + return mnDepth; +} + + +const SwTableBox * WW8TableNodeInfo::getTableBox() const +{ + return getInnerForDepth(mnDepth)->getTableBox(); +} + +sal_uInt32 WW8TableNodeInfo::getCell() const +{ + return getInnerForDepth(mnDepth)->getCell(); +} + +sal_uInt32 WW8TableNodeInfo::getRow() const +{ + return getInnerForDepth(mnDepth)->getRow(); +} + +WW8TableNodeInfoInner::Pointer_t WW8TableNodeInfo::getFirstInner() const +{ + WW8TableNodeInfoInner::Pointer_t pResult; + + if (!mInners.empty()) + pResult = mInners.begin()->second; + + return pResult; +} + +WW8TableNodeInfoInner::Pointer_t WW8TableNodeInfo::getInnerForDepth(sal_uInt32 nDepth) const +{ + WW8TableNodeInfoInner::Pointer_t pResult; + + Inners_t::const_iterator aIt = mInners.find(nDepth); + if (aIt != mInners.end()) + { + pResult = aIt->second; + } + + return pResult; +} + +WW8TableInfo::WW8TableInfo() +{ +} + +WW8TableInfo::~WW8TableInfo() +{ +} + +WW8TableNodeInfo * +WW8TableInfo::processSwTableByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds) +{ + SwTableCellInfo aTableCellInfo(pTable); + + while (aTableCellInfo.getNext()) + { + SwRect aRect = aTableCellInfo.getRect(); + + SAL_INFO( "sw.ww8", "<CellFrame>" ); + SAL_INFO( "sw.ww8", "<rect top=\"" << aRect.Top() << "\" bottom=\"" << aRect.Bottom() + << "\" left=\"" << aRect.Left() << "\" right=\"" << aRect.Right() << "\"/>" ); + const SwTableBox * pTableBox = aTableCellInfo.getTableBox(); + const SwStartNode * pSttNd = pTableBox->GetSttNd(); + + if (pSttNd != nullptr) + { + SwPaM aPam(*pSttNd, 0); + + bool bDone = false; + do + { + SwNode & rNode = aPam.GetPoint()->nNode.GetNode(); + + insertTableNodeInfo(&rNode, pTable, pTableBox, 0, 0, 1, & aRect); + + if (rNode.IsEndNode()) + { + SwEndNode * pEndNode = rNode.GetEndNode(); + SwStartNode * pTmpSttNd = pEndNode->StartOfSectionNode(); + + if (pTmpSttNd == pSttNd) + bDone = true; + } + + aPam.GetPoint()->nNode++; + } + while (!bDone); + } + + SAL_INFO( "sw.ww8", "</CellFrame>" ); + } + + return reorderByLayout(pTable, rLastRowEnds); +} + +void WW8TableInfo::processSwTable(const SwTable * pTable) +{ + SAL_INFO( "sw.ww8", "<processSwTable>" ); + + WW8TableNodeInfo * pPrev = nullptr; + RowEndInners_t aLastRowEnds; + + if (pTable->IsTableComplex() && pTable->HasLayout()) + { + pPrev = processSwTableByLayout(pTable, aLastRowEnds); +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", getCellGridForTable(pTable)->toString()); +#endif + } + else + { + const SwTableLines & rLines = pTable->GetTabLines(); + + for (size_t n = 0; n < rLines.size(); ++n) + { + const SwTableLine * pLine = rLines[n]; + + pPrev = processTableLine(pTable, pLine, static_cast<sal_uInt32>(n), 1, pPrev, aLastRowEnds); + } + + } + + if (pPrev) + { + SwTableNode * pTableNode = pTable->GetTableNode(); + SwEndNode * pEndNode = pTableNode->EndOfSectionNode(); + pPrev->setNextNode(pEndNode); + assert(!aLastRowEnds.empty()); + for (auto &a : aLastRowEnds) + { + assert(a.second->isEndOfLine()); + a.second->setFinalEndOfLine(true); + } + } + SAL_INFO( "sw.ww8", "</processSwTable>" ); +} + +WW8TableNodeInfo * +WW8TableInfo::processTableLine(const SwTable * pTable, + const SwTableLine * pTableLine, + sal_uInt32 nRow, + sal_uInt32 nDepth, + WW8TableNodeInfo * pPrev, + RowEndInners_t &rLastRowEnds) +{ + SAL_INFO( "sw.ww8", "<processTableLine row=\"" << nRow << "\" depth=\"" << nDepth << "\">" ); + + const SwTableBoxes & rBoxes = pTableLine->GetTabBoxes(); + + for (size_t n = 0; n < rBoxes.size(); ++n) + { + const SwTableBox * pBox = rBoxes[n]; + + pPrev = processTableBox(pTable, pBox, nRow, static_cast<sal_uInt32>(n), nDepth, n == rBoxes.size() - 1, pPrev, rLastRowEnds); + } + + SAL_INFO( "sw.ww8", "</processTableLine>" ); + + return pPrev; +} + +WW8TableNodeInfo::Pointer_t +WW8TableInfo::processTableBoxLines(const SwTableBox * pBox, + const SwTable * pTable, + const SwTableBox * pBoxToSet, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth) +{ + SAL_INFO( "sw.ww8", "<processTableBoxLines depth=\"" << nDepth << "\" row=\"" << nRow + << "\" cell=\"" << nCell << "\">" ); + + const SwTableLines & rLines = pBox->GetTabLines(); + WW8TableNodeInfo::Pointer_t pNodeInfo; + + if (!rLines.empty()) + { + for (size_t n = 0; n < rLines.size(); ++n) + { + const SwTableLine * pLine = rLines[n]; + const SwTableBoxes & rBoxes = pLine->GetTabBoxes(); + + for (size_t nBox = 0; nBox < rBoxes.size(); ++nBox) + pNodeInfo = processTableBoxLines(rBoxes[nBox], pTable, pBoxToSet, nRow, nCell, nDepth); + } + } + else + { + const SwStartNode * pSttNd = pBox->GetSttNd(); + const SwEndNode * pEndNd = pSttNd->EndOfSectionNode(); + SwPaM aPaM(*pSttNd, 0); + SwPaM aEndPaM(*pEndNd, 0); + + bool bDone = false; + while (!bDone) + { + SwNode & rNode = aPaM.GetPoint()->nNode.GetNode(); + + pNodeInfo = insertTableNodeInfo(&rNode, pTable, pBoxToSet, nRow, nCell, nDepth); + + if (aPaM.GetPoint()->nNode == aEndPaM.GetPoint()->nNode) + bDone = true; + else + aPaM.GetPoint()->nNode++; + } + } + + SAL_INFO( "sw.ww8", "</processTableBoxLines>" ); + + return pNodeInfo; +} + +static void updateFinalEndOfLine(RowEndInners_t &rLastRowEnds, WW8TableNodeInfo const * pEndOfCellInfo) +{ + sal_Int32 nDepth = pEndOfCellInfo->getDepth(); + WW8TableNodeInfoInner::Pointer_t pInner = pEndOfCellInfo->getInnerForDepth(nDepth); + + auto aIt = rLastRowEnds.find(nDepth); + if (aIt == rLastRowEnds.end() || (pInner->getRow() > aIt->second->getRow())) + rLastRowEnds[nDepth] = pInner.get(); +} + +WW8TableNodeInfo * +WW8TableInfo::processTableBox(const SwTable * pTable, + const SwTableBox * pBox, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth, + bool bEndOfLine, + WW8TableNodeInfo * pPrev, + RowEndInners_t &rLastRowEnds) +{ + SAL_INFO( "sw.ww8", "<processTableBox row=\"" << nRow << "\" cell=\"" << nCell + << "\" depth=\"" << nDepth << "\">" ); + + WW8TableNodeInfo::Pointer_t pNodeInfo; + const SwTableLines & rLines = pBox->GetTabLines(); + const SwStartNode * pSttNd = pBox->GetSttNd(); + WW8TableNodeInfo::Pointer_t pEndOfCellInfo; + + if (!rLines.empty()) + { + pNodeInfo = processTableBoxLines(pBox, pTable, pBox, nRow, nCell, nDepth); + pNodeInfo->setEndOfCell(true); + if (bEndOfLine) + { + pNodeInfo->setEndOfLine(true); + updateFinalEndOfLine(rLastRowEnds, pNodeInfo.get()); + } + + for (size_t n = 0; n < rLines.size(); n++) + { + const SwTableLine * pLine = rLines[n]; + + pPrev = processTableLine(pTable, pLine, n, 1, pPrev, rLastRowEnds); + } + } + else + { + SwPaM aPaM(*pSttNd, 0); + + bool bDone = false; + sal_uInt32 nDepthInsideCell = 0; + + do + { + SwNode & rNode = aPaM.GetPoint()->nNode.GetNode(); + + if (rNode.IsStartNode()) + { + if (nDepthInsideCell > 0) + pEndOfCellInfo.reset(); + + nDepthInsideCell++; + } + + pNodeInfo = insertTableNodeInfo(&rNode, pTable, pBox, nRow, nCell, nDepth); + + if (pPrev) + pPrev->setNext(pNodeInfo.get()); + + pPrev = pNodeInfo.get(); + + if (nDepthInsideCell == 1 && rNode.IsTextNode()) + pEndOfCellInfo = pNodeInfo; + + if (rNode.IsEndNode()) + { + nDepthInsideCell--; + + if (nDepthInsideCell == 0 && !pEndOfCellInfo) + pEndOfCellInfo = pNodeInfo; + + SwEndNode * pEndNode = rNode.GetEndNode( ); + SwStartNode * pTmpSttNd = pEndNode->StartOfSectionNode(); + if (pTmpSttNd == pSttNd) + bDone = true; + } + + aPaM.GetPoint()->nNode++; + } + while (!bDone); + + if (pEndOfCellInfo.get() != nullptr) + { + pEndOfCellInfo->setEndOfCell(true); + + if (bEndOfLine) + { + pEndOfCellInfo->setEndOfLine(true); + updateFinalEndOfLine(rLastRowEnds, pEndOfCellInfo.get()); + } + } + } + + SAL_INFO( "sw.ww8", "</processTableBox>" ); + + return pPrev; +} + +WW8TableNodeInfo::Pointer_t WW8TableInfo::insertTableNodeInfo +(const SwNode * pNode, + const SwTable * pTable, + const SwTableBox * pTableBox, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth, + SwRect const * pRect) +{ + WW8TableNodeInfo::Pointer_t pNodeInfo = getTableNodeInfo(pNode); + + if (pNodeInfo.get() == nullptr) + { + pNodeInfo = + std::make_shared<ww8::WW8TableNodeInfo>(this, pNode); + mMap.emplace(pNode, pNodeInfo); + } + + pNodeInfo->setDepth(nDepth + pNodeInfo->getDepth()); + + pNodeInfo->setTable(pTable); + pNodeInfo->setTableBox(pTableBox); + + pNodeInfo->setCell(nCell); + pNodeInfo->setRow(nRow); + + if (pNode->IsTextNode()) + { + FirstInTableMap_t::const_iterator aIt = mFirstInTableMap.find(pTable); + if (aIt == mFirstInTableMap.end()) + { + mFirstInTableMap[pTable] = pNode; + pNodeInfo->setFirstInTable(true); + } + } + + if (pRect) + { + WW8TableCellGrid::Pointer_t pCellGrid = getCellGridForTable(pTable); + + pCellGrid->insert(*pRect, pNodeInfo.get()); + pNodeInfo->setRect(*pRect); + } + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pNodeInfo->toString()); +#endif + return pNodeInfo; +} + +WW8TableCellGrid::Pointer_t WW8TableInfo::getCellGridForTable +(const SwTable * pTable, bool bCreate) +{ + WW8TableCellGrid::Pointer_t pResult; + CellGridMap_t::iterator aIt = mCellGridMap.find(pTable); + + if (aIt == mCellGridMap.end()) + { + if (bCreate) + { + pResult = std::make_shared<ww8::WW8TableCellGrid>(); + mCellGridMap[pTable] = pResult; + } + } + else + pResult = mCellGridMap[pTable]; + + return pResult; +} + +WW8TableNodeInfo::Pointer_t WW8TableInfo::getTableNodeInfo +(const SwNode * pNode) +{ + WW8TableNodeInfo::Pointer_t pResult; + Map_t::iterator aIt = mMap.find(pNode); + + if (aIt != mMap.end()) + pResult = (*aIt).second; + + return pResult; +} + +const SwNode * WW8TableInfo::getNextNode(const SwNode * pNode) +{ + const SwNode * pResult = nullptr; + + WW8TableNodeInfo::Pointer_t pNodeInfo = getTableNodeInfo(pNode); + + if (pNodeInfo.get() != nullptr) + { + WW8TableNodeInfo * pNextInfo = pNodeInfo->getNext(); + + if (pNextInfo != nullptr) + pResult = pNextInfo->getNode(); + else + { + const SwNode * pNextNode = pNodeInfo->getNextNode(); + + if (pNextNode != nullptr) + pResult = pNextNode; + } + } + + return pResult; +} + +bool WW8TableNodeInfo::operator < (const WW8TableNodeInfo & rInfo) const +{ + bool bRet = false; + + if (rInfo.mpNode != nullptr) + { + if (mpNode == nullptr) + { + bRet = true; + } + else + { + if (mpNode->GetIndex() < rInfo.mpNode->GetIndex()) + bRet = true; + } + } + + return bRet; +} + +bool CellInfo::operator < (const CellInfo & aCellInfo) const +{ + bool aRet = false; + + if (top() < aCellInfo.top()) + aRet = true; + else if (top() == aCellInfo.top()) + { + if (left() < aCellInfo.left()) + aRet = true; + else if (left() == aCellInfo.left()) + { + if (width() < aCellInfo.width()) + aRet = true; + else if (width() == aCellInfo.width()) + { + if (height() < aCellInfo.height()) + aRet = true; + else if (height() == aCellInfo.height()) + { + if (aCellInfo.getTableNodeInfo()) + { + if (m_pNodeInfo == nullptr) + aRet = true; + else + { + aRet = *m_pNodeInfo < *aCellInfo.getTableNodeInfo(); + } + } + } + } + } + } + + return aRet; +} + +#ifdef DBG_UTIL +std::string CellInfo::toString() const +{ + static char sBuffer[256]; + + snprintf(sBuffer, sizeof(sBuffer), + "<cellinfo left=\"%ld\"" + " right=\"%ld\"" + " top=\"%ld\"" + " bottom=\"%ld\"" + " node=\"%p\"/>", + left(), + right(), + top(), + bottom(), + m_pNodeInfo); + + return sBuffer; +} +#endif + +WW8TableNodeInfo * WW8TableInfo::reorderByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds) +{ + WW8TableCellGrid::Pointer_t pCellGrid = getCellGridForTable(pTable); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pCellGrid->toString()); +#endif + + pCellGrid->addShadowCells(); + return pCellGrid->connectCells(rLastRowEnds); +} + +WW8TableCellGrid::WW8TableCellGrid() +{ +} + +WW8TableCellGrid::~WW8TableCellGrid() +{ +} + +WW8TableCellGridRow::Pointer_t WW8TableCellGrid::getRow(long nTop, bool bCreate) +{ + WW8TableCellGridRow::Pointer_t pResult; + + RowTops_t::iterator aIt = m_aRowTops.find(nTop); + + if (aIt == m_aRowTops.end()) + { + if (bCreate) + { + pResult = std::make_shared<ww8::WW8TableCellGridRow>(); + m_aRows[nTop] = pResult; + m_aRowTops.insert(nTop); + } + } + else + pResult = m_aRows[nTop]; + + return pResult; +} + +WW8TableCellGrid::RowTops_t::const_iterator WW8TableCellGrid::getRowTopsBegin() const +{ + return m_aRowTops.begin(); +} + +WW8TableCellGrid::RowTops_t::const_iterator WW8TableCellGrid::getRowTopsEnd() const +{ + return m_aRowTops.end(); +} + +CellInfoMultiSet::const_iterator WW8TableCellGrid::getCellsBegin(long nTop) +{ + return getRow(nTop)->begin(); +} + +CellInfoMultiSet::const_iterator WW8TableCellGrid::getCellsEnd(long nTop) +{ + return getRow(nTop)->end(); +} + +void WW8TableCellGrid::insert(const SwRect & rRect, + WW8TableNodeInfo * pNodeInfo, + const unsigned long * pFormatFrameWidth) +{ + CellInfo aCellInfo(rRect, pNodeInfo); + + if (pFormatFrameWidth != nullptr) + aCellInfo.setFormatFrameWidth(*pFormatFrameWidth); + + WW8TableCellGridRow::Pointer_t pRow = getRow(rRect.Top()); + pRow->insert(aCellInfo); +} + +void WW8TableCellGrid::addShadowCells() +{ + SAL_INFO( "sw.ww8", "<addShadowCells>" ); + + RowTops_t::const_iterator aTopsIt = getRowTopsBegin(); + + while (aTopsIt != getRowTopsEnd()) + { + CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt); + CellInfoMultiSet::const_iterator aCellEndIt = getCellsEnd(*aTopsIt); + + RowSpansPtr pRowSpans = std::make_shared<RowSpans>(); + + bool bBeginningOfCell = true; + bool bVertMerge = false; + SwRect aRect = aCellIt->getRect(); + long nRowSpan = 1; + while (aCellIt != aCellEndIt) + { + WW8TableNodeInfo * pNodeInfo = aCellIt->getTableNodeInfo(); + + if (bBeginningOfCell) + { + RowTops_t::const_iterator aRowSpanIt(aTopsIt); + ++aRowSpanIt; + + if (aRowSpanIt != getRowTopsEnd() && + *aRowSpanIt < aCellIt->bottom()) + { + aRect.Top(*aRowSpanIt); + unsigned long nFormatFrameWidth = aCellIt->getFormatFrameWidth(); + insert(aRect, nullptr, &nFormatFrameWidth); + + bVertMerge = true; + } + else + bVertMerge = false; + + nRowSpan = 1; + while (aRowSpanIt != getRowTopsEnd() && + *aRowSpanIt < aCellIt->bottom()) + { + ++aRowSpanIt; + nRowSpan++; + } + + if (pNodeInfo) + pRowSpans->push_back(nRowSpan); + else + pRowSpans->push_back(-nRowSpan); + } + + if (pNodeInfo) + { + pNodeInfo->setVertMerge(bVertMerge); + } + + ++aCellIt; + if (aCellIt != aCellEndIt) + { + bBeginningOfCell = (aRect.Left() != aCellIt->left()); + aRect = aCellIt->getRect(); + } + } + + WW8TableCellGridRow::Pointer_t pRow = getRow(*aTopsIt); + if (pRow.get() != nullptr) + pRow->setRowSpans(pRowSpans); + + ++aTopsIt; + } + SAL_INFO( "sw.ww8", "</addShadowCells>" ); +} + +WW8TableNodeInfo * WW8TableCellGrid::connectCells(RowEndInners_t &rLastRowEnds) +{ + RowTops_t::const_iterator aTopsIt = getRowTopsBegin(); + sal_uInt32 nRow = 0; + WW8TableNodeInfo * pLastNodeInfo = nullptr; + + while (aTopsIt != getRowTopsEnd()) + { + CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt); + CellInfoMultiSet::const_iterator aCellEndIt = getCellsEnd(*aTopsIt); + GridColsPtr pWidths = std::make_shared<Widths>(); + TableBoxVectorPtr pTableBoxes = std::make_shared<TableBoxVector>(); + + sal_uInt32 nShadows = 0; + sal_uInt32 nCell = 0; + bool bBeginningOfCell = true; + WW8TableNodeInfo * pEndOfCellInfo = nullptr; + sal_uInt32 nDepthInCell = 0; + while (aCellIt != aCellEndIt) + { + long nCellX = aCellIt->left(); + WW8TableNodeInfo * pNodeInfo = aCellIt->getTableNodeInfo(); + if (pNodeInfo) + { + const SwNode * pNode = pNodeInfo->getNode(); + + if (pNode->IsStartNode()) + { + nDepthInCell++; + pEndOfCellInfo = nullptr; + } + + if (nDepthInCell == 1 && pNode->IsTextNode()) + pEndOfCellInfo = pNodeInfo; + + pNodeInfo->setShadowsBefore(nShadows); + pNodeInfo->setCell(nCell); + pNodeInfo->setRow(nRow); + if (pLastNodeInfo) + { + pLastNodeInfo->setNext(pNodeInfo); + pLastNodeInfo->setNextNode(pNode); + } + pLastNodeInfo = pNodeInfo; + nShadows = 0; + + if (pNode->IsEndNode()) + { + nDepthInCell--; + + if (nDepthInCell == 0 && !pEndOfCellInfo) + pEndOfCellInfo = pNodeInfo; + } + } + else + { + nShadows++; + } + + if (bBeginningOfCell) + { + pWidths->push_back(aCellIt->getFormatFrameWidth()); + + if (pNodeInfo) + pTableBoxes->push_back(pNodeInfo->getTableBox()); + else + pTableBoxes->push_back(nullptr); + } + + ++aCellIt; + bBeginningOfCell = false; + + if (aCellIt != aCellEndIt && aCellIt->left() != nCellX) + { + nCell++; + bBeginningOfCell = true; + + if (pEndOfCellInfo) + { + pEndOfCellInfo->setEndOfCell(true); + } + + pEndOfCellInfo = nullptr; + } + } + + pLastNodeInfo->setShadowsAfter(nShadows); + + if (!pEndOfCellInfo) + { + pEndOfCellInfo = pLastNodeInfo; + } + + pEndOfCellInfo->setEndOfCell(true); + pLastNodeInfo->setEndOfLine(true); + updateFinalEndOfLine(rLastRowEnds, pLastNodeInfo); + + WW8TableCellGridRow::Pointer_t pRow(getRow(*aTopsIt)); + pRow->setTableBoxVector(pTableBoxes); + pRow->setWidths(pWidths); + + ++aTopsIt; + nRow++; + } + + return pLastNodeInfo; +} + +#ifdef DBG_UTIL +std::string WW8TableCellGrid::toString() +{ + std::string sResult = "<WW8TableCellGrid>"; + + RowTops_t::const_iterator aTopsIt = getRowTopsBegin(); + static char sBuffer[1024]; + while (aTopsIt != getRowTopsEnd()) + { + sprintf(sBuffer, "<row y=\"%ld\">", *aTopsIt); + sResult += sBuffer; + + CellInfoMultiSet::const_iterator aCellIt = getCellsBegin(*aTopsIt); + CellInfoMultiSet::const_iterator aCellsEnd = getCellsEnd(*aTopsIt); + + while (aCellIt != aCellsEnd) + { + snprintf(sBuffer, sizeof(sBuffer), "<cellInfo top=\"%ld\" bottom=\"%ld\" left=\"%ld\" right=\"%ld\">", + aCellIt->top(), aCellIt->bottom(), aCellIt->left(), aCellIt->right()); + sResult += sBuffer; + + WW8TableNodeInfo * pInfo = aCellIt->getTableNodeInfo(); + if (pInfo) + sResult += pInfo->toString(); + else + sResult += "<shadow/>\n"; + + sResult += "</cellInfo>\n"; + ++aCellIt; + } + + WW8TableCellGridRow::Pointer_t pRow = getRow(*aTopsIt); + WidthsPtr pWidths = pRow->getWidths(); + if (pWidths != nullptr) + { + sResult += "<widths>"; + + Widths::const_iterator aItEnd = pWidths->end(); + for (Widths::const_iterator aIt = pWidths->begin(); + aIt != aItEnd; + ++aIt) + { + if (aIt != pWidths->begin()) + sResult += ", "; + + snprintf(sBuffer, sizeof(sBuffer), "%" SAL_PRIxUINT32 "", *aIt); + sResult += sBuffer; + } + + sResult += "</widths>"; + } + + RowSpansPtr pRowSpans = pRow->getRowSpans(); + if (pRowSpans.get() != nullptr) + { + sResult += "<rowspans>"; + + RowSpans::const_iterator aItEnd = pRowSpans->end(); + for (RowSpans::const_iterator aIt = pRowSpans->begin(); + aIt != aItEnd; + ++aIt) + { + if (aIt != pRowSpans->begin()) + sResult += ", "; + + snprintf(sBuffer, sizeof(sBuffer), "%" SAL_PRIxUINT32 "", *aIt); + sResult += sBuffer; + } + + sResult += "</rowspans>"; + } + + sResult += "</row>\n"; + ++aTopsIt; + } + + sResult += "</WW8TableCellGrid>\n"; + + return sResult; +} +#endif + +TableBoxVectorPtr WW8TableCellGrid::getTableBoxesOfRow +(WW8TableNodeInfoInner const * pNodeInfoInner) +{ + TableBoxVectorPtr pResult; + WW8TableCellGridRow::Pointer_t pRow = + getRow(pNodeInfoInner->getRect().Top(), false); + + if (pRow.get() != nullptr) + { + pResult = pRow->getTableBoxVector(); + } + + return pResult; +} + +WidthsPtr WW8TableCellGrid::getWidthsOfRow +(WW8TableNodeInfoInner const * pNodeInfoInner) +{ + GridColsPtr pResult; + + WW8TableCellGridRow::Pointer_t pRow = + getRow(pNodeInfoInner->getRect().Top(), false); + + if (pRow.get() != nullptr) + { + pResult = pRow->getWidths(); + } + + return pResult; +} + +RowSpansPtr WW8TableCellGrid::getRowSpansOfRow +(WW8TableNodeInfoInner const * pNodeInfoInner) +{ + RowSpansPtr pResult; + + WW8TableCellGridRow::Pointer_t pRow = + getRow(pNodeInfoInner->getRect().Top(), false); + + if (pRow.get() != nullptr) + { + pResult = pRow->getRowSpans(); + } + + return pResult; +} + +WW8TableCellGridRow::WW8TableCellGridRow() +: m_pCellInfos(std::make_shared<CellInfoMultiSet>()) +{ +} + +WW8TableCellGridRow::~WW8TableCellGridRow() +{ +} + +void WW8TableCellGridRow::insert(const CellInfo & rCellInfo) +{ + m_pCellInfos->insert(rCellInfo); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<gridRowInsert>" << rCellInfo.toString() << "</gridRowInsert>" ); +#endif +} + +CellInfoMultiSet::const_iterator WW8TableCellGridRow::begin() const +{ + return m_pCellInfos->begin(); +} + +CellInfoMultiSet::const_iterator WW8TableCellGridRow::end() const +{ + return m_pCellInfos->end(); +} + +void WW8TableCellGridRow::setTableBoxVector(TableBoxVectorPtr const & pTableBoxVector) +{ + if (pTableBoxVector->size() > MAXTABLECELLS) + pTableBoxVector->resize(MAXTABLECELLS); + m_pTableBoxVector = pTableBoxVector; +} + +void WW8TableCellGridRow::setWidths(WidthsPtr const & pWidths) +{ + m_pWidths = pWidths; +} + +void WW8TableCellGridRow::setRowSpans(RowSpansPtr const & pRowSpans) +{ + m_pRowSpans = pRowSpans; +} + + +CellInfo::CellInfo(const SwRect & aRect, WW8TableNodeInfo * pNodeInfo) +: m_aRect(aRect), m_pNodeInfo(pNodeInfo), m_nFormatFrameWidth(0) +{ + if (pNodeInfo != nullptr) + { + const SwTableBox * pBox = pNodeInfo->getTableBox(); + const SwFrameFormat * pFrameFormat = pBox->GetFrameFormat(); + const SwFormatFrameSize & rSize = pFrameFormat->GetFrameSize(); + + m_nFormatFrameWidth = rSize.GetWidth(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/WW8TableInfo.hxx b/sw/source/filter/ww8/WW8TableInfo.hxx new file mode 100644 index 000000000..1f344d4f0 --- /dev/null +++ b/sw/source/filter/ww8/WW8TableInfo.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX +#include <string> +#include <map> +#include <memory> +#include <set> +#include <functional> +#include <unordered_map> +#include <vector> +#include <sal/types.h> +#include <swrect.hxx> + +class SwTable; +class SwTableLine; +class SwTableBox; +class SwNode; +class AttributeOutputBase; + +namespace ww8 +{ +const unsigned int MAXTABLECELLS = 63; + +class WW8TableNodeInfo; +typedef std::vector<const SwTableBox *> TableBoxVector; +typedef std::shared_ptr<TableBoxVector> TableBoxVectorPtr; +typedef std::vector<sal_uInt32> GridCols; +typedef std::shared_ptr<GridCols> GridColsPtr; +typedef std::vector<sal_Int32> RowSpans; +typedef std::shared_ptr<RowSpans> RowSpansPtr; +typedef std::vector<sal_uInt32> Widths; +typedef std::shared_ptr<Widths> WidthsPtr; + +class WW8TableNodeInfoInner +{ + WW8TableNodeInfo * mpParent; + sal_uInt32 mnDepth; + sal_uInt32 mnCell; + sal_uInt32 mnRow; + sal_uInt32 mnShadowsBefore; + sal_uInt32 mnShadowsAfter; + bool mbEndOfLine; + bool mbFinalEndOfLine; + bool mbEndOfCell; + bool mbFirstInTable; + bool mbVertMerge; + const SwTableBox * mpTableBox; + const SwTable * mpTable; + SwRect maRect; + +public: + typedef std::shared_ptr<WW8TableNodeInfoInner> Pointer_t; + + explicit WW8TableNodeInfoInner(WW8TableNodeInfo * pParent); + ~WW8TableNodeInfoInner(); + + void setDepth(sal_uInt32 nDepth); + void setCell(sal_uInt32 nCell); + void setRow(sal_uInt32 nRow); + void setShadowsBefore(sal_uInt32 nShadowsBefore); + void setShadowsAfter(sal_uInt32 nShadowsAfter); + void setEndOfLine(bool bEndOfLine); + void setFinalEndOfLine(bool bEndOfLine); + void setEndOfCell(bool bEndOfCell); + void setFirstInTable(bool bFirstInTable); + void setVertMerge(bool bVertMerge); + void setTableBox(const SwTableBox *pTableBox); + void setTable(const SwTable * pTable); + void setRect(const SwRect & rRect); + + sal_uInt32 getDepth() const { return mnDepth;} + sal_uInt32 getCell() const { return mnCell;} + sal_uInt32 getRow() const { return mnRow;} + sal_uInt32 getShadowsBefore() const { return mnShadowsBefore;} + sal_uInt32 getShadowsAfter() const { return mnShadowsAfter;} + bool isEndOfCell() const { return mbEndOfCell;} + bool isEndOfLine() const { return mbEndOfLine;} + bool isFinalEndOfLine() const { return mbFinalEndOfLine;} + bool isFirstInTable() const { return mbFirstInTable;} + const SwTableBox * getTableBox() const { return mpTableBox;} + const SwTable * getTable() const { return mpTable;} + const SwRect & getRect() const { return maRect;} + + const SwNode * getNode() const; + + TableBoxVectorPtr getTableBoxesOfRow() const; + WidthsPtr getWidthsOfRow() const; + WidthsPtr getColumnWidthsBasedOnAllRows() const; + GridColsPtr getGridColsOfRow(AttributeOutputBase & rBase, bool calculateColumnsFromAllRows = false); + RowSpansPtr getRowSpansOfRow() const; + +#ifdef DBG_UTIL + std::string toString() const; +#endif +}; + +class CellInfo +{ + SwRect m_aRect; + WW8TableNodeInfo * m_pNodeInfo; + unsigned long m_nFormatFrameWidth; + +public: + CellInfo(const SwRect & aRect, WW8TableNodeInfo * pNodeInfo); + + CellInfo(const CellInfo & aRectAndTableInfo) + : m_aRect(aRectAndTableInfo.m_aRect), + m_pNodeInfo(aRectAndTableInfo.m_pNodeInfo), + m_nFormatFrameWidth(aRectAndTableInfo.m_nFormatFrameWidth) + { + } + + bool operator < (const CellInfo & aCellInfo) const; + + long top() const { return m_aRect.Top(); } + long bottom() const { return m_aRect.Bottom(); } + long left() const { return m_aRect.Left(); } + long right() const { return m_aRect.Right(); } + long width() const { return m_aRect.Width(); } + long height() const { return m_aRect.Height(); } + const SwRect& getRect() const { return m_aRect; } + WW8TableNodeInfo * getTableNodeInfo() const + { return m_pNodeInfo; } + unsigned long getFormatFrameWidth() const + { + return m_nFormatFrameWidth; + } + + void setFormatFrameWidth(unsigned long nFormatFrameWidth) + { + m_nFormatFrameWidth = nFormatFrameWidth; + } + +#ifdef DBG_UTIL + std::string toString() const; +#endif +}; + +typedef std::multiset<CellInfo> CellInfoMultiSet; +typedef std::map<sal_uInt32, WW8TableNodeInfoInner*, + std::greater<sal_uInt32> > RowEndInners_t; + + +class WW8TableInfo; +class WW8TableNodeInfo final +{ +public: + typedef std::map<sal_uInt32, WW8TableNodeInfoInner::Pointer_t, + std::greater<sal_uInt32> > Inners_t; + +private: + WW8TableInfo * mpParent; + sal_uInt32 mnDepth; + const SwNode * mpNode; + Inners_t mInners; + WW8TableNodeInfo * mpNext; + const SwNode * mpNextNode; + +public: + typedef std::shared_ptr<WW8TableNodeInfo> Pointer_t; + + WW8TableNodeInfo(WW8TableInfo * pParent, const SwNode * pTextNode); + ~WW8TableNodeInfo(); + + void setDepth(sal_uInt32 nDepth); + void setEndOfLine(bool bEndOfLine); + void setEndOfCell(bool bEndOfCell); + void setFirstInTable(bool bFirstInTable); + void setVertMerge(bool bVertMerge); + void setTableBox(const SwTableBox *pTableBox); + void setTable(const SwTable * pTable); + void setCell(sal_uInt32 nCell); + void setRow(sal_uInt32 nRow); + void setShadowsBefore(sal_uInt32 nShadowsBefore); + void setShadowsAfter(sal_uInt32 nShadowsAfter); + void setNext(WW8TableNodeInfo * pNext); + void setNextNode(const SwNode * pNode); + void setRect(const SwRect & rRect); + + WW8TableInfo * getParent() const { return mpParent;} + sal_uInt32 getDepth() const; + const SwNode * getNode() const { return mpNode;} + const SwTableBox * getTableBox() const; + WW8TableNodeInfo * getNext() const { return mpNext;} + const SwNode * getNextNode() const { return mpNextNode;} + + const Inners_t & getInners() const { return mInners;} + WW8TableNodeInfoInner::Pointer_t getFirstInner() const; + WW8TableNodeInfoInner::Pointer_t getInnerForDepth(sal_uInt32 nDepth) const; + + sal_uInt32 getCell() const; + sal_uInt32 getRow() const; + +#ifdef DBG_UTIL + std::string toString() const; +#endif + + bool operator < (const WW8TableNodeInfo & rInfo) const; +}; + +struct hashNode +{ + size_t operator()(const SwNode * pNode) const + { return reinterpret_cast<size_t>(pNode); } +}; + +struct hashTable +{ + size_t operator()(const SwTable * pTable) const + { return reinterpret_cast<size_t>(pTable); } +}; + +class WW8TableCellGridRow +{ + std::shared_ptr<CellInfoMultiSet> m_pCellInfos; + TableBoxVectorPtr m_pTableBoxVector; + WidthsPtr m_pWidths; + RowSpansPtr m_pRowSpans; + +public: + typedef std::shared_ptr<WW8TableCellGridRow> Pointer_t; + WW8TableCellGridRow(); + ~WW8TableCellGridRow(); + + void insert(const CellInfo & rCellInfo); + CellInfoMultiSet::const_iterator begin() const; + CellInfoMultiSet::const_iterator end() const; + + void setTableBoxVector(TableBoxVectorPtr const & pTableBoxVector); + void setWidths(WidthsPtr const & pGridCols); + void setRowSpans(RowSpansPtr const & pRowSpans); + + const TableBoxVectorPtr& getTableBoxVector() const { return m_pTableBoxVector;} + const WidthsPtr& getWidths() const { return m_pWidths;} + const RowSpansPtr& getRowSpans() const { return m_pRowSpans;} +}; + +class WW8TableCellGrid +{ + typedef std::set<long> RowTops_t; + typedef std::map<long, WW8TableCellGridRow::Pointer_t> Rows_t; + + RowTops_t m_aRowTops; + Rows_t m_aRows; + + WW8TableCellGridRow::Pointer_t getRow(long nTop, bool bCreate = true); + RowTops_t::const_iterator getRowTopsBegin() const; + RowTops_t::const_iterator getRowTopsEnd() const; + CellInfoMultiSet::const_iterator getCellsBegin(long nTop); + CellInfoMultiSet::const_iterator getCellsEnd(long nTop); + +public: + typedef std::shared_ptr<WW8TableCellGrid> Pointer_t; + + WW8TableCellGrid(); + ~WW8TableCellGrid(); + + void insert(const SwRect & rRect, WW8TableNodeInfo * pNodeInfo, + unsigned long const * pFormatFrameWidth = nullptr); + void addShadowCells(); + WW8TableNodeInfo *connectCells(RowEndInners_t &rLastRowEnds); + +#ifdef DBG_UTIL + std::string toString(); +#endif + + TableBoxVectorPtr getTableBoxesOfRow(WW8TableNodeInfoInner const * pNodeInfo); + WidthsPtr getWidthsOfRow(WW8TableNodeInfoInner const * pNodeInfo); + RowSpansPtr getRowSpansOfRow(WW8TableNodeInfoInner const * pNodeInfo); +}; + +class WW8TableInfo final +{ + friend class WW8TableNodeInfoInner; + typedef std::unordered_map<const SwNode *, WW8TableNodeInfo::Pointer_t, hashNode > Map_t; + Map_t mMap; + + typedef std::unordered_map<const SwTable *, WW8TableCellGrid::Pointer_t, hashTable > CellGridMap_t; + CellGridMap_t mCellGridMap; + + typedef std::unordered_map<const SwTable *, const SwNode *, hashTable > FirstInTableMap_t; + FirstInTableMap_t mFirstInTableMap; + + WW8TableNodeInfo * + processTableLine(const SwTable * pTable, + const SwTableLine * pTableLine, + sal_uInt32 nRow, + sal_uInt32 nDepth, WW8TableNodeInfo * pPrev, RowEndInners_t &rLastRowEnds); + + WW8TableNodeInfo * + processTableBox(const SwTable * pTable, + const SwTableBox * pTableBox, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth, bool bEndOfLine, + WW8TableNodeInfo * pPrev, RowEndInners_t &rLastRowEnds); + + WW8TableNodeInfo::Pointer_t + processTableBoxLines(const SwTableBox * pBox, + const SwTable * pTable, + const SwTableBox * pBoxToSet, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth); + + WW8TableNodeInfo::Pointer_t + insertTableNodeInfo(const SwNode * pNode, + const SwTable * pTable, + const SwTableBox * pTableBox, + sal_uInt32 nRow, + sal_uInt32 nCell, + sal_uInt32 nDepth, + SwRect const * pRect = nullptr); + + WW8TableCellGrid::Pointer_t getCellGridForTable(const SwTable * pTable, + bool bCreate = true); + +public: + typedef std::shared_ptr<WW8TableInfo> Pointer_t; + + WW8TableInfo(); + ~WW8TableInfo(); + + void processSwTable(const SwTable * pTable); + WW8TableNodeInfo * processSwTableByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds); + WW8TableNodeInfo::Pointer_t getTableNodeInfo(const SwNode * pNode); + const SwNode * getNextNode(const SwNode * pNode); + + WW8TableNodeInfo * reorderByLayout(const SwTable * pTable, RowEndInners_t &rLastRowEnds); +}; + +} +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8TABLEINFO_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx new file mode 100644 index 000000000..89865c7de --- /dev/null +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -0,0 +1,698 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX + +#include "fields.hxx" +#include "WW8TableInfo.hxx" +#include "wrtww8.hxx" + +#include <rtl/textenc.h> +#include <editeng/svxenum.hxx> +#include <tools/solar.h> +#include <optional> + +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <swtypes.hxx> +#include <fldbas.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <unotools/saveopt.hxx> + +class Point; +class SvxCaseMapItem; +class SvxColorItem; +class SvxContourItem; +class SvxCrossedOutItem; +class SvxEscapementItem; +class SvxFontItem; +class SvxFontHeightItem; +class SvxKerningItem; +class SvxLanguageItem; +class SvxPostureItem; +class SvxShadowedItem; +class SvxUnderlineItem; +class SvxWeightItem; +class SvxAutoKernItem; +class SvxBlinkItem; +class SvxBrushItem; +class XFillStyleItem; +class XFillGradientItem; +class SvxFontItem; +class SvxFontHeightItem; +class SvxLanguageItem; +class SvxPostureItem; +class SvxWeightItem; +class SvxFontItem; +class SvxFontHeightItem; +class SvxLanguageItem; +class SvxPostureItem; +class SvxWeightItem; +class SvxCharRotateItem; +class SvxEmphasisMarkItem; +class SvxTwoLinesItem; +class SvxCharScaleWidthItem; +class SvxCharReliefItem; +class SvxCharHiddenItem; +class SvxBoxItem; +class SwFormatINetFormat; +class SwFormatCharFormat; +class SwFormatField; +class SwFormatFlyCnt; +class SwFormatFootnote; +class SvxLineSpacingItem; +class SvxAdjustItem; +class SvxFormatSplitItem; +class SvxWidowsItem; +class SvxTabStopItem; +class SvxHyphenZoneItem; +class SwNumRuleItem; +class SfxBoolItem; +class SfxPoolItem; +class SfxItemSet; +class SvxParaVertAlignItem; +class SvxParaGridItem; +class SwFormatFrameSize; +class SvxPaperBinItem; +class SvxLRSpaceItem; +class SvxULSpaceItem; +class SwFormatPageDesc; +class SvxFormatBreakItem; +class SwFormatSurround; +class SwFormatVertOrient; +class SwFormatHoriOrient; +class SwFormatAnchor; +class SvxBrushItem; +class SvxBoxItem; +class SwFormatCol; +class SvxFormatKeepItem; +class SwTextGridItem; +class SwFormatLineNumber; +class SvxFrameDirectionItem; +class SfxGrabBagItem; +class SfxUInt16Item; +class SwFormatRuby; +class SwTextNode; +class SwTOXMark; +class SwRedlineData; +class SwSection; +class SwFormatDrop; +class SwFrameFormat; +class SwNumFormat; +class SwFormat; +struct WW8_SepInfo; +class SwLineNumberInfo; +class SwNumRule; +class wwFont; + +namespace editeng { class SvxBorderLine; } + +namespace rtl { class OUString; } + +class MSWordExportBase; + +namespace ww8 { class Frame; } + +namespace msword { + const sal_uInt8 ColumnBreak = 0xE; + const sal_uInt8 PageBreak = 0xC; +} + +/// Type of a style in the style table. +enum StyleType +{ + STYLE_TYPE_PARA, + STYLE_TYPE_CHAR, + STYLE_TYPE_LIST +}; + +class AttributeOutputBase +{ +private: + SvtSaveOptions m_aSaveOpt; + OUString m_sBaseURL; // To be used in ConvertURL + + OUString ConvertURL( const OUString& rUrl, bool bAbsoluteOut ); + +public: + /// Export the state of RTL/CJK. + virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) = 0; + + /// Start of the paragraph. + virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo ) = 0; + + /// End of the paragraph. + virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0; + + /// Called in order to output section breaks. + virtual void SectionBreaks(const SwNode& rNode) = 0; + + /// Called before we start outputting the attributes. + virtual void StartParagraphProperties() = 0; + + /// Called after we end outputting the attributes. + virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) = 0; + + /// Empty paragraph. + virtual void EmptyParagraph() = 0; + + /// Start of the text run. + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) = 0; + + /// End of the text run. + virtual void EndRun( const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false ) = 0; + + /// Called before we start outputting the attributes. + virtual void StartRunProperties() = 0; + + /// Called after we end outputting the attributes. + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) = 0; + + /// docx requires footnoteRef/endnoteRef tag at the beginning of each of them + virtual bool FootnoteEndnoteRefTag() { return false; }; + + /// for footnote/endnote section properties + virtual void SectFootnoteEndnotePr() {}; + + /// for docx w:commentReference + virtual void WritePostitFieldReference() {}; + + /// Output text (inside a run). + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) = 0; + + /// Output text (without markup). + virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) = 0; + + /// Output ruby start. + virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) = 0; + + /// Output ruby end. + virtual void EndRuby( const SwTextNode& rNode, sal_Int32 nPos ) = 0; + + /// Output URL start. + virtual bool StartURL( const OUString& rUrl, const OUString& rTarget ) = 0; + + /// Output URL end. + virtual bool EndURL(bool isAtEndOfParagraph) = 0; + + virtual void FieldVanish( const OUString& rText, ww::eField eType ) = 0; + + /// MSO uses bookmarks to reference sequence fields, so we need to generate these additional bookmarks during export + void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter); + + void StartTOX( const SwSection& rSect ); + + void EndTOX( const SwSection& rSect,bool bCareEnd=true ); + + virtual void OnTOXEnding() {} + + void TOXMark( const SwTextNode& rNode, const SwTOXMark& rAttr ); + + /// Output redlining. + virtual void Redline( const SwRedlineData* pRedline ) = 0; + + virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0; + + /// Output FKP (Formatted disK Page) - necessary for binary formats only. + /// FIXME having it in AttributeOutputBase is probably a hack, it + /// should be in WW8AttributeOutput only... + virtual void OutputFKP(bool /*bForce*/) {} + + /// Output style. + virtual void ParagraphStyle( sal_uInt16 nStyle ) = 0; + + virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) = 0; + + virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) = 0; + + virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) = 0; + + virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) = 0; + + virtual void TableRowEnd( sal_uInt32 nDepth ) = 0; + + /// Start of the styles table. + virtual void StartStyles() = 0; + + /// End of the styles table. + virtual void EndStyles( sal_uInt16 nNumberOfStyles ) = 0; + + /// Write default style. + virtual void DefaultStyle() = 0; + + /// Start of a style in the styles table. + virtual void StartStyle( const OUString& rName, StyleType eType, + sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwId, sal_uInt16 nId, + bool bAutoUpdate ) = 0; + + /// End of a style in the styles table. + virtual void EndStyle() = 0; + + /// Start of (paragraph or run) properties of a style. + virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) = 0; + + /// End of (paragraph or run) properties of a style. + virtual void EndStyleProperties( bool bParProp ) = 0; + + /// Numbering rule and Id. + virtual void OutlineNumbering(sal_uInt8 nLvl) = 0; + + /// Page break + /// As a paragraph property - the paragraph should be on the next page. + virtual void PageBreakBefore( bool bBreak ) = 0; + + /// Write a section break + /// msword::ColumnBreak or msword::PageBreak + /// bBreakAfter: the break must be scheduled for insertion in the end of current paragraph + virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr ) = 0; + + // preserve page vertical alignment + virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust) {}; + + /// Start of the section properties. + virtual void StartSection() = 0; + + /// End of the section properties. + virtual void EndSection() = 0; + + /// Protection of forms. + virtual void SectionFormProtection( bool bProtected ) = 0; + + /// Numbering of the lines in the document. + virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) = 0; + + /// Has different headers/footers for the title page. + virtual void SectionTitlePage() = 0; + + /// Set the state of the Fly at current position + virtual void SetStateOfFlyFrame( FlyProcessingState /*nStateOfFlyFrame*/ ){}; + /// If the node has an anchor linked. + virtual void SetAnchorIsLinkedToNode( bool /*bAnchorLinkedToNode*/){}; + + /// Is processing of fly postponed ? + virtual bool IsFlyProcessingPostponed(){ return false; }; + + /// Reset the flag for FlyProcessing + virtual void ResetFlyProcessingFlag(){}; + + /// Description of the page borders. + virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) = 0; + + /// Columns populated from right/numbers on the right side? + virtual void SectionBiDi( bool bBiDi ) = 0; + + /// The style of the page numbers. + /// + virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) = 0; + + /// The type of breaking. + virtual void SectionType( sal_uInt8 nBreakCode ) = 0; + + /// Definition of a numbering instance. + virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) = 0; + + /// Numbering definition that overrides abstract numbering definition + virtual void OverrideNumberingDefinition(SwNumRule const&, sal_uInt16 /*nNum*/, sal_uInt16 /*nAbstractNum*/, + const std::map< size_t, size_t > & /*rLevelOverrides*/) + { assert(false); } // TODO implement for WW8/RTF + + /// Start of the abstract numbering definition instance. + virtual void StartAbstractNumbering( sal_uInt16 /*nId*/ ) {} + + /// End of the abstract numbering definition instance. + virtual void EndAbstractNumbering() {} + + /// All the numbering level information. + virtual void NumberingLevel( sal_uInt8 nLevel, + sal_uInt16 nStart, + sal_uInt16 nNumberingType, + SvxAdjust eAdjust, + const sal_uInt8 *pNumLvlPos, + sal_uInt8 nFollow, + const wwFont *pFont, + const SfxItemSet *pOutSet, + sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, + sal_Int16 nListTabPos, + const OUString &rNumberingString, + const SvxBrushItem* pBrush) = 0; // #i120928 export graphic of bullet + +protected: + + static void GetNumberPara( OUString& rStr, const SwField& rField ); + + /// Output frames - the implementation. + virtual void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) = 0; + + /// Sfx item Sfx item RES_CHRATR_CASEMAP + virtual void CharCaseMap( const SvxCaseMapItem& ) = 0; + + /// Sfx item Sfx item RES_CHRATR_COLOR + virtual void CharColor( const SvxColorItem& ) = 0; + + /// Sfx item Sfx item RES_CHRATR_CONTOUR + virtual void CharContour( const SvxContourItem& ) = 0; + + /// Sfx item RES_CHRATR_CROSSEDOUT + virtual void CharCrossedOut( const SvxCrossedOutItem& ) = 0; + + /// Sfx item RES_CHRATR_ESCAPEMENT + virtual void CharEscapement( const SvxEscapementItem& ) = 0; + + /// Sfx item RES_CHRATR_FONT + virtual void CharFont( const SvxFontItem& ) = 0; + + /// Sfx item RES_CHRATR_FONTSIZE + virtual void CharFontSize( const SvxFontHeightItem& ) = 0; + + /// Sfx item RES_CHRATR_KERNING + virtual void CharKerning( const SvxKerningItem& ) = 0; + + /// Sfx item RES_CHRATR_LANGUAGE + virtual void CharLanguage( const SvxLanguageItem& ) = 0; + + /// Sfx item RES_CHRATR_POSTURE + virtual void CharPosture( const SvxPostureItem& ) = 0; + + /// Sfx item RES_CHRATR_SHADOWED + virtual void CharShadow( const SvxShadowedItem& ) = 0; + + /// Sfx item RES_CHRATR_UNDERLINE + virtual void CharUnderline( const SvxUnderlineItem& ) = 0; + + /// Sfx item RES_CHRATR_WEIGHT + virtual void CharWeight( const SvxWeightItem& ) = 0; + + /// Sfx item RES_CHRATR_AUTOKERN + virtual void CharAutoKern( const SvxAutoKernItem& ) = 0; + + /// Sfx item RES_CHRATR_BLINK + virtual void CharAnimatedText( const SvxBlinkItem& ) = 0; + + /// Sfx item RES_CHRATR_BACKGROUND + void CharBackgroundBase( const SvxBrushItem& ); + virtual void CharBackground( const SvxBrushItem& ) = 0; + + /// Sfx item RES_CHRATR_CJK_FONT + virtual void CharFontCJK( const SvxFontItem& ) = 0; + + /// Sfx item RES_CHRATR_CJK_FONTSIZE + virtual void CharFontSizeCJK( const SvxFontHeightItem& ) = 0; + + /// Sfx item RES_CHRATR_CJK_LANGUAGE + virtual void CharLanguageCJK( const SvxLanguageItem& ) = 0; + + /// Sfx item RES_CHRATR_CJK_POSTURE + virtual void CharPostureCJK( const SvxPostureItem& ) = 0; + + /// Sfx item RES_CHRATR_CJK_WEIGHT + virtual void CharWeightCJK( const SvxWeightItem& ) = 0; + + /// Sfx item RES_CHRATR_CTL_FONT + virtual void CharFontCTL( const SvxFontItem& ) = 0; + + /// Sfx item RES_CHRATR_CTL_FONTSIZE + virtual void CharFontSizeCTL( const SvxFontHeightItem& ) = 0; + + /// Sfx item RES_CHRATR_CTL_LANGUAGE + virtual void CharLanguageCTL( const SvxLanguageItem& ) = 0; + + /// Sfx item RES_CHRATR_CTL_POSTURE + virtual void CharPostureCTL( const SvxPostureItem& ) = 0; + + /// Sfx item RES_CHRATR_CTL_WEIGHT + virtual void CharWeightCTL( const SvxWeightItem& ) = 0; + + /// Sfx item RES_CHRATR_BidiRTL + virtual void CharBidiRTL( const SfxPoolItem& ) = 0; + + /// Sfx item RES_CHRATR_IdctHint + virtual void CharIdctHint( const SfxPoolItem& ) = 0; + + /// Sfx item RES_CHRATR_ROTATE + virtual void CharRotate( const SvxCharRotateItem& ) = 0; + + /// Sfx item RES_CHRATR_EMPHASIS_MARK + virtual void CharEmphasisMark( const SvxEmphasisMarkItem& ) = 0; + + /// Sfx item RES_CHRATR_TWO_LINES + virtual void CharTwoLines( const SvxTwoLinesItem& ) = 0; + + /// Sfx item RES_CHRATR_SCALEW + virtual void CharScaleWidth( const SvxCharScaleWidthItem& ) = 0; + + /// Sfx item RES_CHRATR_RELIEF + virtual void CharRelief( const SvxCharReliefItem& ) = 0; + + /// Sfx item RES_CHRATR_HIDDEN + virtual void CharHidden( const SvxCharHiddenItem& ) = 0; + + /// Sfx item RES_CHRATR_BOX + void FormatCharBorder( const SvxBoxItem& rBox ); + virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) = 0; + + /// Sfx item RES_CHRATR_HIGHLIGHT + virtual void CharHighlight( const SvxBrushItem& ) = 0; + + /// Sfx item RES_TXTATR_INETFMT + virtual void TextINetFormat( const SwFormatINetFormat& ) = 0; + + /// Sfx item RES_TXTATR_CHARFMT + virtual void TextCharFormat( const SwFormatCharFormat& ) = 0; + + /// Sfx item RES_TXTATR_FIELD, RES_TXTATR_ANNOTATION and RES_TXTATR_INPUTFIELD + void TextField( const SwFormatField& ); + + /// Sfx item RES_TXTATR_FLYCNT + void TextFlyContent( const SwFormatFlyCnt& ); + + /// Sfx item RES_TXTATR_FTN + /// + /// This one is common for both WW8AttributeOutput as well as + /// DocxAttributeOutput. + void TextFootnote( const SwFormatFootnote& ); + + /// Sfx item RES_TXTATR_FTN + virtual void TextFootnote_Impl( const SwFormatFootnote& ) = 0; + + /// Sfx item RES_PARATR_LINESPACING + void ParaLineSpacing( const SvxLineSpacingItem& ); + + /// Count the values in ParaLineSpacing, and pass theme here. + virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) = 0; + + /// Sfx item RES_PARATR_ADJUST + virtual void ParaAdjust( const SvxAdjustItem& ) = 0; + + /// Sfx item RES_PARATR_SPLIT + virtual void ParaSplit( const SvxFormatSplitItem& ) = 0; + + /// Sfx item RES_PARATR_WIDOWS + virtual void ParaWidows( const SvxWidowsItem& ) = 0; + + /// Sfx item RES_PARATR_TABSTOP + virtual void ParaTabStop( const SvxTabStopItem& ) = 0; + + /// Sfx item RES_PARATR_HYPHENZONE + virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) = 0; + + /// Sfx item RES_PARATR_NUMRULE + void ParaNumRule( const SwNumRuleItem& ); + + /// Numbering - the implementation. + virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) = 0; + + /// Sfx item RES_PARATR_SCRIPTSPACE + virtual void ParaScriptSpace( const SfxBoolItem& ) = 0; + + /// Sfx item RES_PARATR_HANGINGPUNCTUATION + virtual void ParaHangingPunctuation( const SfxBoolItem& ) = 0; + + /// Sfx item RES_PARATR_FORBIDDEN_RULES + virtual void ParaForbiddenRules( const SfxBoolItem& ) = 0; + + /// Sfx item RES_PARATR_VERTALIGN + virtual void ParaVerticalAlign( const SvxParaVertAlignItem& ) = 0; + + /// Sfx item RES_PARATR_SNAPTOGRID + virtual void ParaSnapToGrid( const SvxParaGridItem& ) = 0; + + /// Sfx item RES_FRM_SIZE + virtual void FormatFrameSize( const SwFormatFrameSize& ) = 0; + + /// Sfx item RES_PAPER_BIN + virtual void FormatPaperBin( const SvxPaperBinItem& ) = 0; + + /// Sfx item RES_LR_SPACE + virtual void FormatLRSpace( const SvxLRSpaceItem& ) = 0; + + /// Sfx item RES_UL_SPACE + virtual void FormatULSpace( const SvxULSpaceItem& ) = 0; + + /// Sfx item RES_PAGEDESC + void FormatPageDescription( const SwFormatPageDesc& ); + + /// Sfx item RES_BREAK + void FormatBreak( const SvxFormatBreakItem& ); + + /// Sfx item RES_SURROUND + virtual void FormatSurround( const SwFormatSurround& ) = 0; + + /// Sfx item RES_VERT_ORIENT + virtual void FormatVertOrientation( const SwFormatVertOrient& ) = 0; + + /// Sfx item RES_HORI_ORIENT + virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) = 0; + + /// Sfx item RES_ANCHOR + virtual void FormatAnchor( const SwFormatAnchor& ) = 0; + + /// Sfx item RES_BACKGROUND + virtual void FormatBackground( const SvxBrushItem& ) = 0; + + /// Sfx item RES_FILL_STYLE + virtual void FormatFillStyle( const XFillStyleItem& ) = 0; + + /// Sfx item RES_FILL_GRADIENT + virtual void FormatFillGradient( const XFillGradientItem& ) = 0; + + /// Sfx item RES_BOX + virtual void FormatBox( const SvxBoxItem& ) = 0; + + /// Sfx item RES_COL + void FormatColumns( const SwFormatCol& ); + + virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) = 0; + + /// Sfx item RES_KEEP + virtual void FormatKeep( const SvxFormatKeepItem& ) = 0; + + /// Compute the grid character pitch + sal_uInt32 GridCharacterPitch( const SwTextGridItem& rGrid ) const; + + /// Sfx item RES_TEXTGRID + virtual void FormatTextGrid( const SwTextGridItem& ) = 0; + + /// Sfx item RES_LINENUMBER + virtual void FormatLineNumbering( const SwFormatLineNumber& ) = 0; + + /// Sfx item RES_FRAMEDIR + virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) = 0; + + /// Sfx item RES_PARATR_GRABBAG + virtual void ParaGrabBag( const SfxGrabBagItem& ) = 0; + + /// Sfx item RES_CHRATR_GRABBAG + virtual void CharGrabBag( const SfxGrabBagItem& ) = 0; + + /// Sfx item RES_PARATR_OUTLINELEVEL + virtual void ParaOutlineLevel( const SfxUInt16Item& ) = 0; + + /// Write the expanded field + virtual void WriteExpand( const SwField* pField ) = 0; + + virtual void RefField( const SwField& rField, const OUString& rRef ) = 0; + virtual void HiddenField( const SwField& rField ) = 0; + virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) = 0; + virtual void PostitField( const SwField* pField ) = 0; + virtual bool DropdownField( const SwField* pField ) = 0; + virtual bool PlaceholderField( const SwField* pField ) = 0; + + virtual bool AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ); + + /// Insert a bookmark inside the currently processed paragraph. + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) = 0; + + ww8::GridColsPtr GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + ww8::WidthsPtr GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + +public: + AttributeOutputBase(const OUString& sBaseURL) + : m_sBaseURL(sBaseURL) + { + } + virtual ~AttributeOutputBase() {} + + /// Return the right export class. + virtual MSWordExportBase& GetExport() = 0; + + /// @overload + const MSWordExportBase& GetExport() const { return const_cast< AttributeOutputBase* >( this )->GetExport(); } + + /// Call the right virtual function according to the type of the item. + void OutputItem( const SfxPoolItem& rHt ); + + /// Use OutputItem() on an item set - for styles. + void OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault ); + + /// Output frames. + void OutputFlyFrame( const ww8::Frame& rFormat ); + + void GetTablePageSize + ( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, + long& rPageSize, bool& rRelBoxSize ); + + virtual void MaybeOutputBrushItem(SfxItemSet const&) { } + + /// Exports the definition (image, size) of a single numbering picture bullet. + virtual void BulletDefinition(int /*nId*/, const Graphic& /*rGraphic*/, Size /*aSize*/) {} + + // Returns whether or not the 'SwTextNode' has a paragraph marker inserted \ deleted (using 'track changes') + const SwRedlineData* GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType ); +}; + +class WW8Ruby +{ + sal_Int32 m_nJC; + char m_cDirective; + sal_uInt32 m_nRubyHeight; + sal_uInt32 m_nBaseHeight; + OUString m_sFontFamily; + +public: + WW8Ruby(const SwTextNode& rNode, const SwFormatRuby& rRuby, const MSWordExportBase& rExport ); + sal_Int32 GetJC() const { return m_nJC; } + char GetDirective() const { return m_cDirective; } + sal_uInt32 GetRubyHeight() const { return m_nRubyHeight; } + sal_uInt32 GetBaseHeight() const { return m_nBaseHeight; } + OUString const & GetFontFamily() const { return m_sFontFamily; } +}; +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_ATTRIBUTEOUTPUTBASE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx new file mode 100644 index 000000000..c7191ab81 --- /dev/null +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -0,0 +1,9694 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "docxattributeoutput.hxx" +#include "docxhelper.hxx" +#include "docxsdrexport.hxx" +#include "docxexportfilter.hxx" +#include "docxfootnotes.hxx" +#include "writerwordglue.hxx" +#include "ww8par.hxx" +#include <fmtcntnt.hxx> +#include <fmtftn.hxx> +#include <fchrfmt.hxx> +#include <tgrditem.hxx> +#include <fmtruby.hxx> +#include <fmtanchr.hxx> +#include <breakit.hxx> +#include <redline.hxx> +#include <unocoll.hxx> +#include <unoframe.hxx> +#include <textboxhelper.hxx> +#include <rdfhelper.hxx> +#include "wrtww8.hxx" + +#include <comphelper/processfactory.hxx> +#include <comphelper/random.hxx> +#include <comphelper/string.hxx> +#include <comphelper/flagguard.hxx> +#include <comphelper/sequence.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/token/tokens.hxx> +#include <oox/export/utils.hxx> +#include <oox/mathml/export.hxx> +#include <oox/drawingml/drawingmltypes.hxx> +#include <oox/token/relationship.hxx> +#include <oox/export/vmlexport.hxx> +#include <oox/ole/olehelper.hxx> + +#include <editeng/autokernitem.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/twolinesitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/paravertalignitem.hxx> +#include <editeng/pgrditem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/keepitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/svdouno.hxx> +#include <svx/unobrushitemhelper.hxx> +#include <svl/grabbagitem.hxx> +#include <sfx2/sfxbasemodel.hxx> +#include <tools/datetimeutils.hxx> +#include <svl/whiter.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <sot/exchange.hxx> + +#include <docufld.hxx> +#include <authfld.hxx> +#include <flddropdown.hxx> +#include <fmtclds.hxx> +#include <fmtinfmt.hxx> +#include <fmtrowsplt.hxx> +#include <fmtline.hxx> +#include <ftninfo.hxx> +#include <htmltbl.hxx> +#include <lineinfo.hxx> +#include <ndgrf.hxx> +#include <ndole.hxx> +#include <ndtxt.hxx> +#include <pagedesc.hxx> +#include <paratr.hxx> +#include <swmodule.hxx> +#include <swtable.hxx> +#include <txtftn.hxx> +#include <fmtautofmt.hxx> +#include <docsh.hxx> +#include <docary.hxx> +#include <fmtclbl.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <grfatr.hxx> +#include <frmatr.hxx> +#include <txtatr.hxx> +#include <frameformats.hxx> + +#include <osl/file.hxx> +#include <utility> +#include <vcl/embeddedfontshelper.hxx> + +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/drawing/ShadingPattern.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/Aspects.hpp> + +#include <algorithm> +#include <stdarg.h> + +#include <toolkit/helper/vclunohelper.hxx> +#include <oox/mathml/export.hxx> +#include <unicode/regex.h> + +using ::editeng::SvxBorderLine; + +using namespace oox; +using namespace docx; +using namespace sax_fastparser; +using namespace nsSwDocInfoSubType; +using namespace sw::util; +using namespace ::com::sun::star; +using namespace ::com::sun::star::drawing; + +static const sal_Int32 Tag_StartParagraph_1 = 1; +static const sal_Int32 Tag_StartParagraph_2 = 2; +static const sal_Int32 Tag_WriteSdtBlock = 3; +static const sal_Int32 Tag_StartParagraphProperties = 4; +static const sal_Int32 Tag_InitCollectedParagraphProperties = 5; +static const sal_Int32 Tag_StartRun_1 = 6; +static const sal_Int32 Tag_StartRun_2 = 7; +static const sal_Int32 Tag_StartRun_3 = 8; +static const sal_Int32 Tag_EndRun_1 = 9; +static const sal_Int32 Tag_EndRun_2 = 10; +static const sal_Int32 Tag_StartRunProperties = 11; +static const sal_Int32 Tag_InitCollectedRunProperties = 12; +//static const sal_Int32 Tag_Redline_1 = 13; +static const sal_Int32 Tag_Redline_2 = 14; +static const sal_Int32 Tag_TableDefinition = 15; +static const sal_Int32 Tag_OutputFlyFrame = 16; +static const sal_Int32 Tag_StartSection = 17; + +namespace { + +class FFDataWriterHelper +{ + ::sax_fastparser::FSHelperPtr m_pSerializer; + void writeCommonStart( const OUString& rName, + const OUString& rEntryMacro, + const OUString& rExitMacro, + const OUString& rHelp, + const OUString& rHint ) + { + m_pSerializer->startElementNS(XML_w, XML_ffData); + m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName.toUtf8()); + m_pSerializer->singleElementNS(XML_w, XML_enabled); + m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0"); + + if ( !rEntryMacro.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_entryMacro, + FSNS(XML_w, XML_val), rEntryMacro.toUtf8() ); + + if ( !rExitMacro.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_exitMacro, + FSNS(XML_w, XML_val), rExitMacro.toUtf8() ); + + if ( !rHelp.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_helpText, + FSNS(XML_w, XML_type), "text", + FSNS(XML_w, XML_val), rHelp.toUtf8() ); + + if ( !rHint.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_statusText, + FSNS(XML_w, XML_type), "text", + FSNS(XML_w, XML_val), rHint.toUtf8() ); + + } + void writeFinish() + { + m_pSerializer->endElementNS( XML_w, XML_ffData ); + } +public: + explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr rSerializer ) : m_pSerializer(std::move( rSerializer )){} + void WriteFormCheckbox( const OUString& rName, + const OUString& rEntryMacro, + const OUString& rExitMacro, + const OUString& rHelp, + const OUString& rHint, + bool bChecked ) + { + writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint ); + // Checkbox specific bits + m_pSerializer->startElementNS(XML_w, XML_checkBox); + // currently hardcoding autosize + // #TODO check if this defaulted + m_pSerializer->startElementNS(XML_w, XML_sizeAuto); + m_pSerializer->endElementNS( XML_w, XML_sizeAuto ); + if ( bChecked ) + m_pSerializer->singleElementNS(XML_w, XML_checked); + m_pSerializer->endElementNS( XML_w, XML_checkBox ); + writeFinish(); + } + + void WriteFormText( const OUString& rName, + const OUString& rEntryMacro, + const OUString& rExitMacro, + const OUString& rHelp, + const OUString& rHint, + const OUString& rType, + const OUString& rDefaultText, + sal_uInt16 nMaxLength, + const OUString& rFormat ) + { + writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint ); + + m_pSerializer->startElementNS(XML_w, XML_textInput); + if ( !rType.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_type, + FSNS(XML_w, XML_val), rType.toUtf8() ); + if ( !rDefaultText.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_default, + FSNS(XML_w, XML_val), rDefaultText.toUtf8() ); + if ( nMaxLength ) + m_pSerializer->singleElementNS( XML_w, XML_maxLength, + FSNS(XML_w, XML_val), OString::number(nMaxLength) ); + if ( !rFormat.isEmpty() ) + m_pSerializer->singleElementNS( XML_w, XML_format, + FSNS(XML_w, XML_val), rFormat.toUtf8() ); + m_pSerializer->endElementNS( XML_w, XML_textInput ); + + writeFinish(); + } +}; + +class FieldMarkParamsHelper +{ + const sw::mark::IFieldmark& mrFieldmark; + public: + explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {} + OUString const & getName() const { return mrFieldmark.GetName(); } + template < typename T > + bool extractParam( const OUString& rKey, T& rResult ) + { + bool bResult = false; + if ( mrFieldmark.GetParameters() ) + { + sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey ); + if ( it != mrFieldmark.GetParameters()->end() ) + bResult = ( it->second >>= rResult ); + } + return bResult; + } +}; + +} + +void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ ) +{ + if (bIsRTL) + m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true"); +} + +/// Are multiple paragraphs disallowed inside this type of SDT? +static bool lcl_isOnelinerSdt(const OUString& rName) +{ + return rName == "Title" || rName == "Subtitle" || rName == "Company"; +} + +// write a floating table directly to docx without the surrounding frame +void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame) +{ + const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); + m_aFloatingTablesOfParagraph.insert(&rFrameFormat); + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + + //Save data here and restore when out of scope + ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame); + + // set a floatingTableFrame AND unset parent frame, + // otherwise exporter thinks we are still in a frame + m_rExport.SetFloatingTableFrame(pParentFrame); + m_rExport.m_pParentFrame = nullptr; + + GetExport().WriteText(); + + m_rExport.SetFloatingTableFrame(nullptr); +} + +static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput) +{ + const auto& rExport = rDocxAttributeOutput.GetExport(); + // iterate though all SpzFrameFormats and check whether they are anchored to the current text node + for( sal_uInt16 nCnt = rExport.m_pDoc->GetSpzFrameFormats()->size(); nCnt; ) + { + const SwFrameFormat* pFrameFormat = (*rExport.m_pDoc->GetSpzFrameFormats())[ --nCnt ]; + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + const SwPosition* pPosition = rAnchor.GetContentAnchor(); + + if (!pPosition || ! rExport.m_pCurPam->GetNode().GetTextNode()) + continue; + + if (pPosition->nNode != rExport.m_pCurPam->GetNode().GetTextNode()->GetIndex()) + continue; + + const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx(); + if (!pStartNode) + continue; + + SwNodeIndex aStartNode = *pStartNode; + + // go to the next node (actual content) + ++aStartNode; + + // this has to be a table + if (!aStartNode.GetNode().IsTableNode()) + continue; + + // go to the end of the table + sal_uLong aEndIndex = aStartNode.GetNode().EndOfSectionIndex(); + // go one deeper + aEndIndex++; + // this has to be the end of the content + if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex()) + continue; + + // check for a grabBag and "TablePosition" attribute -> then we can export the table directly + SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode(); + SwTable& rTable = pTableNode->GetTable(); + SwFrameFormat* pTableFormat = rTable.GetFrameFormat(); + const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG); + std::map<OUString, css::uno::Any> aTableGrabBag = pTableGrabBag->GetGrabBag(); + // no grabbag? + if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end()) + continue; + + // write table to docx + ww8::Frame aFrame(*pFrameFormat,*pPosition); + rDocxAttributeOutput.WriteFloatingTable(&aFrame); + } +} + +void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo ) +{ + // look ahead for floating tables that were put into a frame during import + // floating tables in shapes are not supported: exclude this case + if (!pTextNodeInfo && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) + { + checkAndWriteFloatingTables(*this); + } + + if ( m_nColBreakStatus == COLBRK_POSTPONE ) + m_nColBreakStatus = COLBRK_WRITE; + + // Output table/table row/table cell starts if needed + if ( pTextNodeInfo ) + { + // New cell/row? + if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen ) + { + ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) ); + if ( pDeepInner->getCell() == 0 ) + StartTableRow( pDeepInner ); + + const sal_uInt32 nCell = pDeepInner->getCell(); + const sal_uInt32 nRow = pDeepInner->getRow(); + + SyncNodelessCells(pDeepInner, nCell, nRow); + StartTableCell(pDeepInner, nCell, nRow); + } + + sal_uInt32 nRow = pTextNodeInfo->getRow(); + sal_uInt32 nCell = pTextNodeInfo->getCell(); + if (nCell == 0) + { + // Do we have to start the table? + // [If we are at the right depth already, it means that we + // continue the table cell] + sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth(); + + if ( nCurrentDepth > m_tableReference->m_nTableDepth ) + { + // Start all the tables that begin here + for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth ) + { + ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) ); + + StartTable( pInner ); + StartTableRow( pInner ); + + StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0); + } + + m_tableReference->m_nTableDepth = nCurrentDepth; + } + } + } + + // Look up the "sdt end before this paragraph" property early, when it + // would normally arrive, it would be too late (would be after the + // paragraph start has been written). + bool bEndParaSdt = false; + if (m_bStartedParaSdt) + { + SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode(); + if (pTextNode && pTextNode->GetpSwAttrSet()) + { + const SfxItemSet* pSet = pTextNode->GetpSwAttrSet(); + if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG)) + { + const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem); + const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag(); + bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end(); + } + } + } + // TODO also avoid multiline paragraphs in those SDT types for shape text + bool bOneliner = m_bStartedParaSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias); + if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner) + { + // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph. + EndSdtBlock(); + m_bStartedParaSdt = false; + m_aStartedParagraphSdtPrAlias.clear(); + } + m_bHadSectPr = false; + + // this mark is used to be able to enclose the paragraph inside a sdr tag. + // We will only know if we have to do that later. + m_pSerializer->mark(Tag_StartParagraph_1); + + m_pSerializer->startElementNS(XML_w, XML_p); + + // postpone the output of the run (we get it before the paragraph + // properties, but must write it after them) + m_pSerializer->mark(Tag_StartParagraph_2); + + // no section break in this paragraph yet; can be set in SectionBreak() + m_pSectionInfo.reset(); + + m_bParagraphOpened = true; + m_bIsFirstParagraph = false; +} + +static OString convertToOOXMLVertOrient(sal_Int16 nOrient) +{ + switch( nOrient ) + { + case text::VertOrientation::CENTER: + case text::VertOrientation::LINE_CENTER: + return "center"; + case text::VertOrientation::BOTTOM: + return "bottom"; + case text::VertOrientation::LINE_BOTTOM: + return "outside"; + case text::VertOrientation::TOP: + return "top"; + case text::VertOrientation::LINE_TOP: + return "inside"; + default: + return OString(); + } +} + +static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle) +{ + switch( nOrient ) + { + case text::HoriOrientation::LEFT: + return bIsPosToggle ? "inside" : "left"; + case text::HoriOrientation::INSIDE: + return "inside"; + case text::HoriOrientation::RIGHT: + return bIsPosToggle ? "outside" : "right"; + case text::HoriOrientation::OUTSIDE: + return "outside"; + case text::HoriOrientation::CENTER: + case text::HoriOrientation::FULL: + return "center"; + default: + return OString(); + } +} + +static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel) +{ + switch (nOrientRel) + { + case text::RelOrientation::PAGE_PRINT_AREA: + return "margin"; + case text::RelOrientation::PAGE_FRAME: + return "page"; + case text::RelOrientation::FRAME: + case text::RelOrientation::TEXT_LINE: + default: + return "text"; + } +} + +static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel) +{ + switch (nOrientRel) + { + case text::RelOrientation::PAGE_PRINT_AREA: + return "margin"; + case text::RelOrientation::PAGE_FRAME: + return "page"; + case text::RelOrientation::CHAR: + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::FRAME: + default: + return "text"; + } +} + +static void lcl_deleteAndResetTheLists( rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias) +{ + if( pSdtPrTokenChildren.is() ) + pSdtPrTokenChildren.clear(); + if( pSdtPrDataBindingAttrs.is() ) + pSdtPrDataBindingAttrs.clear(); + if (!rSdtPrAlias.isEmpty()) + rSdtPrAlias.clear(); +} + +void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize) +{ + sax_fastparser::FastAttributeList* attrList = FastSerializerHelper::createAttrList(); + + awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos()); + + attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width())); + attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height())); + + attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X)); + attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y)); + + sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft(); + sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight(); + sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper(); + sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower(); + + attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2)); + attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2)); + + OString relativeFromH = convertToOOXMLHoriOrientRel( pFrameFormat->GetHoriOrient().GetRelationOrient() ); + OString relativeFromV = convertToOOXMLVertOrientRel( pFrameFormat->GetVertOrient().GetRelationOrient() ); + + switch (pFrameFormat->GetSurround().GetValue()) + { + case css::text::WrapTextMode_NONE: + attrList->add( FSNS( XML_w, XML_wrap), "notBeside"); + break; + case css::text::WrapTextMode_DYNAMIC: + attrList->add(FSNS(XML_w, XML_wrap), "auto"); + break; + case css::text::WrapTextMode_PARALLEL: + default: + attrList->add(FSNS(XML_w, XML_wrap), "around"); + break; + } + attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV ); + attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH ); + attrList->add( FSNS( XML_w, XML_hRule), "exact"); + + sax_fastparser::XFastAttributeListRef xAttrList(attrList); + m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList ); +} + +bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat) +{ + uno::Reference< drawing::XShape > xShape; + const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject(); + if (pSdrObj) + xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY); + uno::Reference< beans::XPropertySetInfo > xPropSetInfo; + if (xPropertySet.is()) + xPropSetInfo = xPropertySet->getPropertySetInfo(); + uno::Any aFrameProperties ; + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) + { + uno::Sequence< beans::PropertyValue > propList; + xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; }); + if (pProp != propList.end()) + aFrameProperties = pProp->Value; + } + bool bFrameProperties = false; + aFrameProperties >>= bFrameProperties; + return bFrameProperties; +} + +void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) +{ + // write the paragraph properties + the run, already in the correct order + m_pSerializer->mergeTopMarks(Tag_StartParagraph_2); + std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox; + // Write the anchored frame if any + // Word can't handle nested text boxes, so write them on the same level. + ++m_nTextFrameLevel; + if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) + { + comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_bStartedParaSdt, false); + + assert(!m_pPostponedCustomShape); + m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>); + for (size_t nIndex = 0; nIndex < m_aFramesOfParagraph.size(); ++nIndex) + { + m_bParagraphFrameOpen = true; + ww8::Frame aFrame = m_aFramesOfParagraph[nIndex]; + const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat(); + + if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter) + { + if (m_bStartedCharSdt) + { + // Run-level SDT still open? Close it before AlternateContent. + EndSdtBlock(); + m_bStartedCharSdt = false; + } + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_mc, XML_AlternateContent); + m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps"); + /** + This is to avoid AlternateContent within another AlternateContent. + So when Choice is Open, only write the DML Drawing instead of both DML + and VML Drawing in another AlternateContent. + **/ + SetAlternateContentChoiceOpen( true ); + /** Save the table info's before writing the shape + as there might be a new table that might get + spawned from within the VML & DML block and alter + the contents. + */ + ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo; + //Reset the table infos after saving. + m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); + + /** FDO#71834 : + Save the table reference attributes before calling WriteDMLTextFrame, + otherwise the StartParagraph function will use the previous existing + table reference attributes since the variable is being shared. + */ + { + DocxTableExportContext aDMLTableExportContext(*this); + m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++); + } + m_pSerializer->endElementNS(XML_mc, XML_Choice); + SetAlternateContentChoiceOpen( false ); + + // Reset table infos, otherwise the depth of the cells will be incorrect, + // in case the text frame had table(s) and we try to export the + // same table second time. + m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); + //reset the tableReference. + + m_pSerializer->startElementNS(XML_mc, XML_Fallback); + { + DocxTableExportContext aVMLTableExportContext(*this); + m_rExport.SdrExporter().writeVMLTextFrame(&aFrame); + } + m_rExport.m_pTableInfo = pOldTableInfo; + + m_pSerializer->endElementNS(XML_mc, XML_Fallback); + m_pSerializer->endElementNS(XML_mc, XML_AlternateContent); + m_pSerializer->endElementNS( XML_w, XML_r ); + m_bParagraphFrameOpen = false; + } + else + { + std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame); + aFramePrTextbox.push_back(pFramePr); + } + } + if (!m_pPostponedCustomShape->empty()) + { + m_pSerializer->startElementNS(XML_w, XML_r); + WritePostponedCustomShape(); + m_pSerializer->endElementNS( XML_w, XML_r ); + } + m_pPostponedCustomShape.reset(); + + m_aFramesOfParagraph.clear(); + + if (!pTextNodeInfoInner) + { + // Ending a non-table paragraph, clear floating tables before paragraph. + m_aFloatingTablesOfParagraph.clear(); + } + } + + --m_nTextFrameLevel; + + /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed. + * This is due to nested hyperlink tags. So close it before end of paragraph. + */ + if(m_nHyperLinkCount > 0) + { + for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose) + m_pSerializer->endElementNS( XML_w, XML_hyperlink ); + m_nHyperLinkCount = 0; + } + + if (m_bStartedCharSdt) + { + // Run-level SDT still open? Close it now. + EndSdtBlock(); + m_bStartedCharSdt = false; + } + + if (m_bPageBreakAfter) + { + // tdf#128889 Trailing page break + SectionBreak(msword::PageBreak, false); + m_bPageBreakAfter = false; + } + + m_pSerializer->endElementNS( XML_w, XML_p ); + // on export sdt blocks are never nested ATM + if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt ) + WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true ); + else + { + //These should be written out to the actual Node and not to the anchor. + //Clear them as they will be repopulated when the node is processed. + m_nParagraphSdtPrToken = 0; + m_bParagraphSdtHasId = false; + lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias ); + } + + //sdtcontent is written so Set m_bParagraphHasDrawing to false + m_rExport.SdrExporter().setParagraphHasDrawing( false ); + m_bRunTextIsOn = false; + m_pSerializer->mergeTopMarks(Tag_StartParagraph_1); + + // Write framePr + for ( const auto & pFrame : aFramePrTextbox ) + { + DocxTableExportContext aTableExportContext(*this); + m_pCurrentFrame = pFrame.get(); + m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get()); + m_pCurrentFrame = nullptr; + } + aFramePrTextbox.clear(); + // Check for end of cell, rows, tables here + FinishTableRowCell( pTextNodeInfoInner ); + + if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) + m_bParagraphOpened = false; + + // Clear bookmarks of the current paragraph + m_aBookmarksOfParagraphStart.clear(); + m_aBookmarksOfParagraphEnd.clear(); +} + +void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, + OUString& rSdtPrAlias, + bool bPara ) +{ + if( nSdtPrToken <= 0 && !pSdtPrDataBindingAttrs.is() ) + return; + + // sdt start mark + m_pSerializer->mark(Tag_WriteSdtBlock); + + m_pSerializer->startElementNS(XML_w, XML_sdt); + + // output sdt properties + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() ) + { + if (!pSdtPrTokenAttributes.is()) + m_pSerializer->startElement(nSdtPrToken); + else + { + XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get()); + pSdtPrTokenAttributes.clear(); + m_pSerializer->startElement(nSdtPrToken, xAttrList); + } + + if (nSdtPrToken == FSNS( XML_w, XML_date ) || nSdtPrToken == FSNS( XML_w, XML_docPartObj ) || nSdtPrToken == FSNS( XML_w, XML_docPartList ) || nSdtPrToken == FSNS( XML_w14, XML_checkbox )) { + const uno::Sequence<xml::FastAttribute> aChildren = pSdtPrTokenChildren->getFastAttributes(); + for( const auto& rChild : aChildren ) + m_pSerializer->singleElement( rChild.Token, + FSNS(XML_w, XML_val), rChild.Value.toUtf8() ); + } + + m_pSerializer->endElement( nSdtPrToken ); + } + else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing())) + { + if (!pSdtPrTokenAttributes.is()) + m_pSerializer->singleElement(nSdtPrToken); + else + { + XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get()); + pSdtPrTokenAttributes.clear(); + m_pSerializer->singleElement(nSdtPrToken, xAttrList); + } + } + + if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) ) + //Word won't open a document with an empty id tag, we fill it with a random number + m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max()))); + + if( pSdtPrDataBindingAttrs.is() && !m_rExport.SdrExporter().IsParagraphHasDrawing()) + { + XFastAttributeListRef xAttrList( pSdtPrDataBindingAttrs.get() ); + pSdtPrDataBindingAttrs.clear(); + m_pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList); + } + + if (!rSdtPrAlias.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), + rSdtPrAlias.toUtf8()); + + m_pSerializer->endElementNS( XML_w, XML_sdtPr ); + + // sdt contents start tag + m_pSerializer->startElementNS(XML_w, XML_sdtContent); + + // prepend the tags since the sdt start mark before the paragraph + m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND); + + // write the ending tags after the paragraph + if (bPara) + { + m_bStartedParaSdt = true; + if (m_tableReference->m_bTableCellOpen) + m_tableReference->m_bTableCellParaSdtOpen = true; + if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen()) + m_rExport.SdrExporter().setParagraphSdtOpen(true); + } + else + // Support multiple runs inside a run-level SDT: don't close the SDT block yet. + m_bStartedCharSdt = true; + + // clear sdt status + nSdtPrToken = 0; + pSdtPrTokenChildren.clear(); + pSdtPrDataBindingAttrs.clear(); + rSdtPrAlias.clear(); + +} + +void DocxAttributeOutput::EndSdtBlock() +{ + m_pSerializer->endElementNS( XML_w, XML_sdtContent ); + m_pSerializer->endElementNS( XML_w, XML_sdt ); +} + +#define MAX_CELL_IN_WORD 62 + +void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow) +{ + sal_Int32 nOpenCell = lastOpenCell.back(); + if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD) + EndTableCell(nOpenCell); + + sal_Int32 nClosedCell = lastClosedCell.back(); + for (sal_Int32 i = nClosedCell+1; i < nCell; ++i) + { + if (i >= MAX_CELL_IN_WORD) + break; + + if (i == 0) + StartTableRow(pInner); + + StartTableCell(pInner, i, nRow); + m_pSerializer->singleElementNS(XML_w, XML_p); + EndTableCell(i); + } +} + +void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph ) +{ + if ( pInner ) + { + // Where are we in the table + sal_uInt32 nRow = pInner->getRow(); + sal_Int32 nCell = pInner->getCell(); + + InitTableHelper( pInner ); + + // HACK + // msoffice seems to have an internal limitation of 63 columns for tables + // and refuses to load .docx with more, even though the spec seems to allow that; + // so simply if there are more columns, don't close the last one msoffice will handle + // and merge the contents of the remaining ones into it (since we don't close the cell + // here, following ones will not be opened) + const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine()); + const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround; + const bool bEndRow = pInner->isEndOfLine(); + + if (bEndCell) + { + while (pInner->getDepth() < m_tableReference->m_nTableDepth) + { + //we expect that the higher depth row was closed, and + //we are just missing the table close + assert(lastOpenCell.back() == -1 && lastClosedCell.back() == -1); + EndTable(); + } + + SyncNodelessCells(pInner, nCell, nRow); + + sal_Int32 nClosedCell = lastClosedCell.back(); + if (nCell == nClosedCell) + { + //Start missing trailing cell(s) + ++nCell; + StartTableCell(pInner, nCell, nRow); + + //Continue on missing next trailing cell(s) + ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow(); + sal_Int32 nRemainingCells = xRowSpans->size() - nCell; + for (sal_Int32 i = 1; i < nRemainingCells; ++i) + { + if (bForceEmptyParagraph) + { + m_pSerializer->singleElementNS(XML_w, XML_p); + } + + EndTableCell(nCell); + + StartTableCell(pInner, nCell, nRow); + } + } + + if (bForceEmptyParagraph) + { + m_pSerializer->singleElementNS(XML_w, XML_p); + } + + EndTableCell(nCell); + } + + // This is a line end + if (bEndRow) + EndTableRow(); + + // This is the end of the table + if (pInner->isFinalEndOfLine()) + EndTable(); + } +} + +void DocxAttributeOutput::EmptyParagraph() +{ + m_pSerializer->singleElementNS(XML_w, XML_p); +} + +void DocxAttributeOutput::SectionBreaks(const SwNode& rNode) +{ + // output page/section breaks + // Writer can have them at the beginning of a paragraph, or at the end, but + // in docx, we have to output them in the paragraph properties of the last + // paragraph in a section. To get it right, we have to switch to the next + // paragraph, and detect the section breaks there. + SwNodeIndex aNextIndex( rNode, 1 ); + + if (rNode.IsTextNode() || rNode.IsSectionNode()) + { + if (aNextIndex.GetNode().IsTextNode()) + { + const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode()); + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty()); + } + else if (aNextIndex.GetNode().IsTableNode()) + { + const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode()); + const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat(); + m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode); + } + } + else if (rNode.IsEndNode()) + { + if (aNextIndex.GetNode().IsTextNode()) + { + // Handle section break between a table and a text node following it. + // Also handle section endings + const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode(); + if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode()) + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty()); + } + } +} + +void DocxAttributeOutput::StartParagraphProperties() +{ + m_pSerializer->mark(Tag_StartParagraphProperties); + + m_pSerializer->startElementNS(XML_w, XML_pPr); + + // and output the section break now (if it appeared) + if (m_pSectionInfo && m_rExport.m_nTextTyp == TXT_MAINTEXT) + { + m_rExport.SectionProperties( *m_pSectionInfo ); + m_pSectionInfo.reset(); + } + + InitCollectedParagraphProperties(); +} + +void DocxAttributeOutput::InitCollectedParagraphProperties() +{ + m_pParagraphSpacingAttrList.clear(); + + // Write the elements in the spec order + static const sal_Int32 aOrder[] = + { + FSNS( XML_w, XML_pStyle ), + FSNS( XML_w, XML_keepNext ), + FSNS( XML_w, XML_keepLines ), + FSNS( XML_w, XML_pageBreakBefore ), + FSNS( XML_w, XML_framePr ), + FSNS( XML_w, XML_widowControl ), + FSNS( XML_w, XML_numPr ), + FSNS( XML_w, XML_suppressLineNumbers ), + FSNS( XML_w, XML_pBdr ), + FSNS( XML_w, XML_shd ), + FSNS( XML_w, XML_tabs ), + FSNS( XML_w, XML_suppressAutoHyphens ), + FSNS( XML_w, XML_kinsoku ), + FSNS( XML_w, XML_wordWrap ), + FSNS( XML_w, XML_overflowPunct ), + FSNS( XML_w, XML_topLinePunct ), + FSNS( XML_w, XML_autoSpaceDE ), + FSNS( XML_w, XML_autoSpaceDN ), + FSNS( XML_w, XML_bidi ), + FSNS( XML_w, XML_adjustRightInd ), + FSNS( XML_w, XML_snapToGrid ), + FSNS( XML_w, XML_spacing ), + FSNS( XML_w, XML_ind ), + FSNS( XML_w, XML_contextualSpacing ), + FSNS( XML_w, XML_mirrorIndents ), + FSNS( XML_w, XML_suppressOverlap ), + FSNS( XML_w, XML_jc ), + FSNS( XML_w, XML_textDirection ), + FSNS( XML_w, XML_textAlignment ), + FSNS( XML_w, XML_textboxTightWrap ), + FSNS( XML_w, XML_outlineLvl ), + FSNS( XML_w, XML_divId ), + FSNS( XML_w, XML_cnfStyle ), + FSNS( XML_w, XML_rPr ), + FSNS( XML_w, XML_sectPr ), + FSNS( XML_w, XML_pPrChange ) + }; + + // postpone the output so that we can later [in EndParagraphProperties()] + // prepend the properties before the run + m_pSerializer->mark(Tag_InitCollectedParagraphProperties, comphelper::containerToSequence(aOrder)); +} + +void DocxAttributeOutput::WriteCollectedParagraphProperties() +{ + if ( m_rExport.SdrExporter().getFlyAttrList().is() ) + { + XFastAttributeListRef xAttrList( m_rExport.SdrExporter().getFlyAttrList().get() ); + m_rExport.SdrExporter().getFlyAttrList().clear(); + + m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList ); + } + + if ( m_pParagraphSpacingAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pParagraphSpacingAttrList.get() ); + m_pParagraphSpacingAttrList.clear(); + + m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList ); + } + + if ( m_pBackgroundAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pBackgroundAttrList.get() ); + m_pBackgroundAttrList.clear(); + + m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList ); + } +} + +namespace +{ + +/// Outputs an item set, that contains the formatting of the paragraph marker. +void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties) +{ + SfxWhichIter aIter(rParagraphMarkerProperties); + sal_uInt16 nWhichId = aIter.FirstWhich(); + const SfxPoolItem* pItem = nullptr; + // Did we already produce a <w:sz> element? + bool bFontSizeWritten = false; + while (nWhichId) + { + if (rParagraphMarkerProperties.GetItemState(nWhichId, true, &pItem) == SfxItemState::SET) + { + if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT) + { + // Will this item produce a <w:sz> element? + bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE; + if (!bFontSizeWritten || !bFontSizeItem) + rAttributeOutput.OutputItem(*pItem); + if (bFontSizeItem) + bFontSizeWritten = true; + } + else if (nWhichId == RES_TXTATR_AUTOFMT) + { + const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem); + lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle()); + } + } + nWhichId = aIter.NextWhich(); + } +} + +const char *RubyAlignValues[] = +{ + "center", + "distributeLetter", + "distributeSpace", + "left", + "right", + "rightVertical" +}; + + +const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC) +{ + const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues); + if ( nJC >=0 && nJC < nElements ) + return RubyAlignValues[nJC]; + return RubyAlignValues[0]; +} + +} + +void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) +{ + // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties. + // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc. + + // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline(). + // As there will be another pPr for redline and LO might mix both. + if(pRedlineData) + WriteCollectedParagraphProperties(); + Redline( pRedlineData ); + + WriteCollectedParagraphProperties(); + + // Merge the marks for the ordered elements + m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties); + + // Write 'Paragraph Mark' properties + m_pSerializer->startElementNS(XML_w, XML_rPr); + // mark() before paragraph mark properties child elements. + InitCollectedRunProperties(); + + // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information + // that should be collected by different properties in the core, and are all flushed together + // to the DOCX when the function 'WriteCollectedRunProperties' gets called. + // So we need to store the current status of these lists, so that we can revert back to them when + // we are done exporting the redline attributes. + rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList); + m_pFontsAttrList.clear(); + rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList); + m_pEastAsianLayoutAttrList.clear(); + rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList); + m_pCharLangAttrList.clear(); + + lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties); + + // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' + WriteCollectedRunProperties(); + + // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' + m_pFontsAttrList = pFontsAttrList_Original.get(); + m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get(); + m_pCharLangAttrList = pCharLangAttrList_Original.get(); + + if ( pRedlineParagraphMarkerDeleted ) + { + StartRedline( pRedlineParagraphMarkerDeleted ); + EndRedline( pRedlineParagraphMarkerDeleted ); + } + if ( pRedlineParagraphMarkerInserted ) + { + StartRedline( pRedlineParagraphMarkerInserted ); + EndRedline( pRedlineParagraphMarkerInserted ); + } + + // mergeTopMarks() after paragraph mark properties child elements. + m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + + if (!m_bWritingHeaderFooter && m_pCurrentFrame) + { + const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat(); + const SvxBoxItem& rBox = rFrameFormat.GetBox(); + if (TextBoxIsFramePr(rFrameFormat)) + { + const Size aSize = m_pCurrentFrame->GetSize(); + PopulateFrameProperties(&rFrameFormat, aSize); + FormatBox(rBox); + } + } + + m_pSerializer->endElementNS( XML_w, XML_pPr ); + + // RDF metadata for this text node. + SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode(); + std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode); + if (!aStatements.empty()) + { + m_pSerializer->startElementNS(XML_w, XML_smartTag, + FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + FSNS(XML_w, XML_element), "RDF"); + m_pSerializer->startElementNS(XML_w, XML_smartTagPr); + for (const auto& rStatement : aStatements) + m_pSerializer->singleElementNS(XML_w, XML_attr, + FSNS(XML_w, XML_name), rStatement.first.toUtf8(), + FSNS(XML_w, XML_val), rStatement.second.toUtf8()); + m_pSerializer->endElementNS(XML_w, XML_smartTagPr); + m_pSerializer->endElementNS(XML_w, XML_smartTag); + } + + if ( m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE ) + { + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column"); + m_pSerializer->endElementNS( XML_w, XML_r ); + + if ( m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE ) + m_nColBreakStatus = COLBRK_POSTPONE; + else + m_nColBreakStatus = COLBRK_NONE; + } + + if ( m_bPostponedPageBreak && !m_bWritingHeaderFooter ) + { + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); + m_pSerializer->endElementNS( XML_w, XML_r ); + + m_bPostponedPageBreak = false; + } + + // merge the properties _before_ the run (strictly speaking, just + // after the start of the paragraph) + m_pSerializer->mergeTopMarks(Tag_StartParagraphProperties, sax_fastparser::MergeMarks::PREPEND); +} + +void DocxAttributeOutput::SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame ) +{ + m_nStateOfFlyFrame = nStateOfFlyFrame; +} + +void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode ) +{ + m_bAnchorLinkedToNode = bAnchorLinkedToNode ; +} + +void DocxAttributeOutput::ResetFlyProcessingFlag() +{ + m_bPostponedProcessingFly = false ; +} + +bool DocxAttributeOutput::IsFlyProcessingPostponed() +{ + return m_bPostponedProcessingFly; +} + +void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ ) +{ + // Don't start redline data here, possibly there is a hyperlink later, and + // that has to be started first. + m_pRedlineData = pRedlineData; + + // this mark is used to be able to enclose the run inside a sdr tag. + m_pSerializer->mark(Tag_StartRun_1); + + // postpone the output of the start of a run (there are elements that need + // to be written before the start of the run, but we learn which they are + // _inside_ of the run) + m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start" + + // postpone the output of the text (we get it before the run properties, + // but must write it after them) + m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text" +} + +void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/) +{ + int nFieldsInPrevHyperlink = m_nFieldsInHyperlink; + // Reset m_nFieldsInHyperlink if a new hyperlink is about to start + if ( m_pHyperlinkAttrList.is() ) + { + m_nFieldsInHyperlink = 0; + } + + // Write field starts + for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); ) + { + // Add the fields starts for all but hyperlinks and TOCs + if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN) + { + StartField_Impl( pNode, nPos, *pIt ); + + // Remove the field from the stack if only the start has to be written + // Unknown fields should be removed too + if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) ) + { + pIt = m_Fields.erase( pIt ); + continue; + } + + if (m_startedHyperlink || m_pHyperlinkAttrList.is()) + { + ++m_nFieldsInHyperlink; + } + } + ++pIt; + } + + // write the run properties + the text, already in the correct order + m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above + + // level down, to be able to prepend the actual run start attribute (just + // before "postponed run start") + m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start" + bool bCloseEarlierSDT = false; + + if (m_bEndCharSdt) + { + // This is the common case: "close sdt before the current run" was requested by the next run. + + // if another sdt starts in this run, then wait + // as closing the sdt now, might cause nesting of sdts + if (m_nRunSdtPrToken > 0) + bCloseEarlierSDT = true; + else + EndSdtBlock(); + m_bEndCharSdt = false; + m_bStartedCharSdt = false; + } + + if ( m_closeHyperlinkInPreviousRun ) + { + if ( m_startedHyperlink ) + { + for ( int i = 0; i < nFieldsInPrevHyperlink; i++ ) + { + // If fields begin before hyperlink then + // it should end before hyperlink close + EndField_Impl( pNode, nPos, m_Fields.back( ) ); + m_Fields.pop_back(); + } + m_pSerializer->endElementNS( XML_w, XML_hyperlink ); + m_startedHyperlink = false; + m_endPageRef = false; + m_nHyperLinkCount--; + } + m_closeHyperlinkInPreviousRun = false; + } + + // Write the hyperlink and toc fields starts + for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); ) + { + // Add the fields starts for hyperlinks, TOCs and index marks + if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN)) + { + StartRedline( m_pRedlineData ); + StartField_Impl( pNode, nPos, *pIt, true ); + EndRedline( m_pRedlineData ); + + if (m_startedHyperlink) + ++m_nFieldsInHyperlink; + + // Remove the field if no end needs to be written + if (!pIt->bSep) + { + pIt = m_Fields.erase( pIt ); + continue; + } + } + if (pIt->bSep && !pIt->pField) + { + CmdEndField_Impl(pNode, nPos, true); + // Remove the field if no end needs to be written + if (!pIt->bClose) + { + pIt = m_Fields.erase( pIt ); + continue; + } + } + ++pIt; + } + + // Start the hyperlink after the fields separators or we would generate invalid file + bool newStartedHyperlink(false); + if ( m_pHyperlinkAttrList.is() ) + { + // if we are ending a hyperlink and there's another one starting here, + // don't do this, so that the fields are closed further down when + // the end hyperlink is handled, which is more likely to put the end in + // the right place, as far as i can tell (not very far in this muck) + if (!m_closeHyperlinkInThisRun) + { + // end ToX fields that want to end _before_ starting the hyperlink + for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); ) + { + if (it->bClose && !it->pField) + { + EndField_Impl( pNode, nPos, *it ); + it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1)); + } + else + { + ++it; + } + } + } + newStartedHyperlink = true; + + XFastAttributeListRef xAttrList ( m_pHyperlinkAttrList.get() ); + m_pHyperlinkAttrList.clear(); + + m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList ); + m_startedHyperlink = true; + m_nHyperLinkCount++; + } + + // if there is some redlining in the document, output it + StartRedline( m_pRedlineData ); + + // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks. + // The same is applied for permission ranges. + // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end. + DoWriteBookmarksStart(m_rBookmarksStart); + DoWriteBookmarksEnd(m_rBookmarksEnd); + DoWritePermissionsStart(); + DoWriteAnnotationMarks(); + + if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc")) + { + OUString sToken; + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_rPr); + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin"); + m_pSerializer->endElementNS( XML_w, XML_fldChar ); + m_pSerializer->endElementNS( XML_w, XML_r ); + + + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_rPr); + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph. + DoWriteCmd( sToken ); + m_pSerializer->endElementNS( XML_w, XML_r ); + + // Write the Field separator + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_rPr); + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + m_pSerializer->singleElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "separate" ); + m_pSerializer->endElementNS( XML_w, XML_r ); + // At start of every "PAGEREF" field m_endPageRef value should be true. + m_endPageRef = true; + } + + DoWriteBookmarkStartIfExist(nPos); + + m_pSerializer->startElementNS(XML_w, XML_r); + if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is()) + { + RunText("\t") ; + } + m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above + + if ( !m_sRawText.isEmpty() ) + { + RunText( m_sRawText ); + m_sRawText.clear(); + } + + // write the run start + the run content + m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start" + // append the actual run end + m_pSerializer->endElementNS( XML_w, XML_r ); + + // if there is some redlining in the document, output it + // (except in the case of fields with multiple runs) + EndRedline( m_pRedlineData ); + + // enclose in a sdt block, if necessary: if one is already started, then don't do it for now + // (so on export sdt blocks are never nested ATM) + if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt ) + { + rtl::Reference<sax_fastparser::FastAttributeList> pRunSdtPrTokenAttributes; + WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false ); + } + else + { + //These should be written out to the actual Node and not to the anchor. + //Clear them as they will be repopulated when the node is processed. + m_nRunSdtPrToken = 0; + lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias ); + } + + if (bCloseEarlierSDT) + { + m_pSerializer->mark(Tag_EndRun_2); + EndSdtBlock(); + m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND); + } + + m_pSerializer->mergeTopMarks(Tag_StartRun_1); + + // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission. + DoWritePermissionsEnd(); + + for (const auto& rpMath : m_aPostponedMaths) + WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment); + m_aPostponedMaths.clear(); + + for (const auto& rpControl : m_aPostponedFormControls) + WritePostponedFormControl(rpControl); + m_aPostponedFormControls.clear(); + + WritePostponedActiveXControl(false); + + WritePendingPlaceholder(); + + if ( !m_bWritingField ) + { + m_pRedlineData = nullptr; + } + + if ( m_closeHyperlinkInThisRun ) + { + if ( m_startedHyperlink ) + { + if( m_endPageRef ) + { + // Hyperlink is started and fldchar "end" needs to be written for PAGEREF + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_rPr); + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + m_pSerializer->singleElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "end" ); + m_pSerializer->endElementNS( XML_w, XML_r ); + m_endPageRef = false; + m_hyperLinkAnchor.clear(); + } + for ( int i = 0; i < m_nFieldsInHyperlink; i++ ) + { + // If fields begin after hyperlink start then + // it should end before hyperlink close + EndField_Impl( pNode, nPos, m_Fields.back( ) ); + m_Fields.pop_back(); + } + m_nFieldsInHyperlink = 0; + + m_pSerializer->endElementNS( XML_w, XML_hyperlink ); + m_startedHyperlink = false; + m_nHyperLinkCount--; + } + m_closeHyperlinkInThisRun = false; + } + + if (!newStartedHyperlink) + { + while ( m_Fields.begin() != m_Fields.end() ) + { + EndField_Impl( pNode, nPos, m_Fields.front( ) ); + m_Fields.erase( m_Fields.begin( ) ); + } + m_nFieldsInHyperlink = 0; + } + + // end ToX fields + for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); ) + { + if (it->bClose && !it->pField) + { + EndField_Impl( pNode, nPos, *it ); + it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1)); + } + else + { + ++it; + } + } + + if ( m_pRedlineData ) + { + EndRedline( m_pRedlineData ); + m_pRedlineData = nullptr; + } + + DoWriteBookmarksStart(m_rFinalBookmarksStart); + DoWriteBookmarksEnd(m_rFinalBookmarksEnd); + DoWriteBookmarkEndIfExist(nPos); +} + +void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString & bookmarkName) +{ + m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart, + FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), + FSNS(XML_w, XML_name), BookmarkToWord(bookmarkName).toUtf8()); +} + +void DocxAttributeOutput::DoWriteBookmarkTagEnd(const OUString & bookmarkName) +{ + const auto nameToIdIter = m_rOpenedBookmarksIds.find(bookmarkName); + if (nameToIdIter != m_rOpenedBookmarksIds.end()) + { + const sal_Int32 nId = nameToIdIter->second; + + m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd, + FSNS(XML_w, XML_id), OString::number(nId)); + } +} + +void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos) +{ + auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + DoWriteBookmarkTagStart(aIter->second); + m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId; + m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8); + m_nNextBookmarkId++; + } +} + +void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos) +{ + auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + // Get the id of the bookmark + auto pPos = m_rOpenedBookmarksIds.find(aIter->second); + if (pPos != m_rOpenedBookmarksIds.end()) + { + // Output the bookmark + DoWriteBookmarkTagEnd(aIter->second); + m_rOpenedBookmarksIds.erase(aIter->second); + } + } +} + +/// Write the start bookmarks +void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts) +{ + for (const OUString & bookmarkName : rStarts) + { + // Output the bookmark + DoWriteBookmarkTagStart(bookmarkName); + + m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId; + m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(bookmarkName), RTL_TEXTENCODING_UTF8); + m_nNextBookmarkId++; + } + rStarts.clear(); +} + +/// export the end bookmarks +void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds) +{ + for (const OUString & bookmarkName : rEnds) + { + // Get the id of the bookmark + auto pPos = m_rOpenedBookmarksIds.find(bookmarkName); + if (pPos != m_rOpenedBookmarksIds.end()) + { + // Output the bookmark + DoWriteBookmarkTagEnd(bookmarkName); + + m_rOpenedBookmarksIds.erase(bookmarkName); + } + } + rEnds.clear(); +} + +// For construction of the special bookmark name template for permissions: +// see, PermInsertPosition::createBookmarkName() +// +// Syntax: +// - "permission-for-user:<permission-id>:<permission-user-name>" +// - "permission-for-group:<permission-id>:<permission-group-name>" +// +void DocxAttributeOutput::DoWritePermissionTagStart(const OUString & permission) +{ + OUString permissionIdAndName; + + if (permission.startsWith("permission-for-group:", &permissionIdAndName)) + { + const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':'); + const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex); + const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1); + + m_pSerializer->singleElementNS(XML_w, XML_permStart, + FSNS(XML_w, XML_id), BookmarkToWord(permissionId).toUtf8(), + FSNS(XML_w, XML_edGrp), BookmarkToWord(permissionName).toUtf8()); + } + else // if (permission.startsWith("permission-for-user:", &permissionIdAndName)) + { + const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':'); + const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex); + const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1); + + m_pSerializer->singleElementNS(XML_w, XML_permStart, + FSNS(XML_w, XML_id), BookmarkToWord(permissionId).toUtf8(), + FSNS(XML_w, XML_ed), BookmarkToWord(permissionName).toUtf8()); + } +} + + +// For construction of the special bookmark name template for permissions: +// see, PermInsertPosition::createBookmarkName() +// +// Syntax: +// - "permission-for-user:<permission-id>:<permission-user-name>" +// - "permission-for-group:<permission-id>:<permission-group-name>" +// +void DocxAttributeOutput::DoWritePermissionTagEnd(const OUString & permission) +{ + OUString permissionIdAndName; + + if (permission.startsWith("permission-for-group:", &permissionIdAndName) || + permission.startsWith("permission-for-user:", &permissionIdAndName)) + { + const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':'); + const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex); + + m_pSerializer->singleElementNS(XML_w, XML_permEnd, + FSNS(XML_w, XML_id), BookmarkToWord(permissionId).toUtf8()); + } +} + +/// Write the start permissions +void DocxAttributeOutput::DoWritePermissionsStart() +{ + for (const OUString & permission : m_rPermissionsStart) + { + DoWritePermissionTagStart(permission); + } + m_rPermissionsStart.clear(); +} + +/// export the end permissions +void DocxAttributeOutput::DoWritePermissionsEnd() +{ + for (const OUString & permission : m_rPermissionsEnd) + { + DoWritePermissionTagEnd(permission); + } + m_rPermissionsEnd.clear(); +} + +void DocxAttributeOutput::DoWriteAnnotationMarks() +{ + // Write the start annotation marks + for ( const auto & rName : m_rAnnotationMarksStart ) + { + // Output the annotation mark + /* Ensure that the existing Annotation Marks are not overwritten + as it causes discrepancy when DocxAttributeOutput::PostitField + refers to this map & while mapping comment id's in document.xml & + comment.xml. + */ + if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) ) + { + const sal_Int32 nId = m_nNextAnnotationMarkId++; + m_rOpenedAnnotationMarksIds[rName] = nId; + m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart, + FSNS( XML_w, XML_id ), OString::number(nId) ); + m_sLastOpenedAnnotationMark = rName; + } + } + m_rAnnotationMarksStart.clear(); + + // export the end annotation marks + for ( const auto & rName : m_rAnnotationMarksEnd ) + { + // Get the id of the annotation mark + std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName ); + if ( pPos != m_rOpenedAnnotationMarksIds.end( ) ) + { + const sal_Int32 nId = ( *pPos ).second; + m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd, + FSNS( XML_w, XML_id ), OString::number(nId) ); + m_rOpenedAnnotationMarksIds.erase( rName ); + + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ), + OString::number(nId) ); + m_pSerializer->endElementNS(XML_w, XML_r); + } + } + m_rAnnotationMarksEnd.clear(); +} + +void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos ) +{ + const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark; + FieldMarkParamsHelper params( rFieldmark ); + + OUString sEntryMacro; + params.extractParam("EntryMacro", sEntryMacro); + OUString sExitMacro; + params.extractParam("ExitMacro", sExitMacro); + OUString sHelp; + params.extractParam("Help", sHelp); + OUString sHint; + params.extractParam("Hint", sHint); // .docx StatusText + if ( sHint.isEmpty() ) + params.extractParam("Description", sHint); // .doc StatusText + + if ( rInfos.eType == ww::eFORMDROPDOWN ) + { + uno::Sequence< OUString> vListEntries; + OUString sName, sSelected; + + params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries ); + if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT) + vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT); + + sName = params.getName(); + sal_Int32 nSelectedIndex = 0; + + if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) ) + { + if (nSelectedIndex < vListEntries.getLength() ) + sSelected = vListEntries[ nSelectedIndex ]; + } + + GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries ); + } + else if ( rInfos.eType == ww::eFORMCHECKBOX ) + { + OUString sName; + bool bChecked = false; + + params.extractParam( ODF_FORMCHECKBOX_NAME, sName ); + + const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark); + if ( pCheckboxFm && pCheckboxFm->IsChecked() ) + bChecked = true; + + FFDataWriterHelper ffdataOut( m_pSerializer ); + ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked ); + } + else if ( rInfos.eType == ww::eFORMTEXT ) + { + OUString sType; + params.extractParam("Type", sType); + OUString sDefaultText; + params.extractParam("Content", sDefaultText); + sal_uInt16 nMaxLength = 0; + params.extractParam("MaxLength", nMaxLength); + OUString sFormat; + params.extractParam("Format", sFormat); + FFDataWriterHelper ffdataOut( m_pSerializer ); + ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint, + sType, sDefaultText, nMaxLength, sFormat ); + } +} + +void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang) +{ + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + if(!sFullDate.isEmpty()) + m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate.toUtf8()); + else + m_pSerializer->startElementNS(XML_w, XML_date); + + // Replace quotation mark used for marking static strings in date format + OString sUTF8DateFormat = sDateFormat.toUtf8(); + sUTF8DateFormat = sUTF8DateFormat.replaceAll("\"", "'"); + m_pSerializer->singleElementNS(XML_w, XML_dateFormat, + FSNS(XML_w, XML_val), sUTF8DateFormat); + m_pSerializer->singleElementNS(XML_w, XML_lid, + FSNS(XML_w, XML_val), sLang.toUtf8()); + m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs, + FSNS(XML_w, XML_val), "dateTime"); + m_pSerializer->singleElementNS(XML_w, XML_calendar, + FSNS(XML_w, XML_val), "gregorian"); + + m_pSerializer->endElementNS(XML_w, XML_date); + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_sdtContent); +} + +void DocxAttributeOutput::WriteSdtEnd() +{ + m_pSerializer->endElementNS(XML_w, XML_sdtContent); + m_pSerializer->endElementNS(XML_w, XML_sdt); +} + +void DocxAttributeOutput::WriteSdtDropDownStart( + OUString const& rName, + OUString const& rSelected, + uno::Sequence<OUString> const& rListItems) +{ + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + m_pSerializer->singleElementNS(XML_w, XML_alias, + FSNS(XML_w, XML_val), OUStringToOString(rName, RTL_TEXTENCODING_UTF8)); + + sal_Int32 nId = comphelper::findValue(rListItems, rSelected); + if (nId == -1) + { + nId = 0; + } + + m_pSerializer->startElementNS(XML_w, XML_dropDownList, + FSNS(XML_w, XML_lastValue), OString::number(nId)); + + for (auto const& rItem : rListItems) + { + auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8)); + m_pSerializer->singleElementNS(XML_w, XML_listItem, + FSNS(XML_w, XML_value), item, + FSNS(XML_w, XML_displayText), item); + } + + m_pSerializer->endElementNS(XML_w, XML_dropDownList); + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_sdtContent); +} + +void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected, + uno::Sequence<OUString> const& rListItems) +{ + // note: rSelected might be empty? + sal_Int32 nId = comphelper::findValue(rListItems, rSelected); + if (nId == -1) + { + nId = 0; + } + + // the lastValue only identifies the entry in the list, also export + // currently selected item's displayText as run content (if one exists) + if (rListItems.size()) + { + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_t); + m_pSerializer->writeEscaped(rListItems[nId]); + m_pSerializer->endElementNS(XML_w, XML_t); + m_pSerializer->endElementNS(XML_w, XML_r); + } + + WriteSdtEnd(); +} + +void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ) +{ + if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN ) + { + // Expand unsupported fields + RunText( rInfos.pField->GetFieldName() ); + } + else if ( rInfos.eType == ww::eFORMDATE ) + { + const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark); + FieldMarkParamsHelper params(rFieldmark); + + OUString sFullDate; + OUString sCurrentDate; + params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate ); + if(!sCurrentDate.isEmpty()) + { + sFullDate = sCurrentDate + "T00:00:00Z"; + } + else + { + std::pair<bool, double> aResult = rFieldmark.GetCurrentDate(); + if(aResult.first) + { + sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z"; + } + } + + OUString sDateFormat; + params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat ); + OUString sLang; + params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang ); + + WriteFormDateStart( sFullDate, sDateFormat, sLang ); + } + else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) + { + assert(!rInfos.pFieldmark); + SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get())); + WriteSdtDropDownStart(rField2.GetName(), + rField2.GetSelectedItem(), + rField2.GetItemSequence()); + } + else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands + { + if ( bWriteRun ) + m_pSerializer->startElementNS(XML_w, XML_r); + + if ( rInfos.eType == ww::eFORMDROPDOWN ) + { + m_pSerializer->startElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "begin" ); + assert( rInfos.pFieldmark && !rInfos.pField ); + WriteFFData(rInfos); + m_pSerializer->endElementNS( XML_w, XML_fldChar ); + + if ( bWriteRun ) + m_pSerializer->endElementNS( XML_w, XML_r ); + + CmdField_Impl( pNode, nPos, rInfos, bWriteRun ); + } + else + { + // Write the field start + if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD ) + { + m_pSerializer->startElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "begin", + FSNS( XML_w, XML_fldLock ), "true" ); + } + else + { + m_pSerializer->startElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "begin" ); + } + + if ( rInfos.pFieldmark ) + WriteFFData( rInfos ); + + m_pSerializer->endElementNS( XML_w, XML_fldChar ); + + if ( bWriteRun ) + m_pSerializer->endElementNS( XML_w, XML_r ); + + // The hyperlinks fields can't be expanded: the value is + // normally in the text run + if ( !rInfos.pField ) + CmdField_Impl( pNode, nPos, rInfos, bWriteRun ); + else + m_bWritingField = true; + } + } +} + +void DocxAttributeOutput::DoWriteCmd( const OUString& rCmd ) +{ + OUString sCmd = rCmd.trim(); + if (sCmd.startsWith("SEQ")) + { + OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim(); + m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark); + } + // Write the Field command + sal_Int32 nTextToken = XML_instrText; + if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete ) + nTextToken = XML_delInstrText; + + m_pSerializer->startElementNS(XML_w, nTextToken); + m_pSerializer->writeEscaped( rCmd ); + m_pSerializer->endElementNS( XML_w, nTextToken ); + +} + +void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ) +{ + bool bWriteCombChars(false); + + // Write the Field instruction + { + if ( bWriteRun ) + { + m_pSerializer->startElementNS(XML_w, XML_r); + + if (rInfos.eType == ww::eEQ) + bWriteCombChars = true; + + DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars ); + } + + sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 }; + while ( nIdx >= 0 ) + { + OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx ); + if ( rInfos.eType == ww::eCREATEDATE + || rInfos.eType == ww::eSAVEDATE + || rInfos.eType == ww::ePRINTDATE + || rInfos.eType == ww::eDATE + || rInfos.eType == ww::eTIME ) + { + sToken = sToken.replaceAll("NNNN", "dddd"); + sToken = sToken.replaceAll("NN", "ddd"); + } + else if ( rInfos.eType == ww::eEquals ) + { + // Use original OOXML formula, if it exists and its conversion hasn't been changed + bool bIsChanged = true; + if ( pNode->GetTableBox() ) + { + if ( const SfxGrabBagItem* pItem = pNode->GetTableBox()->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG) ) + { + OUString sActualFormula = sToken.trim(); + const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); + std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted"); + if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 && + sActualFormula.copy(1).trim() == aStoredFormula->second.get<OUString>().trim() ) + { + aStoredFormula = rGrabBag.find("CellFormula"); + if ( aStoredFormula != rGrabBag.end() ) + { + sToken = " = " + aStoredFormula->second.get<OUString>(); + bIsChanged = false; + } + } + } + } + + if ( bIsChanged ) + { + UErrorCode nErr(U_ZERO_ERROR); + icu::UnicodeString sInput(sToken.getStr()); + // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2 + icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr); + sToken = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr).getTerminatedBuffer(); + } + } + + // Write the Field command + DoWriteCmd( sToken ); + + // Replace tabs by </instrText><tab/><instrText> + if ( nIdx > 0 ) // Is another token expected? + RunText( "\t" ); + } + + if ( bWriteRun ) + { + m_pSerializer->endElementNS( XML_w, XML_r ); + } + } +} + +void DocxAttributeOutput::CmdEndField_Impl(SwTextNode const*const pNode, + sal_Int32 const nPos, bool const bWriteRun) +{ + // Write the Field separator + if ( bWriteRun ) + { + m_pSerializer->startElementNS(XML_w, XML_r); + DoWriteFieldRunProperties( pNode, nPos ); + } + + m_pSerializer->singleElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "separate" ); + + if ( bWriteRun ) + { + m_pSerializer->endElementNS( XML_w, XML_r ); + } +} + +/// Writes properties for run that is used to separate field implementation. +/// There are several runs are used: +/// <w:r> +/// <w:rPr> +/// <!-- properties written with StartRunProperties() / EndRunProperties(). +/// </w:rPr> +/// <w:fldChar w:fldCharType="begin" /> +/// </w:r> +/// <w:r> +/// <w:rPr> +/// <!-- properties written with DoWriteFieldRunProperties() +/// </w:rPr> +/// <w:instrText>TIME \@"HH:mm:ss"</w:instrText> +/// </w:r> +/// <w:r> +/// <w:rPr> +/// <!-- properties written with DoWriteFieldRunProperties() +/// </w:rPr> +/// <w:fldChar w:fldCharType="separate" /> +/// </w:r> +/// <w:r> +/// <w:rPr> +/// <!-- properties written with DoWriteFieldRunProperties() +/// </w:rPr> +/// <w:t>14:01:13</w:t> +/// </w:r> +/// <w:r> +/// <w:rPr> +/// <!-- properties written with DoWriteFieldRunProperties() +/// </w:rPr> +/// <w:fldChar w:fldCharType="end" /> +/// </w:r> +/// See, tdf#38778 +void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars) +{ + if (! pNode) + { + // nothing to do + return; + } + + m_bPreventDoubleFieldsHandling = true; + + { + m_pSerializer->startElementNS(XML_w, XML_rPr); + + // 1. output webHidden flag + if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() ) + { + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + } + + // 2. find all active character properties + SwWW8AttrIter aAttrIt( m_rExport, *pNode ); + aAttrIt.OutAttr( nPos, bWriteCombChars ); + + // 3. write the character properties + WriteCollectedRunProperties(); + + m_pSerializer->endElementNS( XML_w, XML_rPr ); + } + + m_bPreventDoubleFieldsHandling = false; +} + +void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos ) +{ + if (rInfos.eType == ww::eFORMDATE) + { + WriteSdtEnd(); + return; + } + if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField) + { + // write selected item from End not Start to ensure that any bookmarks + // precede it + SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get())); + WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence()); + return; + } + + // The command has to be written before for the hyperlinks + if ( rInfos.pField ) + { + CmdField_Impl( pNode, nPos, rInfos, true ); + CmdEndField_Impl( pNode, nPos, true ); + } + + // Write the bookmark start if any + if ( !m_sFieldBkm.isEmpty() ) + { + DoWriteBookmarkTagStart(m_sFieldBkm); + } + + if (rInfos.pField ) // For hyperlinks and TOX + { + // Write the Field latest value + m_pSerializer->startElementNS(XML_w, XML_r); + DoWriteFieldRunProperties( pNode, nPos ); + + OUString sExpand; + if(rInfos.eType == ww::eCITATION) + { + sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get()) + ->ExpandCitation(AUTH_FIELD_TITLE, nullptr); + } + else if(rInfos.eType != ww::eFORMDROPDOWN) + { + sExpand = rInfos.pField->ExpandField(true, nullptr); + } + // newlines embedded in fields are 0x0B in MSO and 0x0A for us + RunText(sExpand.replace(0x0A, 0x0B)); + + m_pSerializer->endElementNS( XML_w, XML_r ); + } + + // Write the bookmark end if any + if ( !m_sFieldBkm.isEmpty() ) + { + DoWriteBookmarkTagEnd(m_sFieldBkm); + + m_nNextBookmarkId++; + } + + // Write the Field end + if ( rInfos.bClose ) + { + m_bWritingField = false; + m_pSerializer->startElementNS(XML_w, XML_r); + DoWriteFieldRunProperties( pNode, nPos ); + m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end"); + m_pSerializer->endElementNS( XML_w, XML_r ); + } + // Write the ref field if a bookmark had to be set and the field + // should be visible + if ( rInfos.pField ) + { + sal_uInt16 nSubType = rInfos.pField->GetSubType( ); + bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp; + bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0; + + if ( ( !m_sFieldBkm.isEmpty() ) && bShowRef ) + { + // Write the field beginning + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS( XML_w, XML_fldChar, + FSNS( XML_w, XML_fldCharType ), "begin" ); + m_pSerializer->endElementNS( XML_w, XML_r ); + + rInfos.sCmd = FieldString( ww::eREF ); + rInfos.sCmd += "\""; + rInfos.sCmd += m_sFieldBkm; + rInfos.sCmd += "\" "; + + // Clean the field bookmark data to avoid infinite loop + m_sFieldBkm = OUString( ); + + // Write the end of the field + EndField_Impl( pNode, nPos, rInfos ); + } + } +} + +void DocxAttributeOutput::StartRunProperties() +{ + // postpone the output so that we can later [in EndRunProperties()] + // prepend the properties before the text + m_pSerializer->mark(Tag_StartRunProperties); + + m_pSerializer->startElementNS(XML_w, XML_rPr); + + if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() ) + { + m_pSerializer->singleElementNS(XML_w, XML_webHidden); + } + InitCollectedRunProperties(); + + assert( !m_pPostponedGraphic ); + m_pPostponedGraphic.reset(new std::vector<PostponedGraphic>); + + assert( !m_pPostponedDiagrams ); + m_pPostponedDiagrams.reset(new std::vector<PostponedDiagram>); + + assert(!m_pPostponedDMLDrawings); + m_pPostponedDMLDrawings.reset(new std::vector<PostponedDrawing>); + + assert( !m_pPostponedOLEs ); + m_pPostponedOLEs.reset(new std::vector<PostponedOLE>); +} + +void DocxAttributeOutput::InitCollectedRunProperties() +{ + m_pFontsAttrList = nullptr; + m_pEastAsianLayoutAttrList = nullptr; + m_pCharLangAttrList = nullptr; + + // Write the elements in the spec order + static const sal_Int32 aOrder[] = + { + FSNS( XML_w, XML_rStyle ), + FSNS( XML_w, XML_rFonts ), + FSNS( XML_w, XML_b ), + FSNS( XML_w, XML_bCs ), + FSNS( XML_w, XML_i ), + FSNS( XML_w, XML_iCs ), + FSNS( XML_w, XML_caps ), + FSNS( XML_w, XML_smallCaps ), + FSNS( XML_w, XML_strike ), + FSNS( XML_w, XML_dstrike ), + FSNS( XML_w, XML_outline ), + FSNS( XML_w, XML_shadow ), + FSNS( XML_w, XML_emboss ), + FSNS( XML_w, XML_imprint ), + FSNS( XML_w, XML_noProof ), + FSNS( XML_w, XML_snapToGrid ), + FSNS( XML_w, XML_vanish ), + FSNS( XML_w, XML_webHidden ), + FSNS( XML_w, XML_color ), + FSNS( XML_w, XML_spacing ), + FSNS( XML_w, XML_w ), + FSNS( XML_w, XML_kern ), + FSNS( XML_w, XML_position ), + FSNS( XML_w, XML_sz ), + FSNS( XML_w, XML_szCs ), + FSNS( XML_w, XML_highlight ), + FSNS( XML_w, XML_u ), + FSNS( XML_w, XML_effect ), + FSNS( XML_w, XML_bdr ), + FSNS( XML_w, XML_shd ), + FSNS( XML_w, XML_fitText ), + FSNS( XML_w, XML_vertAlign ), + FSNS( XML_w, XML_rtl ), + FSNS( XML_w, XML_cs ), + FSNS( XML_w, XML_em ), + FSNS( XML_w, XML_lang ), + FSNS( XML_w, XML_eastAsianLayout ), + FSNS( XML_w, XML_specVanish ), + FSNS( XML_w, XML_oMath ), + FSNS( XML_w, XML_rPrChange ), + FSNS( XML_w, XML_del ), + FSNS( XML_w14, XML_glow ), + FSNS( XML_w14, XML_shadow ), + FSNS( XML_w14, XML_reflection ), + FSNS( XML_w14, XML_textOutline ), + FSNS( XML_w14, XML_textFill ), + FSNS( XML_w14, XML_scene3d ), + FSNS( XML_w14, XML_props3d ), + FSNS( XML_w14, XML_ligatures ), + FSNS( XML_w14, XML_numForm ), + FSNS( XML_w14, XML_numSpacing ), + FSNS( XML_w14, XML_stylisticSets ), + FSNS( XML_w14, XML_cntxtAlts ), + }; + + // postpone the output so that we can later [in EndParagraphProperties()] + // prepend the properties before the run + m_pSerializer->mark(Tag_InitCollectedRunProperties, comphelper::containerToSequence(aOrder)); +} + +namespace +{ + +struct NameToId +{ + OUString maName; + sal_Int32 maId; +}; + +const NameToId constNameToIdMapping[] = +{ + { OUString("glow"), FSNS( XML_w14, XML_glow ) }, + { OUString("shadow"), FSNS( XML_w14, XML_shadow ) }, + { OUString("reflection"), FSNS( XML_w14, XML_reflection ) }, + { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) }, + { OUString("textFill"), FSNS( XML_w14, XML_textFill ) }, + { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) }, + { OUString("props3d"), FSNS( XML_w14, XML_props3d ) }, + { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) }, + { OUString("numForm"), FSNS( XML_w14, XML_numForm ) }, + { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) }, + { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) }, + { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) }, + + { OUString("val"), FSNS( XML_w14, XML_val ) }, + { OUString("rad"), FSNS( XML_w14, XML_rad ) }, + { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) }, + { OUString("stA"), FSNS( XML_w14, XML_stA ) }, + { OUString("stPos"), FSNS( XML_w14, XML_stPos ) }, + { OUString("endA"), FSNS( XML_w14, XML_endA ) }, + { OUString("endPos"), FSNS( XML_w14, XML_endPos ) }, + { OUString("dist"), FSNS( XML_w14, XML_dist ) }, + { OUString("dir"), FSNS( XML_w14, XML_dir ) }, + { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) }, + { OUString("sx"), FSNS( XML_w14, XML_sx ) }, + { OUString("sy"), FSNS( XML_w14, XML_sy ) }, + { OUString("kx"), FSNS( XML_w14, XML_kx ) }, + { OUString("ky"), FSNS( XML_w14, XML_ky ) }, + { OUString("algn"), FSNS( XML_w14, XML_algn ) }, + { OUString("w"), FSNS( XML_w14, XML_w ) }, + { OUString("cap"), FSNS( XML_w14, XML_cap ) }, + { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) }, + { OUString("pos"), FSNS( XML_w14, XML_pos ) }, + { OUString("ang"), FSNS( XML_w14, XML_ang ) }, + { OUString("scaled"), FSNS( XML_w14, XML_scaled ) }, + { OUString("path"), FSNS( XML_w14, XML_path ) }, + { OUString("l"), FSNS( XML_w14, XML_l ) }, + { OUString("t"), FSNS( XML_w14, XML_t ) }, + { OUString("r"), FSNS( XML_w14, XML_r ) }, + { OUString("b"), FSNS( XML_w14, XML_b ) }, + { OUString("lim"), FSNS( XML_w14, XML_lim ) }, + { OUString("prst"), FSNS( XML_w14, XML_prst ) }, + { OUString("rig"), FSNS( XML_w14, XML_rig ) }, + { OUString("lat"), FSNS( XML_w14, XML_lat ) }, + { OUString("lon"), FSNS( XML_w14, XML_lon ) }, + { OUString("rev"), FSNS( XML_w14, XML_rev ) }, + { OUString("h"), FSNS( XML_w14, XML_h ) }, + { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) }, + { OUString("contourW"), FSNS( XML_w14, XML_contourW ) }, + { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) }, + { OUString("id"), FSNS( XML_w14, XML_id ) }, + + { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) }, + { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) }, + { OUString("tint"), FSNS( XML_w14, XML_tint ) }, + { OUString("shade"), FSNS( XML_w14, XML_shade ) }, + { OUString("alpha"), FSNS( XML_w14, XML_alpha ) }, + { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) }, + { OUString("sat"), FSNS( XML_w14, XML_sat ) }, + { OUString("satOff"), FSNS( XML_w14, XML_satOff ) }, + { OUString("satMod"), FSNS( XML_w14, XML_satMod ) }, + { OUString("lum"), FSNS( XML_w14, XML_lum ) }, + { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) }, + { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) }, + { OUString("noFill"), FSNS( XML_w14, XML_noFill ) }, + { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) }, + { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) }, + { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) }, + { OUString("gs"), FSNS( XML_w14, XML_gs ) }, + { OUString("pos"), FSNS( XML_w14, XML_pos ) }, + { OUString("lin"), FSNS( XML_w14, XML_lin ) }, + { OUString("path"), FSNS( XML_w14, XML_path ) }, + { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) }, + { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) }, + { OUString("round"), FSNS( XML_w14, XML_round ) }, + { OUString("bevel"), FSNS( XML_w14, XML_bevel ) }, + { OUString("miter"), FSNS( XML_w14, XML_miter ) }, + { OUString("camera"), FSNS( XML_w14, XML_camera ) }, + { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) }, + { OUString("rot"), FSNS( XML_w14, XML_rot ) }, + { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) }, + { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) }, + { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) }, + { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) }, + { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) }, +}; + +std::optional<sal_Int32> lclGetElementIdForName(const OUString& rName) +{ + for (auto const & i : constNameToIdMapping) + { + if (rName == i.maName) + { + return i.maId; + } + } + return std::optional<sal_Int32>(); +} + +void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer) +{ + css::uno::Sequence<css::beans::PropertyValue> aAttributes; + FastAttributeList* pAttributes = FastSerializerHelper::createAttrList(); + + for (const auto& rElement : rElements) + { + if (rElement.Name == "attributes") + { + rElement.Value >>= aAttributes; + } + } + + for (const auto& rAttribute : std::as_const(aAttributes)) + { + uno::Any aAny = rAttribute.Value; + OString aValue; + + if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get()) + { + aValue = OString::number(aAny.get<sal_Int32>()); + } + else if(aAny.getValueType() == cppu::UnoType<OUString>::get()) + { + aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US); + } + + std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name); + if(aSubElementId) + pAttributes->add(*aSubElementId, aValue.getStr()); + } + + XFastAttributeListRef xAttributesList( pAttributes ); + + pSerializer->startElement(aElementId, xAttributesList); + + for (const auto& rElement : rElements) + { + css::uno::Sequence<css::beans::PropertyValue> aSumElements; + + std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name); + if(aSubElementId) + { + rElement.Value >>= aSumElements; + lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer); + } + } + + pSerializer->endElement(aElementId); +} + +} + +void DocxAttributeOutput::WriteCollectedRunProperties() +{ + // Write all differed properties + if ( m_pFontsAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pFontsAttrList.get() ); + m_pFontsAttrList.clear(); + m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList ); + } + + if ( m_pColorAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pColorAttrList.get() ); + + m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList ); + } + + if ( m_pEastAsianLayoutAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pEastAsianLayoutAttrList.get() ); + m_pEastAsianLayoutAttrList.clear(); + m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList ); + } + + if ( m_pCharLangAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pCharLangAttrList.get() ); + m_pCharLangAttrList.clear(); + m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList ); + } + + if (m_nCharTransparence != 0 && m_pColorAttrList && m_aTextEffectsGrabBag.empty()) + { + const char* pVal = nullptr; + m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pVal); + if (OString("auto") != pVal) + { + m_pSerializer->startElementNS(XML_w14, XML_textFill); + m_pSerializer->startElementNS(XML_w14, XML_solidFill); + m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal); + sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0; + m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence)); + m_pSerializer->endElementNS(XML_w14, XML_srgbClr); + m_pSerializer->endElementNS(XML_w14, XML_solidFill); + m_pSerializer->endElementNS(XML_w14, XML_textFill); + m_nCharTransparence = 0; + } + } + m_pColorAttrList.clear(); + for (const beans::PropertyValue & i : m_aTextEffectsGrabBag) + { + std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name); + if(aElementId) + { + uno::Sequence<beans::PropertyValue> aGrabBagSeq; + i.Value >>= aGrabBagSeq; + lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer); + } + } + m_aTextEffectsGrabBag.clear(); +} + +void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) +{ + // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties. + // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc. + Redline( pRedlineData ); + + WriteCollectedRunProperties(); + + // Merge the marks for the ordered elements + m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); + + m_pSerializer->endElementNS( XML_w, XML_rPr ); + + // write footnotes/endnotes if we have any + FootnoteEndnoteReference(); + + // merge the properties _before_ the run text (strictly speaking, just + // after the start of the run) + m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND); + + WritePostponedGraphic(); + + WritePostponedDiagram(); + //We need to write w:drawing tag after the w:rPr. + WritePostponedChart(); + + //We need to write w:pict tag after the w:rPr. + WritePostponedDMLDrawing(); + + WritePostponedOLE(); + + WritePostponedActiveXControl(true); +} + +void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj) +{ + if (pSdrObj) + { + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY_THROW); + uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + if( xPropSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + uno::Sequence< beans::PropertyValue > aGrabBag; + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) + { + xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag; + } + else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag")) + { + xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag; + } + + auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(), + [this](const beans::PropertyValue& rProp) { + return "SdtEndBefore" == rProp.Name && m_bStartedCharSdt && !m_bEndCharSdt; }); + if (pProp != aGrabBag.end()) + pProp->Value >>= m_bEndCharSdt; + } + } +} + +void DocxAttributeOutput::WritePostponedGraphic() +{ + for (const auto & rPostponedDiagram : *m_pPostponedGraphic) + FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size, + nullptr, nullptr, + rPostponedDiagram.pSdrObj); + m_pPostponedGraphic.reset(); +} + +void DocxAttributeOutput::WritePostponedDiagram() +{ + for( const auto & rPostponedDiagram : *m_pPostponedDiagrams ) + m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object, + *rPostponedDiagram.frame, m_anchorId++); + m_pPostponedDiagrams.reset(); +} + +bool DocxAttributeOutput::FootnoteEndnoteRefTag() +{ + if( m_footnoteEndnoteRefTag == 0 ) + return false; + + // output the character style for MS Word's benefit + const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ? + m_rExport.m_pDoc->GetFootnoteInfo() : m_rExport.m_pDoc->GetEndNoteInfo(); + const SwCharFormat* pCharFormat = rInfo.GetCharFormat( *m_rExport.m_pDoc ); + if ( pCharFormat ) + { + const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat))); + m_pSerializer->startElementNS(XML_w, XML_rPr); + m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); + m_pSerializer->endElementNS( XML_w, XML_rPr ); + } + + if (m_footnoteCustomLabel.isEmpty()) + m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag); + else + RunText(m_footnoteCustomLabel); + m_footnoteEndnoteRefTag = 0; + return true; +} + +/** Output sal_Unicode* as a run text (<t>the text</t>). + + When bMove is true, update rBegin to point _after_ the end of the text + + 1, meaning that it skips one character after the text. This is to make + the switch in DocxAttributeOutput::RunText() nicer ;-) + */ +static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken, + const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true ) +{ + const sal_Unicode *pBegin = rBegin; + + // skip one character after the end + if ( bMove ) + rBegin = pEnd + 1; + + if ( pBegin >= pEnd ) + return false; // we want to write at least one character + + // we have to add 'preserve' when starting/ending with space + if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' ) + { + pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); + } + else + pSerializer->startElementNS(XML_w, nTextToken); + + pSerializer->writeEscaped( OUString( pBegin, pEnd - pBegin ) ); + + pSerializer->endElementNS( XML_w, nTextToken ); + + return true; +} + +void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ ) +{ + if( m_closeHyperlinkInThisRun ) + { + m_closeHyperlinkInPreviousRun = true; + } + m_bRunTextIsOn = true; + // one text can be split into more <w:t>blah</w:t>'s by line breaks etc. + const sal_Unicode *pBegin = rText.getStr(); + const sal_Unicode *pEnd = pBegin + rText.getLength(); + + // the text run is usually XML_t, with the exception of the deleted text + sal_Int32 nTextToken = XML_t; + if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete ) + nTextToken = XML_delText; + + sal_Unicode prevUnicode = *pBegin; + + for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt ) + { + switch ( *pIt ) + { + case 0x09: // tab + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); + m_pSerializer->singleElementNS(XML_w, XML_tab); + prevUnicode = *pIt; + break; + case 0x0b: // line break + { + if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020) + { + m_pSerializer->singleElementNS(XML_w, XML_br); + prevUnicode = *pIt; + } + } + break; + case 0x1E: //non-breaking hyphen + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); + m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen); + prevUnicode = *pIt; + break; + case 0x1F: //soft (on demand) hyphen + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); + m_pSerializer->singleElementNS(XML_w, XML_softHyphen); + prevUnicode = *pIt; + break; + default: + if ( *pIt < 0x0020 ) // filter out the control codes + { + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ); + SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) ); + } + prevUnicode = *pIt; + break; + } + } + + impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false ); +} + +void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/) +{ + m_sRawText = rText; +} + +void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) +{ + WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() ); + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" ); + EndRun( &rNode, nPos ); // end run before starting ruby to avoid nested runs, and overlap + assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby + assert(!m_closeHyperlinkInPreviousRun); + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->startElementNS(XML_w, XML_ruby); + m_pSerializer->startElementNS(XML_w, XML_rubyPr); + + m_pSerializer->singleElementNS( XML_w, XML_rubyAlign, + FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) ); + sal_uInt32 nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10; + sal_uInt32 nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10; + m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps)); + + m_pSerializer->singleElementNS( XML_w, XML_hpsRaise, + FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) ); + + m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText, + FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) ); + + lang::Locale aLocale( SwBreakIt::Get()->GetLocale( + rNode.GetLang( nPos ) ) ); + OUString sLang( LanguageTag::convertToBcp47( aLocale) ); + m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang.toUtf8()); + + m_pSerializer->endElementNS( XML_w, XML_rubyPr ); + + m_pSerializer->startElementNS(XML_w, XML_rt); + StartRun( nullptr, nPos ); + StartRunProperties( ); + + if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat()) + { + const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat(); + sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0); + sal_uInt16 nWhichFont = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONT : RES_CHRATR_CJK_FONT; + sal_uInt16 nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE; + + CharFont(ItemGet<SvxFontItem>(*pFormat, nWhichFont)); + CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, nWhichFontSize)); + CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, RES_CHRATR_CTL_FONTSIZE)); + } + + EndRunProperties( nullptr ); + RunText( rRuby.GetText( ) ); + EndRun( &rNode, nPos ); + m_pSerializer->endElementNS( XML_w, XML_rt ); + + m_pSerializer->startElementNS(XML_w, XML_rubyBase); + StartRun( nullptr, nPos ); +} + +void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) +{ + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" ); + EndRun( &rNode, nPos ); + m_pSerializer->endElementNS( XML_w, XML_rubyBase ); + m_pSerializer->endElementNS( XML_w, XML_ruby ); + m_pSerializer->endElementNS( XML_w, XML_r ); + StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it +} + +bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) +{ + bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark ); + + if ( !pMark->isEmpty() ) + { + OUString sURL = *pLinkURL; + + if ( bBookMarkOnly ) + sURL = FieldString( ww::eHYPERLINK ); + else + sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\""; + + sURL += " \\l \"" + *pMark + "\""; + + if ( !rTarget.isEmpty() ) + sURL += " \\n " + rTarget; + + *pLinkURL = sURL; + } + + return bBookMarkOnly; +} + +void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) +{ + m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName)); + m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName)); +} + +bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget ) +{ + OUString sMark; + OUString sUrl; + + bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark ); + + m_hyperLinkAnchor = sMark; + + if ( !sMark.isEmpty() && !bBookmarkOnly ) + { + m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl ); + } + else + { + // Output a hyperlink XML element + m_pHyperlinkAttrList = FastSerializerHelper::createAttrList(); + + if ( !bBookmarkOnly ) + { + OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), + oox::getRelationship(Relationship::HYPERLINK), + sUrl, true ), RTL_TEXTENCODING_UTF8 ); + + m_pHyperlinkAttrList->add( FSNS( XML_r, XML_id), sId.getStr()); + } + else + { + // Is this a link to a sequence? Then try to replace that with a + // normal bookmark, as Word won't understand our special + // <seqname>!<index>|sequence syntax. + if (sMark.endsWith("|sequence")) + { + sal_Int32 nPos = sMark.indexOf('!'); + if (nPos != -1) + { + // Extract <seqname>, the field instruction text has the name quoted. + OUString aSequenceName = sMark.copy(0, nPos); + // Extract <index>. + sal_uInt32 nIndex = sMark.copy(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")).toUInt32(); + std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName); + if (it != m_aSeqBookmarksNames.end()) + { + std::vector<OString>& rNames = it->second; + if (rNames.size() > nIndex) + // We know the bookmark name for this sequence and this index, do the replacement. + sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8); + } + } + } + // Spaces are prohibited in bookmark name. + sMark = sMark.replace(' ', '_'); + m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ), + OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ).getStr( ) ); + } + + if ( !rTarget.isEmpty() ) + { + OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 ); + m_pHyperlinkAttrList->add(FSNS( XML_w, XML_tgtFrame ), soTarget.getStr()); + } + } + + return true; +} + +bool DocxAttributeOutput::EndURL(bool const) +{ + m_closeHyperlinkInThisRun = true; + if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc")) + { + m_endPageRef = true; + } + return true; +} + +void DocxAttributeOutput::FieldVanish( const OUString& rText, ww::eField eType ) +{ + WriteField_Impl( nullptr, eType, rText, FieldFlags::All ); +} + +// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: +// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) +// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) +void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) +{ + if ( !pRedlineData ) + return; + + OString aId( OString::number( pRedlineData->GetSeqNo() ) ); + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); + OString aAuthor( rAuthor.toUtf8() ); + OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) ); + + switch( pRedlineData->GetType() ) + { + case RedlineType::Insert: + break; + + case RedlineType::Delete: + break; + + case RedlineType::Format: + m_pSerializer->startElementNS( XML_w, XML_rPrChange, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + + m_pSerializer->endElementNS( XML_w, XML_rPrChange ); + break; + + case RedlineType::ParagraphFormat: + m_pSerializer->startElementNS( XML_w, XML_pPrChange, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + + // Check if there is any extra data stored in the redline object + if (pRedlineData->GetExtraData()) + { + const SwRedlineExtraData* pExtraData = pRedlineData->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(); + const OUString & sParaStyleName = pFormattingChanges->GetFormatName(); + if (pChangesSet || !sParaStyleName.isEmpty()) + { + m_pSerializer->mark(Tag_Redline_2); + + m_pSerializer->startElementNS(XML_w, XML_pPr); + + OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName ); + if ( !sStyleName.isEmpty() ) + m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName); + + // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information + // that should be collected by different properties in the core, and are all flushed together + // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called. + // So we need to store the current status of these lists, so that we can revert back to them when + // we are done exporting the redline attributes. + rtl::Reference<sax_fastparser::FastAttributeList> pFlyAttrList_Original(m_rExport.SdrExporter().getFlyAttrList()); + m_rExport.SdrExporter().getFlyAttrList().clear(); + rtl::Reference<sax_fastparser::FastAttributeList> pParagraphSpacingAttrList_Original(m_pParagraphSpacingAttrList); + m_pParagraphSpacingAttrList.clear(); + + // Output the redline item set + if (pChangesSet) + m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF ); + + // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' + WriteCollectedParagraphProperties(); + + // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' + m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original; + m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original; + + m_pSerializer->endElementNS( XML_w, XML_pPr ); + + m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND); + } + } + } + m_pSerializer->endElementNS( XML_w, XML_pPrChange ); + break; + + default: + SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType())); + break; + } +} + +// The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: +// 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) +// 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) +void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) +{ + if ( !pRedlineData ) + return; + + // FIXME check if it's necessary to travel over the Next()'s in pRedlineData + + OString aId( OString::number( m_nRedlineId++ ) ); + + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); + OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) ); + + OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) ); + + switch ( pRedlineData->GetType() ) + { + case RedlineType::Insert: + m_pSerializer->startElementNS( XML_w, XML_ins, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + break; + + case RedlineType::Delete: + m_pSerializer->startElementNS( XML_w, XML_del, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + break; + + case RedlineType::Format: + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" ); + break; + default: + break; + } +} + +void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData ) +{ + if ( !pRedlineData || m_bWritingField ) + return; + + switch ( pRedlineData->GetType() ) + { + case RedlineType::Insert: + m_pSerializer->endElementNS( XML_w, XML_ins ); + break; + + case RedlineType::Delete: + m_pSerializer->endElementNS( XML_w, XML_del ); + break; + + case RedlineType::Format: + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" ); + break; + default: + break; + } +} + +void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t ) +{ + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" ); +} + +void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle ) +{ + OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle)); + + m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId); +} + +static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist, + bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr ) +{ + // Compute val attribute value + // Can be one of: + // single, double, + // basicWideOutline, basicWideInline + // OOXml also supports those types of borders, but we'll try to play with the first ones. + // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap + // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap + const char* pVal = "nil"; + if ( pBorderLine && !pBorderLine->isEmpty( ) ) + { + switch (pBorderLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + pVal = "single"; + break; + case SvxBorderLineStyle::DOTTED: + pVal = "dotted"; + break; + case SvxBorderLineStyle::DASHED: + pVal = "dashed"; + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::DOUBLE_THIN: + pVal = "double"; + break; + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + pVal = "thinThickSmallGap"; + break; + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + pVal = "thinThickMediumGap"; + break; + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + pVal = "thinThickLargeGap"; + break; + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + pVal = "thickThinSmallGap"; + break; + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + pVal = "thickThinMediumGap"; + break; + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + pVal = "thickThinLargeGap"; + break; + case SvxBorderLineStyle::EMBOSSED: + pVal = "threeDEmboss"; + break; + case SvxBorderLineStyle::ENGRAVED: + pVal = "threeDEngrave"; + break; + case SvxBorderLineStyle::OUTSET: + pVal = "outset"; + break; + case SvxBorderLineStyle::INSET: + pVal = "inset"; + break; + case SvxBorderLineStyle::FINE_DASHED: + pVal = "dashSmallGap"; + break; + case SvxBorderLineStyle::DASH_DOT: + pVal = "dotDash"; + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + pVal = "dotDotDash"; + break; + case SvxBorderLineStyle::NONE: + default: + break; + } + } + else if ( !rStyleProps || !rStyleProps->LineWidth ) + // no line, and no line set by the style either: + // there is no need to write the property + return; + + // compare the properties with the theme properties before writing them: + // if they are equal, it means that they were style-defined and there is + // no need to write them. + if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() && + pBorderLine->GetBorderLineStyle() == static_cast<SvxBorderLineStyle>(rStyleProps->LineStyle) && + pBorderLine->GetColor() == Color(rStyleProps->Color) && + pBorderLine->GetWidth() == convertMm100ToTwip( rStyleProps->LineWidth ) ) + return; + + FastAttributeList* pAttr = FastSerializerHelper::createAttrList(); + pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) ); + + if ( pBorderLine && !pBorderLine->isEmpty() ) + { + // Compute the sz attribute + + double const fConverted( ::editeng::ConvertBorderWidthToWord( + pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth())); + // The unit is the 8th of point + sal_Int32 nWidth = sal_Int32( fConverted / 2.5 ); + const sal_Int32 nMinWidth = 2; + const sal_Int32 nMaxWidth = 96; + + if ( nWidth > nMaxWidth ) + nWidth = nMaxWidth; + else if ( nWidth < nMinWidth ) + nWidth = nMinWidth; + + pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) ); + + // Get the distance (in pt) + pAttr->add( FSNS( XML_w, XML_space ), OString::number( nDist / 20 ) ); + + // Get the color code as an RRGGBB hex value + OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) ); + pAttr->add( FSNS( XML_w, XML_color ), sColor ); + } + + if (bWriteShadow) + { + // Set the shadow value + pAttr->add( FSNS( XML_w, XML_shadow ), "1" ); + } + + XFastAttributeListRef xAttrs( pAttr ); + pSerializer->singleElementNS( XML_w, elementToken, xAttrs ); +} + +static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma) +{ + OutputBorderOptions rOptions; + + rOptions.tag = XML_tcBorders; + rOptions.bUseStartEnd = !bEcma; + rOptions.bWriteTag = true; + rOptions.bWriteDistance = false; + + return rOptions; +} + +static OutputBorderOptions lcl_getBoxBorderOptions() +{ + OutputBorderOptions rOptions; + + rOptions.tag = XML_pBdr; + rOptions.bUseStartEnd = false; + rOptions.bWriteTag = false; + rOptions.bWriteDistance = true; + + return rOptions; +} + +static void impl_borders( FSHelperPtr const & pSerializer, + const SvxBoxItem& rBox, + const OutputBorderOptions& rOptions, + std::map<SvxBoxItemLine, + css::table::BorderLine2> &rTableStyleConf ) +{ + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + + const sal_Int32 aXmlElements[] = + { + XML_top, + rOptions.bUseStartEnd ? XML_start : XML_left, + XML_bottom, + rOptions.bUseStartEnd ? XML_end : XML_right + }; + bool tagWritten = false; + const SvxBoxItemLine* pBrd = aBorders; + + for( int i = 0; i < 4; ++i, ++pBrd ) + { + const SvxBorderLine* pLn = rBox.GetLine( *pBrd ); + const table::BorderLine2 *aStyleProps = nullptr; + if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() ) + aStyleProps = &rTableStyleConf[ *pBrd ]; + + if (!tagWritten && rOptions.bWriteTag) + { + pSerializer->startElementNS(XML_w, rOptions.tag); + tagWritten = true; + } + + bool bWriteShadow = false; + if (rOptions.aShadowLocation == SvxShadowLocation::NONE) + { + // The border has no shadow + } + else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight) + { + // Special case of 'Bottom-Right' shadow: + // If the shadow location is 'Bottom-Right' - then turn on the shadow + // for ALL the sides. This is because in Word - if you select a shadow + // for a border - it turn on the shadow for ALL the sides (but shows only + // the bottom-right one). + // This is so that no information will be lost if passed through LibreOffice + bWriteShadow = true; + } + else + { + // If there is a shadow, and it's not the regular 'Bottom-Right', + // then write only the 'shadowed' sides of the border + if ( + ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::TOP ) || + ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT ) || + ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft ) && *pBrd == SvxBoxItemLine::BOTTOM) || + ((rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::RIGHT ) + ) + { + bWriteShadow = true; + } + } + + sal_uInt16 nDist = 0; + if (rOptions.bWriteDistance) + { + if (rOptions.pDistances) + { + if ( *pBrd == SvxBoxItemLine::TOP) + nDist = rOptions.pDistances->nTop; + else if ( *pBrd == SvxBoxItemLine::LEFT) + nDist = rOptions.pDistances->nLeft; + else if ( *pBrd == SvxBoxItemLine::BOTTOM) + nDist = rOptions.pDistances->nBottom; + else if ( *pBrd == SvxBoxItemLine::RIGHT) + nDist = rOptions.pDistances->nRight; + } + else + { + nDist = rBox.GetDistance(*pBrd); + } + } + + impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps ); + } + if (tagWritten && rOptions.bWriteTag) { + pSerializer->endElementNS( XML_w, rOptions.tag ); + } +} + +static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr) +{ + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + + const sal_Int32 aXmlElements[] = + { + XML_top, + bUseStartEnd ? XML_start : XML_left, + XML_bottom, + bUseStartEnd ? XML_end : XML_right + }; + bool tagWritten = false; + const SvxBoxItemLine* pBrd = aBorders; + for( int i = 0; i < 4; ++i, ++pBrd ) + { + sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) ); + + if (pDefaultMargins) + { + // Skip output if cell margin == table default margin + if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist) + continue; + } + + if (!tagWritten) { + pSerializer->startElementNS(XML_w, tag); + tagWritten = true; + } + pSerializer->singleElementNS( XML_w, aXmlElements[i], + FSNS( XML_w, XML_w ), OString::number(nDist), + FSNS( XML_w, XML_type ), "dxa" ); + } + if (tagWritten) { + pSerializer->endElementNS( XML_w, tag ); + } +} + +void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ) +{ + m_pSerializer->startElementNS(XML_w, XML_tcPr); + + const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( ); + + bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; + + // Output any table cell redlines if there are any attached to this specific cell + TableCellRedline( pTableTextNodeInfoInner ); + + // Cell preferred width + SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell ); + if ( nCell ) + nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 ); + m_pSerializer->singleElementNS( XML_w, XML_tcW, + FSNS( XML_w, XML_w ), OString::number(nWidth), + FSNS( XML_w, XML_type ), "dxa" ); + + // Horizontal spans + const SwWriteTableRows& rRows = m_xTableWrt->GetRows( ); + SwWriteTableRow *pRow = rRows[ nRow ].get(); + const SwWriteTableCells& rTableCells = pRow->GetCells(); + if (nCell < rTableCells.size() ) + { + const SwWriteTableCell& rCell = *rTableCells[nCell]; + const sal_uInt16 nColSpan = rCell.GetColSpan(); + if ( nColSpan > 1 ) + m_pSerializer->singleElementNS( XML_w, XML_gridSpan, + FSNS( XML_w, XML_val ), OString::number(nColSpan) ); + } + + // Vertical merges + ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow(); + sal_Int32 vSpan = (*xRowSpans)[nCell]; + if ( vSpan > 1 ) + { + m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart"); + } + else if ( vSpan < 0 ) + { + m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue"); + } + + if (const SfxGrabBagItem* pItem = pTableBox->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)) + { + const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); + std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle"); + if (it != rGrabBag.end()) + { + uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >(); + m_pTableStyleExport->CnfStyle(aAttributes); + } + } + + + const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( ); + const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( ); + { + // The cell borders + impl_borders(m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), + m_aTableStyleConfs.back()); + } + + TableBackgrounds( pTableTextNodeInfoInner ); + + { + // Cell margins + impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox ); + } + + TableVerticalCell( pTableTextNodeInfoInner ); + + m_pSerializer->endElementNS( XML_w, XML_tcPr ); +} + +void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + if (m_xTableWrt && pTable == m_xTableWrt->GetTable()) + return; + + long nPageSize = 0; + bool bRelBoxSize = false; + + // Create the SwWriteTable instance to use col spans (and maybe other infos) + GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize ); + + const SwFrameFormat *pFormat = pTable->GetFrameFormat( ); + const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( )); + + const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout(); + if( pLayout && pLayout->IsExportable() ) + m_xTableWrt.reset(new SwWriteTable(pTable, pLayout)); + else + m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false)); +} + +void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + m_aTableStyleConfs.push_back({}); + + // In case any paragraph SDT's are open, close them here. + EndParaSdtBlock(); + + m_pSerializer->startElementNS(XML_w, XML_tbl); + + tableFirstCells.push_back(pTableTextNodeInfoInner); + lastOpenCell.push_back(-1); + lastClosedCell.push_back(-1); + + InitTableHelper( pTableTextNodeInfoInner ); + TableDefinition( pTableTextNodeInfoInner ); +} + +void DocxAttributeOutput::EndTable() +{ + m_pSerializer->endElementNS( XML_w, XML_tbl ); + + if ( m_tableReference->m_nTableDepth > 0 ) + --m_tableReference->m_nTableDepth; + + lastClosedCell.pop_back(); + lastOpenCell.pop_back(); + tableFirstCells.pop_back(); + + // We closed the table; if it is a nested table, the cell that contains it + // still continues + // set to true only if we were in a nested table, not otherwise. + if( !tableFirstCells.empty() ) + m_tableReference->m_bTableCellOpen = true; + + // Cleans the table helper + m_xTableWrt.reset(); + + m_aTableStyleConfs.pop_back(); +} + +void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + m_pSerializer->startElementNS(XML_w, XML_tr); + + // Output the row properties + m_pSerializer->startElementNS(XML_w, XML_trPr); + + // Header row: tblHeader + const SwTable *pTable = pTableTextNodeInfoInner->getTable( ); + if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) ) + m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false + + TableRowRedline( pTableTextNodeInfoInner ); + TableHeight( pTableTextNodeInfoInner ); + TableCanSplit( pTableTextNodeInfoInner ); + + const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine* pTableLine = pTableBox->GetUpper(); + if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)) + { + const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag(); + std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle"); + if (it != rGrabBag.end()) + { + uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >(); + m_pTableStyleExport->CnfStyle(aAttributes); + } + } + + + m_pSerializer->endElementNS( XML_w, XML_trPr ); +} + +void DocxAttributeOutput::EndTableRow( ) +{ + m_pSerializer->endElementNS( XML_w, XML_tr ); + lastOpenCell.back() = -1; + lastClosedCell.back() = -1; +} + +void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ) +{ + lastOpenCell.back() = nCell; + + InitTableHelper( pTableTextNodeInfoInner ); + + m_pSerializer->startElementNS(XML_w, XML_tc); + + // Write the cell properties here + TableCellProperties( pTableTextNodeInfoInner, nCell, nRow ); + + m_tableReference->m_bTableCellOpen = true; +} + +void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell) +{ + lastClosedCell.back() = nCell; + lastOpenCell.back() = -1; + + if (m_tableReference->m_bTableCellParaSdtOpen) + EndParaSdtBlock(); + + m_pSerializer->endElementNS( XML_w, XML_tc ); + + m_tableReference->m_bTableCellOpen = false; + m_tableReference->m_bTableCellParaSdtOpen = false; +} + +void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ +} + +void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ ) +{ +} + +namespace +{ + +/// Does the same as comphelper::string::padToLength(), but extends the start, not the end. +OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill) +{ + if (nLen > aString.getLength()) + { + sal_Int32 nDiff = nLen - aString.getLength(); + OStringBuffer aBuffer; + comphelper::string::padToLength(aBuffer, nDiff, cFill); + aBuffer.append(aString); + return aBuffer.makeStringAndClear(); + } + else + return aString; +} + +//Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx +//Since this is not import code, "-1" needs to be handled as the mode that LO will save as. +//To identify how your code should handle a "-1", look in DocxExport::WriteSettings(). +sal_Int32 lcl_getWordCompatibilityMode( const SwDoc& rDoc ) +{ + uno::Reference< beans::XPropertySet > xPropSet( rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + + sal_Int32 nWordCompatibilityMode = -1; + if ( xPropSetInfo->hasPropertyByName( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) ) + { + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList; + + for ( const auto& rProp : std::as_const(propList) ) + { + if ( rProp.Name == "CompatSettings" ) + { + css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings; + rProp.Value >>= aCurrentCompatSettings; + + for ( const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings) ) + { + uno::Sequence< beans::PropertyValue > aCompatSetting; + rCurrentCompatSetting.Value >>= aCompatSetting; + + OUString sName; + OUString sUri; + OUString sVal; + + for ( const auto& rPropVal : std::as_const(aCompatSetting) ) + { + if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName; + if ( rPropVal.Name == "uri" ) rPropVal.Value >>= sUri; + if ( rPropVal.Name == "val" ) rPropVal.Value >>= sVal; + } + + if ( sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word" ) + { + const sal_Int32 nValidMode = sVal.toInt32(); + // if repeated, highest mode wins in MS Word. 11 is the first valid mode. + if ( nValidMode > 10 && nValidMode > nWordCompatibilityMode ) + nWordCompatibilityMode = nValidMode; + + } + } + } + } + } + + // TODO: this is duplicated, better store it in DocxExport member? + if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING)) + { + if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode) + { + nWordCompatibilityMode = 14; + } + } + + return nWordCompatibilityMode; +} + +} + +void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; + + // Write the table properties + m_pSerializer->startElementNS(XML_w, XML_tblPr); + + static const sal_Int32 aOrder[] = + { + FSNS( XML_w, XML_tblStyle ), + FSNS( XML_w, XML_tblpPr ), + FSNS( XML_w, XML_tblOverlap ), + FSNS( XML_w, XML_bidiVisual ), + FSNS( XML_w, XML_tblStyleRowBandSize ), + FSNS( XML_w, XML_tblStyleColBandSize ), + FSNS( XML_w, XML_tblW ), + FSNS( XML_w, XML_jc ), + FSNS( XML_w, XML_tblCellSpacing ), + FSNS( XML_w, XML_tblInd ), + FSNS( XML_w, XML_tblBorders ), + FSNS( XML_w, XML_shd ), + FSNS( XML_w, XML_tblLayout ), + FSNS( XML_w, XML_tblCellMar ), + FSNS( XML_w, XML_tblLook ), + FSNS( XML_w, XML_tblPrChange ) + }; + + // postpone the output so that we can later [] + // prepend the properties before the run + m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder)); + + long nPageSize = 0; + const char* widthType = "dxa"; + + // If actual width of table is relative it should export is as "pct".` + const SwTable *pTable = pTableTextNodeInfoInner->getTable(); + SwFrameFormat *pTableFormat = pTable->GetFrameFormat( ); + const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize(); + int nWidthPercent = rSize.GetWidthPercent(); + // If we export a floating table: we use the widthPercent of the surrounding frame + const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame(); + if (pFloatingTableFrame) + { + const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize(); + nWidthPercent = rFrameSize.GetWidthPercent(); + } + uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY); + bool isWidthRelative = false; + xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative; + + if(isWidthRelative) + { + /** + * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)] + * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html + * + * Fiftieths of a Percent : + * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/ + * pct Width is in Fiftieths of a Percent + * + * ex. If the Table width is 50% then + * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct + **/ + nPageSize = nWidthPercent * 50 ; + widthType = "pct" ; + } + else + { + bool bRelBoxSize = false; + // Create the SwWriteTable instance to use col spans (and maybe other infos) + GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize ); + if(nPageSize == 0) + widthType = "auto"; + } + + // Output the table preferred width + m_pSerializer->singleElementNS( XML_w, XML_tblW, + FSNS( XML_w, XML_w ), OString::number(nPageSize), + FSNS( XML_w, XML_type ), widthType ); + + // Disable layout autofit, as it does not exist in LibreOffice yet + m_pSerializer->singleElementNS( XML_w, XML_tblLayout, + FSNS( XML_w, XML_type ), "fixed" ); + + // Look for the table style property in the table grab bag + std::map<OUString, css::uno::Any> aGrabBag = + pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag(); + + // We should clear the TableStyle map. In case of Table inside multiple tables it contains the + // table border style of the previous table. + std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back(); + rTableStyleConf.clear(); + + // Extract properties from grab bag + for( const auto & rGrabBagElement : aGrabBag ) + { + if( rGrabBagElement.first == "TableStyleName") + { + OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); + m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName); + } + else if( rGrabBagElement.first == "TableStyleTopBorder" ) + rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>(); + else if( rGrabBagElement.first == "TableStyleBottomBorder" ) + rTableStyleConf[SvxBoxItemLine::BOTTOM] + = rGrabBagElement.second.get<table::BorderLine2>(); + else if( rGrabBagElement.first == "TableStyleLeftBorder" ) + rTableStyleConf[SvxBoxItemLine::LEFT] + = rGrabBagElement.second.get<table::BorderLine2>(); + else if( rGrabBagElement.first == "TableStyleRightBorder" ) + rTableStyleConf[SvxBoxItemLine::RIGHT] + = rGrabBagElement.second.get<table::BorderLine2>(); + else if (rGrabBagElement.first == "TableStyleLook") + { + FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList(); + const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); + + for (const auto& rAttribute : aAttributeList) + { + if (rAttribute.Name == "val") + pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0')); + else + { + static DocxStringTokenMap const aTokens[] = + { + {"firstRow", XML_firstRow}, + {"lastRow", XML_lastRow}, + {"firstColumn", XML_firstColumn}, + {"lastColumn", XML_lastColumn}, + {"noHBand", XML_noHBand}, + {"noVBand", XML_noVBand}, + {nullptr, 0} + }; + + if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name)) + pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0")); + } + } + + XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_tblLook, xAttributeList); + } + else if (rGrabBagElement.first == "TablePosition" ) + { + FastAttributeList *attrListTablePos = FastSerializerHelper::createAttrList( ); + const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >(); + // look for a surrounding frame and take it's position values + const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame(); + if( pFrame ) + { + // we export the values of the surrounding Frame + OString sOrientation; + sal_Int32 nValue; + + // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY! + OString sTblpXSpec = convertToOOXMLHoriOrient( pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() ); + OString sTblpYSpec = convertToOOXMLVertOrient( pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() ); + + sOrientation = convertToOOXMLVertOrientRel( pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() ); + attrListTablePos->add( FSNS( XML_w, XML_vertAnchor ), sOrientation.getStr() ); + + if( !sTblpYSpec.isEmpty() ) + attrListTablePos->add( FSNS( XML_w, XML_tblpYSpec ), sTblpYSpec.getStr() ); + + sOrientation = convertToOOXMLHoriOrientRel( pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() ); + attrListTablePos->add( FSNS( XML_w, XML_horzAnchor ), sOrientation.getStr() ); + + if( !sTblpXSpec.isEmpty() ) + attrListTablePos->add( FSNS( XML_w, XML_tblpXSpec ), sTblpXSpec.getStr() ); + + nValue = pFrame->GetFrameFormat().GetULSpace().GetLower(); + if( nValue != 0 ) + attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) ); + + nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft(); + if( nValue != 0 ) + attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) ); + + nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight(); + if( nValue != 0 ) + attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) ); + + nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper(); + if( nValue != 0 ) + attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) ); + + if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec is present + { + nValue = pFrame->GetFrameFormat().GetHoriOrient().GetPos(); + // we need to revert the additional shift introduced by + // lcl_DecrementHoriOrientPosition() in writerfilter + // 1st: left distance of the table + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat(); + const SvxBoxItem& rBox = pFrameFormat->GetBox( ); + sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT); + nValue += nLeftDistance; + + // 2nd: if a left border is given, revert the shift by half the width + // from lcl_DecrementHoriOrientPosition() in writerfilter + if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft()) + { + long nWidth = pLeftBorder->GetWidth(); + nValue += (nWidth / 2); + } + + attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) ); + } + + if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec is present + { + nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos(); + attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) ); + } + } + else // ( pFrame = 0 ) + { + // we export the values from the grabBag + for (const auto& rProp : aTablePosition) + { + if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty()) + { + OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); + attrListTablePos->add( FSNS( XML_w, XML_vertAnchor ), sOrientation.getStr() ); + } + else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty()) + { + OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); + attrListTablePos->add( FSNS( XML_w, XML_tblpYSpec ), sOrientation.getStr() ); + } + else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty()) + { + OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); + attrListTablePos->add( FSNS( XML_w, XML_horzAnchor ), sOrientation.getStr() ); + } + else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty()) + { + OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); + attrListTablePos->add( FSNS( XML_w, XML_tblpXSpec ), sOrientation.getStr() ); + } + else if (rProp.Name == "bottomFromText") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) ); + } + else if (rProp.Name == "leftFromText") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) ); + } + else if (rProp.Name == "rightFromText") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) ); + } + else if (rProp.Name == "topFromText") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) ); + } + else if (rProp.Name == "tblpX") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) ); + } + else if (rProp.Name == "tblpY") + { + sal_Int32 nValue = rProp.Value.get<sal_Int32>(); + attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) ); + } + } + } + + XFastAttributeListRef xAttrListTablePosRef( attrListTablePos ); + + m_pSerializer->singleElementNS( XML_w, XML_tblpPr, xAttrListTablePosRef); + attrListTablePos = nullptr; + } + else + SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first); + } + + // Output the table alignment + const char* pJcVal; + sal_Int32 nIndent = 0; + switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) ) + { + case text::HoriOrientation::CENTER: + pJcVal = "center"; + break; + case text::HoriOrientation::RIGHT: + if ( bEcma ) + pJcVal = "right"; + else + pJcVal = "end"; + break; + default: + case text::HoriOrientation::NONE: + case text::HoriOrientation::LEFT_AND_WIDTH: + { + if ( bEcma ) + pJcVal = "left"; + else + pJcVal = "start"; + nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() ); + + // Table indentation has different meaning in Word, depending if the table is nested or not. + // If nested, tblInd is added to parent table's left spacing and defines left edge position + // If not nested, text position of left-most cell must be at absolute X = tblInd + // so, table_spacing + table_spacing_to_content = tblInd + + // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables; + // if no compatibilityMode is defined (which now should only happen on a new export to .docx), + // LO uses a higher compatibility than 2010's 14. + sal_Int32 nMode = lcl_getWordCompatibilityMode( *m_rExport.m_pDoc ); + + const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat(); + if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0) + nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT ); + else + { + // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border. + nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2; + } + + break; + } + } + m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal); + + // Output the table background color (although cell value still needs to be specified) + const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO; + if ( aColor != COL_AUTO ) + { + OString sColor = msfilter::util::ConvertColor( aColor ); + m_pSerializer->singleElementNS( XML_w, XML_shd, + FSNS( XML_w, XML_fill ), sColor, + FSNS( XML_w, XML_val ), "clear" ); + } + + // Output the table borders + TableDefaultBorders( pTableTextNodeInfoInner ); + + // Output the default cell margins + TableDefaultCellMargins( pTableTextNodeInfoInner ); + + TableBidi( pTableTextNodeInfoInner ); + + // Table indent (need to get written even if == 0) + m_pSerializer->singleElementNS( XML_w, XML_tblInd, + FSNS( XML_w, XML_w ), OString::number(nIndent), + FSNS( XML_w, XML_type ), "dxa" ); + + // Merge the marks for the ordered elements + m_pSerializer->mergeTopMarks(Tag_TableDefinition); + + m_pSerializer->endElementNS( XML_w, XML_tblPr ); + + // Write the table grid infos + m_pSerializer->startElementNS(XML_w, XML_tblGrid); + sal_Int32 nPrv = 0; + ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner ); + for ( auto aColumnWidth : *pColumnWidths ) + { + sal_Int32 nWidth = sal_Int32( aColumnWidth ) - nPrv; + m_pSerializer->singleElementNS( XML_w, XML_gridCol, + FSNS( XML_w, XML_w ), OString::number(nWidth) ); + nPrv = sal_Int32( aColumnWidth ); + } + + m_pSerializer->endElementNS( XML_w, XML_tblGrid ); +} + +void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ + // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style. + // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit. +} + +void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat(); + const SvxBoxItem& rBox = pFrameFormat->GetBox( ); + const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; + + impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma); +} + +void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable *pTable = pTableTextNodeInfoInner->getTable(); + const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( ); + const SwTableLine *pTableRow = pTableBox->GetUpper(); + const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( ); + + const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO; + + const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( ); + const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pRowColorProp && aColor == COL_AUTO) + aColor = pRowColorProp->GetColor(); + + const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( ); + const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pTableColorProp && aColor == COL_AUTO ) + aColor = pTableColorProp->GetColor(); + + const OString sColor = msfilter::util::ConvertColor( aColor ); + + std::map<OUString, css::uno::Any> aGrabBag = + pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag(); + + OString sOriginalColor; + std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor"); + if( aGrabBagElement != aGrabBag.end() ) + sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); + + if ( sOriginalColor != sColor ) + { + // color changed by the user, or no grab bag: write sColor + if ( sColor != "auto" ) + { + m_pSerializer->singleElementNS( XML_w, XML_shd, + FSNS( XML_w, XML_fill ), sColor, + FSNS( XML_w, XML_val ), "clear" ); + } + } + else + { + rtl::Reference<sax_fastparser::FastAttributeList> pAttrList; + + for( const auto & rGrabBagElement : aGrabBag ) + { + if (!rGrabBagElement.second.has<OUString>()) + continue; + + OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 ); + if( rGrabBagElement.first == "themeFill") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() ); + else if( rGrabBagElement.first == "themeFillTint") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() ); + else if( rGrabBagElement.first == "themeFillShade") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() ); + else if( rGrabBagElement.first == "fill" ) + AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() ); + else if( rGrabBagElement.first == "themeColor") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() ); + else if( rGrabBagElement.first == "themeTint") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() ); + else if( rGrabBagElement.first == "themeShade") + AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() ); + else if( rGrabBagElement.first == "color") + AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() ); + else if( rGrabBagElement.first == "val") + AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() ); + } + m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() ); + } +} + +void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + + // search next Redline + const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable(); + for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos ) + { + SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos); + const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline); + if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine) + { + // Redline for this table row + const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData(); + RedlineType nRedlineType = aRedlineData.GetType(); + switch (nRedlineType) + { + case RedlineType::TableRowInsert: + case RedlineType::TableRowDelete: + { + OString aId( OString::number( m_nRedlineId++ ) ); + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) ); + OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) ); + + OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) ); + + if (nRedlineType == RedlineType::TableRowInsert) + m_pSerializer->singleElementNS( XML_w, XML_ins, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + else if (nRedlineType == RedlineType::TableRowDelete) + m_pSerializer->singleElementNS( XML_w, XML_del, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + } + break; + default: break; + } + } + } +} + +void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + + // search next Redline + const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable(); + for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos ) + { + SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos); + const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline); + if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox) + { + // Redline for this table cell + const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData(); + RedlineType nRedlineType = aRedlineData.GetType(); + switch (nRedlineType) + { + case RedlineType::TableCellInsert: + case RedlineType::TableCellDelete: + { + OString aId( OString::number( m_nRedlineId++ ) ); + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) ); + OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) ); + + OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) ); + + if (nRedlineType == RedlineType::TableCellInsert) + m_pSerializer->singleElementNS( XML_w, XML_cellIns, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + else if (nRedlineType == RedlineType::TableCellDelete) + m_pSerializer->singleElementNS( XML_w, XML_cellDel, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor, + FSNS( XML_w, XML_date ), aDate ); + } + break; + default: break; + } + } + } +} + +void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); + + const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize(); + if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() ) + { + sal_Int32 nHeight = rLSz.GetHeight(); + const char *pRule = nullptr; + + switch ( rLSz.GetHeightSizeType() ) + { + case SwFrameSize::Fixed: pRule = "exact"; break; + case SwFrameSize::Minimum: pRule = "atLeast"; break; + default: break; + } + + if ( pRule ) + m_pSerializer->singleElementNS( XML_w, XML_trHeight, + FSNS( XML_w, XML_val ), OString::number(nHeight), + FSNS( XML_w, XML_hRule ), pRule ); + } +} + +void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); + + const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( ); + // if rSplittable is true then no need to write <w:cantSplit w:val="false"/> + // as default row prop is allow row to break across page. + if( !rSplittable.GetValue( ) ) + m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true"); +} + +void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable * pTable = pTableTextNodeInfoInner->getTable(); + const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat(); + + if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB ) + { + m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true"); + } +} + +void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( ); + + if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) ) + m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl"); + else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) ) + { + m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr"); + } + + const SwWriteTableRows& rRows = m_xTableWrt->GetRows( ); + SwWriteTableRow *pRow = rRows[ pTableTextNodeInfoInner->getRow( ) ].get(); + sal_uInt32 nCell = pTableTextNodeInfoInner->getCell(); + const SwWriteTableCells& rTableCells = pRow->GetCells(); + if (nCell < rTableCells.size() ) + { + const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get(); + switch( pCell->GetVertOri()) + { + case text::VertOrientation::TOP: + break; + case text::VertOrientation::CENTER: + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center"); + break; + case text::VertOrientation::BOTTOM: + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom"); + break; + } + } +} + +void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) +{ + // This is called when the nested table ends in a cell, and there's no + // paragraph behind that; so we must check for the ends of cell, rows, + // tables + // ['true' to write an empty paragraph, MS Word insists on that] + FinishTableRowCell( pNodeInfoInner, true ); +} + +void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ + SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" ); +} + +void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ + SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" ); +} + +void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ ) +{ + SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" ); +} + +void DocxAttributeOutput::StartStyles() +{ + m_pSerializer->startElementNS( XML_w, XML_styles, + FSNS( XML_xmlns, XML_w ), GetExport().GetFilter().getNamespaceURL(OOX_NS(doc)).toUtf8(), + FSNS( XML_xmlns, XML_w14 ), GetExport().GetFilter().getNamespaceURL(OOX_NS(w14)).toUtf8(), + FSNS( XML_xmlns, XML_mc ), GetExport().GetFilter().getNamespaceURL(OOX_NS(mce)).toUtf8(), + FSNS( XML_mc, XML_Ignorable ), "w14" ); + + DocDefaults(); + LatentStyles(); +} + +sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, const OUString& rName) +{ + OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8); + while (pMap->pToken) + { + if (sName == pMap->pToken) + return pMap->nToken; + ++pMap; + } + return 0; +} + +namespace +{ + +DocxStringTokenMap const aDefaultTokens[] = { + {"defQFormat", XML_defQFormat}, + {"defUnhideWhenUsed", XML_defUnhideWhenUsed}, + {"defSemiHidden", XML_defSemiHidden}, + {"count", XML_count}, + {"defUIPriority", XML_defUIPriority}, + {"defLockedState", XML_defLockedState}, + {nullptr, 0} +}; + +DocxStringTokenMap const aExceptionTokens[] = { + {"name", XML_name}, + {"locked", XML_locked}, + {"uiPriority", XML_uiPriority}, + {"semiHidden", XML_semiHidden}, + {"unhideWhenUsed", XML_unhideWhenUsed}, + {"qFormat", XML_qFormat}, + {nullptr, 0} +}; + +} + +void DocxAttributeOutput::LatentStyles() +{ + // Do we have latent styles available? + uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aInteropGrabBag; + xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag; + uno::Sequence<beans::PropertyValue> aLatentStyles; + auto pProp = std::find_if(aInteropGrabBag.begin(), aInteropGrabBag.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "latentStyles"; }); + if (pProp != aInteropGrabBag.end()) + pProp->Value >>= aLatentStyles; + if (!aLatentStyles.hasElements()) + return; + + // Extract default attributes first. + sax_fastparser::FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList(); + uno::Sequence<beans::PropertyValue> aLsdExceptions; + for (const auto& rLatentStyle : std::as_const(aLatentStyles)) + { + if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, rLatentStyle.Name)) + pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rLatentStyle.Value.get<OUString>(), RTL_TEXTENCODING_UTF8)); + else if (rLatentStyle.Name == "lsdExceptions") + rLatentStyle.Value >>= aLsdExceptions; + } + + XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->startElementNS(XML_w, XML_latentStyles, xAttributeList); + pAttributeList = nullptr; + + // Then handle the exceptions. + for (const auto& rLsdException : std::as_const(aLsdExceptions)) + { + pAttributeList = FastSerializerHelper::createAttrList(); + + uno::Sequence<beans::PropertyValue> aAttributes; + rLsdException.Value >>= aAttributes; + for (const auto& rAttribute : std::as_const(aAttributes)) + if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, rAttribute.Name)) + pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rAttribute.Value.get<OUString>(), RTL_TEXTENCODING_UTF8)); + + xAttributeList = pAttributeList; + m_pSerializer->singleElementNS(XML_w, XML_lsdException, xAttributeList); + pAttributeList = nullptr; + } + + m_pSerializer->endElementNS(XML_w, XML_latentStyles); +} + +void DocxAttributeOutput::OutputDefaultItem(const SfxPoolItem& rHt) +{ + bool bMustWrite = true; + switch (rHt.Which()) + { + case RES_CHRATR_CASEMAP: + bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SvxCaseMap::NotMapped; + break; + case RES_CHRATR_COLOR: + bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue() != COL_AUTO; + break; + case RES_CHRATR_CONTOUR: + bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue(); + break; + case RES_CHRATR_CROSSEDOUT: + bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE; + break; + case RES_CHRATR_ESCAPEMENT: + bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off; + break; + case RES_CHRATR_FONT: + bMustWrite = true; + break; + case RES_CHRATR_FONTSIZE: + bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default + break; + case RES_CHRATR_KERNING: + bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0; + break; + case RES_CHRATR_LANGUAGE: + bMustWrite = true; + break; + case RES_CHRATR_POSTURE: + bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE; + break; + case RES_CHRATR_SHADOWED: + bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue(); + break; + case RES_CHRATR_UNDERLINE: + bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE; + break; + case RES_CHRATR_WEIGHT: + bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL; + break; + case RES_CHRATR_AUTOKERN: + bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue(); + break; + case RES_CHRATR_BLINK: + bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue(); + break; + case RES_CHRATR_BACKGROUND: + { + const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt); + bMustWrite = (rBrushItem.GetColor() != COL_AUTO || + rBrushItem.GetShadingValue() != ShadingPattern::CLEAR || + rBrushItem.GetGraphic() != nullptr || + rBrushItem.GetGraphicObject() != nullptr); + } + break; + + case RES_CHRATR_CJK_FONT: + bMustWrite = true; + break; + case RES_CHRATR_CJK_FONTSIZE: + bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE + break; + case RES_CHRATR_CJK_LANGUAGE: + bMustWrite = true; + break; + case RES_CHRATR_CJK_POSTURE: + bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE + break; + case RES_CHRATR_CJK_WEIGHT: + bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT + break; + + case RES_CHRATR_CTL_FONT: + bMustWrite = true; + break; + case RES_CHRATR_CTL_FONTSIZE: + bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default + break; + case RES_CHRATR_CTL_LANGUAGE: + bMustWrite = true; + break; + case RES_CHRATR_CTL_POSTURE: + bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE; + break; + case RES_CHRATR_CTL_WEIGHT: + bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL; + break; + + case RES_CHRATR_ROTATE: + bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0; + break; + case RES_CHRATR_EMPHASIS_MARK: + bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE; + break; + case RES_CHRATR_TWO_LINES: + bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue(); + break; + case RES_CHRATR_SCALEW: + bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100; + break; + case RES_CHRATR_RELIEF: + bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE; + break; + case RES_CHRATR_HIDDEN: + bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue(); + break; + case RES_CHRATR_BOX: + { + const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt); + bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() || + rBoxItem.GetBottom() || rBoxItem.GetRight() || + rBoxItem.GetSmallestDistance(); + } + break; + case RES_CHRATR_HIGHLIGHT: + { + const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt); + bMustWrite = (rBrushItem.GetColor() != COL_AUTO || + rBrushItem.GetShadingValue() != ShadingPattern::CLEAR || + rBrushItem.GetGraphic() != nullptr || + rBrushItem.GetGraphicObject() != nullptr); + } + break; + + case RES_PARATR_LINESPACING: + bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off; + break; + case RES_PARATR_ADJUST: + bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SvxAdjust::Left; + break; + case RES_PARATR_SPLIT: + bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue(); + break; + case RES_PARATR_WIDOWS: + bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue(); + break; + case RES_PARATR_TABSTOP: + bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0; + break; + case RES_PARATR_HYPHENZONE: + bMustWrite = true; + break; + case RES_PARATR_NUMRULE: + bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty(); + break; + case RES_PARATR_SCRIPTSPACE: + bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); + break; + case RES_PARATR_HANGINGPUNCTUATION: + bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); + break; + case RES_PARATR_FORBIDDEN_RULES: + bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue(); + break; + case RES_PARATR_VERTALIGN: + bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic; + break; + case RES_PARATR_SNAPTOGRID: + bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue(); + break; + case RES_CHRATR_GRABBAG: + bMustWrite = true; + break; + + default: + SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() ); + break; + } + + if (bMustWrite) + OutputItem(rHt); +} + +void DocxAttributeOutput::DocDefaults( ) +{ + // Write the '<w:docDefaults>' section here + m_pSerializer->startElementNS(XML_w, XML_docDefaults); + + // Output the default run properties + m_pSerializer->startElementNS(XML_w, XML_rPrDefault); + + StartStyleProperties(false, 0); + + for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i) + OutputDefaultItem(m_rExport.m_pDoc->GetDefault(i)); + + EndStyleProperties(false); + + m_pSerializer->endElementNS(XML_w, XML_rPrDefault); + + // Output the default paragraph properties + m_pSerializer->startElementNS(XML_w, XML_pPrDefault); + + StartStyleProperties(true, 0); + + for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i) + OutputDefaultItem(m_rExport.m_pDoc->GetDefault(i)); + + EndStyleProperties(true); + + m_pSerializer->endElementNS(XML_w, XML_pPrDefault); + + m_pSerializer->endElementNS(XML_w, XML_docDefaults); +} + +void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles ) +{ + // HACK + // Ms Office seems to have an internal limitation of 4091 styles + // and refuses to load .docx with more, even though the spec seems to allow that; + // so simply if there are more styles, don't export those + const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles; + m_pTableStyleExport->TableStyles(nCountStylesToWrite); + m_pSerializer->endElementNS( XML_w, XML_styles ); +} + +void DocxAttributeOutput::DefaultStyle() +{ + // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)? + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::DefaultStyle()"); +} + +/* Writes <a:srcRect> tag back to document.xml if a file contains a cropped image. +* NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF. +*/ +void DocxAttributeOutput::WriteSrcRect(const SdrObject* pSdrObj, const SwFrameFormat* pFrameFormat ) +{ + uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + + uno::Reference<graphic::XGraphic> xGraphic; + xPropSet->getPropertyValue("Graphic") >>= xGraphic; + const Graphic aGraphic(xGraphic); + + Size aOriginalSize(aGraphic.GetPrefSize()); + + const MapMode aMap100mm( MapUnit::Map100thMM ); + const MapMode& rMapMode = aGraphic.GetPrefMapMode(); + if (rMapMode.GetMapUnit() == MapUnit::MapPixel) + { + aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm); + } + + css::text::GraphicCrop aGraphicCropStruct; + xPropSet->getPropertyValue( "GraphicCrop" ) >>= aGraphicCropStruct; + sal_Int32 nCropL = aGraphicCropStruct.Left; + sal_Int32 nCropR = aGraphicCropStruct.Right; + sal_Int32 nCropT = aGraphicCropStruct.Top; + sal_Int32 nCropB = aGraphicCropStruct.Bottom; + + // simulate border padding as a negative crop. + const SfxPoolItem* pItem; + if (pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState(RES_BOX, false, &pItem)) + { + const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem); + nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT ); + nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT ); + nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP ); + nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM ); + } + + if ( (0 != nCropL) || (0 != nCropT) || (0 != nCropR) || (0 != nCropB) ) + { + double widthMultiplier = 100000.0/aOriginalSize.Width(); + double heightMultiplier = 100000.0/aOriginalSize.Height(); + + sal_Int32 left = static_cast<sal_Int32>(rtl::math::round(nCropL * widthMultiplier)); + sal_Int32 right = static_cast<sal_Int32>(rtl::math::round(nCropR * widthMultiplier)); + sal_Int32 top = static_cast<sal_Int32>(rtl::math::round(nCropT * heightMultiplier)); + sal_Int32 bottom = static_cast<sal_Int32>(rtl::math::round(nCropB * heightMultiplier)); + + m_pSerializer->singleElementNS( XML_a, XML_srcRect, + XML_l, OString::number(left), + XML_t, OString::number(top), + XML_r, OString::number(right), + XML_b, OString::number(bottom) ); + } +} + +void DocxAttributeOutput::PopRelIdCache() +{ + if (!m_aRelIdCache.empty()) + m_aRelIdCache.pop(); + if (!m_aSdrRelIdCache.empty()) + m_aSdrRelIdCache.pop(); +} + +void DocxAttributeOutput::PushRelIdCache() +{ + m_aRelIdCache.push(std::map<const Graphic*, OString>()); + m_aSdrRelIdCache.push(std::map<BitmapChecksum, OUString>()); +} + +OUString DocxAttributeOutput::FindRelId(BitmapChecksum nChecksum) +{ + OUString aRet; + + if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end()) + aRet = m_aSdrRelIdCache.top()[nChecksum]; + + return aRet; +} + +void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId) +{ + if (!m_aSdrRelIdCache.empty()) + m_aSdrRelIdCache.top()[nChecksum] = rRelId; +} + +void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) +{ + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) - some stuff still missing" ); + + GetSdtEndBefore(pSdrObj); + + // detect mis-use of the API + assert(pGrfNode || (pOLEFrameFormat && pOLENode)); + const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat; + // create the relation ID + OString aRelId; + sal_Int32 nImageType; + if ( pGrfNode && pGrfNode->IsLinkedFile() ) + { + // linked image, just create the relation + OUString aFileName; + pGrfNode->GetFileFilterNms( &aFileName, nullptr ); + + sal_Int32 const nFragment(aFileName.indexOf('#')); + sal_Int32 const nForbiddenU(aFileName.indexOf("%5C")); + sal_Int32 const nForbiddenL(aFileName.indexOf("%5c")); + if ( (nForbiddenU != -1 && (nFragment == -1 || nForbiddenU < nFragment)) + || (nForbiddenL != -1 && (nFragment == -1 || nForbiddenL < nFragment))) + { + SAL_WARN("sw.ww8", "DocxAttributeOutput::FlyFrameGraphic: ignoring image with invalid link URL"); + return; + } + + // TODO Convert the file name to relative for better interoperability + + aRelId = m_rExport.AddRelation( + oox::getRelationship(Relationship::IMAGE), + aFileName ); + + nImageType = XML_link; + } + else + { + // inline, we also have to write the image itself + const Graphic* pGraphic = nullptr; + if (pGrfNode) + pGraphic = &pGrfNode->GetGrf(); + else + pGraphic = pOLENode->GetGraphic(); + + if (!m_aRelIdCache.empty() && m_aRelIdCache.top().find(pGraphic) != m_aRelIdCache.top().end()) + // We already have a RelId for this Graphic. + aRelId = m_aRelIdCache.top()[pGraphic]; + else + { + // Not in cache, then need to write it. + m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream + + OUString aImageId = m_rDrawingML.WriteImage( *pGraphic ); + + aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 ); + if (!m_aRelIdCache.empty()) + m_aRelIdCache.top()[pGraphic] = aRelId; + } + + nImageType = XML_embed; + } + + // In case there are any grab-bag items on the graphic frame, emit them now. + // These are always character grab-bags, as graphics are at-char or as-char in Word. + const SfxPoolItem* pItem = nullptr; + if (pFrameFormat->GetAttrSet().HasItem(RES_FRMATR_GRABBAG, &pItem)) + { + const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem); + CharGrabBag(*pGrabBag); + } + + rtl::Reference<sax_fastparser::FastAttributeList> xFrameAttributes( + FastSerializerHelper::createAttrList()); + Size aSize = rSize; + if (pGrfNode) + { + const SwAttrSet& rSet = pGrfNode->GetSwAttrSet(); + MirrorGraph eMirror = rSet.Get(RES_GRFATR_MIRRORGRF).GetValue(); + if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both) + // Mirror on the vertical axis is a horizontal flip. + xFrameAttributes->add(XML_flipH, "1"); + // RES_GRFATR_ROTATION is sal_uInt16; use sal_uInt32 for multiplication later + if (sal_uInt32 nRot = rSet.Get(RES_GRFATR_ROTATION).GetValue()) + { + // RES_GRFATR_ROTATION is in 10ths of degree; convert to 100ths for macro + sal_uInt32 mOOXMLRot = oox::drawingml::ExportRotateClockwisify(nRot*10); + xFrameAttributes->add(XML_rot, OString::number(mOOXMLRot)); + aSize = pGrfNode->GetTwipSize(); + } + } + + m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, aSize); + + // picture description (used for pic:cNvPr later too) + ::sax_fastparser::FastAttributeList* docPrattrList = FastSerializerHelper::createAttrList(); + docPrattrList->add( XML_id, OString::number( m_anchorId++).getStr()); + docPrattrList->add( XML_name, OUStringToOString( pFrameFormat->GetName(), RTL_TEXTENCODING_UTF8 ) ); + docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription(), RTL_TEXTENCODING_UTF8 ).getStr()); + if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT ) + docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle(), RTL_TEXTENCODING_UTF8 ).getStr()); + XFastAttributeListRef docPrAttrListRef( docPrattrList ); + m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrAttrListRef ); + + OUString sURL, sRelId; + if(pSdrObj) + { + uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + xPropSet->getPropertyValue("HyperLinkURL") >>= sURL; + if(!sURL.isEmpty()) + { + if (sURL.startsWith("#") && sURL.indexOf(' ') != -1 && !sURL.endsWith("|outline") && !sURL.endsWith("|table") && + !sURL.endsWith("|frame") && !sURL.endsWith("|graphic") && !sURL.endsWith("|ole") && !sURL.endsWith("|region")) + { + // Spaces are prohibited in bookmark name. + sURL = sURL.replace(' ', '_'); + } + sRelId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), + oox::getRelationship(Relationship::HYPERLINK), + sURL, !sURL.startsWith("#") ); + m_pSerializer->singleElementNS( XML_a, XML_hlinkClick, + FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main", + FSNS( XML_r, XML_id ), sRelId.toUtf8()); + } + } + + m_pSerializer->endElementNS( XML_wp, XML_docPr ); + + m_pSerializer->startElementNS(XML_wp, XML_cNvGraphicFramePr); + // TODO change aspect? + m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks, + FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8(), + XML_noChangeAspect, "1" ); + m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr ); + + m_pSerializer->startElementNS( XML_a, XML_graphic, + FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8() ); + m_pSerializer->startElementNS( XML_a, XML_graphicData, + XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture" ); + + m_pSerializer->startElementNS( XML_pic, XML_pic, + FSNS( XML_xmlns, XML_pic ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlPicture)).toUtf8() ); + + m_pSerializer->startElementNS(XML_pic, XML_nvPicPr); + // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes + m_pSerializer->startElementNS(XML_pic, XML_cNvPr, docPrAttrListRef); + + if(!sURL.isEmpty()) + m_pSerializer->singleElementNS( XML_a, XML_hlinkClick, + FSNS( XML_r, XML_id ), sRelId.toUtf8()); + + m_pSerializer->endElementNS( XML_pic, XML_cNvPr ); + + m_pSerializer->startElementNS(XML_pic, XML_cNvPicPr); + // TODO change aspect? + m_pSerializer->singleElementNS( XML_a, XML_picLocks, + XML_noChangeAspect, "1", XML_noChangeArrowheads, "1" ); + m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr ); + m_pSerializer->endElementNS( XML_pic, XML_nvPicPr ); + + // the actual picture + m_pSerializer->startElementNS(XML_pic, XML_blipFill); + +/* At this point we are certain that, WriteImage returns empty RelId + for unhandled graphic type. Therefore we write the picture description + and not the relation( coz there ain't any), so that the user knows + there is an image/graphic in the doc but it is broken instead of + completely discarding it. +*/ + if ( aRelId.isEmpty() ) + m_pSerializer->startElementNS(XML_a, XML_blip); + else + m_pSerializer->startElementNS(XML_a, XML_blip, FSNS(XML_r, nImageType), aRelId); + + pItem = nullptr; + GraphicDrawMode nMode = GraphicDrawMode::Standard; + + if ( pGrfNode && SfxItemState::SET == pGrfNode->GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, true, &pItem)) + { + nMode = static_cast<GraphicDrawMode>(static_cast<const SfxEnumItemInterface*>(pItem)->GetEnumValue()); + if (nMode == GraphicDrawMode::Greys) + m_pSerializer->singleElementNS (XML_a, XML_grayscl); + else if (nMode == GraphicDrawMode::Mono) //black/white has a 0,5 threshold in LibreOffice + m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000)); + else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice + m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) ); + } + m_pSerializer->endElementNS( XML_a, XML_blip ); + + if (pSdrObj){ + WriteSrcRect(pSdrObj, pFrameFormat); + } + + m_pSerializer->startElementNS(XML_a, XML_stretch); + m_pSerializer->singleElementNS(XML_a, XML_fillRect); + m_pSerializer->endElementNS( XML_a, XML_stretch ); + m_pSerializer->endElementNS( XML_pic, XML_blipFill ); + + // TODO setup the right values below + m_pSerializer->startElementNS(XML_pic, XML_spPr, XML_bwMode, "auto"); + + m_pSerializer->startElementNS( + XML_a, XML_xfrm, uno::Reference<xml::sax::XFastAttributeList>(xFrameAttributes.get())); + + m_pSerializer->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0"); + OString aWidth( OString::number( TwipsToEMU( aSize.Width() ) ) ); + OString aHeight( OString::number( TwipsToEMU( aSize.Height() ) ) ); + m_pSerializer->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight); + m_pSerializer->endElementNS( XML_a, XML_xfrm ); + m_pSerializer->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect"); + m_pSerializer->singleElementNS(XML_a, XML_avLst); + m_pSerializer->endElementNS( XML_a, XML_prstGeom ); + + const SvxBoxItem& rBoxItem = pFrameFormat->GetBox(); + const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT); + const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT); + const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP); + const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM); + if (pLeft || pRight || pTop || pBottom) + m_rExport.SdrExporter().writeBoxItemLine(rBoxItem); + + m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat); + + m_pSerializer->endElementNS( XML_pic, XML_spPr ); + + m_pSerializer->endElementNS( XML_pic, XML_pic ); + + m_pSerializer->endElementNS( XML_a, XML_graphicData ); + m_pSerializer->endElementNS( XML_a, XML_graphic ); + m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat); +} + +void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment ) +{ + if( WriteOLEChart( pSdrObj, rSize, pFlyFrameFormat )) + return; + if( WriteOLEMath( rOLENode , nFormulaAlignment)) + return; + PostponeOLE( rOLENode, rSize, pFlyFrameFormat ); +} + +bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ) +{ + uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); + if (!xShape.is()) + return false; + + uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY); + if (!xPropSet.is()) + return false; + + OUString clsid; // why is the property of type string, not sequence<byte>? + xPropSet->getPropertyValue("CLSID") >>= clsid; + assert(!clsid.isEmpty()); + SvGlobalName aClassID; + bool const isValid(aClassID.MakeId(clsid)); + assert(isValid); (void)isValid; + + if (!SotExchange::IsChart(aClassID)) + return false; + + m_aPostponedCharts.push_back(PostponedChart(pSdrObj, rSize, pFlyFrameFormat)); + return true; +} + +/* + * Write chart hierarchy in w:drawing after end element of w:rPr tag. + */ +void DocxAttributeOutput::WritePostponedChart() +{ + if (m_aPostponedCharts.empty()) + return; + + for (const PostponedChart& rChart : m_aPostponedCharts) + { + uno::Reference< chart2::XChartDocument > xChartDoc; + uno::Reference< drawing::XShape > xShape(const_cast<SdrObject*>(rChart.object)->getUnoShape(), uno::UNO_QUERY ); + if( xShape.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + if( xPropSet.is() ) + xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY ); + } + + if( xChartDoc.is() ) + { + SAL_INFO("sw.ww8", "DocxAttributeOutput::WriteOLE2Obj: export chart "); + + m_rExport.SdrExporter().startDMLAnchorInline(rChart.frame, rChart.size); + + OUString sName("Object 1"); + uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY ); + if( xNamed.is() ) + sName = xNamed->getName(); + + /* If there is a scenario where a chart is followed by a shape + which is being exported as an alternate content then, the + docPr Id is being repeated, ECMA 20.4.2.5 says that the + docPr Id should be unique, ensuring the same here. + */ + m_pSerializer->singleElementNS( XML_wp, XML_docPr, + XML_id, OString::number(m_anchorId++), + XML_name, sName.toUtf8() ); + + m_pSerializer->singleElementNS(XML_wp, XML_cNvGraphicFramePr); + + m_pSerializer->startElementNS( XML_a, XML_graphic, + FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8() ); + + m_pSerializer->startElementNS( XML_a, XML_graphicData, + XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" ); + + OString aRelId; + m_nChartCount++; + aRelId = m_rExport.OutputChart( xChartDoc, m_nChartCount, m_pSerializer ); + + m_pSerializer->singleElementNS( XML_c, XML_chart, + FSNS( XML_xmlns, XML_c ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlChart)).toUtf8(), + FSNS( XML_xmlns, XML_r ), GetExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)).toUtf8(), + FSNS( XML_r, XML_id ), aRelId ); + + m_pSerializer->endElementNS( XML_a, XML_graphicData ); + m_pSerializer->endElementNS( XML_a, XML_graphic ); + + m_rExport.SdrExporter().endDMLAnchorInline(rChart.frame); + } + } + + m_aPostponedCharts.clear(); +} + +bool DocxAttributeOutput::WriteOLEMath( const SwOLENode& rOLENode ,const sal_Int8 nAlign) +{ + uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef()); + SvGlobalName aObjName(xObj->getClassID()); + + if( !SotExchange::IsMath(aObjName) ) + return false; + + PostponedMathObjects aPostponedMathObject; + try + { + aPostponedMathObject.pMathObject = const_cast<SwOLENode*>( &rOLENode); + aPostponedMathObject.nMathObjAlignment = nAlign; + m_aPostponedMaths.push_back(aPostponedMathObject); + } + catch (const uno::Exception&) + { + } + return true; +} + +void DocxAttributeOutput::WritePostponedMath(const SwOLENode* pPostponedMath, sal_Int8 nAlign) +{ + uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef()); + if (embed::EmbedStates::LOADED == xObj->getCurrentState()) + { + // must be running so there is a Component + try + { + xObj->changeState(embed::EmbedStates::RUNNING); + } + catch (const uno::Exception&) + { + } + } + uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY ); + if (!xInterface.is()) + { + SAL_WARN("sw.ww8", "Broken math object"); + return; + } +// gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class, +// so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated +// to RTLD_GLOBAL, so most probably a gcc bug. + oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get())); + assert( formulaexport != nullptr ); + if (formulaexport) + formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(), + oox::drawingml::DOCUMENT_DOCX, nAlign); +} + +void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject) +{ + if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm) + return; + + SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); + if (!pFormObj) + return; + + uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); + uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY); + if (!xInfo.is()) + return; + + if (xInfo->supportsService("com.sun.star.form.component.DateField")) + { + // gather component properties + + OUString sDateFormat; + uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY); + + OString sDate; + OUString aContentText; + bool bHasDate = false; + css::util::Date aUNODate; + if (xPropertySet->getPropertyValue("Date") >>= aUNODate) + { + bHasDate = true; + Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year); + sDate = DateToOString(aDate); + aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate).getStr()); + sDateFormat = "dd/MM/yyyy"; + } + else + { + aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>(); + if(sDateFormat.isEmpty()) + sDateFormat = "dd/MM/yyyy"; // Need to set date format even if there is no date set + } + + // output component + + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + if (bHasDate) + m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sDate); + else + m_pSerializer->startElementNS(XML_w, XML_date); + + m_pSerializer->singleElementNS(XML_w, XML_dateFormat, + FSNS(XML_w, XML_val), sDateFormat.toUtf8()); + m_pSerializer->singleElementNS(XML_w, XML_lid, + FSNS(XML_w, XML_val), "en-US"); + m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs, + FSNS(XML_w, XML_val), "dateTime"); + m_pSerializer->singleElementNS(XML_w, XML_calendar, + FSNS(XML_w, XML_val), "gregorian"); + + m_pSerializer->endElementNS(XML_w, XML_date); + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_sdtContent); + m_pSerializer->startElementNS(XML_w, XML_r); + + RunText(aContentText); + m_pSerializer->endElementNS(XML_w, XML_r); + m_pSerializer->endElementNS(XML_w, XML_sdtContent); + + m_pSerializer->endElementNS(XML_w, XML_sdt); + } + else if (xInfo->supportsService("com.sun.star.form.component.ComboBox")) + { + // gather component properties + + uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY); + OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>(); + const uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >(); + + // output component + + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_dropDownList); + + for (const auto& rItem : aItems) + { + m_pSerializer->singleElementNS(XML_w, XML_listItem, + FSNS(XML_w, XML_displayText), rItem.toUtf8(), + FSNS(XML_w, XML_value), rItem.toUtf8()); + } + + m_pSerializer->endElementNS(XML_w, XML_dropDownList); + m_pSerializer->endElementNS(XML_w, XML_sdtPr); + + m_pSerializer->startElementNS(XML_w, XML_sdtContent); + m_pSerializer->startElementNS(XML_w, XML_r); + RunText(sText); + m_pSerializer->endElementNS(XML_w, XML_r); + m_pSerializer->endElementNS(XML_w, XML_sdtContent); + + m_pSerializer->endElementNS(XML_w, XML_sdt); + } +} + +void DocxAttributeOutput::WritePostponedActiveXControl(bool bInsideRun) +{ + for( const auto & rPostponedDrawing : m_aPostponedActiveXControls ) + { + WriteActiveXControl(rPostponedDrawing.object, *rPostponedDrawing.frame, bInsideRun); + } + m_aPostponedActiveXControls.clear(); +} + + +void DocxAttributeOutput::WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun) +{ + SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); + if (!pFormObj) + return; + + uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); + if (!xControlModel.is()) + return; + + const bool bAnchoredInline = rFrameFormat.GetAnchor().GetAnchorId() == static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER); + + if(!bInsideRun) + { + m_pSerializer->startElementNS(XML_w, XML_r); + } + + // w:pict for floating embedded control and w:object for inline embedded control + if(bAnchoredInline) + m_pSerializer->startElementNS(XML_w, XML_object); + else + m_pSerializer->startElementNS(XML_w, XML_pict); + + // write ActiveX fragment and ActiveX binary + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), uno::UNO_QUERY); + std::pair<OString,OString> sRelIdAndName = m_rExport.WriteActiveXObject(xShape, xControlModel); + + // VML shape definition + m_rExport.VMLExporter().SetSkipwzName(true); + m_rExport.VMLExporter().SetHashMarkForType(true); + m_rExport.VMLExporter().OverrideShapeIDGen(true, "control_shape_"); + OString sShapeId; + if(bAnchoredInline) + { + sShapeId = m_rExport.VMLExporter().AddInlineSdrObject(*pObject, true); + } + else + { + const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); + const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient(); + SwFormatSurround const& rSurround(rFrameFormat.GetSurround()); + std::unique_ptr<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround)); + sShapeId = m_rExport.VMLExporter().AddSdrObject(*pObject, + rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(), + rHoriOri.GetRelationOrient(), + rVertOri.GetRelationOrient(), + std::move(pAttrList), + true); + } + // Restore default values + m_rExport.VMLExporter().SetSkipwzName(false); + m_rExport.VMLExporter().SetHashMarkForType(false); + m_rExport.VMLExporter().OverrideShapeIDGen(false); + + // control + m_pSerializer->singleElementNS(XML_w, XML_control, + FSNS(XML_r, XML_id), sRelIdAndName.first, + FSNS(XML_w, XML_name), sRelIdAndName.second, + FSNS(XML_w, XML_shapeid), sShapeId); + + if(bAnchoredInline) + m_pSerializer->endElementNS(XML_w, XML_object); + else + m_pSerializer->endElementNS(XML_w, XML_pict); + + if(!bInsideRun) + { + m_pSerializer->endElementNS(XML_w, XML_r); + } +} + +bool DocxAttributeOutput::ExportAsActiveXControl(const SdrObject* pObject) const +{ + SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject)); + if (!pFormObj) + return false; + + uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel(); + if (!xControlModel.is()) + return false; + + uno::Reference< css::frame::XModel > xModel( m_rExport.m_pDoc->GetDocShell() ? m_rExport.m_pDoc->GetDocShell()->GetModel() : nullptr ); + if (!xModel.is()) + return false; + + uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY); + if (!xInfo.is()) + return false; + + // See WritePostponedFormControl + // By now date field and combobox is handled on a different way, so let's not interfere with the other method. + if(xInfo->supportsService("com.sun.star.form.component.DateField") || + xInfo->supportsService("com.sun.star.form.component.ComboBox")) + return false; + + oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, xControlModel); + return exportHelper.isValid(); +} + +void DocxAttributeOutput::PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ) +{ + if( !m_pPostponedOLEs ) + //cannot be postponed, try to write now + WriteOLE( rNode, rSize, pFlyFrameFormat ); + else + m_pPostponedOLEs->push_back( PostponedOLE( &rNode, rSize, pFlyFrameFormat ) ); +} + +/* + * Write w:object hierarchy for embedded objects after end element of w:rPr tag. + */ +void DocxAttributeOutput::WritePostponedOLE() +{ + if( !m_pPostponedOLEs ) + return; + + for( const auto & rPostponedOLE : *m_pPostponedOLEs ) + { + WriteOLE( *rPostponedOLE.object, rPostponedOLE.size, rPostponedOLE.frame ); + } + + // clear list of postponed objects + m_pPostponedOLEs.reset(); +} + +void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* rFlyFrameFormat ) +{ + // get interoperability information about embedded objects + uno::Reference< beans::XPropertySet > xPropSet( m_rExport.m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList,aObjectInteropAttributes; + xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= aGrabBag; + auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "EmbeddedObjects"; }); + if (pProp != aGrabBag.end()) + pProp->Value >>= aObjectsInteropList; + + SwOLEObj& aObject = rNode.GetOLEObj(); + uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() ); + comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer(); + OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj ); + + // set some attributes according to the type of the embedded object + OUString sProgID, sDrawAspect; + switch (rNode.GetAspect()) + { + case embed::Aspects::MSOLE_CONTENT: sDrawAspect = "Content"; break; + case embed::Aspects::MSOLE_DOCPRINT: sDrawAspect = "DocPrint"; break; + case embed::Aspects::MSOLE_ICON: sDrawAspect = "Icon"; break; + case embed::Aspects::MSOLE_THUMBNAIL: sDrawAspect = "Thumbnail"; break; + default: + SAL_WARN("sw.ww8", "DocxAttributeOutput::WriteOLE: invalid aspect value"); + } + auto pObjectsInterop = std::find_if(aObjectsInteropList.begin(), aObjectsInteropList.end(), + [&sObjectName](const beans::PropertyValue& rProp) { return rProp.Name == sObjectName; }); + if (pObjectsInterop != aObjectsInteropList.end()) + pObjectsInterop->Value >>= aObjectInteropAttributes; + + for( const auto& rObjectInteropAttribute : std::as_const(aObjectInteropAttributes) ) + { + if ( rObjectInteropAttribute.Name == "ProgID" ) + { + rObjectInteropAttribute.Value >>= sProgID; + } + } + + // write embedded file + OString sId = m_rExport.WriteOLEObject(aObject, sProgID); + + if( sId.isEmpty() ) + { + // the embedded file could not be saved + // fallback: save as an image + FlyFrameGraphic( nullptr, rSize, rFlyFrameFormat, &rNode ); + return; + } + + // write preview image + const Graphic* pGraphic = rNode.GetGraphic(); + m_rDrawingML.SetFS(m_pSerializer); + OUString sImageId = m_rDrawingML.WriteImage( *pGraphic ); + + if ( sDrawAspect == "Content" ) + { + awt::Size aSize; + try + { + aSize = xObj->getVisualAreaSize( rNode.GetAspect() ); + + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rNode.GetAspect() ) ); + Size aOriginalSize( OutputDevice::LogicToLogic(Size( aSize.Width, aSize.Height), + MapMode(aUnit), MapMode(MapUnit::MapTwip))); + + m_pSerializer->startElementNS( XML_w, XML_object, + FSNS(XML_w, XML_dxaOrig), OString::number(aOriginalSize.Width()), + FSNS(XML_w, XML_dyaOrig), OString::number(aOriginalSize.Height()) ); + } + catch ( uno::Exception& ) + { + m_pSerializer->startElementNS(XML_w, XML_object); + } + } + else + { + m_pSerializer->startElementNS(XML_w, XML_object); + } + + //tdf#131539: Export OLE positions in docx: + //This string will store the position output for the xml + OString aPos; + //This string will store the relative position for aPos + OString aAnch; + + if (rFlyFrameFormat && rFlyFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + //Get the horizontal alignment of the OLE via the frame format, to aHAlign + OString aHAlign = convertToOOXMLHoriOrient(rFlyFrameFormat->GetHoriOrient().GetHoriOrient(), + rFlyFrameFormat->GetHoriOrient().IsPosToggle()); + //Get the vertical alignment of the OLE via the frame format to aVAlign + OString aVAlign = convertToOOXMLVertOrient(rFlyFrameFormat->GetVertOrient().GetVertOrient()); + + //Get the relative horizontal positions for the anchors + OString aHAnch = convertToOOXMLHoriOrientRel(rFlyFrameFormat->GetHoriOrient().GetRelationOrient()); + //Get the relative vertical positions for the anchors + OString aVAnch = convertToOOXMLVertOrientRel(rFlyFrameFormat->GetVertOrient().GetRelationOrient()); + + //Choice that the horizontal position is relative or not + if (!aHAlign.isEmpty()) + aHAlign = ";mso-position-horizontal:" + aHAlign; + aHAlign = ";mso-position-horizontal-relative:" + aHAnch; + + //Choice that the vertical position is relative or not + if (!aVAlign.isEmpty()) + aVAlign = ";mso-position-vertical:" + aVAlign; + aVAlign = ";mso-position-vertical-relative:" + aVAnch; + + //Set the anchoring information into one string for aPos + aAnch = aHAlign + aVAlign; + + //Query the positions to aPos from frameformat + aPos = + "position:absolute;margin-left:" + OString::number(double(rFlyFrameFormat->GetHoriOrient().GetPos()) / 20) + + "pt;margin-top:" + OString::number(double(rFlyFrameFormat->GetVertOrient().GetPos()) / 20) + "pt;"; + } + + OString sShapeStyle = "width:" + OString::number( double( rSize.Width() ) / 20 ) + + "pt;height:" + OString::number( double( rSize.Height() ) / 20 ) + + "pt"; //from VMLExport::AddRectangleDimensions(), it does: value/20 + OString sShapeId = "ole_" + sId; + + //Export anchor setting, if it exists + if (!aPos.isEmpty() && !aAnch.isEmpty()) + sShapeStyle = aPos + sShapeStyle + aAnch; + + // shape definition + m_pSerializer->startElementNS( XML_v, XML_shape, + XML_id, sShapeId.getStr(), + XML_style, sShapeStyle.getStr(), + FSNS( XML_o, XML_ole ), ""); //compulsory, even if it's empty + + // shape filled with the preview image + m_pSerializer->singleElementNS( XML_v, XML_imagedata, + FSNS( XML_r, XML_id ), sImageId.toUtf8(), + FSNS( XML_o, XML_title ), "" ); + + //export wrap settings + if(rFlyFrameFormat && rFlyFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + { + const SwFormatSurround aWrap = rFlyFrameFormat->GetSurround(); + bool bIsCountur = aWrap.IsContour(); + + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_NONE) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "topAndBottom"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_PARALLEL && !bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_PARALLEL && bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_DYNAMIC && !bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "largest"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_LEFT && !bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "left"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_RIGHT && !bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "square", XML_side, "right"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_DYNAMIC && bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "largest"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_LEFT && bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "left"); + if (aWrap.GetSurround() == text::WrapTextMode::WrapTextMode_RIGHT && bIsCountur) + m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, "tight", XML_side, "right"); + } + m_pSerializer->endElementNS( XML_v, XML_shape ); + + // OLE object definition + m_pSerializer->singleElementNS( XML_o, XML_OLEObject, + XML_Type, "Embed", + XML_ProgID, sProgID.toUtf8(), + XML_ShapeID, sShapeId.getStr(), + XML_DrawAspect, sDrawAspect.toUtf8(), + XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())), + FSNS( XML_r, XML_id ), sId ); + + m_pSerializer->endElementNS( XML_w, XML_object ); +} + +void DocxAttributeOutput::WritePostponedCustomShape() +{ + if (!m_pPostponedCustomShape) + return; + + for( const auto & rPostponedDrawing : *m_pPostponedCustomShape) + { + if ( IsAlternateContentChoiceOpen() ) + m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++); + else + m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++); + } + m_pPostponedCustomShape.reset(); +} + +void DocxAttributeOutput::WritePostponedDMLDrawing() +{ + if (!m_pPostponedDMLDrawings) + return; + + // Clear the list early, this method may be called recursively. + std::unique_ptr< std::vector<PostponedDrawing> > pPostponedDMLDrawings(std::move(m_pPostponedDMLDrawings)); + std::unique_ptr< std::vector<PostponedOLE> > pPostponedOLEs(std::move(m_pPostponedOLEs)); + + for( const auto & rPostponedDrawing : *pPostponedDMLDrawings ) + { + // Avoid w:drawing within another w:drawing. + if ( IsAlternateContentChoiceOpen() && !( m_rExport.SdrExporter().IsDrawingOpen()) ) + m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++); + else + m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++); + } + + m_pPostponedOLEs = std::move(pPostponedOLEs); +} + +void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const Point& /*rNdTopLeft*/ ) +{ + m_pSerializer->mark(Tag_OutputFlyFrame); + + switch ( rFrame.GetWriterType() ) + { + case ww8::Frame::eGraphic: + { + const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject(); + const SwNode *pNode = rFrame.GetContent(); + const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : nullptr; + if ( pGrfNode ) + { + if (!m_pPostponedGraphic) + { + m_bPostponedProcessingFly = false ; + FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj); + } + else // we are writing out attributes, but w:drawing should not be inside w:rPr, + { // so write it out later + m_bPostponedProcessingFly = true ; + m_pPostponedGraphic->push_back(PostponedGraphic(pGrfNode, rFrame.GetLayoutSize(), pSdrObj)); + } + } + } + break; + case ww8::Frame::eDrawing: + { + const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject(); + if ( pSdrObj ) + { + uno::Reference<drawing::XShape> xShape( + const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); + + if (xShape.is() && oox::drawingml::DrawingML::IsDiagram(xShape)) + { + if ( !m_pPostponedDiagrams ) + { + m_bPostponedProcessingFly = false ; + m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++); + } + else // we are writing out attributes, but w:drawing should not be inside w:rPr, + { // so write it out later + m_bPostponedProcessingFly = true ; + m_pPostponedDiagrams->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrameFormat()) )); + } + } + else + { + if (!m_pPostponedDMLDrawings) + { + if ( IsAlternateContentChoiceOpen() ) + { + // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing. + if( m_rExport.SdrExporter().IsDrawingOpen() ) + m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); + else + m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrameFormat(), m_anchorId++); + } + else + m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++); + + m_bPostponedProcessingFly = false ; + } + // IsAlternateContentChoiceOpen(): check is to ensure that only one object is getting added. Without this check, plus one object gets added + // m_bParagraphFrameOpen: check if the frame is open. + else if (IsAlternateContentChoiceOpen() && m_bParagraphFrameOpen) + m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); + else + { + // we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later + m_bPostponedProcessingFly = true ; + m_pPostponedDMLDrawings->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()))); + } + } + } + } + break; + case ww8::Frame::eTextBox: + { + // If this is a TextBox of a shape, then ignore: it's handled in WriteTextBox(). + if (DocxSdrExport::isTextBox(rFrame.GetFrameFormat())) + break; + + // If this is a TextBox containing a table which we already exported directly, ignore it + if (m_aFloatingTablesOfParagraph.find(&rFrame.GetFrameFormat()) != m_aFloatingTablesOfParagraph.end()) + break; + + // The frame output is postponed to the end of the anchor paragraph + bool bDuplicate = false; + const OUString& rName = rFrame.GetFrameFormat().GetName(); + unsigned nSize = m_aFramesOfParagraph.size(); + for( unsigned nIndex = 0; nIndex < nSize; ++nIndex ) + { + const OUString& rNameExisting = m_aFramesOfParagraph[nIndex].GetFrameFormat().GetName(); + + if (!rName.isEmpty() && !rNameExisting.isEmpty()) + { + if (rName == rNameExisting) + bDuplicate = true; + } + } + + if( !bDuplicate ) + { + m_bPostponedProcessingFly = true ; + m_aFramesOfParagraph.emplace_back(rFrame); + } + } + break; + case ww8::Frame::eOle: + { + const SwFrameFormat &rFrameFormat = rFrame.GetFrameFormat(); + const SdrObject *pSdrObj = rFrameFormat.FindRealSdrObject(); + if ( pSdrObj ) + { + SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1); + SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode(); + + //output variable for the formula alignment (default inline) + sal_Int8 nAlign(FormulaExportBase::eFormulaAlign::INLINE); + auto xObj(rOLENd.GetOLEObj().GetOleRef()); //get the xObject of the forumla + + //tdf133030: Export formula position + //If we have a formula with inline anchor... + if(SotExchange::IsMath(xObj->getClassID()) && rFrame.IsInline()) + { + SwPosition const* const aAPos = rFrameFormat.GetAnchor().GetContentAnchor(); + if(aAPos) + { + //Get the text node what the forumla anchored to + const SwTextNode* pTextNode = aAPos->nNode.GetNode().GetTextNode(); + if(pTextNode && pTextNode->Len() == 1) + { + //Get the paragraph alignment + auto aParaAdjust = pTextNode->GetSwAttrSet().GetAdjust().GetAdjust(); + //And set the formula according to the paragraph alignment + if (aParaAdjust == SvxAdjust::Center) + nAlign = FormulaExportBase::eFormulaAlign::CENTER; + else if (aParaAdjust == SvxAdjust::Right) + nAlign = FormulaExportBase::eFormulaAlign::RIGHT; + else // left in the case of left and justified paragraph alignments + nAlign = FormulaExportBase::eFormulaAlign::LEFT; + } + } + } + WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrameFormat*>( &rFrameFormat ), nAlign); + m_bPostponedProcessingFly = false ; + } + } + break; + case ww8::Frame::eFormControl: + { + const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject(); + if(ExportAsActiveXControl(pObject)) + m_aPostponedActiveXControls.emplace_back(pObject, &(rFrame.GetFrameFormat())); + else + m_aPostponedFormControls.push_back(pObject); + m_bPostponedProcessingFly = true ; + } + break; + default: + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFrame ) - frame type " << + ( rFrame.GetWriterType() == ww8::Frame::eTextBox ? "eTextBox": + ( rFrame.GetWriterType() == ww8::Frame::eOle ? "eOle": "???" ) ) ); + break; + } + + m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame); +} + +void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj) +{ + const EditTextObject& rEditObj = rParaObj.GetTextObject(); + MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX ); + + sal_Int32 nPara = rEditObj.GetParagraphCount(); + + m_pSerializer->startElementNS(XML_w, XML_txbxContent); + for (sal_Int32 n = 0; n < nPara; ++n) + { + if( n ) + aAttrIter.NextPara( n ); + + OUString aStr( rEditObj.GetText( n )); + sal_Int32 nCurrentPos = 0; + sal_Int32 nEnd = aStr.getLength(); + + StartParagraph(ww8::WW8TableNodeInfo::Pointer_t()); + + // Write paragraph properties. + StartParagraphProperties(); + aAttrIter.OutParaAttr(false); + SfxItemSet aParagraphMarkerProperties(m_rExport.m_pDoc->GetAttrPool()); + EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr); + + do { + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + + m_pSerializer->startElementNS(XML_w, XML_r); + + // Write run properties. + m_pSerializer->startElementNS(XML_w, XML_rPr); + aAttrIter.OutAttr(nCurrentPos); + WriteCollectedRunProperties(); + m_pSerializer->endElementNS(XML_w, XML_rPr); + + bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); + if( !bTextAtr ) + { + OUString aOut( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos ) ); + RunText(aOut); + } + + if ( !m_sRawText.isEmpty() ) + { + RunText( m_sRawText ); + m_sRawText.clear(); + } + + m_pSerializer->endElementNS( XML_w, XML_r ); + + nCurrentPos = nNextAttr; + aAttrIter.NextPos(); + } + while( nCurrentPos < nEnd ); + // Word can't handle nested text boxes, so write them on the same level. + ++m_nTextFrameLevel; + EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t()); + --m_nTextFrameLevel; + } + m_pSerializer->endElementNS( XML_w, XML_txbxContent ); +} + +void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rContext) +{ + rContext.m_pTableInfo = m_rExport.m_pTableInfo; + m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); + + rContext.m_bTableCellOpen = m_tableReference->m_bTableCellOpen; + m_tableReference->m_bTableCellOpen = false; + + rContext.m_nTableDepth = m_tableReference->m_nTableDepth; + m_tableReference->m_nTableDepth = 0; + + rContext.m_bStartedParaSdt = m_bStartedParaSdt; + m_bStartedParaSdt = false; +} + +void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext) +{ + m_rExport.m_pTableInfo = rContext.m_pTableInfo; + m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen; + m_tableReference->m_nTableDepth = rContext.m_nTableDepth; + m_bStartedParaSdt = rContext.m_bStartedParaSdt; +} + +void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape) +{ + DocxTableExportContext aTableExportContext(*this); + + SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape); + const SwPosition* pAnchor = pTextBox->GetAnchor().GetContentAnchor(); + ww8::Frame aFrame(*pTextBox, *pAnchor); + m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++, /*bTextBoxOnly=*/true); +} + +void DocxAttributeOutput::WriteVMLTextBox(uno::Reference<drawing::XShape> xShape) +{ + DocxTableExportContext aTableExportContext(*this); + + SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape); + const SwPosition* pAnchor = pTextBox->GetAnchor().GetContentAnchor(); + ww8::Frame aFrame(*pTextBox, *pAnchor); + m_rExport.SdrExporter().writeVMLTextFrame(&aFrame, /*bTextBoxOnly=*/true); +} + +oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML() +{ + return m_rDrawingML; +} + +void DocxAttributeOutput::MaybeOutputBrushItem(SfxItemSet const& rSet) +{ + const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE)); + + if ((pXFillStyleItem && pXFillStyleItem->GetValue() != drawing::FillStyle_NONE) + || !m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + return; + } + + // sw text frames are opaque by default, even with fill none! + std::unique_ptr<SfxItemSet> const pClone(rSet.Clone()); + XFillColorItem const aColor(OUString(), COL_WHITE); + pClone->Put(aColor); + // call getSvxBrushItemForSolid - this also takes XFillTransparenceItem into account + XFillStyleItem const aSolid(drawing::FillStyle_SOLID); + pClone->Put(aSolid); + std::unique_ptr<SvxBrushItem> const pBrush(getSvxBrushItemFromSourceSet(*pClone, RES_BACKGROUND)); + FormatBackground(*pBrush); +} + +namespace { + +/// Functor to do case-insensitive ordering of OUString instances. +struct OUStringIgnoreCase +{ + bool operator() (const OUString& lhs, const OUString& rhs) const + { + return lhs.compareToIgnoreAsciiCase(rhs) < 0; + } +}; + +} + +/// Guesses if a style created in Writer (no grab-bag) should be qFormat or not. +static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId) +{ + // If the style has no dedicated STI number, then it's probably a custom style -> qFormat. + if (nWwId == ww::stiUser) + return true; + + // Allow exported built-in styles UI language neutral + if ( nWwId == ww::stiNormal || + ( nWwId>= ww::stiLev1 && nWwId <= ww::stiLev9 ) || + nWwId == ww::stiCaption || nWwId == ww::stiTitle || + nWwId == ww::stiSubtitle || nWwId == ww::stiStrong || + nWwId == ww::stiEmphasis ) + return true; + + static o3tl::sorted_vector<OUString, OUStringIgnoreCase> const aWhitelist + { + "No Spacing", + "List Paragraph", + "Quote", + "Intense Quote", + "Subtle Emphasis,", + "Intense Emphasis", + "Subtle Reference", + "Intense Reference", + "Book Title", + "TOC Heading", + }; + // Not custom style? Then we have a list of standard styles which should be qFormat. + return aWhitelist.find(rName) != aWhitelist.end(); +} + +void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType, + sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate ) +{ + bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false; + OUString aLink, aRsid, aUiPriority; + FastAttributeList* pStyleAttributeList = FastSerializerHelper::createAttrList(); + uno::Any aAny; + if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR) + { + const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nId); + pFormat->GetGrabBagItem(aAny); + } + else + { + const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nId); + pRule->GetGrabBagItem(aAny); + } + const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >(); + + for (const auto& rProp : rGrabBag) + { + if (rProp.Name == "uiPriority") + aUiPriority = rProp.Value.get<OUString>(); + else if (rProp.Name == "qFormat") + bQFormat = true; + else if (rProp.Name == "link") + aLink = rProp.Value.get<OUString>(); + else if (rProp.Name == "rsid") + aRsid = rProp.Value.get<OUString>(); + else if (rProp.Name == "unhideWhenUsed") + bUnhideWhenUsed = true; + else if (rProp.Name == "semiHidden") + bSemiHidden = true; + else if (rProp.Name == "locked") + bLocked = true; + else if (rProp.Name == "default") + bDefault = rProp.Value.get<bool>(); + else if (rProp.Name == "customStyle") + bCustomStyle = rProp.Value.get<bool>(); + else + SAL_WARN("sw.ww8", "Unhandled style property: " << rProp.Name); + } + + // MSO exports English names and writerfilter only recognize them. + const char *pEnglishName = nullptr; + const char* pType = nullptr; + switch (eType) + { + case STYLE_TYPE_PARA: + pType = "paragraph"; + if ( nWwId < ww::stiMax) + pEnglishName = ww::GetEnglishNameFromSti( static_cast<ww::sti>(nWwId ) ); + break; + case STYLE_TYPE_CHAR: pType = "character"; break; + case STYLE_TYPE_LIST: pType = "numbering"; break; + } + pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType); + pStyleAttributeList->add(FSNS( XML_w, XML_styleId ), m_rExport.m_pStyles->GetStyleId(nId).getStr()); + if (bDefault) + pStyleAttributeList->add(FSNS(XML_w, XML_default), "1"); + if (bCustomStyle) + pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1"); + XFastAttributeListRef xStyleAttributeList(pStyleAttributeList); + m_pSerializer->startElementNS( XML_w, XML_style, xStyleAttributeList); + m_pSerializer->singleElementNS( XML_w, XML_name, + FSNS( XML_w, XML_val ), pEnglishName ? pEnglishName : rName.toUtf8() ); + + if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST) + { + m_pSerializer->singleElementNS( XML_w, XML_basedOn, + FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase) ); + } + + if ( nNext != nId && eType != STYLE_TYPE_LIST) + { + m_pSerializer->singleElementNS( XML_w, XML_next, + FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext) ); + } + + if (!aLink.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_link, + FSNS(XML_w, XML_val), aLink.toUtf8()); + + if ( bAutoUpdate ) + m_pSerializer->singleElementNS(XML_w, XML_autoRedefine); + + if (!aUiPriority.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_uiPriority, + FSNS(XML_w, XML_val), aUiPriority.toUtf8()); + if (bSemiHidden) + m_pSerializer->singleElementNS(XML_w, XML_semiHidden); + if (bUnhideWhenUsed) + m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed); + + if (bQFormat || lcl_guessQFormat(rName, nWwId)) + m_pSerializer->singleElementNS(XML_w, XML_qFormat); + if (bLocked) + m_pSerializer->singleElementNS(XML_w, XML_locked); + if (!aRsid.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_rsid, + FSNS(XML_w, XML_val), aRsid.toUtf8()); +} + +void DocxAttributeOutput::EndStyle() +{ + m_pSerializer->endElementNS( XML_w, XML_style ); +} + +void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ ) +{ + if ( bParProp ) + { + m_pSerializer->startElementNS(XML_w, XML_pPr); + InitCollectedParagraphProperties(); + } + else + { + m_pSerializer->startElementNS(XML_w, XML_rPr); + InitCollectedRunProperties(); + } +} + +void DocxAttributeOutput::EndStyleProperties( bool bParProp ) +{ + if ( bParProp ) + { + WriteCollectedParagraphProperties(); + + // Merge the marks for the ordered elements + m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties); + + m_pSerializer->endElementNS( XML_w, XML_pPr ); + } + else + { + WriteCollectedRunProperties(); + + // Merge the marks for the ordered elements + m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties); + + m_pSerializer->endElementNS( XML_w, XML_rPr ); + } +} + +namespace +{ + +void lcl_OutlineLevel(sax_fastparser::FSHelperPtr const & pSerializer, sal_uInt16 nLevel) +{ + if (nLevel >= WW8ListManager::nMaxLevel) + nLevel = WW8ListManager::nMaxLevel - 1; + + pSerializer->singleElementNS(XML_w, XML_outlineLvl, + FSNS(XML_w, XML_val), OString::number(nLevel)); +} + +} + +void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/) +{ + // Handled by ParaOutlineLevel() instead. +} + +void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem) +{ + if (rItem.GetValue() > 0) + lcl_OutlineLevel(m_pSerializer, rItem.GetValue() - 1); +} + +void DocxAttributeOutput::PageBreakBefore( bool bBreak ) +{ + if ( bBreak ) + m_pSerializer->singleElementNS(XML_w, XML_pageBreakBefore); + else + m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore, + FSNS( XML_w, XML_val ), "false" ); +} + +void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo ) +{ + switch ( nC ) + { + case msword::ColumnBreak: + // The column break should be output in the next paragraph... + if ( m_nColBreakStatus == COLBRK_WRITE ) + m_nColBreakStatus = COLBRK_WRITEANDPOSTPONE; + else + m_nColBreakStatus = COLBRK_POSTPONE; + break; + case msword::PageBreak: + if ( pSectionInfo ) + { + // Detect when the current node is the last node in the + // document: the last section is written explicitly in + // DocxExport::WriteMainText(), don't duplicate that here. + SwNodeIndex aCurrentNode(m_rExport.m_pCurPam->GetNode()); + SwNodeIndex aLastNode(m_rExport.m_pDoc->GetNodes().GetEndOfContent(), -1); + bool bEmit = aCurrentNode != aLastNode; + + if (!bEmit) + { + // Need to still emit an empty section at the end of the + // document in case balanced columns are wanted, since the last + // section in Word is always balanced. + sal_uInt16 nColumns = 1; + bool bBalance = false; + if (const SwSectionFormat* pFormat = pSectionInfo->pSectionFormat) + { + if (pFormat != reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1))) + { + nColumns = pFormat->GetCol().GetNumCols(); + const SwFormatNoBalancedColumns& rNoBalanced = pFormat->GetBalancedColumns(); + bBalance = !rNoBalanced.GetValue(); + } + } + bEmit = (nColumns > 1 && bBalance); + } + + // don't add section properties if this will be the first + // paragraph in the document + if ( !m_bParagraphOpened && !m_bIsFirstParagraph && bEmit ) + { + // Create a dummy paragraph if needed + m_pSerializer->startElementNS(XML_w, XML_p); + m_pSerializer->startElementNS(XML_w, XML_pPr); + + m_rExport.SectionProperties( *pSectionInfo ); + + m_pSerializer->endElementNS( XML_w, XML_pPr ); + m_pSerializer->endElementNS( XML_w, XML_p ); + } + else + { + // postpone the output of this; it has to be done inside the + // paragraph properties, so remember it until then + m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo )); + } + } + else if ( m_bParagraphOpened ) + { + if (bBreakAfter) + // tdf#128889 + m_bPageBreakAfter = true; + else + { + m_pSerializer->startElementNS(XML_w, XML_r); + m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page"); + m_pSerializer->endElementNS(XML_w, XML_r); + } + } + else + m_bPostponedPageBreak = true; + + break; + default: + SAL_INFO("sw.ww8", "Unknown section break to write: " << nC ); + break; + } +} + +void DocxAttributeOutput::EndParaSdtBlock() +{ + if (m_bStartedParaSdt) + { + // Paragraph-level SDT still open? Close it now. + EndSdtBlock(); + m_bStartedParaSdt = false; + } +} + +void DocxAttributeOutput::StartSection() +{ + m_pSerializer->startElementNS(XML_w, XML_sectPr); + m_bOpenedSectPr = true; + + // Write the elements in the spec order + static const sal_Int32 aOrder[] = + { + FSNS( XML_w, XML_headerReference ), + FSNS( XML_w, XML_footerReference ), + FSNS( XML_w, XML_footnotePr ), + FSNS( XML_w, XML_endnotePr ), + FSNS( XML_w, XML_type ), + FSNS( XML_w, XML_pgSz ), + FSNS( XML_w, XML_pgMar ), + FSNS( XML_w, XML_paperSrc ), + FSNS( XML_w, XML_pgBorders ), + FSNS( XML_w, XML_lnNumType ), + FSNS( XML_w, XML_pgNumType ), + FSNS( XML_w, XML_cols ), + FSNS( XML_w, XML_formProt ), + FSNS( XML_w, XML_vAlign ), + FSNS( XML_w, XML_noEndnote ), + FSNS( XML_w, XML_titlePg ), + FSNS( XML_w, XML_textDirection ), + FSNS( XML_w, XML_bidi ), + FSNS( XML_w, XML_rtlGutter ), + FSNS( XML_w, XML_docGrid ), + FSNS( XML_w, XML_printerSettings ), + FSNS( XML_w, XML_sectPrChange ) + }; + + // postpone the output so that we can later [in EndParagraphProperties()] + // prepend the properties before the run + m_pSerializer->mark(Tag_StartSection, comphelper::containerToSequence(aOrder)); + m_bHadSectPr = true; +} + +void DocxAttributeOutput::EndSection() +{ + // Write the section properties + if ( m_pSectionSpacingAttrList.is() ) + { + XFastAttributeListRef xAttrList( m_pSectionSpacingAttrList.get() ); + m_pSectionSpacingAttrList.clear(); + + m_pSerializer->singleElementNS( XML_w, XML_pgMar, xAttrList ); + } + + // Order the elements + m_pSerializer->mergeTopMarks(Tag_StartSection); + + m_pSerializer->endElementNS( XML_w, XML_sectPr ); + m_bOpenedSectPr = false; +} + +void DocxAttributeOutput::SectionFormProtection( bool bProtected ) +{ + if ( bProtected ) + m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "true"); + else + m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) +{ + FastAttributeList* pAttr = FastSerializerHelper::createAttrList(); + pAttr->add( FSNS( XML_w, XML_countBy ), OString::number(rLnNumInfo.GetCountBy()).getStr()); + pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" ); + if( rLnNumInfo.GetPosFromLeft()) + pAttr->add( FSNS( XML_w, XML_distance ), OString::number(rLnNumInfo.GetPosFromLeft()).getStr()); + if (nRestartNo > 0) + // Writer is 1-based, Word is 0-based. + pAttr->add(FSNS(XML_w, XML_start), OString::number(nRestartNo - 1).getStr()); + XFastAttributeListRef xAttrs( pAttr ); + m_pSerializer->singleElementNS( XML_w, XML_lnNumType, xAttrs ); +} + +void DocxAttributeOutput::SectionTitlePage() +{ + m_pSerializer->singleElementNS(XML_w, XML_titlePg); +} + +void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* /*pFirstPageFormat*/ ) +{ + // Output the margins + + const SvxBoxItem& rBox = pFormat->GetBox( ); + + const SvxBorderLine* pLeft = rBox.GetLeft( ); + const SvxBorderLine* pTop = rBox.GetTop( ); + const SvxBorderLine* pRight = rBox.GetRight( ); + const SvxBorderLine* pBottom = rBox.GetBottom( ); + + if ( !(pBottom || pTop || pLeft || pRight) ) + return; + + OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions(); + + // Check if there is a shadow item + const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW ); + if ( pItem ) + { + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation(); + } + + // By top margin, impl_borders() means the distance between the top of the page and the header frame. + editeng::WordPageMargins aMargins = m_pageMargins; + HdFtDistanceGlue aGlue(pFormat->GetAttrSet()); + if (aGlue.HasHeader()) + aMargins.nTop = aGlue.dyaHdrTop; + // Ditto for bottom margin. + if (aGlue.HasFooter()) + aMargins.nBottom = aGlue.dyaHdrBottom; + + aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>(); + editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances); + + // All distances are relative to the text margins + m_pSerializer->startElementNS(XML_w, XML_pgBorders, + FSNS(XML_w, XML_display), "allPages", + FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text"); + + std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map + impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap ); + + m_pSerializer->endElementNS( XML_w, XML_pgBorders ); + +} + +void DocxAttributeOutput::SectionBiDi( bool bBiDi ) +{ + if ( bBiDi ) + m_pSerializer->singleElementNS(XML_w, XML_bidi); +} + +static OString impl_NumberingType( sal_uInt16 nNumberingType ) +{ + OString aType; + + switch ( nNumberingType ) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: aType = "upperLetter"; break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: aType = "lowerLetter"; break; + case SVX_NUM_ROMAN_UPPER: aType = "upperRoman"; break; + case SVX_NUM_ROMAN_LOWER: aType = "lowerRoman"; break; + + case SVX_NUM_ARABIC: aType = "decimal"; break; + + case SVX_NUM_BITMAP: + case SVX_NUM_CHAR_SPECIAL: aType = "bullet"; break; + + case style::NumberingType::CHARS_HEBREW: aType = "hebrew2"; break; + case style::NumberingType::NUMBER_HEBREW: aType = "hebrew1"; break; + + default: aType = "none"; break; + } + + return aType; +} + +// Converting Level Numbering Format Code to string +static OString impl_LevelNFC(sal_uInt16 nNumberingType, const SfxItemSet* pOutSet, OString& rFormat) +{ + OString aType; + + switch ( nNumberingType ) + { + case style::NumberingType::CHARS_UPPER_LETTER: + case style::NumberingType::CHARS_UPPER_LETTER_N: + case style::NumberingType::CHARS_LOWER_LETTER: + case style::NumberingType::CHARS_LOWER_LETTER_N: + case style::NumberingType::ROMAN_UPPER: + case style::NumberingType::ROMAN_LOWER: + case style::NumberingType::ARABIC: + case style::NumberingType::BITMAP: + case style::NumberingType::CHAR_SPECIAL: + case style::NumberingType::CHARS_HEBREW: + case style::NumberingType::NUMBER_HEBREW: + case style::NumberingType::NUMBER_NONE: + return impl_NumberingType( nNumberingType ); + case style::NumberingType::FULLWIDTH_ARABIC: aType="decimalFullWidth"; break; + case style::NumberingType::TIAN_GAN_ZH: aType="ideographTraditional"; break; + case style::NumberingType::DI_ZI_ZH: aType="ideographZodiac"; break; + case style::NumberingType::NUMBER_LOWER_ZH: + aType="taiwaneseCountingThousand"; + if (pOutSet) { + const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE); + const LanguageType eLang = rLang.GetLanguage(); + + if (LANGUAGE_CHINESE_SIMPLIFIED == eLang) { + aType="chineseCountingThousand"; + } + } + break; + case style::NumberingType::NUMBER_UPPER_ZH_TW: aType="ideographLegalTraditional";break; + case style::NumberingType::NUMBER_UPPER_ZH: aType="chineseLegalSimplified"; break; + case style::NumberingType::NUMBER_TRADITIONAL_JA: aType="japaneseLegal";break; + case style::NumberingType::AIU_FULLWIDTH_JA: aType="aiueoFullWidth";break; + case style::NumberingType::AIU_HALFWIDTH_JA: aType="aiueo";break; + case style::NumberingType::IROHA_FULLWIDTH_JA: aType="iroha";break; + case style::NumberingType::IROHA_HALFWIDTH_JA: aType="irohaFullWidth";break; + case style::NumberingType::HANGUL_SYLLABLE_KO: aType="ganada";break; + case style::NumberingType::HANGUL_JAMO_KO: aType="chosung";break; + case style::NumberingType::NUMBER_HANGUL_KO: aType="koreanDigital";break; + case style::NumberingType::NUMBER_UPPER_KO: aType="koreanLegal"; break; + case style::NumberingType::CIRCLE_NUMBER: aType="decimalEnclosedCircle"; break; + case style::NumberingType::CHARS_ARABIC: aType="arabicAlpha"; break; + case style::NumberingType::CHARS_THAI: aType="thaiLetters"; break; + case style::NumberingType::CHARS_PERSIAN: aType="hindiVowels"; break; + case style::NumberingType::TEXT_NUMBER: aType="ordinal"; break; + case style::NumberingType::TEXT_CARDINAL: aType="cardinalText"; break; + case style::NumberingType::TEXT_ORDINAL: aType="ordinalText"; break; + case style::NumberingType::SYMBOL_CHICAGO: aType="chicago"; break; + case style::NumberingType::ARABIC_ZERO: aType = "decimalZero"; break; + case style::NumberingType::ARABIC_ZERO3: + aType = "custom"; + rFormat = "001, 002, 003, ..."; + break; + case style::NumberingType::ARABIC_ZERO4: + aType = "custom"; + rFormat = "0001, 0002, 0003, ..."; + break; + case style::NumberingType::ARABIC_ZERO5: + aType = "custom"; + rFormat = "00001, 00002, 00003, ..."; + break; +/* + Fallback the rest to decimal. + case style::NumberingType::NATIVE_NUMBERING: + case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: + case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: + case style::NumberingType::CHARS_GREEK_UPPER_LETTER: + case style::NumberingType::CHARS_GREEK_LOWER_LETTER: + case style::NumberingType::PAGE_DESCRIPTOR: + case style::NumberingType::TRANSLITERATION: + case style::NumberingType::CHARS_NEPALI: + case style::NumberingType::CHARS_KHMER: + case style::NumberingType::CHARS_LAO: + case style::NumberingType::CHARS_TIBETAN: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU: + case style::NumberingType::CHARS_MYANMAR: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR: + case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR: + case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR: +*/ + default: + aType = "decimal"; break; + } + return aType; +} + + +void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) +{ + // FIXME Not called properly with page styles like "First Page" + + FastAttributeList* pAttr = FastSerializerHelper::createAttrList(); + + // std::nullopt means no restart: then don't output that attribute if it is negative + if ( oPageRestartNumber ) + pAttr->add( FSNS( XML_w, XML_start ), OString::number( *oPageRestartNumber ) ); + + // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions + OString aFormat( impl_NumberingType( nNumType ) ); + if ( !aFormat.isEmpty() ) + pAttr->add( FSNS( XML_w, XML_fmt ), aFormat.getStr() ); + + XFastAttributeListRef xAttrs( pAttr ); + m_pSerializer->singleElementNS( XML_w, XML_pgNumType, xAttrs ); + + // see 2.6.12 pgNumType (Page Numbering Settings) + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::SectionPageNumbering()" ); +} + +void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode ) +{ + /* break code: 0 No break, 1 New column + 2 New page, 3 Even page, 4 Odd page + */ + const char* pType; + switch ( nBreakCode ) + { + case 1: pType = "nextColumn"; break; + case 2: pType = "nextPage"; break; + case 3: pType = "evenPage"; break; + case 4: pType = "oddPage"; break; + default: pType = "continuous"; break; + } + + m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), pType); +} + +void DocxAttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA ) +{ + switch( nVA ) + { + case drawing::TextVerticalAdjust_CENTER: + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center"); + break; + case drawing::TextVerticalAdjust_BOTTOM: + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom"); + break; + case drawing::TextVerticalAdjust_BLOCK: //justify + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "both"); + break; + default: + break; + } +} + +void DocxAttributeOutput::StartFont( const OUString& rFamilyName ) const +{ + m_pSerializer->startElementNS(XML_w, XML_font, FSNS(XML_w, XML_name), rFamilyName.toUtf8()); +} + +void DocxAttributeOutput::EndFont() const +{ + m_pSerializer->endElementNS( XML_w, XML_font ); +} + +void DocxAttributeOutput::FontAlternateName( const OUString& rName ) const +{ + m_pSerializer->singleElementNS(XML_w, XML_altName, FSNS(XML_w, XML_val), rName.toUtf8()); +} + +void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const +{ + FastAttributeList* pAttr = FastSerializerHelper::createAttrList(); + + OString aCharSet( OString::number( nCharSet, 16 ) ); + if ( aCharSet.getLength() == 1 ) + aCharSet = "0" + aCharSet; + pAttr->add( FSNS( XML_w, XML_val ), aCharSet.getStr()); + + if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT ) + { + if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding )) + pAttr->add( FSNS( XML_w, XML_characterSet ), charset ); + } + + m_pSerializer->singleElementNS( XML_w, XML_charset, XFastAttributeListRef( pAttr )); +} + +void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const +{ + const char* pFamily; + switch ( eFamily ) + { + case FAMILY_ROMAN: pFamily = "roman"; break; + case FAMILY_SWISS: pFamily = "swiss"; break; + case FAMILY_MODERN: pFamily = "modern"; break; + case FAMILY_SCRIPT: pFamily = "script"; break; + case FAMILY_DECORATIVE: pFamily = "decorative"; break; + default: pFamily = "auto"; break; // no font family + } + + m_pSerializer->singleElementNS(XML_w, XML_family, FSNS(XML_w, XML_val), pFamily); +} + +void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const +{ + const char* pPitch; + switch ( ePitch ) + { + case PITCH_VARIABLE: pPitch = "variable"; break; + case PITCH_FIXED: pPitch = "fixed"; break; + default: pPitch = "default"; break; // no info about the pitch + } + + m_pSerializer->singleElementNS(XML_w, XML_pitch, FSNS(XML_w, XML_val), pPitch); +} + +void DocxAttributeOutput::EmbedFont( const OUString& name, FontFamily family, FontPitch pitch ) +{ + if( !m_rExport.m_pDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) + return; // no font embedding with this document + EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch ); + EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch ); + EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch ); + EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch ); +} + +static char toHexChar( int value ) +{ + return value >= 10 ? value + 'A' - 10 : value + '0'; +} + +void DocxAttributeOutput::EmbedFontStyle( const OUString& name, int tag, FontFamily family, FontItalic italic, + FontWeight weight, FontPitch pitch ) +{ + // Embed font if at least viewing is allowed (in which case the opening app must check + // the font license rights too and open either read-only or not use the font for editing). + OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch, + EmbeddedFontsHelper::FontRights::ViewingAllowed ); + if( fontUrl.isEmpty()) + return; + // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS + if( !fontFilesMap.count( fontUrl )) + { + osl::File file( fontUrl ); + if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None ) + return; + uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream( + "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf", + "application/vnd.openxmlformats-officedocument.obfuscatedFont" ); + // Not much point in trying hard with the obfuscation key, whoever reads the spec can read the font anyway, + // so just alter the first and last part of the key. + char fontKeyStr[] = "{00014A78-CABC-4EF0-12AC-5CD89AEFDE00}"; + sal_uInt8 fontKey[ 16 ] = { 0, 0xDE, 0xEF, 0x9A, 0xD8, 0x5C, 0xAC, 0x12, 0xF0, 0x4E, + 0xBC, 0xCA, 0x78, 0x4A, 0x01, 0 }; + fontKey[ 0 ] = fontKey[ 15 ] = m_nextFontId % 256; + fontKeyStr[ 1 ] = fontKeyStr[ 35 ] = toHexChar(( m_nextFontId % 256 ) / 16 ); + fontKeyStr[ 2 ] = fontKeyStr[ 36 ] = toHexChar(( m_nextFontId % 256 ) % 16 ); + unsigned char buffer[ 4096 ]; + sal_uInt64 readSize; + file.read( buffer, 32, readSize ); + if( readSize < 32 ) + { + SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" ); + xOutStream->closeOutput(); + return; + } + for( int i = 0; + i < 16; + ++i ) + { + buffer[ i ] ^= fontKey[ i ]; + buffer[ i + 16 ] ^= fontKey[ i ]; + } + xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), 32 )); + for(;;) + { + sal_Bool eof; + if( file.isEndOfFile( &eof ) != osl::File::E_None ) + { + SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); + xOutStream->closeOutput(); + return; + } + if( eof ) + break; + if( file.read( buffer, 4096, readSize ) != osl::File::E_None ) + { + SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl ); + xOutStream->closeOutput(); + return; + } + if( readSize == 0 ) + break; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize )); + } + xOutStream->closeOutput(); + OString relId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(), + oox::getRelationship(Relationship::FONT), + "fonts/font" + OUString::number( m_nextFontId ) + ".odttf" ), RTL_TEXTENCODING_UTF8 ); + EmbeddedFontRef ref; + ref.relId = relId; + ref.fontKey = fontKeyStr; + fontFilesMap[ fontUrl ] = ref; + ++m_nextFontId; + } + m_pSerializer->singleElementNS( XML_w, tag, + FSNS( XML_r, XML_id ), fontFilesMap[ fontUrl ].relId, + FSNS( XML_w, XML_fontKey ), fontFilesMap[ fontUrl ].fontKey ); +} + +OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco ) +{ + switch (nIco) + { + case 1: return "black"; break; + case 2: return "blue"; break; + case 3: return "cyan"; break; + case 4: return "green"; break; + case 5: return "magenta"; break; + case 6: return "red"; break; + case 7: return "yellow"; break; + case 8: return "white"; break; + case 9: return "darkBlue"; break; + case 10: return "darkCyan"; break; + case 11: return "darkGreen"; break; + case 12: return "darkMagenta"; break; + case 13: return "darkRed"; break; + case 14: return "darkYellow"; break; + case 15: return "darkGray"; break; + case 16: return "lightGray"; break; + default: return OString(); break; + } +} + +void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) +{ + // nId is the same both for abstract numbering definition as well as the + // numbering definition itself + // TODO check that this is actually true & fix if not ;-) + OString aId( OString::number( nId ) ); + + m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), aId); + + m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), aId); + +#if OSL_DEBUG_LEVEL > 1 + // TODO ww8 version writes this, anything to do about it here? + if ( rRule.IsContinusNum() ) + SAL_INFO("sw", "TODO DocxAttributeOutput::NumberingDefinition()" ); +#else + (void) rRule; // to quiet the warning... +#endif + + m_pSerializer->endElementNS( XML_w, XML_num ); +} + +// Not all attributes of SwNumFormat are important for export, so can't just use embedded in +// that classes comparison. +static bool lcl_ListLevelsAreDifferentForExport(const SwNumFormat & rFormat1, const SwNumFormat & rFormat2) +{ + if (rFormat1 == rFormat2) + // They are equal, nothing to do + return false; + + if (!rFormat1.GetCharFormat() != !rFormat2.GetCharFormat()) + // One has charformat, other not. they are different + return true; + + if (rFormat1.GetCharFormat() && rFormat2.GetCharFormat()) + { + const SwAttrSet & a1 = rFormat1.GetCharFormat()->GetAttrSet(); + const SwAttrSet & a2 = rFormat2.GetCharFormat()->GetAttrSet(); + + if (!(a1 == a2)) + // Difference in charformat: they are different + return true; + } + + // Compare numformats with empty charformats + SwNumFormat modified1 = rFormat1; + SwNumFormat modified2 = rFormat2; + modified1.SetCharFormatName(OUString()); + modified2.SetCharFormatName(OUString()); + modified1.SetCharFormat(nullptr); + modified2.SetCharFormat(nullptr); + return modified1 != modified2; +} + +void DocxAttributeOutput::OverrideNumberingDefinition( + SwNumRule const& rRule, + sal_uInt16 const nNum, sal_uInt16 const nAbstractNum, const std::map< size_t, size_t > & rLevelOverrides ) +{ + m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), OString::number(nNum)); + + m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), OString::number(nAbstractNum)); + + SwNumRule const& rAbstractRule = *(*m_rExport.m_pUsedNumTable)[nAbstractNum - 1]; + sal_uInt8 const nLevels = static_cast<sal_uInt8>(rRule.IsContinusNum() + ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel); + for (sal_uInt8 nLevel = 0; nLevel < nLevels; ++nLevel) + { + const auto levelOverride = rLevelOverrides.find(nLevel); + bool bListsAreDifferent = lcl_ListLevelsAreDifferentForExport(rRule.Get(nLevel), rAbstractRule.Get(nLevel)); + + // Export list override only if it is different to abstract one + // or we have a level numbering override + if (bListsAreDifferent || levelOverride != rLevelOverrides.end()) + { + m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, XML_ilvl), OString::number(nLevel)); + + if (bListsAreDifferent) + { + GetExport().NumberingLevel(rRule, nLevel); + } + if (levelOverride != rLevelOverrides.end()) + { + // list numbering restart override + m_pSerializer->singleElementNS(XML_w, XML_startOverride, + FSNS(XML_w, XML_val), OString::number(levelOverride->second)); + } + + m_pSerializer->endElementNS(XML_w, XML_lvlOverride); + } + } + + m_pSerializer->endElementNS( XML_w, XML_num ); +} + +void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId ) +{ + const SwNumRule* pRule = (*m_rExport.m_pUsedNumTable)[nId - 1]; + m_bExportingOutline = pRule && pRule->IsOutlineRule(); + m_pSerializer->startElementNS( XML_w, XML_abstractNum, + FSNS( XML_w, XML_abstractNumId ), OString::number(nId) ); +} + +void DocxAttributeOutput::EndAbstractNumbering() +{ + m_pSerializer->endElementNS( XML_w, XML_abstractNum ); +} + +void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel, + sal_uInt16 nStart, + sal_uInt16 nNumberingType, + SvxAdjust eAdjust, + const sal_uInt8 * /*pNumLvlPos*/, + sal_uInt8 nFollow, + const wwFont *pFont, + const SfxItemSet *pOutSet, + sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, + sal_Int16 nListTabPos, + const OUString &rNumberingString, + const SvxBrushItem* pBrush) +{ + m_pSerializer->startElementNS(XML_w, XML_lvl, FSNS(XML_w, XML_ilvl), OString::number(nLevel)); + + // start with the nStart value. Do not write w:start if Numbered Lists + // starts from zero.As it's an optional parameter. + // refer ECMA 376 Second edition Part-1 + if(!(0 == nLevel && 0 == nStart)) + { + m_pSerializer->singleElementNS( XML_w, XML_start, + FSNS( XML_w, XML_val ), OString::number(nStart) ); + } + + if (m_bExportingOutline) + { + sal_uInt16 nId = m_rExport.m_pStyles->GetHeadingParagraphStyleId( nLevel ); + if ( nId != SAL_MAX_UINT16 ) + m_pSerializer->singleElementNS( XML_w, XML_pStyle , + FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nId) ); + } + // format + OString aCustomFormat; + OString aFormat(impl_LevelNFC(nNumberingType, pOutSet, aCustomFormat)); + + if ( !aFormat.isEmpty() ) + { + if (aCustomFormat.isEmpty()) + { + m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat); + } + else + { + m_pSerializer->startElementNS(XML_mc, XML_AlternateContent); + m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "w14"); + + m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat, + FSNS(XML_w, XML_format), aCustomFormat); + + m_pSerializer->endElementNS(XML_mc, XML_Choice); + m_pSerializer->startElementNS(XML_mc, XML_Fallback); + m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), "decimal"); + m_pSerializer->endElementNS(XML_mc, XML_Fallback); + m_pSerializer->endElementNS(XML_mc, XML_AlternateContent); + } + } + + // suffix + const char *pSuffix = nullptr; + switch ( nFollow ) + { + case 1: pSuffix = "space"; break; + case 2: pSuffix = "nothing"; break; + default: /*pSuffix = "tab";*/ break; + } + if ( pSuffix ) + m_pSerializer->singleElementNS(XML_w, XML_suff, FSNS(XML_w, XML_val), pSuffix); + + // text + OUStringBuffer aBuffer( rNumberingString.getLength() + WW8ListManager::nMaxLevel ); + + const sal_Unicode *pPrev = rNumberingString.getStr(); + const sal_Unicode *pIt = rNumberingString.getStr(); + while ( pIt < rNumberingString.getStr() + rNumberingString.getLength() ) + { + // convert the level values to %NUMBER form + // (we don't use pNumLvlPos at all) + // FIXME so far we support the ww8 limit of levels only + if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) ) + { + aBuffer.append( pPrev, pIt - pPrev ); + aBuffer.append( '%' ); + aBuffer.append( OUString::number( sal_Int32( *pIt ) + 1 ) ); + + pPrev = pIt + 1; + } + ++pIt; + } + if ( pPrev < pIt ) + aBuffer.append( pPrev, pIt - pPrev ); + + // If bullet char is empty, set lvlText as empty + if ( rNumberingString == OUStringChar('\0') && nNumberingType == SVX_NUM_CHAR_SPECIAL ) + { + m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), ""); + } + else + { + // Writer's "zero width space" suffix is necessary, so that LabelFollowedBy shows up, but Word doesn't require that. + OUString aLevelText = aBuffer.makeStringAndClear(); + static OUString aZeroWidthSpace(u'\x200B'); + if (aLevelText == aZeroWidthSpace) + aLevelText.clear(); + m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), aLevelText.toUtf8()); + } + + // bullet + if (nNumberingType == SVX_NUM_BITMAP && pBrush) + { + int nIndex = m_rExport.GetGrfIndex(*pBrush); + if (nIndex != -1) + { + m_pSerializer->singleElementNS(XML_w, XML_lvlPicBulletId, + FSNS(XML_w, XML_val), OString::number(nIndex)); + } + } + + // justification + const char *pJc; + bool ecmaDialect = ( m_rExport.GetFilter().getVersion() == oox::core::ECMA_DIALECT ); + switch ( eAdjust ) + { + case SvxAdjust::Center: pJc = "center"; break; + case SvxAdjust::Right: pJc = !ecmaDialect ? "end" : "right"; break; + default: pJc = !ecmaDialect ? "start" : "left"; break; + } + m_pSerializer->singleElementNS(XML_w, XML_lvlJc, FSNS(XML_w, XML_val), pJc); + + // indentation + m_pSerializer->startElementNS(XML_w, XML_pPr); + if( nListTabPos >= 0 ) + { + m_pSerializer->startElementNS(XML_w, XML_tabs); + m_pSerializer->singleElementNS( XML_w, XML_tab, + FSNS( XML_w, XML_val ), "num", + FSNS( XML_w, XML_pos ), OString::number(nListTabPos) ); + m_pSerializer->endElementNS( XML_w, XML_tabs ); + } + + sal_Int32 nToken = ecmaDialect ? XML_left : XML_start; + sal_Int32 nIndentToken = nFirstLineIndex > 0 ? XML_firstLine : XML_hanging; + m_pSerializer->singleElementNS( XML_w, XML_ind, + FSNS( XML_w, nToken ), OString::number(nIndentAt), + FSNS( XML_w, nIndentToken ), OString::number(abs(nFirstLineIndex)) ); + m_pSerializer->endElementNS( XML_w, XML_pPr ); + + // font + if ( pOutSet ) + { + m_pSerializer->startElementNS(XML_w, XML_rPr); + + if ( pFont ) + { + GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml + OString aFamilyName( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); + m_pSerializer->singleElementNS( XML_w, XML_rFonts, + FSNS( XML_w, XML_ascii ), aFamilyName, + FSNS( XML_w, XML_hAnsi ), aFamilyName, + FSNS( XML_w, XML_cs ), aFamilyName, + FSNS( XML_w, XML_hint ), "default" ); + } + else + { + m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF); + } + + WriteCollectedRunProperties(); + + m_pSerializer->endElementNS( XML_w, XML_rPr ); + } + + // TODO anything to do about nListTabPos? + + m_pSerializer->endElementNS( XML_w, XML_lvl ); +} + +void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap ) +{ + switch ( rCaseMap.GetValue() ) + { + case SvxCaseMap::SmallCaps: + m_pSerializer->singleElementNS(XML_w, XML_smallCaps); + break; + case SvxCaseMap::Uppercase: + m_pSerializer->singleElementNS(XML_w, XML_caps); + break; + default: // Something that ooxml does not support + m_pSerializer->singleElementNS(XML_w, XML_smallCaps, FSNS(XML_w, XML_val), "false"); + m_pSerializer->singleElementNS(XML_w, XML_caps, FSNS(XML_w, XML_val), "false"); + break; + } +} + +void DocxAttributeOutput::CharColor( const SvxColorItem& rColor ) +{ + const Color aColor( rColor.GetValue() ); + OString aColorString = msfilter::util::ConvertColor( aColor ); + + const char* pExistingValue(nullptr); + if (m_pColorAttrList.is() && m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pExistingValue)) + { + assert(aColorString.equalsL(pExistingValue, rtl_str_getLength(pExistingValue))); + return; + } + + AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_val ), aColorString.getStr() ); + m_nCharTransparence = aColor.GetTransparency(); +} + +void DocxAttributeOutput::CharContour( const SvxContourItem& rContour ) +{ + if ( rContour.GetValue() ) + m_pSerializer->singleElementNS(XML_w, XML_outline); + else + m_pSerializer->singleElementNS(XML_w, XML_outline, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut ) +{ + switch ( rCrossedOut.GetStrikeout() ) + { + case STRIKEOUT_DOUBLE: + m_pSerializer->singleElementNS(XML_w, XML_dstrike); + break; + case STRIKEOUT_NONE: + m_pSerializer->singleElementNS(XML_w, XML_dstrike, FSNS(XML_w, XML_val), "false"); + m_pSerializer->singleElementNS(XML_w, XML_strike, FSNS(XML_w, XML_val), "false"); + break; + default: + m_pSerializer->singleElementNS(XML_w, XML_strike); + break; + } +} + +void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement ) +{ + OString sIss; + short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight(); + + // Simplify styles to avoid impossible complexity. Import and export as defaults only + if ( m_rExport.m_bStyDef && nEsc ) + { + nProp = DFLT_ESC_PROP; + nEsc = (nEsc > 0) ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB; + } + + if ( !nEsc ) + { + sIss = OString( "baseline" ); + nEsc = 0; + nProp = 100; + } + else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 ) + { + if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc ) + sIss = OString( "subscript" ); + else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc ) + sIss = OString( "superscript" ); + } + else if ( DFLT_ESC_AUTO_SUPER == nEsc ) + { + // Raised by the differences between the ascenders (ascent = baseline to top of highest letter). + // The ascent is generally about 80% of the total font height. + // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER) + nEsc = .8 * (100 - nProp); + } + else if ( DFLT_ESC_AUTO_SUB == nEsc ) + { + // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter). + // The descent is generally about 20% of the total font height. + // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB) + nEsc = .2 * -(100 - nProp); + } + + if ( !sIss.isEmpty() ) + m_pSerializer->singleElementNS(XML_w, XML_vertAlign, FSNS(XML_w, XML_val), sIss); + + const SvxFontHeightItem& rItem = m_rExport.GetItem(RES_CHRATR_FONTSIZE); + if (sIss.isEmpty() || sIss.match("baseline")) + { + float fHeight = rItem.GetHeight(); + OString sPos = OString::number( round(( fHeight * nEsc ) / 1000) ); + m_pSerializer->singleElementNS(XML_w, XML_position, FSNS(XML_w, XML_val), sPos); + + if( ( 100 != nProp || sIss.match( "baseline" ) ) && !m_rExport.m_bFontSizeWritten ) + { + OString sSize = OString::number( round(( fHeight * nProp ) / 1000) ); + m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), sSize); + } + } +} + +void DocxAttributeOutput::CharFont( const SvxFontItem& rFont) +{ + GetExport().GetId( rFont ); // ensure font info is written to fontTable.xml + const OUString& sFontName(rFont.GetFamilyName()); + const OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); + if (!sFontNameUtf8.isEmpty()) + { + if (m_pFontsAttrList && + ( m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_ascii )) || + m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_hAnsi )) ) + ) + { + // tdf#38778: do to fields output into DOC the font could be added before and after field declaration + // that all sub runs of the field will have correct font inside. + // For DOCX we should do not add the same font information twice in the same node + return; + } + + AddToAttrList( m_pFontsAttrList, 2, + FSNS( XML_w, XML_ascii ), sFontNameUtf8.getStr(), + FSNS( XML_w, XML_hAnsi ), sFontNameUtf8.getStr() ); + } +} + +void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize) +{ + OString fontSize = OString::number( ( rFontSize.GetHeight() + 5 ) / 10 ); + + switch ( rFontSize.Which() ) + { + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_CJK_FONTSIZE: + m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), fontSize); + break; + case RES_CHRATR_CTL_FONTSIZE: + m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), fontSize); + break; + } +} + +void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning ) +{ + OString aKerning = OString::number( rKerning.GetValue() ); + m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning); +} + +void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage ) +{ + OString aLanguageCode( OUStringToOString( + LanguageTag( rLanguage.GetLanguage()).getBcp47MS(), + RTL_TEXTENCODING_UTF8)); + + switch ( rLanguage.Which() ) + { + case RES_CHRATR_LANGUAGE: + AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_val ), aLanguageCode.getStr() ); + break; + case RES_CHRATR_CJK_LANGUAGE: + AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_eastAsia ), aLanguageCode.getStr() ); + break; + case RES_CHRATR_CTL_LANGUAGE: + AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_bidi ), aLanguageCode.getStr() ); + break; + } +} + +void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture ) +{ + if ( rPosture.GetPosture() != ITALIC_NONE ) + m_pSerializer->singleElementNS(XML_w, XML_i); + else + m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow ) +{ + if ( rShadow.GetValue() ) + m_pSerializer->singleElementNS(XML_w, XML_shadow); + else + m_pSerializer->singleElementNS(XML_w, XML_shadow, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline ) +{ + const char *pUnderlineValue; + + switch ( rUnderline.GetLineStyle() ) + { + case LINESTYLE_SINGLE: pUnderlineValue = "single"; break; + case LINESTYLE_BOLD: pUnderlineValue = "thick"; break; + case LINESTYLE_DOUBLE: pUnderlineValue = "double"; break; + case LINESTYLE_DOTTED: pUnderlineValue = "dotted"; break; + case LINESTYLE_DASH: pUnderlineValue = "dash"; break; + case LINESTYLE_DASHDOT: pUnderlineValue = "dotDash"; break; + case LINESTYLE_DASHDOTDOT: pUnderlineValue = "dotDotDash"; break; + case LINESTYLE_WAVE: pUnderlineValue = "wave"; break; + case LINESTYLE_BOLDDOTTED: pUnderlineValue = "dottedHeavy"; break; + case LINESTYLE_BOLDDASH: pUnderlineValue = "dashedHeavy"; break; + case LINESTYLE_LONGDASH: pUnderlineValue = "dashLongHeavy"; break; + case LINESTYLE_BOLDLONGDASH: pUnderlineValue = "dashLongHeavy"; break; + case LINESTYLE_BOLDDASHDOT: pUnderlineValue = "dashDotHeavy"; break; + case LINESTYLE_BOLDDASHDOTDOT: pUnderlineValue = "dashDotDotHeavy"; break; + case LINESTYLE_BOLDWAVE: pUnderlineValue = "wavyHeavy"; break; + case LINESTYLE_DOUBLEWAVE: pUnderlineValue = "wavyDouble"; break; + case LINESTYLE_NONE: // fall through + default: pUnderlineValue = "none"; break; + } + + Color aUnderlineColor = rUnderline.GetColor(); + bool bUnderlineHasColor = aUnderlineColor.GetTransparency() == 0; + if (bUnderlineHasColor) + { + // Underline has a color + m_pSerializer->singleElementNS( XML_w, XML_u, + FSNS( XML_w, XML_val ), pUnderlineValue, + FSNS( XML_w, XML_color ), msfilter::util::ConvertColor(aUnderlineColor) ); + } + else + { + // Underline has no color + m_pSerializer->singleElementNS(XML_w, XML_u, FSNS(XML_w, XML_val), pUnderlineValue); + } +} + +void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight ) +{ + if ( rWeight.GetWeight() == WEIGHT_BOLD ) + m_pSerializer->singleElementNS(XML_w, XML_b); + else + m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern ) +{ + // auto kerning is bound to a minimum font size in Word - but is just a boolean in Writer :-( + // kerning is based on half-point sizes, so 2 enables kerning for fontsize 1pt or higher. (1 is treated as size 12, and 0 is treated as disabled.) + const OString sFontSize = OString::number( static_cast<sal_uInt32>(rAutoKern.GetValue()) * 2 ); + m_pSerializer->singleElementNS(XML_w, XML_kern, FSNS(XML_w, XML_val), sFontSize); +} + +void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink ) +{ + if ( rBlink.GetValue() ) + m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "blinkBackground"); + else + m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "none"); +} + +#define MSWORD_CH_SHADING_FILL "FFFFFF" // The attribute w:fill of w:shd, for MS-Word's character shading, +#define MSWORD_CH_SHADING_COLOR "auto" // The attribute w:color of w:shd, for MS-Word's character shading, +#define MSWORD_CH_SHADING_VAL "pct15" // The attribute w:value of w:shd, for MS-Word's character shading, + +void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush ) +{ + // Check if the brush shading pattern is 'PCT15'. If so - write it back to the DOCX + if (rBrush.GetShadingValue() == ShadingPattern::PCT15) + { + m_pSerializer->singleElementNS( XML_w, XML_shd, + FSNS( XML_w, XML_val ), MSWORD_CH_SHADING_VAL, + FSNS( XML_w, XML_color ), MSWORD_CH_SHADING_COLOR, + FSNS( XML_w, XML_fill ), MSWORD_CH_SHADING_FILL ); + } + else + { + m_pSerializer->singleElementNS( XML_w, XML_shd, + FSNS( XML_w, XML_fill ), msfilter::util::ConvertColor(rBrush.GetColor()), + FSNS( XML_w, XML_val ), "clear" ); + } +} + +void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont ) +{ + if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_eastAsia))) + { + // tdf#38778: do to fields output into DOC the font could be added before and after field declaration + // that all sub runs of the field will have correct font inside. + // For DOCX we should do not add the same font information twice in the same node + return; + } + + const OUString& sFontName(rFont.GetFamilyName()); + OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsia ), sFontNameUtf8.getStr() ); +} + +void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture ) +{ + if ( rPosture.GetPosture() != ITALIC_NONE ) + m_pSerializer->singleElementNS(XML_w, XML_i); + else + m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight ) +{ + if ( rWeight.GetWeight() == WEIGHT_BOLD ) + m_pSerializer->singleElementNS(XML_w, XML_b); + else + m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont ) +{ + if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_cs))) + { + // tdf#38778: do to fields output into DOC the font could be added before and after field declaration + // that all sub runs of the field will have correct font inside. + // For DOCX we should do not add the same font information twice in the same node + return; + } + + const OUString& sFontName(rFont.GetFamilyName()); + OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8); + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cs ), sFontNameUtf8.getStr() ); +} + +void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture) +{ + if ( rPosture.GetPosture() != ITALIC_NONE ) + m_pSerializer->singleElementNS(XML_w, XML_iCs); + else + m_pSerializer->singleElementNS(XML_w, XML_iCs, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight ) +{ + if ( rWeight.GetWeight() == WEIGHT_BOLD ) + m_pSerializer->singleElementNS(XML_w, XML_bCs); + else + m_pSerializer->singleElementNS(XML_w, XML_bCs, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharBidiRTL( const SfxPoolItem& ) +{ +} + +void DocxAttributeOutput::CharIdctHint( const SfxPoolItem& ) +{ +} + +void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate) +{ + // Not rotated? + if ( !rRotate.GetValue()) + return; + + AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vert ), "true" ); + + if (rRotate.IsFitToLine()) + AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vertCompress ), "true" ); +} + +void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark ) +{ + const char *pEmphasis; + const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark(); + + if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove)) + pEmphasis = "dot"; + else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove)) + pEmphasis = "comma"; + else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove)) + pEmphasis = "circle"; + else if (v == (FontEmphasisMark::Dot|FontEmphasisMark::PosBelow)) + pEmphasis = "underDot"; + else + pEmphasis = "none"; + + m_pSerializer->singleElementNS(XML_w, XML_em, FSNS(XML_w, XML_val), pEmphasis); +} + +void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines ) +{ + if ( !rTwoLines.GetValue() ) + return; + + AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combine ), "true" ); + + sal_Unicode cStart = rTwoLines.GetStartBracket(); + sal_Unicode cEnd = rTwoLines.GetEndBracket(); + + if (!cStart && !cEnd) + return; + + OString sBracket; + if ((cStart == '{') || (cEnd == '}')) + sBracket = const_cast<char *>("curly"); + else if ((cStart == '<') || (cEnd == '>')) + sBracket = const_cast<char *>("angle"); + else if ((cStart == '[') || (cEnd == ']')) + sBracket = const_cast<char *>("square"); + else + sBracket = const_cast<char *>("round"); + AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combineBrackets ), sBracket.getStr() ); +} + +void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth ) +{ + // Clamp CharScaleWidth to OOXML limits ([1..600]) + const sal_Int16 nScaleWidth( std::max<sal_Int16>( 1, + std::min<sal_Int16>( rScaleWidth.GetValue(), 600 ) ) ); + m_pSerializer->singleElementNS( XML_w, XML_w, + FSNS( XML_w, XML_val ), OString::number(nScaleWidth) ); +} + +void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief ) +{ + switch ( rRelief.GetValue() ) + { + case FontRelief::Embossed: + m_pSerializer->singleElementNS(XML_w, XML_emboss); + break; + case FontRelief::Engraved: + m_pSerializer->singleElementNS(XML_w, XML_imprint); + break; + default: + m_pSerializer->singleElementNS(XML_w, XML_emboss, FSNS(XML_w, XML_val), "false"); + m_pSerializer->singleElementNS(XML_w, XML_imprint, FSNS(XML_w, XML_val), "false"); + break; + } +} + +void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden ) +{ + if ( rHidden.GetValue() ) + m_pSerializer->singleElementNS(XML_w, XML_vanish); + else + m_pSerializer->singleElementNS(XML_w, XML_vanish, FSNS(XML_w, XML_val), "false"); +} + +void DocxAttributeOutput::CharBorder( + const SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) +{ + css::table::BorderLine2 rStyleBorder; + const SvxBoxItem* pInherited = nullptr; + if ( GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) + pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_CHRATR_BOX); + else if ( m_rExport.m_pChpIter ) // incredibly undocumented, but this is the character-style info, right? + pInherited = static_cast<const SvxBoxItem*>(GetExport().m_pChpIter->HasTextItem(RES_CHRATR_BOX)); + + if ( pInherited ) + rStyleBorder = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false); + + impl_borderLine( m_pSerializer, XML_bdr, pAllBorder, nDist, bShadow, &rStyleBorder ); +} + +void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight ) +{ + const OString sColor = TransHighlightColor( msfilter::util::TransColToIco(rHighlight.GetColor()) ); + if ( !sColor.isEmpty() ) + { + m_pSerializer->singleElementNS(XML_w, XML_highlight, FSNS(XML_w, XML_val), sColor); + } +} + +void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink ) +{ + OString aStyleId = MSWordStyles::CreateStyleId(rLink.GetINetFormat()); + if (!aStyleId.isEmpty() && !aStyleId.equalsIgnoreAsciiCase("DefaultStyle")) + m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); +} + +void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat ) +{ + OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(rCharFormat.GetCharFormat()))); + + m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); +} + +void DocxAttributeOutput::RefField( const SwField& rField, const OUString& rRef ) +{ + SwFieldIds nType = rField.GetTyp( )->Which( ); + if ( nType == SwFieldIds::GetExp ) + { + OUString sCmd = FieldString( ww::eREF ) + + "\"" + rRef + "\" "; + + m_rExport.OutputField( &rField, ww::eREF, sCmd ); + } + + // There is nothing to do here for the set fields +} + +void DocxAttributeOutput::HiddenField( const SwField& /*rField*/ ) +{ + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::HiddenField()" ); +} + +void DocxAttributeOutput::PostitField( const SwField* pField ) +{ + assert( dynamic_cast< const SwPostItField* >( pField )); + const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField); + OString aName = OUStringToOString(pPostItField->GetName(), RTL_TEXTENCODING_UTF8); + sal_Int32 nId = 0; + std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find(aName); + if (it != m_rOpenedAnnotationMarksIds.end()) + // If the postit field has an annotation mark associated, we already have an id. + nId = it->second; + else + // Otherwise get a new one. + nId = m_nNextAnnotationMarkId++; + m_postitFields.emplace_back(pPostItField, nId); +} + +void DocxAttributeOutput::WritePostitFieldReference() +{ + while( m_postitFieldsMaxId < m_postitFields.size()) + { + OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second); + + // In case this file is inside annotation marks, we want to write the + // comment reference after the annotation mark is closed, not here. + OString idname = OUStringToOString(m_postitFields[m_postitFieldsMaxId].first->GetName(), RTL_TEXTENCODING_UTF8); + std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find( idname ); + if ( it == m_rOpenedAnnotationMarksIds.end( ) ) + m_pSerializer->singleElementNS(XML_w, XML_commentReference, FSNS(XML_w, XML_id), idstr); + ++m_postitFieldsMaxId; + } +} + +void DocxAttributeOutput::WritePostitFields() +{ + for (const auto& rPair : m_postitFields) + { + OString idstr = OString::number( rPair.second); + const SwPostItField* f = rPair.first; + m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr, + FSNS( XML_w, XML_author ), f->GetPar1().toUtf8(), + FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()), + FSNS( XML_w, XML_initials ), f->GetInitials().toUtf8() ); + + if (f->GetTextObject() != nullptr) + { + // richtext + GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN); + } + else + { + // just plain text - eg. when the field was created via the + // .uno:InsertAnnotation API + m_pSerializer->startElementNS(XML_w, XML_p); + m_pSerializer->startElementNS(XML_w, XML_r); + RunText(f->GetText()); + m_pSerializer->endElementNS(XML_w, XML_r); + m_pSerializer->endElementNS(XML_w, XML_p); + } + + m_pSerializer->endElementNS( XML_w, XML_comment ); + } +} + +bool DocxAttributeOutput::DropdownField( const SwField* pField ) +{ + ww::eField eType = ww::eFORMDROPDOWN; + OUString sCmd = FieldString( eType ); + GetExport( ).OutputField( pField, eType, sCmd ); + + return false; +} + +bool DocxAttributeOutput::PlaceholderField( const SwField* pField ) +{ + assert( pendingPlaceholder == nullptr ); + pendingPlaceholder = pField; + return false; // do not expand +} + +void DocxAttributeOutput::WritePendingPlaceholder() +{ + if( pendingPlaceholder == nullptr ) + return; + const SwField* pField = pendingPlaceholder; + pendingPlaceholder = nullptr; + m_pSerializer->startElementNS(XML_w, XML_sdt); + m_pSerializer->startElementNS(XML_w, XML_sdtPr); + if( !pField->GetPar2().isEmpty()) + m_pSerializer->singleElementNS( XML_w, XML_alias, + FSNS( XML_w, XML_val ), pField->GetPar2().toUtf8() ); + m_pSerializer->singleElementNS(XML_w, XML_temporary); + m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); + m_pSerializer->singleElementNS(XML_w, XML_text); + m_pSerializer->endElementNS( XML_w, XML_sdtPr ); + m_pSerializer->startElementNS(XML_w, XML_sdtContent); + m_pSerializer->startElementNS(XML_w, XML_r); + RunText( pField->GetPar1()); + m_pSerializer->endElementNS( XML_w, XML_r ); + m_pSerializer->endElementNS( XML_w, XML_sdtContent ); + m_pSerializer->endElementNS( XML_w, XML_sdt ); +} + +void DocxAttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) +{ + // field bookmarks are handled in the EndRun method + GetExport().OutputField(&rField, eType, rCmd ); +} + +void DocxAttributeOutput::WriteExpand( const SwField* pField ) +{ + // Will be written in the next End Run + m_rExport.OutputField( pField, ww::eUNKNOWN, OUString() ); +} + +void DocxAttributeOutput::WriteField_Impl( const SwField* pField, ww::eField eType, const OUString& rFieldCmd, FieldFlags nMode ) +{ + if (m_bPreventDoubleFieldsHandling) + return; + + struct FieldInfos infos; + if (pField) + infos.pField = pField->CopyField(); + infos.sCmd = rFieldCmd; + infos.eType = eType; + infos.bClose = bool(FieldFlags::Close & nMode); + infos.bSep = bool(FieldFlags::CmdEnd & nMode); + infos.bOpen = bool(FieldFlags::Start & nMode); + m_Fields.push_back( infos ); + + if ( pField ) + { + SwFieldIds nType = pField->GetTyp( )->Which( ); + sal_uInt16 nSubType = pField->GetSubType(); + + // TODO Any other field types here ? + if ( ( nType == SwFieldIds::SetExp ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) ) + { + const SwSetExpField *pSet = static_cast<const SwSetExpField*>( pField ); + m_sFieldBkm = pSet->GetPar1( ); + } + else if ( nType == SwFieldIds::Dropdown ) + { + const SwDropDownField* pDropDown = static_cast<const SwDropDownField*>( pField ); + m_sFieldBkm = pDropDown->GetName( ); + } + } +} + +void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark ) +{ + if ( !m_Fields.empty() ) + m_Fields.begin()->pFieldmark = &rFieldmark; +} + +void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ) +{ + for ( const OUString & name : rStarts ) + { + if (name.startsWith("permission-for-group:") || + name.startsWith("permission-for-user:")) + { + m_rPermissionsStart.push_back(name); + } + else + { + m_rBookmarksStart.push_back(name); + } + } + rStarts.clear(); + + for ( const OUString & name : rEnds ) + { + if (name.startsWith("permission-for-group:") || + name.startsWith("permission-for-user:")) + { + m_rPermissionsEnd.push_back(name); + } + else + { + m_rBookmarksEnd.push_back(name); + } + } + rEnds.clear(); +} + +void DocxAttributeOutput::WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ) +{ + for ( const OUString & name : rStarts ) + { + if (name.startsWith("permission-for-group:") || + name.startsWith("permission-for-user:")) + { + m_rPermissionsStart.push_back(name); + } + else + { + m_rFinalBookmarksStart.push_back(name); + } + } + rStarts.clear(); + + for ( const OUString & name : rEnds ) + { + if (name.startsWith("permission-for-group:") || + name.startsWith("permission-for-user:")) + { + m_rPermissionsEnd.push_back(name); + } + else + { + m_rFinalBookmarksEnd.push_back(name); + } + } + rEnds.clear(); +} + +void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, + std::vector< OUString >& rEnds ) +{ + for ( const auto & rAnnotationName : rStarts ) + { + OString rName = OUStringToOString(rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( ); + m_rAnnotationMarksStart.push_back( rName ); + } + rStarts.clear(); + + for ( const auto & rAnnotationName : rEnds ) + { + OString rName = OUStringToOString( rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( ); + m_rAnnotationMarksEnd.push_back( rName ); + } + rEnds.clear(); +} + +void DocxAttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote ) +{ + const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()? + m_rExport.m_pDoc->GetEndNoteInfo(): m_rExport.m_pDoc->GetFootnoteInfo(); + + // footnote/endnote run properties + const SwCharFormat* pCharFormat = rInfo.GetAnchorCharFormat( *m_rExport.m_pDoc ); + + OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat))); + + m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId); + + // remember the footnote/endnote to + // 1) write the footnoteReference/endnoteReference in EndRunProperties() + // 2) be able to dump them all to footnotes.xml/endnotes.xml + if ( !rFootnote.IsEndNote() && m_rExport.m_pDoc->GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER ) + m_pFootnotesList->add( rFootnote ); + else + m_pEndnotesList->add( rFootnote ); +} + +void DocxAttributeOutput::FootnoteEndnoteReference() +{ + sal_Int32 nId; + const SwFormatFootnote *pFootnote = m_pFootnotesList->getCurrent( nId ); + sal_Int32 nToken = XML_footnoteReference; + + // both cannot be set at the same time - if they are, it's a bug + if ( !pFootnote ) + { + pFootnote = m_pEndnotesList->getCurrent( nId ); + nToken = XML_endnoteReference; + } + + if ( !pFootnote ) + return; + + // write it + if ( pFootnote->GetNumStr().isEmpty() ) + { + // autonumbered + m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_id), OString::number(nId)); + } + else + { + // not autonumbered + m_pSerializer->singleElementNS( XML_w, nToken, + FSNS( XML_w, XML_customMarkFollows ), "1", + FSNS( XML_w, XML_id ), OString::number(nId) ); + + RunText( pFootnote->GetNumStr() ); + } +} + +static void WriteFootnoteSeparatorHeight( + ::sax_fastparser::FSHelperPtr const& pSerializer, SwTwips const nHeight) +{ + // try to get the height by setting font size of the paragraph + if (nHeight != 0) + { + pSerializer->startElementNS(XML_w, XML_pPr); + pSerializer->startElementNS(XML_w, XML_rPr); + pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), + OString::number((nHeight + 5) / 10)); + pSerializer->endElementNS(XML_w, XML_rPr); + pSerializer->endElementNS(XML_w, XML_pPr); + } +} + +void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes ) +{ + const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector(); + + sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes; + sal_Int32 nItem = bFootnotes? XML_footnote: XML_endnote; + + m_pSerializer->startElementNS( XML_w, nBody, m_rExport.MainXmlNamespaces() ); + + sal_Int32 nIndex = 0; + + // separator + // note: can only be defined for the whole document, not per section + m_pSerializer->startElementNS( XML_w, nItem, + FSNS( XML_w, XML_id ), OString::number(nIndex++), + FSNS( XML_w, XML_type ), "separator" ); + m_pSerializer->startElementNS(XML_w, XML_p); + + bool bSeparator = true; + SwTwips nHeight(0); + if (bFootnotes) + { + const SwPageFootnoteInfo& rFootnoteInfo = m_rExport.m_pDoc->GetPageDesc(0).GetFootnoteInfo(); + // Request separator only if both width and thickness are non-zero. + bSeparator = rFootnoteInfo.GetLineStyle() != SvxBorderLineStyle::NONE + && rFootnoteInfo.GetLineWidth() > 0 + && double(rFootnoteInfo.GetWidth()) > 0; + nHeight = sw::FootnoteSeparatorHeight(rFootnoteInfo); + } + + WriteFootnoteSeparatorHeight(m_pSerializer, nHeight); + + m_pSerializer->startElementNS(XML_w, XML_r); + if (bSeparator) + m_pSerializer->singleElementNS(XML_w, XML_separator); + m_pSerializer->endElementNS( XML_w, XML_r ); + m_pSerializer->endElementNS( XML_w, XML_p ); + m_pSerializer->endElementNS( XML_w, nItem ); + + // separator + m_pSerializer->startElementNS( XML_w, nItem, + FSNS( XML_w, XML_id ), OString::number(nIndex++), + FSNS( XML_w, XML_type ), "continuationSeparator" ); + m_pSerializer->startElementNS(XML_w, XML_p); + + WriteFootnoteSeparatorHeight(m_pSerializer, nHeight); + + m_pSerializer->startElementNS(XML_w, XML_r); + if (bSeparator) + { + m_pSerializer->singleElementNS(XML_w, XML_continuationSeparator); + } + m_pSerializer->endElementNS( XML_w, XML_r ); + m_pSerializer->endElementNS( XML_w, XML_p ); + m_pSerializer->endElementNS( XML_w, nItem ); + + // if new special ones are added, update also WriteFootnoteEndnotePr() + + // footnotes/endnotes themselves + for ( const auto& rpItem : rVector ) + { + m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef; + m_footnoteCustomLabel = rpItem->GetNumStr(); + + m_pSerializer->startElementNS(XML_w, nItem, FSNS(XML_w, XML_id), OString::number(nIndex)); + + const SwNodeIndex* pIndex = rpItem->GetTextFootnote()->GetStartNode(); + m_rExport.WriteSpecialText( pIndex->GetIndex() + 1, + pIndex->GetNode().EndOfSectionIndex(), + bFootnotes? TXT_FTN: TXT_EDN ); + + m_pSerializer->endElementNS( XML_w, nItem ); + ++nIndex; + } + + m_pSerializer->endElementNS( XML_w, nBody ); + +} + +void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag, + const SwEndNoteInfo& info, int listtag ) +{ + fs->startElementNS(XML_w, tag); + const char* fmt = nullptr; + switch( info.m_aFormat.GetNumberingType()) + { + case SVX_NUM_CHARS_UPPER_LETTER_N: // fall through, map to upper letters + case SVX_NUM_CHARS_UPPER_LETTER: + fmt = "upperLetter"; + break; + case SVX_NUM_CHARS_LOWER_LETTER_N: // fall through, map to lower letters + case SVX_NUM_CHARS_LOWER_LETTER: + fmt = "lowerLetter"; + break; + case SVX_NUM_ROMAN_UPPER: + fmt = "upperRoman"; + break; + case SVX_NUM_ROMAN_LOWER: + fmt = "lowerRoman"; + break; + case SVX_NUM_ARABIC: + fmt = "decimal"; + break; + case SVX_NUM_NUMBER_NONE: + fmt = "none"; + break; + case SVX_NUM_CHAR_SPECIAL: + fmt = "bullet"; + break; + case SVX_NUM_SYMBOL_CHICAGO: + fmt = "chicago"; + break; + case SVX_NUM_ARABIC_ZERO: + fmt = "decimalZero"; + break; + case SVX_NUM_PAGEDESC: + case SVX_NUM_BITMAP: + default: + break; // no format + } + if( fmt != nullptr ) + fs->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), fmt); + if( info.m_nFootnoteOffset != 0 ) + fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ), + OString::number(info.m_nFootnoteOffset + 1) ); + + const SwFootnoteInfo* pFootnoteInfo = dynamic_cast<const SwFootnoteInfo*>(&info); + if( pFootnoteInfo ) + { + switch( pFootnoteInfo->m_eNum ) + { + case FTNNUM_PAGE: fmt = "eachPage"; break; + case FTNNUM_CHAPTER: fmt = "eachSect"; break; + default: fmt = nullptr; break; + } + if( fmt != nullptr ) + fs->singleElementNS(XML_w, XML_numRestart, FSNS(XML_w, XML_val), fmt); + } + + if( listtag != 0 ) // we are writing to settings.xml, write also special footnote/endnote list + { // there are currently only two hardcoded ones ( see FootnotesEndnotes()) + fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "0"); + fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "1"); + } + fs->endElementNS( XML_w, tag ); +} + +void DocxAttributeOutput::SectFootnoteEndnotePr() +{ + if( HasFootnotes()) + WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.m_pDoc->GetFootnoteInfo(), 0 ); + if( HasEndnotes()) + WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.m_pDoc->GetEndNoteInfo(), 0 ); +} + +void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti ) +{ + if ( nSpace < 0 ) + { + AddToAttrList( m_pParagraphSpacingAttrList, 2, + FSNS( XML_w, XML_lineRule ), "exact", + FSNS( XML_w, XML_line ), OString::number( -nSpace ).getStr() ); + } + else if( nSpace > 0 && nMulti ) + { + AddToAttrList( m_pParagraphSpacingAttrList, 2, + FSNS( XML_w, XML_lineRule ), "auto", + FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() ); + } + else + { + AddToAttrList( m_pParagraphSpacingAttrList, 2, + FSNS( XML_w, XML_lineRule ), "atLeast", + FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() ); + } +} + +void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust ) +{ + const char *pAdjustString; + + bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; + + const SfxItemSet* pItems = GetExport().GetCurItemSet(); + const SvxFrameDirectionItem* rFrameDir = pItems? + pItems->GetItem( RES_FRAMEDIR ) : nullptr; + + SvxFrameDirection nDir = SvxFrameDirection::Environment; + if( rFrameDir != nullptr ) + nDir = rFrameDir->GetValue(); + if ( nDir == SvxFrameDirection::Environment ) + nDir = GetExport( ).GetDefaultFrameDirection( ); + bool bRtl = ( nDir == SvxFrameDirection::Horizontal_RL_TB ); + + switch ( rAdjust.GetAdjust() ) + { + case SvxAdjust::Left: + if ( bEcma ) + { + if ( bRtl ) + pAdjustString = "right"; + else + pAdjustString = "left"; + } + else if ( bRtl ) + pAdjustString = "end"; + else + pAdjustString = "start"; + break; + case SvxAdjust::Right: + if ( bEcma ) + { + if ( bRtl ) + pAdjustString = "left"; + else + pAdjustString = "right"; + } + else if ( bRtl ) + pAdjustString = "start"; + else + pAdjustString = "end"; + break; + case SvxAdjust::BlockLine: + case SvxAdjust::Block: + if (rAdjust.GetLastBlock() == SvxAdjust::Block) + pAdjustString = "distribute"; + else + pAdjustString = "both"; + break; + case SvxAdjust::Center: + pAdjustString = "center"; + break; + default: + return; // not supported attribute + } + m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pAdjustString); +} + +void DocxAttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit ) +{ + if (rSplit.GetValue()) + m_pSerializer->singleElementNS(XML_w, XML_keepLines, FSNS(XML_w, XML_val), "false"); + else + m_pSerializer->singleElementNS(XML_w, XML_keepLines); +} + +void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows ) +{ + if (rWidows.GetValue()) + m_pSerializer->singleElementNS(XML_w, XML_widowControl); + else + m_pSerializer->singleElementNS(XML_w, XML_widowControl, FSNS(XML_w, XML_val), "false"); +} + +static void impl_WriteTabElement( FSHelperPtr const & pSerializer, + const SvxTabStop& rTab, long tabsOffset ) +{ + FastAttributeList *pTabElementAttrList = FastSerializerHelper::createAttrList(); + + switch (rTab.GetAdjustment()) + { + case SvxTabAdjust::Right: + pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "right" ) ); + break; + case SvxTabAdjust::Decimal: + pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "decimal" ) ); + break; + case SvxTabAdjust::Center: + pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "center" ) ); + break; + case SvxTabAdjust::Default: + case SvxTabAdjust::Left: + default: + pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "left" ) ); + break; + } + + // Write position according to used offset of the whole paragraph. + // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. + // But in ODT, zero position could be page margins or paragraph indent according to used settings. + // This is handled outside of this method and provided for us in tabsOffset parameter. + pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::number( rTab.GetTabPos() + tabsOffset ) ); + + sal_Unicode cFillChar = rTab.GetFill(); + + if ('.' == cFillChar ) + pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "dot" ) ); + else if ( '-' == cFillChar ) + pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "hyphen" ) ); + else if ( u'\x00B7' == cFillChar ) // middle dot + pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "middleDot" ) ); + else if ( '_' == cFillChar ) + pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "underscore" ) ); + else + pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "none" ) ); + + pSerializer->singleElementNS(XML_w, XML_tab, pTabElementAttrList); +} + +void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop ) +{ + const SvxTabStopItem* pInheritedTabs = nullptr; + if ( GetExport().m_pStyAttr ) + pInheritedTabs = GetExport().m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP); + else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) + pInheritedTabs = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP); + const sal_uInt16 nInheritedTabCount = pInheritedTabs ? pInheritedTabs->Count() : 0; + const sal_uInt16 nCount = rTabStop.Count(); + + // <w:tabs> must contain at least one <w:tab>, so don't write it empty + if ( !nCount && !nInheritedTabCount ) + return; + if( nCount == 1 && rTabStop[ 0 ].GetAdjustment() == SvxTabAdjust::Default ) + { + GetExport().setDefaultTabStop( rTabStop[ 0 ].GetTabPos()); + return; + } + + // do not output inherited tabs twice (inside styles and inside inline properties) + if ( nCount == nInheritedTabCount && nCount > 0 ) + { + if ( *pInheritedTabs == rTabStop ) + return; + } + + m_pSerializer->startElementNS(XML_w, XML_tabs); + + // Get offset for tabs + // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins. + // But in ODT, zero position could be page margins or paragraph indent according to used settings. + long tabsOffset = 0; + if (m_rExport.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT)) + tabsOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft(); + + // clear unused inherited tabs - otherwise the style will add them back in + sal_Int32 nCurrTab = 0; + for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i ) + { + while ( nCurrTab < nCount && rTabStop[nCurrTab] < pInheritedTabs->At(i) ) + ++nCurrTab; + + if ( nCurrTab == nCount || pInheritedTabs->At(i) < rTabStop[nCurrTab] ) + { + m_pSerializer->singleElementNS( XML_w, XML_tab, + FSNS( XML_w, XML_val ), "clear", + FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) ); + } + } + + for (sal_uInt16 i = 0; i < nCount; i++ ) + { + if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default ) + impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset ); + else + GetExport().setDefaultTabStop( rTabStop[i].GetTabPos()); + } + + m_pSerializer->endElementNS( XML_w, XML_tabs ); +} + +void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone ) +{ + m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens, + FSNS( XML_w, XML_val ), OString::boolean( !rHyphenZone.IsHyphen() ) ); +} + +void DocxAttributeOutput::ParaNumRule_Impl( const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) +{ + if ( USHRT_MAX != nNumId ) + { + const sal_Int32 nTableSize = m_rExport.m_pUsedNumTable ? m_rExport.m_pUsedNumTable->size() : 0; + const SwNumRule* pRule = nNumId > 0 && nNumId <= nTableSize ? (*m_rExport.m_pUsedNumTable)[nNumId-1] : nullptr; + const bool bOutlineRule = pRule && pRule->IsOutlineRule(); + + // Do not export outline rules (Chapter Numbering) as paragraph properties, only as style properties. + if ( !pTextNd || !bOutlineRule ) + { + m_pSerializer->startElementNS(XML_w, XML_numPr); + m_pSerializer->singleElementNS(XML_w, XML_ilvl, + FSNS(XML_w, XML_val), OString::number(nLvl)); + m_pSerializer->singleElementNS(XML_w, XML_numId, + FSNS(XML_w, XML_val), OString::number(nNumId)); + m_pSerializer->endElementNS( XML_w, XML_numPr ); + } + } +} + +void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace ) +{ + m_pSerializer->singleElementNS( XML_w, XML_autoSpaceDE, + FSNS( XML_w, XML_val ), OString::boolean( rScriptSpace.GetValue() ) ); +} + +void DocxAttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem ) +{ + m_pSerializer->singleElementNS( XML_w, XML_overflowPunct, + FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); +} + +void DocxAttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem ) +{ + m_pSerializer->singleElementNS( XML_w, XML_kinsoku, + FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); +} + +void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign ) +{ + const char *pAlignString; + + switch ( rAlign.GetValue() ) + { + case SvxParaVertAlignItem::Align::Baseline: + pAlignString = "baseline"; + break; + case SvxParaVertAlignItem::Align::Top: + pAlignString = "top"; + break; + case SvxParaVertAlignItem::Align::Center: + pAlignString = "center"; + break; + case SvxParaVertAlignItem::Align::Bottom: + pAlignString = "bottom"; + break; + case SvxParaVertAlignItem::Align::Automatic: + pAlignString = "auto"; + break; + default: + return; // not supported attribute + } + m_pSerializer->singleElementNS(XML_w, XML_textAlignment, FSNS(XML_w, XML_val), pAlignString); +} + +void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid ) +{ + m_pSerializer->singleElementNS( XML_w, XML_snapToGrid, + FSNS( XML_w, XML_val ), OString::boolean( rGrid.GetValue() ) ); +} + +void DocxAttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize ) +{ + if (m_rExport.SdrExporter().getTextFrameSyntax() && m_rExport.SdrExporter().getFlyFrameSize()) + { + const Size* pSize = m_rExport.SdrExporter().getFlyFrameSize(); + m_rExport.SdrExporter().getTextFrameStyle().append(";width:").append(double(pSize->Width()) / 20); + m_rExport.SdrExporter().getTextFrameStyle().append("pt;height:").append(double(pSize->Height()) / 20).append("pt"); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed ) + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), + FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ).getStr() ); + + if ( rSize.GetHeight() ) + { + OString sRule( "exact" ); + if ( rSize.GetHeightSizeType() == SwFrameSize::Minimum ) + sRule = OString( "atLeast" ); + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, + FSNS( XML_w, XML_hRule ), sRule.getStr(), + FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ).getStr() ); + } + } + else if ( m_rExport.m_bOutPageDescs ) + { + FastAttributeList *attrList = FastSerializerHelper::createAttrList( ); + if ( m_rExport.m_pCurrentPageDesc->GetLandscape( ) ) + attrList->add( FSNS( XML_w, XML_orient ), "landscape" ); + + attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) ); + attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) ); + + XFastAttributeListRef xAttrList( attrList ); + attrList = nullptr; + + m_pSerializer->singleElementNS( XML_w, XML_pgSz, xAttrList ); + } +} + +void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& ) +{ + SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatPaperBin()" ); +} + +void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace ) +{ + bool bEcma = m_rExport.GetFilter().getVersion( ) == oox::core::ECMA_DIALECT; + + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-left:").append(double(rLRSpace.GetLeft()) / 20).append("pt"); + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-right:").append(double(rLRSpace.GetRight()) / 20).append("pt"); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hSpace ), + OString::number( + ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ).getStr() ); + } + else if ( m_rExport.m_bOutPageDescs ) + { + m_pageMargins.nLeft = 0; + m_pageMargins.nRight = 0; + + if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rExport.HasItem( RES_BOX )) ) + { + m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true ); + m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true ); + } + + m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft()); + m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight()); + + AddToAttrList( m_pSectionSpacingAttrList, 2, + FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ).getStr(), + FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ).getStr() ); + } + else + { + FastAttributeList *pLRSpaceAttrList = FastSerializerHelper::createAttrList(); + if((0 != rLRSpace.GetTextLeft()) || (rLRSpace.IsExplicitZeroMarginValLeft())) + { + pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_left : XML_start ) ), OString::number( rLRSpace.GetTextLeft() ) ); + } + if((0 != rLRSpace.GetRight()) || (rLRSpace.IsExplicitZeroMarginValRight())) + { + pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_right : XML_end ) ), OString::number( rLRSpace.GetRight() ) ); + } + sal_Int32 nFirstLineAdjustment = rLRSpace.GetTextFirstLineOffset(); + if (nFirstLineAdjustment > 0) + pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::number( nFirstLineAdjustment ) ); + else + pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::number( - nFirstLineAdjustment ) ); + m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList ); + } +} + +void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace ) +{ + + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-top:").append(double(rULSpace.GetUpper()) / 20).append("pt"); + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-bottom:").append(double(rULSpace.GetLower()) / 20).append("pt"); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vSpace ), + OString::number( + ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ).getStr() ); + } + else if (m_rExport.m_bOutPageDescs ) + { + OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" ); + if ( !m_rExport.GetCurItemSet() ) + return; + + HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() ); + + sal_Int32 nHeader = 0; + if ( aDistances.HasHeader() ) + nHeader = sal_Int32( aDistances.dyaHdrTop ); + else if (m_rExport.m_pFirstPageFormat) + { + HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet()); + if (aFirstPageDistances.HasHeader()) + { + // The follow page style has no header, but the first page style has. In Word terms, + // this means that the header margin of "the" section is coming from the first page + // style. + nHeader = sal_Int32(aFirstPageDistances.dyaHdrTop); + } + } + + // Page top + m_pageMargins.nTop = aDistances.dyaTop; + + sal_Int32 nFooter = 0; + if ( aDistances.HasFooter() ) + nFooter = sal_Int32( aDistances.dyaHdrBottom ); + + // Page Bottom + m_pageMargins.nBottom = aDistances.dyaBottom; + + AddToAttrList( m_pSectionSpacingAttrList, 5, + FSNS( XML_w, XML_header ), OString::number( nHeader ).getStr(), + FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ).getStr(), + FSNS( XML_w, XML_footer ), OString::number( nFooter ).getStr(), + FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ).getStr(), + // FIXME Page Gutter is not handled ATM, setting to 0 as it's mandatory for OOXML + FSNS( XML_w, XML_gutter ), "0" ); + } + else + { + SAL_INFO("sw.ww8", "DocxAttributeOutput::FormatULSpace: setting spacing" << rULSpace.GetUpper() ); + // check if before auto spacing was set during import and spacing we get from actual object is same + // that we set in import. If yes just write beforeAutoSpacing tag. + if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper()) + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_beforeAutospacing ), "1" ); + } + else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1) + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_beforeAutospacing ), "0" ); + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() ); + } + else + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() ); + } + m_bParaBeforeAutoSpacing = false; + // check if after auto spacing was set during import and spacing we get from actual object is same + // that we set in import. If yes just write afterAutoSpacing tag. + if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower()) + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_afterAutospacing ), "1" ); + } + else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1) + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_afterAutospacing ), "0" ); + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() ); + } + else + { + AddToAttrList( m_pParagraphSpacingAttrList, + FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() ); + } + m_bParaAfterAutoSpacing = false; + + if (rULSpace.GetContext()) + m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing); + } +} + +namespace docx { + +std::unique_ptr<FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround) +{ + FastAttributeList * pAttrList(nullptr); + OString sType; + OString sSide; + switch (rSurround.GetSurround()) + { + case css::text::WrapTextMode_NONE: + sType = "topAndBottom"; + break; + case css::text::WrapTextMode_PARALLEL: + sType = "square"; + break; + case css::text::WrapTextMode_DYNAMIC: + sType = "square"; + sSide = "largest"; + break; + case css::text::WrapTextMode_LEFT: + sType = "square"; + sSide = "left"; + break; + case css::text::WrapTextMode_RIGHT: + sType = "square"; + sSide = "right"; + break; + case css::text::WrapTextMode_THROUGH: + /* empty type and side means through */ + default: + sType = "none"; + break; + } + if (!sType.isEmpty() || !sSide.isEmpty()) + { + pAttrList = FastSerializerHelper::createAttrList(); + if (!sType.isEmpty()) + { + pAttrList->add(XML_type, sType); + } + if (!sSide.isEmpty()) + { + pAttrList->add(XML_side, sSide); + } + } + return std::unique_ptr<FastAttributeList>(pAttrList); +} + +} // namespace docx + +void DocxAttributeOutput::FormatSurround( const SwFormatSurround& rSurround ) +{ + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + std::unique_ptr<FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround)); + if (pAttrList) + { + m_rExport.SdrExporter().setFlyWrapAttrList(pAttrList.release()); + } + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + OString sWrap( "auto" ); + switch ( rSurround.GetSurround( ) ) + { + case css::text::WrapTextMode_NONE: + sWrap = OString( "none" ); + break; + case css::text::WrapTextMode_THROUGH: + sWrap = OString( "through" ); + break; + case css::text::WrapTextMode_DYNAMIC: + case css::text::WrapTextMode_PARALLEL: + case css::text::WrapTextMode_LEFT: + case css::text::WrapTextMode_RIGHT: + default: + sWrap = OString( "around" ); + } + + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_wrap ), sWrap.getStr() ); + } +} + +void DocxAttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert ) +{ + OString sAlign = convertToOOXMLVertOrient( rFlyVert.GetVertOrient() ); + OString sVAnchor = convertToOOXMLVertOrientRel( rFlyVert.GetRelationOrient() ); + + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + m_rExport.SdrExporter().getTextFrameStyle().append(";margin-top:").append(double(rFlyVert.GetPos()) / 20).append("pt"); + if ( !sAlign.isEmpty() ) + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical:").append(sAlign); + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical-relative:").append(sVAnchor); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + if ( !sAlign.isEmpty() ) + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_yAlign ), sAlign.getStr() ); + else + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_y ), + OString::number( rFlyVert.GetPos() ).getStr() ); + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vAnchor ), sVAnchor.getStr() ); + } +} + +void DocxAttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori ) +{ + OString sAlign = convertToOOXMLHoriOrient( rFlyHori.GetHoriOrient(), rFlyHori.IsPosToggle() ); + OString sHAnchor = convertToOOXMLHoriOrientRel( rFlyHori.GetRelationOrient() ); + + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + m_rExport.SdrExporter().getTextFrameStyle().append(";margin-left:").append(double(rFlyHori.GetPos()) / 20).append("pt"); + if ( !sAlign.isEmpty() ) + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal:").append(sAlign); + m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal-relative:").append(sHAnchor); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + } + else if ( m_rExport.m_bOutFlyFrameAttrs ) + { + if ( !sAlign.isEmpty() ) + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_xAlign ), sAlign.getStr() ); + else + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_x ), + OString::number( rFlyHori.GetPos() ).getStr() ); + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hAnchor ), sHAnchor.getStr() ); + } +} + +void DocxAttributeOutput::FormatAnchor( const SwFormatAnchor& ) +{ + // Fly frames: anchors here aren't matching the anchors in docx +} + +static std::optional<sal_Int32> lcl_getDmlAlpha(const SvxBrushItem& rBrush) +{ + std::optional<sal_Int32> oRet; + sal_Int32 nTransparency = rBrush.GetColor().GetTransparency(); + if (nTransparency) + { + // Convert transparency to percent + sal_Int8 nTransparencyPercent = SvxBrushItem::TransparencyToPercent(nTransparency); + + // Calculate alpha value + // Consider oox/source/drawingml/color.cxx : getTransparency() function. + sal_Int32 nAlpha = ::oox::drawingml::MAX_PERCENT - ( ::oox::drawingml::PER_PERCENT * nTransparencyPercent ); + oRet = nAlpha; + } + return oRet; +} + +void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush ) +{ + const Color aColor = rBrush.GetColor(); + OString sColor = msfilter::util::ConvertColor( aColor.GetRGBColor() ); + std::optional<sal_Int32> oAlpha = lcl_getDmlAlpha(rBrush); + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + // Handle 'Opacity' + if (oAlpha) + { + // Calculate opacity value + // Consider oox/source/vml/vmlformatting.cxx : decodeColor() function. + double fOpacity = static_cast<double>(*oAlpha) * 65535 / ::oox::drawingml::MAX_PERCENT; + OUString sOpacity = OUString::number(fOpacity) + "f"; + + AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_opacity, OUStringToOString(sOpacity, RTL_TEXTENCODING_UTF8).getStr() ); + } + + sColor = "#" + sColor; + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor.getStr() ); + } + else if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + bool bImageBackground = false; + const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE); + if (pItem) + { + const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem); + if(pFillStyle->GetValue() == drawing::FillStyle_BITMAP) + { + bImageBackground = true; + } + } + if (!bImageBackground) + { + m_pSerializer->startElementNS(XML_a, XML_solidFill); + m_pSerializer->startElementNS(XML_a, XML_srgbClr, XML_val, sColor); + if (oAlpha) + m_pSerializer->singleElementNS(XML_a, XML_alpha, + XML_val, OString::number(*oAlpha)); + m_pSerializer->endElementNS(XML_a, XML_srgbClr); + m_pSerializer->endElementNS(XML_a, XML_solidFill); + } + } + else if ( !m_rExport.m_bOutPageDescs ) + { + // compare fill color with the original fill color + OString sOriginalFill = OUStringToOString( + m_sOriginalBackgroundColor, RTL_TEXTENCODING_UTF8 ); + + if ( aColor == COL_AUTO ) + sColor = "auto"; + + if( !m_pBackgroundAttrList.is() ) + { + m_pBackgroundAttrList = FastSerializerHelper::createAttrList(); + m_pBackgroundAttrList->add( FSNS( XML_w, XML_fill ), sColor.getStr() ); + m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" ); + } + else if ( sOriginalFill != sColor ) + { + // fill was modified during edition, theme fill attribute must be dropped + m_pBackgroundAttrList = FastSerializerHelper::createAttrList(); + m_pBackgroundAttrList->add( FSNS( XML_w, XML_fill ), sColor.getStr() ); + m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" ); + } + m_sOriginalBackgroundColor.clear(); + } +} + +void DocxAttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle ) +{ + if (!m_bIgnoreNextFill) + m_oFillStyle = rFillStyle.GetValue(); + else + m_bIgnoreNextFill = false; + + // Don't round-trip grabbag OriginalBackground if the background has been cleared. + if ( m_pBackgroundAttrList.is() && m_sOriginalBackgroundColor != "auto" && rFillStyle.GetValue() == drawing::FillStyle_NONE ) + m_pBackgroundAttrList.clear(); +} + +void DocxAttributeOutput::FormatFillGradient( const XFillGradientItem& rFillGradient ) +{ + if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && !m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_type, "gradient" ); + + const XGradient& rGradient = rFillGradient.GetGradientValue(); + OString sStartColor = msfilter::util::ConvertColor(rGradient.GetStartColor()); + OString sEndColor = msfilter::util::ConvertColor(rGradient.GetEndColor()); + + // Calculate the angle that was originally in the imported DOCX file + // (reverse calculate the angle that was converted in the file + // /oox/source/vml/vmlformatting.cxx :: FillModel::pushToPropMap + // and also in + // /oox/source/drawingml/fillproperties.cxx :: FillProperties::pushToPropMap + sal_Int32 nReverseAngle = 4500 - rGradient.GetAngle(); + nReverseAngle = nReverseAngle / 10; + nReverseAngle = (270 - nReverseAngle) % 360; + if (nReverseAngle != 0) + AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), + XML_angle, OString::number( nReverseAngle ).getStr() ); + + OString sColor1 = sStartColor; + OString sColor2 = sEndColor; + + switch (rGradient.GetGradientStyle()) + { + case css::awt::GradientStyle_AXIAL: + AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_focus, "50%" ); + // If it is an 'axial' gradient - swap the colors + // (because in the import process they were imported swapped) + sColor1 = sEndColor; + sColor2 = sStartColor; + break; + case css::awt::GradientStyle_LINEAR: break; + case css::awt::GradientStyle_RADIAL: break; + case css::awt::GradientStyle_ELLIPTICAL: break; + case css::awt::GradientStyle_SQUARE: break; + case css::awt::GradientStyle_RECT: break; + default: + break; + } + + sColor1 = "#" + sColor1; + sColor2 = "#" + sColor2; + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor1.getStr() ); + AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_color2, sColor2.getStr() ); + } + else if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + SwFrameFormat & rFormat( + const_cast<SwFrameFormat&>(m_rExport.m_pParentFrame->GetFrameFormat())); + uno::Reference<beans::XPropertySet> const xPropertySet( + SwXTextFrame::CreateXTextFrame(*rFormat.GetDoc(), &rFormat), + uno::UNO_QUERY); + m_rDrawingML.SetFS(m_pSerializer); + m_rDrawingML.WriteGradientFill(xPropertySet); + } + m_oFillStyle.reset(); +} + +void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox ) +{ + if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + // ugh, exporting fill here is quite some hack... this OutputItemSet abstraction is quite leaky + // <a:gradFill> should be before <a:ln>. + const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE); + if (pItem) + { + const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem); + FormatFillStyle(*pFillStyle); + if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_BITMAP) + { + const SdrObject* pSdrObj = m_rExport.m_pParentFrame->GetFrameFormat().FindRealSdrObject(); + if (pSdrObj) + { + uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xPropertySet( xShape, uno::UNO_QUERY ); + m_rDrawingML.SetFS(m_pSerializer); + m_rDrawingML.WriteBlipFill(xPropertySet, "BackGraphic"); + } + } + } + + pItem = GetExport().HasItem(XATTR_FILLGRADIENT); + if (pItem) + { + const XFillGradientItem* pFillGradient = static_cast<const XFillGradientItem*>(pItem); + FormatFillGradient(*pFillGradient); + } + m_bIgnoreNextFill = true; + } + if (m_rExport.SdrExporter().getTextFrameSyntax() || m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + const SvxBorderLine* pLeft = rBox.GetLeft( ); + const SvxBorderLine* pTop = rBox.GetTop( ); + const SvxBorderLine* pRight = rBox.GetRight( ); + const SvxBorderLine* pBottom = rBox.GetBottom( ); + + if (pLeft && pRight && pTop && pBottom && + *pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom) + { + // Check border style + SvxBorderLineStyle eBorderStyle = pTop->GetBorderLineStyle(); + if (eBorderStyle == SvxBorderLineStyle::NONE) + { + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, + XML_stroked, "f", XML_strokeweight, "0pt" ); + } + } + else + { + OString sColor(msfilter::util::ConvertColor(pTop->GetColor())); + double const fConverted(editeng::ConvertBorderWidthToWord(pTop->GetBorderLineStyle(), pTop->GetWidth())); + + if (m_rExport.SdrExporter().getTextFrameSyntax()) + { + sColor = "#" + sColor; + sal_Int32 nWidth = sal_Int32(fConverted / 20); + OString sWidth = OString::number(nWidth) + "pt"; + AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2, + XML_strokecolor, sColor.getStr(), + XML_strokeweight, sWidth.getStr() ); + if( SvxBorderLineStyle::DASHED == pTop->GetBorderLineStyle() ) // Line Style is Dash type + AddToAttrList( m_rExport.SdrExporter().getDashLineStyle(), + XML_dashstyle, "dash" ); + } + else + m_rExport.SdrExporter().writeBoxItemLine(rBox); + } + } + + if (m_rExport.SdrExporter().getDMLTextFrameSyntax()) + { + m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_lIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::LEFT)))); + m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_tIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::TOP)))); + m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_rIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::RIGHT)))); + m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_bIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::BOTTOM)))); + return; + } + + // v:textbox's inset attribute: inner margin values for textbox text - write only non-default values + double fDistanceLeftTwips = double(rBox.GetDistance(SvxBoxItemLine::LEFT)); + double fDistanceTopTwips = double(rBox.GetDistance(SvxBoxItemLine::TOP)); + double fDistanceRightTwips = double(rBox.GetDistance(SvxBoxItemLine::RIGHT)); + double fDistanceBottomTwips = double(rBox.GetDistance(SvxBoxItemLine::BOTTOM)); + + // Convert 'TWIPS' to 'INCH' (because in Word the default values are in Inches) + double fDistanceLeftInch = fDistanceLeftTwips / 1440; + double fDistanceTopInch = fDistanceTopTwips / 1440; + double fDistanceRightInch = fDistanceRightTwips / 1440; + double fDistanceBottomInch = fDistanceBottomTwips / 1440; + + // This code will write ONLY the non-default values. The values are in 'left','top','right','bottom' order. + // so 'bottom' is checked if it is default and if it is non-default - all the values will be written + // otherwise - 'right' is checked if it is default and if it is non-default - all the values except for 'bottom' will be written + // and so on. + OStringBuffer aInset; + if(!aInset.isEmpty() || fDistanceBottomInch != 0.05) + aInset.insert(0, "," + OString::number(fDistanceBottomInch) + "in"); + + if(!aInset.isEmpty() || fDistanceRightInch != 0.1) + aInset.insert(0, "," + OString::number(fDistanceRightInch) + "in"); + + if(!aInset.isEmpty() || fDistanceTopInch != 0.05) + aInset.insert(0, "," + OString::number(fDistanceTopInch) + "in"); + + if(!aInset.isEmpty() || fDistanceLeftInch != 0.1) + aInset.insert(0, OString::number(fDistanceLeftInch) + "in"); + + if (!aInset.isEmpty()) + m_rExport.SdrExporter().getTextboxAttrList()->add(XML_inset, aInset.makeStringAndClear()); + + return; + } + + OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions(); + // Check if there is a shadow item + const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW ); + if ( pItem ) + { + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation(); + } + + if ( !m_bOpenedSectPr || GetWritingHeaderFooter()) + { + // Not inside a section + + // Open the paragraph's borders tag + m_pSerializer->startElementNS(XML_w, XML_pBdr); + + std::map<SvxBoxItemLine, css::table::BorderLine2> aStyleBorders; + const SvxBoxItem* pInherited = nullptr; + if ( GetExport().m_pStyAttr ) + pInherited = GetExport().m_pStyAttr->GetItem<SvxBoxItem>(RES_BOX); + else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() ) + pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_BOX); + + if ( pInherited ) + { + aStyleBorders[ SvxBoxItemLine::TOP ] = SvxBoxItem::SvxLineToLine(pInherited->GetTop(), /*bConvert=*/false); + aStyleBorders[ SvxBoxItemLine::BOTTOM ] = SvxBoxItem::SvxLineToLine(pInherited->GetBottom(), false); + aStyleBorders[ SvxBoxItemLine::LEFT ] = SvxBoxItem::SvxLineToLine(pInherited->GetLeft(), false); + aStyleBorders[ SvxBoxItemLine::RIGHT ] = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false); + } + + impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aStyleBorders ); + + // Close the paragraph's borders tag + m_pSerializer->endElementNS( XML_w, XML_pBdr ); + } +} + +void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, SwTwips nPageSize ) +{ + // Get the columns attributes + FastAttributeList *pColsAttrList = FastSerializerHelper::createAttrList(); + + pColsAttrList->add( FSNS( XML_w, XML_num ), + OString::number( nCols ). getStr( ) ); + + const char* pEquals = "false"; + if ( bEven ) + { + sal_uInt16 nWidth = rCol.GetGutterWidth( true ); + pColsAttrList->add( FSNS( XML_w, XML_space ), + OString::number( nWidth ).getStr( ) ); + + pEquals = "true"; + } + + pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals ); + + bool bHasSep = (COLADJ_NONE != rCol.GetLineAdj()); + + pColsAttrList->add( FSNS( XML_w, XML_sep ), OString::boolean( bHasSep ) ); + + // Write the element + m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList ); + + // Write the columns width if non-equals + const SwColumns & rColumns = rCol.GetColumns( ); + if ( !bEven ) + { + for ( sal_uInt16 n = 0; n < nCols; ++n ) + { + FastAttributeList *pColAttrList = FastSerializerHelper::createAttrList(); + sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, static_cast<sal_uInt16>(nPageSize) ); + pColAttrList->add( FSNS( XML_w, XML_w ), + OString::number( nWidth ).getStr( ) ); + + if ( n + 1 != nCols ) + { + sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( ); + pColAttrList->add( FSNS( XML_w, XML_space ), + OString::number( nSpacing ).getStr( ) ); + } + + m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList ); + } + } + + m_pSerializer->endElementNS( XML_w, XML_cols ); +} + +void DocxAttributeOutput::FormatKeep( const SvxFormatKeepItem& rItem ) +{ + m_pSerializer->singleElementNS( XML_w, XML_keepNext, + FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) ); +} + +void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid ) +{ + FastAttributeList *pGridAttrList = FastSerializerHelper::createAttrList(); + + OString sGridType; + switch ( rGrid.GetGridType( ) ) + { + default: + case GRID_NONE: + sGridType = OString( "default" ); + break; + case GRID_LINES_ONLY: + sGridType = OString( "lines" ); + break; + case GRID_LINES_CHARS: + if ( rGrid.IsSnapToChars( ) ) + sGridType = OString( "snapToChars" ); + else + sGridType = OString( "linesAndChars" ); + break; + } + pGridAttrList->add( FSNS( XML_w, XML_type ), sGridType.getStr( ) ); + + sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight(); + pGridAttrList->add( FSNS( XML_w, XML_linePitch ), + OString::number( nHeight ).getStr( ) ); + + pGridAttrList->add( FSNS( XML_w, XML_charSpace ), + OString::number( GridCharacterPitch( rGrid ) ).getStr( ) ); + + m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList ); +} + +void DocxAttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering ) +{ + if ( !rNumbering.IsCount( ) ) + m_pSerializer->singleElementNS(XML_w, XML_suppressLineNumbers); +} + +void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection ) +{ + OString sTextFlow; + bool bBiDi = false; + SvxFrameDirection nDir = rDirection.GetValue(); + + if ( nDir == SvxFrameDirection::Environment ) + nDir = GetExport( ).GetDefaultFrameDirection( ); + + switch ( nDir ) + { + default: + case SvxFrameDirection::Horizontal_LR_TB: + sTextFlow = OString( "lrTb" ); + break; + case SvxFrameDirection::Horizontal_RL_TB: + sTextFlow = OString( "lrTb" ); + bBiDi = true; + break; + case SvxFrameDirection::Vertical_LR_TB: // many things but not this one + case SvxFrameDirection::Vertical_RL_TB: + sTextFlow = OString( "tbRl" ); + break; + } + + if ( m_rExport.m_bOutPageDescs ) + { + m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), sTextFlow); + if ( bBiDi ) + m_pSerializer->singleElementNS(XML_w, XML_bidi); + } + else if ( !m_rExport.m_bOutFlyFrameAttrs ) + { + if ( bBiDi ) + m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "1"); + else + m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "0"); + } +} + +void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem) +{ + const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag(); + for ( const auto & rGrabBagElement : rMap ) + { + if (rGrabBagElement.first == "MirrorIndents") + m_pSerializer->singleElementNS(XML_w, XML_mirrorIndents); + else if (rGrabBagElement.first == "ParaTopMarginBeforeAutoSpacing") + { + m_bParaBeforeAutoSpacing = true; + // get fixed value which was set during import + rGrabBagElement.second >>= m_nParaBeforeSpacing; + m_nParaBeforeSpacing = convertMm100ToTwip(m_nParaBeforeSpacing); + SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaBeforeSpacing); + } + else if (rGrabBagElement.first == "ParaBottomMarginAfterAutoSpacing") + { + m_bParaAfterAutoSpacing = true; + // get fixed value which was set during import + rGrabBagElement.second >>= m_nParaAfterSpacing; + m_nParaAfterSpacing = convertMm100ToTwip(m_nParaAfterSpacing); + SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaAfterSpacing); + } + else if (rGrabBagElement.first == "CharThemeFill") + { + uno::Sequence<beans::PropertyValue> aGrabBagSeq; + rGrabBagElement.second >>= aGrabBagSeq; + + for (const auto& rProp : std::as_const(aGrabBagSeq)) + { + OString sVal = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8); + + if (sVal.isEmpty()) + continue; + + if (rProp.Name == "val") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_val), sVal.getStr()); + else if (rProp.Name == "color") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_color), sVal.getStr()); + else if (rProp.Name == "themeColor") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeColor), sVal.getStr()); + else if (rProp.Name == "themeTint") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeTint), sVal.getStr()); + else if (rProp.Name == "themeShade") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeShade), sVal.getStr()); + else if (rProp.Name == "fill") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_fill), sVal.getStr()); + else if (rProp.Name == "themeFill") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFill), sVal.getStr()); + else if (rProp.Name == "themeFillTint") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillTint), sVal.getStr()); + else if (rProp.Name == "themeFillShade") + AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillShade), sVal.getStr()); + else if (rProp.Name == "originalColor") + rProp.Value >>= m_sOriginalBackgroundColor; + } + } + else if (rGrabBagElement.first == "SdtPr") + { + const uno::Sequence<beans::PropertyValue> aGrabBagSdt = + rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); + for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) + { + if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" || + aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") + { + if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartObj ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartList ); + + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery") + AddToAttrList( m_pParagraphSdtPrTokenChildren, + FSNS( XML_w, XML_docPartGallery ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory") + AddToAttrList( m_pParagraphSdtPrTokenChildren, + FSNS( XML_w, XML_docPartCategory ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique") + { + if (sValue.isEmpty()) + sValue = "true"; + AddToAttrList( m_pParagraphSdtPrTokenChildren, FSNS( XML_w, XML_docPartUnique ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_equation ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_picture ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_citation ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group") + m_nParagraphSdtPrToken = FSNS( XML_w, XML_group ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text") + m_nParagraphSdtPrToken = FSNS(XML_w, XML_text); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pParagraphSdtPrDataBindingAttrs.is()) + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") + AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, + FSNS( XML_w, XML_prefixMappings ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_xpath") + AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, + FSNS( XML_w, XML_xpath ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") + AddToAttrList( m_pParagraphSdtPrDataBindingAttrs, + FSNS( XML_w, XML_storeItemID ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aParagraphSdtPrAlias.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aParagraphSdtPrAlias)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unexpected sdt alias value"); + m_aStartedParagraphSdtPrAlias = m_aParagraphSdtPrAlias; + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") + { + m_nParagraphSdtPrToken = FSNS( XML_w14, XML_checkbox ); + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") + AddToAttrList( m_pParagraphSdtPrTokenChildren, + FSNS( XML_w14, XML_checked ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") + AddToAttrList( m_pParagraphSdtPrTokenChildren, + FSNS( XML_w14, XML_checkedState ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") + AddToAttrList( m_pParagraphSdtPrTokenChildren, + FSNS( XML_w14, XML_uncheckedState ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") + m_bParagraphSdtHasId = true; + else + SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name); + } + } + else if (rGrabBagElement.first == "ParaCnfStyle") + { + uno::Sequence<beans::PropertyValue> aAttributes = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); + m_pTableStyleExport->CnfStyle(aAttributes); + } + else if (rGrabBagElement.first == "ParaSdtEndBefore") + { + // Handled already in StartParagraph(). + } + else + SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << rGrabBagElement.first ); + } +} + +void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem ) +{ + if (m_bPreventDoubleFieldsHandling) + return; + + const std::map< OUString, css::uno::Any >& rMap = rItem.GetGrabBag(); + + // get original values of theme-derived properties to check if they have changed during the edition + bool bWriteCSTheme = true; + bool bWriteAsciiTheme = true; + bool bWriteEastAsiaTheme = true; + bool bWriteThemeFontColor = true; + OUString sOriginalValue; + for ( const auto & rGrabBagElement : rMap ) + { + if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameCs" ) + { + if ( rGrabBagElement.second >>= sOriginalValue ) + bWriteCSTheme = + ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_cs ) ) == sOriginalValue ); + } + else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameAscii" ) + { + if ( rGrabBagElement.second >>= sOriginalValue ) + bWriteAsciiTheme = + ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_ascii ) ) == sOriginalValue ); + } + else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameEastAsia" ) + { + if ( rGrabBagElement.second >>= sOriginalValue ) + bWriteEastAsiaTheme = + ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_eastAsia ) ) == sOriginalValue ); + } + else if ( m_pColorAttrList.is() && rGrabBagElement.first == "CharThemeOriginalColor" ) + { + if ( rGrabBagElement.second >>= sOriginalValue ) + bWriteThemeFontColor = + ( m_pColorAttrList->getOptionalValue( FSNS( XML_w, XML_val ) ) == sOriginalValue ); + } + } + + // save theme attributes back to the run properties + OUString str; + for ( const auto & rGrabBagElement : rMap ) + { + if ( rGrabBagElement.first == "CharThemeNameAscii" && bWriteAsciiTheme ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_asciiTheme ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeNameCs" && bWriteCSTheme ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cstheme ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeNameEastAsia" && bWriteEastAsiaTheme ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsiaTheme ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeNameHAnsi" && bWriteAsciiTheme ) + // this is not a mistake: in LibO we don't directly support the hAnsi family + // of attributes so we save the same value from ascii attributes instead + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_hAnsiTheme ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeColor" && bWriteThemeFontColor ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeColor ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeColorShade" ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeShade ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( rGrabBagElement.first == "CharThemeColorTint" ) + { + rGrabBagElement.second >>= str; + AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeTint ), + OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if( rGrabBagElement.first == "CharThemeFontNameCs" || + rGrabBagElement.first == "CharThemeFontNameAscii" || + rGrabBagElement.first == "CharThemeFontNameEastAsia" || + rGrabBagElement.first == "CharThemeOriginalColor" ) + { + // just skip these, they were processed before + } + else if(rGrabBagElement.first == "CharGlowTextEffect" || + rGrabBagElement.first == "CharShadowTextEffect" || + rGrabBagElement.first == "CharReflectionTextEffect" || + rGrabBagElement.first == "CharTextOutlineTextEffect" || + rGrabBagElement.first == "CharTextFillTextEffect" || + rGrabBagElement.first == "CharScene3DTextEffect" || + rGrabBagElement.first == "CharProps3DTextEffect" || + rGrabBagElement.first == "CharLigaturesTextEffect" || + rGrabBagElement.first == "CharNumFormTextEffect" || + rGrabBagElement.first == "CharNumSpacingTextEffect" || + rGrabBagElement.first == "CharStylisticSetsTextEffect" || + rGrabBagElement.first == "CharCntxtAltsTextEffect") + { + beans::PropertyValue aPropertyValue; + rGrabBagElement.second >>= aPropertyValue; + m_aTextEffectsGrabBag.push_back(aPropertyValue); + } + else if (rGrabBagElement.first == "SdtEndBefore") + { + if (m_bStartedCharSdt) + m_bEndCharSdt = true; + } + else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame ) + { + const uno::Sequence<beans::PropertyValue> aGrabBagSdt = + rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >(); + for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt) + { + if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox") + { + m_nRunSdtPrToken = FSNS( XML_w14, XML_checkbox ); + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_SdtCheckbox_checked") + AddToAttrList( m_pRunSdtPrTokenChildren, + FSNS( XML_w14, XML_checked ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState") + AddToAttrList( m_pRunSdtPrTokenChildren, + FSNS( XML_w14, XML_checkedState ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState") + AddToAttrList( m_pRunSdtPrTokenChildren, + FSNS( XML_w14, XML_uncheckedState ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pRunSdtPrDataBindingAttrs.is()) + { + uno::Sequence<beans::PropertyValue> aGrabBag; + aPropertyValue.Value >>= aGrabBag; + for (const auto& rProp : std::as_const(aGrabBag)) + { + OUString sValue = rProp.Value.get<OUString>(); + if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings") + AddToAttrList( m_pRunSdtPrDataBindingAttrs, + FSNS( XML_w, XML_prefixMappings ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_xpath") + AddToAttrList( m_pRunSdtPrDataBindingAttrs, + FSNS( XML_w, XML_xpath ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID") + AddToAttrList( m_pRunSdtPrDataBindingAttrs, + FSNS( XML_w, XML_storeItemID ), + OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aRunSdtPrAlias.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aRunSdtPrAlias)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::CharGrabBag: unexpected sdt alias value"); + } + //do not overwrite the parent node. + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text" && !m_pRunSdtPrTokenChildren.is()) + m_nRunSdtPrToken = FSNS( XML_w, XML_text ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id" && m_nRunSdtPrToken == 0) + // only write id token as a marker if no other exist + m_nRunSdtPrToken = FSNS( XML_w, XML_id ); + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") + m_nRunSdtPrToken = FSNS( XML_w, XML_citation ); + } + } + else + SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << rGrabBagElement.first); + } +} + +DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML ) + : AttributeOutputBase(rExport.GetFilter().getFileUrl()), + m_rExport( rExport ), + m_pSerializer( pSerializer ), + m_rDrawingML( *pDrawingML ), + m_bEndCharSdt(false), + m_bStartedCharSdt(false), + m_bStartedParaSdt(false), + m_endPageRef( false ), + m_pFootnotesList( new ::docx::FootnotesList() ), + m_pEndnotesList( new ::docx::FootnotesList() ), + m_footnoteEndnoteRefTag( 0 ), + m_footnoteCustomLabel(), + m_pRedlineData( nullptr ), + m_nRedlineId( 0 ), + m_bOpenedSectPr( false ), + m_bHadSectPr(false), + m_bRunTextIsOn( false ), + m_bWritingHeaderFooter( false ), + m_bAnchorLinkedToNode(false), + m_bWritingField( false ), + m_bPreventDoubleFieldsHandling( false ), + m_sFieldBkm( ), + m_nNextBookmarkId( 0 ), + m_nNextAnnotationMarkId( 0 ), + m_pCurrentFrame( nullptr ), + m_bParagraphOpened( false ), + m_bParagraphFrameOpen( false ), + m_bIsFirstParagraph( true ), + m_bAlternateContentChoiceOpen( false ), + m_bPostponedProcessingFly( false ), + m_nColBreakStatus( COLBRK_NONE ), + m_bPostponedPageBreak( false ), + m_nTextFrameLevel( 0 ), + m_closeHyperlinkInThisRun( false ), + m_closeHyperlinkInPreviousRun( false ), + m_startedHyperlink( false ), + m_nHyperLinkCount(0), + m_nFieldsInHyperlink( 0 ), + m_bExportingOutline(false), + m_nChartCount(0), + pendingPlaceholder( nullptr ), + m_postitFieldsMaxId( 0 ), + m_anchorId( 1 ), + m_nextFontId( 1 ), + m_tableReference(new TableReference()), + m_bIgnoreNextFill(false), + m_pTableStyleExport(std::make_shared<DocxTableStyleExport>(rExport.m_pDoc, pSerializer)), + m_bParaBeforeAutoSpacing(false), + m_bParaAfterAutoSpacing(false), + m_nParaBeforeSpacing(0), + m_nParaAfterSpacing(0) + , m_nParagraphSdtPrToken(0) + , m_nRunSdtPrToken(0) + , m_nStateOfFlyFrame( FLY_NOT_PROCESSED ) + , m_bParagraphSdtHasId(false) +{ + // Push initial items to the RelId cache. In case the document contains no + // special streams (headers, footers, etc.) then these items are used + // during the full export. + PushRelIdCache(); +} + +DocxAttributeOutput::~DocxAttributeOutput() +{ +} + +DocxExport& DocxAttributeOutput::GetExport() +{ + return m_rExport; +} + +void DocxAttributeOutput::SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer ) +{ + m_pSerializer = pSerializer; + m_pTableStyleExport->SetSerializer(pSerializer); +} + +bool DocxAttributeOutput::HasFootnotes() const +{ + return !m_pFootnotesList->isEmpty(); +} + +bool DocxAttributeOutput::HasEndnotes() const +{ + return !m_pEndnotesList->isEmpty(); +} + +bool DocxAttributeOutput::HasPostitFields() const +{ + return !m_postitFields.empty(); +} + +void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) +{ + m_pSerializer->startElementNS(XML_w, XML_numPicBullet, + FSNS(XML_w, XML_numPicBulletId), OString::number(nId)); + + OStringBuffer aStyle; + // Size is in twips, we need it in points. + aStyle.append("width:").append(double(aSize.Width()) / 20); + aStyle.append("pt;height:").append(double(aSize.Height()) / 20).append("pt"); + m_pSerializer->startElementNS(XML_w, XML_pict); + m_pSerializer->startElementNS( XML_v, XML_shape, + XML_style, aStyle.getStr(), + FSNS(XML_o, XML_bullet), "t"); + + OUString aRelId = m_rDrawingML.WriteImage(rGraphic); + m_pSerializer->singleElementNS( XML_v, XML_imagedata, + FSNS(XML_r, XML_id), OUStringToOString(aRelId, RTL_TEXTENCODING_UTF8), + FSNS(XML_o, XML_title), ""); + + m_pSerializer->endElementNS(XML_v, XML_shape); + m_pSerializer->endElementNS(XML_w, XML_pict); + + m_pSerializer->endElementNS(XML_w, XML_numPicBullet); +} + +void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue ) +{ + AddToAttrList( pAttrList, 1, nAttrName, sAttrValue ); +} + +void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ... ) +{ + if( !pAttrList.is() ) + pAttrList = FastSerializerHelper::createAttrList(); + + va_list args; + va_start( args, nAttrs ); + for( sal_Int32 i = 0; i<nAttrs; i++) + { + sal_Int32 nName = va_arg( args, sal_Int32 ); + const char* pValue = va_arg( args, const char* ); + if( pValue ) + pAttrList->add( nName, pValue ); + } + va_end( args ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx new file mode 100644 index 000000000..de0e31b91 --- /dev/null +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -0,0 +1,1061 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX + +#include <memory> + +#include "attributeoutputbase.hxx" +#include "fields.hxx" +#include <IMark.hxx> +#include "docxexport.hxx" +#include <wrtswtbl.hxx> + +#include <editeng/boxitem.hxx> +#include <sax/fshelper.hxx> +#include <sax/fastattribs.hxx> +#include <vcl/vclenum.hxx> +#include <svx/xenum.hxx> + +#include <fldbas.hxx> + +#include <vector> +#include <optional> +#include <o3tl/sorted_vector.hxx> +#include <oox/export/vmlexport.hxx> +#include <oox/export/drawingml.hxx> +#include "docxtablestyleexport.hxx" + +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> + +class SwGrfNode; +class SdrObject; +enum class SvxBoxItemLine; + +namespace docx { class FootnotesList; } +namespace oox::drawingml { class DrawingML; } + +struct FieldInfos +{ + std::shared_ptr<const SwField> pField; + const ::sw::mark::IFieldmark* pFieldmark; + ww::eField eType; + bool bOpen; + bool bSep; + bool bClose; + OUString sCmd; + FieldInfos() + : pFieldmark(nullptr), eType(ww::eUNKNOWN) + , bOpen(false), bSep(false), bClose(false) + {} +}; + +enum DocxColBreakStatus +{ + COLBRK_NONE, + COLBRK_POSTPONE, + COLBRK_WRITEANDPOSTPONE, + COLBRK_WRITE +}; + +/** + * A structure that holds information about the options selected + * when outputting a border to DOCX. + * + * There are 3 functions that initialize this structure: + * - lcl_getTableDefaultBorderOptions - retrieves the options for when outputting table default borders + * - lcl_getTableCellBorderOptions - retrieves the options for when outputting table cell borders + * - lcl_getBoxBorderOptions - retrieves the options for when outputting box borders + * + */ +struct OutputBorderOptions +{ + sal_Int32 tag = 0; + bool bUseStartEnd = false; + bool bWriteTag = true; + bool bWriteDistance = false; + SvxShadowLocation aShadowLocation = SvxShadowLocation::NONE; + std::shared_ptr<editeng::WordBorderDistances> pDistances; +}; + +struct DocxTableExportContext; + +/** + * A structure that holds flags for the table export. + */ +struct TableReference +{ + /// Remember if we are in an open cell, or not. + bool m_bTableCellOpen; + + /// If paragraph sdt got opened in this table cell. + bool m_bTableCellParaSdtOpen; + + /// Remember the current table depth. + sal_uInt32 m_nTableDepth; + + TableReference() + : m_bTableCellOpen(false), + m_bTableCellParaSdtOpen(false), + m_nTableDepth(0) + { + } +}; + +/// The class that has handlers for various resource types when exporting as DOCX. +class DocxAttributeOutput : public AttributeOutputBase, public oox::vml::VMLTextExport, public oox::drawingml::DMLTextExport +{ +public: + /// Export the state of RTL/CJK. + virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override; + + /// Start of the paragraph. + virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo ) override; + + /// End of the paragraph. + virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override; + + /// Empty paragraph. + virtual void EmptyParagraph() override; + + /// Called in order to output section breaks. + virtual void SectionBreaks(const SwNode& rNode) override; + + /// Called before we start outputting the attributes. + virtual void StartParagraphProperties() override; + + /// Called after we end outputting the attributes. + virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) override; + + /// Start of the text run. + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override; + + /// End of the text run. + virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; + + /// Called before we start outputting the attributes. + virtual void StartRunProperties() override; + + /// Called after we end outputting the attributes. + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override; + + virtual bool FootnoteEndnoteRefTag() override; + + virtual void SectFootnoteEndnotePr() override; + + virtual void WritePostitFieldReference() override; + + /// Output text (inside a run). + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) override; + + /// Output text (without markup). + virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override; + + /// Output ruby start. + virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) override; + + /// Output ruby end. + virtual void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override; + + /// Output URL start. + virtual bool StartURL( const OUString& rUrl, const OUString& rTarget ) override; + + /// Output URL end. + virtual bool EndURL(bool) override; + + virtual void FieldVanish( const OUString& rText, ww::eField eType ) override; + + /// Output redlining. + /// + /// The common attribute that can be among the run properties. + virtual void Redline( const SwRedlineData* pRedline ) override; + + /// Output redlining. + /// + /// Start of the tag that encloses the run, fills the info according to + /// the value of pRedlineData. + void StartRedline( const SwRedlineData * pRedlineData ); + + /// Output redlining. + /// + /// End of the tag that encloses the run. + void EndRedline( const SwRedlineData * pRedlineData ); + + virtual void SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame ) override; + virtual void SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode ) override; + virtual bool IsFlyProcessingPostponed() override; + virtual void ResetFlyProcessingFlag() override; + + virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override; + + /// Output style. + virtual void ParagraphStyle( sal_uInt16 nStyle ) override; + + virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + void TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) override; + virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableRowEnd( sal_uInt32 nDepth ) override; + + /// Start of the styles table. + virtual void StartStyles() override; + + /// End of the styles table. + virtual void EndStyles( sal_uInt16 nNumberOfStyles ) override; + + /// Write default style. + virtual void DefaultStyle() override; + + /// Write Doc Defaults + void DocDefaults( ); + + /// Write latent styles. + void LatentStyles(); + + /** Similar to OutputItem(), but write something only if it is not the default. + + This is to output the docDefaults, and we should write something out + only in case it is not what MSO already uses for the document by default. + */ + void OutputDefaultItem(const SfxPoolItem& rHt); + + /// Start of a style in the styles table. + virtual void StartStyle( const OUString& rName, StyleType eType, + sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwId, sal_uInt16 nId, + bool bAutoUpdate ) override; + + /// End of a style in the styles table. + virtual void EndStyle() override; + + /// Start of (paragraph or run) properties of a style. + virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) override; + + /// End of (paragraph or run) properties of a style. + virtual void EndStyleProperties( bool bParProp ) override; + + /// Numbering rule and Id. + virtual void OutlineNumbering(sal_uInt8 nLvl) override; + + /// Page break + /// As a paragraph property - the paragraph should be on the next page. + virtual void PageBreakBefore( bool bBreak ) override; + + /// Write a section break + /// msword::ColumnBreak or msword::PageBreak + /// bBreakAfter: the break must be scheduled for insertion in the end of current paragraph + virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr ) override; + + // preserve DOCX page vertical alignment + virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust ) override; + + /// Start of the section properties. + virtual void StartSection() override; + + /// End of the section properties. + virtual void EndSection() override; + + /// Protection of forms. + virtual void SectionFormProtection( bool bProtected ) override; + + /// Numbering of the lines in the document. + virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) override; + + /// Has different headers/footers for the title page. + virtual void SectionTitlePage() override; + + /// Description of the page borders. + virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) override; + + /// Columns populated from right/numbers on the right side? + virtual void SectionBiDi( bool bBiDi ) override; + + /// The style of the page numbers. + /// + virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) override; + + /// The type of breaking. + virtual void SectionType( sal_uInt8 nBreakCode ) override; + + /// Start the font. + void StartFont( const OUString& rFamilyName ) const; + + /// End the font. + void EndFont() const; + + /// Alternate name for the font. + void FontAlternateName( const OUString& rName ) const; + + /// Font charset. + void FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const; + + /// Font family. + void FontFamilyType( FontFamily eFamily ) const; + + /// Font pitch. + void FontPitchType( FontPitch ePitch ) const; + + /// Write out the font into the document, if it's an embedded font. + void EmbedFont( const OUString& name, FontFamily family, FontPitch pitch ); + + /// Definition of a numbering instance. + virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) override; + + /// Numbering definition that overrides abstract numbering definition + virtual void OverrideNumberingDefinition( SwNumRule const& rRule, + sal_uInt16 nNum, sal_uInt16 nAbstractNum, + const std::map< size_t, size_t > & rLevelOverrides ) override; + + /// Start of the abstract numbering definition instance. + virtual void StartAbstractNumbering( sal_uInt16 nId ) override; + + /// End of the abstract numbering definition instance. + virtual void EndAbstractNumbering() override; + + /// All the numbering level information. + virtual void NumberingLevel( sal_uInt8 nLevel, + sal_uInt16 nStart, + sal_uInt16 nNumberingType, + SvxAdjust eAdjust, + const sal_uInt8 *pNumLvlPos, + sal_uInt8 nFollow, + const wwFont *pFont, + const SfxItemSet *pOutSet, + sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, + sal_Int16 nListTabPos, + const OUString &rNumberingString, + const SvxBrushItem* pBrush ) override; + + void WriteField_Impl( const SwField* pField, ww::eField eType, const OUString& rFieldCmd, FieldFlags nMode ); + void WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark ); + + void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); + void WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); + void WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); + void PushRelIdCache(); + void PopRelIdCache(); + /// End possibly opened paragraph sdt block. + void EndParaSdtBlock(); + + void WriteFloatingTable(ww8::Frame const* pParentFrame); + +private: + /// Initialize the structures where we are going to collect some of the paragraph properties. + /// + /// Some of the properties have to be collected from more sources, and are + /// actually not written between StartParagraphProperties and + /// EndParagraphProperties. They are output in this method, which is + /// supposed to be called just before outputting </rPr> whenever it is done. + void InitCollectedParagraphProperties(); + + /// Output what we collected during the run properties output. + /// + /// @see WriteCollectedParagrapProperties(). + void WriteCollectedParagraphProperties(); + + /// Initialize the structures where we are going to collect some of the run properties. + /// + /// This is an equivalent of InitCollectedParagraphProperties(), resp. + /// WriteCollectectedParagraphProperties(). + /// + /// @see InitCollectedParagraphProperties(). + void InitCollectedRunProperties(); + + /// Output what we collected during the run properties output. + /// + /// @see InitCollectedRunProperies(), WriteCollectedParagraphProperties() + void WriteCollectedRunProperties(); + + /// Output graphic fly frames or replacement graphics for OLE nodes. + /// + /// For graphic frames, just use the first two parameters, for OLE + /// replacement graphics, set the first as 0, and pass the remaining three. + /// + /// @see WriteOLE2Obj() + void FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj = nullptr); + void WriteSrcRect( const SdrObject* pSdrObj, const SwFrameFormat* pFrameFormat ); + void WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment); + bool WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat); + bool WriteOLEMath( const SwOLENode& rNode, const sal_Int8 nAlign ); + void PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat ); + void WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* rFlyFrameFormat ); + + void WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun); + bool ExportAsActiveXControl(const SdrObject* pObject) const; + + void InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + void StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + void StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + void StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ); + void TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow ); + void EndTableCell( sal_uInt32 nCell ); + void EndTableRow( ); + void EndTable(); + void SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow); + void PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize); + static bool TextBoxIsFramePr(const SwFrameFormat& rFrameFormat); + /// End cell, row, and even the entire table if necessary. + void FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph = false ); + + void WriteFFData( const FieldInfos& rInfos ); + void WritePendingPlaceholder(); + + void EmbedFontStyle( const OUString& name, int tag, FontFamily family, FontItalic italic, FontWeight weight, + FontPitch pitch ); + + /** + * Translate an ico value to the corresponding HighlightColorValues enumaration item + * + * @param[in] nIco ico value [0..16] + * @return color name (e.g. "red"), if color is inside [1..16] range + * empty string, otherwise + **/ + static OString TransHighlightColor( sal_uInt8 nIco ); +protected: + + /// Output frames - the implementation. + virtual void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) override; + + /// Sfx item Sfx item RES_CHRATR_CASEMAP + virtual void CharCaseMap( const SvxCaseMapItem& rCaseMap ) override; + + /// Sfx item Sfx item RES_CHRATR_COLOR + virtual void CharColor( const SvxColorItem& rColor) override; + + /// Sfx item Sfx item RES_CHRATR_CONTOUR + virtual void CharContour( const SvxContourItem& rContour ) override; + + /// Sfx item RES_CHRATR_CROSSEDOUT + virtual void CharCrossedOut( const SvxCrossedOutItem& rCrossedOut ) override; + + /// Sfx item RES_CHRATR_ESCAPEMENT + virtual void CharEscapement( const SvxEscapementItem& rEscapement ) override; + + /// Sfx item RES_CHRATR_FONT + virtual void CharFont( const SvxFontItem& rFont ) override; + + /// Sfx item RES_CHRATR_FONTSIZE + virtual void CharFontSize( const SvxFontHeightItem& rFontSize ) override; + + /// Sfx item RES_CHRATR_KERNING + virtual void CharKerning( const SvxKerningItem& rKerning ) override; + + /// Sfx item RES_CHRATR_LANGUAGE + virtual void CharLanguage( const SvxLanguageItem& rLanguage ) override; + + /// Sfx item RES_CHRATR_POSTURE + virtual void CharPosture( const SvxPostureItem& rPosture ) override; + + /// Sfx item RES_CHRATR_SHADOWED + virtual void CharShadow( const SvxShadowedItem& rShadow ) override; + + /// Sfx item RES_CHRATR_UNDERLINE + virtual void CharUnderline( const SvxUnderlineItem& rUnderline ) override; + + /// Sfx item RES_CHRATR_WEIGHT + virtual void CharWeight( const SvxWeightItem& rWeight ) override; + + /// Sfx item RES_CHRATR_AUTOKERN + virtual void CharAutoKern( const SvxAutoKernItem& ) override; + + /// Sfx item RES_CHRATR_BLINK + virtual void CharAnimatedText( const SvxBlinkItem& rBlink ) override; + + /// Sfx item RES_CHRATR_BACKGROUND + virtual void CharBackground( const SvxBrushItem& rBrush ) override; + + /// Sfx item RES_CHRATR_CJK_FONT + virtual void CharFontCJK( const SvxFontItem& rFont ) override; + + /// Sfx item RES_CHRATR_CJK_FONTSIZE + virtual void CharFontSizeCJK( const SvxFontHeightItem& rFontSize ) override { CharFontSize( rFontSize ); } + + /// Sfx item RES_CHRATR_CJK_LANGUAGE + virtual void CharLanguageCJK( const SvxLanguageItem& rLanguageItem ) override { CharLanguage( rLanguageItem ); } + + /// Sfx item RES_CHRATR_CJK_POSTURE + virtual void CharPostureCJK( const SvxPostureItem& rPosture ) override; + + /// Sfx item RES_CHRATR_CJK_WEIGHT + virtual void CharWeightCJK( const SvxWeightItem& rWeight ) override; + + /// Sfx item RES_CHRATR_CTL_FONT + virtual void CharFontCTL( const SvxFontItem& rFont ) override; + + /// Sfx item RES_CHRATR_CTL_FONTSIZE + virtual void CharFontSizeCTL( const SvxFontHeightItem& rFontSize ) override { CharFontSize( rFontSize ); } + + /// Sfx item RES_CHRATR_CTL_LANGUAGE + virtual void CharLanguageCTL( const SvxLanguageItem& rLanguageItem ) override { CharLanguage( rLanguageItem); } + + /// Sfx item RES_CHRATR_CTL_POSTURE + virtual void CharPostureCTL( const SvxPostureItem& rWeight ) override; + + /// Sfx item RES_CHRATR_CTL_WEIGHT + virtual void CharWeightCTL( const SvxWeightItem& rWeight ) override; + + /// Sfx item RES_CHRATR_BidiRTL + virtual void CharBidiRTL( const SfxPoolItem& ) override; + + /// Sfx item RES_CHRATR_IdctHint + virtual void CharIdctHint( const SfxPoolItem& ) override; + + /// Sfx item RES_CHRATR_ROTATE + virtual void CharRotate( const SvxCharRotateItem& rRotate ) override; + + /// Sfx item RES_CHRATR_EMPHASIS_MARK + virtual void CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark ) override; + + /// Sfx item RES_CHRATR_TWO_LINES + virtual void CharTwoLines( const SvxTwoLinesItem& rTwoLines ) override; + + /// Sfx item RES_CHRATR_SCALEW + virtual void CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth ) override; + + /// Sfx item RES_CHRATR_RELIEF + virtual void CharRelief( const SvxCharReliefItem& rRelief) override; + + /// Sfx item RES_CHRATR_HIDDEN + virtual void CharHidden( const SvxCharHiddenItem& rHidden ) override; + + /// Sfx item RES_CHRATR_BOX + virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) override; + + /// Sfx item RES_CHRATR_HIGHLIGHT + virtual void CharHighlight( const SvxBrushItem& rHighlight ) override; + + /// Sfx item RES_TXTATR_INETFMT + virtual void TextINetFormat( const SwFormatINetFormat& ) override; + + /// Sfx item RES_TXTATR_CHARFMT + virtual void TextCharFormat( const SwFormatCharFormat& ) override; + + /// Sfx item RES_TXTATR_FTN + virtual void TextFootnote_Impl( const SwFormatFootnote& ) override; + + /// Output the footnote/endnote reference (if there's one to output). + void FootnoteEndnoteReference(); + + /// Sfx item RES_PARATR_LINESPACING + virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) override; + + /// Sfx item RES_PARATR_ADJUST + virtual void ParaAdjust( const SvxAdjustItem& rAdjust ) override; + + /// Sfx item RES_PARATR_SPLIT + virtual void ParaSplit( const SvxFormatSplitItem& rSplit ) override; + + /// Sfx item RES_PARATR_WIDOWS + virtual void ParaWidows( const SvxWidowsItem& rWidows ) override; + + /// Sfx item RES_PARATR_TABSTOP + virtual void ParaTabStop( const SvxTabStopItem& rTabStop ) override; + + /// Sfx item RES_PARATR_HYPHENZONE + virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) override; + + /// Sfx item RES_PARATR_NUMRULE + virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) override; + + /// Sfx item RES_PARATR_SCRIPTSPACE + virtual void ParaScriptSpace( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_HANGINGPUNCTUATION + virtual void ParaHangingPunctuation( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_FORBIDDEN_RULES + virtual void ParaForbiddenRules( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_VERTALIGN + virtual void ParaVerticalAlign( const SvxParaVertAlignItem& rAlign ) override; + + /// Sfx item RES_PARATR_SNAPTOGRID + virtual void ParaSnapToGrid( const SvxParaGridItem& ) override; + + /// Sfx item RES_FRM_SIZE + virtual void FormatFrameSize( const SwFormatFrameSize& ) override; + + /// Sfx item RES_PAPER_BIN + virtual void FormatPaperBin( const SvxPaperBinItem& ) override; + + /// Sfx item RES_LR_SPACE + virtual void FormatLRSpace( const SvxLRSpaceItem& rLRSpace ) override; + + /// Sfx item RES_UL_SPACE + virtual void FormatULSpace( const SvxULSpaceItem& rULSpace ) override; + + /// Sfx item RES_SURROUND + virtual void FormatSurround( const SwFormatSurround& ) override; + + /// Sfx item RES_VERT_ORIENT + virtual void FormatVertOrientation( const SwFormatVertOrient& ) override; + + /// Sfx item RES_HORI_ORIENT + virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) override; + + /// Sfx item RES_ANCHOR + virtual void FormatAnchor( const SwFormatAnchor& ) override; + + /// Sfx item RES_BACKGROUND + virtual void FormatBackground( const SvxBrushItem& ) override; + + /// Sfx item RES_FILL_STYLE + virtual void FormatFillStyle( const XFillStyleItem& ) override; + + /// Sfx item RES_FILL_GRADIENT + virtual void FormatFillGradient( const XFillGradientItem& ) override; + + /// Sfx item RES_BOX + virtual void FormatBox( const SvxBoxItem& ) override; + + /// Sfx item RES_COL + virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) override; + + /// Sfx item RES_KEEP + virtual void FormatKeep( const SvxFormatKeepItem& ) override; + + /// Sfx item RES_TEXTGRID + virtual void FormatTextGrid( const SwTextGridItem& ) override; + + /// Sfx item RES_LINENUMBER + virtual void FormatLineNumbering( const SwFormatLineNumber& ) override; + + /// Sfx item RES_FRAMEDIR + virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) override; + + /// Sfx item RES_PARATR_GRABBAG + virtual void ParaGrabBag( const SfxGrabBagItem& ) override; + + /// Sfx item RES_CHRATR_GRABBAG + virtual void CharGrabBag( const SfxGrabBagItem& ) override; + + // Sfx item RES_PARATR_OUTLINELEVEL + virtual void ParaOutlineLevel( const SfxUInt16Item& ) override; + + /// Write the expanded field + virtual void WriteExpand( const SwField* pField ) override; + + virtual void RefField( const SwField& rField, const OUString& rRef ) override; + virtual void HiddenField( const SwField& rField ) override; + virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) override; + virtual void PostitField( const SwField* pField ) override; + virtual bool DropdownField( const SwField* pField ) override; + virtual bool PlaceholderField( const SwField* pField ) override; + + virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override; + + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override; + + /// Reference to the export, where to get the data from + DocxExport &m_rExport; + + /// Fast serializer to output the data + ::sax_fastparser::FSHelperPtr m_pSerializer; + + /// DrawingML access + oox::drawingml::DrawingML &m_rDrawingML; + +private: + + void DoWriteBookmarkTagStart(const OUString & bookmarkName); + void DoWriteBookmarkTagEnd(const OUString & bookmarkName); + void DoWriteBookmarksStart(std::vector<OUString>& rStarts); + void DoWriteBookmarksEnd(std::vector<OUString>& rEnds); + void DoWriteBookmarkStartIfExist(sal_Int32 nRunPos); + void DoWriteBookmarkEndIfExist(sal_Int32 nRunPos); + + void DoWritePermissionTagStart(const OUString & permission); + void DoWritePermissionTagEnd(const OUString & permission); + void DoWritePermissionsStart(); + void DoWritePermissionsEnd(); + + void DoWriteAnnotationMarks( ); + void WritePostponedGraphic(); + void WritePostponedMath(const SwOLENode* pObject, sal_Int8 /*nAlign*/); + void WritePostponedFormControl(const SdrObject* pObject); + void WritePostponedActiveXControl(bool bInsideRun); + void WritePostponedDiagram(); + void WritePostponedChart(); + void WritePostponedOLE(); + void WritePostponedDMLDrawing(); + void WritePostponedCustomShape(); + + void WriteSdtBlock(sal_Int32& nSdtPrToken, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes, + rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, + OUString& rSdtPrAlias, + bool bPara); + /// Closes a currently open SDT block. + void EndSdtBlock(); + + void WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang); + void WriteSdtDropDownStart(OUString const& rName, OUString const& rSelected, uno::Sequence<OUString> const& rListItems); + void WriteSdtDropDownEnd(OUString const& rSelected, uno::Sequence<OUString> const& rListItems); + void WriteSdtEnd(); + + void StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun = false ); + void DoWriteCmd( const OUString& rCmd ); + void CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ); + void CmdEndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteRun ); + void EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos ); + void DoWriteFieldRunProperties( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteCombChars = false ); + + static void AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue ); + static void AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nArgs, ... ); + + rtl::Reference<sax_fastparser::FastAttributeList> m_pFontsAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pEastAsianLayoutAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pCharLangAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pSectionSpacingAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSpacingAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pHyperlinkAttrList; + /// If the current SDT around runs should be ended before the current run. + bool m_bEndCharSdt; + /// If an SDT around runs is currently open. + bool m_bStartedCharSdt; + /// If an SDT around paragraphs is currently open. + bool m_bStartedParaSdt; + /// Attributes of the run color + rtl::Reference<sax_fastparser::FastAttributeList> m_pColorAttrList; + sal_uInt8 m_nCharTransparence = 0; + /// Attributes of the paragraph background + rtl::Reference<sax_fastparser::FastAttributeList> m_pBackgroundAttrList; + OUString m_sOriginalBackgroundColor; + OUString m_hyperLinkAnchor; + bool m_endPageRef; + std::unique_ptr<docx::FootnotesList> m_pFootnotesList; + std::unique_ptr<docx::FootnotesList> m_pEndnotesList; + int m_footnoteEndnoteRefTag; + OUString m_footnoteCustomLabel; + std::unique_ptr< const WW8_SepInfo > m_pSectionInfo; + + /// Redline data to remember in the text run. + const SwRedlineData *m_pRedlineData; + + /// Id of the redline + sal_Int32 m_nRedlineId; + + /// Flag indicating that the section properties are being written + bool m_bOpenedSectPr; + /// Did we have a section break in this paragraph? Set by StartSection(), reset by the next StartParagraph(). + bool m_bHadSectPr; + + /// Flag indicating that the Run Text is being written + bool m_bRunTextIsOn; + + /// Flag indicating that the header \ footer are being written + bool m_bWritingHeaderFooter; + bool m_bAnchorLinkedToNode; + + /// Flag indicating that multiple runs of a field are being written + bool m_bWritingField; + + /// Field data to remember in the text run + bool m_bPreventDoubleFieldsHandling; + std::vector< FieldInfos > m_Fields; + OUString m_sFieldBkm; + sal_Int32 m_nNextBookmarkId; + sal_Int32 m_nNextAnnotationMarkId; + + OUString m_sRawText; + + /// Bookmarks to output + std::vector<OUString> m_rBookmarksStart; + std::vector<OUString> m_rBookmarksEnd; + + /// Bookmarks to output at the end + std::vector<OUString> m_rFinalBookmarksStart; + std::vector<OUString> m_rFinalBookmarksEnd; + + /// Bookmarks of the current paragraph + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart; + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd; + + /// Permissions to output + std::vector<OUString> m_rPermissionsStart; + std::vector<OUString> m_rPermissionsEnd; + + /// Annotation marks to output + std::vector<OString> m_rAnnotationMarksStart; + std::vector<OString> m_rAnnotationMarksEnd; + + /// Maps of the bookmarks ids + std::map<OUString, sal_Int32> m_rOpenedBookmarksIds; + + /// Name of the last opened bookmark. + OString m_sLastOpenedBookmark; + + /// Maps of the annotation marks ids + std::map<OString, sal_Int32> m_rOpenedAnnotationMarksIds; + + /// Name of the last opened annotation mark. + OString m_sLastOpenedAnnotationMark; + + /// If there are bookmarks around sequence fields, this map contains the + /// names of these bookmarks for each sequence. + std::map<OUString, std::vector<OString> > m_aSeqBookmarksNames; + + /// GrabBag for text effects like glow, shadow, ... + std::vector<css::beans::PropertyValue> m_aTextEffectsGrabBag; + + /// The current table helper + std::unique_ptr<SwWriteTable> m_xTableWrt; + + ww8::Frame* m_pCurrentFrame; + + bool m_bParagraphOpened; + bool m_bParagraphFrameOpen; + bool m_bIsFirstParagraph; + bool m_bAlternateContentChoiceOpen; + bool m_bPostponedProcessingFly; + + // Remember that a column break has to be opened at the + // beginning of the next paragraph + DocxColBreakStatus m_nColBreakStatus; + + // Remember that a page break has to be opened at the + // beginning of the next paragraph + bool m_bPostponedPageBreak; + + // This paragraph must end with page break + bool m_bPageBreakAfter = false; + + std::vector<ww8::Frame> m_aFramesOfParagraph; + o3tl::sorted_vector<const SwFrameFormat*> m_aFloatingTablesOfParagraph; + sal_Int32 m_nTextFrameLevel; + + // close of hyperlink needed + bool m_closeHyperlinkInThisRun; + bool m_closeHyperlinkInPreviousRun; + bool m_startedHyperlink; + // Count nested HyperLinks + sal_Int32 m_nHyperLinkCount; + sal_Int16 m_nFieldsInHyperlink; + + // If the exported numbering rule defines the outlines + bool m_bExportingOutline; + + struct PostponedGraphic + { + PostponedGraphic( const SwGrfNode* n, Size s, const SdrObject* sObj ) + : grfNode( n ), size( s ), pSdrObj(sObj) {}; + + const SwGrfNode* grfNode; + Size size; + const SdrObject* pSdrObj; + }; + std::unique_ptr< std::vector<PostponedGraphic> > m_pPostponedGraphic; + struct PostponedDiagram + { + PostponedDiagram( const SdrObject* o, const SwFrameFormat* frm ) : object( o ), frame( frm ) {}; + const SdrObject* object; + const SwFrameFormat* frame; + }; + std::unique_ptr< std::vector<PostponedDiagram> > m_pPostponedDiagrams; + + struct PostponedDrawing + { + PostponedDrawing( const SdrObject* sdrObj, const SwFrameFormat* frm) : object( sdrObj ), frame( frm ) {}; + const SdrObject* object; + const SwFrameFormat* frame; + }; + std::unique_ptr< std::vector<PostponedDrawing> > m_pPostponedDMLDrawings; + std::unique_ptr< std::vector<PostponedDrawing> > m_pPostponedCustomShape; + + struct PostponedOLE + { + PostponedOLE( SwOLENode* rObject, const Size& rSize, const SwFlyFrameFormat* rFrame ) : object( rObject ), size( rSize ), frame( rFrame ) {}; + SwOLENode* object; + const Size size; + const SwFlyFrameFormat* frame; + }; + std::unique_ptr< std::vector<PostponedOLE> > m_pPostponedOLEs; + + struct PostponedMathObjects + { + SwOLENode* pMathObject; + sal_Int8 nMathObjAlignment; + }; + std::vector<PostponedMathObjects> m_aPostponedMaths; + /// count charts consistently for unit tests + unsigned int m_nChartCount; + struct PostponedChart + { + PostponedChart( const SdrObject* sdrObject, const Size& rSize, const SwFlyFrameFormat* rFrame ) : object(sdrObject), size(rSize), frame(rFrame) {}; + const SdrObject* object; + const Size size; + const SwFlyFrameFormat* frame; + }; + std::vector<PostponedChart> m_aPostponedCharts; + std::vector<const SdrObject*> m_aPostponedFormControls; + std::vector<PostponedDrawing> m_aPostponedActiveXControls; + const SwField* pendingPlaceholder; + /// Maps postit fields to ID's, used in commentRangeStart/End, commentReference and comment.xml. + std::vector< std::pair<const SwPostItField*, sal_Int32> > m_postitFields; + /// Number of postit fields which already have a commentReference written. + unsigned int m_postitFieldsMaxId; + int m_anchorId; + int m_nextFontId; + struct EmbeddedFontRef + { + OString relId; + OString fontKey; + }; + + std::unique_ptr<TableReference> m_tableReference; + + std::map< OUString, EmbeddedFontRef > fontFilesMap; // font file url to data + + // Remember first cell (used for default borders/margins) of each table + std::vector<ww8::WW8TableNodeInfoInner::Pointer_t> tableFirstCells; + // Remember last open and closed cells on each level + std::vector<sal_Int32> lastOpenCell; + std::vector<sal_Int32> lastClosedCell; + + std::optional<css::drawing::FillStyle> m_oFillStyle; + /// If FormatBox() already handled fill style / gradient. + bool m_bIgnoreNextFill; + + editeng::WordPageMargins m_pageMargins; + + std::shared_ptr<DocxTableStyleExport> m_pTableStyleExport; + // flag to check if auto spacing was set in original file + bool m_bParaBeforeAutoSpacing,m_bParaAfterAutoSpacing; + // store hardcoded value which was set during import. + sal_Int32 m_nParaBeforeSpacing,m_nParaAfterSpacing; + + /// RelId <-> Graphic* cache, so that in case of alternate content, the same graphic only gets written once. + std::stack< std::map<const Graphic*, OString> > m_aRelIdCache; + /// RelId <-> BitmapChecksum cache, similar to m_aRelIdCache, but used for non-Writer graphics, handled in oox. + std::stack< std::map<BitmapChecksum, OUString> > m_aSdrRelIdCache; + + /// members to control the existence of grabbagged SDT properties in the paragraph + sal_Int32 m_nParagraphSdtPrToken; + rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrTokenChildren; + rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrTokenAttributes; + rtl::Reference<sax_fastparser::FastAttributeList> m_pParagraphSdtPrDataBindingAttrs; + /// members to control the existence of grabbagged SDT properties in the text run + sal_Int32 m_nRunSdtPrToken; + /// State of the Fly at current position + FlyProcessingState m_nStateOfFlyFrame; + rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrTokenChildren; + rtl::Reference<sax_fastparser::FastAttributeList> m_pRunSdtPrDataBindingAttrs; + /// Value of the <w:alias> paragraph SDT element. + OUString m_aParagraphSdtPrAlias; + /// Same as m_aParagraphSdtPrAlias, but its content is available till the SDT is closed. + OUString m_aStartedParagraphSdtPrAlias; + OUString m_aRunSdtPrAlias; + /// Currently paragraph SDT has a <w:id> child element. + bool m_bParagraphSdtHasId; + + std::vector<std::map<SvxBoxItemLine, css::table::BorderLine2>> m_aTableStyleConfs; + +public: + DocxAttributeOutput( DocxExport &rExport, const ::sax_fastparser::FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML ); + + virtual ~DocxAttributeOutput() override; + + /// Return the right export class. + virtual DocxExport& GetExport() override; + const DocxExport& GetExport() const { return const_cast< DocxAttributeOutput* >( this )->GetExport(); } + + /// For e.g. the output of the styles, we need to switch the serializer to another one. + void SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer ); + + /// Occasionally need to use this serializer from the outside + const ::sax_fastparser::FSHelperPtr& GetSerializer( ) const { return m_pSerializer; } + + /// Do we have any footnotes? + bool HasFootnotes() const; + + /// Do we have any endnotes? + bool HasEndnotes() const; + + /// Output the content of the footnotes.xml resp. endnotes.xml + void FootnotesEndnotes( bool bFootnotes ); + + /// writes the footnotePr/endnotePr (depending on tag) section + static void WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag, const SwEndNoteInfo& info, int listtag ); + + bool HasPostitFields() const; + void WritePostitFields(); + + /// VMLTextExport + virtual void WriteOutliner(const OutlinerParaObject& rParaObj) override; + virtual void WriteVMLTextBox(css::uno::Reference<css::drawing::XShape> xShape) override; + /// DMLTextExport + virtual void WriteTextBox(css::uno::Reference<css::drawing::XShape> xShape) override; + virtual OUString FindRelId(BitmapChecksum nChecksum) override; + virtual void CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId) override; + virtual oox::drawingml::DrawingML& GetDrawingML() override; + virtual void MaybeOutputBrushItem(SfxItemSet const&) override; + + void BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) override; + + void SetWritingHeaderFooter( bool bWritingHeaderFooter ) { m_bWritingHeaderFooter = bWritingHeaderFooter; } + bool GetWritingHeaderFooter( ) const { return m_bWritingHeaderFooter; } + void SetAlternateContentChoiceOpen( bool bAltContentChoiceOpen ) { m_bAlternateContentChoiceOpen = bAltContentChoiceOpen; } + bool IsAlternateContentChoiceOpen( ) const { return m_bAlternateContentChoiceOpen; } + void GetSdtEndBefore(const SdrObject* pSdrObj); + bool IsFirstParagraph() const { return m_bIsFirstParagraph; } + + /// Stores the table export state to the passed context and resets own state. + void pushToTableExportContext(DocxTableExportContext& rContext); + /// Restores from the remembered state. + void popFromTableExportContext(DocxTableExportContext const & rContext); +}; + +/** +* All the information that should be stashed away when we're in the middle of +* of a table export and still have to do something else, e.g. export a shape. +*/ +struct DocxTableExportContext +{ + DocxAttributeOutput& m_rOutput; + ww8::WW8TableInfo::Pointer_t m_pTableInfo; + bool m_bTableCellOpen; + bool m_bStartedParaSdt; + sal_uInt32 m_nTableDepth; + DocxTableExportContext(DocxAttributeOutput& rOutput) : m_rOutput(rOutput) { m_rOutput.pushToTableExportContext(*this); } + ~DocxTableExportContext() { m_rOutput.popFromTableExportContext(*this); } +}; + +namespace docx { + +std::unique_ptr<sax_fastparser::FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround); + +} + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXATTRIBUTEOUTPUT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx new file mode 100644 index 000000000..849c98784 --- /dev/null +++ b/sw/source/filter/ww8/docxexport.cxx @@ -0,0 +1,1841 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "docxexport.hxx" +#include "docxexportfilter.hxx" +#include "docxattributeoutput.hxx" +#include "docxsdrexport.hxx" +#include "docxhelper.hxx" + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/sax/XSAXSerializable.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <oox/token/namespaces.hxx> +#include <oox/token/tokens.hxx> +#include <oox/export/drawingml.hxx> +#include <oox/export/vmlexport.hxx> +#include <oox/export/chartexport.hxx> +#include <oox/export/shapes.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/token/relationship.hxx> +#include <oox/ole/olestorage.hxx> +#include <oox/ole/olehelper.hxx> + +#include <map> +#include <algorithm> + +#include <IMark.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <docsh.hxx> +#include <ndtxt.hxx> +#include "wrtww8.hxx" +#include <fmtline.hxx> +#include <fmtpdsc.hxx> +#include <frmfmt.hxx> +#include <section.hxx> +#include <ftninfo.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> +#include <swdbdata.hxx> + +#include <editeng/unoprnms.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/hyphenzoneitem.hxx> + +#include <viewsh.hxx> +#include <viewopt.hxx> + +#include "ww8scan.hxx" +#include <oox/token/properties.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/storagehelper.hxx> +#include <o3tl/any.hxx> +#include <sal/log.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <tools/diagnose_ex.h> + +using namespace sax_fastparser; +using namespace ::comphelper; +using namespace ::com::sun::star; +using namespace ::oox; + +using oox::vml::VMLExport; + +using sw::mark::IMark; + +AttributeOutputBase& DocxExport::AttrOutput() const +{ + return *m_pAttrOutput; +} + +DocxAttributeOutput& DocxExport::DocxAttrOutput() const +{ + return *m_pAttrOutput; +} + +MSWordSections& DocxExport::Sections() const +{ + return *m_pSections; +} + +bool DocxExport::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) +{ + // TODO FIXME is this actually true for docx? - this is ~copied from WW8 + if ( nScript == i18n::ScriptType::ASIAN ) + { + // for asian in ww8, there is only one fontsize + // and one fontstyle (posture/weight) + switch ( nWhich ) + { + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + return false; + default: + break; + } + } + else if ( nScript != i18n::ScriptType::COMPLEX ) + { + // for western in ww8, there is only one fontsize + // and one fontstyle (posture/weight) + switch ( nWhich ) + { + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + return false; + default: + break; + } + } + return true; +} + +void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen ) +{ + std::vector< OUString > aStarts; + std::vector< OUString > aEnds; + + IMarkVector aMarks; + if ( GetBookmarks( rNode, nCurrentPos, nCurrentPos + nLen, aMarks ) ) + { + for ( IMark* pMark : aMarks ) + { + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + if ( nStart == nCurrentPos ) + aStarts.push_back( pMark->GetName() ); + + if ( nEnd == nCurrentPos ) + aEnds.push_back( pMark->GetName() ); + } + } + + const OUString& aStr( rNode.GetText() ); + const sal_Int32 nEnd = aStr.getLength(); + + if ( nCurrentPos == nEnd ) + m_pAttrOutput->WriteFinalBookmarks_Impl( aStarts, aEnds ); + else + m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds ); +} + +void DocxExport::AppendBookmark( const OUString& rName ) +{ + std::vector< OUString > aStarts; + std::vector< OUString > aEnds; + + aStarts.push_back( rName ); + aEnds.push_back( rName ); + + m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds ); +} + +void DocxExport::AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) +{ + std::vector< OUString > aStarts; + std::vector< OUString > aEnds; + + IMarkVector aMarks; + if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks)) + { + for ( IMark* pMark : aMarks ) + { + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + if ( nStart == nCurrentPos ) + aStarts.push_back( pMark->GetName() ); + + if ( nEnd == nCurrentPos ) + aEnds.push_back( pMark->GetName() ); + } + } + + m_pAttrOutput->WriteAnnotationMarks_Impl( aStarts, aEnds ); +} + +void DocxExport::ExportGrfBullet(const SwTextNode&) +{ + // Just collect the bullets for now, numbering.xml is not yet started. + CollectGrfsOfBullets(); +} + +OString DocxExport::AddRelation( const OUString& rType, const OUString& rTarget ) +{ + OUString sId = m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + rType, rTarget, true ); + + return sId.toUtf8(); +} + +bool DocxExport::DisallowInheritingOutlineNumbering( const SwFormat& rFormat ) +{ + bool bRet( false ); + + if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false)) + { + if (const SwFormat *pParent = rFormat.DerivedFrom()) + { + if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle()) + { + ::sax_fastparser::FSHelperPtr pSerializer = m_pAttrOutput->GetSerializer( ); + // Level 9 disables the outline + pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), "9"); + + bRet = true; + } + } + } + + return bRet; +} + +void DocxExport::WriteHeadersFooters( sal_uInt8 nHeadFootFlags, + const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode ) +{ + m_nHeadersFootersInSection = 1; + + // document setting indicating the requirement of EVEN and ODD for both headers and footers + if ( nHeadFootFlags & ( nsHdFtFlags::WW8_FOOTER_EVEN | nsHdFtFlags::WW8_HEADER_EVEN )) + m_aSettings.evenAndOddHeaders = true; + + // Turn ON flag for 'Writing Headers \ Footers' + m_pAttrOutput->SetWritingHeaderFooter( true ); + + // headers + if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN ) + WriteHeaderFooter( &rLeftFormat, true, "even" ); + else if ( m_aSettings.evenAndOddHeaders ) + { + if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD ) + WriteHeaderFooter( &rFormat, true, "even" ); + else if ( m_bHasHdr && nBreakCode == 2 ) + WriteHeaderFooter( nullptr, true, "even" ); + } + + if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD ) + WriteHeaderFooter( &rFormat, true, "default" ); + + if ( nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST ) + WriteHeaderFooter( &rFirstPageFormat, true, "first" ); + + if( (nHeadFootFlags & (nsHdFtFlags::WW8_HEADER_EVEN + | nsHdFtFlags::WW8_HEADER_ODD + | nsHdFtFlags::WW8_HEADER_FIRST)) == 0 + && m_bHasHdr && nBreakCode == 2 ) // 2: nexPage + WriteHeaderFooter( nullptr, true, "default" ); + + + // footers + if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN ) + WriteHeaderFooter( &rLeftFormat, false, "even" ); + else if ( m_aSettings.evenAndOddHeaders ) + { + if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD ) + WriteHeaderFooter( &rFormat, false, "even" ); + else if ( m_bHasFtr && nBreakCode == 2 ) + WriteHeaderFooter( nullptr, false, "even"); + } + + if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD ) + WriteHeaderFooter( &rFormat, false, "default" ); + + if ( nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST ) + WriteHeaderFooter( &rFirstPageFormat, false, "first" ); + + if( (nHeadFootFlags & (nsHdFtFlags::WW8_FOOTER_EVEN + | nsHdFtFlags::WW8_FOOTER_ODD + | nsHdFtFlags::WW8_FOOTER_FIRST)) == 0 + && m_bHasFtr && nBreakCode == 2 ) // 2: nexPage + WriteHeaderFooter( nullptr, false, "default"); + + // Turn OFF flag for 'Writing Headers \ Footers' + m_pAttrOutput->SetWritingHeaderFooter( false ); +} + +void DocxExport::OutputField( const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd, FieldFlags nMode ) +{ + m_pAttrOutput->WriteField_Impl( pField, eFieldType, rFieldCmd, nMode ); +} + +void DocxExport::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) +{ + m_pAttrOutput->WriteFormData_Impl( rFieldmark ); +} + +void DocxExport::WriteHyperlinkData( const ::sw::mark::IFieldmark& /*rFieldmark*/ ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "TODO DocxExport::WriteHyperlinkData()\n" ); +#endif +} + +void DocxExport::DoComboBox(const OUString& rName, + const OUString& rHelp, + const OUString& rToolTip, + const OUString& rSelected, + const uno::Sequence<OUString>& rListItems) +{ + m_pDocumentFS->startElementNS(XML_w, XML_ffData); + + m_pDocumentFS->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName.toUtf8()); + + m_pDocumentFS->singleElementNS(XML_w, XML_enabled); + + if ( !rHelp.isEmpty() ) + m_pDocumentFS->singleElementNS(XML_w, XML_helpText, FSNS(XML_w, XML_val), rHelp.toUtf8()); + + if ( !rToolTip.isEmpty() ) + m_pDocumentFS->singleElementNS( XML_w, XML_statusText, + FSNS( XML_w, XML_val ), rToolTip.toUtf8() ); + + m_pDocumentFS->startElementNS(XML_w, XML_ddList); + + // Output the 0-based index of the selected value + sal_Int32 nId = comphelper::findValue(rListItems, rSelected); + if (nId == -1) + nId = 0; + + m_pDocumentFS->singleElementNS(XML_w, XML_result, FSNS(XML_w, XML_val), OString::number(nId)); + + // unfortunately Word 2013 refuses to load DOCX with more than 25 listEntry + SAL_WARN_IF(25 < rListItems.getLength(), "sw.ww8", "DocxExport::DoComboBox data loss with more than 25 entries"); + auto const nSize(std::min(sal_Int32(25), rListItems.getLength())); + for (auto i = 0; i < nSize; ++i) + { + m_pDocumentFS->singleElementNS( XML_w, XML_listEntry, + FSNS(XML_w, XML_val), rListItems[i].toUtf8() ); + } + + m_pDocumentFS->endElementNS( XML_w, XML_ddList ); + + m_pDocumentFS->endElementNS( XML_w, XML_ffData ); +} + +void DocxExport::DoFormText(const SwInputField* pField) +{ + assert(pField); + const OUString sStr = FieldString(ww::eFILLIN) + "\"" + pField->GetPar2() + "\""; + OutputField(pField, ww::eFILLIN, sStr); +} + +OString DocxExport::OutputChart( uno::Reference< frame::XModel > const & xModel, sal_Int32 nCount, ::sax_fastparser::FSHelperPtr const & m_pSerializer ) +{ + OUString aFileName = "charts/chart" + OUString::number(nCount) + ".xml"; + OUString sId = m_pFilter->addRelation( m_pSerializer->getOutputStream(), + oox::getRelationship(Relationship::CHART), + aFileName ); + aFileName = "word/charts/chart" + OUString::number(nCount) + ".xml"; + ::sax_fastparser::FSHelperPtr pChartFS = + m_pFilter->openFragmentStreamWithSerializer( aFileName, + "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" ); + + oox::drawingml::ChartExport aChartExport(XML_w, pChartFS, xModel, m_pFilter, oox::drawingml::DOCUMENT_DOCX); + css::uno::Reference<css::util::XModifiable> xModifiable(xModel, css::uno::UNO_QUERY); + const bool bOldModified = xModifiable && xModifiable->isModified(); + aChartExport.ExportContent(); + if (!bOldModified && xModifiable && xModifiable->isModified()) + // tdf#134973: the model could get modified: e.g., calling XChartDocument::getSubTitle(), + // which creates the object if absent, and sets the modified state. + xModifiable->setModified(bOldModified); + return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 ); +} + +OString DocxExport::WriteOLEObject(SwOLEObj& rObject, OUString & io_rProgID) +{ + uno::Reference <embed::XEmbeddedObject> xObj( rObject.GetOleRef() ); + uno::Reference<uno::XComponentContext> const xContext( + GetFilter().getComponentContext()); + + OUString sMediaType; + OUString sRelationType; + OUString sSuffix; + const char * pProgID(nullptr); + + uno::Reference<io::XInputStream> const xInStream = + oox::GetOLEObjectStream(xContext, xObj, io_rProgID, + sMediaType, sRelationType, sSuffix, pProgID); + + if (!xInStream.is()) + { + return OString(); + } + + assert(!sMediaType.isEmpty()); + assert(!sRelationType.isEmpty()); + assert(!sSuffix.isEmpty()); + OUString sFileName = "embeddings/oleObject" + OUString::number( ++m_nOLEObjects ) + "." + sSuffix; + uno::Reference<io::XOutputStream> const xOutStream = + GetFilter().openFragmentStream("word/" + sFileName, sMediaType); + assert(xOutStream.is()); // no reason why that could fail + + try + { + comphelper::OStorageHelper::CopyInputToOutput(xInStream, xOutStream); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.ww8", "DocxExport::WriteOLEObject"); + return OString(); + } + + OUString const sId = m_pFilter->addRelation( GetFS()->getOutputStream(), + sRelationType, sFileName ); + if (pProgID) + { + io_rProgID = OUString::createFromAscii(pProgID); + } + + return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 ); +} + +std::pair<OString, OString> DocxExport::WriteActiveXObject(const uno::Reference<drawing::XShape>& rxShape, + const uno::Reference<awt::XControlModel>& rxControlModel) +{ + ++m_nActiveXControls; + + // Write out ActiveX binary + const OUString sBinaryFileName = "word/activeX/activeX" + OUString::number(m_nActiveXControls) + ".bin"; + + OString sGUID; + OString sName; + uno::Reference<io::XStream> xOutStorage(m_pFilter->openFragmentStream(sBinaryFileName, "application/vnd.ms-office.activeX"), uno::UNO_QUERY); + if(xOutStorage.is()) + { + oox::ole::OleStorage aOleStorage(m_pFilter->getComponentContext(), xOutStorage, false); + uno::Reference<io::XOutputStream> xOutputStream(aOleStorage.openOutputStream("contents"), uno::UNO_SET_THROW); + uno::Reference< css::frame::XModel > xModel( m_pDoc->GetDocShell() ? m_pDoc->GetDocShell()->GetModel() : nullptr ); + oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, rxControlModel); + if ( !exportHelper.isValid() ) + return std::make_pair<OString, OString>(OString(), OString()); + sGUID = OUStringToOString(exportHelper.getGUID(), RTL_TEXTENCODING_UTF8); + sName = OUStringToOString(exportHelper.getName(), RTL_TEXTENCODING_UTF8); + exportHelper.exportControl(xOutputStream, rxShape->getSize(), true); + aOleStorage.commit(); + } + + // Write out ActiveX fragment + const OUString sXMLFileName = "word/activeX/activeX" + OUString::number( m_nActiveXControls ) + ".xml"; + ::sax_fastparser::FSHelperPtr pActiveXFS = m_pFilter->openFragmentStreamWithSerializer(sXMLFileName, "application/vnd.ms-office.activeX+xml" ); + + const OUString sBinaryId = m_pFilter->addRelation( pActiveXFS->getOutputStream(), + oox::getRelationship(Relationship::ACTIVEXCONTROLBINARY), + sBinaryFileName.copy(sBinaryFileName.lastIndexOf("/") + 1) ); + + pActiveXFS->singleElementNS(XML_ax, XML_ocx, + FSNS(XML_xmlns, XML_ax), m_pFilter->getNamespaceURL(OOX_NS(ax)).toUtf8(), + FSNS(XML_xmlns, XML_r), m_pFilter->getNamespaceURL(OOX_NS(officeRel)).toUtf8(), + FSNS(XML_ax, XML_classid), "{" + sGUID + "}", + FSNS(XML_ax, XML_persistence), "persistStorage", + FSNS(XML_r, XML_id), sBinaryId.toUtf8()); + + OString sXMLId = OUStringToOString(m_pFilter->addRelation(m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::CONTROL), + sXMLFileName.copy(sBinaryFileName.indexOf("/") + 1)), + RTL_TEXTENCODING_UTF8); + + return std::pair<OString, OString>(sXMLId, sName); +} + +void DocxExport::OutputDML(uno::Reference<drawing::XShape> const & xShape) +{ + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); + sal_Int32 nNamespace = XML_wps; + if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + nNamespace = XML_wpg; + else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) + nNamespace = XML_pic; + oox::drawingml::ShapeExport aExport(nNamespace, m_pAttrOutput->GetSerializer(), nullptr, m_pFilter, oox::drawingml::DOCUMENT_DOCX, m_pAttrOutput.get()); + aExport.WriteShape(xShape); +} + +ErrCode DocxExport::ExportDocument_Impl() +{ + // Set the 'Reviewing' flags in the settings structure + m_aSettings.revisionView = m_bOrigShowChanges; + m_aSettings.trackRevisions = bool( RedlineFlags::On & m_nOrigRedlineFlags ); + + InitStyles(); + + // init sections + m_pSections.reset(new MSWordSections( *this )); + + // Make sure images are counted from one, even when exporting multiple documents. + oox::drawingml::DrawingML::ResetCounters(); + + WriteMainText(); + + WriteFootnotesEndnotes(); + + WritePostitFields(); + + WriteNumbering(); + + WriteFonts(); + + WriteSettings(); + + WriteTheme(); + + WriteGlossary(); + + WriteCustomXml(); + + WriteEmbeddings(); + + WriteVBA(); + + m_aLinkedTextboxesHelper.clear(); //final cleanup + m_pStyles.reset(); + m_pSections.reset(); + + return ERRCODE_NONE; +} + +void DocxExport::AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) +{ + AttrOutput().SectionBreak( msword::PageBreak, false, m_pSections->CurrentSectionInfo() ); + m_pSections->AppendSection( pPageDesc, pFormat, nLnNum, m_pAttrOutput->IsFirstParagraph() ); +} + +void DocxExport::OutputEndNode( const SwEndNode& rEndNode ) +{ + MSWordExportBase::OutputEndNode( rEndNode ); + + if ( TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsSectionNode() ) + { + // this originally comes from WW8Export::WriteText(), and looks like it + // could have some code common with SectionNode()... + + const SwSection& rSect = rEndNode.StartOfSectionNode()->GetSectionNode()->GetSection(); + if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() ) + m_bStartTOX = false; + + SwNodeIndex aIdx( rEndNode, 1 ); + const SwNode& rNd = aIdx.GetNode(); + if ( rNd.IsEndNode() && rNd.StartOfSectionNode()->IsSectionNode() ) + return; + + bool isInTable = IsInTable(); + if ( !rNd.IsSectionNode() && isInTable ) // No sections in table + { + const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent(); + if( !pParentFormat ) + pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)); + + sal_uLong nRstLnNum; + if( rNd.IsContentNode() ) + nRstLnNum = rNd.GetContentNode()->GetSwAttrSet().GetLineNumber().GetStartValue(); + else + nRstLnNum = 0; + + AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum ); + } + else + { + AttrOutput().SectionBreaks( rEndNode ); + } + } + else if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode()) + // End node of a table: see if a section break should be written after the table. + AttrOutput().SectionBreaks(rEndNode); +} + +void DocxExport::OutputGrfNode( const SwGrfNode& ) +{ + SAL_INFO("sw.ww8", "TODO DocxExport::OutputGrfNode( const SwGrfNode& )" ); +} + +void DocxExport::OutputOLENode( const SwOLENode& ) +{ + SAL_INFO("sw.ww8", "TODO DocxExport::OutputOLENode( const SwOLENode& )" ); +} + +void DocxExport::OutputLinkedOLE( const OUString& ) +{ + // Nothing to implement here: WW8 only +} + +sal_uLong DocxExport::ReplaceCr( sal_uInt8 ) +{ + // Completely unused for Docx export... only here for code sharing + // purpose with binary export + return 0; +} + +void DocxExport::PrepareNewPageDesc( const SfxItemSet* pSet, + const SwNode& rNd, const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc ) +{ + // tell the attribute output that we are ready to write the section + // break [has to be output inside paragraph properties] + AttrOutput().SectionBreak( msword::PageBreak, false, m_pSections->CurrentSectionInfo() ); + + const SwSectionFormat* pFormat = GetSectionFormat( rNd ); + const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd ); + + OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." ); + + if ( pNewPgDescFormat ) + { + m_pSections->AppendSection( *pNewPgDescFormat, rNd, pFormat, nLnNm ); + } + else if ( pNewPgDesc ) + { + m_pSections->AppendSection( pNewPgDesc, rNd, pFormat, nLnNm ); + } + +} + +void DocxExport::InitStyles() +{ + m_pStyles.reset(new MSWordStyles( *this, /*bListStyles =*/ true )); + + // setup word/styles.xml and the relations + content type + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::STYLES), + "styles.xml" ); + + ::sax_fastparser::FSHelperPtr pStylesFS = + m_pFilter->openFragmentStreamWithSerializer( "word/styles.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" ); + + // switch the serializer to redirect the output to word/styles.xml + m_pAttrOutput->SetSerializer( pStylesFS ); + + // do the work + m_pStyles->OutputStylesTable(); + + // switch the serializer back + m_pAttrOutput->SetSerializer( m_pDocumentFS ); +} + +void DocxExport::WriteFootnotesEndnotes() +{ + if ( m_pAttrOutput->HasFootnotes() ) + { + // setup word/styles.xml and the relations + content type + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::FOOTNOTES), + "footnotes.xml" ); + + ::sax_fastparser::FSHelperPtr pFootnotesFS = + m_pFilter->openFragmentStreamWithSerializer( "word/footnotes.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" ); + + // switch the serializer to redirect the output to word/footnotes.xml + m_pAttrOutput->SetSerializer( pFootnotesFS ); + // tdf#99227 + m_pSdrExport->setSerializer( pFootnotesFS ); + // tdf#107969 + m_pVMLExport->SetFS(pFootnotesFS); + + // do the work + m_pAttrOutput->FootnotesEndnotes( true ); + + // switch the serializer back + m_pVMLExport->SetFS(m_pDocumentFS); + m_pSdrExport->setSerializer( m_pDocumentFS ); + m_pAttrOutput->SetSerializer( m_pDocumentFS ); + } + + if ( m_pAttrOutput->HasEndnotes() ) + { + // setup word/styles.xml and the relations + content type + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::ENDNOTES), + "endnotes.xml" ); + + ::sax_fastparser::FSHelperPtr pEndnotesFS = + m_pFilter->openFragmentStreamWithSerializer( "word/endnotes.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" ); + + // switch the serializer to redirect the output to word/endnotes.xml + m_pAttrOutput->SetSerializer( pEndnotesFS ); + // tdf#99227 + m_pSdrExport->setSerializer( pEndnotesFS ); + // tdf#107969 + m_pVMLExport->SetFS(pEndnotesFS); + + // do the work + m_pAttrOutput->FootnotesEndnotes( false ); + + // switch the serializer back + m_pVMLExport->SetFS(m_pDocumentFS); + m_pSdrExport->setSerializer( m_pDocumentFS ); + m_pAttrOutput->SetSerializer( m_pDocumentFS ); + } +} + +void DocxExport::WritePostitFields() +{ + if ( m_pAttrOutput->HasPostitFields() ) + { + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTS), + "comments.xml" ); + + ::sax_fastparser::FSHelperPtr pPostitFS = + m_pFilter->openFragmentStreamWithSerializer( "word/comments.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" ); + + pPostitFS->startElementNS( XML_w, XML_comments, MainXmlNamespaces()); + m_pAttrOutput->SetSerializer( pPostitFS ); + m_pAttrOutput->WritePostitFields(); + m_pAttrOutput->SetSerializer( m_pDocumentFS ); + pPostitFS->endElementNS( XML_w, XML_comments ); + } +} + +void DocxExport::WriteNumbering() +{ + if ( !m_pUsedNumTable ) + return; // no numbering is used + + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::NUMBERING), + "numbering.xml" ); + + ::sax_fastparser::FSHelperPtr pNumberingFS = m_pFilter->openFragmentStreamWithSerializer( "word/numbering.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml" ); + + // switch the serializer to redirect the output to word/numbering.xml + m_pAttrOutput->SetSerializer( pNumberingFS ); + m_pDrawingML->SetFS( pNumberingFS ); + + pNumberingFS->startElementNS( XML_w, XML_numbering, + FSNS( XML_xmlns, XML_w ), m_pFilter->getNamespaceURL(OOX_NS(doc)).toUtf8(), + FSNS( XML_xmlns, XML_o ), m_pFilter->getNamespaceURL(OOX_NS(vmlOffice)).toUtf8(), + FSNS( XML_xmlns, XML_r ), m_pFilter->getNamespaceURL(OOX_NS(officeRel)).toUtf8(), + FSNS( XML_xmlns, XML_v ), m_pFilter->getNamespaceURL(OOX_NS(vml)).toUtf8(), + FSNS( XML_xmlns, XML_mc ), m_pFilter->getNamespaceURL(OOX_NS(mce)).toUtf8(), + FSNS( XML_xmlns, XML_w14 ), m_pFilter->getNamespaceURL(OOX_NS(w14)).toUtf8(), + FSNS( XML_mc, XML_Ignorable ), "w14" ); + + BulletDefinitions(); + + AbstractNumberingDefinitions(); + + NumberingDefinitions(); + + pNumberingFS->endElementNS( XML_w, XML_numbering ); + + // switch the serializer back + m_pDrawingML->SetFS( m_pDocumentFS ); + m_pAttrOutput->SetSerializer( m_pDocumentFS ); +} + +void DocxExport::WriteHeaderFooter( const SwFormat* pFormat, bool bHeader, const char* pType ) +{ + // setup the xml stream + OUString aRelId; + ::sax_fastparser::FSHelperPtr pFS; + if ( bHeader ) + { + OUString aName( "header" + OUString::number( ++m_nHeaders ) + ".xml" ); + + aRelId = m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::HEADER), + aName ); + + pFS = m_pFilter->openFragmentStreamWithSerializer( "word/" + aName, + "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml" ); + + pFS->startElementNS( XML_w, XML_hdr, MainXmlNamespaces()); + } + else + { + OUString aName( "footer" + OUString::number( ++m_nFooters ) + ".xml" ); + + aRelId = m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::FOOTER), + aName ); + + pFS = m_pFilter->openFragmentStreamWithSerializer( "word/" + aName, + "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml" ); + + pFS->startElementNS( XML_w, XML_ftr, MainXmlNamespaces()); + } + + // switch the serializer to redirect the output to word/styles.xml + m_pAttrOutput->SetSerializer( pFS ); + m_pVMLExport->SetFS( pFS ); + m_pSdrExport->setSerializer(pFS); + SetFS( pFS ); + { + DocxTableExportContext aTableExportContext(*m_pAttrOutput); + //When the stream changes the cache which is maintained for the graphics in case of alternate content is not cleared. + //So clearing the alternate content graphic cache. + m_pAttrOutput->PushRelIdCache(); + // do the work + if (pFormat == nullptr) + AttrOutput().EmptyParagraph(); + else + WriteHeaderFooterText(*pFormat, bHeader); + m_pAttrOutput->PopRelIdCache(); + m_pAttrOutput->EndParaSdtBlock(); + } + + // switch the serializer back + m_pAttrOutput->SetSerializer( m_pDocumentFS ); + m_pVMLExport->SetFS( m_pDocumentFS ); + m_pSdrExport->setSerializer(m_pDocumentFS); + SetFS( m_pDocumentFS ); + + // close the tag + sal_Int32 nReference; + if ( bHeader ) + { + pFS->endElementNS( XML_w, XML_hdr ); + nReference = XML_headerReference; + } + else + { + pFS->endElementNS( XML_w, XML_ftr ); + nReference = XML_footerReference; + } + + // and write the reference + m_pDocumentFS->singleElementNS( XML_w, nReference, + FSNS( XML_w, XML_type ), pType, + FSNS( XML_r, XML_id ), aRelId.toUtf8() ); +} + +void DocxExport::WriteFonts() +{ + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::FONTTABLE), + "fontTable.xml" ); + + ::sax_fastparser::FSHelperPtr pFS = m_pFilter->openFragmentStreamWithSerializer( + "word/fontTable.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" ); + + pFS->startElementNS( XML_w, XML_fonts, + FSNS( XML_xmlns, XML_w ), m_pFilter->getNamespaceURL(OOX_NS(doc)).toUtf8(), + FSNS( XML_xmlns, XML_r ), m_pFilter->getNamespaceURL(OOX_NS(officeRel)).toUtf8() ); + + // switch the serializer to redirect the output to word/styles.xml + m_pAttrOutput->SetSerializer( pFS ); + + // do the work + m_aFontHelper.WriteFontTable( *m_pAttrOutput ); + + // switch the serializer back + m_pAttrOutput->SetSerializer( m_pDocumentFS ); + + pFS->endElementNS( XML_w, XML_fonts ); +} + +void DocxExport::WriteProperties( ) +{ + // Write the core properties + SwDocShell* pDocShell( m_pDoc->GetDocShell( ) ); + uno::Reference<document::XDocumentProperties> xDocProps; + bool bSecurityOptOpenReadOnly = false; + if ( pDocShell ) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel( ), uno::UNO_QUERY ); + xDocProps = xDPS->getDocumentProperties(); + bSecurityOptOpenReadOnly = pDocShell->IsSecurityOptOpenReadOnly(); + } + + m_pFilter->exportDocumentProperties( xDocProps, bSecurityOptOpenReadOnly ); +} + +void DocxExport::WriteDocVars(const sax_fastparser::FSHelperPtr& pFS) +{ + SwDocShell* pDocShell = m_pDoc->GetDocShell(); + if (!pDocShell) + { + return; + } + + uno::Reference<text::XTextFieldsSupplier> xModel(pDocShell->GetModel(), uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xTextFieldMasters = xModel->getTextFieldMasters(); + uno::Sequence<rtl::OUString> aMasterNames = xTextFieldMasters->getElementNames(); + if (!aMasterNames.hasElements()) + { + return; + } + + // Only write docVars if there will be at least a single docVar. + bool bStarted = false; + const OUStringLiteral aPrefix("com.sun.star.text.fieldmaster.User."); + for (const auto& rMasterName : std::as_const(aMasterNames)) + { + if (!rMasterName.startsWith(aPrefix)) + { + // Not a user field. + continue; + } + + uno::Reference<beans::XPropertySet> xField; + xTextFieldMasters->getByName(rMasterName) >>= xField; + if (!xField.is()) + { + continue; + } + + OUString aKey = rMasterName.copy(aPrefix.getLength()); + OUString aValue; + xField->getPropertyValue("Content") >>= aValue; + if (!bStarted) + { + bStarted = true; + pFS->startElementNS(XML_w, XML_docVars); + } + pFS->singleElementNS(XML_w, XML_docVar, FSNS(XML_w, XML_name), aKey.toUtf8(), + FSNS(XML_w, XML_val), aValue.toUtf8()); + } + + if (bStarted) + { + pFS->endElementNS(XML_w, XML_docVars); + } +} + +static auto +WriteCompat(SwDoc const& rDoc, ::sax_fastparser::FSHelperPtr const& rpFS, + sal_Int32 & rTargetCompatibilityMode) -> void +{ + if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING)) + { + rpFS->singleElementNS(XML_w, XML_noLeading); + if (rTargetCompatibilityMode > 14) + { // Word ignores noLeading in compatibilityMode 15 + rTargetCompatibilityMode = 14; + } + } + // Do not justify lines with manual break + if (rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK)) + { + rpFS->singleElementNS(XML_w, XML_doNotExpandShiftReturn); + } +} + +void DocxExport::WriteSettings() +{ + SwViewShell *pViewShell(m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()); + if( !pViewShell && !m_aSettings.hasData() && !m_pAttrOutput->HasFootnotes() && !m_pAttrOutput->HasEndnotes()) + return; + + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::SETTINGS), + "settings.xml" ); + + ::sax_fastparser::FSHelperPtr pFS = m_pFilter->openFragmentStreamWithSerializer( + "word/settings.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" ); + + pFS->startElementNS( XML_w, XML_settings, + FSNS( XML_xmlns, XML_w ), m_pFilter->getNamespaceURL(OOX_NS(doc)).toUtf8() ); + + // View + if (pViewShell && pViewShell->GetViewOptions()->getBrowseMode()) + { + pFS->singleElementNS(XML_w, XML_view, FSNS(XML_w, XML_val), "web"); + } + + // Zoom + if (pViewShell) + { + rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList( + sax_fastparser::FastSerializerHelper::createAttrList()); + + switch (pViewShell->GetViewOptions()->GetZoomType()) + { + case SvxZoomType::WHOLEPAGE: + pAttributeList->add(FSNS(XML_w, XML_val), "fullPage"); + break; + case SvxZoomType::PAGEWIDTH: + pAttributeList->add(FSNS(XML_w, XML_val), "bestFit"); + break; + case SvxZoomType::OPTIMAL: + pAttributeList->add(FSNS(XML_w, XML_val), "textFit"); + break; + default: + break; + } + + OString aZoom(OString::number(pViewShell->GetViewOptions()->GetZoom())); + pAttributeList->add(FSNS(XML_w, XML_percent), aZoom); + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList.get()); + pFS->singleElementNS(XML_w, XML_zoom, xAttributeList); + } + + // Display Background Shape + if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush) + { + // Turn on the 'displayBackgroundShape' + pFS->singleElementNS(XML_w, XML_displayBackgroundShape); + } + + // Track Changes + if ( !m_aSettings.revisionView ) + pFS->singleElementNS( XML_w, XML_revisionView, + FSNS( XML_w, XML_insDel ), "0", + FSNS( XML_w, XML_formatting ), "0" ); + + if ( m_aSettings.trackRevisions ) + pFS->singleElementNS(XML_w, XML_trackRevisions); + + // Mirror Margins + if(isMirroredMargin()) + pFS->singleElementNS(XML_w, XML_mirrorMargins); + + // Embed Fonts + if( m_pDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) + pFS->singleElementNS(XML_w, XML_embedTrueTypeFonts); + + // Embed System Fonts + if( m_pDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_SYSTEM_FONTS )) + pFS->singleElementNS(XML_w, XML_embedSystemFonts); + + // Default Tab Stop + if( m_aSettings.defaultTabStop != 0 ) + pFS->singleElementNS( XML_w, XML_defaultTabStop, FSNS( XML_w, XML_val ), + OString::number(m_aSettings.defaultTabStop) ); + + // export current mail merge database and table names + SwDBData aData = m_pDoc->GetDBData(); + if ( !aData.sDataSource.isEmpty() && aData.nCommandType == css::sdb::CommandType::TABLE && !aData.sCommand.isEmpty() ) + { + OUString sDataSource = + "SELECT * FROM " + + aData.sDataSource + // current database + ".dbo." + // default database owner + aData.sCommand + // sheet name + "$"; // sheet identifier + pFS->startElementNS( XML_w, XML_mailMerge ); + pFS->singleElementNS(XML_w, XML_mainDocumentType, + FSNS( XML_w, XML_val ), "formLetters" ); + pFS->singleElementNS(XML_w, XML_dataType, + FSNS( XML_w, XML_val ), "textFile" ); + pFS->singleElementNS( XML_w, XML_query, + FSNS( XML_w, XML_val ), OUStringToOString( sDataSource, RTL_TEXTENCODING_UTF8 ).getStr() ); + pFS->endElementNS( XML_w, XML_mailMerge ); + } + + // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer. + // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there. + pFS->singleElementNS(XML_w, XML_autoHyphenation, FSNS(XML_w, XML_val), "true"); + + // Hyphenation details set depending on default style + SwTextFormatColl* pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, /*bRegardLanguage=*/false); + const SfxPoolItem* pItem; + if (pColl && SfxItemState::SET == pColl->GetItemState(RES_PARATR_HYPHENZONE, false, &pItem)) + { + if (static_cast<const SvxHyphenZoneItem*>(pItem)->IsNoCapsHyphenation()) + pFS->singleElementNS(XML_w, XML_doNotHyphenateCaps); + } + + // Even and Odd Headers + if( m_aSettings.evenAndOddHeaders ) + pFS->singleElementNS(XML_w, XML_evenAndOddHeaders); + + // Has Footnotes + if( m_pAttrOutput->HasFootnotes()) + DocxAttributeOutput::WriteFootnoteEndnotePr( pFS, XML_footnotePr, m_pDoc->GetFootnoteInfo(), XML_footnote ); + + // Has Endnotes + if( m_pAttrOutput->HasEndnotes()) + DocxAttributeOutput::WriteFootnoteEndnotePr( pFS, XML_endnotePr, m_pDoc->GetEndNoteInfo(), XML_endnote ); + + // Has themeFontLang information + uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + + bool bUseGrabBagProtection = false; + bool bWriterWantsToProtect = false; + bool bWriterWantsToProtectForm = false; + bool bWriterWantsToProtectRedline = false; + bool bHasRedlineProtectionKey = false; + bool bHasDummyRedlineProtectionKey = false; + bool bReadOnlyStatusUnchanged = true; + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + if ( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM) || + m_pSections->DocumentIsProtected() ) + { + bWriterWantsToProtect = bWriterWantsToProtectForm = true; + } + if ( xPropSetInfo->hasPropertyByName( "RedlineProtectionKey" ) ) + { + uno::Sequence<sal_Int8> aKey; + xPropSet->getPropertyValue( "RedlineProtectionKey" ) >>= aKey; + bHasRedlineProtectionKey = aKey.hasElements(); + bHasDummyRedlineProtectionKey = aKey.getLength() == 1 && aKey[0] == 1; + if ( bHasRedlineProtectionKey && !bHasDummyRedlineProtectionKey ) + bWriterWantsToProtect = bWriterWantsToProtectRedline = true; + } + + /* Compatibility Mode (tdf#131304) + * 11: .doc level [Word 97-2003] + * 12: .docx default [Word 2007] [LO < 7.0] + * 14: [Word 2010] + * 15: [Word 2013/2016/2019] [LO >= 7.0] + * + * The PRIMARY purpose of compatibility mode does not seem to be related to layout etc. + * Its focus is on sharing files between multiple users, tracking the lowest supported mode in the group. + * It is to BENEFIT older programs by not using certain new features that they don't understand. + * + * The next time the compat mode needs to be changed, I foresee the following steps: + * 1.) Accept the new mode: Start round-tripping the new value, indicating we understand that format. + * 2.) Many years later, change the TargetCompatilityMode for new documents, when we no longer care + * about working with perfect compatibility with older versions of MS Word. + */ + sal_Int32 nTargetCompatibilityMode = 15; //older versions might not open our files well + bool bHasCompatibilityMode = false; + const OUString aGrabBagName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if ( xPropSetInfo->hasPropertyByName( aGrabBagName ) ) + { + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( aGrabBagName ) >>= propList; + + for( const auto& rProp : std::as_const(propList) ) + { + if ( rProp.Name == "ThemeFontLangProps" ) + { + uno::Sequence< beans::PropertyValue > themeFontLangProps; + rProp.Value >>= themeFontLangProps; + OUString aValues[3]; + for( const auto& rThemeFontLangProp : std::as_const(themeFontLangProps) ) + { + if( rThemeFontLangProp.Name == "val" ) + rThemeFontLangProp.Value >>= aValues[0]; + else if( rThemeFontLangProp.Name == "eastAsia" ) + rThemeFontLangProp.Value >>= aValues[1]; + else if( rThemeFontLangProp.Name == "bidi" ) + rThemeFontLangProp.Value >>= aValues[2]; + } + pFS->singleElementNS( XML_w, XML_themeFontLang, + FSNS( XML_w, XML_val ), aValues[0].toUtf8(), + FSNS( XML_w, XML_eastAsia ), aValues[1].toUtf8(), + FSNS( XML_w, XML_bidi ), aValues[2].toUtf8() ); + } + else if ( rProp.Name == "CompatSettings" ) + { + pFS->startElementNS(XML_w, XML_compat); + + WriteCompat(*m_pDoc, pFS, nTargetCompatibilityMode); + + uno::Sequence< beans::PropertyValue > aCompatSettingsSequence; + rProp.Value >>= aCompatSettingsSequence; + + for(const auto& rCompatSetting : std::as_const(aCompatSettingsSequence)) + { + uno::Sequence< beans::PropertyValue > aCompatSetting; + rCompatSetting.Value >>= aCompatSetting; + OUString aName; + OUString aUri; + OUString aValue; + + for(const auto& rPropVal : std::as_const(aCompatSetting)) + { + if( rPropVal.Name == "name" ) + rPropVal.Value >>= aName; + else if( rPropVal.Name == "uri" ) + rPropVal.Value >>= aUri; + else if( rPropVal.Name == "val" ) + rPropVal.Value >>= aValue; + } + if ( aName == "compatibilityMode" ) + { + bHasCompatibilityMode = true; + // Among the group of programs sharing this document, the lowest mode is retained. + // Reduce this number if we are not comfortable with the new/unknown mode yet. + // Step 1 in accepting a new mode would be to comment out the following clause + // and roundtrip the new value instead of overwriting with the older number. + // There are no newer modes at the time this code was written. + if ( aValue.toInt32() > nTargetCompatibilityMode ) + aValue = OUString::number(nTargetCompatibilityMode); + } + + pFS->singleElementNS( XML_w, XML_compatSetting, + FSNS( XML_w, XML_name ), aName.toUtf8(), + FSNS( XML_w, XML_uri ), aUri.toUtf8(), + FSNS( XML_w, XML_val ), aValue.toUtf8()); + } + + if ( !bHasCompatibilityMode ) + { + pFS->singleElementNS( XML_w, XML_compatSetting, + FSNS( XML_w, XML_name ), "compatibilityMode", + FSNS( XML_w, XML_uri ), "http://schemas.microsoft.com/office/word", + FSNS( XML_w, XML_val ), OString::number(nTargetCompatibilityMode)); + bHasCompatibilityMode = true; + } + + pFS->endElementNS( XML_w, XML_compat ); + } + else if (rProp.Name == "DocumentProtection") + { + uno::Sequence< beans::PropertyValue > rAttributeList; + rProp.Value >>= rAttributeList; + + if (rAttributeList.hasElements()) + { + rtl::Reference<sax_fastparser::FastAttributeList> xAttributeList = sax_fastparser::FastSerializerHelper::createAttrList(); + bool bIsProtectionTrackChanges = false; + // if grabbag protection is not enforced, allow Writer protection to override + bool bEnforced = false; + for (const auto& rAttribute : std::as_const(rAttributeList)) + { + static DocxStringTokenMap const aTokens[] = + { + { "edit", XML_edit }, + { "enforcement", XML_enforcement }, + { "formatting", XML_formatting }, + { "cryptProviderType", XML_cryptProviderType }, + { "cryptAlgorithmClass", XML_cryptAlgorithmClass }, + { "cryptAlgorithmType", XML_cryptAlgorithmType }, + { "cryptAlgorithmSid", XML_cryptAlgorithmSid }, + { "cryptSpinCount", XML_cryptSpinCount }, + { "hash", XML_hash }, + { "salt", XML_salt }, + { nullptr, 0 } + }; + + if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name)) + { + OUString sValue = rAttribute.Value.get<OUString>(); + xAttributeList->add(FSNS(XML_w, nToken), sValue.toUtf8()); + if ( nToken == XML_edit && sValue == "trackedChanges" ) + bIsProtectionTrackChanges = true; + else if ( nToken == XML_edit && sValue == "readOnly" ) + { + // Ignore the case where read-only was not enforced, but now is. That is handled by _MarkAsFinal + bReadOnlyStatusUnchanged = m_pDoc->GetDocShell()->IsSecurityOptOpenReadOnly(); + } + else if ( nToken == XML_enforcement ) + bEnforced = sValue.toBoolean(); + } + } + + // we have document protection from input DOCX file + if ( !bEnforced ) + { + // Leave as an un-enforced suggestion if Writer doesn't want to set any enforcement + bUseGrabBagProtection = !bWriterWantsToProtect; + } + else + { + // Check if the grabbag protection is still valid + // In the case of change tracking protection, we didn't modify it + // and in the case of read-only, we didn't modify it. + bUseGrabBagProtection = (!bIsProtectionTrackChanges || bHasDummyRedlineProtectionKey) + && bReadOnlyStatusUnchanged; + } + + if ( bUseGrabBagProtection ) + { + sax_fastparser::XFastAttributeListRef xFastAttributeList(xAttributeList.get()); + pFS->singleElementNS(XML_w, XML_documentProtection, xFastAttributeList); + } + + } + } + else if (rProp.Name == "HyphenationZone") + { + sal_Int16 nHyphenationZone = *o3tl::doAccess<sal_Int16>(rProp.Value); + if (nHyphenationZone > 0) + pFS->singleElementNS(XML_w, XML_hyphenationZone, FSNS(XML_w, XML_val), + OString::number(nHyphenationZone)); + } + } + } + if ( !bHasCompatibilityMode ) + { + pFS->startElementNS(XML_w, XML_compat); + + WriteCompat(*m_pDoc, pFS, nTargetCompatibilityMode); + + pFS->singleElementNS( XML_w, XML_compatSetting, + FSNS( XML_w, XML_name ), "compatibilityMode", + FSNS( XML_w, XML_uri ), "http://schemas.microsoft.com/office/word", + FSNS( XML_w, XML_val ), OString::number(nTargetCompatibilityMode)); + pFS->endElementNS( XML_w, XML_compat ); + } + + WriteDocVars(pFS); + + if ( !bUseGrabBagProtection ) + { + // Protect form - highest priority + // Section-specific write protection + if ( bWriterWantsToProtectForm ) + { + // we have form protection from Writer or from input ODT file + + pFS->singleElementNS(XML_w, XML_documentProtection, + FSNS(XML_w, XML_edit), "forms", + FSNS(XML_w, XML_enforcement), "true"); + } + // Protect Change Tracking - next priority + else if ( bWriterWantsToProtectRedline ) + { + // we have change tracking protection from Writer or from input ODT file + + pFS->singleElementNS(XML_w, XML_documentProtection, + FSNS(XML_w, XML_edit), "trackedChanges", + FSNS(XML_w, XML_enforcement), "1"); + } + } + + // finish settings.xml + pFS->endElementNS( XML_w, XML_settings ); +} + +void DocxExport::WriteTheme() +{ + uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if ( !xPropSetInfo->hasPropertyByName( aName ) ) + return; + + uno::Reference<xml::dom::XDocument> themeDom; + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( aName ) >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXTheme"; }); + if (pProp != propList.end()) + pProp->Value >>= themeDom; + + // no theme dom to write + if ( !themeDom.is() ) + return; + + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + "theme/theme1.xml" ); + + uno::Reference< xml::sax::XSAXSerializable > serializer( themeDom, uno::UNO_QUERY ); + uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() ); + writer->setOutputStream( GetFilter().openFragmentStream( "word/theme/theme1.xml", + "application/vnd.openxmlformats-officedocument.theme+xml" ) ); + serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), + uno::Sequence< beans::StringPair >() ); +} + +void DocxExport::WriteGlossary() +{ + uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if ( !xPropSetInfo->hasPropertyByName( aName ) ) + return; + + uno::Reference<xml::dom::XDocument> glossaryDocDom; + uno::Sequence< uno::Sequence< uno::Any> > glossaryDomList; + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( aName ) >>= propList; + sal_Int32 collectedProperties = 0; + for ( const auto& rProp : std::as_const(propList) ) + { + OUString propName = rProp.Name; + if ( propName == "OOXGlossary" ) + { + rProp.Value >>= glossaryDocDom; + collectedProperties++; + } + if (propName == "OOXGlossaryDom") + { + rProp.Value >>= glossaryDomList; + collectedProperties++; + } + if (collectedProperties == 2) + break; + } + + // no glossary dom to write + if ( !glossaryDocDom.is() ) + return; + + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::GLOSSARYDOCUMENT), + "glossary/document.xml" ); + + uno::Reference< io::XOutputStream > xOutputStream = GetFilter().openFragmentStream( "word/glossary/document.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml" ); + + uno::Reference< xml::sax::XSAXSerializable > serializer( glossaryDocDom, uno::UNO_QUERY ); + uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() ); + writer->setOutputStream( xOutputStream ); + serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), + uno::Sequence< beans::StringPair >() ); + + for ( const uno::Sequence< uno::Any>& glossaryElement : std::as_const(glossaryDomList)) + { + OUString gTarget, gType, gId, contentType; + uno::Reference<xml::dom::XDocument> xDom; + glossaryElement[0] >>= xDom; + glossaryElement[1] >>= gId; + glossaryElement[2] >>= gType; + glossaryElement[3] >>= gTarget; + glossaryElement[4] >>= contentType; + gId = gId.copy(3); //"rId" only save the numeric value + + PropertySet aProps(xOutputStream); + aProps.setAnyProperty( PROP_RelId, uno::makeAny( gId.toInt32() )); + m_pFilter->addRelation( xOutputStream, gType, gTarget); + uno::Reference< xml::sax::XSAXSerializable > gserializer( xDom, uno::UNO_QUERY ); + writer->setOutputStream(GetFilter().openFragmentStream( "word/glossary/" + gTarget, contentType ) ); + gserializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), + uno::Sequence< beans::StringPair >() ); + } +} + +void DocxExport::WriteCustomXml() +{ + uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + static const OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if ( !xPropSetInfo->hasPropertyByName( aName ) ) + return; + + uno::Sequence<uno::Reference<xml::dom::XDocument> > customXmlDomlist; + uno::Sequence<uno::Reference<xml::dom::XDocument> > customXmlDomPropslist; + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( aName ) >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXCustomXml"; }); + if (pProp != propList.end()) + pProp->Value >>= customXmlDomlist; + + pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXCustomXmlProps"; }); + if (pProp != propList.end()) + pProp->Value >>= customXmlDomPropslist; + + for (sal_Int32 j = 0; j < customXmlDomlist.getLength(); j++) + { + uno::Reference<xml::dom::XDocument> customXmlDom = customXmlDomlist[j]; + uno::Reference<xml::dom::XDocument> customXmlDomProps = customXmlDomPropslist[j]; + if (customXmlDom.is()) + { + m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), + oox::getRelationship(Relationship::CUSTOMXML), + "../customXml/item"+OUString::number((j+1))+".xml" ); + + uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDom, uno::UNO_QUERY ); + uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() ); + writer->setOutputStream( GetFilter().openFragmentStream( "customXml/item"+OUString::number((j+1))+".xml", + "application/xml" ) ); + serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), + uno::Sequence< beans::StringPair >() ); + } + + if (customXmlDomProps.is()) + { + uno::Reference< xml::sax::XSAXSerializable > serializer( customXmlDomProps, uno::UNO_QUERY ); + uno::Reference< xml::sax::XWriter > writer = xml::sax::Writer::create( comphelper::getProcessComponentContext() ); + writer->setOutputStream( GetFilter().openFragmentStream( "customXml/itemProps"+OUString::number((j+1))+".xml", + "application/vnd.openxmlformats-officedocument.customXmlProperties+xml" ) ); + serializer->serialize( uno::Reference< xml::sax::XDocumentHandler >( writer, uno::UNO_QUERY_THROW ), + uno::Sequence< beans::StringPair >() ); + + // Adding itemprops's relationship entry to item.xml.rels file + m_pFilter->addRelation( GetFilter().openFragmentStream( "customXml/item"+OUString::number((j+1))+".xml", + "application/xml" ) , + oox::getRelationship(Relationship::CUSTOMXMLPROPS), + "itemProps"+OUString::number((j+1))+".xml" ); + } + } +} + +void DocxExport::WriteVBA() +{ + uno::Reference<document::XStorageBasedDocument> xStorageBasedDocument(m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference<embed::XStorage> xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + OUString aMacrosName("_MS_VBA_Macros"); + if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName)) + return; + + const sal_Int32 nOpenMode = embed::ElementModes::READ; + uno::Reference<io::XStream> xMacrosStream = xDocumentStorage->openStreamElement(aMacrosName, nOpenMode); + uno::Reference<io::XOutputStream> xProjectStream; + if (xMacrosStream.is()) + { + // First handle the project stream, this sets xProjectStream. + std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xMacrosStream)); + + xProjectStream = GetFilter().openFragmentStream("word/vbaProject.bin", "application/vnd.ms-office.vbaProject"); + uno::Reference<io::XStream> xOutputStream(xProjectStream, uno::UNO_QUERY); + if (!xOutputStream.is()) + return; + std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream)); + + // Write the stream. + pOut->WriteStream(*pIn); + + // Write the relationship. + m_pFilter->addRelation(m_pDocumentFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), "vbaProject.bin"); + } + + OUString aDataName("_MS_VBA_Macros_XML"); + if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aDataName)) + return; + + uno::Reference<io::XStream> xDataStream = xDocumentStorage->openStreamElement(aDataName, nOpenMode); + if (xDataStream.is()) + { + // Then the data stream, which wants to work with an already set + // xProjectStream. + std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xDataStream)); + + uno::Reference<io::XStream> xOutputStream(GetFilter().openFragmentStream("word/vbaData.xml", "application/vnd.ms-word.vbaData+xml"), uno::UNO_QUERY); + if (!xOutputStream.is()) + return; + std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream)); + + // Write the stream. + pOut->WriteStream(*pIn); + + // Write the relationship. + if (!xProjectStream.is()) + return; + + m_pFilter->addRelation(xProjectStream, oox::getRelationship(Relationship::WORDVBADATA), "vbaData.xml"); + } +} + +void DocxExport::WriteEmbeddings() +{ + uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + if ( !xPropSetInfo->hasPropertyByName( aName ) ) + return; + + uno::Sequence< beans::PropertyValue > embeddingsList; + uno::Sequence< beans::PropertyValue > propList; + xPropSet->getPropertyValue( aName ) >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "OOXEmbeddings"; }); + if (pProp != propList.end()) + pProp->Value >>= embeddingsList; + for (const auto& rEmbedding : std::as_const(embeddingsList)) + { + OUString embeddingPath = rEmbedding.Name; + uno::Reference<io::XInputStream> embeddingsStream; + rEmbedding.Value >>= embeddingsStream; + + OUString contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + // FIXME: this .xlsm hack is silly - if anything the mime-type for an existing embedded object should be read from [Content_Types].xml + if (embeddingPath.endsWith(".xlsm")) + contentType = "application/vnd.ms-excel.sheet.macroEnabled.12"; + else if (embeddingPath.endsWith(".bin")) + contentType = "application/vnd.openxmlformats-officedocument.oleObject"; + + if ( embeddingsStream.is() ) + { + uno::Reference< io::XOutputStream > xOutStream = GetFilter().openFragmentStream(embeddingPath, + contentType); + try + { + // tdf#131288: the stream must be seekable for direct access + uno::Reference< io::XSeekable > xSeekable(embeddingsStream, uno::UNO_QUERY); + if (xSeekable) + xSeekable->seek(0); // tdf#131288: a previous save could position it elsewhere + comphelper::OStorageHelper::CopyInputToOutput(embeddingsStream, xOutStream); + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.ww8", "WriteEmbeddings() ::Failed to copy Inputstream to outputstream exception caught"); + } + xOutStream->closeOutput(); + } + } +} + +bool DocxExport::isMirroredMargin() +{ + bool bMirroredMargins = false; + if ( UseOnPage::Mirror == (UseOnPage::Mirror & m_pDoc->GetPageDesc(0).ReadUseOn()) ) + { + bMirroredMargins = true; + } + return bMirroredMargins; +} + +void DocxExport::WriteMainText() +{ + // setup the namespaces + m_pDocumentFS->startElementNS( XML_w, XML_document, MainXmlNamespaces()); + + if ( getenv("SW_DEBUG_DOM") ) + { + m_pDoc->dumpAsXml(); + } + + // reset the incrementing linked-textboxes chain ID before re-saving. + m_nLinkedTextboxesChainId=0; + m_aLinkedTextboxesHelper.clear(); + + // Write background page color + if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush) + { + Color backgroundColor = oBrush->GetColor(); + OString aBackgroundColorStr = msfilter::util::ConvertColor(backgroundColor); + + m_pDocumentFS->singleElementNS(XML_w, XML_background, FSNS(XML_w, XML_color), + aBackgroundColorStr); + } + + // body + m_pDocumentFS->startElementNS(XML_w, XML_body); + + m_pCurPam->GetPoint()->nNode = m_pDoc->GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex(); + + // the text + WriteText(); + + // clear linked textboxes since old ones can't be linked to frames in a different section (correct?) + m_aLinkedTextboxesHelper.clear(); + + // the last section info + m_pAttrOutput->EndParaSdtBlock(); + const WW8_SepInfo *pSectionInfo = m_pSections? m_pSections->CurrentSectionInfo(): nullptr; + if ( pSectionInfo ) + SectionProperties( *pSectionInfo ); + + // finish body and document + m_pDocumentFS->endElementNS( XML_w, XML_body ); + m_pDocumentFS->endElementNS( XML_w, XML_document ); +} + +XFastAttributeListRef DocxExport::MainXmlNamespaces() +{ + FastAttributeList* pAttr = FastSerializerHelper::createAttrList(); + pAttr->add( FSNS( XML_xmlns, XML_o ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(vmlOffice)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_r ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_v ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(vml)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_w ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(doc)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_w10 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(vmlWord)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_wp ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(dmlWordDr)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_wps ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(wps)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_wpg ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(wpg)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_mc ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_wp14 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(wp14)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_xmlns, XML_w14 ), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(w14)), RTL_TEXTENCODING_UTF8).getStr() ); + pAttr->add( FSNS( XML_mc, XML_Ignorable ), "w14 wp14" ); + return XFastAttributeListRef( pAttr ); +} + +bool DocxExport::ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const +{ + if( nWhich == RES_TEXTGRID ) + return true; // w:docGrid is written only to document.xml, not to styles.xml + if (nWhich == RES_PARATR_HYPHENZONE) + return true; // w:suppressAutoHyphens is only a formatting exception, not a default + return MSWordExportBase::ignoreAttributeForStyleDefaults( nWhich ); +} + +void DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp) +{ + const EditTextObject& rEditObj = rParaObj.GetTextObject(); + MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp ); + + sal_Int32 nPara = rEditObj.GetParagraphCount(); + for( sal_Int32 n = 0; n < nPara; ++n ) + { + if( n ) + aAttrIter.NextPara( n ); + + AttrOutput().StartParagraph( ww8::WW8TableNodeInfo::Pointer_t()); + rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet(); + OUString aStr( rEditObj.GetText( n )); + sal_Int32 nCurrentPos = 0; + const sal_Int32 nEnd = aStr.getLength(); + do { + AttrOutput().StartRun( nullptr, 0 ); + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet(); + + bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); + if( !bTextAtr ) + { + if( nCurrentPos == 0 && nNextAttr - nCurrentPos == aStr.getLength()) + AttrOutput().RunText( aStr, eChrSet ); + else + { + OUString tmp( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos )); + AttrOutput().RunText( tmp, eChrSet ); + } + } + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nCurrentPos ); + AttrOutput().EndRunProperties( nullptr ); + + nCurrentPos = nNextAttr; + eChrSet = eNextChrSet; + aAttrIter.NextPos(); + + AttrOutput().EndRun( nullptr, 0 ); + + } while( nCurrentPos < nEnd ); +// aAttrIter.OutParaAttr(false); + AttrOutput().EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t()); + } +} + +void DocxExport::SetFS( ::sax_fastparser::FSHelperPtr const & pFS ) +{ + mpFS = pFS; +} + +DocxExport::DocxExport(DocxExportFilter* pFilter, SwDoc* pDocument, + std::shared_ptr<SwUnoCursor> & pCurrentPam, + SwPaM* pOriginalPam, bool bDocm, bool bTemplate) + : MSWordExportBase( pDocument, pCurrentPam, pOriginalPam ), + m_pFilter( pFilter ), + m_nHeaders( 0 ), + m_nFooters( 0 ), + m_nOLEObjects( 0 ), + m_nActiveXControls( 0 ), + m_nHeadersFootersInSection(0), + m_bDocm(bDocm), + m_bTemplate(bTemplate) +{ + // Write the document properties + WriteProperties( ); + + // relations for the document + m_pFilter->addRelation( oox::getRelationship(Relationship::OFFICEDOCUMENT), + "word/document.xml" ); + + // Set media type depending of document type + OUString aMediaType; + if (m_bDocm) + { + if (m_bTemplate) + { + aMediaType = "application/vnd.ms-word.template.macroEnabledTemplate.main+xml"; + } + else + { + aMediaType = "application/vnd.ms-word.document.macroEnabled.main+xml"; + } + } + else + { + if (m_bTemplate) + { + aMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml"; + } + else + { + aMediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"; + } + } + + + // the actual document + m_pDocumentFS = m_pFilter->openFragmentStreamWithSerializer( "word/document.xml", aMediaType ); + + SetFS(m_pDocumentFS); + + // the DrawingML access + m_pDrawingML.reset(new oox::drawingml::DrawingML(m_pDocumentFS, m_pFilter, oox::drawingml::DOCUMENT_DOCX)); + + // the attribute output for the document + m_pAttrOutput.reset(new DocxAttributeOutput( *this, m_pDocumentFS, m_pDrawingML.get() )); + + // the related VMLExport + m_pVMLExport.reset(new VMLExport( m_pDocumentFS, m_pAttrOutput.get() )); + + // the related drawing export + m_pSdrExport.reset(new DocxSdrExport( *this, m_pDocumentFS, m_pDrawingML.get() )); +} + +DocxExport::~DocxExport() +{ +} + +DocxSettingsData::DocxSettingsData() +: evenAndOddHeaders( false ) +, defaultTabStop( 0 ) +, revisionView( true ) +, trackRevisions( false ) +{ +} + +bool DocxSettingsData::hasData() const +{ + if( evenAndOddHeaders ) + return true; + if( defaultTabStop != 0 ) + return true; + if ( !revisionView ) + return true; + if ( trackRevisions ) + return true; + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx new file mode 100644 index 000000000..74336b72d --- /dev/null +++ b/sw/source/filter/ww8/docxexport.hxx @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_DOCXEXPORT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORT_HXX + +#include "wrtww8.hxx" + +#include <sal/log.hxx> +#include <sax/fshelper.hxx> +#include <rtl/ustring.hxx> + +#include <cstdio> +#include <memory> +#include <ndole.hxx> + +class DocxAttributeOutput; +class DocxExportFilter; +class SwNode; +class SwEndNode; +class SwTableNode; +class SwTextNode; +class SwGrfNode; +class SwOLENode; +class DocxSdrExport; + +namespace oox { + namespace drawingml { class DrawingML; } + namespace vml { class VMLExport; } +} + +namespace com::sun::star { + namespace frame { class XModel; } + namespace drawing { class XShape; } + namespace awt { class XControlModel; } +} + +/// Data to be written in the document settings part of the document +struct DocxSettingsData +{ + DocxSettingsData(); + bool hasData() const; /// returns true if there are any non-default settings (i.e. something to write) + bool evenAndOddHeaders; + int defaultTabStop; + bool revisionView; // don't show tracked changes + bool trackRevisions; // Should 'Track Revisions' be set +}; + +/// The class that does all the actual DOCX export-related work. +class DocxExport : public MSWordExportBase +{ + /// Pointer to the filter that owns us. + DocxExportFilter *m_pFilter; + + /// Fast serializer for the document output. + ::sax_fastparser::FSHelperPtr m_pDocumentFS; + + /// Fast serializer to output the data. + ::sax_fastparser::FSHelperPtr mpFS; + + /// Access to the DrawingML writer. + std::unique_ptr<oox::drawingml::DrawingML> m_pDrawingML; + + /// Attribute output for document. + std::unique_ptr<DocxAttributeOutput> m_pAttrOutput; + + /// Sections/headers/footers + std::unique_ptr<MSWordSections> m_pSections; + + /// Header counter. + sal_Int32 m_nHeaders; + + /// Footer counter. + sal_Int32 m_nFooters; + + /// OLE objects counter. + sal_Int32 m_nOLEObjects; + + /// ActiveX controls counter + sal_Int32 m_nActiveXControls; + + ///Footer and Header counter in Section properties + sal_Int32 m_nHeadersFootersInSection; + + /// Exporter of the VML shapes. + std::unique_ptr<oox::vml::VMLExport> m_pVMLExport; + + /// Exporter of drawings. + std::unique_ptr<DocxSdrExport> m_pSdrExport; + + /// If the result will be a .docm file or not. + bool m_bDocm; + + /// Export is done into template (.dotx) + bool const m_bTemplate; + + DocxSettingsData m_aSettings; + + /// Pointer to the Frame of a floating table it is nested in + const ww8::Frame *m_pFloatingTableFrame = nullptr; + +public: + + DocxExportFilter& GetFilter() { return *m_pFilter; }; + const DocxExportFilter& GetFilter() const { return *m_pFilter; }; + + const ww8::Frame* GetFloatingTableFrame() const { return m_pFloatingTableFrame; } + + /// Access to the attribute output class. + virtual AttributeOutputBase& AttrOutput() const override; + + /// Access to the derived attribute output class. + DocxAttributeOutput& DocxAttrOutput() const; + + /// Access to the sections/headers/footres. + virtual MSWordSections& Sections() const override; + + virtual bool FieldsQuoted() const override { return true; } + + virtual bool AddSectionBreaksForTOX() const override { return true; } + + virtual bool ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const override; + + virtual bool PreferPageBreakBefore() const override { return false; } + + /// Guess the script (asian/western). + virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) override; + + virtual void AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + + virtual void AppendBookmark( const OUString& rName ) override; + + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + + virtual void ExportGrfBullet(const SwTextNode&) override; + + /// Returns the relationd id + OString AddRelation( const OUString& rType, const OUString& rTarget ); + + virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t()*/ ) override { /* FIXME no-op for docx, most probably should not even be in MSWordExportBase */ } + virtual void WriteChar( sal_Unicode ) override { SAL_WARN("sw.ww8", "FIXME: WriteChar() has nothing to do for docx."); } + + /// Return value indicates if an inherited outline numbering is suppressed. + virtual bool DisallowInheritingOutlineNumbering( const SwFormat &rFormat ) override; + + /// Output the actual headers and footers. + virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags, + const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode ) override; + + /// Write the field + virtual void OutputField( const SwField* pField, ww::eField eFieldType, + const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) override; + + /// Write the data of the form field + virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) override; + virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) override; + + virtual void DoComboBox(const OUString &rName, + const OUString &rHelp, + const OUString &ToolTip, + const OUString &rSelected, + const css::uno::Sequence<OUString> &rListItems) override; + + virtual void DoFormText(const SwInputField * pField) override; + + virtual sal_uLong ReplaceCr( sal_uInt8 nChar ) override; + + /// Returns the relationd id + OString OutputChart( css::uno::Reference< css::frame::XModel > const & xModel, sal_Int32 nCount, ::sax_fastparser::FSHelperPtr const & m_pSerializer ); + OString WriteOLEObject(SwOLEObj& rObject, OUString & io_rProgID); + std::pair<OString,OString> WriteActiveXObject(const uno::Reference<css::drawing::XShape>& rxShape, + const uno::Reference<awt::XControlModel>& rxControlModel); + + /// Writes the shape using drawingML syntax. + void OutputDML( css::uno::Reference< css::drawing::XShape > const & xShape ); + + void WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp); + + virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; } + +protected: + /// Format-dependent part of the actual export. + virtual ErrCode ExportDocument_Impl() override; + + /// Output SwEndNode + virtual void OutputEndNode( const SwEndNode& ) override; + + /// Output SwGrfNode + virtual void OutputGrfNode( const SwGrfNode& ) override; + + /// Output SwOLENode + virtual void OutputOLENode( const SwOLENode& ) override; + + virtual void OutputLinkedOLE( const OUString& ) override; + + virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) override; + + virtual void SectionBreaksAndFrames( const SwTextNode& /*rNode*/ ) override {} + + /// Get ready for a new section. + virtual void PrepareNewPageDesc( const SfxItemSet* pSet, + const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc ) override; + +private: + /// Setup pStyles and write styles.xml + void InitStyles(); + + /// Write footnotes.xml and endnotes.xml. + void WriteFootnotesEndnotes(); + + /// Write comments.xml + void WritePostitFields(); + + /// Write the numbering table. + virtual void WriteNumbering() override; + + /// Write reference to a header/footer + the actual xml containing the text. + void WriteHeaderFooter( const SwFormat* pFormat, bool bHeader, const char* pType ); + + /// Write word/fontTable.xml. + void WriteFonts(); + + /// Write docProps/core.xml + void WriteProperties(); + + /// Write word/settings.xml + void WriteSettings(); + + /// Writes the <w:docVars> part of settings.xml + void WriteDocVars(const sax_fastparser::FSHelperPtr& pFS); + + /// Write word/theme/theme1.xml + void WriteTheme(); + + void WriteGlossary(); + + /// Write customXml/item[n].xml and customXml/itemProps[n].xml + void WriteCustomXml(); + + /// Write word/embeddings/Worksheet[n].xlsx + void WriteEmbeddings(); + + /// Writes word/vbaProject.bin. + void WriteVBA(); + + /// return true if Page Layout is set as Mirrored + bool isMirroredMargin(); + +public: + /// All xml namespaces to be used at the top of any text .xml file (main doc, headers, footers,...) + sax_fastparser::XFastAttributeListRef MainXmlNamespaces(); + + /// FIXME this is temporary, remotely reminding the method of the same + /// name in WW8Export. + void WriteMainText(); + + /// Pass the pDocument, pCurrentPam and pOriginalPam to the base class. + DocxExport( DocxExportFilter *pFilter, SwDoc *pDocument, + std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam, + bool bDocm, bool bTemplate); + + /// Destructor. + virtual ~DocxExport() override; + + /// Reference to the VMLExport instance for the main document. + oox::vml::VMLExport& VMLExporter() { return *m_pVMLExport; } + + /// Reference to the DocxSdrExport instance for the main document. + DocxSdrExport& SdrExporter() { return *m_pSdrExport; } + + /// Set the document default tab stop. + void setDefaultTabStop( int stop ) { m_aSettings.defaultTabStop = stop; } + + const ::sax_fastparser::FSHelperPtr& GetFS() const { return mpFS; } + + void SetFS(::sax_fastparser::FSHelperPtr const & mpFS); + + void SetFloatingTableFrame(const ww8::Frame* pF) { m_pFloatingTableFrame = pF; } + +private: + DocxExport( const DocxExport& ) = delete; + + DocxExport& operator=( const DocxExport& ) = delete; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxexportfilter.cxx b/sw/source/filter/ww8/docxexportfilter.cxx new file mode 100644 index 000000000..ad44c9190 --- /dev/null +++ b/sw/source/filter/ww8/docxexportfilter.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 <memory> +#include "docxexportfilter.hxx" +#include "docxexport.hxx" + +#include <docsh.hxx> +#include <pam.hxx> +#include <PostItMgr.hxx> +#include <unotxdoc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <viewsh.hxx> + +#include <unotools/mediadescriptor.hxx> + +using namespace ::comphelper; +using namespace ::com::sun::star; + +DocxExportFilter::DocxExportFilter( const uno::Reference< uno::XComponentContext >& xContext ) + : oox::core::XmlFilterBase( xContext ) +{ +} + +bool DocxExportFilter::exportDocument() +{ + // get SwDoc* + uno::Reference< uno::XInterface > xIfc( getModel(), uno::UNO_QUERY ); + SwXTextDocument *pTextDoc = dynamic_cast< SwXTextDocument * >( xIfc.get() ); + if ( !pTextDoc ) + return false; + + SwDoc *pDoc = pTextDoc->GetDocShell()->GetDoc(); + if ( !pDoc ) + return false; + + // update layout (if present), for SwWriteTable + SwViewShell* pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if (pViewShell != nullptr) + pViewShell->CalcLayout(); + + // if we have an active postit window, update the document model + if (pViewShell && + pViewShell->GetPostItMgr() && + pViewShell->GetPostItMgr()->HasActiveSidebarWin()) + { + pViewShell->GetPostItMgr()->UpdateDataOnActiveSidebarWin(); + } + + // get SwPaM* + // FIXME so far we get SwPaM for the entire document; probably we should + // be able to output just the selection as well - though no idea how to + // get the correct SwPaM* then... + SwPaM aPam( pDoc->GetNodes().GetEndOfContent() ); + aPam.SetMark(); + aPam.Move( fnMoveBackward, GoInDoc ); + + std::shared_ptr<SwUnoCursor> pCurPam(pDoc->CreateUnoCursor(*aPam.End(), false)); + pCurPam->SetMark(); + *pCurPam->GetPoint() = *aPam.Start(); + + OUString aFilterName; + getMediaDescriptor()[utl::MediaDescriptor::PROP_FILTERNAME()] >>= aFilterName; + bool bDocm = aFilterName.endsWith("VBA"); + + // export the document + // (in a separate block so that it's destructed before the commit) + { + DocxExport aExport(this, pDoc, pCurPam, &aPam, bDocm, isExportTemplate()); + aExport.ExportDocument( true ); // FIXME support exporting selection only + } + + commitStorage(); + + // delete the pCurPam + while ( pCurPam->GetNext() != pCurPam.get() ) + delete pCurPam->GetNext(); + + return true; +} + +// UNO stuff so that the filter is registered + +OUString DocxExportFilter::getImplementationName() +{ + return "com.sun.star.comp.Writer.DocxExport"; +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Writer_DocxExport_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new DocxExportFilter(pCtx)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxexportfilter.hxx b/sw/source/filter/ww8/docxexportfilter.hxx new file mode 100644 index 000000000..ae98c87c3 --- /dev/null +++ b/sw/source/filter/ww8/docxexportfilter.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_DOCXEXPORTFILTER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORTFILTER_HXX + +#include <oox/core/xmlfilterbase.hxx> +#include <oox/drawingml/chart/chartconverter.hxx> +#include <oox/vml/vmldrawing.hxx> + + +/// The physical access to the DOCX document (for writing). +class DocxExportFilter : public oox::core::XmlFilterBase +{ +public: + explicit DocxExportFilter( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + // FIXME these should not even exist for the export-only filter! + // For now, let's just do empty implementations of those. + virtual bool importDocument() override { return false; } + virtual const ::oox::drawingml::Theme* getCurrentTheme() const override { return nullptr; } + virtual ::oox::vml::Drawing* getVmlDrawing() override { return nullptr; } + virtual ::oox::drawingml::chart::ChartConverter* getChartConverter() override { return nullptr; } + virtual ::oox::drawingml::table::TableStyleListPtr getTableStyles() override { return ::oox::drawingml::table::TableStyleListPtr(); } + + // Actual export of the DOCX document + virtual bool exportDocument() override; + +private: + virtual OUString SAL_CALL getImplementationName() override; + + virtual ::oox::ole::VbaProject* implCreateVbaProject() const override + { + return nullptr; // FIXME: implement me ! + } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXEXPORTFILTER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxfootnotes.hxx b/sw/source/filter/ww8/docxfootnotes.hxx new file mode 100644 index 000000000..614069f0d --- /dev/null +++ b/sw/source/filter/ww8/docxfootnotes.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_FILTER_WW8_DOCXFOOTNOTES_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXFOOTNOTES_HXX + +#include <sal/types.h> + +#include <vector> + +class SwFormatFootnote; + +namespace docx { + +typedef std::vector< const SwFormatFootnote* > FootnotesVector; + +/** Remember footnotes/endnotes so that we can dump them in one go. + + Also remember the last added footnote Id to be able to write it in the + DocxAttributeOutput::EndRunProperties() method. +*/ +class FootnotesList { + /// The current footnote, that was not written yet. + sal_Int32 m_nCurrent; + + /// List of the footnotes. + FootnotesVector m_aFootnotes; + +public: + FootnotesList() : m_nCurrent( -1 ) {} + + void add( const SwFormatFootnote& rFootnote ) + { + m_aFootnotes.push_back( &rFootnote ); + m_nCurrent = m_aFootnotes.size() - 1; + } + + /// Return the current footnote/endnote and clear the 'current' state. + const SwFormatFootnote* getCurrent( sal_Int32& rId ) + { + // anything to write at all? + if ( m_nCurrent < 0 ) + { + rId = -1; + return nullptr; + } + + // skip ids 0 and 1 - they are reserved for separator and + // continuationSeparator + rId = m_nCurrent + 2; + + const SwFormatFootnote *pFootnote = m_aFootnotes[m_nCurrent]; + m_nCurrent = -1; + + return pFootnote; + } + + /// Return all the footnotes/endnotes. + const FootnotesVector& getVector() const + { + return m_aFootnotes; + } + + /// Do we have any footnotes/endnotes at all? + bool isEmpty() const + { + return m_aFootnotes.empty(); + } +}; + +} // namespace docx + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXFOOTNOTES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxhelper.hxx b/sw/source/filter/ww8/docxhelper.hxx new file mode 100644 index 000000000..4791aaa83 --- /dev/null +++ b/sw/source/filter/ww8/docxhelper.hxx @@ -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/. + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX + +#include <rtl/ustring.hxx> + +struct DocxStringTokenMap +{ + const char* pToken; + sal_Int32 nToken; +}; + +sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, const OUString& rName); + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx new file mode 100644 index 000000000..52bcc0c42 --- /dev/null +++ b/sw/source/filter/ww8/docxsdrexport.cxx @@ -0,0 +1,1576 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "docxsdrexport.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/boxitem.hxx> +#include <svx/svdogrp.hxx> +#include <oox/token/namespaces.hxx> +#include <textboxhelper.hxx> +#include <fmtanchr.hxx> +#include <fmtsrnd.hxx> +#include <fmtcntnt.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <fmtwrapinfluenceonobjpos.hxx> +#include "docxattributeoutput.hxx" +#include "docxexportfilter.hxx" +#include <comphelper/flagguard.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <sal/log.hxx> +#include <frmfmt.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <comphelper/propertysequence.hxx> + +using namespace com::sun::star; +using namespace oox; + +namespace +{ +uno::Sequence<beans::PropertyValue> lclGetProperty(const uno::Reference<drawing::XShape>& rShape, + const OUString& rPropName) +{ + uno::Sequence<beans::PropertyValue> aResult; + uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropSetInfo; + + if (!xPropertySet.is()) + return aResult; + + xPropSetInfo = xPropertySet->getPropertySetInfo(); + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(rPropName)) + { + xPropertySet->getPropertyValue(rPropName) >>= aResult; + } + return aResult; +} + +OUString lclGetAnchorIdFromGrabBag(const SdrObject* pObj) +{ + OUString aResult; + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(), + uno::UNO_QUERY); + OUString aGrabBagName; + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY); + if (xServiceInfo->supportsService("com.sun.star.text.TextFrame")) + aGrabBagName = "FrameInteropGrabBag"; + else + aGrabBagName = "InteropGrabBag"; + uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, aGrabBagName); + auto pProp + = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "AnchorId"; }); + if (pProp != propList.end()) + pProp->Value >>= aResult; + return aResult; +} + +void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, sal_Int64 nRotation) +{ + // code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx) + // TODO: refactor + + if (nRotation == 0) + return; + + if (nRotation < 0) + nRotation = (36000 + nRotation) % 36000; + if (nRotation % 18000 == 0) + nRotation = 0; + while (nRotation > 9000) + nRotation = (18000 - (nRotation % 18000)); + + double fVal = static_cast<double>(nRotation) * F_PI18000; + double fCos = cos(fVal); + double fSin = sin(fVal); + + double nWidthHalf = static_cast<double>(rSize.Width()) / 2; + double nHeightHalf = static_cast<double>(rSize.Height()) / 2; + + double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf; + double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf; + + aPos.X += nXDiff; + aPos.Y += nYDiff; +} + +/// Determines if the anchor is inside a paragraph. +bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame) +{ + const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor(); + return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE; +} +} + +ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd, + ww8::Frame const* pParentFrame) + : m_rExport(rExport) +{ + m_rExport.SaveData(nStt, nEnd); + m_rExport.m_pParentFrame = pParentFrame; +} + +ExportDataSaveRestore::~ExportDataSaveRestore() { m_rExport.RestoreData(); } + +/// Holds data used by DocxSdrExport only. +struct DocxSdrExport::Impl +{ +private: + DocxExport& m_rExport; + sax_fastparser::FSHelperPtr m_pSerializer; + oox::drawingml::DrawingML* m_pDrawingML; + const Size* m_pFlyFrameSize; + bool m_bTextFrameSyntax; + bool m_bDMLTextFrameSyntax; + rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pTextboxAttrList; + OStringBuffer m_aTextFrameStyle; + bool m_bDrawingOpen; + bool m_bParagraphSdtOpen; + bool m_bParagraphHasDrawing; ///Flag for checking drawing in a paragraph. + rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyFillAttrList; + sax_fastparser::FastAttributeList* m_pFlyWrapAttrList; + sax_fastparser::FastAttributeList* m_pBodyPrAttrList; + rtl::Reference<sax_fastparser::FastAttributeList> m_pDashLineStyleAttr; + bool m_bDMLAndVMLDrawingOpen; + /// List of TextBoxes in this document: they are exported as part of their shape, never alone. + /// Preserved rotation for TextFrames. + sal_Int32 m_nDMLandVMLTextFrameRotation; + +public: + bool m_bFlyFrameGraphic = false; + + Impl(DocxExport& rExport, sax_fastparser::FSHelperPtr pSerializer, + oox::drawingml::DrawingML* pDrawingML) + : m_rExport(rExport) + , m_pSerializer(std::move(pSerializer)) + , m_pDrawingML(pDrawingML) + , m_pFlyFrameSize(nullptr) + , m_bTextFrameSyntax(false) + , m_bDMLTextFrameSyntax(false) + , m_bDrawingOpen(false) + , m_bParagraphSdtOpen(false) + , m_bParagraphHasDrawing(false) + , m_pFlyWrapAttrList(nullptr) + , m_pBodyPrAttrList(nullptr) + , m_bDMLAndVMLDrawingOpen(false) + , m_nDMLandVMLTextFrameRotation(0) + { + } + + /// Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax. + + void textFrameShadow(const SwFrameFormat& rFrameFormat); + static bool isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape); + + void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer) + { + m_pSerializer = pSerializer; + } + + const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; } + + void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; } + + const Size* getFlyFrameSize() const { return m_pFlyFrameSize; } + + void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; } + + bool getTextFrameSyntax() const { return m_bTextFrameSyntax; } + + void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax) + { + m_bDMLTextFrameSyntax = bDMLTextFrameSyntax; + } + + bool getDMLTextFrameSyntax() const { return m_bDMLTextFrameSyntax; } + + void setFlyAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pFlyAttrList) + { + m_pFlyAttrList = pFlyAttrList; + } + + rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList() { return m_pFlyAttrList; } + + void + setTextboxAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pTextboxAttrList) + { + m_pTextboxAttrList = pTextboxAttrList; + } + + rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList() + { + return m_pTextboxAttrList; + } + + OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; } + + void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; } + + bool getDrawingOpen() const { return m_bDrawingOpen; } + + void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; } + + bool getParagraphSdtOpen() const { return m_bParagraphSdtOpen; } + + void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen) + { + m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen; + } + + bool getDMLAndVMLDrawingOpen() const { return m_bDMLAndVMLDrawingOpen; } + + void setParagraphHasDrawing(bool bParagraphHasDrawing) + { + m_bParagraphHasDrawing = bParagraphHasDrawing; + } + + bool getParagraphHasDrawing() const { return m_bParagraphHasDrawing; } + + rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList() + { + return m_pFlyFillAttrList; + } + + void setFlyWrapAttrList(sax_fastparser::FastAttributeList* pFlyWrapAttrList) + { + m_pFlyWrapAttrList = pFlyWrapAttrList; + } + + sax_fastparser::FastAttributeList* getFlyWrapAttrList() const { return m_pFlyWrapAttrList; } + + void setBodyPrAttrList(sax_fastparser::FastAttributeList* pBodyPrAttrList) + { + m_pBodyPrAttrList = pBodyPrAttrList; + } + + sax_fastparser::FastAttributeList* getBodyPrAttrList() const { return m_pBodyPrAttrList; } + + rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyleAttr() + { + return m_pDashLineStyleAttr; + } + + bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; } + + oox::drawingml::DrawingML* getDrawingML() const { return m_pDrawingML; } + + DocxExport& getExport() const { return m_rExport; } + + void setDMLandVMLTextFrameRotation(sal_Int32 nDMLandVMLTextFrameRotation) + { + m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation; + } + + sal_Int32& getDMLandVMLTextFrameRotation() { return m_nDMLandVMLTextFrameRotation; } +}; + +DocxSdrExport::DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer, + oox::drawingml::DrawingML* pDrawingML) + : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML)) +{ +} + +DocxSdrExport::~DocxSdrExport() = default; + +void DocxSdrExport::setSerializer(const sax_fastparser::FSHelperPtr& pSerializer) +{ + m_pImpl->setSerializer(pSerializer); +} + +const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); } + +bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); } + +bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); } + +rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyAttrList() +{ + return m_pImpl->getFlyAttrList(); +} + +rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getTextboxAttrList() +{ + return m_pImpl->getTextboxAttrList(); +} + +OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); } + +bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); } + +void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen) +{ + m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen); +} + +bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); } + +bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); } + +void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing) +{ + m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing); +} + +rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyFillAttrList() +{ + return m_pImpl->getFlyFillAttrList(); +} + +sax_fastparser::FastAttributeList* DocxSdrExport::getFlyWrapAttrList() +{ + return m_pImpl->getFlyWrapAttrList(); +} + +sax_fastparser::FastAttributeList* DocxSdrExport::getBodyPrAttrList() +{ + return m_pImpl->getBodyPrAttrList(); +} + +rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getDashLineStyle() +{ + return m_pImpl->getDashLineStyleAttr(); +} + +void DocxSdrExport::setFlyWrapAttrList(sax_fastparser::FastAttributeList* pAttrList) +{ + m_pImpl->setFlyWrapAttrList(pAttrList); +} + +void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize) +{ + m_pImpl->setDrawingOpen(true); + m_pImpl->setParagraphHasDrawing(true); + m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing); + + const SvxLRSpaceItem aLRSpaceItem = pFrameFormat->GetLRSpace(false); + const SvxULSpaceItem aULSpaceItem = pFrameFormat->GetULSpace(false); + + bool isAnchor; + + if (m_pImpl->getFlyFrameGraphic()) + { + isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline + } + else + { + isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR; + } + + // Count effectExtent values, their value is needed before dist{T,B,L,R} is written. + SvxShadowItem aShadowItem = pFrameFormat->GetShadow(); + sal_Int32 nLeftExt = 0; + sal_Int32 nRightExt = 0; + sal_Int32 nTopExt = 0; + sal_Int32 nBottomExt = 0; + if (aShadowItem.GetLocation() != SvxShadowLocation::NONE) + { + sal_Int32 nShadowWidth(TwipsToEMU(aShadowItem.GetWidth())); + switch (aShadowItem.GetLocation()) + { + case SvxShadowLocation::TopLeft: + nTopExt = nLeftExt = nShadowWidth; + break; + case SvxShadowLocation::TopRight: + nTopExt = nRightExt = nShadowWidth; + break; + case SvxShadowLocation::BottomLeft: + nBottomExt = nLeftExt = nShadowWidth; + break; + case SvxShadowLocation::BottomRight: + nBottomExt = nRightExt = nShadowWidth; + break; + case SvxShadowLocation::NONE: + case SvxShadowLocation::End: + break; + } + } + else if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject()) + { + // No shadow, but we have an idea what was the original effectExtent. + uno::Any aAny; + pObject->GetGrabBagItem(aAny); + comphelper::SequenceAsHashMap aGrabBag(aAny); + auto it = aGrabBag.find("CT_EffectExtent"); + if (it != aGrabBag.end()) + { + comphelper::SequenceAsHashMap aEffectExtent(it->second); + for (const std::pair<const OUString, uno::Any>& rDirection : aEffectExtent) + { + if (rDirection.first == "l" && rDirection.second.has<sal_Int32>()) + nLeftExt = rDirection.second.get<sal_Int32>(); + else if (rDirection.first == "t" && rDirection.second.has<sal_Int32>()) + nTopExt = rDirection.second.get<sal_Int32>(); + else if (rDirection.first == "r" && rDirection.second.has<sal_Int32>()) + nRightExt = rDirection.second.get<sal_Int32>(); + else if (rDirection.first == "b" && rDirection.second.has<sal_Int32>()) + nBottomExt = rDirection.second.get<sal_Int32>(); + } + } + } + + if (isAnchor) + { + sax_fastparser::FastAttributeList* attrList + = sax_fastparser::FastSerializerHelper::createAttrList(); + bool bOpaque = pFrameFormat->GetOpaque().GetValue(); + awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), + pFrameFormat->GetVertOrient().GetPos()); + const SdrObject* pObj = pFrameFormat->FindRealSdrObject(); + long nRotation = 0; + if (pObj != nullptr) + { + // SdrObjects know their layer, consider that instead of the frame format. + bOpaque = pObj->GetLayer() + != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId() + && pObj->GetLayer() + != pFrameFormat->GetDoc() + ->getIDocumentDrawModelAccess() + .GetInvisibleHellId(); + + // Do not do this with lines. + if (pObj->GetObjIdentifier() != OBJ_LINE) + { + nRotation = pObj->GetRotateAngle(); + lclMovePositionWithRotation(aPos, rSize, nRotation); + } + } + attrList->add(XML_behindDoc, bOpaque ? "0" : "1"); + // Extend distance with the effect extent if the shape is not rotated, which is the opposite + // of the mapping done at import time. + // The type of dist* attributes is unsigned, so make sure no negative value is written. + sal_Int64 nTopExtDist = nRotation ? 0 : nTopExt; + sal_Int64 nDistT = std::max(static_cast<sal_Int64>(0), + TwipsToEMU(aULSpaceItem.GetUpper()) - nTopExtDist); + attrList->add(XML_distT, OString::number(nDistT).getStr()); + sal_Int64 nBottomExtDist = nRotation ? 0 : nBottomExt; + sal_Int64 nDistB = std::max(static_cast<sal_Int64>(0), + TwipsToEMU(aULSpaceItem.GetLower()) - nBottomExtDist); + attrList->add(XML_distB, OString::number(nDistB).getStr()); + sal_Int64 nLeftExtDist = nRotation ? 0 : nLeftExt; + sal_Int64 nDistL = std::max(static_cast<sal_Int64>(0), + TwipsToEMU(aLRSpaceItem.GetLeft()) - nLeftExtDist); + attrList->add(XML_distL, OString::number(nDistL).getStr()); + sal_Int64 nRightExtDist = nRotation ? 0 : nRightExt; + sal_Int64 nDistR = std::max(static_cast<sal_Int64>(0), + TwipsToEMU(aLRSpaceItem.GetRight()) - nRightExtDist); + attrList->add(XML_distR, OString::number(nDistR).getStr()); + attrList->add(XML_simplePos, "0"); + attrList->add(XML_locked, "0"); + bool bLclInTabCell = true; + if (pObj) + { + uno::Reference<drawing::XShape> xShape((const_cast<SdrObject*>(pObj)->getUnoShape()), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + if (xShapeProps.is()) + xShapeProps->getPropertyValue("IsFollowingTextFlow") >>= bLclInTabCell; + } + if (bLclInTabCell) + attrList->add(XML_layoutInCell, "1"); + else + attrList->add(XML_layoutInCell, "0"); + bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap(); + attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0"); + if (pObj != nullptr) + // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that. + attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2)); + else + // relativeHeight is mandatory attribute, if value is not present, we must write default value + attrList->add(XML_relativeHeight, "0"); + if (pObj != nullptr) + { + OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj); + if (!sAnchorId.isEmpty()) + attrList->addNS(XML_wp14, XML_anchorId, + OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); + } + sax_fastparser::XFastAttributeListRef xAttrList(attrList); + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, xAttrList); + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y, + "0"); // required, unused + const char* relativeFromH; + const char* relativeFromV; + const char* alignH = nullptr; + const char* alignV = nullptr; + switch (pFrameFormat->GetVertOrient().GetRelationOrient()) + { + case text::RelOrientation::PAGE_PRINT_AREA: + relativeFromV = "margin"; + break; + case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM: + relativeFromV = "bottomMargin"; + break; + case text::RelOrientation::PAGE_FRAME: + relativeFromV = "page"; + break; + case text::RelOrientation::FRAME: + relativeFromV = "paragraph"; + break; + case text::RelOrientation::TEXT_LINE: + default: + relativeFromV = "line"; + break; + } + switch (pFrameFormat->GetVertOrient().GetVertOrient()) + { + case text::VertOrientation::TOP: + case text::VertOrientation::CHAR_TOP: + case text::VertOrientation::LINE_TOP: + if (pFrameFormat->GetVertOrient().GetRelationOrient() + == text::RelOrientation::TEXT_LINE) + alignV = "bottom"; + else + alignV = "top"; + break; + case text::VertOrientation::BOTTOM: + case text::VertOrientation::CHAR_BOTTOM: + case text::VertOrientation::LINE_BOTTOM: + if (pFrameFormat->GetVertOrient().GetRelationOrient() + == text::RelOrientation::TEXT_LINE) + alignV = "top"; + else + alignV = "bottom"; + break; + case text::VertOrientation::CENTER: + case text::VertOrientation::CHAR_CENTER: + case text::VertOrientation::LINE_CENTER: + alignV = "center"; + break; + default: + break; + } + switch (pFrameFormat->GetHoriOrient().GetRelationOrient()) + { + case text::RelOrientation::PAGE_PRINT_AREA: + relativeFromH = "margin"; + break; + case text::RelOrientation::PAGE_FRAME: + relativeFromH = "page"; + break; + case text::RelOrientation::CHAR: + relativeFromH = "character"; + break; + case text::RelOrientation::PAGE_RIGHT: + relativeFromH = "rightMargin"; + break; + case text::RelOrientation::PAGE_LEFT: + relativeFromH = "leftMargin"; + break; + case text::RelOrientation::FRAME: + default: + relativeFromH = "column"; + break; + } + switch (pFrameFormat->GetHoriOrient().GetHoriOrient()) + { + case text::HoriOrientation::LEFT: + alignH = "left"; + break; + case text::HoriOrientation::RIGHT: + alignH = "right"; + break; + case text::HoriOrientation::CENTER: + alignH = "center"; + break; + case text::HoriOrientation::INSIDE: + alignH = "inside"; + break; + case text::HoriOrientation::OUTSIDE: + alignH = "outside"; + break; + default: + break; + } + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom, + relativeFromH); + /** + * Sizes of integral types + * climits header defines constants with the limits of integral types for the specific system and compiler implementation used. + * Use of this might cause platform dependent problem like posOffset exceed the limit. + **/ + const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32; + const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32; + if (alignH != nullptr) + { + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align); + m_pImpl->getSerializer()->write(alignH); + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align); + } + else + { + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset); + sal_Int64 nPosXEMU = TwipsToEMU(aPos.X); + + /* Absolute Position Offset Value is of type Int. Hence it should not be greater than + * Maximum value for Int OR Less than the Minimum value for Int. + * - Maximum value for Int = 2147483647 + * - Minimum value for Int = -2147483648 + * + * As per ECMA Specification : ECMA-376, Second Edition, + * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)] + * + * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html + */ + + if (nPosXEMU > MAX_INTEGER_VALUE) + { + nPosXEMU = MAX_INTEGER_VALUE; + } + else if (nPosXEMU < MIN_INTEGER_VALUE) + { + nPosXEMU = MIN_INTEGER_VALUE; + } + m_pImpl->getSerializer()->write(nPosXEMU); + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset); + } + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH); + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom, + relativeFromV); + + sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y); + + // tdf#93675, 0 below line/paragraph and/or top line/paragraph with + // wrap top+bottom or other wraps is affecting the line directly + // above the anchor line, which seems odd, but a tiny adjustment + // here to bring the top down convinces msoffice to wrap like us + if (nPosYEMU == 0 + && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0) + && (!alignV || strcmp(alignV, "top") == 0)) + { + alignV = nullptr; + nPosYEMU = 635; + } + + if (alignV != nullptr) + { + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align); + m_pImpl->getSerializer()->write(alignV); + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align); + } + else + { + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset); + if (nPosYEMU > MAX_INTEGER_VALUE) + { + nPosYEMU = MAX_INTEGER_VALUE; + } + else if (nPosYEMU < MIN_INTEGER_VALUE) + { + nPosYEMU = MIN_INTEGER_VALUE; + } + m_pImpl->getSerializer()->write(nPosYEMU); + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset); + } + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV); + } + else + { + sax_fastparser::FastAttributeList* aAttrList + = sax_fastparser::FastSerializerHelper::createAttrList(); + aAttrList->add(XML_distT, OString::number(TwipsToEMU(aULSpaceItem.GetUpper())).getStr()); + aAttrList->add(XML_distB, OString::number(TwipsToEMU(aULSpaceItem.GetLower())).getStr()); + aAttrList->add(XML_distL, OString::number(TwipsToEMU(aLRSpaceItem.GetLeft())).getStr()); + aAttrList->add(XML_distR, OString::number(TwipsToEMU(aLRSpaceItem.GetRight())).getStr()); + const SdrObject* pObj = pFrameFormat->FindRealSdrObject(); + if (pObj != nullptr) + { + OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj); + if (!sAnchorId.isEmpty()) + aAttrList->addNS(XML_wp14, XML_anchorId, + OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); + } + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList); + } + + // now the common parts + // extent of the image + /** + * Extent width is of type long ( i.e cx & cy ) as + * + * per ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference + * [ 20.4.2.7 extent (Drawing Object Size)] + * + * cy is of type a:ST_PositiveCoordinate. + * Minimum inclusive: 0 + * Maximum inclusive: 27273042316900 + * + * reference : http://www.schemacentral.com/sc/ooxml/e-wp_extent-1.html + * + * Though ECMA mentions the max value as aforementioned. It appears that MSO does not + * handle for the same, in fact it actually can handle a max value of int32 i.e + * 2147483647( MAX_INTEGER_VALUE ). + * Therefore changing the following accordingly so that LO sync's up with MSO. + **/ + sal_uInt64 cx = TwipsToEMU(std::clamp(rSize.Width(), 0L, long(SAL_MAX_INT32))); + OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32)))); + sal_uInt64 cy = TwipsToEMU(std::clamp(rSize.Height(), 0L, long(SAL_MAX_INT32))); + OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32)))); + + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight); + + // effectExtent, extent including the effect (shadow only for now) + m_pImpl->getSerializer()->singleElementNS( + XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExt), XML_t, OString::number(nTopExt), + XML_r, OString::number(nRightExt), XML_b, OString::number(nBottomExt)); + + // See if we know the exact wrap type from grab-bag. + sal_Int32 nWrapToken = 0; + if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject()) + { + uno::Any aAny; + pObject->GetGrabBagItem(aAny); + comphelper::SequenceAsHashMap aGrabBag(aAny); + auto it = aGrabBag.find("EG_WrapType"); + if (it != aGrabBag.end()) + { + auto sType = it->second.get<OUString>(); + if (sType == "wrapTight") + nWrapToken = XML_wrapTight; + else if (sType == "wrapThrough") + nWrapToken = XML_wrapThrough; + else + SAL_WARN("sw.ww8", + "DocxSdrExport::startDMLAnchorInline: unexpected EG_WrapType value"); + + m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, "bothSides"); + + it = aGrabBag.find("CT_WrapPath"); + if (it != aGrabBag.end()) + { + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0"); + auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>(); + auto aPoints(comphelper::sequenceToContainer<std::vector<awt::Point>>(aSeqSeq[0])); + for (auto i = aPoints.begin(); i != aPoints.end(); ++i) + { + awt::Point& rPoint = *i; + m_pImpl->getSerializer()->singleElementNS( + XML_wp, (i == aPoints.begin() ? XML_start : XML_lineTo), XML_x, + OString::number(rPoint.X), XML_y, OString::number(rPoint.Y)); + } + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon); + } + + m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken); + } + } + + // Or if we have a contour. + if (!nWrapToken && pFrameFormat->GetSurround().IsContour()) + { + if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat)) + { + const tools::PolyPolygon* pPolyPoly = pNd->HasContour(); + if (pPolyPoly && pPolyPoly->Count()) + { + nWrapToken = XML_wrapTight; + m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, + "bothSides"); + + m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0"); + tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport( + *pPolyPoly, pNd, /*bCorrectCrop=*/true); + for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i) + m_pImpl->getSerializer()->singleElementNS( + XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x, + OString::number(aPoly[i].X()), XML_y, OString::number(aPoly[i].Y())); + m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon); + + m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken); + } + } + } + + // No? Then just approximate based on what we have. + if (isAnchor && !nWrapToken) + { + switch (pFrameFormat->GetSurround().GetValue()) + { + case css::text::WrapTextMode_NONE: + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom); + break; + case css::text::WrapTextMode_THROUGH: + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone); + break; + case css::text::WrapTextMode_PARALLEL: + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, + "bothSides"); + break; + case css::text::WrapTextMode_DYNAMIC: + default: + m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, + "largest"); + break; + } + } +} + +void DocxSdrExport::endDMLAnchorInline(const SwFrameFormat* pFrameFormat) +{ + bool isAnchor; + if (m_pImpl->getFlyFrameGraphic()) + { + isAnchor = false; // end Inline Graphic object inside DMLTextFrame + } + else + { + isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR; + } + m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline); + + m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing); + m_pImpl->setDrawingOpen(false); +} + +void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat) +{ + m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict); + m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer()); + // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually. + + const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient(); + const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient(); + SwFormatSurround const& rSurround(rFrameFormat.GetSurround()); + + std::unique_ptr<sax_fastparser::FastAttributeList> pAttrList( + docx::SurroundToVMLWrap(rSurround)); + m_pImpl->getExport().VMLExporter().AddSdrObject( + *sdrObj, rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(), rHoriOri.GetRelationOrient(), + rVertOri.GetRelationOrient(), std::move(pAttrList), true); + m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict); +} + +static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape) +{ + uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag"); + /* + * Export as Locked Canvas only if the property + * is in the PropertySet + */ + return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) { + return rProp.Name == "LockedCanvas"; + }); +} + +void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat, + int nAnchorId) +{ + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(), + uno::UNO_QUERY_THROW); + if (!Impl::isSupportedDMLShape(xShape)) + return; + + m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject); + + sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); + Size aSize(pSdrObject->GetLogicRect().GetWidth(), pSdrObject->GetLogicRect().GetHeight()); + startDMLAnchorInline(pFrameFormat, aSize); + + sax_fastparser::FastAttributeList* pDocPrAttrList + = sax_fastparser::FastSerializerHelper::createAttrList(); + pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr()); + pDocPrAttrList->add(XML_name, + OUStringToOString(pSdrObject->GetName(), RTL_TEXTENCODING_UTF8).getStr()); + if (!pSdrObject->GetTitle().isEmpty()) + pDocPrAttrList->add(XML_title, + OUStringToOString(pSdrObject->GetTitle(), RTL_TEXTENCODING_UTF8)); + if (!pSdrObject->GetDescription().isEmpty()) + pDocPrAttrList->add(XML_descr, + OUStringToOString(pSdrObject->GetDescription(), RTL_TEXTENCODING_UTF8)); + if (!pSdrObject->IsVisible() + && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) + + pDocPrAttrList->add(XML_hidden, OString::number(1).getStr()); + sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList); + pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef); + + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); + const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"; + if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"; + else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) + pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture"; + pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8()); + pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace); + + bool bLockedCanvas = lcl_isLockedCanvas(xShape); + if (bLockedCanvas) + pFS->startElementNS( + XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)).toUtf8()); + + m_pImpl->getExport().OutputDML(xShape); + + if (bLockedCanvas) + pFS->endElementNS(XML_lc, XML_lockedCanvas); + pFS->endElementNS(XML_a, XML_graphicData); + pFS->endElementNS(XML_a, XML_graphic); + + // Relative size of the drawing. + if (pSdrObject->GetRelativeWidth()) + { + // At the moment drawinglayer objects are always relative from page. + pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, + (pSdrObject->GetRelativeWidthRelation() == text::RelOrientation::FRAME + ? "margin" + : "page")); + pFS->startElementNS(XML_wp14, XML_pctWidth); + pFS->writeEscaped( + OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT)); + pFS->endElementNS(XML_wp14, XML_pctWidth); + pFS->endElementNS(XML_wp14, XML_sizeRelH); + } + if (pSdrObject->GetRelativeHeight()) + { + pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, + (pSdrObject->GetRelativeHeightRelation() == text::RelOrientation::FRAME + ? "margin" + : "page")); + pFS->startElementNS(XML_wp14, XML_pctHeight); + pFS->writeEscaped( + OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT)); + pFS->endElementNS(XML_wp14, XML_pctHeight); + pFS->endElementNS(XML_wp14, XML_sizeRelV); + } + + endDMLAnchorInline(pFrameFormat); +} + +void DocxSdrExport::Impl::textFrameShadow(const SwFrameFormat& rFrameFormat) +{ + const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); + if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) + return; + + OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt"); + OString aOffset; + switch (aShadowItem.GetLocation()) + { + case SvxShadowLocation::TopLeft: + aOffset = "-" + aShadowWidth + ",-" + aShadowWidth; + break; + case SvxShadowLocation::TopRight: + aOffset = aShadowWidth + ",-" + aShadowWidth; + break; + case SvxShadowLocation::BottomLeft: + aOffset = "-" + aShadowWidth + "," + aShadowWidth; + break; + case SvxShadowLocation::BottomRight: + aOffset = aShadowWidth + "," + aShadowWidth; + break; + case SvxShadowLocation::NONE: + case SvxShadowLocation::End: + break; + } + if (aOffset.isEmpty()) + return; + + OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor()); + m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor, + XML_offset, aOffset); +} + +bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape) +{ + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW); + if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape") + || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape")) + return false; + + // For signature line shapes, we don't want DML, just the VML shape. + bool bIsSignatureLineShape = false; + if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape")) + { + uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY); + xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape; + if (bIsSignatureLineShape) + return false; + } + + return true; +} + +void DocxSdrExport::writeDMLAndVMLDrawing(const SdrObject* sdrObj, + const SwFrameFormat& rFrameFormat, int nAnchorId) +{ + bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); + m_pImpl->setDMLAndVMLDrawingOpen(true); + + // Depending on the shape type, we actually don't write the shape as DML. + OUString sShapeType; + ShapeFlag nMirrorFlags = ShapeFlag::NONE; + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape(), + uno::UNO_QUERY_THROW); + + MSO_SPT eShapeType + = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType); + + // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do. + // A common service created in util to check for VML shapes which are allowed to have textbox in content + if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape) + && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML + { + m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent); + + auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj); + m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires, + (pObjGroup ? "wpg" : "wps")); + writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId); + m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice); + + m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback); + writeVMLDrawing(sdrObj, rFrameFormat); + m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback); + + m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent); + } + else + writeVMLDrawing(sdrObj, rFrameFormat); + + m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); +} + +// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000) +static OString lcl_ConvertTransparency(const Color& rColor) +{ + if (rColor.GetTransparency() > 0) + { + sal_Int32 nTransparencyPercent = 100 - float(rColor.GetTransparency()) / 2.55; + return OString::number(nTransparencyPercent * oox::drawingml::PER_PERCENT); + } + + return OString(); +} + +void DocxSdrExport::writeDMLEffectLst(const SwFrameFormat& rFrameFormat) +{ + const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); + + // Output effects + if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) + return; + + // Distance is measured diagonally from corner + double nShadowDist + = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0); + OString aShadowDist(OString::number(TwipsToEMU(nShadowDist))); + OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor()); + OString aShadowAlpha = lcl_ConvertTransparency(aShadowItem.GetColor()); + sal_uInt32 nShadowDir = 0; + switch (aShadowItem.GetLocation()) + { + case SvxShadowLocation::TopLeft: + nShadowDir = 13500000; + break; + case SvxShadowLocation::TopRight: + nShadowDir = 18900000; + break; + case SvxShadowLocation::BottomLeft: + nShadowDir = 8100000; + break; + case SvxShadowLocation::BottomRight: + nShadowDir = 2700000; + break; + case SvxShadowLocation::NONE: + case SvxShadowLocation::End: + break; + } + OString aShadowDir(OString::number(nShadowDir)); + + m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst); + m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir, + aShadowDir); + if (aShadowAlpha.isEmpty()) + m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor); + else + { + m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor); + m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha); + m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr); + } + m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw); + m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst); +} + +void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, + int nDiagramId) +{ + uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(), + uno::UNO_QUERY); + + // write necessary tags to document.xml + Size aSize(sdrObject->GetSnapRect().GetWidth(), sdrObject->GetSnapRect().GetHeight()); + startDMLAnchorInline(&rFrameFormat, aSize); + + m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId); + + endDMLAnchorInline(&rFrameFormat); +} + +void DocxSdrExport::writeOnlyTextOfFrame(ww8::Frame const* pParentFrame) +{ + const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + + //Save data here and restore when out of scope + ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); + + m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); + ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); + comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); + m_pImpl->getExport().WriteText(); +} + +void DocxSdrExport::writeBoxItemLine(const SvxBoxItem& rBox) +{ + const editeng::SvxBorderLine* pBorderLine = nullptr; + + if (rBox.GetTop()) + { + pBorderLine = rBox.GetTop(); + } + else if (rBox.GetLeft()) + { + pBorderLine = rBox.GetLeft(); + } + else if (rBox.GetBottom()) + { + pBorderLine = rBox.GetBottom(); + } + else if (rBox.GetRight()) + { + pBorderLine = rBox.GetRight(); + } + + if (!pBorderLine) + { + return; + } + + sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); + double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(), + pBorderLine->GetWidth())); + OString sWidth(OString::number(TwipsToEMU(fConverted))); + pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth); + + pFS->startElementNS(XML_a, XML_solidFill); + OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor())); + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor); + pFS->endElementNS(XML_a, XML_solidFill); + + if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type + pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash"); + + pFS->endElementNS(XML_a, XML_ln); +} + +void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId, + bool bTextBoxOnly) +{ + bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); + m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame)); + + sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); + const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + + //Save data here and restore when out of scope + ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); + + // When a frame has some low height, but automatically expanded due + // to lots of contents, this size contains the real size. + const Size aSize = pParentFrame->GetSize(); + + uno::Reference<drawing::XShape> xShape; + const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject(); + if (pSdrObj) + xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropSetInfo; + if (xPropertySet.is()) + xPropSetInfo = xPropertySet->getPropertySetInfo(); + + m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); + { + drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP; + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust")) + xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust; + m_pImpl->getBodyPrAttrList()->add(XML_anchor, + oox::drawingml::GetTextVerticalAdjust(eAdjust)); + } + + if (!bTextBoxOnly) + { + startDMLAnchorInline(&rFrameFormat, aSize); + + sax_fastparser::FastAttributeList* pDocPrAttrList + = sax_fastparser::FastSerializerHelper::createAttrList(); + pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr()); + pDocPrAttrList->add( + XML_name, OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8).getStr()); + sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList); + pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef); + + pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a), + m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8()); + pFS->startElementNS(XML_a, XML_graphicData, XML_uri, + "http://schemas.microsoft.com/office/word/2010/wordprocessingShape"); + pFS->startElementNS(XML_wps, XML_wsp); + pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1"); + + uno::Any aRotation; + m_pImpl->setDMLandVMLTextFrameRotation(0); + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) + { + uno::Sequence<beans::PropertyValue> propList; + xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { + return rProp.Name == "mso-rotation-angle"; + }); + if (pProp != propList.end()) + aRotation = pProp->Value; + } + aRotation >>= m_pImpl->getDMLandVMLTextFrameRotation(); + OString sRotation(OString::number( + oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation()))); + // Shape properties + pFS->startElementNS(XML_wps, XML_spPr); + if (m_pImpl->getDMLandVMLTextFrameRotation()) + { + pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation); + } + else + { + pFS->startElementNS(XML_a, XML_xfrm); + } + pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0"); + OString aWidth(OString::number(TwipsToEMU(aSize.Width()))); + OString aHeight(OString::number(TwipsToEMU(aSize.Height()))); + pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight); + pFS->endElementNS(XML_a, XML_xfrm); + OUString shapeType = "rect"; + if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag")) + { + uno::Sequence<beans::PropertyValue> propList; + xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList; + auto pProp = std::find_if(propList.begin(), propList.end(), + [](const beans::PropertyValue& rProp) { + return rProp.Name == "mso-orig-shape-type"; + }); + if (pProp != propList.end()) + pProp->Value >>= shapeType; + } + //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect" + if (shapeType.isEmpty()) + shapeType = "rect"; + + pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType.toUtf8()); + m_pImpl->setDMLTextFrameSyntax(true); + m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true); + m_pImpl->setDMLTextFrameSyntax(false); + writeDMLEffectLst(rFrameFormat); + pFS->endElementNS(XML_wps, XML_spPr); + } + + //first, loop through ALL of the chained textboxes to identify a unique ID for each chain, and sequence number for each textbox in that chain. + if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized) + { + sal_Int32 nSeq = 0; + for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper) + { + //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link + if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty()) + { + //assign this chain a unique ID and start a new sequence + nSeq = 0; + rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId; + rEntry.second.nSeq = nSeq; + + OUString sCheckForBrokenChains = rEntry.first; + + //follow the chain and assign the same id, and incremental sequence numbers. + auto followChainIter + = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain); + while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end()) + { + //verify that the NEXT textbox also points to me as the PREVIOUS. + // A broken link indicates a leftover remnant that can be ignored. + if (followChainIter->second.sPrevChain != sCheckForBrokenChains) + break; + + followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId; + followChainIter->second.nSeq = ++nSeq; + + //empty next chain indicates the end of the linked chain. + if (followChainIter->second.sNextChain.isEmpty()) + break; + + sCheckForBrokenChains = followChainIter->first; + followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find( + followChainIter->second.sNextChain); + } + } + } + m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true; + } + + m_pImpl->getExport().m_pParentFrame = nullptr; + bool skipTxBxContent = false; + bool isTxbxLinked = false; + + OUString sLinkChainName; + if (xPropSetInfo.is()) + { + if (xPropSetInfo->hasPropertyByName("LinkDisplayName")) + xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName; + else if (xPropSetInfo->hasPropertyByName("ChainName")) + xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName; + } + + // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx + auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName); + if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end()) + { + if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0)) + { + //not the first in the chain, so write the tag as linkedTxbx + pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id, + OString::number(linkedTextboxesIter->second.nId), XML_seq, + OString::number(linkedTextboxesIter->second.nSeq)); + /* no text content should be added to this tag, + since the textbox is linked, the entire content + is written in txbx block + */ + skipTxBxContent = true; + } + else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0)) + { + /* this is the first textbox in the chaining, we add the text content + to this block*/ + //since the text box is linked, it needs an id. + pFS->startElementNS(XML_wps, XML_txbx, XML_id, + OString::number(linkedTextboxesIter->second.nId)); + isTxbxLinked = true; + } + } + + if (!skipTxBxContent) + { + if (!isTxbxLinked) + pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id. + + pFS->startElementNS(XML_w, XML_txbxContent); + + const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir(); + if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB) + m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert"); + else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT) + m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270"); + + { + ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); + comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); + m_pImpl->getExport().WriteText(); + if (m_pImpl->getParagraphSdtOpen()) + { + m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock(); + m_pImpl->setParagraphSdtOpen(false); + } + } + + pFS->endElementNS(XML_w, XML_txbxContent); + pFS->endElementNS(XML_wps, XML_txbx); + } + + // We need to init padding to 0, if it's not set. + // In LO the default is 0 and so ins attributes are not set when padding is 0 + // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly + if (m_pImpl->getBodyPrAttrList()) + { + if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns)) + m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0)); + if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns)) + m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0)); + if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns)) + m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0)); + if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns)) + m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0)); + } + + sax_fastparser::XFastAttributeListRef xBodyPrAttrList(m_pImpl->getBodyPrAttrList()); + m_pImpl->setBodyPrAttrList(nullptr); + if (!bTextBoxOnly) + { + pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList); + // AutoSize of the Text Frame. + const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize(); + pFS->singleElementNS( + XML_a, + (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit)); + pFS->endElementNS(XML_wps, XML_bodyPr); + + pFS->endElementNS(XML_wps, XML_wsp); + pFS->endElementNS(XML_a, XML_graphicData); + pFS->endElementNS(XML_a, XML_graphic); + + // Relative size of the Text Frame. + const sal_uInt8 nWidthPercent = rSize.GetWidthPercent(); + if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED) + { + pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, + (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME + ? "page" + : "margin")); + pFS->startElementNS(XML_wp14, XML_pctWidth); + pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT)); + pFS->endElementNS(XML_wp14, XML_pctWidth); + pFS->endElementNS(XML_wp14, XML_sizeRelH); + } + const sal_uInt8 nHeightPercent = rSize.GetHeightPercent(); + if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED) + { + pFS->startElementNS( + XML_wp14, XML_sizeRelV, XML_relativeFrom, + (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page" + : "margin")); + pFS->startElementNS(XML_wp14, XML_pctHeight); + pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT)); + pFS->endElementNS(XML_wp14, XML_pctHeight); + pFS->endElementNS(XML_wp14, XML_sizeRelV); + } + + endDMLAnchorInline(&rFrameFormat); + } + m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); +} + +void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly) +{ + bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen(); + m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame)); + + sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer(); + const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat(); + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + + //Save data here and restore when out of scope + ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame); + + // When a frame has some low height, but automatically expanded due + // to lots of contents, this size contains the real size. + const Size aSize = pParentFrame->GetSize(); + m_pImpl->setFlyFrameSize(&aSize); + + m_pImpl->setTextFrameSyntax(true); + m_pImpl->setFlyAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); + m_pImpl->setTextboxAttrList(sax_fastparser::FastSerializerHelper::createAttrList()); + m_pImpl->getTextFrameStyle() = "position:absolute"; + if (!bTextBoxOnly) + { + OString sRotation(OString::number(m_pImpl->getDMLandVMLTextFrameRotation() / -100)); + m_pImpl->getExport() + .SdrExporter() + .getTextFrameStyle() + .append(";rotation:") + .append(sRotation); + } + m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true); + m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear()); + + const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject(); + if (pObject != nullptr) + { + OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject); + if (!sAnchorId.isEmpty()) + m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, + OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8)); + } + sax_fastparser::XFastAttributeListRef xFlyAttrList(m_pImpl->getFlyAttrList().get()); + m_pImpl->getFlyAttrList().clear(); + sax_fastparser::XFastAttributeListRef xTextboxAttrList(m_pImpl->getTextboxAttrList().get()); + m_pImpl->getTextboxAttrList().clear(); + m_pImpl->setTextFrameSyntax(false); + m_pImpl->setFlyFrameSize(nullptr); + m_pImpl->getExport().m_pParentFrame = nullptr; + + if (!bTextBoxOnly) + { + pFS->startElementNS(XML_w, XML_pict); + pFS->startElementNS(XML_v, XML_rect, xFlyAttrList); + m_pImpl->textFrameShadow(rFrameFormat); + if (m_pImpl->getFlyFillAttrList().is()) + { + sax_fastparser::XFastAttributeListRef xFlyFillAttrList( + m_pImpl->getFlyFillAttrList().get()); + m_pImpl->getFlyFillAttrList().clear(); + pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList); + } + if (m_pImpl->getDashLineStyleAttr().is()) + { + sax_fastparser::XFastAttributeListRef xDashLineStyleAttr( + m_pImpl->getDashLineStyleAttr().get()); + m_pImpl->getDashLineStyleAttr().clear(); + pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr); + } + pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList); + } + pFS->startElementNS(XML_w, XML_txbxContent); + { + ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); + comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); + m_pImpl->getExport().WriteText(); + if (m_pImpl->getParagraphSdtOpen()) + { + m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock(); + m_pImpl->setParagraphSdtOpen(false); + } + } + pFS->endElementNS(XML_w, XML_txbxContent); + if (!bTextBoxOnly) + { + pFS->endElementNS(XML_v, XML_textbox); + + if (m_pImpl->getFlyWrapAttrList()) + { + sax_fastparser::XFastAttributeListRef xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList()); + m_pImpl->setFlyWrapAttrList(nullptr); + pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList); + } + + pFS->endElementNS(XML_v, XML_rect); + pFS->endElementNS(XML_w, XML_pict); + } + + m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen); +} + +bool DocxSdrExport::isTextBox(const SwFrameFormat& rFrameFormat) +{ + return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxsdrexport.hxx b/sw/source/filter/ww8/docxsdrexport.hxx new file mode 100644 index 000000000..aecb83c23 --- /dev/null +++ b/sw/source/filter/ww8/docxsdrexport.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX + +#include <memory> + +#include <rtl/strbuf.hxx> +#include <sax/fshelper.hxx> +#include <tools/solar.h> + +namespace rtl +{ +template <typename> class Reference; +} +namespace oox +{ +namespace drawingml +{ +class DrawingML; +} +} +class Size; +class SdrObject; +class SvxBoxItem; + +namespace ww8 +{ +class Frame; +} +class SwFrameFormat; + +class DocxExport; + +/// Helper class, so that the DocxExport::RestoreData() call will always happen. +class ExportDataSaveRestore +{ +private: + DocxExport& m_rExport; +public: + ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd, ww8::Frame const* pParentFrame); + ~ExportDataSaveRestore(); +}; + +/// Handles DOCX export of drawings. +class DocxSdrExport +{ + struct Impl; + std::unique_ptr<Impl> m_pImpl; +public: + DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML); + ~DocxSdrExport(); + + void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer); + /// When exporting fly frames, this holds the real size of the frame. + const Size* getFlyFrameSize() const; + bool getTextFrameSyntax() const; + bool getDMLTextFrameSyntax() const; + rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList(); + /// Attributes of the next v:textbox element. + rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList(); + OStringBuffer& getTextFrameStyle(); + + /// Set if paragraph sdt open in the current drawing. + void setParagraphSdtOpen(bool bParagraphSdtOpen); + + bool IsDrawingOpen() const; + bool IsDMLAndVMLDrawingOpen() const; + bool IsParagraphHasDrawing() const; + void setParagraphHasDrawing(bool bParagraphHasDrawing); + rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList(); + sax_fastparser::FastAttributeList* getFlyWrapAttrList(); + void setFlyWrapAttrList(sax_fastparser::FastAttributeList* pAttrList); + /// Attributes of <wps:bodyPr>, used during DML export of text frames. + sax_fastparser::FastAttributeList* getBodyPrAttrList(); + rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyle(); + + void startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize); + void endDMLAnchorInline(const SwFrameFormat* pFrameFormat); + /// Writes a drawing as VML data. + void writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat); + /// Writes a drawing as DML. + void writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat, int nAnchorId); + /// Writes shape in both DML and VML format. + void writeDMLAndVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat, int nAnchorId); + /// Write <a:effectLst>, the effect list. + void writeDMLEffectLst(const SwFrameFormat& rFrameFormat); + /// Writes a diagram (smartart). + void writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat, int nDiagramId); + /// Writes text frame in DML format. + void writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId, bool bTextBoxOnly = false); + /// Writes text frame in VML format. + void writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly = false); + /// Is this a standalone TextFrame, or used as a TextBox of a shape? + static bool isTextBox(const SwFrameFormat& rFrameFormat); + /// Writes text from Textbox for <w:framePr> + void writeOnlyTextOfFrame(ww8::Frame const* pParentFrame); + /// Writes the drawingML <a:ln> markup of a box item. + void writeBoxItemLine(const SvxBoxItem& rBox); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXSDREXPORT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxtablestyleexport.cxx b/sw/source/filter/ww8/docxtablestyleexport.cxx new file mode 100644 index 000000000..3854e2b6f --- /dev/null +++ b/sw/source/filter/ww8/docxtablestyleexport.cxx @@ -0,0 +1,751 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "docxtablestyleexport.hxx" +#include "docxhelper.hxx" +#include <doc.hxx> +#include <docsh.hxx> +#include <oox/token/tokens.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <sax/fastattribs.hxx> + +#include <optional> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XModel.hpp> + +using namespace com::sun::star; +using namespace oox; + +/// Methods in this class handle values in a table style. +struct DocxTableStyleExport::Impl +{ +private: + SwDoc* m_pDoc; + sax_fastparser::FSHelperPtr m_pSerializer; + +public: + Impl(SwDoc* pDoc) + : m_pDoc(pDoc) + { + } + + void TableStyle(const uno::Sequence<beans::PropertyValue>& rStyle); + + void setSerializer(sax_fastparser::FSHelperPtr pSerializer) + { + m_pSerializer = std::move(pSerializer); + } + + const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; } + + SwDoc* getDoc() const { return m_pDoc; } + + /// Handles a boolean value. + void handleBoolean(const OUString& aValue, sal_Int32 nToken); + + /// Export of w:pPr. + void tableStylePPr(const uno::Sequence<beans::PropertyValue>& rPPr); + /// Export of w:tblStylePr. + void tableStyleTableStylePr(const uno::Sequence<beans::PropertyValue>& rTableStylePr); + /// Export of w:rPr. + void tableStyleRPr(const uno::Sequence<beans::PropertyValue>& rRPr); + /// Export of w:rFonts. + void tableStyleRRFonts(const uno::Sequence<beans::PropertyValue>& rRFonts); + /// Export of w:lang. + void tableStyleRLang(const uno::Sequence<beans::PropertyValue>& rLang); + /// Export of w:ind in a pPr. + void tableStylePInd(const uno::Sequence<beans::PropertyValue>& rInd); + /// Export of w:spacing. + void tableStylePSpacing(const uno::Sequence<beans::PropertyValue>& rSpacing); + /// Export of w:tblPr. + void tableStyleTablePr(const uno::Sequence<beans::PropertyValue>& rTablePr); + /// Export of w:trPr. + void tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr); + /// Export of w:tcPr. + void tableStyleTcPr(const uno::Sequence<beans::PropertyValue>& rTcPr); + /// Export of w:tcBorders (and w:tblBorders). + void tableStyleTcBorders(const uno::Sequence<beans::PropertyValue>& rTcBorders, + sal_Int32 nToken = XML_tcBorders); + /// Export of w:tblInd. + void tableStyleTableInd(const uno::Sequence<beans::PropertyValue>& rTableInd); + /// Export of w:tblCellMar (and w:tcMar). + void tableStyleTableCellMar(const uno::Sequence<beans::PropertyValue>& rTableCellMar, + sal_Int32 nType = XML_tblCellMar); + /// Export of a given table cell border type. + void tableStyleTcBorder(sal_Int32 nToken, const uno::Sequence<beans::PropertyValue>& rTcBorder); + /// Export of w:shd. + void tableStyleShd(const uno::Sequence<beans::PropertyValue>& rShd); + /// Export of w:color. + void tableStyleRColor(const uno::Sequence<beans::PropertyValue>& rColor); +}; + +void DocxTableStyleExport::CnfStyle(const uno::Sequence<beans::PropertyValue>& rAttributeList) +{ + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + + for (const auto& rAttribute : rAttributeList) + { + if (rAttribute.Name == "val") + pAttributeList->add(FSNS(XML_w, XML_val), rAttribute.Value.get<OUString>().toUtf8()); + else + { + static DocxStringTokenMap const aTokens[] + = { { "firstRow", XML_firstRow }, + { "lastRow", XML_lastRow }, + { "firstColumn", XML_firstColumn }, + { "lastColumn", XML_lastColumn }, + { "oddVBand", XML_oddVBand }, + { "evenVBand", XML_evenVBand }, + { "oddHBand", XML_oddHBand }, + { "evenHBand", XML_evenHBand }, + { "firstRowFirstColumn", XML_firstRowFirstColumn }, + { "firstRowLastColumn", XML_firstRowLastColumn }, + { "lastRowFirstColumn", XML_lastRowFirstColumn }, + { "lastRowLastColumn", XML_lastRowLastColumn }, + { nullptr, 0 } }; + + if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name)) + pAttributeList->add(FSNS(XML_w, nToken), rAttribute.Value.get<OUString>().toUtf8()); + } + } + + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pImpl->getSerializer()->singleElementNS(XML_w, XML_cnfStyle, xAttributeList); +} + +void DocxTableStyleExport::TableStyles(sal_Int32 nCountStylesToWrite) +{ + // Do we have table styles from InteropGrabBag available? + uno::Reference<beans::XPropertySet> xPropertySet( + m_pImpl->getDoc()->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aInteropGrabBag; + xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag; + uno::Sequence<beans::PropertyValue> aTableStyles; + auto pProp = std::find_if( + aInteropGrabBag.begin(), aInteropGrabBag.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "tableStyles"; }); + if (pProp != aInteropGrabBag.end()) + pProp->Value >>= aTableStyles; + if (!aTableStyles.hasElements()) + return; + + if (nCountStylesToWrite > aTableStyles.getLength()) + nCountStylesToWrite = aTableStyles.getLength(); + + for (sal_Int32 i = 0; i < nCountStylesToWrite; ++i) + { + uno::Sequence<beans::PropertyValue> aTableStyle; + aTableStyles[i].Value >>= aTableStyle; + m_pImpl->TableStyle(aTableStyle); + } +} + +void DocxTableStyleExport::Impl::tableStyleTableCellMar( + const uno::Sequence<beans::PropertyValue>& rTableCellMar, sal_Int32 nType) +{ + static DocxStringTokenMap const aTableCellMarTokens[] + = { { "left", XML_left }, { "right", XML_right }, { "start", XML_start }, + { "end", XML_end }, { "top", XML_top }, { "bottom", XML_bottom }, + { nullptr, 0 } }; + + if (!rTableCellMar.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, nType); + for (const auto& rProp : rTableCellMar) + { + if (sal_Int32 nToken = DocxStringGetToken(aTableCellMarTokens, rProp.Name)) + { + comphelper::SequenceAsHashMap aMap( + rProp.Value.get<uno::Sequence<beans::PropertyValue>>()); + m_pSerializer->singleElementNS( + XML_w, nToken, FSNS(XML_w, XML_w), OString::number(aMap["w"].get<sal_Int32>()), + FSNS(XML_w, XML_type), aMap["type"].get<OUString>().toUtf8()); + } + } + m_pSerializer->endElementNS(XML_w, nType); +} + +void DocxTableStyleExport::Impl::tableStyleTcBorder( + sal_Int32 nToken, const uno::Sequence<beans::PropertyValue>& rTcBorder) +{ + static DocxStringTokenMap const aTcBorderTokens[] = { { "val", XML_val }, + { "sz", XML_sz }, + { "color", XML_color }, + { "space", XML_space }, + { "themeColor", XML_themeColor }, + { "themeTint", XML_themeTint }, + { nullptr, 0 } }; + + if (!rTcBorder.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rTcBorder) + if (sal_Int32 nAttrToken = DocxStringGetToken(aTcBorderTokens, rProp.Name)) + pAttributeList->add(FSNS(XML_w, nAttrToken), rProp.Value.get<OUString>().toUtf8()); + + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, nToken, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleTcBorders( + const uno::Sequence<beans::PropertyValue>& rTcBorders, sal_Int32 nToken) +{ + static DocxStringTokenMap const aTcBordersTokens[] = { { "left", XML_left }, + { "right", XML_right }, + { "start", XML_start }, + { "end", XML_end }, + { "top", XML_top }, + { "bottom", XML_bottom }, + { "insideH", XML_insideH }, + { "insideV", XML_insideV }, + { "tl2br", XML_tl2br }, + { "tr2bl", XML_tr2bl }, + { nullptr, 0 } }; + + if (!rTcBorders.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, nToken); + for (const auto& rTcBorder : rTcBorders) + if (sal_Int32 nSubToken = DocxStringGetToken(aTcBordersTokens, rTcBorder.Name)) + tableStyleTcBorder(nSubToken, + rTcBorder.Value.get<uno::Sequence<beans::PropertyValue>>()); + m_pSerializer->endElementNS(XML_w, nToken); +} + +void DocxTableStyleExport::Impl::tableStyleShd(const uno::Sequence<beans::PropertyValue>& rShd) +{ + if (!rShd.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rShd) + { + if (rProp.Name == "val") + pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "color") + pAttributeList->add(FSNS(XML_w, XML_color), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "fill") + pAttributeList->add(FSNS(XML_w, XML_fill), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeFill") + pAttributeList->add(FSNS(XML_w, XML_themeFill), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeFillShade") + pAttributeList->add(FSNS(XML_w, XML_themeFillShade), + rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeFillTint") + pAttributeList->add(FSNS(XML_w, XML_themeFillTint), + rProp.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_shd, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleRColor(const uno::Sequence<beans::PropertyValue>& rColor) +{ + if (!rColor.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rColor) + { + if (rProp.Name == "val") + pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeColor") + pAttributeList->add(FSNS(XML_w, XML_themeColor), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeTint") + pAttributeList->add(FSNS(XML_w, XML_themeTint), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "themeShade") + pAttributeList->add(FSNS(XML_w, XML_themeShade), rProp.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_color, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleRLang(const uno::Sequence<beans::PropertyValue>& rLang) +{ + if (!rLang.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rLang) + { + if (rProp.Name == "eastAsia") + pAttributeList->add(FSNS(XML_w, XML_eastAsia), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "val") + pAttributeList->add(FSNS(XML_w, XML_val), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "bidi") + pAttributeList->add(FSNS(XML_w, XML_bidi), rProp.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_lang, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleRRFonts( + const uno::Sequence<beans::PropertyValue>& rRFonts) +{ + if (!rRFonts.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rRFont : rRFonts) + { + if (rRFont.Name == "eastAsiaTheme") + pAttributeList->add(FSNS(XML_w, XML_eastAsiaTheme), + rRFont.Value.get<OUString>().toUtf8()); + else if (rRFont.Name == "asciiTheme") + pAttributeList->add(FSNS(XML_w, XML_asciiTheme), rRFont.Value.get<OUString>().toUtf8()); + else if (rRFont.Name == "cstheme") + pAttributeList->add(FSNS(XML_w, XML_cstheme), rRFont.Value.get<OUString>().toUtf8()); + else if (rRFont.Name == "hAnsiTheme") + pAttributeList->add(FSNS(XML_w, XML_hAnsiTheme), rRFont.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_rFonts, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStylePSpacing( + const uno::Sequence<beans::PropertyValue>& rSpacing) +{ + if (!rSpacing.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rSpacing) + { + if (rProp.Name == "after") + pAttributeList->add(FSNS(XML_w, XML_after), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "before") + pAttributeList->add(FSNS(XML_w, XML_before), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "line") + pAttributeList->add(FSNS(XML_w, XML_line), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "lineRule") + pAttributeList->add(FSNS(XML_w, XML_lineRule), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "beforeLines") + pAttributeList->add(FSNS(XML_w, XML_beforeLines), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "ParaTopMarginBeforeAutoSpacing") + // Auto spacing will be available in grab bag only if it was set to true + pAttributeList->add(FSNS(XML_w, XML_beforeAutospacing), "1"); + else if (rProp.Name == "afterLines") + pAttributeList->add(FSNS(XML_w, XML_afterLines), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "ParaBottomMarginAfterAutoSpacing") + // Auto spacing will be available in grab bag only if it was set to true + pAttributeList->add(FSNS(XML_w, XML_afterAutospacing), "1"); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_spacing, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStylePInd(const uno::Sequence<beans::PropertyValue>& rInd) +{ + if (!rInd.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rInd) + { + if (rProp.Name == "rightChars") + pAttributeList->add(FSNS(XML_w, XML_rightChars), rProp.Value.get<OUString>().toUtf8()); + else if (rProp.Name == "right") + pAttributeList->add(FSNS(XML_w, XML_right), rProp.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_ind, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleTableInd( + const uno::Sequence<beans::PropertyValue>& rTableInd) +{ + if (!rTableInd.hasElements()) + return; + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + for (const auto& rProp : rTableInd) + { + if (rProp.Name == "w") + pAttributeList->add(FSNS(XML_w, XML_w), OString::number(rProp.Value.get<sal_Int32>())); + else if (rProp.Name == "type") + pAttributeList->add(FSNS(XML_w, XML_type), rProp.Value.get<OUString>().toUtf8()); + } + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, XML_tblInd, xAttributeList); +} + +void DocxTableStyleExport::Impl::handleBoolean(const OUString& aValue, sal_Int32 nToken) +{ + if (aValue.isEmpty()) + return; + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + if (aValue != "1") + pAttributeList->add(FSNS(XML_w, XML_val), aValue.toUtf8()); + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->singleElementNS(XML_w, nToken, xAttributeList); +} + +void DocxTableStyleExport::Impl::tableStyleRPr(const uno::Sequence<beans::PropertyValue>& rRPr) +{ + if (!rRPr.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, XML_rPr); + + uno::Sequence<beans::PropertyValue> aRFonts; + uno::Sequence<beans::PropertyValue> aLang; + uno::Sequence<beans::PropertyValue> aColor; + uno::Sequence<beans::PropertyValue> aSpacingSequence; + bool bSequenceFlag = false; + OUString aB; + OUString aBCs; + OUString aI; + OUString aSz; + OUString aSzCs; + OUString aCaps; + OUString aSmallCaps; + OUString aSpacing; + for (const auto& rProp : rRPr) + { + if (rProp.Name == "rFonts") + aRFonts = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "lang") + aLang = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "b") + aB = rProp.Value.get<OUString>(); + else if (rProp.Name == "bCs") + aBCs = rProp.Value.get<OUString>(); + else if (rProp.Name == "i") + aI = rProp.Value.get<OUString>(); + else if (rProp.Name == "color") + aColor = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "sz") + aSz = rProp.Value.get<OUString>(); + else if (rProp.Name == "szCs") + aSzCs = rProp.Value.get<OUString>(); + else if (rProp.Name == "caps") + aCaps = rProp.Value.get<OUString>(); + else if (rProp.Name == "smallCaps") + aSmallCaps = rProp.Value.get<OUString>(); + else if (rProp.Name == "spacing") + { + if (rProp.Value.has<OUString>()) + { + aSpacing = rProp.Value.get<OUString>(); + } + else + { + aSpacingSequence = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + bSequenceFlag = true; // set the uno::Sequence flag. + } + } + } + tableStyleRRFonts(aRFonts); + tableStyleRLang(aLang); + handleBoolean(aB, XML_b); + handleBoolean(aBCs, XML_bCs); + handleBoolean(aI, XML_i); + handleBoolean(aCaps, XML_caps); + handleBoolean(aSmallCaps, XML_smallCaps); + tableStyleRColor(aColor); + if (bSequenceFlag) + { + m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), + aSpacingSequence[0].Value.get<OUString>().toUtf8()); + } + if (!aSpacing.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aSpacing.toUtf8()); + if (!aSz.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), aSz.toUtf8()); + if (!aSzCs.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), aSzCs.toUtf8()); + + m_pSerializer->endElementNS(XML_w, XML_rPr); +} + +void DocxTableStyleExport::Impl::tableStylePPr(const uno::Sequence<beans::PropertyValue>& rPPr) +{ + if (!rPPr.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, XML_pPr); + + uno::Sequence<beans::PropertyValue> aSpacing; + uno::Sequence<beans::PropertyValue> aInd; + bool bWordWrap = false; + OUString aJc; + OUString aSnapToGrid; + for (const auto& rProp : rPPr) + { + if (rProp.Name == "spacing") + aSpacing = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "ind") + aInd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "wordWrap") + bWordWrap = true; + else if (rProp.Name == "jc") + aJc = rProp.Value.get<OUString>(); + else if (rProp.Name == "snapToGrid") + aSnapToGrid = rProp.Value.get<OUString>(); + } + if (bWordWrap) + m_pSerializer->singleElementNS(XML_w, XML_wordWrap); + tableStylePInd(aInd); + handleBoolean(aSnapToGrid, XML_snapToGrid); + tableStylePSpacing(aSpacing); + if (!aJc.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), aJc.toUtf8()); + + m_pSerializer->endElementNS(XML_w, XML_pPr); +} + +void DocxTableStyleExport::Impl::tableStyleTablePr( + const uno::Sequence<beans::PropertyValue>& rTablePr) +{ + if (!rTablePr.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, XML_tblPr); + + uno::Sequence<beans::PropertyValue> aTableInd; + uno::Sequence<beans::PropertyValue> aTableBorders; + uno::Sequence<beans::PropertyValue> aTableCellMar; + std::optional<sal_Int32> oTableStyleRowBandSize; + std::optional<sal_Int32> oTableStyleColBandSize; + for (const auto& rProp : rTablePr) + { + if (rProp.Name == "tblStyleRowBandSize") + oTableStyleRowBandSize = rProp.Value.get<sal_Int32>(); + else if (rProp.Name == "tblStyleColBandSize") + oTableStyleColBandSize = rProp.Value.get<sal_Int32>(); + else if (rProp.Name == "tblInd") + aTableInd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tblBorders") + aTableBorders = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tblCellMar") + aTableCellMar = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + } + if (oTableStyleRowBandSize) + m_pSerializer->singleElementNS(XML_w, XML_tblStyleRowBandSize, FSNS(XML_w, XML_val), + OString::number(*oTableStyleRowBandSize)); + if (oTableStyleColBandSize) + m_pSerializer->singleElementNS(XML_w, XML_tblStyleColBandSize, FSNS(XML_w, XML_val), + OString::number(*oTableStyleColBandSize)); + tableStyleTableInd(aTableInd); + tableStyleTcBorders(aTableBorders, XML_tblBorders); + tableStyleTableCellMar(aTableCellMar); + + m_pSerializer->endElementNS(XML_w, XML_tblPr); +} + +void DocxTableStyleExport::Impl::tableStyleTrPr(const uno::Sequence<beans::PropertyValue>& rTrPr) +{ + if (!rTrPr.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, XML_trPr); + + for (const auto& rProp : rTrPr) + { + if (rProp.Name == "tblHeader") + m_pSerializer->singleElementNS(XML_w, XML_tblHeader); + } + + m_pSerializer->endElementNS(XML_w, XML_trPr); +} + +void DocxTableStyleExport::Impl::tableStyleTcPr(const uno::Sequence<beans::PropertyValue>& rTcPr) +{ + if (!rTcPr.hasElements()) + return; + + m_pSerializer->startElementNS(XML_w, XML_tcPr); + + uno::Sequence<beans::PropertyValue> aShd; + uno::Sequence<beans::PropertyValue> aTcBorders; + uno::Sequence<beans::PropertyValue> aTcMar; + OUString aVAlign; + for (const auto& rProp : rTcPr) + { + if (rProp.Name == "shd") + aShd = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tcBorders") + aTcBorders = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tcMar") + aTcMar = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "vAlign") + aVAlign = rProp.Value.get<OUString>(); + } + tableStyleTcBorders(aTcBorders); + tableStyleTableCellMar(aTcMar, XML_tcMar); + tableStyleShd(aShd); + if (!aVAlign.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), aVAlign.toUtf8()); + + m_pSerializer->endElementNS(XML_w, XML_tcPr); +} + +void DocxTableStyleExport::Impl::tableStyleTableStylePr( + const uno::Sequence<beans::PropertyValue>& rTableStylePr) +{ + if (!rTableStylePr.hasElements()) + return; + + OUString aType; + uno::Sequence<beans::PropertyValue> aPPr; + uno::Sequence<beans::PropertyValue> aRPr; + uno::Sequence<beans::PropertyValue> aTablePr; + uno::Sequence<beans::PropertyValue> aTrPr; + uno::Sequence<beans::PropertyValue> aTcPr; + for (const auto& rProp : rTableStylePr) + { + if (rProp.Name == "type") + aType = rProp.Value.get<OUString>(); + else if (rProp.Name == "pPr") + aPPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "rPr") + aRPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tblPr") + aTablePr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "trPr") + aTrPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tcPr") + aTcPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + } + + m_pSerializer->startElementNS(XML_w, XML_tblStylePr, FSNS(XML_w, XML_type), aType.toUtf8()); + + tableStylePPr(aPPr); + tableStyleRPr(aRPr); + if (aTablePr.hasElements()) + tableStyleTablePr(aTablePr); + else + { + // Even if we have an empty container, write it out, as Word does. + m_pSerializer->singleElementNS(XML_w, XML_tblPr); + } + tableStyleTrPr(aTrPr); + tableStyleTcPr(aTcPr); + + m_pSerializer->endElementNS(XML_w, XML_tblStylePr); +} + +void DocxTableStyleExport::Impl::TableStyle(const uno::Sequence<beans::PropertyValue>& rStyle) +{ + bool bDefault = false; + bool bCustomStyle = false; + bool bQFormat = false; + bool bSemiHidden = false; + bool bUnhideWhenUsed = false; + OUString aStyleId; + OUString aName; + OUString aBasedOn; + OUString aRsid; + OUString aUiPriority; + uno::Sequence<beans::PropertyValue> aPPr; + uno::Sequence<beans::PropertyValue> aRPr; + uno::Sequence<beans::PropertyValue> aTablePr; + uno::Sequence<beans::PropertyValue> aTcPr; + std::vector<uno::Sequence<beans::PropertyValue>> aTableStylePrs; + for (const auto& rProp : rStyle) + { + if (rProp.Name == "default") + bDefault = rProp.Value.get<bool>(); + else if (rProp.Name == "customStyle") + bCustomStyle = rProp.Value.get<bool>(); + else if (rProp.Name == "styleId") + aStyleId = rProp.Value.get<OUString>(); + else if (rProp.Name == "name") + aName = rProp.Value.get<OUString>(); + else if (rProp.Name == "basedOn") + aBasedOn = rProp.Value.get<OUString>(); + else if (rProp.Name == "uiPriority") + aUiPriority = rProp.Value.get<OUString>(); + else if (rProp.Name == "qFormat") + bQFormat = true; + else if (rProp.Name == "semiHidden") + bSemiHidden = true; + else if (rProp.Name == "unhideWhenUsed") + bUnhideWhenUsed = true; + else if (rProp.Name == "rsid") + aRsid = rProp.Value.get<OUString>(); + else if (rProp.Name == "pPr") + aPPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "rPr") + aRPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tblPr") + aTablePr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tcPr") + aTcPr = rProp.Value.get<uno::Sequence<beans::PropertyValue>>(); + else if (rProp.Name == "tblStylePr") + aTableStylePrs.push_back(rProp.Value.get<uno::Sequence<beans::PropertyValue>>()); + } + + sax_fastparser::FastAttributeList* pAttributeList + = sax_fastparser::FastSerializerHelper::createAttrList(); + pAttributeList->add(FSNS(XML_w, XML_type), "table"); + if (bDefault) + pAttributeList->add(FSNS(XML_w, XML_default), "1"); + if (bCustomStyle) + pAttributeList->add(FSNS(XML_w, XML_customStyle), "1"); + if (!aStyleId.isEmpty()) + pAttributeList->add(FSNS(XML_w, XML_styleId), aStyleId.toUtf8()); + sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList); + m_pSerializer->startElementNS(XML_w, XML_style, xAttributeList); + + m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), aName.toUtf8()); + if (!aBasedOn.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_basedOn, FSNS(XML_w, XML_val), aBasedOn.toUtf8()); + if (!aUiPriority.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_uiPriority, FSNS(XML_w, XML_val), + aUiPriority.toUtf8()); + if (bSemiHidden) + m_pSerializer->singleElementNS(XML_w, XML_semiHidden); + if (bUnhideWhenUsed) + m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed); + if (bQFormat) + m_pSerializer->singleElementNS(XML_w, XML_qFormat); + if (!aRsid.isEmpty()) + m_pSerializer->singleElementNS(XML_w, XML_rsid, FSNS(XML_w, XML_val), aRsid.toUtf8()); + + tableStylePPr(aPPr); + tableStyleRPr(aRPr); + tableStyleTablePr(aTablePr); + tableStyleTcPr(aTcPr); + for (const uno::Sequence<beans::PropertyValue>& i : std::as_const(aTableStylePrs)) + tableStyleTableStylePr(i); + + m_pSerializer->endElementNS(XML_w, XML_style); +} + +void DocxTableStyleExport::SetSerializer(const sax_fastparser::FSHelperPtr& pSerializer) +{ + m_pImpl->setSerializer(pSerializer); +} + +DocxTableStyleExport::DocxTableStyleExport(SwDoc* pDoc, + const sax_fastparser::FSHelperPtr& pSerializer) + : m_pImpl(std::make_unique<Impl>(pDoc)) +{ + m_pImpl->setSerializer(pSerializer); +} + +DocxTableStyleExport::~DocxTableStyleExport() = default; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/docxtablestyleexport.hxx b/sw/source/filter/ww8/docxtablestyleexport.hxx new file mode 100644 index 000000000..8303aafdc --- /dev/null +++ b/sw/source/filter/ww8/docxtablestyleexport.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX + +#include <memory> + +#include <sax/fshelper.hxx> + +namespace com +{ +namespace sun +{ +namespace star +{ +namespace beans +{ +struct PropertyValue; +} +} +} +} +class SwDoc; + +/// Handles DOCX export of table styles, based on InteropGrabBag. +class DocxTableStyleExport +{ + struct Impl; + std::unique_ptr<Impl> m_pImpl; + +public: + void TableStyles(sal_Int32 nCountStylesToWrite); + + /// Writes <w:cnfStyle .../> based on grab-bagged para, cell or row properties. + void CnfStyle(const css::uno::Sequence<css::beans::PropertyValue>& rAttributeList); + + void SetSerializer(const sax_fastparser::FSHelperPtr& pSerializer); + DocxTableStyleExport(SwDoc* pDoc, const sax_fastparser::FSHelperPtr& pSerializer); + ~DocxTableStyleExport(); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_DOCXTABLESTYLEEXPORT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/escher.hxx b/sw/source/filter/ww8/escher.hxx new file mode 100644 index 000000000..6bdb7d03b --- /dev/null +++ b/sw/source/filter/ww8/escher.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_ESCHER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_ESCHER_HXX + +#include <filter/msfilter/escherex.hxx> +#include <svx/svdtrans.hxx> +#include "wrtww8.hxx" + +const sal_uInt32 nInlineHack = 0x00010001; +class SwFrameFormat; +// #i30669# +class SwFormatHoriOrient; +class SwFormatVertOrient; + +class WinwordAnchoring : public EscherExClientRecord_Base +{ +public: + void WriteData(EscherEx& rEx) const override; + void SetAnchoring(const SwFrameFormat& rFormat); + + /** method to perform conversion of positioning attributes with the help + of corresponding layout information + + #i30669# + Because most of the Writer object positions doesn't correspond to the + object positions in WW8, this method converts the positioning + attributes. For this conversion the corresponding layout information + is needed. If no layout information exists - e.g. no layout exists - no + conversion is performed. + No conversion is performed for as-character anchored objects. Whose + object positions are already treated special in method <WriteData(..)>. + Usage of method: Used by method <SetAnchoring(..)>, nothing else + + @param _iorHoriOri + input/output parameter - containing the current horizontal position + attributes, which are converted by this method. + + @param _iorVertOri + input/output parameter - containing the current vertical position + attributes, which are converted by this method. + + @param _rFrameFormat + input parameter - frame format of the anchored object + + @return boolean, indicating, if a conversion has been performed. + */ + static bool ConvertPosition( SwFormatHoriOrient& _iorHoriOri, + SwFormatVertOrient& _iorVertOri, + const SwFrameFormat& _rFrameFormat ); + +private: + bool mbInline; + sal_uInt32 mnXAlign; + sal_uInt32 mnYAlign; + sal_uInt32 mnXRelTo; + sal_uInt32 mnYRelTo; + +}; + +class SwEscherExGlobal : public EscherExGlobal +{ +public: + explicit SwEscherExGlobal(); + virtual ~SwEscherExGlobal() override; + +private: + /** Override to create a new memory stream for picture data. */ + virtual SvStream* ImplQueryPictureStream() override; + +private: + std::shared_ptr< SvStream > mxPicStrm; +}; + +class SwBasicEscherEx : public EscherEx +{ +private: + void Init(); +protected: + WW8Export& rWrt; + SvStream* pEscherStrm; + long mnEmuMul, mnEmuDiv; + + virtual sal_Int32 WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType, + EscherPropertyContainer& rPropOpt); + void WriteBrushAttr(const SvxBrushItem &rBrush, + EscherPropertyContainer& rPropOpt); + void WriteOLEPicture(EscherPropertyContainer &rPropOpt, + ShapeFlag nShapeFlags, const Graphic &rGraphic, const SdrObject &rObj, + sal_uInt32 nShapeId, const css::awt::Rectangle* pVisArea ); + static void WriteGrfAttr(const SwNoTextNode& rNd, const SwFrameFormat& rFormat, EscherPropertyContainer& rPropOpt); + + sal_Int32 DrawModelToEmu(sal_Int32 nVal) const + { return BigMulDiv(nVal, mnEmuMul, mnEmuDiv); } + + static sal_Int32 ToFract16(sal_Int32 nVal, sal_uInt32 nMax); + + virtual void SetPicId(const SdrObject &, sal_uInt32, EscherPropertyContainer &); + SdrLayerID GetInvisibleHellId() const; + +public: + SwBasicEscherEx(SvStream* pStrm, WW8Export& rWrt); + sal_Int32 WriteGrfFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId); + //For i120928,to export graphic of bullet + void WriteGrfBullet(const Graphic&); + sal_Int32 WriteOLEFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId); + void WriteEmptyFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId); + virtual void WriteFrameExtraData(const SwFrameFormat&); + virtual void WritePictures(); + virtual ~SwBasicEscherEx() override; + //i120927,this function is added to export hyperlink info,such as graphic/frame/OLE + bool IsRelUrl() const; + OUString GetBasePath() const; + OUString BuildFileName(sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl); + void WriteHyperlinkWithinFly( SvMemoryStream& rStrm, const SwFormatURL* pINetFormatArg); + void PreWriteHyperlinkWithinFly(const SwFrameFormat& rFormat,EscherPropertyContainer& rPropOpt); + +private: + SwBasicEscherEx(const SwBasicEscherEx&) = delete; + SwBasicEscherEx& operator=(const SwBasicEscherEx&) = delete; +}; + +class SwEscherEx : public SwBasicEscherEx +{ +private: + std::vector<sal_uLong> aFollowShpIds; + EscherExHostAppData aHostData; + WinwordAnchoring aWinwordAnchoring; + WW8_WrPlcTextBoxes *pTextBxs; + + sal_uInt32 GetFlyShapeId(const SwFrameFormat& rFormat, + unsigned int nHdFtIndex, DrawObjPointerVector &rPVec); + void MakeZOrderArrAndFollowIds(std::vector<DrawObj>& rSrcArr, + DrawObjPointerVector& rDstArr); + + sal_Int32 WriteFlyFrame(const DrawObj &rObj, sal_uInt32 &rShapeId, + DrawObjPointerVector &rPVec); + sal_Int32 WriteTextFlyFrame(const DrawObj &rObj, sal_uInt32 nShapeId, + sal_uInt32 nTextBox, DrawObjPointerVector &rPVec); + void WriteOCXControl(const SwFrameFormat& rFormat,sal_uInt32 nShapeId); + virtual sal_Int32 WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType, + EscherPropertyContainer& rPropOpt) override; + + virtual sal_uInt32 QueryTextID( + const css::uno::Reference< css::drawing::XShape > &, sal_uInt32) override; + virtual void SetPicId(const SdrObject &rSdrObj, sal_uInt32 nShapeId, + EscherPropertyContainer &rPropOpt) override; +public: + SwEscherEx( SvStream* pStrm, WW8Export& rWW8Wrt ); + virtual ~SwEscherEx() override; + void FinishEscher(); + virtual void WritePictures() override; + + virtual void WriteFrameExtraData(const SwFrameFormat& rFormat) override; + + EscherExHostAppData* StartShape(const css::uno::Reference< css::drawing::XShape > &, const tools::Rectangle*) override {return &aHostData;} +private: + SwEscherEx(const SwEscherEx&) = delete; + SwEscherEx &operator=(const SwEscherEx&) = delete; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/fields.cxx b/sw/source/filter/ww8/fields.cxx new file mode 100644 index 000000000..68a58d856 --- /dev/null +++ b/sw/source/filter/ww8/fields.cxx @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "fields.hxx" +#include <osl/diagnose.h> +#include <stddef.h> + +namespace ww +{ + const char *GetEnglishFieldName(eField eIndex) throw() + { + //0 Signifies the field names I can't find. + // #i43956# - field <eFOOTREF> = 5 should be mapped to "REF" + static const char *aFieldNames[] = + { + /* 0*/ nullptr, + /* 1*/ nullptr, + /* 2*/ nullptr, + /* 3*/ "REF", + /* 4*/ "XE", + /* 5*/ "REF", + /* 6*/ "SET", + /* 7*/ "IF", + /* 8*/ "INDEX", + /* 9*/ "TC", + /*10*/ "STYLEREF", + /*11*/ "RD", + /*12*/ "SEQ", + /*13*/ "TOC", + /*14*/ "INFO", + /*15*/ "TITLE", + /*16*/ "SUBJECT", + /*17*/ "AUTHOR", + /*18*/ "KEYWORDS", + /*19*/ "COMMENTS", + /*20*/ "LASTSAVEDBY", + /*21*/ "CREATEDATE", + /*22*/ "SAVEDATE", + /*23*/ "PRINTDATE", + /*24*/ "REVNUM", + /*25*/ "EDITTIME", + /*26*/ "NUMPAGES", + /*27*/ "NUMWORDS", + /*28*/ "NUMCHARS", + /*29*/ "FILENAME", + /*30*/ "TEMPLATE", + /*31*/ "DATE", + /*32*/ "TIME", + /*33*/ "PAGE", + /*34*/ "=", + /*35*/ "QUOTE", + /*36*/ nullptr, + /*37*/ "PAGEREF", + /*38*/ "ASK", + /*39*/ "FILLIN", + /*40*/ nullptr, + /*41*/ "NEXT", + /*42*/ "NEXTIF", + /*43*/ "SKIPIF", + /*44*/ "MERGEREC", + /*45*/ nullptr, + /*46*/ nullptr, + /*47*/ nullptr, + /*48*/ "PRINT", + /*49*/ "EQ", + /*50*/ "GOTOBUTTON", + /*51*/ "MACROBUTTON", + /*52*/ "AUTONUMOUT", + /*53*/ "AUTONUMLGL", + /*54*/ "AUTONUM", + /*55*/ nullptr, + /*56*/ "LINK", + /*57*/ "SYMBOL", + /*58*/ "EMBED", + /*59*/ "MERGEFIELD", + /*60*/ "USERNAME", + /*61*/ "USERINITIALS", + /*62*/ "USERADDRESS", + /*63*/ "BARCODE", + /*64*/ "DOCVARIABLE", + /*65*/ "SECTION", + /*66*/ "SECTIONPAGES", + /*67*/ "INCLUDEPICTURE", + /*68*/ "INCLUDETEXT", + /*69*/ "FILESIZE", + /*70*/ "FORMTEXT", + /*71*/ "FORMCHECKBOX", + /*72*/ "NOTEREF", + /*73*/ "TOA", + /*74*/ "TA", + /*75*/ "MERGESEQ", + /*76*/ nullptr, + /*77*/ "PRIVATE", + /*78*/ "DATABASE", + /*79*/ "AUTOTEXT", + /*80*/ "COMPARE", + /*81*/ nullptr, + /*82*/ nullptr, + /*83*/ "FORMDROPDOWN", + /*84*/ "ADVANCE", + /*85*/ "DOCPROPERTY", + /*86*/ nullptr, + /*87*/ "CONTROL", + /*88*/ "HYPERLINK", + /*89*/ "AUTOTEXTLIST", + /*90*/ "LISTNUM", + /*91*/ nullptr, + /*92*/ "BIDIOUTLINE", + /*93*/ "ADDRESSBLOCK", + /*94*/ "GREETINGLINE", + /*95*/ "SHAPE", + /*96*/ "BIBLIOGRAPHY", + /*97*/ "CITATION" + }; + + size_t nIndex = static_cast<size_t>(eIndex); + if (nIndex >= SAL_N_ELEMENTS(aFieldNames)) + eIndex = eNONE; + OSL_ENSURE(eIndex != eNONE, "Unknown WinWord Field"); + return aFieldNames[eIndex]; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/fields.hxx b/sw/source/filter/ww8/fields.hxx new file mode 100644 index 000000000..9f3b0aafa --- /dev/null +++ b/sw/source/filter/ww8/fields.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 . + */ + +/// @HTML +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_FIELDS_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_FIELDS_HXX + +#include <filter/msfilter/ww8fields.hxx> + +namespace ww +{ + /** Find the English Field Name from a winword index + + See OpenOffice.org issue 12831 + (https://bz.apache.org/ooo/show_bug.cgi?id=12831) and MS + Knowledge Base article 268717 + (http://support.microsoft.com/default.aspx?scid=kb;en-us;268717) for + details of why to use english field names and not localized ones since + Word 2000. + + @param + nIndex the index to search for + + @return 0 if not found, otherwise the fieldname as a C style ASCII + string + */ + const char *GetEnglishFieldName(eField eIndex) throw(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/needed_cast.hxx b/sw/source/filter/ww8/needed_cast.hxx new file mode 100644 index 000000000..1fe7ca091 --- /dev/null +++ b/sw/source/filter/ww8/needed_cast.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_FILTER_WW8_NEEDED_CAST_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_NEEDED_CAST_HXX + +#include <osl/diagnose.h> + +namespace ww +{ + template<typename Ret, typename Param> Ret checking_cast(Param in, Ret) + { + return static_cast<Ret>(in); + } + + template<typename Ret> Ret checking_cast(Ret in, Ret) + { + OSL_ENSURE( false, "UnnecessaryCast" ); + return in; + } + + /* + needed_cast is the same as static_cast except that there will be a compile + time assert when NDEBUG is not defined and the in and out types are the + same. i.e. needed_cast catches unnecessary casts + */ + template<typename Ret, typename Param> Ret needed_cast(Param in) + { + /* + Massage a single argument and a ret value into two arguments to allow + a determination if the dest type is the same as the source type + */ + return checking_cast(in, Ret()); + } +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx new file mode 100644 index 000000000..10d38a569 --- /dev/null +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -0,0 +1,4340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "rtfattributeoutput.hxx" +#include <memory> +#include <cstring> +#include "rtfsdrexport.hxx" +#include "writerwordglue.hxx" +#include "ww8par.hxx" +#include <fmtcntnt.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <sot/exchange.hxx> +#include <svtools/rtfkeywd.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/autokernitem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/twolinesitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/paravertalignitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/opaqitem.hxx> +#include <svx/svdouno.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <sfx2/sfxbasemodel.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflgrit.hxx> +#include <docufld.hxx> +#include <fmtclds.hxx> +#include <fmtrowsplt.hxx> +#include <fmtline.hxx> +#include <fmtanchr.hxx> +#include <ftninfo.hxx> +#include <htmltbl.hxx> +#include <ndgrf.hxx> +#include <pagedesc.hxx> +#include <swmodule.hxx> +#include <txtftn.hxx> +#include <txtinet.hxx> +#include <grfatr.hxx> +#include <ndole.hxx> +#include <lineinfo.hxx> +#include <redline.hxx> +#include <rtf.hxx> +#include <IDocumentSettingAccess.hxx> +#include <vcl/cvtgrf.hxx> +#include <oox/mathml/export.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <svl/grabbagitem.hxx> +#include <frmatr.hxx> +#include <swtable.hxx> +#include "rtfexport.hxx" + +using namespace ::com::sun::star; +using namespace sw::util; + +static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine, + const char* pStr) +{ + OStringBuffer aRet; + if (pLine && !pLine->isEmpty()) + { + aRet.append(pStr); + // single line + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + { + if (DEF_LINE_WIDTH_0 == pLine->GetWidth()) + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRHAIR); + else + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS); + } + break; + case SvxBorderLineStyle::DOTTED: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDOT); + break; + case SvxBorderLineStyle::DASHED: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASH); + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::DOUBLE_THIN: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDB); + break; + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHSG); + break; + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHMG); + break; + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHLG); + break; + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNSG); + break; + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNMG); + break; + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNLG); + break; + case SvxBorderLineStyle::EMBOSSED: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDREMBOSS); + break; + case SvxBorderLineStyle::ENGRAVED: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRENGRAVE); + break; + case SvxBorderLineStyle::OUTSET: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDROUTSET); + break; + case SvxBorderLineStyle::INSET: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRINSET); + break; + case SvxBorderLineStyle::FINE_DASHED: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHSM); + break; + case SvxBorderLineStyle::DASH_DOT: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHD); + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHDD); + break; + case SvxBorderLineStyle::NONE: + default: + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRNONE); + break; + } + + double const fConverted( + ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth())); + if (255 >= pLine->GetWidth()) // That value comes from RTF specs + { + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRW).append(static_cast<sal_Int32>(fConverted)); + } + else + { + // use \brdrth to double the value range... + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTH OOO_STRING_SVTOOLS_RTF_BRDRW); + aRet.append(static_cast<sal_Int32>(fConverted) / 2); + } + + aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF); + aRet.append(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor()))); + } + return aRet.makeStringAndClear(); +} + +static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine, + const char* pStr, sal_uInt16 nDist, + SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE) +{ + OStringBuffer aRet; + aRet.append(OutTBLBorderLine(rExport, pLine, pStr)); + aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP); + aRet.append(static_cast<sal_Int32>(nDist)); + if (eShadowLocation == SvxShadowLocation::BottomRight) + aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH); + return aRet.makeStringAndClear(); +} + +void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript) +{ + m_bIsRTL = bIsRTL; + m_nScript = nScript; + m_bControlLtrRtl = true; +} + +void RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo) +{ + if (m_bIsBeforeFirstParagraph && m_rExport.m_nTextTyp != TXT_HDFT) + m_bIsBeforeFirstParagraph = false; + + // Output table/table row/table cell starts if needed + if (pTextNodeInfo) + { + sal_uInt32 nRow = pTextNodeInfo->getRow(); + sal_uInt32 nCell = pTextNodeInfo->getCell(); + + // New cell/row? + if (m_nTableDepth > 0 && !m_bTableCellOpen) + { + ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( + pTextNodeInfo->getInnerForDepth(m_nTableDepth)); + OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found"); + // Make sure we always start a row between ending one and starting a cell. + // In case of subtables, we may not get the first cell. + if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded)) + { + StartTableRow(pDeepInner); + } + + StartTableCell(); + } + + // Again, if depth was incremented, start a new table even if we skipped the first cell. + if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth())) + { + // Do we have to start the table? + // [If we are at the right depth already, it means that we + // continue the table cell] + sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth(); + + if (nCurrentDepth > m_nTableDepth) + { + // Start all the tables that begin here + for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth(); + ++nDepth) + { + ww8::WW8TableNodeInfoInner::Pointer_t pInner( + pTextNodeInfo->getInnerForDepth(nDepth)); + + m_bLastTable = (nDepth == pTextNodeInfo->getDepth()); + StartTable(); + StartTableRow(pInner); + StartTableCell(); + } + + m_nTableDepth = nCurrentDepth; + } + } + } + + OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty"); +} + +void RtfAttributeOutput::EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) +{ + bool bLastPara = false; + if (m_rExport.m_nTextTyp == TXT_FTN || m_rExport.m_nTextTyp == TXT_EDN + || m_rExport.m_pDoc->IsClipBoard()) + { + // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard. + bLastPara + = m_rExport.GetCurrentNodeIndex() + && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->nNode.GetIndex(); + } + + FinishTableRowCell(pTextNodeInfoInner); + + RtfStringBuffer aParagraph; + + aParagraph.appendAndClear(m_aRun); + aParagraph->append(m_aAfterRuns.makeStringAndClear()); + if (m_bTableAfterCell) + m_bTableAfterCell = false; + else + { + aParagraph->append(SAL_NEWLINE_STRING); + // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph. + if (!bLastPara) + { + aParagraph->append(OOO_STRING_SVTOOLS_RTF_PAR); + aParagraph->append(' '); + } + } + if (m_nColBreakNeeded) + { + aParagraph->append(OOO_STRING_SVTOOLS_RTF_COLUMN); + m_nColBreakNeeded = false; + } + + if (!m_bBufferSectionHeaders) + aParagraph.makeStringAndClear(this); + else + m_aSectionHeaders.append(aParagraph.makeStringAndClear()); +} + +void RtfAttributeOutput::EmptyParagraph() +{ + m_rExport.Strm() + .WriteCharPtr(SAL_NEWLINE_STRING) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAR) + .WriteChar(' '); +} + +void RtfAttributeOutput::SectionBreaks(const SwNode& rNode) +{ + SwNodeIndex aNextIndex(rNode, 1); + if (rNode.IsTextNode()) + { + OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty"); + + // output page/section breaks + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + m_bBufferSectionBreaks = true; + + // output section headers / footers + if (!m_bBufferSectionHeaders) + m_rExport.Strm().WriteOString(m_aSectionHeaders.makeStringAndClear()); + + if (aNextIndex.GetNode().IsTextNode()) + { + const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode()); + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode); + // Save the current page description for now, so later we will be able to access the previous one. + m_pPrevPageDesc = pTextNode->FindPageDesc(); + } + else if (aNextIndex.GetNode().IsTableNode()) + { + const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode()); + const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat(); + m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode); + } + m_bBufferSectionBreaks = false; + } + else if (rNode.IsEndNode()) + { + // End of something: make sure that it's the end of a table. + assert(rNode.StartOfSectionNode()->IsTableNode()); + if (aNextIndex.GetNode().IsTextNode()) + { + // Handle section break between a table and a text node following it. + const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode(); + m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode); + } + } +} + +void RtfAttributeOutput::StartParagraphProperties() +{ + OStringBuffer aPar; + if (!m_rExport.GetRTFFlySyntax()) + { + aPar.append(OOO_STRING_SVTOOLS_RTF_PARD); + aPar.append(OOO_STRING_SVTOOLS_RTF_PLAIN); + aPar.append(' '); + } + if (!m_bBufferSectionHeaders) + m_rExport.Strm().WriteOString(aPar.makeStringAndClear()); + else + m_aSectionHeaders.append(aPar.makeStringAndClear()); +} + +void RtfAttributeOutput::EndParagraphProperties( + const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/, + const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/, + const SwRedlineData* /*pRedlineParagraphMarkerInserted*/) +{ + const OString aProperties = MoveCharacterProperties(true); + m_rExport.Strm().WriteOString(aProperties); +} + +void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, + bool bSingleEmptyRun) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", bSingleEmptyRun: " << bSingleEmptyRun); + + m_bInRun = true; + m_bSingleEmptyRun = bSingleEmptyRun; + if (!m_bSingleEmptyRun) + m_aRun->append('{'); + + // if there is some redlining in the document, output it + Redline(pRedlineData); + + OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty"); +} + +void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, bool /*bLastRun*/) +{ + m_aRun->append(SAL_NEWLINE_STRING); + m_aRun.appendAndClear(m_aRunText); + if (!m_bSingleEmptyRun && m_bInRun) + m_aRun->append('}'); + m_bInRun = false; +} + +void RtfAttributeOutput::StartRunProperties() +{ + OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty"); +} + +void RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/) +{ + const OString aProperties = MoveCharacterProperties(true); + m_aRun->append(aProperties.getStr()); +} + +OString RtfAttributeOutput::MoveCharacterProperties(bool aAutoWriteRtlLtr) +{ + const OString aAssoc = m_aStylesAssoc.makeStringAndClear(); + const OString aNormal = m_aStyles.makeStringAndClear(); + OStringBuffer aBuf; + + if (aAutoWriteRtlLtr && !m_bControlLtrRtl) + { + m_bControlLtrRtl = !aAssoc.isEmpty(); + m_bIsRTL = false; + m_nScript = i18n::ScriptType::LATIN; + } + + if (m_bControlLtrRtl) + { + m_bControlLtrRtl = false; + + /* + You would have thought that + m_rExport.Strm() << (bIsRTL ? OOO_STRING_SVTOOLS_RTF_RTLCH : OOO_STRING_SVTOOLS_RTF_LTRCH); would be sufficient here , + but looks like word needs to see the other directional token to be + satisfied that all is kosher, otherwise it seems in ver 2003 to go and + semi-randomly stick strike through about the place. Perhaps + strikethrough is some ms developers "something is wrong signal" debugging + code that we're triggering ? + */ + if (!aAssoc.isEmpty() || !aNormal.isEmpty()) + { + if (m_bIsRTL) + { + aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH) + .append(aAssoc) + .append(' ') + .append(OOO_STRING_SVTOOLS_RTF_RTLCH) + .append(aNormal); + } + else + { + aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH) + .append(aAssoc) + .append(' ') + .append(OOO_STRING_SVTOOLS_RTF_LTRCH) + .append(aNormal); + } + } + + switch (m_nScript) + { + case i18n::ScriptType::LATIN: + aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH); + break; + case i18n::ScriptType::ASIAN: + aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH); + break; + case i18n::ScriptType::COMPLEX: + /* noop */ + default: + /* should not happen? */ + break; + } + } + else + { + aBuf.append(aAssoc).append(aNormal); + } + + return aBuf.makeStringAndClear(); +} + +void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", rText: " << rText); + RawText(rText, m_rExport.GetCurrentEncoding()); +} + +OStringBuffer& RtfAttributeOutput::RunText() { return m_aRunText.getLastBuffer(); } + +void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet) +{ + m_aRunText->append(msfilter::rtfutil::OutString(rText, eCharSet)); +} + +void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 nPos, + const SwFormatRuby& rRuby) +{ + WW8Ruby aWW8Ruby(rNode, rRuby, GetExport()); + OUString aStr(FieldString(ww::eEQ) + "\\* jc"); + aStr += OUString::number(aWW8Ruby.GetJC()) + " \\* \"Font:"; + aStr += aWW8Ruby.GetFontFamily() + "\" \\* hps"; + aStr += OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o"; + if (aWW8Ruby.GetDirective()) + { + aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective()); + } + aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "("; + EndRun(&rNode, nPos); + m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::Start | FieldFlags::CmdStart); + aStr = rRuby.GetText() + "),"; + m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::NONE); +} + +void RtfAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) +{ + m_rExport.OutputField(nullptr, ww::eEQ, ")", + FieldFlags::CmdEnd | FieldFlags::End | FieldFlags::Close); + EndRun(&rNode, nPos); +} + +bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget) +{ + m_sURL = rUrl; + // Ignore hyperlink without a URL. + if (!rUrl.isEmpty()) + { + m_aRun->append('{'); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FIELD); + m_aRun->append('{'); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_IGNORE); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FLDINST); + m_aRun->append(" HYPERLINK "); + + m_aRun->append("\""); + m_aRun->append(msfilter::rtfutil::OutString(rUrl, m_rExport.GetCurrentEncoding())); + m_aRun->append("\" "); + + if (!rTarget.isEmpty()) + { + m_aRun->append("\\\\t \""); + m_aRun->append(msfilter::rtfutil::OutString(rTarget, m_rExport.GetCurrentEncoding())); + m_aRun->append("\" "); + } + + m_aRun->append("}"); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {"); + } + return true; +} + +bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph) +{ + if (!m_sURL.isEmpty()) + { + // UGLY: usually EndRun is called earlier, but there is an extra + // call to OutAttrWithRange() when at the end of the paragraph, + // so in that special case the output needs to be appended to the + // new run's text instead of the previous run + if (isAtEndOfParagraph) + { + // close the fldrslt group + m_aRunText->append("}}"); + // close the field group + m_aRunText->append('}'); + } + else + { + // close the fldrslt group + m_aRun->append("}}"); + // close the field group + m_aRun->append('}'); + } + m_sURL.clear(); + } + return true; +} + +void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::Redline(const SwRedlineData* pRedline) +{ + if (!pRedline) + return; + + if (pRedline->GetType() == RedlineType::Insert) + { + m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVISED); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTH); + m_aRun->append(static_cast<sal_Int32>( + m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())))); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTM); + } + else if (pRedline->GetType() == RedlineType::Delete) + { + m_aRun->append(OOO_STRING_SVTOOLS_RTF_DELETED); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTHDEL); + m_aRun->append(static_cast<sal_Int32>( + m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())))); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTMDEL); + } + m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp()))); + m_aRun->append(' '); +} + +void RtfAttributeOutput::FormatDrop(const SwTextNode& /*rNode*/, + const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, + ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, + ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::ParagraphStyle(sal_uInt16 nStyle) +{ + OString* pStyle = m_rExport.GetStyle(nStyle); + OStringBuffer aStyle; + aStyle.append(OOO_STRING_SVTOOLS_RTF_S); + aStyle.append(static_cast<sal_Int32>(nStyle)); + if (pStyle) + aStyle.append(pStyle->getStr()); + if (!m_bBufferSectionHeaders) + m_rExport.Strm().WriteOString(aStyle.makeStringAndClear()); + else + m_aSectionHeaders.append(aStyle.makeStringAndClear()); +} + +void RtfAttributeOutput::TableInfoCell( + ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_INTBL); + if (m_nTableDepth > 1) + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ITAP); + m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth)); + } + m_bWroteCellInfo = true; +} + +void RtfAttributeOutput::TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/) +{ + /* noop */ +} + +void RtfAttributeOutput::TableDefinition( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + InitTableHelper(pTableTextNodeInfoInner); + + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + SwFrameFormat* pFormat = pTable->GetFrameFormat(); + + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TROWD); + TableOrientation(pTableTextNodeInfoInner); + TableBidi(pTableTextNodeInfoInner); + TableHeight(pTableTextNodeInfoInner); + TableCanSplit(pTableTextNodeInfoInner); + + // Cell margins + const SvxBoxItem& rBox = pFormat->GetBox(); + static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT }; + + static const char* aRowPadNames[] + = { OOO_STRING_SVTOOLS_RTF_TRPADDT, OOO_STRING_SVTOOLS_RTF_TRPADDL, + OOO_STRING_SVTOOLS_RTF_TRPADDB, OOO_STRING_SVTOOLS_RTF_TRPADDR }; + + static const char* aRowPadUnits[] + = { OOO_STRING_SVTOOLS_RTF_TRPADDFT, OOO_STRING_SVTOOLS_RTF_TRPADDFL, + OOO_STRING_SVTOOLS_RTF_TRPADDFB, OOO_STRING_SVTOOLS_RTF_TRPADDFR }; + + for (int i = 0; i < 4; ++i) + { + m_aRowDefs.append(aRowPadUnits[i]); + m_aRowDefs.append(sal_Int32(3)); + m_aRowDefs.append(aRowPadNames[i]); + m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i]))); + } + + // The cell-dependent properties + const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio(); + const SwWriteTableRows& aRows = m_pTableWrt->GetRows(); + SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get(); + SwTwips nSz = 0; + + // Not using m_nTableDepth, which is not yet incremented here. + sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth(); + m_aCells[nCurrentDepth] = pRow->GetCells().size(); + for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++) + { + const SwWriteTableCell* const pCell = pRow->GetCells()[i].get(); + const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat(); + + pTableTextNodeInfoInner->setCell(i); + TableCellProperties(pTableTextNodeInfoInner); + + // Right boundary: this can't be in TableCellProperties as the old + // value of nSz is needed. + nSz += pCellFormat->GetFrameSize().GetWidth(); + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CELLX); + m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft() + + rtl::math::round(nSz * fWidthRatio))); + } +} + +void RtfAttributeOutput::TableDefaultBorders( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + /* + * The function name is a bit misleading: given that we write borders + * before each row, we just have borders, not default ones. Additionally, + * this function actually writes borders for a specific cell only and is + * called for each cell. + */ + + const SwWriteTableRows& aRows = m_pTableWrt->GetRows(); + SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get(); + const SwWriteTableCell* const pCell + = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get(); + const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat(); + const SfxPoolItem* pItem; + if (pCellFormat->GetAttrSet().HasItem(RES_BOX, &pItem)) + { + auto& rBox = static_cast<const SvxBoxItem&>(*pItem); + static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT }; + static const char* aBorderNames[] + = { OOO_STRING_SVTOOLS_RTF_CLBRDRT, OOO_STRING_SVTOOLS_RTF_CLBRDRL, + OOO_STRING_SVTOOLS_RTF_CLBRDRB, OOO_STRING_SVTOOLS_RTF_CLBRDRR }; + //Yes left and top are swapped with each other for cell padding! Because + //that's what the thundering annoying rtf export/import word xp does. + static const char* aCellPadNames[] + = { OOO_STRING_SVTOOLS_RTF_CLPADL, OOO_STRING_SVTOOLS_RTF_CLPADT, + OOO_STRING_SVTOOLS_RTF_CLPADB, OOO_STRING_SVTOOLS_RTF_CLPADR }; + static const char* aCellPadUnits[] + = { OOO_STRING_SVTOOLS_RTF_CLPADFL, OOO_STRING_SVTOOLS_RTF_CLPADFT, + OOO_STRING_SVTOOLS_RTF_CLPADFB, OOO_STRING_SVTOOLS_RTF_CLPADFR }; + for (int i = 0; i < 4; ++i) + { + if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i])) + m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i])); + if (rBox.GetDistance(aBorders[i])) + { + m_aRowDefs.append(aCellPadUnits[i]); + m_aRowDefs.append(sal_Int32(3)); + m_aRowDefs.append(aCellPadNames[i]); + m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i]))); + } + } + } +} + +void RtfAttributeOutput::TableBackgrounds( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine* pTableLine = pTableBox->GetUpper(); + + Color aColor = COL_AUTO; + auto pTableColorProp + = pTable->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if (pTableColorProp) + aColor = pTableColorProp->GetColor(); + + auto pRowColorProp + = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO) + aColor = pRowColorProp->GetColor(); + + const SwWriteTableRows& aRows = m_pTableWrt->GetRows(); + SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get(); + const SwWriteTableCell* const pCell + = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get(); + const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat(); + const SfxPoolItem* pItem; + if (pCellFormat->GetAttrSet().HasItem(RES_BACKGROUND, &pItem)) + { + auto& rBack = static_cast<const SvxBrushItem&>(*pItem); + if (rBack.GetColor() != COL_AUTO) + aColor = rBack.GetColor(); + } + + if (!aColor.GetTransparency()) + { + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLCBPAT); + m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor))); + } +} + +void RtfAttributeOutput::TableRowRedline( + ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) +{ +} + +void RtfAttributeOutput::TableCellRedline( + ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) +{ +} + +void RtfAttributeOutput::TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine* pTabLine = pTabBox->GetUpper(); + const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat(); + const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize(); + + if (SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) + { + sal_Int32 nHeight = 0; + + switch (rLSz.GetHeightSizeType()) + { + case SwFrameSize::Fixed: + nHeight = -rLSz.GetHeight(); + break; + case SwFrameSize::Minimum: + nHeight = rLSz.GetHeight(); + break; + default: + break; + } + + if (nHeight) + { + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRRH); + m_aRowDefs.append(nHeight); + } + } +} + +void RtfAttributeOutput::TableCanSplit( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine* pTabLine = pTabBox->GetUpper(); + const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat(); + const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit(); + + // The rtf default is to allow a row to break + if (!rSplittable.GetValue()) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRKEEP); +} + +void RtfAttributeOutput::TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat(); + + if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_LTRROW); + else + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_RTLROW); +} + +void RtfAttributeOutput::TableVerticalCell( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwWriteTableRows& aRows = m_pTableWrt->GetRows(); + SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get(); + const SwWriteTableCell* const pCell + = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get(); + const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat(); + + // Text direction. + if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat)) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXTBRL); + else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat)) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXBTLR); + + const SfxPoolItem* pItem; + + // vertical merges + if (pCell->GetRowSpan() > 1) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMGF); + else if (pCell->GetRowSpan() == 0) + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMRG); + + // vertical alignment + if (pCellFormat->GetAttrSet().HasItem(RES_VERT_ORIENT, &pItem)) + switch (static_cast<const SwFormatVertOrient*>(pItem)->GetVertOrient()) + { + case text::VertOrientation::CENTER: + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALC); + break; + case text::VertOrientation::BOTTOM: + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALB); + break; + default: + m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALT); + break; + } +} + +void RtfAttributeOutput::TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner) +{ + // This is called when the nested table ends in a cell, and there's no + // paragraph behind that; so we must check for the ends of cell, rows, + // and tables + FinishTableRowCell(pNodeInfoInner); +} + +void RtfAttributeOutput::TableOrientation( + ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + SwFrameFormat* pFormat = pTable->GetFrameFormat(); + + OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL); + switch (pFormat->GetHoriOrient().GetHoriOrient()) + { + case text::HoriOrientation::CENTER: + aTableAdjust.setLength(0); + aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC); + break; + case text::HoriOrientation::RIGHT: + aTableAdjust.setLength(0); + aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR); + break; + case text::HoriOrientation::NONE: + case text::HoriOrientation::LEFT_AND_WIDTH: + aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT); + aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft())); + break; + default: + break; + } + + m_aRowDefs.append(aTableAdjust.makeStringAndClear()); +} + +void RtfAttributeOutput::TableSpacing( + ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */} + +/* + * Our private table methods. + */ + +void RtfAttributeOutput::InitTableHelper( + const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner) +{ + const SwTable* pTable = pTableTextNodeInfoInner->getTable(); + if (m_pTableWrt && pTable == m_pTableWrt->GetTable()) + return; + + long nPageSize = 0; + bool bRelBoxSize = false; + + // Create the SwWriteTable instance to use col spans + GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize); + + const SwFrameFormat* pFormat = pTable->GetFrameFormat(); + const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth(); + + const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout(); + if (pLayout && pLayout->IsExportable()) + m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout); + else + m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize, + nTableSz, false); +} + +void RtfAttributeOutput::StartTable() +{ + // To trigger calling InitTableHelper() + m_pTableWrt.reset(); +} + +void RtfAttributeOutput::StartTableRow( + const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner) +{ + sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth(); + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", (depth is " << nCurrentDepth << ")"); + m_bTableRowEnded = false; + + TableDefinition(pTableTextNodeInfoInner); + + if (!m_bLastTable) + m_aTables.push_back(m_aRowDefs.makeStringAndClear()); + + // We'll write the table definition for nested tables later + if (nCurrentDepth > 1) + return; + // Empty the previous row closing buffer before starting the new one, + // necessary for subtables. + m_rExport.Strm().WriteOString(m_aAfterRuns.makeStringAndClear()); + m_rExport.Strm().WriteOString(m_aRowDefs.makeStringAndClear()); +} + +void RtfAttributeOutput::StartTableCell() { m_bTableCellOpen = true; } + +void RtfAttributeOutput::TableCellProperties( + const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner) +{ + TableDefaultBorders(pTableTextNodeInfoInner); + TableBackgrounds(pTableTextNodeInfoInner); + TableVerticalCell(pTableTextNodeInfoInner); +} + +void RtfAttributeOutput::EndTableCell() +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", (depth is " << m_nTableDepth << ")"); + + if (!m_bWroteCellInfo) + { + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_INTBL); + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ITAP); + m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth)); + } + if (m_nTableDepth > 1) + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTCELL); + else + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_CELL); + + m_bTableCellOpen = false; + m_bTableAfterCell = true; + m_bWroteCellInfo = false; + if (m_aCells[m_nTableDepth] > 0) + m_aCells[m_nTableDepth]--; +} + +void RtfAttributeOutput::EndTableRow() +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", (depth is " << m_nTableDepth << ")"); + + // Trying to end the row without writing the required number of cells? Fill with empty ones. + for (sal_uInt32 i = 0; i < m_aCells[m_nTableDepth]; i++) + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_CELL); + + if (m_nTableDepth > 1) + { + m_aAfterRuns.append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_NESTTABLEPROPRS); + if (!m_aRowDefs.isEmpty()) + m_aAfterRuns.append(m_aRowDefs.makeStringAndClear()); + else if (!m_aTables.empty()) + { + m_aAfterRuns.append(m_aTables.back()); + m_aTables.pop_back(); + } + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTROW + "}" + "{" OOO_STRING_SVTOOLS_RTF_NONESTTABLES OOO_STRING_SVTOOLS_RTF_PAR "}"); + } + else + { + if (!m_aTables.empty()) + { + m_aAfterRuns.append(m_aTables.back()); + m_aTables.pop_back(); + } + m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ROW).append(OOO_STRING_SVTOOLS_RTF_PARD); + } + m_bTableRowEnded = true; +} + +void RtfAttributeOutput::EndTable() +{ + if (m_nTableDepth > 0) + { + m_nTableDepth--; + m_pTableWrt.reset(); + } + + // We closed the table; if it is a nested table, the cell that contains it + // still continues + m_bTableCellOpen = true; + + // Cleans the table helper + m_pTableWrt.reset(); +} + +void RtfAttributeOutput::FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner) +{ + if (pInner) + { + // Where are we in the table + sal_uInt32 nRow = pInner->getRow(); + + const SwTable* pTable = pInner->getTable(); + const SwTableLines& rLines = pTable->GetTabLines(); + sal_uInt16 nLinesCount = rLines.size(); + + if (pInner->isEndOfCell()) + EndTableCell(); + + // This is a line end + if (pInner->isEndOfLine()) + EndTableRow(); + + // This is the end of the table + if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount) + EndTable(); + } +} + +void RtfAttributeOutput::StartStyles() +{ + m_rExport.Strm() + .WriteCharPtr(SAL_NEWLINE_STRING) + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLORTBL); + m_rExport.OutColorTable(); + OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty"); + m_aStylesheet.append(SAL_NEWLINE_STRING); + m_aStylesheet.append('{'); + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_STYLESHEET); +} + +void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/) +{ + m_rExport.Strm().WriteChar('}'); + m_rExport.Strm().WriteOString(m_aStylesheet.makeStringAndClear()); + m_rExport.Strm().WriteChar('}'); +} + +void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */} + +void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase, + sal_uInt16 nNext, sal_uInt16 /*nWwId*/, sal_uInt16 nId, + bool bAutoUpdate) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", rName = '" << rName << "'"); + + m_aStylesheet.append('{'); + if (eType == STYLE_TYPE_PARA) + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_S); + else + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_CS); + m_aStylesheet.append(static_cast<sal_Int32>(nId)); + + if (nBase != 0x0FFF) + { + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SBASEDON); + m_aStylesheet.append(static_cast<sal_Int32>(nBase)); + } + + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SNEXT); + m_aStylesheet.append(static_cast<sal_Int32>(nNext)); + + if (bAutoUpdate) + m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SAUTOUPD); + + m_rStyleName = rName; + m_nStyleId = nId; +} + +void RtfAttributeOutput::EndStyle() +{ + OString aStyles = MoveCharacterProperties(); + m_rExport.InsStyle(m_nStyleId, aStyles); + m_aStylesheet.append(aStyles); + m_aStylesheet.append(' '); + m_aStylesheet.append( + msfilter::rtfutil::OutString(m_rStyleName, m_rExport.GetCurrentEncoding())); + m_aStylesheet.append(";}"); + m_aStylesheet.append(SAL_NEWLINE_STRING); +} + +void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/) +{ + /* noop */ +} + +void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */} + +void RtfAttributeOutput::OutlineNumbering(sal_uInt8 nLvl) +{ + if (nLvl >= WW8ListManager::nMaxLevel) + nLvl = WW8ListManager::nMaxLevel - 1; + + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL); + m_aStyles.append(static_cast<sal_Int32>(nLvl)); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTLINELEVEL); + m_aStyles.append(static_cast<sal_Int32>(nLvl)); +} + +void RtfAttributeOutput::PageBreakBefore(bool bBreak) +{ + if (bBreak) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAGEBB); + } +} + +void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/, + const WW8_SepInfo* pSectionInfo) +{ + switch (nC) + { + case msword::ColumnBreak: + m_nColBreakNeeded = true; + break; + case msword::PageBreak: + if (pSectionInfo) + m_rExport.SectionProperties(*pSectionInfo); + break; + } +} + +void RtfAttributeOutput::StartSection() +{ + if (m_bIsBeforeFirstParagraph) + return; + + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECT OOO_STRING_SVTOOLS_RTF_SECTD); + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); +} + +void RtfAttributeOutput::EndSection() +{ + /* + * noop, \sect must go to StartSection or Word won't notice multiple + * columns... + */ +} + +void RtfAttributeOutput::SectionFormProtection(bool bProtected) +{ + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED); + m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected)); +} + +void RtfAttributeOutput::SectionLineNumbering(sal_uLong nRestartNo, + const SwLineNumberInfo& rLnNumInfo) +{ + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LINEMOD); + m_rExport.OutLong(rLnNumInfo.GetCountBy()); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LINEX); + m_rExport.OutLong(rLnNumInfo.GetPosFromLeft()); + if (!rLnNumInfo.IsRestartEachPage()) + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LINECONT); + + if (nRestartNo > 0) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LINESTARTS); + m_rExport.OutLong(nRestartNo); + } +} + +void RtfAttributeOutput::SectionTitlePage() +{ + /* + * noop, handled in RtfExport::WriteHeaderFooter() + */ +} + +void RtfAttributeOutput::SectionPageBorders(const SwFrameFormat* pFormat, + const SwFrameFormat* /*pFirstPageFormat*/) +{ + const SvxBoxItem& rBox = pFormat->GetBox(); + const editeng::SvxBorderLine* pLine = rBox.GetTop(); + if (pLine) + m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRT, + rBox.GetDistance(SvxBoxItemLine::TOP))); + pLine = rBox.GetBottom(); + if (pLine) + m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRB, + rBox.GetDistance(SvxBoxItemLine::BOTTOM))); + pLine = rBox.GetLeft(); + if (pLine) + m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRL, + rBox.GetDistance(SvxBoxItemLine::LEFT))); + pLine = rBox.GetRight(); + if (pLine) + m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRR, + rBox.GetDistance(SvxBoxItemLine::RIGHT))); +} + +void RtfAttributeOutput::SectionBiDi(bool bBiDi) +{ + m_rExport.Strm().WriteCharPtr(bBiDi ? OOO_STRING_SVTOOLS_RTF_RTLSECT + : OOO_STRING_SVTOOLS_RTF_LTRSECT); +} + +void RtfAttributeOutput::SectionPageNumbering(sal_uInt16 nNumType, + const ::std::optional<sal_uInt16>& oPageRestartNumber) +{ + if (oPageRestartNumber) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNSTARTS); + m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber)); + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNRESTART); + } + + const char* pStr = nullptr; + switch (nNumType) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + pStr = OOO_STRING_SVTOOLS_RTF_PGNUCLTR; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + pStr = OOO_STRING_SVTOOLS_RTF_PGNLCLTR; + break; + case SVX_NUM_ROMAN_UPPER: + pStr = OOO_STRING_SVTOOLS_RTF_PGNUCRM; + break; + case SVX_NUM_ROMAN_LOWER: + pStr = OOO_STRING_SVTOOLS_RTF_PGNLCRM; + break; + + case SVX_NUM_ARABIC: + pStr = OOO_STRING_SVTOOLS_RTF_PGNDEC; + break; + } + if (pStr) + m_aSectionBreaks.append(pStr); +} + +void RtfAttributeOutput::SectionType(sal_uInt8 nBreakCode) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", nBreakCode = " << int(nBreakCode)); + + /* + * break code: 0 No break, 1 New column + * 2 New page, 3 Even page, 4 Odd page + */ + const char* sType = nullptr; + switch (nBreakCode) + { + case 1: + sType = OOO_STRING_SVTOOLS_RTF_SBKCOL; + break; + case 2: + sType = OOO_STRING_SVTOOLS_RTF_SBKPAGE; + break; + case 3: + sType = OOO_STRING_SVTOOLS_RTF_SBKEVEN; + break; + case 4: + sType = OOO_STRING_SVTOOLS_RTF_SBKODD; + break; + default: + sType = OOO_STRING_SVTOOLS_RTF_SBKNONE; + break; + } + m_aSectionBreaks.append(sType); + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); +} + +void RtfAttributeOutput::SectFootnoteEndnotePr() +{ + WriteFootnoteEndnotePr(true, m_rExport.m_pDoc->GetFootnoteInfo()); + WriteFootnoteEndnotePr(false, m_rExport.m_pDoc->GetEndNoteInfo()); +} + +void RtfAttributeOutput::WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo) +{ + const char* pOut = nullptr; + + if (bFootnote) + { + switch (rInfo.m_aFormat.GetNumberingType()) + { + default: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAR; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNALC; + break; + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAUC; + break; + case SVX_NUM_ROMAN_LOWER: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRLC; + break; + case SVX_NUM_ROMAN_UPPER: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRUC; + break; + case SVX_NUM_SYMBOL_CHICAGO: + pOut = OOO_STRING_SVTOOLS_RTF_SFTNNCHI; + break; + } + } + else + { + switch (rInfo.m_aFormat.GetNumberingType()) + { + default: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAR; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNALC; + break; + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAUC; + break; + case SVX_NUM_ROMAN_LOWER: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRLC; + break; + case SVX_NUM_ROMAN_UPPER: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRUC; + break; + case SVX_NUM_SYMBOL_CHICAGO: + pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNCHI; + break; + } + } + + m_aSectionBreaks.append(pOut); + + if (!m_bBufferSectionBreaks) + { + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + } +} + +void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/) +{ + m_rExport.Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDE); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTID); + m_rExport.OutULong(nId); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDECOUNT).WriteChar('0'); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LS); + m_rExport.OutULong(nId).WriteChar('}'); +} + +void RtfAttributeOutput::StartAbstractNumbering(sal_uInt16 nId) +{ + m_rExport.Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LIST) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTTEMPLATEID); + m_rExport.OutULong(nId); + m_nListId = nId; +} + +void RtfAttributeOutput::EndAbstractNumbering() +{ + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTID); + m_rExport.OutULong(m_nListId).WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING); +} + +void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart, + sal_uInt16 nNumberingType, SvxAdjust eAdjust, + const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow, + const wwFont* pFont, const SfxItemSet* pOutSet, + sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex, + sal_Int16 /*nListTabPos*/, const OUString& rNumberingString, + const SvxBrushItem* pBrush) +{ + m_rExport.Strm().WriteCharPtr(SAL_NEWLINE_STRING); + if (nLevel > 8) // RTF knows only 9 levels + m_rExport.Strm() + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SOUTLVL); + + m_rExport.Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTLEVEL); + + sal_uInt16 nVal = 0; + switch (nNumberingType) + { + case SVX_NUM_ROMAN_UPPER: + nVal = 1; + break; + case SVX_NUM_ROMAN_LOWER: + nVal = 2; + break; + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + nVal = 3; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + nVal = 4; + break; + case SVX_NUM_FULL_WIDTH_ARABIC: + nVal = 14; + break; + case SVX_NUM_CIRCLE_NUMBER: + nVal = 18; + break; + case SVX_NUM_NUMBER_LOWER_ZH: + nVal = 35; + if (pOutSet) + { + const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE); + if (rLang.GetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED) + { + nVal = 39; + } + } + break; + case SVX_NUM_NUMBER_UPPER_ZH: + nVal = 38; + break; + case SVX_NUM_NUMBER_UPPER_ZH_TW: + nVal = 34; + break; + case SVX_NUM_TIAN_GAN_ZH: + nVal = 30; + break; + case SVX_NUM_DI_ZI_ZH: + nVal = 31; + break; + case SVX_NUM_NUMBER_TRADITIONAL_JA: + nVal = 16; + break; + case SVX_NUM_AIU_FULLWIDTH_JA: + nVal = 20; + break; + case SVX_NUM_AIU_HALFWIDTH_JA: + nVal = 12; + break; + case SVX_NUM_IROHA_FULLWIDTH_JA: + nVal = 21; + break; + case SVX_NUM_IROHA_HALFWIDTH_JA: + nVal = 13; + break; + case style::NumberingType::HANGUL_SYLLABLE_KO: + nVal = 24; + break; // ganada + case style::NumberingType::HANGUL_JAMO_KO: + nVal = 25; + break; // chosung + case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: + nVal = 24; + break; + case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: + nVal = 25; + break; + case style::NumberingType::NUMBER_HANGUL_KO: + nVal = 41; + break; + case style::NumberingType::NUMBER_UPPER_KO: + nVal = 44; + break; + + case SVX_NUM_BITMAP: + case SVX_NUM_CHAR_SPECIAL: + nVal = 23; + break; + case SVX_NUM_NUMBER_NONE: + nVal = 255; + break; + case SVX_NUM_ARABIC_ZERO: + nVal = 22; + break; + } + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELNFC); + m_rExport.OutULong(nVal); + + switch (eAdjust) + { + case SvxAdjust::Center: + nVal = 1; + break; + case SvxAdjust::Right: + nVal = 2; + break; + default: + nVal = 0; + break; + } + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELJC); + m_rExport.OutULong(nVal); + + // bullet + if (nNumberingType == SVX_NUM_BITMAP && pBrush) + { + int nIndex = m_rExport.GetGrfIndex(*pBrush); + if (nIndex != -1) + { + m_rExport.Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_LEVELPICTURE); + m_rExport.OutULong(nIndex); + } + } + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELSTARTAT); + m_rExport.OutULong(nStart); + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELFOLLOW); + m_rExport.OutULong(nFollow); + + // leveltext group + m_rExport.Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELTEXT).WriteChar(' '); + + if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType) + { + m_rExport.Strm().WriteCharPtr("\\'01"); + sal_Unicode cChar = rNumberingString[0]; + m_rExport.Strm().WriteCharPtr("\\u"); + m_rExport.OutULong(cChar); + m_rExport.Strm().WriteCharPtr(" ?"); + } + else + { + m_rExport.Strm().WriteCharPtr("\\'").WriteCharPtr( + msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2).getStr()); + m_rExport.Strm().WriteCharPtr(msfilter::rtfutil::OutString(rNumberingString, + m_rExport.GetDefaultEncoding(), + /*bUnicode =*/false) + .getStr()); + } + + m_rExport.Strm().WriteCharPtr(";}"); + + // write the levelnumbers + m_rExport.Strm().WriteCharPtr("{").WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVELNUMBERS); + for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i) + { + m_rExport.Strm().WriteCharPtr("\\'").WriteCharPtr( + msfilter::rtfutil::OutHex(pNumLvlPos[i], 2).getStr()); + } + m_rExport.Strm().WriteCharPtr(";}"); + + if (pOutSet) + { + if (pFont) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_F); + m_rExport.OutULong(m_rExport.m_aFontHelper.GetId(*pFont)); + } + m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN, + m_rExport.m_bExportModeRTF); + const OString aProperties = MoveCharacterProperties(true); + m_rExport.Strm().WriteOString(aProperties); + } + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FI); + m_rExport.OutLong(nFirstLineIndex).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LI); + m_rExport.OutLong(nIndentAt); + + m_rExport.Strm().WriteChar('}'); + if (nLevel > 8) + m_rExport.Strm().WriteChar('}'); +} + +void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/, + const OUString& rFieldCmd, FieldFlags nMode) +{ + // If there are no field instructions, don't export it as a field. + bool bHasInstructions = !rFieldCmd.isEmpty(); + if (FieldFlags::All == nMode) + { + if (bHasInstructions) + { + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD); + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST + " "); + m_aRunText->append( + msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding())); + m_aRunText->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " "); + } + if (pField) + m_aRunText->append(msfilter::rtfutil::OutString(pField->ExpandField(true, nullptr), + m_rExport.GetDefaultEncoding())); + if (bHasInstructions) + m_aRunText->append("}}"); + } + else + { + if (nMode & FieldFlags::CmdStart) + { + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD); + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST + // paragraph break closes group so open another one "inside" to + " {"); // prevent leaving the field instruction + } + if (bHasInstructions) + m_aRunText->append( + msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding())); + if (nMode & FieldFlags::CmdEnd) + { + m_aRunText->append("}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {"); + } + if (nMode & FieldFlags::Close) + { + m_aRunText->append("}}}"); + } + } +} + +void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts, + std::vector<OUString>& rEnds) +{ + for (const auto& rStart : rStarts) + { + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKSTART " "); + m_aRun->append(msfilter::rtfutil::OutString(rStart, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + rStarts.clear(); + + for (const auto& rEnd : rEnds) + { + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKEND " "); + m_aRun->append(msfilter::rtfutil::OutString(rEnd, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + rEnds.clear(); +} + +void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts, + std::vector<OUString>& rEnds) +{ + for (const auto& rStart : rStarts) + { + OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8); + + // Output the annotation mark + const sal_Int32 nId = m_nNextAnnotationMarkId++; + m_rOpenedAnnotationMarksIds[rName] = nId; + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFSTART " "); + m_aRun->append(OString::number(nId).getStr()); + m_aRun->append('}'); + } + rStarts.clear(); + + for (const auto& rEnd : rEnds) + { + OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8); + + // Get the id of the annotation mark + auto it = m_rOpenedAnnotationMarksIds.find(rName); + if (it != m_rOpenedAnnotationMarksIds.end()) + { + const sal_Int32 nId = it->second; + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFEND " "); + m_aRun->append(OString::number(nId).getStr()); + m_aRun->append('}'); + m_rOpenedAnnotationMarksIds.erase(rName); + + if (m_aPostitFields.find(nId) != m_aPostitFields.end()) + { + m_aRunText->append("{"); + m_nCurrentAnnotationMarkId = nId; + PostitField(m_aPostitFields[nId]); + m_nCurrentAnnotationMarkId = -1; + m_aRunText->append("}"); + } + } + } + rEnds.clear(); +} + +void RtfAttributeOutput::WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader, + const char* pStr, bool bTitlepg) +{ + OStringBuffer aSectionBreaks = m_aSectionBreaks; + m_aSectionBreaks.setLength(0); + RtfStringBuffer aRun = m_aRun; + m_aRun.clear(); + + m_aSectionHeaders.append(bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERY + : OOO_STRING_SVTOOLS_RTF_FOOTERY); + m_aSectionHeaders.append( + static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper())); + if (bTitlepg) + m_aSectionHeaders.append(OOO_STRING_SVTOOLS_RTF_TITLEPG); + m_aSectionHeaders.append('{'); + m_aSectionHeaders.append(pStr); + m_bBufferSectionHeaders = true; + m_rExport.WriteHeaderFooterText(rFormat, bHeader); + m_bBufferSectionHeaders = false; + m_aSectionHeaders.append('}'); + + m_aSectionBreaks = aSectionBreaks; + m_aRun = aRun; +} + +namespace +{ +void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties, + const SwFrameFormat& rFrameFormat) +{ + const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); + if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) + return; + + rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow", OString::number(1))); + + const Color& rColor = aShadowItem.GetColor(); + // We in fact need RGB to BGR, but the transformation is symmetric. + rFlyProperties.push_back(std::make_pair<OString, OString>( + "shadowColor", OString::number(wwUtility::RGBToBGR(rColor)))); + + // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip. + OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700); + OString aOffsetX; + OString aOffsetY; + switch (aShadowItem.GetLocation()) + { + case SvxShadowLocation::TopLeft: + aOffsetX = "-" + aShadowWidth; + aOffsetY = "-" + aShadowWidth; + break; + case SvxShadowLocation::TopRight: + aOffsetX = aShadowWidth; + aOffsetY = "-" + aShadowWidth; + break; + case SvxShadowLocation::BottomLeft: + aOffsetX = "-" + aShadowWidth; + aOffsetY = aShadowWidth; + break; + case SvxShadowLocation::BottomRight: + aOffsetX = aShadowWidth; + aOffsetY = aShadowWidth; + break; + case SvxShadowLocation::NONE: + case SvxShadowLocation::End: + break; + } + if (!aOffsetX.isEmpty()) + rFlyProperties.emplace_back("shadowOffsetX", aOffsetX); + if (!aOffsetY.isEmpty()) + rFlyProperties.emplace_back("shadowOffsetY", aOffsetY); +} + +void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties, + const SwFrameFormat& rFrameFormat) +{ + const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize(); + + // Relative size of the Text Frame. + const sal_uInt8 nWidthPercent = rSize.GetWidthPercent(); + if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED) + { + rFlyProperties.push_back( + std::make_pair<OString, OString>("pctHoriz", OString::number(nWidthPercent * 10))); + + OString aRelation; + switch (rSize.GetWidthPercentRelation()) + { + case text::RelOrientation::PAGE_FRAME: + aRelation = "1"; // page + break; + default: + aRelation = "0"; // margin + break; + } + rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation)); + } + const sal_uInt8 nHeightPercent = rSize.GetHeightPercent(); + if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED) + { + rFlyProperties.push_back( + std::make_pair<OString, OString>("pctVert", OString::number(nHeightPercent * 10))); + + OString aRelation; + switch (rSize.GetHeightPercentRelation()) + { + case text::RelOrientation::PAGE_FRAME: + aRelation = "1"; // page + break; + default: + aRelation = "0"; // margin + break; + } + rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation)); + } +} +} + +void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox) +{ + RtfStringBuffer aRunText; + if (bTextBox) + { + m_rExport.setStream(); + aRunText = m_aRunText; + m_aRunText.clear(); + } + + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SHPTXT); + + { + // Save table state, in case the inner text also contains a table. + ww8::WW8TableInfo::Pointer_t pTableInfoOrig = m_rExport.m_pTableInfo; + m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); + std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt)); + sal_uInt32 nTableDepth = m_nTableDepth; + + m_nTableDepth = 0; + /* + * Save m_aRun as we should not lose the opening brace. + * OTOH, just drop the contents of m_aRunText in case something + * would be there, causing a problem later. + */ + OString aSave = m_aRun.makeStringAndClear(); + // Also back m_bInRun and m_bSingleEmptyRun up. + bool bInRunOrig = m_bInRun; + m_bInRun = false; + bool bSingleEmptyRunOrig = m_bSingleEmptyRun; + m_bSingleEmptyRun = false; + m_rExport.SetRTFFlySyntax(true); + + const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat(); + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + m_rExport.SaveData(nStt, nEnd); + m_rExport.m_pParentFrame = &rFrame; + m_rExport.WriteText(); + m_rExport.RestoreData(); + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PARD); + m_rExport.SetRTFFlySyntax(false); + m_aRun->append(aSave); + m_aRunText.clear(); + m_bInRun = bInRunOrig; + m_bSingleEmptyRun = bSingleEmptyRunOrig; + + // Restore table state. + m_rExport.m_pTableInfo = pTableInfoOrig; + m_pTableWrt = std::move(pTableWrt); + m_nTableDepth = nTableDepth; + } + + m_rExport.m_pParentFrame = nullptr; + + m_rExport.Strm().WriteChar('}'); // shptxt + + if (bTextBox) + { + m_aRunText = aRunText; + m_aRunText->append(m_rExport.getStream()); + m_rExport.resetStream(); + } +} + +/** save the current run state around exporting things that contain paragraphs + themselves like text frames. + TODO: probably more things need to be saved? + */ +class SaveRunState +{ +private: + RtfAttributeOutput& m_rRtf; + RtfStringBuffer m_Run; + RtfStringBuffer m_RunText; + bool const m_bSingleEmptyRun; + bool const m_bInRun; + +public: + explicit SaveRunState(RtfAttributeOutput& rRtf) + : m_rRtf(rRtf) + , m_Run(std::move(rRtf.m_aRun)) + , m_RunText(std::move(rRtf.m_aRunText)) + , m_bSingleEmptyRun(rRtf.m_bSingleEmptyRun) + , m_bInRun(rRtf.m_bInRun) + { + m_rRtf.m_rExport.setStream(); + } + ~SaveRunState() + { + m_rRtf.m_aRun = std::move(m_Run); + m_rRtf.m_aRunText = std::move(m_RunText); + m_rRtf.m_bSingleEmptyRun = m_bSingleEmptyRun; + m_rRtf.m_bInRun = m_bInRun; + + m_rRtf.m_aRunText->append(m_rRtf.m_rExport.getStream()); + m_rRtf.m_rExport.resetStream(); + } +}; + +void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/) +{ + const SwNode* pNode = rFrame.GetContent(); + const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr; + + switch (rFrame.GetWriterType()) + { + case ww8::Frame::eTextBox: + { + // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape(). + if (RtfSdrExport::isTextBox(rFrame.GetFrameFormat())) + break; + + SaveRunState const saved(*this); + + m_rExport.m_pParentFrame = &rFrame; + + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SHP); + m_rExport.Strm().WriteCharPtr( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST); + + // Shape properties. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "shapeType", OString::number(ESCHER_ShpInst_TextBox))); + + // When a frame has some low height, but automatically expanded due + // to lots of contents, this size contains the real size. + const Size aSize = rFrame.GetSize(); + m_pFlyFrameSize = &aSize; + + m_rExport.m_bOutFlyFrameAttrs = true; + m_rExport.SetRTFFlySyntax(true); + m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true); + + // Write ZOrder. + if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject()) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPZ); + m_rExport.OutULong(pObject->GetOrdNum()); + } + + m_rExport.Strm().WriteOString(m_aRunText.makeStringAndClear()); + m_rExport.Strm().WriteOString(m_aStyles.makeStringAndClear()); + m_rExport.m_bOutFlyFrameAttrs = false; + m_rExport.SetRTFFlySyntax(false); + m_pFlyFrameSize = nullptr; + + const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat(); + lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat); + lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat); + + for (const std::pair<OString, OString>& rPair : m_aFlyProperties) + { + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{"); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SN " "); + m_rExport.Strm().WriteOString(rPair.first); + m_rExport.Strm().WriteCharPtr("}{" OOO_STRING_SVTOOLS_RTF_SV " "); + m_rExport.Strm().WriteOString(rPair.second); + m_rExport.Strm().WriteCharPtr("}}"); + } + m_aFlyProperties.clear(); + + writeTextFrame(rFrame); + + m_rExport.Strm().WriteChar('}'); // shpinst + m_rExport.Strm().WriteChar('}'); // shp + + m_rExport.Strm().WriteCharPtr(SAL_NEWLINE_STRING); + } + break; + case ww8::Frame::eGraphic: + if (pGrfNode) + { + m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()), + pGrfNode); + } + else if (!rFrame.IsInline()) + { + m_rExport.m_pParentFrame = &rFrame; + m_rExport.SetRTFFlySyntax(true); + m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true); + m_rExport.SetRTFFlySyntax(false); + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE); + m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true); + m_aRunText->append('}'); + m_rExport.m_pParentFrame = nullptr; + } + break; + case ww8::Frame::eDrawing: + { + const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject(); + if (pSdrObj) + { + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD "{"); + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_IGNORE); + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDINST); + m_aRunText->append(" SHAPE "); + m_aRunText->append("}" + "{" OOO_STRING_SVTOOLS_RTF_FLDRSLT); + + m_rExport.SdrExporter().AddSdrObject(*pSdrObj); + + m_aRunText->append('}'); + m_aRunText->append('}'); + } + } + break; + case ww8::Frame::eFormControl: + { + const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat(); + const SdrObject* pObject = rFrameFormat.FindRealSdrObject(); + + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FIELD); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST); + + if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm) + { + if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject)) + { + const uno::Reference<awt::XControlModel>& xControlModel + = pFormObj->GetUnoControlModel(); + uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY); + if (xInfo.is()) + { + uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropSetInfo + = xPropSet->getPropertySetInfo(); + OUString sName; + if (xInfo->supportsService("com.sun.star.form.component.CheckBox")) + { + m_aRun->append(OUStringToOString(FieldString(ww::eFORMCHECKBOX), + m_rExport.GetCurrentEncoding())); + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD + "{"); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox + // checkbox size in half points, this seems to be always 20 + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHPS "20"); + + OUString aStr; + sName = "Name"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME + " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sName = "HelpText"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sName = "HelpF1Text"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sal_Int16 nTemp = 0; + xPropSet->getPropertyValue("DefaultState") >>= nTemp; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES); + m_aRun->append(static_cast<sal_Int32>(nTemp)); + xPropSet->getPropertyValue("State") >>= nTemp; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES); + m_aRun->append(static_cast<sal_Int32>(nTemp)); + + m_aRun->append("}}"); + + // field result is empty, ffres already contains the form result + m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " "); + } + else if (xInfo->supportsService("com.sun.star.form.component.TextField")) + { + OStringBuffer aBuf; + OString aStr; + OUString aTmp; + const char* pStr; + + m_aRun->append(OUStringToOString(FieldString(ww::eFORMTEXT), + m_rExport.GetCurrentEncoding())); + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DATAFIELD + " "); + for (int i = 0; i < 8; i++) + aBuf.append(char(0x00)); + xPropSet->getPropertyValue("Name") >>= aTmp; + aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()); + aBuf.append(static_cast<char>(aStr.getLength())); + aBuf.append(aStr); + aBuf.append(char(0x00)); + xPropSet->getPropertyValue("DefaultText") >>= aTmp; + aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()); + aBuf.append(static_cast<char>(aStr.getLength())); + aBuf.append(aStr); + for (int i = 0; i < 11; i++) + aBuf.append(char(0x00)); + aStr = aBuf.makeStringAndClear(); + pStr = aStr.getStr(); + for (int i = 0; i < aStr.getLength(); i++, pStr++) + m_aRun->append(msfilter::rtfutil::OutHex(*pStr, 2)); + m_aRun->append('}'); + m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " "); + xPropSet->getPropertyValue("Text") >>= aTmp; + m_aRun->append(OUStringToOString(aTmp, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD + "{"); + sName = "HelpText"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aTmp; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " "); + m_aRun->append( + OUStringToOString(aTmp, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sName = "HelpF1Text"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aTmp; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " "); + m_aRun->append( + OUStringToOString(aTmp, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + m_aRun->append("}"); + } + else if (xInfo->supportsService("com.sun.star.form.component.ListBox")) + { + OUString aStr; + uno::Sequence<sal_Int16> aIntSeq; + uno::Sequence<OUString> aStrSeq; + + m_aRun->append(OUStringToOString(FieldString(ww::eFORMDROPDOWN), + m_rExport.GetCurrentEncoding())); + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD + "{"); + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "2"); // 2 = list + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX); + + xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq; + if (aIntSeq.hasElements()) + { + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES); + // a dropdown list can have only one 'selected item by default' + m_aRun->append(static_cast<sal_Int32>(aIntSeq[0])); + } + + xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq; + if (aIntSeq.hasElements()) + { + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES); + // a dropdown list can have only one 'currently selected item' + m_aRun->append(static_cast<sal_Int32>(aIntSeq[0])); + } + + sName = "Name"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME + " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sName = "HelpText"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + sName = "HelpF1Text"; + if (xPropSetInfo->hasPropertyByName(sName)) + { + xPropSet->getPropertyValue(sName) >>= aStr; + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE + OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " "); + m_aRun->append( + OUStringToOString(aStr, m_rExport.GetCurrentEncoding())); + m_aRun->append('}'); + } + + xPropSet->getPropertyValue("StringItemList") >>= aStrSeq; + for (const auto& rStr : std::as_const(aStrSeq)) + m_aRun + ->append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL + " ") + .append(OUStringToOString(rStr, m_rExport.GetCurrentEncoding())) + .append('}'); + + m_aRun->append("}}"); + + // field result is empty, ffres already contains the form result + m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " "); + } + else + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " unhandled form control: '" + << xInfo->getImplementationName() + << "'"); + m_aRun->append('}'); + } + } + } + + m_aRun->append('}'); + } + break; + case ww8::Frame::eOle: + { + const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat(); + const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject(); + if (pSdrObj) + { + SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1); + SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode(); + FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd, + rFrame.GetLayoutSize()); + } + } + break; + default: + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ": unknown type (" + << static_cast<int>(rFrame.GetWriterType()) << ")"); + break; + } +} + +void RtfAttributeOutput::CharCaseMap(const SvxCaseMapItem& rCaseMap) +{ + switch (rCaseMap.GetValue()) + { + case SvxCaseMap::SmallCaps: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS); + break; + case SvxCaseMap::Uppercase: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS); + break; + default: // Something that rtf does not support + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS); + m_aStyles.append(sal_Int32(0)); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS); + m_aStyles.append(sal_Int32(0)); + break; + } +} + +void RtfAttributeOutput::CharColor(const SvxColorItem& rColor) +{ + const Color aColor(rColor.GetValue()); + + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CF); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor))); +} + +void RtfAttributeOutput::CharContour(const SvxContourItem& rContour) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTL); + if (!rContour.GetValue()) + m_aStyles.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharCrossedOut(const SvxCrossedOutItem& rCrossedOut) +{ + switch (rCrossedOut.GetStrikeout()) + { + case STRIKEOUT_NONE: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE); + m_aStyles.append(sal_Int32(0)); + break; + case STRIKEOUT_DOUBLE: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKED); + m_aStyles.append(sal_Int32(1)); + break; + default: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE); + break; + } +} + +void RtfAttributeOutput::CharEscapement(const SvxEscapementItem& rEscapement) +{ + short nEsc = rEscapement.GetEsc(); + short nProp = rEscapement.GetProportionalHeight(); + sal_Int32 nProp100 = nProp * 100; + if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100) + { + if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUB); + else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUPER); + return; + } + if (DFLT_ESC_AUTO_SUPER == nEsc) + { + nEsc = .8 * (100 - nProp); + ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx + } + else if (DFLT_ESC_AUTO_SUB == nEsc) + { + nEsc = .2 * -(100 - nProp); + ++nProp100; + } + + const char* pUpDn; + + double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight(); + + if (0 < nEsc) + pUpDn = OOO_STRING_SVTOOLS_RTF_UP; + else if (0 > nEsc) + { + pUpDn = OOO_STRING_SVTOOLS_RTF_DN; + fHeight = -fHeight; + } + else + return; + + m_aStyles.append('{'); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_IGNORE); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_UPDNPROP); + m_aStyles.append(nProp100); + m_aStyles.append('}'); + m_aStyles.append(pUpDn); + + /* + * Calculate the act. FontSize and the percentage of the displacement; + * RTF file expects half points, while internally it's in twips. + * Formally : (FontSize * 1/20 ) pts x * 2 + * ----------------------- = ------------ + * 100% Escapement + */ + m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000))); +} + +void RtfAttributeOutput::CharFont(const SvxFontItem& rFont) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LOCH); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_F); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont))); + + if (!m_rExport.HasItem(RES_CHRATR_CJK_FONT) && !m_rExport.HasItem(RES_CHRATR_CTL_FONT)) + { + // Be explicit about that the given font should be used everywhere, not + // just for the loch range. + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_HICH); + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AF); + m_aStylesAssoc.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont))); + } + + // FIXME: this may be a tad expensive... but the charset needs to be + // consistent with what wwFont::WriteRtf() does + sw::util::FontMapExport aTmp(rFont.GetFamilyName()); + sal_uInt8 nWindowsCharset = sw::ms::rtl_TextEncodingToWinCharsetRTF( + aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet()); + m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset)); + if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW) + m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding()); +} + +void RtfAttributeOutput::CharFontSize(const SvxFontHeightItem& rFontSize) +{ + switch (rFontSize.Which()) + { + case RES_CHRATR_FONTSIZE: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FS); + m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10)); + break; + case RES_CHRATR_CJK_FONTSIZE: + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_FS); + m_aStylesAssoc.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10)); + break; + case RES_CHRATR_CTL_FONTSIZE: + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AFS); + m_aStylesAssoc.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10)); + break; + } +} + +void RtfAttributeOutput::CharKerning(const SvxKerningItem& rKerning) +{ + // in quarter points then in twips + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPND); + m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5)); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPNDTW); + m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue())); +} + +void RtfAttributeOutput::CharLanguage(const SvxLanguageItem& rLanguage) +{ + switch (rLanguage.Which()) + { + case RES_CHRATR_LANGUAGE: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LANG); + m_aStyles.append( + static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage()))); + break; + case RES_CHRATR_CJK_LANGUAGE: + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_LANGFE); + m_aStylesAssoc.append( + static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage()))); + break; + case RES_CHRATR_CTL_LANGUAGE: + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_ALANG); + m_aStylesAssoc.append( + static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage()))); + break; + } +} + +void RtfAttributeOutput::CharPosture(const SvxPostureItem& rPosture) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_I); + if (rPosture.GetPosture() == ITALIC_NONE) + m_aStyles.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharShadow(const SvxShadowedItem& rShadow) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SHAD); + if (!rShadow.GetValue()) + m_aStyles.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharUnderline(const SvxUnderlineItem& rUnderline) +{ + const char* pStr = nullptr; + const SfxPoolItem* pItem = m_rExport.HasItem(RES_CHRATR_WORDLINEMODE); + bool bWord = false; + if (pItem) + bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue(); + switch (rUnderline.GetLineStyle()) + { + case LINESTYLE_SINGLE: + pStr = bWord ? OOO_STRING_SVTOOLS_RTF_ULW : OOO_STRING_SVTOOLS_RTF_UL; + break; + case LINESTYLE_DOUBLE: + pStr = OOO_STRING_SVTOOLS_RTF_ULDB; + break; + case LINESTYLE_NONE: + pStr = OOO_STRING_SVTOOLS_RTF_ULNONE; + break; + case LINESTYLE_DOTTED: + pStr = OOO_STRING_SVTOOLS_RTF_ULD; + break; + case LINESTYLE_DASH: + pStr = OOO_STRING_SVTOOLS_RTF_ULDASH; + break; + case LINESTYLE_DASHDOT: + pStr = OOO_STRING_SVTOOLS_RTF_ULDASHD; + break; + case LINESTYLE_DASHDOTDOT: + pStr = OOO_STRING_SVTOOLS_RTF_ULDASHDD; + break; + case LINESTYLE_BOLD: + pStr = OOO_STRING_SVTOOLS_RTF_ULTH; + break; + case LINESTYLE_WAVE: + pStr = OOO_STRING_SVTOOLS_RTF_ULWAVE; + break; + case LINESTYLE_BOLDDOTTED: + pStr = OOO_STRING_SVTOOLS_RTF_ULTHD; + break; + case LINESTYLE_BOLDDASH: + pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASH; + break; + case LINESTYLE_LONGDASH: + pStr = OOO_STRING_SVTOOLS_RTF_ULLDASH; + break; + case LINESTYLE_BOLDLONGDASH: + pStr = OOO_STRING_SVTOOLS_RTF_ULTHLDASH; + break; + case LINESTYLE_BOLDDASHDOT: + pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHD; + break; + case LINESTYLE_BOLDDASHDOTDOT: + pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHDD; + break; + case LINESTYLE_BOLDWAVE: + pStr = OOO_STRING_SVTOOLS_RTF_ULHWAVE; + break; + case LINESTYLE_DOUBLEWAVE: + pStr = OOO_STRING_SVTOOLS_RTF_ULULDBWAVE; + break; + default: + break; + } + + if (pStr) + { + m_aStyles.append(pStr); + // NEEDSWORK looks like here rUnderline.GetColor() is always black, + // even if the color in the odt is for example green... + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ULC); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor()))); + } +} + +void RtfAttributeOutput::CharWeight(const SvxWeightItem& rWeight) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_B); + if (rWeight.GetWeight() != WEIGHT_BOLD) + m_aStyles.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharAutoKern(const SvxAutoKernItem& rAutoKern) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KERNING); + m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0)); +} + +void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ANIMTEXT); + m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0)); +} + +void RtfAttributeOutput::CharBackground(const SvxBrushItem& rBrush) +{ + if (!rBrush.GetColor().GetTransparency()) + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHCBPAT); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor()))); + } +} + +void RtfAttributeOutput::CharFontCJK(const SvxFontItem& rFont) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_DBCH); + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AF); + m_aStylesAssoc.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont))); +} + +void RtfAttributeOutput::CharFontSizeCJK(const SvxFontHeightItem& rFontSize) +{ + CharFontSize(rFontSize); +} + +void RtfAttributeOutput::CharLanguageCJK(const SvxLanguageItem& rLanguageItem) +{ + CharLanguage(rLanguageItem); +} + +void RtfAttributeOutput::CharPostureCJK(const SvxPostureItem& rPosture) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_I); + if (rPosture.GetPosture() == ITALIC_NONE) + m_aStylesAssoc.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharWeightCJK(const SvxWeightItem& rWeight) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_B); + if (rWeight.GetWeight() != WEIGHT_BOLD) + m_aStylesAssoc.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharFontCTL(const SvxFontItem& rFont) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_DBCH); + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AF); + m_aStylesAssoc.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont))); +} + +void RtfAttributeOutput::CharFontSizeCTL(const SvxFontHeightItem& rFontSize) +{ + CharFontSize(rFontSize); +} + +void RtfAttributeOutput::CharLanguageCTL(const SvxLanguageItem& rLanguageItem) +{ + CharLanguage(rLanguageItem); +} + +void RtfAttributeOutput::CharPostureCTL(const SvxPostureItem& rPosture) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AI); + if (rPosture.GetPosture() == ITALIC_NONE) + m_aStylesAssoc.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharWeightCTL(const SvxWeightItem& rWeight) +{ + m_aStylesAssoc.append(OOO_STRING_SVTOOLS_RTF_AB); + if (rWeight.GetWeight() != WEIGHT_BOLD) + m_aStylesAssoc.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharBidiRTL(const SfxPoolItem& /*rItem*/) {} + +void RtfAttributeOutput::CharIdctHint(const SfxPoolItem& /*rItem*/) {} + +void RtfAttributeOutput::CharRotate(const SvxCharRotateItem& rRotate) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HORZVERT); + m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0)); +} + +void RtfAttributeOutput::CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark) +{ + FontEmphasisMark v = rEmphasisMark.GetEmphasisMark(); + if (v == FontEmphasisMark::NONE) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCNONE); + else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove)) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCDOT); + else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove)) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCOMMA); + else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove)) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCIRCLE); + else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow)) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCUNDERDOT); +} + +void RtfAttributeOutput::CharTwoLines(const SvxTwoLinesItem& rTwoLines) +{ + if (rTwoLines.GetValue()) + { + sal_Unicode cStart = rTwoLines.GetStartBracket(); + sal_Unicode cEnd = rTwoLines.GetEndBracket(); + + sal_uInt16 nType; + if (!cStart && !cEnd) + nType = 0; + else if ('{' == cStart || '}' == cEnd) + nType = 4; + else if ('<' == cStart || '>' == cEnd) + nType = 3; + else if ('[' == cStart || ']' == cEnd) + nType = 2; + else // all other kind of brackets + nType = 1; + + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TWOINONE); + m_aStyles.append(static_cast<sal_Int32>(nType)); + } +} + +void RtfAttributeOutput::CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHARSCALEX); + m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue())); +} + +void RtfAttributeOutput::CharRelief(const SvxCharReliefItem& rRelief) +{ + const char* pStr; + switch (rRelief.GetValue()) + { + case FontRelief::Embossed: + pStr = OOO_STRING_SVTOOLS_RTF_EMBO; + break; + case FontRelief::Engraved: + pStr = OOO_STRING_SVTOOLS_RTF_IMPR; + break; + default: + pStr = nullptr; + break; + } + + if (pStr) + m_aStyles.append(pStr); +} + +void RtfAttributeOutput::CharHidden(const SvxCharHiddenItem& rHidden) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_V); + if (!rHidden.GetValue()) + m_aStyles.append(sal_Int32(0)); +} + +void RtfAttributeOutput::CharBorder(const editeng::SvxBorderLine* pAllBorder, + const sal_uInt16 nDist, const bool bShadow) +{ + m_aStyles.append( + OutBorderLine(m_rExport, pAllBorder, OOO_STRING_SVTOOLS_RTF_CHBRDR, nDist, + bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE)); +} + +void RtfAttributeOutput::CharHighlight(const SvxBrushItem& rBrush) +{ + if (!rBrush.GetColor().GetTransparency()) + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HIGHLIGHT); + m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor()))); + } +} + +void RtfAttributeOutput::TextINetFormat(const SwFormatINetFormat& rURL) +{ + if (!rURL.GetValue().isEmpty()) + { + const SwCharFormat* pFormat; + const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat(); + + if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat())) + { + sal_uInt16 nStyle = m_rExport.GetId(pFormat); + OString* pString = m_rExport.GetStyle(nStyle); + if (pString) + m_aStyles.append(*pString); + } + } +} + +void RtfAttributeOutput::TextCharFormat(const SwFormatCharFormat& rCharFormat) +{ + sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat()); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CS); + m_aStyles.append(static_cast<sal_Int32>(nStyle)); + OString* pString = m_rExport.GetStyle(nStyle); + if (pString) + m_aStyles.append(*pString); +} + +void RtfAttributeOutput::WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote) +{ + if (rFootnote.GetNumStr().isEmpty()) + m_aRun->append(OOO_STRING_SVTOOLS_RTF_CHFTN); + else + m_aRun->append( + msfilter::rtfutil::OutString(rFootnote.GetNumStr(), m_rExport.GetCurrentEncoding())); +} + +void RtfAttributeOutput::TextFootnote_Impl(const SwFormatFootnote& rFootnote) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER " "); + EndRunProperties(nullptr); + m_aRun->append(' '); + WriteTextFootnoteNumStr(rFootnote); + m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FOOTNOTE); + if (rFootnote.IsEndNote() || m_rExport.m_pDoc->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER) + m_aRun->append(OOO_STRING_SVTOOLS_RTF_FTNALT); + m_aRun->append(' '); + WriteTextFootnoteNumStr(rFootnote); + + /* + * The footnote contains a whole paragraph, so we have to: + * 1) Reset, then later restore the contents of our run buffer and run state. + * 2) Buffer the output of the whole paragraph, as we do so for section headers already. + */ + const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode(); + RtfStringBuffer aRun = m_aRun; + m_aRun.clear(); + bool bInRunOrig = m_bInRun; + m_bInRun = false; + bool bSingleEmptyRunOrig = m_bSingleEmptyRun; + m_bSingleEmptyRun = false; + m_bBufferSectionHeaders = true; + m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(), + !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN); + m_bBufferSectionHeaders = false; + m_bInRun = bInRunOrig; + m_bSingleEmptyRun = bSingleEmptyRunOrig; + m_aRun = aRun; + m_aRun->append(m_aSectionHeaders.makeStringAndClear()); + + m_aRun->append("}"); + m_aRun->append("}"); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SL); + m_aStyles.append(static_cast<sal_Int32>(nSpace)); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SLMULT); + m_aStyles.append(static_cast<sal_Int32>(nMulti)); +} + +void RtfAttributeOutput::ParaAdjust(const SvxAdjustItem& rAdjust) +{ + switch (rAdjust.GetAdjust()) + { + case SvxAdjust::Left: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QL); + break; + case SvxAdjust::Right: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QR); + break; + case SvxAdjust::BlockLine: + case SvxAdjust::Block: + if (rAdjust.GetLastBlock() == SvxAdjust::Block) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QD); + else + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QJ); + break; + case SvxAdjust::Center: + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QC); + break; + default: + break; + } +} + +void RtfAttributeOutput::ParaSplit(const SvxFormatSplitItem& rSplit) +{ + if (!rSplit.GetValue()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEP); +} + +void RtfAttributeOutput::ParaWidows(const SvxWidowsItem& rWidows) +{ + if (rWidows.GetValue()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_WIDCTLPAR); + else + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOWIDCTLPAR); +} + +void RtfAttributeOutput::ParaTabStop(const SvxTabStopItem& rTabStop) +{ + long nOffset = 0; + // Tabs are absolute by default. + if (m_rExport.m_pDoc->getIDocumentSettingAccess().get( + DocumentSettingId::TABS_RELATIVE_TO_INDENT)) + nOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft(); + + for (sal_uInt16 n = 0; n < rTabStop.Count(); n++) + { + const SvxTabStop& rTS = rTabStop[n]; + if (SvxTabAdjust::Default != rTS.GetAdjustment()) + { + const char* pFill = nullptr; + switch (rTS.GetFill()) + { + case cDfltFillChar: + break; + + case '.': + pFill = OOO_STRING_SVTOOLS_RTF_TLDOT; + break; + case '_': + pFill = OOO_STRING_SVTOOLS_RTF_TLUL; + break; + case '-': + pFill = OOO_STRING_SVTOOLS_RTF_TLTH; + break; + case '=': + pFill = OOO_STRING_SVTOOLS_RTF_TLEQ; + break; + default: + break; + } + if (pFill) + m_aStyles.append(pFill); + + const char* pAdjStr = nullptr; + switch (rTS.GetAdjustment()) + { + case SvxTabAdjust::Right: + pAdjStr = OOO_STRING_SVTOOLS_RTF_TQR; + break; + case SvxTabAdjust::Decimal: + pAdjStr = OOO_STRING_SVTOOLS_RTF_TQDEC; + break; + case SvxTabAdjust::Center: + pAdjStr = OOO_STRING_SVTOOLS_RTF_TQC; + break; + default: + break; + } + if (pAdjStr) + m_aStyles.append(pAdjStr); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TX); + m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset)); + } + else + { + m_aTabStop.append(OOO_STRING_SVTOOLS_RTF_DEFTAB); + m_aTabStop.append(rTabStop[0].GetTabPos()); + } + } +} + +void RtfAttributeOutput::ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone) +{ + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HYPHPAR); + m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen())); +} + +void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl, + sal_Int32 nNumId) +{ + if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd) + return; + + const SwNumRule* pRule = pTextNd->GetNumRule(); + + if (!pRule || !pTextNd->IsInList()) + return; + + SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL, + "sw.rtf", "text node does not have valid list level"); + + const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl); + if (!pFormat) + pFormat = &pRule->Get(nLvl); + + const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet(); + + m_aStyles.append('{'); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LISTTEXT); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PARD); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PLAIN); + m_aStyles.append(' '); + + SvxLRSpaceItem aLR(rNdSet.Get(RES_LR_SPACE)); + aLR.SetTextLeft(aLR.GetTextLeft() + pFormat->GetIndentAt()); + aLR.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow + + sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat()); + OString* pString = m_rExport.GetStyle(nStyle); + if (pString) + m_aStyles.append(*pString); + + { + OUString sText; + if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType() + || SVX_NUM_BITMAP == pFormat->GetNumberingType()) + sText = OUString(pFormat->GetBulletChar()); + else + sText = pTextNd->GetNumString(); + + if (!sText.isEmpty()) + { + m_aStyles.append(' '); + m_aStyles.append(msfilter::rtfutil::OutString(sText, m_rExport.GetDefaultEncoding())); + } + + if (OUTLINE_RULE != pRule->GetRuleType()) + { + if (!sText.isEmpty()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB); + m_aStyles.append('}'); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL); + if (nLvl > 8) // RTF knows only 9 levels + { + m_aStyles.append(sal_Int32(8)); + m_aStyles.append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SOUTLVL); + m_aStyles.append(nLvl); + m_aStyles.append('}'); + } + else + m_aStyles.append(nLvl); + } + else + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB "}"); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LS); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1); + m_aStyles.append(' '); + } + FormatLRSpace(aLR); +} + +void RtfAttributeOutput::ParaScriptSpace(const SfxBoolItem& rScriptSpace) +{ + if (!rScriptSpace.GetValue()) + return; + + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ASPALPHA); +} + +void RtfAttributeOutput::ParaHangingPunctuation(const SfxBoolItem& /*rItem*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::ParaForbiddenRules(const SfxBoolItem& /*rItem*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::ParaVerticalAlign(const SvxParaVertAlignItem& rAlign) +{ + const char* pStr; + switch (rAlign.GetValue()) + { + case SvxParaVertAlignItem::Align::Top: + pStr = OOO_STRING_SVTOOLS_RTF_FAHANG; + break; + case SvxParaVertAlignItem::Align::Bottom: + pStr = OOO_STRING_SVTOOLS_RTF_FAVAR; + break; + case SvxParaVertAlignItem::Align::Center: + pStr = OOO_STRING_SVTOOLS_RTF_FACENTER; + break; + case SvxParaVertAlignItem::Align::Baseline: + pStr = OOO_STRING_SVTOOLS_RTF_FAROMAN; + break; + + default: + pStr = OOO_STRING_SVTOOLS_RTF_FAAUTO; + break; + } + m_aStyles.append(pStr); +} + +void RtfAttributeOutput::ParaSnapToGrid(const SvxParaGridItem& /*rGrid*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::FormatFrameSize(const SwFormatFrameSize& rSize) +{ + if (m_rExport.m_bOutPageDescs) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGWSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth())); + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGHSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight())); + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + } +} + +void RtfAttributeOutput::FormatPaperBin(const SvxPaperBinItem& /*rItem*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::FormatLRSpace(const SvxLRSpaceItem& rLRSpace) +{ + if (!m_rExport.m_bOutFlyFrameAttrs) + { + if (m_rExport.m_bOutPageDescs) + { + if (rLRSpace.GetLeft()) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetLeft())); + } + if (rLRSpace.GetRight()) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetRight())); + } + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + } + else + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI); + m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft())); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI); + m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight())); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN); + m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft())); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN); + m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight())); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI); + m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset())); + } + } + else if (m_rExport.GetRTFFlySyntax()) + { + // Wrap: top and bottom spacing, convert from twips to EMUs. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dxWrapDistLeft", OString::number(rLRSpace.GetLeft() * 635))); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dxWrapDistRight", OString::number(rLRSpace.GetRight() * 635))); + } +} + +void RtfAttributeOutput::FormatULSpace(const SvxULSpaceItem& rULSpace) +{ + if (!m_rExport.m_bOutFlyFrameAttrs) + { + if (m_rExport.m_bOutPageDescs) + { + OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible"); + if (!m_rExport.GetCurItemSet()) + return; + + // If we export a follow page format, then our doc model has + // separate header/footer distances for the first page and the + // follow pages, but Word can have only a single distance. In case + // the two values differ, work with the value from the first page + // format to be in sync with the import. + sw::util::HdFtDistanceGlue aDistances(m_rExport.GetFirstPageItemSet() + ? *m_rExport.GetFirstPageItemSet() + : *m_rExport.GetCurItemSet()); + + if (aDistances.dyaTop) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGTSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.dyaTop)); + } + if (aDistances.HasHeader()) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_HEADERY); + m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.dyaHdrTop)); + } + + if (aDistances.dyaBottom) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN); + m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.dyaBottom)); + } + if (aDistances.HasFooter()) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_FOOTERY); + m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.dyaHdrBottom)); + } + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + } + else + { + // Spacing before. + if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper()) + m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "1"); + else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1) + { + m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "0"); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB); + m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper())); + } + else + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB); + m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper())); + } + m_bParaBeforeAutoSpacing = false; + + // Spacing after. + if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower()) + m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "1"); + else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1) + { + m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "0"); + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA); + m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower())); + } + else + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA); + m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower())); + } + m_bParaAfterAutoSpacing = false; + + // Contextual spacing. + if (rULSpace.GetContext()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CONTEXTUALSPACE); + } + } + else if (m_rExport.GetRTFFlySyntax()) + { + // Wrap: top and bottom spacing, convert from twips to EMUs. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dyWrapDistTop", OString::number(rULSpace.GetUpper() * 635))); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dyWrapDistBottom", OString::number(rULSpace.GetLower() * 635))); + } +} + +void RtfAttributeOutput::FormatSurround(const SwFormatSurround& rSurround) +{ + if (m_rExport.m_bOutFlyFrameAttrs && !m_rExport.GetRTFFlySyntax()) + { + css::text::WrapTextMode eSurround = rSurround.GetSurround(); + bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround; + if (bGold) + eSurround = css::text::WrapTextMode_PARALLEL; + RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround)); + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYMAINCNT); + m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue())); + } + else if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()) + { + // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings. + sal_Int32 nWr = -1; + std::optional<sal_Int32> oWrk; + switch (rSurround.GetValue()) + { + case css::text::WrapTextMode_NONE: + nWr = 1; // top and bottom + break; + case css::text::WrapTextMode_THROUGH: + nWr = 3; // none + break; + case css::text::WrapTextMode_PARALLEL: + nWr = 2; // around + oWrk = 0; // both sides + break; + case css::text::WrapTextMode_DYNAMIC: + default: + nWr = 2; // around + oWrk = 3; // largest + break; + } + + if (rSurround.IsContour()) + nWr = 4; // tight + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPWR); + m_rExport.OutLong(nWr); + if (oWrk) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPWRK); + m_rExport.OutLong(*oWrk); + } + } +} + +void RtfAttributeOutput::FormatVertOrientation(const SwFormatVertOrient& rFlyVert) +{ + if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()) + { + switch (rFlyVert.GetRelationOrient()) + { + case text::RelOrientation::PAGE_FRAME: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posrelv", OString::number(1))); + break; + default: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posrelv", OString::number(2))); + m_rExport.Strm() + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPBYPARA) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE); + break; + } + + switch (rFlyVert.GetVertOrient()) + { + case text::VertOrientation::TOP: + case text::VertOrientation::LINE_TOP: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posv", OString::number(1))); + break; + case text::VertOrientation::BOTTOM: + case text::VertOrientation::LINE_BOTTOM: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posv", OString::number(3))); + break; + case text::VertOrientation::CENTER: + case text::VertOrientation::LINE_CENTER: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posv", OString::number(2))); + break; + default: + break; + } + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPTOP); + m_rExport.OutLong(rFlyVert.GetPos()); + if (m_pFlyFrameSize) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM); + m_rExport.OutLong(rFlyVert.GetPos() + m_pFlyFrameSize->Height()); + } + } +} + +void RtfAttributeOutput::FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori) +{ + if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()) + { + switch (rFlyHori.GetRelationOrient()) + { + case text::RelOrientation::PAGE_FRAME: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posrelh", OString::number(1))); + break; + default: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posrelh", OString::number(2))); + m_rExport.Strm() + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPBXCOLUMN) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE); + break; + } + + switch (rFlyHori.GetHoriOrient()) + { + case text::HoriOrientation::LEFT: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posh", OString::number(1))); + break; + case text::HoriOrientation::CENTER: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posh", OString::number(2))); + break; + case text::HoriOrientation::RIGHT: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("posh", OString::number(3))); + break; + default: + break; + } + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPLEFT); + m_rExport.OutLong(rFlyHori.GetPos()); + if (m_pFlyFrameSize) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SHPRIGHT); + m_rExport.OutLong(rFlyHori.GetPos() + m_pFlyFrameSize->Width()); + } + } +} + +void RtfAttributeOutput::FormatAnchor(const SwFormatAnchor& rAnchor) +{ + if (!m_rExport.GetRTFFlySyntax()) + { + RndStdIds eId = rAnchor.GetAnchorId(); + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYANCHOR); + m_aRunText->append(static_cast<sal_Int32>(eId)); + switch (eId) + { + case RndStdIds::FLY_AT_PAGE: + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYPAGE); + m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum())); + break; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AS_CHAR: + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYCNTNT); + break; + default: + break; + } + } +} + +void RtfAttributeOutput::FormatBackground(const SvxBrushItem& rBrush) +{ + if (m_rExport.GetRTFFlySyntax()) + { + const Color& rColor = rBrush.GetColor(); + // We in fact need RGB to BGR, but the transformation is symmetric. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "fillColor", OString::number(wwUtility::RGBToBGR(rColor)))); + } + else if (!rBrush.GetColor().GetTransparency()) + { + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CBPAT); + m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor()))); + } +} + +void RtfAttributeOutput::FormatFillStyle(const XFillStyleItem& rFillStyle) +{ + m_oFillStyle = rFillStyle.GetValue(); +} + +void RtfAttributeOutput::FormatFillGradient(const XFillGradientItem& rFillGradient) +{ + if (*m_oFillStyle == drawing::FillStyle_GRADIENT) + { + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "fillType", OString::number(7))); // Shade using the fillAngle + + const XGradient& rGradient = rFillGradient.GetGradientValue(); + const Color& rStartColor = rGradient.GetStartColor(); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "fillBackColor", OString::number(wwUtility::RGBToBGR(rStartColor)))); + + const Color& rEndColor = rGradient.GetEndColor(); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "fillColor", OString::number(wwUtility::RGBToBGR(rEndColor)))); + + switch (rGradient.GetGradientStyle()) + { + case css::awt::GradientStyle_LINEAR: + break; + case css::awt::GradientStyle_AXIAL: + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("fillFocus", OString::number(50))); + break; + case css::awt::GradientStyle_RADIAL: + case css::awt::GradientStyle_ELLIPTICAL: + case css::awt::GradientStyle_SQUARE: + case css::awt::GradientStyle_RECT: + default: + break; + } + } +} + +void RtfAttributeOutput::FormatBox(const SvxBoxItem& rBox) +{ + static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT }; + static const char* aBorderNames[] + = { OOO_STRING_SVTOOLS_RTF_BRDRT, OOO_STRING_SVTOOLS_RTF_BRDRL, + OOO_STRING_SVTOOLS_RTF_BRDRB, OOO_STRING_SVTOOLS_RTF_BRDRR }; + + sal_uInt16 const nDist = rBox.GetSmallestDistance(); + + if (m_rExport.GetRTFFlySyntax()) + { + // Borders: spacing to contents, convert from twips to EMUs. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dxTextLeft", OString::number(rBox.GetDistance(SvxBoxItemLine::LEFT) * 635))); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dyTextTop", OString::number(rBox.GetDistance(SvxBoxItemLine::TOP) * 635))); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dxTextRight", OString::number(rBox.GetDistance(SvxBoxItemLine::RIGHT) * 635))); + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "dyTextBottom", OString::number(rBox.GetDistance(SvxBoxItemLine::BOTTOM) * 635))); + + const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT); + const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT); + const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP); + const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM); + if (pLeft && pRight && pTop && pBottom && *pLeft == *pRight && *pLeft == *pTop + && *pLeft == *pBottom) + { + const Color& rColor = pTop->GetColor(); + // We in fact need RGB to BGR, but the transformation is symmetric. + m_aFlyProperties.push_back(std::make_pair<OString, OString>( + "lineColor", OString::number(wwUtility::RGBToBGR(rColor)))); + + if (pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE) + { + double const fConverted(editeng::ConvertBorderWidthToWord( + pTop->GetBorderLineStyle(), pTop->GetWidth())); + sal_Int32 nWidth = fConverted * 635; // Twips -> EMUs + m_aFlyProperties.push_back( + std::make_pair<OString, OString>("lineWidth", OString::number(nWidth))); + } + else + // No border: no line. + m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0")); + } + + return; + } + + if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight() + && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft() + && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP) + && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT) + && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM) + && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT)) + m_aSectionBreaks.append( + OutBorderLine(m_rExport, rBox.GetTop(), OOO_STRING_SVTOOLS_RTF_BOX, nDist)); + else + { + SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE; + if (const SfxPoolItem* pItem = GetExport().HasItem(RES_SHADOW)) + eShadowLocation = static_cast<const SvxShadowItem*>(pItem)->GetLocation(); + + const SvxBoxItemLine* pBrd = aBorders; + const char** pBrdNms = aBorderNames; + for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms) + { + if (const editeng::SvxBorderLine* pLn = rBox.GetLine(*pBrd)) + { + m_aSectionBreaks.append(OutBorderLine(m_rExport, pLn, *pBrdNms, + rBox.GetDistance(*pBrd), eShadowLocation)); + } + } + } + + if (!m_bBufferSectionBreaks) + m_aStyles.append(m_aSectionBreaks.makeStringAndClear()); +} + +void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, + SwTwips nPageSize) +{ + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLS); + m_rExport.OutLong(nCols); + + if (rCol.GetLineAdj() != COLADJ_NONE) + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LINEBETCOL); + + if (bEven) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLSX); + m_rExport.OutLong(rCol.GetGutterWidth(true)); + } + else + { + const SwColumns& rColumns = rCol.GetColumns(); + for (sal_uInt16 n = 0; n < nCols;) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLNO); + m_rExport.OutLong(n + 1); + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLW); + m_rExport.OutLong(rCol.CalcPrtColWidth(n, nPageSize)); + + if (++n != nCols) + { + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_COLSR); + m_rExport.OutLong(rColumns[n - 1].GetRight() + rColumns[n].GetLeft()); + } + } + } +} + +void RtfAttributeOutput::FormatKeep(const SvxFormatKeepItem& rItem) +{ + if (rItem.GetValue()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEPN); +} + +void RtfAttributeOutput::FormatTextGrid(const SwTextGridItem& /*rGrid*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::FormatLineNumbering(const SwFormatLineNumber& rNumbering) +{ + if (!rNumbering.IsCount()) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOLINE); +} + +void RtfAttributeOutput::FormatFrameDirection(const SvxFrameDirectionItem& rDirection) +{ + SvxFrameDirection nDir = rDirection.GetValue(); + if (nDir == SvxFrameDirection::Environment) + nDir = GetExport().GetDefaultFrameDirection(); + + if (m_rExport.m_bOutPageDescs) + { + if (nDir == SvxFrameDirection::Vertical_RL_TB) + { + m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_STEXTFLOW); + m_aSectionBreaks.append(static_cast<sal_Int32>(1)); + if (!m_bBufferSectionBreaks) + m_rExport.Strm().WriteOString(m_aSectionBreaks.makeStringAndClear()); + } + return; + } + + if (m_rExport.GetRTFFlySyntax()) + { + if (nDir == SvxFrameDirection::Vertical_RL_TB) + { + // Top to bottom non-ASCII font + m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "3")); + } + else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT) + { + // Bottom to top non-ASCII font + m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "2")); + } + return; + } + + if (nDir == SvxFrameDirection::Horizontal_RL_TB) + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RTLPAR); + else + m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LTRPAR); +} + +void RtfAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem) +{ + const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag(); + for (const auto& rValue : rMap) + { + if (rValue.first == "ParaTopMarginBeforeAutoSpacing") + { + m_bParaBeforeAutoSpacing = true; + rValue.second >>= m_nParaBeforeSpacing; + m_nParaBeforeSpacing = convertMm100ToTwip(m_nParaBeforeSpacing); + } + else if (rValue.first == "ParaBottomMarginAfterAutoSpacing") + { + m_bParaAfterAutoSpacing = true; + rValue.second >>= m_nParaAfterSpacing; + m_nParaAfterSpacing = convertMm100ToTwip(m_nParaAfterSpacing); + } + } +} + +void RtfAttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/) {} + +void RtfAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/) {} + +void RtfAttributeOutput::WriteExpand(const SwField* pField) +{ + OUString sCmd; // for optional Parameters + switch (pField->GetTyp()->Which()) + { + //#i119803# Export user field for RTF filter + case SwFieldIds::User: + sCmd = pField->GetTyp()->GetName(); + m_rExport.OutputField(pField, ww::eNONE, sCmd); + break; + default: + m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd); + break; + } +} + +void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::HiddenField(const SwField& /*rField*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/, + const OUString& /*rCmd*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfAttributeOutput::PostitField(const SwField* pField) +{ + const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField); + + OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8); + auto it = m_rOpenedAnnotationMarksIds.find(aName); + if (it != m_rOpenedAnnotationMarksIds.end()) + { + // In case this field is inside annotation marks, we want to write the + // annotation itself after the annotation mark is closed, not here. + m_aPostitFields[it->second] = &rPField; + return; + } + + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNID " "); + m_aRunText->append(OUStringToOString(rPField.GetInitials(), m_rExport.GetCurrentEncoding())); + m_aRunText->append("}"); + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNAUTHOR " "); + m_aRunText->append(OUStringToOString(rPField.GetPar1(), m_rExport.GetCurrentEncoding())); + m_aRunText->append("}"); + m_aRunText->append(OOO_STRING_SVTOOLS_RTF_CHATN); + + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ANNOTATION); + + if (m_nCurrentAnnotationMarkId != -1) + { + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNREF " "); + m_aRunText->append(m_nCurrentAnnotationMarkId); + m_aRunText->append('}'); + } + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNDATE " "); + m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime()))); + m_aRunText->append('}'); + if (const OutlinerParaObject* pObject = rPField.GetTextObject()) + m_rExport.SdrExporter().WriteOutliner(*pObject, TXT_ATN); + m_aRunText->append('}'); +} + +bool RtfAttributeOutput::DropdownField(const SwField* /*pField*/) +{ + // this is handled in OutputFlyFrame_Impl() + return true; +} + +bool RtfAttributeOutput::PlaceholderField(const SwField* pField) +{ + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST + " MACROBUTTON None "); + RunText(pField->GetPar1()); + m_aRunText->append("}}"); + return false; // do not expand +} + +RtfAttributeOutput::RtfAttributeOutput(RtfExport& rExport) + : AttributeOutputBase("") // ConvertURL isn't used now in RTF output + , m_rExport(rExport) + , m_pPrevPageDesc(nullptr) + , m_nStyleId(0) + , m_nListId(0) + , m_bIsRTL(false) + , m_nScript(i18n::ScriptType::LATIN) + , m_bControlLtrRtl(false) + , m_nNextAnnotationMarkId(0) + , m_nCurrentAnnotationMarkId(-1) + , m_bTableCellOpen(false) + , m_nTableDepth(0) + , m_bTableAfterCell(false) + , m_nColBreakNeeded(false) + , m_bBufferSectionBreaks(false) + , m_bBufferSectionHeaders(false) + , m_bLastTable(true) + , m_bWroteCellInfo(false) + , m_bTableRowEnded(false) + , m_bIsBeforeFirstParagraph(true) + , m_bSingleEmptyRun(false) + , m_bInRun(false) + , m_pFlyFrameSize(nullptr) + , m_bParaBeforeAutoSpacing(false) + , m_nParaBeforeSpacing(0) + , m_bParaAfterAutoSpacing(false) + , m_nParaAfterSpacing(0) +{ +} + +RtfAttributeOutput::~RtfAttributeOutput() = default; + +MSWordExportBase& RtfAttributeOutput::GetExport() { return m_rExport; } + +// These are used by wwFont::WriteRtf() + +/// Start the font. +void RtfAttributeOutput::StartFont(const OUString& rFamilyName) const +{ + // write the font name hex-encoded, but without Unicode - Word at least + // cannot read *both* Unicode and fallback as written by OutString + m_rExport.Strm().WriteCharPtr( + msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false).getStr()); +} + +/// End the font. +void RtfAttributeOutput::EndFont() const +{ + m_rExport.Strm().WriteCharPtr(";}"); + m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding()); +} + +/// Alternate name for the font. +void RtfAttributeOutput::FontAlternateName(const OUString& rName) const +{ + m_rExport.Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FALT) + .WriteChar(' '); + // write the font name hex-encoded, but without Unicode - Word at least + // cannot read *both* Unicode and fallback as written by OutString + m_rExport.Strm() + .WriteCharPtr( + msfilter::rtfutil::OutString(rName, m_rExport.GetCurrentEncoding(), false).getStr()) + .WriteChar('}'); +} + +/// Font charset. +void RtfAttributeOutput::FontCharset(sal_uInt8 nCharSet) const +{ + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FCHARSET); + m_rExport.OutULong(nCharSet); + m_rExport.Strm().WriteChar(' '); + m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet)); +} + +/// Font family. +void RtfAttributeOutput::FontFamilyType(FontFamily eFamily, const wwFont& rFont) const +{ + m_rExport.Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_F); + + const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL; + switch (eFamily) + { + case FAMILY_ROMAN: + pStr = OOO_STRING_SVTOOLS_RTF_FROMAN; + break; + case FAMILY_SWISS: + pStr = OOO_STRING_SVTOOLS_RTF_FSWISS; + break; + case FAMILY_MODERN: + pStr = OOO_STRING_SVTOOLS_RTF_FMODERN; + break; + case FAMILY_SCRIPT: + pStr = OOO_STRING_SVTOOLS_RTF_FSCRIPT; + break; + case FAMILY_DECORATIVE: + pStr = OOO_STRING_SVTOOLS_RTF_FDECOR; + break; + default: + break; + } + m_rExport.OutULong(m_rExport.m_aFontHelper.GetId(rFont)).WriteCharPtr(pStr); +} + +/// Font pitch. +void RtfAttributeOutput::FontPitchType(FontPitch ePitch) const +{ + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FPRQ); + + sal_uInt16 nVal = 0; + switch (ePitch) + { + case PITCH_FIXED: + nVal = 1; + break; + case PITCH_VARIABLE: + nVal = 2; + break; + default: + break; + } + m_rExport.OutULong(nVal); +} + +static void lcl_AppendSP(OStringBuffer& rBuffer, const char cName[], const OUString& rValue, + const RtfExport& rExport) +{ + rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{" + rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn " + rBuffer.append(cName); //"PropName" + rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " "); + // "}{ \sv " + rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding())); + rBuffer.append("}}"); +} + +static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig, + const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr, + const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize, + const RtfExport& rExport, SvStream* pStream = nullptr, + bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr) +{ + OStringBuffer aRet; + if (pBLIPType && nSize && pGraphicAry) + { + bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0; + + aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT); + + if (pFlyFrameFormat && bWritePicProp) + { + OUString sDescription = pFlyFrameFormat->GetObjDescription(); + //write picture properties - wzDescription at first + //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}" + aRet.append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_PICPROP); //"{\*\picprop + lcl_AppendSP(aRet, "wzDescription", sDescription, rExport); + OUString sName = pFlyFrameFormat->GetObjTitle(); + lcl_AppendSP(aRet, "wzName", sName, rExport); + + if (pAttrSet) + { + MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue(); + if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both) + // Mirror on the vertical axis is a horizontal flip. + lcl_AppendSP(aRet, "fFlipH", "1", rExport); + } + + aRet.append("}"); //"}" + } + + long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight()); + long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom()); + /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */ + if (!nXCroppedSize) + nXCroppedSize = 100; + if (!nYCroppedSize) + nYCroppedSize = 100; + + //Given the original size and taking cropping into account + //first, how much has the original been scaled to get the + //final rendered size + aRet.append(OOO_STRING_SVTOOLS_RTF_PICSCALEX); + aRet.append(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize)); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICSCALEY); + aRet.append(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize)); + + aRet.append(OOO_STRING_SVTOOLS_RTF_PICCROPL); + aRet.append(rCr.GetLeft()); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICCROPR); + aRet.append(rCr.GetRight()); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICCROPT); + aRet.append(rCr.GetTop()); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICCROPB); + aRet.append(rCr.GetBottom()); + + aRet.append(OOO_STRING_SVTOOLS_RTF_PICW); + aRet.append(static_cast<sal_Int32>(rMapped.Width())); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICH); + aRet.append(static_cast<sal_Int32>(rMapped.Height())); + + aRet.append(OOO_STRING_SVTOOLS_RTF_PICWGOAL); + aRet.append(static_cast<sal_Int32>(rOrig.Width())); + aRet.append(OOO_STRING_SVTOOLS_RTF_PICHGOAL); + aRet.append(static_cast<sal_Int32>(rOrig.Height())); + + aRet.append(pBLIPType); + if (bIsWMF) + { + aRet.append(sal_Int32(8)); + msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize); + } + aRet.append(SAL_NEWLINE_STRING); + if (pStream) + pStream->WriteOString(aRet.makeStringAndClear()); + if (pStream) + msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream); + else + aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize)); + aRet.append('}'); + if (pStream) + pStream->WriteOString(aRet.makeStringAndClear()); + } + return aRet.makeStringAndClear(); +} + +void RtfAttributeOutput::FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat, + SwOLENode& rOLENode, const Size& rSize) +{ + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT); + Size aSize(rOLENode.GetTwipSize()); + Size aRendered(aSize); + aRendered.setWidth(rSize.Width()); + aRendered.setHeight(rSize.Height()); + const Graphic* pGraphic = rOLENode.GetGraphic(); + Size aMapped(pGraphic->GetPrefSize()); + auto& rCr = static_cast<const SwCropGrf&>(rOLENode.GetAttr(RES_GRFATR_CROPGRF)); + const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP; + const sal_uInt8* pGraphicAry = nullptr; + SvMemoryStream aStream; + if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE) + SAL_WARN("sw.rtf", "failed to export the graphic"); + sal_uInt32 nSize = aStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, + pGraphicAry, nSize, m_rExport)); + m_aRunText->append("}"); // shppict + m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_NONSHPPICT); + pBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE; + SvMemoryStream aWmfStream; + if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE) + SAL_WARN("sw.rtf", "failed to export the graphic"); + nSize = aWmfStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData()); + m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, + pGraphicAry, nSize, m_rExport)); + m_aRunText->append("}"); // nonshppict +} + +bool RtfAttributeOutput::FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat, + SwOLENode& rOLENode, const Size& rSize) +{ + uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef()); + sal_Int64 nAspect = rOLENode.GetAspect(); + svt::EmbeddedObjectRef aObjRef(xObj, nAspect); + SvGlobalName aObjName(aObjRef->getClassID()); + + if (!SotExchange::IsMath(aObjName)) + return false; + + m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATH " "); + uno::Reference<util::XCloseable> xClosable = xObj->getComponent(); + if (!xClosable.is()) + return false; + // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class, + // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated + // to RTLD_GLOBAL, so most probably a gcc bug. + auto pBase + = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xClosable.get())); + assert(pBase != nullptr); + OStringBuffer aBuf; + if (pBase) + pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding()); + m_aRunText->append(aBuf.makeStringAndClear()); + // Replacement graphic. + m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATHPICT " "); + FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize); + m_aRunText->append("}"); // mmathPict + m_aRunText->append("}"); // mmath + + return true; +} + +void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode, + const Size& rSize) +{ + if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize)) + return; + + FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize); +} + +void RtfAttributeOutput::FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat, + const SwGrfNode* pGrfNode) +{ + SvMemoryStream aStream; + const sal_uInt8* pGraphicAry = nullptr; + sal_uInt32 nSize = 0; + + const Graphic& rGraphic(pGrfNode->GetGrf()); + + // If there is no graphic there is not much point in parsing it + if (rGraphic.GetType() == GraphicType::NONE) + return; + + ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF; + const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE; + + GfxLink aGraphicLink; + const char* pBLIPType = nullptr; + if (rGraphic.IsGfxLink()) + { + aGraphicLink = rGraphic.GetGfxLink(); + nSize = aGraphicLink.GetDataSize(); + pGraphicAry = aGraphicLink.GetData(); + switch (aGraphicLink.GetType()) + { + // #i15508# trying to add BMP type for better exports, need to check if this works + // checked, does not work. Also need to reset pGraphicAry to NULL to force conversion + // to PNG, else the BMP array will be used. + // It may work using direct DIB data, but that needs to be checked eventually + // + // #i15508# before GfxLinkType::NativeBmp was added the graphic data + // (to be hold in pGraphicAry) was not available; thus for now to stay + // compatible, keep it that way by assigning NULL value to pGraphicAry + case GfxLinkType::NativeBmp: + // pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP; + pGraphicAry = nullptr; + break; + + case GfxLinkType::NativeJpg: + pBLIPType = OOO_STRING_SVTOOLS_RTF_JPEGBLIP; + break; + case GfxLinkType::NativePng: + pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP; + break; + case GfxLinkType::NativeWmf: + pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP + : OOO_STRING_SVTOOLS_RTF_WMETAFILE; + break; + case GfxLinkType::NativeGif: + // GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here. + aConvertDestinationFormat = ConvertDataFormat::PNG; + pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP; + break; + default: + break; + } + } + + GraphicType eGraphicType = rGraphic.GetType(); + if (!pGraphicAry) + { + if (ERRCODE_NONE + == GraphicConverter::Export(aStream, rGraphic, + (eGraphicType == GraphicType::Bitmap) + ? ConvertDataFormat::PNG + : ConvertDataFormat::WMF)) + { + pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP + : OOO_STRING_SVTOOLS_RTF_WMETAFILE; + nSize = aStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + } + } + + Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel() + : rGraphic.GetPrefSize()); + + auto& rCr = static_cast<const SwCropGrf&>(pGrfNode->GetAttr(RES_GRFATR_CROPGRF)); + + //Get original size in twips + Size aSize(pGrfNode->GetTwipSize()); + Size aRendered(aSize); + + const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize(); + aRendered.setWidth(rS.GetWidth()); + aRendered.setHeight(rS.GetHeight()); + + ww8::Frame* pFrame = nullptr; + for (auto& rFrame : m_rExport.m_aFrames) + { + if (pFlyFrameFormat == &rFrame.GetFrameFormat()) + { + pFrame = &rFrame; + break; + } + } + + /* + If the graphic is not of type WMF then we will have to store two + graphics, one in the native format wrapped in shppict, and the other in + the wmf format wrapped in nonshppict, so as to keep wordpad happy. If its + a wmf already then we don't need any such wrapping + */ + bool bIsWMF = pBLIPType && std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0; + const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet(); + if (!pFrame || pFrame->IsInline()) + { + if (!bIsWMF) + m_rExport.Strm().WriteCharPtr( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT); + } + else + { + m_rExport.Strm().WriteCharPtr( + "{" OOO_STRING_SVTOOLS_RTF_SHP + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST); + m_pFlyFrameSize = &aRendered; + m_rExport.m_pParentFrame = pFrame; + m_rExport.m_bOutFlyFrameAttrs = true; + m_rExport.SetRTFFlySyntax(true); + m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true); + m_rExport.m_bOutFlyFrameAttrs = false; + m_rExport.SetRTFFlySyntax(false); + m_rExport.m_pParentFrame = nullptr; + m_pFlyFrameSize = nullptr; + + std::vector<std::pair<OString, OString>> aFlyProperties; + aFlyProperties.push_back(std::make_pair<OString, OString>( + "shapeType", OString::number(ESCHER_ShpInst_PictureFrame))); + aFlyProperties.push_back(std::make_pair<OString, OString>( + "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(), + m_rExport.GetCurrentEncoding()))); + aFlyProperties.push_back(std::make_pair<OString, OString>( + "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(), + m_rExport.GetCurrentEncoding()))); + + // If we have a wrap polygon, then handle that here. + if (pFlyFrameFormat->GetSurround().IsContour()) + { + if (const SwNoTextNode* pNd + = sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat)) + { + const tools::PolyPolygon* pPolyPoly = pNd->HasContour(); + if (pPolyPoly && pPolyPoly->Count()) + { + tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport( + *pPolyPoly, pNd, /*bCorrectCrop=*/true); + OStringBuffer aVerticies; + for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i) + aVerticies.append(";(") + .append(aPoly[i].X()) + .append(",") + .append(aPoly[i].Y()) + .append(")"); + aFlyProperties.push_back(std::make_pair<OString, OString>( + "pWrapPolygonVertices", + "8;" + OString::number(aPoly.GetSize()) + aVerticies.makeStringAndClear())); + } + } + } + + // Below text, behind document, opaque: they all refer to the same thing. + if (!pFlyFrameFormat->GetOpaque().GetValue()) + aFlyProperties.push_back(std::make_pair<OString, OString>("fBehindDocument", "1")); + + if (pAttrSet) + { + if (sal_Int32 nRot = pAttrSet->Get(RES_GRFATR_ROTATION).GetValue()) + { + // See writerfilter::rtftok::RTFSdrImport::applyProperty(), + // positive rotation angles are clockwise in RTF, we have them + // as counter-clockwise. + // Additionally, RTF type is 0..360*2^16, our is 0..360*10. + nRot = nRot * -1 * RTF_MULTIPLIER / 10; + aFlyProperties.emplace_back("rotation", OString::number(nRot)); + } + } + + for (const std::pair<OString, OString>& rPair : aFlyProperties) + { + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{"); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SN " "); + m_rExport.Strm().WriteOString(rPair.first); + m_rExport.Strm().WriteCharPtr("}{" OOO_STRING_SVTOOLS_RTF_SV " "); + m_rExport.Strm().WriteOString(rPair.second); + m_rExport.Strm().WriteCharPtr("}}"); + } + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{" OOO_STRING_SVTOOLS_RTF_SN + " pib" + "}{" OOO_STRING_SVTOOLS_RTF_SV " "); + } + + bool bWritePicProp = !pFrame || pFrame->IsInline(); + if (pBLIPType) + ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize, + m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet); + else + { + aStream.Seek(0); + if (GraphicConverter::Export(aStream, rGraphic, aConvertDestinationFormat) != ERRCODE_NONE) + SAL_WARN("sw.rtf", "failed to export the graphic"); + pBLIPType = pConvertDestinationBLIPType; + nSize = aStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + + ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize, + m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet); + } + + if (!pFrame || pFrame->IsInline()) + { + if (!bIsWMF) + { + m_rExport.Strm().WriteCharPtr("}" + "{" OOO_STRING_SVTOOLS_RTF_NONSHPPICT); + + aStream.Seek(0); + if (GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE) + SAL_WARN("sw.rtf", "failed to export the graphic"); + pBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE; + nSize = aStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + + ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, + nSize, m_rExport, &m_rExport.Strm()); + + m_rExport.Strm().WriteChar('}'); + } + } + else + m_rExport.Strm().WriteCharPtr("}}}}"); // Close SV, SP, SHPINST and SHP. + + m_rExport.Strm().WriteCharPtr(SAL_NEWLINE_STRING); +} + +void RtfAttributeOutput::BulletDefinition(int /*nId*/, const Graphic& rGraphic, Size aSize) +{ + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT); + m_rExport.Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP); + + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICWGOAL); + m_rExport.OutULong(aSize.Width()); + m_rExport.Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICHGOAL); + m_rExport.OutULong(aSize.Height()); + + m_rExport.Strm().WriteCharPtr(SAL_NEWLINE_STRING); + const sal_uInt8* pGraphicAry = nullptr; + SvMemoryStream aStream; + if (GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE) + SAL_WARN("sw.rtf", "failed to export the numbering picture bullet"); + sal_uInt32 nSize = aStream.TellEnd(); + pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &m_rExport.Strm()); + m_rExport.Strm().WriteCharPtr("}}"); // pict, shppict +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx new file mode 100644 index 000000000..2493ce38f --- /dev/null +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -0,0 +1,670 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX + +#include <memory> +#include <com/sun/star/drawing/FillStyle.hpp> + +#include "attributeoutputbase.hxx" +#include "rtfstringbuffer.hxx" + +#include <wrtswtbl.hxx> + +#include <rtl/strbuf.hxx> + +#include <optional> + +class SwGrfNode; +class SwOLENode; +class SwFlyFrameFormat; +class RtfExport; + +/// The class that has handlers for various resource types when exporting as RTF +class RtfAttributeOutput : public AttributeOutputBase +{ + friend class RtfStringBufferValue; + friend class SaveRunState; + +public: + /// Export the state of RTL/CJK. + void RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript) override; + + /// Start of the paragraph. + void StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo) override; + + /// End of the paragraph. + void EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) override; + + /// Empty paragraph. + void EmptyParagraph() override; + + /// Called in order to output section breaks. + void SectionBreaks(const SwNode& rNode) override; + + /// Called before we start outputting the attributes. + void StartParagraphProperties() override; + + /// Called after we end outputting the attributes. + void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, + const SwRedlineData* pRedlineData, + const SwRedlineData* pRedlineParagraphMarkerDeleted, + const SwRedlineData* pRedlineParagraphMarkerInserted) override; + + /// Start of the text run. + void StartRun(const SwRedlineData* pRedlineData, sal_Int32 nPos, + bool bSingleEmptyRun = false) override; + + /// End of the text run. + void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; + + /// Called before we start outputting the attributes. + void StartRunProperties() override; + + /// Called after we end outputting the attributes. + void EndRunProperties(const SwRedlineData* pRedlineData) override; + + /// Output text (inside a run). + void RunText(const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8) override; + + // Access to (anyway) private buffers, used by the sdr exporter + OStringBuffer& RunText(); + OString MoveCharacterProperties(bool aAutoWriteRtlLtr = false); + + /// Output text (without markup). + void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override; + + /// Output ruby start. + void StartRuby(const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby) override; + + /// Output ruby end. + void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override; + + /// Output URL start. + bool StartURL(const OUString& rUrl, const OUString& rTarget) override; + + /// Output URL end. + bool EndURL(bool isAtEndOfParagraph) override; + + void FieldVanish(const OUString& rText, ww::eField eType) override; + + /// Output redlining. + /// + /// The common attribute that can be among the run properties. + void Redline(const SwRedlineData* pRedline) override; + + void FormatDrop(const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle, + ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, + ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) override; + + /// Output style. + void ParagraphStyle(sal_uInt16 nStyle) override; + + void TableInfoCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableDefinition(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void + TableDefaultBorders(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableBackgrounds(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableRowRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableCellRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableCanSplit(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableVerticalCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner) override; + void TableOrientation(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) override; + void TableRowEnd(sal_uInt32 nDepth) override; + + /// Start of the styles table. + void StartStyles() override; + + /// End of the styles table. + void EndStyles(sal_uInt16 nNumberOfStyles) override; + + /// Write default style. + void DefaultStyle() override; + + /// Start of a style in the styles table. + void StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase, sal_uInt16 nNext, + sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate) override; + + /// End of a style in the styles table. + void EndStyle() override; + + /// Start of (paragraph or run) properties of a style. + void StartStyleProperties(bool bParProp, sal_uInt16 nStyle) override; + + /// End of (paragraph or run) properties of a style. + void EndStyleProperties(bool bParProp) override; + + /// Numbering rule and Id. + void OutlineNumbering(sal_uInt8 nLvl) override; + + /// Page break + /// As a paragraph property - the paragraph should be on the next page. + void PageBreakBefore(bool bBreak) override; + + /// Write a section break + /// msword::ColumnBreak or msword::PageBreak + void SectionBreak(sal_uInt8 nC, bool bBreakAfter, + const WW8_SepInfo* pSectionInfo = nullptr) override; + + /// Start of the section properties. + void StartSection() override; + + /// End of the section properties. + void EndSection() override; + + /// Protection of forms. + void SectionFormProtection(bool bProtected) override; + + /// Numbering of the lines in the document. + void SectionLineNumbering(sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo) override; + + /// Has different headers/footers for the title page. + void SectionTitlePage() override; + + /// Description of the page borders. + void SectionPageBorders(const SwFrameFormat* pFormat, + const SwFrameFormat* pFirstPageFormat) override; + + /// Columns populated from right/numbers on the right side? + void SectionBiDi(bool bBiDi) override; + + /// The style of the page numbers. + /// + void SectionPageNumbering(sal_uInt16 nNumType, + const ::std::optional<sal_uInt16>& oPageRestartNumber) override; + + /// The type of breaking. + void SectionType(sal_uInt8 nBreakCode) override; + + void SectFootnoteEndnotePr() override; + + void WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo); + + /// Definition of a numbering instance. + void NumberingDefinition(sal_uInt16 nId, const SwNumRule& rRule) override; + + /// Start of the abstract numbering definition instance. + void StartAbstractNumbering(sal_uInt16 nId) override; + + /// End of the abstract numbering definition instance. + void EndAbstractNumbering() override; + + /// All the numbering level information. + void + NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart, sal_uInt16 nNumberingType, + SvxAdjust eAdjust, const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow, + const wwFont* pFont, const SfxItemSet* pOutSet, sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, sal_Int16 nListTabPos, + const OUString& rNumberingString, + const SvxBrushItem* pBrush) override; //For i120928,to export graphic of bullet + + void WriteField_Impl(const SwField* pField, ww::eField eType, const OUString& rFieldCmd, + FieldFlags nMode); + void WriteBookmarks_Impl(std::vector<OUString>& rStarts, std::vector<OUString>& rEnds); + void WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts, std::vector<OUString>& rEnds); + void WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader, const char* pStr, + bool bTitlepg); + void WriteBookmarkInActParagraph(const OUString& /*rName*/, sal_Int32 /*nFirstRunPos*/, + sal_Int32 /*nLastRunPos*/) override{}; + +protected: + /// Output frames - the implementation. + void OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& rNdTopLeft) override; + + /// Sfx item Sfx item RES_CHRATR_CASEMAP + void CharCaseMap(const SvxCaseMapItem& rCaseMap) override; + + /// Sfx item Sfx item RES_CHRATR_COLOR + void CharColor(const SvxColorItem& rColor) override; + + /// Sfx item Sfx item RES_CHRATR_CONTOUR + void CharContour(const SvxContourItem& rContour) override; + + /// Sfx item RES_CHRATR_CROSSEDOUT + void CharCrossedOut(const SvxCrossedOutItem& rCrossedOut) override; + + /// Sfx item RES_CHRATR_ESCAPEMENT + void CharEscapement(const SvxEscapementItem& rEscapement) override; + + /// Sfx item RES_CHRATR_FONT + void CharFont(const SvxFontItem& rFont) override; + + /// Sfx item RES_CHRATR_FONTSIZE + void CharFontSize(const SvxFontHeightItem& rFontSize) override; + + /// Sfx item RES_CHRATR_KERNING + void CharKerning(const SvxKerningItem& rKerning) override; + + /// Sfx item RES_CHRATR_LANGUAGE + void CharLanguage(const SvxLanguageItem& rLanguage) override; + + /// Sfx item RES_CHRATR_POSTURE + void CharPosture(const SvxPostureItem& rPosture) override; + + /// Sfx item RES_CHRATR_SHADOWED + void CharShadow(const SvxShadowedItem& rShadow) override; + + /// Sfx item RES_CHRATR_UNDERLINE + void CharUnderline(const SvxUnderlineItem& rUnderline) override; + + /// Sfx item RES_CHRATR_WEIGHT + void CharWeight(const SvxWeightItem& rWeight) override; + + /// Sfx item RES_CHRATR_AUTOKERN + void CharAutoKern(const SvxAutoKernItem& rAutoKern) override; + + /// Sfx item RES_CHRATR_BLINK + void CharAnimatedText(const SvxBlinkItem& rBlink) override; + + /// Sfx item RES_CHRATR_BACKGROUND + void CharBackground(const SvxBrushItem& rBrush) override; + + /// Sfx item RES_CHRATR_CJK_FONT + void CharFontCJK(const SvxFontItem& rFont) override; + + /// Sfx item RES_CHRATR_CJK_FONTSIZE + void CharFontSizeCJK(const SvxFontHeightItem& rFontSize) override; + + /// Sfx item RES_CHRATR_CJK_LANGUAGE + void CharLanguageCJK(const SvxLanguageItem& rLanguageItem) override; + + /// Sfx item RES_CHRATR_CJK_POSTURE + void CharPostureCJK(const SvxPostureItem& rPosture) override; + + /// Sfx item RES_CHRATR_CJK_WEIGHT + void CharWeightCJK(const SvxWeightItem& rWeight) override; + + /// Sfx item RES_CHRATR_CTL_FONT + void CharFontCTL(const SvxFontItem& rFont) override; + + /// Sfx item RES_CHRATR_CTL_FONTSIZE + void CharFontSizeCTL(const SvxFontHeightItem& rFontSize) override; + + /// Sfx item RES_CHRATR_CTL_LANGUAGE + void CharLanguageCTL(const SvxLanguageItem& rLanguageItem) override; + + /// Sfx item RES_CHRATR_CTL_POSTURE + void CharPostureCTL(const SvxPostureItem& rPosture) override; + + /// Sfx item RES_CHRATR_CTL_WEIGHT + void CharWeightCTL(const SvxWeightItem& rWeight) override; + + /// Sfx item RES_CHRATR_BidiRTL + void CharBidiRTL(const SfxPoolItem& rItem) override; + + /// Sfx item RES_CHRATR_IdctHint + void CharIdctHint(const SfxPoolItem& rItem) override; + + /// Sfx item RES_CHRATR_ROTATE + void CharRotate(const SvxCharRotateItem& rRotate) override; + + /// Sfx item RES_CHRATR_EMPHASIS_MARK + void CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark) override; + + /// Sfx item RES_CHRATR_TWO_LINES + void CharTwoLines(const SvxTwoLinesItem& rTwoLines) override; + + /// Sfx item RES_CHRATR_SCALEW + void CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth) override; + + /// Sfx item RES_CHRATR_RELIEF + void CharRelief(const SvxCharReliefItem& rRelief) override; + + /// Sfx item RES_CHRATR_HIDDEN + void CharHidden(const SvxCharHiddenItem& rHidden) override; + + /// Sfx item RES_CHRATR_BOX + void CharBorder(const ::editeng::SvxBorderLine* pAllBorder, sal_uInt16 nDist, + bool bShadow) override; + + /// Sfx item RES_CHRATR_HIGHLIGHT + void CharHighlight(const SvxBrushItem& rBrush) override; + + /// Sfx item RES_TXTATR_INETFMT + void TextINetFormat(const SwFormatINetFormat& rURL) override; + + /// Sfx item RES_TXTATR_CHARFMT + void TextCharFormat(const SwFormatCharFormat& rCharFormat) override; + + /// Sfx item RES_TXTATR_FTN + void TextFootnote_Impl(const SwFormatFootnote& rFootnote) override; + + /// Sfx item RES_PARATR_LINESPACING + void ParaLineSpacing_Impl(short nSpace, short nMulti) override; + + /// Sfx item RES_PARATR_ADJUST + void ParaAdjust(const SvxAdjustItem& rAdjust) override; + + /// Sfx item RES_PARATR_SPLIT + void ParaSplit(const SvxFormatSplitItem& rSplit) override; + + /// Sfx item RES_PARATR_WIDOWS + void ParaWidows(const SvxWidowsItem& rWidows) override; + + /// Sfx item RES_PARATR_TABSTOP + void ParaTabStop(const SvxTabStopItem& rTabStop) override; + + /// Sfx item RES_PARATR_HYPHENZONE + void ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone) override; + + /// Sfx item RES_PARATR_NUMRULE + void ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId) override; + + /// Sfx item RES_PARATR_SCRIPTSPACE + void ParaScriptSpace(const SfxBoolItem& rScriptSpace) override; + + /// Sfx item RES_PARATR_HANGINGPUNCTUATION + void ParaHangingPunctuation(const SfxBoolItem& rItem) override; + + /// Sfx item RES_PARATR_FORBIDDEN_RULES + void ParaForbiddenRules(const SfxBoolItem& rItem) override; + + /// Sfx item RES_PARATR_VERTALIGN + void ParaVerticalAlign(const SvxParaVertAlignItem& rAlign) override; + + /// Sfx item RES_PARATR_SNAPTOGRID + void ParaSnapToGrid(const SvxParaGridItem& rItem) override; + + /// Sfx item RES_FRM_SIZE + void FormatFrameSize(const SwFormatFrameSize& rSize) override; + + /// Sfx item RES_PAPER_BIN + void FormatPaperBin(const SvxPaperBinItem& rItem) override; + + /// Sfx item RES_LR_SPACE + void FormatLRSpace(const SvxLRSpaceItem& rLRSpace) override; + + /// Sfx item RES_UL_SPACE + void FormatULSpace(const SvxULSpaceItem& rULSpace) override; + + /// Sfx item RES_SURROUND + void FormatSurround(const SwFormatSurround& rSurround) override; + + /// Sfx item RES_VERT_ORIENT + void FormatVertOrientation(const SwFormatVertOrient& rFlyVert) override; + + /// Sfx item RES_HORI_ORIENT + void FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori) override; + + /// Sfx item RES_ANCHOR + void FormatAnchor(const SwFormatAnchor& rAnchor) override; + + /// Sfx item RES_BACKGROUND + void FormatBackground(const SvxBrushItem& rBrush) override; + + /// Sfx item RES_FILL_STYLE + void FormatFillStyle(const XFillStyleItem& rFillStyle) override; + + /// Sfx item RES_FILL_GRADIENT + void FormatFillGradient(const XFillGradientItem& rFillGradient) override; + + /// Sfx item RES_BOX + void FormatBox(const SvxBoxItem& rBox) override; + + /// Sfx item RES_COL + void FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, + SwTwips nPageSize) override; + + /// Sfx item RES_KEEP + void FormatKeep(const SvxFormatKeepItem& rItem) override; + + /// Sfx item RES_TEXTGRID + void FormatTextGrid(const SwTextGridItem& rItem) override; + + /// Sfx item RES_LINENUMBER + void FormatLineNumbering(const SwFormatLineNumber& rNumbering) override; + + /// Sfx item RES_FRAMEDIR + void FormatFrameDirection(const SvxFrameDirectionItem& rDirection) override; + + /// Sfx item RES_PARATR_GRABBAG + void ParaGrabBag(const SfxGrabBagItem& rItem) override; + + /// Sfx item RES_CHRATR_GRABBAG + void CharGrabBag(const SfxGrabBagItem& rItem) override; + + /// Sfx item RES_PARATR_OUTLINELEVEL + void ParaOutlineLevel(const SfxUInt16Item& rItem) override; + + /// Write the expanded field + void WriteExpand(const SwField* pField) override; + + void RefField(const SwField& rField, const OUString& rRef) override; + void HiddenField(const SwField& rField) override; + void SetField(const SwField& rField, ww::eField eType, const OUString& rCmd) override; + void PostitField(const SwField* pField) override; + bool DropdownField(const SwField* pField) override; + bool PlaceholderField(const SwField* pField) override; + +private: + /// Reference to the export, where to get the data from + RtfExport& m_rExport; + + OStringBuffer m_aTabStop; + + /// Access to the page style of the previous paragraph. + const SwPageDesc* m_pPrevPageDesc; + + /// Output graphic fly frames. + void FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode); + void FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode, + const Size& rSize); + void FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode, + const Size& rSize); + /// Math export. + bool FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode, + const Size& rSize); + + /* + * Table methods. + */ + void InitTableHelper(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner); + void StartTable(); + void StartTableRow(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner); + void StartTableCell(); + void TableCellProperties(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner); + void EndTableCell(); + void EndTableRow(); + void EndTable(); + + /// End cell, row, and even the entire table if necessary. + void FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner); + + void WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote); + + /* + * Current style name and its ID. + */ + OUString m_rStyleName; + sal_uInt16 m_nStyleId; + /* + * Current list ID. + */ + sal_uInt16 m_nListId; + /* + * This is needed because the call order is: run text, run properties, paragraph properties. + * What we need is the opposite. + */ + RtfStringBuffer m_aRun; + RtfStringBuffer m_aRunText; + /* + * This is written after runs. + */ + OStringBuffer m_aAfterRuns; + /* + * Same for colors and stylesheets: first we just want to output colors, + * need to buffer the stylesheet table to output it after the color one. + */ + OStringBuffer m_aStylesheet; + /* + * This one just holds the style commands in the current style. + */ + OStringBuffer m_aStyles; + /* + * This is the same as m_aStyles but the contents of it is Assoc. + */ + OStringBuffer m_aStylesAssoc; + bool m_bIsRTL; + sal_uInt16 m_nScript; + bool m_bControlLtrRtl; + + sal_Int32 m_nNextAnnotationMarkId; + sal_Int32 m_nCurrentAnnotationMarkId; + /// Maps annotation mark names to ID's. + std::map<OString, sal_Int32> m_rOpenedAnnotationMarksIds; + + /* + * The current table helper. + */ + std::unique_ptr<SwWriteTable> m_pTableWrt; + + /* + * Remember if we are in an open cell, or not. + */ + bool m_bTableCellOpen; + + /* + * Remember the current table depth. + */ + sal_uInt32 m_nTableDepth; + + /* + * Remember if we wrote a \cell or not. + */ + bool m_bTableAfterCell; + + /* + * For late output of row definitions. + */ + OStringBuffer m_aRowDefs; + + /* + * Is a column break needed after the next \par? + */ + bool m_nColBreakNeeded; + + /* + * If section breaks should be buffered to m_aSectionBreaks + */ + bool m_bBufferSectionBreaks; + OStringBuffer m_aSectionBreaks; + + /* + * If section headers (and footers) should be buffered to + * m_aSectionHeaders. + */ + bool m_bBufferSectionHeaders; + OStringBuffer m_aSectionHeaders; + + /* + * Support for starting multiple tables at the same cell. + * If the current table is the last started one. + */ + bool m_bLastTable; + /* + * List of already started but not yet defined tables (need to be defined + * after the nested tables). + */ + std::vector<OString> m_aTables; + /* + * If cell info is already output. + */ + bool m_bWroteCellInfo; + + /// If we ended a table row without starting a new one. + bool m_bTableRowEnded; + + /// Number of cells from the table definition, by depth. + std::map<sal_uInt32, sal_uInt32> m_aCells; + + bool m_bIsBeforeFirstParagraph; + + /// If we're in a paragraph that has a single empty run only. + bool m_bSingleEmptyRun; + + bool m_bInRun; + + /// Maps ID's to postit fields, used in atrfstart/end and atnref. + std::map<sal_uInt16, const SwPostItField*> m_aPostitFields; + + /// When exporting fly frames, this holds the real size of the frame. + const Size* m_pFlyFrameSize; + + std::vector<std::pair<OString, OString>> m_aFlyProperties; + + std::optional<css::drawing::FillStyle> m_oFillStyle; + + /// If we're in the process of exporting a hyperlink, then its URL. + OUString m_sURL; + + /// If original file had \sbauto. + bool m_bParaBeforeAutoSpacing; + /// If m_bParaBeforeAutoSpacing is set, value of \sb. + sal_Int32 m_nParaBeforeSpacing; + /// If original file had \saauto. + bool m_bParaAfterAutoSpacing; + /// If m_bParaBeforeAutoSpacing is set, value of \sa. + sal_Int32 m_nParaAfterSpacing; + +public: + explicit RtfAttributeOutput(RtfExport& rExport); + + ~RtfAttributeOutput() override; + + /// Return the right export class. + MSWordExportBase& GetExport() override; + + // These are used by wwFont::WriteRtf() + /// Start the font. + void StartFont(const OUString& rFamilyName) const; + + /// End the font. + void EndFont() const; + + /// Alternate name for the font. + void FontAlternateName(const OUString& rName) const; + + /// Font charset. + void FontCharset(sal_uInt8 nCharSet) const; + + /// Font family. + void FontFamilyType(FontFamily eFamily, const wwFont& rFont) const; + + /// Font pitch. + void FontPitchType(FontPitch ePitch) const; + + void BulletDefinition(int nId, const Graphic& rGraphic, Size aSize) override; + + /// Handles just the {\shptxt ...} part of a shape export. + void writeTextFrame(const ww8::Frame& rFrame, bool bTextBox = false); + + OStringBuffer& GetTabStop() { return m_aTabStop; } + + const SwPageDesc* GetPrevPageDesc() const { return m_pPrevPageDesc; } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFATTRIBUTEOUTPUT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx new file mode 100644 index 000000000..3cea7bfb8 --- /dev/null +++ b/sw/source/filter/ww8/rtfexport.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 "rtfexport.hxx" + +#include "rtfexportfilter.hxx" +#include "rtfsdrexport.hxx" +#include "rtfattributeoutput.hxx" +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <docsh.hxx> +#include <viewsh.hxx> +#include <viewopt.hxx> +#include <fmtpdsc.hxx> +#include <ftninfo.hxx> +#include <fmthdft.hxx> +#include <editeng/colritem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/paperinf.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <lineinfo.hxx> +#include <redline.hxx> +#include <swmodule.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <comphelper/string.hxx> +#include <svtools/rtfkeywd.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <unotools/docinfohelper.hxx> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#if OSL_DEBUG_LEVEL > 1 +#include <iostream> +#endif +#include <svx/xflclit.hxx> +#include <fmtmeta.hxx> +#include <IDocumentSettingAccess.hxx> +#include <fmtfsize.hxx> +#include <ndtxt.hxx> +#include <numrule.hxx> +#include <frmatr.hxx> +#include <swtable.hxx> +#include <IMark.hxx> + +using namespace ::com::sun::star; + +// the default text encoding for the export, if it doesn't fit unicode will +// be used +#define DEF_ENCODING RTL_TEXTENCODING_ASCII_US + +AttributeOutputBase& RtfExport::AttrOutput() const { return *m_pAttrOutput; } + +MSWordSections& RtfExport::Sections() const { return *m_pSections; } + +RtfSdrExport& RtfExport::SdrExporter() const { return *m_pSdrExport; } + +bool RtfExport::CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich) +{ + // FIXME is this actually true for rtf? - this is copied from DOCX + if (nScript == i18n::ScriptType::ASIAN) + { + // for asian in ww8, there is only one fontsize + // and one fontstyle (posture/weight) + switch (nWhich) + { + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + return false; + default: + break; + } + } + else if (nScript != i18n::ScriptType::COMPLEX) + { + // for western in ww8, there is only one fontsize + // and one fontstyle (posture/weight) + switch (nWhich) + { + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + return false; + default: + break; + } + } + return true; +} + +void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen) +{ + std::vector<OUString> aStarts; + std::vector<OUString> aEnds; + + IMarkVector aMarks; + if (GetBookmarks(rNode, nCurrentPos, nCurrentPos + nLen, aMarks)) + { + for (const auto& pMark : aMarks) + { + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + if (nStart == nCurrentPos) + aStarts.push_back(pMark->GetName()); + + if (nEnd == nCurrentPos) + aEnds.push_back(pMark->GetName()); + } + } + + m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds); +} + +void RtfExport::AppendBookmark(const OUString& rName) +{ + std::vector<OUString> aStarts; + std::vector<OUString> aEnds; + + aStarts.push_back(rName); + aEnds.push_back(rName); + + m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds); +} + +void RtfExport::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, + sal_Int32 nLen) +{ + std::vector<OUString> aStarts; + std::vector<OUString> aEnds; + + IMarkVector aMarks; + if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks)) + { + for (const auto& pMark : aMarks) + { + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + if (nStart == nCurrentPos) + aStarts.push_back(pMark->GetName()); + + if (nEnd == nCurrentPos) + aEnds.push_back(pMark->GetName()); + } + } + + m_pAttrOutput->WriteAnnotationMarks_Impl(aStarts, aEnds); +} + +//For i120928,to export graphic of bullet for RTF filter +void RtfExport::ExportGrfBullet(const SwTextNode& /*rNd*/) +{ + // Noop, would be too late, see WriteNumbering() instead. +} + +void RtfExport::WriteChar(sal_Unicode /*c*/) { /* WriteChar() has nothing to do for rtf. */} + +static bool IsExportNumRule(const SwNumRule& rRule) +{ + sal_uInt8 nEnd = MAXLEVEL; + while (nEnd-- && !rRule.GetNumFormat(nEnd)) + ; + ++nEnd; + + sal_uInt8 nLvl; + + for (nLvl = 0; nLvl < nEnd; ++nLvl) + { + const SwNumFormat* pNFormat = &rRule.Get(nLvl); + if (SVX_NUM_NUMBER_NONE != pNFormat->GetNumberingType() || !pNFormat->GetPrefix().isEmpty() + || (!pNFormat->GetSuffix().isEmpty() && pNFormat->GetSuffix() != ".")) + break; + } + + return nLvl != nEnd; +} + +void RtfExport::BuildNumbering() +{ + const SwNumRuleTable& rListTable = m_pDoc->GetNumRuleTable(); + + SwNumRule* pOutlineRule = m_pDoc->GetOutlineNumRule(); + if (IsExportNumRule(*pOutlineRule)) + GetNumberingId(*pOutlineRule); + + for (auto n = rListTable.size(); n;) + { + SwNumRule* pRule = rListTable[--n]; + if (!SwDoc::IsUsed(*pRule)) + continue; + + if (IsExportNumRule(*pRule)) + GetNumberingId(*pRule); + } +} + +void RtfExport::WriteNumbering() +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + if (!m_pUsedNumTable) + return; // no numbering is used + + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTTABLE); + + CollectGrfsOfBullets(); + if (!m_vecBulletPic.empty()) + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(LO_STRING_SVTOOLS_RTF_LISTPICTURE); + BulletDefinitions(); + if (!m_vecBulletPic.empty()) + Strm().WriteChar('}'); + + AbstractNumberingDefinitions(); + Strm().WriteChar('}'); + + Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDETABLE); + NumberingDefinitions(); + Strm().WriteChar('}'); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfExport::WriteRevTab() +{ + int nRevAuthors = m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); + + if (nRevAuthors < 1) + return; + + // RTF always seems to use Unknown as the default first entry + GetRedline("Unknown"); + + for (SwRangeRedline* pRedl : m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()) + { + GetRedline(SW_MOD()->GetRedlineAuthor(pRedl->GetAuthor())); + } + + // Now write the table + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVTBL) + .WriteChar(' '); + for (std::size_t i = 0; i < m_aRedlineTable.size(); ++i) + { + const OUString* pAuthor = GetRedline(i); + Strm().WriteChar('{'); + if (pAuthor) + Strm().WriteCharPtr( + msfilter::rtfutil::OutString(*pAuthor, m_eDefaultEncoding).getStr()); + Strm().WriteCharPtr(";}"); + } + Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING); +} + +void RtfExport::WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat, + const SwFrameFormat& rLeftFormat, + const SwFrameFormat& rFirstPageFormat, sal_uInt8 /*nBreakCode*/) +{ + // headers + if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN) + WriteHeaderFooter(rLeftFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERL); + + if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD) + WriteHeaderFooter(rFormat, true, OOO_STRING_SVTOOLS_RTF_HEADER); + + if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST) + WriteHeaderFooter(rFirstPageFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERF, true); + + // footers + if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN) + WriteHeaderFooter(rLeftFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERL); + + if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD) + WriteHeaderFooter(rFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTER); + + if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST) + WriteHeaderFooter(rFirstPageFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERF, true); +} + +void RtfExport::OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd, + FieldFlags nMode) +{ + m_pAttrOutput->WriteField_Impl(pField, eFieldType, rFieldCmd, nMode); +} + +void RtfExport::WriteFormData(const ::sw::mark::IFieldmark& /*rFieldmark*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfExport::WriteHyperlinkData(const ::sw::mark::IFieldmark& /*rFieldmark*/) +{ + SAL_INFO("sw.rtf", "TODO: " << OSL_THIS_FUNC); +} + +void RtfExport::DoComboBox(const OUString& /*rName*/, const OUString& /*rHelp*/, + const OUString& /*rToolTip*/, const OUString& /*rSelected*/, + const uno::Sequence<OUString>& /*rListItems*/) +{ + // this is handled in RtfAttributeOutput::OutputFlyFrame_Impl +} + +void RtfExport::DoFormText(const SwInputField* pField) +{ + OUString sResult = pField->ExpandField(true, nullptr); + const OUString& rHelp = pField->GetHelp(); + OUString sName = pField->GetPar2(); + const OUString& rStatus = pField->GetToolTip(); + m_pAttrOutput->RunText().append("{" OOO_STRING_SVTOOLS_RTF_FIELD + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST + "{ FORMTEXT }"); + m_pAttrOutput->RunText().append( + "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD + " {" OOO_STRING_SVTOOLS_RTF_FFTYPE "0"); + if (!rHelp.isEmpty()) + m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP); + if (!rStatus.isEmpty()) + m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT); + m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFTYPETXT "0"); + + if (!sName.isEmpty()) + m_pAttrOutput->RunText() + .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME " ") + .append(msfilter::rtfutil::OutString(sName, m_eDefaultEncoding)) + .append("}"); + if (!rHelp.isEmpty()) + m_pAttrOutput->RunText() + .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ") + .append(msfilter::rtfutil::OutString(rHelp, m_eDefaultEncoding)) + .append("}"); + m_pAttrOutput->RunText() + .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFDEFTEXT " ") + .append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding)) + .append("}"); + if (!rStatus.isEmpty()) + m_pAttrOutput->RunText() + .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ") + .append(msfilter::rtfutil::OutString(rStatus, m_eDefaultEncoding)) + .append("}"); + m_pAttrOutput->RunText().append("}}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " "); + m_pAttrOutput->RunText() + .append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding)) + .append("}}"); +} + +sal_uLong RtfExport::ReplaceCr(sal_uInt8 /*nChar*/) +{ + // Completely unused for Rtf export... only here for code sharing + // purpose with binary export + + return 0; +} + +void RtfExport::WriteFonts() +{ + Strm() + .WriteCharPtr(SAL_NEWLINE_STRING) + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FONTTBL); + m_aFontHelper.WriteFontTable(*m_pAttrOutput); + Strm().WriteChar('}'); +} + +void RtfExport::WriteStyles() +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + m_pStyles->OutputStylesTable(); + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfExport::WriteFootnoteSettings() +{ + const SwPageFootnoteInfo& rFootnoteInfo = m_pDoc->GetPageDesc(0).GetFootnoteInfo(); + // Request a separator only in case the width is larger than zero. + bool bSeparator = double(rFootnoteInfo.GetWidth()) > 0; + + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSEP); + if (bSeparator) + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_CHFTNSEP); + Strm().WriteChar('}'); +} + +void RtfExport::WriteMainText() +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush) + { + Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_VIEWBKSP).WriteChar('1'); + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BACKGROUND); + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SHP); + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST); + + std::vector<std::pair<OString, OString>> aProperties; + aProperties.push_back(std::make_pair<OString, OString>("shapeType", "1")); + aProperties.push_back(std::make_pair<OString, OString>( + "fillColor", OString::number(wwUtility::RGBToBGR(oBrush->GetColor())))); + for (const std::pair<OString, OString>& rPair : aProperties) + { + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{"); + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SN " "); + Strm().WriteOString(rPair.first); + Strm().WriteCharPtr("}{" OOO_STRING_SVTOOLS_RTF_SV " "); + Strm().WriteOString(rPair.second); + Strm().WriteCharPtr("}}"); + } + Strm().WriteChar('}'); // shpinst + Strm().WriteChar('}'); // shp + Strm().WriteChar('}'); // background + } + + SwTableNode* pTableNode = m_pCurPam->GetNode().FindTableNode(); + if (m_pWriter && m_pWriter->m_bWriteOnlyFirstTable && pTableNode != nullptr) + { + m_pCurPam->GetPoint()->nNode = *pTableNode; + m_pCurPam->GetMark()->nNode = *(pTableNode->EndOfSectionNode()); + } + else + { + m_pCurPam->GetPoint()->nNode + = m_pDoc->GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex(); + } + + WriteText(); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfExport::WriteInfo() +{ + OString aGenerator + = OUStringToOString(utl::DocInfoHelper::GetGeneratorString(), RTL_TEXTENCODING_UTF8); + Strm() + .WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_GENERATOR " ") + .WriteOString(aGenerator) + .WriteChar('}'); + Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_INFO); + + SwDocShell* pDocShell(m_pDoc->GetDocShell()); + uno::Reference<document::XDocumentProperties> xDocProps; + if (pDocShell) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(), + uno::UNO_QUERY); + xDocProps.set(xDPS->getDocumentProperties()); + } + + if (xDocProps.is()) + { + // Handle user-defined properties. + uno::Reference<beans::XPropertyContainer> xUserDefinedProperties + = xDocProps->getUserDefinedProperties(); + if (xUserDefinedProperties.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties, + uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropertySetInfo + = xPropertySet->getPropertySetInfo(); + // Do we have explicit markup in RTF for this property name? + if (xPropertySetInfo->hasPropertyByName("Company")) + { + OUString aValue; + xPropertySet->getPropertyValue("Company") >>= aValue; + OutUnicode(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_COMPANY, aValue); + } + } + + OutUnicode(OOO_STRING_SVTOOLS_RTF_TITLE, xDocProps->getTitle(), true); + OutUnicode(OOO_STRING_SVTOOLS_RTF_SUBJECT, xDocProps->getSubject()); + + OutUnicode(OOO_STRING_SVTOOLS_RTF_KEYWORDS, + ::comphelper::string::convertCommaSeparated(xDocProps->getKeywords())); + OutUnicode(OOO_STRING_SVTOOLS_RTF_DOCCOMM, xDocProps->getDescription()); + + OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getAuthor()); + OutDateTime(OOO_STRING_SVTOOLS_RTF_CREATIM, xDocProps->getCreationDate()); + + OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getModifiedBy()); + OutDateTime(OOO_STRING_SVTOOLS_RTF_REVTIM, xDocProps->getModificationDate()); + + OutDateTime(OOO_STRING_SVTOOLS_RTF_PRINTIM, xDocProps->getPrintDate()); + } + + Strm().WriteChar('}'); +} + +void RtfExport::WriteUserPropType(int nType) +{ + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PROPTYPE); + OutULong(nType); +} + +void RtfExport::WriteUserPropValue(const OUString& rValue) +{ + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_STATICVAL " "); + Strm().WriteOString(msfilter::rtfutil::OutString(rValue, m_eDefaultEncoding)); + Strm().WriteChar('}'); +} + +void RtfExport::WriteUserProps() +{ + Strm().WriteChar('{').WriteCharPtr( + OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_USERPROPS); + + SwDocShell* pDocShell(m_pDoc->GetDocShell()); + uno::Reference<document::XDocumentProperties> xDocProps; + if (pDocShell) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(), + uno::UNO_QUERY); + xDocProps.set(xDPS->getDocumentProperties()); + } + else + { + // Clipboard document, read metadata from the meta field manager. + sw::MetaFieldManager& rManager = m_pDoc->GetMetaFieldManager(); + xDocProps.set(rManager.getDocumentProperties()); + } + + if (xDocProps.is()) + { + // Handle user-defined properties. + uno::Reference<beans::XPropertyContainer> xUserDefinedProperties + = xDocProps->getUserDefinedProperties(); + if (xUserDefinedProperties.is()) + { + uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties, + uno::UNO_QUERY); + const uno::Sequence<beans::Property> aProperties + = xPropertySet->getPropertySetInfo()->getProperties(); + + for (const beans::Property& rProperty : aProperties) + { + if (rProperty.Name.startsWith("Company")) + // We have explicit markup in RTF for this property. + continue; + + // Property name. + Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PROPNAME " "); + Strm().WriteCharPtr( + msfilter::rtfutil::OutString(rProperty.Name, m_eDefaultEncoding).getStr()); + Strm().WriteChar('}'); + + // Property value. + OUString aValue; + double fValue; + bool bValue; + util::DateTime aDate; + uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name); + if (aAny >>= bValue) + { + WriteUserPropType(11); + WriteUserPropValue(OUString::number(static_cast<int>(bValue))); + } + else if (aAny >>= aValue) + { + WriteUserPropType(30); + WriteUserPropValue(aValue); + } + else if (aAny >>= fValue) + { + aValue = OUString::number(fValue); + if (aValue.indexOf('.') == -1) + { + // Integer. + WriteUserPropType(3); + WriteUserPropValue(aValue); + } + else + { + // Real number. + WriteUserPropType(5); + WriteUserPropValue(aValue); + } + } + else if (aAny >>= aDate) + { + WriteUserPropType(64); + // Format is 'YYYY. MM. DD.'. + aValue += OUString::number(aDate.Year) + ". "; + if (aDate.Month < 10) + aValue += "0"; + aValue += OUString::number(aDate.Month) + ". "; + if (aDate.Day < 10) + aValue += "0"; + aValue += OUString::number(aDate.Day) + "."; + WriteUserPropValue(aValue); + } + } + } + } + + Strm().WriteChar('}'); +} + +void RtfExport::WritePageDescTable() +{ + // Write page descriptions (page styles) + std::size_t nSize = m_pDoc->GetPageDescCnt(); + if (!nSize) + return; + + Strm().WriteCharPtr(SAL_NEWLINE_STRING); + m_bOutPageDescs = true; + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCTBL); + for (std::size_t n = 0; n < nSize; ++n) + { + const SwPageDesc& rPageDesc = m_pDoc->GetPageDesc(n); + + Strm() + .WriteCharPtr(SAL_NEWLINE_STRING) + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSC); + OutULong(n).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCUSE); + OutULong(static_cast<sal_uLong>(rPageDesc.ReadUseOn())); + + OutPageDescription(rPageDesc, false); + + // search for the next page description + std::size_t i = nSize; + while (i) + if (rPageDesc.GetFollow() == &m_pDoc->GetPageDesc(--i)) + break; + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNXT); + OutULong(i).WriteChar(' '); + Strm() + .WriteCharPtr( + msfilter::rtfutil::OutString(rPageDesc.GetName(), m_eDefaultEncoding).getStr()) + .WriteCharPtr(";}"); + } + Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING); + m_bOutPageDescs = false; + + // reset table infos, otherwise the depth of the cells will be incorrect, + // in case the page style (header or footer) had tables + m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); +} + +ErrCode RtfExport::ExportDocument_Impl() +{ + // Make the header + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RTF) + .WriteChar('1') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_ANSI); + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DEFF); + OutULong(m_aFontHelper.GetId(m_pDoc->GetAttrPool().GetDefaultItem(RES_CHRATR_FONT))); + // If this not exist, MS don't understand our ansi characters (0x80-0xff). + Strm().WriteCharPtr("\\adeflang1025"); + + // Font table + WriteFonts(); + + m_pStyles = std::make_unique<MSWordStyles>(*this); + // Color and stylesheet table + WriteStyles(); + + // List table + BuildNumbering(); + WriteNumbering(); + + WriteRevTab(); + + WriteInfo(); + WriteUserProps(); + // Default TabSize + Strm() + .WriteOString(m_pAttrOutput->GetTabStop().makeStringAndClear()) + .WriteCharPtr(SAL_NEWLINE_STRING); + + // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer. + // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there. + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HYPHAUTO); + OutULong(1); + + // Zoom + SwViewShell* pViewShell(m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()); + if (pViewShell && pViewShell->GetViewOptions()->GetZoomType() == SvxZoomType::PERCENT) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_VIEWSCALE); + OutULong(pViewShell->GetViewOptions()->GetZoom()); + } + // Record changes? + if (RedlineFlags::On & m_nOrigRedlineFlags) + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVISIONS); + // Mirror margins? + if ((UseOnPage::Mirror & m_pDoc->GetPageDesc(0).ReadUseOn()) == UseOnPage::Mirror) + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGMIRROR); + // Init sections + m_pSections = new MSWordSections(*this); + + // Page description + WritePageDescTable(); + + // Enable form protection by default if needed, as there is no switch to + // enable it on a per-section basis. OTOH don't always enable it as it + // breaks moving of drawings - so write it only in case there is really a + // protected section in the document. + for (auto const& pSectionFormat : m_pDoc->GetSections()) + { + if (!pSectionFormat->IsInUndo() && pSectionFormat->GetProtect().IsContentProtected()) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMPROT); + break; + } + } + + // enable form field shading + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMSHADE); + + // size and empty margins of the page + if (m_pDoc->GetPageDescCnt()) + { + // Seeking the first SwFormatPageDesc. If no set, the default is valid + const SwFormatPageDesc* pSttPgDsc = nullptr; + { + const SwNode& rSttNd + = *m_pDoc->GetNodes()[m_pDoc->GetNodes().GetEndOfExtras().GetIndex() + 2]; + const SfxItemSet* pSet = nullptr; + + if (rSttNd.IsContentNode()) + pSet = &rSttNd.GetContentNode()->GetSwAttrSet(); + else if (rSttNd.IsTableNode()) + pSet = &rSttNd.GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet(); + + else if (rSttNd.IsSectionNode()) + pSet = &rSttNd.GetSectionNode()->GetSection().GetFormat()->GetAttrSet(); + + if (pSet) + { + std::size_t nPosInDoc; + pSttPgDsc = &pSet->Get(RES_PAGEDESC); + if (!pSttPgDsc->GetPageDesc()) + pSttPgDsc = nullptr; + else if (m_pDoc->FindPageDesc(pSttPgDsc->GetPageDesc()->GetName(), &nPosInDoc)) + { + Strm() + .WriteChar('{') + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNO); + OutULong(nPosInDoc).WriteChar('}'); + } + } + } + const SwPageDesc& rPageDesc + = pSttPgDsc ? *pSttPgDsc->GetPageDesc() : m_pDoc->GetPageDesc(0); + const SwFrameFormat& rFormatPage = rPageDesc.GetMaster(); + + { + if (rPageDesc.GetLandscape()) + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LANDSCAPE); + + const SwFormatFrameSize& rSz = rFormatPage.GetFrameSize(); + // Clipboard document is always created without a printer, then + // the size will be always LONG_MAX! Solution then is to use A4 + if (LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth()) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH); + Size a4 = SvxPaperInfo::GetPaperSize(PAPER_A4); + OutULong(a4.Height()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW); + OutULong(a4.Width()); + } + else + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH); + OutULong(rSz.GetHeight()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW); + OutULong(rSz.GetWidth()); + } + } + + { + const SvxLRSpaceItem& rLR = rFormatPage.GetLRSpace(); + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGL); + OutLong(rLR.GetLeft()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGR); + OutLong(rLR.GetRight()); + } + + { + const SvxULSpaceItem& rUL = rFormatPage.GetULSpace(); + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGT); + OutLong(rUL.GetUpper()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGB); + OutLong(rUL.GetLower()); + } + + Strm() + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTD) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SBKNONE); + m_pAttrOutput->SectFootnoteEndnotePr(); + // All sections are unlocked by default + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED); + OutLong(1); + OutPageDescription(rPageDesc, true); // Changed bCheckForFirstPage to true so headers + // following title page are correctly added - i13107 + if (pSttPgDsc) + { + m_pCurrentPageDesc = &rPageDesc; + } + } + + // line numbering + const SwLineNumberInfo& rLnNumInfo = m_pDoc->GetLineNumberInfo(); + if (rLnNumInfo.IsPaintLineNumbers()) + { + sal_uLong nLnNumRestartNo = 0; + if (const WW8_SepInfo* pSectionInfo = m_pSections->CurrentSectionInfo()) + nLnNumRestartNo = pSectionInfo->nLnNumRestartNo; + + AttrOutput().SectionLineNumbering(nLnNumRestartNo, rLnNumInfo); + } + + { + // write the footnotes and endnotes-out Info + const SwFootnoteInfo& rFootnoteInfo = m_pDoc->GetFootnoteInfo(); + + const char* pOut = FTNPOS_CHAPTER == rFootnoteInfo.m_ePos ? OOO_STRING_SVTOOLS_RTF_ENDDOC + : OOO_STRING_SVTOOLS_RTF_FTNBJ; + Strm().WriteCharPtr(pOut).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSTART); + OutLong(rFootnoteInfo.m_nFootnoteOffset + 1); + + switch (rFootnoteInfo.m_eNum) + { + case FTNNUM_PAGE: + pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTPG; + break; + case FTNNUM_DOC: + pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTCONT; + break; + default: + pOut = OOO_STRING_SVTOOLS_RTF_FTNRESTART; + break; + } + Strm().WriteCharPtr(pOut); + + switch (rFootnoteInfo.m_aFormat.GetNumberingType()) + { + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNALC; + break; + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNAUC; + break; + case SVX_NUM_ROMAN_LOWER: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNRLC; + break; + case SVX_NUM_ROMAN_UPPER: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNRUC; + break; + case SVX_NUM_SYMBOL_CHICAGO: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNCHI; + break; + default: + pOut = OOO_STRING_SVTOOLS_RTF_FTNNAR; + break; + } + Strm().WriteCharPtr(pOut); + + const SwEndNoteInfo& rEndNoteInfo = m_pDoc->GetEndNoteInfo(); + + Strm() + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AENDDOC) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNRSTCONT) + .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNSTART); + OutLong(rEndNoteInfo.m_nFootnoteOffset + 1); + + switch (rEndNoteInfo.m_aFormat.GetNumberingType()) + { + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNALC; + break; + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAUC; + break; + case SVX_NUM_ROMAN_LOWER: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRLC; + break; + case SVX_NUM_ROMAN_UPPER: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRUC; + break; + case SVX_NUM_SYMBOL_CHICAGO: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNCHI; + break; + default: + pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAR; + break; + } + Strm().WriteCharPtr(pOut); + } + + if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX)) + // RTF default is true, so write compat flag if this should be false. + Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_HTMAUTSP); + + Strm().WriteCharPtr(SAL_NEWLINE_STRING); + + WriteFootnoteSettings(); + + WriteMainText(); + + Strm().WriteChar('}'); + + return ERRCODE_NONE; +} + +void RtfExport::PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc) +{ + const SwSectionFormat* pFormat = GetSectionFormat(rNd); + const sal_uLong nLnNm = GetSectionLineNo(pSet, rNd); + + OSL_ENSURE(pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided."); + + if (pNewPgDescFormat) + m_pSections->AppendSection(*pNewPgDescFormat, rNd, pFormat, nLnNm); + else if (pNewPgDesc) + m_pSections->AppendSection(pNewPgDesc, rNd, pFormat, nLnNm); + + // Don't insert a page break, when we're changing page style just because the next page has to be a different one. + if (!m_pAttrOutput->GetPrevPageDesc() + || m_pAttrOutput->GetPrevPageDesc()->GetFollow() != pNewPgDesc) + AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo()); +} + +bool RtfExport::DisallowInheritingOutlineNumbering(const SwFormat& rFormat) +{ + bool bRet(false); + + if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false)) + { + if (const SwFormat* pParent = rFormat.DerivedFrom()) + { + if (static_cast<const SwTextFormatColl*>(pParent) + ->IsAssignedToListLevelOfOutlineStyle()) + { + // Level 9 disables the outline + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVEL).WriteInt32(9); + + bRet = true; + } + } + } + + return bRet; +} + +void RtfExport::OutputEndNode(const SwEndNode& rEndNode) +{ + if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode()) + // End node of a table: see if a section break should be written after the table. + AttrOutput().SectionBreaks(rEndNode); +} + +void RtfExport::OutputGrfNode(const SwGrfNode& /*rGrfNode*/) +{ + /* noop, see RtfAttributeOutput::FlyFrameGraphic */ +} + +void RtfExport::OutputOLENode(const SwOLENode& /*rOLENode*/) +{ + /* noop, see RtfAttributeOutput::FlyFrameOLE */ +} + +void RtfExport::OutputLinkedOLE(const OUString& /*rLinked*/) {} + +void RtfExport::OutputTextNode(SwTextNode& rNode) +{ + m_nCurrentNodeIndex = rNode.GetIndex(); + if (!m_bOutOutlineOnly || rNode.IsOutline()) + MSWordExportBase::OutputTextNode(rNode); + m_nCurrentNodeIndex = 0; +} + +void RtfExport::AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat, + sal_uLong nLnNum) +{ + m_pSections->AppendSection(pPageDesc, pFormat, nLnNum); + AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo()); +} + +RtfExport::RtfExport(RtfExportFilter* pFilter, SwDoc* pDocument, + std::shared_ptr<SwUnoCursor>& pCurrentPam, SwPaM* pOriginalPam, + Writer* pWriter, bool bOutOutlineOnly) + : MSWordExportBase(pDocument, pCurrentPam, pOriginalPam) + , m_pFilter(pFilter) + , m_pWriter(pWriter) + , m_pSections(nullptr) + , m_bOutOutlineOnly(bOutOutlineOnly) + , m_eDefaultEncoding( + rtl_getTextEncodingFromWindowsCharset(sw::ms::rtl_TextEncodingToWinCharset(DEF_ENCODING))) + , m_eCurrentEncoding(m_eDefaultEncoding) + , m_bRTFFlySyntax(false) + , m_nCurrentNodeIndex(0) +{ + m_bExportModeRTF = true; + // the attribute output for the document + m_pAttrOutput = std::make_unique<RtfAttributeOutput>(*this); + // that just causes problems for RTF + m_bSubstituteBullets = false; + // needed to have a complete font table + m_aFontHelper.bLoadAllFonts = true; + // the related SdrExport + m_pSdrExport = std::make_unique<RtfSdrExport>(*this); + + if (!m_pWriter) + m_pWriter = &m_pFilter->GetWriter(); +} + +RtfExport::~RtfExport() = default; + +SvStream& RtfExport::Strm() +{ + if (m_pStream) + return *m_pStream; + + return m_pWriter->Strm(); +} + +void RtfExport::setStream() { m_pStream = std::make_unique<SvMemoryStream>(); } + +OString RtfExport::getStream() +{ + OString aRet; + + if (m_pStream) + aRet = OString(static_cast<const char*>(m_pStream->GetData()), m_pStream->Tell()); + + return aRet; +} + +void RtfExport::resetStream() { m_pStream.reset(); } + +SvStream& RtfExport::OutULong(sal_uLong nVal) { return Writer::OutULong(Strm(), nVal); } + +SvStream& RtfExport::OutLong(long nVal) { return Writer::OutLong(Strm(), nVal); } + +void RtfExport::OutUnicode(const char* pToken, const OUString& rContent, bool bUpr) +{ + if (!rContent.isEmpty()) + { + if (!bUpr) + { + Strm().WriteChar('{').WriteCharPtr(pToken).WriteChar(' '); + Strm().WriteCharPtr( + msfilter::rtfutil::OutString(rContent, m_eCurrentEncoding).getStr()); + Strm().WriteChar('}'); + } + else + Strm().WriteCharPtr( + msfilter::rtfutil::OutStringUpr(pToken, rContent, m_eCurrentEncoding).getStr()); + } +} + +void RtfExport::OutDateTime(const char* pStr, const util::DateTime& rDT) +{ + Strm().WriteChar('{').WriteCharPtr(pStr).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_YR); + OutULong(rDT.Year).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MO); + OutULong(rDT.Month).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DY); + OutULong(rDT.Day).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HR); + OutULong(rDT.Hours).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MIN); + OutULong(rDT.Minutes).WriteChar('}'); +} + +sal_uInt16 RtfExport::GetColor(const Color& rColor) const +{ + for (const auto& rEntry : m_aColTable) + if (rEntry.second == rColor) + { + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " returning " << rEntry.first << " (" + << rColor.GetRed() << "," << rColor.GetGreen() << "," + << rColor.GetBlue() << ")"); + return rEntry.first; + } + OSL_FAIL("No such Color in m_aColTable!"); + return 0; +} + +void RtfExport::InsColor(const Color& rCol) +{ + sal_uInt16 n; + bool bAutoColorInTable = false; + for (const auto& rEntry : m_aColTable) + { + if (rEntry.second == rCol) + return; // Already in the table + if (rEntry.second == COL_AUTO) + bAutoColorInTable = true; + } + if (rCol == COL_AUTO) + // COL_AUTO gets value 0 + n = 0; + else + { + // other colors get values >0 + n = m_aColTable.size(); + if (!bAutoColorInTable) + // reserve value "0" for COL_AUTO (if COL_AUTO wasn't inserted until now) + n++; + } + m_aColTable.insert(std::pair<sal_uInt16, Color>(n, rCol)); +} + +void RtfExport::InsColorLine(const SvxBoxItem& rBox) +{ + const editeng::SvxBorderLine* pLine = nullptr; + + if (rBox.GetTop()) + { + pLine = rBox.GetTop(); + InsColor(pLine->GetColor()); + } + if (rBox.GetBottom() && pLine != rBox.GetBottom()) + { + pLine = rBox.GetBottom(); + InsColor(pLine->GetColor()); + } + if (rBox.GetLeft() && pLine != rBox.GetLeft()) + { + pLine = rBox.GetLeft(); + InsColor(pLine->GetColor()); + } + if (rBox.GetRight() && pLine != rBox.GetRight()) + InsColor(rBox.GetRight()->GetColor()); +} + +void RtfExport::OutColorTable() +{ + // Build the table from rPool since the colors provided to + // RtfAttributeOutput callbacks are too late. + const SfxItemPool& rPool = m_pDoc->GetAttrPool(); + + // MSO Word uses a default color table with 16 colors (which is used e.g. for highlighting) + InsColor(COL_BLACK); + InsColor(COL_LIGHTBLUE); + InsColor(COL_LIGHTCYAN); + InsColor(COL_LIGHTGREEN); + InsColor(COL_LIGHTMAGENTA); + InsColor(COL_LIGHTRED); + InsColor(COL_YELLOW); + InsColor(COL_WHITE); + InsColor(COL_BLUE); + InsColor(COL_CYAN); + InsColor(COL_GREEN); + InsColor(COL_MAGENTA); + InsColor(COL_RED); + InsColor(COL_BROWN); + InsColor(COL_GRAY); + InsColor(COL_LIGHTGRAY); + + // char color + { + auto pCol = GetDfltAttr(RES_CHRATR_COLOR); + InsColor(pCol->GetValue()); + if ((pCol = rPool.GetPoolDefaultItem(RES_CHRATR_COLOR))) + InsColor(pCol->GetValue()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_COLOR)) + { + if ((pCol = dynamic_cast<const SvxColorItem*>(pItem))) + InsColor(pCol->GetValue()); + } + + auto pUnder = GetDfltAttr(RES_CHRATR_UNDERLINE); + InsColor(pUnder->GetColor()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_UNDERLINE)) + { + if ((pUnder = dynamic_cast<const SvxUnderlineItem*>(pItem))) + InsColor(pUnder->GetColor()); + } + + auto pOver = GetDfltAttr(RES_CHRATR_OVERLINE); + InsColor(pOver->GetColor()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_OVERLINE)) + { + if ((pOver = dynamic_cast<const SvxOverlineItem*>(pItem))) + InsColor(pOver->GetColor()); + } + } + + // background color + static const sal_uInt16 aBrushIds[] = { RES_BACKGROUND, RES_CHRATR_BACKGROUND, 0 }; + + for (const sal_uInt16* pIds = aBrushIds; *pIds; ++pIds) + { + auto pBackground = static_cast<const SvxBrushItem*>(GetDfltAttr(*pIds)); + InsColor(pBackground->GetColor()); + if ((pBackground = static_cast<const SvxBrushItem*>(rPool.GetPoolDefaultItem(*pIds)))) + { + InsColor(pBackground->GetColor()); + } + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pIds)) + { + if ((pBackground = static_cast<const SvxBrushItem*>(pItem))) + { + InsColor(pBackground->GetColor()); + } + } + } + + // shadow color + { + auto pShadow = GetDfltAttr(RES_SHADOW); + InsColor(pShadow->GetColor()); + if (nullptr != (pShadow = rPool.GetPoolDefaultItem(RES_SHADOW))) + { + InsColor(pShadow->GetColor()); + } + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_SHADOW)) + { + if ((pShadow = dynamic_cast<const SvxShadowItem*>(pItem))) + { + InsColor(pShadow->GetColor()); + } + } + } + + // frame border color + { + const SvxBoxItem* pBox; + if (nullptr != (pBox = rPool.GetPoolDefaultItem(RES_BOX))) + InsColorLine(*pBox); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_BOX)) + { + if ((pBox = dynamic_cast<const SvxBoxItem*>(pItem))) + InsColorLine(*pBox); + } + } + + { + const SvxBoxItem* pCharBox; + if ((pCharBox = rPool.GetPoolDefaultItem(RES_CHRATR_BOX))) + InsColorLine(*pCharBox); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_BOX)) + { + if ((pCharBox = dynamic_cast<const SvxBoxItem*>(pItem))) + InsColorLine(*pCharBox); + } + } + + // TextFrame or paragraph background solid fill. + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLCOLOR)) + { + if (auto pColorItem = dynamic_cast<const XFillColorItem*>(pItem)) + InsColor(pColorItem->GetColorValue()); + } + + for (std::size_t n = 0; n < m_aColTable.size(); ++n) + { + const Color& rCol = m_aColTable[n]; + if (n || COL_AUTO != rCol) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RED); + OutULong(rCol.GetRed()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_GREEN); + OutULong(rCol.GetGreen()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_BLUE); + OutULong(rCol.GetBlue()); + } + Strm().WriteChar(';'); + } +} + +void RtfExport::InsStyle(sal_uInt16 nId, const OString& rStyle) +{ + m_aStyTable.insert(std::pair<sal_uInt16, OString>(nId, rStyle)); +} + +OString* RtfExport::GetStyle(sal_uInt16 nId) +{ + auto it = m_aStyTable.find(nId); + if (it != m_aStyTable.end()) + return &it->second; + return nullptr; +} + +sal_uInt16 RtfExport::GetRedline(const OUString& rAuthor) +{ + auto it = m_aRedlineTable.find(rAuthor); + if (it != m_aRedlineTable.end()) + return it->second; + + const sal_uInt16 nId = m_aRedlineTable.size(); + m_aRedlineTable.insert(std::pair<OUString, sal_uInt16>(rAuthor, nId)); + return nId; +} + +const OUString* RtfExport::GetRedline(sal_uInt16 nId) +{ + for (const auto& rEntry : m_aRedlineTable) + if (rEntry.second == nId) + return &rEntry.first; + return nullptr; +} + +void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + const SwPageDesc* pSave = m_pCurrentPageDesc; + + m_pCurrentPageDesc = &rPgDsc; + if (bCheckForFirstPage && m_pCurrentPageDesc->GetFollow() + && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc) + m_pCurrentPageDesc = m_pCurrentPageDesc->GetFollow(); + + if (m_pCurrentPageDesc->GetLandscape()) + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LNDSCPSXN); + + const SwFormat* pFormat = &m_pCurrentPageDesc->GetMaster(); //GetLeft(); + m_bOutPageDescs = true; + if (m_pCurrentPageDesc != &rPgDsc) + m_pFirstPageItemSet = &rPgDsc.GetMaster().GetAttrSet(); + OutputFormat(*pFormat, true, false); + m_pFirstPageItemSet = nullptr; + m_bOutPageDescs = false; + + // normal header / footer (without a style) + const SfxPoolItem* pItem; + if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem) + == SfxItemState::SET) + WriteHeaderFooter(*pItem, true); + if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem) + == SfxItemState::SET) + WriteHeaderFooter(*pItem, false); + + // title page + if (m_pCurrentPageDesc != &rPgDsc) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG); + m_pCurrentPageDesc = &rPgDsc; + if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem) + == SfxItemState::SET) + WriteHeaderFooter(*pItem, true); + if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem) + == SfxItemState::SET) + WriteHeaderFooter(*pItem, false); + } + + // numbering type + AttrOutput().SectionPageNumbering(m_pCurrentPageDesc->GetNumType().GetNumberingType(), + std::nullopt); + + m_pCurrentPageDesc = pSave; + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader) +{ + if (bHeader) + { + const auto& rHeader = static_cast<const SwFormatHeader&>(rItem); + if (!rHeader.IsActive()) + return; + } + else + { + const auto& rFooter = static_cast<const SwFormatFooter&>(rItem); + if (!rFooter.IsActive()) + return; + } + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + const char* pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER); + /* is this a title page? */ + if (m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc) + { + Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG); + pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF); + } + Strm().WriteChar('{').WriteCharPtr(pStr); + WriteHeaderFooterText(m_pCurrentPageDesc->GetMaster(), bHeader); + Strm().WriteChar('}'); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfExport::WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr, + bool bTitlepg) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + m_pAttrOutput->WriteHeaderFooter_Impl(rFormat, bHeader, pStr, bTitlepg); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +namespace +{ +/// Glue class to call RtfExport as an internal filter, needed by copy&paste support. +class SwRTFWriter : public Writer +{ +private: + bool m_bOutOutlineOnly; + +public: + SwRTFWriter(const OUString& rFilterName, const OUString& rBaseURL); + + ErrCode WriteStream() override; +}; +} + +SwRTFWriter::SwRTFWriter(const OUString& rFilterName, const OUString& rBaseURL) +{ + SetBaseURL(rBaseURL); + // export outline nodes, only (send outline to clipboard/presentation) + m_bOutOutlineOnly = rFilterName.startsWith("O"); +} + +ErrCode SwRTFWriter::WriteStream() +{ + std::shared_ptr<SwUnoCursor> pCurPam(m_pDoc->CreateUnoCursor(*m_pCurrentPam->End(), false)); + pCurPam->SetMark(); + *pCurPam->GetPoint() = *m_pCurrentPam->Start(); + RtfExport aExport(nullptr, m_pDoc, pCurPam, m_pCurrentPam.get(), this, m_bOutOutlineOnly); + aExport.ExportDocument(true); + return ERRCODE_NONE; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void ExportRTF(const OUString& rFltName, const OUString& rBaseURL, + WriterRef& xRet) +{ + xRet = new SwRTFWriter(rFltName, rBaseURL); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx new file mode 100644 index 000000000..0c0d35d1a --- /dev/null +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX + +#include <memory> +#include "wrtww8.hxx" + +class RtfAttributeOutput; +class RtfExportFilter; +class RtfSdrExport; +using RtfColorTable = std::map<sal_uInt16, Color>; +class SwNode; +class SwTextNode; +class SwGrfNode; +class SwOLENode; + +/// The class that does all the actual RTF export-related work. +class RtfExport : public MSWordExportBase +{ + /// Pointer to the filter that owns us. + RtfExportFilter* m_pFilter; + Writer* m_pWriter; + + /// Attribute output for document. + std::unique_ptr<RtfAttributeOutput> m_pAttrOutput; + + /// Sections/headers/footers + MSWordSections* m_pSections; + + std::unique_ptr<RtfSdrExport> m_pSdrExport; + bool m_bOutOutlineOnly; + +public: + /// Access to the attribute output class. + AttributeOutputBase& AttrOutput() const override; + + /// Access to the sections/headers/footres. + MSWordSections& Sections() const override; + + /// Access to the Rtf Sdr exporter. + RtfSdrExport& SdrExporter() const; + + bool FieldsQuoted() const override { return true; } + + bool AddSectionBreaksForTOX() const override { return false; } + + bool PreferPageBreakBefore() const override { return true; } + + /// Guess the script (asian/western). + bool CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich) override; + + void AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen) override; + + void AppendBookmark(const OUString& rName) override; + + void AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, + sal_Int32 nLen) override; + + //For i120928,add an interface to export graphic of bullet + void ExportGrfBullet(const SwTextNode& rNd) override; + + void + WriteCR(ww8::WW8TableNodeInfoInner:: + Pointer_t /*pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t()*/) + override + { + /* no-op for rtf, most probably should not even be in MSWordExportBase */ + } + void WriteChar(sal_Unicode c) override; + + /// Write the numbering table. + void WriteNumbering() override; + + /// Write the revision table. + void WriteRevTab(); + + /// Output the actual headers and footers. + void WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat, + const SwFrameFormat& rLeftFormat, + const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode) override; + + /// Write the field + void OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd, + FieldFlags nMode = FieldFlags::All) override; + + /// Write the data of the form field + void WriteFormData(const ::sw::mark::IFieldmark& rFieldmark) override; + void WriteHyperlinkData(const ::sw::mark::IFieldmark& rFieldmark) override; + + void DoComboBox(const OUString& rName, const OUString& rHelp, const OUString& ToolTip, + const OUString& rSelected, + const css::uno::Sequence<OUString>& rListItems) override; + + void DoFormText(const SwInputField* pField) override; + + sal_uLong ReplaceCr(sal_uInt8 nChar) override; + + ExportFormat GetExportFormat() const override { return ExportFormat::RTF; } + +protected: + /// Format-dependent part of the actual export. + ErrCode ExportDocument_Impl() override; + + void SectionBreaksAndFrames(const SwTextNode& /*rNode*/) override {} + + /// Get ready for a new section. + void PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc) override; + + /// Return value indicates if an inherited outline numbering is suppressed. + bool DisallowInheritingOutlineNumbering(const SwFormat& rFormat) override; + + /// Output SwTextNode is depending on outline export mode + void OutputTextNode(SwTextNode& rNode) override; + + /// Output SwEndNode + void OutputEndNode(const SwEndNode& rEndNode) override; + + /// Output SwGrfNode + void OutputGrfNode(const SwGrfNode& rGrfNode) override; + + /// Output SwOLENode + void OutputOLENode(const SwOLENode& rOLENode) override; + + void OutputLinkedOLE(const OUString& rLink) override; + + void AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat, + sal_uLong nLnNum) override; + +public: + /// Pass the pDocument, pCurrentPam and pOriginalPam to the base class. + RtfExport(RtfExportFilter* pFilter, SwDoc* pDocument, std::shared_ptr<SwUnoCursor>& pCurrentPam, + SwPaM* pOriginalPam, Writer* pWriter, bool bOutOutlineOnly = false); + + RtfExport(const RtfExport&) = delete; + + RtfExport& operator=(const RtfExport&) = delete; + + /// Destructor. + ~RtfExport() override; + +private: + rtl_TextEncoding m_eDefaultEncoding; + rtl_TextEncoding m_eCurrentEncoding; + /// This is used by OutputFlyFrame_Impl() to control the written syntax + bool m_bRTFFlySyntax; + /// Index of the current SwTextNode, if any. + sal_uLong m_nCurrentNodeIndex; + +public: + rtl_TextEncoding GetDefaultEncoding() const { return m_eDefaultEncoding; } + void SetCurrentEncoding(rtl_TextEncoding eCurrentEncoding) + { + m_eCurrentEncoding = eCurrentEncoding; + } + rtl_TextEncoding GetCurrentEncoding() const { return m_eCurrentEncoding; } + void SetRTFFlySyntax(bool bRTFFlySyntax) { m_bRTFFlySyntax = bRTFFlySyntax; } + bool GetRTFFlySyntax() const { return m_bRTFFlySyntax; } + sal_uLong GetCurrentNodeIndex() const { return m_nCurrentNodeIndex; } + SvStream& Strm(); + /// From now on, let Strm() return a memory stream, not a real one. + void setStream(); + /// Get the contents of the memory stream as a string. + OString getStream(); + /// Return back to the real stream. + void resetStream(); + SvStream& OutULong(sal_uLong nVal); + SvStream& OutLong(long nVal); + void OutUnicode(const char* pToken, const OUString& rContent, bool bUpr = false); + void OutDateTime(const char* pStr, const css::util::DateTime& rDT); + void OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage); + + sal_uInt16 GetColor(const Color& rColor) const; + void InsColor(const Color& rCol); + void InsColorLine(const SvxBoxItem& rBox); + void OutColorTable(); + sal_uInt16 GetRedline(const OUString& rAuthor); + const OUString* GetRedline(sal_uInt16 nId); + + void InsStyle(sal_uInt16 nId, const OString& rStyle); + OString* GetStyle(sal_uInt16 nId); + + const SfxItemSet* GetFirstPageItemSet() const { return m_pFirstPageItemSet; } + +private: + void WriteFonts(); + void WriteStyles(); + void WriteFootnoteSettings(); + void WriteMainText(); + void WriteInfo(); + /// Writes a single user property type. + void WriteUserPropType(int nType); + /// Writes a single user property value. + void WriteUserPropValue(const OUString& rValue); + /// Writes the userprops group: user defined document properties. + void WriteUserProps(); + /// Writes the writer-specific \pgdsctbl group. + void WritePageDescTable(); + /// This is necessary to have the numbering table ready before the main text is being processed. + void BuildNumbering(); + void WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader); + void WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr, + bool bTitlepg = false); + + RtfColorTable m_aColTable; + std::map<sal_uInt16, OString> m_aStyTable; + std::map<OUString, sal_uInt16> m_aRedlineTable; + /// If set, then Strm() returns this stream, instead of m_pWriter's stream. + std::unique_ptr<SvMemoryStream> m_pStream; + /// Item set of the first page during export of a follow page format. + const SfxItemSet* m_pFirstPageItemSet = nullptr; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfexportfilter.cxx b/sw/source/filter/ww8/rtfexportfilter.cxx new file mode 100644 index 000000000..f8c28d9f9 --- /dev/null +++ b/sw/source/filter/ww8/rtfexportfilter.cxx @@ -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 . + */ + +#include "rtfexportfilter.hxx" +#include "rtfexport.hxx" + +#include <docsh.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <unotxdoc.hxx> +#include <viewsh.hxx> + +#include <unotools/mediadescriptor.hxx> +#include <unotools/ucbstreamhelper.hxx> + +using namespace ::com::sun::star; + +RtfExportFilter::RtfExportFilter(uno::Reference<uno::XComponentContext> xCtx) + : m_xCtx(std::move(xCtx)) +{ +} + +RtfExportFilter::~RtfExportFilter() = default; + +sal_Bool RtfExportFilter::filter(const uno::Sequence<beans::PropertyValue>& aDescriptor) +{ + utl::MediaDescriptor aMediaDesc = aDescriptor; + uno::Reference<io::XStream> xStream = aMediaDesc.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_STREAMFOROUTPUT(), uno::Reference<io::XStream>()); + std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream(xStream, true); + m_aWriter.SetStream(pStream.get()); + + // get SwDoc* + uno::Reference<uno::XInterface> xIfc(m_xSrcDoc, uno::UNO_QUERY); + auto pTextDoc = dynamic_cast<SwXTextDocument*>(xIfc.get()); + if (!pTextDoc) + { + return false; + } + + SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); + if (!pDoc) + { + return false; + } + + // fdo#37161 - update layout (if present), for SwWriteTable + SwViewShell* pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if (pViewShell != nullptr) + pViewShell->CalcLayout(); + + // get SwPaM* + // we get SwPaM for the entire document; copy&paste is handled internally, not via UNO + SwPaM aPam(pDoc->GetNodes().GetEndOfContent()); + aPam.SetMark(); + aPam.Move(fnMoveBackward, GoInDoc); + + std::shared_ptr<SwUnoCursor> pCurPam(pDoc->CreateUnoCursor(*aPam.End(), false)); + pCurPam->SetMark(); + *pCurPam->GetPoint() = *aPam.Start(); + + // export the document + // (in a separate block so that it's destructed before the commit) + { + RtfExport aExport(this, pDoc, pCurPam, &aPam, nullptr); + aExport.ExportDocument(true); + } + + // delete the pCurPam + while (pCurPam->GetNext() != pCurPam.get()) + delete pCurPam->GetNext(); + + return true; +} + +void RtfExportFilter::cancel() {} + +void RtfExportFilter::setSourceDocument(const uno::Reference<lang::XComponent>& xDoc) +{ + m_xSrcDoc = xDoc; +} + +// UNO helpers + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Writer_RtfExport_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new RtfExportFilter(pCtx)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfexportfilter.hxx b/sw/source/filter/ww8/rtfexportfilter.hxx new file mode 100644 index 000000000..b7603c1de --- /dev/null +++ b/sw/source/filter/ww8/rtfexportfilter.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_FILTER_WW8_RTFEXPORTFILTER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORTFILTER_HXX + +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <cppuhelper/implbase.hxx> +#include <shellio.hxx> + +namespace com +{ +namespace sun +{ +namespace star +{ +namespace uno +{ +class XComponentContext; +} +} +} +} + +/// Dummy Writer implementation to be able to use the string format methods of the base class +class RtfWriter : public Writer +{ +protected: + ErrCode WriteStream() override { return ERRCODE_NONE; } +}; + +/// The physical access to the RTF document (for writing). +class RtfExportFilter final + : public cppu::WeakImplHelper<css::document::XFilter, css::document::XExporter> +{ + css::uno::Reference<css::uno::XComponentContext> m_xCtx; + css::uno::Reference<css::lang::XComponent> m_xSrcDoc; + RtfWriter m_aWriter; + +public: + explicit RtfExportFilter(css::uno::Reference<css::uno::XComponentContext> xCtx); + ~RtfExportFilter() override; + + // XFilter + sal_Bool SAL_CALL + filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor) override; + void SAL_CALL cancel() override; + + // XExporter + void SAL_CALL + setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDoc) override; + + Writer& GetWriter() { return m_aWriter; } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFEXPORTFILTER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfsdrexport.cxx b/sw/source/filter/ww8/rtfsdrexport.cxx new file mode 100644 index 000000000..0aa351aa4 --- /dev/null +++ b/sw/source/filter/ww8/rtfsdrexport.cxx @@ -0,0 +1,762 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "rtfsdrexport.hxx" +#include <memory> +#include "rtfattributeoutput.hxx" +#include <svtools/rtfkeywd.hxx> +#include <svtools/unitconv.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <editeng/editobj.hxx> +#include <editeng/editids.hrc> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <svx/svdotext.hxx> +#include <svx/unoapi.hxx> +#include <vcl/cvtgrf.hxx> +#include <textboxhelper.hxx> +#include <dcontact.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <algorithm> +#include "rtfexport.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +using namespace css; + +RtfSdrExport::RtfSdrExport(RtfExport& rExport) + : EscherEx(std::make_shared<EscherExGlobal>(), nullptr) + , m_rExport(rExport) + , m_rAttrOutput(static_cast<RtfAttributeOutput&>(m_rExport.AttrOutput())) + , m_pSdrObject(nullptr) + , m_nShapeType(ESCHER_ShpInst_Nil) + , m_nShapeFlags(ShapeFlag::NONE) + , m_aShapeStyle(200) + , m_pShapeTypeWritten(new bool[ESCHER_ShpInst_COUNT]) +{ + mnGroupLevel = 1; + memset(m_pShapeTypeWritten.get(), 0, ESCHER_ShpInst_COUNT * sizeof(bool)); +} + +RtfSdrExport::~RtfSdrExport() +{ + delete mpOutStrm; + mpOutStrm = nullptr; +} + +void RtfSdrExport::OpenContainer(sal_uInt16 nEscherContainer, int nRecInstance) +{ + EscherEx::OpenContainer(nEscherContainer, nRecInstance); + + if (nEscherContainer == ESCHER_SpContainer) + { + m_nShapeType = ESCHER_ShpInst_Nil; + m_aShapeStyle.setLength(0); + m_aShapeStyle.ensureCapacity(200); + m_aShapeProps.clear(); + } +} + +void RtfSdrExport::CloseContainer() +{ + if (mRecTypes.back() == ESCHER_SpContainer) + { + // write the shape now when we have all the info + sal_Int32 nShapeElement = StartShape(); + EndShape(nShapeElement); + + // cleanup + m_nShapeType = ESCHER_ShpInst_Nil; + } + + EscherEx::CloseContainer(); +} + +sal_uInt32 RtfSdrExport::EnterGroup(const OUString& /*rShapeName*/, + const tools::Rectangle* /*pRect*/) +{ + m_bInGroup = true; + return GenerateShapeId(); +} + +void RtfSdrExport::LeaveGroup() { m_bInGroup = false; } + +void RtfSdrExport::AddShape(sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 /*nShapeId*/) +{ + m_nShapeType = nShapeType; + m_nShapeFlags = nShapeFlags; +} + +static sal_uInt16 impl_GetUInt16(const sal_uInt8*& pVal) +{ + sal_uInt16 nRet = *pVal++; + nRet += (*pVal++) << 8; + return nRet; +} + +static sal_Int32 impl_GetPointComponent(const sal_uInt8*& pVal, std::size_t& rVerticesPos, + sal_uInt16 nPointSize) +{ + sal_Int32 nRet = 0; + if ((nPointSize == 0xfff0) || (nPointSize == 4)) + { + sal_uInt16 nUnsigned = *pVal++; + nUnsigned += (*pVal++) << 8; + rVerticesPos += 2; + + nRet = sal_Int16(nUnsigned); + } + else if (nPointSize == 8) + { + sal_uInt32 nUnsigned = *pVal++; + nUnsigned += (*pVal++) << 8; + nUnsigned += (*pVal++) << 16; + nUnsigned += (*pVal++) << 24; + rVerticesPos += 4; + + nRet = nUnsigned; + } + + return nRet; +} + +void RtfSdrExport::Commit(EscherPropertyContainer& rProps, const tools::Rectangle& rRect) +{ + if (m_nShapeType == ESCHER_ShpInst_Nil) + return; + + if (m_nShapeType == ESCHER_ShpInst_Line) + AddLineDimensions(rRect); + else + AddRectangleDimensions(m_aShapeStyle, rRect); + + // properties + const EscherProperties& rOpts = rProps.GetOpts(); + for (const auto& rOpt : rOpts) + { + sal_uInt16 nId = (rOpt.nPropId & 0x0FFF); + + switch (nId) + { + case ESCHER_Prop_WrapText: + { + int nWrapType = 0; + switch (rOpt.nPropValue) + { + case ESCHER_WrapSquare: + nWrapType = 2; + break; + case ESCHER_WrapByPoints: + nWrapType = 4; + break; + case ESCHER_WrapNone: + nWrapType = 3; + break; + case ESCHER_WrapTopBottom: + nWrapType = 1; + break; + case ESCHER_WrapThrough: + nWrapType = 5; + break; + } + if (nWrapType) + m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPWR) + .append(static_cast<sal_Int32>(nWrapType)); + } + break; + case ESCHER_Prop_fillColor: + m_aShapeProps.insert( + std::pair<OString, OString>("fillColor", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fillBackColor: + m_aShapeProps.insert( + std::pair<OString, OString>("fillBackColor", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_AnchorText: + m_aShapeProps.insert( + std::pair<OString, OString>("anchorText", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fNoFillHitTest: + if (rOpt.nPropValue) + m_aShapeProps.insert( + std::pair<OString, OString>("fNoFillHitTest", OString::number(1))); + break; + case ESCHER_Prop_fNoLineDrawDash: + // for some reason the value is set to 0x90000 if lines are switched off + if (rOpt.nPropValue == 0x90000) + m_aShapeProps.insert(std::pair<OString, OString>("fLine", OString::number(0))); + break; + case ESCHER_Prop_lineColor: + m_aShapeProps.insert( + std::pair<OString, OString>("lineColor", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_lineBackColor: + m_aShapeProps.insert( + std::pair<OString, OString>("lineBackColor", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_lineJoinStyle: + m_aShapeProps.insert( + std::pair<OString, OString>("lineJoinStyle", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fshadowObscured: + if (rOpt.nPropValue) + m_aShapeProps.insert(std::pair<OString, OString>("fshadowObscured", "1")); + break; + case ESCHER_Prop_geoLeft: + case ESCHER_Prop_geoTop: + { + sal_uInt32 nLeft = 0; + sal_uInt32 nTop = 0; + + if (nId == ESCHER_Prop_geoLeft) + { + nLeft = rOpt.nPropValue; + rProps.GetOpt(ESCHER_Prop_geoTop, nTop); + } + else + { + nTop = rOpt.nPropValue; + rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft); + } + + m_aShapeProps.insert( + std::pair<OString, OString>("geoLeft", OString::number(sal_Int32(nLeft)))); + m_aShapeProps.insert( + std::pair<OString, OString>("geoTop", OString::number(sal_Int32(nTop)))); + } + break; + + case ESCHER_Prop_geoRight: + case ESCHER_Prop_geoBottom: + { + sal_uInt32 nLeft = 0; + sal_uInt32 nRight = 0; + sal_uInt32 nTop = 0; + sal_uInt32 nBottom = 0; + rProps.GetOpt(ESCHER_Prop_geoLeft, nLeft); + rProps.GetOpt(ESCHER_Prop_geoTop, nTop); + + if (nId == ESCHER_Prop_geoRight) + { + nRight = rOpt.nPropValue; + rProps.GetOpt(ESCHER_Prop_geoBottom, nBottom); + } + else + { + nBottom = rOpt.nPropValue; + rProps.GetOpt(ESCHER_Prop_geoRight, nRight); + } + + m_aShapeProps.insert(std::pair<OString, OString>( + "geoRight", OString::number(sal_Int32(nRight) - sal_Int32(nLeft)))); + m_aShapeProps.insert(std::pair<OString, OString>( + "geoBottom", OString::number(sal_Int32(nBottom) - sal_Int32(nTop)))); + } + break; + case ESCHER_Prop_pVertices: + case ESCHER_Prop_pSegmentInfo: + { + EscherPropSortStruct aVertices; + EscherPropSortStruct aSegments; + + if (rProps.GetOpt(ESCHER_Prop_pVertices, aVertices) + && rProps.GetOpt(ESCHER_Prop_pSegmentInfo, aSegments) + && aVertices.nProp.size() >= 6 && aSegments.nProp.size() >= 6) + { + const sal_uInt8* pVerticesIt = aVertices.nProp.data() + 6; + std::size_t nVerticesPos = 6; + const sal_uInt8* pSegmentIt = aSegments.nProp.data(); + + OStringBuffer aSegmentInfo(512); + OStringBuffer aVerticies(512); + + sal_uInt16 nPointSize = aVertices.nProp[4] + (aVertices.nProp[5] << 8); + + // number of segments + sal_uInt16 nSegments = impl_GetUInt16(pSegmentIt); + sal_Int32 nVertices = 0; + aSegmentInfo.append("2;").append(static_cast<sal_Int32>(nSegments)); + pSegmentIt += 4; + + for (; nSegments; --nSegments) + { + sal_uInt16 nSeg = impl_GetUInt16(pSegmentIt); + + // The segment type is stored in the upper 3 bits + // and segment count is stored in the lower 13 + // bits. + unsigned char nSegmentType = (nSeg & 0xE000) >> 13; + unsigned short nSegmentCount = nSeg & 0x03FF; + + aSegmentInfo.append(';').append(static_cast<sal_Int32>(nSeg)); + switch (nSegmentType) + { + case msopathLineTo: + for (unsigned short i = 0; i < nSegmentCount; ++i) + { + sal_Int32 nX = impl_GetPointComponent(pVerticesIt, nVerticesPos, + nPointSize); + sal_Int32 nY = impl_GetPointComponent(pVerticesIt, nVerticesPos, + nPointSize); + aVerticies.append(";(") + .append(nX) + .append(",") + .append(nY) + .append(")"); + nVertices++; + } + break; + case msopathMoveTo: + { + sal_Int32 nX + = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize); + sal_Int32 nY + = impl_GetPointComponent(pVerticesIt, nVerticesPos, nPointSize); + aVerticies.append(";(").append(nX).append(",").append(nY).append( + ")"); + nVertices++; + break; + } + case msopathCurveTo: + for (unsigned short j = 0; j < nSegmentCount; ++j) + { + for (int i = 0; i < 3; i++) + { + sal_Int32 nX = impl_GetPointComponent( + pVerticesIt, nVerticesPos, nPointSize); + sal_Int32 nY = impl_GetPointComponent( + pVerticesIt, nVerticesPos, nPointSize); + aVerticies.append(";(") + .append(nX) + .append(",") + .append(nY) + .append(")"); + nVertices++; + } + } + break; + case msopathEscape: + { + // If the segment type is msopathEscape, the lower 13 bits are + // divided in a 5 bit escape code and 8 bit + // vertex count (not segment count!) + unsigned char nVertexCount = nSegmentCount & 0x00FF; + nVerticesPos += nVertexCount; + break; + } + case msopathClientEscape: + case msopathClose: + case msopathEnd: + break; + default: + SAL_WARN("sw.rtf", "Totally b0rked"); + break; + } + } + + if (!aVerticies.isEmpty()) + { + // We know the number of vertices at the end only, so we have to prepend them here. + m_aShapeProps.insert(std::pair<OString, OString>( + "pVerticies", + "8;" + OString::number(nVertices) + aVerticies.makeStringAndClear())); + } + if (!aSegmentInfo.isEmpty()) + m_aShapeProps.insert(std::pair<OString, OString>( + "pSegmentInfo", aSegmentInfo.makeStringAndClear())); + } + else + SAL_INFO( + "sw.rtf", + OSL_THIS_FUNC + << ": unhandled shape path, missing either pVertices or pSegmentInfo"); + } + break; + case ESCHER_Prop_shapePath: + // noop, we use pSegmentInfo instead + break; + case ESCHER_Prop_fFillOK: + if (!rOpt.nPropValue) + m_aShapeProps.insert(std::pair<OString, OString>("fFillOK", "0")); + break; + case ESCHER_Prop_dxTextLeft: + m_aShapeProps.insert( + std::pair<OString, OString>("dxTextLeft", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_dyTextTop: + m_aShapeProps.insert( + std::pair<OString, OString>("dyTextTop", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_dxTextRight: + m_aShapeProps.insert( + std::pair<OString, OString>("dxTextRight", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_dyTextBottom: + m_aShapeProps.insert( + std::pair<OString, OString>("dyTextBottom", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_FitTextToShape: + // Size text to fit shape size: not supported by RTF + break; + case ESCHER_Prop_adjustValue: + m_aShapeProps.insert( + std::pair<OString, OString>("adjustValue", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_txflTextFlow: + m_aShapeProps.insert( + std::pair<OString, OString>("txflTextFlow", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fillType: + m_aShapeProps.insert( + std::pair<OString, OString>("fillType", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fillOpacity: + m_aShapeProps.insert( + std::pair<OString, OString>("fillOpacity", OString::number(rOpt.nPropValue))); + break; + case ESCHER_Prop_fillBlip: + { + OStringBuffer aBuf; + aBuf.append('{') + .append(OOO_STRING_SVTOOLS_RTF_PICT) + .append(OOO_STRING_SVTOOLS_RTF_PNGBLIP) + .append(SAL_NEWLINE_STRING); + int nHeaderSize + = 25; // The first bytes are WW8-specific, we're only interested in the PNG + aBuf.append(msfilter::rtfutil::WriteHex(rOpt.nProp.data() + nHeaderSize, + rOpt.nProp.size() - nHeaderSize)); + aBuf.append('}'); + m_aShapeProps.insert( + std::pair<OString, OString>("fillBlip", aBuf.makeStringAndClear())); + } + break; + default: + SAL_INFO("sw.rtf", OSL_THIS_FUNC << ": unhandled property: " << nId + << " (value: " << rOpt.nPropValue << ")"); + break; + } + } +} + +void RtfSdrExport::AddLineDimensions(const tools::Rectangle& rRectangle) +{ + // We get the position relative to (the current?) character + m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3")); + + if (m_nShapeFlags & ShapeFlag::FlipV) + m_aShapeProps.insert(std::pair<OString, OString>("fFlipV", "1")); + + if (m_nShapeFlags & ShapeFlag::FlipH) + m_aShapeProps.insert(std::pair<OString, OString>("fFlipH", "1")); + + // the actual dimensions + m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT).append(rRectangle.Left()); + m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPTOP).append(rRectangle.Top()); + m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT).append(rRectangle.Right()); + m_aShapeStyle.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM).append(rRectangle.Bottom()); +} + +void RtfSdrExport::AddRectangleDimensions(OStringBuffer& rBuffer, + const tools::Rectangle& rRectangle) +{ + // We get the position relative to (the current?) character + m_aShapeProps.insert(std::pair<OString, OString>("posrelh", "3")); + + rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPLEFT).append(rRectangle.Left()); + rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPTOP).append(rRectangle.Top()); + rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPRIGHT).append(rRectangle.Right()); + rBuffer.append(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM).append(rRectangle.Bottom()); +} + +extern const char* pShapeTypes[]; + +static void lcl_AppendSP(OStringBuffer& rRunText, const char cName[], const OString& rValue) +{ + rRunText.append('{') + .append(OOO_STRING_SVTOOLS_RTF_SP) + .append('{') + .append(OOO_STRING_SVTOOLS_RTF_SN " ") + .append(cName) + .append('}') + .append('{') + .append(OOO_STRING_SVTOOLS_RTF_SV " ") + .append(rValue) + .append('}') + .append('}'); +} + +void RtfSdrExport::impl_writeGraphic() +{ + // Get the Graphic object from the Sdr one. + uno::Reference<drawing::XShape> xShape + = GetXShapeForSdrObject(const_cast<SdrObject*>(m_pSdrObject)); + uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); + + uno::Reference<graphic::XGraphic> xGraphic; + + Graphic aGraphic; + + try + { + xPropertySet->getPropertyValue("Graphic") >>= xGraphic; + } + catch (beans::UnknownPropertyException const&) + { + DBG_UNHANDLED_EXCEPTION("sw.rtf"); + } + + if (xGraphic.is()) + { + aGraphic = Graphic(xGraphic); + } + + // Export it to a stream. + SvMemoryStream aStream; + (void)GraphicConverter::Export(aStream, aGraphic, ConvertDataFormat::PNG); + sal_uInt32 nSize = aStream.TellEnd(); + auto pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData()); + + Size aMapped(aGraphic.GetPrefSize()); + + // Add it to the properties. + RtfStringBuffer aBuf; + aBuf->append('{').append(OOO_STRING_SVTOOLS_RTF_PICT).append(OOO_STRING_SVTOOLS_RTF_PNGBLIP); + aBuf->append(OOO_STRING_SVTOOLS_RTF_PICW).append(sal_Int32(aMapped.Width())); + aBuf->append(OOO_STRING_SVTOOLS_RTF_PICH) + .append(sal_Int32(aMapped.Height())) + .append(SAL_NEWLINE_STRING); + aBuf->append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize)); + aBuf->append('}'); + m_aShapeProps.insert(std::pair<OString, OString>("pib", aBuf.makeStringAndClear())); +} + +sal_Int32 RtfSdrExport::StartShape() +{ + if (m_nShapeType == ESCHER_ShpInst_Nil) + return -1; + + m_aShapeProps.insert(std::pair<OString, OString>("shapeType", OString::number(m_nShapeType))); + if (ESCHER_ShpInst_PictureFrame == m_nShapeType) + impl_writeGraphic(); + + m_rAttrOutput.RunText().append('{').append(OOO_STRING_SVTOOLS_RTF_SHP); + m_rAttrOutput.RunText() + .append('{') + .append(OOO_STRING_SVTOOLS_RTF_IGNORE) + .append(OOO_STRING_SVTOOLS_RTF_SHPINST); + + m_rAttrOutput.RunText().append(m_aShapeStyle.makeStringAndClear()); + // Ignore \shpbxpage, \shpbxmargin, and \shpbxcolumn, in favor of the posrelh property. + m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE); + // Ignore \shpbypage, \shpbymargin, and \shpbycolumn, in favor of the posrelh property. + m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE); + + // Write ZOrder. + if (!m_bInGroup) + { + // Order inside the group shape is not relevant for the flat shape list + // we write. + m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_SHPZ); + m_rAttrOutput.RunText().append(OString::number(m_pSdrObject->GetOrdNum())); + } + + for (auto it = m_aShapeProps.rbegin(); it != m_aShapeProps.rend(); ++it) + lcl_AppendSP(m_rAttrOutput.RunText(), (*it).first.getStr(), (*it).second); + + lcl_AppendSP(m_rAttrOutput.RunText(), "wzDescription", + msfilter::rtfutil::OutString(m_pSdrObject->GetDescription(), + m_rExport.GetCurrentEncoding())); + lcl_AppendSP( + m_rAttrOutput.RunText(), "wzName", + msfilter::rtfutil::OutString(m_pSdrObject->GetName(), m_rExport.GetCurrentEncoding())); + + // now check if we have some text + const SwFrameFormat* pShape = FindFrameFormat(m_pSdrObject); + if (pShape) + { + if (SwFrameFormat* pTextBox + = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + { + ww8::Frame* pFrame = nullptr; + for (auto& rFrame : m_rExport.m_aFrames) + { + if (pTextBox == &rFrame.GetFrameFormat()) + { + pFrame = &rFrame; + break; + } + } + + if (pFrame) + m_rAttrOutput.writeTextFrame(*pFrame, /*bTextBox=*/true); + return m_nShapeType; + } + } + + auto pTextObj = dynamic_cast<const SdrTextObj*>(m_pSdrObject); + if (pTextObj) + { + const OutlinerParaObject* pParaObj = nullptr; + std::unique_ptr<const OutlinerParaObject> pOwnedParaObj; + + /* + #i13885# + When the object is actively being edited, that text is not set into + the objects normal text object, but lives in a separate object. + */ + if (pTextObj->IsTextEditActive()) + { + pOwnedParaObj = pTextObj->CreateEditOutlinerParaObject(); + pParaObj = pOwnedParaObj.get(); + } + else + { + pParaObj = pTextObj->GetOutlinerParaObject(); + } + + if (pParaObj) + { + // this is reached only in case some text is attached to the shape + // Watermark or TextBox? + if (pTextObj->TakeObjNameSingul().match("Text Frame")) + WriteOutliner(*pParaObj, TXT_HFTXTBOX); + else + { + const EditTextObject& rEditObj = pParaObj->GetTextObject(); + const SfxItemSet& rItemSet = rEditObj.GetParaAttribs(0); + + lcl_AppendSP(m_rAttrOutput.RunText(), "gtextUNICODE", + msfilter::rtfutil::OutString(rEditObj.GetText(0), + m_rExport.GetCurrentEncoding())); + + auto pFontFamily + = static_cast<const SvxFontItem*>(rItemSet.GetItem(SID_ATTR_CHAR_FONT)); + if (pFontFamily) + { + lcl_AppendSP(m_rAttrOutput.RunText(), "gtextFont", + msfilter::rtfutil::OutString(pFontFamily->GetFamilyName(), + m_rExport.GetCurrentEncoding())); + } + + auto pFontHeight = static_cast<const SvxFontHeightItem*>( + rItemSet.GetItem(SID_ATTR_CHAR_FONTHEIGHT)); + if (pFontHeight) + { + long nFontHeight = TransformMetric(pFontHeight->GetHeight(), FieldUnit::TWIP, + FieldUnit::POINT); + lcl_AppendSP( + m_rAttrOutput.RunText(), "gtextSize", + msfilter::rtfutil::OutString(OUString::number(nFontHeight * RTF_MULTIPLIER), + m_rExport.GetCurrentEncoding())); + } + + // RTF angle: 0-360 * 2^16 clockwise + // LO angle: 0-360 * 100 counter-clockwise + sal_Int32 nRotation + = -1 * pTextObj->GetGeoStat().nRotationAngle * RTF_MULTIPLIER / 100; + lcl_AppendSP(m_rAttrOutput.RunText(), "rotation", + msfilter::rtfutil::OutString(OUString::number(nRotation), + m_rExport.GetCurrentEncoding())); + } + } + } + + return m_nShapeType; +} + +void RtfSdrExport::WriteOutliner(const OutlinerParaObject& rParaObj, TextTypes eType) +{ + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " start"); + + const EditTextObject& rEditObj = rParaObj.GetTextObject(); + MSWord_SdrAttrIter aAttrIter(m_rExport, rEditObj, eType); + + sal_Int32 nPara = rEditObj.GetParagraphCount(); + + bool bShape = eType == TXT_HFTXTBOX; + if (bShape) + m_rAttrOutput.RunText().append('{').append(OOO_STRING_SVTOOLS_RTF_SHPTXT).append(' '); + for (sal_Int32 n = 0; n < nPara; ++n) + { + if (n) + aAttrIter.NextPara(n); + + rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet(); + + OUString aStr(rEditObj.GetText(n)); + sal_Int32 nCurrentPos = 0; + const sal_Int32 nEnd = aStr.getLength(); + + aAttrIter.OutParaAttr(false); + m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true)); + + do + { + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet(); + + aAttrIter.OutAttr(nCurrentPos); + m_rAttrOutput.RunText().append('{'); + m_rAttrOutput.RunText().append(m_rAttrOutput.MoveCharacterProperties(true)); + m_rAttrOutput.RunText().append(SAL_NEWLINE_STRING); + bool bTextAtr = aAttrIter.IsTextAttr(nCurrentPos); + if (!bTextAtr) + { + OUString aOut(aStr.copy(nCurrentPos, nNextAttr - nCurrentPos)); + m_rAttrOutput.RunText().append(msfilter::rtfutil::OutString(aOut, eChrSet)); + } + + m_rAttrOutput.RunText().append('}'); + + nCurrentPos = nNextAttr; + eChrSet = eNextChrSet; + aAttrIter.NextPos(); + } while (nCurrentPos < nEnd); + if (bShape || n + 1 < nPara) + m_rAttrOutput.RunText().append(OOO_STRING_SVTOOLS_RTF_PAR); + } + if (bShape) + m_rAttrOutput.RunText().append('}'); + + SAL_INFO("sw.rtf", OSL_THIS_FUNC << " end"); +} + +void RtfSdrExport::EndShape(sal_Int32 nShapeElement) +{ + if (nShapeElement >= 0) + { + // end of the shape + m_rAttrOutput.RunText().append('}').append('}'); + } +} + +void RtfSdrExport::AddSdrObject(const SdrObject& rObj) +{ + m_pSdrObject = &rObj; + EscherEx::AddSdrObject(rObj); +} + +bool RtfSdrExport::isTextBox(const SwFrameFormat& rFrameFormat) +{ + return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfsdrexport.hxx b/sw/source/filter/ww8/rtfsdrexport.hxx new file mode 100644 index 000000000..b0c9f151e --- /dev/null +++ b/sw/source/filter/ww8/rtfsdrexport.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_RTFSDREXPORT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFSDREXPORT_HXX + +#include <filter/msfilter/escherex.hxx> +#include <editeng/outlobj.hxx> +#include <rtl/strbuf.hxx> + +#include <map> +#include <memory> + +#include "wrtww8.hxx" + +class RtfExport; +class RtfAttributeOutput; +class SwFrameFormat; + +/// Handles export of drawings using RTF markup +class RtfSdrExport final : public EscherEx +{ + RtfExport& m_rExport; + + RtfAttributeOutput& m_rAttrOutput; + + const SdrObject* m_pSdrObject; + + /// Remember the shape type. + sal_uInt32 m_nShapeType; + + /// Remember the shape flags. + ShapeFlag m_nShapeFlags; + + /// Remember style, the most important shape attribute ;-) + OStringBuffer m_aShapeStyle; + + std::map<OString, OString> m_aShapeProps; + + /// Remember which shape types we had already written. + std::unique_ptr<bool[]> m_pShapeTypeWritten; + + bool m_bInGroup = false; + +public: + explicit RtfSdrExport(RtfExport& rExport); + ~RtfSdrExport() override; + + /// Export the sdr object as Sdr. + /// + /// Call this when you need to export the object as Sdr in RTF. + void AddSdrObject(const SdrObject& rObj); + + /// Is this a standalone TextFrame, or used as a TextBox of a shape? + static bool isTextBox(const SwFrameFormat& rFrameFormat); + /// Write editeng text, e.g. shape or comment. + void WriteOutliner(const OutlinerParaObject& rParaObj, TextTypes eType); + +private: + /// Start the shape for which we just collected the information. + /// + /// Returns the element's tag number, -1 means we wrote nothing. + using EscherEx::StartShape; + sal_Int32 StartShape(); + + /// End the shape. + /// + /// The parameter is just what we got from StartShape(). + using EscherEx::EndShape; + void EndShape(sal_Int32 nShapeElement); + + void Commit(EscherPropertyContainer& rProps, const tools::Rectangle& rRect) override; + + void OpenContainer(sal_uInt16 nEscherContainer, int nRecInstance = 0) override; + void CloseContainer() override; + + sal_uInt32 EnterGroup(const OUString& rShapeName, const tools::Rectangle* pBoundRect) override; + void LeaveGroup() override; + + void AddShape(sal_uInt32 nShapeType, ShapeFlag nShapeFlags, sal_uInt32 nShapeId = 0) override; + + /// Add starting and ending point of a line to the m_pShapeAttrList. + void AddLineDimensions(const tools::Rectangle& rRectangle); + + /// Add position and size to the OStringBuffer. + void AddRectangleDimensions(OStringBuffer& rBuffer, const tools::Rectangle& rRectangle); + + /// Exports the pib property of the shape + void impl_writeGraphic(); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFSDREXPORT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfstringbuffer.cxx b/sw/source/filter/ww8/rtfstringbuffer.cxx new file mode 100644 index 000000000..055ace53a --- /dev/null +++ b/sw/source/filter/ww8/rtfstringbuffer.cxx @@ -0,0 +1,86 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "rtfstringbuffer.hxx" +#include "rtfattributeoutput.hxx" +#include "rtfexport.hxx" + +RtfStringBufferValue::RtfStringBufferValue() = default; + +RtfStringBufferValue::RtfStringBufferValue(const SwFlyFrameFormat* pFlyFrameFormat, + const SwGrfNode* pGrfNode) + : m_pFlyFrameFormat(pFlyFrameFormat) + , m_pGrfNode(pGrfNode) +{ +} + +void RtfStringBufferValue::makeStringAndClear(RtfAttributeOutput* pAttributeOutput) +{ + if (!isGraphic()) + pAttributeOutput->m_rExport.Strm().WriteOString(m_aBuffer.makeStringAndClear()); + else + pAttributeOutput->FlyFrameGraphic(m_pFlyFrameFormat, m_pGrfNode); +} + +OString RtfStringBufferValue::makeStringAndClear() { return m_aBuffer.makeStringAndClear(); } + +bool RtfStringBufferValue::isGraphic() const +{ + return m_pFlyFrameFormat != nullptr && m_pGrfNode != nullptr; +} + +RtfStringBuffer::RtfStringBuffer() = default; + +sal_Int32 RtfStringBuffer::getLength() const +{ + sal_Int32 nRet = 0; + for (const auto& rValue : m_aValues) + if (!rValue.isGraphic()) + nRet += rValue.getBuffer().getLength(); + return nRet; +} + +void RtfStringBuffer::makeStringAndClear(RtfAttributeOutput* pAttributeOutput) +{ + for (auto& rValue : m_aValues) + rValue.makeStringAndClear(pAttributeOutput); +} + +OString RtfStringBuffer::makeStringAndClear() +{ + OStringBuffer aBuf; + for (auto& rValue : m_aValues) + if (!rValue.isGraphic()) + aBuf.append(rValue.makeStringAndClear()); + return aBuf.makeStringAndClear(); +} + +OStringBuffer& RtfStringBuffer::getLastBuffer() +{ + if (m_aValues.empty() || m_aValues.back().isGraphic()) + m_aValues.emplace_back(RtfStringBufferValue()); + return m_aValues.back().getBuffer(); +} + +OStringBuffer* RtfStringBuffer::operator->() { return &getLastBuffer(); } + +void RtfStringBuffer::clear() { m_aValues.clear(); } + +void RtfStringBuffer::append(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode) +{ + m_aValues.emplace_back(RtfStringBufferValue(pFlyFrameFormat, pGrfNode)); +} + +void RtfStringBuffer::appendAndClear(RtfStringBuffer& rBuf) +{ + for (const auto& rValue : rBuf.m_aValues) + m_aValues.push_back(rValue); + rBuf.clear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/rtfstringbuffer.hxx b/sw/source/filter/ww8/rtfstringbuffer.hxx new file mode 100644 index 000000000..67ad66644 --- /dev/null +++ b/sw/source/filter/ww8/rtfstringbuffer.hxx @@ -0,0 +1,69 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_FILTER_WW8_RTFSTRINGBUFFER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_RTFSTRINGBUFFER_HXX + +#include <rtl/strbuf.hxx> +#include <vector> + +class SwGrfNode; +class SwFlyFrameFormat; +class RtfAttributeOutput; + +/// Contains a buffered string or graphic during RTF export. +class RtfStringBufferValue +{ +public: + /// Constructor for a string buffering. + RtfStringBufferValue(); + /// Constructor for graphic buffering. + RtfStringBufferValue(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode); + /// This version handles graphics. + void makeStringAndClear(RtfAttributeOutput* pAttributeOutput); + /// This one doesn't. + OString makeStringAndClear(); + bool isGraphic() const; + OStringBuffer& getBuffer() { return m_aBuffer; } + const OStringBuffer& getBuffer() const { return m_aBuffer; } + +private: + OStringBuffer m_aBuffer; + const SwFlyFrameFormat* m_pFlyFrameFormat = nullptr; + const SwGrfNode* m_pGrfNode = nullptr; +}; + +/// Wrapper around OStringBuffers, so less hexdump of graphics have to be kept in memory during RTF export. +class RtfStringBuffer +{ +public: + RtfStringBuffer(); + /// Length of all the contained buffers. + sal_Int32 getLength() const; + /// Writes the contents of the buffer directly to the supplied stream. + void makeStringAndClear(RtfAttributeOutput* pAttributeOutput); + /// Returns the buffered strings as a string (ignores graphic elements!) + OString makeStringAndClear(); + /// Access to the last buffer. + OStringBuffer& getLastBuffer(); + OStringBuffer* operator->(); + /// Similar to ->setLength(0), but for all buffers. + void clear(); + /// Same as ->append(), but for graphics and without expanding contents to save memory. + void append(const SwFlyFrameFormat* pFlyFrameFormat, const SwGrfNode* pGrfNode); + /// Append all contained buffers and clear the argument. + void appendAndClear(RtfStringBuffer& rBuf); + +private: + using Values_t = std::vector<RtfStringBufferValue>; + Values_t m_aValues; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_RTFSTRINGBUFFER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/sortedarray.hxx b/sw/source/filter/ww8/sortedarray.hxx new file mode 100644 index 000000000..7efec13aa --- /dev/null +++ b/sw/source/filter/ww8/sortedarray.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_SORTEDARRAY_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_SORTEDARRAY_HXX + +#include <osl/diagnose.h> +#include <algorithm> + +//simple template that manages a static [] array by sorting at construction + +namespace ww +{ + /** simple template that manages a static array + + The template sorts the array at construction in place. + */ + template<class C> class SortedArray + { + private: + //The array e.g. of sprms. + C *mpWwSprmTab; + size_t mnNoElems; + + SortedArray(const SortedArray&) = delete; + SortedArray& operator=(const SortedArray&) = delete; + public: + //Find an entry, return its address if found and 0 if not + const C *search(C aSrch) const + { + std::pair<C *, C *> aPair = + std::equal_range(mpWwSprmTab, mpWwSprmTab + mnNoElems, aSrch); + if (aPair.first != aPair.second) + return aPair.first; + else + return 0; + } + + SortedArray(C *pWwSprmTab, size_t nNoElems) + : mpWwSprmTab(pWwSprmTab), mnNoElems(nNoElems) + { + OSL_ENSURE(mnNoElems && pWwSprmTab, "WW8: empty Array: Don't do that"); + std::sort(mpWwSprmTab, mpWwSprmTab + mnNoElems); +#if OSL_DEBUG_LEVEL > 1 + bool bBroken=false; + OUString sError; + const C *pIter = mpWwSprmTab; + const C *pBeforeEnd = mpWwSprmTab + mnNoElems - 1; + while (pIter < pBeforeEnd) + { + if (pIter->nId == (pIter+1)->nId) + { + if (!bBroken) + { + sError = + "WW8: Duplicate in list, almost certainly don't " + "want that!\n" + "(You will not see this message again unless you " + "restart)\n" + "Extra entries are...\n"; + bBroken=true; + } + + size_t nSize = sizeof(C); + const sal_uInt8 *pHack = + reinterpret_cast<const sal_uInt8 *>(&(*pIter)); + for (size_t i=0; i < nSize; ++i) + { + sError += OUString::number( + static_cast<sal_Int32>(pHack[i]), 16); + sError += OUString(' '); + } + sError += OUString('\n'); + while (pIter->nId == (pIter+1)->nId && pIter < pBeforeEnd) + ++pIter; + } + else + ++pIter; + } + if (bBroken) + { + SAL_WARN( "sw", sError ); + } +#endif + } + }; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/sprmids.hxx b/sw/source/filter/ww8/sprmids.hxx new file mode 100644 index 000000000..f06f906fd --- /dev/null +++ b/sw/source/filter/ww8/sprmids.hxx @@ -0,0 +1,606 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_SPRMIDS_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_SPRMIDS_HXX + +#include <sal/types.h> + +namespace NS_sprm +{ +const sal_uInt16 LN_PFSideBySide = 0x2404; +const sal_uInt16 LN_PBrcl = 0x2408; +const sal_uInt16 LN_PBrcp = 0x2409; +const sal_uInt16 LN_PBrcTop10 = 0x461c; +const sal_uInt16 LN_PBrcLeft10 = 0x461d; +const sal_uInt16 LN_PBrcBottom10 = 0x461e; +const sal_uInt16 LN_PBrcRight10 = 0x461f; +const sal_uInt16 LN_PBrcBetween10 = 0x4620; +const sal_uInt16 LN_PBrcBar10 = 0x4621; +const sal_uInt16 LN_PDxaFromText10 = 0x4622; +const sal_uInt16 LN_PRuler = 0xc632; +const sal_uInt16 LN_PISnapBaseLine = 0x243b; +const sal_uInt16 LN_PAnld = 0xc63e; +const sal_uInt16 LN_PPropRMark = 0xc63f; +const sal_uInt16 LN_PCrLf = 0x2444; +const sal_uInt16 LN_PHugePapx = 0x6645; +const sal_uInt16 LN_CChs = 0xea08; +const sal_uInt16 LN_CIdCharType = 0x480b; +const sal_uInt16 LN_CObjLocation = 0x680e; +const sal_uInt16 LN_CFFtcAsciSymb = 0x2a10; +const sal_uInt16 LN_CDefault = 0x2a32; +const sal_uInt16 LN_CFtcDefault = 0x4a3d; +const sal_uInt16 LN_CSizePos = 0xea3f; +const sal_uInt16 LN_CLid = 0x4a41; +const sal_uInt16 LN_CHpsInc = 0x2a44; +const sal_uInt16 LN_CHpsPosAdj = 0x2a46; +const sal_uInt16 LN_CHpsNew50 = 0xca49; +const sal_uInt16 LN_CHpsInc1 = 0xca4a; +const sal_uInt16 LN_CMajority50 = 0xca4c; +const sal_uInt16 LN_CHpsMul = 0x4a4d; +const sal_uInt16 LN_CFDiacColor = 0x085b; +const sal_uInt16 LN_CCpg = 0x486b; +const sal_uInt16 LN_PicBrcl = 0x2e00; +const sal_uInt16 LN_PicScale = 0xce01; +const sal_uInt16 LN_SOlstAnm = 0xd202; +const sal_uInt16 LN_SFAutoPgn = 0x300d; +const sal_uInt16 LN_SDyaPgn = 0xb00f; +const sal_uInt16 LN_SDxaPgn = 0xb010; +const sal_uInt16 LN_SGprfIhdt = 0x3014; +const sal_uInt16 LN_SBCustomize = 0x301e; +const sal_uInt16 LN_SPropRMark = 0xd227; +const sal_uInt16 LN_SFFacingCol = 0x3229; +const sal_uInt16 LN_TDefTable10 = 0xd606; +const sal_uInt16 LN_THTMLProps = 0x740c; +const sal_uInt16 LN_TSetBrc10 = 0xd626; +const sal_uInt16 LN_TSetShd80 = 0x7627; +const sal_uInt16 LN_TSetShdOdd80 = 0x7628; +const sal_uInt16 LN_TDiagLine = 0xd62a; + +namespace v6 +{ +// Microsoft Word for Windows 6.0 Binary File Format +// Parameter size +const sal_uInt16 sprmPIstd = 2; // short +const sal_uInt16 sprmPIstdPermute = 3; // variable +const sal_uInt16 sprmPIncLv1 = 4; // byte +const sal_uInt16 sprmPJc = 5; // byte +const sal_uInt16 sprmPFSideBySide = 6; // byte +const sal_uInt16 sprmPFKeep = 7; // byte +const sal_uInt16 sprmPFKeepFollow = 8; // byte +const sal_uInt16 sprmPPageBreakBefore = 9; // byte +const sal_uInt16 sprmPBrcl = 10; // byte +const sal_uInt16 sprmPBrcp = 11; // byte +const sal_uInt16 sprmPAnld = 12; // variable +const sal_uInt16 sprmPNLvlAnm = 13; // byte +const sal_uInt16 sprmPFNoLineNumb = 14; // byte +const sal_uInt16 sprmPChgTabsPapx = 15; // variable +const sal_uInt16 sprmPDxaRight = 16; // word +const sal_uInt16 sprmPDxaLeft = 17; // word +const sal_uInt16 sprmPNest = 18; // word +const sal_uInt16 sprmPDxaLeft1 = 19; // word +const sal_uInt16 sprmPDyaLine = 20; // long +const sal_uInt16 sprmPDyaBefore = 21; // word +const sal_uInt16 sprmPDyaAfter = 22; // word +const sal_uInt16 sprmPChgTabs = 23; // variable +const sal_uInt16 sprmPFInTable = 24; // byte +const sal_uInt16 sprmPTtp = 25; // byte +const sal_uInt16 sprmPDxaAbs = 26; // word +const sal_uInt16 sprmPDyaAbs = 27; // word +const sal_uInt16 sprmPDxaWidth = 28; // word +const sal_uInt16 sprmPPc = 29; // byte +const sal_uInt16 sprmPBrcTop10 = 30; // word +const sal_uInt16 sprmPBrcLeft10 = 31; // word +const sal_uInt16 sprmPBrcBottom10 = 32; // word +const sal_uInt16 sprmPBrcRight10 = 33; // word +const sal_uInt16 sprmPBrcBetween10 = 34; // word +const sal_uInt16 sprmPBrcBar10 = 35; // word +const sal_uInt16 sprmPFromText10 = 36; // word +const sal_uInt16 sprmPWr = 37; // byte +const sal_uInt16 sprmPBrcTop = 38; // word +const sal_uInt16 sprmPBrcLeft = 39; // word +const sal_uInt16 sprmPBrcBottom = 40; // word +const sal_uInt16 sprmPBrcRight = 41; // word +const sal_uInt16 sprmPBrcBetween = 42; // word +const sal_uInt16 sprmPBrcBar = 43; // word +const sal_uInt16 sprmPFNoAutoHyph = 44; // byte +const sal_uInt16 sprmPWHeightAbs = 45; // word +const sal_uInt16 sprmPDcs = 46; // short +const sal_uInt16 sprmPShd = 47; // word +const sal_uInt16 sprmPDyaFromText = 48; // word +const sal_uInt16 sprmPDxaFromText = 49; // word +const sal_uInt16 sprmPFLocked = 50; // byte +const sal_uInt16 sprmPFWidowControl = 51; // byte +const sal_uInt16 sprmPRuler = 52; +const sal_uInt16 sprmCFStrikeRM = 65; // bit +const sal_uInt16 sprmCFRMark = 66; // bit +const sal_uInt16 sprmCFFldVanish = 67; // bit +const sal_uInt16 sprmCPicLocation = 68; // variable +const sal_uInt16 sprmCIbstRMark = 69; // short +const sal_uInt16 sprmCDttmRMark = 70; // long +const sal_uInt16 sprmCFData = 71; // bit +const sal_uInt16 sprmCRMReason = 72; // short +const sal_uInt16 sprmCChse = 73; // 3 bytes +const sal_uInt16 sprmCSymbol = 74; // variable +const sal_uInt16 sprmCFOle2 = 75; // bit +const sal_uInt16 sprmCIstd = 80; // short +const sal_uInt16 sprmCIstdPermute = 81; // variable +const sal_uInt16 sprmCDefault = 82; // variable +const sal_uInt16 sprmCPlain = 83; // 0 +const sal_uInt16 sprmCFBold = 85; // byte +const sal_uInt16 sprmCFItalic = 86; // byte +const sal_uInt16 sprmCFStrike = 87; // byte +const sal_uInt16 sprmCFOutline = 88; // byte +const sal_uInt16 sprmCFShadow = 89; // byte +const sal_uInt16 sprmCFSmallCaps = 90; // byte +const sal_uInt16 sprmCFCaps = 91; // byte +const sal_uInt16 sprmCFVanish = 92; // byte +const sal_uInt16 sprmCFtc = 93; // word +const sal_uInt16 sprmCKul = 94; // byte +const sal_uInt16 sprmCSizePos = 95; // 3 bytes +const sal_uInt16 sprmCDxaSpace = 96; // word +const sal_uInt16 sprmCLid = 97; // word +const sal_uInt16 sprmCIco = 98; // byte +const sal_uInt16 sprmCHps = 99; // byte +const sal_uInt16 sprmCHpsInc = 100; // byte +const sal_uInt16 sprmCHpsPos = 101; // byte +const sal_uInt16 sprmCHpsPosAdj = 102; // byte +const sal_uInt16 sprmCMajority = 103; // variable +const sal_uInt16 sprmCIss = 104; // byte +const sal_uInt16 sprmCHpsNew50 = 105; // variable +const sal_uInt16 sprmCHpsInc1 = 106; // variable +const sal_uInt16 sprmCHpsKern = 107; // short +const sal_uInt16 sprmCMajority50 = 108; // variable +const sal_uInt16 sprmCHpsMul = 109; // short +const sal_uInt16 sprmCCondHyhen = 110; // short +const sal_uInt16 sprmCFSpec = 117; // bit +const sal_uInt16 sprmCFObj = 118; // bit +const sal_uInt16 sprmPicBrcl = 119; // byte +const sal_uInt16 sprmPicScale = 120; // length +const sal_uInt16 sprmPicBrcTop = 121; // word +const sal_uInt16 sprmPicBrcLeft = 122; // word +const sal_uInt16 sprmPicBrcBottom = 123; // word +const sal_uInt16 sprmPicBrcRight = 124; // word +const sal_uInt16 sprmSScnsPgn = 131; // byte +const sal_uInt16 sprmSiHeadingPgn = 132; // byte +const sal_uInt16 sprmSOlstAnm = 133; // variable +const sal_uInt16 sprmSDxaColWidth = 136; // 3 bytes +const sal_uInt16 sprmSDxaColSpacing = 137; // 3 bytes +const sal_uInt16 sprmSFEvenlySpaced = 138; // byte +const sal_uInt16 sprmSFProtected = 139; // byte +const sal_uInt16 sprmSDmBinFirst = 140; // word +const sal_uInt16 sprmSDmBinOther = 141; // word +const sal_uInt16 sprmSBkc = 142; // byte +const sal_uInt16 sprmSFTitlePage = 143; // byte +const sal_uInt16 sprmSCcolumns = 144; // word +const sal_uInt16 sprmSDxaColumns = 145; // word +const sal_uInt16 sprmSFAutoPgn = 146; // byte +const sal_uInt16 sprmSNfcPgn = 147; // byte +const sal_uInt16 sprmSDyaPgn = 148; // short +const sal_uInt16 sprmSDxaPgn = 149; // short +const sal_uInt16 sprmSFPgnRestart = 150; // byte +const sal_uInt16 sprmSFEndnote = 151; // byte +const sal_uInt16 sprmSLnc = 152; // byte +const sal_uInt16 sprmSGprfIhdt = 153; // byte +const sal_uInt16 sprmSNLnnMod = 154; // word +const sal_uInt16 sprmSDxaLnn = 155; // word +const sal_uInt16 sprmSDyaHdrTop = 156; // word +const sal_uInt16 sprmSDyaHdrBottom = 157; // word +const sal_uInt16 sprmSLBetween = 158; // byte +const sal_uInt16 sprmSVjc = 159; // byte +const sal_uInt16 sprmSLnnMin = 160; // word +const sal_uInt16 sprmSPgnStart = 161; // word +const sal_uInt16 sprmSBOrientation = 162; // byte +const sal_uInt16 sprmSBCustomize = 163; +const sal_uInt16 sprmSXaPage = 164; // word +const sal_uInt16 sprmSYaPage = 165; // word +const sal_uInt16 sprmSDxaLeft = 166; // word +const sal_uInt16 sprmSDxaRight = 167; // word +const sal_uInt16 sprmSDyaTop = 168; // word +const sal_uInt16 sprmSDyaBottom = 169; // word +const sal_uInt16 sprmSDzaGutter = 170; // word +const sal_uInt16 sprmSDMPaperReq = 171; // word +const sal_uInt16 sprmTJc = 182; // word (low +const sal_uInt16 sprmTDxaLeft = 183; // word +const sal_uInt16 sprmTDxaGapHalf = 184; // word +const sal_uInt16 sprmTFCantSplit = 185; // byte +const sal_uInt16 sprmTTableHeader = 186; // byte +const sal_uInt16 sprmTTableBorders = 187; // 12 bytes +const sal_uInt16 sprmTDefTable10 = 188; // variable +const sal_uInt16 sprmTDyaRowHeight = 189; // word +const sal_uInt16 sprmTDefTable = 190; +const sal_uInt16 sprmTDefTableShd = 191; +const sal_uInt16 sprmTTlp = 192; // 4 bytes +const sal_uInt16 sprmTSetBrc = 193; // 5 bytes +const sal_uInt16 sprmTInsert = 194; // 4 bytes +const sal_uInt16 sprmTDelete = 195; // word +const sal_uInt16 sprmTDxaCol = 196; // 4 bytes +const sal_uInt16 sprmTMerge = 197; // word +const sal_uInt16 sprmTSplit = 198; // word +const sal_uInt16 sprmTSetBrc10 = 199; // 5 bytes +const sal_uInt16 sprmTSetShd = 200; // 4 bytes +const sal_uInt16 sprmMax = 208; +} + +// [MS-DOC] - v20170112 Section 2.2.5.1 +enum class sgc +{ + paragraph = 1, + character = 2, + picture = 3, + section = 4, + table = 5 +}; +enum class spra +{ + operand_toggle_1b_0 = 0, + operand_1b_1 = 1, + operand_2b_2 = 2, + operand_4b_3 = 3, + operand_2b_4 = 4, + operand_2b_5 = 5, + operand_varlen_6 = 6, + operand_3b_7 = 7 +}; +#define SPRM_PART(num, mask, shift) ((static_cast<sal_uInt16>(num) & mask) << shift) +#define SPRM(ispmd, fSpec, sgc, spra) \ + SPRM_PART(ispmd, 0x01FF, 0) + SPRM_PART(fSpec, 0x0001, 9) + SPRM_PART(sgc, 0x0007, 10) \ + + SPRM_PART(spra, 0x0007, 13) +#define SPRM_PAR(ispmd, fSpec, spra) SPRM(ispmd, fSpec, sgc::paragraph, spra) +#define SPRM_CHR(ispmd, fSpec, spra) SPRM(ispmd, fSpec, sgc::character, spra) +#define SPRM_PIC(ispmd, fSpec, spra) SPRM(ispmd, fSpec, sgc::picture, spra) +#define SPRM_SEC(ispmd, fSpec, spra) SPRM(ispmd, fSpec, sgc::section, spra) +#define SPRM_TBL(ispmd, fSpec, spra) SPRM(ispmd, fSpec, sgc::table, spra) + +// [MS-DOC] - v20170112 Section 2.6.1 +const sal_uInt16 sprmCFRMarkDel = SPRM_CHR(0x00, 0, spra::operand_toggle_1b_0); // 0x0800 +const sal_uInt16 sprmCFRMarkIns = SPRM_CHR(0x01, 0, spra::operand_toggle_1b_0); // 0x0801 +const sal_uInt16 sprmCFFldVanish = SPRM_CHR(0x02, 0, spra::operand_toggle_1b_0); // 0x0802 +const sal_uInt16 sprmCPicLocation = SPRM_CHR(0x03, 1, spra::operand_4b_3); // 0x6A03 +const sal_uInt16 sprmCIbstRMark = SPRM_CHR(0x04, 0, spra::operand_2b_2); // 0x4804 +const sal_uInt16 sprmCDttmRMark = SPRM_CHR(0x05, 0, spra::operand_4b_3); // 0x6805 +const sal_uInt16 sprmCFData = SPRM_CHR(0x06, 0, spra::operand_toggle_1b_0); // 0x0806 +const sal_uInt16 sprmCIdslRMark = SPRM_CHR(0x07, 0, spra::operand_2b_2); // 0x4807 +const sal_uInt16 sprmCSymbol = SPRM_CHR(0x09, 1, spra::operand_4b_3); // 0x6A09 +const sal_uInt16 sprmCFOle2 = SPRM_CHR(0x0A, 0, spra::operand_toggle_1b_0); // 0x080A +const sal_uInt16 sprmCHighlight = SPRM_CHR(0x0C, 1, spra::operand_1b_1); // 0x2A0C +const sal_uInt16 sprmCFWebHidden = SPRM_CHR(0x11, 0, spra::operand_toggle_1b_0); // 0x0811 +const sal_uInt16 sprmCRsidProp = SPRM_CHR(0x15, 0, spra::operand_4b_3); // 0x6815 +const sal_uInt16 sprmCRsidText = SPRM_CHR(0x16, 0, spra::operand_4b_3); // 0x6816 +const sal_uInt16 sprmCRsidRMDel = SPRM_CHR(0x17, 0, spra::operand_4b_3); // 0x6817 +const sal_uInt16 sprmCFSpecVanish = SPRM_CHR(0x18, 0, spra::operand_toggle_1b_0); // 0x0818 +const sal_uInt16 sprmCFMathPr = SPRM_CHR(0x1A, 0, spra::operand_varlen_6); // 0xC81A +const sal_uInt16 sprmCIstd = SPRM_CHR(0x30, 1, spra::operand_2b_2); // 0x4A30 +const sal_uInt16 sprmCIstdPermute = SPRM_CHR(0x31, 1, spra::operand_varlen_6); // 0xCA31 +const sal_uInt16 sprmCPlain = SPRM_CHR(0x33, 1, spra::operand_1b_1); // 0x2A33 +const sal_uInt16 sprmCKcd = SPRM_CHR(0x34, 1, spra::operand_1b_1); // 0x2A34 +const sal_uInt16 sprmCFBold = SPRM_CHR(0x35, 0, spra::operand_toggle_1b_0); // 0x0835 +const sal_uInt16 sprmCFItalic = SPRM_CHR(0x36, 0, spra::operand_toggle_1b_0); // 0x0836 +const sal_uInt16 sprmCFStrike = SPRM_CHR(0x37, 0, spra::operand_toggle_1b_0); // 0x0837 +const sal_uInt16 sprmCFOutline = SPRM_CHR(0x38, 0, spra::operand_toggle_1b_0); // 0x0838 +const sal_uInt16 sprmCFShadow = SPRM_CHR(0x39, 0, spra::operand_toggle_1b_0); // 0x0839 +const sal_uInt16 sprmCFSmallCaps = SPRM_CHR(0x3A, 0, spra::operand_toggle_1b_0); // 0x083A +const sal_uInt16 sprmCFCaps = SPRM_CHR(0x3B, 0, spra::operand_toggle_1b_0); // 0x083B +const sal_uInt16 sprmCFVanish = SPRM_CHR(0x3C, 0, spra::operand_toggle_1b_0); // 0x083C +const sal_uInt16 sprmCKul = SPRM_CHR(0x3E, 1, spra::operand_1b_1); // 0x2A3E +const sal_uInt16 sprmCDxaSpace = SPRM_CHR(0x40, 0, spra::operand_2b_4); // 0x8840 +const sal_uInt16 sprmCIco = SPRM_CHR(0x42, 1, spra::operand_1b_1); // 0x2A42 +const sal_uInt16 sprmCHps = SPRM_CHR(0x43, 1, spra::operand_2b_2); // 0x4A43 +const sal_uInt16 sprmCHpsPos = SPRM_CHR(0x45, 0, spra::operand_2b_2); // 0x4845 +const sal_uInt16 sprmCMajority = SPRM_CHR(0x47, 1, spra::operand_varlen_6); // 0xCA47 +const sal_uInt16 sprmCIss = SPRM_CHR(0x48, 1, spra::operand_1b_1); // 0x2A48 +const sal_uInt16 sprmCHpsKern = SPRM_CHR(0x4B, 0, spra::operand_2b_2); // 0x484B +const sal_uInt16 sprmCHresi = SPRM_CHR(0x4E, 0, spra::operand_2b_2); // 0x484E +const sal_uInt16 sprmCRgFtc0 = SPRM_CHR(0x4F, 1, spra::operand_2b_2); // 0x4A4F +const sal_uInt16 sprmCRgFtc1 = SPRM_CHR(0x50, 1, spra::operand_2b_2); // 0x4A50 +const sal_uInt16 sprmCRgFtc2 = SPRM_CHR(0x51, 1, spra::operand_2b_2); // 0x4A51 +const sal_uInt16 sprmCCharScale = SPRM_CHR(0x52, 0, spra::operand_2b_2); // 0x4852 +const sal_uInt16 sprmCFDStrike = SPRM_CHR(0x53, 1, spra::operand_1b_1); // 0x2A53 +const sal_uInt16 sprmCFImprint = SPRM_CHR(0x54, 0, spra::operand_toggle_1b_0); // 0x0854 +const sal_uInt16 sprmCFSpec = SPRM_CHR(0x55, 0, spra::operand_toggle_1b_0); // 0x0855 +const sal_uInt16 sprmCFObj = SPRM_CHR(0x56, 0, spra::operand_toggle_1b_0); // 0x0856 +const sal_uInt16 sprmCPropRMark90 = SPRM_CHR(0x57, 1, spra::operand_varlen_6); // 0xCA57 +const sal_uInt16 sprmCFEmboss = SPRM_CHR(0x58, 0, spra::operand_toggle_1b_0); // 0x0858 +const sal_uInt16 sprmCSfxText = SPRM_CHR(0x59, 0, spra::operand_1b_1); // 0x2859 +const sal_uInt16 sprmCFBiDi = SPRM_CHR(0x5A, 0, spra::operand_toggle_1b_0); // 0x085A +const sal_uInt16 sprmCFBoldBi = SPRM_CHR(0x5C, 0, spra::operand_toggle_1b_0); // 0x085C +const sal_uInt16 sprmCFItalicBi = SPRM_CHR(0x5D, 0, spra::operand_toggle_1b_0); // 0x085D +const sal_uInt16 sprmCFtcBi = SPRM_CHR(0x5E, 1, spra::operand_2b_2); // 0x4A5E +const sal_uInt16 sprmCLidBi = SPRM_CHR(0x5F, 0, spra::operand_2b_2); // 0x485F +const sal_uInt16 sprmCIcoBi = SPRM_CHR(0x60, 1, spra::operand_2b_2); // 0x4A60 +const sal_uInt16 sprmCHpsBi = SPRM_CHR(0x61, 1, spra::operand_2b_2); // 0x4A61 +const sal_uInt16 sprmCDispFldRMark = SPRM_CHR(0x62, 1, spra::operand_varlen_6); // 0xCA62 +const sal_uInt16 sprmCIbstRMarkDel = SPRM_CHR(0x63, 0, spra::operand_2b_2); // 0x4863 +const sal_uInt16 sprmCDttmRMarkDel = SPRM_CHR(0x64, 0, spra::operand_4b_3); // 0x6864 +const sal_uInt16 sprmCBrc80 = SPRM_CHR(0x65, 0, spra::operand_4b_3); // 0x6865 +const sal_uInt16 sprmCShd80 = SPRM_CHR(0x66, 0, spra::operand_2b_2); // 0x4866 +const sal_uInt16 sprmCIdslRMarkDel = SPRM_CHR(0x67, 0, spra::operand_2b_2); // 0x4867 +const sal_uInt16 sprmCFUsePgsuSettings = SPRM_CHR(0x68, 0, spra::operand_toggle_1b_0); // 0x0868 +const sal_uInt16 sprmCRgLid0_80 = SPRM_CHR(0x6D, 0, spra::operand_2b_2); // 0x486D +const sal_uInt16 sprmCRgLid1_80 = SPRM_CHR(0x6E, 0, spra::operand_2b_2); // 0x486E +const sal_uInt16 sprmCIdctHint = SPRM_CHR(0x6F, 0, spra::operand_1b_1); // 0x286F +const sal_uInt16 sprmCCv = SPRM_CHR(0x70, 0, spra::operand_4b_3); // 0x6870 +const sal_uInt16 sprmCShd = SPRM_CHR(0x71, 1, spra::operand_varlen_6); // 0xCA71 +const sal_uInt16 sprmCBrc = SPRM_CHR(0x72, 1, spra::operand_varlen_6); // 0xCA72 +const sal_uInt16 sprmCRgLid0 = SPRM_CHR(0x73, 0, spra::operand_2b_2); // 0x4873 +const sal_uInt16 sprmCRgLid1 = SPRM_CHR(0x74, 0, spra::operand_2b_2); // 0x4874 +const sal_uInt16 sprmCFNoProof = SPRM_CHR(0x75, 0, spra::operand_toggle_1b_0); // 0x0875 +const sal_uInt16 sprmCFitText = SPRM_CHR(0x76, 1, spra::operand_varlen_6); // 0xCA76 +const sal_uInt16 sprmCCvUl = SPRM_CHR(0x77, 0, spra::operand_4b_3); // 0x6877 +const sal_uInt16 sprmCFELayout = SPRM_CHR(0x78, 1, spra::operand_varlen_6); // 0xCA78 +const sal_uInt16 sprmCLbcCRJ = SPRM_CHR(0x79, 0, spra::operand_1b_1); // 0x2879 +const sal_uInt16 sprmCFComplexScripts = SPRM_CHR(0x82, 0, spra::operand_toggle_1b_0); // 0x0882 +const sal_uInt16 sprmCWall = SPRM_CHR(0x83, 1, spra::operand_1b_1); // 0x2A83 +const sal_uInt16 sprmCCnf = SPRM_CHR(0x85, 1, spra::operand_varlen_6); // 0xCA85 +const sal_uInt16 sprmCNeedFontFixup = SPRM_CHR(0x86, 1, spra::operand_1b_1); // 0x2A86 +const sal_uInt16 sprmCPbiIBullet = SPRM_CHR(0x87, 0, spra::operand_4b_3); // 0x6887 +const sal_uInt16 sprmCPbiGrf = SPRM_CHR(0x88, 0, spra::operand_2b_2); // 0x4888 +const sal_uInt16 sprmCPropRMark = SPRM_CHR(0x89, 1, spra::operand_varlen_6); // 0xCA89 +const sal_uInt16 sprmCFSdtVanish = SPRM_CHR(0x90, 1, spra::operand_1b_1); // 0x2A90 + +// [MS-DOC] - v20170112 Section 2.6.2 +const sal_uInt16 sprmPIstd = SPRM_PAR(0x00, 1, spra::operand_2b_2); // 0x4600 +const sal_uInt16 sprmPIstdPermute = SPRM_PAR(0x01, 1, spra::operand_varlen_6); // 0xC601 +const sal_uInt16 sprmPIncLvl = SPRM_PAR(0x02, 1, spra::operand_1b_1); // 0x2602 +const sal_uInt16 sprmPJc80 = SPRM_PAR(0x03, 0, spra::operand_1b_1); // 0x2403 +const sal_uInt16 sprmPFKeep = SPRM_PAR(0x05, 0, spra::operand_1b_1); // 0x2405 +const sal_uInt16 sprmPFKeepFollow = SPRM_PAR(0x06, 0, spra::operand_1b_1); // 0x2406 +const sal_uInt16 sprmPFPageBreakBefore = SPRM_PAR(0x07, 0, spra::operand_1b_1); // 0x2407 +const sal_uInt16 sprmPIlvl = SPRM_PAR(0x0A, 1, spra::operand_1b_1); // 0x260A +const sal_uInt16 sprmPIlfo = SPRM_PAR(0x0B, 1, spra::operand_2b_2); // 0x460B +const sal_uInt16 sprmPFNoLineNumb = SPRM_PAR(0x0C, 0, spra::operand_1b_1); // 0x240C +const sal_uInt16 sprmPChgTabsPapx = SPRM_PAR(0x0D, 1, spra::operand_varlen_6); // 0xC60D +const sal_uInt16 sprmPDxaRight80 = SPRM_PAR(0x0E, 0, spra::operand_2b_4); // 0x840E +const sal_uInt16 sprmPDxaLeft80 = SPRM_PAR(0x0F, 0, spra::operand_2b_4); // 0x840F +const sal_uInt16 sprmPNest80 = SPRM_PAR(0x10, 1, spra::operand_2b_2); // 0x4610 +const sal_uInt16 sprmPDxaLeft180 = SPRM_PAR(0x11, 0, spra::operand_2b_4); // 0x8411 +const sal_uInt16 sprmPDyaLine = SPRM_PAR(0x12, 0, spra::operand_4b_3); // 0x6412 +const sal_uInt16 sprmPDyaBefore = SPRM_PAR(0x13, 0, spra::operand_2b_5); // 0xA413 +const sal_uInt16 sprmPDyaAfter = SPRM_PAR(0x14, 0, spra::operand_2b_5); // 0xA414 +const sal_uInt16 sprmPChgTabs = SPRM_PAR(0x15, 1, spra::operand_varlen_6); // 0xC615 +const sal_uInt16 sprmPFInTable = SPRM_PAR(0x16, 0, spra::operand_1b_1); // 0x2416 +const sal_uInt16 sprmPFTtp = SPRM_PAR(0x17, 0, spra::operand_1b_1); // 0x2417 +const sal_uInt16 sprmPDxaAbs = SPRM_PAR(0x18, 0, spra::operand_2b_4); // 0x8418 +const sal_uInt16 sprmPDyaAbs = SPRM_PAR(0x19, 0, spra::operand_2b_4); // 0x8419 +const sal_uInt16 sprmPDxaWidth = SPRM_PAR(0x1A, 0, spra::operand_2b_4); // 0x841A +const sal_uInt16 sprmPPc = SPRM_PAR(0x1B, 1, spra::operand_1b_1); // 0x261B +const sal_uInt16 sprmPWr = SPRM_PAR(0x23, 0, spra::operand_1b_1); // 0x2423 +const sal_uInt16 sprmPBrcTop80 = SPRM_PAR(0x24, 0, spra::operand_4b_3); // 0x6424 +const sal_uInt16 sprmPBrcLeft80 = SPRM_PAR(0x25, 0, spra::operand_4b_3); // 0x6425 +const sal_uInt16 sprmPBrcBottom80 = SPRM_PAR(0x26, 0, spra::operand_4b_3); // 0x6426 +const sal_uInt16 sprmPBrcRight80 = SPRM_PAR(0x27, 0, spra::operand_4b_3); // 0x6427 +const sal_uInt16 sprmPBrcBetween80 = SPRM_PAR(0x28, 0, spra::operand_4b_3); // 0x6428 +const sal_uInt16 sprmPBrcBar80 = SPRM_PAR(0x29, 1, spra::operand_4b_3); // 0x6629 +const sal_uInt16 sprmPFNoAutoHyph = SPRM_PAR(0x2A, 0, spra::operand_1b_1); // 0x242A +const sal_uInt16 sprmPWHeightAbs = SPRM_PAR(0x2B, 0, spra::operand_2b_2); // 0x442B +const sal_uInt16 sprmPDcs = SPRM_PAR(0x2C, 0, spra::operand_2b_2); // 0x442C +const sal_uInt16 sprmPShd80 = SPRM_PAR(0x2D, 0, spra::operand_2b_2); // 0x442D +const sal_uInt16 sprmPDyaFromText = SPRM_PAR(0x2E, 0, spra::operand_2b_4); // 0x842E +const sal_uInt16 sprmPDxaFromText = SPRM_PAR(0x2F, 0, spra::operand_2b_4); // 0x842F +const sal_uInt16 sprmPFLocked = SPRM_PAR(0x30, 0, spra::operand_1b_1); // 0x2430 +const sal_uInt16 sprmPFWidowControl = SPRM_PAR(0x31, 0, spra::operand_1b_1); // 0x2431 +const sal_uInt16 sprmPFKinsoku = SPRM_PAR(0x33, 0, spra::operand_1b_1); // 0x2433 +const sal_uInt16 sprmPFWordWrap = SPRM_PAR(0x34, 0, spra::operand_1b_1); // 0x2434 +const sal_uInt16 sprmPFOverflowPunct = SPRM_PAR(0x35, 0, spra::operand_1b_1); // 0x2435 +const sal_uInt16 sprmPFTopLinePunct = SPRM_PAR(0x36, 0, spra::operand_1b_1); // 0x2436 +const sal_uInt16 sprmPFAutoSpaceDE = SPRM_PAR(0x37, 0, spra::operand_1b_1); // 0x2437 +const sal_uInt16 sprmPFAutoSpaceDN = SPRM_PAR(0x38, 0, spra::operand_1b_1); // 0x2438 +const sal_uInt16 sprmPWAlignFont = SPRM_PAR(0x39, 0, spra::operand_2b_2); // 0x4439 +const sal_uInt16 sprmPFrameTextFlow = SPRM_PAR(0x3A, 0, spra::operand_2b_2); // 0x443A +const sal_uInt16 sprmPOutLvl = SPRM_PAR(0x40, 1, spra::operand_1b_1); // 0x2640 +const sal_uInt16 sprmPFBiDi = SPRM_PAR(0x41, 0, spra::operand_1b_1); // 0x2441 +const sal_uInt16 sprmPFNumRMIns = SPRM_PAR(0x43, 0, spra::operand_1b_1); // 0x2443 +const sal_uInt16 sprmPNumRM = SPRM_PAR(0x45, 1, spra::operand_varlen_6); // 0xC645 +const sal_uInt16 sprmPHugePapx = SPRM_PAR(0x46, 1, spra::operand_4b_3); // 0x6646 +const sal_uInt16 sprmPFUsePgsuSettings = SPRM_PAR(0x47, 0, spra::operand_1b_1); // 0x2447 +const sal_uInt16 sprmPFAdjustRight = SPRM_PAR(0x48, 0, spra::operand_1b_1); // 0x2448 +const sal_uInt16 sprmPItap = SPRM_PAR(0x49, 1, spra::operand_4b_3); // 0x6649 +const sal_uInt16 sprmPDtap = SPRM_PAR(0x4A, 1, spra::operand_4b_3); // 0x664A +const sal_uInt16 sprmPFInnerTableCell = SPRM_PAR(0x4B, 0, spra::operand_1b_1); // 0x244B +const sal_uInt16 sprmPFInnerTtp = SPRM_PAR(0x4C, 0, spra::operand_1b_1); // 0x244C +const sal_uInt16 sprmPShd = SPRM_PAR(0x4D, 1, spra::operand_varlen_6); // 0xC64D +const sal_uInt16 sprmPBrcTop = SPRM_PAR(0x4E, 1, spra::operand_varlen_6); // 0xC64E +const sal_uInt16 sprmPBrcLeft = SPRM_PAR(0x4F, 1, spra::operand_varlen_6); // 0xC64F +const sal_uInt16 sprmPBrcBottom = SPRM_PAR(0x50, 1, spra::operand_varlen_6); // 0xC650 +const sal_uInt16 sprmPBrcRight = SPRM_PAR(0x51, 1, spra::operand_varlen_6); // 0xC651 +const sal_uInt16 sprmPBrcBetween = SPRM_PAR(0x52, 1, spra::operand_varlen_6); // 0xC652 +const sal_uInt16 sprmPBrcBar = SPRM_PAR(0x53, 1, spra::operand_varlen_6); // 0xC653 +const sal_uInt16 sprmPDxcRight = SPRM_PAR(0x55, 0, spra::operand_2b_2); // 0x4455 +const sal_uInt16 sprmPDxcLeft = SPRM_PAR(0x56, 0, spra::operand_2b_2); // 0x4456 +const sal_uInt16 sprmPDxcLeft1 = SPRM_PAR(0x57, 0, spra::operand_2b_2); // 0x4457 +const sal_uInt16 sprmPDylBefore = SPRM_PAR(0x58, 0, spra::operand_2b_2); // 0x4458 +const sal_uInt16 sprmPDylAfter = SPRM_PAR(0x59, 0, spra::operand_2b_2); // 0x4459 +const sal_uInt16 sprmPFOpenTch = SPRM_PAR(0x5A, 0, spra::operand_1b_1); // 0x245A +const sal_uInt16 sprmPFDyaBeforeAuto = SPRM_PAR(0x5B, 0, spra::operand_1b_1); // 0x245B +const sal_uInt16 sprmPFDyaAfterAuto = SPRM_PAR(0x5C, 0, spra::operand_1b_1); // 0x245C +const sal_uInt16 sprmPDxaRight = SPRM_PAR(0x5D, 0, spra::operand_2b_4); // 0x845D +const sal_uInt16 sprmPDxaLeft = SPRM_PAR(0x5E, 0, spra::operand_2b_4); // 0x845E +const sal_uInt16 sprmPNest = SPRM_PAR(0x5F, 1, spra::operand_2b_2); // 0x465F +const sal_uInt16 sprmPDxaLeft1 = SPRM_PAR(0x60, 0, spra::operand_2b_4); // 0x8460 +const sal_uInt16 sprmPJc = SPRM_PAR(0x61, 0, spra::operand_1b_1); // 0x2461 +const sal_uInt16 sprmPFNoAllowOverlap = SPRM_PAR(0x62, 0, spra::operand_1b_1); // 0x2462 +const sal_uInt16 sprmPWall = SPRM_PAR(0x64, 1, spra::operand_1b_1); // 0x2664 +const sal_uInt16 sprmPIpgp = SPRM_PAR(0x65, 0, spra::operand_4b_3); // 0x6465 +const sal_uInt16 sprmPCnf = SPRM_PAR(0x66, 1, spra::operand_varlen_6); // 0xC666 +const sal_uInt16 sprmPRsid = SPRM_PAR(0x67, 0, spra::operand_4b_3); // 0x6467 +const sal_uInt16 sprmPIstdListPermute = SPRM_PAR(0x69, 1, spra::operand_varlen_6); // 0xC669 +const sal_uInt16 sprmPTableProps = SPRM_PAR(0x6B, 0, spra::operand_4b_3); // 0x646B +const sal_uInt16 sprmPTIstdInfo = SPRM_PAR(0x6C, 1, spra::operand_varlen_6); // 0xC66C +const sal_uInt16 sprmPFContextualSpacing = SPRM_PAR(0x6D, 0, spra::operand_1b_1); // 0x246D +const sal_uInt16 sprmPPropRMark = SPRM_PAR(0x6F, 1, spra::operand_varlen_6); // 0xC66F +const sal_uInt16 sprmPFMirrorIndents = SPRM_PAR(0x70, 0, spra::operand_1b_1); // 0x2470 +const sal_uInt16 sprmPTtwo = SPRM_PAR(0x71, 0, spra::operand_1b_1); // 0x2471 + +// [MS-DOC] - v20170112 Section 2.6.3 +const sal_uInt16 sprmTJc90 = SPRM_TBL(0x00, 0, spra::operand_2b_2); // 0x5400 +const sal_uInt16 sprmTDxaLeft = SPRM_TBL(0x01, 1, spra::operand_2b_4); // 0x9601 +const sal_uInt16 sprmTDxaGapHalf = SPRM_TBL(0x02, 1, spra::operand_2b_4); // 0x9602 +const sal_uInt16 sprmTFCantSplit90 = SPRM_TBL(0x03, 0, spra::operand_1b_1); // 0x3403 +const sal_uInt16 sprmTTableHeader = SPRM_TBL(0x04, 0, spra::operand_1b_1); // 0x3404 +const sal_uInt16 sprmTTableBorders80 = SPRM_TBL(0x05, 1, spra::operand_varlen_6); // 0xD605 +const sal_uInt16 sprmTDyaRowHeight = SPRM_TBL(0x07, 0, spra::operand_2b_4); // 0x9407 +const sal_uInt16 sprmTDefTable = SPRM_TBL(0x08, 1, spra::operand_varlen_6); // 0xD608 +const sal_uInt16 sprmTDefTableShd80 = SPRM_TBL(0x09, 1, spra::operand_varlen_6); // 0xD609 +const sal_uInt16 sprmTTlp = SPRM_TBL(0x0A, 0, spra::operand_4b_3); // 0x740A +const sal_uInt16 sprmTFBiDi = SPRM_TBL(0x0B, 1, spra::operand_2b_2); // 0x560B +const sal_uInt16 sprmTDefTableShd3rd = SPRM_TBL(0x0C, 1, spra::operand_varlen_6); // 0xD60C +const sal_uInt16 sprmTPc = SPRM_TBL(0x0D, 1, spra::operand_1b_1); // 0x360D +const sal_uInt16 sprmTDxaAbs = SPRM_TBL(0x0E, 0, spra::operand_2b_4); // 0x940E +const sal_uInt16 sprmTDyaAbs = SPRM_TBL(0x0F, 0, spra::operand_2b_4); // 0x940F +const sal_uInt16 sprmTDxaFromText = SPRM_TBL(0x10, 0, spra::operand_2b_4); // 0x9410 +const sal_uInt16 sprmTDyaFromText = SPRM_TBL(0x11, 0, spra::operand_2b_4); // 0x9411 +const sal_uInt16 sprmTDefTableShd = SPRM_TBL(0x12, 1, spra::operand_varlen_6); // 0xD612 +const sal_uInt16 sprmTTableBorders = SPRM_TBL(0x13, 1, spra::operand_varlen_6); // 0xD613 +const sal_uInt16 sprmTTableWidth = SPRM_TBL(0x14, 1, spra::operand_3b_7); // 0xF614 +const sal_uInt16 sprmTFAutofit = SPRM_TBL(0x15, 1, spra::operand_1b_1); // 0x3615 +const sal_uInt16 sprmTDefTableShd2nd = SPRM_TBL(0x16, 1, spra::operand_varlen_6); // 0xD616 +const sal_uInt16 sprmTWidthBefore = SPRM_TBL(0x17, 1, spra::operand_3b_7); // 0xF617 +const sal_uInt16 sprmTWidthAfter = SPRM_TBL(0x18, 1, spra::operand_3b_7); // 0xF618 +const sal_uInt16 sprmTFKeepFollow = SPRM_TBL(0x19, 1, spra::operand_1b_1); // 0x3619 +const sal_uInt16 sprmTBrcTopCv = SPRM_TBL(0x1A, 1, spra::operand_varlen_6); // 0xD61A +const sal_uInt16 sprmTBrcLeftCv = SPRM_TBL(0x1B, 1, spra::operand_varlen_6); // 0xD61B +const sal_uInt16 sprmTBrcBottomCv = SPRM_TBL(0x1C, 1, spra::operand_varlen_6); // 0xD61C +const sal_uInt16 sprmTBrcRightCv = SPRM_TBL(0x1D, 1, spra::operand_varlen_6); // 0xD61D +const sal_uInt16 sprmTDxaFromTextRight = SPRM_TBL(0x1E, 0, spra::operand_2b_4); // 0x941E +const sal_uInt16 sprmTDyaFromTextBottom = SPRM_TBL(0x1F, 0, spra::operand_2b_4); // 0x941F +const sal_uInt16 sprmTSetBrc80 = SPRM_TBL(0x20, 1, spra::operand_varlen_6); // 0xD620 +const sal_uInt16 sprmTInsert = SPRM_TBL(0x21, 1, spra::operand_4b_3); // 0x7621 +const sal_uInt16 sprmTDelete = SPRM_TBL(0x22, 1, spra::operand_2b_2); // 0x5622 +const sal_uInt16 sprmTDxaCol = SPRM_TBL(0x23, 1, spra::operand_4b_3); // 0x7623 +const sal_uInt16 sprmTMerge = SPRM_TBL(0x24, 1, spra::operand_2b_2); // 0x5624 +const sal_uInt16 sprmTSplit = SPRM_TBL(0x25, 1, spra::operand_2b_2); // 0x5625 +const sal_uInt16 sprmTTextFlow = SPRM_TBL(0x29, 1, spra::operand_4b_3); // 0x7629 +const sal_uInt16 sprmTVertMerge = SPRM_TBL(0x2B, 1, spra::operand_varlen_6); // 0xD62B +const sal_uInt16 sprmTVertAlign = SPRM_TBL(0x2C, 1, spra::operand_varlen_6); // 0xD62C +const sal_uInt16 sprmTSetShd = SPRM_TBL(0x2D, 1, spra::operand_varlen_6); // 0xD62D +const sal_uInt16 sprmTSetShdOdd = SPRM_TBL(0x2E, 1, spra::operand_varlen_6); // 0xD62E +const sal_uInt16 sprmTSetBrc = SPRM_TBL(0x2F, 1, spra::operand_varlen_6); // 0xD62F +const sal_uInt16 sprmTCellPadding = SPRM_TBL(0x32, 1, spra::operand_varlen_6); // 0xD632 +const sal_uInt16 sprmTCellSpacingDefault = SPRM_TBL(0x33, 1, spra::operand_varlen_6); // 0xD633 +const sal_uInt16 sprmTCellPaddingDefault = SPRM_TBL(0x34, 1, spra::operand_varlen_6); // 0xD634 +const sal_uInt16 sprmTCellWidth = SPRM_TBL(0x35, 1, spra::operand_varlen_6); // 0xD635 +const sal_uInt16 sprmTFitText = SPRM_TBL(0x36, 1, spra::operand_3b_7); // 0xF636 +const sal_uInt16 sprmTFCellNoWrap = SPRM_TBL(0x39, 1, spra::operand_varlen_6); // 0xD639 +const sal_uInt16 sprmTIstd = SPRM_TBL(0x3A, 1, spra::operand_2b_2); // 0x563A +const sal_uInt16 sprmTCellPaddingStyle = SPRM_TBL(0x3E, 1, spra::operand_varlen_6); // 0xD63E +const sal_uInt16 sprmTCellFHideMark = SPRM_TBL(0x42, 1, spra::operand_varlen_6); // 0xD642 +const sal_uInt16 sprmTSetShdTable = SPRM_TBL(0x60, 1, spra::operand_varlen_6); // 0xD660 +const sal_uInt16 sprmTWidthIndent = SPRM_TBL(0x61, 1, spra::operand_3b_7); // 0xF661 +const sal_uInt16 sprmTCellBrcType = SPRM_TBL(0x62, 1, spra::operand_varlen_6); // 0xD662 +const sal_uInt16 sprmTFBiDi90 = SPRM_TBL(0x64, 1, spra::operand_2b_2); // 0x5664 +const sal_uInt16 sprmTFNoAllowOverlap = SPRM_TBL(0x65, 0, spra::operand_1b_1); // 0x3465 +const sal_uInt16 sprmTFCantSplit = SPRM_TBL(0x66, 0, spra::operand_1b_1); // 0x3466 +const sal_uInt16 sprmTPropRMark = SPRM_TBL(0x67, 1, spra::operand_varlen_6); // 0xD667 +const sal_uInt16 sprmTWall = SPRM_TBL(0x68, 1, spra::operand_1b_1); // 0x3668 +const sal_uInt16 sprmTIpgp = SPRM_TBL(0x69, 0, spra::operand_4b_3); // 0x7469 +const sal_uInt16 sprmTCnf = SPRM_TBL(0x6A, 1, spra::operand_varlen_6); // 0xD66A +const sal_uInt16 sprmTDefTableShdRaw = SPRM_TBL(0x70, 1, spra::operand_varlen_6); // 0xD670 +const sal_uInt16 sprmTDefTableShdRaw2nd = SPRM_TBL(0x71, 1, spra::operand_varlen_6); // 0xD671 +const sal_uInt16 sprmTDefTableShdRaw3rd = SPRM_TBL(0x72, 1, spra::operand_varlen_6); // 0xD672 +const sal_uInt16 sprmTRsid = SPRM_TBL(0x79, 0, spra::operand_4b_3); // 0x7479 +const sal_uInt16 sprmTCellVertAlignStyle = SPRM_TBL(0x7C, 0, spra::operand_1b_1); // 0x347C +const sal_uInt16 sprmTCellNoWrapStyle = SPRM_TBL(0x7D, 0, spra::operand_1b_1); // 0x347D +const sal_uInt16 sprmTCellBrcTopStyle = SPRM_TBL(0x7F, 0, spra::operand_varlen_6); // 0xD47F +const sal_uInt16 sprmTCellBrcBottomStyle = SPRM_TBL(0x80, 1, spra::operand_varlen_6); // 0xD680 +const sal_uInt16 sprmTCellBrcLeftStyle = SPRM_TBL(0x81, 1, spra::operand_varlen_6); // 0xD681 +const sal_uInt16 sprmTCellBrcRightStyle = SPRM_TBL(0x82, 1, spra::operand_varlen_6); // 0xD682 +const sal_uInt16 sprmTCellBrcInsideHStyle = SPRM_TBL(0x83, 1, spra::operand_varlen_6); // 0xD683 +const sal_uInt16 sprmTCellBrcInsideVStyle = SPRM_TBL(0x84, 1, spra::operand_varlen_6); // 0xD684 +const sal_uInt16 sprmTCellBrcTL2BRStyle = SPRM_TBL(0x85, 1, spra::operand_varlen_6); // 0xD685 +const sal_uInt16 sprmTCellBrcTR2BLStyle = SPRM_TBL(0x86, 1, spra::operand_varlen_6); // 0xD686 +const sal_uInt16 sprmTCellShdStyle = SPRM_TBL(0x87, 1, spra::operand_varlen_6); // 0xD687 +const sal_uInt16 sprmTCHorzBands = SPRM_TBL(0x88, 0, spra::operand_1b_1); // 0x3488 +const sal_uInt16 sprmTCVertBands = SPRM_TBL(0x89, 0, spra::operand_1b_1); // 0x3489 +const sal_uInt16 sprmTJc = SPRM_TBL(0x8A, 0, spra::operand_2b_2); // 0x548A + +// [MS-DOC] - v20170112 Section 2.6.4 +const sal_uInt16 sprmScnsPgn = SPRM_SEC(0x00, 0, spra::operand_1b_1); // 0x3000 +const sal_uInt16 sprmSiHeadingPgn = SPRM_SEC(0x01, 0, spra::operand_1b_1); // 0x3001 +const sal_uInt16 sprmSDxaColWidth = SPRM_SEC(0x03, 1, spra::operand_3b_7); // 0xF203 +const sal_uInt16 sprmSDxaColSpacing = SPRM_SEC(0x04, 1, spra::operand_3b_7); // 0xF204 +const sal_uInt16 sprmSFEvenlySpaced = SPRM_SEC(0x05, 0, spra::operand_1b_1); // 0x3005 +const sal_uInt16 sprmSFProtected = SPRM_SEC(0x06, 0, spra::operand_1b_1); // 0x3006 +const sal_uInt16 sprmSDmBinFirst = SPRM_SEC(0x07, 0, spra::operand_2b_2); // 0x5007 +const sal_uInt16 sprmSDmBinOther = SPRM_SEC(0x08, 0, spra::operand_2b_2); // 0x5008 +const sal_uInt16 sprmSBkc = SPRM_SEC(0x09, 0, spra::operand_1b_1); // 0x3009 +const sal_uInt16 sprmSFTitlePage = SPRM_SEC(0x0A, 0, spra::operand_1b_1); // 0x300A +const sal_uInt16 sprmSCcolumns = SPRM_SEC(0x0B, 0, spra::operand_2b_2); // 0x500B +const sal_uInt16 sprmSDxaColumns = SPRM_SEC(0x0C, 0, spra::operand_2b_4); // 0x900C +const sal_uInt16 sprmSNfcPgn = SPRM_SEC(0x0E, 0, spra::operand_1b_1); // 0x300E +const sal_uInt16 sprmSFPgnRestart = SPRM_SEC(0x11, 0, spra::operand_1b_1); // 0x3011 +const sal_uInt16 sprmSFEndnote = SPRM_SEC(0x12, 0, spra::operand_1b_1); // 0x3012 +const sal_uInt16 sprmSLnc = SPRM_SEC(0x13, 0, spra::operand_1b_1); // 0x3013 +const sal_uInt16 sprmSNLnnMod = SPRM_SEC(0x15, 0, spra::operand_2b_2); // 0x5015 +const sal_uInt16 sprmSDxaLnn = SPRM_SEC(0x16, 0, spra::operand_2b_4); // 0x9016 +const sal_uInt16 sprmSDyaHdrTop = SPRM_SEC(0x17, 0, spra::operand_2b_5); // 0xB017 +const sal_uInt16 sprmSDyaHdrBottom = SPRM_SEC(0x18, 0, spra::operand_2b_5); // 0xB018 +const sal_uInt16 sprmSLBetween = SPRM_SEC(0x19, 0, spra::operand_1b_1); // 0x3019 +const sal_uInt16 sprmSVjc = SPRM_SEC(0x1A, 0, spra::operand_1b_1); // 0x301A +const sal_uInt16 sprmSLnnMin = SPRM_SEC(0x1B, 0, spra::operand_2b_2); // 0x501B +const sal_uInt16 sprmSPgnStart97 = SPRM_SEC(0x1C, 0, spra::operand_2b_2); // 0x501C +const sal_uInt16 sprmSBOrientation = SPRM_SEC(0x1D, 0, spra::operand_1b_1); // 0x301D +const sal_uInt16 sprmSXaPage = SPRM_SEC(0x1F, 0, spra::operand_2b_5); // 0xB01F +const sal_uInt16 sprmSYaPage = SPRM_SEC(0x20, 0, spra::operand_2b_5); // 0xB020 +const sal_uInt16 sprmSDxaLeft = SPRM_SEC(0x21, 0, spra::operand_2b_5); // 0xB021 +const sal_uInt16 sprmSDxaRight = SPRM_SEC(0x22, 0, spra::operand_2b_5); // 0xB022 +const sal_uInt16 sprmSDyaTop = SPRM_SEC(0x23, 0, spra::operand_2b_4); // 0x9023 +const sal_uInt16 sprmSDyaBottom = SPRM_SEC(0x24, 0, spra::operand_2b_4); // 0x9024 +const sal_uInt16 sprmSDzaGutter = SPRM_SEC(0x25, 0, spra::operand_2b_5); // 0xB025 +const sal_uInt16 sprmSDmPaperReq = SPRM_SEC(0x26, 0, spra::operand_2b_2); // 0x5026 +const sal_uInt16 sprmSFBiDi = SPRM_SEC(0x28, 1, spra::operand_1b_1); // 0x3228 +const sal_uInt16 sprmSFRTLGutter = SPRM_SEC(0x2A, 1, spra::operand_1b_1); // 0x322A +const sal_uInt16 sprmSBrcTop80 = SPRM_SEC(0x2B, 0, spra::operand_4b_3); // 0x702B +const sal_uInt16 sprmSBrcLeft80 = SPRM_SEC(0x2C, 0, spra::operand_4b_3); // 0x702C +const sal_uInt16 sprmSBrcBottom80 = SPRM_SEC(0x2D, 0, spra::operand_4b_3); // 0x702D +const sal_uInt16 sprmSBrcRight80 = SPRM_SEC(0x2E, 0, spra::operand_4b_3); // 0x702E +const sal_uInt16 sprmSPgbProp = SPRM_SEC(0x2F, 1, spra::operand_2b_2); // 0x522F +const sal_uInt16 sprmSDxtCharSpace = SPRM_SEC(0x30, 0, spra::operand_4b_3); // 0x7030 +const sal_uInt16 sprmSDyaLinePitch = SPRM_SEC(0x31, 0, spra::operand_2b_4); // 0x9031 +const sal_uInt16 sprmSClm = SPRM_SEC(0x32, 0, spra::operand_2b_2); // 0x5032 +const sal_uInt16 sprmSTextFlow = SPRM_SEC(0x33, 0, spra::operand_2b_2); // 0x5033 +const sal_uInt16 sprmSBrcTop = SPRM_SEC(0x34, 1, spra::operand_varlen_6); // 0xD234 +const sal_uInt16 sprmSBrcLeft = SPRM_SEC(0x35, 1, spra::operand_varlen_6); // 0xD235 +const sal_uInt16 sprmSBrcBottom = SPRM_SEC(0x36, 1, spra::operand_varlen_6); // 0xD236 +const sal_uInt16 sprmSBrcRight = SPRM_SEC(0x37, 1, spra::operand_varlen_6); // 0xD237 +const sal_uInt16 sprmSWall = SPRM_SEC(0x39, 1, spra::operand_1b_1); // 0x3239 +const sal_uInt16 sprmSRsid = SPRM_SEC(0x3A, 0, spra::operand_4b_3); // 0x703A +const sal_uInt16 sprmSFpc = SPRM_SEC(0x3B, 0, spra::operand_1b_1); // 0x303B +const sal_uInt16 sprmSRncFtn = SPRM_SEC(0x3C, 0, spra::operand_1b_1); // 0x303C +const sal_uInt16 sprmSRncEdn = SPRM_SEC(0x3E, 0, spra::operand_1b_1); // 0x303E +const sal_uInt16 sprmSNFtn = SPRM_SEC(0x3F, 0, spra::operand_2b_2); // 0x503F +const sal_uInt16 sprmSNfcFtnRef = SPRM_SEC(0x40, 0, spra::operand_2b_2); // 0x5040 +const sal_uInt16 sprmSNEdn = SPRM_SEC(0x41, 0, spra::operand_2b_2); // 0x5041 +const sal_uInt16 sprmSNfcEdnRef = SPRM_SEC(0x42, 0, spra::operand_2b_2); // 0x5042 +const sal_uInt16 sprmSPropRMark = SPRM_SEC(0x43, 1, spra::operand_varlen_6); // 0xD243 +const sal_uInt16 sprmSPgnStart = SPRM_SEC(0x44, 0, spra::operand_4b_3); // 0x7044 + +// [MS-DOC] - v20170112 Section 2.6.5 +const sal_uInt16 sprmPicBrcTop80 = SPRM_PIC(0x02, 0, spra::operand_4b_3); // 0x6C02 +const sal_uInt16 sprmPicBrcLeft80 = SPRM_PIC(0x03, 0, spra::operand_4b_3); // 0x6C03 +const sal_uInt16 sprmPicBrcBottom80 = SPRM_PIC(0x04, 0, spra::operand_4b_3); // 0x6C04 +const sal_uInt16 sprmPicBrcRight80 = SPRM_PIC(0x05, 0, spra::operand_4b_3); // 0x6C05 +const sal_uInt16 sprmPicBrcTop = SPRM_PIC(0x08, 1, spra::operand_varlen_6); // 0xCE08 +const sal_uInt16 sprmPicBrcLeft = SPRM_PIC(0x09, 1, spra::operand_varlen_6); // 0xCE09 +const sal_uInt16 sprmPicBrcBottom = SPRM_PIC(0x0A, 1, spra::operand_varlen_6); // 0xCE0A +const sal_uInt16 sprmPicBrcRight = SPRM_PIC(0x0B, 1, spra::operand_varlen_6); // 0xCE0B +} + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_SPRMIDS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/styles.cxx b/sw/source/filter/ww8/styles.cxx new file mode 100644 index 000000000..5da6157f7 --- /dev/null +++ b/sw/source/filter/ww8/styles.cxx @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <wwstyles.hxx> + +#include <osl/diagnose.h> + +namespace +{ + const char **GetStiNames() throw() + { + static const char *stiName[] = + { + "Normal", + "Heading 1", + "Heading 2", + "Heading 3", + "Heading 4", + "Heading 5", + "Heading 6", + "Heading 7", + "Heading 8", + "Heading 9", + "Index 1", + "Index 2", + "Index 3", + "Index 4", + "Index 5", + "Index 6", + "Index 7", + "Index 8", + "Index 9", + "TOC 1", + "TOC 2", + "TOC 3", + "TOC 4", + "TOC 5", + "TOC 6", + "TOC 7", + "TOC 8", + "TOC 9", + "Normal Indent", + "Footnote Text", + "Annotation Text", + "Header", + "Footer", + "Index Heading", + "Caption", + "Table of Figures", + "Envelope Address", + "Envelope Return", + "Footnote Reference", + "Annotation Reference", + "Line Number", + "Page Number", + "Endnote Reference", + "Endnote Text", + "Table of Authorities", + "Macro Text", + "TOA Heading", + "List", + "List 2", + "List 3", + "List 4", + "List 5", + "List Bullet", + "List Bullet 2", + "List Bullet 3", + "List Bullet 4", + "List Bullet 5", + "List Number", + "List Number 2", + "List Number 3", + "List Number 4", + "List Number 5", + "Title", + "Closing", + "Signature", + "Default Paragraph Font", + "Body Text", + "Body Text Indent", + "List Continue", + "List Continue 2", + "List Continue 3", + "List Continue 4", + "List Continue 5", + "Message Header", + "Subtitle", + "Salutation", + "Date", + "Body Text First Indent", + "Body Text First Indent 2", + "Note Heading", + "Body Text 2", + "Body Text 3", + "Body Text Indent 2", + "Body Text Indent 3", + "Block Text", + "Hyperlink", + "Followed Hyperlink", + "Strong", + "Emphasis", + "Document Map", + "Plain Text" + }; + + OSL_ENSURE( SAL_N_ELEMENTS(stiName) == ww::stiMax, "WrongSizeOfArray" ); + + return stiName; + } +} + +namespace ww +{ + const char* GetEnglishNameFromSti(sti eSti) throw() + { + if (eSti >= stiMax) + return nullptr; + else + return GetStiNames()[eSti]; + } + + bool StandardStiIsCharStyle(sti eSti) throw() + { + switch (eSti) + { + case stiFootnoteRef: + case stiAtnRef: + case stiLnn: + case stiPgn: + case stiEdnRef: + case stiNormalChar: + return true; + default: + return false; + } + } + + sti GetCanonicalStiFromStc(sal_uInt8 stc) throw() + { + if (stc == 0) + return stiNormal; + else if (stc < 222) + return stiUser; + else + { + static const sti aMapping[] = + { + stiNil, stiAtnRef, stiAtnText, stiToc8, stiToc7, stiToc6, + stiToc5, stiToc4, stiToc3, stiToc2, stiToc1, stiIndex7, + stiIndex6, stiIndex5, stiIndex4, stiIndex3, stiIndex2, + stiIndex1, stiLnn, stiIndexHeading, stiFooter, stiHeader, + stiFootnoteRef, stiFootnoteText, stiLev9, stiLev8, stiLev7, stiLev6, + stiLev5, stiLev4, stiLev3, stiLev2, stiLev1, stiNormIndent + }; + return aMapping[stc-222]; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/types.hxx b/sw/source/filter/ww8/types.hxx new file mode 100644 index 000000000..f2fd85817 --- /dev/null +++ b/sw/source/filter/ww8/types.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_TYPES_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_TYPES_HXX + +#include <sal/types.h> + +#include <vector> + +namespace ww +{ + typedef std::vector<sal_uInt8> bytes; + + enum WordVersion {eWW1 = 1, eWW2 = 2, eWW6 = 6, eWW7 = 7, eWW8 = 8}; + inline bool IsSevenMinus(WordVersion eVer) { return eVer <= eWW7; } + inline bool IsEightPlus(WordVersion eVer) { return eVer >= eWW8; } + + /** For custom wrapping + + When you edit the wrap points of a contour in word, word uses a relative + scale of 0 to 21600 where 21600 is apparently 100% of the graphic width + */ + const int nWrap100Percent = 21600; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/writerhelper.cxx b/sw/source/filter/ww8/writerhelper.cxx new file mode 100644 index 000000000..e52fa5412 --- /dev/null +++ b/sw/source/filter/ww8/writerhelper.cxx @@ -0,0 +1,920 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/util/CloseVetoException.hpp> + +#include <doc.hxx> +#include "writerhelper.hxx" +#include <msfilter.hxx> +#include <com/sun/star/container/XChild.hpp> + +#include <algorithm> +#include <svl/itemiter.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdoole2.hxx> +#include <editeng/formatbreakitem.hxx> +#include <ndtxt.hxx> +#include <ndnotxt.hxx> +#include <fmtcntnt.hxx> +#include <swtable.hxx> +#include <frmfmt.hxx> +#include <flypos.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <SwStyleNameMapper.hxx> +#include <docary.hxx> +#include <charfmt.hxx> +#include <fchrfmt.hxx> +#include <redline.hxx> +#include "types.hxx" +#include <svtools/embedhlp.hxx> +#include <numrule.hxx> +#include <vcl/svapp.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IMark.hxx> +#include <grfatr.hxx> + +using namespace com::sun::star; + +namespace +{ + // #i98791# - adjust sorting + // Utility to sort SwTextFormatColl's by their assigned outline style list level + class outlinecmp + { + public: + bool operator()(const SwTextFormatColl *pA, const SwTextFormatColl *pB) const + { + // #i98791# + bool bResult( false ); + const bool bIsAAssignedToOutlineStyle( pA->IsAssignedToListLevelOfOutlineStyle() ); + const bool bIsBAssignedToOutlineStyle( pB->IsAssignedToListLevelOfOutlineStyle() ); + if ( bIsAAssignedToOutlineStyle != bIsBAssignedToOutlineStyle ) + { + bResult = bIsBAssignedToOutlineStyle; + } + else if ( !bIsAAssignedToOutlineStyle ) + { + // pA and pB are equal regarding the sorting criteria. + // Thus return value does not matter. + bResult = false; + } + else + { + bResult = pA->GetAssignedOutlineStyleLevel() < pB->GetAssignedOutlineStyleLevel(); + } + + return bResult; + } + }; + + bool IsValidSlotWhich(sal_uInt16 nSlotId, sal_uInt16 nWhichId) + { + return (nSlotId != 0 && nWhichId != 0 && nSlotId != nWhichId); + } + + /* + Utility to convert a SwPosFlyFrames into a simple vector of ww8::Frames + + The crucial thing is that a ww8::Frame always has an anchor which + points to some content in the document. This is a requirement of exporting + to Word + */ + ww8::Frames SwPosFlyFramesToFrames(const SwPosFlyFrames &rFlys) + { + ww8::Frames aRet; + + for(const auto& rpFly : rFlys) + { + const SwFrameFormat &rEntry = rpFly->GetFormat(); + + if (const SwPosition* pAnchor = rEntry.GetAnchor().GetContentAnchor()) + { + // the anchor position will be invalidated by SetRedlineFlags + // so set a dummy position and fix it in UpdateFramePositions + SwPosition const dummy(SwNodeIndex( + const_cast<SwNodes&>(pAnchor->nNode.GetNodes()))); + aRet.emplace_back(rEntry, dummy); + } + else + { + SwPosition aPos(rpFly->GetNdIndex()); + + if (SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode()) + { + aPos.nContent.Assign(pTextNd, 0); + } + + aRet.emplace_back(rEntry, aPos); + } + } + return aRet; + } + + //Utility to test if a frame is anchored at a given node index + class anchoredto + { + private: + sal_uLong mnNode; + public: + explicit anchoredto(sal_uLong nNode) : mnNode(nNode) {} + bool operator()(const ww8::Frame &rFrame) const + { + return (mnNode == rFrame.GetPosition().nNode.GetNode().GetIndex()); + } + }; +} + +namespace ww8 +{ + //For i120928,size conversion before exporting graphic of bullet + Frame::Frame(const Graphic &rGrf, const SwPosition &rPos) + : mpFlyFrame(nullptr) + , maPos(rPos) + , maSize() + , maLayoutSize() + , meWriterType(eBulletGrf) + , mpStartFrameContent(nullptr) + , mbIsInline(true) + , mbForBullet(true) + , maGrf(rGrf) + { + const MapMode aMap100mm( MapUnit::Map100thMM ); + Size aSize( rGrf.GetPrefSize() ); + if ( MapUnit::MapPixel == rGrf.GetPrefMapMode().GetMapUnit() ) + { + aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap100mm ); + } + else + { + aSize = OutputDevice::LogicToLogic( aSize,rGrf.GetPrefMapMode(), aMap100mm ); + } + maSize = aSize; + maLayoutSize = maSize; + } + + Frame::Frame(const SwFrameFormat &rFormat, const SwPosition &rPos) + : mpFlyFrame(&rFormat) + , maPos(rPos) + , maSize() + , maLayoutSize() // #i43447# + , meWriterType(eTextBox) + , mpStartFrameContent(nullptr) + // #i43447# - move to initialization list + , mbIsInline( (rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) ) + // #i120928# - handle graphic of bullet within existing implementation + , mbForBullet(false) + , maGrf() + { + switch (rFormat.Which()) + { + case RES_FLYFRMFMT: + if (const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx()) + { + SwNodeIndex aIdx(*pIdx, 1); + const SwNode &rNd = aIdx.GetNode(); + // #i43447# - determine layout size + { + SwRect aLayRect( rFormat.FindLayoutRect() ); + tools::Rectangle aRect( aLayRect.SVRect() ); + // The Object is not rendered (e.g. something in unused + // header/footer) - thus, get the values from the format. + if ( aLayRect.IsEmpty() ) + { + aRect.SetSize( rFormat.GetFrameSize().GetSize() ); + } + maLayoutSize = aRect.GetSize(); + } + switch (rNd.GetNodeType()) + { + case SwNodeType::Grf: + meWriterType = eGraphic; + maSize = rNd.GetNoTextNode()->GetTwipSize(); + break; + case SwNodeType::Ole: + meWriterType = eOle; + maSize = rNd.GetNoTextNode()->GetTwipSize(); + break; + default: + meWriterType = eTextBox; + // #i43447# - Size equals layout size for text boxes + maSize = maLayoutSize; + break; + } + mpStartFrameContent = &rNd; + } + else + { + OSL_ENSURE(false, "Impossible"); + meWriterType = eTextBox; + } + break; + default: + if (const SdrObject* pObj = rFormat.FindRealSdrObject()) + { + if (pObj->GetObjInventor() == SdrInventor::FmForm) + meWriterType = eFormControl; + else + meWriterType = eDrawing; + maSize = pObj->GetSnapRect().GetSize(); + maLayoutSize = maSize; + } + else + { + OSL_ENSURE(false, "Impossible"); + meWriterType = eDrawing; + } + break; + } + } + + + void Frame::ForceTreatAsInline() + { + mbIsInline = true; + } +} + +namespace sw +{ + namespace hack + { + + sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool, + const SfxItemPool &rSrcPool, sal_uInt16 nWhich) + { + sal_uInt16 nSlotId = rSrcPool.GetSlotId(nWhich); + if (IsValidSlotWhich(nSlotId, nWhich)) + nWhich = rDestPool.GetWhich(nSlotId); + else + nWhich = 0; + return nWhich; + } + + sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet, + const SwDoc &rDoc, sal_uInt16 nWhich) + { + if (RES_WHICHHINT_END < *(rSet.GetRanges())) + { + nWhich = TransformWhichBetweenPools(*rSet.GetPool(), + rDoc.GetAttrPool(), nWhich); + } + return nWhich; + } + + DrawingOLEAdaptor::DrawingOLEAdaptor(SdrOle2Obj &rObj, + SfxObjectShell &rPers) + : mxIPRef(rObj.GetObjRef()), mrPers(rPers), + mpGraphic( rObj.GetGraphic() ) + { + rObj.AbandonObject(); + } + + bool DrawingOLEAdaptor::TransferToDoc( OUString &rName ) + { + OSL_ENSURE(mxIPRef.is(), "Transferring invalid object to doc"); + if (!mxIPRef.is()) + return false; + + uno::Reference < container::XChild > xChild( mxIPRef, uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( mrPers.GetModel() ); + + bool bSuccess = mrPers.GetEmbeddedObjectContainer().InsertEmbeddedObject( mxIPRef, rName ); + if (bSuccess) + { + if ( mpGraphic ) + ::svt::EmbeddedObjectRef::SetGraphicToContainer( *mpGraphic, + mrPers.GetEmbeddedObjectContainer(), + rName, + OUString() ); + + mxIPRef = nullptr; + } + + return bSuccess; + } + + DrawingOLEAdaptor::~DrawingOLEAdaptor() + { + if (mxIPRef.is()) + { + OSL_ENSURE( !mrPers.GetEmbeddedObjectContainer().HasEmbeddedObject( mxIPRef ), "Object in adaptor is inserted?!" ); + try + { + mxIPRef->close(true); + } + catch ( const css::util::CloseVetoException& ) + { + } + + mxIPRef = nullptr; + } + } + } + + namespace util + { + SwTwips MakeSafePositioningValue(SwTwips nIn) + { + if (nIn > SHRT_MAX) + nIn = SHRT_MAX; + else if (nIn < SHRT_MIN) + nIn = SHRT_MIN; + return nIn; + } + + void SetLayer::SendObjectToHell(SdrObject &rObject) const + { + SetObjectLayer(rObject, eHell); + } + + void SetLayer::SendObjectToHeaven(SdrObject &rObject) const + { + SetObjectLayer(rObject, eHeaven); + } + + void SetLayer::SetObjectLayer(SdrObject &rObject, Layer eLayer) const + { + if (SdrInventor::FmForm == rObject.GetObjInventor()) + rObject.SetLayer(mnFormLayer); + else + { + switch (eLayer) + { + case eHeaven: + rObject.SetLayer(mnHeavenLayer); + break; + case eHell: + rObject.SetLayer(mnHellLayer); + break; + } + } + } + + //SetLayer boilerplate begin + + // #i38889# - by default put objects into the invisible layers. + SetLayer::SetLayer(const SwDoc &rDoc) + : mnHeavenLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId()), + mnHellLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId()), + mnFormLayer(rDoc.getIDocumentDrawModelAccess().GetInvisibleControlsId()) + { + } + //SetLayer boilerplate end + + void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet ) + { + if( bExportParentItemSet ) + { + sal_uInt16 nTotal = rSet.TotalCount(); + for( sal_uInt16 nItem =0; nItem < nTotal; ++nItem ) + { + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( rSet.GetWhichByPos( nItem ), true, &pItem ) ) + { + rItems[pItem->Which()] = pItem; + } + } + } + else if( rSet.Count()) + { + SfxItemIter aIter(rSet); + if (const SfxPoolItem *pItem = aIter.GetCurItem()) + { + do + rItems[pItem->Which()] = pItem; + while ((pItem = aIter.NextItem())); + } + } + } + + const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems, + sal_uInt16 eType) + { + auto aIter = rItems.find(eType); + if (aIter != rItems.end()) + return aIter->second; + return nullptr; + } + + void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet) + { + if (const SwCharFormat* pCharFormat = rFormat.GetCharFormat()) + { + if (pCharFormat->GetAttrSet().Count()) + { + SfxItemIter aIter(pCharFormat->GetAttrSet()); + const SfxPoolItem *pItem = aIter.GetCurItem(); + do + rSet.ClearItem(pItem->Which()); + while ((pItem = aIter.NextItem())); + } + } + } + + ww8::ParaStyles GetParaStyles(const SwDoc &rDoc) + { + ww8::ParaStyles aStyles; + typedef ww8::ParaStyles::size_type mysizet; + + const SwTextFormatColls *pColls = rDoc.GetTextFormatColls(); + mysizet nCount = pColls ? pColls->size() : 0; + aStyles.reserve(nCount); + for (mysizet nI = 0; nI < nCount; ++nI) + aStyles.push_back((*pColls)[ static_cast< sal_uInt16 >(nI) ]); + return aStyles; + } + + SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName) + { + // Search first in the Doc-Styles + SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName(rName); + if (!pColl) + { + // Collection not found, try in Pool ? + sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName, + SwGetPoolIdFromName::TxtColl); + if (n != SAL_MAX_UINT16) // found or standard + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(n, false); + } + return pColl; + } + + SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName) + { + SwCharFormat *pFormat = rDoc.FindCharFormatByName(rName); + if (!pFormat) + { + // Collection not found, try in Pool ? + sal_uInt16 n = SwStyleNameMapper::GetPoolIdFromUIName(rName, + SwGetPoolIdFromName::ChrFmt); + if (n != SAL_MAX_UINT16) // found or standard + pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(n); + } + return pFormat; + } + + // #i98791# - adjust sorting algorithm + void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles) + { + std::sort(rStyles.begin(), rStyles.end(), outlinecmp()); + } + + /* + Utility to extract FlyFormats from a document, potentially from a + selection. + */ + ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM /*, bool bAll*/) + { + SwPosFlyFrames aFlys(rDoc.GetAllFlyFormats(pPaM, true)); + ww8::Frames aRet(SwPosFlyFramesToFrames(aFlys)); + return aRet; + } + + void UpdateFramePositions(ww8::Frames & rFrames) + { + for (ww8::Frame & rFrame : rFrames) + { + SwFormatAnchor const& rAnchor = rFrame.GetFrameFormat().GetAnchor(); + if (SwPosition const*const pAnchor = rAnchor.GetContentAnchor()) + { + rFrame.SetPosition(*pAnchor); + } + else + { // these don't need to be corrected, they're not in redlines + assert(RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()); + } + } + } + + ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode) + { + ww8::Frames aRet; + std::copy_if(rFrames.begin(), rFrames.end(), + std::back_inserter(aRet), anchoredto(rNode.GetIndex())); + return aRet; + } + + const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule, + int nLevel) + { + if (nLevel < 0 || nLevel >= MAXLEVEL) + { + OSL_FAIL("Invalid level"); + return nullptr; + } + return &(rRule.Get( static_cast< sal_uInt16 >(nLevel) )); + } + + const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode) + { + const SwNumRule *pRule = nullptr; + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetNumRule()) + ) + { + return GetNumFormatFromSwNumRuleLevel(*pRule, + rTextNode.GetActualListLevel()); + } + + OSL_ENSURE(rTextNode.GetDoc(), "No document for node?, suspicious"); + if (!rTextNode.GetDoc()) + return nullptr; + + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetDoc()->GetOutlineNumRule()) + ) + { + return GetNumFormatFromSwNumRuleLevel(*pRule, + rTextNode.GetActualListLevel()); + } + + return nullptr; + } + + const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNode) + { + return GetNormalNumRuleFromTextNode(rTextNode); + } + + const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNode) + { + const SwNumRule *pRule = nullptr; + + if ( + rTextNode.IsNumbered() && rTextNode.IsCountedInList() && + nullptr != (pRule = rTextNode.GetNumRule()) + ) + { + return pRule; + } + return nullptr; + } + + SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat) + { + const SwNodeIndex *pIndex = rFormat.GetContent().GetContentIdx(); + OSL_ENSURE(pIndex, "No NodeIndex in SwFrameFormat ?, suspicious"); + if (!pIndex) + return nullptr; + SwNodeIndex aIdx(*pIndex, 1); + return aIdx.GetNode().GetNoTextNode(); + } + + bool HasPageBreak(const SwNode &rNd) + { + const SvxFormatBreakItem *pBreak = nullptr; + if (rNd.IsTableNode() && rNd.GetTableNode()) + { + const SwTable& rTable = rNd.GetTableNode()->GetTable(); + const SwFrameFormat* pApply = rTable.GetFrameFormat(); + OSL_ENSURE(pApply, "impossible"); + if (pApply) + pBreak = &(ItemGet<SvxFormatBreakItem>(*pApply, RES_BREAK)); + } + else if (const SwContentNode *pNd = rNd.GetContentNode()) + pBreak = &(ItemGet<SvxFormatBreakItem>(*pNd, RES_BREAK)); + + return pBreak && pBreak->GetBreak() == SvxBreak::PageBefore; + } + + tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly) + { + if(1 == rPolyPoly.Count()) + { + return rPolyPoly[0]; + } + else + { + // This method will now just concatenate the polygons contained + // in the given PolyPolygon. Anything else which might be thought of + // for reducing to a single polygon will just need more power and + // cannot create more correct results. + sal_uInt32 nPointCount(0); + sal_uInt16 a; + + for(a = 0; a < rPolyPoly.Count(); a++) + { + nPointCount += static_cast<sal_uInt32>(rPolyPoly[a].GetSize()); + } + + if(nPointCount > 0x0000ffff) + { + OSL_FAIL("PolygonFromPolyPolygon: too many points for a single polygon (!)"); + nPointCount = 0x0000ffff; + } + + tools::Polygon aRetval(static_cast<sal_uInt16>(nPointCount)); + sal_uInt32 nAppendIndex(0); + + for(a = 0; a < rPolyPoly.Count(); a++) + { + const tools::Polygon& rCandidate = rPolyPoly[a]; + + for(sal_uInt16 b(0); nAppendIndex <= nPointCount && b < rCandidate.GetSize(); b++) + { + aRetval[static_cast<sal_uInt16>(nAppendIndex++)] = rCandidate[b]; + } + } + + return aRetval; + } + } + + tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop) + { + tools::Polygon aPoly(PolygonFromPolyPolygon(rPolyPoly)); + const Size &rOrigSize = pNd->GetGraphic().GetPrefSize(); + + const SwAttrSet* pAttrSet = pNd->GetpSwAttrSet(); + if (bCorrectCrop && pAttrSet) + { + if (pAttrSet->HasItem(RES_GRFATR_CROPGRF)) + { + // Word's wrap polygon deals with a canvas which has the size of the already + // cropped graphic, do the opposite of correctCrop() in writerfilter/. + const SwCropGrf& rCrop = pAttrSet->GetCropGrf(); + sal_Int32 nCropLeft = convertTwipToMm100(rCrop.GetLeft()); + sal_Int32 nCropRight = convertTwipToMm100(rCrop.GetRight()); + sal_Int32 nCropTop = convertTwipToMm100(rCrop.GetTop()); + sal_Int32 nCropBottom = convertTwipToMm100(rCrop.GetBottom()); + aPoly.Move(-nCropLeft, -nCropTop); + + Fraction aScaleX(rOrigSize.getWidth(), rOrigSize.getWidth() - nCropLeft - nCropRight); + Fraction aScaleY(rOrigSize.getHeight(), rOrigSize.getHeight() - nCropTop - nCropBottom); + aPoly.Scale(double(aScaleX), double(aScaleY)); + } + } + + Fraction aMapPolyX(ww::nWrap100Percent, rOrigSize.Width()); + Fraction aMapPolyY(ww::nWrap100Percent, rOrigSize.Height()); + aPoly.Scale(double(aMapPolyX), double(aMapPolyY)); + + /* + a) stretch right bound by 15twips + b) shrink bottom bound to where it would have been in word + c) Move it to the left by 15twips + + See the import for details + */ + const Size &rSize = pNd->GetTwipSize(); + Fraction aMoveHack(ww::nWrap100Percent, rSize.Width()); + aMoveHack *= Fraction(15, 1); + long nMove(aMoveHack); + + Fraction aHackX(ww::nWrap100Percent + nMove, + ww::nWrap100Percent); + Fraction aHackY(ww::nWrap100Percent - nMove, + ww::nWrap100Percent); + aPoly.Scale(double(aHackX), double(aHackY)); + + aPoly.Move(-nMove, 0); + return aPoly; + } + + void RedlineStack::open(const SwPosition& rPos, const SfxPoolItem& rAttr) + { + OSL_ENSURE(rAttr.Which() == RES_FLTR_REDLINE, "not a redline"); + maStack.emplace_back(new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone()))); + } + + namespace { + + class SameOpenRedlineType + { + private: + RedlineType meType; + public: + explicit SameOpenRedlineType(RedlineType eType) : meType(eType) {} + bool operator()(const std::unique_ptr<SwFltStackEntry> & pEntry) const + { + const SwFltRedline *pTest = static_cast<const SwFltRedline *> + (pEntry->pAttr.get()); + return (pEntry->bOpen && (pTest->eType == meType)); + } + }; + + } + + bool RedlineStack::close(const SwPosition& rPos, RedlineType eType) + { + //Search from end for same type + auto aResult = std::find_if(maStack.rbegin(), maStack.rend(), + SameOpenRedlineType(eType)); + if (aResult != maStack.rend()) + { + SwTextNode *const pNode(rPos.nNode.GetNode().GetTextNode()); + sal_Int32 const nIndex(rPos.nContent.GetIndex()); + // HACK to prevent overlap of field-mark and redline, + // which would destroy field-mark invariants when the redline + // is hidden: move the redline end one to the left + if (pNode && nIndex > 0 + && pNode->GetText()[nIndex - 1] == CH_TXT_ATR_FIELDEND) + { + SwPosition const end(*rPos.nNode.GetNode().GetTextNode(), + nIndex - 1); + sw::mark::IFieldmark *const pFieldMark( + rPos.GetDoc()->getIDocumentMarkAccess()->getFieldmarkAt(end)); + SAL_WARN_IF(!pFieldMark, "sw.ww8", "expected a field mark"); + if (pFieldMark && pFieldMark->GetMarkPos().nNode.GetIndex() == (*aResult)->m_aMkPos.m_nNode.GetIndex()+1 + && pFieldMark->GetMarkPos().nContent.GetIndex() < (*aResult)->m_aMkPos.m_nContent) + { + (*aResult)->SetEndPos(end); + return true; + } + } + (*aResult)->SetEndPos(rPos); + return true; + } + return false; + } + + void RedlineStack::closeall(const SwPosition& rPos) + { + std::for_each(maStack.begin(), maStack.end(), SetEndIfOpen(rPos)); + } + + void RedlineStack::MoveAttrsFieldmarkInserted(const SwPosition& rPos) + { + size_t nCnt = maStack.size(); + sal_Int32 const nInserted = 2; // CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP + sal_uLong nPosNd = rPos.nNode.GetIndex(); + sal_Int32 nPosCt = rPos.nContent.GetIndex() - nInserted; + + for (size_t i=0; i < nCnt; ++i) + { + SwFltStackEntry& rEntry = *maStack[i]; + bool const isPoint(rEntry.m_aMkPos == rEntry.m_aPtPos); + if ((rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) && + (nPosCt <= rEntry.m_aMkPos.m_nContent)) + { + rEntry.m_aMkPos.m_nContent += nInserted; + SAL_WARN_IF(rEntry.m_aMkPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(), + "sw.ww8", "redline ends after end of line"); + if (isPoint) // sigh ... important special case... + { + rEntry.m_aPtPos.m_nContent += nInserted; + continue; + } + } + // for the end position, leave it alone if it's *on* the dummy + // char position, that should remain *before* + if ((rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) && + (nPosCt < rEntry.m_aPtPos.m_nContent)) + { + rEntry.m_aPtPos.m_nContent += nInserted; + SAL_WARN_IF(rEntry.m_aPtPos.m_nContent > rPos.nNode.GetNodes()[nPosNd]->GetContentNode()->Len(), + "sw.ww8", "redline ends after end of line"); + } + } + } + + void SetInDocAndDelete::operator()(std::unique_ptr<SwFltStackEntry>& pEntry) + { + SwPaM aRegion(pEntry->m_aMkPos.m_nNode); + if (pEntry->MakeRegion(&mrDoc, aRegion, + SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark) && + (*aRegion.GetPoint() != *aRegion.GetMark()) + ) + { + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert | + RedlineFlags::ShowDelete); + const SwFltRedline *pFltRedline = static_cast<const SwFltRedline*> + (pEntry->pAttr.get()); + + SwRedlineData aData(pFltRedline->eType, pFltRedline->nAutorNo, + pFltRedline->aStamp, OUString(), nullptr); + + SwRangeRedline *const pNewRedline(new SwRangeRedline(aData, aRegion)); + // the point node may be deleted in AppendRedline, so park + // the PaM somewhere safe + aRegion.DeleteMark(); + *aRegion.GetPoint() = SwPosition(SwNodeIndex(mrDoc.GetNodes())); + mrDoc.getIDocumentRedlineAccess().AppendRedline(pNewRedline, true); + mrDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::NONE | RedlineFlags::ShowInsert | + RedlineFlags::ShowDelete ); + } + pEntry.reset(); + } + + bool CompareRedlines::operator()(const std::unique_ptr<SwFltStackEntry> & pOneE, + const std::unique_ptr<SwFltStackEntry> & pTwoE) const + { + const SwFltRedline *pOne= static_cast<const SwFltRedline*> + (pOneE->pAttr.get()); + const SwFltRedline *pTwo= static_cast<const SwFltRedline*> + (pTwoE->pAttr.get()); + + //Return the earlier time, if two have the same time, prioritize + //inserts over deletes + if (pOne->aStamp == pTwo->aStamp) + return (pOne->eType == RedlineType::Insert && pTwo->eType != RedlineType::Insert); + else + return (pOne->aStamp < pTwo->aStamp); + } + + RedlineStack::~RedlineStack() + { + std::sort(maStack.begin(), maStack.end(), CompareRedlines()); + std::for_each(maStack.begin(), maStack.end(), SetInDocAndDelete(mrDoc)); + } + + sal_uInt16 WrtRedlineAuthor::AddName( const OUString& rNm ) + { + sal_uInt16 nRet; + auto aIter = std::find(maAuthors.begin(), maAuthors.end(), rNm); + if (aIter != maAuthors.end()) + nRet = static_cast< sal_uInt16 >(aIter - maAuthors.begin()); + else + { + nRet = static_cast< sal_uInt16 >(maAuthors.size()); + maAuthors.push_back(rNm); + } + return nRet; + } + } + + namespace util + { + InsertedTableListener::InsertedTableListener(SwTableNode& rNode) + : m_pTableNode(&rNode) + { + StartListening(rNode.GetNotifier()); + } + + SwTableNode* InsertedTableListener::GetTableNode() + { return m_pTableNode; } + + void InsertedTableListener::Notify(const SfxHint& rHint) + { + if(rHint.GetId() == SfxHintId::Dying) + m_pTableNode = nullptr; + } + + InsertedTablesManager::InsertedTablesManager(const SwDoc &rDoc) + : mbHasRoot(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()) + { } + + void InsertedTablesManager::DelAndMakeTableFrames() + { + if (!mbHasRoot) + return; + for (auto& aTable : maTables) + { + // If already a layout exists, then the BoxFrames must recreated at this table + SwTableNode *pTable = aTable.first->GetTableNode(); + OSL_ENSURE(pTable, "Why no expected table"); + if (pTable) + { + SwFrameFormat * pFrameFormat = pTable->GetTable().GetFrameFormat(); + + if (pFrameFormat != nullptr) + { + SwNodeIndex *pIndex = aTable.second; + pTable->DelFrames(); + pTable->MakeOwnFrames(pIndex); + } + } + } + } + + void InsertedTablesManager::InsertTable(SwTableNode& rTableNode, SwPaM& rPaM) + { + if (!mbHasRoot) + return; + //Associate this tablenode with this after position, replace an //old + //node association if necessary + maTables.emplace( + std::unique_ptr<InsertedTableListener>(new InsertedTableListener(rTableNode)), + &(rPaM.GetPoint()->nNode)); + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/writerhelper.hxx b/sw/source/filter/ww8/writerhelper.hxx new file mode 100644 index 000000000..f9d186ec5 --- /dev/null +++ b/sw/source/filter/ww8/writerhelper.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WRITERHELPER_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WRITERHELPER_HXX + +#include <typeinfo> +#include <vector> +#include <map> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <sfx2/objsh.hxx> +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <svx/svdtypes.hxx> +#include <format.hxx> +#include <node.hxx> +#include <pam.hxx> +#include <tools/poly.hxx> +#include <doc.hxx> +#include <vcl/graph.hxx> + +class SwTextFormatColl; +class SwCharFormat; +class SdrObject; +class SdrOle2Obj; +class OutlinerParaObject; +class SwNumFormat; +class SwTextNode; +class SwNoTextNode; +class SwFormatCharFormat; +class SwDoc; +class SwNumRule; + +namespace sw +{ + namespace util + { + class ItemSort + { + public: + bool operator()(sal_uInt16 nA, sal_uInt16 nB) const; + }; + } +} + +namespace ww8 +{ + /// STL container of Paragraph Styles (SwTextFormatColl) + typedef std::vector<SwTextFormatColl *> ParaStyles; + /// STL container of SfxPoolItems (Attributes) + typedef std::map<sal_uInt16, const SfxPoolItem *, sw::util::ItemSort> PoolItems; + + /** Make exporting a Writer Frame easy + + In word all frames are effectively anchored to character or as + character. This is nice and simple, writer is massively complex in this + area, so this ww8::Frame simplifies matters by providing a single unified + view of the multitude of elements in writer and their differing quirks. + + A ww8::Frame wraps a writer frame and is guaranteed to have a suitable + anchor position available from it. It hides much of the needless + complexity of the multitude of floating/inline elements in writer, it... + + Guarantees an anchor position for a frame. + Provides a readable way to see if we are anchored inline. (as character) + Provides a simple way to flag what type of entity this frame describes. + Provides the size of the element as drawn by writer. + */ + class Frame + { + public: + enum WriterSource {eTextBox, eGraphic, eOle, eDrawing, eFormControl,eBulletGrf}; + private: + const SwFrameFormat* mpFlyFrame; + SwPosition maPos; + Size maSize; + // #i43447# - Size of the frame in the layout. + // Especially needed for graphics, whose layout size can differ from its + // size, because it is scaled into its environment. + Size maLayoutSize; + + WriterSource meWriterType; + const SwNode *mpStartFrameContent; + bool mbIsInline; + bool mbForBullet:1; + Graphic maGrf; + public: + Frame(const SwFrameFormat &rFlyFrame, const SwPosition &rPos); + Frame(const Graphic&, const SwPosition &); + + /** Get the writer SwFrameFormat that this object describes + + @return + The wrapped SwFrameFormat + */ + const SwFrameFormat &GetFrameFormat() const { return *mpFlyFrame; } + + /** Get the position this frame is anchored at + + @return + The anchor position of this frame + */ + const SwPosition &GetPosition() const { return maPos; } + void SetPosition(SwPosition const& rPos) { maPos = rPos; } + + /** Get the node this frame is anchored into + + @return + The SwTextNode this frame is anchored inside + */ + const SwContentNode *GetContentNode() const + { return maPos.nNode.GetNode().GetContentNode(); } + + /** Get the type of frame that this wraps + + @return + a WriterSource which describes the source type of this wrapper + */ + WriterSource GetWriterType() const { return meWriterType; } + + /** Is this frame inline (as character) + + @return + whether this is inline or not + */ + bool IsInline() const { return mbIsInline; } + + /** Even if the frame isn't an inline frame, force it to behave as one + + There are a variety of circumstances where word cannot have + anything except inline elements, e.g. inside frames. So its easier + to force this ww8::Frame into behaving as one, instead of special + casing export code all over the place. + + */ + void ForceTreatAsInline(); + + /** Get the first node of content in the frame + + @return + the first node of content in the frame, might not be any at all. + */ + const SwNode *GetContent() const { return mpStartFrameContent; } + const Graphic &GetGraphic() const { return maGrf; } + bool HasGraphic() const { return mbForBullet; } + + /** Does this ww8::Frame refer to the same writer content as another + + @return + if the two ww8::Frames are handling the same writer frame + */ + bool RefersToSameFrameAs(const Frame &rOther) const + { + if (mbForBullet && rOther.mbForBullet) + return (maGrf == rOther.maGrf); + else if ((!mbForBullet) && (!rOther.mbForBullet)) + return (mpFlyFrame == rOther.mpFlyFrame); + + return false; + } + + /** The Size of the contained element + + @return + the best size to use to export to word + */ + const Size& GetSize() const { return maSize; } + + /** The layout size of the contained element + + #i43447# - Needed for graphics, which are scaled into its environment + + @return layout size + */ + const Size& GetLayoutSize() const + { + return maLayoutSize; + } + }; + + /// STL container of Frames + typedef std::vector<Frame> Frames; + /// STL iterator for Frames + typedef std::vector<Frame>::iterator FrameIter; +} + +namespace sw +{ + namespace util + { + /** Provide a dynamic_cast style cast for SfxPoolItems + + A SfxPoolItem generally need to be cast back to its original type + to be useful, which is both tedious and error prone. So item_cast is + a helper template to aid the process and test if the cast is + correct. + + @param rItem + The SfxPoolItem which is to be casted + + @tplparam T + A SfxPoolItem derived class to cast rItem to + + @return A rItem upcasted back to a T + + @exception std::bad_cast Thrown if the rItem was not a T + */ + template<class T> const T & item_cast(const SfxPoolItem &rItem) + { + if (dynamic_cast<const T *>(&rItem) == nullptr) + throw std::bad_cast(); + return static_cast<const T &>(rItem); + } + + /** Provide a dynamic_cast style cast for SfxPoolItems + + A SfxPoolItem generally need to be cast back to its original type + to be useful, which is both tedious and error prone. So item_cast is + a helper template to aid the process and test if the cast is + correct. + + @param pItem + The SfxPoolItem which is to be casted + + @tplparam T + A SfxPoolItem derived class to cast pItem to + + @return A pItem upcasted back to a T or 0 if pItem was not a T + */ + template<class T> const T * item_cast(const SfxPoolItem *pItem) + { + return dynamic_cast<const T *>(pItem); + } + + /** Extract a SfxPoolItem derived property from a SwContentNode + + Writer's attributes are retrieved by passing a numeric identifier + and receiving a SfxPoolItem reference which must then typically be + cast back to its original type which is both tedious and verbose. + + ItemGet uses item_cast () on the retrieved reference to test that the + retrieved property is of the type that the developer thinks it is. + + @param rNode + The SwContentNode to retrieve the property from + + @param eType + The numeric identifier of the property to be retrieved + + @tplparam T + A SfxPoolItem derived class of the retrieved property + + @exception std::bad_cast Thrown if the property was not a T + + @return The T requested + */ + template<class T> const T & ItemGet(const SwContentNode &rNode, + sal_uInt16 eType) + { + return item_cast<T>(rNode.GetAttr(eType)); + } + + /** Extract a SfxPoolItem derived property from a SwFormat + + Writer's attributes are retrieved by passing a numeric identifier + and receiving a SfxPoolItem reference which must then typically be + cast back to its original type which is both tedious and verbose. + + ItemGet uses item_cast () on the retrieved reference to test that the + retrieved property is of the type that the developer thinks it is. + + @param rFormat + The SwFormat to retrieve the property from + + @param eType + The numeric identifier of the property to be retrieved + + @tplparam T + A SfxPoolItem derived class of the retrieved property + + @exception std::bad_cast Thrown if the property was not a T + */ + template<class T> const T & ItemGet(const SwFormat &rFormat, + sal_uInt16 eType) + { + return item_cast<T>(rFormat.GetFormatAttr(eType)); + } + + /** Extract a SfxPoolItem derived property from a SfxItemSet + + Writer's attributes are retrieved by passing a numeric identifier + and receiving a SfxPoolItem reference which must then typically be + cast back to its original type which is both tedious and verbose. + + ItemGet uses item_cast () on the retrieved reference to test that the + retrieved property is of the type that the developer thinks it is. + + @param rSet + The SfxItemSet to retrieve the property from + + @param eType + The numeric identifier of the property to be retrieved + + @tplparam T + A SfxPoolItem derived class of the retrieved property + + @exception std::bad_cast Thrown if the property was not a T + + @return The T requested + */ + template<class T> const T & ItemGet(const SfxItemSet &rSet, + sal_uInt16 eType) + { + return item_cast<T>(rSet.Get(eType)); + } + + /** Extract a default SfxPoolItem derived property from a SfxItemPool + + Writer's attributes are retrieved by passing a numeric identifier + and receiving a SfxPoolItem reference which must then typically be + cast back to its original type which is both tedious and verbose. + + DefaultItemGet returns a reference to the default property of a + given SfxItemPool for a given property id, e.g. default fontsize + + DefaultItemGet uses item_cast () on the retrieved reference to test + that the retrieved property is of the type that the developer thinks + it is. + + @param rPool + The SfxItemPool whose default property we want + + @param eType + The numeric identifier of the default property to be retrieved + + @tplparam T + A SfxPoolItem derived class of the retrieved property + + @exception std::bad_cast Thrown if the property was not a T + + @return The T requested + */ + template<class T> const T & DefaultItemGet(const SfxItemPool &rPool, + sal_uInt16 eType) + { + return item_cast<T>(rPool.GetDefaultItem(eType)); + } + + /** Extract a default SfxPoolItem derived property from a SwDoc + + Writer's attributes are retrieved by passing a numeric identifier + and receiving a SfxPoolItem reference which must then typically be + cast back to its original type which is both tedious and verbose. + + DefaultItemGet returns a reference to the default property of a + given SwDoc (Writer Document) for a given property id, e.g default + fontsize + + DefaultItemGet uses item_cast () on the retrieved reference to test + that the retrieved property is of the type that the developer thinks + it is. + + @param rPool + The SfxItemPool whose default property we want + + @param eType + The numeric identifier of the default property to be retrieved + + @tplparam T + A SfxPoolItem derived class of the retrieved property + + @exception std::bad_cast Thrown if the property was not a T + + @return The T requested + */ + template<class T> const T & DefaultItemGet(const SwDoc &rDoc, + sal_uInt16 eType) + { + return DefaultItemGet<T>(rDoc.GetAttrPool(), eType); + } + + /** Get the Paragraph Styles of a SwDoc + + Writer's styles are in one of those dreaded macro based pre-STL + containers. Give me an STL container of the paragraph styles + instead. + + @param rDoc + The SwDoc document to get the styles from + + @return A ParaStyles containing the SwDoc's Paragraph Styles + */ + ww8::ParaStyles GetParaStyles(const SwDoc &rDoc); + + /** Get a Paragraph Style which fits a given name + + Its surprisingly tricky to get a style when all you have is a name, + but that's what this does + + @param rDoc + The SwDoc document to search in + + @param rName + The name of the style to search for + + @return A Paragraph Style if one exists which matches the name + */ + SwTextFormatColl* GetParaStyle(SwDoc &rDoc, const OUString& rName); + + /** Get a Character Style which fits a given name + + Its surprisingly tricky to get a style when all you have is a name, + but that's what this does + + @param rDoc + The SwDoc document to search in + + @param rName + The name of the style to search for + + @return A Character Style if one exists which matches the name + */ + SwCharFormat* GetCharStyle(SwDoc &rDoc, const OUString& rName); + + /** Sort sequence of Paragraph Styles by assigned outline style list level + + Sort ParaStyles in ascending order of assigned outline style list level, + e.g. given Normal/Heading1/Heading2/.../Heading10 at their default + assigned outline style list levels of body level/level 1/level 2/.../level 10 + + #i98791# + adjust the sorting algorithm due to introduced outline level attribute + + @param rStyles + The ParaStyles to sort + */ + void SortByAssignedOutlineStyleListLevel(ww8::ParaStyles &rStyles); + + /** Get the SfxPoolItems of a SfxItemSet + + Writer's SfxPoolItems (attributes) are in one of those dreaded + macro based pre-STL containers. Give me an STL container of the + items instead. + + @param rSet + The SfxItemSet to get the items from + + @param rItems + The sw::PoolItems to put the items into + */ + void GetPoolItems(const SfxItemSet &rSet, ww8::PoolItems &rItems, bool bExportParentItemSet ); + + const SfxPoolItem *SearchPoolItems(const ww8::PoolItems &rItems, + sal_uInt16 eType); + + template<class T> const T* HasItem(const ww8::PoolItems &rItems, + sal_uInt16 eType) + { + return item_cast<T>(SearchPoolItems(rItems, eType)); + } + + /** Remove properties from an SfxItemSet which a SwFormatCharFormat overrides + + Given an SfxItemSet and a SwFormatCharFormat remove from the rSet all the + properties which the SwFormatCharFormat would override. An SfxItemSet + contains attributes, and a SwFormatCharFormat is a "Character Style", + so if the SfxItemSet contains bold and so does the character style + then delete bold from the SfxItemSet + + @param + rFormat the SwFormatCharFormat which describes the Character Style + + @param + rSet the SfxItemSet from which we want to remove any properties + which the rFormat would override + + @see #i24291# for examples + */ + void ClearOverridesFromSet(const SwFormatCharFormat &rFormat, SfxItemSet &rSet); + + /** Get the Floating elements in a SwDoc + + Writer's FrameFormats may or may not be anchored to some text content, + e.g. Page Anchored elements will not be. For the winword export we + need them to have something to be anchored to. So this method + returns all the floating elements in a document as a STL container + of ww8::Frames which are guaranteed to have an appropriate anchor. + + @param rDoc + The SwDoc document to get the styles from + + @param pPaM + The SwPam to describe the selection in the document to get the + elements from. 0 means the entire document. + + @return A Frames containing the selections Floating elements + */ + ww8::Frames GetFrames(const SwDoc &rDoc, SwPaM const *pPaM); + + /** fix up frame positions, must be called after SetRedlineFlags */ + void UpdateFramePositions(ww8::Frames & rFrames); + + /** Get the Frames anchored to a given node + + Given a container of frames, find the ones anchored to a given node + + @param rFrames + The container of frames to search in + + @param rNode + The SwNode to check for anchors to + + @return the Frames in rFrames anchored to rNode + */ + ww8::Frames GetFramesInNode(const ww8::Frames &rFrames, const SwNode &rNode); + + /** Get the Numbering Format used on a paragraph + + There are two differing types of numbering formats that may be on a + paragraph, normal and outline. The outline is that numbering you + see in tools->outline numbering. There's no difference in the + numbering itself, just how you get it from the SwTextNode. Needless + to say the filter generally couldn't care less what type of + numbering is in use. + + @param rTextNode + The SwTextNode that is the paragraph + + @return A SwNumFormat pointer that describes the numbering level + on this paragraph, or 0 if there is none. + */ + const SwNumFormat* GetNumFormatFromTextNode(const SwTextNode &rTextNode); + + /** Get the Numbering Format for a given level from a numbering rule + + @param rRule + The numbering rule + + @param nLevel + The numbering level + + @return A SwNumFormat pointer that describes the numbering level + or 0 if the nLevel is out of range + */ + const SwNumFormat* GetNumFormatFromSwNumRuleLevel(const SwNumRule &rRule, + int nLevel); + + const SwNumRule* GetNumRuleFromTextNode(const SwTextNode &rTextNd); + const SwNumRule* GetNormalNumRuleFromTextNode(const SwTextNode &rTextNd); + + /** Get the SwNoTextNode associated with a SwFrameFormat if here is one + + There are two differing types of numbering formats that may be on a + paragraph, normal and outline. The outline is that numbering you + see in tools->outline numbering. There's no difference in the + numbering itself, just how you get it from the SwTextNode. Needless + to say the filter generally couldn't care less what type of + numbering is in use. + + @param rFormat + The SwFrameFormat that may describe a graphic + + @return A SwNoTextNode pointer that describes the graphic of this + frame if there is one, or 0 if there is none. + */ + SwNoTextNode *GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat); + + /** Does a node have a "page break before" applied + + Both text nodes and tables in writer can have "page break before" + This function gives a unified view to both entities + + @param rNode + The SwNode to query the page break of + + @return true if there is a page break, false otherwise + */ + bool HasPageBreak(const SwNode &rNode); + + /** Make a best fit Polygon from a PolyPolygon + + For custom contours in writer we use a PolyPolygon, while word uses + a simple polygon, so we need to try and make the best polygon from + a PolyPolygon + + @param rPolyPoly + The tools::PolyPolygon to try and turn into a Polygon + + @return best fit Polygon from rPolyPoly + */ + tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly); + + /// Undo all scaling / move tricks of the wrap polygon done during import. + tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon& rPolyPoly, const SwNoTextNode* pNd, bool bCorrectCrop); + + /** Make setting a drawing object's layer in a Writer document easy + + Word has the simple concept of a drawing object either in the + foreground and in the background. We have an additional complexity + that form components live in a separate layer, which seems + unnecessarily complicated. So in the winword filter we set the + object's layer through this class with either SendObjectToHell for + the bottom layer and SendObjectToHeaven for the top and we don't + worry about the odd form layer design wrinkle. + */ + class SetLayer + { + private: + SdrLayerID mnHeavenLayer, mnHellLayer, mnFormLayer; + enum Layer {eHeaven, eHell}; + void SetObjectLayer(SdrObject &rObject, Layer eLayer) const; + public: + + /** Make Object live in the bottom drawing layer + + @param rObject + The object to be set to the bottom layer + */ + void SendObjectToHell(SdrObject &rObject) const; + + /** Make Object lives in the top layer + + @param rObject + The object to be set to the top layer + */ + void SendObjectToHeaven(SdrObject &rObject) const; + + /** Normal constructor + + @param rDoc + The Writer document whose drawing layers we will be inserting + objects into + */ + explicit SetLayer(const SwDoc &rDoc); + }; + } + + namespace hack + { + /** Map an ID valid in one SfxItemPool to its equivalent in another + + Given a WhichId (the id that identifies a property e.g. bold) which + is correct in a given SfxItemPool, get the equivalent whichId in + another SfxItemPool + + This arises because the drawing layer uses the same properties as + writer e.g. SvxWeight, but for some reason uses different ids + for the same properties as writer. + + @param rDestPool + The SfxItemPool in whose terms the Id is returned + + @param rSrcPool + The SfxItemPool in whose terms the Id is passed in + + @param nWhich + The Id to transform from source to dest + + @return 0 on failure, the correct property Id on success + */ + sal_uInt16 TransformWhichBetweenPools(const SfxItemPool &rDestPool, + const SfxItemPool &rSrcPool, sal_uInt16 nWhich); + + /** Map a SwDoc WhichId to the equivalent Id for a given SfxItemSet + + Given a WhichId (the id that identifies a property e.g. bold) which + is correct for a Writer document, get the equivalent whichId which + for a given SfxItemSet. + + This arises because the drawing layer uses the same properties as + writer e.g. SvxWeight, but for some reason uses different ids + for the same properties as writer. + + This is effectively the same as TransformWhichBetweenPools except + at a slightly different layer. + + @param rSet + The SfxItemSet in whose terms the Id is returned + + @param rDoc + The SwDoc in whose terms the Id is passed in + + @param nWhich + The Id to transform from writer to the SfxItemSet's domain + + @return 0 on failure, the correct SfxItemSet Id on success + */ + sal_uInt16 GetSetWhichFromSwDocWhich(const SfxItemSet &rSet, + const SwDoc &rDoc, sal_uInt16 nWhich); + + /** Make inserting an OLE object into a Writer document easy + + The rest of Office uses SdrOle2Obj for their OLE objects, Writer + doesn't, which makes things a bit difficult as this is the type of + object that the escher import code shared by the MSOffice filters + produces when it imports an OLE object. + + This utility class takes ownership of the OLE object away from a + SdrOle2Obj and can massage it into the condition best suited to + insertion into Writer. + + If the object was not transferred into Writer then it is deleted + during destruction. + */ + class DrawingOLEAdaptor + { + private: + css::uno::Reference < css::embed::XEmbeddedObject > mxIPRef; + SfxObjectShell& mrPers; + const Graphic* mpGraphic; + public: + /** Take ownership of a SdrOle2Objs OLE object + + @param rObj + The SdrOle2Obj whose OLE object we want to take control of + + @param rPers + The SvPersist of a SwDoc (SwDoc::GetPersist()) into which we + may want to move the object, or remove it from if unwanted. + */ + DrawingOLEAdaptor(SdrOle2Obj &rObj, SfxObjectShell &rPers); + + /// Destructor will destroy the owned OLE object if not transferred + ~DrawingOLEAdaptor(); + + /** Transfer ownership of the OLE object to a document's SvPersist + + TransferToDoc moves the object into the persist under the name + passed in. This name is then suitable to be used as an argument + to SwDoc::InsertOLE. + + The object is no longer owned by the adaptor after this call, + subsequent calls are an error and return false. + + @param rName + The name to store the object under in the document. + + @return On success true is returned, otherwise false. On + success rName is then suitable for user with SwDoc::InsertOLE + */ + bool TransferToDoc(OUString &rName); + private: + DrawingOLEAdaptor& operator=(const DrawingOLEAdaptor&) = delete; + DrawingOLEAdaptor(const DrawingOLEAdaptor &rDoc) = delete; + }; + } +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/writerwordglue.cxx b/sw/source/filter/ww8/writerwordglue.cxx new file mode 100644 index 000000000..8ed09d510 --- /dev/null +++ b/sw/source/filter/ww8/writerwordglue.cxx @@ -0,0 +1,1081 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <msfilter.hxx> +#include "writerwordglue.hxx" +#include <doc.hxx> +#include "writerhelper.hxx" +#include <IDocumentStylePoolAccess.hxx> + +#include <algorithm> + +#include <rtl/tencinfo.h> +#include <sal/log.hxx> + +#include <unicode/ubidi.h> +#include <tools/tenccvt.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> + +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/fontitem.hxx> +#include <o3tl/sorted_vector.hxx> +#include <frmfmt.hxx> +#include <fmtclds.hxx> +#include <hfspacingitem.hxx> +#include <fmtfsize.hxx> +#include <poolfmt.hxx> +#include <swrect.hxx> +#include <fmthdft.hxx> +#include <frmatr.hxx> +#include <ndtxt.hxx> +#include <breakit.hxx> + +using namespace css; + +namespace myImplHelpers +{ + static SwTwips CalcHdFtDist(const SwFrameFormat& rFormat, sal_uInt16 nSpacing) + { + /* + The normal case for reexporting word docs is to have dynamic spacing, + as this is word's only setting, and the reason for the existence of the + dynamic spacing features. If we have dynamic spacing active then we can + add its spacing to the value height of the h/f and get the wanted total + size for word. + + Otherwise we have to get the real layout rendered + height, which is totally nonoptimum, but the best we can do. + */ + long nDist=0; + const SwFormatFrameSize& rSz = rFormat.GetFrameSize(); + + const SwHeaderAndFooterEatSpacingItem &rSpacingCtrl = + sw::util::ItemGet<SwHeaderAndFooterEatSpacingItem> + (rFormat, RES_HEADER_FOOTER_EAT_SPACING); + if (rSpacingCtrl.GetValue()) + nDist += rSz.GetHeight(); + else + { + SwRect aRect(rFormat.FindLayoutRect()); + if (aRect.Height()) + nDist += aRect.Height(); + else + { + const SwFormatFrameSize& rSize = rFormat.GetFrameSize(); + if (SwFrameSize::Variable != rSize.GetHeightSizeType()) + nDist += rSize.GetHeight(); + else + { + nDist += 274; // default for 12pt text + nDist += nSpacing; + } + } + } + return nDist; + } + + static SwTwips CalcHdDist(const SwFrameFormat& rFormat) + { + return CalcHdFtDist(rFormat, rFormat.GetULSpace().GetUpper()); + } + + static SwTwips CalcFtDist(const SwFrameFormat& rFormat) + { + return CalcHdFtDist(rFormat, rFormat.GetULSpace().GetLower()); + } + + /* + SwTextFormatColl and SwCharFormat are quite distinct types and how they are + gotten is also distinct, but the algorithm to match word's equivalents into + them is the same, so we put the different stuff into two separate helper + implementations and a core template that uses the helpers that uses the + same algorithm to do the work. We'll make the helpers specializations of a + non existing template so I can let the compiler figure out the right one + to use from a simple argument to the algorithm class + */ + template <class C> class MapperImpl; + template<> class MapperImpl<SwTextFormatColl> + { + private: + SwDoc &mrDoc; + public: + MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {} + SwTextFormatColl* GetBuiltInStyle(ww::sti eSti); + SwTextFormatColl* GetStyle(const OUString &rName); + SwTextFormatColl* MakeStyle(const OUString &rName); + }; + + SwTextFormatColl* MapperImpl<SwTextFormatColl>::GetBuiltInStyle(ww::sti eSti) + { + const RES_POOL_COLLFMT_TYPE RES_NONE = RES_POOLCOLL_DOC_END; + static const RES_POOL_COLLFMT_TYPE aArr[]= + { + RES_POOLCOLL_STANDARD, RES_POOLCOLL_HEADLINE1, + RES_POOLCOLL_HEADLINE2, RES_POOLCOLL_HEADLINE3, + RES_POOLCOLL_HEADLINE4, RES_POOLCOLL_HEADLINE5, + RES_POOLCOLL_HEADLINE6, RES_POOLCOLL_HEADLINE7, + RES_POOLCOLL_HEADLINE8, RES_POOLCOLL_HEADLINE9, + RES_POOLCOLL_TOX_IDX1, RES_POOLCOLL_TOX_IDX2, + RES_POOLCOLL_TOX_IDX3, RES_NONE, RES_NONE, RES_NONE, RES_NONE, + RES_NONE, RES_NONE, RES_POOLCOLL_TOX_CNTNT1, + RES_POOLCOLL_TOX_CNTNT2, RES_POOLCOLL_TOX_CNTNT3, + RES_POOLCOLL_TOX_CNTNT4, RES_POOLCOLL_TOX_CNTNT5, + RES_POOLCOLL_TOX_CNTNT6, RES_POOLCOLL_TOX_CNTNT7, + RES_POOLCOLL_TOX_CNTNT8, RES_POOLCOLL_TOX_CNTNT9, RES_NONE, + RES_POOLCOLL_FOOTNOTE, RES_NONE, RES_POOLCOLL_HEADER, + RES_POOLCOLL_FOOTER, RES_POOLCOLL_TOX_IDXH, RES_NONE, RES_NONE, + RES_POOLCOLL_JAKETADRESS, RES_POOLCOLL_SENDADRESS, RES_NONE, + RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_ENDNOTE, + RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_LISTS_BEGIN, + RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, + RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, + RES_NONE, RES_NONE, RES_POOLCOLL_HEADLINE_BASE, RES_NONE, + RES_POOLCOLL_SIGNATURE, RES_NONE, RES_POOLCOLL_TEXT, + RES_POOLCOLL_TEXT_MOVE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, + RES_NONE, RES_NONE, RES_POOLCOLL_DOC_SUBTITLE + }; + + OSL_ENSURE(SAL_N_ELEMENTS(aArr) == 75, "Style Array has false size"); + + SwTextFormatColl* pRet = nullptr; + //If this is a built-in word style that has a built-in writer + //equivalent, then map it to one of our built in styles regardless + //of its name + if (sal::static_int_cast< size_t >(eSti) < SAL_N_ELEMENTS(aArr) && aArr[eSti] != RES_NONE) + pRet = mrDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( static_cast< sal_uInt16 >(aArr[eSti]), false); + return pRet; + } + + SwTextFormatColl* MapperImpl<SwTextFormatColl>::GetStyle(const OUString &rName) + { + return sw::util::GetParaStyle(mrDoc, rName); + } + + SwTextFormatColl* MapperImpl<SwTextFormatColl>::MakeStyle(const OUString &rName) + { + return mrDoc.MakeTextFormatColl(rName, + mrDoc.GetDfltTextFormatColl()); + } + + template<> class MapperImpl<SwCharFormat> + { + private: + SwDoc &mrDoc; + public: + MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {} + SwCharFormat* GetBuiltInStyle(ww::sti eSti); + SwCharFormat* GetStyle(const OUString &rName); + SwCharFormat* MakeStyle(const OUString &rName); + }; + + SwCharFormat* MapperImpl<SwCharFormat>::GetBuiltInStyle(ww::sti eSti) + { + RES_POOL_CHRFMT_TYPE eLookup = RES_POOLCHR_NORMAL_END; + switch (eSti) + { + case ww::stiFootnoteRef: + eLookup = RES_POOLCHR_FOOTNOTE; + break; + case ww::stiLnn: + eLookup = RES_POOLCHR_LINENUM; + break; + case ww::stiPgn: + eLookup = RES_POOLCHR_PAGENO; + break; + case ww::stiEdnRef: + eLookup = RES_POOLCHR_ENDNOTE; + break; + case ww::stiHyperlink: + eLookup = RES_POOLCHR_INET_NORMAL; + break; + case ww::stiHyperlinkFollowed: + eLookup = RES_POOLCHR_INET_VISIT; + break; + case ww::stiStrong: + eLookup = RES_POOLCHR_HTML_STRONG; + break; + case ww::stiEmphasis: + eLookup = RES_POOLCHR_HTML_EMPHASIS; + break; + default: + eLookup = RES_POOLCHR_NORMAL_END; + break; + } + SwCharFormat *pRet = nullptr; + if (eLookup != RES_POOLCHR_NORMAL_END) + pRet = mrDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( static_cast< sal_uInt16 >(eLookup) ); + return pRet; + } + + SwCharFormat* MapperImpl<SwCharFormat>::GetStyle(const OUString &rName) + { + return sw::util::GetCharStyle(mrDoc, rName); + } + + SwCharFormat* MapperImpl<SwCharFormat>::MakeStyle(const OUString &rName) + { + return mrDoc.MakeCharFormat(rName, mrDoc.GetDfltCharFormat()); + } + + template<class C> class StyleMapperImpl + { + private: + MapperImpl<C> maHelper; + o3tl::sorted_vector<const C*> maUsedStyles; + C* MakeNonCollidingStyle(const OUString& rName); + public: + typedef std::pair<C*, bool> StyleResult; + explicit StyleMapperImpl(SwDoc &rDoc) : maHelper(rDoc) {} + StyleResult GetStyle(const OUString& rName, ww::sti eSti); + }; + + template<class C> + typename StyleMapperImpl<C>::StyleResult + StyleMapperImpl<C>::GetStyle(const OUString& rName, ww::sti eSti) + { + C *pRet = maHelper.GetBuiltInStyle(eSti); + + //If we've used it once, don't reuse it + if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet))) + pRet = nullptr; + + if (!pRet) + { + pRet = maHelper.GetStyle(rName); + //If we've used it once, don't reuse it + if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet))) + pRet = nullptr; + } + + bool bStyExist = pRet != nullptr; + + if (!pRet) + { + OUString aName(rName); + sal_Int32 nIdx = rName.indexOf(','); + // No commas allow in SW style names + if (-1 != nIdx) + aName = rName.copy( 0, nIdx ); + pRet = MakeNonCollidingStyle( aName ); + } + + if (pRet) + maUsedStyles.insert(pRet); + + return StyleResult(pRet, bStyExist); + } + + template<class C> + C* StyleMapperImpl<C>::MakeNonCollidingStyle(const OUString& rName) + { + OUString aName(rName); + C* pColl = 0; + + if (0 != (pColl = maHelper.GetStyle(aName))) + { + //If the style collides first stick WW- in front of it, unless + //it already has it and then successively add a larger and + //larger number after it, it's got to work at some stage! + if (!aName.startsWith("WW-")) + aName = "WW-" + aName; + + sal_Int32 nI = 1; + OUString aBaseName = aName; + while ( + 0 != (pColl = maHelper.GetStyle(aName)) && + (nI < SAL_MAX_INT32) + ) + { + aName = aBaseName + OUString::number(nI++); + } + } + + return pColl ? 0 : maHelper.MakeStyle(aName); + } + + static OUString FindBestMSSubstituteFont(const OUString &rFont) + { + if (IsStarSymbol(rFont)) + return "Arial Unicode MS"; + return GetSubsFontName(rFont, SubsFontFlags::ONLYONE | SubsFontFlags::MS); + } + + namespace { + + //Utility to remove entries before a given starting position + class IfBeforeStart + { + private: + sal_Int32 mnStart; + public: + explicit IfBeforeStart(sal_Int32 nStart) : mnStart(nStart) {} + bool operator()(const sw::util::CharRunEntry &rEntry) const + { + return rEntry.mnEndPos < mnStart; + } + }; + + } +} + +/// Count what Word calls left/right margin from a format's LRSpace + Box. +static SvxLRSpaceItem lcl_getWordLRSpace(const SwFrameFormat& rFormat) +{ + SvxLRSpaceItem aLR(rFormat.GetLRSpace()); + const SvxBoxItem& rBox = rFormat.GetBox(); + + aLR.SetLeft(aLR.GetLeft() + rBox.GetDistance(SvxBoxItemLine::LEFT)); + if (const editeng::SvxBorderLine* pLeft = rBox.GetLeft()) + aLR.SetLeft(aLR.GetLeft() + pLeft->GetWidth()); + + aLR.SetRight(aLR.GetRight() + rBox.GetDistance(SvxBoxItemLine::RIGHT)); + if (const editeng::SvxBorderLine* pRight = rBox.GetRight()) + aLR.SetRight(aLR.GetRight() + pRight->GetWidth()); + + return aLR; +} + +namespace sw +{ + namespace util + { + + bool IsPlausableSingleWordSection(const SwFrameFormat &rTitleFormat, const SwFrameFormat &rFollowFormat) + { + bool bPlausableSingleWordSection = true; + + const SwFormatCol& rFirstCols = rTitleFormat.GetCol(); + const SwFormatCol& rFollowCols = rFollowFormat.GetCol(); + const SwColumns& rFirstColumns = rFirstCols.GetColumns(); + const SwColumns& rFollowColumns = rFollowCols.GetColumns(); + SvxLRSpaceItem aOneLR = lcl_getWordLRSpace(rTitleFormat); + SvxLRSpaceItem aTwoLR = lcl_getWordLRSpace(rFollowFormat); + const SwFormatFrameSize& rFirstFrameSize = rTitleFormat.GetFrameSize(); + const SwFormatFrameSize& rFollowFrameSize = rFollowFormat.GetFrameSize(); + + if (rFirstColumns.size() != rFollowColumns.size()) + { + //e.g. #i4320# + bPlausableSingleWordSection = false; + } + else if (aOneLR != aTwoLR) + bPlausableSingleWordSection = false; + else if (rFirstFrameSize != rFollowFrameSize) + bPlausableSingleWordSection = false; + else + { + HdFtDistanceGlue aOne(rTitleFormat.GetAttrSet()); + HdFtDistanceGlue aTwo(rFollowFormat.GetAttrSet()); + //e.g. #i14509# + if (!aOne.StrictEqualTopBottom(aTwo)) + bPlausableSingleWordSection = false; + } + return bPlausableSingleWordSection; + } + + HdFtDistanceGlue::HdFtDistanceGlue(const SfxItemSet &rPage) + { + if (const SvxBoxItem *pBox = rPage.GetItem<SvxBoxItem>(RES_BOX)) + { + dyaHdrTop = pBox->CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true ); + dyaHdrBottom = pBox->CalcLineSpace( SvxBoxItemLine::BOTTOM, /*bEvenIfNoLine*/true ); + } + else + { + dyaHdrTop = dyaHdrBottom = 0; + } + const SvxULSpaceItem &rUL = + ItemGet<SvxULSpaceItem>(rPage, RES_UL_SPACE); + dyaHdrTop += rUL.GetUpper(); + dyaHdrBottom += rUL.GetLower(); + + dyaTop = dyaHdrTop; + dyaBottom = dyaHdrBottom; + + const SwFormatHeader *pHd = rPage.GetItem<SwFormatHeader>(RES_HEADER); + if (pHd && pHd->IsActive() && pHd->GetHeaderFormat()) + { + mbHasHeader = true; + dyaTop = dyaTop + static_cast< sal_uInt16 >( (myImplHelpers::CalcHdDist(*(pHd->GetHeaderFormat()))) ); + } + else + mbHasHeader = false; + + const SwFormatFooter *pFt = rPage.GetItem<SwFormatFooter>(RES_FOOTER); + if (pFt && pFt->IsActive() && pFt->GetFooterFormat()) + { + mbHasFooter = true; + dyaBottom = dyaBottom + static_cast< sal_uInt16 >( (myImplHelpers::CalcFtDist(*(pFt->GetFooterFormat()))) ); + } + else + mbHasFooter = false; + } + + bool HdFtDistanceGlue::StrictEqualTopBottom(const HdFtDistanceGlue &rOther) + const + { + // Check top only if both object have a header or if + // both object don't have a header + if (HasHeader() == rOther.HasHeader()) + { + if (dyaTop != rOther.dyaTop) + return false; + } + + // Check bottom only if both object have a footer or if + // both object don't have a footer + if (HasFooter() == rOther.HasFooter()) + { + if (dyaBottom != rOther.dyaBottom) + return false; + } + + return true; + } + + ParaStyleMapper::ParaStyleMapper(SwDoc &rDoc) + : mpImpl(new myImplHelpers::StyleMapperImpl<SwTextFormatColl>(rDoc)) + { + } + + ParaStyleMapper::~ParaStyleMapper() + { + } + + ParaStyleMapper::StyleResult ParaStyleMapper::GetStyle( + const OUString& rName, ww::sti eSti) + { + return mpImpl->GetStyle(rName, eSti); + } + + CharStyleMapper::CharStyleMapper(SwDoc &rDoc) + : mpImpl(new myImplHelpers::StyleMapperImpl<SwCharFormat>(rDoc)) + { + } + + CharStyleMapper::~CharStyleMapper() + { + } + + CharStyleMapper::StyleResult CharStyleMapper::GetStyle( + const OUString& rName, ww::sti eSti) + { + return mpImpl->GetStyle(rName, eSti); + } + + FontMapExport::FontMapExport(const OUString &rFamilyName) + { + sal_Int32 nIndex = 0; + msPrimary = GetNextFontToken(rFamilyName, nIndex); + msSecondary = myImplHelpers::FindBestMSSubstituteFont(msPrimary); + if (msSecondary.isEmpty() && nIndex != -1) + msSecondary = GetNextFontToken(rFamilyName, nIndex); + } + + bool ItemSort::operator()(sal_uInt16 nA, sal_uInt16 nB) const + { + /* + #i24291# + All we want to do is ensure for now is that if a charfmt exist + in the character properties that it rises to the top and is + exported first. In the future we might find more ordering + dependencies for export, in which case this is the place to do + it + */ + if (nA == nB) + return false; + if (nA == RES_TXTATR_CHARFMT) + return true; + if (nB == RES_TXTATR_CHARFMT) + return false; + if (nA == RES_TXTATR_INETFMT) + return true; + if (nB == RES_TXTATR_INETFMT) + return false; + return nA < nB; + } + + CharRuns GetPseudoCharRuns(const SwTextNode& rTextNd) + { + const OUString &rText = rTextNd.GetText(); + + bool bParaIsRTL = false; + OSL_ENSURE(rTextNd.GetDoc(), "No document for node?, suspicious"); + if (rTextNd.GetDoc()) + { + if (SvxFrameDirection::Horizontal_RL_TB == + rTextNd.GetDoc()->GetTextDirection(SwPosition(rTextNd))) + { + bParaIsRTL = true; + } + } + + using namespace ::com::sun::star::i18n; + + sal_uInt16 nScript = i18n::ScriptType::LATIN; + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + if (!rText.isEmpty()) + nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, 0); + + rtl_TextEncoding eChrSet = ItemGet<SvxFontItem>(rTextNd, + GetWhichOfScript(RES_CHRATR_FONT, nScript)).GetCharSet(); + eChrSet = GetExtendedTextEncoding(eChrSet); + + CharRuns aRunChanges; + + if (rText.isEmpty()) + { + aRunChanges.emplace_back(0, nScript, eChrSet, + bParaIsRTL); + return aRunChanges; + } + + typedef std::pair<int32_t, bool> DirEntry; + typedef std::vector<DirEntry> DirChanges; + + typedef std::pair<sal_Int32, sal_uInt16> ScriptEntry; + typedef std::vector<ScriptEntry> ScriptChanges; + + DirChanges aDirChanges; + ScriptChanges aScripts; + + UBiDiDirection eDefaultDir = bParaIsRTL ? UBIDI_RTL : UBIDI_LTR; + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized(rText.getLength(), 0, &nError); + ubidi_setPara(pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(), + static_cast< UBiDiLevel >(eDefaultDir), nullptr, &nError); + + sal_Int32 nCount = ubidi_countRuns(pBidi, &nError); + aDirChanges.reserve(nCount); + + int32_t nStart = 0; + int32_t nEnd; + UBiDiLevel nCurrDir; + + for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx) + { + ubidi_getLogicalRun(pBidi, nStart, &nEnd, &nCurrDir); + /* + UBiDiLevel is the type of the level values in this BiDi + implementation. + + It holds an embedding level and indicates the visual direction + by its bit 0 (even/odd value). + + The value for UBIDI_DEFAULT_LTR is even and the one for + UBIDI_DEFAULT_RTL is odd + */ + aDirChanges.emplace_back(nEnd, nCurrDir & 0x1); + nStart = nEnd; + } + ubidi_close(pBidi); + + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + + sal_Int32 nLen = rText.getLength(); + sal_Int32 nPos = 0; + while (nPos < nLen) + { + sal_Int32 nEnd2 = g_pBreakIt->GetBreakIter()->endOfScript(rText, nPos, + nScript); + if (nEnd2 < 0) + break; + nPos = nEnd2; + aScripts.emplace_back(nPos, nScript); + nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, nPos); + } + + auto aBiDiEnd = aDirChanges.cend(); + auto aScriptEnd = aScripts.cend(); + + auto aBiDiIter = aDirChanges.cbegin(); + auto aScriptIter = aScripts.cbegin(); + + bool bCharIsRTL = bParaIsRTL; + + while ( + aBiDiIter != aBiDiEnd || + aScriptIter != aScriptEnd + ) + { + sal_Int32 nMinPos = rText.getLength(); + + if (aBiDiIter != aBiDiEnd) + { + if (aBiDiIter->first < nMinPos) + nMinPos = aBiDiIter->first; + bCharIsRTL = aBiDiIter->second; + } + + if (aScriptIter != aScriptEnd) + { + if (aScriptIter->first < nMinPos) + nMinPos = aScriptIter->first; + nScript = aScriptIter->second; + } + + aRunChanges.emplace_back(nMinPos, nScript, eChrSet, bCharIsRTL); + + if (aBiDiIter != aBiDiEnd) + { + if (aBiDiIter->first == nMinPos) + ++aBiDiIter; + } + + if (aScriptIter != aScriptEnd) + { + if (aScriptIter->first == nMinPos) + ++aScriptIter; + } + } + + aRunChanges.erase(std::remove_if(aRunChanges.begin(), + aRunChanges.end(), myImplHelpers::IfBeforeStart(0/*nTextStart*/)), aRunChanges.end()); + + return aRunChanges; + } + } + + namespace ms + { + sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding) + { + sal_uInt8 nRet = + rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding); + switch (eTextEncoding) + { + case RTL_TEXTENCODING_DONTKNOW: + case RTL_TEXTENCODING_UCS2: + case RTL_TEXTENCODING_UTF7: + case RTL_TEXTENCODING_UTF8: + case RTL_TEXTENCODING_JAVA_UTF8: + nRet = 0x01; + break; + default: + break; + } + return nRet; + } + + static bool + CanEncode(OUString const& rString, rtl_TextEncoding const eEncoding) + { + OString tmp; + return rString.convertToString(&tmp, eEncoding, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR); + } + + sal_uInt8 rtl_TextEncodingToWinCharsetRTF( + OUString const& rFontName, OUString const& rAltName, + rtl_TextEncoding eTextEncoding) + { + sal_uInt8 nRet = + rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding); + rtl_TextEncoding enc2 = rtl_getTextEncodingFromWindowsCharset(nRet); + if (!rtl_isOctetTextEncoding(enc2) /* check to avoid asserts */ || + !(CanEncode(rFontName, enc2) && CanEncode(rAltName, enc2))) + { + static struct { rtl_TextEncoding enc; sal_uInt8 charset; } + const s_fallbacks [] = { + { RTL_TEXTENCODING_MS_932, 0x80 }, // Shift-JIS + { RTL_TEXTENCODING_MS_936, 0x86 }, // GB-2312 + { RTL_TEXTENCODING_MS_950, 0x88 }, // Big5 + { RTL_TEXTENCODING_MS_949, 0x81 }, // EUC-KR + }; + for (const auto & i : s_fallbacks) + { + // fall back to a charset that can at least encode the + // font's name + if (CanEncode(rFontName, i.enc) + && CanEncode(rAltName, i.enc)) + { + return i.charset; + } + } + SAL_INFO("sw.rtf", "no fallback charset found for font: " + << rFontName << " " << rAltName); + nRet = 0x01; // all hope lost: "default", whatever that is + } + return nRet; + } + + sal_uInt32 DateTime2DTTM( const DateTime& rDT ) + { + /* + mint short :6 0000003F minutes (0-59) + hr short :5 000007C0 hours (0-23) + dom short :5 0000F800 days of month (1-31) + mon short :4 000F0000 months (1-12) + yr short :9 1FF00000 years (1900-2411)-1900 + wdy short :3 E0000000 weekday(Sunday=0 + Monday=1 + ( wdy can be ignored ) Tuesday=2 + Wednesday=3 + Thursday=4 + Friday=5 + Saturday=6) + */ + + if ( rDT.GetDate() == 0 ) + return 0; + sal_uInt32 nDT = ( rDT.GetDayOfWeek() + 1 ) % 7; + nDT <<= 9; + nDT += ( rDT.GetYear() - 1900 ) & 0x1ff; + nDT <<= 4; + nDT += rDT.GetMonth() & 0xf; + nDT <<= 5; + nDT += rDT.GetDay() & 0x1f; + nDT <<= 5; + nDT += rDT.GetHour() & 0x1f; + nDT <<= 6; + nDT += rDT.GetMin() & 0x3f; + return nDT; + } + + + /** Find cFind in rParams if not embedded in " double quotes. + Will NOT find '\\' or '"'. + */ + static sal_Int32 findUnquoted( const OUString& rParams, sal_Unicode cFind, sal_Int32 nFromPos ) + { + const sal_Int32 nLen = rParams.getLength(); + if (nFromPos < 0 || nLen <= nFromPos) + return -1; + for (sal_Int32 nI = nFromPos; nI < nLen; ++nI) + { + const sal_Unicode c = rParams[nI]; + if (c == '\\') + ++nI; + else if (c == '\"') + { + ++nI; + // While not at the end and not at an unescaped end quote + while (nI < nLen) + { + if (rParams[nI] == '\"' && rParams[nI-1] != '\\') + break; + ++nI; + } + } + else //normal unquoted section + { + if (c == cFind) + return nI; + } + } + return -1; + } + + /** Find all rFind in rParams if not embedded in " double quotes and + replace with rReplace. Will NOT find '\\' or '"'. + */ + static bool replaceUnquoted( OUString& rParams, const OUString& rFind, const OUString& rReplace ) + { + bool bReplaced = false; + if (rFind.isEmpty()) + return bReplaced; + const sal_Unicode cFirst = rFind[0]; + + sal_Int32 nLen = rParams.getLength(); + for (sal_Int32 nI = 0; nI < nLen; ++nI) + { + const sal_Unicode c = rParams[nI]; + if (rParams[nI] == '\\') + ++nI; + else if (rParams[nI] == '\"') + { + ++nI; + // While not at the end and not at an unescaped end quote + while (nI < nLen) + { + if (rParams[nI] == '\"' && rParams[nI-1] != '\\') + break; + ++nI; + } + } + else //normal unquoted section + { + if (c == cFirst && rParams.match( rFind, nI)) + { + const sal_Int32 nFindLen = rFind.getLength(); + const sal_Int32 nDiff = rReplace.getLength() - nFindLen; + rParams = rParams.replaceAt( nI, nFindLen, rReplace); + nI += nFindLen + nDiff - 1; + nLen += nDiff; + bReplaced = true; + } + } + } + return bReplaced; + } + + sal_uLong MSDateTimeFormatToSwFormat(OUString& rParams, + SvNumberFormatter *pFormatter, LanguageType &rLang, bool bHijri, + LanguageType nDocLang) + { + // tell the Formatter about the new entry + sal_Int32 nCheckPos = 0; + SvNumFormatType nType = SvNumFormatType::DEFINED; + sal_uInt32 nKey = 0; + + SwapQuotesInField(rParams); + + // Force to Japanese when finding one of 'geE'. + // XXX This actually may not be correct, all era keywords could be + // used in other locales as well. I just don't know about Word. But + // this is how it was for 10 years... + bool bForceJapanese = (-1 != findUnquoted( rParams, 'g', 0)); + // XXX Why replace? The number formatter does handle them and this + // effectively changes from Gengou to Gregorian calendar. Legacy + // because it wasn't supported a decade ago and now moot? Or is + // that a Word specialty? + bForceJapanese |= replaceUnquoted( rParams, "ee", "yyyy"); + bForceJapanese |= replaceUnquoted( rParams, "EE", "YYYY"); + if (LANGUAGE_FRENCH != nDocLang) + { + // Handle the 'a' case here + sal_Int32 nLastPos = 0; + do + { + sal_Int32 nPos = findUnquoted( rParams, 'a', nLastPos + 1 ); + bForceJapanese |= ( nPos != -1 && IsNotAM( rParams, nPos ) ); + nLastPos = nPos; + } while ( -1 != nLastPos ); + } + + // Force to NatNum when finding one of 'oOA' + bool bForceNatNum = replaceUnquoted( rParams, "o", "m") + || replaceUnquoted( rParams, "O", "M"); + if (LANGUAGE_FRENCH != nDocLang) + { + // Handle the 'A' case here + sal_Int32 nLastPos = 0; + do + { + sal_Int32 nPos = findUnquoted( rParams, 'A', nLastPos + 1 ); + bool bIsCharA = ( nPos != -1 && IsNotAM( rParams, nPos ) ); + bForceNatNum |= bIsCharA; + if ( bIsCharA ) + rParams = rParams.replaceAt( nPos, 1, "D" ); + nLastPos = nPos; + } while ( -1 != nLastPos ); + } + + sal_Int32 nLen = rParams.getLength(); + for (sal_Int32 nI = 0; nI < nLen; ++nI) + { + if (rParams[nI] == '\\') + ++nI; + else if (rParams[nI] == '\"') + { + ++nI; + // While not at the end and not at an unescaped end quote + while (nI < nLen) + { + if (rParams[nI] == '\"' && rParams[nI-1] != '\\') + break; + ++nI; + } + } + else //normal unquoted section + { + sal_Unicode nChar = rParams[nI]; + + // Change the localized word string to english + if ( nDocLang == LANGUAGE_FRENCH ) + { + if ( ( nChar == 'a' || nChar == 'A' ) && IsNotAM(rParams, nI) ) + rParams = rParams.replaceAt(nI, 1, "Y"); + } + if (nChar == '/') + { + // MM: We have to escape '/' in case it's used as a char. + // But not if it's a '/' inside AM/PM + if (!(IsPreviousAM(rParams, nI) && IsNextPM(rParams, nI))) + { + rParams = rParams.replaceAt(nI, 1, "\\/"); + nLen++; + } + nI++; + } + + // Deal with language differences in date format expression. + // Should be made with i18n framework. + // The list of the mappings and of those "special" locales is to be found at: + // http://l10n.openoffice.org/i18n_framework/LocaleData.html + if ( !bForceJapanese && !bForceNatNum ) + { + // Convert to the localized equivalent for OOo + if ( rLang == LANGUAGE_FINNISH ) + { + if (nChar == 'y' || nChar == 'Y') + rParams = rParams.replaceAt(nI, 1, "V"); + else if (nChar == 'm' || nChar == 'M') + rParams = rParams.replaceAt(nI, 1, "K"); + else if (nChar == 'd' || nChar == 'D') + rParams = rParams.replaceAt(nI, 1, "P"); + else if (nChar == 'h' || nChar == 'H') + rParams = rParams.replaceAt(nI, 1, "T"); + } + else if ( rLang.anyOf( + LANGUAGE_DANISH, + LANGUAGE_NORWEGIAN, + LANGUAGE_NORWEGIAN_BOKMAL, + LANGUAGE_NORWEGIAN_NYNORSK, + LANGUAGE_SWEDISH, + LANGUAGE_SWEDISH_FINLAND)) + { + if (nChar == 'h' || nChar == 'H') + rParams = rParams.replaceAt(nI, 1, "T"); + } + else if ( rLang.anyOf( + LANGUAGE_PORTUGUESE, + LANGUAGE_PORTUGUESE_BRAZILIAN, + LANGUAGE_SPANISH_MODERN, + LANGUAGE_SPANISH_DATED, + LANGUAGE_SPANISH_MEXICAN, + LANGUAGE_SPANISH_GUATEMALA, + LANGUAGE_SPANISH_COSTARICA, + LANGUAGE_SPANISH_PANAMA, + LANGUAGE_SPANISH_DOMINICAN_REPUBLIC, + LANGUAGE_SPANISH_VENEZUELA, + LANGUAGE_SPANISH_COLOMBIA, + LANGUAGE_SPANISH_PERU, + LANGUAGE_SPANISH_ARGENTINA, + LANGUAGE_SPANISH_ECUADOR, + LANGUAGE_SPANISH_CHILE, + LANGUAGE_SPANISH_URUGUAY, + LANGUAGE_SPANISH_PARAGUAY, + LANGUAGE_SPANISH_BOLIVIA, + LANGUAGE_SPANISH_EL_SALVADOR, + LANGUAGE_SPANISH_HONDURAS, + LANGUAGE_SPANISH_NICARAGUA, + LANGUAGE_SPANISH_PUERTO_RICO)) + { + if (nChar == 'a' || nChar == 'A') + rParams = rParams.replaceAt(nI, 1, "O"); + else if (nChar == 'y' || nChar == 'Y') + rParams = rParams.replaceAt(nI, 1, "A"); + } + else if ( rLang.anyOf( + LANGUAGE_DUTCH, + LANGUAGE_DUTCH_BELGIAN)) + { + if (nChar == 'y' || nChar == 'Y') + rParams = rParams.replaceAt(nI, 1, "J"); + else if (nChar == 'u' || nChar == 'U') + rParams = rParams.replaceAt(nI, 1, "H"); + } + else if ( rLang.anyOf( + LANGUAGE_ITALIAN, + LANGUAGE_ITALIAN_SWISS)) + { + if (nChar == 'a' || nChar == 'A') + rParams = rParams.replaceAt(nI, 1, "O"); + else if (nChar == 'g' || nChar == 'G') + rParams = rParams.replaceAt(nI, 1, "X"); + else if (nChar == 'y' || nChar == 'Y') + rParams = rParams.replaceAt(nI, 1, "A"); + else if (nChar == 'd' || nChar == 'D') + rParams = rParams.replaceAt(nI, 1, "G"); + } + else if ( rLang.anyOf( + LANGUAGE_GERMAN, + LANGUAGE_GERMAN_SWISS, + LANGUAGE_GERMAN_AUSTRIAN, + LANGUAGE_GERMAN_LUXEMBOURG, + LANGUAGE_GERMAN_LIECHTENSTEIN)) + { + if (nChar == 'y' || nChar == 'Y') + rParams = rParams.replaceAt(nI, 1, "J"); + else if (nChar == 'd' || nChar == 'D') + rParams = rParams.replaceAt(nI, 1, "T"); + } + else if ( rLang.anyOf( + LANGUAGE_FRENCH, + LANGUAGE_FRENCH_BELGIAN, + LANGUAGE_FRENCH_CANADIAN, + LANGUAGE_FRENCH_SWISS, + LANGUAGE_FRENCH_LUXEMBOURG, + LANGUAGE_FRENCH_MONACO)) + { + if (nChar == 'y' || nChar == 'Y' || nChar == 'a') + rParams = rParams.replaceAt(nI, 1, "A"); + else if (nChar == 'd' || nChar == 'D' || nChar == 'j') + rParams = rParams.replaceAt(nI, 1, "J"); + } + } + } + } + + if (bForceNatNum) + bForceJapanese = true; + + if (bForceJapanese) + rLang = LANGUAGE_JAPANESE; + + if (bForceNatNum) + rParams = "[NatNum1][$-411]" + rParams; + + if (bHijri) + rParams = "[~hijri]" + rParams; + + pFormatter->PutEntry(rParams, nCheckPos, nType, nKey, rLang); + + return nKey; + } + + bool IsPreviousAM(OUString const & rParams, sal_Int32 nPos) + { + return nPos>=2 && rParams.matchIgnoreAsciiCase("am", nPos-2); + } + bool IsNextPM(OUString const & rParams, sal_Int32 nPos) + { + return nPos+2<rParams.getLength() && rParams.matchIgnoreAsciiCase("pm", nPos+1); + } + bool IsNotAM(OUString const & rParams, sal_Int32 nPos) + { + ++nPos; + return nPos>=rParams.getLength() || (rParams[nPos]!='M' && rParams[nPos]!='m'); + } + + void SwapQuotesInField(OUString &rFormat) + { + //Swap unescaped " and ' with ' and " + const sal_Int32 nLen = rFormat.getLength(); + for (sal_Int32 nI = 0; nI < nLen; ++nI) + { + if (!nI || rFormat[nI-1]!='\\') + { + if (rFormat[nI]=='\"') + rFormat = rFormat.replaceAt(nI, 1, "\'"); + else if (rFormat[nI]=='\'') + rFormat = rFormat.replaceAt(nI, 1, "\""); + } + } + } + + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/writerwordglue.hxx b/sw/source/filter/ww8/writerwordglue.hxx new file mode 100644 index 000000000..3b5323566 --- /dev/null +++ b/sw/source/filter/ww8/writerwordglue.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WRITERWORDGLUE_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WRITERWORDGLUE_HXX + +#include "needed_cast.hxx" + +class SwFrameFormat; +class SfxItemSet; + +namespace sw +{ + namespace types + { + /** A static_cast style cast for conversion of word types to writer's + + There are a number of places where the winword types are larger + than the writer equivalents requiring a cast to silence warnings. + To avoid throwing away this useful information writer_cast is used + to identify where writer's types are smaller than word's. + + Based on needed_cast it will compile time assert if the cast + becomes unnecessary at any time in the future. + + @tplparam + Ret the desired return type + + @tplparam + Param the type of the in param + + @param + in the value to cast from Param to Ret + + @return in casted to type Ret + */ + template<typename Ret, typename Param> Ret writer_cast(Param in) + { + return ww::needed_cast<Ret, Param>(in); + } + + /** A static_cast style cast for conversion of writer types to word's + + There are a number of places where the writer types are larger than + the winword equivalents requiring a cast to silence warnings. To + avoid throwing away this useful information writer_cast is used to + identify where word's types are smaller than writers's. + + Based on needed_cast it will compile time assert if the cast + becomes unnecessary at any time in the future. + + @tplparam + Ret the desired return type + + @tplparam + Param the type of the in param + + @param + in the value to cast from Param to Ret + + @return in casted to type Ret + */ + template<typename Ret, typename Param> Ret msword_cast(Param in) + { + return ww::needed_cast<Ret, Param>(in); + } + } + + namespace util + { + /** See if two page formats can be expressed as a single word section + + Word doesn't have the idea of page descriptors and follow styles + like writer does, the only thing it has is a section with a + different title page. The only difference of the title page from + the rest of the section is different headers/footers, everything + else is the same. + + So this function compares two writer page fmts and sees if the + follow frame and the title frame are the same from word perspective + except for the content of their headers. + + @return true if the rTitleFormat followed by rFollowFormat could be + expressed in word as a single word Section with different title + page enabled. + + @see #i4320#/#i14509#/#i11717# for examples + */ + bool IsPlausableSingleWordSection(const SwFrameFormat &rTitleFormat, + const SwFrameFormat &rFollowFormat); + + /** Make export a word section top/bottom values easy + + The top and bottom margins in word and writer are expressed in very + different ways. This class provides the equivalent word values for + header/footer distances from a given writer attrset of a page + */ + class HdFtDistanceGlue + { + private: + bool mbHasHeader; + bool mbHasFooter; + public: + sal_uInt16 dyaHdrTop; + sal_uInt16 dyaHdrBottom; + sal_uInt16 dyaTop; + sal_uInt16 dyaBottom; + explicit HdFtDistanceGlue(const SfxItemSet &rPage); + bool HasHeader() const { return mbHasHeader; } + bool HasFooter() const { return mbHasFooter; } + + /** Is the top of the page the same in both objects + when there are headers\footers present or non-present in both objects + + This test is important, because we would like to ignore cases + when there is a header in one object and no header in the second + object - because it is wrong to compare between them. + + @param + rOther the other HdFtDistanceGlue to compare against + + @return true if the main text areas top and bottom is at the + same location, false otherwise (assuming both objects have\don't have + a header\footer) + */ + bool StrictEqualTopBottom(const HdFtDistanceGlue &rOther) const; + + }; + } +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtw8esh.cxx b/sw/source/filter/ww8/wrtw8esh.cxx new file mode 100644 index 000000000..0c4e6c9d2 --- /dev/null +++ b/sw/source/filter/ww8/wrtw8esh.cxx @@ -0,0 +1,3076 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/embed/Aspects.hpp> + +#include <hintids.hxx> + +#include <o3tl/any.hxx> +#include <com/sun/star/drawing/XShape.hpp> +#include <vcl/svapp.hxx> +#include <sot/storage.hxx> +#include <svl/itemiter.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdpage.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <svx/svdouno.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdview.hxx> +#include <svx/unopage.hxx> +#include <fmtcnct.hxx> +#include <fmtanchr.hxx> +#include <fmtsrnd.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <fmtfollowtextflow.hxx> +#include <frmfmt.hxx> +#include <fmtcntnt.hxx> +#include <ndindex.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <swrect.hxx> +#include <ndgrf.hxx> +#include <grfatr.hxx> +#include <ndole.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> +#include "ww8par.hxx" +#include <breakit.hxx> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include "attributeoutputbase.hxx" +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "wrtww8.hxx" +#include "escher.hxx" +#include <ndtxt.hxx> +#include "WW8FFData.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/form/XFormComponent.hpp> +#include <IDocumentStylePoolAccess.hxx> +#include <oox/ole/olehelper.hxx> +#include <fmturl.hxx> +#include <frameformats.hxx> +#include <sfx2/sfxsids.hrc> +#include <unotools/saveopt.hxx> +#include <o3tl/enumrange.hxx> +#include <o3tl/enumarray.hxx> +#include <sfx2/docfile.hxx> + +#include <algorithm> + +using ::editeng::SvxBorderLine; +using namespace com::sun::star; +using namespace sw::util; +using namespace sw::types; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::drawing::XShape; + +bool SwBasicEscherEx::IsRelUrl() const +{ + SvtSaveOptions aSaveOpt; + bool bRelUrl = false; + SfxMedium * pMedium = rWrt.GetWriter().GetMedia(); + if ( pMedium ) + bRelUrl = pMedium->IsRemote() ? aSaveOpt.IsSaveRelINet() : aSaveOpt.IsSaveRelFSys(); + return bRelUrl; +} + +OUString SwBasicEscherEx::GetBasePath() const +{ + OUString sDocUrl; + SfxMedium * pMedium = rWrt.GetWriter().GetMedia(); + if (pMedium) + { + const SfxItemSet* pPItemSet = pMedium->GetItemSet(); + if( pPItemSet ) + { + const SfxStringItem* pPItem = dynamic_cast< const SfxStringItem* >( pPItemSet->GetItem( SID_FILE_NAME ) ); + if ( pPItem ) + sDocUrl = pPItem->GetValue(); + } + } + + return sDocUrl.copy(0, sDocUrl.lastIndexOf('/') + 1); +} + +OUString SwBasicEscherEx::BuildFileName(sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl) +{ + OUString aDosName( INetURLObject( rUrl ).getFSysPath( FSysStyle::Dos ) ); + rnLevel = 0; + rbRel = IsRelUrl(); + + if (rbRel) + { + // try to convert to relative file name + OUString aTmpName( aDosName ); + aDosName = INetURLObject::GetRelURL( GetBasePath(), rUrl, + INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::WithCharset ); + + if (aDosName.startsWith(INET_FILE_SCHEME)) + { + // not converted to rel -> back to old, return absolute flag + aDosName = aTmpName; + rbRel = false; + } + else if (aDosName.startsWith("./")) + { + aDosName = aDosName.copy(2); + } + else + { + while (aDosName.startsWith("../")) + { + ++rnLevel; + aDosName = aDosName.copy(3); + } + } + } + return aDosName; +} + +void SwBasicEscherEx::WriteHyperlinkWithinFly( SvMemoryStream& rStrm, const SwFormatURL* pINetFormatArg) +{ + if ( !pINetFormatArg ) return; + + const sal_uInt8 aGuidStdLink[ 16 ] ={ + 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B }; + const sal_uInt8 aGuidUrlMoniker[ 16 ] = { + 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B }; + + const sal_uInt8 aGuidFileMoniker[ 16 ] = { + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + const sal_uInt8 aGuidFileTail[] = { + 0xFF, 0xFF, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + //const sal_uInt18 WW8_ID_HLINK = 0x01B8; + const sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL. + const sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path. + //const sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description. + const sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark. + const sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame. + //const sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path. + SvMemoryStream tmpStrm; + OUString tmpTextMark; + + OUString rUrl = pINetFormatArg->GetURL(); + OUString rTarFrame = pINetFormatArg->GetTargetFrameName(); + sal_uInt32 nFlags = 0; + + INetURLObject aUrlObj( rUrl ); + const INetProtocol eProtocol = aUrlObj.GetProtocol(); + + //Target Frame + if (!rTarFrame.isEmpty()) + { + SwWW8Writer::WriteLong(tmpStrm, rTarFrame.getLength()+1); + SwWW8Writer::WriteString16(tmpStrm, rTarFrame, false); + + tmpStrm.WriteUInt16( 0 ); + + nFlags |= WW8_HLINK_FRAME; + } + + // file link or URL + if (eProtocol == INetProtocol::File || (eProtocol == INetProtocol::NotValid && rUrl[0] != '#')) + { + sal_uInt16 nLevel; + bool bRel; + OUString aFileName( BuildFileName( nLevel, bRel, rUrl )); + + if( !bRel ) + nFlags |= WW8_HLINK_ABS; + + nFlags |= WW8_HLINK_BODY; + + tmpStrm.WriteBytes(aGuidFileMoniker, sizeof(aGuidFileMoniker)); + tmpStrm.WriteUInt16( nLevel ); + SwWW8Writer::WriteLong(tmpStrm, aFileName.getLength()+1); + SwWW8Writer::WriteString8( tmpStrm, aFileName, true, RTL_TEXTENCODING_MS_1252 ); + tmpStrm.WriteBytes(aGuidFileTail, sizeof(aGuidFileTail)); + + //For UNICODE + SwWW8Writer::WriteLong(tmpStrm, 2*aFileName.getLength()+6); + SwWW8Writer::WriteLong(tmpStrm, 2*aFileName.getLength()); + tmpStrm.WriteUInt16( 0x0003 ); + SwWW8Writer::WriteString16(tmpStrm, aFileName, false); + } + else if( eProtocol != INetProtocol::NotValid ) + { + tmpStrm.WriteBytes(aGuidUrlMoniker, sizeof(aGuidUrlMoniker)); + SwWW8Writer::WriteLong(tmpStrm, 2*(rUrl.getLength()+1)); + + SwWW8Writer::WriteString16(tmpStrm, rUrl, true); + nFlags |= WW8_HLINK_BODY | WW8_HLINK_ABS; + } + else if (rUrl[0] == '#' ) + { + OUString aTextMark(rUrl.copy( 1 )); + aTextMark = aTextMark.replaceFirst(".", "!"); + tmpTextMark = aTextMark; + } + + if (tmpTextMark.isEmpty() && aUrlObj.HasMark()) + { + tmpTextMark = aUrlObj.GetMark(); + } + + if (!tmpTextMark.isEmpty()) + { + SwWW8Writer::WriteLong(tmpStrm, tmpTextMark.getLength()+1); + SwWW8Writer::WriteString16(tmpStrm, tmpTextMark, true); + + nFlags |= WW8_HLINK_MARK; + } + + rStrm.WriteBytes(aGuidStdLink, 16); + rStrm .WriteUInt32( 2 ) + .WriteUInt32( nFlags ); + tmpStrm.Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt32 const nLen = tmpStrm.remainingSize(); + if(nLen >0) + { + std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[ nLen ] ); + tmpStrm.ReadBytes(pBuffer.get(), nLen); + rStrm.WriteBytes(pBuffer.get(), nLen); + } +} +void SwBasicEscherEx::PreWriteHyperlinkWithinFly(const SwFrameFormat& rFormat,EscherPropertyContainer& rPropOpt) +{ + const SfxPoolItem* pItem; + const SwAttrSet& rAttrSet = rFormat.GetAttrSet(); + if (SfxItemState::SET == rAttrSet.GetItemState(RES_URL, true, &pItem)) + { + const SwFormatURL *pINetFormat = dynamic_cast<const SwFormatURL*>(pItem); + if (pINetFormat && !pINetFormat->GetURL().isEmpty()) + { + SvMemoryStream aStrm; + WriteHyperlinkWithinFly( aStrm, pINetFormat ); + rPropOpt.AddOpt(ESCHER_Prop_pihlShape, true, 0, aStrm); + sal_uInt32 nValue; + OUString aNamestr = pINetFormat->GetName(); + if (!aNamestr.isEmpty()) + { + rPropOpt.AddOpt(ESCHER_Prop_wzName, aNamestr ); + } + if(rPropOpt.GetOpt( ESCHER_Prop_fPrint, nValue)) + { + nValue|=0x03080008; + rPropOpt.AddOpt(ESCHER_Prop_fPrint, nValue ); + } + else + rPropOpt.AddOpt(ESCHER_Prop_fPrint, 0x03080008 ); + } + } +} + +namespace +{ + /// Get the Z ordering number for a DrawObj in a WW8Export. + /// @param rWrt The containing WW8Export. + /// @param pObj pointer to the drawing object. + /// @returns The ordering number. + sal_uLong lcl_getSdrOrderNumber(const WW8Export& rWrt, DrawObj const *pObj) + { + return rWrt.GetSdrOrdNum(pObj->maContent.GetFrameFormat()); + }; + + /// A function object to act as a predicate comparing the ordering numbers + /// of two drawing obejcts in a WW8Export. + class CompareDrawObjs + { + private: + const WW8Export& wrt; + + public: + explicit CompareDrawObjs(const WW8Export& rWrt) : wrt(rWrt) {}; + bool operator()(DrawObj const *a, DrawObj const *b) const + { + sal_uLong aSort = lcl_getSdrOrderNumber(wrt, a); + sal_uLong bSort = lcl_getSdrOrderNumber(wrt, b); + return aSort < bSort; + } + }; + + /// Make a z-order sorted copy of a collection of DrawObj objects. + /// @param rWrt The containing WW8Export. + /// @param rSrcArr The source array. + /// @param rDstArr The destination array. + void lcl_makeZOrderArray(const WW8Export& rWrt, + std::vector<DrawObj> &rSrcArr, + std::vector<DrawObj*> &rDstArr) + { + rDstArr.clear(); + rDstArr.reserve(rSrcArr.size()); + for(DrawObj & i : rSrcArr) + { + rDstArr.push_back( &i ); + } + std::sort(rDstArr.begin(), rDstArr.end(), CompareDrawObjs(rWrt)); + } + +} + +// get a part fix for this type of element +bool WW8Export::MiserableFormFieldExportHack(const SwFrameFormat& rFrameFormat) +{ + const SdrObject *pObject = rFrameFormat.FindRealSdrObject(); + if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm) + return false; + + const SdrUnoObj *pFormObj = dynamic_cast< const SdrUnoObj* >(pObject); + if (!pFormObj) + return false; + + uno::Reference< awt::XControlModel > xControlModel = + pFormObj->GetUnoControlModel(); + uno::Reference< lang::XServiceInfo > xInfo(xControlModel, + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY); + if (!xInfo.is()) + return false; + + if (xInfo->supportsService("com.sun.star.form.component.ComboBox")) + { + DoComboBox(xPropSet); + return true; + } + + return false; +} + +void WW8Export::DoComboBox(uno::Reference<beans::XPropertySet> const & xPropSet) +{ + OUString sSelected; + uno::Sequence<OUString> aListItems; + xPropSet->getPropertyValue("StringItemList") >>= aListItems; + if (aListItems.hasElements()) + { + uno::Any aTmp = xPropSet->getPropertyValue("DefaultText"); + auto pStr = o3tl::tryAccess<OUString>(aTmp); + if (pStr) + sSelected = *pStr; + } + + OUString sName; + { + uno::Any aTmp = xPropSet->getPropertyValue("Name"); + auto pStr = o3tl::tryAccess<OUString>(aTmp); + if (pStr) + sName = *pStr; + } + + OUString sHelp; + { + // property "Help" does not exist and due to the no-existence an exception is thrown. + try + { + uno::Any aTmp = xPropSet->getPropertyValue("HelpText"); + auto pStr = o3tl::tryAccess<OUString>(aTmp); + if (pStr) + sHelp = *pStr; + } + catch( const uno::Exception& ) + {} + } + + OUString sToolTip; + { + uno::Any aTmp = xPropSet->getPropertyValue("Name"); + auto pStr = o3tl::tryAccess<OUString>(aTmp); + if (pStr) + sToolTip = *pStr; + } + + DoComboBox(sName, sHelp, sToolTip, sSelected, aListItems); +} + +void WW8Export::DoComboBox(const OUString &rName, + const OUString &rHelp, + const OUString &rToolTip, + const OUString &rSelected, + const uno::Sequence<OUString> &rListItems) +{ + OutputField(nullptr, ww::eFORMDROPDOWN, FieldString(ww::eFORMDROPDOWN), + FieldFlags::Start | FieldFlags::CmdStart); + // write the reference to the "picture" structure + sal_uLong nDataStt = pDataStrm->Tell(); + m_pChpPlc->AppendFkpEntry( Strm().Tell() ); + + WriteChar( 0x01 ); + + static sal_uInt8 aArr1[] = + { + 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation + 0x06, 0x08, 0x01, // sprmCFData + 0x55, 0x08, 0x01, // sprmCFSpec + 0x02, 0x08, 0x01 // sprmCFFieldVanish + }; + sal_uInt8* pDataAdr = aArr1 + 2; + Set_UInt32( pDataAdr, nDataStt ); + + m_pChpPlc->AppendFkpEntry(Strm().Tell(), sizeof(aArr1), aArr1); + + OutputField(nullptr, ww::eFORMDROPDOWN, FieldString(ww::eFORMDROPDOWN), + FieldFlags::Close); + + ::sw::WW8FFData aFFData; + + aFFData.setType(2); + aFFData.setName(rName); + aFFData.setHelp(rHelp); + aFFData.setStatus(rToolTip); + + sal_uInt32 nListItems = rListItems.getLength(); + + for (sal_uInt32 i = 0; i < nListItems; i++) + { + if (i < 0x20 && rSelected == rListItems[i]) + aFFData.setResult(::sal::static_int_cast<sal_uInt8>(i)); + aFFData.addListboxEntry(rListItems[i]); + } + + aFFData.Write(pDataStrm); +} + +void WW8Export::DoFormText(const SwInputField * pField) +{ + OutputField(nullptr, ww::eFORMTEXT, FieldString(ww::eFORMTEXT), + FieldFlags::Start | FieldFlags::CmdStart); + // write the reference to the "picture" structure + sal_uLong nDataStt = pDataStrm->Tell(); + m_pChpPlc->AppendFkpEntry( Strm().Tell() ); + + WriteChar( 0x01 ); + static sal_uInt8 aArr1[] = { + 0x02, 0x08, 0x81, // sprmCFFieldVanish + 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation + + 0x06, 0x08, 0x01, // sprmCFData + 0x55, 0x08, 0x01 // sprmCFSpec + }; + sal_uInt8* pDataAdr = aArr1 + 5; + Set_UInt32( pDataAdr, nDataStt ); + + m_pChpPlc->AppendFkpEntry(Strm().Tell(), + sizeof( aArr1 ), aArr1 ); + + ::sw::WW8FFData aFFData; + + aFFData.setType(0); + aFFData.setName(pField->GetPar2()); + aFFData.setHelp(pField->GetHelp()); + aFFData.setStatus(pField->GetToolTip()); + aFFData.Write(pDataStrm); + + OutputField(nullptr, ww::eFORMTEXT, OUString(), FieldFlags::CmdEnd); + + const OUString fieldStr( pField->ExpandField(true, nullptr) ); + SwWW8Writer::WriteString16(Strm(), fieldStr, false); + + static sal_uInt8 aArr2[] = { + 0x55, 0x08, 0x01, // sprmCFSpec + 0x75, 0x08, 0x01 // ??? + }; + + pDataAdr = aArr2 + 2; + Set_UInt32( pDataAdr, nDataStt ); + m_pChpPlc->AppendFkpEntry(Strm().Tell(), + sizeof( aArr2 ), aArr2 ); + + OutputField(nullptr, ww::eFORMTEXT, OUString(), FieldFlags::Close); +} + +PlcDrawObj::~PlcDrawObj() +{ +} + +//It's irritating to have to change the RTL frames position into LTR ones +//so that word will have to place them in the right place. Doubly so that +//the SO drawings and writer frames have different ideas themselves as to +//how to be positioned when in RTL mode! +bool RTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth, +sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft, + SwTwips nPageRight, SwTwips nPageSize) +{ + bool bRet = false; + if (eHoriOri == text::HoriOrientation::NONE) + { + if (eHoriRel == text::RelOrientation::PAGE_FRAME) + { + rLeft = nPageSize - rLeft; + bRet = true; + } + else if ( + (eHoriRel == text::RelOrientation::PAGE_PRINT_AREA) || + (eHoriRel == text::RelOrientation::FRAME) || + (eHoriRel == text::RelOrientation::PRINT_AREA) + ) + { + rLeft = nPageSize - nPageLeft - nPageRight - rLeft; + bRet = true; + } + } + if (bRet) + rLeft -= nWidth; + return bRet; +} + +static bool RTLDrawingsHack(long &rLeft, + sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft, + SwTwips nPageRight, SwTwips nPageSize) +{ + bool bRet = false; + if (eHoriOri == text::HoriOrientation::NONE) + { + if (eHoriRel == text::RelOrientation::PAGE_FRAME) + { + rLeft = nPageSize + rLeft; + bRet = true; + } + else if ( + (eHoriRel == text::RelOrientation::PAGE_PRINT_AREA) || + (eHoriRel == text::RelOrientation::FRAME) || + (eHoriRel == text::RelOrientation::PRINT_AREA) + ) + { + rLeft = nPageSize - nPageLeft - nPageRight + rLeft; + bRet = true; + } + } + return bRet; +} + +void WW8Export::MiserableRTLFrameFormatHack(SwTwips &rLeft, SwTwips &rRight, + const ww8::Frame &rFrameFormat) +{ + //Require nasty bidi swap + if (SvxFrameDirection::Horizontal_RL_TB != m_pDoc->GetTextDirection(rFrameFormat.GetPosition())) + return; + + SwTwips nWidth = rRight - rLeft; + SwTwips nPageLeft, nPageRight; + SwTwips nPageSize = CurrentPageWidth(nPageLeft, nPageRight); + + const SwFormatHoriOrient& rHOr = rFrameFormat.GetFrameFormat().GetHoriOrient(); + + bool bRet = false; + ww8::Frame::WriterSource eSource = rFrameFormat.GetWriterType(); + if (eSource == ww8::Frame::eDrawing || eSource == ww8::Frame::eFormControl) + { + if (RTLDrawingsHack(rLeft, rHOr.GetHoriOrient(), + rHOr.GetRelationOrient(), nPageLeft, nPageRight, nPageSize)) + { + bRet = true; + } + } + else + { + if (RTLGraphicsHack(rLeft, nWidth, rHOr.GetHoriOrient(), + rHOr.GetRelationOrient(), nPageLeft, nPageRight, nPageSize)) + { + bRet = true; + } + } + if (bRet) + rRight = rLeft + nWidth; +} + +void PlcDrawObj::WritePlc( WW8Export& rWrt ) const +{ + if (8 > rWrt.pFib->m_nVersion) // Cannot export drawobject in vers 7- + return; + + sal_uInt32 nFcStart = rWrt.pTableStrm->Tell(); + + if (!maDrawObjs.empty()) + { + // write CPs + WW8Fib& rFib = *rWrt.pFib; + WW8_CP nCpOffs = GetCpOffset(rFib); + + for (const auto& rDrawObj : maDrawObjs) + SwWW8Writer::WriteLong(*rWrt.pTableStrm, rDrawObj.mnCp - nCpOffs); + + SwWW8Writer::WriteLong(*rWrt.pTableStrm, rFib.m_ccpText + rFib.m_ccpFootnote + + rFib.m_ccpHdr + rFib.m_ccpEdn + rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1); + + for (const auto& rDrawObj : maDrawObjs) + { + // write the fspa-struct + const ww8::Frame &rFrameFormat = rDrawObj.maContent; + const SwFrameFormat &rFormat = rFrameFormat.GetFrameFormat(); + const SdrObject* pObj = rFormat.FindRealSdrObject(); + + tools::Rectangle aRect; + SwFormatVertOrient rVOr = rFormat.GetVertOrient(); + SwFormatHoriOrient rHOr = rFormat.GetHoriOrient(); + // #i30669# - convert the positioning attributes. + // Most positions are converted, if layout information exists. + const bool bPosConverted = + WinwordAnchoring::ConvertPosition( rHOr, rVOr, rFormat ); + + Point aObjPos; + bool bHasHeightWidthSwapped(false); + if (RES_FLYFRMFMT == rFormat.Which()) + { + SwRect aLayRect(rFormat.FindLayoutRect(false, &aObjPos)); + // the Object is not visible - so get the values from + // the format. The Position may not be correct. + if( aLayRect.IsEmpty() ) + aRect.SetSize( rFormat.GetFrameSize().GetSize() ); + else + { + // #i56090# Do not only consider the first client + // Note that we actually would have to find the maximum size of the + // frame format clients. However, this already should work in most cases. + const SwRect aSizeRect(rFormat.FindLayoutRect()); + if ( aSizeRect.Width() > aLayRect.Width() ) + aLayRect.Width( aSizeRect.Width() ); + + aRect = aLayRect.SVRect(); + } + } + else + { + OSL_ENSURE(pObj, "Where is the SDR-Object?"); + if (pObj) + { + aRect = pObj->GetLogicRect(); + + // rotating to vertical means swapping height and width as seen in SvxMSDffManager::ImportShape + const long nAngle = NormAngle36000( pObj->GetRotateAngle() ); + const bool bAllowSwap = pObj->GetObjIdentifier() != OBJ_LINE && pObj->GetObjIdentifier() != OBJ_GRUP; + if ( bAllowSwap && (( nAngle > 4500 && nAngle <= 13500 ) || ( nAngle > 22500 && nAngle <= 31500 )) ) + { + const long nWidth = aRect.getWidth(); + const long nHeight = aRect.getHeight(); + aRect.setWidth( nHeight ); + aRect.setHeight( nWidth ); + bHasHeightWidthSwapped = true; + } + } + } + + // #i30669# - use converted position, if conversion is performed. + // Unify position determination of Writer fly frames + // and drawing objects. + if ( bPosConverted ) + { + aRect.SetPos( Point( rHOr.GetPos(), rVOr.GetPos() ) ); + } + else + { + aRect -= rDrawObj.maParentPos; + aObjPos = aRect.TopLeft(); + if (text::VertOrientation::NONE == rVOr.GetVertOrient()) + { + // #i22673# + sal_Int16 eOri = rVOr.GetRelationOrient(); + if (eOri == text::RelOrientation::CHAR || eOri == text::RelOrientation::TEXT_LINE) + aObjPos.setY( -rVOr.GetPos() ); + else + aObjPos.setY( rVOr.GetPos() ); + } + if (text::HoriOrientation::NONE == rHOr.GetHoriOrient()) + aObjPos.setX( rHOr.GetPos() ); + aRect.SetPos( aObjPos ); + } + + sal_Int32 nThick = rDrawObj.mnThick; + + //If we are being exported as an inline hack, set + //corner to 0 and forget about border thickness for positioning + if (rFrameFormat.IsInline()) + { + aRect.SetPos(Point(0,0)); + nThick = 0; + } + + // spid + SwWW8Writer::WriteLong(*rWrt.pTableStrm, rDrawObj.mnShapeId); + + SwTwips nLeft = aRect.Left() + nThick; + SwTwips nRight = aRect.Right() - nThick; + SwTwips nTop = aRect.Top() + nThick; + SwTwips nBottom = aRect.Bottom() - nThick; + + // tdf#93675, 0 below line/paragraph and/or top line/paragraph with + // wrap top+bottom or other wraps is affecting the line directly + // above the anchor line, which seems odd, but a tiny adjustment + // here to bring the top down convinces msoffice to wrap like us + if (nTop == 0 && !rFrameFormat.IsInline() && + rVOr.GetVertOrient() == text::VertOrientation::NONE && + rVOr.GetRelationOrient() == text::RelOrientation::FRAME) + { + nTop = 8; + } + + //Nasty swap for bidi if necessary + rWrt.MiserableRTLFrameFormatHack(nLeft, nRight, rFrameFormat); + + // tdf#70838. Word relates the position to the unrotated rectangle, + // Writer to the rotated one. Because the rotation is around center, + // the difference counts half. + if(pObj && pObj->GetRotateAngle()) + { + SwTwips nXOff; + SwTwips nYOff; + SwTwips nSnapWidth = pObj->GetSnapRect().getWidth(); + SwTwips nSnapHeight = pObj->GetSnapRect().getHeight(); + SwTwips nLogicWidth = pObj->GetLogicRect().getWidth(); + SwTwips nLogicHeight = pObj->GetLogicRect().getHeight(); + // +1 for to compensate integer arithmetic rounding errors + if(bHasHeightWidthSwapped) + { + nXOff = (nSnapWidth - nLogicHeight + 1) / 2; + nYOff = (nSnapHeight - nLogicWidth + 1) / 2; + } + else + { + nXOff = (nSnapWidth - nLogicWidth + 1) / 2; + nYOff = (nSnapHeight - nLogicHeight + 1) / 2; + } + nLeft += nXOff; + nRight += nXOff; + nTop += nYOff; + nBottom += nYOff; + } + + //xaLeft/yaTop/xaRight/yaBottom - rel. to anchor + //(most of) the border is outside the graphic is word, so + //change dimensions to fit + SwWW8Writer::WriteLong(*rWrt.pTableStrm, nLeft); + SwWW8Writer::WriteLong(*rWrt.pTableStrm, nTop); + SwWW8Writer::WriteLong(*rWrt.pTableStrm, nRight); + SwWW8Writer::WriteLong(*rWrt.pTableStrm, nBottom); + + //fHdr/bx/by/wr/wrk/fRcaSimple/fBelowText/fAnchorLock + sal_uInt16 nFlags=0; + //If nFlags isn't 0x14 its overridden by the escher properties + if (RndStdIds::FLY_AT_PAGE == rFormat.GetAnchor().GetAnchorId()) + nFlags = 0x0000; + else + nFlags = 0x0014; // x-rel to text, y-rel to text + + const SwFormatSurround& rSurr = rFormat.GetSurround(); + sal_uInt16 nContour = rSurr.IsContour() ? 0x0080 : 0x0040; + css::text::WrapTextMode eSurround = rSurr.GetSurround(); + + /* + #i3958# + The inline elements being export as anchored to character inside + the shape field hack are required to be wrap through so as to flow + over the following dummy 0x01 graphic + */ + if (rFrameFormat.IsInline()) + eSurround = css::text::WrapTextMode_THROUGH; + + switch (eSurround) + { + case css::text::WrapTextMode_NONE: + nFlags |= 0x0020; + break; + case css::text::WrapTextMode_THROUGH: + nFlags |= 0x0060; + break; + case css::text::WrapTextMode_PARALLEL: + nFlags |= 0x0000 | nContour; + break; + case css::text::WrapTextMode_DYNAMIC: + nFlags |= 0x0600 | nContour; + break; + case css::text::WrapTextMode_LEFT: + nFlags |= 0x0200 | nContour; + break; + case css::text::WrapTextMode_RIGHT: + nFlags |= 0x0400 | nContour; + break; + default: + OSL_ENSURE(false, "Unsupported surround type for export"); + break; + } + if (pObj && (pObj->GetLayer() == rWrt.m_pDoc->getIDocumentDrawModelAccess().GetHellId() || + pObj->GetLayer() == rWrt.m_pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId())) + { + nFlags |= 0x4000; + } + + /* + #i3958# Required to make this inline stuff work in WordXP, not + needed for 2003 interestingly + */ + if (rFrameFormat.IsInline()) + nFlags |= 0x8000; + + SwWW8Writer::WriteShort(*rWrt.pTableStrm, nFlags); + + // cTxbx + SwWW8Writer::WriteLong(*rWrt.pTableStrm, 0); + } + + RegisterWithFib(rFib, nFcStart, rWrt.pTableStrm->Tell() - nFcStart); + } +} + +void MainTextPlcDrawObj::RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart, + sal_uInt32 nLen) const +{ + rFib.m_fcPlcfspaMom = nStart; + rFib.m_lcbPlcfspaMom = nLen; +} + +WW8_CP MainTextPlcDrawObj::GetCpOffset(const WW8Fib &) const +{ + return 0; +} + +void HdFtPlcDrawObj::RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart, + sal_uInt32 nLen) const +{ + rFib.m_fcPlcfspaHdr = nStart; + rFib.m_lcbPlcfspaHdr = nLen; +} + +WW8_CP HdFtPlcDrawObj::GetCpOffset(const WW8Fib &rFib) const +{ + return rFib.m_ccpText + rFib.m_ccpFootnote; +} + +bool PlcDrawObj::Append( WW8Export const & rWrt, WW8_CP nCp, const ww8::Frame& rFormat, + const Point& rNdTopLeft ) +{ + bool bRet = false; + const SwFrameFormat &rFrameFormat = rFormat.GetFrameFormat(); + if (TXT_HDFT == rWrt.m_nTextTyp || TXT_MAINTEXT == rWrt.m_nTextTyp) + { + if (RES_FLYFRMFMT == rFrameFormat.Which()) + { + // check for textflyframe and if it is the first in a Chain + if (rFrameFormat.GetContent().GetContentIdx()) + bRet = true; + } + else + bRet = true; + } + + if (bRet) + { + DrawObj aObj(rFormat, nCp, rNdTopLeft, rWrt.TrueFrameDirection(rFrameFormat), + rWrt.GetHdFtIndex()); + maDrawObjs.push_back(aObj); + } + return bRet; +} + +void DrawObj::SetShapeDetails(sal_uInt32 nId, sal_Int32 nThick) +{ + mnShapeId = nId; + mnThick = nThick; +} + +bool WW8_WrPlcTextBoxes::WriteText( WW8Export& rWrt ) +{ + rWrt.m_bInWriteEscher = true; + WW8_CP& rccp=TXT_TXTBOX == nTyp ? rWrt.pFib->m_ccpTxbx : rWrt.pFib->m_ccpHdrTxbx; + + bool bRet = WriteGenericText( rWrt, nTyp, rccp ); + + WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + WW8Fib& rFib = *rWrt.pFib; + WW8_CP nMyOffset = rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpAtn + + rFib.m_ccpEdn; + if( TXT_TXTBOX == nTyp ) + rWrt.m_pFieldTextBxs->Finish( nCP, nMyOffset ); + else + rWrt.m_pFieldHFTextBxs->Finish( nCP, nMyOffset + rFib.m_ccpTxbx ); + rWrt.m_bInWriteEscher = false; + return bRet; +} + +void WW8_WrPlcTextBoxes::Append( const SdrObject& rObj, sal_uInt32 nShapeId ) +{ + aContent.push_back( &rObj ); + aShapeIds.push_back( nShapeId ); + //save NULL, if we have an actual SdrObject + aSpareFormats.push_back(nullptr); +} + +void WW8_WrPlcTextBoxes::Append( const SwFrameFormat* pFormat, sal_uInt32 nShapeId ) +{ + //no sdr object, we insert a NULL in the aContent and save the real fmt in aSpareFormats. + aContent.push_back( nullptr ); + aShapeIds.push_back( nShapeId ); + aSpareFormats.push_back(pFormat); +} + +const std::vector<sal_uInt32>* WW8_WrPlcTextBoxes::GetShapeIdArr() const +{ + return &aShapeIds; +} + +sal_uInt32 WW8Export::GetSdrOrdNum( const SwFrameFormat& rFormat ) const +{ + sal_uInt32 nOrdNum; + const SdrObject* pObj = rFormat.FindRealSdrObject(); + if( pObj ) + nOrdNum = pObj->GetOrdNum(); + else + { + // no Layout for this format, then recalc the ordnum + SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(&rFormat); + nOrdNum = std::distance(m_pDoc->GetSpzFrameFormats()->begin(), + m_pDoc->GetSpzFrameFormats()->find( pFormat ) ); + + const SwDrawModel* pModel = m_pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if( pModel ) + nOrdNum += pModel->GetPage( 0 )->GetObjCount(); + } + return nOrdNum; +} + +void WW8Export::AppendFlyInFlys(const ww8::Frame& rFrameFormat, + const Point& rNdTopLeft) +{ + OSL_ENSURE(!m_pEscher, "the EscherStream was already written!"); + if (m_pEscher) + return ; + PlcDrawObj *pDrwO; + if (TXT_HDFT == m_nTextTyp) + pDrwO = m_pHFSdrObjs; + else + pDrwO = m_pSdrObjs; + + if (rFrameFormat.IsInline()) + { + OutputField(nullptr, ww::eSHAPE, FieldString(ww::eSHAPE), + FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd); + } + + WW8_CP nCP = Fc2Cp(Strm().Tell()); + bool bSuccess = pDrwO->Append(*this, nCP, rFrameFormat, rNdTopLeft); + OSL_ENSURE(bSuccess, "Couldn't export a graphical element!"); + + if (bSuccess) + { + static const sal_uInt8 aSpec8[] = + { + 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation + 0x55, 0x08, 1 // sprmCFSpec + }; + // fSpec-Attribute true + + // A special character is required in the text for DrawObjects, + // therefore a fSpec-Attribute + m_pChpPlc->AppendFkpEntry( Strm().Tell() ); + WriteChar( 0x8 ); + m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aSpec8 ), aSpec8 ); + + //Need dummy picture frame + if (rFrameFormat.IsInline()) + OutGrf(rFrameFormat); + } + + if (rFrameFormat.IsInline()) + OutputField(nullptr, ww::eSHAPE, OUString(), FieldFlags::Close); +} + +MSWord_SdrAttrIter::MSWord_SdrAttrIter( MSWordExportBase& rWr, + const EditTextObject& rEditObj, sal_uInt8 nTyp ) + : MSWordAttrIter( rWr ), pEditObj(&rEditObj), pEditPool(nullptr), mnTyp(nTyp) +{ + NextPara( 0 ); +} + +void MSWord_SdrAttrIter::NextPara( sal_Int32 nPar ) +{ + nPara = nPar; + // Ignore change of attribute at position 0, because we expect that + // the attributes are outputted at start of a paragraph anyway. + aChrTextAtrArr.clear(); + aChrSetArr.clear(); + nCurrentSwPos = nTmpSwPos = 0; + + SfxItemSet aSet( pEditObj->GetParaAttribs( nPara )); + pEditPool = aSet.GetPool(); + eNdChrSet = ItemGet<SvxFontItem>(aSet,EE_CHAR_FONTINFO).GetCharSet(); + + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + nScript = g_pBreakIt->GetBreakIter()->getScriptType( pEditObj->GetText(nPara), 0); + + pEditObj->GetCharAttribs( nPara, aTextAtrArr ); + nCurrentSwPos = SearchNext( 1 ); +} + +rtl_TextEncoding MSWord_SdrAttrIter::GetNextCharSet() const +{ + if( !aChrSetArr.empty() ) + return aChrSetArr.back(); + return eNdChrSet; +} + +// the first parameter in SearchNext() returns if it's a TextAtr +sal_Int32 MSWord_SdrAttrIter::SearchNext( sal_Int32 nStartPos ) +{ + sal_Int32 nMinPos = SAL_MAX_INT32; + for(const auto& rTextAtr : aTextAtrArr) + { + sal_Int32 nPos = rTextAtr.nStart; // first character attribute + if( nPos >= nStartPos && nPos <= nMinPos ) + { + nMinPos = nPos; + SetCharSet(rTextAtr, true); + } + + nPos = rTextAtr.nEnd; // last character attribute + 1 + if( nPos >= nStartPos && nPos < nMinPos ) + { + nMinPos = nPos; + SetCharSet(rTextAtr, false); + } + } + return nMinPos; +} + +void MSWord_SdrAttrIter::SetCharSet(const EECharAttrib& rAttr, bool bStart) +{ + const SfxPoolItem& rItem = *rAttr.pAttr; + if( rItem.Which() != EE_CHAR_FONTINFO ) + { + return; + } + + if( bStart ) + { + rtl_TextEncoding eChrSet = static_cast<const SvxFontItem&>(rItem).GetCharSet(); + aChrSetArr.push_back( eChrSet ); + aChrTextAtrArr.push_back( &rAttr ); + } + else + { + std::vector<const EECharAttrib*>::iterator it = + std::find( aChrTextAtrArr.begin(), aChrTextAtrArr.end(), &rAttr ); + if ( it != aChrTextAtrArr.end() ) + { + aChrSetArr.erase( aChrSetArr.begin() + (it - aChrTextAtrArr.begin()) ); + aChrTextAtrArr.erase( it ); + } + } +} + +void MSWord_SdrAttrIter::OutEEField(const SfxPoolItem& rHt) +{ + const SvxFieldItem &rField = static_cast<const SvxFieldItem &>(rHt); + const SvxFieldData *pField = rField.GetField(); + if (auto pURL = dynamic_cast< const SvxURLField *>( pField )) + { + sal_uInt8 nOldTextTyp = m_rExport.m_nTextTyp; + m_rExport.m_nTextTyp = mnTyp; + m_rExport.AttrOutput().StartURL( pURL->GetURL(), pURL->GetTargetFrame() ); + + const OUString &rStr = pURL->GetRepresentation(); + m_rExport.AttrOutput().RawText(rStr, GetNodeCharSet()); + + m_rExport.AttrOutput().EndURL(false); + m_rExport.m_nTextTyp = nOldTextTyp; + } +} + +void MSWord_SdrAttrIter::OutAttr( sal_Int32 nSwPos ) +{ + //Collect the which ids belong to the run that we will export after + //outputting the underlying paragraph attributes. We will exclude + //writing these from the underlying paragraph attributes to avoid + //duplicate attributes in docx export. Doesn't matter in doc + //export as later props just override earlier ones. + std::set<sal_uInt16> aUsedRunWhichs; + for(const auto& rTextAtr : aTextAtrArr) + { + if (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd) + { + sal_uInt16 nWhich = rTextAtr.pAttr->Which(); + aUsedRunWhichs.insert(nWhich); + } + + if( nSwPos < rTextAtr.nStart ) + break; + } + + OutParaAttr(true, &aUsedRunWhichs); + + if (!aTextAtrArr.empty()) + { + const SwModify* pOldMod = m_rExport.m_pOutFormatNode; + m_rExport.m_pOutFormatNode = nullptr; + + const SfxItemPool* pSrcPool = pEditPool; + const SfxItemPool& rDstPool = m_rExport.m_pDoc->GetAttrPool(); + + nTmpSwPos = nSwPos; + // Did we already produce a <w:sz> element? + m_rExport.m_bFontSizeWritten = false; + for(const auto& rTextAtr : aTextAtrArr) + { + if (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd) + { + sal_uInt16 nWhich = rTextAtr.pAttr->Which(); + if (nWhich == EE_FEATURE_FIELD) + { + OutEEField(*(rTextAtr.pAttr)); + continue; + } + if (nWhich == EE_FEATURE_TAB) + { + m_rExport.WriteChar(0x9); + continue; + } + + const sal_uInt16 nSlotId = pSrcPool->GetSlotId(nWhich); + if (nSlotId && nWhich != nSlotId) + { + nWhich = rDstPool.GetWhich(nSlotId); + if (nWhich && nWhich != nSlotId && + nWhich < RES_UNKNOWNATR_BEGIN && + m_rExport.CollapseScriptsforWordOk(nScript,nWhich)) + { + // use always the SW-Which Id ! + std::unique_ptr<SfxPoolItem> pI(rTextAtr.pAttr->Clone()); + pI->SetWhich( nWhich ); + // Will this item produce a <w:sz> element? + bool bFontSizeItem = nWhich == RES_CHRATR_FONTSIZE || nWhich == RES_CHRATR_CJK_FONTSIZE; + if (!m_rExport.m_bFontSizeWritten || !bFontSizeItem) + m_rExport.AttrOutput().OutputItem( *pI ); + if (bFontSizeItem) + m_rExport.m_bFontSizeWritten = true; + } + } + } + + if( nSwPos < rTextAtr.nStart ) + break; + } + m_rExport.m_bFontSizeWritten = false; + + nTmpSwPos = 0; // HasTextItem only allowed in the above area + m_rExport.m_pOutFormatNode = pOldMod; + } +} + +bool MSWord_SdrAttrIter::IsTextAttr(sal_Int32 nSwPos) +{ + return std::any_of(aTextAtrArr.begin(), aTextAtrArr.end(), + [nSwPos](const EECharAttrib& rTextAtr) { + return (nSwPos >= rTextAtr.nStart && nSwPos < rTextAtr.nEnd) && + (rTextAtr.pAttr->Which() == EE_FEATURE_FIELD || + rTextAtr.pAttr->Which() == EE_FEATURE_TAB); + } + ); +} + +// HasItem is used for the consolidation of the double attribute Underline and +// WordLineMode as a TextItem. OutAttr() calls the output function, which can +// query for other items at the start position of attribute via HasItem(). +// Only attributes with an end can be queried. +// The search is done with bDeep. +const SfxPoolItem* MSWord_SdrAttrIter::HasTextItem(sal_uInt16 nWhich) const +{ + nWhich = sw::hack::TransformWhichBetweenPools(*pEditPool, + m_rExport.m_pDoc->GetAttrPool(), nWhich); + if (nWhich) + { + for (const auto& rTextAtr : aTextAtrArr) + { + if (nWhich == rTextAtr.pAttr->Which() && nTmpSwPos >= rTextAtr.nStart && nTmpSwPos < rTextAtr.nEnd) + return rTextAtr.pAttr; // Found + if (nTmpSwPos < rTextAtr.nStart) + return nullptr; + } + } + return nullptr; +} + +const SfxPoolItem& MSWord_SdrAttrIter::GetItem( sal_uInt16 nWhich ) const +{ + using sw::hack::GetSetWhichFromSwDocWhich; + const SfxPoolItem* pRet = HasTextItem(nWhich); + if (!pRet) + { + SfxItemSet aSet(pEditObj->GetParaAttribs(nPara)); + nWhich = GetSetWhichFromSwDocWhich(aSet, *m_rExport.m_pDoc, nWhich); + OSL_ENSURE(nWhich, "Impossible, catastrophic failure imminent"); + pRet = &aSet.Get(nWhich); + } + return *pRet; +} + +//Drawing shapes properties inherit from a different pool that the document +//styles. On export to .doc[x] they will default to style "Normal". Here explicitly +//set any items which are not already set, but differ from "Normal". +void MSWord_SdrAttrIter::SetItemsThatDifferFromStandard(bool bCharAttr, SfxItemSet& rSet) +{ + SwTextFormatColl* pC = m_rExport.m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool + (RES_POOLCOLL_STANDARD, false); + + SfxWhichIter aWhichIter(rSet); + for (sal_uInt16 nEEWhich = aWhichIter.FirstWhich(); nEEWhich; nEEWhich = aWhichIter.NextWhich()) + { + if (SfxItemState::SET != rSet.GetItemState(nEEWhich, false)) + { + sal_uInt16 nSwWhich = sw::hack::TransformWhichBetweenPools(m_rExport.m_pDoc->GetAttrPool(), + *pEditPool, nEEWhich); + if (!nSwWhich) + continue; + bool bWanted = ( bCharAttr ? ( nSwWhich >= RES_CHRATR_BEGIN && nSwWhich < RES_TXTATR_END ) + : ( nSwWhich >= RES_PARATR_BEGIN && nSwWhich < RES_FRMATR_END ) ); + if (!bWanted) + continue; + + const SfxPoolItem& rDrawItem = rSet.Get(nEEWhich); + const SfxPoolItem& rStandardItem = pC->GetFormatAttr(nSwWhich); + if (rDrawItem != rStandardItem) + rSet.Put(rDrawItem); + } + } +} + +void MSWord_SdrAttrIter::OutParaAttr(bool bCharAttr, const std::set<sal_uInt16>* pWhichsToIgnore) +{ + SfxItemSet aSet( pEditObj->GetParaAttribs( nPara )); + + SetItemsThatDifferFromStandard(bCharAttr, aSet); + + if (aSet.Count()) + { + const SfxItemSet* pOldSet = m_rExport.GetCurItemSet(); + m_rExport.SetCurItemSet( &aSet ); + + SfxItemIter aIter( aSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + + const SfxItemPool* pSrcPool = pEditPool, + * pDstPool = &m_rExport.m_pDoc->GetAttrPool(); + + do + { + sal_uInt16 nWhich = pItem->Which(); + if (pWhichsToIgnore && pWhichsToIgnore->find(nWhich) != pWhichsToIgnore->end()) + continue; + + sal_uInt16 nSlotId = pSrcPool->GetSlotId(nWhich); + + if ( nSlotId && nWhich != nSlotId && + 0 != ( nWhich = pDstPool->GetWhich( nSlotId ) ) && + nWhich != nSlotId && + ( bCharAttr ? ( nWhich >= RES_CHRATR_BEGIN && nWhich < RES_TXTATR_END ) + : ( nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END ) ) ) + { + // use always the SW-Which Id ! + std::unique_ptr<SfxPoolItem> pI(pItem->Clone()); + pI->SetWhich( nWhich ); + if (m_rExport.CollapseScriptsforWordOk(nScript,nWhich)) + m_rExport.AttrOutput().OutputItem(*pI); + } + } while ((pItem = aIter.NextItem())); + m_rExport.SetCurItemSet( pOldSet ); + } +} + +void WW8Export::WriteSdrTextObj(const SdrTextObj& rTextObj, sal_uInt8 nTyp) +{ + const OutlinerParaObject* pParaObj = nullptr; + bool bOwnParaObj = false; + + /* + #i13885# + When the object is actively being edited, that text is not set into + the objects normal text object, but lives in a separate object. + */ + if (rTextObj.IsTextEditActive()) + { + pParaObj = rTextObj.CreateEditOutlinerParaObject().release(); + bOwnParaObj = true; + } + else + { + pParaObj = rTextObj.GetOutlinerParaObject(); + } + + if( pParaObj ) + { + WriteOutliner(*pParaObj, nTyp); + if( bOwnParaObj ) + delete pParaObj; + } +} + +void WW8Export::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp) +{ + bool bAnyWrite = false; + const EditTextObject& rEditObj = rParaObj.GetTextObject(); + MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp ); + + sal_Int32 nPara = rEditObj.GetParagraphCount(); + sal_uInt8 bNul = 0; + for( sal_Int32 n = 0; n < nPara; ++n ) + { + if( n ) + aAttrIter.NextPara( n ); + + OSL_ENSURE( pO->empty(), " pO is not empty at start of line" ); + + OUString aStr( rEditObj.GetText( n )); + sal_Int32 nCurrentPos = 0; + const sal_Int32 nEnd = aStr.getLength(); + + const SfxItemSet& aSet(rEditObj.GetParaAttribs(n)); + bool bIsRTLPara = false; + const SfxPoolItem *pItem; + if(SfxItemState::SET == aSet.GetItemState(EE_PARA_WRITINGDIR, true, &pItem)) + { + SvxFrameDirection nDir = static_cast<const SvxFrameDirectionItem*>(pItem)->GetValue(); + bIsRTLPara = SvxFrameDirection::Horizontal_RL_TB == nDir; + } + + do { + const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); + + bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); + if( !bTextAtr ) + OutSwString(aStr, nCurrentPos, nNextAttr - nCurrentPos); + + // At the end of the line the attributes are extended over the CR. + // exception: foot note at line end + if( nNextAttr == nEnd && !bTextAtr ) + WriteCR(); // CR after it + + // output of character attributes + aAttrIter.OutAttr( nCurrentPos ); // nCurrentPos - 1 ?? + + if (bIsRTLPara) + { + // This is necessary to make word order correct in MS Word. + // In theory we should do this for complex-script runs only, + // but Outliner does not split runs like Writer core did. + // Fortunately, both MS Word and Writer seems to tolerate + // that we turn it on for non complex-script runs. + AttrOutput().OutputItem(SfxInt16Item(RES_CHRATR_BIDIRTL, 1)); + } + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), + pO->size(), pO->data() ); + pO->clear(); + + // exception: foot note at line end + if( nNextAttr == nEnd && bTextAtr ) + WriteCR(); // CR after it + nCurrentPos = nNextAttr; + aAttrIter.NextPos(); + } + while( nCurrentPos < nEnd ); + + OSL_ENSURE( pO->empty(), " pO is not empty at start of line" ); + + pO->push_back( bNul ); // Style # as short + pO->push_back( bNul ); + + aAttrIter.OutParaAttr(false); + + sal_uLong nPos = Strm().Tell(); + m_pPapPlc->AppendFkpEntry( Strm().Tell(), + pO->size(), pO->data() ); + pO->clear(); + m_pChpPlc->AppendFkpEntry( nPos ); + } + + bAnyWrite = 0 != nPara; + if( !bAnyWrite ) + WriteStringAsPara( OUString() ); +} + +void WinwordAnchoring::WriteData( EscherEx& rEx ) const +{ + //Toplevel groups get their winword extra data attached, and sub elements + //use the defaults + if (rEx.GetGroupLevel() <= 1) + { + SvStream& rSt = rEx.GetStream(); + //The last argument denotes the number of sub properties in this atom + if (mbInline) + { + rEx.AddAtom(18, DFF_msofbtUDefProp, 3, 3); //Prop id is 0xF122 + rSt.WriteUInt16( 0x0390 ).WriteUInt32( 3 ); + rSt.WriteUInt16( 0x0392 ).WriteUInt32( 3 ); + //This sub property is required to be in the dummy inline frame as + //well + rSt.WriteUInt16( 0x053F ).WriteUInt32( nInlineHack ); + } + else + { + rEx.AddAtom(24, DFF_msofbtUDefProp, 3, 4 ); //Prop id is 0xF122 + rSt.WriteUInt16( 0x038F ).WriteUInt32( mnXAlign ); + rSt.WriteUInt16( 0x0390 ).WriteUInt32( mnXRelTo ); + rSt.WriteUInt16( 0x0391 ).WriteUInt32( mnYAlign ); + rSt.WriteUInt16( 0x0392 ).WriteUInt32( mnYRelTo ); + } + } +} + +void WW8Export::CreateEscher() +{ + SfxItemState eBackSet = m_pDoc->GetPageDesc(0).GetMaster(). + GetItemState(RES_BACKGROUND); + if (m_pHFSdrObjs->size() || m_pSdrObjs->size() || SfxItemState::SET == eBackSet) + { + OSL_ENSURE( !m_pEscher, "Who did not deleted the pointer?" ); + SvMemoryStream* pEscherStrm = new SvMemoryStream; + pEscherStrm->SetEndian(SvStreamEndian::LITTLE); + m_pEscher = new SwEscherEx(pEscherStrm, *this); + } +} + +void WW8Export::WriteEscher() +{ + if (m_pEscher) + { + sal_uLong nStart = pTableStrm->Tell(); + + m_pEscher->WritePictures(); + m_pEscher->FinishEscher(); + + pFib->m_fcDggInfo = nStart; + pFib->m_lcbDggInfo = pTableStrm->Tell() - nStart; + delete m_pEscher; + m_pEscher = nullptr; + } +} + +void SwEscherEx::WritePictures() +{ + if( SvStream* pPicStrm = static_cast< SwEscherExGlobal& >( *mxGlobal ).GetPictureStream() ) + { + // set the blip - entries to the correct stream pos + sal_Int32 nEndPos = rWrt.Strm().Tell(); + mxGlobal->SetNewBlipStreamOffset( nEndPos ); + + pPicStrm->Seek( 0 ); + rWrt.Strm().WriteStream( *pPicStrm ); + } + Flush(); +} + +// Output- Routines for Escher Export + +SwEscherExGlobal::SwEscherExGlobal() +{ +} + +SwEscherExGlobal::~SwEscherExGlobal() +{ +} + +SvStream* SwEscherExGlobal::ImplQueryPictureStream() +{ + // this function will be called exactly once + mxPicStrm = std::make_shared<SvMemoryStream>(); + mxPicStrm->SetEndian(SvStreamEndian::LITTLE); + return mxPicStrm.get(); +} + +SwBasicEscherEx::SwBasicEscherEx(SvStream* pStrm, WW8Export& rWW8Wrt) + : EscherEx( std::make_shared<SwEscherExGlobal>(), pStrm), rWrt(rWW8Wrt), pEscherStrm(pStrm) +{ + Init(); +} + +SwBasicEscherEx::~SwBasicEscherEx() +{ +} + +void SwBasicEscherEx::WriteFrameExtraData(const SwFrameFormat&) +{ + AddAtom(4, ESCHER_ClientAnchor); + GetStream().WriteUInt32( 0x80000000 ); +} + +void SwBasicEscherEx::WriteEmptyFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId) +{ + OpenContainer(ESCHER_SpContainer); + AddShape(ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nShapeId); + // store anchor attribute + WriteFrameExtraData(rFormat); + + AddAtom(6, DFF_msofbtUDefProp, 3, 1); //Prop id is 0xF122 + GetStream().WriteUInt16( 0x053F ).WriteUInt32( nInlineHack ); + + CloseContainer(); // ESCHER_SpContainer +} + +static ShapeFlag AddMirrorFlags(ShapeFlag nFlags, const SwMirrorGrf &rMirror) +{ + switch (rMirror.GetValue()) + { + default: + case MirrorGraph::Dont: + break; + case MirrorGraph::Vertical: + nFlags |= ShapeFlag::FlipH; + break; + case MirrorGraph::Horizontal: + nFlags |= ShapeFlag::FlipV; + break; + case MirrorGraph::Both: + nFlags |= ShapeFlag::FlipH | ShapeFlag::FlipV; + break; + + } + return nFlags; +} +//For i120928,this function is added to export graphic of bullet +void SwBasicEscherEx::WriteGrfBullet(const Graphic& rGrf) +{ + OpenContainer( ESCHER_SpContainer ); + AddShape(ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, 0x401); + EscherPropertyContainer aPropOpt; + GraphicObject aGraphicObject( rGrf ); + OString aUniqueId = aGraphicObject.GetUniqueID(); + if ( !aUniqueId.isEmpty() ) + { + sal_uInt32 nBlibId = mxGlobal->GetBlibID( *(mxGlobal->QueryPictureStream()), aGraphicObject ); + if (nBlibId) + aPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true); + } + aPropOpt.AddOpt( ESCHER_Prop_pibFlags, ESCHER_BlipFlagDefault ); + aPropOpt.AddOpt( ESCHER_Prop_dyTextTop, DrawModelToEmu(0)); + aPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, DrawModelToEmu(0)); + aPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, DrawModelToEmu(0)); + aPropOpt.AddOpt( ESCHER_Prop_dxTextRight, DrawModelToEmu(0)); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_dyTextTop, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_dxTextRight, 0 ); + const Color aTmpColor( COL_WHITE ); + std::shared_ptr<SvxBrushItem> aBrush(std::make_shared<SvxBrushItem>(aTmpColor, RES_BACKGROUND)); + const SvxBrushItem* pRet = rWrt.GetCurrentPageBgBrush(); + if (pRet && (pRet->GetGraphic() ||( pRet->GetColor() != COL_TRANSPARENT))) + aBrush.reset(pRet->Clone()); + WriteBrushAttr(*aBrush, aPropOpt); + + aPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0 ); + aPropOpt.Commit( GetStream() ); + AddAtom(4, ESCHER_ClientAnchor); + GetStream().WriteUInt32( 0x80000000 ); + CloseContainer(); +} + +sal_Int32 SwBasicEscherEx::WriteGrfFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId) +{ + sal_Int32 nBorderThick=0; + SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(rFormat); + SwGrfNode *pGrfNd = pNd ? pNd->GetGrfNode() : nullptr; + OSL_ENSURE(pGrfNd, "No SwGrfNode ?, suspicious"); + if (!pGrfNd) + return nBorderThick; + + OpenContainer( ESCHER_SpContainer ); + + const SwMirrorGrf &rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf(); + AddShape(ESCHER_ShpInst_PictureFrame, + AddMirrorFlags(ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, rMirror), + nShapeId); + + EscherPropertyContainer aPropOpt; + + sal_uInt32 nFlags = ESCHER_BlipFlagDefault; + + if (pGrfNd->IsLinkedFile()) + { + OUString sURL; + pGrfNd->GetFileFilterNms( &sURL, nullptr ); + + ww::bytes aBuf; + SwWW8Writer::InsAsString16( aBuf, sURL ); + SwWW8Writer::InsUInt16( aBuf, 0 ); + + aPropOpt.AddOpt(ESCHER_Prop_pibName, true, aBuf.size(), aBuf); + nFlags = ESCHER_BlipFlagLinkToFile | ESCHER_BlipFlagURL | + ESCHER_BlipFlagDoNotSave; + } + else + { + const Graphic& aGraphic(pGrfNd->GetGrf()); + GraphicObject aGraphicObject( aGraphic ); + OString aUniqueId = aGraphicObject.GetUniqueID(); + + if (!aUniqueId.isEmpty()) + { + sal_uInt32 nBlibId = mxGlobal->GetBlibID( *QueryPictureStream(), aGraphicObject); + if (nBlibId) + aPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true); + } + } + + aPropOpt.AddOpt( ESCHER_Prop_pibFlags, nFlags ); + nBorderThick = WriteFlyFrameAttr(rFormat,mso_sptPictureFrame,aPropOpt); + WriteGrfAttr(*pGrfNd, rFormat, aPropOpt); + + aPropOpt.Commit( GetStream() ); + + // store anchor attribute + WriteFrameExtraData( rFormat ); + + CloseContainer(); // ESCHER_SpContainer + return nBorderThick; +} + +void SwBasicEscherEx::WriteGrfAttr(const SwNoTextNode& rNd, const SwFrameFormat& rFormat, + EscherPropertyContainer& rPropOpt) +{ + const SfxPoolItem* pItem; + GraphicDrawMode nMode = GraphicDrawMode::Standard; + sal_Int32 nContrast = 0; + sal_Int16 nBrightness = 0; + + if (SfxItemState::SET == rNd.GetSwAttrSet().GetItemState(RES_GRFATR_CONTRAST, + true, &pItem)) + { + nContrast = static_cast<const SfxInt16Item*>(pItem)->GetValue(); + } + + if (SfxItemState::SET == rNd.GetSwAttrSet().GetItemState(RES_GRFATR_LUMINANCE, + true, &pItem)) + { + nBrightness = static_cast<const SfxInt16Item*>(pItem)->GetValue(); + } + + if (SfxItemState::SET == rNd.GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, + true, &pItem)) + { + nMode = static_cast<GraphicDrawMode>(static_cast<const SfxEnumItemInterface*>(pItem)->GetEnumValue()); + if (nMode == GraphicDrawMode::Watermark) + { + /* + There is no real watermark mode in word, we must use standard + mode and modify our ones by 70% extra brightness and 70% less + contrast. This means that unmodified default OOo watermark + will turn back into watermark, and modified OOo watermark will + change into a close visual representation in standardmode + */ + nBrightness += 70; + if (nBrightness > 100) + nBrightness = 100; + nContrast -= 70; + if (nContrast < -100) + nContrast = -100; + nMode = GraphicDrawMode::Standard; + } + } + + sal_uInt32 nPictureMode; + if (nMode == GraphicDrawMode::Greys) + nPictureMode = 0x40004; + else if (nMode == GraphicDrawMode::Mono) + nPictureMode = 0x60006; + else + nPictureMode = 0; + rPropOpt.AddOpt( ESCHER_Prop_pictureActive, nPictureMode ); + + if (nContrast != 0) + { + nContrast+=100; + if (nContrast == 100) + nContrast = 0x10000; + else if (nContrast < 100) + { + nContrast *= 0x10000; + nContrast /= 100; + } + else if (nContrast < 200) + nContrast = (100 * 0x10000) / (200-nContrast); + else + nContrast = 0x7fffffff; + rPropOpt.AddOpt( ESCHER_Prop_pictureContrast, nContrast); + } + + if (nBrightness != 0) + rPropOpt.AddOpt( ESCHER_Prop_pictureBrightness, nBrightness * 327 ); + + sal_Int32 nCropL = 0; + sal_Int32 nCropR = 0; + sal_Int32 nCropT = 0; + sal_Int32 nCropB = 0; + if (SfxItemState::SET == rNd.GetSwAttrSet().GetItemState(RES_GRFATR_CROPGRF, + true, &pItem)) + { + const SwCropGrf& rCrop = *static_cast<const SwCropGrf*>(pItem); + nCropL += rCrop.GetLeft(); + nCropR += rCrop.GetRight(); + nCropT += rCrop.GetTop(); + nCropB += rCrop.GetBottom(); + } + + // simulate border padding as a negative crop. + if (SfxItemState::SET == rFormat.GetItemState(RES_BOX, false, &pItem)) + { + const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem); + nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT ); + nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT ); + nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP ); + nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM ); + } + + const Size aSz( rNd.GetTwipSize() ); + if( 0 != nCropL ) + rPropOpt.AddOpt( ESCHER_Prop_cropFromLeft, ToFract16( nCropL, aSz.Width()) ); + if( 0 != nCropR ) + rPropOpt.AddOpt( ESCHER_Prop_cropFromRight, ToFract16( nCropR, aSz.Width())); + if( 0 != nCropT ) + rPropOpt.AddOpt( ESCHER_Prop_cropFromTop, ToFract16( nCropT, aSz.Height())); + if( 0 != nCropB ) + rPropOpt.AddOpt( ESCHER_Prop_cropFromBottom, ToFract16( nCropB, aSz.Height())); +} + +void SwBasicEscherEx::SetPicId(const SdrObject &, sal_uInt32, + EscherPropertyContainer &) +{ +} + +void SwEscherEx::SetPicId(const SdrObject &rSdrObj, sal_uInt32 nShapeId, + EscherPropertyContainer &rPropOpt) +{ + pTextBxs->Append(rSdrObj, nShapeId); + sal_uInt32 nPicId = pTextBxs->Count(); + nPicId *= 0x10000; + rPropOpt.AddOpt( ESCHER_Prop_pictureId, nPicId ); +} + +sal_Int32 SwBasicEscherEx::WriteOLEFlyFrame(const SwFrameFormat& rFormat, sal_uInt32 nShapeId) +{ + sal_Int32 nBorderThick = 0; + if (const SdrObject* pSdrObj = rFormat.FindRealSdrObject()) + { + SwNodeIndex aIdx(*rFormat.GetContent().GetContentIdx(), 1); + SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode(); + sal_Int64 nAspect = rOLENd.GetAspect(); + + uno::Reference < embed::XEmbeddedObject > xObj(rOLENd.GetOLEObj().GetOleRef()); + + // the rectangle is used to transport the size of the object + // the left, top corner is set to ( 0, 0 ) by default constructor, + // if the width and height are set correctly bRectIsSet should be set to true + awt::Rectangle aRect; + bool bRectIsSet = false; + + // TODO/LATER: should the icon size be stored in case of iconified object? + if ( xObj.is() && nAspect != embed::Aspects::MSOLE_ICON ) + { + try + { + awt::Size aSize = xObj->getVisualAreaSize( nAspect ); + aRect.Width = aSize.Width; + aRect.Height = aSize.Height; + bRectIsSet = true; + } + catch( const uno::Exception& ) + {} + } + + /* + #i5970# + Export floating ole2 .doc ver 8+ wmf ole2 previews as emf previews + instead ==> allows unicode text to be preserved + */ +#ifdef OLE_PREVIEW_AS_EMF + const Graphic* pGraphic = rOLENd.GetGraphic(); +#endif + OpenContainer(ESCHER_SpContainer); + + EscherPropertyContainer aPropOpt; + const SwMirrorGrf &rMirror = rOLENd.GetSwAttrSet().GetMirrorGrf(); + WriteOLEPicture(aPropOpt, + AddMirrorFlags(ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::OLEShape, rMirror), + pGraphic ? *pGraphic : Graphic(), *pSdrObj, nShapeId, bRectIsSet ? &aRect : nullptr ); + + nBorderThick = WriteFlyFrameAttr(rFormat, mso_sptPictureFrame, aPropOpt); + WriteGrfAttr(rOLENd, rFormat, aPropOpt); + aPropOpt.Commit(GetStream()); + + // store anchor attribute + WriteFrameExtraData( rFormat ); + + CloseContainer(); // ESCHER_SpContainer + } + return nBorderThick; +} + +void SwBasicEscherEx::WriteBrushAttr(const SvxBrushItem &rBrush, + EscherPropertyContainer& rPropOpt) +{ + bool bSetOpacity = false; + sal_uInt32 nOpaque = 0; + if (const GraphicObject *pGraphicObject = rBrush.GetGraphicObject()) + { + OString aUniqueId = pGraphicObject->GetUniqueID(); + if (!aUniqueId.isEmpty()) + { + sal_uInt32 nBlibId = mxGlobal->GetBlibID(*QueryPictureStream(), *pGraphicObject); + if (nBlibId) + rPropOpt.AddOpt(ESCHER_Prop_fillBlip,nBlibId,true); + } + + if (0 != (nOpaque = pGraphicObject->GetAttr().GetTransparency())) + bSetOpacity = true; + + rPropOpt.AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture ); + rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 ); + } + else + { + sal_uInt32 nFillColor = GetColor(rBrush.GetColor()); + rPropOpt.AddOpt( ESCHER_Prop_fillColor, nFillColor ); + rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, nFillColor ^ 0xffffff ); + rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100010 ); + + if (0 != (nOpaque = rBrush.GetColor().GetTransparency())) + bSetOpacity = true; + } + + if (bSetOpacity) + { + nOpaque = (nOpaque * 100) / 0xFE; + nOpaque = ((100 - nOpaque) << 16) / 100; + rPropOpt.AddOpt(ESCHER_Prop_fillOpacity, nOpaque); + } +} + +sal_Int32 SwBasicEscherEx::WriteFlyFrameAttr(const SwFrameFormat& rFormat, + MSO_SPT eShapeType, EscherPropertyContainer& rPropOpt) +{ + sal_Int32 nLineWidth=0; + const SfxPoolItem* pItem; + bool bFirstLine = true; + if (SfxItemState::SET == rFormat.GetItemState(RES_BOX, true, &pItem)) + { + static const o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aExhperProp = + { + ESCHER_Prop_dyTextTop, ESCHER_Prop_dyTextBottom, + ESCHER_Prop_dxTextLeft, ESCHER_Prop_dxTextRight + }; + const SvxBorderLine* pLine; + + for( SvxBoxItemLine n : o3tl::enumrange<SvxBoxItemLine>() ) + if( nullptr != ( pLine = static_cast<const SvxBoxItem*>(pItem)->GetLine( n )) ) + { + if( bFirstLine ) + { + sal_uInt32 nLineColor = GetColor(pLine->GetColor()); + rPropOpt.AddOpt( ESCHER_Prop_lineColor, nLineColor ); + rPropOpt.AddOpt( ESCHER_Prop_lineBackColor, + nLineColor ^ 0xffffff ); + + MSO_LineStyle eStyle; + if( pLine->isDouble() ) + { + // double line + nLineWidth = pLine->GetWidth(); + if( pLine->GetInWidth() == pLine->GetOutWidth() ) + eStyle = mso_lineDouble; + else if( pLine->GetInWidth() < pLine->GetOutWidth() ) + eStyle = mso_lineThickThin; + else + eStyle = mso_lineThinThick; + } + else + { + // simple line + eStyle = mso_lineSimple; + nLineWidth = pLine->GetWidth(); + } + + rPropOpt.AddOpt( ESCHER_Prop_lineStyle, eStyle ); + rPropOpt.AddOpt( ESCHER_Prop_lineWidth, + DrawModelToEmu( nLineWidth )); + + MSO_LineDashing eDashing = mso_lineSolid; + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::DASHED: + eDashing = mso_lineDashGEL; + break; + case SvxBorderLineStyle::DOTTED: + eDashing = mso_lineDotGEL; + break; + case SvxBorderLineStyle::SOLID: + default: + break; + } + rPropOpt.AddOpt( ESCHER_Prop_lineDashing, eDashing ); + rPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x8000E ); + + //Use import logic to determine how much of border will go + //outside graphic + nLineWidth = SwMSDffManager::GetEscherLineMatch( + eStyle,eShapeType,nLineWidth); + bFirstLine = false; + } + rPropOpt.AddOpt( aExhperProp[ n ], DrawModelToEmu( + static_cast<const SvxBoxItem*>(pItem)->GetDistance( n ) )); + } + else + rPropOpt.AddOpt( aExhperProp[ n ], DrawModelToEmu(static_cast<const SvxBoxItem*>(pItem)->GetDistance( n )) ); + } + else + { + rPropOpt.AddOpt( ESCHER_Prop_dyTextTop, 0 ); + rPropOpt.AddOpt( ESCHER_Prop_dyTextBottom, 0 ); + rPropOpt.AddOpt( ESCHER_Prop_dxTextLeft, 0 ); + rPropOpt.AddOpt( ESCHER_Prop_dxTextRight, 0 ); + } + + if( bFirstLine ) // no valid line found + { + rPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + } + const SwAttrSet& rAttrSet = rFormat.GetAttrSet(); + if (SfxItemState::SET == rAttrSet.GetItemState(RES_BOX, false, &pItem)) + { + const SvxBoxItem* pBox = static_cast<const SvxBoxItem*>(pItem); + if( pBox ) + { + const SfxPoolItem* pShadItem; + if (SfxItemState::SET + == rAttrSet.GetItemState(RES_SHADOW, true, &pShadItem)) + { + const SvxShadowItem* pSI = static_cast<const SvxShadowItem*>(pShadItem); + + const sal_uInt16 nCstScale = 635; // unit scale between AOO and MS Word + const sal_uInt32 nShadowType = 131074; // shadow type of ms word. need to set the default value. + + Color nColor = pSI->GetColor(); + sal_Int32 nOffX = pSI->GetWidth() * nCstScale; + sal_Int32 nOffY = pSI->GetWidth() * nCstScale; + + SvxShadowLocation eLocation = pSI->GetLocation(); + if( (eLocation!=SvxShadowLocation::NONE) && (pSI->GetWidth()!=0) ) + { + switch( eLocation ) + { + case SvxShadowLocation::TopLeft: + { + nOffX = -nOffX; + nOffY = -nOffY; + } + break; + case SvxShadowLocation::TopRight: + { + nOffY = -nOffY; + } + break; + case SvxShadowLocation::BottomLeft: + { + nOffX = -nOffX; + } + break; + case SvxShadowLocation::BottomRight: + break; + default: + break; + } + + rPropOpt.AddOpt( DFF_Prop_shadowColor, wwUtility::RGBToBGR(nColor)); + rPropOpt.AddOpt( DFF_Prop_shadowOffsetX, nOffX ); + rPropOpt.AddOpt( DFF_Prop_shadowOffsetY, nOffY ); + rPropOpt.AddOpt( DFF_Prop_fshadowObscured, nShadowType ); + } + } + } + } + + // SwWW8ImplReader::Read_GrafLayer() imports these as opaque + // unconditionally, so if both are true, don't export the property. + const bool bIsInHeader = sw::IsFlyFrameFormatInHeader(rFormat); + const bool bIsThrough = rFormat.GetSurround().GetValue() == css::text::WrapTextMode_THROUGH; + + // Anything (like a transparent image) that allows text to wrap through should not force a non-transparent background, + // and neither should the commonly seen backgrounds anchored in headers. + if (bIsInHeader || bIsThrough) + { + std::unique_ptr<SvxBrushItem> aBrush(rFormat.makeBackgroundBrushItem()); + + if(aBrush) + { + WriteBrushAttr(*aBrush, rPropOpt); + } + } + else + { + // for unknown reasons, force exporting a non-transparent background on fly frames. + std::shared_ptr<SvxBrushItem> aBrush(rWrt.TrueFrameBgBrush(rFormat)); + + if(aBrush) + { + WriteBrushAttr(*aBrush, rPropOpt); + } + } + + const SdrObject* pObj = rFormat.FindRealSdrObject(); + + if( pObj && (pObj->GetLayer() == GetHellLayerId() || + pObj->GetLayer() == GetInvisibleHellId() ) && !(bIsInHeader && bIsThrough)) + { + rPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x200020 ); + } + + PreWriteHyperlinkWithinFly(rFormat,rPropOpt); + + return nLineWidth; +} + +sal_Int32 SwEscherEx::WriteFlyFrameAttr(const SwFrameFormat& rFormat, MSO_SPT eShapeType, + EscherPropertyContainer& rPropOpt) +{ + sal_Int32 nLineWidth = SwBasicEscherEx::WriteFlyFrameAttr(rFormat, eShapeType, + rPropOpt); + + /* + These are not in SwBasicEscherEx::WriteFlyFrameAttr because inline objs + can't do it in word and it hacks it in by stretching the graphic that + way, perhaps we should actually draw in this space into the graphic we + are exporting! + */ + const SfxPoolItem* pItem; + if (SfxItemState::SET == rFormat.GetItemState(RES_LR_SPACE, true, &pItem)) + { + rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, + DrawModelToEmu( static_cast<const SvxLRSpaceItem*>(pItem)->GetLeft() ) ); + rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, + DrawModelToEmu( static_cast<const SvxLRSpaceItem*>(pItem)->GetRight() ) ); + } + else + { + rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, 0 ); + rPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, 0 ); + } + + if (SfxItemState::SET == rFormat.GetItemState(RES_UL_SPACE, true, &pItem)) + { + rPropOpt.AddOpt( ESCHER_Prop_dyWrapDistTop, + DrawModelToEmu( static_cast<const SvxULSpaceItem*>(pItem)->GetUpper() ) ); + rPropOpt.AddOpt( ESCHER_Prop_dyWrapDistBottom, + DrawModelToEmu( static_cast<const SvxULSpaceItem*>(pItem)->GetLower() ) ); + } + + if (rFormat.GetSurround().IsContour()) + { + if (const SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(rFormat)) + { + const tools::PolyPolygon *pPolyPoly = pNd->HasContour(); + if (pPolyPoly && pPolyPoly->Count()) + { + tools::Polygon aPoly = CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/false); + SvMemoryStream aPolyDump; + aPolyDump.SetEndian(SvStreamEndian::LITTLE); + + sal_uInt16 nLen = aPoly.GetSize(); + aPolyDump.WriteUInt16( nLen ); + aPolyDump.WriteUInt16( nLen ); + aPolyDump.WriteUInt16( 8 ); + for (sal_uInt16 nI = 0; nI < nLen; ++nI) + { + aPolyDump.WriteUInt32( aPoly[nI].X() ); + aPolyDump.WriteUInt32( aPoly[nI].Y() ); + } + + rPropOpt.AddOpt(DFF_Prop_pWrapPolygonVertices, false, 0, aPolyDump); + } + } + } + + PreWriteHyperlinkWithinFly(rFormat,rPropOpt); + + return nLineWidth; +} + +void SwBasicEscherEx::Init() +{ + MapUnit eMap = MapUnit::MapTwip; + if (SwDrawModel *pModel = rWrt.m_pDoc->getIDocumentDrawModelAccess().GetDrawModel()) + { + // PPT works only with units of 576DPI + // WW however is using twips, i.e 1440DPI. + eMap = pModel->GetScaleUnit(); + } + + // MS-DFF-Properties mostly are in EMU (English Metric Units) + // 1mm=36000emu, 1twip=635emu + Fraction aFact(360, 1); + aFact /= GetMapFactor(MapUnit::Map100thMM, eMap).X(); + // create little values + aFact = Fraction(aFact.GetNumerator(), aFact.GetDenominator()); + mnEmuMul = aFact.GetNumerator(); + mnEmuDiv = aFact.GetDenominator(); + + SetHellLayerId(rWrt.m_pDoc->getIDocumentDrawModelAccess().GetHellId()); +} + +sal_Int32 SwBasicEscherEx::ToFract16(sal_Int32 nVal, sal_uInt32 nMax) +{ + if (nMax) + { + if (nVal >= 0) + { + sal_Int32 nMSVal = (nVal / 65536) * nMax; + nMSVal += (nVal * 65536) / nMax; + return nMSVal; + } else { + // negative fraction does not have "-0", fractional part is always + // positive: -0.4 represented as -1 + 0.6 + sal_Int32 const nDiv = (nVal / sal_Int32(nMax)) - 1; + sal_uInt32 nMSVal = (sal_uInt32(nDiv) << 16) & 0xffff0000; + nMSVal += (nVal * 65536) / sal_Int32(nMax) + (-nDiv * 65536); + return nMSVal; + } + } + return 0; +} + +SdrLayerID SwBasicEscherEx::GetInvisibleHellId() const +{ + return rWrt.m_pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId(); +} + +void SwBasicEscherEx::WritePictures() +{ + if( SvStream* pPicStrm = static_cast< SwEscherExGlobal& >( *mxGlobal ).GetPictureStream() ) + { + // set the blip - entries to the correct stream pos + sal_Int32 nEndPos = pPicStrm->Tell(); + mxGlobal->WriteBlibStoreEntry(*pEscherStrm, 1, nEndPos); + + pPicStrm->Seek(0); + pEscherStrm->WriteStream( *pPicStrm ); + } +} + +SwEscherEx::SwEscherEx(SvStream* pStrm, WW8Export& rWW8Wrt) + : SwBasicEscherEx(pStrm, rWW8Wrt), + pTextBxs(nullptr) +{ + aHostData.SetClientData(&aWinwordAnchoring); + OpenContainer( ESCHER_DggContainer ); + + sal_uInt16 nColorCount = 4; + pStrm ->WriteUInt16( nColorCount << 4 ) // instance + .WriteUInt16( ESCHER_SplitMenuColors ) // record type + .WriteUInt32( nColorCount * 4 ) // size + .WriteUInt32( 0x08000004 ) + .WriteUInt32( 0x08000001 ) + .WriteUInt32( 0x08000002 ) + .WriteUInt32( 0x100000f7 ); + + CloseContainer(); // ESCHER_DggContainer + + sal_uInt8 i = 2; // for header/footer and the other + PlcDrawObj *pSdrObjs = rWrt.m_pHFSdrObjs; + pTextBxs = rWrt.m_pHFTextBxs; + + // if no header/footer -> skip over + if (!pSdrObjs->size()) + { + --i; + pSdrObjs = rWrt.m_pSdrObjs; + pTextBxs = rWrt.m_pTextBxs; + } + + for( ; i--; pSdrObjs = rWrt.m_pSdrObjs, pTextBxs = rWrt.m_pTextBxs ) + { + // "dummy char" (or any Count ?) - why? Only Microsoft knows it. + GetStream().WriteChar( i ); + + OpenContainer( ESCHER_DgContainer ); + + EnterGroup(); + + sal_uLong nSecondShapeId = pSdrObjs == rWrt.m_pSdrObjs ? GenerateShapeId() : 0; + + // write now all Writer-/DrawObjects + DrawObjPointerVector aSorted; + MakeZOrderArrAndFollowIds(pSdrObjs->GetObjArr(), aSorted); + + sal_uInt32 nShapeId=0; + for (auto& pObj : aSorted) + { + sal_Int32 nBorderThick=0; + OSL_ENSURE(pObj, "impossible"); + if (!pObj) + continue; + const ww8::Frame &rFrame = pObj->maContent; + const SwFrameFormat& rFormat = rFrame.GetFrameFormat(); + + switch (rFrame.GetWriterType()) + { + case ww8::Frame::eTextBox: + case ww8::Frame::eOle: + case ww8::Frame::eGraphic: + nBorderThick = WriteFlyFrame(*pObj, nShapeId, aSorted); + break; + case ww8::Frame::eFormControl: + WriteOCXControl(rFormat, nShapeId = GenerateShapeId()); + break; + case ww8::Frame::eDrawing: + { + aWinwordAnchoring.SetAnchoring(rFormat); + const SdrObject* pSdrObj = rFormat.FindRealSdrObject(); + if (pSdrObj) + { + nShapeId = AddSdrObject(*pSdrObj); + } +#if OSL_DEBUG_LEVEL > 0 + else + OSL_ENSURE( false, "Where is the SDR-Object?" ); +#endif + } + break; + default: + break; + } + + if( !nShapeId ) + { + nShapeId = AddDummyShape(); + } + + pObj->SetShapeDetails(nShapeId, nBorderThick); + } + + EndSdrObjectPage(); // ???? Bugfix for 74724 + + if( nSecondShapeId ) + { + OpenContainer( ESCHER_SpContainer ); + + AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Background, + nSecondShapeId ); + + EscherPropertyContainer aPropOpt; + const SwFrameFormat &rFormat = rWrt.m_pDoc->GetPageDesc(0).GetMaster(); + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = rFormat.GetItemState(RES_BACKGROUND, true, + &pItem); + if (SfxItemState::SET == eState && pItem) + { + const SvxBrushItem* pBrush = static_cast<const SvxBrushItem*>(pItem); + WriteBrushAttr(*pBrush, aPropOpt); + + SvxGraphicPosition ePos = pBrush->GetGraphicPos(); + if( ePos != GPOS_NONE && ePos != GPOS_AREA ) + { + /* #i56806# 0x033F parameter specifies a 32-bit field of shape boolean properties. + 0x10001 means fBackground and fUsefBackground flag are true thus background + picture will be shown as "tiled" fill.*/ + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); + } + } + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropOpt.AddOpt( ESCHER_Prop_lineWidth, 0 ); + + aPropOpt.Commit( *pStrm ); + + AddAtom( 4, ESCHER_ClientData ); + GetStream().WriteInt32( 1 ); + + CloseContainer(); // ESCHER_SpContainer + } + CloseContainer(); // ESCHER_DgContainer + } +} + +SwEscherEx::~SwEscherEx() +{ +} + +void SwEscherEx::FinishEscher() +{ + pEscherStrm->Seek(0); + rWrt.pTableStrm->WriteStream( *pEscherStrm ); + delete pEscherStrm; + pEscherStrm = nullptr; +} + + +namespace +{ + template<typename OrientType> + void lcl_SetRelationOrient(OrientType& rOrient, const sw::WW8AnchorConv eConv, const std::function<void()>& fDefault) + { + switch(eConv) + { + case sw::WW8AnchorConv::RELTOTABLECELL: + // #i33818# + rOrient.SetRelationOrient(text::RelOrientation::PAGE_PRINT_AREA); + break; + case sw::WW8AnchorConv::CONV2PG: + rOrient.SetRelationOrient(text::RelOrientation::PAGE_FRAME); + break; + case sw::WW8AnchorConv::CONV2COL_OR_PARA: + rOrient.SetRelationOrient(text::RelOrientation::FRAME); + break; + case sw::WW8AnchorConv::CONV2CHAR: + rOrient.SetRelationOrient(text::RelOrientation::CHAR); + break; + case sw::WW8AnchorConv::CONV2LINE: + rOrient.SetRelationOrient(text::RelOrientation::TEXT_LINE); + break; + default: + fDefault(); + } + } +} +/** method to perform conversion of positioning attributes with the help + of corresponding layout information + + #i30669# + Because most of the Writer object positions doesn't correspond to the + object positions in WW8, this method converts the positioning + attributes. For this conversion the corresponding layout information + is needed. If no layout information exists - e.g. no layout exists - no + conversion is performed. + No conversion is performed for as-character anchored objects. Whose + object positions are already treated special in method <WriteData(..)>. + + @param _iorHoriOri + input/output parameter - containing the current horizontal position + attributes, which are converted by this method. + + @param _iorVertOri + input/output parameter - containing the current vertical position + attributes, which are converted by this method. + + @param _rFrameFormat + input parameter - frame format of the anchored object + + @return boolean, indicating, if a conversion has been performed. +*/ +bool WinwordAnchoring::ConvertPosition( SwFormatHoriOrient& _iorHoriOri, + SwFormatVertOrient& _iorVertOri, + const SwFrameFormat& _rFrameFormat ) +{ + const RndStdIds eAnchor = _rFrameFormat.GetAnchor().GetAnchorId(); + + if ( (RndStdIds::FLY_AS_CHAR == eAnchor) || (RndStdIds::FLY_AT_FLY == eAnchor) ) + { + // no conversion for as-character or at frame anchored objects + return false; + } + + // determine value of attribute 'Follow text flow', because positions aligned + // at page areas have to be converted, if it's set. + const bool bFollowTextFlow = _rFrameFormat.GetFollowTextFlow().GetValue(); + + // check, if horizontal and vertical position have to be converted due to + // the fact, that the object is anchored at a paragraph, which has a "column + // break before" attribute + bool bConvDueToAnchoredAtColBreakPara( false ); + if ( ( (eAnchor == RndStdIds::FLY_AT_PARA) || (eAnchor == RndStdIds::FLY_AT_CHAR) ) && + _rFrameFormat.GetAnchor().GetContentAnchor() && + _rFrameFormat.GetAnchor().GetContentAnchor()->nNode.GetNode().IsTextNode() ) + { + SwTextNode& rAnchorTextNode = + dynamic_cast<SwTextNode&>(_rFrameFormat.GetAnchor().GetContentAnchor()->nNode.GetNode()); + const SvxFormatBreakItem& rBreak = ItemGet<SvxFormatBreakItem>(rAnchorTextNode, RES_BREAK); + if (rBreak.GetBreak() == SvxBreak::ColumnBefore) + { + bConvDueToAnchoredAtColBreakPara = true; + } + } + + sw::WW8AnchorConv eHoriConv(sw::WW8AnchorConv::NO_CONV); + sw::WW8AnchorConv eVertConv(sw::WW8AnchorConv::NO_CONV); + // convert horizontal position, if needed + { + + // determine, if conversion has to be performed due to the position orientation + bool bConvDueToOrientation( false ); + { + const sal_Int16 eHOri = _iorHoriOri.GetHoriOrient(); + bConvDueToOrientation = eHOri == text::HoriOrientation::LEFT || eHOri == text::HoriOrientation::RIGHT || + eHOri == text::HoriOrientation::INSIDE || eHOri == text::HoriOrientation::OUTSIDE || + ( eHOri != text::HoriOrientation::CENTER && _iorHoriOri.IsPosToggle() ); + } + + // determine conversion type due to the position relation + if ( bConvDueToAnchoredAtColBreakPara ) + { + eHoriConv = sw::WW8AnchorConv::CONV2PG; + } + else if ( _iorHoriOri.IsPosToggle() + && _iorHoriOri.GetHoriOrient() == text::HoriOrientation::RIGHT ) + { + eHoriConv = sw::WW8AnchorConv::NO_CONV; + _iorHoriOri.SetHoriOrient( text::HoriOrientation::OUTSIDE ); + } + else + { + switch ( _iorHoriOri.GetRelationOrient() ) + { + case text::RelOrientation::PAGE_FRAME: + case text::RelOrientation::PAGE_PRINT_AREA: + { + if ( bConvDueToOrientation || bFollowTextFlow ) + eHoriConv = sw::WW8AnchorConv::CONV2PG; + } + break; + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::PAGE_RIGHT: + { + // relation not supported by WW8. Thus, conversion always needed. + eHoriConv = sw::WW8AnchorConv::CONV2PG; + } + break; + case text::RelOrientation::FRAME: + { + if ( bConvDueToOrientation ) + eHoriConv = sw::WW8AnchorConv::CONV2COL_OR_PARA; + } + break; + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::FRAME_LEFT: + case text::RelOrientation::FRAME_RIGHT: + { + // relation not supported by WW8. Thus, conversion always needed. + eHoriConv = sw::WW8AnchorConv::CONV2COL_OR_PARA; + } + break; + case text::RelOrientation::CHAR: + { + if ( bConvDueToOrientation ) + eHoriConv = sw::WW8AnchorConv::CONV2CHAR; + } + break; + default: + OSL_FAIL( "<WinwordAnchoring::ConvertPosition(..)> - unknown horizontal relation" ); + } + } + } + + // convert vertical position, if needed + { + + // determine, if conversion has to be performed due to the position orientation + bool bConvDueToOrientation( false ); + { + const sal_Int16 eVOri = _iorVertOri.GetVertOrient(); + bConvDueToOrientation = ( eVOri == text::VertOrientation::TOP || + eVOri == text::VertOrientation::BOTTOM || + eVOri == text::VertOrientation::CHAR_TOP || + eVOri == text::VertOrientation::CHAR_BOTTOM || + eVOri == text::VertOrientation::CHAR_CENTER || + eVOri == text::VertOrientation::LINE_TOP || + eVOri == text::VertOrientation::LINE_BOTTOM || + eVOri == text::VertOrientation::LINE_CENTER ); + } + + // determine conversion type due to the position relation + if ( bConvDueToAnchoredAtColBreakPara ) + { + eVertConv = sw::WW8AnchorConv::CONV2PG; + } + else + { + switch ( _iorVertOri.GetRelationOrient() ) + { + case text::RelOrientation::PAGE_FRAME: + case text::RelOrientation::PAGE_PRINT_AREA: + { + if ( bConvDueToOrientation || bFollowTextFlow ) + eVertConv = sw::WW8AnchorConv::CONV2PG; + } + break; + case text::RelOrientation::FRAME: + { + if ( bConvDueToOrientation || + _iorVertOri.GetVertOrient() == text::VertOrientation::CENTER ) + { + eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA; + } + } + break; + case text::RelOrientation::PRINT_AREA: + { + // relation not supported by WW8. Thus, conversion always needed. + eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA; + } + break; + case text::RelOrientation::CHAR: + { + // relation not supported by WW8. Thus, conversion always needed. + eVertConv = sw::WW8AnchorConv::CONV2COL_OR_PARA; + } + break; + case text::RelOrientation::TEXT_LINE: + { + if ( bConvDueToOrientation || + _iorVertOri.GetVertOrient() == text::VertOrientation::NONE ) + { + eVertConv = sw::WW8AnchorConv::CONV2LINE; + } + } + break; + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::FRAME_LEFT: + case text::RelOrientation::FRAME_RIGHT: + default: + OSL_FAIL( "<WinwordAnchoring::ConvertPosition(..)> - unknown vertical relation" ); + } + } + + } + if (eVertConv != sw::WW8AnchorConv::NO_CONV || eHoriConv != sw::WW8AnchorConv::NO_CONV) + { + sw::WW8AnchorConvResult aResult(eHoriConv, eVertConv); + _rFrameFormat.CallSwClientNotify(sw::WW8AnchorConvHint(aResult)); + if(!aResult.m_bConverted) + return false; + if (eHoriConv != sw::WW8AnchorConv::NO_CONV) + { + lcl_SetRelationOrient(_iorHoriOri, eHoriConv, [&_iorHoriOri]() {_iorHoriOri.SetHoriOrient(text::HoriOrientation::NONE);} ); + _iorHoriOri.SetPos(aResult.m_aPos.X()); + } + if (eVertConv != sw::WW8AnchorConv::NO_CONV) + { + lcl_SetRelationOrient(_iorVertOri, eVertConv, [&_iorVertOri]() {_iorVertOri.SetVertOrient(text::VertOrientation::NONE);} ); + _iorVertOri.SetPos(aResult.m_aPos.Y()); + } + return true; + } + return false; +} + +void WinwordAnchoring::SetAnchoring(const SwFrameFormat& rFormat) +{ + const RndStdIds eAnchor = rFormat.GetAnchor().GetAnchorId(); + mbInline = (eAnchor == RndStdIds::FLY_AS_CHAR); + + SwFormatHoriOrient rHoriOri = rFormat.GetHoriOrient(); + SwFormatVertOrient rVertOri = rFormat.GetVertOrient(); + + // #i30669# - convert the positioning attributes. + // Most positions are converted, if layout information exists. + const bool bPosConverted = ConvertPosition( rHoriOri, rVertOri, rFormat ); + + const sal_Int16 eHOri = rHoriOri.GetHoriOrient(); + const sal_Int16 eVOri = rVertOri.GetVertOrient(); // #i22673# + + const sal_Int16 eHRel = rHoriOri.GetRelationOrient(); + const sal_Int16 eVRel = rVertOri.GetRelationOrient(); + + // horizontal Adjustment + switch (eHOri) + { + default: + case text::HoriOrientation::NONE: + mnXAlign = 0; + break; + case text::HoriOrientation::LEFT: + mnXAlign = 1; + break; + case text::HoriOrientation::CENTER: + mnXAlign = 2; + break; + case text::HoriOrientation::RIGHT: + mnXAlign = 3; + break; + case text::HoriOrientation::INSIDE: + mnXAlign = 4; + break; + case text::HoriOrientation::OUTSIDE: + mnXAlign = 5; + break; + } + + // vertical Adjustment + // #i22673# + // When adjustment is vertically relative to line or to char + // bottom becomes top and vice versa + const bool bVertSwap = !bPosConverted && + ( (eVRel == text::RelOrientation::CHAR) || + (eVRel == text::RelOrientation::TEXT_LINE) ); + switch (eVOri) + { + default: + case text::VertOrientation::NONE: + mnYAlign = 0; + break; + case text::VertOrientation::TOP: + case text::VertOrientation::LINE_TOP: + case text::VertOrientation::CHAR_TOP: + mnYAlign = bVertSwap ? 3 : 1; + break; + case text::VertOrientation::CENTER: + case text::VertOrientation::LINE_CENTER: + mnYAlign = 2; + break; + case text::VertOrientation::BOTTOM: + case text::VertOrientation::LINE_BOTTOM: + case text::VertOrientation::CHAR_BOTTOM: + mnYAlign = bVertSwap ? 1 : 3; + break; + } + + // Adjustment is horizontally relative to... + switch (eHRel) + { + case text::RelOrientation::PAGE_PRINT_AREA: + mnXRelTo = 0; + break; + case text::RelOrientation::PAGE_FRAME: + case text::RelOrientation::PAGE_LEFT: //:-( + case text::RelOrientation::PAGE_RIGHT: //:-( + mnXRelTo = 1; + break; + case text::RelOrientation::FRAME: + case text::RelOrientation::FRAME_LEFT: //:-( + case text::RelOrientation::FRAME_RIGHT: //:-( + if (eAnchor == RndStdIds::FLY_AT_PAGE) + mnXRelTo = 1; + else + mnXRelTo = 2; + break; + case text::RelOrientation::PRINT_AREA: + if (eAnchor == RndStdIds::FLY_AT_PAGE) + mnXRelTo = 0; + else + mnXRelTo = 2; + break; + case text::RelOrientation::CHAR: + mnXRelTo = 3; + break; + case text::RelOrientation::TEXT_LINE: + break; + } + + // Adjustment is vertically relative to... + switch (eVRel) + { + case text::RelOrientation::PAGE_PRINT_AREA: + mnYRelTo = 0; + break; + case text::RelOrientation::PAGE_FRAME: + mnYRelTo = 1; + break; + case text::RelOrientation::PRINT_AREA: + if (eAnchor == RndStdIds::FLY_AT_PAGE) + mnYRelTo = 0; + else + mnYRelTo = 2; + break; + case text::RelOrientation::FRAME: + if (eAnchor == RndStdIds::FLY_AT_PAGE) + mnYRelTo = 1; + else + mnYRelTo = 2; + break; + case text::RelOrientation::CHAR: + case text::RelOrientation::TEXT_LINE: // #i22673# - vertical alignment at top of line + case text::RelOrientation::PAGE_LEFT: //nonsense + case text::RelOrientation::PAGE_RIGHT: //nonsense + case text::RelOrientation::FRAME_LEFT: //nonsense + case text::RelOrientation::FRAME_RIGHT: //nonsense + mnYRelTo = 3; + break; + } +} + +void SwEscherEx::WriteFrameExtraData( const SwFrameFormat& rFormat ) +{ + aWinwordAnchoring.SetAnchoring(rFormat); + aWinwordAnchoring.WriteData(*this); + + AddAtom(4, ESCHER_ClientAnchor); + GetStream().WriteInt32( 0 ); + + AddAtom(4, ESCHER_ClientData); + GetStream().WriteInt32( 1 ); +} + +sal_Int32 SwEscherEx::WriteFlyFrame(const DrawObj &rObj, sal_uInt32 &rShapeId, + DrawObjPointerVector &rPVec) +{ + const SwFrameFormat &rFormat = rObj.maContent.GetFrameFormat(); + + // check for textflyframe and if it is the first in a Chain + sal_Int32 nBorderThick = 0; + const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx(); + if( pNdIdx ) + { + SwNodeIndex aIdx( *pNdIdx, 1 ); + switch( aIdx.GetNode().GetNodeType() ) + { + case SwNodeType::Grf: + nBorderThick = WriteGrfFlyFrame( rFormat, rShapeId = GenerateShapeId() ); + break; + case SwNodeType::Ole: + nBorderThick = WriteOLEFlyFrame( rFormat, rShapeId = GenerateShapeId() ); + break; + default: + if (const SdrObject* pObj = rFormat.FindRealSdrObject()) + { + // check for the first in a Chain + sal_uInt32 nTextId; + sal_uInt16 nOff = 0; + const SwFrameFormat* pFormat = &rFormat, *pPrev; + while( nullptr != ( pPrev = pFormat->GetChain().GetPrev() )) + { + ++nOff; + pFormat = pPrev; + } + + rShapeId = GetFlyShapeId(rFormat, rObj.mnHdFtIndex, rPVec); + if( !nOff ) + { + nTextId = pTextBxs->GetPos( pObj ); + if( USHRT_MAX == nTextId ) + { + pTextBxs->Append( *pObj, rShapeId ); + nTextId = pTextBxs->Count(); + } + else + ++nTextId; + } + else + { + const SdrObject* pPrevObj = pFormat->FindRealSdrObject(); + nTextId = pTextBxs->GetPos( pPrevObj ); + if( USHRT_MAX == nTextId ) + { + sal_uInt32 nPrevShapeId = + GetFlyShapeId(*pFormat, rObj.mnHdFtIndex, rPVec); + pTextBxs->Append( *pPrevObj, nPrevShapeId ); + nTextId = pTextBxs->Count(); + } + else + ++nTextId; + } + nTextId *= 0x10000; + nTextId += nOff; + + nBorderThick = WriteTextFlyFrame(rObj, rShapeId, nTextId, rPVec); + } + + //In browse mode the sdr object doesn't always exist. For example, the + //object is in the hidden header/footer. We save the fmt directly + //in such cases; we copy most of the logic from the block above + const bool bBrowseMode = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE); + if( bBrowseMode && rFormat.GetDoc()) + { + if( !rFormat.GetChain().GetPrev() )//obj in header/footer? + { + rShapeId = GetFlyShapeId(rFormat, rObj.mnHdFtIndex, rPVec); + pTextBxs->Append( &rFormat, rShapeId ); + sal_uInt32 nTextId = pTextBxs->Count(); + + nTextId *= 0x10000; + nBorderThick = WriteTextFlyFrame(rObj, rShapeId, nTextId, rPVec); + } + } + + } + } + return nBorderThick; +} + +static sal_uInt16 FindPos(const SwFrameFormat &rFormat, unsigned int nHdFtIndex, + DrawObjPointerVector &rPVec) +{ + auto aIter = std::find_if(rPVec.begin(), rPVec.end(), + [&rFormat, nHdFtIndex](const DrawObj* pObj) { + OSL_ENSURE(pObj, "Impossible"); + return pObj && + nHdFtIndex == pObj->mnHdFtIndex && + &rFormat == (&pObj->maContent.GetFrameFormat()); + }); + if (aIter != rPVec.end()) + return static_cast< sal_uInt16 >(aIter - rPVec.begin()); + return USHRT_MAX; +} + +sal_Int32 SwEscherEx::WriteTextFlyFrame(const DrawObj &rObj, sal_uInt32 nShapeId, + sal_uInt32 nTextBox, DrawObjPointerVector &rPVec) +{ + const SwFrameFormat &rFormat = rObj.maContent.GetFrameFormat(); + SvxFrameDirection nDirection = rObj.mnDirection; + + sal_Int32 nBorderThick=0; + OpenContainer( ESCHER_SpContainer ); + + AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nShapeId ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt(ESCHER_Prop_lTxid, nTextBox); + if (const SwFrameFormat *pNext = rFormat.GetChain().GetNext()) + { + sal_uInt16 nPos = FindPos(*pNext, rObj.mnHdFtIndex, rPVec); + if (USHRT_MAX != nPos && aFollowShpIds[nPos]) + aPropOpt.AddOpt(ESCHER_Prop_hspNext, aFollowShpIds[nPos]); + } + nBorderThick = WriteFlyFrameAttr( rFormat, mso_sptTextBox, aPropOpt ); + + MSO_TextFlow nFlow; + + switch (nDirection) + { + default: + OSL_ENSURE(false, "unknown direction type"); + [[fallthrough]]; + case SvxFrameDirection::Horizontal_LR_TB: + nFlow=mso_txflHorzN; + break; + case SvxFrameDirection::Horizontal_RL_TB: + nFlow=mso_txflHorzN; + break; + case SvxFrameDirection::Vertical_LR_TB: //not really possible in word + case SvxFrameDirection::Vertical_RL_TB: + nFlow=mso_txflTtoBA; + break; + case SvxFrameDirection::Vertical_LR_BT: + nFlow = mso_txflBtoT; + break; + } + aPropOpt.AddOpt( ESCHER_Prop_txflTextFlow, nFlow ); + + aPropOpt.Commit( GetStream() ); + + // store anchor attribute + WriteFrameExtraData( rFormat ); + + AddAtom( 4, ESCHER_ClientTextbox ); GetStream().WriteUInt32( nTextBox ); + + CloseContainer(); // ESCHER_SpContainer + return nBorderThick; +} + +void SwBasicEscherEx::WriteOLEPicture(EscherPropertyContainer &rPropOpt, + ShapeFlag nShapeFlags, const Graphic &rGraphic, const SdrObject &rObj, + sal_uInt32 nShapeId, const awt::Rectangle* pVisArea ) +{ + //nShapeFlags == 0xA00 + flips and ole active + AddShape(ESCHER_ShpInst_PictureFrame, nShapeFlags, nShapeId); + + GraphicObject aGraphicObject(rGraphic); + OString aId = aGraphicObject.GetUniqueID(); + if (!aId.isEmpty()) + { + // SJ: the third parameter (pVisArea) should be set... + sal_uInt32 nBlibId = mxGlobal->GetBlibID( *QueryPictureStream(), aGraphicObject, pVisArea); + if (nBlibId) + rPropOpt.AddOpt(ESCHER_Prop_pib, nBlibId, true); + } + + SetPicId(rObj, nShapeId, rPropOpt); + rPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0x10000 ); +} + +void SwEscherEx::WriteOCXControl( const SwFrameFormat& rFormat, sal_uInt32 nShapeId ) +{ + const SdrObject* pSdrObj = rFormat.FindRealSdrObject(); + if (!pSdrObj) + return; + + OpenContainer( ESCHER_SpContainer ); + + SwDrawModel *pModel = rWrt.m_pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + OutputDevice *pDevice = Application::GetDefaultDevice(); + OSL_ENSURE(pModel && pDevice, "no model or device"); + + // #i71538# use complete SdrViews + // SdrExchangeView aExchange(pModel, pDevice); + SdrView aExchange(*pModel, pDevice); + const Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pSdrObj)); + EscherPropertyContainer aPropOpt; + WriteOLEPicture(aPropOpt, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::OLEShape, aGraphic, + *pSdrObj, nShapeId, nullptr ); + + WriteFlyFrameAttr( rFormat, mso_sptPictureFrame , aPropOpt ); + aPropOpt.Commit( GetStream() ); + + // store anchor attribute + WriteFrameExtraData( rFormat ); + + CloseContainer(); // ESCHER_SpContainer + +} + +void SwEscherEx::MakeZOrderArrAndFollowIds( + std::vector<DrawObj>& rSrcArr, DrawObjPointerVector&rDstArr) +{ + ::lcl_makeZOrderArray(rWrt, rSrcArr, rDstArr); + + //Now set up the follow IDs + aFollowShpIds.clear(); + + for (DrawObj* p : rDstArr) + { + const SwFrameFormat &rFormat = p->maContent.GetFrameFormat(); + bool bNeedsShapeId = false; + + if (RES_FLYFRMFMT == rFormat.Which()) + { + const SwFormatChain &rChain = rFormat.GetChain(); + if (rChain.GetPrev() || rChain.GetNext()) + bNeedsShapeId = true; + } + + sal_uLong nShapeId = bNeedsShapeId ? GenerateShapeId() : 0; + + aFollowShpIds.push_back(nShapeId); + } +} + +sal_uInt32 SwEscherEx::GetFlyShapeId(const SwFrameFormat& rFormat, + unsigned int nHdFtIndex, DrawObjPointerVector &rpVec) +{ + sal_uInt16 nPos = FindPos(rFormat, nHdFtIndex, rpVec); + sal_uInt32 nShapeId; + if (USHRT_MAX != nPos) + { + if (0 == (nShapeId = aFollowShpIds[nPos])) + { + nShapeId = GenerateShapeId(); + aFollowShpIds[ nPos ] = nShapeId; + } + } + else + nShapeId = GenerateShapeId(); + return nShapeId; +} + +sal_uInt32 SwEscherEx::QueryTextID( + const uno::Reference< drawing::XShape>& xXShapeRef, sal_uInt32 nShapeId ) +{ + sal_uInt32 nId = 0; + if (SdrObject* pObj = GetSdrObjectFromXShape(xXShapeRef)) + { + pTextBxs->Append( *pObj, nShapeId ); + nId = pTextBxs->Count(); + nId *= 0x10000; + } + return nId; +} + +SwMSConvertControls::SwMSConvertControls( SfxObjectShell const *pDSh, SwPaM *pP ) : oox +::ole::MSConvertOCXControls( pDSh ? pDSh->GetModel() : nullptr ), pPaM( pP ), mnObjectId(0) +{ +} + + +// in transitioning away old filter for ole/ocx controls, ReadOCXStream has been made pure virtual in +// filter/source/msocximex.cxx, so... we need an implementation here +bool SwMSConvertControls::ReadOCXStream( tools::SvRef<SotStorage> const & rSrc1, + css::uno::Reference< css::drawing::XShape > *pShapeRef, + bool bFloatingCtrl ) +{ + uno::Reference< form::XFormComponent > xFComp; + bool bRes = oox::ole::MSConvertOCXControls::ReadOCXStorage( rSrc1, xFComp ); + if ( bRes && xFComp.is() ) + { + css::awt::Size aSz; // not used in import + bRes = InsertControl( xFComp, aSz,pShapeRef,bFloatingCtrl); + } + return bRes; +} + +void SwMSConvertControls::ExportControl(WW8Export &rWW8Wrt, const SdrUnoObj& rFormObj) +{ + const uno::Reference< awt::XControlModel >& xControlModel = + rFormObj.GetUnoControlModel(); + + //Why oh lord do we use so many different units ? + //I think I painted myself into a little bit of a + //corner by trying to use the uno interface for + //controls export + tools::Rectangle aRect = rFormObj.GetLogicRect(); + aRect.SetPos(Point(0,0)); + awt::Size aSize; + aSize.Width = TWIPS_TO_MM(aRect.Right()); + aSize.Height = TWIPS_TO_MM(aRect.Bottom()); + + //Open the ObjectPool + tools::SvRef<SotStorage> xObjPool = rWW8Wrt.GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool); + + //Create a destination storage for the microsoft control + sal_uInt32 nObjId = ++mnObjectId; + OUString sStorageName = "_" + OUString::number( static_cast<sal_Int64>( nObjId )); + tools::SvRef<SotStorage> xOleStg = xObjPool->OpenSotStorage(sStorageName); + + if (!xOleStg.is()) + return; + + OUString sUName; + if (!WriteOCXStream( mxModel, xOleStg,xControlModel,aSize,sUName)) + return; + + sal_uInt8 aSpecOLE[] = + { + 0x03, 0x6a, 0xFF, 0xFF, 0xFF, 0xFF, // sprmCPicLocation + 0x0a, 0x08, 1, // sprmCFOLE2 + 0x55, 0x08, 1, // sprmCFSpec + 0x56, 0x08, 1 // sprmCFObj + }; + //Set the obj id into the sprmCPicLocation + sal_uInt8 *pData = aSpecOLE+2; + Set_UInt32(pData,nObjId ); + + OUString sField = FieldString(ww::eCONTROL) + "Forms." + sUName + ".1 \\s "; + + rWW8Wrt.OutputField(nullptr, ww::eCONTROL, sField, + FieldFlags::Start|FieldFlags::CmdStart|FieldFlags::CmdEnd); + + rWW8Wrt.m_pChpPlc->AppendFkpEntry(rWW8Wrt.Strm().Tell(),sizeof(aSpecOLE), + aSpecOLE); + rWW8Wrt.WriteChar( 0x1 ); + rWW8Wrt.OutputField(nullptr, ww::eCONTROL, OUString(), FieldFlags::End | FieldFlags::Close); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx new file mode 100644 index 000000000..30f06c8d5 --- /dev/null +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -0,0 +1,3612 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vector> +#include <utility> +#include <algorithm> +#include <iostream> + +#include "docxexport.hxx" + +#include <i18nlangtag/mslangid.hxx> +#include <hintids.hxx> +#include <tools/urlobj.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/svxfont.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/wghtitem.hxx> +#include <svl/grabbagitem.hxx> +#include <svl/urihelper.hxx> +#include <svl/whiter.hxx> +#include <fmtpdsc.hxx> +#include <fmtlsplt.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <frmatr.hxx> +#include <paratr.hxx> +#include <txatbase.hxx> +#include <fmtinfmt.hxx> +#include <fmtrfmrk.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <charfmt.hxx> +#include <tox.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <docary.hxx> +#include <swtable.hxx> +#include <swtblfmt.hxx> +#include <section.hxx> +#include <pagedesc.hxx> +#include <swrect.hxx> +#include <reffld.hxx> +#include <redline.hxx> +#include <txttxmrk.hxx> +#include <fmtline.hxx> +#include <fmtruby.hxx> +#include <breakit.hxx> +#include <txtatr.hxx> +#include <cellatr.hxx> +#include <fmtrowsplt.hxx> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/text/RubyPosition.hpp> +#include <oox/export/vmlexport.hxx> +#include <sal/log.hxx> +#include <comphelper/propertysequence.hxx> + +#include "sprmids.hxx" + +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include <numrule.hxx> +#include "wrtww8.hxx" +#include "ww8par.hxx" +#include <IMark.hxx> +#include "ww8attributeoutput.hxx" + +#include <ndgrf.hxx> +#include <ndole.hxx> + +#include <cstdio> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::i18n; +using namespace sw::util; +using namespace sw::types; +using namespace sw::mark; +using namespace ::oox::vml; + +static OUString lcl_getFieldCode( const IFieldmark* pFieldmark ) +{ + assert(pFieldmark); + + if ( pFieldmark->GetFieldname( ) == ODF_FORMTEXT ) + return " FORMTEXT "; + if ( pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN ) + return " FORMDROPDOWN "; + if ( pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX ) + return " FORMCHECKBOX "; + if ( pFieldmark->GetFieldname( ) == ODF_FORMDATE ) + return " ODFFORMDATE "; + if ( pFieldmark->GetFieldname( ) == ODF_TOC ) + return " TOC "; + if ( pFieldmark->GetFieldname( ) == ODF_HYPERLINK ) + return " HYPERLINK "; + if ( pFieldmark->GetFieldname( ) == ODF_PAGEREF ) + return " PAGEREF "; + return pFieldmark->GetFieldname(); +} + +static ww::eField lcl_getFieldId(const IFieldmark*const pFieldmark) +{ + assert(pFieldmark); + + if ( pFieldmark->GetFieldname( ) == ODF_FORMTEXT ) + return ww::eFORMTEXT; + if ( pFieldmark->GetFieldname( ) == ODF_FORMDROPDOWN ) + return ww::eFORMDROPDOWN; + if ( pFieldmark->GetFieldname( ) == ODF_FORMCHECKBOX ) + return ww::eFORMCHECKBOX; + if ( pFieldmark->GetFieldname( ) == ODF_FORMDATE ) + return ww::eFORMDATE; + if ( pFieldmark->GetFieldname( ) == ODF_TOC ) + return ww::eTOC; + if ( pFieldmark->GetFieldname( ) == ODF_HYPERLINK ) + return ww::eHYPERLINK; + if ( pFieldmark->GetFieldname( ) == ODF_PAGEREF ) + return ww::ePAGEREF; + return ww::eUNKNOWN; +} + +MSWordAttrIter::MSWordAttrIter( MSWordExportBase& rExport ) + : pOld( rExport.m_pChpIter ), m_rExport( rExport ) +{ + m_rExport.m_pChpIter = this; +} + +MSWordAttrIter::~MSWordAttrIter() +{ + m_rExport.m_pChpIter = pOld; +} + +namespace { + +class sortswflys +{ +public: + bool operator()(const ww8::Frame &rOne, const ww8::Frame &rTwo) const + { + return rOne.GetPosition() < rTwo.GetPosition(); + } +}; + +} + +void SwWW8AttrIter::IterToCurrent() +{ + OSL_ENSURE(maCharRuns.begin() != maCharRuns.end(), "Impossible"); + mnScript = maCharRunIter->mnScript; + meChrSet = maCharRunIter->meCharSet; + mbCharIsRTL = maCharRunIter->mbRTL; +} + +SwWW8AttrIter::SwWW8AttrIter(MSWordExportBase& rWr, const SwTextNode& rTextNd) : + MSWordAttrIter(rWr), + rNd(rTextNd), + maCharRuns(GetPseudoCharRuns(rTextNd)), + pCurRedline(nullptr), + nCurrentSwPos(0), + nCurRedlinePos(SwRedlineTable::npos), + mrSwFormatDrop(rTextNd.GetSwAttrSet().GetDrop()) +{ + + SwPosition aPos(rTextNd); + mbParaIsRTL = SvxFrameDirection::Horizontal_RL_TB == rWr.m_pDoc->GetTextDirection(aPos); + + maCharRunIter = maCharRuns.begin(); + IterToCurrent(); + + /* + #i2916# + Get list of any graphics which may be anchored from this paragraph. + */ + maFlyFrames = GetFramesInNode(rWr.m_aFrames, rNd); + std::sort(maFlyFrames.begin(), maFlyFrames.end(), sortswflys()); + + /* + #i18480# + If we are inside a frame then anything anchored inside this frame can + only be supported by word anchored inline ("as character"), so force + this in the supportable case. + */ + if (rWr.m_bInWriteEscher) + { + for ( auto& aFlyFrame : maFlyFrames ) + aFlyFrame.ForceTreatAsInline(); + } + + maFlyIter = maFlyFrames.begin(); + + if ( !m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + SwPosition aPosition( rNd, SwIndex( const_cast<SwTextNode*>(&rNd) ) ); + pCurRedline = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedline( aPosition, &nCurRedlinePos ); + } + + nCurrentSwPos = SearchNext(1); +} + +static sal_Int32 lcl_getMinPos( sal_Int32 pos1, sal_Int32 pos2 ) +{ + if ( pos1 >= 0 && pos2 >= 0 ) + { + // both valid: return minimum one + return std::min(pos1, pos2); + } + + // return the valid one, if any, or -1 + return std::max(pos1, pos2); +} + +sal_Int32 SwWW8AttrIter::SearchNext( sal_Int32 nStartPos ) +{ + const OUString aText = rNd.GetText(); + sal_Int32 fieldEndPos = aText.indexOf(CH_TXT_ATR_FIELDEND, nStartPos - 1); + // HACK: for (so far) mysterious reasons the sdtContent element closes + // too late in testDateFormField() unless an empty run is exported at + // the end of the fieldmark; hence find *also* the position after the + // CH_TXT_ATR_FIELDEND here + if (0 <= fieldEndPos && fieldEndPos < nStartPos) + { + ++fieldEndPos; + } + sal_Int32 fieldSepPos = aText.indexOf(CH_TXT_ATR_FIELDSEP, nStartPos); + sal_Int32 fieldStartPos = aText.indexOf(CH_TXT_ATR_FIELDSTART, nStartPos); + sal_Int32 formElementPos = aText.indexOf(CH_TXT_ATR_FORMELEMENT, nStartPos - 1); + if (0 <= formElementPos && formElementPos < nStartPos) + { + ++formElementPos; // tdf#133604 put this in its own run + } + + const sal_Int32 pos = lcl_getMinPos( + lcl_getMinPos(lcl_getMinPos(fieldEndPos, fieldSepPos), fieldStartPos), + formElementPos ); + + sal_Int32 nMinPos = (pos>=0) ? pos : SAL_MAX_INT32; + + // first the redline, then the attributes + if( pCurRedline ) + { + const SwPosition* pEnd = pCurRedline->End(); + if (pEnd->nNode == rNd) + { + const sal_Int32 i = pEnd->nContent.GetIndex(); + if ( i >= nStartPos && i < nMinPos ) + { + nMinPos = i; + } + } + } + + if ( nCurRedlinePos < m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() ) + { + // nCurRedlinePos point to the next redline + SwRedlineTable::size_type nRedLinePos = nCurRedlinePos; + if( pCurRedline ) + ++nRedLinePos; + + for ( ; nRedLinePos < m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); ++nRedLinePos ) + { + const SwRangeRedline* pRedl = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ nRedLinePos ]; + + const SwPosition* pStt = pRedl->Start(); + const SwPosition* pEnd = pStt == pRedl->GetPoint() + ? pRedl->GetMark() + : pRedl->GetPoint(); + + if( pStt->nNode == rNd ) + { + const sal_Int32 i = pStt->nContent.GetIndex(); + if( i >= nStartPos && i < nMinPos ) + nMinPos = i; + } + else + break; + + if( pEnd->nNode == rNd ) + { + const sal_Int32 i = pEnd->nContent.GetIndex(); + if( i >= nStartPos && i < nMinPos ) + { + nMinPos = i; + } + } + } + } + + if (mrSwFormatDrop.GetWholeWord() && nStartPos <= rNd.GetDropLen(0)) + nMinPos = rNd.GetDropLen(0); + else if(nStartPos <= mrSwFormatDrop.GetChars()) + nMinPos = mrSwFormatDrop.GetChars(); + + if(const SwpHints* pTextAttrs = rNd.GetpSwpHints()) + { + +// can be optimized if we consider that the TextAttrs are sorted by start position. +// but then we'd have to save 2 indices + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + sal_Int32 nPos = pHt->GetStart(); // first Attr characters + if( nPos >= nStartPos && nPos <= nMinPos ) + nMinPos = nPos; + + if( pHt->End() ) // Attr with end + { + nPos = *pHt->End(); // last Attr character + 1 + if( nPos >= nStartPos && nPos <= nMinPos ) + nMinPos = nPos; + } + if (pHt->HasDummyChar()) + { + // pos + 1 because of CH_TXTATR in Text + nPos = pHt->GetStart() + 1; + if( nPos >= nStartPos && nPos <= nMinPos ) + nMinPos = nPos; + } + } + } + + if (maCharRunIter != maCharRuns.end()) + { + if (maCharRunIter->mnEndPos < nMinPos) + nMinPos = maCharRunIter->mnEndPos; + IterToCurrent(); + } + + /* + #i2916# + Check to see if there are any graphics anchored to characters in this + paragraph's text. Set nMinPos to 1 past the placement for anchored to + character because anchors in Word appear after the character they are + anchored to. + */ + if (maFlyIter != maFlyFrames.end()) + { + const SwPosition &rAnchor = maFlyIter->GetPosition(); + + sal_Int32 nPos = rAnchor.nContent.GetIndex(); + if (nPos >= nStartPos && nPos <= nMinPos) + nMinPos = nPos; + + if (maFlyIter->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + ++nPos; + if (nPos >= nStartPos && nPos <= nMinPos) + nMinPos = nPos; + } + } + + //nMinPos found and not going to change at this point + + if (maCharRunIter != maCharRuns.end()) + { + if (maCharRunIter->mnEndPos == nMinPos) + ++maCharRunIter; + } + + return nMinPos; +} + +void SwWW8AttrIter::OutAttr( sal_Int32 nSwPos, bool bWriteCombChars) +{ + m_rExport.AttrOutput().RTLAndCJKState( mbCharIsRTL, GetScript() ); + + /* + Depending on whether text is in CTL/CJK or Western, get the id of that + script, the idea is that the font that is actually in use to render this + range of text ends up in pFont + */ + sal_uInt16 nFontId = GetWhichOfScript( RES_CHRATR_FONT, GetScript() ); + + const SvxFontItem &rParentFont = ItemGet<SvxFontItem>( + static_cast<const SwTextFormatColl&>(rNd.GetAnyFormatColl()), nFontId); + const SvxFontItem *pFont = &rParentFont; + const SfxPoolItem *pGrabBag = nullptr; + + SfxItemSet aExportSet(*rNd.GetSwAttrSet().GetPool(), + svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END - 1>{}); + + //The hard formatting properties that affect the entire paragraph + if (rNd.HasSwAttrSet()) + { + // only copy hard attributes - bDeep = false + aExportSet.Set(rNd.GetSwAttrSet(), false/*bDeep*/); + // get the current font item. Use rNd.GetSwAttrSet instead of aExportSet: + const SvxFontItem &rNdFont = ItemGet<SvxFontItem>(rNd.GetSwAttrSet(), nFontId); + pFont = &rNdFont; + aExportSet.ClearItem(nFontId); + } + + //The additional hard formatting properties that affect this range in the + //paragraph + ww8::PoolItems aRangeItems; + if (const SwpHints* pTextAttrs = rNd.GetpSwpHints()) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + const sal_Int32* pEnd = pHt->End(); + + if (pEnd ? ( nSwPos >= pHt->GetStart() && nSwPos < *pEnd) + : nSwPos == pHt->GetStart() ) + { + sal_uInt16 nWhich = pHt->GetAttr().Which(); + if (nWhich == RES_TXTATR_AUTOFMT) + { + const SwFormatAutoFormat& rAutoFormat = static_cast<const SwFormatAutoFormat&>(pHt->GetAttr()); + const std::shared_ptr<SfxItemSet>& pSet = rAutoFormat.GetStyleHandle(); + SfxWhichIter aIter( *pSet ); + const SfxPoolItem* pItem; + sal_uInt16 nWhichId = aIter.FirstWhich(); + while( nWhichId ) + { + if( SfxItemState::SET == pSet->GetItemState( nWhichId, false, &pItem )) + { + if (nWhichId == nFontId) + pFont = &(item_cast<SvxFontItem>(*pItem)); + else if (nWhichId == RES_CHRATR_GRABBAG) + pGrabBag = pItem; + else + aRangeItems[nWhichId] = pItem; + } + nWhichId = aIter.NextWhich(); + } + } + else + aRangeItems[nWhich] = (&(pHt->GetAttr())); + } + else if (nSwPos < pHt->GetStart()) + break; + } + } + + /* + For #i24291# we need to explicitly remove any properties from the + aExportSet which a SwCharFormat would override, we can't rely on word doing + this for us like writer does + */ + const SwFormatCharFormat *pCharFormatItem = + HasItem< SwFormatCharFormat >( aRangeItems, RES_TXTATR_CHARFMT ); + if ( pCharFormatItem ) + ClearOverridesFromSet( *pCharFormatItem, aExportSet ); + + // check toggle properties in DOCX output + { + SvxWeightItem aBoldProperty(WEIGHT_BOLD, RES_CHRATR_WEIGHT); + handleToggleProperty(aExportSet, pCharFormatItem, RES_CHRATR_WEIGHT, &aBoldProperty); + } + + // tdf#113790: AutoFormat style overwrites char style, so remove all + // elements from CHARFMT grab bag which are set in AUTOFMT grab bag + if (const SfxGrabBagItem *pAutoFmtGrabBag = dynamic_cast<const SfxGrabBagItem*>(pGrabBag)) + { + if (const SfxGrabBagItem *pCharFmtGrabBag = aExportSet.GetItem<SfxGrabBagItem>(RES_CHRATR_GRABBAG, false)) + { + std::unique_ptr<SfxGrabBagItem> pNewCharFmtGrabBag(pCharFmtGrabBag->Clone()); + assert(pNewCharFmtGrabBag); + auto & rNewFmtMap = pNewCharFmtGrabBag->GetGrabBag(); + for (auto const & item : pAutoFmtGrabBag->GetGrabBag()) + { + if (item.second.hasValue()) + rNewFmtMap.erase(item.first); + } + aExportSet.Put(std::move(pNewCharFmtGrabBag)); + } + } + + ww8::PoolItems aExportItems; + GetPoolItems( aExportSet, aExportItems, false ); + + if( rNd.GetpSwpHints() == nullptr ) + m_rExport.SetCurItemSet(&aExportSet); + + for ( const auto& aRangeItem : aRangeItems ) + { + aExportItems[aRangeItem.first] = aRangeItem.second; + } + + if ( !aExportItems.empty() ) + { + const SwModify* pOldMod = m_rExport.m_pOutFormatNode; + m_rExport.m_pOutFormatNode = &rNd; + m_rExport.m_aCurrentCharPropStarts.push( nSwPos ); + + // tdf#38778 Fix output of the font in DOC run for fields + const SvxFontItem * pFontToOutput = ( rParentFont != *pFont )? pFont : nullptr; + + m_rExport.ExportPoolItemsToCHP( aExportItems, GetScript(), pFontToOutput, bWriteCombChars ); + + // HasTextItem only allowed in the above range + m_rExport.m_aCurrentCharPropStarts.pop(); + m_rExport.m_pOutFormatNode = pOldMod; + } + + if( rNd.GetpSwpHints() == nullptr ) + m_rExport.SetCurItemSet(nullptr); + + OSL_ENSURE( pFont, "must be *some* font associated with this txtnode" ); + if ( pFont ) + { + SvxFontItem aFont( *pFont ); + + if ( rParentFont != aFont ) + m_rExport.AttrOutput().OutputItem( aFont ); + } + + // Output grab bag attributes + if (pGrabBag) + m_rExport.AttrOutput().OutputItem( *pGrabBag ); +} + +// Toggle Properties +// +// If the value of the toggle property appears at multiple levels of the style hierarchy (17.7.2), their +// effective values shall be combined as follows: +// +// value_{effective} = val_{table} XOR val_{paragraph} XOR val_{character} +// +// If the value specified by the document defaults is true, the effective value is true. +// Otherwise, the values are combined by a Boolean XOR as follows: +// i.e., the effective value to be applied to the content shall be true if its effective value is true for +// an odd number of levels of the style hierarchy. +// +// To prevent such logic inside output, it is required to write inline w:b token on content level. +void SwWW8AttrIter::handleToggleProperty(SfxItemSet& rExportSet, const SwFormatCharFormat* pCharFormatItem, + sal_uInt16 nWhich, const SfxPoolItem* pValue) +{ + if (!rExportSet.HasItem(nWhich) && pValue) + { + bool hasPropertyInCharStyle = false; + bool hasPropertyInParaStyle = false; + + // get bold flag from specified character style + if (pCharFormatItem) + { + if (const SwCharFormat* pCharFormat = pCharFormatItem->GetCharFormat()) + { + const SfxPoolItem* pItem = nullptr; + if (pCharFormat->GetAttrSet().HasItem(nWhich, &pItem)) + { + hasPropertyInCharStyle = (*pItem == *pValue); + } + } + } + + // get bold flag from specified paragraph style + { + SwTextFormatColl& rTextColl = static_cast<SwTextFormatColl&>( rNd.GetAnyFormatColl() ); + sal_uInt16 nStyle = m_rExport.m_pStyles->GetSlot( &rTextColl ); + nStyle = ( nStyle != 0xfff ) ? nStyle : 0; + const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nStyle); + if (pFormat) + { + const SfxPoolItem* pItem = nullptr; + if (pFormat->GetAttrSet().HasItem(nWhich, &pItem)) + { + hasPropertyInParaStyle = (*pItem == *pValue); + } + } + } + + // add inline property + if (hasPropertyInCharStyle && hasPropertyInParaStyle) + { + rExportSet.Put(*pValue); + } + } +} + +bool SwWW8AttrIter::IsWatermarkFrame() +{ + if (maFlyFrames.size() != 1) + return false; + + while ( maFlyIter != maFlyFrames.end() ) + { + const SdrObject* pSdrObj = maFlyIter->GetFrameFormat().FindRealSdrObject(); + + if (pSdrObj) + { + if (VMLExport::IsWaterMarkShape(pSdrObj->GetName())) + return true; + } + ++maFlyIter; + } + + return false; +} + +bool SwWW8AttrIter::IsAnchorLinkedToThisNode( sal_uLong nNodePos ) +{ + ww8::FrameIter aTmpFlyIter = maFlyIter ; + + while ( aTmpFlyIter != maFlyFrames.end() ) + { + const SwPosition &rAnchor = maFlyIter->GetPosition(); + sal_uLong nAnchorPos = rAnchor.nNode.GetIndex(); + /* if current node position and the anchor position are the same + then the frame anchor is linked to this node + */ + if ( nAnchorPos == nNodePos ) + return true ; + + ++aTmpFlyIter; + } + return false ; +} + +bool SwWW8AttrIter::HasFlysAt(sal_Int32 nSwPos) const +{ + for (const auto& rFly : maFlyFrames) + { + const SwPosition& rAnchor = rFly.GetPosition(); + const sal_Int32 nPos = rAnchor.nContent.GetIndex(); + if (nPos == nSwPos) + { + return true; + } + } + + return false; +} + +FlyProcessingState SwWW8AttrIter::OutFlys(sal_Int32 nSwPos) +{ + // collection point to first gather info about all of the potentially linked textboxes: to be analyzed later. + OUString sLinkChainName; + ww8::FrameIter linkedTextboxesIter = maFlyIter; + while ( linkedTextboxesIter != maFlyFrames.end() ) + { + uno::Reference< drawing::XShape > xShape; + ww8::Frame aFrame = *linkedTextboxesIter; + const SdrObject* pSdrObj = aFrame.GetFrameFormat().FindRealSdrObject(); + if( pSdrObj ) + xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY); + uno::Reference< beans::XPropertySetInfo > xPropertySetInfo; + if( xPropertySet.is() ) + xPropertySetInfo = xPropertySet->getPropertySetInfo(); + if( xPropertySetInfo.is() ) + { + MSWordExportBase::LinkedTextboxInfo aLinkedTextboxInfo; + + if( xPropertySetInfo->hasPropertyByName("LinkDisplayName") ) + xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName; + else if( xPropertySetInfo->hasPropertyByName("ChainName") ) + xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName; + + if( xPropertySetInfo->hasPropertyByName("ChainNextName") ) + xPropertySet->getPropertyValue("ChainNextName") >>= aLinkedTextboxInfo.sNextChain; + if( xPropertySetInfo->hasPropertyByName("ChainPrevName") ) + xPropertySet->getPropertyValue("ChainPrevName") >>= aLinkedTextboxInfo.sPrevChain; + + //collect a list of linked textboxes: those with a NEXT or PREVIOUS link + if( !aLinkedTextboxInfo.sNextChain.isEmpty() || !aLinkedTextboxInfo.sPrevChain.isEmpty() ) + { + assert( !sLinkChainName.isEmpty() ); + + //there are many discarded duplicates in documents - no duplicates allowed in the list, so try to find the real one. + //if this LinkDisplayName/ChainName already exists on a different shape... + // the earlier processed duplicates are thrown out unless this one can be proved as bad. (last processed duplicate usually is stored) + auto linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(sLinkChainName); + if( linkFinder != m_rExport.m_aLinkedTextboxesHelper.end() ) + { + //If my NEXT/PREV targets have already been discovered, but don't match me, then assume I'm an abandoned remnant + // (this logic fails if both me and one of my links are duplicated, and the remnants were added first.) + linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sNextChain); + if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sPrevChain != sLinkChainName) ) + { + ++linkedTextboxesIter; + break; + } + + linkFinder = m_rExport.m_aLinkedTextboxesHelper.find(aLinkedTextboxInfo.sPrevChain); + if( (linkFinder != m_rExport.m_aLinkedTextboxesHelper.end()) && (linkFinder->second.sNextChain != sLinkChainName) ) + { + ++linkedTextboxesIter; + break; + } + } + m_rExport.m_bLinkedTextboxesHelperInitialized = false; + m_rExport.m_aLinkedTextboxesHelper[sLinkChainName] = aLinkedTextboxInfo; + } + } + ++linkedTextboxesIter; + } + + /* + #i2916# + May have an anchored graphic to be placed, loop through sorted array + and output all at this position + */ + while ( maFlyIter != maFlyFrames.end() ) + { + const SwPosition &rAnchor = maFlyIter->GetPosition(); + const sal_Int32 nPos = rAnchor.nContent.GetIndex(); + + if ( nPos != nSwPos ) + return FLY_NOT_PROCESSED ; // We haven't processed the fly + + const SdrObject* pSdrObj = maFlyIter->GetFrameFormat().FindRealSdrObject(); + + if (pSdrObj) + { + if (VMLExport::IsWaterMarkShape(pSdrObj->GetName())) + { + // This is a watermark object. Should be written ONLY in the header + if(m_rExport.m_nTextTyp == TXT_HDFT) + { + // Should write a watermark in the header + m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter ); + } + else + { + // Should not write watermark object in the main body text + } + } + else + { + // This is not a watermark object - write normally + m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter ); + } + } + else + { + // This is not a watermark object - write normally + m_rExport.AttrOutput().OutputFlyFrame( *maFlyIter ); + } + ++maFlyIter; + } + return ( m_rExport.AttrOutput().IsFlyProcessingPostponed() ? FLY_POSTPONED : FLY_PROCESSED ) ; +} + +bool SwWW8AttrIter::IsTextAttr( sal_Int32 nSwPos ) const +{ + // search for attrs with dummy character or content + if (const SwpHints* pTextAttrs = rNd.GetpSwpHints()) + { + for (size_t i = 0; i < pTextAttrs->Count(); ++i) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if (nSwPos == pHt->GetStart()) + { + if (pHt->HasDummyChar() || pHt->HasContent() ) + { + return true; + } + } + else if (nSwPos < pHt->GetStart()) + { + break; // sorted by start + } + } + } + + return false; +} + +bool SwWW8AttrIter::IsExportableAttr(sal_Int32 nSwPos) const +{ + if (const SwpHints* pTextAttrs = rNd.GetpSwpHints()) + { + for (size_t i = 0; i < pTextAttrs->Count(); ++i) + { + const SwTextAttr* pHt = pTextAttrs->GetSortedByEnd(i); + const sal_Int32 nStart = pHt->GetStart(); + const sal_Int32 nEnd = pHt->End() ? *pHt->End() : INT_MAX; + if (nSwPos >= nStart && nSwPos < nEnd) + { + switch (pHt->GetAttr().Which()) + { + // Metadata fields should be dynamically generated, not dumped as text. + case RES_TXTATR_METAFIELD: + return false; + } + } + } + } + + return true; +} + +bool SwWW8AttrIter::IsDropCap( int nSwPos ) +{ + // see if the current position falls on a DropCap + int nDropChars = mrSwFormatDrop.GetChars(); + bool bWholeWord = mrSwFormatDrop.GetWholeWord(); + if (bWholeWord) + { + const sal_Int32 nWordLen = rNd.GetDropLen(0); + if(nSwPos == nWordLen && nSwPos != 0) + return true; + } + else + { + if (nSwPos == nDropChars && nSwPos != 0) + return true; + } + return false; +} + +bool SwWW8AttrIter::RequiresImplicitBookmark() +{ + return std::any_of(m_rExport.m_aImplicitBookmarks.begin(), m_rExport.m_aImplicitBookmarks.end(), + [this](const aBookmarkPair& rBookmarkPair) { return rBookmarkPair.second == rNd.GetIndex(); }); +} + +//HasItem is for the summary of the double attributes: Underline and WordlineMode as TextItems. +// OutAttr () calls the output function, which can call HasItem() for other items at the attribute's start position. +// Only attributes with end can be queried. +// It searches with bDeep +const SfxPoolItem* SwWW8AttrIter::HasTextItem( sal_uInt16 nWhich ) const +{ + const SfxPoolItem* pRet = nullptr; + const SwpHints* pTextAttrs = rNd.GetpSwpHints(); + if (pTextAttrs && !m_rExport.m_aCurrentCharPropStarts.empty()) + { + const sal_Int32 nTmpSwPos = m_rExport.m_aCurrentCharPropStarts.top(); + for (size_t i = 0; i < pTextAttrs->Count(); ++i) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + const SfxPoolItem* pItem = &pHt->GetAttr(); + const sal_Int32 * pAtrEnd = nullptr; + if( nullptr != ( pAtrEnd = pHt->End() ) && // only Attr with an end + nTmpSwPos >= pHt->GetStart() && nTmpSwPos < *pAtrEnd ) + { + if ( nWhich == pItem->Which() ) + { + pRet = pItem; // found it + break; + } + else if( RES_TXTATR_INETFMT == pHt->Which() || + RES_TXTATR_CHARFMT == pHt->Which() || + RES_TXTATR_AUTOFMT == pHt->Which() ) + { + const SfxItemSet* pSet = CharFormat::GetItemSet( pHt->GetAttr() ); + const SfxPoolItem* pCharItem; + if ( pSet && + SfxItemState::SET == pSet->GetItemState( nWhich, pHt->Which() != RES_TXTATR_AUTOFMT, &pCharItem ) ) + { + pRet = pCharItem; // found it + break; + } + } + } + else if (nTmpSwPos < pHt->GetStart()) + break; // nothing more to come + } + } + return pRet; +} + +void WW8Export::GetCurrentItems(ww::bytes &rItems) const +{ + rItems.insert(rItems.end(), pO->begin(), pO->end()); +} + +const SfxPoolItem& SwWW8AttrIter::GetItem(sal_uInt16 nWhich) const +{ + const SfxPoolItem* pRet = HasTextItem(nWhich); + return pRet ? *pRet : rNd.SwContentNode::GetAttr(nWhich); +} + +void WW8AttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 /*nPos*/, const SwFormatRuby& rRuby ) +{ + WW8Ruby aWW8Ruby(rNode, rRuby, GetExport()); + OUString aStr( FieldString( ww::eEQ ) + "\\* jc" ); + aStr += OUString::number(aWW8Ruby.GetJC()) + " \\* \"Font:"; + aStr += aWW8Ruby.GetFontFamily() + "\" \\* hps"; + aStr += OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o"; + if (aWW8Ruby.GetDirective()) + { + aStr += OUStringLiteral("\\a") + OUStringChar(aWW8Ruby.GetDirective()); + } + aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "("; + aStr += rRuby.GetText() + ")"; + + // The parameter separator depends on the FIB.lid + if ( m_rWW8Export.pFib->getNumDecimalSep() == '.' ) + aStr += ","; + else + aStr += ";"; + + m_rWW8Export.OutputField( nullptr, ww::eEQ, aStr, + FieldFlags::Start | FieldFlags::CmdStart ); +} + +void WW8AttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) +{ + m_rWW8Export.WriteChar( ')' ); + m_rWW8Export.OutputField( nullptr, ww::eEQ, OUString(), FieldFlags::End | FieldFlags::Close ); +} + +/*#i15387# Better ideas welcome*/ +static OUString &TruncateBookmark( OUString &rRet ) +{ + if ( rRet.getLength() > 40 ) + rRet = rRet.copy( 0, 40 ); + OSL_ENSURE( rRet.getLength() <= 40, "Word cannot have bookmarks longer than 40 chars" ); + return rRet; +} + +OUString AttributeOutputBase::ConvertURL( const OUString& rUrl, bool bAbsoluteOut ) +{ + OUString sURL = rUrl; + + INetURLObject anAbsoluteParent(m_sBaseURL); + OUString sConvertedParent = INetURLObject::GetScheme( anAbsoluteParent.GetProtocol() ) + anAbsoluteParent.GetURLPath(); + OUString sParentPath = sConvertedParent.isEmpty() ? m_sBaseURL : sConvertedParent; + + if ( bAbsoluteOut ) + { + INetURLObject anAbsoluteNew; + + if ( anAbsoluteParent.GetNewAbsURL( rUrl, &anAbsoluteNew ) ) + sURL = anAbsoluteNew.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else + { + OUString sToConvert = rUrl.replaceAll( "\\", "/" ); + INetURLObject aURL( sToConvert ); + sToConvert = INetURLObject::GetScheme( aURL.GetProtocol() ) + aURL.GetURLPath(); + OUString sRelative = INetURLObject::GetRelURL( sParentPath, sToConvert, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::NONE ); + if ( !sRelative.isEmpty() ) + sURL = sRelative; + } + + return sURL; +} + +bool AttributeOutputBase::AnalyzeURL( const OUString& rUrl, const OUString& /*rTarget*/, OUString* pLinkURL, OUString* pMark ) +{ + bool bBookMarkOnly = false; + + OUString sMark; + OUString sURL; + + if ( rUrl.getLength() > 1 && rUrl[0] == '#' ) + { + sMark = BookmarkToWriter( rUrl.copy(1) ); + + const sal_Int32 nPos = sMark.lastIndexOf( cMarkSeparator ); + + const OUString sRefType(nPos>=0 && nPos+1<sMark.getLength() ? + sMark.copy(nPos+1).replaceAll(" ", "") : + OUString()); + + // #i21465# Only interested in outline references + if ( !sRefType.isEmpty() && + (sRefType == "outline" || sRefType == "graphic" || sRefType == "frame" || sRefType == "ole" || sRefType == "region" || sRefType == "table") ) + { + for ( const auto& rBookmarkPair : GetExport().m_aImplicitBookmarks ) + { + if ( rBookmarkPair.first == sMark ) + { + sMark = "_toc" + OUString::number( rBookmarkPair.second ); + break; + } + } + } + } + else + { + INetURLObject aURL( rUrl, INetProtocol::NotValid ); + sURL = aURL.GetURLNoMark( INetURLObject::DecodeMechanism::Unambiguous ); + sMark = aURL.GetMark( INetURLObject::DecodeMechanism::Unambiguous ); + INetProtocol aProtocol = aURL.GetProtocol(); + + if ( aProtocol == INetProtocol::File || aProtocol == INetProtocol::NotValid ) + { + // INetProtocol::NotValid - may be a relative link + bool bExportRelative = m_aSaveOpt.IsSaveRelFSys(); + sURL = ConvertURL( rUrl, !bExportRelative ); + } + } + + if ( !sMark.isEmpty() && sURL.isEmpty() ) + bBookMarkOnly = true; + + *pMark = sMark; + *pLinkURL = sURL; + return bBookMarkOnly; +} + +bool WW8AttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) +{ + bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark ); + + OUString sURL = *pLinkURL; + + if ( !sURL.isEmpty() ) + sURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), sURL ); + + if ( bBookMarkOnly ) + sURL = FieldString( ww::eHYPERLINK ); + else + sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\""; + + if ( !pMark->isEmpty() ) + sURL += " \\l \"" + *pMark + "\""; + + if ( !rTarget.isEmpty() ) + sURL += " \\n " + rTarget; + + *pLinkURL = sURL; + + return bBookMarkOnly; +} + +void WW8AttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) +{ + m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName)); + m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName)); +} + +bool WW8AttributeOutput::StartURL( const OUString &rUrl, const OUString &rTarget ) +{ + INetURLObject aURL( rUrl ); + OUString sURL; + OUString sMark; + + bool bBookMarkOnly = AnalyzeURL( rUrl, rTarget, &sURL, &sMark ); + + m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, sURL, FieldFlags::Start | FieldFlags::CmdStart ); + + // write the reference to the "picture" structure + sal_uLong nDataStt = m_rWW8Export.pDataStrm->Tell(); + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() ); + + // WinWord 2000 doesn't write this - so it's a temp solution by W97 ? + m_rWW8Export.WriteChar( 0x01 ); + + static sal_uInt8 aArr1[] = { + 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation + + 0x06, 0x08, 0x01, // sprmCFData + 0x55, 0x08, 0x01, // sprmCFSpec + 0x02, 0x08, 0x01 // sprmCFFieldVanish + }; + sal_uInt8* pDataAdr = aArr1 + 2; + Set_UInt32( pDataAdr, nDataStt ); + + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), sizeof( aArr1 ), aArr1 ); + + m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, sURL, FieldFlags::CmdEnd ); + + // now write the picture structure + sURL = aURL.GetURLNoMark(); + + // Compare the URL written by AnalyzeURL with the original one to see if + // the output URL is absolute or relative. + OUString sRelativeURL; + if ( !rUrl.isEmpty() ) + sRelativeURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), rUrl ); + bool bAbsolute = sRelativeURL == rUrl; + + static sal_uInt8 aURLData1[] = { + 0,0,0,0, // len of struct + 0x44,0, // the start of "next" data + 0,0,0,0,0,0,0,0,0,0, // PIC-Structure! + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | + 0,0,0,0, // / + }; + static sal_uInt8 MAGIC_A[] = { + // start of "next" data + 0xD0,0xC9,0xEA,0x79,0xF9,0xBA,0xCE,0x11, + 0x8C,0x82,0x00,0xAA,0x00,0x4B,0xA9,0x0B + }; + + m_rWW8Export.pDataStrm->WriteBytes(aURLData1, sizeof(aURLData1)); + /* Write HFD Structure */ + sal_uInt8 nAnchor = 0x00; + if ( !sMark.isEmpty() ) + nAnchor = 0x08; + m_rWW8Export.pDataStrm->WriteUChar(nAnchor); // HFDBits + m_rWW8Export.pDataStrm->WriteBytes(MAGIC_A, sizeof(MAGIC_A)); //clsid + + /* Write Hyperlink Object see [MS-OSHARED] spec*/ + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, 0x00000002); + sal_uInt32 nFlag = bBookMarkOnly ? 0 : 0x01; + if ( bAbsolute ) + nFlag |= 0x02; + if ( !sMark.isEmpty() ) + nFlag |= 0x08; + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, nFlag ); + + INetProtocol eProto = aURL.GetProtocol(); + if ( eProto == INetProtocol::File || eProto == INetProtocol::Smb ) + { + // version 1 (for a document) + + static sal_uInt8 MAGIC_C[] = { + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, + 0x00, 0x00 + }; + + static sal_uInt8 MAGIC_D[] = { + 0xFF, 0xFF, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + // save the links to files as relative + sURL = URIHelper::simpleNormalizedMakeRelative( m_rWW8Export.GetWriter().GetBaseURL(), sURL ); + if ( eProto == INetProtocol::File && sURL.startsWith( "/" ) ) + sURL = aURL.PathToFileName(); + + // special case for the absolute windows names + // (convert '/c:/foo/bar.doc' into 'c:\foo\bar.doc') + if (sURL.getLength()>=3) + { + const sal_Unicode aDrive = sURL[1]; + if ( sURL[0]=='/' && sURL[2]==':' && + ( (aDrive>='A' && aDrive<='Z' ) || (aDrive>='a' && aDrive<='z') ) ) + { + sURL = sURL.copy(1).replaceAll("/", "\\"); + } + } + + // n#261623 convert smb notation to '\\' + const char pSmb[] = "smb://"; + if ( eProto == INetProtocol::Smb && sURL.startsWith( pSmb ) ) + { + sURL = sURL.copy( sizeof(pSmb)-3 ).replaceAll( "/", "\\" ); + } + + m_rWW8Export.pDataStrm->WriteBytes(MAGIC_C, sizeof(MAGIC_C)); + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, sURL.getLength()+1 ); + SwWW8Writer::WriteString8( *m_rWW8Export.pDataStrm, sURL, true, + RTL_TEXTENCODING_MS_1252 ); + m_rWW8Export.pDataStrm->WriteBytes(MAGIC_D, sizeof(MAGIC_D)); + + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, 2*sURL.getLength() + 6 ); + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, 2*sURL.getLength() ); + SwWW8Writer::WriteShort( *m_rWW8Export.pDataStrm, 3 ); + SwWW8Writer::WriteString16( *m_rWW8Export.pDataStrm, sURL, false ); + } + else if ( eProto != INetProtocol::NotValid ) + { + // version 2 (simple url) + // and write some data to the data stream, but don't ask + // what the data mean, except for the URL. + // The First piece is the WW8_PIC structure. + static sal_uInt8 MAGIC_B[] = { + 0xE0,0xC9,0xEA,0x79,0xF9,0xBA,0xCE,0x11, + 0x8C,0x82,0x00,0xAA,0x00,0x4B,0xA9,0x0B + }; + + m_rWW8Export.pDataStrm->WriteBytes(MAGIC_B, sizeof(MAGIC_B)); + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, 2 * ( sURL.getLength() + 1 ) ); + SwWW8Writer::WriteString16( *m_rWW8Export.pDataStrm, sURL, true ); + } + + if ( !sMark.isEmpty() ) + { + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, sMark.getLength()+1 ); + SwWW8Writer::WriteString16( *m_rWW8Export.pDataStrm, sMark, true ); + } + SwWW8Writer::WriteLong( *m_rWW8Export.pDataStrm, nDataStt, + m_rWW8Export.pDataStrm->Tell() - nDataStt ); + + return true; +} + +bool WW8AttributeOutput::EndURL(bool const) +{ + m_rWW8Export.OutputField( nullptr, ww::eHYPERLINK, OUString(), FieldFlags::Close ); + + return true; +} + +OUString BookmarkToWord(const OUString &rBookmark) +{ + OUString sRet(INetURLObject::encode( + rBookmark.replace(' ', '_'), // Spaces are prohibited in bookmark name + INetURLObject::PART_REL_SEGMENT_EXTRA, + INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_ASCII_US)); + // Unicode letters are allowed + sRet = INetURLObject::decode(sRet, INetURLObject::DecodeMechanism::Unambiguous, RTL_TEXTENCODING_UTF8); + return TruncateBookmark(sRet); +} + +OUString BookmarkToWriter(const OUString &rBookmark) +{ + return INetURLObject::decode(rBookmark, + INetURLObject::DecodeMechanism::Unambiguous, RTL_TEXTENCODING_ASCII_US); +} + +void SwWW8AttrIter::OutSwFormatRefMark(const SwFormatRefMark& rAttr) +{ + if(m_rExport.HasRefToAttr(rAttr.GetRefName())) + m_rExport.AppendBookmark( MSWordExportBase::GetBookmarkName( REF_SETREFATTR, + &rAttr.GetRefName(), 0 )); +} + +void SwWW8AttrIter::SplitRun( sal_Int32 nSplitEndPos ) +{ + auto aIter = std::find_if(maCharRuns.begin(), maCharRuns.end(), + [nSplitEndPos](const CharRunEntry& rCharRun) { return rCharRun.mnEndPos >= nSplitEndPos; }); + if (aIter == maCharRuns.end() || aIter->mnEndPos == nSplitEndPos) + return; + + CharRunEntry aNewEntry = *aIter; + aIter->mnEndPos = nSplitEndPos; + maCharRuns.insert( ++aIter, aNewEntry); + maCharRunIter = maCharRuns.begin(); + IterToCurrent(); + nCurrentSwPos = SearchNext(1); +} + +void WW8AttributeOutput::FieldVanish( const OUString& rText, ww::eField /*eType*/ ) +{ + ww::bytes aItems; + m_rWW8Export.GetCurrentItems( aItems ); + + // sprmCFFieldVanish + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFFldVanish ); + aItems.push_back( 1 ); + + sal_uInt16 nStt_sprmCFSpec = aItems.size(); + + // sprmCFSpec -- fSpec-Attribut true + SwWW8Writer::InsUInt16( aItems, 0x855 ); + aItems.push_back( 1 ); + + m_rWW8Export.WriteChar( '\x13' ); + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), aItems.size(), + aItems.data() ); + m_rWW8Export.OutSwString(rText, 0, rText.getLength()); + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), nStt_sprmCFSpec, + aItems.data() ); + m_rWW8Export.WriteChar( '\x15' ); + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), aItems.size(), + aItems.data() ); +} + +void AttributeOutputBase::TOXMark( const SwTextNode& rNode, const SwTOXMark& rAttr ) +{ + // it's a field; so get the Text from the Node and build the field + OUString sText; + ww::eField eType = ww::eNONE; + + const SwTextTOXMark& rTextTOXMark = *rAttr.GetTextTOXMark(); + const sal_Int32* pTextEnd = rTextTOXMark.End(); + if ( pTextEnd ) // has range? + { + sText = rNode.GetExpandText(nullptr, rTextTOXMark.GetStart(), + *pTextEnd - rTextTOXMark.GetStart() ); + } + else + sText = rAttr.GetAlternativeText(); + + switch ( rAttr.GetTOXType()->GetType() ) + { + case TOX_INDEX: + eType = ww::eXE; + if ( !rAttr.GetPrimaryKey().isEmpty() ) + { + if ( !rAttr.GetSecondaryKey().isEmpty() ) + { + sText = rAttr.GetSecondaryKey() + ":" + sText; + } + + sText = rAttr.GetPrimaryKey() + ":" + sText; + } + sText = " XE \"" + sText + "\" "; + break; + + case TOX_USER: + sText += "\" \\f \"" + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *rAttr.GetTOXType() ) )); + [[fallthrough]]; + case TOX_CONTENT: + { + eType = ww::eTC; + sText = " TC \"" + sText; + sal_uInt16 nLvl = rAttr.GetLevel(); + if (nLvl > WW8ListManager::nMaxLevel) + nLvl = WW8ListManager::nMaxLevel; + + sText += "\" \\l " + OUString::number(nLvl) + " "; + } + break; + default: + OSL_ENSURE( false, "Unhandled option for toc export" ); + break; + } + + if (!sText.isEmpty()) + FieldVanish( sText, eType ); +} + +int SwWW8AttrIter::OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos) +{ + int nRet = 0; + if ( const SwpHints* pTextAttrs = rNd.GetpSwpHints() ) + { + m_rExport.m_aCurrentCharPropStarts.push( nPos ); + const sal_Int32* pEnd; + // first process ends of attributes with extent + for (size_t i = 0; i < pTextAttrs->Count(); ++i) + { + const SwTextAttr* pHt = pTextAttrs->GetSortedByEnd(i); + const SfxPoolItem* pItem = &pHt->GetAttr(); + switch ( pItem->Which() ) + { + case RES_TXTATR_INETFMT: + pEnd = pHt->End(); + if (nPos == *pEnd && nPos != pHt->GetStart()) + { + if (m_rExport.AttrOutput().EndURL(nPos == rNd.Len())) + --nRet; + } + break; + case RES_TXTATR_REFMARK: + pEnd = pHt->End(); + if (nullptr != pEnd && nPos == *pEnd && nPos != pHt->GetStart()) + { + OutSwFormatRefMark(*static_cast<const SwFormatRefMark*>(pItem)); + --nRet; + } + break; + case RES_TXTATR_CJK_RUBY: + pEnd = pHt->End(); + if (nPos == *pEnd && nPos != pHt->GetStart()) + { + m_rExport.AttrOutput().EndRuby(rNode, nPos); + --nRet; + } + break; + } + if (nPos < pHt->GetAnyEnd()) + break; // sorted by end + } + for ( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + const SfxPoolItem* pItem = &pHt->GetAttr(); + switch ( pItem->Which() ) + { + case RES_TXTATR_INETFMT: + if ( nPos == pHt->GetStart() ) + { + const SwFormatINetFormat *rINet = static_cast< const SwFormatINetFormat* >( pItem ); + if ( m_rExport.AttrOutput().StartURL( rINet->GetValue(), rINet->GetTargetFrame() ) ) + ++nRet; + } + pEnd = pHt->End(); + if (nPos == *pEnd && nPos == pHt->GetStart()) + { // special case: empty must be handled here + if (m_rExport.AttrOutput().EndURL(nPos == rNd.Len())) + --nRet; + } + break; + case RES_TXTATR_REFMARK: + if ( nPos == pHt->GetStart() ) + { + OutSwFormatRefMark( *static_cast< const SwFormatRefMark* >( pItem ) ); + ++nRet; + } + pEnd = pHt->End(); + if (nullptr != pEnd && nPos == *pEnd && nPos == pHt->GetStart()) + { // special case: empty TODO: is this possible or would empty one have pEnd null? + OutSwFormatRefMark( *static_cast< const SwFormatRefMark* >( pItem ) ); + --nRet; + } + break; + case RES_TXTATR_TOXMARK: + if ( nPos == pHt->GetStart() ) + m_rExport.AttrOutput().TOXMark( rNd, *static_cast< const SwTOXMark* >( pItem ) ); + break; + case RES_TXTATR_CJK_RUBY: + if ( nPos == pHt->GetStart() ) + { + m_rExport.AttrOutput().StartRuby( rNd, nPos, *static_cast< const SwFormatRuby* >( pItem ) ); + ++nRet; + } + pEnd = pHt->End(); + if (nPos == *pEnd && nPos == pHt->GetStart()) + { // special case: empty must be handled here + m_rExport.AttrOutput().EndRuby( rNd, nPos ); + --nRet; + } + break; + } + if (nPos < pHt->GetStart()) + break; // sorted by start + } + m_rExport.m_aCurrentCharPropStarts.pop(); // HasTextItem only allowed in the above range + } + return nRet; +} + +bool SwWW8AttrIter::IncludeEndOfParaCRInRedlineProperties( sal_Int32 nEnd ) const +{ + // search next Redline + for( SwRedlineTable::size_type nPos = nCurRedlinePos; + nPos < m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); ++nPos ) + { + const SwRangeRedline *pRange = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()[nPos]; + const SwPosition* pEnd = pRange->End(); + const SwPosition* pStart = pRange->Start(); + bool bBreak = true; + // In word the paragraph end marker is a real character, in writer it is not. + // Here we find out if the para end marker we will emit is affected by + // redlining, in which case it must be included by the range of character + // attributes that contains the redlining information. + if (pEnd->nNode == rNd) + { + if (pEnd->nContent.GetIndex() == nEnd) + { + // This condition detects if the pseudo-char we will export + // should be explicitly included by the redlining char + // properties on this node because the redlining ends right + // after it + return true; + } + bBreak = false; + } + if (pStart->nNode == rNd) + { + if (pStart->nContent.GetIndex() == nEnd) + { + // This condition detects if the pseudo-char we will export + // should be explicitly included by the redlining char + // properties on this node because the redlining starts right + // before it + return true; + } + bBreak = false; + } + if (pStart->nNode.GetIndex()-1 == rNd.GetIndex()) + { + if (pStart->nContent.GetIndex() == 0) + { + // This condition detects if the pseudo-char we will export + // should be implicitly excluded by the redlining char + // properties starting on the next node. + return true; + } + bBreak = false; + } + + if (bBreak) + break; + } + return false; +} + +const SwRedlineData* SwWW8AttrIter::GetParagraphLevelRedline( ) +{ + pCurRedline = nullptr; + + // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos' + for(SwRangeRedline* pRedl : m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()) + { + const SwPosition* pCheckedStt = pRedl->Start(); + + if( pCheckedStt->nNode == rNd ) + { + // Maybe add here a check that also the start & end of the redline is the entire paragraph + + // Only return if this is a paragraph formatting redline + if (pRedl->GetType() == RedlineType::ParagraphFormat) + { + // write data of this redline + pCurRedline = pRedl; + return &( pCurRedline->GetRedlineData() ); + } + } + } + return nullptr; +} + +const SwRedlineData* SwWW8AttrIter::GetRunLevelRedline( sal_Int32 nPos ) +{ + if( pCurRedline ) + { + const SwPosition* pEnd = pCurRedline->End(); + if (!(pEnd->nNode == rNd && pEnd->nContent.GetIndex() <= nPos)) + { + switch( pCurRedline->GetType() ) + { + case RedlineType::Insert: + case RedlineType::Delete: + case RedlineType::Format: + // write data of this redline + return &( pCurRedline->GetRedlineData() ); + break; + default: + break; + } + } + pCurRedline = nullptr; + ++nCurRedlinePos; + } + + assert(!pCurRedline); + // search next Redline + for( ; nCurRedlinePos < m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); + ++nCurRedlinePos ) + { + const SwRangeRedline* pRedl = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ nCurRedlinePos ]; + + const SwPosition* pStt = pRedl->Start(); + const SwPosition* pEnd = pStt == pRedl->GetPoint() + ? pRedl->GetMark() + : pRedl->GetPoint(); + + if( pStt->nNode == rNd ) + { + if( pStt->nContent.GetIndex() >= nPos ) + { + if( pStt->nContent.GetIndex() == nPos ) + { + switch( pRedl->GetType() ) + { + case RedlineType::Insert: + case RedlineType::Delete: + case RedlineType::Format: + // write data of this redline + pCurRedline = pRedl; + return &( pCurRedline->GetRedlineData() ); + break; + default: + break; + } + } + break; + } + } + else + { + break; + } + + if( pEnd->nNode == rNd && + pEnd->nContent.GetIndex() < nPos ) + { + pCurRedline = pRedl; + break; + } + } + return nullptr; +} + +SvxFrameDirection MSWordExportBase::GetCurrentPageDirection() const +{ + const SwFrameFormat &rFormat = m_pCurrentPageDesc + ? m_pCurrentPageDesc->GetMaster() + : m_pDoc->GetPageDesc( 0 ).GetMaster(); + return rFormat.GetFrameDir().GetValue(); +} + +SvxFrameDirection MSWordExportBase::GetDefaultFrameDirection( ) const +{ + SvxFrameDirection nDir = SvxFrameDirection::Environment; + + if ( m_bOutPageDescs ) + nDir = GetCurrentPageDirection( ); + else if ( m_pOutFormatNode ) + { + if ( m_bOutFlyFrameAttrs ) //frame + { + nDir = TrueFrameDirection( *static_cast< const SwFrameFormat * >(m_pOutFormatNode) ); + } + else if ( dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) != nullptr ) //paragraph + { + const SwContentNode *pNd = static_cast<const SwContentNode *>(m_pOutFormatNode); + SwPosition aPos( *pNd ); + nDir = m_pDoc->GetTextDirection( aPos ); + } + else if ( dynamic_cast< const SwTextFormatColl *>( m_pOutFormatNode ) != nullptr ) + { + if ( MsLangId::isRightToLeft( GetAppLanguage()) ) + nDir = SvxFrameDirection::Horizontal_RL_TB; + else + nDir = SvxFrameDirection::Horizontal_LR_TB; //what else can we do :-( + } + } + + if ( nDir == SvxFrameDirection::Environment ) + { + // fdo#44029 put direction right when the locale are RTL. + if( MsLangId::isRightToLeft( GetAppLanguage()) ) + nDir = SvxFrameDirection::Horizontal_RL_TB; + else + nDir = SvxFrameDirection::Horizontal_LR_TB; //Set something + } + + return nDir; +} + +SvxFrameDirection MSWordExportBase::TrueFrameDirection( const SwFrameFormat &rFlyFormat ) const +{ + const SwFrameFormat *pFlyFormat = &rFlyFormat; + const SvxFrameDirectionItem* pItem = nullptr; + 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; + } + + SvxFrameDirection nRet; + if ( pItem ) + nRet = pItem->GetValue(); + else + nRet = GetCurrentPageDirection(); + + OSL_ENSURE( nRet != SvxFrameDirection::Environment, "leaving with environment direction" ); + return nRet; +} + +const SvxBrushItem* WW8Export::GetCurrentPageBgBrush() const +{ + const SwFrameFormat &rFormat = m_pCurrentPageDesc + ? m_pCurrentPageDesc->GetMaster() + : m_pDoc->GetPageDesc(0).GetMaster(); + + const SfxPoolItem* pItem = nullptr; + //If not set, or "no fill", get real bg + SfxItemState eState = rFormat.GetItemState(RES_BACKGROUND, true, &pItem); + + const SvxBrushItem* pRet = static_cast<const SvxBrushItem*>(pItem); + if (SfxItemState::SET != eState || !pRet || (!pRet->GetGraphic() && + pRet->GetColor() == COL_TRANSPARENT)) + { + pRet = &(DefaultItemGet<SvxBrushItem>(*m_pDoc,RES_BACKGROUND)); + } + return pRet; +} + +std::shared_ptr<SvxBrushItem> WW8Export::TrueFrameBgBrush(const SwFrameFormat &rFlyFormat) const +{ + const SwFrameFormat *pFlyFormat = &rFlyFormat; + const SvxBrushItem* pRet = nullptr; + + while (pFlyFormat) + { + //If not set, or "no fill", get real bg + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = + pFlyFormat->GetItemState(RES_BACKGROUND, true, &pItem); + pRet = static_cast<const SvxBrushItem*>(pItem); + if (SfxItemState::SET != eState || !pRet || (!pRet->GetGraphic() && + pRet->GetColor() == COL_TRANSPARENT)) + { + pRet = 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 (!pRet) + pRet = GetCurrentPageBgBrush(); + + const Color aTmpColor( COL_WHITE ); + std::shared_ptr<SvxBrushItem> aRet(std::make_shared<SvxBrushItem>(aTmpColor, RES_BACKGROUND)); + + if (pRet && (pRet->GetGraphic() ||( pRet->GetColor() != COL_TRANSPARENT))) + { + aRet.reset(pRet->Clone()); + } + + return aRet; +} + +/* +Convert characters that need to be converted, the basic replacements and the +ridiculously complicated title case attribute mapping to hardcoded upper case +because word doesn't have the feature +*/ +OUString SwWW8AttrIter::GetSnippet(const OUString &rStr, sal_Int32 nCurrentPos, + sal_Int32 nLen) const +{ + if (!nLen) + return OUString(); + + OUString aSnippet(rStr.copy(nCurrentPos, nLen)); + // 0x0a ( Hard Line Break ) -> 0x0b + // 0xad ( soft hyphen ) -> 0x1f + // 0x2011 ( hard hyphen ) -> 0x1e + aSnippet = aSnippet.replace(0x0A, 0x0B); + aSnippet = aSnippet.replace(CHAR_HARDHYPHEN, 0x1e); + aSnippet = aSnippet.replace(CHAR_SOFTHYPHEN, 0x1f); + + m_rExport.m_aCurrentCharPropStarts.push( nCurrentPos ); + const SfxPoolItem &rItem = GetItem(RES_CHRATR_CASEMAP); + + if (SvxCaseMap::Capitalize == static_cast<const SvxCaseMapItem&>(rItem).GetValue()) + { + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + sal_uInt16 nScriptType = g_pBreakIt->GetBreakIter()->getScriptType(aSnippet, 0); + + LanguageType nLanguage; + switch (nScriptType) + { + case i18n::ScriptType::ASIAN: + nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_CJK_LANGUAGE)).GetLanguage(); + break; + case i18n::ScriptType::COMPLEX: + nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_CTL_LANGUAGE)).GetLanguage(); + break; + case i18n::ScriptType::LATIN: + default: + nLanguage = static_cast<const SvxLanguageItem&>(GetItem(RES_CHRATR_LANGUAGE)).GetLanguage(); + break; + } + + SvxFont aFontHelper; + aFontHelper.SetCaseMap(SvxCaseMap::Capitalize); + aFontHelper.SetLanguage(nLanguage); + aSnippet = aFontHelper.CalcCaseMap(aSnippet); + + //If we weren't at the begin of a word undo the case change. + //not done before doing the casemap because the sequence might start + //with whitespace + if (!g_pBreakIt->GetBreakIter()->isBeginWord( + rStr, nCurrentPos, g_pBreakIt->GetLocale(nLanguage), + i18n::WordType::ANYWORD_IGNOREWHITESPACES ) ) + { + aSnippet = OUStringChar(rStr[nCurrentPos]) + aSnippet.copy(1); + } + } + m_rExport.m_aCurrentCharPropStarts.pop(); + + return aSnippet; +} + +/** Delivers the right paragraph style + + Because of the different style handling for delete operations, + the track changes have to be analysed. A deletion, starting in paragraph A + with style A, ending in paragraph B with style B, needs a hack. +*/ +static SwTextFormatColl& lcl_getFormatCollection( MSWordExportBase& rExport, const SwTextNode* pTextNode ) +{ + SwRedlineTable::size_type nPos = 0; + SwRedlineTable::size_type nMax = rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); + while( nPos < nMax ) + { + const SwRangeRedline* pRedl = rExport.m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ nPos++ ]; + const SwPosition* pStt = pRedl->Start(); + const SwPosition* pEnd = pStt == pRedl->GetPoint() + ? pRedl->GetMark() + : pRedl->GetPoint(); + // Looking for deletions, which ends in current pTextNode + if( RedlineType::Delete == pRedl->GetRedlineData().GetType() && + pEnd->nNode == *pTextNode && pStt->nNode != *pTextNode && + pStt->nNode.GetNode().IsTextNode() ) + { + pTextNode = pStt->nNode.GetNode().GetTextNode(); + nMax = nPos; + nPos = 0; + } + } + return static_cast<SwTextFormatColl&>( pTextNode->GetAnyFormatColl() ); +} + +void WW8AttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop &rSwFormatDrop, sal_uInt16 nStyle, + ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) +{ + short nDropLines = rSwFormatDrop.GetLines(); + short nDistance = rSwFormatDrop.GetDistance(); + int rFontHeight, rDropHeight, rDropDescent; + + SVBT16 nSty; + ShortToSVBT16( nStyle, nSty ); + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // Style # + + m_rWW8Export.InsUInt16( NS_sprm::sprmPPc ); // Alignment (sprmPPc) + m_rWW8Export.pO->push_back( 0x20 ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmPWr ); // Wrapping (sprmPWr) + m_rWW8Export.pO->push_back( 0x02 ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmPDcs ); // Dropcap (sprmPDcs) + int nDCS = ( nDropLines << 3 ) | 0x01; + m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( nDCS ) ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmPDxaFromText ); // Distance from text (sprmPDxaFromText) + m_rWW8Export.InsUInt16( nDistance ); + + if ( rNode.GetDropSize( rFontHeight, rDropHeight, rDropDescent ) ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaLine ); // Line spacing + m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( -rDropHeight ) ); + m_rWW8Export.InsUInt16( 0 ); + } + + m_rWW8Export.WriteCR( pTextNodeInfoInner ); + + if ( pTextNodeInfo.get() != nullptr ) + { +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pTextNodeInfo->toString()); +#endif + TableInfoCell( pTextNodeInfoInner ); + } + + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + m_rWW8Export.pO->clear(); + + if ( rNode.GetDropSize( rFontHeight, rDropHeight, rDropDescent ) ) + { + const SwCharFormat *pSwCharFormat = rSwFormatDrop.GetCharFormat(); + if ( pSwCharFormat ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCIstd ); + m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pSwCharFormat ) ); + } + + m_rWW8Export.InsUInt16( NS_sprm::sprmCHpsPos ); // Lower the chars + m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( -((nDropLines - 1)*rDropDescent) / 10 ) ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmCHps ); // Font Size + m_rWW8Export.InsUInt16( static_cast< sal_uInt16 >( rFontHeight / 10 ) ); + } + + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + m_rWW8Export.pO->clear(); +} + +sal_Int32 MSWordExportBase::GetNextPos( SwWW8AttrIter const * aAttrIter, const SwTextNode& rNode, sal_Int32 nCurrentPos ) +{ + // Get the bookmarks for the normal run + const sal_Int32 nNextPos = aAttrIter->WhereNext(); + sal_Int32 nNextBookmark = nNextPos; + sal_Int32 nNextAnnotationMark = nNextPos; + + if( nNextBookmark > nCurrentPos ) //no need to search for bookmarks otherwise (checked in UpdatePosition()) + { + GetSortedBookmarks( rNode, nCurrentPos, nNextBookmark - nCurrentPos ); + NearestBookmark( nNextBookmark, nCurrentPos, false ); + GetSortedAnnotationMarks(*aAttrIter, nCurrentPos, nNextAnnotationMark - nCurrentPos); + NearestAnnotationMark( nNextAnnotationMark, nCurrentPos, false ); + } + return std::min( nNextPos, std::min( nNextBookmark, nNextAnnotationMark ) ); +} + +void MSWordExportBase::UpdatePosition( SwWW8AttrIter* aAttrIter, sal_Int32 nCurrentPos ) +{ + sal_Int32 nNextPos; + + // go to next attribute if no bookmark is found or if the bookmark is after the next attribute position + // It may happened that the WhereNext() wasn't used in the previous increment because there was a + // bookmark before it. Use that position before trying to find another one. + bool bNextBookmark = NearestBookmark( nNextPos, nCurrentPos, true ); + if( nCurrentPos == aAttrIter->WhereNext() && ( !bNextBookmark || nNextPos > aAttrIter->WhereNext() ) ) + aAttrIter->NextPos(); +} + +bool MSWordExportBase::GetBookmarks( const SwTextNode& rNd, sal_Int32 nStt, + sal_Int32 nEnd, IMarkVector& rArr ) +{ + IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + sal_uLong nNd = rNd.GetIndex( ); + + const sal_Int32 nMarks = pMarkAccess->getAllMarksCount(); + for ( sal_Int32 i = 0; i < nMarks; i++ ) + { + IMark* pMark = pMarkAccess->getAllMarksBegin()[i]; + + switch (IDocumentMarkAccess::GetType( *pMark )) + { + case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: + case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: + case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: + case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: + case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: + case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: + case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: + case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: + continue; // ignore irrelevant marks + case IDocumentMarkAccess::MarkType::BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: + case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: + break; + } + + // Only keep the bookmarks starting or ending in this node + if ( pMark->GetMarkStart().nNode == nNd || + pMark->GetMarkEnd().nNode == nNd ) + { + const sal_Int32 nBStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nBEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + // Keep only the bookmarks starting or ending in the snippet + bool bIsStartOk = ( pMark->GetMarkStart().nNode == nNd ) && ( nBStart >= nStt ) && ( nBStart <= nEnd ); + bool bIsEndOk = ( pMark->GetMarkEnd().nNode == nNd ) && ( nBEnd >= nStt ) && ( nBEnd <= nEnd ); + + if ( bIsStartOk || bIsEndOk ) + { + rArr.push_back( pMark ); + } + } + } + return ( !rArr.empty() ); +} + +bool MSWordExportBase::GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt, + sal_Int32 nEnd, IMarkVector& rArr ) +{ + IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + sal_uLong nNd = rAttrs.GetNode().GetIndex(); + + const sal_Int32 nMarks = pMarkAccess->getAnnotationMarksCount(); + for ( sal_Int32 i = 0; i < nMarks; i++ ) + { + IMark* pMark = pMarkAccess->getAnnotationMarksBegin()[i]; + + // Only keep the bookmarks starting or ending in this node + if ( pMark->GetMarkStart().nNode == nNd || + pMark->GetMarkEnd().nNode == nNd ) + { + const sal_Int32 nBStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nBEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + // Keep only the bookmarks starting or ending in the snippet + bool bIsStartOk = ( pMark->GetMarkStart().nNode == nNd ) && ( nBStart >= nStt ) && ( nBStart <= nEnd ); + bool bIsEndOk = ( pMark->GetMarkEnd().nNode == nNd ) && ( nBEnd >= nStt ) && ( nBEnd <= nEnd ); + + // Annotation marks always have at least one character: the anchor + // point of the comment field. In this case Word wants only the + // comment field, so ignore the annotation mark itself. + bool bSingleChar = pMark->GetMarkStart().nNode == pMark->GetMarkEnd().nNode && nBStart + 1 == nBEnd; + + if (bSingleChar) + { + if (rAttrs.HasFlysAt(nBStart)) + { + // There is content (an at-char anchored frame) between the annotation mark + // start/end, so still emit range start/end. + bSingleChar = false; + } + } + + if ( ( bIsStartOk || bIsEndOk ) && !bSingleChar ) + { + rArr.push_back( pMark ); + } + } + } + return ( !rArr.empty() ); +} + +namespace { + +class CompareMarksEnd +{ +public: + bool operator() ( const IMark * pOneB, const IMark * pTwoB ) const + { + const sal_Int32 nOEnd = pOneB->GetMarkEnd().nContent.GetIndex(); + const sal_Int32 nTEnd = pTwoB->GetMarkEnd().nContent.GetIndex(); + + return nOEnd < nTEnd; + } +}; + +} + +bool MSWordExportBase::NearestBookmark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly ) +{ + bool bHasBookmark = false; + + if ( !m_rSortedBookmarksStart.empty() ) + { + IMark* pMarkStart = m_rSortedBookmarksStart.front(); + const sal_Int32 nNext = pMarkStart->GetMarkStart().nContent.GetIndex(); + if( !bNextPositionOnly || (nNext > nCurrentPos )) + { + rNearest = nNext; + bHasBookmark = true; + } + } + + if ( !m_rSortedBookmarksEnd.empty() ) + { + IMark* pMarkEnd = m_rSortedBookmarksEnd[0]; + const sal_Int32 nNext = pMarkEnd->GetMarkEnd().nContent.GetIndex(); + if( !bNextPositionOnly || nNext > nCurrentPos ) + { + if ( !bHasBookmark ) + rNearest = nNext; + else + rNearest = std::min( rNearest, nNext ); + bHasBookmark = true; + } + } + + return bHasBookmark; +} + +void MSWordExportBase::NearestAnnotationMark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly ) +{ + bool bHasAnnotationMark = false; + + if ( !m_rSortedAnnotationMarksStart.empty() ) + { + IMark* pMarkStart = m_rSortedAnnotationMarksStart.front(); + const sal_Int32 nNext = pMarkStart->GetMarkStart().nContent.GetIndex(); + if( !bNextPositionOnly || (nNext > nCurrentPos )) + { + rNearest = nNext; + bHasAnnotationMark = true; + } + } + + if ( !m_rSortedAnnotationMarksEnd.empty() ) + { + IMark* pMarkEnd = m_rSortedAnnotationMarksEnd[0]; + const sal_Int32 nNext = pMarkEnd->GetMarkEnd().nContent.GetIndex(); + if( !bNextPositionOnly || nNext > nCurrentPos ) + { + if ( !bHasAnnotationMark ) + rNearest = nNext; + else + rNearest = std::min( rNearest, nNext ); + } + } +} + +void MSWordExportBase::GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) +{ + IMarkVector aMarksStart; + if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarksStart)) + { + IMarkVector aSortedEnd; + IMarkVector aSortedStart; + for ( IMark* pMark : aMarksStart ) + { + // Remove the positions equal to the current pos + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + const SwTextNode& rNode = rAttrs.GetNode(); + if ( nStart > nCurrentPos && ( pMark->GetMarkStart().nNode == rNode.GetIndex()) ) + aSortedStart.push_back( pMark ); + + if ( nEnd > nCurrentPos && nEnd <= ( nCurrentPos + nLen ) && (pMark->GetMarkEnd().nNode == rNode.GetIndex()) ) + aSortedEnd.push_back( pMark ); + } + + // Sort the bookmarks by end position + std::sort( aSortedEnd.begin(), aSortedEnd.end(), CompareMarksEnd() ); + + m_rSortedAnnotationMarksStart.swap( aSortedStart ); + m_rSortedAnnotationMarksEnd.swap( aSortedEnd ); + } + else + { + m_rSortedAnnotationMarksStart.clear( ); + m_rSortedAnnotationMarksEnd.clear( ); + } +} + +void MSWordExportBase::GetSortedBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen ) +{ + IMarkVector aMarksStart; + if ( GetBookmarks( rNode, nCurrentPos, nCurrentPos + nLen, aMarksStart ) ) + { + IMarkVector aSortedEnd; + IMarkVector aSortedStart; + for ( IMark* pMark : aMarksStart ) + { + // Remove the positions equal to the current pos + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex(); + + if ( nStart > nCurrentPos && ( pMark->GetMarkStart().nNode == rNode.GetIndex()) ) + aSortedStart.push_back( pMark ); + + if ( nEnd > nCurrentPos && nEnd <= ( nCurrentPos + nLen ) && (pMark->GetMarkEnd().nNode == rNode.GetIndex()) ) + aSortedEnd.push_back( pMark ); + } + + // Sort the bookmarks by end position + std::sort( aSortedEnd.begin(), aSortedEnd.end(), CompareMarksEnd() ); + + m_rSortedBookmarksStart.swap( aSortedStart ); + m_rSortedBookmarksEnd.swap( aSortedEnd ); + } + else + { + m_rSortedBookmarksStart.clear( ); + m_rSortedBookmarksEnd.clear( ); + } +} + +bool MSWordExportBase::NeedSectionBreak( const SwNode& rNd ) const +{ + if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs || m_pCurrentPageDesc == nullptr ) + return false; + + const SwPageDesc * pPageDesc = rNd.FindPageDesc()->GetFollow(); + + if (m_pCurrentPageDesc != pPageDesc) + { + if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster())) + { + return true; + } + } + + return false; +} + +bool MSWordExportBase::NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreakList& pList ) const +{ + SwSoftPageBreakList tmp; + rNd.fillSoftPageBreakList(tmp); + // hack: move the break behind any field marks; currently we can't hide the + // field mark instruction so the layout position is quite meaningless + IDocumentMarkAccess const& rIDMA(*rNd.GetDoc()->getIDocumentMarkAccess()); + sal_Int32 pos(-1); + for (auto const& it : tmp) + { + if (pos < it) // previous one might have skipped over it + { + pos = it; + while (auto const*const pMark = rIDMA.getFieldmarkFor(SwPosition(const_cast<SwTextNode&>(rNd), pos))) + { + if (pMark->GetMarkEnd().nNode != rNd) + { + pos = rNd.Len(); // skip everything + break; + } + pos = pMark->GetMarkEnd().nContent.GetIndex(); // no +1, it's behind the char + } + pList.insert(pos); + } + } + pList.insert(0); + pList.insert( rNd.GetText().getLength() ); + return pList.size() > 2 && NeedSectionBreak( rNd ); +} + +void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) +{ + SAL_INFO( "sw.ww8", "<OutWW8_SwTextNode>" ); + + ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo( m_pTableInfo->getTableNodeInfo( &rNode ) ); + + //For i120928,identify the last node + bool bLastCR = false; + bool bExported = false; + { + SwNodeIndex aNextIdx(rNode,1); + SwNodeIndex aLastIdx(rNode.GetNodes().GetEndOfContent()); + if (aNextIdx == aLastIdx) + bLastCR = true; + } + + // In order to make sure watermark is stored in 'header.xml', check nTextTyp. + // if it is document.xml, don't write the tags (watermark should be only in the 'header') + SwWW8AttrIter aWatermarkAttrIter( *this, rNode ); + if (( TXT_HDFT != m_nTextTyp) && aWatermarkAttrIter.IsWatermarkFrame()) + { + return; + } + + bool bFlyInTable = m_pParentFrame && IsInTable(); + + SwTextFormatColl& rTextColl = lcl_getFormatCollection( *this, &rNode ); + if ( !bFlyInTable ) + m_nStyleBeforeFly = GetId( rTextColl ); + + // nStyleBeforeFly may change when we recurse into another node, so we + // have to remember it in nStyle + sal_uInt16 nStyle = m_nStyleBeforeFly; + + SwWW8AttrIter aAttrIter( *this, rNode ); + rtl_TextEncoding eChrSet = aAttrIter.GetCharSet(); + + if ( m_bStartTOX ) + { + // ignore TOX header section + const SwSectionNode* pSectNd = rNode.FindSectionNode(); + if ( pSectNd && SectionType::ToxContent == pSectNd->GetSection().GetType() ) + { + AttrOutput().StartTOX( pSectNd->GetSection() ); + m_aCurrentCharPropStarts.push( 0 ); + } + } + + // Emulate: If 1-row table is marked as don't split, then set the row as don't split. + if ( IsInTable() ) + { + const SwTableNode* pTableNode = rNode.FindTableNode(); + if ( pTableNode ) + { + const SwTable& rTable = pTableNode->GetTable(); + const SwTableBox* pBox = rNode.GetTableBox(); + + // export formula cell as formula field instead of only its cell content in DOCX + if ( pBox->IsFormulaOrValueBox() == RES_BOXATR_FORMULA && + GetExportFormat() == MSWordExportBase::ExportFormat::DOCX ) + { + std::unique_ptr<SwTableBoxFormula> pFormula(pBox->GetFrameFormat()->GetTableBoxFormula().Clone()); + pFormula->PtrToBoxNm( &pTableNode->GetTable() ); + OutputField( nullptr, ww::eEquals, " = " + pFormula->GetFormula(), + FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd | FieldFlags::Close ); + } + + const bool bKeep = rTable.GetFrameFormat()->GetKeep().GetValue(); + const bool bDontSplit = !rTable.GetFrameFormat()->GetLayoutSplit().GetValue(); + // bKeep handles this a different way later on, so ignore now + if ( !bKeep && bDontSplit && rTable.GetTabLines().size() == 1 ) + { + // bDontSplit : set don't split once for the row + // but only for non-complex tables + const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr; + if ( pLine && !pLine->GetUpper() ) + { + // check if box is first in that line: + if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() ) + { + // check if paragraph is first in that line: + if ( 1 == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) ) + pLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bDontSplit)); + } + } + } + } + } + + SwSoftPageBreakList softBreakList; + // Let's decide if we need to split the paragraph because of a section break + bool bNeedParaSplit = NeedTextNodeSplit( rNode, softBreakList ) + && !IsInTable(); + + auto aBreakIt = softBreakList.begin(); + // iterate through portions on different pages + do + { + sal_Int32 nCurrentPos = *aBreakIt; + + if( softBreakList.size() > 1 ) // not for empty paragraph + ++aBreakIt; + + AttrOutput().StartParagraph( pTextNodeInfo ); + + const SwSection* pTOXSect = nullptr; + if( m_bInWriteTOX ) + { + // check for end of TOX + SwNodeIndex aIdx( rNode, 1 ); + if( !aIdx.GetNode().IsTextNode() ) + { + const SwSectionNode* pTOXSectNd = rNode.FindSectionNode(); + if ( pTOXSectNd ) + { + pTOXSect = &pTOXSectNd->GetSection(); + + const SwNode* pNxt = rNode.GetNodes().GoNext( &aIdx ); + if( pNxt && pNxt->FindSectionNode() == pTOXSectNd ) + pTOXSect = nullptr; + } + } + } + + if ( aAttrIter.RequiresImplicitBookmark() ) + { + OUString sBkmkName = "_toc" + OUString::number( rNode.GetIndex() ); + // Add a bookmark converted to a Word name. + AppendBookmark( BookmarkToWord( sBkmkName ) ); + } + + // Call this before write out fields and runs + AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); + + const OUString& aStr( rNode.GetText() ); + + sal_Int32 const nEnd = bNeedParaSplit ? *aBreakIt : aStr.getLength(); + bool bIncludeEndOfParaCRInRedlineProperties = false; + sal_Int32 nOpenAttrWithRange = 0; + + ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner; + if ( pTextNodeInfo.get() != nullptr ) + { + pTextNodeInfoInner = pTextNodeInfo->getFirstInner(); + } + + do { + + const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( nCurrentPos ); + FlyProcessingState nStateOfFlyFrame = FLY_PROCESSED; + bool bPostponeWritingText = false ; + OUString aSavedSnippet ; + + sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nCurrentPos ); + + // Skip un-exportable attributes. + if (!aAttrIter.IsExportableAttr(nCurrentPos)) + { + nCurrentPos = nNextAttr; + UpdatePosition(&aAttrIter, nCurrentPos); + eChrSet = aAttrIter.GetCharSet(); + continue; + } + + // Is this the only run in this paragraph and it's empty? + bool bSingleEmptyRun = nCurrentPos == 0 && nNextAttr == 0; + AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun ); + + if( nNextAttr > nEnd ) + nNextAttr = nEnd; + + if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) + { + if( AttrOutput().FootnoteEndnoteRefTag() ) + { + AttrOutput().EndRun( &rNode, nCurrentPos, nNextAttr == nEnd ); + AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun ); + } + } + + /* + 1) If there is a text node and an overlapping anchor, then write them in two different + runs and not as part of the same run. + 2) Ensure that it is a text node and not in a fly. + 3) If the anchor is associated with a text node with empty text then we ignore. + */ + if( rNode.IsTextNode() + && aStr != OUStringChar(CH_TXTATR_BREAKWORD) && !aStr.isEmpty() + && !rNode.GetFlyFormat() + && !(IsInTable() && !AllowPostponedTextInTable()) + && aAttrIter.IsAnchorLinkedToThisNode(rNode.GetIndex()) ) + { + bPostponeWritingText = true ; + } + + nStateOfFlyFrame = aAttrIter.OutFlys( nCurrentPos ); + AttrOutput().SetStateOfFlyFrame( nStateOfFlyFrame ); + AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText && (FLY_POSTPONED != nStateOfFlyFrame) ); + // Append bookmarks in this range after flys, exclusive of final + // position of this range + AppendBookmarks( rNode, nCurrentPos, nNextAttr - nCurrentPos ); + //Sadly only possible for word in main document text + if ( m_nTextTyp == TXT_MAINTEXT ) + AppendAnnotationMarks(aAttrIter, nCurrentPos, nNextAttr - nCurrentPos); + + // At the moment smarttags are only written for paragraphs, at the + // beginning of the paragraph. + if (nCurrentPos == 0) + AppendSmartTags(rNode); + + bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos ); + nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nCurrentPos ); + + sal_Int32 nLen = nNextAttr - nCurrentPos; + if ( !bTextAtr && nLen ) + { + sal_Unicode ch = aStr[nCurrentPos]; + + const sal_Int32 ofs = (ch == CH_TXT_ATR_FIELDSTART + || ch == CH_TXT_ATR_FIELDSEP + || ch == CH_TXT_ATR_FIELDEND + || ch == CH_TXT_ATR_FORMELEMENT) + ? 1 : 0; + + IDocumentMarkAccess* const pMarkAccess = m_pDoc->getIDocumentMarkAccess(); + if ( ch == CH_TXT_ATR_FIELDSTART ) + { + SwPosition aPosition( rNode, SwIndex( &rNode, nCurrentPos ) ); + ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition); + assert(pFieldmark); + + // Date field is exported as content control, not as a simple field + if (pFieldmark->GetFieldname() == ODF_FORMDATE) + { + if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only + { + OutputField( nullptr, lcl_getFieldId( pFieldmark ), + lcl_getFieldCode( pFieldmark ), + FieldFlags::Start | FieldFlags::CmdStart ); + WriteFormData( *pFieldmark ); + } + } + else + { + + if (pFieldmark->GetFieldname() == ODF_FORMTEXT + && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX ) + { + AppendBookmark( pFieldmark->GetName() ); + } + ww::eField eFieldId = lcl_getFieldId( pFieldmark ); + OUString sCode = lcl_getFieldCode( pFieldmark ); + if (pFieldmark->GetFieldname() == ODF_UNHANDLED ) + { + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sFieldId; + it->second >>= sFieldId; + eFieldId = static_cast<ww::eField>(sFieldId.toInt32()); + } + + it = pFieldmark->GetParameters()->find( ODF_CODE_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + it->second >>= sCode; + } + } + + OutputField( nullptr, eFieldId, sCode, FieldFlags::Start | FieldFlags::CmdStart ); + + if (pFieldmark->GetFieldname() == ODF_FORMTEXT) + WriteFormData( *pFieldmark ); + else if (pFieldmark->GetFieldname() == ODF_HYPERLINK) + WriteHyperlinkData( *pFieldmark ); + } + } + else if (ch == CH_TXT_ATR_FIELDSEP) + { + SwPosition aPosition(rNode, SwIndex(&rNode, nCurrentPos)); + // the innermost field is the correct one + ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkFor(aPosition); + assert(pFieldmark); + // DateFieldmark / ODF_FORMDATE is not a field... + if (pFieldmark->GetFieldname() != ODF_FORMDATE) + { + OutputField( nullptr, lcl_getFieldId( pFieldmark ), OUString(), FieldFlags::CmdEnd ); + + if (pFieldmark->GetFieldname() == ODF_UNHANDLED) + { + // Check for the presence of a linked OLE object + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_OLE_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sOleId; + uno::Any aValue = it->second; + aValue >>= sOleId; + if ( !sOleId.isEmpty() ) + OutputLinkedOLE( sOleId ); + } + } + } + } + else if ( ch == CH_TXT_ATR_FIELDEND ) + { + SwPosition aPosition( rNode, SwIndex( &rNode, nCurrentPos ) ); + ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition); + + assert(pFieldmark); + + if (pFieldmark->GetFieldname() == ODF_FORMDATE) + { + if(GetExportFormat() == MSWordExportBase::ExportFormat::DOCX) // supported by DOCX only + { + OutputField( nullptr, ww::eFORMDATE, OUString(), FieldFlags::Close ); + } + } + else + { + ww::eField eFieldId = lcl_getFieldId( pFieldmark ); + if (pFieldmark->GetFieldname() == ODF_UNHANDLED) + { + IFieldmark::parameter_map_t::const_iterator it = pFieldmark->GetParameters()->find( ODF_ID_PARAM ); + if ( it != pFieldmark->GetParameters()->end() ) + { + OUString sFieldId; + it->second >>= sFieldId; + eFieldId = static_cast<ww::eField>(sFieldId.toInt32()); + } + } + + OutputField( nullptr, eFieldId, OUString(), FieldFlags::Close ); + + if (pFieldmark->GetFieldname() == ODF_FORMTEXT + && GetExportFormat() != MSWordExportBase::ExportFormat::DOCX ) + { + AppendBookmark( pFieldmark->GetName() ); + } + } + } + else if ( ch == CH_TXT_ATR_FORMELEMENT ) + { + SwPosition aPosition( rNode, SwIndex( &rNode, nCurrentPos ) ); + ::sw::mark::IFieldmark const*const pFieldmark = pMarkAccess->getFieldmarkAt(aPosition); + assert(pFieldmark); + + bool const isDropdownOrCheckbox(pFieldmark->GetFieldname() == ODF_FORMDROPDOWN || + pFieldmark->GetFieldname() == ODF_FORMCHECKBOX); + if ( isDropdownOrCheckbox ) + AppendBookmark( pFieldmark->GetName() ); + OutputField( nullptr, lcl_getFieldId( pFieldmark ), + lcl_getFieldCode( pFieldmark ), + FieldFlags::Start | FieldFlags::CmdStart ); + if ( isDropdownOrCheckbox ) + WriteFormData( *pFieldmark ); + // tdf#129514 need CmdEnd for docx + OutputField(nullptr, lcl_getFieldId(pFieldmark), OUString(), + FieldFlags::CmdEnd | FieldFlags::Close); + if ( isDropdownOrCheckbox ) + AppendBookmark( pFieldmark->GetName() ); + } + nLen -= ofs; + + // if paragraph needs to be split, write only until split position + assert(!bNeedParaSplit || nCurrentPos <= *aBreakIt); + if( bNeedParaSplit && nCurrentPos + ofs + nLen > *aBreakIt) + nLen = *aBreakIt - nCurrentPos - ofs; + assert(0 <= nLen); + + OUString aSnippet( aAttrIter.GetSnippet( aStr, nCurrentPos + ofs, nLen ) ); + if ( ( m_nTextTyp == TXT_EDN || m_nTextTyp == TXT_FTN ) && nCurrentPos == 0 && nLen > 0 ) + { + // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent + sal_Int32 nFirstLineIndent=0; + SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + const SwTextNode* pTextNode( rNode.GetTextNode() ); + if ( pTextNode && pTextNode->GetAttr(aSet) ) + { + const SvxLRSpaceItem* pLRSpace = aSet.GetItem<SvxLRSpaceItem>(RES_LR_SPACE); + if ( pLRSpace ) + nFirstLineIndent = pLRSpace->GetTextFirstLineOffset(); + } + + // Insert tab for aesthetic purposes #i24762# + if ( m_bAddFootnoteTab && nFirstLineIndent < 0 && aSnippet[0] != 0x09 ) + aSnippet = "\x09" + aSnippet; + m_bAddFootnoteTab = false; + } + + if ( bPostponeWritingText && ( FLY_POSTPONED != nStateOfFlyFrame ) ) + { + bPostponeWritingText = true ; + aSavedSnippet = aSnippet ; + } + else + { + bPostponeWritingText = false ; + AttrOutput().RunText( aSnippet, eChrSet ); + } + } + + if ( aAttrIter.IsDropCap( nNextAttr ) ) + AttrOutput().FormatDrop( rNode, aAttrIter.GetSwFormatDrop(), nStyle, pTextNodeInfo, pTextNodeInfoInner ); + + // Only output character attributes if this is not a postponed text run. + if (0 != nEnd && !(bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame)) + { + // Output the character attributes + // #i51277# do this before writing flys at end of paragraph + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nCurrentPos, false ); + AttrOutput().EndRunProperties( pRedlineData ); + } + + // At the end of line, output the attributes until the CR. + // Exception: footnotes at the end of line + if ( nNextAttr == nEnd ) + { + OSL_ENSURE( nOpenAttrWithRange >= 0, "odd to see this happening, expected >= 0" ); + if ( !bTextAtr && nOpenAttrWithRange <= 0 ) + { + if ( aAttrIter.IncludeEndOfParaCRInRedlineProperties( nEnd ) ) + bIncludeEndOfParaCRInRedlineProperties = true; + else + { + // insert final graphic anchors if any before CR + nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); + // insert final bookmarks if any before CR and after flys + AppendBookmarks( rNode, nEnd, 1 ); + AppendAnnotationMarks(aAttrIter, nEnd, 1); + if ( pTOXSect ) + { + m_aCurrentCharPropStarts.pop(); + AttrOutput().EndTOX( *pTOXSect ,false); + } + //For i120928,the position of the bullet's graphic is at end of doc + if (bLastCR && (!bExported)) + { + ExportGrfBullet(rNode); + bExported = true; + } + + WriteCR( pTextNodeInfoInner ); + } + } + } + + if (0 == nEnd) + { + // Output the character attributes + // do it after WriteCR for an empty paragraph (otherwise + // WW8_WrFkp::Append throws SPRMs away...) + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nCurrentPos, false ); + AttrOutput().EndRunProperties( pRedlineData ); + } + + // Exception: footnotes at the end of line + if ( nNextAttr == nEnd ) + { + OSL_ENSURE(nOpenAttrWithRange >= 0, + "odd to see this happening, expected >= 0"); + bool bAttrWithRange = (nOpenAttrWithRange > 0); + if ( nCurrentPos != nEnd ) + { + nOpenAttrWithRange += aAttrIter.OutAttrWithRange( rNode, nEnd ); + OSL_ENSURE(nOpenAttrWithRange == 0, + "odd to see this happening, expected 0"); + } + + // !bIncludeEndOfParaCRInRedlineProperties implies we have just + // emitted a CR, in which case we want to pass force=true to + // OutputFKP to ensure that an FKP entry for direct character + // formatting is written even if empty, so that the next one will + // start after the CR. + AttrOutput().OutputFKP(!bIncludeEndOfParaCRInRedlineProperties); + + if (bTextAtr || bAttrWithRange || bIncludeEndOfParaCRInRedlineProperties) + { + // insert final graphic anchors if any before CR + nStateOfFlyFrame = aAttrIter.OutFlys( nEnd ); + // insert final bookmarks if any before CR and after flys + AppendBookmarks( rNode, nEnd, 1 ); + AppendAnnotationMarks(aAttrIter, nEnd, 1); + WriteCR( pTextNodeInfoInner ); + // #i120928 - position of the bullet's graphic is at end of doc + if (bLastCR && (!bExported)) + { + ExportGrfBullet(rNode); + bExported = true; + } + + if ( pTOXSect ) + { + m_aCurrentCharPropStarts.pop(); + AttrOutput().EndTOX( *pTOXSect ); + } + + if (bIncludeEndOfParaCRInRedlineProperties) + { + AttrOutput().Redline( aAttrIter.GetRunLevelRedline( nEnd ) ); + //If there was no redline property emitted, force adding + //another entry for the CR so that in the case that this + //has no redline, but the next para does, then this one is + //not merged with the next + AttrOutput().OutputFKP(true); + } + } + } + + AttrOutput().WritePostitFieldReference(); + + if( bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame ) + { + AttrOutput().EndRun(&rNode, nCurrentPos, nNextAttr == nEnd); + //write the postponed text run + AttrOutput().StartRun( pRedlineData, nCurrentPos, bSingleEmptyRun ); + AttrOutput().SetAnchorIsLinkedToNode( false ); + AttrOutput().ResetFlyProcessingFlag(); + if (0 != nEnd) + { + AttrOutput().StartRunProperties(); + aAttrIter.OutAttr( nCurrentPos, false ); + AttrOutput().EndRunProperties( pRedlineData ); + } + AttrOutput().RunText( aSavedSnippet, eChrSet ); + AttrOutput().EndRun(&rNode, nCurrentPos, nNextAttr == nEnd); + } + else if( bPostponeWritingText && !aSavedSnippet.isEmpty() ) + { + //write the postponed text run + AttrOutput().RunText( aSavedSnippet, eChrSet ); + AttrOutput().EndRun(&rNode, nCurrentPos, nNextAttr == nEnd); + } + else + AttrOutput().EndRun(&rNode, nCurrentPos, nNextAttr == nEnd); + + nCurrentPos = nNextAttr; + UpdatePosition( &aAttrIter, nCurrentPos ); + eChrSet = aAttrIter.GetCharSet(); + } + while ( nCurrentPos < nEnd ); + + // if paragraph is split, put the section break between the parts + if( bNeedParaSplit && *aBreakIt != rNode.GetText().getLength() ) + { + SwNodeIndex aNextIndex( rNode, 1 ); + const SwNode& pNextNode = aNextIndex.GetNode(); + // if there is a next node, use its attributes to create the new + // section + if( pNextNode.IsTextNode() ) + { + const SwTextNode& rNextNode = *static_cast<SwTextNode*>( + &aNextIndex.GetNode() ); + OutputSectionBreaks(rNextNode.GetpSwAttrSet(), rNextNode); + } + else if (pNextNode.IsEndNode() ) + { + // In this case the same paragraph holds the next page style + // too. + const SwPageDesc* pNextPageDesc = m_pCurrentPageDesc->GetFollow(); + assert(pNextPageDesc); + PrepareNewPageDesc( rNode.GetpSwAttrSet(), rNode, nullptr , pNextPageDesc); + } + } + else if (!bNeedParaSplit) + { + // else check if section break needed after the paragraph + AttrOutput().SectionBreaks(rNode); + } + + AttrOutput().StartParagraphProperties(); + + AttrOutput().ParagraphStyle( nStyle ); + + if ( m_pParentFrame && IsInTable() ) // Fly-Attrs + OutputFormat( m_pParentFrame->GetFrameFormat(), false, false, true ); + + if ( pTextNodeInfo.get() != nullptr ) + { +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pTextNodeInfo->toString()); +#endif + + AttrOutput().TableInfoCell( pTextNodeInfoInner ); + if (pTextNodeInfoInner->isFirstInTable()) + { + const SwTable * pTable = pTextNodeInfoInner->getTable(); + + const SwTableFormat* pTabFormat = pTable->GetFrameFormat(); + if (pTabFormat != nullptr) + { + if (pTabFormat->GetBreak().GetBreak() == SvxBreak::PageBefore) + AttrOutput().PageBreakBefore(true); + } + } + } + + if ( !bFlyInTable ) + { + SfxItemSet* pTmpSet = nullptr; + const sal_uInt8 nPrvNxtNd = rNode.HasPrevNextLayNode(); + + if( (ND_HAS_PREV_LAYNODE|ND_HAS_NEXT_LAYNODE ) != nPrvNxtNd ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rNode.GetSwAttrSet().GetItemState( + RES_UL_SPACE, true, &pItem ) && + ( ( !( ND_HAS_PREV_LAYNODE & nPrvNxtNd ) && + static_cast<const SvxULSpaceItem*>(pItem)->GetUpper()) || + ( !( ND_HAS_NEXT_LAYNODE & nPrvNxtNd ) && + static_cast<const SvxULSpaceItem*>(pItem)->GetLower()) )) + { + pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); + SvxULSpaceItem aUL( *static_cast<const SvxULSpaceItem*>(pItem) ); + // #i25901#- consider compatibility option + if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES)) + { + if( !(ND_HAS_PREV_LAYNODE & nPrvNxtNd )) + aUL.SetUpper( 0 ); + } + // #i25901# - consider compatibility option + if (!m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS)) + { + if( !(ND_HAS_NEXT_LAYNODE & nPrvNxtNd )) + aUL.SetLower( 0 ); + } + pTmpSet->Put( aUL ); + } + } + + const bool bParaRTL = aAttrIter.IsParaRTL(); + + int nNumberLevel = -1; + if (rNode.IsNumbered()) + nNumberLevel = rNode.GetActualListLevel(); + if (nNumberLevel >= 0 && nNumberLevel < MAXLEVEL) + { + const SwNumRule* pRule = rNode.GetNumRule(); + sal_uInt8 nLvl = static_cast< sal_uInt8 >(nNumberLevel); + const SwNumFormat* pFormat = pRule->GetNumFormat( nLvl ); + if( !pFormat ) + pFormat = &pRule->Get( nLvl ); + + if( !pTmpSet ) + pTmpSet = new SfxItemSet( rNode.GetSwAttrSet() ); + + SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(*pTmpSet, RES_LR_SPACE)); + // #i86652# + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aLR.SetTextLeft( aLR.GetTextLeft() + pFormat->GetAbsLSpace() ); + } + + if( rNode.IsNumbered() && rNode.IsCountedInList() ) + { + // #i86652# + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + if (bParaRTL) + { + aLR.SetTextFirstLineOffsetValue(aLR.GetTextFirstLineOffset() + pFormat->GetAbsLSpace() - pFormat->GetFirstLineOffset()); //TODO: overflow + } + else + { + aLR.SetTextFirstLineOffset(aLR.GetTextFirstLineOffset() + GetWordFirstLineOffset(*pFormat)); + } + } + + // correct fix for issue i94187 + if (SfxItemState::SET != + pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) + { + // List style set via paragraph style - then put it into the itemset. + // This is needed to get list level and list id exported for + // the paragraph. + pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); + + // Put indent values into the itemset in case that the list + // style is applied via paragraph style and the list level + // indent values are not applicable. + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_ALIGNMENT && + !rNode.AreListLevelIndentsApplicable() ) + { + pTmpSet->Put( aLR ); + } + } + } + else + pTmpSet->ClearItem(RES_PARATR_NUMRULE); + + // #i86652# + if ( pFormat->GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + pTmpSet->Put(aLR); + + //#i21847# + SvxTabStopItem aItem( + ItemGet<SvxTabStopItem>(*pTmpSet, RES_PARATR_TABSTOP)); + SvxTabStop aTabStop(pFormat->GetAbsLSpace()); + aItem.Insert(aTabStop); + pTmpSet->Put(aItem); + + MSWordExportBase::CorrectTabStopInSet(*pTmpSet, pFormat->GetAbsLSpace()); + } + } + + /* + If a given para is using the SvxFrameDirection::Environment direction we + cannot export that, if it's ltr then that's ok as that is word's + default. Otherwise we must add a RTL attribute to our export list + Only necessary if the ParaStyle doesn't define the direction. + */ + const SvxFrameDirectionItem* pItem = + rNode.GetSwAttrSet().GetItem(RES_FRAMEDIR); + if ( + (!pItem || pItem->GetValue() == SvxFrameDirection::Environment) && + rTextColl.GetFrameDir().GetValue() == SvxFrameDirection::Environment + ) + { + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + + if ( bParaRTL ) + pTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_RL_TB, RES_FRAMEDIR)); + else + pTmpSet->Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR)); + + const SvxAdjustItem* pAdjust = rNode.GetSwAttrSet().GetItem(RES_PARATR_ADJUST); + if ( pAdjust && (pAdjust->GetAdjust() == SvxAdjust::Left || pAdjust->GetAdjust() == SvxAdjust::Right ) ) + pTmpSet->Put( *pAdjust, RES_PARATR_ADJUST ); + } + // move code for handling of numbered, + // but not counted paragraphs to this place. Otherwise, the paragraph + // isn't exported as numbered, but not counted, if no other attribute + // is found in <pTmpSet> + // #i44815# adjust numbering/indents for numbered paragraphs + // without number (NO_NUMLEVEL) + // #i47013# need to check rNode.GetNumRule()!=NULL as well. + if ( ! rNode.IsCountedInList() && rNode.GetNumRule()!=nullptr ) + { + // WW8 does not know numbered paragraphs without number + // (NO_NUMLEVEL). In WW8AttributeOutput::ParaNumRule(), we will export + // the RES_PARATR_NUMRULE as list-id 0, which in WW8 means + // no numbering. Here, we will adjust the indents to match + // visually. + + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + + // create new LRSpace item, based on the current (if present) + const SfxPoolItem* pPoolItem = nullptr; + pTmpSet->GetItemState(RES_LR_SPACE, true, &pPoolItem); + SvxLRSpaceItem aLRSpace( + ( pPoolItem == nullptr ) + ? SvxLRSpaceItem(0, 0, 0, 0, RES_LR_SPACE) + : *static_cast<const SvxLRSpaceItem*>( pPoolItem ) ); + + // new left margin = old left + label space + const SwNumRule* pRule = rNode.GetNumRule(); + int nLevel = rNode.GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& rNumFormat = pRule->Get( static_cast< sal_uInt16 >(nLevel) ); + + // #i86652# + if ( rNumFormat.GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetAbsLSpace() ); + } + else + { + aLRSpace.SetTextLeft( aLRSpace.GetLeft() + rNumFormat.GetIndentAt() ); + } + + // new first line indent = 0 + // (first line indent is ignored for NO_NUMLEVEL) + if (!bParaRTL) + aLRSpace.SetTextFirstLineOffset( 0 ); + + // put back the new item + pTmpSet->Put( aLRSpace ); + + // assure that numbering rule is in <pTmpSet> + if (SfxItemState::SET != pTmpSet->GetItemState(RES_PARATR_NUMRULE, false) ) + { + pTmpSet->Put( SwNumRuleItem( pRule->GetName() )); + } + } + + // #i75457# + // Export page break after attribute from paragraph style. + // If page break attribute at the text node exist, an existing page + // break after at the paragraph style hasn't got to be considered. + if ( !rNode.GetpSwAttrSet() || + SfxItemState::SET != rNode.GetpSwAttrSet()->GetItemState(RES_BREAK, false) ) + { + const SvxFormatBreakItem& rBreakAtParaStyle + = ItemGet<SvxFormatBreakItem>(rNode.GetSwAttrSet(), RES_BREAK); + if (rBreakAtParaStyle.GetBreak() == SvxBreak::PageAfter) + { + if ( !pTmpSet ) + { + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + } + pTmpSet->Put(rBreakAtParaStyle); + } + else if( pTmpSet ) + { // Even a pagedesc item is set, the break item can be set 'NONE', + // this has to be overruled. + const SwFormatPageDesc& rPageDescAtParaStyle = + ItemGet<SwFormatPageDesc>( rNode, RES_PAGEDESC ); + if( rPageDescAtParaStyle.KnowsPageDesc() ) + pTmpSet->ClearItem( RES_BREAK ); + } + } + + // #i76520# Emulate non-splitting tables + if ( IsInTable() ) + { + const SwTableNode* pTableNode = rNode.FindTableNode(); + + if ( pTableNode ) + { + const SwTable& rTable = pTableNode->GetTable(); + const SvxFormatKeepItem& rKeep = rTable.GetFrameFormat()->GetKeep(); + const bool bKeep = rKeep.GetValue(); + const bool bDontSplit = !(bKeep || + rTable.GetFrameFormat()->GetLayoutSplit().GetValue()); + + if ( bKeep || bDontSplit ) + { + // bKeep: set keep at first paragraphs in all lines + // bDontSplit : set keep at first paragraphs in all lines except from last line + // but only for non-complex tables + const SwTableBox* pBox = rNode.GetTableBox(); + const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr; + + if ( pLine && !pLine->GetUpper() ) + { + // check if box is first in that line: + if ( 0 == pLine->GetBoxPos( pBox ) && pBox->GetSttNd() ) + { + // check if paragraph is first in that line: + if ( 1 == ( rNode.GetIndex() - pBox->GetSttNd()->GetIndex() ) ) + { + bool bSetAtPara = false; + if ( bKeep ) + bSetAtPara = true; + else if ( bDontSplit ) + { + // check if pLine isn't last line in table + if ( rTable.GetTabLines().size() - rTable.GetTabLines().GetPos( pLine ) != 1 ) + bSetAtPara = true; + } + + if ( bSetAtPara ) + { + if ( !pTmpSet ) + pTmpSet = new SfxItemSet(rNode.GetSwAttrSet()); + + const SvxFormatKeepItem aKeepItem( true, RES_KEEP ); + pTmpSet->Put( aKeepItem ); + } + } + } + } + } + } + } + + const SfxItemSet* pNewSet = pTmpSet ? pTmpSet : rNode.GetpSwAttrSet(); + if( pNewSet ) + { // Para-Attrs + m_pStyAttr = &rNode.GetAnyFormatColl().GetAttrSet(); + + const SwModify* pOldMod = m_pOutFormatNode; + m_pOutFormatNode = &rNode; + + // Pap-Attrs, so script is not necessary + OutputItemSet( *pNewSet, true, false, i18n::ScriptType::LATIN, false); + + m_pStyAttr = nullptr; + m_pOutFormatNode = pOldMod; + + if( pNewSet != rNode.GetpSwAttrSet() ) + delete pNewSet; + } + } + + // The formatting of the paragraph marker has two sources: + // 0) If there is a RES_PARATR_LIST_AUTOFMT, then use that. + // 1) If there are hints at the end of the paragraph, then use that. + // 2) Else use the RES_CHRATR_BEGIN..RES_TXTATR_END range of the paragraph + // properties. + // + // Exception: if there is a character style hint at the end of the + // paragraph only, then still go with 2), as RES_TXTATR_CHARFMT is always + // set as a hint. + SfxItemSet aParagraphMarkerProperties(m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END>{}); + bool bCharFormatOnly = true; + + SwFormatAutoFormat const& rListAutoFormat(static_cast<SwFormatAutoFormat const&>(rNode.GetAttr(RES_PARATR_LIST_AUTOFMT))); + if (std::shared_ptr<SfxItemSet> const& pSet = rListAutoFormat.GetStyleHandle()) + { + aParagraphMarkerProperties.Put(*pSet); + bCharFormatOnly = false; + } + else if (const SwpHints* pTextAttrs = rNode.GetpSwpHints()) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + const sal_Int32 startPos = pHt->GetStart(); // first Attr characters + const sal_Int32* endPos = pHt->End(); // end Attr characters + // Check if these attributes are for the last character in the paragraph + // - which means the paragraph marker. If a paragraph has 7 characters, + // then properties on character 8 are for the paragraph marker + if( endPos && (startPos == *endPos ) && (*endPos == rNode.GetText().getLength()) ) + { + SAL_INFO( "sw.ww8", startPos << "startPos == endPos" << *endPos); + sal_uInt16 nWhich = pHt->GetAttr().Which(); + SAL_INFO( "sw.ww8", "nWhich" << nWhich); + if ((nWhich == RES_TXTATR_AUTOFMT && bCharFormatOnly) + || nWhich == RES_TXTATR_CHARFMT) + { + aParagraphMarkerProperties.Put(pHt->GetAttr()); + } + if (nWhich != RES_TXTATR_CHARFMT) + bCharFormatOnly = false; + } + } + } + if (rNode.GetpSwAttrSet() && bCharFormatOnly) + { + aParagraphMarkerProperties.Put(*rNode.GetpSwAttrSet()); + } + const SwRedlineData* pRedlineParagraphMarkerDelete = AttrOutput().GetParagraphMarkerRedline( rNode, RedlineType::Delete ); + const SwRedlineData* pRedlineParagraphMarkerInsert = AttrOutput().GetParagraphMarkerRedline( rNode, RedlineType::Insert ); + const SwRedlineData* pParagraphRedlineData = aAttrIter.GetParagraphLevelRedline( ); + AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, pParagraphRedlineData, pRedlineParagraphMarkerDelete, pRedlineParagraphMarkerInsert); + + AttrOutput().EndParagraph( pTextNodeInfoInner ); + }while(*aBreakIt != rNode.GetText().getLength() && bNeedParaSplit ); + + SAL_INFO( "sw.ww8", "</OutWW8_SwTextNode>" ); +} + +// Tables + +void WW8AttributeOutput::EmptyParagraph() +{ + m_rWW8Export.WriteStringAsPara( OUString() ); +} + +bool MSWordExportBase::NoPageBreakSection( const SfxItemSet* pSet ) +{ + bool bRet = false; + const SfxPoolItem* pI; + if( pSet) + { + bool bNoPageBreak = false; + if ( SfxItemState::SET != pSet->GetItemState(RES_PAGEDESC, true, &pI) + || nullptr == static_cast<const SwFormatPageDesc*>(pI)->GetPageDesc() ) + { + bNoPageBreak = true; + } + + if (bNoPageBreak) + { + if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, true, &pI)) + { + SvxBreak eBreak = static_cast<const SvxFormatBreakItem*>(pI)->GetBreak(); + switch (eBreak) + { + case SvxBreak::PageBefore: + case SvxBreak::PageAfter: + bNoPageBreak = false; + break; + default: + break; + } + } + } + bRet = bNoPageBreak; + } + return bRet; +} + +void MSWordExportBase::OutputSectionNode( const SwSectionNode& rSectionNode ) +{ + const SwSection& rSection = rSectionNode.GetSection(); + + SwNodeIndex aIdx( rSectionNode, 1 ); + const SwNode& rNd = aIdx.GetNode(); + if ( !rNd.IsSectionNode() && !IsInTable() ) //No sections in table + { + // if the first Node inside the section has an own + // PageDesc or PageBreak attribute, then don't write + // here the section break + sal_uLong nRstLnNum = 0; + const SfxItemSet* pSet; + if ( rNd.IsContentNode() ) + { + pSet = &rNd.GetContentNode()->GetSwAttrSet(); + nRstLnNum = pSet->Get( RES_LINENUMBER ).GetStartValue(); + } + else + pSet = nullptr; + + if ( pSet && NoPageBreakSection( pSet ) ) + pSet = nullptr; + else + AttrOutput().SectionBreaks( rSectionNode ); + + const bool bInTOX = rSection.GetType() == SectionType::ToxContent || rSection.GetType() == SectionType::ToxHeader; + if ( !pSet && !bInTOX ) + { + // new Section with no own PageDesc/-Break + // -> write follow section break; + const SwSectionFormat* pFormat = rSection.GetFormat(); + ReplaceCr( msword::PageBreak ); // Indicator for Page/Section-Break + + // Get the page in use at the top of this section + const SwPageDesc *pCurrent = SwPageDesc::GetPageDescOfNode(rNd); + if (!pCurrent) + pCurrent = m_pCurrentPageDesc; + + AppendSection( pCurrent, pFormat, nRstLnNum ); + } + } + if ( SectionType::ToxContent == rSection.GetType() ) + { + m_bStartTOX = true; + UpdateTocSectionNodeProperties(rSectionNode); + } +} + +// tdf#121561: During export of the ODT file with TOC inside into DOCX format, +// the TOC title is being exported as regular paragraph. We should surround it +// with <w:sdt><w:sdtPr><w:sdtContent> to make it (TOC title) recognizable +// by MS Word as part of the TOC. +void MSWordExportBase::UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode) +{ + // check section type + { + const SwSection& rSection = rSectionNode.GetSection(); + if (SectionType::ToxContent != rSection.GetType()) + return; + + const SwTOXBase* pTOX = rSection.GetTOXBase(); + if (pTOX) + { + TOXTypes type = pTOX->GetType(); + if (type != TOXTypes::TOX_CONTENT) + return; + } + } + + // get section node, skip toc-header node + const SwSectionNode* pSectNd = &rSectionNode; + { + SwNodeIndex aIdxNext( *pSectNd, 1 ); + const SwNode& rNdNext = aIdxNext.GetNode(); + + if (rNdNext.IsSectionNode()) + { + const SwSectionNode* pSectNdNext = static_cast<const SwSectionNode*>(&rNdNext); + if (SectionType::ToxHeader == pSectNdNext->GetSection().GetType() && + pSectNdNext->StartOfSectionNode()->IsSectionNode()) + { + pSectNd = pSectNdNext; + } + } + } + + // get node of the first paragraph inside TOC + SwNodeIndex aIdxNext( *pSectNd, 1 ); + const SwNode& rNdTocPara = aIdxNext.GetNode(); + const SwContentNode* pNode = rNdTocPara.GetContentNode(); + if (!pNode) + return; + + // put required flags into grab bag of the first node in TOC + { + uno::Sequence<beans::PropertyValue> aDocPropertyValues(comphelper::InitPropertySequence( + { + {"ooxml:CT_SdtDocPart_docPartGallery", uno::makeAny(OUString("Table of Contents"))}, + {"ooxml:CT_SdtDocPart_docPartUnique", uno::makeAny(OUString("true"))}, + })); + + uno::Sequence<beans::PropertyValue> aSdtPrPropertyValues(comphelper::InitPropertySequence( + { + {"ooxml:CT_SdtPr_docPartObj", uno::makeAny(aDocPropertyValues)}, + })); + + SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG); + aGrabBag.GetGrabBag()["SdtPr"] <<= aSdtPrPropertyValues; + + // create temp attr set + SwAttrSet aSet(pNode->GetSwAttrSet()); + aSet.Put(aGrabBag); + + // set new attr to node + const_cast<SwContentNode*>(pNode)->SetAttr(aSet); + } + + // set flag for the next node after TOC + // in order to indicate that std area has been finished + // see, DomainMapper::lcl_startParagraphGroup() for the same functionality during load + { + SwNodeIndex aEndTocNext( *rSectionNode.EndOfSectionNode(), 1 ); + const SwNode& rEndTocNextNode = aEndTocNext.GetNode(); + const SwContentNode* pNodeAfterToc = rEndTocNextNode.GetContentNode(); + if (pNodeAfterToc) + { + SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG); + aGrabBag.GetGrabBag()["ParaSdtEndBefore"] <<= true; + + // create temp attr set + SwAttrSet aSet(pNodeAfterToc->GetSwAttrSet()); + aSet.Put(aGrabBag); + + // set new attr to node + const_cast<SwContentNode*>(pNodeAfterToc)->SetAttr(aSet); + } + } +} + +void WW8Export::AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) +{ + pSepx->AppendSep(Fc2Cp(Strm().Tell()), pPageDesc, pFormat, nLnNum); +} + +// Flys + +void WW8AttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) +{ + const SwFrameFormat &rFrameFormat = rFormat.GetFrameFormat(); + const SwFormatAnchor& rAnch = rFrameFormat.GetAnchor(); + + bool bUseEscher = true; + + if (rFormat.IsInline()) + { + ww8::Frame::WriterSource eType = rFormat.GetWriterType(); + bUseEscher = eType != ww8::Frame::eGraphic && eType != ww8::Frame::eOle; + + /* + A special case for converting some inline form controls to form fields + when in winword 8+ mode + */ + if (bUseEscher && (eType == ww8::Frame::eFormControl)) + { + if ( m_rWW8Export.MiserableFormFieldExportHack( rFrameFormat ) ) + return ; + } + } + + if (bUseEscher) + { + // write as escher + m_rWW8Export.AppendFlyInFlys(rFormat, rNdTopLeft); + } + else + { + bool bDone = false; + + // Fetch from node and last node the position in the section + const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx(); + + sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex()+1 : 0; + sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0; + + if( nStt >= nEnd ) // no range, hence no valid node + return; + + if ( !m_rWW8Export.IsInTable() && rFormat.IsInline() ) + { + //Test to see if this textbox contains only a single graphic/ole + SwTextNode* pParTextNode = rAnch.GetContentAnchor()->nNode.GetNode().GetTextNode(); + if ( pParTextNode && !m_rWW8Export.m_pDoc->GetNodes()[ nStt ]->IsNoTextNode() ) + bDone = true; + } + if( !bDone ) + { + + m_rWW8Export.SaveData( nStt, nEnd ); + + Point aOffset; + if ( m_rWW8Export.m_pParentFrame ) + { + /* Munge flys in fly into absolutely positioned elements for word 6 */ + const SwTextNode* pParTextNode = rAnch.GetContentAnchor()->nNode.GetNode().GetTextNode(); + const SwRect aPageRect = pParTextNode->FindPageFrameRect(); + + aOffset = rFrameFormat.FindLayoutRect().Pos(); + aOffset -= aPageRect.Pos(); + + m_rWW8Export.m_pFlyOffset = &aOffset; + m_rWW8Export.m_eNewAnchorType = RndStdIds::FLY_AT_PAGE; + } + + m_rWW8Export.m_pParentFrame = &rFormat; + if ( + m_rWW8Export.IsInTable() && + (RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId()) && + !m_rWW8Export.m_pDoc->GetNodes()[ nStt ]->IsNoTextNode() + ) + { + // note: set Flag bOutTable again, + // because we deliver the normal content of the table cell, and no border + // ( Flag was deleted above in aSaveData() ) + m_rWW8Export.m_bOutTable = true; + const OUString& aName = rFrameFormat.GetName(); + m_rWW8Export.StartCommentOutput(aName); + m_rWW8Export.WriteText(); + m_rWW8Export.EndCommentOutput(aName); + } + else + m_rWW8Export.WriteText(); + + m_rWW8Export.RestoreData(); + } + } +} + +void AttributeOutputBase::OutputFlyFrame( const ww8::Frame& rFormat ) +{ + if ( !rFormat.GetContentNode() ) + return; + + const SwContentNode &rNode = *rFormat.GetContentNode(); + Point aLayPos; + + // get the Layout Node-Position + if (RndStdIds::FLY_AT_PAGE == rFormat.GetFrameFormat().GetAnchor().GetAnchorId()) + aLayPos = rNode.FindPageFrameRect().Pos(); + else + aLayPos = rNode.FindLayoutRect().Pos(); + + OutputFlyFrame_Impl( rFormat, aLayPos ); +} + +// write data of any redline +void WW8AttributeOutput::Redline( const SwRedlineData* pRedline ) +{ + if ( !pRedline ) + return; + + if ( pRedline->Next() ) + Redline( pRedline->Next() ); + + static const sal_uInt16 insSprmIds[ 3 ] = + { + // Ids for insert // for WW8 + NS_sprm::sprmCFRMarkIns, NS_sprm::sprmCIbstRMark, NS_sprm::sprmCDttmRMark, + }; + static const sal_uInt16 delSprmIds[ 3 ] = + { + // Ids for delete // for WW8 + NS_sprm::sprmCFRMarkDel, NS_sprm::sprmCIbstRMarkDel, NS_sprm::sprmCDttmRMarkDel, + }; + + const sal_uInt16* pSprmIds = nullptr; + switch( pRedline->GetType() ) + { + case RedlineType::Insert: + pSprmIds = insSprmIds; + break; + + case RedlineType::Delete: + pSprmIds = delSprmIds; + break; + + case RedlineType::Format: + m_rWW8Export.InsUInt16( NS_sprm::sprmCPropRMark90 ); + m_rWW8Export.pO->push_back( 7 ); // len + m_rWW8Export.pO->push_back( 1 ); + m_rWW8Export.InsUInt16( m_rWW8Export.AddRedlineAuthor( pRedline->GetAuthor() ) ); + m_rWW8Export.InsUInt32( sw::ms::DateTime2DTTM( pRedline->GetTimeStamp() )); + break; + default: + OSL_ENSURE(false, "Unhandled redline type for export"); + break; + } + + if ( pSprmIds ) + { + m_rWW8Export.InsUInt16( pSprmIds[0] ); + m_rWW8Export.pO->push_back( 1 ); + + m_rWW8Export.InsUInt16( pSprmIds[1] ); + m_rWW8Export.InsUInt16( m_rWW8Export.AddRedlineAuthor( pRedline->GetAuthor() ) ); + + m_rWW8Export.InsUInt16( pSprmIds[2] ); + m_rWW8Export.InsUInt32( sw::ms::DateTime2DTTM( pRedline->GetTimeStamp() )); + } +} + +void MSWordExportBase::OutputContentNode( SwContentNode& rNode ) +{ + switch ( rNode.GetNodeType() ) + { + case SwNodeType::Text: + OutputTextNode( *rNode.GetTextNode() ); + break; + case SwNodeType::Grf: + OutputGrfNode( *rNode.GetGrfNode() ); + break; + case SwNodeType::Ole: + OutputOLENode( *rNode.GetOLENode() ); + break; + default: + SAL_WARN("sw.ww8", "Unhandled node, type == " << static_cast<int>(rNode.GetNodeType()) ); + break; + } +} + + +WW8Ruby::WW8Ruby(const SwTextNode& rNode, const SwFormatRuby& rRuby, const MSWordExportBase& rExport ): + m_nJC(0), + m_cDirective(0), + m_nRubyHeight(0), + m_nBaseHeight(0) +{ + switch ( rRuby.GetAdjustment() ) + { + case css::text::RubyAdjust_LEFT: + m_nJC = 3; + m_cDirective = 'l'; + break; + case css::text::RubyAdjust_CENTER: + //defaults to 0 + break; + case css::text::RubyAdjust_RIGHT: + m_nJC = 4; + m_cDirective = 'r'; + break; + case css::text::RubyAdjust_BLOCK: + m_nJC = 1; + m_cDirective = 'd'; + break; + case css::text::RubyAdjust_INDENT_BLOCK: + m_nJC = 2; + m_cDirective = 'd'; + break; + default: + OSL_ENSURE( false,"Unhandled Ruby justification code" ); + break; + } + + if ( rRuby.GetPosition() == css::text::RubyPosition::INTER_CHARACTER ) + { + m_nJC = 5; + m_cDirective = 0; + } + + /* + MS needs to know the name and size of the font used in the ruby item, + but we could have written it in a mixture of asian and western + scripts, and each of these can be a different font and size than the + other, so we make a guess based upon the first character of the text, + defaulting to asian. + */ + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + sal_uInt16 nRubyScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0); + + const SwTextRuby* pRubyText = rRuby.GetTextRuby(); + const SwCharFormat* pFormat = pRubyText ? pRubyText->GetCharFormat() : nullptr; + + if (pFormat) + { + const auto& rFont + = ItemGet<SvxFontItem>(*pFormat, GetWhichOfScript(RES_CHRATR_FONT, nRubyScript)); + m_sFontFamily = rFont.GetFamilyName(); + + const auto& rHeight = ItemGet<SvxFontHeightItem>( + *pFormat, GetWhichOfScript(RES_CHRATR_FONTSIZE, nRubyScript)); + m_nRubyHeight = rHeight.GetHeight(); + } + else + { + /*Get defaults if no formatting on ruby text*/ + + const SfxItemPool* pPool = rNode.GetSwAttrSet().GetPool(); + pPool = pPool ? pPool : &rExport.m_pDoc->GetAttrPool(); + + + const auto& rFont + = DefaultItemGet<SvxFontItem>(*pPool, GetWhichOfScript(RES_CHRATR_FONT, nRubyScript)); + m_sFontFamily = rFont.GetFamilyName(); + + const auto& rHeight = DefaultItemGet<SvxFontHeightItem>( + *pPool, GetWhichOfScript(RES_CHRATR_FONTSIZE, nRubyScript)); + m_nRubyHeight = rHeight.GetHeight(); + } + + const OUString &rText = rNode.GetText(); + sal_uInt16 nScript = i18n::ScriptType::LATIN; + + if (!rText.isEmpty()) + nScript = g_pBreakIt->GetBreakIter()->getScriptType(rText, 0); + + sal_uInt16 nWhich = GetWhichOfScript(RES_CHRATR_FONTSIZE, nScript); + auto& rHeightItem = static_cast<const SvxFontHeightItem&>(rExport.GetItem(nWhich)); + m_nBaseHeight = rHeightItem.GetHeight(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtw8num.cxx b/sw/source/filter/ww8/wrtw8num.cxx new file mode 100644 index 000000000..87a6cb68f --- /dev/null +++ b/sw/source/filter/ww8/wrtw8num.cxx @@ -0,0 +1,670 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/font.hxx> +#include <editeng/langitem.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <numrule.hxx> +#include <charfmt.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> + +#include "sprmids.hxx" + +#include "ww8attributeoutput.hxx" +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "wrtww8.hxx" +#include "ww8par.hxx" + +#include <numeric> + +using namespace ::com::sun::star; +using namespace sw::types; +using namespace sw::util; + +SwNumRule* MSWordExportBase::DuplicateNumRuleImpl(const SwNumRule *pRule) +{ + const OUString sPrefix("WW8TempExport" + OUString::number( m_nUniqueList++ )); + SwNumRule* pMyNumRule = + new SwNumRule( m_pDoc->GetUniqueNumRuleName( &sPrefix ), + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ); + m_pUsedNumTable->push_back( pMyNumRule ); + + for ( sal_uInt16 i = 0; i < MAXLEVEL; i++ ) + { + const SwNumFormat& rSubRule = pRule->Get(i); + pMyNumRule->Set( i, rSubRule ); + } + return pMyNumRule; +} + +// multiple SwList can be based on the same SwNumRule; ensure one w:abstractNum +// per SwList +sal_uInt16 MSWordExportBase::DuplicateAbsNum(OUString const& rListId, + SwNumRule const& rAbstractRule) +{ + auto const it(m_Lists.find(rListId)); + if (it != m_Lists.end()) + { + return it->second; + } + else + { + auto const pNewAbstractRule = DuplicateNumRuleImpl(&rAbstractRule); + assert(GetNumberingId(*pNewAbstractRule) == m_pUsedNumTable->size() - 1); + (void) pNewAbstractRule; + m_Lists.insert(std::make_pair(rListId, m_pUsedNumTable->size() - 1)); + return m_pUsedNumTable->size() - 1; + } +} + +// Ideally we want to map SwList to w:abstractNum and SwNumRule to w:num +// The current approach is to keep exporting every SwNumRule to +// 1 w:abstractNum and 1 w:num, and then add extra w:num via this function +// that reference an existing w:abstractNum and may override its formatting; +// of course this will end up exporting some w:num that aren't actually used. +sal_uInt16 MSWordExportBase::OverrideNumRule( + SwNumRule const& rExistingRule, + OUString const& rListId, + SwNumRule const& rAbstractRule) +{ + const sal_uInt16 numdef = GetNumberingId(rExistingRule); + + const sal_uInt16 absnumdef = rListId == rAbstractRule.GetDefaultListId() + ? GetNumberingId(rAbstractRule) + : DuplicateAbsNum(rListId, rAbstractRule); + auto const mapping = std::make_pair(numdef, absnumdef); + + auto it = m_OverridingNums.insert(std::make_pair(m_pUsedNumTable->size(), mapping)); + + m_pUsedNumTable->push_back(nullptr); // dummy, it's unique_ptr... + ++m_nUniqueList; // counter for DuplicateNumRule... + + return it.first->first; +} + +void MSWordExportBase::AddListLevelOverride(sal_uInt16 nListId, + sal_uInt16 nLevelNum, + sal_uInt16 nStartAt) +{ + m_ListLevelOverrides[nListId][nLevelNum] = nStartAt; +} + +sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule ) +{ + if ( !m_pUsedNumTable ) + { + m_pUsedNumTable.reset(new SwNumRuleTable); + m_pUsedNumTable->insert( m_pUsedNumTable->begin(), m_pDoc->GetNumRuleTable().begin(), m_pDoc->GetNumRuleTable().end() ); + // Check, if the outline rule is already inserted into <pUsedNumTable>. + // If yes, do not insert it again. + bool bOutlineRuleAdded( false ); + for ( sal_uInt16 n = m_pUsedNumTable->size(); n; ) + { + const SwNumRule& rRule = *(*m_pUsedNumTable)[ --n ]; + if ( !SwDoc::IsUsed( rRule ) ) + { + m_pUsedNumTable->erase( m_pUsedNumTable->begin() + n ); + } + else if ( &rRule == m_pDoc->GetOutlineNumRule() ) + { + bOutlineRuleAdded = true; + } + } + + if ( !bOutlineRuleAdded ) + { + // still need to paste the OutlineRule + SwNumRule* pR = m_pDoc->GetOutlineNumRule(); + m_pUsedNumTable->push_back( pR ); + } + } + SwNumRule* p = const_cast<SwNumRule*>(&rNumRule); + sal_uInt16 nRet = static_cast<sal_uInt16>(m_pUsedNumTable->GetPos(p)); + + return nRet; +} + +// GetFirstLineOffset should problem never appear unadorned apart from +// here in the ww export filter +sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat) +{ + OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION, + "<GetWordFirstLineOffset> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" ); + + short nFirstLineOffset; + if (rFormat.GetNumAdjust() == SvxAdjust::Right) + nFirstLineOffset = -rFormat.GetCharTextDistance(); + else + nFirstLineOffset = rFormat.GetFirstLineOffset(); //TODO: overflow + return nFirstLineOffset; +} + +void WW8Export::WriteNumbering() +{ + if ( !m_pUsedNumTable ) + return; // no numbering is used + + // list formats - LSTF + pFib->m_fcPlcfLst = pTableStrm->Tell(); + SwWW8Writer::WriteShort( *pTableStrm, m_pUsedNumTable->size() ); + NumberingDefinitions(); + // set len to FIB + pFib->m_lcbPlcfLst = pTableStrm->Tell() - pFib->m_fcPlcfLst; + + // list formats - LVLF + AbstractNumberingDefinitions(); + + // list formats - LFO + OutOverrideListTab(); + + // list formats - ListNames + OutListNamesTab(); +} + +void WW8AttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) +{ + SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId ); + SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId ); + + // not associated with a Style + for ( int i = 0; i < WW8ListManager::nMaxLevel; ++i ) + SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0xFFF ); + + sal_uInt8 nFlags = 0; + if ( rRule.IsContinusNum() ) + nFlags |= 0x1; + + m_rWW8Export.pTableStrm->WriteUChar( nFlags ).WriteUChar( 0/*nDummy*/ ); +} + +void MSWordExportBase::NumberingDefinitions() +{ + if ( !m_pUsedNumTable ) + return; // no numbering is used + + sal_uInt16 nCount = m_pUsedNumTable->size(); + + // Write static data of SwNumRule - LSTF + for ( sal_uInt16 n = 0; n < nCount; ++n ) + { + const SwNumRule * pRule = (*m_pUsedNumTable)[ n ]; + if (pRule) + { + AttrOutput().NumberingDefinition(n + 1, *pRule); + } + else + { + auto it = m_OverridingNums.find(n); + assert(it != m_OverridingNums.end()); + pRule = (*m_pUsedNumTable)[it->second.first]; + assert(pRule); + AttrOutput().OverrideNumberingDefinition(*pRule, n + 1, it->second.second + 1, m_ListLevelOverrides[n]); + } + } +} + +/** + * Converts the SVX numbering type to MSONFC. + * + * This is used for paragraph numbering purposes. + */ +static sal_uInt8 GetLevelNFC( sal_uInt16 eNumType, const SfxItemSet *pOutSet) +{ + sal_uInt8 nRet = 0; + switch( eNumType ) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break; + case SVX_NUM_ROMAN_UPPER: nRet = 1; break; + case SVX_NUM_ROMAN_LOWER: nRet = 2; break; + + case SVX_NUM_BITMAP: + case SVX_NUM_CHAR_SPECIAL: nRet = 23; break; + case SVX_NUM_FULL_WIDTH_ARABIC: nRet = 14; break; + case SVX_NUM_CIRCLE_NUMBER: nRet = 18;break; + case SVX_NUM_NUMBER_LOWER_ZH: + nRet = 35; + if ( pOutSet ) { + const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE); + const LanguageType eLang = rLang.GetLanguage(); + if (LANGUAGE_CHINESE_SIMPLIFIED ==eLang) { + nRet = 39; + } + } + break; + case SVX_NUM_NUMBER_UPPER_ZH: nRet = 38; break; + case SVX_NUM_NUMBER_UPPER_ZH_TW: nRet = 34;break; + case SVX_NUM_TIAN_GAN_ZH: nRet = 30; break; + case SVX_NUM_DI_ZI_ZH: nRet = 31; break; + case SVX_NUM_NUMBER_TRADITIONAL_JA: nRet = 16; break; + case SVX_NUM_AIU_FULLWIDTH_JA: nRet = 20; break; + case SVX_NUM_AIU_HALFWIDTH_JA: nRet = 12; break; + case SVX_NUM_IROHA_FULLWIDTH_JA: nRet = 21; break; + case SVX_NUM_IROHA_HALFWIDTH_JA: nRet = 13; break; + case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada + case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung + case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break; + case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break; + case style::NumberingType::NUMBER_HANGUL_KO: nRet = 41; break; + case style::NumberingType::NUMBER_UPPER_KO: nRet = 44; break; + case SVX_NUM_NUMBER_NONE: nRet = 0xff; break; + // No SVX_NUM_SYMBOL_CHICAGO here: LVLF can't contain 0x09, msonfcChiManSty. + case SVX_NUM_ARABIC_ZERO: + // 0x16, msonfcArabicLZ + nRet = 22; + break; + } + return nRet; +} + + +void WW8AttributeOutput::NumberingLevel( sal_uInt8 /*nLevel*/, + sal_uInt16 nStart, + sal_uInt16 nNumberingType, + SvxAdjust eAdjust, + const sal_uInt8 *pNumLvlPos, + sal_uInt8 nFollow, + const wwFont *pFont, + const SfxItemSet *pOutSet, + sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, + sal_Int16 nListTabPos, + const OUString &rNumberingString, + const SvxBrushItem* pBrush //For i120928,to transfer graphic of bullet + ) +{ + // Start value + SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nStart ); + + // Type + m_rWW8Export.pTableStrm->WriteUChar( GetLevelNFC( nNumberingType ,pOutSet) ); + + // Justification + sal_uInt8 nAlign; + switch ( eAdjust ) + { + case SvxAdjust::Center: + nAlign = 1; + break; + case SvxAdjust::Right: + nAlign = 2; + break; + default: + nAlign = 0; + break; + } + m_rWW8Export.pTableStrm->WriteUChar( nAlign ); + + // Write the rgbxchNums[9], positions of placeholders for paragraph + // numbers in the text + m_rWW8Export.pTableStrm->WriteBytes(pNumLvlPos, WW8ListManager::nMaxLevel); + + // Type of the character between the bullet and the text + m_rWW8Export.pTableStrm->WriteUChar( nFollow ); + + // dxaSoace/dxaIndent (Word 6 compatibility) + SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 ); + SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 ); + + // cbGrpprlChpx + std::unique_ptr<ww::bytes> pCharAtrs; + if ( pOutSet ) + { + std::unique_ptr<ww::bytes> pOldpO = std::move(m_rWW8Export.pO); + m_rWW8Export.pO.reset(new ww::bytes); + if ( pFont ) + { + sal_uInt16 nFontID = m_rWW8Export.m_aFontHelper.GetId( *pFont ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc0 ); + m_rWW8Export.InsUInt16( nFontID ); + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc2 ); + m_rWW8Export.InsUInt16( nFontID ); + } + + m_rWW8Export.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rWW8Export.m_bExportModeRTF ); + //For i120928,achieve graphic's index of bullet from the bullet bookmark + if (SVX_NUM_BITMAP == nNumberingType && pBrush) + { + int nIndex = m_rWW8Export.GetGrfIndex(*pBrush); + if ( nIndex != -1 ) + { + m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiIBullet); + m_rWW8Export.InsUInt32(nIndex); + m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiGrf); + m_rWW8Export.InsUInt16(1); + } + } + + pCharAtrs = std::move(m_rWW8Export.pO); + m_rWW8Export.pO = std::move(pOldpO); + } + m_rWW8Export.pTableStrm->WriteUChar(sal_uInt8(pCharAtrs ? pCharAtrs->size() : 0)); + + // cbGrpprlPapx + sal_uInt8 aPapSprms [] = { + 0x5e, 0x84, 0, 0, // sprmPDxaLeft + 0x60, 0x84, 0, 0, // sprmPDxaLeft1 + 0x15, 0xc6, 0x05, 0x00, 0x01, 0, 0, 0x06 + }; + m_rWW8Export.pTableStrm->WriteUChar( sal_uInt8( sizeof( aPapSprms ) ) ); + + // reserved + SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0 ); + + // pap sprms + sal_uInt8* pData = aPapSprms + 2; + Set_UInt16( pData, nIndentAt ); + pData += 2; + Set_UInt16( pData, nFirstLineIndex ); + pData += 5; + Set_UInt16( pData, nListTabPos ); + + m_rWW8Export.pTableStrm->WriteBytes(aPapSprms, sizeof(aPapSprms)); + + // write Chpx + if (pCharAtrs && !pCharAtrs->empty()) + m_rWW8Export.pTableStrm->WriteBytes(pCharAtrs->data(), pCharAtrs->size()); + + // write the num string + SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, rNumberingString.getLength() ); + SwWW8Writer::WriteString16( *m_rWW8Export.pTableStrm, rNumberingString, false ); +} + +void MSWordExportBase::AbstractNumberingDefinitions() +{ + sal_uInt16 nCount = m_pUsedNumTable->size(); + sal_uInt16 n; + + for( n = 0; n < nCount; ++n ) + { + if (nullptr == (*m_pUsedNumTable)[ n ]) + { + continue; + } + + AttrOutput().StartAbstractNumbering( n + 1 ); + + const SwNumRule& rRule = *(*m_pUsedNumTable)[ n ]; + sal_uInt8 nLvl; + sal_uInt8 nLevels = static_cast< sal_uInt8 >(rRule.IsContinusNum() ? + WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel); + for( nLvl = 0; nLvl < nLevels; ++nLvl ) + { + NumberingLevel(rRule, nLvl); + } + + AttrOutput().EndAbstractNumbering(); + } +} + +void MSWordExportBase::NumberingLevel( + SwNumRule const& rRule, sal_uInt8 const nLvl) +{ + // prepare the NodeNum to generate the NumString + static const SwNumberTree::tNumberVector aNumVector = [] { + SwNumberTree::tNumberVector vec(WW8ListManager::nMaxLevel); + std::iota(vec.begin(), vec.end(), 0); + return vec; + }(); + + // write the static data of the SwNumFormat of this level + sal_uInt8 aNumLvlPos[WW8ListManager::nMaxLevel] = { 0,0,0,0,0,0,0,0,0 }; + + const SwNumFormat& rFormat = rRule.Get( nLvl ); + + sal_uInt8 nFollow = 0; + // #i86652# + if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION) + { + // <nFollow = 2>, if minimum label width equals 0 and + // minimum distance between label and text equals 0 + nFollow = (rFormat.GetFirstLineOffset() == 0 && + rFormat.GetCharTextDistance() == 0) + ? 2 : 0; // ixchFollow: 0 - tab, 1 - blank, 2 - nothing + } + else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + switch (rFormat.GetLabelFollowedBy()) + { + case SvxNumberFormat::LISTTAB: + { + // 0 (tab) unless there would be no content before the tab, in which case 2 (nothing) + nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 0 : 2; + } + break; + case SvxNumberFormat::SPACE: + { + // 1 (space) unless there would be no content before the space in which case 2 (nothing) + nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 1 : 2; + } + break; + case SvxNumberFormat::NOTHING: + { + nFollow = 2; + } + break; + default: + { + nFollow = 0; + OSL_FAIL( "unknown GetLabelFollowedBy() return value" ); + } + } + } + + // Build the NumString for this Level + OUString sNumStr; + OUString sFontName; + bool bWriteBullet = false; + const vcl::Font* pBulletFont=nullptr; + rtl_TextEncoding eChrSet=0; + FontFamily eFamily=FAMILY_DECORATIVE; + if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() || + SVX_NUM_BITMAP == rFormat.GetNumberingType()) + { + // Use bullet + sNumStr = OUString(rFormat.GetBulletChar()); + } + else + { + // Create level string + // For docx it is not the best way: we can just take it from rRule.Get(nLvl).GetListFormat() + // But for compatibility with doc we follow same routine + if (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) + { + sal_uInt8* pLvlPos = aNumLvlPos; + // the numbering string has to be restrict + // to the level currently working on. + sNumStr = rRule.MakeNumString(aNumVector, false, true, nLvl); + + // now search the nums in the string + for (sal_uInt8 i = 0; i <= nLvl; ++i) + { + OUString sSrch(OUString::number(i)); + sal_Int32 nFnd = sNumStr.indexOf(sSrch); + if (-1 != nFnd) + { + *pLvlPos = static_cast<sal_uInt8>(nFnd + rFormat.GetPrefix().getLength() + 1); + ++pLvlPos; + sNumStr = sNumStr.replaceAt(nFnd, 1, OUString(static_cast<char>(i))); + } + } + } + + if (!rRule.Get(nLvl).HasListFormat()) + { + if (!rFormat.GetPrefix().isEmpty()) + sNumStr = rFormat.GetPrefix() + sNumStr; + sNumStr += rFormat.GetSuffix(); + } + } + + if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() || + SVX_NUM_BITMAP == rFormat.GetNumberingType()) + { + bWriteBullet = true; + + pBulletFont = rFormat.GetBulletFont(); + if (!pBulletFont) + { + pBulletFont = &numfunc::GetDefBulletFont(); + } + + eChrSet = pBulletFont->GetCharSet(); + sFontName = pBulletFont->GetFamilyName(); + eFamily = pBulletFont->GetFamilyType(); + + if (IsStarSymbol(sFontName)) + SubstituteBullet(sNumStr, eChrSet, sFontName); + } + + // Attributes of the numbering + std::unique_ptr<wwFont> pPseudoFont; + const SfxItemSet* pOutSet = nullptr; + + // cbGrpprlChpx + SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, + RES_CHRATR_END>{} ); + if (rFormat.GetCharFormat() || bWriteBullet) + { + if (bWriteBullet) + { + pOutSet = &aSet; + + if (rFormat.GetCharFormat()) + aSet.Put( rFormat.GetCharFormat()->GetAttrSet() ); + aSet.ClearItem( RES_CHRATR_CJK_FONT ); + aSet.ClearItem( RES_CHRATR_FONT ); + + if (sFontName.isEmpty()) + sFontName = pBulletFont->GetFamilyName(); + + pPseudoFont.reset(new wwFont( sFontName, pBulletFont->GetPitch(), + eFamily, eChrSet)); + } + else + pOutSet = &rFormat.GetCharFormat()->GetAttrSet(); + } + + sal_Int16 nIndentAt = 0; + sal_Int16 nFirstLineIndex = 0; + sal_Int16 nListTabPos = -1; + + // #i86652# + if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION) + { + nIndentAt = nListTabPos = rFormat.GetAbsLSpace(); //TODO: overflow + nFirstLineIndex = GetWordFirstLineOffset(rFormat); + } + else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + nIndentAt = static_cast<sal_Int16>(rFormat.GetIndentAt()); + nFirstLineIndex = static_cast<sal_Int16>(rFormat.GetFirstLineIndent()); + nListTabPos = rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB? + static_cast<sal_Int16>( rFormat.GetListtabPos() ) : 0; + } + + AttrOutput().NumberingLevel( nLvl, + rFormat.GetStart(), + rFormat.GetNumberingType(), + rFormat.GetNumAdjust(), + aNumLvlPos, + nFollow, + pPseudoFont.get(), pOutSet, + nIndentAt, nFirstLineIndex, nListTabPos, + sNumStr, + rFormat.GetNumberingType()==SVX_NUM_BITMAP ? rFormat.GetBrush() : nullptr); +} + +void WW8Export::OutOverrideListTab() +{ + if( !m_pUsedNumTable ) + return ; // no numbering is used + + // write the "list format override" - LFO + sal_uInt16 nCount = m_pUsedNumTable->size(); + sal_uInt16 n; + + pFib->m_fcPlfLfo = pTableStrm->Tell(); + SwWW8Writer::WriteLong( *pTableStrm, nCount ); + + for( n = 0; n < nCount; ++n ) + { + SwWW8Writer::WriteLong( *pTableStrm, n + 1 ); + SwWW8Writer::FillCount( *pTableStrm, 12 ); + } + for( n = 0; n < nCount; ++n ) + SwWW8Writer::WriteLong( *pTableStrm, -1 ); // no overwrite + + // set len to FIB + pFib->m_lcbPlfLfo = pTableStrm->Tell() - pFib->m_fcPlfLfo; +} + +void WW8Export::OutListNamesTab() +{ + if( !m_pUsedNumTable ) + return ; // no numbering is used + + // write the "list format override" - LFO + sal_uInt16 nNms = 0, nCount = m_pUsedNumTable->size(); + + pFib->m_fcSttbListNames = pTableStrm->Tell(); + SwWW8Writer::WriteShort( *pTableStrm, -1 ); + SwWW8Writer::WriteLong( *pTableStrm, nCount ); + + for( ; nNms < nCount; ++nNms ) + { + const SwNumRule& rRule = *(*m_pUsedNumTable)[ nNms ]; + OUString sNm; + if( !rRule.IsAutoRule() ) + sNm = rRule.GetName(); + + SwWW8Writer::WriteShort( *pTableStrm, sNm.getLength() ); + if (!sNm.isEmpty()) + SwWW8Writer::WriteString16(*pTableStrm, sNm, false); + } + + SwWW8Writer::WriteLong( *pTableStrm, pFib->m_fcSttbListNames + 2, nNms ); + // set len to FIB + pFib->m_lcbSttbListNames = pTableStrm->Tell() - pFib->m_fcSttbListNames; +} + +void MSWordExportBase::SubstituteBullet( OUString& rNumStr, + rtl_TextEncoding& rChrSet, OUString& rFontName ) const +{ + if (!m_bSubstituteBullets) + return; + OUString sFontName = rFontName; + + // If Bullet char is "", don't change + if (rNumStr[0] != u'\0') + { + rNumStr = rNumStr.replaceAt(0, 1, OUString( + msfilter::util::bestFitOpenSymbolToMSFont(rNumStr[0], rChrSet, sFontName))); + } + + rFontName = sFontName; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx new file mode 100644 index 000000000..653078824 --- /dev/null +++ b/sw/source/filter/ww8/wrtw8sty.cxx @@ -0,0 +1,2587 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <memory> + +#include <com/sun/star/i18n/ScriptType.hpp> +#include <hintids.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/fontitem.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdouno.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <doc.hxx> +#include "wrtww8.hxx" +#include <docary.hxx> +#include <poolfmt.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <ndtxt.hxx> +#include <ftninfo.hxx> +#include <fmthdft.hxx> +#include <section.hxx> +#include <fmtcntnt.hxx> +#include <fmtftn.hxx> +#include <ndindex.hxx> +#include <txtftn.hxx> +#include <charfmt.hxx> +#include <docufld.hxx> +#include <dcontact.hxx> +#include <fmtcnct.hxx> +#include <ftnidx.hxx> +#include <fmtclds.hxx> +#include <lineinfo.hxx> +#include <fmtline.hxx> +#include <swtable.hxx> +#include <redline.hxx> +#include <msfilter.hxx> +#include <swmodule.hxx> +#include <charatr.hxx> + +#include "sprmids.hxx" + +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include <wwstyles.hxx> +#include "ww8par.hxx" +#include "ww8attributeoutput.hxx" +#include "docxattributeoutput.hxx" +#include "rtfattributeoutput.hxx" + +#include <unordered_set> + +using namespace css; +using namespace sw::util; +using namespace nsHdFtFlags; + +/// For the output of sections. +struct WW8_PdAttrDesc +{ + std::unique_ptr<sal_uInt8[]> m_pData; + sal_uInt16 m_nLen; + WW8_FC m_nSepxFcPos; + WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/ + { } +}; + +namespace { + +struct WW8_SED +{ + SVBT16 aBits1; // orientation change + internal, Default: 6 + SVBT32 fcSepx; // FC file offset to beginning of SEPX for section. + // 0xFFFFFFFF for no Sprms + SVBT16 fnMpr; // used internally by Windows Word, Default: 0 + SVBT32 fcMpr; // FC, points to offset in FC space for MacWord + // Default: 0xffffffff ( nothing ) + // cbSED is 12 (decimal)), C (hex). +}; + +} + +// class WW8_WrPlc0 is only used for header and footer positioning +// ie there is no content support structure +class WW8_WrPlc0 +{ +private: + std::vector<sal_uLong> aPos; // PTRARR of CPs / FCs + sal_uLong nOfs; + + WW8_WrPlc0(WW8_WrPlc0 const&) = delete; + WW8_WrPlc0& operator=(WW8_WrPlc0 const&) = delete; + +public: + explicit WW8_WrPlc0( sal_uLong nOffset ); + sal_uInt16 Count() const { return aPos.size(); } + void Append( sal_uLong nStartCpOrFc ); + void Write( SvStream& rStrm ); +}; + +// Styles + +#define WW8_RESERVED_SLOTS 15 + +// GetId( SwCharFormat ) for use in text -> zero is not allowed, +// use "Default Char Style" instead +sal_uInt16 MSWordExportBase::GetId( const SwCharFormat* pFormat ) const +{ + sal_uInt16 nRet = m_pStyles->GetSlot( pFormat ); + return ( nRet != 0x0fff ) ? nRet : 10; // Default Char Style +} + +// GetId( SwTextFormatColl ) for use in TextNodes -> zero is not allowed, +// "Standard" instead +sal_uInt16 MSWordExportBase::GetId( const SwTextFormatColl& rColl ) const +{ + sal_uInt16 nRet = m_pStyles->GetSlot( &rColl ); + return ( nRet != 0xfff ) ? nRet : 0; // Default TextFormatColl +} + +//typedef pFormatT +MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles ) + : m_rExport( rExport ), + m_bListStyles(bListStyles) +{ + // if exist any Foot-/End-Notes then get from the EndNoteInfo struct + // the CharFormats. They will create it! + if ( !m_rExport.m_pDoc->GetFootnoteIdxs().empty() ) + { + m_rExport.m_pDoc->GetEndNoteInfo().GetAnchorCharFormat( *m_rExport.m_pDoc ); + m_rExport.m_pDoc->GetEndNoteInfo().GetCharFormat( *m_rExport.m_pDoc ); + m_rExport.m_pDoc->GetFootnoteInfo().GetAnchorCharFormat( *m_rExport.m_pDoc ); + m_rExport.m_pDoc->GetFootnoteInfo().GetCharFormat( *m_rExport.m_pDoc ); + } + sal_uInt16 nAlloc = WW8_RESERVED_SLOTS + m_rExport.m_pDoc->GetCharFormats()->size() - 1 + + m_rExport.m_pDoc->GetTextFormatColls()->size() - 1 + + (bListStyles ? m_rExport.m_pDoc->GetNumRuleTable().size() - 1 : 0); + + // somewhat generous ( free for up to 15 ) + m_pFormatA.reset( new SwFormat*[ nAlloc ] ); + memset( m_pFormatA.get(), 0, nAlloc * sizeof( SwFormat* ) ); + memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16)); + + BuildStylesTable(); + BuildStyleIds(); +} + +MSWordStyles::~MSWordStyles() +{ +} + +// Sty_SetWWSlot() dependencies for the styles -> zero is allowed +sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const +{ + sal_uInt16 n; + for ( n = 0; n < m_nUsedSlots; n++ ) + if ( m_pFormatA[n] == pFormat ) + return n; + return 0xfff; // 0xfff: WW: zero +} + +sal_uInt16 MSWordStyles::BuildGetSlot( const SwFormat& rFormat ) +{ + sal_uInt16 nRet = rFormat.GetPoolFormatId(); + switch ( nRet ) + { + case RES_POOLCOLL_STANDARD: + nRet = 0; + 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: + nRet -= RES_POOLCOLL_HEADLINE1-1; + break; + + default: + nRet = m_nUsedSlots++; + break; + } + return nRet; +} + + +sal_uInt16 MSWordStyles::GetWWId( const SwFormat& rFormat ) +{ + sal_uInt16 nRet = ww::stiUser; // user style as default + sal_uInt16 nPoolId = rFormat.GetPoolFormatId(); + if( nPoolId == RES_POOLCOLL_STANDARD ) + nRet = 0; + else if( nPoolId >= RES_POOLCOLL_HEADLINE1 && + nPoolId <= RES_POOLCOLL_HEADLINE9 ) + nRet = static_cast< sal_uInt16 >(nPoolId + 1 - RES_POOLCOLL_HEADLINE1); + else if( nPoolId >= RES_POOLCOLL_TOX_IDX1 && + nPoolId <= RES_POOLCOLL_TOX_IDX3 ) + nRet = static_cast< sal_uInt16 >(nPoolId + 10 - RES_POOLCOLL_TOX_IDX1); + else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT1 && + nPoolId <= RES_POOLCOLL_TOX_CNTNT5 ) + nRet = static_cast< sal_uInt16 >(nPoolId + 19 - RES_POOLCOLL_TOX_CNTNT1); + else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT6 && + nPoolId <= RES_POOLCOLL_TOX_CNTNT9 ) + nRet = static_cast< sal_uInt16 >(nPoolId + 24 - RES_POOLCOLL_TOX_CNTNT6); + else + switch( nPoolId ) + { + case RES_POOLCOLL_FOOTNOTE: nRet = 29; break; + case RES_POOLCOLL_MARGINAL: nRet = 30; break; + case RES_POOLCOLL_HEADER: nRet = 31; break; + case RES_POOLCOLL_FOOTER: nRet = 32; break; + case RES_POOLCOLL_TOX_IDXH: nRet = 33; break; + case RES_POOLCOLL_LABEL: nRet = 34; break; + case RES_POOLCOLL_LABEL_DRAWING: nRet = 35; break; + case RES_POOLCOLL_JAKETADRESS: nRet = 36; break; + case RES_POOLCOLL_SENDADRESS: nRet = 37; break; + case RES_POOLCOLL_ENDNOTE: nRet = 43; break; + case RES_POOLCOLL_TOX_AUTHORITIESH: nRet = 44; break; + case RES_POOLCOLL_TOX_CNTNTH: nRet = 46; break; + case RES_POOLCOLL_BULLET_LEVEL1: nRet = 48; break; + case RES_POOLCOLL_LISTS_BEGIN: nRet = 47; break; + case RES_POOLCOLL_NUM_LEVEL1: nRet = 49; break; + case RES_POOLCOLL_BULLET_LEVEL2: nRet = 54; break; + case RES_POOLCOLL_BULLET_LEVEL3: nRet = 55; break; + case RES_POOLCOLL_BULLET_LEVEL4: nRet = 56; break; + case RES_POOLCOLL_BULLET_LEVEL5: nRet = 57; break; + case RES_POOLCOLL_NUM_LEVEL2: nRet = 58; break; + case RES_POOLCOLL_NUM_LEVEL3: nRet = 59; break; + case RES_POOLCOLL_NUM_LEVEL4: nRet = 60; break; + case RES_POOLCOLL_NUM_LEVEL5: nRet = 61; break; + case RES_POOLCOLL_DOC_TITLE: nRet = 62; break; + case RES_POOLCOLL_DOC_APPENDIX: nRet = 63; break; + case RES_POOLCOLL_SIGNATURE: nRet = 64; break; + case RES_POOLCOLL_TEXT: nRet = 66; break; + case RES_POOLCOLL_TEXT_MOVE: nRet = 67; break; + case RES_POOLCOLL_BULLET_NONUM1: nRet = 68; break; + case RES_POOLCOLL_BULLET_NONUM2: nRet = 69; break; + case RES_POOLCOLL_BULLET_NONUM3: nRet = 70; break; + case RES_POOLCOLL_BULLET_NONUM4: nRet = 71; break; + case RES_POOLCOLL_BULLET_NONUM5: nRet = 72; break; + case RES_POOLCOLL_DOC_SUBTITLE: nRet = 74; break; + case RES_POOLCOLL_GREETING: nRet = 75; break; + case RES_POOLCOLL_TEXT_IDENT: nRet = 77; break; + + case RES_POOLCHR_FOOTNOTE_ANCHOR: nRet = 38; break; + case RES_POOLCHR_ENDNOTE_ANCHOR: nRet = 42; break; + case RES_POOLCHR_INET_NORMAL: nRet = 85; break; + case RES_POOLCHR_INET_VISIT: nRet = 86; break; + case RES_POOLCHR_HTML_STRONG: nRet = 87; break; + case RES_POOLCHR_HTML_EMPHASIS: nRet = 88; break; + case RES_POOLCHR_LINENUM: nRet = 40; break; + case RES_POOLCHR_PAGENO: nRet = 41; break; + } + return nRet; +} + +void MSWordStyles::BuildStylesTable() +{ + m_nUsedSlots = WW8_RESERVED_SLOTS; // reserved slots for standard, headingX, and others + + const SwCharFormats& rArr = *m_rExport.m_pDoc->GetCharFormats(); // first CharFormat + // the default character style ( 0 ) will not be outputted ! + for( size_t n = 1; n < rArr.size(); n++ ) + { + SwCharFormat* pFormat = rArr[n]; + m_pFormatA[ BuildGetSlot( *pFormat ) ] = pFormat; + } + + const SwTextFormatColls& rArr2 = *m_rExport.m_pDoc->GetTextFormatColls(); // then TextFormatColls + // the default character style ( 0 ) will not be outputted ! + for( size_t n = 1; n < rArr2.size(); n++ ) + { + SwTextFormatColl* pFormat = rArr2[n]; + sal_uInt16 nId = BuildGetSlot( *pFormat ) ; + m_pFormatA[ nId ] = pFormat; + if ( pFormat->IsAssignedToListLevelOfOutlineStyle() ) + { + int nLvl = pFormat->GetAssignedOutlineStyleLevel() ; + if (nLvl >= 0 && nLvl < MAXLEVEL) + m_aHeadingParagraphStyles[nLvl] = nId ; + } + } + + if (!m_bListStyles) + return; + + const SwNumRuleTable& rNumRuleTable = m_rExport.m_pDoc->GetNumRuleTable(); + for (size_t i = 0; i < rNumRuleTable.size(); ++i) + { + const SwNumRule* pNumRule = rNumRuleTable[i]; + if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum")) + continue; + sal_uInt16 nSlot = BuildGetSlot(*pNumRule); + m_aNumRules[nSlot] = pNumRule; + } +} + +OString MSWordStyles::CreateStyleId(const OUString &rName) +{ + OStringBuffer aStyleIdBuf(rName.getLength()); + for (int i = 0; i < rName.getLength(); ++i) + { + sal_Unicode nChar = rName[i]; + if (('0' <= nChar && nChar <= '9') || + ('a' <= nChar && nChar <= 'z') || + ('A' <= nChar && nChar <= 'Z')) + { + // first letter should be uppercase + if (aStyleIdBuf.isEmpty() && 'a' <= nChar && nChar <= 'z') + aStyleIdBuf.append(char(nChar - ('a' - 'A'))); + else + aStyleIdBuf.append(char(nChar)); + } + } + return aStyleIdBuf.makeStringAndClear(); +} + +void MSWordStyles::BuildStyleIds() +{ + std::unordered_set<OString> aUsed; + + m_aStyleIds.emplace_back("Normal"); + aUsed.insert("normal"); + + for (sal_uInt16 n = 1; n < m_nUsedSlots; ++n) + { + OUString aName; + if(m_pFormatA[n]) + aName = m_pFormatA[n]->GetName(); + else if (m_aNumRules.find(n) != m_aNumRules.end()) + aName = m_aNumRules[n]->GetName(); + + OString aStyleId = CreateStyleId(aName); + + if (aStyleId.isEmpty()) + aStyleId = "Style"; + + OString aLower(aStyleId.toAsciiLowerCase()); + + // check for uniqueness & construct something unique if we have to + if (aUsed.insert(aLower).second) + { + m_aStyleIds.push_back(aStyleId); + } + else + { + int nFree = 1; + while (!aUsed.insert(aLower + OString::number(nFree)).second) + ++nFree; + + m_aStyleIds.emplace_back(aStyleId + OString::number(nFree)); + } + } +} + +OString const & MSWordStyles::GetStyleId(sal_uInt16 nId) const +{ + return m_aStyleIds[nId]; +} + +/// For WW8 only - extend pO so that the size of pTableStrm is even. +static void impl_SkipOdd(std::unique_ptr<ww::bytes> const& pO, std::size_t nTableStrmTell) +{ + if ( ( nTableStrmTell + pO->size() ) & 1 ) // start on even + pO->push_back( sal_uInt8(0) ); // Address +} + +void WW8AttributeOutput::EndStyle() +{ + impl_SkipOdd( m_rWW8Export.pO, m_rWW8Export.pTableStrm->Tell() ); + + short nLen = m_rWW8Export.pO->size() - 2; // length of the style + sal_uInt8* p = m_rWW8Export.pO->data() + nPOPosStdLen1; + ShortToSVBT16( nLen, p ); // add + p = m_rWW8Export.pO->data() + nPOPosStdLen2; + ShortToSVBT16( nLen, p ); // also + + m_rWW8Export.pTableStrm->WriteBytes(m_rWW8Export.pO->data(), m_rWW8Export.pO->size()); + m_rWW8Export.pO->clear(); +} + +void WW8AttributeOutput::StartStyle( const OUString& rName, StyleType eType, sal_uInt16 nWwBase, + sal_uInt16 nWwNext, sal_uInt16 nWwId, sal_uInt16 /*nId*/, bool bAutoUpdate ) +{ + sal_uInt8 aWW8_STD[ sizeof( WW8_STD ) ] = {}; + sal_uInt8* pData = aWW8_STD; + + sal_uInt16 nBit16 = 0x1000; // fInvalHeight + nBit16 |= (ww::stiNil & nWwId); + Set_UInt16( pData, nBit16 ); + + nBit16 = nWwBase << 4; // istdBase + nBit16 |= (eType == STYLE_TYPE_PARA ? 1 : 2); // sgc + Set_UInt16( pData, nBit16 ); + + nBit16 = nWwNext << 4; // istdNext + nBit16 |= (eType == STYLE_TYPE_PARA ? 2 : 1); // cupx + Set_UInt16( pData, nBit16 ); + + pData += sizeof( sal_uInt16 ); // bchUpe + + nBit16 = bAutoUpdate ? 1 : 0; // fAutoRedef : 1 + Set_UInt16( pData, nBit16 ); + // now new: + // from Ver8 there are two fields more: + // sal_uInt16 fHidden : 1; /* hidden from UI? + // sal_uInt16 : 14; /* unused bits + + sal_uInt16 nLen = static_cast< sal_uInt16 >( ( pData - aWW8_STD ) + 1 + + (2 * (rName.getLength() + 1)) ); // temporary + + nPOPosStdLen1 = m_rWW8Export.pO->size(); // Adr1 for adding the length + + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nLen ); + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aWW8_STD, pData ); + + nPOPosStdLen2 = nPOPosStdLen1 + 8; // Adr2 for adding of "end of upx" + + // write names + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rName.getLength() ); // length + SwWW8Writer::InsAsString16( *m_rWW8Export.pO, rName ); + m_rWW8Export.pO->push_back( sal_uInt8(0) ); // Despite P-String 0 at the end! +} + +void MSWordStyles::SetStyleDefaults( const SwFormat& rFormat, bool bPap ) +{ + const SwModify* pOldMod = m_rExport.m_pOutFormatNode; + m_rExport.m_pOutFormatNode = &rFormat; + bool aFlags[ RES_FRMATR_END - RES_CHRATR_BEGIN ]; + sal_uInt16 nStt, nEnd, n; + if( bPap ) + { + nStt = RES_PARATR_BEGIN; + nEnd = RES_FRMATR_END; + } + else + { + nStt = RES_CHRATR_BEGIN; + nEnd = RES_TXTATR_END; + } + + // dynamic defaults + const SfxItemPool& rPool = *rFormat.GetAttrSet().GetPool(); + for( n = nStt; n < nEnd; ++n ) + aFlags[ n - RES_CHRATR_BEGIN ] = nullptr != rPool.GetPoolDefaultItem( n ) + || SfxItemState::SET == m_rExport.m_pDoc->GetDfltTextFormatColl()->GetItemState( n, false ); + + // static defaults, that differs between WinWord and SO + if( bPap ) + { + aFlags[ static_cast< sal_uInt16 >(RES_PARATR_WIDOWS) - RES_CHRATR_BEGIN ] = true; + aFlags[ static_cast< sal_uInt16 >(RES_PARATR_HYPHENZONE) - RES_CHRATR_BEGIN ] = true; + aFlags[ static_cast< sal_uInt16 >(RES_FRAMEDIR) - RES_CHRATR_BEGIN ] = true; + } + else + { + aFlags[ RES_CHRATR_FONTSIZE - RES_CHRATR_BEGIN ] = true; + aFlags[ RES_CHRATR_LANGUAGE - RES_CHRATR_BEGIN ] = true; + } + + const SfxItemSet* pOldI = m_rExport.GetCurItemSet(); + m_rExport.SetCurItemSet( &rFormat.GetAttrSet() ); + + const bool* pFlags = aFlags + ( nStt - RES_CHRATR_BEGIN ); + for ( n = nStt; n < nEnd; ++n, ++pFlags ) + { + if ( *pFlags && !m_rExport.ignoreAttributeForStyleDefaults( n ) + && SfxItemState::SET != rFormat.GetItemState(n, false)) + { + //If we are a character property then see if it is one of the + //western/asian ones that must be collapsed together for export to + //word. If so default to the western variant. + if ( bPap || m_rExport.CollapseScriptsforWordOk( + i18n::ScriptType::LATIN, n) ) + { + m_rExport.AttrOutput().OutputItem( rFormat.GetFormatAttr( n ) ); + } + } + } + + m_rExport.SetCurItemSet( pOldI ); + m_rExport.m_pOutFormatNode = pOldMod; +} + +void WW8AttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) +{ + impl_SkipOdd( m_rWW8Export.pO, m_rWW8Export.pTableStrm->Tell() ); + + sal_uInt16 nLen = bParProp ? 2 : 0; // default length + m_nStyleLenPos = m_rWW8Export.pO->size(); // adding length + // Don't save pointer, because it + // changes by _grow! + + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nLen ); // Style-Len + + m_nStyleStartSize = m_rWW8Export.pO->size(); + + if ( bParProp ) + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nStyle ); // Style-Number +} + +void MSWordStyles::WriteProperties( const SwFormat* pFormat, bool bParProp, sal_uInt16 nPos, + bool bInsDefCharSiz ) +{ + m_rExport.AttrOutput().StartStyleProperties( bParProp, nPos ); + + OSL_ENSURE( m_rExport.m_pCurrentStyle == nullptr, "Current style not NULL" ); // set current style before calling out + m_rExport.m_pCurrentStyle = pFormat; + + m_rExport.OutputFormat( *pFormat, bParProp, !bParProp ); + + OSL_ENSURE( m_rExport.m_pCurrentStyle == pFormat, "current style was changed" ); + // reset current style... + m_rExport.m_pCurrentStyle = nullptr; + + if ( bInsDefCharSiz ) // not derived from other Style + SetStyleDefaults( *pFormat, bParProp ); + + m_rExport.AttrOutput().EndStyleProperties( bParProp ); +} + +void WW8AttributeOutput::EndStyleProperties( bool /*bParProp*/ ) +{ + sal_uInt16 nLen = m_rWW8Export.pO->size() - m_nStyleStartSize; + sal_uInt8* pUpxLen = m_rWW8Export.pO->data() + m_nStyleLenPos; // adding length + ShortToSVBT16( nLen, pUpxLen ); // add default length +} + +void MSWordStyles::GetStyleData( SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext ) +{ + bFormatColl = pFormat->Which() == RES_TXTFMTCOLL || pFormat->Which() == RES_CONDTXTFMTCOLL; + + // Default: none + nBase = 0xfff; + + // Derived from? + if ( !pFormat->IsDefault() ) + nBase = GetSlot( pFormat->DerivedFrom() ); + + SwFormat* pNext; + if ( bFormatColl ) + pNext = &static_cast<SwTextFormatColl*>(pFormat)->GetNextTextFormatColl(); + else + pNext = pFormat; // CharFormat: next CharFormat == self + + nNext = GetSlot( pNext ); +} + +void WW8AttributeOutput::DefaultStyle() +{ + m_rWW8Export.pTableStrm->WriteUInt16(0); // empty Style +} + +void MSWordStyles::OutputStyle(const SwNumRule* pNumRule, sal_uInt16 nPos) +{ + m_rExport.AttrOutput().StartStyle( pNumRule->GetName(), STYLE_TYPE_LIST, + /*nBase =*/ 0, /*nWwNext =*/ 0, /*nWWId =*/ 0, nPos, + /*bAutoUpdateFormat =*/ false ); + + m_rExport.AttrOutput().EndStyle(); +} + +// OutputStyle applies for TextFormatColls and CharFormats +void MSWordStyles::OutputStyle( SwFormat* pFormat, sal_uInt16 nPos ) +{ + if ( !pFormat ) + m_rExport.AttrOutput().DefaultStyle(); + else + { + bool bFormatColl; + sal_uInt16 nBase, nWwNext; + + GetStyleData( pFormat, bFormatColl, nBase, nWwNext ); + + OUString aName = pFormat->GetName(); + // We want to map LO's default style to Word's "Normal" style. + // Word looks for this specific style name when reading docx files. + // (It must be the English word regardless of language settings) + if ( nPos == 0 ) + { + assert( pFormat->GetPoolFormatId() == RES_POOLCOLL_STANDARD ); + aName = "Normal"; + } + else if (aName.equalsIgnoreAsciiCase("Normal")) + { + // If LO has a style named "Normal"(!) rename it to something unique + const OUString aBaseName = "LO-" + aName; + aName = aBaseName; + // Check if we still have a clash, in which case we add a suffix + for ( int nSuffix = 0; ; ++nSuffix ) { + bool clash=false; + for ( sal_uInt16 n = 1; n < m_nUsedSlots; ++n ) + if ( m_pFormatA[n] && + m_pFormatA[n]->GetName().equalsIgnoreAsciiCase(aName) ) + { + clash = true; + break; + } + if (!clash) + break; + // TODO: verify if we really need to increment nSuffix in 2 places + aName = aBaseName + OUString::number(++nSuffix); + } + } + else if (!bFormatColl && m_rExport.GetExportFormat() == MSWordExportBase::DOCX && + m_rExport.m_pStyles->GetStyleId(nPos).startsWith("ListLabel")) + { + // tdf#92335 don't export redundant DOCX import style "ListLabel" + return; + } + else if (aName.equalsIgnoreAsciiCase("Internet Link")) + { + aName = "Hyperlink"; + } + else if (aName.equalsIgnoreAsciiCase("Visited Internet Link")) + { + aName = "FollowedHyperlink"; + } + + m_rExport.AttrOutput().StartStyle( aName, (bFormatColl ? STYLE_TYPE_PARA : STYLE_TYPE_CHAR), + nBase, nWwNext, GetWWId( *pFormat ), nPos, + pFormat->IsAutoUpdateFormat() ); + + if ( bFormatColl ) + WriteProperties( pFormat, true, nPos, nBase==0xfff ); // UPX.papx + + WriteProperties( pFormat, false, nPos, bFormatColl && nBase==0xfff ); // UPX.chpx + + m_rExport.AttrOutput().EndStyle(); + } +} + +void WW8AttributeOutput::StartStyles() +{ + WW8Fib& rFib = *m_rWW8Export.pFib; + + sal_uLong nCurPos = m_rWW8Export.pTableStrm->Tell(); + if ( nCurPos & 1 ) // start on even + { + m_rWW8Export.pTableStrm->WriteChar( char(0) ); // Address + ++nCurPos; + } + rFib.m_fcStshfOrig = rFib.m_fcStshf = nCurPos; + m_nStyleCountPos = nCurPos + 2; // count is added later + + static sal_uInt8 aStShi[] = { + 0x12, 0x00, + 0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; + + m_rWW8Export.pTableStrm->WriteBytes(&aStShi, sizeof(aStShi)); +} + +void WW8AttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles ) +{ + WW8Fib& rFib = *m_rWW8Export.pFib; + + rFib.m_lcbStshfOrig = rFib.m_lcbStshf = m_rWW8Export.pTableStrm->Tell() - rFib.m_fcStshf; + SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, m_nStyleCountPos, nNumberOfStyles ); +} + +void MSWordStyles::OutputStylesTable() +{ + m_rExport.m_bStyDef = true; + + m_rExport.AttrOutput().StartStyles(); + + sal_uInt16 n; + // HACK + // Ms Office seems to have an internal limitation of 4091 styles + // and refuses to load .docx with more, even though the spec seems to allow that; + // so simply if there are more styles, don't export those + // Implementing check for all exports DOCX, DOC, RTF + sal_uInt16 const nLimit = MSWORD_MAX_STYLES_LIMIT; + m_nUsedSlots = std::min(nLimit, m_nUsedSlots); + + for ( n = 0; n < m_nUsedSlots; n++ ) + { + if (m_aNumRules.find(n) != m_aNumRules.end()) + OutputStyle(m_aNumRules[n], n); + else + OutputStyle( m_pFormatA[n], n ); + } + + m_rExport.AttrOutput().EndStyles( m_nUsedSlots ); + + m_rExport.m_bStyDef = false; +} + +const SwNumRule* MSWordStyles::GetSwNumRule(sal_uInt16 nId) const +{ + std::map<sal_uInt16, const SwNumRule*>::const_iterator it = m_aNumRules.find(nId); + assert(it != m_aNumRules.end()); + return it->second; +} + +// Fonts + +wwFont::wwFont(const OUString &rFamilyName, FontPitch ePitch, FontFamily eFamily, + rtl_TextEncoding eChrSet) + : mbAlt(false), mePitch(ePitch), meFamily(eFamily), meChrSet(eChrSet) +{ + FontMapExport aResult(rFamilyName); + msFamilyNm = aResult.msPrimary; + msAltNm = aResult.msSecondary; + if (!msAltNm.isEmpty() && msAltNm != msFamilyNm && + (msFamilyNm.getLength() + msAltNm.getLength() + 2 <= 65) ) + { + //max size of szFfn in 65 chars + mbAlt = true; + } + + maWW8_FFN[0] = static_cast<sal_uInt8>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm.getLength() ) )); + if (mbAlt) + maWW8_FFN[0] = static_cast< sal_uInt8 >(maWW8_FFN[0] + 2 * ( 1 + msAltNm.getLength())); + + sal_uInt8 aB = 0; + switch(ePitch) + { + case PITCH_VARIABLE: + aB |= 2; // aF.prg = 2 + break; + case PITCH_FIXED: + aB |= 1; + break; + default: // aF.prg = 0 : DEFAULT_PITCH (windows.h) + break; + } + aB |= 1 << 2; // aF.fTrueType = 1; don't know any better; + + switch(eFamily) + { + case FAMILY_ROMAN: + aB |= 1 << 4; // aF.ff = 1; + break; + case FAMILY_SWISS: + aB |= 2 << 4; // aF.ff = 2; + break; + case FAMILY_MODERN: + aB |= 3 << 4; // aF.ff = 3; + break; + case FAMILY_SCRIPT: + aB |= 4 << 4; // aF.ff = 4; + break; + case FAMILY_DECORATIVE: + aB |= 5 << 4; // aF.ff = 5; + break; + default: // aF.ff = 0; FF_DONTCARE (windows.h) + break; + } + maWW8_FFN[1] = aB; + + ShortToSVBT16( 400, &maWW8_FFN[2] ); // don't know any better + // 400 == FW_NORMAL (windows.h) + + //#i61927# For unicode fonts like Arial Unicode, Word 97+ sets the chs + //to SHIFTJIS presumably to capture that it's a multi-byte encoding font + //but Word95 doesn't do this, and sets it to 0 (ANSI), so we should do the + //same + maWW8_FFN[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet); + + if (mbAlt) + maWW8_FFN[5] = static_cast< sal_uInt8 >(msFamilyNm.getLength() + 1); +} + +void wwFont::Write(SvStream *pTableStrm) const +{ + pTableStrm->WriteBytes(maWW8_FFN, sizeof(maWW8_FFN)); // fixed part + // from Ver8 following two fields intersected, + // we ignore them. + //char panose[ 10 ]; // 0x6 PANOSE + //char fs[ 24 ]; // 0x10 FONTSIGNATURE + SwWW8Writer::FillCount(*pTableStrm, 0x22); + SwWW8Writer::WriteString16(*pTableStrm, msFamilyNm, true); + if (mbAlt) + SwWW8Writer::WriteString16(*pTableStrm, msAltNm, true); +} + +void wwFont::WriteDocx( DocxAttributeOutput* rAttrOutput ) const +{ + // no font embedding, panose id, subsetting, ... implemented + + if (!msFamilyNm.isEmpty()) + { + rAttrOutput->StartFont( msFamilyNm ); + + if ( mbAlt ) + rAttrOutput->FontAlternateName( msAltNm ); + rAttrOutput->FontCharset( sw::ms::rtl_TextEncodingToWinCharset( meChrSet ), meChrSet ); + rAttrOutput->FontFamilyType( meFamily ); + rAttrOutput->FontPitchType( mePitch ); + rAttrOutput->EmbedFont( msFamilyNm, meFamily, mePitch ); + + rAttrOutput->EndFont(); + } +} + +void wwFont::WriteRtf( const RtfAttributeOutput* rAttrOutput ) const +{ + rAttrOutput->FontFamilyType( meFamily, *this ); + rAttrOutput->FontPitchType( mePitch ); + rAttrOutput->FontCharset( + sw::ms::rtl_TextEncodingToWinCharsetRTF(msFamilyNm, msAltNm, meChrSet)); + rAttrOutput->StartFont( msFamilyNm ); + if ( mbAlt ) + rAttrOutput->FontAlternateName( msAltNm ); + rAttrOutput->EndFont(); +} + +bool operator<(const wwFont &r1, const wwFont &r2) +{ + int nRet = memcmp(r1.maWW8_FFN, r2.maWW8_FFN, sizeof(r1.maWW8_FFN)); + if (nRet == 0) + { + nRet = r1.msFamilyNm.compareTo(r2.msFamilyNm); + if (nRet == 0) + nRet = r1.msAltNm.compareTo(r2.msAltNm); + } + return nRet < 0; +} + +sal_uInt16 wwFontHelper::GetId(const wwFont &rFont) +{ + sal_uInt16 nRet; + std::map<wwFont, sal_uInt16>::const_iterator aIter = maFonts.find(rFont); + if (aIter != maFonts.end()) + nRet = aIter->second; + else + { + nRet = static_cast< sal_uInt16 >(maFonts.size()); + maFonts[rFont] = nRet; + } + return nRet; +} + +void wwFontHelper::InitFontTable(const SwDoc& rDoc) +{ + GetId(wwFont("Times New Roman", PITCH_VARIABLE, + FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252)); + + GetId(wwFont("Symbol", PITCH_VARIABLE, FAMILY_ROMAN, + RTL_TEXTENCODING_SYMBOL)); + + GetId(wwFont("Arial", PITCH_VARIABLE, FAMILY_SWISS, + RTL_TEXTENCODING_MS_1252)); + + const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT); + + GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(), + pFont->GetFamily(), pFont->GetCharSet())); + + const SfxItemPool& rPool = rDoc.GetAttrPool(); + if (nullptr != (pFont = rPool.GetPoolDefaultItem(RES_CHRATR_FONT))) + { + GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(), + pFont->GetFamily(), pFont->GetCharSet())); + } + + if (!bLoadAllFonts) + return; + + const sal_uInt16 aTypes[] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT, 0 }; + for (const sal_uInt16* pId = aTypes; *pId; ++pId) + { + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pId)) + { + pFont = static_cast<const SvxFontItem*>(pItem); + GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(), + pFont->GetFamily(), pFont->GetCharSet())); + } + } +} + +sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont) +{ + wwFont aFont(rFont.GetFamilyName(), rFont.GetPitch(), rFont.GetFamily(), + rFont.GetCharSet()); + return GetId(aFont); +} + +std::vector< const wwFont* > wwFontHelper::AsVector() const +{ + std::vector<const wwFont *> aFontList( maFonts.size() ); + + for ( const auto& aFont : maFonts ) + aFontList[aFont.second] = &aFont.first; + + return aFontList; +} + +void wwFontHelper::WriteFontTable(SvStream *pTableStream, WW8Fib& rFib) +{ + rFib.m_fcSttbfffn = pTableStream->Tell(); + /* + * Reserve some space to fill in the len after we know how big it is + */ + SwWW8Writer::WriteLong(*pTableStream, 0); + + /* + * Convert from fast insertion map to linear vector in the order that we + * want to write. + */ + std::vector<const wwFont *> aFontList( AsVector() ); + + /* + * Write them all to pTableStream + */ + for ( auto aFont : aFontList ) + aFont->Write(pTableStream); + + /* + * Write the position and len in the FIB + */ + rFib.m_lcbSttbfffn = pTableStream->Tell() - rFib.m_fcSttbfffn; + SwWW8Writer::WriteLong( *pTableStream, rFib.m_fcSttbfffn, maFonts.size()); +} + +void wwFontHelper::WriteFontTable( DocxAttributeOutput& rAttrOutput ) +{ + std::vector<const wwFont *> aFontList( AsVector() ); + + for ( auto aFont : aFontList ) + aFont->WriteDocx(&rAttrOutput); +} + +void wwFontHelper::WriteFontTable( const RtfAttributeOutput& rAttrOutput ) +{ + std::vector<const wwFont *> aFontList( AsVector() ); + + for ( auto aFont : aFontList ) + aFont->WriteRtf(&rAttrOutput); +} + +WW8_WrPlc0::WW8_WrPlc0( sal_uLong nOffset ) + : nOfs( nOffset ) +{ +} + +void WW8_WrPlc0::Append( sal_uLong nStartCpOrFc ) +{ + aPos.push_back( nStartCpOrFc - nOfs ); +} + +void WW8_WrPlc0::Write( SvStream& rStrm ) +{ + for( const auto& rPos : aPos ) + { + rStrm.WriteUInt32(rPos); + } +} + +// class MSWordSections : translate PageDescs into Sections +// also deals with header and footer + +MSWordSections::MSWordSections( MSWordExportBase& rExport ) + : mbDocumentIsProtected( false ) +{ + const SwSectionFormat *pFormat = nullptr; + rExport.m_pCurrentPageDesc = &rExport.m_pDoc->GetPageDesc( 0 ); + + const SfxPoolItem* pI; + const SwNode* pNd = rExport.m_pCurPam->GetContentNode(); + const SfxItemSet* pSet = pNd ? &static_cast<const SwContentNode*>(pNd)->GetSwAttrSet() : nullptr; + + sal_uLong nRstLnNum = pSet ? pSet->Get( RES_LINENUMBER ).GetStartValue() : 0; + + const SwTableNode* pTableNd = rExport.m_pCurPam->GetNode().FindTableNode(); + const SwSectionNode* pSectNd = nullptr; + if ( pTableNd ) + { + pSet = &pTableNd->GetTable().GetFrameFormat()->GetAttrSet(); + pNd = pTableNd; + } + else if (pNd && nullptr != ( pSectNd = pNd->FindSectionNode() )) + { + if ( SectionType::ToxHeader == pSectNd->GetSection().GetType() && + pSectNd->StartOfSectionNode()->IsSectionNode() ) + { + pSectNd = pSectNd->StartOfSectionNode()->GetSectionNode(); + } + + if ( SectionType::ToxContent == pSectNd->GetSection().GetType() ) + { + pNd = pSectNd; + rExport.m_pCurPam->GetPoint()->nNode = *pNd; + } + + if ( SectionType::Content == pSectNd->GetSection().GetType() ) + pFormat = pSectNd->GetSection().GetFormat(); + } + + // tdf#118393: FILESAVE: DOCX Export loses header/footer + rExport.m_bFirstTOCNodeWithSection = pSectNd && + ( SectionType::ToxHeader == pSectNd->GetSection().GetType() || + SectionType::ToxContent == pSectNd->GetSection().GetType() ); + + // Try to get page descriptor of the first node + if ( pSet && + SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, true, &pI ) && + static_cast<const SwFormatPageDesc*>(pI)->GetPageDesc() ) + { + AppendSection( *static_cast<const SwFormatPageDesc*>(pI), *pNd, pFormat, nRstLnNum ); + } + else + AppendSection( rExport.m_pCurrentPageDesc, pFormat, nRstLnNum, /*bIsFirstParagraph=*/true ); +} + +WW8_WrPlcSepx::WW8_WrPlcSepx( MSWordExportBase& rExport ) + : MSWordSections( rExport ) + , m_bHeaderFooterWritten( false ) +{ + // to be in sync with the AppendSection() call in the MSWordSections + // constructor + aCps.push_back( 0 ); +} + +MSWordSections::~MSWordSections() +{ +} + +WW8_WrPlcSepx::~WW8_WrPlcSepx() +{ +} + +bool MSWordSections::HeaderFooterWritten() +{ + return false; // only relevant for WW8 +} + +bool WW8_WrPlcSepx::HeaderFooterWritten() +{ + return m_bHeaderFooterWritten; +} + +sal_uInt16 MSWordSections::CurrentNumberOfColumns( const SwDoc &rDoc ) const +{ + OSL_ENSURE( !aSects.empty(), "no segment inserted yet" ); + if ( aSects.empty() ) + return 1; + + return NumberOfColumns( rDoc, aSects.back() ); +} + +sal_uInt16 MSWordSections::NumberOfColumns( const SwDoc &rDoc, const WW8_SepInfo& rInfo ) +{ + const SwPageDesc* pPd = rInfo.pPageDesc; + if ( !pPd ) + pPd = &rDoc.GetPageDesc( 0 ); + + const SfxItemSet &rSet = pPd->GetMaster().GetAttrSet(); + SfxItemSet aSet( *rSet.GetPool(), svl::Items<RES_COL, RES_COL>{} ); + aSet.SetParent( &rSet ); + + //0xffffffff, what the hell is going on with that!, fixme most terribly + if ( rInfo.pSectionFormat && reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rInfo.pSectionFormat ) + aSet.Put( rInfo.pSectionFormat->GetFormatAttr( RES_COL ) ); + + const SwFormatCol& rCol = aSet.Get( RES_COL ); + const SwColumns& rColumns = rCol.GetColumns(); + return rColumns.size(); +} + +const WW8_SepInfo* MSWordSections::CurrentSectionInfo() +{ + if ( !aSects.empty() ) + return &aSects.back(); + + return nullptr; +} + +void MSWordSections::AppendSection( const SwPageDesc* pPd, + const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo, bool bIsFirstParagraph ) +{ + if (HeaderFooterWritten()) { + return; // #i117955# prevent new sections in endnotes + } + aSects.emplace_back( pPd, pSectionFormat, nLnNumRestartNo, std::nullopt, nullptr, bIsFirstParagraph ); + NeedsDocumentProtected( aSects.back() ); +} + +void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwPageDesc* pPd, + const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo ) +{ + if (HeaderFooterWritten()) { + return; // #i117955# prevent new sections in endnotes + } + aCps.push_back( nStartCp ); + AppendSection( pPd, pSectionFormat, nLnNumRestartNo ); +} + +void MSWordSections::AppendSection( const SwFormatPageDesc& rPD, + const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo ) +{ + if (HeaderFooterWritten()) { + return; // #i117955# prevent new sections in endnotes + } + + WW8_SepInfo aI( rPD.GetPageDesc(), pSectionFormat, nLnNumRestartNo, rPD.GetNumOffset(), &rNd ); + + aSects.push_back( aI ); + NeedsDocumentProtected( aI ); +} + +void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPD, + const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo ) +{ + if (HeaderFooterWritten()) { + return; // #i117955# prevent new sections in endnotes + } + aCps.push_back( nStartCp ); + AppendSection( rPD, rNd, pSectionFormat, nLnNumRestartNo ); +} + +void WW8_WrPlcSepx::WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt ) +{ + sal_uInt8 nInfoFlags = 0; + const SwFootnoteInfo& rInfo = rWrt.m_pDoc->GetFootnoteInfo(); + if( !rInfo.m_aErgoSum.isEmpty() ) nInfoFlags |= 0x02; + if( !rInfo.m_aQuoVadis.isEmpty() ) nInfoFlags |= 0x04; + + sal_uInt8 nEmptyStt = 0; + if( nInfoFlags ) + { + pTextPos->Append( nCpStt ); // empty footnote separator + + if( 0x02 & nInfoFlags ) // Footnote continuation separator + { + pTextPos->Append( nCpStt ); + rWrt.WriteStringAsPara( rInfo.m_aErgoSum ); + rWrt.WriteStringAsPara( OUString() ); + nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + } + else + pTextPos->Append( nCpStt ); + + if( 0x04 & nInfoFlags ) // Footnote continuation notice + { + pTextPos->Append( nCpStt ); + rWrt.WriteStringAsPara( rInfo.m_aQuoVadis ); + rWrt.WriteStringAsPara( OUString() ); + nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + } + else + pTextPos->Append( nCpStt ); + + nEmptyStt = 3; + } + + while( 6 > nEmptyStt++ ) + pTextPos->Append( nCpStt ); + + // set the flags at the Dop right away + WW8Dop& rDop = *rWrt.pDop; + // Footnote Info + switch( rInfo.m_eNum ) + { + case FTNNUM_PAGE: rDop.rncFootnote = 2; break; + case FTNNUM_CHAPTER: rDop.rncFootnote = 1; break; + default: rDop.rncFootnote = 0; break; + } // rncFootnote + rDop.nfcFootnoteRef = WW8Export::GetNumId( rInfo.m_aFormat.GetNumberingType() ); + rDop.nFootnote = rInfo.m_nFootnoteOffset + 1; + rDop.fpc = rWrt.m_bFootnoteAtTextEnd ? 2 : 1; + + // Endnote Info + rDop.rncEdn = 0; // rncEdn: Don't Restart + const SwEndNoteInfo& rEndInfo = rWrt.m_pDoc->GetEndNoteInfo(); + rDop.nfcEdnRef = WW8Export::GetNumId( rEndInfo.m_aFormat.GetNumberingType() ); + rDop.nEdn = rEndInfo.m_nFootnoteOffset + 1; + rDop.epc = rWrt.m_bEndAtTextEnd ? 3 : 0; +} + +void MSWordSections::SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat, + sal_uInt8 nFlag ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rFormat.GetItemState(RES_HEADER, true, &pItem) + && static_cast<const SwFormatHeader*>(pItem)->IsActive() && + static_cast<const SwFormatHeader*>(pItem)->GetHeaderFormat() ) + rHeadFootFlags |= nFlag; +} + +void MSWordSections::SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat, + sal_uInt8 nFlag ) +{ + const SfxPoolItem* pItem; + if( SfxItemState::SET == rFormat.GetItemState(RES_FOOTER, true, &pItem) + && static_cast<const SwFormatFooter*>(pItem)->IsActive() && + static_cast<const SwFormatFooter*>(pItem)->GetFooterFormat() ) + rHeadFootFlags |= nFlag; +} + +void WW8_WrPlcSepx::OutHeaderFooter( WW8Export& rWrt, bool bHeader, + const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags, + sal_uInt8 nFlag, sal_uInt8 nBreakCode) +{ + if ( nFlag & nHFFlags ) + { + pTextPos->Append( rCpPos ); + rWrt.WriteHeaderFooterText( rFormat, bHeader); + rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains ) + rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + } + else + { + pTextPos->Append( rCpPos ); + if ((bHeader? rWrt.m_bHasHdr : rWrt.m_bHasFtr) && nBreakCode!=0) + { + rWrt.WriteStringAsPara( OUString() ); // Empty paragraph for empty header/footer + rWrt.WriteStringAsPara( OUString() ); // a CR that WW8 needs for end of the stream + rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + } + } +} + +void MSWordSections::NeedsDocumentProtected(const WW8_SepInfo &rInfo) +{ + if (rInfo.IsProtected()) + mbDocumentIsProtected = true; +} + +bool WW8_SepInfo::IsProtected() const +{ + bool bRet = false; + if ( + pSectionFormat && + (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != pSectionFormat) + ) + { + const SwSection *pSection = pSectionFormat->GetSection(); + if (pSection && pSection->IsProtect()) + { + bRet = true; + } + } + return bRet; +} + +void MSWordSections::CheckForFacinPg( const WW8Export& rWrt ) const +{ + // 2 values getting set + // Dop.fFacingPages == Header and Footer different + // Dop.fSwapBordersFacingPgs == mirrored borders + sal_uInt16 nEnd = 0; + for( const WW8_SepInfo& rSepInfo : aSects ) + { + if( !rSepInfo.pSectionFormat ) + { + const SwPageDesc* pPd = rSepInfo.pPageDesc; + if( pPd->GetFollow() && pPd != pPd->GetFollow() && + pPd->GetFollow()->GetFollow() == pPd->GetFollow() && + rSepInfo.pPDNd && + pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) + // so this is first page and subsequent, so only respect follow + pPd = pPd->GetFollow(); + + // left-/right chain of pagedescs ? + else if( !( 1 & nEnd ) && + pPd->GetFollow() && pPd != pPd->GetFollow() && + pPd->GetFollow()->GetFollow() == pPd && + (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) && + UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) || + ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) && + UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) )) + { + rWrt.pDop->fFacingPages = rWrt.pDop->fMirrorMargins = true; + nEnd |= 1; + } + + if( !( 1 & nEnd ) && + ( !pPd->IsHeaderShared() || !pPd->IsFooterShared() )) + { + rWrt.pDop->fFacingPages = true; + nEnd |= 1; + } + if( !( 2 & nEnd ) && + UseOnPage::Mirror == ( UseOnPage::Mirror & pPd->ReadUseOn() )) + { + rWrt.pDop->fSwapBordersFacingPgs = + rWrt.pDop->fMirrorMargins = true; + nEnd |= 2; + } + + if( 3 == nEnd ) + break; // We do not need to go any further + } + } +} + +bool MSWordSections::HasBorderItem( const SwFormat& rFormat ) +{ + const SfxPoolItem* pItem; + return SfxItemState::SET == rFormat.GetItemState(RES_BOX, true, &pItem) && + ( static_cast<const SvxBoxItem*>(pItem)->GetTop() || + static_cast<const SvxBoxItem*>(pItem)->GetBottom() || + static_cast<const SvxBoxItem*>(pItem)->GetLeft() || + static_cast<const SvxBoxItem*>(pItem)->GetRight() ); +} + +void WW8AttributeOutput::StartSection() +{ + m_rWW8Export.pO->clear(); +} + +void WW8AttributeOutput::SectFootnoteEndnotePr() +{ + const SwFootnoteInfo& rInfo = m_rWW8Export.m_pDoc->GetFootnoteInfo(); + const SwEndNoteInfo& rEndNoteInfo = m_rWW8Export.m_pDoc->GetEndNoteInfo(); + m_rWW8Export.InsUInt16( NS_sprm::sprmSRncFtn ); + switch( rInfo.m_eNum ) + { + case FTNNUM_PAGE: m_rWW8Export.pO->push_back( sal_uInt8/*rncRstPage*/ (2) ); break; + case FTNNUM_CHAPTER: m_rWW8Export.pO->push_back( sal_uInt8/*rncRstSect*/ (1) ); break; + default: m_rWW8Export.pO->push_back( sal_uInt8/*rncCont*/ (0) ); break; + } + + m_rWW8Export.InsUInt16(NS_sprm::sprmSNfcFtnRef); + sal_uInt8 nId = WW8Export::GetNumId(rInfo.m_aFormat.GetNumberingType()); + SwWW8Writer::InsUInt16(*m_rWW8Export.pO, nId); + m_rWW8Export.InsUInt16(NS_sprm::sprmSNfcEdnRef); + nId = WW8Export::GetNumId(rEndNoteInfo.m_aFormat.GetNumberingType()); + SwWW8Writer::InsUInt16(*m_rWW8Export.pO, nId); +} + +void WW8AttributeOutput::SectionFormProtection( bool bProtected ) +{ + //If the document is to be exported as protected, then if a segment + //is not protected, set the unlocked flag + if ( m_rWW8Export.pSepx->DocumentIsProtected() && !bProtected ) + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSFProtected ); + m_rWW8Export.pO->push_back( 1 ); + } +} + +void WW8AttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) +{ + // sprmSNLnnMod - activate Line Numbering and define Modulo + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSNLnnMod ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rLnNumInfo.GetCountBy() ); + + // sprmSDxaLnn - xPosition of Line Number + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSDxaLnn ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rLnNumInfo.GetPosFromLeft() ); + + // sprmSLnc - restart number: 0 per page, 1 per section, 2 never restart + if ( nRestartNo || !rLnNumInfo.IsRestartEachPage() ) + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSLnc ); + m_rWW8Export.pO->push_back( nRestartNo ? 1 : 2 ); + } + + // sprmSLnnMin - Restart the Line Number with given value + if ( nRestartNo ) + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSLnnMin ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, static_cast<sal_uInt16>(nRestartNo) - 1 ); + } +} + +void WW8AttributeOutput::SectionTitlePage() +{ + // sprmSFTitlePage + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSFTitlePage ); + m_rWW8Export.pO->push_back( 1 ); +} + +void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, const SwFrameFormat* pPdFirstPgFormat ) +{ + // write border of page + sal_uInt16 nPgBorder = MSWordSections::HasBorderItem( *pPdFormat ) ? 0 : USHRT_MAX; + if ( pPdFormat != pPdFirstPgFormat ) + { + if ( MSWordSections::HasBorderItem( *pPdFirstPgFormat ) ) + { + if ( USHRT_MAX == nPgBorder ) + { + nPgBorder = 1; + // only the first page outlined -> Get the BoxItem from the correct format + m_rWW8Export.m_pISet = &pPdFirstPgFormat->GetAttrSet(); + OutputItem( pPdFirstPgFormat->GetFormatAttr( RES_BOX ) ); + } + } + else if ( !nPgBorder ) + nPgBorder = 2; + } + + // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom + if (m_bFromEdge) + nPgBorder |= (1<<5); + + if ( USHRT_MAX != nPgBorder ) + { + // write the Flag and Border Attribute + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSPgbProp ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nPgBorder ); + } +} + +void WW8AttributeOutput::SectionBiDi( bool bBiDi ) +{ + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSFBiDi ); + m_rWW8Export.pO->push_back( bBiDi? 1: 0 ); +} + +void WW8AttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) +{ + // sprmSNfcPgn + sal_uInt8 nb = WW8Export::GetNumId( nNumType ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSNfcPgn ); + m_rWW8Export.pO->push_back( nb ); + + if ( oPageRestartNumber ) + { + // sprmSFPgnRestart + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSFPgnRestart ); + m_rWW8Export.pO->push_back( 1 ); + + // sprmSPgnStart + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSPgnStart97 ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, *oPageRestartNumber ); + } +} + +void WW8AttributeOutput::SectionType( sal_uInt8 nBreakCode ) +{ + if ( 2 != nBreakCode ) // new page is the default + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSBkc ); + m_rWW8Export.pO->push_back( nBreakCode ); + } +} + +void WW8Export::SetupSectionPositions( WW8_PdAttrDesc* pA ) +{ + if ( !pA ) + return; + + if ( !pO->empty() ) // are there attributes ? + { + pA->m_nLen = pO->size(); + pA->m_pData.reset(new sal_uInt8 [pO->size()]); + // store for later + memcpy( pA->m_pData.get(), pO->data(), pO->size() ); + pO->clear(); // clear HdFt-Text + } + else // no attributes there + { + pA->m_pData.reset(); + pA->m_nLen = 0; + } +} + +void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA ) +{ + if ( drawing::TextVerticalAdjust_TOP != nVA ) // top alignment is the default + { + sal_uInt8 nMSVA = 0; + switch( nVA ) + { + case drawing::TextVerticalAdjust_CENTER: + nMSVA = 1; + break; + case drawing::TextVerticalAdjust_BOTTOM: //Writer = 2, Word = 3 + nMSVA = 3; + break; + case drawing::TextVerticalAdjust_BLOCK: //Writer = 3, Word = 2 + nMSVA = 2; + break; + default: + break; + } + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmSVjc ); + m_rWW8Export.pO->push_back( nMSVA ); + } +} + +void WW8Export::WriteHeadersFooters( sal_uInt8 nHeadFootFlags, + const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode ) +{ + sal_uLong nCpPos = Fc2Cp( Strm().Tell() ); + + IncrementHdFtIndex(); + if ( !(nHeadFootFlags & WW8_HEADER_EVEN) && pDop->fFacingPages ) + pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode ); + else + pSepx->OutHeaderFooter( *this, true, rLeftFormat, nCpPos, nHeadFootFlags, WW8_HEADER_EVEN, nBreakCode ); + IncrementHdFtIndex(); + pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode ); + + IncrementHdFtIndex(); + if ( !(nHeadFootFlags & WW8_FOOTER_EVEN) && pDop->fFacingPages ) + pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode ); + else + pSepx->OutHeaderFooter( *this, false, rLeftFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_EVEN, nBreakCode ); + IncrementHdFtIndex(); + pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode ); + + //#i24344# Drawing objects cannot be directly shared between main hd/ft + //and title hd/ft so we need to differentiate them + IncrementHdFtIndex(); + pSepx->OutHeaderFooter( *this, true, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_HEADER_FIRST, nBreakCode ); + pSepx->OutHeaderFooter( *this, false, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_FIRST, nBreakCode ); +} + +namespace +{ +/** + * Determines if the continuous section break we start should use page style properties (header, + * footer, margins) from the next page style of the previous section. + */ +bool UsePrevSectionNextStyle(sal_uInt8 nBreakCode, const SwPageDesc* pPd, + const WW8_SepInfo& rSepInfo) +{ + if (nBreakCode != 0) + { + // Not a continuous section break. + return false; + } + + if (!pPd->GetFollow()) + { + // Page style has no follow style. + return false; + } + + // We start a continuous section break without headers/footers. Possibly the importer had + // headers/footers for this section break and put them to the closest page break's page style's + // next page style. See "find a node in the section that has a page break" in writerfilter/. + // Try the last-in-practice paragraph of the previous section. + const SwSectionFormat* pSection = rSepInfo.pSectionFormat; + if (pSection == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1))) + { + return false; + } + + const SwNodeIndex* pSectionStart = pSection->GetContent().GetContentIdx(); + if (!pSectionStart) + { + return false; + } + + SwPaM aPaM(*pSectionStart); + aPaM.Move(fnMoveBackward); + if (!aPaM.GetNode().IsTextNode()) + { + return false; + } + + SwTextNode* pTextNode = aPaM.GetNode().GetTextNode(); + const SwAttrSet* pParaProps = &pTextNode->GetSwAttrSet(); + sal_uInt32 nCharHeight = pParaProps->GetSize().GetHeight(); + if (nCharHeight > 20) + { + return false; + } + + aPaM.Move(fnMoveBackward); + if (!aPaM.GetNode().IsTextNode()) + { + return false; + } + + pTextNode = aPaM.GetNode().GetTextNode(); + pParaProps = &pTextNode->GetSwAttrSet(); + return pParaProps->HasItem(RES_PAGEDESC); +} +} + +void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAttrDesc* pA ) +{ + const SwPageDesc* pPd = rSepInfo.pPageDesc; + + if ( rSepInfo.pSectionFormat && !pPd ) + pPd = &m_pDoc->GetPageDesc( 0 ); + + m_pCurrentPageDesc = pPd; + + if ( !pPd ) + return; + + bool bOldPg = m_bOutPageDescs; + m_bOutPageDescs = true; + + AttrOutput().StartSection(); + + AttrOutput().SectFootnoteEndnotePr(); + + // forms + AttrOutput().SectionFormProtection( rSepInfo.IsProtected() ); + + // line numbers + const SwLineNumberInfo& rLnNumInfo = m_pDoc->GetLineNumberInfo(); + if ( rLnNumInfo.IsPaintLineNumbers() ) + AttrOutput().SectionLineNumbering( rSepInfo.nLnNumRestartNo, rLnNumInfo ); + + /* sprmSBkc, break code: 0 No break, 1 New column + 2 New page, 3 Even page, 4 Odd page + */ + sal_uInt8 nBreakCode = 2; // default start new page + bool bOutPgDscSet = true, bLeftRightPgChain = false, bOutputStyleItemSet = false; + bool bEnsureHeaderFooterWritten = rSepInfo.pSectionFormat && rSepInfo.bIsFirstParagraph; + const SwFrameFormat* pPdFormat = &pPd->GetMaster(); + bool bUsePrevSectionNextStyle = false; + if ( rSepInfo.pSectionFormat ) + { + // if pSectionFormat is set, then there is a SectionNode + // valid pointer -> start Section , + // 0xfff -> Section terminated + nBreakCode = 0; // consecutive section + + if ( rSepInfo.pPDNd && rSepInfo.pPDNd->IsContentNode() ) + { + if ( !NoPageBreakSection( &rSepInfo.pPDNd->GetContentNode()->GetSwAttrSet() ) ) + { + nBreakCode = 2; + } + } + + if ( reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) == rSepInfo.pSectionFormat ) + bEnsureHeaderFooterWritten |= !rSepInfo.pPDNd && GetExportFormat() != ExportFormat::RTF; + else + { + if ( nBreakCode == 0 ) + bOutPgDscSet = false; + + // produce Itemset, which inherits PgDesk-Attr-Set: + // as child also the parent is searched if 'deep'-OutputItemSet + const SfxItemSet* pPdSet = &pPdFormat->GetAttrSet(); + + bUsePrevSectionNextStyle = GetExportFormat() == ExportFormat::DOCX + && UsePrevSectionNextStyle(nBreakCode, pPd, rSepInfo); + if (bUsePrevSectionNextStyle) + { + // Take page margins from the previous section's next style. + pPdSet = &pPd->GetFollow()->GetMaster().GetAttrSet(); + } + + SfxItemSet aSet( *pPdSet->GetPool(), pPdSet->GetRanges() ); + aSet.SetParent( pPdSet ); + + // at the child ONLY change column structure according to Sect-Attr. + + const SvxLRSpaceItem &rSectionLR = + ItemGet<SvxLRSpaceItem>( *(rSepInfo.pSectionFormat), RES_LR_SPACE ); + const SvxLRSpaceItem &rPageLR = + ItemGet<SvxLRSpaceItem>( *pPdFormat, RES_LR_SPACE ); + + SvxLRSpaceItem aResultLR( rPageLR.GetLeft() + + rSectionLR.GetLeft(), rPageLR.GetRight() + + rSectionLR.GetRight(), 0, 0, RES_LR_SPACE ); + //i120133: The Section width should consider section indent value. + if (rSectionLR.GetLeft()+rSectionLR.GetRight()!=0) + { + const SwFormatCol& rCol = dynamic_cast<const SwFormatCol&>(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL)); + SwFormatCol aCol(rCol); + aCol.SetAdjustValue(rSectionLR.GetLeft()+rSectionLR.GetRight()); + aSet.Put(aCol); + } + else + aSet.Put(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL)); + + aSet.Put( aResultLR ); + + // and write into the WW-File + const SfxItemSet* pOldI = m_pISet; + m_pISet = &aSet; + + // Switch off test on default item values, if page description + // set (value of <bOutPgDscSet>) isn't written. + AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet ); + bOutputStyleItemSet = true; + + //Cannot export as normal page framedir, as continuous sections + //cannot contain any grid settings like proper sections + AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) ); + + m_pISet = pOldI; + } + } + + // Libreoffice 4.0 introduces support for page styles (SwPageDesc) with + // a different header/footer for the first page. The same effect can be + // achieved by chaining two page styles together (SwPageDesc::GetFollow) + // which are identical except for header/footer. + // The latter method was previously used by the doc/docx import filter. + // In both of these cases, we emit a single Word section with different + // first page header/footer. + const SwFrameFormat* pPdFirstPgFormat = &pPd->GetFirstMaster(); + bool titlePage = !pPd->IsFirstShared(); + if ( bOutPgDscSet ) + { + // if a Follow is set and it does not point to itself, + // then there is a page chain. + // If this emulates a "first page", we can detect it here and write + // it as title page. + // With Left/Right changes it's different - we have to detect where + // the change of pages is, but here it's too late for that! + // tdf#101814 if there is already an explicit first-page, no point + // in checking heuristics here. + if ( !titlePage && pPd->GetFollow() && pPd != pPd->GetFollow() && + pPd->GetFollow()->GetFollow() == pPd->GetFollow() && + pPd->IsHeaderShared() && pPd->IsFooterShared() && + ( !rSepInfo.pPDNd || pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) ) + { + const SwPageDesc *pFollow = pPd->GetFollow(); + const SwFrameFormat& rFollowFormat = pFollow->GetMaster(); + if (sw::util::IsPlausableSingleWordSection(*pPdFirstPgFormat, rFollowFormat)) + { + if (rSepInfo.pPDNd) + pPdFirstPgFormat = pPd->GetPageFormatOfNode( *rSepInfo.pPDNd ); + else + pPdFirstPgFormat = &pPd->GetMaster(); + + m_pCurrentPageDesc = pPd = pFollow; + pPdFormat = &rFollowFormat; + + // has different headers/footers for the title page + titlePage = true; + } + } + + if( titlePage ) + AttrOutput().SectionTitlePage(); + + const SfxItemSet* pOldI = m_pISet; + + const SfxPoolItem* pItem; + if ( titlePage && SfxItemState::SET == + pPdFirstPgFormat->GetItemState( RES_PAPER_BIN, true, &pItem ) ) + { + m_pISet = &pPdFirstPgFormat->GetAttrSet(); + m_bOutFirstPage = true; + AttrOutput().OutputItem( *pItem ); + m_bOutFirstPage = false; + } + + // left-/right chain of pagedescs ? + if ( pPd->GetFollow() && pPd != pPd->GetFollow() && + pPd->GetFollow()->GetFollow() == pPd && + (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) && + UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) || + ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) && + UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) )) + { + bLeftRightPgChain = true; + + // which is the reference point? (left or right?) + // assume it is on the right side! + if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) ) + { + nBreakCode = 3; + pPd = pPd->GetFollow(); + pPdFormat = &pPd->GetMaster(); + } + else + nBreakCode = 4; + } + + m_pISet = &pPdFormat->GetAttrSet(); + if (!bOutputStyleItemSet) + { + if (titlePage) + { + m_pFirstPageFormat = pPdFirstPgFormat; + } + + AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false ); + + if (titlePage) + { + m_pFirstPageFormat = nullptr; + } + } + AttrOutput().SectionPageBorders( pPdFormat, pPdFirstPgFormat ); + m_pISet = pOldI; + + // then the rest of the settings from PageDesc + AttrOutput().SectionPageNumbering( pPd->GetNumType().GetNumberingType(), rSepInfo.oPgRestartNo ); + + // will it be only left or only right pages? + if ( 2 == nBreakCode ) + { + if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) ) + nBreakCode = 3; + else if ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) ) + nBreakCode = 4; + } + } + + AttrOutput().SectionType( nBreakCode ); + + if( rSepInfo.pPageDesc ) { + AttrOutput().TextVerticalAdjustment( rSepInfo.pPageDesc->GetVerticalAdjustment() ); + } + + // Header or Footer + sal_uInt8 nHeadFootFlags = 0; + + const SwFrameFormat* pPdLeftFormat = bLeftRightPgChain + ? &pPd->GetFollow()->GetMaster() + : &pPd->GetLeft(); + + // Ensure that headers are written if section is first paragraph + if ( nBreakCode != 0 || bEnsureHeaderFooterWritten ) + { + if ( titlePage ) + { + // there is a First Page: + MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_HEADER_FIRST ); + MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_FOOTER_FIRST ); + } + MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD ); + MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD ); + + if ( !pPd->IsHeaderShared() || bLeftRightPgChain ) + MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftFormat, WW8_HEADER_EVEN ); + + if ( !pPd->IsFooterShared() || bLeftRightPgChain ) + MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFormat, WW8_FOOTER_EVEN ); + } + + // binary filters only + SetupSectionPositions( pA ); + + /* + !!!!!!!!!!! + // borders at header and footer texts would be done like this: + // This should use something like pOut, + // which is repeated with every special text line. + const SwFrameFormat* pFFormat = rFt.GetFooterFormat(); + const SvxBoxItem& rBox = pFFormat->GetBox(false); + OutWW8_SwFormatBox1( m_rWW8Export.pOut, rBox, false); + !!!!!!!!!!! + You can turn this into paragraph attributes, which are then observed in each paragraph. + Applies to background / border. + !!!!!!!!!!! + */ + + const SwTextNode *pOldPageRoot = GetHdFtPageRoot(); + SetHdFtPageRoot( rSepInfo.pPDNd ? rSepInfo.pPDNd->GetTextNode() : nullptr ); + + if (bUsePrevSectionNextStyle && nHeadFootFlags == 0) + { + // Take headers/footers from the previous section's next style. + pPdFormat = &pPd->GetFollow()->GetMaster(); + MSWordSections::SetHeaderFlag(nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD); + MSWordSections::SetFooterFlag(nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD); + } + + WriteHeadersFooters( nHeadFootFlags, *pPdFormat, *pPdLeftFormat, *pPdFirstPgFormat, nBreakCode ); + + SetHdFtPageRoot( pOldPageRoot ); + + AttrOutput().EndSection(); + + // outside of the section properties again + m_bOutPageDescs = bOldPg; +} + +bool WW8_WrPlcSepx::WriteKFText( WW8Export& rWrt ) +{ + sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + + OSL_ENSURE( !pTextPos, "who set the pointer?" ); + pTextPos.reset( new WW8_WrPlc0( nCpStart ) ); + + WriteFootnoteEndText( rWrt, nCpStart ); + CheckForFacinPg( rWrt ); + + unsigned int nOldIndex = rWrt.GetHdFtIndex(); + rWrt.SetHdFtIndex( 0 ); + + for (const WW8_SepInfo & rSepInfo : aSects) + { + auto pAttrDesc = std::make_shared<WW8_PdAttrDesc>(); + m_SectionAttributes.push_back(pAttrDesc); + + rWrt.SectionProperties( rSepInfo, pAttrDesc.get() ); + + // FIXME: this writes the section properties, but not of all sections; + // it's possible that later in the document (e.g. in endnotes) sections + // are added, but they won't have their properties written here! + m_bHeaderFooterWritten = true; + } + rWrt.SetHdFtIndex( nOldIndex ); //0 + + if ( pTextPos->Count() ) + { + // HdFt available? + sal_uLong nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + pTextPos->Append( nCpEnd ); // End of last Header/Footer for PlcfHdd + + if ( nCpEnd > nCpStart ) + { + ++nCpEnd; + pTextPos->Append( nCpEnd + 1 ); // End of last Header/Footer for PlcfHdd + + rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains ) + } + rWrt.m_pFieldHdFt->Finish( nCpEnd, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote ); + rWrt.pFib->m_ccpHdr = nCpEnd - nCpStart; + } + else + { + pTextPos.reset(); + } + + return rWrt.pFib->m_ccpHdr != 0; +} + +void WW8_WrPlcSepx::WriteSepx( SvStream& rStrm ) const +{ + OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(aSects.size()) + , "WriteSepx(): arrays out of sync!"); + for (const auto & rSectionAttribute : m_SectionAttributes) // all sections + { + WW8_PdAttrDesc *const pA = rSectionAttribute.get(); + if (pA->m_nLen && pA->m_pData != nullptr) + { + pA->m_nSepxFcPos = rStrm.Tell(); + rStrm.WriteUInt16(pA->m_nLen); + rStrm.WriteBytes(pA->m_pData.get(), pA->m_nLen); + } + } +} + +void WW8_WrPlcSepx::WritePlcSed( WW8Export& rWrt ) const +{ + OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(aSects.size()) + , "WritePlcSed(): arrays out of sync!"); + OSL_ENSURE( aCps.size() == aSects.size() + 1, "WrPlcSepx: DeSync" ); + sal_uLong nFcStart = rWrt.pTableStrm->Tell(); + + for( decltype(aSects)::size_type i = 0; i <= aSects.size(); i++ ) + { + sal_uInt32 nP = aCps[i]; + rWrt.pTableStrm->WriteUInt32(nP); + } + + static WW8_SED aSed = {{4, 0},{0, 0, 0, 0},{0, 0},{0xff, 0xff, 0xff, 0xff}}; + + for (const auto & rSectionAttribute : m_SectionAttributes) + { + // Sepx-Pos + UInt32ToSVBT32( rSectionAttribute->m_nSepxFcPos, aSed.fcSepx ); + rWrt.pTableStrm->WriteBytes(&aSed, sizeof(aSed)); + } + rWrt.pFib->m_fcPlcfsed = nFcStart; + rWrt.pFib->m_lcbPlcfsed = rWrt.pTableStrm->Tell() - nFcStart; +} + +void WW8_WrPlcSepx::WritePlcHdd( WW8Export& rWrt ) const +{ + // Don't write out the PlcfHdd if ccpHdd is 0: it's a validation failure case. + if( rWrt.pFib->m_ccpHdr != 0 && pTextPos && pTextPos->Count() ) + { + rWrt.pFib->m_fcPlcfhdd = rWrt.pTableStrm->Tell(); + pTextPos->Write( *rWrt.pTableStrm ); // Plc0 + rWrt.pFib->m_lcbPlcfhdd = rWrt.pTableStrm->Tell() - + rWrt.pFib->m_fcPlcfhdd; + } +} + +void MSWordExportBase::WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader ) +{ + const SwFormatContent *pContent; + if ( bHeader ) + { + m_bHasHdr = true; + const SwFormatHeader& rHd = rFormat.GetHeader(); + OSL_ENSURE( rHd.GetHeaderFormat(), "Header text is not here" ); + pContent = &rHd.GetHeaderFormat()->GetContent(); + } + else + { + m_bHasFtr = true; + const SwFormatFooter& rFt = rFormat.GetFooter(); + OSL_ENSURE( rFt.GetFooterFormat(), "Footer text is not here" ); + pContent = &rFt.GetFooterFormat()->GetContent(); + } + + const SwNodeIndex* pSttIdx = pContent->GetContentIdx(); + + if ( pSttIdx ) + { + SwNodeIndex aIdx( *pSttIdx, 1 ), + aEnd( *pSttIdx->GetNode().EndOfSectionNode() ); + sal_uLong nStart = aIdx.GetIndex(); + sal_uLong nEnd = aEnd.GetIndex(); + + // range, i.e. valid node + if ( nStart < nEnd ) + { + bool bOldKF = m_bOutKF; + m_bOutKF = true; + WriteSpecialText( nStart, nEnd, TXT_HDFT ); + m_bOutKF = bOldKF; + } + else + pSttIdx = nullptr; + } + + if ( !pSttIdx ) + { + // there is no Header/Footer, but a CR is still necessary + OSL_ENSURE( pSttIdx, "Header/Footer text is not really present" ); + AttrOutput().EmptyParagraph(); + } +} + +// class WW8_WrPlcFootnoteEdn : Collect the Footnotes and Endnotes and output their text +// and Plcs at the end of the document. +// WW8_WrPlcFootnoteEdn is the class for Footnotes and Endnotes + +WW8_WrPlcSubDoc::WW8_WrPlcSubDoc() +{ +} + +WW8_WrPlcSubDoc::~WW8_WrPlcSubDoc() +{ +} + +void WW8_WrPlcFootnoteEdn::Append( WW8_CP nCp, const SwFormatFootnote& rFootnote ) +{ + aCps.push_back( nCp ); + aContent.push_back( &rFootnote ); +} + +WW8_Annotation::WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd) + : + maDateTime( DateTime::EMPTY ), + m_nRangeStart(nRangeStart), + m_nRangeEnd(nRangeEnd) +{ + mpRichText = pPostIt->GetTextObject(); + if (!mpRichText) + msSimpleText = pPostIt->GetText(); + msOwner = pPostIt->GetPar1(); + m_sInitials = pPostIt->GetInitials(); + maDateTime = DateTime(pPostIt->GetDate(), pPostIt->GetTime()); +} + +WW8_Annotation::WW8_Annotation(const SwRedlineData* pRedline) + : + mpRichText(nullptr), + msSimpleText(pRedline->GetComment()), + msOwner(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())), + maDateTime(pRedline->GetTimeStamp()), + m_nRangeStart(0), + m_nRangeEnd(0) +{ +} + +bool WW8_Annotation::HasRange() const +{ + if (m_nRangeStart != m_nRangeEnd) + { + return true; + } + + return !m_bIgnoreEmpty; +} + +void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp, + bool bIgnoreEmpty) +{ + m_aRangeStartPositions[rName] = std::make_pair(nStartCp, bIgnoreEmpty); +} + +void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwPostItField *pPostIt ) +{ + aCps.push_back( nCp ); + WW8_Annotation* p; + if( m_aRangeStartPositions.find(pPostIt->GetName()) != m_aRangeStartPositions.end() ) + { + auto [nStartCp, bIgnoreEmpty] = m_aRangeStartPositions[pPostIt->GetName()]; + p = new WW8_Annotation(pPostIt, nStartCp, nCp); + p->m_bIgnoreEmpty = bIgnoreEmpty; + m_aRangeStartPositions.erase(pPostIt->GetName()); + } + else + { + p = new WW8_Annotation(pPostIt, nCp, nCp); + } + aContent.push_back( p ); +} + +void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwRedlineData *pRedline ) +{ + maProcessedRedlines.insert(pRedline); + aCps.push_back( nCp ); + WW8_Annotation* p = new WW8_Annotation(pRedline); + aContent.push_back( p ); +} + +bool WW8_WrPlcAnnotations::IsNewRedlineComment( const SwRedlineData *pRedline ) +{ + return maProcessedRedlines.find(pRedline) == maProcessedRedlines.end(); +} + +WW8_WrPlcAnnotations::~WW8_WrPlcAnnotations() +{ + for(const void * p : aContent) + delete static_cast<WW8_Annotation const *>(p); +} + +bool WW8_WrPlcSubDoc::WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp, + WW8_CP& rCount ) +{ + sal_uInt16 nLen = aContent.size(); + if ( !nLen ) + return false; + + sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + pTextPos.reset( new WW8_WrPlc0( nCpStart ) ); + sal_uInt16 i; + + switch ( nTTyp ) + { + case TXT_ATN: + for ( i = 0; i < nLen; i++ ) + { + // beginning for PlcfAtnText + pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() )); + + rWrt.WritePostItBegin(); + const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]); + if (rAtn.mpRichText) + rWrt.WriteOutliner(*rAtn.mpRichText, nTTyp); + else + { + OUString sText(rAtn.msSimpleText); + rWrt.WriteStringAsPara(sText.replace(0x0A, 0x0B)); + } + } + break; + + case TXT_TXTBOX: + case TXT_HFTXTBOX: + for ( i = 0; i < nLen; i++ ) + { + // textbox content + WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + aCps.insert( aCps.begin()+i, nCP ); + pTextPos->Append( nCP ); + + if( aContent[ i ] != nullptr ) + { + // is it a writer or sdr - textbox? + const SdrObject& rObj = *static_cast<SdrObject const *>(aContent[ i ]); + if (rObj.GetObjInventor() == SdrInventor::FmForm) + { + sal_uInt8 nOldTyp = rWrt.m_nTextTyp; + rWrt.m_nTextTyp = nTTyp; + rWrt.GetOCXExp().ExportControl(rWrt, dynamic_cast<const SdrUnoObj&>(rObj)); + rWrt.m_nTextTyp = nOldTyp; + } + else if( dynamic_cast<const SdrTextObj*>( &rObj) != nullptr ) + rWrt.WriteSdrTextObj(dynamic_cast<const SdrTextObj&>(rObj), nTTyp); + else + { + const SwFrameFormat* pFormat = ::FindFrameFormat( &rObj ); + OSL_ENSURE( pFormat, "where is the format?" ); + + const SwNodeIndex* pNdIdx = pFormat->GetContent().GetContentIdx(); + OSL_ENSURE( pNdIdx, "where is the StartNode of the Textbox?" ); + rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1, + pNdIdx->GetNode().EndOfSectionIndex(), + nTTyp ); + { + SwNodeIndex aContentIdx = *pNdIdx; + ++aContentIdx; + if ( aContentIdx.GetNode().IsTableNode() ) + { + bool bContainsOnlyTables = true; + do { + aContentIdx = *(aContentIdx.GetNode().EndOfSectionNode()); + ++aContentIdx; + if ( !aContentIdx.GetNode().IsTableNode() && + aContentIdx.GetIndex() != pNdIdx->GetNode().EndOfSectionIndex() ) + { + bContainsOnlyTables = false; + } + } while ( aContentIdx.GetNode().IsTableNode() ); + if ( bContainsOnlyTables ) + { + // Additional paragraph containing a space to + // assure that by WW created RTF from written WW8 + // does not crash WW. + rWrt.WriteStringAsPara( " " ); + } + } + } + } + } + else if (i < aSpareFormats.size() && aSpareFormats[i]) + { + const SwFrameFormat& rFormat = *aSpareFormats[i]; + const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx(); + rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1, + pNdIdx->GetNode().EndOfSectionIndex(), nTTyp ); + } + + // CR at end of one textbox text ( otherwise WW gpft :-( ) + rWrt.WriteStringAsPara( OUString() ); + } + break; + + case TXT_EDN: + case TXT_FTN: + for ( i = 0; i < nLen; i++ ) + { + // beginning for PlcfFootnoteText/PlcfEdnText + pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() )); + + // Note content + const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(aContent[ i ]); + rWrt.WriteFootnoteBegin( *pFootnote ); + const SwNodeIndex* pIdx = pFootnote->GetTextFootnote()->GetStartNode(); + OSL_ENSURE( pIdx, "Where is the start node of Foot-/Endnote?" ); + rWrt.WriteSpecialText( pIdx->GetIndex() + 1, + pIdx->GetNode().EndOfSectionIndex(), + nTTyp ); + } + break; + + default: + OSL_ENSURE( false, "What kind of SubDocType is that?" ); + } + + pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() )); + // CR to the end ( otherwise WW complains ) + rWrt.WriteStringAsPara( OUString() ); + + WW8_CP nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() ); + pTextPos->Append( nCpEnd ); + rCount = nCpEnd - nCpStart; + + return ( rCount != 0 ); +} + +static bool lcl_AuthorComp( const std::pair<OUString,OUString>& aFirst, const std::pair<OUString,OUString>& aSecond) +{ + return aFirst.first < aSecond.first; +} + +static bool lcl_PosComp( const std::pair<WW8_CP, int>& aFirst, const std::pair<WW8_CP, int>& aSecond) +{ + return aFirst.first < aSecond.first; +} + +void WW8_WrPlcSubDoc::WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp, + WW8_FC& rTextStart, sal_Int32& rTextCount, WW8_FC& rRefStart, sal_Int32& rRefCount ) const +{ + + sal_uLong nFcStart = rWrt.pTableStrm->Tell(); + sal_uInt16 nLen = aCps.size(); + if ( !nLen ) + return; + + OSL_ENSURE( aCps.size() + 2 == pTextPos->Count(), "WritePlc: DeSync" ); + + std::vector<std::pair<OUString,OUString> > aStrArr; + WW8Fib& rFib = *rWrt.pFib; // n+1-th CP-Pos according to the manual + bool bWriteCP = true; + + switch ( nTTyp ) + { + case TXT_ATN: + { + std::vector< std::pair<WW8_CP, int> > aRangeStartPos; // The second of the pair is the original index before sorting. + std::vector< std::pair<WW8_CP, int> > aRangeEndPos; // Same, so we can map between the indexes before/after sorting. + std::map<int, int> aAtnStartMap; // Maps from annotation index to start index. + std::map<int, int> aStartAtnMap; // Maps from start index to annotation index. + std::map<int, int> aStartEndMap; // Maps from start index to end index. + // then write first the GrpXstAtnOwners + int nIdx = 0; + for ( sal_uInt16 i = 0; i < nLen; ++i ) + { + const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]); + aStrArr.emplace_back(rAtn.msOwner,rAtn.m_sInitials); + // record start and end positions for ranges + if (rAtn.HasRange()) + { + aRangeStartPos.emplace_back(rAtn.m_nRangeStart, nIdx); + aRangeEndPos.emplace_back(rAtn.m_nRangeEnd, nIdx); + ++nIdx; + } + } + + //sort and remove duplicates + std::sort(aStrArr.begin(), aStrArr.end(),&lcl_AuthorComp); + auto aIter = std::unique(aStrArr.begin(), aStrArr.end()); + aStrArr.erase(aIter, aStrArr.end()); + + // Also sort the start and end positions. We need to reference + // the start index in the annotation table and also need to + // reference the end index in the start table, so build a map + // that knows what index to reference, after sorting. + std::sort(aRangeStartPos.begin(), aRangeStartPos.end(), &lcl_PosComp); + for (decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i) + { + aAtnStartMap[aRangeStartPos[i].second] = i; + aStartAtnMap[i] = aRangeStartPos[i].second; + } + std::sort(aRangeEndPos.begin(), aRangeEndPos.end(), &lcl_PosComp); + for (decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i) + aStartEndMap[aAtnStartMap[ aRangeEndPos[i].second ]] = i; + + for ( decltype(aStrArr)::size_type i = 0; i < aStrArr.size(); ++i ) + { + const OUString& sAuthor = aStrArr[i].first; + SwWW8Writer::WriteShort(*rWrt.pTableStrm, sAuthor.getLength()); + SwWW8Writer::WriteString16(*rWrt.pTableStrm, sAuthor, + false); + } + + rFib.m_fcGrpStAtnOwners = nFcStart; + nFcStart = rWrt.pTableStrm->Tell(); + rFib.m_lcbGrpStAtnOwners = nFcStart - rFib.m_fcGrpStAtnOwners; + + // Commented text ranges + if( !aRangeStartPos.empty() ) + { + // Commented text ranges starting positions (Plcfbkf.aCP) + rFib.m_fcPlcfAtnbkf = nFcStart; + for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i ) + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, aRangeStartPos[i].first ); + } + SwWW8Writer::WriteLong( *rWrt.pTableStrm, rFib.m_ccpText + 1); + + // Commented text ranges additional information (Plcfbkf.aFBKF) + for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i ) + { + SwWW8Writer::WriteShort( *rWrt.pTableStrm, aStartEndMap[i] ); // FBKF.ibkl + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); // FBKF.bkc + } + + nFcStart = rWrt.pTableStrm->Tell(); + rFib.m_lcbPlcfAtnbkf = nFcStart - rFib.m_fcPlcfAtnbkf; + + // Commented text ranges ending positions (PlcfBkl.aCP) + rFib.m_fcPlcfAtnbkl = nFcStart; + for ( decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i ) + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, aRangeEndPos[i].first ); + } + SwWW8Writer::WriteLong( *rWrt.pTableStrm, rFib.m_ccpText + 1); + + nFcStart = rWrt.pTableStrm->Tell(); + rFib.m_lcbPlcfAtnbkl = nFcStart - rFib.m_fcPlcfAtnbkl; + + // Commented text ranges as bookmarks (SttbfAtnBkmk) + rFib.m_fcSttbfAtnbkmk = nFcStart; + SwWW8Writer::WriteShort( *rWrt.pTableStrm, sal_Int16(sal_uInt16(0xFFFF)) ); // SttbfAtnBkmk.fExtend + SwWW8Writer::WriteShort( *rWrt.pTableStrm, aRangeStartPos.size() ); // SttbfAtnBkmk.cData + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0xA ); // SttbfAtnBkmk.cbExtra + + for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i ) + { + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); // SttbfAtnBkmk.cchData + // One ATNBE structure for all text ranges + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0x0100 ); // ATNBE.bmc + SwWW8Writer::WriteLong( *rWrt.pTableStrm, aStartAtnMap[i] ); // ATNBE.lTag + SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 ); // ATNBE.lTagOld + } + + nFcStart = rWrt.pTableStrm->Tell(); + rFib.m_lcbSttbfAtnbkmk = nFcStart - rFib.m_fcSttbfAtnbkmk; + } + + // Write the extended >= Word XP ATRD records + for( sal_uInt16 i = 0; i < nLen; ++i ) + { + const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]); + + sal_uInt32 nDTTM = sw::ms::DateTime2DTTM(rAtn.maDateTime); + + SwWW8Writer::WriteLong( *rWrt.pTableStrm, nDTTM ); + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); + } + + rFib.m_fcAtrdExtra = nFcStart; + nFcStart = rWrt.pTableStrm->Tell(); + rFib.m_lcbAtrdExtra = nFcStart - rFib.m_fcAtrdExtra; + rFib.m_fcHplxsdr = 0x01010002; //WTF, but apparently necessary + rFib.m_lcbHplxsdr = 0; + } + break; + case TXT_TXTBOX: + case TXT_HFTXTBOX: + { + pTextPos->Write( *rWrt.pTableStrm ); + const std::vector<sal_uInt32>* pShapeIds = GetShapeIdArr(); + OSL_ENSURE( pShapeIds, "Where are the ShapeIds?" ); + + for ( sal_uInt16 i = 0; i < nLen; ++i ) + { + // write textbox story - FTXBXS + // is it a writer or sdr - textbox? + const SdrObject* pObj = static_cast<SdrObject const *>(aContent[ i ]); + sal_Int32 nCnt = 1; + if (dynamic_cast< const SdrTextObj *>( pObj )) + { + // find the "highest" SdrObject of this + const SwFrameFormat& rFormat = *::FindFrameFormat( pObj ); + + const SwFormatChain* pChn = &rFormat.GetChain(); + while ( pChn->GetNext() ) + { + // has a chain? + // then calc the cur pos in the chain + ++nCnt; + pChn = &pChn->GetNext()->GetChain(); + } + } + if( nullptr == pObj ) + { + if (i < aSpareFormats.size() && aSpareFormats[i]) + { + const SwFrameFormat& rFormat = *aSpareFormats[i]; + + const SwFormatChain* pChn = &rFormat.GetChain(); + while( pChn->GetNext() ) + { + // has a chain? + // then calc the cur pos in the chain + ++nCnt; + pChn = &pChn->GetNext()->GetChain(); + } + } + } + // long cTxbx / iNextReuse + SwWW8Writer::WriteLong( *rWrt.pTableStrm, nCnt ); + // long cReusable + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); + // short fReusable + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); + // long reserved + SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 ); + // long lid + SwWW8Writer::WriteLong( *rWrt.pTableStrm, + (*pShapeIds)[i]); + // long txidUndo + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); + } + SwWW8Writer::FillCount( *rWrt.pTableStrm, 22 ); + bWriteCP = false; + } + break; + } + + if ( bWriteCP ) + { + // write CP Positions + for ( sal_uInt16 i = 0; i < nLen; i++ ) + SwWW8Writer::WriteLong( *rWrt.pTableStrm, aCps[ i ] ); + + // n+1-th CP-Pos according to the manual + SwWW8Writer::WriteLong( *rWrt.pTableStrm, + rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpEdn + + rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1 ); + + if ( TXT_ATN == nTTyp ) + { + sal_uInt16 nlTag = 0; + for ( sal_uInt16 i = 0; i < nLen; ++i ) + { + const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]); + + //aStrArr is sorted + auto aIter = std::lower_bound(aStrArr.begin(), + aStrArr.end(), std::pair<OUString,OUString>(rAtn.msOwner,OUString()), + &lcl_AuthorComp); + OSL_ENSURE(aIter != aStrArr.end() && aIter->first == rAtn.msOwner, + "Impossible"); + sal_uInt16 nFndPos = static_cast< sal_uInt16 >(aIter - aStrArr.begin()); + OUString sInitials( aIter->second ); + sal_uInt8 nInitialsLen = static_cast<sal_uInt8>(sInitials.getLength()); + if ( nInitialsLen > 9 ) + { + sInitials = sInitials.copy( 0, 9 ); + nInitialsLen = 9; + } + + // xstUsrInitl[ 10 ] pascal-style String holding initials + // of annotation author + SwWW8Writer::WriteShort(*rWrt.pTableStrm, nInitialsLen); + SwWW8Writer::WriteString16(*rWrt.pTableStrm, sInitials, + false); + SwWW8Writer::FillCount( *rWrt.pTableStrm, + (9 - nInitialsLen) * 2 ); + + // documents layout of WriteShort's below: + + // SVBT16 ibst; // index into GrpXstAtnOwners + // SVBT16 ak; // not used + // SVBT16 grfbmc; // not used + // SVBT32 ITagBkmk; // when not -1, this tag identifies the ATNBE + + SwWW8Writer::WriteShort( *rWrt.pTableStrm, nFndPos ); + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); + if (rAtn.HasRange()) + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, nlTag ); + ++nlTag; + } + else + SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 ); + } + } + else + { + sal_uInt16 nNo = 0; + for ( sal_uInt16 i = 0; i < nLen; ++i ) // write Flags + { + const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(aContent[ i ]); + SwWW8Writer::WriteShort( *rWrt.pTableStrm, + !pFootnote->GetNumStr().isEmpty() ? 0 : ++nNo ); + } + } + } + rRefStart = nFcStart; + nFcStart = rWrt.pTableStrm->Tell(); + rRefCount = nFcStart - rRefStart; + + pTextPos->Write( *rWrt.pTableStrm ); + + switch ( nTTyp ) + { + case TXT_TXTBOX: + case TXT_HFTXTBOX: + for ( sal_uInt16 i = 0; i < nLen; ++i ) + { + // write break descriptor (BKD) + // short itxbxs + SwWW8Writer::WriteShort( *rWrt.pTableStrm, i ); + // short dcpDepend + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); + // short flags : icol/fTableBreak/fColumnBreak/fMarked/ + // fUnk/fTextOverflow + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0x800 ); + } + SwWW8Writer::FillCount( *rWrt.pTableStrm, 6 ); + break; + } + + rTextStart = nFcStart; + rTextCount = rWrt.pTableStrm->Tell() - nFcStart; +} + +const std::vector<sal_uInt32>* WW8_WrPlcSubDoc::GetShapeIdArr() const +{ + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx new file mode 100644 index 000000000..b3e71ba0f --- /dev/null +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -0,0 +1,4471 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <iostream> + +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/packages/XPackageEncryption.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <algorithm> +#include <map> +#include <hintids.hxx> +#include <string.h> +#include <o3tl/safeint.hxx> +#include <osl/endian.h> +#include <sal/log.hxx> +#include <docsh.hxx> +#include <drawdoc.hxx> + +#include <unotools/fltrcfg.hxx> +#include <sot/storage.hxx> +#include <sfx2/docinf.hxx> +#include <editeng/tstpitem.hxx> +#include <svx/svdpage.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <filter/msfilter/classids.hxx> +#include <filter/msfilter/msoleexp.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <swtypes.hxx> +#include <swrect.hxx> +#include <swtblfmt.hxx> +#include <fmtcntnt.hxx> +#include <fmtpdsc.hxx> +#include <fmtrowsplt.hxx> +#include <frmatr.hxx> +#include <../../core/inc/rootfrm.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentExternalData.hxx> +#include <viewopt.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <shellio.hxx> +#include <docstat.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> +#include <IMark.hxx> +#include <swtable.hxx> +#include "wrtww8.hxx" +#include "ww8par.hxx" +#include <swmodule.hxx> +#include <section.hxx> +#include <fmtinfmt.hxx> +#include <txtinet.hxx> +#include <fmturl.hxx> +#include <vcl/imap.hxx> +#include <vcl/imapobj.hxx> +#include <mdiexp.hxx> +#include <strings.hrc> +#include <fmtline.hxx> +#include <fmtfsize.hxx> +#include "sprmids.hxx" + +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "ww8attributeoutput.hxx" +#include <xmloff/odffields.hxx> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <dbgoutsw.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <svl/stritem.hxx> +#include <unotools/tempfile.hxx> +#include <filter/msfilter/mscodec.hxx> +#include <filter/msfilter/svxmsbas.hxx> +#include <rtl/random.h> +#include <vcl/svapp.hxx> +#include <sfx2/docfilt.hxx> +#include "WW8Sttbf.hxx" +#include <editeng/charrotateitem.hxx> +#include <svx/swframetypes.hxx> +#include "WW8FibData.hxx" +#include <numrule.hxx> +#include <fmtclds.hxx> +#include <rdfhelper.hxx> +#include <fmtclbl.hxx> +#include <iodetect.hxx> + +using namespace css; +using namespace sw::util; +using namespace sw::types; + +/** FKP - Formatted disK Page +*/ +class WW8_WrFkp +{ + sal_uInt8* pFkp; // Fkp total ( first and only FCs and Sprms ) + sal_uInt8* pOfs; // pointer to the offset area, later copied to pFkp + ePLCFT ePlc; + short nStartGrp; // from here on grpprls + short nOldStartGrp; + sal_uInt8 nItemSize; + sal_uInt8 nIMax; // number of entry pairs + sal_uInt8 nOldVarLen; + bool bCombined; // true : paste not allowed + + sal_uInt8 SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms ); + + WW8_WrFkp(const WW8_WrFkp&) = delete; + WW8_WrFkp& operator=(const WW8_WrFkp&) = delete; + +public: + WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc); + ~WW8_WrFkp(); + bool Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms ); + void Combine(); + void Write( SvStream& rStrm, SwWW8WrGrf& rGrf ); + + bool IsEqualPos(WW8_FC nEndFc) const + { return !bCombined && nIMax && nEndFc == reinterpret_cast<sal_Int32*>(pFkp)[nIMax]; } + void MergeToNew( short& rVarLen, sal_uInt8 *& pNewSprms ); + bool IsEmptySprm() const + { return !bCombined && nIMax && !nOldVarLen; } + void SetNewEnd( WW8_FC nEnd ) + { reinterpret_cast<sal_Int32*>(pFkp)[nIMax] = nEnd; } + + WW8_FC GetStartFc() const; + WW8_FC GetEndFc() const; + + sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen); +}; + +// class WW8_WrPc collects all piece entries for one piece +class WW8_WrPc +{ + WW8_CP nStartCp; // Starting character position of the text + WW8_FC nStartFc; // Starting file position of the text + sal_uInt16 nStatus; // End of paragraph inside the piece? + +public: + WW8_WrPc(WW8_FC nSFc, WW8_CP nSCp ) + : nStartCp( nSCp ), nStartFc( nSFc ), nStatus( 0x0040 ) + {} + + void SetStatus() { nStatus = 0x0050; } + sal_uInt16 GetStatus() const { return nStatus; } + WW8_CP GetStartCp() const { return nStartCp; } + WW8_FC GetStartFc() const { return nStartFc; } +}; + +typedef std::map<OUString,long> BKMKNames; +typedef std::pair<bool,OUString> BKMK; +typedef std::pair<long,BKMK> BKMKCP; +typedef std::multimap<long,BKMKCP*> BKMKCPs; +typedef BKMKCPs::iterator CPItr; + +class WW8_WrtBookmarks +{ +private: + /// Structure of one item inside this map: (startPos, (endPos, (a bool value?, bookmarkName))) + BKMKCPs aSttCps; + BKMKNames maSwBkmkNms; + + WW8_WrtBookmarks(WW8_WrtBookmarks const&) = delete; + WW8_WrtBookmarks& operator=(WW8_WrtBookmarks const&) = delete; + +public: + WW8_WrtBookmarks(); + ~WW8_WrtBookmarks(); + //! Add a new bookmark to the list OR add an end position to an existing bookmark. + void Append( WW8_CP nStartCp, const OUString& rNm ); + //! Write out bookmarks to file. + void Write( WW8Export& rWrt ); + //! Move existing field marks from one position to another. + void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo); +}; + +WW8_WrtBookmarks::WW8_WrtBookmarks() +{} + +WW8_WrtBookmarks::~WW8_WrtBookmarks() +{ + for (auto& rEntry : aSttCps) + { + if (rEntry.second) + { + delete rEntry.second; + rEntry.second = nullptr; + } + } +} + +void WW8_WrtBookmarks::Append( WW8_CP nStartCp, const OUString& rNm) +{ + std::pair<BKMKNames::iterator, bool> aResult = maSwBkmkNms.insert(std::pair<OUString,long>(rNm,0L)); + if (aResult.second) + { + BKMK aBK(false,rNm); + BKMKCP* pBKCP = new BKMKCP(static_cast<long>(nStartCp),aBK); + aSttCps.insert(std::pair<long,BKMKCP*>(nStartCp,pBKCP)); + aResult.first->second = static_cast<long>(nStartCp); + } + else + { + std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(aResult.first->second); + for (CPItr aItr = aRange.first;aItr != aRange.second;++aItr) + { + if (aItr->second && aItr->second->second.second == rNm) + { + if (aItr->second->second.first) + nStartCp--; + aItr->second->first = static_cast<long>(nStartCp); + break; + } + } + } +} + +void WW8_WrtBookmarks::Write( WW8Export& rWrt) +{ + if (aSttCps.empty()) + return; + long n; + std::vector<OUString> aNames; + SvMemoryStream aTempStrm1(65535,65535); + SvMemoryStream aTempStrm2(65535,65535); + + BKMKCPs aEndCps; + for (const auto& rEntry : aSttCps) + { + if (rEntry.second) + { + aEndCps.insert(std::pair<long,BKMKCP*>(rEntry.second->first, rEntry.second)); + aNames.push_back(rEntry.second->second.second); + SwWW8Writer::WriteLong(aTempStrm1, rEntry.first); + } + } + + aTempStrm1.Seek(0); + n = 0; + for (const auto& rEntry : aEndCps) + { + if (rEntry.second) + { + rEntry.second->first = n; + SwWW8Writer::WriteLong( aTempStrm2, rEntry.first); + } + ++n; + } + + aTempStrm2.Seek(0); + rWrt.WriteAsStringTable(aNames, rWrt.pFib->m_fcSttbfbkmk,rWrt.pFib->m_lcbSttbfbkmk); + SvStream& rStrm = *rWrt.pTableStrm; + rWrt.pFib->m_fcPlcfbkf = rStrm.Tell(); + rStrm.WriteStream( aTempStrm1 ); + SwWW8Writer::WriteLong(rStrm, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpTxbx); + for (const auto& rEntry : aSttCps) + { + if (rEntry.second) + { + SwWW8Writer::WriteLong(rStrm, rEntry.second->first); + } + } + rWrt.pFib->m_lcbPlcfbkf = rStrm.Tell() - rWrt.pFib->m_fcPlcfbkf; + rWrt.pFib->m_fcPlcfbkl = rStrm.Tell(); + rStrm.WriteStream( aTempStrm2 ); + SwWW8Writer::WriteLong(rStrm, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpTxbx); + rWrt.pFib->m_lcbPlcfbkl = rStrm.Tell() - rWrt.pFib->m_fcPlcfbkl; +} + +void WW8_WrtBookmarks::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo) +{ + std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(nFrom); + CPItr aItr = aRange.first; + while (aItr != aRange.second) + { + if (aItr->second) + { + if (aItr->second->first == static_cast<long>(nFrom)) + { + aItr->second->second.first = true; + aItr->second->first = nTo; + } + aSttCps.insert(std::pair<long,BKMKCP*>(nTo,aItr->second)); + aItr->second = nullptr; + aRange = aSttCps.equal_range(nFrom); + aItr = aRange.first; + continue; + } + ++aItr; + } +} + +/// Handles export of smart tags. +class WW8_WrtFactoids +{ + std::vector<WW8_CP> m_aStartCPs; + std::vector<WW8_CP> m_aEndCPs; + std::vector< std::map<OUString, OUString> > m_aStatements; + + WW8_WrtFactoids(WW8_WrtFactoids const&) = delete; + WW8_WrtFactoids& operator=(WW8_WrtFactoids const&) = delete; + +public: + WW8_WrtFactoids(); + void Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements); + void Write(WW8Export& rWrt); +}; + +WW8_WrtFactoids::WW8_WrtFactoids() +{ +} + +void WW8_WrtFactoids::Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements) +{ + m_aStartCPs.push_back(nStartCp); + m_aEndCPs.push_back(nEndCp); + m_aStatements.push_back(rStatements); +} + +void WW8_WrtFactoids::Write(WW8Export& rExport) +{ + if (m_aStartCPs.empty()) + return; + + // Smart tags are otherwise removed by Word on saving. + rExport.pDop->fEmbedFactoids = true; + + SvStream& rStream = *rExport.pTableStrm; + + rExport.pFib->m_fcSttbfBkmkFactoid = rStream.Tell(); + // Write SttbfBkmkFactoid. + rStream.WriteUInt16(0xffff); // fExtend + rStream.WriteUInt16(m_aStartCPs.size()); // cData + rStream.WriteUInt16(0); // cbExtra + + for (size_t i = 0; i < m_aStartCPs.size(); ++i) + { + rStream.WriteUInt16(6); // cchData + // Write FACTOIDINFO. + rStream.WriteUInt32(i); // dwId + rStream.WriteUInt16(0); // fSubEntry + rStream.WriteUInt16(0); // fto + rStream.WriteUInt32(0); // pfpb + } + rExport.pFib->m_lcbSttbfBkmkFactoid = rStream.Tell() - rExport.pFib->m_fcSttbfBkmkFactoid; + + rExport.pFib->m_fcPlcfBkfFactoid = rStream.Tell(); + for (const WW8_CP& rCP : m_aStartCPs) + rStream.WriteInt32(rCP); + rStream.WriteInt32(rExport.pFib->m_ccpText + rExport.pFib->m_ccpTxbx); + + // Write FBKFD. + for (size_t i = 0; i < m_aStartCPs.size(); ++i) + { + rStream.WriteInt16(i); // ibkl + rStream.WriteInt16(0); // bkc + rStream.WriteInt16(1); // cDepth, 1 as start and end is the same. + } + + rExport.pFib->m_lcbPlcfBkfFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBkfFactoid; + + rExport.pFib->m_fcPlcfBklFactoid = rStream.Tell(); + for (const WW8_CP& rCP : m_aEndCPs) + rStream.WriteInt32(rCP); + rStream.WriteInt32(rExport.pFib->m_ccpText + rExport.pFib->m_ccpTxbx); + + // Write FBKLD. + for (size_t i = 0; i < m_aEndCPs.size(); ++i) + { + rStream.WriteInt16(i); // ibkf + rStream.WriteInt16(0); // cDepth, 0 as does not overlap with any other smart tag. + } + rExport.pFib->m_lcbPlcfBklFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBklFactoid; + + rExport.pFib->m_fcFactoidData = rStream.Tell(); + // Write SmartTagData. + MSOFactoidType aFactoidType; + aFactoidType.m_nId = 1; + aFactoidType.m_aUri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + aFactoidType.m_aTag = "RDF"; + WW8SmartTagData aSmartTagData; + aSmartTagData.m_aPropBagStore.m_aFactoidTypes.push_back(aFactoidType); + + std::set<OUString> aSet; + for (const std::map<OUString, OUString>& rStatements : m_aStatements) + { + // Statements for a single text node. + for (const auto& rPair : rStatements) + { + aSet.insert(rPair.first); + aSet.insert(rPair.second); + } + } + aSmartTagData.m_aPropBagStore.m_aStringTable.assign(aSet.begin(), aSet.end()); + for (const std::map<OUString, OUString>& rStatements : m_aStatements) + { + MSOPropertyBag aPropertyBag; + aPropertyBag.m_nId = 1; + for (const auto& rPair : rStatements) + { + MSOProperty aProperty; + aProperty.m_nKey = std::distance(aSet.begin(), aSet.find(rPair.first)); + aProperty.m_nValue = std::distance(aSet.begin(), aSet.find(rPair.second)); + aPropertyBag.m_aProperties.push_back(aProperty); + } + aSmartTagData.m_aPropBags.push_back(aPropertyBag); + } + + aSmartTagData.Write(rExport); + rExport.pFib->m_lcbFactoidData = rStream.Tell() - rExport.pFib->m_fcFactoidData; +} + +#define DEFAULT_STYLES_COUNT 16 + +// Names of the storage streams +#define sMainStream OUString("WordDocument") +#define sCompObj "\1CompObj" + +static void WriteDop( WW8Export& rWrt ) +{ + WW8Dop& rDop = *rWrt.pDop; + + // i#78951#, store the value of unknown compatibility options + rDop.SetCompatibilityOptions( rWrt.m_pDoc->getIDocumentSettingAccess().Getn32DummyCompatibilityOptions1()); + rDop.SetCompatibilityOptions2( rWrt.m_pDoc->getIDocumentSettingAccess().Getn32DummyCompatibilityOptions2()); + + rDop.fNoLeading = !rWrt.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING); + rDop.fUsePrinterMetrics = !rWrt.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE); + + // write default TabStop + const SvxTabStopItem& rTabStop = + DefaultItemGet<SvxTabStopItem>(*rWrt.m_pDoc, RES_PARATR_TABSTOP); + rDop.dxaTab = static_cast<sal_uInt16>(rTabStop[0].GetTabPos()); + + // Zoom factor and type + SwViewShell *pViewShell(rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()); + if (pViewShell) + { + switch ( pViewShell->GetViewOptions()->GetZoomType() ) + { + case SvxZoomType::WHOLEPAGE: rDop.zkSaved = 1; break; + case SvxZoomType::PAGEWIDTH: rDop.zkSaved = 2; break; + case SvxZoomType::OPTIMAL: rDop.zkSaved = 3; break; + default: rDop.zkSaved = 0; + rDop.wScaleSaved = pViewShell->GetViewOptions()->GetZoom(); + break; + } + } + + // Values from the DocumentStatistics (are definitely needed + // for the DocStat fields) + rDop.fWCFootnoteEdn = true; // because they are included in StarWriter + + const SwDocStat& rDStat = rWrt.m_pDoc->getIDocumentStatistics().GetDocStat(); + rDop.cWords = rDStat.nWord; + rDop.cCh = rDStat.nChar; + rDop.cPg = static_cast< sal_Int16 >(rDStat.nPage); + rDop.cParas = rDStat.nPara; + rDop.cLines = rDStat.nPara; + + SwDocShell *pDocShell(rWrt.m_pDoc->GetDocShell()); + OSL_ENSURE(pDocShell, "no SwDocShell"); + uno::Reference<document::XDocumentProperties> xDocProps; + uno::Reference<beans::XPropertySet> xProps; + if ( pDocShell ) + { + uno::Reference<lang::XComponent> xModelComp = pDocShell->GetModel(); + xProps.set(xModelComp, uno::UNO_QUERY); + uno::Reference<document::XDocumentPropertiesSupplier> xDPS(xModelComp, uno::UNO_QUERY_THROW); + xDocProps = xDPS->getDocumentProperties(); + OSL_ENSURE(xDocProps.is(), "DocumentProperties is null"); + + rDop.lKeyProtDoc = pDocShell->GetModifyPasswordHash(); + } + + if ((rWrt.pSepx && rWrt.pSepx->DocumentIsProtected()) || + rWrt.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM ) || + rDop.lKeyProtDoc != 0) + { + rDop.fProtEnabled = true; + // The password was ignored at import if forms protection was enabled, + // so round-trip it since protection is still enabled. + if ( rDop.lKeyProtDoc == 0 && xProps.is() ) + { + comphelper::SequenceAsHashMap aPropMap( xProps->getPropertyValue("InteropGrabBag")); + aPropMap.getValue("FormPasswordHash") >>= rDop.lKeyProtDoc; + } + } + else + { + rDop.fProtEnabled = false; + } + + if (!xDocProps.is()) + { + rDop.dttmCreated = rDop.dttmRevised = rDop.dttmLastPrint = 0x45FBAC69; + } + else + { + ::util::DateTime uDT = xDocProps->getCreationDate(); + rDop.dttmCreated = sw::ms::DateTime2DTTM(DateTime(uDT)); + uDT = xDocProps->getModificationDate(); + rDop.dttmRevised = sw::ms::DateTime2DTTM(DateTime(uDT)); + uDT = xDocProps->getPrintDate(); + rDop.dttmLastPrint = sw::ms::DateTime2DTTM(DateTime(uDT)); + } + + // Also, the DocStat fields in headers, footers are not calculated correctly. + // ( we do not have this fields! ) + + // and also for the Headers and Footers + rDop.cWordsFootnoteEnd = rDStat.nWord; + rDop.cChFootnoteEdn = rDStat.nChar; + rDop.cPgFootnoteEdn = static_cast<sal_Int16>(rDStat.nPage); + rDop.cParasFootnoteEdn = rDStat.nPara; + rDop.cLinesFootnoteEdn = rDStat.nPara; + + rDop.fDontUseHTMLAutoSpacing = rWrt.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX); + + rDop.fExpShRtn = !rWrt.m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); // #i56856# + + rDop.Write( *rWrt.pTableStrm, *rWrt.pFib ); +} + +const sal_Unicode *WW8DopTypography::GetJapanNotBeginLevel1() +{ + static const sal_Unicode aJapanNotBeginLevel1[nMaxFollowing] = + //Japanese Level 1 + { + 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, + 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032, + 0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d, + 0x300f, 0x3011, 0x3015, 0x309b, 0x309c, 0x309d, 0x309e, 0x30fb, + 0x30fd, 0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, + 0xff1b, 0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, + 0xff9e, 0xff9f, 0xffe0 + }; + return &aJapanNotBeginLevel1[0]; +} + +const sal_Unicode *WW8DopTypography::GetJapanNotEndLevel1() +{ + static const sal_Unicode aJapanNotEndLevel1[nMaxLeading] = + //Japanese Level 1 + { + 0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, + 0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, + 0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5 + }; + return &aJapanNotEndLevel1[0]; +} + +static int lcl_CmpBeginEndChars( const OUString& rSWStr, + const sal_Unicode* pMSStr, int nMSStrByteLen ) +{ + nMSStrByteLen /= sizeof( sal_Unicode ); + if( nMSStrByteLen > rSWStr.getLength() ) + nMSStrByteLen = rSWStr.getLength()+1; + nMSStrByteLen *= sizeof( sal_Unicode ); + + return memcmp( rSWStr.getStr(), pMSStr, nMSStrByteLen ); +} + +/* +Converts the OOo Asian Typography into a best fit match for Microsoft +Asian typography. This structure is actually dumped to disk within the +Dop Writer. Assumption is that rTypo is cleared to 0 on entry +*/ +void WW8Export::ExportDopTypography(WW8DopTypography &rTypo) +{ + static const sal_Unicode aLangNotBegin[4][WW8DopTypography::nMaxFollowing]= + { + //Japanese Level 1 + { + 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, + 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032, + 0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d, + 0x300f, 0x3011, 0x3015, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049, + 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x309b, 0x309c, 0x309d, + 0x309e, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3, + 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fc, 0x30fd, + 0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, + 0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, 0xff67, + 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, + 0xff70, 0xff9e, 0xff9f, 0xffe0 + }, + //Simplified Chinese + { + 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d, + 0x007d, 0x00a8, 0x00b7, 0x02c7, 0x02c9, 0x2015, 0x2016, 0x2019, + 0x201d, 0x2026, 0x2236, 0x3001, 0x3002, 0x3003, 0x3005, 0x3009, + 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0xff01, 0xff02, + 0xff07, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d, + 0xff40, 0xff5c, 0xff5d, 0xff5e, 0xffe0 + }, + //Korean + { + 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, + 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2032, 0x2033, + 0x2103, 0x3009, 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0xff01, + 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d, + 0xff5d, 0xffe0 + }, + //Traditional Chinese + { + 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d, + 0x007d, 0x00a2, 0x00b7, 0x2013, 0x2014, 0x2019, 0x201d, 0x2022, + 0x2025, 0x2026, 0x2027, 0x2032, 0x2574, 0x3001, 0x3002, 0x3009, + 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x301e, 0xfe30, 0xfe31, + 0xfe33, 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40, + 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, 0xfe52, 0xfe54, 0xfe55, + 0xfe56, 0xfe57, 0xfe5a, 0xfe5c, 0xfe5e, 0xff01, 0xff09, 0xff0c, + 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff5c, 0xff5d, 0xff64 + }, + }; + + static const sal_Unicode aLangNotEnd[4][WW8DopTypography::nMaxLeading] = + { + //Japanese Level 1 + { + 0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, + 0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, + 0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5 + }, + //Simplified Chinese + { + 0x0028, 0x005b, 0x007b, 0x00b7, 0x2018, 0x201c, 0x3008, 0x300a, + 0x300c, 0x300e, 0x3010, 0x3014, 0x3016, 0xff08, 0xff0e, 0xff3b, + 0xff5b, 0xffe1, 0xffe5 + }, + //Korean + { + 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, + 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, 0xff08, + 0xff3b, 0xff5b, 0xffe6 + }, + //Traditional Chinese + { + 0x0028, 0x005b, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, 0x2035, + 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0x301d, 0xfe35, + 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59, + 0xfe5b, 0xfe5d, 0xff08, 0xff5b + }, + }; + + const i18n::ForbiddenCharacters *pForbidden = nullptr; + const i18n::ForbiddenCharacters *pUseMe = nullptr; + sal_uInt8 nUseReserved=0; + int nNoNeeded=0; + /* + Now we have some minor difficult issues, to wit... + a. MicroSoft Office can only store one set of begin and end characters in + a given document, not one per language. + b. StarOffice has only a concept of one set of begin and end characters for + a given language, i.e. not the two levels of kinsoku in japanese + + What is unknown as yet is if our default begin and end chars for + japanese, chinese tradition, chinese simplified and korean are different + in Word and Writer. I already suspect that they are different between + different version of word itself. + + So what have come up with is to simply see if any of the four languages + in OOo have been changed away from OUR defaults, and if one has then + export that. If more than one has in the future we may hack in something + which examines our document properties to see which language is used the + most and choose that, for now we choose the first and throw an ASSERT + */ + + /*Our default Japanese Level is 2, this is a special MS hack to set this*/ + rTypo.m_reserved2 = 1; + + for (rTypo.m_reserved1=8;rTypo.m_reserved1>0;rTypo.m_reserved1-=2) + { + if (nullptr != (pForbidden = m_pDoc->getIDocumentSettingAccess().getForbiddenCharacters(rTypo.GetConvertedLang(), + false))) + { + int nIdx = (rTypo.m_reserved1-2)/2; + if( lcl_CmpBeginEndChars( pForbidden->endLine, + aLangNotEnd[ nIdx ], sizeof(aLangNotEnd[ nIdx ]) ) || + lcl_CmpBeginEndChars( pForbidden->beginLine, + aLangNotBegin[ nIdx ], sizeof(aLangNotBegin[ nIdx ]) ) ) + { + //One exception for Japanese, if it matches a level 1 we + //can use one extra flag for that, rather than use a custom + if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE) + { + if ( + !lcl_CmpBeginEndChars + ( + pForbidden->endLine, + WW8DopTypography::GetJapanNotEndLevel1(), + WW8DopTypography::nMaxLeading * sizeof(sal_Unicode) + ) + && + !lcl_CmpBeginEndChars + ( + pForbidden->beginLine, + WW8DopTypography::GetJapanNotBeginLevel1(), + WW8DopTypography::nMaxFollowing * sizeof(sal_Unicode) + ) + ) + { + rTypo.m_reserved2 = 0; + continue; + } + } + + if (!pUseMe) + { + pUseMe = pForbidden; + nUseReserved = rTypo.m_reserved1; + rTypo.m_iLevelOfKinsoku = 2; + } + nNoNeeded++; + } + } + } + + OSL_ENSURE( nNoNeeded<=1, "Example of unexportable forbidden chars" ); + rTypo.m_reserved1=nUseReserved; + if (rTypo.m_iLevelOfKinsoku && pUseMe) + { + rTypo.m_cchFollowingPunct = msword_cast<sal_Int16> + (pUseMe->beginLine.getLength()); + if (rTypo.m_cchFollowingPunct > WW8DopTypography::nMaxFollowing - 1) + rTypo.m_cchFollowingPunct = WW8DopTypography::nMaxFollowing - 1; + + rTypo.m_cchLeadingPunct = msword_cast<sal_Int16> + (pUseMe->endLine.getLength()); + if (rTypo.m_cchLeadingPunct > WW8DopTypography::nMaxLeading - 1) + rTypo.m_cchLeadingPunct = WW8DopTypography::nMaxLeading -1; + + memcpy(rTypo.m_rgxchFPunct,pUseMe->beginLine.getStr(), + (rTypo.m_cchFollowingPunct+1)*2); + + memcpy(rTypo.m_rgxchLPunct,pUseMe->endLine.getStr(), + (rTypo.m_cchLeadingPunct+1)*2); + } + + const IDocumentSettingAccess& rIDocumentSettingAccess = GetWriter().getIDocumentSettingAccess(); + + rTypo.m_fKerningPunct = sal_uInt16(rIDocumentSettingAccess.get(DocumentSettingId::KERN_ASIAN_PUNCTUATION)); + rTypo.m_iJustification = sal_uInt16(m_pDoc->getIDocumentSettingAccess().getCharacterCompressionType()); +} + +// It can only be found something with this method, if it is used within +// WW8_SwAttrIter::OutAttr() and WW8Export::OutputItemSet() +const SfxPoolItem* MSWordExportBase::HasItem( sal_uInt16 nWhich ) const +{ + const SfxPoolItem* pItem=nullptr; + if (m_pISet) + { + // if write an EditEngine text, then the WhichIds are greater than + // our own Ids. So the Id have to translate from our into the + // EditEngine Range + nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, *m_pDoc, nWhich); + if (nWhich && SfxItemState::SET != m_pISet->GetItemState(nWhich, true, &pItem)) + pItem = nullptr; + } + else if( m_pChpIter ) + pItem = m_pChpIter->HasTextItem( nWhich ); + else + { + OSL_ENSURE( false, "Where is my ItemSet / pChpIter ?" ); + pItem = nullptr; + } + return pItem; +} + +const SfxPoolItem& MSWordExportBase::GetItem(sal_uInt16 nWhich) const +{ + assert((m_pISet || m_pChpIter) && "Where is my ItemSet / pChpIter ?"); + if (m_pISet) + { + // if write an EditEngine text, then the WhichIds are greater than + // our own Ids. So the Id have to translate from our into the + // EditEngine Range + nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, *m_pDoc, nWhich); + OSL_ENSURE(nWhich != 0, "All broken, Impossible"); + return m_pISet->Get(nWhich); + } + return m_pChpIter->GetItem( nWhich ); +} + +WW8_WrPlc1::WW8_WrPlc1( sal_uInt16 nStructSz ) + : pData( new sal_uInt8[ 16 * nStructSz ] ), + nDataLen(16 * nStructSz), + nStructSiz( nStructSz ) +{ +} + +WW8_WrPlc1::~WW8_WrPlc1() +{ +} + +WW8_CP WW8_WrPlc1::Prev() const +{ + bool b = !aPos.empty(); + OSL_ENSURE(b,"Prev called on empty list"); + return b ? aPos.back() : 0; +} + +void WW8_WrPlc1::Append( WW8_CP nCp, const void* pNewData ) +{ + sal_uLong nInsPos = aPos.size() * nStructSiz; + aPos.push_back( nCp ); + if( nDataLen < nInsPos + nStructSiz ) + { + sal_uInt8* pNew = new sal_uInt8[ 2 * nDataLen ]; + memcpy( pNew, pData.get(), nDataLen ); + pData.reset(pNew); + nDataLen *= 2; + } + memcpy( pData.get() + nInsPos, pNewData, nStructSiz ); +} + +void WW8_WrPlc1::Finish( sal_uLong nLastCp, sal_uLong nSttCp ) +{ + if( !aPos.empty() ) + { + aPos.push_back( nLastCp ); + if( nSttCp ) + for(WW8_CP & rCp : aPos) + rCp -= nSttCp; + } +} + +void WW8_WrPlc1::Write( SvStream& rStrm ) +{ + decltype(aPos)::size_type i; + for( i = 0; i < aPos.size(); ++i ) + SwWW8Writer::WriteLong( rStrm, aPos[i] ); + if( i ) + rStrm.WriteBytes(pData.get(), (i-1) * nStructSiz); +} + +// Class WW8_WrPlcField for fields + +void WW8_WrPlcField::Write( WW8Export& rWrt ) +{ + if( WW8_WrPlc1::Count() <= 1 ) + return; + + WW8_FC *pfc; + sal_Int32 *plc; + switch (nTextTyp) + { + case TXT_MAINTEXT: + pfc = &rWrt.pFib->m_fcPlcffldMom; + plc = &rWrt.pFib->m_lcbPlcffldMom; + break; + case TXT_HDFT: + pfc = &rWrt.pFib->m_fcPlcffldHdr; + plc = &rWrt.pFib->m_lcbPlcffldHdr; + break; + + case TXT_FTN: + pfc = &rWrt.pFib->m_fcPlcffldFootnote; + plc = &rWrt.pFib->m_lcbPlcffldFootnote; + break; + + case TXT_EDN: + pfc = &rWrt.pFib->m_fcPlcffldEdn; + plc = &rWrt.pFib->m_lcbPlcffldEdn; + break; + + case TXT_ATN: + pfc = &rWrt.pFib->m_fcPlcffldAtn; + plc = &rWrt.pFib->m_lcbPlcffldAtn; + break; + + case TXT_TXTBOX: + pfc = &rWrt.pFib->m_fcPlcffldTxbx; + plc = &rWrt.pFib->m_lcbPlcffldTxbx; + break; + + case TXT_HFTXTBOX: + pfc = &rWrt.pFib->m_fcPlcffldHdrTxbx; + plc = &rWrt.pFib->m_lcbPlcffldHdrTxbx; + break; + + default: + pfc = plc = nullptr; + break; + } + + if( pfc && plc ) + { + sal_uInt64 nFcStart = rWrt.pTableStrm->Tell(); + WW8_WrPlc1::Write( *rWrt.pTableStrm ); + *pfc = nFcStart; + *plc = rWrt.pTableStrm->Tell() - nFcStart; + } +} + +void WW8_WrMagicTable::Write( WW8Export& rWrt ) +{ + if( WW8_WrPlc1::Count() <= 1 ) + return; + sal_uLong nFcStart = rWrt.pTableStrm->Tell(); + WW8_WrPlc1::Write( *rWrt.pTableStrm ); + rWrt.pFib->m_fcPlcfTch = nFcStart; + rWrt.pFib->m_lcbPlcfTch = rWrt.pTableStrm->Tell() - nFcStart; +} + +void WW8_WrMagicTable::Append( WW8_CP nCp, sal_uLong nData) +{ + /* + Tell the undocumented table hack that everything between here and the last + table position is non-table text, don't do it if the previous position is + the same as this one, as that would be a region of 0 length + */ + if ((!Count()) || (Prev() != nCp)) + { + SVBT32 nLittle; + UInt32ToSVBT32(nData,nLittle); + WW8_WrPlc1::Append(nCp, nLittle); + } +} + +void SwWW8Writer::FillCount( SvStream& rStrm, sal_uLong nCount ) +{ + static const sal_uInt32 aNulls[16] = + { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 64 Byte + }; + + while (nCount > 64) + { + rStrm.WriteBytes(aNulls, 64); // in steps of 64-Byte + nCount -= 64; + } + rStrm.WriteBytes(aNulls, nCount); // write the rest (0 .. 64 Bytes) +} + +sal_uLong SwWW8Writer::FillUntil( SvStream& rStrm, sal_uLong nEndPos ) +{ + sal_uInt64 nCurPos = rStrm.Tell(); + if( !nEndPos ) // nEndPos == 0 -> next Page + nEndPos = (nCurPos + 0x1ff) & ~0x1ffUL; + + if( nEndPos > nCurPos ) + SwWW8Writer::FillCount( rStrm, nEndPos - nCurPos ); +#if OSL_DEBUG_LEVEL > 0 + else + OSL_ENSURE( nEndPos == nCurPos, "Wrong FillUntil()" ); +#endif + return rStrm.Tell(); +} + +WW8_WrPlcPn::WW8_WrPlcPn(WW8Export& rWr, ePLCFT ePl, WW8_FC nStartFc) + : rWrt(rWr) + , nFkpStartPage(0) + , ePlc(ePl) +{ + m_Fkps.push_back(std::make_unique<WW8_WrFkp>(ePlc, nStartFc)); +} + +WW8_WrPlcPn::~WW8_WrPlcPn() +{ +} + +sal_uInt8 *WW8_WrPlcPn::CopyLastSprms(sal_uInt8 &rLen) +{ + WW8_WrFkp& rF = *m_Fkps.back(); + return rF.CopyLastSprms(rLen); +} + +void WW8_WrPlcPn::AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8* pSprms) +{ + WW8_WrFkp* pF = m_Fkps.back().get(); + + // big sprm? build the sprmPHugePapx + sal_uInt8* pNewSprms = const_cast<sal_uInt8*>(pSprms); + sal_uInt8 aHugePapx[ 8 ]; + if (PAP == ePlc && 488 <= nVarLen) + { + sal_uInt8* p = aHugePapx; + *p++ = *pSprms++; // set style Id + *p++ = *pSprms++; + nVarLen -= 2; + + long nDataPos = rWrt.pDataStrm->Tell(); + SwWW8Writer::WriteShort( *rWrt.pDataStrm, nVarLen ); + rWrt.pDataStrm->WriteBytes(pSprms, nVarLen); + + Set_UInt16( p, 0x6646 ); // set SprmCode + Set_UInt32( p, nDataPos ); // set startpos (FC) in the datastream + nVarLen = static_cast< short >(p - aHugePapx); + pSprms = pNewSprms = aHugePapx; + } + // if append at the same FC-EndPos and there are sprms, then get the old + // sprms and erase it; they will append now with the new sprms + else if( nVarLen && pF->IsEqualPos( nEndFc )) + pF->MergeToNew( nVarLen, pNewSprms ); + // has the prev EndFC an empty sprm and the current is empty too, then + // expand only the old EndFc to the new EndFc + else if( !nVarLen && pF->IsEmptySprm() ) + { + pF->SetNewEnd( nEndFc ); + return ; + } + + bool bOk = pF->Append(nEndFc, nVarLen, pNewSprms); + if( !bOk ) + { + pF->Combine(); + pF = new WW8_WrFkp(ePlc, pF->GetEndFc()); // Start new Fkp == end of old Fkp + + m_Fkps.push_back(std::unique_ptr<WW8_WrFkp>(pF)); + if( !pF->Append( nEndFc, nVarLen, pNewSprms ) ) + { + OSL_ENSURE( false, "Unable to insert Sprm" ); + } + } + if( pNewSprms != pSprms ) //Merge to new has created a new block + delete[] pNewSprms; +} + +void WW8_WrPlcPn::WriteFkps() +{ + nFkpStartPage = static_cast<sal_uInt16>( SwWW8Writer::FillUntil( rWrt.Strm() ) >> 9 ); + + for(const std::unique_ptr<WW8_WrFkp> & rp : m_Fkps) + { + rp->Write( rWrt.Strm(), *rWrt.m_pGrf ); + } + + if( CHP == ePlc ) + { + rWrt.pFib->m_pnChpFirst = nFkpStartPage; + rWrt.pFib->m_cpnBteChp = m_Fkps.size(); + } + else + { + rWrt.pFib->m_pnPapFirst = nFkpStartPage; + rWrt.pFib->m_cpnBtePap = m_Fkps.size(); + } +} + +void WW8_WrPlcPn::WritePlc() +{ + sal_uInt64 nFcStart = rWrt.pTableStrm->Tell(); + decltype(m_Fkps)::size_type i; + + for (i = 0; i < m_Fkps.size(); ++i) + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, + m_Fkps[ i ]->GetStartFc() ); + } + + SwWW8Writer::WriteLong( *rWrt.pTableStrm, + m_Fkps[ i - 1 ]->GetEndFc() ); + + // for every FKP output the page + for (i = 0; i < m_Fkps.size(); ++i) + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, i + nFkpStartPage ); + } + + if( CHP == ePlc ) + { + rWrt.pFib->m_fcPlcfbteChpx = nFcStart; + rWrt.pFib->m_lcbPlcfbteChpx = rWrt.pTableStrm->Tell() - nFcStart; + } + else + { + rWrt.pFib->m_fcPlcfbtePapx = nFcStart; + rWrt.pFib->m_lcbPlcfbtePapx = rWrt.pTableStrm->Tell() - nFcStart; + } +} + +WW8_WrFkp::WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc) + : ePlc(ePl), nStartGrp(511), nOldStartGrp(511), + nItemSize( ( CHP == ePl ) ? 1 : 13 ), + nIMax(0), nOldVarLen(0), bCombined(false) +{ + pFkp = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte + pOfs = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte + memset( pFkp, 0, 4 * 128 ); + memset( pOfs, 0, 4 * 128 ); + reinterpret_cast<sal_Int32*>(pFkp)[0] = nStartFc; // 0th entry FC at nStartFc +} + +WW8_WrFkp::~WW8_WrFkp() +{ + delete[] reinterpret_cast<sal_Int32 *>(pFkp); + delete[] reinterpret_cast<sal_Int32 *>(pOfs); +} + +sal_uInt8 WW8_WrFkp::SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms ) +{ + if( 3 < nVarLen ) + { + // if the sprms contained picture-references then never equal! + for( sal_uInt8 n = static_cast< sal_uInt8 >(nVarLen - 1); 3 < n; --n ) + if( pSprms[ n ] == GRF_MAGIC_3 && + pSprms[ n-1 ] == GRF_MAGIC_2 && + pSprms[ n-2 ] == GRF_MAGIC_1 ) + return 0; + } + + short i; + for( i = 0; i < nIMax; i++ ) + { + sal_uInt8 nStart = pOfs[i * nItemSize]; + if( nStart ) + { // has Sprms + const sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 ); + if( ( CHP == ePlc + ? (*p++ == nVarLen) + : ((static_cast<sal_uInt16>(*p++) << 1 ) == (( nVarLen+1) & 0xfffe)) ) + && !memcmp( p, pSprms, nVarLen ) ) + return nStart; // found it + } + } + return 0; // didn't found it +} + +sal_uInt8 *WW8_WrFkp::CopyLastSprms(sal_uInt8 &rLen) +{ + rLen=0; + sal_uInt8 *pStart=nullptr,*pRet=nullptr; + + if (!bCombined) + pStart = pOfs; + else + pStart = pFkp + ( nIMax + 1 ) * 4; + + sal_uInt8 nStart = *(pStart + (nIMax-1) * nItemSize); + + const sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 ); + + if (!*p) + p++; + + if (*p) + { + rLen = *p++; + if (PAP == ePlc) + rLen *= 2; + pRet = new sal_uInt8[rLen]; + memcpy(pRet,p,rLen); + } + return pRet; +} + +bool WW8_WrFkp::Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms ) +{ + assert((!nVarLen || pSprms) && "Item pointer missing"); + + OSL_ENSURE( nVarLen < ( ( ePlc == PAP ) ? 497U : 502U ), "Sprms too long !" ); + + if( bCombined ) + { + OSL_ENSURE( false, "Fkp::Append: Fkp is already combined" ); + return false; + } + sal_Int32 n = reinterpret_cast<sal_Int32*>(pFkp)[nIMax]; // last entry + if( nEndFc <= n ) + { + OSL_ENSURE( nEndFc >= n, "+Fkp: FC backwards" ); + OSL_ENSURE( !nVarLen || !pSprms || nEndFc != n, + "+Fkp: used same FC multiple times" ); + // same FC without Sprm is ignored without grumbling + + return true; // ignore (do not create a new Fkp) + } + + sal_uInt8 nOldP = nVarLen ? SearchSameSprm( nVarLen, pSprms ) : 0; + // Combine equal entries + short nOffset=0, nPos = nStartGrp; + if (nVarLen && !nOldP) + { + nPos = PAP == ePlc + ? ( 13 == nItemSize // HACK: PAP and bWrtWW8 !! + ? (nStartGrp & 0xFFFE ) - nVarLen - 1 + : (nStartGrp - (((nVarLen + 1) & 0xFFFE)+1)) & 0xFFFE ) + : ((nStartGrp - nVarLen - 1) & 0xFFFE); + if( nPos < 0 ) + return false; // doesn't fit at all + nOffset = nPos; // save offset (can also be uneven!) + nPos &= 0xFFFE; // Pos for Sprms ( gerade Pos ) + } + + if( o3tl::make_unsigned(nPos) <= ( nIMax + 2U ) * 4U + ( nIMax + 1U ) * nItemSize ) + // does it fits after the CPs and offsets? + return false; // no + + reinterpret_cast<sal_Int32*>(pFkp)[nIMax + 1] = nEndFc; // insert FC + + nOldVarLen = static_cast<sal_uInt8>(nVarLen); + if( nVarLen && !nOldP ) + { // insert it for real + nOldStartGrp = nStartGrp; + + nStartGrp = nPos; + pOfs[nIMax * nItemSize] = static_cast<sal_uInt8>( nStartGrp >> 1 ); + // insert (start-of-data >> 1) + sal_uInt8 nCnt = static_cast< sal_uInt8 >(CHP == ePlc + ? ( nVarLen < 256 ) ? static_cast<sal_uInt8>(nVarLen) : 255 + : ( ( nVarLen + 1 ) >> 1 )); + + pFkp[ nOffset ] = nCnt; // Enter data length + memcpy( pFkp + nOffset + 1, pSprms, nVarLen ); // store Sprms + } + else + { + // do not enter for real ( no Sprms or recurrence ) + // start-of-data 0 ( no data ) or recurrence + pOfs[nIMax * nItemSize] = nOldP; + } + nIMax++; + return true; +} + +void WW8_WrFkp::Combine() +{ + if( bCombined ) + return; + if( nIMax ) + memcpy( pFkp + ( nIMax + 1 ) * 4, pOfs, nIMax * nItemSize ); + delete[] pOfs; + pOfs = nullptr; + pFkp[511] = nIMax; + bCombined = true; + +#if defined OSL_BIGENDIAN // only the FCs will be rotated here + sal_uInt16 i; // the Sprms must be rotated elsewhere + + sal_uInt32* p; + for( i = 0, p = (sal_uInt32*)pFkp; i <= nIMax; i++, p++ ) + *p = OSL_SWAPDWORD( *p ); +#endif // ifdef OSL_BIGENDIAN +} + +void WW8_WrFkp::Write( SvStream& rStrm, SwWW8WrGrf& rGrf ) +{ + Combine(); // If not already combined + + sal_uInt8* p; // search magic for nPicLocFc + sal_uInt8* pEnd = pFkp + nStartGrp; + for( p = pFkp + 511 - 4; p >= pEnd; p-- ) + { + if( *p != GRF_MAGIC_1 ) // search for signature 0x12 0x34 0x56 0xXX + continue; + if( *(p+1) != GRF_MAGIC_2 ) + continue; + if( *(p+2) != GRF_MAGIC_3 ) + continue; + + SVBT32 nPos; // signature found + UInt32ToSVBT32( rGrf.GetFPos(), nPos ); // FilePos the graphics + memcpy( p, nPos, 4 ); // patch FilePos over the signature + } + rStrm.WriteBytes(pFkp, 512); +} + +void WW8_WrFkp::MergeToNew( short& rVarLen, sal_uInt8 *& rpNewSprms ) +{ + sal_uInt8 nStart = pOfs[ (nIMax-1) * nItemSize ]; + if( nStart ) + { // has Sprms + sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 ); + + // old and new equal? Then copy only one into the new sprms + if( nOldVarLen == rVarLen && !memcmp( p+1, rpNewSprms, nOldVarLen )) + { + sal_uInt8* pNew = new sal_uInt8[ nOldVarLen ]; + memcpy( pNew, p+1, nOldVarLen ); + rpNewSprms = pNew; + } + else + { + sal_uInt8* pNew = new sal_uInt8[ nOldVarLen + rVarLen ]; + memcpy( pNew, p+1, nOldVarLen ); + memcpy( pNew + nOldVarLen, rpNewSprms, rVarLen ); + + rpNewSprms = pNew; + rVarLen = rVarLen + nOldVarLen; + } + --nIMax; + // if this Sprms don't used from others, remove it + bool bFnd = false; + for (sal_uInt16 n = 0; n < nIMax; ++n) + { + if (nStart == pOfs[n * nItemSize]) + { + bFnd = true; + break; + } + } + if (!bFnd) + { + nStartGrp = nOldStartGrp; + memset( p, 0, nOldVarLen+1 ); + } + } +} + +WW8_FC WW8_WrFkp::GetStartFc() const +{ + // when bCombined, then the array beginning with pFkp is already byte-swapped + // to LittleEndian, so to extract the start and end positions they must + // be swapped back. + if( bCombined ) + return SVBT32ToUInt32( pFkp ); // 0. Element + return reinterpret_cast<sal_Int32*>(pFkp)[0]; +} + +WW8_FC WW8_WrFkp::GetEndFc() const +{ + if( bCombined ) + return SVBT32ToUInt32( &(pFkp[nIMax*4]) ); // nIMax-th SVBT32-Element + return reinterpret_cast<sal_Int32*>(pFkp)[nIMax]; +} + +// Method for managing the piece table +WW8_WrPct::WW8_WrPct(WW8_FC nfcMin) + : nOldFc(nfcMin) +{ + AppendPc(nOldFc); +} + +WW8_WrPct::~WW8_WrPct() +{ +} + +// Fill the piece and create a new one +void WW8_WrPct::AppendPc(WW8_FC nStartFc) +{ + WW8_CP nStartCp = nStartFc - nOldFc; // subtract the beginning of the text + if ( !nStartCp && !m_Pcts.empty()) + { + OSL_ENSURE(1 == m_Pcts.size(), "empty Piece!"); + m_Pcts.pop_back(); + } + + nOldFc = nStartFc; // remember StartFc as old + + nStartCp >>= 1; // for Unicode: number of characters / 2 + + if (!m_Pcts.empty()) + { + nStartCp += m_Pcts.back()->GetStartCp(); + } + + m_Pcts.push_back(std::make_unique<WW8_WrPc>(nStartFc, nStartCp)); +} + +void WW8_WrPct::WritePc( WW8Export& rWrt ) +{ + sal_uInt64 nPctStart; + sal_uLong nOldPos, nEndPos; + + nPctStart = rWrt.pTableStrm->Tell(); // Start piece table + rWrt.pTableStrm->WriteChar( char(0x02) ); // Status byte PCT + nOldPos = nPctStart + 1; // remember Position + SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); // then the length + + for (auto const& it : m_Pcts) // ranges + { + SwWW8Writer::WriteLong( *rWrt.pTableStrm, it->GetStartCp() ); + } + + // calculate the last Pos + sal_uLong nStartCp = rWrt.pFib->m_fcMac - nOldFc; + nStartCp >>= 1; // For Unicode: number of characters / 2 + nStartCp += m_Pcts.back()->GetStartCp(); + SwWW8Writer::WriteLong( *rWrt.pTableStrm, nStartCp ); + + // piece references + for (auto const& it : m_Pcts) + { + SwWW8Writer::WriteShort(*rWrt.pTableStrm, it->GetStatus()); + SwWW8Writer::WriteLong(*rWrt.pTableStrm, it->GetStartFc()); + SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0); // PRM=0 + } + + // entries in the FIB + rWrt.pFib->m_fcClx = nPctStart; + nEndPos = rWrt.pTableStrm->Tell(); + rWrt.pFib->m_lcbClx = nEndPos - nPctStart; + + // and register the length as well + SwWW8Writer::WriteLong( *rWrt.pTableStrm, nOldPos, + nEndPos - nPctStart-5 ); + +} + +void WW8_WrPct::SetParaBreak() +{ + OSL_ENSURE( !m_Pcts.empty(), "SetParaBreak : m_Pcts.empty()" ); + m_Pcts.back()->SetStatus(); +} + +WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const +{ + OSL_ENSURE( nFc >= o3tl::make_unsigned(nOldFc), "FilePos lies in front of last piece" ); + OSL_ENSURE( ! m_Pcts.empty(), "Fc2Cp no piece available" ); + + nFc -= nOldFc; + nFc /= 2; // Unicode + return nFc + m_Pcts.back()->GetStartCp(); +} + +void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) +{ + std::vector< const ::sw::mark::IMark* > aArr; + sal_uInt16 nContent; + const sal_Int32 nCurrentEnd = nCurrentPos + nLen; + if( GetWriter().GetBookmarks( rNd, nCurrentPos, nCurrentEnd, aArr )) + { + sal_uLong nNd = rNd.GetIndex(), nSttCP = Fc2Cp( Strm().Tell() ); + for(const ::sw::mark::IMark* p : aArr) + { + const ::sw::mark::IMark& rBkmk = *p; + if(dynamic_cast< const ::sw::mark::IFieldmark *>(&rBkmk)) + continue; + + const SwPosition* pPos = &rBkmk.GetMarkPos(); + const SwPosition* pOPos = nullptr; + if(rBkmk.IsExpanded()) + pOPos = &rBkmk.GetOtherMarkPos(); + if( pOPos && pOPos->nNode == pPos->nNode && + pOPos->nContent < pPos->nContent ) + { + pPos = pOPos; + pOPos = &rBkmk.GetMarkPos(); + } + + if( !pOPos || ( nNd == pPos->nNode.GetIndex() && + ( nContent = pPos->nContent.GetIndex() ) >= nCurrentPos && + nContent < nCurrentEnd ) ) + { + sal_uLong nCp = nSttCP + pPos->nContent.GetIndex() - nCurrentPos; + m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName())); + } + if( pOPos && nNd == pOPos->nNode.GetIndex() && + ( nContent = pOPos->nContent.GetIndex() ) >= nCurrentPos && + nContent < nCurrentEnd ) + { + sal_uLong nCp = nSttCP + pOPos->nContent.GetIndex() - nCurrentPos; + m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName())); + } + } + } +} + +void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen) +{ + IMarkVector aMarks; + if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks)) + { + for (const sw::mark::IMark* pMark : aMarks) + { + const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex(); + if (nStart == nCurrentPos) + { + m_pAtn->AddRangeStartPosition(pMark->GetName(), Fc2Cp(Strm().Tell()), + !rAttrs.HasFlysAt(nCurrentPos)); + } + } + } +} + +void WW8Export::AppendSmartTags(SwTextNode& rTextNode) +{ + std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", rTextNode); + if (!aStatements.empty()) + { + WW8_CP nCP = Fc2Cp(Strm().Tell()); + m_pFactoids->Append(nCP, nCP, aStatements); + } +} + +void WW8Export::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo) +{ + m_pBkmks->MoveFieldMarks(nFrom, nTo); +} + +void WW8Export::AppendBookmark( const OUString& rName ) +{ + sal_uLong nSttCP = Fc2Cp( Strm().Tell() ); + m_pBkmks->Append( nSttCP, rName ); +} + +void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName ) +{ + sal_uLong nEndCP = Fc2Cp( Strm().Tell() ); + m_pBkmks->Append( nEndCP - 1, rName ); +} + +std::unique_ptr<SvxBrushItem> MSWordExportBase::getBackground() +{ + const SwFrameFormat &rFormat = m_pDoc->GetPageDesc(0).GetMaster(); + std::unique_ptr<SvxBrushItem> aBrush = std::make_unique<SvxBrushItem>(RES_BACKGROUND); + SfxItemState eState = rFormat.GetBackgroundState(aBrush); + + if (SfxItemState::SET == eState) + { + // The 'color' is set for the first page style - take it and use it as the background color of the entire DOCX + if (aBrush->GetColor() != COL_AUTO) + return aBrush; + } + return nullptr; +} + +// #i120928 collect all the graphics of bullets applied to paragraphs +int MSWordExportBase::CollectGrfsOfBullets() +{ + m_vecBulletPic.clear(); + + if ( m_pDoc ) + { + size_t nCountRule = m_pDoc->GetNumRuleTable().size(); + for (size_t n = 0; n < nCountRule; ++n) + { + const SwNumRule &rRule = *( m_pDoc->GetNumRuleTable().at(n) ); + sal_uInt16 nLevels = rRule.IsContinusNum() ? 1 : 9; + for (sal_uInt16 nLvl = 0; nLvl < nLevels; ++nLvl) + { + const SwNumFormat &rFormat = rRule.Get(nLvl); + if (SVX_NUM_BITMAP != rFormat.GetNumberingType()) + { + continue; + } + const Graphic *pGraf = rFormat.GetBrush()? rFormat.GetBrush()->GetGraphic():nullptr; + if ( pGraf ) + { + bool bHas = false; + for (const Graphic* p : m_vecBulletPic) + { + if (p->GetChecksum() == pGraf->GetChecksum()) + { + bHas = true; + break; + } + } + if (!bHas) + { + Size aSize(pGraf->GetPrefSize()); + if (0 != aSize.Height() && 0 != aSize.Width()) + m_vecBulletPic.push_back(pGraf); + } + } + } + } + } + + return m_vecBulletPic.size(); +} + +void MSWordExportBase::BulletDefinitions() +{ + for (size_t i = 0; i < m_vecBulletPic.size(); ++i) + { + const MapMode aMapMode(MapUnit::MapTwip); + const Graphic& rGraphic = *m_vecBulletPic[i]; + Size aSize(rGraphic.GetPrefSize()); + if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit()) + aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapMode); + else + aSize = OutputDevice::LogicToLogic(aSize,rGraphic.GetPrefMapMode(), aMapMode); + + if (0 != aSize.Height() && 0 != aSize.Width()) + AttrOutput().BulletDefinition(i, rGraphic, aSize); + } +} + +//Export Graphic of Bullets +void WW8Export::ExportGrfBullet(const SwTextNode& rNd) +{ + int nCount = CollectGrfsOfBullets(); + if (nCount > 0) + { + SwPosition aPos(rNd); + OUString aPicBullets("_PictureBullets"); + AppendBookmark(aPicBullets); + for (int i = 0; i < nCount; i++) + { + ww8::Frame aFrame(*(m_vecBulletPic[i]), aPos); + OutGrfBullets(aFrame); + } + AppendBookmark(aPicBullets); + } +} + +static sal_uInt8 nAttrMagicIdx = 0; +void WW8Export::OutGrfBullets(const ww8::Frame & rFrame) +{ + if ( !m_pGrf || !m_pChpPlc || !pO ) + return; + + m_pGrf->Insert(rFrame); + m_pChpPlc->AppendFkpEntry( Strm().Tell(), pO->size(), pO->data() ); + pO->clear(); + // if links... + WriteChar( char(1) ); + + sal_uInt8 aArr[ 22 ]; + sal_uInt8* pArr = aArr; + + // sprmCFSpec + Set_UInt16( pArr, 0x855 ); + Set_UInt8( pArr, 1 ); + + Set_UInt16( pArr, 0x083c ); + Set_UInt8( pArr, 0x81 ); + + // sprmCPicLocation + Set_UInt16( pArr, 0x6a03 ); + Set_UInt32( pArr, GRF_MAGIC_321 ); + + //extern nAttrMagicIdx; + --pArr; + Set_UInt8( pArr, nAttrMagicIdx++ ); + m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr ); +} + +int MSWordExportBase::GetGrfIndex(const SvxBrushItem& rBrush) +{ + int nIndex = -1; + + const Graphic* pGraphic = rBrush.GetGraphic(); + if (pGraphic) + { + for (size_t i = 0; i < m_vecBulletPic.size(); ++i) + { + if (m_vecBulletPic[i]->GetChecksum() == pGraphic->GetChecksum()) + { + nIndex = i; + break; + } + } + } + + return nIndex; +} + +void WW8_WrtRedlineAuthor::Write( Writer& rWrt ) +{ + WW8Export & rWW8Wrt = *(static_cast<SwWW8Writer&>(rWrt).m_pExport); + rWW8Wrt.WriteAsStringTable(maAuthors, rWW8Wrt.pFib->m_fcSttbfRMark, + rWW8Wrt.pFib->m_lcbSttbfRMark); +} + +sal_uInt16 WW8Export::AddRedlineAuthor( std::size_t nId ) +{ + if( !m_pRedlAuthors ) + { + m_pRedlAuthors = new WW8_WrtRedlineAuthor; + m_pRedlAuthors->AddName("Unknown"); + } + return m_pRedlAuthors->AddName( SW_MOD()->GetRedlineAuthor( nId ) ); +} + +void WW8Export::WriteAsStringTable(const std::vector<OUString>& rStrings, + sal_Int32& rfcSttbf, sal_Int32& rlcbSttbf) +{ + sal_uInt16 n, nCount = static_cast< sal_uInt16 >(rStrings.size()); + if( nCount ) + { + // we have some Redlines found in the document -> the + // Author Name Stringtable + SvStream& rStrm = *pTableStrm; + rfcSttbf = rStrm.Tell(); + SwWW8Writer::WriteShort( rStrm, -1 ); + SwWW8Writer::WriteLong( rStrm, nCount ); + for( n = 0; n < nCount; ++n ) + { + const OUString& rNm = rStrings[n]; + SwWW8Writer::WriteShort( rStrm, rNm.getLength() ); + SwWW8Writer::WriteString16(rStrm, rNm, false); + } + rlcbSttbf = rStrm.Tell() - rfcSttbf; + } +} + +// WriteShort() sets at FilePos nPos the value nVal and seeks to the old +// FilePos. Used to insert lengths after the fact. +void SwWW8Writer::WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal ) +{ + sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos + rStrm.Seek( nPos ); + SwWW8Writer::WriteShort( rStrm, nVal ); + rStrm.Seek( nOldPos ); +} + +void SwWW8Writer::WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal ) +{ + sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos + rStrm.Seek( nPos ); + SwWW8Writer::WriteLong( rStrm, nVal ); + rStrm.Seek( nOldPos ); +} + +void SwWW8Writer::InsUInt16(ww::bytes &rO, sal_uInt16 n) +{ + SVBT16 nL; + ShortToSVBT16( n, nL ); + rO.push_back(nL[0]); + rO.push_back(nL[1]); +} + +void SwWW8Writer::InsUInt32(ww::bytes &rO, sal_uInt32 n) +{ + SVBT32 nL; + UInt32ToSVBT32( n, nL ); + rO.push_back(nL[0]); + rO.push_back(nL[1]); + rO.push_back(nL[2]); + rO.push_back(nL[3]); +} + +void SwWW8Writer::InsAsString16(ww::bytes &rO, const OUString& rStr) +{ + const sal_Unicode* pStr = rStr.getStr(); + for (sal_Int32 n = 0, nLen = rStr.getLength(); n < nLen; ++n, ++pStr) + SwWW8Writer::InsUInt16( rO, *pStr ); +} + +void SwWW8Writer::InsAsString8(ww::bytes &rO, const OUString& rStr, + rtl_TextEncoding eCodeSet) +{ + OString sTmp(OUStringToOString(rStr, eCodeSet)); + const char *pStart = sTmp.getStr(); + const char *pEnd = pStart + sTmp.getLength(); + rO.reserve(rO.size() + sTmp.getLength()); + + std::copy(pStart, pEnd, std::inserter(rO, rO.end())); +} + +void SwWW8Writer::WriteString16(SvStream& rStrm, const OUString& rStr, + bool bAddZero) +{ + ww::bytes aBytes; + SwWW8Writer::InsAsString16(aBytes, rStr); + if (bAddZero) + SwWW8Writer::InsUInt16(aBytes, 0); + //vectors are guaranteed to have contiguous memory, so we can do + //this while migrating away from WW8Bytes. Meyers Effective STL, item 16 + if (!aBytes.empty()) + rStrm.WriteBytes(aBytes.data(), aBytes.size()); +} + +void SwWW8Writer::WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero) +{ + ww::bytes aBytes; + SwWW8Writer::InsUInt16(aBytes, rStr.getLength()); + SwWW8Writer::InsAsString16(aBytes, rStr); + if (bAddZero) + SwWW8Writer::InsUInt16(aBytes, 0); + rStrm.WriteBytes(aBytes.data(), aBytes.size()); +} + +void SwWW8Writer::WriteString8(SvStream& rStrm, const OUString& rStr, + bool bAddZero, rtl_TextEncoding eCodeSet) +{ + ww::bytes aBytes; + SwWW8Writer::InsAsString8(aBytes, rStr, eCodeSet); + if (bAddZero) + aBytes.push_back(0); + //vectors are guaranteed to have contiguous memory, so we can do + ////this while migrating away from WW8Bytes. Meyers Effective STL, item 16 + if (!aBytes.empty()) + rStrm.WriteBytes(aBytes.data(), aBytes.size()); +} + +void WW8Export::WriteStringAsPara( const OUString& rText ) +{ + if( !rText.isEmpty() ) + OutSwString(rText, 0, rText.getLength()); + WriteCR(); // CR thereafter + + ww::bytes aArr; + SwWW8Writer::InsUInt16( aArr, 0/*nStyleId*/ ); + if( m_bOutTable ) + { // Tab-Attr + // sprmPFInTable + SwWW8Writer::InsUInt16( aArr, NS_sprm::sprmPFInTable ); + aArr.push_back( 1 ); + } + + sal_uInt64 nPos = Strm().Tell(); + m_pPapPlc->AppendFkpEntry( nPos, aArr.size(), aArr.data() ); + m_pChpPlc->AppendFkpEntry( nPos ); +} + +void MSWordExportBase::WriteSpecialText( sal_uLong nStart, sal_uLong nEnd, sal_uInt8 nTTyp ) +{ + sal_uInt8 nOldTyp = m_nTextTyp; + m_nTextTyp = nTTyp; + auto const pOldPam = m_pCurPam; //!! Simply shifting the PaM without restoring should do the job too + sal_uLong nOldStart = m_nCurStart; + sal_uLong nOldEnd = m_nCurEnd; + SwPaM* pOldEnd = m_pOrigPam; + bool bOldPageDescs = m_bOutPageDescs; + m_bOutPageDescs = false; + if ( nTTyp == TXT_FTN || nTTyp == TXT_EDN ) + m_bAddFootnoteTab = true; // enable one aesthetic tab for this footnote + + SetCurPam(nStart, nEnd); + + // clear linked textboxes since old ones can't be linked to frames in this section + m_aLinkedTextboxesHelper.clear(); + + // tdf#106261 Reset table infos, otherwise the depth of the cells will be + // incorrect, in case the header/footer had table(s) and we try to export + // the same table second time. + ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_pTableInfo; + m_pTableInfo = std::make_shared<ww8::WW8TableInfo>(); + + WriteText(); + + m_pTableInfo = pOldTableInfo; + + m_bOutPageDescs = bOldPageDescs; + m_pCurPam = pOldPam; // delete Pam + m_nCurStart = nOldStart; + m_nCurEnd = nOldEnd; + m_pOrigPam = pOldEnd; + m_nTextTyp = nOldTyp; +} + +void WW8Export::OutSwString(const OUString& rStr, sal_Int32 nStt, + sal_Int32 const nLen) + +{ + SAL_INFO( "sw.ww8.level2", "<OutSwString>" ); + + if( nLen ) + { + if( nStt || nLen != rStr.getLength() ) + { + OUString sOut( rStr.copy( nStt, nLen ) ); + + SAL_INFO( "sw.ww8.level2", sOut ); + + SwWW8Writer::WriteString16(Strm(), sOut, false); + } + else + { + SAL_INFO( "sw.ww8.level2", rStr ); + + SwWW8Writer::WriteString16(Strm(), rStr, false); + } + } + + SAL_INFO( "sw.ww8.level2", "</OutSwString>" ); +} + +void WW8Export::WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + if (pTableTextNodeInfoInner.get() != nullptr && pTableTextNodeInfoInner->getDepth() == 1 && pTableTextNodeInfoInner->isEndOfCell()) + WriteChar('\007'); + else + WriteChar( '\015' ); + + m_pPiece->SetParaBreak(); +} + +void WW8Export::WriteChar( sal_Unicode c ) +{ + Strm().WriteUInt16( c ); +} + +void MSWordExportBase::SetCurPam(sal_uLong nStt, sal_uLong nEnd) +{ + m_nCurStart = nStt; + m_nCurEnd = nEnd; + m_pCurPam = Writer::NewUnoCursor( *m_pDoc, nStt, nEnd ); + + // Recognize tables in special cases + if ( nStt != m_pCurPam->GetMark()->nNode.GetIndex() && + m_pDoc->GetNodes()[ nStt ]->IsTableNode() ) + { + m_pCurPam->GetMark()->nNode = nStt; + } + + m_pOrigPam = m_pCurPam.get(); // ??? + m_pCurPam->Exchange(); +} + +void MSWordExportBase::SaveData( sal_uLong nStt, sal_uLong nEnd ) +{ + MSWordSaveData aData; + + // WW8Export only stuff - zeroed here not to issue warnings + aData.pOOld = nullptr; + + // Common stuff + aData.pOldPam = m_pCurPam; + aData.pOldEnd = m_pOrigPam; + aData.pOldFlyFormat = m_pParentFrame; + aData.pOldPageDesc = m_pCurrentPageDesc; + + aData.pOldFlyOffset = m_pFlyOffset; + aData.eOldAnchorType = m_eNewAnchorType; + + aData.bOldOutTable = m_bOutTable; + aData.bOldFlyFrameAttrs = m_bOutFlyFrameAttrs; + aData.bOldStartTOX = m_bStartTOX; + aData.bOldInWriteTOX = m_bInWriteTOX; + + SetCurPam(nStt, nEnd); + + m_bOutTable = false; + // Caution: bIsInTable should not be set here + m_bOutFlyFrameAttrs = false; + m_bStartTOX = false; + m_bInWriteTOX = false; + + m_aSaveData.push( std::move(aData) ); +} + +void MSWordExportBase::RestoreData() +{ + MSWordSaveData &rData = m_aSaveData.top(); + + m_pCurPam = rData.pOldPam; + m_nCurStart = rData.nOldStart; + m_nCurEnd = rData.nOldEnd; + m_pOrigPam = rData.pOldEnd; + + m_bOutTable = rData.bOldOutTable; + m_bOutFlyFrameAttrs = rData.bOldFlyFrameAttrs; + m_bStartTOX = rData.bOldStartTOX; + m_bInWriteTOX = rData.bOldInWriteTOX; + + m_pParentFrame = rData.pOldFlyFormat; + m_pCurrentPageDesc = rData.pOldPageDesc; + + m_eNewAnchorType = rData.eOldAnchorType; + m_pFlyOffset = rData.pOldFlyOffset; + + m_aSaveData.pop(); +} + +void WW8Export::SaveData( sal_uLong nStt, sal_uLong nEnd ) +{ + MSWordExportBase::SaveData( nStt, nEnd ); + + MSWordSaveData &rData = m_aSaveData.top(); + + if ( !pO->empty() ) + { + rData.pOOld = std::move(pO); + pO.reset(new ww::bytes); + } + else + rData.pOOld = nullptr; // reuse pO + + rData.bOldWriteAll = GetWriter().m_bWriteAll; + GetWriter().m_bWriteAll = true; +} + +void WW8Export::RestoreData() +{ + MSWordSaveData &rData = m_aSaveData.top(); + + GetWriter().m_bWriteAll = rData.bOldWriteAll; + + OSL_ENSURE( pO->empty(), "pO is not empty in WW8Export::RestoreData()" ); + if ( rData.pOOld ) + { + pO = std::move(rData.pOOld); + } + + MSWordExportBase::RestoreData(); +} + +void WW8AttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth(); + + if ( nDepth > 0 ) + { + /* Cell */ + m_rWW8Export.InsUInt16( NS_sprm::sprmPFInTable ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + m_rWW8Export.InsUInt16( NS_sprm::sprmPItap ); + m_rWW8Export.InsUInt32( nDepth ); + + if ( nDepth > 1 && pTableTextNodeInfoInner->isEndOfCell() ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPFInnerTableCell ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + } + } +} + +void WW8AttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth(); + + if ( nDepth > 0 ) + { + /* Row */ + if ( pTableTextNodeInfoInner->isEndOfLine() ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPFInTable ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + + if ( nDepth == 1 ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPFTtp ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + } + + m_rWW8Export.InsUInt16( NS_sprm::sprmPItap ); + m_rWW8Export.InsUInt32( nDepth ); + + if ( nDepth > 1 ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPFInnerTableCell ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + m_rWW8Export.InsUInt16( NS_sprm::sprmPFInnerTtp ); + m_rWW8Export.pO->push_back( sal_uInt8(0x1) ); + } + + // Most of these are per-row definitions, not per-table. + // WW8 has no explicit table start/end markup, + // simply rows with the same table properties that are grouped together as a table. + TableBidi( pTableTextNodeInfoInner ); + TableOrientation( pTableTextNodeInfoInner ); + TableSpacing( pTableTextNodeInfoInner ); + TableDefinition( pTableTextNodeInfoInner ); //per row definitions + TableHeight( pTableTextNodeInfoInner ); //per row definitions + TableBackgrounds( pTableTextNodeInfoInner ); //per row definitions + // Since this isEndOfLine, cell margin defaults for each row come from last column. + TableDefaultBorders( pTableTextNodeInfoInner ); //per row definitions + TableCanSplit( pTableTextNodeInfoInner ); //per row definitions + TableVerticalCell( pTableTextNodeInfoInner ); //per row definitions + TableCellBorders( pTableTextNodeInfoInner ); //per row definitions + } + } +} + +static sal_uInt16 lcl_TCFlags(SwDoc &rDoc, const SwTableBox * pBox, sal_Int32 nRowSpan) +{ + sal_uInt16 nFlags = 0; + + if (nRowSpan > 1) + nFlags |= (3 << 5); + else if (nRowSpan < 0) + nFlags |= (1 << 5); + + if (pBox != nullptr) + { + const SwFrameFormat * pFormat = pBox->GetFrameFormat(); + switch (pFormat->GetVertOrient().GetVertOrient()) + { + case text::VertOrientation::CENTER: + nFlags |= (1 << 7); + break; + case text::VertOrientation::BOTTOM: + nFlags |= (2 << 7); + break; + default: + break; + } + const SwStartNode * pSttNd = pBox->GetSttNd(); + if(pSttNd) + { + SwNodeIndex aIdx( *pSttNd ); + const SwContentNode * pCNd = pSttNd->GetNodes().GoNext( &aIdx ); + if( pCNd && pCNd->IsTextNode()) + { + SfxItemSet aCoreSet(rDoc.GetAttrPool(), svl::Items<RES_CHRATR_ROTATE, RES_CHRATR_ROTATE>{}); + static_cast<const SwTextNode*>(pCNd)->GetParaAttr(aCoreSet, + 0, static_cast<const SwTextNode*>(pCNd)->GetText().getLength()); + const SfxPoolItem * pRotItem; + if ( SfxItemState::SET == aCoreSet.GetItemState(RES_CHRATR_ROTATE, true, &pRotItem)) + { + const SvxCharRotateItem * pRotate = static_cast<const SvxCharRotateItem*>(pRotItem); + if(pRotate && pRotate->GetValue() == 900) + { + nFlags = nFlags | 0x0004 | 0x0008; + } + else if(pRotate && pRotate->GetValue() == 2700 ) + { + nFlags = nFlags | 0x0004 | 0x0010; + } + } + } + } + } + + return nFlags; +} + +void WW8AttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes(); + + sal_uInt8 nBoxes = rTableBoxes.size(); + for ( sal_uInt8 n = 0; n < nBoxes; n++ ) + { + const SwTableBox * pTabBox1 = rTableBoxes[n]; + const SwFrameFormat * pFrameFormat = pTabBox1->GetFrameFormat(); + + // Map from our SvxFrameDirection to WW8 TextFlow. + sal_uInt16 nTextFlow = 0; + switch (m_rWW8Export.TrueFrameDirection(*pFrameFormat)) + { + case SvxFrameDirection::Vertical_RL_TB: + nTextFlow = 5; + break; + case SvxFrameDirection::Vertical_LR_BT: + nTextFlow = 3; + break; + default: + break; + } + + if (nTextFlow != 0) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTTextFlow ); + m_rWW8Export.pO->push_back( n ); //start range + m_rWW8Export.pO->push_back( sal_uInt8(n + 1) ); //end range + m_rWW8Export.InsUInt16(nTextFlow); + } + } +} + +void WW8AttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); + + /* + By default the row can be split in word, and now in writer we have a + feature equivalent to this, Word stores 1 for fCantSplit if the row + cannot be split, we set true if we can split it. An example is #i4569# + */ + + const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit(); + sal_uInt8 nCantSplit = (!rSplittable.GetValue()) ? 1 : 0; + m_rWW8Export.InsUInt16( NS_sprm::sprmTFCantSplit ); + m_rWW8Export.pO->push_back( nCantSplit ); + m_rWW8Export.InsUInt16( NS_sprm::sprmTFCantSplit90 ); // also write fCantSplit90 + m_rWW8Export.pO->push_back( nCantSplit ); +} + +void WW8AttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable * pTable = pTableTextNodeInfoInner->getTable(); + const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat(); + + if ( m_rWW8Export.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTFBiDi ); + m_rWW8Export.InsUInt16( 1 ); + } +} + +void WW8AttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ +} + +void WW8AttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ ) +{ +} + +void WW8AttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat(); + + // output line height sprmTDyaRowHeight + long nHeight = 0; + const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize(); + if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() ) + { + if ( SwFrameSize::Minimum == rLSz.GetHeightSizeType() ) + nHeight = rLSz.GetHeight(); + else + nHeight = -rLSz.GetHeight(); + } + + if ( nHeight ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTDyaRowHeight ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nHeight) ); + } + +} + +void WW8AttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable * pTable = pTableTextNodeInfoInner->getTable(); + + const SwFrameFormat *pFormat = pTable->GetFrameFormat(); + if ( !pFormat ) + { + SAL_WARN( "sw.ww8", "FrameFormat is nil" ); + return; + } + + const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient(); + const SwFormatVertOrient &rVert = pFormat->GetVertOrient(); + + if ( + (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() || + text::RelOrientation::FRAME == rHori.GetRelationOrient()) + && + (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() || + text::RelOrientation::FRAME == rVert.GetRelationOrient()) + ) + { + const bool bIsRTL = m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB; + sal_Int16 eHOri = rHori.GetHoriOrient(); + switch (eHOri) + { + case text::HoriOrientation::CENTER: + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc ); //logical orientation required for MSO + m_rWW8Export.InsUInt16( 1 ); + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc90 ); //physical orientation required for LO + m_rWW8Export.InsUInt16( 1 ); + break; + case text::HoriOrientation::RIGHT: + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc90 ); //required for LO + m_rWW8Export.InsUInt16( 2 ); + if ( !bIsRTL ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc ); //required for MSO + m_rWW8Export.InsUInt16( 2 ); + } + break; + case text::HoriOrientation::LEFT: + if ( bIsRTL ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc ); //required for MSO + m_rWW8Export.InsUInt16( 2 ); + } + break; + case text::HoriOrientation::LEFT_AND_WIDTH: + // Width can only be specified for the LOGICAL left, so in RTL, that is always PHYSICAL right + if ( bIsRTL ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTJc90 ); //required for LO + m_rWW8Export.InsUInt16( 2 ); + } + break; + default: + break; + } + } +} + +void WW8AttributeOutput::TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner) +{ + const SwTable * pTable = pTableTextNodeInfoInner->getTable(); + const SwTableFormat* pTableFormat = pTable->GetFrameFormat(); + + + // Writing these SPRM's will make the table a floating one, so only write + // them in case the table is already inside a frame. + if (pTableFormat != nullptr && pTable->GetTableNode()->GetFlyFormat()) + { + const SvxULSpaceItem & rUL = pTableFormat->GetULSpace(); + + if (rUL.GetUpper() > 0) + { + sal_uInt8 const nPadding = 2; + sal_uInt8 const nPcVert = 0; + sal_uInt8 const nPcHorz = 0; + + sal_uInt8 const nTPc = (nPadding << 4) | (nPcVert << 2) | nPcHorz; + + m_rWW8Export.InsUInt16(NS_sprm::sprmTPc); + m_rWW8Export.pO->push_back( nTPc ); + + m_rWW8Export.InsUInt16(NS_sprm::sprmTDyaAbs); + m_rWW8Export.InsUInt16(rUL.GetUpper()); + + m_rWW8Export.InsUInt16(NS_sprm::sprmTDyaFromText); + m_rWW8Export.InsUInt16(rUL.GetUpper()); + } + + if (rUL.GetLower() > 0) + { + m_rWW8Export.InsUInt16(NS_sprm::sprmTDyaFromTextBottom); + m_rWW8Export.InsUInt16(rUL.GetLower()); + } + } +} + +void WW8AttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable * pTable = pTableTextNodeInfoInner->getTable(); + + if ( pTable->GetRowsToRepeat() > pTableTextNodeInfoInner->getRow() ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTTableHeader ); + m_rWW8Export.pO->push_back( 1 ); + } + + ww8::TableBoxVectorPtr pTableBoxes = + pTableTextNodeInfoInner->getTableBoxesOfRow(); + // number of cell written + sal_uInt32 nBoxes = pTableBoxes->size(); + assert(nBoxes <= ww8::MAXTABLECELLS); + + // sprm header + m_rWW8Export.InsUInt16( NS_sprm::sprmTDefTable ); + sal_uInt16 nSprmSize = 2 + (nBoxes + 1) * 2 + nBoxes * 20; + m_rWW8Export.InsUInt16( nSprmSize ); // length + + // number of boxes + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes) ); + + /* cells */ + /* + ALWAYS relative when text::HoriOrientation::NONE (nPageSize + ( nPageSize / 10 )) < nTableSz, + in that case the cell width's and table width's are not real. The table + width is maxed and cells relative, so we need the frame (generally page) + width that the table is in to work out the true widths. + */ + //const bool bNewTableModel = pTable->IsNewModel(); + const SwFrameFormat *pFormat = pTable->GetFrameFormat(); + if ( !pFormat ) + { + SAL_WARN( "sw.ww8", "FrameFormat is nil" ); + return; + } + + const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient(); + const SwFormatVertOrient &rVert = pFormat->GetVertOrient(); + + SwTwips nTableOffset = 0; + + if ( + (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() || + text::RelOrientation::FRAME == rHori.GetRelationOrient()) + && + (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() || + text::RelOrientation::FRAME == rVert.GetRelationOrient()) + ) + { + sal_Int16 eHOri = rHori.GetHoriOrient(); + switch ( eHOri ) + { + case text::HoriOrientation::CENTER: + case text::HoriOrientation::RIGHT: + break; + + default: + nTableOffset = rHori.GetPos(); + const SvxLRSpaceItem& rLRSp = pFormat->GetLRSpace(); + nTableOffset += rLRSp.GetLeft(); + + // convert offset to be measured from right margin in right-to-left tables + if ( nTableOffset && m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB ) + { + SwTwips nLeftPageMargin, nRightPageMargin; + const SwTwips nPageSize = m_rWW8Export.CurrentPageWidth(nLeftPageMargin, nRightPageMargin); + const SwTwips nTableWidth = pFormat->GetFrameSize().GetWidth(); + nTableOffset = nPageSize - nLeftPageMargin - nRightPageMargin - nTableWidth - nTableOffset; + } + break; + } + } + + m_rWW8Export.InsInt16( nTableOffset ); + + ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner ); + for ( const auto nCol : *pGridCols ) + { + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nCol) + nTableOffset ); + } + + /* TCs */ + ww8::RowSpansPtr pRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow(); + ww8::RowSpans::const_iterator aItRowSpans = pRowSpans->begin(); + + for (const SwTableBox * pTabBox1 : *pTableBoxes) + { + sal_uInt16 npOCount = m_rWW8Export.pO->size(); + + const SwFrameFormat * pBoxFormat = nullptr; + if (pTabBox1 != nullptr) + pBoxFormat = pTabBox1->GetFrameFormat(); + + sal_uInt16 nFlags = + lcl_TCFlags(*m_rWW8Export.m_pDoc, pTabBox1, *aItRowSpans); + m_rWW8Export.InsUInt16( nFlags ); + + static sal_uInt8 aNullBytes[] = { 0x0, 0x0 }; + + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aNullBytes, aNullBytes+2 ); // dummy + if (pBoxFormat != nullptr) + { + const SvxBoxItem & rBoxItem = pBoxFormat->GetBox(); + + WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, &rBoxItem ); // 8/16 Byte + } + else + WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, nullptr); // 8/16 Byte + + SAL_INFO( "sw.ww8.level2", "<tclength>" << ( m_rWW8Export.pO->size() - npOCount ) << "</tclength>" ); + ++aItRowSpans; + } + + int nWidthPercent = pFormat->GetFrameSize().GetWidthPercent(); + // Width is in fiftieths of a percent. For sprmTTableWidth, must be non-negative and 600% max + if ( nWidthPercent > 0 && nWidthPercent <= 600 ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmTTableWidth ); + m_rWW8Export.pO->push_back( sal_uInt8/*ftsPercent*/ (2) ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nWidthPercent) * 50 ); + } +} + +ww8::GridColsPtr AttributeOutputBase::GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + return pTableTextNodeInfoInner->getGridColsOfRow(*this); +} + +ww8::WidthsPtr AttributeOutputBase::GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + // Get the column widths based on ALL the rows, not just the current row + return pTableTextNodeInfoInner->getGridColsOfRow(*this, true); +} + +void AttributeOutputBase::GetTablePageSize( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, long& rPageSize, bool& rRelBoxSize ) +{ + long nPageSize = 0; + + const SwNode *pTextNd = pTableTextNodeInfoInner->getNode( ); + const SwTable *pTable = pTableTextNodeInfoInner->getTable( ); + + const SwFrameFormat *pFormat = pTable->GetFrameFormat(); + if ( !pFormat ) + { + SAL_WARN( "sw.ww8", "FrameFormat is nil" ); + return; + } + + const SwFormatFrameSize &rSize = pFormat->GetFrameSize(); + int nWidthPercent = rSize.GetWidthPercent(); + bool bManualAligned = pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::NONE; + if ( (pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::FULL) || bManualAligned ) + nWidthPercent = 100; + bool bRelBoxSize = nWidthPercent != 0; + unsigned long nTableSz = static_cast<unsigned long>(rSize.GetWidth()); + if (nTableSz > USHRT_MAX/2 && !bRelBoxSize) + { + OSL_ENSURE(bRelBoxSize, "huge table width but not relative, suspicious"); + bRelBoxSize = true; + } + + if ( bRelBoxSize ) + { + Point aPt; + SwRect aRect( pFormat->FindLayoutRect( false, &aPt ) ); + if ( aRect.IsEmpty() ) + { + // Then fetch the page width without margins! + const SwFrameFormat* pParentFormat = + GetExport().m_pParentFrame ? + &(GetExport().m_pParentFrame->GetFrameFormat()) : + GetExport().m_pDoc->GetPageDesc(0).GetPageFormatOfNode(*pTextNd, false); + aRect = pParentFormat->FindLayoutRect(true); + if ( 0 == ( nPageSize = aRect.Width() ) ) + { + const SvxLRSpaceItem& rLR = pParentFormat->GetLRSpace(); + nPageSize = pParentFormat->GetFrameSize().GetWidth() - rLR.GetLeft() + - rLR.GetRight(); + } + } + else + { + nPageSize = aRect.Width(); + if ( bManualAligned ) + { + // #i37571# For manually aligned tables + const SvxLRSpaceItem &rLR = pFormat->GetLRSpace(); + nPageSize -= (rLR.GetLeft() + rLR.GetRight()); + } + + } + + if ( nWidthPercent ) + { + nPageSize *= nWidthPercent; + nPageSize /= 100; + } + else + SAL_WARN( "sw.ww8", "nWidthPercent is zero" ); + } + else + { + // As the table width is not relative, the TablePageSize equals its width + nPageSize = nTableSz; + } + + rPageSize = nPageSize; + rRelBoxSize = bRelBoxSize; +} + +void WW8AttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + // This function name is misleading because it is not a table default, but a row default, + // and it also only sets default cell margins (aka border padding). + // The specs suggest there is no way to define default border lines/colors. + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat(); + + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + + // Set row default cell margins using this last cell in the row + for ( int i = 0; i < 4; ++i ) + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmTCellPaddingDefault ); + m_rWW8Export.pO->push_back( sal_uInt8(6) ); + m_rWW8Export.pO->push_back( sal_uInt8(0) ); + m_rWW8Export.pO->push_back( sal_uInt8(1) ); + m_rWW8Export.pO->push_back( sal_uInt8(1 << i) ); + m_rWW8Export.pO->push_back( sal_uInt8(3) ); + + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, + pFrameFormat->GetBox().GetDistance( aBorders[i] ) ); + } +} + +void WW8AttributeOutput::TableCellBorders( + ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ) +{ + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes(); + sal_uInt8 nBoxes = std::min<size_t>(rTabBoxes.size(), 255); + const SvxBoxItem * pLastBox = nullptr; + sal_uInt8 nSeqStart = 0; // start of sequence of cells with same borders + + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + + sal_uInt16 nDefaultMargin[4] = {31681, 31681, 31681, 31681}; // outside of documented valid range + // last column in each row defines the row default in TableRowDefaultBorders() + if ( nBoxes && rTabBoxes.size() == nBoxes ) + { + const SvxBoxItem& rBox = rTabBoxes[ nBoxes-1 ]->GetFrameFormat()->GetBox(); + for ( int i = 0; i < 4; ++i ) + nDefaultMargin[i] = rBox.GetDistance( aBorders[i] ); + } + + // Detect sequences of cells which have the same borders, and output + // a border description for each such cell range. + for ( unsigned n = 0; n <= nBoxes; ++n ) + { + const SvxBoxItem * pBox = (n == nBoxes) ? nullptr : + &rTabBoxes[n]->GetFrameFormat()->GetBox(); + if( !pLastBox ) + pLastBox = pBox; + else if( !pBox || *pLastBox != *pBox ) + { + if ( !pLastBox ) + break; + + // This cell has different borders than the previous cell, + // so output the borders for the preceding cell range. + m_rWW8Export.Out_CellRangeBorders(pLastBox, nSeqStart, n); + + // The last column is used as the row default for margins, so we can ignore these matching ones + if ( n == nBoxes ) + break; + + // Output cell margins. + // One CSSA can define up to all four margins if they are the same size value. + sal_uInt16 nMargin[4]; + sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 0100:bottom, 1000:right + for ( int i = 0; i < 4; ++i ) // sides: top, left, bottom, right + { + nMargin[i] = std::min(sal_uInt16(31680), pLastBox->GetDistance( aBorders[i] )); + if ( nMargin[i] == nDefaultMargin[i] ) + continue; + + // join a previous side's definition if it shares the same value + for ( int p = 0; p < 4; ++p ) + { + if ( nMargin[i] == nMargin[p] ) + { + nSideBits[p] |= 1 << i; + break; + } + } + } + + // write out the cell margins definitions that were used + for ( int i = 0; i < 4; ++i ) + { + if ( nSideBits[i] ) + { + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmTCellPadding ); + m_rWW8Export.pO->push_back( sal_uInt8(6) ); // 6 bytes + m_rWW8Export.pO->push_back( sal_uInt8(nSeqStart) ); // first cell: apply margin + m_rWW8Export.pO->push_back( sal_uInt8(n) ); // end cell: do not apply margin + m_rWW8Export.pO->push_back( sal_uInt8(nSideBits[i]) ); + m_rWW8Export.pO->push_back( sal_uInt8(3) ); // FtsDxa: size in twips + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nMargin[i] ); + } + } + + nSeqStart = n; + pLastBox = pBox; + } + } +} + +void WW8AttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) +{ + const SwTable * pTab = pTableTextNodeInfoInner->getTable(); + const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox(); + const SwTableLine * pTabLine = pTabBox->GetUpper(); + const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes(); + + sal_uInt8 nBoxes = rTabBoxes.size(); + m_rWW8Export.InsUInt16( NS_sprm::sprmTDefTableShd80 ); + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes * 2) ); // Len + + Color aRowColor = COL_AUTO; + const SvxBrushItem *pTableColorProp = pTab->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pTableColorProp ) + aRowColor = pTableColorProp->GetColor(); + + const SvxBrushItem *pRowColorProp = pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pRowColorProp && pRowColorProp->GetColor() != COL_AUTO ) + aRowColor = pRowColorProp->GetColor(); + + for ( sal_uInt8 n = 0; n < nBoxes; n++ ) + { + const SwTableBox * pBox1 = rTabBoxes[n]; + const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat(); + Color aColor = aRowColor; + + const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO ) + aColor = pCellColorProp->GetColor(); + + WW8_SHD aShd; + WW8Export::TransBrush( aColor, aShd ); + m_rWW8Export.InsUInt16( aShd.GetValue() ); + } + + sal_uInt32 const aSprmIds[] { NS_sprm::sprmTDefTableShd, + NS_sprm::sprmTDefTableShdRaw }; + sal_uInt8 nBoxes0 = rTabBoxes.size(); + if (nBoxes0 > 21) + nBoxes0 = 21; + + for (sal_uInt32 m : aSprmIds) + { + m_rWW8Export.InsUInt16( m ); + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes0 * 10) ); + + for ( sal_uInt8 n = 0; n < nBoxes0; n++ ) + { + const SwTableBox * pBox1 = rTabBoxes[n]; + const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat(); + Color aColor = aRowColor; + + const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND); + if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO ) + aColor = pCellColorProp->GetColor(); + + WW8SHDLong aSHD; + aSHD.setCvFore( 0xFF000000 ); + + if ( aColor == COL_AUTO ) + aSHD.setCvBack( 0xFF000000 ); + else + aSHD.setCvBack( wwUtility::RGBToBGR( aColor ) ); + + aSHD.Write( m_rWW8Export ); + } + } +} + +void WW8Export::SectionBreaksAndFrames( const SwTextNode& rNode ) +{ + // output page/section breaks + OutputSectionBreaks( rNode.GetpSwAttrSet(), rNode ); +} + +namespace { + +class TrackContentToExport +{ +private: + SwPaM *m_pCurPam; + sal_uLong m_nStart, m_nEnd; +public: + TrackContentToExport(SwPaM *pCurPam, sal_uLong nCurStart, sal_uLong nCurEnd) + : m_pCurPam(pCurPam) + , m_nStart(nCurStart) + , m_nEnd(nCurEnd) + { + } + + bool contentRemainsToExport(ww8::WW8TableInfo *pTableInfo) + { + bool bSimpleContentRemains = m_pCurPam->GetPoint()->nNode < m_pCurPam->GetMark()->nNode || + (m_pCurPam->GetPoint()->nNode == m_pCurPam->GetMark()->nNode && + m_pCurPam->GetPoint()->nContent.GetIndex() <= m_pCurPam->GetMark()->nContent.GetIndex()); + if (bSimpleContentRemains) + return true; + + if (!pTableInfo) + return false; + + //An old-school table where one cell may points back to a previous node as the next cell + //so if this node is the last node in the range, we may need to jump back to a previously + //skipped cell to output it in a sane sequence. See ooo47778-3.sxw for one of these + //horrors. So if we are at the end of the selection, but this end point is a table + //cell whose next cell is in the selection allow jumping back to it + const SwNode* pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode(); + const SwNode* pNextNode = pTableInfo->getNextNode(pCurrentNode); + + if (pNextNode && pCurrentNode != pNextNode) + { + return pNextNode->GetIndex() >= m_nStart && + pNextNode->GetIndex() < m_nEnd; + } + + return false; + } +}; + +} + +void MSWordExportBase::WriteText() +{ + TrackContentToExport aContentTracking(m_pCurPam.get(), m_nCurStart, m_nCurEnd); + while (aContentTracking.contentRemainsToExport(m_pTableInfo.get())) + { + SwNode& rNd = m_pCurPam->GetNode(); + + // no section breaks exported for Endnotes + if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN && m_nTextTyp != TXT_FTN ) + { + SwSoftPageBreakList breakList; + // if paragraph need to be split than handle section break somewhere + // else. + if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) ) + SectionBreaksAndFrames( *rNd.GetTextNode() ); + } + + + // output the various types of nodes + if ( rNd.IsContentNode() ) + { + SwContentNode* pCNd = static_cast<SwContentNode*>(&rNd); + + const SwPageDesc* pTemp = rNd.FindPageDesc(); + if ( pTemp ) + m_pCurrentPageDesc = pTemp; + + m_pCurPam->GetPoint()->nContent.Assign( pCNd, 0 ); + OutputContentNode( *pCNd ); + } + else if ( rNd.IsTableNode() ) + { + m_pTableInfo->processSwTable( &rNd.GetTableNode()->GetTable() ); + } + else if ( rNd.IsSectionNode() && TXT_MAINTEXT == m_nTextTyp ) + OutputSectionNode( *rNd.GetSectionNode() ); + else if ( TXT_MAINTEXT == m_nTextTyp && rNd.IsEndNode() && + rNd.StartOfSectionNode()->IsSectionNode() ) + { + const SwSection& rSect = rNd.StartOfSectionNode()->GetSectionNode() + ->GetSection(); + if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() ) + m_bStartTOX = false; + + SwNodeIndex aIdx( rNd, 1 ); + if ( aIdx.GetNode().IsEndNode() && aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) + ; + else if ( aIdx.GetNode().IsSectionNode() ) + ; + else if ( !IsInTable() ) //No sections in table + { + //#120140# Do not need to insert a page/section break after a section end. Check this case first + bool bNeedExportBreakHere = true; + if ( rSect.GetType() == SectionType::ToxContent || rSect.GetType() == SectionType::ToxHeader ) + bNeedExportBreakHere = false; + else if ( aIdx.GetNode().IsTextNode() ) + { + SwTextNode *pTempNext = aIdx.GetNode().GetTextNode(); + if ( pTempNext ) + { + const SfxPoolItem * pTempItem = nullptr; + if (pTempNext->GetpSwAttrSet() && SfxItemState::SET == pTempNext->GetpSwAttrSet()->GetItemState(RES_PAGEDESC, false, &pTempItem) + && pTempItem && static_cast<const SwFormatPageDesc*>(pTempItem)->GetRegisteredIn()) + { + //Next node has a new page style which means this node is a section end. Do not insert another page/section break here + bNeedExportBreakHere = false; + } + } + } + else + { + /* Do not export Section Break in case DOCX containing MultiColumn and + * aIdx.GetNode().IsTextNode() is False i.e. Text node is NULL. + */ + const SwFrameFormat* pPgFormat = rSect.GetFormat(); + const SwFormatCol& rCol = pPgFormat->GetCol(); + sal_uInt16 nColumnCount = rCol.GetNumCols(); + const SwFormatNoBalancedColumns& rNoBalanced = pPgFormat->GetBalancedColumns(); + // Prevent the additional section break only for non-balanced columns. + if (nColumnCount > 1 && rNoBalanced.GetValue()) + { + bNeedExportBreakHere = false; + } + // No need to create a "fake" section if this is the end of the document, + // except to emulate balanced columns. + else if ( nColumnCount < 2 && aIdx == m_pDoc->GetNodes().GetEndOfContent() ) + bNeedExportBreakHere = false; + } + + if (bNeedExportBreakHere) //#120140# End of check + { + ReplaceCr( char(0xc) ); // indicator for Page/Section-Break + + const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent(); + if ( !pParentFormat ) + pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)); + + sal_uLong nRstLnNum; + if ( aIdx.GetNode().IsContentNode() ) + nRstLnNum = static_cast<SwContentNode&>(aIdx.GetNode()).GetSwAttrSet(). + GetLineNumber().GetStartValue(); + else + nRstLnNum = 0; + + AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum ); + } + else + { + OutputEndNode( *rNd.GetEndNode() ); + } + } + } + else if ( rNd.IsStartNode() ) + { + OutputStartNode( *rNd.GetStartNode() ); + } + else if ( rNd.IsEndNode() ) + { + OutputEndNode( *rNd.GetEndNode() ); + } + + if ( &rNd == &rNd.GetNodes().GetEndOfContent() ) + break; + + const SwNode * pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode(); + const SwNode * pNextNode = m_pTableInfo->getNextNode(pCurrentNode); + + if (pCurrentNode == pNextNode) + { + SAL_WARN("sw.ww8", "loop in TableInfo"); + pNextNode = nullptr; + } + + if (pNextNode != nullptr) + m_pCurPam->GetPoint()->nNode.Assign(*pNextNode); + else + ++m_pCurPam->GetPoint()->nNode; + + sal_uLong nPos = m_pCurPam->GetPoint()->nNode.GetIndex(); + ::SetProgressState( nPos, m_pCurPam->GetDoc()->GetDocShell() ); + } + + SAL_INFO( "sw.ww8.level2", "</WriteText>" ); +} + +void WW8Export::WriteMainText() +{ + SAL_INFO( "sw.ww8.level2", "<WriteMainText>" ); + + pFib->m_fcMin = Strm().Tell(); + + m_pCurPam->GetPoint()->nNode = m_pDoc->GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex(); + + WriteText(); + + if( 0 == Strm().Tell() - pFib->m_fcMin ) // no text ? + WriteCR(); // then CR at the end ( otherwise WW will complain ) + + pFib->m_ccpText = Fc2Cp( Strm().Tell() ); + m_pFieldMain->Finish( pFib->m_ccpText, 0 ); + + // ccpText includes Footnote and KF-text + // therefore pFib->ccpText may get updated as well + // save the StyleId of the last paragraph. Because WW97 take the style + // from the last CR, that will be written after footer/Header/footnotes/ + // annotation etc. + const SwTextNode* pLastNd = m_pCurPam->GetMark()->nNode.GetNode().GetTextNode(); + if( pLastNd ) + m_nLastFormatId = GetId( static_cast<SwTextFormatColl&>(pLastNd->GetAnyFormatColl()) ); + + SAL_INFO( "sw.ww8.level2", "</WriteMainText>" ); +} + +bool MSWordExportBase::IsInTable() const +{ + bool bResult = false; + + if (m_pCurPam != nullptr) + { + SwNode& rNode = m_pCurPam->GetNode(); + + if (m_pTableInfo.get() != nullptr) + { + ww8::WW8TableNodeInfo::Pointer_t pTableNodeInfo = m_pTableInfo->getTableNodeInfo(&rNode); + + if (pTableNodeInfo.get() != nullptr && pTableNodeInfo->getDepth() > 0) + { + bResult = true; + } + } + } + + return bResult; +} + +typedef ww8::WW8Sttb< ww8::WW8Struct > WW8SttbAssoc; + +void WW8Export::WriteFkpPlcUsw() +{ + // Graphics in the data stream + m_pGrf->Write(); // Graphics + + // output into WordDocument stream + m_pChpPlc->WriteFkps(); // Fkp.Chpx + m_pPapPlc->WriteFkps(); // Fkp.Papx + pSepx->WriteSepx( Strm() ); // Sepx + + // output into Table stream + m_pStyles->OutputStylesTable(); // for WW8 StyleTab + pFootnote->WritePlc( *this ); // Footnote-Ref & Text Plc + pEdn->WritePlc( *this ); // Endnote-Ref & Text Plc + m_pTextBxs->WritePlc( *this ); // Textbox Text Plc + m_pHFTextBxs->WritePlc( *this ); // Head/Foot-Textbox Text Plc + m_pAtn->WritePlc( *this ); // Annotation-Ref & Text Plc + + pSepx->WritePlcSed( *this ); // Slcx.PlcSed + pSepx->WritePlcHdd( *this ); // Slcx.PlcHdd + + m_pChpPlc->WritePlc(); // Plcx.Chpx + m_pPapPlc->WritePlc(); // Plcx.Papx + + if( m_pRedlAuthors ) + m_pRedlAuthors->Write( GetWriter() ); // sttbfRMark (RedlineAuthors) + m_pFieldMain->Write( *this ); // Fields ( Main Text ) + m_pFieldHdFt->Write( *this ); // Fields ( Header/Footer ) + m_pFieldFootnote->Write( *this ); // Fields ( FootNotes ) + m_pFieldEdn->Write( *this ); // Fields ( EndNotes ) + m_pFieldAtn->Write( *this ); // Fields ( Annotations ) + m_pFieldTextBxs->Write( *this ); // Fields ( Textboxes ) + m_pFieldHFTextBxs->Write( *this ); // Fields ( Head/Foot-Textboxes ) + + if (m_pEscher || m_pDoc->ContainsMSVBasic()) + { + /* + Every time MS 2000 creates an escher stream there is always + an ObjectPool dir (even if empty). It turns out that if a copy of + MS 2000 is used to open a document that contains escher graphics + exported from StarOffice without this empty dir then *if* that + copy of MS Office has never been used to open a MSOffice document + that has escher graphics (and an ObjectPool dir of course) and + that copy of office has not been used to draw escher graphics then + our exported graphics do not appear. Once you do open a ms + document with escher graphics or draw an escher graphic with that + copy of word, then all documents from staroffice that contain + escher work from then on. Tricky to track down, some sort of late + binding trickery in MS where solely for first time initialization + the existence of an ObjectPool dir is necessary for triggering + some magic. + */ + // avoid memory leak #i120098#, the unnamed obj will be released in destructor. + xEscherStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool); + } + + // dggInfo - escher stream + WriteEscher(); + + m_pSdrObjs->WritePlc( *this ); + m_pHFSdrObjs->WritePlc( *this ); + // spamom - office drawing table + // spahdr - header office drawing table + + m_pBkmks->Write( *this ); // Bookmarks - sttbfBkmk/ + // plcfBkmkf/plcfBkmkl + m_pFactoids->Write(*this); + + WriteNumbering(); + + RestoreMacroCmds(); + + m_pMagicTable->Write( *this ); + + m_pPiece->WritePc( *this ); // Piece-Table + m_aFontHelper.WriteFontTable(pTableStrm, *pFib); // FFNs + + //Convert OOo asian typography into MS typography structure + ExportDopTypography(pDop->doptypography); + + WriteDop( *this ); // Document-Properties + + // Write SttbfAssoc + WW8SttbAssoc * pSttbfAssoc = dynamic_cast<WW8SttbAssoc *> + (m_pDoc->getIDocumentExternalData().getExternalData(::sw::tExternalDataType::STTBF_ASSOC).get()); + + if ( pSttbfAssoc ) // #i106057# + { + std::vector<OUString> aStrings(pSttbfAssoc->getStrings()); + WriteAsStringTable(aStrings, pFib->m_fcSttbfAssoc, + pFib->m_lcbSttbfAssoc); + } + + Strm().Seek( 0 ); + + // Reclaim stored FIB data from document. + ::ww8::WW8FibData * pFibData = dynamic_cast<ww8::WW8FibData *> + (m_pDoc->getIDocumentExternalData().getExternalData(::sw::tExternalDataType::FIB).get()); + + if ( pFibData ) + { + pFib->m_fReadOnlyRecommended = + pFibData->getReadOnlyRecommended(); + pFib->m_fWriteReservation = + pFibData->getWriteReservation(); + } + + pFib->Write( Strm() ); // FIB +} + +void WW8Export::StoreDoc1() +{ + bool bNeedsFinalPara = false; + // Start of Text ( overwrite ) + SwWW8Writer::FillUntil( Strm(), pFib->m_fcMin ); + + WriteMainText(); // main text + sal_uInt8 nSprmsLen; + sal_uInt8 *pLastSprms = m_pPapPlc->CopyLastSprms(nSprmsLen); + + bNeedsFinalPara |= pFootnote->WriteText( *this ); // Footnote-Text + bNeedsFinalPara |= pSepx->WriteKFText( *this ); // K/F-Text + bNeedsFinalPara |= m_pAtn->WriteText( *this ); // Annotation-Text + bNeedsFinalPara |= pEdn->WriteText( *this ); // EndNote-Text + + // create the escher streams + CreateEscher(); + + bNeedsFinalPara |= m_pTextBxs->WriteText( *this ); //Textbox Text Plc + bNeedsFinalPara |= m_pHFTextBxs->WriteText( *this );//Head/Foot-Textbox Text Plc + + if (bNeedsFinalPara) + { + WriteCR(); + m_pPapPlc->AppendFkpEntry(Strm().Tell(), nSprmsLen, pLastSprms); + } + delete[] pLastSprms; + + pSepx->Finish( Fc2Cp( Strm().Tell() ));// Text + Footnote + HdFt as section end + m_pMagicTable->Finish( Fc2Cp( Strm().Tell() ),0); + + pFib->m_fcMac = Strm().Tell(); // End of all texts + + WriteFkpPlcUsw(); // FKP, PLC, ... +} + +void MSWordExportBase::AddLinkTarget(const OUString& rURL) +{ + if( rURL.isEmpty() || rURL[0] != '#' ) + return; + + OUString aURL( BookmarkToWriter( rURL.copy( 1 ) ) ); + sal_Int32 nPos = aURL.lastIndexOf( cMarkSeparator ); + + if( nPos < 2 ) + return; + + OUString sCmp = aURL.copy(nPos+1).replaceAll(" ", ""); + if( sCmp.isEmpty() ) + return; + + sCmp = sCmp.toAsciiLowerCase(); + sal_uLong nIdx = 0; + bool noBookmark = false; + + if( sCmp == "outline" ) + { + SwPosition aPos(*m_pCurPam->GetPoint()); + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + // If we can find the outline this bookmark refers to + // save the name of the bookmark and the + // node index number of where it points to + if( m_pDoc->GotoOutline( aPos, aName ) ) + { + nIdx = aPos.nNode.GetIndex(); + noBookmark = true; + } + } + else if( sCmp == "graphic" ) + { + SwNodeIndex* pIdx; + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + const SwFlyFrameFormat* pFormat = m_pDoc->FindFlyByName(aName, SwNodeType::Grf); + if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx()))) + { + nIdx = pIdx->GetNext()->GetIndex(); + noBookmark = true; + } + } + else if( sCmp == "frame" ) + { + SwNodeIndex* pIdx; + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + const SwFlyFrameFormat* pFormat = m_pDoc->FindFlyByName(aName, SwNodeType::Text); + if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx()))) + { + nIdx = pIdx->GetIndex() + 1; + noBookmark = true; + } + } + else if( sCmp == "ole" ) + { + SwNodeIndex* pIdx; + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + const SwFlyFrameFormat* pFormat = m_pDoc->FindFlyByName(aName, SwNodeType::Ole); + if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx()))) + { + nIdx = pIdx->GetNext()->GetIndex(); + noBookmark = true; + } + } + else if( sCmp == "region" ) + { + SwNodeIndex* pIdx; + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + for (const SwSectionFormat* pFormat : m_pDoc->GetSections()) + { + if (aName == pFormat->GetSection()->GetSectionName() + && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx()))) + { + nIdx = pIdx->GetIndex() + 1; + noBookmark = true; + break; + } + } + } + else if( sCmp == "table" ) + { + OUString aName(BookmarkToWriter(aURL.copy(0, nPos))); + const SwTable* pTable = SwTable::FindTable(m_pDoc->FindTableFormatByName(aName)); + if (pTable) + { + SwTableNode* pTableNode = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[1]->GetSttNd()->FindTableNode()); + if (pTableNode) + { + nIdx = pTableNode->GetIndex() + 2; + noBookmark = true; + } + } + } + if (noBookmark) + { + aBookmarkPair aImplicitBookmark; + aImplicitBookmark.first = aURL; + aImplicitBookmark.second = nIdx; + m_aImplicitBookmarks.push_back(aImplicitBookmark); + } +} + +void MSWordExportBase::CollectOutlineBookmarks(const SwDoc &rDoc) +{ + for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT)) + { + auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem); + if (!pINetFormat) + continue; + + const SwTextINetFormat* pTextAttr = pINetFormat->GetTextINetFormat(); + if (!pTextAttr) + continue; + + const SwTextNode* pTextNd = pTextAttr->GetpTextNode(); + if (!pTextNd) + continue; + + if (!pTextNd->GetNodes().IsDocNodes()) + continue; + + AddLinkTarget( pINetFormat->GetValue() ); + } + + for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_URL)) + { + auto pURL = dynamic_cast<const SwFormatURL*>(pItem); + if (!pURL) + continue; + + AddLinkTarget(pURL->GetURL()); + const ImageMap *pIMap = pURL->GetMap(); + if (!pIMap) + continue; + + for (size_t i=0; i < pIMap->GetIMapObjectCount(); ++i) + { + const IMapObject* pObj = pIMap->GetIMapObject(i); + if (!pObj) + continue; + AddLinkTarget( pObj->GetURL() ); + } + } +} + +namespace +{ + const sal_uLong WW_BLOCKSIZE = 0x200; + + ErrCode EncryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut) + { + sal_uLong nLen = rIn.TellEnd(); + rIn.Seek(0); + + sal_uInt8 in[WW_BLOCKSIZE]; + for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock) + { + std::size_t nBS = std::min(nLen - nI, WW_BLOCKSIZE); + nBS = rIn.ReadBytes(in, nBS); + if (!rCtx.InitCipher(nBlock)) { + return ERRCODE_IO_NOTSUPPORTED; + } + rCtx.Encode(in, nBS, in, nBS); + rOut.WriteBytes(in, nBS); + } + return ERRCODE_NONE; + } +} + +ErrCode MSWordExportBase::ExportDocument( bool bWriteAll ) +{ + m_nCharFormatStart = DEFAULT_STYLES_COUNT; + m_nFormatCollStart = m_nCharFormatStart + m_pDoc->GetCharFormats()->size() - 1; + + m_bStyDef = m_bBreakBefore = m_bOutKF = + m_bOutFlyFrameAttrs = m_bOutPageDescs = m_bOutTable = m_bOutFirstPage = + m_bOutGrf = m_bInWriteEscher = m_bStartTOX = + m_bInWriteTOX = false; + + m_bFootnoteAtTextEnd = m_bEndAtTextEnd = true; + + m_pParentFrame = nullptr; + m_pFlyOffset = nullptr; + m_eNewAnchorType = RndStdIds::FLY_AT_PAGE; + m_nTextTyp = TXT_MAINTEXT; + m_nStyleBeforeFly = m_nLastFormatId = 0; + m_pStyAttr = nullptr; + m_pCurrentStyle = nullptr; + m_pOutFormatNode = nullptr; + m_pEscher = nullptr; + m_pRedlAuthors = nullptr; + m_aTOXArr.clear(); + + if ( !m_pOLEExp ) + { + sal_uInt32 nSvxMSDffOLEConvFlags = 0; + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + if ( rOpt.IsMath2MathType() ) + nSvxMSDffOLEConvFlags |= OLE_STARMATH_2_MATHTYPE; + if ( rOpt.IsWriter2WinWord() ) + nSvxMSDffOLEConvFlags |= OLE_STARWRITER_2_WINWORD; + if ( rOpt.IsCalc2Excel() ) + nSvxMSDffOLEConvFlags |= OLE_STARCALC_2_EXCEL; + if ( rOpt.IsImpress2PowerPoint() ) + nSvxMSDffOLEConvFlags |= OLE_STARIMPRESS_2_POWERPOINT; + + m_pOLEExp.reset(new SvxMSExportOLEObjects( nSvxMSDffOLEConvFlags )); + } + + if ( !m_pOCXExp && m_pDoc->GetDocShell() ) + m_pOCXExp.reset(new SwMSConvertControls(m_pDoc->GetDocShell(), m_pCurPam.get())); + + // #i81405# - Collect anchored objects before changing the redline mode. + m_aFrames = GetFrames( *m_pDoc, bWriteAll? nullptr : m_pOrigPam ); + + m_nOrigRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + m_bOrigShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); + + if ( !m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + { + //restored to original state by SwWriter::Write + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags | + RedlineFlags::ShowDelete | + RedlineFlags::ShowInsert); + } + + // fix the SwPositions in m_aFrames after SetRedlineFlags + UpdateFramePositions(m_aFrames); + + m_aFontHelper.InitFontTable(*m_pDoc); + GatherChapterFields(); + + CollectOutlineBookmarks(*m_pDoc); + + // make unique OrdNums (Z-Order) for all drawing-/fly Objects + if ( m_pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + m_pDoc->getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )->RecalcObjOrdNums(); + + ErrCode err = ExportDocument_Impl(); + + m_aFrames.clear(); + + // park m_pCurPam in a "safe place" now that document is fully exported + // before toggling redline mode to avoid ~SwIndexReg assert e.g. export + // ooo103014-1.odt to .doc + // park m_pOrigPam as well, as needed for exporting abi9915-1.odt to doc + m_pOrigPam->DeleteMark(); + *m_pOrigPam->GetPoint() = SwPosition(m_pDoc->GetNodes().GetEndOfContent()); + static_cast<SwPaM&>(*m_pCurPam) = *m_pOrigPam; + + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags); + + return err; +} + +bool SwWW8Writer::InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec ) +{ + uno::Sequence< beans::NamedValue > aEncryptionData; + + if ( mpMedium ) + { + const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false); + if ( pEncryptionDataItem && ( pEncryptionDataItem->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) ) + { + OSL_ENSURE( false, "Unexpected EncryptionData!" ); + aEncryptionData.realloc( 0 ); + } + + if ( !aEncryptionData.hasElements() ) + { + // try to generate the encryption data based on password + const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(mpMedium->GetItemSet(), SID_PASSWORD, false); + if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() && pPasswordItem->GetValue().getLength() <= 15 ) + { + // Generate random number with a seed of time as salt. + rtlRandomPool aRandomPool = rtl_random_createPool (); + sal_uInt8 pDocId[ 16 ]; + rtl_random_getBytes( aRandomPool, pDocId, 16 ); + + rtl_random_destroyPool( aRandomPool ); + + sal_uInt16 aPassword[16] = {}; + + const OUString& sPassword(pPasswordItem->GetValue()); + for ( sal_Int32 nChar = 0; nChar < sPassword.getLength(); ++nChar ) + aPassword[nChar] = sPassword[nChar]; + + rCodec.InitKey( aPassword, pDocId ); + aEncryptionData = rCodec.GetEncryptionData(); + + mpMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) ); + } + } + + if ( aEncryptionData.hasElements() ) + mpMedium->GetItemSet()->ClearItem( SID_PASSWORD ); + } + + // nonempty encryption data means here that the codec was successfully initialized + return aEncryptionData.hasElements(); +} + +ErrCode WW8Export::ExportDocument_Impl() +{ + PrepareStorage(); + + pFib.reset(new WW8Fib(8, m_bDot)); + + tools::SvRef<SotStorageStream> xWwStrm( GetWriter().GetStorage().OpenSotStream( m_aMainStg ) ); + tools::SvRef<SotStorageStream> xTableStrm( xWwStrm ), xDataStrm( xWwStrm ); + xWwStrm->SetBufferSize( 32768 ); + + pFib->m_fWhichTableStm = true; + xTableStrm = GetWriter().GetStorage().OpenSotStream(SL::a1Table, StreamMode::STD_WRITE); + xDataStrm = GetWriter().GetStorage().OpenSotStream(SL::aData, StreamMode::STD_WRITE); + + xDataStrm->SetBufferSize( 32768 ); // for graphics + xTableStrm->SetBufferSize( 16384 ); // for the Font-/Style-Table, etc. + + xTableStrm->SetEndian( SvStreamEndian::LITTLE ); + xDataStrm->SetEndian( SvStreamEndian::LITTLE ); + + GetWriter().SetStream( xWwStrm.get() ); + pTableStrm = xTableStrm.get(); + pDataStrm = xDataStrm.get(); + + Strm().SetEndian( SvStreamEndian::LITTLE ); + + utl::TempFile aTempMain; + aTempMain.EnableKillingFile(); + utl::TempFile aTempTable; + aTempTable.EnableKillingFile(); + utl::TempFile aTempData; + aTempData.EnableKillingFile(); + + msfilter::MSCodec_Std97 aCtx; + bool bEncrypt = GetWriter().InitStd97CodecUpdateMedium(aCtx); + if ( bEncrypt ) + { + GetWriter().SetStream( + aTempMain.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ) ); + + pTableStrm = aTempTable.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ); + + pDataStrm = aTempData.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ); + + sal_uInt8 const aRC4EncryptionHeader[ 52 ] = {0}; + pTableStrm->WriteBytes(aRC4EncryptionHeader, 52); + } + + // Default: "Standard" + pSepx.reset(new WW8_WrPlcSepx( *this )); // Sections/headers/footers + + pFootnote.reset(new WW8_WrPlcFootnoteEdn( TXT_FTN )); // Footnotes + pEdn.reset(new WW8_WrPlcFootnoteEdn( TXT_EDN )); // Endnotes + m_pAtn = new WW8_WrPlcAnnotations; // PostIts + m_pFactoids.reset(new WW8_WrtFactoids); // Smart tags. + m_pTextBxs = new WW8_WrPlcTextBoxes( TXT_TXTBOX ); + m_pHFTextBxs = new WW8_WrPlcTextBoxes( TXT_HFTXTBOX ); + + m_pSdrObjs = new MainTextPlcDrawObj; // Draw-/Fly-Objects for main text + m_pHFSdrObjs = new HdFtPlcDrawObj; // Draw-/Fly-Objects for header/footer + + m_pBkmks = new WW8_WrtBookmarks; // Bookmarks + GetWriter().CreateBookmarkTable(); + + m_pPapPlc.reset(new WW8_WrPlcPn( *this, PAP, pFib->m_fcMin )); + m_pChpPlc.reset(new WW8_WrPlcPn( *this, CHP, pFib->m_fcMin )); + pO.reset(new ww::bytes); + m_pStyles.reset(new MSWordStyles( *this )); + m_pFieldMain.reset(new WW8_WrPlcField( 2, TXT_MAINTEXT )); + m_pFieldHdFt.reset(new WW8_WrPlcField( 2, TXT_HDFT )); + m_pFieldFootnote.reset(new WW8_WrPlcField( 2, TXT_FTN )); + m_pFieldEdn.reset(new WW8_WrPlcField( 2, TXT_EDN )); + m_pFieldAtn.reset(new WW8_WrPlcField( 2, TXT_ATN )); + m_pFieldTextBxs.reset(new WW8_WrPlcField( 2, TXT_TXTBOX )); + m_pFieldHFTextBxs.reset(new WW8_WrPlcField( 2, TXT_HFTXTBOX )); + + m_pMagicTable.reset(new WW8_WrMagicTable); + + m_pGrf.reset(new SwWW8WrGrf( *this )); + m_pPiece = new WW8_WrPct( pFib->m_fcMin ); + pDop.reset(new WW8Dop); + + pDop->fRevMarking = bool( RedlineFlags::On & m_nOrigRedlineFlags ); + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + pDop->fRMView = pLayout == nullptr || !pLayout->IsHideRedlines(); + pDop->fRMPrint = pDop->fRMView; + + // set AutoHyphenation flag if found in default para style + const SfxPoolItem* pItem; + SwTextFormatColl* pStdTextFormatColl = + m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false); + if (pStdTextFormatColl && SfxItemState::SET == pStdTextFormatColl->GetItemState( + RES_PARATR_HYPHENZONE, false, &pItem)) + { + pDop->fAutoHyphen = static_cast<const SvxHyphenZoneItem*>(pItem)->IsHyphen(); + } + + StoreDoc1(); + + ErrCode err = ERRCODE_NONE; + if ( bEncrypt ) + { + SvStream *pStrmTemp, *pTableStrmTemp, *pDataStrmTemp; + pStrmTemp = xWwStrm.get(); + pTableStrmTemp = xTableStrm.get(); + pDataStrmTemp = xDataStrm.get(); + + if ( pDataStrmTemp && pDataStrmTemp != pStrmTemp) { + err = EncryptRC4(aCtx, *pDataStrm, *pDataStrmTemp); + if (err != ERRCODE_NONE) { + goto done; + } + } + + err = EncryptRC4(aCtx, *pTableStrm, *pTableStrmTemp); + if (err != ERRCODE_NONE) { + goto done; + } + + // Write Unencrypted Header 52 bytes to the start of the table stream + // EncryptionVersionInfo (4 bytes): A Version structure where Version.vMajor MUST be 0x0001, and Version.vMinor MUST be 0x0001. + pTableStrmTemp->Seek( 0 ); + pTableStrmTemp->WriteUInt32( 0x10001 ); // nEncType + + sal_uInt8 pDocId[16]; + aCtx.GetDocId( pDocId ); + + sal_uInt8 pSaltData[16]; + sal_uInt8 pSaltDigest[16]; + aCtx.GetEncryptKey( pDocId, pSaltData, pSaltDigest ); + + pTableStrmTemp->WriteBytes(pDocId, 16); + pTableStrmTemp->WriteBytes(pSaltData, 16); + pTableStrmTemp->WriteBytes(pSaltDigest, 16); + + err = EncryptRC4(aCtx, GetWriter().Strm(), *pStrmTemp); + if (err != ERRCODE_NONE) { + goto done; + } + + // Write Unencrypted Fib 68 bytes to the start of the workdocument stream + pFib->m_fEncrypted = true; // fEncrypted indicates the document is encrypted. + pFib->m_fObfuscated = false; // Must be 0 for RC4. + pFib->m_nHash = 0x34; // encrypt header bytes count of table stream. + pFib->m_nKey = 0; // lkey2 must be 0 for RC4. + + pStrmTemp->Seek( 0 ); + pFib->WriteHeader( *pStrmTemp ); + done:; + } + + m_pGrf.reset(); + m_pMagicTable.reset(); + m_pFieldFootnote.reset(); + m_pFieldTextBxs.reset(); + m_pFieldHFTextBxs.reset(); + m_pFieldAtn.reset(); + m_pFieldEdn.reset(); + m_pFieldHdFt.reset(); + m_pFieldMain.reset(); + m_pStyles.reset(); + pO.reset(); + m_pChpPlc.reset(); + m_pPapPlc.reset(); + pSepx.reset(); + + delete m_pRedlAuthors; + delete m_pSdrObjs; + delete m_pHFSdrObjs; + delete m_pTextBxs; + delete m_pHFTextBxs; + delete m_pAtn; + pEdn.reset(); + pFootnote.reset(); + delete m_pBkmks; + delete m_pPiece; + pDop.reset(); + pFib.reset(); + GetWriter().SetStream( nullptr ); + + xWwStrm->SetBufferSize( 0 ); + xTableStrm->SetBufferSize( 0 ); + xDataStrm->SetBufferSize( 0 ); + if( 0 == pDataStrm->Seek( STREAM_SEEK_TO_END )) + { + xDataStrm.clear(); + pDataStrm = nullptr; + GetWriter().GetStorage().Remove(SL::aData); + } + + return err; +} + +void WW8Export::PrepareStorage() +{ + static const sal_uInt8 pData[] = + { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x09, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46, + + 0x18, 0x00, 0x00, 0x00, + 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', + 't', ' ', 'W', 'o', 'r', 'd', '-', 'D', + 'o', 'k', 'u', 'm', 'e', 'n', 't', 0x0, + + 0x0A, 0x00, 0x00, 0x00, + 'M', 'S', 'W', 'o', 'r', 'd', 'D', 'o', + 'c', 0x0, + + 0x10, 0x00, 0x00, 0x00, + 'W', 'o', 'r', 'd', '.', 'D', 'o', 'c', + 'u', 'm', 'e', 'n', 't', '.', '8', 0x0, + + 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + SvGlobalName aGName(MSO_WW8_CLASSID); + GetWriter().GetStorage().SetClass( + aGName, SotClipboardFormatId::NONE, "Microsoft Word-Document"); + tools::SvRef<SotStorageStream> xStor( GetWriter().GetStorage().OpenSotStream(sCompObj) ); + xStor->WriteBytes(pData, sizeof(pData)); + + SwDocShell* pDocShell = m_pDoc->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(), "DocumentProperties is null"); + + if (xDocProps.is()) + { + if ( SvtFilterOptions::Get().IsEnableWordPreview() ) + { + std::shared_ptr<GDIMetaFile> xMetaFile = + pDocShell->GetPreviewMetaFile(); + uno::Sequence<sal_Int8> metaFile( + sfx2::convertMetaFile(xMetaFile.get())); + sfx2::SaveOlePropertySet(xDocProps, &GetWriter().GetStorage(), &metaFile); + } + else + sfx2::SaveOlePropertySet( xDocProps, &GetWriter().GetStorage() ); + } + } +} + +ErrCode SwWW8Writer::WriteStorage() +{ + tools::SvRef<SotStorage> pOrigStg; + uno::Reference< packages::XPackageEncryption > xPackageEncryption; + std::shared_ptr<SvStream> pSotStorageStream; + uno::Sequence< beans::NamedValue > aEncryptionData; + if (mpMedium) + { + // Check for specific encryption requests + const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false); + if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData)) + { + ::comphelper::SequenceAsHashMap aHashData(aEncryptionData); + OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString()); + + if (sCryptoType.getLength()) + { + uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + uno::Sequence<uno::Any> aArguments{ + uno::makeAny(beans::NamedValue("Binary", uno::makeAny(true))) }; + xPackageEncryption.set( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY); + + if (xPackageEncryption.is()) + { + // We have an encryptor + // Create new temporary storage for content + pOrigStg = m_pStg; + pSotStorageStream = std::make_shared<SvMemoryStream>(); + m_pStg = new SotStorage(*pSotStorageStream); + } + } + } + } + + ErrCode nErrorCode = WriteStorageImpl(); + + if (xPackageEncryption.is()) + { + m_pStg->Commit(); + pSotStorageStream->Seek(0); + + // Encrypt data written into temporary storage + xPackageEncryption->setupEncryption(aEncryptionData); + + uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pSotStorageStream.get(), false)); + uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream); + + m_pStg = pOrigStg; + for (const beans::NamedValue & aStreamData : std::as_const(aStreams)) + { + // To avoid long paths split and open substorages recursively + // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09 + tools::SvRef<SotStorage> pStorage = m_pStg.get(); + OUString sFileName; + sal_Int32 idx = 0; + do + { + OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx); + if (!sPathElem.isEmpty()) + { + if (idx < 0) + { + sFileName = sPathElem; + } + else + { + pStorage = pStorage->OpenSotStorage(sPathElem); + if (!pStorage) + break; + } + } + } while (pStorage && idx >= 0); + + if (!pStorage) + { + nErrorCode = ERRCODE_IO_GENERAL; + break; + } + + tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName); + if (!pStream) + { + nErrorCode = ERRCODE_IO_GENERAL; + break; + } + uno::Sequence<sal_Int8> aStreamContent; + aStreamData.Value >>= aStreamContent; + size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getArray(), aStreamContent.getLength()); + if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength())) + { + nErrorCode = ERRCODE_IO_CANTWRITE; + break; + } + } + } + + return nErrorCode; +} +ErrCode SwWW8Writer::WriteStorageImpl() +{ + // #i34818# - update layout (if present), for SwWriteTable + SwViewShell* pViewShell = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( pViewShell != nullptr ) + pViewShell->CalcLayout(); + + long nMaxNode = m_pDoc->GetNodes().Count(); + ::StartProgress( STR_STATSTR_W4WWRITE, 0, nMaxNode, m_pDoc->GetDocShell() ); + + // Respect table at the beginning of the document + { + SwTableNode* pTNd = m_pCurrentPam->GetNode().FindTableNode(); + if( pTNd && m_bWriteAll ) + // start with the table node !! + m_pCurrentPam->GetPoint()->nNode = *pTNd; + } + + // Do the actual export + ErrCode err = ERRCODE_NONE; + { + bool bDot = mpMedium->GetFilter()->GetName().endsWith("Vorlage"); + WW8Export aExport(this, m_pDoc, m_pCurrentPam, m_pOrigPam, bDot); + m_pExport = &aExport; + err = aExport.ExportDocument( m_bWriteAll ); + m_pExport = nullptr; + } + + ::EndProgress( m_pDoc->GetDocShell() ); + return err; +} + +ErrCode SwWW8Writer::WriteMedium( SfxMedium& ) +{ + return WriteStorage(); +} + +ErrCode SwWW8Writer::Write( SwPaM& rPaM, SfxMedium& rMed, + const OUString* pFileName ) +{ + mpMedium = &rMed; + ErrCode nRet = StgWriter::Write( rPaM, rMed, pFileName ); + mpMedium = nullptr; + return nRet; +} + +MSWordExportBase::MSWordExportBase( SwDoc *pDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM *pOriginalPam ) + : m_aMainStg(sMainStream) + , m_pISet(nullptr) + , m_pPiece(nullptr) + , m_pTopNodeOfHdFtPage(nullptr) + , m_pBkmks(nullptr) + , m_pRedlAuthors(nullptr) + , m_pTableInfo(std::make_shared<ww8::WW8TableInfo>()) + , m_nCharFormatStart(0) + , m_nFormatCollStart(0) + , m_nStyleBeforeFly(0) + , m_nLastFormatId(0) + , m_nUniqueList(0) + , m_nHdFtIndex(0) + , m_nOrigRedlineFlags(RedlineFlags::NONE) + , m_bOrigShowChanges(true) + , m_pCurrentPageDesc(nullptr) + , m_bPrevTextNodeIsEmpty(false) + , m_bFirstTOCNodeWithSection(false) + , m_pChpIter(nullptr) + , m_pAtn(nullptr) + , m_pTextBxs(nullptr) + , m_pHFTextBxs(nullptr) + , m_pParentFrame(nullptr) + , m_pFlyOffset(nullptr) + , m_eNewAnchorType(RndStdIds::FLY_AS_CHAR) + , m_pStyAttr(nullptr) + , m_pOutFormatNode(nullptr) + , m_pCurrentStyle(nullptr) + , m_pSdrObjs(nullptr) + , m_pHFSdrObjs(nullptr) + , m_pEscher(nullptr) + , m_nTextTyp(0) + , m_bStyDef(false) + , m_bBreakBefore(false) + , m_bOutKF(false) + , m_bOutFlyFrameAttrs(false) + , m_bOutPageDescs(false) + , m_bOutFirstPage(false) + , m_bOutTable(false) + , m_bOutGrf(false) + , m_bInWriteEscher(false) + , m_bStartTOX(false) + , m_bInWriteTOX(false) + , m_bFootnoteAtTextEnd(false) + , m_bEndAtTextEnd(false) + , m_bHasHdr(false) + , m_bHasFtr(false) + , m_bSubstituteBullets(true) + , m_bTabInTOC(false) + , m_bHideTabLeaderAndPageNumbers(false) + , m_bExportModeRTF(false) + , m_bFontSizeWritten(false) + , m_bAddFootnoteTab(false) + , m_pDoc(pDocument) + , m_nCurStart(pCurrentPam->GetPoint()->nNode.GetIndex()) + , m_nCurEnd(pCurrentPam->GetMark()->nNode.GetIndex()) + , m_pCurPam(pCurrentPam) + , m_pOrigPam(pOriginalPam) +{ +} + +MSWordExportBase::~MSWordExportBase() +{ + if (m_pUsedNumTable) // all used NumRules + { + // clear the part of the list array that was copied from the document + // - it's an auto delete array, so the rest of the array which are + // duplicated lists that were added during the export will be deleted. + m_pUsedNumTable->erase(m_pUsedNumTable->begin(), m_pUsedNumTable->begin() + m_pUsedNumTable->size() - m_nUniqueList); + m_pUsedNumTable.reset(); + } + m_pOLEExp.reset(); + m_pOCXExp.reset(); +} + +WW8Export::WW8Export( SwWW8Writer *pWriter, + SwDoc *pDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM *pOriginalPam, + bool bDot ) + : MSWordExportBase( pDocument, pCurrentPam, pOriginalPam ) + , pTableStrm(nullptr) + , pDataStrm(nullptr) + , m_bDot(bDot) + , m_pWriter(pWriter) + , m_pAttrOutput(new WW8AttributeOutput(*this)) +{ +} + +WW8Export::~WW8Export() +{ +} + +AttributeOutputBase& WW8Export::AttrOutput() const +{ + return *m_pAttrOutput; +} + +MSWordSections& WW8Export::Sections() const +{ + return *pSepx; +} + +SwWW8Writer::SwWW8Writer(const OUString& rFltName, const OUString& rBaseURL) + : StgWriter(), + m_pExport( nullptr ), + mpMedium( nullptr ) +{ + assert(rFltName == FILTER_WW8); // WW6/7 export was removed + (void)rFltName; + SetBaseURL( rBaseURL ); +} + +SwWW8Writer::~SwWW8Writer() +{ +} + +extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell& rDoc, SotStorage& rStor, sal_Bool bSaveInto, const OUString& rStorageName ) +{ + SvxImportMSVBasic aTmp( rDoc, rStor ); + return sal_uInt32(aTmp.SaveOrDelMSVBAStorage( bSaveInto, rStorageName )); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void ExportDOC( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet ) +{ + xRet = new SwWW8Writer( rFltName, rBaseURL ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell &rDocS ) +{ + return sal_uInt32(SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( rDocS )); +} + +bool WW8_WrPlcFootnoteEdn::WriteText( WW8Export& rWrt ) +{ + bool bRet = false; + if (TXT_FTN == nTyp) + { + bRet = WriteGenericText( rWrt, TXT_FTN, rWrt.pFib->m_ccpFootnote ); + rWrt.m_pFieldFootnote->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ), + rWrt.pFib->m_ccpText ); + } + else + { + bRet = WriteGenericText( rWrt, TXT_EDN, rWrt.pFib->m_ccpEdn ); + rWrt.m_pFieldEdn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ), + rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote + + rWrt.pFib->m_ccpHdr + rWrt.pFib->m_ccpAtn ); + } + return bRet; +} + +void WW8_WrPlcFootnoteEdn::WritePlc( WW8Export& rWrt ) const +{ + if( TXT_FTN == nTyp ) + { + WriteGenericPlc( rWrt, TXT_FTN, rWrt.pFib->m_fcPlcffndText, + rWrt.pFib->m_lcbPlcffndText, rWrt.pFib->m_fcPlcffndRef, + rWrt.pFib->m_lcbPlcffndRef ); + } + else + { + WriteGenericPlc( rWrt, TXT_EDN, rWrt.pFib->m_fcPlcfendText, + rWrt.pFib->m_lcbPlcfendText, rWrt.pFib->m_fcPlcfendRef, + rWrt.pFib->m_lcbPlcfendRef ); + } +} + +bool WW8_WrPlcAnnotations::WriteText( WW8Export& rWrt ) +{ + bool bRet = WriteGenericText( rWrt, TXT_ATN, rWrt.pFib->m_ccpAtn ); + rWrt.m_pFieldAtn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ), + rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote + + rWrt.pFib->m_ccpHdr ); + return bRet; +} + +void WW8_WrPlcAnnotations::WritePlc( WW8Export& rWrt ) const +{ + WriteGenericPlc( rWrt, TXT_ATN, rWrt.pFib->m_fcPlcfandText, + rWrt.pFib->m_lcbPlcfandText, rWrt.pFib->m_fcPlcfandRef, + rWrt.pFib->m_lcbPlcfandRef ); +} + +void WW8_WrPlcTextBoxes::WritePlc( WW8Export& rWrt ) const +{ + if( TXT_TXTBOX == nTyp ) + { + WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcftxbxBkd, + rWrt.pFib->m_lcbPlcftxbxBkd, rWrt.pFib->m_fcPlcftxbxText, + rWrt.pFib->m_lcbPlcftxbxText ); + } + else + { + WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcfHdrtxbxBkd, + rWrt.pFib->m_lcbPlcfHdrtxbxBkd, rWrt.pFib->m_fcPlcfHdrtxbxText, + rWrt.pFib->m_lcbPlcfHdrtxbxText ); + } +} + +void WW8Export::RestoreMacroCmds() +{ + pFib->m_fcCmds = pTableStrm->Tell(); + + uno::Reference < embed::XStorage > xSrcRoot(m_pDoc->GetDocShell()->GetStorage()); + try + { + uno::Reference < io::XStream > xSrcStream = + xSrcRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READ ); + std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xSrcStream ); + + if ( pStream && ERRCODE_NONE == pStream->GetError()) + { + pFib->m_lcbCmds = pStream->TellEnd(); + pStream->Seek(0); + + std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[pFib->m_lcbCmds] ); + bool bReadOk = checkRead(*pStream, pBuffer.get(), pFib->m_lcbCmds); + if (bReadOk) + pTableStrm->WriteBytes(pBuffer.get(), pFib->m_lcbCmds); + } + } + catch ( const uno::Exception& ) + { + } + + // set len to FIB + pFib->m_lcbCmds = pTableStrm->Tell() - pFib->m_fcCmds; +} + +void WW8SHDLong::Write( WW8Export& rExport ) +{ + rExport.InsUInt32( m_cvFore ); + rExport.InsUInt32( m_cvBack ); + rExport.InsUInt16( 0 ); // ipat +} + +void WW8Export::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) +{ + const ::sw::mark::IFieldmark* pFieldmark = &rFieldmark; + const ::sw::mark::ICheckboxFieldmark* pAsCheckbox = dynamic_cast< const ::sw::mark::ICheckboxFieldmark* >( pFieldmark ); + + if ( ! ( rFieldmark.GetFieldname() == ODF_FORMTEXT || + rFieldmark.GetFieldname() == ODF_FORMDROPDOWN || + rFieldmark.GetFieldname() == ODF_FORMCHECKBOX ) ) + { + SAL_WARN( "sw.ww8", "unknown field type" ); + return; + } + + int type = 0; // TextFieldmark + if ( pAsCheckbox ) + type = 1; + if ( rFieldmark.GetFieldname() == ODF_FORMDROPDOWN ) + type=2; + + ::sw::mark::IFieldmark::parameter_map_t::const_iterator pParameter = rFieldmark.GetParameters()->find("name"); + OUString ffname; + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aName; + pParameter->second >>= aName; + const sal_Int32 nLen = std::min( sal_Int32(20), aName.getLength() ); + ffname = aName.copy(0, nLen); + } + + sal_uInt64 nDataStt = pDataStrm->Tell(); + m_pChpPlc->AppendFkpEntry(Strm().Tell()); + + WriteChar(0x01); + static sal_uInt8 aArr1[] = + { + 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation + + 0x06, 0x08, 0x01, // sprmCFData + 0x55, 0x08, 0x01, // sprmCFSpec + 0x02, 0x08, 0x01 // sprmCFFieldVanish + }; + sal_uInt8* pDataAdr = aArr1 + 2; + Set_UInt32(pDataAdr, nDataStt); + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aArr1 ), aArr1 ); + + struct FFDataHeader + { + sal_uInt32 version; + sal_uInt16 bits; + sal_uInt16 cch; + sal_uInt16 hps; + FFDataHeader() : version( 0xFFFFFFFF ), bits(0), cch(0), hps(0) {} + }; + + FFDataHeader aFieldHeader; + aFieldHeader.bits |= (type & 0x03); + + sal_Int32 ffres = 0; // rFieldmark.GetFFRes(); + if ( pAsCheckbox && pAsCheckbox->IsChecked() ) + ffres = 1; + else if ( type == 2 ) + { + ::sw::mark::IFieldmark::parameter_map_t::const_iterator pResParameter = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT); + if(pResParameter != rFieldmark.GetParameters()->end()) + pResParameter->second >>= ffres; + else + ffres = 0; + } + aFieldHeader.bits |= ( (ffres<<2) & 0x7C ); + + OUString ffdeftext; + OUString ffformat; + OUString ffhelptext = rFieldmark.GetFieldHelptext(); + if ( ffhelptext.getLength() > 255 ) + ffhelptext = ffhelptext.copy(0, 255); + OUString ffstattext; + OUString ffentrymcr; + OUString ffexitmcr; + if (type == 0) // iTypeText + { + sal_uInt16 nType = 0; + pParameter = rFieldmark.GetParameters()->find("Type"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aType; + pParameter->second >>= aType; + if ( aType == "number" ) nType = 1; + else if ( aType == "date" ) nType = 2; + else if ( aType == "currentTime" ) nType = 3; + else if ( aType == "currentDate" ) nType = 4; + else if ( aType == "calculated" ) nType = 5; + aFieldHeader.bits |= nType<<11; // FFDataBits-F 00111000 00000000 + } + + if ( nType < 3 || nType == 5 ) // not currentTime or currentDate + { + pParameter = rFieldmark.GetParameters()->find("Content"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aDefaultText; + pParameter->second >>= aDefaultText; + const sal_Int32 nLen = std::min( sal_Int32(255), aDefaultText.getLength() ); + ffdeftext = aDefaultText.copy (0, nLen); + } + } + + pParameter = rFieldmark.GetParameters()->find("MaxLength"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + sal_uInt16 nLength = 0; + pParameter->second >>= nLength; + nLength = std::min( sal_uInt16(32767), nLength ); + aFieldHeader.cch = nLength; + } + + pParameter = rFieldmark.GetParameters()->find("Format"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aFormat; + pParameter->second >>= aFormat; + const sal_Int32 nLen = std::min( sal_Int32(64), aFormat.getLength() ); + ffformat = aFormat.copy(0, nLen); + } + } + + pParameter = rFieldmark.GetParameters()->find("Help"); //help + if ( ffhelptext.isEmpty() && pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aHelpText; + pParameter->second >>= aHelpText; + const sal_Int32 nLen = std::min( sal_Int32(255), aHelpText.getLength() ); + ffhelptext = aHelpText.copy (0, nLen); + } + if ( !ffhelptext.isEmpty() ) + aFieldHeader.bits |= 0x1<<7; + + pParameter = rFieldmark.GetParameters()->find("Description"); // doc tooltip + if ( pParameter == rFieldmark.GetParameters()->end() ) + pParameter = rFieldmark.GetParameters()->find("Hint"); //docx tooltip + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aStatusText; + pParameter->second >>= aStatusText; + const sal_Int32 nLen = std::min( sal_Int32(138), aStatusText.getLength() ); + ffstattext = aStatusText.copy (0, nLen); + } + if ( !ffstattext.isEmpty() ) + aFieldHeader.bits |= 0x1<<8; + + pParameter = rFieldmark.GetParameters()->find("EntryMacro"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aEntryMacro; + pParameter->second >>= aEntryMacro; + const sal_Int32 nLen = std::min( sal_Int32(32), aEntryMacro.getLength() ); + ffentrymcr = aEntryMacro.copy (0, nLen); + } + + pParameter = rFieldmark.GetParameters()->find("ExitMacro"); + if ( pParameter != rFieldmark.GetParameters()->end() ) + { + OUString aExitMacro; + pParameter->second >>= aExitMacro; + const sal_Int32 nLen = std::min( sal_Int32(32), aExitMacro.getLength() ); + ffexitmcr = aExitMacro.copy (0, nLen); + } + + std::vector< OUString > aListItems; + if (type==2) + { + aFieldHeader.bits |= 0x8000; // ffhaslistbox + const ::sw::mark::IFieldmark::parameter_map_t* const pParameters = rFieldmark.GetParameters(); + ::sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY); + if(pListEntries != pParameters->end()) + { + uno::Sequence< OUString > vListEntries; + pListEntries->second >>= vListEntries; + copy(vListEntries.begin(), vListEntries.end(), back_inserter(aListItems)); + } + } + + const sal_uInt8 aFieldData[] = + { + 0x44,0, // the start of "next" data + 0,0,0,0,0,0,0,0,0,0, // PIC-Structure! /10 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16 + 0,0,0,0, // / /4 + }; + sal_uInt32 slen = sizeof(sal_uInt32) + + sizeof(aFieldData) + + sizeof( aFieldHeader.version ) + sizeof( aFieldHeader.bits ) + sizeof( aFieldHeader.cch ) + sizeof( aFieldHeader.hps ) + + 2*ffname.getLength() + 4 + + 2*ffformat.getLength() + 4 + + 2*ffhelptext.getLength() + 4 + + 2*ffstattext.getLength() + 4 + + 2*ffentrymcr.getLength() + 4 + + 2*ffexitmcr.getLength() + 4; + if ( type ) + slen += 2; // wDef + else + slen += 2*ffdeftext.getLength() + 4; //xstzTextDef + if ( type==2 ) { + slen += 2; // sttb ( fExtend ) + slen += 4; // for num of list items + const int items = aListItems.size(); + for( int i = 0; i < items; i++ ) { + OUString item = aListItems[i]; + slen += 2 * item.getLength() + 2; + } + } + + pDataStrm->WriteUInt32( slen ); + + int len = sizeof( aFieldData ); + OSL_ENSURE( len == 0x44-sizeof(sal_uInt32), "SwWW8Writer::WriteFormData(..) - wrong aFieldData length" ); + pDataStrm->WriteBytes( aFieldData, len ); + + pDataStrm->WriteUInt32( aFieldHeader.version ).WriteUInt16( aFieldHeader.bits ).WriteUInt16( aFieldHeader.cch ).WriteUInt16( aFieldHeader.hps ); + + SwWW8Writer::WriteString_xstz( *pDataStrm, ffname, true ); // Form field name + + if ( !type ) + SwWW8Writer::WriteString_xstz( *pDataStrm, ffdeftext, true ); + if ( type ) + pDataStrm->WriteUInt16( 0 ); + + SwWW8Writer::WriteString_xstz( *pDataStrm, ffformat, true ); + SwWW8Writer::WriteString_xstz( *pDataStrm, ffhelptext, true ); + SwWW8Writer::WriteString_xstz( *pDataStrm, ffstattext, true ); + SwWW8Writer::WriteString_xstz( *pDataStrm, ffentrymcr, true ); + SwWW8Writer::WriteString_xstz( *pDataStrm, ffexitmcr, true ); + if (type==2) { + pDataStrm->WriteUInt16( 0xFFFF ); + const int items=aListItems.size(); + pDataStrm->WriteUInt32( items ); + for(int i=0;i<items;i++) { + OUString item=aListItems[i]; + SwWW8Writer::WriteString_xstz( *pDataStrm, item, false ); + } + } +} + +void WW8Export::WriteHyperlinkData( const sw::mark::IFieldmark& /*rFieldmark*/ ) +{ + //@TODO implement me !!! +} + +void WW8AttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) +{ + SVBT16 nStyle; + ShortToSVBT16( m_rWW8Export.m_nStyleBeforeFly, nStyle ); + +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<OutWW8_TableNodeInfoInner>" << pNodeInfoInner->toString()); +#endif + + m_rWW8Export.pO->clear(); + + sal_uInt32 nShadowsBefore = pNodeInfoInner->getShadowsBefore(); + if (nShadowsBefore > 0) + { + ww8::WW8TableNodeInfoInner::Pointer_t + pTmpNodeInfoInner = std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr); + + pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth()); + pTmpNodeInfoInner->setEndOfCell(true); + + for (sal_uInt32 n = 0; n < nShadowsBefore; ++n) + { + m_rWW8Export.WriteCR(pTmpNodeInfoInner); + + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style # + TableInfoCell(pTmpNodeInfoInner); + m_rWW8Export.m_pPapPlc->AppendFkpEntry + ( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + + m_rWW8Export.pO->clear(); + } + } + + if (pNodeInfoInner->isEndOfCell()) + { + SAL_INFO( "sw.ww8", "<endOfCell/>" ); + + m_rWW8Export.WriteCR(pNodeInfoInner); + + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style # + TableInfoCell(pNodeInfoInner); + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + + m_rWW8Export.pO->clear(); + } + + sal_uInt32 nShadowsAfter = pNodeInfoInner->getShadowsAfter(); + if (nShadowsAfter > 0) + { + ww8::WW8TableNodeInfoInner::Pointer_t + pTmpNodeInfoInner= std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr); + + pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth()); + pTmpNodeInfoInner->setEndOfCell(true); + + for (sal_uInt32 n = 0; n < nShadowsAfter; ++n) + { + m_rWW8Export.WriteCR(pTmpNodeInfoInner); + + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style # + TableInfoCell(pTmpNodeInfoInner); + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + + m_rWW8Export.pO->clear(); + } + } + + if (pNodeInfoInner->isEndOfLine()) + { + SAL_INFO( "sw.ww8", "<endOfLine/>" ); + + TableRowEnd(pNodeInfoInner->getDepth()); + + ShortToSVBT16(0, nStyle); + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style # + TableInfoRow(pNodeInfoInner); + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + + m_rWW8Export.pO->clear(); + } + SAL_INFO( "sw.ww8", "</OutWW8_TableNodeInfoInner>" ); +} + +void MSWordExportBase::OutputStartNode( const SwStartNode & rNode) +{ + + ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = + m_pTableInfo->getTableNodeInfo( &rNode ); + + if (pNodeInfo.get() != nullptr) + { +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pNodeInfo->toString()); +#endif + const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners(); + ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aIt(aInners.rbegin()); + ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aEnd(aInners.rend()); + while (aIt != aEnd) + { + ww8::WW8TableNodeInfoInner::Pointer_t pInner = aIt->second; + + AttrOutput().TableNodeInfoInner(pInner); + ++aIt; + } + } + SAL_INFO( "sw.ww8", "</OutWW8_SwStartNode>" ); +} + +void MSWordExportBase::OutputEndNode( const SwEndNode &rNode ) +{ +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", "<OutWW8_SwEndNode>" << dbg_out(&rNode)); +#endif + + ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = m_pTableInfo->getTableNodeInfo( &rNode ); + + if (pNodeInfo.get() != nullptr) + { +#ifdef DBG_UTIL + SAL_INFO( "sw.ww8", pNodeInfo->toString()); +#endif + const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners(); + for (const auto& rEntry : aInners) + { + ww8::WW8TableNodeInfoInner::Pointer_t pInner = rEntry.second; + AttrOutput().TableNodeInfoInner(pInner); + } + } + SAL_INFO( "sw.ww8", "</OutWW8_SwEndNode>" ); +} + +const NfKeywordTable & MSWordExportBase::GetNfKeywordTable() +{ + if (m_pKeyMap == nullptr) + { + m_pKeyMap = std::make_shared<NfKeywordTable>(); + NfKeywordTable & rKeywordTable = *m_pKeyMap; + rKeywordTable[NF_KEY_D] = "d"; + rKeywordTable[NF_KEY_DD] = "dd"; + rKeywordTable[NF_KEY_DDD] = "ddd"; + rKeywordTable[NF_KEY_DDDD] = "dddd"; + rKeywordTable[NF_KEY_M] = "M"; + rKeywordTable[NF_KEY_MM] = "MM"; + rKeywordTable[NF_KEY_MMM] = "MMM"; + rKeywordTable[NF_KEY_MMMM] = "MMMM"; + rKeywordTable[NF_KEY_NN] = "ddd"; + rKeywordTable[NF_KEY_NNN] = "dddd"; + rKeywordTable[NF_KEY_NNNN] = "dddd"; + rKeywordTable[NF_KEY_YY] = "yy"; + rKeywordTable[NF_KEY_YYYY] = "yyyy"; + rKeywordTable[NF_KEY_H] = "H"; + rKeywordTable[NF_KEY_HH] = "HH"; + rKeywordTable[NF_KEY_MI] = "m"; + rKeywordTable[NF_KEY_MMI] = "mm"; + rKeywordTable[NF_KEY_S] = "s"; + rKeywordTable[NF_KEY_SS] = "ss"; + rKeywordTable[NF_KEY_AMPM] = "AM/PM"; + } + + return *m_pKeyMap; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx new file mode 100644 index 000000000..50d762f6e --- /dev/null +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -0,0 +1,1650 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_WRTWW8_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WRTWW8_HXX + +#include <sot/storage.hxx> +#include <tools/solar.h> +#include <tools/gen.hxx> +#include <editeng/editdata.hxx> + +#include <shellio.hxx> + +#include "ww8struc.hxx" +#include "ww8scan.hxx" +#include "fields.hxx" +#include "types.hxx" +#include "writerhelper.hxx" +#include <msfilter.hxx> +#include <expfld.hxx> +#include "WW8TableInfo.hxx" + +#include <vcl/graph.hxx> + +#include <optional> +#include <o3tl/typed_flags_set.hxx> + +#include <cstddef> +#include <memory> +#include <map> +#include <vector> + + +class SvxBrushItem; +class EditTextObject; + +// some forward declarations +class SwWW8AttrIter; +namespace msfilter +{ + class MSCodec_Std97; +} + +namespace editeng { class SvxBorderLine; } + +class AttributeOutputBase; +class DocxAttributeOutput; +class RtfAttributeOutput; +class BitmapPalette; +class SwEscherEx; +class DateTime; +namespace vcl { class Font; } +class MSWordExportBase; +class SdrObject; +class SdrTextObj; +class SfxItemSet; +class SvStream; +class SvxFontItem; +class SvxBoxItem; +class SwAttrSet; +class SwCharFormat; +class SwContentNode; +class SwField; +class SwFormat; +class SwFormatContent; +class SwFormatFootnote; +class SwFrameFormat; +class SwGrfNode; +class SwModify; +class SwNumFormat; +class SwNumRule; +class SwNumRuleTable; +class SwPageDesc; +class SwFormatPageDesc; +class SwOLENode; +class SwPostItField; +class SwRedlineData; +class SwSectionFormat; +class SwSectionNode; +class SwTableNode; +class SwTOXType; +class SwTextFormatColl; +class SwTextNode; +class SwWW8WrGrf; +class SwWW8Writer; +class MSWordStyles; +class WW8AttributeOutput; +class WW8Export; +class MSWordAttrIter; +class WW8_WrFkp; +class WW8_WrPlc0; +class WW8_WrPlc1; +class WW8_WrPlcField; +class WW8_WrMagicTable; +class WW8_WrPlcFootnoteEdn; +class WW8_WrPlcPn; +class WW8_WrPlcAnnotations; +class WW8_WrtFactoids; +class MSWordSections; +class WW8_WrPlcTextBoxes; +class WW8_WrPct; // administration +class WW8_WrtBookmarks; +class WW8_WrtRedlineAuthor; +class SvxMSExportOLEObjects; +class SwMSConvertControls; +class WW8_WrPc; +struct WW8_PdAttrDesc; +class SvxBrushItem; +namespace sw::mark { class IFieldmark; } +namespace com::sun::star::embed { class XEmbeddedObject; } + +typedef std::map<const css::embed::XEmbeddedObject*, sal_Int32> WW8OleMap; +typedef std::set< sal_Int32 > SwSoftPageBreakList; + +#define GRF_MAGIC_1 0x12 // 3 magic bytes for PicLocFc attribute +#define GRF_MAGIC_2 0x34 +#define GRF_MAGIC_3 0x56 +#define GRF_MAGIC_321 0x563412L + +#define OLE_PREVIEW_AS_EMF //If we want to export ole2 previews as emf in ww8+ + +enum class FieldFlags : sal_uInt8 // for InsertField- Method +{ + NONE = 0x00, + Start = 0x01, + CmdStart = 0x02, + CmdEnd = 0x04, + End = 0x10, + Close = 0x20, + All = 0x37 +}; +namespace o3tl { + template<> struct typed_flags<FieldFlags> : is_typed_flags<FieldFlags, 0x37> {}; +} + +enum TextTypes //enums for TextTypes +{ + TXT_MAINTEXT = 0, /*TXT_FTNEDN = 1,*/ TXT_HDFT = 2, TXT_FTN = 3, + TXT_EDN = 4, TXT_ATN = 5, TXT_TXTBOX = 6, TXT_HFTXTBOX= 7 +}; + +/** +enum to state the present state of the fly +*/ +enum FlyProcessingState +{ + FLY_PROCESSED, + FLY_POSTPONED, + FLY_NOT_PROCESSED +}; + +struct WW8_SepInfo +{ + const SwPageDesc* pPageDesc; + const SwSectionFormat* pSectionFormat; + const SwNode* pPDNd; + sal_uLong nLnNumRestartNo; + ::std::optional<sal_uInt16> oPgRestartNo; + bool bIsFirstParagraph; + + WW8_SepInfo( const SwPageDesc* pPD, const SwSectionFormat* pFormat, + sal_uLong nLnRestart, ::std::optional<sal_uInt16> oPgRestart = std::nullopt, + const SwNode* pNd = nullptr, bool bIsFirstPara = false ) + : pPageDesc( pPD ), pSectionFormat( pFormat ), pPDNd( pNd ), + nLnNumRestartNo( nLnRestart ), oPgRestartNo( oPgRestart ), + bIsFirstParagraph( bIsFirstPara ) + {} + + bool IsProtected() const; +}; + +/// Class to collect and output the sections/headers/footers. +// Plc for PageDescs -> Sepx ( Section Extensions ) +class MSWordSections +{ +protected: + bool mbDocumentIsProtected; + std::vector<WW8_SepInfo> aSects; + + void CheckForFacinPg( const WW8Export& rWrt ) const; + void NeedsDocumentProtected(const WW8_SepInfo &rInfo); + + //No copy, no assign + MSWordSections( const MSWordSections& ); + MSWordSections& operator=( const MSWordSections& ); +public: + explicit MSWordSections( MSWordExportBase& rExport ); + virtual ~MSWordSections(); + + virtual bool HeaderFooterWritten(); + + void AppendSection( const SwPageDesc* pPd, + const SwSectionFormat* pSectionFormat, + sal_uLong nLnNumRestartNo, + bool bIsFirstParagraph = false ); + void AppendSection( const SwFormatPageDesc& rPd, + const SwNode& rNd, + const SwSectionFormat* pSectionFormat, + sal_uLong nLnNumRestartNo ); + + /// Number of columns based on the most recent WW8_SepInfo. + sal_uInt16 CurrentNumberOfColumns( const SwDoc &rDoc ) const; + + /// Number of columns of the provided WW8_SepInfo. + static sal_uInt16 NumberOfColumns( const SwDoc &rDoc, const WW8_SepInfo& rInfo ); + + bool DocumentIsProtected() const { return mbDocumentIsProtected; } + + /// The most recent WW8_SepInfo. + const WW8_SepInfo* CurrentSectionInfo(); + + static void SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat, + sal_uInt8 nFlag ); + static void SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat, + sal_uInt8 nFlag ); + + /// Should we output borders? + static bool HasBorderItem( const SwFormat& rFormat ); +}; + +class WW8_WrPlcSepx : public MSWordSections +{ + std::vector<WW8_CP> aCps; + std::vector< std::shared_ptr<WW8_PdAttrDesc> > m_SectionAttributes; + // hack to prevent adding sections in endnotes + bool m_bHeaderFooterWritten; + std::unique_ptr<WW8_WrPlc0> pTextPos; // Position of the headers/footers + + WW8_WrPlcSepx( const WW8_WrPlcSepx& ) = delete; + WW8_WrPlcSepx& operator=( const WW8_WrPlcSepx& ) = delete; + +public: + explicit WW8_WrPlcSepx( MSWordExportBase& rExport ); + virtual ~WW8_WrPlcSepx() override; + + virtual bool HeaderFooterWritten() override; // override + + void AppendSep( WW8_CP nStartCp, + const SwPageDesc* pPd, + const SwSectionFormat* pSectionFormat, + sal_uLong nLnNumRestartNo ); + void AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPd, + const SwNode& rNd, + const SwSectionFormat* pSectionFormat, + sal_uLong nLnNumRestartNo ); + void Finish( WW8_CP nEndCp ) { aCps.push_back( nEndCp ); } + + bool WriteKFText( WW8Export& rWrt ); + void WriteSepx( SvStream& rStrm ) const; + void WritePlcSed( WW8Export& rWrt ) const; + void WritePlcHdd( WW8Export& rWrt ) const; + +private: + void WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt ); +public: + void OutHeaderFooter(WW8Export& rWrt, bool bHeader, + const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags, sal_uInt8 nFlag, sal_uInt8 nBreakCode); +}; + +// class WW8_WrPct to construct the piece table +class WW8_WrPct +{ + std::vector<std::unique_ptr<WW8_WrPc>> m_Pcts; + WW8_FC nOldFc; +public: + explicit WW8_WrPct(WW8_FC nStartFc); + ~WW8_WrPct(); + void AppendPc(WW8_FC nStartFc); + void WritePc( WW8Export& rWrt ); + void SetParaBreak(); + WW8_CP Fc2Cp( sal_uLong nFc ) const; +}; + +/// Collects and outputs fonts. +class wwFont +{ +//In some future land the stream could be converted to a nice stream interface +//and we could have harmony +private: + sal_uInt8 maWW8_FFN[6] = {}; + OUString msFamilyNm; + OUString msAltNm; + bool mbAlt; + FontPitch mePitch; + FontFamily meFamily; + rtl_TextEncoding meChrSet; +public: + wwFont( const OUString &rFamilyName, FontPitch ePitch, FontFamily eFamily, + rtl_TextEncoding eChrSet); + void Write( SvStream *pTableStram ) const; + void WriteDocx( DocxAttributeOutput* rAttrOutput ) const; + void WriteRtf( const RtfAttributeOutput* rAttrOutput ) const; + OUString const & GetFamilyName() const { return msFamilyNm; } + friend bool operator < (const wwFont &r1, const wwFont &r2); +}; + +class wwFontHelper +{ +private: + /// Keep track of fonts that need to be exported. + std::map<wwFont, sal_uInt16> maFonts; + + /// Convert from fast insertion map to linear vector in the order that we want to write. + std::vector< const wwFont* > AsVector() const; + +public: + wwFontHelper() : bLoadAllFonts(false) {} + /// rDoc used only to get the initial standard font(s) in use. + void InitFontTable(const SwDoc& rDoc); + sal_uInt16 GetId(const SvxFontItem& rFont); + sal_uInt16 GetId(const wwFont& rFont); + void WriteFontTable( SvStream *pTableStream, WW8Fib& pFib ); + void WriteFontTable( DocxAttributeOutput& rAttrOutput ); + void WriteFontTable( const RtfAttributeOutput& rAttrOutput ); + + /// If true, all fonts are loaded before processing the document. + bool bLoadAllFonts: 1; +}; + +class DrawObj +{ +public: + WW8_CP mnCp; // CP-Pos of references + sal_uInt32 mnShapeId; // ShapeId for the SwFrameFormats + ww8::Frame maContent; // the frame itself + Point maParentPos; // Points + sal_Int32 mnThick; // Border Thicknesses + SvxFrameDirection mnDirection; // If BiDi or not + unsigned int mnHdFtIndex; // 0 for main text, +1 for each subsequent + // msword hd/ft + + DrawObj(const ww8::Frame &rContent, WW8_CP nCp, Point aParentPos, SvxFrameDirection nDir, + unsigned int nHdFtIndex) + : mnCp(nCp), mnShapeId(0), maContent(rContent), maParentPos(aParentPos), + mnThick(0), mnDirection(nDir), mnHdFtIndex(nHdFtIndex) {} + void SetShapeDetails(sal_uInt32 nId, sal_Int32 nThick); +}; + +typedef std::vector<DrawObj> DrawObjVector; +typedef std::vector<DrawObj *> DrawObjPointerVector; + +class PlcDrawObj // PC for DrawObjects and Text-/OLE-/GRF-Boxes +{ +private: + DrawObjVector maDrawObjs; // vector of drawobjs +protected: + virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart, + sal_uInt32 nLen) const = 0; + virtual WW8_CP GetCpOffset(const WW8Fib &rFib) const = 0; +public: + PlcDrawObj() {} + void WritePlc( WW8Export& rWrt ) const; + bool Append( WW8Export const &, WW8_CP nCp, const ww8::Frame& rFormat, + const Point& rNdTopLeft ); + int size() { return maDrawObjs.size(); }; + DrawObjVector &GetObjArr() { return maDrawObjs; } + virtual ~PlcDrawObj(); +private: + PlcDrawObj(const PlcDrawObj&) = delete; + PlcDrawObj& operator=(const PlcDrawObj&) = delete; +}; + +class MainTextPlcDrawObj : public PlcDrawObj +{ +public: + MainTextPlcDrawObj() {} +private: + virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart, + sal_uInt32 nLen) const override; + virtual WW8_CP GetCpOffset(const WW8Fib &) const override; +private: + MainTextPlcDrawObj(const MainTextPlcDrawObj&) = delete; + MainTextPlcDrawObj& operator=(const MainTextPlcDrawObj&) = delete; +}; + +class HdFtPlcDrawObj : public PlcDrawObj +{ +public: + HdFtPlcDrawObj() {} +private: + virtual void RegisterWithFib(WW8Fib &rFib, sal_uInt32 nStart, + sal_uInt32 nLen) const override; + virtual WW8_CP GetCpOffset(const WW8Fib &rFib) const override; +private: + HdFtPlcDrawObj(const HdFtPlcDrawObj&) = delete; + HdFtPlcDrawObj& operator=(const HdFtPlcDrawObj&) = delete; +}; + +typedef std::pair<OUString, sal_uLong> aBookmarkPair; + +class WW8_WrtRedlineAuthor : public sw::util::WrtRedlineAuthor +{ + public: + virtual void Write(Writer &rWrt) override; +}; + +/** Structure that is used to save some of the WW8Export/DocxExport data. + + It is used to be able to recurse inside of the WW8Export/DocxExport (eg. + for the needs of the tables) - you need to tall WriteText() from there with + new values of PaM etc. + + It must contain all the stuff that might be saved either in WW8Export or in + DocxExport, because it makes no sense to do it abstract, and specialize it + for each of the cases. If you implement other *Export, just add the needed + members here, and store them in the appropriate SaveData() method. + */ +struct MSWordSaveData +{ + Point* pOldFlyOffset; + RndStdIds eOldAnchorType; + std::unique_ptr<ww::bytes> pOOld; ///< WW8Export only + std::shared_ptr<SwUnoCursor> pOldPam; + SwPaM* pOldEnd; + sal_uLong nOldStart, nOldEnd; + const ww8::Frame* pOldFlyFormat; + const SwPageDesc* pOldPageDesc; + + bool bOldWriteAll : 1; ///< WW8Export only + bool bOldOutTable : 1; + bool bOldFlyFrameAttrs : 1; + bool bOldStartTOX : 1; + bool bOldInWriteTOX : 1; + // m_bOutPageDescs does not have to be saved in MSWordExportBase::SaveData + // since it is only modified when outputting special texts. +}; + +/// Base class for WW8Export and DocxExport +class MSWordExportBase +{ +public: + wwFontHelper m_aFontHelper; + std::vector<sal_uLong> m_aChapterFieldLocs; + OUString m_aMainStg; + std::vector<const SwTOXType*> m_aTOXArr; + const SfxItemSet* m_pISet; // for double attributes + const SwFrameFormat* m_pFirstPageFormat = nullptr; + WW8_WrPct* m_pPiece; // Pointer to Piece-Table + std::unique_ptr<SwNumRuleTable> m_pUsedNumTable; // all used NumRules + /// overriding numdef index -> (existing numdef index, abstractnumdef index) + std::map<size_t, std::pair<size_t, size_t>> m_OverridingNums; + /// list-id -> abstractnumdef index + std::map<OUString, size_t> m_Lists; + + /// Map of maps for list levels overrides + /// listid -> level number -> restart value + std::map < size_t, std::map<size_t, size_t> > m_ListLevelOverrides; + + const SwTextNode *m_pTopNodeOfHdFtPage; ///< Top node of host page when in hd/ft + std::stack< sal_Int32 > m_aCurrentCharPropStarts; ///< To remember the position in a run. + WW8_WrtBookmarks* m_pBkmks; + WW8_WrtRedlineAuthor* m_pRedlAuthors; + std::shared_ptr<NfKeywordTable> m_pKeyMap; + std::unique_ptr<SvxMSExportOLEObjects> m_pOLEExp; + std::unique_ptr<SwMSConvertControls> m_pOCXExp; + WW8OleMap m_aOleMap; // To remember all already exported ole objects + ww8::WW8TableInfo::Pointer_t m_pTableInfo; + + sal_uInt16 m_nCharFormatStart; + sal_uInt16 m_nFormatCollStart; + sal_uInt16 m_nStyleBeforeFly; ///< style number of the node + ///< to which the Fly is connected + sal_uInt16 m_nLastFormatId; ///< Style of last TextNode in normal range + sal_uInt16 m_nUniqueList; ///< current number for creating unique list names + unsigned int m_nHdFtIndex; + + RedlineFlags m_nOrigRedlineFlags; ///< Remember the original redline mode + bool m_bOrigShowChanges; ///< Remember the original Show Changes mode + +public: + /* implicit bookmark vector containing pairs of node indexes and bookmark names */ + std::vector<aBookmarkPair> m_aImplicitBookmarks; + ww8::Frames m_aFrames; // The floating frames in this document + const SwPageDesc *m_pCurrentPageDesc; + bool m_bPrevTextNodeIsEmpty; + bool m_bFirstTOCNodeWithSection; + std::unique_ptr<WW8_WrPlcPn> m_pPapPlc; + std::unique_ptr<WW8_WrPlcPn> m_pChpPlc; + MSWordAttrIter* m_pChpIter; + std::unique_ptr<MSWordStyles> m_pStyles; + WW8_WrPlcAnnotations* m_pAtn; + std::unique_ptr<WW8_WrtFactoids> m_pFactoids; + WW8_WrPlcTextBoxes *m_pTextBxs, *m_pHFTextBxs; + + struct LinkedTextboxInfo //help analyze textbox flow links + { + sal_Int32 nId; + sal_Int32 nSeq; + OUString sNextChain; + OUString sPrevChain; + LinkedTextboxInfo(): nId(0), nSeq(0) {} + }; + std::map<OUString,LinkedTextboxInfo> m_aLinkedTextboxesHelper; + bool m_bLinkedTextboxesHelperInitialized = false; + sal_Int32 m_nLinkedTextboxesChainId=0; + + const ww8::Frame *m_pParentFrame; // If set we are exporting content inside + // a frame, e.g. a graphic node + + Point* m_pFlyOffset; // for adjusting of character-bound Fly in the Writer, + RndStdIds m_eNewAnchorType; // that is paragraph-bound in the WW. + + std::unique_ptr<WW8_WrPlcField> m_pFieldMain; // fields in MainText + std::unique_ptr<WW8_WrPlcField> m_pFieldHdFt; // fields in Header/Footer + std::unique_ptr<WW8_WrPlcField> m_pFieldFootnote; // fields in FootNotes + std::unique_ptr<WW8_WrPlcField> m_pFieldEdn; // fields in EndNotes + std::unique_ptr<WW8_WrPlcField> m_pFieldAtn; // fields in Annotations + std::unique_ptr<WW8_WrPlcField> m_pFieldTextBxs; // fields in textboxes + std::unique_ptr<WW8_WrPlcField> m_pFieldHFTextBxs; // fields in header/footer textboxes + std::unique_ptr<WW8_WrMagicTable> m_pMagicTable; // keeps track of table cell positions, and + // marks those that contain graphics, + // which is required to make word display + // graphics inside tables + std::unique_ptr<SwWW8WrGrf> m_pGrf; + const SwAttrSet* m_pStyAttr; // StyleAttr for Tabs + const SwModify* m_pOutFormatNode; // write Format or Node + const SwFormat *m_pCurrentStyle; // iff bStyDef=true, then this store the current style + + MainTextPlcDrawObj *m_pSdrObjs; // Draw-/Fly-Objects + HdFtPlcDrawObj *m_pHFSdrObjs; // Draw-/Fly-Objects in header or footer + + SwEscherEx* m_pEscher; // escher export class + // #i43447# - removed +// SwTwips nFlyWidth, nFlyHeight; // for adaptation of graphics + + sal_uInt8 m_nTextTyp; + + bool m_bStyDef : 1; // should Style be written? + bool m_bBreakBefore : 1; // Breaks are being written 2 times + bool m_bOutKF : 1; // Header/Footer texts are being written + bool m_bOutFlyFrameAttrs : 1; // Frame-attr of Flys are being written + bool m_bOutPageDescs : 1; ///< PageDescs (section properties) are being written + bool m_bOutFirstPage : 1; // write Attrset of FirstPageDesc + bool m_bOutTable : 1; // table is being written + // ( is reset e.g. for Flys in a table ) + bool m_bOutGrf : 1; // graphics are being written + bool m_bInWriteEscher : 1; // in write textboxes + bool m_bStartTOX : 1; // true: a TOX is started + bool m_bInWriteTOX : 1; // true: all content are in a TOX + bool m_bFootnoteAtTextEnd : 1; // true: all FTN at Textend + bool m_bEndAtTextEnd : 1; // true: all END at Textend + bool m_bHasHdr : 1; + bool m_bHasFtr : 1; + bool m_bSubstituteBullets : 1; // true: SubstituteBullet() gets called + bool m_bTabInTOC : 1; //true for TOC field flag 'w' + + bool m_bHideTabLeaderAndPageNumbers : 1 ; // true: the 'z' field of TOC is set. + bool m_bExportModeRTF; + /// Is font size written already as part of the current character properties? + bool m_bFontSizeWritten; + bool m_bAddFootnoteTab; // only one aesthetic spacing tab per footnote + + SwDoc *m_pDoc; + sal_uLong m_nCurStart, m_nCurEnd; + std::shared_ptr<SwUnoCursor> & m_pCurPam; + SwPaM *m_pOrigPam; + + /// Stack to remember the nesting (see MSWordSaveData for more) + std::stack< MSWordSaveData > m_aSaveData; + + /// Used to split the runs according to the bookmarks start and ends + typedef std::vector< ::sw::mark::IMark* > IMarkVector; + IMarkVector m_rSortedBookmarksStart; + IMarkVector m_rSortedBookmarksEnd; + IMarkVector m_rSortedAnnotationMarksStart; + IMarkVector m_rSortedAnnotationMarksEnd; + +public: + /// The main function to export the document. + ErrCode ExportDocument( bool bWriteAll ); + + /// Iterate through the nodes and call the appropriate OutputNode() on them. + void WriteText(); + + /// Return whether currently exported node is in table. + bool IsInTable() const; + + /// Set the pCurPam appropriately and call WriteText(). + /// + /// Used to export paragraphs in footnotes/endnotes/etc. + void WriteSpecialText( sal_uLong nStart, sal_uLong nEnd, sal_uInt8 nTTyp ); + + /// Export the pool items to attributes (through an attribute output class). + void ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars = false ); + + /// Return the numeric id of the numbering rule + sal_uInt16 GetNumberingId( const SwNumRule& rNumRule ); + + /// Return the numeric id of the style. + sal_uInt16 GetId( const SwTextFormatColl& rColl ) const; + + /// Return the numeric id of the style. + sal_uInt16 GetId( const SwCharFormat* pFormat ) const; + + sal_uInt16 GetId( const SwTOXType& rTOXType ); + + /// Return the numeric id of the font (and add it to the font list if needed) + sal_uInt16 GetId( const SvxFontItem& rFont) + { + return m_aFontHelper.GetId(rFont); + } + /// @overload + void GetId( const wwFont& rFont) + { + m_aFontHelper.GetId(rFont); + } + + const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const; + template<class T> const T& GetItem( TypedWhichId<T> nWhich ) const + { + return static_cast<const T&>(GetItem(sal_uInt16(nWhich))); + } + + /// Find the reference. + bool HasRefToAttr(const OUString& rName); + bool HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo); + + /// Find the bookmark name. + static OUString GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo ); + + /// Use OutputItem() on an item set according to the parameters. + void OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript, bool bExportParentItemSet ); + + SvxFrameDirection GetDefaultFrameDirection( ) const; + + /// Right to left? + SvxFrameDirection TrueFrameDirection( const SwFrameFormat& rFlyFormat ) const; + + /// Right to left? + SvxFrameDirection GetCurrentPageDirection() const; + + /// In case of numbering restart. + + /// List is set to restart at a particular value so for export make a + /// completely new list based on this one and export that instead, + /// which duplicates words behaviour in this respect. + SwNumRule * DuplicateNumRuleImpl(const SwNumRule *pRule); + + /// check if a new abstractNum is needed for this list + sal_uInt16 DuplicateAbsNum(OUString const& rListId, + SwNumRule const& rAbstractRule); + + + /// Create a overriding numbering definition (if it does not yet exist) + /// @return index of the overriding numbering definition + sal_uInt16 OverrideNumRule(SwNumRule const& rExistingRule, + OUString const& rListId, + SwNumRule const& rAbstractRule); + + /// Store list level overrides (restart of list) + void AddListLevelOverride(sal_uInt16 nListId, + sal_uInt16 nLevelNum, + sal_uInt16 nStartAt); + + /// Access to the attribute output class. + virtual AttributeOutputBase& AttrOutput() const = 0; + + /// Access to the sections/headers/footres. + virtual MSWordSections& Sections() const = 0; + + /// Determines if the import filter already quoted fields or not. + virtual bool FieldsQuoted() const = 0; + + /// Determines the Section Breaks are to be added for TOX Section + virtual bool AddSectionBreaksForTOX() const = 0; + + /// Used to filter out attributes that can be e.g. written to .doc but not to .docx + virtual bool ignoreAttributeForStyleDefaults( sal_uInt16 /*nWhich*/ ) const { return false; } + + /// If saving page break is preferred as a paragraph attribute (yes) or as a special character (no). + virtual bool PreferPageBreakBefore() const = 0; + + /// Text in tables can be postponed except for .doc + virtual bool AllowPostponedTextInTable() const { return true; } + + /// Guess the script (asian/western). + /// + /// Sadly word does not have two different sizes for asian font size and + /// western font size, it has two different fonts, but not sizes, so we + /// have to use our guess as to the script used and disable the export of + /// one type. The same occurs for font weight and posture (bold and + /// italic). + /// + /// In addition WW7- has only one character language identifier while WW8+ + /// has two + virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) = 0; + + virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) = 0; + + virtual void AppendBookmark( const OUString& rName ) = 0; + + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) = 0; + + virtual void AppendSmartTags(SwTextNode& /*rTextNode*/) { } + + //For i120928,add this interface to export graphic of bullet + virtual void ExportGrfBullet(const SwTextNode& rNd) = 0; + + // FIXME probably a hack... + virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t() ) = 0; + + // FIXME definitely a hack, must not be here - it can't do anything + // sensible for docx + virtual void WriteChar( sal_Unicode c ) = 0; + + /// Output attributes. + void OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat = false ); + + /// Getter for pISet. + const SfxItemSet* GetCurItemSet() const { return m_pISet; } + + /// Setter for pISet. + void SetCurItemSet( const SfxItemSet* pS ) { m_pISet = pS; } + + /// Remember some of the members so that we can recurse in WriteText(). + virtual void SaveData( sal_uLong nStt, sal_uLong nEnd ); + + /// Restore what was saved in SaveData(). + virtual void RestoreData(); + + /// The return value indicates, if a follow page desc is written. + bool OutputFollowPageDesc( const SfxItemSet* pSet, + const SwTextNode* pNd ); + + /// Write header/footer text. + void WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader); + + /// Format of the section. + static const SwSectionFormat* GetSectionFormat( const SwNode& rNd ); + + /// Line number of the section start. + static sal_uLong GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd ); + + /// Start new section. + void OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen = false, bool isTextNodeEmpty = false); + + /// Write section properties. + /// + /// pA is ignored for docx. + void SectionProperties( const WW8_SepInfo& rSectionInfo, WW8_PdAttrDesc* pA = nullptr ); + + /// Output the numbering table. + virtual void WriteNumbering() = 0; + + /// Write static data of SwNumRule - LSTF + void NumberingDefinitions(); + + /// Write all Levels for all SwNumRules - LVLF + void AbstractNumberingDefinitions(); + + /// Write one numbering level + void NumberingLevel(SwNumRule const& rRule, sal_uInt8 nLvl); + + // Convert the bullet according to the font. + void SubstituteBullet( OUString& rNumStr, rtl_TextEncoding& rChrSet, + OUString& rFontName ) const; + + /// Setup the pA's info. + virtual void SetupSectionPositions( WW8_PdAttrDesc* /*pA*/ ) {} + + /// Top node of host page when in header/footer. + void SetHdFtPageRoot( const SwTextNode *pNd ) { m_pTopNodeOfHdFtPage = pNd; } + + /// Top node of host page when in header/footer. + const SwTextNode *GetHdFtPageRoot() const { return m_pTopNodeOfHdFtPage; } + + /// Output the actual headers and footers. + virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags, + const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, + sal_uInt8 nBreakCode) = 0; + + /// Write the field + virtual void OutputField( const SwField* pField, ww::eField eFieldType, + const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) = 0; + + /// Write the data of the form field + virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) = 0; + virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) = 0; + + virtual void DoComboBox(const OUString &rName, + const OUString &rHelp, + const OUString &ToolTip, + const OUString &rSelected, + const css::uno::Sequence<OUString> &rListItems) = 0; + + virtual void DoFormText(const SwInputField * pField) = 0; + + static bool NoPageBreakSection( const SfxItemSet *pSet ); + + // Compute the number format for WW dates + bool GetNumberFormat(const SwField& rField, OUString& rStr); + + virtual sal_uLong ReplaceCr( sal_uInt8 nChar ) = 0; + + const SfxPoolItem* HasItem( sal_uInt16 nWhich ) const; + + /// Returns the index of a picture bullet, used in numberings. + int GetGrfIndex(const SvxBrushItem& rBrush); + + enum ExportFormat { DOC = 0, RTF = 1, DOCX = 2}; + virtual ExportFormat GetExportFormat() const = 0; + +protected: + /// Format-dependent part of the actual export. + virtual ErrCode ExportDocument_Impl() = 0; + + /// Get the next position in the text node to output + sal_Int32 GetNextPos( SwWW8AttrIter const * pAttrIter, const SwTextNode& rNode, sal_Int32 nCurrentPos ); + + /// Update the information for GetNextPos(). + void UpdatePosition( SwWW8AttrIter* pAttrIter, sal_Int32 nCurrentPos ); + + /// Output SwTextNode + virtual void OutputTextNode( SwTextNode& ); + + /// Setup the chapter fields (maChapterFieldLocs). + void GatherChapterFields(); + + void AddLinkTarget( const OUString& rURL ); + void CollectOutlineBookmarks( const SwDoc &rDoc ); + + bool SetCurrentPageDescFromNode(const SwNode &rNd); + bool ContentContainsChapterField(const SwFormatContent &rContent) const; + bool FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const; + + virtual void SectionBreaksAndFrames( const SwTextNode& rNode ) = 0; + + virtual void PrepareNewPageDesc( const SfxItemSet* pSet, + const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc ) = 0; + + /// Return value indicates if an inherited outline numbering is suppressed. + virtual bool DisallowInheritingOutlineNumbering(const SwFormat &rFormat) = 0; + + /// Output SwStartNode + void OutputStartNode( const SwStartNode& ); + + /// Output SwEndNode + virtual void OutputEndNode( const SwEndNode& ); + + /// Output SwGrfNode + virtual void OutputGrfNode( const SwGrfNode& ) = 0; + + /// Output SwOLENode + virtual void OutputOLENode( const SwOLENode& ) = 0; + + virtual void OutputLinkedOLE( const OUString& ) = 0; + + /// Output SwSectionNode + void OutputSectionNode( const SwSectionNode& ); + static void UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode); + + virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) = 0; + + /// Call the right (virtual) function according to the type of the item. + /// + /// One of OutputTextNode(), OutputGrfNode(), or OutputOLENode() + void OutputContentNode( SwContentNode& ); + + /// Find the nearest bookmark from the current position. + /// + /// Returns false when there is no bookmark. + bool NearestBookmark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly ); + + void GetSortedBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ); + + bool GetBookmarks( const SwTextNode& rNd, sal_Int32 nStt, sal_Int32 nEnd, + IMarkVector& rArr ); + + /// Find the nearest annotation mark from the current position. + /// + void NearestAnnotationMark( sal_Int32& rNearest, const sal_Int32 nCurrentPos, bool bNextPositionOnly ); + + void GetSortedAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ); + + bool GetAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nStt, sal_Int32 nEnd, + IMarkVector& rArr ); + + const NfKeywordTable & GetNfKeywordTable(); + + void SetCurPam(sal_uLong nStt, sal_uLong nEnd); + + /// Get background color of the document, if there is one. + std::unique_ptr<SvxBrushItem> getBackground(); + /// Populates m_vecBulletPic with all the bullet graphics used by numberings. + int CollectGrfsOfBullets(); + /// Write the numbering picture bullets. + void BulletDefinitions(); + + bool NeedSectionBreak( const SwNode& rNd ) const; + bool NeedTextNodeSplit( const SwTextNode& rNd, SwSoftPageBreakList& pList ) const; + + std::vector<const Graphic*> m_vecBulletPic; ///< Vector to record all the graphics of bullets + +public: + MSWordExportBase( SwDoc *pDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM *pOriginalPam ); + virtual ~MSWordExportBase(); + + // TODO move as much as possible here from WW8Export! ;-) + + static void CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft ); + +private: + MSWordExportBase( const MSWordExportBase& ) = delete; + MSWordExportBase& operator=( const MSWordExportBase& ) = delete; +}; + +/// The writer class that gets called for the WW8 filter. +class SwWW8Writer: public StgWriter +{ +// friends to get access to m_pExport +// FIXME avoid that, this is probably not what we want +// (if yes, remove the friends, and provide here a GetExport() method) +friend void WW8_WrtRedlineAuthor::Write(Writer &rWrt); + + WW8Export *m_pExport; + SfxMedium *mpMedium; + +public: + SwWW8Writer(const OUString& rFltName, const OUString& rBaseURL); + virtual ~SwWW8Writer() override; + + virtual ErrCode WriteStorage() override; + virtual ErrCode WriteMedium( SfxMedium& ) override; + + // TODO most probably we want to be able to get these in + // MSExportFilterBase + using Writer::getIDocumentSettingAccess; + +public: + static void InsUInt16(ww::bytes &rO, sal_uInt16 n); + static void InsUInt32(ww::bytes &rO, sal_uInt32 n); + static void InsAsString16(ww::bytes &rO, const OUString& rStr); + static void InsAsString8(ww::bytes & O, const OUString& rStr, + rtl_TextEncoding eCodeSet); + + static sal_uLong FillUntil( SvStream& rStrm, sal_uLong nEndPos = 0 ); + static void FillCount( SvStream& rStrm, sal_uLong nCount ); + + static void WriteShort( SvStream& rStrm, sal_Int16 nVal ) { rStrm.WriteInt16( nVal ); } + static void WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal ); + + static void WriteLong( SvStream& rStrm, sal_Int32 nVal ) { rStrm.WriteInt32( nVal ); } + static void WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal ); + + static void WriteString16(SvStream& rStrm, const OUString& rStr, + bool bAddZero); + static void WriteString8(SvStream& rStrm, const OUString& rStr, + bool bAddZero, rtl_TextEncoding eCodeSet); + + static void WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero); + + bool InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec ); + + using StgWriter::Write; + virtual ErrCode Write( SwPaM&, SfxMedium&, const OUString* ) override; + //Seems not an expected to provide method to access the private member + SfxMedium* GetMedia() { return mpMedium; } + +private: + SwWW8Writer(const SwWW8Writer&) = delete; + SwWW8Writer& operator=(const SwWW8Writer&) = delete; + ErrCode WriteStorageImpl(); +}; + +/// Exporter of the binary Word file formats. +class WW8Export : public MSWordExportBase +{ +public: + std::unique_ptr<ww::bytes> pO; ///< Buffer + + SvStream *pTableStrm, *pDataStrm; ///< Streams for WW97 Export + + std::unique_ptr<WW8Fib> pFib; ///< File Information Block + std::unique_ptr<WW8Dop> pDop; ///< Document Properties + std::unique_ptr<WW8_WrPlcFootnoteEdn> pFootnote; ///< Footnotes - structure to remember them, and output + std::unique_ptr<WW8_WrPlcFootnoteEdn> pEdn; ///< Endnotes - structure to remember them, and output + std::unique_ptr<WW8_WrPlcSepx> pSepx; ///< Sections/headers/footers + + bool m_bDot; ///< Template or document. + +protected: + SwWW8Writer *m_pWriter; ///< Pointer to the writer + std::unique_ptr<WW8AttributeOutput> m_pAttrOutput; ///< Converting attributes to stream data + +private: + tools::SvRef<SotStorage> xEscherStg; /// memory leak #i120098#, to hold the reference to unnamed SotStorage obj + +public: + /// Access to the attribute output class. + virtual AttributeOutputBase& AttrOutput() const override; + + /// Access to the sections/headers/footres. + virtual MSWordSections& Sections() const override; + + virtual bool PreferPageBreakBefore() const override { return true; } + + virtual bool AllowPostponedTextInTable() const override { return false; } + + virtual bool FieldsQuoted() const override { return false; } + + virtual bool AddSectionBreaksForTOX() const override { return false; } +private: + /// Format-dependent part of the actual export. + virtual ErrCode ExportDocument_Impl() override; + + void PrepareStorage(); + void WriteFkpPlcUsw(); + void WriteMainText(); + void StoreDoc1(); + + /// Output the numbering table. + virtual void WriteNumbering() override; + + void OutOverrideListTab(); + void OutListNamesTab(); + + void RestoreMacroCmds(); + + void DoComboBox(css::uno::Reference<css::beans::XPropertySet> const & xPropSet); + +public: + + /// Setup the pA's info. + virtual void SetupSectionPositions( WW8_PdAttrDesc* pA ) override; + + bool MiserableFormFieldExportHack(const SwFrameFormat& rFrameFormat); + + SwMSConvertControls& GetOCXExp() { return *m_pOCXExp; } + void ExportDopTypography(WW8DopTypography &rTypo); + + sal_uInt16 AddRedlineAuthor( std::size_t nId ); + + void WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pO = nullptr ); + void WritePostItBegin( ww::bytes* pO = nullptr ); + const SvxBrushItem* GetCurrentPageBgBrush() const; + std::shared_ptr<SvxBrushItem> TrueFrameBgBrush(const SwFrameFormat &rFlyFormat) const; + + void AppendFlyInFlys(const ww8::Frame& rFrameFormat, const Point& rNdTopLeft); + void WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp); + void WriteSdrTextObj(const SdrTextObj& rObj, sal_uInt8 nTyp); + + sal_uInt32 GetSdrOrdNum( const SwFrameFormat& rFormat ) const; + void CreateEscher(); + void WriteEscher(); + + /// Write the field + virtual void OutputField( const SwField* pField, ww::eField eFieldType, + const OUString& rFieldCmd, FieldFlags nMode = FieldFlags::All ) override; + + void StartCommentOutput( const OUString& rName ); + void EndCommentOutput( const OUString& rName ); + void OutGrf(const ww8::Frame &rFrame); + bool TestOleNeedsGraphic(const SwAttrSet& rSet, tools::SvRef<SotStorage> const& xOleStg, + const tools::SvRef<SotStorage>& xObjStg, OUString const& rStorageName, + SwOLENode* pOLENd); + + virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + virtual void AppendBookmark( const OUString& rName ) override; + void AppendBookmarkEndWithCorrection( const OUString& rName ); + + virtual void AppendAnnotationMarks( const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + + virtual void AppendSmartTags(SwTextNode& rTextNode) override; + + virtual void ExportGrfBullet(const SwTextNode& rNd) override; + void OutGrfBullets(const ww8::Frame &rFrame); + + void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo); + + void WriteAsStringTable(const std::vector<OUString>&, sal_Int32& rfcSttbf, + sal_Int32& rlcbSttbf); + + virtual sal_uLong ReplaceCr( sal_uInt8 nChar ) override; + + virtual void WriteCR( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner = ww8::WW8TableNodeInfoInner::Pointer_t() ) override; + void WriteChar( sal_Unicode c ) override; + + void OutSwString(const OUString&, sal_Int32 nStt, sal_Int32 nLen); + + WW8_CP Fc2Cp( sal_uLong nFc ) const { return m_pPiece->Fc2Cp( nFc ); } + + // some partly static semi-internal function declarations + + void OutSprmBytes( sal_uInt8* pBytes, sal_uInt16 nSiz ) + { pO->insert( pO->end(), pBytes, pBytes+nSiz ); } + + virtual void SectionBreaksAndFrames( const SwTextNode& rNode ) override; + + /// Helper method for OutputSectionBreaks() and OutputFollowPageDesc(). + // #i76300# + virtual void PrepareNewPageDesc( const SfxItemSet* pSet, + const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc ) override; + + static void Out_BorderLine(ww::bytes& rO, const ::editeng::SvxBorderLine* pLine, + sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, + bool bShadow); + + void Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow); + static void Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * rBox ); + void Out_CellRangeBorders(const SvxBoxItem * pBox, sal_uInt8 nStart, + sal_uInt8 nLimit); + static bool TransBrush(const Color& rCol, WW8_SHD& rShd); + static WW8_BRCVer9 TranslateBorderLine(const ::editeng::SvxBorderLine& pLine, + sal_uInt16 nDist, bool bShadow); + + // #i77805# - new return value indicates, if an inherited outline numbering is suppressed + virtual bool DisallowInheritingOutlineNumbering(const SwFormat &rFormat) override; + + unsigned int GetHdFtIndex() const { return m_nHdFtIndex; } + void SetHdFtIndex(unsigned int nHdFtIndex) { m_nHdFtIndex = nHdFtIndex; } + void IncrementHdFtIndex() { ++m_nHdFtIndex; } + + /** + * Converts the SVX numbering type to MSONFC. + * + * This is used for section, footnote and endnote numbering purposes. + */ + static sal_uInt8 GetNumId( sal_uInt16 eNumType ); + + /// Guess the script (asian/western). + virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) override; + + SwTwips CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const; + + /// Nasty swap for bidi if necessary + void MiserableRTLFrameFormatHack(SwTwips &rLeft, SwTwips &rRight, + const ww8::Frame &rFrameFormat); + + void InsUInt16( sal_uInt16 n ) { SwWW8Writer::InsUInt16( *pO, n ); } + void InsInt16(sal_Int16 n) { InsUInt16(sal_uInt16(n)); } + void InsUInt32( sal_uInt32 n ) { SwWW8Writer::InsUInt32( *pO, n ); } + void WriteStringAsPara( const OUString& rText ); + + /// Setup the exporter. + WW8Export( SwWW8Writer *pWriter, + SwDoc *pDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM *pOriginalPam, + bool bDot ); + virtual ~WW8Export() override; + + virtual void DoComboBox(const OUString &rName, + const OUString &rHelp, + const OUString &ToolTip, + const OUString &rSelected, + const css::uno::Sequence<OUString> &rListItems) override; + + virtual void DoFormText(const SwInputField * pField) override; + + void GetCurrentItems(ww::bytes &rItems) const; + + /// Write the data of the form field + virtual void WriteFormData( const ::sw::mark::IFieldmark& rFieldmark ) override; + virtual void WriteHyperlinkData( const ::sw::mark::IFieldmark& rFieldmark ) override; + + /// Fields. + WW8_WrPlcField* CurrentFieldPlc() const; + + SwWW8Writer& GetWriter() const { return *m_pWriter; } + SvStream& Strm() const { return m_pWriter->Strm(); } + + /// Remember some of the members so that we can recurse in WriteText(). + virtual void SaveData( sal_uLong nStt, sal_uLong nEnd ) override; + + /// Restore what was saved in SaveData(). + virtual void RestoreData() override; + + /// Output the actual headers and footers. + virtual void WriteHeadersFooters( sal_uInt8 nHeadFootFlags, + const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, + sal_uInt8 nBreakCode) override; + + virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOC; } + +protected: + /// Output SwGrfNode + virtual void OutputGrfNode( const SwGrfNode& ) override; + + /// Output SwOLENode + virtual void OutputOLENode( const SwOLENode& ) override; + + virtual void OutputLinkedOLE( const OUString& ) override; + + virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) override; + +private: + WW8Export(const WW8Export&) = delete; + WW8Export& operator=(const WW8Export&) = delete; +}; + +class WW8_WrPlcSubDoc // double Plc for Footnotes/Endnotes and Postits +{ +private: + WW8_WrPlcSubDoc(const WW8_WrPlcSubDoc&) = delete; + WW8_WrPlcSubDoc& operator=(const WW8_WrPlcSubDoc&) = delete; +protected: + std::vector<WW8_CP> aCps; + std::vector<const void*> aContent; // PTRARR of SwFormatFootnote/PostIts/.. + std::vector<const SwFrameFormat*> aSpareFormats; // a backup for aContent: if there's no SdrObject, stores the fmt directly here + std::unique_ptr<WW8_WrPlc0> pTextPos; // positions of the individual texts + + WW8_WrPlcSubDoc(); + virtual ~WW8_WrPlcSubDoc(); + + bool WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp, WW8_CP& rCount ); + void WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp, WW8_FC& rTextStt, + sal_Int32& rTextCnt, WW8_FC& rRefStt, sal_Int32& rRefCnt ) const; + + virtual const std::vector<sal_uInt32>* GetShapeIdArr() const; +}; + +// double Plc for Footnotes/Endnotes +class WW8_WrPlcFootnoteEdn : public WW8_WrPlcSubDoc +{ +private: + sal_uInt8 nTyp; + + WW8_WrPlcFootnoteEdn(const WW8_WrPlcFootnoteEdn&) = delete; + WW8_WrPlcFootnoteEdn& operator=(WW8_WrPlcFootnoteEdn const &) = delete; +public: + explicit WW8_WrPlcFootnoteEdn( sal_uInt8 nTTyp ) : nTyp( nTTyp ) {} + + bool WriteText( WW8Export& rWrt ); + void WritePlc( WW8Export& rWrt ) const; + + void Append( WW8_CP nCp, const SwFormatFootnote& rFootnote ); +}; + +struct WW8_Annotation +{ + const OutlinerParaObject* mpRichText; + OUString msSimpleText; + OUString msOwner; + OUString m_sInitials; + DateTime maDateTime; + WW8_CP m_nRangeStart, m_nRangeEnd; + bool m_bIgnoreEmpty = true; + WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd); + explicit WW8_Annotation(const SwRedlineData* pRedline); + /// An annotation has a range if start != end or the m_bIgnoreEmpty flag is cleared. + bool HasRange() const; +}; + +class WW8_WrPlcAnnotations : public WW8_WrPlcSubDoc // double Plc for Postits +{ +private: + WW8_WrPlcAnnotations(const WW8_WrPlcAnnotations&) = delete; + WW8_WrPlcAnnotations& operator=(WW8_WrPlcAnnotations const &) = delete; + o3tl::sorted_vector<const SwRedlineData*> maProcessedRedlines; + + std::map<const OUString, std::pair<WW8_CP, bool>> m_aRangeStartPositions; +public: + WW8_WrPlcAnnotations() {} + virtual ~WW8_WrPlcAnnotations() override; + + void AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp, bool bIgnoreEmpty); + void Append( WW8_CP nCp, const SwPostItField* pPostIt ); + void Append( WW8_CP nCp, const SwRedlineData* pRedLine ); + bool IsNewRedlineComment( const SwRedlineData* pRedLine ); + bool WriteText( WW8Export& rWrt ); + void WritePlc( WW8Export& rWrt ) const; +}; + +class WW8_WrPlcTextBoxes : public WW8_WrPlcSubDoc // double Plc for Textboxes +{ // Frame/DrawTextboxes! +private: + sal_uInt8 nTyp; + std::vector<sal_uInt32> aShapeIds; // VARARR of ShapeIds for the SwFrameFormats + virtual const std::vector<sal_uInt32>* GetShapeIdArr() const override; + + WW8_WrPlcTextBoxes(const WW8_WrPlcTextBoxes&) = delete; + WW8_WrPlcTextBoxes& operator=(WW8_WrPlcTextBoxes const &) = delete; +public: + explicit WW8_WrPlcTextBoxes( sal_uInt8 nTTyp ) : nTyp( nTTyp ) {} + + bool WriteText( WW8Export& rWrt ); + void WritePlc( WW8Export& rWrt ) const; + void Append( const SdrObject& rObj, sal_uInt32 nShapeId ); + void Append( const SwFrameFormat* pFormat, sal_uInt32 nShapeId ); + sal_uInt16 Count() const { return aContent.size(); } + sal_uInt16 GetPos( const void* p ) const + { + std::vector<const void*>::const_iterator it + = std::find( aContent.begin(), aContent.end(), p ); + return it == aContent.end() ? USHRT_MAX : it - aContent.begin(); + } +}; + +class WW8_WrPlcPn // Plc for Page Numbers +{ +private: + WW8Export& rWrt; + // Plc for Chpx and Papx ( incl PN-Plc ) + std::vector<std::unique_ptr<WW8_WrFkp>> m_Fkps; + sal_uInt16 nFkpStartPage; + ePLCFT ePlc; + + WW8_WrPlcPn(const WW8_WrPlcPn&) = delete; + WW8_WrPlcPn& operator=(const WW8_WrPlcPn&) = delete; +public: + WW8_WrPlcPn( WW8Export& rWrt, ePLCFT ePl, WW8_FC nStartFc ); + ~WW8_WrPlcPn(); + void AppendFkpEntry(WW8_FC nEndFc,short nVarLen = 0,const sal_uInt8* pSprms = nullptr); + void WriteFkps(); + void WritePlc(); + sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen); +}; + +// class WW8_WrPlc1 is only used for fields +class WW8_WrPlc1 +{ +private: + std::vector<WW8_CP> aPos; + std::unique_ptr<sal_uInt8[]> pData; // content ( structures ) + sal_uLong nDataLen; + sal_uInt16 nStructSiz; + + WW8_WrPlc1(const WW8_WrPlc1&) = delete; + WW8_WrPlc1& operator=(const WW8_WrPlc1&) = delete; +protected: + sal_uInt16 Count() const { return aPos.size(); } + void Write( SvStream& rStrm ); + WW8_CP Prev() const; +public: + explicit WW8_WrPlc1( sal_uInt16 nStructSz ); + ~WW8_WrPlc1(); + void Append( WW8_CP nCp, const void* pData ); + void Finish( sal_uLong nLastCp, sal_uLong nStartCp ); +}; + +// class WW8_WrPlcField is for fields +class WW8_WrPlcField : public WW8_WrPlc1 +{ +private: + sal_uInt8 nTextTyp; + sal_uInt16 nResults; + + WW8_WrPlcField(const WW8_WrPlcField&) = delete; + WW8_WrPlcField& operator=(const WW8_WrPlcField&) = delete; +public: + WW8_WrPlcField( sal_uInt16 nStructSz, sal_uInt8 nTTyp ) + : WW8_WrPlc1( nStructSz ), nTextTyp( nTTyp ), nResults(0) + {} + void Write( WW8Export& rWrt ); + void ResultAdded() { ++nResults; } + sal_uInt16 ResultCount() const { return nResults; } +}; + +class WW8_WrMagicTable : public WW8_WrPlc1 +{ +private: + WW8_WrMagicTable(const WW8_WrMagicTable&) = delete; + WW8_WrMagicTable& operator=(const WW8_WrMagicTable&) = delete; +public: + WW8_WrMagicTable() : WW8_WrPlc1( 4 ) {Append(0,0);} + void Append( WW8_CP nCp, sal_uLong nData ); + void Write( WW8Export& rWrt ); +}; + +class GraphicDetails +{ +public: + ww8::Frame maFly; // surrounding FlyFrames + sal_uLong mnPos; // FilePos of the graphics + sal_uInt16 mnWid; // Width of the graphics + sal_uInt16 mnHei; // Height of the graphics + + GraphicDetails(const ww8::Frame &rFly, sal_uInt16 nWid, sal_uInt16 nHei) + : maFly(rFly), mnPos(0), mnWid(nWid), mnHei(nHei) + {} + + bool operator==(const GraphicDetails& rIn) const + { + return ( + (mnWid == rIn.mnWid) && (mnHei == rIn.mnHei) && + (maFly.RefersToSameFrameAs(rIn.maFly)) + ); + } +}; + +// class SwWW8WrGrf collects graphics and issues them +class SwWW8WrGrf +{ +private: + /// for access to the variables + WW8Export& rWrt; + + std::vector<GraphicDetails> maDetails; + sal_uInt16 mnIdx; // index in file positions + + static void WritePICFHeader(SvStream& rStrm, const ww8::Frame &rFly, + sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight, + const SwAttrSet* pAttrSet = nullptr); + void WriteGraphicNode(SvStream& rStrm, const GraphicDetails &rItem); + void WriteGrfFromGrfNode(SvStream& rStrm, const SwGrfNode &rNd, + const ww8::Frame &rFly, sal_uInt16 nWidth, sal_uInt16 nHeight); + + static void WritePICBulletFHeader(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight); + void WriteGrfForBullet(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 nWidth, sal_uInt16 nHeight); + + SwWW8WrGrf(const SwWW8WrGrf&) = delete; + SwWW8WrGrf& operator=(const SwWW8WrGrf&) = delete; +public: + explicit SwWW8WrGrf( WW8Export& rW ) : rWrt( rW ), mnIdx( 0 ) {} + void Insert(const ww8::Frame &rFly); + void Write(); + sal_uLong GetFPos() + { return (mnIdx < maDetails.size()) ? maDetails[mnIdx++].mnPos : 0; } +}; + +/** The class MSWordAttrIter is a helper class to build the Fkp.chpx. + This is a base class to output the SwTextAttrs and the EditEngineTextAttrs. +*/ +class MSWordAttrIter +{ +private: + MSWordAttrIter* pOld; + MSWordAttrIter(const MSWordAttrIter&) = delete; + MSWordAttrIter& operator=(const MSWordAttrIter&) = delete; +protected: + MSWordExportBase& m_rExport; +public: + explicit MSWordAttrIter( MSWordExportBase& rExport ); + virtual ~MSWordAttrIter(); + + virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const = 0; + virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const = 0; + }; + +/// Used to export formatted text associated to drawings. +class MSWord_SdrAttrIter : public MSWordAttrIter +{ +private: + const EditTextObject* pEditObj; + const SfxItemPool* pEditPool; + std::vector<EECharAttrib> aTextAtrArr; + std::vector<const EECharAttrib*> aChrTextAtrArr; + std::vector<rtl_TextEncoding> aChrSetArr; + sal_Int32 nPara; + sal_Int32 nCurrentSwPos; + sal_Int32 nTmpSwPos; // for HasItem() + rtl_TextEncoding eNdChrSet; + sal_uInt16 nScript; + sal_uInt8 mnTyp; + + sal_Int32 SearchNext( sal_Int32 nStartPos ); + void SetCharSet(const EECharAttrib& rTextAttr, bool bStart); + + void SetItemsThatDifferFromStandard(bool bCharAttr, SfxItemSet& rSet); + + MSWord_SdrAttrIter(const MSWord_SdrAttrIter&) = delete; + MSWord_SdrAttrIter& operator=(const MSWord_SdrAttrIter&) = delete; +public: + MSWord_SdrAttrIter( MSWordExportBase& rWr, const EditTextObject& rEditObj, + sal_uInt8 nType ); + void NextPara( sal_Int32 nPar ); + void OutParaAttr(bool bCharAttr, const std::set<sal_uInt16>* pWhichsToIgnore = nullptr); + void OutEEField(const SfxPoolItem& rHt); + + bool IsTextAttr(sal_Int32 nSwPos); + + void NextPos() { if ( nCurrentSwPos < SAL_MAX_INT32 ) nCurrentSwPos = SearchNext( nCurrentSwPos + 1 ); } + + void OutAttr( sal_Int32 nSwPos ); + virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const override; + virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const override; + sal_Int32 WhereNext() const { return nCurrentSwPos; } + rtl_TextEncoding GetNextCharSet() const; + rtl_TextEncoding GetNodeCharSet() const { return eNdChrSet; } +}; + +// class SwWW8AttrIter is a helper for constructing the Fkp.chpx. +// Only character attributes are considered; paragraph attributes do not need this treatment. +// The paragraph and text attributes of the Writer are passed, and +// Where() returns the next position where the attributes change. +// IsTextAtr() tells if, at the position returned by Where(), there is +// an attribute without end and with \xff in the text. +// Using OutAttr(), the attributes on the passed SwPos are returned. +class SwWW8AttrIter : public MSWordAttrIter +{ +private: + const SwTextNode& rNd; + + sw::util::CharRuns maCharRuns; + sw::util::CharRuns::const_iterator maCharRunIter; + + rtl_TextEncoding meChrSet; + sal_uInt16 mnScript; + bool mbCharIsRTL; + + const SwRangeRedline* pCurRedline; + sal_Int32 nCurrentSwPos; + SwRedlineTable::size_type nCurRedlinePos; + + bool mbParaIsRTL; + + const SwFormatDrop &mrSwFormatDrop; + + ww8::Frames maFlyFrames; // #i2916# + ww8::FrameIter maFlyIter; + + sal_Int32 SearchNext( sal_Int32 nStartPos ); + + void OutSwFormatRefMark(const SwFormatRefMark& rAttr); + + void IterToCurrent(); + + SwWW8AttrIter(const SwWW8AttrIter&) = delete; + SwWW8AttrIter& operator=(const SwWW8AttrIter&) = delete; + + void handleToggleProperty(SfxItemSet& rExportSet, const SwFormatCharFormat* pCharFormatItem, sal_uInt16 nWhich, const SfxPoolItem* pValue); +public: + SwWW8AttrIter( MSWordExportBase& rWr, const SwTextNode& rNd ); + + bool IsTextAttr( sal_Int32 nSwPos ) const; + bool IsExportableAttr(sal_Int32 nSwPos) const; + bool IncludeEndOfParaCRInRedlineProperties(sal_Int32 nPos) const; + bool IsDropCap( int nSwPos ); + bool RequiresImplicitBookmark(); + + void NextPos() { if ( nCurrentSwPos < SAL_MAX_INT32 ) nCurrentSwPos = SearchNext( nCurrentSwPos + 1 ); } + + void OutAttr( sal_Int32 nSwPos, bool bWriteCombinedChars ); + virtual const SfxPoolItem* HasTextItem( sal_uInt16 nWhich ) const override; + virtual const SfxPoolItem& GetItem( sal_uInt16 nWhich ) const override; + int OutAttrWithRange(const SwTextNode& rNode, sal_Int32 nPos); + const SwRedlineData* GetParagraphLevelRedline( ); + const SwRedlineData* GetRunLevelRedline( sal_Int32 nPos ); + FlyProcessingState OutFlys(sal_Int32 nSwPos); + bool HasFlysAt(sal_Int32 nSwPos) const; + + sal_Int32 WhereNext() const { return nCurrentSwPos; } + sal_uInt16 GetScript() const { return mnScript; } + bool IsParaRTL() const { return mbParaIsRTL; } + rtl_TextEncoding GetCharSet() const { return meChrSet; } + OUString GetSnippet(const OUString &rStr, sal_Int32 nCurrentPos, + sal_Int32 nLen) const; + const SwFormatDrop& GetSwFormatDrop() const { return mrSwFormatDrop; } + + bool IsWatermarkFrame(); + bool IsAnchorLinkedToThisNode( sal_uLong nNodePos ); + + void SplitRun( sal_Int32 nSplitEndPos ); + + const SwTextNode& GetNode() const { return rNd; } +}; + +/// Class to collect and output the styles table. +class MSWordStyles +{ + MSWordExportBase& m_rExport; + sal_uInt16 m_aHeadingParagraphStyles[MAXLEVEL]; + std::unique_ptr<SwFormat*[]> m_pFormatA; ///< Slot <-> Character and paragraph style array (0 for list styles). + sal_uInt16 m_nUsedSlots; + bool m_bListStyles; ///< If list styles are requested to be exported as well. + std::map<sal_uInt16, const SwNumRule*> m_aNumRules; ///< Slot <-> List style map. + + /// We need to build style id's for DOCX export; ideally we should roundtrip that, but this is good enough. + std::vector<OString> m_aStyleIds; + + /// Create the style table, called from the constructor. + void BuildStylesTable(); + + /// Based on pFormatA, fill in m_aStyleIds with unique, MS-like names. + void BuildStyleIds(); + + /// Get slot number during building the style table. + sal_uInt16 BuildGetSlot( const SwFormat& rFormat ); + sal_uInt16 BuildGetSlot( const SwNumRule& /*rNumRule*/ ) { return m_nUsedSlots++;} + + /// Return information about one style. + void GetStyleData( SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext ); + + /// Outputs attributes of one style. + void WriteProperties( const SwFormat* pFormat, bool bPap, sal_uInt16 nPos, bool bInsDefCharSiz ); + + static sal_uInt16 GetWWId( const SwFormat& rFormat ); + + void SetStyleDefaults( const SwFormat& rFormat, bool bPap ); + + /// Outputs one style - called (in a loop) from OutputStylesTable(). + void OutputStyle( SwFormat* pFormat, sal_uInt16 nPos ); + void OutputStyle( const SwNumRule* pNumRule, sal_uInt16 nPos ); + + MSWordStyles( const MSWordStyles& ) = delete; + MSWordStyles& operator=( const MSWordStyles& ) = delete; + +public: + MSWordStyles( MSWordExportBase& rExport, bool bListStyles = false ); + ~MSWordStyles(); + + /// Output the styles table. + void OutputStylesTable(); + + /// Get id of the style (rFormat). + sal_uInt16 GetSlot( const SwFormat* pFormat ) const; + + /// create style id using only ASCII characters of the style name + static OString CreateStyleId(const OUString &rName); + + /// Get styleId of the nId-th style (nId is its position in pFormatA). + OString const & GetStyleId(sal_uInt16 nId) const; + + const SwFormat* GetSwFormat(sal_uInt16 nId) const { return m_pFormatA[nId]; } + /// Get numbering rule of the nId-th style + const SwNumRule* GetSwNumRule(sal_uInt16 nId) const; + sal_uInt16 GetHeadingParagraphStyleId(sal_uInt16 nLevel) const { return m_aHeadingParagraphStyles[ nLevel ]; } +}; + +#define MSWORD_MAX_STYLES_LIMIT 4091 + +sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat); +// A bit of a bag on the side for now +OUString FieldString(ww::eField eIndex); +OUString BookmarkToWord(const OUString &rBookmark); + +class WW8SHDLong +{ + sal_uInt32 m_cvFore; + sal_uInt32 m_cvBack; + +public: + WW8SHDLong() : m_cvFore(0), m_cvBack(0) {} + + void Write(WW8Export & rExport); + void setCvFore(sal_uInt32 cvFore) { m_cvFore = cvFore; } + void setCvBack(sal_uInt32 cvBack) { m_cvBack = cvBack; } +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WRTWW8_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/wrtww8gr.cxx b/sw/source/filter/ww8/wrtww8gr.cxx new file mode 100644 index 000000000..4f0de200b --- /dev/null +++ b/sw/source/filter/ww8/wrtww8gr.cxx @@ -0,0 +1,881 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <sal/log.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/gdimtf.hxx> +#include <svl/itemiter.hxx> + +#include <svtools/embedhlp.hxx> + +#include <hintids.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <filter/msfilter/msoleexp.hxx> +#include <editeng/fhgtitem.hxx> +#include <svx/svdoole2.hxx> + +#include <unotools/ucbstreamhelper.hxx> +#include <fmtanchr.hxx> +#include <ndgrf.hxx> +#include <frmfmt.hxx> +#include <grfatr.hxx> +#include <ndole.hxx> +#include <ndtxt.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> + +#include "sprmids.hxx" + +#include <doc.hxx> +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "ww8struc.hxx" +#include "wrtww8.hxx" +#include "ww8par.hxx" +#include "escher.hxx" +//Added for i120568 +#include "ww8attributeoutput.hxx" +#include <fmturl.hxx> + +#include <IDocumentDrawModelAccess.hxx> +#include <drawdoc.hxx> + +using namespace ::com::sun::star; + +// TODO: +// 5. convert the MapModes that Widows can't handle + +// OutGrf () is called for every GrfNode in the document. A PicLocFc-Sprm +// will be inserted, which contains a magic number instead of an address. +// The GrfNode-Ptr is saved in Graf-Class (used later for output of +// the graphic and patching of the PicLocFc attributes) + +void WW8Export::OutputGrfNode( const SwGrfNode& /*rNode*/ ) +{ + SAL_INFO("sw", "WW8Export::OutputGrfNode( const SwGrfNode& )" ); + OSL_ENSURE( m_pParentFrame, "frame not set!" ); + if ( m_pParentFrame ) + { + OutGrf( *m_pParentFrame ); + pFib->m_fHasPic = true; + } +} + +bool WW8Export::TestOleNeedsGraphic(const SwAttrSet& rSet, tools::SvRef<SotStorage> const& xOleStg, + const tools::SvRef<SotStorage>& xObjStg, + OUString const& rStorageName, SwOLENode* pOLENd) +{ + bool bGraphicNeeded = false; + SfxItemIter aIter( rSet ); + for (auto pItem = aIter.GetCurItem(); !bGraphicNeeded && pItem; pItem = aIter.NextItem()) + { + switch (pItem->Which()) + { + /* + For an inline object these properties are irrelevant because they + will be the same as the defaults that msword applies in their + absence, so if that is all that there is for these inline objects + then if there turns out to be enough information in the object + itself to regenerate the correct size and preview of the object + then we will not need to provide an additional graphics preview in + the data stream, which can save a lot of disk space. + */ + case RES_FRM_SIZE: + case RES_CNTNT: + case RES_VERT_ORIENT: + case RES_ANCHOR: + break; + default: + bGraphicNeeded = true; + } + } + + /* + Now we must see if the object contains a preview itself which is equal to + the preview that we are currently using. If the graphics are equal then we + don't need to store another preview + */ + GDIMetaFile aWMF; + long nX=0,nY=0; + if (!bGraphicNeeded && SwWW8ImplReader::ImportOleWMF(xOleStg,aWMF,nX,nY)) + { + // bGraphicNeeded set to true is right / fixes #i51670#. + bGraphicNeeded = true; + tools::Rectangle aRect( Point(), Size( nX, nY ) ); + Graphic aGraph(aWMF); + + ErrCode nErr = ERRCODE_NONE; + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + if ( pOLENd ) + nAspect = pOLENd->GetAspect(); + SdrOle2Obj *pRet = SvxMSDffManager::CreateSdrOLEFromStorage( + *m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(), + rStorageName, + xObjStg, + m_pDoc->GetDocStorage(), + aGraph, + aRect, + tools::Rectangle(), + nullptr, + nErr, + 0, + nAspect, + m_pWriter->GetBaseURL()); + + if (pRet) + { + uno::Reference< embed::XEmbeddedObject > xObj = pOLENd->GetOLEObj().GetOleRef(); + if ( xObj.is() ) + { + std::unique_ptr<SvStream> pGraphicStream; + comphelper::EmbeddedObjectContainer aCnt( m_pDoc->GetDocStorage() ); + try + { + uno::Reference< embed::XEmbedPersist > xPersist( + xObj, + uno::UNO_QUERY_THROW ); + + // it makes no sense to search the object in the container by reference since the object was created + // outside of the container and was not inserted there, only the name makes sense + pGraphicStream = + ::utl::UcbStreamHelper::CreateStream( aCnt.GetGraphicStream( xPersist->getEntryName() ) ); + } + catch( const uno::Exception& ) + {} + + OSL_ENSURE( pGraphicStream && !pGraphicStream->GetError(), "No graphic stream available!" ); + if ( pGraphicStream && !pGraphicStream->GetError() ) + { + Graphic aGr1; + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + if( rGF.ImportGraphic( aGr1, OUString(), *pGraphicStream ) == ERRCODE_NONE ) + { + Graphic aGr2; + pGraphicStream = + ::utl::UcbStreamHelper::CreateStream( aCnt.GetGraphicStream( pRet->GetObjRef() ) ); + if( pGraphicStream && rGF.ImportGraphic( aGr2, OUString(), *pGraphicStream ) == ERRCODE_NONE ) + { + if ( aGr1 == aGr2 ) + bGraphicNeeded = false; + } + } + } + } + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pRet); + SdrObject::Free(pTemp); + } + } + else + bGraphicNeeded = true; + return bGraphicNeeded; +} + +void WW8Export::OutputOLENode( const SwOLENode& rOLENode ) +{ + SAL_INFO("sw", "WW8Export::OutputOLENode( const SwOLENode& rOLENode )" ); + sal_uInt8 *pSpecOLE; + sal_uInt8 *pDataAdr; + short nSize; + static sal_uInt8 aSpecOLE_WW8[] = { + 0x03, 0x6a, 0, 0, 0, 0, // sprmCPicLocation + 0x0a, 0x08, 1, // sprmCFOLE2 + 0x56, 0x08, 1 // sprmCFObj + }; + + pSpecOLE = aSpecOLE_WW8; + nSize = sizeof( aSpecOLE_WW8 ); + pDataAdr = pSpecOLE + 2; //WW6 sprm is 1 but has 1 byte len as well. + + tools::SvRef<SotStorage> xObjStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool); + + if( xObjStg.is() ) + { + uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef()); + if( xObj.is() ) + { + const embed::XEmbeddedObject *pObj = xObj.get(); + //Don't want to use pointer ids, as is traditional, because we need + //to put this into a 32bit value, and on 64bit the bottom bits + //might collide and two unrelated ole objects end up considered the + //same. Don't want to simply start at 0 which is a special value + sal_Int32 nPictureId = SAL_MAX_INT32 - m_aOleMap.size(); + WW8OleMap::value_type entry = std::make_pair(pObj, nPictureId); + std::pair<WW8OleMap::iterator, bool> aRes = m_aOleMap.insert(entry); + bool bIsNotDuplicate = aRes.second; //.second is false when element already existed + nPictureId = aRes.first->second; + Set_UInt32(pDataAdr, nPictureId); + OUString sStorageName = "_" + OUString::number( nPictureId ); + tools::SvRef<SotStorage> xOleStg = xObjStg->OpenSotStorage( sStorageName ); + if( xOleStg.is() ) + { + /* + If this object storage has been written already don't + waste time rewriting it + */ + if (bIsNotDuplicate) + { + sal_Int64 nAspect = rOLENode.GetAspect(); + svt::EmbeddedObjectRef aObjRef( xObj, nAspect ); + m_pOLEExp->ExportOLEObject( aObjRef, *xOleStg ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + OUString aObjInfo( "\3ObjInfo" ); + if ( !xOleStg->IsStream( aObjInfo ) ) + { + const sal_uInt8 pObjInfoData[] = { 0x40, 0x00, 0x03, 0x00 }; + tools::SvRef<SotStorageStream> rObjInfoStream = xOleStg->OpenSotStream( aObjInfo ); + if ( rObjInfoStream.is() && !rObjInfoStream->GetError() ) + { + rObjInfoStream->WriteBytes(pObjInfoData, sizeof(pObjInfoData)); + xOleStg->Commit(); + } + } + } + } + + // write as embedded field - the other things will be done + // in the escher export + OUString sServer = FieldString(ww::eEMBED) + xOleStg->GetUserName() + " "; + + OutputField(nullptr, ww::eEMBED, sServer, FieldFlags::Start | + FieldFlags::CmdStart | FieldFlags::CmdEnd); + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), + nSize, pSpecOLE ); + + bool bEndCR = true; + /* + In the word filter we only need a preview image for + floating images, and then only (the usual case) if the + object doesn't contain enough information to reconstruct + what we need. + + We don't need a graphic for inline objects, so we don't + even need the overhead of a graphic in that case. + */ + bool bGraphicNeeded = false; + + if (m_pParentFrame) + { + bGraphicNeeded = true; + + if (m_pParentFrame->IsInline()) + { + const SwAttrSet& rSet = + m_pParentFrame->GetFrameFormat().GetAttrSet(); + bEndCR = false; + bGraphicNeeded = TestOleNeedsGraphic(rSet, + xOleStg, xObjStg, sStorageName, const_cast<SwOLENode*>(&rOLENode)); + } + } + + if (!bGraphicNeeded) + WriteChar(0x1); + else + { + /* + ##897## + We need to insert the graphic representation of + this object for the inline case, otherwise word + has no place to find the dimensions of the ole + object, and will not be able to draw it + */ + OutGrf(*m_pParentFrame); + } + + OutputField(nullptr, ww::eEMBED, OUString(), + FieldFlags::End | FieldFlags::Close); + + if (bEndCR) //No newline in inline case + WriteCR(); + } + } + } +} + +void WW8Export::OutputLinkedOLE( const OUString& rOleId ) +{ + uno::Reference< embed::XStorage > xDocStg = m_pDoc->GetDocStorage(); + uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement( "OLELinks", embed::ElementModes::READ ); + tools::SvRef<SotStorage> xObjSrc = SotStorage::OpenOLEStorage( xOleStg, rOleId, StreamMode::READ ); + + tools::SvRef<SotStorage> xObjStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool); + + if( xObjStg.is() && xObjSrc.is() ) + { + tools::SvRef<SotStorage> xOleDst = xObjStg->OpenSotStorage( rOleId ); + if ( xOleDst.is() ) + xObjSrc->CopyTo( xOleDst.get() ); + + if ( !xOleDst->GetError( ) ) + { + xOleDst->Commit(); + + // Output the cPicLocation attribute + std::unique_ptr<ww::bytes> pBuf( new ww::bytes ); + SwWW8Writer::InsUInt16( *pBuf, NS_sprm::sprmCPicLocation ); + SwWW8Writer::InsUInt32( *pBuf, rOleId.copy( 1 ).toInt32() ); + + SwWW8Writer::InsUInt16( *pBuf, NS_sprm::sprmCFOle2 ); + pBuf->push_back( 1 ); + + SwWW8Writer::InsUInt16( *pBuf, NS_sprm::sprmCFSpec ); + pBuf->push_back( 1 ); + + SwWW8Writer::InsUInt16( *pBuf, NS_sprm::sprmCFObj ); + pBuf->push_back( 1 ); + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), pBuf->size(), pBuf->data() ); + } + } +} + +void WW8Export::OutGrf(const ww8::Frame &rFrame) +{ + //Added for i120568,the hyperlink info within a graphic whose anchor type is "As character" + //will be exported to ensure the fidelity + const SwFormatURL& rURL = rFrame.GetFrameFormat().GetAttrSet().GetURL(); + bool bURLStarted = false; + if( !rURL.GetURL().isEmpty() && rFrame.GetWriterType() == ww8::Frame::eGraphic) + { + bURLStarted = true; + m_pAttrOutput->StartURL( rURL.GetURL(), rURL.GetTargetFrameName() ); + } + + // Store the graphic settings in GrfNode so they may be written-out later + m_pGrf->Insert(rFrame); + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), pO->size(), pO->data() ); + pO->clear(); + + // #i29408# + // linked, as-character anchored graphics have to be exported as fields. + const SwGrfNode* pGrfNd = rFrame.IsInline() && rFrame.GetContent() + ? rFrame.GetContent()->GetGrfNode() : nullptr; + if ( pGrfNd && pGrfNd->IsLinkedFile() ) + { + OUString sStr; + pGrfNd->GetFileFilterNms(&sStr, nullptr); + sStr = FieldString(ww::eINCLUDEPICTURE) + " \"" + sStr + "\" \\d"; + + OutputField( nullptr, ww::eINCLUDEPICTURE, sStr, + FieldFlags::Start | FieldFlags::CmdStart | FieldFlags::CmdEnd ); + } + + WriteChar( char(1) ); // paste graphic symbols in the main text + + sal_uInt8 aArr[ 18 ]; + sal_uInt8* pArr = aArr; + + const SwFrameFormat &rFlyFormat = rFrame.GetFrameFormat(); + const RndStdIds eAn = rFlyFormat.GetAttrSet().GetAnchor(false).GetAnchorId(); + if (eAn == RndStdIds::FLY_AS_CHAR) + { + sal_Int16 eVert = rFlyFormat.GetVertOrient().GetVertOrient(); + if ((eVert == text::VertOrientation::CHAR_CENTER) || (eVert == text::VertOrientation::LINE_CENTER)) + { + bool bVert = false; + //The default for word in vertical text mode is to center, + //otherwise a sub/super script hack is employed + if (dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) ) + { + const SwTextNode* pTextNd = static_cast<const SwTextNode*>(m_pOutFormatNode); + SwPosition aPos(*pTextNd); + bVert = m_pDoc->IsInVerticalText(aPos); + } + if (!bVert) + { + SwTwips nHeight = rFlyFormat.GetFrameSize().GetHeight(); + nHeight/=20; //nHeight was in twips, want it in half points, but + //then half of total height. + long nFontHeight = GetItem(RES_CHRATR_FONTSIZE).GetHeight(); + nHeight-=nFontHeight/20; + + Set_UInt16( pArr, NS_sprm::sprmCHpsPos ); + Set_UInt16( pArr, - static_cast<sal_Int16>(nHeight)); + } + } + } + + // sprmCFSpec + Set_UInt16( pArr, 0x855 ); + Set_UInt8( pArr, 1 ); + + // sprmCPicLocation + Set_UInt16( pArr, NS_sprm::sprmCPicLocation ); + Set_UInt32( pArr, GRF_MAGIC_321 ); + + // vary Magic, so that different graphic attributes will not be merged + static sal_uInt8 nAttrMagicIdx = 0; + --pArr; + Set_UInt8( pArr, nAttrMagicIdx++ ); + m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr ); + + // #i75464# + // Check, if graphic isn't exported as-character anchored. + // Otherwise, an additional paragraph is exported for a graphic, which is + // forced to be treated as inline, because it's anchored inside another frame. + if ( !rFrame.IsInline() && + ( (eAn == RndStdIds::FLY_AT_PARA) || + (eAn == RndStdIds::FLY_AT_PAGE) ) ) + { + WriteChar( char(0x0d) ); // close the surrounding frame with CR + + static sal_uInt8 nSty[2] = { 0, 0 }; + pO->insert( pO->end(), nSty, nSty+2 ); // Style #0 + bool bOldGrf = m_bOutGrf; + m_bOutGrf = true; + + OutputFormat( rFrame.GetFrameFormat(), false, false, true ); // Fly-Attrs + + m_bOutGrf = bOldGrf; + m_pPapPlc->AppendFkpEntry( Strm().Tell(), pO->size(), pO->data() ); + pO->clear(); + } + // #i29408# + // linked, as-character anchored graphics have to be exported as fields. + else if ( pGrfNd && pGrfNd->IsLinkedFile() ) + { + OutputField( nullptr, ww::eINCLUDEPICTURE, OUString(), FieldFlags::Close ); + } + //Added for i120568,the hyperlink info within a graphic whose anchor type is + //"As character" will be exported to ensure the fidelity + if( bURLStarted ) + m_pAttrOutput->EndURL(false); +} + +void SwWW8WrGrf::Insert(const ww8::Frame &rFly) +{ + const Size aSize( rFly.GetLayoutSize() ); + const sal_uInt16 nWidth = static_cast< sal_uInt16 >(aSize.Width()); + const sal_uInt16 nHeight = static_cast< sal_uInt16 >(aSize.Height()); + maDetails.emplace_back(rFly, nWidth, nHeight); +} + +void SwWW8WrGrf::WritePICFHeader(SvStream& rStrm, const ww8::Frame &rFly, + sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight, const SwAttrSet* pAttrSet) +{ + sal_Int16 nXSizeAdd = 0, nYSizeAdd = 0; + sal_Int16 nCropL = 0, nCropR = 0, nCropT = 0, nCropB = 0; + + // write Crop-Attribute content in Header ( if available ) + const SfxPoolItem* pItem; + if (pAttrSet && (SfxItemState::SET + == pAttrSet->GetItemState(RES_GRFATR_CROPGRF, false, &pItem))) + { + const SwCropGrf& rCr = *static_cast<const SwCropGrf*>(pItem); + nCropL = static_cast<sal_Int16>(rCr.GetLeft()); + nCropR = static_cast<sal_Int16>(rCr.GetRight()); + nCropT = static_cast<sal_Int16>(rCr.GetTop()); + nCropB = static_cast<sal_Int16>(rCr.GetBottom()); + nXSizeAdd = nXSizeAdd - static_cast<sal_Int16>( rCr.GetLeft() + rCr.GetRight() ); + nYSizeAdd = nYSizeAdd - static_cast<sal_Int16>( rCr.GetTop() + rCr.GetBottom() ); + } + + Size aGrTwipSz(rFly.GetSize()); + sal_uInt16 nHdrLen = 0x44; + + sal_uInt8 aArr[ 0x44 ] = { 0 }; + + sal_uInt8* pArr = aArr + 0x2E; // Do borders first + + const SwAttrSet& rAttrSet = rFly.GetFrameFormat().GetAttrSet(); + if (SfxItemState::SET == rAttrSet.GetItemState(RES_BOX, false, &pItem)) + { + const SvxBoxItem* pBox = static_cast<const SvxBoxItem*>(pItem); + if( pBox ) + { + bool bShadow = false; // Shadow ? + if (const SvxShadowItem* pSI = rAttrSet.GetItem<SvxShadowItem>(RES_SHADOW)) + { + bShadow = (pSI->GetLocation() != SvxShadowLocation::NONE) && + (pSI->GetWidth() != 0); + } + + static const SvxBoxItemLine aLnArr[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT }; + for(const SvxBoxItemLine & i : aLnArr) + { + const ::editeng::SvxBorderLine* pLn = pBox->GetLine( i ); + WW8_BRC aBrc; + if (pLn) + { + WW8_BRCVer9 aBrc90 = WW8Export::TranslateBorderLine( *pLn, + pBox->GetDistance( i ), bShadow ); + sal_uInt8 ico = msfilter::util::TransColToIco(msfilter::util::BGRToRGB( + aBrc90.cv())); + aBrc = WW8_BRC(aBrc90.dptLineWidth(), aBrc90.brcType(), ico, + aBrc90.dptSpace(), aBrc90.fShadow(), aBrc90.fFrame()); + } + + // use importer logic to determine how large the exported + // border will really be in word and adjust accordingly + short nSpacing; + short nThick = aBrc.DetermineBorderProperties(&nSpacing); + switch (i) + { + case SvxBoxItemLine::TOP: + case SvxBoxItemLine::BOTTOM: + nHeight -= bShadow ? nThick*2 : nThick; + nHeight = nHeight - nSpacing; + break; + case SvxBoxItemLine::LEFT: + case SvxBoxItemLine::RIGHT: + default: + nWidth -= bShadow ? nThick*2 : nThick; + nWidth = nWidth - nSpacing; + break; + } + memcpy( pArr, &aBrc.aBits1, 2); + pArr+=2; + + memcpy( pArr, &aBrc.aBits2, 2); + pArr+=2; + } + } + } + + pArr = aArr + 4; // skip lcb + Set_UInt16( pArr, nHdrLen ); // set cbHeader + + Set_UInt16( pArr, mm ); // set mm + + /* + Just in case our original size is too big to fit inside a ushort we can + substitute the final size and lose on retaining the scaling factor but + still keep the correct display size anyway. + */ + const bool bIsSubstitutedSize = (aGrTwipSz.Width() > SHRT_MAX) || (aGrTwipSz.Height() > SHRT_MAX) || + aGrTwipSz.IsEmpty(); + if ( bIsSubstitutedSize ) + { + aGrTwipSz.setWidth( nWidth ); + aGrTwipSz.setHeight( nHeight ); + } + using namespace sw::types; + // set xExt & yExt + Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Width()))); + Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Height()))); + pArr += 16; + // skip hMF & rcWinMF + // set dxaGoal & dyaGoal + Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Width())); + Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Height())); + + if ( aGrTwipSz.Width() + nXSizeAdd ) // set mx + { + if ( !bIsSubstitutedSize ) + { + const double fVal = nWidth * 1000.0 / (aGrTwipSz.Width() + nXSizeAdd ); + Set_UInt16( pArr, static_cast<sal_uInt16>(::rtl::math::round(fVal)) ); + } + else + { + Set_UInt16( pArr, 1000 ); + } + } + else + { + pArr += 2; + } + + if ( aGrTwipSz.Height() + nYSizeAdd ) // set my + { + if ( !bIsSubstitutedSize ) + { + const double fVal = nHeight * 1000.0 / (aGrTwipSz.Height() + nYSizeAdd); + Set_UInt16( pArr, static_cast<sal_uInt16>(::rtl::math::round(fVal)) ); + } + else + { + Set_UInt16( pArr, 1000 ); + } + } + else + { + pArr += 2; + } + + if ( !bIsSubstitutedSize ) + { + Set_UInt16( pArr, nCropL ); // set dxaCropLeft + Set_UInt16( pArr, nCropT ); // set dyaCropTop + Set_UInt16( pArr, nCropR ); // set dxaCropRight + Set_UInt16( pArr, nCropB ); // set dyaCropBottom + } + + rStrm.WriteBytes(aArr, nHdrLen); +} + +void SwWW8WrGrf::WriteGrfFromGrfNode(SvStream& rStrm, const SwGrfNode &rGrfNd, + const ww8::Frame &rFly, sal_uInt16 nWidth, sal_uInt16 nHeight) +{ + if (rGrfNd.IsLinkedFile()) // Linked File + { + OUString aFileN; + rGrfNd.GetFileFilterNms( &aFileN, nullptr ); + + sal_uInt16 const mm = 94; // 94 = BMP, GIF + + WritePICFHeader(rStrm, rFly, mm, nWidth, nHeight, + rGrfNd.GetpSwAttrSet()); + rStrm.WriteUChar( aFileN.getLength() ); // write Pascal-String + SwWW8Writer::WriteString8(rStrm, aFileN, false, + RTL_TEXTENCODING_MS_1252); + } + else // Embedded File or DDE or something like that + { + WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight, + rGrfNd.GetpSwAttrSet()); + SwBasicEscherEx aInlineEscher(&rStrm, rWrt); + aInlineEscher.WriteGrfFlyFrame(rFly.GetFrameFormat(), 0x401); + aInlineEscher.WritePictures(); + } +} +//For i120928,export graphic info of bullet +void SwWW8WrGrf::WritePICBulletFHeader(SvStream& rStrm, const Graphic &rGrf, + sal_uInt16 mm, sal_uInt16 nWidth, sal_uInt16 nHeight) +{ + sal_Int16 nXSizeAdd = 0, nYSizeAdd = 0; + + Size aGrTwipSz(rGrf.GetPrefSize()); + sal_uInt16 nHdrLen = 0x44; + + sal_uInt8 aArr[ 0x44 ] = { 0 }; + + sal_uInt8* pArr = aArr + 0x2E; //Do borders first + + static const SvxBoxItemLine aLnArr[4] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, + SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT }; + for(const SvxBoxItemLine & i : aLnArr) + { + WW8_BRC aBrc; + + short nSpacing; + short nThick = aBrc.DetermineBorderProperties(&nSpacing); + switch (i) + { + case SvxBoxItemLine::TOP: + case SvxBoxItemLine::BOTTOM: + nHeight -= nThick; + nHeight = nHeight - nSpacing; + break; + case SvxBoxItemLine::LEFT: + case SvxBoxItemLine::RIGHT: + default: + nWidth -= nThick; + nWidth = nWidth - nSpacing; + break; + } + memcpy( pArr, &aBrc.aBits1, 2); + pArr+=2; + + memcpy(pArr, &aBrc.aBits2, 2); + pArr+=2; + } + + pArr = aArr + 4; //skip lcb + Set_UInt16( pArr, nHdrLen ); // set cbHeader + + Set_UInt16( pArr, mm ); // set mm + + if ( (convertTwipToMm100(aGrTwipSz.Width()) > USHRT_MAX ) || ( convertTwipToMm100(aGrTwipSz.Height()) > USHRT_MAX ) + || aGrTwipSz.IsEmpty() ) + { + aGrTwipSz.setWidth( nWidth ); + aGrTwipSz.setHeight( nHeight ); + } + using namespace sw::types; + // set xExt & yExt + Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Width()))); + Set_UInt16(pArr, msword_cast<sal_uInt16>(convertTwipToMm100(aGrTwipSz.Height()))); + pArr += 16; + // skip hMF & rcWinMF + // set dxaGoal & dyaGoal + Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Width())); + Set_UInt16(pArr, msword_cast<sal_uInt16>(aGrTwipSz.Height())); + + if( aGrTwipSz.Width() + nXSizeAdd ) // set mx + { + double fVal = nWidth * 1000.0 / (aGrTwipSz.Width() + nXSizeAdd); + Set_UInt16( pArr, static_cast<sal_uInt16>(::rtl::math::round(fVal)) ); + } + else + pArr += 2; + + if( aGrTwipSz.Height() + nYSizeAdd ) // set my + { + double fVal = nHeight * 1000.0 / (aGrTwipSz.Height() + nYSizeAdd); + Set_UInt16( pArr, static_cast<sal_uInt16>(::rtl::math::round(fVal)) ); + } + else + pArr += 2; + + Set_UInt16( pArr, 0 ); // set dxaCropLeft + Set_UInt16( pArr, 0 ); // set dyaCropTop + Set_UInt16( pArr, 0 ); // set dxaCropRight + Set_UInt16( pArr, 0 ); // set dyaCropBottom + + rStrm.WriteBytes(aArr, nHdrLen); +} + +void SwWW8WrGrf::WriteGrfForBullet(SvStream& rStrm, const Graphic &rGrf, sal_uInt16 nWidth, sal_uInt16 nHeight) +{ + WritePICBulletFHeader(rStrm,rGrf, 0x64,nWidth,nHeight); + SwBasicEscherEx aInlineEscher(&rStrm, rWrt); + aInlineEscher.WriteGrfBullet(rGrf); + aInlineEscher.WritePictures(); +} + +void SwWW8WrGrf::WriteGraphicNode(SvStream& rStrm, const GraphicDetails &rItem) +{ + sal_uInt16 nWidth = rItem.mnWid; + sal_uInt16 nHeight = rItem.mnHei; + sal_uInt32 nPos = rStrm.Tell(); // store start of graphic + + const ww8::Frame &rFly = rItem.maFly; + switch (rFly.GetWriterType()) + { + case ww8::Frame::eGraphic: + { + const SwNode *pNode = rItem.maFly.GetContent(); + const SwGrfNode *pNd = pNode ? pNode->GetGrfNode() : nullptr; + OSL_ENSURE(pNd, "Impossible"); + if (pNd) + WriteGrfFromGrfNode(rStrm, *pNd, rItem.maFly, nWidth, nHeight); + } + break; + //For i120928,add branch to export graphic of bullet + case ww8::Frame::eBulletGrf: + { + if (rItem.maFly.HasGraphic()) + { + const Graphic& rGrf = rItem.maFly.GetGraphic(); + WriteGrfForBullet(rStrm, rGrf, nWidth, nHeight); + } + } + break; + + case ww8::Frame::eOle: + { + const SwNode *pNode = rItem.maFly.GetContent(); + const SwOLENode *pNd = pNode ? pNode->GetOLENode() : nullptr; + OSL_ENSURE(pNd, "Impossible"); + if (pNd) + { +#ifdef OLE_PREVIEW_AS_EMF + //Convert this ole2 preview in ww8+ to an EMF for better unicode + //support (note that at this moment this breaks StarSymbol + //using graphics because I need to embed starsymbol in exported + //documents. + WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight, + pNd->GetpSwAttrSet()); + SwBasicEscherEx aInlineEscher(&rStrm, rWrt); + aInlineEscher.WriteOLEFlyFrame(rFly.GetFrameFormat(), 0x401); + aInlineEscher.WritePictures(); +#else + // cast away const + SwOLENode *pOleNd = const_cast<SwOLENode*>(pNd); + SwOLEObj& rSObj= pOleNd->GetOLEObj(); + + // TODO/LATER: do we need to load object? + Graphic* pGr = SdrOle2Obj::GetGraphicFromObject( pOleNd->GetDoc()->GetDocStorage(), rObj ); + + //TODO/LATER: do we really want to use GDIMetafile?! + GDIMetaFile aMtf; + if ( pGr ) + aMtf = pGr->GetGDIMetaFile(); + + Size aS(aMtf.GetPrefSize()); + aMtf.WindStart(); + aMtf.Play(Application::GetDefaultDevice(), Point(0, 0), + Size(2880, 2880)); + + WritePICFHeader(rStrm, rFly, 8, nWidth, nHeight, + pNd->GetpSwAttrSet()); + WriteWindowMetafileBits(rStrm, aMtf); + delete pGr; +#endif + } + } + break; + case ww8::Frame::eDrawing: + case ww8::Frame::eTextBox: + case ww8::Frame::eFormControl: + /* + #i3958# We only export an empty dummy picture frame here, this is + what word does the escher export should contain an anchored to + character element which is drawn over this dummy and the whole + shebang surrounded with a SHAPE field. This isn't *my* hack :-), + it's what word does. + */ + { + WritePICFHeader(rStrm, rFly, 0x64, nWidth, nHeight); + SwBasicEscherEx aInlineEscher(&rStrm, rWrt); + aInlineEscher.WriteEmptyFlyFrame(rFly.GetFrameFormat(), 0x401); + } + break; + default: + OSL_ENSURE(false, "Some inline export not implemented"); + break; + } + + sal_uInt32 nPos2 = rStrm.Tell(); // store the end + rStrm.Seek( nPos ); + rStrm.WriteUInt32(nPos2 - nPos); // patch graphic length in the header + rStrm.Seek( nPos2 ); // restore Pos +} + +// SwWW8WrGrf::Write() is called after the text. +// It writes out all the graphics and remembers the file locations of the graphics, +// so when writing the attributes of the items it can be patched into PicLocFc-SPRMs. +// The search in the attributes for the Magic sal_uLong and patching +// happens when writing the attributes. Class SwWW8WrGrf provides with +// GetFPos() sequentially the positions +void SwWW8WrGrf::Write() +{ + SvStream& rStrm = *rWrt.pDataStrm; + auto aEnd = maDetails.end(); + for (auto aIter = maDetails.begin(); aIter != aEnd; ++aIter) + { + sal_uInt32 nPos = rStrm.Tell(); // align to 4 Bytes + if( nPos & 0x3 ) + SwWW8Writer::FillCount( rStrm, 4 - ( nPos & 0x3 ) ); + + auto aIter2 = std::find(maDetails.begin(), aIter, *aIter); + if (aIter2 != aIter) + { + aIter->mnPos = aIter2->mnPos; + } + else + { + aIter->mnPos = rStrm.Tell(); + WriteGraphicNode(rStrm, *aIter); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx new file mode 100644 index 000000000..e081b730f --- /dev/null +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -0,0 +1,5628 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 contains methods for the WW8 output + * (nodes, attributes, formats and chars). + */ + + +#include <algorithm> +#include <hintids.hxx> + +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <sal/log.hxx> + +#include <svl/zformat.hxx> +#include <svl/itemiter.hxx> +#include <svl/whiter.hxx> +#include <svl/grabbagitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/autokernitem.hxx> +#include <editeng/pbinitem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/twolinesitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/paravertalignitem.hxx> +#include <editeng/pgrditem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <editeng/paperinf.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflgrit.hxx> +#include <fmtfld.hxx> +#include <fchrfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtpdsc.hxx> +#include <fmtornt.hxx> +#include <fmtanchr.hxx> +#include <fmtclds.hxx> +#include <fmtsrnd.hxx> +#include <fmtftn.hxx> +#include <fmtflcnt.hxx> +#include <frmatr.hxx> +#include <swtable.hxx> +#include <fmtinfmt.hxx> +#include <txtfld.hxx> +#include <txtftn.hxx> +#include <poolfmt.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentListsAccess.hxx> +#include <list.hxx> +#include <docary.hxx> +#include <pam.hxx> +#include <paratr.hxx> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <expfld.hxx> +#include <pagedesc.hxx> +#include <ndtxt.hxx> +#include <swrect.hxx> +#include <redline.hxx> +#include <reffld.hxx> +#include <ftninfo.hxx> +#include <charfmt.hxx> +#include <section.hxx> +#include <fmtline.hxx> +#include <tox.hxx> +#include <fmtftntx.hxx> +#include <breakit.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <unotools/localedatawrapper.hxx> +#include <svx/unobrushitemhelper.hxx> +#include <tgrditem.hxx> +#include <flddropdown.hxx> +#include <chpfld.hxx> +#include <fmthdft.hxx> +#include <authfld.hxx> +#include <dbfld.hxx> + +#include "sprmids.hxx" + +#include <fmtcntnt.hxx> +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "wrtww8.hxx" +#include "ww8par.hxx" +#include "ww8attributeoutput.hxx" +#include "fields.hxx" +#include <i18nlangtag/languagetag.hxx> +#include <unotools/fltrcfg.hxx> + + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace nsSwDocInfoSubType; +using namespace sw::util; +using namespace sw::types; + +bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) +{ + bool bRet = true; + if ( nScript == i18n::ScriptType::ASIAN ) + { + //for asian in ww8, there is only one fontsize + //and one fontstyle (posture/weight) for ww6 + //there is the additional problem that there + //is only one font setting for all three scripts + switch ( nWhich ) + { + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + bRet = false; + break; + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + default: + break; + } + } + else if ( nScript == i18n::ScriptType::COMPLEX ) + { + //Complex is ok in ww8, but for ww6 there is only + //one font, one fontsize, one fontsize (weight/posture) + //and only one language + } + else + { + //for western in ww8, there is only one fontsize + //and one fontstyle (posture/weight) for ww6 + //there is the additional problem that there + //is only one font setting for all three scripts + switch ( nWhich ) + { + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + bRet = false; + break; + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + default: + break; + } + } + return bRet; +} + + +void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars ) +{ + for ( const auto& rItem : rItems ) + { + const SfxPoolItem *pItem = rItem.second; + sal_uInt16 nWhich = pItem->Which(); + if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) ) + { + //In the id definition, RES_TXTATR_INETFMT must precede RES_TXTATR_CHARFMT, so that link style can overwrite char style. + //and in #i24291# it describes "All we want to do is ensure for now is that if a charfmt exist in the character + //properties that it rises to the top and is exported first." + //In bug 119649, it is in such situation, so we need to ignore the link style when doing ms word filter exports and + //add the second judgement for #i24291# definition. + if (nWhich == RES_TXTATR_CHARFMT) + { + const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT); + + if (pINetItem) + { + const SwFormatINetFormat& rINet = static_cast<const SwFormatINetFormat&>(*pINetItem); + + if ( rINet.GetValue().isEmpty() ) + continue; + + const sal_uInt16 nId = rINet.GetINetFormatId(); + const OUString& rStr = rINet.GetINetFormat(); + + if (rStr.isEmpty()) + { + SAL_WARN("sw.ww8", "MSWordExportBase::ExportPoolItemsToCHP(..) - missing unvisited character format at hyperlink attribute" ); + } + + const SwCharFormat* pINetFormat = IsPoolUserFormat( nId ) + ? m_pDoc->FindCharFormatByName( rStr ) + : m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId ); + + const SwCharFormat* pFormat = static_cast<const SwFormatCharFormat&>(*pItem).GetCharFormat(); + ww8::PoolItems aCharItems, aINetItems; + GetPoolItems(pFormat->GetAttrSet(), aCharItems, false); + GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false); + for (const auto& rCharItem : aCharItems) + { + const SfxPoolItem* pCharItem = rCharItem.second; + sal_uInt16 nCharWhich = pCharItem->Which(); + if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich)) + AttrOutput().OutputItem(*pCharItem); + } + continue; + } + } + + // tdf#38778 Fix output of the font in DOC run for fields + if (pFont && + nWhich == RES_TXTATR_FIELD) + { + AttrOutput().OutputItem( *pFont ); + } + + // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's + // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type + // Combined Characters and if so, we half the font size. + if (bWriteCombChars && + nWhich == RES_CHRATR_FONTSIZE) + { + SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem )); + fontHeight.SetHeight( fontHeight.GetHeight() / 2 ); + + AttrOutput().OutputItem( fontHeight ); + } + else if (nWhich == RES_CHRATR_COLOR) + { + const SvxColorItem& rColor = static_cast<const SvxColorItem&>(*pItem); + const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND); + if (rColor.GetValue() == COL_AUTO && pBackgroundItem) + { + const SvxBrushItem& rBrushBackground = static_cast<const SvxBrushItem&>(*pBackgroundItem); + SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR); + AttrOutput().OutputItem(aForeground); + } + else + { + // default + AttrOutput().OutputItem( *pItem ); + } + } + else + { + AttrOutput().OutputItem( *pItem ); + } + } + } +} + +/* + * Output format as follows: + * - output the attributes; without parents! + */ + +void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript, + bool bExportParentItemSet ) +{ + if( bExportParentItemSet || rSet.Count() ) + { + const SfxPoolItem* pItem; + m_pISet = &rSet; // for double attributes + + // If frame dir is set, but not adjust, then force adjust as well + if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) ) + { + // No explicit adjust set ? + if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) ) + { + if ( nullptr != ( pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet ) ) ) + { + // then set the adjust used by the parent format + AttrOutput().OutputItem( *pItem ); + } + } + } + + if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE, bExportParentItemSet, &pItem ) ) + { + AttrOutput().OutputItem( *pItem ); + + // switch off the numbering? + if ( static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() && + SfxItemState::SET != rSet.GetItemState( RES_LR_SPACE, false) && + SfxItemState::SET == rSet.GetItemState( RES_LR_SPACE, true, &pItem ) ) + { + // the set the LR-Space of the parentformat! + AttrOutput().OutputItem( *pItem ); + } + } + + ww8::PoolItems aItems; + GetPoolItems( rSet, aItems, bExportParentItemSet ); + if ( bChpFormat ) + ExportPoolItemsToCHP(aItems, nScript, nullptr); + if ( bPapFormat ) + { + AttrOutput().MaybeOutputBrushItem(rSet); + + for ( const auto& rItem : aItems ) + { + pItem = rItem.second; + sal_uInt16 nWhich = pItem->Which(); + // Handle fill attributes just like frame attributes for now. + if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) || + (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST)) + AttrOutput().OutputItem( *pItem ); + } + + // Has to be called after RES_PARATR_GRABBAG is processed. + const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE)); + if (pXFillStyleItem && pXFillStyleItem->GetValue() == drawing::FillStyle_SOLID && !rSet.HasItem(RES_BACKGROUND)) + { + // Construct an SvxBrushItem, as expected by the exporters. + std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND)); + AttrOutput().OutputItem(*aBrush); + } +#if 0 + else + { + // note: *does not work* due to undocumented Word behavior: must be before a:ln element at least + AttrOutput().MaybeOutputBrushItem(rSet); + } +#endif + } + m_pISet = nullptr; // for double attributes + } +} + +void MSWordExportBase::GatherChapterFields() +{ + //If the header/footer contains a chapter field + SwFieldType* pType = m_pDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter ); + pType->GatherNodeIndex(m_aChapterFieldLocs); +} + +bool MSWordExportBase::ContentContainsChapterField(const SwFormatContent &rContent) const +{ + bool bRet = false; + if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() ) + { + SwNodeIndex aIdx( *pSttIdx, 1 ); + SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() ); + sal_uLong nStart = aIdx.GetIndex(); + sal_uLong nEnd = aEnd.GetIndex(); + //If the header/footer contains a chapter field + bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(), + [nStart, nEnd](sal_uLong i) { return ( nStart <= i ) && ( i <= nEnd ); }); + } + return bRet; +} + +bool MSWordExportBase::FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const +{ + if ( m_aChapterFieldLocs.empty() ) + return false; + + const SwFrameFormat *pFormat = nullptr; + + pFormat = rFormat.GetHeader().GetHeaderFormat(); + if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) ) + return true; + + pFormat = rFormat.GetFooter().GetFooterFormat(); + return pFormat && ContentContainsChapterField( pFormat->GetContent() ); +} + +bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd) +{ + bool bNewPageDesc = false; + const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd); + OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely"); + if (m_pCurrentPageDesc && pCurrent) + { + if (pCurrent != m_pCurrentPageDesc) + { + if (m_pCurrentPageDesc->GetFollow() != pCurrent) + bNewPageDesc = true; + else + { + const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster(); + const SwFrameFormat& rFollowFormat = pCurrent->GetMaster(); + + bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat, + rFollowFormat); + } + m_pCurrentPageDesc = pCurrent; + } + else + { + const SwFrameFormat &rFormat = pCurrent->GetMaster(); + bNewPageDesc = FormatHdFtContainsChapterField(rFormat); + } + } + return bNewPageDesc; +} + +/** + * WW only knows Break-After (page break and section breaks), + * whereas in SW page breaks exist both "before" and "after" and PageDesc exists + * only "before". Therefore the breaks are iterated two times, namely before + * and after every line. + * Depending on the break type they're set before or after the line. + * Only functions can be called, which do not write in output area pO, + * because that one only exits once for CHP and PAP and therefore end up in + * the wrong one. + */ +void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen, bool isTextNodeEmpty) +{ + if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs ) + return; + + m_bBreakBefore = true; + bool bNewPageDesc = false; + const SfxPoolItem* pItem=nullptr; + const SwFormatPageDesc *pPgDesc=nullptr; + + //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a + //pagebreak if there is a pagebreak here, unless the new page (follow + //style) is different to the current one, in which case plump for a + //section. + bool bBreakSet = false; + + const SwPageDesc * pPageDesc = rNd.FindPageDesc(); + + // Even if pAktPageDesc != pPageDesc ,it might be because of the different header & footer types. + if (m_pCurrentPageDesc != pPageDesc) + { + if ( ( isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() )) || + ( isTextNodeEmpty || m_bPrevTextNodeIsEmpty )) + { + /* Do not output a section break in the following scenarios. + 1) Table cell is open and page header types are different + 2) PageBreak is present but text node has no string - it is an empty node. + 3) If the previous node was an empty text node and current node is a non empty text node or vice versa. + 4) If previous node and current node both are empty text nodes. + Converting a page break to section break would cause serious issues while importing + the RT files with different first page being set. + */ + + /* + * If Table cell is open and page header types are different + * set pSet to NULL as we don't want to add any section breaks. + */ + if ( isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ) ) + pSet = nullptr; + + // tdf#118393: FILESAVE: DOCX Export loses header/footer + { + bool bPlausableSingleWordSection = sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()); + + { + const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster(); + const SwFrameFormat& rFollowFormat = pPageDesc->GetMaster(); + + auto pHeaderFormat1 = rTitleFormat.GetHeader().GetHeaderFormat(); + auto pHeaderFormat2 = rFollowFormat.GetHeader().GetHeaderFormat(); + + if (pHeaderFormat1 != pHeaderFormat2) + bPlausableSingleWordSection = false; + + auto pFooterFormat1 = rTitleFormat.GetFooter().GetFooterFormat(); + auto pFooterFormat2 = rFollowFormat.GetFooter().GetFooterFormat(); + + if (pFooterFormat1 != pFooterFormat2) + bPlausableSingleWordSection = false; + } + + if ( !bPlausableSingleWordSection && m_bFirstTOCNodeWithSection ) + { + bBreakSet = false; + bNewPageDesc = true; + m_pCurrentPageDesc = pPageDesc; + } + } + } + else if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster())) + { + bBreakSet = true; + bNewPageDesc = true; + m_pCurrentPageDesc = pPageDesc; + } + } + + if ( pSet && pSet->Count() ) + { + if ( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) && + static_cast<const SwFormatPageDesc*>(pItem)->GetRegisteredIn() != nullptr) + { + bBreakSet = true; + bNewPageDesc = true; + pPgDesc = static_cast<const SwFormatPageDesc*>(pItem); + m_pCurrentPageDesc = pPgDesc->GetPageDesc(); + } + else if ( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) ) + { + // Word does not like hard break attributes in some table cells + bool bRemoveHardBreakInsideTable = false; + if ( m_bOutTable ) + { + const SwTableNode* pTableNode = rNd.FindTableNode(); + if ( pTableNode ) + { + const SwTableBox* pBox = rNd.GetTableBox(); + const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr; + // but only for non-complex tables + if ( pLine && !pLine->GetUpper() ) + { + // check if box is not first in that line: + if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() ) + { + bRemoveHardBreakInsideTable = true; + } + } + } + } + bBreakSet = true; + + if ( !bRemoveHardBreakInsideTable ) + { + OSL_ENSURE(m_pCurrentPageDesc, "should not be possible"); + /* + If because of this pagebreak the page desc following the page + break is the follow style of the current page desc then output a + section break using that style instead. At least in those cases + we end up with the same style in word and writer, nothing can be + done when it happens when we get a new pagedesc because we + overflow from the first page style. + */ + if ( m_pCurrentPageDesc ) + { + // #i76301# - assure that there is a page break before set at the node. + const SvxFormatBreakItem* pBreak = dynamic_cast<const SvxFormatBreakItem*>(pItem); + if ( pBreak && + pBreak->GetBreak() == SvxBreak::PageBefore ) + { + bNewPageDesc |= SetCurrentPageDescFromNode( rNd ); + } + if( isTextNodeEmpty ) + bNewPageDesc = false; + } + if ( !bNewPageDesc ) + AttrOutput().OutputItem( *pItem ); + } + } + } + + /* + #i9301# + No explicit page break, lets see if the style had one and we've moved to a + new page style because of it, if we have to then we take the opportunity to + set the equivalent word section here. We *could* do it for every paragraph + that moves onto a new page because of layout, but that would be insane. + */ + bool bHackInBreak = false; + if ( !bBreakSet ) + { + if ( const SwContentNode *pNd = rNd.GetContentNode() ) + { + const SvxFormatBreakItem &rBreak = + ItemGet<SvxFormatBreakItem>( *pNd, RES_BREAK ); + if ( rBreak.GetBreak() == SvxBreak::PageBefore ) + bHackInBreak = true; + else + { // Even a pagedesc item is set, the break item can be set 'NONE', + // but a pagedesc item is an implicit page break before... + const SwFormatPageDesc &rPageDesc = + ItemGet<SwFormatPageDesc>( *pNd, RES_PAGEDESC ); + if ( rPageDesc.KnowsPageDesc() ) + bHackInBreak = true; + } + } + } + + if ( bHackInBreak ) + { + OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" ); + if ( m_pCurrentPageDesc ) + bNewPageDesc = SetCurrentPageDescFromNode( rNd ); + } + + if ( bNewPageDesc && m_pCurrentPageDesc ) + { + PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc ); + } + m_bBreakBefore = false; + m_bPrevTextNodeIsEmpty = isTextNodeEmpty ; +} + +// #i76300# +bool MSWordExportBase::OutputFollowPageDesc( const SfxItemSet* pSet, const SwTextNode* pNd ) +{ + bool bRet = false; + + if ( pNd && + m_pCurrentPageDesc && + m_pCurrentPageDesc != m_pCurrentPageDesc->GetFollow() ) + { + PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() ); + bRet = true; + } + + return bRet; +} + +const SwSectionFormat* MSWordExportBase::GetSectionFormat( const SwNode& rNd ) +{ + const SwSectionFormat* pFormat = nullptr; + const SwSectionNode* pSect = rNd.FindSectionNode(); + if ( pSect && + SectionType::Content == pSect->GetSection().GetType() ) + { + pFormat = pSect->GetSection().GetFormat(); + } + + return pFormat; +} + +sal_uLong MSWordExportBase::GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd ) +{ + const SwFormatLineNumber* pNItem = nullptr; + if ( pSet ) + { + pNItem = &( ItemGet<SwFormatLineNumber>( *pSet, RES_LINENUMBER ) ); + } + else if ( const SwContentNode *pNd = rNd.GetContentNode() ) + { + pNItem = &( ItemGet<SwFormatLineNumber>( *pNd, RES_LINENUMBER ) ); + } + + return pNItem? pNItem->GetStartValue() : 0; +} + +void WW8Export::PrepareNewPageDesc( const SfxItemSet*pSet, + const SwNode& rNd, + const SwFormatPageDesc* pNewPgDescFormat, + const SwPageDesc* pNewPgDesc ) +{ + // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding + // position by the occurrences of PageDesc attributes. The construction and + // output of the attributes and header/footer of the PageDesc are done + // after the main text and its attributes. + + sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break + + // actually nothing is outputted here, rather the arrays aCps, aSects + // accordingly completed + if ( !nFcPos ) + return; + + const SwSectionFormat* pFormat = GetSectionFormat( rNd ); + const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd ); + + OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." ); + + if ( pNewPgDescFormat ) + { + pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm ); + } + else if ( pNewPgDesc ) + { + pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm ); + } +} + +void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft ) +{ + if (const SvxTabStopItem *pItem = rSet.GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP)) + { + // then it must be corrected for the output + SvxTabStopItem aTStop(*pItem); + for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt ) + { + SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]); + if ( SvxTabAdjust::Default != rTab.GetAdjustment() && + rTab.GetTabPos() >= nAbsLeft ) + { + rTab.GetTabPos() -= nAbsLeft; + } + else + { + aTStop.Remove( nCnt ); + --nCnt; + } + } + rSet.Put( aTStop ); + } +} + +sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType ) +{ + sal_uInt8 nRet = 0; + switch( eNumType ) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break; + case SVX_NUM_ROMAN_UPPER: nRet = 1; break; + case SVX_NUM_ROMAN_LOWER: nRet = 2; break; + + case SVX_NUM_BITMAP: + case SVX_NUM_CHAR_SPECIAL: nRet = 23; break; + + // nothing, WW does the same (undocumented) + case SVX_NUM_NUMBER_NONE: nRet = 0xff; break; + case SVX_NUM_SYMBOL_CHICAGO: + // 0x09, msonfcChiManSty + nRet = 9; + break; + case SVX_NUM_ARABIC_ZERO: + // 0x16, msonfcArabicLZ + nRet = 22; + break; + } + return nRet; +} + +void WW8AttributeOutput::OutlineNumbering(sal_uInt8 nLvl) +{ + if ( nLvl >= WW8ListManager::nMaxLevel ) + nLvl = WW8ListManager::nMaxLevel-1; + + // write sprmPOutLvl sprmPIlvl and sprmPIlfo + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPOutLvl ); + m_rWW8Export.pO->push_back( nLvl ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlvl ); + m_rWW8Export.pO->push_back( nLvl ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlfo ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, + 1 + m_rWW8Export.GetNumberingId(*m_rWW8Export.m_pDoc->GetOutlineNumRule()) ); +} + +// #i77805# +bool WW8Export::DisallowInheritingOutlineNumbering(const SwFormat &rFormat) +{ + bool bRet( false ); + + //If there is no numbering on this fmt, but its parent was outline + //numbered, then in writer this is no inheritied, but in word it would + //be, so we must export "no numbering" and "body level" to make word + //behave like writer (see #i25755) + if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false)) + { + if (const SwFormat *pParent = rFormat.DerivedFrom()) + { + if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle()) + { + SwWW8Writer::InsUInt16(*pO, NS_sprm::sprmPOutLvl); + pO->push_back(sal_uInt8(9)); + SwWW8Writer::InsUInt16(*pO, NS_sprm::sprmPIlfo); + SwWW8Writer::InsUInt16(*pO, 0); + + bRet = true; + } + } + } + + return bRet; +} + +void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat ) +{ + bool bCallOutSet = true; + const SwModify* pOldMod = m_pOutFormatNode; + m_pOutFormatNode = &rFormat; + + switch( rFormat.Which() ) + { + case RES_CONDTXTFMTCOLL: + case RES_TXTFMTCOLL: + if( bPapFormat ) + { + int nLvl = MAXLEVEL; + + if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle()) + nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel(); + + if (nLvl >= 0 && nLvl < MAXLEVEL) + { + //if outline numbered + // if Write StyleDefinition then write the OutlineRule + const SwNumFormat& rNFormat = m_pDoc->GetOutlineNumRule()->Get( static_cast<sal_uInt16>( nLvl ) ); + if ( m_bStyDef ) + AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl)); + + if ( rNFormat.GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION && + rNFormat.GetAbsLSpace() ) + { + SfxItemSet aSet( rFormat.GetAttrSet() ); + SvxLRSpaceItem aLR( + ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE)); + + aLR.SetTextLeft( aLR.GetTextLeft() + rNFormat.GetAbsLSpace() ); + aLR.SetTextFirstLineOffset( GetWordFirstLineOffset(rNFormat)); + + aSet.Put( aLR ); + CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() ); + OutputItemSet( aSet, bPapFormat, bChpFormat, + i18n::ScriptType::LATIN, m_bExportModeRTF); + bCallOutSet = false; + } + } + else + { + //otherwise we might have to remove outline numbering from + //what gets exported if the parent style was outline numbered + // #i77805# + // If inherited outline numbering is suppress, the left/right + // margins has to be exported explicitly. + if ( m_bStyDef && DisallowInheritingOutlineNumbering(rFormat) ) + { + SfxItemSet aSet( rFormat.GetAttrSet() ); + const SvxLRSpaceItem& aLR( + ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE)); + aSet.Put( aLR ); + OutputItemSet( aSet, bPapFormat, bChpFormat, + css::i18n::ScriptType::LATIN, m_bExportModeRTF); + bCallOutSet = false; + } + } + } + break; + + case RES_CHRFMT: + break; + case RES_FLYFRMFMT: + if (bFlyFormat) + { + OSL_ENSURE(m_pParentFrame, "No parent frame, all broken"); + + if (m_pParentFrame) + { + const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat(); + + SfxItemSet aSet(m_pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END-1, + XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + aSet.Set(rFrameFormat.GetAttrSet()); + + // Fly as character becomes a paragraph bound + // now set the distance to paragraph margin + if (m_pFlyOffset) + { + aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X())); + aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y())); + SwFormatAnchor aAnchor(rFrameFormat.GetAnchor()); + aAnchor.SetType(m_eNewAnchorType); + aSet.Put(aAnchor); + } + + if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND)) + aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE)); + + const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE)); + if (pXFillStyleItem) + { + switch (pXFillStyleItem->GetValue()) + { + case drawing::FillStyle_NONE: + break; + case drawing::FillStyle_SOLID: + { + // Construct an SvxBrushItem, as expected by the exporters. + std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND)); + aSet.Put(*aBrush); + break; + } + default: + break; + } + } + + m_bOutFlyFrameAttrs = true; + //script doesn't matter if not exporting chp + OutputItemSet(aSet, true, false, + i18n::ScriptType::LATIN, m_bExportModeRTF); + m_bOutFlyFrameAttrs = false; + + bCallOutSet = false; + } + } + break; + case RES_FRMFMT: + break; + default: + OSL_ENSURE( false, "Which format is exported here?" ); + break; + } + + if( bCallOutSet ) + OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat, + i18n::ScriptType::LATIN, m_bExportModeRTF); + m_pOutFormatNode = pOldMod; +} + +bool MSWordExportBase::HasRefToAttr(const OUString& rName) +{ + SwFieldType* pType = m_pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef); + std::vector<SwGetRefField*> vpRFields; + pType->GatherRefFields(vpRFields, REF_SETREFATTR); + return std::any_of(vpRFields.begin(), vpRFields.end(), + [rName](SwGetRefField* pF) { return rName == pF->GetSetRefName(); }); +} + +bool MSWordExportBase::HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo) +{ + SwFieldType* pType = m_pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef); + std::vector<SwGetRefField*> vpRFields; + pType->GatherRefFields(vpRFields, isEndNote ? REF_ENDNOTE : REF_FOOTNOTE); + return std::any_of(vpRFields.begin(), vpRFields.end(), + [nSeqNo](SwGetRefField* pF) { return nSeqNo == pF->GetSeqNo(); }); +} + +OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo ) +{ + OUString sRet; + switch ( nTyp ) + { + case REF_SETREFATTR: + if ( pName ) + { + sRet = "Ref_" + *pName; + } + break; + case REF_SEQUENCEFLD: + { + assert(pName); + sRet = "Ref_" + *pName; + break; + } + case REF_BOOKMARK: + if ( pName ) + sRet = *pName; + break; + case REF_OUTLINE: + break; // ??? + case REF_FOOTNOTE: + sRet = "_RefF" + OUString::number( nSeqNo ); + break; + case REF_ENDNOTE: + sRet = "_RefE" + OUString::number( nSeqNo ); + break; + } + return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly +} + +/* File CHRATR.HXX: */ +void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) +{ + if (bIsRTL) + { + if( m_rWW8Export.m_pDoc->GetDocumentType() != SwDoc::DOCTYPE_MSWORD ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCFBiDi ); + m_rWW8Export.pO->push_back( sal_uInt8(1) ); + } + } + + // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81. + if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCFComplexScripts ); + m_rWW8Export.pO->push_back( sal_uInt8(0x81) ); + m_rWW8Export.pDop->bUseThaiLineBreakingRules = true; + } +} + +void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) +{ + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + mbOnTOXEnding = false; + m_rWW8Export.pO->clear(); + + if ( pTextNodeInfoInner.get() != nullptr ) + { + if ( pTextNodeInfoInner->isEndOfLine() ) + { + TableRowEnd( pTextNodeInfoInner->getDepth() ); + + SVBT16 nSty; + ShortToSVBT16( 0, nSty ); + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // Style # + TableInfoRow( pTextNodeInfoInner ); + m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data()); + m_rWW8Export.pO->clear(); + } + } + + // Clear bookmarks of the current paragraph + m_aBookmarksOfParagraphStart.clear(); + m_aBookmarksOfParagraphEnd.clear(); +} + +void WW8AttributeOutput::StartRunProperties() +{ + WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc(); + m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0; +} + +void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ ) +{ + if (pRedlineData) + { + const OUString &rComment = pRedlineData->GetComment(); + //Only possible to export to main text + if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT)) + { + if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData)) + { + m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData ); + m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() ); + } + } + } + + /// Insert bookmarks started at this run + auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + GetExport().AppendBookmark(BookmarkToWord(aIter->second)); + } +} + +void WW8AttributeOutput::OnTOXEnding() +{ + mbOnTOXEnding = true; +} + +void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, bool bLastRun ) +{ + /// Insert bookmarks ended after this run + auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + if(bLastRun) + GetExport().AppendBookmarkEndWithCorrection(BookmarkToWord(aIter->second)); + else + GetExport().AppendBookmark(BookmarkToWord(aIter->second)); + } +} + +void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) +{ + Redline( pRedlineData ); + + WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc(); + sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0; + + bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults ); + + // If we have exported a field result, then we will have been forced to + // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the + // properties forced out at the end of the result, so the 0x15 itself + // should remain clean of all other attributes to avoid #iXXXXX# + if ( !bExportedFieldResult ) + { + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), + m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + } + m_rWW8Export.pO->clear(); +} + +void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet ) +{ + RawText(rText, eCharSet); +} + +void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding) +{ + m_rWW8Export.OutSwString(rText, 0, rText.getLength()); +} + +void WW8AttributeOutput::OutputFKP(bool bForce) +{ + if (!m_rWW8Export.pO->empty() || bForce) + { + m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), + m_rWW8Export.pO->size(), m_rWW8Export.pO->data() ); + m_rWW8Export.pO->clear(); + } +} + +void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle ) +{ + OSL_ENSURE( m_rWW8Export.pO->empty(), " pO is not empty at line end" ); + + SVBT16 nSty; + ShortToSVBT16( nStyle, nSty ); + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // style # +} + +void WW8AttributeOutput::OutputWW8Attribute( sal_uInt8 nId, bool bVal ) +{ + m_rWW8Export.InsUInt16( 8 == nId ? NS_sprm::sprmCFDStrike : NS_sprm::sprmCFBold + nId ); + + m_rWW8Export.pO->push_back( bVal ? 1 : 0 ); +} + +void WW8AttributeOutput::OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal ) +{ + OSL_ENSURE( nId <= 1, "out of range" ); + if (nId > 1) + return; + + m_rWW8Export.InsUInt16( NS_sprm::sprmCFBoldBi + nId ); + m_rWW8Export.pO->push_back( bVal ? 1 : 0 ); +} + +void WW8AttributeOutput::CharFont( const SvxFontItem& rFont ) +{ + sal_uInt16 nFontID = m_rWW8Export.GetId( rFont ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc0 ); + m_rWW8Export.InsUInt16( nFontID ); + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc2 ); + + m_rWW8Export.InsUInt16( nFontID ); +} + +void WW8AttributeOutput::CharFontCTL( const SvxFontItem& rFont ) +{ + sal_uInt16 nFontID = m_rWW8Export.GetId( rFont ); + m_rWW8Export.InsUInt16( NS_sprm::sprmCFtcBi ); + m_rWW8Export.InsUInt16( nFontID ); +} + +void WW8AttributeOutput::CharFontCJK( const SvxFontItem& rFont ) +{ + sal_uInt16 nFontID = m_rWW8Export.GetId( rFont ); + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc1 ); + m_rWW8Export.InsUInt16( nFontID ); +} + +void WW8AttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight ) +{ + OutputWW8AttributeCTL( 0, WEIGHT_BOLD == rWeight.GetWeight()); +} + +void WW8AttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture ) +{ + OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() ); +} + +void WW8AttributeOutput::CharPosture( const SvxPostureItem& rPosture ) +{ + OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() ); +} + +void WW8AttributeOutput::CharWeight( const SvxWeightItem& rWeight ) +{ + OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() ); +} + +// Shadowed and Contour are not in WW-UI. JP: ?? +void WW8AttributeOutput::CharContour( const SvxContourItem& rContour ) +{ + OutputWW8Attribute( 3, rContour.GetValue() ); +} + +void WW8AttributeOutput::CharShadow( const SvxShadowedItem& rShadow ) +{ + OutputWW8Attribute( 4, rShadow.GetValue() ); +} + +void WW8AttributeOutput::CharKerning( const SvxKerningItem& rKerning ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCDxaSpace ); + + m_rWW8Export.InsUInt16( rKerning.GetValue() ); +} + +void WW8AttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCHpsKern ); + + m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 ); +} + +void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCSfxText ); + // At the moment the only animated text effect we support is blinking + m_rWW8Export.pO->push_back( rBlink.GetValue() ? 2 : 0 ); +} + +void WW8AttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossed ) +{ + FontStrikeout eSt = rCrossed.GetStrikeout(); + if ( STRIKEOUT_DOUBLE == eSt ) + { + OutputWW8Attribute( 8, true ); + return; + } + if ( STRIKEOUT_NONE != eSt ) + { + OutputWW8Attribute( 2, true ); + return; + } + + // otherwise both off + OutputWW8Attribute( 8, false ); + OutputWW8Attribute( 2, false ); +} + +void WW8AttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap ) +{ + SvxCaseMap eSt = rCaseMap.GetValue(); + switch ( eSt ) + { + case SvxCaseMap::SmallCaps: + OutputWW8Attribute( 5, true ); + return; + case SvxCaseMap::Uppercase: + OutputWW8Attribute( 6, true ); + return; + case SvxCaseMap::Capitalize: + // no such feature in word + break; + default: + // otherwise both off + OutputWW8Attribute( 5, false ); + OutputWW8Attribute( 6, false ); + return; + } +} + +void WW8AttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden ) +{ + OutputWW8Attribute( 7, rHidden.GetValue() ); +} + +void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow ) +{ + WW8Export::Out_BorderLine( *m_rWW8Export.pO, pAllBorder, 0, NS_sprm::sprmCBrc80, NS_sprm::sprmCBrc, bShadow ); +} + +void WW8AttributeOutput::CharHighlight( const SvxBrushItem& rBrush ) +{ + if (rBrush.GetColor() != COL_TRANSPARENT) + { + sal_uInt8 nColor = msfilter::util::TransColToIco( rBrush.GetColor() ); + // sprmCHighlight + m_rWW8Export.InsUInt16( NS_sprm::sprmCHighlight ); + m_rWW8Export.pO->push_back( nColor ); + } +} + +void WW8AttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCKul ); + + const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_CHRATR_WORDLINEMODE ); + bool bWord = false; + if (pItem) + bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue(); + + // WW95 - parameters: 0 = none, 1 = single, 2 = by Word, + // 3 = double, 4 = dotted, 5 = hidden + // WW97 - additional parameters: + // 6 = thick, 7 = dash, 8 = dot(not used) + // 9 = dotdash 10 = dotdotdash, 11 = wave + sal_uInt8 b = 0; + switch ( rUnderline.GetLineStyle() ) + { + case LINESTYLE_SINGLE: + b = bWord ? 2 : 1; + break; + case LINESTYLE_BOLD: + b = 6; + break; + case LINESTYLE_DOUBLE: + b = 3; + break; + case LINESTYLE_DOTTED: + b = 4; + break; + case LINESTYLE_DASH: + b = 7; + break; + case LINESTYLE_DASHDOT: + b = 9; + break; + case LINESTYLE_DASHDOTDOT: + b = 10; + break; + case LINESTYLE_WAVE: + b = 11; + break; + // new in WW2000 + case LINESTYLE_BOLDDOTTED: + b = 20; + break; + case LINESTYLE_BOLDDASH: + b = 23; + break; + case LINESTYLE_LONGDASH: + b = 39; + break; + case LINESTYLE_BOLDLONGDASH: + b = 55; + break; + case LINESTYLE_BOLDDASHDOT: + b = 25; + break; + case LINESTYLE_BOLDDASHDOTDOT: + b = 26; + break; + case LINESTYLE_BOLDWAVE: + b = 27; + break; + case LINESTYLE_DOUBLEWAVE: + b = 43; + break; + case LINESTYLE_NONE: + b = 0; + break; + default: + OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" ); + break; + } + + m_rWW8Export.pO->push_back( b ); + Color aColor = rUnderline.GetColor(); + if( aColor != COL_TRANSPARENT ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCCvUl ); + + m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( aColor ) ); + } +} + +void WW8AttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage ) +{ + sal_uInt16 nId = 0; + switch ( rLanguage.Which() ) + { + case RES_CHRATR_LANGUAGE: + nId = NS_sprm::sprmCRgLid0_80; + break; + case RES_CHRATR_CJK_LANGUAGE: + nId = NS_sprm::sprmCRgLid1_80; + break; + case RES_CHRATR_CTL_LANGUAGE: + nId = NS_sprm::sprmCLidBi; + break; + } + + if ( nId ) + { + // use sprmCRgLid0_80 rather than sprmCLid + m_rWW8Export.InsUInt16( nId ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) ); + + // Word 2000 and above apparently require both old and new versions of + // these sprms to be set, without it spellchecking doesn't work + if ( nId == NS_sprm::sprmCRgLid0_80 ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgLid0 ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) ); + } + else if ( nId == NS_sprm::sprmCRgLid1_80 ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCRgLid1 ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) ); + } + } +} + +void WW8AttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement ) +{ + sal_uInt8 b = 0xFF; + short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight(); + if ( !nEsc ) + { + b = 0; + nEsc = 0; + nProp = 100; + } + else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 ) + { + if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc ) + b = 2; + else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc ) + b = 1; + } + else if ( DFLT_ESC_AUTO_SUPER == nEsc ) + { + // Raised by the differences between the ascenders (ascent = baseline to top of highest letter). + // The ascent is generally about 80% of the total font height. + // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER) + nEsc = .8 * (100 - nProp); + } + else if ( DFLT_ESC_AUTO_SUB == nEsc ) + { + // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter). + // The descent is generally about 20% of the total font height. + // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB) + nEsc = .2 * -(100 - nProp); + } + + if ( 0xFF != b ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCIss ); + + m_rWW8Export.pO->push_back( b ); + } + + if ( 0 == b || 0xFF == b ) + { + double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight(); + m_rWW8Export.InsUInt16( NS_sprm::sprmCHpsPos ); + + m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) )); + + if( 100 != nProp || !b ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCHps ); + m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) )); + } + } +} + +void WW8AttributeOutput::CharFontSize( const SvxFontHeightItem& rHeight ) +{ + sal_uInt16 nId = 0; + switch ( rHeight.Which() ) + { + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_CJK_FONTSIZE: + nId = NS_sprm::sprmCHps; + break; + case RES_CHRATR_CTL_FONTSIZE: + nId = NS_sprm::sprmCHpsBi; + break; + } + + if ( nId ) + { + m_rWW8Export.InsUInt16( nId ); + + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) ); + } +} + +void WW8AttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCCharScale ); + m_rWW8Export.InsUInt16( rScaleWidth.GetValue() ); +} + +void WW8AttributeOutput::CharRelief( const SvxCharReliefItem& rRelief ) +{ + sal_uInt16 nId; + switch ( rRelief.GetValue() ) + { + case FontRelief::Embossed: nId = NS_sprm::sprmCFEmboss; break; + case FontRelief::Engraved: nId = NS_sprm::sprmCFImprint; break; + default: nId = 0; break; + } + + if( nId ) + { + m_rWW8Export.InsUInt16( nId ); + m_rWW8Export.pO->push_back( sal_uInt8(0x81) ); + } + else + { + // switch both flags off + m_rWW8Export.InsUInt16( NS_sprm::sprmCFEmboss ); + m_rWW8Export.pO->push_back( sal_uInt8(0x0) ); + m_rWW8Export.InsUInt16( NS_sprm::sprmCFImprint ); + m_rWW8Export.pO->push_back( sal_uInt8(0x0) ); + } +} + +void WW8AttributeOutput::CharBidiRTL( const SfxPoolItem& rHt ) +{ + const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt); + if( rAttr.GetValue() == 1 ) + { + m_rWW8Export.InsUInt16(0x85a); + m_rWW8Export.pO->push_back(sal_uInt8(1)); + } +} + +void WW8AttributeOutput::CharIdctHint( const SfxPoolItem& rHt ) +{ + const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt); + m_rWW8Export.InsUInt16(0x286F); + m_rWW8Export.pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue())); +} + +void WW8AttributeOutput::CharRotate( const SvxCharRotateItem& rRotate ) +{ + // #i28331# - check that a Value is set + if ( !rRotate.GetValue() ) + return; + + if (!m_rWW8Export.IsInTable()) + { + // #i36867 In word the text in a table is rotated via the TC or NS_sprm::sprmTTextFlow + // This means you can only rotate all or none of the text adding NS_sprm::sprmCFELayout + // here corrupts the table, hence !m_rWW8Export.bIsInTable + + m_rWW8Export.InsUInt16( NS_sprm::sprmCFELayout ); + m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6 + m_rWW8Export.pO->push_back( sal_uInt8(0x01) ); + + m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 ); + static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 }; + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3); + } +} + +void WW8AttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark ) +{ + sal_uInt8 nVal; + const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark(); + if (v == FontEmphasisMark::NONE) + nVal = 0; + else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove)) + nVal = 2; + else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove)) + nVal = 3; + else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow)) + nVal = 4; + else + // case 1: + nVal = 1; + + m_rWW8Export.InsUInt16( NS_sprm::sprmCKcd ); + m_rWW8Export.pO->push_back( nVal ); +} + +/** + * TransBrush converts SW-Brushes to WW. The result is WW8_SHD. + * Non-standard colours of SW won't be converted now to the mixed values + * ( 0 .. 95% ) of WW. + * Also if transparent, e.g. for tables a transparent brush is returned + * + * @return real brush ( not transparent ) + */ +bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd) +{ + if( rCol.GetTransparency() ) + rShd = WW8_SHD(); // all zeros: transparent + else + { + rShd.SetFore( 0); + rShd.SetBack( msfilter::util::TransColToIco( rCol ) ); + rShd.SetStyle( 0 ); + } + return !rCol.GetTransparency(); +} + +static sal_uInt32 SuitableBGColor(Color nIn) +{ + if (nIn == COL_AUTO) + return 0xFF000000; + return wwUtility::RGBToBGR(nIn); +} + +void WW8AttributeOutput::CharColor( const SvxColorItem& rColor ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmCIco ); + + sal_uInt8 nColor = msfilter::util::TransColToIco( rColor.GetValue() ); + m_rWW8Export.pO->push_back( nColor ); + + if (nColor) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCCv ); + m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( rColor.GetValue() ) ); + } +} + +void WW8AttributeOutput::CharBackground( const SvxBrushItem& rBrush ) +{ + WW8_SHD aSHD; + + WW8Export::TransBrush( rBrush.GetColor(), aSHD ); + // sprmCShd80 + m_rWW8Export.InsUInt16( NS_sprm::sprmCShd80 ); + m_rWW8Export.InsUInt16( aSHD.GetValue() ); + + //Quite a few unknowns, some might be transparency or something + //of that nature... + m_rWW8Export.InsUInt16( NS_sprm::sprmCShd ); + m_rWW8Export.pO->push_back( 10 ); + m_rWW8Export.InsUInt32( 0xFF000000 ); + m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); + m_rWW8Export.InsUInt16( 0x0000); +} + +void WW8AttributeOutput::TextINetFormat( const SwFormatINetFormat& rINet ) +{ + if ( !rINet.GetValue().isEmpty() ) + { + const sal_uInt16 nId = rINet.GetINetFormatId(); + const OUString& rStr = rINet.GetINetFormat(); + if (rStr.isEmpty()) + { + OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" ); + } + + const SwCharFormat* pFormat = IsPoolUserFormat( nId ) + ? m_rWW8Export.m_pDoc->FindCharFormatByName( rStr ) + : m_rWW8Export.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmCIstd ); + + m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pFormat ) ); + } +} + +// #i43956# - add optional parameter <pLinkStr> +// It's needed to write the hyperlink data for a certain cross-reference +// - it contains the name of the link target, which is a bookmark. +// add optional parameter <bIncludeEmptyPicLocation> +// It is needed to write an empty picture location for page number field separators +static void InsertSpecialChar( WW8Export& rWrt, sal_uInt8 c, + OUString const * pLinkStr, + bool bIncludeEmptyPicLocation = false ) +{ + ww::bytes aItems; + rWrt.GetCurrentItems(aItems); + + if (c == 0x13) + rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell()); + else + rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data()); + + rWrt.WriteChar(c); + + // store empty sprmCPicLocation for field separator + if ( bIncludeEmptyPicLocation && + ( c == 0x13 || c == 0x14 || c == 0x15 ) ) + { + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCPicLocation ); + SwWW8Writer::InsUInt32( aItems, 0x00000000 ); + } + + // #i43956# - write hyperlink data and attributes + if ( c == 0x01 && pLinkStr) + { + // write hyperlink data to data stream + SvStream& rStrm = *rWrt.pDataStrm; + // position of hyperlink data + const sal_uInt32 nLinkPosInDataStrm = rStrm.Tell(); + // write empty header + const sal_uInt16 nEmptyHdrLen = 0x44; + sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 }; + aEmptyHeader[ 4 ] = 0x44; + rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen ); + // writer fixed header + const sal_uInt16 nFixHdrLen = 0x19; + sal_uInt8 const aFixHeader[ nFixHdrLen ] = + { + 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, + 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, + 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, + }; + rStrm.WriteBytes( aFixHeader, nFixHdrLen ); + // write reference string including length+1 + sal_uInt32 nStrLen( pLinkStr->getLength() + 1 ); + SwWW8Writer::WriteLong( rStrm, nStrLen ); + SwWW8Writer::WriteString16( rStrm, *pLinkStr, false ); + // write additional two NULL Bytes + SwWW8Writer::WriteLong( rStrm, 0 ); + // write length of hyperlink data + const sal_uInt32 nCurrPos = rStrm.Tell(); + rStrm.Seek( nLinkPosInDataStrm ); + rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm); + rStrm.Seek( nCurrPos ); + + // write attributes of hyperlink character 0x01 + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFFldVanish ); + aItems.push_back( sal_uInt8(0x81) ); + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCPicLocation ); + SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm ); + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFData ); + aItems.push_back( sal_uInt8(0x01) ); + } + + //Technically we should probably Remove all attribs + //here for the 0x13, 0x14, 0x15, but our import + //is slightly lacking + //aItems.Remove(0, aItems.Count()); + // fSpec-Attribute true + SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFSpec ); + aItems.push_back( 1 ); + + rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data()); +} + +static OUString lcl_GetExpandedField(const SwField &rField) +{ + //replace LF 0x0A with VT 0x0B + return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B); +} + +WW8_WrPlcField* WW8Export::CurrentFieldPlc() const +{ + WW8_WrPlcField* pFieldP = nullptr; + switch (m_nTextTyp) + { + case TXT_MAINTEXT: + pFieldP = m_pFieldMain.get(); + break; + case TXT_HDFT: + pFieldP = m_pFieldHdFt.get(); + break; + case TXT_FTN: + pFieldP = m_pFieldFootnote.get(); + break; + case TXT_EDN: + pFieldP = m_pFieldEdn.get(); + break; + case TXT_ATN: + pFieldP = m_pFieldAtn.get(); + break; + case TXT_TXTBOX: + pFieldP = m_pFieldTextBxs.get(); + break; + case TXT_HFTXTBOX: + pFieldP = m_pFieldHFTextBxs.get(); + break; + default: + OSL_ENSURE( false, "what type of SubDoc is that?" ); + } + return pFieldP; +} + +void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType, + const OUString& rFieldCmd, FieldFlags nMode ) +{ + OUString sFieldCmd(rFieldCmd); + switch (eFieldType) + { + // map fields that are not supported in WW8 as of Word 2003 + case ww::eBIBLIOGRAPHY: + eFieldType = ww::eQUOTE; + assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY)); + sFieldCmd = FieldString(ww::eQUOTE); + break; + case ww::eCITATION: + eFieldType = ww::eQUOTE; + assert(rFieldCmd.trim().startsWith("CITATION")); + sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION), + FieldString(ww::eQUOTE)); + break; + default: + break; + } + + assert(eFieldType <= 0x5F); // 95 is the highest documented one + + WW8_WrPlcField* pFieldP = CurrentFieldPlc(); + + const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE ); + if (FieldFlags::Start & nMode) + { + sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change + //#i3958#, Needed to make this field work correctly in Word 2000 + if (eFieldType == ww::eSHAPE) + aField13[0] |= 0x80; + aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type + pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 ); + InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation ); + } + if (FieldFlags::CmdStart & nMode) + { + SwWW8Writer::WriteString16(Strm(), sFieldCmd, false); + // #i43956# - write hyperlink character including + // attributes and corresponding binary data for certain reference fields. + bool bHandleBookmark = false; + + if (pField) + { + if (pField->GetTyp()->Which() == SwFieldIds::GetRef && + ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF || + eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF )) + bHandleBookmark = true; + } + + if ( bHandleBookmark ) + { + // retrieve reference destination - the name of the bookmark + OUString aLinkStr; + const sal_uInt16 nSubType = pField->GetSubType(); + const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField); + if ( nSubType == REF_SETREFATTR || + nSubType == REF_BOOKMARK ) + { + const OUString& aRefName(rRField.GetSetRefName()); + aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 ); + } + else if ( nSubType == REF_FOOTNOTE || + nSubType == REF_ENDNOTE ) + { + aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() ); + } + else if ( nSubType == REF_SEQUENCEFLD ) + { + aLinkStr = pField->GetPar2(); + } + // insert hyperlink character including attributes and data. + InsertSpecialChar( *this, 0x01, &aLinkStr ); + } + } + if (FieldFlags::CmdEnd & nMode) + { + static const sal_uInt8 aField14[2] = { 0x14, 0xff }; + pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 ); + pFieldP->ResultAdded(); + InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation ); + } + if (FieldFlags::End & nMode) + { + OUString sOut; + if( pField ) + sOut = lcl_GetExpandedField(*pField); + else + sOut = sFieldCmd; + if( !sOut.isEmpty() ) + { + SwWW8Writer::WriteString16(Strm(), sOut, false); + + if (pField) + { + if (pField->GetTyp()->Which() == SwFieldIds::Input && + eFieldType == ww::eFORMTEXT) + { + sal_uInt8 aArr[12]; + sal_uInt8 *pArr = aArr; + + Set_UInt16( pArr, NS_sprm::sprmCPicLocation ); + Set_UInt32( pArr, 0x0 ); + + Set_UInt16( pArr, NS_sprm::sprmCFSpec ); + Set_UInt8( pArr, 1 ); + + Set_UInt16( pArr, NS_sprm::sprmCFNoProof ); + Set_UInt8( pArr, 1 ); + + m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr ); + } + } + } + } + if (FieldFlags::Close & nMode) + { + sal_uInt8 aField15[2] = { 0x15, 0x80 }; + + if (pField) + { + if (pField->GetTyp()->Which() == SwFieldIds::Input && + eFieldType == ww::eFORMTEXT) + { + sal_uInt16 nSubType = pField->GetSubType(); + + if (nSubType == REF_SEQUENCEFLD) + aField15[0] |= (0x4 << 5); + } + } + + pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 ); + InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation ); + } +} + +void WW8Export::StartCommentOutput(const OUString& rName) +{ + const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " }; + OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::Start | FieldFlags::CmdStart); +} + +void WW8Export::EndCommentOutput(const OUString& rName) +{ + const OUString sStr{ " [" + rName + "] " }; + OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::CmdEnd | FieldFlags::End | + FieldFlags::Close); +} + +sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType ) +{ + std::vector<const SwTOXType*>::iterator it + = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType ); + if ( it != m_aTOXArr.end() ) + { + return it - m_aTOXArr.begin(); + } + m_aTOXArr.push_back( &rTOXType ); + return m_aTOXArr.size() - 1; +} + +// return values: 1 - no PageNum, +// 2 - TabStop before PageNum, +// 3 - Text before PageNum - rText hold the text +// 4 - no Text and no TabStop before PageNum +static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText ) +{ + int nRet = 4; + rText.clear(); + + // #i21237# + SwFormTokens aPattern = rForm.GetPattern(nLvl); + SwFormTokens::iterator aIt = aPattern.begin(); + FormTokenType eTType; + + // #i61362# + if (! aPattern.empty()) + { + bool bPgNumFnd = false; + + // #i21237# + while( ++aIt != aPattern.end() && !bPgNumFnd ) + { + eTType = aIt->eTokenType; + + switch( eTType ) + { + case TOKEN_PAGE_NUMS: + bPgNumFnd = true; + break; + + case TOKEN_TAB_STOP: + nRet = 2; + break; + case TOKEN_TEXT: + { + nRet = 3; + sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength()); + rText = aIt->sText.copy(0, nCount); // #i21237# + break; + } + case TOKEN_LINK_START: + case TOKEN_LINK_END: + break; + + default: + nRet = 4; + break; + } + } + + if( !bPgNumFnd ) + nRet = 1; + } + + return nRet; +} + +static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl) +{ + bool bRes = false; + for (sal_uInt16 nI = 1; nI < nTOXLvl; ++nI) + { + // #i21237# + SwFormTokens aPattern = rForm.GetPattern(nI); + + if ( !aPattern.empty() ) + { + SwFormTokens::iterator aIt = aPattern.begin(); + + FormTokenType eTType; + + // #i21237# + while ( ++aIt != aPattern.end() ) + { + eTType = aIt->eTokenType; + switch (eTType) + { + case TOKEN_LINK_START: + case TOKEN_LINK_END: + bRes = true; + break; + default: + ; + } + } + } + } + return bRes; +} + +void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) +{ + if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF + return; + + if (const SwpHints* pTextAttrs = rNode.GetpSwpHints()) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if (pHt->GetAttr().Which() == RES_TXTATR_FIELD) + { + const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr()); + const SwField* pField = rField.GetField(); + // Need to have bookmarks only for sequence fields + if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ) + { + const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber(); + const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName(); + const SwFieldTypes* pFieldTypes = GetExport().m_pDoc->getIDocumentFieldsAccess().GetFieldTypes(); + bool bHaveFullBkm = false; + bool bHaveLabelAndNumberBkm = false; + bool bHaveCaptionOnlyBkm = false; + bool bHaveNumberOnlyBkm = false; + bool bRunSplittedAtSep = false; + for( auto const & pFieldType : *pFieldTypes ) + { + if( SwFieldIds::GetRef == pFieldType->Which() ) + { + SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType ); + for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() ) + { + SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField()); + // If we have a reference to the current sequence field + if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName) + { + // Need to create a separate run for separator character + SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs + const OUString& aText = rNode.GetText(); + const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName()); + const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart()); + bool bCategoryFirst = nCategoryStart < pHt->GetStart(); + sal_Int32 nSeparatorPos = 0; + if (bCategoryFirst) + { + nSeparatorPos = aLocalAttrIter.WhereNext(); + while (nSeparatorPos <= nPosBeforeSeparator) + { + aLocalAttrIter.NextPos(); + nSeparatorPos = aLocalAttrIter.WhereNext(); + } + } + else + { + nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength(); + } + sal_Int32 nRefTextPos = 0; + if(nSeparatorPos < aText.getLength()) + { + nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), *GetExport().m_pDoc, nSeparatorPos); + if(nRefTextPos != nSeparatorPos) + { + if(!bRunSplittedAtSep) + { + if(!bCategoryFirst) + rAttrIter.SplitRun(nSeparatorPos); + rAttrIter.SplitRun(nRefTextPos); + bRunSplittedAtSep = true; + } + if(!bCategoryFirst) + aLocalAttrIter.SplitRun(nSeparatorPos); + aLocalAttrIter.SplitRun(nRefTextPos); + } + else if (bCategoryFirst) + { + if(!bRunSplittedAtSep) + { + rAttrIter.SplitRun(nSeparatorPos); + bRunSplittedAtSep = true; + } + aLocalAttrIter.SplitRun(nSeparatorPos); + } + } + // Generate bookmarks on the right position + OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo())); + switch (pRefField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + case REF_CONTENT: + case REF_UPDOWN: + if(!bHaveFullBkm) + { + sal_Int32 nLastAttrStart = 0; + sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); + while (nActAttr < rNode.GetText().getLength()) + { + nLastAttrStart = nActAttr; + aLocalAttrIter.NextPos(); + nActAttr = aLocalAttrIter.WhereNext(); + } + WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart ); + bHaveFullBkm = true; + } + break; + case REF_ONLYNUMBER: + { + if(!bHaveLabelAndNumberBkm) + { + sName += "_label_and_number"; + if(bCategoryFirst) + WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) ); + else + { + // Find the last run which contains category text + SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode ); + sal_Int32 nCatLastRun = 0; + sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext(); + while (nNextAttr < nSeparatorPos) + { + nCatLastRun = nNextAttr; + aLocalAttrIter2.NextPos(); + nNextAttr = aLocalAttrIter2.WhereNext(); + } + WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun ); + } + bHaveLabelAndNumberBkm = true; + } + break; + } + case REF_ONLYCAPTION: + { + if(!bHaveCaptionOnlyBkm) + { + // Find last run + sal_Int32 nLastAttrStart = 0; + sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); + while (nActAttr < rNode.GetText().getLength()) + { + nLastAttrStart = nActAttr; + aLocalAttrIter.NextPos(); + nActAttr = aLocalAttrIter.WhereNext(); + } + WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart ); + bHaveCaptionOnlyBkm = true; + } + break; + } + case REF_ONLYSEQNO: + { + if(!bHaveNumberOnlyBkm) + { + WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() ); + bHaveNumberOnlyBkm = true; + } + break; + } + } + } + } + } + } + return; + } + } + } + } +} + +void AttributeOutputBase::StartTOX( const SwSection& rSect ) +{ + if ( const SwTOXBase* pTOX = rSect.GetTOXBase() ) + { + static const char sEntryEnd[] = "\" "; + + ww::eField eCode = ww::eTOC; + OUString sStr = pTOX ->GetMSTOCExpression(); + if ( sStr.isEmpty() ) + { + switch (pTOX->GetType()) + { + case TOX_INDEX: + eCode = ww::eINDEX; + sStr = FieldString(eCode); + + { + const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL ); + const SwColumns& rColumns = rCol.GetColumns(); + sal_Int32 nCol = rColumns.size(); + + if ( 0 < nCol ) + { + // Add a continuous section break + if( GetExport().AddSectionBreaksForTOX() ) + { + SwSection *pParent = rSect.GetParent(); + WW8_SepInfo rInfo(&GetExport( ).m_pDoc->GetPageDesc(0), + pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/); + GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo ); + } + + sStr += "\\c \"" + OUString::number( nCol ) + "\""; + } + } + + if (pTOX->GetTOXForm().IsCommaSeparated()) + sStr += "\\r "; + + if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions()) + sStr += "\\h \"A\" "; + + if(SwTOXElement::IndexEntryType & pTOX->GetCreateType()) + { + sStr += "\\f "; + const OUString& sName = pTOX->GetEntryTypeName(); + if(!sName.isEmpty()) + { + sStr += sName + sEntryEnd; + } + } + + if (!pTOX->GetTOXForm().IsCommaSeparated()) + { + // In case of Run-in style no separators are added. + OUString aFillText; + for (sal_uInt8 n = 1; n <= 3; ++n) + { + OUString aText; + int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText); + + if( 3 == nRet ) + aFillText = aText; + else if ((4 == nRet) || (2 == nRet)) + aFillText = "\t"; + else + aFillText.clear(); + } + sStr += "\\e \"" + aFillText + sEntryEnd; + } + break; + + case TOX_ILLUSTRATIONS: + case TOX_OBJECTS: + case TOX_TABLES: + if (!pTOX->IsFromObjectNames()) + { + sStr = FieldString(eCode) + "\\c "; + const OUString& seqName = pTOX->GetSequenceName(); + if(!seqName.isEmpty()) + { + sStr += "\"" + seqName + sEntryEnd; + } + OUString aText; + int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText ); + if (1 == nRet) + sStr += "\\n "; + else if( 3 == nRet || 4 == nRet ) + { + sStr += "\\p \"" + aText + sEntryEnd; + } + } + break; + + case TOX_AUTHORITIES: + eCode = ww::eBIBLIOGRAPHY; + sStr = FieldString(eCode); + break; + // case TOX_USER: + // case TOX_CONTENT: + default: + { + sStr = FieldString(eCode); + + OUString sTOption; + sal_uInt16 n, nTOXLvl = pTOX->GetLevel(); + if( !nTOXLvl ) + ++nTOXLvl; + + if(SwTOXElement::TableLeader & pTOX->GetCreateType()) + { + sStr +="\\z " ; + GetExport( ).m_bHideTabLeaderAndPageNumbers = true ; + } + if(SwTOXElement::TableInToc & pTOX->GetCreateType()) + { + sStr +="\\w " ; + GetExport( ).m_bTabInTOC = true ; + } + if(SwTOXElement::Newline & pTOX->GetCreateType()) + { + sStr +="\\x " ; + } + if( SwTOXElement::Mark & pTOX->GetCreateType() ) + { + sStr += "\\f "; + + if( TOX_USER == pTOX->GetType() ) + { + sStr += "\"" + + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) )) + + sEntryEnd; + } + } + if(SwTOXElement::Bookmark & pTOX->GetCreateType()) + { + sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd; + } + + if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() ) + { + // Take the TOC value of the max level to evaluate to as + // the starting point for the \o flag, but reduce it to the + // value of the highest outline level filled by a *standard* + // Heading 1 - 9 style because \o "Builds a table of + // contents from paragraphs formatted with built-in heading + // styles". And afterward fill in any outline styles left + // uncovered by that range to the \t flag + + // i.e. for + // Heading 1 + // Heading 2 + // custom-style + // Heading 4 + // output + // \o 1-2 \tcustom-style,3,Heading 3,4 + + // Search over all the outline styles used and figure out + // what is the minimum outline level (if any) filled by a + // non-standard style for that level, i.e. ignore headline + // styles 1-9 and find the lowest valid outline level + sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL; + const SwTextFormatColls& rColls = *GetExport().m_pDoc->GetTextFormatColls(); + for( n = rColls.size(); n; ) + { + const SwTextFormatColl* pColl = rColls[ --n ]; + sal_uInt16 nPoolId = pColl->GetPoolFormatId(); + if ( + //Is a Non-Standard Outline Style + (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) && + //Has a valid outline level + (pColl->IsAssignedToListLevelOfOutlineStyle()) && + // Is less than the lowest known non-standard level + (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl) + ) + { + nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel()); + } + } + + sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl); + + //output \o 1-X where X is the highest normal outline style to be included in the toc + if ( nMaxMSAutoEvaluate ) + { + if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel) + nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel; + + sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd; + } + + //collect up any other styles in the writer TOC which will + //not already appear in the MS TOC and place then into the + //\t option + if( nMaxMSAutoEvaluate < nTOXLvl ) + { + // collect this templates into the \t option + for( n = rColls.size(); n;) + { + const SwTextFormatColl* pColl = rColls[ --n ]; + if (!pColl->IsAssignedToListLevelOfOutlineStyle()) + continue; + sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel()); + if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate) + { + if (!sTOption.isEmpty()) + sTOption += ","; + sTOption += pColl->GetName() + "," + OUString::number( nTestLvl + 1 ); + } + } + } + } + + if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() ) + { + sStr +="\\u " ; + } + + if( SwTOXElement::Template & pTOX->GetCreateType() ) + { + // #i99641# - Consider additional styles regardless of TOX-outlinelevel + for( n = 0; n < MAXLEVEL; ++n ) + { + const OUString& rStyles = pTOX->GetStyleNames( n ); + if( !rStyles.isEmpty() ) + { + sal_Int32 nPos = 0; + const OUString sLvl{ "," + OUString::number( n + 1 ) }; + do { + const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos )); + if( !sStyle.isEmpty() ) + { + SwTextFormatColl* pColl = GetExport().m_pDoc->FindTextFormatCollByName(sStyle); + if (pColl) + { + if (!pColl->IsAssignedToListLevelOfOutlineStyle() || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl) + { + if( !sTOption.isEmpty() ) + sTOption += ","; + sTOption += sStyle + sLvl; + } + } + } + } while( -1 != nPos ); + } + } + } + + // No 'else' branch; why the below snippet is a block I have no idea. + { + OUString aFillText; + sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL; + bool bFirstFillText = true, bOnlyText = true; + for( n = 0; n < nTOXLvl; ++n ) + { + OUString aText; + int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), + static_cast< sal_uInt8 >(n+1), aText ); + if( 1 == nRet ) + { + bOnlyText = false; + if( MAXLEVEL == nNoPgStt ) + nNoPgStt = static_cast< sal_uInt8 >(n+1); + } + else + { + if( MAXLEVEL != nNoPgStt && + MAXLEVEL == nNoPgEnd ) + nNoPgEnd = sal_uInt8(n); + + bOnlyText = bOnlyText && 3 == nRet; + if( 3 == nRet || 4 == nRet ) + { + if( bFirstFillText ) + aFillText = aText; + else if( aFillText != aText ) + aFillText.clear(); + bFirstFillText = false; + } + } + } + if( MAXLEVEL != nNoPgStt ) + { + if (WW8ListManager::nMaxLevel < nNoPgEnd) + nNoPgEnd = WW8ListManager::nMaxLevel; + sStr += "\\n " + + OUString::number( nNoPgStt ) + + "-" + + OUString::number( nNoPgEnd ) + + " "; + } + if( bOnlyText ) + { + sStr += "\\p \"" + aFillText + sEntryEnd; + } + } + + if( !sTOption.isEmpty() ) + { + sStr += "\\t \"" + sTOption + sEntryEnd; + } + + if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl)) + sStr += "\\h"; + break; + } + } + } + + if (!sStr.isEmpty()) + { + GetExport( ).m_bInWriteTOX = true; + if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) + { // tdf#129574: required for RTF; doesn't work with DOCX + StartRun(nullptr, -42, true); + } + GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart | + FieldFlags::CmdEnd ); + if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) + { + EndRun(nullptr, -42, true); + } + } + } + + GetExport( ).m_bStartTOX = false; +} + +void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd ) +{ + const SwTOXBase* pTOX = rSect.GetTOXBase(); + if ( pTOX ) + { + ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC; + GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close ); + + if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() ) + { + const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL ); + const SwColumns& rColumns = rCol.GetColumns(); + sal_Int32 nCol = rColumns.size(); + + if ( 0 < nCol ) + { + WW8_SepInfo rInfo( &GetExport( ).m_pDoc->GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ ); + GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo ); + } + } + } + GetExport( ).m_bInWriteTOX = false; + GetExport( ).m_bHideTabLeaderAndPageNumbers = false; + if (bCareEnd) + OnTOXEnding(); +} + +bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr) +{ + // Returns a date or time format string by using the US NfKeywordTable + bool bHasFormat = false; + SvNumberFormatter* pNFormatr = m_pDoc->GetNumberFormatter(); + sal_uInt32 nFormatIdx = rField.GetFormat(); + const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx ); + if( pNumFormat ) + { + LanguageType nLng = rField.GetLanguage(); + LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(), + LanguageTag(nLng)); + + OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(), + aLocDat)); + + if (!sFormat.isEmpty()) + { + sw::ms::SwapQuotesInField(sFormat); + + rStr = "\\@\"" + sFormat + "\" " ; + bHasFormat = true; + } + } + return bHasFormat; +} + +void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField ) +{ + switch(rField.GetFormat()) + { + case SVX_NUM_CHARS_UPPER_LETTER: + case SVX_NUM_CHARS_UPPER_LETTER_N: + rStr += "\\* ALPHABETIC "; + break; + case SVX_NUM_CHARS_LOWER_LETTER: + case SVX_NUM_CHARS_LOWER_LETTER_N: + rStr += "\\* alphabetic "; + break; + case SVX_NUM_ROMAN_UPPER: + rStr += "\\* ROMAN "; + break; + case SVX_NUM_ROMAN_LOWER: + rStr += "\\* roman "; + break; + default: + OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC, + "Unknown numbering type exported as default of Arabic"); + [[fallthrough]]; + case SVX_NUM_ARABIC: + rStr += "\\* ARABIC "; + break; + case SVX_NUM_PAGEDESC: + //Nothing, use word's default + break; + } +} + +void WW8Export::WritePostItBegin( ww::bytes* pOut ) +{ + sal_uInt8 aArr[ 3 ]; + sal_uInt8* pArr = aArr; + + // sprmCFSpec true + Set_UInt16( pArr, NS_sprm::sprmCFSpec ); + Set_UInt8( pArr, 1 ); + + m_pChpPlc->AppendFkpEntry( Strm().Tell() ); + WriteChar( 0x05 ); // Annotation reference + + if( pOut ) + pOut->insert( pOut->end(), aArr, pArr ); + else + m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr ); +} + +OUString FieldString(ww::eField eIndex) +{ + if (const char *pField = ww::GetEnglishFieldName(eIndex)) + return " " + OUString::createFromAscii(pField) + " "; + return " "; +} + +void WW8AttributeOutput::HiddenField( const SwField& rField ) +{ + //replace LF 0x0A with VT 0x0B + const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B)); + + m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell()); + SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false); + static sal_uInt8 aArr[] = + { + 0x3C, 0x08, 0x1 + }; + m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr); +} + +void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) +{ + const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField); + const OUString &rVar = pSet->GetPar2(); + + sal_uLong nFrom = m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell()); + + GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start | + FieldFlags::CmdStart | FieldFlags::CmdEnd); + + /* + Is there a bookmark at the start position of this field, if so + move it to the 0x14 of the result of the field. This is what word + does. MoveFieldMarks moves any bookmarks at this position to + the beginning of the field result, and marks the bookmark as a + fieldbookmark which is to be ended before the field end mark + instead of after it like a normal bookmark. + */ + m_rWW8Export.MoveFieldMarks(nFrom,m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell())); + + if (!rVar.isEmpty()) + { + SwWW8Writer::WriteString16(m_rWW8Export.Strm(), rVar, false); + } + GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close); +} + +void WW8AttributeOutput::PostitField( const SwField* pField ) +{ + const SwPostItField *pPField = static_cast<const SwPostItField*>(pField); + m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField ); + m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() ); +} + +bool WW8AttributeOutput::DropdownField( const SwField* pField ) +{ + const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField); + uno::Sequence<OUString> aItems = + rField2.GetItemSequence(); + GetExport().DoComboBox(rField2.GetName(), + rField2.GetHelp(), + rField2.GetToolTip(), + rField2.GetSelectedItem(), aItems); + return false; +} + +bool WW8AttributeOutput::PlaceholderField( const SwField* ) +{ + return true; // expand to text? +} + +void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef) +{ + const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " }; + m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Start | + FieldFlags::CmdStart | FieldFlags::CmdEnd ); + const OUString sVar = lcl_GetExpandedField( rField ); + if ( !sVar.isEmpty() ) + { + SwWW8Writer::WriteString16( m_rWW8Export.Strm(), sVar, false ); + } + m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Close ); +} + +void WW8AttributeOutput::WriteExpand( const SwField* pField ) +{ + SwWW8Writer::WriteString16( m_rWW8Export.Strm(), lcl_GetExpandedField( *pField ), false ); +} + +namespace +{ +// Escapes a token string for storing in Word formats. Its import counterpart +// is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx +OUString EscapeToken(const OUString& rCommand) +{ + bool bWasEscaped = false; + + const int nBufferLen = rCommand.getLength()*1.5; + OUStringBuffer sResult(nBufferLen); + sResult.append('"'); // opening quote + for (sal_Int32 i = 0; i < rCommand.getLength(); ++i) + { + sal_Unicode ch = rCommand[i]; + switch (ch) + { + case '\\': + case '"': + // Backslashes and doublequotes must be escaped + bWasEscaped = true; + sResult.append('\\'); + break; + case ' ': + // Spaces require quotation + bWasEscaped = true; + break; + } + sResult.append(ch); + } + + if (bWasEscaped) + { + sResult.append('"'); // closing quote + return sResult.makeStringAndClear(); + } + // No escapement/quotation was required + return rCommand; +} +} + +void AttributeOutputBase::TextField( const SwFormatField& rField ) +{ + const SwField* pField = rField.GetField(); + bool bWriteExpand = false; + const sal_uInt16 nSubType = pField->GetSubType(); + + switch (pField->GetTyp()->Which()) + { + case SwFieldIds::GetExp: + if (nSubType == nsSwGetSetExpType::GSE_STRING) + { + const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField); + RefField( *pGet, pGet->GetFormula() ); + } + else + bWriteExpand = true; + break; + case SwFieldIds::SetExp: + if (nsSwGetSetExpType::GSE_SEQ == nSubType) + { + OUString sStr; + if (GetExport().FieldsQuoted()) + sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " "; + else + sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" "; + GetNumberPara( sStr, *pField ); + GetExport().OutputField(pField, ww::eSEQ, sStr); + } + else if (nSubType & nsSwGetSetExpType::GSE_STRING) + { + bool bShowAsWell = false; + ww::eField eFieldNo; + const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField); + const OUString sVar = pSet->GetPar2(); + OUString sStr; + if (pSet->GetInputFlag()) + { + sStr = FieldString(ww::eASK) + "\"" + + pSet->GetPar1() + "\" " + + pSet->GetPromptText() + " \\d " + + sVar; + eFieldNo = ww::eASK; + } + else + { + sStr = FieldString(ww::eSET) + + pSet->GetPar1() + " \"" + + sVar + "\" "; + eFieldNo = ww::eSET; + bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0; + } + + SetField( *pField, eFieldNo, sStr ); + + if (bShowAsWell) + RefField( *pSet, pSet->GetPar1() ); + } + else + bWriteExpand = true; + break; + case SwFieldIds::PageNumber: + { + OUString sStr = FieldString(ww::ePAGE); + GetNumberPara(sStr, *pField); + GetExport().OutputField(pField, ww::ePAGE, sStr); + } + break; + case SwFieldIds::Filename: + { + OUString sStr = FieldString(ww::eFILENAME); + if (pField->GetFormat() == FF_PATHNAME) + sStr += "\\p "; + GetExport().OutputField(pField, ww::eFILENAME, sStr); + } + break; + case SwFieldIds::Database: + { + OUString sStr = FieldString(ww::eMERGEFIELD) + + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " "; + GetExport().OutputField(pField, ww::eMERGEFIELD, sStr); + } + break; + case SwFieldIds::DatabaseName: + { + SwDBData aData = GetExport().m_pDoc->GetDBData(); + const OUString sStr = FieldString(ww::eDATABASE) + + aData.sDataSource + + OUStringChar(DB_DELIM) + + aData.sCommand; + GetExport().OutputField(pField, ww::eDATABASE, sStr); + } + break; + case SwFieldIds::Author: + { + ww::eField eField = + ((AF_SHORTCUT & pField->GetFormat()) ? ww::eUSERINITIALS : ww::eUSERNAME); + GetExport().OutputField(pField, eField, FieldString(eField)); + } + break; + case SwFieldIds::TemplateName: + GetExport().OutputField(pField, ww::eTEMPLATE, FieldString(ww::eTEMPLATE)); + break; + case SwFieldIds::DocInfo: // Last printed, last edited,... + if( DI_SUB_FIXED & nSubType ) + bWriteExpand = true; + else + { + OUString sStr; + ww::eField eField(ww::eNONE); + switch (0xff & nSubType) + { + case DI_TITLE: + eField = ww::eTITLE; + break; + case DI_SUBJECT: + eField = ww::eSUBJECT; + break; + case DI_KEYS: + eField = ww::eKEYWORDS; + break; + case DI_COMMENT: + eField = ww::eCOMMENTS; + break; + case DI_DOCNO: + eField = ww::eREVNUM; + break; + case DI_CREATE: + if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK)) + eField = ww::eAUTHOR; + else if (GetExport().GetNumberFormat(*pField, sStr)) + eField = ww::eCREATEDATE; + break; + + case DI_CHANGE: + if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK)) + eField = ww::eLASTSAVEDBY; + else if (GetExport().GetNumberFormat(*pField, sStr)) + eField = ww::eSAVEDATE; + break; + + case DI_PRINT: + if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) && + GetExport().GetNumberFormat(*pField, sStr)) + eField = ww::ePRINTDATE; + break; + case DI_EDIT: + if( DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK ) && + GetExport().GetNumberFormat( *pField, sStr )) + eField = ww::eSAVEDATE; + else + eField = ww::eEDITTIME; + break; + case DI_CUSTOM: + eField = ww::eDOCPROPERTY; + { + const SwDocInfoField * pDocInfoField = + dynamic_cast<const SwDocInfoField *> (pField); + + if (pDocInfoField != nullptr) + { + OUString sFieldname = pDocInfoField->GetFieldName(); + + const sal_Int32 nIndex = sFieldname.indexOf(':'); + if (nIndex >= 0) + sFieldname = sFieldname.copy(nIndex + 1); + + sStr = "\"" + sFieldname + "\""; + } + } + break; + default: + break; + } + + if (eField != ww::eNONE) + { + GetExport().OutputField(pField, eField, FieldString(eField) + sStr); + } + else + bWriteExpand = true; + } + break; + case SwFieldIds::DateTime: + { + OUString sStr; + if (!GetExport().GetNumberFormat(*pField, sStr)) + bWriteExpand = true; + else + { + ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME; + GetExport().OutputField(pField, eField, FieldString(eField) + sStr); + } + } + break; + case SwFieldIds::DocStat: + { + ww::eField eField = ww::eNONE; + + switch (nSubType) + { + case DS_PAGE: + eField = ww::eNUMPAGES; + break; + case DS_WORD: + eField = ww::eNUMWORDS; + break; + case DS_CHAR: + eField = ww::eNUMCHARS; + break; + } + + if (eField != ww::eNONE) + { + OUString sStr = FieldString(eField); + GetNumberPara(sStr, *pField); + GetExport().OutputField(pField, eField, sStr); + } + else + bWriteExpand = true; + } + break; + case SwFieldIds::ExtUser: + { + ww::eField eField = ww::eNONE; + switch (0xFF & nSubType) + { + case EU_FIRSTNAME: + case EU_NAME: + eField = ww::eUSERNAME; + break; + case EU_SHORTCUT: + eField = ww::eUSERINITIALS; + break; + case EU_STREET: + case EU_COUNTRY: + case EU_ZIP: + case EU_CITY: + eField = ww::eUSERADDRESS; + break; + } + + if (eField != ww::eNONE) + { + GetExport().OutputField(pField, eField, FieldString(eField)); + } + else + bWriteExpand = true; + } + break; + case SwFieldIds::TableOfAuthorities: + { + OUString sRet(static_cast<SwAuthorityField const*>(pField) + ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr)); + // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed + // field instructions in here, but if the field doesn't originate + // from those filters it won't have that + if (!sRet.trim().startsWith("CITATION")) + { + sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\""; + } + GetExport().OutputField( pField, ww::eCITATION, sRet ); + } + break; + case SwFieldIds::Postit: + //Sadly only possible for word in main document text + if (GetExport().m_nTextTyp == TXT_MAINTEXT) + { + PostitField( pField ); + } + break; + case SwFieldIds::Input: + { + const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField); + + if (pInputField && pInputField->isFormField()) + GetExport().DoFormText(pInputField); + else + { + const OUString sStr = FieldString(ww::eFILLIN) + "\"" + + pField->GetPar2() + "\""; + + GetExport().OutputField(pField, ww::eFILLIN, sStr); + } + } + break; + case SwFieldIds::GetRef: + { + ww::eField eField = ww::eNONE; + OUString sStr; + const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField); + switch (nSubType) + { + case REF_SETREFATTR: + case REF_BOOKMARK: + switch (pField->GetFormat()) + { + case REF_PAGE_PGDESC: + case REF_PAGE: + eField = ww::ePAGEREF; + break; + default: + eField = ww::eREF; + break; + } + { + const OUString& aRefName(rRField.GetSetRefName()); + sStr = FieldString(eField) + + MSWordExportBase::GetBookmarkName(nSubType, &aRefName, 0); + } + switch (pField->GetFormat()) + { + case REF_NUMBER: + sStr += " \\r"; + break; + case REF_NUMBER_NO_CONTEXT: + sStr += " \\n"; + break; + case REF_NUMBER_FULL_CONTEXT: + sStr += " \\w"; + break; + } + break; + case REF_SEQUENCEFLD: + { + // Not implemented for RTF + if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) + break; + + switch (pField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + eField = ww::ePAGEREF; + break; + default: + eField = ww::eREF; + break; + } + // Generate a unique bookmark name + { + OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())}; + switch (pField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + case REF_CONTENT: + case REF_UPDOWN: + sName += "_full"; + break; + case REF_ONLYNUMBER: + sName += "_label_and_number"; + break; + case REF_ONLYCAPTION: + sName += "_caption_only"; + break; + case REF_ONLYSEQNO: + sName += "_number_only"; + break; + default: // Ignore other types of reference fields + eField = ww::eNONE; + break; + } + sStr = FieldString(eField) + MSWordExportBase::GetBookmarkName(nSubType, &sName, 0); + } + switch (pField->GetFormat()) + { + case REF_NUMBER: + sStr += " \\r"; + break; + case REF_NUMBER_NO_CONTEXT: + sStr += " \\n"; + break; + case REF_NUMBER_FULL_CONTEXT: + sStr += " \\w"; + break; + } + break; + } + case REF_FOOTNOTE: + case REF_ENDNOTE: + switch (pField->GetFormat()) + { + case REF_PAGE_PGDESC: + case REF_PAGE: + eField = ww::ePAGEREF; + break; + case REF_UPDOWN: + eField = ww::eREF; + break; + default: + eField = + REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF; + break; + } + sStr = FieldString(eField) + + MSWordExportBase::GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo()); + break; + } + + if (eField != ww::eNONE) + { + switch (pField->GetFormat()) + { + case REF_UPDOWN: + sStr += " \\p \\h "; // with hyperlink + break; + case REF_CHAPTER: + sStr += " \\n \\h "; // with hyperlink + break; + default: + sStr += " \\h "; // insert hyperlink + break; + } + GetExport().OutputField(pField, eField, sStr); + } + else + bWriteExpand = true; + } + break; + case SwFieldIds::CombinedChars: + { + /* + We need a font size to fill in the defaults, if these are overridden + (as they generally are) by character properties then those properties + win. + + The fontsize that is used in MS for determining the defaults is always + the CJK fontsize even if the text is not in that language, in OOo the + largest fontsize used in the field is the one we should take, but + whatever we do, word will actually render using the fontsize set for + CJK text. Nevertheless we attempt to guess whether the script is in + asian or western text based up on the first character and use the + font size of that script as our default. + */ + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0); + + long nHeight = static_cast<const SvxFontHeightItem&>((GetExport().GetItem( + GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript)))).GetHeight(); + + nHeight = (nHeight + 10) / 20; //Font Size in points; + + /* + Divide the combined char string into its up and down part. Get the + font size and fill in the defaults as up == half the font size and + down == a fifth the font size + */ + const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2; + const OUString sStr = FieldString(ww::eEQ) + + "\\o (\\s\\up " + + OUString::number(nHeight/2) + + "(" + + pField->GetPar1().copy(0, nAbove) + + "), \\s\\do " + + OUString::number(nHeight/5) + + "(" + + pField->GetPar1().copy(nAbove) + + "))"; + GetExport().OutputField(pField, ww::eEQ, sStr); + } + break; + case SwFieldIds::Dropdown: + bWriteExpand = DropdownField( pField ); + break; + case SwFieldIds::Chapter: + bWriteExpand = true; + if (GetExport().m_bOutKF && rField.GetTextField()) + { + const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot(); + if (!pTextNd) + { + pTextNd = GetExport().m_pCurPam->GetNode().GetTextNode(); + } + + if (pTextNd) + { + SwChapterField aCopy(*static_cast<const SwChapterField*>(pField)); + aCopy.ChangeExpansion(*pTextNd, false); + const OUString sStr = FieldString(ww::eSTYLEREF) + + " " + + OUString::number(aCopy.GetLevel() + 1) + + " \\* MERGEFORMAT "; + GetExport().OutputField(pField, ww::eSTYLEREF, sStr); + bWriteExpand = false; + } + } + break; + case SwFieldIds::HiddenText: + { + OUString sExpand(pField->GetPar2()); + if (!sExpand.isEmpty()) + { + HiddenField( *pField ); + } + } + break; + case SwFieldIds::JumpEdit: + bWriteExpand = PlaceholderField( pField ); + break; + case SwFieldIds::Macro: + { + const OUString sStr = " MACROBUTTON" + + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", " ") + + " " + + lcl_GetExpandedField(*pField); + GetExport().OutputField( pField, ww::eMACROBUTTON, sStr ); + } + break; + case SwFieldIds::Table: + { + ww::eField eField = ww::eEquals; + OUString aExpand = FieldString(eField) + pField->GetFieldName(); + GetExport().OutputField(pField, eField, aExpand); + } + break; + case SwFieldIds::User: + { + ww::eField eField = ww::eDOCVARIABLE; + OUString aExpand = FieldString(eField) + pField->GetPar1() + " "; + GetExport().OutputField(pField, eField, aExpand); + } + break; + default: + bWriteExpand = true; + break; + } + + if (bWriteExpand) + WriteExpand( pField ); +} + +void AttributeOutputBase::TextFlyContent( const SwFormatFlyCnt& rFlyContent ) +{ + if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) ) + { + Point const origin; + Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos(); + + SwPosition aPos( *pTextNd ); + ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), aPos ); + + OutputFlyFrame_Impl( aFrame, aLayPos ); + } +} + +// TOXMarks are still missing + +// WW allows detailed settings for hyphenation only for the whole document. +// One could implement following mimic: The values of the style "Standard" will +// be set in the Document Properties ( DOP ) if they exist. + +// ACK. This suggestion fits exactly to our implementation of the import, +// therefore I'll implement that right now. (KHZ, 07/15/2000) +void WW8AttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone ) +{ + // sprmPFNoAutoHyph + m_rWW8Export.InsUInt16( NS_sprm::sprmPFNoAutoHyph ); + + m_rWW8Export.pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 ); +} + +void WW8AttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmPFAutoSpaceDE ); + m_rWW8Export.pO->push_back( rScriptSpace.GetValue() ? 1 : 0 ); +} + +void WW8AttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmPFOverflowPunct ); + m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 ); +} + +void WW8AttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem ) +{ + m_rWW8Export.InsUInt16( NS_sprm::sprmPFKinsoku ); + m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 ); +} + +void WW8AttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid ) +{ + // sprmPFUsePgsuSettings + + m_rWW8Export.InsUInt16( NS_sprm::sprmPFUsePgsuSettings ); + m_rWW8Export.pO->push_back( rGrid.GetValue() ? 1 : 0 ); +} + +void WW8AttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign ) +{ + // sprmPWAlignFont + + m_rWW8Export.InsUInt16( NS_sprm::sprmPWAlignFont ); + + SvxParaVertAlignItem::Align nAlign = rAlign.GetValue(); + sal_uInt16 nVal; + switch ( nAlign ) + { + case SvxParaVertAlignItem::Align::Baseline: + nVal = 2; + break; + case SvxParaVertAlignItem::Align::Top: + nVal = 0; + break; + case SvxParaVertAlignItem::Align::Center: + nVal = 1; + break; + case SvxParaVertAlignItem::Align::Bottom: + nVal = 3; + break; + case SvxParaVertAlignItem::Align::Automatic: + nVal = 4; + break; + default: + nVal = 4; + OSL_FAIL( "Unknown vert alignment" ); + break; + } + m_rWW8Export.InsUInt16( nVal ); +} + +// NoHyphen: I didn't find an equal in the SW UI and WW UI + +// RefMark, NoLineBreakHere are still missing + +void WW8Export::WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pOutArr ) +{ + ww::bytes aAttrArr; + const bool bAutoNum = rFootnote.GetNumStr().isEmpty(); + if( bAutoNum ) + { + static const sal_uInt8 aSpec[] = + { + 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation + 0x55, 0x08, 1 // sprmCFSpec + }; + + aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec)); + } + + // sprmCIstd + const SwEndNoteInfo* pInfo; + if( rFootnote.IsEndNote() ) + pInfo = &m_pDoc->GetEndNoteInfo(); + else + pInfo = &m_pDoc->GetFootnoteInfo(); + const SwCharFormat* pCFormat = pOutArr + ? pInfo->GetAnchorCharFormat( *m_pDoc ) + : pInfo->GetCharFormat( *m_pDoc ); + SwWW8Writer::InsUInt16( aAttrArr, NS_sprm::sprmCIstd ); + SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) ); + + // fSpec-Attribut true + // For Auto-Number a special character must go + // into the text and therefore a fSpec attribute + m_pChpPlc->AppendFkpEntry( Strm().Tell() ); + if( bAutoNum ) + WriteChar( 0x02 ); // auto number character + else + // user numbering + OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength()); + + if( pOutArr ) + { + // insert at start of array, so the "hard" attribute overrule the + // attributes of the character template + pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() ); + } + else + { + std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes); + + // insert at start of array, so the "hard" attribute overrule the + // attributes of the character template + pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end()); + + // write for the ftn number in the content, the font of the anchor + const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote(); + if( pTextFootnote ) + { + std::unique_ptr<ww::bytes> pOld = std::move(pO); + pO = std::move(pOwnOutArr); + SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_FONT, + RES_CHRATR_FONT>{} ); + + pCFormat = pInfo->GetCharFormat( *m_pDoc ); + + pTextFootnote->GetTextNode().GetParaAttr(aSet, + pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true); + if (aSet.Count()) + { + m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) ); + } + else + { + m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) ); + } + pOwnOutArr = std::move(pO); + pO = std::move(pOld); + } + m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(), + pOwnOutArr->data() ); + } +} + +static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote) +{ + bool bRet = true; + if( rFootnote.GetTextFootnote() ) + { + sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND) + : sal_uInt16(RES_FTN_AT_TXTEND); + const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode(). + FindSectionNode(); + while( pSectNd && FTNEND_ATPGORDOCEND == + static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()-> + GetFormatAttr( nWh)).GetValue() ) + pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode(); + + if (!pSectNd) + bRet = false; // the is ftn/end collected at Page- or Doc-End + } + return bRet; +} + +void AttributeOutputBase::TextFootnote( const SwFormatFootnote& rFootnote ) +{ + sal_uInt16 nTyp; + if ( rFootnote.IsEndNote() ) + { + nTyp = REF_ENDNOTE; + if ( GetExport().m_bEndAtTextEnd ) + GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote ); + } + else + { + nTyp = REF_FOOTNOTE; + if ( GetExport().m_bFootnoteAtTextEnd ) + GetExport().m_bFootnoteAtTextEnd = lcl_IsAtTextEnd( rFootnote ); + } + + // if any reference to this footnote/endnote then insert an internal + // Bookmark. + OUString sBkmkNm; + if ( GetExport().HasRefToFootOrEndnote( rFootnote.IsEndNote(), rFootnote.GetTextFootnote()->GetSeqRefNo())) + { + sBkmkNm = MSWordExportBase::GetBookmarkName( nTyp, nullptr, + rFootnote.GetTextFootnote()->GetSeqRefNo() ); + GetExport().AppendBookmark( sBkmkNm ); + } + + TextFootnote_Impl( rFootnote ); + + if ( !sBkmkNm.isEmpty() ) + GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()? +} + +void WW8AttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote ) +{ + WW8_WrPlcFootnoteEdn* pFootnoteEnd; + if ( rFootnote.IsEndNote() || GetExport().m_pDoc->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER ) + pFootnoteEnd = m_rWW8Export.pEdn.get(); + else + pFootnoteEnd = m_rWW8Export.pFootnote.get(); + + pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote ); + m_rWW8Export.WriteFootnoteBegin( rFootnote, m_rWW8Export.pO.get() ); +} + +void WW8AttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat ) +{ + if( rCharFormat.GetCharFormat() ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCIstd ); + + m_rWW8Export.InsUInt16( m_rWW8Export.GetId( rCharFormat.GetCharFormat() ) ); + } +} + +/* + See ww8par6.cxx Read_DoubleLine for some more info + */ +void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines ) +{ + // #i28331# - check that bOn is set + if ( rTwoLines.GetValue() ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmCFELayout ); + m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6 + m_rWW8Export.pO->push_back( sal_uInt8(0x02) ); + + sal_Unicode cStart = rTwoLines.GetStartBracket(); + sal_Unicode cEnd = rTwoLines.GetEndBracket(); + + /* + As per usual we have problems. We can have separate left and right brackets + in OOo, it doesn't appear that you can in word. Also in word there appear + to only be a limited number of possibilities, we can use pretty much + anything. + + So if we have none, we export none, if either bracket is set to a known + word type we export both as that type (with the bracket winning out in + the case of a conflict simply being the order of test here. + + Upshot being a documented created in word will be reexported with no + ill effects. + */ + + sal_uInt16 nType; + if (!cStart && !cEnd) + nType = 0; + else if ((cStart == '{') || (cEnd == '}')) + nType = 4; + else if ((cStart == '<') || (cEnd == '>')) + nType = 3; + else if ((cStart == '[') || (cEnd == ']')) + nType = 2; + else + nType = 1; + m_rWW8Export.InsUInt16( nType ); + static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 }; + m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3); + } +} + +void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule ) +{ + const SwTextNode* pTextNd = nullptr; + if (rNumRule.GetValue().isEmpty()) + { + ParaNumRule_Impl(pTextNd, 0, 0); + return; + } + const SwNumRule* pRule = GetExport().m_pDoc->FindNumRulePtr( + rNumRule.GetValue() ); + if (!pRule) + return; + + sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1; + sal_uInt8 nLvl = 0; + + if (!GetExport().m_pOutFormatNode) + { + ParaNumRule_Impl(pTextNd, nLvl, nNumId); + return; + } + + if ( dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) != nullptr ) + { + pTextNd = static_cast<const SwTextNode*>(GetExport().m_pOutFormatNode); + + if( pTextNd->IsCountedInList()) + { + int nLevel = pTextNd->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + nLvl = static_cast< sal_uInt8 >(nLevel); + + if (GetExport().GetExportFormat() == MSWordExportBase::DOCX) // FIXME + { + // tdf#95848 find the abstract list definition + OUString const listId(pTextNd->GetListId()); + if (!listId.isEmpty() + && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping + || pTextNd->IsListRestart()) // or restarting previous list + ) + { + SwList const*const pList( + GetExport().m_pDoc->getIDocumentListsAccess().getListByName(listId)); + if (pList) + { + SwNumRule const*const pAbstractRule( + GetExport().m_pDoc->FindNumRulePtr( + pList->GetDefaultListStyleName())); + assert(pAbstractRule); + if (pAbstractRule == pRule && !pTextNd->IsListRestart()) + { + // different list, but no override + nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1; + } + else + { + nNumId = GetExport().OverrideNumRule( + *pRule, listId, *pAbstractRule) + 1; + + if (pTextNd->IsListRestart()) + { + // For restarted lists we should also keep value for + // future w:lvlOverride / w:startOverride + GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(), + pTextNd->GetActualListStartValue()); + } + } + } + } + } + } + else + { + // #i44815# adjust numbering for numbered paragraphs + // without number (NO_NUMLEVEL). These paragraphs + // will receive a list id 0, which WW interprets as + // 'no number'. + nNumId = 0; + } + } + else if ( dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) != nullptr ) + { + const SwTextFormatColl* pC = static_cast<const SwTextFormatColl*>(GetExport().m_pOutFormatNode); + if ( pC && pC->IsAssignedToListLevelOfOutlineStyle() ) + nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() ); + } + + if ( nLvl >= WW8ListManager::nMaxLevel ) + nLvl = WW8ListManager::nMaxLevel - 1; + + ParaNumRule_Impl( pTextNd, nLvl, nNumId); +} + +void WW8AttributeOutput::ParaNumRule_Impl(const SwTextNode* /*pTextNd*/, + sal_Int32 const nLvl, sal_Int32 const nNumId) +{ + if (USHRT_MAX == nNumId) + return; + + // write sprmPIlvl and sprmPIlfo + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlvl ); + m_rWW8Export.pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlfo ); + SwWW8Writer::InsUInt16( *m_rWW8Export.pO, ::sal::static_int_cast<sal_uInt16>(nNumId) ); +} + +/* File FRMATR.HXX */ + +void WW8AttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize ) +{ + if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys + { + if( m_rWW8Export.m_bOutGrf ) + return; // Fly around graphic -> Auto-size + + //???? What about percentages ??? + if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed) + { + //"sprmPDxaWidth" + m_rWW8Export.InsUInt16( NS_sprm::sprmPDxaWidth ); + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rSize.GetWidth()) ); + } + + if ( rSize.GetHeight() ) + { + // sprmPWHeightAbs + m_rWW8Export.InsUInt16( NS_sprm::sprmPWHeightAbs ); + + sal_uInt16 nH = 0; + switch ( rSize.GetHeightSizeType() ) + { + case SwFrameSize::Variable: break; + case SwFrameSize::Fixed: nH = static_cast<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break; + default: nH = static_cast<sal_uInt16>(rSize.GetHeight()) | 0x8000; break; + } + m_rWW8Export.InsUInt16( nH ); + } + } + else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height + { + if( m_rWW8Export.m_pCurrentPageDesc->GetLandscape() ) + { + /*sprmSBOrientation*/ + m_rWW8Export.InsUInt16( NS_sprm::sprmSBOrientation ); + m_rWW8Export.pO->push_back( 2 ); + } + + /*sprmSXaPage*/ + m_rWW8Export.InsUInt16( NS_sprm::sprmSXaPage ); + m_rWW8Export.InsUInt16( + msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth()))); + + /*sprmSYaPage*/ + m_rWW8Export.InsUInt16( NS_sprm::sprmSYaPage ); + m_rWW8Export.InsUInt16( + msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight()))); + } +} + +// FillOrder is still missing + +/** + * ReplaceCr() is used for Pagebreaks and Pagedescs. An already written CR + * will be replaced by a break character. Replace must be called right after + * the writing of CR. + * + * @return FilePos + 1 of the replaced CR or 0 if nothing was replaced. + */ +sal_uLong WW8Export::ReplaceCr( sal_uInt8 nChar ) +{ + OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" ); + + bool bReplaced = false; + SvStream& rStrm = Strm(); + sal_uLong nRetPos = 0, nPos = rStrm.Tell(); + //If there is at least two characters already output + if (nPos - 2 >= o3tl::make_unsigned(pFib->m_fcMin)) + { + sal_uInt16 nUCode=0; + + rStrm.SeekRel(-2); + rStrm.ReadUInt16( nUCode ); + //If the last char was a cr + if (nUCode == 0x0d) // CR ? + { + if ((nChar == 0x0c) && + (nPos - 4 >= o3tl::make_unsigned(pFib->m_fcMin))) + { + rStrm.SeekRel(-4); + rStrm.ReadUInt16( nUCode ); + } + else + { + rStrm.SeekRel(-2); + nUCode = 0x0; + } + //And the para is not of len 0, then replace this cr with the mark + //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check. + if( nUCode == 0x0d ) + bReplaced = false; + else + { + bReplaced = true; + WriteChar(nChar); + nRetPos = nPos; + } + } + else if ((nUCode == 0x0c) && (nChar == 0x0e)) + { + // a column break after a section has no effect in writer + bReplaced = true; + } + rStrm.Seek( nPos ); + } + else + bReplaced = true; + + if (!bReplaced) + { + // then write as normal char + WriteChar(nChar); + m_pPiece->SetParaBreak(); + m_pPapPlc->AppendFkpEntry(rStrm.Tell()); + m_pChpPlc->AppendFkpEntry(rStrm.Tell()); + nRetPos = rStrm.Tell(); + } + return nRetPos; +} + +void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth) +{ + if ( nDepth == 1 ) + m_rWW8Export.WriteChar( 0x07 ); + else if ( nDepth > 1 ) + m_rWW8Export.WriteChar( 0x0d ); + + //Technically in a word document this is a different value for a row ends + //that are not row ends directly after a cell with a graphic. But it + //doesn't seem to make a difference + //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6); +} + +void AttributeOutputBase::FormatPageDescription( const SwFormatPageDesc& rPageDesc ) +{ + if ( GetExport().m_bStyDef && dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) ) + { + const SwTextFormatColl* pC = static_cast<const SwTextFormatColl*>(GetExport().m_pOutFormatNode); + if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() ) + FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) ); + } +} + +void WW8AttributeOutput::PageBreakBefore( bool bBreak ) +{ + // sprmPPageBreakBefore/sprmPFPageBreakBefore + m_rWW8Export.InsUInt16( NS_sprm::sprmPFPageBreakBefore ); + + m_rWW8Export.pO->push_back( bBreak ? 1 : 0 ); +} + +/** + * breaks write nothing in the output field rWrt.pO, + * but only in the text stream (requirement so they can + * be called from Out_Break...) + */ +void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak ) +{ + if ( GetExport().m_bStyDef ) + { + switch ( rBreak.GetBreak() ) + { + case SvxBreak::NONE: + case SvxBreak::PageBefore: + case SvxBreak::PageBoth: + PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE ); + break; + default: + break; + } + } + else if ( !GetExport().m_pParentFrame ) + { + sal_uInt8 nC = 0; + bool bBefore = false; + // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>. + bool bCheckForFollowPageDesc = false; + + switch ( rBreak.GetBreak() ) + { + case SvxBreak::NONE: // disabled + if ( !GetExport().m_bBreakBefore ) + PageBreakBefore( false ); + return; + + case SvxBreak::ColumnBefore: // ColumnBreak + bBefore = true; + [[fallthrough]]; + case SvxBreak::ColumnAfter: + case SvxBreak::ColumnBoth: + if ( GetExport().m_pDoc->getIDocumentSettingAccess().get( DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK ) + || GetExport().Sections().CurrentNumberOfColumns( *GetExport().m_pDoc ) > 1 ) + { + nC = msword::ColumnBreak; + } + break; + + case SvxBreak::PageBefore: // PageBreak + // From now on(fix for #i77900#) we prefer to save a page break + // as paragraph attribute (if the exporter is OK with that), + // this has to be done after the export of the paragraph ( => + // !GetExport().bBreakBefore ) + if (GetExport().PreferPageBreakBefore()) + { + if (!GetExport().m_bBreakBefore) + PageBreakBefore(true); + } + else + { + bBefore = true; + nC = msword::PageBreak; + } + break; + case SvxBreak::PageAfter: + case SvxBreak::PageBoth: + nC = msword::PageBreak; + // #i76300# - check for follow page description, + // if current writing attributes of a paragraph. + if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) && + GetExport().GetCurItemSet() ) + { + bCheckForFollowPageDesc = true; + } + break; + + default: + break; + } + + if ( ( bBefore == GetExport().m_bBreakBefore ) && nC ) + { + // #i76300# + bool bFollowPageDescWritten = false; + if ( bCheckForFollowPageDesc ) + { + bFollowPageDescWritten = + GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(), + dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) ); + } + if ( !bFollowPageDescWritten ) + { + SectionBreak(nC, !bBefore); + } + } + } +} + +void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/ ) +{ + m_rWW8Export.ReplaceCr( nC ); +} + +sal_uInt32 AttributeOutputBase::GridCharacterPitch( const SwTextGridItem& rGrid ) const +{ + MSWordStyles * pStyles = GetExport().m_pStyles.get(); + const SwFormat * pSwFormat = pStyles->GetSwFormat(0); + + sal_uInt32 nPageCharSize = 0; + + if (pSwFormat != nullptr) + { + nPageCharSize = ItemGet<SvxFontHeightItem> + (*pSwFormat, RES_CHRATR_FONTSIZE).GetHeight(); + } + sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() : + rGrid.GetBaseWidth( ); + + sal_Int32 nCharWidth = nPitch - nPageCharSize; + sal_Int32 nFraction = nCharWidth % 20; + if ( nCharWidth < 0 ) + nFraction = 20 + nFraction; + nFraction = ( nFraction * 0xFFF ) / 20; + nFraction = ( nFraction & 0x00000FFF ); + + sal_Int32 nMain = nCharWidth / 20; + if ( nCharWidth < 0 ) + nMain -= 1; + nMain = nMain * 0x1000; + nMain = ( nMain & 0xFFFFF000 ); + + return sal_uInt32( nFraction + nMain ); +} + +void WW8AttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid ) +{ + if (m_rWW8Export.m_bOutPageDescs) + { + sal_uInt16 nGridType = 0; + switch ( rGrid.GetGridType() ) + { + default: + OSL_FAIL("Unknown grid type"); + [[fallthrough]]; + case GRID_NONE: + nGridType = 0; + break; + case GRID_LINES_ONLY: + nGridType = 2; + break; + case GRID_LINES_CHARS: + if ( rGrid.IsSnapToChars() ) + nGridType = 3; + else + nGridType = 1; + break; + } + m_rWW8Export.InsUInt16( NS_sprm::sprmSClm ); + m_rWW8Export.InsUInt16( nGridType ); + + sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight(); + m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaLinePitch ); + m_rWW8Export.InsUInt16( nHeight ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxtCharSpace ); + m_rWW8Export.InsUInt32( GridCharacterPitch( rGrid ) ); + } +} + +void WW8AttributeOutput::FormatPaperBin( const SvxPaperBinItem& rPaperBin ) +{ + if ( m_rWW8Export.m_bOutPageDescs ) + { + sal_uInt16 nVal; + switch ( rPaperBin.GetValue() ) + { + case 0: nVal = 15; break; // Automatically select + case 1: nVal = 1; break; // Upper paper tray + case 2: nVal = 4; break; // Manual paper feed + default: nVal = 0; break; + } + + if ( nVal ) + { + m_rWW8Export.InsUInt16( m_rWW8Export.m_bOutFirstPage + ? NS_sprm::sprmSDmBinFirst : NS_sprm::sprmSDmBinOther ); + + m_rWW8Export.InsUInt16( nVal ); + } + } +} + +void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR ) +{ + // Flys are still missing ( see RTF ) + + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys + { + // sprmPDxaFromText10 + m_rWW8Export.InsUInt16( NS_sprm::LN_PDxaFromText10 ); + // use average, since WW only knows one value + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) ); + } + else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs + { + m_pageMargins.nLeft = 0; + m_pageMargins.nRight = 0; + + if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rWW8Export.HasItem( RES_BOX )) ) + { + m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true ); + m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true ); + } + + m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft()); + m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight()); + + // sprmSDxaLeft + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaLeft ); + m_rWW8Export.InsUInt16( m_pageMargins.nLeft ); + + // sprmSDxaRight + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaRight ); + m_rWW8Export.InsUInt16( m_pageMargins.nRight ); + } + else + { // normal paragraphs + // sprmPDxaLeft + m_rWW8Export.InsUInt16( 0x845E ); //asian version ? + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetTextLeft()) ); + + // sprmPDxaRight + m_rWW8Export.InsUInt16( 0x845D ); //asian version ? + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetRight()) ); + + // sprmPDxaLeft1 + m_rWW8Export.InsUInt16( 0x8460 ); //asian version ? + m_rWW8Export.InsUInt16( rLR.GetTextFirstLineOffset() ); + } +} + +void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL ) +{ + // Flys are still missing ( see RTF ) + + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys + { + // sprmPDyaFromText + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaFromText ); + // use average, since WW only knows one value + m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) ); + } + else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL + { + OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" ); + if ( !m_rWW8Export.GetCurItemSet() ) + return; + + HdFtDistanceGlue aDistances( *m_rWW8Export.GetCurItemSet() ); + + if ( aDistances.HasHeader() ) + { + //sprmSDyaHdrTop + m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaHdrTop ); + m_rWW8Export.InsUInt16( aDistances.dyaHdrTop ); + } + + // sprmSDyaTop + m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaTop ); + m_rWW8Export.InsUInt16( aDistances.dyaTop ); + m_pageMargins.nTop = aDistances.dyaTop; + + if ( aDistances.HasFooter() ) + { + //sprmSDyaHdrBottom + m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaHdrBottom ); + m_rWW8Export.InsUInt16( aDistances.dyaHdrBottom ); + } + + //sprmSDyaBottom + m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaBottom ); + m_rWW8Export.InsUInt16( aDistances.dyaBottom ); + m_pageMargins.nBottom = aDistances.dyaBottom; + } + else + { + // sprmPDyaBefore + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaBefore ); + m_rWW8Export.InsUInt16( rUL.GetUpper() ); + // sprmPDyaAfter + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaAfter ); + m_rWW8Export.InsUInt16( rUL.GetLower() ); + // sprmPFContextualSpacing + if (rUL.GetContext()) + { + m_rWW8Export.InsUInt16(NS_sprm::sprmPFContextualSpacing); + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) ); + } + } +} + +// print, opaque, protect are still missing + +void WW8AttributeOutput::FormatSurround( const SwFormatSurround& rSurround ) +{ + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPWr ); + + m_rWW8Export.pO->push_back( + ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 ); + } +} + +void WW8AttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert ) +{ + + //!!!! anchor type and corresponding borders are still missing + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) + { + short nPos; + switch( rFlyVert.GetVertOrient() ) + { + case text::VertOrientation::NONE: + nPos = static_cast<short>(rFlyVert.GetPos()); + break; + case text::VertOrientation::CENTER: + case text::VertOrientation::LINE_CENTER: + nPos = -8; + break; + case text::VertOrientation::BOTTOM: + case text::VertOrientation::LINE_BOTTOM: + nPos = -12; + break; + case text::VertOrientation::TOP: + case text::VertOrientation::LINE_TOP: + default: + nPos = -4; + break; + } + + // sprmPDyaAbs + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaAbs ); + m_rWW8Export.InsUInt16( nPos ); + } +} + +void WW8AttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori ) +{ + if ( !m_rWW8Export.m_pParentFrame ) + { + OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" ); + return; + } + + //!!!! anchor type and corresponding borders are still missing + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) + { + short nPos; + switch( rFlyHori.GetHoriOrient() ) + { + case text::HoriOrientation::NONE: + nPos = static_cast<short>(rFlyHori.GetPos()); + if( !nPos ) + nPos = 1; // WW: 0 is reserved + break; + case text::HoriOrientation::LEFT: + nPos = rFlyHori.IsPosToggle() ? -12 : 0; + break; + case text::HoriOrientation::RIGHT: + nPos = rFlyHori.IsPosToggle() ? -16 : -8; + break; + case text::HoriOrientation::CENTER: + case text::HoriOrientation::FULL: // FULL only for tables + default: + nPos = -4; + break; + } + + // sprmPDxaAbs + m_rWW8Export.InsUInt16( NS_sprm::sprmPDxaAbs ); + m_rWW8Export.InsUInt16( nPos ); + } +} + +void WW8AttributeOutput::FormatAnchor( const SwFormatAnchor& rAnchor ) +{ + OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" ); + + if ( m_rWW8Export.m_bOutFlyFrameAttrs ) + { + sal_uInt8 nP = 0; + switch ( rAnchor.GetAnchorId() ) + { + case RndStdIds::FLY_AT_PAGE: + // vertical: page | horizontal: page + nP |= (1 << 4) | (2 << 6); + break; + // in case of Fly as characters: set paragraph-bound!!! + case RndStdIds::FLY_AT_FLY: + case RndStdIds::FLY_AT_CHAR: + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AS_CHAR: + // vertical: page | horizontal: page + nP |= (2 << 4) | (0 << 6); + break; + default: + break; + } + + // sprmPPc + m_rWW8Export.InsUInt16( NS_sprm::sprmPPc ); + m_rWW8Export.pO->push_back( nP ); + } +} + +void WW8AttributeOutput::FormatBackground( const SvxBrushItem& rBrush ) +{ + // WW cannot have background in a section + if ( !m_rWW8Export.m_bOutPageDescs ) + { + WW8_SHD aSHD; + WW8Export::TransBrush( rBrush.GetColor(), aSHD ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmPShd80 ); + m_rWW8Export.InsUInt16( aSHD.GetValue() ); + + m_rWW8Export.InsUInt16( NS_sprm::sprmPShd ); + m_rWW8Export.pO->push_back( 10 ); //size of operand: MUST be 10 + m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto + m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack + m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background + } +} + +void WW8AttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle ) +{ + // WW cannot have background in a section + if ( !m_rWW8Export.m_bOutPageDescs ) + { + // see MSWordExportBase::OutputItemSet for how _SOLID is handled + if ( rFillStyle.GetValue() == drawing::FillStyle_NONE ) + { + //Shd80Nil + m_rWW8Export.InsUInt16( NS_sprm::sprmPShd80 ); + m_rWW8Export.InsUInt16( 0xffff ); + + //cvAuto + m_rWW8Export.InsUInt16( NS_sprm::sprmPShd ); + m_rWW8Export.pO->push_back( 10 ); + m_rWW8Export.InsUInt32( 0xFF000000 ); + m_rWW8Export.InsUInt32( 0xFF000000 ); + m_rWW8Export.InsUInt16( 0x0000 ); + } + } +} + +void WW8AttributeOutput::FormatFillGradient( const XFillGradientItem& /*rFillGradient*/ ) +{ +} + +WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine, + sal_uInt16 nDist, bool bShadow) +{ + sal_uInt32 nColBGR = 0; + sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord( + rLine.GetBorderLineStyle(), rLine.GetWidth()); + sal_uInt8 brcType = 0; + + if( nWidth ) // line ? + { + // BRC.brcType + brcType = 0; + // All the border types values are available on + // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx + switch (rLine.GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + { + if ( rLine.GetWidth( ) == DEF_LINE_WIDTH_0 ) + brcType = 5; + else + brcType = 1; + } + break; + case SvxBorderLineStyle::DOTTED: + brcType = 6; + break; + case SvxBorderLineStyle::DASHED: + brcType = 7; + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::DOUBLE_THIN: + brcType = 3; + break; + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + brcType = 11; + break; + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + brcType = 14; + break; + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + brcType = 17; + break; + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + brcType = 12; + break; + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + brcType = 15; + break; + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + brcType = 18; + break; + case SvxBorderLineStyle::EMBOSSED: + brcType = 24; + break; + case SvxBorderLineStyle::ENGRAVED: + brcType = 25; + break; + case SvxBorderLineStyle::OUTSET: + brcType = 26; + break; + case SvxBorderLineStyle::INSET: + brcType = 27; + break; + case SvxBorderLineStyle::FINE_DASHED: + brcType = 22; + break; + case SvxBorderLineStyle::DASH_DOT: + brcType = 8; + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + brcType = 9; + break; + default: + break; + } + + // convert width from twips (1/20 pt) to eighths of a point + nWidth = (( nWidth * 8 ) + 10 ) / 20; + if( 0xff < nWidth ) + nWidth = 0xff; + + if( 0 == nWidth ) // really thin line + nWidth = 1; // don't omit + + // BRC.cv + nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor()); + } + + // BRC.dptSpace + sal_uInt16 nLDist = nDist; + nLDist /= 20; // unit of measurement: pt + if( nLDist > 0x1f ) + nLDist = 0x1f; + + return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist), + bShadow, false); +} + +/** + * Gets passed a WW8Bytes*, so the function also can be used for the table border. + * + * @param nSprmNo If nSprmNo == 0, then the opcode isn't outputted. + * @param bShadow SHOULDN'T be set for table cells ! + */ +void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine, + sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow) +{ + OSL_ENSURE( ( nSprmNo == 0 ) || + ( nSprmNo >= 38 && nSprmNo <= 41 ) || + ( nSprmNo >= NS_sprm::sprmPBrcTop80 + && nSprmNo <= NS_sprm::sprmPBrcRight80 ) || + ( nSprmNo >= NS_sprm::sprmSBrcTop80 + && nSprmNo <= NS_sprm::sprmSBrcRight80 ), + "Sprm for border out is of range" ); + + WW8_BRCVer9 aBrcVer9; + WW8_BRC aBrcVer8; + + if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE ) + { + aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow ); + sal_uInt8 ico = msfilter::util::TransColToIco( msfilter::util::BGRToRGB(aBrcVer9.cv()) ); + aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico, + aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() ); + } + + // WW97-SprmIds + if ( nSprmNo != 0 ) + SwWW8Writer::InsUInt16( rO, nSprmNo ); + + rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 ); + + if ( nSprmNoVer9 != 0 ) + { + SwWW8Writer::InsUInt16( rO, nSprmNoVer9 ); + rO.push_back(sizeof(WW8_BRCVer9)); + rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4); + } +} + +/** + * is for all boxes except in tables. pO of the WW8Writer is used + * + * @param rBox + * @param bShadow + */ +void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow) +{ + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + static const sal_uInt16 aPBrc[] = + { + // WW8 SPRMs + NS_sprm::sprmPBrcTop80, NS_sprm::sprmPBrcLeft80, + NS_sprm::sprmPBrcBottom80, NS_sprm::sprmPBrcRight80, + // WW9 SPRMs + NS_sprm::sprmPBrcTop, NS_sprm::sprmPBrcLeft, + NS_sprm::sprmPBrcBottom, NS_sprm::sprmPBrcRight + }; + static const sal_uInt16 aSBrc[] = + { + // WW8 SPRMs + NS_sprm::sprmSBrcTop80, NS_sprm::sprmSBrcLeft80, + NS_sprm::sprmSBrcBottom80, NS_sprm::sprmSBrcRight80, + // WW9 SPRMs + NS_sprm::sprmSBrcTop, NS_sprm::sprmSBrcLeft, + NS_sprm::sprmSBrcBottom, NS_sprm::sprmSBrcRight + }; + + const SvxBoxItemLine* pBrd = aBorders; + for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd ) + { + const SvxBorderLine* pLn = rBox.GetLine( *pBrd ); + + sal_uInt16 nSprmNo, nSprmNoVer9 = 0; + if (m_bOutPageDescs) + { + nSprmNo = aSBrc[i]; + nSprmNoVer9 = aSBrc[i+4]; + } + else + { + nSprmNo = aPBrc[i]; + nSprmNoVer9 = aPBrc[i+4]; + } + + Out_BorderLine( *pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo, + nSprmNoVer9, bShadow ); + } +} + +/** + * FormatBox2() is for TC structures in tables. The Sprm opcode isn't written + * because it is packed into the TC structure without opcode. + * dxpSpace always becomes 0, because WW requires that in tables + * ( table borders otherwise will fray ) + * + * @param rO A WW8Bytes pointer is passed in as output parameter + */ +void WW8Export::Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * pBox ) +{ + // possible and maybe better would be 0xffff + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + static const SvxBorderLine aBorderLine; + + for(const SvxBoxItemLine & rBorder : aBorders) + { + const SvxBorderLine* pLn; + if (pBox != nullptr) + pLn = pBox->GetLine( rBorder ); + else + pLn = & aBorderLine; + + Out_BorderLine(rO, pLn, 0, 0, 0, false); + } +} + +void WW8Export::Out_CellRangeBorders( const SvxBoxItem * pBox, sal_uInt8 nStart, + sal_uInt8 nLimit ) +{ + if ( !pBox ) + return; + + static const SvxBoxItemLine aBorders[] = + { + SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT + }; + + for( int i = 0; i < 4; ++i ) + { + const SvxBorderLine* pLn = pBox->GetLine( aBorders[i] ); + if (!pLn) + continue; + + InsUInt16( NS_sprm::sprmTSetBrc ); + pO->push_back( 11 ); + pO->push_back( nStart ); + pO->push_back( nLimit ); + pO->push_back( 1<<i ); + WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false ); + pO->insert( pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 ); + } +} + +void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox ) +{ + // Fly around graphic -> here no border, because the + // graphics header already has the border + if ( !m_rWW8Export.m_bOutGrf ) + { + bool bShadow = false; + const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_SHADOW ); + if ( pItem ) + { + const SvxShadowItem* p = static_cast<const SvxShadowItem*>(pItem); + bShadow = ( p->GetLocation() != SvxShadowLocation::NONE ) + && ( p->GetWidth() != 0 ); + } + + SvxBoxItem aBox(rBox); + if (m_rWW8Export.m_bOutPageDescs) + { + editeng::WordBorderDistances aDistances; + editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances); + + aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP); + aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT); + aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM); + aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT); + + m_bFromEdge = aDistances.bFromEdge; + } + + m_rWW8Export.Out_SwFormatBox( aBox, bShadow ); + } +} + +SwTwips WW8Export::CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const +{ + const SwFrameFormat* pFormat = m_pCurrentPageDesc ? &m_pCurrentPageDesc->GetMaster() + : &m_pDoc->GetPageDesc(0).GetMaster(); + + const SvxLRSpaceItem& rLR = pFormat->GetLRSpace(); + SwTwips nPageSize = pFormat->GetFrameSize().GetWidth(); + rLeft = rLR.GetLeft(); + rRight = rLR.GetRight(); + return nPageSize; +} + +void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) +{ + // CColumns + m_rWW8Export.InsUInt16( NS_sprm::sprmSCcolumns ); + m_rWW8Export.InsUInt16( nCols - 1 ); + + // DxaColumns + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColumns ); + m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) ); + + // LBetween + m_rWW8Export.InsUInt16( NS_sprm::sprmSLBetween ); + m_rWW8Export.pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 ); + + const SwColumns & rColumns = rCol.GetColumns( ); + + // FEvenlySpaced + m_rWW8Export.InsUInt16( NS_sprm::sprmSFEvenlySpaced ); + m_rWW8Export.pO->push_back( bEven ? 1 : 0 ); + + if ( !bEven ) + { + for ( sal_uInt16 n = 0; n < nCols; ++n ) + { + //sprmSDxaColWidth + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColWidth ); + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) ); + m_rWW8Export.InsUInt16( rCol. + CalcPrtColWidth( n, + static_cast<sal_uInt16>(nPageSize) ) ); + + if ( n + 1 != nCols ) + { + //sprmSDxaColSpacing + m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColSpacing ); + m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) ); + m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) + + rColumns[n + 1].GetLeft( ) ); + } + } + } +} + +void AttributeOutputBase::FormatColumns( const SwFormatCol& rCol ) +{ + const SwColumns& rColumns = rCol.GetColumns(); + + sal_uInt16 nCols = rColumns.size(); + if ( 1 < nCols && !GetExport( ).m_bOutFlyFrameAttrs ) + { + // get the page width without borders !! + + const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc *>(GetExport( ).m_pDoc)->GetPageDesc(0).GetMaster(); + const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir(); + SwTwips nPageSize; + if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB ) + { + const SvxULSpaceItem &rUL = pFormat->GetULSpace(); + nPageSize = pFormat->GetFrameSize().GetHeight(); + nPageSize -= rUL.GetUpper() + rUL.GetLower(); + + const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER); + if ( header ) + { + const SwFrameFormat *headerFormat = header->GetHeaderFormat(); + if (headerFormat) + { + nPageSize -= headerFormat->GetFrameSize().GetHeight(); + } + } + const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER); + if ( footer ) + { + const SwFrameFormat *footerFormat = footer->GetFooterFormat(); + if ( footerFormat ) + { + nPageSize -= footerFormat->GetFrameSize().GetHeight(); + } + } + } + else + { + const SvxLRSpaceItem &rLR = pFormat->GetLRSpace(); + nPageSize = pFormat->GetFrameSize().GetWidth(); + nPageSize -= rLR.GetLeft() + rLR.GetRight(); + //i120133: The Section width should consider page indent value. + nPageSize -= rCol.GetAdjustValue(); + + } + + // look if all columns are equal + bool bEven = true; + sal_uInt16 n; + sal_uInt16 nColWidth = rCol.CalcPrtColWidth( 0, static_cast<sal_uInt16>(nPageSize) ); + for ( n = 1; n < nCols; n++ ) + { + short nDiff = nColWidth - + rCol.CalcPrtColWidth( n, static_cast<sal_uInt16>(nPageSize) ); + + if ( nDiff > 10 || nDiff < -10 ) // Tolerance: 10 tw + { + bEven = false; + break; + } + } + + FormatColumns_Impl( nCols, rCol, bEven, nPageSize ); + } +} + +// "Paragraphs together" +void WW8AttributeOutput::FormatKeep( const SvxFormatKeepItem& rKeep ) +{ + // sprmFKeepFollow + m_rWW8Export.InsUInt16( NS_sprm::sprmPFKeepFollow ); + + m_rWW8Export.pO->push_back( rKeep.GetValue() ? 1 : 0 ); +} + +// exclude a paragraph from Line Numbering +void WW8AttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering ) +{ + // sprmPFNoLineNumb + m_rWW8Export.InsUInt16( NS_sprm::sprmPFNoLineNumb ); + + m_rWW8Export.pO->push_back( rNumbering.IsCount() ? 0 : 1 ); +} + +/* File PARATR.HXX */ + +void WW8AttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti ) +{ + // sprmPDyaLine + m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaLine ); + + m_rWW8Export.InsUInt16( nSpace ); + m_rWW8Export.InsUInt16( nMulti ); +} + +void AttributeOutputBase::ParaLineSpacing( const SvxLineSpacingItem& rSpacing ) +{ + short nSpace = 240, nMulti = 0; + + switch ( rSpacing.GetLineSpaceRule() ) + { + default: + break; + case SvxLineSpaceRule::Fix: // Fix + nSpace = -static_cast<short>(rSpacing.GetLineHeight()); + break; + case SvxLineSpaceRule::Min: // At least + nSpace = static_cast<short>(rSpacing.GetLineHeight()); + break; + case SvxLineSpaceRule::Auto: + { + if( rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) // Leading + { + // doesn't exist in WW - how do you get the MaxLineHeight? + nSpace = rSpacing.GetInterLineSpace(); + sal_uInt16 nScript = + i18n::ScriptType::LATIN; + const SwAttrSet *pSet = nullptr; + if ( auto pFormat = dynamic_cast< const SwFormat *>( GetExport().m_pOutFormatNode ) ) + { + pSet = &pFormat->GetAttrSet(); + } + else if ( auto pNd = dynamic_cast< const SwTextNode *>( GetExport().m_pOutFormatNode ) ) + { + pSet = &pNd->GetSwAttrSet(); + nScript = g_pBreakIt->GetBreakIter()->getScriptType(pNd->GetText(), 0); + } + OSL_ENSURE( pSet, "No attrset for lineheight :-(" ); + if ( pSet ) + { + nSpace = nSpace + static_cast<short>( AttrSetToLineHeight( GetExport().m_pDoc->getIDocumentSettingAccess(), + *pSet, *Application::GetDefaultDevice(), nScript ) ); + } + } + else // Proportional + { + if ( rSpacing.GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off ) + nSpace = static_cast<short>( ( 240L * rSpacing.GetPropLineSpace() ) / 100L ); + nMulti = 1; + } + } + break; + } + // if nSpace is negative, it is a fixed size in 1/20 of a point + // if nSpace is positive and nMulti is 1, it is 1/240 of a single line height + // otherwise, it is a minimum size in 1/20 of a point + ParaLineSpacing_Impl( nSpace, nMulti ); +} + +void WW8AttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust ) +{ + // sprmPJc + sal_uInt8 nAdj; + sal_uInt8 nAdjBiDi; + switch ( rAdjust.GetAdjust() ) + { + case SvxAdjust::Left: + nAdj = 0; + nAdjBiDi = 2; + break; + case SvxAdjust::Right: + nAdj = 2; + nAdjBiDi = 0; + break; + case SvxAdjust::BlockLine: + case SvxAdjust::Block: + nAdj = nAdjBiDi = 3; + break; + case SvxAdjust::Center: + nAdj = nAdjBiDi = 1; + break; + default: + return; // not a supported Attribute + } + + m_rWW8Export.InsUInt16(NS_sprm::sprmPJc80); + m_rWW8Export.pO->push_back(nAdj); + + /* + Sadly for left to right paragraphs both these values are the same, + for right to left paragraphs the bidi one is the reverse of the + normal one. + */ + m_rWW8Export.InsUInt16(NS_sprm::sprmPJc); //bidi version ? + bool bBiDiSwap = false; + if (m_rWW8Export.m_pOutFormatNode) + { + SvxFrameDirection nDirection = SvxFrameDirection::Horizontal_LR_TB; + if (dynamic_cast<const SwTextNode*>(m_rWW8Export.m_pOutFormatNode) != nullptr) + { + SwPosition aPos(*static_cast<const SwContentNode*>(m_rWW8Export.m_pOutFormatNode)); + nDirection = m_rWW8Export.m_pDoc->GetTextDirection(aPos); + } + else if (dynamic_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode) != nullptr) + { + const SwTextFormatColl* pC = + static_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode); + const SvxFrameDirectionItem &rItem = + ItemGet<SvxFrameDirectionItem>(*pC, RES_FRAMEDIR); + nDirection = rItem.GetValue(); + } + if ( ( nDirection == SvxFrameDirection::Horizontal_RL_TB ) || + ( nDirection == SvxFrameDirection::Environment && AllSettings::GetLayoutRTL() ) ) + { + bBiDiSwap = true; + } + } + + if (bBiDiSwap) + m_rWW8Export.pO->push_back(nAdjBiDi); + else + m_rWW8Export.pO->push_back(nAdj); +} + +void WW8AttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection ) +{ + sal_uInt16 nTextFlow=0; + bool bBiDi = false; + SvxFrameDirection nDir = rDirection.GetValue(); + + if ( nDir == SvxFrameDirection::Environment ) + nDir = GetExport( ).GetDefaultFrameDirection( ); + + + switch ( nDir ) + { + default: + //Can't get an unknown type here + OSL_FAIL("Unknown frame direction"); + [[fallthrough]]; + case SvxFrameDirection::Horizontal_LR_TB: + nTextFlow = 0; + break; + case SvxFrameDirection::Horizontal_RL_TB: + nTextFlow = 0; + bBiDi = true; + break; + case SvxFrameDirection::Vertical_LR_TB: //word doesn't have this + case SvxFrameDirection::Vertical_RL_TB: + nTextFlow = 1; + break; + } + + if ( m_rWW8Export.m_bOutPageDescs ) + { + m_rWW8Export.InsUInt16( NS_sprm::sprmSTextFlow ); + m_rWW8Export.InsUInt16( nTextFlow ); + m_rWW8Export.InsUInt16( NS_sprm::sprmSFBiDi ); + m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 ); + } + else if ( !m_rWW8Export.m_bOutFlyFrameAttrs ) //paragraph/style + { + m_rWW8Export.InsUInt16( NS_sprm::sprmPFBiDi ); + m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 ); + } +} + +void WW8AttributeOutput::ParaGrabBag(const SfxGrabBagItem& /*rItem*/) +{ +} + +void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/) +{ +} + +void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/) +{ +} + +// "Separate paragraphs" +void WW8AttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit ) +{ + // sprmPFKeep + m_rWW8Export.InsUInt16( NS_sprm::sprmPFKeep ); + m_rWW8Export.pO->push_back( rSplit.GetValue() ? 0 : 1 ); +} + +/** + * Only convert the item "SvxWidowItem" and not the orphans, because + * in WW only one attribute "paragraph control" exists for both and + * in SW probably both or none is set by the user. + */ +void WW8AttributeOutput::ParaWidows( const SvxWidowsItem& rWidows ) +{ + // sprmPFWidowControl + m_rWW8Export.InsUInt16( NS_sprm::sprmPFWidowControl ); + m_rWW8Export.pO->push_back( rWidows.GetValue() ? 1 : 0 ); +} + +namespace { + +class SwWW8WrTabu +{ + std::unique_ptr<sal_uInt8[]> pDel; // DelArray + std::unique_ptr<sal_uInt8[]> pAddPos; // AddPos-Array + std::unique_ptr<sal_uInt8[]> pAddTyp; // AddTyp-Array + sal_uInt16 nAdd; // number of tabs to be added + sal_uInt16 nDel; // number of tabs to be deleted + + SwWW8WrTabu(const SwWW8WrTabu&) = delete; + SwWW8WrTabu& operator=(const SwWW8WrTabu&) = delete; + +public: + SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax); + + void Add(const SvxTabStop &rTS, long nAdjustment); + void Del(const SvxTabStop &rTS, long nAdjustment); + void PutAll(WW8Export& rWW8Wrt); +}; + +} + +SwWW8WrTabu::SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax) + : nAdd(0), nDel(0) +{ + if (nDelMax) + pDel.reset( new sal_uInt8[nDelMax * 2] ); + pAddPos.reset( new sal_uInt8[nAddMax * 2] ); + pAddTyp.reset( new sal_uInt8[nAddMax] ); +} + +/** + * insert a tab in the WW structure + */ +void SwWW8WrTabu::Add(const SvxTabStop & rTS, long nAdjustment) +{ + // insert tab position + ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment), + pAddPos.get() + (nAdd * 2)); + + // insert tab type + sal_uInt8 nPara = 0; + switch (rTS.GetAdjustment()) + { + case SvxTabAdjust::Right: + nPara = 2; + break; + case SvxTabAdjust::Center: + nPara = 1; + break; + case SvxTabAdjust::Decimal: + /* + There is nothing we can do btw the decimal separator has been + customized, but if you think different remember that different + locales have different separators, i.e. german is a , while english + is a . + */ + nPara = 3; + break; + default: + break; + } + + switch( rTS.GetFill() ) + { + case '.': // dotted leader + nPara |= 1 << 3; + break; + case '_': // Single line leader + nPara |= 3 << 3; + break; + case '-': // hyphenated leader + nPara |= 2 << 3; + break; + case '=': // heavy line leader + nPara |= 4 << 3; + break; + } + + pAddTyp[nAdd] = nPara; + ++nAdd; +} + +/** + * Insert a to be deleted tab in the WW structure + */ +void SwWW8WrTabu::Del(const SvxTabStop &rTS, long nAdjustment) +{ + // insert tab position + ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment), + pDel.get() + (nDel * 2)); + ++nDel; +} + +/** + * Writes the attribute to rWrt.pO + */ +void SwWW8WrTabu::PutAll(WW8Export& rWrt) +{ + if (!nAdd && !nDel) //If it's a no-op + return; + OSL_ENSURE(nAdd <= 255, "more than 255 added tabstops?"); + OSL_ENSURE(nDel <= 255, "more than 244 removed tabstops?"); + if (nAdd > 255) + nAdd = 255; + if (nDel > 255) + nDel = 255; + + sal_uInt16 nSiz = 2 * nDel + 3 * nAdd + 2; + if (nSiz > 255) + nSiz = 255; + + rWrt.InsUInt16(NS_sprm::sprmPChgTabsPapx); + // insert cch + rWrt.pO->push_back(msword_cast<sal_uInt8>(nSiz)); + // write DelArr + rWrt.pO->push_back(msword_cast<sal_uInt8>(nDel)); + rWrt.OutSprmBytes(pDel.get(), nDel * 2); + // write InsArr + rWrt.pO->push_back(msword_cast<sal_uInt8>(nAdd)); + rWrt.OutSprmBytes(pAddPos.get(), 2 * nAdd); // AddPosArray + rWrt.OutSprmBytes(pAddTyp.get(), nAdd); // AddTypArray +} + +static void ParaTabStopAdd( WW8Export& rWrt, + const SvxTabStopItem& rTStops, + const long nLParaMgn ) +{ + SwWW8WrTabu aTab( 0, rTStops.Count()); + + for( sal_uInt16 n = 0; n < rTStops.Count(); n++ ) + { + const SvxTabStop& rTS = rTStops[n]; + // ignore default tabs + if (SvxTabAdjust::Default != rTS.GetAdjustment()) + aTab.Add(rTS, nLParaMgn); + } + aTab.PutAll( rWrt ); +} + +static bool lcl_IsEqual(long nOneLeft, const SvxTabStop &rOne, + long nTwoLeft, const SvxTabStop &rTwo) +{ + return( + nOneLeft == nTwoLeft && + rOne.GetAdjustment() == rTwo.GetAdjustment() && + rOne.GetDecimal() == rTwo.GetDecimal() && + rOne.GetFill() == rTwo.GetFill() + ); +} + +static void ParaTabStopDelAdd( WW8Export& rWrt, + const SvxTabStopItem& rTStyle, + const long nLStypeMgn, + const SvxTabStopItem& rTNew, + const long nLParaMgn ) +{ + SwWW8WrTabu aTab(rTStyle.Count(), rTNew.Count()); + + sal_uInt16 nO = 0; // rTStyle Index + sal_uInt16 nN = 0; // rTNew Index + + do { + const SvxTabStop* pTO; + long nOP; + if( nO < rTStyle.Count() ) // old not yet at the end? + { + pTO = &rTStyle[ nO ]; + nOP = pTO->GetTabPos() + nLStypeMgn; + if( SvxTabAdjust::Default == pTO->GetAdjustment() ) + { + nO++; // ignore default tab + continue; + } + } + else + { + pTO = nullptr; + nOP = LONG_MAX; + } + + const SvxTabStop* pTN; + long nNP; + if( nN < rTNew.Count() ) // new not yet at the end + { + pTN = &rTNew[ nN ]; + nNP = pTN->GetTabPos() + nLParaMgn; + if( SvxTabAdjust::Default == pTN->GetAdjustment() ) + { + nN++; // ignore default tab + continue; + } + } + else + { + pTN = nullptr; + nNP = LONG_MAX; + } + + if( nOP == LONG_MAX && nNP == LONG_MAX ) + break; // everything done + + if( nOP < nNP ) // next tab is old + { + assert(pTO); + aTab.Del(*pTO, nLStypeMgn); // must be deleted + nO++; + } + else if( nNP < nOP ) // next tab is new + { + assert(pTN); + aTab.Add(*pTN, nLParaMgn); // must be inserted + nN++; + } + else if (lcl_IsEqual(nOP, *pTO, nNP, *pTN)) // tabs are equal + { + nO++; // nothing to do + nN++; + } + else // tabs same position, different type + { + aTab.Del(*pTO, nLStypeMgn); // delete old one + aTab.Add(*pTN, nLParaMgn); // insert new one + nO++; + nN++; + } + } while( true ); + + aTab.PutAll( rWrt ); +} + +void WW8AttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStops ) +{ + const bool bTabsRelativeToIndex = m_rWW8Export.m_pCurPam->GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::TABS_RELATIVE_TO_INDENT ); + + long nCurrentLeft = 0; + if ( bTabsRelativeToIndex ) + { + const SfxPoolItem* pLR = m_rWW8Export.HasItem( RES_LR_SPACE ); + + if ( pLR != nullptr ) + nCurrentLeft = static_cast<const SvxLRSpaceItem*>(pLR)->GetTextLeft(); + } + + // #i100264# + if ( m_rWW8Export.m_bStyDef && + m_rWW8Export.m_pCurrentStyle != nullptr && + m_rWW8Export.m_pCurrentStyle->DerivedFrom() != nullptr ) + { + SvxTabStopItem aParentTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + const SwFormat *pParentStyle = m_rWW8Export.m_pCurrentStyle->DerivedFrom(); + { + if (const SvxTabStopItem* pParentTabs = pParentStyle->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP)) + { + aParentTabs.Insert( pParentTabs ); + } + } + + // #i120938# - consider left indentation of style and its parent style + long nParentLeft = 0; + if ( bTabsRelativeToIndex ) + { + const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>( pParentStyle->GetAttrSet(), RES_LR_SPACE ); + nParentLeft = rStyleLR.GetTextLeft(); + } + + ParaTabStopDelAdd( m_rWW8Export, aParentTabs, nParentLeft, rTabStops, nCurrentLeft ); + return; + } + + const SvxTabStopItem* pStyleTabs = nullptr; + if ( !m_rWW8Export.m_bStyDef && m_rWW8Export.m_pStyAttr ) + { + pStyleTabs = m_rWW8Export.m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP); + } + + if ( !pStyleTabs ) + { + ParaTabStopAdd(m_rWW8Export, rTabStops, nCurrentLeft); + } + else + { + long nStyleLeft = 0; + if ( bTabsRelativeToIndex ) + { + const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>(*m_rWW8Export.m_pStyAttr, RES_LR_SPACE); + nStyleLeft = rStyleLR.GetTextLeft(); + } + + ParaTabStopDelAdd( m_rWW8Export, + *pStyleTabs, nStyleLeft, + rTabStops, nCurrentLeft); + } +} + +void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt ) +{ + // FIXME maybe use 'item_cast', like 'item_cast<SvxCharHiddenItem>( rHt )'? + switch ( rHt.Which() ) + { + case RES_CHRATR_CASEMAP: + CharCaseMap( static_cast< const SvxCaseMapItem& >( rHt ) ); + break; + case RES_CHRATR_COLOR: + CharColor( static_cast< const SvxColorItem& >( rHt ) ); + break; + case RES_CHRATR_CONTOUR: + CharContour( static_cast< const SvxContourItem& >( rHt ) ); + break; + case RES_CHRATR_CROSSEDOUT: + CharCrossedOut( static_cast< const SvxCrossedOutItem& >( rHt ) ); + break; + case RES_CHRATR_ESCAPEMENT: + CharEscapement( static_cast< const SvxEscapementItem& >( rHt ) ); + break; + case RES_CHRATR_FONT: + CharFont( static_cast< const SvxFontItem& >( rHt ) ); + break; + case RES_CHRATR_FONTSIZE: + CharFontSize( static_cast< const SvxFontHeightItem& >( rHt ) ); + break; + case RES_CHRATR_KERNING: + CharKerning( static_cast< const SvxKerningItem& >( rHt ) ); + break; + case RES_CHRATR_LANGUAGE: + CharLanguage( static_cast< const SvxLanguageItem& >( rHt ) ); + break; + case RES_CHRATR_POSTURE: + CharPosture( static_cast< const SvxPostureItem& >( rHt ) ); + break; + case RES_CHRATR_SHADOWED: + CharShadow( static_cast< const SvxShadowedItem& >( rHt ) ); + break; + case RES_CHRATR_UNDERLINE: + CharUnderline( static_cast< const SvxUnderlineItem& >( rHt ) ); + break; + case RES_CHRATR_WEIGHT: + CharWeight( static_cast< const SvxWeightItem& >( rHt ) ); + break; + case RES_CHRATR_AUTOKERN: + CharAutoKern( static_cast< const SvxAutoKernItem& >( rHt ) ); + break; + case RES_CHRATR_BLINK: + CharAnimatedText( static_cast< const SvxBlinkItem& >( rHt ) ); + break; + case RES_CHRATR_BACKGROUND: + CharBackgroundBase( static_cast< const SvxBrushItem& >( rHt ) ); + break; + + case RES_CHRATR_CJK_FONT: + CharFontCJK( static_cast< const SvxFontItem& >( rHt ) ); + break; + case RES_CHRATR_CJK_FONTSIZE: + CharFontSizeCJK( static_cast< const SvxFontHeightItem& >( rHt ) ); + break; + case RES_CHRATR_CJK_LANGUAGE: + CharLanguageCJK( static_cast< const SvxLanguageItem& >( rHt ) ); + break; + case RES_CHRATR_CJK_POSTURE: + CharPostureCJK( static_cast< const SvxPostureItem& >( rHt ) ); + break; + case RES_CHRATR_CJK_WEIGHT: + CharWeightCJK( static_cast< const SvxWeightItem& >( rHt ) ); + break; + + case RES_CHRATR_CTL_FONT: + CharFontCTL( static_cast< const SvxFontItem& >( rHt ) ); + break; + case RES_CHRATR_CTL_FONTSIZE: + CharFontSizeCTL( static_cast< const SvxFontHeightItem& >( rHt ) ); + break; + case RES_CHRATR_CTL_LANGUAGE: + CharLanguageCTL( static_cast< const SvxLanguageItem& >( rHt ) ); + break; + case RES_CHRATR_CTL_POSTURE: + CharPostureCTL( static_cast< const SvxPostureItem& >( rHt ) ); + break; + case RES_CHRATR_CTL_WEIGHT: + CharWeightCTL( static_cast< const SvxWeightItem& >( rHt ) ); + break; + + case RES_CHRATR_ROTATE: + CharRotate( static_cast< const SvxCharRotateItem& >( rHt ) ); + break; + case RES_CHRATR_EMPHASIS_MARK: + CharEmphasisMark( static_cast< const SvxEmphasisMarkItem& >( rHt ) ); + break; + case RES_CHRATR_TWO_LINES: + CharTwoLines( static_cast< const SvxTwoLinesItem& >( rHt ) ); + break; + case RES_CHRATR_SCALEW: + CharScaleWidth( static_cast< const SvxCharScaleWidthItem& >( rHt ) ); + break; + case RES_CHRATR_RELIEF: + CharRelief( static_cast< const SvxCharReliefItem& >( rHt ) ); + break; + case RES_CHRATR_HIDDEN: + CharHidden( static_cast< const SvxCharHiddenItem& >( rHt ) ); + break; + case RES_CHRATR_BOX: + FormatCharBorder( static_cast< const SvxBoxItem& >( rHt ) ); + break; + case RES_CHRATR_HIGHLIGHT: + CharHighlight( static_cast< const SvxBrushItem& >( rHt ) ); + break; + case RES_CHRATR_BIDIRTL: + CharBidiRTL( rHt ); + break; + case RES_CHRATR_IDCTHINT: + CharIdctHint( rHt ); + break; + case RES_TXTATR_INETFMT: + TextINetFormat( static_cast< const SwFormatINetFormat& >( rHt ) ); + break; + case RES_TXTATR_CHARFMT: + TextCharFormat( static_cast< const SwFormatCharFormat& >( rHt ) ); + break; + + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + TextField( static_cast< const SwFormatField& >( rHt ) ); + break; + + case RES_TXTATR_FLYCNT: + TextFlyContent( static_cast< const SwFormatFlyCnt& >( rHt ) ); + break; + case RES_TXTATR_FTN: + TextFootnote( static_cast< const SwFormatFootnote& >( rHt ) ); + break; + + case RES_PARATR_LINESPACING: + ParaLineSpacing( static_cast< const SvxLineSpacingItem& >( rHt ) ); + break; + case RES_PARATR_ADJUST: + ParaAdjust( static_cast< const SvxAdjustItem& >( rHt ) ); + break; + case RES_PARATR_SPLIT: + ParaSplit( static_cast< const SvxFormatSplitItem& >( rHt ) ); + break; + case RES_PARATR_WIDOWS: + ParaWidows( static_cast< const SvxWidowsItem& >( rHt ) ); + break; + case RES_PARATR_TABSTOP: + ParaTabStop( static_cast< const SvxTabStopItem& >( rHt ) ); + break; + case RES_PARATR_HYPHENZONE: + ParaHyphenZone( static_cast< const SvxHyphenZoneItem& >( rHt ) ); + break; + case RES_PARATR_NUMRULE: + ParaNumRule( static_cast< const SwNumRuleItem& >( rHt ) ); + break; + case RES_PARATR_SCRIPTSPACE: + ParaScriptSpace( static_cast< const SfxBoolItem& >( rHt ) ); + break; + case RES_PARATR_HANGINGPUNCTUATION: + ParaHangingPunctuation( static_cast< const SfxBoolItem& >( rHt ) ); + break; + case RES_PARATR_FORBIDDEN_RULES: + ParaForbiddenRules( static_cast< const SfxBoolItem& >( rHt ) ); + break; + case RES_PARATR_VERTALIGN: + ParaVerticalAlign( static_cast< const SvxParaVertAlignItem& >( rHt ) ); + break; + case RES_PARATR_SNAPTOGRID: + ParaSnapToGrid( static_cast< const SvxParaGridItem& >( rHt ) ); + break; + + case RES_FRM_SIZE: + FormatFrameSize( static_cast< const SwFormatFrameSize& >( rHt ) ); + break; + case RES_PAPER_BIN: + FormatPaperBin( static_cast< const SvxPaperBinItem& >( rHt ) ); + break; + case RES_LR_SPACE: + FormatLRSpace( static_cast< const SvxLRSpaceItem& >( rHt ) ); + break; + case RES_UL_SPACE: + FormatULSpace( static_cast< const SvxULSpaceItem& >( rHt ) ); + break; + case RES_PAGEDESC: + FormatPageDescription( static_cast< const SwFormatPageDesc& >( rHt ) ); + break; + case RES_BREAK: + FormatBreak( static_cast< const SvxFormatBreakItem& >( rHt ) ); + break; + case RES_SURROUND: + FormatSurround( static_cast< const SwFormatSurround& >( rHt ) ); + break; + case RES_VERT_ORIENT: + FormatVertOrientation( static_cast< const SwFormatVertOrient& >( rHt ) ); + break; + case RES_HORI_ORIENT: + FormatHorizOrientation( static_cast< const SwFormatHoriOrient& >( rHt ) ); + break; + case RES_ANCHOR: + FormatAnchor( static_cast< const SwFormatAnchor& >( rHt ) ); + break; + case RES_BACKGROUND: + FormatBackground( static_cast< const SvxBrushItem& >( rHt ) ); + break; + case XATTR_FILLSTYLE: + FormatFillStyle( static_cast< const XFillStyleItem& >( rHt ) ); + break; + case XATTR_FILLGRADIENT: + FormatFillGradient( static_cast< const XFillGradientItem& >( rHt ) ); + break; + case RES_BOX: + FormatBox( static_cast< const SvxBoxItem& >( rHt ) ); + break; + case RES_COL: + FormatColumns( static_cast< const SwFormatCol& >( rHt ) ); + break; + case RES_KEEP: + FormatKeep( static_cast< const SvxFormatKeepItem& >( rHt ) ); + break; + case RES_TEXTGRID: + FormatTextGrid( static_cast< const SwTextGridItem& >( rHt ) ); + break; + case RES_LINENUMBER: + FormatLineNumbering( static_cast< const SwFormatLineNumber& >( rHt ) ); + break; + case RES_FRAMEDIR: + FormatFrameDirection( static_cast< const SvxFrameDirectionItem& >( rHt ) ); + break; + case RES_PARATR_GRABBAG: + ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt)); + break; + case RES_PARATR_OUTLINELEVEL: + ParaOutlineLevel(static_cast<const SfxUInt16Item&>(rHt)); + break; + case RES_CHRATR_GRABBAG: + CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt)); + break; + + default: + SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() ); + break; + } +} + +void AttributeOutputBase::OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault ) +{ + // based on OutputItemSet() from wrt_fn.cxx + + const SfxItemPool& rPool = *rSet.GetPool(); + const SfxItemSet* pSet = &rSet; + if ( !pSet->Count() ) + { + while ( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() ) + ; + + if ( !pSet ) + return; + } + + const SfxPoolItem* pItem; + if ( !pSet->GetParent() ) + { + assert(rSet.Count() && "Was already handled or?"); + SfxItemIter aIter( *pSet ); + pItem = aIter.GetCurItem(); + do { + OutputItem( *pItem ); + } while ((pItem = aIter.NextItem())); + } + else + { + SfxWhichIter aIter( *pSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + if ( SfxItemState::SET == pSet->GetItemState( nWhich, true/*bDeep*/, &pItem ) && + ( !bTestForDefault || + nWhich == RES_UL_SPACE || + nWhich == RES_LR_SPACE || + *pItem != rPool.GetDefaultItem( nWhich ) || + ( pSet->GetParent() && *pItem != pSet->GetParent()->Get( nWhich ) ) ) ) + { + OutputItem( *pItem ); + } + nWhich = aIter.NextWhich(); + } + } +} + +void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox ) +{ + // Get one of the borders (if there is any border then in docx also will be) + const SvxBorderLine* pBorderLine = nullptr; + sal_uInt16 nDist = 0; + if( rBox.GetTop() ) + { + pBorderLine = rBox.GetTop(); + nDist = rBox.GetDistance( SvxBoxItemLine::TOP ); + } + else if( rBox.GetLeft() ) + { + pBorderLine = rBox.GetLeft(); + nDist = rBox.GetDistance( SvxBoxItemLine::LEFT ); + } + else if( rBox.GetBottom() ) + { + pBorderLine = rBox.GetBottom(); + nDist = rBox.GetDistance( SvxBoxItemLine::BOTTOM ); + } + else if( rBox.GetRight() ) + { + pBorderLine = rBox.GetRight(); + nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT ); + } + + // RTF: avoid regressions since RTF doesn't know how to export a border_NONE style-override + if( pBorderLine || GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF ) + { + const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW ); + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + const bool bShadow = pBorderLine && + pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE && + pShadowItem->GetWidth() > 0; + + CharBorder( pBorderLine, nDist, bShadow ); + } +} + +/* + * This function is used to check if the current SwTextNode (paragraph) has a redline object + * that is attached to the paragraph marker. + * This is done by checking if the range (SwPaM) of the redline is : + * - Start = the last character of the current paragraph + * - End = the first character of the next paragraph + */ +const SwRedlineData* AttributeOutputBase::GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType) +{ + // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos' + for(SwRangeRedline* pRedl : GetExport().m_pDoc->getIDocumentRedlineAccess().GetRedlineTable()) + { + // Only check redlines that are of type 'Delete' + if ( pRedl->GetRedlineData().GetType() != aRedlineType ) + continue; + + sal_uLong uStartNodeIndex = pRedl->Start()->nNode.GetIndex(); + sal_uLong uEndNodeIndex = pRedl->End()->nNode.GetIndex(); + sal_uLong uNodeIndex = rNode.GetIndex(); + + if( uStartNodeIndex <= uNodeIndex && uNodeIndex < uEndNodeIndex ) + return &( pRedl->GetRedlineData() ); + } + return nullptr; +} + +void AttributeOutputBase::CharBackgroundBase( const SvxBrushItem& rBrush ) +{ + bool bConvertToShading = SvtFilterOptions::Get().IsCharBackground2Shading(); + bool bHasShadingMarker = false; + + // Check shading marker + const SfxPoolItem* pItem = GetExport().HasItem(RES_CHRATR_GRABBAG); + if( pItem ) + { + const SfxGrabBagItem aGrabBag = static_cast< const SfxGrabBagItem& >(*pItem); + const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if( aIterator != rMap.end() ) + { + aIterator->second >>= bHasShadingMarker; + } + } + + if( bConvertToShading || bHasShadingMarker ) + { + CharBackground(rBrush); + } + else + { + CharHighlight(rBrush); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx new file mode 100644 index 000000000..ac4e931ec --- /dev/null +++ b/sw/source/filter/ww8/ww8attributeoutput.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX + +#include "attributeoutputbase.hxx" +#include "wrtww8.hxx" +#include <editeng/boxitem.hxx> +#include <sfx2/docfile.hxx> + +class WW8AttributeOutput : public AttributeOutputBase +{ +public: + /// Export the state of RTL/CJK. + virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override; + + /// Start of the paragraph. + virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/ ) override {} + + /// End of the paragraph. + virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override; + + /// Called in order to output section breaks. + virtual void SectionBreaks(const SwNode& /*rNode*/) override {} + + /// Called before we start outputting the attributes. + virtual void StartParagraphProperties() override {} + + /// Called after we end outputting the attributes. + virtual void EndParagraphProperties(const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/, const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/, const SwRedlineData* /*pRedlineParagraphMarkerInserted*/) override {} + + /// Empty paragraph. + virtual void EmptyParagraph() override; + + /// Start of the text run. + /// + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override; + + virtual void OnTOXEnding() override; + + /// End of the text run. + /// + /// No-op for binary filters. + virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; + + /// Before we start outputting the attributes. + virtual void StartRunProperties() override; + + /// After we end outputting the attributes. + virtual void EndRunProperties( const SwRedlineData* pRedlineData ) override; + + /// Output text. + virtual void RunText( const OUString& rText, rtl_TextEncoding eCharSet = RTL_TEXTENCODING_UTF8 ) override; + + /// Output text (without markup). + virtual void RawText(const OUString& rText, rtl_TextEncoding eCharSet) override; + + /// Output ruby start. + virtual void StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby ) override; + + /// Output ruby end. + virtual void EndRuby(const SwTextNode& rNode, sal_Int32 nPos) override; + + /// Output URL start. + virtual bool StartURL( const OUString &rUrl, const OUString &rTarget ) override; + + /// Output URL end. + virtual bool EndURL(bool) override; + + virtual void FieldVanish( const OUString& rText, ww::eField eType ) override; + + /// Output redlining. + virtual void Redline( const SwRedlineData* pRedline ) override; + + virtual void FormatDrop( const SwTextNode& rNode, const SwFormatDrop &rSwFormatDrop, sal_uInt16 nStyle, ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override; + + /// Output FKP (Formatted disK Page) - necessary for binary formats only. + /// FIXME having it in AttributeOutputBase is probably a hack, it + /// should be in WW8AttributeOutput only... + virtual void OutputFKP(bool bForce) override; + + /// Output style. + virtual void ParagraphStyle( sal_uInt16 nStyle ) override; + + virtual void TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfo ) override; + virtual void TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner ) override; + virtual void TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ) override; + virtual void TableRowEnd( sal_uInt32 nDepth ) override; + + /// Start of the styles table. + virtual void StartStyles() override; + + /// End of the styles table. + virtual void EndStyles( sal_uInt16 nNumberOfStyles ) override; + + /// Write default style. + virtual void DefaultStyle() override; + + /// Start of a style in the styles table. + virtual void StartStyle( const OUString& rName, StyleType eType, + sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwIdi, sal_uInt16 nId, + bool bAutoUpdate ) override; + + /// End of a style in the styles table. + virtual void EndStyle() override; + + /// Start of (paragraph or run) properties of a style. + virtual void StartStyleProperties( bool bParProp, sal_uInt16 nStyle ) override; + + /// End of (paragraph or run) properties of a style. + virtual void EndStyleProperties( bool bParProp ) override; + + /// Numbering rule and Id. + virtual void OutlineNumbering(sal_uInt8 nLvl) override; + + /// Page break + /// As a paragraph property - the paragraph should be on the next page. + virtual void PageBreakBefore( bool bBreak ) override; + + /// Write a section break + /// msword::ColumnBreak or msword::PageBreak + virtual void SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo = nullptr ) override; + + // preserve DOC page vertical alignment + virtual void TextVerticalAdjustment( const css::drawing::TextVerticalAdjust ) override; + + /// Start of the section properties. + virtual void StartSection() override; + + // footnote / endnote section properties + virtual void SectFootnoteEndnotePr() override; + + /// End of the section properties. + /// + /// No-op for binary filters. + virtual void EndSection() override {} + + /// Protection of forms. + virtual void SectionFormProtection( bool bProtected ) override; + + /// Numbering of the lines in the document. + virtual void SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo ) override; + + /// Has different headers/footers for the title page. + virtual void SectionTitlePage() override; + + /// Description of the page borders. + virtual void SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* pFirstPageFormat ) override; + + /// Columns populated from right/numbers on the right side? + virtual void SectionBiDi( bool bBiDi ) override; + + /// The style of the page numbers. + /// + virtual void SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber ) override; + + /// The type of breaking. + virtual void SectionType( sal_uInt8 nBreakCode ) override; + + /// Definition of a numbering instance. + virtual void NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) override; + + /// All the numbering level information. + virtual void NumberingLevel( sal_uInt8 nLevel, + sal_uInt16 nStart, + sal_uInt16 nNumberingType, + SvxAdjust eAdjust, + const sal_uInt8 *pNumLvlPos, + sal_uInt8 nFollow, + const wwFont *pFont, + const SfxItemSet *pOutSet, + sal_Int16 nIndentAt, + sal_Int16 nFirstLineIndex, + sal_Int16 nListTabPos, + const OUString &rNumberingString, + const SvxBrushItem* pBrush) override; //For i120928,transfer graphic of bullet + +protected: + /// Output frames - the implementation. + void OutputFlyFrame_Impl( const ww8::Frame& rFormat, const Point& rNdTopLeft ) override; + + /// Sfx item Sfx item RES_CHRATR_CASEMAP + virtual void CharCaseMap( const SvxCaseMapItem& ) override; + + /// Sfx item Sfx item RES_CHRATR_COLOR + virtual void CharColor( const SvxColorItem& ) override; + + /// Sfx item Sfx item RES_CHRATR_CONTOUR + virtual void CharContour( const SvxContourItem& ) override; + + /// Sfx item RES_CHRATR_CROSSEDOUT + virtual void CharCrossedOut( const SvxCrossedOutItem& rHt ) override; + + /// Sfx item RES_CHRATR_ESCAPEMENT + virtual void CharEscapement( const SvxEscapementItem& ) override; + + /// Sfx item RES_CHRATR_FONT + virtual void CharFont( const SvxFontItem& ) override; + + /// Sfx item RES_CHRATR_FONTSIZE + virtual void CharFontSize( const SvxFontHeightItem& ) override; + + /// Sfx item RES_CHRATR_KERNING + virtual void CharKerning( const SvxKerningItem& ) override; + + /// Sfx item RES_CHRATR_LANGUAGE + virtual void CharLanguage( const SvxLanguageItem& ) override; + + /// Sfx item RES_CHRATR_POSTURE + virtual void CharPosture( const SvxPostureItem& ) override; + + /// Sfx item RES_CHRATR_SHADOWED + virtual void CharShadow( const SvxShadowedItem& ) override; + + /// Sfx item RES_CHRATR_UNDERLINE + virtual void CharUnderline( const SvxUnderlineItem& ) override; + + /// Sfx item RES_CHRATR_WEIGHT + virtual void CharWeight( const SvxWeightItem& ) override; + + /// Sfx item RES_CHRATR_AUTOKERN + virtual void CharAutoKern( const SvxAutoKernItem& ) override; + + /// Sfx item RES_CHRATR_BLINK + virtual void CharAnimatedText( const SvxBlinkItem& ) override; + + /// Sfx item RES_CHRATR_BACKGROUND + virtual void CharBackground( const SvxBrushItem& ) override; + + /// Sfx item RES_CHRATR_CJK_FONT + virtual void CharFontCJK( const SvxFontItem& ) override; + + /// Sfx item RES_CHRATR_CJK_FONTSIZE + virtual void CharFontSizeCJK( const SvxFontHeightItem& rHt ) override { CharFontSize( rHt ); } + + /// Sfx item RES_CHRATR_CJK_LANGUAGE + virtual void CharLanguageCJK( const SvxLanguageItem& rHt ) override { CharLanguage( rHt ); } + + /// Sfx item RES_CHRATR_CJK_POSTURE + virtual void CharPostureCJK( const SvxPostureItem& rHt ) override { CharPosture( rHt ); } + + /// Sfx item RES_CHRATR_CJK_WEIGHT + virtual void CharWeightCJK( const SvxWeightItem& rHt ) override { CharWeight( rHt ); } + + /// Sfx item RES_CHRATR_CTL_FONT + virtual void CharFontCTL( const SvxFontItem& ) override; + + /// Sfx item RES_CHRATR_CTL_FONTSIZE + virtual void CharFontSizeCTL( const SvxFontHeightItem& rHt ) override { CharFontSize( rHt ); } + + /// Sfx item RES_CHRATR_CTL_LANGUAGE + virtual void CharLanguageCTL( const SvxLanguageItem& rHt ) override { CharLanguage( rHt ); } + + /// Sfx item RES_CHRATR_CTL_POSTURE + virtual void CharPostureCTL( const SvxPostureItem& ) override; + + /// Sfx item RES_CHRATR_CTL_WEIGHT + virtual void CharWeightCTL( const SvxWeightItem& ) override; + + /// Sfx item RES_CHRATR_BidiRTL + virtual void CharBidiRTL( const SfxPoolItem& rHt ) override; + + /// Sfx item RES_CHRATR_IdctHint + virtual void CharIdctHint( const SfxPoolItem& rHt ) override; + + /// Sfx item RES_CHRATR_ROTATE + virtual void CharRotate( const SvxCharRotateItem& ) override; + + /// Sfx item RES_CHRATR_EMPHASIS_MARK + virtual void CharEmphasisMark( const SvxEmphasisMarkItem& rHt ) override; + + /// Sfx item RES_CHRATR_TWO_LINES + virtual void CharTwoLines( const SvxTwoLinesItem& ) override; + + /// Sfx item RES_CHRATR_SCALEW + virtual void CharScaleWidth( const SvxCharScaleWidthItem& ) override; + + /// Sfx item RES_CHRATR_RELIEF + virtual void CharRelief( const SvxCharReliefItem& ) override; + + /// Sfx item RES_CHRATR_HIDDEN + virtual void CharHidden( const SvxCharHiddenItem& ) override; + + /// Sfx item RES_CHRATR_BOX + virtual void CharBorder( const ::editeng::SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow ) override; + + /// Sfx item RES_CHRATR_HIGHLIGHT + virtual void CharHighlight( const SvxBrushItem& ) override; + + /// Sfx item RES_TXTATR_INETFMT + virtual void TextINetFormat( const SwFormatINetFormat& ) override; + + /// Sfx item RES_TXTATR_CHARFMT + virtual void TextCharFormat( const SwFormatCharFormat& ) override; + + /// Sfx item RES_TXTATR_FTN + virtual void TextFootnote_Impl( const SwFormatFootnote& ) override; + + /// Sfx item RES_PARATR_LINESPACING + virtual void ParaLineSpacing_Impl( short nSpace, short nMulti ) override; + + /// Sfx item RES_PARATR_ADJUST + virtual void ParaAdjust( const SvxAdjustItem& rHt ) override; + + /// Sfx item RES_PARATR_SPLIT + virtual void ParaSplit( const SvxFormatSplitItem& ) override; + + /// Sfx item RES_PARATR_WIDOWS + virtual void ParaWidows( const SvxWidowsItem& rHt ) override; + + /// Sfx item RES_PARATR_TABSTOP + virtual void ParaTabStop( const SvxTabStopItem& rHt ) override; + + /// Sfx item RES_PARATR_HYPHENZONE + virtual void ParaHyphenZone( const SvxHyphenZoneItem& ) override; + + /// Sfx item RES_PARATR_NUMRULE + virtual void ParaNumRule_Impl( const SwTextNode *pTextNd, sal_Int32 nLvl, sal_Int32 nNumId ) override; + + /// Sfx item RES_PARATR_SCRIPTSPACE + virtual void ParaScriptSpace( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_HANGINGPUNCTUATION + virtual void ParaHangingPunctuation( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_FORBIDDEN_RULES + virtual void ParaForbiddenRules( const SfxBoolItem& ) override; + + /// Sfx item RES_PARATR_VERTALIGN + virtual void ParaVerticalAlign( const SvxParaVertAlignItem& ) override; + + /// Sfx item RES_PARATR_SNAPTOGRID + virtual void ParaSnapToGrid( const SvxParaGridItem& ) override; + + /// Sfx item RES_FRM_SIZE + virtual void FormatFrameSize( const SwFormatFrameSize& ) override; + + /// Sfx item RES_PAPER_BIN + virtual void FormatPaperBin( const SvxPaperBinItem& ) override; + + /// Sfx item RES_LR_SPACE + virtual void FormatLRSpace( const SvxLRSpaceItem& ) override; + + /// Sfx item RES_UL_SPACE + virtual void FormatULSpace( const SvxULSpaceItem& rHt ) override; + + /// Sfx item RES_SURROUND + virtual void FormatSurround( const SwFormatSurround& ) override; + + /// Sfx item RES_VERT_ORIENT + virtual void FormatVertOrientation( const SwFormatVertOrient& ) override; + + /// Sfx item RES_HORI_ORIENT + virtual void FormatHorizOrientation( const SwFormatHoriOrient& ) override; + + /// Sfx item RES_ANCHOR + virtual void FormatAnchor( const SwFormatAnchor& ) override; + + /// Sfx item RES_BACKGROUND + virtual void FormatBackground( const SvxBrushItem& ) override; + + /// Sfx item RES_FILL_STYLE + virtual void FormatFillStyle( const XFillStyleItem& ) override; + + /// Sfx item RES_FILL_GRADIENT + virtual void FormatFillGradient( const XFillGradientItem& ) override; + + /// Sfx item RES_BOX + virtual void FormatBox( const SvxBoxItem& ) override; + + /// Sfx item RES_COL + virtual void FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize ) override; + + /// Sfx item RES_KEEP + virtual void FormatKeep( const SvxFormatKeepItem& ) override; + + /// Sfx item RES_TEXTGRID + virtual void FormatTextGrid( const SwTextGridItem& ) override; + + /// Sfx item RES_LINENUMBER + virtual void FormatLineNumbering( const SwFormatLineNumber& ) override; + + /// Sfx item RES_FRAMEDIR + virtual void FormatFrameDirection( const SvxFrameDirectionItem& ) override; + + /// Sfx item RES_PARATR_GRABBAG + virtual void ParaGrabBag( const SfxGrabBagItem& ) override; + + /// Sfx item RES_TXTATR_GRABBAG + virtual void CharGrabBag( const SfxGrabBagItem& ) override; + + // Sfx item RES_PARATR_OUTLINELEVEL + virtual void ParaOutlineLevel( const SfxUInt16Item& ) override; + + /// Write the expanded field + virtual void WriteExpand( const SwField* pField ) override; + + virtual void RefField ( const SwField& rField, const OUString& rRef ) override; + virtual void HiddenField( const SwField& rField ) override; + virtual void SetField( const SwField& rField, ww::eField eType, const OUString& rCmd ) override; + virtual void PostitField( const SwField* pField ) override; + virtual bool DropdownField( const SwField* pField ) override; + virtual bool PlaceholderField( const SwField* pField ) override; + + virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override; + + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override; + + /// Reference to the export, where to get the data from + WW8Export &m_rWW8Export; + + /// For output of styles. + /// + /// We have to remember these positions between the StartStyle() and + /// EndStyle(). + sal_uInt16 nPOPosStdLen1, nPOPosStdLen2; + + /// For output of styles. + /// + /// We have to remember this position between StartStyleProperties() and + /// EndStyleProperties(). + sal_uInt16 m_nStyleStartSize, m_nStyleLenPos; + + /// For output of styles. + /// + /// Used between StartStyles() and EndStyles(). + sal_uLong m_nStyleCountPos; + + /// For output of run properties. + /// + /// We have to remember the number of field results, and do not export end + /// of the field results if we were forced to split text. + sal_uInt16 m_nFieldResults; + + bool mbOnTOXEnding; + + /// Bookmarks of the current paragraph + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart; + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd; + +public: + explicit WW8AttributeOutput( WW8Export &rWW8Export ) + : AttributeOutputBase(rWW8Export.GetWriter().GetMedia()->GetURLObject().GetMainURL( + INetURLObject::DecodeMechanism::NONE)) + , m_rWW8Export(rWW8Export) + , nPOPosStdLen1(0) + , nPOPosStdLen2(0) + , m_nStyleStartSize(0) + , m_nStyleLenPos(0) + , m_nStyleCountPos(0) + , m_nFieldResults(0) + , mbOnTOXEnding(false) + { + } + + /// Return the right export class. + virtual WW8Export& GetExport() override { return m_rWW8Export; } + +protected: + /// Output the bold etc. attributes + void OutputWW8Attribute( sal_uInt8 nId, bool bVal ); + + /// Output the bold etc. attributes, the Complex Text Layout version + void OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal ); + + void TableCellBorders( + ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); + +private: + + editeng::WordPageMargins m_pageMargins; + bool m_bFromEdge = false; + +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8glsy.cxx b/sw/source/filter/ww8/ww8glsy.cxx new file mode 100644 index 000000000..6d8cdce27 --- /dev/null +++ b/sw/source/filter/ww8/ww8glsy.cxx @@ -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 . + */ + +#include <memory> +#include <tools/urlobj.hxx> +#include <svl/urihelper.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <poolfmt.hxx> +#include <shellio.hxx> +#include <docsh.hxx> +#include <fmtanchr.hxx> +#include <frmfmt.hxx> +#include <doc.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <docary.hxx> +#include <frameformats.hxx> +#include "ww8glsy.hxx" +#include "ww8par.hxx" + +WW8Glossary::WW8Glossary(tools::SvRef<SotStorageStream> &refStrm, sal_uInt8 nVersion, SotStorage *pStg) + : rStrm(refStrm) + , xStg(pStg) + , nStrings(0) +{ + refStrm->SetEndian(SvStreamEndian::LITTLE); + WW8Fib aWwFib(*refStrm, nVersion); + + if (aWwFib.m_nFibBack >= 0x6A) //Word97 + { + xTableStream = pStg->OpenSotStream( + aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table, StreamMode::STD_READ); + + if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError()) + { + xTableStream->SetEndian(SvStreamEndian::LITTLE); + xGlossary = std::make_shared<WW8GlossaryFib>(*refStrm, nVersion, aWwFib); + } + } +} + +bool WW8Glossary::HasBareGraphicEnd(SwDoc *pDoc,SwNodeIndex const &rIdx) +{ + bool bRet=false; + for( sal_uInt16 nCnt = pDoc->GetSpzFrameFormats()->size(); nCnt; ) + { + const SwFrameFormat* pFrameFormat = (*pDoc->GetSpzFrameFormats())[ --nCnt ]; + if ( RES_FLYFRMFMT != pFrameFormat->Which() && + RES_DRAWFRMFMT != pFrameFormat->Which() ) + continue; + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + SwPosition const*const pAPos = rAnchor.GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) && + rIdx == pAPos->nNode.GetIndex() ) + { + bRet=true; + break; + } + } + return bRet; +} + +bool WW8Glossary::MakeEntries(SwDoc *pD, SwTextBlocks &rBlocks, + bool bSaveRelFile, const std::vector<OUString>& rStrings, + const std::vector<ww::bytes>& rExtra) +{ + // this code will be called after reading all text into the + // empty sections + const OUString aOldURL( rBlocks.GetBaseURL() ); + bool bRet=false; + if( bSaveRelFile ) + { + rBlocks.SetBaseURL( + URIHelper::SmartRel2Abs( + INetURLObject(), rBlocks.GetFileName(), + URIHelper::GetMaybeFileHdl())); + } + else + rBlocks.SetBaseURL( OUString() ); + + SwNodeIndex aDocEnd( pD->GetNodes().GetEndOfContent() ); + SwNodeIndex aStart( *aDocEnd.GetNode().StartOfSectionNode(), 1 ); + + // search the first NormalStartNode + while( !( aStart.GetNode().IsStartNode() && SwNormalStartNode == + aStart.GetNode().GetStartNode()->GetStartNodeType()) && + aStart < aDocEnd ) + ++aStart; + + if( aStart < aDocEnd ) + { + SwTextFormatColl* pColl = pD->getIDocumentStylePoolAccess().GetTextCollFromPool + (RES_POOLCOLL_STANDARD, false); + sal_uInt16 nGlosEntry = 0; + SwContentNode* pCNd = nullptr; + do { + SwPaM aPam( aStart ); + { + SwNodeIndex& rIdx = aPam.GetPoint()->nNode; + ++rIdx; + if( nullptr == ( pCNd = rIdx.GetNode().GetTextNode() ) ) + { + pCNd = pD->GetNodes().MakeTextNode( rIdx, pColl ); + rIdx = *pCNd; + } + } + aPam.GetPoint()->nContent.Assign( pCNd, 0 ); + aPam.SetMark(); + { + SwNodeIndex& rIdx = aPam.GetPoint()->nNode; + rIdx = aStart.GetNode().EndOfSectionIndex() - 1; + if(( nullptr == ( pCNd = rIdx.GetNode().GetContentNode() ) ) + || HasBareGraphicEnd(pD,rIdx)) + { + ++rIdx; + pCNd = pD->GetNodes().MakeTextNode( rIdx, pColl ); + rIdx = *pCNd; + } + } + aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + + // now we have the right selection for one entry. Copy this to + // the defined TextBlock, but only if it is not an autocorrection + // entry (== -1) otherwise the group indicates the group in the + // sttbfglsystyle list that this entry belongs to. Unused at the + // moment + const ww::bytes &rData = rExtra[nGlosEntry]; + sal_uInt16 n = SVBT16ToUInt16( &(rData[2]) ); + if(n != 0xFFFF) + { + rBlocks.ClearDoc(); + const OUString &rLNm = rStrings[nGlosEntry]; + + OUString sShortcut = rLNm; + + // Need to check make sure the shortcut is not already being used + sal_Int32 nStart = 0; + sal_uInt16 nCurPos = rBlocks.GetIndex( sShortcut ); + while( sal_uInt16(-1) != nCurPos ) + { + sShortcut = rLNm + OUString::number(++nStart); // add a Number to it + nCurPos = rBlocks.GetIndex( sShortcut ); + } + + if( rBlocks.BeginPutDoc( sShortcut, sShortcut )) // Make the shortcut and the name the same + + { + SwDoc* pGlDoc = rBlocks.GetDoc(); + SwNodeIndex aIdx( pGlDoc->GetNodes().GetEndOfContent(), + -1 ); + pCNd = aIdx.GetNode().GetContentNode(); + SwPosition aPos(aIdx, SwIndex(pCNd, pCNd ? pCNd->Len() : 0)); + pD->getIDocumentContentOperations().CopyRange(aPam, aPos, SwCopyFlags::CheckPosInFly); + rBlocks.PutDoc(); + } + } + aStart = aStart.GetNode().EndOfSectionIndex() + 1; + ++nGlosEntry; + } while( aStart.GetNode().IsStartNode() && + SwNormalStartNode == aStart.GetNode(). + GetStartNode()->GetStartNodeType()); + bRet=true; + } + +// this code will be called after reading all text into the empty sections + + rBlocks.SetBaseURL( aOldURL ); + return bRet; +} + +bool WW8Glossary::Load( SwTextBlocks &rBlocks, bool bSaveRelFile ) +{ + bool bRet=false; + if (xGlossary && xGlossary->IsGlossaryFib() && rBlocks.StartPutMuchBlockEntries()) + { + //read the names of the autotext entries + std::vector<OUString> aStrings; + std::vector<ww::bytes> aData; + + rtl_TextEncoding eStructCharSet = + WW8Fib::GetFIBCharset(xGlossary->m_chseTables, xGlossary->m_lid); + + WW8ReadSTTBF(true, *xTableStream, xGlossary->m_fcSttbfglsy, + xGlossary->m_lcbSttbfglsy, 0, eStructCharSet, aStrings, &aData ); + + rStrm->Seek(0); + + if ( 0 != (nStrings = static_cast< sal_uInt16 >(aStrings.size()))) + { + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + if (xDocSh->DoInitNew()) + { + SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc(); + + SwNodeIndex aIdx( + *pD->GetNodes().GetEndOfContent().StartOfSectionNode(), 1); + if( !aIdx.GetNode().IsTextNode() ) + { + OSL_ENSURE( false, "Where is the TextNode?" ); + pD->GetNodes().GoNext( &aIdx ); + } + SwPaM aPamo( aIdx ); + aPamo.GetPoint()->nContent.Assign(aIdx.GetNode().GetContentNode(), + 0); + std::unique_ptr<SwWW8ImplReader> xRdr(new SwWW8ImplReader( + xGlossary->m_nVersion, xStg.get(), rStrm.get(), *pD, rBlocks.GetBaseURL(), + true, false, *aPamo.GetPoint())); + xRdr->LoadDoc(this); + bRet = MakeEntries(pD, rBlocks, bSaveRelFile, aStrings, aData); + } + xDocSh->DoClose(); + rBlocks.EndPutMuchBlockEntries(); + } + } + return bRet; +} + +sal_uInt32 WW8GlossaryFib::FindGlossaryFibOffset(const WW8Fib &rFib) +{ + sal_uInt32 nGlossaryFibOffset = 0; + if ( rFib.m_fDot ) // it's a template + { + if ( rFib.m_pnNext ) + nGlossaryFibOffset = ( rFib.m_pnNext * 512 ); + } + return nGlossaryFibOffset; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8glsy.hxx b/sw/source/filter/ww8/ww8glsy.hxx new file mode 100644 index 000000000..fd847de71 --- /dev/null +++ b/sw/source/filter/ww8/ww8glsy.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_FILTER_WW8_WW8GLSY_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8GLSY_HXX + +#include <memory> +#include <sot/storage.hxx> + +#include <doc.hxx> +#include "ww8scan.hxx" + +class SwTextBlocks; +class SwNodeIndex; + +/* + * GlossaryFib takes the document fib and finds the glossary fib which may + * not exist. The glossary fib has the offsets into the autotext subdocument + * which is at the end of template .dot's + */ +class WW8GlossaryFib : public WW8Fib +{ +public: + WW8GlossaryFib( SvStream& rStrm, sal_uInt8 nWantedVersion, const WW8Fib &rFib) + : WW8Fib(rStrm, nWantedVersion, FindGlossaryFibOffset(rFib)) {} + // fGlsy will indicate whether this has AutoText or not + bool IsGlossaryFib() const { + return m_fGlsy; + } +private: + static sal_uInt32 FindGlossaryFibOffset(const WW8Fib &rFib); +}; + +/* + * Imports glossaries from word, given the document it gets the usual word + * doc information, then the glossary fib and uses the usual reader class to + * wrap the autotext into a star doc. Afterwards taking each section entry and + * making it a single star autotext entry. + * + * ToDo currently all autotext entries become resource hungry star autotext + * formatted text, need to use a flag in the ww8reader class to determine if + * an entry is formatted or not. + */ +class WW8Glossary +{ +public: + WW8Glossary( tools::SvRef<SotStorageStream> &refStrm, sal_uInt8 nVersion, SotStorage *pStg); + bool Load( SwTextBlocks &rBlocks, bool bSaveRelFile ); + std::shared_ptr<WW8GlossaryFib>& GetFib() + { + return xGlossary; + } + sal_uInt16 GetNoStrings() const + { + return nStrings; + } + +private: + std::shared_ptr<WW8GlossaryFib> xGlossary; + tools::SvRef<SotStorageStream> xTableStream; + tools::SvRef<SotStorageStream> &rStrm; + tools::SvRef<SotStorage> xStg; + sal_uInt16 nStrings; + + static bool MakeEntries(SwDoc *pD, SwTextBlocks &rBlocks, bool bSaveRelFile, + const std::vector<OUString>& rStrings, + const std::vector<ww::bytes>& rExtra); + static bool HasBareGraphicEnd(SwDoc *pD,SwNodeIndex const &rIdx); + + WW8Glossary(const WW8Glossary&) = delete; + WW8Glossary& operator=(const WW8Glossary&) = delete; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8graf.cxx b/sw/source/filter/ww8/ww8graf.cxx new file mode 100644 index 000000000..3c3961695 --- /dev/null +++ b/sw/source/filter/ww8/ww8graf.cxx @@ -0,0 +1,3220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/string.hxx> +#include <svl/urihelper.hxx> +#include <hintids.hxx> +#include <osl/endian.h> +#include <sal/log.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdocapt.hxx> +#include <svx/sxctitm.hxx> +#include <svx/sdggaitm.hxx> +#include <svx/sdgluitm.hxx> +#include <svx/sdgmoitm.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdsxyitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtditm.hxx> +#include <svx/sdtfsitm.hxx> +#include <editeng/editeng.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdocirc.hxx> +#include <editeng/outlobj.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/outliner.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/xfltrit.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <grfatr.hxx> +#include <fmtornt.hxx> +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> +#include <fmtanchr.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <ndgrf.hxx> +#include <dcontact.hxx> +#include <docsh.hxx> +#include <mdiexp.hxx> +#include "ww8struc.hxx" +#include "ww8scan.hxx" +#include "ww8par.hxx" +#include "ww8par2.hxx" +#include "ww8graf.hxx" +#include <fmtinfmt.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <fmtfollowtextflow.hxx> +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <editeng/editobj.hxx> +#include <math.h> +#include <fmturl.hxx> +#include <o3tl/enumrange.hxx> +#include <o3tl/safeint.hxx> +#include <memory> +#include <filter/msfilter/escherex.hxx> +#include "sprmids.hxx" + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace sw::types; +using namespace sw::util; + +// helper methods +static Color WW8TransCol(SVBT32 nWC) +{ +#if 1 // 1 = use predefined color, 0 = ignore + + // color table to convert RGB values to pre-defined colors + // (to make the writer UI show the right color names) + // the table is split in base 3, the greys are missing as + // they don't fit into that system (4 values: bw, wb, 2 * grey) + static const Color eColA[] = { // B G R B G R B G R + COL_BLACK, COL_RED, COL_LIGHTRED, // 0 0 0, 0 0 1, 0 0 2 + COL_GREEN, COL_BROWN, COL_BLACK, // 0 1 0, 0 1 1, 0 1 2 + COL_LIGHTGREEN, COL_BLACK, COL_YELLOW, // 0 2 0, 0 2 1, 0 2 2 + COL_BLUE, COL_MAGENTA, COL_BLACK, // 1 0 0, 1 0 1, 1 0 2 + COL_CYAN, COL_LIGHTGRAY, COL_BLACK, // 1 1 0, 1 1 1, 1 1 2 + COL_BLACK, COL_BLACK, COL_BLACK, // 1 2 0, 1 2 1, 1 2 2 + COL_LIGHTBLUE, COL_BLACK, COL_LIGHTMAGENTA, // 2 0 0, 2 0 1, 2 0 2 + COL_BLACK, COL_BLACK, COL_BLACK, // 2 1 0, 2 1 1, 2 1 2 + COL_LIGHTCYAN, COL_BLACK, COL_WHITE }; // 2 2 0, 2 2 1, 2 2 2 + + // In nWC[3] is a byte that's not described in the WW documentation. + // Its meaning appears to be the following: For 0, it's a normal color + // whose RGB values are in nWC[0..2]. If nWC[3] is 0x1, 0x7d or 0x83, + // it's a grey value whose black portion is given in 0.5% in nWC[0]. + // I guess that BIT(0) in nWC[3] is relevant for distinguishing RGB/Grey. + + if( !( nWC[3] & 0x1 ) && // not special (grey) + ( ( nWC[0] == 0 || nWC[0]== 0x80 || nWC[0] == 0xff ) // R + && ( nWC[1] == 0 || nWC[1]== 0x80 || nWC[1] == 0xff ) // G + && ( nWC[2] == 0 || nWC[2]== 0x80 || nWC[2] == 0xff ) ) ){// B + int nIdx = 0; // and now: Idx-calculation in base 3 + for (int i = 2; i >= 0; i--) + { + nIdx *= 3; + if (nWC[i]) + nIdx += ((nWC[i] == 0xff) ? 2 : 1); + } + if (eColA[nIdx] != COL_BLACK) + return eColA[nIdx]; // default color + } +#endif + + if (nWC[3] & 0x1) + { + // Special color gray + sal_uInt8 u = static_cast<sal_uInt8>( static_cast<sal_uLong>( 200 - nWC[0] ) * 256 / 200 ); + return Color(u, u, u); + } + + // User-Color + return Color(nWC[0], nWC[1], nWC[2]); +} + +void wwFrameNamer::SetUniqueGraphName(SwFrameFormat *pFrameFormat, const OUString &rFixed) +{ + if (mbIsDisabled || rFixed.isEmpty()) + return; + + pFrameFormat->SetName(msSeed+OUString::number(++mnImportedGraphicsCount) + ": " + rFixed); +} + +// ReadGrafStart reads object data and if necessary creates an anchor +bool SwWW8ImplReader::ReadGrafStart(void* pData, short nDataSiz, + WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + if (SVBT16ToUInt16(pHd->cb) < sizeof(WW8_DPHEAD) + nDataSiz) + { + OSL_ENSURE( false, "+graphic element: too short?" ); + m_pStrm->SeekRel(SVBT16ToUInt16(pHd->cb) - sizeof(WW8_DPHEAD)); + return false; + } + + bool bCouldRead = checkRead(*m_pStrm, pData, nDataSiz); + OSL_ENSURE(bCouldRead, "Short Graphic header"); + if (!bCouldRead) + return false; + + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR ); + aAnchor.SetAnchor( m_pPaM->GetPoint() ); + rSet.Put( aAnchor ); + + m_nDrawXOfs2 = m_nDrawXOfs; + m_nDrawYOfs2 = m_nDrawYOfs; + + return true; +} + +// SetStdAttr() sets standard attributes +static void SetStdAttr( SfxItemSet& rSet, WW8_DP_LINETYPE& rL, + WW8_DP_SHADOW const & rSh ) +{ + if( SVBT16ToUInt16( rL.lnps ) == 5 ){ // invisible + rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + }else{ // visible + Color aCol( WW8TransCol( rL.lnpc ) ); // line color + rSet.Put( XLineColorItem( OUString(), aCol ) ); + rSet.Put( XLineWidthItem( SVBT16ToUInt16( rL.lnpw ) ) ); + // line thickness + if( SVBT16ToUInt16( rL.lnps ) >= 1 + && SVBT16ToUInt16(rL.lnps ) <= 4 ){ // line style + rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) ); + sal_Int16 nLen = SVBT16ToUInt16( rL.lnpw ); + XDash aD( css::drawing::DashStyle_RECT, 1, 2 * nLen, 1, 5 * nLen, 5 * nLen ); + switch( SVBT16ToUInt16( rL.lnps ) ){ + case 1: aD.SetDots( 0 ); // Dash + aD.SetDashLen( 6 * nLen ); + aD.SetDistance( 4 * nLen ); + break; + case 2: aD.SetDashes( 0 ); break; // Dot + case 3: break; // Dash Dot + case 4: aD.SetDots( 2 ); break; // Dash Dot Dot + } + rSet.Put( XLineDashItem( OUString(), aD ) ); + }else{ + rSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); // needed for TextBox + } + } + if( SVBT16ToUInt16( rSh.shdwpi ) ){ // shadow + rSet.Put(makeSdrShadowItem(true)); + rSet.Put( makeSdrShadowXDistItem( SVBT16ToUInt16( rSh.xaOffset ) ) ); + rSet.Put( makeSdrShadowYDistItem( SVBT16ToUInt16( rSh.yaOffset ) ) ); + } +} + +// SetFill() sets fill attributes such as fore- and background color and +// pattern by reducing to a color +// SetFill() doesn't yet set a pattern, because Sdr can't easily do that +// and the Sdr hatching (XDash) isn't finished yet. +// Instead, a mixed color will be picked that's between the selected ones. +static void SetFill( SfxItemSet& rSet, WW8_DP_FILL& rFill ) +{ + static const sal_uInt8 nPatA[] = + { + 0, 0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, + 90, 50, 50, 50, 50, 50, 50, 33, 33, 33, 33, 33, 33 + }; + sal_uInt16 nPat = SVBT16ToUInt16(rFill.flpp); + + if (nPat == 0) // transparent + rSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + else + { + rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); // necessary for textbox + if (nPat <= 1 || (SAL_N_ELEMENTS(nPatA) <= nPat)) + { + // Solid background or unknown + rSet.Put(XFillColorItem(OUString(), WW8TransCol(rFill.dlpcBg))); + } + else + { // Brush -> color mix + Color aB( WW8TransCol( rFill.dlpcBg ) ); + Color aF( WW8TransCol( rFill.dlpcFg ) ); + aB.SetRed( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetRed()) * nPatA[nPat] + + static_cast<sal_uLong>(aB.GetRed()) * ( 100 - nPatA[nPat] ) ) / 100 ) ); + aB.SetGreen( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetGreen()) * nPatA[nPat] + + static_cast<sal_uLong>(aB.GetGreen()) * ( 100 - nPatA[nPat] ) ) / 100 ) ); + aB.SetBlue( static_cast<sal_uInt8>( ( static_cast<sal_uLong>(aF.GetBlue()) * nPatA[nPat] + + static_cast<sal_uLong>(aB.GetBlue()) * ( 100 - nPatA[nPat] ) ) / 100 ) ); + rSet.Put( XFillColorItem( OUString(), aB ) ); + } + } +} + +static void SetLineEndAttr( SfxItemSet& rSet, WW8_DP_LINEEND const & rLe, + WW8_DP_LINETYPE const & rLt ) +{ + sal_uInt16 aSB = SVBT16ToUInt16( rLe.aStartBits ); + if( aSB & 0x3 ) + { + ::basegfx::B2DPolygon aPolygon; + aPolygon.append(::basegfx::B2DPoint(0.0, 330.0)); + aPolygon.append(::basegfx::B2DPoint(100.0, 0.0)); + aPolygon.append(::basegfx::B2DPoint(200.0, 330.0)); + aPolygon.setClosed(true); + rSet.Put( XLineEndItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) ); + sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw ) + * ( ( aSB >> 2 & 0x3 ) + ( aSB >> 4 & 0x3 ) ); + if( nSiz < 220 ) nSiz = 220; + rSet.Put(XLineEndWidthItem(nSiz)); + rSet.Put(XLineEndCenterItem(false)); + } + + sal_uInt16 aEB = SVBT16ToUInt16( rLe.aEndBits ); + if( aEB & 0x3 ){ + ::basegfx::B2DPolygon aPolygon; + aPolygon.append(::basegfx::B2DPoint(0.0, 330.0)); + aPolygon.append(::basegfx::B2DPoint(100.0, 0.0)); + aPolygon.append(::basegfx::B2DPoint(200.0, 330.0)); + aPolygon.setClosed(true); + rSet.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon(aPolygon) ) ); + sal_uInt16 nSiz = SVBT16ToUInt16( rLt.lnpw ) + * ( ( aEB >> 2 & 0x3 ) + ( aEB >> 4 & 0x3 ) ); + if( nSiz < 220 ) nSiz = 220; + rSet.Put(XLineStartWidthItem(nSiz)); + rSet.Put(XLineStartCenterItem(false)); + } +} + +// start of routines for the different objects +SdrObject* SwWW8ImplReader::ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + WW8_DP_LINE aLine; + + if( !ReadGrafStart( static_cast<void*>(&aLine), sizeof( aLine ), pHd, rSet ) ) + return nullptr; + + Point aP[2]; + { + Point& rP0 = aP[0]; + Point& rP1 = aP[1]; + + rP0.setX( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2 ); + rP0.setY( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 ); + rP1 = rP0; + rP0.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaStart )) ); + rP0.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaStart )) ); + rP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.xaEnd )) ); + rP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aLine.yaEnd )) ); + } + + ::basegfx::B2DPolygon aPolygon; + aPolygon.append(::basegfx::B2DPoint(aP[0].X(), aP[0].Y())); + aPolygon.append(::basegfx::B2DPoint(aP[1].X(), aP[1].Y())); + SdrObject* pObj = new SdrPathObj( + *m_pDrawModel, + OBJ_LINE, + ::basegfx::B2DPolyPolygon(aPolygon)); + + SetStdAttr( rSet, aLine.aLnt, aLine.aShd ); + SetLineEndAttr( rSet, aLine.aEpp, aLine.aLnt ); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + WW8_DP_RECT aRect; + + if( !ReadGrafStart( static_cast<void*>(&aRect), sizeof( aRect ), pHd, rSet ) ) + return nullptr; + + Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2, + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 ); + Point aP1( aP0 ); + aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ); + aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + + SdrObject* pObj = new SdrRectObj( + *m_pDrawModel, + tools::Rectangle(aP0, aP1)); + + SetStdAttr( rSet, aRect.aLnt, aRect.aShd ); + SetFill( rSet, aRect.aFill ); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadEllipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + WW8_DP_ELLIPSE aEllipse; + + if( !ReadGrafStart( static_cast<void*>(&aEllipse), sizeof( aEllipse ), pHd, rSet ) ) + return nullptr; + + Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2, + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 ); + Point aP1( aP0 ); + aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ); + aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + + SdrObject* pObj = new SdrCircObj( + *m_pDrawModel, + SdrCircKind::Full, + tools::Rectangle(aP0, aP1)); + + SetStdAttr( rSet, aEllipse.aLnt, aEllipse.aShd ); + SetFill( rSet, aEllipse.aFill ); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + WW8_DP_ARC aArc; + + if( !ReadGrafStart( static_cast<void*>(&aArc), sizeof( aArc ), pHd, rSet ) ) + return nullptr; + + Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2, + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 ); + Point aP1( aP0 ); + aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) * 2 ); + aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) * 2 ); + + short nA[] = { 2, 3, 1, 0 }; + short nW = nA[ ( ( aArc.fLeft & 1 ) << 1 ) + ( aArc.fUp & 1 ) ]; + if( !aArc.fLeft ){ + aP0.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + aP1.AdjustY( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + } + if( aArc.fUp ){ + aP0.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ); + aP1.AdjustX( -static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ); + } + + SdrObject* pObj = new SdrCircObj( + *m_pDrawModel, + SdrCircKind::Section, + tools::Rectangle(aP0, aP1), + nW * 9000, + ( ( nW + 1 ) & 3 ) * 9000); + + SetStdAttr( rSet, aArc.aLnt, aArc.aShd ); + SetFill( rSet, aArc.aFill ); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + WW8_DP_POLYLINE aPoly; + + if( !ReadGrafStart( static_cast<void*>(&aPoly), sizeof( aPoly ), pHd, rSet ) ) + return nullptr; + + sal_uInt16 nCount = SVBT16ToUInt16( aPoly.aBits1 ) >> 1 & 0x7fff; + std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]); + + bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points + OSL_ENSURE(bCouldRead, "Short PolyLine header"); + if (!bCouldRead) + return nullptr; + + tools::Polygon aP( nCount ); + Point aPt; + for (sal_uInt16 i=0; i<nCount; ++i) + { + aPt.setX( SVBT16ToUInt16( xP[i << 1] ) + m_nDrawXOfs2 + + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) ); + aPt.setY( SVBT16ToUInt16( xP[( i << 1 ) + 1] ) + m_nDrawYOfs2 + + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) ); + aP[i] = aPt; + } + xP.reset(); + + SdrObject* pObj = new SdrPathObj( + *m_pDrawModel, + (SVBT16ToUInt16(aPoly.aBits1) & 0x1) ? OBJ_POLY : OBJ_PLIN, + ::basegfx::B2DPolyPolygon(aP.getB2DPolygon())); + + SetStdAttr( rSet, aPoly.aLnt, aPoly.aShd ); + SetFill( rSet, aPoly.aFill ); + + return pObj; +} + +static ESelection GetESelection(EditEngine const &rDrawEditEngine, long nCpStart, long nCpEnd) +{ + sal_Int32 nPCnt = rDrawEditEngine.GetParagraphCount(); + sal_Int32 nSP = 0; + sal_Int32 nEP = 0; + while( (nSP < nPCnt) + && (nCpStart >= rDrawEditEngine.GetTextLen( nSP ) + 1) ) + { + nCpStart -= rDrawEditEngine.GetTextLen( nSP ) + 1; + nSP++; + } + // at the end, switch to the new line only 1 character later as + // otherwise line attributes reach one line too far + while( (nEP < nPCnt) + && (nCpEnd > rDrawEditEngine.GetTextLen( nEP ) + 1) ) + { + nCpEnd -= rDrawEditEngine.GetTextLen( nEP ) + 1; + nEP++; + } + return ESelection( nSP, nCpStart, nEP, nCpEnd ); +} + +// InsertTxbxStyAttrs() sets style attributes into the passed ItemSet. +// SW styles are used since import-WW-styles are already destroyed. +// SW styles are examined in depth first search order (with parent styles) +// for the attributes given in aSrcTab. They're cloned, and the clones' +// Which-IDs are changed according to the aDstTab table so that the +// EditEngine will not ignore them. +// Both Paragraph and character attributes are stuffed into the ItemSet. +void SwWW8ImplReader::InsertTxbxStyAttrs( SfxItemSet& rS, sal_uInt16 nColl ) +{ + SwWW8StyInf * pStyInf = GetStyle(nColl); + if( pStyInf != nullptr && pStyInf->m_pFormat && pStyInf->m_bColl ) + { + const SfxPoolItem* pItem; + for( sal_uInt16 i = POOLATTR_BEGIN; i < POOLATTR_END; i++ ) + { + // If we are set in the source and not set in the destination + // then add it in. + if ( SfxItemState::SET == pStyInf->m_pFormat->GetItemState( + i, true, &pItem ) ) + { + SfxItemPool *pEditPool = rS.GetPool(); + sal_uInt16 nWhich = i; + sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich); + if ( + nSlotId && nWhich != nSlotId && + 0 != (nWhich = pEditPool->GetWhich(nSlotId)) && + nWhich != nSlotId && + ( SfxItemState::SET != rS.GetItemState(nWhich, false) ) + ) + { + rS.Put( pItem->CloneSetWhich(nWhich) ); + } + } + } + } + +} + +static void lcl_StripFields(OUString &rString, WW8_CP &rNewStartCp) +{ + sal_Int32 nStartPos = 0; + for (;;) + { + nStartPos = rString.indexOf(0x13, nStartPos); + if (nStartPos<0) + return; + + const sal_Unicode cStops[] = {0x14, 0x15, 0}; + const sal_Int32 nStopPos = comphelper::string::indexOfAny(rString, cStops, nStartPos); + if (nStopPos<0) + { + rNewStartCp += rString.getLength()-nStartPos; + rString = rString.copy(0, nStartPos); + return; + } + + const bool was0x14 = rString[nStopPos]==0x14; + rString = rString.replaceAt(nStartPos, nStopPos+1-nStartPos, ""); + rNewStartCp += nStopPos-nStartPos; + + if (was0x14) + { + ++rNewStartCp; + nStartPos = rString.indexOf(0x15, nStartPos); + if (nStartPos<0) + return; + rString = rString.replaceAt(nStartPos, 1, ""); + } + } +} + +namespace { + +class Chunk +{ +private: + OUString msURL; + long mnStartPos; // 0x13 + long mnEndPos; // 0x15 +public: + explicit Chunk(long nStart, const OUString &rURL) + : msURL(rURL), mnStartPos(nStart), mnEndPos(0) {} + + void SetEndPos(long nEnd) { mnEndPos = nEnd; } + long GetStartPos() const {return mnStartPos;} + long GetEndPos() const {return mnEndPos;} + const OUString &GetURL() const {return msURL;} + void Adjust(sal_Int32 nAdjust) + { + mnStartPos-=nAdjust; + mnEndPos-=nAdjust; + } +}; + + bool IsValidSel(const EditEngine& rEngine, const ESelection& rSel) + { + const auto nParaCount = rEngine.GetParagraphCount(); + if (rSel.nStartPara < nParaCount && rSel.nEndPara < nParaCount) + return rSel.nStartPos >= 0 && rSel.nEndPos >= 0; + return false; + } +} + +// InsertAttrsAsDrawingAttrs() sets attributes between StartCp and EndCp. +// Style attributes are set as hard, paragraph and character attributes. +void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp, + ManTypes eType, bool bONLYnPicLocFc) +{ + /* + Save and create new plcxman for this drawing object, of the type that + will include the para end mark inside a paragraph property range, as + drawing boxes have real paragraph marks as part of their text, while + normal writer has separate nodes for each paragraph and so has no actual + paragraph mark as part of the paragraph text. + */ + WW8ReaderSave aSave(this); + m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), eType, nStartCp, true); + + WW8_CP nStart = m_xPlcxMan->Where(); + WW8_CP nNext, nStartReplace=0; + + bool bDoingSymbol = false; + sal_Unicode cReplaceSymbol = m_cSymbol; + + std::unique_ptr<SfxItemSet> pS(new SfxItemSet(m_pDrawEditEngine->GetEmptyItemSet())); + WW8PLCFManResult aRes; + + std::deque<Chunk> aChunks; + + // Here store stack location + size_t nCurrentCount = m_xCtrlStck->size(); + while (nStart < nEndCp) + { + // nStart is the beginning of the attributes for this range, and + // may be before the text itself. So watch out for that + WW8_CP nTextStart = nStart; + if (nTextStart < nStartCp) + nTextStart = nStartCp; + + // get position of next SPRM + bool bStartAttr = m_xPlcxMan->Get(&aRes); + m_nCurrentColl = m_xPlcxMan->GetColl(); + if (aRes.nSprmId) + { + if( bONLYnPicLocFc ) + { + if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) ) + { + Read_PicLoc(aRes.nSprmId, aRes.pMemPos + + m_xSprmParser->DistanceToData(aRes.nSprmId), 4); + // Ok, that's what we were looking for. Now let's get + // out of here! + break; + } + } + else if ((eFTN > aRes.nSprmId) || (0x0800 <= aRes.nSprmId)) + { + // Here place them onto our usual stack and we will pop them + // off and convert them later + if (bStartAttr) + { + ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId); + if (!bDoingSymbol && m_bSymbol) + { + bDoingSymbol = true; + nStartReplace = nTextStart; + cReplaceSymbol = m_cSymbol; + } + } + else + { + EndSprm( aRes.nSprmId ); + if (!m_bSymbol && bDoingSymbol) + { + bDoingSymbol = false; + OUStringBuffer sTemp; + comphelper::string::padToLength(sTemp, + nTextStart - nStartReplace, cReplaceSymbol); + m_pDrawEditEngine->QuickInsertText(sTemp.makeStringAndClear(), + GetESelection(*m_pDrawEditEngine, nStartReplace - nStartCp, + nTextStart - nStartCp ) ); + } + } + } + else if (aRes.nSprmId == eFLD) + { + if (bStartAttr) + { + size_t nCount = m_xCtrlStck->size(); + if (m_aFieldStack.empty() && Read_Field(&aRes)) + { + OUString sURL; + for (size_t nI = m_xCtrlStck->size(); nI > nCount; --nI) + { + const SfxPoolItem *pItem = ((*m_xCtrlStck)[nI-1]).pAttr.get(); + sal_uInt16 nWhich = pItem->Which(); + if (nWhich == RES_TXTATR_INETFMT) + { + const SwFormatINetFormat *pURL = + static_cast<const SwFormatINetFormat *>(pItem); + sURL = pURL->GetValue(); + } + m_xCtrlStck->DeleteAndDestroy(nI-1); + } + aChunks.emplace_back(nStart, sURL); + } + } + else + { + if (!m_aFieldStack.empty() && End_Field() && !aChunks.empty()) + aChunks.back().SetEndPos(nStart+1); + } + } + } + + m_xPlcxMan->advance(); + nNext = m_xPlcxMan->Where(); + + const WW8_CP nEnd = ( nNext < nEndCp ) ? nNext : nEndCp; + if (!bONLYnPicLocFc && nNext != nStart && nEnd >= nStartCp) + { + SfxItemPool *pEditPool = pS->GetPool(); + + // Here read current properties and convert them into pS + // and put those attrs into the draw box if they can be converted + // to draw attributes + if (m_xCtrlStck->size() - nCurrentCount) + { + for (size_t i = nCurrentCount; i < m_xCtrlStck->size(); ++i) + { + const SfxPoolItem *pItem = ((*m_xCtrlStck)[i]).pAttr.get(); + sal_uInt16 nWhich = pItem->Which(); + if( nWhich < RES_FLTRATTR_BEGIN || + nWhich >= RES_FLTRATTR_END ) + { + sal_uInt16 nSlotId = m_rDoc.GetAttrPool().GetSlotId(nWhich); + if ( + nSlotId && nWhich != nSlotId && + 0 != (nWhich = pEditPool->GetWhich(nSlotId)) && + nWhich != nSlotId + ) + { + pS->Put( pItem->CloneSetWhich(nWhich) ); + } + } + } + } + // Fill in the remainder from the style + InsertTxbxStyAttrs(*pS, m_nCurrentColl); + + if( pS->Count() ) + { + m_pDrawEditEngine->QuickSetAttribs( *pS, + GetESelection(*m_pDrawEditEngine, nTextStart - nStartCp, nEnd - nStartCp ) ); + pS.reset( new SfxItemSet(m_pDrawEditEngine->GetEmptyItemSet()) ); + } + } + nStart = nNext; + } + pS.reset(); + + // pop off as far as recorded location just in case there were some left + // unclosed + for (size_t nI = m_xCtrlStck->size(); nI > nCurrentCount; --nI) + m_xCtrlStck->DeleteAndDestroy(nI-1); + + auto aEnd = aChunks.end(); + for (auto aIter = aChunks.begin(); aIter != aEnd; ++aIter) + { + ESelection aSel(GetESelection(*m_pDrawEditEngine, aIter->GetStartPos()-nStartCp, + aIter->GetEndPos()-nStartCp)); + if (!IsValidSel(*m_pDrawEditEngine, aSel)) + continue; + OUString aString(m_pDrawEditEngine->GetText(aSel)); + const sal_Int32 nOrigLen = aString.getLength(); + WW8_CP nDummy(0); + lcl_StripFields(aString, nDummy); + + sal_Int32 nChanged; + if (!aIter->GetURL().isEmpty()) + { + SvxURLField aURL(aIter->GetURL(), aString, SvxURLFormat::AppDefault); + m_pDrawEditEngine->QuickInsertField(SvxFieldItem(aURL, EE_FEATURE_FIELD), aSel); + nChanged = nOrigLen - 1; + } + else + { + m_pDrawEditEngine->QuickInsertText(aString, aSel); + nChanged = nOrigLen - aString.getLength(); + } + for (auto aIter2 = aIter+1; aIter2 != aEnd; ++aIter2) + aIter2->Adjust(nChanged); + } + + /* + Don't worry about the new pPlcxMan, the restore removes it when + replacing the current one with the old one. + */ + aSave.Restore(this); +} + +bool SwWW8ImplReader::GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp, + sal_uInt16 nTxBxS, sal_uInt16 nSequence) +{ + // grab the TextBox-PLCF quickly + WW8PLCFspecial* pT = m_xPlcxMan ? m_xPlcxMan->GetTxbx() : nullptr; + if( !pT ) + { + OSL_ENSURE( false, "+where's the text graphic (1)?" ); + return false; + } + + // if applicable first find the right TextBox-Story + bool bCheckTextBoxStory = ( nTxBxS && pT->GetIMax() >= nTxBxS ); + if( bCheckTextBoxStory ) + pT->SetIdx( nTxBxS-1 ); + + // then determine start and end + void* pT0; + if (!pT->Get(rStartCp, pT0) || rStartCp < 0) + { + OSL_ENSURE( false, "+where's the text graphic (2)?" ); + return false; + } + + if( bCheckTextBoxStory ) + { + bool bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable )); + while( bReusable ) + { + pT->advance(); + if( !pT->Get( rStartCp, pT0 ) ) + { + OSL_ENSURE( false, "+where's the text graphic (2a)?" ); + return false; + } + bReusable = (0 != SVBT16ToUInt16( static_cast<WW8_TXBXS*>(pT0)->fReusable )); + } + } + pT->advance(); + if (!pT->Get(rEndCp, pT0) || rEndCp < 0) + { + OSL_ENSURE( false, "+where's the text graphic (3)?" ); + return false; + } + + // find the right page in the break table (if necessary) + if( bCheckTextBoxStory ) + { + // special case: entire chain should be determined - done! + if( USHRT_MAX > nSequence ) + { + long nMinStartCp = rStartCp; + long nMaxEndCp = rEndCp; + // quickly grab the TextBox-Break-Descriptor-PLCF + pT = m_xPlcxMan->GetTxbxBkd(); + if (!pT) // It can occur on occasion, Caolan + return false; + + // find first entry for this TextBox story + if( !pT->SeekPos( rStartCp ) ) + { + OSL_ENSURE( false, "+where's the text graphic (4)" ); + return false; + } + // if needed skip the appropriate number of entries + for (sal_uInt16 iSequence = 0; iSequence < nSequence; ++iSequence) + pT->advance(); + // and determine actual start and end + if( (!pT->Get( rStartCp, pT0 )) + || ( nMinStartCp > rStartCp ) ) + { + OSL_ENSURE( false, "+where's the text graphic (5)?" ); + return false; + } + if( rStartCp >= nMaxEndCp ) + rEndCp = rStartCp; // not an error: empty string + else + { + pT->advance(); + if ( (!pT->Get(rEndCp, pT0)) || (nMaxEndCp < rEndCp-1) ) + { + OSL_ENSURE( false, "+where's the text graphic (6)?" ); + return false; + } + rEndCp -= 1; + } + } + else + rEndCp -= 1; + } + else + rEndCp -= 1; + return true; +} + +// TxbxText() grabs the text from the WW file and returns that along with +// the StartCp and the corrected (by -2, or -1 for version 8) EndCp. +sal_Int32 SwWW8ImplReader::GetRangeAsDrawingString(OUString& rString, long nStartCp, long nEndCp, ManTypes eType) +{ + WW8_CP nOffset = 0; + m_xWwFib->GetBaseCp(eType, &nOffset); //TODO: check return value + + OSL_ENSURE(nStartCp <= nEndCp, "+where's the graphic text (7)?"); + if (nStartCp == nEndCp) + rString.clear(); // empty string: entirely possible + else if (nStartCp < nEndCp) + { + // read the text: can be split into multiple pieces + const sal_Int32 nLen = m_xSBase->WW8ReadString(*m_pStrm, rString, + nStartCp + nOffset, nEndCp - nStartCp, GetCurrentCharSet()); + OSL_ENSURE(nLen, "+where's the text graphic (8)?"); + if (nLen>0) + { + if( rString[nLen-1]==0x0d ) + rString = rString.copy(0, nLen-1); + + rString = rString.replace( 0xb, 0xa ); + return nLen; + } + } + return 0; +} + +//EditEngine::InsertText will replace dos lines resulting in a shorter +//string than is passed in, so inserting attributes based on the original +//string len can fail. So here replace the dos line ends similar to +//how EditEngine does it, but preserve the length and replace the extra +//chars with placeholders, record the position of the placeholders and +//remove those extra chars after attributes have been inserted +static std::vector<sal_Int32> replaceDosLineEndsButPreserveLength(OUString &rIn) +{ + OUStringBuffer aNewData(rIn); + std::vector<sal_Int32> aDosLineEndDummies; + sal_Int32 i = 0; + sal_Int32 nStrLen = rIn.getLength(); + while (i < nStrLen) + { + // \r or \n causes linebreak + if (rIn[i] == '\r' || rIn[i] == '\n') + { + // skip char if \r\n or \n\r + if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) && + (rIn[i] != rIn[i+1]) ) + { + ++i; + aDosLineEndDummies.push_back(i); + aNewData[i] = 0; + } + } + ++i; + } + rIn = aNewData.makeStringAndClear(); + return aDosLineEndDummies; +} + +static void removePositions(EditEngine &rDrawEditEngine, const std::vector<sal_Int32>& rDosLineEndDummies) +{ + for (auto aIter = rDosLineEndDummies.rbegin(); aIter != rDosLineEndDummies.rend(); ++aIter) + { + sal_Int32 nCharPos(*aIter); + rDrawEditEngine.QuickDelete(GetESelection(rDrawEditEngine, nCharPos, nCharPos+1)); + } +} + +std::unique_ptr<OutlinerParaObject> SwWW8ImplReader::ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType) +{ + std::unique_ptr<OutlinerParaObject> pRet; + + sal_Int32 nLen = GetRangeAsDrawingString(rString, nStartCp, nEndCp, eType); + if (nLen > 0) + { + if (!m_pDrawEditEngine) + { + m_pDrawEditEngine.reset(new EditEngine(nullptr)); + } + + //replace dos line endings with editeng ones, replace any extra chars with + //placeholders to keep the inserted string len in sync with the attribute cps + //and record in aDosLineEnds the superfluous positions + OUString sEEString(rString); + std::vector<sal_Int32> aDosLineEnds(replaceDosLineEndsButPreserveLength(sEEString)); + m_pDrawEditEngine->SetText(sEEString); + InsertAttrsAsDrawingAttrs(nStartCp, nStartCp+nLen, eType); + //remove any superfluous placeholders of replaceDosLineEndsButPreserveLength + //after attributes have been inserted + removePositions(*m_pDrawEditEngine, aDosLineEnds); + + // Annotations typically begin with a (useless) 0x5 + if ((eType == MAN_AND) && m_pDrawEditEngine->GetTextLen()) + { + ESelection aFirstChar(0, 0, 0, 1); + if (m_pDrawEditEngine->GetText( aFirstChar ) == "\x05") + m_pDrawEditEngine->QuickDelete(aFirstChar); + } + + std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject(); + pRet.reset( new OutlinerParaObject( std::move(pTemporaryText) ) ); + pRet->SetOutlinerMode( OutlinerMode::TextObject ); + + m_pDrawEditEngine->SetText( OUString() ); + m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet()); + + // Strip out fields, leaving the result + WW8_CP nDummy(0); + lcl_StripFields(rString, nDummy); + // Strip out word's special characters for the simple string + rString = rString.replaceAll("\x01", ""); + rString = rString.replaceAll("\x05", ""); + rString = rString.replaceAll("\x08", ""); + rString = rString.replaceAll("\007\007", "\007\012"); + rString = rString.replace(0x7, ' '); + } + + return pRet; +} + +// InsertTxbxText() adds the Text and the Attributes for TextBoxes and CaptionBoxes +void SwWW8ImplReader::InsertTxbxText(SdrTextObj* pTextObj, + Size const * pObjSiz, sal_uInt16 nTxBxS, sal_uInt16 nSequence, long nPosCp, + SwFrameFormat const * pOldFlyFormat, bool bMakeSdrGrafObj, bool& rbEraseTextObj, + bool* pbTestTxbxContainsText, long* pnStartCp, long* pnEndCp, + bool* pbContainsGraphics, SvxMSDffImportRec const * pRecord) +{ + SwFrameFormat* pFlyFormat = nullptr; + sal_uLong nOld = m_pStrm->Tell(); + + ManTypes eType = m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX; + + rbEraseTextObj = false; + + OUString aString; + WW8_CP nStartCp, nEndCp; + bool bContainsGraphics = false; + bool bTextWasRead = GetTxbxTextSttEndCp(nStartCp, nEndCp, nTxBxS, nSequence) && + GetRangeAsDrawingString(aString, nStartCp, nEndCp, eType) > 0; + + if (!m_pDrawEditEngine) + { + m_pDrawEditEngine.reset(new EditEngine(nullptr)); + } + if( pObjSiz ) + m_pDrawEditEngine->SetPaperSize( *pObjSiz ); + + const OUString aOrigString(aString); + if( bTextWasRead ) + { + WW8_CP nNewStartCp = nStartCp; + lcl_StripFields(aString, nNewStartCp); + + if (aString.getLength()!=1) + { + bContainsGraphics = aString.indexOf(0x1)<0 || aString.indexOf(0x8)<0; + } + else // May be a single graphic or object + { + bool bDone = true; + switch( aString[0] ) + { + case 0x1: + if (!pbTestTxbxContainsText) + { + WW8ReaderSave aSave(this, nNewStartCp -1); + bool bOldEmbeddObj = m_bEmbeddObj; + // bEmbeddObj Ordinarily would have been set by field + // parse, but this is impossible here so... + m_bEmbeddObj = true; + + // 1st look for OLE- or Graph-Indicator Sprms + WW8PLCFx_Cp_FKP* pChp = m_xPlcxMan->GetChpPLCF(); + WW8PLCFxDesc aDesc; + pChp->GetSprms( &aDesc ); + WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, *m_xSprmParser); + + for( int nLoop = 0; nLoop < 2; ++nLoop ) + { + while (aSprmIter.GetSprms()) + { + const sal_uInt8 *const pParams(aSprmIter.GetCurrentParams()); + if (nullptr == pParams) + break; + sal_uInt16 nCurrentId = aSprmIter.GetCurrentId(); + switch( nCurrentId ) + { + case 75: + case 118: + case 0x080A: + case 0x0856: + Read_Obj(nCurrentId, pParams, 1); + break; + case 68: // Read_Pic() + case 0x6A03: + case NS_sprm::LN_CObjLocation: + Read_PicLoc(nCurrentId, pParams, 1); + break; + } + aSprmIter.advance(); + } + + if( !nLoop ) + { + pChp->GetPCDSprms( aDesc ); + aSprmIter.SetSprms( aDesc.pMemPos, + aDesc.nSprmsLen ); + } + } + aSave.Restore(this); + m_bEmbeddObj=bOldEmbeddObj; + + // then import either an OLE of a Graphic + if( m_bObj ) + { + if( bMakeSdrGrafObj && pTextObj && + pTextObj->getParentSdrObjectFromSdrObject() ) + { + // use SdrOleObj/SdrGrafObj instead of + // SdrTextObj in this Group + + Graphic aGraph; + SdrObject* pNew = ImportOleBase(aGraph); + + if( !pNew ) + { + pNew = new SdrGrafObj(*m_pDrawModel); + static_cast<SdrGrafObj*>(pNew)->SetGraphic(aGraph); + } + + GrafikCtor(); + + pNew->SetLogicRect( pTextObj->GetCurrentBoundRect() ); + pNew->SetLayer( pTextObj->GetLayer() ); + + pTextObj->getParentSdrObjectFromSdrObject()->GetSubList()-> + ReplaceObject(pNew, pTextObj->GetOrdNum()); + } + else + pFlyFormat = ImportOle(); + m_bObj = false; + } + else + { + InsertAttrsAsDrawingAttrs(nNewStartCp, nNewStartCp+1, + eType, true); + pFlyFormat = ImportGraf(bMakeSdrGrafObj ? pTextObj : nullptr, + pOldFlyFormat); + } + } + break; + case 0x8: + if ( (!pbTestTxbxContainsText) && (!m_bObj) ) + pFlyFormat = Read_GrafLayer( nPosCp ); + break; + default: + bDone = false; + break; + } + + if( bDone ) + { + if( pFlyFormat && pRecord ) + { + SfxItemSet aFlySet( m_rDoc.GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>{} ); + + tools::Rectangle aInnerDist( pRecord->nDxTextLeft, + pRecord->nDyTextTop, + pRecord->nDxTextRight, + pRecord->nDyTextBottom ); + MatchSdrItemsIntoFlySet( pTextObj, + aFlySet, + pRecord->eLineStyle, + pRecord->eLineDashing, + pRecord->eShapeType, + aInnerDist ); + + pFlyFormat->SetFormatAttr( aFlySet ); + + MapWrapIntoFlyFormat(pRecord, pFlyFormat); + } + aString.clear(); + rbEraseTextObj = (nullptr != pFlyFormat); + } + } + } + + if( pnStartCp ) + *pnStartCp = nStartCp; + if( pnEndCp ) + *pnEndCp = nEndCp; + + if( pbTestTxbxContainsText ) + *pbTestTxbxContainsText = bTextWasRead && ! rbEraseTextObj; + else if( !rbEraseTextObj ) + { + if( bTextWasRead ) + { + m_pDrawEditEngine->SetText(aOrigString); + InsertAttrsAsDrawingAttrs(nStartCp, nEndCp, eType); + } + + bool bVertical = pTextObj->IsVerticalWriting(); + std::unique_ptr<EditTextObject> pTemporaryText = m_pDrawEditEngine->CreateTextObject(); + std::unique_ptr<OutlinerParaObject> pOp( new OutlinerParaObject(*pTemporaryText) ); + pOp->SetOutlinerMode( OutlinerMode::TextObject ); + pOp->SetVertical( bVertical ); + pTemporaryText.reset(); + pTextObj->NbcSetOutlinerParaObject( std::move(pOp) ); + pTextObj->SetVerticalWriting(bVertical); + + // For the next TextBox also remove the old paragraph attributes + // and styles, otherwise the next box will start with the wrong + // attributes. + // Course of action: delete text = reduce to one paragraph + // and on this one delete the paragraph attributes + // and styles + m_pDrawEditEngine->SetText( OUString() ); + m_pDrawEditEngine->SetParaAttribs(0, m_pDrawEditEngine->GetEmptyItemSet()); + } + + m_pStrm->Seek( nOld ); + if (pbContainsGraphics) + *pbContainsGraphics = bContainsGraphics; +} + +bool SwWW8ImplReader::TxbxChainContainsRealText(sal_uInt16 nTxBxS, long& rStartCp, + long& rEndCp) +{ + bool bErase, bContainsText; + InsertTxbxText( nullptr,nullptr,nTxBxS,USHRT_MAX,0,nullptr,false, bErase, &bContainsText, + &rStartCp, &rEndCp ); + return bContainsText; +} + +// TextBoxes only for Ver67 !! +SdrObject* SwWW8ImplReader::ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + bool bDummy; + WW8_DP_TXTBOX aTextB; + + if( !ReadGrafStart( static_cast<void*>(&aTextB), sizeof( aTextB ), pHd, rSet ) ) + return nullptr; + + Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + m_nDrawXOfs2, + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + m_nDrawYOfs2 ); + Point aP1( aP0 ); + aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) ); + aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + + SdrRectObj* pObj = new SdrRectObj( + *m_pDrawModel, + OBJ_TEXT, + tools::Rectangle(aP0, aP1)); + + pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1)); + Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dxa )) , + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->dya )) ); + + long nStartCpFly,nEndCpFly; + bool bContainsGraphics; + InsertTxbxText(pObj, &aSize, 0, 0, 0, nullptr, false, + bDummy,nullptr,&nStartCpFly,&nEndCpFly,&bContainsGraphics); + + SetStdAttr( rSet, aTextB.aLnt, aTextB.aShd ); + SetFill( rSet, aTextB.aFill ); + + rSet.Put( SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE ) ); + rSet.Put( makeSdrTextAutoGrowWidthItem(false)); + rSet.Put( makeSdrTextAutoGrowHeightItem(false)); + rSet.Put( makeSdrTextLeftDistItem( MIN_BORDER_DIST*2 ) ); + rSet.Put( makeSdrTextRightDistItem( MIN_BORDER_DIST*2 ) ); + rSet.Put( makeSdrTextUpperDistItem( MIN_BORDER_DIST ) ); + rSet.Put( makeSdrTextLowerDistItem( MIN_BORDER_DIST ) ); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + static const SdrCaptionType aCaptA[] = { SdrCaptionType::Type1, SdrCaptionType::Type2, + SdrCaptionType::Type3, SdrCaptionType::Type4 }; + + WW8_DP_CALLOUT_TXTBOX aCallB; + + if( !ReadGrafStart( static_cast<void*>(&aCallB), sizeof( aCallB ), pHd, rSet ) ) + return nullptr; + + sal_uInt16 nCount = SVBT16ToUInt16( aCallB.dpPolyLine.aBits1 ) >> 1 & 0x7fff; + if (nCount < 1) + { + SAL_WARN("sw.ww8", "Short CaptionBox header"); + return nullptr; + } + + std::unique_ptr<SVBT16[]> xP(new SVBT16[nCount * 2]); + + bool bCouldRead = checkRead(*m_pStrm, xP.get(), nCount * 4); // read points + if (!bCouldRead) + { + SAL_WARN("sw.ww8", "Short CaptionBox header"); + return nullptr; + } + + sal_uInt8 nTyp = static_cast<sal_uInt8>(nCount) - 1; + if( nTyp == 1 && SVBT16ToUInt16( xP[0] ) == SVBT16ToUInt16( xP[2] ) ) + nTyp = 0; + + Point aP0( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.xa )) + m_nDrawXOfs2, + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.ya )) + m_nDrawYOfs2 ); + Point aP1( aP0 ); + aP1.AdjustX(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )) ); + aP1.AdjustY(static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dya )) ); + Point aP2( static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )) + + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.xa )) + + m_nDrawXOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[0] )), + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )) + + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadPolyLine.ya )) + + m_nDrawYOfs2 + static_cast<sal_Int16>(SVBT16ToUInt16( xP[1] )) ); + xP.reset(); + + SdrCaptionObj* pObj = new SdrCaptionObj( + *m_pDrawModel, + tools::Rectangle(aP0, aP1), + aP2); + + pObj->NbcSetSnapRect(tools::Rectangle(aP0, aP1)); + Size aSize( static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dxa )), + static_cast<sal_Int16>(SVBT16ToUInt16( aCallB.dpheadTxbx.dya )) ); + bool bEraseThisObject; + + InsertTxbxText(pObj, &aSize, 0, 0, 0, nullptr, false, bEraseThisObject ); + + if( SVBT16ToUInt16( aCallB.dptxbx.aLnt.lnps ) != 5 ) // Is border visible ? + SetStdAttr( rSet, aCallB.dptxbx.aLnt, aCallB.dptxbx.aShd ); + else // no -> take lines + SetStdAttr( rSet, aCallB.dpPolyLine.aLnt, aCallB.dptxbx.aShd ); + SetFill( rSet, aCallB.dptxbx.aFill ); + rSet.Put(SdrCaptionTypeItem(aCaptA[nTyp % SAL_N_ELEMENTS(aCaptA)])); + + return pObj; +} + +SdrObject *SwWW8ImplReader::ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet) +{ + sal_Int16 nGrouped; + + if( !ReadGrafStart( static_cast<void*>(&nGrouped), sizeof( nGrouped ), pHd, rSet ) ) + return nullptr; + +#ifdef OSL_BIGENDIAN + nGrouped = (sal_Int16)OSL_SWAPWORD( nGrouped ); +#endif + + m_nDrawXOfs = m_nDrawXOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )); + m_nDrawYOfs = m_nDrawYOfs + static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )); + + SdrObject* pObj = new SdrObjGroup(*m_pDrawModel); + + short nLeft = static_cast<sal_Int16>(SVBT16ToUInt16( pHd->cb )) - sizeof( WW8_DPHEAD ); + for (int i = 0; i < nGrouped && nLeft >= static_cast<short>(sizeof(WW8_DPHEAD)); ++i) + { + SfxAllItemSet aSet(m_pDrawModel->GetItemPool()); + if (SdrObject *pObject = ReadGrafPrimitive(nLeft, aSet)) + { + // first add and then set ItemSet + SdrObjList *pSubGroup = pObj->GetSubList(); + OSL_ENSURE(pSubGroup, "Why no sublist available?"); + if (pSubGroup) + pSubGroup->InsertObject(pObject, 0); + pObject->SetMergedItemSetAndBroadcast(aSet); + } + } + + m_nDrawXOfs = m_nDrawXOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->xa )); + m_nDrawYOfs = m_nDrawYOfs - static_cast<sal_Int16>(SVBT16ToUInt16( pHd->ya )); + + return pObj; +} + +SdrObject* SwWW8ImplReader::ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet) +{ + // This whole archaic word 6 graphic import can probably be refactored + // into an object hierarchy with a little effort. + SdrObject *pRet=nullptr; + WW8_DPHEAD aHd; // Read Draw-Primitive-Header + bool bCouldRead = checkRead(*m_pStrm, &aHd, sizeof(WW8_DPHEAD)) && + SVBT16ToUInt16(aHd.cb) >= sizeof(WW8_DPHEAD); + OSL_ENSURE(bCouldRead, "Graphic Primitive header short read" ); + if (!bCouldRead) + { + rLeft=0; + return pRet; + } + + if( rLeft >= SVBT16ToUInt16(aHd.cb) ) // precautions + { + rSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH)); + switch (SVBT16ToUInt16(aHd.dpk) & 0xff ) + { + case 0: + pRet = ReadGroup(&aHd, rSet); + break; + case 1: + pRet = ReadLine(&aHd, rSet); + break; + case 2: + pRet = ReadTextBox(&aHd, rSet); + break; + case 3: + pRet = ReadRect(&aHd, rSet); + break; + case 4: + pRet = ReadEllipse(&aHd, rSet); + break; + case 5: + pRet = ReadArc(&aHd, rSet); + break; + case 6: + pRet = ReadPolyLine(&aHd, rSet); + break; + case 7: + pRet = ReadCaptionBox(&aHd, rSet); + break; + default: // unknown + m_pStrm->SeekRel(SVBT16ToUInt16(aHd.cb) - sizeof(WW8_DPHEAD)); + break; + } + } + else + { + OSL_ENSURE( false, "+Grafik-Overlap" ); + } + rLeft = rLeft - SVBT16ToUInt16( aHd.cb ); + return pRet; +} + +void SwWW8ImplReader::ReadGrafLayer1( WW8PLCFspecial* pPF, long nGrafAnchorCp ) +{ + pPF->SeekPos( nGrafAnchorCp ); + WW8_FC nStartFc; + void* pF0; + if( !pPF->Get( nStartFc, pF0 ) ) + { + OSL_ENSURE( false, "+Where is the graphic (2) ?" ); + return; + } + WW8_FDOA* pF = static_cast<WW8_FDOA*>(pF0); + if( !SVBT32ToUInt32( pF->fc ) ) + { + OSL_ENSURE( false, "+Where is the graphic (3) ?" ); + return; + } + + bool bCouldSeek = checkSeek(*m_pStrm, SVBT32ToUInt32(pF->fc)); + OSL_ENSURE(bCouldSeek, "Invalid graphic offset"); + if (!bCouldSeek) + return; + + // read Draw-Header + WW8_DO aDo; + bool bCouldRead = checkRead(*m_pStrm, &aDo, sizeof(WW8_DO)); + OSL_ENSURE(bCouldRead, "Short graphic header"); + if (!bCouldRead) + return; + + short nLeft = SVBT16ToUInt16( aDo.cb ) - sizeof( WW8_DO ); + while (nLeft > static_cast<short>(sizeof(WW8_DPHEAD))) + { + SfxAllItemSet aSet( m_pDrawModel->GetItemPool() ); + if (SdrObject *pObject = ReadGrafPrimitive(nLeft, aSet)) + { + m_xWWZOrder->InsertDrawingObject(pObject, SVBT16ToUInt16(aDo.dhgt)); + + tools::Rectangle aRect(pObject->GetSnapRect()); + + const sal_uInt32 nCntRelTo = 3; + + // Adjustment is horizontally relative to... + static const sal_Int16 aHoriRelOriTab[nCntRelTo] = + { + text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin + text::RelOrientation::PAGE_FRAME, // 1 is page margin + text::RelOrientation::FRAME, // 2 is relative to paragraph + }; + + // Adjustment is vertically relative to... + static const sal_Int16 aVertRelOriTab[nCntRelTo] = + { + text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin + text::RelOrientation::PAGE_FRAME, // 1 is page margin + text::RelOrientation::FRAME, // 2 is relative to paragraph + }; + + const int nXAlign = aDo.bx < nCntRelTo ? aDo.bx : 0; + const int nYAlign = aDo.by < nCntRelTo ? aDo.by : 0; + + aSet.Put(SwFormatHoriOrient(aRect.Left(), text::HoriOrientation::NONE, + aHoriRelOriTab[ nXAlign ])); + aSet.Put(SwFormatVertOrient(aRect.Top(), text::VertOrientation::NONE, + aVertRelOriTab[ nYAlign ])); + + SwFrameFormat *pFrame = m_rDoc.getIDocumentContentOperations().InsertDrawObj( *m_pPaM, *pObject, aSet ); + pObject->SetMergedItemSet(aSet); + + if (SwDrawFrameFormat *pDrawFrame = dynamic_cast<SwDrawFrameFormat*>(pFrame)) + { + pDrawFrame->PosAttrSet(); + } + + AddAutoAnchor(pFrame); + } + } +} + +sal_Int32 SwMSDffManager::GetEscherLineMatch(MSO_LineStyle eStyle, + MSO_SPT eShapeType, sal_Int32 &rThick) +{ + sal_Int32 nOutsideThick = 0; + /* + Note: In contrast to the regular WinWord table and frame border width, + where the overall border width has to be calculated from the width of *one* + line, the data from ESCHER already contains the overall width [twips]! + + The WinWord default is 15 tw. We take for this our 20 tw line. + (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our + 0.05 pt hairline.) The hairline we only set by WinWord width up to max. + 0.5 pt. + */ + switch( eStyle ) + { + case mso_lineTriple: + case mso_lineSimple: + nOutsideThick = eShapeType != mso_sptTextBox ? rThick : rThick/2; + break; + case mso_lineDouble: + if (eShapeType == mso_sptTextBox) + { + nOutsideThick = rThick/6; + rThick = rThick*2/3; + } + else + nOutsideThick = rThick*2/3; + break; + case mso_lineThickThin: + if (eShapeType == mso_sptTextBox) + { + nOutsideThick = rThick*3/10; + rThick = rThick*4/5; + } + else + nOutsideThick = rThick*4/5; + break; + case mso_lineThinThick: + { + if (eShapeType == mso_sptTextBox) + { + nOutsideThick = rThick/10; + rThick = rThick*3/5; + } + else + nOutsideThick = rThick*3/5; + } + break; + default: + break; + } + return nOutsideThick; +} + +// Returns the thickness of the line outside the frame, the logic of +// words positioning of borders around floating objects is that of a +// disturbed mind. +sal_Int32 SwWW8ImplReader::MatchSdrBoxIntoFlyBoxItem(const Color& rLineColor, + MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineThick, + SvxBoxItem& rBox ) +{ + sal_Int32 nOutsideThick = 0; + if( !rLineThick ) + return nOutsideThick; + + SvxBorderLineStyle nIdx = SvxBorderLineStyle::NONE; + + sal_Int32 nLineThick=rLineThick; + nOutsideThick = SwMSDffManager::GetEscherLineMatch(eLineStyle, + eShapeType, rLineThick); + + /* + Note: In contrast to the regular WinWord table and frame border width, + where the overall border width has to be calculated from the width of *one* + line, the data from ESCHER already contains the overall width [twips]! + + The WinWord default is 15 tw. We take for this our 20 tw line. + (0.75 pt and 1.0 pt looking more similar on hardcopy than 0.75 pt and our + 0.05 pt hairline.) The hairline we only set by WinWord width up to max. + 0.5 pt. + */ + switch( +eLineStyle ) + { + // first the single lines + case mso_lineSimple: + nIdx = SvxBorderLineStyle::SOLID; + break; + // second the double lines + case mso_lineDouble: + nIdx = SvxBorderLineStyle::DOUBLE; + break; + case mso_lineThickThin: + nIdx = SvxBorderLineStyle::THICKTHIN_SMALLGAP; + break; + case mso_lineThinThick: + nIdx = SvxBorderLineStyle::THINTHICK_SMALLGAP; + break; + // We have no triple border, use double instead. + case mso_lineTriple: + nIdx = SvxBorderLineStyle::DOUBLE; + break; + // no line style is set + case MSO_LineStyle(USHRT_MAX): + break; + // erroneously not implemented line style is set + default: + OSL_ENSURE(false, "eLineStyle is not (yet) implemented!"); + break; + } + + switch( eDashing ) + { + case mso_lineDashGEL: + nIdx = SvxBorderLineStyle::DASHED; + break; + case mso_lineDotGEL: + nIdx = SvxBorderLineStyle::DOTTED; + break; + default: + break; + } + + if (SvxBorderLineStyle::NONE != nIdx) + { + SvxBorderLine aLine; + aLine.SetColor( rLineColor ); + + aLine.SetWidth( nLineThick ); // No conversion here, nLineThick is already in twips + aLine.SetBorderLineStyle(nIdx); + + for(SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>()) + { + // aLine is cloned by SetLine + rBox.SetLine(&aLine, nLine); + } + } + + return nOutsideThick; +} + +#define WW8ITEMVALUE(ItemSet,Id,Cast) ItemSet.GetItem<Cast>(Id)->GetValue() + +void SwWW8ImplReader::MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj, + SfxItemSet& rFlySet, MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, + tools::Rectangle& rInnerDist ) +{ + /* + attributes to be set on the frame + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SwFormatFrameSize if not set, set here + SvxLRSpaceItem set here + SvxULSpaceItem set here + SvxOpaqueItem (Currently not possible for frames! khz 10.2.1999) + SwFormatSurround already set + SwFormatVertOrient already set + SwFormatHoriOrient already set + SwFormatAnchor already set + SvxBoxItem set here + SvxBrushItem set here + SvxShadowItem set here + */ + + // 1. GraphicObject of documents? + GrafikCtor(); + + const SfxItemSet& rOldSet = pSdrObj->GetMergedItemSet(); + + // some Items can be taken over directly + static sal_uInt16 const aDirectMatch[] + { + RES_LR_SPACE, // outer spacing left/right: SvxLRSpaceItem + RES_UL_SPACE // outer spacing top/bottom: SvxULSpaceItem + }; + const SfxPoolItem* pPoolItem; + for(sal_uInt16 i : aDirectMatch) + if( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) ) + { + rFlySet.Put( *pPoolItem ); + } + + // take new XATTR items directly. Skip old RES_BACKGROUND if new FILLSTYLE taken. + bool bSkipResBackground = false; + SfxItemPool* pPool = rFlySet.GetPool(); + if ( pPool ) + { + for ( sal_uInt16 i = XATTR_START; i < XATTR_END; ++i ) + { + // Not all Fly types support XATTRs - skip unsupported attributes + SfxItemPool* pAttrPool = pPool->GetMasterPool(); + while ( pAttrPool && !pAttrPool->IsInRange(i) ) + pAttrPool = pAttrPool->GetSecondaryPool(); + if ( !pAttrPool ) + continue; + + if ( SfxItemState::SET == rOldSet.GetItemState(i, false, &pPoolItem) ) + { + rFlySet.Put( *pPoolItem ); + if ( i == XATTR_FILLSTYLE ) + { + const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pPoolItem)->GetValue(); + // Transparency forced in certain situations when fillstyle is none - use old logic for that case still + // which is especially needed for export purposes (tdf112618). + if ( eFill != drawing::FillStyle_NONE ) + bSkipResBackground = true; + } + } + } + } + + // now calculate the borders and build the box: The unit is needed for the + // frame SIZE! + SvxBoxItem aBox(sw::util::ItemGet<SvxBoxItem>(rFlySet, RES_BOX)); + // dashed or solid becomes solid + // WW-default: 0.75 pt = 15 twips + sal_Int32 nLineThick = 15, nOutside=0; + + // check if LineStyle is *really* set! + const SfxPoolItem* pItem; + + SfxItemState eState = rOldSet.GetItemState(XATTR_LINESTYLE,true,&pItem); + if( eState == SfxItemState::SET ) + { + // Now, that we know there is a line style we will make use the + // parameter given to us when calling the method... :-) + const Color aLineColor = rOldSet.Get(XATTR_LINECOLOR).GetColorValue(); + nLineThick = WW8ITEMVALUE(rOldSet, XATTR_LINEWIDTH, XLineWidthItem); + + if( !nLineThick ) + nLineThick = 1; // for Writer, zero is "no border", so set a minimal value + + nOutside = MatchSdrBoxIntoFlyBoxItem(aLineColor, eLineStyle, + eDashing, eShapeType, nLineThick, aBox); + } + + rInnerDist.AdjustLeft(nLineThick ); + rInnerDist.AdjustTop(nLineThick ); + rInnerDist.AdjustRight(nLineThick ); + rInnerDist.AdjustBottom(nLineThick ); + + rInnerDist.AdjustLeft( -(aBox.CalcLineWidth( SvxBoxItemLine::LEFT )) ); + rInnerDist.AdjustTop( -(aBox.CalcLineWidth( SvxBoxItemLine::TOP )) ); + rInnerDist.AdjustRight( -(aBox.CalcLineWidth( SvxBoxItemLine::RIGHT )) ); + rInnerDist.AdjustBottom( -(aBox.CalcLineWidth( SvxBoxItemLine::BOTTOM )) ); + + // set distances from box's border to text contained within the box + if( 0 < rInnerDist.Left() ) + aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Left()), SvxBoxItemLine::LEFT ); + if( 0 < rInnerDist.Top() ) + aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Top()), SvxBoxItemLine::TOP ); + if( 0 < rInnerDist.Right() ) + aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Right()), SvxBoxItemLine::RIGHT ); + if( 0 < rInnerDist.Bottom() ) + aBox.SetDistance( static_cast<sal_uInt16>(rInnerDist.Bottom()), SvxBoxItemLine::BOTTOM ); + + bool bFixSize = !(WW8ITEMVALUE(rOldSet, SDRATTR_TEXT_AUTOGROWHEIGHT, + SdrOnOffItem)); + + // Size: SwFormatFrameSize + if( SfxItemState::SET != rFlySet.GetItemState(RES_FRM_SIZE, false) ) + { + const tools::Rectangle& rSnapRect = pSdrObj->GetSnapRect(); + // if necessary adapt width and position of the framework: The + // recorded interior is to remain equally large despite thick edges. + rFlySet.Put( SwFormatFrameSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable, + rSnapRect.GetWidth() + 2*nOutside, + rSnapRect.GetHeight() + 2*nOutside) ); + } + else // If a size is set, adjust it to consider border thickness + { + SwFormatFrameSize aSize = rFlySet.Get(RES_FRM_SIZE); + + SwFormatFrameSize aNewSize(bFixSize ? SwFrameSize::Fixed : SwFrameSize::Variable, + aSize.GetWidth() + 2*nOutside, + aSize.GetHeight() + 2*nOutside); + aNewSize.SetWidthSizeType(aSize.GetWidthSizeType()); + rFlySet.Put( aNewSize ); + } + + // Sadly word puts escher borders outside the graphic, but orients the + // graphic in relation to the top left inside the border. We don't + if (nOutside) + { + SwFormatHoriOrient aHori = rFlySet.Get(RES_HORI_ORIENT); + aHori.SetPos(MakeSafePositioningValue(aHori.GetPos()-nOutside)); + rFlySet.Put(aHori); + + SwFormatVertOrient aVert = rFlySet.Get(RES_VERT_ORIENT); + aVert.SetPos(aVert.GetPos()-nOutside); + rFlySet.Put(aVert); + } + + // now set the border + rFlySet.Put( aBox ); + + // shadow of the box: SvxShadowItem + if( WW8ITEMVALUE(rOldSet, SDRATTR_SHADOW, SdrOnOffItem) ) + { + SvxShadowItem aShadow( RES_SHADOW ); + + const Color aShdColor = rOldSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue(); + const sal_Int32 nShdDistX = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWXDIST, + SdrMetricItem); + const sal_Int32 nShdDistY = WW8ITEMVALUE(rOldSet, SDRATTR_SHADOWYDIST, + SdrMetricItem); + + aShadow.SetColor( aShdColor ); + + aShadow.SetWidth(writer_cast<sal_uInt16>((std::abs( nShdDistX) + + std::abs( nShdDistY )) / 2 )); + + SvxShadowLocation eShdPosi; + if( 0 <= nShdDistX ) + { + if( 0 <= nShdDistY ) + eShdPosi = SvxShadowLocation::BottomRight; + else + eShdPosi = SvxShadowLocation::TopRight; + } + else + { + if( 0 <= nShdDistY ) + eShdPosi = SvxShadowLocation::BottomLeft; + else + eShdPosi = SvxShadowLocation::TopLeft; + } + aShadow.SetLocation( eShdPosi ); + + rFlySet.Put( aShadow ); + } + SvxBrushItem aBrushItem(COL_WHITE, RES_BACKGROUND); + bool bBrushItemOk = false; + sal_uInt8 nTrans = 0; + + // Separate transparency + eState = rOldSet.GetItemState(XATTR_FILLTRANSPARENCE, true, &pItem); + if (!bSkipResBackground && eState == SfxItemState::SET) + { + sal_uInt16 nRes = WW8ITEMVALUE(rOldSet, XATTR_FILLTRANSPARENCE, + XFillTransparenceItem); + nTrans = sal_uInt8((nRes * 0xFE) / 100); + aBrushItem.GetColor().SetTransparency(nTrans); + bBrushItemOk = true; + } + + // Background: SvxBrushItem + eState = rOldSet.GetItemState(XATTR_FILLSTYLE, true, &pItem); + if (!bSkipResBackground && eState == SfxItemState::SET) + { + const drawing::FillStyle eFill = static_cast<const XFillStyleItem*>(pItem)->GetValue(); + + switch (eFill) + { + default: + case drawing::FillStyle_NONE: + // Writer graphics don't have it yet + if (eShapeType != mso_sptPictureFrame) + { + aBrushItem.GetColor().SetTransparency(0xFE); + bBrushItemOk = true; + } + break; + case drawing::FillStyle_SOLID: + case drawing::FillStyle_GRADIENT: + { + const Color aColor = + rOldSet.Get(XATTR_FILLCOLOR).GetColorValue(); + aBrushItem.SetColor(aColor); + + if (bBrushItemOk) // has trans + aBrushItem.GetColor().SetTransparency(nTrans); + + bBrushItemOk = true; + } + break; + case drawing::FillStyle_HATCH: + break; + case drawing::FillStyle_BITMAP: + { + GraphicObject aGrfObj(rOldSet.Get(XATTR_FILLBITMAP).GetGraphicObject()); + const bool bTile(WW8ITEMVALUE(rOldSet, XATTR_FILLBMP_TILE, SfxBoolItem)); + + if(bBrushItemOk) // has trans + { + GraphicAttr aAttr(aGrfObj.GetAttr()); + + aAttr.SetTransparency(nTrans); + aGrfObj.SetAttr(aAttr); + } + + aBrushItem.SetGraphicObject(aGrfObj); + aBrushItem.SetGraphicPos(bTile ? GPOS_TILED : GPOS_AREA); + bBrushItemOk = true; + } + break; + } + } + + if (bBrushItemOk) + rFlySet.Put(aBrushItem); +} + +void SwWW8ImplReader::AdjustLRWrapForWordMargins( + const SvxMSDffImportRec &rRecord, SvxLRSpaceItem &rLR) +{ + sal_uInt32 nXRelTo = SvxMSDffImportRec::RELTO_DEFAULT; + if ( rRecord.nXRelTo ) + { + nXRelTo = *rRecord.nXRelTo; + } + + // Left adjustments - if horizontally aligned to left of + // margin or column then remove the left wrapping + if (rRecord.nXAlign == 1) + { + if ((nXRelTo == 0) || (nXRelTo == 2)) + rLR.SetLeft(sal_uInt16(0)); + } + + // Right adjustments - if horizontally aligned to right of + // margin or column then remove the right wrapping + if (rRecord.nXAlign == 3) + { + if ((nXRelTo == 0) || (nXRelTo == 2)) + rLR.SetRight(sal_uInt16(0)); + } + + // Inside margin, remove left wrapping + if ((rRecord.nXAlign == 4) && (nXRelTo == 0)) + { + rLR.SetLeft(sal_uInt16(0)); + } + + // Outside margin, remove left wrapping + if ((rRecord.nXAlign == 5) && (nXRelTo == 0)) + { + rLR.SetRight(sal_uInt16(0)); + } +} + +void SwWW8ImplReader::AdjustULWrapForWordMargins( + const SvxMSDffImportRec &rRecord, SvxULSpaceItem &rUL) +{ + sal_uInt32 nYRelTo = SvxMSDffImportRec::RELTO_DEFAULT; + if ( rRecord.nYRelTo ) + { + nYRelTo = *rRecord.nYRelTo; + } + + // Top adjustment - remove upper wrapping if aligned to page + // printable area or to page + if (rRecord.nYAlign == 1) + { + if ((nYRelTo == 0) || (nYRelTo == 1)) + rUL.SetUpper(sal_uInt16(0)); + } + + // Bottom adjustment - remove bottom wrapping if aligned to page or + // printable area or to page + if (rRecord.nYAlign == 3) + { + if ((nYRelTo == 0) || (nYRelTo == 1)) + rUL.SetLower(sal_uInt16(0)); + } + + // Remove top margin if aligned vertically inside margin + if ((rRecord.nYAlign == 4) && (nYRelTo == 0)) + rUL.SetUpper(sal_uInt16(0)); +} + +void SwWW8ImplReader::MapWrapIntoFlyFormat(SvxMSDffImportRec const * pRecord, + SwFrameFormat* pFlyFormat) +{ + if (!pRecord || !pFlyFormat) + return; + + if (pRecord->nDxWrapDistLeft || pRecord->nDxWrapDistRight) + { + SvxLRSpaceItem aLR(writer_cast<sal_uInt16>(pRecord->nDxWrapDistLeft), + writer_cast<sal_uInt16>(pRecord->nDxWrapDistRight), 0, 0, RES_LR_SPACE); + AdjustLRWrapForWordMargins(*pRecord, aLR); + pFlyFormat->SetFormatAttr(aLR); + } + if (pRecord->nDyWrapDistTop || pRecord->nDyWrapDistBottom) + { + SvxULSpaceItem aUL(writer_cast<sal_uInt16>(pRecord->nDyWrapDistTop), + writer_cast<sal_uInt16>(pRecord->nDyWrapDistBottom), RES_UL_SPACE); + AdjustULWrapForWordMargins(*pRecord, aUL); + pFlyFormat->SetFormatAttr(aUL); + } + + // If we are contoured and have a custom polygon... + if (pRecord->pWrapPolygon && pFlyFormat->GetSurround().IsContour()) + { + if (SwNoTextNode *pNd = GetNoTextNodeFromSwFrameFormat(*pFlyFormat)) + { + + /* + Gather round children and hear of a tale that will raise the + hairs on the back of your neck this dark halloween night. + + There is a polygon in word that describes the wrapping around + the graphic. + + Here are some sample values for the simplest case of a square + around some solid coloured graphics + + X Y Pixel size of graphic + TopLeft -54 21600 400x400 + Bottom Right 0 21546 + + TopLeft -108 21600 200x200 + Bottom Right 0 21492 + + TopLeft -216 21600 100x100 + Bottom Right 0 21384 + + TopLeft -432 21600 50x50 + Bottom Right 0 21168 + + TopLeft -76 21600 283x212 + Bottom Right 0 21498 + + So given that the size of the values remains pretty much the + same despite the size of the graphic, we can tell that the + polygon is measured in units that are independent of the + graphic. But why does the left corner move a different value + to the left each time, and why does the bottom move upwards + each time, when the right and top remain at the same value ? + + I have no idea, but clearly once we calculate the values out + we see that the left margin is always a fixed realworld + distance from the true left and the polygon bottom is the same + fixed value from the bottom. i.e. 15twips. + + So here we take our word provided polygon, shift it to the + right by 15twips and rescale it widthwise to shrink the width + a little to fit the now moved right margin back to where it + was, and stretch the height a little to make the bottom move + down the missing 15twips then we get a polygon that matches + what I actually see in word + */ + + tools::PolyPolygon aPoly(*pRecord->pWrapPolygon); + const Size &rSize = pNd->GetTwipSize(); + /* + Move to the left by 15twips, and rescale to + a) shrink right bound back to orig position + b) stretch bottom bound to where I think it should have been + in the first place + */ + Fraction aMoveHack(ww::nWrap100Percent, rSize.Width()); + aMoveHack *= Fraction(15, 1); + long nMove(aMoveHack); + aPoly.Move(nMove, 0); + + Fraction aHackX(ww::nWrap100Percent, ww::nWrap100Percent + nMove); + Fraction aHackY(ww::nWrap100Percent, ww::nWrap100Percent - nMove); + aPoly.Scale(double(aHackX), double(aHackY)); + + // Turn polygon back into units that match the graphic's + const Size &rOrigSize = pNd->GetGraphic().GetPrefSize(); + Fraction aMapPolyX(rOrigSize.Width(), ww::nWrap100Percent); + Fraction aMapPolyY(rOrigSize.Height(), ww::nWrap100Percent); + aPoly.Scale(double(aMapPolyX), double(aMapPolyY)); + + // #i47277# - contour is already in unit of the + // graphic preferred unit. Thus, call method <SetContour(..)> + pNd->SetContour(&aPoly); + } + } + else if (pFlyFormat->GetSurround().IsContour()) + { + // Contour is enabled, but no polygon is set: disable contour, because Word does not + // Writer-style auto-contour in that case. + SwFormatSurround aSurround(pFlyFormat->GetSurround()); + aSurround.SetContour(false); + pFlyFormat->SetFormatAttr(aSurround); + } +} + +static sal_Int32 lcl_ConvertCrop(sal_uInt32 const nCrop, sal_Int32 const nSize) +{ + // cast to sal_Int32 to handle negative crop properly + sal_Int32 const nIntegral(static_cast<sal_Int32>(nCrop) >> 16); + // fdo#77454: heuristic to detect mangled values written by old OOo/LO + if (abs(nIntegral) >= 50) // FIXME: what's a good cut-off? + { + SAL_INFO("sw.ww8", "ignoring suspiciously large crop: " << nIntegral); + return 0; + } + return (nIntegral * nSize) + (((nCrop & 0xffff) * nSize) >> 16); +} + +void +SwWW8ImplReader::SetAttributesAtGrfNode(SvxMSDffImportRec const*const pRecord, + SwFrameFormat const *pFlyFormat, WW8_FSPA const *pF ) +{ + const SwNodeIndex* pIdx = pFlyFormat->GetContent(false).GetContentIdx(); + SwGrfNode *const pGrfNd( + pIdx ? m_rDoc.GetNodes()[pIdx->GetIndex() + 1]->GetGrfNode() : nullptr); + if (pGrfNd) + { + Size aSz(pGrfNd->GetTwipSize()); + // use type <sal_uInt64> instead of sal_uLong to get correct results + // in the following calculations. + sal_uInt64 nHeight = aSz.Height(); + sal_uInt64 nWidth = aSz.Width(); + if (!nWidth && pF) + nWidth = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft); + else if (!nHeight && pF) + nHeight = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop); + + if( pRecord->nCropFromTop || pRecord->nCropFromBottom || + pRecord->nCropFromLeft || pRecord->nCropFromRight ) + { + SwCropGrf aCrop; // Cropping is stored in 'fixed floats' + // 16.16 (fraction times total + if( pRecord->nCropFromTop ) // image width or height resp.) + { + aCrop.SetTop(lcl_ConvertCrop(pRecord->nCropFromTop, nHeight)); + } + if( pRecord->nCropFromBottom ) + { + aCrop.SetBottom(lcl_ConvertCrop(pRecord->nCropFromBottom, nHeight)); + } + if( pRecord->nCropFromLeft ) + { + aCrop.SetLeft(lcl_ConvertCrop(pRecord->nCropFromLeft, nWidth)); + } + if( pRecord->nCropFromRight ) + { + aCrop.SetRight(lcl_ConvertCrop(pRecord->nCropFromRight, nWidth)); + } + + pGrfNd->SetAttr( aCrop ); + } + + bool bFlipH(pRecord->nFlags & ShapeFlag::FlipH); + bool bFlipV(pRecord->nFlags & ShapeFlag::FlipV); + if ( bFlipH || bFlipV ) + { + SwMirrorGrf aMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf(); + if( bFlipH ) + { + if( bFlipV ) + aMirror.SetValue(MirrorGraph::Both); + else + aMirror.SetValue(MirrorGraph::Vertical); + } + else + aMirror.SetValue(MirrorGraph::Horizontal); + + pGrfNd->SetAttr( aMirror ); + } + + if (pRecord->pObj) + { + const SfxItemSet& rOldSet = pRecord->pObj->GetMergedItemSet(); + // contrast + if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFCONTRAST, + SdrGrafContrastItem)) + { + SwContrastGrf aContrast( + WW8ITEMVALUE(rOldSet, + SDRATTR_GRAFCONTRAST, SdrGrafContrastItem)); + pGrfNd->SetAttr( aContrast ); + } + + // luminance + if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFLUMINANCE, + SdrGrafLuminanceItem)) + { + SwLuminanceGrf aLuminance(WW8ITEMVALUE(rOldSet, + SDRATTR_GRAFLUMINANCE, SdrGrafLuminanceItem)); + pGrfNd->SetAttr( aLuminance ); + } + // gamma + if (WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA, SdrGrafGamma100Item)) + { + double fVal = WW8ITEMVALUE(rOldSet, SDRATTR_GRAFGAMMA, + SdrGrafGamma100Item); + pGrfNd->SetAttr(SwGammaGrf(fVal/100.)); + } + + // drawmode + auto nGrafMode = rOldSet.GetItem<SdrGrafModeItem>(SDRATTR_GRAFMODE)->GetValue(); + if ( nGrafMode != GraphicDrawMode::Standard) + { + SwDrawModeGrf aDrawMode( nGrafMode ); + pGrfNd->SetAttr( aDrawMode ); + } + } + } +} + +SdrObject* SwWW8ImplReader::CreateContactObject(SwFrameFormat* pFlyFormat) +{ + if (pFlyFormat) + { + SdrObject* pNewObject = m_bNewDoc ? nullptr : pFlyFormat->FindRealSdrObject(); + if (!pNewObject) + pNewObject = pFlyFormat->FindSdrObject(); + if (!pNewObject && dynamic_cast< const SwFlyFrameFormat *>( pFlyFormat ) != nullptr) + { + SwFlyDrawContact* pContactObject(static_cast<SwFlyFrameFormat*>(pFlyFormat)->GetOrCreateContact()); + pNewObject = pContactObject->GetMaster(); + } + return pNewObject; + } + return nullptr; +} + +// Miserable miserable hack to fudge word's graphic layout in RTL mode to ours. +bool SwWW8ImplReader::MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth, + sal_Int16 eHoriOri, sal_Int16 eHoriRel) +{ + if (!IsRightToLeft()) + return false; + return RTLGraphicsHack(rLeft, nWidth, eHoriOri, eHoriRel, + m_aSectionManager.GetPageLeft(), + m_aSectionManager.GetPageRight(), + m_aSectionManager.GetPageWidth()); +} + +RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec* pRecord, + WW8_FSPA *pFSPA, SfxItemSet &rFlySet) +{ + OSL_ENSURE(pRecord || pFSPA, "give me something! to work with for anchoring"); + if (!pRecord && !pFSPA) + return RndStdIds::FLY_AT_PAGE; + bool bCurSectionVertical = m_aSectionManager.CurrentSectionIsVertical(); + + SvxMSDffImportRec aRecordFromFSPA; + if (!pRecord) + pRecord = &aRecordFromFSPA; + if (!(pRecord->nXRelTo) && pFSPA) + { + pRecord->nXRelTo = sal_Int32(pFSPA->nbx); + } + if (!(pRecord->nYRelTo) && pFSPA) + { + pRecord->nYRelTo = sal_Int32(pFSPA->nby); + } + + // nXAlign - abs. Position, Left, Centered, Right, Inside, Outside + // nYAlign - abs. Position, Top, Centered, Bottom, Inside, Outside + + // nXRelTo - Page printable area, Page, Column, Character + // nYRelTo - Page printable area, Page, Paragraph, Line + + const sal_uInt32 nCntXAlign = 6; + const sal_uInt32 nCntYAlign = 6; + + const sal_uInt32 nCntRelTo = 4; + + sal_uInt32 nXAlign = nCntXAlign > pRecord->nXAlign ? pRecord->nXAlign : 1; + sal_uInt32 nYAlign = nCntYAlign > pRecord->nYAlign ? pRecord->nYAlign : 1; + + if (pFSPA) + { + // #i52565# - try to handle special case for objects in tables regarding its X Rel + + // if X and Y Rel values are on default take it as a hint, that they have not been set + // by <SwMSDffManager::ProcessObj(..)> + const bool bXYRelHaveDefaultValues = *pRecord->nXRelTo == 2 && *pRecord->nYRelTo == 2; + if ( bXYRelHaveDefaultValues + && m_nInTable > 0 + && !bCurSectionVertical ) + { + if ( sal_uInt32(pFSPA->nby) != pRecord->nYRelTo ) + { + pRecord->nYRelTo = sal_uInt32(pFSPA->nby); + } + } + } + + sal_uInt32 nXRelTo = (pRecord->nXRelTo && nCntRelTo > pRecord->nXRelTo) ? *pRecord->nXRelTo : 1; + sal_uInt32 nYRelTo = (pRecord->nYRelTo && nCntRelTo > pRecord->nYRelTo) ? *pRecord->nYRelTo : 1; + + RndStdIds eAnchor = IsInlineEscherHack() ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_CHAR; // #i43718# + + SwFormatAnchor aAnchor( eAnchor ); + aAnchor.SetAnchor( m_pPaM->GetPoint() ); + rFlySet.Put( aAnchor ); + + if (pFSPA) + { + // #i18732# + // Given new layout where everything is changed to be anchored to + // character the following 4 tables may need to be changed. + + // horizontal Adjustment + static const sal_Int16 aHoriOriTab[ nCntXAlign ] = + { + text::HoriOrientation::NONE, // From left position + text::HoriOrientation::LEFT, // left + text::HoriOrientation::CENTER, // centered + text::HoriOrientation::RIGHT, // right + // #i36649# + // - inside -> text::HoriOrientation::LEFT and outside -> text::HoriOrientation::RIGHT + text::HoriOrientation::LEFT, // inside + text::HoriOrientation::RIGHT // outside + }; + + // generic vertical Adjustment + static const sal_Int16 aVertOriTab[ nCntYAlign ] = + { + text::VertOrientation::NONE, // From Top position + text::VertOrientation::TOP, // top + text::VertOrientation::CENTER, // centered + text::VertOrientation::BOTTOM, // bottom + text::VertOrientation::LINE_TOP, // inside (obscure) + text::VertOrientation::LINE_BOTTOM // outside (obscure) + }; + + // #i22673# - to-line vertical alignment + static const sal_Int16 aToLineVertOriTab[ nCntYAlign ] = + { + text::VertOrientation::NONE, // below + text::VertOrientation::LINE_BOTTOM, // top + text::VertOrientation::LINE_CENTER, // centered + text::VertOrientation::LINE_TOP, // bottom + text::VertOrientation::LINE_BOTTOM, // inside (obscure) + text::VertOrientation::LINE_TOP // outside (obscure) + }; + + // Adjustment is horizontally relative to... + static const sal_Int16 aHoriRelOriTab[nCntRelTo] = + { + text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin + text::RelOrientation::PAGE_FRAME, // 1 is page margin + text::RelOrientation::FRAME, // 2 is relative to column + text::RelOrientation::CHAR // 3 is relative to character + }; + + // Adjustment is vertically relative to... + // #i22673# - adjustment for new vertical alignment at top of line. + static const sal_Int16 aVertRelOriTab[nCntRelTo] = + { + text::RelOrientation::PAGE_PRINT_AREA, // 0 is page textarea margin + text::RelOrientation::PAGE_FRAME, // 1 is page margin + text::RelOrientation::FRAME, // 2 is relative to paragraph + text::RelOrientation::TEXT_LINE // 3 is relative to line + }; + + sal_Int16 eHoriOri = aHoriOriTab[ nXAlign ]; + sal_Int16 eHoriRel = aHoriRelOriTab[ nXRelTo ]; + + // #i36649# - adjustments for certain alignments + if ( eHoriOri == text::HoriOrientation::LEFT && eHoriRel == text::RelOrientation::PAGE_FRAME ) + { + // convert 'left to page' to 'from left -<width> to page text area' + eHoriOri = text::HoriOrientation::NONE; + eHoriRel = text::RelOrientation::PAGE_PRINT_AREA; + const long nWidth = pFSPA->nXaRight - pFSPA->nXaLeft; + pFSPA->nXaLeft = -nWidth; + pFSPA->nXaRight = 0; + } + else if ( eHoriOri == text::HoriOrientation::RIGHT && eHoriRel == text::RelOrientation::PAGE_FRAME ) + { + // convert 'right to page' to 'from left 0 to right page border' + eHoriOri = text::HoriOrientation::NONE; + eHoriRel = text::RelOrientation::PAGE_RIGHT; + const long nWidth = pFSPA->nXaRight - pFSPA->nXaLeft; + pFSPA->nXaLeft = 0; + pFSPA->nXaRight = nWidth; + } + + // #i24255# - position of floating screen objects in + // R2L layout are given in L2R layout, thus convert them of all + // floating screen objects, which are imported. + { + // Miserable miserable hack. + SwTwips nWidth = o3tl::saturating_sub(pFSPA->nXaRight, pFSPA->nXaLeft); + SwTwips nLeft = pFSPA->nXaLeft; + if (MiserableRTLGraphicsHack(nLeft, nWidth, eHoriOri, + eHoriRel)) + { + pFSPA->nXaLeft = nLeft; + pFSPA->nXaRight = pFSPA->nXaLeft + nWidth; + } + } + + // if the object is anchored inside a table cell, is horizontal aligned + // at frame|character and has wrap through, but its attribute + // 'layout in table cell' isn't set, convert its horizontal alignment to page text area. + // #i84783# - use new method <IsObjectLayoutInTableCell()> + if ( m_nInTable && + ( eHoriRel == text::RelOrientation::FRAME || eHoriRel == text::RelOrientation::CHAR ) && + pFSPA->nwr == 3 && + !IsObjectLayoutInTableCell( pRecord->nLayoutInTableCell ) ) + { + eHoriRel = text::RelOrientation::PAGE_PRINT_AREA; + } + + // Writer honours this wrap distance when aligned as "left" or "right", + // Word doesn't. Writer doesn't honour it when its "from left". + if (eHoriOri == text::HoriOrientation::LEFT) + pRecord->nDxWrapDistLeft=0; + else if (eHoriOri == text::HoriOrientation::RIGHT) + pRecord->nDxWrapDistRight=0; + + sal_Int16 eVertRel; + + eVertRel = aVertRelOriTab[ nYRelTo ]; // #i18732# + if ( bCurSectionVertical && nYRelTo == 2 ) + eVertRel = text::RelOrientation::PAGE_PRINT_AREA; + // #i22673# - fill <eVertOri> in dependence of <eVertRel> + sal_Int16 eVertOri; + if ( eVertRel == text::RelOrientation::TEXT_LINE ) + { + eVertOri = aToLineVertOriTab[ nYAlign ]; + } + else + { + eVertOri = aVertOriTab[ nYAlign ]; + } + + // Below line in word is a positive value, while in writer its + // negative + long nYPos = pFSPA->nYaTop; + // #i22673# + if ((eVertRel == text::RelOrientation::TEXT_LINE) && (eVertOri == text::VertOrientation::NONE)) + nYPos = -nYPos; + + SwFormatHoriOrient aHoriOri(MakeSafePositioningValue( bCurSectionVertical ? nYPos : pFSPA->nXaLeft ), + bCurSectionVertical ? eVertOri : eHoriOri, + bCurSectionVertical ? eVertRel : eHoriRel); + if( 4 <= nXAlign ) + aHoriOri.SetPosToggle(true); + rFlySet.Put( aHoriOri ); + + rFlySet.Put(SwFormatVertOrient(MakeSafePositioningValue( !bCurSectionVertical ? nYPos : -pFSPA->nXaRight ), + !bCurSectionVertical ? eVertOri : eHoriOri, + !bCurSectionVertical ? eVertRel : eHoriRel )); + } + + return eAnchor; +} + +// #i84783# +bool SwWW8ImplReader::IsObjectLayoutInTableCell( const sal_uInt32 nLayoutInTableCell ) const +{ + bool bIsObjectLayoutInTableCell = false; + + if ( m_bVer8 ) + { + sal_uInt16 nWWVersion = m_xWwFib->m_nProduct & 0xE000; + if (nWWVersion == 0) + { + // 0 nProduct can happen for Word >97 as well, check cswNew in this case instead. + if (m_xWwFib->m_cswNew > 0) + { + // This is Word >=2000. + nWWVersion = 0x2000; + } + } + + switch ( nWWVersion ) + { + case 0x0000: // version 8 aka Microsoft Word 97 + { + bIsObjectLayoutInTableCell = false; + OSL_ENSURE( nLayoutInTableCell == 0xFFFFFFFF, + "no explicit object attribute layout in table cell expected." ); + } + break; + case 0x2000: // version 9 aka Microsoft Word 2000 + case 0x4000: // version 10 aka Microsoft Word 2002 + case 0x6000: // version 11 aka Microsoft Word 2003 + case 0x8000: // version 12 aka Microsoft Word 2007 + case 0xC000: // version 14 aka Microsoft Word 2010 + case 0xE000: // version 15 aka Microsoft Word 2013 + { + // #i98037# + // adjustment of conditions needed after deeper analysis of + // certain test cases. + if ( nLayoutInTableCell == 0xFFFFFFFF || // no explicit attribute value given + nLayoutInTableCell == 0x80008000 || + ( nLayoutInTableCell & 0x02000000 && + !(nLayoutInTableCell & 0x80000000 ) ) ) + { + bIsObjectLayoutInTableCell = true; + } + else + { + // Documented in [MS-ODRAW], 2.3.4.44 "Group Shape Boolean Properties". + bool fUsefLayoutInCell = (nLayoutInTableCell & 0x80000000) >> 31; + bool fLayoutInCell = (nLayoutInTableCell & 0x8000) >> 15; + bIsObjectLayoutInTableCell = fUsefLayoutInCell && fLayoutInCell; + } + } + break; + default: + { + OSL_FAIL( "unknown version." ); + } + } + } + + return bIsObjectLayoutInTableCell; +} + +SwFrameFormat* SwWW8ImplReader::Read_GrafLayer( long nGrafAnchorCp ) +{ + if( m_nIniFlags & WW8FL_NO_GRAFLAYER ) + return nullptr; + + ::SetProgressState(m_nProgress, m_pDocShell); // Update + + m_nDrawCpO = 0; + m_bDrawCpOValid = m_xWwFib->GetBaseCp(m_xPlcxMan->GetManType() == MAN_HDFT ? MAN_TXBX_HDFT : MAN_TXBX, &m_nDrawCpO); + + GrafikCtor(); + + WW8PLCFspecial* pPF = m_xPlcxMan->GetFdoa(); + if( !pPF ) + { + OSL_ENSURE( false, "Where is the graphic (1) ?" ); + return nullptr; + } + + if( m_bVer67 ) + { + long nOldPos = m_pStrm->Tell(); + + m_nDrawXOfs = m_nDrawYOfs = 0; + ReadGrafLayer1( pPF, nGrafAnchorCp ); + + m_pStrm->Seek( nOldPos ); + return nullptr; + } + + // Normal case of Word 8+ version stuff + pPF->SeekPos( nGrafAnchorCp ); + + WW8_FC nStartFc; + void* pF0; + if( !pPF->Get( nStartFc, pF0 ) ){ + OSL_ENSURE( false, "+Where is the graphic (2) ?" ); + return nullptr; + } + + WW8_FSPA_SHADOW* pFS = static_cast<WW8_FSPA_SHADOW*>(pF0); + WW8_FSPA* pF; + WW8_FSPA aFSFA; + pF = &aFSFA; + WW8FSPAShadowToReal( pFS, pF ); + if( !pF->nSpId ) + { + OSL_ENSURE( false, "+Where is the graphic (3) ?" ); + return nullptr; + } + + if (!m_xMSDffManager->GetModel()) + m_xMSDffManager->SetModel(m_pDrawModel, 1440); + + tools::Rectangle aRect(pF->nXaLeft, pF->nYaTop, pF->nXaRight, pF->nYaBottom); + SvxMSDffImportData aData( aRect ); + + /* + #i20540# + The SdrOle2Obj will try and manage any ole objects it finds, causing all + sorts of trouble later on + */ + SwDocShell* pPersist = m_rDoc.GetDocShell(); + m_rDoc.SetDocShell(nullptr); // #i20540# Persist guard + + SdrObject* pObject = nullptr; + bool bOk = (m_xMSDffManager->GetShape(pF->nSpId, pObject, aData) && pObject); + + m_rDoc.SetDocShell(pPersist); // #i20540# Persist guard + + if (!bOk) + { + OSL_ENSURE( false, "Where is the Shape ?" ); + return nullptr; + } + + // tdf#118375 Word relates position to the unrotated rectangle, + // Writer uses the rotated one. + if (pObject->GetRotateAngle()) + { + tools::Rectangle aObjSnapRect(pObject->GetSnapRect()); // recalculates the SnapRect + pF->nXaLeft = aObjSnapRect.Left(); + pF->nYaTop = aObjSnapRect.Top(); + pF->nXaRight = aObjSnapRect.Right(); + pF->nYaBottom = aObjSnapRect.Bottom(); + } + + bool bDone = false; + SdrObject* pOurNewObject = nullptr; + bool bReplaceable = false; + + switch (SdrObjKind(pObject->GetObjIdentifier())) + { + case OBJ_GRAF: + bReplaceable = true; + bDone = true; + break; + case OBJ_OLE2: + bReplaceable = true; + break; + default: + break; + + } + + // when in a header or footer word appears to treat all elements as wrap through + + // determine wrapping mode + SfxItemSet aFlySet(m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1, XATTR_START, XATTR_END>{}); + Reader::ResetFrameFormatAttrs(aFlySet); // tdf#122425: Explicitly remove borders and spacing + css::text::WrapTextMode eSurround = css::text::WrapTextMode_PARALLEL; + bool bContour = false; + switch (pF->nwr) + { + case 0: // 0 like 2, but doesn't require absolute object + case 2: // 2 wrap around absolute object + eSurround = css::text::WrapTextMode_PARALLEL; + break; + case 1: // 1 no text next to shape + eSurround = css::text::WrapTextMode_NONE; + break; + case 3: // 3 wrap as if no object present + eSurround = css::text::WrapTextMode_THROUGH; + break; + case 4: // 4 wrap tightly around object + case 5: // 5 wrap tightly, but allow holes + eSurround = css::text::WrapTextMode_PARALLEL; + bContour = true; + break; + } + + // if mode 2 or 4 also regard the additional parameters + if ( (2 == pF->nwr) || (4 == pF->nwr) ) + { + switch( pF->nwrk ) + { + // 0 wrap both sides + case 0: + eSurround = css::text::WrapTextMode_PARALLEL; + break; + // 1 wrap only on left + case 1: + eSurround = css::text::WrapTextMode_LEFT; + break; + // 2 wrap only on right + case 2: + eSurround = css::text::WrapTextMode_RIGHT; + break; + // 3 wrap only on largest side + case 3: + eSurround = css::text::WrapTextMode_DYNAMIC; + break; + } + } + + SwFormatSurround aSur( eSurround ); + aSur.SetContour( bContour ); + aSur.SetOutside(true); // Winword can only do outside contours + aFlySet.Put( aSur ); + + // now position imported object correctly and so on (can be a whole group) + + OSL_ENSURE(!((aData.size() != 1) && bReplaceable), + "Replaceable drawing with > 1 entries ?"); + + if (aData.size() != 1) + bReplaceable = false; + + /* + Get the record for top level object, so we can get the word anchoring + and wrapping information for it. + */ + SvxMSDffImportRec* pRecord = aData.find(pObject); + OSL_ENSURE(pRecord, "how did that happen?"); + if (!pRecord) + { + // remove old object from the Z-Order list + m_xMSDffManager->RemoveFromShapeOrder(pObject); + // and delete the object + SdrObject::Free(pObject); + return nullptr; + } + const bool bLayoutInTableCell = + m_nInTable && IsObjectLayoutInTableCell( pRecord->nLayoutInTableCell ); + + // #i18732# - Switch on 'follow text flow', if object is laid out + // inside table cell + if (bLayoutInTableCell) + { + SwFormatFollowTextFlow aFollowTextFlow( true ); + aFlySet.Put( aFollowTextFlow ); + } + + // #i21847# + // Some shapes are set to *hidden*, don't import those ones. + if (pRecord->bHidden) + { + // remove old object from the Z-Order list + m_xMSDffManager->RemoveFromShapeOrder(pObject); + // and delete the object + SdrObject::Free(pObject); + return nullptr; + } + + sal_uInt16 nCount = pObject->GetUserDataCount(); + if(nCount) + { + OUString lnName, aObjName, aTarFrame; + for (sal_uInt16 i = 0; i < nCount; i++ ) + { + SdrObjUserData* pData = pObject->GetUserData( i ); + if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw + && pData->GetId() == SW_UD_IMAPDATA) + { + SwMacroInfo* macInf = dynamic_cast<SwMacroInfo*>(pData); + if( macInf && macInf->GetShapeId() == pF->nSpId ) + { + lnName = macInf->GetHlink(); + aObjName = macInf->GetName(); + aTarFrame = macInf->GetTarFrame(); + break; + } + } + } + SwFormatURL* pFormatURL = new SwFormatURL(); + pFormatURL->SetURL( lnName, false ); + if (!aObjName.isEmpty()) + pFormatURL->SetName(aObjName); + if (!aTarFrame.isEmpty()) + pFormatURL->SetTargetFrameName(aTarFrame); + pFormatURL->SetMap(nullptr); + aFlySet.Put(*pFormatURL); + } + + // If we are to be "below text" then we are not to be opaque + // #i14045# MM If we are in a header or footer then make the object transparent + // Not exactly like word but close enough for now + + // both flags <bBelowText> and <bDrawHell> have to be set to move object into the background. + // #i46794# - it reveals that value of flag <bBelowText> can be neglected. + const bool bMoveToBackgrd = pRecord->bDrawHell || + ( ( m_bIsHeader || m_bIsFooter ) && pF->nwr == 3 ); + if ( bMoveToBackgrd ) + aFlySet.Put(SvxOpaqueItem(RES_OPAQUE,false)); + + OUString aObjName = pObject->GetName(); + + SwFrameFormat* pRetFrameFormat = nullptr; + if (bReplaceable) + { + // Single graphics or ole objects + pRetFrameFormat = ImportReplaceableDrawables(pObject, pOurNewObject, pRecord, + pF, aFlySet); + } + else + { + // Drawing objects, (e.g. ovals or drawing groups) + if (pF->bRcaSimple) + { + pF->nbx = WW8_FSPA::RelPageBorder; + pF->nby = WW8_FSPA::RelPageBorder; + } + + RndStdIds eAnchor = ProcessEscherAlign(pRecord, pF, aFlySet); + + // Should we, and is it possible to make this into a writer textbox + if ((!(m_nIniFlags1 & WW8FL_NO_FLY_FOR_TXBX)) && pRecord->bReplaceByFly) + { + pRetFrameFormat = ConvertDrawTextToFly(pObject, pOurNewObject, pRecord, + eAnchor, pF, aFlySet); + if (pRetFrameFormat) + bDone = true; + } + + if (!bDone) + { + sw::util::SetLayer aSetLayer(m_rDoc); + if ( bMoveToBackgrd ) + aSetLayer.SendObjectToHell(*pObject); + else + aSetLayer.SendObjectToHeaven(*pObject); + + if (!IsInlineEscherHack()) + { + /* Need to make sure that the correct layer ordering is applied. */ + // pass information, if object is in page header|footer to method. + m_xWWZOrder->InsertEscherObject( pObject, pF->nSpId, + m_bIsHeader || m_bIsFooter ); + } + else + { + m_xWWZOrder->InsertTextLayerObject(pObject); + } + + pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aFlySet ); + + OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() == + eAnchor, "Not the anchor type requested!"); + + /* + Insert text if necessary into textboxes contained in groups. + */ + for (const auto& it : aData) + { + pRecord = it.get(); + if (pRecord->pObj && pRecord->aTextId.nTxBxS) + { // #i52825# pRetFrameFormat can be NULL + pRetFrameFormat = MungeTextIntoDrawBox( + pRecord, nGrafAnchorCp, pRetFrameFormat); + } + } + } + } + + // #i44344#, #i44681# - positioning attributes already set + if ( pRetFrameFormat /*#i52825# */ && dynamic_cast< const SwDrawFrameFormat *>( pRetFrameFormat ) != nullptr ) + { + static_cast<SwDrawFrameFormat*>(pRetFrameFormat)->PosAttrSet(); + } + if (!IsInlineEscherHack()) + MapWrapIntoFlyFormat(pRecord, pRetFrameFormat); + + // Set frame name with object name + if( pRetFrameFormat /*#i52825# */ && !aObjName.isEmpty() ) + pRetFrameFormat->SetName( aObjName ); + return AddAutoAnchor(pRetFrameFormat); +} + +SwFrameFormat *SwWW8ImplReader::AddAutoAnchor(SwFrameFormat *pFormat) +{ + /* + * anchored to character at the current position will move along the + * paragraph as text is added because we are at the insertion point. + * + * Leave to later and set the correct location then. + */ + if (pFormat && (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)) + { + m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), pFormat); + } + return pFormat; +} + +SwFrameFormat* SwWW8ImplReader::MungeTextIntoDrawBox(SvxMSDffImportRec *pRecord, + long nGrafAnchorCp, SwFrameFormat* pRetFrameFormat) +{ + SdrObject* pTrueObject = pRecord->pObj; + + SdrTextObj* pSdrTextObj; + + // check for group object (e.g. two parentheses) + if (SdrObjGroup* pThisGroup = dynamic_cast<SdrObjGroup*>( pRecord->pObj) ) + { + // Group objects don't have text. Insert a text object into + // the group for holding the text. + pSdrTextObj = new SdrRectObj( + *m_pDrawModel, + OBJ_TEXT, + pThisGroup->GetCurrentBoundRect()); + + SfxItemSet aSet(m_pDrawModel->GetItemPool()); + aSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + aSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + aSet.Put(SdrTextFitToSizeTypeItem( drawing::TextFitToSizeType_NONE )); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pSdrTextObj->SetMergedItemSet(aSet); + pSdrTextObj->NbcSetLayer( pThisGroup->GetLayer() ); + pThisGroup->GetSubList()->NbcInsertObject(pSdrTextObj); + } + else + pSdrTextObj = dynamic_cast<SdrTextObj*>( pRecord->pObj ); + + if( pSdrTextObj ) + { + Size aObjSize(pSdrTextObj->GetSnapRect().GetWidth(), + pSdrTextObj->GetSnapRect().GetHeight()); + + // Object is part of a group? + SdrObject* pGroupObject = pSdrTextObj->getParentSdrObjectFromSdrObject(); + + const size_t nOrdNum = pSdrTextObj->GetOrdNum(); + bool bEraseThisObject; + InsertTxbxText( pSdrTextObj, &aObjSize, pRecord->aTextId.nTxBxS, + pRecord->aTextId.nSequence, nGrafAnchorCp, pRetFrameFormat, + (pSdrTextObj != pTrueObject) || (nullptr != pGroupObject), + bEraseThisObject, nullptr, nullptr, nullptr, nullptr, pRecord); + + // was this object replaced ?? + if (bEraseThisObject) + { + if( pGroupObject || (pSdrTextObj != pTrueObject) ) + { + // Object is already replaced by a new SdrGrafObj (in the group + // and) the Drawing-Page. + + SdrObject* pNewObj = pGroupObject ? + pGroupObject->GetSubList()->GetObj(nOrdNum) : pTrueObject; + if (pSdrTextObj != pNewObj) + { + // Replace object in the Z-Order-List + m_xMSDffManager->ExchangeInShapeOrder(pSdrTextObj, 0, pNewObj); + // now delete object + SdrObject::Free( pRecord->pObj ); + // and save the new object. + pRecord->pObj = pNewObj; + } + } + else + { + // remove the object from Z-Order list + m_xMSDffManager->RemoveFromShapeOrder( pSdrTextObj ); + // take the object from the drawing page + if( pSdrTextObj->getSdrPageFromSdrObject() ) + m_pDrawPg->RemoveObject( pSdrTextObj->GetOrdNum() ); + // and delete FrameFormat, because replaced by graphic + // (this also deletes the object) + m_rDoc.DelFrameFormat( pRetFrameFormat ); + pRetFrameFormat = nullptr; + // also delete the object record + pRecord->pObj = nullptr; + } + } + else + { + // use ww8-default border distance + SfxItemSet aItemSet(m_pDrawModel->GetItemPool(), + svl::Items<SDRATTR_TEXT_LEFTDIST, SDRATTR_TEXT_LOWERDIST>{}); + aItemSet.Put( makeSdrTextLeftDistItem( pRecord->nDxTextLeft ) ); + aItemSet.Put( makeSdrTextRightDistItem( pRecord->nDxTextRight ) ); + aItemSet.Put( makeSdrTextUpperDistItem( pRecord->nDyTextTop ) ); + aItemSet.Put( makeSdrTextLowerDistItem( pRecord->nDyTextBottom ) ); + pSdrTextObj->SetMergedItemSetAndBroadcast(aItemSet); + } + } + return pRetFrameFormat; +} + +SwFlyFrameFormat* SwWW8ImplReader::ConvertDrawTextToFly(SdrObject* &rpObject, + SdrObject* &rpOurNewObject, SvxMSDffImportRec const * pRecord, RndStdIds eAnchor, + WW8_FSPA const *pF, SfxItemSet &rFlySet) +{ + SwFlyFrameFormat* pRetFrameFormat = nullptr; + long nStartCp; + long nEndCp; + + // Check if this textbox chain contains text as conversion of an empty + // chain would not make sense. + if ( TxbxChainContainsRealText(pRecord->aTextId.nTxBxS,nStartCp,nEndCp) ) + { + // The Text is not read into SdrTextObj! Rather insert a frame and + // insert the text from nStartCp to nEndCp. + + // More attributes can be used in a frame compared to the + // Edit-Engine, and it can contain field, OLEs or graphics... + tools::Rectangle aInnerDist(pRecord->nDxTextLeft, pRecord->nDyTextTop, + pRecord->nDxTextRight, pRecord->nDyTextBottom); + + SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, pF->nXaRight - pF->nXaLeft, pF->nYaBottom - pF->nYaTop); + aFrameSize.SetWidthSizeType(pRecord->bAutoWidth ? SwFrameSize::Variable : SwFrameSize::Fixed); + rFlySet.Put(aFrameSize); + + MatchSdrItemsIntoFlySet( rpObject, rFlySet, pRecord->eLineStyle, + pRecord->eLineDashing, pRecord->eShapeType, aInnerDist ); + + SdrTextObj *pSdrTextObj = dynamic_cast<SdrTextObj*>(rpObject); + if (pSdrTextObj && pSdrTextObj->IsVerticalWriting()) + rFlySet.Put(SvxFrameDirectionItem(SvxFrameDirection::Vertical_RL_TB, RES_FRAMEDIR)); + + pRetFrameFormat = m_rDoc.MakeFlySection(eAnchor, m_pPaM->GetPoint(), &rFlySet); + OSL_ENSURE(pRetFrameFormat->GetAnchor().GetAnchorId() == eAnchor, + "Not the anchor type requested!"); + + // if everything is OK, find pointer on new object and correct + // Z-order list (or delete entry) + rpOurNewObject = CreateContactObject(pRetFrameFormat); + + // remove old object from the Z-Order list + m_xMSDffManager->RemoveFromShapeOrder( rpObject ); + + // and delete the object + SdrObject::Free( rpObject ); + /* + NB: only query pOrgShapeObject starting here! + */ + + if (rpOurNewObject) + { + /* + We do not store our rpOutNewObject in the ShapeOrder because we + have a FrameFormat from which we can regenerate the contact object when + we need it. Because, we can have frames anchored to paragraphs in + header/footers and we can copy header/footers, if we do copy a + header/footer with a nonpage anchored frame in it then the contact + objects are invalidated. Under this condition the FrameFormat will be + updated to reflect this change and can be used to get a new + contact object, while a raw rpOutNewObject stored here becomes + deleted and useless. + */ + m_xMSDffManager->StoreShapeOrder(pF->nSpId, + (static_cast<sal_uLong>(pRecord->aTextId.nTxBxS) << 16) + + pRecord->aTextId.nSequence, nullptr, pRetFrameFormat); + + // The Contact object has to be inserted into the draw page, so + // SwWW8ImplReader::LoadDoc1() can determine the z-order. + if (!rpOurNewObject->IsInserted()) + { + // pass information, if object is in page header|footer to method. + m_xWWZOrder->InsertEscherObject( rpOurNewObject, pF->nSpId, + m_bIsHeader || m_bIsFooter ); + } + } + + // Box-0 receives the text for the whole chain! + if( !pRecord->aTextId.nSequence ) + { + // save flags etc and reset them + WW8ReaderSave aSave( this ); + + MoveInsideFly(pRetFrameFormat); + + m_xWWZOrder->InsideEscher(pF->nSpId); + + // read in the text + m_bTxbxFlySection = true; + bool bJoined = ReadText(nStartCp, (nEndCp-nStartCp), + MAN_MAINTEXT == m_xPlcxMan->GetManType() ? + MAN_TXBX : MAN_TXBX_HDFT); + + m_xWWZOrder->OutsideEscher(); + + MoveOutsideFly(pRetFrameFormat, aSave.GetStartPos(),!bJoined); + + aSave.Restore( this ); + + StripNegativeAfterIndent(pRetFrameFormat); + } + + } + return pRetFrameFormat; +} + +void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord, + SfxItemSet &rFlySet) +{ + if (rRecord.bVFlip || rRecord.bHFlip) + { + MirrorGraph eType(MirrorGraph::Dont); + if (rRecord.bVFlip && rRecord.bHFlip) + eType = MirrorGraph::Both; + else if (rRecord.bVFlip) + eType = MirrorGraph::Horizontal; + else + eType = MirrorGraph::Vertical; + rFlySet.Put( SwMirrorGrf(eType) ); + } +} + +SwFlyFrameFormat* SwWW8ImplReader::ImportReplaceableDrawables( SdrObject* &rpObject, + SdrObject* &rpOurNewObject, SvxMSDffImportRec* pRecord, WW8_FSPA *pF, + SfxItemSet &rFlySet ) +{ + SwFlyFrameFormat* pRetFrameFormat = nullptr; + sal_Int32 nWidthTw = o3tl::saturating_sub(pF->nXaRight, pF->nXaLeft); + if (0 > nWidthTw) + nWidthTw = 0; + sal_Int32 nHeightTw = o3tl::saturating_sub(pF->nYaBottom, pF->nYaTop); + if (0 > nHeightTw) + nHeightTw = 0; + + ProcessEscherAlign(pRecord, pF, rFlySet); + + rFlySet.Put(SwFormatFrameSize(SwFrameSize::Fixed, nWidthTw, nHeightTw)); + + SfxItemSet aGrSet(m_rDoc.GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, RES_GRFATR_END-1>{}); + + if (pRecord) + { + // Note that the escher inner distance only seems to be honoured in + // word for textboxes, not for graphics and ole objects. + tools::Rectangle aInnerDist(0, 0, 0, 0); + + MatchSdrItemsIntoFlySet(rpObject, rFlySet, pRecord->eLineStyle, + pRecord->eLineDashing, pRecord->eShapeType, aInnerDist); + + MatchEscherMirrorIntoFlySet(*pRecord, aGrSet); + } + + OUString aObjectName(rpObject->GetName()); + if (OBJ_OLE2 == SdrObjKind(rpObject->GetObjIdentifier())) + pRetFrameFormat = InsertOle(*static_cast<SdrOle2Obj*>(rpObject), rFlySet, &aGrSet); + else + { + const SdrGrafObj *pGrf = static_cast<const SdrGrafObj*>(rpObject); + bool bDone = false; + if (pGrf->IsLinkedGraphic() && !pGrf->GetFileName().isEmpty()) + { + GraphicType eType = pGrf->GetGraphicType(); + OUString aGrfName( + URIHelper::SmartRel2Abs( + INetURLObject(m_sBaseURL), pGrf->GetFileName(), + URIHelper::GetMaybeFileHdl())); + // correction of fix for issue #i10939#: + // One of the two conditions have to be true to insert the graphic + // as a linked graphic - + if (GraphicType::NONE == eType || CanUseRemoteLink(aGrfName)) + { + pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, aGrfName, OUString(), nullptr, + &rFlySet, &aGrSet, nullptr); + bDone = true; + } + } + if (!bDone) + { + const Graphic& rGraph = pGrf->GetGraphic(); + pRetFrameFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, OUString(), OUString(), &rGraph, + &rFlySet, &aGrSet, nullptr); + } + } + + if (pRetFrameFormat) + { + if( pRecord ) + { + if( OBJ_OLE2 != SdrObjKind(rpObject->GetObjIdentifier()) ) + SetAttributesAtGrfNode( pRecord, pRetFrameFormat, pF ); + } + // avoid multiple occurrences of the same graphic name + m_aGrfNameGenerator.SetUniqueGraphName(pRetFrameFormat, aObjectName); + } + // if everything is OK, determine pointer to new object and correct + // Z-Order-List accordingly (or delete entry) + rpOurNewObject = CreateContactObject(pRetFrameFormat); + + // remove old object from Z-Order-List + m_xMSDffManager->RemoveFromShapeOrder( rpObject ); + // remove from Drawing-Page + if( rpObject->getSdrPageFromSdrObject() ) + m_pDrawPg->RemoveObject( rpObject->GetOrdNum() ); + + // and delete the object + SdrObject::Free( rpObject ); + /* + Warning: from now on query only pOrgShapeObject! + */ + + // add Contact-Object to the Z-Order-List and the page + if (rpOurNewObject) + { + if (!m_bHdFtFootnoteEdn) + m_xMSDffManager->StoreShapeOrder(pF->nSpId, 0, rpOurNewObject ); + + // The Contact-Object MUST be set in the Draw-Page, so that in + // SwWW8ImplReader::LoadDoc1() the Z-Order can be defined !!! + if (!rpOurNewObject->IsInserted()) + { + // pass information, if object is in page header|footer to method. + m_xWWZOrder->InsertEscherObject( rpOurNewObject, pF->nSpId, + m_bIsHeader || m_bIsFooter ); + } + } + return pRetFrameFormat; +} + +void SwWW8ImplReader::GrafikCtor() // For SVDraw and VCControls and Escher +{ + if (!m_pDrawModel) + { + m_rDoc.getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed + m_pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel(); + OSL_ENSURE(m_pDrawModel, "Cannot create DrawModel"); + m_pDrawPg = m_pDrawModel->GetPage(0); + + m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages)); + m_xMSDffManager->SetModel(m_pDrawModel, 1440); + /* + Now the dff manager always needs a controls converter as well, but a + control converter may still exist without a dffmanager. + */ + m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM)); + + m_xWWZOrder.reset(new wwZOrderer(sw::util::SetLayer(m_rDoc), m_pDrawPg, + m_xMSDffManager->GetShapeOrders())); + } +} + +void SwWW8ImplReader::GrafikDtor() +{ + m_pDrawEditEngine.reset(); // maybe created by graphic + m_xWWZOrder.reset(); // same +} + +void SwWW8FltAnchorStack::AddAnchor(const SwPosition& rPos, SwFrameFormat *pFormat) +{ + OSL_ENSURE(pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR, + "Don't use fltanchors with inline frames, slap!"); + NewAttr(rPos, SwFltAnchor(pFormat)); +} + +void SwWW8FltAnchorStack::Flush() +{ + size_t nCnt = size(); + for (size_t i=0; i < nCnt; ++i) + { + SwFltStackEntry &rEntry = (*this)[i]; + SwPosition aDummy(rEntry.m_aMkPos.m_nNode); + SetAttrInDoc(aDummy, rEntry); + DeleteAndDestroy(i--); + --nCnt; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8graf.hxx b/sw/source/filter/ww8/ww8graf.hxx new file mode 100644 index 000000000..3b05c53d2 --- /dev/null +++ b/sw/source/filter/ww8/ww8graf.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8GRAF_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8GRAF_HXX + +#include <vector> +#include <stack> + +#include <filter/msfilter/msdffimp.hxx> +#include <svx/svdpage.hxx> + +#include "writerhelper.hxx" +#include "ww8struc.hxx" + +struct EscherShape +{ + sal_uLong mnEscherShapeOrder; + sal_uLong mnNoInlines; + // new member <mbInHeaderFooter> + bool mbInHeaderFooter; + EscherShape( sal_uLong nEscherShapeOrder, + bool _bInHeaderFooter ) + : mnEscherShapeOrder(nEscherShapeOrder), + mnNoInlines(0), + mbInHeaderFooter( _bInHeaderFooter ) + {} +}; + +class wwZOrderer +{ +private: + // consider that objects in page header/footer + // are always behind objects in page body. Thus, assure, that in vector + // <maEscherLayer> objects in page header|footer are inserted before + // objects in page body - see method <GetEscherObjectPos(..)>. + //No of objects in doc before starting (always 0 unless using file->insert + //and probably 0 then as well + std::vector<EscherShape> maEscherLayer; + typedef std::vector<EscherShape>::iterator myeiter; + + std::vector<short> maDrawHeight; + + std::stack<sal_uInt16> maIndexes; + + sw::util::SetLayer maSetLayer; + + sal_uLong mnNoInitialObjects; + sal_uLong mnInlines; + SdrPage* mpDrawPg; + const SvxMSDffShapeOrders *mpShapeOrders; + + sal_uInt16 GetEscherObjectIdx(sal_uLong nSpId); + myeiter MapEscherIdxToIter(sal_uLong nIdx); + // new parameter <_bInHeaderFooter>, indicating + // that object is in header or footer + sal_uLong GetEscherObjectPos( sal_uLong nSpId, + const bool _bInHeaderFooter ); + sal_uLong GetDrawingObjectPos(short nWwHeight); + void InsertObject(SdrObject *pObject, sal_uLong nPos); +public: + wwZOrderer(const sw::util::SetLayer &rSetLayer, SdrPage* pDrawPg, + const SvxMSDffShapeOrders *pShapeOrders); + void InsertTextLayerObject(SdrObject* pObject); + /* + We should have separate ZOrder classes for 95- and 97+ and + instantiate the appropriate one at run time. + */ + void InsertDrawingObject(SdrObject* pObj, short nWwHeight); + // new parameter <_bInHeaderFooter>, indicating that object is in header or footer + void InsertEscherObject( SdrObject* pObject, + sal_uLong nSpId, + const bool _bInHeaderFooter ); + void InsideEscher(sal_uLong nIndex); + void OutsideEscher(); +}; + +void WW8FSPAShadowToReal( WW8_FSPA_SHADOW const * pFSPAS, WW8_FSPA* pPic ); +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8graf2.cxx b/sw/source/filter/ww8/ww8graf2.cxx new file mode 100644 index 000000000..a540648b8 --- /dev/null +++ b/sw/source/filter/ww8/ww8graf2.cxx @@ -0,0 +1,769 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <iterator> +#include <numeric> +#include <hintids.hxx> +#include <svl/urihelper.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <unotools/configmgr.hxx> +#include <grfatr.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <frmfmt.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <mdiexp.hxx> +#include "writerwordglue.hxx" +#include "ww8struc.hxx" +#include "ww8scan.hxx" +#include "ww8par.hxx" +#include "ww8par2.hxx" +#include "ww8graf.hxx" +#include <vcl/gdimtf.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/wmf.hxx> + +using namespace ::com::sun::star; +using namespace sw::types; + +wwZOrderer::wwZOrderer(const sw::util::SetLayer &rSetLayer, SdrPage* pDrawPg, + const SvxMSDffShapeOrders *pShapeOrders) + : maSetLayer(rSetLayer), mnInlines(0), mpDrawPg(pDrawPg), + mpShapeOrders(pShapeOrders) +{ + mnNoInitialObjects = mpDrawPg->GetObjCount(); + OSL_ENSURE(mpDrawPg,"Missing draw page impossible!"); +} + +void wwZOrderer::InsideEscher(sal_uLong nSpId) +{ + maIndexes.push(GetEscherObjectIdx(nSpId)); +} + +void wwZOrderer::OutsideEscher() +{ + maIndexes.pop(); +} + +// consider new parameter <_bInHeaderFooter> +void wwZOrderer::InsertEscherObject( SdrObject* pObject, + sal_uLong nSpId, + const bool _bInHeaderFooter ) +{ + sal_uLong nInsertPos = GetEscherObjectPos( nSpId, _bInHeaderFooter ); + InsertObject(pObject, nInsertPos + mnNoInitialObjects + mnInlines); +} + +wwZOrderer::myeiter wwZOrderer::MapEscherIdxToIter(sal_uLong nIdx) +{ + return std::find_if(maEscherLayer.begin(), maEscherLayer.end(), + [nIdx](const EscherShape& rShape) { return rShape.mnEscherShapeOrder == nIdx; }); +} + +sal_uInt16 wwZOrderer::GetEscherObjectIdx(sal_uLong nSpId) +{ + sal_uInt16 nFound=0; + sal_uInt16 nShapeCount = mpShapeOrders ? mpShapeOrders->size() : 0; + // First, find out what position this shape is in the Escher order. + for (sal_uInt16 nShapePos=0; nShapePos < nShapeCount; nShapePos++) + { + const SvxMSDffShapeOrder& rOrder = *(*mpShapeOrders)[nShapePos]; + if (rOrder.nShapeId == nSpId) + { + nFound = nShapePos; + break; + } + } + return nFound; +} + +// consider new parameter <_bInHeaderFooter> +sal_uLong wwZOrderer::GetEscherObjectPos( sal_uLong nSpId, + const bool _bInHeaderFooter ) +{ + /* + EscherObjects have their own ordering which needs to be matched to + the actual ordering that should be used when inserting them into the + document. + */ + sal_uInt16 nFound = GetEscherObjectIdx(nSpId); + // Match the ordering position from the ShapeOrders to the ordering of all + // objects in the document, there is a complexity when escherobjects + // contain inlines objects, we need to consider those as part of the + // escher count + sal_uLong nRet=0; + myeiter aIter = maEscherLayer.begin(); + myeiter aEnd = maEscherLayer.end(); + // skip objects in page header|footer, + // if current object isn't in page header|footer + if ( !_bInHeaderFooter ) + { + while ( aIter != aEnd ) + { + if ( !aIter->mbInHeaderFooter ) + { + break; + } + nRet += aIter->mnNoInlines + 1; + ++aIter; + } + } + while (aIter != aEnd) + { + // insert object in page header|footer + // before objects in page body + if ( _bInHeaderFooter && !aIter->mbInHeaderFooter ) + { + break; + } + if ( aIter->mnEscherShapeOrder > nFound ) + break; + nRet += aIter->mnNoInlines+1; + ++aIter; + } + maEscherLayer.insert(aIter, EscherShape( nFound, _bInHeaderFooter ) ); + return nRet; +} + +// InsertObj() adds the object into the Sw-Page and memorize the Z-position +// in a VarArr +void wwZOrderer::InsertDrawingObject(SdrObject* pObj, short nWwHeight) +{ + sal_uLong nPos = GetDrawingObjectPos(nWwHeight); + if (nWwHeight & 0x2000) // Heaven ? + maSetLayer.SendObjectToHeaven(*pObj); + else + maSetLayer.SendObjectToHell(*pObj); + + InsertObject(pObj, nPos + mnNoInitialObjects + mnInlines); +} + +void wwZOrderer::InsertTextLayerObject(SdrObject* pObject) +{ + maSetLayer.SendObjectToHeaven(*pObject); + if (maIndexes.empty()) + { + InsertObject(pObject, mnNoInitialObjects + mnInlines); + ++mnInlines; + } + else + { + //If we are inside an escher objects, place us just after that + //escher obj, and increment its inline count + sal_uInt16 nIdx = maIndexes.top(); + myeiter aEnd = MapEscherIdxToIter(nIdx); + + sal_uLong nInsertPos = std::accumulate(maEscherLayer.begin(), aEnd, sal_uLong(0), + [](const sal_uLong nPos, const EscherShape& rShape) { return nPos + rShape.mnNoInlines + 1; }); + + OSL_ENSURE(aEnd != maEscherLayer.end(), "Something very wrong here"); + if (aEnd != maEscherLayer.end()) + { + aEnd->mnNoInlines++; + nInsertPos += aEnd->mnNoInlines; + } + + InsertObject(pObject, mnNoInitialObjects + mnInlines + nInsertPos); + } +} + +/* Parallel to the Obj-array in the document I also build an array which + * contains the Ww-height (-> what covers what). + * Based on this VARARR the position where the insertion happens is + * determined. + * When inserting the offset in an existing document with a graphic layer the + * caller has to increment the index by mnNoInitialObjects, so that the new + * objects are added at the end (inserting is faster then) + */ +sal_uLong wwZOrderer::GetDrawingObjectPos(short nWwHeight) +{ + auto aIter = std::find_if( + maDrawHeight.begin(), maDrawHeight.end(), + [nWwHeight](short aHeight){ return (aHeight & 0x1fff) > (nWwHeight & 0x1fff); }); + + aIter = maDrawHeight.insert(aIter, nWwHeight); + return std::distance(maDrawHeight.begin(), aIter); +} + +void wwZOrderer::InsertObject(SdrObject* pObject, sal_uLong nPos) +{ + if (!pObject->IsInserted()) + { + mpDrawPg->InsertObject(pObject, nPos); + } +} + +static void WW8PicShadowToReal( WW8_PIC_SHADOW const * pPicS, WW8_PIC* pPic ); + +bool SwWW8ImplReader::GetPictGrafFromStream(Graphic& rGraphic, SvStream& rSrc) +{ + return ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(rGraphic, OUString(), rSrc); +} + +bool SwWW8ImplReader::ReadGrafFile(OUString& rFileName, std::unique_ptr<Graphic>& rpGraphic, + const WW8_PIC& rPic, SvStream* pSt, sal_uLong nFilePos, bool* pbInDoc) +{ // Write the graphic to the file + *pbInDoc = true; // default + + sal_uLong nPosFc = nFilePos + rPic.cbHeader; + + switch (rPic.MFP.mm) + { + case 94: // BMP-file ( not embedded ) or GIF + case 99: // TIFF-file ( not embedded ) + pSt->Seek(nPosFc); + // read name as P-string + rFileName = read_uInt8_PascalString(*pSt, m_eStructCharSet); + if (!rFileName.isEmpty()) + rFileName = URIHelper::SmartRel2Abs( + INetURLObject(m_sBaseURL), rFileName, + URIHelper::GetMaybeFileHdl()); + *pbInDoc = false; // Don't delete the file afterwards + return !rFileName.isEmpty(); // read was successful + } + + GDIMetaFile aWMF; + bool bOk = checkSeek(*pSt, nPosFc) && ReadWindowMetafile( *pSt, aWMF ); + + if (!bOk || pSt->GetError() || !aWMF.GetActionSize()) + return false; + + //skip duplicate graphics when fuzzing + if (utl::ConfigManager::IsFuzzing()) + { + if (!m_aGrafPosSet.insert(nPosFc).second) + return false; + } + + if (m_xWwFib->m_envr != 1) // !MAC as creator + { + rpGraphic.reset(new Graphic(aWMF)); + return true; + } + + // MAC - word as creator + // The WMF only says "Please use Word 6.0c" and Mac-Pict follows but without + // the first 512 Bytes which are not relevant in a MAC-PICT (they are not + // interpreted) + bOk = false; + long nData = rPic.lcb - ( pSt->Tell() - nPosFc ); + if (nData > 0) + { + rpGraphic.reset(new Graphic()); + bOk = SwWW8ImplReader::GetPictGrafFromStream(*rpGraphic, *pSt); + if (!bOk) + rpGraphic.reset(); + } + return bOk; // Contains graphic +} + +struct WW8PicDesc +{ + sal_Int16 nCL, nCR, nCT, nCB; + long nWidth, nHeight; + + explicit WW8PicDesc( const WW8_PIC& rPic ); +}; + +WW8PicDesc::WW8PicDesc( const WW8_PIC& rPic ) + : nCL(rPic.dxaCropLeft), + nCR(rPic.dxaCropRight), + nCT(rPic.dyaCropTop), + nCB(rPic.dyaCropBottom) +{ + //See #i21190# before fiddling with this method + long nOriWidth = rPic.dxaGoal; //Size in 1/100 mm before crop + long nOriHeight = rPic.dyaGoal; + + + long nCurrentWidth = nOriWidth - (nCL + nCR); // Size after crop + long nCurrentHeight = nOriHeight - (nCT + nCB); + if (!nCurrentWidth) + nCurrentWidth = 1; + if (!nCurrentHeight) + nCurrentHeight = 1; + nWidth = nCurrentWidth * rPic.mx / 1000; // Writer Size + nHeight = nCurrentHeight * rPic.my / 1000; +} + +void SwWW8ImplReader::ReplaceObj(const SdrObject &rReplaceObj, + SdrObject &rSubObj) +{ + // Insert SdrGrafObj instead of SdrTextObj into this group + if (SdrObject* pGroupObject = rReplaceObj.getParentSdrObjectFromSdrObject()) + { + SdrObjList* pObjectList = pGroupObject->GetSubList(); + + rSubObj.SetLogicRect(rReplaceObj.GetCurrentBoundRect()); + rSubObj.SetLayer(rReplaceObj.GetLayer()); + + // remove old object from group-list and add new one + // (this also exchanges it in the drawing page) + pObjectList->ReplaceObject(&rSubObj, rReplaceObj.GetOrdNum()); + } + else + { + OSL_ENSURE( false, "Impossible!"); + } +} + +// MakeGrafNotInContent inserts a non character bound graphic +// ( bGrafApo == true) +SwFlyFrameFormat* SwWW8ImplReader::MakeGrafNotInContent(const WW8PicDesc& rPD, + const Graphic* pGraph, const OUString& rFileName, const SfxItemSet& rGrfSet) +{ + + sal_uInt32 nWidth = rPD.nWidth; + sal_uInt32 nHeight = rPD.nHeight; + + // Vertical shift through line spacing + sal_Int32 nNetHeight = nHeight + rPD.nCT + rPD.nCB; + if (m_xSFlyPara->nLineSpace && m_xSFlyPara->nLineSpace > nNetHeight) + m_xSFlyPara->nYPos = + static_cast<sal_uInt16>( m_xSFlyPara->nYPos + m_xSFlyPara->nLineSpace - nNetHeight ); + + WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), true); + + SwFormatAnchor aAnchor(WW8SwFlyPara::eAnchor); + aAnchor.SetAnchor(m_pPaM->GetPoint()); + aFlySet.Put(aAnchor); + + aFlySet.Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth, nHeight ) ); + + SwFlyFrameFormat *const pFlyFormat = + m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, rFileName, OUString(), pGraph, + &aFlySet, &rGrfSet, nullptr); + + // So the frames are generated when inserted in an existing doc: + if (m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && + (RndStdIds::FLY_AT_PARA == pFlyFormat->GetAnchor().GetAnchorId())) + { + pFlyFormat->MakeFrames(); + } + return pFlyFormat; +} + +// MakeGrafInContent inserts a character bound graphic +SwFrameFormat* SwWW8ImplReader::MakeGrafInContent(const WW8_PIC& rPic, + const WW8PicDesc& rPD, const Graphic* pGraph, const OUString& rFileName, + const SfxItemSet& rGrfSet) +{ + WW8FlySet aFlySet(*this, m_pPaM, rPic, rPD.nWidth, rPD.nHeight); + + SwFrameFormat* pFlyFormat = nullptr; + + if (rFileName.isEmpty() && m_nObjLocFc) // then it should be an OLE-Object + pFlyFormat = ImportOle(pGraph, &aFlySet, &rGrfSet); + + if( !pFlyFormat ) // then just as graphic + { + + pFlyFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, rFileName, OUString(), pGraph, &aFlySet, + &rGrfSet, nullptr); + } + + // Resize the frame to the size of the picture if graphic is inside a frame + // (only if auto-width) + if (m_xSFlyPara) + m_xSFlyPara->BoxUpWidth( rPD.nWidth ); + return pFlyFormat; +} + +SwFrameFormat* SwWW8ImplReader::ImportGraf1(WW8_PIC const & rPic, SvStream* pSt, + sal_uLong nFilePos ) +{ + SwFrameFormat* pRet = nullptr; + if( pSt->eof() || rPic.fError || rPic.MFP.mm == 99 ) + return nullptr; + + OUString aFileName; + bool bInDoc; + std::unique_ptr<Graphic> pGraph; + bool bOk = ReadGrafFile(aFileName, pGraph, rPic, pSt, nFilePos, &bInDoc); + + if (!bOk) + { + return nullptr; // Graphic could not be read correctly + } + + WW8PicDesc aPD( rPic ); + + SwAttrSet aGrfSet( m_rDoc.GetAttrPool(), RES_GRFATR_BEGIN, RES_GRFATR_END-1); + if( aPD.nCL || aPD.nCR || aPD.nCT || aPD.nCB ) + { + SwCropGrf aCrop( aPD.nCL, aPD.nCR, aPD.nCT, aPD.nCB) ; + aGrfSet.Put( aCrop ); + } + + if (m_xWFlyPara && m_xWFlyPara->bGrafApo) + pRet = MakeGrafNotInContent(aPD, pGraph.get(), aFileName, aGrfSet); + else + pRet = MakeGrafInContent(rPic, aPD, pGraph.get(), aFileName, aGrfSet); + return pRet; +} + +void SwWW8ImplReader::PicRead(SvStream *pDataStream, WW8_PIC *pPic, + bool bVer67) +{ + //Only the first 0x2e bytes are the same between version 6/7 and 8+ + WW8_PIC_SHADOW aPicS; + pDataStream->ReadBytes( &aPicS, sizeof( aPicS ) ); + WW8PicShadowToReal( &aPicS, pPic ); + for (WW8_BRC & i : pPic->rgbrc) + pDataStream->ReadBytes(&i, bVer67 ? 2 : 4); + pDataStream->ReadInt16( pPic->dxaOrigin ); + pDataStream->ReadInt16( pPic->dyaOrigin ); + if (!bVer67) + pDataStream->SeekRel(2); //cProps +} + +namespace +{ + SwNodeType GetNodeType(SwFrameFormat const &rSource) + { + const SwNodeIndex* pNodeIndex = rSource.GetContent().GetContentIdx(); + if (!pNodeIndex) + return SwNodeType::NONE; + const SwNode& rCSttNd = pNodeIndex->GetNode(); + SwNodeRange aRg(rCSttNd, 1, *rCSttNd.EndOfSectionNode()); + return aRg.aStart.GetNode().GetNodeType(); + } +} + +SwFrameFormat* SwWW8ImplReader::ImportGraf(SdrTextObj const * pTextObj, + SwFrameFormat const * pOldFlyFormat) +{ + SwFrameFormat* pRet = nullptr; + if ( + ((m_pStrm == m_pDataStream ) && !m_nPicLocFc) || + (m_nIniFlags & WW8FL_NO_GRAF) + ) + { + return nullptr; + } + + ::SetProgressState(m_nProgress, m_pDocShell); // Update + + GrafikCtor(); + + /* + * Little joke from Microsoft: sometimes a stream named DATA exists. This + * stream then contains the PICF and the corresponding graphic! + * We otherwise map the variable pDataStream to pStream. + */ + auto nOldPos = m_pDataStream->Tell(); + WW8_PIC aPic; + bool bValid = checkSeek(*m_pDataStream, m_nPicLocFc); + + if (bValid) + PicRead( m_pDataStream, &aPic, m_bVer67); + + // Sanity check is needed because for example check boxes in field results + // contain a WMF-like struct + if (bValid && m_pDataStream->good() && (aPic.lcb >= 58)) + { + if( m_pFlyFormatOfJustInsertedGraphic ) + { + // We just added a graphic-link into the doc. Now we need to set + // its position and scale it. + WW8PicDesc aPD( aPic ); + + WW8FlySet aFlySet( *this, m_pPaM, aPic, aPD.nWidth, aPD.nHeight ); + + // the correct anchor is set in Read_F_IncludePicture and the + // current PaM point is after the position if it is anchored in + // content; because this anchor add a character into the textnode. + // #i2806# + if (RndStdIds::FLY_AS_CHAR == + m_pFlyFormatOfJustInsertedGraphic->GetAnchor().GetAnchorId() ) + { + aFlySet.ClearItem( RES_ANCHOR ); + } + + m_pFlyFormatOfJustInsertedGraphic->SetFormatAttr( aFlySet ); + + m_pFlyFormatOfJustInsertedGraphic = nullptr; + } + else if((0x64 == aPic.MFP.mm) || (0x66 == aPic.MFP.mm)) + { + // linked graphic in ESCHER-Object + SdrObject* pObject = nullptr; + + WW8PicDesc aPD( aPic ); + if (!m_xMSDffManager) + m_xMSDffManager.reset(new SwMSDffManager(*this, m_bSkipImages)); + /* ##835## + * Disable use of main stream as fallback stream for inline direct + * blips as it is known that they are directly after the record + * header, testing for existence in main stream may lead to an + * incorrect fallback graphic being found if other escher graphics + * have been inserted in the document + */ + m_xMSDffManager->DisableFallbackStream(); + if (!m_xMSDffManager->GetModel()) + m_xMSDffManager->SetModel(m_pDrawModel, 1440); + + if (0x66 == aPic.MFP.mm) + { + //These ones have names prepended + sal_uInt8 nNameLen=0; + m_pDataStream->ReadUChar( nNameLen ); + m_pDataStream->SeekRel( nNameLen ); + } + + tools::Rectangle aClientRect( 0,0, aPD.nWidth, aPD.nHeight); + SvxMSDffImportData aData( aClientRect ); + pObject = m_xMSDffManager->ImportObj(*m_pDataStream, aData, aClientRect, tools::Rectangle(), /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + if (pObject) + { + // for the frame + SfxItemSet aAttrSet( m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END-1>{} ); + + SvxMSDffImportRec const*const pRecord = (1 == aData.size()) + ? aData.begin()->get() : nullptr; + + if( pRecord ) + { + + // Horizontal rule may have its width given as % of page + // width (-1 is used if not given, 0 means the object has + // fixed width). + // Additionally, if it's a horizontal rule without width + // given, assume 100.0% width. + int relativeWidth = pRecord->relativeHorizontalWidth; + if( relativeWidth == -1 ) + relativeWidth = pRecord->isHorizontalRule ? 1000 : 0; + if( relativeWidth != 0 ) + { + const sal_Int16 nScale = aPic.dxaGoal ? aPic.dxaGoal : 1000; + aPic.mx = msword_cast<sal_uInt16>( + m_aSectionManager.GetPageWidth() - + m_aSectionManager.GetPageRight() - + m_aSectionManager.GetPageLeft()) * relativeWidth / nScale; + aPD = WW8PicDesc( aPic ); + // This SetSnapRect() call adjusts the size of the + // object itself, no idea why it's this call (or even + // what the call actually does), but that's what + // ImportGraf() (called by ImportObj()) uses. + pObject->SetSnapRect( tools::Rectangle( 0, 0, aPD.nWidth, aPD.nHeight )); + } + + // A graphic of this type in this location is always + // inline, and uses the pic in the same module as ww6 + // graphics. + if (m_xWFlyPara && m_xWFlyPara->bGrafApo) + { + WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), true); + + SwFormatAnchor aAnchor(WW8SwFlyPara::eAnchor); + aAnchor.SetAnchor(m_pPaM->GetPoint()); + aFlySet.Put(aAnchor); + + aAttrSet.Put(aFlySet); + } + else + { + WW8FlySet aFlySet( *this, m_pPaM, aPic, aPD.nWidth, + aPD.nHeight ); + + aAttrSet.Put(aFlySet); + } + // Modified for i120716,for graf importing from MS Word 2003 + // binary format, there is no border distance. + tools::Rectangle aInnerDist(0,0,0,0); + MatchSdrItemsIntoFlySet( pObject, aAttrSet, + pRecord->eLineStyle, pRecord->eLineDashing, + pRecord->eShapeType, aInnerDist ); + + // Set the size from the WinWord PIC-structure as graphic + // size + aAttrSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, aPD.nWidth, + aPD.nHeight ) ); + } + + // for the graphic + SfxItemSet aGrSet( m_rDoc.GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, + RES_GRFATR_END-1>{} ); + + if( aPD.nCL || aPD.nCR || aPD.nCT || aPD.nCB ) + { + SwCropGrf aCrop( aPD.nCL, aPD.nCR, aPD.nCT, aPD.nCB ); + aGrSet.Put( aCrop ); + } + + if (pRecord) + MatchEscherMirrorIntoFlySet(*pRecord, aGrSet); + + // if necessary adopt old AttrSet and correct horizontal + // positioning relation + if( pOldFlyFormat ) + { + aAttrSet.Put( pOldFlyFormat->GetAttrSet() ); + const SwFormatHoriOrient &rHori = pOldFlyFormat->GetHoriOrient(); + if( text::RelOrientation::FRAME == rHori.GetRelationOrient() ) + { + aAttrSet.Put( SwFormatHoriOrient( rHori.GetPos(), + text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ) ); + } + } + + bool bTextObjWasGrouped = false; + if (pOldFlyFormat && pTextObj && pTextObj->getParentSdrObjectFromSdrObject()) + bTextObjWasGrouped = true; + + if (bTextObjWasGrouped) + ReplaceObj(*pTextObj, *pObject); + else + { + if (sal_uInt16(OBJ_OLE2) == pObject->GetObjIdentifier()) + { + // the size from BLIP, if there is any, should be already set + pRet = InsertOle(*static_cast<SdrOle2Obj*>(pObject), aAttrSet, &aGrSet); + } + else + { + if (SdrGrafObj* pGraphObject = dynamic_cast<SdrGrafObj*>( pObject) ) + { + // Now add the link or rather the graphic to the doc + const Graphic& rGraph = pGraphObject->GetGraphic(); + + if (m_nObjLocFc) // is it an OLE-Object? + pRet = ImportOle(&rGraph, &aAttrSet, &aGrSet, pObject->GetBLIPSizeRectangle()); + + if (!pRet) + { + pRet = m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, OUString(), OUString(), + &rGraph, &aAttrSet, &aGrSet, nullptr ); + } + } + else + pRet = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pObject, aAttrSet ); + } + } + + // only if we made an *Insert* + if (pRet) + { + if (pRecord) + SetAttributesAtGrfNode(pRecord, pRet, nullptr); + + OUString aObjectName(pObject->GetName()); + if (aObjectName.isEmpty() || !m_rDoc.FindFlyByName(aObjectName, GetNodeType(*pRet))) + pRet->SetName(aObjectName); + else + m_aGrfNameGenerator.SetUniqueGraphName(pRet, aObjectName); + + // determine the pointer to the new object and update + // Z-order-list accordingly (or delete entry) + if (SdrObject* pOurNewObject = CreateContactObject(pRet)) + { + if (pOurNewObject != pObject) + { + m_xMSDffManager->ExchangeInShapeOrder( pObject, 0, + pOurNewObject ); + + // delete and destroy old SdrGrafObj from page + if (pObject->getSdrPageFromSdrObject()) + m_pDrawPg->RemoveObject(pObject->GetOrdNum()); + SdrObject::Free( pObject ); + } + } + else + m_xMSDffManager->RemoveFromShapeOrder( pObject ); + } + else + m_xMSDffManager->RemoveFromShapeOrder( pObject ); + + // also delete this from the page if not grouped + if (pTextObj && !bTextObjWasGrouped && pTextObj->getSdrPageFromSdrObject()) + m_pDrawPg->RemoveObject( pTextObj->GetOrdNum() ); + } + m_xMSDffManager->EnableFallbackStream(); + } + else if (aPic.lcb >= 58) + pRet = ImportGraf1(aPic, m_pDataStream, m_nPicLocFc); + } + m_pDataStream->Seek( nOldPos ); + + if (pRet) + { + SdrObject* pOurNewObject = CreateContactObject(pRet); + m_xWWZOrder->InsertTextLayerObject(pOurNewObject); + } + + return AddAutoAnchor(pRet); +} + +void WW8PicShadowToReal( WW8_PIC_SHADOW const * pPicS, WW8_PIC * pPic ) +{ + pPic->lcb = SVBT32ToUInt32( pPicS->lcb ); + pPic->cbHeader = SVBT16ToUInt16( pPicS->cbHeader ); + pPic->MFP.mm = SVBT16ToUInt16( pPicS->MFP.mm ); + pPic->MFP.xExt = SVBT16ToUInt16( pPicS->MFP.xExt ); + pPic->MFP.yExt = SVBT16ToUInt16( pPicS->MFP.yExt ); + pPic->MFP.hMF = SVBT16ToUInt16( pPicS->MFP.hMF ); + for( sal_uInt16 i = 0; i < 14 ; i++ ) + pPic->rcWinMF[i] = pPicS->rcWinMF[i]; + pPic->dxaGoal = SVBT16ToUInt16( pPicS->dxaGoal ); + pPic->dyaGoal = SVBT16ToUInt16( pPicS->dyaGoal ); + pPic->mx = SVBT16ToUInt16( pPicS->mx ); + pPic->my = SVBT16ToUInt16( pPicS->my ); + pPic->dxaCropLeft = SVBT16ToUInt16( pPicS->dxaCropLeft ); + pPic->dyaCropTop = SVBT16ToUInt16( pPicS->dyaCropTop ); + pPic->dxaCropRight = SVBT16ToUInt16( pPicS->dxaCropRight ); + pPic->dyaCropBottom = SVBT16ToUInt16( pPicS->dyaCropBottom ); + pPic->brcl = pPicS->aBits1 & 0x0f; + pPic->fFrameEmpty = (pPicS->aBits1 & 0x10) >> 4; + pPic->fBitmap = (pPicS->aBits1 & 0x20) >> 5; + pPic->fDrawHatch = (pPicS->aBits1 & 0x40) >> 6; + pPic->fError = (pPicS->aBits1 & 0x80) >> 7; + pPic->bpp = pPicS->aBits2; +} + +void WW8FSPAShadowToReal( WW8_FSPA_SHADOW const * pFSPAS, WW8_FSPA * pFSPA ) +{ + pFSPA->nSpId = SVBT32ToUInt32( pFSPAS->nSpId ); + pFSPA->nXaLeft = SVBT32ToUInt32( pFSPAS->nXaLeft ); + pFSPA->nYaTop = SVBT32ToUInt32( pFSPAS->nYaTop ); + pFSPA->nXaRight = SVBT32ToUInt32( pFSPAS->nXaRight ); + pFSPA->nYaBottom = SVBT32ToUInt32( pFSPAS->nYaBottom ); + + sal_uInt16 nBits = SVBT16ToUInt16( pFSPAS->aBits1 ); + + pFSPA->bHdr = sal_uInt16(0 != ( nBits & 0x0001 )); + pFSPA->nbx = ( nBits & 0x0006 ) >> 1; + pFSPA->nby = ( nBits & 0x0018 ) >> 3; + pFSPA->nwr = ( nBits & 0x01E0 ) >> 5; + pFSPA->nwrk = ( nBits & 0x1E00 ) >> 9; + pFSPA->bRcaSimple = sal_uInt16(0 != ( nBits & 0x2000 )); + pFSPA->bBelowText = sal_uInt16(0 != ( nBits & 0x4000 )); + pFSPA->bAnchorLock = sal_uInt16(0 != ( nBits & 0x8000 )); + pFSPA->nTxbx = SVBT32ToUInt32( pFSPAS->nTxbx ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx new file mode 100644 index 000000000..16691bdb5 --- /dev/null +++ b/sw/source/filter/ww8/ww8par.cxx @@ -0,0 +1,6697 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> + +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/packages/XPackageEncryption.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <i18nlangtag/languagetag.hxx> + +#include <unotools/configmgr.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <rtl/random.h> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> + +#include <sfx2/docinf.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/zoomitem.hxx> +#include <tools/urlobj.hxx> +#include <unotools/tempfile.hxx> + +#include <comphelper/docpasswordrequest.hxx> +#include <comphelper/documentinfo.hxx> +#include <comphelper/propertysequence.hxx> + +#include <editeng/outlobj.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/editeng.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdoashp.hxx> +#include <svx/svxerr.hxx> +#include <filter/msfilter/mscodec.hxx> +#include <svx/svdmodel.hxx> +#include <svx/xflclit.hxx> +#include <svx/sdasitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtcfitm.hxx> +#include <svx/sdtditm.hxx> +#include <svx/sdtmfitm.hxx> +#include <unotools/fltrcfg.hxx> +#include <fmtfld.hxx> +#include <fmturl.hxx> +#include <fmtinfmt.hxx> +#include <reffld.hxx> +#include <fmthdft.hxx> +#include <fmtcntnt.hxx> +#include <fmtcnct.hxx> +#include <fmtanchr.hxx> +#include <fmtpdsc.hxx> +#include <ftninfo.hxx> +#include <fmtftn.hxx> +#include <txtftn.hxx> +#include <ndtxt.hxx> +#include <pagedesc.hxx> +#include <paratr.hxx> +#include <poolfmt.hxx> +#include <fmtclbl.hxx> +#include <section.hxx> +#include <docsh.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentExternalData.hxx> +#include <../../core/inc/DocumentRedlineManager.hxx> +#include <docufld.hxx> +#include <swfltopt.hxx> +#include <viewsh.hxx> +#include <shellres.hxx> +#include <swerror.h> +#include <swtable.hxx> +#include <fchrfmt.hxx> +#include <charfmt.hxx> +#include <IDocumentSettingAccess.hxx> +#include "sprmids.hxx" + +#include <fltini.hxx> + +#include "writerwordglue.hxx" + +#include <ndgrf.hxx> +#include <editeng/editids.hrc> +#include <fmtflcnt.hxx> +#include <txatbase.hxx> + +#include "ww8par2.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XViewDataSupplier.hpp> +#include <com/sun/star/document/IndexedPropertyValues.hpp> + +#include <svl/lngmisc.hxx> +#include <svl/itemiter.hxx> + +#include <comphelper/processfactory.hxx> +#include <basic/basmgr.hxx> + +#include "ww8toolbar.hxx" +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> + +#include <breakit.hxx> + +#if OSL_DEBUG_LEVEL > 1 +#include <iostream> +#include <dbgoutsw.hxx> +#endif + +#include <sfx2/docfile.hxx> +#include <swdll.hxx> +#include "WW8Sttbf.hxx" +#include "WW8FibData.hxx" +#include <unordered_set> +#include <memory> + +using namespace ::com::sun::star; +using namespace sw::util; +using namespace sw::types; +using namespace nsHdFtFlags; + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <unotools/pathoptions.hxx> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include <oox/ole/vbaproject.hxx> +#include <oox/ole/olestorage.hxx> +#include <comphelper/storagehelper.hxx> +#include <sfx2/DocumentMetadataAccess.hxx> +#include <tools/diagnose_ex.h> + +static SwMacroInfo* GetMacroInfo( SdrObject* pObj ) +{ + if ( pObj ) + { + sal_uInt16 nCount = pObj->GetUserDataCount(); + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + SdrObjUserData* pData = pObj->GetUserData( i ); + if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw + && pData->GetId() == SW_UD_IMAPDATA) + { + return dynamic_cast<SwMacroInfo*>(pData); + } + } + SwMacroInfo* pData = new SwMacroInfo; + pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); + return pData; + } + + return nullptr; +}; + +static void lclGetAbsPath(OUString& rPath, sal_uInt16 nLevel, SwDocShell const * pDocShell) +{ + OUStringBuffer aTmpStr; + while( nLevel ) + { + aTmpStr.append("../"); + --nLevel; + } + if (!aTmpStr.isEmpty()) + aTmpStr.append(rPath); + else + aTmpStr = rPath; + + if (!aTmpStr.isEmpty()) + { + bool bWasAbs = false; + rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + // full path as stored in SvxURLField must be encoded + } +} + +namespace +{ + void lclIgnoreUString32(SvStream& rStrm) + { + sal_uInt32 nChars(0); + rStrm.ReadUInt32(nChars); + nChars *= 2; + rStrm.SeekRel(nChars); + } +} + +void SwWW8ImplReader::ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr) +{ + // (0x01B8) HLINK + // const sal_uInt16 WW8_ID_HLINK = 0x01B8; + const sal_uInt32 WW8_HLINK_BODY = 0x00000001; /// Contains file link or URL. + const sal_uInt32 WW8_HLINK_ABS = 0x00000002; /// Absolute path. + const sal_uInt32 WW8_HLINK_DESCR = 0x00000014; /// Description. + const sal_uInt32 WW8_HLINK_MARK = 0x00000008; /// Text mark. + const sal_uInt32 WW8_HLINK_FRAME = 0x00000080; /// Target frame. + const sal_uInt32 WW8_HLINK_UNC = 0x00000100; /// UNC path. + + //sal_uInt8 maGuidStdLink[ 16 ] ={ + // 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B }; + + sal_uInt8 const aGuidUrlMoniker[ 16 ] = { + 0xE0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE, 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B }; + + sal_uInt8 const aGuidFileMoniker[ 16 ] = { + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + + sal_uInt8 aGuid[16]; + sal_uInt32 nFlags(0); + + rStrm.ReadBytes(aGuid, 16); + rStrm.SeekRel( 4 ); + rStrm.ReadUInt32( nFlags ); + + sal_uInt16 nLevel = 0; // counter for level to climb down in path + std::unique_ptr< OUString > xLongName; // link / file name + std::unique_ptr< OUString > xShortName; // 8.3-representation of file name + std::unique_ptr< OUString > xTextMark; // text mark + + // description (ignore) + if( ::get_flag( nFlags, WW8_HLINK_DESCR ) ) + lclIgnoreUString32( rStrm ); + + // target frame + if( ::get_flag( nFlags, WW8_HLINK_FRAME ) ) + { + hlStr.tarFrame = read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm); + } + + // UNC path + if( ::get_flag( nFlags, WW8_HLINK_UNC ) ) + { + // MS-OSHARED: An unsigned integer that specifies the number of Unicode characters in the + // string field, including the null-terminating character. + sal_uInt32 nStrLen(0); + rStrm.ReadUInt32(nStrLen); + if (nStrLen) + { + xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1))); + rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end + } + lclGetAbsPath( *xLongName, 0 , pDocShell); + } + // file link or URL + else if( ::get_flag( nFlags, WW8_HLINK_BODY ) ) + { + rStrm.ReadBytes(aGuid, 16); + + if( memcmp(aGuid, aGuidFileMoniker, 16) == 0 ) + { + rStrm.ReadUInt16( nLevel ); + // MS-OSHARED: An unsigned integer that specifies the number of + // ANSI characters in ansiPath, including the terminating NULL character + sal_uInt32 nUnits = 0; + rStrm.ReadUInt32(nUnits); + if (nUnits) + { + OString sStr(read_uInt8s_ToOString(rStrm, nUnits - 1)); + rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end + xShortName.reset(new OUString(sStr.getStr(), sStr.getLength(), GetCharSetFromLanguage())); + } + rStrm.SeekRel( 24 ); + + sal_uInt32 nStrLen(0); + rStrm.ReadUInt32( nStrLen ); + if( nStrLen ) + { + nStrLen = 0; + rStrm.ReadUInt32( nStrLen ); + nStrLen /= 2; + rStrm.SeekRel( 2 ); + // MS-OSHARED: This array MUST not include a terminating NULL character. + xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen))); + lclGetAbsPath( *xLongName, nLevel, pDocShell); + } + else + lclGetAbsPath( *xShortName, nLevel, pDocShell); + } + else if( memcmp(aGuid, aGuidUrlMoniker, 16) == 0 ) + { + // MS-OSHARED: An unsigned integer that specifies the size of this + // structure in bytes, excluding the size of the length field. The + // value of this field MUST be ... the byte size of the url + // field (including the terminating NULL character) + sal_uInt32 nStrLen(0); + rStrm.ReadUInt32( nStrLen ); + nStrLen /= 2; + if (nStrLen) + { + xLongName.reset(new OUString(read_uInt16s_ToOUString(rStrm, nStrLen - 1))); + rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end + } + if( !::get_flag( nFlags, WW8_HLINK_ABS ) ) + lclGetAbsPath( *xLongName, 0 ,pDocShell); + } + else + { + SAL_INFO("sw.ww8", "WW8Hyperlink::ReadEmbeddedData - unknown content GUID"); + } + } + + // text mark + if( ::get_flag( nFlags, WW8_HLINK_MARK ) ) + { + xTextMark.reset(new OUString(read_uInt32_lenPrefixed_uInt16s_ToOUString(rStrm))); + } + + if (!xLongName && xShortName) + { + xLongName.reset( new OUString ); + *xLongName += *xShortName; + } + else if (!xLongName && xTextMark) + xLongName.reset( new OUString ); + + if (xLongName) + { + if (xTextMark) + { + if (xLongName->isEmpty()) + *xTextMark = xTextMark->replace('!', '.'); + *xLongName += "#" + *xTextMark; + } + hlStr.hLinkAddr = *xLongName; + } +} + +namespace { + +class BasicProjImportHelper +{ + SwDocShell& mrDocShell; + uno::Reference< uno::XComponentContext > mxCtx; +public: + explicit BasicProjImportHelper( SwDocShell& rShell ) : mrDocShell( rShell ), + mxCtx(comphelper::getProcessComponentContext()) + { + } + bool import( const uno::Reference< io::XInputStream >& rxIn ); + OUString getProjectName() const; +}; + +} + +bool BasicProjImportHelper::import( const uno::Reference< io::XInputStream >& rxIn ) +{ + bool bRet = false; + try + { + oox::ole::OleStorage root( mxCtx, rxIn, false ); + oox::StorageRef vbaStg = root.openSubStorage( "Macros" , false ); + if ( vbaStg ) + { + oox::ole::VbaProject aVbaPrj( mxCtx, mrDocShell.GetModel(), "Writer" ); + bRet = aVbaPrj.importVbaProject( *vbaStg ); + } + } + catch( const uno::Exception& ) + { + bRet = false; + } + return bRet; +} + +OUString BasicProjImportHelper::getProjectName() const +{ + OUString sProjName( "Standard" ); + uno::Reference< beans::XPropertySet > xProps( mrDocShell.GetModel(), uno::UNO_QUERY ); + if ( xProps.is() ) + { + try + { + uno::Reference< script::vba::XVBACompatibility > xVBA( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW ); + sProjName = xVBA->getProjectName(); + + } + catch( const uno::Exception& ) + { + } + } + return sProjName; +} + +namespace { + +class Sttb : public TBBase +{ +struct SBBItem +{ + sal_uInt16 cchData; + OUString data; + SBBItem() : cchData(0){} +}; + sal_uInt16 fExtend; + sal_uInt16 cData; + sal_uInt16 cbExtra; + + std::vector< SBBItem > dataItems; + + Sttb(Sttb const&) = delete; + Sttb& operator=(Sttb const&) = delete; + +public: + Sttb(); + + bool Read(SvStream &rS) override; + OUString getStringAtIndex( sal_uInt32 ); +}; + +} + +Sttb::Sttb() + : fExtend(0) + , cData(0) + , cbExtra(0) +{ +} + +bool Sttb::Read( SvStream& rS ) +{ + SAL_INFO("sw.ww8", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadUInt16( fExtend ).ReadUInt16( cData ).ReadUInt16( cbExtra ); + if ( cData ) + { + //if they are all going to be empty strings, how many could there be + const size_t nMaxPossibleRecords = rS.remainingSize() / sizeof(sal_uInt16); + if (cData > nMaxPossibleRecords) + return false; + for ( sal_Int32 index = 0; index < cData; ++index ) + { + SBBItem aItem; + rS.ReadUInt16( aItem.cchData ); + aItem.data = read_uInt16s_ToOUString(rS, aItem.cchData); + dataItems.push_back( aItem ); + } + } + return true; +} + +OUString +Sttb::getStringAtIndex( sal_uInt32 index ) +{ + OUString aRet; + if ( index < dataItems.size() ) + aRet = dataItems[ index ].data; + return aRet; + +} + +SwMSDffManager::SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages ) + : SvxMSDffManager(*rRdr.m_pTableStream, rRdr.GetBaseURL(), rRdr.m_xWwFib->m_fcDggInfo, + rRdr.m_pDataStream, nullptr, 0, COL_WHITE, rRdr.m_pStrm, bSkipImages), + rReader(rRdr), pFallbackStream(nullptr) +{ + SetSvxMSDffSettings( GetSvxMSDffSettings() ); + nSvxMSDffOLEConvFlags = SwMSDffManager::GetFilterFlags(); +} + +sal_uInt32 SwMSDffManager::GetFilterFlags() +{ + sal_uInt32 nFlags(0); + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + if (rOpt.IsMathType2Math()) + nFlags |= OLE_MATHTYPE_2_STARMATH; + if (rOpt.IsExcel2Calc()) + nFlags |= OLE_EXCEL_2_STARCALC; + if (rOpt.IsPowerPoint2Impress()) + nFlags |= OLE_POWERPOINT_2_STARIMPRESS; + if (rOpt.IsWinWord2Writer()) + nFlags |= OLE_WINWORD_2_STARWRITER; + return nFlags; +} + +/* + * I would like to override the default OLE importing to add a test + * and conversion of OCX controls from their native OLE type into our + * native nonOLE Form Control Objects. + */ +// #i32596# - consider new parameter <_nCalledByGroup> +SdrObject* SwMSDffManager::ImportOLE( sal_uInt32 nOLEId, + const Graphic& rGrf, + const tools::Rectangle& rBoundRect, + const tools::Rectangle& rVisArea, + const int _nCalledByGroup ) const +{ + // #i32596# - no import of OLE object, if it's inside a group. + // NOTE: This can be undone, if grouping of Writer fly frames is possible or + // if drawing OLE objects are allowed in Writer. + if ( _nCalledByGroup > 0 ) + { + return nullptr; + } + + SdrObject* pRet = nullptr; + OUString sStorageName; + tools::SvRef<SotStorage> xSrcStg; + uno::Reference < embed::XStorage > xDstStg; + if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg )) + { + tools::SvRef<SotStorage> xSrc = xSrcStg->OpenSotStorage( sStorageName ); + OSL_ENSURE(rReader.m_xFormImpl, "No Form Implementation!"); + css::uno::Reference< css::drawing::XShape > xShape; + if ( (!(rReader.m_bIsHeader || rReader.m_bIsFooter)) && + rReader.m_xFormImpl->ReadOCXStream(xSrc,&xShape,true)) + { + pRet = GetSdrObjectFromXShape(xShape); + } + else + { + ErrCode nError = ERRCODE_NONE; + pRet = CreateSdrOLEFromStorage( + *pSdrModel, + sStorageName, + xSrcStg, + xDstStg, + rGrf, + rBoundRect, + rVisArea, + pStData, + nError, + nSvxMSDffOLEConvFlags, + css::embed::Aspects::MSOLE_CONTENT, + rReader.GetBaseURL()); + } + } + return pRet; +} + +void SwMSDffManager::DisableFallbackStream() +{ + OSL_ENSURE(!pFallbackStream, + "if you're recursive, you're broken"); + pFallbackStream = pStData2; + aOldEscherBlipCache = aEscherBlipCache; + aEscherBlipCache.clear(); + pStData2 = nullptr; +} + +void SwMSDffManager::EnableFallbackStream() +{ + pStData2 = pFallbackStream; + aEscherBlipCache = aOldEscherBlipCache; + aOldEscherBlipCache.clear(); + pFallbackStream = nullptr; +} + +sal_uInt16 SwWW8ImplReader::GetToggleAttrFlags() const +{ + return m_xCtrlStck ? m_xCtrlStck->GetToggleAttrFlags() : 0; +} + +sal_uInt16 SwWW8ImplReader::GetToggleBiDiAttrFlags() const +{ + return m_xCtrlStck ? m_xCtrlStck->GetToggleBiDiAttrFlags() : 0; +} + +void SwWW8ImplReader::SetToggleAttrFlags(sal_uInt16 nFlags) +{ + if (m_xCtrlStck) + m_xCtrlStck->SetToggleAttrFlags(nFlags); +} + +void SwWW8ImplReader::SetToggleBiDiAttrFlags(sal_uInt16 nFlags) +{ + if (m_xCtrlStck) + m_xCtrlStck->SetToggleBiDiAttrFlags(nFlags); +} + +SdrObject* SwMSDffManager::ProcessObj(SvStream& rSt, + DffObjData& rObjData, + SvxMSDffClientData& rData, + tools::Rectangle& rTextRect, + SdrObject* pObj + ) +{ + if( !rTextRect.IsEmpty() ) + { + SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData); + std::unique_ptr<SvxMSDffImportRec> pImpRec(new SvxMSDffImportRec); + + // fill Import Record with data + pImpRec->nShapeId = rObjData.nShapeId; + pImpRec->eShapeType = rObjData.eShapeType; + + rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt, + DFF_msofbtClientAnchor, + SEEK_FROM_CURRENT_AND_RESTART ); + if( rObjData.bClientAnchor ) + ProcessClientAnchor( rSt, + maShapeRecords.Current()->nRecLen, + pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen ); + + rObjData.bClientData = maShapeRecords.SeekToContent( rSt, + DFF_msofbtClientData, + SEEK_FROM_CURRENT_AND_RESTART ); + if( rObjData.bClientData ) + ProcessClientData( rSt, + maShapeRecords.Current()->nRecLen, + pImpRec->pClientDataBuffer, pImpRec->nClientDataLen ); + + // process user (== Winword) defined parameters in 0xF122 record + // #i84783# - set special value to determine, if property is provided or not. + pImpRec->nLayoutInTableCell = 0xFFFFFFFF; + + if( maShapeRecords.SeekToContent( rSt, + DFF_msofbtUDefProp, + SEEK_FROM_CURRENT_AND_RESTART ) + && maShapeRecords.Current()->nRecLen ) + { + sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen; + auto nAvailableBytes = rSt.remainingSize(); + if (nBytesLeft > nAvailableBytes) + { + SAL_WARN("sw.ww8", "Document claimed to have shape record of " << nBytesLeft << " bytes, but only " << nAvailableBytes << " available"); + nBytesLeft = nAvailableBytes; + } + while( 5 < nBytesLeft ) + { + sal_uInt16 nPID(0); + rSt.ReadUInt16(nPID); + sal_uInt32 nUDData(0); + rSt.ReadUInt32(nUDData); + if (!rSt.good()) + break; + switch (nPID) + { + case 0x038F: pImpRec->nXAlign = nUDData; break; + case 0x0390: + pImpRec->nXRelTo = nUDData; + break; + case 0x0391: pImpRec->nYAlign = nUDData; break; + case 0x0392: + pImpRec->nYRelTo = nUDData; + break; + case 0x03BF: pImpRec->nLayoutInTableCell = nUDData; break; + case 0x0393: + // This seems to correspond to o:hrpct from .docx (even including + // the difference that it's in 0.1% even though the .docx spec + // says it's in 1%). + pImpRec->relativeHorizontalWidth = nUDData; + break; + case 0x0394: + // And this is really just a guess, but a mere presence of this + // flag makes a horizontal rule be as wide as the page (unless + // overridden by something), so it probably matches o:hr from .docx. + pImpRec->isHorizontalRule = true; + break; + } + nBytesLeft -= 6; + } + } + + // Text Frame also Title or Outline + sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 ); + if( nTextId ) + { + SfxItemSet aSet( pSdrModel->GetItemPool() ); + + // Originally anything that as a mso_sptTextBox was created as a + // textbox, this was changed to be created as a simple + // rect to keep impress happy. For the rest of us we'd like to turn + // it back into a textbox again. + bool bIsSimpleDrawingTextBox = (pImpRec->eShapeType == mso_sptTextBox); + if (!bIsSimpleDrawingTextBox) + { + // Either + // a) it's a simple text object or + // b) it's a rectangle with text and square wrapping. + bIsSimpleDrawingTextBox = + ( + (pImpRec->eShapeType == mso_sptTextSimple) || + ( + (pImpRec->eShapeType == mso_sptRectangle) + && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() ) + ) + ); + } + + // Distance of Textbox to its surrounding Autoshape + sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440); + sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440 ); + sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720 ); + sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720 ); + + ScaleEmu( nTextLeft ); + ScaleEmu( nTextRight ); + ScaleEmu( nTextTop ); + ScaleEmu( nTextBottom ); + + sal_Int32 nTextRotationAngle=0; + bool bVerticalText = false; + if ( IsProperty( DFF_Prop_txflTextFlow ) ) + { + MSO_TextFlow eTextFlow = static_cast<MSO_TextFlow>(GetPropertyValue( + DFF_Prop_txflTextFlow, 0) & 0xFFFF); + switch( eTextFlow ) + { + case mso_txflBtoT: + nTextRotationAngle = 9000; + break; + case mso_txflVertN: + case mso_txflTtoBN: + nTextRotationAngle = 27000; + break; + case mso_txflTtoBA: + bVerticalText = true; + break; + case mso_txflHorzA: + bVerticalText = true; + nTextRotationAngle = 9000; + break; + case mso_txflHorzN: + default : + break; + } + } + + if (nTextRotationAngle) + { + if (nTextRotationAngle == 9000) + { + long nWidth = rTextRect.GetWidth(); + rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() ); + rTextRect.SetBottom( rTextRect.Top() + nWidth ); + + sal_Int32 nOldTextLeft = nTextLeft; + sal_Int32 nOldTextRight = nTextRight; + sal_Int32 nOldTextTop = nTextTop; + sal_Int32 nOldTextBottom = nTextBottom; + + nTextLeft = nOldTextBottom; + nTextRight = nOldTextTop; + nTextTop = nOldTextLeft; + nTextBottom = nOldTextRight; + } + else if (nTextRotationAngle == 27000) + { + long nWidth = rTextRect.GetWidth(); + rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() ); + rTextRect.SetBottom( rTextRect.Top() + nWidth ); + + sal_Int32 nOldTextLeft = nTextLeft; + sal_Int32 nOldTextRight = nTextRight; + sal_Int32 nOldTextTop = nTextTop; + sal_Int32 nOldTextBottom = nTextBottom; + + nTextLeft = nOldTextTop; + nTextRight = nOldTextBottom; + nTextTop = nOldTextRight; + nTextBottom = nOldTextLeft; + } + } + + if (bIsSimpleDrawingTextBox) + { + SdrObject::Free( pObj ); + pObj = new SdrRectObj( + *pSdrModel, + OBJ_TEXT, + rTextRect); + } + + // The vertical paragraph justification are contained within the + // BoundRect so calculate it here + tools::Rectangle aNewRect(rTextRect); + aNewRect.AdjustBottom( -(nTextTop + nTextBottom) ); + aNewRect.AdjustRight( -(nTextLeft + nTextRight) ); + + // Only if it's a simple Textbox, Writer can replace the Object + // with a Frame, else + if( bIsSimpleDrawingTextBox ) + { + std::shared_ptr<SvxMSDffShapeInfo> const xTmpRec = + std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId); + + SvxMSDffShapeInfos_ById::const_iterator const it = + GetShapeInfos()->find(xTmpRec); + if (it != GetShapeInfos()->end()) + { + SvxMSDffShapeInfo& rInfo = **it; + pImpRec->bReplaceByFly = rInfo.bReplaceByFly; + } + + ApplyAttributes(rSt, aSet, rObjData); + } + + if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2) + { + aSet.Put( makeSdrTextAutoGrowHeightItem( true ) ); + aSet.Put( makeSdrTextMinFrameHeightItem( + aNewRect.Bottom() - aNewRect.Top() ) ); + aSet.Put( makeSdrTextMinFrameWidthItem( + aNewRect.Right() - aNewRect.Left() ) ); + } + else + { + aSet.Put( makeSdrTextAutoGrowHeightItem( false ) ); + aSet.Put( makeSdrTextAutoGrowWidthItem( false ) ); + } + + switch ( static_cast<MSO_WrapMode>(GetPropertyValue( DFF_Prop_WrapText, mso_wrapSquare )) ) + { + case mso_wrapNone : + aSet.Put( makeSdrTextAutoGrowWidthItem( true ) ); + pImpRec->bAutoWidth = true; + break; + case mso_wrapByPoints : + aSet.Put( makeSdrTextContourFrameItem( true ) ); + break; + default: + ; + } + + // Set distances on Textbox's margins + aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) ); + aSet.Put( makeSdrTextRightDistItem( nTextRight ) ); + aSet.Put( makeSdrTextUpperDistItem( nTextTop ) ); + aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) ); + pImpRec->nDxTextLeft = nTextLeft; + pImpRec->nDyTextTop = nTextTop; + pImpRec->nDxTextRight = nTextRight; + pImpRec->nDyTextBottom = nTextBottom; + + // Taking the correct default (which is mso_anchorTop) + sal_uInt32 eTextAnchor = + GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ); + + SdrTextVertAdjust eTVA = bVerticalText + ? SDRTEXTVERTADJUST_BLOCK + : SDRTEXTVERTADJUST_CENTER; + SdrTextHorzAdjust eTHA = bVerticalText + ? SDRTEXTHORZADJUST_CENTER + : SDRTEXTHORZADJUST_BLOCK; + + switch( eTextAnchor ) + { + case mso_anchorTop: + { + if ( bVerticalText ) + eTHA = SDRTEXTHORZADJUST_RIGHT; + else + eTVA = SDRTEXTVERTADJUST_TOP; + } + break; + case mso_anchorTopCentered: + { + if ( bVerticalText ) + eTHA = SDRTEXTHORZADJUST_RIGHT; + else + eTVA = SDRTEXTVERTADJUST_TOP; + } + break; + case mso_anchorMiddle: + break; + case mso_anchorMiddleCentered: + break; + case mso_anchorBottom: + { + if ( bVerticalText ) + eTHA = SDRTEXTHORZADJUST_LEFT; + else + eTVA = SDRTEXTVERTADJUST_BOTTOM; + } + break; + case mso_anchorBottomCentered: + { + if ( bVerticalText ) + eTHA = SDRTEXTHORZADJUST_LEFT; + else + eTVA = SDRTEXTVERTADJUST_BOTTOM; + } + break; + default: + ; + } + + aSet.Put( SdrTextVertAdjustItem( eTVA ) ); + aSet.Put( SdrTextHorzAdjustItem( eTHA ) ); + + if (pObj != nullptr) + { + pObj->SetMergedItemSet(aSet); + + if (bVerticalText) + { + SdrTextObj *pTextObj = dynamic_cast< SdrTextObj* >(pObj); + if (pTextObj) + pTextObj->SetVerticalWriting(true); + } + + if ( bIsSimpleDrawingTextBox ) + { + if ( nTextRotationAngle ) + { + long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ? + rTextRect.GetWidth() : rTextRect.GetHeight(); + nMinWH /= 2; + Point aPivot(rTextRect.TopLeft()); + aPivot.AdjustX(nMinWH ); + aPivot.AdjustY(nMinWH ); + double a = nTextRotationAngle * F_PI18000; + pObj->NbcRotate(aPivot, nTextRotationAngle, sin(a), cos(a)); + } + } + + if ( ( ( rObjData.nSpFlags & ShapeFlag::FlipV ) || mnFix16Angle || nTextRotationAngle ) && dynamic_cast< SdrObjCustomShape* >( pObj ) ) + { + SdrObjCustomShape* pCustomShape = dynamic_cast< SdrObjCustomShape* >( pObj ); + if (pCustomShape) + { + double fExtraTextRotation = 0.0; + if ( mnFix16Angle && !( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 4 ) ) + { // text is already rotated, we have to take back the object rotation if DFF_Prop_RotateText is false + fExtraTextRotation = -mnFix16Angle; + } + if ( rObjData.nSpFlags & ShapeFlag::FlipV ) // sj: in ppt the text is flipped, whereas in word the text + { // remains unchanged, so we have to take back the flipping here + fExtraTextRotation += 18000.0; // because our core will flip text if the shape is flipped. + } + fExtraTextRotation += nTextRotationAngle; + if ( !::basegfx::fTools::equalZero( fExtraTextRotation ) ) + { + fExtraTextRotation /= 100.0; + SdrCustomShapeGeometryItem aGeometryItem( pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) ); + const OUString sTextRotateAngle( "TextRotateAngle" ); + css::beans::PropertyValue aPropVal; + aPropVal.Name = sTextRotateAngle; + aPropVal.Value <<= fExtraTextRotation; + aGeometryItem.SetPropertyValue( aPropVal ); + pCustomShape->SetMergedItem( aGeometryItem ); + } + } + } + else if ( mnFix16Angle ) + { + // rotate text with shape ? + double a = mnFix16Angle * F_PI18000; + pObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle, + sin( a ), cos( a ) ); + } + } + } + else if( !pObj ) + { + // simple rectangular objects are ignored by ImportObj() :-( + // this is OK for Draw but not for Calc and Writer + // cause here these objects have a default border + pObj = new SdrRectObj( + *pSdrModel, + rTextRect); + + SfxItemSet aSet( pSdrModel->GetItemPool() ); + ApplyAttributes( rSt, aSet, rObjData ); + + const SfxPoolItem* pPoolItem=nullptr; + SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR, + false, &pPoolItem ); + if( SfxItemState::DEFAULT == eState ) + aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) ); + pObj->SetMergedItemSet(aSet); + } + + // Means that fBehindDocument is set + if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20) + pImpRec->bDrawHell = true; + else + pImpRec->bDrawHell = false; + if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02) + pImpRec->bHidden = true; + pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 ); + + if ( nTextId ) + { + pImpRec->aTextId.nTxBxS = static_cast<sal_uInt16>( nTextId >> 16 ); + pImpRec->aTextId.nSequence = static_cast<sal_uInt16>(nTextId); + } + + pImpRec->nDxWrapDistLeft = GetPropertyValue( + DFF_Prop_dxWrapDistLeft, 114935 ) / 635; + pImpRec->nDyWrapDistTop = GetPropertyValue( + DFF_Prop_dyWrapDistTop, 0 ) / 635; + pImpRec->nDxWrapDistRight = GetPropertyValue( + DFF_Prop_dxWrapDistRight, 114935 ) / 635; + pImpRec->nDyWrapDistBottom = GetPropertyValue( + DFF_Prop_dyWrapDistBottom, 0 ) / 635; + // 16.16 fraction times total image width or height, as appropriate. + + if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt)) + { + pImpRec->pWrapPolygon.reset(); + + sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(0); + rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert ); + bool bOk = false; + if (nNumElemVert && ((nElemSizeVert == 8) || (nElemSizeVert == 4))) + { + //check if there is enough data in the file to make the + //record sane + bOk = rSt.remainingSize() / nElemSizeVert >= nNumElemVert; + } + if (bOk) + { + pImpRec->pWrapPolygon.reset( new tools::Polygon(nNumElemVert) ); + for (sal_uInt16 i = 0; i < nNumElemVert; ++i) + { + sal_Int32 nX(0), nY(0); + if (nElemSizeVert == 8) + rSt.ReadInt32( nX ).ReadInt32( nY ); + else + { + sal_Int16 nSmallX(0), nSmallY(0); + rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY ); + nX = nSmallX; + nY = nSmallY; + } + (*(pImpRec->pWrapPolygon))[i].setX( nX ); + (*(pImpRec->pWrapPolygon))[i].setY( nY ); + } + } + } + + pImpRec->nCropFromTop = GetPropertyValue( + DFF_Prop_cropFromTop, 0 ); + pImpRec->nCropFromBottom = GetPropertyValue( + DFF_Prop_cropFromBottom, 0 ); + pImpRec->nCropFromLeft = GetPropertyValue( + DFF_Prop_cropFromLeft, 0 ); + pImpRec->nCropFromRight = GetPropertyValue( + DFF_Prop_cropFromRight, 0 ); + + sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ); + + if ( !IsHardAttribute( DFF_Prop_fLine ) && + pImpRec->eShapeType == mso_sptPictureFrame ) + { + nLineFlags &= ~0x08; + } + + pImpRec->eLineStyle = (nLineFlags & 8) + ? static_cast<MSO_LineStyle>(GetPropertyValue( + DFF_Prop_lineStyle, + mso_lineSimple )) + : MSO_LineStyle(USHRT_MAX); + pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue( + DFF_Prop_lineDashing, mso_lineSolid )); + + pImpRec->nFlags = rObjData.nSpFlags; + + if( pImpRec->nShapeId ) + { + auto nShapeId = pImpRec->nShapeId; + auto nShapeOrder = (static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16) + + pImpRec->aTextId.nSequence; + // Complement Import Record List + pImpRec->pObj = pObj; + rImportData.insert(std::move(pImpRec)); + + // Complement entry in Z Order List with a pointer to this Object + // Only store objects which are not deep inside the tree + if( ( rObjData.nCalledByGroup == 0 ) + || + ( (rObjData.nSpFlags & ShapeFlag::Group) + && (rObjData.nCalledByGroup < 2) ) + ) + { + StoreShapeOrder(nShapeId, nShapeOrder, pObj); + } + } + else + pImpRec.reset(); + } + + sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 ); + if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rSt ) ) + { + SvMemoryStream aMemStream; + struct HyperLinksTable hlStr; + sal_uInt16 nRawRecId,nRawRecSize; + aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize ); + + // copy from DFF stream to memory stream + std::vector< sal_uInt8 > aBuffer( nBufferSize ); + if (rSt.ReadBytes(aBuffer.data(), nBufferSize) == nBufferSize) + { + aMemStream.WriteBytes(aBuffer.data(), nBufferSize); + sal_uInt8 nStreamSize = aMemStream.TellEnd(); + aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); + bool bRet = 4 <= nStreamSize; + if( bRet ) + aMemStream.ReadUInt16( nRawRecId ).ReadUInt16( nRawRecSize ); + SwDocShell* pDocShell = rReader.m_pDocShell; + if (pDocShell) + { + rReader.ReadEmbeddedData(aMemStream, pDocShell, hlStr); + } + } + + if (pObj && !hlStr.hLinkAddr.isEmpty()) + { + SwMacroInfo* pInfo = GetMacroInfo( pObj ); + if( pInfo ) + { + pInfo->SetShapeId( rObjData.nShapeId ); + pInfo->SetHlink( hlStr.hLinkAddr ); + if (!hlStr.tarFrame.isEmpty()) + pInfo->SetTarFrame( hlStr.tarFrame ); + OUString aNameStr = GetPropertyString( DFF_Prop_wzName, rSt ); + if (!aNameStr.isEmpty()) + pInfo->SetName( aNameStr ); + } + } + } + + return pObj; +} + +/** + * Special FastSave - Attributes + */ +void SwWW8ImplReader::Read_StyleCode( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 0) + { + m_bCpxStyle = false; + return; + } + sal_uInt16 nColl = 0; + if (m_xWwFib->GetFIBVersion() <= ww::eWW2) + nColl = *pData; + else + nColl = SVBT16ToUInt16(pData); + if (nColl < m_vColl.size()) + { + SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[nColl] ); + m_bCpxStyle = true; + } +} + +/** + * Read_Majority is for Majority (103) and Majority50 (108) + */ +void SwWW8ImplReader::Read_Majority( sal_uInt16, const sal_uInt8* , short ) +{ +} + +/** + * Stack + */ +void SwWW8FltControlStack::NewAttr(const SwPosition& rPos, + const SfxPoolItem& rAttr) +{ + OSL_ENSURE(RES_TXTATR_FIELD != rAttr.Which(), "probably don't want to put" + "fields into the control stack"); + OSL_ENSURE(RES_TXTATR_INPUTFIELD != rAttr.Which(), "probably don't want to put" + "input fields into the control stack"); + OSL_ENSURE(RES_TXTATR_ANNOTATION != rAttr.Which(), "probably don't want to put" + "annotations into the control stack"); + OSL_ENSURE(RES_FLTR_REDLINE != rAttr.Which(), "probably don't want to put" + "redlines into the control stack"); + SwFltControlStack::NewAttr(rPos, rAttr); +} + +SwFltStackEntry* SwWW8FltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, + bool bTstEnd, long nHand, bool ) +{ + SwFltStackEntry *pRet = nullptr; + // Doing a textbox, and using the control stack only as a temporary + // collection point for properties which will are not to be set into + // the real document + if (rReader.m_xPlcxMan && rReader.m_xPlcxMan->GetDoingDrawTextBox()) + { + size_t nCnt = size(); + for (size_t i=0; i < nCnt; ++i) + { + SwFltStackEntry& rEntry = (*this)[i]; + if (nAttrId == rEntry.pAttr->Which()) + { + DeleteAndDestroy(i--); + --nCnt; + } + } + } + else // Normal case, set the attribute into the document + pRet = SwFltControlStack::SetAttr(rPos, nAttrId, bTstEnd, nHand); + return pRet; +} + +long GetListFirstLineIndent(const SwNumFormat &rFormat) +{ + OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION, + "<GetListFirstLineIndent> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" ); + + SvxAdjust eAdj = rFormat.GetNumAdjust(); + long nReverseListIndented; + if (eAdj == SvxAdjust::Right) + nReverseListIndented = -rFormat.GetCharTextDistance(); + else if (eAdj == SvxAdjust::Center) + nReverseListIndented = rFormat.GetFirstLineOffset()/2; + else + nReverseListIndented = rFormat.GetFirstLineOffset(); + return nReverseListIndented; +} + +static long lcl_GetTrueMargin(const SvxLRSpaceItem &rLR, const SwNumFormat &rFormat, + long &rFirstLinePos) +{ + OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION, + "<lcl_GetTrueMargin> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" ); + + const long nBodyIndent = rLR.GetTextLeft(); + const long nFirstLineDiff = rLR.GetTextFirstLineOffset(); + rFirstLinePos = nBodyIndent + nFirstLineDiff; + + const auto nPseudoListBodyIndent = rFormat.GetAbsLSpace(); + const long nReverseListIndented = GetListFirstLineIndent(rFormat); + long nExtraListIndent = nPseudoListBodyIndent + nReverseListIndented; + + return std::max<long>(nExtraListIndent, 0); +} + +// #i103711# +// #i105414# +void SyncIndentWithList( SvxLRSpaceItem &rLR, + const SwNumFormat &rFormat, + const bool bFirstLineOfstSet, + const bool bLeftIndentSet ) +{ + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + long nWantedFirstLinePos; + long nExtraListIndent = lcl_GetTrueMargin(rLR, rFormat, nWantedFirstLinePos); + rLR.SetTextLeft(nWantedFirstLinePos - nExtraListIndent); + rLR.SetTextFirstLineOffset(0); + } + else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + if ( !bFirstLineOfstSet && bLeftIndentSet && + rFormat.GetFirstLineIndent() != 0 ) + { + rLR.SetTextFirstLineOffset( rFormat.GetFirstLineIndent() ); + } + else if ( bFirstLineOfstSet && !bLeftIndentSet && + rFormat.GetIndentAt() != 0 ) + { + rLR.SetTextLeft( rFormat.GetIndentAt() ); + } + else if (!bFirstLineOfstSet && !bLeftIndentSet ) + { + if ( rFormat.GetFirstLineIndent() != 0 ) + { + rLR.SetTextFirstLineOffset( rFormat.GetFirstLineIndent() ); + } + if ( rFormat.GetIndentAt() != 0 ) + { + rLR.SetTextLeft( rFormat.GetIndentAt() ); + } + } + } +} + +const SwNumFormat* SwWW8FltControlStack::GetNumFormatFromStack(const SwPosition &rPos, + const SwTextNode &rTextNode) +{ + const SwNumFormat *pRet = nullptr; + const SfxPoolItem *pItem = GetStackAttr(rPos, RES_FLTR_NUMRULE); + if (pItem && rTextNode.GetNumRule()) + { + if (rTextNode.IsCountedInList()) + { + OUString sName(static_cast<const SfxStringItem*>(pItem)->GetValue()); + const SwNumRule *pRule = pDoc->FindNumRulePtr(sName); + if (pRule) + pRet = GetNumFormatFromSwNumRuleLevel(*pRule, rTextNode.GetActualListLevel()); + } + } + return pRet; +} + +sal_Int32 SwWW8FltControlStack::GetCurrAttrCP() const +{ + return rReader.GetCurrAttrCP(); +} + +bool SwWW8FltControlStack::IsParaEndInCPs(sal_Int32 nStart,sal_Int32 nEnd,bool bSdOD) const +{ + return rReader.IsParaEndInCPs(nStart,nEnd,bSdOD); +} + +/** + * Clear the para end position recorded in reader intermittently + * for the least impact on loading performance. + */ +void SwWW8FltControlStack::ClearParaEndPosition() +{ + if ( !empty() ) + return; + + rReader.ClearParaEndPosition(); +} + +bool SwWW8FltControlStack::CheckSdOD(sal_Int32 nStart,sal_Int32 nEnd) +{ + return rReader.IsParaEndInCPs(nStart,nEnd); +} + +void SwWW8ReferencedFltEndStack::SetAttrInDoc( const SwPosition& rTmpPos, + SwFltStackEntry& rEntry ) +{ + switch( rEntry.pAttr->Which() ) + { + case RES_FLTR_BOOKMARK: + { + // suppress insertion of bookmark, which is recognized as an internal bookmark used for table-of-content + // and which is not referenced. + bool bInsertBookmarkIntoDoc = true; + + SwFltBookmark* pFltBookmark = dynamic_cast<SwFltBookmark*>(rEntry.pAttr.get()); + if ( pFltBookmark != nullptr && pFltBookmark->IsTOCBookmark() ) + { + const OUString& rName = pFltBookmark->GetName(); + std::set< OUString, SwWW8::ltstr >::const_iterator aResult = aReferencedTOCBookmarks.find(rName); + if ( aResult == aReferencedTOCBookmarks.end() ) + { + bInsertBookmarkIntoDoc = false; + } + } + if ( bInsertBookmarkIntoDoc ) + { + SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry ); + } + break; + } + default: + SwFltEndStack::SetAttrInDoc( rTmpPos, rEntry ); + break; + } + +} + +void SwWW8FltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, + SwFltStackEntry& rEntry) +{ + switch (rEntry.pAttr->Which()) + { + case RES_LR_SPACE: + { + /* + Loop over the affected nodes and + a) convert the word style absolute indent to indent relative + to any numbering indent active on the nodes + b) adjust the writer style tabstops relative to the old + paragraph indent to be relative to the new paragraph indent + */ + SwPaM aRegion(rTmpPos); + if (rEntry.MakeRegion(pDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck)) + { + SvxLRSpaceItem aNewLR( *static_cast<SvxLRSpaceItem*>(rEntry.pAttr.get()) ); + sal_uLong nStart = aRegion.Start()->nNode.GetIndex(); + sal_uLong nEnd = aRegion.End()->nNode.GetIndex(); + for(; nStart <= nEnd; ++nStart) + { + SwNode* pNode = pDoc->GetNodes()[ nStart ]; + if (!pNode || !pNode->IsTextNode()) + continue; + + SwContentNode* pNd = static_cast<SwContentNode*>(pNode); + SvxLRSpaceItem aOldLR = static_cast<const SvxLRSpaceItem&>(pNd->GetAttr(RES_LR_SPACE)); + + SwTextNode *pTextNode = static_cast<SwTextNode*>(pNode); + + const SwNumFormat* pNum + = GetNumFormatFromStack(*aRegion.GetPoint(), *pTextNode); + if (!pNum) + { + pNum = GetNumFormatFromTextNode(*pTextNode); + } + + if ( pNum ) + { + // #i103711# + const bool bFirstLineIndentSet = + ( rReader.m_aTextNodesHavingFirstLineOfstSet.end() != + rReader.m_aTextNodesHavingFirstLineOfstSet.find( pNode ) ); + // #i105414# + const bool bLeftIndentSet = + ( rReader.m_aTextNodesHavingLeftIndentSet.end() != + rReader.m_aTextNodesHavingLeftIndentSet.find( pNode ) ); + SyncIndentWithList( aNewLR, *pNum, + bFirstLineIndentSet, + bLeftIndentSet ); + } + + if (aNewLR == aOldLR) + continue; + + pNd->SetAttr(aNewLR); + + } + } + } + break; + + case RES_TXTATR_FIELD: + OSL_ENSURE(false, "What is a field doing in the control stack," + "probably should have been in the endstack"); + break; + + case RES_TXTATR_ANNOTATION: + OSL_ENSURE(false, "What is an annotation doing in the control stack," + "probably should have been in the endstack"); + break; + + case RES_TXTATR_INPUTFIELD: + OSL_ENSURE(false, "What is an input field doing in the control stack," + "probably should have been in the endstack"); + break; + + case RES_TXTATR_INETFMT: + { + SwPaM aRegion(rTmpPos); + if (rEntry.MakeRegion(pDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck)) + { + SwFrameFormat *pFrame; + // If we have just one single inline graphic then + // don't insert a field for the single frame, set + // the frames hyperlink field attribute directly. + if (nullptr != (pFrame = SwWW8ImplReader::ContainsSingleInlineGraphic(aRegion))) + { + const SwFormatINetFormat *pAttr = static_cast<const SwFormatINetFormat *>( + rEntry.pAttr.get()); + SwFormatURL aURL; + aURL.SetURL(pAttr->GetValue(), false); + aURL.SetTargetFrameName(pAttr->GetTargetFrame()); + pFrame->SetFormatAttr(aURL); + } + else + { + pDoc->getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.pAttr); + } + } + } + break; + default: + SwFltControlStack::SetAttrInDoc(rTmpPos, rEntry); + break; + } +} + +const SfxPoolItem* SwWW8FltControlStack::GetFormatAttr(const SwPosition& rPos, + sal_uInt16 nWhich) +{ + const SfxPoolItem *pItem = GetStackAttr(rPos, nWhich); + if (!pItem) + { + SwContentNode const*const pNd = rPos.nNode.GetNode().GetContentNode(); + if (!pNd) + pItem = &pDoc->GetAttrPool().GetDefaultItem(nWhich); + else + { + /* + If we're hunting for the indent on a paragraph and need to use the + parent style indent, then return the indent in msword format, and + not writer format, because that's the style that the filter works + in (naturally) + */ + if (nWhich == RES_LR_SPACE) + { + SfxItemState eState = SfxItemState::DEFAULT; + if (const SfxItemSet *pSet = pNd->GetpSwAttrSet()) + eState = pSet->GetItemState(RES_LR_SPACE, false); + if (eState != SfxItemState::SET && rReader.m_nCurrentColl < rReader.m_vColl.size()) + pItem = rReader.m_vColl[rReader.m_nCurrentColl].maWordLR.get(); + } + + /* + If we're hunting for a character property, try and exact position + within the text node for lookup + */ + if (pNd->IsTextNode()) + { + const sal_Int32 nPos = rPos.nContent.GetIndex(); + m_xScratchSet.reset(new SfxItemSet(pDoc->GetAttrPool(), {{nWhich, nWhich}})); + if (pNd->GetTextNode()->GetParaAttr(*m_xScratchSet, nPos, nPos)) + pItem = m_xScratchSet->GetItem(nWhich); + } + + if (!pItem) + pItem = &pNd->GetAttr(nWhich); + } + } + return pItem; +} + +const SfxPoolItem* SwWW8FltControlStack::GetStackAttr(const SwPosition& rPos, + sal_uInt16 nWhich) +{ + SwFltPosition aFltPos(rPos); + + size_t nSize = size(); + while (nSize) + { + const SwFltStackEntry& rEntry = (*this)[ --nSize ]; + if (rEntry.pAttr->Which() == nWhich) + { + if ( (rEntry.bOpen) || + ( + (rEntry.m_aMkPos.m_nNode <= aFltPos.m_nNode) && + (rEntry.m_aPtPos.m_nNode >= aFltPos.m_nNode) && + (rEntry.m_aMkPos.m_nContent <= aFltPos.m_nContent) && + (rEntry.m_aPtPos.m_nContent > aFltPos.m_nContent) + ) + ) + /* + * e.g. half-open range [0-3) so asking for properties at 3 + * means props that end at 3 are not included + */ + { + return rEntry.pAttr.get(); + } + } + } + return nullptr; +} + +bool SwWW8FltRefStack::IsFootnoteEdnBkmField( + const SwFormatField& rFormatField, + sal_uInt16& rBkmNo) +{ + const SwField* pField = rFormatField.GetField(); + sal_uInt16 nSubType; + if(pField && (SwFieldIds::GetRef == pField->Which()) + && ((REF_FOOTNOTE == (nSubType = pField->GetSubType())) || (REF_ENDNOTE == nSubType)) + && !static_cast<const SwGetRefField*>(pField)->GetSetRefName().isEmpty()) + { + const IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBkmk = + pMarkAccess->findMark( static_cast<const SwGetRefField*>(pField)->GetSetRefName() ); + if(ppBkmk != pMarkAccess->getAllMarksEnd()) + { + // find Sequence No of corresponding Foot-/Endnote + rBkmNo = ppBkmk - pMarkAccess->getAllMarksBegin(); + return true; + } + } + return false; +} + +void SwWW8FltRefStack::SetAttrInDoc(const SwPosition& rTmpPos, + SwFltStackEntry& rEntry) +{ + switch (rEntry.pAttr->Which()) + { + /* + Look up these in our lists of bookmarks that were changed to + variables, and replace the ref field with a var field, otherwise + do normal (?) strange stuff + */ + case RES_TXTATR_FIELD: + case RES_TXTATR_ANNOTATION: + case RES_TXTATR_INPUTFIELD: + { + SwNodeIndex aIdx(rEntry.m_aMkPos.m_nNode, 1); + SwPaM aPaM(aIdx, rEntry.m_aMkPos.m_nContent); + + SwFormatField& rFormatField = *static_cast<SwFormatField*>(rEntry.pAttr.get()); + SwField* pField = rFormatField.GetField(); + + if (!RefToVar(pField, rEntry)) + { + sal_uInt16 nBkmNo; + if( IsFootnoteEdnBkmField(rFormatField, nBkmNo) ) + { + ::sw::mark::IMark const * const pMark = pDoc->getIDocumentMarkAccess()->getAllMarksBegin()[nBkmNo]; + + const SwPosition& rBkMrkPos = pMark->GetMarkPos(); + + SwTextNode* pText = rBkMrkPos.nNode.GetNode().GetTextNode(); + if( pText && rBkMrkPos.nContent.GetIndex() ) + { + SwTextAttr* const pFootnote = pText->GetTextAttrForCharAt( + rBkMrkPos.nContent.GetIndex()-1, RES_TXTATR_FTN ); + if( pFootnote ) + { + sal_uInt16 nRefNo = static_cast<SwTextFootnote*>(pFootnote)->GetSeqRefNo(); + + static_cast<SwGetRefField*>(pField)->SetSeqNo( nRefNo ); + + if( pFootnote->GetFootnote().IsEndNote() ) + static_cast<SwGetRefField*>(pField)->SetSubType(REF_ENDNOTE); + } + } + } + } + + pDoc->getIDocumentContentOperations().InsertPoolItem(aPaM, *rEntry.pAttr); + MoveAttrs(*aPaM.GetPoint()); + } + break; + case RES_FLTR_TOX: + SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry); + break; + default: + case RES_FLTR_BOOKMARK: + OSL_ENSURE(false, "EndStck used with non field, not what we want"); + SwFltEndStack::SetAttrInDoc(rTmpPos, rEntry); + break; + } +} + +/* + For styles we will do our tabstop arithmetic in word style and adjust them to + writer style after all the styles have been finished and the dust settles as + to what affects what. + + For explicit attributes we turn the adjusted writer tabstops back into 0 based + word indexes and we'll turn them back into writer indexes when setting them + into the document. If explicit left indent exist which affects them, then this + is handled when the explicit left indent is set into the document +*/ +void SwWW8ImplReader::Read_Tab(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + if (nLen < 0) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_TABSTOP); + return; + } + + sal_uInt8 nDel = (nLen > 0) ? pData[0] : 0; + const sal_uInt8* pDel = pData + 1; // Del - Array + + sal_uInt8 nIns = (nLen > nDel*2+1) ? pData[nDel*2+1] : 0; + const sal_uInt8* pIns = pData + 2*nDel + 2; // Ins - Array + + short nRequiredLength = 2 + 2*nDel + 2*nIns + 1*nIns; + if (nRequiredLength > nLen) + { + // would require more data than available to describe! + // discard invalid record + nIns = 0; + nDel = 0; + } + + WW8_TBD const * pTyp = reinterpret_cast<WW8_TBD const *>(pData + 2*nDel + 2*nIns + 2); // Type Array + + std::shared_ptr<SvxTabStopItem> aAttr(std::make_shared<SvxTabStopItem>(0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP)); + + const SwFormat * pSty = nullptr; + sal_uInt16 nTabBase; + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // StyleDef + { + nTabBase = m_vColl[m_nCurrentColl].m_nBase; + if (nTabBase < m_vColl.size()) // Based On + pSty = m_vColl[nTabBase].m_pFormat; + } + else + { // Text + nTabBase = m_nCurrentColl; + if (m_nCurrentColl < m_vColl.size()) + pSty = m_vColl[m_nCurrentColl].m_pFormat; + //TODO: figure out else here + } + + bool bFound = false; + std::unordered_set<size_t> aLoopWatch; + while (pSty && !bFound) + { + const SfxPoolItem* pTabs; + bFound = pSty->GetAttrSet().GetItemState(RES_PARATR_TABSTOP, false, + &pTabs) == SfxItemState::SET; + if( bFound ) + { + aAttr.reset(static_cast<SvxTabStopItem*>(pTabs->Clone())); + } + else + { + sal_uInt16 nOldTabBase = nTabBase; + // If based on another + if (nTabBase < m_vColl.size()) + nTabBase = m_vColl[nTabBase].m_nBase; + + if ( + nTabBase < m_vColl.size() && + nOldTabBase != nTabBase && + nTabBase != ww::stiNil + ) + { + // #i61789: Stop searching when next style is the same as the + // current one (prevent loop) + aLoopWatch.insert(reinterpret_cast<size_t>(pSty)); + if (nTabBase < m_vColl.size()) + pSty = m_vColl[nTabBase].m_pFormat; + //TODO figure out the else branch + + if (aLoopWatch.find(reinterpret_cast<size_t>(pSty)) != + aLoopWatch.end()) + pSty = nullptr; + } + else + pSty = nullptr; // Give up on the search + } + } + + SvxTabStop aTabStop; + for (short i=0; i < nDel; ++i) + { + sal_uInt16 nPos = aAttr->GetPos(SVBT16ToUInt16(pDel + i*2)); + if( nPos != SVX_TAB_NOTFOUND ) + aAttr->Remove( nPos ); + } + + for (short i=0; i < nIns; ++i) + { + short nPos = SVBT16ToUInt16(pIns + i*2); + aTabStop.GetTabPos() = nPos; + switch( pTyp[i].aBits1 & 0x7 ) // pTyp[i].jc + { + case 0: + aTabStop.GetAdjustment() = SvxTabAdjust::Left; + break; + case 1: + aTabStop.GetAdjustment() = SvxTabAdjust::Center; + break; + case 2: + aTabStop.GetAdjustment() = SvxTabAdjust::Right; + break; + case 3: + aTabStop.GetAdjustment() = SvxTabAdjust::Decimal; + break; + case 4: + continue; // Ignore Bar + } + + switch( pTyp[i].aBits1 >> 3 & 0x7 ) + { + case 0: + aTabStop.GetFill() = ' '; + break; + case 1: + aTabStop.GetFill() = '.'; + break; + case 2: + aTabStop.GetFill() = '-'; + break; + case 3: + case 4: + aTabStop.GetFill() = '_'; + break; + } + + sal_uInt16 nPos2 = aAttr->GetPos( nPos ); + if (nPos2 != SVX_TAB_NOTFOUND) + aAttr->Remove(nPos2); // Or else Insert() refuses + aAttr->Insert(aTabStop); + } + + if (nIns || nDel) + NewAttr(*aAttr); + else + { + // Here we have a tab definition which inserts no extra tabs, or deletes + // no existing tabs. An older version of writer is probably the creator + // of the document :-( . So if we are importing a style we can just + // ignore it. But if we are importing into text we cannot as during + // text SwWW8ImplReader::Read_Tab is called at the begin and end of + // the range the attrib affects, and ignoring it would upset the + // balance + if (!m_pCurrentColl) // not importing into a style + { + SvxTabStopItem aOrig = pSty ? + ItemGet<SvxTabStopItem>(*pSty, RES_PARATR_TABSTOP) : + DefaultItemGet<SvxTabStopItem>(m_rDoc, RES_PARATR_TABSTOP); + NewAttr(aOrig); + } + } +} + +/** + * DOP +*/ +void SwWW8ImplReader::ImportDop() +{ + // correct the LastPrinted date in DocumentProperties + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocuProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocuProps.is(), "DocumentProperties is null"); + if (xDocuProps.is()) + { + DateTime aLastPrinted( + msfilter::util::DTTM2DateTime(m_xWDop->dttmLastPrint)); + ::util::DateTime uDT = aLastPrinted.GetUNODateTime(); + xDocuProps->setPrintDate(uDT); + } + + // COMPATIBILITY FLAGS START + + // #i78951# - remember the unknown compatibility options + // so as to export them out + m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions1(m_xWDop->GetCompatibilityOptions()); + m_rDoc.getIDocumentSettingAccess().Setn32DummyCompatibilityOptions2(m_xWDop->GetCompatibilityOptions2()); + + // The distance between two paragraphs is the sum of the bottom distance of + // the first paragraph and the top distance of the second one + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, m_xWDop->fDontUseHTMLAutoSpacing); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, true ); + // move tabs on alignment + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, true); + // #i24363# tab stops relative to indent + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, false); + // tdf#117923 + m_rDoc.getIDocumentSettingAccess().set( + DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, true); + m_rDoc.getIDocumentSettingAccess().set( + DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, true); + // tdf#128195 + m_rDoc.getIDocumentSettingAccess().set( + DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, true); + + // Import Default Tabs + long nDefTabSiz = m_xWDop->dxaTab; + if( nDefTabSiz < 56 ) + nDefTabSiz = 709; + + // We want exactly one DefaultTab + SvxTabStopItem aNewTab( 1, sal_uInt16(nDefTabSiz), SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + const_cast<SvxTabStop&>(aNewTab[0]).GetAdjustment() = SvxTabAdjust::Default; + + m_rDoc.GetAttrPool().SetPoolDefaultItem( aNewTab ); + + // Import zoom factor + if (m_xWDop->wScaleSaved) + { + //Import zoom type + sal_Int16 nZoomType; + switch (m_xWDop->zkSaved) { + case 1: nZoomType = sal_Int16(SvxZoomType::WHOLEPAGE); break; + case 2: nZoomType = sal_Int16(SvxZoomType::PAGEWIDTH); break; + case 3: nZoomType = sal_Int16(SvxZoomType::OPTIMAL); break; + default: nZoomType = sal_Int16(SvxZoomType::PERCENT); break; + } + uno::Sequence<beans::PropertyValue> aViewProps( comphelper::InitPropertySequence({ + { "ZoomFactor", uno::Any(sal_Int16(m_xWDop->wScaleSaved)) }, + { "VisibleBottom", uno::Any(sal_Int32(0)) }, + { "ZoomType", uno::Any(nZoomType) } + })); + + uno::Reference< uno::XComponentContext > xComponentContext(comphelper::getProcessComponentContext()); + uno::Reference<container::XIndexContainer> xBox = document::IndexedPropertyValues::create(xComponentContext); + xBox->insertByIndex(sal_Int32(0), uno::makeAny(aViewProps)); + uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY); + xViewDataSupplier->setViewData(xBox); + } + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_VIRTUAL_DEVICE, !m_xWDop->fUsePrinterMetrics); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, true ); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, !m_xWDop->fNoLeading); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, false); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, false); // #i47448# + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, !m_xWDop->fExpShRtn); // #i49277#, #i56856# + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, false); // #i53199# + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, false); + + // #i25901# - set new compatibility option + // 'Add paragraph and table spacing at bottom of table cells' + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, true); + + // #i11860# - set new compatibility option + // 'Use former object positioning' to <false> + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, false); + + // #i27767# - set new compatibility option + // 'Consider Wrapping mode when positioning object' to <true> + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, true); + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, false); // #i13832#, #i24135# + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, true); //SetTableRowKeep( true ); + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, true); // #i3952# + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVERFLOW, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, true); + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, true); + + // COMPATIBILITY FLAGS END + + // Import magic doptypography information, if its there + if (m_xWwFib->m_nFib > 105) + ImportDopTypography(m_xWDop->doptypography); + + // disable form design mode to be able to use imported controls directly + // #i31239# always disable form design mode, not only in protected docs + uno::Reference<beans::XPropertySet> xDocProps(m_pDocShell->GetModel(), uno::UNO_QUERY); + if (xDocProps.is()) + { + uno::Reference<beans::XPropertySetInfo> xInfo = xDocProps->getPropertySetInfo(); + if (xInfo.is()) + { + if (xInfo->hasPropertyByName("ApplyFormDesignMode")) + xDocProps->setPropertyValue("ApplyFormDesignMode", css::uno::makeAny(false)); + } + } + + // The password can force read-only, comments-only, fill-in-form-only, or require track-changes. + // Treat comments-only like read-only since Writer has no support for that. + // Still allow editing of form fields, without requiring the password. + // Still allow editing if track-changes is locked on. (Currently LockRev is ignored/lost on export anyway.) + if (!m_xWDop->fProtEnabled && !m_xWDop->fLockRev) + m_pDocShell->SetModifyPasswordHash(m_xWDop->lKeyProtDoc); + else if ( xDocProps.is() ) + { + comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue("InteropGrabBag")); + aGrabBag["FormPasswordHash"] <<= m_xWDop->lKeyProtDoc; + xDocProps->setPropertyValue("InteropGrabBag", uno::Any(aGrabBag.getAsConstPropertyValueList())); + } + + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + if (rOpt.IsUseEnhancedFields()) + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, m_xWDop->fProtEnabled ); +} + +void SwWW8ImplReader::ImportDopTypography(const WW8DopTypography &rTypo) +{ + switch (rTypo.m_iLevelOfKinsoku) + { + case 2: // custom + { + i18n::ForbiddenCharacters aForbidden(rTypo.m_rgxchFPunct, + rTypo.m_rgxchLPunct); + m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(rTypo.GetConvertedLang(), + aForbidden); + // Obviously cannot set the standard level 1 for japanese, so + // bail out now while we can. + if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE) + return; + } + break; + default: + break; + } + + /* + This MS hack means that level 2 of japanese is not in operation, so we put + in what we know are the MS defaults, there is a complementary reverse + hack in the writer. Its our default as well, but we can set it anyway + as a flag for later. + */ + if (!rTypo.m_reserved2) + { + i18n::ForbiddenCharacters aForbidden(WW8DopTypography::GetJapanNotBeginLevel1(), + WW8DopTypography::GetJapanNotEndLevel1()); + m_rDoc.getIDocumentSettingAccess().setForbiddenCharacters(LANGUAGE_JAPANESE,aForbidden); + } + + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bool(rTypo.m_fKerningPunct)); + m_rDoc.getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(rTypo.m_iJustification)); +} + +/** + * Footnotes and Endnotes + */ +WW8ReaderSave::WW8ReaderSave(SwWW8ImplReader* pRdr ,WW8_CP nStartCp) : + maTmpPos(*pRdr->m_pPaM->GetPoint()), + mxOldStck(std::move(pRdr->m_xCtrlStck)), + mxOldAnchorStck(std::move(pRdr->m_xAnchorStck)), + mxOldRedlines(std::move(pRdr->m_xRedlineStack)), + mxOldPlcxMan(pRdr->m_xPlcxMan), + mpWFlyPara(std::move(pRdr->m_xWFlyPara)), + mpSFlyPara(std::move(pRdr->m_xSFlyPara)), + mpPreviousNumPaM(pRdr->m_pPreviousNumPaM), + mpPrevNumRule(pRdr->m_pPrevNumRule), + mxTableDesc(std::move(pRdr->m_xTableDesc)), + mnInTable(pRdr->m_nInTable), + mnCurrentColl(pRdr->m_nCurrentColl), + mcSymbol(pRdr->m_cSymbol), + mbIgnoreText(pRdr->m_bIgnoreText), + mbSymbol(pRdr->m_bSymbol), + mbHdFtFootnoteEdn(pRdr->m_bHdFtFootnoteEdn), + mbTxbxFlySection(pRdr->m_bTxbxFlySection), + mbAnl(pRdr->m_bAnl), + mbInHyperlink(pRdr->m_bInHyperlink), + mbPgSecBreak(pRdr->m_bPgSecBreak), + mbWasParaEnd(pRdr->m_bWasParaEnd), + mbHasBorder(pRdr->m_bHasBorder), + mbFirstPara(pRdr->m_bFirstPara) +{ + pRdr->m_bSymbol = false; + pRdr->m_bHdFtFootnoteEdn = true; + pRdr->m_bTxbxFlySection = pRdr->m_bAnl = pRdr->m_bPgSecBreak = pRdr->m_bWasParaEnd + = pRdr->m_bHasBorder = false; + pRdr->m_bFirstPara = true; + pRdr->m_nInTable = 0; + pRdr->m_pPreviousNumPaM = nullptr; + pRdr->m_pPrevNumRule = nullptr; + pRdr->m_nCurrentColl = 0; + + pRdr->m_xCtrlStck.reset(new SwWW8FltControlStack(&pRdr->m_rDoc, pRdr->m_nFieldFlags, + *pRdr)); + + pRdr->m_xRedlineStack.reset(new sw::util::RedlineStack(pRdr->m_rDoc)); + + pRdr->m_xAnchorStck.reset(new SwWW8FltAnchorStack(&pRdr->m_rDoc, pRdr->m_nFieldFlags)); + + // Save the attribute manager: we need this as the newly created PLCFx Manager + // access the same FKPs as the old one and their Start-End position changes. + if (pRdr->m_xPlcxMan) + pRdr->m_xPlcxMan->SaveAllPLCFx(maPLCFxSave); + + if (nStartCp != -1) + { + pRdr->m_xPlcxMan = std::make_shared<WW8PLCFMan>(pRdr->m_xSBase.get(), + mxOldPlcxMan->GetManType(), nStartCp); + } + + maOldApos.push_back(false); + maOldApos.swap(pRdr->m_aApos); + maOldFieldStack.swap(pRdr->m_aFieldStack); +} + +void WW8ReaderSave::Restore( SwWW8ImplReader* pRdr ) +{ + pRdr->m_xWFlyPara = std::move(mpWFlyPara); + pRdr->m_xSFlyPara = std::move(mpSFlyPara); + pRdr->m_pPreviousNumPaM = mpPreviousNumPaM; + pRdr->m_pPrevNumRule = mpPrevNumRule; + pRdr->m_xTableDesc = std::move(mxTableDesc); + pRdr->m_cSymbol = mcSymbol; + pRdr->m_bSymbol = mbSymbol; + pRdr->m_bIgnoreText = mbIgnoreText; + pRdr->m_bHdFtFootnoteEdn = mbHdFtFootnoteEdn; + pRdr->m_bTxbxFlySection = mbTxbxFlySection; + pRdr->m_nInTable = mnInTable; + pRdr->m_bAnl = mbAnl; + pRdr->m_bInHyperlink = mbInHyperlink; + pRdr->m_bWasParaEnd = mbWasParaEnd; + pRdr->m_bPgSecBreak = mbPgSecBreak; + pRdr->m_nCurrentColl = mnCurrentColl; + pRdr->m_bHasBorder = mbHasBorder; + pRdr->m_bFirstPara = mbFirstPara; + + // Close all attributes as attributes could be created that extend the Fly + pRdr->DeleteCtrlStack(); + pRdr->m_xCtrlStck = std::move(mxOldStck); + + pRdr->m_xRedlineStack->closeall(*pRdr->m_pPaM->GetPoint()); + pRdr->m_aFrameRedlines.emplace(std::move(pRdr->m_xRedlineStack)); + pRdr->m_xRedlineStack = std::move(mxOldRedlines); + + pRdr->DeleteAnchorStack(); + pRdr->m_xAnchorStck = std::move(mxOldAnchorStck); + + *pRdr->m_pPaM->GetPoint() = maTmpPos; + + if (mxOldPlcxMan != pRdr->m_xPlcxMan) + pRdr->m_xPlcxMan = mxOldPlcxMan; + if (pRdr->m_xPlcxMan) + pRdr->m_xPlcxMan->RestoreAllPLCFx(maPLCFxSave); + pRdr->m_aApos.swap(maOldApos); + pRdr->m_aFieldStack.swap(maOldFieldStack); +} + +void SwWW8ImplReader::Read_HdFtFootnoteText( const SwNodeIndex* pSttIdx, + WW8_CP nStartCp, WW8_CP nLen, ManTypes nType ) +{ + if (nStartCp < 0 || nLen < 0) + return; + + // Saves Flags (amongst other things) and resets them + WW8ReaderSave aSave( this ); + + m_pPaM->GetPoint()->nNode = pSttIdx->GetIndex() + 1; + m_pPaM->GetPoint()->nContent.Assign( m_pPaM->GetContentNode(), 0 ); + + // Read Text for Header, Footer or Footnote + ReadText( nStartCp, nLen, nType ); // Ignore Sepx when doing so + aSave.Restore( this ); +} + +/** + * Use authornames, if not available fall back to initials. + */ +long SwWW8ImplReader::Read_And(WW8PLCFManResult* pRes) +{ + WW8PLCFx_SubDoc* pSD = m_xPlcxMan->GetAtn(); + if (!pSD) + return 0; + + const void* pData = pSD->GetData(); + if (!pData) + return 0; + + OUString sAuthor; + OUString sInitials; + if( m_bVer67 ) + { + const WW67_ATRD* pDescri = static_cast<const WW67_ATRD*>(pData); + const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst)); + if (pA) + sAuthor = *pA; + else + { + const sal_uInt8 nLen = std::min<sal_uInt8>(pDescri->xstUsrInitl[0], + SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1); + sAuthor = OUString(pDescri->xstUsrInitl + 1, nLen, RTL_TEXTENCODING_MS_1252); + } + } + else + { + const WW8_ATRD* pDescri = static_cast<const WW8_ATRD*>(pData); + { + const sal_uInt16 nLen = std::min<sal_uInt16>(SVBT16ToUInt16(pDescri->xstUsrInitl[0]), + SAL_N_ELEMENTS(pDescri->xstUsrInitl)-1); + OUStringBuffer aBuf; + aBuf.setLength(nLen); + for(sal_uInt16 nIdx = 1; nIdx <= nLen; ++nIdx) + aBuf[nIdx-1] = SVBT16ToUInt16(pDescri->xstUsrInitl[nIdx]); + sInitials = aBuf.makeStringAndClear(); + } + + if (const OUString* pA = GetAnnotationAuthor(SVBT16ToUInt16(pDescri->ibst))) + sAuthor = *pA; + else + sAuthor = sInitials; + } + + sal_uInt32 nDateTime = 0; + + if (sal_uInt8 * pExtended = m_xPlcxMan->GetExtendedAtrds()) // Word < 2002 has no date data for comments + { + sal_uLong nIndex = pSD->GetIdx() & 0xFFFF; // Index is (stupidly) multiplexed for WW8PLCFx_SubDocs + if (m_xWwFib->m_lcbAtrdExtra/18 > nIndex) + nDateTime = SVBT32ToUInt32(*reinterpret_cast<SVBT32*>(pExtended+(nIndex*18))); + } + + DateTime aDate = msfilter::util::DTTM2DateTime(nDateTime); + + OUString sText; + std::unique_ptr<OutlinerParaObject> pOutliner = ImportAsOutliner( sText, pRes->nCp2OrIdx, + pRes->nCp2OrIdx + pRes->nMemLen, MAN_AND ); + + m_xFormatOfJustInsertedApo.reset(); + SwPostItField aPostIt( + static_cast<SwPostItFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit)), sAuthor, + sText, sInitials, OUString(), aDate ); + aPostIt.SetTextObject(std::move(pOutliner)); + + SwPaM aEnd(*m_pPaM->End(), *m_pPaM->End()); + m_xCtrlStck->NewAttr(*aEnd.GetPoint(), SvxCharHiddenItem(false, RES_CHRATR_HIDDEN)); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(aEnd, SwFormatField(aPostIt)); + m_xCtrlStck->SetAttr(*aEnd.GetPoint(), RES_CHRATR_HIDDEN); + // If this is a range, make sure that it ends after the just inserted character, not before it. + m_xReffedStck->MoveAttrs(*aEnd.GetPoint(), SwFltControlStack::MoveAttrsMode::POSTIT_INSERTED); + + return 0; +} + +void SwWW8ImplReader::Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen, + SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth) +{ + const SwNodeIndex* pSttIdx = rHdFtFormat.GetContent().GetContentIdx(); + OSL_ENSURE(pSttIdx, "impossible"); + if (!pSttIdx) + return; + + SwPosition aTmpPos(*m_pPaM->GetPoint()); + + m_pPaM->GetPoint()->nNode = pSttIdx->GetIndex() + 1; + m_pPaM->GetPoint()->nContent.Assign(m_pPaM->GetContentNode(), 0); + + // tdf#122425: Explicitly remove borders and spacing + SfxItemSet aFlySet(m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{}); + Reader::ResetFrameFormatAttrs(aFlySet); + + SwFlyFrameFormat* pFrame + = m_rDoc.MakeFlySection(RndStdIds::FLY_AT_PARA, m_pPaM->GetPoint(), &aFlySet); + + SwFormatAnchor aAnch( pFrame->GetAnchor() ); + aAnch.SetType( RndStdIds::FLY_AT_PARA ); + pFrame->SetFormatAttr( aAnch ); + SwFormatFrameSize aSz(SwFrameSize::Minimum, nPageWidth, MINLAY); + SwFrameSize eFrameSize = SwFrameSize::Minimum; + if( eFrameSize != aSz.GetWidthSizeType() ) + aSz.SetWidthSizeType( eFrameSize ); + pFrame->SetFormatAttr(aSz); + pFrame->SetFormatAttr(SwFormatSurround(css::text::WrapTextMode_THROUGH)); + pFrame->SetFormatAttr(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); //iFOO + + // #i43427# - send frame for header/footer into background. + pFrame->SetFormatAttr( SvxOpaqueItem( RES_OPAQUE, false ) ); + SdrObject* pFrameObj = CreateContactObject( pFrame ); + OSL_ENSURE( pFrameObj, + "<SwWW8ImplReader::Read_HdFtTextAsHackedFrame(..)> - missing SdrObject instance" ); + if ( pFrameObj ) + { + pFrameObj->SetOrdNum( 0 ); + } + MoveInsideFly(pFrame); + + const SwNodeIndex* pHackIdx = pFrame->GetContent().GetContentIdx(); + + Read_HdFtFootnoteText(pHackIdx, nStart, nLen - 1, MAN_HDFT); + + MoveOutsideFly(pFrame, aTmpPos); +} + +void SwWW8ImplReader::Read_HdFtText(WW8_CP nStart, WW8_CP nLen, SwFrameFormat const * pHdFtFormat) +{ + const SwNodeIndex* pSttIdx = pHdFtFormat->GetContent().GetContentIdx(); + if (!pSttIdx) + return; + + SwPosition aTmpPos( *m_pPaM->GetPoint() ); // Remember old cursor position + + Read_HdFtFootnoteText(pSttIdx, nStart, nLen - 1, MAN_HDFT); + + *m_pPaM->GetPoint() = aTmpPos; +} + +bool SwWW8ImplReader::isValid_HdFt_CP(WW8_CP nHeaderCP) const +{ + // Each CP of Plcfhdd MUST be less than FibRgLw97.ccpHdd + return (nHeaderCP < m_xWwFib->m_ccpHdr && nHeaderCP >= 0); +} + +bool SwWW8ImplReader::HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt, + int nSect) +{ + if (m_xHdFt) + { + WW8_CP nStart, nLen; + sal_uInt8 nNumber = 5; + + for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- ) + { + if (nI & nWhichItems) + { + bool bOk = true; + if( m_bVer67 ) + bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nStart >= 0 && nLen >= 2 ); + else + { + m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen); + bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart); + } + + if (bOk) + return true; + } + } + } + return false; +} + +void SwWW8ImplReader::Read_HdFt(int nSect, const SwPageDesc *pPrev, + const wwSection &rSection) +{ + sal_uInt8 grpfIhdt = rSection.maSep.grpfIhdt; + SwPageDesc *pPD = rSection.mpPage; + + if( m_xHdFt ) + { + WW8_CP nStart, nLen; + sal_uInt8 nNumber = 5; + + // This loops through the 6 flags WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST} + // corresponding to bit fields in grpfIhdt indicating which + // header/footer(s) are present in this section + for( sal_uInt8 nI = 0x20; nI; nI >>= 1, nNumber-- ) + { + if (nI & grpfIhdt) + { + bool bOk = true; + if( m_bVer67 ) + bOk = ( m_xHdFt->GetTextPos(grpfIhdt, nI, nStart, nLen ) && nLen >= 2 ); + else + { + m_xHdFt->GetTextPosExact( static_cast< short >(nNumber + (nSect+1)*6), nStart, nLen); + bOk = ( 2 <= nLen ) && isValid_HdFt_CP(nStart); + } + + bool bUseLeft + = (nI & ( WW8_HEADER_EVEN | WW8_FOOTER_EVEN )) != 0; + bool bUseFirst + = (nI & ( WW8_HEADER_FIRST | WW8_FOOTER_FIRST )) != 0; + + // If we are loading a first-page header/footer which is not + // actually enabled in this section (it still needs to be + // loaded as it may be inherited by a later section) + bool bDisabledFirst = bUseFirst && !rSection.HasTitlePage(); + + bool bFooter + = (nI & ( WW8_FOOTER_EVEN | WW8_FOOTER_ODD | WW8_FOOTER_FIRST )) != 0; + + SwFrameFormat& rFormat = bUseLeft ? pPD->GetLeft() + : bUseFirst ? pPD->GetFirstMaster() + : pPD->GetMaster(); + + SwFrameFormat* pHdFtFormat; + // If we have empty first page header and footer. + bool bNoFirst = !(grpfIhdt & WW8_HEADER_FIRST) && !(grpfIhdt & WW8_FOOTER_FIRST); + if (bFooter) + { + m_bIsFooter = true; + //#i17196# Cannot have left without right + if (!bDisabledFirst + && !pPD->GetMaster().GetFooter().GetFooterFormat()) + pPD->GetMaster().SetFormatAttr(SwFormatFooter(true)); + if (bUseLeft) + pPD->GetLeft().SetFormatAttr(SwFormatFooter(true)); + if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst)) + pPD->GetFirstMaster().SetFormatAttr(SwFormatFooter(true)); + pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat()); + } + else + { + m_bIsHeader = true; + //#i17196# Cannot have left without right + if (!bDisabledFirst + && !pPD->GetMaster().GetHeader().GetHeaderFormat()) + pPD->GetMaster().SetFormatAttr(SwFormatHeader(true)); + if (bUseLeft) + pPD->GetLeft().SetFormatAttr(SwFormatHeader(true)); + if (bUseFirst || (rSection.maSep.fTitlePage && bNoFirst)) + pPD->GetFirstMaster().SetFormatAttr(SwFormatHeader(true)); + pHdFtFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat()); + } + + if (bOk) + { + bool bHackRequired = false; + if (m_bIsHeader && rSection.IsFixedHeightHeader()) + bHackRequired = true; + else if (m_bIsFooter && rSection.IsFixedHeightFooter()) + bHackRequired = true; + + if (bHackRequired) + { + Read_HdFtTextAsHackedFrame(nStart, nLen, *pHdFtFormat, + static_cast< sal_uInt16 >(rSection.GetTextAreaWidth()) ); + } + else + Read_HdFtText(nStart, nLen, pHdFtFormat); + } + else if (pPrev) + CopyPageDescHdFt(pPrev, pPD, nI); + + m_bIsHeader = m_bIsFooter = false; + } + } + } +} + +bool wwSectionManager::SectionIsProtected(const wwSection &rSection) const +{ + return ( mrReader.m_xWDop->fProtEnabled && !rSection.IsNotProtected() ); +} + +void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect, + const wwSection *pPrevious) +{ + // Header/Footer not present + if (!rSection.maSep.grpfIhdt) + return; + + OSL_ENSURE(rSection.mpPage, "makes no sense to call with a main page"); + if (rSection.mpPage) + { + mrReader.Read_HdFt(nSect, pPrevious ? pPrevious->mpPage : nullptr, + rSection); + } + + // Header/Footer - Update Index + // So that the index is still valid later on + if (mrReader.m_xHdFt) + mrReader.m_xHdFt->UpdateIndex(rSection.maSep.grpfIhdt); + +} + +void SwWW8ImplReader::AppendTextNode(SwPosition& rPos) +{ + SwTextNode* pText = m_pPaM->GetNode().GetTextNode(); + + const SwNumRule* pRule = nullptr; + + if (pText != nullptr) + pRule = sw::util::GetNumRuleFromTextNode(*pText); + + if ( + pRule && !m_xWDop->fDontUseHTMLAutoSpacing && + (m_bParaAutoBefore || m_bParaAutoAfter) + ) + { + // If after spacing is set to auto, set the after space to 0 + if (m_bParaAutoAfter) + SetLowerSpacing(*m_pPaM, 0); + + // If the previous textnode had numbering and + // and before spacing is set to auto, set before space to 0 + if(m_pPrevNumRule && m_bParaAutoBefore) + SetUpperSpacing(*m_pPaM, 0); + + // If the previous numbering rule was different we need + // to insert a space after the previous paragraph + if((pRule != m_pPrevNumRule) && m_pPreviousNumPaM) + SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing)); + + // cache current paragraph + if(m_pPreviousNumPaM) + { + delete m_pPreviousNumPaM; + m_pPreviousNumPaM = nullptr; + } + + m_pPreviousNumPaM = new SwPaM(*m_pPaM, m_pPaM); + m_pPrevNumRule = pRule; + } + else if(!pRule && m_pPreviousNumPaM) + { + // If the previous paragraph has numbering but the current one does not + // we need to add a space after the previous paragraph + SetLowerSpacing(*m_pPreviousNumPaM, GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing)); + delete m_pPreviousNumPaM; + m_pPreviousNumPaM = nullptr; + m_pPrevNumRule = nullptr; + } + else + { + // clear paragraph cache + if(m_pPreviousNumPaM) + { + delete m_pPreviousNumPaM; + m_pPreviousNumPaM = nullptr; + } + m_pPrevNumRule = pRule; + } + + // If this is the first paragraph in the document and + // Auto-spacing before paragraph is set, + // set the upper spacing value to 0 + if(m_bParaAutoBefore && m_bFirstPara && !m_xWDop->fDontUseHTMLAutoSpacing) + SetUpperSpacing(*m_pPaM, 0); + + m_bFirstPara = false; + + m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos); + + // We can flush all anchored graphics at the end of a paragraph. + m_xAnchorStck->Flush(); +} + +bool SwWW8ImplReader::SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper ) +{ + bool bRet = false; + const SwPosition* pSpacingPos = rMyPam.GetPoint(); + + const SvxULSpaceItem* pULSpaceItem = m_xCtrlStck->GetFormatAttr(*pSpacingPos, RES_UL_SPACE); + + if(pULSpaceItem != nullptr) + { + SvxULSpaceItem aUL(*pULSpaceItem); + + if(bIsUpper) + aUL.SetUpper( static_cast< sal_uInt16 >(nSpace) ); + else + aUL.SetLower( static_cast< sal_uInt16 >(nSpace) ); + + const sal_Int32 nEnd = pSpacingPos->nContent.GetIndex(); + rMyPam.GetPoint()->nContent.Assign(rMyPam.GetContentNode(), 0); + m_xCtrlStck->NewAttr(*pSpacingPos, aUL); + rMyPam.GetPoint()->nContent.Assign(rMyPam.GetContentNode(), nEnd); + m_xCtrlStck->SetAttr(*pSpacingPos, RES_UL_SPACE); + bRet = true; + } + return bRet; +} + +bool SwWW8ImplReader::SetLowerSpacing(SwPaM &rMyPam, int nSpace) +{ + return SetSpacing(rMyPam, nSpace, false); +} + +bool SwWW8ImplReader::SetUpperSpacing(SwPaM &rMyPam, int nSpace) +{ + return SetSpacing(rMyPam, nSpace, true); +} + +sal_uInt16 SwWW8ImplReader::TabRowSprm(int nLevel) const +{ + if (m_bVer67) + return NS_sprm::v6::sprmPTtp; + return nLevel ? NS_sprm::sprmPFInnerTtp : NS_sprm::sprmPFTtp; +} + +void SwWW8ImplReader::EndSpecial() +{ + // Frame/Table/Anl + if (m_bAnl) + StopAllAnl(); // -> bAnl = false + + while(m_aApos.size() > 1) + { + StopTable(); + m_aApos.pop_back(); + --m_nInTable; + if (m_aApos[m_nInTable]) + StopApo(); + } + + if (m_aApos[0]) + StopApo(); + + OSL_ENSURE(!m_nInTable, "unclosed table!"); +} + +bool SwWW8ImplReader::FloatingTableConversion(WW8PLCFx_Cp_FKP* pPap) +{ + // This is ww8 version of the code deciding if the table needs to be + // in a floating frame. + // For OOXML code, see SectionPropertyMap::FloatingTableConversion in + // writerfilter/source/dmapper/PropertyMap.cxx + // The two should do ~same, so if you make changes here, please check + // that the other is in sync. + + // Note that this is just a list of heuristics till sw core can have a + // table that is floating and can span over multiple pages at the same + // time. + + // If the floating table is in a header or footer, then it won't be a + // multi-page one, so can always do the conversion. + if (m_bIsHeader || m_bIsFooter) + { + return true; + } + + bool bResult = true; + + SprmResult aRes = pPap->HasSprm(NS_sprm::sprmTDefTable); + if (nullptr != aRes.pSprm) + { + bResult = false; + WW8TabBandDesc aDesc; + aDesc.ReadDef(false, aRes.pSprm, aRes.nRemainingData); + int nTextAreaWidth = m_aSectionManager.GetTextAreaWidth(); + int nTableWidth = aDesc.nCenter[aDesc.nWwCols] - aDesc.nCenter[0]; + + // It seems Word has a limit here, so that in case the table width is quite + // close to the text area width, then it won't perform a wrapping, even in + // case the content (e.g. an empty paragraph) would fit. The magic constant + // here represents this limit. + const int nMagicNumber = 469; + + // If the table is wider than the text area, then don't create a fly + // for the table: no wrapping will be performed anyway, but multi-page + // tables will be broken. + if ((nTableWidth + nMagicNumber) < nTextAreaWidth) + bResult = true; + + // If there are columns, do create a fly, as the flow of the columns + // would otherwise restrict the table. + if (!bResult && (m_aSectionManager.CurrentSectionColCount() >= 2)) + bResult = true; + } + + if (bResult) + { + WW8PLCFxSave1 aSave; + pPap->Save(aSave); + if (SearchTableEnd(pPap)) + { + // Table is considered to be imported into a fly frame and we + // know where the end of the table is. + bool bIsUnicode; + WW8_FC nFc = m_xSBase->WW8Cp2Fc(pPap->Where(), &bIsUnicode); + sal_uInt64 nPos = m_pStrm->Tell(); + m_pStrm->Seek(nFc); + sal_uInt16 nUChar = 0; + if (bIsUnicode) + m_pStrm->ReadUInt16(nUChar); + else + { + sal_uInt8 nChar = 0; + m_pStrm->ReadUChar(nChar); + nUChar = nChar; + } + m_pStrm->Seek(nPos); + if (nUChar == 0xc) + // The pap after the table starts with a page break, so + // there will be no wrapping around the float-table. + // Request no fly in this case, so the table can properly + // be a multi-page one if necessary. + bResult = false; + } + pPap->Restore(aSave); + } + + return bResult; +} + +bool SwWW8ImplReader::ProcessSpecial(bool &rbReSync, WW8_CP nStartCp) +{ + // Frame/Table/Anl + if (m_bInHyperlink) + return false; + + rbReSync = false; + + OSL_ENSURE(m_nInTable >= 0,"nInTable < 0!"); + + // TabRowEnd + bool bTableRowEnd = (m_xPlcxMan->HasParaSprm(m_bVer67 ? 25 : 0x2417).pSprm != nullptr); + +// Unfortunately, for every paragraph we need to check first whether +// they contain a sprm 29 (0x261B), which starts an APO. +// All other sprms then refer to that APO and not to the normal text +// surrounding it. +// The same holds true for a Table (sprm 24 (0x2416)) and Anls (sprm 13). + +// WW: Table in APO is possible (Both Start-Ends occur at the same time) +// WW: APO in Table not possible + +// This mean that of a Table is the content of an APO, the APO start needs +// to be edited first, so that the Table remains in the APO and not the +// other way around. +// At the End, however, we need to edit the Table End first as the APO +// must end after that Table (or else we never find the APO End). + +// The same holds true for Fly / Anl, Tab / Anl, Fly / Tab / Anl. + +// If the Table is within an APO the TabRowEnd Area misses the +// APO settings. +// To not end the APO there, we do not call ProcessApo + +// KHZ: When there is a table inside the Apo the Apo-flags are also +// missing for the 2nd, 3rd... paragraphs of each cell. + +// 1st look for in-table flag, for 2000+ there is a subtable flag to +// be considered, the sprm 6649 gives the level of the table + sal_uInt8 nCellLevel = 0; + + if (m_bVer67) + nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(24).pSprm); + else + { + nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x2416).pSprm); + if (!nCellLevel) + nCellLevel = int(nullptr != m_xPlcxMan->HasParaSprm(0x244B).pSprm); + } + do + { + WW8_TablePos *pTabPos=nullptr; + WW8_TablePos aTabPos; + if(nCellLevel && !m_bVer67) + { + WW8PLCFxSave1 aSave; + m_xPlcxMan->GetPap()->Save( aSave ); + rbReSync = true; + WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF(); + WW8_CP nMyStartCp=nStartCp; + + SprmResult aLevel = m_xPlcxMan->HasParaSprm(0x6649); + if (aLevel.pSprm && aLevel.nRemainingData >= 1) + nCellLevel = *aLevel.pSprm; + + bool bHasRowEnd = SearchRowEnd(pPap, nMyStartCp, (m_nInTable<nCellLevel?m_nInTable:nCellLevel-1)); + + // Bad Table, remain unchanged in level, e.g. #i19667# + if (!bHasRowEnd) + nCellLevel = static_cast< sal_uInt8 >(m_nInTable); + + if (bHasRowEnd && ParseTabPos(&aTabPos,pPap)) + pTabPos = &aTabPos; + + m_xPlcxMan->GetPap()->Restore( aSave ); + } + + // Then look if we are in an Apo + + ApoTestResults aApo = TestApo(nCellLevel, bTableRowEnd, pTabPos); + + // Look to see if we are in a Table, but Table in foot/end note not allowed + bool bStartTab = (m_nInTable < nCellLevel) && !m_bFootnoteEdn; + + bool bStopTab = m_bWasTabRowEnd && (m_nInTable > nCellLevel) && !m_bFootnoteEdn; + + m_bWasTabRowEnd = false; // must be deactivated right here to prevent next + // WW8TabDesc::TableCellEnd() from making nonsense + + if (m_nInTable && !bTableRowEnd && !bStopTab && (m_nInTable == nCellLevel && aApo.HasStartStop())) + bStopTab = bStartTab = true; // Required to stop and start table + + // Test for Anl (Numbering) and process all events in the right order + if( m_bAnl && !bTableRowEnd ) + { + SprmResult aSprm13 = m_xPlcxMan->HasParaSprm(13); + const sal_uInt8* pSprm13 = aSprm13.pSprm; + if (pSprm13 && aSprm13.nRemainingData >= 1) + { // Still Anl left? + sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType( *pSprm13 )); + if( ( nT != WW8_Pause && nT != m_nWwNumType ) // Anl change + || aApo.HasStartStop() // Forced Anl end + || bStopTab || bStartTab ) + { + StopAnlToRestart(nT); // Anl-Restart (= change) over sprms + } + else + { + NextAnlLine( pSprm13 ); // Next Anl Line + } + } + else + { // Regular Anl end + StopAllAnl(); // Actual end + } + } + if (bStopTab) + { + StopTable(); + m_aApos.pop_back(); + --m_nInTable; + } + if (aApo.mbStopApo) + { + StopApo(); + m_aApos[m_nInTable] = false; + } + + if (aApo.mbStartApo) + { + m_aApos[m_nInTable] = StartApo(aApo, pTabPos); + // We need an ReSync after StartApo + // (actually only if the Apo extends past a FKP border) + rbReSync = true; + } + if (bStartTab) + { + WW8PLCFxSave1 aSave; + m_xPlcxMan->GetPap()->Save( aSave ); + + // Numbering for cell borders causes a crash -> no Anls in Tables + if (m_bAnl) + StopAllAnl(); + + if(m_nInTable < nCellLevel) + { + if (StartTable(nStartCp)) + ++m_nInTable; + else + break; + m_aApos.push_back(false); + } + + if(m_nInTable >= nCellLevel) + { + // We need an ReSync after StartTable + // (actually only if the Apo extends past a FKP border) + rbReSync = true; + m_xPlcxMan->GetPap()->Restore( aSave ); + } + } + } while (!m_bFootnoteEdn && (m_nInTable < nCellLevel)); + return bTableRowEnd; +} + +rtl_TextEncoding SwWW8ImplReader::GetCharSetFromLanguage() +{ + /* + #i22206#/#i52786# + The (default) character set used for a run of text is the default + character set for the version of Word that last saved the document. + + This is a bit tentative, more might be required if the concept is correct. + When later version of word write older 6/95 documents the charset is + correctly set in the character runs involved, so it's hard to reproduce + documents that require this to be sure of the process involved. + */ + const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE)); + LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM; + css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang)); + return msfilter::util::getBestTextEncodingFromLocale(aLocale); +} + +rtl_TextEncoding SwWW8ImplReader::GetCJKCharSetFromLanguage() +{ + /* + #i22206#/#i52786# + The (default) character set used for a run of text is the default + character set for the version of Word that last saved the document. + + This is a bit tentative, more might be required if the concept is correct. + When later version of word write older 6/95 documents the charset is + correctly set in the character runs involved, so it's hard to reproduce + documents that require this to be sure of the process involved. + */ + const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_CJK_LANGUAGE)); + LanguageType eLang = pLang ? pLang->GetLanguage() : LANGUAGE_SYSTEM; + css::lang::Locale aLocale(LanguageTag::convertToLocale(eLang)); + return msfilter::util::getBestTextEncodingFromLocale(aLocale); +} + +rtl_TextEncoding SwWW8ImplReader::GetCurrentCharSet() +{ + /* + #i2015 + If the hard charset is set use it, if not see if there is an open + character run that has set the charset, if not then fallback to the + current underlying paragraph style. + */ + rtl_TextEncoding eSrcCharSet = m_eHardCharSet; + if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) + { + if (!m_bVer67) + eSrcCharSet = GetCharSetFromLanguage(); + else if (!m_aFontSrcCharSets.empty()) + eSrcCharSet = m_aFontSrcCharSets.top(); + if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() ) + eSrcCharSet = m_vColl[m_nCharFormat].GetCharSet(); + if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size()) + eSrcCharSet = m_vColl[m_nCurrentColl].GetCharSet(); + if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) + eSrcCharSet = GetCharSetFromLanguage(); + } + return eSrcCharSet; +} + +//Takashi Ono for CJK +rtl_TextEncoding SwWW8ImplReader::GetCurrentCJKCharSet() +{ + /* + #i2015 + If the hard charset is set use it, if not see if there is an open + character run that has set the charset, if not then fallback to the + current underlying paragraph style. + */ + rtl_TextEncoding eSrcCharSet = m_eHardCharSet; + if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) + { + if (!m_aFontSrcCJKCharSets.empty()) + eSrcCharSet = m_aFontSrcCJKCharSets.top(); + if ((eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) && m_nCharFormat >= 0 && o3tl::make_unsigned(m_nCharFormat) < m_vColl.size() ) + eSrcCharSet = m_vColl[m_nCharFormat].GetCJKCharSet(); + if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW && StyleExists(m_nCurrentColl) && m_nCurrentColl < m_vColl.size()) + eSrcCharSet = m_vColl[m_nCurrentColl].GetCJKCharSet(); + if (eSrcCharSet == RTL_TEXTENCODING_DONTKNOW) + eSrcCharSet = GetCJKCharSetFromLanguage(); + } + return eSrcCharSet; +} + +void SwWW8ImplReader::PostProcessAttrs() +{ + if (m_pPostProcessAttrsInfo != nullptr) + { + SfxItemIter aIter(m_pPostProcessAttrsInfo->mItemSet); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + m_xCtrlStck->NewAttr(*m_pPostProcessAttrsInfo->mPaM.GetPoint(), + *pItem); + m_xCtrlStck->SetAttr(*m_pPostProcessAttrsInfo->mPaM.GetMark(), + pItem->Which()); + } + + m_pPostProcessAttrsInfo.reset(); + } +} + +/* + #i9240# + It appears that some documents that are in a baltic 8 bit encoding which has + some undefined characters can have use made of those characters, in which + case they default to CP1252. If not then it's perhaps that the font encoding + is only in use for 6/7 and for 8+ if we are in 8bit mode then the encoding + is always 1252. + + So an encoding converter that on an undefined character attempts to + convert from 1252 on the undefined character +*/ +static std::size_t Custom8BitToUnicode(rtl_TextToUnicodeConverter hConverter, + char const *pIn, std::size_t nInLen, sal_Unicode *pOut, std::size_t nOutLen) +{ + const sal_uInt32 nFlags = + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE | + RTL_TEXTTOUNICODE_FLAGS_FLUSH; + + const sal_uInt32 nFlags2 = + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_IGNORE | + RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE | + RTL_TEXTTOUNICODE_FLAGS_FLUSH; + + std::size_t nDestChars=0; + std::size_t nConverted=0; + + do + { + sal_uInt32 nInfo = 0; + sal_Size nThisConverted=0; + + nDestChars += rtl_convertTextToUnicode(hConverter, nullptr, + pIn+nConverted, nInLen-nConverted, + pOut+nDestChars, nOutLen-nDestChars, + nFlags, &nInfo, &nThisConverted); + + OSL_ENSURE(nInfo == 0, "A character conversion failed!"); + + nConverted += nThisConverted; + + if ( + nInfo & RTL_TEXTTOUNICODE_INFO_UNDEFINED || + nInfo & RTL_TEXTTOUNICODE_INFO_MBUNDEFINED + ) + { + sal_Size nOtherConverted; + rtl_TextToUnicodeConverter hCP1252Converter = + rtl_createTextToUnicodeConverter(RTL_TEXTENCODING_MS_1252); + nDestChars += rtl_convertTextToUnicode(hCP1252Converter, nullptr, + pIn+nConverted, 1, + pOut+nDestChars, nOutLen-nDestChars, + nFlags2, &nInfo, &nOtherConverted); + rtl_destroyTextToUnicodeConverter(hCP1252Converter); + nConverted+=1; + } + } while (nConverted < nInLen); + + return nDestChars; +} + +bool SwWW8ImplReader::LangUsesHindiNumbers(LanguageType nLang) +{ + bool bResult = false; + + switch (static_cast<sal_uInt16>(nLang)) + { + case 0x1401: // Arabic(Algeria) + case 0x3c01: // Arabic(Bahrain) + case 0xc01: // Arabic(Egypt) + case 0x801: // Arabic(Iraq) + case 0x2c01: // Arabic (Jordan) + case 0x3401: // Arabic(Kuwait) + case 0x3001: // Arabic(Lebanon) + case 0x1001: // Arabic(Libya) + case 0x1801: // Arabic(Morocco) + case 0x2001: // Arabic(Oman) + case 0x4001: // Arabic(Qatar) + case 0x401: // Arabic(Saudi Arabia) + case 0x2801: // Arabic(Syria) + case 0x1c01: // Arabic(Tunisia) + case 0x3801: // Arabic(U.A.E) + case 0x2401: // Arabic(Yemen) + bResult = true; + break; + default: + break; + } + + return bResult; +} + +sal_Unicode SwWW8ImplReader::TranslateToHindiNumbers(sal_Unicode nChar) +{ + if (nChar >= 0x0030 && nChar <= 0x0039) + return nChar + 0x0630; + + return nChar; +} + +namespace +{ + OUString makeOUString(rtl_uString *pStr, sal_Int32 nAllocLen) + { + //if read len was in or around that of allocated len, just reuse pStr + if (nAllocLen < pStr->length + 256) + return OUString(pStr, SAL_NO_ACQUIRE); + //otherwise copy the shorter used section to release extra mem + OUString sRet(pStr->buffer, pStr->length); + rtl_uString_release(pStr); + return sRet; + } +} + +/** + * Return value: true for non special chars + */ +bool SwWW8ImplReader::ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs) +{ + sal_Int32 nRequestedStrLen = nEnd - rPos; + + OSL_ENSURE(nRequestedStrLen, "String is 0"); + if (nRequestedStrLen <= 0) + return true; + + sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+rPos, &m_bIsUnicode); + bool bValidPos = checkSeek(*m_pStrm, nRequestedPos); + OSL_ENSURE(bValidPos, "Document claimed to have more text than available"); + if (!bValidPos) + { + // Swallow missing range, e.g. #i95550# + rPos+=nRequestedStrLen; + return true; + } + + std::size_t nAvailableStrLen = m_pStrm->remainingSize() / (m_bIsUnicode ? 2 : 1); + OSL_ENSURE(nAvailableStrLen, "Document claimed to have more text than available"); + if (!nAvailableStrLen) + { + // Swallow missing range, e.g. #i95550# + rPos+=nRequestedStrLen; + return true; + } + + sal_Int32 nValidStrLen = std::min<std::size_t>(nRequestedStrLen, nAvailableStrLen); + + // Reset Unicode flag and correct FilePos if needed. + // Note: Seek is not expensive, as we're checking inline whether or not + // the correct FilePos has already been reached. + const sal_Int32 nStrLen = std::min(nValidStrLen, SAL_MAX_INT32-1); + + rtl_TextEncoding eSrcCharSet = m_bVer67 ? GetCurrentCharSet() : + RTL_TEXTENCODING_MS_1252; + if (m_bVer67 && eSrcCharSet == RTL_TEXTENCODING_MS_932) + { + /* + fdo#82904 + + Older documents exported as word 95 that use unicode aware fonts will + have the charset of those fonts set to RTL_TEXTENCODING_MS_932 on + export as the conversion from RTL_TEXTENCODING_UNICODE. This is a serious + pain. + + We will try and use a fallback encoding if the conversion from + RTL_TEXTENCODING_MS_932 fails, but you can get unlucky and get a document + which isn't really in RTL_TEXTENCODING_MS_932 but parts of it form + valid RTL_TEXTENCODING_MS_932 by chance :-( + + We're not the only ones that struggle with this: Here's the help from + MSOffice 2003 on the topic: + + << + Earlier versions of Microsoft Word were sometimes used in conjunction with + third-party language-processing add-in programs designed to support Chinese or + Korean on English versions of Microsoft Windows. Use of these add-ins sometimes + results in incorrect text display in more recent versions of Word. + + However, you can set options to convert these documents so that text is + displayed correctly. On the Tools menu, click Options, and then click the + General tab. In the English Word 6.0/95 documents list, select Contain Asian + text (to have Word interpret the text as Asian code page data, regardless of + its font) or Automatically detect Asian text (to have Word attempt to determine + which parts of the text are meant to be Asian). + >> + + What we can try here is to ignore a RTL_TEXTENCODING_MS_932 codepage if + the language is not Japanese + */ + + const SfxPoolItem * pItem = GetFormatAttr(RES_CHRATR_CJK_LANGUAGE); + if (pItem != nullptr && LANGUAGE_JAPANESE != static_cast<const SvxLanguageItem *>(pItem)->GetLanguage()) + { + SAL_WARN("sw.ww8", "discarding word95 RTL_TEXTENCODING_MS_932 encoding"); + eSrcCharSet = GetCharSetFromLanguage(); + } + } + const rtl_TextEncoding eSrcCJKCharSet = m_bVer67 ? GetCurrentCJKCharSet() : + RTL_TEXTENCODING_MS_1252; + + // allocate unicode string data + rtl_uString *pStr = rtl_uString_alloc(nStrLen); + sal_Unicode* pBuffer = pStr->buffer; + sal_Unicode* pWork = pBuffer; + + std::unique_ptr<char[]> p8Bits; + + rtl_TextToUnicodeConverter hConverter = nullptr; + if (!m_bIsUnicode || m_bVer67) + hConverter = rtl_createTextToUnicodeConverter(eSrcCharSet); + + if (!m_bIsUnicode) + p8Bits.reset( new char[nStrLen] ); + + // read the stream data + sal_uInt8 nBCode = 0; + sal_uInt16 nUCode; + + LanguageType nCTLLang = LANGUAGE_SYSTEM; + const SfxPoolItem * pItem = GetFormatAttr(RES_CHRATR_CTL_LANGUAGE); + if (pItem != nullptr) + nCTLLang = static_cast<const SvxLanguageItem *>(pItem)->GetLanguage(); + + sal_Int32 nL2; + for (nL2 = 0; nL2 < nStrLen; ++nL2) + { + if (m_bIsUnicode) + m_pStrm->ReadUInt16( nUCode ); // unicode --> read 2 bytes + else + { + m_pStrm->ReadUChar( nBCode ); // old code --> read 1 byte + nUCode = nBCode; + } + + if (m_pStrm->GetError()) + { + rPos = WW8_CP_MAX-10; // -> eof or other error + std::free(pStr); + return true; + } + + if ((32 > nUCode) || (0xa0 == nUCode)) + { + m_pStrm->SeekRel( m_bIsUnicode ? -2 : -1 ); + break; // Special character < 32, == 0xa0 found + } + + if (m_bIsUnicode) + { + if (!m_bVer67) + *pWork++ = nUCode; + else + { + if (nUCode >= 0x3000) //0x8000 ? + { + char aTest[2]; + aTest[0] = static_cast< char >((nUCode & 0xFF00) >> 8); + aTest[1] = static_cast< char >(nUCode & 0x00FF); + OUString aTemp(aTest, 2, eSrcCJKCharSet); + OSL_ENSURE(aTemp.getLength() == 1, "so much for that theory"); + *pWork++ = aTemp[0]; + } + else + { + char cTest = static_cast< char >(nUCode & 0x00FF); + pWork += Custom8BitToUnicode(hConverter, &cTest, 1, pWork, 1); + } + } + } + else + p8Bits[nL2] = nBCode; + } + + if (nL2) + { + const sal_Int32 nEndUsed = !m_bIsUnicode + ? Custom8BitToUnicode(hConverter, p8Bits.get(), nL2, pBuffer, nStrLen) + : pWork - pBuffer; + + if (m_bRegardHindiDigits && m_bBidi && LangUsesHindiNumbers(nCTLLang)) + { + for (sal_Int32 nI = 0; nI < nEndUsed; ++nI, ++pBuffer) + *pBuffer = TranslateToHindiNumbers(*pBuffer); + } + + pStr->buffer[nEndUsed] = 0; + pStr->length = nEndUsed; + + emulateMSWordAddTextToParagraph(makeOUString(pStr, nStrLen)); + pStr = nullptr; + rPos += nL2; + if (!m_aApos.back()) // a para end in apo doesn't count + m_bWasParaEnd = false; // No CR + } + + if (hConverter) + rtl_destroyTextToUnicodeConverter(hConverter); + if (pStr) + rtl_uString_release(pStr); + return nL2 >= nStrLen; +} + +#define MSASCII SAL_MAX_INT16 + +namespace +{ + // We want to force weak chars inside 0x0020 to 0x007F to LATIN + sal_Int16 lcl_getScriptType( + const uno::Reference<i18n::XBreakIterator>& rBI, + const OUString &rString, sal_Int32 nPos) + { + sal_Int16 nScript = rBI->getScriptType(rString, nPos); + if (nScript == i18n::ScriptType::WEAK && rString[nPos] >= 0x0020 && rString[nPos] <= 0x007F) + nScript = MSASCII; + return nScript; + } + + // We want to know about WEAK segments, so endOfScript isn't + // useful, and see lcl_getScriptType anyway + sal_Int32 lcl_endOfScript( + const uno::Reference<i18n::XBreakIterator>& rBI, + const OUString &rString, sal_Int32 nPos, sal_Int16 nScript) + { + while (nPos < rString.getLength()) + { + sal_Int16 nNewScript = lcl_getScriptType(rBI, rString, nPos); + if (nScript != nNewScript) + break; + ++nPos; + } + return nPos; + } + + sal_Int32 lcl_getWriterScriptType( + const uno::Reference<i18n::XBreakIterator>& rBI, + const OUString &rString, sal_Int32 nPos) + { + sal_Int16 nScript = i18n::ScriptType::WEAK; + + if (rString.isEmpty()) + return nScript; + + while (nPos >= 0) + { + nScript = rBI->getScriptType(rString, nPos); + if (nScript != i18n::ScriptType::WEAK) + break; + --nPos; + } + + return nScript; + } + + bool samePitchIgnoreUnknown(FontPitch eA, FontPitch eB) + { + return (eA == eB || eA == PITCH_DONTKNOW || eB == PITCH_DONTKNOW); + } + + bool sameFontIgnoringIrrelevantFields(const SvxFontItem &rA, const SvxFontItem &rB) + { + // Ignoring CharSet, and ignoring unknown pitch + return rA.GetFamilyName() == rB.GetFamilyName() && + rA.GetStyleName() == rB.GetStyleName() && + rA.GetFamily() == rB.GetFamily() && + samePitchIgnoreUnknown(rA.GetPitch(), rB.GetPitch()); + } +} + +// In writer we categorize text into CJK, CTL and "Western" for everything else. +// Microsoft Word basically categorizes text into East Asian, Complex, ASCII, +// NonEastAsian/HighAnsi, with some shared characters and some properties to +// hint as to which way to bias those shared characters. + +// That's four categories, we however have three categories. Given that problem +// here we would ideally find out "what would word do" to see what font/language +// word would assign to characters based on the unicode range they fall into and +// hack the word one onto the range we use. However it's unclear what word's +// categorization is. So we don't do that here yet. + +// Additional to the categorization, when word encounters weak text for ambiguous +// chars it uses idcthint to indicate which way to bias. We don't have an idcthint +// feature in writer. + +// So what we currently do here then is to split our text into non-weak/weak +// sections and uses word's idcthint to determine what font it would use and +// force that on for the segment. Following what we *do* know about word's +// categorization, we know that the range 0x0020 and 0x007F is sprmCRgFtc0 in +// word, something we map to LATIN, so we consider all weak chars in that range +// to auto-bias to LATIN. + +// See https://bugs.libreoffice.org/show_bug.cgi?id=34319 for an example +void SwWW8ImplReader::emulateMSWordAddTextToParagraph(const OUString& rAddString) +{ + if (rAddString.isEmpty()) + return; + + uno::Reference<i18n::XBreakIterator> xBI(g_pBreakIt->GetBreakIter()); + assert(xBI.is()); + + sal_Int16 nScript = lcl_getScriptType(xBI, rAddString, 0); + sal_Int32 nLen = rAddString.getLength(); + + OUString sParagraphText; + const SwContentNode *pCntNd = m_pPaM->GetContentNode(); + const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr; + if (pNd) + sParagraphText = pNd->GetText(); + sal_Int32 nParaOffset = sParagraphText.getLength(); + sParagraphText = sParagraphText + rAddString; + + sal_Int32 nPos = 0; + while (nPos < nLen) + { + sal_Int32 nEnd = lcl_endOfScript(xBI, rAddString, nPos, nScript); + if (nEnd < 0) + break; + + OUString sChunk(rAddString.copy(nPos, nEnd-nPos)); + const sal_uInt16 aIds[] = {RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT}; + const SvxFontItem *pOverriddenItems[] = {nullptr, nullptr, nullptr}; + bool aForced[] = {false, false, false}; + + int nLclIdctHint = 0xFF; + if (nScript == i18n::ScriptType::WEAK) + { + const SfxInt16Item *pIdctHint = static_cast<const SfxInt16Item*>(GetFormatAttr(RES_CHRATR_IDCTHINT)); + nLclIdctHint = pIdctHint->GetValue(); + } + else if (nScript == MSASCII) // Force weak chars in ascii range to use LATIN font + nLclIdctHint = 0; + + sal_uInt16 nForceFromFontId = 0; + if (nLclIdctHint != 0xFF) + { + switch (nLclIdctHint) + { + case 0: + nForceFromFontId = RES_CHRATR_FONT; + break; + case 1: + nForceFromFontId = RES_CHRATR_CJK_FONT; + break; + case 2: + nForceFromFontId = RES_CHRATR_CTL_FONT; + break; + default: + break; + } + } + + if (nForceFromFontId != 0) + { + // Now we know that word would use the nForceFromFontId font for this range + // Try and determine what script writer would assign this range to + + sal_Int32 nWriterScript = lcl_getWriterScriptType(xBI, sParagraphText, + nPos + nParaOffset); + + bool bWriterWillUseSameFontAsWordAutomatically = false; + + if (nWriterScript != i18n::ScriptType::WEAK) + { + if ( + (nWriterScript == i18n::ScriptType::ASIAN && nForceFromFontId == RES_CHRATR_CJK_FONT) || + (nWriterScript == i18n::ScriptType::COMPLEX && nForceFromFontId == RES_CHRATR_CTL_FONT) || + (nWriterScript == i18n::ScriptType::LATIN && nForceFromFontId == RES_CHRATR_FONT) + ) + { + bWriterWillUseSameFontAsWordAutomatically = true; + } + else + { + const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId)); + sal_uInt16 nDestId = aIds[nWriterScript-1]; + const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(nDestId)); + bWriterWillUseSameFontAsWordAutomatically = sameFontIgnoringIrrelevantFields(*pSourceFont, *pDestFont); + } + } + + // Writer won't use the same font as word, so force the issue + if (!bWriterWillUseSameFontAsWordAutomatically) + { + const SvxFontItem *pSourceFont = static_cast<const SvxFontItem*>(GetFormatAttr(nForceFromFontId)); + + for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i) + { + const SvxFontItem *pDestFont = static_cast<const SvxFontItem*>(GetFormatAttr(aIds[i])); + aForced[i] = aIds[i] != nForceFromFontId && *pSourceFont != *pDestFont; + if (aForced[i]) + { + pOverriddenItems[i] = + static_cast<const SvxFontItem*>(m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), aIds[i])); + + SvxFontItem aForceFont(*pSourceFont); + aForceFont.SetWhich(aIds[i]); + m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), aForceFont); + } + } + } + } + + simpleAddTextToParagraph(sChunk); + + for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i) + { + if (aForced[i]) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), aIds[i]); + if (pOverriddenItems[i]) + m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *(pOverriddenItems[i])); + } + } + + nPos = nEnd; + if (nPos < nLen) + nScript = lcl_getScriptType(xBI, rAddString, nPos); + } +} + +namespace sw { + +auto FilterControlChars(OUString const& rString) -> OUString +{ + OUStringBuffer buf(rString.getLength()); + for (sal_Int32 i = 0; i < rString.getLength(); ++i) + { + sal_Unicode const ch(rString[i]); + if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t') + { + buf.append(ch); + } + else + { + SAL_INFO("sw.ww8", "filtering control character"); + } + } + return buf.makeStringAndClear(); +} + +} // namespace sw + +void SwWW8ImplReader::simpleAddTextToParagraph(const OUString& rAddString) +{ + OUString const addString(sw::FilterControlChars(rAddString)); + + if (addString.isEmpty()) + return; + +#if OSL_DEBUG_LEVEL > 1 + SAL_INFO("sw.ww8", "<addTextToParagraph>" << addString << "</addTextToParagraph>"); +#endif + + const SwContentNode *pCntNd = m_pPaM->GetContentNode(); + const SwTextNode* pNd = pCntNd ? pCntNd->GetTextNode() : nullptr; + + OSL_ENSURE(pNd, "What the hell, where's my text node"); + + if (!pNd) + return; + + const sal_Int32 nCharsLeft = SAL_MAX_INT32 - pNd->GetText().getLength(); + if (nCharsLeft > 0) + { + if (addString.getLength() <= nCharsLeft) + { + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString); + } + else + { + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft)); + AppendTextNode(*m_pPaM->GetPoint()); + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft)); + } + } + else + { + AppendTextNode(*m_pPaM->GetPoint()); + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString); + } + + m_bReadTable = false; +} + +/** + * Return value: true for para end + */ +bool SwWW8ImplReader::ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, long nTextEnd, + long nCpOfs) +{ + long nEnd = ( nNextAttr < nTextEnd ) ? nNextAttr : nTextEnd; + + if (m_bSymbol || m_bIgnoreText) + { + WW8_CP nRequested = nEnd - rPos; + if (m_bSymbol) // Insert special chars + { + sal_uInt64 nMaxPossible = m_pStrm->remainingSize(); + if (o3tl::make_unsigned(nRequested) > nMaxPossible) + { + SAL_WARN("sw.ww8", "document claims to have more characters, " << nRequested << " than remaining, " << nMaxPossible); + nRequested = nMaxPossible; + } + + if (!linguistic::IsControlChar(m_cSymbol) + || m_cSymbol == '\r' || m_cSymbol == '\n' || m_cSymbol == '\t') + { + for (WW8_CP nCh = 0; nCh < nRequested; ++nCh) + { + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(m_cSymbol)); + } + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CJK_FONT); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_CTL_FONT); + } + } + m_pStrm->SeekRel(nRequested); + rPos = nEnd; // Ignore until attribute end + return false; + } + + while (true) + { + if (ReadPlainChars(rPos, nEnd, nCpOfs)) + return false; // Done + + bool bStartLine = ReadChar(rPos, nCpOfs); + rPos++; + if (m_bPgSecBreak || bStartLine || rPos == nEnd) // CR or Done + { + return bStartLine; + } + } +} + +bool SwWW8ImplReader::HandlePageBreakChar() +{ + bool bParaEndAdded = false; + // #i1909# section/page breaks should not occur in tables, word + // itself ignores them in this case. + if (!m_nInTable) + { + bool IsTemp=true; + SwTextNode* pTemp = m_pPaM->GetNode().GetTextNode(); + if (pTemp && pTemp->GetText().isEmpty() + && (m_bFirstPara || m_bFirstParaOfPage)) + { + IsTemp = false; + AppendTextNode(*m_pPaM->GetPoint()); + pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE)); + } + + m_bPgSecBreak = true; + m_xCtrlStck->KillUnlockedAttrs(*m_pPaM->GetPoint()); + /* + If it's a 0x0c without a paragraph end before it, act like a + paragraph end, but nevertheless, numbering (and perhaps other + similar constructs) do not exist on the para. + */ + if (!m_bWasParaEnd && IsTemp) + { + bParaEndAdded = true; + if (0 >= m_pPaM->GetPoint()->nContent.GetIndex()) + { + if (SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode()) + { + pTextNode->SetAttr( + *GetDfltAttr(RES_PARATR_NUMRULE)); + } + } + } + } + return bParaEndAdded; +} + +bool SwWW8ImplReader::ReadChar(long nPosCp, long nCpOfs) +{ + bool bNewParaEnd = false; + // Reset Unicode flag and correct FilePos if needed. + // Note: Seek is not expensive, as we're checking inline whether or not + // the correct FilePos has already been reached. + std::size_t nRequestedPos = m_xSBase->WW8Cp2Fc(nCpOfs+nPosCp, &m_bIsUnicode); + if (!checkSeek(*m_pStrm, nRequestedPos)) + return false; + + sal_uInt8 nBCode(0); + sal_uInt16 nWCharVal(0); + if( m_bIsUnicode ) + m_pStrm->ReadUInt16( nWCharVal ); // unicode --> read 2 bytes + else + { + m_pStrm -> ReadUChar( nBCode ); // old code --> read 1 byte + nWCharVal = nBCode; + } + + sal_Unicode cInsert = '\x0'; + bool bParaMark = false; + + if ( 0xc != nWCharVal ) + m_bFirstParaOfPage = false; + + switch (nWCharVal) + { + case 0: + { + // Page number + SwPageNumberField aField( + static_cast<SwPageNumberFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( + SwFieldIds::PageNumber )), PG_RANDOM, SVX_NUM_ARABIC); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + } + break; + case 0xe: + // if there is only one column word treats a column break like a pagebreak. + if (m_aSectionManager.CurrentSectionColCount() < 2) + bParaMark = HandlePageBreakChar(); + else if (!m_nInTable) + { + // Always insert a txtnode for a column break, e.g. ## + SwContentNode *pCntNd=m_pPaM->GetContentNode(); + if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed + AppendTextNode(*m_pPaM->GetPoint()); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK)); + } + break; + case 0x7: + { + bNewParaEnd = true; + WW8PLCFxDesc* pPap = m_xPlcxMan->GetPap(); + //The last paragraph of each cell is terminated by a special + //paragraph mark called a cell mark. Following the cell mark + //that ends the last cell of a table row, the table row is + //terminated by a special paragraph mark called a row mark + // + //So the 0x7 should be right at the end of the previous + //range to be a real cell-end. + if (pPap->nOrigStartPos == nPosCp+1 || + pPap->nOrigStartPos == WW8_CP_MAX) + { + TabCellEnd(); // Table cell/row end + } + else + bParaMark = true; + } + break; + case 0xf: + if( !m_bSpec ) // "Satellite" + cInsert = u'\x00a4'; + break; + case 0x14: + if( !m_bSpec ) // "Para End" char + cInsert = u'\x00b5'; + //TODO: should this be U+00B6 PILCROW SIGN rather than + // U+00B5 MICRO SIGN? + break; + case 0x15: + if( !m_bSpec ) // Juristenparagraph + { + cp_set::iterator aItr = m_aTOXEndCps.find(static_cast<WW8_CP>(nPosCp)); + if (aItr == m_aTOXEndCps.end()) + cInsert = u'\x00a7'; + else + m_aTOXEndCps.erase(aItr); + } + break; + case 0x9: + cInsert = '\x9'; // Tab + break; + case 0xb: + cInsert = '\xa'; // Hard NewLine + break; + case 0xc: + bParaMark = HandlePageBreakChar(); + break; + case 0x1e: // Non-breaking hyphen + m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDHYPHEN) ); + break; + case 0x1f: // Non-required hyphens + m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_SOFTHYPHEN) ); + break; + case 0xa0: // Non-breaking spaces + m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, OUString(CHAR_HARDBLANK) ); + break; + case 0x1: + /* + Current thinking is that if bObj is set then we have a + straightforward "traditional" ole object, otherwise we have a + graphic preview of an associated ole2 object (or a simple + graphic of course) + + normally in the canvas field, the code is 0x8 0x1. + in a special case, the code is 0x1 0x1, which yields a simple picture + */ + { + bool bReadObj = IsInlineEscherHack(); + if( bReadObj ) + { + long nCurPos = m_pStrm->Tell(); + sal_uInt16 nWordCode(0); + + if( m_bIsUnicode ) + m_pStrm->ReadUInt16( nWordCode ); + else + { + sal_uInt8 nByteCode(0); + m_pStrm->ReadUChar( nByteCode ); + nWordCode = nByteCode; + } + if( nWordCode == 0x1 ) + bReadObj = false; + m_pStrm->Seek( nCurPos ); + } + if( !bReadObj ) + { + SwFrameFormat *pResult = nullptr; + if (m_bObj) + pResult = ImportOle(); + else if (m_bSpec) + pResult = ImportGraf(); + + // If we have a bad 0x1 insert a space instead. + if (!pResult) + { + cInsert = ' '; + OSL_ENSURE(!m_bObj && !m_bEmbeddObj && !m_nObjLocFc, + "WW8: Please report this document, it may have a " + "missing graphic"); + } + else + { + // reset the flags. + m_bObj = m_bEmbeddObj = false; + m_nObjLocFc = 0; + } + } + } + break; + case 0x8: + if( !m_bObj ) + Read_GrafLayer( nPosCp ); + break; + case 0xd: + bNewParaEnd = bParaMark = true; + if (m_nInTable > 1) + { + /* + #i9666#/#i23161# + Yes complex, if there is an entry in the undocumented PLCF + which I believe to be a record of cell and row boundaries + see if the magic bit which I believe to mean cell end is + set. I also think btw that the third byte of the 4 byte + value is the level of the cell + */ + WW8PLCFspecial* pTest = m_xPlcxMan->GetMagicTables(); + if (pTest && pTest->SeekPosExact(nPosCp+1+nCpOfs) && + pTest->Where() == nPosCp+1+nCpOfs) + { + WW8_FC nPos; + void *pData; + sal_uInt32 nData = pTest->Get(nPos, pData) ? SVBT32ToUInt32(*static_cast<SVBT32*>(pData)) + : 0; + if (nData & 0x2) // Might be how it works + { + TabCellEnd(); + bParaMark = false; + } + } + // tdf#106799: We expect TTP marks to be also cell marks, + // but sometimes sprmPFInnerTtp comes without sprmPFInnerTableCell + else if (m_bWasTabCellEnd || m_bWasTabRowEnd) + { + TabCellEnd(); + bParaMark = false; + } + } + + m_bWasTabCellEnd = false; + + break; // line end + case 0x5: // Annotation reference + case 0x13: + break; + case 0x2: // TODO: Auto-Footnote-Number, should be replaced by SwWW8ImplReader::End_Footnote later + if (!m_aFootnoteStack.empty()) + cInsert = '?'; + break; + default: + SAL_INFO( "sw.ww8.level2", "<unknownValue val=\"" << nWCharVal << "\">" ); + break; + } + + if( '\x0' != cInsert ) + { + OUString sInsert(cInsert); + emulateMSWordAddTextToParagraph(sInsert); + } + if (!m_aApos.back()) // a para end in apo doesn't count + m_bWasParaEnd = bNewParaEnd; + return bParaMark; +} + +void SwWW8ImplReader::ProcessCurrentCollChange(WW8PLCFManResult& rRes, + bool* pStartAttr, bool bCallProcessSpecial) +{ + sal_uInt16 nOldColl = m_nCurrentColl; + m_nCurrentColl = m_xPlcxMan->GetColl(); + + // Invalid Style-Id + if (m_nCurrentColl >= m_vColl.size() || !m_vColl[m_nCurrentColl].m_pFormat || !m_vColl[m_nCurrentColl].m_bColl) + { + m_nCurrentColl = 0; + m_bParaAutoBefore = false; + m_bParaAutoAfter = false; + } + else + { + m_bParaAutoBefore = m_vColl[m_nCurrentColl].m_bParaAutoBefore; + m_bParaAutoAfter = m_vColl[m_nCurrentColl].m_bParaAutoAfter; + } + + if (nOldColl >= m_vColl.size()) + nOldColl = 0; // guess! TODO make sure this is what we want + + bool bTabRowEnd = false; + if( pStartAttr && bCallProcessSpecial && !m_bInHyperlink ) + { + bool bReSync; + // Frame/Table/Autonumbering List Level + bTabRowEnd = ProcessSpecial(bReSync, rRes.nCurrentCp + m_xPlcxMan->GetCpOfs()); + if( bReSync ) + *pStartAttr = m_xPlcxMan->Get( &rRes ); // Get Attribut-Pos again + } + + if (!bTabRowEnd && StyleExists(m_nCurrentColl)) + { + SetTextFormatCollAndListLevel( *m_pPaM, m_vColl[ m_nCurrentColl ]); + ChkToggleAttr(m_vColl[ nOldColl ].m_n81Flags, m_vColl[ m_nCurrentColl ].m_n81Flags); + ChkToggleBiDiAttr(m_vColl[nOldColl].m_n81BiDiFlags, + m_vColl[m_nCurrentColl].m_n81BiDiFlags); + } +} + +long SwWW8ImplReader::ReadTextAttr(WW8_CP& rTextPos, long nTextEnd, bool& rbStartLine, int nDepthGuard) +{ + long nSkipChars = 0; + WW8PLCFManResult aRes; + + OSL_ENSURE(m_pPaM->GetNode().GetTextNode(), "Missing txtnode"); + bool bStartAttr = m_xPlcxMan->Get(&aRes); // Get Attribute position again + aRes.nCurrentCp = rTextPos; // Current Cp position + + bool bNewSection = (aRes.nFlags & MAN_MASK_NEW_SEP) && !m_bIgnoreText; + if ( bNewSection ) // New Section + { + OSL_ENSURE(m_pPaM->GetNode().GetTextNode(), "Missing txtnode"); + // Create PageDesc and fill it + m_aSectionManager.CreateSep(rTextPos); + // -> 0xc was a Sectionbreak, but not a Pagebreak; + // Create PageDesc and fill it + m_bPgSecBreak = false; + OSL_ENSURE(m_pPaM->GetNode().GetTextNode(), "Missing txtnode"); + } + + // New paragraph over Plcx.Fkp.papx + if ( (aRes.nFlags & MAN_MASK_NEW_PAP)|| rbStartLine ) + { + ProcessCurrentCollChange( aRes, &bStartAttr, + MAN_MASK_NEW_PAP == (aRes.nFlags & MAN_MASK_NEW_PAP) && + !m_bIgnoreText ); + rbStartLine = false; + } + + // position of last CP that's to be ignored + long nSkipPos = -1; + + if( 0 < aRes.nSprmId ) // Ignore empty Attrs + { + if( ( eFTN > aRes.nSprmId ) || ( 0x0800 <= aRes.nSprmId ) ) + { + if( bStartAttr ) // WW attributes + { + if( aRes.nMemLen >= 0 ) + ImportSprm(aRes.pMemPos, aRes.nMemLen, aRes.nSprmId); + } + else + EndSprm( aRes.nSprmId ); // Switch off Attr + } + else if( aRes.nSprmId < 0x800 ) // Own helper attributes + { + if (bStartAttr) + { + nSkipChars = ImportExtSprm(&aRes); + if ( + (aRes.nSprmId == eFTN) || (aRes.nSprmId == eEDN) || + (aRes.nSprmId == eFLD) || (aRes.nSprmId == eAND) + ) + { + WW8_CP nMaxLegalSkip = nTextEnd - rTextPos; + // Skip Field/Footnote-/End-Note here + rTextPos += std::min<WW8_CP>(nSkipChars, nMaxLegalSkip); + nSkipPos = rTextPos-1; + } + } + else + EndExtSprm( aRes.nSprmId ); + } + } + + sal_Int32 nRequestedPos = m_xSBase->WW8Cp2Fc(m_xPlcxMan->GetCpOfs() + rTextPos, &m_bIsUnicode); + bool bValidPos = checkSeek(*m_pStrm, nRequestedPos); + SAL_WARN_IF(!bValidPos, "sw.ww8", "Document claimed to have text at an invalid position, skip attributes for region"); + + // Find next Attr position (and Skip attributes of field contents if needed) + if (nSkipChars && !m_bIgnoreText) + m_xCtrlStck->MarkAllAttrsOld(); + bool bOldIgnoreText = m_bIgnoreText; + m_bIgnoreText = true; + sal_uInt16 nOldColl = m_nCurrentColl; + bool bDoPlcxManPlusPLus = true; + long nNext; + do + { + if( bDoPlcxManPlusPLus ) + m_xPlcxMan->advance(); + nNext = bValidPos ? m_xPlcxMan->Where() : nTextEnd; + + if (m_pPostProcessAttrsInfo && + m_pPostProcessAttrsInfo->mnCpStart == nNext) + { + m_pPostProcessAttrsInfo->mbCopy = true; + } + + if( (0 <= nNext) && (nSkipPos >= nNext) ) + { + if (nDepthGuard >= 1024) + { + SAL_WARN("sw.ww8", "ReadTextAttr hit recursion limit"); + nNext = nTextEnd; + } + else + nNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine, nDepthGuard + 1); + bDoPlcxManPlusPLus = false; + m_bIgnoreText = true; + } + + if (m_pPostProcessAttrsInfo && + nNext > m_pPostProcessAttrsInfo->mnCpEnd) + { + m_pPostProcessAttrsInfo->mbCopy = false; + } + } + while( nSkipPos >= nNext ); + m_bIgnoreText = bOldIgnoreText; + if( nSkipChars ) + { + m_xCtrlStck->KillUnlockedAttrs( *m_pPaM->GetPoint() ); + if( nOldColl != m_xPlcxMan->GetColl() ) + ProcessCurrentCollChange(aRes, nullptr, false); + } + + return nNext; +} + +//Revised 2012.8.16 for the complex attribute presentation of 0x0D in MS +bool SwWW8ImplReader::IsParaEndInCPs(sal_Int32 nStart, sal_Int32 nEnd,bool bSdOD) const +{ + //Revised for performance consideration + if (nStart == -1 || nEnd == -1 || nEnd < nStart ) + return false; + + return std::any_of(m_aEndParaPos.rbegin(), m_aEndParaPos.rend(), + [=](const WW8_CP& rPos) { + //Revised 2012.8.16,to the 0x0D,the attribute will have two situations + //*********within***********exact****** + //*********but also sample with only left and the position of 0x0d is the edge of the right side*********** + return (bSdOD && ((nStart < rPos && nEnd > rPos) || (nStart == nEnd && rPos == nStart))) || + (!bSdOD && (nStart < rPos && nEnd >= rPos)); + } + ); +} + +//Clear the para end position recorded in reader intermittently for the least impact on loading performance +void SwWW8ImplReader::ClearParaEndPosition() +{ + if ( !m_aEndParaPos.empty() ) + m_aEndParaPos.clear(); +} + +void SwWW8ImplReader::ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, long nTextEnd, bool& rbStartLine) +{ + // Do we have attributes? + if( rTextPos >= rNext ) + { + do + { + m_aCurrAttrCP = rTextPos; + rNext = ReadTextAttr(rTextPos, nTextEnd, rbStartLine); + if (rTextPos == rNext && rTextPos >= nTextEnd) + break; + } + while( rTextPos >= rNext ); + + } + else if ( rbStartLine ) + { + /* No attributes, but still a new line. + * If a line ends with a line break and paragraph attributes or paragraph templates + * do NOT change the line end was not added to the Plcx.Fkp.papx i.e. (nFlags & MAN_MASK_NEW_PAP) + * is false. + * Due to this we need to set the template here as a kind of special treatment. + */ + if (!m_bCpxStyle && m_nCurrentColl < m_vColl.size()) + SetTextFormatCollAndListLevel(*m_pPaM, m_vColl[m_nCurrentColl]); + rbStartLine = false; + } +} + +/** + * CloseAttrEnds to only read the attribute ends at the end of a text or a + * text area (Header, Footnote, ...). + * We ignore attribute starts and fields. + */ +void SwWW8ImplReader::CloseAttrEnds() +{ + // If there are any unclosed sprms then copy them to + // another stack and close the ones that must be closed + std::stack<sal_uInt16> aStack; + m_xPlcxMan->TransferOpenSprms(aStack); + + while (!aStack.empty()) + { + sal_uInt16 nSprmId = aStack.top(); + if ((0 < nSprmId) && (( eFTN > nSprmId) || (0x0800 <= nSprmId))) + EndSprm(nSprmId); + aStack.pop(); + } + + EndSpecial(); +} + +bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType) +{ + bool bJoined=false; + + bool bStartLine = true; + short nCrCount = 0; + short nDistance = 0; + + m_bWasParaEnd = false; + m_nCurrentColl = 0; + m_xCurrentItemSet.reset(); + m_nCharFormat = -1; + m_bSpec = false; + m_bPgSecBreak = false; + + m_xPlcxMan = std::make_shared<WW8PLCFMan>(m_xSBase.get(), nType, nStartCp); + long nCpOfs = m_xPlcxMan->GetCpOfs(); // Offset for Header/Footer, Footnote + + WW8_CP nNext = m_xPlcxMan->Where(); + m_pPreviousNode = nullptr; + sal_uInt8 nDropLines = 0; + SwCharFormat* pNewSwCharFormat = nullptr; + const SwCharFormat* pFormat = nullptr; + + bool bValidPos = checkSeek(*m_pStrm, m_xSBase->WW8Cp2Fc(nStartCp + nCpOfs, &m_bIsUnicode)); + if (!bValidPos) + return false; + + WW8_CP l = nStartCp; + const WW8_CP nMaxPossible = WW8_CP_MAX-nStartCp; + if (nTextLen > nMaxPossible) + { + SAL_WARN_IF(nTextLen > nMaxPossible, "sw.ww8", "TextLen too long"); + nTextLen = nMaxPossible; + } + WW8_CP nTextEnd = nStartCp+nTextLen; + while (l < nTextEnd) + { + ReadAttrs( l, nNext, nTextEnd, bStartLine );// Takes SectionBreaks into account, too + OSL_ENSURE(m_pPaM->GetNode().GetTextNode(), "Missing txtnode"); + + if (m_pPostProcessAttrsInfo != nullptr) + PostProcessAttrs(); + + if (l >= nTextEnd) + break; + + bStartLine = ReadChars(l, nNext, nTextEnd, nCpOfs); + + // If the previous paragraph was a dropcap then do not + // create a new txtnode and join the two paragraphs together + if (bStartLine && !m_pPreviousNode) // Line end + { + bool bSplit = true; + if (m_bCareFirstParaEndInToc) + { + m_bCareFirstParaEndInToc = false; + if (m_pPaM->End() && m_pPaM->End()->nNode.GetNode().GetTextNode() && m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() == 0) + bSplit = false; + } + if (m_bCareLastParaEndInToc) + { + m_bCareLastParaEndInToc = false; + if (m_pPaM->End() && m_pPaM->End()->nNode.GetNode().GetTextNode() && m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() == 0) + bSplit = false; + } + if (bSplit) + { + // We will record the CP of a paragraph end ('0x0D'), if current loading contents is from main stream; + if (m_bOnLoadingMain) + m_aEndParaPos.push_back(l-1); + AppendTextNode(*m_pPaM->GetPoint()); + } + } + + if (m_pPreviousNode && bStartLine) + { + SwTextNode* pEndNd = m_pPaM->GetNode().GetTextNode(); + const sal_Int32 nDropCapLen = m_pPreviousNode->GetText().getLength(); + + // Need to reset the font size and text position for the dropcap + { + SwPaM aTmp(*pEndNd, 0, *pEndNd, nDropCapLen+1); + m_xCtrlStck->Delete(aTmp); + } + + // Get the default document dropcap which we can use as our template + const SwFormatDrop* defaultDrop = + static_cast<const SwFormatDrop*>( GetFormatAttr(RES_PARATR_DROP)); + SwFormatDrop aDrop(*defaultDrop); + + aDrop.GetLines() = nDropLines; + aDrop.GetDistance() = nDistance; + aDrop.GetChars() = writer_cast<sal_uInt8>(nDropCapLen); + // Word has no concept of a "whole word dropcap" + aDrop.GetWholeWord() = false; + + if (pFormat) + aDrop.SetCharFormat(const_cast<SwCharFormat*>(pFormat)); + else if(pNewSwCharFormat) + aDrop.SetCharFormat(pNewSwCharFormat); + + SwPosition aStart(*pEndNd); + m_xCtrlStck->NewAttr(aStart, aDrop); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_DROP); + m_pPreviousNode = nullptr; + } + else if (m_bDropCap) + { + // If we have found a dropcap store the textnode + m_pPreviousNode = m_pPaM->GetNode().GetTextNode(); + + SprmResult aDCS; + if (m_bVer67) + aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(46); + else + aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(0x442C); + + if (aDCS.pSprm && aDCS.nRemainingData >= 1) + nDropLines = (*aDCS.pSprm) >> 3; + else // There is no Drop Cap Specifier hence no dropcap + m_pPreviousNode = nullptr; + + SprmResult aDistance = m_xPlcxMan->GetPapPLCF()->HasSprm(0x842F); + if (aDistance.pSprm && aDistance.nRemainingData >= 2) + nDistance = SVBT16ToUInt16(aDistance.pSprm); + else + nDistance = 0; + + const SwFormatCharFormat *pSwFormatCharFormat = nullptr; + + if (m_xCurrentItemSet) + pSwFormatCharFormat = &(ItemGet<SwFormatCharFormat>(*m_xCurrentItemSet, RES_TXTATR_CHARFMT)); + + if (pSwFormatCharFormat) + pFormat = pSwFormatCharFormat->GetCharFormat(); + + if (m_xCurrentItemSet && !pFormat) + { + OUString sPrefix = "WW8Dropcap" + OUString::number(m_nDropCap++); + pNewSwCharFormat = m_rDoc.MakeCharFormat(sPrefix, m_rDoc.GetDfltCharFormat()); + m_xCurrentItemSet->ClearItem(RES_CHRATR_ESCAPEMENT); + pNewSwCharFormat->SetFormatAttr(*m_xCurrentItemSet); + } + + m_xCurrentItemSet.reset(); + m_bDropCap=false; + } + + if (bStartLine || m_bWasTabRowEnd) + { + // Call all 64 CRs; not for Header and the like + if ((nCrCount++ & 0x40) == 0 && nType == MAN_MAINTEXT && l <= nTextLen) + { + if (nTextLen < WW8_CP_MAX/100) + m_nProgress = static_cast<sal_uInt16>(l * 100 / nTextLen); + else + m_nProgress = static_cast<sal_uInt16>(l / nTextLen * 100); + m_xProgress->Update(m_nProgress); // Update + } + } + + // If we have encountered a 0x0c which indicates either section of + // pagebreak then look it up to see if it is a section break, and + // if it is not then insert a page break. If it is a section break + // it will be handled as such in the ReadAttrs of the next loop + if (m_bPgSecBreak) + { + // We need only to see if a section is ending at this cp, + // the plcf will already be sitting on the correct location + // if it is there. + WW8PLCFxDesc aTemp; + aTemp.nStartPos = aTemp.nEndPos = WW8_CP_MAX; + if (m_xPlcxMan->GetSepPLCF()) + m_xPlcxMan->GetSepPLCF()->GetSprms(&aTemp); + if ((aTemp.nStartPos != l) && (aTemp.nEndPos != l)) + { + // #i39251# - insert text node for page break, if no one inserted. + // #i43118# - refine condition: the anchor + // control stack has to have entries, otherwise it's not needed + // to insert a text node. + if (!bStartLine && !m_xAnchorStck->empty()) + { + AppendTextNode(*m_pPaM->GetPoint()); + } + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, + SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK)); + m_bFirstParaOfPage = true; + m_bPgSecBreak = false; + } + } + } + + m_pPreviousNode = nullptr; + + if (m_pPaM->GetPoint()->nContent.GetIndex()) + AppendTextNode(*m_pPaM->GetPoint()); + + if (!m_bInHyperlink) + bJoined = JoinNode(*m_pPaM); + + CloseAttrEnds(); + + m_xPlcxMan.reset(); + return bJoined; +} + +SwWW8ImplReader::SwWW8ImplReader(sal_uInt8 nVersionPara, SotStorage* pStorage, + SvStream* pSt, SwDoc& rD, const OUString& rBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos) + : m_pDocShell(rD.GetDocShell()) + , m_pStg(pStorage) + , m_pStrm(pSt) + , m_pTableStream(nullptr) + , m_pDataStream(nullptr) + , m_rDoc(rD) + , m_pPaM(nullptr) + , m_aSectionManager(*this) + , m_aExtraneousParas(rD) + , m_aInsertedTables(rD) + , m_aSectionNameGenerator(rD, "WW") + , m_aGrfNameGenerator(bNewDoc, OUString('G')) + , m_aParaStyleMapper(rD) + , m_aCharStyleMapper(rD) + , m_pFlyFormatOfJustInsertedGraphic(nullptr) + , m_pPreviousNumPaM(nullptr) + , m_pPrevNumRule(nullptr) + , m_aTextNodesHavingFirstLineOfstSet() + , m_aTextNodesHavingLeftIndentSet() + , m_pCurrentColl(nullptr) + , m_pDfltTextFormatColl(nullptr) + , m_pStandardFormatColl(nullptr) + , m_pDrawModel(nullptr) + , m_pDrawPg(nullptr) + , m_pNumFieldType(nullptr) + , m_sBaseURL(rBaseURL) + , m_nIniFlags(0) + , m_nIniFlags1(0) + , m_nFieldFlags(0) + , m_bRegardHindiDigits( false ) + , m_bDrawCpOValid( false ) + , m_nDrawCpO(0) + , m_nPicLocFc(0) + , m_nObjLocFc(0) + , m_nIniFlyDx(0) + , m_nIniFlyDy(0) + , m_eTextCharSet(RTL_TEXTENCODING_ASCII_US) + , m_eStructCharSet(RTL_TEXTENCODING_ASCII_US) + , m_eHardCharSet(RTL_TEXTENCODING_DONTKNOW) + , m_nProgress(0) + , m_nCurrentColl(0) + , m_nFieldNum(0) + , m_nLFOPosition(USHRT_MAX) + , m_nCharFormat(0) + , m_nDrawXOfs(0) + , m_nDrawYOfs(0) + , m_nDrawXOfs2(0) + , m_nDrawYOfs2(0) + , m_cSymbol(0) + , m_nWantedVersion(nVersionPara) + , m_nSwNumLevel(0xff) + , m_nWwNumType(0xff) + , m_nListLevel(WW8ListManager::nMaxLevel) + , m_bNewDoc(bNewDoc) + , m_bSkipImages(bSkipImages) + , m_bReadNoTable(false) + , m_bPgSecBreak(false) + , m_bSpec(false) + , m_bObj(false) + , m_bTxbxFlySection(false) + , m_bHasBorder(false) + , m_bSymbol(false) + , m_bIgnoreText(false) + , m_nInTable(0) + , m_bWasTabRowEnd(false) + , m_bWasTabCellEnd(false) + , m_bAnl(false) + , m_bHdFtFootnoteEdn(false) + , m_bFootnoteEdn(false) + , m_bIsHeader(false) + , m_bIsFooter(false) + , m_bIsUnicode(false) + , m_bCpxStyle(false) + , m_bStyNormal(false) + , m_bWWBugNormal(false) + , m_bNoAttrImport(false) + , m_bInHyperlink(false) + , m_bWasParaEnd(false) + , m_bVer67(false) + , m_bVer6(false) + , m_bVer7(false) + , m_bVer8(false) + , m_bEmbeddObj(false) + , m_bCurrentAND_fNumberAcross(false) + , m_bNoLnNumYet(true) + , m_bFirstPara(true) + , m_bFirstParaOfPage(false) + , m_bParaAutoBefore(false) + , m_bParaAutoAfter(false) + , m_bDropCap(false) + , m_nDropCap(0) + , m_bBidi(false) + , m_bReadTable(false) + , m_bLoadingTOXCache(false) + , m_nEmbeddedTOXLevel(0) + , m_bLoadingTOXHyperlink(false) + , m_pPreviousNode(nullptr) + , m_bCareFirstParaEndInToc(false) + , m_bCareLastParaEndInToc(false) + , m_aTOXEndCps() + , m_aCurrAttrCP(-1) + , m_bOnLoadingMain(false) + , m_bNotifyMacroEventRead(false) +{ + m_pStrm->SetEndian( SvStreamEndian::LITTLE ); + m_aApos.push_back(false); + + mpCursor = m_rDoc.CreateUnoCursor(rPos); +} + +SwWW8ImplReader::~SwWW8ImplReader() +{ +} + +void SwWW8ImplReader::DeleteStack(std::unique_ptr<SwFltControlStack> pStck) +{ + if( pStck ) + { + pStck->SetAttr( *m_pPaM->GetPoint(), 0, false); + pStck->SetAttr( *m_pPaM->GetPoint(), 0, false); + } + else + { + OSL_ENSURE( false, "WW stack already deleted" ); + } +} + +void wwSectionManager::SetSegmentToPageDesc(const wwSection &rSection, + bool bIgnoreCols) +{ + SwPageDesc &rPage = *rSection.mpPage; + + SetNumberingType(rSection, rPage); + + SwFrameFormat &rFormat = rPage.GetMaster(); + + if(mrReader.m_xWDop->fUseBackGroundInAllmodes) // #i56806# Make sure mrReader is initialized + mrReader.GrafikCtor(); + + if (mrReader.m_xWDop->fUseBackGroundInAllmodes && mrReader.m_xMSDffManager) + { + tools::Rectangle aRect(0, 0, 100, 100); // A dummy, we don't care about the size + SvxMSDffImportData aData(aRect); + SdrObject* pObject = nullptr; + if (mrReader.m_xMSDffManager->GetShape(0x401, pObject, aData) && !aData.empty()) + { + // Only handle shape if it is a background shape + if (aData.begin()->get()->nFlags & ShapeFlag::Background) + { + SfxItemSet aSet(rFormat.GetDoc()->GetAttrPool(), + svl::Items<RES_BACKGROUND, RES_BACKGROUND,XATTR_START, XATTR_END>{}); + mrReader.MatchSdrItemsIntoFlySet(pObject, aSet, mso_lineSimple, + mso_lineSolid, mso_sptRectangle, aRect); + if ( aSet.HasItem(RES_BACKGROUND) ) + rFormat.SetFormatAttr(aSet.Get(RES_BACKGROUND)); + else + rFormat.SetFormatAttr(aSet); + } + } + SdrObject::Free(pObject); + } + wwULSpaceData aULData; + GetPageULData(rSection, aULData); + SetPageULSpaceItems(rFormat, aULData, rSection); + + rPage.SetVerticalAdjustment( rSection.mnVerticalAdjustment ); + + SetPage(rPage, rFormat, rSection, bIgnoreCols); + + if (!(rSection.maSep.pgbApplyTo & 1)) + SwWW8ImplReader::SetPageBorder(rFormat, rSection); + if (!(rSection.maSep.pgbApplyTo & 2)) + SwWW8ImplReader::SetPageBorder(rPage.GetFirstMaster(), rSection); + + mrReader.SetDocumentGrid(rFormat, rSection); +} + +void wwSectionManager::SetUseOn(wwSection &rSection) +{ + bool bMirror = mrReader.m_xWDop->fMirrorMargins || + mrReader.m_xWDop->doptypography.m_f2on1; + + UseOnPage eUseBase = bMirror ? UseOnPage::Mirror : UseOnPage::All; + UseOnPage eUse = eUseBase; + if (!mrReader.m_xWDop->fFacingPages) + eUse |= UseOnPage::HeaderShare | UseOnPage::FooterShare; + if (!rSection.HasTitlePage()) + eUse |= UseOnPage::FirstShare; + + OSL_ENSURE(rSection.mpPage, "Makes no sense to call me with no pages to set"); + if (rSection.mpPage) + rSection.mpPage->WriteUseOn(eUse); +} + +/** + * Set the page descriptor on this node, handle the different cases for a text + * node or a table + */ +static void GiveNodePageDesc(SwNodeIndex const &rIdx, const SwFormatPageDesc &rPgDesc, + SwDoc &rDoc) +{ + /* + If it's a table here, apply the pagebreak to the table + properties, otherwise we add it to the para at this + position + */ + if (rIdx.GetNode().IsTableNode()) + { + SwTable& rTable = + rIdx.GetNode().GetTableNode()->GetTable(); + SwFrameFormat* pApply = rTable.GetFrameFormat(); + OSL_ENSURE(pApply, "impossible"); + if (pApply) + pApply->SetFormatAttr(rPgDesc); + } + else + { + SwPosition aPamStart(rIdx); + aPamStart.nContent.Assign( + rIdx.GetNode().GetContentNode(), 0); + SwPaM aPage(aPamStart); + + rDoc.getIDocumentContentOperations().InsertPoolItem(aPage, rPgDesc); + } +} + +/** + * Map a word section to a writer page descriptor + */ +SwFormatPageDesc wwSectionManager::SetSwFormatPageDesc(mySegIter const &rIter, + mySegIter const &rStart, bool bIgnoreCols) +{ + if (mrReader.m_bNewDoc && rIter == rStart) + { + rIter->mpPage = + mrReader.m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD); + } + else + { + rIter->mpPage = mrReader.m_rDoc.MakePageDesc( + SwViewShell::GetShellRes()->GetPageDescName(mnDesc, ShellResource::NORMAL_PAGE), + nullptr, false); + } + OSL_ENSURE(rIter->mpPage, "no page!"); + if (!rIter->mpPage) + return SwFormatPageDesc(); + + // Set page before hd/ft + const wwSection *pPrevious = nullptr; + if (rIter != rStart) + pPrevious = &(*(rIter-1)); + SetHdFt(*rIter, std::distance(rStart, rIter), pPrevious); + SetUseOn(*rIter); + + // Set hd/ft after set page + SetSegmentToPageDesc(*rIter, bIgnoreCols); + + SwFormatPageDesc aRet(rIter->mpPage); + + rIter->mpPage->SetFollow(rIter->mpPage); + + if (rIter->PageRestartNo()) + aRet.SetNumOffset(rIter->PageStartAt()); + + ++mnDesc; + return aRet; +} + +void wwSectionManager::InsertSegments() +{ + mySegIter aEnd = maSegments.end(); + mySegIter aStart = maSegments.begin(); + for (mySegIter aIter = aStart; aIter != aEnd; ++aIter) + { + // If the section is of type "New column" (0x01), then simply insert a column break. + // But only if there actually are columns on the page, otherwise a column break + // seems to be handled like a page break by MSO. + if ( aIter->maSep.bkc == 1 && aIter->maSep.ccolM1 > 0 ) + { + SwPaM start( aIter->maStart ); + mrReader.m_rDoc.getIDocumentContentOperations().InsertPoolItem( start, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK)); + continue; + } + + mySegIter aNext = aIter+1; + mySegIter aPrev = (aIter == aStart) ? aIter : aIter-1; + + // If two following sections are different in following properties, Word will interpret a continuous + // section break between them as if it was a section break next page. + bool bThisAndPreviousAreCompatible = ((aIter->GetPageWidth() == aPrev->GetPageWidth()) && + (aIter->GetPageHeight() == aPrev->GetPageHeight()) && (aIter->IsLandScape() == aPrev->IsLandScape())); + + bool bInsertSection = (aIter != aStart) && aIter->IsContinuous() && bThisAndPreviousAreCompatible; + bool bInsertPageDesc = !bInsertSection; + // HACK Force new pagedesc if left/right margins change, otherwise e.g. floating tables may be anchored improperly. + if( aIter->maSep.dxaLeft != aPrev->maSep.dxaLeft || aIter->maSep.dxaRight != aPrev->maSep.dxaRight ) + bInsertPageDesc = true; + bool bProtected = SectionIsProtected(*aIter); // do we really need this ?? I guess I have a different logic in editshell which disables this... + + if (bInsertPageDesc) + { + /* + If a cont section follows this section then we won't be + creating a page desc with 2+ cols as we cannot host a one + col section in a 2+ col pagedesc and make it look like + word. But if the current section actually has columns then + we are forced to insert a section here as well as a page + descriptor. + */ + + bool bIgnoreCols = bInsertSection; + bool bThisAndNextAreCompatible = (aNext == aEnd) || + ((aIter->GetPageWidth() == aNext->GetPageWidth()) && + (aIter->GetPageHeight() == aNext->GetPageHeight()) && + (aIter->IsLandScape() == aNext->IsLandScape())); + + if ((aNext != aEnd && aNext->IsContinuous() && bThisAndNextAreCompatible) || bProtected) + { + bIgnoreCols = true; + if ((aIter->NoCols() > 1) || bProtected) + bInsertSection = true; + } + + SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols)); + if (!aDesc.GetPageDesc()) + continue; + + // special case handling for odd/even section break + // a) as before create a new page style for the section break + // b) set Layout of generated page style to right/left ( according + // to section break odd/even ) + // c) create a new style to follow the break page style + if ( aIter->maSep.bkc == 3 || aIter->maSep.bkc == 4 ) + { + // SetSwFormatPageDesc calls some methods that could + // modify aIter (e.g. wwSection ). + // Since we call SetSwFormatPageDesc below to generate the + // 'Following' style of the Break style, it is safer + // to take a copy of the contents of aIter. + wwSection aTmpSection = *aIter; + // create a new following page style + SwFormatPageDesc aFollow(SetSwFormatPageDesc(aIter, aStart, bIgnoreCols)); + // restore any contents of aIter trashed by SetSwFormatPageDesc + *aIter = aTmpSection; + + // Handle the section break + UseOnPage eUseOnPage = UseOnPage::Left; + if ( aIter->maSep.bkc == 4 ) // Odd ( right ) Section break + eUseOnPage = UseOnPage::Right; + + // Keep the share flags. + aDesc.GetPageDesc()->SetUseOn( eUseOnPage ); + aDesc.GetPageDesc()->SetFollow( aFollow.GetPageDesc() ); + } + + GiveNodePageDesc(aIter->maStart, aDesc, mrReader.m_rDoc); + } + + SwTextNode* pTextNd = nullptr; + if (bInsertSection) + { + // Start getting the bounds of this section + SwPaM aSectPaM(*mrReader.m_pPaM, mrReader.m_pPaM); + SwNodeIndex aAnchor(aSectPaM.GetPoint()->nNode); + if (aNext != aEnd) + { + aAnchor = aNext->maStart; + aSectPaM.GetPoint()->nNode = aAnchor; + aSectPaM.GetPoint()->nContent.Assign( + aNext->maStart.GetNode().GetContentNode(), 0); + aSectPaM.Move(fnMoveBackward); + } + + const SwPosition* pPos = aSectPaM.GetPoint(); + SwTextNode const*const pSttNd = pPos->nNode.GetNode().GetTextNode(); + const SwTableNode* pTableNd = pSttNd ? pSttNd->FindTableNode() : nullptr; + if (pTableNd) + { + pTextNd = + mrReader.m_rDoc.GetNodes().MakeTextNode(aAnchor, + mrReader.m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT )); + + aSectPaM.GetPoint()->nNode.Assign(*pTextNd); + aSectPaM.GetPoint()->nContent.Assign( + aSectPaM.GetContentNode(), 0); + } + + aSectPaM.SetMark(); + + aSectPaM.GetPoint()->nNode = aIter->maStart; + aSectPaM.GetPoint()->nContent.Assign( + aSectPaM.GetContentNode(), 0); + + bool bHasOwnHdFt = false; + /* + In this nightmare scenario the continuous section has its own + headers and footers so we will try and find a hard page break + between here and the end of the section and put the headers and + footers there. + */ + if (!bInsertPageDesc) + { + bHasOwnHdFt = + mrReader.HasOwnHeaderFooter( + aIter->maSep.grpfIhdt & ~(WW8_HEADER_FIRST | WW8_FOOTER_FIRST), + aIter->maSep.grpfIhdt, std::distance(aStart, aIter) + ); + } + if (bHasOwnHdFt) + { + // #i40766# Need to cache the page descriptor in case there is + // no page break in the section + SwPageDesc *pOrig = aIter->mpPage; + bool bFailed = true; + SwFormatPageDesc aDesc(SetSwFormatPageDesc(aIter, aStart, true)); + if (aDesc.GetPageDesc()) + { + sal_uLong nStart = aSectPaM.Start()->nNode.GetIndex(); + sal_uLong nEnd = aSectPaM.End()->nNode.GetIndex(); + for(; nStart <= nEnd; ++nStart) + { + SwNode* pNode = mrReader.m_rDoc.GetNodes()[nStart]; + if (!pNode) + continue; + if (sw::util::HasPageBreak(*pNode)) + { + SwNodeIndex aIdx(*pNode); + GiveNodePageDesc(aIdx, aDesc, mrReader.m_rDoc); + bFailed = false; + break; + } + } + } + if(bFailed) + { + aIter->mpPage = pOrig; + } + } + + // End getting the bounds of this section, quite a job eh? + SwSectionFormat *pRet = InsertSection(aSectPaM, *aIter); + // The last section if continuous is always unbalanced + if (pRet) + { + // Set the columns to be UnBalanced if that compatibility option is set + if (mrReader.m_xWDop->fNoColumnBalance) + pRet->SetFormatAttr(SwFormatNoBalancedColumns(true)); + else + { + // Otherwise set to unbalanced if the following section is + // not continuous, (which also means that the last section + // is unbalanced) + if (aNext == aEnd || !aNext->IsContinuous()) + pRet->SetFormatAttr(SwFormatNoBalancedColumns(true)); + } + } + } + + if (pTextNd) + { + SwNodeIndex aIdx(*pTextNd); + SwPaM aTest(aIdx); + mrReader.m_rDoc.getIDocumentContentOperations().DelFullPara(aTest); + pTextNd = nullptr; + } + } +} + +void wwExtraneousParas::delete_all_from_doc() +{ + auto aEnd = m_aTextNodes.rend(); + for (auto aI = m_aTextNodes.rbegin(); aI != aEnd; ++aI) + { + SwTextNode *pTextNode = *aI; + SwNodeIndex aIdx(*pTextNode); + SwPaM aTest(aIdx); + m_rDoc.getIDocumentContentOperations().DelFullPara(aTest); + } + m_aTextNodes.clear(); +} + +void SwWW8ImplReader::StoreMacroCmds() +{ + if (m_xWwFib->m_lcbCmds) + { + bool bValidPos = checkSeek(*m_pTableStream, m_xWwFib->m_fcCmds); + if (!bValidPos) + return; + + uno::Reference < embed::XStorage > xRoot(m_pDocShell->GetStorage()); + + if (!xRoot.is()) + return; + + try + { + uno::Reference < io::XStream > xStream = + xRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READWRITE ); + std::unique_ptr<SvStream> xOutStream(::utl::UcbStreamHelper::CreateStream(xStream)); + + sal_uInt32 lcbCmds = std::min<sal_uInt32>(m_xWwFib->m_lcbCmds, m_pTableStream->remainingSize()); + std::unique_ptr<sal_uInt8[]> xBuffer(new sal_uInt8[lcbCmds]); + m_xWwFib->m_lcbCmds = m_pTableStream->ReadBytes(xBuffer.get(), lcbCmds); + xOutStream->WriteBytes(xBuffer.get(), m_xWwFib->m_lcbCmds); + } + catch (...) + { + } + } +} + +void SwWW8ImplReader::ReadDocVars() +{ + std::vector<OUString> aDocVarStrings; + std::vector<ww::bytes> aDocVarStringIds; + std::vector<OUString> aDocValueStrings; + WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcStwUser, + m_xWwFib->m_lcbStwUser, m_bVer67 ? 2 : 0, m_eStructCharSet, + aDocVarStrings, &aDocVarStringIds, &aDocValueStrings); + if (!m_bVer67) { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocProps.is(), "DocumentProperties is null"); + uno::Reference<beans::XPropertyContainer> xUserDefinedProps = + xDocProps->getUserDefinedProperties(); + OSL_ENSURE(xUserDefinedProps.is(), "UserDefinedProperties is null"); + + for(size_t i=0; i<aDocVarStrings.size(); i++) + { + const OUString &rName = aDocVarStrings[i]; + uno::Any aValue; + aValue <<= rName; + try { + xUserDefinedProps->addProperty( rName, + beans::PropertyAttribute::REMOVABLE, + aValue ); + } catch (const uno::Exception &) { + // ignore + } + } + } +} + +/** + * Document Info + */ +void SwWW8ImplReader::ReadDocInfo() +{ + if( m_pStg ) + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps( + xDPS->getDocumentProperties()); + OSL_ENSURE(xDocProps.is(), "DocumentProperties is null"); + + if (xDocProps.is()) + { + if ( m_xWwFib->m_fDot ) + { + OUString sTemplateURL; + SfxMedium* pMedium = m_pDocShell->GetMedium(); + if ( pMedium ) + { + const OUString& aName = pMedium->GetName(); + INetURLObject aURL( aName ); + sTemplateURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri); + if ( !sTemplateURL.isEmpty() ) + xDocProps->setTemplateURL( sTemplateURL ); + } + } + else if (m_xWwFib->m_lcbSttbfAssoc) // not a template, and has a SttbfAssoc + { + auto nCur = m_pTableStream->Tell(); + Sttb aSttb; + // point at tgc record + if (!checkSeek(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc) || !aSttb.Read(*m_pTableStream)) + SAL_WARN("sw.ww8", "** Read of SttbAssoc data failed!!!! "); + m_pTableStream->Seek( nCur ); // return to previous position, is that necessary? + OUString sPath = aSttb.getStringAtIndex( 0x1 ); + OUString aURL; + // attempt to convert to url (won't work for obvious reasons on linux) + if ( !sPath.isEmpty() ) + osl::FileBase::getFileURLFromSystemPath( sPath, aURL ); + if (aURL.isEmpty()) + xDocProps->setTemplateURL( aURL ); + else + xDocProps->setTemplateURL( sPath ); + + } + sfx2::LoadOlePropertySet(xDocProps, m_pStg); + } + } +} + +static void lcl_createTemplateToProjectEntry( const uno::Reference< container::XNameContainer >& xPrjNameCache, const OUString& sTemplatePathOrURL, const OUString& sVBAProjName ) +{ + if ( xPrjNameCache.is() ) + { + INetURLObject aObj; + aObj.SetURL( sTemplatePathOrURL ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + OUString aURL; + if ( bIsURL ) + aURL = sTemplatePathOrURL; + else + { + osl::FileBase::getFileURLFromSystemPath( sTemplatePathOrURL, aURL ); + aObj.SetURL( aURL ); + } + try + { + OUString templateNameWithExt = aObj.GetLastName(); + OUString templateName; + sal_Int32 nIndex = templateNameWithExt.lastIndexOf( '.' ); + if ( nIndex != -1 ) + { + templateName = templateNameWithExt.copy( 0, nIndex ); + xPrjNameCache->insertByName( templateName, uno::makeAny( sVBAProjName ) ); + } + } + catch( const uno::Exception& ) + { + } + } +} + +namespace { + +class WW8Customizations +{ + SvStream* mpTableStream; + WW8Fib mWw8Fib; +public: + WW8Customizations( SvStream*, WW8Fib const & ); + void Import( SwDocShell* pShell ); +}; + +} + +WW8Customizations::WW8Customizations( SvStream* pTableStream, WW8Fib const & rFib ) : mpTableStream(pTableStream), mWw8Fib( rFib ) +{ +} + +void WW8Customizations::Import( SwDocShell* pShell ) +{ + if ( mWw8Fib.m_lcbCmds == 0 || !IsEightPlus(mWw8Fib.GetFIBVersion()) ) + return; + try + { + Tcg aTCG; + long nCur = mpTableStream->Tell(); + if (!checkSeek(*mpTableStream, mWw8Fib.m_fcCmds)) // point at tgc record + { + SAL_WARN("sw.ww8", "** Seek to Customization data failed!!!! "); + return; + } + bool bReadResult = aTCG.Read( *mpTableStream ); + mpTableStream->Seek( nCur ); // return to previous position, is that necessary? + if ( !bReadResult ) + { + SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! "); + return; + } + aTCG.ImportCustomToolBar( *pShell ); + } + catch(...) + { + SAL_WARN("sw.ww8", "** Read of Customization data failed!!!! epically"); + } +} + +void SwWW8ImplReader::ReadGlobalTemplateSettings( const OUString& sCreatedFrom, const uno::Reference< container::XNameContainer >& xPrjNameCache ) +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + SvtPathOptions aPathOpt; + const OUString& aAddinPath = aPathOpt.GetAddinPath(); + uno::Sequence< OUString > sGlobalTemplates; + + // first get the autoload addins in the directory STARTUP + uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(::comphelper::getProcessComponentContext())); + + if( xSFA->isFolder( aAddinPath ) ) + sGlobalTemplates = xSFA->getFolderContents( aAddinPath, false ); + + for ( const auto& rGlobalTemplate : std::as_const(sGlobalTemplates) ) + { + INetURLObject aObj; + aObj.SetURL( rGlobalTemplate ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + OUString aURL; + if ( bIsURL ) + aURL = rGlobalTemplate; + else + osl::FileBase::getFileURLFromSystemPath( rGlobalTemplate, aURL ); + if ( !aURL.endsWithIgnoreAsciiCase( ".dot" ) || ( !sCreatedFrom.isEmpty() && sCreatedFrom == aURL ) ) + continue; // don't try and read the same document as ourselves + + tools::SvRef<SotStorage> rRoot = new SotStorage( aURL, StreamMode::STD_READWRITE ); + + BasicProjImportHelper aBasicImporter( *m_pDocShell ); + // Import vba via oox filter + aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() ); + lcl_createTemplateToProjectEntry( xPrjNameCache, aURL, aBasicImporter.getProjectName() ); + // Read toolbars & menus + tools::SvRef<SotStorageStream> refMainStream = rRoot->OpenSotStream( "WordDocument"); + refMainStream->SetEndian(SvStreamEndian::LITTLE); + WW8Fib aWwFib( *refMainStream, 8 ); + tools::SvRef<SotStorageStream> xTableStream = + rRoot->OpenSotStream(aWwFib.m_fWhichTableStm ? SL::a1Table : SL::a0Table, StreamMode::STD_READ); + + if (xTableStream.is() && ERRCODE_NONE == xTableStream->GetError()) + { + xTableStream->SetEndian(SvStreamEndian::LITTLE); + WW8Customizations aGblCustomisations( xTableStream.get(), aWwFib ); + aGblCustomisations.Import( m_pDocShell ); + } + } +} + +ErrCode SwWW8ImplReader::CoreLoad(WW8Glossary const *pGloss) +{ + m_rDoc.SetDocumentType( SwDoc::DOCTYPE_MSWORD ); + if (m_bNewDoc && m_pStg && !pGloss) + { + // Initialize RDF metadata, to be able to add statements during the import. + try + { + uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel()); + uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY_THROW); + uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + uno::Reference<embed::XStorage> xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + const uno::Reference<rdf::XURI> xBaseURI(sfx2::createBaseURI(xComponentContext, xModel, m_sBaseURL)); + uno::Reference<task::XInteractionHandler> xHandler; + xDocumentMetadataAccess->loadMetadataFromStorage(xStorage, xBaseURI, xHandler); + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sw.ww8", "failed to initialize RDF metadata"); + } + ReadDocInfo(); + } + + auto pFibData = std::make_shared<::ww8::WW8FibData>(); + + if (m_xWwFib->m_fReadOnlyRecommended) + pFibData->setReadOnlyRecommended(true); + else + pFibData->setReadOnlyRecommended(false); + + if (m_xWwFib->m_fWriteReservation) + pFibData->setWriteReservation(true); + else + pFibData->setWriteReservation(false); + + m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::FIB, pFibData); + + ::sw::tExternalDataPointer pSttbfAsoc + = std::make_shared<::ww8::WW8Sttb<ww8::WW8Struct>>(*m_pTableStream, m_xWwFib->m_fcSttbfAssoc, m_xWwFib->m_lcbSttbfAssoc); + + m_rDoc.getIDocumentExternalData().setExternalData(::sw::tExternalDataType::STTBF_ASSOC, pSttbfAsoc); + + if (m_xWwFib->m_fWriteReservation || m_xWwFib->m_fReadOnlyRecommended) + { + SwDocShell * pDocShell = m_rDoc.GetDocShell(); + if (pDocShell) + pDocShell->SetReadOnlyUI(); + } + + m_pPaM = mpCursor.get(); + + m_xCtrlStck.reset(new SwWW8FltControlStack(&m_rDoc, m_nFieldFlags, *this)); + + m_xRedlineStack.reset(new sw::util::RedlineStack(m_rDoc)); + + /* + RefFieldStck: Keeps track of bookmarks which may be inserted as + variables instead. + */ + m_xReffedStck.reset(new SwWW8ReferencedFltEndStack(&m_rDoc, m_nFieldFlags)); + m_xReffingStck.reset(new SwWW8FltRefStack(&m_rDoc, m_nFieldFlags)); + + m_xAnchorStck.reset(new SwWW8FltAnchorStack(&m_rDoc, m_nFieldFlags)); + + size_t nPageDescOffset = m_rDoc.GetPageDescCnt(); + + SwNodeIndex aSttNdIdx( m_rDoc.GetNodes() ); + + RedlineFlags eMode = RedlineFlags::ShowInsert | RedlineFlags::ShowDelete; + + m_xSprmParser.reset(new wwSprmParser(*m_xWwFib)); + + // Set handy helper variables + m_bVer6 = (6 == m_xWwFib->m_nVersion); + m_bVer7 = (7 == m_xWwFib->m_nVersion); + m_bVer67 = m_bVer6 || m_bVer7; + m_bVer8 = (8 == m_xWwFib->m_nVersion); + + m_eTextCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chse, m_xWwFib->m_lid); + m_eStructCharSet = WW8Fib::GetFIBCharset(m_xWwFib->m_chseTables, m_xWwFib->m_lid); + + m_bWWBugNormal = m_xWwFib->m_nProduct == 0xc03d; + + if (!m_bNewDoc) + aSttNdIdx = m_pPaM->GetPoint()->nNode; + + m_xProgress.reset(new ImportProgress(m_pDocShell, 0, 100)); + + // read Font Table + m_xFonts.reset(new WW8Fonts(*m_pTableStream, *m_xWwFib)); + + // Document Properties + m_xWDop.reset(new WW8Dop(*m_pTableStream, m_xWwFib->m_nFib, m_xWwFib->m_fcDop, + m_xWwFib->m_lcbDop)); + + if (m_bNewDoc) + ImportDop(); + + /* + Import revisioning data: author names + */ + if( m_xWwFib->m_lcbSttbfRMark ) + { + ReadRevMarkAuthorStrTabl(*m_pTableStream, + m_xWwFib->m_fcSttbfRMark, + m_xWwFib->m_lcbSttbfRMark, m_rDoc); + } + + // Initialize our String/ID map for Linked Sections + std::vector<OUString> aLinkStrings; + std::vector<ww::bytes> aStringIds; + + WW8ReadSTTBF(!m_bVer67, *m_pTableStream, m_xWwFib->m_fcSttbFnm, + m_xWwFib->m_lcbSttbFnm, m_bVer67 ? 2 : 0, m_eStructCharSet, + aLinkStrings, &aStringIds); + + for (size_t i=0; i < aLinkStrings.size() && i < aStringIds.size(); ++i) + { + const ww::bytes& stringId = aStringIds[i]; + if (stringId.size() < sizeof(WW8_STRINGID)) + { + SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: WW8_STRINGID is too short"); + continue; + } + const WW8_STRINGID *stringIdStruct = reinterpret_cast<const WW8_STRINGID*>(stringId.data()); + m_aLinkStringMap[SVBT16ToUInt16(stringIdStruct->nStringId)] = aLinkStrings[i]; + } + + ReadDocVars(); // import document variables as meta information. + + m_xProgress->Update(m_nProgress); // Update + + m_xLstManager.reset(new WW8ListManager(*m_pTableStream, *this)); + + /* + first (1) import all styles (see WW8PAR2.CXX) + BEFORE the import of the lists !! + */ + m_xProgress->Update(m_nProgress); // Update + m_xStyles.reset(new WW8RStyle(*m_xWwFib, this)); // Styles + m_xStyles->Import(); + + /* + In the end: (also see WW8PAR3.CXX) + + Go through all Styles and attach respective List Format + AFTER we imported the Styles and AFTER we imported the Lists! + */ + m_xProgress->Update(m_nProgress); // Update + m_xStyles->PostProcessStyles(); + + if (!m_vColl.empty()) + SetOutlineStyles(); + + m_xSBase.reset(new WW8ScannerBase(m_pStrm,m_pTableStream,m_pDataStream, m_xWwFib.get())); + + if (m_xSBase->AreThereFootnotes()) + { + static const SwFootnoteNum eNumA[4] = + { + FTNNUM_DOC, FTNNUM_CHAPTER, FTNNUM_PAGE, FTNNUM_DOC + }; + + SwFootnoteInfo aInfo = m_rDoc.GetFootnoteInfo(); // Copy-Ctor private + + aInfo.m_ePos = FTNPOS_PAGE; + aInfo.m_eNum = eNumA[m_xWDop->rncFootnote]; + sal_uInt16 nfcFootnoteRef = m_xWDop->nfcFootnoteRef; + aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcFootnoteRef)); + if( m_xWDop->nFootnote ) + aInfo.m_nFootnoteOffset = m_xWDop->nFootnote - 1; + m_rDoc.SetFootnoteInfo( aInfo ); + } + if (m_xSBase->AreThereEndnotes()) + { + SwEndNoteInfo aInfo = m_rDoc.GetEndNoteInfo(); // Same as for Footnote + sal_uInt16 nfcEdnRef = m_xWDop->nfcEdnRef; + aInfo.m_aFormat.SetNumberingType(WW8ListManager::GetSvxNumTypeFromMSONFC(nfcEdnRef)); + if( m_xWDop->nEdn ) + aInfo.m_nFootnoteOffset = m_xWDop->nEdn - 1; + m_rDoc.SetEndNoteInfo( aInfo ); + } + + if (m_xWwFib->m_lcbPlcfhdd) + m_xHdFt.reset(new WW8PLCF_HdFt(m_pTableStream, *m_xWwFib, *m_xWDop)); + + if (!m_bNewDoc) + { + // inserting into an existing document: + // As only complete paragraphs are inserted, the current one + // needs to be split - once or even twice. + const SwPosition* pPos = m_pPaM->GetPoint(); + + // split current paragraph to get new paragraph for the insertion + m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false ); + + // another split, if insertion position was not at the end of the current paragraph. + SwTextNode const*const pTextNd = pPos->nNode.GetNode().GetTextNode(); + if ( pTextNd->GetText().getLength() ) + { + m_rDoc.getIDocumentContentOperations().SplitNode( *pPos, false ); + // move PaM back to the newly empty paragraph + m_pPaM->Move( fnMoveBackward ); + } + + // suppress insertion of tables inside footnotes. + const sal_uLong nNd = pPos->nNode.GetIndex(); + m_bReadNoTable = ( nNd < m_rDoc.GetNodes().GetEndOfInserts().GetIndex() && + m_rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd ); + } + + m_xProgress->Update(m_nProgress); // Update + + // loop for each glossary entry and add dummy section node + if (pGloss) + { + WW8PLCF aPlc(*m_pTableStream, m_xWwFib->m_fcPlcfglsy, m_xWwFib->m_lcbPlcfglsy, 0); + + WW8_CP nStart, nEnd; + void* pDummy; + + for (int i = 0; i < pGloss->GetNoStrings(); ++i, aPlc.advance()) + { + SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfContent()); + SwTextFormatColl* pColl = + m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, + false); + SwStartNode *pNode = + m_rDoc.GetNodes().MakeTextSection(aIdx, + SwNormalStartNode,pColl); + m_pPaM->GetPoint()->nNode = pNode->GetIndex()+1; + m_pPaM->GetPoint()->nContent.Assign(m_pPaM->GetContentNode(),0); + aPlc.Get( nStart, nEnd, pDummy ); + ReadText(nStart,nEnd-nStart-1,MAN_MAINTEXT); + } + } + else // ordinary case + { + if (m_bNewDoc && m_pStg) /*meaningless for a glossary */ + { + m_pDocShell->SetIsTemplate( m_xWwFib->m_fDot ); // point at tgc record + uno::Reference<document::XDocumentPropertiesSupplier> const + xDocPropSupp(m_pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW ); + + OUString sCreatedFrom = xDocProps->getTemplateURL(); + uno::Reference< container::XNameContainer > xPrjNameCache; + uno::Reference< lang::XMultiServiceFactory> xSF(m_pDocShell->GetModel(), uno::UNO_QUERY); + if ( xSF.is() ) + xPrjNameCache.set( xSF->createInstance( "ooo.vba.VBAProjectNameProvider" ), uno::UNO_QUERY ); + + // Read Global templates + ReadGlobalTemplateSettings( sCreatedFrom, xPrjNameCache ); + + // Create and insert Word vba Globals + uno::Any aGlobs; + uno::Sequence< uno::Any > aArgs(1); + aArgs[ 0 ] <<= m_pDocShell->GetModel(); + try + { + aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.word.Globals", aArgs ); + } + catch (const uno::Exception&) + { + SAL_WARN("sw.ww8", "SwWW8ImplReader::CoreLoad: ooo.vba.word.Globals is not available"); + } + +#if HAVE_FEATURE_SCRIPTING + if (!utl::ConfigManager::IsFuzzing()) + { + BasicManager *pBasicMan = m_pDocShell->GetBasicManager(); + if (pBasicMan) + pBasicMan->SetGlobalUNOConstant( "VBAGlobals", aGlobs ); + } +#endif + BasicProjImportHelper aBasicImporter( *m_pDocShell ); + // Import vba via oox filter + bool bRet = aBasicImporter.import( m_pDocShell->GetMedium()->GetInputStream() ); + + lcl_createTemplateToProjectEntry( xPrjNameCache, sCreatedFrom, aBasicImporter.getProjectName() ); + WW8Customizations aCustomisations( m_pTableStream, *m_xWwFib ); + aCustomisations.Import( m_pDocShell ); + + if( bRet ) + m_rDoc.SetContainsMSVBasic(true); + + StoreMacroCmds(); + } + m_bOnLoadingMain = true; + ReadText(0, m_xWwFib->m_ccpText, MAN_MAINTEXT); + m_bOnLoadingMain = false; + } + + m_xProgress->Update(m_nProgress); // Update + + if (m_pDrawPg && m_xMSDffManager && m_xMSDffManager->GetShapeOrders()) + { + // Helper array to chain the inserted frames (instead of SdrTextObj) + SvxMSDffShapeTxBxSort aTxBxSort; + + // Ensure correct z-order of read Escher objects + sal_uInt16 nShapeCount = m_xMSDffManager->GetShapeOrders()->size(); + + for (sal_uInt16 nShapeNum=0; nShapeNum < nShapeCount; nShapeNum++) + { + SvxMSDffShapeOrder *pOrder = + (*m_xMSDffManager->GetShapeOrders())[nShapeNum].get(); + // Insert Pointer into new Sort array + if (pOrder->nTxBxComp && pOrder->pFly) + aTxBxSort.insert(pOrder); + } + // Chain Frames + if( !aTxBxSort.empty() ) + { + SwFormatChain aChain; + for( SvxMSDffShapeTxBxSort::iterator it = aTxBxSort.begin(); it != aTxBxSort.end(); ++it ) + { + SvxMSDffShapeOrder *pOrder = *it; + + // Initialize FlyFrame Formats + SwFlyFrameFormat* pFlyFormat = pOrder->pFly; + SwFlyFrameFormat* pNextFlyFormat = nullptr; + SwFlyFrameFormat* pPrevFlyFormat = nullptr; + + // Determine successor, if we can + SvxMSDffShapeTxBxSort::iterator tmpIter1 = it; + ++tmpIter1; + if( tmpIter1 != aTxBxSort.end() ) + { + SvxMSDffShapeOrder *pNextOrder = *tmpIter1; + if ((0xFFFF0000 & pOrder->nTxBxComp) + == (0xFFFF0000 & pNextOrder->nTxBxComp)) + pNextFlyFormat = pNextOrder->pFly; + } + // Determine predecessor, if we can + if( it != aTxBxSort.begin() ) + { + SvxMSDffShapeTxBxSort::iterator tmpIter2 = it; + --tmpIter2; + SvxMSDffShapeOrder *pPrevOrder = *tmpIter2; + if ((0xFFFF0000 & pOrder->nTxBxComp) + == (0xFFFF0000 & pPrevOrder->nTxBxComp)) + pPrevFlyFormat = pPrevOrder->pFly; + } + // If successor or predecessor present, insert the + // chain at the FlyFrame Format + if (pNextFlyFormat || pPrevFlyFormat) + { + aChain.SetNext( pNextFlyFormat ); + aChain.SetPrev( pPrevFlyFormat ); + pFlyFormat->SetFormatAttr( aChain ); + } + } + } + } + + bool isHideRedlines(false); + + if (m_bNewDoc) + { + if( m_xWDop->fRevMarking ) + eMode |= RedlineFlags::On; + isHideRedlines = !m_xWDop->fRMView; + } + + m_aInsertedTables.DelAndMakeTableFrames(); + m_aSectionManager.InsertSegments(); + + m_vColl.clear(); + + m_xStyles.reset(); + + m_xFormImpl.reset(); + GrafikDtor(); + m_xMSDffManager.reset(); + m_xHdFt.reset(); + m_xSBase.reset(); + m_xWDop.reset(); + m_xFonts.reset(); + m_pAtnNames.reset(); + m_xSprmParser.reset(); + m_xProgress.reset(); + + m_pDataStream = nullptr; + m_pTableStream = nullptr; + + DeleteCtrlStack(); + DeleteAnchorStack(); + DeleteRefStacks(); + m_pLastAnchorPos.reset();//ensure this is deleted before UpdatePageDescs + // ofz#10994 remove any trailing fly paras before processing redlines + m_xWFlyPara.reset(); + // ofz#12660 remove any trailing fly paras before deleting extra paras + m_xSFlyPara.reset(); + // remove extra paragraphs after attribute ctrl + // stacks etc. are destroyed, and before fields + // are updated + m_aExtraneousParas.delete_all_from_doc(); + m_xRedlineStack->closeall(*m_pPaM->GetPoint()); + while (!m_aFrameRedlines.empty()) + m_aFrameRedlines.pop(); + + // For i120928,achieve the graphics from the special bookmark with is for graphic bullet + { + std::vector<const SwGrfNode*> vecBulletGrf; + std::vector<SwFrameFormat*> vecFrameFormat; + + IDocumentMarkAccess* const pMarkAccess = m_rDoc.getIDocumentMarkAccess(); + if ( pMarkAccess ) + { + IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark( "_PictureBullets" ); + if ( ppBkmk != pMarkAccess->getBookmarksEnd() && + IDocumentMarkAccess::GetType(**ppBkmk) == IDocumentMarkAccess::MarkType::BOOKMARK ) + { + SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().nNode.GetNode().GetTextNode(); + + if ( pTextNode ) + { + const SwpHints* pHints = pTextNode->GetpSwpHints(); + for( size_t nHintPos = 0; pHints && nHintPos < pHints->Count(); ++nHintPos) + { + const SwTextAttr *pHt = pHints->Get(nHintPos); + if (pHt->Which() != RES_TXTATR_FLYCNT) + continue; + const sal_Int32 st = pHt->GetStart(); + if (st >= (*ppBkmk)->GetMarkStart().nContent.GetIndex()) + { + SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat(); + vecFrameFormat.push_back(pFrameFormat); + const SwNodeIndex* pNdIdx = pFrameFormat->GetContent().GetContentIdx(); + const SwNodes* pNodesArray = (pNdIdx != nullptr) + ? &(pNdIdx->GetNodes()) + : nullptr; + const SwGrfNode *pGrf = (pNodesArray != nullptr) + ? dynamic_cast<const SwGrfNode*>((*pNodesArray)[pNdIdx->GetIndex() + 1]) + : nullptr; + vecBulletGrf.push_back(pGrf); + } + } + // update graphic bullet information + size_t nCount = m_xLstManager->GetWW8LSTInfoNum(); + for (size_t i = 0; i < nCount; ++i) + { + SwNumRule* pRule = m_xLstManager->GetNumRule(i); + for (sal_uInt16 j = 0; j < MAXLEVEL; ++j) + { + SwNumFormat aNumFormat(pRule->Get(j)); + const sal_Int16 nType = aNumFormat.GetNumberingType(); + const sal_uInt16 nGrfBulletCP = aNumFormat.GetGrfBulletCP(); + if ( nType == SVX_NUM_BITMAP + && vecBulletGrf.size() > nGrfBulletCP + && vecBulletGrf[nGrfBulletCP] != nullptr ) + { + Graphic aGraphic = vecBulletGrf[nGrfBulletCP]->GetGrf(); + SvxBrushItem aBrush(aGraphic, GPOS_AREA, SID_ATTR_BRUSH); + const vcl::Font& aFont = numfunc::GetDefBulletFont(); + int nHeight = aFont.GetFontHeight() * 12; + Size aPrefSize( aGraphic.GetPrefSize()); + if (aPrefSize.Height() * aPrefSize.Width() != 0 ) + { + int nWidth = (nHeight * aPrefSize.Width()) / aPrefSize.Height(); + Size aSize(nWidth, nHeight); + aNumFormat.SetGraphicBrush(&aBrush, &aSize); + } + else + { + aNumFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + aNumFormat.SetBulletChar(0x2190); + } + pRule->Set( j, aNumFormat ); + } + } + } + // Remove additional pictures + for (SwFrameFormat* p : vecFrameFormat) + { + m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat(p); + } + } + } + } + m_xLstManager.reset(); + } + + m_pPosAfterTOC.reset(); + m_xRedlineStack.reset(); + mpCursor.reset(); + m_pPaM = nullptr; + + UpdateFields(); + + // delete the pam before the call for hide all redlines (Bug 73683) + if (m_bNewDoc) + m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(eMode); + + UpdatePageDescs(m_rDoc, nPageDescOffset); + + // can't set it on the layout or view shell because it doesn't exist yet + m_rDoc.GetDocumentRedlineManager().SetHideRedlines(isHideRedlines); + + return ERRCODE_NONE; +} + +ErrCode SwWW8ImplReader::SetSubStreams(tools::SvRef<SotStorageStream> &rTableStream, + tools::SvRef<SotStorageStream> &rDataStream) +{ + ErrCode nErrRet = ERRCODE_NONE; + // 6 stands for "6 OR 7", 7 stands for "ONLY 7" + switch (m_xWwFib->m_nVersion) + { + case 6: + case 7: + m_pTableStream = m_pStrm; + m_pDataStream = m_pStrm; + break; + case 8: + if(!m_pStg) + { + OSL_ENSURE( m_pStg, "Version 8 always needs to have a Storage!!" ); + nErrRet = ERR_SWG_READ_ERROR; + break; + } + + rTableStream = m_pStg->OpenSotStream( + m_xWwFib->m_fWhichTableStm ? SL::a1Table : SL::a0Table, + StreamMode::STD_READ); + + m_pTableStream = rTableStream.get(); + m_pTableStream->SetEndian( SvStreamEndian::LITTLE ); + + rDataStream = m_pStg->OpenSotStream(SL::aData, StreamMode::STD_READ); + + if (rDataStream.is() && ERRCODE_NONE == rDataStream->GetError()) + { + m_pDataStream = rDataStream.get(); + m_pDataStream->SetEndian(SvStreamEndian::LITTLE); + } + else + m_pDataStream = m_pStrm; + break; + default: + // Program error! + OSL_ENSURE( false, "We forgot to encode nVersion!" ); + nErrRet = ERR_SWG_READ_ERROR; + break; + } + return nErrRet; +} + +namespace +{ + std::unique_ptr<utl::TempFile> MakeTemp(SvFileStream &rSt) + { + std::unique_ptr<utl::TempFile> pT(new utl::TempFile); + pT->EnableKillingFile(); + rSt.Open(pT->GetFileName(), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE); + return pT; + } + +#define WW_BLOCKSIZE 0x200 + + void DecryptRC4(msfilter::MSCodec97& rCtx, SvStream &rIn, SvStream &rOut) + { + const std::size_t nLen = rIn.TellEnd(); + rIn.Seek(0); + + sal_uInt8 in[WW_BLOCKSIZE]; + for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock) + { + std::size_t nBS = std::min<size_t>(nLen - nI, WW_BLOCKSIZE); + nBS = rIn.ReadBytes(in, nBS); + rCtx.InitCipher(nBlock); + rCtx.Decode(in, nBS, in, nBS); + rOut.WriteBytes(in, nBS); + } + } + + void DecryptXOR(msfilter::MSCodec_XorWord95 &rCtx, SvStream &rIn, SvStream &rOut) + { + std::size_t nSt = rIn.Tell(); + std::size_t nLen = rIn.TellEnd(); + + rCtx.InitCipher(); + rCtx.Skip(nSt); + + sal_uInt8 in[0x4096]; + for (std::size_t nI = nSt; nI < nLen; nI += 0x4096) + { + std::size_t nBS = std::min<size_t>(nLen - nI, 0x4096 ); + nBS = rIn.ReadBytes(in, nBS); + rCtx.Decode(in, nBS); + rOut.WriteBytes(in, nBS); + } + } + + // moan, copy and paste :-( + OUString QueryPasswordForMedium(SfxMedium& rMedium) + { + OUString aPassw; + + const SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxPoolItem *pPasswordItem; + + if(pSet && SfxItemState::SET == pSet->GetItemState(SID_PASSWORD, true, &pPasswordItem)) + aPassw = static_cast<const SfxStringItem *>(pPasswordItem)->GetValue(); + else + { + try + { + uno::Reference< task::XInteractionHandler > xHandler( rMedium.GetInteractionHandler() ); + if( xHandler.is() ) + { + ::comphelper::DocPasswordRequest* pRequest = new ::comphelper::DocPasswordRequest( + ::comphelper::DocPasswordRequestType::MS, task::PasswordRequestMode_PASSWORD_ENTER, + INetURLObject(rMedium.GetOrigURL()) + .GetLastName(INetURLObject::DecodeMechanism::WithCharset)); + uno::Reference< task::XInteractionRequest > xRequest( pRequest ); + + xHandler->handle( xRequest ); + + if( pRequest->isPassword() ) + aPassw = pRequest->getPassword(); + } + } + catch( const uno::Exception& ) + { + } + } + + return aPassw; + } + + uno::Sequence< beans::NamedValue > InitXorWord95Codec( ::msfilter::MSCodec_XorWord95& rCodec, SfxMedium& rMedium, WW8Fib const * pWwFib ) + { + uno::Sequence< beans::NamedValue > aEncryptionData; + const SfxUnoAnyItem* pEncryptionData = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false); + if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) ) + aEncryptionData.realloc( 0 ); + + if ( !aEncryptionData.hasElements() ) + { + OUString sUniPassword = QueryPasswordForMedium( rMedium ); + + OString sPassword(OUStringToOString(sUniPassword, + WW8Fib::GetFIBCharset(pWwFib->m_chseTables, pWwFib->m_lid))); + + sal_Int32 nLen = sPassword.getLength(); + if( nLen <= 15 ) + { + sal_uInt8 pPassword[16]; + memcpy(pPassword, sPassword.getStr(), nLen); + memset(pPassword+nLen, 0, sizeof(pPassword)-nLen); + + rCodec.InitKey( pPassword ); + aEncryptionData = rCodec.GetEncryptionData(); + + // the export supports RC4 algorithm only, so we have to + // generate the related EncryptionData as well, so that Save + // can export the document without asking for a password; + // as result there will be EncryptionData for both algorithms + // in the MediaDescriptor + ::msfilter::MSCodec_Std97 aCodec97; + + rtlRandomPool aRandomPool = rtl_random_createPool(); + sal_uInt8 pDocId[ 16 ]; + rtl_random_getBytes( aRandomPool, pDocId, 16 ); + + rtl_random_destroyPool( aRandomPool ); + + sal_uInt16 pStd97Pass[16] = {}; + for( sal_Int32 nChar = 0; nChar < nLen; ++nChar ) + pStd97Pass[nChar] = sUniPassword[nChar]; + + aCodec97.InitKey( pStd97Pass, pDocId ); + + // merge the EncryptionData, there should be no conflicts + ::comphelper::SequenceAsHashMap aEncryptionHash( aEncryptionData ); + aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) ); + aEncryptionHash >> aEncryptionData; + } + } + + return aEncryptionData; + } + + uno::Sequence< beans::NamedValue > Init97Codec(msfilter::MSCodec97& rCodec, sal_uInt8 const pDocId[16], SfxMedium& rMedium) + { + uno::Sequence< beans::NamedValue > aEncryptionData; + const SfxUnoAnyItem* pEncryptionData = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false); + if ( pEncryptionData && ( pEncryptionData->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) ) + aEncryptionData.realloc( 0 ); + + if ( !aEncryptionData.hasElements() ) + { + OUString sUniPassword = QueryPasswordForMedium( rMedium ); + + sal_Int32 nLen = sUniPassword.getLength(); + if ( nLen <= 15 ) + { + sal_uInt16 pPassword[16] = {}; + for( sal_Int32 nChar = 0; nChar < nLen; ++nChar ) + pPassword[nChar] = sUniPassword[nChar]; + + rCodec.InitKey( pPassword, pDocId ); + aEncryptionData = rCodec.GetEncryptionData(); + } + } + + return aEncryptionData; + } +} + +//TO-DO: merge this with lclReadFilepass8_Strong in sc which uses a different +//stream thing +static bool lclReadCryptoAPIHeader(msfilter::RC4EncryptionInfo &info, SvStream &rStream) +{ + //It is possible there are other variants in existence but these + //are the defaults I get with Word 2013 + + rStream.ReadUInt32(info.header.flags); + if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL)) + return false; + + sal_uInt32 nHeaderSize(0); + rStream.ReadUInt32(nHeaderSize); + sal_uInt32 actualHeaderSize = sizeof(info.header); + + if (nHeaderSize < actualHeaderSize) + return false; + + rStream.ReadUInt32(info.header.flags); + rStream.ReadUInt32(info.header.sizeExtra); + rStream.ReadUInt32(info.header.algId); + rStream.ReadUInt32(info.header.algIdHash); + rStream.ReadUInt32(info.header.keyBits); + rStream.ReadUInt32(info.header.providedType); + rStream.ReadUInt32(info.header.reserved1); + rStream.ReadUInt32(info.header.reserved2); + + rStream.SeekRel(nHeaderSize - actualHeaderSize); + + rStream.ReadUInt32(info.verifier.saltSize); + if (info.verifier.saltSize != msfilter::SALT_LENGTH) + return false; + rStream.ReadBytes(&info.verifier.salt, sizeof(info.verifier.salt)); + rStream.ReadBytes(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)); + + rStream.ReadUInt32(info.verifier.encryptedVerifierHashSize); + if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1) + return false; + rStream.ReadBytes(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize); + + // check flags and algorithm IDs, required are AES128 and SHA-1 + if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI)) + return false; + + if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES)) + return false; + + if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4) + return false; + + // hash algorithm ID 0 defaults to SHA-1 too + if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1) + return false; + + return true; +} + +ErrCode SwWW8ImplReader::LoadThroughDecryption(WW8Glossary *pGloss) +{ + ErrCode nErrRet = ERRCODE_NONE; + if (pGloss) + m_xWwFib = pGloss->GetFib(); + else + m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion); + + if (m_xWwFib->m_nFibError) + nErrRet = ERR_SWG_READ_ERROR; + + tools::SvRef<SotStorageStream> xTableStream, xDataStream; + + if (!nErrRet) + nErrRet = SetSubStreams(xTableStream, xDataStream); + + std::unique_ptr<utl::TempFile> pTempMain; + std::unique_ptr<utl::TempFile> pTempTable; + std::unique_ptr<utl::TempFile> pTempData; + SvFileStream aDecryptMain; + SvFileStream aDecryptTable; + SvFileStream aDecryptData; + + bool bDecrypt = false; + enum {RC4CryptoAPI, RC4, XOR, Other} eAlgo = Other; + if (m_xWwFib->m_fEncrypted && !nErrRet) + { + if (!pGloss) + { + bDecrypt = true; + if (8 != m_xWwFib->m_nVersion) + eAlgo = XOR; + else + { + if (m_xWwFib->m_nKey != 0) + eAlgo = XOR; + else + { + m_pTableStream->Seek(0); + sal_uInt32 nEncType(0); + m_pTableStream->ReadUInt32(nEncType); + if (nEncType == msfilter::VERSION_INFO_1997_FORMAT) + eAlgo = RC4; + else if (nEncType == msfilter::VERSION_INFO_2007_FORMAT || nEncType == msfilter::VERSION_INFO_2007_FORMAT_SP2) + eAlgo = RC4CryptoAPI; + } + } + } + } + + if (bDecrypt) + { + nErrRet = ERRCODE_SVX_WRONGPASS; + SfxMedium* pMedium = m_pDocShell->GetMedium(); + + if ( pMedium ) + { + switch (eAlgo) + { + default: + nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT; + break; + case XOR: + { + msfilter::MSCodec_XorWord95 aCtx; + uno::Sequence< beans::NamedValue > aEncryptionData = InitXorWord95Codec(aCtx, *pMedium, m_xWwFib.get()); + + // if initialization has failed the EncryptionData should be empty + if (aEncryptionData.hasElements() && aCtx.VerifyKey(m_xWwFib->m_nKey, m_xWwFib->m_nHash)) + { + nErrRet = ERRCODE_NONE; + pTempMain = MakeTemp(aDecryptMain); + + m_pStrm->Seek(0); + size_t nUnencryptedHdr = + (8 == m_xWwFib->m_nVersion) ? 0x44 : 0x34; + std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]); + nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr); + aDecryptMain.WriteBytes(pIn.get(), nUnencryptedHdr); + pIn.reset(); + + DecryptXOR(aCtx, *m_pStrm, aDecryptMain); + + if (!m_pTableStream || m_pTableStream == m_pStrm) + m_pTableStream = &aDecryptMain; + else + { + pTempTable = MakeTemp(aDecryptTable); + DecryptXOR(aCtx, *m_pTableStream, aDecryptTable); + m_pTableStream = &aDecryptTable; + } + + if (!m_pDataStream || m_pDataStream == m_pStrm) + m_pDataStream = &aDecryptMain; + else + { + pTempData = MakeTemp(aDecryptData); + DecryptXOR(aCtx, *m_pDataStream, aDecryptData); + m_pDataStream = &aDecryptData; + } + + pMedium->GetItemSet()->ClearItem( SID_PASSWORD ); + pMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) ); + } + } + break; + case RC4: + case RC4CryptoAPI: + { + std::unique_ptr<msfilter::MSCodec97> xCtx; + msfilter::RC4EncryptionInfo info; + bool bCouldReadHeaders; + + if (eAlgo == RC4) + { + xCtx.reset(new msfilter::MSCodec_Std97); + assert(sizeof(info.verifier.encryptedVerifierHash) >= RTL_DIGEST_LENGTH_MD5); + bCouldReadHeaders = + checkRead(*m_pTableStream, info.verifier.salt, sizeof(info.verifier.salt)) && + checkRead(*m_pTableStream, info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier)) && + checkRead(*m_pTableStream, info.verifier.encryptedVerifierHash, RTL_DIGEST_LENGTH_MD5); + } + else + { + xCtx.reset(new msfilter::MSCodec_CryptoAPI); + bCouldReadHeaders = lclReadCryptoAPIHeader(info, *m_pTableStream); + } + + // if initialization has failed the EncryptionData should be empty + uno::Sequence< beans::NamedValue > aEncryptionData; + if (bCouldReadHeaders) + aEncryptionData = Init97Codec(*xCtx, info.verifier.salt, *pMedium); + else + nErrRet = ERRCODE_SVX_READ_FILTER_CRYPT; + if (aEncryptionData.hasElements() && xCtx->VerifyKey(info.verifier.encryptedVerifier, + info.verifier.encryptedVerifierHash)) + { + nErrRet = ERRCODE_NONE; + + pTempMain = MakeTemp(aDecryptMain); + + m_pStrm->Seek(0); + std::size_t nUnencryptedHdr = 0x44; + std::unique_ptr<sal_uInt8[]> pIn(new sal_uInt8[nUnencryptedHdr]); + nUnencryptedHdr = m_pStrm->ReadBytes(pIn.get(), nUnencryptedHdr); + + DecryptRC4(*xCtx, *m_pStrm, aDecryptMain); + + aDecryptMain.Seek(0); + aDecryptMain.WriteBytes(pIn.get(), nUnencryptedHdr); + pIn.reset(); + + pTempTable = MakeTemp(aDecryptTable); + DecryptRC4(*xCtx, *m_pTableStream, aDecryptTable); + m_pTableStream = &aDecryptTable; + + if (!m_pDataStream || m_pDataStream == m_pStrm) + m_pDataStream = &aDecryptMain; + else + { + pTempData = MakeTemp(aDecryptData); + DecryptRC4(*xCtx, *m_pDataStream, aDecryptData); + m_pDataStream = &aDecryptData; + } + + pMedium->GetItemSet()->ClearItem( SID_PASSWORD ); + pMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) ); + } + } + break; + } + } + + if (nErrRet == ERRCODE_NONE) + { + m_pStrm = &aDecryptMain; + + m_xWwFib = std::make_shared<WW8Fib>(*m_pStrm, m_nWantedVersion); + if (m_xWwFib->m_nFibError) + nErrRet = ERR_SWG_READ_ERROR; + } + } + + if (!nErrRet) + nErrRet = CoreLoad(pGloss); + + pTempMain.reset(); + pTempTable.reset(); + pTempData.reset(); + + m_xWwFib.reset(); + return nErrRet; +} + +void SwWW8ImplReader::SetOutlineStyles() +{ + // If we are inserted into a document then don't clobber existing outline + // levels. + sal_uInt16 nOutlineStyleListLevelWithAssignment = 0; + if (!m_bNewDoc) + { + ww8::ParaStyles aOutLined(sw::util::GetParaStyles(m_rDoc)); + sw::util::SortByAssignedOutlineStyleListLevel(aOutLined); + ww8::ParaStyles::reverse_iterator aEnd = aOutLined.rend(); + for ( ww8::ParaStyles::reverse_iterator aIter = aOutLined.rbegin(); aIter < aEnd; ++aIter) + { + if ((*aIter)->IsAssignedToListLevelOfOutlineStyle()) + nOutlineStyleListLevelWithAssignment |= 1 << (*aIter)->GetAssignedOutlineStyleLevel(); + else + break; + } + } + + // Check applied WW8 list styles at WW8 Built-In Heading Styles + // - Choose the list style which occurs most often as the one which provides + // the list level properties for the Outline Style. + // - Populate temporary list of WW8 Built-In Heading Styles for further + // iteration + std::vector<SwWW8StyInf*> aWW8BuiltInHeadingStyles; + const SwNumRule* pChosenWW8ListStyle = nullptr; + { + std::map<const SwNumRule*, int> aWW8ListStyleCounts; + for (SwWW8StyInf & rSI : m_vColl) + { + if (!rSI.IsWW8BuiltInHeadingStyle() || !rSI.HasWW8OutlineLevel()) + { + continue; + } + + aWW8BuiltInHeadingStyles.push_back(&rSI); + + const SwNumRule* pWW8ListStyle = rSI.GetOutlineNumrule(); + if (pWW8ListStyle != nullptr) + { + std::map<const SwNumRule*, int>::iterator aCountIter + = aWW8ListStyleCounts.find(pWW8ListStyle); + if (aCountIter == aWW8ListStyleCounts.end()) + { + aWW8ListStyleCounts[pWW8ListStyle] = 1; + } + else + { + ++(aCountIter->second); + } + } + } + + int nCurrentMaxCount = 0; + for (const auto& rEntry : aWW8ListStyleCounts) + { + if (rEntry.second > nCurrentMaxCount) + { + nCurrentMaxCount = rEntry.second; + pChosenWW8ListStyle = rEntry.first; + } + } + } + + // - set list level properties of Outline Style - ODF's list style applied + // by default to headings + // - assign corresponding Heading Paragraph Styles to the Outline Style + // - If a heading Paragraph Styles is not applying the WW8 list style which + // had been chosen as + // the one which provides the list level properties for the Outline Style, + // its assignment to + // the Outline Style is removed. A potential applied WW8 list style is + // assigned directly and + // its default outline level is applied. + SwNumRule aOutlineRule(*m_rDoc.GetOutlineNumRule()); + bool bAppliedChangedOutlineStyle = false; + for (const SwWW8StyInf* pStyleInf : aWW8BuiltInHeadingStyles) + { + if (!pStyleInf->m_bColl) //Character Style + continue; + + const sal_uInt16 nOutlineStyleListLevelOfWW8BuiltInHeadingStyle + = 1 << pStyleInf->mnWW8OutlineLevel; + if (nOutlineStyleListLevelOfWW8BuiltInHeadingStyle + & nOutlineStyleListLevelWithAssignment) + { + continue; + } + + if (pChosenWW8ListStyle != nullptr && pStyleInf->mnWW8OutlineLevel + == pStyleInf->m_nListLevel) + { + const SwNumFormat& rRule + = pChosenWW8ListStyle->Get(pStyleInf->mnWW8OutlineLevel); + aOutlineRule.Set(pStyleInf->mnWW8OutlineLevel, rRule); + bAppliedChangedOutlineStyle = true; + } + + // in case that there are more styles on this level ignore them + nOutlineStyleListLevelWithAssignment + |= nOutlineStyleListLevelOfWW8BuiltInHeadingStyle; + + SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pStyleInf->m_pFormat); + if (pStyleInf->GetOutlineNumrule() != pChosenWW8ListStyle + || (pStyleInf->m_nListLevel < WW8ListManager::nMaxLevel + && pStyleInf->mnWW8OutlineLevel != pStyleInf->m_nListLevel)) + { + // WW8 Built-In Heading Style does not apply the chosen one. + // --> delete assignment to OutlineStyle, but keep its current + // outline level + pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle(); + // Apply existing WW8 list style a normal list style at the + // Paragraph Style + if (pStyleInf->GetOutlineNumrule() != nullptr) + { + pTextFormatColl->SetFormatAttr( + SwNumRuleItem(pStyleInf->GetOutlineNumrule()->GetName())); + } + // apply default outline level of WW8 Built-in Heading Style + const sal_uInt8 nOutlineLevel + = SwWW8StyInf::WW8OutlineLevelToOutlinelevel( + pStyleInf->mnWW8OutlineLevel); + pTextFormatColl->SetFormatAttr( + SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel)); + } + else + { + pTextFormatColl->AssignToListLevelOfOutlineStyle( + pStyleInf->mnWW8OutlineLevel); + } + } + + if (bAppliedChangedOutlineStyle) + { + m_rDoc.SetOutlineNumRule(aOutlineRule); + } +} + +const OUString* SwWW8ImplReader::GetAnnotationAuthor(sal_uInt16 nIdx) +{ + if (!m_pAtnNames && m_xWwFib->m_lcbGrpStAtnOwners) + { + // Determine authors: can be found in the TableStream + m_pAtnNames.reset(new std::vector<OUString>); + SvStream& rStrm = *m_pTableStream; + + long nOldPos = rStrm.Tell(); + rStrm.Seek( m_xWwFib->m_fcGrpStAtnOwners ); + + long nRead = 0, nCount = m_xWwFib->m_lcbGrpStAtnOwners; + while (nRead < nCount && rStrm.good()) + { + if( m_bVer67 ) + { + m_pAtnNames->push_back(read_uInt8_PascalString(rStrm, + RTL_TEXTENCODING_MS_1252)); + nRead += m_pAtnNames->rbegin()->getLength() + 1; // Length + sal_uInt8 count + } + else + { + m_pAtnNames->push_back(read_uInt16_PascalString(rStrm)); + // Unicode: double the length + sal_uInt16 count + nRead += (m_pAtnNames->rbegin()->getLength() + 1)*2; + } + } + rStrm.Seek( nOldPos ); + } + + const OUString *pRet = nullptr; + if (m_pAtnNames && nIdx < m_pAtnNames->size()) + pRet = &((*m_pAtnNames)[nIdx]); + return pRet; +} + +void SwWW8ImplReader::GetSmartTagInfo(SwFltRDFMark& rMark) +{ + if (!m_pSmartTagData && m_xWwFib->m_lcbFactoidData) + { + m_pSmartTagData.reset(new WW8SmartTagData); + m_pSmartTagData->Read(*m_pTableStream, m_xWwFib->m_fcFactoidData, m_xWwFib->m_lcbFactoidData); + } + + if (!m_pSmartTagData) + return; + + // Check if the handle is a valid smart tag bookmark index. + size_t nIndex = rMark.GetHandle(); + if (nIndex >= m_pSmartTagData->m_aPropBags.size()) + return; + + // Check if the smart tag bookmark refers to a valid factoid type. + const MSOPropertyBag& rPropertyBag = m_pSmartTagData->m_aPropBags[rMark.GetHandle()]; + auto& rFactoidTypes = m_pSmartTagData->m_aPropBagStore.m_aFactoidTypes; + auto itPropertyBag = std::find_if(rFactoidTypes.begin(), rFactoidTypes.end(), + [&rPropertyBag](const MSOFactoidType& rType) { return rType.m_nId == rPropertyBag.m_nId; }); + if (itPropertyBag == rFactoidTypes.end()) + return; + + // Check if the factoid is an RDF one. + const MSOFactoidType& rFactoidType = *itPropertyBag; + if (rFactoidType.m_aUri != "http://www.w3.org/1999/02/22-rdf-syntax-ns#") + return; + + // Finally put the relevant attributes to the mark. + std::vector< std::pair<OUString, OUString> > aAttributes; + for (const MSOProperty& rProperty : rPropertyBag.m_aProperties) + { + OUString aKey; + OUString aValue; + if (rProperty.m_nKey < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size()) + aKey = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nKey]; + if (rProperty.m_nValue < m_pSmartTagData->m_aPropBagStore.m_aStringTable.size()) + aValue = m_pSmartTagData->m_aPropBagStore.m_aStringTable[rProperty.m_nValue]; + if (!aKey.isEmpty() && !aValue.isEmpty()) + aAttributes.emplace_back(aKey, aValue); + } + rMark.SetAttributes(aAttributes); +} + +ErrCode SwWW8ImplReader::LoadDoc(WW8Glossary *pGloss) +{ + ErrCode nErrRet = ERRCODE_NONE; + + { + static const char* aNames[ 13 ] = { + "WinWord/WW", "WinWord/WW8", "WinWord/WWFT", + "WinWord/WWFLX", "WinWord/WWFLY", + "WinWord/WWF", + "WinWord/WWFA0", "WinWord/WWFA1", "WinWord/WWFA2", + "WinWord/WWFB0", "WinWord/WWFB1", "WinWord/WWFB2", + "WinWord/RegardHindiDigits" + }; + sal_uInt64 aVal[ 13 ]; + + SwFilterOptions aOpt( 13, aNames, aVal ); + + m_nIniFlags = aVal[ 0 ]; + m_nIniFlags1= aVal[ 1 ]; + // Moves Flys by x twips to the right or left + m_nIniFlyDx = aVal[ 3 ]; + m_nIniFlyDy = aVal[ 4 ]; + + m_nFieldFlags = aVal[ 5 ]; + m_nFieldTagAlways[0] = aVal[ 6 ]; + m_nFieldTagAlways[1] = aVal[ 7 ]; + m_nFieldTagAlways[2] = aVal[ 8 ]; + m_nFieldTagBad[0] = aVal[ 9 ]; + m_nFieldTagBad[1] = aVal[ 10 ]; + m_nFieldTagBad[2] = aVal[ 11 ]; + m_bRegardHindiDigits = aVal[ 12 ] > 0; + } + + sal_uInt16 nMagic(0); + m_pStrm->ReadUInt16( nMagic ); + + // Remember: 6 means "6 OR 7", 7 means "JUST 7" + switch (m_nWantedVersion) + { + case 6: + case 7: + if ( + 0xa59b != nMagic && 0xa59c != nMagic && + 0xa5dc != nMagic && 0xa5db != nMagic && + (nMagic < 0xa697 || nMagic > 0xa699) + ) + { + // Test for own 97 fake! + if (m_pStg && 0xa5ec == nMagic) + { + sal_uLong nCurPos = m_pStrm->Tell(); + if (m_pStrm->Seek(nCurPos + 22)) + { + sal_uInt32 nfcMin; + m_pStrm->ReadUInt32( nfcMin ); + if (0x300 != nfcMin) + nErrRet = ERR_WW6_NO_WW6_FILE_ERR; + } + m_pStrm->Seek( nCurPos ); + } + else + nErrRet = ERR_WW6_NO_WW6_FILE_ERR; + } + break; + case 8: + if (0xa5ec != nMagic) + nErrRet = ERR_WW8_NO_WW8_FILE_ERR; + break; + default: + nErrRet = ERR_WW8_NO_WW8_FILE_ERR; + OSL_ENSURE( false, "We forgot to encode nVersion!" ); + break; + } + + if (!nErrRet) + nErrRet = LoadThroughDecryption(pGloss); + + m_rDoc.PropagateOutlineRule(); + + return nErrRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT Reader* ImportDOC() +{ + return new WW8Reader; +} + +namespace +{ + class FontCacheGuard + { + public: + ~FontCacheGuard() + { + FlushFontCache(); + } + }; +} + +bool TestImportDOC(SvStream &rStream, const OUString &rFltName) +{ + FontCacheGuard aFontCacheGuard; + std::unique_ptr<Reader> xReader(ImportDOC()); + + tools::SvRef<SotStorage> xStorage; + xReader->m_pStream = &rStream; + if (rFltName != "WW6") + { + try + { + xStorage = tools::SvRef<SotStorage>(new SotStorage(rStream)); + if (xStorage->GetError()) + return false; + } + catch (...) + { + return false; + } + xReader->m_pStorage = xStorage.get(); + } + xReader->SetFltName(rFltName); + + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + SwDoc *pD = static_cast<SwDocShell*>((&xDocSh))->GetDoc(); + + SwNodeIndex aIdx(pD->GetNodes().GetEndOfContent(), -1); + SwPaM aPaM(aIdx); + aPaM.GetPoint()->nContent.Assign(aIdx.GetNode().GetContentNode(), 0); + pD->SetInReading(true); + bool bRet = xReader->Read(*pD, OUString(), aPaM, OUString()) == ERRCODE_NONE; + pD->SetInReading(false); + + return bRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW8(SvStream &rStream) +{ + return TestImportDOC(rStream, "CWW8"); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW6(SvStream &rStream) +{ + return TestImportDOC(rStream, "CWW6"); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWW2(SvStream &rStream) +{ + return TestImportDOC(rStream, "WW6"); +} + +ErrCode WW8Reader::OpenMainStream( tools::SvRef<SotStorageStream>& rRef, sal_uInt16& rBuffSize ) +{ + ErrCode nRet = ERR_SWG_READ_ERROR; + OSL_ENSURE(m_pStorage, "Where is my Storage?"); + rRef = m_pStorage->OpenSotStream( "WordDocument", StreamMode::READ | StreamMode::SHARE_DENYALL); + + if( rRef.is() ) + { + if( ERRCODE_NONE == rRef->GetError() ) + { + sal_uInt16 nOld = rRef->GetBufferSize(); + rRef->SetBufferSize( rBuffSize ); + rBuffSize = nOld; + nRet = ERRCODE_NONE; + } + else + nRet = rRef->GetError(); + } + return nRet; +} + +static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, const OUString& sPrefix) +{ + SvStorageInfoList aElements; + pStorage->FillInfoList(&aElements); + for (const auto & aElement : aElements) + { + OUString sStreamFullName = sPrefix.getLength() ? sPrefix + "/" + aElement.GetName() : aElement.GetName(); + if (aElement.IsStorage()) + { + tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL); + lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName); + } + else + { + // Read stream + tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL); + if (rStream.is()) + { + sal_Int32 nStreamSize = rStream->GetSize(); + css::uno::Sequence< sal_Int8 > oData; + oData.realloc(nStreamSize); + sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize); + if (nStreamSize == nReadBytes) + aStreamsData[sStreamFullName] <<= oData; + } + } + } +} + +ErrCode WW8Reader::DecryptDRMPackage() +{ + // We have DRM encrypted storage. We should try to decrypt it first, if we can + uno::Sequence< uno::Any > aArguments; + uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + uno::Reference< packages::XPackageEncryption > xPackageEncryption( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY); + + if (!xPackageEncryption.is()) + { + // We do not know how to decrypt this + return ERRCODE_IO_ACCESSDENIED; + } + + comphelper::SequenceAsHashMap aStreamsData; + lcl_getListOfStreams(m_pStorage.get(), aStreamsData, ""); + + try { + uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList(); + if (!xPackageEncryption->readEncryptionInfo(aStreams)) + { + // We failed with decryption + return ERRCODE_IO_ACCESSDENIED; + } + + tools::SvRef<SotStorageStream> rContentStream = m_pStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL); + if (!rContentStream.is()) + { + return ERRCODE_IO_NOTEXISTS; + } + + mDecodedStream = std::make_shared<SvMemoryStream>(); + + uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false)); + uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*mDecodedStream)); + + if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream)) + { + // We failed with decryption + return ERRCODE_IO_ACCESSDENIED; + } + + mDecodedStream->Seek(0); + + // Further reading is done from new document + m_pStorage = new SotStorage(*mDecodedStream); + + // Set the media descriptor data + uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData(""); + m_pMedium->GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::makeAny(aEncryptionData))); + } + catch (const std::exception&) + { + return ERRCODE_IO_ACCESSDENIED; + } + + return ERRCODE_NONE; +} + +ErrCode WW8Reader::Read(SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & /* FileName */) +{ + sal_uInt16 nOldBuffSize = 32768; + bool bNew = !m_bInsertMode; // New Doc (no inserting) + + tools::SvRef<SotStorageStream> refStrm; // So that no one else can steal the Stream + SvStream* pIn = m_pStream; + + ErrCode nRet = ERRCODE_NONE; + sal_uInt8 nVersion = 8; + + const OUString sFltName = GetFltName(); + if ( sFltName=="WW6" ) + { + if (m_pStream) + nVersion = 6; + else + { + OSL_ENSURE(false, "WinWord 95 Reader-Read without Stream"); + nRet = ERR_SWG_READ_ERROR; + } + } + else + { + if ( sFltName=="CWW6" ) + nVersion = 6; + else if ( sFltName=="CWW7" ) + nVersion = 7; + + if( m_pStorage.is() ) + { + // Check if we have special encrypted content + tools::SvRef<SotStorageStream> rRef = m_pStorage->OpenSotStream("\006DataSpaces/DataSpaceInfo/\011DRMDataSpace", StreamMode::READ | StreamMode::SHARE_DENYALL); + if (rRef.is()) + { + nRet = DecryptDRMPackage(); + } + nRet = OpenMainStream(refStrm, nOldBuffSize); + pIn = refStrm.get(); + } + else + { + OSL_ENSURE(false, "WinWord 95/97 Reader-Read without Storage"); + nRet = ERR_SWG_READ_ERROR; + } + } + + if( !nRet ) + { + std::unique_ptr<SwWW8ImplReader> pRdr(new SwWW8ImplReader(nVersion, m_pStorage.get(), pIn, rDoc, + rBaseURL, bNew, m_bSkipImages, *rPaM.GetPoint())); + if (bNew) + { + rPaM.GetBound().nContent.Assign(nullptr, 0); + rPaM.GetBound(false).nContent.Assign(nullptr, 0); + } + try + { + nRet = pRdr->LoadDoc(); + } + catch( const std::exception& ) + { + nRet = ERR_WW8_NO_WW8_FILE_ERR; + } + + if( refStrm.is() ) + { + refStrm->SetBufferSize( nOldBuffSize ); + refStrm.clear(); + } + else + { + pIn->ResetError(); + } + + } + return nRet; +} + +SwReaderType WW8Reader::GetReaderType() +{ + return SwReaderType::Storage | SwReaderType::Stream; +} + +bool WW8Reader::HasGlossaries() const +{ + return true; +} + +bool WW8Reader::ReadGlossaries(SwTextBlocks& rBlocks, bool bSaveRelFiles) const +{ + bool bRet=false; + + WW8Reader *pThis = const_cast<WW8Reader *>(this); + + sal_uInt16 nOldBuffSize = 32768; + tools::SvRef<SotStorageStream> refStrm; + if (!pThis->OpenMainStream(refStrm, nOldBuffSize)) + { + WW8Glossary aGloss( refStrm, 8, m_pStorage.get() ); + bRet = aGloss.Load( rBlocks, bSaveRelFiles ); + } + return bRet; +} + +bool SwMSDffManager::GetOLEStorageName(sal_uInt32 nOLEId, OUString& rStorageName, + tools::SvRef<SotStorage>& rSrcStorage, uno::Reference < embed::XStorage >& rDestStorage) const +{ + bool bRet = false; + + sal_Int32 nPictureId = 0; + if (rReader.m_pStg) + { + // Via the TextBox-PLCF we get the right char Start-End positions + // We should then find the EmbeddedField and the corresponding Sprms + // in that Area. + // We only need the Sprm for the Picture Id. + long nOldPos = rReader.m_pStrm->Tell(); + { + // #i32596# - consider return value of method + // <rReader.GetTxbxTextSttEndCp(..)>. If it returns false, method + // wasn't successful. Thus, continue in this case. + // Note: Ask MM for initialization of <nStartCp> and <nEndCp>. + // Note: Ask MM about assertions in method <rReader.GetTxbxTextSttEndCp(..)>. + WW8_CP nStartCp, nEndCp; + if ( rReader.m_bDrawCpOValid && rReader.GetTxbxTextSttEndCp(nStartCp, nEndCp, + static_cast<sal_uInt16>((nOLEId >> 16) & 0xFFFF), + static_cast<sal_uInt16>(nOLEId & 0xFFFF)) ) + { + WW8PLCFxSaveAll aSave; + rReader.m_xPlcxMan->SaveAllPLCFx( aSave ); + + nStartCp += rReader.m_nDrawCpO; + nEndCp += rReader.m_nDrawCpO; + WW8PLCFx_Cp_FKP* pChp = rReader.m_xPlcxMan->GetChpPLCF(); + wwSprmParser aSprmParser(*rReader.m_xWwFib); + while (nStartCp <= nEndCp && !nPictureId) + { + if (!pChp->SeekPos( nStartCp)) + break; + WW8PLCFxDesc aDesc; + pChp->GetSprms( &aDesc ); + + if (aDesc.nSprmsLen && aDesc.pMemPos) // Attributes present + { + long nLen = aDesc.nSprmsLen; + const sal_uInt8* pSprm = aDesc.pMemPos; + + while (nLen >= 2 && !nPictureId) + { + sal_uInt16 nId = aSprmParser.GetSprmId(pSprm); + sal_uInt16 nSL = aSprmParser.GetSprmSize(nId, pSprm, nLen); + + if( nLen < nSL ) + break; // Not enough Bytes left + + if (0x6A03 == nId) + { + nPictureId = SVBT32ToUInt32(pSprm + + aSprmParser.DistanceToData(nId)); + bRet = true; + } + pSprm += nSL; + nLen -= nSL; + } + } + nStartCp = aDesc.nEndPos; + } + + rReader.m_xPlcxMan->RestoreAllPLCFx( aSave ); + } + } + rReader.m_pStrm->Seek( nOldPos ); + } + + if( bRet ) + { + rStorageName = "_"; + rStorageName += OUString::number(nPictureId); + rSrcStorage = rReader.m_pStg->OpenSotStorage(SL::aObjectPool); + if (!rReader.m_pDocShell) + bRet=false; + else + rDestStorage = rReader.m_pDocShell->GetStorage(); + } + return bRet; +} + +/** + * When reading a single Box (which possibly is part of a group), we do + * not yet have enough information to decide whether we need it as a TextField + * or not. + * So convert all of them as a precaution. + * FIXME: Actually implement this! + */ +bool SwMSDffManager::ShapeHasText(sal_uLong, sal_uLong) const +{ + return true; +} + +bool SwWW8ImplReader::InEqualOrHigherApo(int nLvl) const +{ + if (nLvl) + --nLvl; + // #i60827# - check size of <maApos> to assure that <maApos.begin() + nLvl> can be performed. + if ( sal::static_int_cast< sal_Int32>(nLvl) >= sal::static_int_cast< sal_Int32>(m_aApos.size()) ) + { + return false; + } + auto aIter = std::find(m_aApos.begin() + nLvl, m_aApos.end(), true); + return aIter != m_aApos.end(); +} + +bool SwWW8ImplReader::InEqualApo(int nLvl) const +{ + // If we are in a table, see if an apo was inserted at the level below the table. + if (nLvl) + --nLvl; + if (nLvl < 0 || o3tl::make_unsigned(nLvl) >= m_aApos.size()) + return false; + return m_aApos[nLvl]; +} + +namespace sw::hack +{ + Position::Position(const SwPosition &rPos) + : maPtNode(rPos.nNode), mnPtContent(rPos.nContent.GetIndex()) + { + } + + Position::operator SwPosition() const + { + SwPosition aRet(maPtNode); + aRet.nContent.Assign(maPtNode.GetNode().GetContentNode(), mnPtContent); + return aRet; + } +} + +SwMacroInfo::SwMacroInfo() + : SdrObjUserData( SdrInventor::ScOrSwDraw, SW_UD_IMAPDATA ) + , mnShapeId(-1) +{ +} + +SwMacroInfo::~SwMacroInfo() +{ +} + +std::unique_ptr<SdrObjUserData> SwMacroInfo::Clone( SdrObject* /*pObj*/ ) const +{ + return std::unique_ptr<SdrObjUserData>(new SwMacroInfo( *this )); +} + +std::unique_ptr<SfxItemSet> SwWW8ImplReader::SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet) +{ + std::unique_ptr<SfxItemSet> xRet(std::move(m_xCurrentItemSet)); + m_xCurrentItemSet = std::move(pItemSet); + return xRet; +} + +void SwWW8ImplReader::NotifyMacroEventRead() +{ + if (m_bNotifyMacroEventRead) + return; + uno::Reference<frame::XModel> const xModel(m_rDoc.GetDocShell()->GetBaseModel()); + comphelper::DocumentInfo::notifyMacroEventRead(xModel); + m_bNotifyMacroEventRead = true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par.hxx b/sw/source/filter/ww8/ww8par.hxx new file mode 100644 index 000000000..22ffce886 --- /dev/null +++ b/sw/source/filter/ww8/ww8par.hxx @@ -0,0 +1,1940 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_WW8PAR_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR_HXX + +#include <rtl/ustring.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include <filter/msfilter/util.hxx> +#include <editeng/frmdir.hxx> +#include <fltshell.hxx> + +#include <svx/svdobj.hxx> + +#include <vector> +#include <stack> +#include <deque> +#include <map> +#include <utility> +#include <memory> + +#include "ww8struc.hxx" +#include "ww8scan.hxx" +#include "ww8glsy.hxx" +#include "ww8graf.hxx" +#include "wrtww8.hxx" +#include <msfilter.hxx> +#include <xmloff/odffields.hxx> +#include <IMark.hxx> + +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <swtypes.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtsrnd.hxx> +#include <ndtxt.hxx> +#include <editeng/lrspitem.hxx> +#include <o3tl/deleter.hxx> +#include <o3tl/sorted_vector.hxx> +#include <oox/ole/olehelper.hxx> + +#define SW_UD_IMAPDATA 2 + +class SwDoc; +class SwPaM; +class SfxPoolItem; +class SwTextFormatColl; +class SwPageDesc; +class SvxBoxItem; +class SwFormat; +class SwNodeIndex; +class SwFlyFrameFormat; +class SwAttrSet; +class SwNumRule; +class SwFrameFormat; +class Writer; +class SwFormatField; +class WW8Fib; +class WW8PLCFMan; +struct WW8PLCFManResult; +class WW8RStyle; +class WW8PLCF_HdFt; +class WW8ScannerBase; +struct WW8FlyPara; +struct WW8SwFlyPara; +struct WW8_PIC; +class WW8TabDesc; +struct WW8_SHD; +struct WW8_OLST; +class SwNumFormat; +struct WW8_ANLD; +struct WW8_ANLV; +struct WW8_DO; +struct WW8_DPHEAD; +struct WW8_FSPA; +class SdrModel; +class SdrPage; +class SdrObject; +class SdrTextObj; +class SdrUnoObj; +class Size; +class EditEngine; +struct SwPosition; +class WW8ReaderSave; +struct WW8PicDesc; +class Graphic; +class SwFieldType; +class SotStorage; +class SwAttrSet; +class GDIMetaFile; +struct ESelection; +class SfxItemSet; +class OutlinerParaObject; + +namespace com::sun::star{ + namespace beans{ class XPropertySet;} + namespace form { class XFormComponent;} + namespace drawing{class XShape;} + namespace lang{class XMultiServiceFactory;} +} + +// defines only for the WW8-variable of the INI file +#define WW8FL_NO_STYLES 2 +#define WW8FL_NO_GRAF 0x80 + +#define WW8FL_NO_OUTLINE 0x1000 +#define WW8FL_NO_IMPLPASP 0x4000 // no implicit para space +#define WW8FL_NO_GRAFLAYER 0x8000 + +// Add-on-filter-flags, valid from Winword 8 on +#define WW8FL_NO_FLY_FOR_TXBX 1 + +// List-Manager (from Ver8 on) + +struct WW8LFOInfo; + +class WW8Reader : public StgReader +{ + std::shared_ptr<SvStream> mDecodedStream; + virtual ErrCode Read(SwDoc &, const OUString& rBaseURL, SwPaM &, const OUString &) override; + ErrCode OpenMainStream( tools::SvRef<SotStorageStream>& rRef, sal_uInt16& rBuffSize ); + ErrCode DecryptDRMPackage(); +public: + WW8Reader() {} + virtual SwReaderType GetReaderType() override; + + virtual bool HasGlossaries() const override; + virtual bool ReadGlossaries( SwTextBlocks&, bool bSaveRelFiles ) const override; +}; + +class SwWW8ImplReader; +struct WW8LSTInfo; +class WW8ListManager +{ +public: + WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_); + //Min and Max possible List Levels in Word + enum ListLevel {nMinLevel=1, nMaxLevel=9}; + //the rParaSprms returns back the original word paragraph indent + //sprms which were attached to the original numbering format + SwNumRule* GetNumRuleForActivation(sal_uInt16 nLFOPosition, const sal_uInt8 nLevel, + std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode=nullptr); + SwNumRule* CreateNextRule(bool bSimple); + ~WW8ListManager() COVERITY_NOEXCEPT_FALSE; + SwNumRule* GetNumRule(size_t i); + size_t GetWW8LSTInfoNum() const{return maLSTInfos.size();} + static SvxNumType GetSvxNumTypeFromMSONFC(sal_uInt16 nMSONFC); + +private: + wwSprmParser maSprmParser; + SwWW8ImplReader& rReader; + SwDoc& rDoc; + const WW8Fib& rFib; + SvStream& rSt; + std::vector<std::unique_ptr<WW8LSTInfo>> maLSTInfos; + std::vector<std::unique_ptr<WW8LFOInfo>> m_LFOInfos;// D. from PLF LFO, sorted exactly like in the WW8 Stream + sal_uInt16 nUniqueList; // current number for creating unique list names + SprmResult GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms, sal_uInt8 nLen); + WW8LSTInfo* GetLSTByListId( sal_uInt32 nIdLst ) const; + //the rParaSprms returns back the original word paragraph indent + //sprms which are attached to this numbering level + bool ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet, sal_uInt16 nLevelStyle, + bool bSetStartNo, sal_uInt16 nLevel, ww::bytes &rParaSprms); + + // character attributes from GrpprlChpx + typedef std::unique_ptr<SfxItemSet> WW8aISet[nMaxLevel]; + // character style pointer + typedef SwCharFormat* WW8aCFormat[nMaxLevel]; + + void AdjustLVL(sal_uInt8 nLevel, SwNumRule& rNumRule, WW8aISet const & rListItemSet, + WW8aCFormat& aCharFormat, bool& bNewCharFormatCreated, + const OUString& aPrefix = OUString()); + + WW8ListManager(const WW8ListManager&) = delete; + WW8ListManager& operator=(const WW8ListManager&) = delete; + sal_uInt16 nLastLFOPosition; +}; + +struct WW8FlyPara +{ // WinWord-attributes + // Attention: *DO NOT* reorder, since parts will be + // compared using memcmp + bool bVer67; + sal_Int16 nSp26, nSp27; // raw position + sal_Int16 nSp45, nSp28; // width / height + sal_Int16 nLeMgn, nRiMgn, nUpMgn, nLoMgn; // borders + sal_uInt8 nSp29; // raw binding + alignment + sal_uInt8 nSp37; // Wrap-Mode ( 1 / 2; 0 = no Apo ? ) + WW8_BRCVer9_5 brc; // borders Top, Left, Bottom, Right, Between + bool bBorderLines; // border lines + bool bGrafApo; // true: this frame is only used to position + // the contained graphics *not* as a character + bool mbVertSet; // true if vertical positioning has been set + + WW8FlyPara(bool bIsVer67, const WW8FlyPara* pSrc = nullptr); + bool operator==(const WW8FlyPara& rSrc) const; + void Read(sal_uInt8 nSprm29, WW8PLCFx_Cp_FKP* pPap); + void ReadFull(sal_uInt8 nSprm29, SwWW8ImplReader* pIo); + void Read(sal_uInt8 nSprm29, WW8RStyle const * pStyle); + void ApplyTabPos(const WW8_TablePos *pTabPos); + bool IsEmpty() const; +}; + +class SwWW8StyInf +{ + OUString m_sWWStyleName; + sal_uInt16 m_nWWStyleId; +public: + rtl_TextEncoding m_eLTRFontSrcCharSet; // rtl_TextEncoding for the font + rtl_TextEncoding m_eRTLFontSrcCharSet; // rtl_TextEncoding for the font + rtl_TextEncoding m_eCJKFontSrcCharSet; // rtl_TextEncoding for the font + SwFormat* m_pFormat; + std::shared_ptr<WW8FlyPara> m_xWWFly; + SwNumRule* m_pOutlineNumrule; + long m_nFilePos; + sal_uInt16 m_nBase; + sal_uInt16 m_nFollow; + sal_uInt16 m_nLFOIndex; + sal_uInt8 m_nListLevel; + + // WW8 outline level is zero-based: + // 0: outline level 1 + // 1: outline level 2 + // ... + // 8: outline level 9 + // 9: body text + sal_uInt8 mnWW8OutlineLevel; + + sal_uInt16 m_n81Flags; // for bold, italic, ... + sal_uInt16 m_n81BiDiFlags; // for bold, italic, ... + std::shared_ptr<SvxLRSpaceItem> maWordLR; + bool m_bValid; // empty of valid + bool m_bImported; // for recursive imports + bool m_bColl; // true-> pFormat is SwTextFormatColl + bool m_bImportSkipped; // only true if !bNewDoc && existing style + bool m_bHasStyNumRule; // true-> named NumRule in style + bool m_bHasBrokenWW6List; // true-> WW8+ style has a WW7- list + bool m_bListReleventIndentSet; //true if this style's indent has + //been explicitly set, it's set to the value + //of pFormat->GetItemState(RES_LR_SPACE, false) + //if it was possible to get the ItemState + //for L of the LR space independently + bool m_bParaAutoBefore; // For Auto spacing before a paragraph + bool m_bParaAutoAfter; // For Auto Spacing after a paragraph + sal_Int16 m_nRelativeJustify; + + SwWW8StyInf() : + m_sWWStyleName( OUString() ), + m_nWWStyleId( 0 ), + m_eLTRFontSrcCharSet(0), + m_eRTLFontSrcCharSet(0), + m_eCJKFontSrcCharSet(0), + m_pFormat( nullptr ), + m_pOutlineNumrule( nullptr ), + m_nFilePos( 0 ), + m_nBase( 0 ), + m_nFollow( 0 ), + m_nLFOIndex( USHRT_MAX ), + m_nListLevel(WW8ListManager::nMaxLevel), + mnWW8OutlineLevel( MAXLEVEL ), + m_n81Flags( 0 ), + m_n81BiDiFlags(0), + maWordLR(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE)), + m_bValid(false), + m_bImported(false), + m_bColl(false), + m_bImportSkipped(false), + m_bHasStyNumRule(false), + m_bHasBrokenWW6List(false), + m_bListReleventIndentSet(false), + m_bParaAutoBefore(false), + m_bParaAutoAfter(false), + m_nRelativeJustify(-1) + + {} + + void SetOrgWWIdent( const OUString& rName, const sal_uInt16 nId ) + { + m_sWWStyleName = rName; + m_nWWStyleId = nId; + + // apply default WW8 outline level to WW8 Built-in Heading styles + if (IsWW8BuiltInHeadingStyle()) + { + mnWW8OutlineLevel = m_nWWStyleId - 1; + } + } + + const OUString& GetOrgWWName() const + { + return m_sWWStyleName; + } + + bool HasWW8OutlineLevel() const + { + return (m_pFormat != nullptr && (MAXLEVEL > mnWW8OutlineLevel)); + } + + bool IsOutlineNumbered() const + { + return m_pOutlineNumrule && HasWW8OutlineLevel(); + } + + const SwNumRule* GetOutlineNumrule() const + { + return m_pOutlineNumrule; + } + rtl_TextEncoding GetCharSet() const; + rtl_TextEncoding GetCJKCharSet() const; + + sal_uInt16 GetWWStyleId() const + { + return m_nWWStyleId; + } + + bool IsWW8BuiltInHeadingStyle() const + { + return GetWWStyleId() >= 1 && GetWWStyleId() <= 9; + } + + bool IsWW8BuiltInDefaultStyle() const + { + return GetWWStyleId() == 0; + } + + static sal_uInt8 + WW8OutlineLevelToOutlinelevel(const sal_uInt8 nWW8OutlineLevel) + { + if (nWW8OutlineLevel < MAXLEVEL) + { + if (nWW8OutlineLevel == 9) + { + return 0; // no outline level --> body text + } + else + { + return nWW8OutlineLevel + 1; // outline level 1..9 + } + } + + return 0; + } +}; + +// Stack + +class SwWW8FltControlStack : public SwFltControlStack +{ +private: + SwWW8ImplReader& rReader; + std::unique_ptr<SfxItemSet> m_xScratchSet; + sal_uInt16 nToggleAttrFlags; + sal_uInt16 nToggleBiDiAttrFlags; + SwWW8FltControlStack(const SwWW8FltControlStack&) = delete; + SwWW8FltControlStack& operator=(const SwWW8FltControlStack&) = delete; + const SwNumFormat* GetNumFormatFromStack(const SwPosition &rPos, + const SwTextNode &rTextNode); +protected: + virtual void SetAttrInDoc(const SwPosition& rTmpPos, + SwFltStackEntry& rEntry) override; + + virtual sal_Int32 GetCurrAttrCP() const override; + virtual bool IsParaEndInCPs(sal_Int32 nStart, sal_Int32 nEnd, bool bSdOD) const override; + //Clear the para end position recorded in reader intermittently for the least impact on loading performance + virtual void ClearParaEndPosition() override; + virtual bool CheckSdOD(sal_Int32 nStart,sal_Int32 nEnd) override; + +public: + SwWW8FltControlStack(SwDoc* pDo, sal_uLong nFieldFl, SwWW8ImplReader& rReader_ ) + : SwFltControlStack( pDo, nFieldFl ), rReader( rReader_ ), + nToggleAttrFlags(0), nToggleBiDiAttrFlags(0) + {} + + void NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr); + + virtual SwFltStackEntry* SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, bool bTstEnd=true, long nHand=LONG_MAX, bool consumedByField=false) override; + + void SetToggleAttr(sal_uInt8 nId, bool bOn) + { + if( bOn ) + nToggleAttrFlags |= (1 << nId); + else + nToggleAttrFlags &= ~(1 << nId); + } + + sal_uInt16 GetToggleAttrFlags() const { return nToggleAttrFlags; } + + void SetToggleBiDiAttr(sal_uInt8 nId, bool bOn) + { + if( bOn ) + nToggleBiDiAttrFlags |= (1 << nId); + else + nToggleBiDiAttrFlags &= ~(1 << nId); + } + + sal_uInt16 GetToggleBiDiAttrFlags() const { return nToggleBiDiAttrFlags; } + void SetToggleAttrFlags(sal_uInt16 nFlags) { nToggleAttrFlags = nFlags; } + void SetToggleBiDiAttrFlags(sal_uInt16 nFlags) {nToggleBiDiAttrFlags = nFlags;} + + const SfxPoolItem* GetFormatAttr(const SwPosition& rPos, sal_uInt16 nWhich); + template<class T> const T* GetFormatAttr( const SwPosition& rPos, TypedWhichId<T> nWhich ) + { + return static_cast<const T*>(GetFormatAttr(rPos, sal_uInt16(nWhich))); + } + const SfxPoolItem* GetStackAttr(const SwPosition& rPos, sal_uInt16 nWhich); +}; + +//The only thing this is for is RES_FLTR_ANCHOR, anything else is an error. +//For graphics whose anchoring position would otherwise be automatically moved +//along by the insertion of text. +class SwWW8FltAnchorStack : public SwFltControlStack +{ +public: + SwWW8FltAnchorStack(SwDoc* pDo, sal_uLong nFieldFl) + : SwFltControlStack( pDo, nFieldFl ) {} + void AddAnchor(const SwPosition& rPos,SwFrameFormat *pFormat); + void Flush(); +private: + SwWW8FltAnchorStack(const SwWW8FltAnchorStack&) = delete; + SwWW8FltAnchorStack& operator=(const SwWW8FltAnchorStack&) = delete; +}; + + +namespace SwWW8 +{ + struct ltstr + { + bool operator()(const OUString &r1, const OUString &r2) const + { + return r1.compareToIgnoreAsciiCase(r2)<0; + } + }; + + struct ltnode + { + bool operator()(const SwTextNode *r1, const SwTextNode *r2) const + { + return r1->GetIndex() < r2->GetIndex(); + } + }; +}; + +class SwWW8ReferencedFltEndStack : public SwFltEndStack +{ +public: + SwWW8ReferencedFltEndStack( SwDoc* pDo, sal_uLong nFieldFl ) + : SwFltEndStack( pDo, nFieldFl ) + , aReferencedTOCBookmarks() + {} + + // Keep track of referenced TOC bookmarks in order to suppress the import + // of unreferenced ones. + std::set<OUString, SwWW8::ltstr> aReferencedTOCBookmarks; +protected: + virtual void SetAttrInDoc( const SwPosition& rTmpPos, + SwFltStackEntry& rEntry ) override; +}; + +class SwWW8FltRefStack final : public SwFltEndStack +{ +public: + SwWW8FltRefStack(SwDoc* pDo, sal_uLong nFieldFl) + : SwFltEndStack( pDo, nFieldFl ) + , aFieldVarNames() + {} + bool IsFootnoteEdnBkmField(const SwFormatField& rFormatField, sal_uInt16& rBkmNo); + + //Keep track of variable names created with fields, and the bookmark + //mapped to their position, hopefully the same, but very possibly + //an additional pseudo bookmark + std::map<OUString, OUString, SwWW8::ltstr> aFieldVarNames; +private: + SwFltStackEntry *RefToVar(const SwField* pField,SwFltStackEntry& rEntry); + virtual void SetAttrInDoc(const SwPosition& rTmpPos, + SwFltStackEntry& rEntry) override; + SwWW8FltRefStack(const SwWW8FltRefStack&) = delete; + SwWW8FltRefStack& operator=(const SwWW8FltRefStack&) = delete; +}; + +template< typename Type > +inline bool get_flag( Type nBitField, Type nMask ) +{ return (nBitField & nMask) != 0; } + +template< typename ReturnType, typename Type > +inline ReturnType ulimit_cast( Type nValue, ReturnType nMax ) +{ return static_cast< ReturnType >( std::min< Type >( nValue, nMax ) ); } + +template< typename ReturnType, typename Type > +inline ReturnType ulimit_cast( Type nValue ) +{ return ulimit_cast( nValue, std::numeric_limits< ReturnType >::max() ); } + +class SwMacroInfo : public SdrObjUserData +{ +public: + SwMacroInfo(); + virtual ~SwMacroInfo() override; + + SwMacroInfo(SwMacroInfo const &) = default; + SwMacroInfo(SwMacroInfo &&) = default; + SwMacroInfo & operator =(SwMacroInfo const &) = delete; // due to SdrObjUserData + SwMacroInfo & operator =(SwMacroInfo &&) = delete; // due to SdrObjUserData + + virtual std::unique_ptr<SdrObjUserData> Clone( SdrObject* pObj ) const override; + + void SetHlink( const OUString& rHlink ) { maHlink = rHlink; } + const OUString& GetHlink() const { return maHlink; } + void SetTarFrame( const OUString& rTarFrame ) { maTarFrame = rTarFrame; } + const OUString& GetTarFrame() const { return maTarFrame; } + void SetShapeId( sal_Int32 rShapeId ) { mnShapeId = rShapeId; } + const sal_Int32& GetShapeId() const { return mnShapeId; } + void SetName( const OUString& rName ) { maNameStr = rName; } + const OUString& GetName() const { return maNameStr; } + +private: + sal_Int32 mnShapeId; + OUString maHlink; + OUString maNameStr; + OUString maTarFrame; +}; + +struct HyperLinksTable +{ + OUString hLinkAddr; + OUString tarFrame; +}; + +namespace sw +{ + namespace hack + { + class Position + { + private: + SwNodeIndex maPtNode; + sal_Int32 mnPtContent; + public: + explicit Position(const SwPosition &rPos); + operator SwPosition() const; + const SwNodeIndex& GetPtNode() const { return maPtNode; }; + sal_Int32 GetPtContent() const { return mnPtContent; }; + }; + } + + auto FilterControlChars(OUString const& rString) -> OUString; +} + +class WW8FieldEntry +{ + private: + OUString msBookmarkName; + OUString msMarkType; + OUString msMarkCode; + ::sw::mark::IFieldmark::parameter_map_t maParams; + + public: + sw::hack::Position maStartPos; + sal_uInt16 mnFieldId; + sal_uLong mnObjLocFc; + WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) throw(); + WW8FieldEntry(const WW8FieldEntry &rOther) throw(); + WW8FieldEntry &operator=(const WW8FieldEntry &rOther) throw(); + void Swap(WW8FieldEntry &rOther) throw(); + + SwNodeIndex GetPtNode() const { return maStartPos.GetPtNode(); }; + sal_Int32 GetPtContent() const { return maStartPos.GetPtContent(); }; + + const OUString& GetBookmarkName() const { return msBookmarkName;} + const OUString& GetBookmarkCode() const { return msMarkCode;} + void SetBookmarkName(const OUString& bookmarkName); + void SetBookmarkType(const OUString& bookmarkType); + void SetBookmarkCode(const OUString& bookmarkCode); + ::sw::mark::IFieldmark::parameter_map_t& getParameters() { return maParams;} +}; + +// mini marker for some flags + +class WW8ReaderSave +{ +private: + WW8PLCFxSaveAll maPLCFxSave; + SwPosition maTmpPos; + std::deque<bool> maOldApos; + std::deque<WW8FieldEntry> maOldFieldStack; + std::unique_ptr<SwWW8FltControlStack> mxOldStck; + std::unique_ptr<SwWW8FltAnchorStack> mxOldAnchorStck; + std::unique_ptr<sw::util::RedlineStack> mxOldRedlines; + std::shared_ptr<WW8PLCFMan> mxOldPlcxMan; + std::unique_ptr<WW8FlyPara> mpWFlyPara; + std::unique_ptr<WW8SwFlyPara> mpSFlyPara; + SwPaM* mpPreviousNumPaM; + const SwNumRule* mpPrevNumRule; + std::unique_ptr<WW8TabDesc> mxTableDesc; + int mnInTable; + sal_uInt16 mnCurrentColl; + sal_Unicode mcSymbol; + bool mbIgnoreText; + bool mbSymbol; + bool mbHdFtFootnoteEdn; + bool mbTxbxFlySection; + bool mbAnl; + bool mbInHyperlink; + bool mbPgSecBreak; + bool mbWasParaEnd; + bool mbHasBorder; + bool mbFirstPara; +public: + WW8ReaderSave(SwWW8ImplReader* pRdr, WW8_CP nStart=-1); + void Restore(SwWW8ImplReader* pRdr); + const SwPosition &GetStartPos() const { return maTmpPos; } +}; + +enum class eF_ResT { OK, TEXT, TAGIGN, READ_FSPA }; + +class SwWW8Shade +{ +public: + Color aColor; + SwWW8Shade(bool bVer67, const WW8_SHD& rSHD); + SwWW8Shade(Color nFore, Color nBack, sal_uInt16 nIndex) + { + SetShade(nFore, nBack, nIndex); + } +private: + void SetShade(Color nFore, Color nBack, sal_uInt16 nIndex); +}; + +// Formulas + +enum SwWw8ControlType +{ + WW8_CT_EDIT, + WW8_CT_CHECKBOX, + WW8_CT_DROPDOWN +}; + +class WW8FormulaControl +{ +protected: + SwWW8ImplReader &mrRdr; + + WW8FormulaControl(WW8FormulaControl const&) = delete; + WW8FormulaControl& operator=(WW8FormulaControl const&) = delete; + +public: + WW8FormulaControl(const OUString& rN, SwWW8ImplReader &rRdr) + : mrRdr(rRdr), mfUnknown(0), mfDropdownIndex(0), + mfToolTip(0), mfNoMark(0), mfType(0), + mfUnused(0), mhpsCheckBox(20), mnChecked(0), mnMaxLen(0), + mbHelp(false), msName( rN ) + { + } + sal_uInt8 mfUnknown:2; + sal_uInt8 mfDropdownIndex:6; + sal_uInt8 mfToolTip:1; + sal_uInt8 mfNoMark:1; + sal_uInt8 mfType:3; + sal_uInt8 mfUnused:3; + + sal_uInt16 mhpsCheckBox; + sal_uInt16 mnChecked; + + /// FFData.cch in the spec: maximum length, in characters, of the value of the textbox. + sal_uInt16 mnMaxLen; + OUString msTitle; + OUString msDefault; + OUString msFormatting; + bool mbHelp; + OUString msHelp; + OUString msToolTip; + OUString msEntryMcr; + OUString msExitMcr; + std::vector<OUString> maListEntries; + virtual ~WW8FormulaControl() {} + void FormulaRead(SwWw8ControlType nWhich,SvStream *pD); + virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory, + css::uno::Reference< css::form::XFormComponent> &rFComp, + css::awt::Size &rSz) = 0; + OUString msName; +}; + +class WW8FormulaCheckBox : public WW8FormulaControl +{ +private: + WW8FormulaCheckBox(const WW8FormulaCheckBox&) = delete; + WW8FormulaCheckBox& operator=(const WW8FormulaCheckBox&) = delete; + +public: + explicit WW8FormulaCheckBox(SwWW8ImplReader &rR); + + virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory, + css::uno::Reference< css::form::XFormComponent> &rFComp, + css::awt::Size &rSz) override; +}; + +class WW8FormulaListBox : public WW8FormulaControl +{ +private: + WW8FormulaListBox(const WW8FormulaListBox&) = delete; + WW8FormulaListBox& operator=(const WW8FormulaListBox&) = delete; + +public: + explicit WW8FormulaListBox(SwWW8ImplReader &rR); + + virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> &rServiceFactory, + css::uno::Reference< css::form::XFormComponent> &rFComp, + css::awt::Size &rSz) override; +}; + +class WW8FormulaEditBox : public WW8FormulaControl +{ +private: + WW8FormulaEditBox(const WW8FormulaEditBox&) = delete; + WW8FormulaEditBox& operator=(const WW8FormulaEditBox&) = delete; +public: + explicit WW8FormulaEditBox(SwWW8ImplReader &rR); + //no real implementation, return false + virtual bool Import(const css::uno::Reference< css::lang::XMultiServiceFactory> & /* rServiceFactory */, + css::uno::Reference< css::form::XFormComponent> & /* rFComp */, + css::awt::Size & /* rSz */) override { return false; } +}; + +class SwMSConvertControls: public oox::ole::MSConvertOCXControls +{ +public: + SwMSConvertControls( SfxObjectShell const *pDSh, SwPaM *pP ); + void InsertFormula( WW8FormulaControl &rFormula); + virtual bool InsertControl(const css::uno::Reference< css::form::XFormComponent >& rFComp, + const css::awt::Size& rSize, + css::uno::Reference< css::drawing::XShape > *pShape, bool bFloatingCtrl) override; + void ExportControl(WW8Export &rWrt, const SdrUnoObj& rFormObj); + bool ReadOCXStream( tools::SvRef<SotStorage> const & rSrc1, + css::uno::Reference< css::drawing::XShape > *pShapeRef, + bool bFloatingCtrl=false ); +private: + SwPaM *pPaM; + sal_uInt32 mnObjectId; +}; + +class SwMSDffManager : public SvxMSDffManager +{ +private: + SwWW8ImplReader& rReader; + SvStream *pFallbackStream; + std::unordered_map<sal_uInt32, Graphic> aOldEscherBlipCache; + + virtual bool GetOLEStorageName( sal_uInt32 nOLEId, OUString& rStorageName, + tools::SvRef<SotStorage>& rSrcStorage, css::uno::Reference < css::embed::XStorage >& rDestStorage ) const override; + virtual bool ShapeHasText( sal_uLong nShapeId, sal_uLong nFilePos ) const override; + // #i32596# - new parameter <_nCalledByGroup>, which + // indicates, if the OLE object is imported inside a group object + virtual SdrObject* ImportOLE( sal_uInt32 nOLEId, + const Graphic& rGrf, + const tools::Rectangle& rBoundRect, + const tools::Rectangle& rVisArea, + const int _nCalledByGroup ) const override; + + SwMSDffManager(const SwMSDffManager&) = delete; + SwMSDffManager& operator=(const SwMSDffManager&) = delete; +public: + static sal_uInt32 GetFilterFlags(); + static sal_Int32 GetEscherLineMatch(MSO_LineStyle eStyle, MSO_SPT eShapeType, + sal_Int32 &rThick); + SwMSDffManager( SwWW8ImplReader& rRdr, bool bSkipImages ); + void DisableFallbackStream(); + void EnableFallbackStream(); +protected: + virtual SdrObject* ProcessObj( SvStream& rSt, DffObjData& rObjData, SvxMSDffClientData& rData, tools::Rectangle& rTextRect, SdrObject* pObj ) override; +}; + +class wwSection +{ +public: + explicit wwSection(const SwPosition &rPos); + SEPr maSep; + WW8_BRCVer9 brc[4]; + SwNodeIndex maStart; + SwSection *mpSection; + SwPageDesc *mpPage; + SvxFrameDirection meDir; + + sal_uInt32 nPgWidth; + sal_uInt32 nPgLeft; + sal_uInt32 nPgRight; + + css::drawing::TextVerticalAdjust mnVerticalAdjustment; + sal_uInt8 mnBorders; + bool mbHasFootnote; + void SetDirection(); + bool IsContinuous() const { return maSep.bkc == 0; } + bool IsNotProtected() const { return maSep.fUnlocked != 0; } + bool IsVertical() const; + sal_Int16 NoCols() const { return maSep.ccolM1 + 1; } + sal_Int32 StandardColSeparation() const { return maSep.dxaColumns; } + bool HasTitlePage() const { return maSep.fTitlePage != 0; } + sal_uInt16 PageStartAt() const { return maSep.pgnStart; } + bool PageRestartNo() const { return maSep.fPgnRestart != 0; } + bool IsBiDi() const { return maSep.fBiDi != 0; } + sal_uInt32 GetPageWidth() const { return nPgWidth; } + sal_uInt32 GetTextAreaWidth() const + { return GetPageWidth() - GetPageLeft() - GetPageRight(); } + sal_uInt32 GetPageHeight() const { return maSep.yaPage; } + sal_uInt32 GetPageLeft() const { return nPgLeft; } + sal_uInt32 GetPageRight() const { return nPgRight; } + bool IsLandScape() const { return maSep.dmOrientPage != 0; } + bool IsFixedHeightHeader() const { return maSep.dyaTop < 0; } + bool IsFixedHeightFooter() const { return maSep.dyaBottom < 0; } +}; + +class wwSectionManager +{ +private: + /* + A queue of the ms sections in the document + */ + SwWW8ImplReader& mrReader; + std::deque<wwSection> maSegments; + typedef std::deque<wwSection>::iterator mySegIter; + + //Num of page desc's entered into the document + sal_uInt16 mnDesc; + + struct wwULSpaceData + { + bool bHasHeader, bHasFooter; + sal_uInt32 nSwHLo, nSwFUp, nSwUp, nSwLo; + wwULSpaceData() + : bHasHeader(false) + , bHasFooter(false) + , nSwHLo(0) + , nSwFUp(0) + , nSwUp(0) + , nSwLo(0) + {} + }; + + void SetSegmentToPageDesc(const wwSection &rSection, bool bIgnoreCols); + + void GetPageULData(const wwSection &rNewSection, + wwULSpaceData& rData) const; + static void SetPageULSpaceItems(SwFrameFormat &rFormat, wwULSpaceData const & rData, + const wwSection &rSection); + + static void SetPage(SwPageDesc &rPageDesc, SwFrameFormat &rFormat, + const wwSection &rSection, bool bIgnoreCols); + + static void SetNumberingType(const wwSection &rNewSection, SwPageDesc &rPageDesc); + + void SetUseOn(wwSection &rSection); + void SetHdFt(wwSection const &rSection, int nSect, const wwSection *pPrevious); + + SwSectionFormat *InsertSection(SwPaM const & rMyPaM, wwSection &rSection); + static bool SetCols(SwFrameFormat &rFormat, const wwSection &rSection, + sal_uInt32 nNetWidth); + bool SectionIsProtected(const wwSection &rSection) const; + void SetLeftRight(wwSection &rSection); + /* + The segment we're inserting, the start of the segments container, and the + nodeindex of where we want the page break to (normally the segments start + position + */ + SwFormatPageDesc SetSwFormatPageDesc(mySegIter const &rIter, mySegIter const &rStart, + bool bIgnoreCols); + + wwSectionManager(const wwSectionManager&) = delete; + wwSectionManager& operator=(const wwSectionManager&) = delete; +public: + explicit wwSectionManager(SwWW8ImplReader &rReader) : mrReader(rReader), mnDesc(0) + {} + void SetCurrentSectionHasFootnote(); + void SetCurrentSectionVerticalAdjustment(const css::drawing::TextVerticalAdjust nVA); + bool CurrentSectionIsVertical() const; + bool CurrentSectionIsProtected() const; + void PrependedInlineNode(const SwPosition &rPos, const SwNode &rNode); + sal_uInt16 CurrentSectionColCount() const; + bool WillHavePageDescHere(const SwNodeIndex& rIdx) const; + void CreateSep(const long nTextPos); + void InsertSegments(); + void JoinNode(const SwPosition &rPos, const SwNode &rNode); + sal_uInt32 GetPageLeft() const; + sal_uInt32 GetPageRight() const; + sal_uInt32 GetPageWidth() const; + sal_uInt32 GetWWPageTopMargin() const; + sal_uInt32 GetTextAreaWidth() const; +}; + +//Various writer elements like frames start off containing a blank paragraph, +//sometimes this paragraph turns out to be extraneous, e.g. the frame should +//only contain a table with no trailing paragraph. + +//We want to remove these extra paragraphs, but removing them during the parse +//is problematic, because we don't want to remove any paragraphs that are still +//addressed by property entries in a SwFltControlStack which have not yet been +//committed to the document. + +//Safest thing is to not delete SwTextNodes from a document during import, and +//remove these extraneous paragraphs at the end after all SwFltControlStack are +//destroyed. +class wwExtraneousParas +{ +private: + /* + A vector of SwTextNodes to erase from a document after import is complete + */ + std::set<SwTextNode*, SwWW8::ltnode> m_aTextNodes; + SwDoc& m_rDoc; + + wwExtraneousParas(wwExtraneousParas const&) = delete; + wwExtraneousParas& operator=(wwExtraneousParas const&) = delete; + +public: + explicit wwExtraneousParas(SwDoc &rDoc) : m_rDoc(rDoc) {} + ~wwExtraneousParas() { delete_all_from_doc(); } + void insert(SwTextNode *pTextNode) { m_aTextNodes.insert(pTextNode); } + void delete_all_from_doc(); +}; + +class wwFrameNamer +{ +private: + OUString msSeed; + sal_Int32 mnImportedGraphicsCount; + bool mbIsDisabled; + + wwFrameNamer(wwFrameNamer const&) = delete; + wwFrameNamer& operator=(wwFrameNamer const&) = delete; + +public: + void SetUniqueGraphName(SwFrameFormat *pFrameFormat, const OUString &rFixedPart); + wwFrameNamer(bool bIsDisabled, const OUString &rSeed) + : msSeed(rSeed), mnImportedGraphicsCount(0), mbIsDisabled(bIsDisabled) + { + } +}; + +class wwSectionNamer +{ +private: + const SwDoc &mrDoc; + OUString msFileLinkSeed; + int mnFileSectionNo; + wwSectionNamer(const wwSectionNamer&) = delete; + wwSectionNamer& operator=(const wwSectionNamer&) = delete; +public: + OUString UniqueName(); + wwSectionNamer(const SwDoc &rDoc, const OUString &rSeed) + : mrDoc(rDoc), msFileLinkSeed(rSeed), mnFileSectionNo(0) + { } +}; + +class FootnoteDescriptor +{ +public: + ManTypes meType; + bool mbAutoNum; + WW8_CP mnStartCp; + WW8_CP mnLen; +}; + +struct ApoTestResults +{ + bool mbStartApo; + bool mbStopApo; + bool m_bHasSprm37; + bool m_bHasSprm29; + sal_uInt8 m_nSprm29; + WW8FlyPara* mpStyleApo; + ApoTestResults() + : mbStartApo(false), mbStopApo(false), m_bHasSprm37(false) + , m_bHasSprm29(false), m_nSprm29(0), mpStyleApo(nullptr) {} + bool HasStartStop() const { return (mbStartApo || mbStopApo); } + bool HasFrame() const { return (m_bHasSprm29 || m_bHasSprm37 || mpStyleApo); } +}; + +struct ANLDRuleMap +{ + OUString msOutlineNumRule; // WinWord 6 numbering, variant 1 + OUString msNumberingNumRule; // WinWord 6 numbering, variant 2 + SwNumRule* GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType); + void SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType); +}; + +struct SprmReadInfo; +class SwDocShell; +struct WW8PostProcessAttrsInfo +{ + bool mbCopy; + WW8_CP mnCpStart; + WW8_CP mnCpEnd; + SwPaM mPaM; + SfxItemSet mItemSet; + + WW8PostProcessAttrsInfo(WW8_CP nCpStart, WW8_CP nCpEnd, SwPaM & rPaM); +}; + +#define MAX_COL 64 // WW6-description: 32, WW6-UI: 31 & WW8-UI: 63! + +struct WW8TabBandDesc +{ + WW8TabBandDesc* pNextBand; + short nGapHalf; + short mnDefaultLeft; + short mnDefaultTop; + short mnDefaultRight; + short mnDefaultBottom; + bool mbHasSpacing; + short nLineHeight; + short nRows; + sal_uInt16 maDirections[MAX_COL + 1]; + short nCenter[MAX_COL + 1]; // X-edge of all cells of this band + short nWidth[MAX_COL + 1]; // length of all cells of this band + short nWwCols; // sal_uInt8 would be sufficient, alignment -> short + short nSwCols; // SW: number of columns for the writer + bool bLEmptyCol; // SW: an additional empty column at the left + bool bREmptyCol; // SW: same at the right + bool bCantSplit; + bool bCantSplit90; + WW8_TCell* pTCs; + sal_uInt8 nOverrideSpacing[MAX_COL + 1]; + short nOverrideValues[MAX_COL + 1][4]; + WW8_SHD* pSHDs; + Color* pNewSHDs; + WW8_BRCVer9 aDefBrcs[6]; + + bool bExist[MAX_COL]; // does this cell exist ? + sal_uInt8 nTransCell[MAX_COL + 2]; // translation WW-Index -> SW-Index + + sal_uInt8 transCell(sal_uInt8 nWwCol) const + { + return nWwCol < SAL_N_ELEMENTS(nTransCell) ? nTransCell[nWwCol] : 0xFF; + } + + WW8TabBandDesc(); + WW8TabBandDesc(WW8TabBandDesc const & rBand); // deep copy + ~WW8TabBandDesc(); + void ReadDef(bool bVer67, const sal_uInt8* pS, short nLen); + void ProcessDirection(const sal_uInt8* pParams); + void ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen); + void ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen); + void ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol); + void ProcessSprmTDelete(const sal_uInt8* pParamsTDelete); + void ProcessSprmTInsert(const sal_uInt8* pParamsTInsert); + void ProcessSpacing(const sal_uInt8* pParamsTInsert); + void ProcessSpecificSpacing(const sal_uInt8* pParamsTInsert); + void ReadShd(const sal_uInt8* pS ); + void ReadNewShd(const sal_uInt8* pS, bool bVer67); + + enum wwDIR {wwTOP = 0, wwLEFT = 1, wwBOTTOM = 2, wwRIGHT = 3}; + +private: + WW8TabBandDesc & operator =(WW8TabBandDesc const &) = default; // only for use in copy ctor +}; + +// Storage-Reader + +typedef std::set<WW8_CP> cp_set; + +class SwWW8ImplReader +{ +private: + SwDocShell *m_pDocShell; // The Real DocShell + +friend class WW8RStyle; +friend class WW8TabDesc; +friend class WW8ReaderSave; +friend struct WW8FlyPara; +friend struct WW8SwFlyPara; +friend class WW8FlySet; +friend class SwMSDffManager; +friend class SwWW8FltControlStack; +friend class WW8FormulaControl; +friend class wwSectionManager; + +private: + + SotStorage* m_pStg; // Input-Storage + SvStream* m_pStrm; // Input-(Storage)Stream + SvStream* m_pTableStream; // Input-(Storage)Stream + SvStream* m_pDataStream; // Input-(Storage)Stream + +// general stuff + SwDoc& m_rDoc; + std::shared_ptr<SwUnoCursor> mpCursor; + SwPaM* m_pPaM; + + std::unique_ptr<SwWW8FltControlStack> m_xCtrlStck; // stack for the attributes + + /* + This stack is for redlines, because their sequence of discovery can + be out of order of their order of insertion into the document. + */ + std::stack<std::unique_ptr<sw::util::RedlineStack>> m_aFrameRedlines; //inside frames, tables, etc + std::unique_ptr<sw::util::RedlineStack> m_xRedlineStack; //main document + + /* + This stack is for fields that get referenced later, e.g. BookMarks and TOX. + They get inserted at the end of the document, it is the same stack for + headers/footers/main text/textboxes/tables etc... + */ + std::unique_ptr<SwWW8ReferencedFltEndStack> m_xReffedStck; + + /* + This stack is for fields whose true conversion cannot be determined until + the end of the document, it is the same stack for headers/footers/main + text/textboxes/tables etc... They are things that reference other things + e.g. NoteRef and Ref, they are processed after m_xReffedStck + */ + std::unique_ptr<SwWW8FltRefStack> m_xReffingStck; + + /* + For graphics anchors. Determines the graphics whose anchors are in the + current paragraph, and works around the difficulty in inserting a graphic + anchored to character before a character to be anchored to has been + inserted. Is emptied at the end of each paragraph. + */ + std::unique_ptr<SwWW8FltAnchorStack> m_xAnchorStck; + + /* + A stack of fields identifiers to keep track of any open fields that need + to be closed. Generally word fields are inserted as writer fields as soon + as they are encountered, and so their end point is normally unimportant. + But hyperlink fields need to be applied as attributes to text and it is + far easier and safer to set the end point of an attribute when we + encounter the end marker of the field instead of calculating in advance + where the end point will fall, to do so fully correctly duplicates the + main logic of the filter itself. + */ + std::deque<WW8FieldEntry> m_aFieldStack; + + /* + A stack of open footnotes. Should only be one in it at any time. + */ + std::deque<FootnoteDescriptor> m_aFootnoteStack; + + /* + A queue of the ms sections in the document + */ + wwSectionManager m_aSectionManager; + + /* + A vector of surplus-to-requirements paragraph in the final document, + that exist because of quirks of the SwDoc document model and/or API, + which need to be removed. + */ + wwExtraneousParas m_aExtraneousParas; + + /* + A map of tables to their follow nodes for use in inserting tables into + already existing document, i.e. insert file + */ + sw::util::InsertedTablesManager m_aInsertedTables; + + /* + Creates unique names to give to (file link) sections (WW1/WW2/...) + */ + wwSectionNamer m_aSectionNameGenerator; + + /* + Knows how to split a series of bytes into sprms and their arguments + */ + std::unique_ptr<wwSprmParser> m_xSprmParser; + + /* + Creates unique names to give to graphics + */ + wwFrameNamer m_aGrfNameGenerator; + + /* + Knows which writer style a given word style should be imported as. + */ + sw::util::ParaStyleMapper m_aParaStyleMapper; + sw::util::CharStyleMapper m_aCharStyleMapper; + + /* + Stack of textencoding being used as we progress through the document text + */ + std::stack<rtl_TextEncoding> m_aFontSrcCharSets; + std::stack<rtl_TextEncoding> m_aFontSrcCJKCharSets; + + /* + Progress bar + */ + std::unique_ptr<ImportProgress> m_xProgress; + + std::unique_ptr<SwMSConvertControls> m_xFormImpl; // implementation of control + + SwFlyFrameFormat* m_pFlyFormatOfJustInsertedGraphic; + std::unique_ptr<FrameDeleteWatch> m_xFormatOfJustInsertedApo; + SwPaM* m_pPreviousNumPaM; + const SwNumRule* m_pPrevNumRule; + + //Keep track of APO environments + std::deque<bool> m_aApos; + /* + Keep track of generated Ruby character formats we can minimize the + number of character formats created + */ + std::vector<const SwCharFormat*> m_aRubyCharFormats; + + /* + For fuzzing keep track of source offset of inserted graphics + */ + std::set<sal_uLong> m_aGrafPosSet; + + std::unique_ptr<WW8PostProcessAttrsInfo> m_pPostProcessAttrsInfo; + + std::shared_ptr<WW8Fib> m_xWwFib; + std::unique_ptr<WW8Fonts> m_xFonts; + std::unique_ptr<WW8Dop> m_xWDop; + std::unique_ptr<WW8ListManager, o3tl::default_delete<WW8ListManager>> m_xLstManager; + std::unique_ptr<WW8ScannerBase> m_xSBase; + std::shared_ptr<WW8PLCFMan> m_xPlcxMan; + std::map<short, OUString> m_aLinkStringMap; + + o3tl::sorted_vector<const SwNode*> m_aTextNodesHavingFirstLineOfstSet; // #i103711# + o3tl::sorted_vector<const SwNode*> m_aTextNodesHavingLeftIndentSet; // #i105414# + + std::unique_ptr<WW8RStyle> m_xStyles; // pointer to the style reading class + SwFormat* m_pCurrentColl; // collection to be created now + // ( always 0 outside of a Style-Def ) + std::unique_ptr<SfxItemSet> m_xCurrentItemSet;// character attributes to be read in now + // (always 0 outside of the WW8ListManager Ctor) + std::vector<SwWW8StyInf> m_vColl; + const SwTextFormatColl* m_pDfltTextFormatColl; // Default + SwFormat* m_pStandardFormatColl;// "Standard" + + std::unique_ptr<WW8PLCF_HdFt> m_xHdFt; // pointer to Header / Footer - scanner class + + std::unique_ptr<WW8FlyPara> m_xWFlyPara; // WW-parameter + std::unique_ptr<WW8SwFlyPara> m_xSFlyPara; // Sw parameters created from previous + + std::unique_ptr<WW8TabDesc> m_xTableDesc; // description of table properties + //Keep track of tables within tables + std::stack<std::unique_ptr<WW8TabDesc>> m_aTableStack; + + ANLDRuleMap m_aANLDRules; + std::unique_ptr<WW8_OLST> m_xNumOlst; // position in text + + SdrModel* m_pDrawModel; + SdrPage* m_pDrawPg; + std::unique_ptr<EditEngine> m_pDrawEditEngine; + std::unique_ptr<wwZOrderer> m_xWWZOrder; + + SwFieldType* m_pNumFieldType; // for number circle + + std::unique_ptr<SwMSDffManager> m_xMSDffManager; + + std::unique_ptr<std::vector<OUString>> m_pAtnNames; + + std::unique_ptr<WW8SmartTagData> m_pSmartTagData; + + sw::util::AuthorInfos m_aAuthorInfos; + OUString m_sBaseURL; + + // Ini-Flags: + sal_uInt32 m_nIniFlags; // flags from writer.ini + sal_uInt32 m_nIniFlags1; // dito ( additional flags ) + sal_uInt32 m_nFieldFlags; // dito for fields + sal_uInt32 m_nFieldTagAlways[3]; // dito for tagging of fields + sal_uInt32 m_nFieldTagBad[3]; // dito for tagging of fields that can't be imported + bool m_bRegardHindiDigits; // import digits in CTL scripts as Hindi numbers + + bool m_bDrawCpOValid; + WW8_CP m_nDrawCpO; // start of Txbx-SubDocs + + sal_uLong m_nPicLocFc; // Picture Location in File (FC) + sal_uLong m_nObjLocFc; // Object Location in File (FC) + + sal_Int32 m_nIniFlyDx; // X-offset of Flys + sal_Int32 m_nIniFlyDy; // Y-offset of Flys + + rtl_TextEncoding m_eTextCharSet; // Default charset for Text + rtl_TextEncoding m_eStructCharSet; // rtl_TextEncoding for structures + rtl_TextEncoding m_eHardCharSet; // Hard rtl_TextEncoding-Attribute + sal_uInt16 m_nProgress; // percentage for Progressbar + sal_uInt16 m_nCurrentColl; // per WW-count + sal_uInt16 m_nFieldNum; // serial number for that + sal_uInt16 m_nLFOPosition; + + short m_nCharFormat; // per WW-count, <0 for none + + short m_nDrawXOfs, m_nDrawYOfs; + short m_nDrawXOfs2, m_nDrawYOfs2; + + sal_Unicode m_cSymbol; // symbol to be read now + + sal_uInt8 m_nWantedVersion; // originally requested WW-Doc version by Writer + + sal_uInt8 m_nSwNumLevel; // level number for outline / enumeration + sal_uInt8 m_nWwNumType; // outline / number / enumeration + sal_uInt8 m_nListLevel; + + bool m_bNewDoc; // new document? + bool m_bSkipImages; // skip images for text extraction/indexing + bool m_bReadNoTable; // no tables + bool m_bPgSecBreak; // Page- or Sectionbreak is still to be added + bool m_bSpec; // special char follows in text + bool m_bObj; // Obj in Text + bool m_bTxbxFlySection; // FlyFrame that was inserted as replacement for Winword Textbox + bool m_bHasBorder; // for bundling of the borders + bool m_bSymbol; // e.g. Symbol instead of Times + bool m_bIgnoreText; // e.g. for FieldVanish + int m_nInTable; // are we currently reading a table? + bool m_bWasTabRowEnd; // table : Row End Mark + bool m_bWasTabCellEnd; // table: Cell End Mark + + bool m_bAnl; // enumeration in work + // Anl stands for "Autonumber level" + + bool m_bHdFtFootnoteEdn; // special text: header/footer/and so on + bool m_bFootnoteEdn; // footnote or endnote + bool m_bIsHeader; // text is read from header ( line height ) + bool m_bIsFooter; // text is read from footer ( line height ) + + bool m_bIsUnicode; // current piece of text is encoded as 2-byte Unicode + // please do NOT handle this as bit field! + + bool m_bCpxStyle; // style in the complex part + bool m_bStyNormal; // style with Id 0 is currently read + bool m_bWWBugNormal; // WW-Version with Bug Dya in Style Normal + bool m_bNoAttrImport; // ignore attributes for ignoring styles + bool m_bInHyperlink; // special case for reading 0x01 + // see also: SwWW8ImplReader::Read_F_Hyperlink() + bool m_bWasParaEnd; + + // useful helper variables + bool m_bVer67; // ( (6 == nVersion) || (7 == nVersion) ); + bool m_bVer6; // (6 == nVersion); + bool m_bVer7; // (7 == nVersion); + bool m_bVer8; // (8 == nVersion); + + bool m_bEmbeddObj; // EmbeddField is being read + + bool m_bCurrentAND_fNumberAcross; // current active Annotated List Descriptor - ROW flag + + bool m_bNoLnNumYet; // no Line Numbering has been activated yet (we import + // the very 1st Line Numbering and ignore the rest) + + bool m_bFirstPara; // first paragraph? + bool m_bFirstParaOfPage; + bool m_bParaAutoBefore; + bool m_bParaAutoAfter; + + bool m_bDropCap; + sal_Int32 m_nDropCap; + + bool m_bBidi; + bool m_bReadTable; + // Indicate that currently on loading a TOC, managed by Read_F_TOX() and End_Field() + bool m_bLoadingTOXCache; + int m_nEmbeddedTOXLevel; + // Indicate that current on loading a hyperlink, which is inside a TOC; Managed by Read_F_Hyperlink() and End_Field() + bool m_bLoadingTOXHyperlink; + // a document position recorded the after-position of TOC section, managed by Read_F_TOX() and End_Field() + std::unique_ptr<SwPaM> m_pPosAfterTOC; + // used for some dropcap tweaking + SwTextNode* m_pPreviousNode; + + std::unique_ptr< SwPosition > m_pLastAnchorPos; + + bool m_bCareFirstParaEndInToc; + bool m_bCareLastParaEndInToc; + cp_set m_aTOXEndCps; + + std::vector<WW8_CP> m_aEndParaPos; + WW8_CP m_aCurrAttrCP; + bool m_bOnLoadingMain:1; + bool m_bNotifyMacroEventRead:1; + + const SprmReadInfo& GetSprmReadInfo(sal_uInt16 nId) const; + + bool StyleExists(unsigned int nColl) const { return (nColl < m_vColl.size()); } + SwWW8StyInf *GetStyle(sal_uInt16 nColl) const; + void AppendTextNode(SwPosition& rPos); + + void Read_HdFt(int nSect, const SwPageDesc *pPrev, + const wwSection &rSection); + void Read_HdFtText(WW8_CP nStartCp, WW8_CP nLen, SwFrameFormat const * pHdFtFormat); + void Read_HdFtTextAsHackedFrame(WW8_CP nStart, WW8_CP nLen, + SwFrameFormat const &rHdFtFormat, sal_uInt16 nPageWidth); + + bool isValid_HdFt_CP(WW8_CP nHeaderCP) const; + + bool HasOwnHeaderFooter(sal_uInt8 nWhichItems, sal_uInt8 grpfIhdt, int nSect); + + void HandleLineNumbering(const wwSection &rSection); + + void CopyPageDescHdFt( const SwPageDesc* pOrgPageDesc, + SwPageDesc* pNewPageDesc, sal_uInt8 nCode ); + + void DeleteStack(std::unique_ptr<SwFltControlStack> prStck); + void DeleteCtrlStack() + { + DeleteStack(std::move(m_xCtrlStck)); + } + void DeleteRefStacks() + { + DeleteStack(std::move(m_xReffedStck)); + DeleteStack(std::move(m_xReffingStck)); + } + void DeleteAnchorStack() + { + DeleteStack(std::move(m_xAnchorStck)); + } + void emulateMSWordAddTextToParagraph(const OUString& rAddString); + void simpleAddTextToParagraph(const OUString& rAddString); + bool HandlePageBreakChar(); + bool ReadChar(long nPosCp, long nCpOfs); + bool ReadPlainChars(WW8_CP& rPos, sal_Int32 nEnd, sal_Int32 nCpOfs); + bool ReadChars(WW8_CP& rPos, WW8_CP nNextAttr, long nTextEnd, long nCpOfs); + static bool LangUsesHindiNumbers(LanguageType nLang); + static sal_Unicode TranslateToHindiNumbers(sal_Unicode); + + void SetDocumentGrid(SwFrameFormat &rFormat, const wwSection &rSection); + + void ProcessCurrentCollChange(WW8PLCFManResult& rRes, bool* pStartAttr, + bool bCallProcessSpecial); + long ReadTextAttr(WW8_CP& rTextPos, long nTextEnd, bool& rbStartLine, int nDepthGuard = 0); + void ReadAttrs(WW8_CP& rTextPos, WW8_CP& rNext, long nTextEnd, bool& rbStartLine); + void CloseAttrEnds(); + bool ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType); + + void ReadRevMarkAuthorStrTabl( SvStream& rStrm, sal_Int32 nTablePos, + sal_Int32 nTableSiz, SwDoc& rDoc ); + + void Read_HdFtFootnoteText(const SwNodeIndex* pSttIdx, WW8_CP nStartCp, + WW8_CP nLen, ManTypes nType); + + void ImportTox( int nFieldId, const OUString& aStr ); + + void EndSprm( sal_uInt16 nId ); + // #i103711# + // #i105414# + void NewAttr( const SfxPoolItem& rAttr, + const bool bFirstLineOfStSet = false, + const bool bLeftIndentSet = false ); + + bool GetFontParams(sal_uInt16, FontFamily&, OUString&, FontPitch&, + rtl_TextEncoding&); + bool SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums, sal_uInt16 nWhich); + void ResetCharSetVars(); + void ResetCJKCharSetVars(); + + const SfxPoolItem* GetFormatAttr( sal_uInt16 nWhich ); + bool JoinNode(SwPaM &rPam, bool bStealAttr = false); + + static bool IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn = false); + + //Set closest writer border equivalent into rBox from pbrc, optionally + //recording true winword dimensions in pSizeArray. nSetBorders to mark a + //border which has been previously set to a value and for which becoming + //empty is valid. Set bCheBtwn to work with paragraphs that have a special + //between paragraphs border + + // Note #i20672# we can't properly support between lines so best to ignore + // them for now + static bool SetBorder(SvxBoxItem& rBox, const WW8_BRCVer9* pbrc, + short *pSizeArray=nullptr, sal_uInt8 nSetBorders=0xFF); + static void GetBorderDistance(const WW8_BRCVer9* pbrc, tools::Rectangle& rInnerDist); + static sal_uInt16 GetParagraphAutoSpace(bool fDontUseHTMLAutoSpacing); + static bool SetShadow(SvxShadowItem& rShadow, const short *pSizeArray, + const WW8_BRCVer9& aRightBrc); + //returns true is a shadow was set + static bool SetFlyBordersShadow(SfxItemSet& rFlySet, const WW8_BRCVer9 *pbrc, + short *SizeArray); + static void SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSection); + + static sal_Int32 MatchSdrBoxIntoFlyBoxItem( const Color& rLineColor, + MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, sal_Int32 &rLineWidth, + SvxBoxItem& rBox ); + void MatchSdrItemsIntoFlySet( SdrObject const * pSdrObj, SfxItemSet &aFlySet, + MSO_LineStyle eLineStyle, MSO_LineDashing eDashing, MSO_SPT eShapeType, tools::Rectangle &rInnerDist ); + static void AdjustLRWrapForWordMargins(const SvxMSDffImportRec &rRecord, + SvxLRSpaceItem &rLR); + static void AdjustULWrapForWordMargins(const SvxMSDffImportRec &rRecord, + SvxULSpaceItem &rUL); + static void MapWrapIntoFlyFormat(SvxMSDffImportRec const * pRecord, SwFrameFormat* pFlyFormat); + + void SetAttributesAtGrfNode(SvxMSDffImportRec const* pRecord, + SwFrameFormat const *pFlyFormat, WW8_FSPA const *pF); + + bool IsDropCap() const; + bool IsListOrDropcap() const { return (!m_xCurrentItemSet || m_bDropCap); }; + + //Apo == Absolutely Positioned Object, MSWord's old-style frames + std::unique_ptr<WW8FlyPara> ConstructApo(const ApoTestResults &rApo, + const WW8_TablePos *pTabPos); + bool StartApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos); + void StopApo(); + bool TestSameApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos); + ApoTestResults TestApo(int nCellLevel, bool bTableRowEnd, + const WW8_TablePos *pTabPos); + static void StripNegativeAfterIndent(SwFrameFormat const *pFlyFormat); + + void EndSpecial(); + bool ProcessSpecial(bool &rbReSync, WW8_CP nStartCp); + sal_uInt16 TabRowSprm(int nLevel) const; + + bool ReadGrafFile(OUString& rFileName, std::unique_ptr<Graphic>& rpGraphic, + const WW8_PIC& rPic, SvStream* pSt, sal_uLong nFilePos, bool* pDelIt); + + static void ReplaceObj(const SdrObject &rReplaceTextObj, + SdrObject &rSubObj); + + SwFlyFrameFormat* MakeGrafNotInContent(const WW8PicDesc& rPD, + const Graphic* pGraph, const OUString& rFileName, + const SfxItemSet& rGrfSet); + + SwFrameFormat* MakeGrafInContent(const WW8_PIC& rPic, const WW8PicDesc& rPD, + const Graphic* pGraph, const OUString& rFileName, + const SfxItemSet& rGrfSet); + + SwFrameFormat *AddAutoAnchor(SwFrameFormat *pFormat); + SwFrameFormat* ImportGraf1(WW8_PIC const & rPic, SvStream* pSt, sal_uLong nFilePos); + SwFrameFormat* ImportGraf(SdrTextObj const * pTextObj = nullptr, SwFrameFormat const * pFlyFormat = nullptr); + + SdrObject* ImportOleBase( Graphic& rGraph, const Graphic* pGrf=nullptr, + const SfxItemSet* pFlySet=nullptr, const tools::Rectangle& aVisArea = tools::Rectangle() ); + + SwFrameFormat* ImportOle( const Graphic* = nullptr, const SfxItemSet* pFlySet = nullptr, + const SfxItemSet* pGrfSet = nullptr, const tools::Rectangle& aVisArea = tools::Rectangle() ); + SwFlyFrameFormat* InsertOle(SdrOle2Obj &rObject, const SfxItemSet &rFlySet, + const SfxItemSet *rGrfSet); + + bool ImportFormulaControl(WW8FormulaControl &rBox,WW8_CP nStart, + SwWw8ControlType nWhich); + + void ImportDop(); + + //This converts MS Asian Typography information into OOo's + void ImportDopTypography(const WW8DopTypography &rTypo); + + ErrCode LoadThroughDecryption(WW8Glossary *pGloss); + ErrCode SetSubStreams(tools::SvRef<SotStorageStream> &rTableStream, tools::SvRef<SotStorageStream> &rDataStream); + ErrCode CoreLoad(WW8Glossary const *pGloss); + + void ReadDocVars(); + + bool StartTable(WW8_CP nStartCp); + bool InEqualApo(int nLvl) const; + bool InLocalApo() const { return InEqualApo(m_nInTable); } + bool InEqualOrHigherApo(int nLvl) const; + void TabCellEnd(); + void StopTable(); + bool IsInvalidOrToBeMergedTabCell() const; + +// Enumerations / lists ( Autonumbered List Data Descriptor ) +// list: ANLD ( Autonumbered List Data Descriptor ) +// one level: ANLV ( Autonumber Level Descriptor ) + +// Chg7-8: +// lists are separate structures in WW8 that are handled via the following three tables: +// rglst, hpllfo and hsttbListNames +// the corresponding structures are: LSTF, LVLF, LFO LFOLVL + + void SetAnlvStrings(SwNumFormat &rNum, WW8_ANLV const &rAV, const sal_uInt8* pText, + size_t nStart, size_t nElements, bool bOutline); + void SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel, bool bOutLine); + void SetNumOlst( SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel ); + SwNumRule* GetStyRule(); + + void StartAnl(const sal_uInt8* pSprm13); + void NextAnlLine(const sal_uInt8* pSprm13); + void StopAllAnl(bool bGoBack = true); + void StopAnlToRestart(sal_uInt8 nType, bool bGoBack = true); + +// graphics layer + + bool ReadGrafStart(void* pData, short nDataSiz, WW8_DPHEAD const * pHd, + SfxAllItemSet &rSet); + SdrObject *ReadLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadRect(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadEllipse(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadArc(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadPolyLine(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + void InsertTxbxStyAttrs( SfxItemSet& rS, sal_uInt16 nColl ); + void InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType, bool bONLYnPicLocFc=false); + + bool GetTxbxTextSttEndCp(WW8_CP& rStartCp, WW8_CP& rEndCp, sal_uInt16 nTxBxS, + sal_uInt16 nSequence); + sal_Int32 GetRangeAsDrawingString(OUString& rString, long StartCp, long nEndCp, ManTypes eType); + std::unique_ptr<OutlinerParaObject> ImportAsOutliner(OUString &rString, WW8_CP nStartCp, WW8_CP nEndCp, ManTypes eType); + void InsertTxbxText(SdrTextObj* pTextObj, Size const * pObjSiz, + sal_uInt16 nTxBxS, sal_uInt16 nSequence, long nPosCp, SwFrameFormat const * pFlyFormat, + bool bMakeSdrGrafObj, bool& rbEraseTextObj, + bool* pbTestTxbxContainsText = nullptr, long* pnStartCp = nullptr, + long* pnEndCp = nullptr, bool* pbContainsGraphics = nullptr, + SvxMSDffImportRec const * pRecord = nullptr); + bool TxbxChainContainsRealText( sal_uInt16 nTxBxS, + long& rStartCp, + long& rEndCp ); + SdrObject *ReadTextBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadCaptionBox(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadGroup(WW8_DPHEAD const * pHd, SfxAllItemSet &rSet); + SdrObject *ReadGrafPrimitive(short& rLeft, SfxAllItemSet &rSet); + void ReadGrafLayer1( WW8PLCFspecial* pPF, long nGrafAnchorCp ); + SdrObject* CreateContactObject(SwFrameFormat* pFlyFormat); + RndStdIds ProcessEscherAlign(SvxMSDffImportRec* pRecord, WW8_FSPA *pFSPA, + SfxItemSet &rFlySet); + bool MiserableRTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth, + sal_Int16 eHoriOri, sal_Int16 eHoriRel); + SwFrameFormat* Read_GrafLayer( long nGrafAnchorCp ); + SwFlyFrameFormat* ImportReplaceableDrawables( SdrObject* &rpObject, + SdrObject* &rpOurNewObject, SvxMSDffImportRec* pRecord, WW8_FSPA *pF, + SfxItemSet &rFlySet ); + SwFlyFrameFormat *ConvertDrawTextToFly( SdrObject* &rpObject, + SdrObject* &rpOurNewObject, SvxMSDffImportRec const * pRecord, + RndStdIds eAnchor, WW8_FSPA const *pF, SfxItemSet &rFlySet ); + SwFrameFormat* MungeTextIntoDrawBox(SvxMSDffImportRec *pRecord, + long nGrafAnchorCp, SwFrameFormat *pRetFrameFormat); + + void GrafikCtor(); + void GrafikDtor(); + +// other stuff + OUString GetFieldResult( WW8FieldDesc const * pF ); + void MakeTagString( OUString& rStr, const OUString& rOrg ); + void UpdateFields(); + OUString ConvertFFileName(const OUString& rRaw); + WW8_CP Read_F_Tag(WW8FieldDesc* pF); + void InsertTagField( const sal_uInt16 nId, const OUString& rTagText ); + long ImportExtSprm(WW8PLCFManResult* pRes); + void EndExtSprm(sal_uInt16 nSprmId); + void ReadDocInfo(); + +// Ver8 lists + + void RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel, + const bool bSetAttr = true); + + void RegisterNumFormatOnStyle(sal_uInt16 nStyle); + void SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO, + sal_uInt8 nCurrentLevel); + void RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel); + +// to be replaced by calls in the appropriate extended SvxMSDffManager + + const OUString* GetAnnotationAuthor(sal_uInt16 nIdx); + + void GetSmartTagInfo(SwFltRDFMark& rMark); + + // interfaces for the toggle attributes + void SetToggleAttr(sal_uInt8 nAttrId, bool bOn); + void SetToggleBiDiAttr(sal_uInt8 nAttrId, bool bOn); + void ChkToggleAttr_( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask ); + + void ChkToggleAttr( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask ) + { + if( nOldStyle81Mask != nNewStyle81Mask && + m_xCtrlStck->GetToggleAttrFlags() ) + ChkToggleAttr_( nOldStyle81Mask, nNewStyle81Mask ); + } + + void ChkToggleBiDiAttr_( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask ); + + void ChkToggleBiDiAttr( sal_uInt16 nOldStyle81Mask, sal_uInt16 nNewStyle81Mask ) + { + if( nOldStyle81Mask != nNewStyle81Mask && + m_xCtrlStck->GetToggleBiDiAttrFlags() ) + ChkToggleBiDiAttr_( nOldStyle81Mask, nNewStyle81Mask ); + } + + void PopTableDesc(); + void MoveInsideFly(const SwFrameFormat *pFlyFormat); + SwTwips MoveOutsideFly(SwFrameFormat *pFlyFormat, const SwPosition &rPos, + bool bTableJoin = true); + + void SetOutlineStyles(); + + bool SetSpacing(SwPaM &rMyPam, int nSpace, bool bIsUpper); + bool SetUpperSpacing(SwPaM &pMyPam, int nSpace); + bool SetLowerSpacing(SwPaM &rMyPam, int nSpace); + + bool IsInlineEscherHack() const + { return !m_aFieldStack.empty() && m_aFieldStack.back().mnFieldId == 95; }; + + void StoreMacroCmds(); + + // #i84783# + // determine object attribute "Layout in Table Cell" + bool IsObjectLayoutInTableCell( const sal_uInt32 nLayoutInTableCell ) const; + void ReadGlobalTemplateSettings( const OUString& sCreatedFrom, const css::uno::Reference< css::container::XNameContainer >& xPrjNameMap ); + SwWW8ImplReader(const SwWW8ImplReader &) = delete; + SwWW8ImplReader& operator=(const SwWW8ImplReader&) = delete; +public: // really private, but can only be done public + ~SwWW8ImplReader(); + sal_uInt16 GetToggleAttrFlags() const; + sal_uInt16 GetToggleBiDiAttrFlags() const; + void SetToggleAttrFlags(sal_uInt16 nFlags); + void SetToggleBiDiAttrFlags(sal_uInt16 nFlags); + WW8_CP GetCurrAttrCP() const {return m_aCurrAttrCP;} + bool IsParaEndInCPs(sal_Int32 , sal_Int32,bool bSdOD=true) const; + //Clear the para end position recorded in reader intermittently for the least impact on loading performance + void ClearParaEndPosition(); + + long Read_Footnote(WW8PLCFManResult* pRes); + sal_uInt16 End_Footnote(); + long Read_Field(WW8PLCFManResult* pRes); + sal_uInt16 End_Field(); + long Read_Book(WW8PLCFManResult*); + long Read_And(WW8PLCFManResult* pRes); + long Read_AtnBook(WW8PLCFManResult*); + long Read_FactoidBook(WW8PLCFManResult*); + + // attributes + + void Read_Special(sal_uInt16, const sal_uInt8*, short nLen); + void Read_Obj(sal_uInt16, const sal_uInt8*, short nLen); + void Read_PicLoc(sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_BoldUsw(sal_uInt16 nId, const sal_uInt8*, short nLen); + void Read_Bidi(sal_uInt16 nId, const sal_uInt8*, short nLen); + void Read_BoldBiDiUsw(sal_uInt16 nId, const sal_uInt8*, short nLen); + void Read_AmbiguousSPRM(sal_uInt16 nId, const sal_uInt8*, short nLen); + void Read_SubSuper( sal_uInt16, const sal_uInt8*, short nLen ); + bool ConvertSubToGraphicPlacement(); + static SwFrameFormat *ContainsSingleInlineGraphic(const SwPaM &rRegion); + void Read_SubSuperProp( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_Underline( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_TextColor( sal_uInt16, const sal_uInt8*, short nLen ); + void openFont(sal_uInt16 nFCode, sal_uInt16 nId); + void closeFont(sal_uInt16 nId); + void Read_FontCode( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_FontSize( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_CharSet(sal_uInt16 , const sal_uInt8* pData, short nLen); + void Read_Language( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_CColl( sal_uInt16, const sal_uInt8*, short nLen ); + void Read_Kern( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_FontKern( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_Emphasis( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_ScaleWidth( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_Relief( sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_TextAnim( sal_uInt16, const sal_uInt8* pData, short nLen); + + void Read_NoLineNumb( sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + + void Read_LR( sal_uInt16 nId, const sal_uInt8*, short nLen ); + void Read_UL( sal_uInt16 nId, const sal_uInt8*, short nLen ); + void Read_ParaAutoBefore(sal_uInt16 , const sal_uInt8 *pData, short nLen); + void Read_ParaAutoAfter(sal_uInt16 , const sal_uInt8 *pData, short nLen); + void Read_ParaContextualSpacing( sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + void Read_LineSpace( sal_uInt16, const sal_uInt8*, short nLen ); + + void SetRelativeJustify( bool bRel ); + bool IsRelativeJustify(); + bool IsRelativeJustify(sal_uInt16 nColl, o3tl::sorted_vector<sal_uInt16>& rVisitedStyles); + void Read_Justify(sal_uInt16, const sal_uInt8*, short nLen); + + void Read_IdctHint(sal_uInt16, const sal_uInt8*, short nLen); + bool IsRightToLeft(); + void Read_RTLJustify(sal_uInt16, const sal_uInt8*, short nLen); + void Read_Hyphenation( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_WidowControl( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_AlignFont( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_UsePgsuSettings( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_KeepLines( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_KeepParas( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_BreakBefore( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_ApoPPC(sal_uInt16, const sal_uInt8* pData, short); + + void Read_BoolItem( sal_uInt16 nId, const sal_uInt8*, short nLen ); + + void Read_Border( sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + void Read_CharBorder(sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + void Read_Tab( sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + void Read_Symbol(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_FieldVanish( sal_uInt16 nId, const sal_uInt8* pData, short nLen ); + + // Revision Marks ( == Redlining ) + + // insert or delete content (change char attributes resp.) + void Read_CRevisionMark(RedlineType eType, const sal_uInt8* pData, short nLen); + // insert new content + void Read_CFRMark(sal_uInt16 , const sal_uInt8* pData, short nLen); + // delete old content + void Read_CFRMarkDel(sal_uInt16 , const sal_uInt8* pData, short nLen); + // change properties of content (e.g. char formatting) + void Read_CPropRMark(sal_uInt16 , const sal_uInt8* pData, short nLen); // complex! + + void Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen ); + bool ParseTabPos(WW8_TablePos *aTabPos, WW8PLCFx_Cp_FKP* pPap); + void Read_Shade( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ); + + // outline level Ver8 + void Read_POutLvl(sal_uInt16, const sal_uInt8* pData, short nLen); + + void Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen ); + + void Read_CharShadow( sal_uInt16, const sal_uInt8* pData, short nLen ); + void Read_CharHighlight( sal_uInt16, const sal_uInt8* pData, short nLen ); + // Ver8-Listen + + void Read_ListLevel( sal_uInt16 nId, const sal_uInt8* pData, short nLen); + + /** + * read and interpret the sprmPIlfo used to determine which list + * contains the paragraph. + * @param nId unused (sprm value, 0x460b for sprmPIlfo). + * @param[in] pData operand. + * @param[in] nLen size of the operand (pData) in byte, should be 2. + * -1 to indicate the actual level is finished. + */ + void Read_LFOPosition( sal_uInt16 nId, const sal_uInt8* pData, short nLen); + bool SetTextFormatCollAndListLevel(const SwPaM& rRg, SwWW8StyInf& rStyleInfo); + + void Read_StyleCode(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_Majority(sal_uInt16, const sal_uInt8* , short ); + void Read_DoubleLine_Rotate( sal_uInt16, const sal_uInt8* pDATA, short nLen); + + void Read_TextForeColor(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_TextBackColor(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_ParaBackColor(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_ParaBiDi(sal_uInt16, const sal_uInt8* pData, short nLen); + static Color ExtractColour(const sal_uInt8* &rpData, bool bVer67); + + void Read_TextVerticalAdjustment(sal_uInt16, const sal_uInt8* pData, short nLen); + void Read_UnderlineColor(sal_uInt16, const sal_uInt8* pData, short nLen); + long MapBookmarkVariables(const WW8FieldDesc* pF, OUString &rOrigName, + const OUString &rData); + OUString GetMappedBookmark(const OUString &rOrigName); + + // fields + eF_ResT Read_F_Input(WW8FieldDesc*, OUString& rStr); + eF_ResT Read_F_InputVar(WW8FieldDesc*, OUString& rStr); + eF_ResT Read_F_ANumber( WW8FieldDesc*, OUString& ); + eF_ResT Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr ); + eF_ResT Read_F_Author( WW8FieldDesc*, OUString& ); + eF_ResT Read_F_TemplName( WW8FieldDesc*, OUString& ); + SvNumFormatType GetTimeDatePara(OUString const & rStr, sal_uInt32& rFormat, LanguageType &rLang, + int nWhichDefault, bool bHijri = false); + bool ForceFieldLanguage(SwField &rField, LanguageType nLang); + eF_ResT Read_F_DateTime( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_FileName( WW8FieldDesc*, OUString& rStr); + eF_ResT Read_F_Num( WW8FieldDesc* pF, OUString& ); + eF_ResT Read_F_CurPage( WW8FieldDesc*, OUString& ); + eF_ResT Read_F_Ref( WW8FieldDesc* pF, OUString& ); + + eF_ResT Read_F_Set( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_PgRef( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_NoteReference( WW8FieldDesc* pF, OUString& rStr ); + + eF_ResT Read_F_Tox( WW8FieldDesc* pF, OUString& rStr ); + eF_ResT Read_F_Symbol( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_Embedd( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr); + eF_ResT Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr ); + eF_ResT Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr); + css::awt::Size MiserableDropDownFormHack(const OUString &rString, + css::uno::Reference<css::beans::XPropertySet> const & rPropSet); + + eF_ResT Read_F_Macro( WW8FieldDesc*, OUString& rStr); + eF_ResT Read_F_DBField( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_DBNext( WW8FieldDesc*, OUString& ); + eF_ResT Read_F_DBNum( WW8FieldDesc*, OUString& ); + eF_ResT Read_F_Equation( WW8FieldDesc*, OUString& ); + void Read_SubF_Ruby( msfilter::util::WW8ReadFieldParams& rReadParam); + eF_ResT Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_IncludeText( WW8FieldDesc*, OUString& rStr ); + eF_ResT Read_F_Seq( WW8FieldDesc*, OUString& rStr ); + /// Reads a STYLEREF field. + eF_ResT Read_F_Styleref(WW8FieldDesc*, OUString& rStr); + + eF_ResT Read_F_OCX(WW8FieldDesc*, OUString&); + eF_ResT Read_F_Hyperlink(WW8FieldDesc*, OUString& rStr); + eF_ResT Read_F_Shape(WW8FieldDesc* pF, OUString& rStr); + eF_ResT Read_F_HTMLControl( WW8FieldDesc* pF, OUString& rStr); + + short ImportSprm(const sal_uInt8* pPos, sal_Int32 nMemLen, sal_uInt16 nId = 0); + + bool SearchRowEnd(WW8PLCFx_Cp_FKP* pPap,WW8_CP &rStartCp, int nLevel) const; + /// Seek to the end of the table with pPap, returns true on success. + bool SearchTableEnd(WW8PLCFx_Cp_FKP* pPap) const; + bool FloatingTableConversion(WW8PLCFx_Cp_FKP* pPap); + + const WW8Fib& GetFib() const { return *m_xWwFib; } + SwDoc& GetDoc() const { return m_rDoc; } + sal_uInt16 GetCurrentColl() const { return m_nCurrentColl; } + void SetNCurrentColl( sal_uInt16 nColl ) { m_nCurrentColl = nColl; } + std::unique_ptr<SfxItemSet> SetCurrentItemSet(std::unique_ptr<SfxItemSet> pItemSet); + sal_uInt16 StyleUsingLFO( sal_uInt16 nLFOIndex ) const ; + const SwFormat* GetStyleWithOrgWWName( OUString const & rName ) const ; + + static bool GetPictGrafFromStream(Graphic& rGraphic, SvStream& rSrc); + static void PicRead( SvStream *pDataStream, WW8_PIC *pPic, bool bVer67); + static bool ImportOleWMF(const tools::SvRef<SotStorage>& xSrc1, GDIMetaFile& rWMF, long& rX, + long& rY); + static Color GetCol(sal_uInt8 nIco); + + SwWW8ImplReader( sal_uInt8 nVersionPara, SotStorage* pStorage, SvStream* pSt, + SwDoc& rD, const OUString& rBaseURL, bool bNewDoc, bool bSkipImages, SwPosition const &rPos ); + + const OUString& GetBaseURL() const { return m_sBaseURL; } + // load a complete doc file + ErrCode LoadDoc(WW8Glossary *pGloss=nullptr); + rtl_TextEncoding GetCurrentCharSet(); + rtl_TextEncoding GetCurrentCJKCharSet(); + rtl_TextEncoding GetCharSetFromLanguage(); + rtl_TextEncoding GetCJKCharSetFromLanguage(); + + void PostProcessAttrs(); + void ReadEmbeddedData(SvStream& rStrm, SwDocShell const * pDocShell, struct HyperLinksTable& hlStr); + void NotifyMacroEventRead(); +}; + +bool CanUseRemoteLink(const OUString &rGrfName); +void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat); +void SetStyleIndent(SwWW8StyInf &rStyleInfo, const SwNumFormat &rFormat); +// #i103711# +// #i105414# +void SyncIndentWithList( SvxLRSpaceItem &rLR, + const SwNumFormat &rFormat, + const bool bFirstLineOfStSet, + const bool bLeftIndentSet ); +long GetListFirstLineIndent(const SwNumFormat &rFormat); +OUString BookmarkToWriter(const OUString &rBookmark); +bool RTLGraphicsHack(SwTwips &rLeft, SwTwips nWidth, + sal_Int16 eHoriOri, sal_Int16 eHoriRel, SwTwips nPageLeft, + SwTwips nPageRight, SwTwips nPageSize); +void MatchEscherMirrorIntoFlySet(const SvxMSDffImportRec &rRecord, + SfxItemSet &rFlySet); +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx new file mode 100644 index 000000000..5ccf13bd1 --- /dev/null +++ b/sw/source/filter/ww8/ww8par2.cxx @@ -0,0 +1,4577 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <tools/solar.h> +#include <vcl/font.hxx> +#include <hintids.hxx> +#include <editeng/colritem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/pgrditem.hxx> +#include <msfilter.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <docary.hxx> +#include <ndtxt.hxx> +#include <paratr.hxx> +#include <poolfmt.hxx> +#include <swtable.hxx> +#include <tblsel.hxx> +#include <mdiexp.hxx> +#include <txtftn.hxx> +#include <frmfmt.hxx> +#include <ftnidx.hxx> +#include <fmtftn.hxx> +#include <fmtlsplt.hxx> +#include <charfmt.hxx> +#include <fmtanchr.hxx> +#include <fmtrowsplt.hxx> +#include <fmtfollowtextflow.hxx> +#include <numrule.hxx> +#include "sprmids.hxx" +#include <wwstyles.hxx> +#include "writerhelper.hxx" +#include "ww8struc.hxx" +#include "ww8par.hxx" +#include "ww8par2.hxx" + +#include <frmatr.hxx> +#include <itabenum.hxx> +#include <unocrsr.hxx> + +#include <iostream> +#include <memory> + +using namespace ::com::sun::star; + +WW8TabBandDesc::WW8TabBandDesc(): + pNextBand(nullptr), nGapHalf(0), mnDefaultLeft(0), mnDefaultTop(0), mnDefaultRight(0), + mnDefaultBottom(0), mbHasSpacing(false), nLineHeight(0), nRows(0), nCenter{}, nWidth{}, + nWwCols(0), nSwCols(0), bLEmptyCol(false), bREmptyCol(false), bCantSplit(false), + bCantSplit90(false), pTCs(nullptr), nOverrideSpacing{}, nOverrideValues{}, pSHDs(nullptr), + pNewSHDs(nullptr), bExist{}, nTransCell{} +{ + for (sal_uInt16 & rn : maDirections) + rn = 4; +} + +WW8TabBandDesc::~WW8TabBandDesc() +{ + delete[] pTCs; + delete[] pSHDs; + delete[] pNewSHDs; +} + +void sw::util::RedlineStack::close( const SwPosition& rPos, + RedlineType eType, WW8TabDesc* pTabDesc ) +{ + // If the redline type is not found in the redline stack, we have to check if there has been + // a tabledesc and to check its saved redline stack, too. (#136939, #i68139) + if( !close( rPos, eType ) ) + { + if( pTabDesc && pTabDesc->getOldRedlineStack() ) + { + bool const bResult = + pTabDesc->getOldRedlineStack()->close(rPos, eType); + OSL_ENSURE( bResult, "close without open!"); + } + } +} + +void wwSectionManager::SetCurrentSectionHasFootnote() +{ + OSL_ENSURE(!maSegments.empty(), + "should not be possible, must be at least one segment"); + if (!maSegments.empty()) + maSegments.back().mbHasFootnote = true; +} + +void wwSectionManager::SetCurrentSectionVerticalAdjustment(const drawing::TextVerticalAdjust nVA) +{ + OSL_ENSURE(!maSegments.empty(), + "should not be possible, must be at least one segment"); + if ( !maSegments.empty() ) + maSegments.back().mnVerticalAdjustment = nVA; +} + +bool wwSectionManager::CurrentSectionIsVertical() const +{ + OSL_ENSURE(!maSegments.empty(), + "should not be possible, must be at least one segment"); + if (!maSegments.empty()) + return maSegments.back().IsVertical(); + return false; +} + +bool wwSectionManager::CurrentSectionIsProtected() const +{ + OSL_ENSURE(!maSegments.empty(), + "should not be possible, must be at least one segment"); + if (!maSegments.empty()) + return SectionIsProtected(maSegments.back()); + return false; +} + +sal_uInt32 wwSectionManager::GetPageLeft() const +{ + return !maSegments.empty() ? maSegments.back().nPgLeft : 0; +} + +sal_uInt32 wwSectionManager::GetPageRight() const +{ + return !maSegments.empty() ? maSegments.back().nPgRight : 0; +} + +sal_uInt32 wwSectionManager::GetPageWidth() const +{ + return !maSegments.empty() ? maSegments.back().GetPageWidth() : 0; +} + +sal_uInt32 wwSectionManager::GetTextAreaWidth() const +{ + return !maSegments.empty() ? maSegments.back().GetTextAreaWidth() : 0; +} + +sal_uInt32 wwSectionManager::GetWWPageTopMargin() const +{ + return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0; +} + +sal_uInt16 SwWW8ImplReader::End_Footnote() +{ + /* + Ignoring Footnote outside of the normal Text. People will put footnotes + into field results and field commands. + */ + if (m_bIgnoreText || + m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex()) + { + return 0; + } + + OSL_ENSURE(!m_aFootnoteStack.empty(), "footnote end without start"); + if (m_aFootnoteStack.empty()) + return 0; + + bool bFtEdOk = false; + const FootnoteDescriptor &rDesc = m_aFootnoteStack.back(); + + //Get the footnote character and remove it from the txtnode. We'll + //replace it with the footnote + SwTextNode* pText = m_pPaM->GetNode().GetTextNode(); + sal_Int32 nPos = m_pPaM->GetPoint()->nContent.GetIndex(); + + OUString sChar; + SwTextAttr* pFN = nullptr; + //There should have been a footnote char, we will replace this. + if (pText && nPos) + { + sChar += OUStringChar(pText->GetText()[--nPos]); + m_pPaM->SetMark(); + --m_pPaM->GetMark()->nContent; + std::shared_ptr<SwUnoCursor> xLastAnchorCursor(m_pLastAnchorPos ? m_rDoc.CreateUnoCursor(*m_pLastAnchorPos) : nullptr); + m_pLastAnchorPos.reset(); + m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM ); + m_pPaM->DeleteMark(); + if (xLastAnchorCursor) + m_pLastAnchorPos.reset(new SwPosition(*xLastAnchorCursor->GetPoint())); + SwFormatFootnote aFootnote(rDesc.meType == MAN_EDN); + pFN = pText->InsertItem(aFootnote, nPos, nPos); + } + OSL_ENSURE(pFN, "Problems creating the footnote text"); + if (pFN) + { + + SwPosition aTmpPos( *m_pPaM->GetPoint() ); // remember old cursor position + WW8PLCFxSaveAll aSave; + m_xPlcxMan->SaveAllPLCFx( aSave ); + std::shared_ptr<WW8PLCFMan> xOldPlcxMan = m_xPlcxMan; + + const SwNodeIndex* pSttIdx = static_cast<SwTextFootnote*>(pFN)->GetStartNode(); + OSL_ENSURE(pSttIdx, "Problems creating footnote text"); + + static_cast<SwTextFootnote*>(pFN)->SetSeqNo( m_rDoc.GetFootnoteIdxs().size() ); + + bool bOld = m_bFootnoteEdn; + m_bFootnoteEdn = true; + + // read content of Ft-/End-Note + Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType); + bFtEdOk = true; + m_bFootnoteEdn = bOld; + + OSL_ENSURE(sChar.getLength()==1 && ((rDesc.mbAutoNum == (sChar[0] == 2))), + "footnote autonumbering must be 0x02, and everything else must not be"); + + // If no automatic numbering use the following char from the main text + // as the footnote number + if (!rDesc.mbAutoNum) + static_cast<SwTextFootnote*>(pFN)->SetNumber(0, 0, sChar); + + /* + Delete the footnote char from the footnote if its at the beginning + as usual. Might not be if the user has already deleted it, e.g. + #i14737# + */ + SwNodeIndex& rNIdx = m_pPaM->GetPoint()->nNode; + rNIdx = pSttIdx->GetIndex() + 1; + SwTextNode* pTNd = rNIdx.GetNode().GetTextNode(); + if (pTNd && !pTNd->GetText().isEmpty() && !sChar.isEmpty()) + { + const OUString &rText = pTNd->GetText(); + if (rText[0] == sChar[0]) + { + // Allow MSO to emulate LO footnote text starting at left margin - only meaningful with hanging indent + sal_Int32 nFirstLineIndent=0; + SfxItemSet aSet( m_rDoc.GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + if ( pTNd->GetAttr(aSet) ) + { + const SvxLRSpaceItem* pLRSpace = aSet.GetItem<SvxLRSpaceItem>(RES_LR_SPACE); + if ( pLRSpace ) + nFirstLineIndent = pLRSpace->GetTextFirstLineOffset(); + } + + m_pPaM->GetPoint()->nContent.Assign( pTNd, 0 ); + m_pPaM->SetMark(); + // Strip out aesthetic tabs we may have inserted on export #i24762# + if (nFirstLineIndent < 0 && rText.getLength() > 1 && rText[1] == 0x09) + ++m_pPaM->GetMark()->nContent; + ++m_pPaM->GetMark()->nContent; + m_xReffingStck->Delete(*m_pPaM); + m_rDoc.getIDocumentContentOperations().DeleteRange( *m_pPaM ); + m_pPaM->DeleteMark(); + } + } + + *m_pPaM->GetPoint() = aTmpPos; // restore Cursor + + m_xPlcxMan = xOldPlcxMan; // Restore attributes + m_xPlcxMan->RestoreAllPLCFx( aSave ); + } + + if (bFtEdOk) + m_aSectionManager.SetCurrentSectionHasFootnote(); + + m_aFootnoteStack.pop_back(); + return 0; +} + +long SwWW8ImplReader::Read_Footnote(WW8PLCFManResult* pRes) +{ + /* + Ignoring Footnote outside of the normal Text. People will put footnotes + into field results and field commands. + */ + if (m_bIgnoreText || + m_pPaM->GetPoint()->nNode < m_rDoc.GetNodes().GetEndOfExtras().GetIndex()) + { + return 0; + } + + FootnoteDescriptor aDesc; + aDesc.mbAutoNum = true; + if (eEDN == pRes->nSprmId) + { + aDesc.meType = MAN_EDN; + WW8PLCFx_SubDoc* pEndNote = m_xPlcxMan->GetEdn(); + if (const void* pData = pEndNote ? pEndNote->GetData() : nullptr) + aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData); + } + else + { + aDesc.meType = MAN_FTN; + WW8PLCFx_SubDoc* pFootNote = m_xPlcxMan->GetFootnote(); + if (const void* pData = pFootNote ? pFootNote->GetData() : nullptr) + aDesc.mbAutoNum = 0 != *static_cast<short const*>(pData); + } + + aDesc.mnStartCp = pRes->nCp2OrIdx; + aDesc.mnLen = pRes->nMemLen; + + m_aFootnoteStack.push_back(aDesc); + + return 0; +} + +bool SwWW8ImplReader::SearchRowEnd(WW8PLCFx_Cp_FKP* pPap, WW8_CP &rStartCp, + int nLevel) const +{ + WW8PLCFxDesc aRes; + aRes.pMemPos = nullptr; + aRes.nEndPos = rStartCp; + std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes; + + while (pPap->HasFkp() && rStartCp != WW8_CP_MAX) + { + if (pPap->Where() != WW8_CP_MAX) + { + SprmResult aSprmRes = pPap->HasSprm(TabRowSprm(nLevel)); + const sal_uInt8* pB = aSprmRes.pSprm; + if (pB && aSprmRes.nRemainingData >= 1 && *pB == 1) + { + aSprmRes = pPap->HasSprm(0x6649); + const sal_uInt8 *pLevel = aSprmRes.pSprm; + if (pLevel && aSprmRes.nRemainingData >= 1) + { + if (nLevel + 1 == *pLevel) + return true; + } + else + { + OSL_ENSURE(!nLevel || pLevel, "sublevel without level sprm"); + return true; // RowEnd found + } + } + } + + aRes.nStartPos = aRes.nEndPos; + aRes.pMemPos = nullptr; + //Seek to our next block of properties + if (!(pPap->SeekPos(aRes.nStartPos))) + { + aRes.nEndPos = WW8_CP_MAX; + pPap->SetDirty(true); + } + pPap->GetSprms(&aRes); + pPap->SetDirty(false); + auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos)); + if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop + { + SAL_WARN("sw.ww8", "SearchRowEnd, loop in paragraph property chain"); + break; + } + //Update our aRes to get the new starting point of the next properties + rStartCp = aRes.nEndPos; + } + + return false; +} + +bool SwWW8ImplReader::SearchTableEnd(WW8PLCFx_Cp_FKP* pPap) const +{ + if (m_bVer67) + // The below SPRM is for WW8 only. + return false; + + WW8PLCFxDesc aRes; + aRes.pMemPos = nullptr; + aRes.nEndPos = pPap->Where(); + std::set<std::pair<WW8_CP, WW8_CP>> aPrevRes; + + while (pPap->HasFkp() && pPap->Where() != WW8_CP_MAX) + { + // See if the current pap is outside the table. + SprmResult aSprmRes = pPap->HasSprm(NS_sprm::sprmPFInTable); + const sal_uInt8* pB = aSprmRes.pSprm; + if (!pB || aSprmRes.nRemainingData < 1 || *pB != 1) + // Yes, this is the position after the end of the table. + return true; + + // It is, so seek to the next pap. + aRes.nStartPos = aRes.nEndPos; + aRes.pMemPos = nullptr; + if (!pPap->SeekPos(aRes.nStartPos)) + return false; + + // Read the sprms and make sure we moved forward to avoid infinite loops. + pPap->GetSprms(&aRes); + auto aBounds(std::make_pair(aRes.nStartPos, aRes.nEndPos)); + if (!aPrevRes.insert(aBounds).second) //already seen these bounds, infinite loop + { + SAL_WARN("sw.ww8", "SearchTableEnd, loop in paragraph property chain"); + break; + } + } + + return false; +} + +ApoTestResults SwWW8ImplReader::TestApo(int nCellLevel, bool bTableRowEnd, + const WW8_TablePos *pTabPos) +{ + const WW8_TablePos *pTopLevelTable = nCellLevel <= 1 ? pTabPos : nullptr; + ApoTestResults aRet; + // Frame in Style Definition (word appears to ignore them if inside an + // text autoshape) + sal_uInt16 const nStyle(m_xPlcxMan->GetColl()); + if (!m_bTxbxFlySection && nStyle < m_vColl.size()) + aRet.mpStyleApo = StyleExists(nStyle) ? m_vColl[nStyle].m_xWWFly.get() : nullptr; + + /* + #i1140# + If I have a table and apply a style to one of its frames that should cause + a paragraph that it is applied to it to only exist as a separate floating + frame, then the behaviour depends on which cell that it has been applied + to. If it is the first cell of a row then the whole table row jumps into the + new frame, if it isn't then the paragraph attributes are applied + "except" for the floating frame stuff. i.e. it's ignored. So if there's a + table, and we're not in the first cell then we ignore the fact that the + paragraph style wants to be in a different frame. + + This sort of mindbending inconsistency is surely why frames are deprecated + in word 97 onwards and hidden away from the user + + #i1532# & #i5379# + If we are already a table in a frame then we must grab the para properties + to see if we are still in that frame. + */ + + aRet.m_bHasSprm37 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 37 : 0x2423).pSprm != nullptr; + SprmResult aSrpm29 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 29 : 0x261B); + const sal_uInt8 *pSrpm29 = aSrpm29.pSprm; + aRet.m_bHasSprm29 = pSrpm29 != nullptr; + aRet.m_nSprm29 = (pSrpm29 && aSrpm29.nRemainingData >= 1) ? *pSrpm29 : 0; + + // Is there some frame data here + bool bNowApo = aRet.HasFrame() || pTopLevelTable; + if (bNowApo) + { + if (!ConstructApo(aRet, pTabPos)) + bNowApo = false; + } + + bool bTestAllowed = !m_bTxbxFlySection && !bTableRowEnd; + if (bTestAllowed) + { + //Test is allowed if there is no table. + //Otherwise only allowed if we are in the + //first paragraph of the first cell of a row. + //(And only if the row we are inside is at the + //same level as the previous row, think tables + //in tables) + if (nCellLevel == m_nInTable) + { + + if (!m_nInTable) + bTestAllowed = true; + else + { + if (!m_xTableDesc) + { + OSL_ENSURE(m_xTableDesc, "What!"); + bTestAllowed = false; + } + else + { + // #i39468# + // If current cell isn't valid, the test is allowed. + // The cell isn't valid, if e.g. there is a new row + // <pTableDesc->nCurrentRow> >= <pTableDesc->pTabLines->Count()> + bTestAllowed = + m_xTableDesc->GetCurrentCol() == 0 && + ( !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) || + m_xTableDesc->InFirstParaInCell() ); + } + } + } + } + + if (!bTestAllowed) + return aRet; + + aRet.mbStartApo = bNowApo && !InEqualOrHigherApo(1); // APO-start + aRet.mbStopApo = InEqualOrHigherApo(nCellLevel) && !bNowApo; // APO-end + + //If it happens that we are in a table, then if it's not the first cell + //then any attributes that might otherwise cause the contents to jump + //into another frame don't matter, a table row sticks together as one + //unit no matter what else happens. So if we are not in a table at + //all, or if we are in the first cell then test that the last frame + //data is the same as the current one + if (bNowApo && InEqualApo(nCellLevel)) + { + // two bordering each other + if (!TestSameApo(aRet, pTabPos)) + aRet.mbStopApo = aRet.mbStartApo = true; + } + + return aRet; +} + +// helper methods for outline, numbering and bullets + +static void SetBaseAnlv(SwNumFormat &rNum, WW8_ANLV const &rAV, sal_uInt8 nSwLevel ) +{ + static const SvxNumType eNumA[8] = { SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER, + SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N, SVX_NUM_ARABIC, + SVX_NUM_ARABIC, SVX_NUM_ARABIC }; + + static const SvxAdjust eAdjA[4] = { SvxAdjust::Left, + SvxAdjust::Right, SvxAdjust::Left, SvxAdjust::Left }; + if (rAV.nfc < 8) { + rNum.SetNumberingType( eNumA[ rAV.nfc ] ); + } else { + SvxNumType nType = SVX_NUM_ARABIC; + switch( rAV.nfc ) { + case 14: + case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break; + case 30:nType = SVX_NUM_TIAN_GAN_ZH; break; + case 31:nType = SVX_NUM_DI_ZI_ZH; break; + case 35: + case 36: + case 37: + case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break; + case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break; + case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break; + case 10: + case 11:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break; + case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break; + case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break; + case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break; + case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break; + case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break; + case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break; + case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break; + //case 42: + //case 43: + case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break; + default: + nType= SVX_NUM_ARABIC;break; + } + rNum.SetNumberingType( nType ); + } + + if ((rAV.aBits1 & 0x4) >> 2) + { + rNum.SetIncludeUpperLevels(nSwLevel + 1); + } + rNum.SetStart( SVBT16ToUInt16( rAV.iStartAt ) ); + rNum.SetNumAdjust( eAdjA[ rAV.aBits1 & 0x3] ); + + rNum.SetCharTextDistance( SVBT16ToUInt16( rAV.dxaSpace ) ); + sal_Int16 nIndent = std::abs(static_cast<sal_Int16>(SVBT16ToUInt16( rAV.dxaIndent ))); + if( rAV.aBits1 & 0x08 ) //fHang + { + rNum.SetFirstLineOffset( -nIndent ); + rNum.SetAbsLSpace( nIndent ); + } + else + rNum.SetCharTextDistance( nIndent ); // width of number is missing + + if( rAV.nfc == 5 || rAV.nfc == 7 ) + { + OUString sP = "." + rNum.GetSuffix(); + rNum.SetSuffix( sP ); // ordinal number + } +} + +void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, WW8_ANLV const &rAV, + const sal_uInt8* pText, size_t nStart, size_t nElements, bool bOutline) +{ + if (nStart > nElements) + return; + + pText += nStart; + nElements -= nStart; + + bool bInsert = false; // Default + rtl_TextEncoding eCharSet = m_eStructCharSet; + + const WW8_FFN* pF = m_xFonts->GetFont(SVBT16ToUInt16(rAV.ftc)); // FontInfo + bool bListSymbol = pF && ( pF->aFFNBase.chs == 2 ); // Symbol/WingDings/... + + OUStringBuffer sText; + sal_uInt32 nLen = rAV.cbTextBefore + rAV.cbTextAfter; + if (m_bVer67) + { + if (nLen > nElements) + { + SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range " + << nLen << " vs " << nElements << " max"); + return; + } + sText = OUString(reinterpret_cast<char const *>(pText), nLen, eCharSet); + // ofz#23961 in case of multi-byte input encoding resulting in shorter + // output pad to full length with something semi-arbitrary + comphelper::string::padToLength(sText, nLen, cBulletChar); + } + else + { + if (nLen > nElements / 2) + { + SAL_WARN("sw.ww8", "SetAnlvStrings: ignoring out of range " + << nLen << " vs " << nElements / 2 << " max"); + return; + } + for(sal_uInt32 i = 0; i < nLen; ++i, pText += 2) + { + sText.append(static_cast<sal_Unicode>(SVBT16ToUInt16(*reinterpret_cast<SVBT16 const *>(pText)))); + } + } + + if( bOutline ) + { // outline + if( !rNum.GetIncludeUpperLevels() // there are <= 1 number to show + || rNum.GetNumberingType() == SVX_NUM_NUMBER_NONE ) // or this level has none + { + // if self defined digits + bInsert = true; // then apply character + + // replace by simple Bullet ? + if( bListSymbol ) + { + // use cBulletChar for correct mapping on MAC + OUStringBuffer aBuf; + comphelper::string::padToLength(aBuf, rAV.cbTextBefore + + rAV.cbTextAfter, cBulletChar); + sText = aBuf; + } + } + } + else + { // numbering / bullets + bInsert = true; + if( bListSymbol ) + { + FontFamily eFamily; + OUString aName; + FontPitch ePitch; + + if( GetFontParams( SVBT16ToUInt16( rAV.ftc ), eFamily, aName, + ePitch, eCharSet ) ){ + + vcl::Font aFont; + aFont.SetFamilyName( aName ); + aFont.SetFamily( eFamily ); + + aFont.SetCharSet( eCharSet ); + rNum.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + + rNum.SetBulletFont( &aFont ); + + // take only the very first character + if (rAV.cbTextBefore || rAV.cbTextAfter) + rNum.SetBulletChar( sText[ 0 ] ); + else + rNum.SetBulletChar( 0x2190 ); + } + } + } + if( bInsert ) + { + if (rAV.cbTextBefore) + { + OUString sP( sText.copy( 0, rAV.cbTextBefore ).makeStringAndClear() ); + rNum.SetPrefix( sP ); + } + if( rAV.cbTextAfter ) + { + OUString sP( rNum.GetSuffix() ); + sP += sText.copy( rAV.cbTextBefore, rAV.cbTextAfter).makeStringAndClear(); + rNum.SetSuffix( sP ); + } +// The characters before and after multiple digits do not apply because +// those are handled differently by the writer and the result is in most +// cases worse than without. + } +} + +// SetAnld gets a WW-ANLD-Descriptor and a Level and modifies the NumRules +// which are provided by pNumR. This is used for everything beside +// outline inside the text. +void SwWW8ImplReader::SetAnld(SwNumRule* pNumR, WW8_ANLD const * pAD, sal_uInt8 nSwLevel, + bool bOutLine) +{ + SwNumFormat aNF; + if (pAD) + { // there is an Anld-Sprm + m_bCurrentAND_fNumberAcross = 0 != pAD->fNumberAcross; + WW8_ANLV const &rAV = pAD->eAnlv; + SetBaseAnlv(aNF, rAV, nSwLevel); // set the base format + SetAnlvStrings(aNF, rAV, pAD->rgchAnld, 0, SAL_N_ELEMENTS(pAD->rgchAnld), bOutLine); // set the rest + } + pNumR->Set(nSwLevel, aNF); +} + +// chapter numbering and bullets + +// Chapter numbering happens in the style definition. +// Sprm 13 provides the level, Sprm 12 the content. + +SwNumRule* SwWW8ImplReader::GetStyRule() +{ + if( m_xStyles->mpStyRule ) // Bullet-Style already present + return m_xStyles->mpStyRule; + + const OUString aBaseName("WW8StyleNum"); + const OUString aName( m_rDoc.GetUniqueNumRuleName( &aBaseName, false) ); + + // #i86652# + sal_uInt16 nRul = m_rDoc.MakeNumRule( aName, nullptr, false, + SvxNumberFormat::LABEL_ALIGNMENT ); + m_xStyles->mpStyRule = m_rDoc.GetNumRuleTable()[nRul]; + // Auto == false-> numbering style + m_xStyles->mpStyRule->SetAutoRule(false); + + return m_xStyles->mpStyRule; +} + +// Sprm 13 +void SwWW8ImplReader::Read_ANLevelNo( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + m_nSwNumLevel = 0xff; // Default: invalid + + if( nLen <= 0 ) + return; + + // StyleDef ? + if( m_pCurrentColl ) + { + // only for SwTextFormatColl, not CharFormat + // WW: 0 = no Numbering + SwWW8StyInf * pColl = GetStyle(m_nCurrentColl); + if (pColl != nullptr && pColl->m_bColl && *pData) + { + // Range WW:1..9 -> SW:0..8 no bullets / numbering + + if (*pData <= 9) + { + m_nSwNumLevel = *pData - 1; + if (!m_bNoAttrImport) + static_cast<SwTextFormatColl*>(m_pCurrentColl)->AssignToListLevelOfOutlineStyle( m_nSwNumLevel ); + // For WW-NoNumbering also NO_NUMBERING could be used. + // ( For normal numberierung NO_NUM has to be used: + // NO_NUM : pauses numbering, + // NO_NUMBERING : no numbering at all ) + + } + else if( *pData == 10 || *pData == 11 ) + { + // remember type, the rest happens at Sprm 12 + m_xStyles->mnWwNumLevel = *pData; + } + } + } + else + { + //Not StyleDef + if (!m_bAnl) + StartAnl(pData); // begin of outline / bullets + NextAnlLine(pData); + } +} + +void SwWW8ImplReader::Read_ANLevelDesc( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm 12 +{ + SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl); + if( !m_pCurrentColl || nLen <= 0 // only for Styledef + || (pStyInf && !pStyInf->m_bColl) // ignore CharFormat -> + || ( m_nIniFlags & WW8FL_NO_OUTLINE ) ) + { + m_nSwNumLevel = 0xff; + return; + } + + if (o3tl::make_unsigned(nLen) < sizeof(WW8_ANLD)) + { + SAL_WARN("sw.ww8", "ANLevelDesc property is " << nLen << " long, needs to be at least " << sizeof(WW8_ANLD)); + m_nSwNumLevel = 0xff; + return; + } + + if (m_nSwNumLevel <= 9) // Value range mapping WW:1..9 -> SW:0..8 + { + + // If NumRuleItems were set, either directly or through inheritance, disable them now + m_pCurrentColl->SetFormatAttr( SwNumRuleItem() ); + + const OUString aName("Outline"); + SwNumRule aNR( m_rDoc.GetUniqueNumRuleName( &aName ), + SvxNumberFormat::LABEL_WIDTH_AND_POSITION, + OUTLINE_RULE ); + aNR = *m_rDoc.GetOutlineNumRule(); + + SetAnld(&aNR, reinterpret_cast<WW8_ANLD const *>(pData), m_nSwNumLevel, true); + + // Missing Levels need not be replenished + m_rDoc.SetOutlineNumRule( aNR ); + } + else if( m_xStyles->mnWwNumLevel == 10 || m_xStyles->mnWwNumLevel == 11 ){ + SwNumRule* pNR = GetStyRule(); + SetAnld(pNR, reinterpret_cast<WW8_ANLD const *>(pData), 0, false); + m_pCurrentColl->SetFormatAttr( SwNumRuleItem( pNR->GetName() ) ); + + pStyInf = GetStyle(m_nCurrentColl); + if (pStyInf != nullptr) + pStyInf->m_bHasStyNumRule = true; + } +} + +// Numbering / Bullets + +// SetNumOlst() carries the Numrules for this cell to SwNumFormat. +// For this the info is fetched from OLST and not from ANLD ( see later ) +// ( only for outline inside text; Bullets / numbering use ANLDs ) +void SwWW8ImplReader::SetNumOlst(SwNumRule* pNumR, WW8_OLST* pO, sal_uInt8 nSwLevel) +{ + SwNumFormat aNF; + WW8_ANLV &rAV = pO->rganlv[nSwLevel]; + SetBaseAnlv(aNF, rAV, nSwLevel); + // ... and then the Strings + int nTextOfs = 0; + sal_uInt8 i; + WW8_ANLV* pAV1; // search String-Positions + for (i = 0, pAV1 = pO->rganlv; i < nSwLevel; ++i, ++pAV1) + nTextOfs += pAV1->cbTextBefore + pAV1->cbTextAfter; + + if (!m_bVer67) + nTextOfs *= 2; + SetAnlvStrings(aNF, rAV, pO->rgch, nTextOfs, SAL_N_ELEMENTS(pO->rgch), true); // and apply + pNumR->Set(nSwLevel, aNF); +} + +// The OLST is at the beginning of each section that contains outlines. +// The ANLDs that are connected to each outline-line contain only nonsense, +// so the OLSTs are remembered for the section to have usable information +// when outline-paragraphs occur. +void SwWW8ImplReader::Read_OLST( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + m_xNumOlst.reset(); + if (nLen <= 0) + return; + + if (o3tl::make_unsigned(nLen) < sizeof(WW8_OLST)) + { + SAL_WARN("sw.ww8", "WW8_OLST property is " << nLen << " long, needs to be at least " << sizeof(WW8_OLST)); + return; + } + + m_xNumOlst.reset(new WW8_OLST); + *m_xNumOlst = *reinterpret_cast<WW8_OLST const *>(pData); +} + +WW8LvlType GetNumType(sal_uInt8 nWwLevelNo) +{ + WW8LvlType nRet = WW8_None; + if( nWwLevelNo == 12 ) + nRet = WW8_Pause; + else if( nWwLevelNo == 10 ) + nRet = WW8_Numbering; + else if( nWwLevelNo == 11 ) + nRet = WW8_Sequence; + else if( nWwLevelNo > 0 && nWwLevelNo <= 9 ) + nRet = WW8_Outline; + return nRet; +} + +SwNumRule *ANLDRuleMap::GetNumRule(const SwDoc& rDoc, sal_uInt8 nNumType) +{ + const OUString& rNumRule = WW8_Numbering == nNumType ? msNumberingNumRule : msOutlineNumRule; + if (rNumRule.isEmpty()) + return nullptr; + return rDoc.FindNumRulePtr(rNumRule); +} + +void ANLDRuleMap::SetNumRule(const OUString& rNumRule, sal_uInt8 nNumType) +{ + if (WW8_Numbering == nNumType) + msNumberingNumRule = rNumRule; + else + msOutlineNumRule = rNumRule; +} + +// StartAnl is called at the beginning of a row area that contains +// outline / numbering / bullets +void SwWW8ImplReader::StartAnl(const sal_uInt8* pSprm13) +{ + m_bCurrentAND_fNumberAcross = false; + + sal_uInt8 nT = static_cast< sal_uInt8 >(GetNumType(*pSprm13)); + if (nT == WW8_Pause || nT == WW8_None) + return; + + m_nWwNumType = nT; + SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType); + + // check for COL numbering: + SprmResult aS12; // sprmAnld + OUString sNumRule; + + if (m_xTableDesc) + { + sNumRule = m_xTableDesc->GetNumRuleName(); + if (!sNumRule.isEmpty()) + { + pNumRule = m_rDoc.FindNumRulePtr(sNumRule); + if (!pNumRule) + sNumRule.clear(); + else + { + // this is ROW numbering ? + aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld + if (aS12.pSprm && aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD)) && 0 != reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross) + sNumRule.clear(); + } + } + } + + SwWW8StyInf * pStyInf = GetStyle(m_nCurrentColl); + if (sNumRule.isEmpty() && pStyInf != nullptr && pStyInf->m_bHasStyNumRule) + { + sNumRule = pStyInf->m_pFormat->GetNumRule().GetValue(); + pNumRule = m_rDoc.FindNumRulePtr(sNumRule); + if (!pNumRule) + sNumRule.clear(); + } + + if (sNumRule.isEmpty()) + { + if (!pNumRule) + { + // #i86652# + pNumRule = m_rDoc.GetNumRuleTable()[ + m_rDoc.MakeNumRule( sNumRule, nullptr, false, + SvxNumberFormat::LABEL_ALIGNMENT ) ]; + } + if (m_xTableDesc) + { + if (!aS12.pSprm) + aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); // sprmAnld + if (!aS12.pSprm || aS12.nRemainingData < sal_Int32(sizeof(WW8_ANLD)) || !reinterpret_cast<WW8_ANLD const *>(aS12.pSprm)->fNumberAcross) + m_xTableDesc->SetNumRuleName(pNumRule->GetName()); + } + } + + m_bAnl = true; + + sNumRule = pNumRule ? pNumRule->GetName() : OUString(); + // set NumRules via stack + m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), + SfxStringItem(RES_FLTR_NUMRULE, sNumRule)); + + m_aANLDRules.SetNumRule(sNumRule, m_nWwNumType); +} + +// NextAnlLine() is called once for every row of a +// outline / numbering / bullet +void SwWW8ImplReader::NextAnlLine(const sal_uInt8* pSprm13) +{ + if (!m_bAnl) + return; + + SwNumRule *pNumRule = m_aANLDRules.GetNumRule(m_rDoc, m_nWwNumType); + + // pNd->UpdateNum without a set of rules crashes at the latest whilst storing as sdw3 + + // WW:10 = numbering -> SW:0 & WW:11 = bullets -> SW:0 + if (*pSprm13 == 10 || *pSprm13 == 11) + { + m_nSwNumLevel = 0; + if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel)) + { + // not defined yet + // sprmAnld o. 0 + SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); + if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD))) + SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false); + } + } + else if( *pSprm13 > 0 && *pSprm13 <= MAXLEVEL ) // range WW:1..9 -> SW:0..8 + { + m_nSwNumLevel = *pSprm13 - 1; // outline + // undefined + if (pNumRule && !pNumRule->GetNumFormat(m_nSwNumLevel)) + { + if (m_xNumOlst) // there was a OLST + { + //Assure upper levels are set, #i9556# + for (sal_uInt8 nI = 0; nI < m_nSwNumLevel; ++nI) + { + if (!pNumRule->GetNumFormat(nI)) + SetNumOlst(pNumRule, m_xNumOlst.get(), nI); + } + + SetNumOlst(pNumRule, m_xNumOlst.get(), m_nSwNumLevel); + } + else // no Olst -> use Anld + { + // sprmAnld + SprmResult aS12 = m_xPlcxMan->HasParaSprm(m_bVer67 ? 12 : NS_sprm::LN_PAnld); + if (aS12.nRemainingData >= sal_Int32(sizeof(WW8_ANLD))) + SetAnld(pNumRule, reinterpret_cast<WW8_ANLD const *>(aS12.pSprm), m_nSwNumLevel, false); + } + } + } + else + m_nSwNumLevel = 0xff; // no number + + SwTextNode* pNd = m_pPaM->GetNode().GetTextNode(); + if (!pNd) + return; + + if (m_nSwNumLevel < MAXLEVEL) + pNd->SetAttrListLevel( m_nSwNumLevel ); + else + { + pNd->SetAttrListLevel(0); + pNd->SetCountedInList( false ); + } +} + +void SwWW8ImplReader::StopAllAnl(bool bGoBack) +{ + //Of course we're not restarting, but we'll make use of our knowledge + //of the implementation to do it. + StopAnlToRestart(WW8_None, bGoBack); +} + +void SwWW8ImplReader::StopAnlToRestart(sal_uInt8 nNewType, bool bGoBack) +{ + if (bGoBack) + { + SwPosition aTmpPos(*m_pPaM->GetPoint()); + m_pPaM->Move(fnMoveBackward, GoInContent); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE); + *m_pPaM->GetPoint() = aTmpPos; + } + else + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_NUMRULE); + + m_aANLDRules.msNumberingNumRule.clear(); + /* + #i18816# + my take on this problem is that moving either way from an outline to a + numbering doesn't halt the outline, while the numbering is always halted + */ + bool bNumberingNotStopOutline = + (((m_nWwNumType == WW8_Outline) && (nNewType == WW8_Numbering)) || + ((m_nWwNumType == WW8_Numbering) && (nNewType == WW8_Outline))); + if (!bNumberingNotStopOutline) + m_aANLDRules.msOutlineNumRule.clear(); + + m_nSwNumLevel = 0xff; + m_nWwNumType = WW8_None; + m_bAnl = false; +} + +WW8TabBandDesc::WW8TabBandDesc( WW8TabBandDesc const & rBand ) +{ + *this = rBand; + if( rBand.pTCs ) + { + pTCs = reinterpret_cast<WW8_TCell *>(new char[nWwCols * sizeof (WW8_TCell)]); + // create uninitialized + memcpy( pTCs, rBand.pTCs, nWwCols * sizeof( WW8_TCell ) ); + } + if( rBand.pSHDs ) + { + pSHDs = new WW8_SHD[nWwCols]; + memcpy( pSHDs, rBand.pSHDs, nWwCols * sizeof( WW8_SHD ) ); + } + if( rBand.pNewSHDs ) + { + pNewSHDs = new Color[nWwCols]; + memcpy(pNewSHDs, rBand.pNewSHDs, nWwCols * sizeof(Color)); + } + memcpy(aDefBrcs, rBand.aDefBrcs, sizeof(aDefBrcs)); +} + +// ReadDef reads the cell position and the borders of a band +void WW8TabBandDesc::ReadDef(bool bVer67, const sal_uInt8* pS, short nLen) +{ + if (!bVer67) + { + //the ww8 version of this is unusual in masquerading as a srpm with a + //single byte len arg while it really has a word len arg, after this + //increment nLen is correct to describe the remaining amount of data + pS++; + } + + --nLen; //reduce len by expected nCols arg + if (nLen < 0) + return; + sal_uInt8 nCols = *pS; // number of cells + + if (nCols > MAX_COL) + return; + + nLen -= 2 * (nCols + 1); //reduce len by claimed amount of next x-borders arguments + if (nLen < 0) + return; + + short nOldCols = nWwCols; + nWwCols = nCols; + + const sal_uInt8* pT = &pS[1]; + for (int i = 0; i <= nCols; i++, pT+=2) + nCenter[i] = static_cast<sal_Int16>(SVBT16ToUInt16( pT )); // X-borders + + if( nCols != nOldCols ) // different column count + { + delete[] pTCs; + pTCs = nullptr; + delete[] pSHDs; + pSHDs = nullptr; + delete[] pNewSHDs; + pNewSHDs = nullptr; + } + + short nFileCols = nLen / ( bVer67 ? 10 : 20 ); // really saved + + if (!pTCs && nCols) + { + // create empty TCs + pTCs = new WW8_TCell[nCols]; + } + + short nColsToRead = std::min<short>(nFileCols, nCols); + + if (nColsToRead > 0) + { + // read TCs + + /* + Attention: Beginning with Ver8 there is an extra ushort per TC + added and the size of the border code is doubled. + Because of this a simple copy (pTCs[i] = *pTc;) + is not possible. + --- + Advantage: The work structure suits better. + */ + WW8_TCell* pCurrentTC = pTCs; + if( bVer67 ) + { + WW8_TCellVer6 const * pTc = reinterpret_cast<WW8_TCellVer6 const *>(pT); + for (int i = 0; i < nColsToRead; i++, ++pCurrentTC,++pTc) + { + // TC from file ? + sal_uInt8 aBits1 = pTc->aBits1Ver6; + pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x01 ) != 0 ); + pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x02 ) != 0 ); + pCurrentTC->rgbrc[ WW8_TOP ] + = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_TOP ] )); + pCurrentTC->rgbrc[ WW8_LEFT ] + = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_LEFT ] )); + pCurrentTC->rgbrc[ WW8_BOT ] + = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_BOT ] )); + pCurrentTC->rgbrc[ WW8_RIGHT ] + = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] )); + if( ( pCurrentTC->bMerged ) + && ( i > 0 ) ) + { + // Cell merged -> remember + //bWWMergedVer6[i] = true; + pTCs[i-1].rgbrc[ WW8_RIGHT ] + = WW8_BRCVer9(WW8_BRC( pTc->rgbrcVer6[ WW8_RIGHT ] )); + // apply right border to previous cell + // bExist must not be set to false, because WW + // does not count this cells in text boxes... + } + } + } + else + { + WW8_TCellVer8 const * pTc = reinterpret_cast<WW8_TCellVer8 const *>(pT); + for (int k = 0; k < nColsToRead; ++k, ++pCurrentTC, ++pTc ) + { + sal_uInt16 aBits1 = SVBT16ToUInt16( pTc->aBits1Ver8 ); + pCurrentTC->bFirstMerged = sal_uInt8( ( aBits1 & 0x0001 ) != 0 ); + pCurrentTC->bMerged = sal_uInt8( ( aBits1 & 0x0002 ) != 0 ); + pCurrentTC->bVertical = sal_uInt8( ( aBits1 & 0x0004 ) != 0 ); + pCurrentTC->bBackward = sal_uInt8( ( aBits1 & 0x0008 ) != 0 ); + pCurrentTC->bRotateFont = sal_uInt8( ( aBits1 & 0x0010 ) != 0 ); + pCurrentTC->bVertMerge = sal_uInt8( ( aBits1 & 0x0020 ) != 0 ); + pCurrentTC->bVertRestart = sal_uInt8( ( aBits1 & 0x0040 ) != 0 ); + pCurrentTC->nVertAlign = ( ( aBits1 & 0x0180 ) >> 7 ); + // note: in aBits1 there are 7 bits unused, + // followed by another 16 unused bits + + pCurrentTC->rgbrc[ WW8_TOP ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_TOP ]); + pCurrentTC->rgbrc[ WW8_LEFT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_LEFT ]); + pCurrentTC->rgbrc[ WW8_BOT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_BOT ]); + pCurrentTC->rgbrc[ WW8_RIGHT ] = WW8_BRCVer9(pTc->rgbrcVer8[ WW8_RIGHT ]); + } + } + + // #i25071 In '97 text direction appears to be only set using TC properties + // not with sprmTTextFlow so we need to cycle through the maDirections and + // double check any non-default directions + for (int k = 0; k < nCols; ++k) + { + if(maDirections[k] == 4) + { + if(pTCs[k].bVertical) + { + if(pTCs[k].bBackward) + maDirections[k] = 3; + else + maDirections[k] = 1; + } + } + } + } +} + +void WW8TabBandDesc::ProcessSprmTSetBRC(int nBrcVer, const sal_uInt8* pParamsTSetBRC, sal_uInt16 nParamsLen) +{ + if( !pParamsTSetBRC || !pTCs ) // set one or more cell border(s) + return; + + if (nParamsLen < 3) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + + sal_uInt8 nitcFirst= pParamsTSetBRC[0];// first col to be changed + sal_uInt8 nitcLim = pParamsTSetBRC[1];// (last col to be changed)+1 + sal_uInt8 nFlag = *(pParamsTSetBRC+2); + + if (nitcFirst >= nWwCols) + return; + + if (nitcLim > nWwCols) + nitcLim = nWwCols; + + bool bChangeRight = (nFlag & 0x08) != 0; + bool bChangeBottom = (nFlag & 0x04) != 0; + bool bChangeLeft = (nFlag & 0x02) != 0; + bool bChangeTop = (nFlag & 0x01) != 0; + + WW8_TCell* pCurrentTC = pTCs + nitcFirst; + WW8_BRCVer9 brcVer9; + if( nBrcVer == 6 ) + { + if (nParamsLen < sizeof(WW8_BRCVer6) + 3) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + brcVer9 = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<WW8_BRCVer6 const *>(pParamsTSetBRC+3))); + } + else if( nBrcVer == 8 ) + { + static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size"); + if (nParamsLen < sizeof(WW8_BRC) + 3) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + brcVer9 = WW8_BRCVer9(*reinterpret_cast<WW8_BRC const *>(pParamsTSetBRC+3)); + } + else + { + if (nParamsLen < sizeof(WW8_BRCVer9) + 3) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + brcVer9 = *reinterpret_cast<WW8_BRCVer9 const *>(pParamsTSetBRC+3); + } + + for( int i = nitcFirst; i < nitcLim; ++i, ++pCurrentTC ) + { + if( bChangeTop ) + pCurrentTC->rgbrc[ WW8_TOP ] = brcVer9; + if( bChangeLeft ) + pCurrentTC->rgbrc[ WW8_LEFT ] = brcVer9; + if( bChangeBottom ) + pCurrentTC->rgbrc[ WW8_BOT ] = brcVer9; + if( bChangeRight ) + pCurrentTC->rgbrc[ WW8_RIGHT ] = brcVer9; + } +} + +void WW8TabBandDesc::ProcessSprmTTableBorders(int nBrcVer, const sal_uInt8* pParams, sal_uInt16 nParamsLen) +{ + // sprmTTableBorders + if( nBrcVer == 6 ) + { + if (nParamsLen < sizeof(WW8_BRCVer6) * 6) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + WW8_BRCVer6 const *pVer6 = reinterpret_cast<WW8_BRCVer6 const *>(pParams); + for (int i = 0; i < 6; ++i) + aDefBrcs[i] = WW8_BRCVer9(WW8_BRC(pVer6[i])); + } + else if ( nBrcVer == 8 ) + { + static_assert(sizeof (WW8_BRC) == 4, "this has to match the msword size"); + if (nParamsLen < sizeof(WW8_BRC) * 6) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + for( int i = 0; i < 6; ++i ) + aDefBrcs[i] = WW8_BRCVer9(reinterpret_cast<WW8_BRC const *>(pParams)[i]); + } + else + { + if (nParamsLen < sizeof( aDefBrcs )) + { + SAL_WARN("sw.ww8", "table border property is too short"); + return; + } + memcpy( aDefBrcs, pParams, sizeof( aDefBrcs ) ); + } +} + +void WW8TabBandDesc::ProcessSprmTDxaCol(const sal_uInt8* pParamsTDxaCol) +{ + // sprmTDxaCol (opcode 0x7623) changes the width of cells + // whose index is within a certain range to be a certain value. + + if( nWwCols && pParamsTDxaCol ) // set one or more cell length(s) + { + sal_uInt8 nitcFirst= pParamsTDxaCol[0]; // first col to be changed + sal_uInt8 nitcLim = pParamsTDxaCol[1]; // (last col to be changed)+1 + short nDxaCol = static_cast<sal_Int16>(SVBT16ToUInt16( pParamsTDxaCol + 2 )); + + for( int i = nitcFirst; (i < nitcLim) && (i < nWwCols); i++ ) + { + const short nOrgWidth = nCenter[i+1] - nCenter[i]; + const short nDelta = nDxaCol - nOrgWidth; + for( int j = i+1; j <= nWwCols; j++ ) + { + nCenter[j] = nCenter[j] + nDelta; + } + } + } +} + +void WW8TabBandDesc::ProcessSprmTInsert(const sal_uInt8* pParamsTInsert) +{ + if( !nWwCols || !pParamsTInsert ) // set one or more cell length(s) + return; + + sal_uInt8 nitcInsert = pParamsTInsert[0]; // position at which to insert + if (nitcInsert >= MAX_COL) // cannot insert into cell outside max possible index + return; + sal_uInt8 nctc = pParamsTInsert[1]; // number of cells + sal_uInt16 ndxaCol = SVBT16ToUInt16( pParamsTInsert+2 ); + + short nNewWwCols; + if (nitcInsert > nWwCols) + { + nNewWwCols = nitcInsert+nctc; + //if new count would be outside max possible count, clip it, and calc a new replacement + //legal nctc + if (nNewWwCols > MAX_COL) + { + nNewWwCols = MAX_COL; + nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nitcInsert); + } + } + else + { + nNewWwCols = nWwCols+nctc; + //if new count would be outside max possible count, clip it, and calc a new replacement + //legal nctc + if (nNewWwCols > MAX_COL) + { + nNewWwCols = MAX_COL; + nctc = ::sal::static_int_cast<sal_uInt8>(nNewWwCols-nWwCols); + } + } + + WW8_TCell *pTC2s = new WW8_TCell[nNewWwCols]; + + if (pTCs) + { + memcpy( pTC2s, pTCs, nWwCols * sizeof( WW8_TCell ) ); + delete[] pTCs; + } + pTCs = pTC2s; + + //If we have to move some cells + if (nitcInsert <= nWwCols) + { + // adjust the left x-position of the dummy at the very end + nCenter[nWwCols + nctc] = nCenter[nWwCols]+nctc*ndxaCol; + for( int i = nWwCols-1; i >= nitcInsert; i--) + { + // adjust the left x-position + nCenter[i + nctc] = nCenter[i]+nctc*ndxaCol; + + // adjust the cell's borders + pTCs[i + nctc] = pTCs[i]; + } + } + + //if itcMac is larger than full size, fill in missing ones first + for( int i = nWwCols; i > nitcInsert+nWwCols; i--) + nCenter[i] = i ? (nCenter[i - 1]+ndxaCol) : 0; + + //now add in our new cells + for( int j = 0;j < nctc; j++) + nCenter[j + nitcInsert] = (j + nitcInsert) ? (nCenter[j + nitcInsert -1]+ndxaCol) : 0; + + nWwCols = nNewWwCols; + +} + +void WW8TabBandDesc::ProcessDirection(const sal_uInt8* pParams) +{ + sal_uInt8 nStartCell = *pParams++; + sal_uInt8 nEndCell = *pParams++; + sal_uInt16 nCode = SVBT16ToUInt16(pParams); + + OSL_ENSURE(nStartCell < nEndCell, "not as I thought"); + OSL_ENSURE(nEndCell < MAX_COL + 1, "not as I thought"); + if (nStartCell > MAX_COL) + return; + if (nEndCell > MAX_COL + 1) + nEndCell = MAX_COL + 1; + + for (;nStartCell < nEndCell; ++nStartCell) + maDirections[nStartCell] = nCode; +} + +void WW8TabBandDesc::ProcessSpacing(const sal_uInt8* pParams) +{ + sal_uInt8 nLen = pParams ? *(pParams - 1) : 0; + OSL_ENSURE(nLen == 6, "Unexpected spacing len"); + if (nLen != 6) + return; + mbHasSpacing=true; +#if OSL_DEBUG_LEVEL > 0 + sal_uInt8 nWhichCell = *pParams; + OSL_ENSURE(nWhichCell == 0, "Expected cell to be 0!"); +#endif + ++pParams; //Skip which cell + ++pParams; //unknown byte + + sal_uInt8 nSideBits = *pParams++; + OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits"); + ++pParams; //unknown byte + sal_uInt16 nValue = SVBT16ToUInt16( pParams ); + for (int i = wwTOP; i <= wwRIGHT; i++) + { + switch (nSideBits & (1 << i)) + { + case 1 << wwTOP: + mnDefaultTop = nValue; + break; + case 1 << wwLEFT: + mnDefaultLeft = nValue; + break; + case 1 << wwBOTTOM: + mnDefaultBottom = nValue; + break; + case 1 << wwRIGHT: + mnDefaultRight = nValue; + break; + case 0: + break; + default: + OSL_ENSURE(false, "Impossible"); + break; + } + } +} + +void WW8TabBandDesc::ProcessSpecificSpacing(const sal_uInt8* pParams) +{ + sal_uInt8 nLen = pParams ? *(pParams - 1) : 0; + OSL_ENSURE(nLen == 6, "Unexpected spacing len"); + if (nLen != 6) + return; + + const sal_uInt8 nStartCell = *pParams++; // The first cell these margins could apply to. + const sal_uInt8 nEndCell = *pParams++; // The cell that does NOT apply these margins. + OSL_ENSURE(nStartCell < MAX_COL + 1, "Cell out of range in spacings"); + if ( nStartCell >= nEndCell || nEndCell > MAX_COL+1 ) + return; + + sal_uInt8 nSideBits = *pParams++; + OSL_ENSURE(nSideBits < 0x10, "Unexpected value for nSideBits"); + + const sal_uInt8 nSizeType = *pParams++; // Fts: FtsDxa(0x3) is the only type that mentions cellMargin + OSL_ENSURE(nSizeType == 0x3, "Unexpected non-twip value for margin width"); + if ( nSizeType != 0x3 ) // i.e FtsNil: The size is wrong (or unconverted) and MUST be ignored + return; + + sal_uInt16 nValue = SVBT16ToUInt16( pParams ); + + for (int nCell = nStartCell; nCell < nEndCell; ++nCell) + { + nOverrideSpacing[ nCell ] |= nSideBits; + OSL_ENSURE(nOverrideSpacing[ nCell ] < 0x10, "Unexpected value for nSideBits"); + + for (int i=0; i < 4; i++) + { + if (nSideBits & (1 << i)) + nOverrideValues[ nCell ][ i ] = nValue; + } + } +} + +void WW8TabBandDesc::ProcessSprmTDelete(const sal_uInt8* pParamsTDelete) +{ + if( nWwCols && pParamsTDelete ) // set one or more cell length(s) + { + sal_uInt8 nitcFirst= pParamsTDelete[0]; // first col to be deleted + if (nitcFirst >= nWwCols) // first index to delete from doesn't exist + return; + sal_uInt8 nitcLim = pParamsTDelete[1]; // (last col to be deleted)+1 + if (nitcLim <= nitcFirst) // second index to delete to is not greater than first index + return; + + /* + * sprmTDelete causes any rgdxaCenter and rgtc entries whose index is + * greater than or equal to itcLim to be moved + */ + int nShlCnt = nWwCols - nitcLim; // count of cells to be shifted + + if (nShlCnt >= 0) //There exist entries whose index is greater than or equal to itcLim + { + WW8_TCell* pCurrentTC = pTCs + nitcFirst; + int i = 0; + while( i < nShlCnt ) + { + // adjust the left x-position + nCenter[nitcFirst + i] = nCenter[nitcLim + i]; + + // adjust the cell's borders + *pCurrentTC = pTCs[ nitcLim + i]; + + ++i; + ++pCurrentTC; + } + // adjust the left x-position of the dummy at the very end + nCenter[nitcFirst + i] = nCenter[nitcLim + i]; + } + + short nCellsDeleted = nitcLim - nitcFirst; + //clip delete request to available number of cells + if (nCellsDeleted > nWwCols) + nCellsDeleted = nWwCols; + nWwCols -= nCellsDeleted; + } +} + +// ReadShd reads the background color of a cell +// ReadDef must be called before +void WW8TabBandDesc::ReadShd(const sal_uInt8* pS ) +{ + sal_uInt8 nLen = pS ? *(pS - 1) : 0; + if( !nLen ) + return; + + if( !pSHDs ) + { + pSHDs = new WW8_SHD[nWwCols]; + } + + short nCount = nLen >> 1; + if (nCount > nWwCols) + nCount = nWwCols; + + SVBT16 const * pShd; + int i; + for(i=0, pShd = reinterpret_cast<SVBT16 const *>(pS); i<nCount; i++, pShd++ ) + pSHDs[i].SetWWValue( *pShd ); +} + +void WW8TabBandDesc::ReadNewShd(const sal_uInt8* pS, bool bVer67) +{ + sal_uInt8 nLen = pS ? *(pS - 1) : 0; + if (!nLen) + return; + + if (!pNewSHDs) + pNewSHDs = new Color[nWwCols]; + + short nCount = nLen / 10; //10 bytes each + if (nCount > nWwCols) + nCount = nWwCols; + + int i=0; + while (i < nCount) + pNewSHDs[i++] = SwWW8ImplReader::ExtractColour(pS, bVer67); + + while (i < nWwCols) + pNewSHDs[i++] = COL_AUTO; +} + +namespace +{ + SprmResult HasTabCellSprm(WW8PLCFx_Cp_FKP* pPap, bool bVer67) + { + if (bVer67) + return pPap->HasSprm(24); + SprmResult aRes = pPap->HasSprm(0x244B); + if (aRes.pSprm == nullptr) + aRes = pPap->HasSprm(0x2416); + return aRes; + } +} + +namespace { + +enum wwTableSprm +{ + sprmNil, + + sprmTTableWidth, sprmTTextFlow, sprmTFCantSplit, sprmTJc, sprmTFBiDi, + sprmTDefTable, sprmTDyaRowHeight, sprmTDefTableShd, sprmTDxaLeft, + sprmTSetBrc, sprmTSetBrc90, sprmTDxaCol, sprmTInsert, sprmTDelete, + sprmTTableHeader, sprmTDxaGapHalf, sprmTTableBorders, sprmTTableBorders90, + sprmTDefTableNewShd, sprmTCellPadding, sprmTCellPaddingDefault +}; + +} + +static wwTableSprm GetTableSprm(sal_uInt16 nId, ww::WordVersion eVer) +{ + switch (eVer) + { + case ww::eWW8: + switch (nId) + { + case NS_sprm::sprmTTableWidth: + return sprmTTableWidth; + case NS_sprm::sprmTTextFlow: + return sprmTTextFlow; + case NS_sprm::sprmTTableHeader: + return sprmTTableHeader; + case NS_sprm::sprmTFCantSplit: + return sprmTFCantSplit; + case NS_sprm::sprmTJc90: + return sprmTJc; + case NS_sprm::sprmTFBiDi: + return sprmTFBiDi; + case NS_sprm::sprmTDelete: + return sprmTDelete; + case NS_sprm::sprmTInsert: + return sprmTInsert; + case NS_sprm::sprmTDxaCol: + return sprmTDxaCol; + case NS_sprm::sprmTDyaRowHeight: + return sprmTDyaRowHeight; + case NS_sprm::sprmTDxaLeft: + return sprmTDxaLeft; + case NS_sprm::sprmTDxaGapHalf: + return sprmTDxaGapHalf; + case NS_sprm::sprmTTableBorders80: + return sprmTTableBorders; + case NS_sprm::sprmTDefTable: + return sprmTDefTable; + case NS_sprm::sprmTDefTableShd80: + return sprmTDefTableShd; + case NS_sprm::sprmTDefTableShd: + return sprmTDefTableNewShd; + case NS_sprm::sprmTTableBorders: + return sprmTTableBorders90; + case NS_sprm::sprmTSetBrc80: + return sprmTSetBrc; + case NS_sprm::sprmTSetBrc: + return sprmTSetBrc90; + case NS_sprm::sprmTCellPadding: + return sprmTCellPadding; + case NS_sprm::sprmTCellPaddingDefault: + return sprmTCellPaddingDefault; + } + break; + case ww::eWW7: + case ww::eWW6: + switch (nId) + { + case 182: + return sprmTJc; + case 183: + return sprmTDxaLeft; + case 184: + return sprmTDxaGapHalf; + case 186: + return sprmTTableHeader; + case 187: + return sprmTTableBorders; + case 189: + return sprmTDyaRowHeight; + case 190: + return sprmTDefTable; + case 191: + return sprmTDefTableShd; + case 193: + return sprmTSetBrc; + case 194: + return sprmTInsert; + case 195: + return sprmTDelete; + case 196: + return sprmTDxaCol; + } + break; + case ww::eWW1: + case ww::eWW2: + switch (nId) + { + case 146: + return sprmTJc; + case 147: + return sprmTDxaLeft; + case 148: + return sprmTDxaGapHalf; + case 153: + return sprmTDyaRowHeight; + case 154: + return sprmTDefTable; + case 155: + return sprmTDefTableShd; + case 157: + return sprmTSetBrc; + case 158: + return sprmTInsert; + case 159: + return sprmTDelete; + case 160: + return sprmTDxaCol; + } + break; + } + return sprmNil; +} + +WW8TabDesc::WW8TabDesc(SwWW8ImplReader* pIoClass, WW8_CP nStartCp) : + m_pIo(pIoClass), + m_pFirstBand(nullptr), + m_pActBand(nullptr), + m_pTableNd(nullptr), + m_pTabLines(nullptr), + m_pTabLine(nullptr), + m_pTabBoxes(nullptr), + m_pTabBox(nullptr), + m_pCurrentWWCell(nullptr), + m_nRows(0), + m_nDefaultSwCols(0), + m_nBands(0), + m_nMinLeft(0), + m_nConvertedLeft(0), + m_nMaxRight(0), + m_nSwWidth(0), + m_nPreferredWidth(0), + m_nPercentWidth(0), + m_bOk(true), + m_bClaimLineFormat(false), + m_eOri(text::HoriOrientation::LEFT), + m_bIsBiDi(false), + m_nCurrentRow(0), + m_nCurrentBandRow(0), + m_nCurrentCol(0), + m_nRowsToRepeat(0), + m_pTable(nullptr), + m_pParentPos(nullptr), + m_pFlyFormat(nullptr), + m_aItemSet(m_pIo->m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>{}) +{ + m_pIo->m_bCurrentAND_fNumberAcross = false; + + static const sal_Int16 aOriArr[] = + { + text::HoriOrientation::LEFT, text::HoriOrientation::CENTER, text::HoriOrientation::RIGHT, text::HoriOrientation::CENTER + }; + + bool bOldVer = ww::IsSevenMinus(m_pIo->GetFib().GetFIBVersion()); + WW8_TablePos aTabPos; + + WW8PLCFxSave1 aSave; + m_pIo->m_xPlcxMan->GetPap()->Save( aSave ); + + WW8PLCFx_Cp_FKP* pPap = m_pIo->m_xPlcxMan->GetPapPLCF(); + + WW8TabBandDesc* pNewBand = new WW8TabBandDesc; + + wwSprmParser aSprmParser(m_pIo->GetFib()); + + // process pPap until end of table found + do + { + short nTabeDxaNew = SHRT_MAX; + bool bTabRowJustRead = false; + const sal_uInt8* pShadeSprm = nullptr; + const sal_uInt8* pNewShadeSprm = nullptr; + const sal_uInt8* pTableBorders = nullptr; + sal_uInt16 nTableBordersLen = 0; + const sal_uInt8* pTableBorders90 = nullptr; + sal_uInt16 nTableBorders90Len = 0; + // params, len + std::vector<std::pair<const sal_uInt8*, sal_uInt16>> aTSetBrcs, aTSetBrc90s; + WW8_TablePos *pTabPos = nullptr; + + // search end of a tab row + if(!(m_pIo->SearchRowEnd(pPap, nStartCp, m_pIo->m_nInTable))) + { + m_bOk = false; + break; + } + + // Get the SPRM chains: + // first from PAP and then from PCD (of the Piece Table) + WW8PLCFxDesc aDesc; + pPap->GetSprms( &aDesc ); + WW8SprmIter aSprmIter(aDesc.pMemPos, aDesc.nSprmsLen, aSprmParser); + + for (int nLoop = 0; nLoop < 2; ++nLoop) + { + const sal_uInt8* pParams; + while (aSprmIter.GetSprms() && nullptr != (pParams = aSprmIter.GetCurrentParams())) + { + sal_uInt16 nId = aSprmIter.GetCurrentId(); + sal_uInt16 nFixedLen = aSprmParser.DistanceToData(nId); + sal_uInt16 nL = aSprmParser.GetSprmSize(nId, aSprmIter.GetSprms(), aSprmIter.GetRemLen()); + sal_uInt16 nLen = nL - nFixedLen; + wwTableSprm eSprm = GetTableSprm(nId, m_pIo->GetFib().GetFIBVersion()); + switch (eSprm) + { + case sprmTTableWidth: + { + const sal_uInt8 b0 = pParams[0]; + const sal_uInt8 b1 = pParams[1]; + const sal_uInt8 b2 = pParams[2]; + if (b0 == 3) // Twips + m_nPreferredWidth = b2 * 0x100 + b1; + else if (b0 == 2) // percent in fiftieths of a percent + { + m_nPercentWidth = (b2 * 0x100 + b1); + // MS documentation: non-negative, and 600% max + if ( m_nPercentWidth >= 0 && m_nPercentWidth <= 30000 ) + m_nPercentWidth *= .02; + else + m_nPercentWidth = 100; + } + } + break; + case sprmTTextFlow: + pNewBand->ProcessDirection(pParams); + break; + case sprmTFCantSplit: + pNewBand->bCantSplit = *pParams; + m_bClaimLineFormat = true; + break; + case sprmTTableBorders: + pTableBorders = pParams; // process at end + nTableBordersLen = nLen; + break; + case sprmTTableBorders90: + pTableBorders90 = pParams; // process at end + nTableBorders90Len = nLen; + break; + case sprmTTableHeader: + // tdf#105570 + if ( m_nRowsToRepeat == m_nRows ) + m_nRowsToRepeat = (m_nRows + 1); + break; + case sprmTJc: + // sprmTJc - Justification Code + if (m_nRows == 0) + m_eOri = aOriArr[*pParams & 0x3]; + break; + case sprmTFBiDi: + m_bIsBiDi = SVBT16ToUInt16(pParams) != 0; + break; + case sprmTDxaGapHalf: + pNewBand->nGapHalf = static_cast<sal_Int16>(SVBT16ToUInt16( pParams )); + break; + case sprmTDyaRowHeight: + pNewBand->nLineHeight = static_cast<sal_Int16>(SVBT16ToUInt16( pParams )); + m_bClaimLineFormat = true; + break; + case sprmTDefTable: + pNewBand->ReadDef(bOldVer, pParams, nLen); + bTabRowJustRead = true; + break; + case sprmTDefTableShd: + pShadeSprm = pParams; + break; + case sprmTDefTableNewShd: + pNewShadeSprm = pParams; + break; + case sprmTDxaLeft: + // our Writer cannot shift single table lines + // horizontally so we have to find the smallest + // parameter (meaning the left-most position) and then + // shift the whole table to that margin (see below) + { + short nDxaNew = static_cast<sal_Int16>(SVBT16ToUInt16( pParams )); + if( nDxaNew < nTabeDxaNew ) + nTabeDxaNew = nDxaNew; + } + break; + case sprmTSetBrc: + aTSetBrcs.emplace_back(pParams, nLen); // process at end + break; + case sprmTSetBrc90: + aTSetBrc90s.emplace_back(pParams, nLen); // process at end + break; + case sprmTDxaCol: + pNewBand->ProcessSprmTDxaCol(pParams); + break; + case sprmTInsert: + pNewBand->ProcessSprmTInsert(pParams); + break; + case sprmTDelete: + pNewBand->ProcessSprmTDelete(pParams); + break; + case sprmTCellPaddingDefault: + pNewBand->ProcessSpacing(pParams); + break; + case sprmTCellPadding: + pNewBand->ProcessSpecificSpacing(pParams); + break; + default: + ; + } + aSprmIter.advance(); + } + + if( !nLoop ) + { + pPap->GetPCDSprms( aDesc ); + aSprmIter.SetSprms( aDesc.pMemPos, aDesc.nSprmsLen ); + } + } + + // WW-Tables can contain Fly-changes. For this abort tables here + // and start again. *pPap is still before TabRowEnd, so TestApo() + // can be called with the last parameter set to false and therefore + // take effect. + + if (bTabRowJustRead) + { + // Some SPRMs need to be processed *after* ReadDef is called + // so they were saved up until here + if (pShadeSprm) + pNewBand->ReadShd(pShadeSprm); + if (pNewShadeSprm) + pNewBand->ReadNewShd(pNewShadeSprm, bOldVer); + if (pTableBorders90) + pNewBand->ProcessSprmTTableBorders(9, pTableBorders90, nTableBorders90Len); + else if (pTableBorders) + pNewBand->ProcessSprmTTableBorders(bOldVer ? 6 : 8, + pTableBorders, nTableBordersLen); + for (const auto& a : aTSetBrcs) + pNewBand->ProcessSprmTSetBRC(bOldVer ? 6 : 8, a.first, a.second); + for (const auto& a : aTSetBrc90s) + pNewBand->ProcessSprmTSetBRC(9, a.first, a.second); + } + + if( nTabeDxaNew < SHRT_MAX ) + { + short* pCenter = pNewBand->nCenter; + short firstDxaCenter = *pCenter; + for( int i = 0; i < pNewBand->nWwCols; i++, ++pCenter ) + { + // #i30298# Use sprmTDxaLeft to adjust the left indent + // #i40461# Use dxaGapHalf during calculation + *pCenter += + (nTabeDxaNew - (firstDxaCenter + pNewBand->nGapHalf)); + } + } + + if (!m_pActBand) + m_pActBand = m_pFirstBand = pNewBand; + else + { + m_pActBand->pNextBand = pNewBand; + m_pActBand = pNewBand; + } + m_nBands++; + + pNewBand = new WW8TabBandDesc; + + m_nRows++; + m_pActBand->nRows++; + + //Seek our pap to its next block of properties + WW8PLCFxDesc aRes; + aRes.pMemPos = nullptr; + aRes.nStartPos = nStartCp; + + if (!(pPap->SeekPos(aRes.nStartPos))) + { + aRes.nEndPos = WW8_CP_MAX; + pPap->SetDirty(true); + } + pPap->GetSprms(&aRes); + pPap->SetDirty(false); + + //Are we at the end of available properties + if ( + !pPap->HasFkp() || pPap->Where() == WW8_CP_MAX || + aRes.nStartPos == WW8_CP_MAX + ) + { + m_bOk = false; + break; + } + + //Are we still in a table cell + SprmResult aParamsRes = HasTabCellSprm(pPap, bOldVer); + const sal_uInt8* pParams = aParamsRes.pSprm; + SprmResult aLevelRes = pPap->HasSprm(0x6649); + const sal_uInt8 *pLevel = aLevelRes.pSprm; + // InTable + if (!pParams || aParamsRes.nRemainingData < 1 || (1 != *pParams) || + (pLevel && aLevelRes.nRemainingData >= 1 && (*pLevel <= m_pIo->m_nInTable))) + { + break; + } + + //Get the end of row new table positioning data + WW8_CP nMyStartCp=nStartCp; + if (m_pIo->SearchRowEnd(pPap, nMyStartCp, m_pIo->m_nInTable)) + if (m_pIo->ParseTabPos(&aTabPos, pPap)) + pTabPos = &aTabPos; + + //Move back to this cell + aRes.pMemPos = nullptr; + aRes.nStartPos = nStartCp; + + // PlcxMan currently points too far ahead so we need to bring + // it back to where we are trying to make a table + m_pIo->m_xPlcxMan->GetPap()->nOrigStartPos = aRes.nStartPos; + m_pIo->m_xPlcxMan->GetPap()->nCpOfs = aRes.nCpOfs; + if (!(pPap->SeekPos(aRes.nStartPos))) + { + aRes.nEndPos = WW8_CP_MAX; + pPap->SetDirty(true); + } + pPap->GetSprms(&aRes); + pPap->SetDirty(false); + + //Does this row match up with the last row closely enough to be + //considered part of the same table + ApoTestResults aApo = m_pIo->TestApo(m_pIo->m_nInTable + 1, false, pTabPos); + + /* + ##513##, #79474# If this is not sufficient, then we should look at + sprmPD{y|x}aAbs as our indicator that the following set of rows is not + part of this table, but instead is an absolutely positioned table + outside of this one + */ + if (aApo.mbStopApo) + break; + if (aApo.mbStartApo) + { + //if there really is a fly here, and not a "null" fly then break. + if (m_pIo->ConstructApo(aApo, pTabPos)) + break; + } + + if (nStartCp == aRes.nEndPos) + { + SAL_WARN("sw.ww8", "WW8TabDesc End same as Start, abandoning to avoid looping"); + break; + } + nStartCp = aRes.nEndPos; + } + while(true); + + if( m_bOk ) + { + if( m_pActBand->nRows > 1 ) + { + // last band has more than 1 cell + delete pNewBand; + pNewBand = new WW8TabBandDesc( *m_pActBand ); // create new + m_pActBand->nRows--; // because of special treatment of border defaults + pNewBand->nRows = 1; + m_pActBand->pNextBand = pNewBand; // append at the end + m_nBands++; + pNewBand = nullptr; // do not delete + } + CalcDefaults(); + } + delete pNewBand; + + m_pIo->m_xPlcxMan->GetPap()->Restore( aSave ); +} + +WW8TabDesc::~WW8TabDesc() +{ + WW8TabBandDesc* pR = m_pFirstBand; + while(pR) + { + WW8TabBandDesc* pR2 = pR->pNextBand; + delete pR; + pR = pR2; + } + + delete m_pParentPos; +} + +void WW8TabDesc::CalcDefaults() +{ + short nMinCols = SHRT_MAX; + WW8TabBandDesc* pR; + + m_nMinLeft = SHRT_MAX; + m_nMaxRight = SHRT_MIN; + + /* + If we are an honestly inline centered table, then the normal rules of + engagement for left and right margins do not apply. The multiple rows are + centered regardless of the actual placement of rows, so we cannot have + mismatched rows as is possible in other configurations. + + e.g. change the example bugdoc in word from text wrapping of none (inline) + to around (in frame (bApo)) and the table splits into two very disjoint + rows as the beginning point of each row are very different + */ + if ((!m_pIo->InLocalApo()) && (m_eOri == text::HoriOrientation::CENTER)) + { + for (pR = m_pFirstBand; pR; pR = pR->pNextBand) + for( short i = pR->nWwCols; i >= 0; --i) + pR->nCenter[i] = pR->nCenter[i] - pR->nCenter[0]; + } + + // First loop: find outermost L and R borders + for( pR = m_pFirstBand; pR; pR = pR->pNextBand ) + { + if( pR->nCenter[0] < m_nMinLeft ) + m_nMinLeft = pR->nCenter[0]; + + // Following adjustment moves a border and then uses it to find width + // of next cell, so collect current widths, to avoid situation when width + // adjustment to too narrow cell makes next cell have negative width + short nOrigWidth[MAX_COL + 1]; + for( short i = 0; i < pR->nWwCols; i++ ) + { + nOrigWidth[i] = pR->nCenter[i+1] - pR->nCenter[i]; + } + + for( short i = 0; i < pR->nWwCols; i++ ) + { + /* + If the margins are so large as to make the displayable + area inside them smaller than the minimum allowed then adjust the + width to fit. But only do it if the two cells are not the exact + same value, if they are then the cell does not really exist and will + be blended together into the same cell through the use of the + nTrans(late) array. + #i28333# If the nGapHalf is greater than the cell width best to ignore it + */ + int nCellWidth = pR->nCenter[i+1] - pR->nCenter[i]; + if (nCellWidth != nOrigWidth[i]) + { + if (nOrigWidth[i] == 0) + nCellWidth = 0; // restore zero-width "cell" + else if ((pR->nGapHalf >= nCellWidth) && (pR->nGapHalf < nOrigWidth[i])) + nCellWidth = pR->nGapHalf + 1; // avoid false ignore + else if ((nCellWidth <= 0) && (nOrigWidth[i] > 0)) + nCellWidth = 1; // minimal non-zero width to minimize distortion + } + if (nCellWidth && ((nCellWidth - pR->nGapHalf*2) < MINLAY) && pR->nGapHalf < nCellWidth) + { + nCellWidth = MINLAY + pR->nGapHalf * 2; + } + pR->nCenter[i + 1] = pR->nCenter[i] + nCellWidth; + } + + if( pR->nCenter[pR->nWwCols] > m_nMaxRight ) + m_nMaxRight = pR->nCenter[pR->nWwCols]; + } + m_nSwWidth = m_nMaxRight - m_nMinLeft; + + // If the table is right aligned we need to align all rows to the + // row that has the furthest right point + + if(m_eOri == text::HoriOrientation::RIGHT) + { + for( pR = m_pFirstBand; pR; pR = pR->pNextBand ) + { + int adjust = m_nMaxRight - pR->nCenter[pR->nWwCols]; + for( short i = 0; i < pR->nWwCols + 1; i++ ) + { + pR->nCenter[i] = static_cast< short >(pR->nCenter[i] + adjust); + } + + } + } + + // 2. pass: Detect number of writer columns. This can exceed the count + // of columns in WW by 2, because SW in contrast to WW does not provide + // fringed left and right borders and has to fill with empty boxes. + // Non existent cells can reduce the number of columns. + + // 3. pass: Replace border with defaults if needed + m_nConvertedLeft = m_nMinLeft; + + short nLeftMaxThickness = 0, nRightMaxThickness=0; + for( pR = m_pFirstBand ; pR; pR = pR->pNextBand ) + { + if( !pR->pTCs ) + { + pR->pTCs = new WW8_TCell[ pR->nWwCols ]; + } + for (int k = 0; k < pR->nWwCols; ++k) + { + WW8_TCell& rT = pR->pTCs[k]; + for (int i = 0; i < 4; ++i) + { + if (rT.rgbrc[i].brcType()==0) + { + // if shadow is set, its invalid + int j = i; + switch( i ) + { + case 0: + // outer top / horizontally inside + j = (pR == m_pFirstBand) ? 0 : 4; + break; + case 1: + // outer left / vertically inside + j = k ? 5 : 1; + break; + case 2: + // outer bottom / horizontally inside + j = pR->pNextBand ? 4 : 2; + break; + case 3: + // outer right / vertically inside + j = (k == pR->nWwCols - 1) ? 3 : 5; + break; + } + // merge with above defaults + rT.rgbrc[i] = pR->aDefBrcs[j]; + } + } + } + if (pR->nWwCols) + { + /* + Similar to graphics and other elements word does not totally + factor the width of the border into its calculations of size, we + do so we must adjust out widths and other dimensions to fit. It + appears that what occurs is that the last cell's right margin if + the margin width that is not calculated into winwords table + dimensions, so in that case increase the table to include the + extra width of the right margin. + */ + if ( ! pR->pTCs[pR->nWwCols-1].rgbrc[3].fShadow() ) + { + short nThickness = pR->pTCs[pR->nWwCols-1].rgbrc[3]. + DetermineBorderProperties(); + pR->nCenter[pR->nWwCols] = pR->nCenter[pR->nWwCols] + nThickness; + if (nThickness > nRightMaxThickness) + nRightMaxThickness = nThickness; + } + + /* + The left space of the table is in nMinLeft, but again this + does not consider the margin thickness to its left in the + placement value, so get the thickness of the left border, + half is placed to the left of the nominal left side, and + half to the right. + */ + if ( ! pR->pTCs[0].rgbrc[1].fShadow() ) + { + short nThickness = pR->pTCs[0].rgbrc[1]. + DetermineBorderProperties(); + if (nThickness > nLeftMaxThickness) + nLeftMaxThickness = nThickness; + } + } + } + m_nSwWidth = m_nSwWidth + nRightMaxThickness; + m_nMaxRight = m_nMaxRight + nRightMaxThickness; + m_nConvertedLeft = m_nMinLeft-(nLeftMaxThickness/2); + + for( pR = m_pFirstBand; pR; pR = pR->pNextBand ) + { + pR->nSwCols = pR->nWwCols; + pR->bLEmptyCol = pR->nCenter[0] - m_nMinLeft >= MINLAY; + pR->bREmptyCol = (m_nMaxRight - pR->nCenter[pR->nWwCols] - nRightMaxThickness) >= MINLAY; + + short nAddCols = short(pR->bLEmptyCol) + short(pR->bREmptyCol); + sal_uInt16 i; + sal_uInt16 j = ( pR->bLEmptyCol ) ? 1 : 0; + for (i = 0; i < pR->nWwCols; ++i) + { + pR->nTransCell[i] = static_cast<sal_Int8>(j); + if ( pR->nCenter[i] < pR->nCenter[i+1] ) + { + pR->bExist[i] = true; + j++; + } + else + { + pR->bExist[i] = false; + nAddCols--; + } + } + + OSL_ENSURE(i,"no columns in row ?"); + + /* + If the last cell was "false" then there is no valid cell following it, + so the default mapping forward won't work. So map it (and + contiguous invalid cells backwards to the last valid cell instead.) + */ + if (i && !pR->bExist[i-1]) + { + sal_uInt16 k=i-1; + while (k && !pR->bExist[k]) + k--; + for (sal_uInt16 n=k+1;n<i;n++) + pR->nTransCell[n] = pR->nTransCell[k]; + } + + pR->nTransCell[i++] = static_cast<sal_Int8>(j++); // Can exceed by 2 among other + pR->nTransCell[i] = static_cast<sal_Int8>(j); // things because of bREmptyCol + + pR->nSwCols = pR->nSwCols + nAddCols; + if( pR->nSwCols < nMinCols ) + nMinCols = pR->nSwCols; + } + + if ((m_nMinLeft && !m_bIsBiDi && text::HoriOrientation::LEFT == m_eOri) || + (m_nMinLeft != -108 && m_bIsBiDi && text::HoriOrientation::RIGHT == m_eOri)) // Word sets the first nCenter value to -108 when no indent is used + m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; // absolutely positioned + + m_nDefaultSwCols = nMinCols; // because inserting cells is cheaper than merging + if( m_nDefaultSwCols == 0 ) + m_bOk = false; + m_pActBand = m_pFirstBand; + m_nCurrentBandRow = 0; + OSL_ENSURE( m_pActBand, "pActBand is 0" ); +} + +void WW8TabDesc::SetSizePosition(SwFrameFormat* pFrameFormat) +{ + SwFrameFormat* pApply = pFrameFormat; + if (!pApply ) + pApply = m_pTable->GetFrameFormat(); + OSL_ENSURE(pApply,"No frame"); + pApply->SetFormatAttr(m_aItemSet); + if (pFrameFormat) + { + SwFormatFrameSize aSize = pFrameFormat->GetFrameSize(); + aSize.SetHeightSizeType(SwFrameSize::Minimum); + aSize.SetHeight(MINLAY); + pFrameFormat->SetFormatAttr(aSize); + m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatHoriOrient(0,text::HoriOrientation::FULL)); + } +} + +void wwSectionManager::PrependedInlineNode(const SwPosition &rPos, + const SwNode &rNode) +{ + OSL_ENSURE(!maSegments.empty(), + "should not be possible, must be at least one segment"); + if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.nNode)) + maSegments.back().maStart.Assign(rNode); +} + +void WW8TabDesc::CreateSwTable() +{ + ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update + + // if there is already some content on the Node append new node to ensure + // that this content remains ABOVE the table + SwPosition* pPoint = m_pIo->m_pPaM->GetPoint(); + bool bInsNode = pPoint->nContent.GetIndex() != 0; + bool bSetMinHeight = false; + + /* + #i8062# + Set fly anchor to its anchor pos, so that if a table starts immediately + at this position a new node will be inserted before inserting the table. + */ + SwFrameFormat* pFormat = (!bInsNode && m_pIo->m_xFormatOfJustInsertedApo) + ? m_pIo->m_xFormatOfJustInsertedApo->GetFormat() : nullptr; + if (pFormat) + { + const SwPosition* pAPos = + pFormat->GetAnchor().GetContentAnchor(); + if (pAPos && &pAPos->nNode.GetNode() == &pPoint->nNode.GetNode()) + { + bInsNode = true; + bSetMinHeight = true; + + SwFormatSurround aSur(pFormat->GetSurround()); + aSur.SetAnchorOnly(true); + pFormat->SetFormatAttr(aSur); + } + } + + if (bSetMinHeight) + { + // minimize Fontsize to minimize height growth of the header/footer + // set font size to 1 point to minimize y-growth of Hd/Ft + SvxFontHeightItem aSz(20, 100, RES_CHRATR_FONTSIZE); + m_pIo->NewAttr( aSz ); + m_pIo->m_xCtrlStck->SetAttr(*pPoint, RES_CHRATR_FONTSIZE); + } + + if (bInsNode) + m_pIo->AppendTextNode(*pPoint); + + m_xTmpPos.reset(new SwPosition(*m_pIo->m_pPaM->GetPoint())); + + // Because SW cannot handle multi-page floating frames, + // _any unnecessary_ floating tables have been converted to inline. + long nLeft = 0; + if ( m_pIo->m_xSFlyPara && !m_pIo->m_xSFlyPara->pFlyFormat ) + { + // Get the table orientation from the fly + // Do we also need to check m_pIo->m_xSFlyPara->bToggelPos/IsPosToggle()? [Probably not - layout-only concern] + const bool bAdjustMargin = m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME || m_pIo->m_xSFlyPara->nXPos; + const bool bIsInsideMargin = m_bIsBiDi ? m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::RIGHT + : m_pIo->m_xSFlyPara->eHAlign == text::HoriOrientation::LEFT; + if ( bIsInsideMargin && bAdjustMargin ) + m_eOri = text::HoriOrientation::LEFT_AND_WIDTH; + else if ( m_pIo->m_xSFlyPara->eHAlign != text::HoriOrientation::NONE ) + m_eOri = m_pIo->m_xSFlyPara->eHAlign; + if ( m_eOri == text::HoriOrientation::LEFT_AND_WIDTH ) + { + nLeft = m_pIo->m_xSFlyPara->nXPos; + if ( m_pIo->m_xSFlyPara->eHRel == text::RelOrientation::PAGE_FRAME ) + { + if ( !m_bIsBiDi ) + nLeft -= m_pIo->m_aSectionManager.GetPageLeft(); + else + nLeft += m_pIo->m_aSectionManager.GetPageRight(); + } + } + } + + // The table is small: The number of columns is the lowest count of + // columns of the origin, because inserting is faster than deleting. + // The number of rows is the count of bands because (identically) + // rows of a band can be duplicated easy. + m_pTable = m_pIo->m_rDoc.InsertTable( + SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 0 ), + *m_xTmpPos, m_nBands, m_nDefaultSwCols, m_eOri ); + + OSL_ENSURE(m_pTable && m_pTable->GetFrameFormat(), "insert table failed"); + if (!m_pTable || !m_pTable->GetFrameFormat()) + return; + + SwTableNode* pTableNode = m_pTable->GetTableNode(); + OSL_ENSURE(pTableNode, "no table node!"); + if (pTableNode) + { + m_pIo->m_aSectionManager.PrependedInlineNode(*m_pIo->m_pPaM->GetPoint(), + *pTableNode); + } + + // Check if the node into which the table should be inserted already + // contains a Pagedesc. If so that Pagedesc would be moved to the + // row after the table, that would be wrong. So delete and + // set later to the table format. + if (SwTextNode *const pNd = m_xTmpPos->nNode.GetNode().GetTextNode()) + { + if (const SfxItemSet* pSet = pNd->GetpSwAttrSet()) + { + SfxPoolItem *pSetAttr = nullptr; + const SfxPoolItem* pItem; + if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false, &pItem)) + { + pSetAttr = new SvxFormatBreakItem( *static_cast<const SvxFormatBreakItem*>(pItem) ); + pNd->ResetAttr( RES_BREAK ); + } + + // eventually set the PageDesc/Break now to the table + if (pSetAttr) + { + m_aItemSet.Put(*pSetAttr); + delete pSetAttr; + } + } + } + + // total width of table + if( m_nMaxRight - m_nMinLeft > MINLAY * m_nDefaultSwCols ) + { + SwFormatFrameSize aFrameSize(SwFrameSize::Fixed, m_nSwWidth); + // Don't set relative width if the table is in a floating frame + if ( m_nPercentWidth && (!m_pIo->m_xSFlyPara || !m_pIo->m_xSFlyPara->pFlyFormat) ) + aFrameSize.SetWidthPercent(m_nPercentWidth); + m_pTable->GetFrameFormat()->SetFormatAttr(aFrameSize); + m_aItemSet.Put(aFrameSize); + } + + SvxFrameDirectionItem aDirection( + m_bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR ); + m_pTable->GetFrameFormat()->SetFormatAttr(aDirection); + + if (text::HoriOrientation::LEFT_AND_WIDTH == m_eOri) + { + if (!m_pIo->m_nInTable && m_pIo->InLocalApo() && m_pIo->m_xSFlyPara && + m_pIo->m_xSFlyPara->pFlyFormat && GetMinLeft()) + { + //If we are inside a frame and we have a border, the frames + //placement does not consider the tables border, which word + //displays outside the frame, so adjust here. + SwFormatHoriOrient aHori(m_pIo->m_xSFlyPara->pFlyFormat->GetHoriOrient()); + sal_Int16 eHori = aHori.GetHoriOrient(); + if ((eHori == text::HoriOrientation::NONE) || (eHori == text::HoriOrientation::LEFT) || + (eHori == text::HoriOrientation::LEFT_AND_WIDTH)) + { + //With multiple table, use last table settings. Perhaps + //the maximum is what word does ? + aHori.SetPos(m_pIo->m_xSFlyPara->nXPos + GetMinLeft()); + aHori.SetHoriOrient(text::HoriOrientation::NONE); + m_pIo->m_xSFlyPara->pFlyFormat->SetFormatAttr(aHori); + } + } + else // Not directly in a floating frame. + { + //Historical note: If InLocalApo(), then this table is being placed in a floating + //frame, and the frame matches the left and right *lines* of the + //table, so the space to the left of the table isn't to be used + //inside the frame, in word the dialog involved greys out the + //ability to set the margin. + SvxLRSpaceItem aL( RES_LR_SPACE ); + + if (!m_bIsBiDi) + nLeft += GetMinLeft(); + else + { + const short nTableWidth = m_nPreferredWidth ? m_nPreferredWidth : m_nSwWidth; + nLeft += m_pIo->m_aSectionManager.GetTextAreaWidth(); + nLeft = nLeft - nTableWidth - GetMinLeft(); + } + aL.SetLeft(nLeft); + + m_aItemSet.Put(aL); + } + } + + mxOldRedlineStack = std::move(m_pIo->m_xRedlineStack); + m_pIo->m_xRedlineStack.reset(new sw::util::RedlineStack(m_pIo->m_rDoc)); +} + +void WW8TabDesc::UseSwTable() +{ + // init global Vars + m_pTabLines = &m_pTable->GetTabLines(); + m_nCurrentRow = m_nCurrentCol = m_nCurrentBandRow = 0; + + m_pTableNd = const_cast<SwTableNode*>((*m_pTabLines)[0]->GetTabBoxes()[0]-> + GetSttNd()->FindTableNode()); + OSL_ENSURE( m_pTableNd, "Where is my table node" ); + + // #i69519# - Restrict rows to repeat to a decent value + if ( m_nRowsToRepeat == static_cast<sal_uInt16>(m_nRows) ) + m_nRowsToRepeat = 1; + + m_pTableNd->GetTable().SetRowsToRepeat( m_nRowsToRepeat ); + // insert extra cells if needed and something like this + AdjustNewBand(); + + WW8DupProperties aDup(m_pIo->m_rDoc, m_pIo->m_xCtrlStck.get()); + m_pIo->m_xCtrlStck->SetAttr(*m_pIo->m_pPaM->GetPoint(), 0, false); + + // now set the correct PaM and prepare first merger group if any + SetPamInCell(m_nCurrentCol, true); + aDup.Insert(*m_pIo->m_pPaM->GetPoint()); + + m_pIo->m_bWasTabRowEnd = false; + m_pIo->m_bWasTabCellEnd = false; +} + +void WW8TabDesc::MergeCells() +{ + short nRow; + + for (m_pActBand=m_pFirstBand, nRow=0; m_pActBand; m_pActBand=m_pActBand->pNextBand) + { + // insert current box into merge group if appropriate. + // The algorithm must ensure proper row and column order in WW8SelBoxInfo! + if( m_pActBand->pTCs ) + { + for( short j = 0; j < m_pActBand->nRows; j++, nRow++ ) + for( short i = 0; i < m_pActBand->nWwCols; i++ ) + { + WW8SelBoxInfo* pActMGroup = nullptr; + + // start a new merge group if appropriate + + OSL_ENSURE(nRow < static_cast<sal_uInt16>(m_pTabLines->size()), + "Too few lines, table ended early"); + if (nRow >= static_cast<sal_uInt16>(m_pTabLines->size())) + return; + m_pTabLine = (*m_pTabLines)[ nRow ]; + m_pTabBoxes = &m_pTabLine->GetTabBoxes(); + + sal_uInt16 nCol = m_pActBand->nTransCell[ i ]; + if (!m_pActBand->bExist[i]) + continue; + OSL_ENSURE(nCol < m_pTabBoxes->size(), + "Too few columns, table ended early"); + if (nCol >= m_pTabBoxes->size()) + return; + m_pTabBox = (*m_pTabBoxes)[nCol]; + WW8_TCell& rCell = m_pActBand->pTCs[ i ]; + // is this the left upper cell of a merge group ? + + bool bMerge = false; + if ( rCell.bVertRestart && !rCell.bMerged ) + bMerge = true; + else if (rCell.bFirstMerged && m_pActBand->bExist[i]) + { + // Some tests to avoid merging cells which previously were + // declared invalid because of sharing the exact same dimensions + // as their previous cell + + //If there's anything underneath/above we're ok. + if (rCell.bVertMerge || rCell.bVertRestart) + bMerge = true; + else + { + //If it's a hori merge only, and the only things in + //it are invalid cells then it's already taken care + //of, so don't merge. + for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ ) + if (m_pActBand->pTCs[ i2 ].bMerged && + !m_pActBand->pTCs[ i2 ].bFirstMerged ) + { + if (m_pActBand->bExist[i2]) + { + bMerge = true; + break; + } + } + else + break; + } + } + + // remove numbering from cells that will be disabled in the merge + if( rCell.bVertMerge && !rCell.bVertRestart ) + { + SwPaM aPam( *m_pTabBox->GetSttNd(), 0 ); + aPam.GetPoint()->nNode++; + SwTextNode* pNd = aPam.GetNode().GetTextNode(); + while( pNd ) + { + pNd->SetCountedInList( false ); + + aPam.GetPoint()->nNode++; + pNd = aPam.GetNode().GetTextNode(); + } + } + + if (bMerge) + { + short nX1 = m_pActBand->nCenter[ i ]; + short nWidth = m_pActBand->nWidth[ i ]; + + // 2. create current merge group + pActMGroup = new WW8SelBoxInfo( nX1, nWidth ); + + // determine size of new merge group + // before inserted the new merge group. + // Needed to correctly locked previously created merge groups. + // Calculate total width and set + short nSizCell = m_pActBand->nWidth[ i ]; + for (sal_uInt16 i2 = i+1; i2 < m_pActBand->nWwCols; i2++ ) + if (m_pActBand->pTCs[ i2 ].bMerged && + !m_pActBand->pTCs[ i2 ].bFirstMerged ) + { + nSizCell = nSizCell + m_pActBand->nWidth[ i2 ]; + } + else + break; + pActMGroup->nGroupWidth = nSizCell; + + // locked previously created merge groups, + // after determining the size for the new merge group. + // 1. If necessary close old merge group(s) that overlap + // the X-area of the new group + for (;;) + { + WW8SelBoxInfo* p = FindMergeGroup( + nX1, pActMGroup->nGroupWidth, false ); + if (p == nullptr) + { + break; + } + p->bGroupLocked = true; + } + + // 3. push to group array + m_MergeGroups.push_back(std::unique_ptr<WW8SelBoxInfo>(pActMGroup)); + } + + // if necessary add the current box to a merge group + // (that can be a newly created or another group) + UpdateTableMergeGroup( rCell, pActMGroup, m_pTabBox, i ); + } + } + } +} + +//There is a limbo area in word at the end of the row marker +//where properties can live in word, there is no location in +//writer equivalent, so try and park the cursor in the best +//match, see #i23022#/#i18644# +void WW8TabDesc::ParkPaM() +{ + SwTableBox *pTabBox2 = nullptr; + short nRow = m_nCurrentRow + 1; + if (nRow < static_cast<sal_uInt16>(m_pTabLines->size())) + { + if (SwTableLine *pLine = (*m_pTabLines)[nRow]) + { + SwTableBoxes &rBoxes = pLine->GetTabBoxes(); + pTabBox2 = rBoxes.empty() ? nullptr : rBoxes.front(); + } + } + + if (!pTabBox2 || !pTabBox2->GetSttNd()) + { + MoveOutsideTable(); + return; + } + + sal_uLong nSttNd = pTabBox2->GetSttIdx() + 1, + nEndNd = pTabBox2->GetSttNd()->EndOfSectionIndex(); + + if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd) + { + do + { + m_pIo->m_pPaM->GetPoint()->nNode = nSttNd; + } + while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd); + + m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0); + m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl)); + } +} + +void WW8TabDesc::MoveOutsideTable() +{ + OSL_ENSURE(m_xTmpPos && m_pIo, "I've forgotten where the table is anchored"); + if (m_xTmpPos && m_pIo) + *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos; +} + +void WW8TabDesc::FinishSwTable() +{ + m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint()); + m_pIo->m_aFrameRedlines.emplace(std::move(m_pIo->m_xRedlineStack)); + m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack); + + WW8DupProperties aDup(m_pIo->m_rDoc,m_pIo->m_xCtrlStck.get()); + m_pIo->m_xCtrlStck->SetAttr( *m_pIo->m_pPaM->GetPoint(), 0, false); + + MoveOutsideTable(); + m_xTmpPos.reset(); + + aDup.Insert(*m_pIo->m_pPaM->GetPoint()); + + m_pIo->m_bWasTabRowEnd = false; + m_pIo->m_bWasTabCellEnd = false; + + m_pIo->m_aInsertedTables.InsertTable(*m_pTableNd, *m_pIo->m_pPaM); + + MergeCells(); + + // if needed group cells together that should be merged + if (!m_MergeGroups.empty()) + { + // process all merge groups one by one + for (auto const& groupIt : m_MergeGroups) + { + if((1 < groupIt->size()) && groupIt->row(0)[0]) + { + SwFrameFormat* pNewFormat = groupIt->row(0)[0]->ClaimFrameFormat(); + pNewFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, groupIt->nGroupWidth, 0)); + const sal_uInt16 nRowSpan = groupIt->rowsCount(); + for (sal_uInt16 n = 0; n < nRowSpan; ++n) + { + auto& rRow = groupIt->row(n); + for (size_t i = 0; i<rRow.size(); ++i) + { + const long nRowSpanSet = (n == 0) && (i == 0) ? + nRowSpan : + (-1 * (nRowSpan - n)); + SwTableBox* pCurrentBox = rRow[i]; + pCurrentBox->setRowSpan(nRowSpanSet); + + if (i == 0) + pCurrentBox->ChgFrameFormat(static_cast<SwTableBoxFormat*>(pNewFormat)); + else + { + SwFrameFormat* pFormat = pCurrentBox->ClaimFrameFormat(); + pFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 0, 0)); + } + } + } + } + } + m_pIo->m_xFormatOfJustInsertedApo.reset(); + m_MergeGroups.clear(); + } +} + +// browse m_MergeGroups, detect the index of the first fitting group or -1 otherwise + +// Parameter: nXcenter = center position of asking box +// nWidth = width of asking box +// bExact = flag, if box has to fit into group +// or only has to touch + +WW8SelBoxInfo* WW8TabDesc::FindMergeGroup(short nX1, short nWidth, bool bExact) +{ + if (!m_MergeGroups.empty()) + { + // still valid area near the boundary + const short nTolerance = 4; + // box boundary + short nX2 = nX1 + nWidth; + // approximate group boundary + short nGrX1; + short nGrX2; + + // improvement: search backwards + for (short iGr = m_MergeGroups.size() - 1; iGr >= 0; --iGr) + { + // the currently inspected group + WW8SelBoxInfo& rActGroup = *m_MergeGroups[ iGr ]; + if (!rActGroup.bGroupLocked) + { + // approximate group boundary with room (tolerance) to the *outside* + nGrX1 = rActGroup.nGroupXStart - nTolerance; + nGrX2 = rActGroup.nGroupXStart + + rActGroup.nGroupWidth + nTolerance; + + // If box fits report success + + if( ( nX1 > nGrX1 ) && ( nX2 < nGrX2 ) ) + { + return &rActGroup; + } + + // does the box share areas with the group? + + if( !bExact ) + { + // successful if nX1 *or* nX2 are inside the group + if( ( ( nX1 > nGrX1 ) + && ( nX1 < nGrX2 - 2*nTolerance ) ) + || ( ( nX2 > nGrX1 + 2*nTolerance ) + && ( nX2 < nGrX2 ) ) + // or nX1 and nX2 surround the group + || ( ( nX1 <=nGrX1 ) + && ( nX2 >=nGrX2 ) ) ) + { + return &rActGroup; + } + } + } + } + } + return nullptr; +} + +bool WW8TabDesc::IsValidCell(short nCol) const +{ + return (o3tl::make_unsigned(nCol) < SAL_N_ELEMENTS(m_pActBand->bExist)) && + m_pActBand->bExist[nCol] && + o3tl::make_unsigned(m_nCurrentRow) < m_pTabLines->size(); +} + +bool WW8TabDesc::InFirstParaInCell() const +{ + //e.g. #i19718# + if (!m_pTabBox || !m_pTabBox->GetSttNd()) + { + OSL_FAIL("Problem with table"); + return false; + } + + if (!IsValidCell(GetCurrentCol())) + return false; + + return m_pIo->m_pPaM->GetPoint()->nNode == m_pTabBox->GetSttIdx() + 1; +} + +void WW8TabDesc::SetPamInCell(short nWwCol, bool bPam) +{ + OSL_ENSURE( m_pActBand, "pActBand is 0" ); + if (!m_pActBand) + return; + + sal_uInt16 nCol = m_pActBand->transCell(nWwCol); + + if (o3tl::make_unsigned(m_nCurrentRow) >= m_pTabLines->size()) + { + OSL_ENSURE(false, "Actual row bigger than expected." ); + if (bPam) + MoveOutsideTable(); + return; + } + + m_pTabLine = (*m_pTabLines)[m_nCurrentRow]; + m_pTabBoxes = &m_pTabLine->GetTabBoxes(); + + if (nCol >= m_pTabBoxes->size()) + { + if (bPam) + { + // The first paragraph in a cell with upper autospacing has upper + // spacing set to 0 + if ( + m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && + !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing + ) + { + m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0); + } + + // The last paragraph in a cell with lower autospacing has lower + // spacing set to 0 + if (m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing) + m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0); + + ParkPaM(); + } + return; + } + m_pTabBox = (*m_pTabBoxes)[nCol]; + if( !m_pTabBox->GetSttNd() ) + { + OSL_ENSURE(m_pTabBox->GetSttNd(), "Problems building the table"); + if (bPam) + MoveOutsideTable(); + return; + } + if (bPam) + { + m_pCurrentWWCell = &m_pActBand->pTCs[ nWwCol ]; + + // The first paragraph in a cell with upper autospacing has upper spacing set to 0 + if(m_pIo->m_bParaAutoBefore && m_pIo->m_bFirstPara && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing) + m_pIo->SetUpperSpacing(*m_pIo->m_pPaM, 0); + + // The last paragraph in a cell with lower autospacing has lower spacing set to 0 + if(m_pIo->m_bParaAutoAfter && !m_pIo->m_xWDop->fDontUseHTMLAutoSpacing) + m_pIo->SetLowerSpacing(*m_pIo->m_pPaM, 0); + + //We need to set the pPaM on the first cell, invalid + //or not so that we can collect paragraph properties over + //all the cells, but in that case on the valid cell we do not + //want to reset the fmt properties + sal_uLong nSttNd = m_pTabBox->GetSttIdx() + 1, + nEndNd = m_pTabBox->GetSttNd()->EndOfSectionIndex(); + if (m_pIo->m_pPaM->GetPoint()->nNode != nSttNd) + { + do + { + m_pIo->m_pPaM->GetPoint()->nNode = nSttNd; + } + while (m_pIo->m_pPaM->GetNode().GetNodeType() != SwNodeType::Text && ++nSttNd < nEndNd); + m_pIo->m_pPaM->GetPoint()->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0); + // Precautionally set now, otherwise the style is not set for cells + // that are inserted for margin balancing. + m_pIo->m_rDoc.SetTextFormatColl(*m_pIo->m_pPaM, const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl)); + // because this cells are invisible helper constructions only to simulate + // the frayed view of WW-tables we do NOT need SetTextFormatCollAndListLevel() + } + + // Better to turn Snap to Grid off for all paragraphs in tables + if(SwTextNode *pNd = m_pIo->m_pPaM->GetNode().GetTextNode()) + { + const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_PARATR_SNAPTOGRID); + const SvxParaGridItem &rSnapToGrid = static_cast<const SvxParaGridItem&>(rItm); + + if(rSnapToGrid.GetValue()) + { + SvxParaGridItem aGridItem( rSnapToGrid ); + aGridItem.SetValue(false); + + SwPosition* pGridPos = m_pIo->m_pPaM->GetPoint(); + + const sal_Int32 nEnd = pGridPos->nContent.GetIndex(); + pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), 0); + m_pIo->m_xCtrlStck->NewAttr(*pGridPos, aGridItem); + pGridPos->nContent.Assign(m_pIo->m_pPaM->GetContentNode(), nEnd); + m_pIo->m_xCtrlStck->SetAttr(*pGridPos, RES_PARATR_SNAPTOGRID); + } + } + } +} + +void WW8TabDesc::InsertCells( short nIns ) +{ + m_pTabLine = (*m_pTabLines)[m_nCurrentRow]; + m_pTabBoxes = &m_pTabLine->GetTabBoxes(); + m_pTabBox = (*m_pTabBoxes)[0]; + + m_pIo->m_rDoc.GetNodes().InsBoxen( m_pTableNd, m_pTabLine, static_cast<SwTableBoxFormat*>(m_pTabBox->GetFrameFormat()), + const_cast<SwTextFormatColl*>(m_pIo->m_pDfltTextFormatColl), nullptr, m_pTabBoxes->size(), nIns ); + // The third parameter contains the FrameFormat of the boxes. + // Here it is possible to optimize to save (reduce) FrameFormats. +} + +void WW8TabDesc::SetTabBorders(SwTableBox* pBox, short nWwIdx) +{ + if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols ) + return; // faked cells -> no border + + SvxBoxItem aFormatBox( RES_BOX ); + if (m_pActBand->pTCs) // neither Cell Border nor Default Border defined ? + { + WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx]; + if (SwWW8ImplReader::IsBorder(pT->rgbrc)) + SwWW8ImplReader::SetBorder(aFormatBox, pT->rgbrc); + } + + if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwTOP)) + { + aFormatBox.SetDistance( + m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwTOP], + SvxBoxItemLine::TOP); + } + else + aFormatBox.SetDistance(m_pActBand->mnDefaultTop, SvxBoxItemLine::TOP); + if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwBOTTOM)) + { + aFormatBox.SetDistance( + m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwBOTTOM], + SvxBoxItemLine::BOTTOM); + } + else + aFormatBox.SetDistance(m_pActBand->mnDefaultBottom,SvxBoxItemLine::BOTTOM); + + // nGapHalf for WW is a *horizontal* gap between table cell and content. + short nLeftDist = + m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultLeft : m_pActBand->nGapHalf; + short nRightDist = + m_pActBand->mbHasSpacing ? m_pActBand->mnDefaultRight : m_pActBand->nGapHalf; + if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwLEFT)) + { + aFormatBox.SetDistance( + m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwLEFT], + SvxBoxItemLine::LEFT); + } + else + aFormatBox.SetDistance(nLeftDist, SvxBoxItemLine::LEFT); + if (m_pActBand->nOverrideSpacing[nWwIdx] & (1 << WW8TabBandDesc::wwRIGHT)) + { + aFormatBox.SetDistance( + m_pActBand->nOverrideValues[nWwIdx][WW8TabBandDesc::wwRIGHT], + SvxBoxItemLine::RIGHT); + } + else + aFormatBox.SetDistance(nRightDist,SvxBoxItemLine::RIGHT); + + pBox->GetFrameFormat()->SetFormatAttr(aFormatBox); +} + +void WW8TabDesc::SetTabShades( SwTableBox* pBox, short nWwIdx ) +{ + if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols ) + return; // faked cells -> no color + + bool bFound=false; + if (m_pActBand->pNewSHDs && m_pActBand->pNewSHDs[nWwIdx] != COL_AUTO) + { + Color aColor(m_pActBand->pNewSHDs[nWwIdx]); + pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aColor, RES_BACKGROUND)); + bFound = true; + } + + //If there was no new shades, or no new shade setting + if (m_pActBand->pSHDs && !bFound) + { + WW8_SHD& rSHD = m_pActBand->pSHDs[nWwIdx]; + if (!rSHD.GetValue()) // auto + return; + + SwWW8Shade aSh( m_pIo->m_bVer67, rSHD ); + pBox->GetFrameFormat()->SetFormatAttr(SvxBrushItem(aSh.aColor, RES_BACKGROUND)); + } +} + +static SvxFrameDirection MakeDirection(sal_uInt16 nCode, bool bIsBiDi) +{ + SvxFrameDirection eDir = SvxFrameDirection::Environment; + // 1: Asian layout with rotated CJK characters + // 5: Asian layout + // 3: Western layout rotated by 90 degrees + // 4: Western layout + switch (nCode) + { + default: + OSL_ENSURE(eDir == SvxFrameDirection::Environment, "unknown direction code, maybe it's a bitfield"); + [[fallthrough]]; + case 3: + eDir = SvxFrameDirection::Vertical_LR_BT; + break; + case 5: + eDir = SvxFrameDirection::Vertical_RL_TB; + break; + case 1: + eDir = SvxFrameDirection::Vertical_RL_TB; + break; + case 4: + eDir = bIsBiDi ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; // #i38158# - Consider RTL tables + break; + } + return eDir; +} + +void WW8TabDesc::SetTabDirection(SwTableBox* pBox, short nWwIdx) +{ + if (nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols) + return; + SvxFrameDirectionItem aItem(MakeDirection(m_pActBand->maDirections[nWwIdx], m_bIsBiDi), RES_FRAMEDIR); + pBox->GetFrameFormat()->SetFormatAttr(aItem); +} + +void WW8TabDesc::SetTabVertAlign( SwTableBox* pBox, short nWwIdx ) +{ + if( nWwIdx < 0 || nWwIdx >= m_pActBand->nWwCols ) + return; + + sal_Int16 eVertOri=text::VertOrientation::TOP; + + if( m_pActBand->pTCs ) + { + WW8_TCell* pT = &m_pActBand->pTCs[nWwIdx]; + switch (pT->nVertAlign) + { + case 0: + default: + eVertOri = text::VertOrientation::TOP; + break; + case 1: + eVertOri = text::VertOrientation::CENTER; + break; + case 2: + eVertOri = text::VertOrientation::BOTTOM; + break; + } + } + + pBox->GetFrameFormat()->SetFormatAttr( SwFormatVertOrient(0,eVertOri) ); +} + +void WW8TabDesc::AdjustNewBand() +{ + if( m_pActBand->nSwCols > m_nDefaultSwCols ) // split cells + InsertCells( m_pActBand->nSwCols - m_nDefaultSwCols ); + + SetPamInCell( 0, false); + OSL_ENSURE( m_pTabBoxes && m_pTabBoxes->size() == static_cast<sal_uInt16>(m_pActBand->nSwCols), + "Wrong column count in table" ); + + if( m_bClaimLineFormat ) + { + m_pTabLine->ClaimFrameFormat(); // necessary because of cell height + SwFormatFrameSize aF( SwFrameSize::Minimum, 0, 0 ); // default + + if (m_pActBand->nLineHeight == 0) // 0 = Auto + aF.SetHeightSizeType( SwFrameSize::Variable ); + else + { + if (m_pActBand->nLineHeight < 0) // positive = min, negative = exact + { + aF.SetHeightSizeType(SwFrameSize::Fixed); + m_pActBand->nLineHeight = -m_pActBand->nLineHeight; + } + if (m_pActBand->nLineHeight < MINLAY) // invalid cell height + m_pActBand->nLineHeight = MINLAY; + + aF.SetHeight(m_pActBand->nLineHeight);// set min/exact height + } + m_pTabLine->GetFrameFormat()->SetFormatAttr(aF); + } + + //Word stores 1 for bCantSplit if the row cannot be split, we set true if + //we can split the row + bool bSetCantSplit = m_pActBand->bCantSplit; + m_pTabLine->GetFrameFormat()->SetFormatAttr(SwFormatRowSplit(!bSetCantSplit)); + + // if table is only a single row, and row is set as don't split, set the same value for the whole table. + if (bSetCantSplit && m_pTabLines->size() == 1) + m_pTable->GetFrameFormat()->SetFormatAttr(SwFormatLayoutSplit(false)); + + short i; // SW-Index + short j; // WW-Index + short nW; // Width + SwFormatFrameSize aFS( SwFrameSize::Fixed ); + j = m_pActBand->bLEmptyCol ? -1 : 0; + + for( i = 0; i < m_pActBand->nSwCols; i++ ) + { + // set cell width + if( j < 0 ) + nW = m_pActBand->nCenter[0] - m_nMinLeft; + else + { + //Set j to first non invalid cell + while ((j < m_pActBand->nWwCols) && (!m_pActBand->bExist[j])) + j++; + + if( j < m_pActBand->nWwCols ) + nW = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j]; + else + nW = m_nMaxRight - m_pActBand->nCenter[j]; + m_pActBand->nWidth[ j ] = nW; + } + + SwTableBox* pBox = (*m_pTabBoxes)[i]; + // could be reduced further by intelligent moving of FrameFormats + pBox->ClaimFrameFormat(); + + SetTabBorders(pBox, j); + + SvxBoxItem aCurrentBox(sw::util::ItemGet<SvxBoxItem>(*(pBox->GetFrameFormat()), RES_BOX)); + pBox->GetFrameFormat()->SetFormatAttr(aCurrentBox); + + SetTabVertAlign(pBox, j); + SetTabDirection(pBox, j); + if( m_pActBand->pSHDs || m_pActBand->pNewSHDs) + SetTabShades(pBox, j); + j++; + + aFS.SetWidth( nW ); + pBox->GetFrameFormat()->SetFormatAttr( aFS ); + + // skip non existing cells + while( ( j < m_pActBand->nWwCols ) && !m_pActBand->bExist[j] ) + { + m_pActBand->nWidth[j] = m_pActBand->nCenter[j+1] - m_pActBand->nCenter[j]; + j++; + } + } +} + +void WW8TabDesc::TableCellEnd() +{ + ::SetProgressState(m_pIo->m_nProgress, m_pIo->m_pDocShell); // Update + + // new line/row + if( m_pIo->m_bWasTabRowEnd ) + { + // bWasTabRowEnd will be deactivated in + // SwWW8ImplReader::ProcessSpecial() + + sal_uInt16 iCol = GetLogicalWWCol(); + if (iCol < m_aNumRuleNames.size()) + { + m_aNumRuleNames.erase(m_aNumRuleNames.begin() + iCol, + m_aNumRuleNames.end()); + } + + m_nCurrentCol = 0; + m_nCurrentRow++; + m_nCurrentBandRow++; + OSL_ENSURE( m_pActBand , "pActBand is 0" ); + if( m_pActBand ) + { + if( m_nCurrentRow >= m_nRows ) // nothing to at end of table + return; + + bool bNewBand = m_nCurrentBandRow >= m_pActBand->nRows; + if( bNewBand ) + { // new band needed ? + m_pActBand = m_pActBand->pNextBand; + m_nCurrentBandRow = 0; + OSL_ENSURE( m_pActBand, "pActBand is 0" ); + AdjustNewBand(); + } + else + { + SwTableBox* pBox = (*m_pTabBoxes)[0]; + SwSelBoxes aBoxes; + m_pIo->m_rDoc.InsertRow( SwTable::SelLineFromBox( pBox, aBoxes ) ); + } + } + } + else + { // new column ( cell ) + m_nCurrentCol++; + } + SetPamInCell(m_nCurrentCol, true); + + // finish Annotated Level Numbering ? + if (m_pIo->m_bAnl && !m_pIo->m_bCurrentAND_fNumberAcross && m_pActBand) + m_pIo->StopAllAnl(IsValidCell(m_nCurrentCol)); +} + +// if necessary register the box for the merge group for this column +void WW8TabDesc::UpdateTableMergeGroup( WW8_TCell const & rCell, + WW8SelBoxInfo* pActGroup, + SwTableBox* pActBox, + sal_uInt16 nCol ) +{ + // check if the box has to be merged + // If cell is the first one to be merged, a new merge group has to be provided. + // E.g., it could be that a cell is the first one to be merged, but no + // new merge group is provided, because the potential other cell to be merged + // doesn't exist - see method <WW8TabDesc::MergeCells>. + if ( m_pActBand->bExist[ nCol ] && + ( ( rCell.bFirstMerged && pActGroup ) || + rCell.bMerged || + rCell.bVertMerge || + rCell.bVertRestart ) ) + { + // detect appropriate merge group + WW8SelBoxInfo* pTheMergeGroup = nullptr; + if( pActGroup ) + // assign group + pTheMergeGroup = pActGroup; + else + { + // find group + pTheMergeGroup = FindMergeGroup( + m_pActBand->nCenter[ nCol ], m_pActBand->nWidth[ nCol ], true ); + } + if( pTheMergeGroup ) + { + // add current box to merge group + pTheMergeGroup->push_back(pActBox); + } + } +} + +sal_uInt16 WW8TabDesc::GetLogicalWWCol() const // returns number of col as INDICATED within WW6 UI status line -1 +{ + sal_uInt16 nCol = 0; + if( m_pActBand && m_pActBand->pTCs) + { + for( sal_uInt16 iCol = 1; iCol <= m_nCurrentCol && iCol <= m_pActBand->nWwCols; ++iCol ) + { + if( !m_pActBand->pTCs[ iCol-1 ].bMerged ) + ++nCol; + } + } + return nCol; +} + +// find name of numrule valid for current WW-COL +OUString WW8TabDesc::GetNumRuleName() const +{ + sal_uInt16 nCol = GetLogicalWWCol(); + if (nCol < m_aNumRuleNames.size()) + return m_aNumRuleNames[nCol]; + return OUString(); +} + +void WW8TabDesc::SetNumRuleName( const OUString& rName ) +{ + sal_uInt16 nCol = GetLogicalWWCol(); + for (sal_uInt16 nSize = static_cast< sal_uInt16 >(m_aNumRuleNames.size()); nSize <= nCol; ++nSize) + m_aNumRuleNames.emplace_back(); + m_aNumRuleNames[nCol] = rName; +} + +bool SwWW8ImplReader::StartTable(WW8_CP nStartCp) +{ + // Entering a table so make sure the FirstPara flag gets set + m_bFirstPara = true; + // no recursive table, not with InsertFile in table or foot note + if (m_bReadNoTable) + return false; + + if (m_xTableDesc) + m_aTableStack.push(std::move(m_xTableDesc)); + + // #i33818# - determine absolute position object attributes, + // if possible. It's needed for nested tables. + std::unique_ptr<WW8FlyPara> pTableWFlyPara; + WW8SwFlyPara* pTableSFlyPara( nullptr ); + // #i45301# - anchor nested table inside Writer fly frame + // only at-character, if absolute position object attributes are available. + // Thus, default anchor type is as-character anchored. + RndStdIds eAnchor( RndStdIds::FLY_AS_CHAR ); + if ( m_nInTable ) + { + WW8_TablePos* pNestedTabPos( nullptr ); + WW8_TablePos aNestedTabPos; + WW8PLCFxSave1 aSave; + m_xPlcxMan->GetPap()->Save( aSave ); + WW8PLCFx_Cp_FKP* pPap = m_xPlcxMan->GetPapPLCF(); + WW8_CP nMyStartCp = nStartCp; + if ( SearchRowEnd( pPap, nMyStartCp, m_nInTable ) && + ParseTabPos( &aNestedTabPos, pPap ) ) + { + pNestedTabPos = &aNestedTabPos; + } + m_xPlcxMan->GetPap()->Restore( aSave ); + if ( pNestedTabPos ) + { + ApoTestResults aApo = TestApo( m_nInTable + 1, false, pNestedTabPos ); + pTableWFlyPara = ConstructApo( aApo, pNestedTabPos ); + if ( pTableWFlyPara ) + { + // <WW8SwFlyPara> constructor has changed - new 4th parameter + // containing WW8 page top margin. + pTableSFlyPara = new WW8SwFlyPara(*m_pPaM, *this, *pTableWFlyPara, + m_aSectionManager.GetWWPageTopMargin(), + m_aSectionManager.GetTextAreaWidth(), + m_nIniFlyDx, m_nIniFlyDy); + + // #i45301# - anchor nested table Writer fly frame at-character + eAnchor = RndStdIds::FLY_AT_CHAR; + } + } + } + // if first paragraph in table has break-before-page, transfer that setting to the table itself. + else if( StyleExists(m_nCurrentColl) ) + { + const SwFormat* pStyleFormat = m_vColl[m_nCurrentColl].m_pFormat; + if( pStyleFormat && pStyleFormat->GetBreak().GetBreak() == SvxBreak::PageBefore ) + NewAttr( pStyleFormat->GetBreak() ); + } + + m_xTableDesc.reset(new WW8TabDesc(this, nStartCp)); + + if( m_xTableDesc->Ok() ) + { + int nNewInTable = m_nInTable + 1; + + if ((eAnchor == RndStdIds::FLY_AT_CHAR) + && !m_aTableStack.empty() && !InEqualApo(nNewInTable) ) + { + m_xTableDesc->m_pParentPos = new SwPosition(*m_pPaM->GetPoint()); + SfxItemSet aItemSet(m_rDoc.GetAttrPool(), + svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{}); + // #i33818# - anchor the Writer fly frame for the nested table at-character. + // #i45301# + SwFormatAnchor aAnchor( eAnchor ); + aAnchor.SetAnchor( m_xTableDesc->m_pParentPos ); + aItemSet.Put( aAnchor ); + m_xTableDesc->m_pFlyFormat = m_rDoc.MakeFlySection( eAnchor, + m_xTableDesc->m_pParentPos, &aItemSet); + OSL_ENSURE( m_xTableDesc->m_pFlyFormat->GetAnchor().GetAnchorId() == eAnchor, + "Not the anchor type requested!" ); + MoveInsideFly(m_xTableDesc->m_pFlyFormat); + } + m_xTableDesc->CreateSwTable(); + if (m_xTableDesc->m_pFlyFormat) + { + m_xTableDesc->SetSizePosition(m_xTableDesc->m_pFlyFormat); + // #i33818# - Use absolute position object attributes, + // if existing, and apply them to the created Writer fly frame. + if ( pTableWFlyPara && pTableSFlyPara ) + { + WW8FlySet aFlySet( *this, pTableWFlyPara.get(), pTableSFlyPara, false ); + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR ); + aAnchor.SetAnchor( m_xTableDesc->m_pParentPos ); + aFlySet.Put( aAnchor ); + m_xTableDesc->m_pFlyFormat->SetFormatAttr( aFlySet ); + } + else + { + SwFormatHoriOrient aHori = + m_xTableDesc->m_pTable->GetFrameFormat()->GetHoriOrient(); + m_xTableDesc->m_pFlyFormat->SetFormatAttr(aHori); + m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatSurround( css::text::WrapTextMode_NONE ) ); + } + // #i33818# - The nested table doesn't have to leave + // the table cell. Thus, the Writer fly frame has to follow the text flow. + m_xTableDesc->m_pFlyFormat->SetFormatAttr( SwFormatFollowTextFlow( true ) ); + } + else + m_xTableDesc->SetSizePosition(nullptr); + m_xTableDesc->UseSwTable(); + } + else + PopTableDesc(); + + // #i33818# + delete pTableSFlyPara; + + return m_xTableDesc != nullptr; +} + +void SwWW8ImplReader::TabCellEnd() +{ + if (m_nInTable && m_xTableDesc) + m_xTableDesc->TableCellEnd(); + + m_bFirstPara = true; // We have come to the end of a cell so FirstPara flag + m_bReadTable = false; +} + +void SwWW8ImplReader::Read_TabCellEnd( sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if( ( nLen > 0 ) && ( *pData == 1 ) ) + m_bWasTabCellEnd = true; +} + +void SwWW8ImplReader::Read_TabRowEnd( sal_uInt16, const sal_uInt8* pData, short nLen ) // Sprm25 +{ + if( ( nLen > 0 ) && ( *pData == 1 ) ) + m_bWasTabRowEnd = true; +} + +void SwWW8ImplReader::PopTableDesc() +{ + if (m_xTableDesc && m_xTableDesc->m_pFlyFormat) + { + MoveOutsideFly(m_xTableDesc->m_pFlyFormat, *m_xTableDesc->m_pParentPos); + } + + m_xTableDesc.reset(); + if (!m_aTableStack.empty()) + { + m_xTableDesc = std::move(m_aTableStack.top()); + m_aTableStack.pop(); + } +} + +void SwWW8ImplReader::StopTable() +{ + OSL_ENSURE(m_xTableDesc, "Panic, stop table with no table!"); + if (!m_xTableDesc) + return; + + // We are leaving a table so make sure the next paragraph doesn't think + // it's the first paragraph + m_bFirstPara = false; + + m_xTableDesc->FinishSwTable(); + PopTableDesc(); + + m_bReadTable = true; +} + +bool SwWW8ImplReader::IsInvalidOrToBeMergedTabCell() const +{ + if( !m_xTableDesc ) + return false; + + const WW8_TCell* pCell = m_xTableDesc->GetCurrentWWCell(); + + return !m_xTableDesc->IsValidCell( m_xTableDesc->GetCurrentCol() ) + || ( pCell + && ( !pCell->bFirstMerged + && ( pCell->bMerged + || ( pCell->bVertMerge + && !pCell->bVertRestart + ) + ) + ) + ); +} + +sal_uInt16 SwWW8ImplReader::StyleUsingLFO( sal_uInt16 nLFOIndex ) const +{ + sal_uInt16 nRes = USHRT_MAX; + if( !m_vColl.empty() ) + { + for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ ) + if( m_vColl[ nI ].m_bValid + && (nLFOIndex == m_vColl[ nI ].m_nLFOIndex) ) + nRes = nI; + } + return nRes; +} + +const SwFormat* SwWW8ImplReader::GetStyleWithOrgWWName( OUString const & rName ) const +{ + SwFormat* pRet = nullptr; + if( !m_vColl.empty() ) + { + for(sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); nI++ ) + if( m_vColl[ nI ].m_bValid + && (rName == m_vColl[ nI ].GetOrgWWName()) ) + { + pRet = m_vColl[ nI ].m_pFormat; + break; + } + } + return pRet; +} + + +SprmResult WW8RStyle::HasParaSprm(sal_uInt16 nId) const +{ + if( !mpParaSprms || !mnSprmsLen ) + return SprmResult(); + + return maSprmParser.findSprmData(nId, mpParaSprms, mnSprmsLen); +} + +void WW8RStyle::ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap) +{ + if (!nLen) + return; + + if( bPap ) + { + mpParaSprms = pSprms; // for HasParaSprms() + mnSprmsLen = nLen; + } + + WW8SprmIter aSprmIter(pSprms, nLen, maSprmParser); + while (const sal_uInt8* pSprm = aSprmIter.GetSprms()) + { +#ifdef DEBUGSPRMREADER + fprintf(stderr, "id is %x\n", aIter.GetCurrentId()); +#endif + mpIo->ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId()); + aSprmIter.advance(); + } + + mpParaSprms = nullptr; + mnSprmsLen = 0; +} + +void WW8RStyle::ImportSprms(std::size_t nPosFc, short nLen, bool bPap) +{ + if (!nLen) + return; + + if (checkSeek(*mpStStrm, nPosFc)) + { + std::unique_ptr<sal_uInt8[]> pSprms( new sal_uInt8[nLen] ); + nLen = mpStStrm->ReadBytes(pSprms.get(), nLen); + ImportSprms(pSprms.get(), nLen, bPap); + } +} + +static short WW8SkipOdd(SvStream* pSt ) +{ + if ( pSt->Tell() & 0x1 ) + { + sal_uInt8 c; + return pSt->ReadBytes( &c, 1 ); + } + return 0; +} + +static short WW8SkipEven(SvStream* pSt ) +{ + if (!(pSt->Tell() & 0x1)) + { + sal_uInt8 c; + return pSt->ReadBytes( &c, 1 ); + } + return 0; +} + +short WW8RStyle::ImportUPX(short nLen, bool bPAP, bool bOdd) +{ + if( 0 < nLen ) // Empty ? + { + if (bOdd) + nLen = nLen - WW8SkipEven( mpStStrm ); + else + nLen = nLen - WW8SkipOdd( mpStStrm ); + + sal_Int16 cbUPX(0); + mpStStrm->ReadInt16( cbUPX ); + + nLen-=2; + + if ( cbUPX > nLen ) + cbUPX = nLen; // shrink cbUPX to nLen + + if( (1 < cbUPX) || ( (0 < cbUPX) && !bPAP ) ) + { + if( bPAP ) + { + sal_uInt16 id; + mpStStrm->ReadUInt16( id ); + + cbUPX-= 2; + nLen-= 2; + } + + if( 0 < cbUPX ) + { + sal_uInt64 const nPos = mpStStrm->Tell(); // if something is interpreted wrong, + // this should make it work again + ImportSprms( nPos, cbUPX, bPAP ); + + if ( mpStStrm->Tell() != nPos + cbUPX ) + mpStStrm->Seek( nPos+cbUPX ); + + nLen = nLen - cbUPX; + } + } + } + return nLen; +} + +void WW8RStyle::ImportGrupx(short nLen, bool bPara, bool bOdd) +{ + if( nLen <= 0 ) + return; + if (bOdd) + nLen = nLen - WW8SkipEven( mpStStrm ); + else + nLen = nLen - WW8SkipOdd( mpStStrm ); + + if( bPara ) // Grupx.Papx + nLen = ImportUPX(nLen, true, bOdd); + ImportUPX(nLen, false, bOdd); // Grupx.Chpx +} + +WW8RStyle::WW8RStyle(WW8Fib& _rFib, SwWW8ImplReader* pI) + : WW8Style(*pI->m_pTableStream, _rFib) + , maSprmParser(_rFib) + , mpIo(pI) + , mpStStrm(pI->m_pTableStream) + , mpStyRule(nullptr) + , mpParaSprms(nullptr) + , mnSprmsLen(0) + , mnWwNumLevel(0) + , mbTextColChanged(false) + , mbFontChanged(false) + , mbCJKFontChanged(false) + , mbCTLFontChanged(false) + , mbFSizeChanged(false) + , mbFCTLSizeChanged(false) + , mbWidowsChanged(false) + , mbBidiChanged(false) +{ + mpIo->m_vColl.resize(m_cstd); +} + +void WW8RStyle::Set1StyleDefaults() +{ + // see #i25247#, #i25561#, #i48064#, #i92341# for default font + if (!mbCJKFontChanged) // Style no CJK Font? set the default + mpIo->SetNewFontAttr(m_ftcFE, true, RES_CHRATR_CJK_FONT); + + if (!mbCTLFontChanged) // Style no CTL Font? set the default + mpIo->SetNewFontAttr(m_ftcBi, true, RES_CHRATR_CTL_FONT); + + // western 2nd to make western charset conversion the default + if (!mbFontChanged) // Style has no Font? set the default, + mpIo->SetNewFontAttr(m_ftcAsci, true, RES_CHRATR_FONT); + + if( !mpIo->m_bNoAttrImport ) + { + // Style has no text color set, winword default is auto + if ( !mbTextColChanged ) + mpIo->m_pCurrentColl->SetFormatAttr(SvxColorItem(COL_AUTO, RES_CHRATR_COLOR)); + + // Style has no FontSize ? WinWord Default is 10pt for western and asian + if( !mbFSizeChanged ) + { + SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE); + mpIo->m_pCurrentColl->SetFormatAttr(aAttr); + aAttr.SetWhich(RES_CHRATR_CJK_FONTSIZE); + mpIo->m_pCurrentColl->SetFormatAttr(aAttr); + } + + // Style has no FontSize ? WinWord Default is 10pt for western and asian + if( !mbFCTLSizeChanged ) + { + SvxFontHeightItem aAttr(200, 100, RES_CHRATR_FONTSIZE); + aAttr.SetWhich(RES_CHRATR_CTL_FONTSIZE); + mpIo->m_pCurrentColl->SetFormatAttr(aAttr); + } + + if( !mbWidowsChanged ) // Widows ? + { + mpIo->m_pCurrentColl->SetFormatAttr( SvxWidowsItem( 2, RES_PARATR_WIDOWS ) ); + mpIo->m_pCurrentColl->SetFormatAttr( SvxOrphansItem( 2, RES_PARATR_ORPHANS ) ); + } + + // Word defaults to ltr, not inheriting from the environment like Writer. Regardless of + // the page/sections rtl setting, the standard/no-inherit styles lack of rtl still means ltr + if( !mbBidiChanged ) // likely, since no UI to change LTR except in default style + { + mpIo->m_pCurrentColl->SetFormatAttr( + SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR)); + } + } +} + +bool WW8RStyle::PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle, sal_uInt16 nNextStyle) +{ + SwFormat* pColl; + bool bStyExist; + + if (rSI.m_bColl) + { + // Para-Style + sw::util::ParaStyleMapper::StyleResult aResult = + mpIo->m_aParaStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti); + pColl = aResult.first; + bStyExist = aResult.second; + } + else + { + // Char-Style + sw::util::CharStyleMapper::StyleResult aResult = + mpIo->m_aCharStyleMapper.GetStyle(rSI.GetOrgWWName(), eSti); + pColl = aResult.first; + bStyExist = aResult.second; + } + + bool bImport = !bStyExist || mpIo->m_bNewDoc; // import content ? + + // Do not override character styles the list import code created earlier. + if (bImport && bStyExist && rSI.GetOrgWWName().startsWith("WW8Num")) + bImport = false; + + bool bOldNoImp = mpIo->m_bNoAttrImport; + rSI.m_bImportSkipped = !bImport; + + if( !bImport ) + mpIo->m_bNoAttrImport = true; + else + { + if (bStyExist) + { + pColl->ResetAllFormatAttr(); // #i73790# - method renamed + } + pColl->SetAuto(false); // suggested by JP + } // but changes the UI + mpIo->m_pCurrentColl = pColl; + rSI.m_pFormat = pColl; // remember translation WW->SW + rSI.m_bImportSkipped = !bImport; + + // Set Based on style + sal_uInt16 j = rSI.m_nBase; + if (j != nThisStyle && j < m_cstd ) + { + SwWW8StyInf* pj = &mpIo->m_vColl[j]; + if (rSI.m_pFormat && pj->m_pFormat && rSI.m_bColl == pj->m_bColl) + { + rSI.m_pFormat->SetDerivedFrom( pj->m_pFormat ); // ok, set Based on + rSI.m_eLTRFontSrcCharSet = pj->m_eLTRFontSrcCharSet; + rSI.m_eRTLFontSrcCharSet = pj->m_eRTLFontSrcCharSet; + rSI.m_eCJKFontSrcCharSet = pj->m_eCJKFontSrcCharSet; + rSI.m_n81Flags = pj->m_n81Flags; + rSI.m_n81BiDiFlags = pj->m_n81BiDiFlags; + if (!rSI.IsWW8BuiltInHeadingStyle()) + { + rSI.mnWW8OutlineLevel = pj->mnWW8OutlineLevel; + } + rSI.m_bParaAutoBefore = pj->m_bParaAutoBefore; + rSI.m_bParaAutoAfter = pj->m_bParaAutoAfter; + + if (pj->m_xWWFly) + rSI.m_xWWFly = std::make_shared<WW8FlyPara>(mpIo->m_bVer67, pj->m_xWWFly.get()); + } + } + else if( mpIo->m_bNewDoc && bStyExist ) + rSI.m_pFormat->SetDerivedFrom(); + + rSI.m_nFollow = nNextStyle; // remember Follow + + mpStyRule = nullptr; // recreate if necessary + mbTextColChanged = mbFontChanged = mbCJKFontChanged = mbCTLFontChanged = + mbFSizeChanged = mbFCTLSizeChanged = mbWidowsChanged = false; + mpIo->SetNCurrentColl( nThisStyle ); + mpIo->m_bStyNormal = nThisStyle == 0; + return bOldNoImp; +} + +void WW8RStyle::PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp) +{ + // Reset attribute flags, because there are no style-ends. + + mpIo->m_bHasBorder = mpIo->m_bSpec = mpIo->m_bObj = mpIo->m_bSymbol = false; + mpIo->m_nCharFormat = -1; + + // if style is based on nothing or base ignored + if ((rSI.m_nBase >= m_cstd || mpIo->m_vColl[rSI.m_nBase].m_bImportSkipped) && rSI.m_bColl) + { + // If Char-Styles does not work + // -> set hard WW-Defaults + Set1StyleDefaults(); + } + + mpStyRule = nullptr; // to be on the safe side + mpIo->m_bStyNormal = false; + mpIo->SetNCurrentColl( 0 ); + mpIo->m_bNoAttrImport = bOldNoImp; + // reset the list-remember-fields, if used when reading styles + mpIo->m_nLFOPosition = USHRT_MAX; + mpIo->m_nListLevel = WW8ListManager::nMaxLevel; +} + +void WW8RStyle::Import1Style( sal_uInt16 nNr ) +{ + if (nNr >= mpIo->m_vColl.size()) + return; + + SwWW8StyInf &rSI = mpIo->m_vColl[nNr]; + + if( rSI.m_bImported || !rSI.m_bValid ) + return; + + rSI.m_bImported = true; // set flag now to avoid endless loops + + // valid and not NUL and not yet imported + + if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported ) + Import1Style( rSI.m_nBase ); + + mpStStrm->Seek( rSI.m_nFilePos ); + + sal_uInt16 nSkip; + OUString sName; + + std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, &sName));// read Style + + if (xStd) + rSI.SetOrgWWIdent( sName, xStd->sti ); + + // either no Name or unused Slot or unknown Style + + if ( !xStd || sName.isEmpty() || ((1 != xStd->sgc) && (2 != xStd->sgc)) ) + { + nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize()); + mpStStrm->Seek(mpStStrm->Tell() + nSkip); + return; + } + + bool bOldNoImp = PrepareStyle(rSI, static_cast<ww::sti>(xStd->sti), nNr, xStd->istdNext); + + // if something is interpreted wrong, this should make it work again + long nPos = mpStStrm->Tell(); + + //Variable parts of the STD start at even byte offsets, but "inside + //the STD", which I take to meaning even in relation to the starting + //position of the STD, which matches findings in #89439#, generally it + //doesn't matter as the STSHI starts off nearly always on an even + //offset + + //Import of the Style Contents + ImportGrupx(nSkip, xStd->sgc == 1, rSI.m_nFilePos & 1); + + PostStyle(rSI, bOldNoImp); + + mpStStrm->Seek( nPos+nSkip ); +} + +void WW8RStyle::RecursiveReg(sal_uInt16 nNr) +{ + if (nNr >= mpIo->m_vColl.size()) + return; + + SwWW8StyInf &rSI = mpIo->m_vColl[nNr]; + if( rSI.m_bImported || !rSI.m_bValid ) + return; + + rSI.m_bImported = true; + + if( rSI.m_nBase < m_cstd && !mpIo->m_vColl[rSI.m_nBase].m_bImported ) + RecursiveReg(rSI.m_nBase); + + mpIo->RegisterNumFormatOnStyle(nNr); + +} + +/* + After all styles are imported then we can recursively apply numbering + styles to them, and change their tab stop settings if they turned out + to have special first line indentation. +*/ +void WW8RStyle::PostProcessStyles() +{ + sal_uInt16 i; + /* + Clear all imported flags so that we can recursively apply numbering + formats and use it to mark handled ones + */ + for (i=0; i < m_cstd; ++i) + mpIo->m_vColl[i].m_bImported = false; + + /* + Register the num formats and tabstop changes on the styles recursively. + */ + + /* + In the same loop apply the tabstop changes required because we need to + change their location if there's a special indentation for the first line, + By avoiding making use of each styles margins during reading of their + tabstops we don't get problems with doubly adjusting tabstops that + are inheritied. + */ + for (i=0; i < m_cstd; ++i) + { + if (mpIo->m_vColl[i].m_bValid) + { + RecursiveReg(i); + } + } +} + +void WW8RStyle::ScanStyles() // investigate style dependencies +{ // and detect Filepos for each Style + for (sal_uInt16 i = 0; i < m_cstd; ++i) + { + SwWW8StyInf &rSI = mpIo->m_vColl[i]; + + rSI.m_nFilePos = mpStStrm->Tell(); // remember FilePos + sal_uInt16 nSkip; + std::unique_ptr<WW8_STD> xStd(Read1Style(nSkip, nullptr)); // read STD + rSI.m_bValid = xStd != nullptr; + if (rSI.m_bValid) + { + rSI.m_nBase = xStd->istdBase; // remember Basis + rSI.m_bColl = xStd->sgc == 1; // Para-Style + } + else + rSI = SwWW8StyInf(); + + xStd.reset(); + nSkip = std::min<sal_uInt64>(nSkip, mpStStrm->remainingSize()); + mpStStrm->Seek(mpStStrm->Tell() + nSkip); // skip Names and Sprms + } +} + +std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx) +{ + std::vector<sal_uInt8> aRet; + + aRet.push_back(60); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBold) ); + + aRet.push_back(61); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalic) ); + + aRet.push_back(62); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fStrike) ); + + aRet.push_back(63); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fOutline) ); + + aRet.push_back(65); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fSmallCaps) ); + + aRet.push_back(66); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fCaps) ); + + aRet.push_back(67); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fVanish) ); + + if (rChpx.fsFtc) + { + aRet.push_back(68); + SVBT16 a; + ShortToSVBT16(rChpx.ftc, a); + aRet.push_back(a[1]); + aRet.push_back(a[0]); + } + + if (rChpx.fsKul) + { + aRet.push_back(69); + aRet.push_back(rChpx.kul); + } + + if (rChpx.fsLid) + { + aRet.push_back(72); + SVBT16 a; + ShortToSVBT16(rChpx.lid, a); + aRet.push_back(a[1]); + aRet.push_back(a[0]); + } + + if (rChpx.fsIco) + { + aRet.push_back(73); + aRet.push_back(rChpx.ico); + } + + if (rChpx.fsHps) + { + aRet.push_back(74); + + SVBT16 a; + ShortToSVBT16(rChpx.hps, a); + aRet.push_back(a[0]); + } + + if (rChpx.fsPos) + { + aRet.push_back(76); + aRet.push_back(rChpx.hpsPos); + } + + aRet.push_back(80); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fBoldBi) ); + + aRet.push_back(81); + aRet.push_back( static_cast< sal_uInt8 >(128 + rChpx.fItalicBi) ); + + if (rChpx.fsFtcBi) + { + aRet.push_back(82); + SVBT16 a; + ShortToSVBT16(rChpx.fsFtcBi, a); + aRet.push_back(a[1]); + aRet.push_back(a[0]); + } + + if (rChpx.fsLidBi) + { + aRet.push_back(83); + SVBT16 a; + ShortToSVBT16(rChpx.lidBi, a); + aRet.push_back(a[1]); + aRet.push_back(a[0]); + } + + if (rChpx.fsIcoBi) + { + aRet.push_back(84); + aRet.push_back(rChpx.icoBi); + } + + if (rChpx.fsHpsBi) + { + aRet.push_back(85); + SVBT16 a; + ShortToSVBT16(rChpx.hpsBi, a); + aRet.push_back(a[1]); + aRet.push_back(a[0]); + } + + return aRet; +} + +Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize) +{ + Word2CHPX aChpx; + + if (!nSize) + return aChpx; + + rSt.Seek(nOffset); + + sal_uInt8 nCount=0; + + while (true) + { + sal_uInt8 nFlags8; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.fBold = nFlags8 & 0x01; + aChpx.fItalic = (nFlags8 & 0x02) >> 1; + aChpx.fRMarkDel = (nFlags8 & 0x04) >> 2; + aChpx.fOutline = (nFlags8 & 0x08) >> 3; + aChpx.fFieldVanish = (nFlags8 & 0x10) >> 4; + aChpx.fSmallCaps = (nFlags8 & 0x20) >> 5; + aChpx.fCaps = (nFlags8 & 0x40) >> 6; + aChpx.fVanish = (nFlags8 & 0x80) >> 7; + + if (nCount >= nSize) break; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.fRMark = nFlags8 & 0x01; + aChpx.fSpec = (nFlags8 & 0x02) >> 1; + aChpx.fStrike = (nFlags8 & 0x04) >> 2; + aChpx.fObj = (nFlags8 & 0x08) >> 3; + aChpx.fBoldBi = (nFlags8 & 0x10) >> 4; + aChpx.fItalicBi = (nFlags8 & 0x20) >> 5; + aChpx.fBiDi = (nFlags8 & 0x40) >> 6; + aChpx.fDiacUSico = (nFlags8 & 0x80) >> 7; + + if (nCount >= nSize) break; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.fsIco = nFlags8 & 0x01; + aChpx.fsFtc = (nFlags8 & 0x02) >> 1; + aChpx.fsHps = (nFlags8 & 0x04) >> 2; + aChpx.fsKul = (nFlags8 & 0x08) >> 3; + aChpx.fsPos = (nFlags8 & 0x10) >> 4; + aChpx.fsSpace = (nFlags8 & 0x20) >> 5; + aChpx.fsLid = (nFlags8 & 0x40) >> 6; + aChpx.fsIcoBi = (nFlags8 & 0x80) >> 7; + + if (nCount >= nSize) break; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.fsFtcBi = nFlags8 & 0x01; + aChpx.fsHpsBi = (nFlags8 & 0x02) >> 1; + aChpx.fsLidBi = (nFlags8 & 0x04) >> 2; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.ftc ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.hps ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.qpsSpace = nFlags8 & 0x3F; + aChpx.fSysVanish = (nFlags8 & 0x40) >> 6; + aChpx.fNumRun = (nFlags8 & 0x80) >> 7; + + if (nCount >= nSize) break; + rSt.ReadUChar( nFlags8 ); + nCount++; + + aChpx.ico = nFlags8 & 0x1F; + aChpx.kul = (nFlags8 & 0xE0) >> 5; + + if (nCount >= nSize) break; + rSt.ReadUChar( aChpx.hpsPos ); + nCount++; + + if (nCount >= nSize) break; + rSt.ReadUChar( aChpx.icoBi ); + nCount++; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.lid ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.ftcBi ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.hpsBi ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUInt16( aChpx.lidBi ); + nCount+=2; + + if (nCount >= nSize) break; + rSt.ReadUInt32( aChpx.fcPic ); + nCount+=4; + + break; + } + + rSt.SeekRel(nSize-nCount); + return aChpx; +} + +namespace +{ + struct pxoffset { std::size_t mnOffset; sal_uInt8 mnSize; }; +} + +void WW8RStyle::ImportOldFormatStyles() +{ + for (sal_uInt16 i=0; i < m_cstd; ++i) + { + mpIo->m_vColl[i].m_bColl = true; + //every chain must end eventually at the null style (style code 222) + mpIo->m_vColl[i].m_nBase = 222; + } + + rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset( + mpIo->m_xWwFib->m_chseTables, mpIo->m_xWwFib->m_lid); + + sal_uInt16 cstcStd(0); + m_rStream.ReadUInt16( cstcStd ); + + size_t nMaxByteCount = m_rStream.remainingSize(); + sal_uInt16 cbName(0); + m_rStream.ReadUInt16(cbName); + if (cbName > nMaxByteCount) + { + SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range " + << cbName << " to " << nMaxByteCount); + cbName = nMaxByteCount; + } + sal_uInt16 nByteCount = 2; + sal_uInt16 stcp=0; + while (nByteCount < cbName) + { + sal_uInt8 nCount(0); + m_rStream.ReadUChar( nCount ); + nByteCount++; + + sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255); + if (stc >=mpIo->m_vColl.size()) + continue; + + SwWW8StyInf &rSI = mpIo->m_vColl[stc]; + OUString sName; + + if (nCount != 0xFF) // undefined style + { + if (nCount != 0) // user style + { + OString aTmp = read_uInt8s_ToOString(m_rStream, nCount); + nByteCount += aTmp.getLength(); + sName = OStringToOUString(aTmp, eStructChrSet); + } + rSI.m_bImported = true; + } + + if (sName.isEmpty()) + { + ww::sti eSti = ww::GetCanonicalStiFromStc(stc); + if (const char *pStr = GetEnglishNameFromSti(eSti)) + sName = OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US); + } + + if (sName.isEmpty()) + sName = "Unknown Style: " + OUString::number(stc); + + rSI.SetOrgWWIdent(sName, stc); + stcp++; + } + + sal_uInt16 nStyles=stcp; + + std::vector<pxoffset> aCHPXOffsets(stcp); + nMaxByteCount = m_rStream.remainingSize(); + sal_uInt16 cbChpx(0); + m_rStream.ReadUInt16(cbChpx); + if (cbChpx > nMaxByteCount) + { + SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range " + << cbChpx << " to " << nMaxByteCount); + cbChpx = nMaxByteCount; + } + nByteCount = 2; + stcp=0; + std::vector< std::vector<sal_uInt8> > aConvertedChpx; + while (nByteCount < cbChpx) + { + if (stcp == aCHPXOffsets.size()) + { + //more data than style slots, skip remainder + m_rStream.SeekRel(cbChpx-nByteCount); + break; + } + + sal_uInt8 cb(0); + m_rStream.ReadUChar( cb ); + nByteCount++; + + aCHPXOffsets[stcp].mnSize = 0; + + if (cb != 0xFF) + { + sal_uInt8 nRemainder = cb; + + aCHPXOffsets[stcp].mnOffset = m_rStream.Tell(); + aCHPXOffsets[stcp].mnSize = nRemainder; + + Word2CHPX aChpx = ReadWord2Chpx(m_rStream, aCHPXOffsets[stcp].mnOffset, + aCHPXOffsets[stcp].mnSize); + aConvertedChpx.push_back( ChpxToSprms(aChpx) ); + + nByteCount += nRemainder; + } + else + aConvertedChpx.emplace_back( ); + + ++stcp; + } + + std::vector<pxoffset> aPAPXOffsets(stcp); + nMaxByteCount = m_rStream.remainingSize(); + sal_uInt16 cbPapx(0); + m_rStream.ReadUInt16(cbPapx); + if (cbPapx > nMaxByteCount) + { + SAL_WARN("sw.ww8", "WW8RStyle::ImportOldFormatStyles: truncating out of range " + << cbPapx << " to " << nMaxByteCount); + cbPapx = nMaxByteCount; + } + nByteCount = 2; + stcp=0; + while (nByteCount < cbPapx) + { + if (stcp == aPAPXOffsets.size()) + { + m_rStream.SeekRel(cbPapx-nByteCount); + break; + } + + sal_uInt8 cb(0); + m_rStream.ReadUChar( cb ); + nByteCount++; + + aPAPXOffsets[stcp].mnSize = 0; + + if (cb != 0xFF) + { + sal_uInt8 stc2(0); + m_rStream.ReadUChar( stc2 ); + m_rStream.SeekRel(6); + nByteCount+=7; + sal_uInt8 nRemainder = cb-7; + + aPAPXOffsets[stcp].mnOffset = m_rStream.Tell(); + aPAPXOffsets[stcp].mnSize = nRemainder; + + m_rStream.SeekRel(nRemainder); + nByteCount += nRemainder; + } + + ++stcp; + } + + sal_uInt16 iMac(0); + m_rStream.ReadUInt16( iMac ); + + if (iMac > nStyles) iMac = nStyles; + + for (stcp = 0; stcp < iMac; ++stcp) + { + sal_uInt8 stcNext(0), stcBase(0); + m_rStream.ReadUChar( stcNext ); + m_rStream.ReadUChar( stcBase ); + + sal_uInt8 stc = static_cast< sal_uInt8 >((stcp - cstcStd) & 255); + + /* + #i64557# style based on itself + every chain must end eventually at the null style (style code 222) + */ + if (stc == stcBase) + stcBase = 222; + + SwWW8StyInf &rSI = mpIo->m_vColl[stc]; + rSI.m_nBase = stcBase; + + ww::sti eSti = ww::GetCanonicalStiFromStc(stc); + + if (eSti == ww::stiNil) + continue; + + if (stcp >= aPAPXOffsets.size()) + continue; + + rSI.m_bValid = true; + + if (ww::StandardStiIsCharStyle(eSti) && !aPAPXOffsets[stcp].mnSize) + mpIo->m_vColl[stc].m_bColl = false; + + bool bOldNoImp = PrepareStyle(rSI, eSti, stc, stcNext); + + ImportSprms(aPAPXOffsets[stcp].mnOffset, aPAPXOffsets[stcp].mnSize, + true); + + if (!aConvertedChpx[stcp].empty()) + ImportSprms(aConvertedChpx[stcp].data(), + static_cast< short >(aConvertedChpx[stcp].size()), + false); + + PostStyle(rSI, bOldNoImp); + } +} + +void WW8RStyle::ImportNewFormatStyles() +{ + ScanStyles(); // Scan Based On + + for (sal_uInt16 i = 0; i < m_cstd; ++i) // import Styles + if (mpIo->m_vColl[i].m_bValid) + Import1Style( i ); +} + +void WW8RStyle::Import() +{ + mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl(); + mpIo->m_pStandardFormatColl = + mpIo->m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false); + + if( mpIo->m_nIniFlags & WW8FL_NO_STYLES ) + return; + + if (mpIo->m_xWwFib->GetFIBVersion() <= ww::eWW2) + ImportOldFormatStyles(); + else + ImportNewFormatStyles(); + + for (sal_uInt16 i = 0; i < m_cstd; ++i) + { + // Follow chain + SwWW8StyInf* pi = &mpIo->m_vColl[i]; + sal_uInt16 j = pi->m_nFollow; + if( j < m_cstd ) + { + SwWW8StyInf* pj = &mpIo->m_vColl[j]; + if ( j != i // rational Index ? + && pi->m_pFormat // Format ok ? + && pj->m_pFormat // Derived-Format ok ? + && pi->m_bColl // only possible for paragraph templates (WW) + && pj->m_bColl ){ // identical Type ? + static_cast<SwTextFormatColl*>(pi->m_pFormat)->SetNextTextFormatColl( + *static_cast<SwTextFormatColl*>(pj->m_pFormat) ); // ok, register + } + } + } + + // Missing special handling for default character template + // "Absatz-Standardschriftart" ( Style-ID 65 ). + // That is empty by default ( WW6 dt and US ) and not changeable + // via WW-UI so this does not matter. + // This could be done by: + // if( bNew ) rDoc.SetDefault( pDefCharFormat->GetAttrSet() ); + + // for e.g. tables an always valid Std-Style is necessary + + if( mpIo->StyleExists(0) && !mpIo->m_vColl.empty() && + mpIo->m_vColl[0].m_pFormat && mpIo->m_vColl[0].m_bColl && mpIo->m_vColl[0].m_bValid ) + mpIo->m_pDfltTextFormatColl = static_cast<SwTextFormatColl*>(mpIo->m_vColl[0].m_pFormat); + else + mpIo->m_pDfltTextFormatColl = mpIo->m_rDoc.GetDfltTextFormatColl(); + + // set Hyphenation flag on BASIC para-style + if (mpIo->m_bNewDoc && mpIo->m_pStandardFormatColl) + { + if (mpIo->m_xWDop->fAutoHyphen + && SfxItemState::SET != mpIo->m_pStandardFormatColl->GetItemState( + RES_PARATR_HYPHENZONE, false) ) + { + SvxHyphenZoneItem aAttr(true, RES_PARATR_HYPHENZONE); + aAttr.GetMinLead() = 2; + aAttr.GetMinTrail() = 2; + aAttr.GetMaxHyphens() = 0; + + mpIo->m_pStandardFormatColl->SetFormatAttr( aAttr ); + } + } + + // we do not read styles anymore: + mpIo->m_pCurrentColl = nullptr; +} + +rtl_TextEncoding SwWW8StyInf::GetCharSet() const +{ + if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB)) + return m_eRTLFontSrcCharSet; + return m_eLTRFontSrcCharSet; +} + +rtl_TextEncoding SwWW8StyInf::GetCJKCharSet() const +{ + if (m_pFormat && (m_pFormat->GetFrameDir().GetValue() == SvxFrameDirection::Horizontal_RL_TB)) + return m_eRTLFontSrcCharSet; + return m_eCJKFontSrcCharSet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par2.hxx b/sw/source/filter/ww8/ww8par2.hxx new file mode 100644 index 000000000..22ca540b5 --- /dev/null +++ b/sw/source/filter/ww8/ww8par2.hxx @@ -0,0 +1,298 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_WW8PAR2_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8PAR2_HXX + +#include <fmtfsize.hxx> +#include <svl/itemset.hxx> +#include <svx/swframetypes.hxx> +#include <swtable.hxx> + +#include "ww8scan.hxx" +#include "ww8par.hxx" + +class WW8RStyle; + +class WW8DupProperties +{ +public: + WW8DupProperties(SwDoc &rDoc, SwWW8FltControlStack *pStack); + void Insert(const SwPosition &rPos); +private: + WW8DupProperties(const WW8DupProperties&) = delete; + WW8DupProperties& operator=(const WW8DupProperties&) = delete; + SwWW8FltControlStack* pCtrlStck; + SfxItemSet aChrSet,aParSet; +}; + +struct WW8SwFlyPara +{ + SwFlyFrameFormat* pFlyFormat; + + // part 1: directly derived Sw attributes + sal_Int16 nXPos, nYPos; // Position + sal_Int16 nLeMgn, nRiMgn; // borders + sal_Int16 nUpMgn, nLoMgn; // borders + sal_Int16 nWidth, nHeight; // size + sal_Int16 nNetWidth; + + SwFrameSize eHeightFix; // height fixed or min + static constexpr RndStdIds eAnchor = RndStdIds::FLY_AT_PARA; // binding + short eHRel; // page or page border + short eVRel; // page or page border + sal_Int16 eVAlign; // up, down, centered + sal_Int16 eHAlign; // left, right, centered + css::text::WrapTextMode + eSurround; // wrap mode + + sal_uInt8 nXBind, nYBind; // bound relative to what + + // part 2: changes found during reading + long nNewNetWidth; + std::unique_ptr<SwPosition> xMainTextPos; // to return to main text after apo + sal_uInt16 nLineSpace; // line space in tw for graf apos + bool bAutoWidth; + bool bToggelPos; + + // add parameter <nWWPgTop> - WW8's page top margin + WW8SwFlyPara( SwPaM& rPaM, + SwWW8ImplReader& rIo, + WW8FlyPara& rWW, + const sal_uInt32 nWWPgTop, + const sal_uInt32 nPgWidth, + const sal_Int32 nIniFlyDx, + const sal_Int32 nIniFlyDy ); + + void BoxUpWidth( long nWidth ); + std::unique_ptr<SwWW8FltAnchorStack> xOldAnchorStck; +}; + +class WW8RStyle: public WW8Style +{ +friend class SwWW8ImplReader; + wwSprmParser maSprmParser; + SwWW8ImplReader* mpIo; // parser class + SvStream* mpStStrm; // input file + + SwNumRule* mpStyRule; // bullets and enumerations in styles + + sal_uInt8* mpParaSprms; // all ParaSprms of the UPX if UPX.Papx + sal_uInt16 mnSprmsLen; // its length + + sal_uInt8 mnWwNumLevel; // for bullets and enumerations in styles + + bool mbTextColChanged; + bool mbFontChanged; // For Simulating Default-Font + bool mbCJKFontChanged; // For Simulating Default-CJK Font + bool mbCTLFontChanged; // For Simulating Default-CTL Font + bool mbFSizeChanged; // For Simulating Default-FontSize + bool mbFCTLSizeChanged; // For Simulating Default-CTL FontSize + bool mbWidowsChanged; // For Simulating Default-Widows / Orphans + bool mbBidiChanged; // For Simulating Default-LTR + + void ImportSprms(std::size_t nPosFc, short nLen, bool bPap); + void ImportSprms(sal_uInt8 *pSprms, short nLen, bool bPap); + void ImportGrupx(short nLen, bool bPara, bool bOdd); + short ImportUPX(short nLen, bool bPAP, bool bOdd); + + void Set1StyleDefaults(); + void Import1Style(sal_uInt16 nNr); + void RecursiveReg(sal_uInt16 nNr); + + void ImportNewFormatStyles(); + void ScanStyles(); + void ImportOldFormatStyles(); + + bool PrepareStyle(SwWW8StyInf &rSI, ww::sti eSti, sal_uInt16 nThisStyle, sal_uInt16 nNextStyle); + void PostStyle(SwWW8StyInf const &rSI, bool bOldNoImp); + + WW8RStyle(const WW8RStyle&) = delete; + WW8RStyle& operator=(const WW8RStyle&) = delete; +public: + WW8RStyle( WW8Fib& rFib, SwWW8ImplReader* pI ); + void Import(); + void PostProcessStyles(); + SprmResult HasParaSprm(sal_uInt16 nId) const; +}; + +class WW8FlySet: public SfxItemSet +{ +private: + const WW8FlySet& operator=(const WW8FlySet&) = delete; + void Init(const SwWW8ImplReader& rReader, const SwPaM* pPaM); +public: + WW8FlySet(SwWW8ImplReader& rReader, const WW8FlyPara* pFW, + const WW8SwFlyPara* pFS, bool bGraf); + WW8FlySet(SwWW8ImplReader& rReader, const SwPaM* pPaM, const WW8_PIC& rPic, + long nWidth, long nHeight); +}; + +// Gets filled in WW8TabDesc::MergeCells(). +// Algorithm must ensure proper row and column order in WW8SelBoxInfo! +class WW8SelBoxInfo +{ +private: + std::vector<std::vector<SwTableBox*> > m_vRows; + + WW8SelBoxInfo(WW8SelBoxInfo const&) = delete; + WW8SelBoxInfo& operator=(WW8SelBoxInfo const&) = delete; + +public: + short nGroupXStart; + short nGroupWidth; + bool bGroupLocked; + + WW8SelBoxInfo(short nXCenter, short nWidth) + : nGroupXStart( nXCenter ), nGroupWidth( nWidth ), bGroupLocked(false) + {} + + size_t size() const + { + size_t nResult = 0; + for (auto& it : m_vRows) + nResult += it.size(); + return nResult; + } + + size_t rowsCount() const { return m_vRows.size(); } + + const std::vector<SwTableBox*>& row( size_t nIndex ) { return m_vRows[nIndex]; } + + void push_back( SwTableBox* pBox ) + { + bool bDone = false; + for (auto& iRow : m_vRows) + if (iRow[0]->GetUpper() == pBox->GetUpper()) + { + iRow.push_back(pBox); + bDone = true; + break; + } + if (!bDone) + { + const size_t sz = m_vRows.size(); + m_vRows.resize(sz+1); + m_vRows[sz].push_back(pBox); + } + } +}; + +class WW8TabDesc +{ + std::vector<OUString> m_aNumRuleNames; + std::unique_ptr<sw::util::RedlineStack> mxOldRedlineStack; + + SwWW8ImplReader* m_pIo; + + WW8TabBandDesc* m_pFirstBand; + WW8TabBandDesc* m_pActBand; + + std::unique_ptr<SwPosition> m_xTmpPos; + + SwTableNode* m_pTableNd; // table node + const SwTableLines* m_pTabLines; // row array of node + SwTableLine* m_pTabLine; // current row + SwTableBoxes* m_pTabBoxes; // boxes array in current row + SwTableBox* m_pTabBox; // current cell + + std::vector<std::unique_ptr<WW8SelBoxInfo>> m_MergeGroups; // list of all cells to be merged + + WW8_TCell* m_pCurrentWWCell; + + short m_nRows; + short m_nDefaultSwCols; + short m_nBands; + short m_nMinLeft; + short m_nConvertedLeft; + short m_nMaxRight; + short m_nSwWidth; + short m_nPreferredWidth; + short m_nPercentWidth; + + bool m_bOk; + bool m_bClaimLineFormat; + sal_Int16 m_eOri; + bool m_bIsBiDi; + // 2. common admin info + short m_nCurrentRow; + short m_nCurrentBandRow; // SW: row of current band + // 3. admin info for writer + short m_nCurrentCol; + + sal_uInt16 m_nRowsToRepeat; + + // 4. methods + + sal_uInt16 GetLogicalWWCol() const; + void SetTabBorders( SwTableBox* pBox, short nIdx ); + void SetTabShades( SwTableBox* pBox, short nWwIdx ); + void SetTabVertAlign( SwTableBox* pBox, short nWwIdx ); + void SetTabDirection( SwTableBox* pBox, short nWwIdx ); + void CalcDefaults(); + void SetPamInCell(short nWwCol, bool bPam); + void InsertCells( short nIns ); + void AdjustNewBand(); + + WW8SelBoxInfo* FindMergeGroup(short nX1, short nWidth, bool bExact); + + // single box - maybe used in a merge group + // (the merge groups are processed later at once) + void UpdateTableMergeGroup(WW8_TCell const & rCell, + WW8SelBoxInfo* pActGroup, SwTableBox* pActBox, sal_uInt16 nCol ); + + WW8TabDesc(WW8TabDesc const&) = delete; + WW8TabDesc& operator=(WW8TabDesc const&) = delete; + +public: + const SwTable* m_pTable; // table + SwPosition* m_pParentPos; + SwFlyFrameFormat* m_pFlyFormat; + SfxItemSet m_aItemSet; + bool IsValidCell(short nCol) const; + bool InFirstParaInCell() const; + + WW8TabDesc( SwWW8ImplReader* pIoClass, WW8_CP nStartCp ); + bool Ok() const { return m_bOk; } + void CreateSwTable(); + void UseSwTable(); + void SetSizePosition(SwFrameFormat* pFrameFormat); + void TableCellEnd(); + void MoveOutsideTable(); + void ParkPaM(); + void FinishSwTable(); + void MergeCells(); + short GetMinLeft() const { return m_nConvertedLeft; } + ~WW8TabDesc(); + + const WW8_TCell* GetCurrentWWCell() const { return m_pCurrentWWCell; } + short GetCurrentCol() const { return m_nCurrentCol; } + // find name of numrule valid for current WW-COL + OUString GetNumRuleName() const; + void SetNumRuleName( const OUString& rName ); + + sw::util::RedlineStack* getOldRedlineStack() { return mxOldRedlineStack.get(); } +}; + +enum WW8LvlType {WW8_None, WW8_Outline, WW8_Numbering, WW8_Sequence, WW8_Pause}; + +WW8LvlType GetNumType(sal_uInt8 nWwLevelNo); +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par3.cxx b/sw/source/filter/ww8/ww8par3.cxx new file mode 100644 index 000000000..5b49f9838 --- /dev/null +++ b/sw/source/filter/ww8/ww8par3.cxx @@ -0,0 +1,2543 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/itemiter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <sal/log.hxx> + +#include <vcl/unohelp.hxx> +#include <com/sun/star/form/XFormComponent.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <algorithm> +#include <hintids.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <o3tl/safeint.hxx> +#include <unotextrange.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <docsh.hxx> +#include <numrule.hxx> +#include <paratr.hxx> +#include <charatr.hxx> +#include <charfmt.hxx> +#include <ndtxt.hxx> +#include <expfld.hxx> +#include <fmtfld.hxx> +#include <flddropdown.hxx> +#include "sprmids.hxx" +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "ww8par.hxx" +#include "ww8par2.hxx" + +#include <IMark.hxx> +#include <unotools/fltrcfg.hxx> +#include <rtl/character.hxx> +#include <xmloff/odffields.hxx> +#include <comphelper/string.hxx> + +using namespace com::sun::star; +using namespace sw::util; +using namespace sw::types; +using namespace sw::mark; + +// UNO-Controls + +// OCX i.e. word 97 form controls +eF_ResT SwWW8ImplReader::Read_F_OCX( WW8FieldDesc*, OUString& ) +{ + if( m_bObj && m_nPicLocFc ) + m_nObjLocFc = m_nPicLocFc; + m_bEmbeddObj = true; + return eF_ResT::TEXT; +} + +eF_ResT SwWW8ImplReader::Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr ) +{ + WW8FormulaEditBox aFormula(*this); + + sal_Int32 const nPos(rStr.indexOf(0x01)); + if (pF->nLCode && nPos != -1 && nPos < pF->nLCode) { + ImportFormulaControl(aFormula, pF->nSCode + nPos, WW8_CT_EDIT); + } + + /* + Here we have a small complication. This formula control contains + the default text that is displayed if you edit the form field in + the "default text" area. But MSOffice does not display that + information, instead it display the result of the field, + MSOffice just uses the default text of the control as its + initial value for the displayed default text. So we will swap in + the field result into the formula here in place of the default + text. + */ + + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + const bool bUseEnhFields = rOpt.IsUseEnhancedFields(); + + if (!bUseEnhFields) + { + aFormula.msDefault = GetFieldResult(pF); + + SwInputField aField( + static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )), + aFormula.msDefault, + aFormula.msTitle, + INP_TXT, + 0 ); + aField.SetHelp(aFormula.msHelp); + aField.SetToolTip(aFormula.msToolTip); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + return eF_ResT::OK; + } + else + { + WW8PLCFx_Book* pB = m_xPlcxMan->GetBook(); + OUString aBookmarkName; + if (pB!=nullptr) { + WW8_CP currentCP=pF->nSCode; + WW8_CP currentLen=pF->nLen; + + WW8_CP nEnd; + if (o3tl::checked_add(currentCP, currentLen-1, nEnd)) { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + } + else + { + sal_uInt16 bkmFindIdx; + OUString aBookmarkFind=pB->GetBookmark(currentCP-1, nEnd, bkmFindIdx); + + if (!aBookmarkFind.isEmpty()) { + pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark bookmark as consumed, such that tl'll not get inserted as a "normal" bookmark again + if (!aBookmarkFind.isEmpty()) { + aBookmarkName=aBookmarkFind; + } + } + } + } + + if (pB!=nullptr && aBookmarkName.isEmpty()) { + aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle); + } + + if (!aBookmarkName.isEmpty()) { + m_aFieldStack.back().SetBookmarkName(aBookmarkName); + m_aFieldStack.back().SetBookmarkType(ODF_FORMTEXT); + if ( aFormula.msToolTip.getLength() < 139 ) + m_aFieldStack.back().getParameters()["Description"] <<= aFormula.msToolTip; + m_aFieldStack.back().getParameters()["Name"] <<= aFormula.msTitle; + if (aFormula.mnMaxLen && aFormula.mnMaxLen < 32768 ) + m_aFieldStack.back().getParameters()["MaxLength"] <<= aFormula.mnMaxLen; + + if ( aFormula.mfType == 1 ) + m_aFieldStack.back().getParameters()["Type"] <<= OUString("number"); + else if ( aFormula.mfType == 2 ) + m_aFieldStack.back().getParameters()["Type"] <<= OUString("date"); + else if ( aFormula.mfType == 3 ) + m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentTime"); + else if ( aFormula.mfType == 4 ) + m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentDate"); + else if ( aFormula.mfType == 5 ) + m_aFieldStack.back().getParameters()["Type"] <<= OUString("calculated"); + } + return eF_ResT::TEXT; + } +} + +eF_ResT SwWW8ImplReader::Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr ) +{ + WW8FormulaCheckBox aFormula(*this); + + if (!m_xFormImpl) + m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM)); + + if (rStr[pF->nLCode-1]==0x01) + ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_CHECKBOX); + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + const bool bUseEnhFields = rOpt.IsUseEnhancedFields(); + + if (!bUseEnhFields) + { + m_xFormImpl->InsertFormula(aFormula); + return eF_ResT::OK; + } + + OUString aBookmarkName; + WW8PLCFx_Book* pB = m_xPlcxMan->GetBook(); + if (pB!=nullptr) { + WW8_CP currentCP=pF->nSCode; + WW8_CP currentLen=pF->nLen; + + sal_uInt16 bkmFindIdx; + OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx); + + if (!aBookmarkFind.isEmpty()) { + pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field + if (!aBookmarkFind.isEmpty()) { + aBookmarkName=aBookmarkFind; + } + } + } + + if (pB!=nullptr && aBookmarkName.isEmpty()) { + aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle); + } + + if (!aBookmarkName.isEmpty()) + { + IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( ); + IFieldmark* pFieldmark = pMarksAccess->makeNoTextFieldBookmark( + *m_pPaM, aBookmarkName, ODF_FORMCHECKBOX ); + OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?"); + if (pFieldmark!=nullptr) { + IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters(); + ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark*>(pFieldmark); + (*pParameters)[ODF_FORMCHECKBOX_NAME] <<= aFormula.msTitle; + (*pParameters)[ODF_FORMCHECKBOX_HELPTEXT] <<= aFormula.msToolTip; + + if(pCheckboxFm) + pCheckboxFm->SetChecked(aFormula.mnChecked != 0); + // set field data here... + } + } + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr) +{ + WW8FormulaListBox aFormula(*this); + + if (pF->nLCode > 0 && rStr.getLength() >= pF->nLCode && rStr[pF->nLCode-1] == 0x01) + ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_DROPDOWN); + + const SvtFilterOptions& rOpt = SvtFilterOptions::Get(); + bool bUseEnhFields = rOpt.IsUseEnhancedFields(); + + if (!bUseEnhFields) + { + SwDropDownField aField(static_cast<SwDropDownFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Dropdown))); + + aField.SetName(aFormula.msTitle); + aField.SetHelp(aFormula.msHelp); + aField.SetToolTip(aFormula.msToolTip); + + if (!aFormula.maListEntries.empty()) + { + aField.SetItems(aFormula.maListEntries); + int nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0; + aField.SetSelectedItem(aFormula.maListEntries[nIndex]); + } + + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + return eF_ResT::OK; + } + else + { + // TODO: review me + OUString aBookmarkName; + WW8PLCFx_Book* pB = m_xPlcxMan->GetBook(); + if (pB!=nullptr) + { + WW8_CP currentCP=pF->nSCode; + WW8_CP currentLen=pF->nLen; + + sal_uInt16 bkmFindIdx; + OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx); + + if (!aBookmarkFind.isEmpty()) + { + pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field + if (!aBookmarkFind.isEmpty()) + aBookmarkName=aBookmarkFind; + } + } + + if (pB!=nullptr && aBookmarkName.isEmpty()) + aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle); + + if (!aBookmarkName.isEmpty()) + { + IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( ); + IFieldmark *pFieldmark = + pMarksAccess->makeNoTextFieldBookmark( *m_pPaM, aBookmarkName, ODF_FORMDROPDOWN ); + OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?"); + if ( pFieldmark != nullptr ) + { + uno::Sequence< OUString > vListEntries(aFormula.maListEntries.size()); + std::copy(aFormula.maListEntries.begin(), aFormula.maListEntries.end(), vListEntries.begin()); + (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries; + sal_Int32 nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0; + (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= nIndex; + // set field data here... + } + } + + return eF_ResT::OK; + } +} + +eF_ResT SwWW8ImplReader::Read_F_HTMLControl(WW8FieldDesc*, OUString&) +{ + if( m_bObj && m_nPicLocFc ) + m_nObjLocFc = m_nPicLocFc; + m_bEmbeddObj = true; + return eF_ResT::TEXT; +} + +// Helper declarations + +// Style Id's for each level +typedef sal_uInt16 WW8aIdSty[WW8ListManager::nMaxLevel]; +// Character Style Pointer +typedef SwCharFormat* WW8aCFormat[WW8ListManager::nMaxLevel]; + +namespace { + +struct WW8LST // only THOSE entries, WE need! +{ + WW8aIdSty aIdSty; // Style Id's for each level, + // nIStDNil if no style linked + sal_uInt32 nIdLst; // Unique List ID + sal_uInt32 nTplC; // Unique template code - What is this? + bool bSimpleList:1; // Flag: List only has ONE level + bool bRestartHdn:1; // WW6-Compatibility-Flag: + // true if the list should start numbering over +}; // at the beginning of each section + +} + +const sal_uInt32 cbLSTF=28; + +namespace { + +struct WW8LFO // only THOSE entries, WE need! +{ + SwNumRule* pNumRule; // Parent NumRule + sal_uInt32 nIdLst; // Unique List ID + sal_uInt8 nLfoLvl; // count of levels whose format is overridden + bool bSimpleList; +}; + +struct WW8LVL // only THE entries, WE need! +{ + sal_Int32 nStartAt; // start at value for this value + sal_Int32 nV6DxaSpace;// Ver6-Compatible: min Space between Num and text::Paragraph + sal_Int32 nV6Indent; // Ver6-Compatible: Width of prefix text; + // Use definition of first line indent if appropriate! + // Paragraph attributes from GrpprlPapx + sal_uInt16 nDxaLeft; // left indent + short nDxaLeft1; // first line indent + + sal_uInt8 nNFC; // number format code + // Offset of fieldcodes in Num-X-String + sal_uInt8 aOfsNumsXCH[WW8ListManager::nMaxLevel]; + sal_uInt8 nLenGrpprlChpx; // length, in bytes, of the LVL's grpprlChpx + sal_uInt8 nLenGrpprlPapx; // length, in bytes, of the LVL's grpprlPapx + sal_uInt8 nAlign: 2; // alignment (left, right, centered) of the number + sal_uInt8: 1; // doesn't matter ("bLegal") + sal_uInt8: 1; // doesn't matter ("bNoRest") + sal_uInt8 bV6Prev:1; // Ver6-Compatible: number will include previous levels + sal_uInt8 bV6PrSp:1; // Ver6-Compatible: doesn't matter + sal_uInt8 bV6: 1; // if true, pay attention to the V6-Compatible Entries! + sal_uInt8: 1; // (fills the byte) + +}; + +struct WW8LFOLVL +{ + sal_Int32 nStartAt; // start-at value if bFormat==false and bStartAt == true + // (if bFormat==true, the start-at is stored in the LVL) + sal_uInt8 nLevel; // the level to be overridden + // this byte has not been packed into the following byte on _purpose_ !! + // (see comment of struct WW8LFOInfo) + + bool bStartAt :1; // true if the start-at value is overridden + bool bFormat :1; // true if the formatting is overridden + + WW8LFOLVL() : + nStartAt(1), nLevel(0), bStartAt(true), bFormat(false) {} +}; + +} + +// Data to be saved in ListInfo + +struct WW8LSTInfo // sorted by nIdLst (in WW8 used list-Id) +{ + std::vector<ww::bytes> maParaSprms; + WW8aIdSty aIdSty; // Style Id's for each level + WW8aCFormat aCharFormat = {}; // Character Style Pointer + + SwNumRule* pNumRule; // Pointer to list-template in Writer + sal_uInt32 nIdLst; // WW8Id of this list + bool bSimpleList:1;// Flag, if this NumRule only uses one Level + bool bUsedInDoc :1;// Flag, if this NumRule is used in the Doc, + // or is supposed to be deleted on Reader-End + + WW8LSTInfo(SwNumRule* pNumRule_, const WW8LST& aLST) + : pNumRule(pNumRule_), nIdLst(aLST.nIdLst), + bSimpleList(aLST.bSimpleList), bUsedInDoc(false) + { + memcpy( aIdSty, aLST.aIdSty, sizeof( aIdSty )); + } + +}; + +// Data to be saved in ListenFormatOverrideInfos + +struct WW8LFOInfo // unordered, means ordered like in WW8 Stream +{ + std::vector<ww::bytes> maParaSprms; + std::vector<WW8LFOLVL> maOverrides; + SwNumRule* pNumRule; // Pointer to list template in Writer + // either List in LSTInfos or own List + // (in Ctor use the list from LSTInfos first) + + sal_uInt32 nIdLst; // WW8-Id of the relevant list + sal_uInt8 nLfoLvl; // count of levels whose format is overridden + // yes we could include nLfoLvl (via :4) into the following byte, + // but it probably would be a source of error once MS increases their Listformat + // to more than 15 levels + + bool bOverride :1;// Flag if NumRule is not included in maLSTInfos, + // but was created for m_LFOInfos + bool bUsedInDoc :1;// Flag if NumRule is used in Doc, + // or should be deleted on Reader-End + bool bLSTbUIDSet :1;// Flag, if bUsedInDoc is set in maLSTInfos + + explicit WW8LFOInfo(const WW8LFO& rLFO); +}; + +WW8LFOInfo::WW8LFOInfo(const WW8LFO& rLFO) + : maParaSprms(WW8ListManager::nMaxLevel) + , maOverrides(WW8ListManager::nMaxLevel) + , pNumRule(rLFO.pNumRule) + , nIdLst(rLFO.nIdLst) + , nLfoLvl(rLFO.nLfoLvl) + , bOverride(rLFO.nLfoLvl != 0) + , bUsedInDoc(false) + , bLSTbUIDSet(false) +{ +} + +// Helper methods + +// find Sprm-Parameter-Data, if Sprm is included in Grpprl +SprmResult WW8ListManager::GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms, + sal_uInt8 nLen) +{ + return maSprmParser.findSprmData(nId, &rSprms, nLen); +} + +namespace { + +class ListWithId +{ +private: + sal_uInt32 mnIdLst; +public: + explicit ListWithId(sal_uInt32 nIdLst) : mnIdLst(nIdLst) {} + bool operator() (const std::unique_ptr<WW8LSTInfo>& pEntry) const + { return (pEntry->nIdLst == mnIdLst); } +}; + +} + +// Access via List-Id of LST Entry +WW8LSTInfo* WW8ListManager::GetLSTByListId( sal_uInt32 nIdLst ) const +{ + auto aResult = + std::find_if(maLSTInfos.begin(),maLSTInfos.end(),ListWithId(nIdLst)); + if (aResult == maLSTInfos.end()) + return nullptr; + return aResult->get(); +} + +static OUString sanitizeString(const OUString& rString) +{ + sal_Int32 i=0; + while (i < rString.getLength()) + { + sal_Unicode c = rString[i]; + if (rtl::isHighSurrogate(c)) + { + if (i+1 == rString.getLength() + || !rtl::isLowSurrogate(rString[i+1])) + { + SAL_WARN("sw.ww8", "Surrogate error: high without low"); + return rString.copy(0, i); + } + ++i; //skip correct low + } + if (rtl::isLowSurrogate(c)) //bare low without preceding high + { + SAL_WARN("sw.ww8", "Surrogate error: low without high"); + return rString.copy(0, i); + } + ++i; + } + return rString; +} + +SvxNumType WW8ListManager::GetSvxNumTypeFromMSONFC(sal_uInt16 nNFC) +{ + SvxNumType nType(SVX_NUM_ARABIC); + + switch (nNFC) + { + case 0: + nType = SVX_NUM_ARABIC; + break; + case 1: + nType = SVX_NUM_ROMAN_UPPER; + break; + case 2: + nType = SVX_NUM_ROMAN_LOWER; + break; + case 3: + nType = SVX_NUM_CHARS_UPPER_LETTER_N; + break; + case 4: + nType = SVX_NUM_CHARS_LOWER_LETTER_N; + break; + case 5: + // actually: ORDINAL + nType = SVX_NUM_ARABIC; + break; + case 9: + // 0x09, msonfcChiManSty + nType = SVX_NUM_SYMBOL_CHICAGO; + break; + case 22: + // 0x16, msonfcArabicLZ + nType = SVX_NUM_ARABIC_ZERO; + break; + case 23: + nType = SVX_NUM_CHAR_SPECIAL; + + break; + case 255: + nType = SVX_NUM_NUMBER_NONE; + break; + case 14: + case 19: + nType = SVX_NUM_FULL_WIDTH_ARABIC; + break; + case 30: + nType = SVX_NUM_TIAN_GAN_ZH; + break; + case 31: + nType = SVX_NUM_DI_ZI_ZH; + break; + case 35: + case 36: + case 37: + case 11: + case 39: + nType = SVX_NUM_NUMBER_LOWER_ZH; + break; + case 34: + nType = SVX_NUM_NUMBER_UPPER_ZH_TW; + break; + case 38: + nType = SVX_NUM_NUMBER_UPPER_ZH; + break; + case 10: + nType = SVX_NUM_NUMBER_TRADITIONAL_JA; + break; + case 20: + nType = SVX_NUM_AIU_FULLWIDTH_JA; + break; + case 12: + nType = SVX_NUM_AIU_HALFWIDTH_JA; + break; + case 21: + nType = SVX_NUM_IROHA_FULLWIDTH_JA; + break; + case 13: + nType = SVX_NUM_IROHA_HALFWIDTH_JA; + break; + case 24: + nType = SVX_NUM_HANGUL_SYLLABLE_KO; + break; + case 25: + nType = SVX_NUM_HANGUL_JAMO_KO; + break; + case 41: + nType = SVX_NUM_NUMBER_HANGUL_KO; + break; + //case 42: + //case 43: + case 44: + nType = SVX_NUM_NUMBER_UPPER_KO; + break; + default: + nType = SVX_NUM_ARABIC; + break; + } + + return nType; +} + +bool WW8ListManager::ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet, + sal_uInt16 nLevelStyle, bool bSetStartNo, sal_uInt16 /*nLevel*/, ww::bytes &rParaSprms) +{ + sal_uInt8 aBits1(0); + SvxNumType nType(SVX_NUM_ARABIC); + SvxAdjust eAdj; // Alignment (Left/right/centered) + sal_Unicode cBullet(0x2190); // default safe bullet + + sal_Unicode cGrfBulletCP(USHRT_MAX); + + WW8LVL aLVL = {}; + + // 1. read LVLF + + rSt.ReadInt32( aLVL.nStartAt ); + rSt.ReadUChar( aLVL.nNFC ); + rSt.ReadUChar( aBits1 ); + if( ERRCODE_NONE != rSt.GetError() ) return false; + aLVL.nAlign = (aBits1 & 0x03); + if( aBits1 & 0x10 ) aLVL.bV6Prev = sal_uInt8(true); + if( aBits1 & 0x20 ) aLVL.bV6PrSp = sal_uInt8(true); + if( aBits1 & 0x40 ) aLVL.bV6 = sal_uInt8(true); + bool bLVLOkB = true; + for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB) + { + rSt.ReadUChar( aLVL.aOfsNumsXCH[ nLevelB ] ); + if( ERRCODE_NONE != rSt.GetError() ) + { + bLVLOkB = false; + break; + } + } + + if( !bLVLOkB ) + return false; + + sal_uInt8 ixchFollow(0); + rSt.ReadUChar( ixchFollow ); + rSt.ReadInt32( aLVL.nV6DxaSpace ); + rSt.ReadInt32( aLVL.nV6Indent ); + rSt.ReadUChar( aLVL.nLenGrpprlChpx ); + rSt.ReadUChar( aLVL.nLenGrpprlPapx ); + rSt.SeekRel( 2 ); + if( ERRCODE_NONE != rSt.GetError()) return false; + + // 2. read PAPx if needed and search for indent values + + short nTabPos = 0; // #i86652# - read tab setting + if( aLVL.nLenGrpprlPapx ) + { + sal_uInt8 aGrpprlPapx[ 255 ]; + if (aLVL.nLenGrpprlPapx != rSt.ReadBytes(&aGrpprlPapx, aLVL.nLenGrpprlPapx)) + return false; + // "sprmPDxaLeft" pap.dxaLeft;dxa;word; + SprmResult aSprm = GrpprlHasSprm(0x840F,aGrpprlPapx[0],aLVL.nLenGrpprlPapx); + if (!aSprm.pSprm) + aSprm = GrpprlHasSprm(0x845E,aGrpprlPapx[0],aLVL.nLenGrpprlPapx); + + if (aSprm.pSprm && aSprm.nRemainingData >= 2) + { + const sal_uInt8 *pBegin = aSprm.pSprm - 2; + for(int i=0;i<4;++i) + rParaSprms.push_back(*pBegin++); + short nDxaLeft = SVBT16ToUInt16(aSprm.pSprm); + aLVL.nDxaLeft = (0 < nDxaLeft) ? static_cast<sal_uInt16>(nDxaLeft) + : static_cast<sal_uInt16>(-nDxaLeft); + } + + // "sprmPDxaLeft1" pap.dxaLeft1;dxa;word; + aSprm = GrpprlHasSprm(0x8411,aGrpprlPapx[0],aLVL.nLenGrpprlPapx); + if (!aSprm.pSprm) + aSprm = GrpprlHasSprm(0x8460,aGrpprlPapx[0],aLVL.nLenGrpprlPapx); + + if (aSprm.pSprm && aSprm.nRemainingData >= 2) + { + const sal_uInt8 *pBegin = aSprm.pSprm - 2; + for(int i=0;i<4;++i) + rParaSprms.push_back(*pBegin++); + aLVL.nDxaLeft1 = SVBT16ToUInt16(aSprm.pSprm); + } + + // #i86652# - read tab setting + aSprm = GrpprlHasSprm(0xC615,aGrpprlPapx[0],aLVL.nLenGrpprlPapx); + const sal_uInt8* pSprm = aSprm.pSprm; + if (pSprm && aSprm.nRemainingData >= 5) + { + bool bDone = false; + if (*(pSprm-1) == 5) + { + if (*pSprm++ == 0) //nDel + { + if (*pSprm++ == 1) //nIns + { + nTabPos = SVBT16ToUInt16(pSprm); + pSprm+=2; + if (*pSprm == 6) //type + { + bDone = true; + } + } + } + } + OSL_ENSURE(bDone, "tab setting in numbering is " + "of unexpected configuration"); + } + if ( rNumFormat.GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + // If there is a tab setting with a larger value, then use that. + // Ideally we would allow tabs to be used in numbering fields and set + // this on the containing paragraph which would make it actually work + // most of the time. + if ( nTabPos != 0 ) + { + const sal_uInt16 nDesired = aLVL.nDxaLeft + aLVL.nDxaLeft1; + + bool bDoAdjust = false; + if ( nDesired < aLVL.nDxaLeft ) + { + if ( nDesired < nTabPos && nTabPos < aLVL.nDxaLeft ) + { + bDoAdjust = true; + } + } + else + { + if ( aLVL.nDxaLeft < nTabPos && nTabPos < nDesired ) + { + bDoAdjust = true; + } + } + + if (bDoAdjust) + { + aLVL.nDxaLeft = (0 < nTabPos) + ? static_cast<sal_uInt16>(nTabPos) + : static_cast<sal_uInt16>(-nTabPos); + + aLVL.nDxaLeft1 = nDesired - aLVL.nDxaLeft; + } + } + } + } + + // 3. read CHPx if needed + + sal_uInt16 nWitchPicIsBullet = USHRT_MAX; + bool bIsPicBullet = false; + + if( aLVL.nLenGrpprlChpx ) + { + sal_uInt8 aGrpprlChpx[ 255 ] = {}; + if (aLVL.nLenGrpprlChpx != rSt.ReadBytes(&aGrpprlChpx, aLVL.nLenGrpprlChpx)) + return false; + + //For i120928,parse the graphic info of bullets + SprmResult aSprmWhichPis = GrpprlHasSprm(NS_sprm::sprmCPbiIBullet, aGrpprlChpx[0],aLVL.nLenGrpprlChpx); + SprmResult aSprmIsPicBullet = GrpprlHasSprm(NS_sprm::sprmCPbiGrf, aGrpprlChpx[0],aLVL.nLenGrpprlChpx); + if (aSprmWhichPis.pSprm && aSprmWhichPis.nRemainingData >= 1) + { + nWitchPicIsBullet = *aSprmWhichPis.pSprm; + } + if (aSprmIsPicBullet.pSprm && aSprmIsPicBullet.nRemainingData >= 1) + { + bIsPicBullet = (*aSprmIsPicBullet.pSprm) & 0x0001; + } + + // create new Itemset for character attributes + rpItemSet.reset(new SfxItemSet( rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>{})); + + // Set Reader-ItemSet-Pointer to the newly created set + rReader.SetCurrentItemSet(std::move(rpItemSet)); + // Set Reader-Style to Style of this Level + sal_uInt16 nOldColl = rReader.GetCurrentColl(); + sal_uInt16 nNewColl = nLevelStyle; + if (ww::stiNil == nNewColl) + nNewColl = 0; + rReader.SetNCurrentColl( nNewColl ); + + // The Read_xy() methods in WW8PAR6.cxx are calling their respective + // NewAttr() or GetFormatAttr() which can determine, by using the assigned + // Reader-ItemSet-Pointer, whether this specific ItemSet is relevant + // and not a Stack or Style! + sal_uInt16 nOldFlags1 = rReader.GetToggleAttrFlags(); + sal_uInt16 nOldFlags2 = rReader.GetToggleBiDiAttrFlags(); + + WW8SprmIter aSprmIter(&aGrpprlChpx[0], aLVL.nLenGrpprlChpx, + maSprmParser); + while (const sal_uInt8* pSprm = aSprmIter.GetSprms()) + { + rReader.ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId()); + aSprmIter.advance(); + } + + // Reset Reader-ItemSet-Pointer and Reader-Style + rpItemSet = rReader.SetCurrentItemSet(nullptr); + rReader.SetNCurrentColl( nOldColl ); + rReader.SetToggleAttrFlags(nOldFlags1); + rReader.SetToggleBiDiAttrFlags(nOldFlags2); + } + + // 4. Read numbering String. Results in prefix and postfix + + OUString sNumString(sanitizeString(read_uInt16_PascalString(rSt))); + + // 5. convert read values into Writer syntax + + nType = GetSvxNumTypeFromMSONFC(aLVL.nNFC); + //For i120928,type info + if (bIsPicBullet) + { + nType = SVX_NUM_BITMAP; + } + + if (style::NumberingType::CHAR_SPECIAL == nType) + { + cBullet = !sNumString.isEmpty() ? sNumString[0] : 0x2190; + + if (!cBullet) // unsave control code? + cBullet = 0x2190; + } + else if (style::NumberingType::BITMAP == nType) //For i120928,position index info of graphic + { + cGrfBulletCP = nWitchPicIsBullet; // This is a bullet picture ID + } + + switch( aLVL.nAlign ) + { + case 0: + eAdj = SvxAdjust::Left; + break; + case 1: + eAdj = SvxAdjust::Center; + break; + case 2: + eAdj = SvxAdjust::Right; + break; + case 3: + // Writer here cannot do block justification + eAdj = SvxAdjust::Left; + break; + default: + // undefined value + OSL_ENSURE( false, "Value of aLVL.nAlign is not supported" ); + // take default + eAdj = SvxAdjust::Left; + break; + } + + // 6. Configure NumFormat + if( bSetStartNo && 0 <= aLVL.nStartAt) + rNumFormat.SetStart(static_cast<sal_uInt16>(aLVL.nStartAt)); + rNumFormat.SetNumberingType( nType ); + rNumFormat.SetNumAdjust( eAdj ); + + if( style::NumberingType::CHAR_SPECIAL == nType ) + { + // first character of the Prefix-Text is the Bullet + rNumFormat.SetBulletChar(cBullet); + // Don't forget: further below, after building styles + // Call SetBulletFont() !!! + } + //For i120928,position index info + else if (style::NumberingType::BITMAP == nType) + { + rNumFormat.SetGrfBulletCP(cGrfBulletCP); + } + else + { + // Replace symbols at aOfsNumsXCH offsets to %1, %2 as supported by DOCX and LO + OUString sListFormat = sNumString; + if (sListFormat.getLength()) + { + sal_uInt32 nExtraOffset = 0; + sal_uInt8 nLevelB = 0; + while (nLevelB < nMaxLevel && aLVL.aOfsNumsXCH[nLevelB]) + { + // Replacement symbol is read from source string from position taken from aOfsNumsXCH array + sal_uInt8 nOffset = aLVL.aOfsNumsXCH[nLevelB] + nExtraOffset - 1; + if (nOffset >= sListFormat.getLength()) + { + SAL_WARN("sw.ww8", "List level reference is beyond the border. Ignored."); + nLevelB++; + continue; + } + sal_uInt8 nReplacement = sListFormat[nOffset] + 1; + + OUString sReplacement("%" + OUString::number(nReplacement)); + sListFormat = sListFormat.replaceAt(nOffset, 1, sReplacement); + + // We need also update an offset, since we are replacing one symbol by at least two + nExtraOffset += sReplacement.getLength() - 1; + nLevelB++; + } + } + + rNumFormat.SetListFormat(sListFormat); + + // Total count of replacement holders is determining amount of required parent numbering to include + // TODO: not sure how "%" symbol is escaped. This is not supported yet + sal_Int16 nParentNum = comphelper::string::getTokenCount(sListFormat, '%'); + rNumFormat.SetIncludeUpperLevels(nParentNum); + } + + // #i89181# + if ( rNumFormat.GetPositionAndSpaceMode() == + SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + if (eAdj == SvxAdjust::Right) + { + rNumFormat.SetAbsLSpace(aLVL.nDxaLeft); + rNumFormat.SetFirstLineOffset(-aLVL.nDxaLeft); + rNumFormat.SetCharTextDistance(-aLVL.nDxaLeft1); + } + else + { + rNumFormat.SetAbsLSpace( aLVL.nDxaLeft ); + rNumFormat.SetFirstLineOffset(aLVL.nDxaLeft1); + } + } + else + { + rNumFormat.SetIndentAt( aLVL.nDxaLeft ); + rNumFormat.SetFirstLineIndent(aLVL.nDxaLeft1); + if ( !aLVL.bV6 ) + rNumFormat.SetListtabPos( nTabPos ); + else + rNumFormat.SetListtabPos( aLVL.nV6Indent ); + SvxNumberFormat::LabelFollowedBy eNumLabelFollowedBy = SvxNumberFormat::LISTTAB; + switch ( ixchFollow ) + { + case 0: + { + eNumLabelFollowedBy = SvxNumberFormat::LISTTAB; + } + break; + case 1: + { + eNumLabelFollowedBy = SvxNumberFormat::SPACE; + } + break; + case 2: + { + eNumLabelFollowedBy = SvxNumberFormat::NOTHING; + } + break; + } + rNumFormat.SetLabelFollowedBy( eNumLabelFollowedBy ); + } + + return true; +} + +void WW8ListManager::AdjustLVL( sal_uInt8 nLevel, SwNumRule& rNumRule, + WW8aISet const & rListItemSet, WW8aCFormat& rCharFormat, bool& bNewCharFormatCreated, + const OUString& sPrefix ) +{ + bNewCharFormatCreated = false; + sal_uInt8 nIdenticalItemSetLevel; + const SfxPoolItem* pItem; + + SwNumFormat aNumFormat = rNumRule.Get( nLevel ); + + SfxItemSet* pThisLevelItemSet = rListItemSet[nLevel].get(); + + if( pThisLevelItemSet && pThisLevelItemSet->Count()) + { + nIdenticalItemSetLevel = nMaxLevel; + SfxItemIter aIter( *pThisLevelItemSet ); + for (sal_uInt8 nLowerLevel = 0; nLowerLevel < nLevel; ++nLowerLevel) + { + SfxItemSet* pLowerLevelItemSet = rListItemSet[nLowerLevel].get(); + if( pLowerLevelItemSet + && (pLowerLevelItemSet->Count() == pThisLevelItemSet->Count()) ) + { + nIdenticalItemSetLevel = nLowerLevel; + const SfxPoolItem* pItemIter = aIter.GetCurItem(); + do + { + if( // search for appropriate pItem in pLowerLevelItemSet + (SfxItemState::SET != pLowerLevelItemSet->GetItemState( + pItemIter->Which(), false, &pItem ) ) + || // use virtual "!=" Operator + (*pItem != *pItemIter) ) + // if no Item with equal nWhich was found or Item value was not equal + // store inequality and break! + { + nIdenticalItemSetLevel = nMaxLevel; + break; + } + pItemIter = aIter.NextItem(); + } while (pItemIter); + + if( nIdenticalItemSetLevel != nMaxLevel ) + break; + } + } + + SwCharFormat* pFormat; + if (nMaxLevel == nIdenticalItemSetLevel) + { + // Define Style + const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName()) + + "z" + OUString::number( nLevel ) ); + + // remove const by casting + pFormat = rDoc.MakeCharFormat(aName, rDoc.GetDfltCharFormat()); + bNewCharFormatCreated = true; + // Set Attributes + pFormat->SetFormatAttr( *pThisLevelItemSet ); + } + else + { + // append Style + pFormat = rCharFormat[ nIdenticalItemSetLevel ]; + } + + // store + rCharFormat[ nLevel ] = pFormat; + + // Append Style to NumFormat + + aNumFormat.SetCharFormat( pFormat ); + } + //Ensure the default char fmt is initialized for any level of num ruler if no customized attr + else + { + SwCharFormat* pFormat = aNumFormat.GetCharFormat(); + if ( !pFormat) + { + const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName()) + + "z" + OUString::number( nLevel ) ); + + pFormat = rDoc.MakeCharFormat(aName, rDoc.GetDfltCharFormat()); + bNewCharFormatCreated = true; + rCharFormat[ nLevel ] = pFormat; + aNumFormat.SetCharFormat( pFormat ); + } + } + + // if necessary: Append Bullet Font to NumFormat + + if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() ) + { + SwCharFormat* pFormat = aNumFormat.GetCharFormat(); + vcl::Font aFont; + if( !pFormat ) + { + aFont = numfunc::GetDefBulletFont(); + } + else + { + const SvxFontItem& rFontItem = pFormat->GetFont(); + aFont.SetFamily( rFontItem.GetFamily() ); + aFont.SetFamilyName( rFontItem.GetFamilyName() ); + aFont.SetStyleName( rFontItem.GetStyleName() ); + aFont.SetPitch( rFontItem.GetPitch() ); + aFont.SetCharSet( rFontItem.GetCharSet() ); + } + aNumFormat.SetBulletFont( &aFont ); + } + + // Set NumFormat in NumRule + + rNumRule.Set(nLevel, aNumFormat); +} + +SwNumRule* WW8ListManager::CreateNextRule(bool bSimple) +{ + // Used to build the Style Name + const OUString sPrefix("WW8Num" + OUString::number(nUniqueList++)); + // #i86652# + sal_uInt16 nRul = + rDoc.MakeNumRule( rDoc.GetUniqueNumRuleName(&sPrefix), nullptr, false, + SvxNumberFormat::LABEL_ALIGNMENT ); + SwNumRule* pMyNumRule = rDoc.GetNumRuleTable()[nRul]; + pMyNumRule->SetAutoRule(false); + pMyNumRule->SetContinusNum(bSimple); + return pMyNumRule; +} + +SwNumRule* WW8ListManager::GetNumRule(size_t i) +{ + if (i < maLSTInfos.size()) + return maLSTInfos[i]->pNumRule; + else + return nullptr; +} + +// public methods + +WW8ListManager::WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_) + : maSprmParser(rReader_.GetFib()), rReader(rReader_) + , rDoc(rReader.GetDoc()) + , rFib(rReader.GetFib()), rSt(rSt_) + , nUniqueList(1) + , nLastLFOPosition(USHRT_MAX) +{ + + // LST and LFO only since WW8 + if( ( 8 > rFib.m_nVersion ) + || ( rFib.m_fcPlcfLst == rFib.m_fcPlfLfo ) + || ( rFib.m_lcbPlcfLst < 2 ) + || ( rFib.m_lcbPlfLfo < 2) ) return; // no public lists + + // create Arrays + bool bLVLOk = true; + + long nOriginalPos = rSt.Tell(); + + // 1. read PLCF LST and create list templates in Writer + + bool bOk = checkSeek(rSt, rFib.m_fcPlcfLst); + + if (!bOk) + return; + + sal_uInt32 nRemainingPlcfLst = rFib.m_lcbPlcfLst; + + sal_uInt16 nListCount(0); + rSt.ReadUInt16( nListCount ); + nRemainingPlcfLst -= 2; + bOk = nListCount > 0; + + if (!bOk) + return; + + // 1.1 read all LST + const size_t nMinRecordSize = 10 + 2*nMaxLevel; + const size_t nMaxRecords = rSt.remainingSize() / nMinRecordSize; + if (nListCount > nMaxRecords) + { + SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nListCount << " claimed, truncating"); + nListCount = nMaxRecords; + } + for (sal_uInt16 nList=0; nList < nListCount; ++nList) + { + if (nRemainingPlcfLst < cbLSTF) + break; + + WW8LST aLST = {}; + + // 1.1.1 read Data + + rSt.ReadUInt32( aLST.nIdLst ); + rSt.ReadUInt32( aLST.nTplC ); + for (sal_uInt16 & nLevel : aLST.aIdSty) + rSt.ReadUInt16( nLevel ); + + sal_uInt8 aBits1(0); + rSt.ReadUChar( aBits1 ); + + rSt.SeekRel( 1 ); + + if( aBits1 & 0x01 ) + aLST.bSimpleList = true; + if( aBits1 & 0x02 ) + aLST.bRestartHdn = true; + + // 1.1.2 new NumRule inserted in Doc and WW8LSTInfo marked + + /* + #i1869# + In word 2000 microsoft got rid of creating new "simple lists" with + only 1 level, all new lists are created with 9 levels. To hack it + so that the list types formerly known as simple lists still have + their own tab page to themselves one of the reserved bits is used + to show that a given list is to be in the simple list tabpage. + This has now nothing to do with the actual number of list level a + list has, only how many will be shown in the user interface. + + i.e. create a simple list in 2000 and open it in 97 and 97 will + claim (correctly) that it is an outline list. We can set our + continuous flag in these lists to store this information. + */ + SwNumRule* pMyNumRule = CreateNextRule( + aLST.bSimpleList || (aBits1 & 0x10)); + + WW8LSTInfo* pLSTInfo = new WW8LSTInfo(pMyNumRule, aLST); + maLSTInfos.emplace_back(pLSTInfo); + + nRemainingPlcfLst -= cbLSTF; + } + + // 1.2 read all LVL of all aLST + + sal_uInt16 nLSTInfos = static_cast< sal_uInt16 >(maLSTInfos.size()); + for (sal_uInt16 nList = 0; nList < nLSTInfos; ++nList) + { + WW8aISet aItemSet; // Character attributes from GrpprlChpx + + WW8LSTInfo* pListInfo = maLSTInfos[nList].get(); + if( !pListInfo || !pListInfo->pNumRule ) break; + SwNumRule& rMyNumRule = *pListInfo->pNumRule; + + // 1.2.1 read specific LVL(s) for this aLST + + sal_uInt16 nLvlCount = static_cast< sal_uInt16 >(pListInfo->bSimpleList ? nMinLevel : nMaxLevel); + pListInfo->maParaSprms.resize(nMaxLevel); + for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel) + { + SwNumFormat aNumFormat( rMyNumRule.Get( nLevel ) ); + // read LVLF + bLVLOk = ReadLVL( aNumFormat, aItemSet[nLevel], + pListInfo->aIdSty[nLevel], true, nLevel, + pListInfo->maParaSprms[nLevel]); + if( !bLVLOk ) + break; + // and set in rMyNumRule + rMyNumRule.Set( nLevel, aNumFormat ); + } + if( !bLVLOk ) + break; + + // 1.2.2 compare ItemPools and CHPx Settings of different Levels + // and create Style(s) if necessary + + for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel) + { + bool bDummy; + AdjustLVL( nLevel, rMyNumRule, aItemSet, + pListInfo->aCharFormat, bDummy ); + } + } + + // 2. read and save PLF LFO + + bOk = checkSeek(rSt, rFib.m_fcPlfLfo); + + if (!bOk) + return; + + sal_Int32 nLfoCount(0); + rSt.ReadInt32( nLfoCount ); + bOk = nLfoCount > 0; + + if (!bOk) + return; + + // 2.1 read all LFO + + for (sal_Int32 nLfo = 0; nLfo < nLfoCount; ++nLfo) + { + bOk = false; + + WW8LFO aLFO = {}; + + rSt.ReadUInt32( aLFO.nIdLst ); + rSt.SeekRel( 8 ); + rSt.ReadUChar( aLFO.nLfoLvl ); + if (!rSt.good()) + break; + rSt.SeekRel( 3 ); + // as many Overrides as there are + if ((nMaxLevel < aLFO.nLfoLvl) || rSt.GetError()) + break; + + // get the Parent NumRule of the current List + WW8LSTInfo* pParentListInfo = GetLSTByListId(aLFO.nIdLst); + if (pParentListInfo) + { + // Save the NumRule in this first step + aLFO.pNumRule = pParentListInfo->pNumRule; + + // are there multiple Levels in the List? + aLFO.bSimpleList = pParentListInfo->bSimpleList; + } + // store in Array + std::unique_ptr<WW8LFOInfo> pLFOInfo(new WW8LFOInfo(aLFO)); + if (pParentListInfo) + { + //Copy the basic paragraph properties for each level from the + //original list into the list format override levels. + int nMaxSize = pParentListInfo->maParaSprms.size(); + pLFOInfo->maParaSprms.resize(nMaxSize); + for (int i = 0; i < nMaxSize; ++i) + pLFOInfo->maParaSprms[i] = pParentListInfo->maParaSprms[i]; + } + m_LFOInfos.push_back(std::move(pLFOInfo)); + bOk = true; + } + + if( bOk ) + { + + // 2.2 read specific LFOLVL for all LFO + + size_t nLFOInfos = m_LFOInfos.size(); + for (size_t nLfo = 0; nLfo < nLFOInfos; ++nLfo) + { + WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLfo]; + // Do LFOLVL exist? + if( rLFOInfo.bOverride ) + { + WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst); + if (!pParentListInfo) + break; + + // 2.2.1 create new NumRule for this List + + SwNumRule* pParentNumRule = rLFOInfo.pNumRule; + OSL_ENSURE(pParentNumRule, "ww: Impossible lists, please report"); + if( !pParentNumRule ) + break; + // create name-prefix for NumRule-Name + // and (if necessary) for Style-Name + const OUString sPrefix("WW8NumSt" + OUString::number( nLfo + 1 )); + // Now assign pNumRule its actual value!!! + // (it contained the parent NumRule up to this point) + + // check if a Style is referencing this LFO + if( USHRT_MAX > rReader.StyleUsingLFO( nLfo ) ) + { + sal_uInt16 nRul = rDoc.MakeNumRule( + rDoc.GetUniqueNumRuleName( &sPrefix ), pParentNumRule); + rLFOInfo.pNumRule = rDoc.GetNumRuleTable()[ nRul ]; + rLFOInfo.pNumRule->SetAutoRule(false); + } + else + { + sal_uInt16 nRul = rDoc.MakeNumRule( + rDoc.GetUniqueNumRuleName(), pParentNumRule); + rLFOInfo.pNumRule = rDoc.GetNumRuleTable()[ nRul ]; + rLFOInfo.pNumRule->SetAutoRule(true); // = default + } + + // 2.2.2 read all LFOLVL (and LVL) for the new NumRule + + WW8aISet aItemSet; // Character attributes from GrpprlChpx + WW8aCFormat aCharFormat = {}; // Character Style Pointer + + //2.2.2.0 skip inter-group of override header ? + //See #i25438# for why I moved this here, compare + //that original bugdoc's binary to what it looks like + //when resaved with word, i.e. there is always a + //4 byte header, there might be more than one if + //that header was 0xFFFFFFFF, e.g. #114412# ? + sal_uInt32 nTest; + rSt.ReadUInt32( nTest ); + do + { + nTest = 0; + rSt.ReadUInt32( nTest ); + } + while (nTest == 0xFFFFFFFF); + rSt.SeekRel(-4); + + for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel) + { + WW8LFOLVL aLFOLVL; + bLVLOk = false; + + // 2.2.2.1 read LFOLVL + + rSt.ReadInt32( aLFOLVL.nStartAt ); + sal_uInt8 aBits1(0); + rSt.ReadUChar( aBits1 ); + rSt.SeekRel( 3 ); + if (rSt.GetError()) + break; + + // Note: MS writes the Override-Level-Number into 4 bit. + // We do not! (See comment at "struct WW8LFOInfo") + aLFOLVL.nLevel = aBits1 & 0x0F; + if( (0xFF > aBits1) && + (nMaxLevel > aLFOLVL.nLevel) ) + { + if (aBits1 & 0x10) + aLFOLVL.bStartAt = true; + else + aLFOLVL.bStartAt = false; + + // 2.2.2.2 load dedicated LVL if necessary + + SwNumFormat aNumFormat( + rLFOInfo.pNumRule->Get(aLFOLVL.nLevel)); + if (aBits1 & 0x20) + { + aLFOLVL.bFormat = true; + // if bStartup is true, replace Startup-Level + // with the LVLF that is saved in the LVL + bLVLOk = nLevel < rLFOInfo.maParaSprms.size() && + ReadLVL(aNumFormat, aItemSet[nLevel], + pParentListInfo->aIdSty[nLevel], + aLFOLVL.bStartAt, nLevel, + rLFOInfo.maParaSprms[nLevel]); + + if (!bLVLOk) + break; + } + else if (aLFOLVL.bStartAt) + { + aNumFormat.SetStart( + writer_cast<sal_uInt16>(aLFOLVL.nStartAt)); + } + + // 2.2.2.3 Set NumFormat in NumRule + + rLFOInfo.pNumRule->Set(aLFOLVL.nLevel, aNumFormat); + } + bLVLOk = true; + + if (nMaxLevel > aLFOLVL.nLevel) + rLFOInfo.maOverrides[aLFOLVL.nLevel] = aLFOLVL; + } + if( !bLVLOk ) + break; + + // 2.2.3 adjust LVL of the new NumRule + + bool bNewCharFormatCreated = false; + for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel) + { + AdjustLVL( nLevel, *rLFOInfo.pNumRule, aItemSet, aCharFormat, + bNewCharFormatCreated, sPrefix ); + } + } + } + } + // and we're done! + rSt.Seek( nOriginalPos ); +} + +WW8ListManager::~WW8ListManager() COVERITY_NOEXCEPT_FALSE +{ + /* + named lists remain in document + unused automatic lists are removed from document (DelNumRule) + */ + for(auto & rpInfo : maLSTInfos) + { + if (rpInfo->pNumRule && !rpInfo->bUsedInDoc && + rpInfo->pNumRule->IsAutoRule()) + { + rDoc.DelNumRule(rpInfo->pNumRule->GetName()); + } + rpInfo.reset(); + } + for (auto aIter = m_LFOInfos.rbegin(); aIter != m_LFOInfos.rend(); ++aIter) + { + if ((*aIter)->bOverride + && (*aIter)->pNumRule + && !(*aIter)->bUsedInDoc + && (*aIter)->pNumRule->IsAutoRule()) + { + rDoc.DelNumRule( (*aIter)->pNumRule->GetName() ); + } + } +} + +static bool IsEqualFormatting(const SwNumRule &rOne, const SwNumRule &rTwo) +{ + bool bRet = + ( + rOne.GetRuleType() == rTwo.GetRuleType() && + rOne.IsContinusNum() == rTwo.IsContinusNum() && + rOne.IsAbsSpaces() == rTwo.IsAbsSpaces() && + rOne.GetPoolFormatId() == rTwo.GetPoolFormatId() && + rOne.GetPoolHelpId() == rTwo.GetPoolHelpId() && + rOne.GetPoolHlpFileId() == rTwo.GetPoolHlpFileId() + ); + + if (bRet) + { + for (sal_uInt8 n = 0; n < MAXLEVEL; ++n ) + { + //The SvxNumberFormat compare, not the SwNumFormat compare + const SvxNumberFormat &rO = rOne.Get(n); + const SvxNumberFormat &rT = rTwo.Get(n); + if (rO != rT) + { + bRet = false; + break; + } + } + } + return bRet; +} + +SwNumRule* WW8ListManager::GetNumRuleForActivation(sal_uInt16 nLFOPosition, + const sal_uInt8 nLevel, std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode) +{ + if (m_LFOInfos.size() <= nLFOPosition) + return nullptr; + + WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLFOPosition]; + + bool bFirstUse = !rLFOInfo.bUsedInDoc; + rLFOInfo.bUsedInDoc = true; + + if( !rLFOInfo.pNumRule ) + return nullptr; + + // #i25545# + // #i100132# - a number format does not have to exist on given list level + SwNumFormat aFormat(rLFOInfo.pNumRule->Get(nLevel)); + + if (rReader.IsRightToLeft() && nLastLFOPosition != nLFOPosition) { + if ( aFormat.GetNumAdjust() == SvxAdjust::Right) + aFormat.SetNumAdjust(SvxAdjust::Left); + else if ( aFormat.GetNumAdjust() == SvxAdjust::Left) + aFormat.SetNumAdjust(SvxAdjust::Right); + rLFOInfo.pNumRule->Set(nLevel, aFormat); + } + nLastLFOPosition = nLFOPosition; + /* + #i1869# + If this list has had its bits set in word 2000 to pretend that it is a + simple list from the point of view of the user, then it is almost + certainly a simple continuous list, and we will try to keep it like that. + Otherwise when we save again it will be shown as the true outline list + that it is, confusing the user that just wanted what they thought was a + simple list. On the other hand it is possible that some of the other levels + were used by the user, in which case we will not pretend anymore that it + is a simple list. Something that word 2000 does anyway, that 97 didn't, to + my bewilderment. + */ + if (nLevel && rLFOInfo.pNumRule->IsContinusNum()) + rLFOInfo.pNumRule->SetContinusNum(false); + + if( (!rLFOInfo.bOverride) && (!rLFOInfo.bLSTbUIDSet) ) + { + WW8LSTInfo* pParentListInfo = GetLSTByListId( rLFOInfo.nIdLst ); + if( pParentListInfo ) + pParentListInfo->bUsedInDoc = true; + rLFOInfo.bLSTbUIDSet = true; + } + + if (rLFOInfo.maParaSprms.size() > nLevel) + rParaSprms = rLFOInfo.maParaSprms[nLevel]; + + SwNumRule *pRet = rLFOInfo.pNumRule; + + bool bRestart(false); + sal_uInt16 nStart(0); + bool bNewstart(false); + /* + Note: If you fiddle with this then you have to make sure that #i18322# + #i13833#, #i20095# and #112466# continue to work + + Check if there were overrides for this level + */ + if (rLFOInfo.bOverride && nLevel < rLFOInfo.nLfoLvl) + { + WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst); + OSL_ENSURE(pParentListInfo, "ww: Impossible lists, please report"); + if (pParentListInfo && pParentListInfo->pNumRule) + { + const WW8LFOLVL &rOverride = rLFOInfo.maOverrides[nLevel]; + bool bNoChangeFromParent = + IsEqualFormatting(*pRet, *(pParentListInfo->pNumRule)); + + //If so then I think word still uses the parent (maybe) + if (bNoChangeFromParent) + { + pRet = pParentListInfo->pNumRule; + + //did it not affect start at value ? + if (bFirstUse && rOverride.bStartAt) + { + const SwNumFormat &rFormat = + pParentListInfo->pNumRule->Get(nLevel); + if ( + rFormat.GetStart() == + rLFOInfo.maOverrides[nLevel].nStartAt + ) + { + bRestart = true; + } + else + { + bNewstart = true; + nStart = writer_cast<sal_uInt16> + (rLFOInfo.maOverrides[nLevel].nStartAt); + } + } + + pParentListInfo->bUsedInDoc = true; + } + } + } + + if (pNode) + { + pNode->SetAttrListLevel(nLevel); + + if (bRestart || bNewstart) + pNode->SetListRestart(true); + if (bNewstart) + pNode->SetAttrListRestartValue(nStart); + } + return pRet; +} + +// SwWW8ImplReader: append a List to a Style or Paragraph + +bool SwWW8ImplReader::SetTextFormatCollAndListLevel(const SwPaM& rRg, + SwWW8StyInf& rStyleInfo) +{ + bool bRes = true; + if( rStyleInfo.m_pFormat && rStyleInfo.m_bColl ) + { + bRes = m_rDoc.SetTextFormatColl(rRg, static_cast<SwTextFormatColl*>(rStyleInfo.m_pFormat)); + SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode(); + OSL_ENSURE( pTextNode, "No Text-Node at PaM-Position" ); + if ( !pTextNode ) + { + // make code robust + return bRes; + } + + const SwNumRule * pNumRule = pTextNode->GetNumRule(); // #i27610# + + if( !IsInvalidOrToBeMergedTabCell() && + ! (pNumRule && pNumRule->IsOutlineRule()) ) // #i27610# + { + pTextNode->ResetAttr( RES_PARATR_NUMRULE ); + } + + if (USHRT_MAX > rStyleInfo.m_nLFOIndex && WW8ListManager::nMaxLevel + > rStyleInfo.m_nListLevel) + { + const bool bApplyListStyle = false; + RegisterNumFormatOnTextNode(rStyleInfo.m_nLFOIndex, rStyleInfo.m_nListLevel, + bApplyListStyle); + } + } + return bRes; +} + +void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat) +{ + // #i86652# + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + const auto nAbsLSpace = rFormat.GetAbsLSpace(); + const long nListFirstLineIndent = GetListFirstLineIndent(rFormat); + SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(*rStyle.m_pFormat, RES_LR_SPACE)); + aLR.SetTextLeft(nAbsLSpace); + aLR.SetTextFirstLineOffset(writer_cast<short>(nListFirstLineIndent)); + rStyle.m_pFormat->SetFormatAttr(aLR); + rStyle.m_bListReleventIndentSet = true; + } +} + +void SetStyleIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat) +{ + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) // #i86652# + { + SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(*rStyle.m_pFormat, RES_LR_SPACE)); + if (rStyle.m_bListReleventIndentSet) + { + + SyncIndentWithList( aLR, rFormat, false, false ); // #i103711#, #i105414# + } + else + { + aLR.SetTextLeft(0); + aLR.SetTextFirstLineOffset(0); + } + rStyle.m_pFormat->SetFormatAttr(aLR); + } +} + +void SwWW8ImplReader::SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO, + sal_uInt8 nCurrentLevel) +{ + if (nStyle >= m_vColl.size()) + return; + + SwWW8StyInf &rStyleInf = m_vColl[nStyle]; + if (rStyleInf.m_bValid) + { + OSL_ENSURE(m_pCurrentColl, "Cannot be called outside of style import"); + // Phase 1: Numbering attributes when reading a StyleDef + if( m_pCurrentColl ) + { + // only save the Parameters for now. The actual List will be appended + // at a later point, when the Listdefinitions is read... + if ( + (USHRT_MAX > nCurrentLFO) && + (WW8ListManager::nMaxLevel > nCurrentLevel) + ) + { + rStyleInf.m_nLFOIndex = nCurrentLFO; + rStyleInf.m_nListLevel = nCurrentLevel; + + std::vector<sal_uInt8> aParaSprms; + SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation( + nCurrentLFO, nCurrentLevel, aParaSprms); + if (pNmRule) + UseListIndent(rStyleInf, pNmRule->Get(nCurrentLevel)); + } + } + } +} + +void SwWW8ImplReader::RegisterNumFormatOnStyle(sal_uInt16 nStyle) +{ + + if (nStyle >= m_vColl.size()) + return; + + SwWW8StyInf &rStyleInf = m_vColl[nStyle]; + if (rStyleInf.m_bValid && rStyleInf.m_pFormat) + { + //Save old pre-list modified indent, which are the word indent values + rStyleInf.maWordLR.reset(ItemGet<SvxLRSpaceItem>(*rStyleInf.m_pFormat, RES_LR_SPACE).Clone()); + + // Phase 2: refresh StyleDef after reading all Lists + SwNumRule* pNmRule = nullptr; + const sal_uInt16 nLFO = rStyleInf.m_nLFOIndex; + const sal_uInt8 nLevel = rStyleInf.m_nListLevel; + if ( + (USHRT_MAX > nLFO) && + (WW8ListManager::nMaxLevel > nLevel) + ) + { + std::vector<sal_uInt8> aParaSprms; + pNmRule = m_xLstManager->GetNumRuleForActivation(nLFO, nLevel, + aParaSprms); + + if (pNmRule != nullptr) + { + if (rStyleInf.IsWW8BuiltInHeadingStyle() + && rStyleInf.HasWW8OutlineLevel()) + { + rStyleInf.m_pOutlineNumrule = pNmRule; + } + else + { + rStyleInf.m_pFormat->SetFormatAttr( + SwNumRuleItem(pNmRule->GetName())); + rStyleInf.m_bHasStyNumRule = true; + } + } + } + + if (pNmRule) + SetStyleIndent(rStyleInf, pNmRule->Get(nLevel)); + } +} + +void SwWW8ImplReader::RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO, + sal_uInt8 nCurrentLevel, + const bool bSetAttr) +{ + // Note: the method appends NumRule to the Text Node if + // bSetAttr (of course the lists have to be read before) + // and only sets the Level. It does not check if there is a NumRule + // attached to the STYLE !!! + + if (m_xLstManager) // are all list declarations read? + { + SwTextNode* pTextNd = m_pPaM->GetNode().GetTextNode(); + OSL_ENSURE(pTextNd, "No Text-Node at PaM-Position"); + if (!pTextNd) + return; + + std::vector<sal_uInt8> aParaSprms; + const SwNumRule* pRule = bSetAttr ? + m_xLstManager->GetNumRuleForActivation( nCurrentLFO, nCurrentLevel, + aParaSprms, pTextNd) : nullptr; + + if (pRule != nullptr || !bSetAttr) + { + if (bSetAttr && pTextNd->GetNumRule() != pRule + && pTextNd->GetNumRule() != m_rDoc.GetOutlineNumRule()) + { + pTextNd->SetAttr(SwNumRuleItem(pRule->GetName())); + } + pTextNd->SetAttrListLevel(nCurrentLevel); + + // <IsCounted()> state of text node has to be adjusted accordingly. + if ( /*nCurrentLevel >= 0 &&*/ nCurrentLevel < MAXLEVEL ) + { + pTextNd->SetCountedInList( true ); + } + + // #i99822# + // Direct application of the list level formatting no longer + // needed for list levels of mode LABEL_ALIGNMENT + bool bApplyListLevelIndentDirectlyAtPara(true); + { + if (pTextNd->GetNumRule() && nCurrentLevel < MAXLEVEL) + { + const SwNumFormat& rFormat = pTextNd->GetNumRule()->Get(nCurrentLevel); + if (rFormat.GetPositionAndSpaceMode() + == SvxNumberFormat::LABEL_ALIGNMENT) + { + bApplyListLevelIndentDirectlyAtPara = false; + } + } + } + + if (bApplyListLevelIndentDirectlyAtPara) + { + std::unique_ptr<SfxItemSet> xListIndent(new SfxItemSet(m_rDoc.GetAttrPool(), svl::Items<RES_LR_SPACE, + RES_LR_SPACE>{})); + const SvxLRSpaceItem *pItem = static_cast<const SvxLRSpaceItem*>( + GetFormatAttr(RES_LR_SPACE)); + OSL_ENSURE(pItem, "impossible"); + if (pItem) + xListIndent->Put(*pItem); + + /* + Take the original paragraph sprms attached to this list level + formatting and apply them to the paragraph. I'm convinced that + this is exactly what word does. + */ + if (short nLen = static_cast< short >(aParaSprms.size())) + { + std::unique_ptr<SfxItemSet> xOldCurrentItemSet(SetCurrentItemSet(std::move(xListIndent))); + + sal_uInt8* pSprms1 = aParaSprms.data(); + while (0 < nLen) + { + sal_uInt16 nL1 = ImportSprm(pSprms1, nLen); + nLen = nLen - nL1; + pSprms1 += nL1; + } + + xListIndent = SetCurrentItemSet(std::move(xOldCurrentItemSet)); + } + + if (const SvxLRSpaceItem *pLR = xListIndent->GetItem<SvxLRSpaceItem>(RES_LR_SPACE)) + { + m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pLR); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_LR_SPACE); + } + } + } + } +} + +void SwWW8ImplReader::RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel) +{ + // Are we reading the StyleDef ? + if (m_pCurrentColl) + SetStylesList( m_nCurrentColl , nCurrentLFO, nCurrentLevel); + else + RegisterNumFormatOnTextNode(nCurrentLFO, nCurrentLevel); +} + +void SwWW8ImplReader::Read_ListLevel(sal_uInt16, const sal_uInt8* pData, + short nLen) +{ + if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox()) + return; + + if( nLen < 0 ) + { + // the current level is finished, what should we do ? + m_nListLevel = WW8ListManager::nMaxLevel; + if (m_xStyles && !m_bVer67) + m_xStyles->mnWwNumLevel = 0; + } + else + { + // security check + if( !pData ) + return; + + // the Streamdata is zero based + m_nListLevel = *pData; + + if (m_xStyles && !m_bVer67) + { + /* + if this is the case, then if the numbering is actually stored in + winword 6 format, and its likely that sprmPIlvl has been abused + to set the ww6 list level information which we will need when we + reach the true ww6 list def. So set it now + */ + m_xStyles->mnWwNumLevel = m_nListLevel; + } + + if (WW8ListManager::nMaxLevel <= m_nListLevel ) + m_nListLevel = WW8ListManager::nMaxLevel; + else if (USHRT_MAX > m_nLFOPosition) + { + RegisterNumFormat(m_nLFOPosition, m_nListLevel); + m_nLFOPosition = USHRT_MAX; + m_nListLevel = WW8ListManager::nMaxLevel; + } + } +} + +void SwWW8ImplReader::Read_LFOPosition(sal_uInt16, const sal_uInt8* pData, + short nLen) +{ + if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox()) + return; + + if( nLen < 0 ) + { + // the current level is finished, what should we do ? + m_nLFOPosition = USHRT_MAX; + m_nListLevel = WW8ListManager::nMaxLevel; + } + else + { + // security check + if( !pData ) + return; + + short nData = SVBT16ToUInt16( pData ); + if( 0 >= nData ) + { + // disable the numbering/list style apply to the paragraph or the style + + /* + If you have a paragraph in word with left and/or hanging indent + and remove its numbering, then the indentation appears to get + reset, but not back to the base style, instead it goes to a blank + setting. + Unless it's a broken ww6 list in 97 in which case more hackery is + required, some more details about broken ww6 list in + ww8par6.cxx#SwWW8ImplReader::Read_LR + */ + + if (m_pCurrentColl) + { + // here a "named" style is being configured + + // disable the numbering/list in the style currently configured + m_pCurrentColl->SetFormatAttr(*GetDfltAttr(RES_PARATR_NUMRULE)); + + // reset/blank the indent + m_pCurrentColl->SetFormatAttr(SvxLRSpaceItem(RES_LR_SPACE)); + } + else if (SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode()) + { + // here a paragraph is being directly formatted + + // empty the numbering/list style applied to the current paragraph + SwNumRuleItem aEmptyRule; + pTextNode->SetAttr( aEmptyRule ); + + // create an empty SvxLRSpaceItem + std::shared_ptr<SvxLRSpaceItem> aLR(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE)); + + // replace it with the one of the current node if it exist + const SfxPoolItem* pLR = GetFormatAttr(RES_LR_SPACE); + if( pLR ) + aLR.reset(static_cast<SvxLRSpaceItem*>(pLR->Clone())); + + // reset/blank the left indent (and only the left) + aLR->SetTextLeft(0); + aLR->SetTextFirstLineOffset(0); + + // apply the modified SvxLRSpaceItem to the current paragraph + pTextNode->SetAttr( *aLR ); + } + + m_nLFOPosition = USHRT_MAX; + } + else // nData in (0..0x7FFF] + { + m_nLFOPosition = static_cast<sal_uInt16>(nData)-1; // m_nLFOPosition in [0..0x7FFF) + /* + If we are a ww8+ style with ww7- style lists then there is a + bizarre broken word bug where when the list is removed from a para + the ww6 list first line indent still affects the first line + indentation. Setting this flag will allow us to recover from this + braindeadness + */ + if (m_pCurrentColl && (m_nLFOPosition == 2047-1) && m_nCurrentColl < m_vColl.size()) + m_vColl[m_nCurrentColl].m_bHasBrokenWW6List = true; + + // here the stream data is 1-based, we subtract ONE + if (m_nLFOPosition != 2047-1) //Normal ww8+ list behaviour + { + if (WW8ListManager::nMaxLevel == m_nListLevel) + m_nListLevel = 0; + if (WW8ListManager::nMaxLevel > m_nListLevel) + { + RegisterNumFormat(m_nLFOPosition, m_nListLevel); + m_nLFOPosition = USHRT_MAX; + m_nListLevel = WW8ListManager::nMaxLevel; + } + } + else if (m_xPlcxMan && m_xPlcxMan->HasParaSprm(NS_sprm::LN_PAnld).pSprm) + { + /* + #i8114# Horrific backwards compatible ww7- lists in ww8+ + docs + */ + Read_ANLevelNo(13 /*equiv ww7- sprm no*/, &m_nListLevel, 1); + } + } + } +} + +// Reading Controls + +bool SwWW8ImplReader::ImportFormulaControl(WW8FormulaControl &aFormula, + WW8_CP nStart, SwWw8ControlType nWhich ) +{ + bool bRet=false; + /* + * Save the reader state and process the sprms for this anchor cp. + * Doing so will set the nPicLocFc to the offset to find the hypertext + * data in the data stream. + */ + WW8_CP nEndCp = nStart+1; //Only interested in the single 0x01 character + + WW8ReaderSave aSave(this,nStart); + + WW8PLCFManResult aRes; + nStart = m_xPlcxMan->Where(); + while(nStart <= nEndCp) + { + if ( m_xPlcxMan->Get(&aRes) + && aRes.pMemPos && aRes.nSprmId ) + { + //only interested in sprms which would set nPicLocFc + if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) ) + { + Read_PicLoc( aRes.nSprmId, aRes.pMemPos + + m_xSprmParser->DistanceToData(aRes.nSprmId), 4); + break; + } + } + m_xPlcxMan->advance(); + nStart = m_xPlcxMan->Where(); + } + sal_uLong nOffset = m_nPicLocFc; + aSave.Restore(this); + + sal_uLong nOldPos = m_pDataStream->Tell(); + WW8_PIC aPic; + m_pDataStream->Seek( nOffset); + PicRead( m_pDataStream, &aPic, m_bVer67); + + if((aPic.lcb > 0x3A) && !m_pDataStream->GetError() ) + { + aFormula.FormulaRead(nWhich,m_pDataStream); + bRet = true; + } + + /* + There is a problem with aPic, the WW8_PIC is always used even though it + is too big for the WW95 files, it needs to be modified to check the + version C. + */ + m_pDataStream->Seek( nOldPos ); + return bRet; +} + +void SwMSConvertControls::InsertFormula(WW8FormulaControl &rFormula) +{ + const uno::Reference< lang::XMultiServiceFactory > & rServiceFactory = + GetServiceFactory(); + + if(!rServiceFactory.is()) + return; + + awt::Size aSz; + uno::Reference< form::XFormComponent> xFComp; + + if (rFormula.Import(rServiceFactory, xFComp, aSz)) + { + uno::Reference <drawing::XShape> xShapeRef; + if (InsertControl(xFComp, aSz, &xShapeRef, false)) + GetShapes()->add(xShapeRef); + } +} + +void WW8FormulaControl::FormulaRead(SwWw8ControlType nWhich, + SvStream *pDataStream) +{ + sal_uInt8 nField; + + // The following is a FFData structure as described in + // Microsoft's DOC specification (chapter 2.9.78) + sal_uInt32 nVersion = 0; + pDataStream->ReadUInt32(nVersion); + // An unsigned integer that MUST be 0xFFFFFFFF + if (nVersion != 0xFFFFFFFF) + { + SAL_WARN("sw.ww8", "Parsing error: invalid header for FFData"); + return; // bail out + } + + // might be better to read the bits as a 16 bit word + // ( like it is in the spec. ) + sal_uInt8 bits1 = 0; + pDataStream->ReadUChar( bits1 ); + sal_uInt8 bits2 = 0; + pDataStream->ReadUChar( bits2 ); + + sal_uInt8 iType = ( bits1 & 0x3 ); + + // we should verify that bits.iType & nWhich concur + OSL_ENSURE( iType == nWhich, "something wrong, expect control type read from stream doesn't match nWhich passed in"); + if ( iType != nWhich ) + return; // bail out + + sal_uInt8 iRes = (bits1 & 0x7C) >> 2; + + pDataStream->ReadUInt16( mnMaxLen ); + + sal_uInt16 hps = 0; + pDataStream->ReadUInt16( hps ); + + // xstzName + msTitle = read_uInt16_BeltAndBracesString(*pDataStream); + + if (nWhich == WW8_CT_EDIT) + { // Field is a textbox + // Default text + // xstzTextDef + msDefault = read_uInt16_BeltAndBracesString(*pDataStream); + } + else + { + // CheckBox or ComboBox + sal_uInt16 wDef = 0; + pDataStream->ReadUInt16( wDef ); + mnChecked = wDef; // default + if (nWhich == WW8_CT_CHECKBOX) + { + if ( iRes != 25 ) + mnChecked = iRes; + msDefault = ( wDef == 0 ) ? OUStringLiteral( "0" ) : OUStringLiteral( "1" ); + } + } + // xstzTextFormat + msFormatting = read_uInt16_BeltAndBracesString(*pDataStream); + // xstzHelpText + msHelp = read_uInt16_BeltAndBracesString(*pDataStream); + // xstzStatText + msToolTip = read_uInt16_BeltAndBracesString(*pDataStream); + + // xstzEntryMcr + msEntryMcr = read_uInt16_BeltAndBracesString(*pDataStream); + //xstzExitMcr + msExitMcr = read_uInt16_BeltAndBracesString(*pDataStream); + + if (nWhich == WW8_CT_DROPDOWN) + { + bool bAllOk = true; + // SSTB (see Spec. 2.2.4) + sal_uInt16 fExtend = 0; + pDataStream->ReadUInt16( fExtend ); + sal_uInt16 nStringsCnt = 0; + + // Isn't it that if fExtend isn't 0xFFFF then fExtend actually + // doesn't exist and we really have just read nStringsCnt ( or cData )? + if (fExtend != 0xFFFF) + bAllOk = false; + pDataStream->ReadUInt16( nStringsCnt ); + + // I guess this should be zero ( and we should ensure that ) + sal_uInt16 cbExtra = 0; + pDataStream->ReadUInt16( cbExtra ); + + OSL_ENSURE(bAllOk, "Unknown formfield dropdown list structure"); + if (!bAllOk) //Not as expected, don't risk it at all. + nStringsCnt = 0; + const size_t nMinRecordSize = sizeof(sal_uInt16); + const size_t nMaxRecords = pDataStream->remainingSize() / nMinRecordSize; + if (nStringsCnt > nMaxRecords) + { + SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nStringsCnt << " claimed, truncating"); + nStringsCnt = nMaxRecords; + } + maListEntries.reserve(nStringsCnt); + for (sal_uInt32 nI = 0; nI < nStringsCnt; ++nI) + { + OUString sEntry = read_uInt16_PascalString(*pDataStream); + maListEntries.push_back(sEntry); + } + } + mfDropdownIndex = iRes; + + mbHelp = bits1 & 0x80; + + nField = bits2; + mfToolTip = nField & 0x01; + mfNoMark = (nField & 0x02)>>1; + mfType = (nField & 0x38)>>3; + mfUnused = (nField & 0xE0)>>5; +} + +WW8FormulaListBox::WW8FormulaListBox(SwWW8ImplReader &rR) + : WW8FormulaControl(SL::aListBox, rR) +{ +} + +//Miserable hack to get a hardcoded guesstimate of the size of a list dropdown +//box's first entry to set as the lists default size +awt::Size SwWW8ImplReader::MiserableDropDownFormHack(const OUString &rString, + uno::Reference<beans::XPropertySet> const & rPropSet) +{ + awt::Size aRet; + struct CtrlFontMapEntry + { + sal_uInt16 nWhichId; + const char* pPropNm; + }; + const CtrlFontMapEntry aMapTable[] = + { + { RES_CHRATR_COLOR, "TextColor" }, + { RES_CHRATR_FONT, "FontName" }, + { RES_CHRATR_FONTSIZE, "FontHeight" }, + { RES_CHRATR_WEIGHT, "FontWeight" }, + { RES_CHRATR_UNDERLINE, "FontUnderline" }, + { RES_CHRATR_CROSSEDOUT, "FontStrikeout" }, + { RES_CHRATR_POSTURE, "FontSlant" }, + { 0, nullptr } + }; + + vcl::Font aFont; + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = + rPropSet->getPropertySetInfo(); + + uno::Any aTmp; + for (const CtrlFontMapEntry* pMap = aMapTable; pMap->nWhichId; ++pMap) + { + bool bSet = true; + const SfxPoolItem* pItem = GetFormatAttr( pMap->nWhichId ); + OSL_ENSURE(pItem, "Impossible"); + if (!pItem) + continue; + + switch ( pMap->nWhichId ) + { + case RES_CHRATR_COLOR: + { + OUString aNm; + if (xPropSetInfo->hasPropertyByName(aNm = "TextColor")) + { + aTmp <<= static_cast<sal_Int32>(static_cast<const SvxColorItem*>(pItem)->GetValue()); + rPropSet->setPropertyValue(aNm, aTmp); + } + } + aFont.SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue()); + break; + case RES_CHRATR_FONT: + { + const SvxFontItem *pFontItem = static_cast<const SvxFontItem *>(pItem); + OUString aNm; + if (xPropSetInfo->hasPropertyByName(aNm = "FontStyleName")) + { + aTmp <<= pFontItem->GetStyleName(); + rPropSet->setPropertyValue( aNm, aTmp ); + } + if (xPropSetInfo->hasPropertyByName(aNm = "FontFamily")) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily()); + rPropSet->setPropertyValue( aNm, aTmp ); + } + if (xPropSetInfo->hasPropertyByName(aNm = "FontCharset")) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet()); + rPropSet->setPropertyValue( aNm, aTmp ); + } + if (xPropSetInfo->hasPropertyByName(aNm = "FontPitch")) + { + aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch()); + rPropSet->setPropertyValue( aNm, aTmp ); + } + + aTmp <<= pFontItem->GetFamilyName(); + aFont.SetFamilyName( pFontItem->GetFamilyName() ); + aFont.SetStyleName( pFontItem->GetStyleName() ); + aFont.SetFamily( pFontItem->GetFamily() ); + aFont.SetCharSet( pFontItem->GetCharSet() ); + aFont.SetPitch( pFontItem->GetPitch() ); + } + break; + + case RES_CHRATR_FONTSIZE: + { + Size aSize( aFont.GetFontSize().Width(), + static_cast<const SvxFontHeightItem*>(pItem)->GetHeight() ); + aTmp <<= static_cast<float>(aSize.Height()) / 20.0; + + aFont.SetFontSize(OutputDevice::LogicToLogic(aSize, + MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM))); + } + break; + + case RES_CHRATR_WEIGHT: + aTmp <<= vcl::unohelper::ConvertFontWeight( + static_cast<const SvxWeightItem*>(pItem)->GetWeight() ); + aFont.SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() ); + break; + + case RES_CHRATR_UNDERLINE: + aTmp <<= static_cast<sal_Int16>(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle()); + aFont.SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle()); + break; + + case RES_CHRATR_CROSSEDOUT: + aTmp <<= static_cast<sal_Int16>( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() ); + aFont.SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() ); + break; + + case RES_CHRATR_POSTURE: + aTmp <<= static_cast<sal_Int16>( static_cast<const SvxPostureItem*>(pItem)->GetPosture() ); + aFont.SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() ); + break; + + default: + bSet = false; + break; + } + + if (bSet && xPropSetInfo->hasPropertyByName(OUString::createFromAscii(pMap->pPropNm))) + rPropSet->setPropertyValue(OUString::createFromAscii(pMap->pPropNm), aTmp); + } + // now calculate the size of the control + OutputDevice* pOut = Application::GetDefaultDevice(); + OSL_ENSURE(pOut, "Impossible"); + if (pOut) + { + pOut->Push( PushFlags::FONT | PushFlags::MAPMODE ); + pOut->SetMapMode( MapMode( MapUnit::Map100thMM )); + pOut->SetFont( aFont ); + aRet.Width = pOut->GetTextWidth(rString); + aRet.Width += 500; //plus size of button, total hack territory + aRet.Height = pOut->GetTextHeight(); + pOut->Pop(); + } + return aRet; +} + +bool WW8FormulaListBox::Import(const uno::Reference < + lang::XMultiServiceFactory> &rServiceFactory, + uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz ) +{ + uno::Reference<uno::XInterface> xCreate = rServiceFactory->createInstance("com.sun.star.form.component.ComboBox"); + if( !xCreate.is() ) + return false; + + rFComp.set(xCreate, uno::UNO_QUERY); + if( !rFComp.is() ) + return false; + + uno::Reference<beans::XPropertySet> xPropSet(xCreate, uno::UNO_QUERY); + + uno::Any aTmp; + if (!msTitle.isEmpty()) + aTmp <<= msTitle; + else + aTmp <<= msName; + xPropSet->setPropertyValue("Name", aTmp ); + + if (!msToolTip.isEmpty()) + { + aTmp <<= msToolTip; + xPropSet->setPropertyValue("HelpText", aTmp ); + } + + xPropSet->setPropertyValue("Dropdown", css::uno::makeAny(true)); + + if (!maListEntries.empty()) + { + sal_uInt32 nLen = maListEntries.size(); + uno::Sequence< OUString > aListSource(nLen); + for (sal_uInt32 nI = 0; nI < nLen; ++nI) + aListSource[nI] = maListEntries[nI]; + aTmp <<= aListSource; + xPropSet->setPropertyValue("StringItemList", aTmp ); + + if (mfDropdownIndex < nLen) + { + aTmp <<= aListSource[mfDropdownIndex]; + } + else + { + aTmp <<= aListSource[0]; + } + + xPropSet->setPropertyValue("DefaultText", aTmp ); + + rSz = mrRdr.MiserableDropDownFormHack(maListEntries[0], xPropSet); + } + else + { + static const sal_Unicode aBlank[] = + { + 0x2002,0x2002,0x2002,0x2002,0x2002 + }; + rSz = mrRdr.MiserableDropDownFormHack(OUString(aBlank, SAL_N_ELEMENTS(aBlank)), xPropSet); + } + + return true; +} + +WW8FormulaCheckBox::WW8FormulaCheckBox(SwWW8ImplReader &rR) + : WW8FormulaControl(SL::aCheckBox, rR) +{ +} + +static void lcl_AddToPropertyContainer +(uno::Reference<beans::XPropertySet> const & xPropSet, + const OUString & rPropertyName, const OUString & rValue) +{ + uno::Reference<beans::XPropertySetInfo> xPropSetInfo = + xPropSet->getPropertySetInfo(); + if (xPropSetInfo.is() && + ! xPropSetInfo->hasPropertyByName(rPropertyName)) + { + uno::Reference<beans::XPropertyContainer> + xPropContainer(xPropSet, uno::UNO_QUERY); + uno::Any aAny((OUString())); + xPropContainer->addProperty + (rPropertyName, + static_cast<sal_Int16>(beans::PropertyAttribute::BOUND | + beans::PropertyAttribute::REMOVABLE), + aAny); + } + + uno::Any aAnyValue(rValue); + xPropSet->setPropertyValue(rPropertyName, aAnyValue ); +} + +bool WW8FormulaCheckBox::Import(const uno::Reference < + lang::XMultiServiceFactory> &rServiceFactory, + uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz ) +{ + uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance("com.sun.star.form.component.CheckBox"); + if( !xCreate.is() ) + return false; + + rFComp.set( xCreate, uno::UNO_QUERY ); + if( !rFComp.is() ) + return false; + + uno::Reference< beans::XPropertySet > xPropSet( xCreate, uno::UNO_QUERY ); + + rSz.Width = 16 * mhpsCheckBox; + rSz.Height = 16 * mhpsCheckBox; + + uno::Any aTmp; + if (!msTitle.isEmpty()) + aTmp <<= msTitle; + else + aTmp <<= msName; + xPropSet->setPropertyValue("Name", aTmp ); + + aTmp <<= static_cast<sal_Int16>(mnChecked); + xPropSet->setPropertyValue("DefaultState", aTmp); + + if (!msToolTip.isEmpty()) + lcl_AddToPropertyContainer(xPropSet, "HelpText", msToolTip); + + if (!msHelp.isEmpty()) + lcl_AddToPropertyContainer(xPropSet, "HelpF1Text", msHelp); + + return true; + +} + +WW8FormulaEditBox::WW8FormulaEditBox(SwWW8ImplReader &rR) + : WW8FormulaControl(SL::aTextField ,rR) +{ +} + +bool SwMSConvertControls::InsertControl( + const uno::Reference< form::XFormComponent > & rFComp, + const awt::Size& rSize, uno::Reference< drawing::XShape > *pShape, + bool bFloatingCtrl) +{ + const uno::Reference< container::XIndexContainer > &rComps = GetFormComps(); + uno::Any aTmp( &rFComp, cppu::UnoType<form::XFormComponent>::get()); + rComps->insertByIndex( rComps->getCount(), aTmp ); + + const uno::Reference< lang::XMultiServiceFactory > &rServiceFactory = + GetServiceFactory(); + if( !rServiceFactory.is() ) + return false; + + uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance( + "com.sun.star.drawing.ControlShape"); + if( !xCreate.is() ) + return false; + + uno::Reference< drawing::XShape > xShape(xCreate, uno::UNO_QUERY); + + OSL_ENSURE(xShape.is(), "Did not get XShape"); + xShape->setSize(rSize); + + uno::Reference< beans::XPropertySet > xShapePropSet( + xCreate, uno::UNO_QUERY ); + + //I lay a small bet that this will change to + //sal_Int16 nTemp=TextContentAnchorType::AS_CHARACTER; + text::TextContentAnchorType nTemp; + if (bFloatingCtrl) + nTemp = text::TextContentAnchorType_AT_PARAGRAPH; + else + nTemp = text::TextContentAnchorType_AS_CHARACTER; + + xShapePropSet->setPropertyValue("AnchorType", uno::Any(static_cast<sal_Int16>(nTemp)) ); + + xShapePropSet->setPropertyValue("VertOrient", uno::Any(sal_Int16(text::VertOrientation::TOP)) ); + + uno::Reference< text::XText > xDummyTextRef; + uno::Reference< text::XTextRange > xTextRg = + new SwXTextRange( *pPaM, xDummyTextRef ); + + aTmp <<= xTextRg; + xShapePropSet->setPropertyValue("TextRange", aTmp ); + + // Set the Control-Model for the Control-Shape + uno::Reference< drawing::XControlShape > xControlShape( xShape, + uno::UNO_QUERY ); + uno::Reference< awt::XControlModel > xControlModel( rFComp, + uno::UNO_QUERY ); + xControlShape->setControl( xControlModel ); + + if (pShape) + *pShape = xShape; + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par4.cxx b/sw/source/filter/ww8/ww8par4.cxx new file mode 100644 index 000000000..3c0c6a701 --- /dev/null +++ b/sw/source/filter/ww8/ww8par4.cxx @@ -0,0 +1,549 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <IDocumentContentOperations.hxx> +#include "writerhelper.hxx" +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/embed/Aspects.hpp> + +#include <cstddef> +#include <osl/endian.h> +#include <sot/storage.hxx> +#include <com/sun/star/drawing/XShape.hpp> +#include <hintids.hxx> +#include <svx/svdoole2.hxx> +#include <filter/msfilter/msdffimp.hxx> +#include "sprmids.hxx" +#include <svx/unoapi.hxx> +#include <sal/log.hxx> + +#include <sot/exchange.hxx> +#include <fmtanchr.hxx> +#include <frmfmt.hxx> +#include <pam.hxx> +#include <docsh.hxx> +#include <mdiexp.hxx> +#include <fltshell.hxx> +#include <shellio.hxx> + +#include <vcl/wmf.hxx> +#include <vcl/gdimtf.hxx> + +#include "ww8scan.hxx" +#include "ww8par.hxx" +#include "ww8par2.hxx" + +namespace { + +struct OLE_MFP +{ + sal_Int16 mm; // 0x6 int + sal_Int16 xExt; // 0x8 int in 1/100 mm + sal_Int16 yExt; // 0xa int in 1/100 mm + sal_Int16 hMF; // 0xc int +}; + +} + +using namespace ::com::sun::star; + +static bool SwWw8ReadScaling(long& rX, long& rY, tools::SvRef<SotStorage> const & rSrc1) +{ + // Getting the scaling factor: + // Information in the PIC-stream (by trying out) + // 0x0 (l)cb + // 0x08 .. 0x0a Flags ?? + // 0x08 contains: 1 / 0 + // 0x09 contains: 0,8,0x18 + // 0x0a contains: always 8, MAP_ANISOTROPIC ??? + // 0x0b contains: always 0 + // 0x0c, 0x10 original size x,y in 1/100 mm + // 0x14, 0x16 original size x,y in tw + // 0x2c, 0x30 scaling x,y in per thousand + // 0x34, 0x38, 0x3c, 0x40 Crop Left, Top, Right, Bot in tw + + tools::SvRef<SotStorageStream> xSrc3 = rSrc1->OpenSotStream( "\3PIC", + StreamMode::STD_READ ); + SotStorageStream* pS = xSrc3.get(); + pS->SetEndian( SvStreamEndian::LITTLE ); + pS->Seek( STREAM_SEEK_TO_END ); + + OSL_ENSURE( pS->Tell() >= 76, "+OLE-PIC-Stream is shorter than 76 Byte" ); + + sal_Int32 nOrgWidth, + nOrgHeight, + nScaleX, + nScaleY, + nCropLeft, + nCropTop, + nCropRight, + nCropBottom; + pS->Seek( 0x14 ); + pS->ReadInt32( nOrgWidth ) // Original Size in 1/100 mm + .ReadInt32( nOrgHeight ); + pS->Seek( 0x2c ); + pS->ReadInt32( nScaleX ) // Scaling in Promille + .ReadInt32( nScaleY ) + .ReadInt32( nCropLeft ) // Cropping in 1/100 mm + .ReadInt32( nCropTop ) + .ReadInt32( nCropRight ) + .ReadInt32( nCropBottom ); + + rX = nOrgWidth - nCropLeft - nCropRight; + rY = nOrgHeight - nCropTop - nCropBottom; + if (10 > nScaleX || 65536 < nScaleX || 10 > nScaleY || 65536 < nScaleY) + { + OSL_ENSURE( !pS, "+OLE-scaling information in PIC-stream wrong" ); + return false; + } + else + { + rX = (rX * nScaleX) / 1000; + rY = (rY * nScaleY) / 1000; + } + return true; +} + +static bool SwWw6ReadMetaStream(GDIMetaFile& rWMF, OLE_MFP* pMfp, + tools::SvRef<SotStorage> const & rSrc1) +{ + tools::SvRef<SotStorageStream> xSrc2 = rSrc1->OpenSotStream( "\3META", + StreamMode::STD_READ ); + SotStorageStream* pSt = xSrc2.get(); + pSt->SetEndian( SvStreamEndian::LITTLE ); + size_t const nRead = pSt->ReadBytes(pMfp, sizeof(*pMfp)); + // read mini-placable-header + if (nRead != sizeof(*pMfp)) + return false; + +#if defined OSL_BIGENDIAN + pMfp->mm = OSL_SWAPWORD( pMfp->mm ); + pMfp->xExt = OSL_SWAPWORD( pMfp->xExt ); + pMfp->yExt = OSL_SWAPWORD( pMfp->yExt ); +#endif // OSL_BIGENDIAN + + if( pMfp->mm == 94 || pMfp->mm == 99 ) + { + SAL_WARN("sw.ww8", "+OLE: wrong metafile type"); + return false; + } + if( pMfp->mm != 8 ) + { + SAL_WARN("sw.ww8", "OLE: wrong mMetafile type (not anisotropic)"); + } + if( !pMfp->xExt || !pMfp->yExt ) + { + SAL_WARN("sw.ww8", "+OLE: size of 0?"); + return false; + } + bool bOk = ReadWindowMetafile( *pSt, rWMF ); // read WMF + // *pSt >> aWMF doesn't work without the placable header + if (!bOk || pSt->GetError() || rWMF.GetActionSize() == 0) + { + SAL_WARN("sw.ww8", "+OLE: could not read the metafile"); + return false; + } + + rWMF.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) ); + + // Scale MetaFile to new size and save new size to MetaFile + Size aOldSiz( rWMF.GetPrefSize() ); + Size aNewSiz( pMfp->xExt, pMfp->yExt ); + Fraction aFracX( aNewSiz.Width(), aOldSiz.Width() ); + Fraction aFracY( aNewSiz.Height(), aOldSiz.Height() ); + + rWMF.Scale( aFracX, aFracY ); + rWMF.SetPrefSize( aNewSiz ); + + return true; +} + +static bool SwWw6ReadMacPICTStream(Graphic& rGraph, tools::SvRef<SotStorage> const & rSrc1) +{ + // 03-META-stream does not exist. Maybe a 03-PICT? + tools::SvRef<SotStorageStream> xSrc4 = rSrc1->OpenSotStream("\3PICT"); + SotStorageStream* pStp = xSrc4.get(); + pStp->SetEndian( SvStreamEndian::LITTLE ); + sal_uInt8 aTestA[10]; // Does the 01Ole-stream even exist? + size_t const nReadTst = pStp->ReadBytes(aTestA, sizeof(aTestA)); + if (nReadTst != sizeof(aTestA)) + return false; + + pStp->Seek( STREAM_SEEK_TO_BEGIN ); + + // Mac-Pict is in the 03PICT-StorageStream but without the first 512 Bytes + // which are not relevant in a MAC-PICT (they are not evaluated) + return SwWW8ImplReader::GetPictGrafFromStream(rGraph, *pStp); +} + +SwFlyFrameFormat* SwWW8ImplReader::InsertOle(SdrOle2Obj &rObject, + const SfxItemSet &rFlySet, const SfxItemSet *rGrfSet) +{ + SfxObjectShell *pPersist = m_rDoc.GetPersist(); + OSL_ENSURE(pPersist, "No persist, cannot insert objects correctly"); + if (!pPersist) + return nullptr; + + SwFlyFrameFormat *pRet = nullptr; + + std::unique_ptr<SfxItemSet> pMathFlySet; + uno::Reference < embed::XClassifiedObject > xClass = rObject.GetObjRef(); + if( xClass.is() ) + { + SvGlobalName aClassName( xClass->getClassID() ); + if (SotExchange::IsMath(aClassName)) + { + // StarMath sets it own fixed size, so its counter productive to use + // the size Word says it is. i.e. Don't attempt to override its size. + pMathFlySet.reset(new SfxItemSet(rFlySet)); + pMathFlySet->ClearItem(RES_FRM_SIZE); + } + } + + /* + Take complete responsibility of the object away from SdrOle2Obj and to + me here locally. This utility class now owns the object. + */ + + // TODO/MBA: is the object inserted multiple times here? Testing! + // And is it a problem that we now use the same naming scheme as in the other apps? + sw::hack::DrawingOLEAdaptor aOLEObj(rObject, *pPersist); + OUString sNewName; + bool bSuccess = aOLEObj.TransferToDoc(sNewName); + + OSL_ENSURE(bSuccess, "Insert OLE failed"); + if (bSuccess) + { + const SfxItemSet *pFlySet = pMathFlySet ? pMathFlySet.get() : &rFlySet; + pRet = m_rDoc.getIDocumentContentOperations().InsertOLE(*m_pPaM, sNewName, rObject.GetAspect(), pFlySet, rGrfSet); + } + return pRet; +} + +SwFrameFormat* SwWW8ImplReader::ImportOle(const Graphic* pGrf, + const SfxItemSet* pFlySet, const SfxItemSet *pGrfSet, const tools::Rectangle& aVisArea ) +{ + ::SetProgressState(m_nProgress, m_pDocShell); // Update + SwFrameFormat* pFormat = nullptr; + + GrafikCtor(); + + Graphic aGraph; + SdrObject* pRet = ImportOleBase(aGraph, pGrf, pFlySet, aVisArea ); + + // create flyset + std::unique_ptr<SfxItemSet> pTempSet; + if( !pFlySet ) + { + pTempSet.reset( new SfxItemSet( m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END-1>{}) ); + + pFlySet = pTempSet.get(); + + // Remove distance/borders + Reader::ResetFrameFormatAttrs( *pTempSet ); + + SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR ); + aAnchor.SetAnchor( m_pPaM->GetPoint() ); + pTempSet->Put( aAnchor ); + + const Size aSizeTwip = OutputDevice::LogicToLogic( + aGraph.GetPrefSize(), aGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + + pTempSet->Put( SwFormatFrameSize( SwFrameSize::Fixed, aSizeTwip.Width(), + aSizeTwip.Height() ) ); + pTempSet->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME )); + + if (m_xSFlyPara) + { + // Resize the frame to the picture size if there is an OLE object + // in the frame (only if auto-width) + m_xSFlyPara->BoxUpWidth(aSizeTwip.Width()); + } + } + + if (pRet) // OLE object was inserted + { + if (SdrOle2Obj *pOleObj = dynamic_cast<SdrOle2Obj*>(pRet)) + { + pFormat = InsertOle(*pOleObj, *pFlySet, pGrfSet); + SdrObject::Free(pRet); // we don't need this anymore + } + else + pFormat = m_rDoc.getIDocumentContentOperations().InsertDrawObj(*m_pPaM, *pRet, *pFlySet ); + } + else if ( + GraphicType::GdiMetafile == aGraph.GetType() || + GraphicType::Bitmap == aGraph.GetType() + ) + { + pFormat = m_rDoc.getIDocumentContentOperations().InsertGraphic( + *m_pPaM, OUString(), OUString(), &aGraph, pFlySet, + pGrfSet, nullptr); + } + return pFormat; +} + +bool SwWW8ImplReader::ImportOleWMF(const tools::SvRef<SotStorage>& xSrc1, GDIMetaFile& rWMF, + long& rX, long& rY) +{ + bool bOk = false; + OLE_MFP aMfp; + if( SwWw6ReadMetaStream( rWMF, &aMfp, xSrc1 ) ) + { + // take scaling factor as found in PIC and apply it to graphic. + SwWw8ReadScaling( rX, rY, xSrc1 ); + Size aFinalSize, aOrigSize; + aFinalSize.setWidth( rX ); + aFinalSize.setHeight( rY ); + aFinalSize = OutputDevice::LogicToLogic( + aFinalSize, MapMode(MapUnit::MapTwip), rWMF.GetPrefMapMode() ); + aOrigSize = rWMF.GetPrefSize(); + Fraction aScaleX(aFinalSize.Width(),aOrigSize.Width()); + Fraction aScaleY(aFinalSize.Height(),aOrigSize.Height()); + rWMF.Scale( aScaleX, aScaleY ); + bOk = true; + } + return bOk; +} + +SdrObject* SwWW8ImplReader::ImportOleBase( Graphic& rGraph, + const Graphic* pGrf, const SfxItemSet* pFlySet, const tools::Rectangle& aVisArea ) +{ + if (!m_pStg) + { + SAL_WARN("sw.ww8", "no storage for ole objects"); + return nullptr; + } + + ::SetProgressState( m_nProgress, m_rDoc.GetDocShell() ); // Update + + long nX=0, nY=0; // nX, nY is graphic size + bool bOleOk = true; + + // results in the name "_4711" + OUString aSrcStgName = "_" + OUString::number( m_nObjLocFc ); + + tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool); + tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( aSrcStgName ); + + if (pGrf) + { + rGraph = *pGrf; + const Size aSizeTwip = OutputDevice::LogicToLogic( + rGraph.GetPrefSize(), rGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + nX = aSizeTwip.Width(); + nY = aSizeTwip.Height(); + } + else + { + GDIMetaFile aWMF; + + if (ImportOleWMF(xSrc1,aWMF,nX,nY)) + rGraph = Graphic( aWMF ); + else if( SwWw6ReadMacPICTStream( rGraph, xSrc1 ) ) + { + // 03-META stream is not available. Maybe it's a 03-PICT? + const Size aSizeTwip = OutputDevice::LogicToLogic( + rGraph.GetPrefSize(), rGraph.GetPrefMapMode(), MapMode(MapUnit::MapTwip)); + nX = aSizeTwip.Width(); + nY = aSizeTwip.Height(); + // PICT: no WMF available -> Graphic instead of OLE + bOleOk = false; + } + } // StorageStreams closed again + + tools::Rectangle aRect(0, 0, nX, nY); + + if (pFlySet) + { + if (const SwFormatFrameSize* pSize = pFlySet->GetItem<SwFormatFrameSize>(RES_FRM_SIZE, false)) + { + aRect.SetSize(pSize->GetSize()); + } + } + + SdrObject* pRet = nullptr; + + if (!(m_bIsHeader || m_bIsFooter)) + { + //Can't put them in headers/footers :-( + uno::Reference< drawing::XShape > xRef; + OSL_ENSURE(m_xFormImpl, "Impossible"); + if (m_xFormImpl && m_xFormImpl->ReadOCXStream(xSrc1, &xRef)) + { + pRet = GetSdrObjectFromXShape(xRef); + OSL_ENSURE(pRet, "Impossible"); + if (pRet) + pRet->SetLogicRect(aRect); + return pRet; + } + } + + if (GraphicType::GdiMetafile == rGraph.GetType() || + GraphicType::Bitmap == rGraph.GetType()) + { + ::SetProgressState(m_nProgress, m_pDocShell); // Update + + if (bOleOk) + { + sal_uLong nOldPos = m_pDataStream->Tell(); + m_pDataStream->Seek(STREAM_SEEK_TO_END); + SvStream *pTmpData = nullptr; + if (m_nObjLocFc < m_pDataStream->Tell()) + { + pTmpData = m_pDataStream; + pTmpData->Seek( m_nObjLocFc ); + } + + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + + { + tools::SvRef<SotStorageStream> xObjInfoSrc = xSrc1->OpenSotStream("\3ObjInfo", + StreamMode::STD_READ ); + if ( xObjInfoSrc.is() && !xObjInfoSrc->GetError() ) + { + sal_uInt8 nByte = 0; + xObjInfoSrc->ReadUChar( nByte ); + if ( ( nByte >> 4 ) & embed::Aspects::MSOLE_ICON ) + nAspect = embed::Aspects::MSOLE_ICON; + } + } + + ErrCode nError = ERRCODE_NONE; + GrafikCtor(); + + pRet = SvxMSDffManager::CreateSdrOLEFromStorage( + *m_pDrawModel, + aSrcStgName, + xSrc0, + m_pDocShell->GetStorage(), + rGraph, + aRect, + aVisArea, + pTmpData, + nError, + SwMSDffManager::GetFilterFlags(), + nAspect, + GetBaseURL()); + m_pDataStream->Seek( nOldPos ); + } + } + return pRet; +} + +void SwWW8ImplReader::ReadRevMarkAuthorStrTabl( SvStream& rStrm, + sal_Int32 nTablePos, sal_Int32 nTableSiz, SwDoc& rDocOut ) +{ + std::vector<OUString> aAuthorNames; + WW8ReadSTTBF( !m_bVer67, rStrm, nTablePos, nTableSiz, m_bVer67 ? 2 : 0, + m_eStructCharSet, aAuthorNames ); + + sal_uInt16 nCount = static_cast< sal_uInt16 >(aAuthorNames.size()); + for( sal_uInt16 nAuthor = 0; nAuthor < nCount; ++nAuthor ) + { + // Store author in doc + std::size_t nSWId = rDocOut.getIDocumentRedlineAccess().InsertRedlineAuthor(aAuthorNames[nAuthor]); + // Store matchpair + m_aAuthorInfos[nAuthor] = nSWId; + } +} + +/* + Revision Marks ( == Redlining ) +*/ +// insert or delete content (change char attributes resp.) +void SwWW8ImplReader::Read_CRevisionMark(RedlineType eType, + const sal_uInt8* pData, short nLen ) +{ + // there *must* be a SprmCIbstRMark[Del] and a SprmCDttmRMark[Del] + // pointing to the very same char position as our SprmCFRMark[Del] + if (!m_xPlcxMan) + return; + const sal_uInt8* pSprmCIbstRMark; + const sal_uInt8* pSprmCDttmRMark; + if( RedlineType::Format == eType ) + { + pSprmCIbstRMark = nLen >= 3 ? pData+1 : nullptr; + pSprmCDttmRMark = nLen >= 7 ? pData+3 : nullptr; + } + else + { + /* It is possible to have a number of date stamps for the created time + * of the change, (possibly a word bug) so we must use the "get a full + * list" variant of HasCharSprm and take the last one as the true one. + */ + std::vector<SprmResult> aResult; + bool bIns = (RedlineType::Insert == eType); + if( m_bVer67 ) + { + m_xPlcxMan->HasCharSprm(69, aResult); + pSprmCIbstRMark = (aResult.empty() || aResult.back().nRemainingData < 2) ? nullptr : aResult.back().pSprm; + aResult.clear(); + m_xPlcxMan->HasCharSprm(70, aResult); + pSprmCDttmRMark = (aResult.empty() || aResult.back().nRemainingData < 4) ? nullptr : aResult.back().pSprm; + } + else + { + m_xPlcxMan->HasCharSprm( bIns ? 0x4804 : 0x4863, aResult); + pSprmCIbstRMark = (aResult.empty() || aResult.back().nRemainingData < 2) ? nullptr : aResult.back().pSprm; + aResult.clear(); + m_xPlcxMan->HasCharSprm( bIns ? 0x6805 : NS_sprm::sprmCDttmRMarkDel, aResult); + pSprmCDttmRMark = (aResult.empty() || aResult.back().nRemainingData < 4) ? nullptr : aResult.back().pSprm; + } + } + + if (nLen < 0) + m_xRedlineStack->close(*m_pPaM->GetPoint(), eType, m_xTableDesc.get()); + else + { + // start of new revision mark, if not there default to first entry + sal_uInt16 nWWAutNo = pSprmCIbstRMark ? SVBT16ToUInt16(pSprmCIbstRMark) : 0; + sal_uInt32 nWWDate = pSprmCDttmRMark ? SVBT32ToUInt32(pSprmCDttmRMark): 0; + DateTime aStamp(msfilter::util::DTTM2DateTime(nWWDate)); + std::size_t nAuthorNo = m_aAuthorInfos[nWWAutNo]; + SwFltRedline aNewAttr(eType, nAuthorNo, aStamp); + NewAttr(aNewAttr); + } +} + +// insert new content +void SwWW8ImplReader::Read_CFRMark(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + Read_CRevisionMark( RedlineType::Insert, pData, nLen ); +} + +// delete old content +void SwWW8ImplReader::Read_CFRMarkDel(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + Read_CRevisionMark( RedlineType::Delete, pData, nLen ); +} + +// change properties of content ( == char formatting) +void SwWW8ImplReader::Read_CPropRMark(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + // complex (len is always 7) + // 1 byte - chp.fPropRMark + // 2 bytes - chp.ibstPropRMark + // 4 bytes - chp.dttmPropRMark; + Read_CRevisionMark( RedlineType::Format, pData, nLen ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par5.cxx b/sw/source/filter/ww8/ww8par5.cxx new file mode 100644 index 000000000..9178b7961 --- /dev/null +++ b/sw/source/filter/ww8/ww8par5.cxx @@ -0,0 +1,3740 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/types.h> +#include <tools/solar.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <comphelper/simplefileaccessinteraction.hxx> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> + +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <svl/cintitem.hxx> +#include <svl/lngmisc.hxx> +#include <svl/urihelper.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <sfx2/linkmgr.hxx> +#include <rtl/character.hxx> +#include <unotools/charclass.hxx> + +#include <ucbhelper/content.hxx> +#include <ucbhelper/commandenvironment.hxx> + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <hintids.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/langitem.hxx> +#include <fmtfld.hxx> +#include <fmtanchr.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentMarkAccess.hxx> +#include <IDocumentState.hxx> +#include <flddat.hxx> +#include <docufld.hxx> +#include <reffld.hxx> +#include <IMark.hxx> +#include <expfld.hxx> +#include <dbfld.hxx> +#include <tox.hxx> +#include <section.hxx> +#include <ndtxt.hxx> +#include <fmtinfmt.hxx> +#include <chpfld.hxx> +#include <fmtruby.hxx> +#include <charfmt.hxx> +#include <breakit.hxx> +#include <fmtclds.hxx> +#include <poolfmt.hxx> +#include <SwStyleNameMapper.hxx> + +#include "ww8scan.hxx" +#include "ww8par.hxx" +#include "writerhelper.hxx" +#include "fields.hxx" +#include <o3tl/safeint.hxx> +#include <unotools/fltrcfg.hxx> +#include <xmloff/odffields.hxx> + +#include <algorithm> + +#define MAX_FIELDLEN 64000 + +#define WW8_TOX_LEVEL_DELIM ':' + +using namespace ::com::sun::star; +using namespace msfilter::util; +using namespace sw::util; +using namespace sw::mark; +using namespace std; // #i24377# +using namespace nsSwDocInfoSubType; + +// Bookmarks +namespace +{ + // #120879# - helper method to identify a bookmark name to match the internal TOC bookmark naming convention + bool IsTOCBookmarkName(const OUString& rName) + { + return rName.startsWith("_Toc") || rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"); + } + + OUString EnsureTOCBookmarkName(const OUString& rName) + { + OUString sTmp = rName; + if ( IsTOCBookmarkName ( rName ) ) + { + if ( ! rName.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) ) + sTmp = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix() + rName; + } + return sTmp; + } +} + +long SwWW8ImplReader::Read_Book(WW8PLCFManResult*) +{ + // should also work via pRes.nCo2OrIdx + WW8PLCFx_Book* pB = m_xPlcxMan->GetBook(); + if( !pB ) + { + OSL_ENSURE( pB, "WW8PLCFx_Book - Pointer does not exist" ); + return 0; + } + + eBookStatus eB = pB->GetStatus(); + if (eB & BOOK_IGNORE) + return 0; // ignore bookmark + + if (pB->GetIsEnd()) + { + m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, + pB->GetHandle(), (eB & BOOK_FIELD)!=0); + return 0; + } + + // "_Hlt*" are unnecessary + const OUString* pName = pB->GetName(); + // Now, as we read the TOC field completely, we also need the hyperlinks inside keep available. + // So the hidden bookmarks inside for hyperlink jumping also should be kept. + if ( !pName || + pName->startsWithIgnoreAsciiCase( "_Hlt" ) ) + { + return 0; + } + + // do NOT call ToUpper as the bookmark name can also be a hyperlink target! + + OUString aVal; + if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::BOOK_TO_VAR_REF ) ) + { + // set variable for translation bookmark + long nLen = pB->GetLen(); + if( nLen > MAX_FIELDLEN ) + nLen = MAX_FIELDLEN; + + long nOldPos = m_pStrm->Tell(); + m_xSBase->WW8ReadString( *m_pStrm, aVal, pB->GetStartPos(), nLen, + m_eStructCharSet ); + m_pStrm->Seek( nOldPos ); + + // now here the implementation of the old "QuoteString" and + // I hope with a better performance as before. It's also only + // needed if the filterflags say we will convert bookmarks + // to SetExpFields! And this the exception! + + bool bSetAsHex; + bool bAllowCr = SwFltGetFlag(m_nFieldFlags, + SwFltControlStack::ALLOW_FLD_CR); + + for( sal_Int32 nI = 0; + nI < aVal.getLength() && aVal.getLength() < (MAX_FIELDLEN - 4); + ++nI ) + { + const sal_Unicode cChar = aVal[nI]; + switch( cChar ) + { + case 0x0b: + case 0x0c: + case 0x0d: + if( bAllowCr ) + { + aVal = aVal.replaceAt( nI, 1, "\n" ); + bSetAsHex = false; + } + else + bSetAsHex = true; + break; + + case 0xFE: + case 0xFF: + bSetAsHex = true; + break; + + default: + bSetAsHex = 0x20 > cChar; + break; + } + + if( bSetAsHex ) + { + //all Hex-Numbers with \x before + OUString sTmp( "\\x" ); + if( cChar < 0x10 ) + sTmp += "0"; + sTmp += OUString::number( cChar, 16 ); + aVal = aVal.replaceAt( nI, 1 , sTmp ); + nI += sTmp.getLength() - 1; + } + } + + if ( aVal.getLength() > (MAX_FIELDLEN - 4)) + aVal = aVal.copy( 0, MAX_FIELDLEN - 4 ); + } + + //e.g. inserting bookmark around field result, so we need to put + //it around the entire writer field, as we don't have the separation + //of field and field result of word, see #i16941# + SwPosition aStart(*m_pPaM->GetPoint()); + if (!m_aFieldStack.empty()) + { + const WW8FieldEntry &rTest = m_aFieldStack.back(); + aStart = rTest.maStartPos; + } + + const OUString sOrigName = BookmarkToWriter(*pName); + m_xReffedStck->NewAttr( aStart, + SwFltBookmark( EnsureTOCBookmarkName( sOrigName ), aVal, pB->GetHandle(), IsTOCBookmarkName( sOrigName ) )); + return 0; +} + +long SwWW8ImplReader::Read_AtnBook(WW8PLCFManResult*) +{ + if (WW8PLCFx_AtnBook* pAtnBook = m_xPlcxMan->GetAtnBook()) + { + if (pAtnBook->getIsEnd()) + m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_ANNOTATIONMARK, true, pAtnBook->getHandle()); + else + m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), CntUInt16Item(RES_FLTR_ANNOTATIONMARK, pAtnBook->getHandle())); + } + return 0; +} + +long SwWW8ImplReader::Read_FactoidBook(WW8PLCFManResult*) +{ + if (WW8PLCFx_FactoidBook* pFactoidBook = m_xPlcxMan->GetFactoidBook()) + { + if (pFactoidBook->getIsEnd()) + m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_RDFMARK, true, pFactoidBook->getHandle()); + else + { + SwFltRDFMark aMark; + aMark.SetHandle(pFactoidBook->getHandle()); + GetSmartTagInfo(aMark); + m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), aMark); + } + } + return 0; +} + +// general help methods to separate parameters + +/// translate FieldParameter names into the system character set and +/// at the same time, double backslashes are converted into single ones +OUString SwWW8ImplReader::ConvertFFileName(const OUString& rOrg) +{ + OUString aName = rOrg.replaceAll("\\\\", "\\"); + aName = aName.replaceAll("%20", " "); + + // remove attached quotation marks + if (aName.endsWith("\"")) + aName = aName.copy(0, aName.getLength()-1); + + // Need the more sophisticated url converter. + if (!aName.isEmpty()) + aName = URIHelper::SmartRel2Abs( + INetURLObject(m_sBaseURL), aName, Link<OUString *, bool>(), false); + + return aName; +} + +namespace +{ + /// translate FieldParameter names into the + /// system character set and makes them uppercase + void ConvertUFName( OUString& rName ) + { + rName = GetAppCharClass().uppercase( rName ); + } +} + +static void lcl_ConvertSequenceName(OUString& rSequenceName) +{ + ConvertUFName(rSequenceName); + if ('0' <= rSequenceName[0] && '9' >= rSequenceName[0]) + rSequenceName = "_" + rSequenceName; +} + +// FindParaStart() finds 1st Parameter that follows '\' and cToken +// and returns start of this parameter or -1 +static sal_Int32 FindParaStart( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 ) +{ + bool bStr = false; // ignore inside a string + + for( sal_Int32 nBuf = 0; nBuf+1 < rStr.getLength(); nBuf++ ) + { + if( rStr[ nBuf ] == '"' ) + bStr = !bStr; + + if( !bStr + && rStr[ nBuf ] == '\\' + && ( rStr[ nBuf + 1 ] == cToken + || rStr[ nBuf + 1 ] == cToken2 ) ) + { + nBuf += 2; + // skip spaces between cToken and its parameters + while( nBuf < rStr.getLength() + && rStr[ nBuf ] == ' ' ) + nBuf++; + // return start of parameters + return nBuf < rStr.getLength() ? nBuf : -1; + } + } + return -1; +} + +// FindPara() finds the first parameter including '\' and cToken. +// A new String will be allocated (has to be deallocated by the caller) +// and everything that is part of the parameter will be returned. +static OUString FindPara( const OUString& rStr, sal_Unicode cToken, sal_Unicode cToken2 ) +{ + sal_Int32 n2; // end + sal_Int32 n = FindParaStart( rStr, cToken, cToken2 ); // start + if( n == -1) + return OUString(); + + if( rStr[ n ] == '"' + || rStr[ n ] == 132 ) + { // Quotationmark in front of parameter + n++; // Skip quotationmark + n2 = n; // search for the end starting from here + while( n2 < rStr.getLength() + && rStr[ n2 ] != 147 + && rStr[ n2 ] != '"' ) + n2++; // search end of parameter + } + else + { // no quotationmarks + n2 = n; // search for the end starting from here + while( n2 < rStr.getLength() + && rStr[ n2 ] != ' ' ) + n2++; // search end of parameter + } + return rStr.copy( n, n2-n ); +} + +static SvxNumType GetNumTypeFromName(const OUString& rStr, + bool bAllowPageDesc = false) +{ + SvxNumType eTyp = bAllowPageDesc ? SVX_NUM_PAGEDESC : SVX_NUM_ARABIC; + if( rStr.startsWithIgnoreAsciiCase( "Arabi" ) ) // Arabisch, Arabic + eTyp = SVX_NUM_ARABIC; + else if( rStr.startsWith( "misch" ) ) // r"omisch + eTyp = SVX_NUM_ROMAN_LOWER; + else if( rStr.startsWith( "MISCH" ) ) // R"OMISCH + eTyp = SVX_NUM_ROMAN_UPPER; + else if( rStr.startsWithIgnoreAsciiCase( "alphabeti" ) )// alphabetisch, alphabetic + eTyp = ( rStr[0] == 'A' ) + ? SVX_NUM_CHARS_UPPER_LETTER_N + : SVX_NUM_CHARS_LOWER_LETTER_N; + else if( rStr.startsWithIgnoreAsciiCase( "roman" ) ) // us + eTyp = ( rStr[0] == 'R' ) + ? SVX_NUM_ROMAN_UPPER + : SVX_NUM_ROMAN_LOWER; + return eTyp; +} + +static SvxNumType GetNumberPara(const OUString& rStr, bool bAllowPageDesc = false) +{ + OUString s( FindPara( rStr, '*', '*' ) ); // Type of number + SvxNumType aType = GetNumTypeFromName( s, bAllowPageDesc ); + return aType; +} + +bool SwWW8ImplReader::ForceFieldLanguage(SwField &rField, LanguageType nLang) +{ + bool bRet(false); + + const SvxLanguageItem *pLang = + static_cast<const SvxLanguageItem*>(GetFormatAttr(RES_CHRATR_LANGUAGE)); + OSL_ENSURE(pLang, "impossible"); + LanguageType nDefault = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US; + + if (nLang != nDefault) + { + rField.SetAutomaticLanguage(false); + rField.SetLanguage(nLang); + bRet = true; + } + + return bRet; +} + +static OUString GetWordDefaultDateStringAsUS(SvNumberFormatter* pFormatter, LanguageType nLang) +{ + //Get the system date in the correct final language layout, convert to + //a known language and modify the 2 digit year part to be 4 digit, and + //convert back to the correct language layout. + const sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, nLang); + + SvNumberformat aFormat = *(pFormatter->GetEntry(nIndex)); + aFormat.ConvertLanguage(*pFormatter, nLang, LANGUAGE_ENGLISH_US); + + OUString sParams(aFormat.GetFormatstring()); + // #i36594# + // Fix provided by mloiseleur@openoffice.org. + // A default date can have already 4 year digits, in some case + const sal_Int32 pos = sParams.indexOf("YYYY"); + if ( pos == -1 ) + { + sParams = sParams.replaceFirst("YY", "YYYY"); + } + return sParams; +} + +SvNumFormatType SwWW8ImplReader::GetTimeDatePara(OUString const & rStr, sal_uInt32& rFormat, + LanguageType &rLang, int nWhichDefault, bool bHijri) +{ + bool bRTL = false; + if (m_xPlcxMan && !m_bVer67) + { + SprmResult aResult = m_xPlcxMan->HasCharSprm(0x85A); + if (aResult.pSprm && aResult.nRemainingData >= 1 && *aResult.pSprm) + bRTL = true; + } + sal_uInt16 eLang = bRTL ? RES_CHRATR_CTL_LANGUAGE : RES_CHRATR_LANGUAGE; + const SvxLanguageItem *pLang = static_cast<const SvxLanguageItem*>(GetFormatAttr(eLang)); + OSL_ENSURE(pLang, "impossible"); + rLang = pLang ? pLang->GetValue() : LANGUAGE_ENGLISH_US; + + SvNumberFormatter* pFormatter = m_rDoc.GetNumberFormatter(); + OUString sParams( FindPara( rStr, '@', '@' ) );// Date/Time + if (sParams.isEmpty()) + { + bool bHasTime = false; + switch (nWhichDefault) + { + case ww::ePRINTDATE: + case ww::eSAVEDATE: + sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang); + sParams += " HH:MM:SS AM/PM"; + bHasTime = true; + break; + case ww::eCREATEDATE: + sParams += "DD/MM/YYYY HH:MM:SS"; + bHasTime = true; + break; + default: + case ww::eDATE: + sParams = GetWordDefaultDateStringAsUS(pFormatter, rLang); + break; + } + + if (bHijri) + sParams = "[~hijri]" + sParams; + + sal_Int32 nCheckPos = 0; + SvNumFormatType nType = SvNumFormatType::DEFINED; + rFormat = 0; + + OUString sTemp(sParams); + pFormatter->PutandConvertEntry(sTemp, nCheckPos, nType, rFormat, + LANGUAGE_ENGLISH_US, rLang, false); + sParams = sTemp; + + return bHasTime ? SvNumFormatType::DATETIME : SvNumFormatType::DATE; + } + + sal_uLong nFormatIdx = + sw::ms::MSDateTimeFormatToSwFormat(sParams, pFormatter, rLang, bHijri, + GetFib().m_lid); + SvNumFormatType nNumFormatType = SvNumFormatType::UNDEFINED; + if (nFormatIdx) + nNumFormatType = pFormatter->GetType(nFormatIdx); + rFormat = nFormatIdx; + + return nNumFormatType; +} + +// Fields + +// Update respective fields after loading (currently references) +void SwWW8ImplReader::UpdateFields() +{ + m_rDoc.getIDocumentState().SetUpdateExpFieldStat(true); + m_rDoc.SetInitDBFields(true); // Also update fields in the database +} + +sal_uInt16 SwWW8ImplReader::End_Field() +{ + sal_uInt16 nRet = 0; + WW8PLCFx_FLD* pF = m_xPlcxMan->GetField(); + OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available"); + WW8_CP nCP = 0; + if (!pF || !pF->EndPosIsFieldEnd(nCP)) + return nRet; + + const SvtFilterOptions &rOpt = SvtFilterOptions::Get(); + bool bUseEnhFields = rOpt.IsUseEnhancedFields(); + + OSL_ENSURE(!m_aFieldStack.empty(), "Empty field stack"); + if (!m_aFieldStack.empty()) + { + /* + only hyperlinks currently need to be handled like this, for the other + cases we have inserted a field not an attribute with an unknown end + point + */ + nRet = m_aFieldStack.back().mnFieldId; + switch (nRet) + { + case ww::eFORMTEXT: + if (bUseEnhFields && m_pPaM!=nullptr && m_pPaM->GetPoint()!=nullptr) { + SwPosition aEndPos = *m_pPaM->GetPoint(); + SwPaM aFieldPam( m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(), aEndPos.nNode, aEndPos.nContent.GetIndex()); + IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( ); + IFieldmark *pFieldmark = pMarksAccess->makeFieldBookmark( + aFieldPam, m_aFieldStack.back().GetBookmarkName(), ODF_FORMTEXT, + aFieldPam.Start() /*same pos as start!*/ ); + OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?"); + if (pFieldmark!=nullptr) { + // adapt redline positions to inserted field mark start + // dummy char (assume not necessary for end dummy char) + m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start()); + const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters(); + pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end()); + } + } + break; + // Doing corresponding status management for TOX field, index field, hyperlink field and page reference field + case ww::eTOC://TOX + case ww::eINDEX://index + if (m_bLoadingTOXCache) + { + if (m_nEmbeddedTOXLevel > 0) + { + JoinNode(*m_pPaM); + --m_nEmbeddedTOXLevel; + } + else + { + m_aTOXEndCps.insert(nCP); + m_bLoadingTOXCache = false; + if ( m_pPaM->End() && + m_pPaM->End()->nNode.GetNode().GetTextNode() && + m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() == 0 ) + { + JoinNode(*m_pPaM); + } + else + { + m_bCareLastParaEndInToc = true; + } + + if (m_pPosAfterTOC) + { + *m_pPaM = *m_pPosAfterTOC; + m_pPosAfterTOC.reset(); + } + } + } + break; + case ww::ePAGEREF: //REF + if (m_bLoadingTOXCache && !m_bLoadingTOXHyperlink) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_TXTATR_INETFMT); + } + break; + case ww::eHYPERLINK: + if (m_bLoadingTOXHyperlink) + m_bLoadingTOXHyperlink = false; + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_INETFMT); + break; + case ww::eMERGEINC: + case ww::eINCLUDETEXT: + //Move outside the section associated with this type of field + *m_pPaM->GetPoint() = m_aFieldStack.back().maStartPos; + break; + case ww::eIF: // IF-field + { + // conditional field parameters + const OUString& fieldDefinition = m_aFieldStack.back().GetBookmarkCode(); + + OUString paramCondition; + OUString paramTrue; + OUString paramFalse; + + SwHiddenTextField::ParseIfFieldDefinition(fieldDefinition, paramCondition, paramTrue, paramFalse); + + // create new field + SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText); + SwHiddenTextField aHTField( + static_cast<SwHiddenTextFieldType*>(pFieldType), + paramCondition, + paramTrue, + paramFalse, + SwFieldTypesEnum::ConditionalText); + + // insert new field into document + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aHTField)); + break; + } + default: + OUString aCode = m_aFieldStack.back().GetBookmarkCode(); + if (!aCode.isEmpty() && !aCode.trim().startsWith("SHAPE")) + { + // Unhandled field with stored code + SwPosition aEndPos = *m_pPaM->GetPoint(); + SwPaM aFieldPam( + m_aFieldStack.back().GetPtNode(), m_aFieldStack.back().GetPtContent(), + aEndPos.nNode, aEndPos.nContent.GetIndex()); + + IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( ); + + IFieldmark* pFieldmark = pMarksAccess->makeFieldBookmark( + aFieldPam, + m_aFieldStack.back().GetBookmarkName(), + ODF_UNHANDLED, + aFieldPam.Start() /*same pos as start!*/ ); + if ( pFieldmark ) + { + // adapt redline positions to inserted field mark start + // dummy char (assume not necessary for end dummy char) + m_xRedlineStack->MoveAttrsFieldmarkInserted(*aFieldPam.Start()); + const IFieldmark::parameter_map_t& rParametersToAdd = m_aFieldStack.back().getParameters(); + pFieldmark->GetParameters()->insert(rParametersToAdd.begin(), rParametersToAdd.end()); + OUString sFieldId = OUString::number( m_aFieldStack.back().mnFieldId ); + pFieldmark->GetParameters()->insert( + std::pair< OUString, uno::Any > ( + ODF_ID_PARAM, + uno::makeAny( sFieldId ) ) ); + pFieldmark->GetParameters()->insert( + std::pair< OUString, uno::Any > ( + ODF_CODE_PARAM, + uno::makeAny( aCode ) ) ); + + if ( m_aFieldStack.back().mnObjLocFc > 0 ) + { + // Store the OLE object as an internal link + OUString sOleId = "_" + + OUString::number( m_aFieldStack.back().mnObjLocFc ); + + tools::SvRef<SotStorage> xSrc0 = m_pStg->OpenSotStorage(SL::aObjectPool); + tools::SvRef<SotStorage> xSrc1 = xSrc0->OpenSotStorage( sOleId, StreamMode::READ ); + + // Store it now! + uno::Reference< embed::XStorage > xDocStg = GetDoc().GetDocStorage(); + if (xDocStg.is()) + { + uno::Reference< embed::XStorage > xOleStg = xDocStg->openStorageElement( + "OLELinks", embed::ElementModes::WRITE ); + tools::SvRef<SotStorage> xObjDst = SotStorage::OpenOLEStorage( xOleStg, sOleId ); + + if ( xObjDst.is() ) + { + xSrc1->CopyTo( xObjDst.get() ); + + if ( !xObjDst->GetError() ) + xObjDst->Commit(); + } + + uno::Reference< embed::XTransactedObject > xTransact( xOleStg, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + } + + // Store the OLE Id as a parameter + pFieldmark->GetParameters()->insert( + std::pair< OUString, uno::Any >( + ODF_OLE_PARAM, uno::makeAny( sOleId ) ) ); + } + } + } + + break; + } + m_aFieldStack.pop_back(); + } + return nRet; +} + +static bool AcceptableNestedField(sal_uInt16 nFieldCode) +{ + switch (nFieldCode) + { + case ww::eINDEX: // allow recursive field in TOC... + case ww::eTOC: // allow recursive field in TOC... + case ww::eMERGEINC: + case ww::eINCLUDETEXT: + case ww::eAUTOTEXT: + case ww::eHYPERLINK: + // Accept AutoTextList field as nested field. + // Thus, the field result is imported as plain text. + case ww::eAUTOTEXTLIST: + // tdf#129247 CONTROL contains a nested SHAPE field in the result + case ww::eCONTROL: + return true; + default: + return false; + } +} + +WW8FieldEntry::WW8FieldEntry(SwPosition const &rPos, sal_uInt16 nFieldId) throw() + : maStartPos(rPos), mnFieldId(nFieldId), mnObjLocFc(0) +{ +} + +WW8FieldEntry::WW8FieldEntry(const WW8FieldEntry &rOther) throw() + : maStartPos(rOther.maStartPos), mnFieldId(rOther.mnFieldId), mnObjLocFc(rOther.mnObjLocFc) +{ +} + +void WW8FieldEntry::Swap(WW8FieldEntry &rOther) throw() +{ + std::swap(maStartPos, rOther.maStartPos); + std::swap(mnFieldId, rOther.mnFieldId); +} + +WW8FieldEntry &WW8FieldEntry::operator=(const WW8FieldEntry &rOther) throw() +{ + WW8FieldEntry aTemp(rOther); + Swap(aTemp); + return *this; +} + + +void WW8FieldEntry::SetBookmarkName(const OUString& bookmarkName) +{ + msBookmarkName=bookmarkName; +} + +void WW8FieldEntry::SetBookmarkType(const OUString& bookmarkType) +{ + msMarkType=bookmarkType; +} + +void WW8FieldEntry::SetBookmarkCode(const OUString& bookmarkCode) +{ + msMarkCode = bookmarkCode; +} + + +// Read_Field reads a field or returns 0 if the field cannot be read, +// so that the calling function reads the field in text format. +// Returnvalue: Total length of field +long SwWW8ImplReader::Read_Field(WW8PLCFManResult* pRes) +{ + typedef eF_ResT (SwWW8ImplReader:: *FNReadField)( WW8FieldDesc*, OUString& ); + enum Limits {eMax = 96}; + static const FNReadField aWW8FieldTab[eMax+1] = + { + nullptr, + &SwWW8ImplReader::Read_F_Input, + nullptr, + &SwWW8ImplReader::Read_F_Ref, // 3 + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_Set, // 6 + nullptr, + &SwWW8ImplReader::Read_F_Tox, // 8 + nullptr, + &SwWW8ImplReader::Read_F_Styleref, // 10 + nullptr, + &SwWW8ImplReader::Read_F_Seq, // 12 + &SwWW8ImplReader::Read_F_Tox, // 13 + &SwWW8ImplReader::Read_F_DocInfo, // 14 + &SwWW8ImplReader::Read_F_DocInfo, // 15 + &SwWW8ImplReader::Read_F_DocInfo, // 16 + &SwWW8ImplReader::Read_F_Author, // 17 + &SwWW8ImplReader::Read_F_DocInfo, // 18 + &SwWW8ImplReader::Read_F_DocInfo, // 19 + &SwWW8ImplReader::Read_F_DocInfo, // 20 + &SwWW8ImplReader::Read_F_DocInfo, // 21 + &SwWW8ImplReader::Read_F_DocInfo, // 22 + &SwWW8ImplReader::Read_F_DocInfo, // 23 + &SwWW8ImplReader::Read_F_DocInfo, // 24 + &SwWW8ImplReader::Read_F_DocInfo, // 25 + &SwWW8ImplReader::Read_F_Num, // 26 + &SwWW8ImplReader::Read_F_Num, // 27 + &SwWW8ImplReader::Read_F_Num, // 28 + &SwWW8ImplReader::Read_F_FileName, // 29 + &SwWW8ImplReader::Read_F_TemplName, // 30 + &SwWW8ImplReader::Read_F_DateTime, // 31 + &SwWW8ImplReader::Read_F_DateTime, // 32 + &SwWW8ImplReader::Read_F_CurPage, // 33 + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_IncludeText, // 36 + &SwWW8ImplReader::Read_F_PgRef, // 37 + &SwWW8ImplReader::Read_F_InputVar, // 38 + &SwWW8ImplReader::Read_F_Input, // 39 + nullptr, + &SwWW8ImplReader::Read_F_DBNext, // 41 + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_DBNum, // 44 + nullptr, + nullptr, + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_Equation, // 49 + nullptr, + &SwWW8ImplReader::Read_F_Macro, // 51 + &SwWW8ImplReader::Read_F_ANumber, // 52 + &SwWW8ImplReader::Read_F_ANumber, // 53 + &SwWW8ImplReader::Read_F_ANumber, // 54 + nullptr, + + nullptr, // 56 + + &SwWW8ImplReader::Read_F_Symbol, // 57 + &SwWW8ImplReader::Read_F_Embedd, // 58 + &SwWW8ImplReader::Read_F_DBField, // 59 + nullptr, + nullptr, + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_DocInfo, // 64 - DOCVARIABLE + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_IncludePicture, // 67 + &SwWW8ImplReader::Read_F_IncludeText, // 68 + nullptr, + &SwWW8ImplReader::Read_F_FormTextBox, // 70 + &SwWW8ImplReader::Read_F_FormCheckBox, // 71 + &SwWW8ImplReader::Read_F_NoteReference, // 72 + nullptr, /*&SwWW8ImplReader::Read_F_Tox*/ + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + &SwWW8ImplReader::Read_F_FormListBox, // 83 + nullptr, // 84 + &SwWW8ImplReader::Read_F_DocInfo, // 85 + nullptr, // 86 + &SwWW8ImplReader::Read_F_OCX, // 87 + &SwWW8ImplReader::Read_F_Hyperlink, // 88 + nullptr, // 89 + nullptr, // 90 + &SwWW8ImplReader::Read_F_HTMLControl, // 91 + nullptr, // 92 + nullptr, // 93 + nullptr, // 94 + &SwWW8ImplReader::Read_F_Shape, // 95 + nullptr // eMax - Dummy empty method + }; + OSL_ENSURE( SAL_N_ELEMENTS( aWW8FieldTab ) == eMax+1, "FieldFunc table not right" ); + + WW8PLCFx_FLD* pF = m_xPlcxMan->GetField(); + OSL_ENSURE(pF, "WW8PLCFx_FLD - Pointer not available"); + + if (!pF || !pF->StartPosIsFieldStart()) + return 0; + + bool bNested = false; + if (!m_aFieldStack.empty()) + { + bNested = std::any_of(m_aFieldStack.cbegin(), m_aFieldStack.cend(), + [](const WW8FieldEntry& aField) { return !AcceptableNestedField(aField.mnFieldId); }); + } + + WW8FieldDesc aF; + bool bOk = pF->GetPara(pRes->nCp2OrIdx, aF); + + OSL_ENSURE(bOk, "WW8: Bad Field!"); + if (aF.nId == 33) aF.bCodeNest=false; // do not recurse into nested page fields + bool bCodeNest = aF.bCodeNest; + if ( aF.nId == 6 ) bCodeNest = false; // We can handle them and lose the inner data + if (aF.nId == 70) bCodeNest = false; // need to import 0x01 in FORMTEXT + + m_aFieldStack.emplace_back(*m_pPaM->GetPoint(), aF.nId); + + if (bNested) + return 0; + + sal_uInt16 n = (aF.nId <= eMax) ? aF.nId : static_cast<sal_uInt16>(eMax); + sal_uInt16 nI = n / 32; // # of sal_uInt32 + sal_uInt32 nMask = 1 << ( n % 32 ); // Mask for bits + + if (SAL_N_ELEMENTS(m_nFieldTagAlways) <= nI) + { // if indexes larger than 95 are needed, then a new configuration + // item has to be added, and nFieldTagAlways/nFieldTagBad expanded! + return aF.nLen; + } + + if( m_nFieldTagAlways[nI] & nMask ) // Flag: Tag it + return Read_F_Tag( &aF ); // Result not as text + + if( !bOk || !aF.nId ) // Field corrupted + return aF.nLen; // -> ignore + + if( aF.nId > eMax - 1) // WW: Nested Field + { + if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad + return Read_F_Tag( &aF ); // Result not as text + else + return aF.nLen; + } + + //Only one type of field (hyperlink) in drawing textboxes exists + if (aF.nId != 88 && m_xPlcxMan->GetDoingDrawTextBox()) + return aF.nLen; + + bool bHasHandler = aWW8FieldTab[aF.nId] != nullptr; + if (aF.nId == 10) // STYLEREF + { + // STYLEREF, by default these are not handled. + bHasHandler = false; + sal_uInt64 nOldPos = m_pStrm->Tell(); + OUString aStr; + aF.nLCode = m_xSBase->WW8ReadString(*m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + aF.nSCode, aF.nLCode, m_eTextCharSet); + m_pStrm->Seek(nOldPos); + + WW8ReadFieldParams aReadParam(aStr); + sal_Int32 nRet = aReadParam.SkipToNextToken(); + if (nRet == -2 && !aReadParam.GetResult().isEmpty()) + // Single numeric argument: this can be handled by SwChapterField. + bHasHandler = rtl::isAsciiDigit(aReadParam.GetResult()[0]); + + if (bHasHandler) + { + nRet = aReadParam.SkipToNextToken(); + // Handle using SwChapterField only in case there is no \[a-z] + // switch after the field argument. + bHasHandler = nRet < 0 || nRet == '*'; + } + } + + // no routine available + if (!bHasHandler || bCodeNest) + { + if( m_nFieldTagBad[nI] & nMask ) // Flag: Tag it when bad + return Read_F_Tag( &aF ); // Result not as text + // only read result + if (aF.bResNest && !AcceptableNestedField(aF.nId)) + return aF.nLen; // Result nested -> unusable + + long nOldPos = m_pStrm->Tell(); + OUString aStr; + aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+ + aF.nSCode, aF.nLCode, m_eTextCharSet ); + m_pStrm->Seek( nOldPos ); + + // field codes which contain '/' or '.' are not displayed in WinWord + // skip if it is formula field or found space before. see #i119446, #i119585. + const sal_Int32 nDotPos = aStr.indexOf('.'); + const sal_Int32 nSlashPos = aStr.indexOf('/'); + sal_Int32 nSpacePos = aStr.indexOf( ' ', 1 ); + if ( nSpacePos<0 ) + nSpacePos = aStr.getLength(); + + if ( !( aStr.getLength()>1 && aStr[1]=='=') && + (( nDotPos>=0 && nDotPos < nSpacePos ) || + ( nSlashPos>=0 && nSlashPos < nSpacePos ))) + return aF.nLen; + else + { + // Link fields aren't supported, but they are bound to an OLE object + // that needs to be roundtripped + if ( aF.nId == 56 ) + m_bEmbeddObj = true; + // Field not supported: store the field code for later use + m_aFieldStack.back().SetBookmarkCode( aStr ); + return aF.nLen - aF.nLRes - 1; // skipped too many, the resulted field will be read like main text + } + } + else + { // read field + auto nOldPos = m_pStrm->Tell(); + OUString aStr; + if ( aF.nId == 6 && aF.bCodeNest ) + { + // TODO Extract the whole code string using the nested codes + aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs() + + aF.nSCode, aF.nSRes - aF.nSCode - 1, m_eTextCharSet ); + } + else + { + aF.nLCode = m_xSBase->WW8ReadString( *m_pStrm, aStr, m_xPlcxMan->GetCpOfs()+ + aF.nSCode, aF.nLCode, m_eTextCharSet ); + } + + // #i51312# - graphics inside field code not supported by Writer. + // Thus, delete character 0x01, which stands for such a graphic. + if (aF.nId==51) //#i56768# only do it for the MACROBUTTON field, since DropListFields need the 0x01. + { + aStr = aStr.replaceAll("\x01", ""); + } + + eF_ResT eRes = (this->*aWW8FieldTab[aF.nId])( &aF, aStr ); + m_pStrm->Seek(nOldPos); + + switch ( eRes ) + { + case eF_ResT::OK: + return aF.nLen; + case eF_ResT::TEXT: + // skipped too many, the resulted field will be read like main text + // attributes can start at char 0x14 so skip one + // char more back == "-2" + if (aF.nLRes) + return aF.nLen - aF.nLRes - 2; + else + return aF.nLen; + case eF_ResT::TAGIGN: + if ( m_nFieldTagBad[nI] & nMask ) // Flag: Tag bad + return Read_F_Tag( &aF ); // Tag it + return aF.nLen; // or ignore + case eF_ResT::READ_FSPA: + return aF.nLen - aF.nLRes - 2; // position on char 1 + default: + return aF.nLen; // ignore + } + } +} + +// Tag fields + +// MakeTagString() returns the position of the first CR / end of line / page break +// in pText and converts only up to this point. +// If none of these special characters is found, the function returns 0. +void SwWW8ImplReader::MakeTagString( OUString& rStr, const OUString& rOrg ) +{ + bool bAllowCr = SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT ) + || SwFltGetFlag( m_nFieldFlags, SwFltControlStack::ALLOW_FLD_CR ); + sal_Unicode cChar; + rStr = rOrg; + + for( sal_Int32 nI = 0; + nI < rStr.getLength() && rStr.getLength() < (MAX_FIELDLEN - 4); ++nI ) + { + bool bSetAsHex = false; + cChar = rStr[ nI ]; + switch( cChar ) + { + case 132: // Exchange typographical quotation marks for normal ones + case 148: + case 147: + rStr = rStr.replaceAt( nI, 1, "\"" ); + break; + case 19: + rStr = rStr.replaceAt( nI, 1, "{" ); + break; // 19..21 to {|} + case 20: + rStr = rStr.replaceAt( nI, 1, "|" ); + break; + case 21: + rStr = rStr.replaceAt( nI, 1, "}" ); + break; + case '\\': // Tag \{|} with \ ... + case '{': + case '|': + case '}': + rStr = rStr.replaceAt( nI, 0, "\\" ); + ++nI; + break; + case 0x0b: + case 0x0c: + case 0x0d: + if( bAllowCr ) + rStr = rStr.replaceAt( nI, 1, "\n" ); + else + bSetAsHex = true; + break; + case 0xFE: + case 0xFF: + bSetAsHex = true; + break; + default: + bSetAsHex = 0x20 > cChar; + break; + } + + if( bSetAsHex ) + { + //all Hex-Numbers with \x before + OUString sTmp( "\\x" ); + if( cChar < 0x10 ) + sTmp += "0"; + sTmp += OUString::number( cChar, 16 ); + rStr = rStr.replaceAt( nI, 1 , sTmp ); + nI += sTmp.getLength() - 1; + } + } + + if( rStr.getLength() > (MAX_FIELDLEN - 4)) + rStr = rStr.copy( 0, MAX_FIELDLEN - 4 ); +} + +void SwWW8ImplReader::InsertTagField( const sal_uInt16 nId, const OUString& rTagText ) +{ + OUString aName("WwFieldTag"); + if( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_DO_ID ) ) // Number? + aName += OUString::number( nId ); // return it? + + if( SwFltGetFlag(m_nFieldFlags, SwFltControlStack::TAGS_IN_TEXT)) + { + aName += rTagText; // tag as text + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, aName, + SwInsertFlags::NOHINTEXPAND); + } + else + { // tag normally + + SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( + SwSetExpFieldType( &m_rDoc, aName, nsSwGetSetExpType::GSE_STRING ) ); + SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), rTagText ); // SUB_INVISIBLE + sal_uInt16 nSubType = ( SwFltGetFlag( m_nFieldFlags, SwFltControlStack::TAGS_VISIBLE ) ) ? 0 : nsSwExtendedSubType::SUB_INVISIBLE; + aField.SetSubType(nSubType | nsSwGetSetExpType::GSE_STRING); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + } +} + +WW8_CP SwWW8ImplReader::Read_F_Tag( WW8FieldDesc* pF ) +{ + long nOldPos = m_pStrm->Tell(); + + WW8_CP nStart = pF->nSCode - 1; // starting with 0x19 + WW8_CP nL = pF->nLen; // Total length with result and nest + if( nL > MAX_FIELDLEN ) + nL = MAX_FIELDLEN; // MaxLength, by quoting + // max. 4 times as big + OUString sFText; + m_xSBase->WW8ReadString( *m_pStrm, sFText, + m_xPlcxMan->GetCpOfs() + nStart, nL, m_eStructCharSet); + + OUString aTagText; + MakeTagString( aTagText, sFText ); + InsertTagField( pF->nId, aTagText ); + + m_pStrm->Seek( nOldPos ); + return pF->nLen; +} + +// normal fields + +eF_ResT SwWW8ImplReader::Read_F_Input( WW8FieldDesc* pF, OUString& rStr ) +{ + OUString aDef; + OUString aQ; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aQ.isEmpty() ) + aQ = aReadParam.GetResult(); + break; + case 'd': + case 'D': + if ( aReadParam.GoToTokenParam() ) + aDef = aReadParam.GetResult(); + break; + } + } + if( aDef.isEmpty() ) + aDef = GetFieldResult( pF ); + + if ( pF->nId != 0x01 ) // 0x01 fields have no result + { + SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )), + aDef, aQ, INP_TXT, 0, false ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + } + + return eF_ResT::OK; +} + +// GetFieldResult allocates a string and reads the resulted field +OUString SwWW8ImplReader::GetFieldResult( WW8FieldDesc const * pF ) +{ + long nOldPos = m_pStrm->Tell(); + + WW8_CP nStart = pF->nSRes; // result start + WW8_CP nL = pF->nLRes; // result length + if( !nL ) + return OUString(); // no result + + if( nL > MAX_FIELDLEN ) + nL = MAX_FIELDLEN; // MaxLength, by quoting + // max. 4 times as big + + OUString sRes; + m_xSBase->WW8ReadString( *m_pStrm, sRes, m_xPlcxMan->GetCpOfs() + nStart, + nL, m_eStructCharSet ); + + m_pStrm->Seek( nOldPos ); + + //replace both CR 0x0D and VT 0x0B with LF 0x0A + // at least in the cases where the result is added to an SwInputField + // there must not be control characters in it + OUStringBuffer buf(sRes.getLength()); + for (sal_Int32 i = 0; i < sRes.getLength(); ++i) + { + sal_Unicode const ch(sRes[i]); + if (!linguistic::IsControlChar(ch)) + { + buf.append(ch); + } + else + { + switch (ch) + { + case 0x0B: + case '\r': + buf.append('\n'); + break; + case '\n': + case '\t': + buf.append(ch); + break; + default: + SAL_INFO("sw.ww8", "GetFieldResult(): filtering control character"); + break; + } + } + } + return buf.makeStringAndClear(); +} + +/* +Bookmarks can be set with fields SET and ASK, and they can be referenced with +REF. When set, they behave like variables in writer, otherwise they behave +like normal bookmarks. We can check whether we should use a show variable +instead of a normal bookmark ref by converting to "show variable" at the end +of the document those refs which look for the content of a bookmark but whose +bookmarks were set with SET or ASK. (See SwWW8FltRefStack) + +The other piece of the puzzle is that refs that point to the "location" of the +bookmark will in word actually point to the last location where the bookmark +was set with SET or ASK, not the actual bookmark. This is only noticeable when +a document sets the bookmark more than once. This is because word places the +true bookmark at the location of the last set, but the refs will display the +position of the first set before the ref. + +So what we will do is + +1) keep a list of all bookmarks that were set, any bookmark names mentioned +here that are referred by content will be converted to show variables. + +2) create pseudo bookmarks for every position that a bookmark is set with SET +or ASK but has no existing bookmark. We can then keep a map from the original +bookmark name to the new one. As we parse the document new pseudo names will +replace the older ones, so the map always contains the bookmark of the +location that msword itself would use. + +3) word's bookmarks are case insensitive, writers are not. So we need to +map case different versions together, regardless of whether they are +variables or not. + +4) when a reference is (first) SET or ASK, the bookmark associated with it +is placed around the 0x14 0x15 result part of the field. We will fiddle +the placement to be the writer equivalent of directly before and after +the field, which gives the same effect and meaning, to do so we must +get any bookmarks in the field range, and begin them immediately before +the set/ask field, and end them directly afterwards. MapBookmarkVariables +returns an identifier of the bookmark attribute to close after inserting +the appropriate set/ask field. +*/ +long SwWW8ImplReader::MapBookmarkVariables(const WW8FieldDesc* pF, + OUString &rOrigName, const OUString &rData) +{ + OSL_ENSURE(m_xPlcxMan.get(), "No pPlcxMan"); + long nNo; + /* + If there was no bookmark associated with this set field, then we create a + pseudo one and insert it in the document. + */ + sal_uInt16 nIndex; + m_xPlcxMan->GetBook()->MapName(rOrigName); + OUString sName = m_xPlcxMan->GetBook()->GetBookmark( + pF->nSCode, pF->nSCode + pF->nLen, nIndex); + if (!sName.isEmpty()) + { + m_xPlcxMan->GetBook()->SetStatus(nIndex, BOOK_IGNORE); + nNo = nIndex; + } + else + { + nNo = m_xReffingStck->aFieldVarNames.size()+1; + sName = "WWSetBkmk" + OUString::number(nNo); + nNo += m_xPlcxMan->GetBook()->GetIMax(); + } + m_xReffedStck->NewAttr(*m_pPaM->GetPoint(), + SwFltBookmark( BookmarkToWriter(sName), rData, nNo )); + m_xReffingStck->aFieldVarNames[rOrigName] = sName; + return nNo; +} + +/* +Word can set a bookmark with set or with ask, such a bookmark is equivalent to +our variables, but until the end of a document we cannot be sure if a bookmark +is a variable or not, at the end we will have a list of reference names which +were set or asked, all bookmarks using the content of those bookmarks are +converted to show variables, those that reference the position of the field +can be left as references, because a bookmark is also inserted at the position +of a set or ask field, either by word, or in some special cases by the import +filter itself. +*/ +SwFltStackEntry *SwWW8FltRefStack::RefToVar(const SwField* pField, + SwFltStackEntry &rEntry) +{ + SwFltStackEntry *pRet=nullptr; + if (pField && SwFieldIds::GetRef == pField->Which()) + { + //Get the name of the ref field, and see if actually a variable + const OUString sName = pField->GetPar1(); + std::map<OUString, OUString, SwWW8::ltstr>::const_iterator + aResult = aFieldVarNames.find(sName); + + if (aResult != aFieldVarNames.end()) + { + SwGetExpField aField( static_cast<SwGetExpFieldType*>( + pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp)), sName, nsSwGetSetExpType::GSE_STRING, 0); + SwFormatField aTmp(aField); + rEntry.pAttr.reset( aTmp.Clone() ); + pRet = &rEntry; + } + } + return pRet; +} + +OUString SwWW8ImplReader::GetMappedBookmark(const OUString &rOrigName) +{ + OUString sName(BookmarkToWriter(rOrigName)); + OSL_ENSURE(m_xPlcxMan.get(), "no pPlcxMan"); + m_xPlcxMan->GetBook()->MapName(sName); + + //See if there has been a variable set with this name, if so get + //the pseudo bookmark name that was set with it. + std::map<OUString, OUString, SwWW8::ltstr>::const_iterator aResult = + m_xReffingStck->aFieldVarNames.find(sName); + + return (aResult == m_xReffingStck->aFieldVarNames.end()) + ? sName : (*aResult).second; +} + +// "ASK" +eF_ResT SwWW8ImplReader::Read_F_InputVar( WW8FieldDesc* pF, OUString& rStr ) +{ + OUString sOrigName, aQ; + OUString aDef; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if (sOrigName.isEmpty()) + sOrigName = aReadParam.GetResult(); + else if (aQ.isEmpty()) + aQ = aReadParam.GetResult(); + break; + case 'd': + case 'D': + if ( aReadParam.GoToTokenParam() ) + aDef = aReadParam.GetResult(); + break; + } + } + + if (sOrigName.isEmpty()) + return eF_ResT::TAGIGN; // does not make sense without textmark + + const OUString aResult(GetFieldResult(pF)); + + //#i24377#, munge Default Text into title as we have only one slot + //available for aResult and aDef otherwise + if (!aDef.isEmpty()) + { + if (!aQ.isEmpty()) + aQ += " - "; + aQ += aDef; + } + + const long nNo = MapBookmarkVariables(pF, sOrigName, aResult); + + SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType( + SwSetExpFieldType(&m_rDoc, sOrigName, nsSwGetSetExpType::GSE_STRING))); + SwSetExpField aField(pFT, aResult); + aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING); + aField.SetInputFlag(true); + aField.SetPromptText( aQ ); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + + m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo); + return eF_ResT::OK; +} + +// "AUTONR" +eF_ResT SwWW8ImplReader::Read_F_ANumber( WW8FieldDesc*, OUString& rStr ) +{ + if( !m_pNumFieldType ){ // 1st time + SwSetExpFieldType aT( &m_rDoc, "AutoNr", nsSwGetSetExpType::GSE_SEQ ); + m_pNumFieldType = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aT ); + } + SwSetExpField aField( static_cast<SwSetExpFieldType*>(m_pNumFieldType), OUString(), + GetNumberPara( rStr ) ); + aField.SetValue( ++m_nFieldNum, nullptr ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + return eF_ResT::OK; +} + +// "SEQ" +eF_ResT SwWW8ImplReader::Read_F_Seq( WW8FieldDesc*, OUString& rStr ) +{ + OUString aSequenceName; + OUString aBook; + bool bHidden = false; + bool bFormat = false; + bool bCountOn = true; + OUString sStart; + SvxNumType eNumFormat = SVX_NUM_ARABIC; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aSequenceName.isEmpty() ) + aSequenceName = aReadParam.GetResult(); + else if( aBook.isEmpty() ) + aBook = aReadParam.GetResult(); + break; + + case 'h': + if( !bFormat ) + bHidden = true; // activate hidden flag + break; + + case '*': + bFormat = true; // activate format flag + if ( aReadParam.SkipToNextToken()!=-2 ) + break; + if ( aReadParam.GetResult()!="MERGEFORMAT" && aReadParam.GetResult()!="CHARFORMAT" ) + eNumFormat = GetNumTypeFromName( aReadParam.GetResult() ); + break; + + case 'r': + bCountOn = false; + if ( aReadParam.SkipToNextToken()==-2 ) + sStart = aReadParam.GetResult(); + break; + + case 'c': + bCountOn = false; + break; + + case 'n': + bCountOn = true; // Increase value by one (default) + break; + + case 's': // Outline Level + //#i19682, what I have to do with this value? + break; + } + } + if (aSequenceName.isEmpty() && aBook.isEmpty()) + return eF_ResT::TAGIGN; + + SwSetExpFieldType* pFT = static_cast<SwSetExpFieldType*>(m_rDoc.getIDocumentFieldsAccess().InsertFieldType( + SwSetExpFieldType( &m_rDoc, aSequenceName, nsSwGetSetExpType::GSE_SEQ ) ) ); + SwSetExpField aField( pFT, OUString(), eNumFormat ); + + //#i120654# Add bHidden for /h flag (/h: Hide the field result.) + if (bHidden) + aField.SetSubType(aField.GetSubType() | nsSwExtendedSubType::SUB_INVISIBLE); + + if (!sStart.isEmpty()) + aField.SetFormula( aSequenceName + "=" + sStart ); + else if (!bCountOn) + aField.SetFormula(aSequenceName); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_Styleref(WW8FieldDesc*, OUString& rString) +{ + WW8ReadFieldParams aReadParam(rString); + sal_Int32 nRet = aReadParam.SkipToNextToken(); + if (nRet != -2) + // \param was found, not normal text. + return eF_ResT::TAGIGN; + + OUString aResult = aReadParam.GetResult(); + sal_Int32 nResult = aResult.toInt32(); + if (nResult < 1) + return eF_ResT::TAGIGN; + + SwFieldType* pFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter); + SwChapterField aField(static_cast<SwChapterFieldType*>(pFieldType), CF_TITLE); + aField.SetLevel(nResult - 1); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_DocInfo( WW8FieldDesc* pF, OUString& rStr ) +{ + sal_uInt16 nSub=0; + // RegInfoFormat, DefaultFormat for DocInfoFields + sal_uInt16 nReg = DI_SUB_AUTHOR; + bool bDateTime = false; + + if( 85 == pF->nId ) + { + OUString aDocProperty; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aDocProperty.isEmpty() ) + aDocProperty = aReadParam.GetResult(); + break; + case '*': + //Skip over MERGEFORMAT + (void)aReadParam.SkipToNextToken(); + break; + } + } + + aDocProperty = aDocProperty.replaceAll("\"", ""); + + /* + There are up to 26 fields that may be meant by 'DocumentProperty'. + Which of them is to be inserted here ? + This Problem can only be solved by implementing a name matching + method that compares the given Parameter String with the four + possible name sets (english, german, french, spanish) + */ + + static const char* aName10 = "\x0F"; // SW field code + static const char* aName11 // German + = "TITEL"; + static const char* aName12 // French + = "TITRE"; + static const char* aName13 // English + = "TITLE"; + static const char* aName14 // Spanish + = "TITRO"; + static const char* aName20 = "\x15"; // SW field code + static const char* aName21 // German + = "ERSTELLDATUM"; + static const char* aName22 // French + = "CR\xC9\xC9"; + static const char* aName23 // English + = "CREATED"; + static const char* aName24 // Spanish + = "CREADO"; + static const char* aName30 = "\x16"; // SW field code + static const char* aName31 // German + = "ZULETZTGESPEICHERTZEIT"; + static const char* aName32 // French + = "DERNIERENREGISTREMENT"; + static const char* aName33 // English + = "SAVED"; + static const char* aName34 // Spanish + = "MODIFICADO"; + static const char* aName40 = "\x17"; // SW field code + static const char* aName41 // German + = "ZULETZTGEDRUCKT"; + static const char* aName42 // French + = "DERNI\xC8" "REIMPRESSION"; + static const char* aName43 // English + = "LASTPRINTED"; + static const char* aName44 // Spanish + = "HUPS PUPS"; + static const char* aName50 = "\x18"; // SW field code + static const char* aName51 // German + = "\xDC" "BERARBEITUNGSNUMMER"; + static const char* aName52 // French + = "NUM\xC9" "RODEREVISION"; + static const char* aName53 // English + = "REVISIONNUMBER"; + static const char* aName54 // Spanish + = "SNUBBEL BUBBEL"; + static const sal_uInt16 nFieldCnt = 5; + + // additional fields are to be coded soon! + + static const sal_uInt16 nLangCnt = 4; + static const char *aNameSet_26[nFieldCnt][nLangCnt+1] = + { + {aName10, aName11, aName12, aName13, aName14}, + {aName20, aName21, aName22, aName23, aName24}, + {aName30, aName31, aName32, aName33, aName34}, + {aName40, aName41, aName42, aName43, aName44}, + {aName50, aName51, aName52, aName53, aName54} + }; + + bool bFieldFound= false; + sal_uInt16 nFIdx; + for(sal_uInt16 nLIdx=1; !bFieldFound && (nLangCnt > nLIdx); ++nLIdx) + { + for(nFIdx = 0; !bFieldFound && (nFieldCnt > nFIdx); ++nFIdx) + { + if( aDocProperty == OUString( aNameSet_26[nFIdx][nLIdx], strlen(aNameSet_26[nFIdx][nLIdx]), + RTL_TEXTENCODING_MS_1252 ) ) + { + bFieldFound = true; + pF->nId = aNameSet_26[nFIdx][0][0]; + } + } + } + + if( !bFieldFound ) + { + SwDocInfoField aField( static_cast<SwDocInfoFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )), DI_CUSTOM|nReg, aDocProperty, GetFieldResult( pF ) ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + + return eF_ResT::OK; + } + } + + switch( pF->nId ) + { + case 14: + /* supports all INFO variables! */ + nSub = DI_KEYS; + break; + case 15: + nSub = DI_TITLE; + break; + case 16: + nSub = DI_SUBJECT; + break; + case 18: + nSub = DI_KEYS; + break; + case 19: + nSub = DI_COMMENT; + break; + case 20: + nSub = DI_CHANGE; + nReg = DI_SUB_AUTHOR; + break; + case 21: + nSub = DI_CREATE; + nReg = DI_SUB_DATE; + bDateTime = true; + break; + case 23: + nSub = DI_PRINT; + nReg = DI_SUB_DATE; + bDateTime = true; + break; + case 24: + nSub = DI_DOCNO; + break; + case 22: + nSub = DI_CHANGE; + nReg = DI_SUB_DATE; + bDateTime = true; + break; + case 25: + nSub = DI_CHANGE; + nReg = DI_SUB_TIME; + bDateTime = true; + break; + case 64: // DOCVARIABLE + nSub = DI_CUSTOM; + break; + } + + sal_uInt32 nFormat = 0; + + LanguageType nLang(LANGUAGE_SYSTEM); + if (bDateTime) + { + SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, pF->nId); + switch (nDT) + { + case SvNumFormatType::DATE: + nReg = DI_SUB_DATE; + break; + case SvNumFormatType::TIME: + nReg = DI_SUB_TIME; + break; + case SvNumFormatType::DATETIME: + nReg = DI_SUB_DATE; + break; + default: + nReg = DI_SUB_DATE; + break; + } + } + + OUString aData; + // Extract DOCVARIABLE varname + if ( 64 == pF->nId ) + { + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1) + break; + switch( nRet ) + { + case -2: + if( aData.isEmpty() ) + aData = aReadParam.GetResult(); + break; + case '*': + //Skip over MERGEFORMAT + (void)aReadParam.SkipToNextToken(); + break; + } + } + + aData = aData.replaceAll("\"", ""); + } + + SwDocInfoField aField( static_cast<SwDocInfoFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )), nSub|nReg, aData, nFormat ); + if (bDateTime) + ForceFieldLanguage(aField, nLang); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_Author( WW8FieldDesc*, OUString& ) +{ + // SH: The SwAuthorField refers not to the original author but to the current user, better use DocInfo + SwDocInfoField aField( static_cast<SwDocInfoFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )), + DI_CREATE|DI_SUB_AUTHOR, OUString() ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_TemplName( WW8FieldDesc*, OUString& ) +{ + SwTemplNameField aField( static_cast<SwTemplNameFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )), FF_NAME ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + return eF_ResT::OK; +} + +// Both the date and the time fields can be used for showing a date a time or both. +eF_ResT SwWW8ImplReader::Read_F_DateTime( WW8FieldDesc*pF, OUString& rStr ) +{ + bool bHijri = false; + WW8ReadFieldParams aReadParam(rStr); + for (;;) + { + const sal_Int32 nTok = aReadParam.SkipToNextToken(); + if ( nTok==-1 ) + break; + switch (nTok) + { + default: + case 'l': + case -2: + break; + case 'h': + bHijri = true; + break; + case 's': + //Saka Calendar, should we do something with this ? + break; + } + } + + sal_uInt32 nFormat = 0; + + LanguageType nLang(LANGUAGE_SYSTEM); + SvNumFormatType nDT = GetTimeDatePara(rStr, nFormat, nLang, ww::eDATE, bHijri); + + if( SvNumFormatType::UNDEFINED == nDT ) // no D/T-Formatstring + { + if (32 == pF->nId) + { + nDT = SvNumFormatType::TIME; + nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex( + NF_TIME_START, LANGUAGE_SYSTEM ); + } + else + { + nDT = SvNumFormatType::DATE; + nFormat = m_rDoc.GetNumberFormatter()->GetFormatIndex( + NF_DATE_START, LANGUAGE_SYSTEM ); + } + } + + if (nDT & SvNumFormatType::DATE) + { + SwDateTimeField aField(static_cast<SwDateTimeFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime )), DATEFLD, nFormat); + ForceFieldLanguage(aField, nLang); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + } + else if (nDT == SvNumFormatType::TIME) + { + SwDateTimeField aField(static_cast<SwDateTimeFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime)), TIMEFLD, nFormat); + ForceFieldLanguage(aField, nLang); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + } + + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_FileName(WW8FieldDesc*, OUString &rStr) +{ + SwFileNameFormat eType = FF_NAME; + WW8ReadFieldParams aReadParam(rStr); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch (nRet) + { + case 'p': + eType = FF_PATHNAME; + break; + case '*': + //Skip over MERGEFORMAT + (void)aReadParam.SkipToNextToken(); + break; + default: + OSL_ENSURE(false, "unknown option in FileName field"); + break; + } + } + + SwFileNameField aField( + static_cast<SwFileNameFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename)), eType); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_Num( WW8FieldDesc* pF, OUString& rStr ) +{ + sal_uInt16 nSub = DS_PAGE; // page number + switch ( pF->nId ){ + case 27: nSub = DS_WORD; break; // number of words + case 28: nSub = DS_CHAR; break; // number of characters + } + SwDocStatField aField( static_cast<SwDocStatFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocStat )), nSub, + GetNumberPara( rStr ) ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_CurPage( WW8FieldDesc*, OUString& rStr ) +{ + // page number + SwPageNumberField aField( static_cast<SwPageNumberFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )), PG_RANDOM, + GetNumberPara(rStr, true)); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + return eF_ResT::OK; +} + +eF_ResT SwWW8ImplReader::Read_F_Symbol( WW8FieldDesc*, OUString& rStr ) +{ + //e.g. #i20118# + OUString aQ; + OUString aName; + sal_Int32 nSize = 0; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aQ.isEmpty() ) + aQ = aReadParam.GetResult(); + break; + case 'f': + case 'F': + if ( aReadParam.GoToTokenParam() ) + aName = aReadParam.GetResult(); + break; + case 's': + case 'S': + if ( aReadParam.GoToTokenParam() ) + { + const OUString aSiz = aReadParam.GetResult(); + if (!aSiz.isEmpty()) + { + bool bFail = o3tl::checked_multiply<sal_Int32>(aSiz.toInt32(), 20, nSize); // pT -> twip + if (bFail) + nSize = -1; + } + } + break; + } + } + if( aQ.isEmpty() ) + return eF_ResT::TAGIGN; // -> no 0-char in text + + sal_Unicode const cChar = static_cast<sal_Unicode>(aQ.toInt32()); + if (!linguistic::IsControlChar(cChar) || cChar == '\r' || cChar == '\n' || cChar == '\t') + { + if (!aName.isEmpty()) // Font Name set ? + { + SvxFontItem aFont(FAMILY_DONTKNOW, aName, OUString(), + PITCH_DONTKNOW, RTL_TEXTENCODING_SYMBOL, RES_CHRATR_FONT); + NewAttr(aFont); // new Font + } + + if (nSize > 0) //#i20118# + { + SvxFontHeightItem aSz(nSize, 100, RES_CHRATR_FONTSIZE); + NewAttr(aSz); + } + + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, OUString(cChar)); + + if (nSize > 0) + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONTSIZE); + if (!aName.isEmpty()) + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_FONT); + } + else + { + m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, "###"); + } + + return eF_ResT::OK; +} + +// "EMBED" +eF_ResT SwWW8ImplReader::Read_F_Embedd( WW8FieldDesc*, OUString& rStr ) +{ + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + // sHost + break; + + case 's': + // use ObjectSize + break; + } + } + + if( m_bObj && m_nPicLocFc ) + m_nObjLocFc = m_nPicLocFc; + m_bEmbeddObj = true; + return eF_ResT::TEXT; +} + +// "SET" +eF_ResT SwWW8ImplReader::Read_F_Set( WW8FieldDesc* pF, OUString& rStr ) +{ + OUString sOrigName; + OUString sVal; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if (sOrigName.isEmpty()) + sOrigName = aReadParam.GetResult(); + else if (sVal.isEmpty()) + sVal = aReadParam.GetResult(); + break; + } + } + + const long nNo = MapBookmarkVariables(pF, sOrigName, sVal); + + SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( SwSetExpFieldType( &m_rDoc, sOrigName, + nsSwGetSetExpType::GSE_STRING ) ); + SwSetExpField aField( static_cast<SwSetExpFieldType*>(pFT), sVal, ULONG_MAX ); + aField.SetSubType(nsSwExtendedSubType::SUB_INVISIBLE | nsSwGetSetExpType::GSE_STRING); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + + m_xReffedStck->SetAttr(*m_pPaM->GetPoint(), RES_FLTR_BOOKMARK, true, nNo); + + return eF_ResT::OK; +} + +// "REF" +eF_ResT SwWW8ImplReader::Read_F_Ref( WW8FieldDesc*, OUString& rStr ) +{ // Reference - Field + OUString sOrigBkmName; + REFERENCEMARK eFormat = REF_CONTENT; + + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( sOrigBkmName.isEmpty() ) // get name of bookmark + sOrigBkmName = aReadParam.GetResult(); + break; + + /* References to numbers in Word could be either to a numbered + paragraph or to a chapter number. However Word does not seem to + have the capability we do, of referring to the chapter number some + other bookmark is in. As a result, cross-references to chapter + numbers in a word document will be cross-references to a numbered + paragraph, being the chapter heading paragraph. As it happens, our + cross-references to numbered paragraphs will do the right thing + when the target is a numbered chapter heading, so there is no need + for us to use the REF_CHAPTER bookmark format on import. + */ + case 'n': + eFormat = REF_NUMBER_NO_CONTEXT; + break; + case 'r': + eFormat = REF_NUMBER; + break; + case 'w': + eFormat = REF_NUMBER_FULL_CONTEXT; + break; + + case 'p': + eFormat = REF_UPDOWN; + break; + case 'h': + break; + default: + // unimplemented switch: just do 'nix nought nothing' :-) + break; + } + } + + OUString sBkmName(GetMappedBookmark(sOrigBkmName)); + + // #i120879# add cross reference bookmark name prefix, if it + // matches internal TOC bookmark naming convention + if ( IsTOCBookmarkName( sBkmName ) ) + { + sBkmName = EnsureTOCBookmarkName(sBkmName); + // track <sBookmarkName> as referenced TOC bookmark. + m_xReffedStck->aReferencedTOCBookmarks.insert( sBkmName ); + } + + SwGetRefField aField( + static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), + sBkmName,"",REF_BOOKMARK,0,eFormat); + + if (eFormat == REF_CONTENT) + { + /* + If we are just inserting the contents of the bookmark, then it + is possible that the bookmark is actually a variable, so we + must store it until the end of the document to see if it was, + in which case we'll turn it into a show variable + */ + m_xReffingStck->NewAttr( *m_pPaM->GetPoint(), SwFormatField(aField) ); + m_xReffingStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_FIELD); + } + else + { + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + } + return eF_ResT::OK; +} + +// Note Reference - Field +eF_ResT SwWW8ImplReader::Read_F_NoteReference( WW8FieldDesc*, OUString& rStr ) +{ + OUString aBkmName; + bool bAboveBelow = false; + + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aBkmName.isEmpty() ) // get name of foot/endnote + aBkmName = aReadParam.GetResult(); + break; + case 'r': + // activate flag 'Chapter Number' + break; + case 'p': + bAboveBelow = true; + break; + case 'h': + break; + default: + // unimplemented switch: just do 'nix nought nothing' :-) + break; + } + } + + // set Sequence No of corresponding Foot-/Endnote to Zero + // (will be corrected in + SwGetRefField aField( static_cast<SwGetRefFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), aBkmName, "", REF_FOOTNOTE, 0, + REF_ONLYNUMBER ); + m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField)); + m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD); + if (bAboveBelow) + { + SwGetRefField aField2( static_cast<SwGetRefFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )),aBkmName, "", REF_FOOTNOTE, 0, + REF_UPDOWN ); + m_xReffingStck->NewAttr(*m_pPaM->GetPoint(), SwFormatField(aField2)); + m_xReffingStck->SetAttr(*m_pPaM->GetPoint(), RES_TXTATR_FIELD); + } + return eF_ResT::OK; +} + +// "PAGEREF" +eF_ResT SwWW8ImplReader::Read_F_PgRef( WW8FieldDesc*, OUString& rStr ) +{ + OUString sOrigName; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + else if ( nRet == -2 && sOrigName.isEmpty() ) + { + sOrigName = aReadParam.GetResult(); + } + } + + const OUString sName(GetMappedBookmark(sOrigName)); + + // loading page reference field in TOX + if (m_bLoadingTOXCache) + { + // insert page ref representation as plain text --> return FLD_TEXT + // if there is no hyperlink settings for current toc and referenced bookmark is available, + // assign link to current ref area + if (!m_bLoadingTOXHyperlink && !sName.isEmpty()) + { + // #i120879# add cross reference bookmark name prefix, if it + // matches internal TOC bookmark naming convention + OUString sBookmarkName; + if ( IsTOCBookmarkName( sName ) ) + { + sBookmarkName = EnsureTOCBookmarkName(sName); + // track <sBookmarkName> as referenced TOC bookmark. + m_xReffedStck->aReferencedTOCBookmarks.insert( sBookmarkName ); + } + else + { + sBookmarkName = sName; + } + OUString sURL = "#" + sBookmarkName; + const OUString sTarget; + SwFormatINetFormat aURL( sURL, sTarget ); + const OUString sLinkStyle("Index Link"); + const sal_uInt16 nPoolId = + SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt ); + aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId); + aURL.SetINetFormatAndId( sLinkStyle, nPoolId ); + m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL ); + } + return eF_ResT::TEXT; + } + + // #i120879# add cross reference bookmark name prefix, if it matches + // internal TOC bookmark naming convention + OUString sPageRefBookmarkName; + if ( IsTOCBookmarkName( sName ) ) + { + sPageRefBookmarkName = EnsureTOCBookmarkName(sName); + // track <sPageRefBookmarkName> as referenced TOC bookmark. + m_xReffedStck->aReferencedTOCBookmarks.insert( sPageRefBookmarkName ); + } + else + { + sPageRefBookmarkName = sName; + } + SwGetRefField aField( static_cast<SwGetRefFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef )), + sPageRefBookmarkName, "", REF_BOOKMARK, 0, REF_PAGE ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + + return eF_ResT::OK; +} + +//helper function +//For MS MacroButton field, the symbol in plain text is always "(" (0x28), +//which should be mapped according to the macro type +static bool ConvertMacroSymbol( const OUString& rName, OUString& rReference ) +{ + bool bConverted = false; + if( rReference == "(" ) + { + bConverted = true; + sal_Unicode cSymbol = sal_Unicode(); // silence false warning + if (rName == "CheckIt") + cSymbol = 0xF06F; + else if (rName == "UncheckIt") + cSymbol = 0xF0FE; + else if (rName == "ShowExample") + cSymbol = 0xF02A; + //else if... : todo + else + bConverted = false; + + if( bConverted ) + rReference = OUString(cSymbol); + } + return bConverted; +} + +// "MACROBUTTON" +eF_ResT SwWW8ImplReader::Read_F_Macro( WW8FieldDesc*, OUString& rStr) +{ + OUString aName; + OUString aVText; + bool bNewVText = true; + bool bBracket = false; + WW8ReadFieldParams aReadParam( rStr ); + + sal_Int32 nOffset = 0; + + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aName.isEmpty() ) + aName = aReadParam.GetResult(); + else if( aVText.isEmpty() || bBracket ) + { + nOffset = aReadParam.GetTokenSttPtr() + 1; + + if( bBracket ) + aVText += " "; + aVText += aReadParam.GetResult(); + if (bNewVText) + { + bBracket = (aVText[0] == '['); + bNewVText = false; + } + else if( aVText.endsWith("]") ) + bBracket = false; + } + break; + } + } + if( aName.isEmpty() ) + return eF_ResT::TAGIGN; // makes no sense without Macro-Name + + NotifyMacroEventRead(); + + //try converting macro symbol according to macro name + bool bApplyWingdings = ConvertMacroSymbol( aName, aVText ); + aName = "StarOffice.Standard.Modul1." + aName; + + SwMacroField aField( static_cast<SwMacroFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Macro )), aName, aVText ); + + if( !bApplyWingdings ) + { + + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + WW8_CP nOldCp = m_xPlcxMan->Where(); + WW8_CP nCp = nOldCp + nOffset; + + SwPaM aPaM(*m_pPaM, m_pPaM); + aPaM.SetMark(); + aPaM.Move(fnMoveBackward); + aPaM.Exchange(); + + m_pPostProcessAttrsInfo.reset(new WW8PostProcessAttrsInfo(nCp, nCp, aPaM)); + } + else + { + //set Wingdings font + sal_uInt16 i = 0; + for ( ; i < m_xFonts->GetMax(); i++ ) + { + FontFamily eFamily; + OUString aFontName; + FontPitch ePitch; + rtl_TextEncoding eSrcCharSet; + if( GetFontParams( i, eFamily, aFontName, ePitch, eSrcCharSet ) + && aFontName=="Wingdings" ) + { + break; + } + } + + if ( i < m_xFonts->GetMax() ) + { + + SetNewFontAttr( i, true, RES_CHRATR_FONT ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT ); + ResetCharSetVars(); + } + } + + return eF_ResT::OK; +} + +WW8PostProcessAttrsInfo::WW8PostProcessAttrsInfo(WW8_CP nCpStart, WW8_CP nCpEnd, + SwPaM & rPaM) + : mbCopy(false) + , mnCpStart(nCpStart) + , mnCpEnd(nCpEnd) + , mPaM(*rPaM.GetMark(), *rPaM.GetPoint()) + , mItemSet(rPaM.GetDoc()->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_END - 1>{}) +{ +} + +bool CanUseRemoteLink(const OUString &rGrfName) +{ + bool bUseRemote = false; + try + { + // Related: tdf#102499, add a default css::ucb::XCommandEnvironment + // in order to have https protocol manage certificates correctly + uno::Reference< task::XInteractionHandler > xIH( + task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr)); + + uno::Reference< ucb::XProgressHandler > xProgress; + ::ucbhelper::CommandEnvironment* pCommandEnv = + new ::ucbhelper::CommandEnvironment(new comphelper::SimpleFileAccessInteraction( xIH ), xProgress); + + ::ucbhelper::Content aCnt(rGrfName, + static_cast< ucb::XCommandEnvironment* >(pCommandEnv), + comphelper::getProcessComponentContext()); + + if ( !INetURLObject( rGrfName ).isAnyKnownWebDAVScheme() ) + { + OUString aTitle; + aCnt.getPropertyValue("Title") >>= aTitle; + bUseRemote = !aTitle.isEmpty(); + } + else + { + // is a link to a WebDAV resource + // need to use MediaType to check for link usability + OUString aMediaType; + aCnt.getPropertyValue("MediaType") >>= aMediaType; + bUseRemote = !aMediaType.isEmpty(); + } + } + catch ( ... ) + { + // this file did not exist, so we will not set this as graphiclink + bUseRemote = false; + } + return bUseRemote; +} + +// "INCLUDEPICTURE" +eF_ResT SwWW8ImplReader::Read_F_IncludePicture( WW8FieldDesc*, OUString& rStr ) +{ + OUString aGrfName; + bool bEmbedded = true; + + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if (aGrfName.isEmpty()) + aGrfName = ConvertFFileName(aReadParam.GetResult()); + break; + + case 'd': + bEmbedded = false; + break; + + case 'c':// skip the converter name + aReadParam.FindNextStringPiece(); + break; + } + } + + if (!bEmbedded) + bEmbedded = !CanUseRemoteLink(aGrfName); + + if (!bEmbedded) + { + /* + Special case: + + Now we write the Link into the Doc and remember the SwFlyFrameFormat. + Since we end on return FLD_READ_FSPA below, the skip value will be set + so that Char-1 will still be read. + When we then call SwWW8ImplReader::ImportGraf() it will then recognize + that we have inserted a graphic link and the suiting SwAttrSet will be + inserted into the frame format. + */ + SfxItemSet aFlySet( m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END-1>{} ); + aFlySet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) ); + aFlySet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME )); + m_pFlyFormatOfJustInsertedGraphic = + m_rDoc.getIDocumentContentOperations().InsertGraphic(*m_pPaM, + aGrfName, + OUString(), + nullptr, // Graphic* + &aFlySet, + nullptr, nullptr); // SwFrameFormat* + m_aGrfNameGenerator.SetUniqueGraphName(m_pFlyFormatOfJustInsertedGraphic, + INetURLObject(aGrfName).GetBase()); + } + return eF_ResT::READ_FSPA; +} + +OUString wwSectionNamer::UniqueName() +{ + const OUString aName(msFileLinkSeed + OUString::number(++mnFileSectionNo)); + return mrDoc.GetUniqueSectionName(&aName); +} + +// "INCLUDETEXT" +eF_ResT SwWW8ImplReader::Read_F_IncludeText( WW8FieldDesc* /*pF*/, OUString& rStr ) +{ + OUString aPara; + OUString aBook; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aPara.isEmpty() ) + aPara = aReadParam.GetResult(); + else if( aBook.isEmpty() ) + aBook = aReadParam.GetResult(); + break; + case '*': + //Skip over MERGEFORMAT + (void)aReadParam.SkipToNextToken(); + break; + } + } + aPara = ConvertFFileName(aPara); + + if (!aBook.isEmpty() && aBook[ 0 ] != '\\') + { + // Section from Source (no switch)? + ConvertUFName(aBook); + aPara += OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + aBook; + } + + /* + ##509## + What we will do is insert a section to be linked to a file, but just in + case the file is not available we will fill in the section with the stored + content of this winword field as a fallback. + */ + SwPosition aTmpPos(*m_pPaM->GetPoint()); + + SwSectionData aSection(SectionType::FileLink, + m_aSectionNameGenerator.UniqueName()); + aSection.SetLinkFileName( aPara ); + aSection.SetProtectFlag(true); + + SwSection *const pSection = + m_rDoc.InsertSwSection(*m_pPaM, aSection, nullptr, nullptr, false); + OSL_ENSURE(pSection, "no section inserted"); + if (!pSection) + return eF_ResT::TEXT; + const SwSectionNode* pSectionNode = pSection->GetFormat()->GetSectionNode(); + OSL_ENSURE(pSectionNode, "no section node!"); + if (!pSectionNode) + return eF_ResT::TEXT; + + m_pPaM->GetPoint()->nNode = pSectionNode->GetIndex()+1; + m_pPaM->GetPoint()->nContent.Assign(m_pPaM->GetContentNode(), 0 ); + + //we have inserted a section before this point, so adjust pos + //for future page/section segment insertion + m_aSectionManager.PrependedInlineNode(aTmpPos, m_pPaM->GetNode()); + + return eF_ResT::TEXT; +} + +// "SERIALPRINT" +eF_ResT SwWW8ImplReader::Read_F_DBField( WW8FieldDesc* pF, OUString& rStr ) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) pF; + (void) rStr; +#else + OUString aName; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( aName.isEmpty() ) + aName = aReadParam.GetResult(); + break; + } + } + SwDBFieldType aD( &m_rDoc, aName, SwDBData() ); // Database: nothing + + SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aD ); + SwDBField aField( static_cast<SwDBFieldType*>(pFT) ); + aField.SetFieldCode( rStr ); + + OUString aResult; + m_xSBase->WW8ReadString( *m_pStrm, aResult, m_xPlcxMan->GetCpOfs()+ + pF->nSRes, pF->nLRes, m_eTextCharSet ); + + aResult = aResult.replace( '\xb', '\n' ); + + aField.InitContent(aResult); + + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField( aField )); +#endif + return eF_ResT::OK; +} + +// "NEXT" +eF_ResT SwWW8ImplReader::Read_F_DBNext( WW8FieldDesc*, OUString& ) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SwDBNextSetFieldType aN; + SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN ); + SwDBNextSetField aField( static_cast<SwDBNextSetFieldType*>(pFT), OUString(), + SwDBData() ); // Database: nothing + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); +#endif + return eF_ResT::OK; +} + +// "DATASET" +eF_ResT SwWW8ImplReader::Read_F_DBNum( WW8FieldDesc*, OUString& ) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SwDBSetNumberFieldType aN; + SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType( aN ); + SwDBSetNumberField aField( static_cast<SwDBSetNumberFieldType*>(pFT), + SwDBData() ); // Datenbase: nothing + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); +#endif + return eF_ResT::OK; +} + +/* + EQ , only the usage for + a. Combined Characters supported, must be exactly in the form that word + only accepts as combined characters, i.e. + eq \o(\s\up Y(XXX),\s\do Y(XXX)) + b. Ruby Text supported, must be in the form that word recognizes as being + ruby text + ... +*/ +eF_ResT SwWW8ImplReader::Read_F_Equation( WW8FieldDesc*, OUString& rStr ) +{ + WW8ReadFieldParams aReadParam( rStr ); + const sal_Int32 cChar = aReadParam.SkipToNextToken(); + if ('o' == cChar || 'O' == cChar) + { + EquationResult aResult(ParseCombinedChars(rStr)); + + if (aResult.sType == "Input") + { + SwInputField aField( static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )), + aResult.sResult, aResult.sResult, INP_TXT, 0 ); + m_rDoc.getIDocumentContentOperations().InsertPoolItem( *m_pPaM, SwFormatField( aField ) ); // insert input field + } + else if (aResult.sType == "CombinedCharacters") + { + SwCombinedCharField aField(static_cast<SwCombinedCharFieldType*>( + m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars)), aResult.sType); + m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField)); + } + } + else if ('*' == cChar) + Read_SubF_Ruby(aReadParam); + + return eF_ResT::OK; +} + +void SwWW8ImplReader::Read_SubF_Ruby( WW8ReadFieldParams& rReadParam) +{ + sal_uInt16 nJustificationCode=0; + OUString sFontName; + sal_uInt32 nFontSize=0; + OUString sRuby; + OUString sText; + for (;;) + { + const sal_Int32 nRet = rReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + { + OUString sTemp = rReadParam.GetResult(); + if( sTemp.startsWithIgnoreAsciiCase( "jc" ) ) + { + sTemp = sTemp.copy(2); + nJustificationCode = static_cast<sal_uInt16>(sTemp.toInt32()); + } + else if( sTemp.startsWithIgnoreAsciiCase( "hps" ) ) + { + sTemp = sTemp.copy(3); + nFontSize= static_cast<sal_uInt32>(sTemp.toInt32()); + } + else if( sTemp.startsWithIgnoreAsciiCase( "Font:" ) ) + { + sTemp = sTemp.copy(5); + sFontName = sTemp; + } + } + break; + case '*': + break; + case 'o': + for (;;) + { + const sal_Int32 nRes = rReadParam.SkipToNextToken(); + if ( nRes==-1 ) + break; + if ('u' == nRes) + { + if (-2 == rReadParam.SkipToNextToken() && + rReadParam.GetResult().startsWithIgnoreAsciiCase("p")) + { + if (-2 == rReadParam.SkipToNextToken()) + { + OUString sPart = rReadParam.GetResult(); + sal_Int32 nBegin = sPart.indexOf('('); + + //Word disallows brackets in this field, + sal_Int32 nEnd = sPart.indexOf(')'); + + if ((nBegin != -1) && + (nEnd != -1) && (nBegin < nEnd)) + { + sRuby = sPart.copy(nBegin+1,nEnd-nBegin-1); + } + if (-1 != nEnd) + { + if (-1 == + (nBegin = sPart.indexOf(',',nEnd))) + { + nBegin = sPart.indexOf(';',nEnd); + } + nEnd = sPart.lastIndexOf(')'); + } + if ((nBegin != -1) && (nEnd != -1) && (nBegin < nEnd)) + { + sText = sPart.copy(nBegin+1,nEnd-nBegin-1); + sText = sw::FilterControlChars(sText); + } + } + } + } + } + break; + } + } + + //Translate and apply + if (sRuby.isEmpty() || sText.isEmpty() || sFontName.isEmpty() || !nFontSize) + return; + + css::text::RubyAdjust eRubyAdjust; + switch (nJustificationCode) + { + case 0: + eRubyAdjust = css::text::RubyAdjust_CENTER; + break; + case 1: + eRubyAdjust = css::text::RubyAdjust_BLOCK; + break; + case 2: + eRubyAdjust = css::text::RubyAdjust_INDENT_BLOCK; + break; + default: + case 3: + eRubyAdjust = css::text::RubyAdjust_LEFT; + break; + case 4: + eRubyAdjust = css::text::RubyAdjust_RIGHT; + break; + } + + SwFormatRuby aRuby(sRuby); + const SwCharFormat *pCharFormat=nullptr; + //Make a guess at which of asian of western we should be setting + assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); + sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(sRuby, 0); + + //Check to see if we already have a ruby charstyle that this fits + for(const auto& rpCharFormat : m_aRubyCharFormats) + { + const SvxFontHeightItem &rFH = + ItemGet<SvxFontHeightItem>(*rpCharFormat, + GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript)); + if (rFH.GetHeight() == nFontSize*10) + { + const SvxFontItem &rF = ItemGet<SvxFontItem>(*rpCharFormat, + GetWhichOfScript(RES_CHRATR_FONT,nScript)); + if (rF.GetFamilyName() == sFontName) + { + pCharFormat = rpCharFormat; + break; + } + } + } + + //Create a new char style if necessary + if (!pCharFormat) + { + OUString aNm; + //Take this as the base name + SwStyleNameMapper::FillUIName(RES_POOLCHR_RUBYTEXT,aNm); + aNm+=OUString::number(m_aRubyCharFormats.size()+1); + SwCharFormat *pFormat = m_rDoc.MakeCharFormat(aNm, m_rDoc.GetDfltCharFormat()); + SvxFontHeightItem aHeightItem(nFontSize*10, 100, RES_CHRATR_FONTSIZE); + SvxFontItem aFontItem(FAMILY_DONTKNOW,sFontName, + OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, RES_CHRATR_FONT); + aHeightItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript)); + aFontItem.SetWhich(GetWhichOfScript(RES_CHRATR_FONT,nScript)); + pFormat->SetFormatAttr(aHeightItem); + pFormat->SetFormatAttr(aFontItem); + m_aRubyCharFormats.push_back(pFormat); + pCharFormat = pFormat; + } + + //Set the charstyle and justification + aRuby.SetCharFormatName(pCharFormat->GetName()); + aRuby.SetCharFormatId(pCharFormat->GetPoolFormatId()); + aRuby.SetAdjustment(eRubyAdjust); + + NewAttr(aRuby); + m_rDoc.getIDocumentContentOperations().InsertString( *m_pPaM, sText ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CJK_RUBY ); + +} + +// "table of ..." fields + +static void lcl_toxMatchACSwitch(SwDoc const & rDoc, + SwTOXBase& rBase, + WW8ReadFieldParams& rParam, + SwCaptionDisplay eCaptionType) +{ + if ( rParam.GoToTokenParam() ) + { + SwTOXType* pType = const_cast<SwTOXType*>(rDoc.GetTOXType( TOX_ILLUSTRATIONS, 0)); + rBase.RegisterToTOXType( *pType ); + rBase.SetCaptionDisplay( eCaptionType ); + // Read Sequence Name and store in TOXBase + OUString sSeqName( rParam.GetResult() ); + lcl_ConvertSequenceName( sSeqName ); + rBase.SetSequenceName( sSeqName ); + } +} + +static void EnsureMaxLevelForTemplates(SwTOXBase& rBase) +{ + //If the TOC contains Template entries at levels > the evaluation level + //that was initially taken from the max normal outline level of the word TOC + //then we cannot use that for the evaluation level because writer cuts off + //all styles above that level, while word just cuts off the "standard" + //outline styles, we have no option but to expand to the highest level + //Word included. + if ((rBase.GetLevel() != MAXLEVEL) && (SwTOXElement::Template & rBase.GetCreateType())) + { + for (sal_uInt16 nI = MAXLEVEL; nI > 0; --nI) + { + if (!rBase.GetStyleNames(nI-1).isEmpty()) + { + rBase.SetLevel(nI); + break; + } + } + } +} + +static void lcl_toxMatchTSwitch(SwWW8ImplReader const & rReader, SwTOXBase& rBase, + WW8ReadFieldParams& rParam) +{ + if ( rParam.GoToTokenParam() ) + { + OUString sParams( rParam.GetResult() ); + if( !sParams.isEmpty() ) + { + sal_Int32 nIndex = 0; + + // Delimiters between styles and style levels appears to allow both ; and , + + OUString sTemplate( sParams.getToken(0, ';', nIndex) ); + if( -1 == nIndex ) + { + nIndex=0; + sTemplate = sParams.getToken(0, ',', nIndex); + } + if( -1 == nIndex ) + { + const SwFormat* pStyle = rReader.GetStyleWithOrgWWName(sTemplate); + if( pStyle ) + sTemplate = pStyle->GetName(); + // Store Style for Level 0 into TOXBase + rBase.SetStyleNames( sTemplate, 0 ); + } + else while( -1 != nIndex ) + { + sal_Int32 nOldIndex=nIndex; + sal_uInt16 nLevel = static_cast<sal_uInt16>( + sParams.getToken(0, ';', nIndex).toInt32()); + if( -1 == nIndex ) + { + nIndex = nOldIndex; + nLevel = static_cast<sal_uInt16>( + sParams.getToken(0, ',', nIndex).toInt32()); + } + + if( (0 < nLevel) && (MAXLEVEL >= nLevel) ) + { + nLevel--; + // Store Style and Level into TOXBase + const SwFormat* pStyle + = rReader.GetStyleWithOrgWWName( sTemplate ); + + if( pStyle ) + sTemplate = pStyle->GetName(); + + OUString sStyles( rBase.GetStyleNames( nLevel ) ); + if( !sStyles.isEmpty() ) + sStyles += OUStringChar(TOX_STYLE_DELIMITER); + sStyles += sTemplate; + rBase.SetStyleNames( sStyles, nLevel ); + } + // read next style name... + nOldIndex = nIndex; + sTemplate = sParams.getToken(0, ';', nIndex); + if( -1 == nIndex ) + { + nIndex=nOldIndex; + sTemplate = sParams.getToken(0, ',', nIndex); + } + } + } + } +} + +sal_uInt16 wwSectionManager::CurrentSectionColCount() const +{ + sal_uInt16 nIndexCols = 1; + if (!maSegments.empty()) + nIndexCols = maSegments.back().maSep.ccolM1 + 1; + return nIndexCols; +} + +//Will there be a new pagebreak at this position (don't know what type +//until later) +bool wwSectionManager::WillHavePageDescHere(const SwNodeIndex& rIdx) const +{ + bool bRet = false; + if (!maSegments.empty()) + { + if (!maSegments.back().IsContinuous() && + maSegments.back().maStart == rIdx) + { + bRet = true; + } + } + return bRet; +} + +static sal_uInt16 lcl_GetMaxValidWordTOCLevel(const SwForm &rForm) +{ + // GetFormMax() returns level + 1, hence the -1 + sal_uInt16 nRet = rForm.GetFormMax()-1; + + // If the max of this type of TOC is greater than the max of a word + // possible toc, then clip to the word max + if (nRet > WW8ListManager::nMaxLevel) + nRet = WW8ListManager::nMaxLevel; + + return nRet; +} + +eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr ) +{ + if (!m_bLoadingTOXCache) + { + m_bLoadingTOXCache = true; + } + else + { + // Embedded TOX --> continue reading its content, but no further TOX + // field + ++m_nEmbeddedTOXLevel; + return eF_ResT::TEXT; + } + + if (pF->nLRes < 3) + return eF_ResT::TEXT; // ignore (#i25440#) + + TOXTypes eTox; // create a ToxBase + switch( pF->nId ) + { + case 8: + eTox = TOX_INDEX; + break; + case 13: + eTox = TOX_CONTENT; + break; + default: + eTox = TOX_USER; + break; + } + + SwTOXElement nCreateOf = (eTox == TOX_CONTENT) ? SwTOXElement::OutlineLevel : SwTOXElement::Mark; + + sal_uInt16 nIndexCols = 1; + + const SwTOXType* pType = m_rDoc.GetTOXType( eTox, 0 ); + SwForm aOrigForm(eTox); + std::shared_ptr<SwTOXBase> pBase = std::make_shared<SwTOXBase>( pType, aOrigForm, nCreateOf, OUString() ); + pBase->SetProtected(m_aSectionManager.CurrentSectionIsProtected()); + switch( eTox ){ + case TOX_INDEX: + { + SwTOIOptions eOptions = SwTOIOptions::SameEntry | SwTOIOptions::CaseSensitive; + + // We set SwTOXElement::OutlineLevel only if + // the parameter \o is within 1 to 9 + // or the parameter \f exists + // or NO switch parameter are given at all. + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case 'c': + if ( aReadParam.GoToTokenParam() ) + { + const OUString sParams( aReadParam.GetResult() ); + // if NO OUString just ignore the \c + if( !sParams.isEmpty() ) + { + nIndexCols = static_cast<sal_uInt16>(sParams.toInt32()); + } + } + break; + case 'e': + { + if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \e + { + OUString sDelimiter( aReadParam.GetResult() ); + SwForm aForm( pBase->GetTOXForm() ); + + // Attention: if TOX_CONTENT brave + // GetFormMax() returns MAXLEVEL + 1 !! + sal_uInt16 nEnd = aForm.GetFormMax()-1; + + for(sal_uInt16 nLevel = 1; + nLevel <= nEnd; + ++nLevel) + { + // Levels count from 1 + // Level 0 is reserved for CAPTION + + // Insert delimiter instead of tab in front of the page number if there is one: + FormTokenType ePrevType = TOKEN_END; + FormTokenType eType; + // -> #i21237# + SwFormTokens aPattern = + aForm.GetPattern(nLevel); + SwFormTokens::iterator aIt = aPattern.begin(); + do + { + eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType; + + if (eType == TOKEN_PAGE_NUMS) + { + if (TOKEN_TAB_STOP == ePrevType) + { + --aIt; + + if(0x09 == sDelimiter[0]) + aIt->eTabAlign = SvxTabAdjust::End; + else + { + SwFormToken aToken(TOKEN_TEXT); + aToken.sText = sDelimiter; + *aIt = aToken; + } + aForm.SetPattern(nLevel, aPattern); + } + + eType = TOKEN_END; + } + + ePrevType = eType; + } + while (TOKEN_END != eType); + // <- #i21237# + } + pBase->SetTOXForm( aForm ); + } + } + break; + case 'h': + { + eOptions |= SwTOIOptions::AlphaDelimiter; + } + break; + } + } + pBase->SetOptions( eOptions ); + } + break; + + case TOX_CONTENT: + { + bool bIsHyperlink = false; + // We set SwTOXElement::OutlineLevel only if + // the parameter \o is within 1 to 9 + // or the parameter \f exists + // or NO switch parameter are given at all. + SwTOXElement eCreateFrom = SwTOXElement::NONE; + sal_Int32 nMaxLevel = 0; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case 'h': + bIsHyperlink = true; + break; + case 'a': + case 'c': + lcl_toxMatchACSwitch(m_rDoc, *pBase, aReadParam, + ('c' == nRet) + ? CAPTION_COMPLETE + : CAPTION_TEXT ); + break; + case 'o': + { + sal_Int32 nVal; + if( !aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) ) + nVal = lcl_GetMaxValidWordTOCLevel(aOrigForm); + if( nMaxLevel < nVal ) + nMaxLevel = nVal; + eCreateFrom |= SwTOXElement::OutlineLevel; + } + break; + case 'f': + eCreateFrom |= SwTOXElement::Mark; + break; + case 'l': + { + sal_Int32 nVal; + if( aReadParam.GetTokenSttFromTo(nullptr, &nVal, WW8ListManager::nMaxLevel) ) + { + if( nMaxLevel < nVal ) + nMaxLevel = nVal; + eCreateFrom |= SwTOXElement::Mark; + } + } + break; + case 't': // paragraphs using special styles shall + // provide the TOX's content + lcl_toxMatchTSwitch(*this, *pBase, aReadParam); + eCreateFrom |= SwTOXElement::Template; + break; + case 'p': + { + if ( aReadParam.GoToTokenParam() ) // if NO String just ignore the \p + { + OUString sDelimiter( aReadParam.GetResult() ); + SwForm aForm( pBase->GetTOXForm() ); + + // Attention: if TOX_CONTENT brave + // GetFormMax() returns MAXLEVEL + 1 !! + sal_uInt16 nEnd = aForm.GetFormMax()-1; + + for(sal_uInt16 nLevel = 1; + nLevel <= nEnd; + ++nLevel) + { + // Levels count from 1 + // Level 0 is reserved for CAPTION + + // Insert delimiter instead of tab in front of the pagenumber if there is one: + FormTokenType ePrevType = TOKEN_END; + FormTokenType eType; + + // -> #i21237# + SwFormTokens aPattern = aForm.GetPattern(nLevel); + SwFormTokens::iterator aIt = aPattern.begin(); + do + { + eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType; + + if (eType == TOKEN_PAGE_NUMS) + { + if (TOKEN_TAB_STOP == ePrevType) + { + --aIt; + + SwFormToken aToken(TOKEN_TEXT); + aToken.sText = sDelimiter; + + *aIt = aToken; + aForm.SetPattern(nLevel, + aPattern); + } + eType = TOKEN_END; + } + ePrevType = eType; + } + while( TOKEN_END != eType ); + // <- #i21237# + } + pBase->SetTOXForm( aForm ); + } + } + break; + case 'n': // don't print page numbers + { + // read START and END param + sal_Int32 nStart(0); + sal_Int32 nEnd(0); + if( !aReadParam.GetTokenSttFromTo( &nStart, &nEnd, + WW8ListManager::nMaxLevel ) ) + { + nStart = 1; + nEnd = aOrigForm.GetFormMax()-1; + } + // remove page numbers from this levels + SwForm aForm( pBase->GetTOXForm() ); + if (aForm.GetFormMax() <= nEnd) + nEnd = aForm.GetFormMax()-1; + for ( sal_Int32 nLevel = nStart; nLevel<=nEnd; ++nLevel ) + { + // Levels count from 1 + // Level 0 is reserved for CAPTION + + // Remove pagenumber and if necessary the tab in front of it: + FormTokenType eType; + // -> #i21237# + SwFormTokens aPattern = aForm.GetPattern(nLevel); + SwFormTokens::iterator aIt = aPattern.begin(); + do + { + eType = ++aIt == aPattern.end() ? TOKEN_END : aIt->eTokenType; + + if (eType == TOKEN_PAGE_NUMS) + { + aIt = aPattern.erase(aIt); + --aIt; + if ( + TOKEN_TAB_STOP == + aIt->eTokenType + ) + { + aPattern.erase(aIt); + aForm.SetPattern(nLevel, aPattern); + } + eType = TOKEN_END; + } + } + while (TOKEN_END != eType); + // <- #i21237# + } + pBase->SetTOXForm( aForm ); + } + break; + + /* + // the following switches are not (yet) supported + // by good old StarWriter: + case 'b': + case 's': + case 'd': + break; + */ + } + } + + // For loading the expression of TOC field, we need to mapping its parameters to TOX entries tokens + // also include the hyperlinks and page references + SwFormToken aLinkStart(TOKEN_LINK_START); + SwFormToken aLinkEnd(TOKEN_LINK_END); + aLinkStart.sCharStyleName = "Index Link"; + aLinkEnd.sCharStyleName = "Index Link"; + SwForm aForm(pBase->GetTOXForm()); + sal_uInt16 nEnd = aForm.GetFormMax()-1; + + for(sal_uInt16 nLevel = 1; nLevel <= nEnd; ++nLevel) + { + SwFormTokens aPattern = aForm.GetPattern(nLevel); + if ( bIsHyperlink ) + { + aPattern.insert(aPattern.begin(), aLinkStart); + } + else + { + auto aItr = std::find_if(aPattern.begin(), aPattern.end(), + [](const SwFormToken& rToken) { return rToken.eTokenType == TOKEN_PAGE_NUMS; }); + if (aItr != aPattern.end()) + aPattern.insert(aItr, aLinkStart); + } + aPattern.push_back(aLinkEnd); + aForm.SetPattern(nLevel, aPattern); + } + pBase->SetTOXForm(aForm); + + if (!nMaxLevel) + nMaxLevel = WW8ListManager::nMaxLevel; + pBase->SetLevel(nMaxLevel); + + const TOXTypes eType = pBase->GetTOXType()->GetType(); + switch( eType ) + { + case TOX_CONTENT: + { + //If we would be created from outlines, either explicitly or by default + //then see if we need extra styles added to the outlines + SwTOXElement eEffectivelyFrom = eCreateFrom != SwTOXElement::NONE ? eCreateFrom : SwTOXElement::OutlineLevel; + if (eEffectivelyFrom & SwTOXElement::OutlineLevel) + { + // #i19683# Insert a text token " " between the number and entry token. + // In an ideal world we could handle the tab stop between the number and + // the entry correctly, but I currently have no clue how to obtain + // the tab stop position. It is _not_ set at the paragraph style. + std::unique_ptr<SwForm> pForm; + for (const SwWW8StyInf & rSI : m_vColl) + { + if (rSI.IsOutlineNumbered()) + { + sal_uInt16 nStyleLevel = rSI.mnWW8OutlineLevel; + const SwNumFormat& rFormat = rSI.GetOutlineNumrule()->Get( nStyleLevel ); + if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ) + { + ++nStyleLevel; + + if ( !pForm ) + pForm.reset(new SwForm( pBase->GetTOXForm() )); + + SwFormTokens aPattern = pForm->GetPattern(nStyleLevel); + SwFormTokens::iterator aIt = + find_if(aPattern.begin(), aPattern.end(), + SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO)); + + if ( aIt != aPattern.end() ) + { + SwFormToken aNumberEntrySeparator( TOKEN_TEXT ); + aNumberEntrySeparator.sText = " "; + aPattern.insert( ++aIt, aNumberEntrySeparator ); + pForm->SetPattern( nStyleLevel, aPattern ); + } + } + } + } + if ( pForm ) + { + pBase->SetTOXForm( *pForm ); + } + } + + if (eCreateFrom != SwTOXElement::NONE) + pBase->SetCreate(eCreateFrom); + EnsureMaxLevelForTemplates(*pBase); + } + break; + case TOX_ILLUSTRATIONS: + { + if( eCreateFrom == SwTOXElement::NONE ) + eCreateFrom = SwTOXElement::Sequence; + pBase->SetCreate( eCreateFrom ); + + /* + We don't know until here if we are an illustration + or not, and so have being used a TOX_CONTENT so far + which has 10 levels, while TOX has only two, this + level is set only in the constructor of SwForm, so + create a new one and copy over anything that could + be set in the old one, and remove entries from the + pattern which do not apply to illustration indices + */ + SwForm aOldForm( pBase->GetTOXForm() ); + SwForm aNewForm( eType ); + sal_uInt16 nNewEnd = aNewForm.GetFormMax()-1; + + // #i21237# + for(sal_uInt16 nLevel = 1; nLevel <= nNewEnd; ++nLevel) + { + SwFormTokens aPattern = aOldForm.GetPattern(nLevel); + SwFormTokens::iterator new_end = + remove_if(aPattern.begin(), aPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_ENTRY_NO)); + aPattern.erase(new_end, aPattern.end() ); // table index imported with wrong page number format + aForm.SetPattern( nLevel, aPattern ); + aForm.SetTemplate( nLevel, aOldForm.GetTemplate(nLevel) ); + } + + pBase->SetTOXForm( aNewForm ); + } + break; + default: + OSL_ENSURE(false, "Unhandled toc options!"); + break; + } + } + break; + case TOX_USER: + break; + default: + OSL_ENSURE(false, "Unhandled toc options!"); + break; + } // ToxBase fertig + + // #i21237# - propagate tab stops from paragraph styles used in TOX to patterns of the TOX + pBase->AdjustTabStops( m_rDoc ); + + //#i10028# inserting a toc implicitly acts like a parabreak in word and writer + if ( m_pPaM->End() && + m_pPaM->End()->nNode.GetNode().GetTextNode() && + m_pPaM->End()->nNode.GetNode().GetTextNode()->Len() != 0 ) + { + m_bCareFirstParaEndInToc = true; + } + + if (m_pPaM->GetPoint()->nContent.GetIndex()) + AppendTextNode(*m_pPaM->GetPoint()); + + const SwPosition* pPos = m_pPaM->GetPoint(); + + SwFltTOX aFltTOX( pBase ); + + // test if there is already a break item on this node + if(SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode()) + { + const SfxItemSet* pSet = pNd->GetpSwAttrSet(); + if( pSet ) + { + if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false)) + aFltTOX.SetHadBreakItem(true); + if (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false)) + aFltTOX.SetHadPageDescItem(true); + } + } + + //Will there be a new pagebreak at this position (don't know what type + //until later) + if (m_aSectionManager.WillHavePageDescHere(pPos->nNode)) + aFltTOX.SetHadPageDescItem(true); + + // Set start in stack + m_xReffedStck->NewAttr( *pPos, aFltTOX ); + + m_rDoc.InsertTableOf(*m_pPaM->GetPoint(), aFltTOX.GetBase()); + + //The TOC field representation contents should be inserted into TOC section, but not after TOC section. + //So we need update the document position when loading TOC representation and after loading TOC; + m_pPosAfterTOC.reset(new SwPaM(*m_pPaM, m_pPaM)); + (*m_pPaM).Move(fnMoveBackward); + SwPaM aRegion(*m_pPaM, m_pPaM); + + OSL_ENSURE(SwDoc::GetCurTOX(*aRegion.GetPoint()), "Misunderstood how toc works"); + if (SwTOXBase* pBase2 = SwDoc::GetCurTOX(*aRegion.GetPoint())) + { + pBase2->SetMSTOCExpression(rStr); + + if ( nIndexCols > 1 ) + { + // Set the column number for index + SfxItemSet aSet( m_rDoc.GetAttrPool(), svl::Items<RES_COL, RES_COL>{} ); + SwFormatCol aCol; + aCol.Init( nIndexCols, 708, USHRT_MAX ); + aSet.Put( aCol ); + pBase2->SetAttrSet( aSet ); + } + + // inserting a toc inserts a section before this point, so adjust pos + // for future page/section segment insertion + m_aSectionManager.PrependedInlineNode( *m_pPosAfterTOC->GetPoint(), aRegion.GetNode() ); + } + + // Set end in stack + m_xReffedStck->SetAttr( *pPos, RES_FLTR_TOX ); + + if (!m_aApos.back()) //a para end in apo doesn't count + m_bWasParaEnd = true; + + //Return FLD_TEXT, instead of FLD_OK + //FLD_TEXT means the following content, commonly indicate the field representation content should be parsed + //FLD_OK means the current field loading is finished. The rest part should be ignored. + return eF_ResT::TEXT; +} + +eF_ResT SwWW8ImplReader::Read_F_Shape(WW8FieldDesc* /*pF*/, OUString& /*rStr*/) +{ + /* + #i3958# 0x8 followed by 0x1 where the shape is the 0x8 and its anchoring + to be ignored followed by a 0x1 with an empty drawing. Detect in inserting + the drawing that we are in the Shape field and respond accordingly + */ + return eF_ResT::TEXT; + } + +eF_ResT SwWW8ImplReader::Read_F_Hyperlink( WW8FieldDesc* /*pF*/, OUString& rStr ) +{ + OUString sURL, sTarget, sMark; + + //HYPERLINK "filename" [switches] + rStr = comphelper::string::stripEnd(rStr, 1); + + bool bOptions = false; + WW8ReadFieldParams aReadParam( rStr ); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if (sURL.isEmpty() && !bOptions) + sURL = ConvertFFileName(aReadParam.GetResult()); + break; + + case 'n': + sTarget = "_blank"; + bOptions = true; + break; + + case 'l': + bOptions = true; + if ( aReadParam.SkipToNextToken()==-2 ) + { + sMark = aReadParam.GetResult(); + if( sMark.endsWith("\"")) + { + sMark = sMark.copy( 0, sMark.getLength() - 1 ); + } + // #120879# add cross reference bookmark name prefix, if it matches internal TOC bookmark naming convention + if ( IsTOCBookmarkName( sMark ) ) + { + sMark = EnsureTOCBookmarkName(sMark); + // track <sMark> as referenced TOC bookmark. + m_xReffedStck->aReferencedTOCBookmarks.insert( sMark ); + } + + if (m_bLoadingTOXCache) + { + m_bLoadingTOXHyperlink = true; //on loading a TOC field nested hyperlink field + } + } + break; + case 't': + bOptions = true; + if ( aReadParam.SkipToNextToken()==-2 ) + sTarget = aReadParam.GetResult(); + break; + case 'h': + case 'm': + OSL_ENSURE( false, "Analysis still missing - unknown data" ); + [[fallthrough]]; + case 's': //worthless fake anchor option + bOptions = true; + break; + } + } + + // use the result + OSL_ENSURE(!sURL.isEmpty() || !sMark.isEmpty(), "WW8: Empty URL"); + + if( !sMark.isEmpty() ) + sURL += "#" + sMark; + + SwFormatINetFormat aURL(sURL, sTarget); + // If on loading TOC field, change the default style into the "index link" + if (m_bLoadingTOXCache) + { + OUString sLinkStyle("Index Link"); + sal_uInt16 nPoolId = + SwStyleNameMapper::GetPoolIdFromUIName( sLinkStyle, SwGetPoolIdFromName::ChrFmt ); + aURL.SetVisitedFormatAndId( sLinkStyle, nPoolId ); + aURL.SetINetFormatAndId( sLinkStyle, nPoolId ); + } + + //As an attribute this needs to be closed, and that'll happen from + //EndExtSprm in conjunction with the maFieldStack. If there are flyfrms + //between the start and begin, their hyperlinks will be set at that time + //as well. + m_xCtrlStck->NewAttr( *m_pPaM->GetPoint(), aURL ); + return eF_ResT::TEXT; +} + +static void lcl_ImportTox(SwDoc &rDoc, SwPaM const &rPaM, const OUString &rStr, bool bIdx) +{ + TOXTypes eTox = ( !bIdx ) ? TOX_CONTENT : TOX_INDEX; // Default + + sal_uInt16 nLevel = 1; + + OUString sFieldText; + WW8ReadFieldParams aReadParam(rStr); + for (;;) + { + const sal_Int32 nRet = aReadParam.SkipToNextToken(); + if ( nRet==-1 ) + break; + switch( nRet ) + { + case -2: + if( sFieldText.isEmpty() ) + { + // PrimaryKey without ":", 2nd after + sFieldText = aReadParam.GetResult(); + } + break; + + case 'f': + if ( aReadParam.GoToTokenParam() ) + { + const OUString sParams( aReadParam.GetResult() ); + if( sParams[0]!='C' && sParams[0]!='c' ) + eTox = TOX_USER; + } + break; + + case 'l': + if ( aReadParam.GoToTokenParam() ) + { + const OUString sParams( aReadParam.GetResult() ); + // if NO String just ignore the \l + if( !sParams.isEmpty() && sParams[0]>'0' && sParams[0]<='9' ) + { + nLevel = static_cast<sal_uInt16>(sParams.toInt32()); + } + } + break; + } + } + + OSL_ENSURE( rDoc.GetTOXTypeCount( eTox ), "Doc.GetTOXTypeCount() == 0 :-(" ); + + const SwTOXType* pT = rDoc.GetTOXType( eTox, 0 ); + SwTOXMark aM( pT ); + + if( eTox != TOX_INDEX ) + aM.SetLevel( nLevel ); + else + { + sal_Int32 nFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM ); + if( -1 != nFnd ) // it exist levels + { + aM.SetPrimaryKey( sFieldText.copy( 0, nFnd ) ); + sal_Int32 nScndFnd = sFieldText.indexOf( WW8_TOX_LEVEL_DELIM, nFnd+1 ); + if( -1 != nScndFnd ) + { + aM.SetSecondaryKey( sFieldText.copy( nFnd+1, nScndFnd - nFnd - 1 )); + nFnd = nScndFnd; + } + sFieldText = sFieldText.copy( nFnd+1 ); + } + } + + if (!sFieldText.isEmpty()) + { + aM.SetAlternativeText( sFieldText ); + rDoc.getIDocumentContentOperations().InsertPoolItem( rPaM, aM ); + } +} + +void SwWW8ImplReader::ImportTox( int nFieldId, const OUString& aStr ) +{ + bool bIdx = (nFieldId != 9); + lcl_ImportTox(m_rDoc, *m_pPaM, aStr, bIdx); +} + +void SwWW8ImplReader::Read_FieldVanish( sal_uInt16, const sal_uInt8*, short nLen ) +{ + //Meaningless in a style + if (m_pCurrentColl || !m_xPlcxMan) + return; + + const int nChunk = 64; //number of characters to read at one time + + // Careful: MEMICMP doesn't work with fieldnames including umlauts! + const static char *aFieldNames[] = { "\x06""INHALT", "\x02""XE", // dt. + "\x02""TC" }; // us + const static sal_uInt8 aFieldId[] = { 9, 4, 9 }; + + if( nLen < 0 ) + { + m_bIgnoreText = false; + return; + } + + // our method was called from + // ''Skip attributes of field contents'' loop within ReadTextAttr() + if( m_bIgnoreText ) + return; + + m_bIgnoreText = true; + long nOldPos = m_pStrm->Tell(); + + WW8_CP nStartCp = m_xPlcxMan->Where() + m_xPlcxMan->GetCpOfs(); + + OUString sFieldName; + sal_Int32 nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sFieldName, nStartCp, + nChunk, m_eStructCharSet ); + nStartCp+=nFieldLen; + + sal_Int32 nC = 0; + //If the first chunk did not start with a field start then + //reset the stream position and give up + if( !nFieldLen || sFieldName[nC]!=0x13 ) // Field Start Mark + { + // If Field End Mark found + if( nFieldLen && sFieldName[nC]==0x15 ) + m_bIgnoreText = false; + m_pStrm->Seek( nOldPos ); + return; // no field found + } + + sal_Int32 nFnd; + //If this chunk does not contain a field end, keep reading chunks + //until we find one, or we run out of text, + for (;;) + { + nFnd = sFieldName.indexOf(0x15); + //found field end, we can stop now + if (nFnd != -1) + break; + OUString sTemp; + nFieldLen = m_xSBase->WW8ReadString( *m_pStrm, sTemp, + nStartCp, nChunk, m_eStructCharSet ); + sFieldName+=sTemp; + nStartCp+=nFieldLen; + if (!nFieldLen) + break; + } + + m_pStrm->Seek( nOldPos ); + + //if we have no 0x15 give up, otherwise erase everything from the 0x15 + //onwards + if (nFnd<0) + return; + + sFieldName = sFieldName.copy(0, nFnd); + + nC++; + while ( sFieldName[nC]==' ' ) + nC++; + + for( int i = 0; i < 3; i++ ) + { + const char* pName = aFieldNames[i]; + const sal_Int32 nNameLen = static_cast<sal_Int32>(*pName++); + if( sFieldName.matchIgnoreAsciiCaseAsciiL( pName, nNameLen, nC ) ) + { + ImportTox( aFieldId[i], sFieldName.copy( nC + nNameLen ) ); + break; // no duplicates allowed + } + } + m_bIgnoreText = true; + m_pStrm->Seek( nOldPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx new file mode 100644 index 000000000..b3b5443e2 --- /dev/null +++ b/sw/source/filter/ww8/ww8par6.cxx @@ -0,0 +1,6121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <o3tl/safeint.hxx> +#include <svl/itemiter.hxx> +#include <svl/grabbagitem.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> + +#include <hintids.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/autokernitem.hxx> +#include <editeng/paperinf.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/twolinesitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/blinkitem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/paravertalignitem.hxx> +#include <editeng/pgrditem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include "sortedarray.hxx" +#include "sprmids.hxx" +#include <node.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <pagedesc.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <fchrfmt.hxx> +#include <fmthdft.hxx> +#include <fmtclds.hxx> +#include <fmtftntx.hxx> +#include <frmatr.hxx> +#include <section.hxx> +#include <lineinfo.hxx> +#include <fmtline.hxx> +#include <txatbase.hxx> +#include <fmtflcnt.hxx> +#include <tgrditem.hxx> +#include <hfspacingitem.hxx> +#include <swtable.hxx> +#include <fltini.hxx> +#include "writerhelper.hxx" +#include "writerwordglue.hxx" +#include "ww8scan.hxx" +#include "ww8par2.hxx" +#include "ww8graf.hxx" + +#include <fmtwrapinfluenceonobjpos.hxx> + +using namespace sw::util; +using namespace sw::types; +using namespace ::com::sun::star; +using namespace nsHdFtFlags; + +// various + +#define MM_250 1417 // WW default for horizontal borders: 2.5 cm +#define MM_200 1134 // WW default for lower border: 2.0 cm + + +static sal_uInt8 lcl_ReadBorders(bool bVer67, WW8_BRCVer9* brc, WW8PLCFx_Cp_FKP* pPap, + const WW8RStyle* pSty = nullptr, const WW8PLCFx_SEPX* pSep = nullptr); + +Color SwWW8ImplReader::GetCol(sal_uInt8 nIco) +{ + static const Color eSwWW8ColA[] = + { + COL_AUTO, COL_BLACK, COL_LIGHTBLUE, COL_LIGHTCYAN, COL_LIGHTGREEN, + COL_LIGHTMAGENTA, COL_LIGHTRED, COL_YELLOW, COL_WHITE, COL_BLUE, + COL_CYAN, COL_GREEN, COL_MAGENTA, COL_RED, COL_BROWN, COL_GRAY, + COL_LIGHTGRAY + }; + SAL_WARN_IF( + nIco >= SAL_N_ELEMENTS(eSwWW8ColA), "sw.ww8", + "ico " << sal_uInt32(nIco) << " >= " << SAL_N_ELEMENTS(eSwWW8ColA)); + return nIco < SAL_N_ELEMENTS(eSwWW8ColA) ? eSwWW8ColA[nIco] : COL_AUTO; +} + +static sal_uInt32 MSRoundTweak(sal_uInt32 x) +{ + return x; +} + +// page attribute which are not handled via the attribute management but +// using ...->HasSprm +// (except OLST which stays a normal attribute) +static short ReadSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal ) +{ + SprmResult aRes = pSep->HasSprm(nId); // sprm here? + const sal_uInt8* pS = aRes.pSprm; + short nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToInt16(pS) : nDefaultVal; + return nVal; +} + +static sal_uInt16 ReadUSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, short nDefaultVal ) +{ + SprmResult aRes = pSep->HasSprm(nId); // sprm here? + const sal_uInt8* pS = aRes.pSprm; + sal_uInt16 nVal = (pS && aRes.nRemainingData >= 2) ? SVBT16ToUInt16(pS) : nDefaultVal; + return nVal; +} + +static sal_uInt8 ReadBSprm( const WW8PLCFx_SEPX* pSep, sal_uInt16 nId, sal_uInt8 nDefaultVal ) +{ + SprmResult aRes = pSep->HasSprm(nId); // sprm here? + const sal_uInt8* pS = aRes.pSprm; + sal_uInt8 nVal = (pS && aRes.nRemainingData >= 1) ? *pS : nDefaultVal; + return nVal; +} + +void wwSection::SetDirection() +{ + //sprmSTextFlow + switch (maSep.wTextFlow) + { + default: + OSL_ENSURE(false, "Unknown layout type"); + [[fallthrough]]; + case 0: + meDir=SvxFrameDirection::Horizontal_LR_TB; + break; + case 1: + meDir=SvxFrameDirection::Vertical_RL_TB; + break; + case 2: + //asian letters are not rotated, western are. We can't import + //bottom to top going left to right, we can't do this in + //pages, (in drawboxes we could partly hack it with a rotated + //drawing box, though not frame) + meDir=SvxFrameDirection::Vertical_RL_TB; + break; + case 3: + //asian letters are not rotated, western are. We can't import + meDir=SvxFrameDirection::Vertical_RL_TB; + break; + case 4: + //asian letters are rotated, western not. We can't import + meDir=SvxFrameDirection::Horizontal_LR_TB; + break; + } + + sal_uInt8 bRTLPgn = maSep.fBiDi; + if ((meDir == SvxFrameDirection::Horizontal_LR_TB) && bRTLPgn) + meDir = SvxFrameDirection::Horizontal_RL_TB; +} + +bool wwSection::IsVertical() const +{ + return meDir == SvxFrameDirection::Vertical_RL_TB || meDir == SvxFrameDirection::Vertical_LR_TB; +} + +/* + This is something of festering mapping, I'm open to better ways of doing it, + but primarily the grid in writer is different to that in word. In writer the + grid elements are squares with ruby rows inbetween. While in word there is no + ruby stuff, and the elements are rectangles. By misusing the ruby row I can + handle distortions in one direction, but its all a bit of a mess: +*/ +void SwWW8ImplReader::SetDocumentGrid(SwFrameFormat &rFormat, const wwSection &rSection) +{ + if (m_bVer67) + return; + + rFormat.SetFormatAttr(SvxFrameDirectionItem(rSection.meDir, RES_FRAMEDIR)); + + SwTwips nTextareaHeight = rFormat.GetFrameSize().GetHeight(); + const SvxULSpaceItem &rUL = ItemGet<SvxULSpaceItem>(rFormat, RES_UL_SPACE); + nTextareaHeight -= rUL.GetUpper(); + nTextareaHeight -= rUL.GetLower(); + + SwTwips nTextareaWidth = rFormat.GetFrameSize().GetWidth(); + const SvxLRSpaceItem &rLR = ItemGet<SvxLRSpaceItem>(rFormat, RES_LR_SPACE); + nTextareaWidth -= rLR.GetLeft(); + nTextareaWidth -= rLR.GetRight(); + + if (rSection.IsVertical()) + std::swap(nTextareaHeight, nTextareaWidth); + + SwTextGridItem aGrid; + aGrid.SetDisplayGrid(false); + aGrid.SetPrintGrid(false); + SwTextGrid eType=GRID_NONE; + + switch (rSection.maSep.clm) + { + case 0: + eType = GRID_NONE; + break; + default: + OSL_ENSURE(false, "Unknown grid type"); + [[fallthrough]]; + case 3: + eType = GRID_LINES_CHARS; + aGrid.SetSnapToChars(true); + break; + case 1: + eType = GRID_LINES_CHARS; + aGrid.SetSnapToChars(false); + break; + case 2: + eType = GRID_LINES_ONLY; + break; + } + + aGrid.SetGridType(eType); + + // seem to not add external leading in word, or the character would run across + // two line in some cases. + if (eType != GRID_NONE) + m_rDoc.getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, false); + + //force to set document as standard page mode + bool bSquaredMode = false; + m_rDoc.SetDefaultPageMode( bSquaredMode ); + aGrid.SetSquaredMode( bSquaredMode ); + + //Get the size of word's default styles font + sal_uInt32 nCharWidth=240; + for (sal_uInt16 nI = 0; nI < m_xStyles->GetCount(); ++nI) + { + if (m_vColl[nI].m_bValid && m_vColl[nI].m_pFormat && + m_vColl[nI].IsWW8BuiltInDefaultStyle()) + { + nCharWidth = ItemGet<SvxFontHeightItem>(*(m_vColl[nI].m_pFormat), + RES_CHRATR_CJK_FONTSIZE).GetHeight(); + break; + } + } + + //dxtCharSpace + if (rSection.maSep.dxtCharSpace) + { + sal_uInt32 nCharSpace = rSection.maSep.dxtCharSpace; + //main lives in top 20 bits, and is signed. + sal_Int32 nMain = (nCharSpace & 0xFFFFF000); + nMain/=0x1000; + nCharWidth += nMain*20; + + int nFraction = (nCharSpace & 0x00000FFF); + nFraction = (nFraction*20)/0xFFF; + nCharWidth += nFraction; + } + + aGrid.SetBaseWidth( writer_cast<sal_uInt16>(nCharWidth)); + + //sep.dyaLinePitch + sal_Int32 nLinePitch = rSection.maSep.dyaLinePitch; + if (nLinePitch >= 1 && nLinePitch <= 31680) + { + aGrid.SetLines(writer_cast<sal_uInt16>(nTextareaHeight/nLinePitch)); + aGrid.SetBaseHeight(writer_cast<sal_uInt16>(nLinePitch)); + } + + aGrid.SetRubyHeight(0); + + rFormat.SetFormatAttr(aGrid); +} + +void SwWW8ImplReader::SetRelativeJustify( bool bRel ) +{ + if ( m_pCurrentColl && StyleExists(m_nCurrentColl) ) // importing style + m_vColl[m_nCurrentColl].m_nRelativeJustify = bRel ? 1 : 0; + else if ( m_xPlcxMan && m_xPlcxMan->GetPap() ) // importing paragraph + m_xPlcxMan->GetPap()->nRelativeJustify = bRel ? 1 : 0; +} + +bool SwWW8ImplReader::IsRelativeJustify() +{ + bool bRet = m_xWwFib->GetFIBVersion() >= ww::eWW8; + if ( bRet ) + { + // if relativeJustify is undefined (-1), then check the parent style. + if ( m_pCurrentColl && StyleExists(m_nCurrentColl) ) + { + sal_Int16 nRelative = m_vColl[m_nCurrentColl].m_nRelativeJustify; + if ( nRelative < 0 && m_nCurrentColl ) + { + o3tl::sorted_vector<sal_uInt16> aVisitedStyles; + bRet = IsRelativeJustify(m_vColl[m_nCurrentColl].m_nBase, aVisitedStyles); + } + else + bRet = nRelative > 0; + } + else if ( m_xPlcxMan && m_xPlcxMan->GetPap() ) + { + sal_Int16 nRelative = m_xPlcxMan->GetPap()->nRelativeJustify; + if ( nRelative < 0 ) + { + o3tl::sorted_vector<sal_uInt16> aVisitedStyles; + bRet = IsRelativeJustify(m_nCurrentColl, aVisitedStyles); + } + else + bRet = nRelative > 0; + } + } + + return bRet; +} + +bool SwWW8ImplReader::IsRelativeJustify(sal_uInt16 nColl, o3tl::sorted_vector<sal_uInt16>& rVisitedStyles) +{ + assert( m_xWwFib->GetFIBVersion() >= ww::eWW8 + && "pointless to search styles if relative justify is impossible"); + bool bRet = true; + if ( StyleExists(nColl) ) + { + rVisitedStyles.insert(nColl); + // if relativeJustify is undefined (-1), then check the parent style. + sal_Int16 nRelative = m_vColl[nColl].m_nRelativeJustify; + if ( nColl == 0 || nRelative >= 0 ) + bRet = nRelative > 0; + else if (rVisitedStyles.find(m_vColl[nColl].m_nBase) == rVisitedStyles.end()) // detect loop in chain + bRet = IsRelativeJustify(m_vColl[nColl].m_nBase, rVisitedStyles); + } + + return bRet; +} + +void SwWW8ImplReader::Read_ParaBiDi(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_FRAMEDIR); + else + { + SvxFrameDirection eDir = + *pData ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; + + // In eWW8+, justify can be absolute, or relative to BiDi + bool bBiDiSwap = IsRelativeJustify(); + if ( bBiDiSwap ) + { + // Only change if ParaBiDi doesn't match previous setting. + const bool bParentRTL = IsRightToLeft(); + bBiDiSwap = (eDir == SvxFrameDirection::Horizontal_RL_TB && !bParentRTL) + || (eDir == SvxFrameDirection::Horizontal_LR_TB && bParentRTL); + } + + if ( bBiDiSwap ) + { + const SvxAdjustItem* pItem = static_cast<const SvxAdjustItem*>(GetFormatAttr(RES_PARATR_ADJUST)); + if ( !pItem ) + { + // no previous adjust: set appropriate default + if ( eDir == SvxFrameDirection::Horizontal_LR_TB ) + NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) ); + else + NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) ); + } + else + { + // previous adjust and bidi has changed: swap Left/Right + const SvxAdjust eJustify = pItem->GetAdjust(); + if ( eJustify == SvxAdjust::Left ) + NewAttr( SvxAdjustItem( SvxAdjust::Right, RES_PARATR_ADJUST ) ); + else if ( eJustify == SvxAdjust::Right ) + NewAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) ); + } + } + + NewAttr(SvxFrameDirectionItem(eDir, RES_FRAMEDIR)); + + if ( m_pCurrentColl && m_xStyles ) // in style definition + m_xStyles->mbBidiChanged = true; + } +} + +bool wwSectionManager::SetCols(SwFrameFormat &rFormat, const wwSection &rSection, + sal_uInt32 nNetWidth) +{ + //sprmSCcolumns - number of columns - 1 + const sal_Int16 nCols = rSection.NoCols(); + + if (nCols < 2) //check for no columns or other weird state + return false; + + const sal_uInt16 nNetWriterWidth = writer_cast<sal_uInt16>(nNetWidth); + if (nNetWriterWidth == 0) + return false; + + SwFormatCol aCol; // Create SwFormatCol + + //sprmSDxaColumns - Default distance is 1.25 cm + sal_Int32 nColSpace = rSection.StandardColSeparation(); + + const SEPr& rSep = rSection.maSep; + + // sprmSLBetween + if (rSep.fLBetween) + { + aCol.SetLineAdj(COLADJ_TOP); // Line + aCol.SetLineHeight(100); + aCol.SetLineColor(COL_BLACK); + aCol.SetLineWidth(1); + } + + aCol.Init(nCols, writer_cast<sal_uInt16>(nColSpace), nNetWriterWidth); + + // sprmSFEvenlySpaced + if (!rSep.fEvenlySpaced) + { + aCol.SetOrtho_(false); + const sal_uInt16 maxIdx = SAL_N_ELEMENTS(rSep.rgdxaColumnWidthSpacing); + for (sal_uInt16 i = 0, nIdx = 1; i < nCols && nIdx < maxIdx; i++, nIdx+=2 ) + { + SwColumn* pCol = &aCol.GetColumns()[i]; + const sal_Int32 nLeft = rSep.rgdxaColumnWidthSpacing[nIdx-1]/2; + const sal_Int32 nRight = rSep.rgdxaColumnWidthSpacing[nIdx+1]/2; + const sal_Int32 nWishWidth = rSep.rgdxaColumnWidthSpacing[nIdx] + + nLeft + nRight; + pCol->SetWishWidth(writer_cast<sal_uInt16>(nWishWidth)); + pCol->SetLeft(writer_cast<sal_uInt16>(nLeft)); + pCol->SetRight(writer_cast<sal_uInt16>(nRight)); + } + aCol.SetWishWidth(nNetWriterWidth); + } + rFormat.SetFormatAttr(aCol); + return true; +} + +void wwSectionManager::SetLeftRight(wwSection &rSection) +{ + // 3. LR-Margin + sal_uInt32 nWWLe = MSRoundTweak(rSection.maSep.dxaLeft); + sal_uInt32 nWWRi = MSRoundTweak(rSection.maSep.dxaRight); + sal_uInt32 nWWGu = rSection.maSep.dzaGutter; + + /* + fRTLGutter is set if the gutter is on the right, the gutter is otherwise + placed on the left unless the global dop options are to put it on top, that + case is handled in GetPageULData. + */ + if (rSection.maSep.fRTLGutter) + nWWRi += nWWGu; + else if (!mrReader.m_xWDop->iGutterPos) + nWWLe += nWWGu; + + // Left / Right + if ((rSection.nPgWidth - nWWLe - nWWRi) < MINLAY) + { + /* + There are some label templates which are "broken", they specify + margins which make no sense e.g. Left 16.10cm, Right 16.10cm. So the + space left between the margins is less than 0 In word the left margin + is honoured and if the right margin would be past the left margin is + left at the left margin position. + + Now this will work fine for importing, layout and exporting, *but* the + page layout dialog has a hardcoded minimum page width of 0.5cm so it + will report a different value than what is actually being used. i.e. + it will add up the values to give a wider page than is actually being + used. + */ + nWWRi = rSection.nPgWidth - nWWLe - MINLAY; + } + + rSection.nPgLeft = nWWLe; + rSection.nPgRight = nWWRi; +} + +void wwSectionManager::SetPage(SwPageDesc &rInPageDesc, SwFrameFormat &rFormat, + const wwSection &rSection, bool bIgnoreCols) +{ + // 1. orientation + rInPageDesc.SetLandscape(rSection.IsLandScape()); + + // 2. paper size + SwFormatFrameSize aSz( rFormat.GetFrameSize() ); + aSz.SetWidth(rSection.GetPageWidth()); + aSz.SetHeight(SvxPaperInfo::GetSloppyPaperDimension(rSection.GetPageHeight())); + rFormat.SetFormatAttr(aSz); + + rFormat.SetFormatAttr( + SvxLRSpaceItem(rSection.GetPageLeft(), rSection.GetPageRight(), 0, 0, RES_LR_SPACE)); + + if (!bIgnoreCols) + SetCols(rFormat, rSection, rSection.GetTextAreaWidth()); +} + +namespace { +// Returns corrected (ODF) margin size +long SetBorderDistance(bool bFromEdge, SvxBoxItem& aBox, SvxBoxItemLine eLine, long nMSMargin) +{ + const editeng::SvxBorderLine* pLine = aBox.GetLine(eLine); + if (!pLine) + return nMSMargin; + sal_Int32 nNewMargin = nMSMargin; + sal_Int32 nNewDist = aBox.GetDistance(eLine); + sal_Int32 nLineWidth = pLine->GetScaledWidth(); + + editeng::BorderDistanceFromWord(bFromEdge, nNewMargin, nNewDist, nLineWidth); + aBox.SetDistance(nNewDist, eLine); + + return nNewMargin; +} +} + +void SwWW8ImplReader::SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSection) +{ + if (!IsBorder(rSection.brc)) + return; + + SfxItemSet aSet(rFormat.GetAttrSet()); + short aSizeArray[5]={0}; + SetFlyBordersShadow(aSet, rSection.brc, &aSizeArray[0]); + SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE)); + SvxULSpaceItem aUL(ItemGet<SvxULSpaceItem>(aSet, RES_UL_SPACE)); + SvxBoxItem aBox(ItemGet<SvxBoxItem>(aSet, RES_BOX)); + bool bFromEdge = rSection.maSep.pgbOffsetFrom == 1; + + aLR.SetLeft(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::LEFT, aLR.GetLeft())); + aLR.SetRight(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::RIGHT, aLR.GetRight())); + aUL.SetUpper(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::TOP, aUL.GetUpper())); + aUL.SetLower(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::BOTTOM, aUL.GetLower())); + + aSet.Put(aBox); + aSet.Put(aLR); + aSet.Put(aUL); + rFormat.SetFormatAttr(aSet); +} + +void wwSectionManager::GetPageULData(const wwSection &rSection, + wwSectionManager::wwULSpaceData& rData) const +{ + sal_Int32 nWWUp = rSection.maSep.dyaTop; + sal_Int32 nWWLo = rSection.maSep.dyaBottom; + sal_uInt32 nWWHTop = rSection.maSep.dyaHdrTop; + sal_uInt32 nWWFBot = rSection.maSep.dyaHdrBottom; + + /* + If there is gutter in 97+ and the dop says put it on top then get the + gutter distance and set it to the top margin. When we are "two pages + in one" the gutter is put at the top of odd pages, and bottom of + even pages, something we cannot do. So we will put it on top of all + pages, that way the pages are at least the right size. + */ + if (!mrReader.m_bVer67 && mrReader.m_xWDop->iGutterPos && + rSection.maSep.fRTLGutter) + { + nWWUp += rSection.maSep.dzaGutter; + } + + /* Check whether this section has headers / footers */ + sal_uInt16 nHeaderMask = WW8_HEADER_EVEN | WW8_HEADER_ODD; + sal_uInt16 nFooterMask = WW8_FOOTER_EVEN | WW8_FOOTER_ODD; + /* Ignore the presence of a first-page header/footer unless it is enabled */ + if( rSection.HasTitlePage() ) + { + nHeaderMask |= WW8_HEADER_FIRST; + nFooterMask |= WW8_FOOTER_FIRST; + } + rData.bHasHeader = (rSection.maSep.grpfIhdt & nHeaderMask) != 0; + rData.bHasFooter = (rSection.maSep.grpfIhdt & nFooterMask) != 0; + + if( rData.bHasHeader ) + { + rData.nSwUp = nWWHTop; // Header -> convert + // #i19922# - correction: + // consider that <nWWUp> can be negative, compare only if it's positive + if ( nWWUp > 0 && + o3tl::make_unsigned(abs(nWWUp)) >= nWWHTop ) + rData.nSwHLo = nWWUp - nWWHTop; + else + rData.nSwHLo = 0; + + // #i19922# - minimum page header height is now 1mm + // use new constant <cMinHdFtHeight> + if (rData.nSwHLo < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight)) + rData.nSwHLo = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight); + } + else // no header -> just use Up as-is + rData.nSwUp = std::abs(nWWUp); + + if( rData.bHasFooter ) + { + rData.nSwLo = nWWFBot; // footer -> convert + // #i19922# - correction: consider that <nWWLo> can be negative, compare only if it's positive + if ( nWWLo > 0 && + o3tl::make_unsigned(abs(nWWLo)) >= nWWFBot ) + rData.nSwFUp = nWWLo - nWWFBot; + else + rData.nSwFUp = 0; + + // #i19922# - minimum page header height is now 1mm + // use new constant <cMinHdFtHeight> + if (rData.nSwFUp < sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight)) + rData.nSwFUp = sal::static_int_cast< sal_uInt32 >(cMinHdFtHeight); + } + else // no footer -> just use Lo as-is + rData.nSwLo = std::abs(nWWLo); +} + +void wwSectionManager::SetPageULSpaceItems(SwFrameFormat &rFormat, + wwSectionManager::wwULSpaceData const & rData, const wwSection &rSection) +{ + if (rData.bHasHeader) // ... and set Header-Lower + { + // set header height to minimum + if (SwFrameFormat* pHdFormat = const_cast<SwFrameFormat*>(rFormat.GetHeader().GetHeaderFormat())) + { + SvxULSpaceItem aHdUL(pHdFormat->GetULSpace()); + if (!rSection.IsFixedHeightHeader()) //normal + { + pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwHLo)); + // #i19922# - minimum page header height is now 1mm + // use new constant <cMinHdFtHeight> + aHdUL.SetLower( writer_cast<sal_uInt16>(rData.nSwHLo - cMinHdFtHeight) ); + pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem( + RES_HEADER_FOOTER_EAT_SPACING, true)); + } + else + { + // #i48832# - set correct spacing between header and body. + const sal_Int32 nHdLowerSpace( std::abs(rSection.maSep.dyaTop) - rData.nSwUp - rData.nSwHLo ); + pHdFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwHLo + nHdLowerSpace)); + aHdUL.SetLower( static_cast< sal_uInt16 >(nHdLowerSpace) ); + pHdFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem( + RES_HEADER_FOOTER_EAT_SPACING, false)); + } + pHdFormat->SetFormatAttr(aHdUL); + } + } + + if (rData.bHasFooter) // ... and set footer-upper + { + if (SwFrameFormat* pFtFormat = const_cast<SwFrameFormat*>(rFormat.GetFooter().GetFooterFormat())) + { + SvxULSpaceItem aFtUL(pFtFormat->GetULSpace()); + if (!rSection.IsFixedHeightFooter()) //normal + { + pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Minimum, 0, rData.nSwFUp)); + // #i19922# - minimum page header height is now 1mm + // use new constant <cMinHdFtHeight> + aFtUL.SetUpper( writer_cast<sal_uInt16>(rData.nSwFUp - cMinHdFtHeight) ); + pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem( + RES_HEADER_FOOTER_EAT_SPACING, true)); + } + else + { + // #i48832# - set correct spacing between footer and body. + const SwTwips nFtUpperSpace( std::abs(rSection.maSep.dyaBottom) - rData.nSwLo - rData.nSwFUp ); + pFtFormat->SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, 0, rData.nSwFUp + nFtUpperSpace)); + aFtUL.SetUpper( static_cast< sal_uInt16 >(nFtUpperSpace) ); + pFtFormat->SetFormatAttr(SwHeaderAndFooterEatSpacingItem( + RES_HEADER_FOOTER_EAT_SPACING, false)); + } + pFtFormat->SetFormatAttr(aFtUL); + } + } + + SvxULSpaceItem aUL(writer_cast<sal_uInt16>(rData.nSwUp), + writer_cast<sal_uInt16>(rData.nSwLo), RES_UL_SPACE); + rFormat.SetFormatAttr(aUL); +} + +SwSectionFormat *wwSectionManager::InsertSection( + SwPaM const & rMyPaM, wwSection &rSection) +{ + SwSectionData aSection( SectionType::Content, + mrReader.m_rDoc.GetUniqueSectionName() ); + + SfxItemSet aSet( mrReader.m_rDoc.GetAttrPool(), aFrameFormatSetRange ); + + bool bRTLPgn = !maSegments.empty() && maSegments.back().IsBiDi(); + aSet.Put(SvxFrameDirectionItem( + bRTLPgn ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR)); + + if (2 == mrReader.m_xWDop->fpc) + aSet.Put( SwFormatFootnoteAtTextEnd(FTNEND_ATTXTEND)); + if (0 == mrReader.m_xWDop->epc) + aSet.Put( SwFormatEndAtTextEnd(FTNEND_ATTXTEND)); + + aSection.SetProtectFlag(SectionIsProtected(rSection)); + + rSection.mpSection = + mrReader.m_rDoc.InsertSwSection( rMyPaM, aSection, nullptr, & aSet ); + OSL_ENSURE(rSection.mpSection, "section not inserted!"); + if (!rSection.mpSection) + return nullptr; + + SwPageDesc *pPage = nullptr; + auto aIter = std::find_if(maSegments.rbegin(), maSegments.rend(), + [](const wwSection& rSegment) { return rSegment.mpPage != nullptr; }); + if (aIter != maSegments.rend()) + pPage = aIter->mpPage; + + OSL_ENSURE(pPage, "no page outside this section!"); + + if (!pPage) + pPage = &mrReader.m_rDoc.GetPageDesc(0); + + SwSectionFormat *pFormat = rSection.mpSection->GetFormat(); + OSL_ENSURE(pFormat, "impossible"); + if (!pFormat) + return nullptr; + + SwFrameFormat& rFormat = pPage->GetMaster(); + const SvxLRSpaceItem& rLR = rFormat.GetLRSpace(); + long nPageLeft = rLR.GetLeft(); + long nPageRight = rLR.GetRight(); + long nSectionLeft = rSection.GetPageLeft() - nPageLeft; + long nSectionRight = rSection.GetPageRight() - nPageRight; + if ((nSectionLeft != 0) || (nSectionRight != 0)) + { + SvxLRSpaceItem aLR(nSectionLeft, nSectionRight, 0, 0, RES_LR_SPACE); + pFormat->SetFormatAttr(aLR); + } + + SetCols(*pFormat, rSection, rSection.GetTextAreaWidth()); + return pFormat; +} + +void SwWW8ImplReader::HandleLineNumbering(const wwSection &rSection) +{ + // check if Line Numbering must be activated or reset + if (m_bNewDoc && rSection.maSep.nLnnMod) + { + // restart-numbering-mode: 0 per page, 1 per section, 2 never restart + bool bRestartLnNumPerSection = (1 == rSection.maSep.lnc); + + if (m_bNoLnNumYet) + { + SwLineNumberInfo aInfo( m_rDoc.GetLineNumberInfo() ); + + aInfo.SetPaintLineNumbers(true); + + aInfo.SetRestartEachPage(rSection.maSep.lnc == 0); + + // A value of 0 (auto) indicates that the application MUST automatically determine positioning. + if ( rSection.maSep.dxaLnn ) + aInfo.SetPosFromLeft(writer_cast<sal_uInt16>(rSection.maSep.dxaLnn)); + + //Paint only for every n line + aInfo.SetCountBy(rSection.maSep.nLnnMod); + + // to be defaulted features ( HARDCODED in MS Word 6,7,8,9 ) + aInfo.SetCountBlankLines(true); + aInfo.SetCountInFlys(false); + aInfo.SetPos( LINENUMBER_POS_LEFT ); + SvxNumberType aNumType; // this sets SVX_NUM_ARABIC per default + aInfo.SetNumType( aNumType ); + + m_rDoc.SetLineNumberInfo( aInfo ); + m_bNoLnNumYet = false; + } + + if ((0 < rSection.maSep.lnnMin) || bRestartLnNumPerSection) + { + SwFormatLineNumber aLN; + if (const SwFormatLineNumber* pLN + = static_cast<const SwFormatLineNumber*>(GetFormatAttr(RES_LINENUMBER))) + { + aLN.SetCountLines( pLN->IsCount() ); + } + aLN.SetStartValue(1 + rSection.maSep.lnnMin); + NewAttr(aLN); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_LINENUMBER); + } + } +} + +wwSection::wwSection(const SwPosition &rPos) : maStart(rPos.nNode) + , mpSection(nullptr) + , mpPage(nullptr) + , meDir(SvxFrameDirection::Horizontal_LR_TB) + , nPgWidth(SvxPaperInfo::GetPaperSize(PAPER_A4).Width()) + , nPgLeft(MM_250) + , nPgRight(MM_250) + , mnVerticalAdjustment(drawing::TextVerticalAdjust_TOP) + , mnBorders(0) + , mbHasFootnote(false) +{ +} + +void wwSectionManager::SetNumberingType(const wwSection &rNewSection, + SwPageDesc &rPageDesc) +{ + // save page number format + static const SvxNumType aNumTyp[5] = + { + SVX_NUM_ARABIC, SVX_NUM_ROMAN_UPPER, SVX_NUM_ROMAN_LOWER, + SVX_NUM_CHARS_UPPER_LETTER_N, SVX_NUM_CHARS_LOWER_LETTER_N + }; + + SvxNumberType aType; + aType.SetNumberingType( aNumTyp[rNewSection.maSep.nfcPgn] ); + rPageDesc.SetNumType(aType); +} + +// CreateSep is called for every section change (even at the start of +// the document. CreateSep also creates the pagedesc(s) and +// fills it/them with attributes and KF texts. +// This has become necessary because the translation of the various +// page attributes is interconnected too much. +void wwSectionManager::CreateSep(const long nTextPos) +{ + /* + #i1909# section/page breaks should not occur in tables or subpage + elements like frames. Word itself ignores them in this case. The bug is + more likely that this filter created such documents in the past! + */ + if (mrReader.m_nInTable || mrReader.m_bTxbxFlySection || mrReader.InLocalApo()) + return; + + WW8PLCFx_SEPX* pSep = mrReader.m_xPlcxMan->GetSepPLCF(); + OSL_ENSURE(pSep, "impossible!"); + if (!pSep) + return; + + if (!maSegments.empty() && mrReader.m_pLastAnchorPos && *mrReader.m_pLastAnchorPos == *mrReader.m_pPaM->GetPoint()) + { + bool insert = true; + SwPaM pam( *mrReader.m_pLastAnchorPos ); + if( pam.Move(fnMoveBackward, GoInNode)) + if( SwTextNode* txtNode = pam.GetPoint()->nNode.GetNode().GetTextNode()) + if( txtNode->Len() == 0 ) + insert = false; + if( insert ) + mrReader.AppendTextNode(*mrReader.m_pPaM->GetPoint()); + } + + ww::WordVersion eVer = mrReader.GetFib().GetFIBVersion(); + + // M.M. Create a linked section if the WkbPLCF + // has an entry for one at this cp + WW8PLCFspecial* pWkb = mrReader.m_xPlcxMan->GetWkbPLCF(); + if (pWkb && pWkb->SeekPosExact(nTextPos) && + pWkb->Where() == nTextPos) + { + void* pData; + WW8_CP nTest; + bool bSuccess = pWkb->Get(nTest, pData); + if (!bSuccess) + return; + OUString sSectionName = mrReader.m_aLinkStringMap[SVBT16ToUInt16( static_cast<WW8_WKB*>(pData)->nLinkId) ]; + sSectionName = mrReader.ConvertFFileName(sSectionName); + SwSectionData aSection(SectionType::FileLink, sSectionName); + aSection.SetLinkFileName( sSectionName ); + aSection.SetProtectFlag(true); + // #i19922# - improvement: return value of method <Insert> not used. + mrReader.m_rDoc.InsertSwSection(*mrReader.m_pPaM, aSection, nullptr, nullptr, false); + } + + wwSection aLastSection(*mrReader.m_pPaM->GetPoint()); + if (!maSegments.empty()) + aLastSection = maSegments.back(); + + //Here + sal_uInt16 nLIdx = ( ( static_cast<sal_uInt16>(mrReader.m_xWwFib->m_lid) & 0xff ) == 0x9 ) ? 1 : 0; + + //BEGIN read section values + wwSection aNewSection(*mrReader.m_pPaM->GetPoint()); + + static const sal_uInt16 aVer2Ids0[] = + { + /*sprmSBkc*/ 117, + /*sprmSFTitlePage*/ 118, + /*sprmSNfcPgn*/ 122, + /*sprmSCcolumns*/ 119, + /*sprmSDxaColumns*/ 120, + /*sprmSLBetween*/ 133 + }; + + static const sal_uInt16 aVer67Ids0[] = + { + NS_sprm::v6::sprmSBkc, + NS_sprm::v6::sprmSFTitlePage, + NS_sprm::v6::sprmSNfcPgn, + NS_sprm::v6::sprmSCcolumns, + NS_sprm::v6::sprmSDxaColumns, + NS_sprm::v6::sprmSLBetween + }; + + static const sal_uInt16 aVer8Ids0[] = + { + NS_sprm::sprmSBkc, + NS_sprm::sprmSFTitlePage, + NS_sprm::sprmSNfcPgn, + NS_sprm::sprmSCcolumns, + NS_sprm::sprmSDxaColumns, + NS_sprm::sprmSLBetween + }; + + const sal_uInt16* pIds = eVer <= ww::eWW2 ? aVer2Ids0 : eVer <= ww::eWW7 ? aVer67Ids0 : aVer8Ids0; + + SprmResult aRes = pSep->HasSprm(pIds[0]); + const sal_uInt8* pSprmBkc = aRes.pSprm; + if (!maSegments.empty()) + { + // Type of break: break codes are: + // 0 No break + // 1 New column + // 2 New page + // 3 Even page + // 4 Odd page + if (pSprmBkc && aRes.nRemainingData >= 1) + aNewSection.maSep.bkc = *pSprmBkc; + } + + // Has a table page + aNewSection.maSep.fTitlePage = + sal_uInt8(0 != ReadBSprm( pSep, pIds[1], 0 )); + + // sprmSNfcPgn + aNewSection.maSep.nfcPgn = ReadBSprm( pSep, pIds[2], 0 ); + if (aNewSection.maSep.nfcPgn > 4) + aNewSection.maSep.nfcPgn = 0; + + aNewSection.maSep.fUnlocked = eVer > ww::eWW2 ? ReadBSprm(pSep, (eVer <= ww::eWW7 ? NS_sprm::v6::sprmSFProtected : NS_sprm::sprmSFProtected), 0 ) : 0; + + // sprmSFBiDi + aNewSection.maSep.fBiDi = eVer >= ww::eWW8 ? ReadBSprm(pSep, NS_sprm::sprmSFBiDi, 0) : 0; + + // Reading section property sprmSCcolumns - one less than the number of columns in the section. + // It must be less than MAX_NO_OF_SEP_COLUMNS according the WW8 specification. + aNewSection.maSep.ccolM1 = ReadSprm(pSep, pIds[3], 0 ); + if ( aNewSection.maSep.ccolM1 >= MAX_NO_OF_SEP_COLUMNS ) + { + // clip to max + aNewSection.maSep.ccolM1 = MAX_NO_OF_SEP_COLUMNS-1; + } + + //sprmSDxaColumns - default distance 1.25 cm + aNewSection.maSep.dxaColumns = ReadUSprm( pSep, pIds[4], 708 ); + + // sprmSLBetween + aNewSection.maSep.fLBetween = ReadBSprm(pSep, pIds[5], 0 ); + + if (eVer >= ww::eWW6) + { + // sprmSFEvenlySpaced + aNewSection.maSep.fEvenlySpaced = + sal_uInt8(ReadBSprm(pSep, (eVer <= ww::eWW7 ? NS_sprm::v6::sprmSFEvenlySpaced : NS_sprm::sprmSFEvenlySpaced), 1) != 0); + + if (aNewSection.maSep.ccolM1 > 0 && !aNewSection.maSep.fEvenlySpaced) + { + int nColumnDataIdx = 0; + aNewSection.maSep.rgdxaColumnWidthSpacing[nColumnDataIdx] = 0; + + const sal_uInt16 nColumnWidthSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColWidth : NS_sprm::sprmSDxaColWidth); + const sal_uInt16 nColumnSpacingSprmId = ( eVer <= ww::eWW7 ? NS_sprm::v6::sprmSDxaColSpacing : NS_sprm::sprmSDxaColSpacing); + const sal_uInt8 nColumnCount = static_cast< sal_uInt8 >(aNewSection.maSep.ccolM1 + 1); + for ( sal_uInt8 nColumn = 0; nColumn < nColumnCount; ++nColumn ) + { + //sprmSDxaColWidth + SprmResult aSWRes = pSep->HasSprm(nColumnWidthSprmId, nColumn); + const sal_uInt8* pSW = aSWRes.pSprm; + + OSL_ENSURE( pSW, "+Sprm 136 (resp. 0xF203) (ColWidth) missing" ); + sal_uInt16 nWidth = (pSW && aSWRes.nRemainingData >= 3) ? SVBT16ToUInt16(pSW + 1) : 1440; + + aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth; + + if ( nColumn < nColumnCount - 1 ) + { + //sprmSDxaColSpacing + SprmResult aSDRes = pSep->HasSprm(nColumnSpacingSprmId, nColumn); + const sal_uInt8* pSD = aSDRes.pSprm; + + OSL_ENSURE( pSD, "+Sprm 137 (resp. 0xF204) (Colspacing) missing" ); + if (pSD && aSDRes.nRemainingData >= 3) + { + nWidth = SVBT16ToUInt16(pSD + 1); + aNewSection.maSep.rgdxaColumnWidthSpacing[++nColumnDataIdx] = nWidth; + } + } + } + } + } + + static const sal_uInt16 aVer2Ids1[] = + { + /*sprmSBOrientation*/ 137, + /*sprmSXaPage*/ 139, + /*sprmSYaPage*/ 140, + /*sprmSDxaLeft*/ 141, + /*sprmSDxaRight*/ 142, + /*sprmSDzaGutter*/ 145, + /*sprmSFPgnRestart*/ 125, + /*sprmSPgnStart*/ 136, + /*sprmSDmBinFirst*/ 115, + /*sprmSDmBinOther*/ 116 + }; + + static const sal_uInt16 aVer67Ids1[] = + { + NS_sprm::v6::sprmSBOrientation, + NS_sprm::v6::sprmSXaPage, + NS_sprm::v6::sprmSYaPage, + NS_sprm::v6::sprmSDxaLeft, + NS_sprm::v6::sprmSDxaRight, + NS_sprm::v6::sprmSDzaGutter, + NS_sprm::v6::sprmSFPgnRestart, + NS_sprm::v6::sprmSPgnStart, + NS_sprm::v6::sprmSDmBinFirst, + NS_sprm::v6::sprmSDmBinOther + }; + + static const sal_uInt16 aVer8Ids1[] = + { + NS_sprm::sprmSBOrientation, + NS_sprm::sprmSXaPage, + NS_sprm::sprmSYaPage, + NS_sprm::sprmSDxaLeft, + NS_sprm::sprmSDxaRight, + NS_sprm::sprmSDzaGutter, + NS_sprm::sprmSFPgnRestart, + NS_sprm::sprmSPgnStart97, + NS_sprm::sprmSDmBinFirst, + NS_sprm::sprmSDmBinOther + }; + + pIds = eVer <= ww::eWW2 ? aVer2Ids1 : eVer <= ww::eWW7 ? aVer67Ids1 : aVer8Ids1; + + // 1. orientation + aNewSection.maSep.dmOrientPage = ReadBSprm(pSep, pIds[0], 0); + + // 2. paper size + aNewSection.maSep.xaPage = ReadUSprm(pSep, pIds[1], lLetterWidth); + aNewSection.nPgWidth = SvxPaperInfo::GetSloppyPaperDimension(aNewSection.maSep.xaPage); + + aNewSection.maSep.yaPage = ReadUSprm(pSep, pIds[2], lLetterHeight); + + // 3. LR borders + static const sal_uInt16 nLef[] = { MM_250, 1800 }; + static const sal_uInt16 nRig[] = { MM_250, 1800 }; + + aNewSection.maSep.dxaLeft = ReadUSprm( pSep, pIds[3], nLef[nLIdx]); + aNewSection.maSep.dxaRight = ReadUSprm( pSep, pIds[4], nRig[nLIdx]); + + // 2pages in 1sheet hackery ? + // #i31806# but only swap if 2page in 1sheet is enabled. + // it's not clear if dmOrientPage is the correct member to + // decide on this. + if(mrReader.m_xWDop->doptypography.m_f2on1 && + aNewSection.maSep.dmOrientPage == 2) + std::swap(aNewSection.maSep.dxaLeft, aNewSection.maSep.dxaRight); + + aNewSection.maSep.dzaGutter = ReadUSprm( pSep, pIds[5], 0); + + aNewSection.maSep.fRTLGutter = static_cast< sal_uInt8 >(eVer >= ww::eWW8 ? ReadUSprm( pSep, NS_sprm::sprmSFRTLGutter, 0 ) : 0); + + // Page Number Restarts - sprmSFPgnRestart + aNewSection.maSep.fPgnRestart = ReadBSprm(pSep, pIds[6], 0); + + aNewSection.maSep.pgnStart = ReadUSprm( pSep, pIds[7], 0 ); + + // if the document's first page number is unspecified, but it starts with an even page break, + // then set the first page number to two + if ( maSegments.empty() && !aNewSection.maSep.fPgnRestart && pSprmBkc && *pSprmBkc == 3 ) + { + aNewSection.maSep.pgnStart = 2; + aNewSection.maSep.fPgnRestart = 1; + } + + if (eVer >= ww::eWW6) + { + aRes = pSep->HasSprm(eVer <= ww::eWW7 ? NS_sprm::v6::sprmSiHeadingPgn : NS_sprm::sprmSiHeadingPgn); + if (aRes.pSprm && aRes.nRemainingData >= 1) + aNewSection.maSep.iHeadingPgn = *aRes.pSprm; + + aRes = pSep->HasSprm(eVer <= ww::eWW7 ? NS_sprm::v6::sprmSScnsPgn : NS_sprm::sprmScnsPgn); + if (aRes.pSprm && aRes.nRemainingData >= 1) + aNewSection.maSep.cnsPgn = *aRes.pSprm; + } + + aRes = pSep->HasSprm(pIds[8]); + const sal_uInt8* pSprmSDmBinFirst = aRes.pSprm; + if (pSprmSDmBinFirst && aRes.nRemainingData >= 1) + aNewSection.maSep.dmBinFirst = *pSprmSDmBinFirst; + + aRes = pSep->HasSprm(pIds[9]); + const sal_uInt8* pSprmSDmBinOther = aRes.pSprm; + if (pSprmSDmBinOther && aRes.nRemainingData >= 1) + aNewSection.maSep.dmBinOther = *pSprmSDmBinOther; + + static const sal_uInt16 nTop[] = { MM_250, 1440 }; + static const sal_uInt16 nBot[] = { MM_200, 1440 }; + + static const sal_uInt16 aVer2Ids2[] = + { + /*sprmSDyaTop*/ 143, + /*sprmSDyaBottom*/ 144, + /*sprmSDyaHdrTop*/ 131, + /*sprmSDyaHdrBottom*/ 132, + /*sprmSNLnnMod*/ 129, + /*sprmSLnc*/ 127, + /*sprmSDxaLnn*/ 130, + /*sprmSLnnMin*/ 135 + }; + + static const sal_uInt16 aVer67Ids2[] = + { + NS_sprm::v6::sprmSDyaTop, + NS_sprm::v6::sprmSDyaBottom, + NS_sprm::v6::sprmSDyaHdrTop, + NS_sprm::v6::sprmSDyaHdrBottom, + NS_sprm::v6::sprmSNLnnMod, + NS_sprm::v6::sprmSLnc, + NS_sprm::v6::sprmSDxaLnn, + NS_sprm::v6::sprmSLnnMin + }; + static const sal_uInt16 aVer8Ids2[] = + { + NS_sprm::sprmSDyaTop, + NS_sprm::sprmSDyaBottom, + NS_sprm::sprmSDyaHdrTop, + NS_sprm::sprmSDyaHdrBottom, + NS_sprm::sprmSNLnnMod, + NS_sprm::sprmSLnc, + NS_sprm::sprmSDxaLnn, + NS_sprm::sprmSLnnMin + }; + + pIds = eVer <= ww::eWW2 ? aVer2Ids2 : eVer <= ww::eWW7 ? aVer67Ids2 : aVer8Ids2; + + aNewSection.maSep.dyaTop = ReadSprm( pSep, pIds[0], nTop[nLIdx] ); + aNewSection.maSep.dyaBottom = ReadSprm( pSep, pIds[1], nBot[nLIdx] ); + aNewSection.maSep.dyaHdrTop = ReadUSprm( pSep, pIds[2], 720 ); + aNewSection.maSep.dyaHdrBottom = ReadUSprm( pSep, pIds[3], 720 ); + + if (eVer >= ww::eWW8) + { + aNewSection.maSep.wTextFlow = ReadUSprm(pSep, NS_sprm::sprmSTextFlow, 0); + aNewSection.maSep.clm = ReadUSprm( pSep, NS_sprm::sprmSClm, 0 ); + aNewSection.maSep.dyaLinePitch = ReadUSprm(pSep, NS_sprm::sprmSDyaLinePitch, 360); + aRes = pSep->HasSprm(NS_sprm::sprmSDxtCharSpace); + if (aRes.pSprm && aRes.nRemainingData >= 4) + aNewSection.maSep.dxtCharSpace = SVBT32ToUInt32(aRes.pSprm); + + //sprmSPgbProp + sal_uInt16 pgbProp = ReadSprm( pSep, NS_sprm::sprmSPgbProp, 0 ); + aNewSection.maSep.pgbApplyTo = pgbProp & 0x0007; + aNewSection.maSep.pgbPageDepth = (pgbProp & 0x0018) >> 3; + aNewSection.maSep.pgbOffsetFrom = (pgbProp & 0x00E0) >> 5; + + aNewSection.mnBorders = ::lcl_ReadBorders(false, aNewSection.brc, nullptr, nullptr, pSep); + } + + // check if Line Numbering must be activated or reset + SprmResult aSprmSNLnnMod = pSep->HasSprm(pIds[4]); + if (aSprmSNLnnMod.pSprm && aSprmSNLnnMod.nRemainingData >= 1) + aNewSection.maSep.nLnnMod = *aSprmSNLnnMod.pSprm; + + SprmResult aSprmSLnc = pSep->HasSprm(pIds[5]); + if (aSprmSLnc.pSprm && aSprmSLnc.nRemainingData >= 1) + aNewSection.maSep.lnc = *aSprmSLnc.pSprm; + + SprmResult aSprmSDxaLnn = pSep->HasSprm(pIds[6]); + if (aSprmSDxaLnn.pSprm && aSprmSDxaLnn.nRemainingData >= 2) + aNewSection.maSep.dxaLnn = SVBT16ToUInt16(aSprmSDxaLnn.pSprm); + + SprmResult aSprmSLnnMin = pSep->HasSprm(pIds[7]); + if (aSprmSLnnMin.pSprm && aSprmSLnnMin.nRemainingData >= 1) + aNewSection.maSep.lnnMin = *aSprmSLnnMin.pSprm; + + if (eVer <= ww::eWW7) + aNewSection.maSep.grpfIhdt = ReadBSprm(pSep, eVer <= ww::eWW2 ? 128 : 153, 0); + else if (mrReader.m_xHdFt) + { + aNewSection.maSep.grpfIhdt = WW8_HEADER_ODD | WW8_FOOTER_ODD + | WW8_HEADER_FIRST | WW8_FOOTER_FIRST; + + // It is possible for a first page header to be provided + // for this section, but not actually shown in this section. In this + // case (aNewSection.maSep.grpfIhdt & WW8_HEADER_FIRST) will be nonzero + // but aNewSection.HasTitlePage() will be false. + // Likewise for first page footer. + + if (mrReader.m_xWDop->fFacingPages) + aNewSection.maSep.grpfIhdt |= WW8_HEADER_EVEN | WW8_FOOTER_EVEN; + + //See if we have a header or footer for each enabled possibility + //if we do not then we inherit the previous sections header/footer, + for (int nI = 0, nMask = 1; nI < 6; ++nI, nMask <<= 1) + { + if (aNewSection.maSep.grpfIhdt & nMask) + { + WW8_CP nStart, nLen; + mrReader.m_xHdFt->GetTextPosExact( static_cast< short >(nI + ( maSegments.size() + 1) * 6), nStart, nLen); + //No header or footer, inherit previous one, or set to zero + //if no previous one + if (!nLen) + { + if ( + maSegments.empty() || + !(maSegments.back().maSep.grpfIhdt & nMask) + ) + { + aNewSection.maSep.grpfIhdt &= ~nMask; + } + } + } + } + } + + SetLeftRight(aNewSection); + //END read section values + + if (eVer >= ww::eWW8) + aNewSection.SetDirection(); + + mrReader.HandleLineNumbering(aNewSection); + maSegments.push_back(aNewSection); +} + +void SwWW8ImplReader::CopyPageDescHdFt(const SwPageDesc* pOrgPageDesc, + SwPageDesc* pNewPageDesc, sal_uInt8 nCode ) +{ + // copy odd header content section + if( nCode & WW8_HEADER_ODD ) + { + m_rDoc.CopyHeader(pOrgPageDesc->GetMaster(), + pNewPageDesc->GetMaster() ); + } + // copy odd footer content section + if( nCode & WW8_FOOTER_ODD ) + { + m_rDoc.CopyFooter(pOrgPageDesc->GetMaster(), + pNewPageDesc->GetMaster()); + } + // copy even header content section + if( nCode & WW8_HEADER_EVEN ) + { + m_rDoc.CopyHeader(pOrgPageDesc->GetLeft(), + pNewPageDesc->GetLeft()); + } + // copy even footer content section + if( nCode & WW8_FOOTER_EVEN ) + { + m_rDoc.CopyFooter(pOrgPageDesc->GetLeft(), + pNewPageDesc->GetLeft()); + } + // copy first page header content section + if( nCode & WW8_HEADER_FIRST ) + { + m_rDoc.CopyHeader(pOrgPageDesc->GetFirstMaster(), + pNewPageDesc->GetFirstMaster()); + } + // copy first page footer content section + if( nCode & WW8_FOOTER_FIRST ) + { + m_rDoc.CopyFooter(pOrgPageDesc->GetFirstMaster(), + pNewPageDesc->GetFirstMaster()); + } +} + +// helper functions for graphics, Apos and tables + +// Read BoRder Control structure +// nBrcVer should be set to the version of the BRC record being read (6, 8 or 9) +// This will be converted to the latest format (9). +static bool SetWW8_BRC(int nBrcVer, WW8_BRCVer9& rVar, const sal_uInt8* pS, size_t nLen) +{ + + if( pS ) + { + if (nBrcVer == 9 && nLen >= sizeof(WW8_BRCVer9)) + rVar = *reinterpret_cast<const WW8_BRCVer9*>(pS); + else if (nBrcVer == 8 && nLen >= sizeof(WW8_BRC)) + rVar = WW8_BRCVer9(*reinterpret_cast<const WW8_BRC*>(pS)); + else if (nLen >= sizeof(WW8_BRCVer6)) // nBrcVer == 6 + rVar = WW8_BRCVer9(WW8_BRC(*reinterpret_cast<const WW8_BRCVer6*>(pS))); + } + + return nullptr != pS; +} + +static sal_uInt8 lcl_ReadBorders(bool bVer67, WW8_BRCVer9* brc, WW8PLCFx_Cp_FKP* pPap, + const WW8RStyle* pSty, const WW8PLCFx_SEPX* pSep) +{ + +//returns a sal_uInt8 filled with a bit for each position that had a sprm +//setting that border + + sal_uInt8 nBorder = 0; + if( pSep ) + { + if( !bVer67 ) + { + SprmResult a8Sprm[4]; + if (pSep->Find4Sprms( + NS_sprm::sprmSBrcTop80, NS_sprm::sprmSBrcLeft80, + NS_sprm::sprmSBrcBottom80, NS_sprm::sprmSBrcRight80, + a8Sprm[0], a8Sprm[1], a8Sprm[2], a8Sprm[3])) + { + for( int i = 0; i < 4; ++i ) + nBorder |= int(SetWW8_BRC(8, brc[i], a8Sprm[i].pSprm, a8Sprm[i].nRemainingData))<<i; + } + + // Version 9 BRCs if present will override version 8 + SprmResult a9Sprm[4]; + if (pSep->Find4Sprms( + NS_sprm::sprmSBrcTop, NS_sprm::sprmSBrcLeft, + NS_sprm::sprmSBrcBottom, NS_sprm::sprmSBrcRight, + a9Sprm[0], a9Sprm[1], a9Sprm[2], a9Sprm[3])) + { + for( int i = 0; i < 4; ++i ) + nBorder |= int(SetWW8_BRC(9, brc[i], a9Sprm[i].pSprm, a9Sprm[i].nRemainingData))<<i; + } + } + } + else + { + + static const sal_uInt16 aVer67Ids[5] = { + NS_sprm::v6::sprmPBrcTop, + NS_sprm::v6::sprmPBrcLeft, + NS_sprm::v6::sprmPBrcBottom, + NS_sprm::v6::sprmPBrcRight, + NS_sprm::v6::sprmPBrcBetween + }; + + static const sal_uInt16 aVer8Ids[5] = { + NS_sprm::sprmPBrcTop80, + NS_sprm::sprmPBrcLeft80, + NS_sprm::sprmPBrcBottom80, + NS_sprm::sprmPBrcRight80, + NS_sprm::sprmPBrcBetween80 + }; + + static const sal_uInt16 aVer9Ids[5] = { + NS_sprm::sprmPBrcTop, + NS_sprm::sprmPBrcLeft, + NS_sprm::sprmPBrcBottom, + NS_sprm::sprmPBrcRight, + NS_sprm::sprmPBrcBetween + }; + + if( pPap ) + { + if (bVer67) + { + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pPap->HasSprm(aVer67Ids[i])); + nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + } + else + { + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pPap->HasSprm(aVer8Ids[i])); + nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + // Version 9 BRCs if present will override version 8 + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pPap->HasSprm(aVer9Ids[i])); + nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + } + } + else if( pSty ) + { + if (bVer67) + { + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pSty->HasParaSprm(aVer67Ids[i])); + nBorder |= int(SetWW8_BRC(6 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + } + else + { + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pSty->HasParaSprm(aVer8Ids[i])); + nBorder |= int(SetWW8_BRC(8 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + // Version 9 BRCs if present will override version 8 + for( int i = 0; i < 5; ++i ) + { + SprmResult aRes(pSty->HasParaSprm(aVer9Ids[i])); + nBorder |= int(SetWW8_BRC(9 , brc[i], aRes.pSprm, aRes.nRemainingData))<<i; + } + } + } + else { + OSL_ENSURE( pSty || pPap, "WW8PLCFx_Cp_FKP and WW8RStyle " + "and WW8PLCFx_SEPX is 0" ); + } + } + + return nBorder; +} + +static void GetLineIndex(SvxBoxItem &rBox, short nLineThickness, short nSpace, + sal_uInt32 cv, short nIdx, SvxBoxItemLine nOOIndex, sal_uInt16 nWWIndex, + short *pSize) +{ + // LO cannot handle outset/inset (new in WW9 BRC) so fall back same as WW8 + if ( nIdx == 0x1A || nIdx == 0x1B ) + { + nIdx = (nIdx == 0x1A) ? 0x12 : 0x11; + cv = 0xc0c0c0; + } + + SvxBorderLineStyle const eStyle( + ::editeng::ConvertBorderStyleFromWord(nIdx)); + + ::editeng::SvxBorderLine aLine; + aLine.SetBorderLineStyle( eStyle ); + double const fConverted( (SvxBorderLineStyle::NONE == eStyle) ? 0.0 : + ::editeng::ConvertBorderWidthFromWord(eStyle, nLineThickness, nIdx)); + aLine.SetWidth(fConverted); + + //No AUTO for borders as yet, so if AUTO, use BLACK + Color col = (cv==0xff000000) ? COL_BLACK : msfilter::util::BGRToRGB(cv); + + aLine.SetColor(col); + + if (pSize) + pSize[nWWIndex] = fConverted + nSpace; + + rBox.SetLine(&aLine, nOOIndex); + rBox.SetDistance(nSpace, nOOIndex); + +} + +static void Set1Border(SvxBoxItem &rBox, const WW8_BRCVer9& rBor, SvxBoxItemLine nOOIndex, + sal_uInt16 nWWIndex, short *pSize, const bool bIgnoreSpace) +{ + short nSpace; + short nLineThickness = rBor.DetermineBorderProperties(&nSpace); + + GetLineIndex(rBox, nLineThickness, bIgnoreSpace ? 0 : nSpace, + rBor.cv(), rBor.brcType(), nOOIndex, nWWIndex, pSize ); + +} + +static bool lcl_IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn = false) +{ + return pbrc[WW8_TOP ].brcType() || // brcType != 0 + pbrc[WW8_LEFT ].brcType() || + pbrc[WW8_BOT ].brcType() || + pbrc[WW8_RIGHT].brcType() || + (bChkBtwn && pbrc[WW8_BETW ].brcType()); +} + +bool SwWW8ImplReader::IsBorder(const WW8_BRCVer9* pbrc, bool bChkBtwn) +{ + return lcl_IsBorder(pbrc, bChkBtwn); +} + +bool SwWW8ImplReader::SetBorder(SvxBoxItem& rBox, const WW8_BRCVer9* pbrc, + short *pSizeArray, sal_uInt8 nSetBorders) +{ + bool bChange = false; + static const std::pair<sal_uInt16, SvxBoxItemLine> aIdArr[] = + { + { WW8_TOP, SvxBoxItemLine::TOP }, + { WW8_LEFT, SvxBoxItemLine::LEFT }, + { WW8_RIGHT, SvxBoxItemLine::RIGHT }, + { WW8_BOT, SvxBoxItemLine::BOTTOM }, + { WW8_BETW, SvxBoxItemLine::BOTTOM } + }; + + for( int i = 0; i < 4; ++i ) + { + // filter out the invalid borders + const WW8_BRCVer9& rB = pbrc[ aIdArr[ i ].first ]; + if( !rB.isNil() && rB.brcType() ) + { + Set1Border(rBox, rB, aIdArr[i].second, aIdArr[i].first, pSizeArray, false); + bChange = true; + } + else if ( nSetBorders & (1 << aIdArr[i].first) ) + { + /* + ##826##, ##653## + + If a style has borders set,and the para attributes attempt remove + the borders, then this is perfectly acceptable, so we shouldn't + ignore this blank entry + + nSetBorders has a bit set for each location that a sprm set a + border, so with a sprm set, but no border, then disable the + appropriate border + */ + rBox.SetLine( nullptr, aIdArr[ i ].second ); + } + } + return bChange; +} + +bool SwWW8ImplReader::SetShadow(SvxShadowItem& rShadow, const short *pSizeArray, + const WW8_BRCVer9& aRightBrc) +{ + bool bRet = aRightBrc.fShadow() && pSizeArray && pSizeArray[WW8_RIGHT]; + if (bRet) + { + rShadow.SetColor(COL_BLACK); + //i120718 + short nVal = aRightBrc.DetermineBorderProperties(); + //End + if (nVal < 0x10) + nVal = 0x10; + rShadow.SetWidth(nVal); + rShadow.SetLocation(SvxShadowLocation::BottomRight); + bRet = true; + } + return bRet; +} + +void SwWW8ImplReader::GetBorderDistance(const WW8_BRCVer9* pbrc, + tools::Rectangle& rInnerDist) +{ + rInnerDist = tools::Rectangle( pbrc[ 1 ].dptSpace() * 20, + pbrc[ 0 ].dptSpace() * 20, + pbrc[ 3 ].dptSpace() * 20, + pbrc[ 2 ].dptSpace() * 20 ); +} + +bool SwWW8ImplReader::SetFlyBordersShadow(SfxItemSet& rFlySet, + const WW8_BRCVer9 *pbrc, short *pSizeArray) +{ + bool bShadowed = false; + if (IsBorder(pbrc)) + { + SvxBoxItem aBox( RES_BOX ); + SetBorder(aBox, pbrc, pSizeArray); + + rFlySet.Put( aBox ); + + // fShadow + SvxShadowItem aShadow( RES_SHADOW ); + if( SetShadow( aShadow, pSizeArray, pbrc[WW8_RIGHT] )) + { + bShadowed = true; + rFlySet.Put( aShadow ); + } + } + return bShadowed; +} + +// APOs + + // for computing the minimal FrameSize +#define MAX_BORDER_SIZE 210 // max. size of border +#define MAX_EMPTY_BORDER 10 // for off-by-one errors, at least 1 + +static void FlySecur1(short& rSize, const bool bBorder) +{ + short nMin = MINFLY + + (bBorder ? MAX_BORDER_SIZE : MAX_EMPTY_BORDER); + + if ( rSize < nMin ) + rSize = nMin; +} + +static bool SetValSprm( sal_Int16* pVar, WW8PLCFx_Cp_FKP* pPap, sal_uInt16 nId ) +{ + SprmResult aS = pPap->HasSprm(nId); + if (aS.pSprm && aS.nRemainingData >= 2) + *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm)); + return aS.pSprm != nullptr; +} + +static bool SetValSprm( sal_Int16* pVar, const WW8RStyle* pStyle, sal_uInt16 nId ) +{ + SprmResult aS = pStyle->HasParaSprm(nId); + if (aS.pSprm && aS.nRemainingData >= 2) + *pVar = static_cast<sal_Int16>(SVBT16ToUInt16(aS.pSprm)); + return aS.pSprm != nullptr; +} + +/* +#i1930 revealed that sprm 0x360D (sprmTPc) as used in tables can affect the frame +around the table. Its full structure is not fully understood as yet. +*/ +void WW8FlyPara::ApplyTabPos(const WW8_TablePos *pTabPos) +{ + if (pTabPos) + { + nSp26 = pTabPos->nSp26; + nSp27 = pTabPos->nSp27; + nSp29 = pTabPos->nSp29; + nLeMgn = pTabPos->nLeMgn; + nRiMgn = pTabPos->nRiMgn; + nUpMgn = pTabPos->nUpMgn; + nLoMgn = pTabPos->nLoMgn; + nSp37 = pTabPos->nSp37; + } +} + +WW8FlyPara::WW8FlyPara(bool bIsVer67, const WW8FlyPara* pSrc /* = 0 */) +{ + if ( pSrc ) + memcpy( this, pSrc, sizeof( WW8FlyPara ) ); // Copy-Ctor + else + { + nSp26 = 0; + nSp27 = 0; + nSp45 = 0; + nSp28 = 0; + nLeMgn = 0; + nRiMgn = 0; + nUpMgn = 0; + nLoMgn = 0; + nSp29 = 0; + nSp37 = 2; // Default: wrapping + bBorderLines = false; + bGrafApo = false; + mbVertSet = false; + } + bVer67 = bIsVer67; +} + +bool WW8FlyPara::operator==(const WW8FlyPara& rSrc) const +{ + /* + Compare the parts that word seems to compare for equivalence. + Interestingly being autoheight or absolute height (the & 0x7fff) doesn't + matter to word + */ + return + ( + (nSp26 == rSrc.nSp26) && + (nSp27 == rSrc.nSp27) && + ((nSp45 & 0x7fff) == (rSrc.nSp45 & 0x7fff)) && + (nSp28 == rSrc.nSp28) && + (nLeMgn == rSrc.nLeMgn) && + (nRiMgn == rSrc.nRiMgn) && + (nUpMgn == rSrc.nUpMgn) && + (nLoMgn == rSrc.nLoMgn) && + (nSp29 == rSrc.nSp29) && + (nSp37 == rSrc.nSp37) + ); +} + +// Read for normal text +void WW8FlyPara::Read(sal_uInt8 nOrigSp29, WW8PLCFx_Cp_FKP* pPap) +{ + if( bVer67 ) + { + SetValSprm( &nSp26, pPap, 26 ); // X-position //sprmPDxaAbs + //set in me or in parent style + mbVertSet |= SetValSprm( &nSp27, pPap, 27 ); // Y-position //sprmPDyaAbs + SetValSprm( &nSp45, pPap, 45 ); // height //sprmPWHeightAbs + SetValSprm( &nSp28, pPap, 28 ); // width //sprmPDxaWidth + SetValSprm( &nLeMgn, pPap, 49 ); // L-border //sprmPDxaFromText + SetValSprm( &nRiMgn, pPap, 49 ); // R-border //sprmPDxaFromText + SetValSprm( &nUpMgn, pPap, 48 ); // U-border //sprmPDyaFromText + SetValSprm( &nLoMgn, pPap, 48 ); // D-border //sprmPDyaFromText + + SprmResult aS = pPap->HasSprm(NS_sprm::v6::sprmPWr); + if (aS.pSprm && aS.nRemainingData >= 1) + nSp37 = *aS.pSprm; + } + else + { + SetValSprm( &nSp26, pPap, NS_sprm::sprmPDxaAbs ); // X-position + //set in me or in parent style + mbVertSet |= SetValSprm( &nSp27, pPap, NS_sprm::sprmPDyaAbs ); // Y-position + SetValSprm( &nSp45, pPap, NS_sprm::sprmPWHeightAbs ); // height + SetValSprm( &nSp28, pPap, NS_sprm::sprmPDxaWidth ); // width + SetValSprm( &nLeMgn, pPap, NS_sprm::sprmPDxaFromText ); // L-border + SetValSprm( &nRiMgn, pPap, NS_sprm::sprmPDxaFromText ); // R-border + SetValSprm( &nUpMgn, pPap, NS_sprm::sprmPDyaFromText ); // U-border + SetValSprm( &nLoMgn, pPap, NS_sprm::sprmPDyaFromText ); // D-border + + SprmResult aS = pPap->HasSprm(NS_sprm::sprmPWr); // wrapping + if (aS.pSprm && aS.nRemainingData >= 1) + nSp37 = *aS.pSprm; + } + + if( ::lcl_ReadBorders( bVer67, brc, pPap )) // borders + bBorderLines = ::lcl_IsBorder( brc ); + + /* + #i8798# + Appears that with no dyaAbs set then the actual vert anchoring set is + ignored and we remain relative to text, so if that is the case we are 0 + from para anchor, so we update the frame to have explicitly this type of + anchoring + */ + if (!mbVertSet) + nSp29 = (nOrigSp29 & 0xCF) | 0x20; + else + nSp29 = nOrigSp29; +} + +void WW8FlyPara::ReadFull(sal_uInt8 nOrigSp29, SwWW8ImplReader* pIo) +{ + std::shared_ptr<WW8PLCFMan> xPlcxMan = pIo->m_xPlcxMan; + WW8PLCFx_Cp_FKP* pPap = xPlcxMan->GetPapPLCF(); + + Read(nOrigSp29, pPap); // read Apo parameter + + do{ // block for quick exit + if( nSp45 != 0 /* || nSp28 != 0 */ ) + break; // bGrafApo only automatic for height + if( pIo->m_xWwFib->m_fComplex ) + break; // (*pPap)++ does not work for FastSave + // -> for FastSave, no test for graphics APO + SvStream* pIoStrm = pIo->m_pStrm; + sal_uLong nPos = pIoStrm->Tell(); + WW8PLCFxSave1 aSave; + xPlcxMan->GetPap()->Save( aSave ); + bGrafApo = false; + + do{ // block for quick exit + sal_uInt8 nText[2]; + + if (!checkRead(*pIoStrm, nText, 2)) // read text + break; + + if( nText[0] != 0x01 || nText[1] != 0x0d )// only graphics + CR? + break; // no + + pPap->advance(); // next line + + // in APO ? + //sprmPPc + SprmResult aS = pPap->HasSprm( bVer67 ? NS_sprm::v6::sprmPPc : NS_sprm::sprmPPc); + + // no -> graphics Apo + if (!aS.pSprm || aS.nRemainingData < 1) + { + bGrafApo = true; + break; // end of APO + } + + ww::WordVersion eVer = pIo->GetFib().GetFIBVersion(); + WW8FlyPara *pNowStyleApo=nullptr; + sal_uInt16 nColl = pPap->GetIstd(); + ww::sti eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc( static_cast< sal_uInt8 >(nColl) ) : static_cast<ww::sti>(nColl); + while (eSti != ww::stiNil && sal::static_int_cast<size_t>(nColl) < pIo->m_vColl.size() && nullptr == (pNowStyleApo = pIo->m_vColl[nColl].m_xWWFly.get())) + { + nColl = pIo->m_vColl[nColl].m_nBase; + eSti = eVer < ww::eWW6 ? ww::GetCanonicalStiFromStc( static_cast< sal_uInt8 >(nColl) ) : static_cast<ww::sti>(nColl); + } + + WW8FlyPara aF(bVer67, pNowStyleApo); + // new FlaPara for comparison + aF.Read(*aS.pSprm, pPap); // WWPara for new Para + if( !( aF == *this ) ) // same APO? (or a new one?) + bGrafApo = true; // no -> 1-line APO + // -> graphics APO + } + while( false ); // block for quick exit + + xPlcxMan->GetPap()->Restore( aSave ); + pIoStrm->Seek( nPos ); + }while( false ); // block for quick exit +} + +// read for Apo definitions in Styledefs +void WW8FlyPara::Read(sal_uInt8 nOrigSp29, WW8RStyle const * pStyle) +{ + if (bVer67) + { + SetValSprm( &nSp26, pStyle, NS_sprm::v6::sprmPDxaAbs ); // X-position + //set in me or in parent style + mbVertSet |= SetValSprm(&nSp27, pStyle, NS_sprm::v6::sprmPDyaAbs); // Y-position + SetValSprm( &nSp45, pStyle, NS_sprm::v6::sprmPWHeightAbs ); // height + SetValSprm( &nSp28, pStyle, NS_sprm::v6::sprmPDxaWidth ); // width + SetValSprm( &nLeMgn, pStyle, NS_sprm::v6::sprmPDxaFromText ); // L-border + SetValSprm( &nRiMgn, pStyle, NS_sprm::v6::sprmPDxaFromText ); // R-border + SetValSprm( &nUpMgn, pStyle, NS_sprm::v6::sprmPDyaFromText ); // U-border + SetValSprm( &nLoMgn, pStyle, NS_sprm::v6::sprmPDyaFromText ); // D-border + + SprmResult aS = pStyle->HasParaSprm( NS_sprm::v6::sprmPWr ); // wrapping + if (aS.pSprm && aS.nRemainingData >= 1) + nSp37 = *aS.pSprm; + } + else + { + SetValSprm( &nSp26, pStyle, NS_sprm::sprmPDxaAbs ); // X-position + //set in me or in parent style + mbVertSet |= SetValSprm(&nSp27, pStyle, NS_sprm::sprmPDyaAbs); // Y-position + SetValSprm( &nSp45, pStyle, NS_sprm::sprmPWHeightAbs ); // height + SetValSprm( &nSp28, pStyle, NS_sprm::sprmPDxaWidth ); // width + SetValSprm( &nLeMgn, pStyle, NS_sprm::sprmPDxaFromText ); // L-border + SetValSprm( &nRiMgn, pStyle, NS_sprm::sprmPDxaFromText ); // R-border + SetValSprm( &nUpMgn, pStyle, NS_sprm::sprmPDyaFromText ); // U-border + SetValSprm( &nLoMgn, pStyle, NS_sprm::sprmPDyaFromText ); // D-border + + SprmResult aS = pStyle->HasParaSprm( NS_sprm::sprmPWr ); // wrapping + if (aS.pSprm && aS.nRemainingData >= 1) + nSp37 = *aS.pSprm; + } + + if (::lcl_ReadBorders(bVer67, brc, nullptr, pStyle)) // border + bBorderLines = ::lcl_IsBorder(brc); + + /* + #i8798# + Appears that with no dyaAbs set then the actual vert anchoring set is + ignored and we remain relative to text, so if that is the case we are 0 + from para anchor, so we update the frame to have explicitly this type of + anchoring + */ + if (!mbVertSet) + nSp29 = (nOrigSp29 & 0xCF) | 0x20; + else + nSp29 = nOrigSp29; +} + +bool WW8FlyPara::IsEmpty() const +{ + WW8FlyPara aEmpty(bVer67); + /* + wr of 0 like 2 appears to me to be equivalent for checking here. See + #107103# if wrong, so given that the empty is 2, if we are 0 then set + empty to 0 to make 0 equiv to 2 for empty checking + */ + OSL_ENSURE(aEmpty.nSp37 == 2, "this is not what we expect for nSp37"); + if (this->nSp37 == 0) + aEmpty.nSp37 = 0; + return aEmpty == *this; +} + +// #i18732# - changes made on behalf of CMC +WW8SwFlyPara::WW8SwFlyPara( SwPaM& rPaM, + SwWW8ImplReader& rIo, + WW8FlyPara& rWW, + const sal_uInt32 nWWPgTop, + const sal_uInt32 nPgWidth, + const sal_Int32 nIniFlyDx, + const sal_Int32 nIniFlyDy ): +pFlyFormat(nullptr), +nXPos(0), +nYPos(0), +nLeMgn(rWW.nLeMgn), +nRiMgn(rWW.nRiMgn), +nUpMgn(rWW.nUpMgn), +nLoMgn(rWW.nLoMgn), +nWidth(rWW.nSp28), +nHeight(rWW.nSp45), +nNetWidth(rWW.nSp28), +eHeightFix(SwFrameSize::Fixed), +eHRel(text::RelOrientation::PAGE_FRAME), +eVRel(text::RelOrientation::FRAME), +eVAlign(text::VertOrientation::NONE), +eHAlign(text::HoriOrientation::NONE), +eSurround(( rWW.nSp37 > 1 ) ? css::text::WrapTextMode_DYNAMIC : css::text::WrapTextMode_NONE), +nXBind(( rWW.nSp29 & 0xc0 ) >> 6), +nYBind(( rWW.nSp29 & 0x30 ) >> 4), +nNewNetWidth(MINFLY), +nLineSpace(0), +bAutoWidth(false), +bToggelPos(false) +{ + //#i119466 mapping "Around" wrap setting to "Parallel" for table + const bool bIsTable = rIo.m_xPlcxMan->HasParaSprm(NS_sprm::sprmPFInTable).pSprm; + if (bIsTable && rWW.nSp37 == 2) + eSurround = css::text::WrapTextMode_PARALLEL; + + /* + #95905#, #83307# seems to have gone away now, so re-enable parallel + wrapping support for frames in headers/footers. I don't know if we truly + have an explicitly specified behaviour for these circumstances. + */ + + if( nHeight & 0x8000 ) + { + nHeight &= 0x7fff; + eHeightFix = SwFrameSize::Minimum; + } + + if( nHeight <= MINFLY ) + { // no data, or bad data + eHeightFix = SwFrameSize::Minimum; + nHeight = MINFLY; + } + + if( nWidth <= 10 ) // auto width + { + bAutoWidth = true; + nWidth = nNetWidth = + msword_cast<sal_Int16>(nPgWidth ? nPgWidth : 2268); // 4 cm + } + if( nWidth <= MINFLY ) + nWidth = nNetWidth = MINFLY; // minimum width + + /* + See issue #i9178# for the 9 anchoring options, and make sure they stay + working if you modify the anchoring logic here. + */ + + // If the Fly is aligned left, right, up, or down, + // the outer text distance will be ignored, because otherwise + // the Fly will end up in the wrong position. + // The only problem is with inside/outside. + + //#i53725# - absolute positioned objects have to be + // anchored at-paragraph to assure its correct anchor position. + rIo.m_pLastAnchorPos.reset( new SwPosition(*rPaM.GetPoint())); + + switch (nYBind) + { + case 0: //relative to margin + eVRel = text::RelOrientation::PAGE_PRINT_AREA; + break; + case 1: //relative to page + eVRel = text::RelOrientation::PAGE_FRAME; + break; + default: //relative to text + // put in initialization part eVRel = text::RelOrientation::FRAME; + break; + } + +// #i18732# + switch( rWW.nSp27 ) // particular Y-positions ? + { + case -4: + eVAlign = text::VertOrientation::TOP; + if (nYBind < 2) + nUpMgn = 0; + break; // up + case -8: + eVAlign = text::VertOrientation::CENTER; + break; // centered + case -12: + eVAlign = text::VertOrientation::BOTTOM; + if (nYBind < 2) + nLoMgn = 0; + break; // down + default: + nYPos = rWW.nSp27 + static_cast<short>(nIniFlyDy); + break; // corrections from ini file + } + + switch( rWW.nSp26 ) // particular X-positions ? + { + case 0: + eHAlign = text::HoriOrientation::LEFT; + nLeMgn = 0; + break; // left + case -4: + eHAlign = text::HoriOrientation::CENTER; + break; // centered + case -8: + eHAlign = text::HoriOrientation::RIGHT; + nRiMgn = 0; + break; // right + case -12: + eHAlign = text::HoriOrientation::LEFT; + bToggelPos = true; + break; // inside + case -16: + eHAlign = text::HoriOrientation::RIGHT; + bToggelPos = true; + break; // outside + default: + nXPos = rWW.nSp26 + static_cast<short>(nIniFlyDx); + break; // corrections from ini file + } + +// #i18732# + switch (nXBind) // X - binding -> transform coordinates + { + case 0: //relative to column + eHRel = text::RelOrientation::FRAME; + break; + case 1: //relative to margin + eHRel = text::RelOrientation::PAGE_PRINT_AREA; + break; + default: //relative to page + // put in initialization part eHRel= text::RelOrientation::PAGE_FRAME; + break; + } + + // #i36649# - adjustments for certain horizontal alignments + // Note: These special adjustments found by an investigation of documents + // containing frames with different left/right border distances and + // distances to text. The outcome is somehow strange. + // Note: These adjustments causes wrong horizontal positions for frames, + // which are aligned inside|outside to page|margin on even pages, + // the left and right border distances are different. + // no adjustments possible, if frame has automatic width. + // determine left border distance + sal_Int16 nLeBorderMgn( 0 ); + if ( !bAutoWidth ) + { + WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT]; + sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeBorderMgn); + nLeBorderMgn = nLeBorderMgn + nTemp; + } + // determine right border distance + sal_Int16 nRiBorderMgn( 0 ); + if ( !bAutoWidth ) + { + WW8_BRCVer9 &rBrc = rWW.brc[WW8_RIGHT]; + sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nRiBorderMgn); + nRiBorderMgn = nRiBorderMgn + nTemp; + } + if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_FRAME ) + { + // convert 'left to page' to + // 'from left -<width>-<2*left border distance>-<right wrap distance> + // to page text area' + eHAlign = text::HoriOrientation::NONE; + eHRel = text::RelOrientation::PAGE_PRINT_AREA; + nXPos = -nWidth - (2*nLeBorderMgn) - rWW.nRiMgn; + // re-set left wrap distance + nLeMgn = rWW.nLeMgn; + } + else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_FRAME ) + { + // convert 'right to page' to + // 'from left <right border distance-left border distance>+<left wrap distance> + // to right page border' + eHAlign = text::HoriOrientation::NONE; + eHRel = text::RelOrientation::PAGE_RIGHT; + nXPos = ( nRiBorderMgn - nLeBorderMgn ) + rWW.nLeMgn; + // re-set right wrap distance + nRiMgn = rWW.nRiMgn; + } + else if ( !bAutoWidth && eHAlign == text::HoriOrientation::LEFT && eHRel == text::RelOrientation::PAGE_PRINT_AREA ) + { + // convert 'left to margin' to + // 'from left -<left border distance> to page text area' + eHAlign = text::HoriOrientation::NONE; + eHRel = text::RelOrientation::PAGE_PRINT_AREA; + nXPos = -nLeBorderMgn; + // re-set left wrap distance + nLeMgn = rWW.nLeMgn; + } + else if ( !bAutoWidth && eHAlign == text::HoriOrientation::RIGHT && eHRel == text::RelOrientation::PAGE_PRINT_AREA ) + { + // convert 'right to margin' to + // 'from left -<width>-<left border distance> to right page border' + eHAlign = text::HoriOrientation::NONE; + eHRel = text::RelOrientation::PAGE_RIGHT; + nXPos = -nWidth - nLeBorderMgn; + // re-set right wrap distance + nRiMgn = rWW.nRiMgn; + } + else if (rWW.bBorderLines) + { + /* + #i582# + Word has a curious bug where the offset stored do not take into + account the internal distance from the corner both + */ + WW8_BRCVer9 &rBrc = rWW.brc[WW8_LEFT]; + sal_Int16 nLeLMgn = 0; + sal_Int16 nTemp = rBrc.DetermineBorderProperties(&nLeLMgn); + nLeLMgn = nLeLMgn + nTemp; + + if (nLeLMgn) + { + if (eHAlign == text::HoriOrientation::LEFT) + eHAlign = text::HoriOrientation::NONE; + nXPos = nXPos - nLeLMgn; + } + } + + // adjustments for certain vertical alignments + if ( eVAlign == text::VertOrientation::NONE && eVRel == text::RelOrientation::PAGE_PRINT_AREA ) + { + // convert "<X> from top page text area" to + // "<X + page top margin> from page" + eVRel = text::RelOrientation::PAGE_FRAME; + nYPos = static_cast< sal_Int16 >( nYPos + nWWPgTop ); + } + + FlySecur1( nWidth, rWW.bBorderLines ); // Do the borders match ? + FlySecur1( nHeight, rWW.bBorderLines ); + +} + +// If a Fly in WW has automatic width, this has to be simulated +// by modifying the Fly width (fixed in SW) afterwards. +// This can increase or decrease the Fly width, because the default value +// is set without knowledge of the contents. +void WW8SwFlyPara::BoxUpWidth( long nInWidth ) +{ + if( bAutoWidth && nInWidth > nNewNetWidth ) + nNewNetWidth = nInWidth; +}; + +// The class WW8FlySet is derived from SfxItemSet and does not +// provide more, but is easier to handle for me. +// WW8FlySet-ctor for Apos and graphics Apos +WW8FlySet::WW8FlySet(SwWW8ImplReader& rReader, const WW8FlyPara* pFW, + const WW8SwFlyPara* pFS, bool bGraf) + : SfxItemSet(rReader.m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>{}) +{ + Reader::ResetFrameFormatAttrs(*this); // remove distance/border + // position + Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR)); + +/*Below can all go when we have from left in rtl mode*/ + SwTwips nXPos = pFS->nXPos; + sal_Int16 eHRel = pFS->eHRel; + rReader.MiserableRTLGraphicsHack(nXPos, pFS->nWidth, pFS->eHAlign, eHRel); +/*Above can all go when we have from left in rtl mode*/ + Put( SwFormatHoriOrient(nXPos, pFS->eHAlign, pFS->eHRel, pFS->bToggelPos )); + Put( SwFormatVertOrient( pFS->nYPos, pFS->eVAlign, pFS->eVRel ) ); + + if (pFS->nLeMgn || pFS->nRiMgn) // set borders + Put(SvxLRSpaceItem(pFS->nLeMgn, pFS->nRiMgn, 0, 0, RES_LR_SPACE)); + + if (pFS->nUpMgn || pFS->nLoMgn) + Put(SvxULSpaceItem(pFS->nUpMgn, pFS->nLoMgn, RES_UL_SPACE)); + + //we no longer need to hack around the header/footer problems + SwFormatSurround aSurround(pFS->eSurround); + if ( pFS->eSurround == css::text::WrapTextMode_DYNAMIC ) + aSurround.SetAnchorOnly( true ); + Put( aSurround ); + + short aSizeArray[5]={0}; + SwWW8ImplReader::SetFlyBordersShadow(*this,pFW->brc,&aSizeArray[0]); + + // the 5th parameter is always 0, thus we lose nothing due to the cast + + // #i27767# + // #i35017# - constant name has changed + Put( SwFormatWrapInfluenceOnObjPos( + text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ) ); + + if( !bGraf ) + { + Put( SwFormatAnchor(WW8SwFlyPara::eAnchor) ); + // adjust size + + //Ordinarily with frames, the border width and spacing is + //placed outside the frame, making it larger. With these + //types of frames, the left right thickness and space makes + //it wider, but the top bottom spacing and border thickness + //is placed inside. + Put( SwFormatFrameSize( pFS->eHeightFix, pFS->nWidth + + aSizeArray[WW8_LEFT] + aSizeArray[WW8_RIGHT], + pFS->nHeight)); + } +} + +// WW8FlySet-ctor for character bound graphics +WW8FlySet::WW8FlySet( SwWW8ImplReader& rReader, const SwPaM* pPaM, + const WW8_PIC& rPic, long nWidth, long nHeight ) + : SfxItemSet(rReader.m_rDoc.GetAttrPool(),svl::Items<RES_FRMATR_BEGIN,RES_FRMATR_END-1>{}) +{ + Init(rReader, pPaM); + + Put(SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR)); + + short aSizeArray[5]={0}; + /* + If we have set borders then in word the graphic is displaced from the left + and top the width of the borders of those sides, and then the shadow + itself is drawn to the bottom and right of the displaced graphic. In word + the total size is that of the graphic plus the borders, plus the total + shadow around all edges, for this translation the top and left shadow + region is translated spacing around the graphic to those sides, and the + bottom and right shadow size is added to the graphic size. + */ + WW8_BRCVer9 brcVer9[4]; + for (int i = 0; i < 4; i++) + brcVer9[i] = WW8_BRCVer9(rPic.rgbrc[i]); + if (SwWW8ImplReader::SetFlyBordersShadow( *this, brcVer9, &aSizeArray[0])) + { + Put(SvxLRSpaceItem( aSizeArray[WW8_LEFT], 0, 0, 0, RES_LR_SPACE ) ); + Put(SvxULSpaceItem( aSizeArray[WW8_TOP], 0, RES_UL_SPACE )); + aSizeArray[WW8_RIGHT]*=2; + aSizeArray[WW8_BOT]*=2; + } + + Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth+aSizeArray[WW8_LEFT]+ + aSizeArray[WW8_RIGHT], nHeight+aSizeArray[WW8_TOP] + + aSizeArray[WW8_BOT]) ); +} + +void WW8FlySet::Init(const SwWW8ImplReader& rReader, const SwPaM* pPaM) +{ + Reader::ResetFrameFormatAttrs(*this); // remove distance/borders + + Put(SvxLRSpaceItem(RES_LR_SPACE)); //inline writer ole2 objects start with 0.2cm l/r + SwFormatAnchor aAnchor(RndStdIds::FLY_AS_CHAR); + + aAnchor.SetAnchor(pPaM->GetPoint()); + Put(aAnchor); + + //The horizontal default is on the baseline, the vertical is centered + //around the character center it appears + if (rReader.m_aSectionManager.CurrentSectionIsVertical()) + Put(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER,text::RelOrientation::CHAR)); + else + Put(SwFormatVertOrient(0, text::VertOrientation::TOP, text::RelOrientation::FRAME)); +} + +WW8DupProperties::WW8DupProperties(SwDoc &rDoc, SwWW8FltControlStack *pStack) + : pCtrlStck(pStack), + aChrSet(rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>{} ), + aParSet(rDoc.GetAttrPool(), svl::Items<RES_PARATR_BEGIN, RES_PARATR_END - 1>{} ) +{ + //Close any open character properties and duplicate them inside the + //first table cell + size_t nCnt = pCtrlStck->size(); + for (size_t i=0; i < nCnt; ++i) + { + const SwFltStackEntry& rEntry = (*pCtrlStck)[ i ]; + if (rEntry.bOpen) + { + if (isCHRATR(rEntry.pAttr->Which())) + { + aChrSet.Put( *rEntry.pAttr ); + + } + else if (isPARATR(rEntry.pAttr->Which())) + { + aParSet.Put( *rEntry.pAttr ); + } + } + } +} + +void WW8DupProperties::Insert(const SwPosition &rPos) +{ + for (const SfxItemSet* pSet : {&aChrSet, &aParSet}) + { + if( pSet->Count() ) + { + SfxItemIter aIter( *pSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + do + { + pCtrlStck->NewAttr(rPos, *pItem); + } while ((pItem = aIter.NextItem())); + } + } +} + +void SwWW8ImplReader::MoveInsideFly(const SwFrameFormat *pFlyFormat) +{ + WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get()); + + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false); + + // set Pam in FlyFrame + const SwFormatContent& rContent = pFlyFormat->GetContent(); + OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." ); + m_pPaM->GetPoint()->nNode = rContent.GetContentIdx()->GetIndex() + 1; + m_pPaM->GetPoint()->nContent.Assign( m_pPaM->GetContentNode(), 0 ); + + aDup.Insert(*m_pPaM->GetPoint()); +} + +SwTwips SwWW8ImplReader::MoveOutsideFly(SwFrameFormat *pFlyFormat, + const SwPosition &rPos, bool bTableJoin) +{ + SwTwips nRetWidth = 0; + if (!pFlyFormat) + return nRetWidth; + // Close all attributes, because otherwise attributes can appear + // that extend out of Flys + WW8DupProperties aDup(m_rDoc, m_xCtrlStck.get()); + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), 0, false); + + /* + #i1291 + If this fly frame consists entirely of one table inside a frame + followed by an empty paragraph then we want to delete the empty + paragraph so as to get the frame to autoshrink to the size of the + table to emulate words behaviour closer. + */ + if (bTableJoin) + { + const SwNodeIndex* pNodeIndex = pFlyFormat->GetContent(). + GetContentIdx(); + if (pNodeIndex) + { + SwNodeIndex aIdx( *pNodeIndex, 1 ), + aEnd( *pNodeIndex->GetNode().EndOfSectionNode() ); + + if (aIdx < aEnd) + { + if(aIdx.GetNode().IsTableNode()) + { + SwTableNode *pTable = aIdx.GetNode().GetTableNode(); + aIdx = *aIdx.GetNode().EndOfSectionNode(); + ++aIdx; + if ( (aIdx < aEnd) && aIdx.GetNode().IsTextNode() ) + { + SwTextNode *pNd = aIdx.GetNode().GetTextNode(); + ++aIdx; + if (aIdx == aEnd && pNd && pNd->GetText().isEmpty()) + { + //An extra pre-created by writer unused paragraph + + //delete after import is complete rather than now + //to avoid the complication of managing uncommitted + //ctrlstack properties that refer to it. + m_aExtraneousParas.insert(pNd); + + SwTable& rTable = pTable->GetTable(); + SwFrameFormat* pTableFormat = rTable.GetFrameFormat(); + + if (pTableFormat) + { + SwFormatFrameSize aSize = pTableFormat->GetFrameSize(); + aSize.SetHeightSizeType(SwFrameSize::Minimum); + aSize.SetHeight(MINLAY); + pFlyFormat->SetFormatAttr(aSize); + SwFormatHoriOrient aHori = pTableFormat->GetHoriOrient(); + // passing the table orientation of + // LEFT_AND_WIDTH to the frame seems to + // work better than FULL, especially if the + // table width exceeds the page width, however + // I am not brave enough to set it in all + // instances + pTableFormat->SetFormatAttr( SwFormatHoriOrient(0, ( aHori.GetHoriOrient() == text::HoriOrientation::LEFT_AND_WIDTH ) ? ::text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::FULL ) ); + nRetWidth = aSize.GetWidth(); + } + } + } + } + } + } + } + + *m_pPaM->GetPoint() = rPos; + aDup.Insert(*m_pPaM->GetPoint()); + return nRetWidth; +} + +std::unique_ptr<WW8FlyPara> SwWW8ImplReader::ConstructApo(const ApoTestResults &rApo, + const WW8_TablePos *pTabPos) +{ + OSL_ENSURE(rApo.HasFrame() || pTabPos, + "If no frame found, *MUST* be in a table"); + + std::unique_ptr<WW8FlyPara> pRet(new WW8FlyPara(m_bVer67, rApo.mpStyleApo)); + + // find APO parameter and test for bGrafApo + if (rApo.HasFrame()) + pRet->ReadFull(rApo.m_nSprm29, this); + + pRet->ApplyTabPos(pTabPos); + + if (pRet->IsEmpty()) + { + pRet.reset(); + } + return pRet; +} + +bool SwWW8ImplReader::IsDropCap() const +{ + // Find the DCS (Drop Cap Specifier) for the paragraph + // if does not exist or if the first three bits are 0 + // then there is no dropcap on the paragraph + WW8PLCFx_Cp_FKP *pPap = m_xPlcxMan ? m_xPlcxMan->GetPapPLCF() : nullptr; + if (pPap) + { + SprmResult aDCS; + if (m_bVer67) + aDCS = pPap->HasSprm(NS_sprm::v6::sprmPDcs); + else + aDCS = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::sprmPDcs); + if (aDCS.pSprm && aDCS.nRemainingData >= 2) + { + /* + fdct short :3 0007 drop cap type + 0 no drop cap + 1 normal drop cap + 2 drop cap in margin + */ + short nDCS = SVBT16ToUInt16(aDCS.pSprm); + if (nDCS & 7) + return true; + } + } + return false; +} + +bool SwWW8ImplReader::StartApo(const ApoTestResults &rApo, const WW8_TablePos *pTabPos) +{ + m_xWFlyPara = ConstructApo(rApo, pTabPos); + if (!m_xWFlyPara) + return false; + + // <WW8SwFlyPara> constructor has changed - new 4th parameter + // containing WW8 page top margin. + m_xSFlyPara.reset(new WW8SwFlyPara( *m_pPaM, *this, *m_xWFlyPara, + m_aSectionManager.GetWWPageTopMargin(), + m_aSectionManager.GetTextAreaWidth(), + m_nIniFlyDx, m_nIniFlyDy)); + + // If this paragraph is a Dropcap set the flag and we will deal with it later + if (IsDropCap()) + { + m_bDropCap = true; + m_xCurrentItemSet.reset(new SfxItemSet(m_rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_END - 1>{})); + return false; + } + + if (!m_xWFlyPara->bGrafApo) + { + + // Within the GrafApo text attributes have to be ignored, because + // they would apply to the following lines. The frame is only inserted + // if it is not merely positioning a single image. If it is an image + // frame, pWFlyPara and pSFlyPara are retained and the resulting + // attributes applied to the image when inserting the image. + + WW8FlySet aFlySet(*this, m_xWFlyPara.get(), m_xSFlyPara.get(), false); + + if (pTabPos && pTabPos->bNoFly) + { + m_xSFlyPara->pFlyFormat = nullptr; + } + else + { + m_xSFlyPara->pFlyFormat = m_rDoc.MakeFlySection(WW8SwFlyPara::eAnchor, + m_pPaM->GetPoint(), &aFlySet); + OSL_ENSURE(m_xSFlyPara->pFlyFormat->GetAnchor().GetAnchorId() == + WW8SwFlyPara::eAnchor, "Not the anchor type requested!"); + } + + if (m_xSFlyPara->pFlyFormat) + { + if (!m_pDrawModel) + GrafikCtor(); + + SdrObject* pOurNewObject = CreateContactObject(m_xSFlyPara->pFlyFormat); + m_xWWZOrder->InsertTextLayerObject(pOurNewObject); + } + + if (RndStdIds::FLY_AS_CHAR != WW8SwFlyPara::eAnchor && m_xSFlyPara->pFlyFormat) + { + m_xAnchorStck->AddAnchor(*m_pPaM->GetPoint(), m_xSFlyPara->pFlyFormat); + } + + // remember Pos in body text + m_xSFlyPara->xMainTextPos.reset(new SwPosition(*m_pPaM->GetPoint())); + + //remove fltanchors, otherwise they will be closed inside the + //frame, which makes no sense, restore them after the frame is + //closed + m_xSFlyPara->xOldAnchorStck = std::move(m_xAnchorStck); + m_xAnchorStck.reset(new SwWW8FltAnchorStack(&m_rDoc, m_nFieldFlags)); + + if (m_xSFlyPara->pFlyFormat) + MoveInsideFly(m_xSFlyPara->pFlyFormat); + + // 1) ReadText() is not called recursively because the length of + // the Apo is unknown at that time, and ReadText() needs it. + // 2) the CtrlStck is not re-created. + // the Char attributes continue (trouble with Sw-attributes) + // Para attributes must be reset at the end of every paragraph, + // i.e. at the end of a paragraph there must not be para attributes + // on the stack + } + return true; +} + +void wwSectionManager::JoinNode(const SwPosition &rPos, const SwNode &rNode) +{ + if ((!maSegments.empty()) && (maSegments.back().maStart == rPos.nNode)) + maSegments.back().maStart.Assign(rNode); +} + +bool SwWW8ImplReader::JoinNode(SwPaM &rPam, bool bStealAttr) +{ + bool bRet = false; + rPam.GetPoint()->nContent = 0; // go to start of paragraph + + SwNodeIndex aPref(rPam.GetPoint()->nNode, -1); + + if (SwTextNode* pNode = aPref.GetNode().GetTextNode()) + { + m_aSectionManager.JoinNode(*rPam.GetPoint(), aPref.GetNode()); + rPam.GetPoint()->nNode = aPref; + rPam.GetPoint()->nContent.Assign(pNode, pNode->GetText().getLength()); + if (bStealAttr) + m_xCtrlStck->StealAttr(rPam.GetPoint()->nNode); + + if (m_pLastAnchorPos || m_pPreviousNode || (m_xSFlyPara && m_xSFlyPara->xMainTextPos)) + { + SwNodeIndex aToBeJoined(aPref, 1); + + if (m_pLastAnchorPos) + { + //If the last anchor pos is here, then clear the anchor pos. + //This "last anchor pos" is only used for fixing up the + //positions of things anchored to page breaks and here + //we are removing the last paragraph of a frame, so there + //cannot be a page break at this point so we can + //safely reset m_pLastAnchorPos to avoid any dangling + //SwIndex's pointing into the deleted paragraph + SwNodeIndex aLastAnchorPos(m_pLastAnchorPos->nNode); + if (aLastAnchorPos == aToBeJoined) + m_pLastAnchorPos.reset(); + } + + if (m_pPreviousNode) + { + //If the drop character start pos is here, then clear it. + SwNodeIndex aDropCharPos(*m_pPreviousNode); + if (aDropCharPos == aToBeJoined) + m_pPreviousNode = nullptr; + } + + if (m_xSFlyPara && m_xSFlyPara->xMainTextPos) + { + // If an open apo pos is here, then clear it before + // JoinNext destroys it + SwNodeIndex aOpenApoPos(m_xSFlyPara->xMainTextPos->nNode); + if (aOpenApoPos == aToBeJoined) + m_xSFlyPara->xMainTextPos.reset(); + } + } + + pNode->JoinNext(); + + bRet = true; + } + return bRet; +} + +//In auto-width word frames negative after-indent values are ignored +void SwWW8ImplReader::StripNegativeAfterIndent(SwFrameFormat const *pFlyFormat) +{ + const SwNodeIndex* pSttNd = pFlyFormat->GetContent().GetContentIdx(); + if (!pSttNd) + return; + + SwNodeIndex aIdx(*pSttNd, 1); + SwNodeIndex aEnd(*pSttNd->GetNode().EndOfSectionNode()); + while (aIdx < aEnd) + { + SwTextNode *pNd = aIdx.GetNode().GetTextNode(); + if (pNd) + { + const SvxLRSpaceItem& rLR = ItemGet<SvxLRSpaceItem>(*pNd, RES_LR_SPACE); + if (rLR.GetRight() < 0) + { + SvxLRSpaceItem aLR(rLR); + aLR.SetRight(0); + pNd->SetAttr(aLR); + } + } + ++aIdx; + } +} + +void SwWW8ImplReader::StopApo() +{ + OSL_ENSURE(m_xWFlyPara, "no pWFlyPara to close"); + if (!m_xWFlyPara) + return; + if (m_xWFlyPara->bGrafApo) + { + // image frame that has not been inserted: delete empty paragraph + attr + JoinNode(*m_pPaM, true); + + } + else + { + if (!m_xSFlyPara->xMainTextPos) + { + OSL_ENSURE(m_xSFlyPara->xMainTextPos, "StopApo: xMainTextPos is nullptr"); + return; + } + + /* + What we are doing with this temporary nodeindex is as follows: The + stack of attributes normally only places them into the document when + the current insertion point has passed them by. Otherwise the end + point of the attribute gets pushed along with the insertion point. The + insertion point is moved and the properties committed during + MoveOutsideFly. We also may want to remove the final paragraph in the + frame, but we need to wait until the properties for that frame text + have been committed otherwise they will be lost. So we first get a + handle to the last the filter inserted. After the attributes are + committed, if that paragraph exists we join it with the para after it + that comes with the frame by default so that as normal we don't end up + with one more paragraph than we wanted. + */ + SwNodeIndex aPref(m_pPaM->GetPoint()->nNode, -1); + + SwTwips nNewWidth = + MoveOutsideFly(m_xSFlyPara->pFlyFormat, *m_xSFlyPara->xMainTextPos); + if (nNewWidth) + m_xSFlyPara->BoxUpWidth(nNewWidth); + + Color aBg(0xFE, 0xFF, 0xFF, 0xFF); //Transparent by default + + SwTextNode* pNd = aPref.GetNode().GetTextNode(); + SwTextNode* pJoinNext = nullptr; + if (pNd && m_xSFlyPara->pFlyFormat) + { + /* + #i582# + Take the last paragraph background colour and fill the frame with + it. Otherwise, make it transparent, this appears to be how MSWord + works + */ + const SfxPoolItem &rItm = pNd->SwContentNode::GetAttr(RES_BACKGROUND); + const SvxBrushItem &rBrush = static_cast<const SvxBrushItem&>(rItm); + if (rBrush.GetColor() != COL_AUTO) + aBg = rBrush.GetColor(); + + if (m_pLastAnchorPos) + { + //If the last anchor pos is here, then clear the anchor pos. + //This "last anchor pos" is only used for fixing up the + //positions of things anchored to page breaks and here + //we are removing the last paragraph of a frame, so there + //cannot be a page break at this point so we can + //safely reset m_pLastAnchorPos to avoid any dangling + //SwIndex's pointing into the deleted paragraph + SwNodeIndex aLastAnchorPos(m_pLastAnchorPos->nNode); + SwNodeIndex aToBeJoined(aPref, 1); + if (aLastAnchorPos == aToBeJoined) + m_pLastAnchorPos.reset(); + } + + //Get rid of extra empty paragraph + pJoinNext = pNd; + } + + if (m_xSFlyPara->pFlyFormat) + m_xSFlyPara->pFlyFormat->SetFormatAttr(SvxBrushItem(aBg, RES_BACKGROUND)); + + DeleteAnchorStack(); + if (pJoinNext) + pJoinNext->JoinNext(); + + m_xAnchorStck = std::move(m_xSFlyPara->xOldAnchorStck); + + // When inserting a graphic into the fly frame using the auto + // function, the extension of the SW-fly has to be set + // manually as the SW fly has no auto function to adjust the + // frame´s size. + if (m_xSFlyPara->nNewNetWidth > MINFLY && m_xSFlyPara->pFlyFormat) // BoxUpWidth ? + { + long nW = m_xSFlyPara->nNewNetWidth; + nW += m_xSFlyPara->nWidth - m_xSFlyPara->nNetWidth; // border for it + m_xSFlyPara->pFlyFormat->SetFormatAttr( + SwFormatFrameSize(m_xSFlyPara->eHeightFix, nW, m_xSFlyPara->nHeight)); + } + /* + Word set *no* width meaning it's an automatic width. The + SwFlyPara reader will have already set a fallback width of the + printable regions width, so we should reuse it. Despite the related + problems with layout addressed with a hack in WW8FlyPara's constructor + #i27204# Added AutoWidth setting. Left the old CalculateFlySize in place + so that if the user unselects autowidth, the width doesn't max out + */ + else if (!m_xWFlyPara->nSp28 && m_xSFlyPara->pFlyFormat) + { + using namespace sw::util; + SfxItemSet aFlySet( m_xSFlyPara->pFlyFormat->GetAttrSet() ); + + SwFormatFrameSize aSize(ItemGet<SwFormatFrameSize>(aFlySet, RES_FRM_SIZE)); + + aFlySet.ClearItem(RES_FRM_SIZE); + + CalculateFlySize(aFlySet, m_xSFlyPara->xMainTextPos->nNode, + m_xSFlyPara->nWidth); + + nNewWidth = ItemGet<SwFormatFrameSize>(aFlySet, RES_FRM_SIZE).GetWidth(); + + aSize.SetWidth(nNewWidth); + aSize.SetWidthSizeType(SwFrameSize::Variable); + + m_xSFlyPara->pFlyFormat->SetFormatAttr(aSize); + } + + m_xSFlyPara->xMainTextPos.reset(); +// To create the SwFrames when inserting into an existing document, fltshell.cxx +// will call pFlyFrame->MakeFrames() when setting the FltAnchor attribute + + } + + //#i8062# + if (m_xSFlyPara && m_xSFlyPara->pFlyFormat) + m_xFormatOfJustInsertedApo.reset(new FrameDeleteWatch(m_xSFlyPara->pFlyFormat)); + + m_xSFlyPara.reset(); + m_xWFlyPara.reset(); +} + +// TestSameApo() returns if it's the same Apo or a different one +bool SwWW8ImplReader::TestSameApo(const ApoTestResults &rApo, + const WW8_TablePos *pTabPos) +{ + if (!m_xWFlyPara) + { + OSL_ENSURE(m_xWFlyPara, " Where is my pWFlyPara ? "); + return true; + } + + // We need to a full comparison (excepting borders) to identify all + // combinations style/hard correctly. For this reason we create a + // temporary WW8FlyPara (depending on if style or not), apply the + // hard attributes and then compare. + + // For comparison + WW8FlyPara aF(m_bVer67, rApo.mpStyleApo); + // WWPara for current para + if (rApo.HasFrame()) + aF.Read(rApo.m_nSprm29, m_xPlcxMan->GetPapPLCF()); + aF.ApplyTabPos(pTabPos); + + return aF == *m_xWFlyPara; +} + +void SwWW8ImplReader::NewAttr( const SfxPoolItem& rAttr, + const bool bFirstLineOfStSet, + const bool bLeftIndentSet ) +{ + if( !m_bNoAttrImport ) // for ignoring styles during doc inserts + { + if (m_pCurrentColl) + { + OSL_ENSURE(rAttr.Which() != RES_FLTR_REDLINE, "redline in style!"); + m_pCurrentColl->SetFormatAttr(rAttr); + } + else if (m_xCurrentItemSet) + { + m_xCurrentItemSet->Put(rAttr); + } + else if (rAttr.Which() == RES_FLTR_REDLINE) + { + m_xRedlineStack->open(*m_pPaM->GetPoint(), rAttr); + } + else + { + m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), rAttr); + // #i103711# + if ( bFirstLineOfStSet ) + { + const SwNode* pNd = &(m_pPaM->GetPoint()->nNode.GetNode()); + m_aTextNodesHavingFirstLineOfstSet.insert( pNd ); + } + // #i105414# + if ( bLeftIndentSet ) + { + const SwNode* pNd = &(m_pPaM->GetPoint()->nNode.GetNode()); + m_aTextNodesHavingLeftIndentSet.insert( pNd ); + } + } + + if (m_pPostProcessAttrsInfo && m_pPostProcessAttrsInfo->mbCopy) + m_pPostProcessAttrsInfo->mItemSet.Put(rAttr); + } +} + +// fetches attribute from FormatColl / Stack / Doc +const SfxPoolItem* SwWW8ImplReader::GetFormatAttr( sal_uInt16 nWhich ) +{ + const SfxPoolItem* pRet = nullptr; + if (m_pCurrentColl) + pRet = &(m_pCurrentColl->GetFormatAttr(nWhich)); + else if (m_xCurrentItemSet) + { + pRet = m_xCurrentItemSet->GetItem(nWhich); + if (!pRet) + pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr; + if (!pRet) + pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich); + } + else if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox()) + { + pRet = m_xCtrlStck->GetStackAttr(*m_pPaM->GetPoint(), nWhich); + if (!pRet) + { + if (m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_pFormat && + m_vColl[m_nCurrentColl].m_bColl) + { + pRet = &(m_vColl[m_nCurrentColl].m_pFormat->GetFormatAttr(nWhich)); + } + } + if (!pRet) + pRet = m_pStandardFormatColl ? &(m_pStandardFormatColl->GetFormatAttr(nWhich)) : nullptr; + if (!pRet) + pRet = &m_rDoc.GetAttrPool().GetDefaultItem(nWhich); + } + else + pRet = m_xCtrlStck->GetFormatAttr(*m_pPaM->GetPoint(), nWhich); + return pRet; +} + +// The methods get as parameters the token id and the length of the following +// parameters according to the table in WWScan.cxx. +void SwWW8ImplReader::Read_Special(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + { + m_bSpec = false; + return; + } + m_bSpec = ( *pData != 0 ); +} + +// Read_Obj is used for fObj and for fOle2 ! +void SwWW8ImplReader::Read_Obj(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + m_bObj = false; + else + { + m_bObj = 0 != *pData; + + if( m_bObj && m_nPicLocFc && m_bEmbeddObj ) + { + if (!m_aFieldStack.empty() && m_aFieldStack.back().mnFieldId == 56) + { + // For LINK fields, store the nObjLocFc value in the field entry + m_aFieldStack.back().mnObjLocFc = m_nPicLocFc; + } + else + { + m_nObjLocFc = m_nPicLocFc; + } + } + } +} + +void SwWW8ImplReader::Read_PicLoc(sal_uInt16 , const sal_uInt8* pData, short nLen ) +{ + if (nLen < 4) + { + m_nPicLocFc = 0; + m_bSpec = false; // Is this always correct? + } + else + { + m_nPicLocFc = SVBT32ToUInt32( pData ); + m_bSpec = true; + + if( m_bObj && m_nPicLocFc && m_bEmbeddObj ) + m_nObjLocFc = m_nPicLocFc; + } +} + +void SwWW8ImplReader::Read_POutLvl(sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 0) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_PARATR_OUTLINELEVEL); + return; + } + + if (m_pCurrentColl != nullptr) + { + SwWW8StyInf* pSI = GetStyle(m_nCurrentColl); + if (pSI && pSI->m_bColl && pSI->m_pFormat) + { + pSI->mnWW8OutlineLevel = + static_cast< sal_uInt8 >( ( (pData && nLen >= 1) ? *pData : 0 ) ); + auto nLevel = SwWW8StyInf::WW8OutlineLevelToOutlinelevel(pSI->mnWW8OutlineLevel); + if (nLevel == 0) + { + SwTextFormatColl* pTextFormatColl = static_cast<SwTextFormatColl*>(pSI->m_pFormat); + pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle(); + } + NewAttr(SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nLevel)); + } + } + else if (m_pPaM != nullptr) + { + const sal_uInt8 nOutlineLevel + = SwWW8StyInf::WW8OutlineLevelToOutlinelevel( + static_cast<sal_uInt8>(((pData && nLen >= 1) ? *pData : 0))); + NewAttr(SfxUInt16Item(RES_PARATR_OUTLINELEVEL, nOutlineLevel)); + } +} + +void SwWW8ImplReader::Read_Symbol(sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if( !m_bIgnoreText ) + { + if (nLen < (m_bVer67 ? 3 : 4)) + { + //otherwise disable after we print the char + if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox()) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_FONT ); + m_bSymbol = false; + } + else + { + // Make new Font-Attribut + // (will be closed in SwWW8ImplReader::ReadChars() ) + + //Will not be added to the charencoding stack, for styles the real + //font setting will be put in as the styles charset, and for plain + //text encoding for symbols is moot. Drawing boxes will check bSymbol + //themselves so they don't need to add it to the stack either. + if (SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_FONT)) + { + SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CJK_FONT); + SetNewFontAttr(SVBT16ToUInt16( pData ), false, RES_CHRATR_CTL_FONT); + if( m_bVer67 ) + { + //convert single byte from MS1252 to Unicode + m_cSymbol = OUString( + reinterpret_cast<const char*>(pData+2), 1, + RTL_TEXTENCODING_MS_1252).toChar(); + } + else + { + //already is Unicode + m_cSymbol = SVBT16ToUInt16( pData+2 ); + } + m_bSymbol = true; + } + } + } +} + +SwWW8StyInf *SwWW8ImplReader::GetStyle(sal_uInt16 nColl) const +{ + return const_cast<SwWW8StyInf *>(nColl < m_vColl.size() ? &m_vColl[nColl] : nullptr); +} + +// Read_BoldUsw for italic, bold, small caps, majuscule, struck out, +// contour and shadow +void SwWW8ImplReader::Read_BoldUsw( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + const int nContigiousWestern = 8; + const int nWestern = nContigiousWestern + 1; + const int nEastern = 2; + const int nCTL = 2; + const int nIds = nWestern + nEastern + nCTL; + static const sal_uInt16 nEndIds[ nIds ] = + { + RES_CHRATR_WEIGHT, RES_CHRATR_POSTURE, + RES_CHRATR_CROSSEDOUT, RES_CHRATR_CONTOUR, + RES_CHRATR_SHADOWED, RES_CHRATR_CASEMAP, + RES_CHRATR_CASEMAP, RES_CHRATR_HIDDEN, + + RES_CHRATR_CROSSEDOUT, + + RES_CHRATR_CJK_WEIGHT, RES_CHRATR_CJK_POSTURE, + + RES_CHRATR_CTL_WEIGHT, RES_CHRATR_CTL_POSTURE + }; + + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + sal_uInt8 nI; + // the attribute number for "double strike-through" breaks rank + if (NS_sprm::sprmCFDStrike == nId) + nI = nContigiousWestern; // The out of sequence western id + else + { + // The contiguous western ids + if (eVersion <= ww::eWW2) + nI = static_cast< sal_uInt8 >(nId - 60); + else if (eVersion < ww::eWW8) + nI = static_cast< sal_uInt8 >(nId - NS_sprm::v6::sprmCFBold); + else + nI = static_cast< sal_uInt8 >(nId - NS_sprm::sprmCFBold); + } + + sal_uInt16 nMask = 1 << nI; + + if (nLen < 1) + { + if (nI < 2) + { + if (eVersion <= ww::eWW6) + { + // reset the CTL Weight and Posture, because they are the same as their + // western equivalents in ww6 + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nEastern + nI ] ); + } + // reset the CJK Weight and Posture, because they are the same as their + // western equivalents in word + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nWestern + nI ] ); + } + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nEndIds[ nI ] ); + m_xCtrlStck->SetToggleAttr(nI, false); + return; + } + // value: 0 = off, 1 = on, 128 = like style, 129 contrary to style + bool bOn = *pData & 1; + SwWW8StyInf* pSI = GetStyle(m_nCurrentColl); + if (m_xPlcxMan && eVersion > ww::eWW2) + { + SprmResult aCharIstd = + m_xPlcxMan->GetChpPLCF()->HasSprm(m_bVer67 ? NS_sprm::v6::sprmCIstd : NS_sprm::sprmCIstd); + if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2) + pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm)); + } + + if( m_pCurrentColl ) // StyleDef -> remember flags + { + if (pSI) + { + // The style based on has Bit 7 set ? + if ( + pSI->m_nBase < m_vColl.size() && (*pData & 0x80) && + (m_vColl[pSI->m_nBase].m_n81Flags & nMask) + ) + { + bOn = !bOn; // invert + } + + if (bOn) + pSI->m_n81Flags |= nMask; // set flag + else + pSI->m_n81Flags &= ~nMask; // delete flag + } + } + else + { + + // in text -> look at flags + if( *pData & 0x80 ) // bit 7 set? + { + if (pSI && pSI->m_n81Flags & nMask) // and in StyleDef at ? + bOn = !bOn; // then invert + // remember on stack that this is a toggle-attribute + m_xCtrlStck->SetToggleAttr(nI, true); + } + } + + SetToggleAttr( nI, bOn ); +} + +void SwWW8ImplReader::Read_Bidi(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) //Property end + { + m_bBidi = false; + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_CHRATR_BIDIRTL); + } + else //Property start + { + m_bBidi = true; + sal_uInt8 nBidi = *pData; + NewAttr( SfxInt16Item( RES_CHRATR_BIDIRTL, (nBidi!=0)? 1 : 0 ) ); + } +} + +/* + tdf#91916, #i8726, #i42685# there is an ambiguity + around certain properties as to what they mean, + which appears to be a problem with different versions + of the file format where properties conflict, i.e. + +ooo40606-2.doc, magic is a699 + : 0x6f 0x4 0x0 0x71 0x4 0x0 +ooo40635-1.doc, magic is a699 + : 0x6f 0x4 0x0 0x71 0x4 0x0 +ooo31093/SIMPCHIN.doc, magic is a699 + : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0 + : 0x6f 0x5 0x0 0x70 0x5 0x0 +ooo31093/TRADCHIN.doc, magic is a699 + : 0x6f 0x1 0x0 0x70 0x0 0x0 0x71 0x1 0x0 +ooo31093/JAPANESE.doc, magic is a697 + : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0 +ooo31093/KOREAN.doc, magic is a698 + : 0x6f 0x2 0x0 0x70 0x0 0x0 0x71 0x2 0x0 +ooo31093-1.doc, magic is a698 + : 0x6f 0x5 0x0 0x70 0x5 0x0 +ooo31093-1.doc, magic is a698 + : 0x6f 0x5 0x0 0x70 0x5 0x0 + +meanwhile... + +ooo27954-1.doc, magic is a5dc + : 0x6f 0x1 0x81 0x71 0x2 0x4 0x0 0x74 0x2 0x20 0x0 + +ooo33251-1.doc, magic is a5dc + : 0x6f 0x1 0x81 0x71 0x2 0x3 0x0 0x74 0x2 0x1c 0x0 + +--- + +So we have the same sprm values, but different payloads, where +the a5dc versions appear to use a len argument, followed by len +bytes, while the a698<->a699 versions use a 2byte argument + +commit c2213db9ed70c1fd546482d22e36e4029c10aa45 + + INTEGRATION: CWS tl28 (1.169.24); FILE MERGED + 2006/10/25 13:40:41 tl 1.169.24.2: RESYNC: (1.169-1.170); FILE MERGED + 2006/09/20 11:55:50 hbrinkm 1.169.24.1: #i42685# applied patch + +changed 0x6f and 0x70 from Read_BoldBiDiUsw to Read_FontCode for all versions. + +In the Word for Window 2 spec we have... + 78 //sprmCMajority + 80 //sprmCFBoldBi + 81 //sprmCFItalicBi + 82 //sprmCFtcBi + 83 //sprmClidBi + 84 //sprmCIcoBi + 85 //sprmCHpsBi +as see in GetWW2SprmDispatcher, different numbers, but the sequence starts with +the same sprmCMajority as appears before 0x6f in word 6/95 + +I think the easiest explanation is that the CJK Word for Window 95, or whatever +the product was went rogue, and did their own things with at least first three +slots after sprmCMajority to do a different thing. I have no reason to think Tono +was wrong with what they do in the a698<->a699 versions, but with magic +a5dc they probably did mean sprmCFBoldBi, sprmCFItalicBi cause they have that 0x81 +pattern which has significance for those types of properties. +*/ +void SwWW8ImplReader::Read_AmbiguousSPRM(sal_uInt16 nId, const sal_uInt8* pData, + short nLen) +{ + if (m_xWwFib->m_wIdent >= 0xa697 && m_xWwFib->m_wIdent <= 0xa699) + { + Read_FontCode(nId, pData, nLen); + } + else + { + Read_BoldBiDiUsw(nId, pData, nLen); + } +} + +// Read_BoldUsw for BiDi Italic, Bold +void SwWW8ImplReader::Read_BoldBiDiUsw(sal_uInt16 nId, const sal_uInt8* pData, + short nLen) +{ + static const sal_uInt16 nEndIds[2] = + { + RES_CHRATR_CTL_WEIGHT, RES_CHRATR_CTL_POSTURE, + }; + + sal_uInt8 nI; + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + if (eVersion <= ww::eWW2) + nI = static_cast< sal_uInt8 >(nId - 80); + else if (eVersion < ww::eWW8) + nI = static_cast< sal_uInt8 >(nId - 111); + else + nI = static_cast< sal_uInt8 >(nId - NS_sprm::sprmCFBoldBi); + + OSL_ENSURE(nI <= 1, "not happening"); + if (nI > 1) + return; + + sal_uInt16 nMask = 1 << nI; + + if (nLen < 1) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),nEndIds[nI]); + m_xCtrlStck->SetToggleBiDiAttr(nI, false); + return; + } + bool bOn = *pData & 1; + SwWW8StyInf* pSI = GetStyle(m_nCurrentColl); + if (m_xPlcxMan) + { + SprmResult aCharIstd = + m_xPlcxMan->GetChpPLCF()->HasSprm(m_bVer67 ? NS_sprm::v6::sprmCIstd : NS_sprm::sprmCIstd); + if (aCharIstd.pSprm && aCharIstd.nRemainingData >= 2) + pSI = GetStyle(SVBT16ToUInt16(aCharIstd.pSprm)); + } + + if (m_pCurrentColl && eVersion > ww::eWW2) // StyleDef -> remember flags + { + if (pSI) + { + if( pSI->m_nBase < m_vColl.size() // Style Based on + && ( *pData & 0x80 ) // bit 7 set? + && ( m_vColl[pSI->m_nBase].m_n81BiDiFlags & nMask ) ) // base mask? + bOn = !bOn; // invert + + if( bOn ) + pSI->m_n81BiDiFlags |= nMask; // set flag + else + pSI->m_n81BiDiFlags &= ~nMask; // delete flag + } + } + else + { + + // in text -> look at flags + if (*pData & 0x80) // Bit 7 set? + { + if (pSI && pSI->m_n81BiDiFlags & nMask) // and in StyleDef at ? + bOn = !bOn; // then invert + // remember on stack that this is a toggle-attribute + m_xCtrlStck->SetToggleBiDiAttr(nI, true); + } + } + + SetToggleBiDiAttr(nI, bOn); +} + +void SwWW8ImplReader::SetToggleBiDiAttr(sal_uInt8 nAttrId, bool bOn) +{ + switch (nAttrId) + { + case 0: + { + SvxWeightItem aAttr( bOn ? WEIGHT_BOLD : WEIGHT_NORMAL, RES_CHRATR_WEIGHT ); + aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT ); + NewAttr( aAttr ); + } + break; + case 1: + { + SvxPostureItem aAttr( bOn ? ITALIC_NORMAL : ITALIC_NONE, RES_CHRATR_POSTURE ); + aAttr.SetWhich( RES_CHRATR_CTL_POSTURE ); + NewAttr( aAttr ); + } + break; + default: + OSL_ENSURE(false, "Unhandled unknown bidi toggle attribute"); + break; + + } +} + +void SwWW8ImplReader::SetToggleAttr(sal_uInt8 nAttrId, bool bOn) +{ + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + switch (nAttrId) + { + case 0: + { + SvxWeightItem aAttr( bOn ? WEIGHT_BOLD : WEIGHT_NORMAL, RES_CHRATR_WEIGHT ); + NewAttr( aAttr ); + aAttr.SetWhich( RES_CHRATR_CJK_WEIGHT ); + NewAttr( aAttr ); + if (eVersion <= ww::eWW6) + { + aAttr.SetWhich( RES_CHRATR_CTL_WEIGHT ); + NewAttr( aAttr ); + } + } + break; + case 1: + { + SvxPostureItem aAttr( bOn ? ITALIC_NORMAL : ITALIC_NONE, RES_CHRATR_POSTURE ); + NewAttr( aAttr ); + aAttr.SetWhich( RES_CHRATR_CJK_POSTURE ); + NewAttr( aAttr ); + if (eVersion <= ww::eWW6) + { + aAttr.SetWhich( RES_CHRATR_CTL_POSTURE ); + NewAttr( aAttr ); + } + } + break; + case 2: + NewAttr(SvxCrossedOutItem(bOn ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT)); + break; + case 3: + NewAttr( SvxContourItem( bOn, RES_CHRATR_CONTOUR ) ); + break; + case 4: + NewAttr( SvxShadowedItem( bOn, RES_CHRATR_SHADOWED ) ); + break; + case 5: + NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::SmallCaps + : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) ); + break; + case 6: + NewAttr( SvxCaseMapItem( bOn ? SvxCaseMap::Uppercase + : SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP ) ); + break; + case 7: + NewAttr(SvxCharHiddenItem(bOn, RES_CHRATR_HIDDEN)); + break; + case 8: + NewAttr( SvxCrossedOutItem( bOn ? STRIKEOUT_DOUBLE + : STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT ) ); + break; + default: + OSL_ENSURE(false, "Unhandled unknown toggle attribute"); + break; + } +} + +void SwWW8ImplReader::ChkToggleAttr_( sal_uInt16 nOldStyle81Mask, + sal_uInt16 nNewStyle81Mask ) +{ + sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleAttrFlags(); + for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1) + { + if ( + (i & nToggleAttrFlags) && + ((i & nOldStyle81Mask) != (i & nNewStyle81Mask)) + ) + { + SetToggleAttr(n, (i & nOldStyle81Mask)); + } + } +} + +void SwWW8ImplReader::ChkToggleBiDiAttr_( sal_uInt16 nOldStyle81Mask, + sal_uInt16 nNewStyle81Mask ) +{ + sal_uInt16 i = 1, nToggleAttrFlags = m_xCtrlStck->GetToggleBiDiAttrFlags(); + for (sal_uInt8 n = 0; n < 7; ++n, i <<= 1) + { + if ( + (i & nToggleAttrFlags) && + ((i & nOldStyle81Mask) != (i & nNewStyle81Mask)) + ) + { + SetToggleBiDiAttr(n, (i & nOldStyle81Mask)); + } + } +} + +void SwWW8ImplReader::Read_SubSuper( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ESCAPEMENT ); + return; + } + + short nEs; + sal_uInt8 nProp; + switch( *pData ) + { + case 1: + nEs = DFLT_ESC_AUTO_SUPER; + nProp = DFLT_ESC_PROP; + break; + case 2: + nEs = DFLT_ESC_AUTO_SUB; + nProp = DFLT_ESC_PROP; + break; + default: + nEs = 0; + nProp = 100; + break; + } + NewAttr( SvxEscapementItem( nEs, nProp, RES_CHRATR_ESCAPEMENT ) ); +} + +SwFrameFormat *SwWW8ImplReader::ContainsSingleInlineGraphic(const SwPaM &rRegion) +{ + /* + For inline graphics and objects word has a hacked in feature to use + subscripting to force the graphic into a centered position on the line, so + we must check when applying sub/super to see if it the subscript range + contains only a single graphic, and if that graphic is anchored as + RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line. + */ + SwFrameFormat *pRet=nullptr; + SwNodeIndex aBegin(rRegion.Start()->nNode); + const sal_Int32 nBegin(rRegion.Start()->nContent.GetIndex()); + SwNodeIndex aEnd(rRegion.End()->nNode); + const sal_Int32 nEnd(rRegion.End()->nContent.GetIndex()); + const SwTextNode* pTNd; + const SwTextAttr* pTFlyAttr; + if ( + aBegin == aEnd && nBegin == nEnd - 1 && + nullptr != (pTNd = aBegin.GetNode().GetTextNode()) && + nullptr != (pTFlyAttr = pTNd->GetTextAttrForCharAt(nBegin, RES_TXTATR_FLYCNT)) + ) + { + const SwFormatFlyCnt& rFly = pTFlyAttr->GetFlyCnt(); + SwFrameFormat *pFlyFormat = rFly.GetFrameFormat(); + if (pFlyFormat && + (RndStdIds::FLY_AS_CHAR == pFlyFormat->GetAnchor().GetAnchorId())) + { + pRet = pFlyFormat; + } + } + return pRet; +} + +bool SwWW8ImplReader::ConvertSubToGraphicPlacement() +{ + /* + For inline graphics and objects word has a hacked in feature to use + subscripting to force the graphic into a centered position on the line, so + we must check when applying sub/super to see if it the subscript range + contains only a single graphic, and if that graphic is anchored as + RndStdIds::FLY_AS_CHAR and then we can change its anchoring to centered in the line. + */ + bool bIsGraphicPlacementHack = false; + sal_uInt16 nPos; + if (m_xCtrlStck->GetFormatStackAttr(RES_CHRATR_ESCAPEMENT, &nPos)) + { + SwPaM aRegion(*m_pPaM->GetPoint()); + + SwFltPosition aMkPos((*m_xCtrlStck)[nPos].m_aMkPos); + SwFltPosition aPtPos(*m_pPaM->GetPoint()); + + SwFrameFormat *pFlyFormat = nullptr; + if (SwFltStackEntry::MakeRegion(&m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck, aMkPos, aPtPos) + && nullptr != (pFlyFormat = ContainsSingleInlineGraphic(aRegion))) + { + m_xCtrlStck->DeleteAndDestroy(nPos); + pFlyFormat->SetFormatAttr(SwFormatVertOrient(0, text::VertOrientation::CHAR_CENTER, text::RelOrientation::CHAR)); + bIsGraphicPlacementHack = true; + } + } + return bIsGraphicPlacementHack; +} + +void SwWW8ImplReader::Read_SubSuperProp( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + if (nLen < (eVersion <= ww::eWW2 ? 1 : 2)) + { + if (!ConvertSubToGraphicPlacement()) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ESCAPEMENT ); + return; + } + + // if the fontsize for these characters is specified, make sure it is updated first + if ( m_xPlcxMan ) + { + const sal_uInt16 nFontsizeID = m_bVer67 ? NS_sprm::v6::sprmCHps : NS_sprm::sprmCHps; + const SprmResult aFontsize = m_xPlcxMan->GetChpPLCF()->HasSprm( nFontsizeID, /*bFindFirst=*/false ); + if ( aFontsize.pSprm && aFontsize.nRemainingData ) + Read_FontSize(nFontsizeID, aFontsize.pSprm, aFontsize.nRemainingData); + } + + // font position in HalfPoints + short nPos = eVersion <= ww::eWW2 ? static_cast< sal_Int8 >( *pData ) : SVBT16ToInt16( pData ); + sal_Int32 nPos2 = nPos * ( 10 * 100 ); // HalfPoints in 100 * tw + const SvxFontHeightItem* pF + = static_cast<const SvxFontHeightItem*>(GetFormatAttr(RES_CHRATR_FONTSIZE)); + OSL_ENSURE(pF, "Expected to have the fontheight available here"); + + // #i59022: Check ensure nHeight != 0. Div by zero otherwise. + sal_Int32 nHeight = 240; + if (pF != nullptr && pF->GetHeight() != 0) + nHeight = pF->GetHeight(); + nPos2 /= nHeight; // ... now in % (rounded) + if( nPos2 > MAX_ESC_POS ) + nPos2 = MAX_ESC_POS; + if( nPos2 < -MAX_ESC_POS ) + nPos2 = -MAX_ESC_POS; + SvxEscapementItem aEs( static_cast<short>(nPos2), 100, RES_CHRATR_ESCAPEMENT ); + NewAttr( aEs ); +} + +void SwWW8ImplReader::Read_Underline( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + FontLineStyle eUnderline = LINESTYLE_NONE; + bool bWordLine = false; + if (pData && nLen) + { + // Parameter: 0 = none, 1 = single, 2 = by Word, + // 3 = double, 4 = dotted, 5 = hidden + // 6 = thick, 7 = dash, 8 = dot(not used) + // 9 = dotdash 10 = dotdotdash 11 = wave + switch( *pData ) + { + case 2: bWordLine = true; + [[fallthrough]]; + case 1: eUnderline = LINESTYLE_SINGLE; break; + case 3: eUnderline = LINESTYLE_DOUBLE; break; + case 4: eUnderline = LINESTYLE_DOTTED; break; + case 7: eUnderline = LINESTYLE_DASH; break; + case 9: eUnderline = LINESTYLE_DASHDOT; break; + case 10:eUnderline = LINESTYLE_DASHDOTDOT; break; + case 6: eUnderline = LINESTYLE_BOLD; break; + case 11:eUnderline = LINESTYLE_WAVE; break; + case 20:eUnderline = LINESTYLE_BOLDDOTTED; break; + case 23:eUnderline = LINESTYLE_BOLDDASH; break; + case 39:eUnderline = LINESTYLE_LONGDASH; break; + case 55:eUnderline = LINESTYLE_BOLDLONGDASH; break; + case 25:eUnderline = LINESTYLE_BOLDDASHDOT; break; + case 26:eUnderline = LINESTYLE_BOLDDASHDOTDOT;break; + case 27:eUnderline = LINESTYLE_BOLDWAVE; break; + case 43:eUnderline = LINESTYLE_DOUBLEWAVE; break; + } + } + + // if necessary, mix up stack and exit! + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_UNDERLINE ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_WORDLINEMODE ); + } + else + { + NewAttr( SvxUnderlineItem( eUnderline, RES_CHRATR_UNDERLINE )); + if( bWordLine ) + NewAttr(SvxWordLineModeItem(true, RES_CHRATR_WORDLINEMODE)); + } +} + +/* +//The last three vary, measurements, rotation ? ? +NoBracket 78 CA 06 - 02 00 00 02 34 52 +() 78 CA 06 - 02 01 00 02 34 52 +[] 78 CA 06 - 02 02 00 02 34 52 +<> 78 CA 06 - 02 03 00 02 34 52 +{} 78 CA 06 - 02 04 00 02 34 52 +*/ +void SwWW8ImplReader::Read_DoubleLine_Rotate( sal_uInt16, const sal_uInt8* pData, + short nLen ) +{ + if (nLen < 0) // close the tag + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_TWO_LINES ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_ROTATE ); + } + else if( pData && 6 == nLen ) + { + switch( *pData ) + { + case 2: // double line + { + sal_Unicode cStt = 0, cEnd = 0; + switch( SVBT16ToUInt16( pData+1 ) ) + { + case 1: cStt = '('; cEnd = ')'; break; + case 2: cStt = '['; cEnd = ']'; break; + case 3: cStt = '<'; cEnd = '>'; break; + case 4: cStt = '{'; cEnd = '}'; break; + } + NewAttr( SvxTwoLinesItem( true, cStt, cEnd, RES_CHRATR_TWO_LINES )); + } + break; + + case 1: // rotated characters + { + bool bFitToLine = 0 != *(pData+1); + NewAttr( SvxCharRotateItem( 900, bFitToLine, RES_CHRATR_ROTATE )); + } + break; + } + } +} + +void SwWW8ImplReader::Read_TextColor( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + //Has newer colour variant, ignore this old variant + if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::sprmCCv).pSprm) + return; + + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_COLOR ); + else + { + sal_uInt8 b = *pData; // parameter: 0 = Auto, 1..16 colors + + if( b > 16 ) // unknown -> Black + b = 0; + + NewAttr( SvxColorItem(GetCol(b), RES_CHRATR_COLOR)); + if (m_pCurrentColl && m_xStyles) + m_xStyles->mbTextColChanged = true; + } +} + +void SwWW8ImplReader::Read_TextForeColor(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 4) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_COLOR ); + else + { + Color aColor(msfilter::util::BGRToRGB(SVBT32ToUInt32(pData))); + + // At least when transparency is 0xff and the color is black, Word renders that as black. + if (aColor.GetTransparency() && aColor != COL_AUTO) + { + aColor.SetTransparency(0); + } + + NewAttr(SvxColorItem(aColor, RES_CHRATR_COLOR)); + if (m_pCurrentColl && m_xStyles) + m_xStyles->mbTextColChanged = true; + } +} + +void SwWW8ImplReader::Read_UnderlineColor(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 0) + { + //because the UnderlineColor is not a standalone attribute in SW, it belongs to the underline attribute. + //And, the .doc file stores attributes separately, this attribute ends here, the "underline" + //attribute also terminates (if the character next owns underline, that will be a new underline attribute). + //so nothing is left to be done here. + return; + } + else + { + if ( m_pCurrentColl ) //importing style + { + if( SfxItemState::SET == m_pCurrentColl->GetItemState( RES_CHRATR_UNDERLINE, false ) ) + { + if (nLen >= 4) + { + const SwAttrSet& aSet = m_pCurrentColl->GetAttrSet(); + std::unique_ptr<SvxUnderlineItem> pUnderline(aSet.Get(RES_CHRATR_UNDERLINE, false).Clone()); + pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) ); + m_pCurrentColl->SetFormatAttr( *pUnderline ); + } + } + } + else if (m_xCurrentItemSet) + { + if ( SfxItemState::SET == m_xCurrentItemSet->GetItemState( RES_CHRATR_UNDERLINE, false ) ) + { + if (nLen >= 4) + { + std::unique_ptr<SvxUnderlineItem> pUnderline(m_xCurrentItemSet->Get(RES_CHRATR_UNDERLINE, false).Clone()); + pUnderline->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32(pData)) ); + m_xCurrentItemSet->Put( std::move(pUnderline) ); + } + } + } + else + { + SvxUnderlineItem* pUnderlineAttr = const_cast<SvxUnderlineItem*>(static_cast<const SvxUnderlineItem*>(m_xCtrlStck->GetOpenStackAttr( *m_pPaM->GetPoint(), RES_CHRATR_UNDERLINE ))); + if (pUnderlineAttr && nLen >= 4) + pUnderlineAttr->SetColor( msfilter::util::BGRToRGB(SVBT32ToUInt32( pData ) )); + } + } +} +bool SwWW8ImplReader::GetFontParams( sal_uInt16 nFCode, FontFamily& reFamily, + OUString& rName, FontPitch& rePitch, rtl_TextEncoding& reCharSet ) +{ + // the definitions that are the base for these tables are in windows.h + static const FontPitch ePitchA[] = + { + PITCH_DONTKNOW, PITCH_FIXED, PITCH_VARIABLE, PITCH_DONTKNOW + }; + + static const FontFamily eFamilyA[] = + { + FAMILY_DONTKNOW, FAMILY_ROMAN, FAMILY_SWISS, FAMILY_MODERN, + FAMILY_SCRIPT, FAMILY_DECORATIVE, FAMILY_DONTKNOW, FAMILY_DONTKNOW + }; + + const WW8_FFN* pF = m_xFonts->GetFont( nFCode ); // Info for it + if( !pF ) // font number unknown ? + return false; // then ignore + + rName = pF->sFontname; + + // pF->prg : Pitch + rePitch = ePitchA[pF->aFFNBase.prg]; + + // pF->chs: Charset + if( 77 == pF->aFFNBase.chs ) // Mac font in Mac Charset or + reCharSet = m_eTextCharSet; // translated to ANSI charset + else + { + // #i52786#, for word 67 we'll assume that ANSI is basically invalid, + // might be true for (above) mac as well, but would need a mac example + // that exercises this to be sure + if (m_bVer67 && pF->aFFNBase.chs == 0) + reCharSet = RTL_TEXTENCODING_DONTKNOW; + else + reCharSet = rtl_getTextEncodingFromWindowsCharset(pF->aFFNBase.chs); + } + + // make sure Font Family Code is set correctly + // at least for the most important fonts + // ( might be set wrong when Doc was not created by + // Winword but by third party program like Applixware... ) + if (rName.startsWithIgnoreAsciiCase("Tms Rmn") || + rName.startsWithIgnoreAsciiCase("Timmons") || + rName.startsWithIgnoreAsciiCase("CG Times") || + rName.startsWithIgnoreAsciiCase("MS Serif") || + rName.startsWithIgnoreAsciiCase("Garamond") || + rName.startsWithIgnoreAsciiCase("Times Roman") || + rName.startsWithIgnoreAsciiCase("Times New Roman")) + { + reFamily = FAMILY_ROMAN; + } + else if (rName.startsWithIgnoreAsciiCase("Helv") || + rName.startsWithIgnoreAsciiCase("Arial") || + rName.startsWithIgnoreAsciiCase("Univers") || + rName.startsWithIgnoreAsciiCase("LinePrinter") || + rName.startsWithIgnoreAsciiCase("Lucida Sans") || + rName.startsWithIgnoreAsciiCase("Small Fonts") || + rName.startsWithIgnoreAsciiCase("MS Sans Serif")) + { + reFamily = FAMILY_SWISS; + } + else + { + reFamily = eFamilyA[pF->aFFNBase.ff]; + } + + return true; +} + +bool SwWW8ImplReader::SetNewFontAttr(sal_uInt16 nFCode, bool bSetEnums, + sal_uInt16 nWhich) +{ + FontFamily eFamily; + OUString aName; + FontPitch ePitch; + rtl_TextEncoding eSrcCharSet; + + if( !GetFontParams( nFCode, eFamily, aName, ePitch, eSrcCharSet ) ) + { + //If we fail (and are not doing a style) then put something into the + //character encodings stack anyway so that the property end that pops + //off the stack will keep in sync + if (!m_pCurrentColl && IsListOrDropcap()) + { + if (nWhich == RES_CHRATR_CJK_FONT) + { + if (!m_aFontSrcCJKCharSets.empty()) + { + eSrcCharSet = m_aFontSrcCJKCharSets.top(); + } + else + { + eSrcCharSet = RTL_TEXTENCODING_DONTKNOW; + } + + m_aFontSrcCJKCharSets.push(eSrcCharSet); + } + else + { + if (!m_aFontSrcCharSets.empty()) + { + eSrcCharSet = m_aFontSrcCharSets.top(); + } + else + { + eSrcCharSet = RTL_TEXTENCODING_DONTKNOW; + } + + m_aFontSrcCharSets.push(eSrcCharSet); + } + } + return false; + } + + rtl_TextEncoding eDstCharSet = eSrcCharSet; + + SvxFontItem aFont( eFamily, aName, OUString(), ePitch, eDstCharSet, nWhich); + + if( bSetEnums ) + { + if( m_pCurrentColl && m_nCurrentColl < m_vColl.size() ) // StyleDef + { + switch(nWhich) + { + default: + case RES_CHRATR_FONT: + m_vColl[m_nCurrentColl].m_eLTRFontSrcCharSet = eSrcCharSet; + break; + case RES_CHRATR_CTL_FONT: + m_vColl[m_nCurrentColl].m_eRTLFontSrcCharSet = eSrcCharSet; + break; + case RES_CHRATR_CJK_FONT: + m_vColl[m_nCurrentColl].m_eCJKFontSrcCharSet = eSrcCharSet; + break; + } + } + else if (IsListOrDropcap()) + { + //Add character text encoding to stack + if (nWhich == RES_CHRATR_CJK_FONT) + m_aFontSrcCJKCharSets.push(eSrcCharSet); + else + m_aFontSrcCharSets.push(eSrcCharSet); + } + } + + NewAttr( aFont ); // ...and insert + + return true; +} + +void SwWW8ImplReader::ResetCharSetVars() +{ + OSL_ENSURE(!m_aFontSrcCharSets.empty(),"no charset to remove"); + if (!m_aFontSrcCharSets.empty()) + m_aFontSrcCharSets.pop(); +} + +void SwWW8ImplReader::ResetCJKCharSetVars() +{ + OSL_ENSURE(!m_aFontSrcCJKCharSets.empty(),"no charset to remove"); + if (!m_aFontSrcCJKCharSets.empty()) + m_aFontSrcCJKCharSets.pop(); +} + +void SwWW8ImplReader::openFont(sal_uInt16 nFCode, sal_uInt16 nId) +{ + if (SetNewFontAttr(nFCode, true, nId) && m_pCurrentColl && m_xStyles) + { + // remember for simulating default font + if (RES_CHRATR_CJK_FONT == nId) + m_xStyles->mbCJKFontChanged = true; + else if (RES_CHRATR_CTL_FONT == nId) + m_xStyles->mbCTLFontChanged = true; + else + m_xStyles->mbFontChanged = true; + } +} + +void SwWW8ImplReader::closeFont(sal_uInt16 nId) +{ + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId ); + if (nId == RES_CHRATR_CJK_FONT) + ResetCJKCharSetVars(); + else + ResetCharSetVars(); +} + +/* + Turn font on or off: +*/ +void SwWW8ImplReader::Read_FontCode( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + //Note: this function needs to be able to run multiple times on the same data. + //It is called by Read_SubSuperProp to ensure that the current fontsize is known. + + if (!m_bSymbol) // if bSymbol, the symbol's font + { // (see sprmCSymbol) is valid! + switch( nId ) + { + case 113: //WW7 + case NS_sprm::sprmCRgFtc2: //"Other" font, override with BiDi if it exists + case NS_sprm::sprmCFtcBi: //BiDi Font + nId = RES_CHRATR_CTL_FONT; + break; + case NS_sprm::v6::sprmCFtc: //WW6 + case 111: //WW7 + case NS_sprm::sprmCRgFtc0: + nId = RES_CHRATR_FONT; + break; + case 112: //WW7 + case NS_sprm::sprmCRgFtc1: + nId = RES_CHRATR_CJK_FONT; + break; + default: + return ; + } + + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + if (nLen < 2) // end of attribute + { + if (eVersion <= ww::eWW6) + { + closeFont(RES_CHRATR_CTL_FONT); + closeFont(RES_CHRATR_CJK_FONT); + } + closeFont(nId); + } + else + { + sal_uInt16 nFCode = SVBT16ToUInt16( pData ); // font number + openFont(nFCode, nId); + if (eVersion <= ww::eWW6) + { + openFont(nFCode, RES_CHRATR_CJK_FONT); + openFont(nFCode, RES_CHRATR_CTL_FONT); + } + } + } +} + +void SwWW8ImplReader::Read_FontSize( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + switch( nId ) + { + case 74: // WW2 + case NS_sprm::v6::sprmCHps: + case NS_sprm::sprmCHps: + nId = RES_CHRATR_FONTSIZE; + break; + case 85: //WW2 + case 116: //WW7 + case NS_sprm::sprmCHpsBi: + nId = RES_CHRATR_CTL_FONTSIZE; + break; + default: + return ; + } + + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + if (nLen < (eVersion <= ww::eWW2 ? 1 : 2)) // end of attribute + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId ); + if (eVersion <= ww::eWW6) // reset additionally the CTL size + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_CTL_FONTSIZE ); + if (RES_CHRATR_FONTSIZE == nId) // reset additionally the CJK size + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_CJK_FONTSIZE ); + } + else + { + // Font-Size in half points e.g. 10 = 1440 / ( 72 * 2 ) + sal_uLong nFSize = eVersion <= ww::eWW2 ? *pData : SVBT16ToUInt16(pData); + nFSize*= 10; + + SvxFontHeightItem aSz( nFSize, 100, nId ); + NewAttr( aSz ); + if (RES_CHRATR_FONTSIZE == nId) // set additionally the CJK size + { + aSz.SetWhich( RES_CHRATR_CJK_FONTSIZE ); + NewAttr( aSz ); + } + if (eVersion <= ww::eWW6) // set additionally the CTL size + { + aSz.SetWhich( RES_CHRATR_CTL_FONTSIZE ); + NewAttr( aSz ); + } + if (m_pCurrentColl && m_xStyles) // Style-Def ? + { + // remember for simulating default font size + if (nId == RES_CHRATR_CTL_FONTSIZE) + m_xStyles->mbFCTLSizeChanged = true; + else + { + m_xStyles->mbFSizeChanged = true; + if (eVersion <= ww::eWW6) + m_xStyles->mbFCTLSizeChanged= true; + } + } + } +} + +void SwWW8ImplReader::Read_CharSet(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + { // end of attribute + m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW; + return; + } + sal_uInt8 nfChsDiff = *pData; + + if (nfChsDiff && nLen >= 2) + m_eHardCharSet = rtl_getTextEncodingFromWindowsCharset( *(pData + 1) ); + else + m_eHardCharSet = RTL_TEXTENCODING_DONTKNOW; +} + +void SwWW8ImplReader::Read_Language( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + switch( nId ) + { + case NS_sprm::v6::sprmCLid: + case NS_sprm::sprmCRgLid0_80: + case NS_sprm::sprmCRgLid0: + nId = RES_CHRATR_LANGUAGE; + break; + case NS_sprm::sprmCRgLid1_80: + case NS_sprm::sprmCRgLid1: + nId = RES_CHRATR_CJK_LANGUAGE; + break; + case 83: // WW2 + case 114: // WW7 + case NS_sprm::sprmCLidBi: + nId = RES_CHRATR_CTL_LANGUAGE; + break; + default: + return; + } + + if (nLen < 2) // end of attribute + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId ); + else + { + sal_uInt16 nLang = SVBT16ToUInt16( pData ); // Language-Id + NewAttr(SvxLanguageItem(LanguageType(nLang), nId)); + } +} + +/* + Turn on character style: +*/ +void SwWW8ImplReader::Read_CColl( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) // end of attribute + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_TXTATR_CHARFMT ); + m_nCharFormat = -1; + return; + } + sal_uInt16 nId = SVBT16ToUInt16( pData ); // Style-Id (NOT Sprm-Id!) + + if( nId >= m_vColl.size() || !m_vColl[nId].m_pFormat // invalid Id? + || m_vColl[nId].m_bColl ) // or paragraph style? + return; // then ignore + + // if current on loading a TOX field, and current trying to apply a hyperlink character style, + // just ignore. For the hyperlinks inside TOX in MS Word is not same with a common hyperlink + // Character styles: without underline and blue font color. And such type style will be applied in others + // processes. + if (m_bLoadingTOXCache && m_vColl[nId].GetWWStyleId() == ww::stiHyperlink) + { + return; + } + + NewAttr( SwFormatCharFormat( static_cast<SwCharFormat*>(m_vColl[nId].m_pFormat) ) ); + m_nCharFormat = static_cast<short>(nId); +} + +/* + Narrower or wider than normal: +*/ +void SwWW8ImplReader::Read_Kern( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) // end of attribute + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_KERNING ); + return; + } + sal_Int16 nKern = SVBT16ToUInt16( pData ); // Kerning in Twips + NewAttr( SvxKerningItem( nKern, RES_CHRATR_KERNING ) ); +} + +void SwWW8ImplReader::Read_FontKern( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) // end of attribute + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_AUTOKERN ); + return; + } + sal_Int16 nAutoKern = SVBT16ToUInt16( pData ); // Kerning in Twips + NewAttr(SvxAutoKernItem(static_cast<bool>(nAutoKern), RES_CHRATR_AUTOKERN)); +} + +void SwWW8ImplReader::Read_CharShadow( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + //Has newer colour variant, ignore this old variant + if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::sprmCShd).pSprm) + return; + + if (nLen < 2) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BACKGROUND ); + } + else + { + WW8_SHD aSHD; + aSHD.SetWWValue( *reinterpret_cast<SVBT16 const *>(pData) ); + SwWW8Shade aSh( m_bVer67, aSHD ); + + NewAttr( SvxBrushItem( aSh.aColor, RES_CHRATR_BACKGROUND )); + + // Add a marker to the grabbag indicating that character background was imported from MSO shading + SfxGrabBagItem aGrabBag = *static_cast<const SfxGrabBagItem*>(GetFormatAttr(RES_CHRATR_GRABBAG)); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::makeAny(true))); + NewAttr(aGrabBag); + } +} + +void SwWW8ImplReader::Read_TextBackColor(sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen <= 0) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BACKGROUND ); + } + else + { + OSL_ENSURE(nLen == 10, "Len of para back colour not 10!"); + if (nLen != 10) + return; + Color aColour(ExtractColour(pData, m_bVer67)); + NewAttr(SvxBrushItem(aColour, RES_CHRATR_BACKGROUND)); + + // Add a marker to the grabbag indicating that character background was imported from MSO shading + SfxGrabBagItem aGrabBag = *static_cast<const SfxGrabBagItem*>(GetFormatAttr(RES_CHRATR_GRABBAG)); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + rMap.insert(std::pair<OUString, css::uno::Any>("CharShadingMarker",uno::makeAny(true))); + NewAttr(aGrabBag); + } +} + +void SwWW8ImplReader::Read_CharHighlight(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_HIGHLIGHT ); + } + else + { + sal_uInt8 b = *pData; // Parameter: 0 = Auto, 1..16 colors + + if( b > 16 ) // invalid -> Black + b = 0; // Auto -> Black + + Color aCol(GetCol(b)); + NewAttr( SvxBrushItem( aCol , RES_CHRATR_HIGHLIGHT )); + } +} + +void SwWW8ImplReader::Read_NoLineNumb(sal_uInt16 , const sal_uInt8* pData, short nLen) +{ + if (nLen < 0) // end of attribute + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_LINENUMBER ); + return; + } + SwFormatLineNumber aLN; + if (const SwFormatLineNumber* pLN + = static_cast<const SwFormatLineNumber*>(GetFormatAttr(RES_LINENUMBER))) + { + aLN.SetStartValue( pLN->GetStartValue() ); + } + + aLN.SetCountLines(pData && nLen >= 1 && (0 == *pData)); + NewAttr( aLN ); +} + +static bool lcl_HasExplicitLeft(const WW8PLCFMan *pPlcxMan, bool bVer67) +{ + WW8PLCFx_Cp_FKP *pPap = pPlcxMan ? pPlcxMan->GetPapPLCF() : nullptr; + if (pPap) + { + if (bVer67) + return pPap->HasSprm(NS_sprm::v6::sprmPDxaLeft).pSprm; + else + return (pPap->HasSprm(NS_sprm::sprmPDxaLeft80).pSprm || pPap->HasSprm(NS_sprm::sprmPDxaLeft).pSprm); + } + return false; +} + +// Sprm 16, 17 +void SwWW8ImplReader::Read_LR( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) // end of attribute + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_LR_SPACE); + return; + } + + short nPara = SVBT16ToUInt16( pData ); + + std::shared_ptr<SvxLRSpaceItem> aLR(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE)); + const SfxPoolItem* pLR = GetFormatAttr(RES_LR_SPACE); + if( pLR ) + aLR.reset(static_cast<SvxLRSpaceItem*>(pLR->Clone())); + + // Fix the regression issue: #i99822#: Discussion? + // Since the list level formatting doesn't apply into paragraph style + // for list levels of mode LABEL_ALIGNMENT.(see ww8par3.cxx + // W8ImplReader::RegisterNumFormatOnTextNode). + // Need to apply the list format to the paragraph here. + SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode(); + if( pTextNode && pTextNode->AreListLevelIndentsApplicable() ) + { + SwNumRule * pNumRule = pTextNode->GetNumRule(); + if( pNumRule ) + { + sal_uInt8 nLvl = static_cast< sal_uInt8 >(pTextNode->GetActualListLevel()); + const SwNumFormat* pFormat = pNumRule->GetNumFormat( nLvl ); + if ( pFormat && pFormat->GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + aLR->SetTextLeft( pFormat->GetIndentAt() ); + aLR->SetTextFirstLineOffset( static_cast<short>(pFormat->GetFirstLineIndent()) ); + // make paragraph have hard-set indent attributes + pTextNode->SetAttr( *aLR ); + } + } + } + + /* + The older word sprms mean left/right, while the new ones mean before/after. + Writer now also works with before after, so when we see old left/right and + we're RTL. We swap them + */ + if (IsRightToLeft()) + { + switch (nId) + { + //Left becomes after; + case NS_sprm::v6::sprmPDxaLeft: + nId = NS_sprm::v6::sprmPDxaRight; + break; + case NS_sprm::sprmPDxaLeft80: + nId = NS_sprm::sprmPDxaRight80; + break; + //Right becomes before; + case NS_sprm::v6::sprmPDxaRight: + nId = NS_sprm::v6::sprmPDxaLeft; + break; + case NS_sprm::sprmPDxaRight80: + nId = NS_sprm::sprmPDxaLeft80; + break; + } + } + + bool bFirstLinOfstSet( false ); // #i103711# + bool bLeftIndentSet( false ); // #i105414# + + switch (nId) + { + //sprmPDxaLeft + case NS_sprm::v6::sprmPDxaLeft: + case NS_sprm::sprmPDxaLeft80: + case NS_sprm::sprmPDxaLeft: + aLR->SetTextLeft( nPara ); + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + { + m_vColl[m_nCurrentColl].m_bListReleventIndentSet = true; + } + bLeftIndentSet = true; // #i105414# + break; + //sprmPDxaLeft1 + case NS_sprm::v6::sprmPDxaLeft1: + case NS_sprm::sprmPDxaLeft180: + case NS_sprm::sprmPDxaLeft1: + /* + As part of an attempt to break my spirit ww 8+ formats can contain + ww 7- lists. If they do and the list is part of the style, then + when removing the list from a paragraph of that style there + appears to be a bug where the hanging indent value which the list + set is still factored into the left indent of the paragraph. Its + not listed in the winword dialogs, but it is clearly there. So if + our style has a broken ww 7- list and we know that the list has + been removed then we will factor the original list applied hanging + into our calculation. + */ + if (m_xPlcxMan && m_nCurrentColl < m_vColl.size() && m_vColl[m_nCurrentColl].m_bHasBrokenWW6List) + { + SprmResult aIsZeroed = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::sprmPIlfo); + if (aIsZeroed.pSprm && aIsZeroed.nRemainingData >= 1 && *aIsZeroed.pSprm == 0) + { + const SvxLRSpaceItem &rLR = + ItemGet<SvxLRSpaceItem>(*(m_vColl[m_nCurrentColl].m_pFormat), + RES_LR_SPACE); + nPara = nPara - rLR.GetTextFirstLineOffset(); + } + } + + aLR->SetTextFirstLineOffset(nPara); + + if (!m_pCurrentColl) + { + if (const SwTextNode* pNode = m_pPaM->GetNode().GetTextNode()) + { + if ( const SwNumFormat *pNumFormat = GetNumFormatFromTextNode(*pNode) ) + { + if (!lcl_HasExplicitLeft(m_xPlcxMan.get(), m_bVer67)) + { + aLR->SetTextLeft(pNumFormat->GetIndentAt()); + + // If have not explicit left, set number format list tab position is doc default tab + const SvxTabStopItem *pDefaultStopItem = m_rDoc.GetAttrPool().GetPoolDefaultItem(RES_PARATR_TABSTOP); + if ( pDefaultStopItem && pDefaultStopItem->Count() > 0 ) + const_cast<SwNumFormat*>(pNumFormat)->SetListtabPos( const_cast<SvxTabStop&>((*pDefaultStopItem)[0]).GetTabPos() ); + } + } + } + } + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + { + m_vColl[m_nCurrentColl].m_bListReleventIndentSet = true; + } + bFirstLinOfstSet = true; // #i103711# + break; + //sprmPDxaRight + case NS_sprm::v6::sprmPDxaRight: + case NS_sprm::sprmPDxaRight80: + case NS_sprm::sprmPDxaRight: + aLR->SetRight( nPara ); + break; + default: + return; + } + + NewAttr( *aLR, bFirstLinOfstSet, bLeftIndentSet ); // #i103711#, #i105414# +} + +// Sprm 20 +void SwWW8ImplReader::Read_LineSpace( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ +// comment see Read_UL() + if (m_bStyNormal && m_bWWBugNormal) + return; + + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + + if (nLen < (eVersion <= ww::eWW2 ? 3 : 4)) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_LINESPACING ); + if( !( m_nIniFlags & WW8FL_NO_IMPLPASP ) ) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE ); + return; + } + + short nSpace = SVBT16ToUInt16( pData ); + short nMulti = (eVersion <= ww::eWW2) ? 1 : SVBT16ToUInt16( pData + 2 ); + + SvxLineSpaceRule eLnSpc; + if( 0 > nSpace ) + { + nSpace = -nSpace; + eLnSpc = SvxLineSpaceRule::Fix; + } + else + eLnSpc = SvxLineSpaceRule::Min; + + // WW has implicit additional paragraph spacing depending on + // the line spacing. It is, for "exactly", 0.8 * line spacing "before" + // and 0.2 * line spacing "after". + // For "at least", it is 1 * line spacing "before" and 0 * line spacing "after". + // For "multiple", it is 0 "before" and min(0cm, FontSize*(nFach-1)) "after". + + // SW also has implicit line spacing. It is, for "at least" + // 1 * line spacing "before" and 0 "after". + // For proportional, it is min(0cm, FontSize*(nFach-1)) both "before" and "after". + + sal_uInt16 nSpaceTw = 0; + + SvxLineSpacingItem aLSpc( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING ); + + if( 1 == nMulti ) // MultilineSpace ( proportional ) + { + long n = nSpace * 10 / 24; // WW: 240 = 100%, SW: 100 = 100% + + // here n is in [0..13653] + aLSpc.SetPropLineSpace( static_cast<sal_uInt16>(n) ); + const SvxFontHeightItem* pH = static_cast<const SvxFontHeightItem*>( + GetFormatAttr( RES_CHRATR_FONTSIZE )); + nSpaceTw = static_cast<sal_uInt16>( n * pH->GetHeight() / 100 ); + } + else // Fixed / Minimum + { + // for negative space, the distance is "exact", otherwise "at least" + nSpaceTw = static_cast<sal_uInt16>(nSpace); + aLSpc.SetLineHeight( nSpaceTw ); + aLSpc.SetLineSpaceRule( eLnSpc); + } + NewAttr( aLSpc ); + if (m_xSFlyPara) + m_xSFlyPara->nLineSpace = nSpaceTw; // linespace for graphics APOs +} + +//#i18519# AutoSpace value depends on Dop fDontUseHTMLAutoSpacing setting +sal_uInt16 SwWW8ImplReader::GetParagraphAutoSpace(bool fDontUseHTMLAutoSpacing) +{ + if (fDontUseHTMLAutoSpacing) + return 100; //Seems to be always 5points in this case + else + return 280; //Seems to be always 14points in this case +} + +void SwWW8ImplReader::Read_ParaAutoBefore(sal_uInt16, const sal_uInt8 *pData, short nLen) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE); + return; + } + + if (*pData) + { + SvxULSpaceItem aUL(*static_cast<const SvxULSpaceItem*>(GetFormatAttr(RES_UL_SPACE))); + aUL.SetUpper(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing)); + NewAttr(aUL); + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + m_vColl[m_nCurrentColl].m_bParaAutoBefore = true; + else + m_bParaAutoBefore = true; + } + else + { + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + m_vColl[m_nCurrentColl].m_bParaAutoBefore = false; + else + m_bParaAutoBefore = false; + } +} + +void SwWW8ImplReader::Read_ParaAutoAfter(sal_uInt16, const sal_uInt8 *pData, short nLen) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_UL_SPACE); + return; + } + + if (*pData) + { + SvxULSpaceItem aUL(*static_cast<const SvxULSpaceItem*>(GetFormatAttr(RES_UL_SPACE))); + aUL.SetLower(GetParagraphAutoSpace(m_xWDop->fDontUseHTMLAutoSpacing)); + NewAttr(aUL); + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + m_vColl[m_nCurrentColl].m_bParaAutoAfter = true; + else + m_bParaAutoAfter = true; + } + else + { + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) + m_vColl[m_nCurrentColl].m_bParaAutoAfter = false; + else + m_bParaAutoAfter = false; + } +} + +// Sprm 21, 22 +void SwWW8ImplReader::Read_UL( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + // A workaround for an error in WW: For nProduct == 0c03d, usually + // DyaAfter 240 (delta y distance after, comment of the translator) + // is incorrectly inserted into style "Normal", even though it isn't there. + // Using the ini flag WW8FL_NO_STY_DYA you can force this behavior for other + // WW versions as well. + // OSL_ENSURE( !bStyNormal || bWWBugNormal, "+This Document may point to a bug + // in the WW version used for creating it. If the Styles <Standard> resp. + // <Normal> differentiate between WW and SW in paragraph or line spacing, + // then please send this Document to SH."); + // bWWBugNormal is not a sufficient criterion for this distance being wrong. + + if (nLen < 2) + { + // end of attribute + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE ); + return; + } + short nPara = SVBT16ToUInt16( pData ); + if( nPara < 0 ) + nPara = -nPara; + + SvxULSpaceItem aUL( *static_cast<const SvxULSpaceItem*>(GetFormatAttr( RES_UL_SPACE ))); + + switch( nId ) + { + //sprmPDyaBefore + case NS_sprm::v6::sprmPDyaBefore: + case NS_sprm::sprmPDyaBefore: + aUL.SetUpper( nPara ); + break; + //sprmPDyaAfter + case NS_sprm::v6::sprmPDyaAfter: + case NS_sprm::sprmPDyaAfter: + aUL.SetLower( nPara ); + break; + default: + return; + } + + NewAttr( aUL ); +} + +void SwWW8ImplReader::Read_ParaContextualSpacing( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_UL_SPACE ); + return; + } + SvxULSpaceItem aUL( *static_cast<const SvxULSpaceItem*>(GetFormatAttr( RES_UL_SPACE ))); + aUL.SetContextValue(*pData != 0); + NewAttr( aUL ); +} + +void SwWW8ImplReader::Read_IdctHint( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + // sprmcidcthint (opcode 0x286f) specifies a script bias for the text in the run. + // for unicode characters that are shared between far east and non-far east scripts, + // this property determines what font and language the character will use. + // when this value is 0, text properties bias towards non-far east properties. + // when this value is 1, text properties bias towards far east properties. + // when this value is 2, text properties bias towards complex properties. + if (nLen < 1) //Property end + { + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(),RES_CHRATR_IDCTHINT); + } + else //Property start + { + NewAttr(SfxInt16Item(RES_CHRATR_IDCTHINT, *pData)); + } +} + +void SwWW8ImplReader::Read_Justify( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ADJUST ); + return; + } + + SvxAdjust eAdjust(SvxAdjust::Left); + bool bDistributed = false; + switch (*pData) + { + default: + case 0: + break; + case 1: + eAdjust = SvxAdjust::Center; + break; + case 2: + eAdjust = SvxAdjust::Right; + break; + case 3: + eAdjust = SvxAdjust::Block; + break; + case 4: + eAdjust = SvxAdjust::Block; + bDistributed = true; + break; + } + SvxAdjustItem aAdjust(eAdjust, RES_PARATR_ADJUST); + if (bDistributed) + aAdjust.SetLastBlock(SvxAdjust::Block); + + NewAttr(aAdjust); + SetRelativeJustify( nId != NS_sprm::sprmPJc80 ); +} + +bool SwWW8ImplReader::IsRightToLeft() +{ + bool bRTL = false; + SprmResult aDir; + if (m_xPlcxMan) + aDir = m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::sprmPFBiDi); + if (aDir.pSprm && aDir.nRemainingData >= 1) + bRTL = *aDir.pSprm != 0; + else + { + const SvxFrameDirectionItem* pItem= + static_cast<const SvxFrameDirectionItem*>(GetFormatAttr(RES_FRAMEDIR)); + if (pItem && (pItem->GetValue() == SvxFrameDirection::Horizontal_RL_TB)) + bRTL = true; + } + return bRTL; +} + +void SwWW8ImplReader::Read_RTLJustify( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ADJUST ); + return; + } + + //If we are in a ltr paragraph this is the same as normal Justify, + //If we are in a rtl paragraph the meaning is reversed. + if (!IsRightToLeft()) + Read_Justify(nId, pData, nLen); + else + { + SvxAdjust eAdjust(SvxAdjust::Right); + bool bDistributed = false; + switch (*pData) + { + default: + case 0: + break; + case 1: + eAdjust = SvxAdjust::Center; + break; + case 2: + eAdjust = SvxAdjust::Left; + break; + case 3: + eAdjust = SvxAdjust::Block; + break; + case 4: + eAdjust = SvxAdjust::Block; + bDistributed = true; + break; + } + SvxAdjustItem aAdjust(eAdjust, RES_PARATR_ADJUST); + if (bDistributed) + aAdjust.SetLastBlock(SvxAdjust::Block); + + NewAttr(aAdjust); + SetRelativeJustify( true ); + } +} + +void SwWW8ImplReader::Read_BoolItem( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + switch( nId ) + { + case NS_sprm::sprmPFKinsoku: + nId = RES_PARATR_FORBIDDEN_RULES; + break; + case NS_sprm::sprmPFOverflowPunct: + nId = RES_PARATR_HANGINGPUNCTUATION; + break; + case NS_sprm::sprmPFAutoSpaceDE: + nId = RES_PARATR_SCRIPTSPACE; + break; + default: + OSL_ENSURE( false, "wrong Id" ); + return ; + } + + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), nId ); + else + { + std::unique_ptr<SfxBoolItem> pI(static_cast<SfxBoolItem*>(GetDfltAttr( nId )->Clone())); + pI->SetValue( 0 != *pData ); + NewAttr( *pI ); + } +} + +void SwWW8ImplReader::Read_Emphasis( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_EMPHASIS_MARK ); + else + { + LanguageType nLang; + //Check to see if there is an up and coming cjk language property. If + //there is use it, if there is not fall back to the currently set one. + //Only the cjk language setting seems to matter to word, the western + //one is ignored + SprmResult aLang; + if (m_xPlcxMan) + aLang = m_xPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::sprmCRgLid1_80); + + if (aLang.pSprm && aLang.nRemainingData >= 2) + nLang = LanguageType(SVBT16ToUInt16(aLang.pSprm)); + else + { + nLang = static_cast<const SvxLanguageItem *>( + GetFormatAttr(RES_CHRATR_CJK_LANGUAGE))->GetLanguage(); + } + + FontEmphasisMark nVal; + switch( *pData ) + { + case 0: + nVal = FontEmphasisMark::NONE; + break; + case 2: + if (MsLangId::isKorean(nLang) || MsLangId::isTraditionalChinese(nLang)) + nVal = (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove); + else if (nLang == LANGUAGE_JAPANESE) + nVal = (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove); + else + nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow); + break; + case 3: + nVal = (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove); + break; + case 4: + nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow); + break; + case 1: + if (MsLangId::isSimplifiedChinese(nLang)) + nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow); + else + nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove); + break; + default: + nVal = (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove); + break; + } + + NewAttr( SvxEmphasisMarkItem( nVal, RES_CHRATR_EMPHASIS_MARK ) ); + } +} + +void SwWW8ImplReader::Read_ScaleWidth( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_SCALEW ); + else + { + sal_uInt16 nVal = SVBT16ToUInt16( pData ); + //The number must be between 1 and 600 + if (nVal < 1 || nVal > 600) + nVal = 100; + NewAttr( SvxCharScaleWidthItem( nVal, RES_CHRATR_SCALEW ) ); + } +} + +void SwWW8ImplReader::Read_Relief( sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_RELIEF ); + else + { + if( *pData ) + { +// not so easy because this is also a toggle attribute! +// 2 x emboss on -> no emboss !!! +// the actual value must be searched over the stack / template + + const SvxCharReliefItem* pOld = static_cast<const SvxCharReliefItem*>( + GetFormatAttr( RES_CHRATR_RELIEF )); + FontRelief nNewValue = NS_sprm::sprmCFImprint == nId ? FontRelief::Engraved + : ( NS_sprm::sprmCFEmboss == nId ? FontRelief::Embossed + : FontRelief::NONE ); + if( pOld->GetValue() == nNewValue ) + { + if( FontRelief::NONE != nNewValue ) + nNewValue = FontRelief::NONE; + } + NewAttr( SvxCharReliefItem( nNewValue, RES_CHRATR_RELIEF )); + } + } +} + +void SwWW8ImplReader::Read_TextAnim(sal_uInt16 /*nId*/, const sal_uInt8* pData, short nLen) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_CHRATR_BLINK); + else + { + if (*pData) + { + bool bBlink; + + // The 7 animated text effects available in word all get + // mapped to a blinking text effect in LibreOffice + // 0 no animation 1 Las Vegas lights + // 2 background blink 3 sparkle text + // 4 marching ants 5 marching red ants + // 6 shimmer + bBlink = *pData > 0 && *pData < 7; + + NewAttr(SvxBlinkItem(bBlink, RES_CHRATR_BLINK)); + } + } +} + +SwWW8Shade::SwWW8Shade(bool bVer67, const WW8_SHD& rSHD) +{ + sal_uInt8 b = rSHD.GetFore(); + OSL_ENSURE(b < 17, "ww8: colour out of range"); + if (b >= 17) + b = 0; + + Color nFore(SwWW8ImplReader::GetCol(b)); + + b = rSHD.GetBack(); + OSL_ENSURE(b < 17, "ww8: colour out of range"); + if( b >= 17 ) + b = 0; + + Color nBack(SwWW8ImplReader::GetCol(b)); + + b = rSHD.GetStyle(bVer67); + + SetShade(nFore, nBack, b); +} + +void SwWW8Shade::SetShade(Color nFore, Color nBack, sal_uInt16 nIndex) +{ + static const sal_uLong eMSGrayScale[] = + { + // Clear-Brush + 0, // 0 clear + // Solid-Brush + 1000, // 1 solid + // Percent values + 50, // 2 pct5 + 100, // 3 pct10 + 200, // 4 pct20 + 250, // 5 pct25 + 300, // 6 pct30 + 400, // 7 pct40 + 500, // 8 pct50 + 600, // 9 pct60 + 700, // 10 pct70 + 750, // 11 pct75 + 800, // 12 pct80 + 900, // 13 pct90 + // Special cases + 333, // 14 Dark Horizontal + 333, // 15 Dark Vertical + 333, // 16 Dark Forward Diagonal + 333, // 17 Dark Backward Diagonal + 333, // 18 Dark Cross + 333, // 19 Dark Diagonal Cross + 333, // 20 Horizontal + 333, // 21 Vertical + 333, // 22 Forward Diagonal + 333, // 23 Backward Diagonal + 333, // 24 Cross + 333, // 25 Diagonal Cross + // Undefined values in DOC spec-sheet + 500, // 26 + 500, // 27 + 500, // 28 + 500, // 29 + 500, // 30 + 500, // 31 + 500, // 32 + 500, // 33 + 500, // 34 + // Different shading types + 25, // 35 [available in DOC, not available in DOCX] + 75, // 36 [available in DOC, not available in DOCX] + 125, // 37 pct12 + 150, // 38 pct15 + 175, // 39 [available in DOC, not available in DOCX] + 225, // 40 [available in DOC, not available in DOCX] + 275, // 41 [available in DOC, not available in DOCX] + 325, // 42 [available in DOC, not available in DOCX] + 350, // 43 pct35 + 375, // 44 pct37 + 425, // 45 [available in DOC, not available in DOCX] + 450, // 46 pct45 + 475, // 47 [available in DOC, not available in DOCX] + 525, // 48 [available in DOC, not available in DOCX] + 550, // 49 pct55 + 575, // 50 [available in DOC, not available in DOCX] + 625, // 51 pct62 + 650, // 52 pct65 + 675, // 53 [available in DOC, not available in DOCX] + 725, // 54 [available in DOC, not available in DOCX] + 775, // 55 [available in DOC, not available in DOCX] + 825, // 56 [available in DOC, not available in DOCX] + 850, // 57 pct85 + 875, // 58 pct87 + 925, // 59 [available in DOC, not available in DOCX] + 950, // 60 pct95 + 975 // 61 [available in DOC, not available in DOCX] + };// 62 + + //NO auto for shading so Foreground: Auto = Black + if (nFore == COL_AUTO) + nFore = COL_BLACK; + + //NO auto for shading so background: Auto = White + Color nUseBack = nBack; + if (nUseBack == COL_AUTO) + nUseBack = COL_WHITE; + + if( nIndex >= SAL_N_ELEMENTS( eMSGrayScale ) ) + nIndex = 0; + + sal_uLong nWW8BrushStyle = eMSGrayScale[nIndex]; + + switch (nWW8BrushStyle) + { + case 0: // Null-Brush + aColor = nBack; + break; + default: + { + Color aForeColor(nFore); + Color aBackColor(nUseBack); + + sal_uInt32 nRed = aForeColor.GetRed() * nWW8BrushStyle; + sal_uInt32 nGreen = aForeColor.GetGreen() * nWW8BrushStyle; + sal_uInt32 nBlue = aForeColor.GetBlue() * nWW8BrushStyle; + nRed += aBackColor.GetRed() * (1000 - nWW8BrushStyle); + nGreen += aBackColor.GetGreen()* (1000 - nWW8BrushStyle); + nBlue += aBackColor.GetBlue() * (1000 - nWW8BrushStyle); + + aColor = Color( nRed/1000, nGreen/1000, nBlue/1000 ); + } + break; + } +} + +void SwWW8ImplReader::Read_Shade( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (!m_bVer67 && m_xPlcxMan && m_xPlcxMan->GetPapPLCF()->HasSprm(NS_sprm::sprmPShd).pSprm) + return; + + if (nLen < 2) + { + // end of attribute + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLSTYLE ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLCOLOR ); + } + else + { + WW8_SHD aSHD; + aSHD.SetWWValue( *reinterpret_cast<SVBT16 const *>(pData) ); + SwWW8Shade aSh( m_bVer67, aSHD ); + + NewAttr( XFillStyleItem(drawing::FillStyle_SOLID) ); + NewAttr( XFillColorItem(OUString(), aSh.aColor) ); + } +} + +void SwWW8ImplReader::Read_ParaBackColor(sal_uInt16, const sal_uInt8* pData, short nLen) +{ + if (nLen <= 0) + { + // end of attribute + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLSTYLE ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), XATTR_FILLCOLOR ); + } + else + { + OSL_ENSURE(nLen == 10, "Len of para back colour not 10!"); + if (nLen != 10) + return; + + const Color aColor = ExtractColour(pData, m_bVer67); + NewAttr( XFillColorItem(OUString(), aColor) ); + if ( aColor == COL_AUTO ) + NewAttr( XFillStyleItem(drawing::FillStyle_NONE) ); + else + NewAttr( XFillStyleItem(drawing::FillStyle_SOLID) ); + } +} + +Color SwWW8ImplReader::ExtractColour(const sal_uInt8* &rpData, bool bVer67) +{ + OSL_ENSURE(!bVer67, "Impossible"); + Color nFore = msfilter::util::BGRToRGB(SVBT32ToUInt32(rpData)); + rpData+=4; + Color nBack = msfilter::util::BGRToRGB(SVBT32ToUInt32(rpData)); + rpData+=4; + sal_uInt16 nIndex = SVBT16ToUInt16(rpData); + rpData+=2; + //Being a transparent background colour doesn't actually show the page + //background through, it merely acts like white + if (nBack == Color(0xFF000000)) + nBack = COL_AUTO; + OSL_ENSURE(nBack == COL_AUTO || (nBack.GetTransparency() == 0), + "ww8: don't know what to do with such a transparent bg colour, report"); + SwWW8Shade aShade(nFore, nBack, nIndex); + return aShade.aColor; +} + +void SwWW8ImplReader::Read_TextVerticalAdjustment( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if( nLen > 0 ) + { + drawing::TextVerticalAdjust nVA = drawing::TextVerticalAdjust_TOP; + switch( *pData ) + { + case 1: + nVA = drawing::TextVerticalAdjust_CENTER; + break; + case 2: //justify + nVA = drawing::TextVerticalAdjust_BLOCK; + break; + case 3: + nVA = drawing::TextVerticalAdjust_BOTTOM; + break; + default: + break; + } + m_aSectionManager.SetCurrentSectionVerticalAdjustment( nVA ); + } +} + +void SwWW8ImplReader::Read_Border(sal_uInt16 , const sal_uInt8*, short nLen) +{ + if (nLen < 0) + { + if( m_bHasBorder ) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_BOX ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_SHADOW ); + m_bHasBorder = false; + } + } + else if( !m_bHasBorder ) + { + // the borders on all four sides are bundled. That + // simplifies the administration, i.e., the box does not have + // to be put on and removed from CtrlStack 4 times. + m_bHasBorder = true; + + WW8_BRCVer9_5 aBrcs; // Top, Left, Bottom, Right, Between + sal_uInt8 nBorder; + + if( m_pCurrentColl ) + nBorder = ::lcl_ReadBorders(m_bVer67, aBrcs, nullptr, m_xStyles.get()); + else + nBorder = ::lcl_ReadBorders(m_bVer67, aBrcs, m_xPlcxMan ? m_xPlcxMan->GetPapPLCF() : nullptr); + + if( nBorder ) // Border + { + bool bIsB = IsBorder(aBrcs, true); + if (!InLocalApo() || !bIsB || (m_xWFlyPara && !m_xWFlyPara->bBorderLines)) + { + // Do not turn *on* borders in APO, since otherwise + // I get the Fly border twice; + // but only when it is set on in the Fly, skip it; + // otherwise there is none at all! + + // even if no border is set, the attribute has to be set, + // otherwise it's not possible to turn off the style attribute. + const SvxBoxItem* pBox + = static_cast<const SvxBoxItem*>(GetFormatAttr( RES_BOX )); + std::shared_ptr<SvxBoxItem> aBox(std::make_shared<SvxBoxItem>(RES_BOX)); + if (pBox) + aBox.reset(pBox->Clone()); + short aSizeArray[5]={0}; + + SetBorder(*aBox, aBrcs, &aSizeArray[0], nBorder); + + tools::Rectangle aInnerDist; + GetBorderDistance( aBrcs, aInnerDist ); + + if (nBorder & (1 << WW8_LEFT)) + aBox->SetDistance( static_cast<sal_uInt16>(aInnerDist.Left()), SvxBoxItemLine::LEFT ); + + if (nBorder & (1 << WW8_TOP)) + aBox->SetDistance( static_cast<sal_uInt16>(aInnerDist.Top()), SvxBoxItemLine::TOP ); + + if (nBorder & (1 << WW8_RIGHT)) + aBox->SetDistance( static_cast<sal_uInt16>(aInnerDist.Right()), SvxBoxItemLine::RIGHT ); + + if (nBorder & (1 << WW8_BOT)) + aBox->SetDistance( static_cast<sal_uInt16>(aInnerDist.Bottom()), SvxBoxItemLine::BOTTOM ); + + NewAttr( *aBox ); + + SvxShadowItem aS(RES_SHADOW); + // Word only allows shadows on visible borders + if ( aBox->CalcLineSpace( SvxBoxItemLine::RIGHT ) ) + SetShadow( aS, &aSizeArray[0], aBrcs[WW8_RIGHT] ); + NewAttr( aS ); + } + } + } +} + +void SwWW8ImplReader::Read_CharBorder(sal_uInt16 nId, const sal_uInt8* pData, short nLen ) +{ + //Ignore this old border type + //if (!bVer67 && pPlcxMan && pPlcxMan->GetChpPLCF()->HasSprm(NS_sprm::sprmCBrc)) + // return; + + if (nLen < 0) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_BOX ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_CHRATR_SHADOW ); + } + else + { + const SvxBoxItem* pBox + = static_cast<const SvxBoxItem*>(GetFormatAttr( RES_CHRATR_BOX )); + if( pBox ) + { + std::unique_ptr<SvxBoxItem> aBoxItem(pBox->Clone()); + WW8_BRCVer9 aBrc; + int nBrcVer = (nId == NS_sprm::sprmCBrc) ? 9 : (m_bVer67 ? 6 : 8); + + SetWW8_BRC(nBrcVer, aBrc, pData, nLen); + + Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::TOP, 0, nullptr, true); + Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::BOTTOM, 0, nullptr, true); + Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::LEFT, 0, nullptr, true); + Set1Border(*aBoxItem, aBrc, SvxBoxItemLine::RIGHT, 0, nullptr, true); + NewAttr( *aBoxItem ); + + short aSizeArray[WW8_RIGHT+1]={0}; aSizeArray[WW8_RIGHT] = 1; + SvxShadowItem aShadowItem(RES_CHRATR_SHADOW); + // Word only allows shadows on visible borders + if ( aBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) ) + SetShadow( aShadowItem, &aSizeArray[0], aBrc ); + NewAttr( aShadowItem ); + } + } +} + +void SwWW8ImplReader::Read_Hyphenation( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + // set Hyphenation flag + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_HYPHENZONE ); + else + { + SvxHyphenZoneItem aAttr( + *static_cast<const SvxHyphenZoneItem*>(GetFormatAttr( RES_PARATR_HYPHENZONE ) )); + + aAttr.SetHyphen( 0 == *pData ); // sic ! + + if( !*pData ) + { + aAttr.GetMinLead() = 2; + aAttr.GetMinTrail() = 2; + aAttr.GetMaxHyphens() = 0; + } + + NewAttr( aAttr ); + } +} + +void SwWW8ImplReader::Read_WidowControl( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + { + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_WIDOWS ); + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_ORPHANS ); + } + else + { + sal_uInt8 nL = ( *pData & 1 ) ? 2 : 0; + + NewAttr( SvxWidowsItem( nL, RES_PARATR_WIDOWS ) ); // Off -> nLines = 0 + NewAttr( SvxOrphansItem( nL, RES_PARATR_ORPHANS ) ); + + if( m_pCurrentColl && m_xStyles ) // Style-Def ? + m_xStyles->mbWidowsChanged = true; // save for simulation + // Default-Widows + } +} + +void SwWW8ImplReader::Read_UsePgsuSettings(sal_uInt16,const sal_uInt8* pData,short nLen) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_SNAPTOGRID); + else + { + if(m_nInTable) + NewAttr( SvxParaGridItem(false, RES_PARATR_SNAPTOGRID) ); + else + NewAttr( SvxParaGridItem(*pData, RES_PARATR_SNAPTOGRID) ); + } +} + +void SwWW8ImplReader::Read_AlignFont( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 2) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_VERTALIGN); + else + { + sal_uInt16 nVal = SVBT16ToUInt16( pData ); + SvxParaVertAlignItem::Align nAlign; + switch (nVal) + { + case 0: + nAlign = SvxParaVertAlignItem::Align::Top; + break; + case 1: + nAlign = SvxParaVertAlignItem::Align::Center; + break; + case 2: + nAlign = SvxParaVertAlignItem::Align::Baseline; + break; + case 3: + nAlign = SvxParaVertAlignItem::Align::Bottom; + break; + case 4: + nAlign = SvxParaVertAlignItem::Align::Automatic; + break; + default: + nAlign = SvxParaVertAlignItem::Align::Automatic; + OSL_ENSURE(false,"Unknown paragraph vertical align"); + break; + } + NewAttr( SvxParaVertAlignItem( nAlign, RES_PARATR_VERTALIGN ) ); + } +} + +void SwWW8ImplReader::Read_KeepLines( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_PARATR_SPLIT ); + else + NewAttr( SvxFormatSplitItem( ( *pData & 1 ) == 0, RES_PARATR_SPLIT ) ); +} + +void SwWW8ImplReader::Read_KeepParas( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_KEEP ); + else + NewAttr( SvxFormatKeepItem( ( *pData & 1 ) != 0 , RES_KEEP) ); +} + +void SwWW8ImplReader::Read_BreakBefore( sal_uInt16, const sal_uInt8* pData, short nLen ) +{ + if (nLen < 1) + m_xCtrlStck->SetAttr( *m_pPaM->GetPoint(), RES_BREAK ); + else + NewAttr( SvxFormatBreakItem( + ( *pData & 1 ) ? SvxBreak::PageBefore : SvxBreak::NONE, RES_BREAK ) ); +} + +void SwWW8ImplReader::Read_ApoPPC( sal_uInt16, const sal_uInt8* pData, short ) +{ + if (m_pCurrentColl && m_nCurrentColl < m_vColl.size()) // only for Styledef, otherwise solved differently + { + SwWW8StyInf& rSI = m_vColl[m_nCurrentColl]; + if (!rSI.m_xWWFly) + rSI.m_xWWFly = std::make_shared<WW8FlyPara>(m_bVer67); + rSI.m_xWWFly->Read(*pData, m_xStyles.get()); + if (rSI.m_xWWFly->IsEmpty()) + { + m_vColl[m_nCurrentColl].m_xWWFly.reset(); + } + } +} + +bool SwWW8ImplReader::ParseTabPos(WW8_TablePos *pTabPos, WW8PLCFx_Cp_FKP* pPap) +{ + bool bRet = false; + memset(pTabPos, 0, sizeof(WW8_TablePos)); + // sprmTPc contains a PositionCodeOperand structure that specifies the origin + // that is used to calculate the table position when it is absolutely positioned + SprmResult aRes = pPap->HasSprm(NS_sprm::sprmTPc); + if (aRes.pSprm && aRes.nRemainingData >= 1) + { + pTabPos->nSp29 = *aRes.pSprm; + pTabPos->nSp37 = 2; //Possible fail area, always parallel wrap + aRes = pPap->HasSprm(NS_sprm::sprmTDxaAbs); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nSp26 = SVBT16ToUInt16(aRes.pSprm); + aRes = pPap->HasSprm(NS_sprm::sprmTDyaAbs); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nSp27 = SVBT16ToUInt16(aRes.pSprm); + aRes = pPap->HasSprm(NS_sprm::sprmTDxaFromText); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nLeMgn = SVBT16ToUInt16(aRes.pSprm); + aRes = pPap->HasSprm(NS_sprm::sprmTDxaFromTextRight); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nRiMgn = SVBT16ToUInt16(aRes.pSprm); + aRes = pPap->HasSprm(NS_sprm::sprmTDyaFromText); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nUpMgn = SVBT16ToUInt16(aRes.pSprm); + aRes = pPap->HasSprm(NS_sprm::sprmTDyaFromTextBottom); + if (aRes.pSprm && aRes.nRemainingData >= 2) + pTabPos->nLoMgn = SVBT16ToUInt16(aRes.pSprm); + pTabPos->bNoFly = !FloatingTableConversion(pPap); + bRet = true; + } + return bRet; +} + +// page attribute won't be used as attribute anymore +// ( except OLST ) +long SwWW8ImplReader::ImportExtSprm(WW8PLCFManResult* pRes) +{ + // array for reading of the extended ( self-defined ) SPRMs + typedef long (SwWW8ImplReader:: *FNReadRecordExt)(WW8PLCFManResult*); + + static const FNReadRecordExt aWwSprmTab[] = + { + /* 0 (256) */ &SwWW8ImplReader::Read_Footnote, // FootNote + /* 1 (257) */ &SwWW8ImplReader::Read_Footnote, // EndNote + /* 2 (258) */ &SwWW8ImplReader::Read_Field, // Field + /* 3 (259) */ &SwWW8ImplReader::Read_Book, // Bookmark + /* 4 (260) */ &SwWW8ImplReader::Read_And, // Annotation + /* 5 (261) */ &SwWW8ImplReader::Read_AtnBook, // Annotationmark + /* 6 (262) */ &SwWW8ImplReader::Read_FactoidBook // Smart tag bookmark + }; + + if( pRes->nSprmId < 280 ) + { + sal_uInt8 nIdx = static_cast< sal_uInt8 >(pRes->nSprmId - eFTN); + if( nIdx < SAL_N_ELEMENTS(aWwSprmTab) + && aWwSprmTab[nIdx] ) + return (this->*aWwSprmTab[nIdx])(pRes); + else + return 0; + } + else + return 0; +} + +void SwWW8ImplReader::EndExtSprm(sal_uInt16 nSprmId) +{ + typedef sal_uInt16 (SwWW8ImplReader:: *FNReadRecordExt)(); + + static const FNReadRecordExt aWwSprmTab[] = + { + /* 0 (256) */ &SwWW8ImplReader::End_Footnote, // FootNote + /* 1 (257) */ &SwWW8ImplReader::End_Footnote, // EndNote + /* 2 (258) */ &SwWW8ImplReader::End_Field, // Field + /* 3 (259) */ nullptr, // Bookmark + /* 4 (260) */ nullptr // Annotation + }; + + sal_uInt8 nIdx = static_cast< sal_uInt8 >(nSprmId - eFTN); + if( nIdx < SAL_N_ELEMENTS(aWwSprmTab) + && aWwSprmTab[nIdx] ) + (this->*aWwSprmTab[nIdx])(); +} + +// arrays for reading the SPRMs + +// function for reading of SPRMs. Par1: SprmId +typedef void (SwWW8ImplReader:: *FNReadRecord)( sal_uInt16, const sal_uInt8*, short ); + +struct SprmReadInfo +{ + sal_uInt16 nId; + FNReadRecord pReadFnc; +}; + +static bool operator<(const SprmReadInfo &rFirst, const SprmReadInfo &rSecond) +{ + return (rFirst.nId < rSecond.nId); +} + +typedef ww::SortedArray<SprmReadInfo> wwSprmDispatcher; + +static const wwSprmDispatcher *GetWW2SprmDispatcher() +{ + static SprmReadInfo aSprms[] = + { + {0, nullptr}, // "0" default resp. error + // will be skipped! , + {2, &SwWW8ImplReader::Read_StyleCode}, //"sprmPIstd", pap.istd + //(style code) + {3, nullptr}, //"sprmPIstdPermute", pap.istd + //permutation + {4, nullptr}, //"sprmPIncLv1", + //pap.istddifference + {5, &SwWW8ImplReader::Read_Justify}, //"sprmPJc", pap.jc + //(justification) + {6, nullptr}, //"sprmPFSideBySide", + //pap.fSideBySide + {7, &SwWW8ImplReader::Read_KeepLines}, //"sprmPFKeep", pap.fKeep + {8, &SwWW8ImplReader::Read_KeepParas}, //"sprmPFKeepFollow ", + //pap.fKeepFollow + {9, &SwWW8ImplReader::Read_BreakBefore}, //"sprmPPageBreakBefore", + //pap.fPageBreakBefore + {10, nullptr}, //"sprmPBrcl", pap.brcl + {11, nullptr}, //"sprmPBrcp ", pap.brcp + {12, &SwWW8ImplReader::Read_ANLevelDesc}, //"sprmPAnld", pap.anld (ANLD + //structure) + {13, &SwWW8ImplReader::Read_ANLevelNo}, //"sprmPNLvlAnm", pap.nLvlAnm + //nn + {14, &SwWW8ImplReader::Read_NoLineNumb}, //"sprmPFNoLineNumb", ap.fNoLnn + {15, &SwWW8ImplReader::Read_Tab}, //"?sprmPChgTabsPapx", + //pap.itbdMac, ... + {16, &SwWW8ImplReader::Read_LR}, //"sprmPDxaRight", pap.dxaRight + {17, &SwWW8ImplReader::Read_LR}, //"sprmPDxaLeft", pap.dxaLeft + {18, nullptr}, //"sprmPNest", pap.dxaLeft + {19, &SwWW8ImplReader::Read_LR}, //"sprmPDxaLeft1", pap.dxaLeft1 + {20, &SwWW8ImplReader::Read_LineSpace}, //"sprmPDyaLine", pap.lspd + //an LSPD + {21, &SwWW8ImplReader::Read_UL}, //"sprmPDyaBefore", + //pap.dyaBefore + {22, &SwWW8ImplReader::Read_UL}, //"sprmPDyaAfter", pap.dyaAfter + {23, nullptr}, //"?sprmPChgTabs", pap.itbdMac, + //pap.rgdxaTab, ... + {24, nullptr}, //"sprmPFInTable", pap.fInTable + {25, &SwWW8ImplReader::Read_TabRowEnd}, //"sprmPTtp", pap.fTtp + {26, nullptr}, //"sprmPDxaAbs", pap.dxaAbs + {27, nullptr}, //"sprmPDyaAbs", pap.dyaAbs + {28, nullptr}, //"sprmPDxaWidth", pap.dxaWidth + {29, &SwWW8ImplReader::Read_ApoPPC}, //"sprmPPc", pap.pcHorz, + //pap.pcVert + {30, nullptr}, //"sprmPBrcTop10", pap.brcTop + //BRC10 + {31, nullptr}, //"sprmPBrcLeft10", + //pap.brcLeft BRC10 + {32, nullptr}, //"sprmPBrcBottom10", + //pap.brcBottom BRC10 + {33, nullptr}, //"sprmPBrcRight10", + //pap.brcRight BRC10 + {34, nullptr}, //"sprmPBrcBetween10", + //pap.brcBetween BRC10 + {35, nullptr}, //"sprmPBrcBar10", pap.brcBar + //BRC10 + {36, nullptr}, //"sprmPFromText10", + //pap.dxaFromText dxa + {37, nullptr}, //"sprmPWr", pap.wr wr + {38, &SwWW8ImplReader::Read_Border}, //"sprmPBrcTop", pap.brcTop BRC + {39, &SwWW8ImplReader::Read_Border}, //"sprmPBrcLeft", + //pap.brcLeft BRC + {40, &SwWW8ImplReader::Read_Border}, //"sprmPBrcBottom", + //pap.brcBottom BRC + {41, &SwWW8ImplReader::Read_Border}, //"sprmPBrcRight", + //pap.brcRight BRC + {42, &SwWW8ImplReader::Read_Border}, //"sprmPBrcBetween", + //pap.brcBetween BRC + {43, nullptr}, //"sprmPBrcBar", pap.brcBar + //BRC word + {44, &SwWW8ImplReader::Read_Hyphenation}, //"sprmPFNoAutoHyph", + //pap.fNoAutoHyph + {45, nullptr}, //"sprmPWHeightAbs", + //pap.wHeightAbs w + {46, nullptr}, //"sprmPDcs", pap.dcs DCS + {47, &SwWW8ImplReader::Read_Shade}, //"sprmPShd", pap.shd SHD + {48, nullptr}, //"sprmPDyaFromText", + //pap.dyaFromText dya + {49, nullptr}, //"sprmPDxaFromText", + //pap.dxaFromText dxa + {50, nullptr}, //"sprmPFLocked", pap.fLocked + //0 or 1 byte + {51, &SwWW8ImplReader::Read_WidowControl}, //"sprmPFWidowControl", + //pap.fWidowControl 0 or 1 byte + {52, nullptr}, //"?sprmPRuler 52", + {53, nullptr}, //"??53", + {54, nullptr}, //"??54", + {55, nullptr}, //"??55", + {56, nullptr}, //"??56", + {57, nullptr}, //"??57", + {58, nullptr}, //"??58", + {59, nullptr}, //"??59", + + {60, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFBold", chp.fBold 0,1, + //128, or 129 byte + {61, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFItalic", chp.fItalic + //0,1, 128, or 129 byte + {62, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFStrike", chp.fStrike + //0,1, 128, or 129 byte + {63, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFOutline", chp.fOutline + //0,1, 128, or 129 byte + {64, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFShadow", chp.fShadow + //0,1, 128, or 129 byte + {65, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFSmallCaps", + //chp.fSmallCaps 0,1, 128, or + //129 byte + {66, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFCaps", chp.fCaps 0,1, + //128, or 129 byte + {67, &SwWW8ImplReader::Read_BoldUsw}, //"sprmCFVanish", chp.fVanish + //0,1, 128, or 129 byte + {68, &SwWW8ImplReader::Read_FontCode}, //"sprmCFtc", chp.ftc ftc word + {69, &SwWW8ImplReader::Read_Underline}, // "sprmCKul", chp.kul kul byte + {70, nullptr}, //"sprmCSizePos", chp.hps, + //chp.hpsPos 3 bytes + {71, &SwWW8ImplReader::Read_Kern}, //"sprmCDxaSpace", + //chp.dxaSpace dxa word + {72, &SwWW8ImplReader::Read_Language}, //"sprmCLid", chp.lid LID word + {73, &SwWW8ImplReader::Read_TextColor}, //"sprmCIco", chp.ico ico byte + {74, &SwWW8ImplReader::Read_FontSize}, //"sprmCHps", chp.hps hps word! + {75, nullptr}, //"sprmCHpsInc", chp.hps byte + {76, &SwWW8ImplReader::Read_SubSuperProp}, //"sprmCHpsPos", chp.hpsPos + //hps byte + {77, nullptr}, //"sprmCHpsPosAdj", chp.hpsPos + //hps byte + {78, &SwWW8ImplReader::Read_Majority}, //"?sprmCMajority", chp.fBold, + //chp.fItalic, chp.fSmallCaps + {80, &SwWW8ImplReader::Read_BoldBiDiUsw}, //sprmCFBoldBi + {81, &SwWW8ImplReader::Read_BoldBiDiUsw}, //sprmCFItalicBi + {82, &SwWW8ImplReader::Read_FontCode}, //sprmCFtcBi + {83, &SwWW8ImplReader::Read_Language}, //sprmClidBi + {84, &SwWW8ImplReader::Read_TextColor}, //sprmCIcoBi + {85, &SwWW8ImplReader::Read_FontSize}, //sprmCHpsBi + {86, nullptr}, //sprmCFBiDi + {87, nullptr}, //sprmCFDiacColor + {94, nullptr}, //"sprmPicBrcl", pic.brcl brcl + //(see PIC structure + //definition) byte + {95, nullptr}, //"sprmPicScale", pic.mx, + //pic.my, pic.dxaCropleft, + {96, nullptr}, //"sprmPicBrcTop", pic.brcTop + //BRC word + {97, nullptr}, //"sprmPicBrcLeft", + //pic.brcLeft BRC word + {98, nullptr}, //"sprmPicBrcBottom", + //pic.brcBottom BRC word + {99, nullptr} //"sprmPicBrcRight", + }; + + static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +} + +static const wwSprmDispatcher *GetWW6SprmDispatcher() +{ + static SprmReadInfo aSprms[] = + { + {0, nullptr}, // "0" default resp. error + // will be skipped! + {NS_sprm::v6::sprmPIstd, &SwWW8ImplReader::Read_StyleCode}, // pap.istd (style code) + {NS_sprm::v6::sprmPIstdPermute, nullptr}, // pap.istd permutation + {NS_sprm::v6::sprmPIncLv1, nullptr}, // pap.istddifference + {NS_sprm::v6::sprmPJc, &SwWW8ImplReader::Read_Justify}, // pap.jc (justification) + {NS_sprm::v6::sprmPFSideBySide, nullptr}, // pap.fSideBySide + {NS_sprm::v6::sprmPFKeep, &SwWW8ImplReader::Read_KeepLines}, // pap.fKeep + {NS_sprm::v6::sprmPFKeepFollow, &SwWW8ImplReader::Read_KeepParas}, // pap.fKeepFollow + {NS_sprm::v6::sprmPPageBreakBefore, &SwWW8ImplReader::Read_BreakBefore}, // pap.fPageBreakBefore + {NS_sprm::v6::sprmPBrcl, nullptr}, // pap.brcl + {NS_sprm::v6::sprmPBrcp, nullptr}, // pap.brcp + {NS_sprm::v6::sprmPAnld, &SwWW8ImplReader::Read_ANLevelDesc}, // pap.anld (ANLD structure) + {NS_sprm::v6::sprmPNLvlAnm, &SwWW8ImplReader::Read_ANLevelNo}, // pap.nLvlAnm nn + {NS_sprm::v6::sprmPFNoLineNumb, &SwWW8ImplReader::Read_NoLineNumb}, // ap.fNoLnn + {NS_sprm::v6::sprmPChgTabsPapx, &SwWW8ImplReader::Read_Tab}, // pap.itbdMac, ... + {NS_sprm::v6::sprmPDxaRight, &SwWW8ImplReader::Read_LR}, // pap.dxaRight + {NS_sprm::v6::sprmPDxaLeft, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft + {NS_sprm::v6::sprmPNest, nullptr}, // pap.dxaLeft + {NS_sprm::v6::sprmPDxaLeft1, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft1 + {NS_sprm::v6::sprmPDyaLine, &SwWW8ImplReader::Read_LineSpace}, // pap.lspd an LSPD + {NS_sprm::v6::sprmPDyaBefore, &SwWW8ImplReader::Read_UL}, // pap.dyaBefore + {NS_sprm::v6::sprmPDyaAfter, &SwWW8ImplReader::Read_UL}, // pap.dyaAfter + {NS_sprm::v6::sprmPChgTabs, nullptr}, // pap.itbdMac, pap.rgdxaTab, ... + {NS_sprm::v6::sprmPFInTable, nullptr}, // pap.fInTable + {NS_sprm::v6::sprmPTtp, &SwWW8ImplReader::Read_TabRowEnd}, // pap.fTtp + {NS_sprm::v6::sprmPDxaAbs, nullptr}, // pap.dxaAbs + {NS_sprm::v6::sprmPDyaAbs, nullptr}, // pap.dyaAbs + {NS_sprm::v6::sprmPDxaWidth, nullptr}, // pap.dxaWidth + {NS_sprm::v6::sprmPPc, &SwWW8ImplReader::Read_ApoPPC}, // pap.pcHorz, pap.pcVert + {NS_sprm::v6::sprmPBrcTop10, nullptr}, // pap.brcTop BRC10 + {NS_sprm::v6::sprmPBrcLeft10, nullptr}, // pap.brcLeft BRC10 + {NS_sprm::v6::sprmPBrcBottom10, nullptr}, // pap.brcBottom BRC10 + {NS_sprm::v6::sprmPBrcRight10, nullptr}, // pap.brcRight BRC10 + {NS_sprm::v6::sprmPBrcBetween10, nullptr}, // pap.brcBetween BRC10 + {NS_sprm::v6::sprmPBrcBar10, nullptr}, // pap.brcBar BRC10 + {NS_sprm::v6::sprmPFromText10, nullptr}, // pap.dxaFromText dxa + {NS_sprm::v6::sprmPWr, nullptr}, // pap.wr wr + {NS_sprm::v6::sprmPBrcTop, &SwWW8ImplReader::Read_Border}, // pap.brcTop BRC + {NS_sprm::v6::sprmPBrcLeft, &SwWW8ImplReader::Read_Border}, // pap.brcLeft BRC + {NS_sprm::v6::sprmPBrcBottom, &SwWW8ImplReader::Read_Border}, // pap.brcBottom BRC + {NS_sprm::v6::sprmPBrcRight, &SwWW8ImplReader::Read_Border}, // pap.brcRight BRC + {NS_sprm::v6::sprmPBrcBetween, &SwWW8ImplReader::Read_Border}, // pap.brcBetween BRC + {NS_sprm::v6::sprmPBrcBar, nullptr}, // pap.brcBar BRC word + {NS_sprm::v6::sprmPFNoAutoHyph, &SwWW8ImplReader::Read_Hyphenation}, // pap.fNoAutoHyph + {NS_sprm::v6::sprmPWHeightAbs, nullptr}, // pap.wHeightAbs w + {NS_sprm::v6::sprmPDcs, nullptr}, // pap.dcs DCS + {NS_sprm::v6::sprmPShd, &SwWW8ImplReader::Read_Shade}, // pap.shd SHD + {NS_sprm::v6::sprmPDyaFromText, nullptr}, // pap.dyaFromText dya + {NS_sprm::v6::sprmPDxaFromText, nullptr}, // pap.dxaFromText dxa + {NS_sprm::v6::sprmPFLocked, nullptr}, // pap.fLocked 0 or 1 byte + {NS_sprm::v6::sprmPFWidowControl, &SwWW8ImplReader::Read_WidowControl}, // pap.fWidowControl 0 or 1 byte + {NS_sprm::v6::sprmPRuler, nullptr}, + {53, nullptr}, //"??53", + {54, nullptr}, //"??54", + {55, nullptr}, //"??55", + {56, nullptr}, //"??56", + {57, nullptr}, //"??57", + {58, nullptr}, //"??58", + {59, nullptr}, //"??59", + {60, nullptr}, //"??60", + {61, nullptr}, //"??61", + {62, nullptr}, //"??62", + {63, nullptr}, //"??63", + {64, &SwWW8ImplReader::Read_ParaBiDi}, //"rtl bidi ? + {NS_sprm::v6::sprmCFStrikeRM, &SwWW8ImplReader::Read_CFRMarkDel}, // chp.fRMarkDel 1 or 0 bit + {NS_sprm::v6::sprmCFRMark, &SwWW8ImplReader::Read_CFRMark}, // chp.fRMark 1 or 0 bit + {NS_sprm::v6::sprmCFFldVanish, &SwWW8ImplReader::Read_FieldVanish}, // chp.fFieldVanish 1 or 0 bit + {NS_sprm::v6::sprmCPicLocation, &SwWW8ImplReader::Read_PicLoc}, // chp.fcPic and chp.fSpec + {NS_sprm::v6::sprmCIbstRMark, nullptr}, // chp.ibstRMark index into sttbRMark + {NS_sprm::v6::sprmCDttmRMark, nullptr}, // chp.dttm DTTM long + {NS_sprm::v6::sprmCFData, nullptr}, // chp.fData 1 or 0 bit + {NS_sprm::v6::sprmCRMReason, nullptr}, // chp.idslRMReason an index to a table + {NS_sprm::v6::sprmCChse, &SwWW8ImplReader::Read_CharSet}, // chp.fChsDiff and chp.chse 3 bytes + {NS_sprm::v6::sprmCSymbol, &SwWW8ImplReader::Read_Symbol}, // chp.fSpec, chp.chSym and chp.ftcSym + {NS_sprm::v6::sprmCFOle2, &SwWW8ImplReader::Read_Obj}, // chp.fOle2 1 or 0 bit + {76, nullptr}, //"??76", + {77, nullptr}, //"??77", + {78, nullptr}, //"??78", + {79, nullptr}, //"??79", + {NS_sprm::v6::sprmCIstd, &SwWW8ImplReader::Read_CColl}, // chp.istd istd, see stylesheet definition; short + {NS_sprm::v6::sprmCIstdPermute, nullptr}, // chp.istd permutation vector + {NS_sprm::v6::sprmCDefault, nullptr}, // whole CHP none variable length + {NS_sprm::v6::sprmCPlain, nullptr}, // whole CHP none 0 + {84, nullptr}, //"??84", + {NS_sprm::v6::sprmCFBold, &SwWW8ImplReader::Read_BoldUsw}, // chp.fBold 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFItalic, &SwWW8ImplReader::Read_BoldUsw}, // chp.fItalic 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFStrike, &SwWW8ImplReader::Read_BoldUsw}, // chp.fStrike 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFOutline, &SwWW8ImplReader::Read_BoldUsw}, // chp.fOutline 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFShadow, &SwWW8ImplReader::Read_BoldUsw}, // chp.fShadow 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFSmallCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fSmallCaps 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fCaps 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFVanish, &SwWW8ImplReader::Read_BoldUsw}, // chp.fVanish 0,1, 128, or 129 byte + {NS_sprm::v6::sprmCFtc, &SwWW8ImplReader::Read_FontCode}, // chp.ftc ftc word + {NS_sprm::v6::sprmCKul, &SwWW8ImplReader::Read_Underline}, // chp.kul kul byte + {NS_sprm::v6::sprmCSizePos, nullptr}, // chp.hps, chp.hpsPos 3 bytes + {NS_sprm::v6::sprmCDxaSpace, &SwWW8ImplReader::Read_Kern}, // chp.dxaSpace dxa word + {NS_sprm::v6::sprmCLid, &SwWW8ImplReader::Read_Language}, // chp.lid LID word + {NS_sprm::v6::sprmCIco, &SwWW8ImplReader::Read_TextColor}, // chp.ico ico byte + {NS_sprm::v6::sprmCHps, &SwWW8ImplReader::Read_FontSize}, // chp.hps hps word! + {NS_sprm::v6::sprmCHpsInc, nullptr}, // chp.hps byte + {NS_sprm::v6::sprmCHpsPos, &SwWW8ImplReader::Read_SubSuperProp}, // chp.hpsPos hps byte + {NS_sprm::v6::sprmCHpsPosAdj, nullptr}, // chp.hpsPos hps byte + {NS_sprm::v6::sprmCMajority, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fSmallCaps + {NS_sprm::v6::sprmCIss, &SwWW8ImplReader::Read_SubSuper}, // chp.iss iss byte + {NS_sprm::v6::sprmCHpsNew50, nullptr}, // chp.hps hps variable width, length always recorded as 2 + {NS_sprm::v6::sprmCHpsInc1, nullptr}, // chp.hps complex variable width, length always recorded as 2 + {NS_sprm::v6::sprmCHpsKern, &SwWW8ImplReader::Read_FontKern}, // chp.hpsKern hps short + {NS_sprm::v6::sprmCMajority50, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fSmallCaps, chp.fVanish, ... + {NS_sprm::v6::sprmCHpsMul, nullptr}, // chp.hps percentage to grow hps short + {NS_sprm::v6::sprmCCondHyhen, nullptr}, // chp.ysri ysri short + {111, &SwWW8ImplReader::Read_AmbiguousSPRM},//sprmCFBoldBi or font code + {112, &SwWW8ImplReader::Read_AmbiguousSPRM},//sprmCFItalicBi or font code + {113, &SwWW8ImplReader::Read_FontCode}, //sprmCFtcBi + {114, &SwWW8ImplReader::Read_Language}, //sprmClidBi + {115, &SwWW8ImplReader::Read_TextColor}, //sprmCIcoBi + {116, &SwWW8ImplReader::Read_FontSize}, //sprmCHpsBi + {NS_sprm::v6::sprmCFSpec, &SwWW8ImplReader::Read_Special}, // chp.fSpec 1 or 0 bit + {NS_sprm::v6::sprmCFObj, &SwWW8ImplReader::Read_Obj}, // chp.fObj 1 or 0 bit + {NS_sprm::v6::sprmPicBrcl, nullptr}, // pic.brcl brcl (see PIC structure definition) byte + {NS_sprm::v6::sprmPicScale, nullptr}, // pic.mx, pic.my, pic.dxaCropleft, + {NS_sprm::v6::sprmPicBrcTop, nullptr}, // pic.brcTop BRC word + {NS_sprm::v6::sprmPicBrcLeft, nullptr}, // pic.brcLeft BRC word + {NS_sprm::v6::sprmPicBrcBottom, nullptr}, // pic.brcBottom BRC word + {NS_sprm::v6::sprmPicBrcRight, nullptr}, // pic.brcRight BRC word + {125, nullptr}, //"??125", + {126, nullptr}, //"??126", + {127, nullptr}, //"??127", + {128, nullptr}, //"??128", + {129, nullptr}, //"??129", + {130, nullptr}, //"??130", + {NS_sprm::v6::sprmSScnsPgn, nullptr}, // sep.cnsPgn cns byte + {NS_sprm::v6::sprmSiHeadingPgn, nullptr}, // sep.iHeadingPgn heading number level byte + {NS_sprm::v6::sprmSOlstAnm, &SwWW8ImplReader::Read_OLST}, // sep.olstAnm OLST variable length + {134, nullptr}, //"??135", + {135, nullptr}, //"??135", + {NS_sprm::v6::sprmSDxaColWidth, nullptr}, // sep.rgdxaColWidthSpacing complex 3 bytes + {NS_sprm::v6::sprmSDxaColSpacing, nullptr}, // sep.rgdxaColWidthSpacing complex 3 bytes + {NS_sprm::v6::sprmSFEvenlySpaced, nullptr}, // sep.fEvenlySpaced 1 or 0 byte + {NS_sprm::v6::sprmSFProtected, nullptr}, // sep.fUnlocked 1 or 0 byte + {NS_sprm::v6::sprmSDmBinFirst, nullptr}, // sep.dmBinFirst word + {NS_sprm::v6::sprmSDmBinOther, nullptr}, // sep.dmBinOther word + {NS_sprm::v6::sprmSBkc, nullptr}, // sep.bkc bkc byte BreakCode + {NS_sprm::v6::sprmSFTitlePage, nullptr}, // sep.fTitlePage 0 or 1 byte + {NS_sprm::v6::sprmSCcolumns, nullptr}, // sep.ccolM1 # of cols - 1 word + {NS_sprm::v6::sprmSDxaColumns, nullptr}, // sep.dxaColumns dxa word + {NS_sprm::v6::sprmSFAutoPgn, nullptr}, // sep.fAutoPgn obsolete byte + {NS_sprm::v6::sprmSNfcPgn, nullptr}, // sep.nfcPgn nfc byte + {NS_sprm::v6::sprmSDyaPgn, nullptr}, // sep.dyaPgn dya short + {NS_sprm::v6::sprmSDxaPgn, nullptr}, // sep.dxaPgn dya short + {NS_sprm::v6::sprmSFPgnRestart, nullptr}, // sep.fPgnRestart 0 or 1 byte + {NS_sprm::v6::sprmSFEndnote, nullptr}, // sep.fEndnote 0 or 1 byte + {NS_sprm::v6::sprmSLnc, nullptr}, // sep.lnc lnc byte + {NS_sprm::v6::sprmSGprfIhdt, nullptr}, // sep.grpfIhdt grpfihdt byte + {NS_sprm::v6::sprmSNLnnMod, nullptr}, // sep.nLnnMod non-neg int. word + {NS_sprm::v6::sprmSDxaLnn, nullptr}, // sep.dxaLnn dxa word + {NS_sprm::v6::sprmSDyaHdrTop, nullptr}, // sep.dyaHdrTop dya word + {NS_sprm::v6::sprmSDyaHdrBottom, nullptr}, // sep.dyaHdrBottom dya word + {NS_sprm::v6::sprmSLBetween, nullptr}, // sep.fLBetween 0 or 1 byte + {NS_sprm::v6::sprmSVjc, nullptr}, // sep.vjc vjc byte + {NS_sprm::v6::sprmSLnnMin, nullptr}, // sep.lnnMin lnn word + {NS_sprm::v6::sprmSPgnStart, nullptr}, // sep.pgnStart pgn word + {NS_sprm::v6::sprmSBOrientation, nullptr}, // sep.dmOrientPage dm byte + {NS_sprm::v6::sprmSBCustomize, nullptr}, // ? + {NS_sprm::v6::sprmSXaPage, nullptr}, // sep.xaPage xa word + {NS_sprm::v6::sprmSYaPage, nullptr}, // sep.yaPage ya word + {NS_sprm::v6::sprmSDxaLeft, nullptr}, // sep.dxaLeft dxa word + {NS_sprm::v6::sprmSDxaRight, nullptr}, // sep.dxaRight dxa word + {NS_sprm::v6::sprmSDyaTop, nullptr}, // sep.dyaTop dya word + {NS_sprm::v6::sprmSDyaBottom, nullptr}, // sep.dyaBottom dya word + {NS_sprm::v6::sprmSDzaGutter, nullptr}, // sep.dzaGutter dza word + {NS_sprm::v6::sprmSDMPaperReq, nullptr}, // sep.dmPaperReq dm word + {172, nullptr}, //"??172", + {173, nullptr}, //"??173", + {174, nullptr}, //"??174", + {175, nullptr}, //"??175", + {176, nullptr}, //"??176", + {177, nullptr}, //"??177", + {178, nullptr}, //"??178", + {179, nullptr}, //"??179", + {180, nullptr}, //"??180", + {181, nullptr}, //"??181", + {NS_sprm::v6::sprmTJc, nullptr}, // tap.jc jc word (low order byte is significant) + {NS_sprm::v6::sprmTDxaLeft, nullptr}, // tap.rgdxaCenter dxa word + {NS_sprm::v6::sprmTDxaGapHalf, nullptr}, // tap.dxaGapHalf, tap.rgdxaCenter dxa word + {NS_sprm::v6::sprmTFCantSplit, nullptr}, // tap.fCantSplit 1 or 0 byte + {NS_sprm::v6::sprmTTableHeader, nullptr}, // tap.fTableHeader 1 or 0 byte + {NS_sprm::v6::sprmTTableBorders, nullptr}, // tap.rgbrcTable complex 12 bytes + {NS_sprm::v6::sprmTDefTable10, nullptr}, // tap.rgdxaCenter, tap.rgtc complex variable length + {NS_sprm::v6::sprmTDyaRowHeight, nullptr}, // tap.dyaRowHeight dya word + {NS_sprm::v6::sprmTDefTable, nullptr}, // tap.rgtc complex + {NS_sprm::v6::sprmTDefTableShd, nullptr}, // tap.rgshd complex + {NS_sprm::v6::sprmTTlp, nullptr}, // tap.tlp TLP 4 bytes + {NS_sprm::v6::sprmTSetBrc, nullptr}, // tap.rgtc[].rgbrc complex 5 bytes + {NS_sprm::v6::sprmTInsert, nullptr}, // tap.rgdxaCenter, tap.rgtc complex 4 bytes + {NS_sprm::v6::sprmTDelete, nullptr}, // tap.rgdxaCenter, tap.rgtc complex word + {NS_sprm::v6::sprmTDxaCol, nullptr}, // tap.rgdxaCenter complex 4 bytes + {NS_sprm::v6::sprmTMerge, nullptr}, // tap.fFirstMerged, tap.fMerged complex word + {NS_sprm::v6::sprmTSplit, nullptr}, // tap.fFirstMerged, tap.fMerged complex word + {NS_sprm::v6::sprmTSetBrc10, nullptr}, // tap.rgtc[].rgbrc complex 5 bytes + {NS_sprm::v6::sprmTSetShd, nullptr}, // tap.rgshd complex 4 bytes + {207, nullptr}, //dunno + }; + + static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +} + +static const wwSprmDispatcher *GetWW8SprmDispatcher() +{ + static SprmReadInfo aSprms[] = + { + {0, nullptr}, // "0" default resp. error + + {NS_sprm::sprmPIstd, &SwWW8ImplReader::Read_StyleCode}, // pap.istd;istd (style code);short; + {NS_sprm::sprmPIstdPermute, nullptr}, // pap.istd;permutation vector; + // variable length; + {NS_sprm::sprmPIncLvl, nullptr}, // pap.istd, pap.lvl;difference between + // istd of base PAP and istd of + // PAP to be produced;byte; + {NS_sprm::sprmPJc80, &SwWW8ImplReader::Read_Justify}, // pap.jc;jc (justification);byte; + {NS_sprm::LN_PFSideBySide, nullptr}, // pap.fSideBySide;0 or 1;byte; + {NS_sprm::sprmPFKeep, &SwWW8ImplReader::Read_KeepLines}, // pap.fKeep;0 or 1;byte; + {NS_sprm::sprmPFKeepFollow, &SwWW8ImplReader::Read_KeepParas}, // pap.fKeepFollow;0 or 1;byte; + {NS_sprm::sprmPFPageBreakBefore, &SwWW8ImplReader::Read_BreakBefore}, // pap.fPageBreakBefore;0 or 1;byte; + {NS_sprm::LN_PBrcl, nullptr}, // pap.brcl;brcl;byte; + {NS_sprm::LN_PBrcp, nullptr}, // pap.brcp;brcp;byte; + {NS_sprm::sprmPIlvl, &SwWW8ImplReader::Read_ListLevel}, // pap.ilvl;ilvl;byte; + {NS_sprm::sprmPIlfo, &SwWW8ImplReader::Read_LFOPosition}, // pap.ilfo;ilfo (list index);short; + {NS_sprm::sprmPFNoLineNumb, &SwWW8ImplReader::Read_NoLineNumb}, // pap.fNoLnn;0 or 1;byte; + {NS_sprm::sprmPChgTabsPapx, &SwWW8ImplReader::Read_Tab}, // pap.itbdMac, pap.rgdxaTab, pap.rgtbd; + // complex;variable length + {NS_sprm::sprmPDxaRight80, &SwWW8ImplReader::Read_LR}, // pap.dxaRight;dxa;word; + {NS_sprm::sprmPDxaLeft80, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft;dxa;word; + {NS_sprm::sprmPNest80, nullptr}, // pap.dxaLeft;dxa;word; + {NS_sprm::sprmPDxaLeft180, &SwWW8ImplReader::Read_LR}, // pap.dxaLeft1;dxa;word; + {NS_sprm::sprmPDyaLine, &SwWW8ImplReader::Read_LineSpace}, // pap.lspd;an LSPD, a long word + // structure consisting of a short + // of dyaLine followed by a short + // of fMultLinespace;long; + {NS_sprm::sprmPDyaBefore, &SwWW8ImplReader::Read_UL}, // pap.dyaBefore;dya;word; + {NS_sprm::sprmPDyaAfter, &SwWW8ImplReader::Read_UL}, // pap.dyaAfter;dya;word; + {NS_sprm::sprmPChgTabs, nullptr}, // pap.itbdMac, pap.rgdxaTab, pap.rgtbd; + // complex;variable length; + {NS_sprm::sprmPFInTable, nullptr}, // pap.fInTable;0 or 1;byte; + {NS_sprm::sprmPFTtp, &SwWW8ImplReader::Read_TabRowEnd}, // pap.fTtp;0 or 1;byte; + {NS_sprm::sprmPDxaAbs, nullptr}, // pap.dxaAbs;dxa;word; + {NS_sprm::sprmPDyaAbs, nullptr}, // pap.dyaAbs;dya;word; + {NS_sprm::sprmPDxaWidth, nullptr}, // pap.dxaWidth;dxa;word; + {NS_sprm::sprmPPc, &SwWW8ImplReader::Read_ApoPPC}, // pap.pcHorz, pap.pcVert;complex;byte; + {NS_sprm::LN_PBrcTop10, nullptr}, // pap.brcTop;BRC10;word; + {NS_sprm::LN_PBrcLeft10, nullptr}, // pap.brcLeft;BRC10;word; + {NS_sprm::LN_PBrcBottom10, nullptr}, // pap.brcBottom;BRC10;word; + {NS_sprm::LN_PBrcRight10, nullptr}, // pap.brcRight;BRC10;word; + {NS_sprm::LN_PBrcBetween10, nullptr}, // pap.brcBetween;BRC10;word; + {NS_sprm::LN_PBrcBar10, nullptr}, // pap.brcBar;BRC10;word; + {NS_sprm::LN_PDxaFromText10, nullptr}, // pap.dxaFromText;dxa;word; + {NS_sprm::sprmPWr, nullptr}, // pap.wr;wr;byte; + {NS_sprm::sprmPBrcTop80, &SwWW8ImplReader::Read_Border}, // pap.brcTop;BRC;long; + {NS_sprm::sprmPBrcLeft80, &SwWW8ImplReader::Read_Border}, // pap.brcLeft;BRC;long; + {NS_sprm::sprmPBrcBottom80, &SwWW8ImplReader::Read_Border}, // pap.brcBottom;BRC;long; + {NS_sprm::sprmPBrcRight80, &SwWW8ImplReader::Read_Border}, // pap.brcRight;BRC;long; + {NS_sprm::sprmPBrcBetween80, &SwWW8ImplReader::Read_Border}, // pap.brcBetween;BRC;long; + {NS_sprm::sprmPBrcBar80, nullptr}, // pap.brcBar;BRC;long; + {NS_sprm::sprmPFNoAutoHyph, &SwWW8ImplReader::Read_Hyphenation}, // pap.fNoAutoHyph;0 or 1;byte; + {NS_sprm::sprmPWHeightAbs, nullptr}, // pap.wHeightAbs;w;word; + {NS_sprm::sprmPDcs, nullptr}, // pap.dcs;DCS;short; + {NS_sprm::sprmPShd80, &SwWW8ImplReader::Read_Shade}, // pap.shd;SHD;word; + {NS_sprm::sprmPDyaFromText, nullptr}, // pap.dyaFromText;dya;word; + {NS_sprm::sprmPDxaFromText, nullptr}, // pap.dxaFromText;dxa;word; + {NS_sprm::sprmPFLocked, nullptr}, // pap.fLocked;0 or 1;byte; + {NS_sprm::sprmPFWidowControl, &SwWW8ImplReader::Read_WidowControl}, // pap.fWidowControl;0 or 1;byte + {NS_sprm::LN_PRuler, nullptr}, // variable length; + {NS_sprm::sprmPFKinsoku, &SwWW8ImplReader::Read_BoolItem}, // pap.fKinsoku;0 or 1;byte; + {NS_sprm::sprmPFWordWrap, nullptr}, // pap.fWordWrap;0 or 1;byte; + {NS_sprm::sprmPFOverflowPunct, &SwWW8ImplReader::Read_BoolItem}, // pap.fOverflowPunct; 0 or 1;byte; + {NS_sprm::sprmPFTopLinePunct, nullptr}, // pap.fTopLinePunct;0 or 1;byte + {NS_sprm::sprmPFAutoSpaceDE, &SwWW8ImplReader::Read_BoolItem}, // pap.fAutoSpaceDE;0 or 1;byte; + {NS_sprm::sprmPFAutoSpaceDN, nullptr}, // pap.fAutoSpaceDN;0 or 1;byte; + {NS_sprm::sprmPWAlignFont, &SwWW8ImplReader::Read_AlignFont}, // pap.wAlignFont;iFa;word; + {NS_sprm::sprmPFrameTextFlow, nullptr}, // pap.fVertical pap.fBackward + // pap.fRotateFont;complex; word + {NS_sprm::LN_PISnapBaseLine, nullptr}, // obsolete, not applicable in + // Word97 and later versions;;byte; + {NS_sprm::LN_PAnld, &SwWW8ImplReader::Read_ANLevelDesc}, // pap.anld;;variable length; + {NS_sprm::LN_PPropRMark, nullptr}, // pap.fPropRMark;complex; + // variable length; + {NS_sprm::sprmPOutLvl, &SwWW8ImplReader::Read_POutLvl}, // pap.lvl;has no effect if pap.istd + // is < 1 or is > 9;byte; + {NS_sprm::sprmPFBiDi, &SwWW8ImplReader::Read_ParaBiDi}, // ;;byte; + {NS_sprm::sprmPFNumRMIns, nullptr}, // pap.fNumRMIns;1 or 0;bit; + {NS_sprm::LN_PCrLf, nullptr}, // ;;byte; + {NS_sprm::sprmPNumRM, nullptr}, // pap.numrm;;variable length; + {NS_sprm::LN_PHugePapx, nullptr}, // ;fc in the data stream to locate + // the huge grpprl;long; + {NS_sprm::sprmPHugePapx, nullptr}, // ;fc in the data stream to locate + // the huge grpprl;long; + {NS_sprm::sprmPFUsePgsuSettings, &SwWW8ImplReader::Read_UsePgsuSettings}, // pap.fUsePgsuSettings;1 or 0;byte; + {NS_sprm::sprmPFAdjustRight, nullptr}, // pap.fAdjustRight;1 or 0;byte; + {NS_sprm::sprmCFRMarkDel, &SwWW8ImplReader::Read_CFRMarkDel}, // chp.fRMarkDel;1 or 0;bit; + {NS_sprm::sprmCFRMarkIns, &SwWW8ImplReader::Read_CFRMark}, // chp.fRMark;1 or 0;bit; + {NS_sprm::sprmCFFldVanish, &SwWW8ImplReader::Read_FieldVanish}, // chp.fFieldVanish;1 or 0;bit; + {NS_sprm::sprmCPicLocation, &SwWW8ImplReader::Read_PicLoc}, // chp.fcPic and chp.fSpec;variable + // length, length recorded is always 4; + {NS_sprm::sprmCIbstRMark, nullptr}, // chp.ibstRMark;index into + // sttbRMark;short; + {NS_sprm::sprmCDttmRMark, nullptr}, // chp.dttmRMark;DTTM;long; + {NS_sprm::sprmCFData, nullptr}, // chp.fData;1 or 0;bit; + {NS_sprm::sprmCIdslRMark, nullptr}, // chp.idslRMReason;an index to + // a table of strings defined in + // Word 6.0 executables;short; + {NS_sprm::LN_CChs, &SwWW8ImplReader::Read_CharSet}, // chp.fChsDiff and chp.chse;3 bytes; + {NS_sprm::sprmCSymbol, &SwWW8ImplReader::Read_Symbol}, // chp.fSpec, chp.xchSym and chp.ftcSym; + // variable length, length + // recorded is always 4; + {NS_sprm::sprmCFOle2, &SwWW8ImplReader::Read_Obj}, // chp.fOle2;1 or 0;bit; + //NS_sprm::LN_CIdCharType, // obsolete: not applicable in Word97 + // and later versions + {NS_sprm::sprmCHighlight, &SwWW8ImplReader::Read_CharHighlight}, // chp.fHighlight, chp.icoHighlight;ico + // (fHighlight is set to 1 iff + // ico is not 0);byte; + {NS_sprm::LN_CObjLocation, &SwWW8ImplReader::Read_PicLoc}, // chp.fcObj;FC;long; + //NS_sprm::LN_CFFtcAsciSymb, ? ? ?, + {NS_sprm::sprmCIstd, &SwWW8ImplReader::Read_CColl}, // chp.istd;istd,short; + {NS_sprm::sprmCIstdPermute, nullptr}, // chp.istd;permutation vector; + // variable length; + {NS_sprm::LN_CDefault, nullptr}, // whole CHP;none;variable length; + {NS_sprm::sprmCPlain, nullptr}, // whole CHP;none;length: 0; + {NS_sprm::sprmCKcd, &SwWW8ImplReader::Read_Emphasis}, + {NS_sprm::sprmCFBold, &SwWW8ImplReader::Read_BoldUsw}, // chp.fBold;0,1, 128, or 129;byte; + {NS_sprm::sprmCFItalic, &SwWW8ImplReader::Read_BoldUsw}, // chp.fItalic;0,1, 128, or 129; byte; + {NS_sprm::sprmCFStrike, &SwWW8ImplReader::Read_BoldUsw}, // chp.fStrike;0,1, 128, or 129; byte; + {NS_sprm::sprmCFOutline, &SwWW8ImplReader::Read_BoldUsw}, // chp.fOutline;0,1, 128, or 129; byte; + {NS_sprm::sprmCFShadow, &SwWW8ImplReader::Read_BoldUsw}, // chp.fShadow;0,1, 128, or 129; byte; + {NS_sprm::sprmCFSmallCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fSmallCaps;0,1, 128, or 129;byte; + {NS_sprm::sprmCFCaps, &SwWW8ImplReader::Read_BoldUsw}, // chp.fCaps;0,1, 128, or 129; byte; + {NS_sprm::sprmCFVanish, &SwWW8ImplReader::Read_BoldUsw}, // chp.fVanish;0,1, 128, or 129; byte; + //NS_sprm::LN_CFtcDefault, 0, // ftc, only used internally, never + // stored in file;word; + {NS_sprm::sprmCKul, &SwWW8ImplReader::Read_Underline}, // chp.kul;kul;byte; + {NS_sprm::LN_CSizePos, nullptr}, // chp.hps, chp.hpsPos;3 bytes; + {NS_sprm::sprmCDxaSpace, &SwWW8ImplReader::Read_Kern}, // chp.dxaSpace;dxa;word; + {NS_sprm::LN_CLid, &SwWW8ImplReader::Read_Language}, // ;only used internally, never stored; + // word; + {NS_sprm::sprmCIco, &SwWW8ImplReader::Read_TextColor}, // chp.ico;ico;byte; + {NS_sprm::sprmCHps, &SwWW8ImplReader::Read_FontSize}, // chp.hps;hps;byte; + {NS_sprm::LN_CHpsInc, nullptr}, // chp.hps;byte; + {NS_sprm::sprmCHpsPos, &SwWW8ImplReader::Read_SubSuperProp}, // chp.hpsPos;hps;byte; + {NS_sprm::LN_CHpsPosAdj, nullptr}, // chp.hpsPos;hps;byte; + {NS_sprm::sprmCMajority, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fStrike, + // chp.fSmallCaps, chp.fVanish, chp.fCaps, + // chp.hps, chp.hpsPos, chp.dxaSpace, + // chp.kul, chp.ico, chp.rgftc, chp.rglid; + // complex;variable length, length byte + // plus size of following grpprl; + {NS_sprm::sprmCIss, &SwWW8ImplReader::Read_SubSuper}, // chp.iss;iss;byte; + {NS_sprm::LN_CHpsNew50, nullptr}, // chp.hps;hps;variable width, length + // always recorded as 2; + {NS_sprm::LN_CHpsInc1, nullptr}, // chp.hps;complex; variable width, + // length always recorded as 2; + {NS_sprm::sprmCHpsKern, &SwWW8ImplReader::Read_FontKern}, // chp.hpsKern;hps;short; + {NS_sprm::LN_CMajority50, &SwWW8ImplReader::Read_Majority}, // chp.fBold, chp.fItalic, chp.fStrike, + // chp.fSmallCaps, chp.fVanish, chp.fCaps, + // chp.ftc, chp.hps, chp.hpsPos, chp.kul, + // chp.dxaSpace, chp.ico;complex; + // variable length; + {NS_sprm::LN_CHpsMul, nullptr}, // chp.hps;percentage to grow hps;short; + {NS_sprm::sprmCHresi, nullptr}, // ???? "sprmCYsri" chp.ysri;ysri;short; + {NS_sprm::sprmCRgFtc0, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[0];ftc for ASCII text;short; + {NS_sprm::sprmCRgFtc1, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[1];ftc for Far East text; + // short; + {NS_sprm::sprmCRgFtc2, &SwWW8ImplReader::Read_FontCode}, // chp.rgftc[2];ftc for non-Far East text; + // short; + {NS_sprm::sprmCCharScale, &SwWW8ImplReader::Read_ScaleWidth}, + {NS_sprm::sprmCFDStrike, &SwWW8ImplReader::Read_BoldUsw}, // chp.fDStrike;;byte; + {NS_sprm::sprmCFImprint, &SwWW8ImplReader::Read_Relief}, // chp.fImprint;1 or 0;bit; + {NS_sprm::sprmCFSpec, &SwWW8ImplReader::Read_Special}, // chp.fSpec;1 or 0;bit; + {NS_sprm::sprmCFObj, &SwWW8ImplReader::Read_Obj}, // chp.fObj;1 or 0;bit; + {NS_sprm::sprmCPropRMark90, &SwWW8ImplReader::Read_CPropRMark}, // chp.fPropRMark, chp.ibstPropRMark, + // chp.dttmPropRMark;Complex;variable + // length always recorded as 7 bytes; + {NS_sprm::sprmCFEmboss, &SwWW8ImplReader::Read_Relief}, // chp.fEmboss;1 or 0;bit; + {NS_sprm::sprmCSfxText, &SwWW8ImplReader::Read_TextAnim}, // chp.sfxtText;text animation;byte; + {NS_sprm::sprmCFBiDi, &SwWW8ImplReader::Read_Bidi}, + {NS_sprm::LN_CFDiacColor, nullptr}, + {NS_sprm::sprmCFBoldBi, &SwWW8ImplReader::Read_BoldBiDiUsw}, + {NS_sprm::sprmCFItalicBi, &SwWW8ImplReader::Read_BoldBiDiUsw}, + {NS_sprm::sprmCFtcBi, &SwWW8ImplReader::Read_FontCode}, + {NS_sprm::sprmCLidBi, &SwWW8ImplReader::Read_Language}, + //NS_sprm::sprmCIcoBi, ? ? ?, + {NS_sprm::sprmCHpsBi, &SwWW8ImplReader::Read_FontSize}, + {NS_sprm::sprmCDispFldRMark, nullptr}, // chp.fDispFieldRMark, + // chp.ibstDispFieldRMark, + // chp.dttmDispFieldRMark; + // Complex;variable length + // always recorded as 39 bytes; + {NS_sprm::sprmCIbstRMarkDel, nullptr}, // chp.ibstRMarkDel;index into + // sttbRMark;short; + {NS_sprm::sprmCDttmRMarkDel, nullptr}, // chp.dttmRMarkDel;DTTM;long; + {NS_sprm::sprmCBrc80, &SwWW8ImplReader::Read_CharBorder}, // chp.brc;BRC;long; + {NS_sprm::sprmCBrc, &SwWW8ImplReader::Read_CharBorder}, // chp.brc;BRC;long; + {NS_sprm::sprmCShd80, &SwWW8ImplReader::Read_CharShadow}, // chp.shd;SHD;short; + {NS_sprm::sprmCIdslRMarkDel, nullptr}, // chp.idslRMReasonDel;an index to + // a table of strings defined in + // Word 6.0 executables;short; + {NS_sprm::sprmCFUsePgsuSettings, nullptr}, // chp.fUsePgsuSettings; 1 or 0;bit; + {NS_sprm::LN_CCpg, nullptr}, // ;;word; + {NS_sprm::sprmCRgLid0_80, &SwWW8ImplReader::Read_Language}, // chp.rglid[0]; + // LID: for non-Far East text;word; + {NS_sprm::sprmCRgLid1_80, &SwWW8ImplReader::Read_Language}, // chp.rglid[1]; + // LID: for Far East text;word; + {NS_sprm::sprmCIdctHint, &SwWW8ImplReader::Read_IdctHint}, // chp.idctHint;IDCT: byte; + {NS_sprm::LN_PicBrcl, nullptr}, // pic.brcl;brcl (see PIC structure + // definition);byte; + {NS_sprm::LN_PicScale, nullptr}, // pic.mx, pic.my, pic.dxaCropleft, + // pic.dyaCropTop pic.dxaCropRight, + // pic.dyaCropBottom;Complex; + // length byte plus 12 bytes; + {NS_sprm::sprmPicBrcTop80, nullptr}, // pic.brcTop;BRC;long; + {NS_sprm::sprmPicBrcLeft80, nullptr}, // pic.brcLeft;BRC;long; + {NS_sprm::sprmPicBrcBottom80, nullptr}, // pic.brcBottom;BRC;long; + {NS_sprm::sprmPicBrcRight80, nullptr}, // pic.brcRight;BRC;long; + {NS_sprm::sprmScnsPgn, nullptr}, // sep.cnsPgn;cns;byte; + {NS_sprm::sprmSiHeadingPgn, nullptr}, // sep.iHeadingPgn;heading number level; + // byte; + {NS_sprm::LN_SOlstAnm, &SwWW8ImplReader::Read_OLST}, // sep.olstAnm;OLST;variable length; + {NS_sprm::sprmSDxaColWidth, nullptr}, // sep.rgdxaColWidthSpacing;complex; + // 3 bytes; + {NS_sprm::sprmSDxaColSpacing, nullptr}, // sep.rgdxaColWidthSpacing;complex; + // 3 bytes; + {NS_sprm::sprmSFEvenlySpaced, nullptr}, // sep.fEvenlySpaced; 1 or 0;byte; + {NS_sprm::sprmSFProtected, nullptr}, // sep.fUnlocked;1 or 0;byte; + {NS_sprm::sprmSDmBinFirst, nullptr}, // sep.dmBinFirst;;word; + {NS_sprm::sprmSDmBinOther, nullptr}, // sep.dmBinOther;;word; + {NS_sprm::sprmSBkc, nullptr}, // sep.bkc;bkc;byte; + {NS_sprm::sprmSFTitlePage, nullptr}, // sep.fTitlePage;0 or 1;byte; + {NS_sprm::sprmSCcolumns, nullptr}, // sep.ccolM1;# of cols - 1;word; + {NS_sprm::sprmSDxaColumns, nullptr}, // sep.dxaColumns;dxa;word; + {NS_sprm::LN_SFAutoPgn, nullptr}, // sep.fAutoPgn;obsolete;byte; + {NS_sprm::sprmSNfcPgn, nullptr}, // sep.nfcPgn;nfc;byte; + {NS_sprm::LN_SDyaPgn, nullptr}, // sep.dyaPgn;dya;short; + {NS_sprm::LN_SDxaPgn, nullptr}, // sep.dxaPgn;dya;short; + {NS_sprm::sprmSFPgnRestart, nullptr}, // sep.fPgnRestart;0 or 1;byte; + {NS_sprm::sprmSFEndnote, nullptr}, // sep.fEndnote;0 or 1;byte; + {NS_sprm::sprmSLnc, nullptr}, // sep.lnc;lnc;byte; + {NS_sprm::LN_SGprfIhdt, nullptr}, // sep.grpfIhdt;grpfihdt;byte; + {NS_sprm::sprmSNLnnMod, nullptr}, // sep.nLnnMod;non-neg int.;word; + {NS_sprm::sprmSDxaLnn, nullptr}, // sep.dxaLnn;dxa;word; + {NS_sprm::sprmSDyaHdrTop, nullptr}, // sep.dyaHdrTop;dya;word; + {NS_sprm::sprmSDyaHdrBottom, nullptr}, // sep.dyaHdrBottom;dya;word; + {NS_sprm::sprmSLBetween, nullptr}, // sep.fLBetween;0 or 1;byte; + {NS_sprm::sprmSVjc, &SwWW8ImplReader::Read_TextVerticalAdjustment}, // sep.vjc;vjc;byte; + {NS_sprm::sprmSLnnMin, nullptr}, // sep.lnnMin;lnn;word; + {NS_sprm::sprmSPgnStart97, nullptr}, // sep.pgnStart;pgn;word; + {NS_sprm::sprmSBOrientation, nullptr}, // sep.dmOrientPage;dm;byte; + //NS_sprm::LN_SBCustomize, ? ? ?, + {NS_sprm::sprmSXaPage, nullptr}, // sep.xaPage;xa;word; + {NS_sprm::sprmSYaPage, nullptr}, // sep.yaPage;ya;word; + {0x2205, nullptr}, // ???? "sprmSDxaLeft" sep.dxaLeft; + // dxa;word; + {NS_sprm::sprmSDxaLeft, nullptr}, // sep.dxaLeft;dxa;word; + {NS_sprm::sprmSDxaRight, nullptr}, // sep.dxaRight;dxa;word; + {NS_sprm::sprmSDyaTop, nullptr}, // sep.dyaTop;dya;word; + {NS_sprm::sprmSDyaBottom, nullptr}, // sep.dyaBottom;dya;word; + {NS_sprm::sprmSDzaGutter, nullptr}, // sep.dzaGutter;dza;word; + {NS_sprm::sprmSDmPaperReq, nullptr}, // sep.dmPaperReq;dm;word; + {NS_sprm::LN_SPropRMark, nullptr}, // sep.fPropRMark, sep.ibstPropRMark, + // sep.dttmPropRMark;complex; variable + // length always recorded as 7 bytes; + //NS_sprm::sprmSFBiDi, ? ? ?, + //NS_sprm::LN_SFFacingCol, ? ? ?, + {NS_sprm::sprmSFRTLGutter, nullptr}, // set to 1 if gutter is on the right. + {NS_sprm::sprmSBrcTop80, nullptr}, // sep.brcTop;BRC;long; + {NS_sprm::sprmSBrcLeft80, nullptr}, // sep.brcLeft;BRC;long; + {NS_sprm::sprmSBrcBottom80, nullptr}, // sep.brcBottom;BRC;long; + {NS_sprm::sprmSBrcRight80, nullptr}, // sep.brcRight;BRC;long; + {NS_sprm::sprmSPgbProp, nullptr}, // sep.pgbProp;word; + {NS_sprm::sprmSDxtCharSpace, nullptr}, // sep.dxtCharSpace;dxt;long; + {NS_sprm::sprmSDyaLinePitch, nullptr}, // sep.dyaLinePitch;dya; + // WRONG:long; RIGHT:short; ! + //NS_sprm::sprmSClm, ? ? ?, + {NS_sprm::sprmSTextFlow, nullptr}, // sep.wTextFlow;complex;short + {NS_sprm::sprmTJc90, nullptr}, // tap.jc;jc;word + // (low order byte is significant); + {NS_sprm::sprmTDxaLeft, nullptr}, // tap.rgdxaCenter;dxa;word; + {NS_sprm::sprmTDxaGapHalf, nullptr}, // tap.dxaGapHalf, + // tap.rgdxaCenter;dxa;word; + {NS_sprm::sprmTFCantSplit90, nullptr}, // tap.fCantSplit90;1 or 0;byte; + {NS_sprm::sprmTTableHeader, nullptr}, // tap.fTableHeader;1 or 0;byte; + {NS_sprm::sprmTFCantSplit, nullptr}, // tap.fCantSplit;1 or 0;byte; + {NS_sprm::sprmTTableBorders80, nullptr}, // tap.rgbrcTable;complex;24 bytes; + {NS_sprm::LN_TDefTable10, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex; + // variable length; + {NS_sprm::sprmTDyaRowHeight, nullptr}, // tap.dyaRowHeight;dya;word; + {NS_sprm::sprmTDefTable, nullptr}, // tap.rgtc;complex + {NS_sprm::sprmTDefTableShd80, nullptr}, // tap.rgshd;complex + {NS_sprm::sprmTTlp, nullptr}, // tap.tlp;TLP;4 bytes; + //NS_sprm::sprmTFBiDi, ? ? ?, + //NS_sprm::LN_THTMLProps, ? ? ?, + {NS_sprm::sprmTSetBrc80, nullptr}, // tap.rgtc[].rgbrc;complex;5 bytes; + {NS_sprm::sprmTInsert, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex; + // 4 bytes; + {NS_sprm::sprmTDelete, nullptr}, // tap.rgdxaCenter, tap.rgtc;complex; + // word; + {NS_sprm::sprmTDxaCol, nullptr}, // tap.rgdxaCenter;complex;4 bytes; + {NS_sprm::sprmTMerge, nullptr}, // tap.fFirstMerged, tap.fMerged; + // complex; word; + {NS_sprm::sprmTSplit, nullptr}, // tap.fFirstMerged, tap.fMerged; + // complex;word; + {NS_sprm::LN_TSetBrc10, nullptr}, // tap.rgtc[].rgbrc;complex;5 bytes; + {NS_sprm::LN_TSetShd80, nullptr}, // tap.rgshd;complex;4 bytes; + {NS_sprm::LN_TSetShdOdd80, nullptr}, // tap.rgshd;complex;4 bytes; + {NS_sprm::sprmTTextFlow, nullptr}, // tap.rgtc[].fVertical + // tap.rgtc[].fBackward + // tap.rgtc[].fRotateFont + // 0 or 10 or 10 or 1;word; + //NS_sprm::LN_TDiagLine, ? ? ? , + {NS_sprm::sprmTVertMerge, nullptr}, // tap.rgtc[].vertMerge;complex;variable + // length always recorded as 2 bytes; + {NS_sprm::sprmTVertAlign, nullptr}, // tap.rgtc[].vertAlign;complex;variable + // length always recorded as 3 bytes; + {NS_sprm::sprmCFELayout, &SwWW8ImplReader::Read_DoubleLine_Rotate}, + {NS_sprm::sprmPItap, nullptr}, + {NS_sprm::sprmTTableWidth, nullptr}, // recorded as 3 bytes; + {NS_sprm::sprmTDefTableShd, nullptr}, + {NS_sprm::sprmTTableBorders, nullptr}, + {NS_sprm::sprmTBrcTopCv, nullptr}, + {NS_sprm::sprmTBrcLeftCv, nullptr}, + {NS_sprm::sprmTBrcBottomCv, nullptr}, + {NS_sprm::sprmTBrcRightCv, nullptr}, + {NS_sprm::sprmTCellPaddingDefault, nullptr}, + {NS_sprm::sprmTCellPadding, nullptr}, + {0xD238, nullptr}, // undocumented sep + {NS_sprm::sprmPBrcTop, &SwWW8ImplReader::Read_Border}, + {NS_sprm::sprmPBrcLeft, &SwWW8ImplReader::Read_Border}, + {NS_sprm::sprmPBrcBottom, &SwWW8ImplReader::Read_Border}, + {NS_sprm::sprmPBrcRight, &SwWW8ImplReader::Read_Border}, + {NS_sprm::sprmPBrcBetween, &SwWW8ImplReader::Read_Border}, + {NS_sprm::sprmTWidthIndent, nullptr}, + {NS_sprm::sprmCRgLid0, &SwWW8ImplReader::Read_Language}, // chp.rglid[0]; + // LID: for non-Far East text; + {NS_sprm::sprmCRgLid1, nullptr}, // chp.rglid[1]; + // LID: for Far East text + {0x6463, nullptr}, // undocumented + {NS_sprm::sprmPJc, &SwWW8ImplReader::Read_RTLJustify}, + {NS_sprm::sprmPDxaLeft, &SwWW8ImplReader::Read_LR}, + {NS_sprm::sprmPDxaLeft1, &SwWW8ImplReader::Read_LR}, + {NS_sprm::sprmPDxaRight, &SwWW8ImplReader::Read_LR}, + {NS_sprm::sprmTFAutofit, nullptr}, + {NS_sprm::sprmTPc, nullptr}, + {NS_sprm::sprmTDxaAbs, nullptr}, + {NS_sprm::sprmTDyaAbs, nullptr}, + {NS_sprm::sprmTDxaFromText, nullptr}, + {NS_sprm::sprmSRsid, nullptr}, + {NS_sprm::sprmSFpc, nullptr}, + {NS_sprm::sprmPFInnerTableCell, &SwWW8ImplReader::Read_TabCellEnd}, + {NS_sprm::sprmPFInnerTtp, &SwWW8ImplReader::Read_TabRowEnd}, + {NS_sprm::sprmCRsidProp, nullptr}, + {NS_sprm::sprmCRsidText, nullptr}, + {NS_sprm::sprmCCv, &SwWW8ImplReader::Read_TextForeColor}, + {NS_sprm::sprmCCvUl, &SwWW8ImplReader::Read_UnderlineColor}, + {NS_sprm::sprmPShd, &SwWW8ImplReader::Read_ParaBackColor}, + {NS_sprm::sprmPRsid, nullptr}, + {NS_sprm::sprmTWidthBefore, nullptr}, + {NS_sprm::sprmTSetShdTable, nullptr}, + {NS_sprm::sprmTDefTableShdRaw, nullptr}, + {NS_sprm::sprmCShd, &SwWW8ImplReader::Read_TextBackColor}, + {NS_sprm::sprmSRncFtn, nullptr}, + {NS_sprm::sprmPFDyaBeforeAuto, &SwWW8ImplReader::Read_ParaAutoBefore}, + {NS_sprm::sprmPFDyaAfterAuto, &SwWW8ImplReader::Read_ParaAutoAfter}, + {NS_sprm::sprmPFContextualSpacing, &SwWW8ImplReader::Read_ParaContextualSpacing}, + }; + + static wwSprmDispatcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +} + +// helper routines : find SPRM + +const SprmReadInfo& SwWW8ImplReader::GetSprmReadInfo(sal_uInt16 nId) const +{ + ww::WordVersion eVersion = m_xWwFib->GetFIBVersion(); + const wwSprmDispatcher *pDispatcher; + if (eVersion <= ww::eWW2) + pDispatcher = GetWW2SprmDispatcher(); + else if (eVersion < ww::eWW8) + pDispatcher = GetWW6SprmDispatcher(); + else + pDispatcher = GetWW8SprmDispatcher(); + + SprmReadInfo aSrch = {0, nullptr}; + aSrch.nId = nId; + const SprmReadInfo* pFound = pDispatcher->search(aSrch); + + if (!pFound) + { + aSrch.nId = 0; + pFound = pDispatcher->search(aSrch); + } + + return *pFound; +} + +// helper routines : SPRMs + +void SwWW8ImplReader::EndSprm( sal_uInt16 nId ) +{ + if( ( nId > 255 ) && ( nId < 0x0800 ) ) return; + + const SprmReadInfo& rSprm = GetSprmReadInfo( nId ); + + if (rSprm.pReadFnc) + (this->*rSprm.pReadFnc)( nId, nullptr, -1 ); +} + +short SwWW8ImplReader::ImportSprm(const sal_uInt8* pPos, sal_Int32 nMemLen, sal_uInt16 nId) +{ + if (!nId) + nId = m_xSprmParser->GetSprmId(pPos); + + OSL_ENSURE( nId != 0xff, "Sprm FF !!!!" ); + + const SprmReadInfo& rSprm = GetSprmReadInfo(nId); + + sal_uInt16 nFixedLen = m_xSprmParser->DistanceToData(nId); + sal_uInt16 nL = m_xSprmParser->GetSprmSize(nId, pPos, nMemLen); + + if (rSprm.pReadFnc) + (this->*rSprm.pReadFnc)(nId, pPos + nFixedLen, nL - nFixedLen); + + return nL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8scan.cxx b/sw/source/filter/ww8/ww8scan.cxx new file mode 100644 index 000000000..14780313c --- /dev/null +++ b/sw/source/filter/ww8/ww8scan.cxx @@ -0,0 +1,8480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "ww8scan.hxx" +#include "ww8par.hxx" + +#include <cassert> +#include <cstddef> +#include <cstring> +#include <algorithm> + +#include <i18nlangtag/mslangid.hxx> +#include "sprmids.hxx" +#include <rtl/tencinfo.h> +#include <sal/macros.h> +#include <sal/log.hxx> + +#include <swerror.h> + +#include <comphelper/string.hxx> +#include <unotools/localedatawrapper.hxx> +#include <i18nlangtag/lang.h> +#include <o3tl/safeint.hxx> +#include <tools/stream.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#ifdef DEBUGSPRMREADER +#include <stdio.h> +#endif + +using namespace ::com::sun::star::lang; + +namespace +{ + /** + winword strings are typically Belt and Braces strings preceded with a + pascal style count, and ending with a c style 0 terminator. 16bit chars + and count for ww8+ and 8bit chars and count for ww7-. The count and 0 + can be checked for integrity to catch errors (e.g. lotus created + documents) where in error 8bit strings are used instead of 16bits + strings for style names. + */ + bool TestBeltAndBraces(SvStream& rStrm) + { + bool bRet = false; + sal_uInt32 nOldPos = rStrm.Tell(); + sal_uInt16 nBelt(0); + rStrm.ReadUInt16( nBelt ); + nBelt *= sizeof(sal_Unicode); + if (rStrm.good() && (rStrm.remainingSize() >= (nBelt + sizeof(sal_Unicode)))) + { + rStrm.SeekRel(nBelt); + if (rStrm.good()) + { + sal_Unicode cBraces(0); + rStrm.ReadUtf16( cBraces ); + if (rStrm.good() && cBraces == 0) + bRet = true; + } + } + rStrm.Seek(nOldPos); + return bRet; + } +} + +const wwSprmSearcher *wwSprmParser::GetWW2SprmSearcher() +{ + //double lock me + // WW2 Sprms + static const SprmInfoRow aSprms[] = + { + { 0, { 0, L_FIX} }, // "Default-sprm", will be skipped + { 2, { 1, L_FIX} }, // "sprmPIstd", pap.istd (style code) + { 3, { 0, L_VAR} }, // "sprmPIstdPermute pap.istd permutation + { 4, { 1, L_FIX} }, // "sprmPIncLv1" pap.istddifference + { 5, { 1, L_FIX} }, // "sprmPJc" pap.jc (justification) + { 6, { 1, L_FIX} }, // "sprmPFSideBySide" pap.fSideBySide + { 7, { 1, L_FIX} }, // "sprmPFKeep" pap.fKeep + { 8, { 1, L_FIX} }, // "sprmPFKeepFollow " pap.fKeepFollow + { 9, { 1, L_FIX} }, // "sprmPPageBreakBefore" pap.fPageBreakBefore + { 10, { 1, L_FIX} }, // "sprmPBrcl" pap.brcl + { 11, { 1, L_FIX} }, // "sprmPBrcp" pap.brcp + { 12, { 1, L_FIX} }, // "sprmPNfcSeqNumb" pap.nfcSeqNumb + { 13, { 1, L_FIX} }, // "sprmPNoSeqNumb" pap.nnSeqNumb + { 14, { 1, L_FIX} }, // "sprmPFNoLineNumb" pap.fNoLnn + { 15, { 0, L_VAR} }, // "?sprmPChgTabsPapx" pap.itbdMac, ... + { 16, { 2, L_FIX} }, // "sprmPDxaRight" pap.dxaRight + { 17, { 2, L_FIX} }, // "sprmPDxaLeft" pap.dxaLeft + { 18, { 2, L_FIX} }, // "sprmPNest" pap.dxaLeft + { 19, { 2, L_FIX} }, // "sprmPDxaLeft1" pap.dxaLeft1 + { 20, { 2, L_FIX} }, // "sprmPDyaLine" pap.lspd an LSPD + { 21, { 2, L_FIX} }, // "sprmPDyaBefore" pap.dyaBefore + { 22, { 2, L_FIX} }, // "sprmPDyaAfter" pap.dyaAfter + { 23, { 0, L_VAR} }, // "?sprmPChgTabs" pap.itbdMac, pap.rgdxaTab, ... + { 24, { 1, L_FIX} }, // "sprmPFInTable" pap.fInTable + { 25, { 1, L_FIX} }, // "sprmPTtp" pap.fTtp + { 26, { 2, L_FIX} }, // "sprmPDxaAbs" pap.dxaAbs + { 27, { 2, L_FIX} }, // "sprmPDyaAbs" pap.dyaAbs + { 28, { 2, L_FIX} }, // "sprmPDxaWidth" pap.dxaWidth + { 29, { 1, L_FIX} }, // "sprmPPc" pap.pcHorz, pap.pcVert + { 30, { 2, L_FIX} }, // "sprmPBrcTop10" pap.brcTop BRC10 + { 31, { 2, L_FIX} }, // "sprmPBrcLeft10" pap.brcLeft BRC10 + { 32, { 2, L_FIX} }, // "sprmPBrcBottom10" pap.brcBottom BRC10 + { 33, { 2, L_FIX} }, // "sprmPBrcRight10" pap.brcRight BRC10 + { 34, { 2, L_FIX} }, // "sprmPBrcBetween10" pap.brcBetween BRC10 + { 35, { 2, L_FIX} }, // "sprmPBrcBar10" pap.brcBar BRC10 + { 36, { 2, L_FIX} }, // "sprmPFromText10" pap.dxaFromText dxa + { 37, { 1, L_FIX} }, // "sprmPWr" pap.wr wr + { 38, { 2, L_FIX} }, // "sprmPBrcTop" pap.brcTop BRC + { 39, { 2, L_FIX} }, // "sprmPBrcLeft" pap.brcLeft BRC + { 40, { 2, L_FIX} }, // "sprmPBrcBottom" pap.brcBottom BRC + { 41, { 2, L_FIX} }, // "sprmPBrcRight" pap.brcRight BRC + { 42, { 2, L_FIX} }, // "sprmPBrcBetween" pap.brcBetween BRC + { 43, { 2, L_FIX} }, // "sprmPBrcBar" pap.brcBar BRC word + { 44, { 1, L_FIX} }, // "sprmPFNoAutoHyph" pap.fNoAutoHyph + { 45, { 2, L_FIX} }, // "sprmPWHeightAbs" pap.wHeightAbs w + { 46, { 2, L_FIX} }, // "sprmPDcs" pap.dcs DCS + { 47, { 2, L_FIX} }, // "sprmPShd" pap.shd SHD + { 48, { 2, L_FIX} }, // "sprmPDyaFromText" pap.dyaFromText dya + { 49, { 2, L_FIX} }, // "sprmPDxaFromText" pap.dxaFromText dxa + { 50, { 1, L_FIX} }, // "sprmPFBiDi" pap.fBiDi 0 or 1 byte + { 51, { 1, L_FIX} }, // "sprmPFWidowControl" pap.fWidowControl 0 or 1 byte + { 52, { 0, L_FIX} }, // "?sprmPRuler 52" + { 53, { 1, L_FIX} }, // "sprmCFStrikeRM" chp.fRMarkDel 1 or 0 bit + { 54, { 1, L_FIX} }, // "sprmCFRMark" chp.fRMark 1 or 0 bit + { 55, { 1, L_FIX} }, // "sprmCFFieldVanish" chp.fFieldVanish 1 or 0 bit + { 57, { 0, L_VAR} }, // "sprmCDefault" whole CHP + { 58, { 0, L_FIX} }, // "sprmCPlain" whole CHP + { 60, { 1, L_FIX} }, // "sprmCFBold" chp.fBold 0,1, 128, or 129 + { 61, { 1, L_FIX} }, // "sprmCFItalic" chp.fItalic 0,1, 128, or 129 + { 62, { 1, L_FIX} }, // "sprmCFStrike" chp.fStrike 0,1, 128, or 129 + { 63, { 1, L_FIX} }, // "sprmCFOutline" chp.fOutline 0,1, 128, or 129 + { 64, { 1, L_FIX} }, // "sprmCFShadow" chp.fShadow 0,1, 128, or 129 + { 65, { 1, L_FIX} }, // "sprmCFSmallCaps" chp.fSmallCaps 0,1, 128, or 129 + { 66, { 1, L_FIX} }, // "sprmCFCaps" chp.fCaps 0,1, 128, or 129 + { 67, { 1, L_FIX} }, // "sprmCFVanish" chp.fVanish 0,1, 128, or 129 + { 68, { 2, L_FIX} }, // "sprmCFtc" chp.ftc ftc word + { 69, { 1, L_FIX} }, // "sprmCKul" chp.kul kul byte + { 70, { 3, L_FIX} }, // "sprmCSizePos" chp.hps, chp.hpsPos + { 71, { 2, L_FIX} }, // "sprmCDxaSpace" chp.dxaSpace dxa + { 72, { 2, L_FIX} }, // "sprmCLid" chp.lid LID + { 73, { 1, L_FIX} }, // "sprmCIco" chp.ico ico byte + { 74, { 1, L_FIX} }, // "sprmCHps" chp.hps hps !word! + { 75, { 1, L_FIX} }, // "sprmCHpsInc" chp.hps + { 76, { 1, L_FIX} }, // "sprmCHpsPos" chp.hpsPos hps !word! + { 77, { 1, L_FIX} }, // "sprmCHpsPosAdj" chp.hpsPos hps + { 78, { 0, L_VAR} }, // "?sprmCMajority" chp.fBold, chp.fItalic, ... + { 80, { 1, L_FIX} }, // "sprmCFBoldBi" chp.fBoldBi + { 81, { 1, L_FIX} }, // "sprmCFItalicBi" chp.fItalicBi + { 82, { 2, L_FIX} }, // "sprmCFtcBi" chp.ftcBi + { 83, { 2, L_FIX} }, // "sprmClidBi" chp.lidBi + { 84, { 1, L_FIX} }, // "sprmCIcoBi" chp.icoBi + { 85, { 1, L_FIX} }, // "sprmCHpsBi" chp.hpsBi + { 86, { 1, L_FIX} }, // "sprmCFBiDi" chp.fBiDi + { 87, { 1, L_FIX} }, // "sprmCFDiacColor" chp.fDiacUSico + { 94, { 1, L_FIX} }, // "sprmPicBrcl" pic.brcl brcl (see PIC definition) + { 95, {12, L_VAR} }, // "sprmPicScale" pic.mx, pic.my, pic.dxaCropleft, + { 96, { 2, L_FIX} }, // "sprmPicBrcTop" pic.brcTop BRC word + { 97, { 2, L_FIX} }, // "sprmPicBrcLeft" pic.brcLeft BRC word + { 98, { 2, L_FIX} }, // "sprmPicBrcBottom" pic.brcBottom BRC word + { 99, { 2, L_FIX} }, // "sprmPicBrcRight" pic.brcRight BRC word + {112, { 1, L_FIX} }, // "sprmSFRTLGutter", set to one if gutter is on + {114, { 1, L_FIX} }, // "sprmSFBiDi" ;;; + {115, { 2, L_FIX} }, // "sprmSDmBinFirst" sep.dmBinFirst word + {116, { 2, L_FIX} }, // "sprmSDmBinOther" sep.dmBinOther word + {117, { 1, L_FIX} }, // "sprmSBkc" sep.bkc bkc byte + {118, { 1, L_FIX} }, // "sprmSFTitlePage" sep.fTitlePage 0 or 1 byte + {119, { 2, L_FIX} }, // "sprmSCcolumns" sep.ccolM1 # of cols - 1 word + {120, { 2, L_FIX} }, // "sprmSDxaColumns" sep.dxaColumns dxa word + {121, { 1, L_FIX} }, // "sprmSFAutoPgn" sep.fAutoPgn obsolete byte + {122, { 1, L_FIX} }, // "sprmSNfcPgn" sep.nfcPgn nfc byte + {123, { 2, L_FIX} }, // "sprmSDyaPgn" sep.dyaPgn dya short + {124, { 2, L_FIX} }, // "sprmSDxaPgn" sep.dxaPgn dya short + {125, { 1, L_FIX} }, // "sprmSFPgnRestart" sep.fPgnRestart 0 or 1 byte + {126, { 1, L_FIX} }, // "sprmSFEndnote" sep.fEndnote 0 or 1 byte + {127, { 1, L_FIX} }, // "sprmSLnc" sep.lnc lnc byte + {128, { 1, L_FIX} }, // "sprmSGprfIhdt" sep.grpfIhdt grpfihdt + {129, { 2, L_FIX} }, // "sprmSNLnnMod" sep.nLnnMod non-neg int. word + {130, { 2, L_FIX} }, // "sprmSDxaLnn" sep.dxaLnn dxa word + {131, { 2, L_FIX} }, // "sprmSDyaHdrTop" sep.dyaHdrTop dya word + {132, { 2, L_FIX} }, // "sprmSDyaHdrBottom" sep.dyaHdrBottom dya word + {133, { 1, L_FIX} }, // "sprmSLBetween" sep.fLBetween 0 or 1 byte + {134, { 1, L_FIX} }, // "sprmSVjc" sep.vjc vjc byte + {135, { 2, L_FIX} }, // "sprmSLnnMin" sep.lnnMin lnn word + {136, { 2, L_FIX} }, // "sprmSPgnStart" sep.pgnStart pgn word + {137, { 1, L_FIX} }, // "sprmSBOrientation" sep.dmOrientPage dm byte + {138, { 1, L_FIX} }, // "sprmSFFacingCol" ;;; + {139, { 2, L_FIX} }, // "sprmSXaPage" sep.xaPage xa word + {140, { 2, L_FIX} }, // "sprmSYaPage" sep.yaPage ya word + {141, { 2, L_FIX} }, // "sprmSDxaLeft" sep.dxaLeft dxa word + {142, { 2, L_FIX} }, // "sprmSDxaRight" sep.dxaRight dxa word + {143, { 2, L_FIX} }, // "sprmSDyaTop" sep.dyaTop dya word + {144, { 2, L_FIX} }, // "sprmSDyaBottom" sep.dyaBottom dya word + {145, { 2, L_FIX} }, // "sprmSDzaGutter" sep.dzaGutter dza word + {146, { 2, L_FIX} }, // "sprmTJc" tap.jc jc (low order byte is significant) + {147, { 2, L_FIX} }, // "sprmTDxaLeft" tap.rgdxaCenter dxa word + {148, { 2, L_FIX} }, // "sprmTDxaGapHalf" tap.dxaGapHalf, tap.rgdxaCenter + {149, { 1, L_FIX} }, // "sprmTFBiDi" ;;; + {152, { 0, L_VAR} }, // "sprmTDefTable10" tap.rgdxaCenter, tap.rgtc complex + {153, { 2, L_FIX} }, // "sprmTDyaRowHeight" tap.dyaRowHeight dya word + {154, { 0, L_VAR2} },// "sprmTDefTable" tap.rgtc complex + {155, { 1, L_VAR} }, // "sprmTDefTableShd" tap.rgshd complex + {157, { 5, L_FIX} }, // "sprmTSetBrc" tap.rgtc[].rgbrc complex 5 bytes + {158, { 4, L_FIX} }, // "sprmTInsert" tap.rgdxaCenter,tap.rgtc complex + {159, { 2, L_FIX} }, // "sprmTDelete" tap.rgdxaCenter, tap.rgtc complex + {160, { 4, L_FIX} }, // "sprmTDxaCol" tap.rgdxaCenter complex + {161, { 2, L_FIX} }, // "sprmTMerge" tap.fFirstMerged, tap.fMerged complex + {162, { 2, L_FIX} }, // "sprmTSplit" tap.fFirstMerged, tap.fMerged complex + {163, { 5, L_FIX} }, // "sprmTSetBrc10" tap.rgtc[].rgbrc complex 5 bytes + {164, { 4, L_FIX} }, // "sprmTSetShd", tap.rgshd complex 4 bytes + }; + + static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +}; + +const wwSprmSearcher *wwSprmParser::GetWW6SprmSearcher(const WW8Fib& rFib) +{ + //double lock me + // WW7- Sprms + static const SprmInfoRow aSprms[] = + { + { 0, { 0, L_FIX} }, // "Default-sprm", is skipped + { 2, { 2, L_FIX} }, // "sprmPIstd", pap.istd (style code) + { 3, { 3, L_VAR} }, // "sprmPIstdPermute pap.istd permutation + { 4, { 1, L_FIX} }, // "sprmPIncLv1" pap.istddifference + { 5, { 1, L_FIX} }, // "sprmPJc" pap.jc (justification) + { 6, { 1, L_FIX} }, // "sprmPFSideBySide" pap.fSideBySide + { 7, { 1, L_FIX} }, // "sprmPFKeep" pap.fKeep + { 8, { 1, L_FIX} }, // "sprmPFKeepFollow " pap.fKeepFollow + { 9, { 1, L_FIX} }, // "sprmPPageBreakBefore" pap.fPageBreakBefore + { 10, { 1, L_FIX} }, // "sprmPBrcl" pap.brcl + { 11, { 1, L_FIX} }, // "sprmPBrcp" pap.brcp + { 12, { 0, L_VAR} }, // "sprmPAnld" pap.anld (ANLD structure) + { 13, { 1, L_FIX} }, // "sprmPNLvlAnm" pap.nLvlAnm nn + { 14, { 1, L_FIX} }, // "sprmPFNoLineNumb" pap.fNoLnn + { 15, { 0, L_VAR} }, // "?sprmPChgTabsPapx" pap.itbdMac, ... + { 16, { 2, L_FIX} }, // "sprmPDxaRight" pap.dxaRight + { 17, { 2, L_FIX} }, // "sprmPDxaLeft" pap.dxaLeft + { 18, { 2, L_FIX} }, // "sprmPNest" pap.dxaLeft + { 19, { 2, L_FIX} }, // "sprmPDxaLeft1" pap.dxaLeft1 + { 20, { 4, L_FIX} }, // "sprmPDyaLine" pap.lspd an LSPD + { 21, { 2, L_FIX} }, // "sprmPDyaBefore" pap.dyaBefore + { 22, { 2, L_FIX} }, // "sprmPDyaAfter" pap.dyaAfter + { 23, { 0, L_VAR} }, // "?sprmPChgTabs" pap.itbdMac, pap.rgdxaTab, ... + { 24, { 1, L_FIX} }, // "sprmPFInTable" pap.fInTable + { 25, { 1, L_FIX} }, // "sprmPTtp" pap.fTtp + { 26, { 2, L_FIX} }, // "sprmPDxaAbs" pap.dxaAbs + { 27, { 2, L_FIX} }, // "sprmPDyaAbs" pap.dyaAbs + { 28, { 2, L_FIX} }, // "sprmPDxaWidth" pap.dxaWidth + { 29, { 1, L_FIX} }, // "sprmPPc" pap.pcHorz, pap.pcVert + { 30, { 2, L_FIX} }, // "sprmPBrcTop10" pap.brcTop BRC10 + { 31, { 2, L_FIX} }, // "sprmPBrcLeft10" pap.brcLeft BRC10 + { 32, { 2, L_FIX} }, // "sprmPBrcBottom10" pap.brcBottom BRC10 + { 33, { 2, L_FIX} }, // "sprmPBrcRight10" pap.brcRight BRC10 + { 34, { 2, L_FIX} }, // "sprmPBrcBetween10" pap.brcBetween BRC10 + { 35, { 2, L_FIX} }, // "sprmPBrcBar10" pap.brcBar BRC10 + { 36, { 2, L_FIX} }, // "sprmPFromText10" pap.dxaFromText dxa + { 37, { 1, L_FIX} }, // "sprmPWr" pap.wr wr + { 38, { 2, L_FIX} }, // "sprmPBrcTop" pap.brcTop BRC + { 39, { 2, L_FIX} }, // "sprmPBrcLeft" pap.brcLeft BRC + { 40, { 2, L_FIX} }, // "sprmPBrcBottom" pap.brcBottom BRC + { 41, { 2, L_FIX} }, // "sprmPBrcRight" pap.brcRight BRC + { 42, { 2, L_FIX} }, // "sprmPBrcBetween" pap.brcBetween BRC + { 43, { 2, L_FIX} }, // "sprmPBrcBar" pap.brcBar BRC word + { 44, { 1, L_FIX} }, // "sprmPFNoAutoHyph" pap.fNoAutoHyph + { 45, { 2, L_FIX} }, // "sprmPWHeightAbs" pap.wHeightAbs w + { 46, { 2, L_FIX} }, // "sprmPDcs" pap.dcs DCS + { 47, { 2, L_FIX} }, // "sprmPShd" pap.shd SHD + { 48, { 2, L_FIX} }, // "sprmPDyaFromText" pap.dyaFromText dya + { 49, { 2, L_FIX} }, // "sprmPDxaFromText" pap.dxaFromText dxa + { 50, { 1, L_FIX} }, // "sprmPFLocked" pap.fLocked 0 or 1 byte + { 51, { 1, L_FIX} }, // "sprmPFWidowControl" pap.fWidowControl 0 or 1 byte + { 52, { 0, L_FIX} }, // "?sprmPRuler 52" + { 64, { 0, L_VAR} }, // rtl property ? + { 65, { 1, L_FIX} }, // "sprmCFStrikeRM" chp.fRMarkDel 1 or 0 bit + { 66, { 1, L_FIX} }, // "sprmCFRMark" chp.fRMark 1 or 0 bit + { 67, { 1, L_FIX} }, // "sprmCFFieldVanish" chp.fFieldVanish 1 or 0 bit + { 68, { 0, L_VAR} }, // "sprmCPicLocation" chp.fcPic and chp.fSpec + { 69, { 2, L_FIX} }, // "sprmCIbstRMark" chp.ibstRMark index into sttbRMark + { 70, { 4, L_FIX} }, // "sprmCDttmRMark" chp.dttm DTTM long + { 71, { 1, L_FIX} }, // "sprmCFData" chp.fData 1 or 0 bit + { 72, { 2, L_FIX} }, // "sprmCRMReason" chp.idslRMReason an index to a table + { 73, { 3, L_FIX} }, // "sprmCChse" chp.fChsDiff and chp.chse + { 74, { 0, L_VAR} }, // "sprmCSymbol" chp.fSpec, chp.chSym and chp.ftcSym + { 75, { 1, L_FIX} }, // "sprmCFOle2" chp.fOle2 1 or 0 bit + { 77, { 0, L_VAR} }, // unknown + { 79, { 0, L_VAR} }, // unknown + { 80, { 2, L_FIX} }, // "sprmCIstd" chp.istd istd, see stylesheet definition + { 81, { 0, L_VAR} }, // "sprmCIstdPermute" chp.istd permutation vector + { 82, { 0, L_VAR} }, // "sprmCDefault" whole CHP + { 83, { 0, L_FIX} }, // "sprmCPlain" whole CHP + { 85, { 1, L_FIX} }, // "sprmCFBold" chp.fBold 0,1, 128, or 129 + { 86, { 1, L_FIX} }, // "sprmCFItalic" chp.fItalic 0,1, 128, or 129 + { 87, { 1, L_FIX} }, // "sprmCFStrike" chp.fStrike 0,1, 128, or 129 + { 88, { 1, L_FIX} }, // "sprmCFOutline" chp.fOutline 0,1, 128, or 129 + { 89, { 1, L_FIX} }, // "sprmCFShadow" chp.fShadow 0,1, 128, or 129 + { 90, { 1, L_FIX} }, // "sprmCFSmallCaps" chp.fSmallCaps 0,1, 128, or 129 + { 91, { 1, L_FIX} }, // "sprmCFCaps" chp.fCaps 0,1, 128, or 129 + { 92, { 1, L_FIX} }, // "sprmCFVanish" chp.fVanish 0,1, 128, or 129 + { 93, { 2, L_FIX} }, // "sprmCFtc" chp.ftc ftc word + { 94, { 1, L_FIX} }, // "sprmCKul" chp.kul kul byte + { 95, { 3, L_FIX} }, // "sprmCSizePos" chp.hps, chp.hpsPos + { 96, { 2, L_FIX} }, // "sprmCDxaSpace" chp.dxaSpace dxa + { 97, { 2, L_FIX} }, // "sprmCLid" chp.lid LID + { 98, { 1, L_FIX} }, // "sprmCIco" chp.ico ico byte + { 99, { 2, L_FIX} }, // "sprmCHps" chp.hps hps !word! + {100, { 1, L_FIX} }, // "sprmCHpsInc" chp.hps + {101, { 2, L_FIX} }, // "sprmCHpsPos" chp.hpsPos hps !word! + {102, { 1, L_FIX} }, // "sprmCHpsPosAdj" chp.hpsPos hps + {103, { 0, L_VAR} }, // "?sprmCMajority" chp.fBold, chp.fItalic, ... + {104, { 1, L_FIX} }, // "sprmCIss" chp.iss iss + {105, { 0, L_VAR} }, // "sprmCHpsNew50" chp.hps hps variable width + {106, { 0, L_VAR} }, // "sprmCHpsInc1" chp.hps complex + {107, { 2, L_FIX} }, // "sprmCHpsKern" chp.hpsKern hps + {108, { 0, L_VAR} }, // "sprmCMajority50" chp.fBold, chp.fItalic, ... + {109, { 2, L_FIX} }, // "sprmCHpsMul" chp.hps percentage to grow hps + {110, { 2, L_FIX} }, // "sprmCCondHyhen" chp.ysri ysri + {111, { 0, L_VAR} }, // sprmCFBoldBi or font code + {112, { 0, L_VAR} }, // sprmCFItalicBi or font code + {113, { 0, L_VAR} }, // ww7 rtl font + {114, { 0, L_VAR} }, // ww7 lid + {115, { 0, L_VAR} }, // ww7 CJK font + {116, { 0, L_VAR} }, // ww7 fontsize + {117, { 1, L_FIX} }, // "sprmCFSpec" chp.fSpec 1 or 0 bit + {118, { 1, L_FIX} }, // "sprmCFObj" chp.fObj 1 or 0 bit + {119, { 1, L_FIX} }, // "sprmPicBrcl" pic.brcl brcl (see PIC definition) + {120, {12, L_VAR} }, // "sprmPicScale" pic.mx, pic.my, pic.dxaCropleft, + {121, { 2, L_FIX} }, // "sprmPicBrcTop" pic.brcTop BRC word + {122, { 2, L_FIX} }, // "sprmPicBrcLeft" pic.brcLeft BRC word + {123, { 2, L_FIX} }, // "sprmPicBrcBottom" pic.brcBottom BRC word + {124, { 2, L_FIX} }, // "sprmPicBrcRight" pic.brcRight BRC word + {131, { 1, L_FIX} }, // "sprmSScnsPgn" sep.cnsPgn cns byte + {132, { 1, L_FIX} }, // "sprmSiHeadingPgn" sep.iHeadingPgn + {133, { 0, L_VAR} }, // "sprmSOlstAnm" sep.olstAnm OLST variable length + {136, { 3, L_FIX} }, // "sprmSDxaColWidth" sep.rgdxaColWidthSpacing complex + {137, { 3, L_FIX} }, // "sprmSDxaColSpacing" sep.rgdxaColWidthSpacing + {138, { 1, L_FIX} }, // "sprmSFEvenlySpaced" sep.fEvenlySpaced 1 or 0 + {139, { 1, L_FIX} }, // "sprmSFProtected" sep.fUnlocked 1 or 0 byte + {140, { 2, L_FIX} }, // "sprmSDmBinFirst" sep.dmBinFirst word + {141, { 2, L_FIX} }, // "sprmSDmBinOther" sep.dmBinOther word + {142, { 1, L_FIX} }, // "sprmSBkc" sep.bkc bkc byte + {143, { 1, L_FIX} }, // "sprmSFTitlePage" sep.fTitlePage 0 or 1 byte + {144, { 2, L_FIX} }, // "sprmSCcolumns" sep.ccolM1 # of cols - 1 word + {145, { 2, L_FIX} }, // "sprmSDxaColumns" sep.dxaColumns dxa word + {146, { 1, L_FIX} }, // "sprmSFAutoPgn" sep.fAutoPgn obsolete byte + {147, { 1, L_FIX} }, // "sprmSNfcPgn" sep.nfcPgn nfc byte + {148, { 2, L_FIX} }, // "sprmSDyaPgn" sep.dyaPgn dya short + {149, { 2, L_FIX} }, // "sprmSDxaPgn" sep.dxaPgn dya short + {150, { 1, L_FIX} }, // "sprmSFPgnRestart" sep.fPgnRestart 0 or 1 byte + {151, { 1, L_FIX} }, // "sprmSFEndnote" sep.fEndnote 0 or 1 byte + {152, { 1, L_FIX} }, // "sprmSLnc" sep.lnc lnc byte + {153, { 1, L_FIX} }, // "sprmSGprfIhdt" sep.grpfIhdt grpfihdt + {154, { 2, L_FIX} }, // "sprmSNLnnMod" sep.nLnnMod non-neg int. word + {155, { 2, L_FIX} }, // "sprmSDxaLnn" sep.dxaLnn dxa word + {156, { 2, L_FIX} }, // "sprmSDyaHdrTop" sep.dyaHdrTop dya word + {157, { 2, L_FIX} }, // "sprmSDyaHdrBottom" sep.dyaHdrBottom dya word + {158, { 1, L_FIX} }, // "sprmSLBetween" sep.fLBetween 0 or 1 byte + {159, { 1, L_FIX} }, // "sprmSVjc" sep.vjc vjc byte + {160, { 2, L_FIX} }, // "sprmSLnnMin" sep.lnnMin lnn word + {161, { 2, L_FIX} }, // "sprmSPgnStart" sep.pgnStart pgn word + {162, { 1, L_FIX} }, // "sprmSBOrientation" sep.dmOrientPage dm byte + {163, { 0, L_FIX} }, // "?SprmSBCustomize 163" + {164, { 2, L_FIX} }, // "sprmSXaPage" sep.xaPage xa word + {165, { 2, L_FIX} }, // "sprmSYaPage" sep.yaPage ya word + {166, { 2, L_FIX} }, // "sprmSDxaLeft" sep.dxaLeft dxa word + {167, { 2, L_FIX} }, // "sprmSDxaRight" sep.dxaRight dxa word + {168, { 2, L_FIX} }, // "sprmSDyaTop" sep.dyaTop dya word + {169, { 2, L_FIX} }, // "sprmSDyaBottom" sep.dyaBottom dya word + {170, { 2, L_FIX} }, // "sprmSDzaGutter" sep.dzaGutter dza word + {171, { 2, L_FIX} }, // "sprmSDMPaperReq" sep.dmPaperReq dm word + {179, { 0, L_VAR} }, // rtl property ? + {181, { 0, L_VAR} }, // rtl property ? + {182, { 2, L_FIX} }, // "sprmTJc" tap.jc jc (low order byte is significant) + {183, { 2, L_FIX} }, // "sprmTDxaLeft" tap.rgdxaCenter dxa word + {184, { 2, L_FIX} }, // "sprmTDxaGapHalf" tap.dxaGapHalf, tap.rgdxaCenter + {185, { 1, L_FIX} }, // "sprmTFCantSplit" tap.fCantSplit 1 or 0 byte + {186, { 1, L_FIX} }, // "sprmTTableHeader" tap.fTableHeader 1 or 0 byte + {187, {12, L_FIX} }, // "sprmTTableBorders" tap.rgbrcTable complex 12 bytes + {188, { 0, L_VAR} }, // "sprmTDefTable10" tap.rgdxaCenter, tap.rgtc complex + {189, { 2, L_FIX} }, // "sprmTDyaRowHeight" tap.dyaRowHeight dya word + {190, { 0, L_VAR2} },// "sprmTDefTable" tap.rgtc complex + {191, { 1, L_VAR} }, // "sprmTDefTableShd" tap.rgshd complex + {192, { 4, L_FIX} }, // "sprmTTlp" tap.tlp TLP 4 bytes + {193, { 5, L_FIX} }, // "sprmTSetBrc" tap.rgtc[].rgbrc complex 5 bytes + {194, { 4, L_FIX} }, // "sprmTInsert" tap.rgdxaCenter,tap.rgtc complex + {195, { 2, L_FIX} }, // "sprmTDelete" tap.rgdxaCenter, tap.rgtc complex + {196, { 4, L_FIX} }, // "sprmTDxaCol" tap.rgdxaCenter complex + {197, { 2, L_FIX} }, // "sprmTMerge" tap.fFirstMerged, tap.fMerged complex + {198, { 2, L_FIX} }, // "sprmTSplit" tap.fFirstMerged, tap.fMerged complex + {199, { 5, L_FIX} }, // "sprmTSetBrc10" tap.rgtc[].rgbrc complex 5 bytes + {200, { 4, L_FIX} }, // "sprmTSetShd", tap.rgshd complex 4 bytes + {207, { 0, L_VAR} } // rtl property ? + }; + + if (rFib.m_wIdent >= 0xa697 && rFib.m_wIdent <= 0xa699) + { + //see Read_AmbiguousSPRM for this oddity + static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms), true); + return &aSprmSrch; + } + + static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +}; + +void wwSprmSearcher::patchCJKVariant() +{ + for (sal_uInt16 nId = 111; nId <= 113; ++nId) + { + SprmInfo& amb1 = map_[nId]; + amb1.nLen = 2; + amb1.nVari = wwSprmParser::L_FIX; + } +} + +const wwSprmSearcher *wwSprmParser::GetWW8SprmSearcher() +{ + //double lock me + //WW8+ Sprms + static const SprmInfoRow aSprms[] = + { + { 0, { 0, L_FIX} }, // "Default-sprm"/ is skipped + {0x4600, { 2, L_FIX} }, // "sprmPIstd" pap.istd;istd (style code);short; + {0xC601, { 0, L_VAR} }, // "sprmPIstdPermute" pap.istd;permutation vector + {0x2602, { 1, L_FIX} }, // "sprmPIncLvl" pap.istd, pap.lvl;difference + // between istd of base PAP and istd of PAP to be + // produced + {0x2403, { 1, L_FIX} }, // "sprmPJc" pap.jc;jc (justification);byte; + {NS_sprm::LN_PFSideBySide, { 1, L_FIX} }, // "sprmPFSideBySide" pap.fSideBySide;0 or 1;byte; + {0x2405, { 1, L_FIX} }, // "sprmPFKeep" pap.fKeep;0 or 1;byte; + {0x2406, { 1, L_FIX} }, // "sprmPFKeepFollow" pap.fKeepFollow;0 or 1;byte; + {0x2407, { 1, L_FIX} }, // "sprmPFPageBreakBefore" pap.fPageBreakBefore; + // 0 or 1 + {NS_sprm::LN_PBrcl, { 1, L_FIX} }, // "sprmPBrcl" pap.brcl;brcl;byte; + {NS_sprm::LN_PBrcp, { 1, L_FIX} }, // "sprmPBrcp" pap.brcp;brcp;byte; + {0x260A, { 1, L_FIX} }, // "sprmPIlvl" pap.ilvl;ilvl;byte; + {0x460B, { 2, L_FIX} }, // "sprmPIlfo" pap.ilfo;ilfo (list index) ;short; + {0x240C, { 1, L_FIX} }, // "sprmPFNoLineNumb" pap.fNoLnn;0 or 1;byte; + {0xC60D, { 0, L_VAR} }, // "sprmPChgTabsPapx" pap.itbdMac, pap.rgdxaTab, + // pap.rgtbd;complex + {0x840E, { 2, L_FIX} }, // "sprmPDxaRight" pap.dxaRight;dxa;word; + {0x840F, { 2, L_FIX} }, // "sprmPDxaLeft" pap.dxaLeft;dxa;word; + {0x4610, { 2, L_FIX} }, // "sprmPNest" pap.dxaLeft;dxa + {0x8411, { 2, L_FIX} }, // "sprmPDxaLeft1" pap.dxaLeft1;dxa;word; + {0x6412, { 4, L_FIX} }, // "sprmPDyaLine" pap.lspd;an LSPD, a long word + // structure consisting of a short of dyaLine + // followed by a short of fMultLinespace + {0xA413, { 2, L_FIX} }, // "sprmPDyaBefore" pap.dyaBefore;dya;word; + {0xA414, { 2, L_FIX} }, // "sprmPDyaAfter" pap.dyaAfter;dya;word; + {0xC615, { 0, L_VAR} }, // "sprmPChgTabs" pap.itbdMac, pap.rgdxaTab, + // pap.rgtbd;complex + {0x2416, { 1, L_FIX} }, // "sprmPFInTable" pap.fInTable;0 or 1;byte; + {0x2417, { 1, L_FIX} }, // "sprmPFTtp" pap.fTtp;0 or 1;byte; + {0x8418, { 2, L_FIX} }, // "sprmPDxaAbs" pap.dxaAbs;dxa;word; + {0x8419, { 2, L_FIX} }, // "sprmPDyaAbs" pap.dyaAbs;dya;word; + {0x841A, { 2, L_FIX} }, // "sprmPDxaWidth" pap.dxaWidth;dxa;word; + {0x261B, { 1, L_FIX} }, // "sprmPPc" pap.pcHorz, pap.pcVert;complex + {NS_sprm::LN_PBrcTop10, { 2, L_FIX} }, // "sprmPBrcTop10" pap.brcTop;BRC10;word; + {NS_sprm::LN_PBrcLeft10, { 2, L_FIX} }, // "sprmPBrcLeft10" pap.brcLeft;BRC10;word; + {NS_sprm::LN_PBrcBottom10, { 2, L_FIX} }, // "sprmPBrcBottom10" pap.brcBottom;BRC10;word; + {NS_sprm::LN_PBrcRight10, { 2, L_FIX} }, // "sprmPBrcRight10" pap.brcRight;BRC10;word; + {NS_sprm::LN_PBrcBetween10, { 2, L_FIX} }, // "sprmPBrcBetween10" pap.brcBetween;BRC10;word; + {NS_sprm::LN_PBrcBar10, { 2, L_FIX} }, // "sprmPBrcBar10" pap.brcBar;BRC10;word; + {0x4622, { 2, L_FIX} }, // "sprmPDxaFromText10" pap.dxaFromText;dxa;word; + {0x2423, { 1, L_FIX} }, // "sprmPWr" pap.wr;wr + {0x6424, { 4, L_FIX} }, // "sprmPBrcTop80" pap.brcTop;BRC;long; + {0x6425, { 4, L_FIX} }, // "sprmPBrcLeft80" pap.brcLeft;BRC;long; + {0x6426, { 4, L_FIX} }, // "sprmPBrcBottom80" pap.brcBottom;BRC;long; + {0x6427, { 4, L_FIX} }, // "sprmPBrcRight80" pap.brcRight;BRC;long; + {0x6428, { 4, L_FIX} }, // "sprmPBrcBetween80" pap.brcBetween;BRC;long; + {0x6629, { 4, L_FIX} }, // "sprmPBrcBar80" pap.brcBar;BRC;long; + {0x242A, { 1, L_FIX} }, // "sprmPFNoAutoHyph" pap.fNoAutoHyph;0 or 1;byte; + {0x442B, { 2, L_FIX} }, // "sprmPWHeightAbs" pap.wHeightAbs;w;word; + {0x442C, { 2, L_FIX} }, // "sprmPDcs" pap.dcs;DCS;short; + {0x442D, { 2, L_FIX} }, // "sprmPShd" pap.shd;SHD;word; + {0x842E, { 2, L_FIX} }, // "sprmPDyaFromText" pap.dyaFromText;dya;word; + {0x842F, { 2, L_FIX} }, // "sprmPDxaFromText" pap.dxaFromText;dxa;word; + {0x2430, { 1, L_FIX} }, // "sprmPFLocked" pap.fLocked;0 or 1;byte; + {0x2431, { 1, L_FIX} }, // "sprmPFWidowControl" pap.fWidowControl;0 or 1 + {NS_sprm::LN_PRuler, { 0, L_VAR} }, // "sprmPRuler" ;;variable length; + {0x2433, { 1, L_FIX} }, // "sprmPFKinsoku" pap.fKinsoku;0 or 1;byte; + {0x2434, { 1, L_FIX} }, // "sprmPFWordWrap" pap.fWordWrap;0 or 1;byte; + {0x2435, { 1, L_FIX} }, // "sprmPFOverflowPunct" pap.fOverflowPunct;0 or 1 + {0x2436, { 1, L_FIX} }, // "sprmPFTopLinePunct" pap.fTopLinePunct;0 or 1 + {0x2437, { 1, L_FIX} }, // "sprmPFAutoSpaceDE" pap.fAutoSpaceDE;0 or 1 + {0x2438, { 1, L_FIX} }, // "sprmPFAutoSpaceDN" pap.fAutoSpaceDN;0 or 1 + {NS_sprm::sprmPWAlignFont, { 2, L_FIX} }, // "sprmPWAlignFont" pap.wAlignFont;iFa + {0x443A, { 2, L_FIX} }, // "sprmPFrameTextFlow" pap.fVertical pap.fBackward + // pap.fRotateFont;complex + {NS_sprm::LN_PISnapBaseLine, { 1, L_FIX} }, // "sprmPISnapBaseLine" obsolete: not applicable in + // Word97 and later versions; + {NS_sprm::LN_PAnld, { 0, L_VAR} }, // "sprmPAnld" pap.anld;;variable length; + {NS_sprm::LN_PPropRMark, { 0, L_VAR} }, // "sprmPPropRMark" pap.fPropRMark;complex + {0x2640, { 1, L_FIX} }, // "sprmPOutLvl" pap.lvl;has no effect if pap.istd + // is < 1 or is > 9 + {0x2441, { 1, L_FIX} }, // "sprmPFBiDi" ;;byte; + {0x2443, { 1, L_FIX} }, // "sprmPFNumRMIns" pap.fNumRMIns;1 or 0;bit; + {NS_sprm::LN_PCrLf, { 1, L_FIX} }, // "sprmPCrLf" ;;byte; + {0xC645, { 0, L_VAR} }, // "sprmPNumRM" pap.numrm;;variable length; + {NS_sprm::LN_PHugePapx, { 4, L_FIX} }, // "sprmPHugePapx" fc in the data stream to locate + // the huge grpprl + {0x6646, { 4, L_FIX} }, // "sprmPHugePapx" fc in the data stream to locate + // the huge grpprl + {0x2447, { 1, L_FIX} }, // "sprmPFUsePgsuSettings" pap.fUsePgsuSettings; + // 1 or 0 + {0x2448, { 1, L_FIX} }, // "sprmPFAdjustRight" pap.fAdjustRight;1 or 0;byte; + {0x0800, { 1, L_FIX} }, // "sprmCFRMarkDel" chp.fRMarkDel;1 or 0;bit; + {0x0801, { 1, L_FIX} }, // "sprmCFRMark" chp.fRMark;1 or 0;bit; + {0x0802, { 1, L_FIX} }, // "sprmCFFieldVanish" chp.fFieldVanish;1 or 0;bit; + {0x6A03, { 4, L_FIX} }, // "sprmCPicLocation" chp.fcPic and chp.fSpec; + {0x4804, { 2, L_FIX} }, // "sprmCIbstRMark" chp.ibstRMark;index into + // sttbRMark + {0x6805, { 4, L_FIX} }, // "sprmCDttmRMark" chp.dttmRMark;DTTM;long; + {0x0806, { 1, L_FIX} }, // "sprmCFData" chp.fData;1 or 0;bit; + {0x4807, { 2, L_FIX} }, // "sprmCIdslRMark" chp.idslRMReason;an index to a + // table of strings defined in Word 6.0 + // executables;short; + {NS_sprm::LN_CChs, { 1, L_FIX} }, // "sprmCChs" chp.fChsDiff and chp.chse; + {0x6A09, { 4, L_FIX} }, // "sprmCSymbol" chp.fSpec, chp.xchSym and + // chp.ftcSym + {0x080A, { 1, L_FIX} }, // "sprmCFOle2" chp.fOle2;1 or 0;bit; + {NS_sprm::LN_CIdCharType, { 0, L_FIX} }, // "sprmCIdCharType" obsolete: not applicable in + // Word97 and later versions; + {0x2A0C, { 1, L_FIX} }, // "sprmCHighlight" chp.fHighlight, + // chp.icoHighlight;ico (fHighlight is set to 1 iff + // ico is not 0) + {NS_sprm::LN_CObjLocation, { 4, L_FIX} }, // "sprmCObjLocation" chp.fcObj;FC;long; + {NS_sprm::LN_CFFtcAsciSymb, { 0, L_FIX} }, // "sprmCFFtcAsciSymb" ;;; + {0x4A30, { 2, L_FIX} }, // "sprmCIstd" chp.istd;istd, see stylesheet def + {0xCA31, { 0, L_VAR} }, // "sprmCIstdPermute" chp.istd;permutation vector + {NS_sprm::LN_CDefault, { 0, L_VAR} }, // "sprmCDefault" whole CHP;none;variable length; + {0x2A33, { 0, L_FIX} }, // "sprmCPlain" whole CHP;none;0; + {0x2A34, { 1, L_FIX} }, // "sprmCKcd" ;;; + {0x0835, { 1, L_FIX} }, // "sprmCFBold" chp.fBold;0,1, 128, or 129 + {0x0836, { 1, L_FIX} }, // "sprmCFItalic" chp.fItalic;0,1, 128, or 129 + {0x0837, { 1, L_FIX} }, // "sprmCFStrike" chp.fStrike;0,1, 128, or 129 + {0x0838, { 1, L_FIX} }, // "sprmCFOutline" chp.fOutline;0,1, 128, or 129 + {0x0839, { 1, L_FIX} }, // "sprmCFShadow" chp.fShadow;0,1, 128, or 129 + {0x083A, { 1, L_FIX} }, // "sprmCFSmallCaps" chp.fSmallCaps;0,1, 128, or 129 + {0x083B, { 1, L_FIX} }, // "sprmCFCaps" chp.fCaps;0,1, 128, or 129 + {0x083C, { 1, L_FIX} }, // "sprmCFVanish" chp.fVanish;0,1, 128, or 129 + {NS_sprm::LN_CFtcDefault, { 2, L_FIX} }, // "sprmCFtcDefault" ;ftc, only used internally + {0x2A3E, { 1, L_FIX} }, // "sprmCKul" chp.kul;kul;byte; + {NS_sprm::LN_CSizePos, { 3, L_FIX} }, // "sprmCSizePos" chp.hps, chp.hpsPos;3 bytes; + {0x8840, { 2, L_FIX} }, // "sprmCDxaSpace" chp.dxaSpace;dxa;word; + {NS_sprm::LN_CLid, { 2, L_FIX} }, // "sprmCLid" ;only used internally never stored + {0x2A42, { 1, L_FIX} }, // "sprmCIco" chp.ico;ico;byte; + {0x4A43, { 2, L_FIX} }, // "sprmCHps" chp.hps;hps + {NS_sprm::LN_CHpsInc, { 1, L_FIX} }, // "sprmCHpsInc" chp.hps; + {0x4845, { 2, L_FIX} }, // "sprmCHpsPos" chp.hpsPos;hps;short; (doc wrong) + {NS_sprm::LN_CHpsPosAdj, { 1, L_FIX} }, // "sprmCHpsPosAdj" chp.hpsPos;hps + {0xCA47, { 0, L_VAR} }, // "sprmCMajority" chp.fBold, chp.fItalic, + // chp.fSmallCaps, chp.fVanish, chp.fStrike, + // chp.fCaps, chp.rgftc, chp.hps, chp.hpsPos, + // chp.kul, chp.dxaSpace, chp.ico, + // chp.rglid;complex;variable length, length byte + // plus size of following grpprl; + {0x2A48, { 1, L_FIX} }, // "sprmCIss" chp.iss;iss;byte; + {NS_sprm::LN_CHpsNew50, { 0, L_VAR} }, // "sprmCHpsNew50" chp.hps;hps;variable width + {NS_sprm::LN_CHpsInc1, { 0, L_VAR} }, // "sprmCHpsInc1" chp.hps;complex + {0x484B, { 2, L_FIX} }, // "sprmCHpsKern" chp.hpsKern;hps;short; + {NS_sprm::LN_CMajority50, { 2, L_FIX} }, // "sprmCMajority50" chp.fBold, chp.fItalic, + // chp.fSmallCaps, chp.fVanish, chp.fStrike, + // chp.fCaps, chp.ftc, chp.hps, chp.hpsPos, chp.kul, + // chp.dxaSpace, chp.ico,;complex + {NS_sprm::LN_CHpsMul, { 2, L_FIX} }, // "sprmCHpsMul" chp.hps;percentage to grow hps + {0x484E, { 2, L_FIX} }, // "sprmCYsri" chp.ysri;ysri;short; + {0x4A4F, { 2, L_FIX} }, // "sprmCRgFtc0" chp.rgftc[0];ftc for ASCII text + {0x4A50, { 2, L_FIX} }, // "sprmCRgFtc1" chp.rgftc[1];ftc for Far East text + {0x4A51, { 2, L_FIX} }, // "sprmCRgFtc2" chp.rgftc[2];ftc for non-FE text + {0x4852, { 2, L_FIX} }, // "sprmCCharScale" + {0x2A53, { 1, L_FIX} }, // "sprmCFDStrike" chp.fDStrike;;byte; + {0x0854, { 1, L_FIX} }, // "sprmCFImprint" chp.fImprint;1 or 0;bit; + {0x0855, { 1, L_FIX} }, // "sprmCFSpec" chp.fSpec ;1 or 0;bit; + {0x0856, { 1, L_FIX} }, // "sprmCFObj" chp.fObj;1 or 0;bit; + {0xCA57, { 0, L_VAR} }, // "sprmCPropRMark" chp.fPropRMark, + // chp.ibstPropRMark, chp.dttmPropRMark;Complex + {0x0858, { 1, L_FIX} }, // "sprmCFEmboss" chp.fEmboss;1 or 0;bit; + {0x2859, { 1, L_FIX} }, // "sprmCSfxText" chp.sfxtText;text animation;byte; + {0x085A, { 1, L_FIX} }, // "sprmCFBiDi" ;;; + {NS_sprm::LN_CFDiacColor, { 1, L_FIX} }, // "sprmCFDiacColor" ;;; + {0x085C, { 1, L_FIX} }, // "sprmCFBoldBi" ;;; + {0x085D, { 1, L_FIX} }, // "sprmCFItalicBi" ;;; + {0x4A5E, { 2, L_FIX} }, + {0x485F, { 2, L_FIX} }, // "sprmCLidBi" ;;; + {0x4A60, { 2, L_FIX} }, // "sprmCIcoBi" ;;; + {0x4A61, { 2, L_FIX} }, // "sprmCHpsBi" ;;; + {0xCA62, { 0, L_VAR} }, // "sprmCDispFieldRMark" chp.fDispFieldRMark, + // chp.ibstDispFieldRMark, chp.dttmDispFieldRMark ; + {0x4863, { 2, L_FIX} }, // "sprmCIbstRMarkDel" chp.ibstRMarkDel;index into + // sttbRMark;short; + {NS_sprm::sprmCDttmRMarkDel, { 4, L_FIX} }, // chp.dttmRMarkDel;DTTM;long; + {0x6865, { 4, L_FIX} }, // "sprmCBrc80" chp.brc;BRC;long; + {0x4866, { 2, L_FIX} }, // "sprmCShd80" chp.shd;SHD;short; + {0x4867, { 2, L_FIX} }, // "sprmCIdslRMarkDel" chp.idslRMReasonDel;an index + // to a table of strings defined in Word 6.0 + // executables;short; + {0x0868, { 1, L_FIX} }, // "sprmCFUsePgsuSettings" + // chp.fUsePgsuSettings;1 or 0 + {NS_sprm::LN_CCpg, { 2, L_FIX} }, // "sprmCCpg" ;;word; + {0x486D, { 2, L_FIX} }, // "sprmCRgLid0_80" chp.rglid[0];LID: for non-FE text + {0x486E, { 2, L_FIX} }, // "sprmCRgLid1_80" chp.rglid[1];LID: for Far East text + {0x286F, { 1, L_FIX} }, // "sprmCIdctHint" chp.idctHint;IDCT: + {NS_sprm::LN_PicBrcl, { 1, L_FIX} }, // "sprmPicBrcl" pic.brcl;brcl (see PIC definition) + {NS_sprm::LN_PicScale, { 0, L_VAR} }, // "sprmPicScale" pic.mx, pic.my, pic.dxaCropleft, + // pic.dyaCropTop pic.dxaCropRight, + // pic.dyaCropBottom;Complex + {0x6C02, { 4, L_FIX} }, // "sprmPicBrcTop80" pic.brcTop;BRC;long; + {0x6C03, { 4, L_FIX} }, // "sprmPicBrcLeft80" pic.brcLeft;BRC;long; + {0x6C04, { 4, L_FIX} }, // "sprmPicBrcBottom80" pic.brcBottom;BRC;long; + {0x6C05, { 4, L_FIX} }, // "sprmPicBrcRight80" pic.brcRight;BRC;long; + {0x3000, { 1, L_FIX} }, // "sprmScnsPgn" sep.cnsPgn;cns;byte; + {0x3001, { 1, L_FIX} }, // "sprmSiHeadingPgn" sep.iHeadingPgn;heading number + // level;byte; + {NS_sprm::LN_SOlstAnm, { 0, L_VAR} }, // "sprmSOlstAnm" sep.olstAnm;OLST;variable length; + {0xF203, { 3, L_FIX} }, // "sprmSDxaColWidth" sep.rgdxaColWidthSpacing; + {0xF204, { 3, L_FIX} }, // "sprmSDxaColSpacing" sep.rgdxaColWidthSpacing; + // complex + {0x3005, { 1, L_FIX} }, // "sprmSFEvenlySpaced" sep.fEvenlySpaced;1 or 0 + {0x3006, { 1, L_FIX} }, // "sprmSFProtected" sep.fUnlocked;1 or 0;byte; + {0x5007, { 2, L_FIX} }, // "sprmSDmBinFirst" sep.dmBinFirst;;word; + {0x5008, { 2, L_FIX} }, // "sprmSDmBinOther" sep.dmBinOther;;word; + {0x3009, { 1, L_FIX} }, // "sprmSBkc" sep.bkc;bkc;byte; + {0x300A, { 1, L_FIX} }, // "sprmSFTitlePage" sep.fTitlePage;0 or 1;byte; + {0x500B, { 2, L_FIX} }, // "sprmSCcolumns" sep.ccolM1;# of cols - 1;word; + {0x900C, { 2, L_FIX} }, // "sprmSDxaColumns" sep.dxaColumns;dxa;word; + {NS_sprm::LN_SFAutoPgn, { 1, L_FIX} }, // "sprmSFAutoPgn" sep.fAutoPgn;obsolete;byte; + {0x300E, { 1, L_FIX} }, // "sprmSNfcPgn" sep.nfcPgn;nfc;byte; + {NS_sprm::LN_SDyaPgn, { 2, L_FIX} }, // "sprmSDyaPgn" sep.dyaPgn;dya;short; + {NS_sprm::LN_SDxaPgn, { 2, L_FIX} }, // "sprmSDxaPgn" sep.dxaPgn;dya;short; + {0x3011, { 1, L_FIX} }, // "sprmSFPgnRestart" sep.fPgnRestart;0 or 1;byte; + {0x3012, { 1, L_FIX} }, // "sprmSFEndnote" sep.fEndnote;0 or 1;byte; + {0x3013, { 1, L_FIX} }, // "sprmSLnc" sep.lnc;lnc;byte; + {NS_sprm::LN_SGprfIhdt, { 1, L_FIX} }, // "sprmSGprfIhdt" sep.grpfIhdt;grpfihdt + {0x5015, { 2, L_FIX} }, // "sprmSNLnnMod" sep.nLnnMod;non-neg int.;word; + {0x9016, { 2, L_FIX} }, // "sprmSDxaLnn" sep.dxaLnn;dxa;word; + {0xB017, { 2, L_FIX} }, // "sprmSDyaHdrTop" sep.dyaHdrTop;dya;word; + {0xB018, { 2, L_FIX} }, // "sprmSDyaHdrBottom" sep.dyaHdrBottom;dya;word; + {0x3019, { 1, L_FIX} }, // "sprmSLBetween" sep.fLBetween;0 or 1;byte; + {0x301A, { 1, L_FIX} }, // "sprmSVjc" sep.vjc;vjc;byte; + {0x501B, { 2, L_FIX} }, // "sprmSLnnMin" sep.lnnMin;lnn;word; + {0x501C, { 2, L_FIX} }, // "sprmSPgnStart" sep.pgnStart;pgn;word; + {0x301D, { 1, L_FIX} }, // "sprmSBOrientation" sep.dmOrientPage;dm;byte; + {NS_sprm::LN_SBCustomize, { 1, L_FIX} }, // "sprmSBCustomize" ;;; + {0xB01F, { 2, L_FIX} }, // "sprmSXaPage" sep.xaPage;xa;word; + {0xB020, { 2, L_FIX} }, // "sprmSYaPage" sep.yaPage;ya;word; + {0xB021, { 2, L_FIX} }, // "sprmSDxaLeft" sep.dxaLeft;dxa;word; + {0xB022, { 2, L_FIX} }, // "sprmSDxaRight" sep.dxaRight;dxa;word; + {0x9023, { 2, L_FIX} }, // "sprmSDyaTop" sep.dyaTop;dya;word; + {0x9024, { 2, L_FIX} }, // "sprmSDyaBottom" sep.dyaBottom;dya;word; + {0xB025, { 2, L_FIX} }, // "sprmSDzaGutter" sep.dzaGutter;dza;word; + {0x5026, { 2, L_FIX} }, // "sprmSDmPaperReq" sep.dmPaperReq;dm;word; + {NS_sprm::LN_SPropRMark, { 0, L_VAR} }, // "sprmSPropRMark" sep.fPropRMark, + // sep.ibstPropRMark, sep.dttmPropRMark ;complex + {0x3228, { 1, L_FIX} }, // "sprmSFBiDi" ;;; + {NS_sprm::LN_SFFacingCol, { 1, L_FIX} }, // "sprmSFFacingCol" ;;; + {0x322A, { 1, L_FIX} }, // "sprmSFRTLGutter", set to one if gutter is on + // right + {0x702B, { 4, L_FIX} }, // "sprmSBrcTop80" sep.brcTop;BRC;long; + {0x702C, { 4, L_FIX} }, // "sprmSBrcLeft80" sep.brcLeft;BRC;long; + {0x702D, { 4, L_FIX} }, // "sprmSBrcBottom80" sep.brcBottom;BRC;long; + {0x702E, { 4, L_FIX} }, // "sprmSBrcRight80" sep.brcRight;BRC;long; + {0x522F, { 2, L_FIX} }, // "sprmSPgbProp" sep.pgbProp;;word; + {0x7030, { 4, L_FIX} }, // "sprmSDxtCharSpace" sep.dxtCharSpace;dxt;long; + {0x9031, { 2, L_FIX} }, // "sprmSDyaLinePitch" + // sep.dyaLinePitch;dya; WRONG:long; RIGHT:short; ! + {0x5032, { 2, L_FIX} }, // "sprmSClm" ;;; + {0x5033, { 2, L_FIX} }, // "sprmSTextFlow" sep.wTextFlow;complex + {0x5400, { 2, L_FIX} }, // "sprmTJc90" tap.jc;jc;word (low order byte is + // significant); + {0x9601, { 2, L_FIX} }, // "sprmTDxaLeft" tap.rgdxaCenter + {0x9602, { 2, L_FIX} }, // "sprmTDxaGapHalf" tap.dxaGapHalf, + // tap.rgdxaCenter + {0x3403, { 1, L_FIX} }, // "sprmTFCantSplit90" tap.fCantSplit90;1 or 0;byte; + {0x3404, { 1, L_FIX} }, // "sprmTTableHeader" tap.fTableHeader;1 or 0;byte; + {0x3466, { 1, L_FIX} }, // "sprmTFCantSplit" tap.fCantSplit;1 or 0;byte; + {0xD605, { 0, L_VAR} }, // "sprmTTableBorders80" tap.rgbrcTable;complex + {NS_sprm::LN_TDefTable10, { 0, L_VAR} }, // "sprmTDefTable10" tap.rgdxaCenter, + // tap.rgtc;complex + {0x9407, { 2, L_FIX} }, // "sprmTDyaRowHeight" tap.dyaRowHeight;dya;word; + {0xD608, { 0, L_VAR} }, // "sprmTDefTable" tap.rgtc;complex + {0xD609, { 0, L_VAR} }, // "sprmTDefTableShd80" tap.rgshd;complex + {0x740A, { 4, L_FIX} }, // "sprmTTlp" tap.tlp;TLP;4 bytes; + {0x560B, { 2, L_FIX} }, // "sprmTFBiDi" ;;; + {NS_sprm::LN_THTMLProps, { 1, L_FIX} }, // "sprmTHTMLProps" ;;; + {0xD620, { 0, L_VAR} }, // "sprmTSetBrc80" tap.rgtc[].rgbrc;complex + {0x7621, { 4, L_FIX} }, // "sprmTInsert" tap.rgdxaCenter, tap.rgtc;complex + {0x5622, { 2, L_FIX} }, // "sprmTDelete" tap.rgdxaCenter, tap.rgtc;complex + {0x7623, { 4, L_FIX} }, // "sprmTDxaCol" tap.rgdxaCenter;complex + {0x5624, { 0, L_VAR} }, // "sprmTMerge" tap.fFirstMerged, tap.fMerged; + {0x5625, { 0, L_VAR} }, // "sprmTSplit" tap.fFirstMerged, tap.fMerged; + {NS_sprm::LN_TSetBrc10, { 0, L_VAR} }, // "sprmTSetBrc10" tap.rgtc[].rgbrc;complex + {NS_sprm::LN_TSetShd80, { 0, L_VAR} }, // "sprmTSetShd80" tap.rgshd;complex + {NS_sprm::LN_TSetShdOdd80, { 0, L_VAR} }, // "sprmTSetShdOdd80" tap.rgshd;complex + {0x7629, { 4, L_FIX} }, // "sprmTTextFlow" tap.rgtc[].fVerticaltap, + // rgtc[].fBackwardtap, rgtc[].fRotateFont;0 or 10 + // or 10 or 1;word; + {NS_sprm::LN_TDiagLine, { 1, L_FIX} }, // "sprmTDiagLine" ;;; + {0xD62B, { 0, L_VAR} }, // "sprmTVertMerge" tap.rgtc[].vertMerge + {0xD62C, { 0, L_VAR} }, // "sprmTVertAlign" tap.rgtc[].vertAlign + {NS_sprm::sprmCFELayout, { 0, L_VAR} }, + {0x6649, { 4, L_FIX} }, // undocumented + {0xF614, { 3, L_FIX} }, // undocumented + {0xD612, { 0, L_VAR} }, // "sprmTDefTableShd" + {0xD613, { 0, L_VAR} }, // "sprmTTableBorders" + {0xD61A, { 0, L_VAR} }, // undocumented + {0xD61B, { 0, L_VAR} }, // undocumented + {0xD61C, { 0, L_VAR} }, // undocumented + {0xD61D, { 0, L_VAR} }, // undocumented + {0xD632, { 0, L_VAR} }, // undocumented + {0xD634, { 0, L_VAR} }, // undocumented + {0xD238, { 0, L_VAR} }, // undocumented sep + {0xC64E, { 0, L_VAR} }, // "sprmPBrcTop" + {0xC64F, { 0, L_VAR} }, // "sprmPBrcLeft" + {0xC650, { 0, L_VAR} }, // "sprmPBrcBottom" + {0xC651, { 0, L_VAR} }, // "sprmPBrcRight" + {0xC652, { 0, L_VAR} }, // "sprmPBrcBetween" + {0xF661, { 3, L_FIX} }, // undocumented + {0x4873, { 2, L_FIX} }, // "sprmCRgLid0" chp.rglid[0];LID: for non-FE text + {0x4874, { 2, L_FIX} }, // "sprmCRgLid1" chp.rglid[1];LID: for Far East text + {0x6463, { 4, L_FIX} }, // undocumented + {0x2461, { 1, L_FIX} }, // undoc, must be asian version of "sprmPJc" + {0x845D, { 2, L_FIX} }, // undoc, must be asian version of "sprmPDxaRight" + {0x845E, { 2, L_FIX} }, // undoc, must be asian version of "sprmPDxaLeft" + {0x8460, { 2, L_FIX} }, // undoc, must be asian version of "sprmPDxaLeft1" + {0x3615, { 1, L_FIX} }, // undocumented + {0x360D, { 1, L_FIX} }, // undocumented + {0x703A, { 4, L_FIX} }, // undocumented, sep, perhaps related to textgrids ? + {0x303B, { 1, L_FIX} }, // undocumented, sep + {0x244B, { 1, L_FIX} }, // undocumented, subtable "sprmPFInTable" equiv ? + {0x244C, { 1, L_FIX} }, // undocumented, subtable "sprmPFTtp" equiv ? + {0x940E, { 2, L_FIX} }, // undocumented + {0x940F, { 2, L_FIX} }, // undocumented + {0x9410, { 2, L_FIX} }, // undocumented + {0x6815, { 4, L_FIX} }, // undocumented + {0x6816, { 4, L_FIX} }, // undocumented + {NS_sprm::sprmCCv, { 4, L_FIX} }, // text colour + {0xC64D, { 0, L_VAR} }, // undocumented, para back colour + {0x6467, { 4, L_FIX} }, // undocumented + {0x646B, { 4, L_FIX} }, // undocumented + {0xF617, { 3, L_FIX} }, // undocumented + {0xD660, { 0, L_VAR} }, // undocumented, something to do with colour. + {0xD670, { 0, L_VAR} }, // undocumented, something to do with colour. + {0xCA71, { 0, L_VAR} }, // "sprmCShd", text backcolour + {0x303C, { 1, L_FIX} }, // undocumented, sep + {0x245B, { 1, L_FIX} }, // undocumented, para autobefore + {0x245C, { 1, L_FIX} }, // undocumented, para autoafter + // "sprmPFContextualSpacing", don't add space between para of the same style + {0x246D, { 1, L_FIX} } + }; + + static wwSprmSearcher aSprmSrch(aSprms, SAL_N_ELEMENTS(aSprms)); + return &aSprmSrch; +}; + +wwSprmParser::wwSprmParser(const WW8Fib& rFib) : meVersion(rFib.GetFIBVersion()) +{ + OSL_ENSURE((meVersion >= ww::eWW1 && meVersion <= ww::eWW8), + "Impossible value for version"); + + mnDelta = (ww::IsSevenMinus(meVersion)) ? 0 : 1; + + if (meVersion <= ww::eWW2) + mpKnownSprms = GetWW2SprmSearcher(); + else if (meVersion < ww::eWW8) + mpKnownSprms = GetWW6SprmSearcher(rFib); + else + mpKnownSprms = GetWW8SprmSearcher(); +} + +SprmInfo wwSprmParser::GetSprmInfo(sal_uInt16 nId) const +{ + const SprmInfo* pFound = mpKnownSprms->search(nId); + if (pFound != nullptr) + { + return *pFound; + } + + OSL_ENSURE(ww::IsEightPlus(meVersion), + "Unknown ww7- sprm, dangerous, report to development"); + + //All the unknown ww7 sprms appear to be variable (which makes sense) + SprmInfo aSrch = { 0, L_VAR }; + if (ww::IsEightPlus(meVersion)) //We can recover perfectly in this case + { + aSrch.nVari = L_FIX; + switch (nId >> 13) + { + case 0: + case 1: + aSrch.nLen = 1; + break; + case 2: + aSrch.nLen = 2; + break; + case 3: + aSrch.nLen = 4; + break; + case 4: + case 5: + aSrch.nLen = 2; + break; + case 6: + aSrch.nLen = 0; + aSrch.nVari = L_VAR; + break; + case 7: + default: + aSrch.nLen = 3; + break; + } + } + return aSrch; +} + +//-end + +static sal_uInt8 Get_Byte( sal_uInt8 *& p ) +{ + sal_uInt8 n = *p; + p += 1; + return n; +} + +static sal_uInt16 Get_UShort( sal_uInt8 *& p ) +{ + const sal_uInt16 n = SVBT16ToUInt16( *reinterpret_cast<SVBT16*>(p) ); + p += 2; + return n; +} + +static sal_Int16 Get_Short( sal_uInt8 *& p ) +{ + return Get_UShort(p); +} + +static sal_uInt32 Get_ULong( sal_uInt8 *& p ) +{ + sal_uInt32 n = SVBT32ToUInt32( *reinterpret_cast<SVBT32*>(p) ); + p += 4; + return n; +} + +static sal_Int32 Get_Long( sal_uInt8 *& p ) +{ + return Get_ULong(p); +} + +WW8SprmIter::WW8SprmIter(const sal_uInt8* pSprms_, sal_Int32 nLen_, + const wwSprmParser &rParser) + : mrSprmParser(rParser), pSprms( pSprms_), nRemLen( nLen_) +{ + UpdateMyMembers(); +} + +void WW8SprmIter::SetSprms(const sal_uInt8* pSprms_, sal_Int32 nLen_) +{ + pSprms = pSprms_; + nRemLen = nLen_; + UpdateMyMembers(); +} + +void WW8SprmIter::advance() +{ + if (nRemLen > 0 ) + { + sal_uInt16 nSize = nCurrentSize; + if (nSize > nRemLen) + nSize = nRemLen; + pSprms += nSize; + nRemLen -= nSize; + UpdateMyMembers(); + } +} + +void WW8SprmIter::UpdateMyMembers() +{ + bool bValid = (pSprms && nRemLen >= mrSprmParser.MinSprmLen()); + + if (bValid) + { + nCurrentId = mrSprmParser.GetSprmId(pSprms); + nCurrentSize = mrSprmParser.GetSprmSize(nCurrentId, pSprms, nRemLen); + pCurrentParams = pSprms + mrSprmParser.DistanceToData(nCurrentId); + bValid = nCurrentSize <= nRemLen; + SAL_WARN_IF(!bValid, "sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong"); + } + + if (!bValid) + { + nCurrentId = 0; + pCurrentParams = nullptr; + nCurrentSize = 0; + nRemLen = 0; + } +} + +SprmResult WW8SprmIter::FindSprm(sal_uInt16 nId, bool bFindFirst, const sal_uInt8* pNextByteMatch) +{ + SprmResult aRet; + + while (GetSprms()) + { + if (GetCurrentId() == nId) + { + sal_uInt16 nFixedLen = mrSprmParser.DistanceToData(nId); + sal_uInt16 nL = mrSprmParser.GetSprmSize(nId, GetSprms(), GetRemLen()); + SprmResult aSprmResult(GetCurrentParams(), nL - nFixedLen); + // typically pNextByteMatch is nullptr and we just return the first match + // very occasionally we want one with a specific following byte + if ( !pNextByteMatch || (aSprmResult.nRemainingData >= 1 && *aSprmResult.pSprm == *pNextByteMatch) ) + { + if ( bFindFirst ) + return aSprmResult; + aRet = aSprmResult; + } + } + advance(); + } + + return aRet; +} + +// temporary test +// WW8PLCFx_PCDAttrs cling to WW8PLCF_Pcd and therefore do not have their own iterators. +// All methods relating to iterators are therefore dummies. +WW8PLCFx_PCDAttrs::WW8PLCFx_PCDAttrs(const WW8Fib& rFib, + WW8PLCFx_PCD* pPLCFx_PCD, const WW8ScannerBase* pBase) + : WW8PLCFx(rFib, true), pPcdI(pPLCFx_PCD->GetPLCFIter()), + pPcd(pPLCFx_PCD), mrGrpprls(pBase->m_aPieceGrpprls) +{ +} + +sal_uInt32 WW8PLCFx_PCDAttrs::GetIdx() const +{ + return 0; +} + +void WW8PLCFx_PCDAttrs::SetIdx(sal_uInt32) +{ +} + +bool WW8PLCFx_PCDAttrs::SeekPos(WW8_CP ) +{ + return true; +} + +void WW8PLCFx_PCDAttrs::advance() +{ +} + +WW8_CP WW8PLCFx_PCDAttrs::Where() +{ + return pPcd ? pPcd->Where() : WW8_CP_MAX; +} + +void WW8PLCFx_PCDAttrs::GetSprms(WW8PLCFxDesc* p) +{ + void* pData; + + p->bRealLineEnd = false; + if ( !pPcdI || !pPcdI->Get(p->nStartPos, p->nEndPos, pData) ) + { + // PLCF fully processed + p->nStartPos = p->nEndPos = WW8_CP_MAX; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + return; + } + + const sal_uInt16 nPrm = SVBT16ToUInt16( static_cast<WW8_PCD*>(pData)->prm ); + if ( nPrm & 1 ) + { + // PRM Variant 2 + const sal_uInt16 nSprmIdx = nPrm >> 1; + + if( nSprmIdx >= mrGrpprls.size() ) + { + // Invalid Index + p->nStartPos = p->nEndPos = WW8_CP_MAX; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + return; + } + const sal_uInt8* pSprms = mrGrpprls[ nSprmIdx ].get(); + + p->nSprmsLen = SVBT16ToUInt16( pSprms ); // Length + pSprms += 2; + p->pMemPos = pSprms; // Position + } + else + { + // SPRM is stored directly into members var + /* + These are the attr that are in the piece-table instead of in the text! + */ + + if (IsSevenMinus(GetFIBVersion())) + { + aShortSprm[0] = static_cast<sal_uInt8>( ( nPrm & 0xfe) >> 1 ); + aShortSprm[1] = static_cast<sal_uInt8>( nPrm >> 8 ); + p->nSprmsLen = nPrm ? 2 : 0; // length + + // store Position of internal mini storage in Data Pointer + p->pMemPos = aShortSprm; + } + else + { + p->pMemPos = nullptr; + p->nSprmsLen = 0; + sal_uInt8 nSprmListIdx = static_cast<sal_uInt8>((nPrm & 0xfe) >> 1); + if( nSprmListIdx ) + { + // process Sprm Id Matching as explained in MS Documentation + + // ''Property Modifier(variant 1) (PRM)'' + // see file: s62f39.htm + + // Since Sprm is 7 bits, rgsprmPrm can hold 0x80 entries. + static const sal_uInt16 aSprmId[0x80] = + { + // sprmNoop, sprmNoop, sprmNoop, sprmNoop + 0x0000,0x0000,0x0000,0x0000, + // sprmPIncLvl, sprmPJc, sprmPFSideBySide, sprmPFKeep + 0x2402,0x2403,NS_sprm::LN_PFSideBySide,0x2405, + // sprmPFKeepFollow, sprmPFPageBreakBefore, sprmPBrcl, + // sprmPBrcp + 0x2406,0x2407,NS_sprm::LN_PBrcl,NS_sprm::LN_PBrcp, + // sprmPIlvl, sprmNoop, sprmPFNoLineNumb, sprmNoop + 0x260A,0x0000,0x240C,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmNoop + 0x0000,0x0000,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmNoop + 0x0000,0x0000,0x0000,0x0000, + // sprmPFInTable, sprmPFTtp, sprmNoop, sprmNoop + 0x2416,0x2417,0x0000,0x0000, + // sprmNoop, sprmPPc, sprmNoop, sprmNoop + 0x0000,0x261B,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmNoop + 0x0000,0x0000,0x0000,0x0000, + // sprmNoop, sprmPWr, sprmNoop, sprmNoop + 0x0000,0x2423,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmNoop + 0x0000,0x0000,0x0000,0x0000, + // sprmPFNoAutoHyph, sprmNoop, sprmNoop, sprmNoop + 0x242A,0x0000,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmPFLocked, sprmPFWidowControl + 0x0000,0x0000,0x2430,0x2431, + // sprmNoop, sprmPFKinsoku, sprmPFWordWrap, + // sprmPFOverflowPunct + 0x0000,0x2433,0x2434,0x2435, + // sprmPFTopLinePunct, sprmPFAutoSpaceDE, + // sprmPFAutoSpaceDN, sprmNoop + 0x2436,0x2437,0x2438,0x0000, + // sprmNoop, sprmPISnapBaseLine, sprmNoop, sprmNoop + 0x0000,NS_sprm::LN_PISnapBaseLine,0x000,0x0000, + // sprmNoop, sprmCFStrikeRM, sprmCFRMark, sprmCFFieldVanish + 0x0000,0x0800,0x0801,0x0802, + // sprmNoop, sprmNoop, sprmNoop, sprmCFData + 0x0000,0x0000,0x0000,0x0806, + // sprmNoop, sprmNoop, sprmNoop, sprmCFOle2 + 0x0000,0x0000,0x0000,0x080A, + // sprmNoop, sprmCHighlight, sprmCFEmboss, sprmCSfxText + 0x0000,0x2A0C,0x0858,0x2859, + // sprmNoop, sprmNoop, sprmNoop, sprmCPlain + 0x0000,0x0000,0x0000,0x2A33, + // sprmNoop, sprmCFBold, sprmCFItalic, sprmCFStrike + 0x0000,0x0835,0x0836,0x0837, + // sprmCFOutline, sprmCFShadow, sprmCFSmallCaps, sprmCFCaps, + 0x0838,0x0839,0x083a,0x083b, + // sprmCFVanish, sprmNoop, sprmCKul, sprmNoop, + 0x083C,0x0000,0x2A3E,0x0000, + // sprmNoop, sprmNoop, sprmCIco, sprmNoop, + 0x0000,0x0000,0x2A42,0x0000, + // sprmCHpsInc, sprmNoop, sprmCHpsPosAdj, sprmNoop, + NS_sprm::LN_CHpsInc,0x0000,NS_sprm::LN_CHpsPosAdj,0x0000, + // sprmCIss, sprmNoop, sprmNoop, sprmNoop, + 0x2A48,0x0000,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmNoop, + 0x0000,0x0000,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmNoop, sprmCFDStrike, + 0x0000,0x0000,0x0000,0x2A53, + // sprmCFImprint, sprmCFSpec, sprmCFObj, sprmPicBrcl, + 0x0854,0x0855,0x0856,NS_sprm::LN_PicBrcl, + // sprmPOutLvl, sprmPFBiDi, sprmNoop, sprmNoop, + 0x2640,0x2441,0x0000,0x0000, + // sprmNoop, sprmNoop, sprmPPnbrRMarkNot + 0x0000,0x0000,0x0000,0x0000 + }; + + // find real Sprm Id: + const sal_uInt16 nSprmId = aSprmId[ nSprmListIdx ]; + + if( nSprmId ) + { + // move Sprm Id and Sprm Param to internal mini storage: + aShortSprm[0] = static_cast<sal_uInt8>( nSprmId & 0x00ff) ; + aShortSprm[1] = static_cast<sal_uInt8>( ( nSprmId & 0xff00) >> 8 ); + aShortSprm[2] = static_cast<sal_uInt8>( nPrm >> 8 ); + + // store Sprm Length in member: + p->nSprmsLen = nPrm ? 3 : 0; + + // store Position of internal mini storage in Data Pointer + p->pMemPos = aShortSprm; + } + } + } + } +} + +WW8PLCFx_PCD::WW8PLCFx_PCD(const WW8Fib& rFib, WW8PLCFpcd* pPLCFpcd, + WW8_CP nStartCp, bool bVer67P) + : WW8PLCFx(rFib, false), nClipStart(-1) +{ + // construct own iterator + pPcdI.reset( new WW8PLCFpcd_Iter(*pPLCFpcd, nStartCp) ); + bVer67= bVer67P; +} + +WW8PLCFx_PCD::~WW8PLCFx_PCD() +{ +} + +sal_uInt32 WW8PLCFx_PCD::GetIMax() const +{ + return pPcdI ? pPcdI->GetIMax() : 0; +} + +sal_uInt32 WW8PLCFx_PCD::GetIdx() const +{ + return pPcdI ? pPcdI->GetIdx() : 0; +} + +void WW8PLCFx_PCD::SetIdx(sal_uInt32 nIdx) +{ + if (pPcdI) + pPcdI->SetIdx( nIdx ); +} + +bool WW8PLCFx_PCD::SeekPos(WW8_CP nCpPos) +{ + return pPcdI && pPcdI->SeekPos( nCpPos ); +} + +WW8_CP WW8PLCFx_PCD::Where() +{ + return pPcdI ? pPcdI->Where() : WW8_CP_MAX; +} + +long WW8PLCFx_PCD::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) +{ + void* pData; + rLen = 0; + + if ( !pPcdI || !pPcdI->Get(rStart, rEnd, pData) ) + { + rStart = rEnd = WW8_CP_MAX; + return -1; + } + return pPcdI->GetIdx(); +} + +void WW8PLCFx_PCD::advance() +{ + OSL_ENSURE(pPcdI , "missing pPcdI"); + if (pPcdI) + pPcdI->advance(); +} + +WW8_FC WW8PLCFx_PCD::CurrentPieceStartCp2Fc( WW8_CP nCp ) +{ + WW8_CP nCpStart, nCpEnd; + void* pData; + + if ( !pPcdI->Get(nCpStart, nCpEnd, pData) ) + { + OSL_ENSURE( false, "CurrentPieceStartCp2Fc() with false Cp found (1)" ); + return WW8_FC_MAX; + } + + OSL_ENSURE( nCp >= nCpStart && nCp < nCpEnd, + "AktPieceCp2Fc() with false Cp found (2)" ); + + if( nCp < nCpStart ) + nCp = nCpStart; + if( nCp >= nCpEnd ) + nCp = nCpEnd - 1; + + bool bIsUnicode = false; + WW8_FC nFC = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc ); + if( !bVer67 ) + nFC = WW8PLCFx_PCD::TransformPieceAddress( nFC, bIsUnicode ); + + WW8_CP nDistance; + bool bFail = o3tl::checked_sub(nCp, nCpStart, nDistance); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_FC_MAX; + } + + if (bIsUnicode) + { + bFail = o3tl::checked_multiply<WW8_CP>(nDistance, 2, nDistance); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_FC_MAX; + } + } + + WW8_FC nRet; + bFail = o3tl::checked_add(nFC, nDistance, nRet); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_FC_MAX; + } + + return nRet; +} + +void WW8PLCFx_PCD::CurrentPieceFc2Cp( WW8_CP& rStartPos, WW8_CP& rEndPos, + const WW8ScannerBase *pSBase ) +{ + //No point going anywhere with this + if ((rStartPos == WW8_CP_MAX) && (rEndPos == WW8_CP_MAX)) + return; + + rStartPos = pSBase->WW8Fc2Cp( rStartPos ); + rEndPos = pSBase->WW8Fc2Cp( rEndPos ); +} + +WW8_CP WW8PLCFx_PCD::CurrentPieceStartFc2Cp( WW8_FC nStartPos ) +{ + WW8_CP nCpStart, nCpEnd; + void* pData; + if ( !pPcdI->Get( nCpStart, nCpEnd, pData ) ) + { + OSL_ENSURE( false, "CurrentPieceStartFc2Cp() - error" ); + return WW8_CP_MAX; + } + bool bIsUnicode = false; + sal_Int32 nFcStart = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc ); + if( !bVer67 ) + nFcStart = WW8PLCFx_PCD::TransformPieceAddress( nFcStart, bIsUnicode ); + + sal_Int32 nUnicodeFactor = bIsUnicode ? 2 : 1; + + if( nStartPos < nFcStart ) + nStartPos = nFcStart; + + WW8_CP nCpLen; + bool bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + WW8_CP nCpLenBytes; + bFail = o3tl::checked_multiply(nCpLen, nUnicodeFactor, nCpLenBytes); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + WW8_FC nFcLen; + bFail = o3tl::checked_add(nFcStart, nCpLenBytes, nFcLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + WW8_FC nFcEnd; + bFail = o3tl::checked_add(nFcStart, nFcLen, nFcEnd); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + + if (nStartPos >= nFcEnd) + nStartPos = nFcEnd - (1 * nUnicodeFactor); + + WW8_FC nFcDiff = (nStartPos - nFcStart) / nUnicodeFactor; + + WW8_FC nCpRet; + bFail = o3tl::checked_add(nCpStart, nFcDiff, nCpRet); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + return nCpRet; +} + +// Helper routines for all + +// Convert BRC from WW6 to WW8 format +WW8_BRC::WW8_BRC(const WW8_BRCVer6& brcVer6) +{ + sal_uInt8 _dptLineWidth = brcVer6.dxpLineWidth(), + _brcType = brcVer6.brcType(); + + if (_dptLineWidth > 5) // this signifies dashed(6) or dotted(7) line + { + _brcType = _dptLineWidth; + _dptLineWidth = 1; + } + _dptLineWidth *= 6; // convert units from 0.75pt to 1/8pt + + *this = WW8_BRC(_dptLineWidth, _brcType, brcVer6.ico(), brcVer6.dxpSpace(), + brcVer6.fShadow(), false); +} + +// Convert BRC from WW8 to WW9 format +WW8_BRCVer9::WW8_BRCVer9(const WW8_BRC& brcVer8) +{ + if (brcVer8.isNil()) { + UInt32ToSVBT32(0, aBits1); + UInt32ToSVBT32(0xffffffff, aBits2); + } + else + { + sal_uInt32 _cv = brcVer8.ico() == 0 ? 0xff000000 // "auto" colour + : wwUtility::RGBToBGR(SwWW8ImplReader::GetCol(brcVer8.ico())); + *this = WW8_BRCVer9(_cv, brcVer8.dptLineWidth(), brcVer8.brcType(), + brcVer8.dptSpace(), brcVer8.fShadow(), brcVer8.fFrame()); + } +} + +short WW8_BRC::DetermineBorderProperties(short *pSpace) const +{ + WW8_BRCVer9 brcVer9(*this); + return brcVer9.DetermineBorderProperties(pSpace); +} + +short WW8_BRCVer9::DetermineBorderProperties(short *pSpace) const +{ + /* + Word does not factor the width of the border into the width/height + stored in the information for graphic/table/object widths, so we need + to figure out this extra width here and utilize the returned size in + our calculations + */ + short nMSTotalWidth; + + //Specification in 8ths of a point, 1 Point = 20 Twips, so by 2.5 + nMSTotalWidth = static_cast<short>(dptLineWidth()) * 20 / 8; + + //Figure out the real size of the border according to word + switch (brcType()) + { + //Note that codes over 25 are undocumented, and I can't create + //these 4 here in the wild. + case 2: + case 4: + case 5: + case 22: + OSL_FAIL("Can't create these from the menus, please report"); + break; + default: + case 23: //Only 3pt in the menus, but honours the size setting. + break; + case 10: + /* + triple line is five times the width of an ordinary line, + except that the smallest 1/4 point size appears to have + exactly the same total border width as a 3/4 point size + ordinary line, i.e. three times the nominal line width. The + second smallest 1/2 point size appears to have exactly the + total border width as a 2 1/4 border, i.e 4.5 times the size. + */ + if (nMSTotalWidth == 5) + nMSTotalWidth*=3; + else if (nMSTotalWidth == 10) + nMSTotalWidth = nMSTotalWidth*9/2; + else + nMSTotalWidth*=5; + break; + case 20: + /* + wave, the dimensions appear to be created by the drawing of + the wave, so we have only two possibilities in the menus, 3/4 + point is equal to solid 3 point. This calculation seems to + match well to results. + */ + nMSTotalWidth +=45; + break; + case 21: + /* + double wave, the dimensions appear to be created by the + drawing of the wave, so we have only one possibilities in the + menus, that of 3/4 point is equal to solid 3 point. This + calculation seems to match well to results. + */ + nMSTotalWidth += 45*2; + break; + } + + if (pSpace) + *pSpace = static_cast<short>(dptSpace()) * 20; // convert from points to twips + return nMSTotalWidth; +} + +/* + * WW8Cp2Fc is a good method, a CP always maps to a FC + * WW8Fc2Cp on the other hand is more dubious, a random FC + * may not map to a valid CP. Try and avoid WW8Fc2Cp where + * possible + */ +WW8_CP WW8ScannerBase::WW8Fc2Cp( WW8_FC nFcPos ) const +{ + WW8_CP nFallBackCpEnd = WW8_CP_MAX; + if( nFcPos == WW8_FC_MAX ) + return nFallBackCpEnd; + + bool bIsUnicode; + if (m_pWw8Fib->m_nVersion >= 8) + bIsUnicode = false; + else + bIsUnicode = m_pWw8Fib->m_fExtChar; + + if( m_pPieceIter ) // Complex File ? + { + sal_uInt32 nOldPos = m_pPieceIter->GetIdx(); + + for (m_pPieceIter->SetIdx(0); + m_pPieceIter->GetIdx() < m_pPieceIter->GetIMax(); m_pPieceIter->advance()) + { + WW8_CP nCpStart, nCpEnd; + void* pData; + if( !m_pPieceIter->Get( nCpStart, nCpEnd, pData ) ) + { // outside PLCFfpcd ? + OSL_ENSURE( false, "PLCFpcd-WW8Fc2Cp() went wrong" ); + break; + } + sal_Int32 nFcStart = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc ); + if (m_pWw8Fib->m_nVersion >= 8) + { + nFcStart = WW8PLCFx_PCD::TransformPieceAddress( nFcStart, + bIsUnicode ); + } + else + { + bIsUnicode = m_pWw8Fib->m_fExtChar; + } + + sal_Int32 nLen; + if (o3tl::checked_sub(nCpEnd, nCpStart, nLen)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + if (bIsUnicode) + { + if (o3tl::checked_multiply<WW8_CP>(nLen, 2, nLen)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + } + + /* + If this cp is inside this piece, or it's the last piece and we are + on the very last cp of that piece + */ + if (nFcPos >= nFcStart) + { + // found + WW8_FC nFcDiff; + if (o3tl::checked_sub(nFcPos, nFcStart, nFcDiff)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + if (bIsUnicode) + nFcDiff /= 2; + WW8_CP nTempCp; + if (o3tl::checked_add(nCpStart, nFcDiff, nTempCp)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + WW8_FC nFcEnd; + if (o3tl::checked_add(nFcStart, nLen, nFcEnd)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + if (nFcPos < nFcEnd) + { + m_pPieceIter->SetIdx( nOldPos ); + return nTempCp; + } + else if (nFcPos == nFcEnd) + { + //Keep this cp as its on a piece boundary because we might + //need it if tests fail + nFallBackCpEnd = nTempCp; + } + } + } + // not found + m_pPieceIter->SetIdx( nOldPos ); // not found + /* + If it was not found, then this is because it has fallen between two + stools, i.e. either it is the last cp/fc of the last piece, or it is + the last cp/fc of a disjoint piece. + */ + return nFallBackCpEnd; + } + + WW8_FC nFcDiff; + if (o3tl::checked_sub(nFcPos, m_pWw8Fib->m_fcMin, nFcDiff)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + // No complex file + if (!bIsUnicode) + nFallBackCpEnd = nFcDiff; + else + nFallBackCpEnd = (nFcDiff + 1) / 2; + + return nFallBackCpEnd; +} + +// the fib of WinWord2 has a last entry of cpnBtePap of 2 byte sized type PN at +// offset 324 +const int nSmallestPossibleFib = 326; + +WW8_FC WW8ScannerBase::WW8Cp2Fc(WW8_CP nCpPos, bool* pIsUnicode, + WW8_CP* pNextPieceCp, bool* pTestFlag) const +{ + if( pTestFlag ) + *pTestFlag = true; + if( WW8_CP_MAX == nCpPos ) + return WW8_CP_MAX; + + bool bIsUnicode; + if( !pIsUnicode ) + pIsUnicode = &bIsUnicode; + + if (m_pWw8Fib->m_nVersion >= 8) + *pIsUnicode = false; + else + *pIsUnicode = m_pWw8Fib->m_fExtChar; + + WW8_FC nRet; + + if( m_pPieceIter ) + { + // Complex File + if( pNextPieceCp ) + *pNextPieceCp = WW8_CP_MAX; + + if( !m_pPieceIter->SeekPos( nCpPos ) ) + { + if( pTestFlag ) + *pTestFlag = false; + else { + OSL_ENSURE( false, "Handed over wrong CP to WW8Cp2Fc()" ); + } + return WW8_FC_MAX; + } + WW8_CP nCpStart, nCpEnd; + void* pData; + if( !m_pPieceIter->Get( nCpStart, nCpEnd, pData ) ) + { + if( pTestFlag ) + *pTestFlag = false; + else { + OSL_ENSURE( false, "PLCFfpcd-Get went wrong" ); + } + return WW8_FC_MAX; + } + if( pNextPieceCp ) + *pNextPieceCp = nCpEnd; + + nRet = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc ); + if (m_pWw8Fib->m_nVersion >= 8) + nRet = WW8PLCFx_PCD::TransformPieceAddress( nRet, *pIsUnicode ); + else + *pIsUnicode = m_pWw8Fib->m_fExtChar; + + WW8_CP nCpLen; + bool bFail = o3tl::checked_sub(nCpPos, nCpStart, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + if (*pIsUnicode) + { + bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + } + + bFail = o3tl::checked_add(nRet, nCpLen, nRet); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + return nRet; + } + + if (*pIsUnicode) + { + const bool bFail = o3tl::checked_multiply<WW8_CP>(nCpPos, 2, nCpPos); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + } + + // No complex file + const bool bFail = o3tl::checked_add(m_pWw8Fib->m_fcMin, nCpPos, nRet); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + // the text and the fib share the same stream, if the text is inside the fib + // then it's definitely a bad offset. The smallest FIB supported is that of + // WW2 which is 326 bytes in size + if (nRet < nSmallestPossibleFib) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return WW8_CP_MAX; + } + + return nRet; +} + +std::unique_ptr<WW8PLCFpcd> WW8ScannerBase::OpenPieceTable( SvStream* pStr, const WW8Fib* pWwF ) +{ + if ( ((8 > m_pWw8Fib->m_nVersion) && !pWwF->m_fComplex) || !pWwF->m_lcbClx ) + return nullptr; + + if (pWwF->m_lcbClx < 0) + return nullptr; + + WW8_FC nClxPos = pWwF->m_fcClx; + + if (!checkSeek(*pStr, nClxPos)) + return nullptr; + + sal_Int32 nClxLen = pWwF->m_lcbClx; + sal_Int32 nLeft = nClxLen; + + while (true) + { + sal_uInt8 clxt(2); + pStr->ReadUChar( clxt ); + nLeft--; + if( 2 == clxt) // PLCFfpcd ? + break; // PLCFfpcd found + sal_uInt16 nLen(0); + pStr->ReadUInt16( nLen ); + nLeft -= 2 + nLen; + if( nLeft < 0 ) + return nullptr; // gone wrong + if( 1 == clxt ) // clxtGrpprl ? + { + if (m_aPieceGrpprls.size() == SHRT_MAX) + return nullptr; + if (nLen > pStr->remainingSize()) + return nullptr; + std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[nLen+2]); // allocate + ShortToSVBT16(nLen, p.get()); // add length + if (!checkRead(*pStr, p.get()+2, nLen)) // read grpprl + { + return nullptr; + } + m_aPieceGrpprls.push_back(std::move(p)); // add to array + } + else + { + nLen = std::min<sal_uInt64>(nLen, pStr->remainingSize()); + pStr->Seek(pStr->Tell() + nLen); // non-Grpprl left + } + } + + // read Piece Table PLCF + sal_Int32 nPLCFfLen(0); + if (pWwF->GetFIBVersion() <= ww::eWW2) + { + sal_Int16 nWordTwoLen(0); + pStr->ReadInt16( nWordTwoLen ); + nPLCFfLen = nWordTwoLen; + } + else + pStr->ReadInt32( nPLCFfLen ); + OSL_ENSURE( 65536 > nPLCFfLen, "PLCFfpcd above 64 k" ); + return std::make_unique<WW8PLCFpcd>( pStr, pStr->Tell(), nPLCFfLen, 8 ); +} + +WW8ScannerBase::WW8ScannerBase( SvStream* pSt, SvStream* pTableSt, + SvStream* pDataSt, WW8Fib* pWwFib ) + : m_pWw8Fib(pWwFib) +{ + m_pPiecePLCF = OpenPieceTable( pTableSt, m_pWw8Fib ); // Complex + if( m_pPiecePLCF ) + { + m_pPieceIter.reset(new WW8PLCFpcd_Iter( *m_pPiecePLCF )); + m_pPLCFx_PCD.reset( new WW8PLCFx_PCD(*pWwFib, m_pPiecePLCF.get(), 0, + IsSevenMinus(m_pWw8Fib->GetFIBVersion()))); + m_pPLCFx_PCDAttrs.reset(new WW8PLCFx_PCDAttrs(*pWwFib, + m_pPLCFx_PCD.get(), this)); + } + else + { + m_pPieceIter = nullptr; + m_pPLCFx_PCD = nullptr; + m_pPLCFx_PCDAttrs = nullptr; + } + + // pChpPLCF and pPapPLCF may NOT be created before pPLCFx_PCD !! + m_pChpPLCF.reset(new WW8PLCFx_Cp_FKP( pSt, pTableSt, pDataSt, *this, CHP )); // CHPX + m_pPapPLCF.reset(new WW8PLCFx_Cp_FKP( pSt, pTableSt, pDataSt, *this, PAP )); // PAPX + + m_pSepPLCF.reset(new WW8PLCFx_SEPX( pSt, pTableSt, *pWwFib, 0 )); // SEPX + + // Footnotes + m_pFootnotePLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0, + pWwFib->m_fcPlcffndRef, pWwFib->m_lcbPlcffndRef, pWwFib->m_fcPlcffndText, + pWwFib->m_lcbPlcffndText, 2 )); + // Endnotes + m_pEdnPLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0, + pWwFib->m_fcPlcfendRef, pWwFib->m_lcbPlcfendRef, pWwFib->m_fcPlcfendText, + pWwFib->m_lcbPlcfendText, 2 )); + // Comments + m_pAndPLCF.reset(new WW8PLCFx_SubDoc( pTableSt, *pWwFib, 0, + pWwFib->m_fcPlcfandRef, pWwFib->m_lcbPlcfandRef, pWwFib->m_fcPlcfandText, + pWwFib->m_lcbPlcfandText, IsSevenMinus(pWwFib->GetFIBVersion()) ? 20 : 30)); + + // Fields Main Text + m_pFieldPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_MAINTEXT)); + // Fields Header / Footer + m_pFieldHdFtPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_HDFT)); + // Fields Footnote + m_pFieldFootnotePLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_FTN)); + // Fields Endnote + m_pFieldEdnPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_EDN)); + // Fields Comments + m_pFieldAndPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_AND)); + // Fields in Textboxes in Main Text + m_pFieldTxbxPLCF.reset(new WW8PLCFx_FLD(pTableSt, *pWwFib, MAN_TXBX)); + // Fields in Textboxes in Header / Footer + m_pFieldTxbxHdFtPLCF.reset(new WW8PLCFx_FLD(pTableSt,*pWwFib,MAN_TXBX_HDFT)); + + // Note: 6 stands for "6 OR 7", 7 stands for "ONLY 7" + switch( m_pWw8Fib->m_nVersion ) + { + case 6: + case 7: + if( pWwFib->m_fcPlcfdoaMom && pWwFib->m_lcbPlcfdoaMom ) + { + m_pMainFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfdoaMom, + pWwFib->m_lcbPlcfdoaMom, 6 )); + } + if( pWwFib->m_fcPlcfdoaHdr && pWwFib->m_lcbPlcfdoaHdr ) + { + m_pHdFtFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfdoaHdr, + pWwFib->m_lcbPlcfdoaHdr, 6 )); + } + break; + case 8: + if( pWwFib->m_fcPlcfspaMom && pWwFib->m_lcbPlcfspaMom ) + { + m_pMainFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfspaMom, + pWwFib->m_lcbPlcfspaMom, 26 )); + } + if( pWwFib->m_fcPlcfspaHdr && pWwFib->m_lcbPlcfspaHdr ) + { + m_pHdFtFdoa.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfspaHdr, + pWwFib->m_lcbPlcfspaHdr, 26 )); + } + // PLCF for TextBox break-descriptors in the main text + if( pWwFib->m_fcPlcftxbxBkd && pWwFib->m_lcbPlcftxbxBkd ) + { + m_pMainTxbxBkd.reset(new WW8PLCFspecial( pTableSt, + pWwFib->m_fcPlcftxbxBkd, pWwFib->m_lcbPlcftxbxBkd, 0)); + } + // PLCF for TextBox break-descriptors in Header/Footer range + if( pWwFib->m_fcPlcfHdrtxbxBkd && pWwFib->m_lcbPlcfHdrtxbxBkd ) + { + m_pHdFtTxbxBkd.reset(new WW8PLCFspecial( pTableSt, + pWwFib->m_fcPlcfHdrtxbxBkd, pWwFib->m_lcbPlcfHdrtxbxBkd, 0)); + } + // Sub table cp positions + if (pWwFib->m_fcPlcfTch && pWwFib->m_lcbPlcfTch) + { + m_pMagicTables.reset(new WW8PLCFspecial( pTableSt, + pWwFib->m_fcPlcfTch, pWwFib->m_lcbPlcfTch, 4)); + } + // Sub document cp positions + if (pWwFib->m_fcPlcfwkb && pWwFib->m_lcbPlcfwkb) + { + m_pSubdocs.reset(new WW8PLCFspecial( pTableSt, + pWwFib->m_fcPlcfwkb, pWwFib->m_lcbPlcfwkb, 12)); + } + // Extended ATRD + if (pWwFib->m_fcAtrdExtra && pWwFib->m_lcbAtrdExtra) + { + sal_uInt64 const nOldPos = pTableSt->Tell(); + if (checkSeek(*pTableSt, pWwFib->m_fcAtrdExtra) && (pTableSt->remainingSize() >= pWwFib->m_lcbAtrdExtra)) + { + m_pExtendedAtrds.reset( new sal_uInt8[pWwFib->m_lcbAtrdExtra] ); + pWwFib->m_lcbAtrdExtra = pTableSt->ReadBytes(m_pExtendedAtrds.get(), pWwFib->m_lcbAtrdExtra); + } + else + pWwFib->m_lcbAtrdExtra = 0; + pTableSt->Seek(nOldPos); + } + + break; + default: + OSL_ENSURE( false, "nVersion not implemented!" ); + break; + } + + // PLCF for TextBox stories in main text + sal_uInt32 nLenTxBxS = (8 > m_pWw8Fib->m_nVersion) ? 0 : 22; + if( pWwFib->m_fcPlcftxbxText && pWwFib->m_lcbPlcftxbxText ) + { + m_pMainTxbx.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcftxbxText, + pWwFib->m_lcbPlcftxbxText, nLenTxBxS )); + } + + // PLCF for TextBox stories in Header/Footer range + if( pWwFib->m_fcPlcfHdrtxbxText && pWwFib->m_lcbPlcfHdrtxbxText ) + { + m_pHdFtTxbx.reset(new WW8PLCFspecial( pTableSt, pWwFib->m_fcPlcfHdrtxbxText, + pWwFib->m_lcbPlcfHdrtxbxText, nLenTxBxS )); + } + + m_pBook.reset(new WW8PLCFx_Book(pTableSt, *pWwFib)); + m_pAtnBook.reset(new WW8PLCFx_AtnBook(pTableSt, *pWwFib)); + m_pFactoidBook.reset(new WW8PLCFx_FactoidBook(pTableSt, *pWwFib)); +} + +WW8ScannerBase::~WW8ScannerBase() +{ + m_aPieceGrpprls.clear(); + m_pPLCFx_PCDAttrs.reset(); + m_pPLCFx_PCD.reset(); + m_pPieceIter.reset(); + m_pPiecePLCF.reset(); + m_pFactoidBook.reset(); + m_pAtnBook.reset(); + m_pBook.reset(); + m_pFieldEdnPLCF.reset(); + m_pFieldFootnotePLCF.reset(); + m_pFieldAndPLCF.reset(); + m_pFieldHdFtPLCF.reset(); + m_pFieldPLCF.reset(); + m_pFieldTxbxPLCF.reset(); + m_pFieldTxbxHdFtPLCF.reset(); + m_pEdnPLCF.reset(); + m_pFootnotePLCF.reset(); + m_pAndPLCF.reset(); + m_pSepPLCF.reset(); + m_pPapPLCF.reset(); + m_pChpPLCF.reset(); + m_pMainFdoa.reset(); + m_pHdFtFdoa.reset(); + m_pMainTxbx.reset(); + m_pMainTxbxBkd.reset(); + m_pHdFtTxbx.reset(); + m_pHdFtTxbxBkd.reset(); + m_pMagicTables.reset(); + m_pSubdocs.reset(); +} + +// Fields + +static bool WW8SkipField(WW8PLCFspecial& rPLCF) +{ + void* pData; + WW8_CP nP; + + if (!rPLCF.Get(nP, pData)) // End of PLCFspecial? + return false; + + rPLCF.advance(); + + if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) != 0x13 ) // No beginning? + return true; // Do not terminate on error + + if( !rPLCF.Get( nP, pData ) ) + return false; + + while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 ) + { + // still new (nested) beginnings ? + WW8SkipField( rPLCF ); // nested Field in description + if( !rPLCF.Get( nP, pData ) ) + return false; + } + + if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x14 ) + { + + // Field Separator ? + rPLCF.advance(); + + if( !rPLCF.Get( nP, pData ) ) + return false; + + while ((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13) + { + // still new (nested) beginnings? + WW8SkipField( rPLCF ); // nested Field in Results + if( !rPLCF.Get( nP, pData ) ) + return false; + } + } + rPLCF.advance(); + + return true; +} + +static bool WW8GetFieldPara(WW8PLCFspecial& rPLCF, WW8FieldDesc& rF) +{ + void* pData; + sal_uInt32 nOldIdx = rPLCF.GetIdx(); + + rF.nLen = rF.nId = rF.nOpt = 0; + rF.bCodeNest = rF.bResNest = false; + + if (!rPLCF.Get(rF.nSCode, pData) || rF.nSCode < 0) // end of PLCFspecial? + goto Err; + + rPLCF.advance(); + + if (!pData || (static_cast<sal_uInt8*>(pData)[0] & 0x1f) != 0x13) // No beginning? + goto Err; + + rF.nId = static_cast<sal_uInt8*>(pData)[1]; + + if( !rPLCF.Get( rF.nLCode, pData ) ) + goto Err; + + if (rF.nLCode < rF.nSCode) + goto Err; + + rF.nSRes = rF.nLCode; // Default + rF.nSCode++; // without markers + rF.nLCode -= rF.nSCode; // Pos -> length + + while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 ) + { + // still new (nested) beginnings ? + WW8SkipField( rPLCF ); // nested Field in description + rF.bCodeNest = true; + if (!rPLCF.Get(rF.nSRes, pData) || rF.nSRes < 0) + goto Err; + } + + if ((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x14 ) // Field Separator? + { + rPLCF.advance(); + + if (!rPLCF.Get(rF.nLRes, pData) || rF.nLRes < 0) + goto Err; + + while((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x13 ) + { + // still new (nested) beginnings ? + WW8SkipField( rPLCF ); // nested Field in results + rF.bResNest = true; + if (!rPLCF.Get(rF.nLRes, pData) || rF.nLRes < 0) + goto Err; + } + WW8_CP nTmp; + if (o3tl::checked_sub<WW8_CP>(rF.nLRes, rF.nSCode, nTmp)) + { + rF.nLen = 0; + goto Err; + } + if (o3tl::checked_add<WW8_CP>(nTmp, 2, rF.nLen)) // nLRes is still the final position + { + rF.nLen = 0; + goto Err; + } + rF.nLRes -= rF.nSRes; // now: nLRes = length + if (o3tl::checked_add<WW8_CP>(rF.nSRes, 1, rF.nSRes)) // Endpos including Markers + { + rF.nLen = 0; + goto Err; + } + rF.nLRes--; + }else{ + rF.nLRes = 0; // no result found + WW8_CP nTmp; + if (o3tl::checked_sub<WW8_CP>(rF.nSRes, rF.nSCode, nTmp)) + { + rF.nLen = 0; + goto Err; + } + if (o3tl::checked_add<WW8_CP>(nTmp, 2, rF.nLen)) // total length + { + rF.nLen = 0; + goto Err; + } + } + + if (rF.nLen < 0) + { + rF.nLen = 0; + goto Err; + } + + rPLCF.advance(); + if((static_cast<sal_uInt8*>(pData)[0] & 0x1f ) == 0x15 ) + { + // Field end ? + // INDEX-Field has set Bit7? + rF.nOpt = static_cast<sal_uInt8*>(pData)[1]; // yes -> copy flags + }else{ + rF.nId = 0; // no -> Field invalid + } + + rPLCF.SetIdx( nOldIdx ); + return true; +Err: + rPLCF.SetIdx( nOldIdx ); + return false; +} + +OUString read_uInt8_BeltAndBracesString(SvStream& rStrm, rtl_TextEncoding eEnc) +{ + const OUString aRet = read_uInt8_lenPrefixed_uInt8s_ToOUString(rStrm, eEnc); + rStrm.SeekRel(sizeof(sal_uInt8)); // skip null-byte at end + return aRet; +} + +OUString read_uInt16_BeltAndBracesString(SvStream& rStrm) +{ + const OUString aRet = read_uInt16_PascalString(rStrm); + rStrm.SeekRel(sizeof(sal_Unicode)); // skip null-byte at end + return aRet; +} + +sal_Int32 WW8ScannerBase::WW8ReadString( SvStream& rStrm, OUString& rStr, + WW8_CP nCurrentStartCp, long nTotalLen, rtl_TextEncoding eEnc ) const +{ + // Read in plain text, which can extend over several pieces + rStr.clear(); + + if (nCurrentStartCp < 0 || nTotalLen < 0) + return 0; + + WW8_CP nBehindTextCp = nCurrentStartCp + nTotalLen; + WW8_CP nNextPieceCp = nBehindTextCp; // Initialization, important for Ver6 + long nTotalRead = 0; + do + { + bool bIsUnicode(false), bPosOk(false); + WW8_FC fcAct = WW8Cp2Fc(nCurrentStartCp,&bIsUnicode,&nNextPieceCp,&bPosOk); + + // Probably aimed beyond file end, doesn't matter! + if( !bPosOk ) + break; + + bool bValid = checkSeek(rStrm, fcAct); + if (!bValid) + break; + + WW8_CP nEnd = (nNextPieceCp < nBehindTextCp) ? nNextPieceCp + : nBehindTextCp; + WW8_CP nLen; + const bool bFail = o3tl::checked_sub(nEnd, nCurrentStartCp, nLen); + if (bFail) + break; + + if( 0 >= nLen ) + break; + + rStr += bIsUnicode + ? read_uInt16s_ToOUString(rStrm, nLen) + : read_uInt8s_ToOUString(rStrm, nLen, eEnc); + + nTotalRead += nLen; + nCurrentStartCp += nLen; + if ( nTotalRead != rStr.getLength() ) + break; + } + while( nTotalRead < nTotalLen ); + + return rStr.getLength(); +} + +WW8PLCFspecial::WW8PLCFspecial(SvStream* pSt, sal_uInt32 nFilePos, + sal_uInt32 nPLCF, sal_uInt32 nStruct) + : nIdx(0), nStru(nStruct) +{ + const sal_uInt32 nValidMin=4; + + sal_uInt64 const nOldPos = pSt->Tell(); + + bool bValid = checkSeek(*pSt, nFilePos); + std::size_t nRemainingSize = pSt->remainingSize(); + if( !(nRemainingSize >= nValidMin && nPLCF >= nValidMin )) + bValid = false; + nPLCF = bValid ? std::min(nRemainingSize, static_cast<std::size_t>(nPLCF)) : nValidMin; + + // Pointer to Pos- and Struct-array + pPLCF_PosArray.reset( new sal_Int32[ ( nPLCF + 3 ) / 4 ] ); + pPLCF_PosArray[0] = 0; + + nPLCF = bValid ? pSt->ReadBytes(pPLCF_PosArray.get(), nPLCF) : nValidMin; + + nPLCF = std::max(nPLCF, nValidMin); + + nIMax = ( nPLCF - 4 ) / ( 4 + nStruct ); +#ifdef OSL_BIGENDIAN + for( nIdx = 0; nIdx <= nIMax; nIdx++ ) + pPLCF_PosArray[nIdx] = OSL_SWAPDWORD( pPLCF_PosArray[nIdx] ); + nIdx = 0; +#endif // OSL_BIGENDIAN + if( nStruct ) // Pointer to content array + pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&pPLCF_PosArray[nIMax + 1]); + else + pPLCF_Contents = nullptr; // no content + + pSt->Seek(nOldPos); +} + +// WW8PLCFspecial::SeekPos() sets WW8PLCFspecial to position nPos, while also the entry is used +// that begins before nPos and ends after nPos. +// Suitable for normal attributes. However, the beginning of the attribute is not corrected onto +// the position nPos. +bool WW8PLCFspecial::SeekPos(long nP) +{ + if( nP < pPLCF_PosArray[0] ) + { + nIdx = 0; + return false; // Not found: nP less than smallest entry + } + + // Search from beginning? + if ((nIdx < 1) || (nP < pPLCF_PosArray[nIdx - 1])) + nIdx = 1; + + long nI = nIdx; + long nEnd = nIMax; + + for(int n = (1==nIdx ? 1 : 2); n; --n ) + { + for( ; nI <=nEnd; ++nI) + { // search with an index that is incremented by 1 + if( nP < pPLCF_PosArray[nI] ) + { // found position + nIdx = nI - 1; // nI - 1 is the correct index + return true; // done + } + } + nI = 1; + nEnd = nIdx-1; + } + nIdx = nIMax; // not found, greater than all entries + return false; +} + +// WW8PLCFspecial::SeekPosExact() like SeekPos(), but it is ensured that no attribute is cut, +// i.e. the next given attribute begins at or after nPos. +// Is used for fields and bookmarks. +bool WW8PLCFspecial::SeekPosExact(long nP) +{ + if( nP < pPLCF_PosArray[0] ) + { + nIdx = 0; + return false; // Not found: nP less than smallest entry + } + // Search from beginning? + if( nP <=pPLCF_PosArray[nIdx] ) + nIdx = 0; + + long nI = nIdx ? nIdx-1 : 0; + long nEnd = nIMax; + + for(int n = (0==nIdx ? 1 : 2); n; --n ) + { + for( ; nI < nEnd; ++nI) + { + if( nP <=pPLCF_PosArray[nI] ) + { // found position + nIdx = nI; // nI is the correct index + return true; // done + } + } + nI = 0; + nEnd = nIdx; + } + nIdx = nIMax; // Not found, greater than all entries + return false; +} + +bool WW8PLCFspecial::Get(WW8_CP& rPos, void*& rpValue) const +{ + return GetData( nIdx, rPos, rpValue ); +} + +bool WW8PLCFspecial::GetData(long nInIdx, WW8_CP& rPos, void*& rpValue) const +{ + if ( nInIdx >= nIMax ) + { + rPos = WW8_CP_MAX; + return false; + } + rPos = pPLCF_PosArray[nInIdx]; + rpValue = pPLCF_Contents ? static_cast<void*>(&pPLCF_Contents[nInIdx * nStru]) : nullptr; + return true; +} + +// WW8PLCF e.g. for SEPX +// Ctor for *others* than Fkps +// With nStartPos < 0, the first element of PLCFs will be taken +WW8PLCF::WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct, + WW8_CP nStartPos) : nIdx(0), nStru(nStruct) +{ + if (nPLCF < 0) + { + SAL_WARN("sw.ww8", "broken WW8PLCF, ignoring"); + nPLCF = 0; + } + else + nIMax = (nPLCF - 4) / (4 + nStruct); + + ReadPLCF(rSt, nFilePos, nPLCF); + + if( nStartPos >= 0 ) + SeekPos( nStartPos ); +} + +// Ctor *only* for Fkps +// The last 2 parameters are needed for PLCF.Chpx and PLCF.Papx. +// If ncpN != 0, then an incomplete PLCF will be completed. This is always required for WW6 with +// lack of resources and for WordPad (W95). +// With nStartPos < 0, the first element of the PLCFs is taken. +WW8PLCF::WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct, + WW8_CP nStartPos, sal_Int32 nPN, sal_Int32 ncpN): nIdx(0), + nStru(nStruct) +{ + if (nPLCF < 0) + { + SAL_WARN("sw.ww8", "broken WW8PLCF, ignoring"); + nIMax = SAL_MAX_INT32; + } + else + nIMax = (nPLCF - 4) / (4 + nStruct); + + if( nIMax >= ncpN ) + ReadPLCF(rSt, nFilePos, nPLCF); + else + GeneratePLCF(rSt, nPN, ncpN); + + if( nStartPos >= 0 ) + SeekPos( nStartPos ); +} + +void WW8PLCF::ReadPLCF(SvStream& rSt, WW8_FC nFilePos, sal_uInt32 nPLCF) +{ + sal_uInt64 const nOldPos = rSt.Tell(); + bool bValid = nPLCF != 0 && checkSeek(rSt, nFilePos) + && (rSt.remainingSize() >= nPLCF); + + if (bValid) + { + // Pointer to Pos-array + pPLCF_PosArray.reset( new WW8_CP[ ( nPLCF + 3 ) / 4 ] ); + bValid = checkRead(rSt, pPLCF_PosArray.get(), nPLCF); + } + + if (bValid) + { +#ifdef OSL_BIGENDIAN + for( nIdx = 0; nIdx <= nIMax; nIdx++ ) + pPLCF_PosArray[nIdx] = OSL_SWAPDWORD( pPLCF_PosArray[nIdx] ); + nIdx = 0; +#endif // OSL_BIGENDIAN + // Pointer to content array + pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&pPLCF_PosArray[nIMax + 1]); + + TruncToSortedRange(); + } + + OSL_ENSURE(bValid, "Document has corrupt PLCF, ignoring it"); + + if (!bValid) + MakeFailedPLCF(); + + rSt.Seek(nOldPos); +} + +void WW8PLCF::MakeFailedPLCF() +{ + nIMax = 0; + pPLCF_PosArray.reset( new sal_Int32[2] ); + pPLCF_PosArray[0] = pPLCF_PosArray[1] = WW8_CP_MAX; + pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&pPLCF_PosArray[nIMax + 1]); +} + +void WW8PLCF::TruncToSortedRange() +{ + //Docs state that: ... all Plcs ... are sorted in ascending order. + //So ensure that here for broken documents. + for (auto nI = 0; nI < nIMax; ++nI) + { + if (pPLCF_PosArray[nI] > pPLCF_PosArray[nI+1]) + { + SAL_WARN("sw.ww8", "Document has unsorted PLCF, truncated to sorted portion"); + nIMax = nI; + break; + } + } +} + +void WW8PLCF::GeneratePLCF(SvStream& rSt, sal_Int32 nPN, sal_Int32 ncpN) +{ + OSL_ENSURE( nIMax < ncpN, "Pcl.Fkp: Why is PLCF too big?" ); + + bool failure = false; + nIMax = ncpN; + + if ((nIMax < 1) || (nIMax > (WW8_CP_MAX - 4) / (4 + nStru)) || nPN < 0) + failure = true; + + if (!failure) + { + // Check arguments to ShortToSVBT16 in loop below will all be valid: + sal_Int32 nResult; + failure = o3tl::checked_add(nPN, ncpN, nResult) || nResult > SAL_MAX_UINT16; + } + + if (!failure) + { + size_t nSiz = (4 + nStru) * nIMax + 4; + size_t nElems = ( nSiz + 3 ) / 4; + pPLCF_PosArray.reset( new sal_Int32[ nElems ] ); // Pointer to Pos-array + + for (sal_Int32 i = 0; i < ncpN && !failure; ++i) + { + failure = true; + // construct FC entries + // first FC entry of each Fkp + if (!checkSeek(rSt, (nPN + i) << 9)) + break; + + WW8_CP nFc(0); + rSt.ReadInt32( nFc ); + pPLCF_PosArray[i] = nFc; + + failure = bool(rSt.GetError()); + } + } + + if (!failure) + { + do + { + failure = true; + + std::size_t nLastFkpPos = nPN + nIMax - 1; + nLastFkpPos = nLastFkpPos << 9; + // number of FC entries of last Fkp + if (!checkSeek(rSt, nLastFkpPos + 511)) + break; + + sal_uInt8 nb(0); + rSt.ReadUChar( nb ); + // last FC entry of last Fkp + if (!checkSeek(rSt, nLastFkpPos + nb * 4)) + break; + + WW8_CP nFc(0); + rSt.ReadInt32( nFc ); + pPLCF_PosArray[nIMax] = nFc; // end of the last Fkp + + failure = bool(rSt.GetError()); + } while(false); + } + + if (!failure) + { + // Pointer to content array + pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&pPLCF_PosArray[nIMax + 1]); + sal_uInt8* p = pPLCF_Contents; + + for (sal_Int32 i = 0; i < ncpN; ++i) // construct PNs + { + ShortToSVBT16(static_cast<sal_uInt16>(nPN + i), p); + p += nStru; + } + } + + SAL_WARN_IF(failure, "sw.ww8", "Document has corrupt PLCF, ignoring it"); + + if (failure) + MakeFailedPLCF(); +} + +bool WW8PLCF::SeekPos(WW8_CP nPos) +{ + WW8_CP nP = nPos; + + if( nP < pPLCF_PosArray[0] ) + { + nIdx = 0; + // not found: nPos less than smallest entry + return false; + } + + // Search from beginning? + if ((nIdx < 1) || (nP < pPLCF_PosArray[nIdx - 1])) + nIdx = 1; + + sal_Int32 nI = nIdx; + sal_Int32 nEnd = nIMax; + + for(int n = (1==nIdx ? 1 : 2); n; --n ) + { + for( ; nI <=nEnd; ++nI) // search with an index that is incremented by 1 + { + if( nP < pPLCF_PosArray[nI] ) // found position + { + nIdx = nI - 1; // nI - 1 is the correct index + return true; // done + } + } + nI = 1; + nEnd = nIdx-1; + } + + nIdx = nIMax; // not found, greater than all entries + return false; +} + +bool WW8PLCF::Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const +{ + if ( nIdx >= nIMax ) + { + rStart = rEnd = WW8_CP_MAX; + return false; + } + rStart = pPLCF_PosArray[ nIdx ]; + rEnd = pPLCF_PosArray[ nIdx + 1 ]; + rpValue = static_cast<void*>(&pPLCF_Contents[nIdx * nStru]); + return true; +} + +WW8_CP WW8PLCF::Where() const +{ + if ( nIdx >= nIMax ) + return WW8_CP_MAX; + + return pPLCF_PosArray[nIdx]; +} + +WW8PLCFpcd::WW8PLCFpcd(SvStream* pSt, sal_uInt32 nFilePos, + sal_uInt32 nPLCF, sal_uInt32 nStruct) + : nStru( nStruct ) +{ + const sal_uInt32 nValidMin=4; + + sal_uInt64 const nOldPos = pSt->Tell(); + + bool bValid = checkSeek(*pSt, nFilePos); + std::size_t nRemainingSize = pSt->remainingSize(); + if( !(nRemainingSize >= nValidMin && nPLCF >= nValidMin )) + bValid = false; + nPLCF = bValid ? std::min(nRemainingSize, static_cast<std::size_t>(nPLCF)) : nValidMin; + + pPLCF_PosArray.reset( new sal_Int32[ ( nPLCF + 3 ) / 4 ] ); // Pointer to Pos-array + pPLCF_PosArray[0] = 0; + + nPLCF = bValid ? pSt->ReadBytes(pPLCF_PosArray.get(), nPLCF) : nValidMin; + nPLCF = std::max(nPLCF, nValidMin); + + nIMax = ( nPLCF - 4 ) / ( 4 + nStruct ); +#ifdef OSL_BIGENDIAN + for( long nI = 0; nI <= nIMax; nI++ ) + pPLCF_PosArray[nI] = OSL_SWAPDWORD( pPLCF_PosArray[nI] ); +#endif // OSL_BIGENDIAN + + // Pointer to content array + pPLCF_Contents = reinterpret_cast<sal_uInt8*>(&pPLCF_PosArray[nIMax + 1]); + + pSt->Seek( nOldPos ); +} + +// If nStartPos < 0, the first element of PLCFs will be taken +WW8PLCFpcd_Iter::WW8PLCFpcd_Iter( WW8PLCFpcd& rPLCFpcd, long nStartPos ) + :rPLCF( rPLCFpcd ), nIdx( 0 ) +{ + if( nStartPos >= 0 ) + SeekPos( nStartPos ); +} + +bool WW8PLCFpcd_Iter::SeekPos(long nPos) +{ + long nP = nPos; + + if( nP < rPLCF.pPLCF_PosArray[0] ) + { + nIdx = 0; + return false; // not found: nPos less than smallest entry + } + // Search from beginning? + if ((nIdx < 1) || (nP < rPLCF.pPLCF_PosArray[nIdx - 1])) + nIdx = 1; + + long nI = nIdx; + long nEnd = rPLCF.nIMax; + + for(int n = (1==nIdx ? 1 : 2); n; --n ) + { + for( ; nI <=nEnd; ++nI) + { // search with an index that is incremented by 1 + if( nP < rPLCF.pPLCF_PosArray[nI] ) + { // found position + nIdx = nI - 1; // nI - 1 is the correct index + return true; // done + } + } + nI = 1; + nEnd = nIdx-1; + } + nIdx = rPLCF.nIMax; // not found, greater than all entries + return false; +} + +bool WW8PLCFpcd_Iter::Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const +{ + if( nIdx >= rPLCF.nIMax ) + { + rStart = rEnd = WW8_CP_MAX; + return false; + } + rStart = rPLCF.pPLCF_PosArray[nIdx]; + rEnd = rPLCF.pPLCF_PosArray[nIdx + 1]; + rpValue = static_cast<void*>(&rPLCF.pPLCF_Contents[nIdx * rPLCF.nStru]); + return true; +} + +sal_Int32 WW8PLCFpcd_Iter::Where() const +{ + if ( nIdx >= rPLCF.nIMax ) + return SAL_MAX_INT32; + + return rPLCF.pPLCF_PosArray[nIdx]; +} + +bool WW8PLCFx_Fc_FKP::WW8Fkp::Entry::operator< + (const WW8PLCFx_Fc_FKP::WW8Fkp::Entry& rSecond) const +{ + return (mnFC < rSecond.mnFC); +} + +static bool IsReplaceAllSprm(sal_uInt16 nSpId) +{ + return (NS_sprm::LN_PHugePapx == nSpId || 0x6646 == nSpId); +} + +static bool IsExpandableSprm(sal_uInt16 nSpId) +{ + return 0x646B == nSpId; +} + +void WW8PLCFx_Fc_FKP::WW8Fkp::FillEntry(WW8PLCFx_Fc_FKP::WW8Fkp::Entry &rEntry, + std::size_t nDataOffset, sal_uInt16 nLen) +{ + bool bValidPos = (nDataOffset < sizeof(maRawData)); + + OSL_ENSURE(bValidPos, "sprm sequence offset is out of range, ignoring"); + + if (!bValidPos) + { + rEntry.mnLen = 0; + return; + } + + const sal_uInt16 nAvailableData = sizeof(maRawData)-nDataOffset; + OSL_ENSURE(nLen <= nAvailableData, "srpm sequence len is out of range, clipping"); + rEntry.mnLen = std::min(nLen, nAvailableData); + rEntry.mpData = maRawData + nDataOffset; +} + +WW8PLCFx_Fc_FKP::WW8Fkp::WW8Fkp(const WW8Fib& rFib, SvStream* pSt, + SvStream* pDataSt, long _nFilePos, long nItemSiz, ePLCFT ePl, + WW8_FC nStartFc) + : nItemSize(nItemSiz), nFilePos(_nFilePos), mnIdx(0), ePLCF(ePl) + , mnMustRemainCached(0), maSprmParser(rFib) +{ + memset(maRawData, 0, 512); + + const ww::WordVersion eVersion = rFib.GetFIBVersion(); + + sal_uInt64 const nOldPos = pSt->Tell(); + + bool bCouldSeek = checkSeek(*pSt, nFilePos); + bool bCouldRead = bCouldSeek && checkRead(*pSt, maRawData, 512); + + mnIMax = bCouldRead ? maRawData[511] : 0; + + sal_uInt8 *pStart = maRawData; + // Offset-Location in maRawData + const size_t nRawDataStart = (mnIMax + 1) * 4; + + for (mnIdx = 0; mnIdx < mnIMax; ++mnIdx) + { + const size_t nRawDataOffset = nRawDataStart + mnIdx * nItemSize; + + //clip to available data, corrupt fkp + if (nRawDataOffset >= 511) + { + mnIMax = mnIdx; + break; + } + + unsigned int nOfs = maRawData[nRawDataOffset] * 2; + // nOfs in [0..0xff*2=510] + + Entry aEntry(Get_Long(pStart)); + + if (nOfs) + { + switch (ePLCF) + { + case CHP: + { + aEntry.mnLen = maRawData[nOfs]; + + //len byte + std::size_t nDataOffset = nOfs + 1; + + FillEntry(aEntry, nDataOffset, aEntry.mnLen); + + if (aEntry.mnLen && eVersion <= ww::eWW2) + { + Word2CHPX aChpx = ReadWord2Chpx(*pSt, nFilePos + nOfs + 1, static_cast< sal_uInt8 >(aEntry.mnLen)); + std::vector<sal_uInt8> aSprms = ChpxToSprms(aChpx); + aEntry.mnLen = static_cast< sal_uInt16 >(aSprms.size()); + if (aEntry.mnLen) + { + aEntry.mpData = new sal_uInt8[aEntry.mnLen]; + memcpy(aEntry.mpData, aSprms.data(), aEntry.mnLen); + aEntry.mbMustDelete = true; + } + } + break; + } + case PAP: + { + sal_uInt8 nDelta = 0; + + aEntry.mnLen = maRawData[nOfs]; + if (IsEightPlus(eVersion) && !aEntry.mnLen) + { + aEntry.mnLen = maRawData[nOfs+1]; + nDelta++; + } + aEntry.mnLen *= 2; + + //stylecode, std/istd + if (eVersion <= ww::eWW2) + { + if (aEntry.mnLen >= 1) + { + aEntry.mnIStd = *(maRawData+nOfs+1+nDelta); + aEntry.mnLen--; //style code + if (aEntry.mnLen >= 6) + { + aEntry.mnLen-=6; //PHE + //skip stc, len byte + 6 byte PHE + unsigned int nOffset = nOfs + 8; + if (nOffset >= 511) //Bad offset + aEntry.mnLen=0; + if (aEntry.mnLen) //start is ok + { + if (nOffset + aEntry.mnLen > 512) //Bad end, clip + aEntry.mnLen = 512 - nOffset; + aEntry.mpData = maRawData + nOffset; + } + } + else + aEntry.mnLen=0; //Too short + } + } + else + { + if (aEntry.mnLen >= 2) + { + //len byte + optional extra len byte + std::size_t nDataOffset = nOfs + 1 + nDelta; + aEntry.mnIStd = nDataOffset <= sizeof(maRawData)-sizeof(aEntry.mnIStd) ? + SVBT16ToUInt16(maRawData+nDataOffset) : 0; + aEntry.mnLen-=2; //istd + if (aEntry.mnLen) + { + //additional istd + nDataOffset += sizeof(aEntry.mnIStd); + + FillEntry(aEntry, nDataOffset, aEntry.mnLen); + } + } + else + aEntry.mnLen=0; //Too short, ignore + } + + const sal_uInt16 nSpId = aEntry.mnLen + ? maSprmParser.GetSprmId(aEntry.mpData) : 0; + + /* + If we replace then we throw away the old data, if we + are expanding, then we tack the old data onto the end + of the new data + */ + const bool bExpand = IsExpandableSprm(nSpId); + const sal_uInt8* pStartData + = aEntry.mpData == nullptr ? nullptr : aEntry.mpData + 2; + const sal_uInt8* pLastValidDataPos = maRawData + 512 - sizeof(sal_uInt32); + if (pStartData != nullptr && pStartData > pLastValidDataPos) + pStartData = nullptr; + if ((IsReplaceAllSprm(nSpId) || bExpand) && pStartData) + { + sal_uInt32 nCurr = pDataSt->Tell(); + sal_uInt32 nPos = SVBT32ToUInt32(pStartData); + sal_uInt16 nLen(0); + + bool bOk = checkSeek(*pDataSt, nPos); + if (bOk) + { + pDataSt->ReadUInt16( nLen ); + bOk = nLen <= pDataSt->remainingSize(); + } + + if (bOk) + { + const sal_uInt16 nOrigLen = bExpand ? aEntry.mnLen : 0; + sal_uInt8 *pOrigData = bExpand ? aEntry.mpData : nullptr; + + aEntry.mnLen = nLen; + aEntry.mpData = + new sal_uInt8[aEntry.mnLen + nOrigLen]; + aEntry.mbMustDelete = true; + aEntry.mnLen = + pDataSt->ReadBytes(aEntry.mpData, aEntry.mnLen); + + pDataSt->Seek( nCurr ); + + if (pOrigData) + { + memcpy(aEntry.mpData + aEntry.mnLen, + pOrigData, nOrigLen); + aEntry.mnLen = aEntry.mnLen + nOrigLen; + } + } + } + } + break; + default: + OSL_FAIL("sweet god, what have you done!"); + break; + } + } + + maEntries.push_back(aEntry); + +#ifdef DEBUGSPRMREADER + { + sal_Int32 nLen; + sal_uInt8* pSprms = GetLenAndIStdAndSprms( nLen ); + WW8SprmIter aIter(pSprms, nLen, maSprmParser); + while (aIter.GetSprms()) + { + fprintf(stderr, "id is %x\n", aIter.GetCurrentId()); + aIter.advance(); + } + } +#endif + } + + //one more FC than grrpl entries + maEntries.emplace_back(Get_Long(pStart)); + + //we expect them sorted, but it appears possible for them to arrive unsorted + std::stable_sort(maEntries.begin(), maEntries.end()); + + mnIdx = 0; + + if (nStartFc >= 0) + SeekPos(nStartFc); + + pSt->Seek(nOldPos); +} + +WW8PLCFx_Fc_FKP::WW8Fkp::Entry::Entry(const Entry &rEntry) + : mnFC(rEntry.mnFC), mnLen(rEntry.mnLen), mnIStd(rEntry.mnIStd), + mbMustDelete(rEntry.mbMustDelete) +{ + if (mbMustDelete) + { + mpData = new sal_uInt8[mnLen]; + memcpy(mpData, rEntry.mpData, mnLen); + } + else + mpData = rEntry.mpData; +} + +WW8PLCFx_Fc_FKP::WW8Fkp::Entry& + WW8PLCFx_Fc_FKP::WW8Fkp::Entry::operator=(const Entry &rEntry) +{ + if (this == &rEntry) + return *this; + + if (mbMustDelete) + delete[] mpData; + + mnFC = rEntry.mnFC; + mnLen = rEntry.mnLen; + mnIStd = rEntry.mnIStd; + mbMustDelete = rEntry.mbMustDelete; + + if (rEntry.mbMustDelete) + { + mpData = new sal_uInt8[mnLen]; + memcpy(mpData, rEntry.mpData, mnLen); + } + else + mpData = rEntry.mpData; + + return *this; +} + +WW8PLCFx_Fc_FKP::WW8Fkp::Entry::~Entry() +{ + if (mbMustDelete) + delete[] mpData; +} + +void WW8PLCFx_Fc_FKP::WW8Fkp::Reset(WW8_FC nFc) +{ + SetIdx(0); + if (nFc >= 0) + SeekPos(nFc); +} + +bool WW8PLCFx_Fc_FKP::WW8Fkp::SeekPos(WW8_FC nFc) +{ + if (nFc < maEntries[0].mnFC) + { + mnIdx = 0; + return false; // not found: nPos less than smallest entry + } + + // Search from beginning? + if ((mnIdx < 1) || (nFc < maEntries[mnIdx - 1].mnFC)) + mnIdx = 1; + + sal_uInt8 nI = mnIdx; + sal_uInt8 nEnd = mnIMax; + + for(sal_uInt8 n = (1==mnIdx ? 1 : 2); n; --n ) + { + for( ; nI <=nEnd; ++nI) + { // search with an index that is incremented by 1 + if (nFc < maEntries[nI].mnFC) + { // found position + mnIdx = nI - 1; // nI - 1 is the correct index + return true; // done + } + } + nI = 1; + nEnd = mnIdx-1; + } + mnIdx = mnIMax; // not found, greater than all entries + return false; +} + +sal_uInt8* WW8PLCFx_Fc_FKP::WW8Fkp::Get(WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen) + const +{ + rLen = 0; + + if (mnIdx >= mnIMax) + { + rStart = WW8_FC_MAX; + return nullptr; + } + + rStart = maEntries[mnIdx].mnFC; + rEnd = maEntries[mnIdx + 1].mnFC; + + sal_uInt8* pSprms = GetLenAndIStdAndSprms( rLen ); + return pSprms; +} + +void WW8PLCFx_Fc_FKP::WW8Fkp::SetIdx(sal_uInt8 nI) +{ + if (nI < mnIMax) + { + mnIdx = nI; + } +} + +sal_uInt8* WW8PLCFx_Fc_FKP::WW8Fkp::GetLenAndIStdAndSprms(sal_Int32& rLen) const +{ + rLen = maEntries[mnIdx].mnLen; + return maEntries[mnIdx].mpData; +} + +SprmResult WW8PLCFx_Fc_FKP::WW8Fkp::HasSprm( sal_uInt16 nId, bool bFindFirst ) +{ + if (mnIdx >= mnIMax) + return SprmResult(); + + sal_Int32 nLen; + sal_uInt8* pSprms = GetLenAndIStdAndSprms( nLen ); + + WW8SprmIter aIter(pSprms, nLen, maSprmParser); + return aIter.FindSprm(nId, bFindFirst); +} + +void WW8PLCFx_Fc_FKP::WW8Fkp::HasSprm(sal_uInt16 nId, + std::vector<SprmResult> &rResult) +{ + if (mnIdx >= mnIMax) + return; + + sal_Int32 nLen; + sal_uInt8* pSprms = GetLenAndIStdAndSprms( nLen ); + + WW8SprmIter aIter(pSprms, nLen, maSprmParser); + + while(aIter.GetSprms()) + { + if (aIter.GetCurrentId() == nId) + { + sal_uInt16 nFixedLen = maSprmParser.DistanceToData(nId); + sal_uInt16 nL = maSprmParser.GetSprmSize(nId, aIter.GetSprms(), aIter.GetRemLen()); + rResult.emplace_back(aIter.GetCurrentParams(), nL - nFixedLen); + } + aIter.advance(); + }; +} + +ww::WordVersion WW8PLCFx::GetFIBVersion() const +{ + return mrFib.GetFIBVersion(); +} + +void WW8PLCFx::GetSprms( WW8PLCFxDesc* p ) +{ + OSL_ENSURE( false, "Called wrong GetSprms" ); + p->nStartPos = p->nEndPos = WW8_CP_MAX; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + p->bRealLineEnd = false; +} + +long WW8PLCFx::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) +{ + OSL_ENSURE( false, "Called wrong GetNoSprms" ); + rStart = rEnd = WW8_CP_MAX; + rLen = 0; + return 0; +} + +// ...Idx2: Default: ignore +sal_uInt32 WW8PLCFx::GetIdx2() const +{ + return 0; +} + +void WW8PLCFx::SetIdx2(sal_uInt32) +{ +} + +namespace { + +class SamePos +{ +private: + long mnPo; +public: + explicit SamePos(long nPo) : mnPo(nPo) {} + bool operator()(const std::unique_ptr<WW8PLCFx_Fc_FKP::WW8Fkp>& pFkp) + {return mnPo == pFkp->GetFilePos();} +}; + +} + +bool WW8PLCFx_Fc_FKP::NewFkp() +{ + WW8_CP nPLCFStart, nPLCFEnd; + void* pPage; + + static const int WW8FkpSizeTabVer2[ PLCF_END ] = + { + 1, 1, 0 /*, 0, 0, 0*/ + }; + static const int WW8FkpSizeTabVer6[ PLCF_END ] = + { + 1, 7, 0 /*, 0, 0, 0*/ + }; + static const int WW8FkpSizeTabVer8[ PLCF_END ] = + { + 1, 13, 0 /*, 0, 0, 0*/ + }; + const int* pFkpSizeTab; + + switch (GetFIBVersion()) + { + case ww::eWW1: + case ww::eWW2: + pFkpSizeTab = WW8FkpSizeTabVer2; + break; + case ww::eWW6: + case ww::eWW7: + pFkpSizeTab = WW8FkpSizeTabVer6; + break; + case ww::eWW8: + pFkpSizeTab = WW8FkpSizeTabVer8; + break; + default: + // program error! + OSL_ENSURE( false, "nVersion not implemented!" ); + return false; + } + + if (!pPLCF->Get( nPLCFStart, nPLCFEnd, pPage )) + { + pFkp = nullptr; + return false; // PLCF completely processed + } + pPLCF->advance(); + long nPo = SVBT16ToUInt16( static_cast<sal_uInt8 *>(pPage) ); + nPo <<= 9; // shift as LONG + + long nCurrentFkpFilePos = pFkp ? pFkp->GetFilePos() : -1; + if (nCurrentFkpFilePos == nPo) + pFkp->Reset(GetStartFc()); + else + { + auto aIter = + std::find_if(maFkpCache.begin(), maFkpCache.end(), SamePos(nPo)); + if (aIter != maFkpCache.end()) + { + pFkp = aIter->get(); + pFkp->Reset(GetStartFc()); + } + else + { + pFkp = new WW8Fkp(GetFIB(), pFKPStrm, pDataStrm, nPo, + pFkpSizeTab[ ePLCF ], ePLCF, GetStartFc()); + maFkpCache.push_back(std::unique_ptr<WW8Fkp>(pFkp)); + + if (maFkpCache.size() > eMaxCache) + { + WW8Fkp* pCachedFkp = maFkpCache.front().get(); + if (!pCachedFkp->IsMustRemainCache()) + { + maFkpCache.pop_front(); + } + } + } + } + + SetStartFc( -1 ); // only the first time + return true; +} + +WW8PLCFx_Fc_FKP::WW8PLCFx_Fc_FKP(SvStream* pSt, SvStream* pTableSt, + SvStream* pDataSt, const WW8Fib& rFib, ePLCFT ePl, WW8_FC nStartFcL) + : WW8PLCFx(rFib, true), pFKPStrm(pSt), pDataStrm(pDataSt) + , pFkp(nullptr), ePLCF(ePl) +{ + SetStartFc(nStartFcL); + long nLenStruct = (8 > rFib.m_nVersion) ? 2 : 4; + if (ePl == CHP) + { + pPLCF.reset(new WW8PLCF(*pTableSt, rFib.m_fcPlcfbteChpx, rFib.m_lcbPlcfbteChpx, + nLenStruct, GetStartFc(), rFib.m_pnChpFirst, rFib.m_cpnBteChp)); + } + else + { + pPLCF.reset(new WW8PLCF(*pTableSt, rFib.m_fcPlcfbtePapx, rFib.m_lcbPlcfbtePapx, + nLenStruct, GetStartFc(), rFib.m_pnPapFirst, rFib.m_cpnBtePap)); + } +} + +WW8PLCFx_Fc_FKP::~WW8PLCFx_Fc_FKP() +{ + maFkpCache.clear(); + pPLCF.reset(); + pPCDAttrs.reset(); +} + +sal_uInt32 WW8PLCFx_Fc_FKP::GetIdx() const +{ + sal_uInt32 u = pPLCF->GetIdx() << 8; + if (pFkp) + u |= pFkp->GetIdx(); + return u; +} + +void WW8PLCFx_Fc_FKP::SetIdx(sal_uInt32 nIdx) +{ + if( !( nIdx & 0xffffff00L ) ) + { + pPLCF->SetIdx( nIdx >> 8 ); + pFkp = nullptr; + } + else + { // there was a Fkp + // Set PLCF one position back to retrieve the address of the Fkp + pPLCF->SetIdx( ( nIdx >> 8 ) - 1 ); + if (NewFkp()) // read Fkp again + { + sal_uInt8 nFkpIdx = static_cast<sal_uInt8>(nIdx & 0xff); + pFkp->SetIdx(nFkpIdx); // set Fkp-Pos again + } + } +} + +bool WW8PLCFx_Fc_FKP::SeekPos(WW8_FC nFcPos) +{ + // StartPos for next Where() + SetStartFc( nFcPos ); + + // find StartPos for next pPLCF->Get() + bool bRet = pPLCF->SeekPos(nFcPos); + + // make FKP invalid? + WW8_CP nPLCFStart, nPLCFEnd; + void* pPage; + if( pFkp && pPLCF->Get( nPLCFStart, nPLCFEnd, pPage ) ) + { + long nPo = SVBT16ToUInt16( static_cast<sal_uInt8 *>(pPage) ); + nPo <<= 9; // shift as LONG + if (nPo != pFkp->GetFilePos()) + pFkp = nullptr; + else + pFkp->SeekPos( nFcPos ); + } + return bRet; +} + +WW8_FC WW8PLCFx_Fc_FKP::Where() +{ + if( !pFkp && !NewFkp() ) + return WW8_FC_MAX; + WW8_FC nP = pFkp ? pFkp->Where() : WW8_FC_MAX; + if( nP != WW8_FC_MAX ) + return nP; + + pFkp = nullptr; // FKP finished -> get new + return Where(); // easiest way: do it recursively +} + +sal_uInt8* WW8PLCFx_Fc_FKP::GetSprmsAndPos(WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen) +{ + rLen = 0; // Default + rStart = rEnd = WW8_FC_MAX; + + if( !pFkp ) // Fkp not there ? + { + if( !NewFkp() ) + return nullptr; + } + + sal_uInt8* pPos = pFkp ? pFkp->Get( rStart, rEnd, rLen ) : nullptr; + if( rStart == WW8_FC_MAX ) //Not found + return nullptr; + return pPos; +} + +void WW8PLCFx_Fc_FKP::advance() +{ + if( !pFkp && !NewFkp() ) + return; + + if (!pFkp) + return; + + pFkp->advance(); + if( pFkp->Where() == WW8_FC_MAX ) + (void)NewFkp(); +} + +sal_uInt16 WW8PLCFx_Fc_FKP::GetIstd() const +{ + return pFkp ? pFkp->GetIstd() : 0xFFFF; +} + +void WW8PLCFx_Fc_FKP::GetPCDSprms( WW8PLCFxDesc& rDesc ) +{ + rDesc.pMemPos = nullptr; + rDesc.nSprmsLen = 0; + if( pPCDAttrs ) + { + if( !pFkp ) + { + OSL_FAIL("+Problem: GetPCDSprms: NewFkp necessary (not possible!)" ); + if( !NewFkp() ) + return; + } + pPCDAttrs->GetSprms(&rDesc); + } +} + +SprmResult WW8PLCFx_Fc_FKP::HasSprm(sal_uInt16 nId, bool bFindFirst) +{ + // const would be nicer, but for that, NewFkp() would need to be replaced or eliminated + if( !pFkp ) + { + OSL_FAIL( "+Motz: HasSprm: NewFkp needed ( no const possible )" ); + // happens in BugDoc 31722 + if( !NewFkp() ) + return SprmResult(); + } + + if (!pFkp) + return SprmResult(); + + SprmResult aRes = pFkp->HasSprm(nId, bFindFirst); + + if (!aRes.pSprm) + { + WW8PLCFxDesc aDesc; + GetPCDSprms( aDesc ); + + if (aDesc.pMemPos) + { + WW8SprmIter aIter(aDesc.pMemPos, aDesc.nSprmsLen, + pFkp->GetSprmParser()); + aRes = aIter.FindSprm(nId, bFindFirst); + } + } + + return aRes; +} + +void WW8PLCFx_Fc_FKP::HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult) +{ + // const would be nicer, but for that, NewFkp() would need to be replaced or eliminated + if (!pFkp) + { + OSL_FAIL( "+Motz: HasSprm: NewFkp needed ( no const possible )" ); + // happens in BugDoc 31722 + if( !NewFkp() ) + return; + } + + if (!pFkp) + return; + + pFkp->HasSprm(nId, rResult); + + WW8PLCFxDesc aDesc; + GetPCDSprms( aDesc ); + + if (aDesc.pMemPos) + { + const wwSprmParser &rSprmParser = pFkp->GetSprmParser(); + WW8SprmIter aIter(aDesc.pMemPos, aDesc.nSprmsLen, rSprmParser); + while(aIter.GetSprms()) + { + if (aIter.GetCurrentId() == nId) + { + sal_uInt16 nFixedLen = rSprmParser.DistanceToData(nId); + sal_uInt16 nL = rSprmParser.GetSprmSize(nId, aIter.GetSprms(), aIter.GetRemLen()); + rResult.emplace_back(aIter.GetCurrentParams(), nL - nFixedLen); + } + aIter.advance(); + }; + } +} + +WW8PLCFx_Cp_FKP::WW8PLCFx_Cp_FKP( SvStream* pSt, SvStream* pTableSt, + SvStream* pDataSt, const WW8ScannerBase& rBase, ePLCFT ePl ) + : WW8PLCFx_Fc_FKP(pSt, pTableSt, pDataSt, *rBase.m_pWw8Fib, ePl, + rBase.WW8Cp2Fc(0)), rSBase(rBase), nAttrStart(-1), nAttrEnd(-1), + bLineEnd(false), + bComplex( (7 < rBase.m_pWw8Fib->m_nVersion) || rBase.m_pWw8Fib->m_fComplex ) +{ + ResetAttrStartEnd(); + + if (rSBase.m_pPiecePLCF) + pPcd.reset( new WW8PLCFx_PCD(GetFIB(), rBase.m_pPiecePLCF.get(), 0, IsSevenMinus(GetFIBVersion())) ); + + /* + Make a copy of the piece attributes for so that the calls to HasSprm on a + Fc_FKP will be able to take into account the current piece attributes, + despite the fact that such attributes can only be found through a cp based + mechanism. + */ + if (pPcd) + { + pPCDAttrs.reset( rSBase.m_pPLCFx_PCDAttrs ? new WW8PLCFx_PCDAttrs( + *rSBase.m_pWw8Fib, pPcd.get(), &rSBase) : nullptr); + } + + pPieceIter = rSBase.m_pPieceIter.get(); +} + +WW8PLCFx_Cp_FKP::~WW8PLCFx_Cp_FKP() +{ +} + +void WW8PLCFx_Cp_FKP::ResetAttrStartEnd() +{ + nAttrStart = -1; + nAttrEnd = -1; + bLineEnd = false; +} + +sal_uInt32 WW8PLCFx_Cp_FKP::GetPCDIdx() const +{ + return pPcd ? pPcd->GetIdx() : 0; +} + +bool WW8PLCFx_Cp_FKP::SeekPos(WW8_CP nCpPos) +{ + if( pPcd ) // Complex + { + if( !pPcd->SeekPos( nCpPos ) ) // set piece + return false; + if (pPCDAttrs && !pPCDAttrs->GetIter()->SeekPos(nCpPos)) + return false; + return WW8PLCFx_Fc_FKP::SeekPos(pPcd->CurrentPieceStartCp2Fc(nCpPos)); + } + // NO piece table !!! + return WW8PLCFx_Fc_FKP::SeekPos( rSBase.WW8Cp2Fc(nCpPos) ); +} + +WW8_CP WW8PLCFx_Cp_FKP::Where() +{ + WW8_FC nFc = WW8PLCFx_Fc_FKP::Where(); + if( pPcd ) + return pPcd->CurrentPieceStartFc2Cp( nFc ); // identify piece + return rSBase.WW8Fc2Cp( nFc ); // NO piece table !!! +} + +void WW8PLCFx_Cp_FKP::GetSprms(WW8PLCFxDesc* p) +{ + WW8_CP nOrigCp = p->nStartPos; + + if (!GetDirty()) //Normal case + { + p->pMemPos = WW8PLCFx_Fc_FKP::GetSprmsAndPos(p->nStartPos, p->nEndPos, + p->nSprmsLen); + } + else + { + /* + For the odd case where we have a location in a fastsaved file which + does not have an entry in the FKP, perhaps its para end is in the next + piece, or perhaps the cp just doesn't exist at all in this document. + AdvSprm doesn't know so it sets the PLCF as dirty and we figure out + in this method what the situation is + + It doesn't exist then the piece iterator will not be able to find it. + Otherwise our cool fastsave algorithm can be brought to bear on the + problem. + */ + if( !pPieceIter ) + return; + const sal_uInt32 nOldPos = pPieceIter->GetIdx(); + bool bOk = pPieceIter->SeekPos(nOrigCp); + pPieceIter->SetIdx(nOldPos); + if (!bOk) + return; + } + + if( pPcd ) // piece table available + { + // Init ( no ++ called, yet ) + if( (nAttrStart > nAttrEnd) || (nAttrStart == -1) ) + { + p->bRealLineEnd = (ePLCF == PAP); + + if ( ((ePLCF == PAP ) || (ePLCF == CHP)) && (nOrigCp != WW8_CP_MAX) ) + { + bool bIsUnicode=false; + /* + To find the end of a paragraph for a character in a + complex format file. + + It is necessary to know the piece that contains the + character and the FC assigned to the character. + */ + + //We set the piece iterator to the piece that contains the + //character, now we have the correct piece for this character + sal_uInt32 nOldPos = pPieceIter->GetIdx(); + p->nStartPos = nOrigCp; + pPieceIter->SeekPos( p->nStartPos); + + //This is the FC assigned to the character, but we already + //have the result of the next stage, so we can skip this step + //WW8_FC nStartFc = rSBase.WW8Cp2Fc(p->nStartPos, &bIsUnicode); + + /* + Using the FC of the character, first search the FKP that + describes the character to find the smallest FC in the rgfc + that is larger than the character FC. + */ + //But the search has already been done, the next largest FC is + //p->nEndPos. + WW8_FC nOldEndPos = p->nEndPos; + + /* + If the FC found in the FKP is less than or equal to the limit + FC of the piece, the end of the paragraph that contains the + character is at the FKP FC minus 1. + */ + WW8_CP nCpStart, nCpEnd; + void* pData=nullptr; + bool bOk = pPieceIter->Get(nCpStart, nCpEnd, pData); + + if (!bOk) + { + pPieceIter->SetIdx(nOldPos); + return; + } + + WW8_FC nLimitFC = SVBT32ToUInt32( static_cast<WW8_PCD*>(pData)->fc ); + WW8_FC nBeginLimitFC = nLimitFC; + if (IsEightPlus(GetFIBVersion())) + { + nBeginLimitFC = + WW8PLCFx_PCD::TransformPieceAddress(nLimitFC, + bIsUnicode); + } + + WW8_CP nCpLen; + bool bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + pPieceIter->SetIdx(nOldPos); + return; + } + + if (bIsUnicode) + { + bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + pPieceIter->SetIdx(nOldPos); + return; + } + } + + bFail = o3tl::checked_add(nBeginLimitFC, nCpLen, nLimitFC); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + pPieceIter->SetIdx(nOldPos); + return; + } + + if (nOldEndPos <= nLimitFC) + { + bFail = o3tl::checked_sub(nLimitFC, nOldEndPos, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + pPieceIter->SetIdx(nOldPos); + return; + } + + nCpLen /= (bIsUnicode ? 2 : 1); + + bFail = o3tl::checked_sub(nCpEnd, nCpLen, p->nEndPos); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + pPieceIter->SetIdx(nOldPos); + return; + } + } + else + { + if (ePLCF == CHP) + p->nEndPos = nCpEnd; + else + { + /* + If the FKP FC that was found was greater than the FC + of the end of the piece, scan piece by piece toward + the end of the document until a piece is found that + contains a paragraph end mark. + */ + + /* + It's possible to check if a piece contains a paragraph + mark by using the FC of the beginning of the piece to + search in the FKPs for the smallest FC in the FKP rgfc + that is greater than the FC of the beginning of the + piece. If the FC found is less than or equal to the + limit FC of the piece, then the character that ends + the paragraph is the character immediately before the + FKP fc + */ + + pPieceIter->advance(); + + for (;pPieceIter->GetIdx() < pPieceIter->GetIMax(); + pPieceIter->advance()) + { + if( !pPieceIter->Get( nCpStart, nCpEnd, pData ) ) + { + OSL_ENSURE( false, "piece iter broken!" ); + break; + } + bIsUnicode = false; + sal_Int32 nFcStart=SVBT32ToUInt32(static_cast<WW8_PCD*>(pData)->fc); + + if (IsEightPlus(GetFIBVersion())) + { + nFcStart = + WW8PLCFx_PCD::TransformPieceAddress( + nFcStart,bIsUnicode ); + } + + bFail = o3tl::checked_sub(nCpEnd, nCpStart, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + continue; + } + + if (bIsUnicode) + { + bFail = o3tl::checked_multiply<WW8_CP>(nCpLen, 2, nCpLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + continue; + } + } + + bFail = o3tl::checked_add(nFcStart, nCpLen, nLimitFC); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + continue; + } + + //if it doesn't exist, skip it + if (!SeekPos(nCpStart)) + continue; + + WW8_FC nOne,nSmallest; + p->pMemPos = WW8PLCFx_Fc_FKP::GetSprmsAndPos(nOne, + nSmallest, p->nSprmsLen); + + if (nSmallest <= nLimitFC) + { + WW8_CP nCpDiff; + bFail = o3tl::checked_sub(nLimitFC, nSmallest, nCpDiff); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + continue; + } + if (bIsUnicode) + nCpDiff /= 2; + + WW8_CP nEndPos; + bFail = o3tl::checked_sub(nCpEnd, nCpDiff, nEndPos); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nStartPos = p->nEndPos = WW8_FC_MAX; + continue; + } + + OSL_ENSURE(nEndPos >= p->nStartPos, "EndPos before StartPos"); + + if (nEndPos >= p->nStartPos) + p->nEndPos = nEndPos; + + break; + } + } + } + } + pPieceIter->SetIdx( nOldPos ); + } + else + WW8PLCFx_PCD::CurrentPieceFc2Cp( p->nStartPos, p->nEndPos,&rSBase ); + } + else + { + p->nStartPos = nAttrStart; + p->nEndPos = nAttrEnd; + p->bRealLineEnd = bLineEnd; + } + } + else // NO piece table !!! + { + p->nStartPos = rSBase.WW8Fc2Cp( p->nStartPos ); + p->nEndPos = rSBase.WW8Fc2Cp( p->nEndPos ); + p->bRealLineEnd = ePLCF == PAP; + } +} + +void WW8PLCFx_Cp_FKP::advance() +{ + WW8PLCFx_Fc_FKP::advance(); + // !pPcd: emergency break + if ( !bComplex || !pPcd ) + return; + + if( GetPCDIdx() >= pPcd->GetIMax() ) // End of PLCF + { + nAttrStart = nAttrEnd = WW8_CP_MAX; + return; + } + + sal_Int32 nFkpLen; // Fkp entry + // get Fkp entry + WW8PLCFx_Fc_FKP::GetSprmsAndPos(nAttrStart, nAttrEnd, nFkpLen); + + WW8PLCFx_PCD::CurrentPieceFc2Cp( nAttrStart, nAttrEnd, &rSBase ); + bLineEnd = (ePLCF == PAP); +} + +WW8PLCFx_SEPX::WW8PLCFx_SEPX(SvStream* pSt, SvStream* pTableSt, + const WW8Fib& rFib, WW8_CP nStartCp) + : WW8PLCFx(rFib, true), maSprmParser(rFib), + pStrm(pSt), nArrMax(256), nSprmSiz(0) +{ + if (rFib.m_lcbPlcfsed) + pPLCF.reset( new WW8PLCF(*pTableSt, rFib.m_fcPlcfsed, rFib.m_lcbPlcfsed, + GetFIBVersion() <= ww::eWW2 ? 6 : 12, nStartCp) ); + + pSprms.reset( new sal_uInt8[nArrMax] ); // maximum length +} + +WW8PLCFx_SEPX::~WW8PLCFx_SEPX() +{ +} + +sal_uInt32 WW8PLCFx_SEPX::GetIdx() const +{ + return pPLCF ? pPLCF->GetIdx() : 0; +} + +void WW8PLCFx_SEPX::SetIdx(sal_uInt32 nIdx) +{ + if( pPLCF ) pPLCF->SetIdx( nIdx ); +} + +bool WW8PLCFx_SEPX::SeekPos(WW8_CP nCpPos) +{ + return pPLCF && pPLCF->SeekPos( nCpPos ); +} + +WW8_CP WW8PLCFx_SEPX::Where() +{ + return pPLCF ? pPLCF->Where() : 0; +} + +void WW8PLCFx_SEPX::GetSprms(WW8PLCFxDesc* p) +{ + if( !pPLCF ) return; + + void* pData; + + p->bRealLineEnd = false; + if (!pPLCF->Get( p->nStartPos, p->nEndPos, pData )) + { + p->nStartPos = p->nEndPos = WW8_CP_MAX; // PLCF completely processed + p->pMemPos = nullptr; + p->nSprmsLen = 0; + } + else + { + sal_uInt32 nPo = SVBT32ToUInt32( static_cast<sal_uInt8*>(pData)+2 ); + if (nPo == 0xFFFFFFFF || !checkSeek(*pStrm, nPo)) + { + p->nStartPos = p->nEndPos = WW8_CP_MAX; // Sepx empty + p->pMemPos = nullptr; + p->nSprmsLen = 0; + } + else + { + // read len + if (GetFIBVersion() <= ww::eWW2) // eWW6 ?, docs say yes, but... + { + sal_uInt8 nSiz(0); + pStrm->ReadUChar( nSiz ); + nSprmSiz = nSiz; + } + else + { + pStrm->ReadUInt16( nSprmSiz ); + } + + std::size_t nRemaining = pStrm->remainingSize(); + if (nSprmSiz > nRemaining) + nSprmSiz = nRemaining; + + if( nSprmSiz > nArrMax ) + { // does not fit + nArrMax = nSprmSiz; // Get more memory + pSprms.reset( new sal_uInt8[nArrMax] ); + } + nSprmSiz = pStrm->ReadBytes(pSprms.get(), nSprmSiz); // read Sprms + + p->nSprmsLen = nSprmSiz; + p->pMemPos = pSprms.get(); // return Position + } + } +} + +void WW8PLCFx_SEPX::advance() +{ + if (pPLCF) + pPLCF->advance(); +} + +SprmResult WW8PLCFx_SEPX::HasSprm(sal_uInt16 nId) const +{ + return HasSprm(nId, pSprms.get(), nSprmSiz); +} + +SprmResult WW8PLCFx_SEPX::HasSprm( sal_uInt16 nId, const sal_uInt8* pOtherSprms, + long nOtherSprmSiz ) const +{ + SprmResult aRet; + if (pPLCF) + { + WW8SprmIter aIter(pOtherSprms, nOtherSprmSiz, maSprmParser); + aRet = aIter.FindSprm(nId, /*bFindFirst=*/true); + } + return aRet; +} + +bool WW8PLCFx_SEPX::Find4Sprms(sal_uInt16 nId1,sal_uInt16 nId2,sal_uInt16 nId3,sal_uInt16 nId4, + SprmResult& r1, SprmResult& r2, SprmResult& r3, SprmResult& r4) const +{ + if( !pPLCF ) + return false; + + bool bFound = false; + + sal_uInt8* pSp = pSprms.get(); + size_t i = 0; + while (i + maSprmParser.MinSprmLen() <= nSprmSiz) + { + // Sprm found? + const sal_uInt16 nCurrentId = maSprmParser.GetSprmId(pSp); + sal_Int32 nRemLen = nSprmSiz - i; + const sal_uInt16 x = maSprmParser.GetSprmSize(nCurrentId, pSp, nRemLen); + bool bValid = x <= nRemLen; + if (!bValid) + { + SAL_WARN("sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong"); + break; + } + bool bOk = true; + if( nCurrentId == nId1 ) + { + sal_uInt16 nFixedLen = maSprmParser.DistanceToData(nId1); + r1 = SprmResult(pSp + nFixedLen, x - nFixedLen); + } + else if( nCurrentId == nId2 ) + { + sal_uInt16 nFixedLen = maSprmParser.DistanceToData(nId2); + r2 = SprmResult(pSp + nFixedLen, x - nFixedLen); + } + else if( nCurrentId == nId3 ) + { + sal_uInt16 nFixedLen = maSprmParser.DistanceToData(nId3); + r3 = SprmResult(pSp + nFixedLen, x - nFixedLen); + } + else if( nCurrentId == nId4 ) + { + sal_uInt16 nFixedLen = maSprmParser.DistanceToData(nId4); + r4 = SprmResult(pSp + nFixedLen, x - nFixedLen); + } + else + bOk = false; + bFound |= bOk; + // increment pointer so that it points to next SPRM + i += x; + pSp += x; + } + return bFound; +} + +SprmResult WW8PLCFx_SEPX::HasSprm( sal_uInt16 nId, sal_uInt8 n2nd ) const +{ + SprmResult aRet; + if (pPLCF) + { + WW8SprmIter aIter(pSprms.get(), nSprmSiz, maSprmParser); + aRet = aIter.FindSprm(nId, /*bFindFirst=*/true, &n2nd); + } + return aRet; +} + +WW8PLCFx_SubDoc::WW8PLCFx_SubDoc(SvStream* pSt, const WW8Fib& rFib, + WW8_CP nStartCp, long nFcRef, long nLenRef, long nFcText, long nLenText, + long nStruct) + : WW8PLCFx(rFib, true) +{ + if( nLenRef && nLenText ) + { + pRef.reset(new WW8PLCF(*pSt, nFcRef, nLenRef, nStruct, nStartCp)); + pText.reset(new WW8PLCF(*pSt, nFcText, nLenText, 0, nStartCp)); + } +} + +WW8PLCFx_SubDoc::~WW8PLCFx_SubDoc() +{ + pRef.reset(); + pText.reset(); +} + +sal_uInt32 WW8PLCFx_SubDoc::GetIdx() const +{ + // Probably pText ... no need for it + if( pRef ) + return ( pRef->GetIdx() << 16 | pText->GetIdx() ); + return 0; +} + +void WW8PLCFx_SubDoc::SetIdx(sal_uInt32 nIdx) +{ + if( pRef ) + { + pRef->SetIdx( nIdx >> 16 ); + // Probably pText ... no need for it + pText->SetIdx( nIdx & 0xFFFF ); + } +} + +bool WW8PLCFx_SubDoc::SeekPos( WW8_CP nCpPos ) +{ + return pRef && pRef->SeekPos( nCpPos ); +} + +WW8_CP WW8PLCFx_SubDoc::Where() +{ + return pRef ? pRef->Where() : WW8_CP_MAX; +} + +void WW8PLCFx_SubDoc::GetSprms(WW8PLCFxDesc* p) +{ + p->nStartPos = p->nEndPos = WW8_CP_MAX; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + p->bRealLineEnd = false; + + if (!pRef) + return; + + sal_uInt32 nNr = pRef->GetIdx(); + + void *pData; + WW8_CP nFoo; + if (!pRef->Get(p->nStartPos, nFoo, pData)) + { + p->nEndPos = p->nStartPos = WW8_CP_MAX; + return; + } + + if (o3tl::checked_add<WW8_CP>(p->nStartPos, 1, p->nEndPos)) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + p->nEndPos = p->nStartPos = WW8_CP_MAX; + return; + } + + if (!pText) + return; + + pText->SetIdx(nNr); + + if (!pText->Get(p->nCp2OrIdx, p->nSprmsLen, pData)) + { + p->nEndPos = p->nStartPos = WW8_CP_MAX; + p->nSprmsLen = 0; + return; + } + + if (p->nCp2OrIdx < 0 || p->nCp2OrIdx > p->nSprmsLen) + { + SAL_WARN("sw.ww8", "Document has invalid Cp or Idx, ignoring it"); + p->nEndPos = p->nStartPos = WW8_CP_MAX; + p->nSprmsLen = 0; + return; + } + + p->nSprmsLen -= p->nCp2OrIdx; +} + +void WW8PLCFx_SubDoc::advance() +{ + if (pRef && pText) + { + pRef->advance(); + pText->advance(); + } +} + +// fields +WW8PLCFx_FLD::WW8PLCFx_FLD( SvStream* pSt, const WW8Fib& rMyFib, short nType) + : WW8PLCFx(rMyFib, true), rFib(rMyFib) +{ + WW8_FC nFc; + sal_Int32 nLen; + + switch( nType ) + { + case MAN_HDFT: + nFc = rFib.m_fcPlcffldHdr; + nLen = rFib.m_lcbPlcffldHdr; + break; + case MAN_FTN: + nFc = rFib.m_fcPlcffldFootnote; + nLen = rFib.m_lcbPlcffldFootnote; + break; + case MAN_EDN: + nFc = rFib.m_fcPlcffldEdn; + nLen = rFib.m_lcbPlcffldEdn; + break; + case MAN_AND: + nFc = rFib.m_fcPlcffldAtn; + nLen = rFib.m_lcbPlcffldAtn; + break; + case MAN_TXBX: + nFc = rFib.m_fcPlcffldTxbx; + nLen = rFib.m_lcbPlcffldTxbx; + break; + case MAN_TXBX_HDFT: + nFc = rFib.m_fcPlcffldHdrTxbx; + nLen = rFib.m_lcbPlcffldHdrTxbx; + break; + default: + nFc = rFib.m_fcPlcffldMom; + nLen = rFib.m_lcbPlcffldMom; + break; + } + + if( nLen ) + pPLCF.reset( new WW8PLCFspecial( pSt, nFc, nLen, 2 ) ); +} + +WW8PLCFx_FLD::~WW8PLCFx_FLD() +{ +} + +sal_uInt32 WW8PLCFx_FLD::GetIdx() const +{ + return pPLCF ? pPLCF->GetIdx() : 0; +} + +void WW8PLCFx_FLD::SetIdx(sal_uInt32 nIdx) +{ + if( pPLCF ) + pPLCF->SetIdx( nIdx ); +} + +bool WW8PLCFx_FLD::SeekPos(WW8_CP nCpPos) +{ + return pPLCF && pPLCF->SeekPosExact( nCpPos ); +} + +WW8_CP WW8PLCFx_FLD::Where() +{ + return pPLCF ? pPLCF->Where() : WW8_CP_MAX; +} + +bool WW8PLCFx_FLD::StartPosIsFieldStart() +{ + void* pData; + sal_Int32 nTest; + return pPLCF && pPLCF->Get(nTest, pData) && ((static_cast<sal_uInt8*>(pData)[0] & 0x1f) == 0x13); +} + +bool WW8PLCFx_FLD::EndPosIsFieldEnd(WW8_CP& nCP) +{ + bool bRet = false; + + if (pPLCF) + { + long n = pPLCF->GetIdx(); + + pPLCF->advance(); + + void* pData; + sal_Int32 nTest; + if ( pPLCF->Get(nTest, pData) && ((static_cast<sal_uInt8*>(pData)[0] & 0x1f) == 0x15) ) + { + nCP = nTest; + bRet = true; + } + + pPLCF->SetIdx(n); + } + + return bRet; +} + +void WW8PLCFx_FLD::GetSprms(WW8PLCFxDesc* p) +{ + p->nStartPos = p->nEndPos = WW8_CP_MAX; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + p->bRealLineEnd = false; + + if (!pPLCF) + { + p->nStartPos = WW8_CP_MAX; // there are no fields + return; + } + + long n = pPLCF->GetIdx(); + + sal_Int32 nP; + void *pData; + if (!pPLCF->Get(nP, pData)) // end of PLCFspecial? + { + p->nStartPos = WW8_CP_MAX; // PLCF completely processed + return; + } + + p->nStartPos = nP; + + pPLCF->advance(); + if (!pPLCF->Get(nP, pData)) // end of PLCFspecial? + { + p->nStartPos = WW8_CP_MAX; // PLCF completely processed + return; + } + + p->nEndPos = nP; + + pPLCF->SetIdx(n); + + p->nCp2OrIdx = pPLCF->GetIdx(); +} + +void WW8PLCFx_FLD::advance() +{ + SAL_WARN_IF(!pPLCF, "sw.ww8", "Call without PLCFspecial field"); + if( !pPLCF ) + return; + pPLCF->advance(); +} + +bool WW8PLCFx_FLD::GetPara(long nIdx, WW8FieldDesc& rF) +{ + SAL_WARN_IF(!pPLCF, "sw.ww8", "Call without PLCFspecial field"); + if( !pPLCF ) + return false; + + long n = pPLCF->GetIdx(); + pPLCF->SetIdx(nIdx); + + bool bOk = WW8GetFieldPara(*pPLCF, rF); + + pPLCF->SetIdx(n); + return bOk; +} + +// WW8PLCF_Book +void WW8ReadSTTBF(bool bVer8, SvStream& rStrm, sal_uInt32 nStart, sal_Int32 nLen, + sal_uInt16 nExtraLen, rtl_TextEncoding eCS, std::vector<OUString> &rArray, + std::vector<ww::bytes>* pExtraArray, std::vector<OUString>* pValueArray) +{ + if (nLen==0) // Handle Empty STTBF + return; + + sal_uInt64 const nOldPos = rStrm.Tell(); + if (checkSeek(rStrm, nStart)) + { + sal_uInt16 nLen2(0); + rStrm.ReadUInt16( nLen2 ); // bVer67: total length of structure + // bVer8 : count of strings + + if( bVer8 ) + { + sal_uInt16 nStrings(0); + bool bUnicode = (0xFFFF == nLen2); + if (bUnicode) + rStrm.ReadUInt16( nStrings ); + else + nStrings = nLen2; + + rStrm.ReadUInt16( nExtraLen ); + + const size_t nMinStringLen = bUnicode ? sizeof(sal_uInt16) : sizeof(sal_uInt8); + const size_t nMinRecordSize = nExtraLen + nMinStringLen; + const size_t nMaxPossibleStrings = rStrm.remainingSize() / nMinRecordSize; + if (nStrings > nMaxPossibleStrings) + { + SAL_WARN("sw.ww8", "STTBF claims " << nStrings << " entries, but only " << nMaxPossibleStrings << " are possible"); + nStrings = nMaxPossibleStrings; + } + + if (nExtraLen && nStrings) + { + const size_t nMaxExtraLen = (rStrm.remainingSize() - (nStrings * nMinStringLen)) / nStrings; + if (nExtraLen > nMaxExtraLen) + { + SAL_WARN("sw.ww8", "STTBF claims " << nMaxExtraLen << " extra len, but only " << nMaxExtraLen << " are possible"); + nExtraLen = nMaxExtraLen; + } + } + + for (sal_uInt16 i=0; i < nStrings; ++i) + { + if (bUnicode) + rArray.push_back(read_uInt16_PascalString(rStrm)); + else + { + OString aTmp = read_uInt8_lenPrefixed_uInt8s_ToOString(rStrm); + rArray.push_back(OStringToOUString(aTmp, eCS)); + } + + // Skip the extra data + if (nExtraLen) + { + if (pExtraArray) + { + ww::bytes extraData(nExtraLen); + rStrm.ReadBytes(extraData.data(), nExtraLen); + pExtraArray->push_back(extraData); + } + else + rStrm.SeekRel( nExtraLen ); + } + } + // read the value of the document variables, if requested. + if (pValueArray) + { + for (sal_uInt16 i=0; i < nStrings; ++i) + { + if( bUnicode ) + pValueArray->push_back(read_uInt16_PascalString(rStrm)); + else + { + OString aTmp = read_uInt8_lenPrefixed_uInt8s_ToOString(rStrm); + pValueArray->push_back(OStringToOUString(aTmp, eCS)); + } + } + } + } + else + { + if( nLen2 != nLen ) + { + OSL_ENSURE(nLen2 == nLen, + "Fib length and read length are different"); + if (nLen > SAL_MAX_UINT16) + nLen = SAL_MAX_UINT16; + else if (nLen < 2 ) + nLen = 2; + nLen2 = static_cast<sal_uInt16>(nLen); + } + sal_uLong nRead = 0; + for( nLen2 -= 2; nRead < nLen2; ) + { + sal_uInt8 nBChar(0); + rStrm.ReadUChar( nBChar ); + ++nRead; + if (nBChar) + { + OString aTmp = read_uInt8s_ToOString(rStrm, nBChar); + nRead += aTmp.getLength(); + rArray.push_back(OStringToOUString(aTmp, eCS)); + } + else + rArray.emplace_back(); + + // Skip the extra data (for bVer67 versions this must come from + // external knowledge) + if (nExtraLen) + { + if (pExtraArray) + { + ww::bytes extraData(nExtraLen); + rStrm.ReadBytes(extraData.data(), nExtraLen); + pExtraArray->push_back(extraData); + } + else + rStrm.SeekRel( nExtraLen ); + nRead+=nExtraLen; + } + } + } + } + rStrm.Seek(nOldPos); +} + +WW8PLCFx_Book::WW8PLCFx_Book(SvStream* pTableSt, const WW8Fib& rFib) + : WW8PLCFx(rFib, false), nIsEnd(0), nBookmarkId(1) +{ + if( !rFib.m_fcPlcfbkf || !rFib.m_lcbPlcfbkf || !rFib.m_fcPlcfbkl || + !rFib.m_lcbPlcfbkl || !rFib.m_fcSttbfbkmk || !rFib.m_lcbSttbfbkmk ) + { + nIMax = 0; + } + else + { + pBook[0].reset( new WW8PLCFspecial(pTableSt,rFib.m_fcPlcfbkf,rFib.m_lcbPlcfbkf,4) ); + + pBook[1].reset( new WW8PLCFspecial(pTableSt,rFib.m_fcPlcfbkl,rFib.m_lcbPlcfbkl,0) ); + + rtl_TextEncoding eStructChrSet = WW8Fib::GetFIBCharset(rFib.m_chseTables, rFib.m_lid); + + WW8ReadSTTBF( (7 < rFib.m_nVersion), *pTableSt, rFib.m_fcSttbfbkmk, + rFib.m_lcbSttbfbkmk, 0, eStructChrSet, aBookNames ); + + nIMax = aBookNames.size(); + + if( pBook[0]->GetIMax() < nIMax ) // Count of Bookmarks + nIMax = pBook[0]->GetIMax(); + if( pBook[1]->GetIMax() < nIMax ) + nIMax = pBook[1]->GetIMax(); + aStatus.resize(nIMax); + } +} + +WW8PLCFx_Book::~WW8PLCFx_Book() +{ +} + +sal_uInt32 WW8PLCFx_Book::GetIdx() const +{ + return nIMax ? pBook[0]->GetIdx() : 0; +} + +void WW8PLCFx_Book::SetIdx(sal_uInt32 nI) +{ + if( nIMax ) + pBook[0]->SetIdx( nI ); +} + +sal_uInt32 WW8PLCFx_Book::GetIdx2() const +{ + return nIMax ? ( pBook[1]->GetIdx() | ( nIsEnd ? 0x80000000 : 0 ) ) : 0; +} + +void WW8PLCFx_Book::SetIdx2(sal_uInt32 nI) +{ + if( nIMax ) + { + pBook[1]->SetIdx( nI & 0x7fffffff ); + nIsEnd = static_cast<sal_uInt16>( ( nI >> 31 ) & 1 ); // 0 or 1 + } +} + +bool WW8PLCFx_Book::SeekPos(WW8_CP nCpPos) +{ + if( !pBook[0] ) + return false; + + bool bOk = pBook[0]->SeekPosExact( nCpPos ); + bOk &= pBook[1]->SeekPosExact( nCpPos ); + nIsEnd = 0; + + return bOk; +} + +WW8_CP WW8PLCFx_Book::Where() +{ + return pBook[nIsEnd]->Where(); +} + +long WW8PLCFx_Book::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) +{ + void* pData; + rEnd = WW8_CP_MAX; + rLen = 0; + + if (!pBook[0] || !pBook[1] || !nIMax || (pBook[nIsEnd]->GetIdx()) >= nIMax) + { + rStart = rEnd = WW8_CP_MAX; + return -1; + } + + (void)pBook[nIsEnd]->Get( rStart, pData ); // query position + return pBook[nIsEnd]->GetIdx(); +} + +// The operator ++ has a pitfall: If 2 bookmarks adjoin each other, +// we should first go to the end of the first one +// and then to the beginning of the second one. +// But if 2 bookmarks with the length of 0 lie on top of each other, +// we *must* first find the start and end of each bookmark. +// The case of: ][ +// [...] +// ][ +// is not solved yet. +// Because I must jump back and forth in the start- and end-indices then. +// This would require one more index or bitfield to remember +// the already processed bookmarks. + +void WW8PLCFx_Book::advance() +{ + if( pBook[0] && pBook[1] && nIMax ) + { + (*pBook[nIsEnd]).advance(); + + sal_uLong l0 = pBook[0]->Where(); + sal_uLong l1 = pBook[1]->Where(); + if( l0 < l1 ) + nIsEnd = 0; + else if( l1 < l0 ) + nIsEnd = 1; + else + { + const void * p = pBook[0]->GetData(pBook[0]->GetIdx()); + long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p)); + if (nPairFor == pBook[1]->GetIdx()) + nIsEnd = 0; + else + nIsEnd = nIsEnd ? 0 : 1; + } + } +} + +long WW8PLCFx_Book::GetLen() const +{ + if( nIsEnd ) + { + OSL_ENSURE( false, "Incorrect call (1) of PLCF_Book::GetLen()" ); + return 0; + } + void * p; + WW8_CP nStartPos; + if( !pBook[0]->Get( nStartPos, p ) ) + { + OSL_ENSURE( false, "Incorrect call (2) of PLCF_Book::GetLen()" ); + return 0; + } + const sal_uInt16 nEndIdx = SVBT16ToUInt16( *static_cast<SVBT16*>(p) ); + long nNum = pBook[1]->GetPos( nEndIdx ); + nNum -= nStartPos; + return nNum; +} + +void WW8PLCFx_Book::SetStatus(sal_uInt16 nIndex, eBookStatus eStat) +{ + SAL_WARN_IF(nIndex >= nIMax, "sw.ww8", + "bookmark index " << nIndex << " invalid"); + eBookStatus eStatus = aStatus.at(nIndex); + aStatus[nIndex] = static_cast<eBookStatus>(eStatus | eStat); +} + +eBookStatus WW8PLCFx_Book::GetStatus() const +{ + if (aStatus.empty()) + return BOOK_NORMAL; + long nEndIdx = GetHandle(); + return ( nEndIdx < nIMax ) ? aStatus[nEndIdx] : BOOK_NORMAL; +} + +long WW8PLCFx_Book::GetHandle() const +{ + if( !pBook[0] || !pBook[1] ) + return LONG_MAX; + + if( nIsEnd ) + return pBook[1]->GetIdx(); + else + { + if (const void* p = pBook[0]->GetData(pBook[0]->GetIdx())) + return SVBT16ToUInt16( *static_cast<SVBT16 const *>(p) ); + else + return LONG_MAX; + } +} + +OUString WW8PLCFx_Book::GetBookmark(long nStart,long nEnd, sal_uInt16 &nIndex) +{ + bool bFound = false; + sal_uInt16 i = 0; + if (pBook[0] && pBook[1]) + { + WW8_CP nStartCurrent, nEndCurrent; + while (sal::static_int_cast<decltype(aBookNames)::size_type>(i) < aBookNames.size()) + { + void* p; + sal_uInt16 nEndIdx; + + if( pBook[0]->GetData( i, nStartCurrent, p ) && p ) + nEndIdx = SVBT16ToUInt16( *static_cast<SVBT16*>(p) ); + else + { + OSL_ENSURE( false, "Bookmark-EndIdx not readable" ); + nEndIdx = i; + } + + nEndCurrent = pBook[1]->GetPos( nEndIdx ); + + if ((nStartCurrent >= nStart) && (nEndCurrent <= nEnd)) + { + nIndex = i; + bFound=true; + break; + } + ++i; + } + } + return bFound ? aBookNames[i] : OUString(); +} + +OUString WW8PLCFx_Book::GetUniqueBookmarkName(const OUString &rSuggestedName) +{ + OUString aRet(rSuggestedName.isEmpty() ? OUString("Unnamed") : rSuggestedName); + size_t i = 0; + while (i < aBookNames.size()) + { + if (aRet == aBookNames[i]) + { + sal_Int32 len = aRet.getLength(); + sal_Int32 p = len - 1; + while (p > 0 && aRet[p] >= '0' && aRet[p] <= '9') + --p; + aRet = aRet.copy(0, p+1) + OUString::number(nBookmarkId++); + i = 0; // start search from beginning + } + else + ++i; + } + return aRet; +} + +void WW8PLCFx_Book::MapName(OUString& rName) +{ + if( !pBook[0] || !pBook[1] ) + return; + + size_t i = 0; + while (i < aBookNames.size()) + { + if (rName.equalsIgnoreAsciiCase(aBookNames[i])) + { + rName = aBookNames[i]; + break; + } + ++i; + } +} + +const OUString* WW8PLCFx_Book::GetName() const +{ + const OUString *pRet = nullptr; + if (!nIsEnd && (pBook[0]->GetIdx() < nIMax)) + pRet = &(aBookNames[pBook[0]->GetIdx()]); + return pRet; +} + +WW8PLCFx_AtnBook::WW8PLCFx_AtnBook(SvStream* pTableSt, const WW8Fib& rFib) + : WW8PLCFx(rFib, /*bSprm=*/false), + m_bIsEnd(false) +{ + if (!rFib.m_fcPlcfAtnbkf || !rFib.m_lcbPlcfAtnbkf || !rFib.m_fcPlcfAtnbkl || !rFib.m_lcbPlcfAtnbkl) + { + nIMax = 0; + } + else + { + m_pBook[0].reset( new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfAtnbkf, rFib.m_lcbPlcfAtnbkf, 4) ); + m_pBook[1].reset( new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfAtnbkl, rFib.m_lcbPlcfAtnbkl, 0) ); + + nIMax = m_pBook[0]->GetIMax(); + if (m_pBook[1]->GetIMax() < nIMax) + nIMax = m_pBook[1]->GetIMax(); + } +} + +WW8PLCFx_AtnBook::~WW8PLCFx_AtnBook() +{ +} + +sal_uInt32 WW8PLCFx_AtnBook::GetIdx() const +{ + return nIMax ? m_pBook[0]->GetIdx() : 0; +} + +void WW8PLCFx_AtnBook::SetIdx(sal_uInt32 nI) +{ + if( nIMax ) + m_pBook[0]->SetIdx( nI ); +} + +sal_uInt32 WW8PLCFx_AtnBook::GetIdx2() const +{ + if (nIMax) + return m_pBook[1]->GetIdx() | ( m_bIsEnd ? 0x80000000 : 0 ); + else + return 0; +} + +void WW8PLCFx_AtnBook::SetIdx2(sal_uInt32 nI) +{ + if( nIMax ) + { + m_pBook[1]->SetIdx( nI & 0x7fffffff ); + m_bIsEnd = static_cast<bool>(( nI >> 31 ) & 1); + } +} + +bool WW8PLCFx_AtnBook::SeekPos(WW8_CP nCpPos) +{ + if (!m_pBook[0]) + return false; + + bool bOk = m_pBook[0]->SeekPosExact(nCpPos); + bOk &= m_pBook[1]->SeekPosExact(nCpPos); + m_bIsEnd = false; + + return bOk; +} + +WW8_CP WW8PLCFx_AtnBook::Where() +{ + return m_pBook[static_cast<int>(m_bIsEnd)]->Where(); +} + +long WW8PLCFx_AtnBook::GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) +{ + void* pData; + rEnd = WW8_CP_MAX; + rLen = 0; + + if (!m_pBook[0] || !m_pBook[1] || !nIMax || (m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx()) >= nIMax) + { + rStart = rEnd = WW8_CP_MAX; + return -1; + } + + (void)m_pBook[static_cast<int>(m_bIsEnd)]->Get(rStart, pData); + return m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx(); +} + +void WW8PLCFx_AtnBook::advance() +{ + if( m_pBook[0] && m_pBook[1] && nIMax ) + { + (*m_pBook[static_cast<int>(m_bIsEnd)]).advance(); + + sal_uLong l0 = m_pBook[0]->Where(); + sal_uLong l1 = m_pBook[1]->Where(); + if( l0 < l1 ) + m_bIsEnd = false; + else if( l1 < l0 ) + m_bIsEnd = true; + else + { + const void * p = m_pBook[0]->GetData(m_pBook[0]->GetIdx()); + long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p)); + if (nPairFor == m_pBook[1]->GetIdx()) + m_bIsEnd = false; + else + m_bIsEnd = !m_bIsEnd; + } + } +} + +long WW8PLCFx_AtnBook::getHandle() const +{ + if (!m_pBook[0] || !m_pBook[1]) + return LONG_MAX; + + if (m_bIsEnd) + return m_pBook[1]->GetIdx(); + else + { + if (const void* p = m_pBook[0]->GetData(m_pBook[0]->GetIdx())) + return SVBT16ToUInt16(*static_cast<const SVBT16*>(p)); + else + return LONG_MAX; + } +} + +bool WW8PLCFx_AtnBook::getIsEnd() const +{ + return m_bIsEnd; +} + +WW8PLCFx_FactoidBook::WW8PLCFx_FactoidBook(SvStream* pTableSt, const WW8Fib& rFib) + : WW8PLCFx(rFib, /*bSprm=*/false), + m_bIsEnd(false) +{ + if (!rFib.m_fcPlcfBkfFactoid || !rFib.m_lcbPlcfBkfFactoid || !rFib.m_fcPlcfBklFactoid || !rFib.m_lcbPlcfBklFactoid) + { + m_nIMax = 0; + } + else + { + m_pBook[0].reset(new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfBkfFactoid, rFib.m_lcbPlcfBkfFactoid, 6)); + m_pBook[1].reset(new WW8PLCFspecial(pTableSt, rFib.m_fcPlcfBklFactoid, rFib.m_lcbPlcfBklFactoid, 4)); + + m_nIMax = m_pBook[0]->GetIMax(); + if (m_pBook[1]->GetIMax() < m_nIMax) + m_nIMax = m_pBook[1]->GetIMax(); + } +} + +WW8PLCFx_FactoidBook::~WW8PLCFx_FactoidBook() +{ +} + +sal_uInt32 WW8PLCFx_FactoidBook::GetIdx() const +{ + return m_nIMax ? m_pBook[0]->GetIdx() : 0; +} + +void WW8PLCFx_FactoidBook::SetIdx(sal_uInt32 nI) +{ + if (m_nIMax) + m_pBook[0]->SetIdx(nI); +} + +sal_uInt32 WW8PLCFx_FactoidBook::GetIdx2() const +{ + if (m_nIMax) + return m_pBook[1]->GetIdx() | (m_bIsEnd ? 0x80000000 : 0); + else + return 0; +} + +void WW8PLCFx_FactoidBook::SetIdx2(sal_uInt32 nI) +{ + if (m_nIMax) + { + m_pBook[1]->SetIdx(nI & 0x7fffffff); + m_bIsEnd = static_cast<bool>((nI >> 31) & 1); + } +} + +bool WW8PLCFx_FactoidBook::SeekPos(WW8_CP nCpPos) +{ + if (!m_pBook[0]) + return false; + + bool bOk = m_pBook[0]->SeekPosExact(nCpPos); + bOk &= m_pBook[1]->SeekPosExact(nCpPos); + m_bIsEnd = false; + + return bOk; +} + +WW8_CP WW8PLCFx_FactoidBook::Where() +{ + return m_pBook[static_cast<int>(m_bIsEnd)]->Where(); +} + +long WW8PLCFx_FactoidBook::GetNoSprms(WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen) +{ + void* pData; + rEnd = WW8_CP_MAX; + rLen = 0; + + if (!m_pBook[0] || !m_pBook[1] || !m_nIMax || (m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx()) >= m_nIMax) + { + rStart = rEnd = WW8_CP_MAX; + return -1; + } + + (void)m_pBook[static_cast<int>(m_bIsEnd)]->Get(rStart, pData); + return m_pBook[static_cast<int>(m_bIsEnd)]->GetIdx(); +} + +void WW8PLCFx_FactoidBook::advance() +{ + if (m_pBook[0] && m_pBook[1] && m_nIMax) + { + (*m_pBook[static_cast<int>(m_bIsEnd)]).advance(); + + sal_uLong l0 = m_pBook[0]->Where(); + sal_uLong l1 = m_pBook[1]->Where(); + if (l0 < l1) + m_bIsEnd = false; + else if (l1 < l0) + m_bIsEnd = true; + else + { + const void * p = m_pBook[0]->GetData(m_pBook[0]->GetIdx()); + long nPairFor = (p == nullptr) ? 0 : SVBT16ToUInt16(*static_cast<SVBT16 const *>(p)); + if (nPairFor == m_pBook[1]->GetIdx()) + m_bIsEnd = false; + else + m_bIsEnd = !m_bIsEnd; + } + } +} + +long WW8PLCFx_FactoidBook::getHandle() const +{ + if (!m_pBook[0] || !m_pBook[1]) + return LONG_MAX; + + if (m_bIsEnd) + return m_pBook[1]->GetIdx(); + else + { + if (const void* p = m_pBook[0]->GetData(m_pBook[0]->GetIdx())) + return SVBT16ToUInt16(*static_cast<const SVBT16*>(p)); + else + return LONG_MAX; + } +} + +bool WW8PLCFx_FactoidBook::getIsEnd() const +{ + return m_bIsEnd; +} + +// In the end of a paragraph in WW6 the attribute extends after the <CR>. +// This will be reset by one character to be used with SW, +// if we don't expect trouble thereby. +void WW8PLCFMan::AdjustEnds( WW8PLCFxDesc& rDesc ) +{ + // might be necessary to do this for pChp and/or pSep as well, + // but its definitely the case for paragraphs that EndPos > StartPos + // for a well formed paragraph as those always have a paragraph + // <cr> in them + if (&rDesc == m_pPap && rDesc.bRealLineEnd) + { + if (rDesc.nStartPos == rDesc.nEndPos && rDesc.nEndPos != WW8_CP_MAX) + { + SAL_WARN("sw.ww8", "WW8PLCFxDesc End same as Start, abandoning to avoid looping"); + rDesc.nEndPos = WW8_CP_MAX; + } + } + + //Store old end position for supercool new property finder that uses + //cp instead of fc's as nature intended + rDesc.nOrigEndPos = rDesc.nEndPos; + rDesc.nOrigStartPos = rDesc.nStartPos; + + /* + Normally given ^XXX{para end}^ we don't actually insert a para end + character into the document, so we clip the para end property one to the + left to make the para properties end when the paragraph text does. In a + drawing textbox we actually do insert a para end character, so we don't + clip it. Making the para end properties end after the para end char. + */ + if (GetDoingDrawTextBox()) + return; + + if ( (&rDesc == m_pPap) && rDesc.bRealLineEnd ) + { + if ( m_pPap->nEndPos != WW8_CP_MAX ) // Para adjust + { + m_nLineEnd = m_pPap->nEndPos;// nLineEnd points *after* the <CR> + m_pPap->nEndPos--; // shorten paragraph end by one character + + // Is there already a sep end, which points to the current paragraph end? + // Then we also must shorten by one character + if( m_pSep->nEndPos == m_nLineEnd ) + m_pSep->nEndPos--; + } + } + else if (&rDesc == m_pSep) + { + // Sep Adjust if end Char-Attr == paragraph end ... + if( (rDesc.nEndPos == m_nLineEnd) && (rDesc.nEndPos > rDesc.nStartPos) ) + rDesc.nEndPos--; // ... then shorten by one character + } +} + +void WW8PLCFxDesc::ReduceByOffset() +{ + SAL_WARN_IF(WW8_CP_MAX != nStartPos && nStartPos > nEndPos, "sw.ww8", + "End " << nEndPos << " before Start " << nStartPos); + + if( nStartPos != WW8_CP_MAX ) + { + /* + ##516##,##517## + Force the property change to happen at the beginning of this + subdocument, same as in GetNewNoSprms, except that the target type is + attributes attached to a piece that might span subdocument boundaries + */ + if (nCpOfs > nStartPos) + nStartPos = 0; + else + nStartPos -= nCpOfs; + } + if (nEndPos != WW8_CP_MAX) + { + if (nCpOfs > nEndPos) + { + SAL_WARN("sw.ww8", "broken subdocument piece entry"); + nEndPos = WW8_CP_MAX; + } + else + nEndPos -= nCpOfs; + } +} + +void WW8PLCFMan::GetNewSprms( WW8PLCFxDesc& rDesc ) +{ + rDesc.pPLCFx->GetSprms(&rDesc); + rDesc.ReduceByOffset(); + + rDesc.bFirstSprm = true; + AdjustEnds( rDesc ); + rDesc.nOrigSprmsLen = rDesc.nSprmsLen; +} + +void WW8PLCFMan::GetNewNoSprms( WW8PLCFxDesc& rDesc ) +{ + rDesc.nCp2OrIdx = rDesc.pPLCFx->GetNoSprms(rDesc.nStartPos, rDesc.nEndPos, + rDesc.nSprmsLen); + + SAL_WARN_IF(WW8_CP_MAX != rDesc.nStartPos && rDesc.nStartPos > rDesc.nEndPos, "sw.ww8", + "End " << rDesc.nEndPos << " before Start " << rDesc.nStartPos); + + rDesc.ReduceByOffset(); + + rDesc.bFirstSprm = true; + rDesc.nOrigSprmsLen = rDesc.nSprmsLen; +} + +sal_uInt16 WW8PLCFMan::GetId(const WW8PLCFxDesc* p) const +{ + sal_uInt16 nId = 0; // Id = 0 for empty attributes + + if (p == m_pField) + nId = eFLD; + else if (p == m_pFootnote) + nId = eFTN; + else if (p == m_pEdn) + nId = eEDN; + else if (p == m_pAnd) + nId = eAND; + else if (p->nSprmsLen >= maSprmParser.MinSprmLen()) + nId = maSprmParser.GetSprmId(p->pMemPos); + + return nId; +} + +WW8PLCFMan::WW8PLCFMan(const WW8ScannerBase* pBase, ManTypes nType, long nStartCp, + bool bDoingDrawTextBox) + : maSprmParser(*pBase->m_pWw8Fib), + m_nLineEnd(WW8_CP_MAX), + mbDoingDrawTextBox(bDoingDrawTextBox) +{ + m_pWwFib = pBase->m_pWw8Fib; + + m_nManType = nType; + + if( MAN_MAINTEXT == nType ) + { + // search order of the attributes + m_nPLCF = MAN_PLCF_COUNT; + m_pField = &m_aD[0]; + m_pBkm = &m_aD[1]; + m_pEdn = &m_aD[2]; + m_pFootnote = &m_aD[3]; + m_pAnd = &m_aD[4]; + + m_pPcd = pBase->m_pPLCFx_PCD ? &m_aD[5] : nullptr; + //pPcdA index == pPcd index + 1 + m_pPcdA = pBase->m_pPLCFx_PCDAttrs ? &m_aD[6] : nullptr; + + m_pChp = &m_aD[7]; + m_pPap = &m_aD[8]; + m_pSep = &m_aD[9]; + m_pAtnBkm = &m_aD[10]; + m_pFactoidBkm = &m_aD[11]; + + m_pSep->pPLCFx = pBase->m_pSepPLCF.get(); + m_pFootnote->pPLCFx = pBase->m_pFootnotePLCF.get(); + m_pEdn->pPLCFx = pBase->m_pEdnPLCF.get(); + m_pBkm->pPLCFx = pBase->m_pBook.get(); + m_pAnd->pPLCFx = pBase->m_pAndPLCF.get(); + m_pAtnBkm->pPLCFx = pBase->m_pAtnBook.get(); + m_pFactoidBkm->pPLCFx = pBase->m_pFactoidBook.get(); + + } + else + { + // search order of the attributes + m_nPLCF = 7; + m_pField = &m_aD[0]; + m_pBkm = pBase->m_pBook ? &m_aD[1] : nullptr; + + m_pPcd = pBase->m_pPLCFx_PCD ? &m_aD[2] : nullptr; + //pPcdA index == pPcd index + 1 + m_pPcdA= pBase->m_pPLCFx_PCDAttrs ? &m_aD[3] : nullptr; + + m_pChp = &m_aD[4]; + m_pPap = &m_aD[5]; + m_pSep = &m_aD[6]; // Dummy + + m_pAnd = m_pAtnBkm = m_pFactoidBkm = m_pFootnote = m_pEdn = nullptr; // not used at SpezText + } + + m_pChp->pPLCFx = pBase->m_pChpPLCF.get(); + m_pPap->pPLCFx = pBase->m_pPapPLCF.get(); + if( m_pPcd ) + m_pPcd->pPLCFx = pBase->m_pPLCFx_PCD.get(); + if( m_pPcdA ) + m_pPcdA->pPLCFx= pBase->m_pPLCFx_PCDAttrs.get(); + if( m_pBkm ) + m_pBkm->pPLCFx = pBase->m_pBook.get(); + + m_pMagicTables = pBase->m_pMagicTables.get(); + m_pSubdocs = pBase->m_pSubdocs.get(); + m_pExtendedAtrds = pBase->m_pExtendedAtrds.get(); + + switch( nType ) // field initialization + { + case MAN_HDFT: + m_pField->pPLCFx = pBase->m_pFieldHdFtPLCF.get(); + m_pFdoa = pBase->m_pHdFtFdoa.get(); + m_pTxbx = pBase->m_pHdFtTxbx.get(); + m_pTxbxBkd = pBase->m_pHdFtTxbxBkd.get(); + break; + case MAN_FTN: + m_pField->pPLCFx = pBase->m_pFieldFootnotePLCF.get(); + m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr; + break; + case MAN_EDN: + m_pField->pPLCFx = pBase->m_pFieldEdnPLCF.get(); + m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr; + break; + case MAN_AND: + m_pField->pPLCFx = pBase->m_pFieldAndPLCF.get(); + m_pFdoa = m_pTxbx = m_pTxbxBkd = nullptr; + break; + case MAN_TXBX: + m_pField->pPLCFx = pBase->m_pFieldTxbxPLCF.get(); + m_pTxbx = pBase->m_pMainTxbx.get(); + m_pTxbxBkd = pBase->m_pMainTxbxBkd.get(); + m_pFdoa = nullptr; + break; + case MAN_TXBX_HDFT: + m_pField->pPLCFx = pBase->m_pFieldTxbxHdFtPLCF.get(); + m_pTxbx = pBase->m_pHdFtTxbx.get(); + m_pTxbxBkd = pBase->m_pHdFtTxbxBkd.get(); + m_pFdoa = nullptr; + break; + default: + m_pField->pPLCFx = pBase->m_pFieldPLCF.get(); + m_pFdoa = pBase->m_pMainFdoa.get(); + m_pTxbx = pBase->m_pMainTxbx.get(); + m_pTxbxBkd = pBase->m_pMainTxbxBkd.get(); + break; + } + + WW8_CP cp = 0; + m_pWwFib->GetBaseCp(nType, &cp); //TODO: check return value + m_nCpO = cp; + + if( nStartCp || m_nCpO ) + SeekPos( nStartCp ); // adjust PLCFe at text StartPos + + // initialization to the member vars Low-Level + GetChpPLCF()->ResetAttrStartEnd(); + GetPapPLCF()->ResetAttrStartEnd(); + for( sal_uInt16 i=0; i < m_nPLCF; ++i) + { + WW8PLCFxDesc* p = &m_aD[i]; + + /* + ##516##,##517## + For subdocuments we modify the cp of properties to be relative to + the beginning of subdocuments, we should also do the same for + piecetable changes, and piecetable properties, otherwise a piece + change that happens in a subdocument is lost. + */ + p->nCpOfs = ( p == m_pChp || p == m_pPap || p == m_pBkm || p == m_pPcd || + p == m_pPcdA ) ? m_nCpO : 0; + + p->nCp2OrIdx = 0; + p->bFirstSprm = false; + p->pIdStack = nullptr; + + if ((p == m_pChp) || (p == m_pPap)) + p->nStartPos = p->nEndPos = nStartCp; + else + p->nStartPos = p->nEndPos = WW8_CP_MAX; + } + + // initialization to the member vars High-Level + for( sal_uInt16 i=0; i<m_nPLCF; ++i){ + WW8PLCFxDesc* p = &m_aD[i]; + + if( !p->pPLCFx ) + { + p->nStartPos = p->nEndPos = WW8_CP_MAX; + continue; + } + + if( p->pPLCFx->IsSprm() ) + { + // Careful: nEndPos must be + p->pIdStack = new std::stack<sal_uInt16>; + if ((p == m_pChp) || (p == m_pPap)) + { + WW8_CP nTemp = p->nEndPos+p->nCpOfs; + p->pMemPos = nullptr; + p->nSprmsLen = 0; + p->nStartPos = nTemp; + if (!(*p->pPLCFx).SeekPos(p->nStartPos)) + p->nEndPos = p->nStartPos = WW8_CP_MAX; + else + GetNewSprms( *p ); + } + else + GetNewSprms( *p ); // initialized at all PLCFs + } + else if( p->pPLCFx ) + GetNewNoSprms( *p ); + } +} + +WW8PLCFMan::~WW8PLCFMan() +{ + for( sal_uInt16 i=0; i<m_nPLCF; i++) + delete m_aD[i].pIdStack; +} + +// 0. which attr class, +// 1. if it's an attr start, +// 2. CP, where is next attr change +sal_uInt16 WW8PLCFMan::WhereIdx(bool *const pbStart, WW8_CP *const pPos) const +{ + OSL_ENSURE(m_nPLCF,"What the hell"); + WW8_CP nNext = WW8_CP_MAX; // search order: + sal_uInt16 nNextIdx = m_nPLCF;// first ending found ( CHP, PAP, ( SEP ) ), + bool bStart = true; // now find beginnings ( ( SEP ), PAP, CHP ) + const WW8PLCFxDesc* pD; + for (sal_uInt16 i=0; i < m_nPLCF; ++i) + { + pD = &m_aD[i]; + if (pD != m_pPcdA) + { + if( (pD->nEndPos < nNext) && (pD->nStartPos == WW8_CP_MAX) ) + { + // otherwise start = end + nNext = pD->nEndPos; + nNextIdx = i; + bStart = false; + } + } + } + for (sal_uInt16 i=m_nPLCF; i > 0; --i) + { + pD = &m_aD[i-1]; + if (pD != m_pPcdA && pD->nStartPos < nNext ) + { + nNext = pD->nStartPos; + nNextIdx = i-1; + bStart = true; + } + } + if( pPos ) + *pPos = nNext; + if( pbStart ) + *pbStart = bStart; + return nNextIdx; +} + +// gives the CP pos of the next attr change +WW8_CP WW8PLCFMan::Where() const +{ + WW8_CP l; + WhereIdx(nullptr, &l); + return l; +} + +void WW8PLCFMan::SeekPos( long nNewCp ) +{ + m_pChp->pPLCFx->SeekPos( nNewCp + m_nCpO ); // create new attr + m_pPap->pPLCFx->SeekPos( nNewCp + m_nCpO ); + m_pField->pPLCFx->SeekPos( nNewCp ); + if( m_pPcd ) + m_pPcd->pPLCFx->SeekPos( nNewCp + m_nCpO ); + if( m_pBkm ) + m_pBkm->pPLCFx->SeekPos( nNewCp + m_nCpO ); +} + +void WW8PLCFMan::SaveAllPLCFx( WW8PLCFxSaveAll& rSave ) const +{ + sal_uInt16 n=0; + if( m_pPcd ) + m_pPcd->Save( rSave.aS[n++] ); + if( m_pPcdA ) + m_pPcdA->Save( rSave.aS[n++] ); + + for(sal_uInt16 i=0; i<m_nPLCF; ++i) + if( m_pPcd != &m_aD[i] && m_pPcdA != &m_aD[i] ) + m_aD[i].Save( rSave.aS[n++] ); +} + +void WW8PLCFMan::RestoreAllPLCFx( const WW8PLCFxSaveAll& rSave ) +{ + sal_uInt16 n=0; + if( m_pPcd ) + m_pPcd->Restore( rSave.aS[n++] ); + if( m_pPcdA ) + m_pPcdA->Restore( rSave.aS[n++] ); + + for(sal_uInt16 i=0; i<m_nPLCF; ++i) + if( m_pPcd != &m_aD[i] && m_pPcdA != &m_aD[i] ) + m_aD[i].Restore( rSave.aS[n++] ); +} + +void WW8PLCFMan::GetSprmStart( short nIdx, WW8PLCFManResult* pRes ) const +{ + memset( pRes, 0, sizeof( WW8PLCFManResult ) ); + + // verifying !!! + + pRes->nMemLen = 0; + + const WW8PLCFxDesc* p = &m_aD[nIdx]; + + // first Sprm in a Group + if( p->bFirstSprm ) + { + if( p == m_pPap ) + pRes->nFlags |= MAN_MASK_NEW_PAP; + else if( p == m_pSep ) + pRes->nFlags |= MAN_MASK_NEW_SEP; + } + pRes->pMemPos = p->pMemPos; + pRes->nSprmId = GetId(p); + pRes->nCp2OrIdx = p->nCp2OrIdx; + if ((p == m_pFootnote) || (p == m_pEdn) || (p == m_pAnd)) + pRes->nMemLen = p->nSprmsLen; + else if (p->nSprmsLen >= maSprmParser.MinSprmLen()) //normal + { + // Length of actual sprm + pRes->nMemLen = maSprmParser.GetSprmSize(pRes->nSprmId, pRes->pMemPos, p->nSprmsLen); + if (pRes->nMemLen > p->nSprmsLen) + { + SAL_WARN("sw.ww8", "Short sprm, len " << pRes->nMemLen << " claimed, max possible is " << p->nSprmsLen); + pRes->nSprmId = 0; + } + } +} + +void WW8PLCFMan::GetSprmEnd( short nIdx, WW8PLCFManResult* pRes ) const +{ + memset( pRes, 0, sizeof( WW8PLCFManResult ) ); + + const WW8PLCFxDesc* p = &m_aD[nIdx]; + + if (!(p->pIdStack->empty())) + pRes->nSprmId = p->pIdStack->top(); // get end position + else + { + OSL_ENSURE( false, "No Id on the Stack" ); + pRes->nSprmId = 0; + } +} + +void WW8PLCFMan::GetNoSprmStart( short nIdx, WW8PLCFManResult* pRes ) const +{ + const WW8PLCFxDesc* p = &m_aD[nIdx]; + + pRes->nCpPos = p->nStartPos; + pRes->nMemLen = p->nSprmsLen; + pRes->nCp2OrIdx = p->nCp2OrIdx; + + if( p == m_pField ) + pRes->nSprmId = eFLD; + else if( p == m_pFootnote ) + pRes->nSprmId = eFTN; + else if( p == m_pEdn ) + pRes->nSprmId = eEDN; + else if( p == m_pBkm ) + pRes->nSprmId = eBKN; + else if (p == m_pAtnBkm) + pRes->nSprmId = eATNBKN; + else if (p == m_pFactoidBkm) + pRes->nSprmId = eFACTOIDBKN; + else if( p == m_pAnd ) + pRes->nSprmId = eAND; + else if( p == m_pPcd ) + { + //We slave the piece table attributes to the piece table, the piece + //table attribute iterator contains the sprms for this piece. + GetSprmStart( nIdx+1, pRes ); + } + else + pRes->nSprmId = 0; // default: not found +} + +void WW8PLCFMan::GetNoSprmEnd( short nIdx, WW8PLCFManResult* pRes ) const +{ + pRes->nMemLen = -1; // end tag + + if( &m_aD[nIdx] == m_pBkm ) + pRes->nSprmId = eBKN; + else if (&m_aD[nIdx] == m_pAtnBkm) + pRes->nSprmId = eATNBKN; + else if (&m_aD[nIdx] == m_pFactoidBkm) + pRes->nSprmId = eFACTOIDBKN; + else if( &m_aD[nIdx] == m_pPcd ) + { + //We slave the piece table attributes to the piece table, the piece + //table attribute iterator contains the sprms for this piece. + GetSprmEnd( nIdx+1, pRes ); + } + else + pRes->nSprmId = 0; +} + +void WW8PLCFMan::TransferOpenSprms(std::stack<sal_uInt16> &rStack) +{ + for (sal_uInt16 i = 0; i < m_nPLCF; ++i) + { + WW8PLCFxDesc* p = &m_aD[i]; + if (!p || !p->pIdStack) + continue; + while (!p->pIdStack->empty()) + { + rStack.push(p->pIdStack->top()); + p->pIdStack->pop(); + } + } +} + +void WW8PLCFMan::AdvSprm(short nIdx, bool bStart) +{ + WW8PLCFxDesc* p = &m_aD[nIdx]; // determine sprm class(!) + + p->bFirstSprm = false; + if( bStart ) + { + const sal_uInt16 nLastId = GetId(p); + p->pIdStack->push(nLastId); // remember Id for attribute end + + if( p->nSprmsLen ) + { /* + Check, if we have to process more sprm(s). + */ + if( p->pMemPos ) + { + // Length of last sprm + const sal_uInt16 nSprmL = maSprmParser.GetSprmSize(nLastId, p->pMemPos, p->nSprmsLen); + + // Reduce length of all sprms by length of last sprm + p->nSprmsLen -= nSprmL; + + // pos of next possible sprm + if (p->nSprmsLen < maSprmParser.MinSprmLen()) + { + // preventively set to 0, because the end follows! + p->pMemPos = nullptr; + p->nSprmsLen = 0; + } + else + p->pMemPos += nSprmL; + } + else + p->nSprmsLen = 0; + } + if (p->nSprmsLen < maSprmParser.MinSprmLen()) + p->nStartPos = WW8_CP_MAX; // the ending follows + } + else + { + if (!(p->pIdStack->empty())) + p->pIdStack->pop(); + if (p->pIdStack->empty()) + { + if ( (p == m_pChp) || (p == m_pPap) ) + { + p->pMemPos = nullptr; + p->nSprmsLen = 0; + p->nStartPos = p->nOrigEndPos+p->nCpOfs; + + /* + On failed seek we have run out of sprms, probably. But if its + a fastsaved file (has pPcd) then we may be just in a sprm free + gap between pieces that have them, so set dirty flag in sprm + finder to consider than. + */ + if (!(*p->pPLCFx).SeekPos(p->nStartPos)) + { + p->nEndPos = WW8_CP_MAX; + p->pPLCFx->SetDirty(true); + } + if (!p->pPLCFx->GetDirty() || m_pPcd) + GetNewSprms( *p ); + p->pPLCFx->SetDirty(false); + + /* + #i2325# + To get the character and paragraph properties you first get + the pap and chp and then apply the fastsaved pPcd properties + to the range. If a pap or chp starts inside the pPcd range + then we must bring the current pPcd range to a halt so as to + end those sprms, then the pap/chp will be processed, and then + we must force a restart of the pPcd on that pap/chp starting + boundary. Doing that effectively means that the pPcd sprms will + be applied to the new range. Not doing it means that the pPcd + sprms will only be applied to the first pap/chp set of + properties contained in the pap/chp range. + + So we bring the pPcd to a halt on this location here, by + settings its end to the current start, then store the starting + position of the current range to clipstart. The pPcd sprms + will end as normal (albeit earlier than originally expected), + and the existence of a clipstart will force the pPcd iterator + to reread the current set of sprms instead of advancing to its + next set. Then the clipstart will be set as the starting + position which will force them to be applied directly after + the pap and chps. + */ + if (m_pPcd && ((p->nStartPos > m_pPcd->nStartPos) || + (m_pPcd->nStartPos == WW8_CP_MAX)) && + (m_pPcd->nEndPos != p->nStartPos)) + { + m_pPcd->nEndPos = p->nStartPos; + static_cast<WW8PLCFx_PCD *>(m_pPcd->pPLCFx)->SetClipStart( + p->nStartPos); + } + + } + else + { + p->pPLCFx->advance(); // next Group of Sprms + p->pMemPos = nullptr; // !!! + p->nSprmsLen = 0; + GetNewSprms( *p ); + } + SAL_WARN_IF(p->nStartPos > p->nEndPos, "sw.ww8", + "End " << p->nEndPos << " before Start " << p->nStartPos); + } + } +} + +void WW8PLCFMan::AdvNoSprm(short nIdx, bool bStart) +{ + /* + For the case of a piece table we slave the piece table attribute iterator + to the piece table and access it through that only. They are two separate + structures, but act together as one logical one. The attributes only go + to the next entry when the piece changes + */ + WW8PLCFxDesc* p = &m_aD[nIdx]; + + if( p == m_pPcd ) + { + AdvSprm(nIdx+1,bStart); + if( bStart ) + p->nStartPos = m_aD[nIdx+1].nStartPos; + else + { + if (m_aD[nIdx+1].pIdStack->empty()) + { + WW8PLCFx_PCD *pTemp = static_cast<WW8PLCFx_PCD*>(m_pPcd->pPLCFx); + /* + #i2325# + As per normal, go on to the next set of properties, i.e. we + have traversed over to the next piece. With a clipstart set + we are being told to reread the current piece sprms so as to + reapply them to a new chp or pap range. + */ + if (pTemp->GetClipStart() == -1) + p->pPLCFx->advance(); + p->pMemPos = nullptr; + p->nSprmsLen = 0; + GetNewSprms( m_aD[nIdx+1] ); + GetNewNoSprms( *p ); + if (pTemp->GetClipStart() != -1) + { + /* + #i2325#, now we will force our starting position to the + clipping start so as to force the application of these + sprms after the current pap/chp sprms so as to apply the + fastsave sprms to the current range. + */ + p->nStartPos = pTemp->GetClipStart(); + pTemp->SetClipStart(-1); + } + } + } + } + else + { // NoSprm without end + p->pPLCFx->advance(); + p->pMemPos = nullptr; // MemPos invalid + p->nSprmsLen = 0; + GetNewNoSprms( *p ); + } +} + +void WW8PLCFMan::advance() +{ + bool bStart; + const sal_uInt16 nIdx = WhereIdx(&bStart); + if (nIdx < m_nPLCF) + { + WW8PLCFxDesc* p = &m_aD[nIdx]; + + p->bFirstSprm = true; // Default + + if( p->pPLCFx->IsSprm() ) + AdvSprm( nIdx, bStart ); + else // NoSprm + AdvNoSprm( nIdx, bStart ); + } +} + +// return true for the beginning of an attribute or error, +// false for the end of an attribute +// remaining return values are delivered to the caller from WW8PclxManResults. +bool WW8PLCFMan::Get(WW8PLCFManResult* pRes) const +{ + memset( pRes, 0, sizeof( WW8PLCFManResult ) ); + bool bStart; + const sal_uInt16 nIdx = WhereIdx(&bStart); + + if( nIdx >= m_nPLCF ) + { + OSL_ENSURE( false, "Position not found" ); + return true; + } + + if( m_aD[nIdx].pPLCFx->IsSprm() ) + { + if( bStart ) + { + GetSprmStart( nIdx, pRes ); + return true; + } + else + { + GetSprmEnd( nIdx, pRes ); + return false; + } + } + else + { + if( bStart ) + { + GetNoSprmStart( nIdx, pRes ); + return true; + } + else + { + GetNoSprmEnd( nIdx, pRes ); + return false; + } + } +} + +sal_uInt16 WW8PLCFMan::GetColl() const +{ + if( m_pPap->pPLCFx ) + return m_pPap->pPLCFx->GetIstd(); + else + { + OSL_ENSURE( false, "GetColl without PLCF_Pap" ); + return 0; + } +} + +WW8PLCFx_FLD* WW8PLCFMan::GetField() const +{ + return static_cast<WW8PLCFx_FLD*>(m_pField->pPLCFx); +} + +SprmResult WW8PLCFMan::HasParaSprm( sal_uInt16 nId ) const +{ + return static_cast<WW8PLCFx_Cp_FKP*>(m_pPap->pPLCFx)->HasSprm( nId ); +} + +SprmResult WW8PLCFMan::HasCharSprm( sal_uInt16 nId ) const +{ + return static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx)->HasSprm( nId ); +} + +void WW8PLCFMan::HasCharSprm(sal_uInt16 nId, + std::vector<SprmResult> &rResult) const +{ + static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx)->HasSprm(nId, rResult); +} + +void WW8PLCFx::Save( WW8PLCFxSave1& rSave ) const +{ + rSave.nPLCFxPos = GetIdx(); + rSave.nPLCFxPos2 = GetIdx2(); + rSave.nPLCFxMemOfs = 0; + rSave.nStartFC = GetStartFc(); +} + +void WW8PLCFx::Restore( const WW8PLCFxSave1& rSave ) +{ + SetIdx( rSave.nPLCFxPos ); + SetIdx2( rSave.nPLCFxPos2 ); + SetStartFc( rSave.nStartFC ); +} + +sal_uInt32 WW8PLCFx_Cp_FKP::GetIdx2() const +{ + return GetPCDIdx(); +} + +void WW8PLCFx_Cp_FKP::SetIdx2(sal_uInt32 nIdx) +{ + if( pPcd ) + pPcd->SetIdx( nIdx ); +} + +void WW8PLCFx_Cp_FKP::Save( WW8PLCFxSave1& rSave ) const +{ + if (pFkp) + pFkp->IncMustRemainCache(); + WW8PLCFx::Save( rSave ); + + rSave.nAttrStart = nAttrStart; + rSave.nAttrEnd = nAttrEnd; + rSave.bLineEnd = bLineEnd; +} + +void WW8PLCFx_Cp_FKP::Restore( const WW8PLCFxSave1& rSave ) +{ + WW8PLCFx::Restore( rSave ); + + nAttrStart = rSave.nAttrStart; + nAttrEnd = rSave.nAttrEnd; + bLineEnd = rSave.bLineEnd; + + if (pFkp) + pFkp->DecMustRemainCache(); +} + +void WW8PLCFxDesc::Save( WW8PLCFxSave1& rSave ) const +{ + if( pPLCFx ) + { + pPLCFx->Save( rSave ); + if( pPLCFx->IsSprm() ) + { + WW8PLCFxDesc aD; + aD.nStartPos = nOrigStartPos+nCpOfs; + aD.nCpOfs = rSave.nCpOfs = nCpOfs; + if (!(pPLCFx->SeekPos(aD.nStartPos))) + { + aD.nEndPos = WW8_CP_MAX; + pPLCFx->SetDirty(true); + } + pPLCFx->GetSprms(&aD); + pPLCFx->SetDirty(false); + aD.ReduceByOffset(); + rSave.nStartCp = aD.nStartPos; + rSave.nPLCFxMemOfs = nOrigSprmsLen - nSprmsLen; + } + } +} + +void WW8PLCFxDesc::Restore( const WW8PLCFxSave1& rSave ) +{ + if( pPLCFx ) + { + pPLCFx->Restore( rSave ); + if( pPLCFx->IsSprm() ) + { + WW8PLCFxDesc aD; + aD.nStartPos = rSave.nStartCp+rSave.nCpOfs; + nCpOfs = aD.nCpOfs = rSave.nCpOfs; + if (!(pPLCFx->SeekPos(aD.nStartPos))) + { + aD.nEndPos = WW8_CP_MAX; + pPLCFx->SetDirty(true); + } + pPLCFx->GetSprms(&aD); + pPLCFx->SetDirty(false); + aD.ReduceByOffset(); + + if (nOrigSprmsLen > aD.nSprmsLen) + { + //two entries exist for the same offset, cut and run + SAL_WARN("sw.ww8", "restored properties don't match saved properties, bailing out"); + nSprmsLen = 0; + pMemPos = nullptr; + } + else + { + nSprmsLen = nOrigSprmsLen - rSave.nPLCFxMemOfs; + pMemPos = aD.pMemPos == nullptr ? nullptr : aD.pMemPos + rSave.nPLCFxMemOfs; + } + } + } +} + +namespace +{ + sal_uInt32 Readcb(SvStream& rSt, ww::WordVersion eVer) + { + if (eVer <= ww::eWW2) + { + sal_uInt16 nShort(0); + rSt.ReadUInt16(nShort); + return nShort; + } + else + { + sal_uInt32 nLong(0); + rSt.ReadUInt32(nLong); + return nLong; + } + } +} + +bool WW8Fib::GetBaseCp(ManTypes nType, WW8_CP * cp) const +{ + assert(cp != nullptr); + WW8_CP nOffset = 0; + + switch (nType) + { + case MAN_TXBX_HDFT: + if (m_ccpTxbx < 0) { + return false; + } + nOffset = m_ccpTxbx; + [[fallthrough]]; + case MAN_TXBX: + if (m_ccpEdn < 0 || m_ccpEdn > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpEdn; + [[fallthrough]]; + case MAN_EDN: + if (m_ccpAtn < 0 || m_ccpAtn > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpAtn; + [[fallthrough]]; + case MAN_AND: + if (m_ccpMcr < 0 || m_ccpMcr > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpMcr; + /* + // fall through + + A subdocument of this kind (MAN_MACRO) probably exists in some defunct + version of MSWord, but now ccpMcr is always 0. If some example that + uses this comes to light, this is the likely calculation required + + case MAN_MACRO: + */ + if (m_ccpHdr < 0 || m_ccpHdr > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpHdr; + [[fallthrough]]; + case MAN_HDFT: + if (m_ccpFootnote < 0 || m_ccpFootnote > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpFootnote; + [[fallthrough]]; + case MAN_FTN: + if (m_ccpText < 0 || m_ccpText > std::numeric_limits<WW8_CP>::max() - nOffset) { + return false; + } + nOffset += m_ccpText; + [[fallthrough]]; + case MAN_MAINTEXT: + break; + } + *cp = nOffset; + return true; +} + +ww::WordVersion WW8Fib::GetFIBVersion() const +{ + ww::WordVersion eVer = ww::eWW8; + /* + * Word for Windows 2 I think (1.X might work too if anyone has an example. + * + * 0xA59B for Word 1 for Windows + * 0xA59C for Word 1 for OS/2 "PM Word" + * + * Various pages claim that the fileformats of Word 1 and 2 for Windows are + * equivalent to Word for Macintosh 4 and 5. On the other hand + * + * wIdents for Word for Mac versions... + * 0xFE32 for Word 1 + * 0xFE34 for Word 3 + * 0xFE37 for Word 4 et 5. + * + * and this document + * http://cmsdoc.cern.ch/documents/docformat/CMS_CERN_LetterHead.word is + * claimed to be "Word 5 for Mac" by Office etc and has that wIdent, but + * its format isn't the same as that of Word 2 for windows. Nor is it + * the same as that of Word for DOS/PCWord 5 + */ + if (m_wIdent == 0xa59b || m_wIdent == 0xa59c) + eVer = ww::eWW1; + else if (m_wIdent == 0xa5db) + eVer = ww::eWW2; + else + { + switch (m_nVersion) + { + case 6: + eVer = ww::eWW6; + break; + case 7: + eVer = ww::eWW7; + break; + case 8: + eVer = ww::eWW8; + break; + } + } + return eVer; +} + +WW8Fib::WW8Fib(SvStream& rSt, sal_uInt8 nWantedVersion, sal_uInt32 nOffset): + m_fDot(false), m_fGlsy(false), m_fComplex(false), m_fHasPic(false), m_cQuickSaves(0), + m_fEncrypted(false), m_fWhichTableStm(false), m_fReadOnlyRecommended(false), + m_fWriteReservation(false), m_fExtChar(false), m_fFarEast(false), m_fObfuscated(false), + m_fMac(false), m_fEmptySpecial(false), m_fLoadOverridePage(false), m_fFuturesavedUndo(false), + m_fWord97Saved(false), m_fWord2000Saved(false) + // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the + // above bit-field member initializations can be moved to the class definition +{ + // See [MS-DOC] 2.5.15 "How to read the FIB". + sal_uInt8 aBits1; + sal_uInt8 aBits2; + sal_uInt8 aVer8Bits1; // only used starting with WinWord 8 + rSt.Seek( nOffset ); + /* + note desired number, identify file version number + and check against desired number! + */ + m_nVersion = nWantedVersion; + rSt.ReadUInt16( m_wIdent ); + rSt.ReadUInt16( m_nFib ); + rSt.ReadUInt16( m_nProduct ); + if( ERRCODE_NONE != rSt.GetError() ) + { + sal_Int16 nFibMin; + sal_Int16 nFibMax; + // note: 6 stands for "6 OR 7", 7 stands for "ONLY 7" + switch( m_nVersion ) + { + case 6: + nFibMin = 0x0065; // from 101 WinWord 6.0 + // 102 " + // and 103 WinWord 6.0 for Macintosh + // 104 " + nFibMax = 0x0069; // to 105 WinWord 95 + break; + case 7: + nFibMin = 0x0069; // from 105 WinWord 95 + nFibMax = 0x0069; // to 105 WinWord 95 + break; + case 8: + nFibMin = 0x006A; // from 106 WinWord 97 + nFibMax = 0x00c1; // to 193 WinWord 97 (?) + break; + default: + nFibMin = 0; // program error! + nFibMax = 0; + m_nFib = 1; + OSL_ENSURE( false, "nVersion not implemented!" ); + break; + } + if ( (m_nFib < nFibMin) || (m_nFib > nFibMax) ) + { + m_nFibError = ERR_SWG_READ_ERROR; // report error + return; + } + } + + ww::WordVersion eVer = GetFIBVersion(); + + // helper vars for Ver67: + sal_Int16 pnChpFirst_Ver67=0; + sal_Int16 pnPapFirst_Ver67=0; + sal_Int16 cpnBteChp_Ver67=0; + sal_Int16 cpnBtePap_Ver67=0; + + // read FIB + sal_uInt16 nTmpLid = 0; + rSt.ReadUInt16(nTmpLid); + m_lid = LanguageType(nTmpLid); + rSt.ReadInt16( m_pnNext ); + rSt.ReadUChar( aBits1 ); + rSt.ReadUChar( aBits2 ); + rSt.ReadUInt16( m_nFibBack ); + rSt.ReadUInt16( m_nHash ); + rSt.ReadUInt16( m_nKey ); + rSt.ReadUChar( m_envr ); + rSt.ReadUChar( aVer8Bits1 ); // only have an empty reserve field under Ver67 + // content from aVer8Bits1 + + // sal_uInt8 fMac :1; + // sal_uInt8 fEmptySpecial :1; + // sal_uInt8 fLoadOverridePage :1; + // sal_uInt8 fFuturesavedUndo :1; + // sal_uInt8 fWord97Saved :1; + // sal_uInt8 :3; + rSt.ReadUInt16( m_chse ); + rSt.ReadUInt16( m_chseTables ); + rSt.ReadInt32( m_fcMin ); + rSt.ReadInt32( m_fcMac ); + +// insertion for WW8 + if (IsEightPlus(eVer)) + { + rSt.ReadUInt16( m_csw ); + + // Marke: "rgsw" Beginning of the array of shorts + rSt.ReadUInt16( m_wMagicCreated ); + rSt.ReadUInt16( m_wMagicRevised ); + rSt.ReadUInt16( m_wMagicCreatedPrivate ); + rSt.ReadUInt16( m_wMagicRevisedPrivate ); + rSt.SeekRel( 9 * sizeof( sal_Int16 ) ); + + /* + // these are the 9 unused fields: + && (bVer67 || WW8ReadINT16( rSt, pnFbpChpFirst_W6 )) // 1 + && (bVer67 || WW8ReadINT16( rSt, pnChpFirst_W6 )) // 2 + && (bVer67 || WW8ReadINT16( rSt, cpnBteChp_W6 )) // 3 + && (bVer67 || WW8ReadINT16( rSt, pnFbpPapFirst_W6 )) // 4 + && (bVer67 || WW8ReadINT16( rSt, pnPapFirst_W6 )) // 5 + && (bVer67 || WW8ReadINT16( rSt, cpnBtePap_W6 )) // 6 + && (bVer67 || WW8ReadINT16( rSt, pnFbpLvcFirst_W6 )) // 7 + && (bVer67 || WW8ReadINT16( rSt, pnLvcFirst_W6 )) // 8 + && (bVer67 || WW8ReadINT16( rSt, cpnBteLvc_W6 )) // 9 + */ + sal_uInt16 nTmpFE = 0; + rSt.ReadUInt16(nTmpFE); + m_lidFE = LanguageType(nTmpFE); + rSt.ReadUInt16( m_clw ); + } + +// end of the insertion for WW8 + + // Marke: "rglw" Beginning of the array of longs + rSt.ReadInt32( m_cbMac ); + + // ignore 2 longs, because they are unimportant + rSt.SeekRel( 2 * sizeof( sal_Int32) ); + + // skipping 2 more longs only at Ver67 + if (IsSevenMinus(eVer)) + rSt.SeekRel( 2 * sizeof( sal_Int32) ); + + rSt.ReadInt32( m_ccpText ); + rSt.ReadInt32( m_ccpFootnote ); + rSt.ReadInt32( m_ccpHdr ); + rSt.ReadInt32( m_ccpMcr ); + rSt.ReadInt32( m_ccpAtn ); + rSt.ReadInt32( m_ccpEdn ); + rSt.ReadInt32( m_ccpTxbx ); + rSt.ReadInt32( m_ccpHdrTxbx ); + + // only skip one more long at Ver67 + if (IsSevenMinus(eVer)) + rSt.SeekRel( 1 * sizeof( sal_Int32) ); + else + { +// insertion for WW8 + rSt.ReadInt32( m_pnFbpChpFirst ); + rSt.ReadInt32( m_pnChpFirst ); + rSt.ReadInt32( m_cpnBteChp ); + rSt.ReadInt32( m_pnFbpPapFirst ); + rSt.ReadInt32( m_pnPapFirst ); + rSt.ReadInt32( m_cpnBtePap ); + rSt.ReadInt32( m_pnFbpLvcFirst ); + rSt.ReadInt32( m_pnLvcFirst ); + rSt.ReadInt32( m_cpnBteLvc ); + rSt.ReadInt32( m_fcIslandFirst ); + rSt.ReadInt32( m_fcIslandLim ); + rSt.ReadUInt16( m_cfclcb ); + + // Read cswNew to find out if nFib should be ignored. + sal_uInt32 nPos = rSt.Tell(); + rSt.SeekRel(m_cfclcb * 8); + if (rSt.good()) + { + rSt.ReadUInt16(m_cswNew); + } + rSt.Seek(nPos); + } + +// end of the insertion for WW8 + + // Marke: "rgfclcb" Beginning of array of FC/LCB pairs. + rSt.ReadInt32( m_fcStshfOrig ); + m_lcbStshfOrig = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcStshf ); + m_lcbStshf = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffndRef ); + m_lcbPlcffndRef = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffndText ); + m_lcbPlcffndText = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfandRef ); + m_lcbPlcfandRef = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfandText ); + m_lcbPlcfandText = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfsed ); + m_lcbPlcfsed = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfpad ); + m_lcbPlcfpad = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfphe ); + m_lcbPlcfphe = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfglsy ); + m_lcbSttbfglsy = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfglsy ); + m_lcbPlcfglsy = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfhdd ); + m_lcbPlcfhdd = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfbteChpx ); + m_lcbPlcfbteChpx = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfbtePapx ); + m_lcbPlcfbtePapx = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfsea ); + m_lcbPlcfsea = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfffn ); + m_lcbSttbfffn = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffldMom ); + m_lcbPlcffldMom = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffldHdr ); + m_lcbPlcffldHdr = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffldFootnote ); + m_lcbPlcffldFootnote = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffldAtn ); + m_lcbPlcffldAtn = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcffldMcr ); + m_lcbPlcffldMcr = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfbkmk ); + m_lcbSttbfbkmk = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfbkf ); + m_lcbPlcfbkf = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfbkl ); + m_lcbPlcfbkl = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcCmds ); + m_lcbCmds = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfmcr ); + m_lcbPlcfmcr = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfmcr ); + m_lcbSttbfmcr = Readcb(rSt, eVer); + if (eVer >= ww::eWW2) + { + rSt.ReadInt32( m_fcPrDrvr ); + m_lcbPrDrvr = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPrEnvPort ); + m_lcbPrEnvPort = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPrEnvLand ); + m_lcbPrEnvLand = Readcb(rSt, eVer); + } + else + { + rSt.ReadInt32( m_fcPrEnvPort ); + m_lcbPrEnvPort = Readcb(rSt, eVer); + } + rSt.ReadInt32( m_fcWss ); + m_lcbWss = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcDop ); + m_lcbDop = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfAssoc ); + m_lcbSttbfAssoc = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcClx ); + m_lcbClx = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcPlcfpgdFootnote ); + m_lcbPlcfpgdFootnote = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcAutosaveSource ); + m_lcbAutosaveSource = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcGrpStAtnOwners ); + m_lcbGrpStAtnOwners = Readcb(rSt, eVer); + rSt.ReadInt32( m_fcSttbfAtnbkmk ); + m_lcbSttbfAtnbkmk = Readcb(rSt, eVer); + + // only skip more shot at Ver67 + if (IsSevenMinus(eVer)) + { + if (eVer == ww::eWW1) + rSt.SeekRel(1*sizeof(sal_Int32)); + rSt.SeekRel(1*sizeof(sal_Int16)); + + if (eVer >= ww::eWW2) + { + rSt.ReadInt16(pnChpFirst_Ver67); + rSt.ReadInt16(pnPapFirst_Ver67); + } + rSt.ReadInt16(cpnBteChp_Ver67); + rSt.ReadInt16(cpnBtePap_Ver67); + } + + if (eVer > ww::eWW2) + { + rSt.ReadInt32( m_fcPlcfdoaMom ); + rSt.ReadInt32( m_lcbPlcfdoaMom ); + rSt.ReadInt32( m_fcPlcfdoaHdr ); + rSt.ReadInt32( m_lcbPlcfdoaHdr ); + rSt.ReadInt32( m_fcPlcfspaMom ); + rSt.ReadInt32( m_lcbPlcfspaMom ); + rSt.ReadInt32( m_fcPlcfspaHdr ); + rSt.ReadInt32( m_lcbPlcfspaHdr ); + + rSt.ReadInt32( m_fcPlcfAtnbkf ); + rSt.ReadInt32( m_lcbPlcfAtnbkf ); + rSt.ReadInt32( m_fcPlcfAtnbkl ); + rSt.ReadInt32( m_lcbPlcfAtnbkl ); + rSt.ReadInt32( m_fcPms ); + rSt.ReadInt32( m_lcbPMS ); + rSt.ReadInt32( m_fcFormFieldSttbf ); + rSt.ReadInt32( m_lcbFormFieldSttbf ); + rSt.ReadInt32( m_fcPlcfendRef ); + rSt.ReadInt32( m_lcbPlcfendRef ); + rSt.ReadInt32( m_fcPlcfendText ); + rSt.ReadInt32( m_lcbPlcfendText ); + rSt.ReadInt32( m_fcPlcffldEdn ); + rSt.ReadInt32( m_lcbPlcffldEdn ); + rSt.ReadInt32( m_fcPlcfpgdEdn ); + rSt.ReadInt32( m_lcbPlcfpgdEdn ); + rSt.ReadInt32( m_fcDggInfo ); + rSt.ReadInt32( m_lcbDggInfo ); + rSt.ReadInt32( m_fcSttbfRMark ); + rSt.ReadInt32( m_lcbSttbfRMark ); + rSt.ReadInt32( m_fcSttbfCaption ); + rSt.ReadInt32( m_lcbSttbfCaption ); + rSt.ReadInt32( m_fcSttbAutoCaption ); + rSt.ReadInt32( m_lcbSttbAutoCaption ); + rSt.ReadInt32( m_fcPlcfwkb ); + rSt.ReadInt32( m_lcbPlcfwkb ); + rSt.ReadInt32( m_fcPlcfspl ); + rSt.ReadInt32( m_lcbPlcfspl ); + rSt.ReadInt32( m_fcPlcftxbxText ); + rSt.ReadInt32( m_lcbPlcftxbxText ); + rSt.ReadInt32( m_fcPlcffldTxbx ); + rSt.ReadInt32( m_lcbPlcffldTxbx ); + rSt.ReadInt32( m_fcPlcfHdrtxbxText ); + rSt.ReadInt32( m_lcbPlcfHdrtxbxText ); + rSt.ReadInt32( m_fcPlcffldHdrTxbx ); + rSt.ReadInt32( m_lcbPlcffldHdrTxbx ); + rSt.ReadInt32( m_fcStwUser ); + rSt.ReadUInt32( m_lcbStwUser ); + rSt.ReadInt32( m_fcSttbttmbd ); + rSt.ReadUInt32( m_lcbSttbttmbd ); + } + + if( ERRCODE_NONE == rSt.GetError() ) + { + // set bit flag + m_fDot = aBits1 & 0x01 ; + m_fGlsy = ( aBits1 & 0x02 ) >> 1; + m_fComplex = ( aBits1 & 0x04 ) >> 2; + m_fHasPic = ( aBits1 & 0x08 ) >> 3; + m_cQuickSaves = ( aBits1 & 0xf0 ) >> 4; + m_fEncrypted = aBits2 & 0x01 ; + m_fWhichTableStm= ( aBits2 & 0x02 ) >> 1; + m_fReadOnlyRecommended = (aBits2 & 0x4) >> 2; + m_fWriteReservation = (aBits2 & 0x8) >> 3; + m_fExtChar = ( aBits2 & 0x10 ) >> 4; + // dummy = ( aBits2 & 0x20 ) >> 5; + m_fFarEast = ( aBits2 & 0x40 ) >> 6; // #i90932# + // dummy = ( aBits2 & 0x80 ) >> 7; + + /* + p.r.n. fill targeted variable with xxx_Ver67 + or set flags + */ + if (IsSevenMinus(eVer)) + { + m_pnChpFirst = pnChpFirst_Ver67; + m_pnPapFirst = pnPapFirst_Ver67; + m_cpnBteChp = cpnBteChp_Ver67; + m_cpnBtePap = cpnBtePap_Ver67; + } + else if (IsEightPlus(eVer)) + { + m_fMac = aVer8Bits1 & 0x01 ; + m_fEmptySpecial = ( aVer8Bits1 & 0x02 ) >> 1; + m_fLoadOverridePage = ( aVer8Bits1 & 0x04 ) >> 2; + m_fFuturesavedUndo = ( aVer8Bits1 & 0x08 ) >> 3; + m_fWord97Saved = ( aVer8Bits1 & 0x10 ) >> 4; + m_fWord2000Saved = ( aVer8Bits1 & 0x20 ) >> 5; + + /* + especially for WW8: + identify the values for PLCF and PLF LFO + and PLCF for the textbox break descriptors + */ + long nOldPos = rSt.Tell(); + + rSt.Seek( 0x02da ); + rSt.ReadInt32( m_fcSttbFnm ); + rSt.ReadInt32( m_lcbSttbFnm ); + rSt.ReadInt32( m_fcPlcfLst ); + rSt.ReadInt32( m_lcbPlcfLst ); + rSt.ReadInt32( m_fcPlfLfo ); + rSt.ReadInt32( m_lcbPlfLfo ); + rSt.ReadInt32( m_fcPlcftxbxBkd ); + rSt.ReadInt32( m_lcbPlcftxbxBkd ); + rSt.ReadInt32( m_fcPlcfHdrtxbxBkd ); + rSt.ReadInt32( m_lcbPlcfHdrtxbxBkd ); + if( ERRCODE_NONE != rSt.GetError() ) + { + m_nFibError = ERR_SWG_READ_ERROR; + } + + rSt.Seek( 0x372 ); // fcSttbListNames + rSt.ReadInt32( m_fcSttbListNames ); + rSt.ReadInt32( m_lcbSttbListNames ); + + if (m_cfclcb > 93) + { + rSt.Seek( 0x382 ); // MagicTables + rSt.ReadInt32( m_fcPlcfTch ); + rSt.ReadInt32( m_lcbPlcfTch ); + } + + if (m_cfclcb > 113) + { + rSt.Seek( 0x41A ); // new ATRD + rSt.ReadInt32( m_fcAtrdExtra ); + rSt.ReadUInt32( m_lcbAtrdExtra ); + } + + // Factoid bookmarks + if (m_cfclcb > 134) + { + rSt.Seek(0x432); + rSt.ReadInt32(m_fcPlcfBkfFactoid); + rSt.ReadUInt32(m_lcbPlcfBkfFactoid); + + rSt.Seek(0x442); + rSt.ReadInt32(m_fcPlcfBklFactoid); + rSt.ReadUInt32(m_lcbPlcfBklFactoid); + + rSt.Seek(0x44a); + rSt.ReadInt32(m_fcFactoidData); + rSt.ReadUInt32(m_lcbFactoidData); + } + + if( ERRCODE_NONE != rSt.GetError() ) + m_nFibError = ERR_SWG_READ_ERROR; + + rSt.Seek( 0x5bc ); // Actual nFib introduced in Word 2003 + rSt.ReadUInt16( m_nFib_actual ); + + rSt.Seek( nOldPos ); + } + } + else + { + m_nFibError = ERR_SWG_READ_ERROR; // report error + } +} + +WW8Fib::WW8Fib(sal_uInt8 nVer, bool bDot): + m_nVersion(nVer), m_fDot(false), m_fGlsy(false), m_fComplex(false), m_fHasPic(false), m_cQuickSaves(0), + m_fEncrypted(false), m_fWhichTableStm(false), m_fReadOnlyRecommended(false), + m_fWriteReservation(false), m_fExtChar(false), m_fFarEast(false), m_fObfuscated(false), + m_fMac(false), m_fEmptySpecial(false), m_fLoadOverridePage(false), m_fFuturesavedUndo(false), + m_fWord97Saved(false), m_fWord2000Saved(false) + // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the + // above bit-field member initializations can be moved to the class definition +{ + if (8 == nVer) + { + m_fcMin = 0x800; + m_wIdent = 0xa5ec; + m_nFib = 0x0101; + m_nFibBack = 0xbf; + m_nProduct = 0x204D; + m_fDot = bDot; + + m_csw = 0x0e; // Is this really necessary??? + m_cfclcb = 0x88; // -""- + m_clw = 0x16; // -""- + m_pnFbpChpFirst = m_pnFbpPapFirst = m_pnFbpLvcFirst = 0x000fffff; + m_fExtChar = true; + m_fWord97Saved = m_fWord2000Saved = true; + + // Just a fancy way to write 'Caolan80'. + m_wMagicCreated = 0x6143; + m_wMagicRevised = 0x6C6F; + m_wMagicCreatedPrivate = 0x6E61; + m_wMagicRevisedPrivate = 0x3038; + } + else + { + m_fcMin = 0x300; + m_wIdent = 0xa5dc; + m_nFib = m_nFibBack = 0x65; + m_nProduct = 0xc02d; + } + + //If nFib is 0x00D9 or greater, then cQuickSaves MUST be 0xF + m_cQuickSaves = m_nFib >= 0x00D9 ? 0xF : 0; + + // --> #i90932# + m_lid = LanguageType(0x409); // LANGUAGE_ENGLISH_US + + LanguageType nLang = Application::GetSettings().GetLanguageTag().getLanguageType(); + m_fFarEast = MsLangId::isCJK(nLang); + if (m_fFarEast) + m_lidFE = nLang; + else + m_lidFE = m_lid; + + LanguageTag aLanguageTag( m_lid ); + LocaleDataWrapper aLocaleWrapper( aLanguageTag ); + m_nNumDecimalSep = aLocaleWrapper.getNumDecimalSep()[0]; +} + + +void WW8Fib::WriteHeader(SvStream& rStrm) +{ + bool bVer8 = 8 == m_nVersion; + + size_t nUnencryptedHdr = bVer8 ? 0x44 : 0x24; + std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ nUnencryptedHdr ] ); + sal_uInt8 *pData = pDataPtr.get(); + memset( pData, 0, nUnencryptedHdr ); + + m_cbMac = rStrm.TellEnd(); + + Set_UInt16( pData, m_wIdent ); + Set_UInt16( pData, m_nFib ); + Set_UInt16( pData, m_nProduct ); + Set_UInt16( pData, static_cast<sal_uInt16>(m_lid) ); + Set_UInt16( pData, m_pnNext ); + + sal_uInt16 nBits16 = 0; + if( m_fDot ) nBits16 |= 0x0001; + if( m_fGlsy) nBits16 |= 0x0002; + if( m_fComplex ) nBits16 |= 0x0004; + if( m_fHasPic ) nBits16 |= 0x0008; + nBits16 |= (0xf0 & ( m_cQuickSaves << 4 )); + if( m_fEncrypted ) nBits16 |= 0x0100; + if( m_fWhichTableStm ) nBits16 |= 0x0200; + + if (m_fReadOnlyRecommended) + nBits16 |= 0x0400; + if (m_fWriteReservation) + nBits16 |= 0x0800; + + if( m_fExtChar ) nBits16 |= 0x1000; + if( m_fFarEast ) nBits16 |= 0x4000; // #i90932# + if( m_fObfuscated ) nBits16 |= 0x8000; + Set_UInt16( pData, nBits16 ); + + Set_UInt16( pData, m_nFibBack ); + Set_UInt16( pData, m_nHash ); + Set_UInt16( pData, m_nKey ); + Set_UInt8( pData, m_envr ); + + sal_uInt8 nBits8 = 0; + if( bVer8 ) + { + if( m_fMac ) nBits8 |= 0x0001; + if( m_fEmptySpecial ) nBits8 |= 0x0002; + if( m_fLoadOverridePage ) nBits8 |= 0x0004; + if( m_fFuturesavedUndo ) nBits8 |= 0x0008; + if( m_fWord97Saved ) nBits8 |= 0x0010; + if( m_fWord2000Saved ) nBits8 |= 0x0020; + } + // under Ver67 these are only reserved + Set_UInt8( pData, nBits8 ); + + Set_UInt16( pData, m_chse ); + Set_UInt16( pData, m_chseTables ); + Set_UInt32( pData, m_fcMin ); + Set_UInt32( pData, m_fcMac ); + +// insertion for WW8 + + // Marke: "rgsw" Beginning of the array of shorts + if( bVer8 ) + { + Set_UInt16( pData, m_csw ); + Set_UInt16( pData, m_wMagicCreated ); + Set_UInt16( pData, m_wMagicRevised ); + Set_UInt16( pData, m_wMagicCreatedPrivate ); + Set_UInt16( pData, m_wMagicRevisedPrivate ); + pData += 9 * sizeof( sal_Int16 ); + Set_UInt16( pData, static_cast<sal_uInt16>(m_lidFE) ); + Set_UInt16( pData, m_clw ); + } + +// end of the insertion for WW8 + + // Marke: "rglw" Beginning of the array of longs + Set_UInt32( pData, m_cbMac ); + + rStrm.WriteBytes(pDataPtr.get(), nUnencryptedHdr); +} + +void WW8Fib::Write(SvStream& rStrm) +{ + bool bVer8 = 8 == m_nVersion; + + WriteHeader( rStrm ); + + size_t nUnencryptedHdr = bVer8 ? 0x44 : 0x24; + + std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ m_fcMin - nUnencryptedHdr ] ); + sal_uInt8 *pData = pDataPtr.get(); + memset( pData, 0, m_fcMin - nUnencryptedHdr ); + + m_cbMac = rStrm.TellEnd(); + + // ignore 2 longs, because they are unimportant + pData += 2 * sizeof( sal_Int32); + + // skipping 2 more longs only at Ver67 + if( !bVer8 ) + pData += 2 * sizeof( sal_Int32); + + Set_UInt32( pData, m_ccpText ); + Set_UInt32( pData, m_ccpFootnote ); + Set_UInt32( pData, m_ccpHdr ); + Set_UInt32( pData, m_ccpMcr ); + Set_UInt32( pData, m_ccpAtn ); + Set_UInt32( pData, m_ccpEdn ); + Set_UInt32( pData, m_ccpTxbx ); + Set_UInt32( pData, m_ccpHdrTxbx ); + + // only skip one more long at Ver67 + if( !bVer8 ) + pData += 1 * sizeof( sal_Int32); + +// insertion for WW8 + if( bVer8 ) + { + Set_UInt32( pData, m_pnFbpChpFirst ); + Set_UInt32( pData, m_pnChpFirst ); + Set_UInt32( pData, m_cpnBteChp ); + Set_UInt32( pData, m_pnFbpPapFirst ); + Set_UInt32( pData, m_pnPapFirst ); + Set_UInt32( pData, m_cpnBtePap ); + Set_UInt32( pData, m_pnFbpLvcFirst ); + Set_UInt32( pData, m_pnLvcFirst ); + Set_UInt32( pData, m_cpnBteLvc ); + Set_UInt32( pData, m_fcIslandFirst ); + Set_UInt32( pData, m_fcIslandLim ); + Set_UInt16( pData, m_cfclcb ); + } +// end of the insertion for WW8 + + // Marke: "rgfclcb" Beginning of array of FC/LCB pairs. + Set_UInt32( pData, m_fcStshfOrig ); + Set_UInt32( pData, m_lcbStshfOrig ); + Set_UInt32( pData, m_fcStshf ); + Set_UInt32( pData, m_lcbStshf ); + Set_UInt32( pData, m_fcPlcffndRef ); + Set_UInt32( pData, m_lcbPlcffndRef ); + Set_UInt32( pData, m_fcPlcffndText ); + Set_UInt32( pData, m_lcbPlcffndText ); + Set_UInt32( pData, m_fcPlcfandRef ); + Set_UInt32( pData, m_lcbPlcfandRef ); + Set_UInt32( pData, m_fcPlcfandText ); + Set_UInt32( pData, m_lcbPlcfandText ); + Set_UInt32( pData, m_fcPlcfsed ); + Set_UInt32( pData, m_lcbPlcfsed ); + Set_UInt32( pData, m_fcPlcfpad ); + Set_UInt32( pData, m_lcbPlcfpad ); + Set_UInt32( pData, m_fcPlcfphe ); + Set_UInt32( pData, m_lcbPlcfphe ); + Set_UInt32( pData, m_fcSttbfglsy ); + Set_UInt32( pData, m_lcbSttbfglsy ); + Set_UInt32( pData, m_fcPlcfglsy ); + Set_UInt32( pData, m_lcbPlcfglsy ); + Set_UInt32( pData, m_fcPlcfhdd ); + Set_UInt32( pData, m_lcbPlcfhdd ); + Set_UInt32( pData, m_fcPlcfbteChpx ); + Set_UInt32( pData, m_lcbPlcfbteChpx ); + Set_UInt32( pData, m_fcPlcfbtePapx ); + Set_UInt32( pData, m_lcbPlcfbtePapx ); + Set_UInt32( pData, m_fcPlcfsea ); + Set_UInt32( pData, m_lcbPlcfsea ); + Set_UInt32( pData, m_fcSttbfffn ); + Set_UInt32( pData, m_lcbSttbfffn ); + Set_UInt32( pData, m_fcPlcffldMom ); + Set_UInt32( pData, m_lcbPlcffldMom ); + Set_UInt32( pData, m_fcPlcffldHdr ); + Set_UInt32( pData, m_lcbPlcffldHdr ); + Set_UInt32( pData, m_fcPlcffldFootnote ); + Set_UInt32( pData, m_lcbPlcffldFootnote ); + Set_UInt32( pData, m_fcPlcffldAtn ); + Set_UInt32( pData, m_lcbPlcffldAtn ); + Set_UInt32( pData, m_fcPlcffldMcr ); + Set_UInt32( pData, m_lcbPlcffldMcr ); + Set_UInt32( pData, m_fcSttbfbkmk ); + Set_UInt32( pData, m_lcbSttbfbkmk ); + Set_UInt32( pData, m_fcPlcfbkf ); + Set_UInt32( pData, m_lcbPlcfbkf ); + Set_UInt32( pData, m_fcPlcfbkl ); + Set_UInt32( pData, m_lcbPlcfbkl ); + Set_UInt32( pData, m_fcCmds ); + Set_UInt32( pData, m_lcbCmds ); + Set_UInt32( pData, m_fcPlcfmcr ); + Set_UInt32( pData, m_lcbPlcfmcr ); + Set_UInt32( pData, m_fcSttbfmcr ); + Set_UInt32( pData, m_lcbSttbfmcr ); + Set_UInt32( pData, m_fcPrDrvr ); + Set_UInt32( pData, m_lcbPrDrvr ); + Set_UInt32( pData, m_fcPrEnvPort ); + Set_UInt32( pData, m_lcbPrEnvPort ); + Set_UInt32( pData, m_fcPrEnvLand ); + Set_UInt32( pData, m_lcbPrEnvLand ); + Set_UInt32( pData, m_fcWss ); + Set_UInt32( pData, m_lcbWss ); + Set_UInt32( pData, m_fcDop ); + Set_UInt32( pData, m_lcbDop ); + Set_UInt32( pData, m_fcSttbfAssoc ); + Set_UInt32( pData, m_lcbSttbfAssoc ); + Set_UInt32( pData, m_fcClx ); + Set_UInt32( pData, m_lcbClx ); + Set_UInt32( pData, m_fcPlcfpgdFootnote ); + Set_UInt32( pData, m_lcbPlcfpgdFootnote ); + Set_UInt32( pData, m_fcAutosaveSource ); + Set_UInt32( pData, m_lcbAutosaveSource ); + Set_UInt32( pData, m_fcGrpStAtnOwners ); + Set_UInt32( pData, m_lcbGrpStAtnOwners ); + Set_UInt32( pData, m_fcSttbfAtnbkmk ); + Set_UInt32( pData, m_lcbSttbfAtnbkmk ); + + // only skip one more short at Ver67 + if( !bVer8 ) + { + pData += 1*sizeof( sal_Int16); + Set_UInt16( pData, static_cast<sal_uInt16>(m_pnChpFirst) ); + Set_UInt16( pData, static_cast<sal_uInt16>(m_pnPapFirst) ); + Set_UInt16( pData, static_cast<sal_uInt16>(m_cpnBteChp) ); + Set_UInt16( pData, static_cast<sal_uInt16>(m_cpnBtePap) ); + } + + Set_UInt32( pData, m_fcPlcfdoaMom ); // only at Ver67, in Ver8 unused + Set_UInt32( pData, m_lcbPlcfdoaMom ); // only at Ver67, in Ver8 unused + Set_UInt32( pData, m_fcPlcfdoaHdr ); // only at Ver67, in Ver8 unused + Set_UInt32( pData, m_lcbPlcfdoaHdr ); // only at Ver67, in Ver8 unused + + Set_UInt32( pData, m_fcPlcfspaMom ); // in Ver67 empty reserve + Set_UInt32( pData, m_lcbPlcfspaMom ); // in Ver67 empty reserve + Set_UInt32( pData, m_fcPlcfspaHdr ); // in Ver67 empty reserve + Set_UInt32( pData, m_lcbPlcfspaHdr ); // in Ver67 empty reserve + + Set_UInt32( pData, m_fcPlcfAtnbkf ); + Set_UInt32( pData, m_lcbPlcfAtnbkf ); + Set_UInt32( pData, m_fcPlcfAtnbkl ); + Set_UInt32( pData, m_lcbPlcfAtnbkl ); + Set_UInt32( pData, m_fcPms ); + Set_UInt32( pData, m_lcbPMS ); + Set_UInt32( pData, m_fcFormFieldSttbf ); + Set_UInt32( pData, m_lcbFormFieldSttbf ); + Set_UInt32( pData, m_fcPlcfendRef ); + Set_UInt32( pData, m_lcbPlcfendRef ); + Set_UInt32( pData, m_fcPlcfendText ); + Set_UInt32( pData, m_lcbPlcfendText ); + Set_UInt32( pData, m_fcPlcffldEdn ); + Set_UInt32( pData, m_lcbPlcffldEdn ); + Set_UInt32( pData, m_fcPlcfpgdEdn ); + Set_UInt32( pData, m_lcbPlcfpgdEdn ); + Set_UInt32( pData, m_fcDggInfo ); // in Ver67 empty reserve + Set_UInt32( pData, m_lcbDggInfo ); // in Ver67 empty reserve + Set_UInt32( pData, m_fcSttbfRMark ); + Set_UInt32( pData, m_lcbSttbfRMark ); + Set_UInt32( pData, m_fcSttbfCaption ); + Set_UInt32( pData, m_lcbSttbfCaption ); + Set_UInt32( pData, m_fcSttbAutoCaption ); + Set_UInt32( pData, m_lcbSttbAutoCaption ); + Set_UInt32( pData, m_fcPlcfwkb ); + Set_UInt32( pData, m_lcbPlcfwkb ); + Set_UInt32( pData, m_fcPlcfspl ); // in Ver67 empty reserve + Set_UInt32( pData, m_lcbPlcfspl ); // in Ver67 empty reserve + Set_UInt32( pData, m_fcPlcftxbxText ); + Set_UInt32( pData, m_lcbPlcftxbxText ); + Set_UInt32( pData, m_fcPlcffldTxbx ); + Set_UInt32( pData, m_lcbPlcffldTxbx ); + Set_UInt32( pData, m_fcPlcfHdrtxbxText ); + Set_UInt32( pData, m_lcbPlcfHdrtxbxText ); + Set_UInt32( pData, m_fcPlcffldHdrTxbx ); + Set_UInt32( pData, m_lcbPlcffldHdrTxbx ); + + if( bVer8 ) + { + pData += 0x2da - 0x27a; // Pos + Offset (fcPlcfLst - fcStwUser) + Set_UInt32( pData, m_fcSttbFnm); + Set_UInt32( pData, m_lcbSttbFnm); + Set_UInt32( pData, m_fcPlcfLst ); + Set_UInt32( pData, m_lcbPlcfLst ); + Set_UInt32( pData, m_fcPlfLfo ); + Set_UInt32( pData, m_lcbPlfLfo ); + Set_UInt32( pData, m_fcPlcftxbxBkd ); + Set_UInt32( pData, m_lcbPlcftxbxBkd ); + Set_UInt32( pData, m_fcPlcfHdrtxbxBkd ); + Set_UInt32( pData, m_lcbPlcfHdrtxbxBkd ); + + pData += 0x372 - 0x302; // Pos + Offset (fcSttbListNames - fcDocUndo) + Set_UInt32( pData, m_fcSttbListNames ); + Set_UInt32( pData, m_lcbSttbListNames ); + + pData += 0x382 - 0x37A; + Set_UInt32( pData, m_fcPlcfTch ); + Set_UInt32( pData, m_lcbPlcfTch ); + + pData += 0x3FA - 0x38A; + Set_UInt16( pData, sal_uInt16(0x0002)); + Set_UInt16( pData, sal_uInt16(0x00D9)); + + pData += 0x41A - 0x3FE; + Set_UInt32( pData, m_fcAtrdExtra ); + Set_UInt32( pData, m_lcbAtrdExtra ); + + pData += 0x42a - 0x422; + Set_UInt32(pData, m_fcSttbfBkmkFactoid); + Set_UInt32(pData, m_lcbSttbfBkmkFactoid); + Set_UInt32(pData, m_fcPlcfBkfFactoid); + Set_UInt32(pData, m_lcbPlcfBkfFactoid); + + pData += 0x442 - 0x43A; + Set_UInt32(pData, m_fcPlcfBklFactoid); + Set_UInt32(pData, m_lcbPlcfBklFactoid); + Set_UInt32(pData, m_fcFactoidData); + Set_UInt32(pData, m_lcbFactoidData); + + pData += 0x4BA - 0x452; + Set_UInt32(pData, m_fcPlcffactoid); + Set_UInt32(pData, m_lcbPlcffactoid); + + pData += 0x4DA - 0x4c2; + Set_UInt32( pData, m_fcHplxsdr ); + Set_UInt32( pData, 0); + } + + rStrm.WriteBytes(pDataPtr.get(), m_fcMin - nUnencryptedHdr); +} + +rtl_TextEncoding WW8Fib::GetFIBCharset(sal_uInt16 chs, LanguageType nLidLocale) +{ + OSL_ENSURE(chs <= 0x100, "overflowed winword charset set"); + if (chs == 0x0100) + return RTL_TEXTENCODING_APPLE_ROMAN; + if (chs == 0 && static_cast<sal_uInt16>(nLidLocale) >= 999) + { + /* + nLidLocale: + language stamp -- localized version In pre-WinWord 2.0 files this + value was the nLocale. If value is < 999, then it is the nLocale, + otherwise it is the lid. + */ + css::lang::Locale aLocale(LanguageTag::convertToLocale(nLidLocale)); + return msfilter::util::getBestTextEncodingFromLocale(aLocale); + } + return rtl_getTextEncodingFromWindowsCharset(static_cast<sal_uInt8>(chs)); +} + +MSOFactoidType::MSOFactoidType() + : m_nId(0) +{ +} + +namespace MSOPBString +{ +static OUString Read(SvStream& rStream) +{ + OUString aRet; + + sal_uInt16 nBuf(0); + rStream.ReadUInt16(nBuf); + sal_uInt16 nCch = nBuf & 0x7fff; // Bits 1..15. + bool bAnsiString = (nBuf & (1 << 15)) >> 15; // 16th bit. + if (bAnsiString) + aRet = OStringToOUString(read_uInt8s_ToOString(rStream, nCch), RTL_TEXTENCODING_ASCII_US); + else + aRet = read_uInt16s_ToOUString(rStream, nCch); + + return aRet; +} + +static void Write(const OUString& rString, SvStream& rStream) +{ + sal_uInt16 nBuf = 0; + nBuf |= rString.getLength(); // cch, 0..14th bits. + nBuf |= 0x8000; // fAnsiString, 15th bit. + rStream.WriteUInt16(nBuf); + SwWW8Writer::WriteString8(rStream, rString, false, RTL_TEXTENCODING_ASCII_US); +} +}; + +void MSOFactoidType::Read(SvStream& rStream) +{ + sal_uInt32 cbFactoid(0); + rStream.ReadUInt32(cbFactoid); + rStream.ReadUInt32(m_nId); + m_aUri = MSOPBString::Read(rStream); + m_aTag = MSOPBString::Read(rStream); + MSOPBString::Read(rStream); // rgbDownloadURL +} + +void MSOFactoidType::Write(WW8Export& rExport) +{ + SvStream& rStream = *rExport.pTableStrm; + + SvMemoryStream aStream; + aStream.WriteUInt32(m_nId); // id + MSOPBString::Write(m_aUri, aStream); + MSOPBString::Write(m_aTag, aStream); + MSOPBString::Write("", aStream); // rgbDownloadURL + rStream.WriteUInt32(aStream.Tell()); + aStream.Seek(0); + rStream.WriteStream(aStream); +} + +void MSOPropertyBagStore::Read(SvStream& rStream) +{ + sal_uInt32 cFactoidType(0); + rStream.ReadUInt32(cFactoidType); + for (sal_uInt32 i = 0; i < cFactoidType && rStream.good(); ++i) + { + MSOFactoidType aFactoidType; + aFactoidType.Read(rStream); + m_aFactoidTypes.push_back(aFactoidType); + } + sal_uInt16 cbHdr(0); + rStream.ReadUInt16(cbHdr); + SAL_WARN_IF(cbHdr != 0xc, "sw.ww8", "MSOPropertyBagStore::Read: unexpected cbHdr"); + sal_uInt16 nVer(0); + rStream.ReadUInt16(nVer); + SAL_WARN_IF(nVer != 0x0100, "sw.ww8", "MSOPropertyBagStore::Read: unexpected nVer"); + rStream.SeekRel(4); // cfactoid + sal_uInt32 nCste(0); + rStream.ReadUInt32(nCste); + + //each string has a 2 byte len record at the start + const size_t nMaxPossibleRecords = rStream.remainingSize() / sizeof(sal_uInt16); + if (nCste > nMaxPossibleRecords) + { + SAL_WARN("sw.ww8", nCste << " records claimed, but max possible is " << nMaxPossibleRecords); + nCste = nMaxPossibleRecords; + } + + for (sal_uInt32 i = 0; i < nCste; ++i) + { + OUString aString = MSOPBString::Read(rStream); + m_aStringTable.push_back(aString); + } +} + +void MSOPropertyBagStore::Write(WW8Export& rExport) +{ + SvStream& rStream = *rExport.pTableStrm; + rStream.WriteUInt32(m_aFactoidTypes.size()); // cFactoidType + for (MSOFactoidType& rType : m_aFactoidTypes) + rType.Write(rExport); + rStream.WriteUInt16(0xc); // cbHdr + rStream.WriteUInt16(0x0100); // sVer + rStream.WriteUInt32(0); // cfactoid + rStream.WriteUInt32(m_aStringTable.size()); // cste + for (const OUString& rString : m_aStringTable) + MSOPBString::Write(rString, rStream); +} + +MSOProperty::MSOProperty() + : m_nKey(0) + , m_nValue(0) +{ +} + +void MSOProperty::Read(SvStream& rStream) +{ + rStream.ReadUInt32(m_nKey); + rStream.ReadUInt32(m_nValue); +} + +void MSOProperty::Write(SvStream& rStream) +{ + rStream.WriteUInt32(m_nKey); + rStream.WriteUInt32(m_nValue); +} + +MSOPropertyBag::MSOPropertyBag() + : m_nId(0) +{ +} + +bool MSOPropertyBag::Read(SvStream& rStream) +{ + rStream.ReadUInt16(m_nId); + sal_uInt16 cProp(0); + rStream.ReadUInt16(cProp); + if (!rStream.good()) + return false; + rStream.SeekRel(2); // cbUnknown + //each MSOProperty is 8 bytes in size + const size_t nMaxPossibleRecords = rStream.remainingSize() / 8; + if (cProp > nMaxPossibleRecords) + { + SAL_WARN("sw.ww8", cProp << " records claimed, but max possible is " << nMaxPossibleRecords); + cProp = nMaxPossibleRecords; + } + for (sal_uInt16 i = 0; i < cProp && rStream.good(); ++i) + { + MSOProperty aProperty; + aProperty.Read(rStream); + m_aProperties.push_back(aProperty); + } + return rStream.good(); +} + +void MSOPropertyBag::Write(WW8Export& rExport) +{ + SvStream& rStream = *rExport.pTableStrm; + rStream.WriteUInt16(m_nId); + rStream.WriteUInt16(m_aProperties.size()); + rStream.WriteUInt16(0); // cbUnknown + for (MSOProperty& rProperty : m_aProperties) + rProperty.Write(rStream); +} + +void WW8SmartTagData::Read(SvStream& rStream, WW8_FC fcFactoidData, sal_uInt32 lcbFactoidData) +{ + sal_uInt64 nOldPosition = rStream.Tell(); + if (!checkSeek(rStream, fcFactoidData)) + return; + + m_aPropBagStore.Read(rStream); + while (rStream.good() && rStream.Tell() < fcFactoidData + lcbFactoidData) + { + MSOPropertyBag aPropertyBag; + if (!aPropertyBag.Read(rStream)) + break; + m_aPropBags.push_back(aPropertyBag); + } + + rStream.Seek(nOldPosition); +} + +void WW8SmartTagData::Write(WW8Export& rExport) +{ + m_aPropBagStore.Write(rExport); + for (MSOPropertyBag& rPropertyBag : m_aPropBags) + rPropertyBag.Write(rExport); +} + +WW8Style::WW8Style(SvStream& rStream, WW8Fib& rFibPara) + : m_rFib(rFibPara), m_rStream(rStream), m_cstd(0), m_cbSTDBaseInFile(0), m_fStdStylenamesWritten(0) + , m_stiMaxWhenSaved(0), m_istdMaxFixedWhenSaved(0), m_nVerBuiltInNamesWhenSaved(0) + , m_ftcAsci(0), m_ftcFE(0), m_ftcOther(0), m_ftcBi(0) +{ + if (!checkSeek(m_rStream, m_rFib.m_fcStshf)) + return; + + sal_uInt16 cbStshi = 0; // 2 bytes size of the following STSHI structure + sal_uInt32 nRemaining = m_rFib.m_lcbStshf; + const sal_uInt32 nMinValidStshi = 4; + + if (m_rFib.GetFIBVersion() <= ww::eWW2) + { + cbStshi = 0; + m_cstd = 256; + } + else + { + if (m_rFib.m_nFib < 67) // old Version ? (need to find this again to fix) + cbStshi = nMinValidStshi; + else // new version + { + if (nRemaining < sizeof(cbStshi)) + return; + // reads the length of the structure in the file + m_rStream.ReadUInt16( cbStshi ); + nRemaining-=2; + } + } + + cbStshi = std::min(static_cast<sal_uInt32>(cbStshi), nRemaining); + if (cbStshi < nMinValidStshi) + return; + + const sal_uInt16 nRead = cbStshi; + do + { + m_rStream.ReadUInt16( m_cstd ); + + m_rStream.ReadUInt16( m_cbSTDBaseInFile ); + + if( 6 > nRead ) break; + + sal_uInt16 a16Bit; + m_rStream.ReadUInt16( a16Bit ); + m_fStdStylenamesWritten = a16Bit & 0x0001; + + if( 8 > nRead ) break; + m_rStream.ReadUInt16( m_stiMaxWhenSaved ); + + if( 10 > nRead ) break; + m_rStream.ReadUInt16( m_istdMaxFixedWhenSaved ); + + if( 12 > nRead ) break; + m_rStream.ReadUInt16( m_nVerBuiltInNamesWhenSaved ); + + if( 14 > nRead ) break; + m_rStream.ReadUInt16( m_ftcAsci ); + + if( 16 > nRead ) break; + m_rStream.ReadUInt16( m_ftcFE ); + + if ( 18 > nRead ) break; + m_rStream.ReadUInt16( m_ftcOther ); + + m_ftcBi = m_ftcOther; + + if ( 20 > nRead ) break; + m_rStream.ReadUInt16( m_ftcBi ); + + // p.r.n. ignore the rest + if( 20 < nRead ) + m_rStream.SeekRel( nRead-20 ); + } + while( false ); // trick: the block above will be passed through exactly one time + // and that's why we can early exit with "break". + + nRemaining -= cbStshi; + + //There will be stshi.cstd (cbSTD, STD) pairs in the file following the + //STSHI. Note that styles can be empty, i.e. cbSTD == 0 + const sal_uInt32 nMinRecordSize = sizeof(sal_uInt16); + const sal_uInt16 nMaxPossibleRecords = nRemaining/nMinRecordSize; + + OSL_ENSURE(m_cstd <= nMaxPossibleRecords, + "allegedly more styles that available data"); + m_cstd = std::min(m_cstd, nMaxPossibleRecords); +} + +// Read1STDFixed() reads a style. If the style is completely existent, +// so it has no empty slot, we should allocate memory and a pointer should +// reference to STD (perhaps filled with 0). If the slot is empty, +// it will return a null pointer. +std::unique_ptr<WW8_STD> WW8Style::Read1STDFixed(sal_uInt16& rSkip) +{ + std::unique_ptr<WW8_STD> pStd; + + sal_uInt16 cbStd(0); + m_rStream.ReadUInt16(cbStd); // read length + + const sal_uInt16 nRead = m_cbSTDBaseInFile; + if( cbStd >= m_cbSTDBaseInFile ) + { + // Fixed part completely available + + // read fixed part of STD + pStd.reset(new WW8_STD); + memset( pStd.get(), 0, sizeof( *pStd ) ); + + do + { + if( 2 > nRead ) break; + + sal_uInt16 a16Bit = 0; + m_rStream.ReadUInt16( a16Bit ); + pStd->sti = a16Bit & 0x0fff ; + pStd->fScratch = sal_uInt16(0 != ( a16Bit & 0x1000 )); + pStd->fInvalHeight = sal_uInt16(0 != ( a16Bit & 0x2000 )); + pStd->fHasUpe = sal_uInt16(0 != ( a16Bit & 0x4000 )); + pStd->fMassCopy = sal_uInt16(0 != ( a16Bit & 0x8000 )); + + if( 4 > nRead ) break; + a16Bit = 0; + m_rStream.ReadUInt16( a16Bit ); + pStd->sgc = a16Bit & 0x000f ; + pStd->istdBase = ( a16Bit & 0xfff0 ) >> 4; + + if( 6 > nRead ) break; + a16Bit = 0; + m_rStream.ReadUInt16( a16Bit ); + pStd->cupx = a16Bit & 0x000f ; + pStd->istdNext = ( a16Bit & 0xfff0 ) >> 4; + + if( 8 > nRead ) break; + m_rStream.ReadUInt16( pStd->bchUpe ); + + // from Ver8 this two fields should be added: + if(10 > nRead ) break; + a16Bit = 0; + m_rStream.ReadUInt16( a16Bit ); + pStd->fAutoRedef = a16Bit & 0x0001 ; + pStd->fHidden = ( a16Bit & 0x0002 ) >> 1; + // You never know: cautionary skipped + if (nRead > 10) + { + auto nSkip = std::min<sal_uInt64>(nRead - 10, m_rStream.remainingSize()); + m_rStream.Seek(m_rStream.Tell() + nSkip); + } + } + while( false ); // trick: the block above will passed through exactly one time + // and can be left early with a "break" + + if (!m_rStream.good() || !nRead) + { + pStd.reset(); // report error with NULL + } + + rSkip = cbStd - m_cbSTDBaseInFile; + } + else + { // Fixed part too short + if( cbStd ) + m_rStream.SeekRel( cbStd ); // skip leftovers + rSkip = 0; + } + return pStd; +} + +std::unique_ptr<WW8_STD> WW8Style::Read1Style(sal_uInt16& rSkip, OUString* pString) +{ + // Attention: MacWord-Documents have their Stylenames + // always in ANSI, even if eStructCharSet == CHARSET_MAC !! + + std::unique_ptr<WW8_STD> pStd = Read1STDFixed(rSkip); // read STD + + // string desired? + if( pString ) + { // real style? + if ( pStd ) + { + sal_Int32 nLenStringBytes = 0; + switch( m_rFib.m_nVersion ) + { + case 6: + case 7: + // read pascal string + *pString = read_uInt8_BeltAndBracesString(m_rStream, RTL_TEXTENCODING_MS_1252); + // leading len and trailing zero --> 2 + nLenStringBytes = pString->getLength() + 2; + break; + case 8: + // handle Unicode-String with leading length short and + // trailing zero + if (TestBeltAndBraces(m_rStream)) + { + *pString = read_uInt16_BeltAndBracesString(m_rStream); + nLenStringBytes = (pString->getLength() + 2) * 2; + } + else + { + /* + #i8114# + This is supposed to be impossible, it's just supposed + to be 16 bit count followed by the string and ending + in a 0 short. But "Lotus SmartSuite Product: Word Pro" + is creating invalid style names in ww7- format. So we + use the belt and braces of the ms strings to see if + they are not corrupt. If they are then we try them as + 8bit ones + */ + *pString = read_uInt8_BeltAndBracesString(m_rStream,RTL_TEXTENCODING_MS_1252); + // leading len and trailing zero --> 2 + nLenStringBytes = pString->getLength() + 2; + } + break; + default: + OSL_ENSURE(false, "It was forgotten to code nVersion!"); + break; + } + if (nLenStringBytes > rSkip) + { + SAL_WARN("sw.ww8", "WW8Style structure corrupt"); + nLenStringBytes = rSkip; + } + rSkip -= nLenStringBytes; + } + else + pString->clear(); // can not return a name + } + return pStd; +} + +namespace { +const sal_uInt16 maxStrSize = 65; + +struct WW8_FFN_Ver6 +{ + WW8_FFN_BASE base; + // from Ver6 + char szFfn[maxStrSize]; // 0x6 or 0x40 from Ver8 on zero terminated string that + // records name of font. + // Maximal size of szFfn is 65 characters. + // Attention: This array can also be smaller!!! + // Possibly followed by a second sz which records the + // name of an alternate font to use if the first named + // font does not exist on this system. +}; + +} + +// #i43762# check font name for illegal characters +static void lcl_checkFontname( OUString& sString ) +{ + // for efficiency, we'd like to use String methods as far as possible. + // Hence, we will: + // 1) convert all invalid chars to \u0001 + // 2) then erase all \u0001 chars (if anywhere found), and + // 3) erase leading/trailing ';', in case a font name was + // completely removed + + // convert all invalid chars to \u0001 + OUStringBuffer aBuf(sString); + const sal_Int32 nLen = aBuf.getLength(); + bool bFound = false; + for ( sal_Int32 n = 0; n < nLen; ++n ) + { + if ( aBuf[n] < 0x20 ) + { + aBuf[n] = 1; + bFound = true; + } + } + sString = aBuf.makeStringAndClear(); + + // if anything was found, remove \u0001 + leading/trailing ';' + if( bFound ) + { + sString = comphelper::string::strip(sString.replaceAll("\001", ""), ';'); + } +} + +namespace +{ + sal_uInt16 calcMaxFonts(sal_uInt8 *p, sal_Int32 nFFn) + { + // Figure out the max number of fonts defined here + sal_uInt16 nMax = 0; + sal_Int32 nRemaining = nFFn; + while (nRemaining) + { + //p[0] is cbFfnM1, the alleged total length of FFN - 1. + //i.e. length after cbFfnM1 + const sal_uInt16 cbFfnM1 = *p++; + --nRemaining; + + if (cbFfnM1 > nRemaining) + break; + + nMax++; + nRemaining -= cbFfnM1; + p += cbFfnM1; + } + return nMax; + } + + template<typename T> bool readU8( + sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd, + T * value) + { + assert(p <= pEnd); + assert(value != nullptr); + if (offset >= o3tl::make_unsigned(pEnd - p)) { + return false; + } + *value = p[offset]; + return true; + } + + bool readS16( + sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd, + short * value) + { + assert(p <= pEnd); + assert(value != nullptr); + if (offset > o3tl::make_unsigned(pEnd - p) + || static_cast<std::size_t>(pEnd - p) - offset < 2) + { + return false; + } + *value = unsigned(p[offset]) + (unsigned(p[offset + 1]) << 8); + return true; + } + + sal_Int32 getStringLength( + sal_uInt8 const * p, std::size_t offset, sal_uInt8 const * pEnd) + { + assert(p <= pEnd); + assert(pEnd - p <= SAL_MAX_INT32); + if (offset >= o3tl::make_unsigned(pEnd - p)) { + return -1; + } + void const * p2 = std::memchr( + p + offset, 0, static_cast<std::size_t>(pEnd - p) - offset); + if (p2 == nullptr) { + return -1; + } + return static_cast<sal_uInt8 const *>(p2) - (p + offset); + } +} + +WW8Fonts::WW8Fonts( SvStream& rSt, WW8Fib const & rFib ) +{ + // Attention: MacWord-Documents have their Fontnames + // always in ANSI, even if eStructCharSet == CHARSET_MAC !! + if( rFib.m_lcbSttbfffn <= 2 ) + { + OSL_ENSURE( false, "font table is broken! (rFib.lcbSttbfffn < 2)" ); + return; + } + + if (!checkSeek(rSt, rFib.m_fcSttbfffn)) + return; + + sal_Int32 nFFn = rFib.m_lcbSttbfffn - 2; + + const sal_uInt64 nMaxPossible = rSt.remainingSize(); + if (o3tl::make_unsigned(nFFn) > nMaxPossible) + { + SAL_WARN("sw.ww8", "FFN structure longer than available data"); + nFFn = nMaxPossible; + } + + // allocate Font Array + std::vector<sal_uInt8> aA(nFFn); + memset(aA.data(), 0, nFFn); + + ww::WordVersion eVersion = rFib.GetFIBVersion(); + + sal_uInt16 nMax(0); + if( eVersion >= ww::eWW8 ) + { + // bVer8: read the count of strings in nMax + rSt.ReadUInt16(nMax); + } + + // Ver8: skip undefined uint16 + // Ver67: skip the herein stored total byte of structure + // - we already got that information in rFib.lcbSttbfffn + rSt.SeekRel( 2 ); + + // read all font information + nFFn = rSt.ReadBytes(aA.data(), nFFn); + sal_uInt8 * const pEnd = aA.data() + nFFn; + const sal_uInt16 nCalcMax = calcMaxFonts(aA.data(), nFFn); + + if (eVersion < ww::eWW8) + nMax = nCalcMax; + else + { + //newer versions include supportive count of fonts, so take min of that + //and calced max + nMax = std::min(nMax, nCalcMax); + } + + if (nMax) + { + // allocate Index Array + m_aFontA.resize(nMax); + WW8_FFN* p = m_aFontA.data(); + + if( eVersion <= ww::eWW2 ) + { + sal_uInt8 const * pVer2 = aA.data(); + sal_uInt16 i = 0; + for(; i<nMax; ++i, ++p) + { + if (!readU8( + pVer2, offsetof(WW8_FFN_BASE, cbFfnM1), pEnd, + &p->aFFNBase.cbFfnM1)) + { + break; + } + + p->aFFNBase.prg = 0; + p->aFFNBase.fTrueType = 0; + p->aFFNBase.ff = 0; + + if (!(readU8(pVer2, 1, pEnd, &p->aFFNBase.wWeight) + && readU8(pVer2, 2, pEnd, &p->aFFNBase.chs))) + { + break; + } + /* + #i8726# 7- seems to encode the name in the same encoding as + the font, e.g load the doc in 97 and save to see the unicode + ver of the asian fontnames in that example to confirm. + */ + rtl_TextEncoding eEnc = WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid); + if ((eEnc == RTL_TEXTENCODING_SYMBOL) || (eEnc == RTL_TEXTENCODING_DONTKNOW)) + eEnc = RTL_TEXTENCODING_MS_1252; + + sal_Int32 n = getStringLength(pVer2, 1 + 2, pEnd); + if (n == -1) { + break; + } + p->sFontname = OUString( + reinterpret_cast<char const *>(pVer2 + 1 + 2), n, eEnc); + pVer2 = pVer2 + p->aFFNBase.cbFfnM1 + 1; + } + nMax = i; + } + else if( eVersion < ww::eWW8 ) + { + sal_uInt8 const * pVer6 = aA.data(); + sal_uInt16 i = 0; + for(; i<nMax; ++i, ++p) + { + if (!readU8( + pVer6, offsetof(WW8_FFN_BASE, cbFfnM1), pEnd, + &p->aFFNBase.cbFfnM1)) + { + break; + } + sal_uInt8 c2; + if (!readU8(pVer6, 1, pEnd, &c2)) { + break; + } + + p->aFFNBase.prg = c2 & 0x02; + p->aFFNBase.fTrueType = (c2 & 0x04) >> 2; + // skip a reserve bit + p->aFFNBase.ff = (c2 & 0x70) >> 4; + + if (!(readS16( + pVer6, offsetof(WW8_FFN_BASE, wWeight), pEnd, + &p->aFFNBase.wWeight) + && readU8( + pVer6, offsetof(WW8_FFN_BASE, chs), pEnd, &p->aFFNBase.chs) + && readU8( + pVer6, offsetof(WW8_FFN_BASE, ibszAlt), pEnd, + &p->aFFNBase.ibszAlt))) + { + break; + } + /* + #i8726# 7- seems to encode the name in the same encoding as + the font, e.g load the doc in 97 and save to see the unicode + ver of the asian fontnames in that example to confirm. + */ + rtl_TextEncoding eEnc = WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid); + if ((eEnc == RTL_TEXTENCODING_SYMBOL) || (eEnc == RTL_TEXTENCODING_DONTKNOW)) + eEnc = RTL_TEXTENCODING_MS_1252; + sal_Int32 n = getStringLength( + pVer6, offsetof(WW8_FFN_Ver6, szFfn), pEnd); + if (n == -1) { + break; + } + p->sFontname = OUString( + reinterpret_cast<char const *>( + pVer6 + offsetof(WW8_FFN_Ver6, szFfn)), + n, eEnc); + if (p->aFFNBase.ibszAlt && p->aFFNBase.ibszAlt < maxStrSize) //don't start after end of string + { + n = getStringLength( + pVer6, offsetof(WW8_FFN_Ver6, szFfn) + p->aFFNBase.ibszAlt, + pEnd); + if (n == -1) { + break; + } + p->sFontname += ";" + OUString( + reinterpret_cast<char const *>( + pVer6 + offsetof(WW8_FFN_Ver6, szFfn) + p->aFFNBase.ibszAlt), + n, eEnc); + } + else + { + //#i18369# if it's a symbol font set Symbol as fallback + if ( + RTL_TEXTENCODING_SYMBOL == WW8Fib::GetFIBCharset(p->aFFNBase.chs, rFib.m_lid) + && p->sFontname!="Symbol" + ) + { + p->sFontname += ";Symbol"; + } + } + pVer6 = pVer6 + p->aFFNBase.cbFfnM1 + 1; + } + nMax = i; + } + else + { + //count of bytes in minimum FontFamilyInformation payload + const sal_uInt8 cbMinFFNPayload = 41; + sal_uInt16 nValidFonts = 0; + sal_Int32 nRemainingFFn = nFFn; + sal_uInt8* pRaw = aA.data(); + for (sal_uInt16 i=0; i < nMax && nRemainingFFn; ++i, ++p) + { + //pRaw[0] is cbFfnM1, the alleged total length of FFN - 1 + //i.e. length after cbFfnM1 + sal_uInt8 cbFfnM1 = *pRaw++; + --nRemainingFFn; + + if (cbFfnM1 > nRemainingFFn) + break; + + if (cbFfnM1 < cbMinFFNPayload) + break; + + p->aFFNBase.cbFfnM1 = cbFfnM1; + + sal_uInt8 *pVer8 = pRaw; + + sal_uInt8 c2 = *pVer8++; + --cbFfnM1; + + p->aFFNBase.prg = c2 & 0x02; + p->aFFNBase.fTrueType = (c2 & 0x04) >> 2; + // skip a reserve bit + p->aFFNBase.ff = (c2 & 0x70) >> 4; + + p->aFFNBase.wWeight = SVBT16ToUInt16(*reinterpret_cast<SVBT16*>(pVer8)); + pVer8+=2; + cbFfnM1-=2; + + p->aFFNBase.chs = *pVer8++; + --cbFfnM1; + + p->aFFNBase.ibszAlt = *pVer8++; + --cbFfnM1; + + pVer8 += 10; //PANOSE + cbFfnM1-=10; + pVer8 += 24; //FONTSIGNATURE + cbFfnM1-=24; + + assert(cbFfnM1 >= 2); + + sal_uInt8 nMaxNullTerminatedPossible = cbFfnM1/2 - 1; + sal_Unicode *pPrimary = reinterpret_cast<sal_Unicode*>(pVer8); + pPrimary[nMaxNullTerminatedPossible] = 0; +#ifdef OSL_BIGENDIAN + swapEndian(pPrimary); +#endif + p->sFontname = pPrimary; + if (p->aFFNBase.ibszAlt && p->aFFNBase.ibszAlt < nMaxNullTerminatedPossible) + { + sal_Unicode *pSecondary = pPrimary + p->aFFNBase.ibszAlt; +#ifdef OSL_BIGENDIAN + swapEndian(pSecondary); +#endif + p->sFontname += OUStringLiteral(";") + pSecondary; + } + + // #i43762# check font name for illegal characters + lcl_checkFontname( p->sFontname ); + + // set pointer one font back to original array + pRaw += p->aFFNBase.cbFfnM1; + nRemainingFFn -= p->aFFNBase.cbFfnM1; + ++nValidFonts; + } + OSL_ENSURE(nMax == nValidFonts, "Font count differs with availability"); + nMax = std::min(nMax, nValidFonts); + } + } + m_aFontA.resize(nMax); + m_aFontA.shrink_to_fit(); +} + +const WW8_FFN* WW8Fonts::GetFont( sal_uInt16 nNum ) const +{ + if (nNum >= m_aFontA.size()) + return nullptr; + + return &m_aFontA[nNum]; +} + +// Search after a header/footer for an index in the ww list from header/footer + +// specials for WinWord6 and -7: +// +// 1) At the start of reading we must build WWPLCF_HdFt with Fib and Dop +// 2) The main text must be read sequentially over all sections +// 3) For every header/footer in the main text, we must call UpdateIndex() +// exactly once with the parameter from the attribute. +// (per section can be maximally one). This call must take place *after* +// the last call from GetTextPos(). +// 4) GetTextPos() can be called with exactly one flag +// out of WW8_{FOOTER,HEADER}_{ODD,EVEN,FIRST} (Do not change!) +// -> maybe we can get a right result then + +WW8PLCF_HdFt::WW8PLCF_HdFt( SvStream* pSt, WW8Fib const & rFib, WW8Dop const & rDop ) + : aPLCF(*pSt, rFib.m_fcPlcfhdd , rFib.m_lcbPlcfhdd , 0) +{ + nIdxOffset = 0; + + /* + This dop.grpfIhdt has a bit set for each special + footnote *and endnote!!* separator,continuation separator, and + continuation notice entry, the documentation does not mention the + endnote separators, the documentation also gets the index numbers + backwards when specifying which bits to test. The bottom six bits + of this value must be tested and skipped over. Each section's + grpfIhdt is then tested for the existence of the appropriate headers + and footers, at the end of each section the nIdxOffset must be updated + to point to the beginning of the next section's group of headers and + footers in this PLCF, UpdateIndex does that task. + */ + for( sal_uInt8 nI = 0x1; nI <= 0x20; nI <<= 1 ) + if( nI & rDop.grpfIhdt ) // bit set? + nIdxOffset++; +} + +bool WW8PLCF_HdFt::GetTextPos(sal_uInt8 grpfIhdt, sal_uInt8 nWhich, WW8_CP& rStart, + WW8_CP& rLen) +{ + sal_uInt8 nI = 0x01; + short nIdx = nIdxOffset; + while (true) + { + if( nI & nWhich ) + break; // found + if( grpfIhdt & nI ) + nIdx++; // uninteresting Header / Footer + nI <<= 1; // text next bit + if( nI > 0x20 ) + return false; // not found + } + // nIdx is HdFt-Index + WW8_CP nEnd; + void* pData; + + aPLCF.SetIdx( nIdx ); // Lookup suitable CP + aPLCF.Get( rStart, nEnd, pData ); + if (nEnd < rStart) + { + SAL_WARN("sw.ww8", "End " << nEnd << " before Start " << rStart); + return false; + } + + bool bFail = o3tl::checked_sub(nEnd, rStart, rLen); + if (bFail) + { + SAL_WARN("sw.ww8", "broken offset, ignoring"); + return false; + } + + aPLCF.advance(); + + return true; +} + +void WW8PLCF_HdFt::GetTextPosExact(short nIdx, WW8_CP& rStart, WW8_CP& rLen) +{ + WW8_CP nEnd; + void* pData; + + aPLCF.SetIdx( nIdx ); // Lookup suitable CP + aPLCF.Get( rStart, nEnd, pData ); + if (nEnd < rStart) + { + SAL_WARN("sw.ww8", "End " << nEnd << " before Start " << rStart); + rLen = 0; + return; + } + if (o3tl::checked_sub(nEnd, rStart, rLen)) + { + SAL_WARN("sw.ww8", "GetTextPosExact overflow"); + rLen = 0; + } +} + +void WW8PLCF_HdFt::UpdateIndex( sal_uInt8 grpfIhdt ) +{ + // Caution: Description is not correct + for( sal_uInt8 nI = 0x01; nI <= 0x20; nI <<= 1 ) + if( nI & grpfIhdt ) + nIdxOffset++; +} + +WW8Dop::WW8Dop(SvStream& rSt, sal_Int16 nFib, sal_Int32 nPos, sal_uInt32 nSize): + fFacingPages(false), fWidowControl(false), fPMHMainDoc(false), grfSuppression(0), fpc(0), + grpfIhdt(0), rncFootnote(0), nFootnote(0), fOutlineDirtySave(false), fOnlyMacPics(false), + fOnlyWinPics(false), fLabelDoc(false), fHyphCapitals(false), fAutoHyphen(false), + fFormNoFields(false), fLinkStyles(false), fRevMarking(false), fBackup(false), + fExactCWords(false), fPagHidden(false), fPagResults(false), fLockAtn(false), + fMirrorMargins(false), fReadOnlyRecommended(false), fDfltTrueType(false), + fPagSuppressTopSpacing(false), fProtEnabled(false), fDispFormFieldSel(false), fRMView(false), + fRMPrint(false), fWriteReservation(false), fLockRev(false), fEmbedFonts(false), + copts_fNoTabForInd(false), copts_fNoSpaceRaiseLower(false), copts_fSupressSpbfAfterPgBrk(false), + copts_fWrapTrailSpaces(false), copts_fMapPrintTextColor(false), copts_fNoColumnBalance(false), + copts_fConvMailMergeEsc(false), copts_fSupressTopSpacing(false), + copts_fOrigWordTableRules(false), copts_fTransparentMetafiles(false), + copts_fShowBreaksInFrames(false), copts_fSwapBordersFacingPgs(false), copts_fExpShRtn(false), + rncEdn(0), nEdn(0), epc(0), fPrintFormData(false), fSaveFormData(false), fShadeFormData(false), + fWCFootnoteEdn(false), wvkSaved(0), wScaleSaved(0), zkSaved(0), fRotateFontW6(false), + iGutterPos(false), fNoTabForInd(false), fNoSpaceRaiseLower(false), + fSupressSpbfAfterPageBreak(false), fWrapTrailSpaces(false), fMapPrintTextColor(false), + fNoColumnBalance(false), fConvMailMergeEsc(false), fSupressTopSpacing(false), + fOrigWordTableRules(false), fTransparentMetafiles(false), fShowBreaksInFrames(false), + fSwapBordersFacingPgs(false), fCompatibilityOptions_Unknown1_13(false), fExpShRtn(false), + fCompatibilityOptions_Unknown1_15(false), fCompatibilityOptions_Unknown1_16(false), + fSuppressTopSpacingMac5(false), fTruncDxaExpand(false), fPrintBodyBeforeHdr(false), + fNoLeading(false), fCompatibilityOptions_Unknown1_21(false), fMWSmallCaps(false), + fCompatibilityOptions_Unknown1_23(false), fCompatibilityOptions_Unknown1_24(false), + fCompatibilityOptions_Unknown1_25(false), fCompatibilityOptions_Unknown1_26(false), + fCompatibilityOptions_Unknown1_27(false), fCompatibilityOptions_Unknown1_28(false), + fCompatibilityOptions_Unknown1_29(false), fCompatibilityOptions_Unknown1_30(false), + fCompatibilityOptions_Unknown1_31(false), fUsePrinterMetrics(false), lvl(0), fHtmlDoc(false), + fSnapBorder(false), fIncludeHeader(false), fIncludeFooter(false), fForcePageSizePag(false), + fMinFontSizePag(false), fHaveVersions(false), fAutoVersion(false), + fCompatibilityOptions_Unknown2_1(false), fCompatibilityOptions_Unknown2_2(false), + fDontUseHTMLAutoSpacing(false), fCompatibilityOptions_Unknown2_4(false), + fCompatibilityOptions_Unknown2_5(false), fCompatibilityOptions_Unknown2_6(false), + fCompatibilityOptions_Unknown2_7(false), fCompatibilityOptions_Unknown2_8(false), + fCompatibilityOptions_Unknown2_9(false), fCompatibilityOptions_Unknown2_10(false), + fCompatibilityOptions_Unknown2_11(false), fCompatibilityOptions_Unknown2_12(false), + fCompatibilityOptions_Unknown2_13(false), fCompatibilityOptions_Unknown2_14(false), + fCompatibilityOptions_Unknown2_15(false), fCompatibilityOptions_Unknown2_16(false), + fCompatibilityOptions_Unknown2_17(false), fCompatibilityOptions_Unknown2_18(false), + fCompatibilityOptions_Unknown2_19(false), fCompatibilityOptions_Unknown2_20(false), + fCompatibilityOptions_Unknown2_21(false), fCompatibilityOptions_Unknown2_22(false), + fCompatibilityOptions_Unknown2_23(false), fCompatibilityOptions_Unknown2_24(false), + fCompatibilityOptions_Unknown2_25(false), fCompatibilityOptions_Unknown2_26(false), + fCompatibilityOptions_Unknown2_27(false), fCompatibilityOptions_Unknown2_28(false), + fCompatibilityOptions_Unknown2_29(false), fCompatibilityOptions_Unknown2_30(false), + fCompatibilityOptions_Unknown2_31(false), fCompatibilityOptions_Unknown2_32(false), + fUnknown3(0), fUseBackGroundInAllmodes(false), fDoNotEmbedSystemFont(false), fWordCompat(false), + fLiveRecover(false), fEmbedFactoids(false), fFactoidXML(false), fFactoidAllDone(false), + fFolioPrint(false), fReverseFolio(false), iTextLineEnding(0), fHideFcc(false), + fAcetateShowMarkup(false), fAcetateShowAtn(false), fAcetateShowInsDel(false), + fAcetateShowProps(false) + // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the + // above bit-field member initializations can be moved to the class definition +{ + fDontUseHTMLAutoSpacing = true; //default + fAcetateShowAtn = true; //default + const sal_uInt32 nMaxDopSize = 0x268; + std::unique_ptr<sal_uInt8[]> pDataPtr( new sal_uInt8[ nMaxDopSize ] ); + sal_uInt8* pData = pDataPtr.get(); + + sal_uInt32 nRead = std::min(nMaxDopSize, nSize); + if (nSize < 2 || !checkSeek(rSt, nPos) || nRead != rSt.ReadBytes(pData, nRead)) + nDopError = ERR_SWG_READ_ERROR; // report error + else + { + if (nMaxDopSize > nRead) + memset( pData + nRead, 0, nMaxDopSize - nRead ); + + // interpret the data + sal_uInt32 a32Bit; + sal_uInt16 a16Bit; + sal_uInt8 a8Bit; + + a16Bit = Get_UShort( pData ); // 0 0x00 + fFacingPages = 0 != ( a16Bit & 0x0001 ) ; + fWidowControl = 0 != ( a16Bit & 0x0002 ) ; + fPMHMainDoc = 0 != ( a16Bit & 0x0004 ) ; + grfSuppression = ( a16Bit & 0x0018 ) >> 3; + fpc = ( a16Bit & 0x0060 ) >> 5; + grpfIhdt = ( a16Bit & 0xff00 ) >> 8; + + a16Bit = Get_UShort( pData ); // 2 0x02 + rncFootnote = a16Bit & 0x0003 ; + nFootnote = ( a16Bit & ~0x0003 ) >> 2 ; + + a8Bit = Get_Byte( pData ); // 4 0x04 + fOutlineDirtySave = 0 != ( a8Bit & 0x01 ); + + a8Bit = Get_Byte( pData ); // 5 0x05 + fOnlyMacPics = 0 != ( a8Bit & 0x01 ); + fOnlyWinPics = 0 != ( a8Bit & 0x02 ); + fLabelDoc = 0 != ( a8Bit & 0x04 ); + fHyphCapitals = 0 != ( a8Bit & 0x08 ); + fAutoHyphen = 0 != ( a8Bit & 0x10 ); + fFormNoFields = 0 != ( a8Bit & 0x20 ); + fLinkStyles = 0 != ( a8Bit & 0x40 ); + fRevMarking = 0 != ( a8Bit & 0x80 ); + + a8Bit = Get_Byte( pData ); // 6 0x06 + fBackup = 0 != ( a8Bit & 0x01 ); + fExactCWords = 0 != ( a8Bit & 0x02 ); + fPagHidden = 0 != ( a8Bit & 0x04 ); + fPagResults = 0 != ( a8Bit & 0x08 ); + fLockAtn = 0 != ( a8Bit & 0x10 ); + fMirrorMargins = 0 != ( a8Bit & 0x20 ); + fReadOnlyRecommended = 0 != ( a8Bit & 0x40 ); + fDfltTrueType = 0 != ( a8Bit & 0x80 ); + + a8Bit = Get_Byte( pData ); // 7 0x07 + fPagSuppressTopSpacing = 0 != ( a8Bit & 0x01 ); + fProtEnabled = 0 != ( a8Bit & 0x02 ); + fDispFormFieldSel = 0 != ( a8Bit & 0x04 ); + fRMView = 0 != ( a8Bit & 0x08 ); + fRMPrint = 0 != ( a8Bit & 0x10 ); + fWriteReservation = 0 != ( a8Bit & 0x20 ); + fLockRev = 0 != ( a8Bit & 0x40 ); + fEmbedFonts = 0 != ( a8Bit & 0x80 ); + + a8Bit = Get_Byte( pData ); // 8 0x08 + copts_fNoTabForInd = 0 != ( a8Bit & 0x01 ); + copts_fNoSpaceRaiseLower = 0 != ( a8Bit & 0x02 ); + copts_fSupressSpbfAfterPgBrk = 0 != ( a8Bit & 0x04 ); + copts_fWrapTrailSpaces = 0 != ( a8Bit & 0x08 ); + copts_fMapPrintTextColor = 0 != ( a8Bit & 0x10 ); + copts_fNoColumnBalance = 0 != ( a8Bit & 0x20 ); + copts_fConvMailMergeEsc = 0 != ( a8Bit & 0x40 ); + copts_fSupressTopSpacing = 0 != ( a8Bit & 0x80 ); + + a8Bit = Get_Byte( pData ); // 9 0x09 + copts_fOrigWordTableRules = 0 != ( a8Bit & 0x01 ); + copts_fTransparentMetafiles = 0 != ( a8Bit & 0x02 ); + copts_fShowBreaksInFrames = 0 != ( a8Bit & 0x04 ); + copts_fSwapBordersFacingPgs = 0 != ( a8Bit & 0x08 ); + copts_fExpShRtn = 0 != ( a8Bit & 0x20 ); // #i56856# + + dxaTab = Get_Short( pData ); // 10 0x0a + wSpare = Get_UShort( pData ); // 12 0x0c + dxaHotZ = Get_UShort( pData ); // 14 0x0e + cConsecHypLim = Get_UShort( pData ); // 16 0x10 + wSpare2 = Get_UShort( pData ); // 18 0x12 + dttmCreated = Get_Long( pData ); // 20 0x14 + dttmRevised = Get_Long( pData ); // 24 0x18 + dttmLastPrint = Get_Long( pData ); // 28 0x1c + nRevision = Get_Short( pData ); // 32 0x20 + tmEdited = Get_Long( pData ); // 34 0x22 + cWords = Get_Long( pData ); // 38 0x26 + cCh = Get_Long( pData ); // 42 0x2a + cPg = Get_Short( pData ); // 46 0x2e + cParas = Get_Long( pData ); // 48 0x30 + + a16Bit = Get_UShort( pData ); // 52 0x34 + rncEdn = a16Bit & 0x0003 ; + nEdn = ( a16Bit & ~0x0003 ) >> 2; + + a16Bit = Get_UShort( pData ); // 54 0x36 + epc = a16Bit & 0x0003 ; + nfcFootnoteRef = ( a16Bit & 0x003c ) >> 2; + nfcEdnRef = ( a16Bit & 0x03c0 ) >> 6; + fPrintFormData = 0 != ( a16Bit & 0x0400 ); + fSaveFormData = 0 != ( a16Bit & 0x0800 ); + fShadeFormData = 0 != ( a16Bit & 0x1000 ); + fWCFootnoteEdn = 0 != ( a16Bit & 0x8000 ); + + cLines = Get_Long( pData ); // 56 0x38 + cWordsFootnoteEnd = Get_Long( pData ); // 60 0x3c + cChFootnoteEdn = Get_Long( pData ); // 64 0x40 + cPgFootnoteEdn = Get_Short( pData ); // 68 0x44 + cParasFootnoteEdn = Get_Long( pData ); // 70 0x46 + cLinesFootnoteEdn = Get_Long( pData ); // 74 0x4a + lKeyProtDoc = Get_Long( pData ); // 78 0x4e + + a16Bit = Get_UShort( pData ); // 82 0x52 + wvkSaved = a16Bit & 0x0007 ; + wScaleSaved = ( a16Bit & 0x0ff8 ) >> 3 ; + zkSaved = ( a16Bit & 0x3000 ) >> 12; + fRotateFontW6 = ( a16Bit & 0x4000 ) >> 14; + iGutterPos = ( a16Bit & 0x8000 ) >> 15; + + if (nFib >= 103) // Word 6/32bit, 95, 97, 2000, 2002, 2003, 2007 + { + a32Bit = Get_ULong( pData ); // 84 0x54 + SetCompatibilityOptions(a32Bit); + } + + //#i22436#, for all WW7- documents + if (nFib <= 104) // Word 95 + fUsePrinterMetrics = true; + + if (nFib > 105) // Word 97, 2000, 2002, 2003, 2007 + { + adt = Get_Short( pData ); // 88 0x58 + + doptypography.ReadFromMem(pData); // 90 0x5a + + memcpy( &dogrid, pData, sizeof( WW8_DOGRID )); // 400 0x190 + pData += sizeof( WW8_DOGRID ); + + a16Bit = Get_UShort( pData ); // 410 0x19a + // the following 9 bit are uninteresting + fHtmlDoc = ( a16Bit & 0x0200 ) >> 9 ; + fSnapBorder = ( a16Bit & 0x0800 ) >> 11 ; + fIncludeHeader = ( a16Bit & 0x1000 ) >> 12 ; + fIncludeFooter = ( a16Bit & 0x2000 ) >> 13 ; + fForcePageSizePag = ( a16Bit & 0x4000 ) >> 14 ; + fMinFontSizePag = ( a16Bit & 0x8000 ) >> 15 ; + + a16Bit = Get_UShort( pData ); // 412 0x19c + fHaveVersions = 0 != ( a16Bit & 0x0001 ); + fAutoVersion = 0 != ( a16Bit & 0x0002 ); + + pData += 12; // 414 0x19e + + cChWS = Get_Long( pData ); // 426 0x1aa + cChWSFootnoteEdn = Get_Long( pData ); // 430 0x1ae + grfDocEvents = Get_Long( pData ); // 434 0x1b2 + + pData += 4+30+8; // 438 0x1b6; 442 0x1ba; 472 0x1d8; 476 0x1dc + + cDBC = Get_Long( pData ); // 480 0x1e0 + cDBCFootnoteEdn = Get_Long( pData ); // 484 0x1e4 + + pData += 1 * sizeof( sal_Int32); // 488 0x1e8 + + nfcFootnoteRef = Get_Short( pData ); // 492 0x1ec + nfcEdnRef = Get_Short( pData ); // 494 0x1ee + hpsZoonFontPag = Get_Short( pData ); // 496 0x1f0 + dywDispPag = Get_Short( pData ); // 498 0x1f2 + + if (nRead >= 516) + { + //500 -> 508, Appear to be repeated here in 2000+ + pData += 8; // 500 0x1f4 + a32Bit = Get_Long( pData ); // 508 0x1fc + SetCompatibilityOptions(a32Bit); + a32Bit = Get_Long( pData ); // 512 0x200 + + // i#78591# + SetCompatibilityOptions2(a32Bit); + } + if (nRead >= 550) + { + pData += 32; + a16Bit = Get_UShort( pData ); + fDoNotEmbedSystemFont = ( a16Bit & 0x0001 ); + fWordCompat = ( a16Bit & 0x0002 ) >> 1; + fLiveRecover = ( a16Bit & 0x0004 ) >> 2; + fEmbedFactoids = ( a16Bit & 0x0008 ) >> 3; + fFactoidXML = ( a16Bit & 0x00010 ) >> 4; + fFactoidAllDone = ( a16Bit & 0x0020 ) >> 5; + fFolioPrint = ( a16Bit & 0x0040 ) >> 6; + fReverseFolio = ( a16Bit & 0x0080 ) >> 7; + iTextLineEnding = ( a16Bit & 0x0700 ) >> 8; + fHideFcc = ( a16Bit & 0x0800 ) >> 11; + fAcetateShowMarkup = ( a16Bit & 0x1000 ) >> 12; + fAcetateShowAtn = ( a16Bit & 0x2000 ) >> 13; + fAcetateShowInsDel = ( a16Bit & 0x4000 ) >> 14; + fAcetateShowProps = ( a16Bit & 0x8000 ) >> 15; + } + if (nRead >= 600) + { + pData += 48; + a16Bit = Get_Short(pData); + fUseBackGroundInAllmodes = (a16Bit & 0x0080) >> 7; + } + } + } +} + +WW8Dop::WW8Dop(): + fFacingPages(false), fWidowControl(true), fPMHMainDoc(false), grfSuppression(0), fpc(1), + grpfIhdt(0), rncFootnote(0), nFootnote(1), fOutlineDirtySave(true), fOnlyMacPics(false), + fOnlyWinPics(false), fLabelDoc(false), fHyphCapitals(true), fAutoHyphen(false), + fFormNoFields(false), fLinkStyles(false), fRevMarking(false), fBackup(true), + fExactCWords(false), fPagHidden(true), fPagResults(true), fLockAtn(false), + fMirrorMargins(false), fReadOnlyRecommended(false), fDfltTrueType(true), + fPagSuppressTopSpacing(false), fProtEnabled(false), fDispFormFieldSel(false), fRMView(true), + fRMPrint(true), fWriteReservation(false), fLockRev(false), fEmbedFonts(false), + copts_fNoTabForInd(false), copts_fNoSpaceRaiseLower(false), copts_fSupressSpbfAfterPgBrk(false), + copts_fWrapTrailSpaces(false), copts_fMapPrintTextColor(false), copts_fNoColumnBalance(false), + copts_fConvMailMergeEsc(false), copts_fSupressTopSpacing(false), + copts_fOrigWordTableRules(false), copts_fTransparentMetafiles(false), + copts_fShowBreaksInFrames(false), copts_fSwapBordersFacingPgs(false), copts_fExpShRtn(false), + dxaTab(0x2d0), dxaHotZ(0x168), nRevision(1), + rncEdn(0), nEdn(1), epc(3), fPrintFormData(false), fSaveFormData(false), fShadeFormData(true), + fWCFootnoteEdn(false), wvkSaved(2), wScaleSaved(100), zkSaved(0), fRotateFontW6(false), + iGutterPos(false), fNoTabForInd(false), fNoSpaceRaiseLower(false), + fSupressSpbfAfterPageBreak(false), fWrapTrailSpaces(false), fMapPrintTextColor(false), + fNoColumnBalance(false), fConvMailMergeEsc(false), fSupressTopSpacing(false), + fOrigWordTableRules(false), fTransparentMetafiles(false), fShowBreaksInFrames(false), + fSwapBordersFacingPgs(false), fCompatibilityOptions_Unknown1_13(false), fExpShRtn(false), + fCompatibilityOptions_Unknown1_15(false), fCompatibilityOptions_Unknown1_16(false), + fSuppressTopSpacingMac5(false), fTruncDxaExpand(false), fPrintBodyBeforeHdr(false), + fNoLeading(true), fCompatibilityOptions_Unknown1_21(false), fMWSmallCaps(false), + fCompatibilityOptions_Unknown1_23(false), fCompatibilityOptions_Unknown1_24(false), + fCompatibilityOptions_Unknown1_25(false), fCompatibilityOptions_Unknown1_26(false), + fCompatibilityOptions_Unknown1_27(false), fCompatibilityOptions_Unknown1_28(false), + fCompatibilityOptions_Unknown1_29(false), fCompatibilityOptions_Unknown1_30(false), + fCompatibilityOptions_Unknown1_31(false), fUsePrinterMetrics(true), lvl(9), fHtmlDoc(false), + fSnapBorder(false), fIncludeHeader(true), fIncludeFooter(true), fForcePageSizePag(false), + fMinFontSizePag(false), fHaveVersions(false), fAutoVersion(false), + cChWS(0), cChWSFootnoteEdn(0), cDBC(0), cDBCFootnoteEdn(0), nfcEdnRef(2), + fCompatibilityOptions_Unknown2_1(false), fCompatibilityOptions_Unknown2_2(false), + fDontUseHTMLAutoSpacing(false), fCompatibilityOptions_Unknown2_4(false), + fCompatibilityOptions_Unknown2_5(false), fCompatibilityOptions_Unknown2_6(false), + fCompatibilityOptions_Unknown2_7(false), fCompatibilityOptions_Unknown2_8(false), + fCompatibilityOptions_Unknown2_9(false), fCompatibilityOptions_Unknown2_10(false), + fCompatibilityOptions_Unknown2_11(false), fCompatibilityOptions_Unknown2_12(false), + fCompatibilityOptions_Unknown2_13(false), fCompatibilityOptions_Unknown2_14(false), + fCompatibilityOptions_Unknown2_15(false), fCompatibilityOptions_Unknown2_16(false), + fCompatibilityOptions_Unknown2_17(false), fCompatibilityOptions_Unknown2_18(false), + fCompatibilityOptions_Unknown2_19(false), fCompatibilityOptions_Unknown2_20(false), + fCompatibilityOptions_Unknown2_21(false), fCompatibilityOptions_Unknown2_22(false), + fCompatibilityOptions_Unknown2_23(false), fCompatibilityOptions_Unknown2_24(false), + fCompatibilityOptions_Unknown2_25(false), fCompatibilityOptions_Unknown2_26(false), + fCompatibilityOptions_Unknown2_27(false), fCompatibilityOptions_Unknown2_28(false), + fCompatibilityOptions_Unknown2_29(false), fCompatibilityOptions_Unknown2_30(false), + fCompatibilityOptions_Unknown2_31(false), fCompatibilityOptions_Unknown2_32(false), + fUnknown3(0), fUseBackGroundInAllmodes(false), fDoNotEmbedSystemFont(false), fWordCompat(false), + fLiveRecover(false), fEmbedFactoids(false), fFactoidXML(false), fFactoidAllDone(false), + fFolioPrint(false), fReverseFolio(false), iTextLineEnding(0), fHideFcc(false), + fAcetateShowMarkup(false), fAcetateShowAtn(true), fAcetateShowInsDel(false), + fAcetateShowProps(false) + // in C++20 with P06831R1 "Default member initializers for bit-fields (revision 1)", the + // above bit-field member initializations can be moved to the class definition +{ + /* + Writer acts like this all the time at the moment, ideally we need an + option for these two as well to import word docs that are not like + this by default + */ + // put in initialization list + // fNoLeading = true; + //fUsePrinterMetrics = true; +} + +void WW8Dop::SetCompatibilityOptions(sal_uInt32 a32Bit) +{ + fNoTabForInd = ( a32Bit & 0x00000001 ) ; + fNoSpaceRaiseLower = ( a32Bit & 0x00000002 ) >> 1 ; + fSupressSpbfAfterPageBreak = ( a32Bit & 0x00000004 ) >> 2 ; + fWrapTrailSpaces = ( a32Bit & 0x00000008 ) >> 3 ; + fMapPrintTextColor = ( a32Bit & 0x00000010 ) >> 4 ; + fNoColumnBalance = ( a32Bit & 0x00000020 ) >> 5 ; + fConvMailMergeEsc = ( a32Bit & 0x00000040 ) >> 6 ; + fSupressTopSpacing = ( a32Bit & 0x00000080 ) >> 7 ; + fOrigWordTableRules = ( a32Bit & 0x00000100 ) >> 8 ; + fTransparentMetafiles = ( a32Bit & 0x00000200 ) >> 9 ; + fShowBreaksInFrames = ( a32Bit & 0x00000400 ) >> 10 ; + fSwapBordersFacingPgs = ( a32Bit & 0x00000800 ) >> 11 ; + fCompatibilityOptions_Unknown1_13 = ( a32Bit & 0x00001000 ) >> 12 ; + fExpShRtn = ( a32Bit & 0x00002000 ) >> 13 ; // #i56856# + fCompatibilityOptions_Unknown1_15 = ( a32Bit & 0x00004000 ) >> 14 ; + fCompatibilityOptions_Unknown1_16 = ( a32Bit & 0x00008000 ) >> 15 ; + fSuppressTopSpacingMac5 = ( a32Bit & 0x00010000 ) >> 16 ; + fTruncDxaExpand = ( a32Bit & 0x00020000 ) >> 17 ; + fPrintBodyBeforeHdr = ( a32Bit & 0x00040000 ) >> 18 ; + fNoLeading = ( a32Bit & 0x00080000 ) >> 19 ; + fCompatibilityOptions_Unknown1_21 = ( a32Bit & 0x00100000 ) >> 20 ; + fMWSmallCaps = ( a32Bit & 0x00200000 ) >> 21 ; + fCompatibilityOptions_Unknown1_23 = ( a32Bit & 0x00400000 ) >> 22 ; + fCompatibilityOptions_Unknown1_24 = ( a32Bit & 0x00800800 ) >> 23 ; + fCompatibilityOptions_Unknown1_25 = ( a32Bit & 0x01000000 ) >> 24 ; + fCompatibilityOptions_Unknown1_26 = ( a32Bit & 0x02000000 ) >> 25 ; + fCompatibilityOptions_Unknown1_27 = ( a32Bit & 0x04000000 ) >> 26 ; + fCompatibilityOptions_Unknown1_28 = ( a32Bit & 0x08000000 ) >> 27 ; + fCompatibilityOptions_Unknown1_29 = ( a32Bit & 0x10000000 ) >> 28 ; + fCompatibilityOptions_Unknown1_30 = ( a32Bit & 0x20000000 ) >> 29 ; + fCompatibilityOptions_Unknown1_31 = ( a32Bit & 0x40000000 ) >> 30 ; + + fUsePrinterMetrics = ( a32Bit & 0x80000000 ) >> 31 ; +} + +sal_uInt32 WW8Dop::GetCompatibilityOptions() const +{ + sal_uInt32 a32Bit = 0; + if (fNoTabForInd) a32Bit |= 0x00000001; + if (fNoSpaceRaiseLower) a32Bit |= 0x00000002; + if (fSupressSpbfAfterPageBreak) a32Bit |= 0x00000004; + if (fWrapTrailSpaces) a32Bit |= 0x00000008; + if (fMapPrintTextColor) a32Bit |= 0x00000010; + if (fNoColumnBalance) a32Bit |= 0x00000020; + if (fConvMailMergeEsc) a32Bit |= 0x00000040; + if (fSupressTopSpacing) a32Bit |= 0x00000080; + if (fOrigWordTableRules) a32Bit |= 0x00000100; + if (fTransparentMetafiles) a32Bit |= 0x00000200; + if (fShowBreaksInFrames) a32Bit |= 0x00000400; + if (fSwapBordersFacingPgs) a32Bit |= 0x00000800; + if (fCompatibilityOptions_Unknown1_13) a32Bit |= 0x00001000; + if (fExpShRtn) a32Bit |= 0x00002000; // #i56856# + if (fCompatibilityOptions_Unknown1_15) a32Bit |= 0x00004000; + if (fCompatibilityOptions_Unknown1_16) a32Bit |= 0x00008000; + if (fSuppressTopSpacingMac5) a32Bit |= 0x00010000; + if (fTruncDxaExpand) a32Bit |= 0x00020000; + if (fPrintBodyBeforeHdr) a32Bit |= 0x00040000; + if (fNoLeading) a32Bit |= 0x00080000; + if (fCompatibilityOptions_Unknown1_21) a32Bit |= 0x00100000; + if (fMWSmallCaps) a32Bit |= 0x00200000; + if (fCompatibilityOptions_Unknown1_23) a32Bit |= 0x00400000; + if (fCompatibilityOptions_Unknown1_24) a32Bit |= 0x00800000; + if (fCompatibilityOptions_Unknown1_25) a32Bit |= 0x01000000; + if (fCompatibilityOptions_Unknown1_26) a32Bit |= 0x02000000; + if (fCompatibilityOptions_Unknown1_27) a32Bit |= 0x04000000; + if (fCompatibilityOptions_Unknown1_28) a32Bit |= 0x08000000; + if (fCompatibilityOptions_Unknown1_29) a32Bit |= 0x10000000; + if (fCompatibilityOptions_Unknown1_30) a32Bit |= 0x20000000; + if (fCompatibilityOptions_Unknown1_31) a32Bit |= 0x40000000; + if (fUsePrinterMetrics) a32Bit |= 0x80000000; + return a32Bit; +} + +// i#78591# +void WW8Dop::SetCompatibilityOptions2(sal_uInt32 a32Bit) +{ + fCompatibilityOptions_Unknown2_1 = ( a32Bit & 0x00000001 ); + fCompatibilityOptions_Unknown2_2 = ( a32Bit & 0x00000002 ) >> 1 ; + fDontUseHTMLAutoSpacing = ( a32Bit & 0x00000004 ) >> 2 ; + fCompatibilityOptions_Unknown2_4 = ( a32Bit & 0x00000008 ) >> 3 ; + fCompatibilityOptions_Unknown2_5 = ( a32Bit & 0x00000010 ) >> 4 ; + fCompatibilityOptions_Unknown2_6 = ( a32Bit & 0x00000020 ) >> 5 ; + fCompatibilityOptions_Unknown2_7 = ( a32Bit & 0x00000040 ) >> 6 ; + fCompatibilityOptions_Unknown2_8 = ( a32Bit & 0x00000080 ) >> 7 ; + fCompatibilityOptions_Unknown2_9 = ( a32Bit & 0x00000100 ) >> 8 ; + fCompatibilityOptions_Unknown2_10 = ( a32Bit & 0x00000200 ) >> 9 ; + fCompatibilityOptions_Unknown2_11 = ( a32Bit & 0x00000400 ) >> 10 ; + fCompatibilityOptions_Unknown2_12 = ( a32Bit & 0x00000800 ) >> 11 ; + fCompatibilityOptions_Unknown2_13 = ( a32Bit & 0x00001000 ) >> 12 ; + fCompatibilityOptions_Unknown2_14 = ( a32Bit & 0x00002000 ) >> 13 ; + fCompatibilityOptions_Unknown2_15 = ( a32Bit & 0x00004000 ) >> 14 ; + fCompatibilityOptions_Unknown2_16 = ( a32Bit & 0x00008000 ) >> 15 ; + fCompatibilityOptions_Unknown2_17 = ( a32Bit & 0x00010000 ) >> 16 ; + fCompatibilityOptions_Unknown2_18 = ( a32Bit & 0x00020000 ) >> 17 ; + fCompatibilityOptions_Unknown2_19 = ( a32Bit & 0x00040000 ) >> 18 ; + fCompatibilityOptions_Unknown2_20 = ( a32Bit & 0x00080000 ) >> 19 ; + fCompatibilityOptions_Unknown2_21 = ( a32Bit & 0x00100000 ) >> 20 ; + fCompatibilityOptions_Unknown2_22 = ( a32Bit & 0x00200000 ) >> 21 ; + fCompatibilityOptions_Unknown2_23 = ( a32Bit & 0x00400000 ) >> 22 ; + fCompatibilityOptions_Unknown2_24 = ( a32Bit & 0x00800800 ) >> 23 ; + fCompatibilityOptions_Unknown2_25 = ( a32Bit & 0x01000800 ) >> 24 ; + fCompatibilityOptions_Unknown2_26 = ( a32Bit & 0x02000800 ) >> 25 ; + fCompatibilityOptions_Unknown2_27 = ( a32Bit & 0x04000800 ) >> 26 ; + fCompatibilityOptions_Unknown2_28 = ( a32Bit & 0x08000800 ) >> 27 ; + fCompatibilityOptions_Unknown2_29 = ( a32Bit & 0x10000800 ) >> 28 ; + fCompatibilityOptions_Unknown2_30 = ( a32Bit & 0x20000800 ) >> 29 ; + fCompatibilityOptions_Unknown2_31 = ( a32Bit & 0x40000800 ) >> 30 ; + fCompatibilityOptions_Unknown2_32 = ( a32Bit & 0x80000000 ) >> 31 ; +} + +sal_uInt32 WW8Dop::GetCompatibilityOptions2() const +{ + sal_uInt32 a32Bit = 0; + if (fCompatibilityOptions_Unknown2_1) a32Bit |= 0x00000001; + if (fCompatibilityOptions_Unknown2_2) a32Bit |= 0x00000002; + if (fDontUseHTMLAutoSpacing) a32Bit |= 0x00000004; + if (fCompatibilityOptions_Unknown2_4) a32Bit |= 0x00000008; + if (fCompatibilityOptions_Unknown2_5) a32Bit |= 0x00000010; + if (fCompatibilityOptions_Unknown2_6) a32Bit |= 0x00000020; + if (fCompatibilityOptions_Unknown2_7) a32Bit |= 0x00000040; + if (fCompatibilityOptions_Unknown2_8) a32Bit |= 0x00000080; + if (fCompatibilityOptions_Unknown2_9) a32Bit |= 0x00000100; + if (fCompatibilityOptions_Unknown2_10) a32Bit |= 0x00000200; + if (fCompatibilityOptions_Unknown2_11) a32Bit |= 0x00000400; + if (fCompatibilityOptions_Unknown2_12) a32Bit |= 0x00000800; + if (fCompatibilityOptions_Unknown2_13) a32Bit |= 0x00001000; + //#i42909# set thai "line breaking rules" compatibility option + // pflin, wonder whether bUseThaiLineBreakingRules is correct + // when importing word document. + if (bUseThaiLineBreakingRules) a32Bit |= 0x00002000; + else if (fCompatibilityOptions_Unknown2_14) a32Bit |= 0x00002000; + if (fCompatibilityOptions_Unknown2_15) a32Bit |= 0x00004000; + if (fCompatibilityOptions_Unknown2_16) a32Bit |= 0x00008000; + if (fCompatibilityOptions_Unknown2_17) a32Bit |= 0x00010000; + if (fCompatibilityOptions_Unknown2_18) a32Bit |= 0x00020000; + if (fCompatibilityOptions_Unknown2_19) a32Bit |= 0x00040000; + if (fCompatibilityOptions_Unknown2_20) a32Bit |= 0x00080000; + if (fCompatibilityOptions_Unknown2_21) a32Bit |= 0x00100000; + if (fCompatibilityOptions_Unknown2_22) a32Bit |= 0x00200000; + if (fCompatibilityOptions_Unknown2_23) a32Bit |= 0x00400000; + if (fCompatibilityOptions_Unknown2_24) a32Bit |= 0x00800000; + if (fCompatibilityOptions_Unknown2_25) a32Bit |= 0x01000000; + if (fCompatibilityOptions_Unknown2_26) a32Bit |= 0x02000000; + if (fCompatibilityOptions_Unknown2_27) a32Bit |= 0x04000000; + if (fCompatibilityOptions_Unknown2_28) a32Bit |= 0x08000000; + if (fCompatibilityOptions_Unknown2_29) a32Bit |= 0x10000000; + if (fCompatibilityOptions_Unknown2_30) a32Bit |= 0x20000000; + if (fCompatibilityOptions_Unknown2_31) a32Bit |= 0x40000000; + if (fCompatibilityOptions_Unknown2_32) a32Bit |= 0x80000000; + return a32Bit; +} + +void WW8Dop::Write(SvStream& rStrm, WW8Fib& rFib) const +{ + const int nMaxDopLen = 610; + sal_uInt32 nLen = 8 == rFib.m_nVersion ? nMaxDopLen : 84; + rFib.m_fcDop = rStrm.Tell(); + rFib.m_lcbDop = nLen; + + sal_uInt8 aData[ nMaxDopLen ] = {}; + sal_uInt8* pData = aData; + + // analyse the data + sal_uInt16 a16Bit; + sal_uInt8 a8Bit; + + a16Bit = 0; // 0 0x00 + if (fFacingPages) + a16Bit |= 0x0001; + if (fWidowControl) + a16Bit |= 0x0002; + if (fPMHMainDoc) + a16Bit |= 0x0004; + a16Bit |= ( 0x0018 & (grfSuppression << 3)); + a16Bit |= ( 0x0060 & (fpc << 5)); + a16Bit |= ( 0xff00 & (grpfIhdt << 8)); + Set_UInt16( pData, a16Bit ); + + a16Bit = 0; // 2 0x02 + a16Bit |= ( 0x0003 & rncFootnote ); + a16Bit |= ( ~0x0003 & (nFootnote << 2)); + Set_UInt16( pData, a16Bit ); + + a8Bit = 0; // 4 0x04 + if( fOutlineDirtySave ) a8Bit |= 0x01; + Set_UInt8( pData, a8Bit ); + + a8Bit = 0; // 5 0x05 + if( fOnlyMacPics ) a8Bit |= 0x01; + if( fOnlyWinPics ) a8Bit |= 0x02; + if( fLabelDoc ) a8Bit |= 0x04; + if( fHyphCapitals ) a8Bit |= 0x08; + if( fAutoHyphen ) a8Bit |= 0x10; + if( fFormNoFields ) a8Bit |= 0x20; + if( fLinkStyles ) a8Bit |= 0x40; + if( fRevMarking ) a8Bit |= 0x80; + Set_UInt8( pData, a8Bit ); + + a8Bit = 0; // 6 0x06 + if( fBackup ) a8Bit |= 0x01; + if( fExactCWords ) a8Bit |= 0x02; + if( fPagHidden ) a8Bit |= 0x04; + if( fPagResults ) a8Bit |= 0x08; + if( fLockAtn ) a8Bit |= 0x10; + if( fMirrorMargins ) a8Bit |= 0x20; + if( fReadOnlyRecommended ) a8Bit |= 0x40; + if( fDfltTrueType ) a8Bit |= 0x80; + Set_UInt8( pData, a8Bit ); + + a8Bit = 0; // 7 0x07 + if( fPagSuppressTopSpacing ) a8Bit |= 0x01; + if( fProtEnabled ) a8Bit |= 0x02; + if( fDispFormFieldSel ) a8Bit |= 0x04; + if( fRMView ) a8Bit |= 0x08; + if( fRMPrint ) a8Bit |= 0x10; + if( fWriteReservation ) a8Bit |= 0x20; + if( fLockRev ) a8Bit |= 0x40; + if( fEmbedFonts ) a8Bit |= 0x80; + Set_UInt8( pData, a8Bit ); + + a8Bit = 0; // 8 0x08 + if( copts_fNoTabForInd ) a8Bit |= 0x01; + if( copts_fNoSpaceRaiseLower ) a8Bit |= 0x02; + if( copts_fSupressSpbfAfterPgBrk ) a8Bit |= 0x04; + if( copts_fWrapTrailSpaces ) a8Bit |= 0x08; + if( copts_fMapPrintTextColor ) a8Bit |= 0x10; + if( copts_fNoColumnBalance ) a8Bit |= 0x20; + if( copts_fConvMailMergeEsc ) a8Bit |= 0x40; + if( copts_fSupressTopSpacing ) a8Bit |= 0x80; + Set_UInt8( pData, a8Bit ); + + a8Bit = 0; // 9 0x09 + if( copts_fOrigWordTableRules ) a8Bit |= 0x01; + if( copts_fTransparentMetafiles ) a8Bit |= 0x02; + if( copts_fShowBreaksInFrames ) a8Bit |= 0x04; + if( copts_fSwapBordersFacingPgs ) a8Bit |= 0x08; + if( copts_fExpShRtn ) a8Bit |= 0x20; // #i56856# + Set_UInt8( pData, a8Bit ); + + Set_UInt16( pData, dxaTab ); // 10 0x0a + Set_UInt16( pData, wSpare ); // 12 0x0c + Set_UInt16( pData, dxaHotZ ); // 14 0x0e + Set_UInt16( pData, cConsecHypLim ); // 16 0x10 + Set_UInt16( pData, wSpare2 ); // 18 0x12 + Set_UInt32( pData, dttmCreated ); // 20 0x14 + Set_UInt32( pData, dttmRevised ); // 24 0x18 + Set_UInt32( pData, dttmLastPrint ); // 28 0x1c + Set_UInt16( pData, nRevision ); // 32 0x20 + Set_UInt32( pData, tmEdited ); // 34 0x22 + Set_UInt32( pData, cWords ); // 38 0x26 + Set_UInt32( pData, cCh ); // 42 0x2a + Set_UInt16( pData, cPg ); // 46 0x2e + Set_UInt32( pData, cParas ); // 48 0x30 + + a16Bit = 0; // 52 0x34 + a16Bit |= ( 0x0003 & rncEdn ); + a16Bit |= (~0x0003 & ( nEdn << 2)); + Set_UInt16( pData, a16Bit ); + + a16Bit = 0; // 54 0x36 + a16Bit |= (0x0003 & epc ); + a16Bit |= (0x003c & (nfcFootnoteRef << 2)); + a16Bit |= (0x03c0 & (nfcEdnRef << 6)); + if( fPrintFormData ) a16Bit |= 0x0400; + if( fSaveFormData ) a16Bit |= 0x0800; + if( fShadeFormData ) a16Bit |= 0x1000; + if( fWCFootnoteEdn ) a16Bit |= 0x8000; + Set_UInt16( pData, a16Bit ); + + Set_UInt32( pData, cLines ); // 56 0x38 + Set_UInt32( pData, cWordsFootnoteEnd ); // 60 0x3c + Set_UInt32( pData, cChFootnoteEdn ); // 64 0x40 + Set_UInt16( pData, cPgFootnoteEdn ); // 68 0x44 + Set_UInt32( pData, cParasFootnoteEdn ); // 70 0x46 + Set_UInt32( pData, cLinesFootnoteEdn ); // 74 0x4a + Set_UInt32( pData, lKeyProtDoc ); // 78 0x4e + + a16Bit = 0; // 82 0x52 + if (wvkSaved) + a16Bit |= 0x0007; + a16Bit |= (0x0ff8 & (wScaleSaved << 3)); + a16Bit |= (0x3000 & (zkSaved << 12)); + Set_UInt16( pData, a16Bit ); + + if( 8 == rFib.m_nVersion ) + { + Set_UInt32(pData, GetCompatibilityOptions()); // 84 0x54 + + Set_UInt16( pData, adt ); // 88 0x58 + + doptypography.WriteToMem(pData); // 400 0x190 + + memcpy( pData, &dogrid, sizeof( WW8_DOGRID )); + pData += sizeof( WW8_DOGRID ); + + a16Bit = 0x12; // set lvl to 9 // 410 0x19a + if( fHtmlDoc ) a16Bit |= 0x0200; + if( fSnapBorder ) a16Bit |= 0x0800; + if( fIncludeHeader ) a16Bit |= 0x1000; + if( fIncludeFooter ) a16Bit |= 0x2000; + if( fForcePageSizePag ) a16Bit |= 0x4000; + if( fMinFontSizePag ) a16Bit |= 0x8000; + Set_UInt16( pData, a16Bit ); + + a16Bit = 0; // 412 0x19c + if( fHaveVersions ) a16Bit |= 0x0001; + if( fAutoVersion ) a16Bit |= 0x0002; + Set_UInt16( pData, a16Bit ); + + pData += 12; // 414 0x19e + + Set_UInt32( pData, cChWS ); // 426 0x1aa + Set_UInt32( pData, cChWSFootnoteEdn ); // 430 0x1ae + Set_UInt32( pData, grfDocEvents ); // 434 0x1b2 + + pData += 4+30+8; // 438 0x1b6; 442 0x1ba; 472 0x1d8; 476 0x1dc + + Set_UInt32( pData, cDBC ); // 480 0x1e0 + Set_UInt32( pData, cDBCFootnoteEdn ); // 484 0x1e4 + + pData += 1 * sizeof( sal_Int32); // 488 0x1e8 + + Set_UInt16( pData, nfcFootnoteRef ); // 492 0x1ec + Set_UInt16( pData, nfcEdnRef ); // 494 0x1ee + Set_UInt16( pData, hpsZoonFontPag ); // 496 0x1f0 + Set_UInt16( pData, dywDispPag ); // 498 0x1f2 + + //500 -> 508, Appear to be repeated here in 2000+ + pData += 8; + Set_UInt32(pData, GetCompatibilityOptions()); + Set_UInt32(pData, GetCompatibilityOptions2()); + pData += 32; + + a16Bit = 0; + if (fEmbedFactoids) + a16Bit |= 0x8; + if (fAcetateShowMarkup) + a16Bit |= 0x1000; + //Word XP at least requires fAcetateShowMarkup to honour fAcetateShowAtn + if (fAcetateShowAtn) + { + a16Bit |= 0x1000; + a16Bit |= 0x2000; + } + Set_UInt16(pData, a16Bit); + + pData += 48; + a16Bit = 0x0080; + Set_UInt16(pData, a16Bit); + } + rStrm.WriteBytes(aData, nLen); +} + +void WW8DopTypography::ReadFromMem(sal_uInt8 *&pData) +{ + sal_uInt16 a16Bit = Get_UShort(pData); + m_fKerningPunct = (a16Bit & 0x0001); + m_iJustification = (a16Bit & 0x0006) >> 1; + m_iLevelOfKinsoku = (a16Bit & 0x0018) >> 3; + m_f2on1 = (a16Bit & 0x0020) >> 5; + m_reserved1 = (a16Bit & 0x03C0) >> 6; + m_reserved2 = (a16Bit & 0xFC00) >> 10; + + m_cchFollowingPunct = Get_Short(pData); + m_cchLeadingPunct = Get_Short(pData); + + sal_Int16 i; + for (i=0; i < nMaxFollowing; ++i) + m_rgxchFPunct[i] = Get_Short(pData); + for (i=0; i < nMaxLeading; ++i) + m_rgxchLPunct[i] = Get_Short(pData); + + if (m_cchFollowingPunct >= 0 && m_cchFollowingPunct < nMaxFollowing) + m_rgxchFPunct[m_cchFollowingPunct]=0; + else + m_rgxchFPunct[nMaxFollowing - 1]=0; + + if (m_cchLeadingPunct >= 0 && m_cchLeadingPunct < nMaxLeading) + m_rgxchLPunct[m_cchLeadingPunct]=0; + else + m_rgxchLPunct[nMaxLeading - 1]=0; + +} + +void WW8DopTypography::WriteToMem(sal_uInt8 *&pData) const +{ + sal_uInt16 a16Bit = sal_uInt16(m_fKerningPunct); + a16Bit |= (m_iJustification << 1) & 0x0006; + a16Bit |= (m_iLevelOfKinsoku << 3) & 0x0018; + a16Bit |= (int(m_f2on1) << 5) & 0x0020; + a16Bit |= (m_reserved1 << 6) & 0x03C0; + a16Bit |= (m_reserved2 << 10) & 0xFC00; + Set_UInt16(pData,a16Bit); + + Set_UInt16(pData,m_cchFollowingPunct); + Set_UInt16(pData,m_cchLeadingPunct); + + sal_Int16 i; + for (i=0; i < nMaxFollowing; ++i) + Set_UInt16(pData,m_rgxchFPunct[i]); + for (i=0; i < nMaxLeading; ++i) + Set_UInt16(pData,m_rgxchLPunct[i]); +} + +LanguageType WW8DopTypography::GetConvertedLang() const +{ + LanguageType nLang; + //I have assumed people's republic/taiwan == simplified/traditional + + //This isn't a documented issue, so we might have it all wrong, + //i.e. i.e. what's with the powers of two ? + + /* + One example of 3 for reserved1 which was really Japanese, perhaps last bit + is for some other use ?, or redundant. If more examples trigger the assert + we might be able to figure it out. + */ + switch(m_reserved1 & 0xE) + { + case 2: //Japan + nLang = LANGUAGE_JAPANESE; + break; + case 4: //Chinese (People's Republic) + nLang = LANGUAGE_CHINESE_SIMPLIFIED; + break; + case 6: //Korean + nLang = LANGUAGE_KOREAN; + break; + case 8: //Chinese (Taiwan) + nLang = LANGUAGE_CHINESE_TRADITIONAL; + break; + default: + OSL_ENSURE(false, "Unknown MS Asian Typography language, report"); + nLang = LANGUAGE_CHINESE_SIMPLIFIED_LEGACY; + break; + case 0: + //And here we have the possibility that it says 2, but it's really + //a bug and only japanese level 2 has been selected after a custom + //version was chosen on last save! + nLang = LANGUAGE_JAPANESE; + break; + } + return nLang; +} + +// Sprms + +sal_uInt16 wwSprmParser::GetSprmTailLen(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) + const +{ + SprmInfo aSprm = GetSprmInfo(nId); + sal_uInt16 nL = 0; // number of Bytes to read + + //sprmPChgTabs + switch( nId ) + { + case 23: + case 0xC615: + if( pSprm[1 + mnDelta] != 255 ) + nL = static_cast< sal_uInt16 >(pSprm[1 + mnDelta] + aSprm.nLen); + else + { + sal_uInt8 nDelIdx = 2 + mnDelta; + sal_uInt8 nDel = nDelIdx < nRemLen ? pSprm[nDelIdx] : 0; + sal_uInt8 nInsIdx = 3 + mnDelta + 4 * nDel; + sal_uInt8 nIns = nInsIdx < nRemLen ? pSprm[nInsIdx] : 0; + + nL = 2 + 4 * nDel + 3 * nIns; + } + break; + case 0xD608: + { + sal_uInt8 nIndex = 1 + mnDelta; + if (nIndex + 1 >= nRemLen) + { + SAL_WARN("sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong"); + nL = 0; + } + else + nL = SVBT16ToUInt16(&pSprm[nIndex]); + break; + } + default: + switch (aSprm.nVari) + { + case L_FIX: + nL = aSprm.nLen; // Excl. Token + break; + case L_VAR: + // Variable 1-Byte Length? + // Excl. Token + Var-Lengthbyte + nL = static_cast< sal_uInt16 >(pSprm[1 + mnDelta] + aSprm.nLen); + break; + case L_VAR2: + { + // Variable 2-Byte Length? + // Excl. Token + Var-Lengthbyte + sal_uInt8 nIndex = 1 + mnDelta; + sal_uInt16 nCount; + if (nIndex + 1 >= nRemLen) + { + SAL_WARN("sw.ww8", "sprm longer than remaining bytes, doc or parser is wrong"); + nCount = 0; + } + else + nCount = SVBT16ToUInt16(&pSprm[nIndex]); + nL = static_cast< sal_uInt16 >(nCount + aSprm.nLen - 1); + break; + } + default: + OSL_ENSURE(false, "Unknown sprm variant"); + break; + } + break; + } + return nL; +} + +// one or two bytes at the beginning at the sprm id +sal_uInt16 wwSprmParser::GetSprmId(const sal_uInt8* pSp) const +{ + OSL_ENSURE(pSp, "Why GetSprmId with pSp of 0"); + if (!pSp) + return 0; + + sal_uInt16 nId = 0; + + if (ww::IsSevenMinus(meVersion)) + { + nId = *pSp; // [0..0xff] + } + else + { + nId = SVBT16ToUInt16(pSp); + if (0x0800 > nId) + nId = 0; + } + + return nId; +} + +// with tokens and length byte +sal_uInt16 wwSprmParser::GetSprmSize(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const +{ + return GetSprmTailLen(nId, pSprm, nRemLen) + 1 + mnDelta + SprmDataOfs(nId); +} + +sal_uInt8 wwSprmParser::SprmDataOfs(sal_uInt16 nId) const +{ + return GetSprmInfo(nId).nVari; +} + +sal_uInt16 wwSprmParser::DistanceToData(sal_uInt16 nId) const +{ + return 1 + mnDelta + SprmDataOfs(nId); +} + +SprmResult wwSprmParser::findSprmData(sal_uInt16 nId, sal_uInt8* pSprms, + sal_uInt16 nLen) const +{ + while (nLen >= MinSprmLen()) + { + const sal_uInt16 nCurrentId = GetSprmId(pSprms); + // set pointer to data + sal_uInt16 nSize = GetSprmSize(nCurrentId, pSprms, nLen); + + bool bValid = nSize <= nLen; + + SAL_WARN_IF(!bValid, "sw.ww8", + "sprm 0x" << std::hex << nCurrentId << std::dec << " longer than remaining bytes, " << + nSize << " vs " << nLen << "doc or parser is wrong"); + + if (nCurrentId == nId && bValid) // Sprm found + { + sal_uInt16 nFixedLen = DistanceToData(nId); + return SprmResult(pSprms + nFixedLen, nSize - nFixedLen); + } + + //Clip to available size if wrong + nSize = std::min(nSize, nLen); + pSprms += nSize; + nLen -= nSize; + } + // Sprm not found + return SprmResult(); +} + +SEPr::SEPr() : + bkc(2), fTitlePage(0), fAutoPgn(0), nfcPgn(0), fUnlocked(0), cnsPgn(0), + fPgnRestart(0), fEndNote(1), lnc(0), grpfIhdt(0), nLnnMod(0), dxaLnn(0), + dxaPgn(720), dyaPgn(720), fLBetween(0), vjc(0), dmBinFirst(0), + dmBinOther(0), dmPaperReq(0), fPropRMark(0), ibstPropRMark(0), + dttmPropRMark(0), dxtCharSpace(0), dyaLinePitch(0), clm(0), reserved1(0), + dmOrientPage(0), iHeadingPgn(0), pgnStart(1), lnnMin(0), wTextFlow(0), + reserved2(0), pgbApplyTo(0), pgbPageDepth(0), pgbOffsetFrom(0), + xaPage(lLetterWidth), yaPage(lLetterHeight), xaPageNUp(lLetterWidth), yaPageNUp(lLetterHeight), + dxaLeft(1800), dxaRight(1800), dyaTop(1440), dyaBottom(1440), dzaGutter(0), + dyaHdrTop(720), dyaHdrBottom(720), ccolM1(0), fEvenlySpaced(1), + reserved3(0), fBiDi(0), fFacingCol(0), fRTLGutter(0), fRTLAlignment(0), + dxaColumns(720), dxaColumnWidth(0), dmOrientFirst(0), fLayout(0), + reserved4(0) +{ +} + +bool checkRead(SvStream &rSt, void *pDest, sal_uInt32 nLength) +{ + return (rSt.ReadBytes(pDest, nLength) == static_cast<std::size_t>(nLength)); +} + +#ifdef OSL_BIGENDIAN +void swapEndian(sal_Unicode *pString) +{ + for (sal_Unicode *pWork = pString; *pWork; ++pWork) + *pWork = OSL_SWAPWORD(*pWork); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8scan.hxx b/sw/source/filter/ww8/ww8scan.hxx new file mode 100644 index 000000000..d2b32ae80 --- /dev/null +++ b/sw/source/filter/ww8/ww8scan.hxx @@ -0,0 +1,1874 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_WW8_WW8SCAN_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8SCAN_HXX + +#include <cassert> +#include <cstddef> +#include <deque> +#include <memory> +#include <stack> +#include <unordered_map> +#include <vector> + +#include <osl/endian.h> +#include <tools/solar.h> +#include <tools/stream.hxx> +#include <rtl/ustring.hxx> + +#include "ww8struc.hxx" +#include "types.hxx" + +class SvStream; + +//Commonly used string literals for stream and storage names in word docs +namespace SL +{ + const char aObjectPool[] = "ObjectPool"; + const char a1Table[] = "1Table"; + const char a0Table[] = "0Table"; + const char aData[] = "Data"; + const char aCheckBox[] = "CheckBox"; + const char aListBox[] = "ListBox"; + const char aTextBox[] = "TextBox"; + const char aTextField[] = "TextField"; + const char aMSMacroCmds[] = "MSMacroCmds"; +} + +struct SprmInfo +{ + unsigned int nLen : 6; + unsigned int nVari : 2; +}; + +struct SprmInfoRow { + sal_uInt16 nId; ///< A ww8 sprm is hardcoded as 16bits + SprmInfo info; +}; + +class wwSprmSearcher { +public: + //see Read_AmbiguousSPRM for the bPatchCJK oddity + wwSprmSearcher(SprmInfoRow const * rows, std::size_t size, bool bPatchCJK = false) { + for (std::size_t i = 0; i != size; ++i) { + bool ins = map_.emplace(rows[i].nId, rows[i].info).second; + assert(ins); (void) ins; + } + if (bPatchCJK) + patchCJKVariant(); + } + + SprmInfo const * search(sal_uInt16 id) const { + Map::const_iterator i(map_.find(id)); + return i == map_.end() ? nullptr : &i->second; + } + +private: + typedef std::unordered_map<sal_uInt16, SprmInfo> Map; + + Map map_; + + void patchCJKVariant(); +}; + +class WW8Fib; + +struct SprmResult +{ + const sal_uInt8* pSprm; + sal_Int32 nRemainingData; + SprmResult() + : pSprm(nullptr) + , nRemainingData(0) + { + } + SprmResult(const sal_uInt8* pInSprm, sal_Int32 nInRemainingData) + : pSprm(pInSprm) + , nRemainingData(nInRemainingData) + { + } +}; + +/** + wwSprmParser knows how to take a sequence of bytes and split it up into + sprms and their arguments +*/ +class wwSprmParser +{ +private: + ww::WordVersion meVersion; + sal_uInt8 mnDelta; + const wwSprmSearcher *mpKnownSprms; + static const wwSprmSearcher* GetWW8SprmSearcher(); + static const wwSprmSearcher* GetWW6SprmSearcher(const WW8Fib& rFib); + static const wwSprmSearcher* GetWW2SprmSearcher(); + + SprmInfo GetSprmInfo(sal_uInt16 nId) const; + + sal_uInt8 SprmDataOfs(sal_uInt16 nId) const; + +public: + enum SprmType {L_FIX=0, L_VAR=1, L_VAR2=2}; + + //7- ids are very different to 8+ ones + explicit wwSprmParser(const WW8Fib& rFib); + /// Return the SPRM id at the beginning of this byte sequence + sal_uInt16 GetSprmId(const sal_uInt8* pSp) const; + + sal_uInt16 GetSprmSize(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const; + + /// Get known len of a sprms head, the bytes of the sprm id + any bytes + /// reserved to hold a variable length + sal_uInt16 DistanceToData(sal_uInt16 nId) const; + + /// Get len of a sprms data area, ignoring the bytes of the sprm id and + /// ignoring any len bytes. Reports the remaining data after those bytes + sal_uInt16 GetSprmTailLen(sal_uInt16 nId, const sal_uInt8* pSprm, sal_Int32 nRemLen) const; + + /// The minimum acceptable sprm len possible for this type of parser + int MinSprmLen() const { return (IsSevenMinus(meVersion)) ? 2 : 3; } + + /// Returns the offset to data of the first sprm of id nId, 0 + // if not found. nLen must be the <= length of pSprms + SprmResult findSprmData(sal_uInt16 nId, sal_uInt8* pSprms, sal_uInt16 nLen) const; +}; + +//Read a Pascal-style, i.e. single byte string length followed +//by string contents +inline OUString read_uInt8_PascalString(SvStream& rStrm, rtl_TextEncoding eEnc) +{ + return read_uInt8_lenPrefixed_uInt8s_ToOUString(rStrm, eEnc); +} + +inline OUString read_uInt16_PascalString(SvStream& rStrm) +{ + return read_uInt16_lenPrefixed_uInt16s_ToOUString(rStrm); +} + +//Belt and Braces strings, i.e. Pascal-style strings followed by +//null termination, Spolsky calls them "fucked strings" FWIW +//http://www.joelonsoftware.com/articles/fog0000000319.html +OUString read_uInt8_BeltAndBracesString(SvStream& rStrm, rtl_TextEncoding eEnc); +OUString read_uInt16_BeltAndBracesString(SvStream& rStrm); + +//--Line above which the code has meaningful comments + +class WW8ScannerBase; +class WW8PLCFspecial; +struct WW8PLCFxDesc; +class WW8PLCFx_PCD; + +/** + reads array of strings (see MS documentation: String Table stored in File) + returns NOT the original pascal strings but an array of converted char* + + attention: the *extra data* of each string are SKIPPED and ignored + */ +void WW8ReadSTTBF(bool bVer8, SvStream& rStrm, sal_uInt32 nStart, sal_Int32 nLen, + sal_uInt16 nExtraLen, rtl_TextEncoding eCS, std::vector<OUString> &rArray, + std::vector<ww::bytes>* pExtraArray = nullptr, std::vector<OUString>* pValueArray = nullptr); + +struct WW8FieldDesc +{ + WW8_CP nLen; ///< total length (to skip over text) + WW8_CP nSCode; ///< start of instructions code + WW8_CP nLCode; ///< length + WW8_CP nSRes; ///< start of result + WW8_CP nLRes; ///< length ( == 0, if no result ) + sal_uInt16 nId; ///< WW-id for fields + sal_uInt8 nOpt; ///< WW-Flags ( e.g.: changed by user ) + bool bCodeNest:1; ///< instruction used recursively + bool bResNest:1; ///< instruction inserted into result +}; + +struct WW8PLCFxSave1 +{ + sal_uInt32 nPLCFxPos; + sal_uInt32 nPLCFxPos2; ///< for PLCF_Cp_Fkp: PieceIter-Pos + long nPLCFxMemOfs; + WW8_CP nStartCp; ///< for cp based iterator like PAP and CHP + long nCpOfs; + WW8_FC nStartFC; + WW8_CP nAttrStart; + WW8_CP nAttrEnd; + bool bLineEnd; +}; + +/** + among others for fields, that is, the same number of attr as positions, + if Ctor-Param bNoEnd = false +*/ +class WW8PLCFspecial // iterator for PLCFs +{ +private: + std::unique_ptr<sal_Int32[]> pPLCF_PosArray; ///< pointer to Pos-array and to the whole structure + sal_uInt8* pPLCF_Contents; ///< pointer to content-array-part of Pos-array + long nIMax; ///< number of elements + long nIdx; ///< marker where we currently are + sal_uInt32 nStru; + + WW8PLCFspecial(const WW8PLCFspecial&) = delete; + WW8PLCFspecial& operator=(const WW8PLCFspecial&) = delete; + +public: + WW8PLCFspecial(SvStream* pSt, sal_uInt32 nFilePos, sal_uInt32 nPLCF, + sal_uInt32 nStruct); + long GetIdx() const { return nIdx; } + void SetIdx( long nI ) { nIdx = nI; } + long GetIMax() const { return nIMax; } + bool SeekPos(long nPos); // walks over FC- or CP-value + // resp. next biggest value + bool SeekPosExact(long nPos); + sal_Int32 Where() const + { return ( nIdx >= nIMax ) ? SAL_MAX_INT32 : pPLCF_PosArray[nIdx]; } + bool Get(WW8_CP& rStart, void*& rpValue) const; + bool GetData(long nIdx, WW8_CP& rPos, void*& rpValue) const; + + const void* GetData( long nInIdx ) const + { + return ( nInIdx >= nIMax ) ? nullptr + : static_cast<const void*>(&pPLCF_Contents[nInIdx * nStru]); + } + sal_Int32 GetPos( long nInIdx ) const + { return ( nInIdx >= nIMax ) ? SAL_MAX_INT32 : pPLCF_PosArray[nInIdx]; } + + void advance() + { + if (nIdx <= nIMax) + ++nIdx; + } +}; + +/** simple Iterator for SPRMs */ +class WW8SprmIter +{ +private: + const wwSprmParser &mrSprmParser; + // these members will be updated + const sal_uInt8* pSprms; // remaining part of the SPRMs ( == start of current SPRM) + const sal_uInt8* pCurrentParams; // start of current SPRM's parameters + sal_uInt16 nCurrentId; + sal_uInt16 nCurrentSize; + + sal_Int32 nRemLen; // length of remaining SPRMs (including current SPRM) + + void UpdateMyMembers(); + +public: + explicit WW8SprmIter(const sal_uInt8* pSprms_, sal_Int32 nLen_, + const wwSprmParser &rSprmParser); + void SetSprms(const sal_uInt8* pSprms_, sal_Int32 nLen_); + SprmResult FindSprm(sal_uInt16 nId, bool bFindFirst, const sal_uInt8* pNextByteMatch = nullptr); + void advance(); + const sal_uInt8* GetSprms() const + { return ( pSprms && (0 < nRemLen) ) ? pSprms : nullptr; } + const sal_uInt8* GetCurrentParams() const { return pCurrentParams; } + sal_uInt16 GetCurrentId() const { return nCurrentId; } + sal_Int32 GetRemLen() const { return nRemLen; } + +private: + WW8SprmIter(const WW8SprmIter&) = delete; + WW8SprmIter& operator=(const WW8SprmIter&) = delete; +}; + +/* among others for FKPs to normal attr., i.e. one less attr than positions */ +class WW8PLCF // Iterator for PLCFs +{ +private: + std::unique_ptr<WW8_CP[]> pPLCF_PosArray; // pointer to Pos-array and the whole structure + sal_uInt8* pPLCF_Contents; // pointer to content-array-part of Pos-array + sal_Int32 nIMax; // number of elements + sal_Int32 nIdx; + int nStru; + + void ReadPLCF(SvStream& rSt, WW8_FC nFilePos, sal_uInt32 nPLCF); + + /* + If a PLC is missing in the doc and the FKPs stand alone, + we create a PLC with this: + */ + void GeneratePLCF(SvStream& rSt, sal_Int32 nPN, sal_Int32 ncpN); + + void MakeFailedPLCF(); + + void TruncToSortedRange(); +public: + WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct, + WW8_CP nStartPos = -1); + + /* + the following ctor generates a PLC from nPN and ncpN, if necessary + */ + WW8PLCF(SvStream& rSt, WW8_FC nFilePos, sal_Int32 nPLCF, int nStruct, + WW8_CP nStartPos, sal_Int32 nPN, sal_Int32 ncpN); + + sal_Int32 GetIdx() const { return nIdx; } + void SetIdx( sal_Int32 nI ) { nIdx = nI; } + sal_Int32 GetIMax() const { return nIMax; } + bool SeekPos(WW8_CP nPos); + WW8_CP Where() const; + bool Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const; + void advance() { if( nIdx < nIMax ) ++nIdx; } + + const void* GetData( sal_Int32 nInIdx ) const + { + return ( nInIdx >= nIMax ) ? nullptr : + static_cast<const void*>(&pPLCF_Contents[nInIdx * nStru]); + } +}; + +/* for Piece Table (.i.e. FastSave Table) */ +class WW8PLCFpcd +{ + friend class WW8PLCFpcd_Iter; + + std::unique_ptr<sal_Int32[]> pPLCF_PosArray; // pointer to Pos-array and the whole structure + sal_uInt8* pPLCF_Contents; // pointer to content-array-part of Pos-array + long nIMax; + sal_uInt32 nStru; + + WW8PLCFpcd(const WW8PLCFpcd&) = delete; + WW8PLCFpcd& operator=(const WW8PLCFpcd&) = delete; + +public: + WW8PLCFpcd(SvStream* pSt, sal_uInt32 nFilePos, sal_uInt32 nPLCF, + sal_uInt32 nStruct); +}; + +/* multiple WW8PLCFpcd_Iter may point to the same WW8PLCFpcd !!! */ +class WW8PLCFpcd_Iter +{ +private: + WW8PLCFpcd& rPLCF; + long nIdx; + + WW8PLCFpcd_Iter(const WW8PLCFpcd_Iter&) = delete; + WW8PLCFpcd_Iter& operator=(const WW8PLCFpcd_Iter&) = delete; + +public: + WW8PLCFpcd_Iter( WW8PLCFpcd& rPLCFpcd, long nStartPos = -1 ); + long GetIdx() const { return nIdx; } + void SetIdx( long nI ) { nIdx = nI; } + long GetIMax() const { return rPLCF.nIMax; } + bool SeekPos(long nPos); + sal_Int32 Where() const; + bool Get(WW8_CP& rStart, WW8_CP& rEnd, void*& rpValue) const; + void advance() + { + if( nIdx < rPLCF.nIMax ) + ++nIdx; + } +}; + +// PLCF-type: +enum ePLCFT{ CHP=0, PAP, SEP, /*HED, FNR, ENR,*/ PLCF_END }; + +//It's hardcoded that eFTN be the first one: a very poor hack, needs to be fixed +enum eExtSprm { eFTN = 256, eEDN = 257, eFLD = 258, eBKN = 259, eAND = 260, eATNBKN = 261, eFACTOIDBKN = 262 }; + +/* + pure virtual: +*/ +class WW8PLCFx // virtual iterator for Piece Table Exceptions +{ +private: + const WW8Fib& mrFib; + bool bIsSprm; // PLCF of Sprms or other stuff ( Footnote, ... ) + WW8_FC nStartFc; + bool bDirty; + + WW8PLCFx(const WW8PLCFx&) = delete; + WW8PLCFx& operator=(const WW8PLCFx&) = delete; + +public: + WW8PLCFx(const WW8Fib& rFib, bool bSprm) + : mrFib(rFib) + , bIsSprm(bSprm) + , nStartFc(-1) + , bDirty(false) + { + } + virtual ~WW8PLCFx() {} + bool IsSprm() const { return bIsSprm; } + virtual sal_uInt32 GetIdx() const = 0; + virtual void SetIdx(sal_uInt32 nIdx) = 0; + virtual sal_uInt32 GetIdx2() const; + virtual void SetIdx2(sal_uInt32 nIdx); + virtual bool SeekPos(WW8_CP nCpPos) = 0; + virtual WW8_FC Where() = 0; + virtual void GetSprms( WW8PLCFxDesc* p ); + virtual long GetNoSprms( WW8_CP& rStart, WW8_CP&, sal_Int32& rLen ); + virtual void advance() = 0; + virtual sal_uInt16 GetIstd() const { return 0xffff; } + virtual void Save( WW8PLCFxSave1& rSave ) const; + virtual void Restore( const WW8PLCFxSave1& rSave ); + ww::WordVersion GetFIBVersion() const; + const WW8Fib& GetFIB() const { return mrFib; } + void SetStartFc( WW8_FC nFc ) { nStartFc = nFc; } + WW8_FC GetStartFc() const { return nStartFc; } + void SetDirty(bool bIn) {bDirty=bIn;} + bool GetDirty() const {return bDirty;} +}; + +class WW8PLCFx_PCDAttrs : public WW8PLCFx +{ +private: + WW8PLCFpcd_Iter* pPcdI; + WW8PLCFx_PCD* pPcd; + std::vector<std::unique_ptr<sal_uInt8[]>> const & mrGrpprls; // attribute of Piece-table + SVBT32 aShortSprm; // mini storage: can contain ONE sprm with + // 1 byte param + + WW8PLCFx_PCDAttrs(const WW8PLCFx_PCDAttrs&) = delete; + WW8PLCFx_PCDAttrs& operator=(const WW8PLCFx_PCDAttrs&) = delete; + +public: + WW8PLCFx_PCDAttrs(const WW8Fib& rFib, WW8PLCFx_PCD* pPLCFx_PCD, + const WW8ScannerBase* pBase ); + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nI) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual void GetSprms( WW8PLCFxDesc* p ) override; + virtual void advance() override; + + WW8PLCFpcd_Iter* GetIter() const { return pPcdI; } +}; + +class WW8PLCFx_PCD : public WW8PLCFx // iterator for Piece table +{ +private: + std::unique_ptr<WW8PLCFpcd_Iter> pPcdI; + bool bVer67; + WW8_CP nClipStart; + + WW8PLCFx_PCD(const WW8PLCFx_PCD&) = delete; + WW8PLCFx_PCD& operator=(const WW8PLCFx_PCD&) = delete; + +public: + WW8PLCFx_PCD(const WW8Fib& rFib, WW8PLCFpcd* pPLCFpcd, + WW8_CP nStartCp, bool bVer67P); + virtual ~WW8PLCFx_PCD() override; + sal_uInt32 GetIMax() const; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nI) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual long GetNoSprms( WW8_CP& rStart, WW8_CP&, sal_Int32& rLen ) override; + virtual void advance() override; + WW8_CP CurrentPieceStartFc2Cp( WW8_FC nStartPos ); + WW8_FC CurrentPieceStartCp2Fc( WW8_CP nCp ); + static void CurrentPieceFc2Cp(WW8_CP& rStartPos, WW8_CP& rEndPos, + const WW8ScannerBase *pSBase); + WW8PLCFpcd_Iter* GetPLCFIter() { return pPcdI.get(); } + void SetClipStart(WW8_CP nIn) { nClipStart = nIn; } + WW8_CP GetClipStart() const { return nClipStart; } + + static sal_Int32 TransformPieceAddress(long nfc, bool& bIsUnicodeAddress) + { + bIsUnicodeAddress = 0 == (0x40000000 & nfc); + return bIsUnicodeAddress ? nfc : (nfc & 0x3fffFFFF) / 2; + } +}; + +/** + Iterator for Piece Table Exceptions of Fkps + works only with FCs, not with CPs ! ( Low-Level ) +*/ +class WW8PLCFx_Fc_FKP : public WW8PLCFx +{ +public: + class WW8Fkp // Iterator for Formatted Disk Page + { + private: + class Entry + { + public: + WW8_FC mnFC; + + sal_uInt8* mpData; + sal_uInt16 mnLen; + sal_uInt16 mnIStd; // only for Fkp.Papx (actually Style-Nr) + bool mbMustDelete; + + explicit Entry(WW8_FC nFC) : mnFC(nFC), mpData(nullptr), mnLen(0), + mnIStd(0), mbMustDelete(false) {} + Entry(const Entry &rEntry); + ~Entry(); + bool operator<(const Entry& rEntry) const; + Entry& operator=(const Entry& rEntry); + }; + + sal_uInt8 maRawData[512]; + std::vector<Entry> maEntries; + + long nItemSize; // either 1 Byte or a complete BX + + // Offset in Stream where last read of 512 bytes took place + long nFilePos; + sal_uInt8 mnIdx; // Pos marker + ePLCFT ePLCF; + sal_uInt8 mnIMax; // number of entries + int mnMustRemainCached; // after SaveAllPLCFx, before RestoreAllPLCFx + + wwSprmParser maSprmParser; + + //Fill in an Entry with sanity testing + void FillEntry(Entry &rEntry, std::size_t nDataOffset, sal_uInt16 nLen); + + public: + WW8Fkp (const WW8Fib& rFib, SvStream* pFKPStrm, + SvStream* pDataStrm, long _nFilePos, long nItemSiz, ePLCFT ePl, + WW8_FC nStartFc); + void Reset(WW8_FC nPos); + long GetFilePos() const { return nFilePos; } + sal_uInt8 GetIdx() const { return mnIdx; } + void SetIdx(sal_uInt8 nI); + bool SeekPos(WW8_FC nFc); + WW8_FC Where() const + { + return (mnIdx < mnIMax) ? maEntries[mnIdx].mnFC : WW8_FC_MAX; + } + void advance() + { + if (mnIdx < mnIMax) + ++mnIdx; + } + sal_uInt8* Get( WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen ) const; + sal_uInt16 GetIstd() const { return maEntries[mnIdx].mnIStd; } + + /* + returns a real pointer to the Sprm of type nId, + if such a thing is in the Fkp. + */ + sal_uInt8* GetLenAndIStdAndSprms(sal_Int32& rLen) const; + + /* + calls GetLenAndIStdAndSprms()... + 2020 bFindFirst note: Normally the last SPRM takes effect, so I don't know why HasSprm always returned the first value! + I don't dare to change the default due to regression potential (and slower in the few cases looking for a boolean result), + but first thing to try is use FindFirst as false. + */ + SprmResult HasSprm(sal_uInt16 nId, bool bFindFirst = true); + void HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult); + + const wwSprmParser &GetSprmParser() const { return maSprmParser; } + + void IncMustRemainCache() { ++mnMustRemainCached; } + bool IsMustRemainCache() const { return mnMustRemainCached > 0; } + void DecMustRemainCache() { --mnMustRemainCached; } + }; + +private: + SvStream* pFKPStrm; // input file + SvStream* pDataStrm; // input file + std::unique_ptr<WW8PLCF> pPLCF; +protected: + WW8Fkp* pFkp; +private: + + /* + Keep a cache of eMaxCache entries of previously seen pFkps, which + speeds up considerably table parsing and load save plcfs for what turn + out to be small text frames, which frames generally are + + size : cache hits + cache all : 19168 pap, 48 chp + == 100 : 19166 pap, 48 chp + == 50 : 18918 pap, 48 chp + == 10 : 18549 pap, 47 chp + == 5 : 18515 pap, 47 chp + */ + std::deque<std::unique_ptr<WW8Fkp>> maFkpCache; + enum Limits {eMaxCache = 50000}; + + bool NewFkp(); + + WW8PLCFx_Fc_FKP(const WW8PLCFx_Fc_FKP&) = delete; + WW8PLCFx_Fc_FKP& operator=(const WW8PLCFx_Fc_FKP&) = delete; + +protected: + ePLCFT ePLCF; + std::unique_ptr<WW8PLCFx_PCDAttrs> pPCDAttrs; + +public: + WW8PLCFx_Fc_FKP( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt, + const WW8Fib& rFib, ePLCFT ePl, WW8_FC nStartFcL ); + virtual ~WW8PLCFx_Fc_FKP() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_FC nFcPos) override; + virtual WW8_FC Where() override; + sal_uInt8* GetSprmsAndPos( WW8_FC& rStart, WW8_FC& rEnd, sal_Int32& rLen ); + virtual void advance() override; + virtual sal_uInt16 GetIstd() const override; + void GetPCDSprms( WW8PLCFxDesc& rDesc ); + SprmResult HasSprm(sal_uInt16 nId, bool bFindFirst = true); + void HasSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult); + bool HasFkp() const { return (nullptr != pFkp); } +}; + +/// iterator for Piece Table Exceptions of Fkps works on CPs (high-level) +class WW8PLCFx_Cp_FKP : public WW8PLCFx_Fc_FKP +{ +private: + const WW8ScannerBase& rSBase; + std::unique_ptr<WW8PLCFx_PCD> pPcd; + WW8PLCFpcd_Iter *pPieceIter; + WW8_CP nAttrStart, nAttrEnd; + bool bLineEnd : 1; + bool bComplex : 1; + + WW8PLCFx_Cp_FKP(const WW8PLCFx_Cp_FKP&) = delete; + WW8PLCFx_Cp_FKP& operator=(const WW8PLCFx_Cp_FKP&) = delete; + +public: + WW8PLCFx_Cp_FKP( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt, + const WW8ScannerBase& rBase, ePLCFT ePl ); + virtual ~WW8PLCFx_Cp_FKP() override; + void ResetAttrStartEnd(); + sal_uInt32 GetPCDIdx() const; + virtual sal_uInt32 GetIdx2() const override; + virtual void SetIdx2(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual void GetSprms( WW8PLCFxDesc* p ) override; + virtual void advance() override; + virtual void Save( WW8PLCFxSave1& rSave ) const override; + virtual void Restore( const WW8PLCFxSave1& rSave ) override; +}; + +/// Iterator for Piece Table Exceptions of Sepx +class WW8PLCFx_SEPX : public WW8PLCFx +{ +private: + wwSprmParser maSprmParser; + SvStream* pStrm; + std::unique_ptr<WW8PLCF> pPLCF; + std::unique_ptr<sal_uInt8[]> pSprms; + sal_uInt16 nArrMax; + sal_uInt16 nSprmSiz; + + WW8PLCFx_SEPX(const WW8PLCFx_SEPX&) = delete; + WW8PLCFx_SEPX& operator=(const WW8PLCFx_SEPX&) = delete; + +public: + WW8PLCFx_SEPX( SvStream* pSt, SvStream* pTablexySt, const WW8Fib& rFib, + WW8_CP nStartCp ); + virtual ~WW8PLCFx_SEPX() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual void GetSprms( WW8PLCFxDesc* p ) override; + virtual void advance() override; + SprmResult HasSprm( sal_uInt16 nId ) const; + SprmResult HasSprm( sal_uInt16 nId, sal_uInt8 n2nd ) const; + SprmResult HasSprm( sal_uInt16 nId, const sal_uInt8* pOtherSprms, + long nOtherSprmSiz ) const; + bool Find4Sprms(sal_uInt16 nId1, sal_uInt16 nId2, sal_uInt16 nId3, sal_uInt16 nId4, + SprmResult& r1, SprmResult& r2, SprmResult& r3, SprmResult& r4) const; +}; + +/// iterator for footnotes/endnotes and comments +class WW8PLCFx_SubDoc : public WW8PLCFx +{ +private: + std::unique_ptr<WW8PLCF> pRef; + std::unique_ptr<WW8PLCF> pText; + + WW8PLCFx_SubDoc(const WW8PLCFx_SubDoc&) = delete; + WW8PLCFx_SubDoc& operator=(const WW8PLCFx_SubDoc&) = delete; + +public: + WW8PLCFx_SubDoc(SvStream* pSt, const WW8Fib& rFib, WW8_CP nStartCp, + long nFcRef, long nLenRef, long nFcText, long nLenText, long nStruc); + virtual ~WW8PLCFx_SubDoc() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + + // returns reference descriptors + const void* GetData() const + { + return pRef ? pRef->GetData( pRef->GetIdx() ) : nullptr; + } + + virtual void GetSprms(WW8PLCFxDesc* p) override; + virtual void advance() override; + long Count() const { return pRef ? pRef->GetIMax() : 0; } +}; + +/// Iterator for fields +class WW8PLCFx_FLD : public WW8PLCFx +{ +private: + std::unique_ptr<WW8PLCFspecial> pPLCF; + const WW8Fib& rFib; + WW8PLCFx_FLD(const WW8PLCFx_FLD&) = delete; + WW8PLCFx_FLD& operator=(const WW8PLCFx_FLD &) = delete; + +public: + WW8PLCFx_FLD(SvStream* pSt, const WW8Fib& rMyFib, short nType); + virtual ~WW8PLCFx_FLD() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual void GetSprms(WW8PLCFxDesc* p) override; + virtual void advance() override; + bool StartPosIsFieldStart(); + bool EndPosIsFieldEnd(WW8_CP&); + bool GetPara(long nIdx, WW8FieldDesc& rF); +}; + +enum eBookStatus { BOOK_NORMAL = 0, BOOK_IGNORE = 0x1, BOOK_FIELD = 0x2 }; + +/// Iterator for Booknotes +class WW8PLCFx_Book : public WW8PLCFx +{ +private: + std::unique_ptr<WW8PLCFspecial> pBook[2]; // Start and End Position + std::vector<OUString> aBookNames; // Name + std::vector<eBookStatus> aStatus; + long nIMax; // Number of Booknotes + sal_uInt16 nIsEnd; + sal_Int32 nBookmarkId; // counter incremented by GetUniqueBookmarkName. + + WW8PLCFx_Book(const WW8PLCFx_Book&) = delete; + WW8PLCFx_Book& operator=(const WW8PLCFx_Book&) = delete; + +public: + WW8PLCFx_Book(SvStream* pTableSt,const WW8Fib& rFib); + virtual ~WW8PLCFx_Book() override; + long GetIMax() const { return nIMax; } + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nI) override; + virtual sal_uInt32 GetIdx2() const override; + virtual void SetIdx2(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual long GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) override; + virtual void advance() override; + const OUString* GetName() const; + WW8_CP GetStartPos() const + { return nIsEnd ? WW8_CP_MAX : pBook[0]->Where(); } + long GetLen() const; + bool GetIsEnd() const { return nIsEnd != 0; } + long GetHandle() const; + void SetStatus( sal_uInt16 nIndex, eBookStatus eStat ); + void MapName(OUString& rName); + OUString GetBookmark(long nStart,long nEnd, sal_uInt16 &nIndex); + eBookStatus GetStatus() const; + OUString GetUniqueBookmarkName(const OUString &rSuggestedName); +}; + +/// Handles the import of PlcfAtnBkf and PlcfAtnBkl: start / end position of annotation marks. +class WW8PLCFx_AtnBook : public WW8PLCFx +{ +private: + /// Start and end positions. + std::unique_ptr<WW8PLCFspecial> m_pBook[2]; + /// Number of annotation marks + sal_Int32 nIMax; + bool m_bIsEnd; + + WW8PLCFx_AtnBook(const WW8PLCFx_AtnBook&) = delete; + WW8PLCFx_AtnBook& operator=(const WW8PLCFx_AtnBook&) = delete; + +public: + WW8PLCFx_AtnBook(SvStream* pTableSt,const WW8Fib& rFib); + virtual ~WW8PLCFx_AtnBook() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nI) override; + virtual sal_uInt32 GetIdx2() const override; + virtual void SetIdx2(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual long GetNoSprms( WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen ) override; + virtual void advance() override; + + /// Handle is the unique ID of an annotation mark. + long getHandle() const; + bool getIsEnd() const; +}; + +/// Handles the import of PlcfBkfFactoid and PlcfBklFactoid: start / end position of factoids. +class WW8PLCFx_FactoidBook : public WW8PLCFx +{ +private: + /// Start and end positions. + std::unique_ptr<WW8PLCFspecial> m_pBook[2]; + /// Number of factoid marks + sal_Int32 m_nIMax; + bool m_bIsEnd; + + WW8PLCFx_FactoidBook(const WW8PLCFx_FactoidBook&) = delete; + WW8PLCFx_FactoidBook& operator=(const WW8PLCFx_FactoidBook&) = delete; + +public: + WW8PLCFx_FactoidBook(SvStream* pTableSt,const WW8Fib& rFib); + virtual ~WW8PLCFx_FactoidBook() override; + virtual sal_uInt32 GetIdx() const override; + virtual void SetIdx(sal_uInt32 nI) override; + virtual sal_uInt32 GetIdx2() const override; + virtual void SetIdx2(sal_uInt32 nIdx) override; + virtual bool SeekPos(WW8_CP nCpPos) override; + virtual WW8_CP Where() override; + virtual long GetNoSprms(WW8_CP& rStart, WW8_CP& rEnd, sal_Int32& rLen) override; + virtual void advance() override; + + /// Handle is the unique ID of a factoid mark. + long getHandle() const; + bool getIsEnd() const; +}; + +/* + this is what we use outside: +*/ +struct WW8PLCFManResult +{ + WW8_CP nCpPos; // attribute starting position + long nMemLen; // length for previous + long nCp2OrIdx; // footnote-textpos or index in PLCF + WW8_CP nCurrentCp; // only used by caller + const sal_uInt8* pMemPos;// Mem-Pos for Sprms + sal_uInt16 nSprmId; // Sprm-Id ( 0 = invalid Id -> skip! ) + // (2..255) or pseudo-Sprm-Id (256..260) + // from Winword-Ver8 Sprm-Id (800..) resp. + sal_uInt8 nFlags; // start of paragraph or section +}; + +enum ManMaskTypes +{ + MAN_MASK_NEW_PAP = 1, // new line + MAN_MASK_NEW_SEP = 2 // new section +}; + +enum ManTypes // enums for PLCFMan-ctor +{ + MAN_MAINTEXT = 0, MAN_FTN = 1, MAN_EDN = 2, MAN_HDFT = 3, MAN_AND = 4, + MAN_TXBX = 5, MAN_TXBX_HDFT = 6 +}; + +/* + this is what the manager uses inside: +*/ +struct WW8PLCFxDesc +{ + WW8PLCFx* pPLCFx; + std::stack<sal_uInt16>* pIdStack; // memory for Attr-Id for Attr-end(s) + const sal_uInt8* pMemPos;// where are the Sprm(s) + long nOrigSprmsLen; + + WW8_CP nStartPos; + WW8_CP nEndPos; + + WW8_CP nOrigStartPos; + WW8_CP nOrigEndPos; // The ending character position of a paragraph is + // always one before the end reported in the FKP, + // also a character run that ends on the same location + // as the paragraph mark is adjusted to end just before + // the paragraph mark so as to handle their close + // first. The value being used to determining where the + // properties end is in nEndPos, but the original + // unadjusted end character position is important as + // it can be used as the beginning cp of the next set + // of properties + + WW8_CP nCp2OrIdx; // where are the NoSprm(s) + sal_Int32 nSprmsLen; // how many bytes for further Sprms / length of footnote + long nCpOfs; // for Offset Header .. Footnote + bool bFirstSprm; // for recognizing the first Sprm of a group + bool bRealLineEnd; // false for Pap-Piece-end + sal_Int16 nRelativeJustify; + void Save( WW8PLCFxSave1& rSave ) const; + void Restore( const WW8PLCFxSave1& rSave ); + //With nStartPos set to WW8_CP_MAX then in the case of a pap or chp + //GetSprms will not search for the sprms, but instead take the + //existing ones. + WW8PLCFxDesc() + : pPLCFx(nullptr) + , pIdStack(nullptr) + , pMemPos(nullptr) + , nOrigSprmsLen(0) + , nStartPos(WW8_CP_MAX) + , nEndPos(WW8_CP_MAX) + , nOrigStartPos(WW8_CP_MAX) + , nOrigEndPos(WW8_CP_MAX) + , nCp2OrIdx(WW8_CP_MAX) + , nSprmsLen(0) + , nCpOfs(0) + , bFirstSprm(false) + , bRealLineEnd(false) + , nRelativeJustify(-1) + { + } + void ReduceByOffset(); +}; + +struct WW8PLCFxSaveAll; +class WW8PLCFMan +{ +public: + enum WW8PLCFManLimits {MAN_PLCF_COUNT = 12}; + +private: + wwSprmParser maSprmParser; + WW8_CP m_nCpO; //< Origin Cp -- the basis for nNewCp + + WW8_CP m_nLineEnd; // points *after* the <CR> + sal_uInt16 m_nPLCF; // this many PLCFs are managed + ManTypes m_nManType; + bool mbDoingDrawTextBox; //Normally we adjust the end of attributes + //so that the end of a paragraph occurs + //before the para end mark, but for + //drawboxes we want the true offsets + + WW8PLCFxDesc m_aD[MAN_PLCF_COUNT]; + WW8PLCFxDesc *m_pChp, *m_pPap, *m_pSep, *m_pField, *m_pFootnote, *m_pEdn, *m_pBkm, *m_pPcd, + *m_pPcdA, *m_pAnd, *m_pAtnBkm, *m_pFactoidBkm; + WW8PLCFspecial *m_pFdoa, *m_pTxbx, *m_pTxbxBkd,*m_pMagicTables, *m_pSubdocs; + sal_uInt8* m_pExtendedAtrds; + + const WW8Fib* m_pWwFib; + + sal_uInt16 WhereIdx(bool* pbStart, WW8_CP * pPos=nullptr) const; + void AdjustEnds(WW8PLCFxDesc& rDesc); + void GetNewSprms(WW8PLCFxDesc& rDesc); + static void GetNewNoSprms(WW8PLCFxDesc& rDesc); + void GetSprmStart(short nIdx, WW8PLCFManResult* pRes) const; + void GetSprmEnd(short nIdx, WW8PLCFManResult* pRes) const; + void GetNoSprmStart(short nIdx, WW8PLCFManResult* pRes) const; + void GetNoSprmEnd(short nIdx, WW8PLCFManResult* pRes) const; + void AdvSprm(short nIdx, bool bStart); + void AdvNoSprm(short nIdx, bool bStart); + sal_uInt16 GetId(const WW8PLCFxDesc* p ) const; + +public: + WW8PLCFMan(const WW8ScannerBase* pBase, ManTypes nType, long nStartCp, + bool bDoingDrawTextBox = false); + ~WW8PLCFMan(); + + /* + Where asks on which following position any Attr changes... + */ + WW8_CP Where() const; + + bool Get(WW8PLCFManResult* pResult) const; + void advance(); + sal_uInt16 GetColl() const; // index of actual Style + WW8PLCFx_FLD* GetField() const; + WW8PLCFx_SubDoc* GetEdn() const { return static_cast<WW8PLCFx_SubDoc*>(m_pEdn->pPLCFx); } + WW8PLCFx_SubDoc* GetFootnote() const { return static_cast<WW8PLCFx_SubDoc*>(m_pFootnote->pPLCFx); } + WW8PLCFx_SubDoc* GetAtn() const { return static_cast<WW8PLCFx_SubDoc*>(m_pAnd->pPLCFx); } + WW8PLCFx_Book* GetBook() const { return static_cast<WW8PLCFx_Book*>(m_pBkm->pPLCFx); } + WW8PLCFx_AtnBook* GetAtnBook() const { return static_cast<WW8PLCFx_AtnBook*>(m_pAtnBkm->pPLCFx); } + WW8PLCFx_FactoidBook* GetFactoidBook() const { return static_cast<WW8PLCFx_FactoidBook*>(m_pFactoidBkm->pPLCFx); } + long GetCpOfs() const { return m_pChp->nCpOfs; } // for Header/Footer... + + /* asks, if *current paragraph* has an Sprm of this type */ + SprmResult HasParaSprm(sal_uInt16 nId) const; + + /* asks, if *current textrun* has an Sprm of this type */ + SprmResult HasCharSprm(sal_uInt16 nId) const; + void HasCharSprm(sal_uInt16 nId, std::vector<SprmResult> &rResult) const; + + WW8PLCFx_Cp_FKP* GetChpPLCF() const + { return static_cast<WW8PLCFx_Cp_FKP*>(m_pChp->pPLCFx); } + WW8PLCFx_Cp_FKP* GetPapPLCF() const + { return static_cast<WW8PLCFx_Cp_FKP*>(m_pPap->pPLCFx); } + WW8PLCFx_SEPX* GetSepPLCF() const + { return static_cast<WW8PLCFx_SEPX*>(m_pSep->pPLCFx); } + WW8PLCFxDesc* GetPap() const { return m_pPap; } + void TransferOpenSprms(std::stack<sal_uInt16> &rStack); + void SeekPos( long nNewCp ); + void SaveAllPLCFx( WW8PLCFxSaveAll& rSave ) const; + void RestoreAllPLCFx( const WW8PLCFxSaveAll& rSave ); + WW8PLCFspecial* GetFdoa() const { return m_pFdoa; } + WW8PLCFspecial* GetTxbx() const { return m_pTxbx; } + WW8PLCFspecial* GetTxbxBkd() const { return m_pTxbxBkd; } + WW8PLCFspecial* GetMagicTables() const { return m_pMagicTables; } + WW8PLCFspecial* GetWkbPLCF() const { return m_pSubdocs; } + sal_uInt8* GetExtendedAtrds() const { return m_pExtendedAtrds; } + ManTypes GetManType() const { return m_nManType; } + bool GetDoingDrawTextBox() const { return mbDoingDrawTextBox; } +}; + +struct WW8PLCFxSaveAll +{ + WW8PLCFxSave1 aS[WW8PLCFMan::MAN_PLCF_COUNT] = {}; + WW8PLCFxSaveAll() = default; +}; + +class WW8ScannerBase +{ +friend WW8PLCFx_PCDAttrs::WW8PLCFx_PCDAttrs(const WW8Fib& rFib, + WW8PLCFx_PCD* pPLCFx_PCD, const WW8ScannerBase* pBase ); +friend WW8PLCFx_Cp_FKP::WW8PLCFx_Cp_FKP( SvStream*, SvStream*, SvStream*, + const WW8ScannerBase&, ePLCFT ); + +friend WW8PLCFMan::WW8PLCFMan(const WW8ScannerBase*, ManTypes, long, bool); +friend class SwWW8FltControlStack; + +private: + WW8Fib* m_pWw8Fib; + std::unique_ptr<WW8PLCFx_Cp_FKP> m_pChpPLCF; // Character-Attrs + std::unique_ptr<WW8PLCFx_Cp_FKP> m_pPapPLCF; // Paragraph-Attrs + std::unique_ptr<WW8PLCFx_SEPX> m_pSepPLCF; // Section-Attrs + std::unique_ptr<WW8PLCFx_SubDoc> m_pFootnotePLCF; // Footnotes + std::unique_ptr<WW8PLCFx_SubDoc> m_pEdnPLCF; // EndNotes + std::unique_ptr<WW8PLCFx_SubDoc> m_pAndPLCF; // Comments + std::unique_ptr<WW8PLCFx_FLD> m_pFieldPLCF; // Fields in Main Text + std::unique_ptr<WW8PLCFx_FLD> m_pFieldHdFtPLCF; // Fields in Header / Footer + std::unique_ptr<WW8PLCFx_FLD> m_pFieldTxbxPLCF; // Fields in Textboxes in Main Text + std::unique_ptr<WW8PLCFx_FLD> m_pFieldTxbxHdFtPLCF; // Fields in Textboxes in Header / Footer + std::unique_ptr<WW8PLCFx_FLD> m_pFieldFootnotePLCF; // Fields in Footnotes + std::unique_ptr<WW8PLCFx_FLD> m_pFieldEdnPLCF; // Fields in Endnotes + std::unique_ptr<WW8PLCFx_FLD> m_pFieldAndPLCF; // Fields in Comments + std::unique_ptr<WW8PLCFspecial> m_pMainFdoa; // Graphic Primitives in Main Text + std::unique_ptr<WW8PLCFspecial> m_pHdFtFdoa; // Graphic Primitives in Header / Footer + std::unique_ptr<WW8PLCFspecial> m_pMainTxbx; // Textboxes in Main Text + std::unique_ptr<WW8PLCFspecial> m_pMainTxbxBkd; // Break-Descriptors for them + std::unique_ptr<WW8PLCFspecial> m_pHdFtTxbx; // TextBoxes in Header / Footer + std::unique_ptr<WW8PLCFspecial> m_pHdFtTxbxBkd; // Break-Descriptors for previous + std::unique_ptr<WW8PLCFspecial> m_pMagicTables; // Break-Descriptors for them + std::unique_ptr<WW8PLCFspecial> m_pSubdocs; // subdoc references in master document + std::unique_ptr<sal_uInt8[]> + m_pExtendedAtrds; // Extended ATRDs + std::unique_ptr<WW8PLCFx_Book> m_pBook; // Bookmarks + std::unique_ptr<WW8PLCFx_AtnBook> m_pAtnBook; // Annotationmarks + /// Smart tag bookmarks. + std::unique_ptr<WW8PLCFx_FactoidBook> m_pFactoidBook; + + std::unique_ptr<WW8PLCFpcd> m_pPiecePLCF; // for FastSave ( Basis-PLCF without iterator ) + std::unique_ptr<WW8PLCFpcd_Iter> m_pPieceIter; // for FastSave ( iterator for previous ) + std::unique_ptr<WW8PLCFx_PCD> m_pPLCFx_PCD; // ditto + std::unique_ptr<WW8PLCFx_PCDAttrs> m_pPLCFx_PCDAttrs; + std::vector<std::unique_ptr<sal_uInt8[]>> m_aPieceGrpprls; // attributes of Piece-Table + + std::unique_ptr<WW8PLCFpcd> OpenPieceTable( SvStream* pStr, const WW8Fib* pWwF ); + + WW8ScannerBase(const WW8ScannerBase&) = delete; + WW8ScannerBase& operator=(const WW8ScannerBase&) = delete; + +public: + WW8ScannerBase( SvStream* pSt, SvStream* pTableSt, SvStream* pDataSt, + WW8Fib* pWwF ); + ~WW8ScannerBase(); + bool AreThereFootnotes() const { return m_pFootnotePLCF->Count() > 0; }; + bool AreThereEndnotes() const { return m_pEdnPLCF->Count() > 0; }; + + //If you use WW8Fc2Cp you are almost certainly doing the wrong thing + //when it comes to fastsaved files, avoid like the plague. For export + //given that we never write fastsaved files you can use it, otherwise + //I will beat you with a stick + WW8_CP WW8Fc2Cp(WW8_FC nFcPos) const ; + WW8_FC WW8Cp2Fc(WW8_CP nCpPos, bool* pIsUnicode = nullptr, + WW8_CP* pNextPieceCp = nullptr, bool* pTestFlag = nullptr) const; + + sal_Int32 WW8ReadString(SvStream& rStrm, OUString& rStr, WW8_CP nCurrentStartCp, + long nTotalLen, rtl_TextEncoding eEnc ) const; + +}; + +/** FIB - the File Information Block + + The FIB contains a "magic word" and pointers to the various other parts of + the file, as well as information about the length of the file. + The FIB starts at the beginning of the file. +*/ +class WW8Fib +{ +private: + sal_Unicode m_nNumDecimalSep = u'\0'; + +public: + /** + Program-Version asked for by us: + in Ctor we check if it matches the value of nFib + + 6 == "WinWord 6 or WinWord 95", + 7 == "only WinWord 95" + 8 == "WinWord 97 or newer" + */ + sal_uInt8 m_nVersion = 0; + /* + error status + */ + ErrCode m_nFibError; + /* + data read from FIB by Ctor + (corresponds only approximately to the real structure + of the Winword-FIB) + */ + sal_uInt16 m_wIdent = 0; // 0x0 int magic number + /* + File Information Block (FIB) values: + WinWord 1.0 = 33 + WinWord 2.0 = 45 + WinWord 6.0c for 16bit = 101 + Word 6/32 bit = 104 + Word 95 = 104 + Word 97 = 193 + Word 2000 = 217 + Word 2002 = 257 + Word 2003 = 268 + Word 2007 = 274 + */ + sal_uInt16 m_nFib = 0; // 0x2 FIB version written + sal_uInt16 m_nProduct = 0; // 0x4 product version written by + LanguageType m_lid; // 0x6 language stamp---localized version; + WW8_PN m_pnNext = 0; // 0x8 + + bool m_fDot :1 /*= false*/; // 0xa 0001 + bool m_fGlsy :1 /*= false*/; + bool m_fComplex :1 /*= false*/; // 0004 when 1, file is in complex, fast-saved format. + bool m_fHasPic :1 /*= false*/; // 0008 file contains 1 or more pictures + sal_uInt16 m_cQuickSaves :4 /*= 0*/; // 00F0 count of times file was quicksaved + bool m_fEncrypted :1 /*= false*/; //0100 1 if file is encrypted, 0 if not + bool m_fWhichTableStm :1 /*= false*/; //0200 When 0, this fib refers to the table stream + bool m_fReadOnlyRecommended :1 /*= false*/; + bool m_fWriteReservation :1 /*= false*/; + // named "0Table", when 1, this fib refers to the + // table stream named "1Table". Normally, a file + // will have only one table stream, but under unusual + // circumstances a file may have table streams with + // both names. In that case, this flag must be used + // to decide which table stream is valid. + + bool m_fExtChar :1 /*= false*/; // 1000 =1, when using extended character set in file + bool m_fFarEast :1 /*= false*/; // 4000 =1, probably, when far-East language variants of Word is used to create a file #i90932# + + bool m_fObfuscated :1 /*= false*/; // 8000=1. specifies whether the document is obfuscated using XOR obfuscation. otherwise this bit MUST be ignored. + + sal_uInt16 m_nFibBack = 0; // 0xc + sal_uInt16 m_nHash = 0; // 0xe file encrypted hash + sal_uInt16 m_nKey = 0; // 0x10 file encrypted key + sal_uInt8 m_envr = 0; // 0x12 environment in which file was created + // 0 created by Win Word / 1 created by Mac Word + bool m_fMac :1 /*= false*/; // 0x13 when 1, this file was last saved in the Mac environment + bool m_fEmptySpecial :1 /*= false*/; + bool m_fLoadOverridePage :1 /*= false*/; + bool m_fFuturesavedUndo :1 /*= false*/; + bool m_fWord97Saved :1 /*= false*/; + bool m_fWord2000Saved :1 /*= false*/; + sal_uInt8 :2; + + sal_uInt16 m_chse = 0; // 0x14 default extended character set id for text in document stream. (overridden by chp.chse) + // 0 = ANSI / 256 Macintosh character set. + sal_uInt16 m_chseTables = 0; // 0x16 default extended character set id for text in + // internal data structures: 0 = ANSI, 256 = Macintosh + WW8_FC m_fcMin = 0; // 0x18 file offset of first character of text + WW8_FC m_fcMac = 0; // 0x1c file offset of last character of text + 1 + + // start of WW8 section + sal_uInt16 m_csw = 0; // Count of fields in the array of "shorts" + + // marker: "rgsw" Beginning of the array of shorts + sal_uInt16 m_wMagicCreated = 0; // unique number Identifying the File's creator + // 0x6A62 is the creator ID for Word and is reserved. + // Other creators should choose a different value. + sal_uInt16 m_wMagicRevised = 0; // identifies the File's last modifier + sal_uInt16 m_wMagicCreatedPrivate = 0; // private data + sal_uInt16 m_wMagicRevisedPrivate = 0; // private data + + LanguageType m_lidFE; // Language id if document was written by Far East version + // of Word (i.e. FIB.fFarEast is on) + sal_uInt16 m_clw = 0; // Number of fields in the array of longs + + // end of WW8 section + + // Marker: "rglw" Beginning of the array of longs + WW8_FC m_cbMac = 0; // 0x20 file offset of last byte written to file + 1. + + // WW8_FC u4[4]; // 0x24 + WW8_CP m_ccpText = 0; // 0x34 length of main document text stream + WW8_CP m_ccpFootnote = 0; // 0x38 length of footnote subdocument text stream + WW8_CP m_ccpHdr = 0; // 0x3c length of header subdocument text stream + WW8_CP m_ccpMcr = 0; // 0x40 length of macro subdocument text stream + WW8_CP m_ccpAtn = 0; // 0x44 length of annotation subdocument text stream + WW8_CP m_ccpEdn = 0; // 0x48 length of endnote subdocument text stream + WW8_CP m_ccpTxbx = 0; // 0x4c length of textbox subdocument text stream + WW8_CP m_ccpHdrTxbx = 0; // 0x50 length of header textbox subdocument text stream + + // start of WW8 section + sal_Int32 m_pnFbpChpFirst = 0; // when there was insufficient memory for Word to expand + // the PLCFbte at save time, the PLCFbte is written + // to the file in a linked list of 512-byte pieces + // starting with this pn. + sal_Int32 m_pnFbpPapFirst = 0; // when there was insufficient memory for Word to expand + // the PLCFbte at save time, the PLCFbte is written to + // the file in a linked list of 512-byte pieces + // starting with this pn + + sal_Int32 m_pnFbpLvcFirst = 0; // when there was insufficient memory for Word to expand + // the PLCFbte at save time, the PLCFbte is written to + // the file in a linked list of 512-byte pieces + // starting with this pn + sal_Int32 m_pnLvcFirst = 0; // the page number of the lowest numbered page in the + // document that records LVC FKP information + sal_Int32 m_cpnBteLvc = 0; // count of LVC FKPs recorded in file. In non-complex + // files if the number of entries in the PLCFbtePapx is + // less than this, the PLCFbtePapx is incomplete. + sal_Int32 m_fcIslandFirst = 0; // ? + sal_Int32 m_fcIslandLim = 0; // ? + sal_uInt16 m_cfclcb = 0; // Number of fields in the array of FC/LCB pairs. + /// Specifies the count of 16-bit values corresponding to fibRgCswNew that follow. + sal_uInt16 m_cswNew = 0; + + // end of WW8 section + + // Marker: "rgfclcb" Beginning of array of FC/LCB pairs. + WW8_FC m_fcStshfOrig = 0; // file offset of original allocation for STSH in table + // stream. During fast save Word will attempt to reuse + // this allocation if STSH is small enough to fit. + sal_Int32 m_lcbStshfOrig = 0; // 0x5c count of bytes of original STSH allocation + WW8_FC m_fcStshf = 0; // 0x60 file offset of STSH in file. + sal_Int32 m_lcbStshf = 0; // 0x64 count of bytes of current STSH allocation + WW8_FC m_fcPlcffndRef = 0; // 0x68 file offset of footnote reference PLCF. + sal_Int32 m_lcbPlcffndRef = 0; // 0x6c count of bytes of footnote reference PLCF + // == 0 if no footnotes defined in document. + + WW8_FC m_fcPlcffndText = 0; // 0x70 file offset of footnote text PLCF. + sal_Int32 m_lcbPlcffndText = 0; // 0x74 count of bytes of footnote text PLCF. + // == 0 if no footnotes defined in document + + WW8_FC m_fcPlcfandRef = 0; // 0x78 file offset of annotation reference PLCF. + sal_Int32 m_lcbPlcfandRef = 0; // 0x7c count of bytes of annotation reference PLCF. + + WW8_FC m_fcPlcfandText = 0; // 0x80 file offset of annotation text PLCF. + sal_Int32 m_lcbPlcfandText = 0; // 0x84 count of bytes of the annotation text PLCF + + WW8_FC m_fcPlcfsed = 0; // 8x88 file offset of section descriptor PLCF. + sal_Int32 m_lcbPlcfsed = 0; // 0x8c count of bytes of section descriptor PLCF. + + WW8_FC m_fcPlcfpad = 0; // 0x90 file offset of paragraph descriptor PLCF + sal_Int32 m_lcbPlcfpad = 0; // 0x94 count of bytes of paragraph descriptor PLCF. + // ==0 if file was never viewed in Outline view. + // Should not be written by third party creators + + WW8_FC m_fcPlcfphe = 0; // 0x98 file offset of PLCF of paragraph heights. + sal_Int32 m_lcbPlcfphe = 0; // 0x9c count of bytes of paragraph height PLCF. + // ==0 when file is non-complex. + + WW8_FC m_fcSttbfglsy = 0; // 0xa0 file offset of glossary string table. + sal_Int32 m_lcbSttbfglsy = 0; // 0xa4 count of bytes of glossary string table. + // == 0 for non-glossary documents. + // !=0 for glossary documents. + + WW8_FC m_fcPlcfglsy = 0; // 0xa8 file offset of glossary PLCF. + sal_Int32 m_lcbPlcfglsy = 0; // 0xac count of bytes of glossary PLCF. + // == 0 for non-glossary documents. + // !=0 for glossary documents. + + WW8_FC m_fcPlcfhdd = 0; // 0xb0 byte offset of header PLCF. + sal_Int32 m_lcbPlcfhdd = 0; // 0xb4 count of bytes of header PLCF. + // == 0 if document contains no headers + + WW8_FC m_fcPlcfbteChpx = 0; // 0xb8 file offset of character property bin table.PLCF. + sal_Int32 m_lcbPlcfbteChpx = 0;// 0xbc count of bytes of character property bin table PLCF. + + WW8_FC m_fcPlcfbtePapx = 0; // 0xc0 file offset of paragraph property bin table.PLCF. + sal_Int32 m_lcbPlcfbtePapx = 0;// 0xc4 count of bytes of paragraph property bin table PLCF. + + WW8_FC m_fcPlcfsea = 0; // 0xc8 file offset of PLCF reserved for private use. The SEA is 6 bytes long. + sal_Int32 m_lcbPlcfsea = 0; // 0xcc count of bytes of private use PLCF. + + WW8_FC m_fcSttbfffn = 0; // 0xd0 file offset of font information STTBF. See the FFN file structure definition. + sal_Int32 m_lcbSttbfffn = 0; // 0xd4 count of bytes in sttbfffn. + + WW8_FC m_fcPlcffldMom = 0; // 0xd8 offset in doc stream to the PLCF of field positions in the main document. + sal_Int32 m_lcbPlcffldMom = 0; // 0xdc + + WW8_FC m_fcPlcffldHdr = 0; // 0xe0 offset in doc stream to the PLCF of field positions in the header subdocument. + sal_Int32 m_lcbPlcffldHdr = 0; // 0xe4 + + WW8_FC m_fcPlcffldFootnote = 0; // 0xe8 offset in doc stream to the PLCF of field positions in the footnote subdocument. + sal_Int32 m_lcbPlcffldFootnote = 0; // 0xec + + WW8_FC m_fcPlcffldAtn = 0; // 0xf0 offset in doc stream to the PLCF of field positions in the annotation subdocument. + sal_Int32 m_lcbPlcffldAtn = 0; // 0xf4 + + WW8_FC m_fcPlcffldMcr = 0; // 0xf8 offset in doc stream to the PLCF of field positions in the macro subdocument. + sal_Int32 m_lcbPlcffldMcr = 0; // 9xfc + + WW8_FC m_fcSttbfbkmk = 0; // 0x100 offset in document stream of the STTBF that records bookmark names in the main document + sal_Int32 m_lcbSttbfbkmk = 0; // 0x104 + + WW8_FC m_fcPlcfbkf = 0; // 0x108 offset in document stream of the PLCF that records the beginning CP offsets of bookmarks in the main document. See BKF + sal_Int32 m_lcbPlcfbkf = 0; // 0x10c + + WW8_FC m_fcPlcfbkl = 0; // 0x110 offset in document stream of the PLCF that records the ending CP offsets of bookmarks recorded in the main document. See the BKL structure definition. + sal_Int32 m_lcbPlcfbkl = 0; // 0x114 sal_Int32 + + WW8_FC m_fcCmds = 0; // 0x118 FC + sal_uInt32 m_lcbCmds = 0; // 0x11c + + WW8_FC m_fcPlcfmcr = 0; // 0x120 FC + sal_Int32 m_lcbPlcfmcr = 0; // 0x124 + + WW8_FC m_fcSttbfmcr = 0; // 0x128 FC + sal_Int32 m_lcbSttbfmcr = 0; // 0x12c + + WW8_FC m_fcPrDrvr = 0; // 0x130 file offset of the printer driver information (names of drivers, port etc...) + sal_Int32 m_lcbPrDrvr = 0; // 0x134 count of bytes of the printer driver information (names of drivers, port etc...) + + WW8_FC m_fcPrEnvPort = 0; // 0x138 file offset of the print environment in portrait mode. + sal_Int32 m_lcbPrEnvPort = 0; // 0x13c count of bytes of the print environment in portrait mode. + + WW8_FC m_fcPrEnvLand = 0; // 0x140 file offset of the print environment in landscape mode. + sal_Int32 m_lcbPrEnvLand = 0; // 0x144 count of bytes of the print environment in landscape mode. + + WW8_FC m_fcWss = 0; // 0x148 file offset of Window Save State data structure. See WSS. + sal_Int32 m_lcbWss = 0; // 0x14c count of bytes of WSS. ==0 if unable to store the window state. + + WW8_FC m_fcDop = 0; // 0x150 file offset of document property data structure. + sal_uInt32 m_lcbDop = 0; // 0x154 count of bytes of document properties. + // cbDOP is 84 when nFib < 103 + + WW8_FC m_fcSttbfAssoc = 0; // 0x158 offset to STTBF of associated strings. See STTBFASSOC. + sal_Int32 m_lcbSttbfAssoc = 0; // 0x15C + + WW8_FC m_fcClx = 0; // 0x160 file offset of beginning of information for complex files. + sal_Int32 m_lcbClx = 0; // 0x164 count of bytes of complex file information. 0 if file is non-complex. + + WW8_FC m_fcPlcfpgdFootnote = 0; // 0x168 file offset of page descriptor PLCF for footnote subdocument. + sal_Int32 m_lcbPlcfpgdFootnote = 0; // 0x16C count of bytes of page descriptor PLCF for footnote subdocument. + // ==0 if document has not been paginated. The length of the PGD is 8 bytes. + + WW8_FC m_fcAutosaveSource = 0; // 0x170 file offset of the name of the original file. + sal_Int32 m_lcbAutosaveSource = 0; // 0x174 count of bytes of the name of the original file. + + WW8_FC m_fcGrpStAtnOwners = 0; // 0x178 group of strings recording the names of the owners of annotations + sal_Int32 m_lcbGrpStAtnOwners = 0; // 0x17C count of bytes of the group of strings + + WW8_FC m_fcSttbfAtnbkmk = 0; // 0x180 file offset of the sttbf that records names of bookmarks in the annotation subdocument + sal_Int32 m_lcbSttbfAtnbkmk = 0; // 0x184 length in bytes of the sttbf that records names of bookmarks in the annotation subdocument + + // end of WW67 section + + WW8_FC m_fcPlcfdoaMom = 0; // 0x192 file offset of the FDOA (drawn object) PLCF for main document. + // ==0 if document has no drawn objects. The length of the FDOA is 6 bytes. + // unused starting from Ver8 + sal_Int32 m_lcbPlcfdoaMom = 0; // 0x196 length in bytes of the FDOA PLCF of the main document + // unused starting from Ver8 + WW8_FC m_fcPlcfdoaHdr = 0; // 0x19A file offset of the FDOA (drawn object) PLCF for the header document. + // ==0 if document has no drawn objects. The length of the FDOA is 6 bytes. + // unused starting from Ver8 + sal_Int32 m_lcbPlcfdoaHdr = 0; // 0x19E length in bytes of the FDOA PLCF of the header document + // unused starting from Ver8 + + WW8_FC m_fcPlcfspaMom = 0; // offset in table stream of the FSPA PLCF for main document. + // == 0 if document has no office art objects + // was empty reserve in Ver67 + sal_Int32 m_lcbPlcfspaMom = 0; // length in bytes of the FSPA PLCF of the main document + // was empty reserve in Ver67 + WW8_FC m_fcPlcfspaHdr = 0; // offset in table stream of the FSPA PLCF for header document. + // == 0 if document has no office art objects + // was empty reserve in Ver67 + sal_Int32 m_lcbPlcfspaHdr = 0; // length in bytes of the FSPA PLCF of the header document + // was empty reserve in Ver67 + + WW8_FC m_fcPlcfAtnbkf = 0; // 0x1B2 file offset of BKF (bookmark first) PLCF of the annotation subdocument + sal_Int32 m_lcbPlcfAtnbkf = 0; // 0x1B6 length in bytes of BKF (bookmark first) PLCF of the annotation subdocument + + WW8_FC m_fcPlcfAtnbkl = 0; // 0x1BA file offset of BKL (bookmark last) PLCF of the annotation subdocument + sal_Int32 m_lcbPlcfAtnbkl = 0; // 0x1BE length in bytes of BKL (bookmark first) PLCF of the annotation subdocument + + WW8_FC m_fcPms = 0; // 0x1C2 file offset of PMS (Print Merge State) information block + sal_Int32 m_lcbPMS = 0; // 0x1C6 length in bytes of PMS + + WW8_FC m_fcFormFieldSttbf = 0; // 0x1CA file offset of form field Sttbf which contains strings used in form field dropdown controls + sal_Int32 m_lcbFormFieldSttbf = 0; // 0x1CE length in bytes of form field Sttbf + + WW8_FC m_fcPlcfendRef = 0; // 0x1D2 file offset of PLCFendRef which points to endnote references in the main document stream + sal_Int32 m_lcbPlcfendRef = 0; // 0x1D6 + + WW8_FC m_fcPlcfendText = 0; // 0x1DA file offset of PLCFendRef which points to endnote text in the endnote document + // stream which corresponds with the PLCFendRef + sal_Int32 m_lcbPlcfendText = 0; // 0x1DE + + WW8_FC m_fcPlcffldEdn = 0; // 0x1E2 offset to PLCF of field positions in the endnote subdoc + sal_Int32 m_lcbPlcffldEdn = 0; // 0x1E6 + + WW8_FC m_fcPlcfpgdEdn = 0; // 0x1EA offset to PLCF of page boundaries in the endnote subdoc. + sal_Int32 m_lcbPlcfpgdEdn = 0; // 0x1EE + + WW8_FC m_fcDggInfo = 0; // offset in table stream of the office art object table data. + // The format of office art object table data is found in a separate document. + // was empty reserve in Ver67 + sal_Int32 m_lcbDggInfo = 0; // length in bytes of the office art object table data + // was empty reserve in Ver67 + + WW8_FC m_fcSttbfRMark = 0; // 0x1fa offset to STTBF that records the author abbreviations... + sal_Int32 m_lcbSttbfRMark = 0; // 0x1fe + WW8_FC m_fcSttbfCaption = 0; // 0x202 offset to STTBF that records caption titles... + sal_Int32 m_lcbSttbfCaption = 0; // 0x206 + WW8_FC m_fcSttbAutoCaption = 0; // offset in table stream to the STTBF that records the object names and + // indices into the caption STTBF for objects which get auto captions. + sal_Int32 m_lcbSttbAutoCaption = 0; // 0x20e + + WW8_FC m_fcPlcfwkb = 0; // 0x212 offset to PLCF that describes the boundaries of contributing documents... + sal_Int32 m_lcbPlcfwkb = 0; // 0x216 + + WW8_FC m_fcPlcfspl = 0; // offset in table stream of PLCF (of SPLS structures) that records spell check state + // was empty reserve in Ver67 + sal_Int32 m_lcbPlcfspl = 0; // was empty reserve in Ver67 + + WW8_FC m_fcPlcftxbxText = 0; // 0x222 ...PLCF of beginning CP in the text box subdoc + sal_Int32 m_lcbPlcftxbxText = 0; // 0x226 + WW8_FC m_fcPlcffldTxbx = 0; // 0x22a ...PLCF of field boundaries recorded in the textbox subdoc. + sal_Int32 m_lcbPlcffldTxbx = 0; // 0x22e + WW8_FC m_fcPlcfHdrtxbxText = 0;// 0x232 ...PLCF of beginning CP in the header text box subdoc + sal_Int32 m_lcbPlcfHdrtxbxText = 0;// 0x236 + WW8_FC m_fcPlcffldHdrTxbx = 0;// 0x23a ...PLCF of field boundaries recorded in the header textbox subdoc. + sal_Int32 m_lcbPlcffldHdrTxbx = 0;// 0x23e + WW8_FC m_fcStwUser = 0; + sal_uInt32 m_lcbStwUser = 0; + WW8_FC m_fcSttbttmbd = 0; + sal_uInt32 m_lcbSttbttmbd = 0; + + WW8_FC m_fcSttbFnm = 0; // 0x02da offset in the table stream of masters subdocument names + sal_Int32 m_lcbSttbFnm = 0; // 0x02de length + + /* + special list handling for WW8 + */ + WW8_FC m_fcPlcfLst = 0; // 0x02e2 offset in the table stream of list format information. + sal_Int32 m_lcbPlcfLst = 0; // 0x02e6 length + WW8_FC m_fcPlfLfo = 0; // 0x02ea offset in the table stream of list format override information. + sal_Int32 m_lcbPlfLfo = 0; // 0x02ee length + /* + special Break handling for text-box-stories in WW8 + */ + WW8_FC m_fcPlcftxbxBkd = 0; // 0x02f2 PLCF for TextBox-Break-descriptors in the Maintext + sal_Int32 m_lcbPlcftxbxBkd = 0; // 0x02f6 + WW8_FC m_fcPlcfHdrtxbxBkd = 0;// 0x02fa PLCF for TextBox-Break-descriptors in the Header-/Footer- area + sal_Int32 m_lcbPlcfHdrtxbxBkd = 0;// 0x02fe + + // 0x302 - 372 == ignore + /* + ListNames (skip to here!) + */ + WW8_FC m_fcSttbListNames = 0;// 0x0372 PLCF for Listname Table + sal_Int32 m_lcbSttbListNames = 0;// 0x0376 + + WW8_FC m_fcPlcfTch = 0; + sal_Int32 m_lcbPlcfTch = 0; + + // 0x38A - 41A == ignore + WW8_FC m_fcAtrdExtra = 0; + sal_uInt32 m_lcbAtrdExtra = 0; + + // 0x422 - 0x429 == ignore + + /// 0x42a smart-tag bookmark string table offset. + WW8_FC m_fcSttbfBkmkFactoid = 0; + /// 0x42e smart-tag bookmark string table length. + sal_uInt32 m_lcbSttbfBkmkFactoid = 0; + /// 0x432 smart-tag bookmark starts offset. + WW8_FC m_fcPlcfBkfFactoid = 0; + /// 0x436 smart-tag bookmark ends length. + sal_uInt32 m_lcbPlcfBkfFactoid = 0; + + // 0x43a - 0x441 == ignore + + /// 0x442 smart-tag bookmark ends offset. + WW8_FC m_fcPlcfBklFactoid = 0; + /// 0x446 smart-tag bookmark ends length. + sal_uInt32 m_lcbPlcfBklFactoid = 0; + /// 0x44a smart tag data offset. + WW8_FC m_fcFactoidData = 0; + /// 0x44e smart tag data length. + sal_uInt32 m_lcbFactoidData = 0; + + // 0x452 - 0x4b9 == ignore + + /// 0x4ba Plcffactoid offset. + WW8_FC m_fcPlcffactoid = 0; + /// 0x4be Plcffactoid offset. + sal_uInt32 m_lcbPlcffactoid = 0; + + // 0x4bf - 0x4d4 == ignore + + WW8_FC m_fcHplxsdr = 0; //bizarrely, word xp seems to require this set to shows dates from AtrdExtra + sal_uInt32 m_lcbHplxsdr = 0; + + /* + general variables that were used for Ver67 and Ver8, + even though they had different sizes in the corresponding files: + */ + sal_Int32 m_pnChpFirst = 0; + sal_Int32 m_pnPapFirst = 0; + sal_Int32 m_cpnBteChp = 0; + sal_Int32 m_cpnBtePap = 0; + /* + The actual nFib, moved here because some readers assumed + they couldn't read any format with nFib > some constant + */ + sal_uInt16 m_nFib_actual = 0; // 0x05bc #i56856# + + WW8Fib(SvStream& rStrm, sal_uInt8 nWantedVersion,sal_uInt32 nOffset=0); + explicit WW8Fib(sal_uInt8 nVersion, bool bDot = false); + + void WriteHeader(SvStream& rStrm); + void Write(SvStream& rStrm); + static rtl_TextEncoding GetFIBCharset(sal_uInt16 chs, LanguageType nLidLocale); + ww::WordVersion GetFIBVersion() const; + bool GetBaseCp(ManTypes nType, WW8_CP * cp) const; + sal_Unicode getNumDecimalSep() const { return m_nNumDecimalSep;} +}; + +class WW8Style +{ +protected: + WW8Fib& m_rFib; + SvStream& m_rStream; + + sal_uInt16 m_cstd; // Count of styles in stylesheet + sal_uInt16 m_cbSTDBaseInFile; // Length of STD Base as stored in a file + sal_uInt16 m_fStdStylenamesWritten : 1; // Are built-in stylenames stored? + sal_uInt16 : 15; // Spare flags + sal_uInt16 m_stiMaxWhenSaved; // Max sti known when file was written + sal_uInt16 m_istdMaxFixedWhenSaved; // How many fixed-index istds are there? + sal_uInt16 m_nVerBuiltInNamesWhenSaved; // Current version of built-in stylenames + // ftc used by StandardChpStsh for this document + sal_uInt16 m_ftcAsci; + // CJK ftc used by StandardChpStsh for this document + sal_uInt16 m_ftcFE; + // CTL/Other ftc used by StandardChpStsh for this document + sal_uInt16 m_ftcOther; + // CTL ftc used by StandardChpStsh for this document + sal_uInt16 m_ftcBi; + + //No copying + WW8Style(const WW8Style&); + WW8Style& operator=(const WW8Style&); + +public: + WW8Style( SvStream& rSt, WW8Fib& rFibPara ); + std::unique_ptr<WW8_STD> Read1STDFixed(sal_uInt16& rSkip); + std::unique_ptr<WW8_STD> Read1Style(sal_uInt16& rSkip, OUString* pString); + sal_uInt16 GetCount() const { return m_cstd; } +}; + +class WW8Fonts final +{ +private: + WW8Fonts(const WW8Fonts&) = delete; + WW8Fonts& operator=(const WW8Fonts&) = delete; + + std::vector<WW8_FFN> m_aFontA; // Array of Pointers to Font Description + +public: + WW8Fonts( SvStream& rSt, WW8Fib const & rFib ); + const WW8_FFN* GetFont( sal_uInt16 nNum ) const; + sal_uInt16 GetMax() const { return m_aFontA.size(); } +}; + +typedef sal_uInt8 HdFtFlags; +namespace nsHdFtFlags +{ + const HdFtFlags WW8_HEADER_EVEN = 0x01; + const HdFtFlags WW8_HEADER_ODD = 0x02; + const HdFtFlags WW8_FOOTER_EVEN = 0x04; + const HdFtFlags WW8_FOOTER_ODD = 0x08; + const HdFtFlags WW8_HEADER_FIRST = 0x10; + const HdFtFlags WW8_FOOTER_FIRST = 0x20; +} + +/// Document Properties +struct WW8Dop +{ +public: + /* Error Status */ + ErrCode nDopError; + /* + Corresponds only roughly to the actual structure of the Winword DOP, + the winword FIB version matters to what exists. + */ + bool fFacingPages : 1 /*= false*/; // 1 when facing pages should be printed + + bool fWidowControl : 1 /*= false*/; //a: orig 97 docs say + // 1 when widow control is in effect. 0 when widow control disabled. + //b: MS-DOC: Word Binary File Format (.doc) Structure Specification 2008 says + // B - unused1 (1 bit): Undefined and MUST be ignored. + + bool fPMHMainDoc : 1 /*= false*/; // 1 when doc is a main doc for Print Merge Helper, 0 when not; default=0 + sal_uInt16 grfSuppression : 2 /*= 0*/; // 0 Default line suppression storage; 0= form letter line suppression; 1= no line suppression; default=0 + sal_uInt16 fpc : 2 /*= 0*/; // 1 footnote position code: 0 as endnotes, 1 at bottom of page, 2 immediately beneath text + sal_uInt16 : 1; // 0 unused + + sal_uInt16 grpfIhdt : 8 /*= 0*/; // 0 specification of document headers and footers. See explanation under Headers and Footers topic. + + sal_uInt16 rncFootnote : 2 /*= 0*/; // 0 restart index for footnotes, 0 don't restart note numbering, 1 section, 2 page + sal_uInt16 nFootnote : 14 /*= 0*/; // 1 initial footnote number for document + bool fOutlineDirtySave : 1 /*= false*/; // when 1, indicates that information in the hPLCFpad should be refreshed since outline has been dirtied + sal_uInt16 : 7; // reserved + bool fOnlyMacPics : 1 /*= false*/; // when 1, Word believes all pictures recorded in the document were created on a Macintosh + bool fOnlyWinPics : 1 /*= false*/; // when 1, Word believes all pictures recorded in the document were created in Windows + bool fLabelDoc : 1 /*= false*/; // when 1, document was created as a print merge labels document + bool fHyphCapitals : 1 /*= false*/; // when 1, Word is allowed to hyphenate words that are capitalized. When 0, capitalized may not be hyphenated + bool fAutoHyphen : 1 /*= false*/; // when 1, Word will hyphenate newly typed text as a background task + bool fFormNoFields : 1 /*= false*/; + bool fLinkStyles : 1 /*= false*/; // when 1, Word will merge styles from its template + bool fRevMarking : 1 /*= false*/; // when 1, Word will mark revisions as the document is edited + bool fBackup : 1 /*= false*/; // always make backup when document saved when 1. + bool fExactCWords : 1 /*= false*/; + bool fPagHidden : 1 /*= false*/; + bool fPagResults : 1 /*= false*/; + bool fLockAtn : 1 /*= false*/; // when 1, annotations are locked for editing + bool fMirrorMargins : 1 /*= false*/; // swap margins on left/right pages when 1. + bool fReadOnlyRecommended : 1 /*= false*/;// user has recommended that this doc be opened read-only when 1 + bool fDfltTrueType : 1 /*= false*/; // when 1, use TrueType fonts by default (flag obeyed only when doc was created by WinWord 2.x) + bool fPagSuppressTopSpacing : 1 /*= false*/;//when 1, file created with SUPPRESSTOPSPACING=YES in win.ini. (flag obeyed only when doc was created by WinWord 2.x). + bool fProtEnabled : 1 /*= false*/; // when 1, document is protected from edit operations + bool fDispFormFieldSel : 1 /*= false*/;// when 1, restrict selections to occur only within form fields + bool fRMView : 1 /*= false*/; // when 1, show revision markings on screen + bool fRMPrint : 1 /*= false*/; // when 1, print revision marks when document is printed + bool fWriteReservation : 1 /*= false*/; + bool fLockRev : 1 /*= false*/; // when 1, the current revision marking state is locked + bool fEmbedFonts : 1 /*= false*/; // when 1, document contains embedded True Type fonts + // compatibility options + bool copts_fNoTabForInd : 1 /*= false*/; // when 1, don't add automatic tab stops for hanging indent + bool copts_fNoSpaceRaiseLower : 1 /*= false*/; // when 1, don't add extra space for raised or lowered characters + bool copts_fSupressSpbfAfterPgBrk : 1 /*= false*/; // when 1, suppress the paragraph Space Before and Space After options after a page break + bool copts_fWrapTrailSpaces : 1 /*= false*/; // when 1, wrap trailing spaces at the end of a line to the next line + bool copts_fMapPrintTextColor : 1 /*= false*/; // when 1, print colors as black on non-color printers + bool copts_fNoColumnBalance : 1 /*= false*/; // when 1, don't balance columns for Continuous Section starts + bool copts_fConvMailMergeEsc : 1 /*= false*/; + bool copts_fSupressTopSpacing : 1 /*= false*/; // when 1, suppress extra line spacing at top of page + bool copts_fOrigWordTableRules : 1 /*= false*/; // when 1, combine table borders like Word 5.x for the Macintosh + bool copts_fTransparentMetafiles : 1 /*= false*/; // when 1, don't blank area between metafile pictures + bool copts_fShowBreaksInFrames : 1 /*= false*/; // when 1, show hard page or column breaks in frames + bool copts_fSwapBordersFacingPgs : 1 /*= false*/; // when 1, swap left and right pages on odd facing pages + bool copts_fExpShRtn : 1 /*= false*/; // when 1, expand character spaces on the line ending SHIFT+RETURN // #i56856# + + sal_Int16 dxaTab = 0; // 720 twips - default tab width + sal_uInt16 wSpare = 0; + sal_uInt16 dxaHotZ = 0; // width of hyphenation hot zone measured in twips + sal_uInt16 cConsecHypLim = 0; // number of lines allowed to have consecutive hyphens + sal_uInt16 wSpare2 = 0; // reserved + sal_Int32 dttmCreated = 0; // DTTM date and time document was created + sal_Int32 dttmRevised = 0; // DTTM date and time document was last revised + sal_Int32 dttmLastPrint = 0; // DTTM date and time document was last printed + sal_Int16 nRevision = 0; // number of times document has been revised since its creation + sal_Int32 tmEdited = 0; // time document was last edited + sal_Int32 cWords = 0; // count of words tallied by last Word Count execution + sal_Int32 cCh = 0; // count of characters tallied by last Word Count execution + sal_Int16 cPg = 0; // count of pages tallied by last Word Count execution + sal_Int32 cParas = 0; // count of paragraphs tallied by last Word Count execution + sal_uInt16 rncEdn : 2 /*= 0*/; // restart endnote number code: 0 don't restart endnote numbering, 1 section, 2 page + sal_uInt16 nEdn : 14 /*= 0*/; // beginning endnote number + sal_uInt16 epc : 2 /*= 0*/; // endnote position code: 0 at end of section, 3 at end of document + + bool fPrintFormData : 1 /*= false*/; // only print data inside of form fields + bool fSaveFormData : 1 /*= false*/; // only save document data that is inside of a form field. + bool fShadeFormData : 1 /*= false*/; // shade form fields + sal_uInt16 : 2; // reserved + bool fWCFootnoteEdn : 1 /*= false*/; // when 1, include footnotes and endnotes in word count + sal_Int32 cLines = 0; // count of lines tallied by last Word Count operation + sal_Int32 cWordsFootnoteEnd = 0; // count of words in footnotes and endnotes tallied by last Word Count operation + sal_Int32 cChFootnoteEdn = 0; // count of characters in footnotes and endnotes tallied by last Word Count operation + sal_Int16 cPgFootnoteEdn = 0; // count of pages in footnotes and endnotes tallied by last Word Count operation + sal_Int32 cParasFootnoteEdn = 0; // count of paragraphs in footnotes and endnotes tallied by last Word Count operation + sal_Int32 cLinesFootnoteEdn = 0; // count of paragraphs in footnotes and endnotes tallied by last Word Count operation + sal_Int32 lKeyProtDoc = 0; // document protection password key, only valid if dop.fProtEnabled, dop.fLockAtn or dop.fLockRev are 1. + sal_uInt16 wvkSaved : 3 /*= 0*/; // document view kind: 0 Normal view, 1 Outline view, 2 Page View + sal_uInt16 wScaleSaved : 9 /*= 0*/; ///< Specifies the zoom percentage that was in use when the document was saved. + sal_uInt16 zkSaved : 2 /*= 0*/; // document zoom type: 0 percent, 1 whole/entire page, 2 page width, 3 text width/optimal + bool fRotateFontW6 : 1 /*= false*/; + bool iGutterPos : 1 /*= false*/; + + // this should be the end for nFib < 103, otherwise the file is broken! + + /* + for nFib >= 103 it continues: + */ + bool fNoTabForInd : 1 /*= false*/; // see above in compatibility options + bool fNoSpaceRaiseLower : 1 /*= false*/; // see above + bool fSupressSpbfAfterPageBreak : 1 /*= false*/; // see above + bool fWrapTrailSpaces : 1 /*= false*/; // see above + bool fMapPrintTextColor : 1 /*= false*/; // see above + bool fNoColumnBalance : 1 /*= false*/; // see above + bool fConvMailMergeEsc : 1 /*= false*/; // see above + bool fSupressTopSpacing : 1 /*= false*/; // see above + bool fOrigWordTableRules : 1 /*= false*/; // see above + bool fTransparentMetafiles : 1 /*= false*/; // see above + bool fShowBreaksInFrames : 1 /*= false*/; // see above + bool fSwapBordersFacingPgs : 1 /*= false*/; // see above + bool fCompatibilityOptions_Unknown1_13 : 1 /*= false*/; // #i78591# + bool fExpShRtn : 1 /*= false*/; // #i78591# and #i56856# + bool fCompatibilityOptions_Unknown1_15 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_16 : 1 /*= false*/; // #i78591# + bool fSuppressTopSpacingMac5 : 1 /*= false*/; // Suppress extra line spacing at top + // of page like MacWord 5.x + bool fTruncDxaExpand : 1 /*= false*/; // Expand/Condense by whole number of points + bool fPrintBodyBeforeHdr : 1 /*= false*/; // Print body text before header/footer + bool fNoLeading : 1 /*= false*/; // Don't add extra spacebetween rows of text + bool fCompatibilityOptions_Unknown1_21 : 1 /*= false*/; // #i78591# + bool fMWSmallCaps : 1 /*= false*/; // Use larger small caps like MacWord 5.x + bool fCompatibilityOptions_Unknown1_23 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_24 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_25 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_26 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_27 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_28 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_29 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_30 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown1_31 : 1 /*= false*/; // #i78591# + bool fUsePrinterMetrics : 1 /*= false*/; //The magic option + + // this should be the end for nFib <= 105, otherwise the file is broken! + + /* + for nFib > 105 it continues: + */ + sal_Int16 adt = 0; // Autoformat Document Type: + // 0 for normal. + // 1 for letter, and + // 2 for email. + WW8DopTypography doptypography = {}; // see WW8STRUC.HXX + WW8_DOGRID dogrid = {}; // see WW8STRUC.HXX + sal_uInt16 : 1; // reserved + sal_uInt16 lvl : 4 /*= 0*/; // Which outline levels are showing in outline view + sal_uInt16 : 4; // reserved + bool fHtmlDoc : 1 /*= false*/; // This file is based upon an HTML file + sal_uInt16 : 1; // reserved + bool fSnapBorder : 1 /*= false*/; // Snap table and page borders to page border + bool fIncludeHeader : 1 /*= false*/; // Place header inside page border + bool fIncludeFooter : 1 /*= false*/; // Place footer inside page border + bool fForcePageSizePag : 1 /*= false*/; // Are we in online view + bool fMinFontSizePag : 1 /*= false*/; // Are we auto-promoting fonts to >= hpsZoonFontPag? + bool fHaveVersions : 1 /*= false*/; // versioning is turned on + bool fAutoVersion : 1 /*= false*/; // autoversioning is enabled + sal_uInt16 : 14; // reserved + // Skip 12 Bytes here: ASUMI + sal_Int32 cChWS = 0; + sal_Int32 cChWSFootnoteEdn = 0; + sal_Int32 grfDocEvents = 0; + // Skip 4+30+8 Bytes here + sal_Int32 cDBC = 0; + sal_Int32 cDBCFootnoteEdn = 0; + // Skip 4 Bytes here + sal_Int16 nfcFootnoteRef = 0; + sal_Int16 nfcEdnRef = 0; + sal_Int16 hpsZoonFontPag = 0; + sal_Int16 dywDispPag = 0; + + bool fCompatibilityOptions_Unknown2_1 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_2 : 1 /*= false*/; // #i78591# + bool fDontUseHTMLAutoSpacing : 1 /*= false*/; + bool fCompatibilityOptions_Unknown2_4 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_5 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_6 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_7 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_8 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_9 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_10 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_11 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_12 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_13 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_14 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_15 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_16 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_17 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_18 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_19 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_20 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_21 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_22 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_23 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_24 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_25 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_26 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_27 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_28 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_29 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_30 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_31 : 1 /*= false*/; // #i78591# + bool fCompatibilityOptions_Unknown2_32 : 1 /*= false*/; // #i78591# + + sal_uInt16 fUnknown3 : 15 /*= 0*/; + bool fUseBackGroundInAllmodes : 1 /*= false*/; + + bool fDoNotEmbedSystemFont : 1 /*= false*/; + bool fWordCompat : 1 /*= false*/; + bool fLiveRecover : 1 /*= false*/; + bool fEmbedFactoids : 1 /*= false*/; + bool fFactoidXML : 1 /*= false*/; + bool fFactoidAllDone : 1 /*= false*/; + bool fFolioPrint : 1 /*= false*/; + bool fReverseFolio : 1 /*= false*/; + sal_uInt16 iTextLineEnding : 3 /*= 0*/; + bool fHideFcc : 1 /*= false*/; + bool fAcetateShowMarkup : 1 /*= false*/; + bool fAcetateShowAtn : 1 /*= false*/; + bool fAcetateShowInsDel : 1 /*= false*/; + bool fAcetateShowProps : 1 /*= false*/; + + bool bUseThaiLineBreakingRules = false; + + /* Constructor for importing, needs to know the version of word used */ + WW8Dop(SvStream& rSt, sal_Int16 nFib, sal_Int32 nPos, sal_uInt32 nSize); + + /* Constructs default DOP suitable for exporting */ + WW8Dop(); + void Write(SvStream& rStrm, WW8Fib& rFib) const; + + sal_uInt32 GetCompatibilityOptions() const; + void SetCompatibilityOptions(sal_uInt32 a32Bit); + // i#78591# + sal_uInt32 GetCompatibilityOptions2() const; + void SetCompatibilityOptions2(sal_uInt32 a32Bit); +}; + +class WW8PLCF_HdFt +{ +private: + WW8PLCF aPLCF; + short nIdxOffset; + +public: + WW8PLCF_HdFt( SvStream* pSt, WW8Fib const & rFib, WW8Dop const & rDop ); + bool GetTextPos(sal_uInt8 grpfIhdt, sal_uInt8 nWhich, WW8_CP& rStart, WW8_CP& rLen); + void GetTextPosExact(short nIdx, WW8_CP& rStart, WW8_CP& rLen); + void UpdateIndex( sal_uInt8 grpfIhdt ); +}; + +Word2CHPX ReadWord2Chpx(SvStream &rSt, std::size_t nOffset, sal_uInt8 nSize); +std::vector<sal_uInt8> ChpxToSprms(const Word2CHPX &rChpx); + +[[nodiscard]] bool checkRead(SvStream &rSt, void *pDest, sal_uInt32 nLength); + +//MS has a (slightly) inaccurate view of how many twips +//are in the default letter size of a page +const sal_uInt16 lLetterWidth = 12242; +const sal_uInt16 lLetterHeight = 15842; + +#ifdef OSL_BIGENDIAN +void swapEndian(sal_Unicode *pString); +#endif + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8struc.hxx b/sw/source/filter/ww8/ww8struc.hxx new file mode 100644 index 000000000..7e448e1c3 --- /dev/null +++ b/sw/source/filter/ww8/ww8struc.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_FILTER_WW8_WW8STRUC_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8STRUC_HXX + +#include <sal/config.h> + +#include <rtl/ustring.hxx> + +#include <filter/msfilter/util.hxx> +#include <i18nlangtag/lang.h> +#include <tools/color.hxx> +#include <tools/solar.h> +#include <tools/stream.hxx> + +#include <vector> + +#ifdef _WIN32 +# pragma pack(push, 2) +#endif + +class WW8Export; + +inline void Set_UInt8( sal_uInt8 *& p, sal_uInt8 n ) +{ + *p = n; + p+= 1; +} + +inline void Set_UInt16( sal_uInt8 *& p, sal_uInt16 n ) +{ + ShortToSVBT16( n, *reinterpret_cast<SVBT16*>(p) ); + p+= 2; +} + +inline void Set_UInt32( sal_uInt8 *& p, sal_uInt32 n ) +{ + UInt32ToSVBT32( n, *reinterpret_cast<SVBT32*>(p) ); + p+= 4; +} + +struct Word2CHPX +{ + sal_uInt16 fBold:1; + sal_uInt16 fItalic:1; + sal_uInt16 fRMarkDel:1; + sal_uInt16 fOutline:1; + sal_uInt16 fFieldVanish:1; + sal_uInt16 fSmallCaps:1; + sal_uInt16 fCaps:1; + sal_uInt16 fVanish:1; + sal_uInt16 fRMark:1; + sal_uInt16 fSpec:1; + sal_uInt16 fStrike:1; + sal_uInt16 fObj:1; + sal_uInt16 fBoldBi:1; + sal_uInt16 fItalicBi:1; + sal_uInt16 fBiDi:1; + sal_uInt16 fDiacUSico:1; + sal_uInt16 fsIco:1; + sal_uInt16 fsFtc:1; + sal_uInt16 fsHps:1; + sal_uInt16 fsKul:1; + sal_uInt16 fsPos:1; + sal_uInt16 fsSpace:1; + sal_uInt16 fsLid:1; + sal_uInt16 fsIcoBi:1; + sal_uInt16 fsFtcBi:1; + sal_uInt16 fsHpsBi:1; + sal_uInt16 fsLidBi:1; + + sal_uInt16 ftc; + sal_uInt16 hps; + sal_uInt8 qpsSpace:6; + sal_uInt8 fSysVanish:1; + sal_uInt8 fNumRun:1; + sal_uInt8 ico:5; + sal_uInt8 kul:3; + sal_uInt8 hpsPos; + sal_uInt8 icoBi; + sal_uInt16 lid; + sal_uInt16 ftcBi; + sal_uInt16 hpsBi; + sal_uInt16 lidBi; + sal_uInt32 fcPic; + + Word2CHPX() + : fBold(0), + fItalic(0), + fRMarkDel(0), + fOutline(0), + fFieldVanish(0), + fSmallCaps(0), + fCaps(0), + fVanish(0), + fRMark(0), + fSpec(0), + fStrike(0), + fObj(0), + fBoldBi(0), + fItalicBi(0), + fBiDi(0), + fDiacUSico(0), + fsIco(0), + fsFtc(0), + fsHps(0), + fsKul(0), + fsPos(0), + fsSpace(0), + fsLid(0), + fsIcoBi(0), + fsFtcBi(0), + fsHpsBi(0), + fsLidBi(0), + + ftc(0), + hps(0), + qpsSpace(0), + fSysVanish(0), + fNumRun(0), + ico(0), + kul(0), + hpsPos(0), + icoBi(0), + lid(0), + ftcBi(0), + hpsBi(0), + lidBi(0), + fcPic(0) + { + } +}; + +typedef sal_Int16 WW8_PN; +typedef sal_Int32 WW8_FC; +typedef sal_Int32 WW8_CP; + +const WW8_FC WW8_FC_MAX = SAL_MAX_INT32; +const WW8_CP WW8_CP_MAX = SAL_MAX_INT32; + +/** STD - STyle Definition + + The STD contains the entire definition of a style. + It has two parts, a fixed-length base (cbSTDBase bytes long) + and a variable length remainder holding the name, and the upx and upe + arrays (a upx and upe for each type stored in the style, std.cupx) + Note that new fields can be added to the BASE of the STD without + invalidating the file format, because the STSHI contains the length + that is stored in the file. When reading STDs from an older version, + new fields will be zero. +*/ +struct WW8_STD +{ + // Base part of STD: + sal_uInt16 sti : 12; // invariant style identifier + sal_uInt16 fScratch : 1; // spare field for any temporary use, + // always reset back to zero! + sal_uInt16 fInvalHeight : 1; // PHEs of all text with this style are wrong + sal_uInt16 fHasUpe : 1; // UPEs have been generated + sal_uInt16 fMassCopy : 1; // std has been mass-copied; if unused at + // save time, style should be deleted + sal_uInt16 sgc : 4; // style type code + sal_uInt16 istdBase : 12; // base style + sal_uInt16 cupx : 4; // # of UPXs (and UPEs) + sal_uInt16 istdNext : 12; // next style + sal_uInt16 bchUpe; // offset to end of upx's, start of upe's + // new: + // from Ver8 on there are two more fields: + sal_uInt16 fAutoRedef : 1; /* auto redefine style when appropriate */ + sal_uInt16 fHidden : 1; /* hidden from UI? */ + sal_uInt16 : 14; /* unused bits */ + + // Variable length part of STD: + // sal_uInt8 stzName[2]; /* sub-names are separated by chDelimStyle + // char grupx[]; + // the UPEs are not stored on the file; they are a cache of the based-on + // chain + // char grupe[]; +}; + +static_assert(sizeof (WW8_STD) == 10, "this has to match the msword size"); + +/** base for reading AND working on (will have different subclasses */ +struct WW8_FFN_BASE // Font Descriptor +{ + // from Ver6 on + sal_uInt8 cbFfnM1; // 0x0 total length of FFN - 1. + + sal_uInt8 prg: 2; // 0x1:03 pitch request + sal_uInt8 fTrueType : 1; // 0x1:04 when 1, font is a TrueType font + sal_uInt8 _reserved1 : 1; // 0x1:08 reserved + sal_uInt8 ff : 3; // 0x1:70 font family id + sal_uInt8 _reserved2 : 1; // 0x1:80 reserved + + short wWeight; // 0x2 base weight of font + sal_uInt8 chs; // 0x4 character set identifier + sal_uInt8 ibszAlt; // 0x5 index into ffn.szFfn to the name of the alternate font +}; + +static_assert(sizeof (WW8_FFN_BASE) == 6, "this has to match the msword size"); + +/** This is what we use in the Parser (and Dumper) +*/ +struct WW8_FFN +{ + // from Ver8 on as Unicode + OUString sFontname;// 0x6 or 0x40 resp. from Ver8 on zero terminated string that + // records name of font. + // Maximal size of szFfn is 65 characters. + // Attention: This array can be smaller!!! + // Possibly followed by a second sz which records the + // name of an alternate font to use if the first named + // font does not exist on this system. + WW8_FFN_BASE aFFNBase; +}; + +struct WW8_BRCVer6 // BoRder Code (WW6 version) +{ + SVBT16 aBits1 = {}; +// sal_uInt16 dxpLineWidth : 3;// 0007 When dxpLineWidth is 0, 1, 2, 3, 4, or 5, this field is the width of + // a single line of border in units of 0.75 points + // Must be nonzero when brcType is nonzero. + // 6 == dotted, 7 == dashed. +// sal_uInt16 brcType : 2; // 0018 border type code: 0 == none, 1 == single, 2 == thick, 3 == double +// sal_uInt16 fShadow : 1; // 0020 when 1, border is drawn with shadow. Must be 0 when BRC is a substructure of the TC +// sal_uInt16 ico : 5; // 07C0 color code (see chp.ico) +// sal_uInt16 dxpSpace : 5; // F800 width of space to maintain between border and text within border. + // Must be 0 when BRC is a substructure of the TC. Stored in points for Windows. + WW8_BRCVer6() = default; + + sal_uInt8 dxpLineWidth() const + { return aBits1[0] & 0x07; } + sal_uInt8 brcType() const + { return (aBits1[0] & 0x18) >> 3; } + bool fShadow() const + { return !!(aBits1[0] & 0x20); } + sal_uInt8 ico() const + { return ((aBits1[0] & 0xc0) >> 6) | ((aBits1[1] & 0x07) << 2); } + sal_uInt8 dxpSpace() const + { return aBits1[1] >> 3; } +}; + +struct WW8_BRC // BoRder Code (WW8 version) +// Documented at http://msdn.microsoft.com/en-us/library/dd952599.aspx +{ + SVBT16 aBits1 = {}; + SVBT16 aBits2 = {}; +// sal_uInt8 dptLineWidth; +// sal_uInt8 brcType; +// sal_uInt8 ico; +// sal_uInt8 dptSpace : 5 +// bool fShadow : 1; +// bool fFrame : 1; +// bool fReserved : 1; + WW8_BRC() = default; + + sal_uInt8 dptLineWidth() const // border line width (1/8pt) + { return aBits1[0]; } + sal_uInt8 brcType() const // border type (eg single, double, dotted) + { return aBits1[1]; } + sal_uInt8 ico() const // colour index, 1-17 or 0=auto + { return aBits2[0]; } + sal_uInt8 dptSpace() const // space between text & border (pt) + { return aBits2[1] & 0x1f; } + bool fShadow() const // shadow effect + { return !!(aBits2[1] & 0x20); } + bool fFrame() const // 3D frame effect + { return !!(aBits2[1] & 0x40); } + bool isNil() const // nil = no border + { return aBits1[0] == 0xff && aBits1[1] == 0xff; } + + WW8_BRC(sal_uInt8 _dptLineWidth, sal_uInt8 _brcType, sal_uInt8 _ico, + sal_uInt8 _dptSpace, bool _fShadow, bool _fFrame) + { + assert(_dptSpace < 0x20); + aBits1[0] = _dptLineWidth; + aBits1[1] = _brcType; + aBits2[0] = _ico; + aBits2[1] = _dptSpace | (static_cast<sal_uInt8>(_fShadow) << 5) + | (static_cast<sal_uInt8>(_fFrame) << 6); + } + // Convert BRC from WW6 to WW8 format + explicit WW8_BRC(const WW8_BRCVer6& brcVer6); + + // Returns LO border width in twips=1/20pt, taking into account brcType + short DetermineBorderProperties(short *pSpace) const; +}; + +typedef WW8_BRC WW8_BRC5[5]; // 5 * Border Code + +struct WW8_BRCVer9 // BoRder Code (WW9 version) +// Documented at http://msdn.microsoft.com/en-us/library/dd907496.aspx +{ + SVBT32 aBits1 = {}; // border colour (RGB) + SVBT32 aBits2 = {}; +// sal_uInt8 dptLineWidth; // border line width (1/8pt) +// sal_uInt8 brcType; // border type (eg single, double, dotted) +// sal_uInt8 dptSpace : 5; // space between text & border (pt) +// bool fShadow : 1; // border has shadow effect +// bool fFrame : 1; // border has 3D effect +// sal_uInt16 fReserved : 9; // unused + WW8_BRCVer9() = default; + + sal_uInt32 cv() const // colour value (BGR) + { return SVBT32ToUInt32(aBits1); } + sal_uInt8 dptLineWidth() const // border line width (1/8pt) + { return aBits2[0]; } + sal_uInt8 brcType() const // border type (eg single, double, dotted) + { return aBits2[1]; } + sal_uInt8 dptSpace() const // space between text & border (pt) + { return aBits2[2] & 0x1f; } + bool fShadow() const // shadow effect + { return !!(aBits2[2] & 0x20); } + bool fFrame() const // 3D frame effect + { return !!(aBits2[2] & 0x40); } + bool isNil() const // nil = no border + { return SVBT32ToUInt32(aBits2) == 0xffffffff; } + + WW8_BRCVer9(sal_uInt32 _cv, sal_uInt8 _dptLineWidth, sal_uInt8 _brcType, + sal_uInt8 _dptSpace, bool _fShadow, bool _fFrame) + { + assert(_dptSpace < 0x20); + UInt32ToSVBT32(_cv, aBits1); + aBits2[0] = _dptLineWidth; + aBits2[1] = _brcType; + aBits2[2] = _dptSpace | (static_cast<sal_uInt8>(_fShadow) << 5) + | (static_cast<sal_uInt8>(_fFrame) << 6); + aBits2[3] = 0; + } + // Convert BRC from WW8 to WW9 format + explicit WW8_BRCVer9(const WW8_BRC& brcVer8); + + // Returns LO border width in twips=1/20pt, taking into account brcType + short DetermineBorderProperties(short *pSpace=nullptr) const; +}; + +typedef WW8_BRCVer9 WW8_BRCVer9_5[5]; // 5 * Border Code + +enum BRC_Sides +{ + WW8_TOP = 0, WW8_LEFT = 1, WW8_BOT = 2, WW8_RIGHT = 3, WW8_BETW = 4 +}; + +/* +Document Typography Info (DOPTYPOGRAPHY) These options are Far East only, +and are accessible through the Typography tab of the Tools/Options dialog. +*/ +class WW8DopTypography +{ +public: + void ReadFromMem(sal_uInt8 *&pData); + void WriteToMem(sal_uInt8 *&pData) const; + + //Maps what I think is the language this is to affect to the OOo language + LanguageType GetConvertedLang() const; + + sal_uInt16 m_fKerningPunct : 1; // true if we're kerning punctuation + sal_uInt16 m_iJustification : 2; // Kinsoku method of justification: + // 0 = always expand + // 1 = compress punctuation + // 2 = compress punctuation and kana. + sal_uInt16 m_iLevelOfKinsoku : 2; // Level of Kinsoku: + // 0 = Level 1 + // 1 = Level 2 + // 2 = Custom + sal_uInt16 m_f2on1 : 1; // 2-page-on-1 feature is turned on. + sal_uInt16 m_reserved1 : 4; // in 97 it's marked as reserved BUT + sal_uInt16 m_reserved2 : 6; // reserved ? + //we find that the following applies, + //2 == Japanese + //4 == Chinese (VR... + //6 == Korean + //8 == Chinese (Ta... + //perhaps a bit field where the DOP can possibly relate to more than + //one language at a time, nevertheless MS seems to have painted + //themselves into a small corner with one DopTypography for the + //full document, might not matter all that much though ? + + enum RuleLengths {nMaxFollowing = 101, nMaxLeading = 51}; + static const sal_Unicode * GetJapanNotBeginLevel1(); + static const sal_Unicode * GetJapanNotEndLevel1(); + + sal_Int16 m_cchFollowingPunct; // length of rgxchFPunct + sal_Int16 m_cchLeadingPunct; // length of rgxchLPunct + + // array of characters that should never appear at the start of a line + sal_Unicode m_rgxchFPunct[nMaxFollowing]; + // array of characters that should never appear at the end of a line + sal_Unicode m_rgxchLPunct[nMaxLeading]; +}; + +struct WW8_DOGRID +{ + short xaGrid; // x-coord of the upper left-hand corner of the grid + short yaGrid; // y-coord of the upper left-hand corner of the grid + short dxaGrid; // width of each grid square + short dyaGrid; // height of each grid square + + /* attention: you must not put bit fields on top of such a byte array read from a file! + instead put an aBits1 on it and read it out with &. + reason: compilers on Intel and Sparc sort the bits differently + */ + + short dyGridDisplay:7; // the number of grid squares (in the y direction) + // between each gridline drawn on the screen. 0 means + // don't display any gridlines in the y direction. + short fTurnItOff :1; // suppress display of gridlines + short dxGridDisplay:7; // the number of grid squares (in the x direction) + // between each gridline drawn on the screen. 0 means + // don't display any gridlines in the y direction. + short fFollowMargins:1; // if true, the grid will start at the left and top + // margins and ignore xaGrid and yaGrid. +}; + +static_assert(sizeof (WW8_DOGRID) == 10, "this has to match the msword size"); + +struct WW8_PIC +{ + sal_Int32 lcb; // 0x0 number of bytes in the PIC structure plus size of following picture data which may be a Window's metafile, a bitmap, or the filename of a TIFF file. + sal_uInt16 cbHeader; // 0x4 number of bytes in the PIC (to allow for future expansion). + struct { + sal_Int16 mm; // 0x6 int + sal_Int16 xExt; // 0x8 int + sal_Int16 yExt; // 0xa int + sal_Int16 hMF; // 0xc int + }MFP; +// sal_uInt8 bm[14]; // 0xe BITMAP(14 bytes) Window's bitmap structure when PIC describes a BITMAP. + sal_uInt8 rcWinMF[14]; // 0xe rc (rectangle - 8 bytes) rect for window origin + // and extents when metafile is stored -- ignored if 0 + sal_Int16 dxaGoal; // 0x1c horizontal measurement in twips of the rectangle the picture should be imaged within. + sal_Int16 dyaGoal; // 0x1e vertical measurement in twips of the rectangle the picture should be imaged within. + sal_uInt16 mx; // 0x20 horizontal scaling factor supplied by user in .1% units. + sal_uInt16 my; // 0x22 vertical scaling factor supplied by user in .1% units. + sal_Int16 dxaCropLeft; // 0x24 the amount the picture has been cropped on the left in twips. + sal_Int16 dyaCropTop; // 0x26 the amount the picture has been cropped on the top in twips. + sal_Int16 dxaCropRight; // 0x28 the amount the picture has been cropped on the right in twips. + sal_Int16 dyaCropBottom;// 0x2a the amount the picture has been cropped on the bottom in twips. + sal_Int16 brcl : 4; // 000F Obsolete, superseded by brcTop, etc. In + sal_Int16 fFrameEmpty : 1; // 0010 picture consists of a single frame + sal_Int16 fBitmap : 1; // 0020 ==1, when picture is just a bitmap + sal_Int16 fDrawHatch : 1; // 0040 ==1, when picture is an active OLE object + sal_Int16 fError : 1; // 0080 ==1, when picture is just an error message + sal_Int16 bpp : 8; // FF00 bits per pixel, 0 = unknown + WW8_BRC rgbrc[4]; +// BRC brcTop; // 0x2e specification for border above picture +// BRC brcLeft; // 0x30 specification for border to the left +// BRC brcBottom; // 0x32 specification for border below picture +// BRC brcRight; // 0x34 specification for border to the right + sal_Int16 dxaOrigin; // 0x36 horizontal offset of hand annotation origin + sal_Int16 dyaOrigin; // 0x38 vertical offset of hand annotation origin +// sal_uInt8 rgb[]; // 0x3a variable array of bytes containing Window's metafile, bitmap or TIFF file filename. +}; + +struct WW8_PIC_SHADOW +{ + SVBT32 lcb; // 0x0 number of bytes in the PIC structure plus size of following picture data which may be a Window's metafile, a bitmap, or the filename of a TIFF file. + SVBT16 cbHeader; // 0x4 number of bytes in the PIC (to allow for future expansion). + struct { + SVBT16 mm; // 0x6 int + SVBT16 xExt; // 0x8 int + SVBT16 yExt; // 0xa int + SVBT16 hMF; // 0xc int + }MFP; +// sal_uInt8 bm[14]; // 0xe BITMAP(14 bytes) Window's bitmap structure when PIC describes a BITMAP. + sal_uInt8 rcWinMF[14]; // 0xe rc (rectangle - 8 bytes) rect for window origin + // and extents when metafile is stored -- ignored if 0 + SVBT16 dxaGoal; // 0x1c horizontal measurement in twips of the rectangle the picture should be imaged within. + SVBT16 dyaGoal; // 0x1e vertical measurement in twips of the rectangle the picture should be imaged within. + SVBT16 mx; // 0x20 horizontal scaling factor supplied by user in .1% units. + SVBT16 my; // 0x22 vertical scaling factor supplied by user in .1% units. + SVBT16 dxaCropLeft; // 0x24 the amount the picture has been cropped on the left in twips. + SVBT16 dyaCropTop; // 0x26 the amount the picture has been cropped on the top in twips. + SVBT16 dxaCropRight; // 0x28 the amount the picture has been cropped on the right in twips. + SVBT16 dyaCropBottom;// 0x2a the amount the picture has been cropped on the bottom in twips. + sal_uInt8 aBits1; //0x2c + sal_uInt8 aBits2; +// WW8_BRC rgbrc[4]; +// BRC brcTop; // 0x2e specification for border above picture +// BRC brcLeft; // 0x30 specification for border to the left +// BRC brcBottom; // 0x32 specification for border below picture +// BRC brcRight; // 0x34 specification for border to the right +// SVBT16 dxaOrigin; // 0x36 horizontal offset of hand annotation origin +// SVBT16 dyaOrigin; // 0x38 vertical offset of hand annotation origin +// sal_uInt8 rgb[]; // 0x3a variable array of bytes containing Window's metafile, bitmap or TIFF file filename. +}; + +static_assert(sizeof (WW8_PIC_SHADOW) == 0x2E, "this has to match the msword size"); + // "0x2E": cf. SwWW8ImplReader::PicRead pDataStream->Read call + +struct WW8_TBD +{ + sal_uInt8 aBits1; +// sal_uInt8 jc : 3; // 0x07 justification code: 0=left tab, 1=centered tab, 2=right tab, 3=decimal tab, 4=bar +// sal_uInt8 tlc : 3; // 0x38 tab leader code: 0=no leader, 1=dotted leader, + // 2=hyphenated leader, 3=single line leader, 4=heavy line leader +// * int :2 C0 reserved +}; + +struct WW8_TCell // this is the base for further work (corresponds mostly to the Ver8 format) +{ + // The single-bit fields should ideally be bool, but probably need to keep + // them as sal_uInt8 to make them combine with the following two-bit + // nVertAlign: + sal_uInt8 bFirstMerged : 1;// 0001 set to 1 when cell is first cell of a range of cells that have been merged. + sal_uInt8 bMerged : 1;// 0002 set to 1 when cell has been merged with preceding cell. + sal_uInt8 bVertical : 1;// set to 1 when cell has vertical text flow + sal_uInt8 bBackward : 1;// for a vertical table cell, text flow is bottom to top when 1 and is bottom to top when 0. + sal_uInt8 bRotateFont : 1;// set to 1 when cell has rotated characters (i.e. uses @font) + sal_uInt8 bVertMerge : 1;// set to 1 when cell is vertically merged with the cell(s) above and/or below. When cells are vertically merged, the display area of the merged cells are consolidated. The consolidated area is used to display the contents of the first vertically merged cell (the cell with fVertRestart set to 1), and all other vertically merged cells (those with fVertRestart set to 0) must be empty. Cells can only be merged vertically if their left and right boundaries are (nearly) identical (i.e. if corresponding entries in rgdxaCenter of the table rows differ by at most 3). + sal_uInt8 bVertRestart : 1;// set to 1 when the cell is the first of a set of vertically merged cells. The contents of a cell with fVertStart set to 1 are displayed in the consolidated area belonging to the entire set of vertically merged cells. Vertically merged cells with fVertRestart set to 0 must be empty. + sal_uInt8 nVertAlign : 2;// specifies the alignment of the cell contents relative to text flow (e.g. in a cell with bottom to top text flow and bottom vertical alignment, the text is shifted horizontally to match the cell's right boundary): + // 0 top + // 1 center + // 2 bottom + sal_uInt16 fUnused : 7;// reserved - do not remove, fills up the sal_uInt16! + + WW8_BRCVer9 rgbrc[4]; // border codes +//notational convenience for referring to brcTop, brcLeft, etc fields. +// BRC brcTop; // specification of the top border of a table cell +// BRC brcLeft; // specification of left border of table row +// BRC brcBottom; // specification of bottom border of table row +// BRC brcRight; // specification of right border of table row. + + WW8_TCell(): + bFirstMerged(0), bMerged(0), bVertical(0), bBackward(0), bRotateFont(0), bVertMerge(0), + bVertRestart(0), nVertAlign(0), fUnused(0) {} + // default member initializers for the bitfields will only work in C++20 +}; +// cbTC (count of bytes of a TC) is 18(decimal), 12(hex). + +struct WW8_TCellVer6 // read from file +{ + sal_uInt8 aBits1Ver6; + sal_uInt8 aBits2Ver6; +// sal_uInt16 fFirstMerged : 1;// 0001 set to 1 when cell is first cell of a range of cells that have been merged. +// sal_uInt16 fMerged : 1; // 0002 set to 1 when cell has been merged with preceding cell. +// sal_uInt16 fUnused : 14; // FFFC reserved + WW8_BRCVer6 rgbrcVer6[4]; +// notational convenience for referring to brcTop, brcLeft, etc fields: +// BRC brcTop; // specification of the top border of a table cell +// BRC brcLeft; // specification of left border of table row +// BRC brcBottom; // specification of bottom border of table row +// BRC brcRight; // specification of right border of table row. +}; +// cbTC (count of bytes of a TC) is 10(decimal), A(hex). + +struct WW8_TCellVer8 // read from file +{ + SVBT16 aBits1Ver8; // Documentation: see above at WW8_TCell + SVBT16 aUnused; // reserve + WW8_BRC rgbrcVer8[4]; // Documentation: see above at WW8_TCell +}; +// cbTC (count of bytes of a TC) is 20(decimal), 14(hex). + +struct WW8_SHD // struct SHD is missing from the description +{ +private: + sal_uInt16 maBits; +// sal_uInt16 nFore : 5; // 0x001f ForegroundColor +// sal_uInt16 nBack : 5; // 0x03e0 BackgroundColor +// sal_uInt16 nStyle : 5; // 0x7c00 Percentage and Style +// sal_uInt16 nDontKnow : 1; // 0x8000 ??? from Ver8: also for Style + +public: + WW8_SHD() : maBits(0) {} + + sal_uInt8 GetFore() const { return static_cast<sal_uInt8>( maBits & 0x1f); } + sal_uInt8 GetBack() const { return static_cast<sal_uInt8>((maBits >> 5 ) & 0x1f); } + sal_uInt8 GetStyle(bool bVer67) const + { return static_cast<sal_uInt8>((maBits >> 10) & ( bVer67 ? 0x1f : 0x3f ) ); } + + sal_uInt16 GetValue() const { return maBits; } + + void SetWWValue(SVBT16 const nVal) { maBits = SVBT16ToUInt16(nVal); } + + void SetFore(sal_uInt8 nVal) + { + maBits &= 0xffe0; + maBits |= (nVal & 0x1f); + } + void SetBack(sal_uInt8 nVal) + { + maBits &= 0xfc1f; + maBits |= (nVal & 0x1f) << 5; + } + void SetStyle(sal_uInt8 nVal) + { + maBits &= 0x03ff; + maBits |= (nVal & 0x3f) << 10; + } +}; + +struct WW8_ANLV +{ + sal_uInt8 nfc; // 0 number format code, 0=Arabic, 1=Upper case Roman, 2=Lower case Roman + // 3=Upper case Letter, 4=Lower case letter, 5=Ordinal + sal_uInt8 cbTextBefore; // 1 offset into anld.rgch limit of prefix text + sal_uInt8 cbTextAfter; // 2 + sal_uInt8 aBits1; +// sal_uInt8 jc : 2; // 3 : 0x03 justification code, 0=left, 1=center, 2=right, 3=left and right justify +// sal_uInt8 fPrev : 1; // 0x04 when ==1, include previous levels +// sal_uInt8 fHang : 1; // 0x08 when ==1, number will be displayed using a hanging indent +// sal_uInt8 fSetBold : 1; // 0x10 when ==1, boldness of number will be determined by anld.fBold. +// sal_uInt8 fSetItalic : 1;// 0x20 when ==1, italicness of number will be determined by anld.fItalic +// sal_uInt8 fSetSmallCaps : 1;// 0x40 when ==1, anld.fSmallCaps will determine whether number will be displayed in small caps or not. +// sal_uInt8 fSetCaps : 1; // 0x80 when ==1, anld.fCaps will determine whether number will be displayed capitalized or not + sal_uInt8 aBits2; +// sal_uInt8 fSetStrike : 1;// 4 : 0x01 when ==1, anld.fStrike will determine whether the number will be displayed using strikethrough or not. +// sal_uInt8 fSetKul : 1; // 0x02 when ==1, anld.kul will determine the underlining state of the autonumber. +// sal_uInt8 fPrevSpace : 1;// 0x04 when ==1, autonumber will be displayed with a single prefixing space character +// sal_uInt8 fBold : 1; // 0x08 determines boldness of autonumber when anld.fSetBold == 1. +// sal_uInt8 fItalic : 1; // 0x10 determines italicness of autonumber when anld.fSetItalic == 1. +// sal_uInt8 fSmallCaps : 1;// 0x20 determines whether autonumber will be displayed using small caps when anld.fSetSmallCaps == 1. +// sal_uInt8 fCaps : 1; // 0x40 determines whether autonumber will be displayed using caps when anld.fSetCaps == 1. +// sal_uInt8 fStrike : 1; // 0x80 determines whether autonumber will be displayed using caps when anld.fSetStrike == 1. + sal_uInt8 aBits3; +// sal_uInt8 kul : 3; // 5 : 0x07 determines whether autonumber will be displayed with underlining when anld.fSetKul == 1. +// sal_uInt8 ico : 5; // 0xF1 color of autonumber + SVBT16 ftc; // 6 font code of autonumber + SVBT16 hps; // 8 font half point size (or 0=auto) + SVBT16 iStartAt; // 0x0a starting value (0 to 65535) + SVBT16 dxaIndent; // 0x0c *short?* *sal_uInt16?* width of prefix text (same as indent) + SVBT16 dxaSpace; // 0x0e minimum space between number and paragraph +}; +// *cbANLV (count of bytes of ANLV) is 16 (decimal), 10(hex). + +struct WW8_ANLD +{ + WW8_ANLV eAnlv; // 0 + sal_uInt8 fNumber1; // 0x10 number only 1 item per table cell + sal_uInt8 fNumberAcross; // 0x11 number across cells in table rows(instead of down) + sal_uInt8 fRestartHdn; // 0x12 restart heading number on section boundary + sal_uInt8 fSpareX; // 0x13 unused( should be 0) + sal_uInt8 rgchAnld[32]; // 0x14 characters displayed before/after autonumber +}; + +struct WW8_OLST +{ + WW8_ANLV rganlv[9]; // 0 an array of 9 ANLV structures (heading levels) + sal_uInt8 fRestartHdr; // 0x90 when ==1, restart heading on section break + sal_uInt8 fSpareOlst2; // 0x91 reserved + sal_uInt8 fSpareOlst3; // 0x92 reserved + sal_uInt8 fSpareOlst4; // 0x93 reserved + sal_uInt8 rgch[64]; // 0x94 array of 64 chars text before/after number +}; +// cbOLST is 212(decimal), D4(hex). + +struct WW8_FDOA +{ + SVBT32 fc; // 0 FC pointing to drawing object data + SVBT16 ctxbx; // 4 count of textboxes in the drawing object +}; + +struct WW8_DO +{ + SVBT16 dok; // 0 Drawn Object Kind, currently this is always 0 + SVBT16 cb; // 2 size (count of bytes) of the entire DO + sal_uInt8 bx; // 4 x position relative to anchor CP + sal_uInt8 by; // 5 y position relative to anchor CP + + /* + bx and by above are apparently better described by this info from the rtf standard... + + \dobxpage The drawing object is page relative in the x-direction. + \dobxcolumn The drawing object is column relative in the x-direction. + \dobxmargin The drawing object is margin relative in the x-direction. + + \dobypage The drawing object is page relative in the y-direction. + \dobypara The drawing object is paragraph relative in the y-direction. + \dobymargin The drawing object is margin relative in the y-direction. + + */ + + SVBT16 dhgt; // 6 height of DO + SVBT16 aBits1; +// sal_uInt16 fAnchorLock : 1; // 8 1 if the DO anchor is locked +// sal_uInt8[] rgdp; // 0xa variable length array of drawing primitives +}; + +struct WW8_DPHEAD +{ + SVBT16 dpk; // 0 Drawn Primitive Kind REVIEW davebu + // 0=start of grouping, 1=line, 2=textbox, 3=rectangle, + // 4=arc, 5=ellipse, 6=polyline, 7=callout textbox, + // 8=end of grouping, 9=sample primitive holding default values + SVBT16 cb; // 2 size (count of bytes) of this DP + SVBT16 xa; // 4 These 2 points describe the rectangle + SVBT16 ya; // 6 enclosing this DP relative to the origin of + SVBT16 dxa; // 8 the DO + SVBT16 dya; // 0xa +}; + +struct WW8_DP_LINETYPE +{ + SVBT32 lnpc; // LiNe Property Color -- RGB color value + SVBT16 lnpw; // line property weight in twips + SVBT16 lnps; // line property style : 0=Solid, 1=Dashed + // 2=Dotted, 3=Dash Dot, 4=Dash Dot Dot, 5=Hollow +}; + +struct WW8_DP_SHADOW // shading! +{ + SVBT16 shdwpi; // Shadow Property Intensity + SVBT16 xaOffset; // x offset of shadow + SVBT16 yaOffset; // y offset of shadow +}; + +struct WW8_DP_FILL +{ + SVBT32 dlpcFg; // FiLl Property Color ForeGround -- RGB color value + SVBT32 dlpcBg; // Property Color BackGround -- RGB color value + SVBT16 flpp; // FiLl Property Pattern REVIEW davebu +}; + +struct WW8_DP_LINEEND +{ + SVBT16 aStartBits; +// sal_uInt16 eppsStart : 2; // Start EndPoint Property Style + // 0=None, 1=Hollow, 2=Filled +// sal_uInt16 eppwStart : 2; // Start EndPoint Property Weight +// sal_uInt16 epplStart : 2; // Start EndPoint Property length +// sal_uInt16 dummyStart : 10; // Alignment + SVBT16 aEndBits; +// sal_uInt16 eppsEnd : 2; // End EndPoint Property Style +// sal_uInt16 eppwEnd : 2; // End EndPoint Property Weight +// sal_uInt16 epplEnd : 2; // End EndPoint Property length +// sal_uInt16 dummyEnd : 10; // Alignment +}; + +struct WW8_DP_LINE +{ +// WW8_DPHEAD dphead; // 0 Common header for a drawing primitive + SVBT16 xaStart; // starting point for line + SVBT16 yaStart; + SVBT16 xaEnd; // ending point for line + SVBT16 yaEnd; + WW8_DP_LINETYPE aLnt; + WW8_DP_LINEEND aEpp; + WW8_DP_SHADOW aShd; +}; + +struct WW8_DP_TXTBOX +{ + WW8_DP_LINETYPE aLnt; + WW8_DP_FILL aFill; + WW8_DP_SHADOW aShd; + SVBT16 aBits1; +// sal_uInt16 fRoundCorners : 1; //0x24 0001 1 if the textbox has rounded corners +// sal_uInt16 zaShape : 15; // 0x24 000e REVIEW davebu + SVBT16 dzaInternalMargin; // 0x26 REVIEW davebu +}; + +struct WW8_DP_RECT +{ + WW8_DP_LINETYPE aLnt; + WW8_DP_FILL aFill; + WW8_DP_SHADOW aShd; + SVBT16 aBits1; +// sal_uInt16 fRoundCorners : 1; // 0x24 0001 1 if the textbox has rounded corners +// sal_uInt16 zaShape : 15; // 0x24 000e REVIEW davebu +}; + +struct WW8_DP_ARC +{ + WW8_DP_LINETYPE aLnt; + WW8_DP_FILL aFill; + WW8_DP_SHADOW aShd; + sal_uInt8 fLeft; // 0x24 00ff REVIEW davebu + sal_uInt8 fUp; // 0x24 ff00 REVIEW davebu +// sal_uInt16 fLeft : 8; // 0x24 00ff REVIEW davebu +// sal_uInt16 fUp : 8; // 0x24 ff00 REVIEW davebu +}; + +struct WW8_DP_ELLIPSE +{ + WW8_DP_LINETYPE aLnt; + WW8_DP_FILL aFill; + WW8_DP_SHADOW aShd; +}; + +struct WW8_DP_POLYLINE +{ + WW8_DP_LINETYPE aLnt; + WW8_DP_FILL aFill; + WW8_DP_LINEEND aEpp; + WW8_DP_SHADOW aShd; + SVBT16 aBits1; +// sal_uInt16 fPolygon : 1; // 0x28 0001 1 if this is a polygon +// sal_uInt16 cpt : 15; // 0x28 00fe count of points +// short xaFirst; // 0x2a These are the endpoints of the first line. +// short yaFirst; // 0x2c +// short xaEnd; // 0x2e +// short yaEnd; // 0x30 +// short rgpta[]; // 0x32 An array of xa,ya pairs for the remaining points +}; + +struct WW8_DP_CALLOUT_TXTBOX +{ + SVBT16 flags; // 0x0c REVIEW davebu flags + SVBT16 dzaOffset; // 0x0e REVIEW davebu + SVBT16 dzaDescent; // 0x10 REVIEW davebu + SVBT16 dzaLength; // 0x12 REVIEW davebu + WW8_DPHEAD dpheadTxbx; // 0x14 DPHEAD for a textbox + WW8_DP_TXTBOX dptxbx; // 0x20 DP for a textbox + WW8_DPHEAD dpheadPolyLine; // 0x4c DPHEAD for a Polyline + WW8_DP_POLYLINE dpPolyLine; // 0x48 DP for a polyline +}; + +struct WW8_PCD +{ + sal_uInt8 aBits1; +// sal_uInt8 fNoParaLast : 1; // when 1, means that piece contains no end of paragraph marks. +// sal_uInt8 fPaphNil : 1; // used internally by Word +// sal_uInt8 fCopied : 1; // used internally by Word +// * int :5 + sal_uInt8 aBits2; // fn int:8, used internally by Word + SVBT32 fc; // file offset of beginning of piece. The size of the + // ithpiece can be determined by subtracting rgcp[i] of + // the containing plcfpcd from its rgcp[i+1]. + SVBT16 prm; // PRM contains either a single sprm or else an index number + // of the grpprl which contains the sprms that modify the + // properties of the piece. +}; + +// AnnoTation References Descriptor (ATRD) +struct WW8_ATRD // for version 8 +{ + SVBT16 xstUsrInitl[ 10 ]; // pascal-style String holding initials + // of annotation author + SVBT16 ibst; // index into GrpXstAtnOwners + SVBT16 ak; // not used + SVBT16 grfbmc; // not used + SVBT32 ITagBkmk; // when not -1, this tag identifies the + // annotation bookmark that locates the + // range of CPs in the main document which + // this annotation references. +}; + +struct WW8_ATRDEXTRA +{ + // --- Extended bit since Word 2002 --- + + SVBT32 dttm; + SVBT16 bf; + SVBT32 cDepth; + SVBT32 diatrdParent; + SVBT32 Discussitem; +}; + +struct WW67_ATRD // for versions 6/7 +{ + char xstUsrInitl[ 10 ]; // pascal-style String holding initials + // of annotation author + SVBT16 ibst; // index into GrpXstAtnOwners + SVBT16 ak; // not used + SVBT16 grfbmc; // not used + SVBT32 ITagBkmk; // when not -1, this tag identifies the + // annotation bookmark that locates the + // range of CPs in the main document which + // this annotation references. +}; + +struct WW8_TablePos +{ + sal_Int16 nSp26; + sal_Int16 nSp27; + sal_Int16 nLeMgn; + sal_Int16 nRiMgn; + sal_Int16 nUpMgn; + sal_Int16 nLoMgn; + sal_uInt8 nSp29; + sal_uInt8 nSp37; + bool bNoFly; +}; + +struct WW8_FSPA +{ +public: + sal_Int32 nSpId; //Shape Identifier. Used in conjunction with the office art data (found via fcDggInfo in the FIB) to find the actual data for this shape. + sal_Int32 nXaLeft; //left of rectangle enclosing shape relative to the origin of the shape + sal_Int32 nYaTop; //top of rectangle enclosing shape relative to the origin of the shape + sal_Int32 nXaRight; //right of rectangle enclosing shape relative to the origin of the shape + sal_Int32 nYaBottom;//bottom of the rectangle enclosing shape relative to the origin of the shape + sal_uInt16 bHdr:1; + //0001 1 in the undo doc when shape is from the header doc, 0 otherwise (undefined when not in the undo doc) + sal_uInt16 nbx:2; + //0006 x position of shape relative to anchor CP + //0 relative to page margin + //1 relative to top of page + //2 relative to text (column for horizontal text; paragraph for vertical text) + //3 reserved for future use + sal_uInt16 nby:2; + //0018 y position of shape relative to anchor CP + //0 relative to page margin + //1 relative to top of page + //2 relative to text (paragraph for horizontal text; column for vertical text) + sal_uInt16 nwr:4; + //01E0 text wrapping mode + //0 like 2, but doesn't require absolute object + //1 no text next to shape + //2 wrap around absolute object + //3 wrap as if no object present + //4 wrap tightly around object + //5 wrap tightly, but allow holes + //6-15 reserved for future use + sal_uInt16 nwrk:4; + //1E00 text wrapping mode type (valid only for wrapping modes 2 and 4 + //0 wrap both sides + //1 wrap only on left + //2 wrap only on right + //3 wrap only on largest side + sal_uInt16 bRcaSimple:1; + //2000 when set, temporarily overrides bx, by, forcing the xaLeft, xaRight, yaTop, and yaBottom fields to all be page relative. + sal_uInt16 bBelowText:1; + //4000 + //1 shape is below text + //0 shape is above text + sal_uInt16 bAnchorLock:1; + //8000 1 anchor is locked + // 0 anchor is not locked + sal_Int32 nTxbx; //count of textboxes in shape (undo doc only) +public: + enum FSPAOrient {RelPgMargin, RelPageBorder, RelText}; +}; + +struct WW8_FSPA_SHADOW // all members at same position and size +{ // due to: pF = (WW8_FSPA*)pFS; + SVBT32 nSpId; + SVBT32 nXaLeft; + SVBT32 nYaTop; + SVBT32 nXaRight; + SVBT32 nYaBottom; + SVBT16 aBits1; + SVBT32 nTxbx; +}; + +static_assert(sizeof (WW8_FSPA_SHADOW) == 26, "this has to match the msword size"); + // "26": cf. WW8ScannerBase ctor case 8 creation of pMainFdoa and pHdFtFdoa + +struct WW8_TXBXS +{ + SVBT32 cTxbx_iNextReuse; + SVBT32 cReusable; + SVBT16 fReusable; + SVBT32 reserved; + SVBT32 ShapeId; + SVBT32 txidUndo; +}; + +struct WW8_STRINGID +{ + // M.M. This is the extra data stored in the SttbfFnm + // For now I only need the String Id + SVBT16 nStringId; + SVBT16 reserved1; + SVBT16 reserved2; + SVBT16 reserved3; +}; + +struct WW8_WKB +{ + // M.M. This is the WkbPLCF struct + // For now I only need the Link Id + SVBT16 reserved1; + SVBT16 reserved2; + SVBT16 reserved3; + SVBT16 nLinkId; + SVBT16 reserved4; + SVBT16 reserved5; +}; + +#ifdef _WIN32 +# pragma pack(pop) +#endif + +// Maximum number of columns according the WW8 specification +static const sal_uInt8 MAX_NO_OF_SEP_COLUMNS = 44; + +struct SEPr +{ + SEPr(); + sal_uInt8 bkc; + sal_uInt8 fTitlePage; + sal_Int8 fAutoPgn; + sal_uInt8 nfcPgn; + sal_uInt8 fUnlocked; + sal_uInt8 cnsPgn; + sal_uInt8 fPgnRestart; + sal_uInt8 fEndNote; + sal_Int8 lnc; + sal_Int8 grpfIhdt; + sal_uInt16 nLnnMod; + sal_Int32 dxaLnn; + sal_Int16 dxaPgn; + sal_Int16 dyaPgn; + sal_Int8 fLBetween; + sal_Int8 vjc; + sal_uInt16 dmBinFirst; + sal_uInt16 dmBinOther; + sal_uInt16 dmPaperReq; +/* + 28 1C brcTop BRC top page border + + 32 20 brcLeft BRC left page border + + 36 24 brcBottom BRC bottom page border + + 40 28 brcRight BRC right page border +*/ + sal_Int16 fPropRMark; + sal_Int16 ibstPropRMark; + sal_Int32 dttmPropRMark; //DTTM + sal_Int32 dxtCharSpace; + sal_Int32 dyaLinePitch; + sal_uInt16 clm; + sal_Int16 reserved1; + sal_uInt8 dmOrientPage; + sal_uInt8 iHeadingPgn; + sal_uInt16 pgnStart; + sal_Int16 lnnMin; + sal_uInt16 wTextFlow; + sal_Int16 reserved2; + sal_uInt16 pgbApplyTo:3; + sal_uInt16 pgbPageDepth:2; + sal_Int16 pgbOffsetFrom:3; + sal_Int16 :8; + sal_uInt32 xaPage; + sal_uInt32 yaPage; + sal_uInt32 xaPageNUp; + sal_uInt32 yaPageNUp; + sal_uInt32 dxaLeft; + sal_uInt32 dxaRight; + sal_Int32 dyaTop; + sal_Int32 dyaBottom; + sal_uInt32 dzaGutter; + sal_uInt32 dyaHdrTop; + sal_uInt32 dyaHdrBottom; + sal_Int16 ccolM1; // have to be less than MAX_NO_OF_SEP_COLUMNS according the WW8 specification + sal_Int8 fEvenlySpaced; + sal_Int8 reserved3; + sal_uInt8 fBiDi; + sal_uInt8 fFacingCol; + sal_uInt8 fRTLGutter; + sal_uInt8 fRTLAlignment; + sal_Int32 dxaColumns; + + // Fixed array - two entries for each SEP column to store width of column and spacing to next column. + // At odd index values [1,3,5,...] the column widths are stored. + // At even index values [2,4,6,...] the spacings to the next columns are stored. + // Value at index 0 is initialized with 0 and used for easier iteration on the array + sal_Int32 rgdxaColumnWidthSpacing[MAX_NO_OF_SEP_COLUMNS*2 + 1] = {}; + + sal_Int32 dxaColumnWidth; + sal_uInt8 dmOrientFirst; + sal_uInt8 fLayout; + sal_Int16 reserved4; +}; + +namespace wwUtility +{ + inline sal_uInt32 RGBToBGR(::Color nColour) + { + // we can use this because the translation is symmetric + return msfilter::util::BGRToRGB(sal_uInt32(nColour)); + } +} + +/// [MS-OSHARED] FactoidType: one smart tag type. +class MSOFactoidType +{ +public: + MSOFactoidType(); + void Read(SvStream& rStream); + void Write(WW8Export& rExport); + + sal_uInt32 m_nId; + OUString m_aUri; + OUString m_aTag; +}; + +/// [MS-OSHARED] PropertyBagStore: smart tag types and string store. +class MSOPropertyBagStore +{ +public: + void Read(SvStream& rStream); + void Write(WW8Export& rExport); + + std::vector<MSOFactoidType> m_aFactoidTypes; + std::vector<OUString> m_aStringTable; +}; + +/// [MS-OSHARED] Property: stores information about one smart-tag key/value. +class MSOProperty +{ +public: + MSOProperty(); + void Read(SvStream& rStream); + void Write(SvStream& rStream); + + /// Index into MSOPropertyBagStore::m_aStringTable. + sal_uInt32 m_nKey; + /// Index into MSOPropertyBagStore::m_aStringTable. + sal_uInt32 m_nValue; +}; + +/// [MS-OSHARED] PropertyBag: stores information about one smart tag. +class MSOPropertyBag +{ +public: + MSOPropertyBag(); + bool Read(SvStream& rStream); + void Write(WW8Export& rExport); + + /// Matches MSOFactoidType::m_nId in MSOPropertyBagStore::m_aFactoidTypes. + sal_uInt16 m_nId; + std::vector<MSOProperty> m_aProperties; +}; + +/// [MS-DOC] SmartTagData: stores information about all smart tags in the document. +class WW8SmartTagData +{ +public: + void Read(SvStream& rStream, WW8_FC fcFactoidData, sal_uInt32 lcbFactoidData); + void Write(WW8Export& rExport); + + MSOPropertyBagStore m_aPropBagStore; + std::vector<MSOPropertyBag> m_aPropBags; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8toolbar.cxx b/sw/source/filter/ww8/ww8toolbar.cxx new file mode 100644 index 000000000..f41121945 --- /dev/null +++ b/sw/source/filter/ww8/ww8toolbar.cxx @@ -0,0 +1,1006 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "ww8toolbar.hxx" +#include "ww8scan.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/IndexedPropertyValues.hpp> +#include <com/sun/star/ui/XUIConfigurationPersistence.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/ui/ItemType.hpp> +#include <fstream> +#include <comphelper/documentinfo.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <o3tl/safeint.hxx> +#include <sfx2/objsh.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/configmgr.hxx> +#include <map> +#include <sal/log.hxx> + +using namespace com::sun::star; + +// no. of visual data elements in a SwCTB ( fixed ) +const short nVisualData = 5; + +typedef std::map< sal_Int16, OUString > IdToString; + +namespace { + +class MSOWordCommandConvertor : public MSOCommandConvertor +{ + IdToString msoToOOcmd; + IdToString tcidToOOcmd; + +public: + MSOWordCommandConvertor(); + virtual OUString MSOCommandToOOCommand( sal_Int16 msoCmd ) override; + virtual OUString MSOTCIDToOOCommand( sal_Int16 key ) override; +}; + +} + +MSOWordCommandConvertor::MSOWordCommandConvertor() +{ + // mso command id to ooo command string + // #FIXME and *HUNDREDS* of id's to added here + msoToOOcmd[ 0x20b ] = ".uno:CloseDoc"; + msoToOOcmd[ 0x50 ] = ".uno:Open"; + + // mso tcid to ooo command string + // #FIXME and *HUNDREDS* of id's to added here + tcidToOOcmd[ 0x9d9 ] = ".uno:Print"; +} + +OUString MSOWordCommandConvertor::MSOCommandToOOCommand( sal_Int16 key ) +{ + IdToString::iterator it = msoToOOcmd.find( key ); + if ( it != msoToOOcmd.end() ) + return it->second; + return OUString(); +} + +OUString MSOWordCommandConvertor::MSOTCIDToOOCommand( sal_Int16 key ) +{ + IdToString::iterator it = tcidToOOcmd.find( key ); + if ( it != tcidToOOcmd.end() ) + return it->second; + return OUString(); +} + +SwCTBWrapper::SwCTBWrapper() : +reserved2(0) +,reserved3(0) +,reserved4(0) +,reserved5(0) +,cbTBD(0) +,cCust(0) +,cbDTBC(0) +,rtbdc(0) +{ +} + +SwCTBWrapper::~SwCTBWrapper() +{ +} + +Customization* SwCTBWrapper::GetCustomizaton( sal_Int16 index ) +{ + if ( index < 0 || index >= static_cast<sal_Int16>( rCustomizations.size() ) ) + return nullptr; + return &rCustomizations[ index ]; +} + +SwCTB* SwCTBWrapper::GetCustomizationData( const OUString& sTBName ) +{ + auto it = std::find_if(rCustomizations.begin(), rCustomizations.end(), + [&sTBName](Customization& rCustomization) { + SwCTB* pCTB = rCustomization.GetCustomizationData(); + return pCTB && pCTB->GetName() == sTBName; + }); + if (it != rCustomizations.end()) + return it->GetCustomizationData(); + return nullptr; +} + +bool SwCTBWrapper::Read( SvStream& rS ) +{ + SAL_INFO("sw.ww8","SwCTBWrapper::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + rS.ReadUInt16( reserved2 ).ReadUChar( reserved3 ).ReadUInt16( reserved4 ).ReadUInt16( reserved5 ); + rS.ReadInt16( cbTBD ).ReadUInt16( cCust ).ReadInt32( cbDTBC ); + long nExpectedPos = rS.Tell() + cbDTBC; + if ( cbDTBC ) + { + // cbDTBC is the size in bytes of the SwTBC array + // but the size of a SwTBC element is dynamic ( and this relates to TBDelta's + int nStart = rS.Tell(); + + int bytesToRead; + // cbDTBC specifies the size ( in bytes ) taken by an array ( of unspecified size ) + // of SwTBC records ( SwTBC records have dynamic length, so we need to check our position + // after each read ) + do + { + SwTBC aTBC; + if ( !aTBC.Read( rS ) ) + return false; + rtbdc.push_back( aTBC ); + bytesToRead = cbDTBC - ( rS.Tell() - nStart ); + } while ( bytesToRead > 0 ); + } + if ( static_cast< long >( rS.Tell() ) != nExpectedPos ) + { + // Strange error condition, shouldn't happen ( but does in at least + // one test document ) In the case where it happens the SwTBC & + // TBCHeader records seem blank??? ( and incorrect ) + SAL_WARN_IF( static_cast< long >(rS.Tell()) != nExpectedPos, "sw.ww8","### Error: Expected pos not equal to actual pos after reading rtbdc"); + SAL_INFO("sw.ww8","\tPos now is 0x" << std::hex << rS.Tell() << " should be 0x" << std::hex << nExpectedPos ); + // seek to correct position after rtbdc + rS.Seek( nExpectedPos ); + } + if (cCust) + { + //Each customization takes a min of 8 bytes + size_t nMaxPossibleRecords = rS.remainingSize() / 8; + if (cCust > nMaxPossibleRecords) + { + return false; + } + for (sal_uInt16 index = 0; index < cCust; ++index) + { + Customization aCust( this ); + if ( !aCust.Read( rS ) ) + return false; + rCustomizations.push_back( aCust ); + } + } + for ( const auto& rIndex : dropDownMenuIndices ) + { + if (rIndex < 0 || o3tl::make_unsigned(rIndex) >= rCustomizations.size()) + continue; + rCustomizations[rIndex].bIsDroppedMenuTB = true; + } + return rS.good(); +} + +SwTBC* SwCTBWrapper::GetTBCAtOffset( sal_uInt32 nStreamOffset ) +{ + auto it = std::find_if(rtbdc.begin(), rtbdc.end(), + [&nStreamOffset](SwTBC& rItem) { return rItem.GetOffset() == nStreamOffset; }); + if ( it != rtbdc.end() ) + return &(*it); + return nullptr; +} + +bool SwCTBWrapper::ImportCustomToolBar( SfxObjectShell& rDocSh ) +{ + for ( auto& rCustomization : rCustomizations ) + { + try + { + css::uno::Reference<css::ui::XUIConfigurationManager> xCfgMgr; + if (!utl::ConfigManager::IsFuzzing()) + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xAppCfgSupp( ui::theModuleUIConfigurationManagerSupplier::get(xContext) ); + xCfgMgr = xAppCfgSupp->getUIConfigurationManager("com.sun.star.text.TextDocument"); + } + CustomToolBarImportHelper helper(rDocSh, xCfgMgr); + helper.setMSOCommandMap( new MSOWordCommandConvertor() ); + + if ( !rCustomization.ImportCustomToolBar( *this, helper ) ) + return false; + } + catch (...) + { + continue; + } + } + return true; +} + +Customization::Customization( SwCTBWrapper* wrapper ) + : tbidForTBD( 0 ) + , reserved1( 0 ) + , ctbds( 0 ) + , pWrapper( wrapper ) + , bIsDroppedMenuTB( false ) +{ +} + +bool Customization::Read( SvStream &rS) +{ + SAL_INFO("sw.ww8","Customization::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadInt32( tbidForTBD ).ReadUInt16( reserved1 ).ReadUInt16( ctbds ); + if ( tbidForTBD ) + { + //each TBDelta is at least 18 bytes in size + size_t nMaxAvailableRecords = rS.remainingSize() / 18; + if (ctbds > nMaxAvailableRecords) + return false; + for (sal_uInt16 index = 0; index < ctbds; ++index) + { + TBDelta aTBDelta; + if (!aTBDelta.Read( rS ) ) + return false; + customizationDataTBDelta.push_back( aTBDelta ); + // Only set the drop down for menus associated with standard toolbar + if ( aTBDelta.ControlDropsToolBar() && tbidForTBD == 0x25 ) + pWrapper->InsertDropIndex( aTBDelta.CustomizationIndex() ); + } + } + else + { + customizationDataCTB = std::make_shared<SwCTB>(); + if ( !customizationDataCTB->Read( rS ) ) + return false; + } + return rS.good(); +} + +bool Customization::ImportMenu( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper ) +{ + if ( tbidForTBD == 0x25 ) // we can handle in a limited way additions the built-in menu bar + { + for ( auto& rTBDelta : customizationDataTBDelta ) + { + // for each new menu ( control that drops a toolbar ) + // import a toolbar + if ( rTBDelta.ControlIsInserted() && rTBDelta.ControlDropsToolBar() ) + { + Customization* pCust = pWrapper->GetCustomizaton( rTBDelta.CustomizationIndex() ); + if ( pCust ) + { + // currently only support built-in menu + const OUString sMenuBar( "private:resource/menubar/menubar" ); + + // Get menu name + SwTBC* pTBC = pWrapper->GetTBCAtOffset( rTBDelta.TBCStreamOffset() ); + if ( !pTBC ) + return false; + const OUString sMenuName = pTBC->GetCustomText().replace('&','~'); + + // see if the document has already setting for the menubar + + uno::Reference< container::XIndexContainer > xIndexContainer; + bool bHasSettings = false; + if ( helper.getCfgManager()->hasSettings( sMenuBar ) ) + { + xIndexContainer.set( helper.getCfgManager()->getSettings( sMenuBar, true ), uno::UNO_QUERY_THROW ); + bHasSettings = true; + } + else + { + if ( helper.getAppCfgManager()->hasSettings( sMenuBar ) ) + xIndexContainer.set( helper.getAppCfgManager()->getSettings( sMenuBar, true ), uno::UNO_QUERY_THROW ); + else + xIndexContainer.set( helper.getAppCfgManager()->createSettings(), uno::UNO_SET_THROW ); + } + + uno::Reference< lang::XSingleComponentFactory > xSCF( xIndexContainer, uno::UNO_QUERY_THROW ); + uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + // create the popup menu + uno::Sequence< beans::PropertyValue > aPopupMenu( 4 ); + aPopupMenu[0].Name = "CommandURL"; + aPopupMenu[0].Value <<= "vnd.openoffice.org:" + sMenuName; + aPopupMenu[1].Name = "Label"; + aPopupMenu[1].Value <<= sMenuName; + aPopupMenu[2].Name = "Type"; + aPopupMenu[2].Value <<= sal_Int32( 0 ); + aPopupMenu[3].Name = "ItemDescriptorContainer"; + uno::Reference< container::XIndexContainer > xMenuContainer( xSCF->createInstanceWithContext( xContext ), uno::UNO_QUERY_THROW ); + aPopupMenu[3].Value <<= xMenuContainer; + if ( pCust->customizationDataCTB && !pCust->customizationDataCTB->ImportMenuTB( rWrapper, xMenuContainer, helper ) ) + return false; + SAL_INFO("sw.ww8","** there are " << xIndexContainer->getCount() << " menu items on the bar, inserting after that"); + xIndexContainer->insertByIndex( xIndexContainer->getCount(), uno::makeAny( aPopupMenu ) ); + + if ( bHasSettings ) + helper.getCfgManager()->replaceSettings( sMenuBar, uno::Reference< container::XIndexAccess >( xIndexContainer, uno::UNO_QUERY_THROW ) ); + else + helper.getCfgManager()->insertSettings( sMenuBar, uno::Reference< container::XIndexAccess >( xIndexContainer, uno::UNO_QUERY_THROW ) ); + + uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager(), uno::UNO_QUERY_THROW ); + xPersistence->store(); + } + } + } + return true; + } + return true; +} + +bool Customization::ImportCustomToolBar( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper ) +{ + if ( tbidForTBD == 0x25 ) + return ImportMenu( rWrapper, helper ); + if ( !customizationDataCTB ) + return false; + if ( !customizationDataCTB->IsMenuToolbar() ) + { + if ( !customizationDataCTB->ImportCustomToolBar( rWrapper, helper ) ) + return false; + } + return true; +} + +TBDelta::TBDelta() + : doprfatendFlags(0) + , ibts(0) + , cidNext(0) + , cid(0) + , fc(0) + , CiTBDE(0) + , cbTBC(0) +{ +} + +bool TBDelta::ControlIsInserted() +{ + return ( ( doprfatendFlags & 0x3 ) == 0x1 ); +} + +bool TBDelta::ControlDropsToolBar() +{ + return !( CiTBDE & 0x8000 ); +} + + +sal_Int16 TBDelta::CustomizationIndex() +{ + sal_Int16 nIndex = CiTBDE; + nIndex = nIndex >> 1; + nIndex &= 0x1ff; // only 13 bits are relevant + return nIndex; +} + +bool TBDelta::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","TBDelta::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadUChar( doprfatendFlags ).ReadUChar( ibts ).ReadInt32( cidNext ).ReadInt32( cid ).ReadInt32( fc ) ; + rS.ReadUInt16( CiTBDE ).ReadUInt16( cbTBC ); + return rS.good(); +} + +SwCTB::SwCTB() : cbTBData( 0 ) +,iWCTBl( 0 ) +,reserved( 0 ) +,unused( 0 ) +,cCtls( 0 ) +{ +} + +SwCTB::~SwCTB() +{ +} + +bool SwCTB::IsMenuToolbar() const +{ + return tb.IsMenuToolbar(); +} + +bool SwCTB::Read( SvStream &rS) +{ + SAL_INFO("sw.ww8","SwCTB::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + if ( !name.Read( rS ) ) + return false; + rS.ReadInt32( cbTBData ); + if ( !tb.Read( rS ) ) + return false; + for ( short index = 0; index < nVisualData; ++index ) + { + TBVisualData aVisData; + aVisData.Read( rS ); + rVisualData.push_back( aVisData ); + } + + rS.ReadInt32( iWCTBl ).ReadUInt16( reserved ).ReadUInt16( unused ).ReadInt32( cCtls ); + + if ( cCtls ) + { + for ( sal_Int32 index = 0; index < cCtls; ++index ) + { + SwTBC aTBC; + if ( !aTBC.Read( rS ) ) + return false; + rTBC.push_back( aTBC ); + } + } + return rS.good(); +} + +bool SwCTB::ImportCustomToolBar( SwCTBWrapper& rWrapper, CustomToolBarImportHelper& helper ) +{ + bool bRes = false; + try + { + if ( !tb.IsEnabled() ) + return true; // didn't fail, just ignoring + // Create default setting + uno::Reference< container::XIndexContainer > xIndexContainer( helper.getCfgManager()->createSettings(), uno::UNO_SET_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xIndexContainer, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xProps( xIndexContainer, uno::UNO_QUERY_THROW ); + + // set UI name for toolbar + xProps->setPropertyValue( "UIName", uno::makeAny( name.getString() ) ); + + const OUString sToolBarName = "private:resource/toolbar/custom_" + name.getString(); + for ( auto& rItem : rTBC ) + { + // createToolBar item for control + if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, helper, IsMenuToolbar() ) ) + return false; + } + + SAL_INFO("sw.ww8","Name of toolbar :-/ " << sToolBarName ); + + helper.getCfgManager()->insertSettings( sToolBarName, xIndexAccess ); + helper.applyIcons(); +#if 1 // don't think this is necessary + uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW ); + xPersistence->store(); + + xPersistence.set( helper.getCfgManager(), uno::UNO_QUERY_THROW ); + xPersistence->store(); +#endif + bRes = true; + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("sw.ww8","***** For some reason we have an" ); + bRes = false; + } + return bRes; +} + +bool SwCTB::ImportMenuTB( SwCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& xIndexContainer, CustomToolBarImportHelper& rHelper ) +{ + for ( auto& rItem : rTBC ) + { + // createToolBar item for control + if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, rHelper, true ) ) + return false; + } + return true; +} + +SwTBC::SwTBC() +{ +} + +bool SwTBC::Read( SvStream &rS ) +{ + SAL_INFO("sw.ww8","SwTBC::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + if ( !tbch.Read( rS ) ) + return false; + if ( tbch.getTcID() != 0x1 && tbch.getTcID() != 0x1051 ) + { + cid = std::make_shared<sal_uInt32>(); + rS.ReadUInt32( *cid ); + } + // MUST exist if tbch.tct is not equal to 0x16 + if ( tbch.getTct() != 0x16 ) + { + tbcd = std::make_shared<TBCData>( tbch ); + if ( !tbcd->Read( rS ) ) + return false; + } + return rS.good(); +} + +bool +SwTBC::ImportToolBarControl( SwCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuBar ) +{ + // cmtFci 0x1 Command based on a built-in command. See CidFci. + // cmtMacro 0x2 Macro command. See CidMacro. + // cmtAllocated 0x3 Allocated command. See CidAllocated. + // cmtNil 0x7 No command. See Cid. + bool bBuiltin = false; + sal_Int16 cmdId = 0; + if ( cid ) + { + const sal_uInt32 nCid = ( *cid & 0xFFFF ); + + const sal_uInt8 cmt = static_cast<sal_uInt8>( nCid & 0x7 ); + const sal_Int16 arg2 = static_cast<sal_Int16>( nCid >> 3 ); + + switch ( cmt ) + { + case 1: + SAL_INFO("sw.ww8","cmt is cmtFci builtin command 0x" << std::hex << arg2); + bBuiltin = true; + cmdId = arg2; + break; + case 2: + SAL_INFO("sw.ww8","cmt is cmtMacro macro 0x" << std::hex << arg2); + break; + case 3: + SAL_INFO("sw.ww8","cmt is cmtAllocated [???] 0x" << std::hex << arg2); + break; + case 7: + SAL_INFO("sw.ww8","cmt is cmNill no-thing 0x" << std::hex << arg2); + break; + default: + SAL_INFO("sw.ww8","illegal 0x" << std::hex << cmt); + break; + } + } + + if ( tbcd ) + { + std::vector< css::beans::PropertyValue > props; + if ( bBuiltin ) + { + const OUString sCommand = helper.MSOCommandToOOCommand( cmdId ); + if ( !sCommand.isEmpty() ) + { + beans::PropertyValue aProp; + + aProp.Name = "CommandURL"; + aProp.Value <<= sCommand; + props.push_back( aProp ); + } + } + bool bBeginGroup = false; + tbcd->ImportToolBarControl( helper, props, bBeginGroup, bIsMenuBar ); + + TBCMenuSpecific* pMenu = tbcd->getMenuSpecific(); + if ( pMenu ) + { + SAL_INFO("sw.ww8","** control has a menu, name of toolbar with menu items is " << pMenu->Name() ); + // search for SwCTB with the appropriate name ( it contains the + // menu items, although we cannot import ( or create ) a menu on + // a custom toolbar we can import the menu items in a separate + // toolbar ( better than nothing ) + SwCTB* pCustTB = rWrapper.GetCustomizationData( pMenu->Name() ); + if ( pCustTB ) + { + uno::Reference< container::XIndexContainer > xMenuDesc = document::IndexedPropertyValues::create( comphelper::getProcessComponentContext() ); + if ( !pCustTB->ImportMenuTB( rWrapper,xMenuDesc, helper ) ) + return false; + if ( !bIsMenuBar ) + { + if ( !helper.createMenu( pMenu->Name(), xMenuDesc ) ) + return false; + } + else + { + beans::PropertyValue aProp; + aProp.Name = "ItemDescriptorContainer"; + aProp.Value <<= xMenuDesc; + props.push_back( aProp ); + } + } + } + + if ( bBeginGroup ) + { + // insert spacer + uno::Sequence< beans::PropertyValue > sProps( 1 ); + sProps[ 0 ].Name = "Type"; + sProps[ 0 ].Value <<= ui::ItemType::SEPARATOR_LINE; + toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::makeAny( sProps ) ); + } + + toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::makeAny( comphelper::containerToSequence(props) ) ); + } + return true; +} + +OUString +SwTBC::GetCustomText() +{ + if ( tbcd ) + return tbcd->getGeneralInfo().CustomText(); + return OUString(); +} + +bool +Xst::Read( SvStream& rS ) +{ + SAL_INFO("sw.ww8","Xst::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + sString = read_uInt16_PascalString(rS); + return rS.good(); +} + +Tcg::Tcg() : nTcgVer( -1 ) +{ +} + +bool Tcg::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Tcg::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadSChar( nTcgVer ); + if ( nTcgVer != -1 ) + return false; + tcg.reset( new Tcg255() ); + return tcg->Read( rS ); +} + +bool Tcg::ImportCustomToolBar( SfxObjectShell& rDocSh ) +{ + if (tcg) + return tcg->ImportCustomToolBar( rDocSh ); + return false; +} + +Tcg255::Tcg255() +{ +} + +Tcg255::~Tcg255() +{ +} + +bool Tcg255::processSubStruct( sal_uInt8 nId, SvStream &rS ) +{ + std::unique_ptr<Tcg255SubStruct> xSubStruct; + switch ( nId ) + { + case 0x1: + { + xSubStruct.reset(new PlfMcd); + break; + } + case 0x2: + { + xSubStruct.reset(new PlfAcd); + break; + } + case 0x3: + case 0x4: + { + xSubStruct.reset(new PlfKme); + break; + } + case 0x10: + { + xSubStruct.reset(new TcgSttbf); + break; + } + case 0x11: + { + xSubStruct.reset(new MacroNames); + break; + } + case 0x12: + { + xSubStruct.reset(new SwCTBWrapper); + break; + } + default: + SAL_INFO("sw.ww8","Unknown id 0x" << std::hex << nId); + return false; + } + xSubStruct->ch = nId; + if (!xSubStruct->Read(rS)) + return false; + rgtcgData.push_back(std::move(xSubStruct)); + return true; +} + +bool Tcg255::ImportCustomToolBar( SfxObjectShell& rDocSh ) +{ + // Find the SwCTBWrapper + for ( const auto & rSubStruct : rgtcgData ) + { + if ( rSubStruct->id() == 0x12 ) + { + // not so great, shouldn't really have to do a horror casting + SwCTBWrapper* pCTBWrapper = dynamic_cast< SwCTBWrapper* > ( rSubStruct.get() ); + if ( pCTBWrapper ) + { + // tdf#127048 set this flag if we might import something + uno::Reference<frame::XModel> const xModel(rDocSh.GetBaseModel()); + comphelper::DocumentInfo::notifyMacroEventRead(xModel); + + if ( !pCTBWrapper->ImportCustomToolBar( rDocSh ) ) + return false; + } + } + } + return true; +} + +bool Tcg255::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Tcg255::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + sal_uInt8 nId = 0x40; + rS.ReadUChar( nId ); + while ( nId != 0x40 ) + { + if ( !processSubStruct( nId, rS ) ) + return false; + nId = 0x40; + rS.ReadUChar( nId ); + } + return rS.good(); + // Peek at +} + +Tcg255SubStruct::Tcg255SubStruct( ) : ch(0) +{ +} + +bool Tcg255SubStruct::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Tcg255SubStruct::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + return rS.good(); +} + +PlfMcd::PlfMcd() + : iMac(0) +{ +} + +bool PlfMcd::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","PffMcd::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + rS.ReadInt32( iMac ); + if (iMac < 0) + return false; + auto nMaxPossibleRecords = rS.remainingSize() / 24 /*sizeof MCD*/; + if (o3tl::make_unsigned(iMac) > nMaxPossibleRecords) + { + SAL_WARN("sw.ww8", iMac << " records claimed, but max possible is " << nMaxPossibleRecords); + iMac = nMaxPossibleRecords; + } + if (iMac) + { + rgmcd.resize(iMac); + for ( sal_Int32 index = 0; index < iMac; ++index ) + { + if ( !rgmcd[ index ].Read( rS ) ) + return false; + } + } + return rS.good(); +} + +PlfAcd::PlfAcd() : + iMac(0) +{ +} + +PlfAcd::~PlfAcd() +{ +} + +bool PlfAcd::Read( SvStream &rS) +{ + SAL_INFO("sw.ww8","PffAcd::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + rS.ReadInt32( iMac ); + if (iMac < 0) + return false; + auto nMaxPossibleRecords = rS.remainingSize() / (sizeof(sal_uInt16)*2); + if (o3tl::make_unsigned(iMac) > nMaxPossibleRecords) + { + SAL_WARN("sw.ww8", iMac << " records claimed, but max possible is " << nMaxPossibleRecords); + iMac = nMaxPossibleRecords; + } + if (iMac) + { + rgacd.reset( new Acd[ iMac ] ); + for ( sal_Int32 index = 0; index < iMac; ++index ) + { + if ( !rgacd[ index ].Read( rS ) ) + return false; + } + } + return rS.good(); +} + +PlfKme::PlfKme() : + iMac( 0 ) +{ +} + +PlfKme::~PlfKme() +{ +} + +bool PlfKme::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","PlfKme::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + rS.ReadInt32( iMac ); + if (iMac > 0) + { + //each Kme is 14 bytes in size + size_t nMaxAvailableRecords = rS.remainingSize() / 14; + if (o3tl::make_unsigned(iMac) > nMaxAvailableRecords) + return false; + + rgkme.reset( new Kme[ iMac ] ); + for( sal_Int32 index=0; index<iMac; ++index ) + { + if ( !rgkme[ index ].Read( rS ) ) + return false; + } + } + return rS.good(); +} + +TcgSttbf::TcgSttbf() +{ +} + +bool TcgSttbf::Read( SvStream &rS) +{ + SAL_INFO("sw.ww8","TcgSttbf::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + return sttbf.Read( rS ); +} + +TcgSttbfCore::TcgSttbfCore() : fExtend( 0 ) +,cData( 0 ) +,cbExtra( 0 ) +{ +} + +TcgSttbfCore::~TcgSttbfCore() +{ +} + +bool TcgSttbfCore::Read( SvStream& rS ) +{ + SAL_INFO("sw.ww8","TcgSttbfCore::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadUInt16( fExtend ).ReadUInt16( cData ).ReadUInt16( cbExtra ); + if ( cData ) + { + if (cData > rS.remainingSize() / 4) //definitely an invalid record + return false; + dataItems.reset( new SBBItem[ cData ] ); + for ( sal_Int32 index = 0; index < cData; ++index ) + { + rS.ReadUInt16( dataItems[ index ].cchData ); + dataItems[ index ].data = read_uInt16s_ToOUString(rS, dataItems[index].cchData); + rS.ReadUInt16( dataItems[ index ].extraData ); + } + } + return rS.good(); +} + +MacroNames::MacroNames() : + iMac( 0 ) +{ +} + +MacroNames::~MacroNames() +{ +} + +bool MacroNames::Read( SvStream &rS) +{ + SAL_INFO("sw.ww8","MacroNames::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + Tcg255SubStruct::Read( rS ); + rS.ReadUInt16( iMac ); + if ( iMac ) + { + //even an empty MacroName will take 2 bytes + size_t nMaxAvailableRecords = rS.remainingSize()/sizeof(sal_uInt16); + if (iMac > nMaxAvailableRecords) + return false; + rgNames.reset( new MacroName[ iMac ] ); + for ( sal_Int32 index = 0; index < iMac; ++index ) + { + if ( !rgNames[ index ].Read( rS ) ) + return false; + } + } + return rS.good(); +} + +MacroName::MacroName():ibst(0) +{ +} + +bool MacroName::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","MacroName::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadUInt16( ibst ); + return xstz.Read( rS ); +} + +Xstz::Xstz():chTerm(0) +{ +} + +bool +Xstz::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Xstz::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + if ( !xst.Read( rS ) ) + return false; + rS.ReadUInt16( chTerm ); + if ( chTerm != 0 ) // should be an assert + return false; + return rS.good(); +} + +Kme::Kme() : reserved1(0) +,reserved2(0) +,kcm1(0) +,kcm2(0) +,kt(0) +,param(0) +{ +} + +Kme::~Kme() +{ +} + +bool +Kme::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Kme::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadInt16( reserved1 ).ReadInt16( reserved2 ).ReadUInt16( kcm1 ).ReadUInt16( kcm2 ).ReadUInt16( kt ).ReadUInt32( param ); + return rS.good(); +} + +Acd::Acd() : ibst( 0 ) +, fciBasedOnABC( 0 ) +{ +} + +bool Acd::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","Acd::Read() stream pos 0x" << std::hex << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadInt16( ibst ).ReadUInt16( fciBasedOnABC ); + return rS.good(); +} + +MCD::MCD() : reserved1(0x56) +,reserved2( 0 ) +,ibst( 0 ) +,ibstName( 0 ) +,reserved3( 0xFFFF ) +,reserved4( 0 ) +,reserved5( 0 ) +,reserved6( 0 ) +,reserved7( 0 ) +{ +} + +bool MCD::Read(SvStream &rS) +{ + SAL_INFO("sw.ww8","MCD::Read() stream pos 0x" << rS.Tell() ); + nOffSet = rS.Tell(); + rS.ReadSChar( reserved1 ).ReadUChar( reserved2 ).ReadUInt16( ibst ).ReadUInt16( ibstName ).ReadUInt16( reserved3 ); + rS.ReadUInt32( reserved4 ).ReadUInt32( reserved5 ).ReadUInt32( reserved6 ).ReadUInt32( reserved7 ); + return rS.good(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/ww8toolbar.hxx b/sw/source/filter/ww8/ww8toolbar.hxx new file mode 100644 index 000000000..346c39535 --- /dev/null +++ b/sw/source/filter/ww8/ww8toolbar.hxx @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_FILTER_WW8_WW8TOOLBAR_HXX +#define INCLUDED_SW_SOURCE_FILTER_WW8_WW8TOOLBAR_HXX + +#include <com/sun/star/container/XIndexContainer.hpp> +#include <filter/msfilter/mstoolbar.hxx> +#include <memory> + + +class SfxObjectShell; +class SwCTBWrapper; + +class Xst : public TBBase +{ + OUString sString; + +public: + Xst(){} + bool Read(SvStream &rS) override; + const OUString& getString() const { return sString; } +}; + +class SwTBC : public TBBase +{ + TBCHeader tbch; + std::shared_ptr< sal_uInt32 > cid; // optional + std::shared_ptr<TBCData> tbcd; + +public: + SwTBC(); + bool Read(SvStream &rS) override; + bool ImportToolBarControl( SwCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper&, bool ); + OUString GetCustomText(); +}; + +class SwCTB : public TBBase +{ + Xst name; + sal_Int32 cbTBData; + TB tb; + std::vector<TBVisualData> rVisualData; + sal_Int32 iWCTBl; + sal_uInt16 reserved; + sal_uInt16 unused; + sal_Int32 cCtls; + std::vector< SwTBC > rTBC; + + SwCTB(const SwCTB&) = delete; + SwCTB& operator = ( const SwCTB&) = delete; + +public: + SwCTB(); + virtual ~SwCTB() override; + bool Read(SvStream &rS) override; + bool IsMenuToolbar() const; + bool ImportCustomToolBar( SwCTBWrapper&, CustomToolBarImportHelper& ); + bool ImportMenuTB( SwCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper& ); + OUString const & GetName() { return tb.getName().getString(); } +}; + +class TBDelta : public TBBase +{ + sal_uInt8 doprfatendFlags; + + sal_uInt8 ibts; + sal_Int32 cidNext; + sal_Int32 cid; + sal_Int32 fc; + sal_uInt16 CiTBDE; // careful of this ( endian matters etc. ) + sal_uInt16 cbTBC; + +public: + TBDelta(); + bool Read(SvStream &rS) override; + bool ControlIsInserted(); + bool ControlDropsToolBar(); + sal_Int32 TBCStreamOffset() { return fc;} + sal_Int16 CustomizationIndex(); +}; + +class Tcg255SubStruct : public TBBase +{ + friend class Tcg255; + + Tcg255SubStruct(const Tcg255SubStruct&) = delete; + Tcg255SubStruct& operator = ( const Tcg255SubStruct&) = delete; + +protected: + sal_uInt8 ch; + +public: + explicit Tcg255SubStruct(); + sal_uInt8 id() const { return ch; } + bool Read(SvStream &rS) override; +}; + +class Customization : public TBBase +{ + friend class SwCTBWrapper; + + sal_Int32 tbidForTBD; + sal_uInt16 reserved1; + sal_uInt16 ctbds; + SwCTBWrapper* pWrapper; + std::shared_ptr< SwCTB > customizationDataCTB; + std::vector< TBDelta > customizationDataTBDelta; + bool bIsDroppedMenuTB; + +public: + explicit Customization( SwCTBWrapper* rapper ); + bool Read(SvStream &rS) override; + bool ImportCustomToolBar( SwCTBWrapper&, CustomToolBarImportHelper& ); + bool ImportMenu( SwCTBWrapper&, CustomToolBarImportHelper& ); + SwCTB* GetCustomizationData() { return customizationDataCTB.get(); }; +}; + +class SwCTBWrapper : public Tcg255SubStruct +{ + // reserved1 is the ch field of Tcg255SubStruct + sal_uInt16 reserved2; + sal_uInt8 reserved3; + sal_uInt16 reserved4; + sal_uInt16 reserved5; + + sal_Int16 cbTBD; + sal_uInt16 cCust; + + sal_Int32 cbDTBC; + + std::vector< SwTBC > rtbdc; + std::vector< Customization > rCustomizations; // array of Customizations + std::vector< sal_Int16 > dropDownMenuIndices; // array of indexes of Customization toolbars that are dropped by a menu + SwCTBWrapper(const SwCTBWrapper&) = delete; + SwCTBWrapper& operator = ( const SwCTBWrapper&) = delete; + +public: + explicit SwCTBWrapper(); + virtual ~SwCTBWrapper() override; + void InsertDropIndex( sal_Int32 aIndex ) { dropDownMenuIndices.push_back( aIndex ); } + SwTBC* GetTBCAtOffset( sal_uInt32 nStreamOffset ); + bool Read(SvStream &rS) override; + bool ImportCustomToolBar( SfxObjectShell& rDocSh ); + + Customization* GetCustomizaton( sal_Int16 index ); + SwCTB* GetCustomizationData( const OUString& name ); +}; + +class MCD : public TBBase +{ + sal_Int8 reserved1; // A signed integer that MUST be 0x56. + sal_uInt8 reserved2; // MUST be 0. + sal_uInt16 ibst; // Unsigned integer that specifies the name of the macro. Macro name is specified by MacroName.xstz of the MacroName entry in the MacroNames such that MacroName.ibst equals ibst. MacroNames MUST contain such an entry. + sal_uInt16 ibstName; // An unsigned integer that specifies the index into the Command String Table (TcgSttbf.sttbf) where the macro's name and arguments are specified. + sal_uInt16 reserved3; // An unsigned integer that MUST be 0xFFFF. + sal_uInt32 reserved4; //MUST be ignored. + sal_uInt32 reserved5; //MUST be 0. + sal_uInt32 reserved6; //MUST be ignored. + sal_uInt32 reserved7; //MUST be ignored + +public: + MCD(); + bool Read(SvStream &rS) override; +}; + +class PlfMcd : public Tcg255SubStruct +{ + sal_Int32 iMac; + std::vector<MCD> rgmcd; // array of MCD's + PlfMcd(const PlfMcd&) = delete; + PlfMcd& operator = ( const PlfMcd&) = delete; + +public: + explicit PlfMcd(); + bool Read(SvStream &rS) override; +}; + +class Acd : public TBBase +{ + sal_Int16 ibst; + sal_uInt16 fciBasedOnABC; // fciBasedOn(13 bits) A(1bit)B(1bit)C(1Bit) + Acd(const Acd&) = delete; + Acd& operator = ( const Acd&) = delete; + +public: + Acd(); + bool Read(SvStream &rS) override; +}; + +class PlfAcd: public Tcg255SubStruct +{ + sal_Int32 iMac; + std::unique_ptr<Acd[]> rgacd; + PlfAcd(const PlfAcd&) = delete; + PlfAcd& operator = ( const PlfAcd&) = delete; + +public: + explicit PlfAcd(); + virtual ~PlfAcd() override; + bool Read(SvStream &rS) override; +}; + +class Kme : public TBBase +{ + sal_Int16 reserved1; //MUST be zero. + sal_Int16 reserved2; //MUST be zero. + sal_uInt16 kcm1; //A Kcm that specifies the primary shortcut key. + sal_uInt16 kcm2; //A Kcm that specifies the secondary shortcut key, or 0x00FF if there is no secondary shortcut key. + sal_uInt16 kt; //A Kt that specifies the type of action to be taken when the key combination is pressed. + sal_uInt32 param; //The meaning of this field depends on the value of kt + + Kme(const Kme&) = delete; + Kme& operator = ( const Kme&) = delete; + +public: + Kme(); + virtual ~Kme() override; + bool Read(SvStream &rS) override; +}; + +class PlfKme : public Tcg255SubStruct +{ + sal_Int32 iMac; + std::unique_ptr<Kme[]> rgkme; + PlfKme(const PlfKme&) = delete; + PlfKme& operator = ( const PlfKme&) = delete; + +public: + explicit PlfKme(); + virtual ~PlfKme() override; + bool Read(SvStream &rS) override; +}; + +class TcgSttbfCore : public TBBase +{ + struct SBBItem + { + sal_uInt16 cchData; + OUString data; + sal_uInt16 extraData; + SBBItem() : cchData(0), extraData(0){} + }; + + sal_uInt16 fExtend; + sal_uInt16 cData; + sal_uInt16 cbExtra; + std::unique_ptr<SBBItem[]> dataItems; + TcgSttbfCore(const TcgSttbfCore&) = delete; + TcgSttbfCore& operator = ( const TcgSttbfCore&) = delete; + +public: + TcgSttbfCore(); + virtual ~TcgSttbfCore() override; + bool Read(SvStream &rS) override; +}; + +class TcgSttbf : public Tcg255SubStruct +{ + TcgSttbfCore sttbf; + TcgSttbf(const TcgSttbf&) = delete; + TcgSttbf& operator = ( const TcgSttbf&) = delete; + +public: + explicit TcgSttbf(); + bool Read(SvStream &rS) override; +}; + +class Xstz : public TBBase +{ + Xst xst; //An Xst specifying the string with its pre-pended length. + sal_uInt16 chTerm; + + Xstz(const Xstz&) = delete; + Xstz& operator = ( const Xstz&) = delete; + +public: + Xstz(); + bool Read(SvStream &rS) override; +}; + +class MacroName : public TBBase +{ + sal_uInt16 ibst; //An unsigned integer that specifies the index of the current entry in the macro name table. MUST NOT be the same as the index of any other entry. + Xstz xstz; + MacroName(const MacroName&) = delete; + MacroName& operator = ( const MacroName&) = delete; + +public: + MacroName(); + bool Read(SvStream &rS) override; +}; + +class MacroNames : public Tcg255SubStruct +{ + sal_uInt16 iMac; //An unsigned integer that specifies the number of MacroName structures in rgNames. + std::unique_ptr<MacroName[]> rgNames; + + MacroNames(const MacroNames&) = delete; + MacroNames& operator = ( const MacroNames&) = delete; + +public: + explicit MacroNames(); + virtual ~MacroNames() override; + bool Read(SvStream &rS) override; +}; + +class Tcg255 : public TBBase +{ + std::vector< std::unique_ptr<Tcg255SubStruct> > rgtcgData; // array of sub structures + Tcg255(const Tcg255&) = delete; + Tcg255& operator = ( const Tcg255&) = delete; + bool processSubStruct( sal_uInt8 nId, SvStream& ); + +public: + Tcg255(); + virtual ~Tcg255() override; + bool Read(SvStream &rS) override; + bool ImportCustomToolBar( SfxObjectShell& rDocSh ); +}; + +class Tcg: public TBBase +{ + sal_Int8 nTcgVer; + std::unique_ptr< Tcg255 > tcg; + Tcg(const Tcg&) = delete; + Tcg& operator = ( const Tcg&) = delete; + +public: + Tcg(); + bool Read(SvStream &rS) override; + bool ImportCustomToolBar( SfxObjectShell& rDocSh ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx b/sw/source/filter/xml/XMLRedlineImportHelper.cxx new file mode 100644 index 000000000..47e89d497 --- /dev/null +++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx @@ -0,0 +1,744 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cstddef> + +#include "XMLRedlineImportHelper.hxx" +#include <unotextcursor.hxx> +#include <unotextrange.hxx> +#include <unocrsr.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <tools/datetime.hxx> +#include <poolfmt.hxx> +#include <unoredline.hxx> +#include <DocumentRedlineManager.hxx> +#include "xmlimp.hxx" +#include <o3tl/any.hxx> +#include <xmloff/xmltoken.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +using ::com::sun::star::text::XTextCursor; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::text::XWordCursor; +using ::com::sun::star::lang::XUnoTunnel; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +// collision with tools/DateTime: use UNO DateTime as util::DateTime +// using util::DateTime; + +// a few helper functions +static SwDoc* lcl_GetDocViaTunnel( Reference<XTextCursor> const & rCursor ) +{ + Reference<XUnoTunnel> xTunnel( rCursor, UNO_QUERY); + OSL_ENSURE(xTunnel.is(), "missing XUnoTunnel for XTextCursor"); + OTextCursorHelper *const pXCursor = + ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xTunnel); + OSL_ENSURE( pXCursor, "OTextCursorHelper missing" ); + return pXCursor ? pXCursor->GetDoc() : nullptr; +} + +static SwDoc* lcl_GetDocViaTunnel( Reference<XTextRange> const & rRange ) +{ + Reference<XUnoTunnel> xTunnel(rRange, UNO_QUERY); + OSL_ENSURE(xTunnel.is(), "missing XUnoTunnel for XTextRange"); + SwXTextRange *const pXRange = + ::sw::UnoTunnelGetImplementation<SwXTextRange>(xTunnel); + // #i115174#: this may be a SvxUnoTextRange + // OSL_ENSURE( pXRange, "SwXTextRange missing" ); + return pXRange ? &pXRange->GetDoc() : nullptr; +} + +// XTextRangeOrNodeIndexPosition: store a position into the text +// *either* as an XTextRange or as an SwNodeIndex. The reason is that +// we must store either pointers to StartNodes (because redlines may +// start on start nodes) or to a text position, and there appears to +// be no existing type that could do both. Things are complicated by +// the matter that (e.g in section import) we delete a few characters, +// which may cause bookmarks (as used by XTextRange) to be deleted. + +namespace { + +class XTextRangeOrNodeIndexPosition +{ + Reference<XTextRange> xRange; + std::unique_ptr<SwNodeIndex> pIndex; // pIndex will point to the *previous* node + +public: + XTextRangeOrNodeIndexPosition(); + + void Set( Reference<XTextRange> const & rRange ); + void Set( SwNodeIndex const & rIndex ); + void SetAsNodeIndex( Reference<XTextRange> const & rRange ); + + void CopyPositionInto(SwPosition& rPos, SwDoc & rDoc); + SwDoc* GetDoc(); + + bool IsValid() const; +}; + +} + +XTextRangeOrNodeIndexPosition::XTextRangeOrNodeIndexPosition() +{ +} + +void XTextRangeOrNodeIndexPosition::Set( Reference<XTextRange> const & rRange ) +{ + xRange = rRange->getStart(); // set bookmark + pIndex.reset(); +} + +void XTextRangeOrNodeIndexPosition::Set( SwNodeIndex const & rIndex ) +{ + pIndex.reset( new SwNodeIndex(rIndex) ); + (*pIndex)-- ; // previous node!!! + xRange = nullptr; +} + +void XTextRangeOrNodeIndexPosition::SetAsNodeIndex( + Reference<XTextRange> const & rRange ) +{ + // XTextRange -> XTunnel -> SwXTextRange + SwDoc* pDoc = lcl_GetDocViaTunnel(rRange); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc"); + return; + } + + // SwXTextRange -> PaM + SwUnoInternalPaM aPaM(*pDoc); + bool bSuccess = ::sw::XTextRangeToSwPaM(aPaM, rRange); + OSL_ENSURE(bSuccess, "illegal range"); + + // PaM -> Index + Set(aPaM.GetPoint()->nNode); +} + +void +XTextRangeOrNodeIndexPosition::CopyPositionInto(SwPosition& rPos, SwDoc & rDoc) +{ + OSL_ENSURE(IsValid(), "Can't get Position"); + + // create PAM from start cursor (if no node index is present) + if (nullptr == pIndex) + { + SwUnoInternalPaM aUnoPaM(rDoc); + bool bSuccess = ::sw::XTextRangeToSwPaM(aUnoPaM, xRange); + OSL_ENSURE(bSuccess, "illegal range"); + + rPos = *aUnoPaM.GetPoint(); + } + else + { + rPos.nNode = *pIndex; + rPos.nNode++; // pIndex points to previous index !!! + rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), 0 ); + } +} + +SwDoc* XTextRangeOrNodeIndexPosition::GetDoc() +{ + OSL_ENSURE(IsValid(), "Can't get Doc"); + + return (nullptr != pIndex) ? pIndex->GetNodes().GetDoc() : lcl_GetDocViaTunnel(xRange); +} + +bool XTextRangeOrNodeIndexPosition::IsValid() const +{ + return ( xRange.is() || (pIndex != nullptr) ); +} + +// RedlineInfo: temporary storage for redline data +class RedlineInfo +{ +public: + RedlineInfo(); + ~RedlineInfo(); + + // redline type (insert, delete, ...) + RedlineType eType; + + // info fields: + OUString sAuthor; // change author string + OUString sComment; // change comment string + util::DateTime aDateTime; // change DateTime + bool bMergeLastParagraph; // the SwRangeRedline::IsDelLastPara flag + + // each position can may be either empty, an XTextRange, or an SwNodeIndex + + // start pos of anchor (may be empty) + XTextRangeOrNodeIndexPosition aAnchorStart; + + // end pos of anchor (may be empty) + XTextRangeOrNodeIndexPosition aAnchorEnd; + + // index of content node (maybe NULL) + SwNodeIndex* pContentIndex; + + // next redline info (for hierarchical redlines) + RedlineInfo* pNextRedline; + + // store whether we expect an adjustment for this redline + bool bNeedsAdjustment; +}; + +RedlineInfo::RedlineInfo() : + eType(RedlineType::Insert), + sAuthor(), + sComment(), + aDateTime(), + bMergeLastParagraph( false ), + aAnchorStart(), + aAnchorEnd(), + pContentIndex(nullptr), + pNextRedline(nullptr), + bNeedsAdjustment( false ) +{ +} + +RedlineInfo::~RedlineInfo() +{ + delete pContentIndex; + delete pNextRedline; +} + +static const char g_sShowChanges[] = "ShowChanges"; +static const char g_sRecordChanges[] = "RecordChanges"; +static const char g_sRedlineProtectionKey[] = "RedlineProtectionKey"; + +XMLRedlineImportHelper::XMLRedlineImportHelper( + SvXMLImport & rImport, + bool bNoRedlinesPlease, + const Reference<XPropertySet> & rModel, + const Reference<XPropertySet> & rImportInfo ) + : m_rImport(rImport) + , + m_sInsertion( GetXMLToken( XML_INSERTION )), + m_sDeletion( GetXMLToken( XML_DELETION )), + m_sFormatChange( GetXMLToken( XML_FORMAT_CHANGE )), + m_aRedlineMap(), + m_bIgnoreRedlines(bNoRedlinesPlease), + m_xModelPropertySet(rModel), + m_xImportInfoPropertySet(rImportInfo) +{ + // check to see if redline mode is handled outside of component + bool bHandleShowChanges = true; + bool bHandleRecordChanges = true; + bool bHandleProtectionKey = true; + if ( m_xImportInfoPropertySet.is() ) + { + Reference<XPropertySetInfo> xInfo = + m_xImportInfoPropertySet->getPropertySetInfo(); + + bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges ); + bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges ); + bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey ); + } + + // get redline mode + m_bShowChanges = *o3tl::doAccess<bool>( + ( bHandleShowChanges ? m_xModelPropertySet : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sShowChanges )); + m_bRecordChanges = *o3tl::doAccess<bool>( + ( bHandleRecordChanges ? m_xModelPropertySet : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sRecordChanges )); + { + Any aAny = (bHandleProtectionKey ? m_xModelPropertySet + : m_xImportInfoPropertySet ) + ->getPropertyValue( g_sRedlineProtectionKey ); + aAny >>= m_aProtectionKey; + } + + // set redline mode to "don't record changes" + if( bHandleRecordChanges ) + { + m_xModelPropertySet->setPropertyValue( g_sRecordChanges, makeAny(false) ); + } +} + +XMLRedlineImportHelper::~XMLRedlineImportHelper() +{ + // delete all left over (and obviously incomplete) RedlineInfos (and map) + for( const auto& rEntry : m_aRedlineMap ) + { + RedlineInfo* pInfo = rEntry.second; + + // left-over redlines. Insert them if possible (but assert), + // and delete the incomplete ones. Finally, delete it. + if( IsReady(pInfo) ) + { + OSL_FAIL("forgotten RedlineInfo; now inserted"); + InsertIntoDocument( pInfo ); + } + else + { + // try if only the adjustment was missing + pInfo->bNeedsAdjustment = false; + if( IsReady(pInfo) ) + { + OSL_FAIL("RedlineInfo without adjustment; now inserted"); + InsertIntoDocument( pInfo ); + } + else + { + // this situation occurs if redlines aren't closed + // (i.e. end without start, or start without + // end). This may well be a problem in the file, + // rather than the code. + OSL_FAIL("incomplete redline (maybe file was corrupt); " + "now deleted"); + } + } + delete pInfo; + } + m_aRedlineMap.clear(); + + // set redline mode, either to info property set, or directly to + // the document + bool bHandleShowChanges = true; + bool bHandleRecordChanges = true; + bool bHandleProtectionKey = true; + if ( m_xImportInfoPropertySet.is() ) + { + Reference<XPropertySetInfo> xInfo = + m_xImportInfoPropertySet->getPropertySetInfo(); + + bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges ); + bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges ); + bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey ); + } + + // set redline mode & key + try + { + Any aAny; + + aAny <<= m_bShowChanges; + if ( bHandleShowChanges ) + { + aAny <<= true; + m_xModelPropertySet->setPropertyValue( g_sShowChanges, aAny ); + // TODO maybe we need some property for the view-setting? + SwDoc *const pDoc(SwImport::GetDocFromXMLImport(m_rImport)); + assert(pDoc); + pDoc->GetDocumentRedlineManager().SetHideRedlines(!m_bShowChanges); + } + else + m_xImportInfoPropertySet->setPropertyValue( g_sShowChanges, aAny ); + + aAny <<= m_bRecordChanges; + if ( bHandleRecordChanges ) + m_xModelPropertySet->setPropertyValue( g_sRecordChanges, aAny ); + else + m_xImportInfoPropertySet->setPropertyValue( g_sRecordChanges, aAny ); + + aAny <<= m_aProtectionKey; + if ( bHandleProtectionKey ) + m_xModelPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny ); + else + m_xImportInfoPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny); + } + catch (const uno::RuntimeException &) // fdo#65882 + { + SAL_WARN( "sw", "potentially benign ordering issue during shutdown" ); + } +} + +void XMLRedlineImportHelper::Add( + const OUString& rType, + const OUString& rId, + const OUString& rAuthor, + const OUString& rComment, + const util::DateTime& rDateTime, + bool bMergeLastPara) +{ + // we need to do the following: + // 1) parse type string + // 2) create RedlineInfo and fill it with data + // 3) check for existing redline with same ID + // 3a) insert redline into map + // 3b) attach to existing redline + + // ad 1) + RedlineType eType; + if (rType == m_sInsertion) + { + eType = RedlineType::Insert; + } + else if (rType == m_sDeletion) + { + eType = RedlineType::Delete; + } + else if (rType == m_sFormatChange) + { + eType = RedlineType::Format; + } + else + { + // no proper type found: early out! + return; + } + + // ad 2) create a new RedlineInfo + RedlineInfo* pInfo = new RedlineInfo(); + + // fill entries + pInfo->eType = eType; + pInfo->sAuthor = rAuthor; + pInfo->sComment = rComment; + pInfo->aDateTime = rDateTime; + pInfo->bMergeLastParagraph = bMergeLastPara; + + // ad 3) + auto itPair = m_aRedlineMap.emplace(rId, pInfo); + if (!itPair.second) + { + // 3b) we already have a redline with this name: hierarchical redlines + // insert pInfo as last element in the chain. + // (hierarchy sanity checking happens on inserting into the document) + + // find last element + RedlineInfo* pInfoChain; + for( pInfoChain = itPair.first->second; + nullptr != pInfoChain->pNextRedline; + pInfoChain = pInfoChain->pNextRedline) ; // empty loop + + // insert as last element + pInfoChain->pNextRedline = pInfo; + } +} + +Reference<XTextCursor> XMLRedlineImportHelper::CreateRedlineTextSection( + Reference<XTextCursor> const & xOldCursor, + const OUString& rId) +{ + Reference<XTextCursor> xReturn; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // get RedlineInfo + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() != aFind) + { + // get document from old cursor (via tunnel) + SwDoc* pDoc = lcl_GetDocViaTunnel(xOldCursor); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc => cannot create section."); + return nullptr; + } + + // create text section for redline + SwTextFormatColl *pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool + (RES_POOLCOLL_STANDARD, false ); + SwStartNode* pRedlineNode = pDoc->GetNodes().MakeTextSection( + pDoc->GetNodes().GetEndOfRedlines(), + SwNormalStartNode, + pColl); + + // remember node-index in RedlineInfo + SwNodeIndex aIndex(*pRedlineNode); + aFind->second->pContentIndex = new SwNodeIndex(aIndex); + + // create XText for document + rtl::Reference<SwXRedlineText> pXText = new SwXRedlineText(pDoc, aIndex); + + // create (UNO-) cursor + SwPosition aPos(*pRedlineNode); + SwXTextCursor *const pXCursor = + new SwXTextCursor(*pDoc, pXText.get(), CursorType::Redline, aPos); + pXCursor->GetCursor().Move(fnMoveForward, GoInNode); + // cast to avoid ambiguity + xReturn = static_cast<text::XWordCursor*>(pXCursor); + } + // else: unknown redline -> Ignore + + return xReturn; +} + +void XMLRedlineImportHelper::SetCursor( + const OUString& rId, + bool bStart, + Reference<XTextRange> const & rRange, + bool bIsOutsideOfParagraph) +{ + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() != aFind) + { + // RedlineInfo found; now set Cursor + RedlineInfo* pInfo = aFind->second; + if (bIsOutsideOfParagraph) + { + // outside of paragraph: remember SwNodeIndex + if (bStart) + { + pInfo->aAnchorStart.SetAsNodeIndex(rRange); + } + else + { + pInfo->aAnchorEnd.SetAsNodeIndex(rRange); + } + + // also remember that we expect an adjustment for this redline + pInfo->bNeedsAdjustment = true; + } + else + { + // inside of a paragraph: use regular XTextRanges (bookmarks) + if (bStart) + pInfo->aAnchorStart.Set(rRange); + else + pInfo->aAnchorEnd.Set(rRange); + } + + // if this Cursor was the last missing info, we insert the + // node into the document + // then we can remove the entry from the map and destroy the object + if (IsReady(pInfo)) + { + InsertIntoDocument(pInfo); + m_aRedlineMap.erase(rId); + delete pInfo; + } + } + // else: unknown Id -> ignore +} + +void XMLRedlineImportHelper::AdjustStartNodeCursor( + const OUString& rId) /// ID used in RedlineAdd() call +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // start + end nodes are treated the same. For either it's + // necessary that the target node already exists. + + RedlineMapType::iterator aFind = m_aRedlineMap.find(rId); + if (m_aRedlineMap.end() != aFind) + { + // RedlineInfo found; now set Cursor + RedlineInfo* pInfo = aFind->second; + + pInfo->bNeedsAdjustment = false; + + // if now ready, insert into document + if( IsReady(pInfo) ) + { + InsertIntoDocument(pInfo); + m_aRedlineMap.erase(rId); + delete pInfo; + } + } + // else: can't find redline -> ignore +} + +inline bool XMLRedlineImportHelper::IsReady(const RedlineInfo* pRedline) +{ + // we can insert a redline if we have start & end, and we don't + // expect adjustments for either of these + return ( pRedline->aAnchorEnd.IsValid() && + pRedline->aAnchorStart.IsValid() && + !pRedline->bNeedsAdjustment ); +} + +void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo) +{ + OSL_ENSURE(nullptr != pRedlineInfo, "need redline info"); + OSL_ENSURE(IsReady(pRedlineInfo), "redline info not complete yet!"); + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + // Insert the Redline as described by pRedlineInfo into the + // document. If we are in insert mode, don't insert any redlines + // (and delete 'deleted' inline redlines) + + // get the document (from one of the positions) + SwDoc* pDoc = pRedlineInfo->aAnchorStart.GetDoc(); + + if (!pDoc) + { + SAL_WARN("sw", "no SwDoc => cannot insert redline."); + return; + } + + // now create the PaM for the redline + SwPaM aPaM(pDoc->GetNodes().GetEndOfContent()); + pRedlineInfo->aAnchorStart.CopyPositionInto(*aPaM.GetPoint(), *pDoc); + aPaM.SetMark(); + pRedlineInfo->aAnchorEnd.CopyPositionInto(*aPaM.GetPoint(), *pDoc); + + // collapse PaM if (start == end) + if (*aPaM.GetPoint() == *aPaM.GetMark()) + { + aPaM.DeleteMark(); + } + + // cover three cases: + // 1) empty redlines (no range, no content) + // 2) check for: + // a) bIgnoreRedline (e.g. insert mode) + // b) illegal PaM range (CheckNodesRange()) + // c) redline with empty content section (quite useless) + // 3) normal case: insert redline + SwTextNode const* pTempNode(nullptr); + if( !aPaM.HasMark() && (pRedlineInfo->pContentIndex == nullptr) ) + { + // these redlines have no function, and will thus be ignored (just as + // in sw3io), so no action here + } + else if ( m_bIgnoreRedlines || + !CheckNodesRange( aPaM.GetPoint()->nNode, + aPaM.GetMark()->nNode, + true ) + || (pRedlineInfo->pContentIndex + && (pRedlineInfo->pContentIndex->GetIndex() + 2 + == pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex()) + && (pTempNode = pDoc->GetNodes()[pRedlineInfo->pContentIndex->GetIndex() + 1]->GetTextNode()) != nullptr + && pTempNode->GetText().isEmpty() + && !pTempNode->GetpSwpHints() + && !pTempNode->GetAnchoredFlys())) + { + // ignore redline (e.g. file loaded in insert mode): + // delete 'deleted' redlines and forget about the whole thing + if (RedlineType::Delete == pRedlineInfo->eType) + { + pDoc->getIDocumentContentOperations().DeleteRange(aPaM); + // And what about the "deleted nodes"? + // They have to be deleted as well (#i80689)! + if( m_bIgnoreRedlines && pRedlineInfo->pContentIndex != nullptr ) + { + SwNodeIndex aIdx( *pRedlineInfo->pContentIndex ); + const SwNode* pEnd = aIdx.GetNode().EndOfSectionNode(); + if( pEnd ) + { + SwNodeIndex aEnd( *pEnd, 1 ); + SwPaM aDel( aIdx, aEnd ); + pDoc->getIDocumentContentOperations().DeleteRange(aDel); + } + } + } + } + else + { + // regular file loading: insert redline + + // create redline (using pRedlineData which gets copied in SwRangeRedline()) + SwRedlineData* pRedlineData = ConvertRedline(pRedlineInfo, pDoc); + SwRangeRedline* pRedline = + new SwRangeRedline( pRedlineData, *aPaM.GetPoint(), + !pRedlineInfo->bMergeLastParagraph ); + + // set mark + if( aPaM.HasMark() ) + { + pRedline->SetMark(); + *(pRedline->GetMark()) = *aPaM.GetMark(); + } + + // set content node (if necessary) + if (nullptr != pRedlineInfo->pContentIndex) + { + sal_uLong nPoint = aPaM.GetPoint()->nNode.GetIndex(); + if( nPoint < pRedlineInfo->pContentIndex->GetIndex() || + nPoint > pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex() ) + pRedline->SetContentIdx(pRedlineInfo->pContentIndex); +#if OSL_DEBUG_LEVEL > 1 + else + OSL_FAIL( "Recursive change tracking" ); +#endif + } + + // set redline mode (without doing the associated book-keeping) + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::On); + pDoc->getIDocumentRedlineAccess().AppendRedline(pRedline, false); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE); + } +} + +SwRedlineData* XMLRedlineImportHelper::ConvertRedline( + RedlineInfo* pRedlineInfo, + SwDoc* pDoc) +{ + // convert info: + // 1) Author String -> Author ID (default to zero) + std::size_t nAuthorId = (nullptr == pDoc) ? 0 : + pDoc->getIDocumentRedlineAccess().InsertRedlineAuthor( pRedlineInfo->sAuthor ); + + // 2) util::DateTime -> DateTime + DateTime aDT( DateTime::EMPTY ); + aDT.SetYear( pRedlineInfo->aDateTime.Year ); + aDT.SetMonth( pRedlineInfo->aDateTime.Month ); + aDT.SetDay( pRedlineInfo->aDateTime.Day ); + aDT.SetHour( pRedlineInfo->aDateTime.Hours ); + aDT.SetMin( pRedlineInfo->aDateTime.Minutes ); + aDT.SetSec( pRedlineInfo->aDateTime.Seconds ); + aDT.SetNanoSec( pRedlineInfo->aDateTime.NanoSeconds ); + + // 3) recursively convert next redline + // ( check presence and sanity of hierarchical redline info ) + SwRedlineData* pNext = nullptr; + if ( (nullptr != pRedlineInfo->pNextRedline) && + (RedlineType::Delete == pRedlineInfo->eType) && + (RedlineType::Insert == pRedlineInfo->pNextRedline->eType) ) + { + pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc); + } + + // create redline data + SwRedlineData* pData = new SwRedlineData(pRedlineInfo->eType, + nAuthorId, aDT, + pRedlineInfo->sComment, + pNext); // next data (if available) + + return pData; +} + +void XMLRedlineImportHelper::SetShowChanges( bool bShow ) +{ + m_bShowChanges = bShow; +} + +void XMLRedlineImportHelper::SetRecordChanges( bool bRecord ) +{ + m_bRecordChanges = bRecord; +} + +void XMLRedlineImportHelper::SetProtectionKey( + const Sequence<sal_Int8> & rKey ) +{ + m_aProtectionKey = rKey; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.hxx b/sw/source/filter/xml/XMLRedlineImportHelper.hxx new file mode 100644 index 000000000..43bcdd5a9 --- /dev/null +++ b/sw/source/filter/xml/XMLRedlineImportHelper.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_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLREDLINEIMPORTHELPER_HXX + +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/util/DateTime.hpp> +#include <redline.hxx> + +#include <map> + +class SvXMLImport; +class RedlineInfo; +class SwRedlineData; +class SwDoc; +namespace com::sun::star { + namespace text { class XTextCursor; } + namespace text { class XTextRange; } + namespace frame { class XModel; } +} + +typedef std::map< OUString, RedlineInfo* > RedlineMapType; + +class XMLRedlineImportHelper final +{ + SvXMLImport & m_rImport; + + const OUString m_sInsertion; + const OUString m_sDeletion; + const OUString m_sFormatChange; + + RedlineMapType m_aRedlineMap; + + // if true, no redlines should be inserted into document + // (This typically happen when a document is loaded in 'insert'-mode.) + bool m_bIgnoreRedlines; + + // save information for saving and reconstruction of the redline mode + css::uno::Reference<css::beans::XPropertySet> m_xModelPropertySet; + css::uno::Reference<css::beans::XPropertySet> m_xImportInfoPropertySet; + bool m_bShowChanges; + bool m_bRecordChanges; + css::uno::Sequence<sal_Int8> m_aProtectionKey; + +public: + + XMLRedlineImportHelper( + SvXMLImport & rImport, + bool bIgnoreRedlines, // ignore redlines mode + // property sets of model + import info for saving + restoring the + // redline mode + const css::uno::Reference<css::beans::XPropertySet> & rModel, + const css::uno::Reference<css::beans::XPropertySet> & rImportInfoSet ); + ~XMLRedlineImportHelper(); + + // create a redline object + // (The redline will be inserted into the document after both start + // and end cursor has been set.) + void Add( + const OUString& rType, // redline type (insert, del,... ) + const OUString& rId, // use to identify this redline + const OUString& rAuthor, // name of the author + const OUString& rComment, // redline comment + const css::util::DateTime& rDateTime, // date+time + bool bMergeLastParagraph); // merge last paragraph? + + // create a text section for the redline, and return an + // XText/XTextCursor that may be used to write into it. + css::uno::Reference<css::text::XTextCursor> CreateRedlineTextSection( + css::uno::Reference<css::text::XTextCursor> const & xOldCursor, // needed to get the document + const OUString& rId); // ID used to RedlineAdd() call + + // Set start or end position for a redline in the text body. + // Accepts XTextRange objects. + void SetCursor( + const OUString& rId, // ID used in RedlineAdd() call + bool bStart, // start or end Range + css::uno::Reference<css::text::XTextRange> const & rRange, // the actual XTextRange + // text range is (from an XML view) outside of a paragraph + // (i.e. before a table) + bool bIsOusideOfParagraph); + + /** + * Adjust the start (end) position for a redline that begins in a + * start node. It takes the cursor positions _inside_ the redlined + * element (e.g. section or table). + */ + void AdjustStartNodeCursor( + const OUString& rId); // ID used in RedlineAdd() call + + // set redline mode: show changes + void SetShowChanges( bool bShowChanges ); + + // set redline mode: record changes + void SetRecordChanges( bool bRecordChanges ); + + // set redline protection key + void SetProtectionKey(const css::uno::Sequence<sal_Int8> & rKey ); + +private: + + static inline bool IsReady(const RedlineInfo* pRedline); + + void InsertIntoDocument(RedlineInfo* pRedline); + + SwRedlineData* ConvertRedline( + RedlineInfo* pRedline, // RedlineInfo to be converted + SwDoc* pDoc); // document needed for Author-ID conversion +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/swxml.cxx b/sw/source/filter/xml/swxml.cxx new file mode 100644 index 000000000..d36e206e1 --- /dev/null +++ b/sw/source/filter/xml/swxml.cxx @@ -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 . + */ + +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/Parser.hpp> +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/document/NamedPropertyValues.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <o3tl/any.hxx> +#include <vcl/errinf.hxx> +#include <sfx2/docfile.hxx> +#include <svtools/sfxecode.hxx> +#include <svl/stritem.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <sal/log.hxx> +#include <sfx2/frame.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <swerror.h> +#include <fltini.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <docfunc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <DocumentRedlineManager.hxx> +#include <docary.hxx> +#include <docsh.hxx> +#include <unotextrange.hxx> +#include <SwXMLSectionList.hxx> + +#include <SwStyleNameMapper.hxx> +#include <poolfmt.hxx> +#include <numrule.hxx> +#include <paratr.hxx> +#include <fmtrowsplt.hxx> + +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/sfxsids.hrc> +#include <istyleaccess.hxx> + +#include <sfx2/DocumentMetadataAccess.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::lang; + +static void lcl_EnsureValidPam( SwPaM& rPam ) +{ + if( rPam.GetContentNode() != nullptr ) + { + // set proper point content + if( rPam.GetContentNode() != rPam.GetPoint()->nContent.GetIdxReg() ) + { + rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 ); + } + // else: point was already valid + + // if mark is invalid, we delete it + if( ( rPam.GetContentNode( false ) == nullptr ) || + ( rPam.GetContentNode( false ) != rPam.GetMark()->nContent.GetIdxReg() ) ) + { + rPam.DeleteMark(); + } + } + else + { + // point is not valid, so move it into the first content + rPam.DeleteMark(); + rPam.GetPoint()->nNode = + *rPam.GetDoc()->GetNodes().GetEndOfContent().StartOfSectionNode(); + ++ rPam.GetPoint()->nNode; + rPam.Move( fnMoveForward, GoInContent ); // go into content + } +} + +XMLReader::XMLReader() +{ +} + +SwReaderType XMLReader::GetReaderType() +{ + return SwReaderType::Storage; +} + +namespace +{ + +/// read a component (file + filter version) +ErrCode ReadThroughComponent( + uno::Reference<io::XInputStream> const & xInputStream, + uno::Reference<XComponent> const & xModelComponent, + const OUString& rStreamName, + uno::Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessfull, + bool bEncrypted ) +{ + OSL_ENSURE(xInputStream.is(), "input stream missing"); + OSL_ENSURE(xModelComponent.is(), "document missing"); + OSL_ENSURE(rxContext.is(), "factory missing"); + OSL_ENSURE(nullptr != pFilterName,"I need a service name for the component!"); + + // prepare ParserInputSrouce + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rName; + aParserInput.aInputStream = xInputStream; + + // get parser + uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(rxContext); + SAL_INFO( "sw.filter", "parser created" ); + // get filter + const OUString aFilterName(OUString::createFromAscii(pFilterName)); + uno::Reference< xml::sax::XDocumentHandler > xFilter( + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(aFilterName, rFilterArguments, rxContext), + UNO_QUERY); + SAL_WARN_IF(!xFilter.is(), "sw.filter", "Can't instantiate filter component: " << aFilterName); + if( !xFilter.is() ) + return ERR_SWG_READ_ERROR; + SAL_INFO( "sw.filter", "" << pFilterName << " created" ); + // connect parser and filter + xParser->setDocumentHandler( xFilter ); + + // connect model and filter + uno::Reference < XImporter > xImporter( xFilter, UNO_QUERY ); + xImporter->setTargetDocument( xModelComponent ); + uno::Reference< xml::sax::XFastParser > xFastParser = dynamic_cast< + xml::sax::XFastParser* >( xFilter.get() ); + + // finally, parser the stream + try + { + if( xFastParser.is() ) + xFastParser->parseStream( aParserInput ); + else + xParser->parseStream( aParserInput ); + } + catch( xml::sax::SAXParseException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + // sax parser sends wrapped exceptions, + // try to find the original one + xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException*>(&r); + bool bTryChild = true; + + while( bTryChild ) + { + xml::sax::SAXException aTmp; + if ( aSaxEx.WrappedException >>= aTmp ) + aSaxEx = aTmp; + else + bTryChild = false; + } + + packages::zip::ZipIOException aBrokenPackage; + if ( aSaxEx.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sw", "SAX parse exception caught while importing: " << exceptionToString(ex) ); + + const OUString sErr( OUString::number( r.LineNumber ) + + "," + + OUString::number( r.ColumnNumber ) ); + + if( !rStreamName.isEmpty() ) + { + return *new TwoStringErrorInfo( + (bMustBeSuccessfull ? ERR_FORMAT_FILE_ROWCOL + : WARN_FORMAT_FILE_ROWCOL), + rStreamName, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + else + { + OSL_ENSURE( bMustBeSuccessfull, "Warnings are not supported" ); + return *new StringErrorInfo( ERR_FORMAT_ROWCOL, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + } + catch(const xml::sax::SAXException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + packages::zip::ZipIOException aBrokenPackage; + if ( r.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sw", "uno exception caught while importing: " << exceptionToString(ex) ); + return ERR_SWG_READ_ERROR; + } + catch(const packages::zip::ZipIOException&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERRCODE_IO_BROKENPACKAGE; + } + catch(const io::IOException&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERR_SWG_READ_ERROR; + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sw", "uno exception caught while importing" ); + return ERR_SWG_READ_ERROR; + } + + // success! + return ERRCODE_NONE; +} + +// read a component (storage version) +ErrCode ReadThroughComponent( + uno::Reference<embed::XStorage> const & xStorage, + uno::Reference<XComponent> const & xModelComponent, + const char* pStreamName, + uno::Reference<uno::XComponentContext> const & rxContext, + const char* pFilterName, + const Sequence<Any>& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessfull) +{ + OSL_ENSURE(xStorage.is(), "Need storage!"); + OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!"); + + // open stream (and set parser input) + OUString sStreamName = OUString::createFromAscii(pStreamName); + bool bContainsStream = false; + try + { + bContainsStream = xStorage->isStreamElement(sStreamName); + } + catch( container::NoSuchElementException& ) + { + } + + if (!bContainsStream ) + { + // stream name not found! return immediately with OK signal + return ERRCODE_NONE; + } + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rFilterArguments.hasElements() ) + rFilterArguments.getConstArray()[0] >>= xInfoSet; + OSL_ENSURE( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", makeAny( sStreamName ) ); + } + + try + { + // get input stream + uno::Reference <io::XStream> xStream = xStorage->openStreamElement( sStreamName, embed::ElementModes::READ ); + uno::Reference <beans::XPropertySet > xProps( xStream, uno::UNO_QUERY ); + + Any aAny = xProps->getPropertyValue("Encrypted"); + + auto b = o3tl::tryAccess<bool>(aAny); + bool bEncrypted = b && *b; + + uno::Reference <io::XInputStream> xInputStream = xStream->getInputStream(); + + // read from the stream + return ReadThroughComponent( + xInputStream, xModelComponent, sStreamName, rxContext, + pFilterName, rFilterArguments, + rName, bMustBeSuccessfull, bEncrypted ); + } + catch ( packages::WrongPasswordException& ) + { + return ERRCODE_SFX_WRONGPASSWORD; + } + catch( packages::zip::ZipIOException& ) + { + return ERRCODE_IO_BROKENPACKAGE; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Error on import" ); + // TODO/LATER: error handling + } + + return ERR_SWG_READ_ERROR; +} + +} + +// #i44177# +static void lcl_AdjustOutlineStylesForOOo(SwDoc& _rDoc) +{ + // array containing the names of the default outline styles ('Heading 1', + // 'Heading 2', ..., 'Heading 10') + OUString aDefOutlStyleNames[ MAXLEVEL ]; + { + OUString sStyleName; + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + sStyleName = + SwStyleNameMapper::GetProgName( RES_POOLCOLL_HEADLINE1 + i, + sStyleName ); + aDefOutlStyleNames[i] = sStyleName; + } + } + + // array indicating, which outline level already has a style assigned. + bool aOutlineLevelAssigned[ MAXLEVEL ]; + // array of the default outline styles, which are created for the document. + SwTextFormatColl* aCreatedDefaultOutlineStyles[ MAXLEVEL ]; + + { + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + aOutlineLevelAssigned[ i ] = false; + aCreatedDefaultOutlineStyles[ i ] = nullptr; + } + } + + // determine, which outline level has already a style assigned and + // which of the default outline styles is created. + const SwTextFormatColls& rColls = *(_rDoc.GetTextFormatColls()); + for ( size_t n = 1; n < rColls.size(); ++n ) + { + SwTextFormatColl* pColl = rColls[ n ]; + if ( pColl->IsAssignedToListLevelOfOutlineStyle() ) + { + aOutlineLevelAssigned[ pColl->GetAssignedOutlineStyleLevel() ] = true; + } + + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + if ( aCreatedDefaultOutlineStyles[ i ] == nullptr && + pColl->GetName() == aDefOutlStyleNames[i] ) + { + aCreatedDefaultOutlineStyles[ i ] = pColl; + break; + } + } + } + + // assign already created default outline style to outline level, which + // doesn't have a style assigned to it. + const SwNumRule* pOutlineRule = _rDoc.GetOutlineNumRule(); + for ( sal_uInt8 i = 0; i < MAXLEVEL; ++i ) + { + // #i73361# + // Do not change assignment of already created default outline style + // to a certain outline level. + if ( !aOutlineLevelAssigned[ i ] && + aCreatedDefaultOutlineStyles[ i ] != nullptr && + ! aCreatedDefaultOutlineStyles[ i ]->IsAssignedToListLevelOfOutlineStyle() ) + { + // apply outline level at created default outline style + aCreatedDefaultOutlineStyles[ i ]->AssignToListLevelOfOutlineStyle(i); + + // apply outline numbering rule, if none is set. + const SfxPoolItem& rItem = + aCreatedDefaultOutlineStyles[ i ]->GetFormatAttr( RES_PARATR_NUMRULE, false ); + if ( static_cast<const SwNumRuleItem&>(rItem).GetValue().isEmpty() ) + { + SwNumRuleItem aItem( pOutlineRule->GetName() ); + aCreatedDefaultOutlineStyles[ i ]->SetFormatAttr( aItem ); + } + } + } +} + +static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs(SwDoc& _rDoc) +{ + if ( _rDoc.getIDocumentDrawModelAccess().GetDrawModel() && + _rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ) ) + { + const SdrPage& rSdrPage( *(_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) ); + + // iterate recursive with group objects over all shapes on the draw page + SdrObjListIter aIter( &rSdrPage ); + 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( + pOle2Obj->getSdrModelFromSdrObject(), + 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* pReplaced = pObjList->ReplaceObject( pGraphicObj, pOle2Obj->GetOrdNum() ); + SdrObject::Free( pReplaced ); + } + } + } +} + +ErrCode XMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, const OUString & rName ) +{ + // needed for relative URLs, but in clipboard copy/paste there may be none + // and also there is the SwXMLTextBlocks special case + SAL_INFO_IF(rBaseURL.isEmpty(), "sw.filter", "sw::XMLReader: no base URL"); + + // Get service factory + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper; + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + + // get the input stream (storage or stream) + uno::Reference<embed::XStorage> xStorage; + if( m_pMedium ) + xStorage = m_pMedium->GetStorage(); + else + xStorage = m_xStorage; + + if( !xStorage.is() ) + return ERR_SWG_READ_ERROR; + + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, + SvXMLGraphicHelperMode::Read ); + xGraphicStorageHandler = xGraphicHelper.get(); + SfxObjectShell *pPersist = rDoc.GetPersist(); + if( pPersist ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + xStorage, *pPersist, + SvXMLEmbeddedObjectHelperMode::Read ); + xObjectResolver = xObjectHelper.get(); + } + + // Get the docshell, the model, and finally the model's component + SwDocShell *pDocSh = rDoc.GetDocShell(); + OSL_ENSURE( pDocSh, "XMLReader::Read: got no doc shell" ); + if( !pDocSh ) + return ERR_SWG_READ_ERROR; + uno::Reference< lang::XComponent > xModelComp = pDocSh->GetModel(); + OSL_ENSURE( xModelComp.is(), + "XMLReader::Read: got no model" ); + if( !xModelComp.is() ) + return ERR_SWG_READ_ERROR; + + // create and prepare the XPropertySet that gets passed through + // the components, and the XStatusIndicator that shows progress to + // the user. + + // create XPropertySet with three properties for status indicator + comphelper::PropertyMapEntry const aInfoMap[] = + { + { OUString("ProgressRange"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("NumberStyles"), 0, + cppu::UnoType<container::XNameContainer>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("RecordChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("ShowChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("RedlineProtectionKey"), 0, + cppu::UnoType<Sequence<sal_Int8>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("PrivateData"), 0, + cppu::UnoType<XInterface>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + // properties for insert modes + { OUString("StyleInsertModeFamilies"), 0, + cppu::UnoType<Sequence<OUString>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleInsertModeOverwrite"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TextInsertModeRange"), 0, + cppu::UnoType<text::XTextRange>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("AutoTextMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("OrganizerMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + // #i28749# - Add property, which indicates, if the + // shape position attributes are given in horizontal left-to-right layout. + // This is the case for the OpenOffice.org file format. + { OUString("ShapePositionInHoriL2R"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + { OUString("BuildId"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + + // Add property, which indicates, if a text document in OpenOffice.org + // file format is read. + // Note: Text documents read via the binary filter are also finally + // read using the OpenOffice.org file format. Thus, e.g. for text + // documents in StarOffice 5.2 binary file format this property + // will be true. + { OUString("TextDocInOOoFileFormat"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), + css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + uno::Reference< beans::XPropertySet > xInfoSet( + comphelper::GenericPropertySet_CreateInstance( + new comphelper::PropertySetInfo( aInfoMap ) ) ); + + // get BuildId from parent container if available + uno::Reference< container::XChild > xChild( xModelComp, uno::UNO_QUERY ); + if( xChild.is() ) + { + uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY ); + if( xParentSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() ); + const OUString sPropName("BuildId" ); + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) ) + { + xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) ); + } + } + } + + // try to get an XStatusIndicator from the Medium + uno::Reference<task::XStatusIndicator> xStatusIndicator; + + if (pDocSh->GetMedium()) + { + SfxItemSet* pSet = pDocSh->GetMedium()->GetItemSet(); + if (pSet) + { + const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>( + pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) ); + if (pItem) + { + pItem->GetValue() >>= xStatusIndicator; + } + } + } + + // set progress range and start status indicator + sal_Int32 nProgressRange(1000000); + if (xStatusIndicator.is()) + { + xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange); + } + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue("ProgressRange", aProgRange); + + Reference< container::XNameAccess > xLateInitSettings( document::NamedPropertyValues::create(xContext), UNO_QUERY_THROW ); + beans::NamedValue aLateInitSettings( "LateInitSettings", makeAny( xLateInitSettings ) ); + + xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) ); + + // prepare filter arguments, WARNING: the order is important! + Sequence<Any> aFilterArgs{ Any(xInfoSet), + Any(xStatusIndicator), + Any(xGraphicStorageHandler), + Any(xObjectResolver), + Any(aLateInitSettings) }; + + Sequence<Any> aEmptyArgs{ Any(xInfoSet), + Any(xStatusIndicator) }; + + // prepare for special modes + if( m_aOption.IsFormatsOnly() ) + { + sal_Int32 nCount = + (m_aOption.IsFrameFormats() ? 1 : 0) + + (m_aOption.IsPageDescs() ? 1 : 0) + + (m_aOption.IsTextFormats() ? 2 : 0) + + (m_aOption.IsNumRules() ? 1 : 0); + + Sequence< OUString> aFamiliesSeq( nCount ); + OUString *pSeq = aFamiliesSeq.getArray(); + if( m_aOption.IsFrameFormats() ) + // SfxStyleFamily::Frame; + *pSeq++ = "FrameStyles"; + if( m_aOption.IsPageDescs() ) + // SfxStyleFamily::Page; + *pSeq++ = "PageStyles"; + if( m_aOption.IsTextFormats() ) + { + // (SfxStyleFamily::Char|SfxStyleFamily::Para); + *pSeq++ = "CharacterStyles"; + *pSeq++ = "ParagraphStyles"; + } + if( m_aOption.IsNumRules() ) + // SfxStyleFamily::Pseudo; + *pSeq++ = "NumberingStyles"; + + xInfoSet->setPropertyValue( "StyleInsertModeFamilies", + makeAny(aFamiliesSeq) ); + + xInfoSet->setPropertyValue( "StyleInsertModeOverwrite", makeAny(!m_aOption.IsMerge()) ); + } + else if( m_bInsertMode ) + { + const uno::Reference<text::XTextRange> xInsertTextRange = + SwXTextRange::CreateXTextRange(rDoc, *rPaM.GetPoint(), nullptr); + xInfoSet->setPropertyValue( "TextInsertModeRange", + makeAny(xInsertTextRange) ); + } + else + { + rPaM.GetBound().nContent.Assign(nullptr, 0); + rPaM.GetBound(false).nContent.Assign(nullptr, 0); + } + + if( IsBlockMode() ) + { + xInfoSet->setPropertyValue( "AutoTextMode", makeAny(true) ); + } + if( IsOrganizerMode() ) + { + xInfoSet->setPropertyValue( "OrganizerMode", makeAny(true) ); + } + + // Set base URI + // there is ambiguity which medium should be used here + // for now the own medium has a preference + SfxMedium* pMedDescrMedium = m_pMedium ? m_pMedium : pDocSh->GetMedium(); + OSL_ENSURE( pMedDescrMedium, "There is no medium to get MediaDescriptor from!" ); + + xInfoSet->setPropertyValue( "BaseURI", makeAny( rBaseURL ) ); + + // TODO/LATER: separate links from usual embedded objects + OUString StreamPath; + if( SfxObjectCreateMode::EMBEDDED == rDoc.GetDocShell()->GetCreateMode() ) + { + if ( pMedDescrMedium && pMedDescrMedium->GetItemSet() ) + { + const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>( + pMedDescrMedium->GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) ); + if ( pDocHierarchItem ) + StreamPath = pDocHierarchItem->GetValue(); + } + else + { + StreamPath = "dummyObjectName"; + } + + if( !StreamPath.isEmpty() ) + { + xInfoSet->setPropertyValue( "StreamRelPath", makeAny( StreamPath ) ); + } + } + + rtl::Reference<SwDoc> aHoldRef(&rDoc); // prevent deletion + ErrCode nRet = ERRCODE_NONE; + + // save redline mode into import info property set + const OUString sShowChanges("ShowChanges"); + const OUString sRecordChanges("RecordChanges"); + const OUString sRedlineProtectionKey("RedlineProtectionKey"); + xInfoSet->setPropertyValue( sShowChanges, + makeAny(IDocumentRedlineAccess::IsShowChanges(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) ); + xInfoSet->setPropertyValue( sRecordChanges, + makeAny(IDocumentRedlineAccess::IsRedlineOn(rDoc.getIDocumentRedlineAccess().GetRedlineFlags())) ); + xInfoSet->setPropertyValue( sRedlineProtectionKey, + makeAny(rDoc.getIDocumentRedlineAccess().GetRedlinePassword()) ); + + // force redline mode to "none" + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE ); + + const bool bOASIS = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 ); + // #i28749# - set property <ShapePositionInHoriL2R> + { + const bool bShapePositionInHoriL2R = !bOASIS; + xInfoSet->setPropertyValue( + "ShapePositionInHoriL2R", + makeAny( bShapePositionInHoriL2R ) ); + } + { + const bool bTextDocInOOoFileFormat = !bOASIS; + xInfoSet->setPropertyValue( + "TextDocInOOoFileFormat", + makeAny( bTextDocInOOoFileFormat ) ); + } + + ErrCode nWarnRDF = ERRCODE_NONE; + if ( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() || + m_bInsertMode) ) + { + // RDF metadata - must be read before styles/content + // N.B.: embedded documents have their own manifest.rdf! + try + { + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA(xModelComp, + uno::UNO_QUERY_THROW); + const uno::Reference<frame::XModel> xModel(xModelComp, + uno::UNO_QUERY_THROW); + const uno::Reference<rdf::XURI> xBaseURI( ::sfx2::createBaseURI( + xContext, xModel, rBaseURL, StreamPath) ); + const uno::Reference<task::XInteractionHandler> xHandler( + pDocSh->GetMedium()->GetInteractionHandler() ); + xDMA->loadMetadataFromStorage(xStorage, xBaseURI, xHandler); + } + catch (const lang::WrappedTargetException & e) + { + ucb::InteractiveAugmentedIOException iaioe; + if (e.TargetException >>= iaioe) + { + // import error that was not ignored by InteractionHandler! + nWarnRDF = ERR_SWG_READ_ERROR; + } + else + { + nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something wrong? + } + } + catch (uno::Exception &) + { + nWarnRDF = WARN_SWG_FEATURES_LOST; // uhh... something went wrong? + } + } + + // read storage streams + + // #i103539#: always read meta.xml for generator + ErrCode const nWarn = ReadThroughComponent( + xStorage, xModelComp, "meta.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaImporter" + : "com.sun.star.comp.Writer.XMLMetaImporter"), + aEmptyArgs, rName, false ); + + ErrCode nWarn2 = ERRCODE_NONE; + if( !(IsOrganizerMode() || IsBlockMode() || m_aOption.IsFormatsOnly() || + m_bInsertMode) ) + { + nWarn2 = ReadThroughComponent( + xStorage, xModelComp, "settings.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsImporter" + : "com.sun.star.comp.Writer.XMLSettingsImporter"), + aFilterArgs, rName, false ); + } + + nRet = ReadThroughComponent( + xStorage, xModelComp, "styles.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesImporter" + : "com.sun.star.comp.Writer.XMLStylesImporter"), + aFilterArgs, rName, true ); + + if( !nRet && !(IsOrganizerMode() || m_aOption.IsFormatsOnly()) ) + nRet = ReadThroughComponent( + xStorage, xModelComp, "content.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentImporter" + : "com.sun.star.comp.Writer.XMLContentImporter"), + aFilterArgs, rName, true ); + + if( !(IsOrganizerMode() || IsBlockMode() || m_bInsertMode || + m_aOption.IsFormatsOnly() || + // sw_redlinehide: disable layout cache for now + !*o3tl::doAccess<bool>(xInfoSet->getPropertyValue(sShowChanges)))) + { + try + { + uno::Reference < io::XStream > xStm = xStorage->openStreamElement( "layout-cache", embed::ElementModes::READ ); + std::unique_ptr<SvStream> pStrm2 = utl::UcbStreamHelper::CreateStream( xStm ); + if( !pStrm2->GetError() ) + rDoc.ReadLayoutCache( *pStrm2 ); + } + catch (const uno::Exception&) + { + } + } + + // Notify math objects + if( m_bInsertMode ) + rDoc.PrtOLENotify( false ); + else if ( rDoc.IsOLEPrtNotifyPending() ) + rDoc.PrtOLENotify( true ); + + nRet = nRet ? nRet : (nWarn ? nWarn : (nWarn2 ? nWarn2 : nWarnRDF ) ); + + m_aOption.ResetAllFormatsOnly(); + + // redline password + Any aAny = xInfoSet->getPropertyValue( sRedlineProtectionKey ); + Sequence<sal_Int8> aKey; + aAny >>= aKey; + rDoc.getIDocumentRedlineAccess().SetRedlinePassword( aKey ); + + // restore redline mode from import info property set + RedlineFlags nRedlineFlags = RedlineFlags::ShowInsert; + aAny = xInfoSet->getPropertyValue( sShowChanges ); + if ( *o3tl::doAccess<bool>(aAny) ) + nRedlineFlags |= RedlineFlags::ShowDelete; + aAny = xInfoSet->getPropertyValue( sRecordChanges ); + if ( *o3tl::doAccess<bool>(aAny) || aKey.hasElements() ) + nRedlineFlags |= RedlineFlags::On; + + // ... restore redline mode + // (First set bogus mode to make sure the mode in getIDocumentRedlineAccess().SetRedlineFlags() + // is different from its previous mode.) + rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ~nRedlineFlags ); + // must set flags to show delete so that CompressRedlines works + rDoc.getIDocumentRedlineAccess().SetRedlineFlags(nRedlineFlags|RedlineFlags::ShowDelete); + // tdf#83260 ensure that the first call of CompressRedlines after loading + // the document is a no-op by calling it now + rDoc.getIDocumentRedlineAccess().CompressRedlines(); + // can't set it on the layout or view shell because it doesn't exist yet + rDoc.GetDocumentRedlineManager().SetHideRedlines(!(nRedlineFlags & RedlineFlags::ShowDelete)); + + lcl_EnsureValidPam( rPaM ); // move Pam into valid content + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + aHoldRef.clear(); + + if ( !bOASIS ) + { + // #i44177# - assure that for documents in OpenOffice.org + // file format the relation between outline numbering rule and styles is + // filled-up accordingly. + // Note: The OpenOffice.org file format, which has no content that applies + // a certain style, which is related to the outline numbering rule, + // has lost the information, that this certain style is related to + // the outline numbering rule. + // #i70748# - only for templates + if ( m_pMedium && m_pMedium->GetFilter() && + m_pMedium->GetFilter()->IsOwnTemplateFormat() ) + { + lcl_AdjustOutlineStylesForOOo( rDoc ); + } + // Fix #i58251#: Unfortunately is the static default different to SO7 behaviour, + // so we have to set a dynamic default after importing SO7 + rDoc.SetDefault(SwFormatRowSplit(false)); + } + + rDoc.PropagateOutlineRule(); + + // #i62875# + if ( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && !docfunc::ExistsDrawObjs( rDoc ) ) + { + rDoc.getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false); + } + + // Convert all instances of <SdrOle2Obj> into <SdrGrafObj>, because the + // Writer doesn't support such objects. + lcl_ConvertSdrOle2ObjsToSdrGrafObjs( rDoc ); + + // set BuildId on XModel for later OLE object loading + if( xInfoSet.is() ) + { + uno::Reference< beans::XPropertySet > xModelSet( xModelComp, uno::UNO_QUERY ); + if( xModelSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() ); + const OUString sName("BuildId" ); + if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sName) ) + { + xModelSet->setPropertyValue( sName, xInfoSet->getPropertyValue(sName) ); + } + } + } + + if (xStatusIndicator.is()) + { + xStatusIndicator->end(); + } + + rDoc.GetIStyleAccess().clearCaches(); // Clear Automatic-Style-Caches(shared_pointer!) + return nRet; +} + + // read the sections of the document, which is equal to the medium. + // returns the count of it +size_t XMLReader::GetSectionList( SfxMedium& rMedium, + std::vector<OUString>& rStrings) const +{ + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + uno::Reference < embed::XStorage > xStg2; + if( ( xStg2 = rMedium.GetStorage() ).is() ) + { + try + { + xml::sax::InputSource aParserInput; + const OUString sDocName( "content.xml" ); + aParserInput.sSystemId = sDocName; + + uno::Reference < io::XStream > xStm = xStg2->openStreamElement( sDocName, embed::ElementModes::READ ); + aParserInput.aInputStream = xStm->getInputStream(); + + // get filter + rtl::Reference< SwXMLSectionList > xImport = new SwXMLSectionList( xContext, rStrings ); + + // parse + xImport->parseStream( aParserInput ); + } + catch( xml::sax::SAXParseException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( xml::sax::SAXException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( io::IOException& ) + { + TOOLS_WARN_EXCEPTION("sw", ""); + // re throw ? + } + catch( packages::WrongPasswordException& ) + { + // re throw ? + } + } + return rStrings.size(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/wrtxml.cxx b/sw/source/filter/xml/wrtxml.cxx new file mode 100644 index 000000000..4a7d5f879 --- /dev/null +++ b/sw/source/filter/xml/wrtxml.cxx @@ -0,0 +1,568 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/frame/XModule.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/genericpropertyset.hxx> +#include <comphelper/propertysetinfo.hxx> +#include <vcl/errinf.hxx> +#include <sal/log.hxx> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <unotools/saveopt.hxx> +#include <svl/stritem.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <pam.hxx> +#include <doc.hxx> +#include <docfunc.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> +#include <docstat.hxx> +#include <docsh.hxx> + +#include <unotools/ucbstreamhelper.hxx> +#include <swerror.h> +#include "wrtxml.hxx" +#include <strings.hrc> + +#include <comphelper/documentconstants.hxx> +#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +SwXMLWriter::SwXMLWriter( const OUString& rBaseURL ) +{ + SetBaseURL( rBaseURL ); +} + +SwXMLWriter::~SwXMLWriter() +{ +} + +ErrCode SwXMLWriter::Write_( const uno::Reference < task::XStatusIndicator >& xStatusIndicator, + const OUString& aDocHierarchicalName ) +{ + // Get service factory + uno::Reference< uno::XComponentContext > xContext = + comphelper::getProcessComponentContext(); + + // Get data sink ... + uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler; + rtl::Reference<SvXMLGraphicHelper> xGraphicHelper ; + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper; + + OSL_ENSURE( m_xStg.is(), "Where is my storage?" ); + xGraphicHelper = SvXMLGraphicHelper::Create( m_xStg, + SvXMLGraphicHelperMode::Write ); + xGraphicStorageHandler = xGraphicHelper.get(); + + SfxObjectShell *pPersist = m_pDoc->GetPersist(); + if( pPersist ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + m_xStg, *pPersist, + SvXMLEmbeddedObjectHelperMode::Write ); + xObjectResolver = xObjectHelper.get(); + } + + // create and prepare the XPropertySet that gets passed through + // the components, and the XStatusIndicator that shows progress to + // the user. + + // create XPropertySet with three properties for status indicator + comphelper::PropertyMapEntry const aInfoMap[] = + { + { OUString("ProgressRange"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, + ::cppu::UnoType<sal_Int32>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("WrittenNumberStyles"), 0, + cppu::UnoType<uno::Sequence<sal_Int32>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("UsePrettyPrinting"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ShowChanges"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("RedlineProtectionKey"), 0, + cppu::UnoType<Sequence<sal_Int8>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, + ::cppu::UnoType<OUString>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("AutoTextMode"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleNames"), 0, + cppu::UnoType<Sequence<OUString>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleFamilies"), 0, + cppu::UnoType<Sequence<sal_Int32>>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + // #i69627# + { OUString("OutlineStyleAsNormalListStyle"), 0, + cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TargetStorage"),0, cppu::UnoType<embed::XStorage>::get(), + css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString(), 0, css::uno::Type(), 0, 0 } + }; + uno::Reference< beans::XPropertySet > xInfoSet( + comphelper::GenericPropertySet_CreateInstance( + new comphelper::PropertySetInfo( aInfoMap ) ) ); + + xInfoSet->setPropertyValue( "TargetStorage", Any( m_xStg ) ); + + if (m_bShowProgress) + { + // set progress range and start status indicator + sal_Int32 nProgressRange(1000000); + if (xStatusIndicator.is()) + { + xStatusIndicator->start(SwResId( STR_STATSTR_SWGWRITE), + nProgressRange); + } + xInfoSet->setPropertyValue("ProgressRange", Any(nProgressRange)); + + xInfoSet->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32 >( -1 ))); + } + + SvtSaveOptions aSaveOpt; + xInfoSet->setPropertyValue( "UsePrettyPrinting", makeAny(aSaveOpt.IsPrettyPrinting()) ); + + // save show redline mode ... + const OUString sShowChanges("ShowChanges"); + RedlineFlags const nOrigRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + RedlineFlags nRedlineFlags(nOrigRedlineFlags); + bool isShowChanges; + // TODO: ideally this would be stored per-view... + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + isShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); + xInfoSet->setPropertyValue(sShowChanges, makeAny(isShowChanges)); + // ... and hide redlines for export + nRedlineFlags &= ~RedlineFlags::ShowMask; + nRedlineFlags |= RedlineFlags::ShowInsert; + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + + // Set base URI + xInfoSet->setPropertyValue( "BaseURI", makeAny( GetBaseURL() ) ); + + if( SfxObjectCreateMode::EMBEDDED == m_pDoc->GetDocShell()->GetCreateMode() ) + { + const OUString aName( !aDocHierarchicalName.isEmpty() + ? aDocHierarchicalName + : OUString( "dummyObjectName" ) ); + + xInfoSet->setPropertyValue( "StreamRelPath", makeAny( aName ) ); + } + + if( m_bBlock ) + { + xInfoSet->setPropertyValue( "AutoTextMode", makeAny(true) ); + } + + // #i69627# + const bool bOASIS = ( SotStorage::GetVersion( m_xStg ) > SOFFICE_FILEFORMAT_60 ); + if ( bOASIS && + docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *m_pDoc ) ) + { + xInfoSet->setPropertyValue( "OutlineStyleAsNormalListStyle", makeAny( true ) ); + } + + // filter arguments + // - graphics + object resolver for styles + content + // - status indicator + // - info property set + // - else empty + sal_Int32 nArgs = 1; + if( xStatusIndicator.is() ) + nArgs++; + + Sequence < Any > aEmptyArgs( nArgs ); + Any *pArgs = aEmptyArgs.getArray(); + *pArgs++ <<= xInfoSet; + if( xStatusIndicator.is() ) + *pArgs++ <<= xStatusIndicator; + + if( xGraphicStorageHandler.is() ) + nArgs++; + if( xObjectResolver.is() ) + nArgs++; + + Sequence < Any > aFilterArgs( nArgs ); + pArgs = aFilterArgs.getArray(); + *pArgs++ <<= xInfoSet; + if( xGraphicStorageHandler.is() ) + *pArgs++ <<= xGraphicStorageHandler; + if( xObjectResolver.is() ) + *pArgs++ <<= xObjectResolver; + if( xStatusIndicator.is() ) + *pArgs++ <<= xStatusIndicator; + + //Get model + uno::Reference< lang::XComponent > xModelComp = m_pDoc->GetDocShell()->GetModel(); + OSL_ENSURE( xModelComp.is(), "XMLWriter::Write: got no model" ); + if( !xModelComp.is() ) + return ERR_SWG_WRITE_ERROR; + + PutNumFormatFontsInAttrPool(); + PutEditEngFontsInAttrPool(); + + // properties + Sequence < PropertyValue > aProps( m_pOrigFileName ? 1 : 0 ); + if( m_pOrigFileName ) + { + PropertyValue *pProps = aProps.getArray(); + pProps->Name = "FileName"; + pProps->Value <<= *m_pOrigFileName; + } + + // export sub streams for package, else full stream into a file + bool bWarn = false; + + // RDF metadata: export if ODF >= 1.2 + // N.B.: embedded documents have their own manifest.rdf! + if ( bOASIS ) + { + const uno::Reference<beans::XPropertySet> xPropSet(m_xStg, + uno::UNO_QUERY_THROW); + try + { + OUString Version; + // ODF >= 1.2 + if ((xPropSet->getPropertyValue("Version") >>= Version) + && Version != ODFVER_010_TEXT + && Version != ODFVER_011_TEXT) + { + const uno::Reference<rdf::XDocumentMetadataAccess> xDMA( + xModelComp, uno::UNO_QUERY_THROW); + xDMA->storeMetadataToStorage(m_xStg); + } + } + catch (beans::UnknownPropertyException &) + { /* ignore */ } + catch (uno::Exception &) + { + bWarn = true; + } + } + + bool bStoreMeta = ( SfxObjectCreateMode::EMBEDDED != m_pDoc->GetDocShell()->GetCreateMode() ); + if ( !bStoreMeta ) + { + try + { + Reference< frame::XModule > xModule( xModelComp, UNO_QUERY ); + if ( xModule.is() ) + { + const OUString aModuleID = xModule->getIdentifier(); + bStoreMeta = !aModuleID.isEmpty() && + ( aModuleID == "com.sun.star.sdb.FormDesign" || + aModuleID == "com.sun.star.sdb.TextReportDesign" ); + } + } + catch( uno::Exception& ) + {} + } + + OUString sWarnFile; + if( !m_bOrganizerMode && !m_bBlock && bStoreMeta ) + { + if( !WriteThroughComponent( + xModelComp, "meta.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaExporter" + : "com.sun.star.comp.Writer.XMLMetaExporter"), + aEmptyArgs, aProps ) ) + { + bWarn = true; + sWarnFile = "meta.xml"; + } + } + + if( !m_bBlock ) + { + if( !WriteThroughComponent( + xModelComp, "settings.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsExporter" + : "com.sun.star.comp.Writer.XMLSettingsExporter"), + aEmptyArgs, aProps ) ) + { + if( !bWarn ) + { + bWarn = true; + sWarnFile = "settings.xml"; + } + } + } + + bool bErr = false; + + OUString sErrFile; + if( !WriteThroughComponent( + xModelComp, "styles.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesExporter" + : "com.sun.star.comp.Writer.XMLStylesExporter"), + aFilterArgs, aProps ) ) + { + bErr = true; + sErrFile = "styles.xml"; + } + + if( !m_bOrganizerMode && !bErr ) + { + if( !WriteThroughComponent( + xModelComp, "content.xml", xContext, + (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentExporter" + : "com.sun.star.comp.Writer.XMLContentExporter"), + aFilterArgs, aProps ) ) + { + bErr = true; + sErrFile = "content.xml"; + } + } + + if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && m_pDoc->getIDocumentStatistics().GetDocStat().nPage > 1 && + !(m_bOrganizerMode || m_bBlock || bErr || + // sw_redlinehide: disable layout cache for now + m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines())) + { + try + { + uno::Reference < io::XStream > xStm = m_xStg->openStreamElement( "layout-cache", embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + std::unique_ptr<SvStream> pStream = utl::UcbStreamHelper::CreateStream( xStm ); + if( !pStream->GetError() ) + { + uno::Reference < beans::XPropertySet > xSet( xStm, UNO_QUERY ); + uno::Any aAny2; + aAny2 <<= OUString("application/binary"); + xSet->setPropertyValue("MediaType", aAny2 ); + m_pDoc->WriteLayoutCache( *pStream ); + } + } + catch ( uno::Exception& ) + { + } + } + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + + // restore redline mode + nRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + nRedlineFlags &= ~RedlineFlags::ShowMask; + nRedlineFlags |= RedlineFlags::ShowInsert; + nRedlineFlags |= nOrigRedlineFlags & RedlineFlags::ShowMask; + m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + + if (xStatusIndicator.is()) + { + xStatusIndicator->end(); + } + + if( bErr ) + { + if( !sErrFile.isEmpty() ) + return *new StringErrorInfo( ERR_WRITE_ERROR_FILE, sErrFile, + DialogMask::ButtonsOk | DialogMask::MessageError ); + return ERR_SWG_WRITE_ERROR; + } + else if( bWarn ) + { + if( !sWarnFile.isEmpty() ) + return *new StringErrorInfo( WARN_WRITE_ERROR_FILE, sWarnFile, + DialogMask::ButtonsOk | DialogMask::MessageError ); + return WARN_SWG_FEATURES_LOST; + } + + return ERRCODE_NONE; +} + +ErrCode SwXMLWriter::WriteStorage() +{ + return Write_( uno::Reference < task::XStatusIndicator >(), OUString() ); +} + +ErrCode SwXMLWriter::WriteMedium( SfxMedium& aTargetMedium ) +{ + uno::Reference < task::XStatusIndicator > xStatusIndicator; + OUString aName; + const SfxUnoAnyItem* pStatusBarItem = static_cast<const SfxUnoAnyItem*>( + aTargetMedium.GetItemSet()->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) ); + if ( pStatusBarItem ) + pStatusBarItem->GetValue() >>= xStatusIndicator; + const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>( + aTargetMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) ); + if ( pDocHierarchItem ) + aName = pDocHierarchItem->GetValue(); + + return Write_( xStatusIndicator, aName ); +} + +ErrCode SwXMLWriter::Write( SwPaM& rPaM, SfxMedium& rMed, + const OUString* pFileName ) +{ + return IsStgWriter() + ? static_cast<StgWriter *>(this)->Write( rPaM, rMed.GetOutputStorage(), pFileName, &rMed ) + : static_cast<Writer *>(this)->Write( rPaM, *rMed.GetOutStream(), pFileName ); +} + +bool SwXMLWriter::WriteThroughComponent( + const uno::Reference<XComponent> & xComponent, + const char* pStreamName, + const uno::Reference<uno::XComponentContext> & rxContext, + const char* pServiceName, + const Sequence<Any> & rArguments, + const Sequence<beans::PropertyValue> & rMediaDesc ) +{ + OSL_ENSURE( m_xStg.is(), "Need storage!" ); + OSL_ENSURE( nullptr != pStreamName, "Need stream name!" ); + OSL_ENSURE( nullptr != pServiceName, "Need service name!" ); + + SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName ); + // open stream + bool bRet = false; + try + { + const OUString sStreamName = OUString::createFromAscii( pStreamName ); + uno::Reference<io::XStream> xStream = + m_xStg->openStreamElement( sStreamName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + uno::Reference <beans::XPropertySet > xSet( xStream, uno::UNO_QUERY ); + if( !xSet.is() ) + return false; + + xSet->setPropertyValue("MediaType", Any(OUString("text/xml")) ); + + // even plain stream should be encrypted in encrypted documents + xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", makeAny(true) ); + + // set buffer and create outputstream + uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rArguments.hasElements() ) + rArguments.getConstArray()[0] >>= xInfoSet; + OSL_ENSURE( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", makeAny( sStreamName ) ); + } + + // write the stuff + bRet = WriteThroughComponent( + xOutputStream, xComponent, rxContext, + pServiceName, rArguments, rMediaDesc ); + } + catch ( uno::Exception& ) + { + } + + return bRet; + +} + +bool SwXMLWriter::WriteThroughComponent( + const uno::Reference<io::XOutputStream> & xOutputStream, + const uno::Reference<XComponent> & xComponent, + const uno::Reference<XComponentContext> & rxContext, + const char* pServiceName, + const Sequence<Any> & rArguments, + const Sequence<PropertyValue> & rMediaDesc ) +{ + OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" ); + OSL_ENSURE( xComponent.is(), "Need component!" ); + OSL_ENSURE( nullptr != pServiceName, "Need component name!" ); + + // get component + uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext); + SAL_INFO( "sw.filter", "SAX-Writer created" ); + // connect XML writer to output stream + xSaxWriter->setOutputStream( xOutputStream ); + + // prepare arguments (prepend doc handler to given arguments) + Sequence<Any> aArgs( 1 + rArguments.getLength() ); + aArgs[0] <<= xSaxWriter; + std::copy(rArguments.begin(), rArguments.end(), std::next(aArgs.begin())); + + // get filter component + uno::Reference< document::XExporter > xExporter( + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( + OUString::createFromAscii(pServiceName), aArgs, rxContext), UNO_QUERY); + OSL_ENSURE( xExporter.is(), + "can't instantiate export filter component" ); + if( !xExporter.is() ) + return false; + SAL_INFO( "sw.filter", pServiceName << " instantiated." ); + // connect model and filter + xExporter->setSourceDocument( xComponent ); + + // filter! + SAL_INFO( "sw.filter", "call filter()" ); + uno::Reference<XFilter> xFilter( xExporter, UNO_QUERY ); + return xFilter->filter( rMediaDesc ); +} + +void GetXMLWriter( const OUString& /*rName*/, const OUString& rBaseURL, WriterRef& xRet ) +{ + xRet = new SwXMLWriter( rBaseURL ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/wrtxml.hxx b/sw/source/filter/xml/wrtxml.hxx new file mode 100644 index 000000000..c82afcc97 --- /dev/null +++ b/sw/source/filter/xml/wrtxml.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_FILTER_XML_WRTXML_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX + +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <shellio.hxx> + +class SwPaM; +class SfxMedium; + +namespace com::sun::star { + namespace uno { template<class A> class Reference; } + namespace uno { template<class A> class Sequence; } + namespace uno { class Any; } + namespace lang { class XComponent; } + namespace lang { class XMultiServiceFactory; } + namespace beans { struct PropertyValue; } +} + +class SwXMLWriter : public StgWriter +{ + ErrCode Write_( const css::uno::Reference < css::task::XStatusIndicator>&, const OUString& ); + + using StgWriter::Write; + +protected: + virtual ErrCode WriteStorage() override; + virtual ErrCode WriteMedium( SfxMedium& aTargetMedium ) override; + +public: + + SwXMLWriter( const OUString& rBaseURL ); + virtual ~SwXMLWriter() override; + + virtual ErrCode Write( SwPaM&, SfxMedium&, const OUString* ) override; + +private: + + // helper methods to write XML streams + + // write a single XML stream into the package + bool WriteThroughComponent( + // the component we export + const css::uno::Reference<css::lang::XComponent> & xComponent, + const char* pStreamName, // the stream name + // service factory for pServiceName + const css::uno::Reference<css::uno::XComponentContext> & rFactory, + const char* pServiceName, // service name of the component + // the argument (XInitialization) + const css::uno::Sequence<css::uno::Any> & rArguments, + // output descriptor + const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc ); + + // write a single output stream + // (to be called either directly or by WriteThroughComponent(...)) + static bool WriteThroughComponent( + const css::uno::Reference<css::io::XOutputStream> & xOutputStream, + const css::uno::Reference<css::lang::XComponent> & xComponent, + const css::uno::Reference<css::uno::XComponentContext> & rFactory, + const char* pServiceName, + const css::uno::Sequence<css::uno::Any> & rArguments, + const css::uno::Sequence<css::beans::PropertyValue> & rMediaDesc ); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_WRTXML_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrsh.cxx b/sw/source/filter/xml/xmlbrsh.cxx new file mode 100644 index 000000000..871240984 --- /dev/null +++ b/sw/source/filter/xml/xmlbrsh.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 <editeng/memberids.h> +#include <vcl/graph.hxx> + +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmltkmap.hxx> +#include <xmloff/XMLBase64ImportContext.hxx> +#include <editeng/brushitem.hxx> +#include <xmloff/xmluconv.hxx> + +#include "xmlbrshi.hxx" +#include "xmlbrshe.hxx" +#include "xmlexp.hxx" +#include "xmlimpit.hxx" +#include "xmlexpit.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +enum SvXMLTokenMapAttrs +{ + XML_TOK_BGIMG_HREF, + XML_TOK_BGIMG_TYPE, + XML_TOK_BGIMG_ACTUATE, + XML_TOK_BGIMG_SHOW, + XML_TOK_BGIMG_POSITION, + XML_TOK_BGIMG_REPEAT, + XML_TOK_BGIMG_FILTER, +}; + +} + +static const SvXMLTokenMapEntry aBGImgAttributesAttrTokenMap[] = +{ + { XML_NAMESPACE_XLINK, XML_HREF, XML_TOK_BGIMG_HREF }, + { XML_NAMESPACE_XLINK, XML_TYPE, XML_TOK_BGIMG_TYPE }, + { XML_NAMESPACE_XLINK, XML_ACTUATE, XML_TOK_BGIMG_ACTUATE }, + { XML_NAMESPACE_XLINK, XML_SHOW, XML_TOK_BGIMG_SHOW }, + { XML_NAMESPACE_STYLE, XML_POSITION, XML_TOK_BGIMG_POSITION }, + { XML_NAMESPACE_STYLE, XML_REPEAT, XML_TOK_BGIMG_REPEAT }, + { XML_NAMESPACE_STYLE, XML_FILTER_NAME, XML_TOK_BGIMG_FILTER }, + XML_TOKEN_MAP_END +}; + + +void SwXMLBrushItemImportContext::ProcessAttrs( + const uno::Reference< xml::sax::XAttributeList >& xAttrList, + const SvXMLUnitConverter& rUnitConv ) +{ + SvXMLTokenMap aTokenMap( aBGImgAttributesAttrTokenMap ); + + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + + switch( aTokenMap.Get( nPrefix, aLocalName ) ) + { + case XML_TOK_BGIMG_HREF: + m_xGraphic = GetImport().loadGraphicByURL(rValue); + break; + case XML_TOK_BGIMG_TYPE: + case XML_TOK_BGIMG_ACTUATE: + case XML_TOK_BGIMG_SHOW: + break; + case XML_TOK_BGIMG_POSITION: + SvXMLImportItemMapper::PutXMLValue( + *pItem, rValue, MID_GRAPHIC_POSITION, rUnitConv ); + break; + case XML_TOK_BGIMG_REPEAT: + SvXMLImportItemMapper::PutXMLValue( + *pItem, rValue, MID_GRAPHIC_REPEAT, rUnitConv ); + break; + case XML_TOK_BGIMG_FILTER: + SvXMLImportItemMapper::PutXMLValue( + *pItem, rValue, MID_GRAPHIC_FILTER, rUnitConv ); + break; + } + } + +} + +SvXMLImportContextRef SwXMLBrushItemImportContext::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + if (xmloff::token::IsXMLToken(rLocalName, xmloff::token::XML_BINARY_DATA)) + { + if (!m_xBase64Stream.is()) + { + m_xBase64Stream = GetImport().GetStreamForGraphicObjectURLFromBase64(); + if (m_xBase64Stream.is()) + pContext = new XMLBase64ImportContext(GetImport(), nPrefix, rLocalName, xAttrList, m_xBase64Stream); + } + } + + return pContext; +} + +void SwXMLBrushItemImportContext::EndElement() +{ + if (m_xBase64Stream.is()) + { + m_xGraphic = GetImport().loadGraphicFromBase64(m_xBase64Stream); + m_xBase64Stream = nullptr; + } + + if (m_xGraphic.is()) + { + Graphic aGraphic(m_xGraphic); + SvxGraphicPosition eOldGraphicPos = pItem->GetGraphicPos(); + pItem->SetGraphic(aGraphic); + if (GPOS_NONE == eOldGraphicPos && GPOS_NONE != pItem->GetGraphicPos()) + pItem->SetGraphicPos(GPOS_TILED); + } + + if (!(pItem->GetGraphic())) + pItem->SetGraphicPos(GPOS_NONE); + else if (GPOS_NONE == pItem->GetGraphicPos()) + pItem->SetGraphicPos(GPOS_TILED); +} + +SwXMLBrushItemImportContext::SwXMLBrushItemImportContext( + SvXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList >& xAttrList, + const SvXMLUnitConverter& rUnitConv, + const SvxBrushItem& rItem ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + pItem( new SvxBrushItem( rItem ) ) +{ + // delete any graphic that is existing + pItem->SetGraphicPos( GPOS_NONE ); + + ProcessAttrs( xAttrList, rUnitConv ); +} + +SwXMLBrushItemImportContext::SwXMLBrushItemImportContext( + SvXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + sal_uInt16 nWhich ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + pItem( new SvxBrushItem( nWhich ) ) +{ + ProcessAttrs( xAttrList, rUnitConv ); +} + +SwXMLBrushItemImportContext::~SwXMLBrushItemImportContext() +{ +} + +SwXMLBrushItemExport::SwXMLBrushItemExport( SwXMLExport& rExp ) : + rExport( rExp ) +{ +} + +SwXMLBrushItemExport::~SwXMLBrushItemExport() +{ +} + +void SwXMLBrushItemExport::exportXML( const SvxBrushItem& rItem ) +{ + GetExport().CheckAttrList(); + + uno::Reference<graphic::XGraphic> xGraphic; + + const Graphic* pGraphic = rItem.GetGraphic(); + + if (pGraphic) + xGraphic = pGraphic->GetXGraphic(); + + if (xGraphic.is()) + { + OUString sMimeType; + OUString sValue = GetExport().AddEmbeddedXGraphic(xGraphic, sMimeType); + if (!sValue.isEmpty()) + { + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sValue ); + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + GetExport().AddAttribute( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } + + const SvXMLUnitConverter& rUnitConv = GetExport().GetTwipUnitConverter(); + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_POSITION, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_POSITION, sValue ); + + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_REPEAT, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_REPEAT, sValue ); + + if (SvXMLExportItemMapper::QueryXMLValue(rItem, sValue, MID_GRAPHIC_FILTER, rUnitConv)) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_FILTER_NAME, sValue ); + } + + { + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE, XML_BACKGROUND_IMAGE, + true, true ); + if (xGraphic.is()) + { + // optional office:binary-data + GetExport().AddEmbeddedXGraphicAsBase64(xGraphic); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrshe.hxx b/sw/source/filter/xml/xmlbrshe.hxx new file mode 100644 index 000000000..1e970ff8e --- /dev/null +++ b/sw/source/filter/xml/xmlbrshe.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_FILTER_XML_XMLBRSHE_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX + +class SvxBrushItem; +class SwXMLExport; + +class SwXMLBrushItemExport +{ + SwXMLExport& rExport; + + SwXMLExport& GetExport() { return rExport; } +public: + + explicit SwXMLBrushItemExport( SwXMLExport& rExport ); + ~SwXMLBrushItemExport(); + + // core API + void exportXML( const SvxBrushItem& rItem ); +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlbrshi.hxx b/sw/source/filter/xml/xmlbrshi.hxx new file mode 100644 index 000000000..7fb735e30 --- /dev/null +++ b/sw/source/filter/xml/xmlbrshi.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLBRSHI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLBRSHI_HXX + +#include <memory> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <xmloff/xmlictxt.hxx> + +class SvXMLImport; +class SvXMLUnitConverter; +class SvxBrushItem; + +namespace com::sun::star { + namespace io { class XOutputStream; } +} + +class SwXMLBrushItemImportContext : public SvXMLImportContext +{ +private: + css::uno::Reference<css::io::XOutputStream> m_xBase64Stream; + css::uno::Reference<css::graphic::XGraphic> m_xGraphic; + + std::unique_ptr<SvxBrushItem> pItem; + + void ProcessAttrs( + const css::uno::Reference<css::xml::sax::XAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv ); + +public: + + SwXMLBrushItemImportContext( + SvXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + const css::uno::Reference<css::xml::sax::XAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + const SvxBrushItem& rItem ); + + SwXMLBrushItemImportContext( + SvXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + const css::uno::Reference<css::xml::sax::XAttributeList > & xAttrList, + const SvXMLUnitConverter& rUnitConv, + sal_uInt16 nWhich ); + + virtual ~SwXMLBrushItemImportContext() override; + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ) override; + + virtual void EndElement() override; + + const SvxBrushItem& GetItem() const { return *pItem; } +}; + +#endif // _XMLBRSHI_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexp.cxx b/sw/source/filter/xml/xmlexp.cxx new file mode 100644 index 000000000..f1e650979 --- /dev/null +++ b/sw/source/filter/xml/xmlexp.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 <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/document/IndexedPropertyValues.hpp> +#include <com/sun/star/xforms/XFormsSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <o3tl/any.hxx> +#include <sax/tools/converter.hxx> +#include <svx/svdpage.hxx> +#include <svx/xmleohlp.hxx> +#include <svx/xmlgrhlp.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svddef.hxx> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <editeng/xmlcnitm.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xformsexport.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <viewsh.hxx> +#include <rootfrm.hxx> +#include <docstat.hxx> +#include <swerror.h> +#include <unotext.hxx> +#include "xmltexte.hxx" +#include "xmlexp.hxx" +#include "xmlexpit.hxx" +#include <comphelper/processfactory.hxx> +#include <docary.hxx> +#include <frameformats.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/svapp.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> + + +#include <pausethreadstarting.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::xforms; +using namespace ::xmloff::token; + +SwXMLExport::SwXMLExport( + const uno::Reference< uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLExportFlags nExportFlags) +: SvXMLExport( util::MeasureUnit::INCH, rContext, implementationName, XML_TEXT, + nExportFlags ), + m_bBlock( false ), + m_bShowProgress( true ), + m_bSavedShowChanges( false ), + m_pDoc( nullptr ) +{ + InitItemExport(); +} + +ErrCode SwXMLExport::exportDoc( enum XMLTokenEnum eClass ) +{ + if( !GetModel().is() ) + return ERR_SWG_WRITE_ERROR; + + SwPauseThreadStarting aPauseThreadStarting; // #i73788# + + // from here, we use core interfaces -> lock Solar-Mutex + SolarMutexGuard aGuard; + + { + Reference<XPropertySet> rInfoSet = getExportInfo(); + if( rInfoSet.is() ) + { + const OUString sAutoTextMode("AutoTextMode"); + if( rInfoSet->getPropertySetInfo()->hasPropertyByName( + sAutoTextMode ) ) + { + Any aAny = rInfoSet->getPropertyValue(sAutoTextMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bBlock = true; + } + } + } + } + + SwDoc *pDoc = getDoc(); + if (!pDoc) + return ERR_SWG_WRITE_ERROR; + + if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES| + SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) + { + if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + GetNamespaceMap_().Add( + GetXMLToken(XML_NP_OFFICE_EXT), + GetXMLToken(XML_N_OFFICE_EXT), + XML_NAMESPACE_OFFICE_EXT); + } + + GetTextParagraphExport()->SetBlockMode( m_bBlock ); + + const SfxItemPool& rPool = pDoc->GetAttrPool(); + sal_uInt16 aWhichIds[5] = { RES_UNKNOWNATR_CONTAINER, + RES_TXTATR_UNKNOWN_CONTAINER, + SDRATTR_XMLATTRIBUTES, + EE_PARA_XMLATTRIBS, + EE_CHAR_XMLATTRIBS }; + + const int nWhichIds = rPool.GetSecondaryPool() ? 5 : 2; + for( int j=0; j < nWhichIds; ++j ) + { + const sal_uInt16 nWhichId = aWhichIds[j]; + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + auto pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>( pItem ); + OSL_ENSURE( pUnknown, "illegal attribute container item" ); + if( pUnknown && (pUnknown->GetAttrCount() > 0) ) + { + sal_uInt16 nIdx = pUnknown->GetFirstNamespaceIndex(); + while( USHRT_MAX != nIdx ) + { + GetNamespaceMap_().Add( pUnknown->GetPrefix( nIdx ), + pUnknown->GetNamespace( nIdx ) ); + nIdx = pUnknown->GetNextNamespaceIndex( nIdx ); + } + } + } + } + } + + sal_uInt16 const eUnit = SvXMLUnitConverter::GetMeasureUnit( + SW_MOD()->GetMetric(pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))); + if (GetMM100UnitConverter().GetXMLMeasureUnit() != eUnit ) + { + GetMM100UnitConverter().SetXMLMeasureUnit( eUnit ); + m_pTwipUnitConverter->SetXMLMeasureUnit( eUnit ); + } + + if( getExportFlags() & SvXMLExportFlags::META) + { + // Update doc stat, so that correct values are exported and + // the progress works correctly. + pDoc->getIDocumentStatistics().UpdateDocStat( false, true ); + } + if( m_bShowProgress ) + { + ProgressBarHelper *pProgress = GetProgressBarHelper(); + if( -1 == pProgress->GetReference() ) + { + // progress isn't initialized: + // We assume that the whole doc is exported, and the following + // durations: + // - meta information: 2 + // - settings: 4 (TODO: not now!) + // - styles (except page styles): 2 + // - page styles: 2 (TODO: not now!) + 2 for each paragraph + // - paragraph: 2 (1 for automatic styles and one for content) + + // count each item once, and then multiply by two to reach the + // figures given above + // The styles in pDoc also count the default style that never + // gets exported -> subtract one. + sal_Int32 nRef = 1; // meta.xml + nRef += pDoc->GetCharFormats()->size() - 1; + nRef += pDoc->GetFrameFormats()->size() - 1; + nRef += pDoc->GetTextFormatColls()->size() - 1; + nRef *= 2; // for the above styles, xmloff will increment by 2! + // #i93174#: count all paragraphs for the progress bar + nRef += pDoc->getIDocumentStatistics().GetUpdatedDocStat( false, true ).nAllPara; // 1: only content, no autostyle + pProgress->SetReference( nRef ); + pProgress->SetValue( 0 ); + } + } + + if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) + { + //We depend on the correctness of OrdNums. + SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if( pModel ) + pModel->GetPage( 0 )->RecalcObjOrdNums(); + } + + // adjust document class (eClass) + if (pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT)) + { + eClass = XML_TEXT_GLOBAL; + + // additionally, we take care of the save-linked-sections-thingy + mbSaveLinkedSections = pDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS); + } + // MIB: 03/26/04: The Label information is saved in the settings, so + // we don't need it here. + // else: keep default pClass that we received + + rtl::Reference<SvXMLGraphicHelper> xGraphicStorageHandler; + if (!GetGraphicStorageHandler().is()) + { + xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Write, GetImageFilterName()); + SetGraphicStorageHandler(xGraphicStorageHandler.get()); + } + + rtl::Reference<SvXMLEmbeddedObjectHelper> xEmbeddedResolver; + if( !GetEmbeddedResolver().is() ) + { + SfxObjectShell *pPersist = pDoc->GetPersist(); + if( pPersist ) + { + xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create( + *pPersist, + SvXMLEmbeddedObjectHelperMode::Write ); + SetEmbeddedResolver( Reference<XEmbeddedObjectResolver>( xEmbeddedResolver.get() ) ); + } + } + + // set redline mode if we export STYLES or CONTENT, unless redline + // mode is taken care of outside (through info XPropertySet) + bool bSaveRedline = + bool( getExportFlags() & (SvXMLExportFlags::CONTENT|SvXMLExportFlags::STYLES) ); + if( bSaveRedline ) + { + // if the info property set has a ShowChanges property, + // then change tracking is taken care of on the outside, + // so we don't have to! + Reference<XPropertySet> rInfoSet = getExportInfo(); + if( rInfoSet.is() ) + { + bSaveRedline = ! rInfoSet->getPropertySetInfo()->hasPropertyByName( + "ShowChanges" ); + } + } + RedlineFlags nRedlineFlags = RedlineFlags::NONE; + SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); + m_bSavedShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); + if( bSaveRedline ) + { + // now save and switch redline mode + nRedlineFlags = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( + ( nRedlineFlags & RedlineFlags::ShowMask ) | RedlineFlags::ShowInsert ); + } + + ErrCode nRet = SvXMLExport::exportDoc( eClass ); + + // now we can restore the redline mode (if we changed it previously) + if( bSaveRedline ) + { + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); + } + + if (xGraphicStorageHandler) + xGraphicStorageHandler->dispose(); + xGraphicStorageHandler.clear(); + if( xEmbeddedResolver ) + xEmbeddedResolver->dispose(); + xEmbeddedResolver.clear(); + + OSL_ENSURE( !m_pTableLines, "there are table columns infos left" ); + + return nRet; +} + +XMLTextParagraphExport* SwXMLExport::CreateTextParagraphExport() +{ + return new SwXMLTextParagraphExport(*this, *GetAutoStylePool()); +} + +XMLShapeExport* SwXMLExport::CreateShapeExport() +{ + XMLShapeExport* pShapeExport = new XMLShapeExport( *this, XMLTextParagraphExport::CreateShapeExtPropMapper( *this ) ); + Reference < XDrawPageSupplier > xDPS( GetModel(), UNO_QUERY ); + if( xDPS.is() ) + { + Reference < XShapes > xShapes = xDPS->getDrawPage(); + pShapeExport->seekShapes( xShapes ); + } + + return pShapeExport; +} + +SwXMLExport::~SwXMLExport() +{ + DeleteTableLines(); + FinitItemExport(); +} + +void SwXMLExport::ExportFontDecls_() +{ + GetFontAutoStylePool(); // make sure the pool is created + SvXMLExport::ExportFontDecls_(); +} + +void SwXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps) +{ + aProps.realloc(7); + // Currently exporting 9 properties + PropertyValue *pValue = aProps.getArray(); + + Reference < XIndexContainer > xBox = IndexedPropertyValues::create( comphelper::getProcessComponentContext() ); + pValue[0].Name = "Views"; + pValue[0].Value <<= xBox; + + SwDoc *pDoc = getDoc(); + const tools::Rectangle rRect = + pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT ); + bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip; + + OSL_ENSURE( bTwip, "Map unit for visible area is not in TWIPS!" ); + + pValue[1].Name = "ViewAreaTop"; + pValue[1].Value <<= bTwip ? convertTwipToMm100 ( rRect.Top() ) : rRect.Top(); + + pValue[2].Name = "ViewAreaLeft"; + pValue[2].Value <<= bTwip ? convertTwipToMm100 ( rRect.Left() ) : rRect.Left(); + + pValue[3].Name = "ViewAreaWidth"; + pValue[3].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetWidth() ) : rRect.GetWidth(); + + pValue[4].Name = "ViewAreaHeight"; + pValue[4].Value <<= bTwip ? convertTwipToMm100 ( rRect.GetHeight() ) : rRect.GetHeight(); + + // "show redline mode" cannot simply be read from the document + // since it gets changed during execution. If it's in the info + // XPropertySet, we take it from there. + bool bShowRedlineChanges = m_bSavedShowChanges; + Reference<XPropertySet> xInfoSet( getExportInfo() ); + if ( xInfoSet.is() ) + { + const OUString sShowChanges( "ShowChanges" ); + if( xInfoSet->getPropertySetInfo()->hasPropertyByName( sShowChanges ) ) + { + bShowRedlineChanges = *o3tl::doAccess<bool>(xInfoSet-> + getPropertyValue( sShowChanges )); + } + } + + pValue[5].Name = "ShowRedlineChanges"; + pValue[5].Value <<= bShowRedlineChanges; + + pValue[6].Name = "InBrowseMode"; + pValue[6].Value <<= pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE); +} + +void SwXMLExport::GetConfigurationSettings( Sequence < PropertyValue >& rProps) +{ + Reference< XMultiServiceFactory > xFac( GetModel(), UNO_QUERY ); + if( xFac.is() ) + { + Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY ); + if( xProps.is() ) + SvXMLUnitConverter::convertPropertySet( rProps, xProps ); + } +} + +sal_Int32 SwXMLExport::GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings ) +{ + // the only doc-specific settings group we know so far are the XForms settings + uno::Sequence<beans::PropertyValue> aXFormsSettings; + Reference< XFormsSupplier > xXFormsSupp( GetModel(), UNO_QUERY ); + Reference< XNameAccess > xXForms; + if ( xXFormsSupp.is() ) + xXForms = xXFormsSupp->getXForms().get(); + if ( xXForms.is() ) + { + getXFormsSettings( xXForms, aXFormsSettings ); + _out_rSettings.emplace_back( XML_XFORM_MODEL_SETTINGS, aXFormsSettings ); + } + + return aXFormsSettings.getLength() + SvXMLExport::GetDocumentSpecificSettings( _out_rSettings ); +} + +void SwXMLExport::SetBodyAttributes() +{ + // export use of soft page breaks + SwDoc *pDoc = getDoc(); + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && + pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetPageCount() > 1 ) + { + OUStringBuffer sBuffer; + ::sax::Converter::convertBool(sBuffer, true); + AddAttribute(XML_NAMESPACE_TEXT, XML_USE_SOFT_PAGE_BREAKS, + sBuffer.makeStringAndClear()); + } +} + +void SwXMLExport::ExportContent_() +{ + // export forms + Reference<XDrawPageSupplier> xDrawPageSupplier(GetModel(), UNO_QUERY); + if (xDrawPageSupplier.is()) + { + // export only if we actually have elements + Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage(); + if (xPage.is()) + { + // prevent export of form controls which are embedded in mute sections + GetTextParagraphExport()->PreventExportOfControlsInMuteSections( + xPage, GetFormExport() ); + + // #i36597# + if ( xmloff::OFormLayerXMLExport::pageContainsForms( xPage ) || GetFormExport()->documentContainsXForms() ) + { + ::xmloff::OOfficeFormsExport aOfficeForms(*this); + + GetFormExport()->exportXForms(); + + GetFormExport()->seekPage(xPage); + GetFormExport()->exportForms(xPage); + } + } + } + + Reference<XPropertySet> xPropSet(GetModel(), UNO_QUERY); + if (xPropSet.is()) + { + Any aAny = xPropSet->getPropertyValue( "TwoDigitYear" ); + aAny <<= sal_Int16(1930); + + sal_Int16 nYear = 0; + aAny >>= nYear; + if (nYear != 1930 ) + { + AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear)); + SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true); + } + } + + GetTextParagraphExport()->exportTrackedChanges( false ); + GetTextParagraphExport()->exportTextDeclarations(); + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + + GetTextParagraphExport()->exportFramesBoundToPage( m_bShowProgress ); + GetTextParagraphExport()->exportText( xText, m_bShowProgress ); +} + +namespace +{ + class theSwXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXMLExportUnoTunnelId > {}; +} + +const Sequence< sal_Int8 > & SwXMLExport::getUnoTunnelId() throw() +{ + return theSwXMLExportUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXMLExport::getSomething( const Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXMLExport>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return SvXMLExport::getSomething( rId ); +} + +SwDoc* SwXMLExport::getDoc() +{ + if( m_pDoc != nullptr ) + return m_pDoc; + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + if (!xTextDoc) + { + SAL_WARN("sw.filter", "Problem of mismatching filter for export."); + return nullptr; + } + + Reference < XText > xText = xTextDoc->getText(); + Reference<XUnoTunnel> xTextTunnel( xText, UNO_QUERY); + assert( xTextTunnel.is()); + SwXText *pText = reinterpret_cast< SwXText *>( + sal::static_int_cast< sal_IntPtr >( xTextTunnel->getSomething( SwXText::getUnoTunnelId() ))); + assert( pText != nullptr ); + m_pDoc = pText->GetDoc(); + assert( m_pDoc != nullptr ); + return m_pDoc; +} + +const SwDoc* SwXMLExport::getDoc() const +{ + return const_cast< SwXMLExport* >( this )->getDoc(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLExporter", + SvXMLExportFlags::ALL)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLStylesExporter", + SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLContentExporter", + SvXMLExportFlags::SCRIPTS | SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLMetaExporter", + SvXMLExportFlags::META)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLSettingsExporter", + SvXMLExportFlags::SETTINGS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisExporter", + SvXMLExportFlags::ALL | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisStylesExporter", + SvXMLExportFlags::STYLES | SvXMLExportFlags::MASTERSTYLES | SvXMLExportFlags::AUTOSTYLES | + SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisContentExporter", + SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT | SvXMLExportFlags::SCRIPTS | + SvXMLExportFlags::FONTDECLS | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisMetaExporter", + SvXMLExportFlags::META | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLExport(context, "com.sun.star.comp.Writer.XMLOasisSettingsExporter", + SvXMLExportFlags::SETTINGS | SvXMLExportFlags::OASIS)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexp.hxx b/sw/source/filter/xml/xmlexp.hxx new file mode 100644 index 000000000..22e6a4236 --- /dev/null +++ b/sw/source/filter/xml/xmlexp.hxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLEXP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX + +#include <xmloff/xmlexp.hxx> +#include "xmlitmap.hxx" +#include <xmloff/xmltoken.hxx> +#include <vector> + +class SwDoc; +class SwFormat; +class SwFrameFormat; +class SvXMLUnitConverter; +class SvXMLExportItemMapper; +class SvXMLAutoStylePoolP; +class SwTableLine; +class SwTableLines; +class SwTableBox; +class SwXMLTableColumn_Impl; +class SwXMLTableLines_Impl; +class SwXMLTableColumnsSortByWidth_Impl; +class SwXMLTableFrameFormatsSort_Impl; +class SwXMLTableInfo_Impl; +class SwTableNode; +class XMLPropertySetMapper; +class SwXMLTableLines_Impl; + +typedef std::vector< SwXMLTableLines_Impl* > SwXMLTableLinesCache_Impl; + +class SwXMLExport : public SvXMLExport +{ + std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConverter; + std::unique_ptr<SvXMLExportItemMapper> m_pTableItemMapper; + std::unique_ptr<SwXMLTableLinesCache_Impl> m_pTableLines; + + SvXMLItemMapEntriesRef m_xTableItemMap; + SvXMLItemMapEntriesRef m_xTableRowItemMap; + SvXMLItemMapEntriesRef m_xTableCellItemMap; + + bool m_bBlock : 1; // export text block? + bool m_bShowProgress : 1; + bool m_bSavedShowChanges : 1; + + SwDoc* m_pDoc; // cached for getDoc() + + void InitItemExport(); + void FinitItemExport(); + void ExportTableLinesAutoStyles( const SwTableLines& rLines, + sal_uInt32 nAbsWidth, + sal_uInt32 nBaseWidth, + const OUString& rNamePrefix, + SwXMLTableColumnsSortByWidth_Impl& rExpCols, + SwXMLTableFrameFormatsSort_Impl& rExpRows, + SwXMLTableFrameFormatsSort_Impl& rExpCells, + SwXMLTableInfo_Impl& rTableInfo, + bool bTop=false ); + + void ExportFormat( const SwFormat& rFormat, enum ::xmloff::token::XMLTokenEnum eClass ); + void ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth ); + + void ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol ); + void ExportTableBox( const SwTableBox& rBox, sal_uInt32 nColSpan, sal_uInt32 nRowSpan, + SwXMLTableInfo_Impl& rTableInfo ); + void ExportTableLine( const SwTableLine& rLine, + const SwXMLTableLines_Impl& rLines, + SwXMLTableInfo_Impl& rTableInfo ); + void ExportTableLines( const SwTableLines& rLines, + SwXMLTableInfo_Impl& rTableInfo, + sal_uInt32 nHeaderRows = 0 ); + + virtual void ExportMeta_() override; + virtual void ExportFontDecls_() override; + virtual void ExportStyles_( bool bUsed ) override; + virtual void ExportAutoStyles_() override; + virtual void ExportMasterStyles_() override; + virtual void SetBodyAttributes() override; + virtual void ExportContent_() override; + virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; + virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& aProps) override; + virtual sal_Int32 GetDocumentSpecificSettings( std::vector< SettingsGroup >& _out_rSettings ) override; + +private: + void DeleteTableLines(); +protected: + + virtual XMLTextParagraphExport* CreateTextParagraphExport() override; + virtual SvXMLAutoStylePoolP* CreateAutoStylePool() override; + virtual XMLPageExport* CreatePageExport() override; + virtual XMLShapeExport* CreateShapeExport() override; + virtual XMLFontAutoStylePool* CreateFontAutoStylePool() override; + +public: + SwXMLExport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLExportFlags nExportFlags); + + virtual ~SwXMLExport() override; + + void collectAutoStyles() override; + + virtual ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override; + + inline const SvXMLUnitConverter& GetTwipUnitConverter() const; + + void ExportTableAutoStyles( const SwTableNode& rTableNd ); + void ExportTable( const SwTableNode& rTableNd ); + + bool IsShowProgress() const { return m_bShowProgress; } + void SetShowProgress( bool b ) { m_bShowProgress = b; } + + // XUnoTunnel + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() throw(); + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + const SwDoc* getDoc() const; + SwDoc* getDoc(); +}; + +inline const SvXMLUnitConverter& SwXMLExport::GetTwipUnitConverter() const +{ + return *m_pTwipUnitConverter; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexpit.cxx b/sw/source/filter/xml/xmlexpit.cxx new file mode 100644 index 000000000..bbb4e9830 --- /dev/null +++ b/sw/source/filter/xml/xmlexpit.cxx @@ -0,0 +1,1110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "xmlexpit.hxx" + +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sax/tools/converter.hxx> +#include <svl/poolitem.hxx> +#include <svl/itemset.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include <xmloff/attrlist.hxx> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/prhdlfac.hxx> +#include <xmloff/xmltypes.hxx> +#include <editeng/xmlcnitm.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlprhdl.hxx> +#include <editeng/memberids.h> +#include <hintids.hxx> +#include <unomid.h> +#include <svx/unomid.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <fmtpdsc.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> + +#include "xmlithlp.hxx" + +#include <fmtrowsplt.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using uno::Any; + +// fills the given attribute list with the items in the given set +void SvXMLExportItemMapper::exportXML( const SvXMLExport& rExport, + SvXMLAttributeList& rAttrList, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + std::vector<sal_uInt16> *pIndexArray ) const +{ + const sal_uInt16 nCount = mrMapEntries->getCount(); + sal_uInt16 nIndex = 0; + + while( nIndex < nCount ) + { + SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nIndex ); + + // we have a valid map entry here, so lets use it... + if( 0 == (rEntry.nMemberId & MID_SW_FLAG_NO_ITEM_EXPORT) ) + { + const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId ); + // do we have an item? + if(pItem) + { + if( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) ) + { + // element items do not add any properties, + // we export it later + if( pIndexArray ) + pIndexArray->push_back( nIndex ); + + } + else + { + exportXML( rExport, rAttrList, *pItem, rEntry, rUnitConverter, + rNamespaceMap, &rSet ); + } + } + } + else + { + OSL_FAIL( "no item not handled in xml export" ); + } + nIndex++; + } +} + +void SvXMLExportItemMapper::exportXML(const SvXMLExport&, + SvXMLAttributeList& rAttrList, + const SfxPoolItem& rItem, + const SvXMLItemMapEntry& rEntry, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const +{ + if( 0 != (rEntry.nMemberId & MID_SW_FLAG_SPECIAL_ITEM_EXPORT) ) + { + if( dynamic_cast<const SwFormatRowSplit*>( &rItem) != nullptr ) + { + OUString aValue; + bool bAddAttribute = true; + if( rEntry.nNameSpace == XML_NAMESPACE_STYLE ) + { + bAddAttribute = false; + } + else + { + OUStringBuffer aOut; + const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pSplit && "Wrong Which-ID"); + const sal_uInt16 eEnum = (pSplit && pSplit->GetValue()) ? 1 : 0; + SvXMLUnitConverter::convertEnum( aOut, eEnum, aXML_KeepTogetherType ); + aValue = aOut.makeStringAndClear(); + } + if( bAddAttribute ) + { + const OUString sName( rNamespaceMap.GetQNameByKey( rEntry.nNameSpace, + GetXMLToken(rEntry.eLocalName) ) ); + rAttrList.AddAttribute( sName, aValue ); + } + } + + if (const SvXMLAttrContainerItem *pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>(&rItem)) + { + std::unique_ptr<SvXMLNamespaceMap> pNewNamespaceMap; + const SvXMLNamespaceMap *pNamespaceMap = &rNamespaceMap; + + const sal_uInt16 nCount = pUnknown->GetAttrCount(); + for( sal_uInt16 i=0; i < nCount; i++ ) + { + const OUString sPrefix( pUnknown->GetAttrPrefix( i ) ); + if( !sPrefix.isEmpty() ) + { + const OUString sNamespace( pUnknown->GetAttrNamespace( i ) ); + + // if the prefix isn't defined yet or has another meaning, + // we have to redefine it now. + const sal_uInt16 nIdx = pNamespaceMap->GetIndexByPrefix( sPrefix ); + if( USHRT_MAX == nIdx || + pNamespaceMap->GetNameByIndex( nIdx ) != sNamespace ) + { + if( !pNewNamespaceMap ) + { + pNewNamespaceMap.reset( + new SvXMLNamespaceMap( rNamespaceMap )); + pNamespaceMap = pNewNamespaceMap.get(); + } + pNewNamespaceMap->Add( sPrefix, sNamespace ); + + rAttrList.AddAttribute( GetXMLToken(XML_XMLNS) + ":" + sPrefix, + sNamespace ); + } + + rAttrList.AddAttribute( sPrefix + ":" + pUnknown->GetAttrLName(i), + pUnknown->GetAttrValue(i) ); + } + else + { + rAttrList.AddAttribute( pUnknown->GetAttrLName(i), + pUnknown->GetAttrValue(i) ); + } + } + } + else + { + handleSpecialItem( rAttrList, rEntry, rItem, rUnitConverter, + rNamespaceMap, pSet ); + } + } + else if( 0 == (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) ) + { + bool bDone = false; + switch (rItem.Which()) + { + case RES_FRAMEDIR: + { + // Write bt-lr to the extension namespace, handle other values + // below. + auto pDirection = static_cast<const SvxFrameDirectionItem*>(&rItem); + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + && pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT) + { + const OUString sName(rNamespaceMap.GetQNameByKey( + XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE))); + rAttrList.AddAttribute(sName, GetXMLToken(XML_BT_LR)); + } + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + || pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT) + bDone = true; + break; + } + } + + if (!bDone) + { + OUString aValue; + if( QueryXMLValue(rItem, aValue, + static_cast< sal_uInt16 >( + rEntry.nMemberId & MID_SW_FLAG_MASK ), + rUnitConverter ) ) + { + const OUString sName( + rNamespaceMap.GetQNameByKey( rEntry.nNameSpace, + GetXMLToken(rEntry.eLocalName))); + rAttrList.AddAttribute( sName, aValue ); + } + } + } +} + +void SvXMLExportItemMapper::exportElementItems( + SvXMLExport& rExport, + const SfxItemSet &rSet, + const std::vector<sal_uInt16> &rIndexArray ) const +{ + const size_t nCount = rIndexArray.size(); + + bool bItemsExported = false; + for( size_t nIndex = 0; nIndex < nCount; ++nIndex ) + { + const sal_uInt16 nElement = rIndexArray[ nIndex ]; + SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nElement ); + OSL_ENSURE( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT), + "wrong mid flag!" ); + + const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId ); + // do we have an item? + if(pItem) + { + rExport.IgnorableWhitespace(); + handleElementItem( rEntry, *pItem ); + bItemsExported = true; + } + } + + if( bItemsExported ) + rExport.IgnorableWhitespace(); +} + +/** returns the item with the given WhichId from the given ItemSet if its + set +*/ +const SfxPoolItem* SvXMLExportItemMapper::GetItem( const SfxItemSet& rSet, + sal_uInt16 nWhichId) +{ + // first get item from itemset + const SfxPoolItem* pItem; + SfxItemState eState = rSet.GetItemState( nWhichId, false, &pItem ); + + if( SfxItemState::SET == eState ) + { + return pItem; + } + else + { + return nullptr; + } +} + +SvXMLExportItemMapper::SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries ) + : mrMapEntries(std::move(rMapEntries)) +{ +} + +SvXMLExportItemMapper::~SvXMLExportItemMapper() +{ +} + +void SvXMLExportItemMapper::exportXML( SvXMLExport& rExport, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + XMLTokenEnum ePropToken ) const +{ + std::vector<sal_uInt16> aIndexArray; + + exportXML( rExport, rExport.GetAttrList(), rSet, rUnitConverter, + rExport.GetNamespaceMap(), &aIndexArray ); + + if( rExport.GetAttrList().getLength() > 0 || !aIndexArray.empty() ) + { + rExport.IgnorableWhitespace(); + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, ePropToken, + false, false ); + exportElementItems( rExport, rSet, aIndexArray ); + } +} + +/** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */ +void SvXMLExportItemMapper::handleSpecialItem( SvXMLAttributeList& /*rAttrList*/, + const SvXMLItemMapEntry& /*rEntry*/, + const SfxPoolItem& /*rItem*/, + const SvXMLUnitConverter& /*rUnitConverter*/, + const SvXMLNamespaceMap& /*rNamespaceMap*/, + const SfxItemSet* /*pSet*/ /* = NULL */ ) const +{ + OSL_FAIL( "special item not handled in xml export" ); +} + +/** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ +void SvXMLExportItemMapper::handleElementItem( + const SvXMLItemMapEntry& /*rEntry*/, + const SfxPoolItem& /*rItem*/ ) const +{ + OSL_FAIL( "element item not handled in xml export" ); +} + +static bool lcl_isOdfDoubleLine( const SvxBorderLine* pLine ) +{ + bool bIsOdfDouble = false; + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + bIsOdfDouble = true; + break; + default: + break; + } + return bIsOdfDouble; +} + +bool SvXMLExportItemMapper::QueryXMLValue( + const SfxPoolItem& rItem, + OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ) +{ + bool bOk = false; + OUStringBuffer aOut; + + switch ( rItem.Which() ) + { + + case RES_LR_SPACE: + { + const SvxLRSpaceItem& rLRSpace = dynamic_cast<const SvxLRSpaceItem&>(rItem); + + bOk = true; + switch( nMemberId ) + { + case MID_L_MARGIN: + if (rLRSpace.GetPropLeft() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropLeft() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetLeft() ); + } + break; + + case MID_R_MARGIN: + if (rLRSpace.GetPropRight() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropRight() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetRight() ); + } + break; + + case MID_FIRST_AUTO: + if (rLRSpace.IsAutoFirst()) + { + ::sax::Converter::convertBool( + aOut, rLRSpace.IsAutoFirst() ); + } + else + bOk = false; + break; + + case MID_FIRST_LINE_INDENT: + if (!rLRSpace.IsAutoFirst()) + { + if (rLRSpace.GetPropTextFirstLineOffset() != 100) + { + ::sax::Converter::convertPercent( + aOut, rLRSpace.GetPropTextFirstLineOffset() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rLRSpace.GetTextFirstLineOffset() ); + } + } + else + bOk = false; + break; + + default: + OSL_FAIL( "unknown member id!"); + bOk = false; + break; + } + } + break; + + case RES_UL_SPACE: + { + const SvxULSpaceItem& rULSpace = dynamic_cast<const SvxULSpaceItem&>(rItem); + + switch( nMemberId ) + { + case MID_UP_MARGIN: + if (rULSpace.GetPropUpper() != 100) + { + ::sax::Converter::convertPercent( + aOut, rULSpace.GetPropUpper() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rULSpace.GetUpper() ); + } + break; + + case MID_LO_MARGIN: + if (rULSpace.GetPropLower() != 100) + { + ::sax::Converter::convertPercent( + aOut, rULSpace.GetPropLower() ); + } + else + { + rUnitConverter.convertMeasureToXML( + aOut, rULSpace.GetLower() ); + } + break; + + default: + OSL_FAIL("unknown MemberId"); + }; + + bOk = true; + } + break; + + case RES_SHADOW: + { + const SvxShadowItem* pShadow = dynamic_cast<const SvxShadowItem*>( &rItem ); + assert(pShadow && "Wrong Which-ID"); + if (pShadow) + { + sal_Int32 nX = 1, nY = 1; + switch( pShadow->GetLocation() ) + { + case SvxShadowLocation::TopLeft: + nX = -1; + nY = -1; + break; + case SvxShadowLocation::TopRight: + nY = -1; + break; + case SvxShadowLocation::BottomLeft: + nX = -1; + break; + case SvxShadowLocation::BottomRight: + break; + case SvxShadowLocation::NONE: + default: + rValue = GetXMLToken(XML_NONE); + return true; + } + + nX *= pShadow->GetWidth(); + nY *= pShadow->GetWidth(); + + ::sax::Converter::convertColor(aOut, pShadow->GetColor()); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, nX ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, nY ); + + bOk = true; + } + } + break; + + case RES_BOX: + { + const SvxBoxItem* pBox = dynamic_cast<const SvxBoxItem*>( &rItem ); + assert(pBox && "Wrong Which-ID"); + if (pBox) + { + /** + xml -> MemberId + + border-padding ALL_BORDER_PADDING + border-padding-before LEFT_BORDER_PADDING + border-padding-after RIGHT_BORDER_PADDING + border-padding-start TOP_BORDER_PADDING + border-padding-end BOTTOM_BORDER_PADDING + + border ALL_BORDER + border-before LEFT_BORDER + border-after RIGHT_BORDER + border-start TOP_BORDER + border-end BOTTOM_BORDER + + border-line-width ALL_BORDER_LINE_WIDTH + border-line-width-before LEFT_BORDER_LINE_WIDTH + border-line-width-after RIGHT_BORDER_LINE_WIDTH + border-line-width-start TOP_BORDER_LINE_WIDTH + border-line-width-end BOTTOM_BORDER_LINE_WIDTH + */ + + const SvxBorderLine* pLeft = pBox->GetLeft(); + const SvxBorderLine* pRight = pBox->GetRight(); + const SvxBorderLine* pTop = pBox->GetTop(); + const SvxBorderLine* pBottom = pBox->GetBottom(); + const sal_uInt16 nTopDist = pBox->GetDistance( SvxBoxItemLine::TOP ); + const sal_uInt16 nBottomDist = pBox->GetDistance( SvxBoxItemLine::BOTTOM ); + const sal_uInt16 nLeftDist = pBox->GetDistance( SvxBoxItemLine::LEFT ); + const sal_uInt16 nRightDist = pBox->GetDistance( SvxBoxItemLine::RIGHT ); + + // check if we need to export it + switch( nMemberId ) + { + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + case RIGHT_BORDER_PADDING: + case TOP_BORDER_PADDING: + case BOTTOM_BORDER_PADDING: + { + bool bEqual = nLeftDist == nRightDist && + nLeftDist == nTopDist && + nLeftDist == nBottomDist; + // don't export individual paddings if all paddings are equal and + // don't export all padding if some paddings are not equal + if( (bEqual && ALL_BORDER_PADDING != nMemberId) || + (!bEqual && ALL_BORDER_PADDING == nMemberId) ) + return false; + } + break; + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + bool bEqual = ( nullptr == pTop && nullptr == pBottom && + nullptr == pLeft && nullptr == pRight ) || + ( pTop && pBottom && pLeft && pRight && + *pTop == *pBottom && *pTop == *pLeft && + *pTop == *pRight ); + + // don't export individual borders if all are the same and + // don't export all borders if some are not equal + if( (bEqual && ALL_BORDER != nMemberId) || + (!bEqual && ALL_BORDER == nMemberId) ) + return false; + } + break; + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + { + // if no line is set, there is nothing to export + if( !pTop && !pBottom && !pLeft && !pRight ) + return false; + + bool bEqual = nullptr != pTop && + nullptr != pBottom && + nullptr != pLeft && + nullptr != pRight; + + if( bEqual ) + { + const sal_uInt16 nDistance = pTop->GetDistance(); + const sal_uInt16 nInWidth = pTop->GetInWidth(); + const sal_uInt16 nOutWidth = pTop->GetOutWidth(); + const long nWidth = pTop->GetWidth(); + + bEqual = nDistance == pLeft->GetDistance() && + nInWidth == pLeft->GetInWidth() && + nOutWidth == pLeft->GetOutWidth() && + nWidth == pLeft->GetWidth() && + nDistance == pRight->GetDistance() && + nInWidth == pRight->GetInWidth() && + nOutWidth == pRight->GetOutWidth() && + nWidth == pRight->GetWidth() && + nDistance == pBottom->GetDistance() && + nInWidth == pBottom->GetInWidth() && + nOutWidth == pBottom->GetOutWidth() && + nWidth == pBottom->GetWidth(); + } + + switch( nMemberId ) + { + case ALL_BORDER_LINE_WIDTH: + if( !bEqual || pTop->GetDistance() == 0 || + !lcl_isOdfDoubleLine( pTop ) ) + return false; + break; + case LEFT_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pLeft || + 0 == pLeft->GetDistance() || + !lcl_isOdfDoubleLine( pLeft ) ) + return false; + break; + case RIGHT_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pRight || + 0 == pRight->GetDistance() || + !lcl_isOdfDoubleLine( pRight ) ) + return false; + break; + case TOP_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pTop || + 0 == pTop->GetDistance() || + !lcl_isOdfDoubleLine( pTop ) ) + return false; + break; + case BOTTOM_BORDER_LINE_WIDTH: + if( bEqual || nullptr == pBottom || + 0 == pBottom->GetDistance() || + !lcl_isOdfDoubleLine( pBottom ) ) + return false; + break; + } + } + break; + } + + // now export it export + switch( nMemberId ) + { + // padding + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nLeftDist ); + break; + case RIGHT_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nRightDist ); + break; + case TOP_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nTopDist ); + break; + case BOTTOM_BORDER_PADDING: + rUnitConverter.convertMeasureToXML( aOut, nBottomDist ); + break; + + // border + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + const SvxBorderLine* pLine; + switch( nMemberId ) + { + case ALL_BORDER: + case LEFT_BORDER: + pLine = pLeft; + break; + case RIGHT_BORDER: + pLine = pRight; + break; + case TOP_BORDER: + pLine = pTop; + break; + case BOTTOM_BORDER: + pLine = pBottom; + break; + default: + pLine = nullptr; + break; + } + + if( nullptr != pLine ) + { + sal_Int32 nWidth = pLine->GetWidth(); + + enum XMLTokenEnum eStyle = XML_SOLID; + bool bNoBorder = false; + switch (pLine->GetBorderLineStyle()) + { + case SvxBorderLineStyle::SOLID: + eStyle = XML_SOLID; + break; + case SvxBorderLineStyle::DOTTED: + eStyle = XML_DOTTED; + break; + case SvxBorderLineStyle::DASHED: + eStyle = XML_DASHED; + break; + case SvxBorderLineStyle::FINE_DASHED: + eStyle = XML_FINE_DASHED; + break; + case SvxBorderLineStyle::DASH_DOT: + eStyle = XML_DASH_DOT; + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + eStyle = XML_DASH_DOT_DOT; + break; + case SvxBorderLineStyle::DOUBLE_THIN: + eStyle = XML_DOUBLE_THIN; + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + eStyle = XML_DOUBLE; + break; + case SvxBorderLineStyle::EMBOSSED: + eStyle = XML_RIDGE; + break; + case SvxBorderLineStyle::ENGRAVED: + eStyle = XML_GROOVE; + break; + case SvxBorderLineStyle::INSET: + eStyle = XML_INSET; + break; + case SvxBorderLineStyle::OUTSET: + eStyle = XML_OUTSET; + break; + default: + bNoBorder = true; + } + + if ( !bNoBorder ) + { + ::sax::Converter::convertMeasure(aOut, nWidth, + util::MeasureUnit::TWIP, + util::MeasureUnit::POINT); + aOut.append( ' ' ); + aOut.append( GetXMLToken( eStyle ) ); + aOut.append( ' ' ); + ::sax::Converter::convertColor(aOut, + pLine->GetColor()); + } + } + else + { + aOut.append( GetXMLToken(XML_NONE) ); + } + } + break; + + // width + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + const SvxBorderLine* pLine; + switch( nMemberId ) + { + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + pLine = pLeft; + break; + case RIGHT_BORDER_LINE_WIDTH: + pLine = pRight; + break; + case TOP_BORDER_LINE_WIDTH: + pLine = pTop; + break; + case BOTTOM_BORDER_LINE_WIDTH: + pLine = pBottom; + break; + default: + return false; + } + rUnitConverter.convertMeasureToXML( aOut, pLine->GetInWidth() ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, pLine->GetDistance() ); + aOut.append( ' ' ); + rUnitConverter.convertMeasureToXML( aOut, pLine->GetOutWidth() ); + break; + } + bOk = true; + } + } + break; + + case RES_BREAK: + { + const SvxFormatBreakItem& rFormatBreak = dynamic_cast<const SvxFormatBreakItem&>(rItem); + + sal_uInt16 eEnum = 0; + + switch( nMemberId ) + { + case MID_BREAK_BEFORE: + switch (rFormatBreak.GetBreak()) + { + case SvxBreak::ColumnBefore: + eEnum = 1; + break; + case SvxBreak::PageBefore: + eEnum = 2; + break; + case SvxBreak::NONE: + eEnum = 0; + break; + default: + return false; + } + break; + case MID_BREAK_AFTER: + switch (rFormatBreak.GetBreak()) + { + case SvxBreak::ColumnAfter: + eEnum = 1; + break; + case SvxBreak::PageAfter: + eEnum = 2; + break; + case SvxBreak::NONE: + eEnum = 0; + break; + default: + return false; + } + break; + } + + bOk = SvXMLUnitConverter::convertEnum( aOut, eEnum, psXML_BreakType ); + } + break; + + case RES_KEEP: + { + const SvxFormatKeepItem* pFormatKeep = dynamic_cast<const SvxFormatKeepItem*>( &rItem ); + assert(pFormatKeep && "Wrong Which-ID"); + if (pFormatKeep) + { + aOut.append( pFormatKeep->GetValue() + ? GetXMLToken( XML_ALWAYS ) + : GetXMLToken( XML_AUTO ) ); + bOk = true; + } + } + break; + + case RES_BACKGROUND: + { + const SvxBrushItem& rBrush = dynamic_cast<const SvxBrushItem&>(rItem); + + // note: the graphic is only exported if nMemberId equals + // MID_GRAPHIC... + // If not, only the color or transparency is exported + + switch( nMemberId ) + { + case MID_BACK_COLOR: + if ( rBrush.GetColor().GetTransparency() ) + aOut.append( GetXMLToken(XML_TRANSPARENT) ); + else + { + ::sax::Converter::convertColor(aOut, + rBrush.GetColor()); + } + bOk = true; + break; + + case MID_GRAPHIC_POSITION: + switch (rBrush.GetGraphicPos()) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + aOut.append( GetXMLToken(XML_TOP) ); + bOk = true; + break; + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + aOut.append( GetXMLToken(XML_CENTER) ); + bOk = true; + break; + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + aOut.append( GetXMLToken(XML_BOTTOM) ); + bOk = true; + break; + default: + ; + } + + if( bOk ) + { + aOut.append( ' ' ); + + switch (rBrush.GetGraphicPos()) + { + case GPOS_LT: + case GPOS_LB: + case GPOS_LM: + aOut.append( GetXMLToken(XML_LEFT) ); + break; + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + aOut.append( GetXMLToken(XML_CENTER) ); + break; + case GPOS_RM: + case GPOS_RT: + case GPOS_RB: + aOut.append( GetXMLToken(XML_RIGHT) ); + break; + default: + ; + } + } + break; + + case MID_GRAPHIC_REPEAT: + { + SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos(); + if( GPOS_AREA == eGraphicPos ) + { + aOut.append( GetXMLToken(XML_STRETCH) ); + bOk = true; + } + else if( GPOS_NONE != eGraphicPos && GPOS_TILED != eGraphicPos ) + { + aOut.append( GetXMLToken(XML_BACKGROUND_NO_REPEAT) ); + bOk = true; + } + } + break; + + case MID_GRAPHIC_FILTER: + if (rBrush.GetGraphicPos() != GPOS_NONE && + !rBrush.GetGraphicFilter().isEmpty()) + { + aOut.append(rBrush.GetGraphicFilter()); + bOk = true; + } + break; + } + } + break; + + case RES_PAGEDESC: + { + const SwFormatPageDesc& rPageDesc = dynamic_cast<const SwFormatPageDesc&>(rItem); + + if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId ) + { + ::std::optional<sal_uInt16> oNumOffset = rPageDesc.GetNumOffset(); + if (oNumOffset && *oNumOffset > 0) + { + // #i114163# positiveInteger only! + sal_Int32 const number(*oNumOffset); + aOut.append(number); + } + else + { + aOut.append(GetXMLToken(XML_AUTO)); + } + bOk = true; + } + } + break; + + case RES_LAYOUT_SPLIT: + case RES_ROW_SPLIT: + { + const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pSplit && "Wrong Which-ID"); + if (pSplit) + { + ::sax::Converter::convertBool( aOut, pSplit->GetValue() ); + bOk = true; + } + } + break; + + case RES_HORI_ORIENT: + { + const SwFormatHoriOrient* pHoriOrient = dynamic_cast<const SwFormatHoriOrient*>( &rItem ); + assert(pHoriOrient && "Wrong Which-ID"); + if (pHoriOrient) + { + SvXMLUnitConverter::convertEnum( aOut, pHoriOrient->GetHoriOrient(), + aXMLTableAlignMap ); + bOk = true; + } + } + break; + + case RES_VERT_ORIENT: + { + const SwFormatVertOrient* pVertOrient = dynamic_cast<const SwFormatVertOrient*>( &rItem ); + assert(pVertOrient && "Wrong Which-ID"); + + SvXMLUnitConverter::convertEnum( aOut, pVertOrient->GetVertOrient(), + aXMLTableVAlignMap ); + bOk = true; + } + break; + + case RES_FRM_SIZE: + { + const SwFormatFrameSize& rFrameSize = dynamic_cast<const SwFormatFrameSize&>(rItem); + + bool bOutHeight = false; + switch( nMemberId ) + { + case MID_FRMSIZE_REL_WIDTH: + if (rFrameSize.GetWidthPercent()) + { + ::sax::Converter::convertPercent( + aOut, rFrameSize.GetWidthPercent() ); + bOk = true; + } + break; + case MID_FRMSIZE_MIN_HEIGHT: + if( SwFrameSize::Minimum == rFrameSize.GetHeightSizeType() ) + bOutHeight = true; + break; + case MID_FRMSIZE_FIX_HEIGHT: + if( SwFrameSize::Fixed == rFrameSize.GetHeightSizeType() ) + bOutHeight = true; + break; + } + + if( bOutHeight ) + { + rUnitConverter.convertMeasureToXML(aOut, rFrameSize.GetHeight()); + bOk = true; + } + } + break; + + case RES_FRAMEDIR: + { + Any aAny; + bOk = rItem.QueryValue( aAny ); + if( bOk ) + { + std::unique_ptr<XMLPropertyHandler> pWritingModeHandler = + XMLPropertyHandlerFactory::CreatePropertyHandler( + XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT ); + OUString sValue; + bOk = pWritingModeHandler->exportXML( sValue, aAny, + rUnitConverter ); + if( bOk ) + aOut.append( sValue ); + } + } + break; + + case RES_COLLAPSING_BORDERS: + { + const SfxBoolItem* pBorders = dynamic_cast<const SfxBoolItem*>( &rItem ); + assert(pBorders && "Wrong RES-ID"); + if (pBorders) + { + aOut.append( pBorders->GetValue() + ? GetXMLToken( XML_COLLAPSING ) + : GetXMLToken( XML_SEPARATING ) ); + bOk = true; + } + } + break; + + default: + OSL_FAIL("GetXMLValue not implemented for this item."); + break; + } + + if ( bOk ) + rValue = aOut.makeStringAndClear(); + + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlexpit.hxx b/sw/source/filter/xml/xmlexpit.hxx new file mode 100644 index 000000000..7639dd063 --- /dev/null +++ b/sw/source/filter/xml/xmlexpit.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_FILTER_XML_XMLEXPIT_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX + +#include <xmloff/xmlexppr.hxx> +#include "xmlitmap.hxx" +#include <vector> + +class SvXMLUnitConverter; +class SfxPoolItem; +class SfxItemSet; +class SvXMLAttributeList; +class SvXMLNamespaceMap; +class SvXMLExport; + +class SvXMLExportItemMapper +{ +protected: + SvXMLItemMapEntriesRef mrMapEntries; + + /** fills the given attribute list with the items in the given set */ + void exportXML( const SvXMLExport& rExport, + SvXMLAttributeList& rAttrList, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + std::vector<sal_uInt16> *pIndexArray ) const; + + void exportXML( const SvXMLExport& rExport, + SvXMLAttributeList& rAttrList, + const SfxPoolItem& rItem, + const SvXMLItemMapEntry &rEntry, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const; + + void exportElementItems( SvXMLExport& rExport, + const SfxItemSet &rSet, + const std::vector<sal_uInt16> &rIndexArray ) const; + + static const SfxPoolItem* GetItem( const SfxItemSet &rSet, + sal_uInt16 nWhichId ); + +public: + explicit SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries ); + virtual ~SvXMLExportItemMapper(); + + void exportXML( SvXMLExport& rExport, + const SfxItemSet& rSet, + const SvXMLUnitConverter& rUnitConverter, + ::xmloff::token::XMLTokenEnum ePropToken ) const; + + /** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */ + virtual void handleSpecialItem( SvXMLAttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const; + + /** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ + virtual void handleElementItem( const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const; + + inline void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ); + + static bool QueryXMLValue( const SfxPoolItem& rItem, + OUString& rValue, sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ); +}; + +inline void +SvXMLExportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) +{ + mrMapEntries = rMapEntries; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLEXPIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfmt.cxx b/sw/source/filter/xml/xmlfmt.cxx new file mode 100644 index 000000000..8bf022141 --- /dev/null +++ b/sw/source/filter/xml/xmlfmt.cxx @@ -0,0 +1,1135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/ustrbuf.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> +#include <xmloff/nmspmap.hxx> +#include <fmtcol.hxx> +#include <hints.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <unoprnms.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlnumfi.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/txtstyli.hxx> +#include <xmloff/txtimp.hxx> +#include <xmloff/families.hxx> +#include <xmloff/XMLTextMasterStylesContext.hxx> +#include <xmloff/XMLTextShapeStyleContext.hxx> +#include <xmloff/XMLGraphicsDefaultStyle.hxx> +#include <xmloff/XMLDrawingPageStyleContext.hxx> +#include <xmloff/XMLTextMasterPageContext.hxx> +#include <xmloff/table/XMLTableImport.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include "xmlimp.hxx" +#include <cellatr.hxx> +#include <SwStyleNameMapper.hxx> +#include <xmloff/attrlist.hxx> +#include <ccoll.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +class SwXMLConditionParser_Impl +{ + OUString sInput; + + Master_CollCondition nCondition; + sal_uInt32 nSubCondition; + + sal_Int32 nPos; + sal_Int32 nLength; + + inline bool SkipWS(); + inline bool MatchChar( sal_Unicode c ); + inline bool MatchName( OUString& rName ); + inline bool MatchNumber( sal_uInt32& rNumber ); + +public: + + explicit SwXMLConditionParser_Impl( const OUString& rInp ); + + bool IsValid() const { return Master_CollCondition::NONE != nCondition; } + + Master_CollCondition GetCondition() const { return nCondition; } + sal_uInt32 GetSubCondition() const { return nSubCondition; } +}; + +} + +inline bool SwXMLConditionParser_Impl::SkipWS() +{ + while( nPos < nLength && ' ' == sInput[nPos] ) + nPos++; + return true; +} + +inline bool SwXMLConditionParser_Impl::MatchChar( sal_Unicode c ) +{ + bool bRet = false; + if( nPos < nLength && c == sInput[nPos] ) + { + nPos++; + bRet = true; + } + return bRet; +} + +inline bool SwXMLConditionParser_Impl::MatchName( OUString& rName ) +{ + OUStringBuffer sBuffer( nLength ); + while( nPos < nLength && + ( ('a' <= sInput[nPos] && sInput[nPos] <= 'z') || + '-' == sInput[nPos] ) ) + { + sBuffer.append( sInput[nPos] ); + nPos++; + } + rName = sBuffer.makeStringAndClear(); + return !rName.isEmpty(); +} + +inline bool SwXMLConditionParser_Impl::MatchNumber( sal_uInt32& rNumber ) +{ + OUStringBuffer sBuffer( nLength ); + while( nPos < nLength && '0' <= sInput[nPos] && sInput[nPos] <= '9' ) + { + sBuffer.append( sInput[nPos] ); + nPos++; + } + + OUString sNum( sBuffer.makeStringAndClear() ); + if( !sNum.isEmpty() ) + rNumber = sNum.toInt32(); + return !sNum.isEmpty(); +} + +SwXMLConditionParser_Impl::SwXMLConditionParser_Impl( const OUString& rInp ) : + sInput( rInp ), + nCondition( Master_CollCondition::NONE ), + nSubCondition( 0 ), + nPos( 0 ), + nLength( rInp.getLength() ) +{ + OUString sFunc; + bool bHasSub = false; + sal_uInt32 nSub = 0; + bool bOK = SkipWS() && MatchName( sFunc ) && SkipWS() && + MatchChar( '(' ) && SkipWS() && MatchChar( ')' ) && SkipWS(); + if( bOK && MatchChar( '=' ) ) + { + bOK = SkipWS() && MatchNumber( nSub ) && SkipWS(); + bHasSub = true; + } + + bOK &= nPos == nLength; + + if( bOK ) + { + if( IsXMLToken( sFunc, XML_ENDNOTE ) && !bHasSub ) + nCondition = Master_CollCondition::PARA_IN_ENDNOTE; + else if( IsXMLToken( sFunc, XML_FOOTER ) && !bHasSub ) + nCondition = Master_CollCondition::PARA_IN_FOOTER; + else if( IsXMLToken( sFunc, XML_FOOTNOTE ) && !bHasSub ) + nCondition = Master_CollCondition::PARA_IN_FOOTNOTE; + else if( IsXMLToken( sFunc, XML_HEADER ) && !bHasSub ) + nCondition = Master_CollCondition::PARA_IN_HEADER; + else if( IsXMLToken( sFunc, XML_LIST_LEVEL) && + nSub >=1 && nSub <= MAXLEVEL ) + { + nCondition = Master_CollCondition::PARA_IN_LIST; + nSubCondition = nSub-1; + } + else if( IsXMLToken( sFunc, XML_OUTLINE_LEVEL) && + nSub >=1 && nSub <= MAXLEVEL ) + { + nCondition = Master_CollCondition::PARA_IN_OUTLINE; + nSubCondition = nSub-1; + } + else if( IsXMLToken( sFunc, XML_SECTION ) && !bHasSub ) + { + nCondition = Master_CollCondition::PARA_IN_SECTION; + } + else if( IsXMLToken( sFunc, XML_TABLE ) && !bHasSub ) + { + nCondition = Master_CollCondition::PARA_IN_TABLEBODY; + } + else if( IsXMLToken( sFunc, XML_TABLE_HEADER ) && !bHasSub ) + { + nCondition = Master_CollCondition::PARA_IN_TABLEHEAD; + } + else if( IsXMLToken( sFunc, XML_TEXT_BOX ) && !bHasSub ) + { + nCondition = Master_CollCondition::PARA_IN_FRAME; + } + } +} + +namespace { + +class SwXMLConditionContext_Impl : public SvXMLImportContext +{ + Master_CollCondition nCondition; + sal_uInt32 nSubCondition; + + OUString sApplyStyle; + +public: + + SwXMLConditionContext_Impl( + SvXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ); + + bool IsValid() const { return Master_CollCondition::NONE != nCondition; } + + Master_CollCondition getCondition() const { return nCondition; } + sal_uInt32 getSubCondition() const { return nSubCondition; } + OUString const &getApplyStyle() const { return sApplyStyle; } +}; + +} + +SwXMLConditionContext_Impl::SwXMLConditionContext_Impl( + SvXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + nCondition( Master_CollCondition::NONE ), + nSubCondition( 0 ) +{ + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + + // TODO: use a map here + if( XML_NAMESPACE_STYLE == nPrefix ) + { + if( IsXMLToken( aLocalName, XML_CONDITION ) ) + { + SwXMLConditionParser_Impl aCondParser( rValue ); + if( aCondParser.IsValid() ) + { + nCondition = aCondParser.GetCondition(); + nSubCondition = aCondParser.GetSubCondition(); + } + } + else if( IsXMLToken( aLocalName, XML_APPLY_STYLE_NAME ) ) + { + sApplyStyle = rValue; + } + } + } +} + +typedef std::vector<rtl::Reference<SwXMLConditionContext_Impl>> SwXMLConditions_Impl; + +namespace { + +class SwXMLTextStyleContext_Impl : public XMLTextStyleContext +{ + std::unique_ptr<SwXMLConditions_Impl> pConditions; + uno::Reference < style::XStyle > xNewStyle; + +protected: + + virtual uno::Reference < style::XStyle > Create() override; + virtual void Finish( bool bOverwrite ) override; + +public: + + + SwXMLTextStyleContext_Impl( SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList, + XmlStyleFamily nFamily, + SvXMLStylesContext& rStyles ); + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) override; +}; + +} + +uno::Reference < style::XStyle > SwXMLTextStyleContext_Impl::Create() +{ + + if( pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() ) + { + uno::Reference< lang::XMultiServiceFactory > xFactory( GetImport().GetModel(), + uno::UNO_QUERY ); + if( xFactory.is() ) + { + uno::Reference < uno::XInterface > xIfc = + xFactory->createInstance( "com.sun.star.style.ConditionalParagraphStyle" ); + if( xIfc.is() ) + xNewStyle.set( xIfc, uno::UNO_QUERY ); + } + } + else + { + xNewStyle = XMLTextStyleContext::Create(); + } + + return xNewStyle; +} + +void +SwXMLTextStyleContext_Impl::Finish( bool bOverwrite ) +{ + + if( pConditions && XmlStyleFamily::TEXT_PARAGRAPH == GetFamily() && xNewStyle.is() ) + { + CommandStruct const*const pCommands = SwCondCollItem::GetCmds(); + + Reference< XPropertySet > xPropSet( xNewStyle, UNO_QUERY ); + + uno::Sequence< beans::NamedValue > aSeq( pConditions->size() ); + + for (std::vector<rtl::Reference<SwXMLConditionContext_Impl>>::size_type i = 0; + i < pConditions->size(); ++i) + { + assert((*pConditions)[i]->IsValid()); // checked before inserting + Master_CollCondition nCond = (*pConditions)[i]->getCondition(); + sal_uInt32 nSubCond = (*pConditions)[i]->getSubCondition(); + + for (size_t j = 0; j < COND_COMMAND_COUNT; ++j) + { + if (pCommands[j].nCnd == nCond && + pCommands[j].nSubCond == nSubCond) + { + aSeq[i].Name = GetCommandContextByIndex( j ); + aSeq[i].Value <<= GetImport().GetStyleDisplayName( + GetFamily(), (*pConditions)[i]->getApplyStyle() ); + break; + } + } + } + + try + { + xPropSet->setPropertyValue(UNO_NAME_PARA_STYLE_CONDITIONS, uno::makeAny(aSeq)); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.xml", "exception when setting ParaStyleConditions"); + } + } + XMLTextStyleContext::Finish( bOverwrite ); +} + +SwXMLTextStyleContext_Impl::SwXMLTextStyleContext_Impl( SwXMLImport& rImport, + sal_uInt16 nPrfx, const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList, + XmlStyleFamily nFamily, + SvXMLStylesContext& rStyles ) : + XMLTextStyleContext( rImport, nPrfx, rLName, xAttrList, rStyles, nFamily ) +{ +} + +SvXMLImportContextRef SwXMLTextStyleContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContextRef xContext; + + if( XML_NAMESPACE_STYLE == nPrefix && IsXMLToken( rLocalName, XML_MAP ) ) + { + rtl::Reference<SwXMLConditionContext_Impl> xCond{ + new SwXMLConditionContext_Impl( GetImport(), nPrefix, + rLocalName, xAttrList )}; + if( xCond->IsValid() ) + { + if( !pConditions ) + pConditions = std::make_unique<SwXMLConditions_Impl>(); + pConditions->push_back( xCond ); + } + xContext = xCond.get(); + } + + if (!xContext) + xContext = XMLTextStyleContext::CreateChildContext( nPrefix, rLocalName, + xAttrList ); + + return xContext; +} + +namespace { + +class SwXMLCellStyleContext : public XMLPropStyleContext +{ + OUString m_sDataStyleName; + void AddDataFormat(); +public: + using XMLPropStyleContext::XMLPropStyleContext; + virtual void FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet) override; + virtual void SetAttribute(sal_uInt16 nPrefixKey, const OUString& rLocalName, const OUString& rValue) override; +}; + +class SwXMLItemSetStyleContext_Impl : public SvXMLStyleContext +{ + OUString sMasterPageName; + std::unique_ptr<SfxItemSet> pItemSet; + SwXMLTextStyleContext_Impl *pTextStyle; + SvXMLStylesContext &rStyles; + + OUString sDataStyleName; + + bool bHasMasterPageName : 1; + bool bPageDescConnected : 1; + bool bDataStyleIsResolved; + + SvXMLImportContext *CreateItemSetContext( + sal_uInt16 nPrefix, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList); + +protected: + + virtual void SetAttribute( sal_uInt16 nPrefixKey, + const OUString& rLocalName, + const OUString& rValue ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + + SwXMLItemSetStyleContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList, + SvXMLStylesContext& rStylesC, + XmlStyleFamily nFamily); + + virtual void CreateAndInsert( bool bOverwrite ) override; + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) override; + + // The item set may be empty! + SfxItemSet *GetItemSet() { return pItemSet.get(); } + + bool HasMasterPageName() const { return bHasMasterPageName; } + + bool IsPageDescConnected() const { return bPageDescConnected; } + void ConnectPageDesc(); + + bool ResolveDataStyleName(); +}; + +} + +void SwXMLCellStyleContext::AddDataFormat() +{ + if (m_sDataStyleName.isEmpty() || IsDefaultStyle()) + return; + + const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext*>( + GetStyles()->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, m_sDataStyleName, true)); + + if (!pStyle) + { + SAL_WARN("sw.xml", "not possible to get data style " << m_sDataStyleName); + return; + } + + sal_Int32 nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey(); + if (nNumberFormat < 0) + return; + + rtl::Reference<SvXMLImportPropertyMapper> xPropertyMapper(GetStyles()->GetImportPropertyMapper(GetFamily())); + if (!xPropertyMapper.is()) + { + SAL_WARN("sw.xml", "there is no import prop mapper"); + return; + } + + const rtl::Reference<XMLPropertySetMapper>& xPropertySetMapper(xPropertyMapper->getPropertySetMapper()); + sal_Int32 nIndex = xPropertySetMapper->GetEntryIndex(XML_NAMESPACE_STYLE, GetXMLToken(XML_DATA_STYLE_NAME), 0); + if (nIndex < 0) + { + SAL_WARN("sw.xml", "could not find id for " << GetXMLToken(XML_DATA_STYLE_NAME)); + return; + } + + auto aIter = std::find_if(GetProperties().begin(), GetProperties().end(), + [&nIndex](const XMLPropertyState& rProp) { + return rProp.mnIndex == nIndex; + }); + + if (aIter != GetProperties().end()) + aIter->maValue <<= nNumberFormat; + else + GetProperties().push_back(XMLPropertyState(nIndex, makeAny(nNumberFormat))); +} + +void SwXMLCellStyleContext::FillPropertySet(const css::uno::Reference<css::beans::XPropertySet>& rPropSet) +{ + AddDataFormat(); + XMLPropStyleContext::FillPropertySet(rPropSet); +} + +void SwXMLCellStyleContext::SetAttribute(sal_uInt16 nPrefixKey, const OUString& rLocalName, const OUString& rValue) +{ + if (IsXMLToken(rLocalName, XML_DATA_STYLE_NAME)) + m_sDataStyleName = rValue; + else + XMLPropStyleContext::SetAttribute(nPrefixKey, rLocalName, rValue); +} + +void SwXMLItemSetStyleContext_Impl::SetAttribute( sal_uInt16 nPrefixKey, + const OUString& rLocalName, + const OUString& rValue ) +{ + if( XML_NAMESPACE_STYLE == nPrefixKey ) + { + if ( IsXMLToken( rLocalName, XML_MASTER_PAGE_NAME ) ) + { + sMasterPageName = rValue; + bHasMasterPageName = true; + } + else if ( IsXMLToken( rLocalName, XML_DATA_STYLE_NAME ) ) + { + // if we have a valid data style name + if (!rValue.isEmpty()) + { + sDataStyleName = rValue; + bDataStyleIsResolved = false; // needs to be resolved + } + } + else + { + SvXMLStyleContext::SetAttribute( nPrefixKey, rLocalName, rValue ); + } + } + else + { + SvXMLStyleContext::SetAttribute( nPrefixKey, rLocalName, rValue ); + } +} + +SvXMLImportContext *SwXMLItemSetStyleContext_Impl::CreateItemSetContext( + sal_uInt16 nPrefix, const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + OSL_ENSURE( !pItemSet, + "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: item set exists" ); + + SvXMLImportContext *pContext = nullptr; + + SwDoc* pDoc = SwImport::GetDocFromXMLImport( GetSwImport() ); + + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + switch( GetFamily() ) + { + case XmlStyleFamily::TABLE_TABLE: + pItemSet.reset( new SfxItemSet( rItemPool, aTableSetRange ) ); + break; + case XmlStyleFamily::TABLE_COLUMN: + pItemSet.reset( new SfxItemSet( rItemPool, svl::Items<RES_FRM_SIZE, RES_FRM_SIZE>{} ) ); + break; + case XmlStyleFamily::TABLE_ROW: + pItemSet.reset( new SfxItemSet( rItemPool, aTableLineSetRange ) ); + break; + case XmlStyleFamily::TABLE_CELL: + pItemSet.reset( new SfxItemSet( rItemPool, aTableBoxSetRange ) ); + break; + default: + OSL_ENSURE( false, + "SwXMLItemSetStyleContext_Impl::CreateItemSetContext: unknown family" ); + break; + } + if( pItemSet ) + pContext = GetSwImport().CreateTableItemImportContext( + nPrefix, rLName, xAttrList, GetFamily(), + *pItemSet ); + if( !pContext ) + { + pItemSet.reset(); + } + + return pContext; +} + + +SwXMLItemSetStyleContext_Impl::SwXMLItemSetStyleContext_Impl( SwXMLImport& rImport, + sal_uInt16 nPrfx, const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList, + SvXMLStylesContext& rStylesC, + XmlStyleFamily nFamily ) : + SvXMLStyleContext( rImport, nPrfx, rLName, xAttrList, nFamily ), + pTextStyle( nullptr ), + rStyles( rStylesC ), + bHasMasterPageName( false ), + bPageDescConnected( false ), + bDataStyleIsResolved( true ) +{ +} + +void SwXMLItemSetStyleContext_Impl::CreateAndInsert( bool bOverwrite ) +{ + if( pTextStyle ) + pTextStyle->CreateAndInsert( bOverwrite ); +} + +SvXMLImportContextRef SwXMLItemSetStyleContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContextRef xContext; + + if( XML_NAMESPACE_STYLE == nPrefix ) + { + if( IsXMLToken( rLocalName, XML_TABLE_PROPERTIES ) || + IsXMLToken( rLocalName, XML_TABLE_COLUMN_PROPERTIES ) || + IsXMLToken( rLocalName, XML_TABLE_ROW_PROPERTIES ) || + IsXMLToken( rLocalName, XML_TABLE_CELL_PROPERTIES ) ) + { + xContext = CreateItemSetContext( nPrefix, rLocalName, xAttrList ); + } + else if( IsXMLToken( rLocalName, XML_TEXT_PROPERTIES ) || + IsXMLToken( rLocalName, XML_PARAGRAPH_PROPERTIES )) + { + if( !pTextStyle ) + { + SvXMLAttributeList *pTmp = new SvXMLAttributeList; + const OUString aStr = GetImport().GetNamespaceMap().GetQNameByKey( + nPrefix, GetXMLToken(XML_NAME) ); + pTmp->AddAttribute( aStr, GetName() ); + uno::Reference <xml::sax::XAttributeList> xTmpAttrList = pTmp; + pTextStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), nPrefix, + rLocalName, xTmpAttrList, XmlStyleFamily::TEXT_PARAGRAPH, rStyles ); + pTextStyle->StartElement( xTmpAttrList ); + rStyles.AddStyle( *pTextStyle ); + } + xContext = pTextStyle->CreateChildContext( nPrefix, rLocalName, xAttrList ); + } + } + + if (!xContext) + xContext = SvXMLStyleContext::CreateChildContext( nPrefix, rLocalName, + xAttrList ); + + return xContext; +} + +void SwXMLItemSetStyleContext_Impl::ConnectPageDesc() +{ + if( bPageDescConnected || !HasMasterPageName() ) + return; + bPageDescConnected = true; + + SwDoc *pDoc = SwImport::GetDocFromXMLImport( GetSwImport() ); + + // #i40788# - first determine the display name of the page style, + // then map this name to the corresponding user interface name. + OUString sName = GetImport().GetStyleDisplayName( XmlStyleFamily::MASTER_PAGE, + sMasterPageName ); + SwStyleNameMapper::FillUIName( sName, + sName, + SwGetPoolIdFromName::PageDesc); + SwPageDesc *pPageDesc = pDoc->FindPageDesc(sName); + if( !pPageDesc ) + { + // If the page style is a pool style, then we maybe have to create it + // first if it hasn't been used by now. + const sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sName, SwGetPoolIdFromName::PageDesc ); + if( USHRT_MAX != nPoolId ) + pPageDesc = pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false ); + } + + if( !pPageDesc ) + return; + + if( !pItemSet ) + { + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + pItemSet.reset( new SfxItemSet( rItemPool, aTableSetRange ) ); + } + + const SfxPoolItem *pItem; + std::unique_ptr<SwFormatPageDesc> pFormatPageDesc; + if( SfxItemState::SET == pItemSet->GetItemState( RES_PAGEDESC, false, + &pItem ) ) + { + if( static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc() != pPageDesc ) + pFormatPageDesc.reset(new SwFormatPageDesc( *static_cast<const SwFormatPageDesc *>(pItem) )); + } + else + pFormatPageDesc.reset(new SwFormatPageDesc()); + + if( pFormatPageDesc ) + { + pFormatPageDesc->RegisterToPageDesc( *pPageDesc ); + pItemSet->Put( *pFormatPageDesc ); + } +} + +bool SwXMLItemSetStyleContext_Impl::ResolveDataStyleName() +{ + // resolve, if not already done + if (! bDataStyleIsResolved) + { + // get the format key + sal_Int32 nFormat = + GetImport().GetTextImport()->GetDataStyleKey(sDataStyleName); + + // if the key is valid, insert Item into ItemSet + if( -1 != nFormat ) + { + if( !pItemSet ) + { + SwDoc *pDoc = SwImport::GetDocFromXMLImport( GetSwImport() ); + + SfxItemPool& rItemPool = pDoc->GetAttrPool(); + pItemSet.reset( new SfxItemSet( rItemPool, aTableBoxSetRange ) ); + } + SwTableBoxNumFormat aNumFormatItem(nFormat); + pItemSet->Put(aNumFormatItem); + } + + // now resolved + bDataStyleIsResolved = true; + return true; + } + else + { + // was already resolved; nothing to do + return false; + } +} + +namespace { + +class SwXMLStylesContext_Impl : public SvXMLStylesContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + const SwXMLImport& GetSwImport() const + { return static_cast<const SwXMLImport&>(GetImport()); } + +protected: + + virtual SvXMLStyleContext *CreateStyleChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ) override; + + virtual SvXMLStyleContext *CreateStyleStyleChildContext( XmlStyleFamily nFamily, + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) override; + virtual SvXMLStyleContext *CreateDefaultStyleStyleChildContext( + XmlStyleFamily nFamily, sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) override; + // HACK + virtual rtl::Reference < SvXMLImportPropertyMapper > GetImportPropertyMapper( + XmlStyleFamily nFamily ) const override; + + virtual uno::Reference < container::XNameContainer > + GetStylesContainer( XmlStyleFamily nFamily ) const override; + virtual OUString GetServiceName( XmlStyleFamily nFamily ) const override; + // HACK + +public: + + + SwXMLStylesContext_Impl( + SwXMLImport& rImport, + bool bAuto ); + + virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override; + + virtual void EndElement() override; +}; + +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLStyleContext* pContext = nullptr; + + if(nPrefix == XML_NAMESPACE_TABLE && IsXMLToken(rLocalName, XML_TABLE_TEMPLATE)) + { + rtl::Reference<XMLTableImport> xTableImport = GetImport().GetShapeImport()->GetShapeTableImport(); + pContext = xTableImport->CreateTableTemplateContext(nPrefix, rLocalName, xAttrList); + } + if (!pContext) + pContext = SvXMLStylesContext::CreateStyleChildContext(nPrefix, rLocalName, xAttrList); + + return pContext; +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateStyleStyleChildContext( + XmlStyleFamily nFamily, sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLStyleContext *pStyle = nullptr; + + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + pStyle = new SwXMLTextStyleContext_Impl( GetSwImport(), nPrefix, + rLocalName, xAttrList, nFamily, *this ); + break; + case XmlStyleFamily::TABLE_TABLE: + case XmlStyleFamily::TABLE_COLUMN: + case XmlStyleFamily::TABLE_ROW: + case XmlStyleFamily::TABLE_CELL: + // Distinguish real and automatic styles. + if (IsAutomaticStyle()) + pStyle = new SwXMLItemSetStyleContext_Impl(GetSwImport(), nPrefix, rLocalName, xAttrList, *this, nFamily); + else if (nFamily == XmlStyleFamily::TABLE_CELL) // Real cell styles are used for table-template import. + pStyle = new SwXMLCellStyleContext(GetSwImport(), nPrefix, rLocalName, xAttrList, *this, nFamily); + else + SAL_WARN("sw.xml", "Context does not exists for non automatic table, column or row style."); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + // As long as there are no element items, we can use the text + // style class. + pStyle = new XMLTextShapeStyleContext( GetImport(), nPrefix, + rLocalName, xAttrList, *this, nFamily ); + break; + case XmlStyleFamily::SD_DRAWINGPAGE_ID: + pStyle = new XMLDrawingPageStyleContext(GetImport(), nPrefix, rLocalName, + xAttrList, *this, g_MasterPageContextIDs, g_MasterPageFamilies); + break; + default: + pStyle = SvXMLStylesContext::CreateStyleStyleChildContext( nFamily, + nPrefix, + rLocalName, + xAttrList ); + break; + } + + return pStyle; +} + +SvXMLStyleContext *SwXMLStylesContext_Impl::CreateDefaultStyleStyleChildContext( + XmlStyleFamily nFamily, sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLStyleContext *pStyle = nullptr; + + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + case XmlStyleFamily::TABLE_TABLE: + case XmlStyleFamily::TABLE_ROW: + pStyle = new XMLTextStyleContext( GetImport(), nPrefix, rLocalName, + xAttrList, *this, nFamily, + true ); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + // There are no writer specific defaults for graphic styles! + pStyle = new XMLGraphicsDefaultStyle( GetImport(), nPrefix, + rLocalName, xAttrList, *this ); + break; + default: + pStyle = SvXMLStylesContext::CreateDefaultStyleStyleChildContext( nFamily, + nPrefix, + rLocalName, + xAttrList ); + break; + } + + return pStyle; +} + +SwXMLStylesContext_Impl::SwXMLStylesContext_Impl( + SwXMLImport& rImport, + bool bAuto ) : + SvXMLStylesContext( rImport, bAuto ) +{ +} + +bool SwXMLStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const +{ + const SwXMLImport& rSwImport = GetSwImport(); + const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask(); + + bool bIns = true; + switch( nFamily ) + { + case XmlStyleFamily::TEXT_PARAGRAPH: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Para); + break; + case XmlStyleFamily::TEXT_TEXT: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Char); + break; + case XmlStyleFamily::SD_GRAPHICS_ID: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Frame); + break; + case XmlStyleFamily::TEXT_LIST: + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Pseudo); + break; + case XmlStyleFamily::TEXT_OUTLINE: + case XmlStyleFamily::TEXT_FOOTNOTECONFIG: + case XmlStyleFamily::TEXT_ENDNOTECONFIG: + case XmlStyleFamily::TEXT_LINENUMBERINGCONFIG: + case XmlStyleFamily::TEXT_BIBLIOGRAPHYCONFIG: + bIns = !(rSwImport.IsInsertMode() || rSwImport.IsStylesOnlyMode() || + rSwImport.IsBlockMode()); + break; + default: + bIns = SvXMLStylesContext::InsertStyleFamily( nFamily ); + break; + } + + return bIns; +} + +rtl::Reference < SvXMLImportPropertyMapper > SwXMLStylesContext_Impl::GetImportPropertyMapper( + XmlStyleFamily nFamily ) const +{ + rtl::Reference < SvXMLImportPropertyMapper > xMapper; + if( nFamily == XmlStyleFamily::TABLE_TABLE ) + xMapper = XMLTextImportHelper::CreateTableDefaultExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if( nFamily == XmlStyleFamily::TABLE_ROW ) + xMapper = XMLTextImportHelper::CreateTableRowDefaultExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if( nFamily == XmlStyleFamily::TABLE_CELL ) + xMapper = XMLTextImportHelper::CreateTableCellExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>( this )->GetImport() ); + else if (nFamily == XmlStyleFamily::SD_DRAWINGPAGE_ID) + { + xMapper = XMLTextImportHelper::CreateDrawingPageExtPropMapper( + const_cast<SwXMLStylesContext_Impl*>(this)->GetImport()); + } + else + xMapper = SvXMLStylesContext::GetImportPropertyMapper( nFamily ); + return xMapper; +} + +uno::Reference < container::XNameContainer > SwXMLStylesContext_Impl::GetStylesContainer( + XmlStyleFamily nFamily ) const +{ + uno::Reference < container::XNameContainer > xStyles; + if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily ) + xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetFrameStyles(); + else if( XmlStyleFamily::TABLE_CELL == nFamily ) + xStyles = const_cast<SvXMLImport *>(&GetImport())->GetTextImport()->GetCellStyles(); + + if (!xStyles.is()) + xStyles = SvXMLStylesContext::GetStylesContainer( nFamily ); + + return xStyles; +} + +OUString SwXMLStylesContext_Impl::GetServiceName( XmlStyleFamily nFamily ) const +{ + if( XmlStyleFamily::SD_GRAPHICS_ID == nFamily ) + return "com.sun.star.style.FrameStyle"; + else if( XmlStyleFamily::TABLE_CELL == nFamily ) + return "com.sun.star.style.CellStyle"; + + return SvXMLStylesContext::GetServiceName( nFamily ); +} + +void SwXMLStylesContext_Impl::EndElement() +{ + GetSwImport().InsertStyles( IsAutomaticStyle() ); +} + +namespace { + +class SwXMLMasterStylesContext_Impl : public XMLTextMasterStylesContext +{ +protected: + virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + const SwXMLImport& GetSwImport() const + { return static_cast<const SwXMLImport&>(GetImport()); } + +public: + + + SwXMLMasterStylesContext_Impl( SwXMLImport& rImport ); + + virtual void EndElement() override; +}; + +} + +SwXMLMasterStylesContext_Impl::SwXMLMasterStylesContext_Impl( + SwXMLImport& rImport ) : + XMLTextMasterStylesContext( rImport ) +{ +} + +bool SwXMLMasterStylesContext_Impl::InsertStyleFamily( XmlStyleFamily nFamily ) const +{ + bool bIns; + + const SwXMLImport& rSwImport = GetSwImport(); + const SfxStyleFamily nStyleFamilyMask = rSwImport.GetStyleFamilyMask(); + if( XmlStyleFamily::MASTER_PAGE == nFamily ) + bIns = bool(nStyleFamilyMask & SfxStyleFamily::Page); + else + bIns = XMLTextMasterStylesContext::InsertStyleFamily( nFamily ); + + return bIns; +} + +void SwXMLMasterStylesContext_Impl::EndElement() +{ + FinishStyles( !GetSwImport().IsInsertMode() ); + GetSwImport().FinishStyles(); +} + +SvXMLImportContext *SwXMLImport::CreateStylesContext( + bool bAuto ) +{ + SvXMLStylesContext *pContext = new SwXMLStylesContext_Impl( *this, bAuto ); + if( bAuto ) + SetAutoStyles( pContext ); + else + SetStyles( pContext ); + + return pContext; +} + +SvXMLImportContext *SwXMLImport::CreateMasterStylesContext() +{ + SvXMLStylesContext *pContext = + new SwXMLMasterStylesContext_Impl( *this ); + SetMasterStyles( pContext ); + + return pContext; +} + +void SwXMLImport::InsertStyles( bool bAuto ) +{ + if( bAuto && GetAutoStyles() ) + GetAutoStyles()->CopyAutoStylesToDoc(); + if( !bAuto && GetStyles() ) + GetStyles()->CopyStylesToDoc( !IsInsertMode(), false ); +} + +void SwXMLImport::FinishStyles() +{ + if( GetStyles() ) + GetStyles()->FinishStyles( !IsInsertMode() ); +} + +void SwXMLImport::UpdateTextCollConditions( SwDoc *pDoc ) +{ + if( !pDoc ) + pDoc = SwImport::GetDocFromXMLImport( *this ); + + const SwTextFormatColls& rColls = *pDoc->GetTextFormatColls(); + const size_t nCount = rColls.size(); + for( size_t i=0; i < nCount; ++i ) + { + SwTextFormatColl *pColl = rColls[i]; + if( pColl && RES_CONDTXTFMTCOLL == pColl->Which() ) + { + const SwFormatCollConditions& rConditions = + static_cast<const SwConditionTextFormatColl *>(pColl)->GetCondColls(); + bool bSendModify = false; + for( size_t j=0; j < rConditions.size() && !bSendModify; ++j ) + { + const SwCollCondition& rCond = *rConditions[j]; + switch( rCond.GetCondition() ) + { + case Master_CollCondition::PARA_IN_TABLEHEAD: + case Master_CollCondition::PARA_IN_TABLEBODY: + case Master_CollCondition::PARA_IN_FOOTER: + case Master_CollCondition::PARA_IN_HEADER: + bSendModify = true; + break; + default: break; + } + } + if( bSendModify ) + { + SwCondCollCondChg aMsg( pColl ); + pColl->ModifyNotification( &aMsg, &aMsg ); + } + } + } +} + +bool SwXMLImport::FindAutomaticStyle( + XmlStyleFamily nFamily, + const OUString& rName, + const SfxItemSet **ppItemSet ) const +{ + SwXMLItemSetStyleContext_Impl *pStyle = nullptr; + if( GetAutoStyles() ) + { + pStyle = const_cast<SwXMLItemSetStyleContext_Impl*>(dynamic_cast< const SwXMLItemSetStyleContext_Impl* >( + GetAutoStyles()-> + FindStyleChildContext( nFamily, rName, + true ) ) ); + if( pStyle ) + { + if( ppItemSet ) + { + if( XmlStyleFamily::TABLE_TABLE == pStyle->GetFamily() && + pStyle->HasMasterPageName() && + !pStyle->IsPageDescConnected() ) + pStyle->ConnectPageDesc(); + (*ppItemSet) = pStyle->GetItemSet(); + + // resolve data style name late + if( XmlStyleFamily::TABLE_CELL == pStyle->GetFamily() && + pStyle->ResolveDataStyleName() ) + { + (*ppItemSet) = pStyle->GetItemSet(); + } + + } + } + } + + return pStyle != nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfmte.cxx b/sw/source/filter/xml/xmlfmte.cxx new file mode 100644 index 000000000..eb3821654 --- /dev/null +++ b/sw/source/filter/xml/xmlfmte.cxx @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XTextDocument.hpp> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/attrlist.hxx> +#include "xmlexpit.hxx" +#include <xmloff/nmspmap.hxx> +#include <xmloff/XMLTextListAutoStylePool.hxx> +#include <xmloff/XMLTextMasterPageExport.hxx> +#include <xmloff/table/XMLTableExport.hxx> + +#include <xmloff/txtprmap.hxx> +#include <xmloff/xmlaustp.hxx> +#include <xmloff/families.hxx> +#include <xmloff/maptype.hxx> +#include <format.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <cellatr.hxx> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include "xmlexp.hxx" +#include <SwStyleNameMapper.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; +using namespace ::xmloff::token; + +void SwXMLExport::ExportFormat( const SwFormat& rFormat, enum XMLTokenEnum eFamily ) +{ + // <style:style ...> + CheckAttrList(); + + // style:family="..." + OSL_ENSURE( RES_FRMFMT==rFormat.Which(), "frame format expected" ); + if( RES_FRMFMT != rFormat.Which() ) + return; + OSL_ENSURE( eFamily != XML_TOKEN_INVALID, "family must be specified" ); + // style:name="..." + bool bEncoded = false; + AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName( + rFormat.GetName(), &bEncoded ) ); + if( bEncoded ) + AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rFormat.GetName() ); + + if( eFamily != XML_TOKEN_INVALID ) + AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, eFamily ); + +#if OSL_DEBUG_LEVEL > 0 + // style:parent-style-name="..." (if it's not the default only) + const SwFormat* pParent = rFormat.DerivedFrom(); + // Only adopt parent name, if it's not the default + OSL_ENSURE( !pParent || pParent->IsDefault(), "unexpected parent" ); + + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolFormatId(), "pool ids aren't supported" ); + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId(), "help ids aren't supported" ); + OSL_ENSURE( USHRT_MAX == rFormat.GetPoolHelpId() || + UCHAR_MAX == rFormat.GetPoolHlpFileId(), "help file ids aren't supported" ); +#endif + + // style:master-page-name + if( RES_FRMFMT == rFormat.Which() && XML_TABLE == eFamily ) + { + const SfxPoolItem *pItem; + if( SfxItemState::SET == rFormat.GetAttrSet().GetItemState( RES_PAGEDESC, + false, &pItem ) ) + { + OUString sName; + const SwPageDesc *pPageDesc = + static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc(); + if( pPageDesc ) + SwStyleNameMapper::FillProgName( + pPageDesc->GetName(), + sName, + SwGetPoolIdFromName::PageDesc); + AddAttribute( XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, + EncodeStyleName( sName ) ); + } + } + + if( XML_TABLE_CELL == eFamily ) + { + OSL_ENSURE(RES_FRMFMT == rFormat.Which(), "only frame format"); + + const SfxPoolItem *pItem; + if( SfxItemState::SET == + rFormat.GetAttrSet().GetItemState( RES_BOXATR_FORMAT, + false, &pItem ) ) + { + sal_Int32 nFormat = static_cast<sal_Int32>(static_cast<const SwTableBoxNumFormat *>(pItem)->GetValue()); + + if ( (nFormat != -1) && (nFormat != static_cast<sal_Int32>(getSwDefaultTextFormat())) ) + { + // if we have a format, register and then export + // (Careful: here we assume that data styles will be + // written after cell styles) + addDataStyle(nFormat); + OUString sDataStyleName = getDataStyleName(nFormat); + if( !sDataStyleName.isEmpty() ) + AddAttribute( XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, + sDataStyleName ); + } + } + } + + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, + true, true ); + + SvXMLItemMapEntriesRef xItemMap; + XMLTokenEnum ePropToken = XML_TABLE_PROPERTIES; + if( XML_TABLE == eFamily ) + { + xItemMap = m_xTableItemMap; + } + else if( XML_TABLE_ROW == eFamily ) + { + xItemMap = m_xTableRowItemMap; + ePropToken = XML_TABLE_ROW_PROPERTIES; + } + else if( XML_TABLE_CELL == eFamily ) + { + xItemMap = m_xTableCellItemMap; + ePropToken = XML_TABLE_CELL_PROPERTIES; + } + + if( xItemMap.is() ) + { + m_pTableItemMapper->setMapEntries( xItemMap ); + m_pTableItemMapper->exportXML( *this, + rFormat.GetAttrSet(), + GetTwipUnitConverter(), + ePropToken ); + } + } +} + +void SwXMLExport::ExportStyles_( bool bUsed ) +{ + SvXMLExport::ExportStyles_( bUsed ); + + // drawing defaults + GetShapeExport()->ExportGraphicDefaults(); + + GetTextParagraphExport()->exportTextStyles( bUsed + ,IsShowProgress() + ); + collectDataStyles(true); + exportDataStyles(); + GetShapeExport()->GetShapeTableExport()->exportTableStyles(); + //page defaults + GetPageExport()->exportDefaultStyle(); +} + +void SwXMLExport::collectAutoStyles() +{ + SvXMLExport::collectAutoStyles(); + + if (mbAutoStylesCollected) + return; + + // The order in which styles are collected *MUST* be the same as + // the order in which they are exported. Otherwise, caching will + // fail. + if( getExportFlags() & (SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) ) + { + if( !(getExportFlags() & SvXMLExportFlags::CONTENT) ) + { + // only master pages are exported => styles for frames bound + // to frames (but none for frames bound to pages) need to be + // collected. + // TODO: exclude PageBoundFrames on export + } + } + + // exported in _ExportMasterStyles + if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES ) + GetPageExport()->collectAutoStyles( false ); + + + // exported in ExportContent_ + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + // collect form autostyle + // (do this before collectTextAutoStyles, 'cause the shapes need the results of the work + // done by examineForms) + Reference<XDrawPageSupplier> xDrawPageSupplier( GetModel(), UNO_QUERY ); + if (xDrawPageSupplier.is() && GetFormExport().is()) + { + Reference<XDrawPage> xPage = xDrawPageSupplier->getDrawPage(); + if (xPage.is()) + GetFormExport()->examineForms(xPage); + } + + GetTextParagraphExport()->collectTextAutoStylesOptimized( m_bShowProgress ); + } + + mbAutoStylesCollected = true; +} + +void SwXMLExport::ExportAutoStyles_() +{ + collectAutoStyles(); + + // if we don't export styles (i.e. in content stream only, but not + // in single-stream case), then we can save ourselves a bit of + // work and memory by not collecting field masters + if( !(getExportFlags() & SvXMLExportFlags::STYLES) ) + GetTextParagraphExport()->exportUsedDeclarations(); + + // exported in ExportContent_ + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + GetTextParagraphExport()->exportTrackedChanges( true ); + } + + GetTextParagraphExport()->exportTextAutoStyles(); + GetShapeExport()->exportAutoStyles(); + if( getExportFlags() & SvXMLExportFlags::MASTERSTYLES ) + GetPageExport()->exportAutoStyles(); + + // we rely on data styles being written after cell styles in the + // ExportFormat() method; so be careful when changing order. + exportAutoDataStyles(); + + SvXMLExportFlags nContentAutostyles = SvXMLExportFlags::CONTENT | SvXMLExportFlags::AUTOSTYLES; + if ( ( getExportFlags() & nContentAutostyles ) == nContentAutostyles ) + GetFormExport()->exportAutoStyles(); +} + +XMLPageExport* SwXMLExport::CreatePageExport() +{ + return new XMLTextMasterPageExport( *this ); +} + +void SwXMLExport::ExportMasterStyles_() +{ + // export master styles + GetPageExport()->exportMasterStyles( false ); +} + +namespace { + +class SwXMLAutoStylePoolP : public SvXMLAutoStylePoolP +{ + SvXMLExport& rExport; + const OUString sListStyleName; + const OUString sMasterPageName; + +protected: + + virtual void exportStyleAttributes( + SvXMLAttributeList& rAttrList, + XmlStyleFamily nFamily, + const std::vector< XMLPropertyState >& rProperties, + const SvXMLExportPropertyMapper& rPropExp + , const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap + ) const override; +public: + + explicit SwXMLAutoStylePoolP( SvXMLExport& rExport ); +}; + +} + +void SwXMLAutoStylePoolP::exportStyleAttributes( + SvXMLAttributeList& rAttrList, + XmlStyleFamily nFamily, + const std::vector< XMLPropertyState >& rProperties, + const SvXMLExportPropertyMapper& rPropExp + , const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap + ) const +{ + SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap); + + if( XmlStyleFamily::TEXT_PARAGRAPH == nFamily ) + { + for( const auto& rProperty : rProperties ) + { + if (rProperty.mnIndex != -1) // #i26762# + { + switch( rPropExp.getPropertySetMapper()-> + GetEntryContextId( rProperty.mnIndex ) ) + { + case CTF_NUMBERINGSTYLENAME: + { + OUString sStyleName; + rProperty.maValue >>= sStyleName; + // #i70748# - export also empty list styles + if( !sStyleName.isEmpty() ) + { + OUString sTmp = rExport.GetTextParagraphExport()->GetListAutoStylePool().Find( sStyleName ); + if( !sTmp.isEmpty() ) + sStyleName = sTmp; + } + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + sListStyleName, + GetExport().EncodeStyleName( sStyleName ) ); + } + break; + case CTF_PAGEDESCNAME: + { + OUString sStyleName; + rProperty.maValue >>= sStyleName; + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + sMasterPageName, + GetExport().EncodeStyleName( sStyleName ) ); + } + break; + } + } + } + } +} + +SwXMLAutoStylePoolP::SwXMLAutoStylePoolP(SvXMLExport& rExp ) : + SvXMLAutoStylePoolP( rExp ), + rExport( rExp ), + sListStyleName( GetXMLToken( XML_LIST_STYLE_NAME ) ), + sMasterPageName( GetXMLToken( XML_MASTER_PAGE_NAME ) ) +{ +} + +SvXMLAutoStylePoolP* SwXMLExport::CreateAutoStylePool() +{ + return new SwXMLAutoStylePoolP( *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlfonte.cxx b/sw/source/filter/xml/xmlfonte.cxx new file mode 100644 index 000000000..9b90f94fd --- /dev/null +++ b/sw/source/filter/xml/xmlfonte.cxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <xmloff/XMLFontAutoStylePool.hxx> +#include <editeng/fontitem.hxx> +#include <doc.hxx> +#include "xmlexp.hxx" +#include "xmlimp.hxx" +#include <IDocumentSettingAccess.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; + +namespace { + +class SwXMLFontAutoStylePool_Impl: public XMLFontAutoStylePool +{ +public: + SwXMLFontAutoStylePool_Impl(SwXMLExport& rExport, bool bFontEmbedding); +}; + +} + +SwXMLFontAutoStylePool_Impl::SwXMLFontAutoStylePool_Impl(SwXMLExport& _rExport, bool bFontEmbedding) + : XMLFontAutoStylePool(_rExport, bFontEmbedding) +{ + sal_uInt16 const aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT }; + + const SfxItemPool& rPool = _rExport.getDoc()->GetAttrPool(); + for(sal_uInt16 nWhichId : aWhichIds) + { + const SvxFontItem& rFont = + static_cast<const SvxFontItem&>(rPool.GetDefaultItem( nWhichId )); + Add( rFont.GetFamilyName(), rFont.GetStyleName(), + rFont.GetFamily(), rFont.GetPitch(), + rFont.GetCharSet() ); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + auto pFont = static_cast<const SvxFontItem *>(pItem); + Add( pFont->GetFamilyName(), pFont->GetStyleName(), + pFont->GetFamily(), pFont->GetPitch(), + pFont->GetCharSet() ); + } + } + auto const & pDocument = _rExport.getDoc(); + + m_bEmbedUsedOnly = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_USED_FONTS); + m_bEmbedLatinScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS); + m_bEmbedAsianScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS); + m_bEmbedComplexScript = pDocument->getIDocumentSettingAccess().get(DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS); + +} + +XMLFontAutoStylePool* SwXMLExport::CreateFontAutoStylePool() +{ + bool blockFontEmbedding = false; + // We write font info to both content.xml and styles.xml, but they are both + // written by different SwXMLExport instance, and would therefore write each + // font file twice without complicated checking for duplicates, so handle + // the embedding only in one of them. + if( !( getExportFlags() & SvXMLExportFlags::CONTENT) ) + blockFontEmbedding = true; + if( !getDoc()->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) + blockFontEmbedding = true; + return new SwXMLFontAutoStylePool_Impl( *this, !blockFontEmbedding ); +} + +void SwXMLImport::NotifyEmbeddedFontRead() +{ + getDoc()->getIDocumentSettingAccess().set( DocumentSettingId::EMBED_FONTS, true ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimp.cxx b/sw/source/filter/xml/xmlimp.cxx new file mode 100644 index 000000000..19e7b1323 --- /dev/null +++ b/sw/source/filter/xml/xmlimp.cxx @@ -0,0 +1,1879 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cassert> + +#include <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +#include <o3tl/any.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltkmap.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/txtimp.hxx> +#include <xmloff/XMLTextShapeImportHelper.hxx> +#include <xmloff/XMLFontStylesContext.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentListsAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <TextCursorHelper.hxx> +#include <unotext.hxx> +#include <unotextrange.hxx> +#include <poolfmt.hxx> +#include <ndtxt.hxx> +#include <editsh.hxx> +#include <strings.hrc> +#include <svl/stritem.hxx> +#include "xmlimp.hxx" +#include "xmlimpit.hxx" +#include "xmltexti.hxx" +#include <list.hxx> +#include <swdll.hxx> +#include <xmloff/DocumentSettingsContext.hxx> +#include <docsh.hxx> +#include <svx/xmlgrhlp.hxx> +#include <svx/xmleohlp.hxx> +#include <sfx2/printer.hxx> +#include <xmloff/xmluconv.hxx> +#include <unotools/saveopt.hxx> +#include <unotools/streamwrap.hxx> +#include <tools/helpers.hxx> + +#include <vcl/svapp.hxx> +#include <unotxdoc.hxx> +#include <numrule.hxx> + +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xformsimport.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> + +#include <unordered_set> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::xforms; +using namespace ::xmloff::token; +using namespace ::std; + +namespace { + +enum SwXMLDocTokens +{ + XML_TOK_DOC_FONTDECLS, + XML_TOK_DOC_STYLES, + XML_TOK_DOC_AUTOSTYLES, + XML_TOK_DOC_MASTERSTYLES, + XML_TOK_DOC_META, + XML_TOK_DOC_BODY, + XML_TOK_DOC_SCRIPT, + XML_TOK_DOC_SETTINGS, + XML_TOK_DOC_XFORMS, +}; + +} + +static const SvXMLTokenMapEntry aDocTokenMap[] = +{ + { XML_NAMESPACE_OFFICE, XML_FONT_FACE_DECLS, XML_TOK_DOC_FONTDECLS }, + { XML_NAMESPACE_OFFICE, XML_STYLES, XML_TOK_DOC_STYLES }, + { XML_NAMESPACE_OFFICE, XML_AUTOMATIC_STYLES, XML_TOK_DOC_AUTOSTYLES }, + { XML_NAMESPACE_OFFICE, XML_MASTER_STYLES, XML_TOK_DOC_MASTERSTYLES }, + { XML_NAMESPACE_OFFICE, XML_META, XML_TOK_DOC_META }, + { XML_NAMESPACE_OFFICE, XML_BODY, XML_TOK_DOC_BODY }, + { XML_NAMESPACE_OFFICE, XML_SCRIPTS, XML_TOK_DOC_SCRIPT }, + { XML_NAMESPACE_OFFICE, XML_SETTINGS, XML_TOK_DOC_SETTINGS }, + { XML_NAMESPACE_XFORMS, XML_MODEL, XML_TOK_DOC_XFORMS }, + XML_TOKEN_MAP_END +}; + +namespace { + +class SwXMLBodyContext_Impl : public SvXMLImportContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + SwXMLBodyContext_Impl( SwXMLImport& rImport ); + + virtual void SAL_CALL startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) override {} + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +} + +SwXMLBodyContext_Impl::SwXMLBodyContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ) +{ + // tdf#107211: if at this point we don't have a defined char style "Default" + // or "Default Style", add a mapping for it as it is not written + // into the file since it's not really a style but "no style" + // (hence referencing it actually makes no sense except for hyperlinks + // which default to something other than "Default") + OUString const sDefault(SwResId(STR_POOLCHR_STANDARD)); + uno::Reference<container::XNameContainer> const& xStyles( + rImport.GetTextImport()->GetTextStyles()); + if (!xStyles->hasByName("Default")) + { // this old name was used before LO 4.0 + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default", sDefault); + } + if (!xStyles->hasByName("Default_20_Style")) + { // this new name contains a space which is converted to _20_ on export + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, "Default_20_Style", sDefault); + } + bool isEncoded(false); + OUString const defaultEncoded( + rImport.GetMM100UnitConverter().encodeStyleName(sDefault, &isEncoded)); + if (isEncoded && defaultEncoded != "Default_20_Style" + && !xStyles->hasByName(defaultEncoded)) + { // new name may contain a space which is converted to _20_ on export + rImport.AddStyleDisplayName(XmlStyleFamily::TEXT_TEXT, defaultEncoded, sDefault); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLBodyContext_Impl::createFastChildContext( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + return GetSwImport().CreateBodyContentContext(); +} + +namespace { + +// #i69629# +// enhance class <SwXMLDocContext_Impl> in order to be able to create subclasses +// NB: virtually inherit so we can multiply inherit properly +// in SwXMLOfficeDocContext_Impl +class SwXMLDocContext_Impl : public virtual SvXMLImportContext +{ + +protected: // #i69629# + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + SwXMLDocContext_Impl( SwXMLImport& rImport ); + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} +}; + +} + +SwXMLDocContext_Impl::SwXMLDocContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLDocContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + switch (nElement) + { + case XML_ELEMENT(OFFICE, XML_SCRIPTS): + return GetSwImport().CreateScriptContext(); + break; + case XML_ELEMENT(OFFICE, XML_SETTINGS): + return new XMLDocumentSettingsContext( GetImport() ); + break; + case XML_ELEMENT(OFFICE, XML_STYLES): + GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP ); + return GetSwImport().CreateStylesContext( false ); + break; + case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES): + // don't use the autostyles from the styles-document for the progress + if ( !IsPrefixFilledIn() || ! IsXMLToken( GetLocalName(), XML_DOCUMENT_STYLES ) ) + GetSwImport().GetProgressBarHelper()->Increment + ( PROGRESS_BAR_STEP ); + return GetSwImport().CreateStylesContext( true ); + break; + case XML_ELEMENT(OFFICE, XML_MASTER_STYLES): + return GetSwImport().CreateMasterStylesContext(); + break; + case XML_ELEMENT(OFFICE, XML_FONT_FACE_DECLS): + return GetSwImport().CreateFontDeclsContext(); + break; + case XML_ELEMENT(OFFICE, XML_META): + OSL_FAIL(" XML_ELEMENT(OFFICE, XML_META): should not have come here, maybe document is invalid?"); + break; + case XML_ELEMENT(OFFICE, XML_BODY): + GetSwImport().GetProgressBarHelper()->Increment( PROGRESS_BAR_STEP ); + return new SwXMLBodyContext_Impl( GetSwImport() ); + break; + } + return nullptr; +} + +SvXMLImportContextRef SwXMLDocContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & /*xAttrList*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + const SvXMLTokenMap& rTokenMap = GetSwImport().GetDocElemTokenMap(); + switch( rTokenMap.Get( nPrefix, rLocalName ) ) + { + case XML_TOK_DOC_XFORMS: + pContext = createXFormsModelContext(GetImport(), nPrefix, rLocalName); + break; + } + + return pContext; +} + +namespace { + +// #i69629# - new subclass <SwXMLOfficeDocContext_Impl> of class <SwXMLDocContext_Impl> +class SwXMLOfficeDocContext_Impl : + public SwXMLDocContext_Impl, public SvXMLMetaDocumentContext +{ +public: + + SwXMLOfficeDocContext_Impl( SwXMLImport& rImport, + const Reference< document::XDocumentProperties >& xDocProps); + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override + { SvXMLMetaDocumentContext::startFastElement(nElement, xAttrList); } + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; +}; + +} + +SwXMLOfficeDocContext_Impl::SwXMLOfficeDocContext_Impl( + SwXMLImport& rImport, + const Reference< document::XDocumentProperties >& xDocProps) : + SvXMLImportContext( rImport ), + SwXMLDocContext_Impl( rImport ), + SvXMLMetaDocumentContext( rImport, xDocProps ) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SwXMLOfficeDocContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + // assign paragraph styles to list levels of outline style after all styles + // are imported and finished. This is the case, when <office:body> starts + // in flat OpenDocument file format. + { + if( nElement == XML_ELEMENT( OFFICE, XML_BODY ) ) + { + GetImport().GetTextImport()->SetOutlineStyles( true ); + } + } + + // behave like meta base class iff we encounter office:meta + if ( nElement == XML_ELEMENT( OFFICE, XML_META ) ) { + return SvXMLMetaDocumentContext::createFastChildContext( + nElement, xAttrList ); + } else { + return SwXMLDocContext_Impl::createFastChildContext( + nElement, xAttrList ); + } +} + +namespace { + +// #i69629# - new subclass <SwXMLDocStylesContext_Impl> of class <SwXMLDocContext_Impl> +class SwXMLDocStylesContext_Impl : public SwXMLDocContext_Impl +{ +public: + + SwXMLDocStylesContext_Impl( SwXMLImport& rImport ); + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SwXMLDocStylesContext_Impl::SwXMLDocStylesContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ), + SwXMLDocContext_Impl( rImport ) +{ +} + +void SwXMLDocStylesContext_Impl::endFastElement(sal_Int32 ) +{ + // assign paragraph styles to list levels of outline style after all styles + // are imported and finished. + SwXMLImport& rSwImport = dynamic_cast<SwXMLImport&>( GetImport()); + GetImport().GetTextImport()->SetOutlineStyles( + bool(rSwImport.GetStyleFamilyMask() & SfxStyleFamily::Para)); +} + +const SvXMLTokenMap& SwXMLImport::GetDocElemTokenMap() +{ + if( !m_pDocElemTokenMap ) + m_pDocElemTokenMap.reset( new SvXMLTokenMap( aDocTokenMap ) ); + + return *m_pDocElemTokenMap; +} + +SvXMLImportContext *SwXMLImport::CreateFastContext( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + switch (nElement) + { + case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ): + pContext = CreateMetaContext(nElement); + break; + case XML_ELEMENT( OFFICE, XML_DOCUMENT ): + { + uno::Reference<document::XDocumentProperties> const xDocProps( + GetDocumentProperties()); + // flat OpenDocument file format + pContext = new SwXMLOfficeDocContext_Impl( *this, xDocProps ); + } + break; + // #i69629# - own subclasses for <office:document> and <office:document-styles> + case XML_ELEMENT(OFFICE, XML_DOCUMENT_SETTINGS): + case XML_ELEMENT(OFFICE, XML_DOCUMENT_CONTENT): + pContext = new SwXMLDocContext_Impl( *this ); + break; + case XML_ELEMENT(OFFICE, XML_DOCUMENT_STYLES): + pContext = new SwXMLDocStylesContext_Impl( *this ); + break; + } + return pContext; +} + +SwXMLImport::SwXMLImport( + const uno::Reference< uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLImportFlags nImportFlags) +: SvXMLImport( rContext, implementationName, nImportFlags ), + m_nStyleFamilyMask( SfxStyleFamily::All ), + m_bLoadDoc( true ), + m_bInsert( false ), + m_bBlock( false ), + m_bOrganizerMode( false ), + m_bInititedXForms( false ), + m_pDoc( nullptr ) +{ + InitItemImport(); +} + +SwXMLImport::~SwXMLImport() throw () +{ + if (HasShapeImport()) + { + SAL_WARN("sw", "endDocument skipped, dropping shapes now to avoid dangling SvTextShapeImportHelper pointing to this"); + ClearShapeImport(); + } + m_pDocElemTokenMap.reset(); + m_pTableElemTokenMap.reset(); + m_pTableCellAttrTokenMap.reset(); + FinitItemImport(); +} + +void SwXMLImport::setTextInsertMode( + const Reference< XTextRange > & rInsertPos ) +{ + m_bInsert = true; + + Reference < XText > xText = rInsertPos->getText(); + Reference < XTextCursor > xTextCursor = + xText->createTextCursorByRange( rInsertPos ); + GetTextImport()->SetCursor( xTextCursor ); +} + +void SwXMLImport::setStyleInsertMode( SfxStyleFamily nFamilies, + bool bOverwrite ) +{ + m_bInsert = !bOverwrite; + m_nStyleFamilyMask = nFamilies; + m_bLoadDoc = false; +} + +namespace +{ + class theSwXMLImportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXMLImportUnoTunnelId > {}; +} + +const Sequence< sal_Int8 > & SwXMLImport::getUnoTunnelId() throw() +{ + return theSwXMLImportUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXMLImport::getSomething( const Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXMLImport>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) ); + } + return SvXMLImport::getSomething( rId ); +} + +static OTextCursorHelper *lcl_xml_GetSwXTextCursor( const Reference < XTextCursor >& rTextCursor ) +{ + Reference<XUnoTunnel> xCursorTunnel( rTextCursor, UNO_QUERY ); + OSL_ENSURE( xCursorTunnel.is(), "missing XUnoTunnel for Cursor" ); + if( !xCursorTunnel.is() ) + return nullptr; + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper *>( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + return pTextCursor; +} + +void SwXMLImport::startDocument() +{ + // delegate to parent + SvXMLImport::startDocument(); + + OSL_ENSURE( GetModel().is(), "model is missing" ); + if( !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + Reference< XPropertySet > xImportInfo( getImportInfo() ); + Reference< XPropertySetInfo > xPropertySetInfo; + if( xImportInfo.is() ) + xPropertySetInfo = xImportInfo->getPropertySetInfo(); + if( xPropertySetInfo.is() ) + { + Any aAny; + // insert style mode? + OUString sStyleInsertModeFamilies("StyleInsertModeFamilies"); + if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeFamilies) ) + { + aAny = xImportInfo->getPropertyValue(sStyleInsertModeFamilies); + Sequence< OUString> aFamiliesSeq; + if( aAny >>= aFamiliesSeq ) + { + SfxStyleFamily nFamilyMask = SfxStyleFamily::None; + for( const OUString& rFamily : std::as_const(aFamiliesSeq) ) + { + if( rFamily=="FrameStyles" ) + nFamilyMask |= SfxStyleFamily::Frame; + else if( rFamily=="PageStyles" ) + nFamilyMask |= SfxStyleFamily::Page; + else if( rFamily=="CharacterStyles" ) + nFamilyMask |= SfxStyleFamily::Char; + else if( rFamily=="ParagraphStyles" ) + nFamilyMask |= SfxStyleFamily::Para; + else if( rFamily=="NumberingStyles" ) + nFamilyMask |= SfxStyleFamily::Pseudo; + } + + bool bOverwrite = false; + const OUString sStyleInsertModeOverwrite("StyleInsertModeOverwrite"); + if( xPropertySetInfo->hasPropertyByName(sStyleInsertModeOverwrite) ) + { + aAny = xImportInfo->getPropertyValue(sStyleInsertModeOverwrite); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + bOverwrite = true; + } + } + + setStyleInsertMode( nFamilyMask, bOverwrite ); + } + } + + // text insert mode? + const OUString sTextInsertModeRange("TextInsertModeRange"); + if( xPropertySetInfo->hasPropertyByName(sTextInsertModeRange) ) + { + aAny = xImportInfo->getPropertyValue(sTextInsertModeRange); + Reference<XTextRange> xInsertTextRange; + if( aAny >>= xInsertTextRange ) + setTextInsertMode( xInsertTextRange ); + } + + // auto text mode + const OUString sAutoTextMode("AutoTextMode"); + if( xPropertySetInfo->hasPropertyByName(sAutoTextMode) ) + { + aAny = xImportInfo->getPropertyValue(sAutoTextMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bBlock = true; + } + } + + // organizer mode + const OUString sOrganizerMode("OrganizerMode"); + if( xPropertySetInfo->hasPropertyByName(sOrganizerMode) ) + { + aAny = xImportInfo->getPropertyValue(sOrganizerMode); + if( auto b = o3tl::tryAccess<bool>(aAny) ) + { + if( *b ) + m_bOrganizerMode = true; + } + } + + // default document properties + const OUString sDefSettings("DefaultDocumentSettings"); + if (xPropertySetInfo->hasPropertyByName(sDefSettings)) + { + aAny = xImportInfo->getPropertyValue(sDefSettings); + Sequence<PropertyValue> aProps; + if (aAny >>= aProps) + { + Reference<lang::XMultiServiceFactory> xFac(GetModel(), UNO_QUERY); + Reference<XPropertySet> xProps( + xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY); + Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo()); + + if (xProps.is() && xInfo.is()) + { + for (const auto& rProp : std::as_const(aProps)) + { + if (xInfo->hasPropertyByName(rProp.Name)) + { + xProps->setPropertyValue(rProp.Name, rProp.Value); + } + } + } + } + } + } + + // There only is a text cursor by now if we are in insert mode. In any + // other case we have to create one at the start of the document. + // We also might change into the insert mode later, so we have to make + // sure to first set the insert mode and then create the text import + // helper. Otherwise it won't have the insert flag set! + OTextCursorHelper *pTextCursor = nullptr; + Reference < XTextCursor > xTextCursor; + if( HasTextImport() ) + xTextCursor = GetTextImport()->GetCursor(); + if( !xTextCursor.is() ) + { + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + xTextCursor = xText->createTextCursor(); + SwCursorShell *pCursorSh = nullptr; + SwDoc *pDoc = nullptr; + if( SvXMLImportFlags::ALL == getImportFlags() ) + { + pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor ); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + if( !pTextCursor ) + return; + + pDoc = pTextCursor->GetDoc(); + OSL_ENSURE( pDoc, "SwDoc missing" ); + if( !pDoc ) + return; + + // Is there an edit shell. If yes, then we are currently inserting + // a document. We then have to insert at the current edit shell's + // cursor position. That not quite clean code, but there is no other + // way currently. + pCursorSh = pDoc->GetEditShell(); + } + if( pCursorSh ) + { + const uno::Reference<text::XTextRange> xInsertTextRange( + SwXTextRange::CreateXTextRange( + *pDoc, *pCursorSh->GetCursor()->GetPoint(), nullptr ) ); + setTextInsertMode( xInsertTextRange ); + xTextCursor = GetTextImport()->GetCursor(); + pTextCursor = nullptr; + } + else + GetTextImport()->SetCursor( xTextCursor ); + } + + if( !(getImportFlags() & (SvXMLImportFlags::CONTENT|SvXMLImportFlags::MASTERSTYLES)) ) + return; + + if( !pTextCursor ) + pTextCursor = lcl_xml_GetSwXTextCursor( xTextCursor ); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + if( !pTextCursor ) + return; + + SwDoc *pDoc = pTextCursor->GetDoc(); + OSL_ENSURE( pDoc, "SwDoc missing" ); + if( !pDoc ) + return; + + if (SvXMLImportFlags::ALL == getImportFlags()) + { + // for flat ODF - this is done in SwReader::Read() for package ODF + pDoc->SetInReading(true); + pDoc->SetInXMLImport(true); + } + + if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() ) + { + m_pSttNdIdx.reset(new SwNodeIndex( pDoc->GetNodes() )); + if( IsInsertMode() ) + { + SwPaM *pPaM = pTextCursor->GetPaM(); + const SwPosition* pPos = pPaM->GetPoint(); + + // Split once and remember the node that has been split. + pDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + *m_pSttNdIdx = pPos->nNode.GetIndex()-1; + + // Split again. + pDoc->getIDocumentContentOperations().SplitNode( *pPos, false ); + + // Insert all content into the new node + pPaM->Move( fnMoveBackward ); + pDoc->SetTextFormatColl + ( *pPaM, pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false ) ); + } + } + + // We need a draw model to be able to set the z order + pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); // #i52858# - method name changed + + // SJ: #i49801# locking the model to disable repaints + SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if ( pDrawModel ) + pDrawModel->setLock(true); + + if (!GetGraphicStorageHandler().is()) + { + m_xGraphicStorageHandler = SvXMLGraphicHelper::Create(SvXMLGraphicHelperMode::Read); + SetGraphicStorageHandler(m_xGraphicStorageHandler.get()); + } + + if( !GetEmbeddedResolver().is() ) + { + SfxObjectShell *pPersist = pDoc->GetPersist(); + if( pPersist ) + { + m_xEmbeddedResolver = SvXMLEmbeddedObjectHelper::Create( + *pPersist, + SvXMLEmbeddedObjectHelperMode::Read ); + Reference< document::XEmbeddedObjectResolver > xEmbeddedResolver( m_xEmbeddedResolver.get() ); + SetEmbeddedResolver( xEmbeddedResolver ); + } + } +} + +void SwXMLImport::endDocument() +{ + OSL_ENSURE( GetModel().is(), "model missing; maybe startDocument wasn't called?" ); + if( !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + if (m_xGraphicStorageHandler) + m_xGraphicStorageHandler->dispose(); + m_xGraphicStorageHandler.clear(); + + if( m_xEmbeddedResolver ) + m_xEmbeddedResolver->dispose(); + m_xEmbeddedResolver.clear(); + // Clear the shape import to sort the shapes (and not in the + // destructor that might be called after the import has finished + // for Java filters. + if( HasShapeImport() ) + ClearShapeImport(); + + SwDoc *pDoc = nullptr; + if( (getImportFlags() & SvXMLImportFlags::CONTENT) && !IsStylesOnlyMode() ) + { + Reference<XUnoTunnel> xCursorTunnel( GetTextImport()->GetCursor(), + UNO_QUERY); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper *>( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + assert(pTextCursor && "SwXTextCursor missing"); + SwPaM *pPaM = pTextCursor->GetPaM(); + if( IsInsertMode() && m_pSttNdIdx->GetIndex() ) + { + // If we are in insert mode, join the split node that is in front + // of the new content with the first new node. Or in other words: + // Revert the first split node. + SwTextNode* pTextNode = m_pSttNdIdx->GetNode().GetTextNode(); + SwNodeIndex aNxtIdx( *m_pSttNdIdx ); + if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx ) && + m_pSttNdIdx->GetIndex() + 1 == aNxtIdx.GetIndex() ) + { + // If the PaM points to the first new node, move the PaM to the + // end of the previous node. + if( pPaM->GetPoint()->nNode == aNxtIdx ) + { + pPaM->GetPoint()->nNode = *m_pSttNdIdx; + pPaM->GetPoint()->nContent.Assign( pTextNode, + pTextNode->GetText().getLength()); + } + +#if OSL_DEBUG_LEVEL > 0 + // !!! This should be impossible !!!! + OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != + pPaM->GetBound().nNode.GetIndex(), + "PaM.Bound1 point to new node " ); + OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != + pPaM->GetBound( false ).nNode.GetIndex(), + "PaM.Bound2 points to new node" ); + + if( m_pSttNdIdx->GetIndex()+1 == + pPaM->GetBound().nNode.GetIndex() ) + { + const sal_Int32 nCntPos = + pPaM->GetBound().nContent.GetIndex(); + pPaM->GetBound().nContent.Assign( pTextNode, + pTextNode->GetText().getLength() + nCntPos ); + } + if( m_pSttNdIdx->GetIndex()+1 == + pPaM->GetBound( false ).nNode.GetIndex() ) + { + const sal_Int32 nCntPos = + pPaM->GetBound( false ).nContent.GetIndex(); + pPaM->GetBound( false ).nContent.Assign( pTextNode, + pTextNode->GetText().getLength() + nCntPos ); + } +#endif + // If the first new node isn't empty, convert the node's text + // attributes into hints. Otherwise, set the new node's + // paragraph style at the previous (empty) node. + SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode(); + if (!pTextNode->GetText().isEmpty()) + pDelNd->FormatToTextAttr( pTextNode ); + else + pTextNode->ChgFormatColl( pDelNd->GetTextColl() ); + pTextNode->JoinNext(); + } + } + + SwPosition* pPos = pPaM->GetPoint(); + OSL_ENSURE( !pPos->nContent.GetIndex(), "last paragraph isn't empty" ); + if( !pPos->nContent.GetIndex() ) + { + SwTextNode* pCurrNd; + sal_uLong nNodeIdx = pPos->nNode.GetIndex(); + pDoc = pPaM->GetDoc(); + + OSL_ENSURE( pPos->nNode.GetNode().IsContentNode(), + "insert position is not a content node" ); + if( !IsInsertMode() ) + { + // If we're not in insert mode, the last node is deleted. + const SwNode *pPrev = pDoc->GetNodes()[nNodeIdx -1]; + if( pPrev->IsContentNode() || + ( pPrev->IsEndNode() && + pPrev->StartOfSectionNode()->IsSectionNode() ) ) + { + SwContentNode* pCNd = pPaM->GetContentNode(); + if( pCNd && pCNd->StartOfSectionIndex()+2 < + pCNd->EndOfSectionIndex() ) + { + pPaM->GetBound().nContent.Assign( nullptr, 0 ); + pPaM->GetBound(false).nContent.Assign( nullptr, 0 ); + pDoc->GetNodes().Delete( pPaM->GetPoint()->nNode ); + } + } + } + else if( nullptr != (pCurrNd = pDoc->GetNodes()[nNodeIdx]->GetTextNode()) ) + { + // Id we're in insert mode, the empty node is joined with + // the next and the previous one. + if( pCurrNd->CanJoinNext( &pPos->nNode )) + { + SwTextNode* pNextNd = pPos->nNode.GetNode().GetTextNode(); + pPos->nContent.Assign( pNextNd, 0 ); + pPaM->SetMark(); pPaM->DeleteMark(); + pNextNd->JoinPrev(); + + // Remove line break that has been inserted by the import, + // but only if one has been inserted! + if( pNextNd->CanJoinPrev(/* &pPos->nNode*/ ) && + *m_pSttNdIdx != pPos->nNode ) + { + pNextNd->JoinPrev(); + } + } + else if (pCurrNd->GetText().isEmpty()) + { + pPos->nContent.Assign( nullptr, 0 ); + pPaM->SetMark(); pPaM->DeleteMark(); + pDoc->GetNodes().Delete( pPos->nNode ); + pPaM->Move( fnMoveBackward ); + } + } + + // tdf#113877 + // when we insert one document with list inside into another one with list at the insert position, + // the resulting numbering in these lists is not consequent. + // + // Main document: + // 1. One + // 2. Two + // 3. Three + // 4. <-- insert position + // + // Inserted document: + // 1. One + // 2. Two + // 3. Three + // 4. + // + // Expected result + // 1. One + // 2. Two + // 3. Three + // 4. One + // 5. Two + // 6. Three + // 7. + // + MergeListsAtDocumentInsertPosition(pDoc); + } + } + + /* Was called too early. Moved from SwXMLBodyContext_Impl::EndElement */ + + GetTextImport()->RedlineAdjustStartNodeCursor(); + + if( (getImportFlags() & SvXMLImportFlags::CONTENT) || + ((getImportFlags() & SvXMLImportFlags::MASTERSTYLES) && IsStylesOnlyMode()) ) + { + // pDoc might be 0. In this case UpdateTextCollCondition is looking + // for it itself. + UpdateTextCollConditions( pDoc ); + } + + GetTextImport()->ResetCursor(); + + m_pSttNdIdx.reset(); + + // SJ: #i49801# -> now permitting repaints + if ( pDoc ) + { + if( getImportFlags() == SvXMLImportFlags::ALL ) + { + // Notify math objects. If we are in the package filter this will + // be done by the filter object itself + if( IsInsertMode() ) + pDoc->PrtOLENotify( false ); + else if ( pDoc->IsOLEPrtNotifyPending() ) + pDoc->PrtOLENotify( true ); + + assert(pDoc->IsInReading()); + assert(pDoc->IsInXMLImport()); + pDoc->SetInReading(false); + pDoc->SetInXMLImport(false); + } + + SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + if ( pDrawModel ) + pDrawModel->setLock(false); + } + + // #i90243# + if ( m_bInititedXForms ) + { + Reference< xforms::XFormsSupplier > xFormsSupp( GetModel(), UNO_QUERY ); + Reference< XNameAccess > xXForms; + if ( xFormsSupp.is() ) + xXForms = xFormsSupp->getXForms().get(); + + if ( xXForms.is() ) + { + try + { + Sequence< beans::PropertyValue > aXFormsSettings; + + const OUString& sXFormsSettingsName( GetXMLToken( XML_XFORM_MODEL_SETTINGS ) ); + if ( m_xLateInitSettings.is() && m_xLateInitSettings->hasByName( sXFormsSettingsName ) ) + { + OSL_VERIFY( m_xLateInitSettings->getByName( sXFormsSettingsName ) >>= aXFormsSettings ); + applyXFormsSettings( xXForms, aXFormsSettings ); + } + } + catch( const Exception& ) + { + } + } + } + + // delegate to parent: takes care of error handling + SvXMLImport::endDocument(); + ClearTextImport(); +} + +// tdf#113877 +// when we insert one document with list inside into another one with list at the insert position, +// the resulting numbering in these lists is not consequent. +// +// CASE-1: Main document: +// 1. One +// 2. Two +// 3. Three +// 4. <-- insert position +// +// Inserted document: +// 1. One +// 2. Two +// 3. Three +// 4. +// +// Expected result +// 1. One +// 2. Two +// 3. Three +// 4. One +// 5. Two +// 6. Three +// 7. +// +// CASE-2: Main document: +// 1. One +// 2. Two +// 3. Three +// 4. <-- insert position +// +// Inserted document: +// A) One +// B) Two +// C) Three +// D) +// +// Expected result +// 1. One +// 2. Two +// 3. Three +// 4. One +// A) Two +// B) Three +// 5. +// +void SwXMLImport::MergeListsAtDocumentInsertPosition(SwDoc *pDoc) +{ + // 1. check environment + if (! pDoc) + return; + + if (! IsInsertMode() || ! m_pSttNdIdx->GetIndex()) + return; + + sal_uLong index = 1; + + // the last node of the main document where we have inserted a document + const SwNodePtr node1 = pDoc->GetNodes()[m_pSttNdIdx->GetIndex() + 0]; + + // the first node of the inserted document + SwNodePtr node2 = pDoc->GetNodes()[m_pSttNdIdx->GetIndex() + index]; + + if (! (node1 && node2 + && (node1->GetNodeType() == node2->GetNodeType()) + && (node1->IsTextNode() == node2->IsTextNode()) + )) + { + // not a text node at insert position + return; + } + + // 2. get the first node of the inserted document, + // which will be used to detect if inside inserted document a new list was started after the first list + const SfxPoolItem* pListId2Initial = nullptr; + { + SwContentNode* contentNode1 = static_cast<SwContentNode *>(node1); + SwContentNode* contentNode2 = static_cast<SwContentNode *>(node2); + + // check if both lists have the same list properties + const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + + if (! pListId1) + return; + if (! pListId2) + return; + + auto pStringListId1 = dynamic_cast<const SfxStringItem*>(pListId1); + assert(pStringListId1); + const OUString& sListId1 = pStringListId1->GetValue(); + auto pStringListId2 = dynamic_cast<const SfxStringItem*>(pListId2); + assert(pStringListId2); + const OUString& sListId2 = pStringListId2->GetValue(); + + const SwList* pList1 = pDoc->getIDocumentListsAccess().getListByName( sListId1 ); + const SwList* pList2 = pDoc->getIDocumentListsAccess().getListByName( sListId2 ); + + if (! pList1) + return; + if (! pList2) + return; + + const OUString& sDefaultListStyleName1 = pList1->GetDefaultListStyleName(); + const OUString& sDefaultListStyleName2 = pList2->GetDefaultListStyleName(); + + if (sDefaultListStyleName1 != sDefaultListStyleName2) + { + const SwNumRule* pNumRule1 = pDoc->FindNumRulePtr( sDefaultListStyleName1 ); + const SwNumRule* pNumRule2 = pDoc->FindNumRulePtr( sDefaultListStyleName2 ); + + if (pNumRule1 && pNumRule2) + { + // check style of the each list level + for( sal_uInt8 n = 0; n < MAXLEVEL; ++n ) + { + if( pNumRule1->Get( n ) != pNumRule2->Get( n ) ) + { + return; + } + } + + // our list should be merged + pListId2Initial = pListId2; + } + } + else + { + // our list should be merged + pListId2Initial = pListId2; + } + } + + if (! pListId2Initial) + { + // two lists have different styles => they should not be merged + return; + } + + // 3. merge two lists + while ( + node1 && node2 + && (node1->GetNodeType() == node2->GetNodeType()) + && (node1->IsTextNode() == node2->IsTextNode()) + ) + { + SwContentNode* contentNode1 = static_cast<SwContentNode *>( node1 ); + SwContentNode* contentNode2 = static_cast<SwContentNode *>( node2 ); + + const SfxPoolItem* pListId1 = contentNode1->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + const SfxPoolItem* pListId2 = contentNode2->GetNoCondAttr( RES_PARATR_LIST_ID, false ); + + if (! pListId1) + return; + if (! pListId2) + return; + + if (*pListId2Initial != *pListId2) + { + // no more list items of the first list inside inserted document + return; + } + + // set list style to this list element + contentNode2->SetAttr(*pListId1); + + // get next item + index++; + if (index >= pDoc->GetNodes().Count()) + { + // no more items + return; + } + + node2 = pDoc->GetNodes()[m_pSttNdIdx->GetIndex() + index]; + } +} + +namespace { + +// Locally derive XMLTextShapeImportHelper, so we can take care of the +// form import This is Writer, but not text specific, so it should go +// here! +class SvTextShapeImportHelper : public XMLTextShapeImportHelper +{ + // hold own reference form import helper, because the SvxImport + // stored in the superclass, from whom we originally got the + // reference, is already destroyed when we want to use it in the + // destructor + rtl::Reference< ::xmloff::OFormLayerXMLImport > rFormImport; + + // hold reference to the one page (if it exists) for calling startPage() + // and endPage. If !xPage.is(), then this document doesn't have a + // XDrawPage. + Reference<drawing::XDrawPage> xPage; + +public: + explicit SvTextShapeImportHelper(SvXMLImport& rImp); + virtual ~SvTextShapeImportHelper() override; +}; + +} + +SvTextShapeImportHelper::SvTextShapeImportHelper(SvXMLImport& rImp) : + XMLTextShapeImportHelper(rImp) +{ + Reference<drawing::XDrawPageSupplier> xSupplier(rImp.GetModel(),UNO_QUERY); + if (xSupplier.is()) + { + if (rImp.GetFormImport().is()) + { + rImp.GetFormImport()->startPage(xSupplier->getDrawPage()); + rFormImport = rImp.GetFormImport(); + } + + xPage = xSupplier->getDrawPage(); + XMLShapeImportHelper::startPage( xPage ); + } +} + +SvTextShapeImportHelper::~SvTextShapeImportHelper() +{ + rFormImport->endPage(); + + if (xPage.is()) + { + XMLShapeImportHelper::endPage(xPage); + } +} + +XMLTextImportHelper* SwXMLImport::CreateTextImport() +{ + return new SwXMLTextImportHelper( GetModel(), *this, getImportInfo(), + IsInsertMode(), + IsStylesOnlyMode(), + IsBlockMode(), m_bOrganizerMode ); +} + +XMLShapeImportHelper* SwXMLImport::CreateShapeImport() +{ + return new SvTextShapeImportHelper( *this ); +} + +SvXMLImportContext *SwXMLImport::CreateFontDeclsContext() +{ + XMLFontStylesContext *pFSContext = + new XMLFontStylesContext( *this, osl_getThreadTextEncoding() ); + SetFontDecls( pFSContext ); + return pFSContext; +} + +void SwXMLImport::SetViewSettings(const Sequence < PropertyValue > & aViewProps) +{ + if (IsInsertMode() || IsStylesOnlyMode() || IsBlockMode() || m_bOrganizerMode || !GetModel().is() ) + return; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + SwDoc *pDoc = getDoc(); + tools::Rectangle aRect; + if( pDoc->GetDocShell() ) + aRect = pDoc->GetDocShell()->GetVisArea( ASPECT_CONTENT ); + //TODO/LATER: why that cast?! + //aRect = ((SfxInPlaceObject *)pDoc->GetDocShell())->GetVisArea(); + + sal_Int64 nTmp = 0; + bool bShowRedlineChanges = false, bBrowseMode = false; + bool bChangeShowRedline = false, bChangeBrowseMode = false; + + //TODO/LATER: why that cast?! + bool bTwip = pDoc->GetDocShell()->GetMapUnit ( ) == MapUnit::MapTwip; + //sal_Bool bTwip = pDoc->GetDocShell()->SfxInPlaceObject::GetMapUnit ( ) == MapUnit::MapTwip; + + for (const PropertyValue& rValue : aViewProps) + { + if ( rValue.Name == "ViewAreaTop" ) + { + rValue.Value >>= nTmp; + aRect.setY( static_cast< long >(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp) ); + } + else if ( rValue.Name == "ViewAreaLeft" ) + { + rValue.Value >>= nTmp; + aRect.setX( static_cast< long >(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp) ); + } + else if ( rValue.Name == "ViewAreaWidth" ) + { + rValue.Value >>= nTmp; + Size aSize( aRect.GetSize() ); + aSize.setWidth( static_cast< long >(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp) ); + aRect.SetSize( aSize ); + } + else if ( rValue.Name == "ViewAreaHeight" ) + { + rValue.Value >>= nTmp; + Size aSize( aRect.GetSize() ); + aSize.setHeight( static_cast< long >(bTwip ? sanitiseMm100ToTwip(nTmp) : nTmp) ); + aRect.SetSize( aSize ); + } + else if ( rValue.Name == "ShowRedlineChanges" ) + { + bShowRedlineChanges = *o3tl::doAccess<bool>(rValue.Value); + bChangeShowRedline = true; + } +// Headers and footers are not displayed in BrowseView anymore + else if ( rValue.Name == "InBrowseMode" ) + { + bBrowseMode = *o3tl::doAccess<bool>(rValue.Value); + bChangeBrowseMode = true; + } + } + if( pDoc->GetDocShell() ) + pDoc->GetDocShell()->SetVisArea ( aRect ); + + if (bChangeBrowseMode) + pDoc->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, bBrowseMode ); + + if (bChangeShowRedline) + GetTextImport()->SetShowChanges( bShowRedlineChanges ); +} + +// Note: this will be called only if there are OOo elements in settings.xml. +// So if a setting is missing there we can assume that it was written +// by an OOo/LO version that is older than the introduction of the setting! +void SwXMLImport::SetConfigurationSettings(const Sequence < PropertyValue > & aConfigProps) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + Reference< lang::XMultiServiceFactory > xFac( GetModel(), UNO_QUERY ); + if( !xFac.is() ) + return; + + Reference< XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), UNO_QUERY ); + if( !xProps.is() ) + return; + + Reference< XPropertySetInfo > xInfo( xProps->getPropertySetInfo() ); + if( !xInfo.is() ) + return; + + std::unordered_set< OUString > aExcludeAlways; + aExcludeAlways.insert("LinkUpdateMode"); + // this should contain things that are actually user-settable, via Tools->Options + std::unordered_set< OUString > aExcludeWhenNotLoadingUserSettings { + "ForbiddenCharacters", + "IsKernAsianPunctuation", + "CharacterCompressionType", + "FieldAutoUpdate", + "ChartAutoUpdate", + "AddParaTableSpacing", + "AddParaTableSpacingAtStart", + "PrintAnnotationMode", + "PrintBlackFonts", + "PrintControls", + "PrintDrawings", + "PrintGraphics", + "PrintHiddenText", + "PrintLeftPages", + "PrintPageBackground", + "PrintProspect", + "PrintReversed", + "PrintRightPages", + "PrintFaxName", + "PrintPaperFromSetup", + "PrintTables", + "PrintTextPlaceholder", + "PrintSingleJobs", + "UpdateFromTemplate", + "PrinterIndependentLayout", + "PrintEmptyPages", + "ConsiderTextWrapOnObjPos", + "DoNotJustifyLinesWithManualBreak", + "ProtectForm", + "MsWordCompTrailingBlanks", + "SubtractFlysAnchoredAtFlys", + "EmptyDbFieldHidesPara" + }; + + SvtSaveOptions aSaveOpt; + bool bIsUserSetting = aSaveOpt.IsLoadUserSettings(); + + // for some properties we don't want to use the application + // default if they're missing. So we watch for them in the loop + // below, and set them if not found + bool bPrinterIndependentLayout = false; + bool bUseOldNumbering = false; + bool bAddExternalLeading = false; + bool bAddParaSpacingToTableCells = false; + bool bAddParaLineSpacingToTableCells = false; + bool bUseFormerLineSpacing = false; + bool bUseFormerObjectPositioning = false; + bool bUseFormerTextWrapping = false; + bool bConsiderWrapOnObjPos = false; + bool bIgnoreFirstLineIndentInNumbering = false; + bool bDoNotJustifyLinesWithManualBreak = false; + bool bDoNotResetParaAttrsForNumFont = false; + bool bLoadReadonly = false; + bool bDoNotCaptureDrawObjsOnPage( false ); + bool bClipAsCharacterAnchoredWriterFlyFrames( false ); + bool bUnixForceZeroExtLeading = false; + bool bSmallCapsPercentage66 = false; + bool bTabOverflow = false; + bool bUnbreakableNumberings = false; + bool bClippedPictures = false; + bool bBackgroundParaOverDrawings = false; + bool bTabOverMargin = false; + bool bTabOverMarginValue = false; + bool bPropLineSpacingShrinksFirstLine = false; + bool bSubtractFlysAnchoredAtFlys = false; + bool bCollapseEmptyCellPara = false; + + const PropertyValue* currentDatabaseDataSource = nullptr; + const PropertyValue* currentDatabaseCommand = nullptr; + const PropertyValue* currentDatabaseCommandType = nullptr; + const PropertyValue* embeddedDatabaseName = nullptr; + + for( const PropertyValue& rValue : aConfigProps ) + { + bool bSet = aExcludeAlways.find(rValue.Name) == aExcludeAlways.end(); + if( bSet && !bIsUserSetting + && (aExcludeWhenNotLoadingUserSettings.find(rValue.Name) + != aExcludeWhenNotLoadingUserSettings.end()) ) + { + bSet = false; + } + + if( bSet ) + { + try + { + if( xInfo->hasPropertyByName( rValue.Name ) ) + { + if( rValue.Name == "RedlineProtectionKey" ) + { + Sequence<sal_Int8> aKey; + rValue.Value >>= aKey; + GetTextImport()->SetChangesProtectionKey( aKey ); + } + else + { + // HACK: Setting these out of order does not work. + if( rValue.Name == "CurrentDatabaseDataSource" ) + currentDatabaseDataSource = &rValue; + else if( rValue.Name == "CurrentDatabaseCommand" ) + currentDatabaseCommand = &rValue; + else if( rValue.Name == "CurrentDatabaseCommandType" ) + currentDatabaseCommandType = &rValue; + else if (rValue.Name == "EmbeddedDatabaseName") + embeddedDatabaseName = &rValue; + else + xProps->setPropertyValue( rValue.Name, rValue.Value ); + } + } + + // did we find any of the non-default cases? + if ( rValue.Name == "PrinterIndependentLayout" ) + bPrinterIndependentLayout = true; + else if ( rValue.Name == "AddExternalLeading" ) + bAddExternalLeading = true; + else if ( rValue.Name == "AddParaSpacingToTableCells" ) + bAddParaSpacingToTableCells = true; + else if ( rValue.Name == "AddParaLineSpacingToTableCells" ) + bAddParaLineSpacingToTableCells = true; + else if ( rValue.Name == "UseFormerLineSpacing" ) + bUseFormerLineSpacing = true; + else if ( rValue.Name == "UseFormerObjectPositioning" ) + bUseFormerObjectPositioning = true; + else if ( rValue.Name == "UseFormerTextWrapping" ) + bUseFormerTextWrapping = true; + else if ( rValue.Name == "UseOldNumbering" ) + bUseOldNumbering = true; + else if ( rValue.Name == "ConsiderTextWrapOnObjPos" ) + bConsiderWrapOnObjPos = true; + else if ( rValue.Name == "IgnoreFirstLineIndentInNumbering" ) + bIgnoreFirstLineIndentInNumbering = true; + else if ( rValue.Name == "DoNotJustifyLinesWithManualBreak" ) + bDoNotJustifyLinesWithManualBreak = true; + else if ( rValue.Name == "DoNotResetParaAttrsForNumFont" ) + bDoNotResetParaAttrsForNumFont = true; + else if ( rValue.Name == "LoadReadonly" ) + bLoadReadonly = true; + else if ( rValue.Name == "DoNotCaptureDrawObjsOnPage" ) + bDoNotCaptureDrawObjsOnPage = true; + else if ( rValue.Name == "ClipAsCharacterAnchoredWriterFlyFrames" ) + bClipAsCharacterAnchoredWriterFlyFrames = true; + else if ( rValue.Name == "UnxForceZeroExtLeading" ) + bUnixForceZeroExtLeading = true; + else if ( rValue.Name == "SmallCapsPercentage66" ) + bSmallCapsPercentage66 = true; + else if ( rValue.Name == "TabOverflow" ) + bTabOverflow = true; + else if ( rValue.Name == "UnbreakableNumberings" ) + bUnbreakableNumberings = true; + else if ( rValue.Name == "ClippedPictures" ) + bClippedPictures = true; + else if ( rValue.Name == "BackgroundParaOverDrawings" ) + bBackgroundParaOverDrawings = true; + else if ( rValue.Name == "TabOverMargin" ) + { + bTabOverMargin = true; + rValue.Value >>= bTabOverMarginValue; + } + else if ( rValue.Name == "PropLineSpacingShrinksFirstLine" ) + bPropLineSpacingShrinksFirstLine = true; + else if (rValue.Name == "SubtractFlysAnchoredAtFlys") + bSubtractFlysAnchoredAtFlys = true; + else if (rValue.Name == "CollapseEmptyCellPara") + bCollapseEmptyCellPara = true; + } + catch( Exception& ) + { + OSL_FAIL( "SwXMLImport::SetConfigurationSettings: Exception!" ); + } + } + } + + try + { + if( currentDatabaseDataSource != nullptr ) + xProps->setPropertyValue( currentDatabaseDataSource->Name, currentDatabaseDataSource->Value ); + if( currentDatabaseCommand != nullptr ) + xProps->setPropertyValue( currentDatabaseCommand->Name, currentDatabaseCommand->Value ); + if( currentDatabaseCommandType != nullptr ) + xProps->setPropertyValue( currentDatabaseCommandType->Name, currentDatabaseCommandType->Value ); + if (embeddedDatabaseName) + xProps->setPropertyValue(embeddedDatabaseName->Name, embeddedDatabaseName->Value); + } catch( Exception& ) + { + OSL_FAIL( "SwXMLImport::SetConfigurationSettings: Exception!" ); + } + + // finally, treat the non-default cases + // introduce boolean, that indicates a document, written by version prior SO8. + const bool bDocumentPriorSO8 = !bConsiderWrapOnObjPos; + + if( ! bPrinterIndependentLayout ) + { + xProps->setPropertyValue( "PrinterIndependentLayout", Any(sal_Int16(document::PrinterIndependentLayout::DISABLED)) ); + } + + if( ! bAddExternalLeading ) + { + xProps->setPropertyValue( "AddExternalLeading", makeAny( false ) ); + } + + if( ! bUseFormerLineSpacing ) + { + xProps->setPropertyValue( "UseFormerLineSpacing", makeAny( true ) ); + } + + if( !bUseFormerObjectPositioning ) + { + xProps->setPropertyValue( "UseFormerObjectPositioning", makeAny( true ) ); + } + + if( !bUseOldNumbering ) + { + xProps->setPropertyValue( "UseOldNumbering", makeAny(true) ); + } + + if( !bAddParaSpacingToTableCells ) + { + xProps->setPropertyValue( "AddParaSpacingToTableCells", + makeAny( false ) ); + } + if (!bAddParaLineSpacingToTableCells) + { + xProps->setPropertyValue("AddParaLineSpacingToTableCells", makeAny(false)); + } + + if( !bUseFormerTextWrapping ) + { + xProps->setPropertyValue( "UseFormerTextWrapping", makeAny( true ) ); + } + + if( !bConsiderWrapOnObjPos ) + { + xProps->setPropertyValue( "ConsiderTextWrapOnObjPos", makeAny( false ) ); + } + + // #i47448# + // For SO7pp4, part of the 'new numbering' stuff has been backported from + // SO8. Unfortunately, only part of it and by using the same compatibility option + // like in SO8. Therefore documents generated with SO7pp4, containing + // numbered paragraphs with first line indent differ between SO7pp4 and + // SO8. In order to fix this for SO8pp1, I introduce a new compatibility + // flag 'bIgnoreFirstLineIndentInNumbering'. This flag has to be set for all + // documents < SO8, but not for SO8. So if the property is not present, the + // flag will be set to 'true'. SO8 documents surely have the + // 'ConsiderWrapOnObjPos' property set (no matter if 'true' or 'false'), + // therefore the correct condition to set this flag is this: + if( !bIgnoreFirstLineIndentInNumbering && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "IgnoreFirstLineIndentInNumbering", + makeAny( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotJustifyLinesWithManualBreak && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotJustifyLinesWithManualBreak", + makeAny( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotResetParaAttrsForNumFont && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotResetParaAttrsForNumFont", + makeAny( true ) ); + } + + if ( !bLoadReadonly ) + { + xProps->setPropertyValue( "LoadReadonly", makeAny( false ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bDoNotCaptureDrawObjsOnPage && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "DoNotCaptureDrawObjsOnPage", + makeAny( true ) ); + } + + // This flag has to be set for all documents < SO8 + if ( !bClipAsCharacterAnchoredWriterFlyFrames && bDocumentPriorSO8 ) + { + xProps->setPropertyValue( "ClipAsCharacterAnchoredWriterFlyFrames", + makeAny( true ) ); + } + + if ( !bUnixForceZeroExtLeading ) + { + xProps->setPropertyValue( "UnxForceZeroExtLeading", makeAny( true ) ); + } + + // Old LO versions had 66 as the value for small caps percentage, later changed to 80. + // In order to keep backwards compatibility, SmallCapsPercentage66 option is written to .odt + // files, and the default for new documents is 'false'. Files without this option + // are considered to be old files, so set the compatibility option too. + if ( !bSmallCapsPercentage66 ) + { + xProps->setPropertyValue( "SmallCapsPercentage66", makeAny( true ) ); + } + + if ( !bTabOverflow ) + { + xProps->setPropertyValue( "TabOverflow", makeAny( false ) ); + } + + if ( !bUnbreakableNumberings ) + { + xProps->setPropertyValue( "UnbreakableNumberings", makeAny( false ) ); + } + + if ( !bClippedPictures ) + { + xProps->setPropertyValue( "ClippedPictures", makeAny( false ) ); + } + + if ( !bBackgroundParaOverDrawings ) + xProps->setPropertyValue("BackgroundParaOverDrawings", makeAny( false ) ); + + if ( !bTabOverMargin ) + xProps->setPropertyValue("TabOverMargin", makeAny( false ) ); + + if (bTabOverMarginValue) + // Let TabOverMargin imply the new default for + // PrinterIndependentLayout, knowing the first is set by Word import + // filters and Word defaults to our new default as well. + xProps->setPropertyValue( + "PrinterIndependentLayout", + uno::Any(document::PrinterIndependentLayout::HIGH_RESOLUTION)); + + if (!bPropLineSpacingShrinksFirstLine) + xProps->setPropertyValue("PropLineSpacingShrinksFirstLine", makeAny(false)); + + if (!bSubtractFlysAnchoredAtFlys) + xProps->setPropertyValue("SubtractFlysAnchoredAtFlys", makeAny(true)); + + if (!bCollapseEmptyCellPara) + xProps->setPropertyValue("CollapseEmptyCellPara", makeAny(false)); + + SwDoc *pDoc = getDoc(); + SfxPrinter *pPrinter = pDoc->getIDocumentDeviceAccess().getPrinter( false ); + if( pPrinter ) + { + // If the printer is known, then the OLE objects will + // already have correct sizes, and we don't have to call + // PrtOLENotify again. Otherwise we have to call it. + // The flag might be set from setting the printer, so it + // it is required to clear it. + pDoc->SetOLEPrtNotifyPending( !pPrinter->IsKnown() ); + } +} + +void SwXMLImport::SetDocumentSpecificSettings( + const OUString& _rSettingsGroupName, + const Sequence< PropertyValue>& _rSettings ) +{ + // the only doc-specific settings group we know so far are the XForms settings + if ( !IsXMLToken( _rSettingsGroupName, XML_XFORM_MODEL_SETTINGS ) ) + return; + + // preserve the settings for a later iteration - we are currently reading the settings.xml, + // the content.xml will be read later, by another instance of SwXMLImport + OSL_ENSURE( m_xLateInitSettings.is(), "SwXMLImport::SetDocumentSpecificSettings: no storage for those settings!" ); + if ( !m_xLateInitSettings.is() ) + return; + + try + { + if ( m_xLateInitSettings->hasByName( _rSettingsGroupName ) ) + { + m_xLateInitSettings->replaceByName( _rSettingsGroupName, makeAny( _rSettings ) ); + OSL_FAIL( "SwXMLImport::SetDocumentSpecificSettings: already have settings for this model!" ); + } + else + m_xLateInitSettings->insertByName( _rSettingsGroupName, makeAny( _rSettings ) ); + } + catch( const Exception& ) + { + } +} + +void SwXMLImport::initialize( + const Sequence<Any>& aArguments ) +{ + // delegate to super class + SvXMLImport::initialize(aArguments); + + // we are only looking for a NamedValue "LateInitSettings" + for(const auto& rArgument : aArguments) + { + beans::NamedValue aNamedValue; + if ( rArgument >>= aNamedValue ) + { + if (aNamedValue.Name == "LateInitSettings") + { + OSL_VERIFY( aNamedValue.Value >>= m_xLateInitSettings ); + } + } + } +} + +SwDoc* SwImport::GetDocFromXMLImport( SvXMLImport const & rImport ) +{ + auto pTextDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(rImport.GetModel()); + assert( pTextDoc ); + assert( pTextDoc->GetDocShell() ); + SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); + OSL_ENSURE( pDoc, "Where is my document?" ); + return pDoc; +} + +void SwXMLImport::initXForms() +{ + // obtain SwDoc + auto pXTextDocument = comphelper::getUnoTunnelImplementation<SwXTextDocument>(GetModel()); + if( pXTextDocument == nullptr ) + return; + + SwDoc *pDoc = pXTextDocument->GetDocShell()->GetDoc(); + + // init XForms (if not already done) + // (no default model, since we'll load the models) + if( ! pDoc->isXForms() ) + pDoc->initXForms( false ); + + m_bInititedXForms = true; +} + +SwDoc* SwXMLImport::getDoc() +{ + if( m_pDoc != nullptr ) + return m_pDoc; + Reference < XTextDocument > xTextDoc( GetModel(), UNO_QUERY ); + Reference < XText > xText = xTextDoc->getText(); + Reference<XUnoTunnel> xTextTunnel( xText, UNO_QUERY); + assert( xTextTunnel.is()); + SwXText *pText = reinterpret_cast< SwXText *>( + sal::static_int_cast< sal_IntPtr >( xTextTunnel->getSomething( SwXText::getUnoTunnelId() ))); + assert( pText != nullptr ); + m_pDoc = pText->GetDoc(); + assert( m_pDoc != nullptr ); + return m_pDoc; +} + +const SwDoc* SwXMLImport::getDoc() const +{ + return const_cast< SwXMLImport* >( this )->getDoc(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisImporter", + SvXMLImportFlags::ALL)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisStylesImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisStylesImporter", + SvXMLImportFlags::STYLES | SvXMLImportFlags::MASTERSTYLES | SvXMLImportFlags::AUTOSTYLES | + SvXMLImportFlags::FONTDECLS)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisContentImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisContentImporter", + SvXMLImportFlags::CONTENT | SvXMLImportFlags::SCRIPTS | SvXMLImportFlags::AUTOSTYLES | + SvXMLImportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisMetaImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisMetaImporter", + SvXMLImportFlags::META)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_XMLOasisSettingsImporter_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXMLImport(context, "com.sun.star.comp.Writer.XMLOasisSettingsImporter", + SvXMLImportFlags::SETTINGS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODT(SvStream &rStream) +{ + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW); + + css::uno::Sequence<OUString> aUserData(7); + aUserData[0] = "com.sun.star.comp.filter.OdfFlatXml"; + aUserData[2] = "com.sun.star.comp.Writer.XMLOasisImporter"; + aUserData[3] = "com.sun.star.comp.Writer.XMLOasisExporter"; + aUserData[6] = "true"; + uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence( + { + { "UserData", uno::Any(aUserData) }, + })); + css::uno::Sequence<uno::Any> aOuterArgs(1); + aOuterArgs[0] <<= aAdaptorArgs; + + uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW); + xInit->initialize(aOuterArgs); + + uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "URL", uno::Any(OUString("private:stream")) }, + })); + xImporter->setTargetDocument(xModel); + + uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW); + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = xFilter->filter(aArgs); + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDOCX(SvStream &rStream) +{ + SwGlobals::ensure(); + + SfxObjectShellLock xDocSh(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xDocSh->DoInitNew(); + uno::Reference<frame::XModel> xModel(xDocSh->GetModel()); + + uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream)); + + uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.WriterFilter"), uno::UNO_QUERY_THROW); + + uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW); + uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::makeAny(xStream) }, + { "InputMode", uno::makeAny(true) }, + })); + xImporter->setTargetDocument(xModel); + + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = false; + try + { + ret = xFilter->filter(aArgs); + } + catch (...) + { + } + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimp.hxx b/sw/source/filter/xml/xmlimp.hxx new file mode 100644 index 000000000..4b62c408b --- /dev/null +++ b/sw/source/filter/xml/xmlimp.hxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLIMP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMP_HXX + +#include <sal/config.h> + +#include <memory> + +#include <com/sun/star/document/XDocumentProperties.hpp> + +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmlimp.hxx> + +#include "xmlitmap.hxx" +#include <o3tl/typed_flags_set.hxx> + +class SwDoc; +class SvXMLUnitConverter; +class SvXMLTokenMap; +class SvXMLImportItemMapper; +class SfxItemSet; +class SwNodeIndex; +class XMLTextImportHelper; +class SvXMLGraphicHelper; +class SvXMLEmbeddedObjectHelper; +enum class SfxStyleFamily; + +// define, how many steps ( = paragraphs ) the progress bar should advance +// for styles, autostyles and settings + meta +#define PROGRESS_BAR_STEP 20 + +namespace SwImport { + SwDoc* GetDocFromXMLImport( SvXMLImport const & ); +} + +// we only need this scoped enum to be flags here, in sw +namespace o3tl +{ + template<> struct typed_flags<SfxStyleFamily> : is_typed_flags<SfxStyleFamily, 0xffff> {}; +} + +class SwXMLImport: public SvXMLImport +{ + std::unique_ptr<SwNodeIndex> m_pSttNdIdx; + + std::unique_ptr<SvXMLUnitConverter> m_pTwipUnitConv; + std::unique_ptr<SvXMLImportItemMapper> m_pTableItemMapper;// paragraph item import + std::unique_ptr<SvXMLTokenMap> m_pDocElemTokenMap; + std::unique_ptr<SvXMLTokenMap> m_pTableElemTokenMap; + std::unique_ptr<SvXMLTokenMap> m_pTableCellAttrTokenMap; + + rtl::Reference<SvXMLGraphicHelper> m_xGraphicStorageHandler; + + rtl::Reference<SvXMLEmbeddedObjectHelper> m_xEmbeddedResolver; + + SvXMLItemMapEntriesRef m_xTableItemMap; + SvXMLItemMapEntriesRef m_xTableColItemMap; + SvXMLItemMapEntriesRef m_xTableRowItemMap; + SvXMLItemMapEntriesRef m_xTableCellItemMap; + css::uno::Reference< css::container::XNameContainer > + m_xLateInitSettings; + + SfxStyleFamily m_nStyleFamilyMask;// Mask of styles to load + bool m_bLoadDoc : 1; // Load doc or styles only + bool m_bInsert : 1; // Insert mode. If styles are + // loaded only false means that + // existing styles will be + // overwritten. + bool m_bBlock : 1; // Load text block + bool m_bOrganizerMode : 1; + bool m_bInititedXForms : 1; + + SwDoc* m_pDoc; // cached for getDoc() + + void InitItemImport(); + void FinitItemImport(); + void UpdateTextCollConditions( SwDoc *pDoc ); + + void setTextInsertMode( + const css::uno::Reference< css::text::XTextRange > & rInsertPos ); + void setStyleInsertMode( SfxStyleFamily nFamilies, + bool bOverwrite ); + +protected: + + virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement, + const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual XMLTextImportHelper* CreateTextImport() override; + + virtual XMLShapeImportHelper* CreateShapeImport() override; + +public: + SwXMLImport( + const css::uno::Reference< css::uno::XComponentContext >& rContext, + OUString const & implementationName, SvXMLImportFlags nImportFlags); + + virtual ~SwXMLImport() throw() override; + + // css::xml::sax::XDocumentHandler + virtual void SAL_CALL startDocument() override; + virtual void SAL_CALL endDocument() override; + + // XUnoTunnel + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() throw(); + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + void InsertStyles( bool bAuto ); + void FinishStyles(); + + // namespace office + + // NB: in contrast to other CreateFooContexts, this particular one handles + // the root element (i.e. office:document-meta) + SvXMLImportContext *CreateMetaContext( const sal_Int32 nElement ); + SvXMLImportContext *CreateScriptContext(); + SvXMLImportContext *CreateStylesContext( bool bAuto ); + SvXMLImportContext *CreateMasterStylesContext(); + SvXMLImportContext *CreateFontDeclsContext(); + SvXMLImportContext *CreateBodyContentContext(); + SfxStyleFamily GetStyleFamilyMask() const { return m_nStyleFamilyMask; } + bool IsInsertMode() const { return m_bInsert; } + bool IsStylesOnlyMode() const { return !m_bLoadDoc; } + bool IsBlockMode() const { return m_bBlock; } + + inline const SvXMLImportItemMapper& GetTableItemMapper() const; + inline SvXMLImportItemMapper& GetTableItemMapper(); + SvXMLImportContext *CreateTableItemImportContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList, + XmlStyleFamily nSubFamily, SfxItemSet& rItemSet ); + + const SvXMLTokenMap& GetDocElemTokenMap(); + const SvXMLTokenMap& GetTableElemTokenMap(); + const SvXMLTokenMap& GetTableCellAttrTokenMap(); + + bool FindAutomaticStyle( XmlStyleFamily nFamily, + const OUString& rName, + const SfxItemSet **ppItemSet ) const; + void MergeListsAtDocumentInsertPosition(SwDoc *pDoc); + + virtual void SetStatistics( + const css::uno::Sequence< css::beans::NamedValue> & i_rStats) override; + virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override; + virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aConfigProps) override; + virtual void SetDocumentSpecificSettings(const OUString& _rSettingsGroupName, + const css::uno::Sequence<css::beans::PropertyValue>& _rSettings) override; + + // initialize XForms + virtual void initXForms() override; + + // get the document properties, but only if they actually need importing + css::uno::Reference<css::document::XDocumentProperties> + GetDocumentProperties() const; + + virtual void NotifyEmbeddedFontRead() override; + + const SwDoc* getDoc() const; + SwDoc* getDoc(); +}; + +inline const SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper() const +{ + return *m_pTableItemMapper; +} + +inline SvXMLImportItemMapper& SwXMLImport::GetTableItemMapper() +{ + return *m_pTableItemMapper; +} + +#endif // _XMLIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimpit.cxx b/sw/source/filter/xml/xmlimpit.cxx new file mode 100644 index 000000000..476865ec5 --- /dev/null +++ b/sw/source/filter/xml/xmlimpit.cxx @@ -0,0 +1,957 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "xmlimpit.hxx" + +#include <sax/tools/converter.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include <svl/itempool.hxx> +#include <svl/poolitem.hxx> +#include <svl/itemset.hxx> +#include <xmloff/nmspmap.hxx> +#include <editeng/xmlcnitm.hxx> +#include <editeng/memberids.h> +#include <osl/diagnose.h> + +#include <hintids.hxx> +#include <unomid.h> +#include <svx/unomid.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdir.hxx> +#include <fmtpdsc.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> + +#include <xmloff/prhdlfac.hxx> +#include <xmloff/xmltypes.hxx> +#include <xmloff/xmlprhdl.hxx> +#include "xmlithlp.hxx" +#include <com/sun/star/uno/Any.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace ::xmloff::token; +using uno::Any; + +static const sal_uInt16 nUnknownWhich = RES_UNKNOWNATR_CONTAINER; + +SvXMLImportItemMapper::SvXMLImportItemMapper( + SvXMLItemMapEntriesRef const & rMapEntries ) : + mrMapEntries( rMapEntries ) +{ +} + +SvXMLImportItemMapper::~SvXMLImportItemMapper() +{ +} + +void +SvXMLImportItemMapper::setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) +{ + mrMapEntries = std::move(rMapEntries); +} + +// fills the given itemset with the attributes in the given list +void SvXMLImportItemMapper::importXML( SfxItemSet& rSet, + uno::Reference< xml::sax::XAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ) +{ + sal_Int16 nAttr = xAttrList->getLength(); + + std::unique_ptr<SvXMLAttrContainerItem> pUnknownItem; + for( sal_Int16 i=0; i < nAttr; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + OUString aLocalName, aPrefix, aNamespace; + sal_uInt16 nPrefix = + rNamespaceMap.GetKeyByAttrName( rAttrName, &aPrefix, &aLocalName, + &aNamespace ); + if( XML_NAMESPACE_XMLNS == nPrefix ) + continue; + + const OUString& rValue = xAttrList->getValueByIndex( i ); + + // find a map entry for this attribute + SvXMLItemMapEntry const * pEntry = mrMapEntries->getByName( nPrefix, aLocalName ); + + if( pEntry ) + { + // we have a valid map entry here, so lets use it... + if( 0 == (pEntry->nMemberId & (MID_SW_FLAG_NO_ITEM_IMPORT| + MID_SW_FLAG_ELEMENT_ITEM_IMPORT)) ) + { + // first get item from itemset + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = rSet.GetItemState( pEntry->nWhichId, true, + &pItem ); + + // if it's not set, try the pool + if (SfxItemState::SET != eState && SfxItemPool::IsWhich(pEntry->nWhichId)) + pItem = &rSet.GetPool()->GetDefaultItem(pEntry->nWhichId); + + // do we have an item? + if(eState >= SfxItemState::DEFAULT && pItem) + { + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + bool bPut = false; + + if( 0 == (pEntry->nMemberId&MID_SW_FLAG_SPECIAL_ITEM_IMPORT) ) + { + bPut = PutXMLValue( *pNewItem, rValue, + static_cast<sal_uInt16>( pEntry->nMemberId & MID_SW_FLAG_MASK ), + rUnitConverter ); + + } + else + { + bPut = handleSpecialItem( *pEntry, *pNewItem, rSet, + rValue, rUnitConverter ); + } + + if( bPut ) + rSet.Put( *pNewItem ); + } + else + { + OSL_FAIL( "Could not get a needed item for xml import!" ); + } + } + else if( 0 != (pEntry->nMemberId & MID_SW_FLAG_NO_ITEM_IMPORT) ) + { + handleNoItem( *pEntry, rSet, rValue, rUnitConverter, + rNamespaceMap ); + } + } + else if (USHRT_MAX != nUnknownWhich && !aLocalName.isEmpty()) + { + if( !pUnknownItem ) + { + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( nUnknownWhich, true, + &pItem ) ) + { + pUnknownItem.reset( static_cast<SvXMLAttrContainerItem*>( pItem->Clone() ) ); + } + else + { + pUnknownItem.reset( new SvXMLAttrContainerItem( nUnknownWhich ) ); + } + } + if( pUnknownItem ) + { + if( XML_NAMESPACE_NONE == nPrefix ) + pUnknownItem->AddAttr( aLocalName, rValue ); + else + pUnknownItem->AddAttr( aPrefix, aNamespace, aLocalName, + rValue ); + } + } + } + + if( pUnknownItem ) + { + rSet.Put( *pUnknownItem ); + } + + finished(rSet, rUnitConverter); +} + +/** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */ +bool +SvXMLImportItemMapper::handleSpecialItem( const SvXMLItemMapEntry& /*rEntry*/, + SfxPoolItem& /*rItem*/, + SfxItemSet& /*rSet*/, + const OUString& /*rValue*/, + const SvXMLUnitConverter& /*rUnitConverter*/ ) +{ + OSL_FAIL( "unsupported special item in xml import" ); + return false; +} + +/** this method is called for every item that has the + MID_SW_FLAG_NO_ITEM_IMPORT flag set */ +bool SvXMLImportItemMapper::handleNoItem( const SvXMLItemMapEntry& /*rEntry*/, + SfxItemSet& /*rSet*/, + const OUString& /*rValue*/, + const SvXMLUnitConverter& /*rUnitConverter*/, + const SvXMLNamespaceMap& /*rNamespaceMap*/ ) +{ + OSL_FAIL( "unsupported no item in xml import" ); + return false; +} + +void +SvXMLImportItemMapper::finished(SfxItemSet &, SvXMLUnitConverter const&) const +{ + // nothing to do here +} + +namespace { + +struct BoxHolder +{ + std::unique_ptr<SvxBorderLine> pTop; + std::unique_ptr<SvxBorderLine> pBottom; + std::unique_ptr<SvxBorderLine> pLeft; + std::unique_ptr<SvxBorderLine> pRight; + + BoxHolder(BoxHolder const&) = delete; + BoxHolder& operator=(BoxHolder const&) = delete; + + explicit BoxHolder(SvxBoxItem const & rBox) + { + if (rBox.GetTop()) + pTop.reset(new SvxBorderLine( *rBox.GetTop() )); + if (rBox.GetBottom()) + pBottom.reset(new SvxBorderLine( *rBox.GetBottom() )); + if (rBox.GetLeft()) + pLeft.reset(new SvxBorderLine( *rBox.GetLeft() )); + if (rBox.GetRight()) + pRight.reset(new SvxBorderLine( *rBox.GetRight() )); + } +}; + +} + +// put an XML-string value into an item +bool SvXMLImportItemMapper::PutXMLValue( + SfxPoolItem& rItem, + const OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ) +{ + bool bOk = false; + + switch (rItem.Which()) + { + case RES_LR_SPACE: + { + SvxLRSpaceItem& rLRSpace = dynamic_cast<SvxLRSpaceItem&>(rItem); + + switch( nMemberId ) + { + case MID_L_MARGIN: + case MID_R_MARGIN: + { + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent(nProp, rValue); + else + bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue); + + if( bOk ) + { + switch( nMemberId ) + { + case MID_L_MARGIN: + rLRSpace.SetTextLeft( nAbs, static_cast<sal_uInt16>(nProp) ); + break; + case MID_R_MARGIN: + rLRSpace.SetRight( nAbs, static_cast<sal_uInt16>(nProp) ); + break; + } + } + } + break; + + case MID_FIRST_LINE_INDENT: + { + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent(nProp, rValue); + else + bOk = rUnitConverter.convertMeasureToCore(nAbs, rValue, + -0x7fff, 0x7fff ); + + rLRSpace.SetTextFirstLineOffset( static_cast<short>(nAbs), static_cast<sal_uInt16>(nProp) ); + } + break; + + case MID_FIRST_AUTO: + { + bool bAutoFirst(false); + bOk = ::sax::Converter::convertBool( bAutoFirst, rValue ); + if( bOk ) + rLRSpace.SetAutoFirst( bAutoFirst ); + } + break; + + default: + OSL_FAIL( "unknown member id!"); + } + } + break; + + case RES_UL_SPACE: + { + SvxULSpaceItem& rULSpace = dynamic_cast<SvxULSpaceItem&>(rItem); + + sal_Int32 nProp = 100; + sal_Int32 nAbs = 0; + + if( rValue.indexOf( '%' ) != -1 ) + bOk = ::sax::Converter::convertPercent( nProp, rValue ); + else + bOk = rUnitConverter.convertMeasureToCore( nAbs, rValue ); + + switch( nMemberId ) + { + case MID_UP_MARGIN: + rULSpace.SetUpper( static_cast<sal_uInt16>(nAbs), static_cast<sal_uInt16>(nProp) ); + break; + case MID_LO_MARGIN: + rULSpace.SetLower( static_cast<sal_uInt16>(nAbs), static_cast<sal_uInt16>(nProp) ); + break; + default: + OSL_FAIL("unknown MemberId"); + } + } + break; + + case RES_SHADOW: + { + SvxShadowItem& rShadow = dynamic_cast<SvxShadowItem&>(rItem); + + bool bColorFound = false; + bool bOffsetFound = false; + + SvXMLTokenEnumerator aTokenEnum( rValue ); + + Color aColor( 128,128, 128 ); + rShadow.SetLocation( SvxShadowLocation::BottomRight ); + + OUString aToken; + while( aTokenEnum.getNextToken( aToken ) ) + { + if( IsXMLToken( aToken, XML_NONE ) ) + { + rShadow.SetLocation( SvxShadowLocation::NONE ); + bOk = true; + } + else if( !bColorFound && aToken.startsWith("#") ) + { + bOk = ::sax::Converter::convertColor( aColor, aToken ); + if( !bOk ) + return false; + + bColorFound = true; + } + else if( !bOffsetFound ) + { + sal_Int32 nX = 0, nY = 0; + + bOk = rUnitConverter.convertMeasureToCore( nX, aToken ); + if( bOk && aTokenEnum.getNextToken( aToken ) ) + bOk = rUnitConverter.convertMeasureToCore( nY, aToken ); + + if( bOk ) + { + if( nX < 0 ) + { + if( nY < 0 ) + { + rShadow.SetLocation( SvxShadowLocation::TopLeft ); + } + else + { + rShadow.SetLocation( SvxShadowLocation::BottomLeft ); + } + } + else + { + if( nY < 0 ) + { + rShadow.SetLocation( SvxShadowLocation::TopRight ); + } + else + { + rShadow.SetLocation( SvxShadowLocation::BottomRight ); + } + } + + if( nX < 0 ) nX *= -1; + if( nY < 0 ) nY *= -1; + + rShadow.SetWidth( static_cast< sal_uInt16 >( (nX + nY) >> 1 ) ); + } + } + } + + if( bOk && ( bColorFound || bOffsetFound ) ) + { + rShadow.SetColor(aColor); + } + else + bOk = false; + } + break; + + case RES_BOX: + { + SvxBoxItem& rBox = dynamic_cast<SvxBoxItem&>(rItem); + + // copy SvxBorderLines + BoxHolder aBoxes(rBox); + + sal_Int32 nTemp; + + switch( nMemberId ) + { + case ALL_BORDER_PADDING: + case LEFT_BORDER_PADDING: + case RIGHT_BORDER_PADDING: + case TOP_BORDER_PADDING: + case BOTTOM_BORDER_PADDING: + if (!rUnitConverter.convertMeasureToCore( nTemp, rValue, + 0, 0xffff )) + { + return false; + } + + if( nMemberId == LEFT_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( static_cast<sal_uInt16>(nTemp), SvxBoxItemLine::LEFT ); + if( nMemberId == RIGHT_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( static_cast<sal_uInt16>(nTemp), SvxBoxItemLine::RIGHT ); + if( nMemberId == TOP_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( static_cast<sal_uInt16>(nTemp), SvxBoxItemLine::TOP ); + if( nMemberId == BOTTOM_BORDER_PADDING || + nMemberId == ALL_BORDER_PADDING ) + rBox.SetDistance( static_cast<sal_uInt16>(nTemp), SvxBoxItemLine::BOTTOM); + break; + + case ALL_BORDER: + case LEFT_BORDER: + case RIGHT_BORDER: + case TOP_BORDER: + case BOTTOM_BORDER: + { + bool bHasStyle = false; + bool bHasWidth = false; + bool bHasColor = false; + + sal_uInt16 nStyle = USHRT_MAX; + sal_uInt16 nWidth = 0; + sal_uInt16 nNamedWidth = USHRT_MAX; + + Color aColor( COL_BLACK ); + + if( !sw_frmitems_parseXMLBorder( rValue, rUnitConverter, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ) ) + return false; + + if( TOP_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pTop, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( BOTTOM_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pBottom, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( LEFT_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pLeft, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + + if( RIGHT_BORDER == nMemberId || ALL_BORDER == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pRight, + bHasStyle, nStyle, + bHasWidth, nWidth, nNamedWidth, + bHasColor, aColor ); + } + break; + case ALL_BORDER_LINE_WIDTH: + case LEFT_BORDER_LINE_WIDTH: + case RIGHT_BORDER_LINE_WIDTH: + case TOP_BORDER_LINE_WIDTH: + case BOTTOM_BORDER_LINE_WIDTH: + { + SvXMLTokenEnumerator aTokenEnum( rValue ); + + sal_Int32 nInWidth, nDistance, nOutWidth; + + OUString aToken; + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nInWidth, aToken)) + return false; + + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nDistance, aToken)) + return false; + + if( !aTokenEnum.getNextToken( aToken ) ) + return false; + + if (!rUnitConverter.convertMeasureToCore(nOutWidth, aToken)) + return false; + + // #i61946: accept line style even it's not part of our "normal" set of line styles + sal_uInt16 nWidth = 0; + + if( TOP_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pTop, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( BOTTOM_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pBottom, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( LEFT_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pLeft, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + + if( RIGHT_BORDER_LINE_WIDTH == nMemberId || + ALL_BORDER_LINE_WIDTH == nMemberId ) + sw_frmitems_setXMLBorder( aBoxes.pRight, nWidth, + static_cast< sal_uInt16 >( nOutWidth ), + static_cast< sal_uInt16 >( nInWidth ), + static_cast< sal_uInt16 >( nDistance ) ); + } + break; + } + + rBox.SetLine( aBoxes.pTop.get(), SvxBoxItemLine::TOP ); + rBox.SetLine( aBoxes.pBottom.get(), SvxBoxItemLine::BOTTOM ); + rBox.SetLine( aBoxes.pLeft.get(), SvxBoxItemLine::LEFT ); + rBox.SetLine( aBoxes.pRight.get(), SvxBoxItemLine::RIGHT ); + + bOk = true; + } + break; + + case RES_BREAK: + { + SvxFormatBreakItem& rFormatBreak = dynamic_cast<SvxFormatBreakItem&>(rItem); + sal_uInt16 eEnum{}; + + if( !SvXMLUnitConverter::convertEnum( eEnum, rValue, psXML_BreakType ) ) + return false; + + if( eEnum == 0 ) + { + rFormatBreak.SetValue( SvxBreak::NONE ); + bOk = true; + } + else + { + switch( nMemberId ) + { + case MID_BREAK_BEFORE: + rFormatBreak.SetValue( eEnum == 1 ? + SvxBreak::ColumnBefore : + SvxBreak::PageBefore ); + break; + case MID_BREAK_AFTER: + rFormatBreak.SetValue( eEnum == 1 ? + SvxBreak::ColumnAfter : + SvxBreak::PageAfter ); + break; + } + bOk = true; + } + } + break; + + case RES_KEEP: + { + SvxFormatKeepItem& rFormatKeep = dynamic_cast<SvxFormatKeepItem&>(rItem); + + if( IsXMLToken( rValue, XML_ALWAYS ) || + IsXMLToken( rValue, XML_TRUE ) ) + { + rFormatKeep.SetValue( true ); + bOk = true; + } + else if( IsXMLToken( rValue, XML_AUTO ) || + IsXMLToken( rValue, XML_FALSE ) ) + { + rFormatKeep.SetValue( false ); + bOk = true; + } + } + break; + + case RES_BACKGROUND: + { + SvxBrushItem& rBrush = dynamic_cast<SvxBrushItem&>(rItem); + + sal_Int32 nTempColor(0); + switch( nMemberId ) + { + case MID_BACK_COLOR: + if( IsXMLToken( rValue, XML_TRANSPARENT ) ) + { + rBrush.GetColor().SetTransparency(0xff); + bOk = true; + } + else if (::sax::Converter::convertColor(nTempColor, rValue)) + { + Color aTempColor(nTempColor); + aTempColor.SetTransparency(0); + rBrush.SetColor( aTempColor ); + bOk = true; + } + break; + + case MID_GRAPHIC_REPEAT: + { + SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos(); + SvxGraphicPosition nPos = GPOS_NONE; + if( SvXMLUnitConverter::convertEnum( nPos, rValue, + psXML_BrushRepeat ) ) + { + if( GPOS_MM != nPos || GPOS_NONE == eGraphicPos || + GPOS_AREA == eGraphicPos || GPOS_TILED == eGraphicPos ) + rBrush.SetGraphicPos( nPos ); + bOk = true; + } + } + break; + + case MID_GRAPHIC_POSITION: + { + SvxGraphicPosition ePos = GPOS_NONE, eTmp; + SvxGraphicPosition nTmp; + SvXMLTokenEnumerator aTokenEnum( rValue ); + OUString aToken; + bool bHori = false, bVert = false; + bOk = true; + while( bOk && aTokenEnum.getNextToken( aToken ) ) + { + if( bHori && bVert ) + { + bOk = false; + } + else if( -1 != aToken.indexOf( '%' ) ) + { + sal_Int32 nPrc = 50; + if (::sax::Converter::convertPercent(nPrc, aToken)) + { + if( !bHori ) + { + ePos = nPrc < 25 ? GPOS_LT : + (nPrc < 75 ? GPOS_MM : GPOS_RB); + bHori = true; + } + else + { + eTmp = nPrc < 25 ? GPOS_LT: + (nPrc < 75 ? GPOS_LM : GPOS_LB); + sw_frmitems_MergeXMLVertPos( ePos, eTmp ); + bVert = true; + } + } + else + { + // wrong percentage + bOk = false; + } + } + else if( IsXMLToken( aToken, XML_CENTER ) ) + { + if( bHori ) + sw_frmitems_MergeXMLVertPos( ePos, GPOS_MM ); + else if ( bVert ) + sw_frmitems_MergeXMLHoriPos( ePos, GPOS_MM ); + else + ePos = GPOS_MM; + } + else if( SvXMLUnitConverter::convertEnum( nTmp, aToken, + psXML_BrushHoriPos ) ) + { + if( bVert ) + sw_frmitems_MergeXMLHoriPos( + ePos, nTmp ); + else if( !bHori ) + ePos = nTmp; + else + bOk = false; + bHori = true; + } + else if( SvXMLUnitConverter::convertEnum( nTmp, aToken, + psXML_BrushVertPos ) ) + { + if( bHori ) + sw_frmitems_MergeXMLVertPos( + ePos, nTmp ); + else if( !bVert ) + ePos = nTmp; + else + bOk = false; + bVert = true; + } + else + { + bOk = false; + } + } + + if( GPOS_NONE == ePos ) bOk = false; + if( bOk ) + rBrush.SetGraphicPos( ePos ); + } + break; + + case MID_GRAPHIC_FILTER: + rBrush.SetGraphicFilter( rValue ); + bOk = true; + break; + } + } + break; + + case RES_PAGEDESC: + { + SwFormatPageDesc& rPageDesc = dynamic_cast<SwFormatPageDesc&>(rItem); + + if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId ) + { + sal_Int32 nVal; + bOk = ::sax::Converter::convertNumber( + nVal, rValue, 0, USHRT_MAX); + // i#114163 tdf#77111: OOo < 3.3 had a bug where it wrote + // "auto" as "0" for tables - now that we support a real offset + // 0, this fake "0" MUST NOT be imported as offset 0! + if( bOk && nVal > 0 ) + rPageDesc.SetNumOffset( static_cast<sal_uInt16>(nVal) ); + } + } + break; + + case RES_LAYOUT_SPLIT: + case RES_ROW_SPLIT: + { + SfxBoolItem& rSplit = dynamic_cast<SfxBoolItem&>(rItem); + + if( IsXMLToken( rValue, XML_AUTO ) || + IsXMLToken( rValue, XML_TRUE ) ) + { + rSplit.SetValue( true ); + bOk = true; + } + else if( IsXMLToken( rValue, XML_ALWAYS ) || + IsXMLToken( rValue, XML_FALSE ) ) + { + rSplit.SetValue( false ); + bOk = true; + } + } + break; + + case RES_HORI_ORIENT: + { + SwFormatHoriOrient& rHoriOrient = dynamic_cast<SwFormatHoriOrient&>(rItem); + + sal_Int16 nValue; + bOk = SvXMLUnitConverter::convertEnum( nValue, rValue, + aXMLTableAlignMap ); + if( bOk ) + rHoriOrient.SetHoriOrient( nValue ); + } + break; + + case RES_VERT_ORIENT: + { + SwFormatVertOrient& rVertOrient = dynamic_cast<SwFormatVertOrient&>(rItem); + + sal_Int16 nValue; + bOk = SvXMLUnitConverter::convertEnum( nValue, rValue, + aXMLTableVAlignMap ); + if( bOk ) + rVertOrient.SetVertOrient( nValue ); + //#i8855# text::VertOrientation::NONE is stored as empty string and should be applied here + else if(rValue.isEmpty()) + { + rVertOrient.SetVertOrient( text::VertOrientation::NONE ); + bOk = true; + } + } + break; + + case RES_FRM_SIZE: + { + SwFormatFrameSize& rFrameSize = dynamic_cast<SwFormatFrameSize&>(rItem); + + bool bSetHeight = false; + bool bSetWidth = false; + bool bSetSizeType = false; + SwFrameSize eSizeType = SwFrameSize::Variable; + sal_Int32 nMin = MINLAY; + + switch( nMemberId ) + { + case MID_FRMSIZE_REL_WIDTH: + { + sal_Int32 nValue; + bOk = ::sax::Converter::convertPercent( nValue, rValue ); + if( bOk ) + { + if( nValue < 1 ) + nValue = 1; + else if( nValue > 100 ) + nValue = 100; + + rFrameSize.SetWidthPercent( static_cast<sal_Int8>(nValue) ); + } + } + break; + case MID_FRMSIZE_WIDTH: + bSetWidth = true; + break; + case MID_FRMSIZE_MIN_HEIGHT: + eSizeType = SwFrameSize::Minimum; + bSetHeight = true; + nMin = 1; + bSetSizeType = true; + break; + case MID_FRMSIZE_FIX_HEIGHT: + eSizeType = SwFrameSize::Fixed; + bSetHeight = true; + nMin = 1; + bSetSizeType = true; + break; + case MID_FRMSIZE_COL_WIDTH: + eSizeType = SwFrameSize::Fixed; + bSetWidth = true; + bSetSizeType = true; + break; + case MID_FRMSIZE_REL_COL_WIDTH: + { + sal_Int32 nPos = rValue.indexOf( '*' ); + if( -1 != nPos ) + { + sal_Int32 nValue = rValue.toInt32(); + if( nValue < MINLAY ) + nValue = MINLAY; + else if( nValue > SAL_MAX_UINT16 ) + nValue = SAL_MAX_UINT16; + + rFrameSize.SetWidth( static_cast<sal_uInt16>(nValue) ); + rFrameSize.SetHeightSizeType( SwFrameSize::Variable ); + bOk = true; + } + } + break; + } + + sal_Int32 nValue; + if( bSetHeight || bSetWidth ) + { + bOk = rUnitConverter.convertMeasureToCore(nValue, rValue, nMin, + USHRT_MAX ); + if( bOk ) + { + if( bSetWidth ) + rFrameSize.SetWidth( static_cast<sal_uInt16>(nValue) ); + if( bSetHeight ) + rFrameSize.SetHeight( static_cast<sal_uInt16>(nValue) ); + if( bSetSizeType ) + rFrameSize.SetHeightSizeType( eSizeType ); + } + } + } + break; + + case RES_FRAMEDIR: + { + if (IsXMLToken(rValue, XML_BT_LR)) + { + // Read bt-lr from the extension namespace, handle other values + // below. + Any aAny; + aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_LR_BT); + bOk = rItem.PutValue(aAny, 0); + } + else + { + std::unique_ptr<XMLPropertyHandler> pWritingModeHandler = + XMLPropertyHandlerFactory::CreatePropertyHandler( + XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT ); + Any aAny; + bOk = pWritingModeHandler->importXML( rValue, aAny, + rUnitConverter ); + if( bOk ) + bOk = rItem.PutValue( aAny, 0 ); + } + } + break; + + case RES_COLLAPSING_BORDERS: + { + SfxBoolItem& rBorders = dynamic_cast<SfxBoolItem&>(rItem); + + if( IsXMLToken( rValue, XML_COLLAPSING ) ) + { + rBorders.SetValue(true); + bOk = true; + } + else if( IsXMLToken( rValue, XML_SEPARATING ) ) + { + rBorders.SetValue(false); + bOk = true; + } + else + bOk = false; + } + break; + + default: + OSL_FAIL("Item not implemented!"); + break; + } + + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlimpit.hxx b/sw/source/filter/xml/xmlimpit.hxx new file mode 100644 index 000000000..ff6920faa --- /dev/null +++ b/sw/source/filter/xml/xmlimpit.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_FILTER_XML_XMLIMPIT_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX + +#include <com/sun/star/xml/sax/XAttributeList.hpp> +#include "xmlitmap.hxx" + +class SvXMLUnitConverter; +class SfxPoolItem; +class SfxItemSet; +class SvXMLNamespaceMap; +struct SvXMLItemMapEntry; + +class SvXMLImportItemMapper +{ +protected: + SvXMLItemMapEntriesRef mrMapEntries; + +public: + explicit SvXMLImportItemMapper( SvXMLItemMapEntriesRef const & rMapEntries ); + virtual ~SvXMLImportItemMapper(); + + /** fills the given itemset with the attributes in the given list */ + void importXML( SfxItemSet& rSet, + css::uno::Reference< css::xml::sax::XAttributeList > const & xAttrList, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ); + + /** this method is called for every item that has the + MID_SW_FLAG_SPECIAL_ITEM_IMPORT flag set */ + virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter ); + + /** this method is called for every item that has the + MID_SW_FLAG_NO_ITEM_IMPORT flag set */ + virtual bool handleNoItem( const SvXMLItemMapEntry& rEntry, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ); + + /** This method is called when all attributes have benn processed. It + * may be used to remove items that are incomplete */ + virtual void finished(SfxItemSet & rSet, + SvXMLUnitConverter const& rUnitConverter) const; + + virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ); + inline const SvXMLItemMapEntriesRef& getMapEntries() const; + + /** This method is called for every item that should be set based + upon an XML attribute value. */ + static bool PutXMLValue( + SfxPoolItem& rItem, + const OUString& rValue, + sal_uInt16 nMemberId, + const SvXMLUnitConverter& rUnitConverter ); +}; + +inline const SvXMLItemMapEntriesRef& +SvXMLImportItemMapper::getMapEntries() const +{ + return mrMapEntries; +} + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLIMPIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitem.cxx b/sw/source/filter/xml/xmlitem.cxx new file mode 100644 index 000000000..4d12335a1 --- /dev/null +++ b/sw/source/filter/xml/xmlitem.cxx @@ -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 . + */ + +#include <xmloff/xmlimp.hxx> +#include "xmlimpit.hxx" +#include "xmlitem.hxx" + +using namespace ::com::sun::star; + +SvXMLItemSetContext::SvXMLItemSetContext( SvXMLImport& rImp, sal_uInt16 nPrfx, + const OUString& rLName, + const uno::Reference< xml::sax::XAttributeList >& xAttrList, + SfxItemSet& rISet, + SvXMLImportItemMapper& rIMap, + const SvXMLUnitConverter& rUnitConverter ): + SvXMLImportContext( rImp, nPrfx, rLName ), + rItemSet( rISet ), + rIMapper( rIMap ), + rUnitConv( rUnitConverter ) +{ + rIMap.importXML( rItemSet, xAttrList, rUnitConv, + GetImport().GetNamespaceMap() ); +} + +SvXMLItemSetContext::~SvXMLItemSetContext() +{ +} + +SvXMLImportContextRef SvXMLItemSetContext::CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const uno::Reference< xml::sax::XAttributeList >& xAttrList ) +{ + SvXMLItemMapEntriesRef xMapEntries = rIMapper.getMapEntries(); + SvXMLItemMapEntry const * pEntry = xMapEntries->getByName( nPrefix, rLocalName ); + + if( pEntry && 0 != (pEntry->nMemberId & MID_SW_FLAG_ELEMENT_ITEM_IMPORT) ) + { + return CreateChildContext( nPrefix, rLocalName, xAttrList, + rItemSet, *pEntry, rUnitConv ); + } + return nullptr; +} + +/** This method is called from this instance implementation of + CreateChildContext if the element matches an entry in the + SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT +*/ +SvXMLImportContextRef SvXMLItemSetContext::CreateChildContext( sal_uInt16 /*nPrefix*/, + const OUString& /*rLocalName*/, + const uno::Reference< xml::sax::XAttributeList >& /*xAttrList*/, + SfxItemSet& /*rItemSet*/, + const SvXMLItemMapEntry& /*rEntry*/, + const SvXMLUnitConverter& /*rUnitConv*/ ) +{ + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitem.hxx b/sw/source/filter/xml/xmlitem.hxx new file mode 100644 index 000000000..4f06cfc94 --- /dev/null +++ b/sw/source/filter/xml/xmlitem.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLITEM_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX + +#include <com/sun/star/xml/sax/XAttributeList.hpp> +#include <xmloff/xmlictxt.hxx> + +class SfxItemSet; +class SvXMLImportItemMapper; +class SvXMLUnitConverter; +struct SvXMLItemMapEntry; + +class SvXMLItemSetContext : public SvXMLImportContext +{ +protected: + SfxItemSet &rItemSet; + const SvXMLImportItemMapper &rIMapper; + const SvXMLUnitConverter &rUnitConv; + +public: + + SvXMLItemSetContext( SvXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList, + SfxItemSet& rItemSet, + SvXMLImportItemMapper& rIMap, + const SvXMLUnitConverter& rUnitConv ); + + virtual ~SvXMLItemSetContext() override; + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList ) override; + + // This method is called from this instance implementation of + // CreateChildContext if the element matches an entry in the + // SvXMLImportItemMapper with the mid flag MID_SW_FLAG_ELEMENT_ITEM_IMPORT + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList, + SfxItemSet& rItemSet, + const SvXMLItemMapEntry& rEntry, + const SvXMLUnitConverter& rUnitConv ); + +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITEM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmliteme.cxx b/sw/source/filter/xml/xmliteme.cxx new file mode 100644 index 000000000..3df09efc1 --- /dev/null +++ b/sw/source/filter/xml/xmliteme.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 <com/sun/star/util/MeasureUnit.hpp> + +#include <hintids.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <utility> +#include <xmloff/xmluconv.hxx> +#include "xmlexpit.hxx" +#include <xmloff/nmspmap.hxx> +#include "xmlbrshe.hxx" +#include <editeng/brushitem.hxx> +#include <fmtornt.hxx> +#include <unomid.h> +#include <frmfmt.hxx> +#include "xmlexp.hxx" +#include <editeng/memberids.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +class SwXMLTableItemMapper_Impl: public SvXMLExportItemMapper +{ + SwXMLBrushItemExport aBrushItemExport; + +protected: + + sal_uInt32 nAbsWidth; + + static void AddAttribute( sal_uInt16 nPrefix, enum XMLTokenEnum eLName, + const OUString& rValue, + const SvXMLNamespaceMap& rNamespaceMap, + SvXMLAttributeList& rAttrList ); + +public: + + SwXMLTableItemMapper_Impl( + SvXMLItemMapEntriesRef rMapEntries, + SwXMLExport& rExp ); + + virtual void handleSpecialItem( SvXMLAttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const override; + + virtual void handleElementItem( + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const override; + + inline void SetAbsWidth( sal_uInt32 nAbs ); +}; + +} + +SwXMLTableItemMapper_Impl::SwXMLTableItemMapper_Impl( + SvXMLItemMapEntriesRef rMapEntries, + SwXMLExport& rExp ) : + SvXMLExportItemMapper( std::move(rMapEntries) ), + aBrushItemExport( rExp ), + nAbsWidth( USHRT_MAX ) +{ +} + +void SwXMLTableItemMapper_Impl::AddAttribute( sal_uInt16 nPrefix, + enum XMLTokenEnum eLName, + const OUString& rValue, + const SvXMLNamespaceMap& rNamespaceMap, + SvXMLAttributeList& rAttrList ) +{ + OUString sName( rNamespaceMap.GetQNameByKey( nPrefix, + GetXMLToken(eLName) ) ); + rAttrList.AddAttribute( sName, rValue ); +} + +void SwXMLTableItemMapper_Impl::handleSpecialItem( + SvXMLAttributeList& rAttrList, + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const SfxItemSet *pSet ) const +{ + switch( rEntry.nWhichId ) + { + case RES_LR_SPACE: + { + const SfxPoolItem *pItem; + if( pSet && + SfxItemState::SET == pSet->GetItemState( RES_HORI_ORIENT, true, + &pItem ) ) + { + sal_Int16 eHoriOrient = + static_cast<const SwFormatHoriOrient *>(pItem)->GetHoriOrient(); + bool bExport = false; + sal_uInt16 nMemberId = + static_cast<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK ); + switch( nMemberId ) + { + case MID_L_MARGIN: + bExport = text::HoriOrientation::NONE == eHoriOrient || + text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient; + break; + case MID_R_MARGIN: + bExport = text::HoriOrientation::NONE == eHoriOrient; + break; + } + OUString sValue; + if( bExport && SvXMLExportItemMapper::QueryXMLValue( + rItem, sValue, nMemberId, rUnitConverter ) ) + { + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, sValue, + rNamespaceMap, rAttrList ); + } + } + } + break; + + case RES_FRM_SIZE: + { + sal_uInt16 nMemberId = + static_cast<sal_uInt16>( rEntry.nMemberId & MID_SW_FLAG_MASK ); + switch( nMemberId ) + { + case MID_FRMSIZE_WIDTH: + if( nAbsWidth ) + { + OUStringBuffer sBuffer; + rUnitConverter.convertMeasureToXML( sBuffer, nAbsWidth ); + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, + sBuffer.makeStringAndClear(), + rNamespaceMap, rAttrList ); + } + break; + case MID_FRMSIZE_REL_WIDTH: + { + OUString sValue; + if( SvXMLExportItemMapper::QueryXMLValue( + rItem, sValue, nMemberId, rUnitConverter ) ) + { + AddAttribute( rEntry.nNameSpace, rEntry.eLocalName, + sValue, rNamespaceMap, rAttrList ); + } + } + break; + } + } + break; + } +} + +/** this method is called for every item that has the + MID_SW_FLAG_ELEMENT_EXPORT flag set */ +void SwXMLTableItemMapper_Impl::handleElementItem( + const SvXMLItemMapEntry& rEntry, + const SfxPoolItem& rItem ) const +{ + switch( rEntry.nWhichId ) + { + case RES_BACKGROUND: + { + const_cast<SwXMLTableItemMapper_Impl *>(this)->aBrushItemExport.exportXML( + static_cast<const SvxBrushItem&>(rItem) ); + } + break; + } +} + +inline void SwXMLTableItemMapper_Impl::SetAbsWidth( sal_uInt32 nAbs ) +{ + nAbsWidth = nAbs; +} + +void SwXMLExport::InitItemExport() +{ + m_pTwipUnitConverter.reset(new SvXMLUnitConverter(getComponentContext(), + util::MeasureUnit::TWIP, GetMM100UnitConverter().GetXMLMeasureUnit())); + + m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap ); + m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap ); + m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap ); + + m_pTableItemMapper.reset(new SwXMLTableItemMapper_Impl( m_xTableItemMap, *this )); +} + +void SwXMLExport::FinitItemExport() +{ + m_pTableItemMapper.reset(); + m_pTwipUnitConverter.reset(); +} + +void SwXMLExport::ExportTableFormat( const SwFrameFormat& rFormat, sal_uInt32 nAbsWidth ) +{ + static_cast<SwXMLTableItemMapper_Impl *>(m_pTableItemMapper.get()) + ->SetAbsWidth( nAbsWidth ); + ExportFormat( rFormat, XML_TABLE ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitemi.cxx b/sw/source/filter/xml/xmlitemi.cxx new file mode 100644 index 000000000..9c1c8abb0 --- /dev/null +++ b/sw/source/filter/xml/xmlitemi.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 <rtl/ustring.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/util/MeasureUnit.hpp> + +#include <xmloff/xmluconv.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltoken.hxx> + +#include <editeng/brushitem.hxx> +#include <editeng/memberids.h> +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> + +#include <hintids.hxx> +#include <unomid.h> +#include "xmlbrshi.hxx" +#include "xmlimp.hxx" +#include "xmlitmap.hxx" +#include "xmlimpit.hxx" +#include "xmlitem.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +class SwXMLImportTableItemMapper_Impl: public SvXMLImportItemMapper +{ + +public: + + explicit SwXMLImportTableItemMapper_Impl(const SvXMLItemMapEntriesRef& rMapEntries); + + virtual bool handleSpecialItem( const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter ) override; + + virtual bool + handleNoItem(SvXMLItemMapEntry const& rEntry, + SfxItemSet & rSet, + OUString const& rValue, + SvXMLUnitConverter const& rUnitConverter, + SvXMLNamespaceMap const& rNamespaceMap) override; + + virtual void finished(SfxItemSet & rSet, + SvXMLUnitConverter const& rUnitConverter) const override; + + virtual void setMapEntries( SvXMLItemMapEntriesRef rMapEntries ) override; + +private: + void Reset(); + + OUString m_FoMarginValue; + enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3 }; + bool m_bHaveMargin[4]; +}; + +} + +SwXMLImportTableItemMapper_Impl::SwXMLImportTableItemMapper_Impl( + const SvXMLItemMapEntriesRef& rMapEntries ) : + SvXMLImportItemMapper( rMapEntries ) +{ + Reset(); +} + +void SwXMLImportTableItemMapper_Impl::Reset() +{ + m_FoMarginValue.clear(); + for (int i = 0; i < 3; ++i) + { + m_bHaveMargin[i] = false; + } +} + +void SwXMLImportTableItemMapper_Impl::setMapEntries( + SvXMLItemMapEntriesRef rMapEntries ) +{ + Reset(); + SvXMLImportItemMapper::setMapEntries(rMapEntries); +} + +bool SwXMLImportTableItemMapper_Impl::handleSpecialItem( + const SvXMLItemMapEntry& rEntry, + SfxPoolItem& rItem, + SfxItemSet& rItemSet, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConv ) +{ + bool bRet = false; + sal_uInt16 nMemberId = static_cast< sal_Int16 >(rEntry.nMemberId & MID_SW_FLAG_MASK); + switch( rItem.Which() ) + { + case RES_LR_SPACE: + switch (nMemberId) + { + case MID_L_MARGIN: + m_bHaveMargin[LEFT] = true; + break; + case MID_R_MARGIN: + m_bHaveMargin[RIGHT] = true; + break; + } + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv); + break; + case RES_UL_SPACE: + switch (nMemberId) + { + case MID_UP_MARGIN: + m_bHaveMargin[TOP] = true; + break; + case MID_LO_MARGIN: + m_bHaveMargin[BOTTOM] = true; + break; + } + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv); + break; + case RES_FRM_SIZE: + switch( nMemberId ) + { + case MID_FRMSIZE_COL_WIDTH: + // If the item is existing already, a relative value has been set + // already that must be preserved. + if( SfxItemState::SET != rItemSet.GetItemState( RES_FRM_SIZE, + false ) ) + bRet = SvXMLImportItemMapper::PutXMLValue( + rItem, rValue, nMemberId, rUnitConv ); + break; + } + } + + return bRet; +} + +bool SwXMLImportTableItemMapper_Impl::handleNoItem( + SvXMLItemMapEntry const& rEntry, + SfxItemSet & rSet, + OUString const& rValue, + SvXMLUnitConverter const& rUnitConverter, + SvXMLNamespaceMap const& rNamespaceMap) +{ + if ((XML_NAMESPACE_FO == rEntry.nNameSpace) && + (xmloff::token::XML_MARGIN == rEntry.eLocalName)) + { + m_FoMarginValue = rValue; + return true; + } + else + { + return SvXMLImportItemMapper::handleNoItem( + rEntry, rSet, rValue, rUnitConverter, rNamespaceMap); + } +} + +void SwXMLImportTableItemMapper_Impl::finished( + SfxItemSet & rSet, SvXMLUnitConverter const& rUnitConverter) const +{ + if (!m_FoMarginValue.isEmpty()) + { + sal_uInt16 const Ids[4][2] = { + { RES_LR_SPACE, MID_L_MARGIN }, + { RES_LR_SPACE, MID_R_MARGIN }, + { RES_UL_SPACE, MID_UP_MARGIN }, + { RES_UL_SPACE, MID_LO_MARGIN }, + }; + for (int i = 0; i < 4; ++i) + { + if (m_bHaveMargin[i]) + { + continue; // already read fo:margin-top etc. + } + // first get item from itemset + SfxPoolItem const* pItem = nullptr; + SfxItemState eState = + rSet.GetItemState(Ids[i][0], true, &pItem); + + // if not set, try the pool + if ((SfxItemState::SET != eState) && SfxItemPool::IsWhich(Ids[i][0])) + { + pItem = &rSet.GetPool()->GetDefaultItem(Ids[i][0]); + } + + // do we have an item? + if (eState >= SfxItemState::DEFAULT && pItem) + { + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + bool const bPut = PutXMLValue( + *pNewItem, m_FoMarginValue, Ids[i][1], rUnitConverter); + if (bPut) + { + rSet.Put(std::move(pNewItem)); + } + } + else + { + OSL_ENSURE(false, "could not get item"); + } + } + } +} + +namespace { + +class SwXMLItemSetContext_Impl : public SvXMLItemSetContext +{ + SvXMLImportContextRef xBackground; + + using SvXMLItemSetContext::CreateChildContext; + +public: + SwXMLItemSetContext_Impl( SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SfxItemSet& rItemSet, + SvXMLImportItemMapper & rIMapper, + const SvXMLUnitConverter& rUnitConv ); + virtual ~SwXMLItemSetContext_Impl() override; + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const ::uno::Reference< xml::sax::XAttributeList > & xAttrList, + SfxItemSet& rItemSet, + const SvXMLItemMapEntry& rEntry, + const SvXMLUnitConverter& rUnitConv ) override; +}; + +} + +SwXMLItemSetContext_Impl::SwXMLItemSetContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SfxItemSet& _rItemSet, + SvXMLImportItemMapper & _rIMapper, + const SvXMLUnitConverter& _rUnitConv ) : + SvXMLItemSetContext( rImport, nPrfx, rLName, xAttrList, + _rItemSet, _rIMapper, _rUnitConv ) +{ +} + +SwXMLItemSetContext_Impl::~SwXMLItemSetContext_Impl() +{ + if( xBackground.is() ) + { + const SvxBrushItem& rItem = + static_cast<SwXMLBrushItemImportContext*>(xBackground.get())->GetItem(); + rItemSet.Put( rItem ); + } +} + +SvXMLImportContextRef SwXMLItemSetContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SfxItemSet& _rItemSet, + const SvXMLItemMapEntry& rEntry, + const SvXMLUnitConverter& _rUnitConv ) +{ + SvXMLImportContextRef xContext; + + switch( rEntry.nWhichId ) + { + case RES_BACKGROUND: + { + const SfxPoolItem *pItem; + if( SfxItemState::SET == _rItemSet.GetItemState( RES_BACKGROUND, + false, &pItem ) ) + { + xContext = new SwXMLBrushItemImportContext( + GetImport(), nPrefix, rLocalName, xAttrList, + _rUnitConv, *static_cast<const SvxBrushItem *>(pItem) ); + } + else + { + xContext = new SwXMLBrushItemImportContext( + GetImport(), nPrefix, rLocalName, xAttrList, + _rUnitConv, RES_BACKGROUND ); + } + xBackground = xContext; + } + break; + } + + if (!xContext) + xContext = SvXMLItemSetContext::CreateChildContext( nPrefix, rLocalName, + xAttrList, _rItemSet, + rEntry, _rUnitConv ); + + return xContext; +} + +void SwXMLImport::InitItemImport() +{ + m_pTwipUnitConv.reset( new SvXMLUnitConverter( GetComponentContext(), + util::MeasureUnit::TWIP, util::MeasureUnit::TWIP ) ); + + m_xTableItemMap = new SvXMLItemMapEntries( aXMLTableItemMap ); + m_xTableColItemMap = new SvXMLItemMapEntries( aXMLTableColItemMap ); + m_xTableRowItemMap = new SvXMLItemMapEntries( aXMLTableRowItemMap ); + m_xTableCellItemMap = new SvXMLItemMapEntries( aXMLTableCellItemMap ); + + m_pTableItemMapper.reset( new SwXMLImportTableItemMapper_Impl( m_xTableItemMap ) ); +} + +void SwXMLImport::FinitItemImport() +{ + m_pTableItemMapper.reset(); + m_pTwipUnitConv.reset(); +} + +SvXMLImportContext *SwXMLImport::CreateTableItemImportContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList, + XmlStyleFamily nFamily, + SfxItemSet& rItemSet ) +{ + SvXMLItemMapEntriesRef xItemMap; + + switch( nFamily ) + { + case XmlStyleFamily::TABLE_TABLE: + xItemMap = m_xTableItemMap; + break; + case XmlStyleFamily::TABLE_COLUMN: + xItemMap = m_xTableColItemMap; + break; + case XmlStyleFamily::TABLE_ROW: + xItemMap = m_xTableRowItemMap; + break; + case XmlStyleFamily::TABLE_CELL: + xItemMap = m_xTableCellItemMap; + break; + default: break; + } + + m_pTableItemMapper->setMapEntries( xItemMap ); + + return new SwXMLItemSetContext_Impl( *this, nPrefix, rLocalName, + xAttrList, rItemSet, + GetTableItemMapper(), + *m_pTwipUnitConv ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitemm.cxx b/sw/source/filter/xml/xmlitemm.cxx new file mode 100644 index 000000000..e7c093190 --- /dev/null +++ b/sw/source/filter/xml/xmlitemm.cxx @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/memberids.h> +#include <editeng/xmlcnitm.hxx> +#include <hintids.hxx> +#include <svx/unomid.hxx> +#include <xmloff/xmlnmspe.hxx> +#include "xmlitmap.hxx" +#include <xmloff/xmltoken.hxx> + +#include <unomid.h> + +using namespace ::xmloff::token; + +#define MAP_ENTRY( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, static_cast<sal_uInt16>(w), m } +#define M_E_SI( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, static_cast<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m } +#define M_E_SE( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, static_cast<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|m } +#define M_E_SIE( p, l, w, m ) \ + { XML_NAMESPACE_##p, XML_##l, static_cast<sal_uInt16>(w), MID_SW_FLAG_SPECIAL_ITEM_EXPORT|MID_SW_FLAG_SPECIAL_ITEM_IMPORT|m } + +#define M_END { 0, XML_TOKEN_INVALID, 0, 0 } + +SvXMLItemMapEntry const aXMLTableItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + M_E_SE( STYLE, WIDTH, RES_FRM_SIZE, MID_FRMSIZE_WIDTH ), + M_E_SE( STYLE, REL_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_WIDTH ), + // RES_PAPER_BIN + // not required + // TODO: RES_LR_SPACE + M_E_SE( FO, MARGIN, 0xFFFF/*invalid*/, MID_SW_FLAG_NO_ITEM_IMPORT), + M_E_SIE( FO, MARGIN_LEFT, RES_LR_SPACE, MID_L_MARGIN ), + M_E_SIE( FO, MARGIN_RIGHT, RES_LR_SPACE, MID_R_MARGIN ), + // RES_UL_SPACE + M_E_SI( FO, MARGIN_TOP, RES_UL_SPACE, MID_UP_MARGIN ), + M_E_SI( FO, MARGIN_BOTTOM, RES_UL_SPACE, MID_LO_MARGIN ), + // RES_PAGEDESC + MAP_ENTRY( STYLE, PAGE_NUMBER, RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET), + // RES_BREAK + MAP_ENTRY( FO, BREAK_BEFORE, RES_BREAK, MID_BREAK_BEFORE ), + MAP_ENTRY( FO, BREAK_AFTER, RES_BREAK, MID_BREAK_AFTER ), + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // not required + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + // not required + // RES_HORI_ORIENT + MAP_ENTRY( TABLE, ALIGN, RES_HORI_ORIENT, 0 ), + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + // not required + // RES_SHADOW + MAP_ENTRY( STYLE, SHADOW, RES_SHADOW, 0 ), + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + MAP_ENTRY( FO, KEEP_WITH_NEXT, RES_KEEP, 0 ), + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + MAP_ENTRY( STYLE, MAY_BREAK_BETWEEN_ROWS, RES_LAYOUT_SPLIT, 0 ), + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ), + + // RES_FRAMEDIR + MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ), + + // RES_COLLAPSING_BORDERS + MAP_ENTRY( TABLE, BORDER_MODEL, RES_COLLAPSING_BORDERS, 0 ), + + M_END +}; + +SvXMLItemMapEntry const aXMLTableColItemMap[] = +{ + M_E_SI( STYLE, COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_COL_WIDTH ), + MAP_ENTRY( STYLE, REL_COLUMN_WIDTH, RES_FRM_SIZE, MID_FRMSIZE_REL_COL_WIDTH ), + M_END +}; + +SvXMLItemMapEntry const aXMLTableRowItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + MAP_ENTRY( STYLE, ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_FIX_HEIGHT ), + MAP_ENTRY( STYLE, MIN_ROW_HEIGHT, RES_FRM_SIZE, MID_FRMSIZE_MIN_HEIGHT ), + // RES_PAPER_BIN + // not required + // RES_LR_SPACE + // not required + // RES_UL_SPACE + // not required + // RES_PAGEDESC + // not required + // RES_BREAK + // not required + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // not required + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + // not required + // RES_HORI_ORIENT + // not required + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + // not required + // RES_ANCHOR + // not required + // RES_SHADOW + // not required + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + // not required + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + M_E_SE( STYLE, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ), + M_E_SE( FO, KEEP_TOGETHER, RES_ROW_SPLIT, 0 ), + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ), + M_END +}; + +SvXMLItemMapEntry const aXMLTableCellItemMap[] = +{ + // RES_FILL_ORDER + // not required + // RES_FRM_SIZE + // not required + // RES_PAPER_BIN + // not required + // RES_LR_SPACE + // not required + // RES_UL_SPACE + // not required + // RES_PAGEDESC + // not required + // RES_BREAK + // not required + // RES_CNTNT + // not required + // RES_HEADER + // not required + // RES_FOOTER + // not required + // RES_PRINT + // not required + // RES_OPAQUE + // not required + // RES_PROTECT + // not required + // RES_SURROUND + // not required + // RES_VERT_ORIENT + MAP_ENTRY( STYLE, VERTICAL_ALIGN, RES_VERT_ORIENT, 0 ), + // RES_HORI_ORIENT + // not required + // RES_ANCHOR + // not required + // RES_BACKGROUND + MAP_ENTRY( FO, BACKGROUND_COLOR, RES_BACKGROUND, MID_BACK_COLOR ), + MAP_ENTRY( STYLE, BACKGROUND_IMAGE, RES_BACKGROUND, MID_SW_FLAG_ELEMENT_ITEM ), + // RES_BOX + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH, RES_BOX, ALL_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_LEFT, RES_BOX, LEFT_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_RIGHT, RES_BOX, RIGHT_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_TOP, RES_BOX, TOP_BORDER_LINE_WIDTH ), + MAP_ENTRY( STYLE, BORDER_LINE_WIDTH_BOTTOM, RES_BOX, BOTTOM_BORDER_LINE_WIDTH ), + MAP_ENTRY( FO, PADDING, RES_BOX, ALL_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_LEFT, RES_BOX, LEFT_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_RIGHT, RES_BOX, RIGHT_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_TOP, RES_BOX, TOP_BORDER_PADDING ), + MAP_ENTRY( FO, PADDING_BOTTOM, RES_BOX, BOTTOM_BORDER_PADDING ), + MAP_ENTRY( FO, BORDER, RES_BOX, ALL_BORDER ), + MAP_ENTRY( FO, BORDER_LEFT, RES_BOX, LEFT_BORDER ), + MAP_ENTRY( FO, BORDER_RIGHT, RES_BOX, RIGHT_BORDER ), + MAP_ENTRY( FO, BORDER_TOP, RES_BOX, TOP_BORDER ), + MAP_ENTRY( FO, BORDER_BOTTOM, RES_BOX, BOTTOM_BORDER ), + // RES_SHADOW + // not required + // RES_FRMMACRO + // not required + // RES_COL + // not required + // RES_KEEP + // not required + // RES_URL + // not required + // RES_EDIT_IN_READONLY + // not required + // RES_LAYOUT_SPLIT + // not required + // RES_CHAIN + // not required + // RES_LINENUMBER + // not required + // RES_FTN_AT_TXTEND + // not required + // RES_END_AT_TXTEND + // not required + // RES_UNKNOWNATR_CONTAINER + M_E_SE( TEXT, XMLNS, RES_UNKNOWNATR_CONTAINER, 0 ), + + // RES_FRAMEDIR + MAP_ENTRY( STYLE, WRITING_MODE, RES_FRAMEDIR, 0 ), + MAP_ENTRY( LO_EXT, WRITING_MODE, RES_FRAMEDIR, 0 ), + + M_END +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlithlp.cxx b/sw/source/filter/xml/xmlithlp.cxx new file mode 100644 index 000000000..eb2bd25db --- /dev/null +++ b/sw/source/filter/xml/xmlithlp.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "xmlithlp.hxx" +#include <sax/tools/converter.hxx> +#include <editeng/borderline.hxx> +#include <editeng/brushitem.hxx> + +#include <xmloff/xmluconv.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/table/BorderLineStyle.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> + +using ::editeng::SvxBorderLine; +using namespace ::xmloff::token; +using namespace ::com::sun::star; + +#define SVX_XML_BORDER_WIDTH_THIN 0 +#define SVX_XML_BORDER_WIDTH_MIDDLE 1 +#define SVX_XML_BORDER_WIDTH_THICK 2 + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[] = +{ + { XML_NONE, table::BorderLineStyle::NONE }, + { XML_HIDDEN, table::BorderLineStyle::NONE }, + { XML_SOLID, table::BorderLineStyle::SOLID }, + { XML_DOUBLE, table::BorderLineStyle::DOUBLE }, + { XML_DOUBLE_THIN, table::BorderLineStyle::DOUBLE_THIN }, + { XML_DOTTED, table::BorderLineStyle::DOTTED }, + { XML_DASHED, table::BorderLineStyle::DASHED }, + { XML_FINE_DASHED, table::BorderLineStyle::FINE_DASHED }, + { XML_DASH_DOT, table::BorderLineStyle::DASH_DOT }, + { XML_DASH_DOT_DOT, table::BorderLineStyle::DASH_DOT_DOT }, + { XML_GROOVE, table::BorderLineStyle::ENGRAVED }, + { XML_RIDGE, table::BorderLineStyle::EMBOSSED }, + { XML_INSET, table::BorderLineStyle::INSET }, + { XML_OUTSET, table::BorderLineStyle::OUTSET }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[] = +{ + { XML_THIN, SVX_XML_BORDER_WIDTH_THIN }, + { XML_MIDDLE, SVX_XML_BORDER_WIDTH_MIDDLE }, + { XML_THICK, SVX_XML_BORDER_WIDTH_THICK }, + { XML_TOKEN_INVALID, 0 } +}; +// mapping tables to map external xml input to internal box line widths + +const sal_uInt16 aBorderWidths[] = +{ + DEF_LINE_WIDTH_0, + DEF_LINE_WIDTH_5, + DEF_LINE_WIDTH_1, +}; + +bool sw_frmitems_parseXMLBorder( const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter, + bool& rHasStyle, sal_uInt16& rStyle, + bool& rHasWidth, sal_uInt16& rWidth, + sal_uInt16& rNamedWidth, + bool& rHasColor, Color& rColor ) +{ + OUString aToken; + SvXMLTokenEnumerator aTokens( rValue ); + + rHasStyle = false; + rHasWidth = false; + rHasColor = false; + + rStyle = USHRT_MAX; + rWidth = 0; + rNamedWidth = USHRT_MAX; + + sal_Int32 nTemp; + while( aTokens.getNextToken( aToken ) && !aToken.isEmpty() ) + { + if( !rHasWidth && + SvXMLUnitConverter::convertEnum( rNamedWidth, aToken, + psXML_NamedBorderWidths ) ) + { + rHasWidth = true; + } + else if( !rHasStyle && + SvXMLUnitConverter::convertEnum( rStyle, aToken, + psXML_BorderStyles ) ) + { + rHasStyle = true; + } + else if (!rHasColor && ::sax::Converter::convertColor(rColor, aToken)) + { + rHasColor = true; + } + else if( !rHasWidth && + rUnitConverter.convertMeasureToCore(nTemp, aToken, 0, USHRT_MAX)) + { + rWidth = static_cast<sal_uInt16>(nTemp); + rHasWidth = true; + } + else + { + // misformed + return false; + } + } + + return rHasStyle || rHasWidth || rHasColor; +} + +static void sw_frmitems_setXMLBorderStyle( SvxBorderLine& rLine, sal_uInt16 nStyle ) +{ + SvxBorderLineStyle eStyle = SvxBorderLineStyle::NONE; + if ( nStyle != table::BorderLineStyle::NONE ) + eStyle = SvxBorderLineStyle( nStyle ); + rLine.SetBorderLineStyle(eStyle); +} + +bool sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine, + bool bHasStyle, sal_uInt16 nStyle, + bool bHasWidth, sal_uInt16 nWidth, + sal_uInt16 nNamedWidth, + bool bHasColor, const Color& rColor ) +{ + // first of all, delete an empty line + if( (bHasStyle && table::BorderLineStyle::NONE == nStyle) || + (bHasWidth && USHRT_MAX == nNamedWidth && 0 == nWidth) ) + { + bool bRet = nullptr != rpLine; + rpLine.reset(); + return bRet; + } + + // if there is no line and no style and no with, there will never be a line + if( !rpLine && !(bHasStyle && bHasWidth) ) + return false; + + // We now do know that there will be a line + if( !rpLine ) + rpLine.reset(new SvxBorderLine); + + if( ( bHasWidth && + (USHRT_MAX != nNamedWidth || (nWidth != rpLine->GetWidth() ) ) ) || + ( bHasStyle && + ((table::BorderLineStyle::SOLID == nStyle && rpLine->GetDistance()) || + (table::BorderLineStyle::DOUBLE == nStyle && !rpLine->GetDistance())) ) ) + { + bool bDouble = (bHasWidth && table::BorderLineStyle::DOUBLE == nStyle ) || + rpLine->GetDistance(); + + // fdo#38542: for double borders, do not override the width + // set via style:border-line-width{,-left,-right,-top,-bottom} + if (!bDouble || !rpLine->GetWidth()) + { + // The width has to be changed + if (bHasWidth && USHRT_MAX != nNamedWidth) + { + if (bDouble) + { + rpLine->SetBorderLineStyle( SvxBorderLineStyle::DOUBLE ); + } + rpLine->SetWidth( aBorderWidths[nNamedWidth] ); + } + else + { + if (!bHasWidth) + nWidth = rpLine->GetScaledWidth(); + + rpLine->SetWidth( nWidth ); + } + } + sw_frmitems_setXMLBorderStyle( *rpLine, nStyle ); + } + + // set color + if( bHasColor ) + rpLine->SetColor( rColor ); + + return true; +} + +void sw_frmitems_setXMLBorder( std::unique_ptr<SvxBorderLine>& rpLine, + sal_uInt16 nWidth, sal_uInt16 nOutWidth, + sal_uInt16 nInWidth, sal_uInt16 nDistance ) +{ + if( !rpLine ) + rpLine.reset(new SvxBorderLine); + + if( nWidth > 0 ) + rpLine->SetWidth( nWidth ); + else + rpLine->GuessLinesWidths(SvxBorderLineStyle::DOUBLE, + nOutWidth, nInWidth, nDistance); +} + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[] = +{ + { XML_REPEAT, GPOS_TILED }, + { XML_BACKGROUND_NO_REPEAT, GPOS_MM }, + { XML_STRETCH, GPOS_AREA }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[] = +{ + { XML_LEFT, GPOS_LM }, + { XML_RIGHT, GPOS_RM }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[] = +{ + { XML_TOP, GPOS_MT }, + { XML_BOTTOM, GPOS_MB }, + { XML_TOKEN_INVALID, SvxGraphicPosition(0) } +}; + +void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eHori ) +{ + OSL_ENSURE( GPOS_LM==eHori || GPOS_MM==eHori || GPOS_RM==eHori, + "sw_frmitems_MergeXMLHoriPos: vertical pos must be middle" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_MT: + case GPOS_RT: + ePos = GPOS_LM==eHori ? GPOS_LT : (GPOS_MM==eHori ? GPOS_MT : GPOS_RT); + break; + + case GPOS_LM: + case GPOS_MM: + case GPOS_RM: + ePos = eHori; + break; + + case GPOS_LB: + case GPOS_MB: + case GPOS_RB: + ePos = GPOS_LM==eHori ? GPOS_LB : (GPOS_MM==eHori ? GPOS_MB : GPOS_RB); + break; + default: + ; + } +} + +void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eVert ) +{ + OSL_ENSURE( GPOS_MT==eVert || GPOS_MM==eVert || GPOS_MB==eVert, + "sw_frmitems_MergeXMLVertPos: horizontal pos must be middle" ); + + switch( ePos ) + { + case GPOS_LT: + case GPOS_LM: + case GPOS_LB: + ePos = GPOS_MT==eVert ? GPOS_LT : (GPOS_MM==eVert ? GPOS_LM : GPOS_LB); + break; + + case GPOS_MT: + case GPOS_MM: + case GPOS_MB: + ePos = eVert; + break; + + case GPOS_RT: + case GPOS_RM: + case GPOS_RB: + ePos = GPOS_MT==eVert ? GPOS_RT : (GPOS_MM==eVert ? GPOS_RM : GPOS_RB); + break; + default: + ; + } +} + +const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[] = +{ + { XML_AUTO, 0 }, + { XML_COLUMN, 1 }, + { XML_PAGE, 2 }, + { XML_EVEN_PAGE, 2 }, + { XML_ODD_PAGE, 2 }, + { XML_TOKEN_INVALID, 0} +}; + +const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[] = +{ + { XML_LEFT, text::HoriOrientation::LEFT }, + { XML_LEFT, text::HoriOrientation::LEFT_AND_WIDTH }, + { XML_CENTER, text::HoriOrientation::CENTER }, + { XML_RIGHT, text::HoriOrientation::RIGHT }, + { XML_MARGINS, text::HoriOrientation::FULL }, + { XML_MARGINS, text::HoriOrientation::NONE }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[] = +{ + { XML_TOP, text::VertOrientation::TOP }, + { XML_MIDDLE, text::VertOrientation::CENTER }, + { XML_BOTTOM, text::VertOrientation::BOTTOM }, + { XML_TOKEN_INVALID, 0 } +}; + +const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[] = +{ + { XML_ALWAYS, 0 }, + { XML_AUTO, 1 }, + { XML_TOKEN_INVALID, 0} +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlithlp.hxx b/sw/source/filter/xml/xmlithlp.hxx new file mode 100644 index 000000000..57d688fef --- /dev/null +++ b/sw/source/filter/xml/xmlithlp.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_FILTER_XML_XMLITHLP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITHLP_HXX + +#include <sal/types.h> +#include <xmloff/xmlement.hxx> +#include <editeng/brushitem.hxx> + +namespace editeng { class SvxBorderLine; } + +template<typename EnumT> struct SvXMLEnumMapEntry; +class SvXMLUnitConverter; +class Color; + +/** Define various helper variables and functions for xmlimpit.cxx and + * xmlexpit.cxx. */ +bool sw_frmitems_parseXMLBorder( const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter, + bool& rHasStyle, sal_uInt16& rStyle, + bool& rHasWidth, sal_uInt16& rWidth, + sal_uInt16& rNamedWidth, + bool& rHasColor, Color& rColor ); + +bool sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine, + bool bHasStyle, sal_uInt16 nStyle, + bool bHasWidth, sal_uInt16 nWidth, + sal_uInt16 nNamedWidth, + bool bHasColor, const Color& rColor ); + +void sw_frmitems_setXMLBorder( std::unique_ptr<editeng::SvxBorderLine>& rpLine, + sal_uInt16 nWidth, sal_uInt16 nOutWidth, + sal_uInt16 nInWidth, sal_uInt16 nDistance ); + +void sw_frmitems_MergeXMLHoriPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eHori ); + +void sw_frmitems_MergeXMLVertPos( SvxGraphicPosition& ePos, + SvxGraphicPosition eVert ); + +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BorderStyles[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_NamedBorderWidths[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushRepeat[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushHoriPos[]; +extern const struct SvXMLEnumMapEntry<SvxGraphicPosition> psXML_BrushVertPos[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> psXML_BreakType[]; +extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableAlignMap[]; +extern const struct SvXMLEnumMapEntry<sal_Int16> aXMLTableVAlignMap[]; +extern const struct SvXMLEnumMapEntry<sal_uInt16> aXML_KeepTogetherType[]; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitmap.hxx b/sw/source/filter/xml/xmlitmap.hxx new file mode 100644 index 000000000..af4c12ebd --- /dev/null +++ b/sw/source/filter/xml/xmlitmap.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_FILTER_XML_XMLITMAP_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX + +#include <sal/types.h> +#include <tools/ref.hxx> +#include <xmloff/xmltoken.hxx> +#include <memory> + +#define MID_SW_FLAG_MASK 0x0000ffff + +// this flags are used in the item mapper for import and export +#define MID_SW_FLAG_SPECIAL_ITEM_IMPORT 0x80000000 +#define MID_SW_FLAG_NO_ITEM_IMPORT 0x40000000 +#define MID_SW_FLAG_SPECIAL_ITEM_EXPORT 0x20000000 +#define MID_SW_FLAG_NO_ITEM_EXPORT 0x10000000 +#define MID_SW_FLAG_ELEMENT_ITEM_IMPORT 0x08000000 +#define MID_SW_FLAG_ELEMENT_ITEM_EXPORT 0x04000000 +#define MID_SW_FLAG_ELEMENT_ITEM 0x0c000000 // both import and export + +struct SvXMLItemMapEntry +{ + sal_uInt16 nNameSpace; // declares the Namespace in which this item + // exists + sal_uInt16 nWhichId; // the WhichId to identify the item + // in the pool + enum ::xmloff::token::XMLTokenEnum const eLocalName; + // the local name for the item inside + // the Namespace (as an XMLTokenEnum) + sal_uInt32 nMemberId; // the memberid specifies which part + // of the item should be imported or + // exported with this Namespace + // and localName + SvXMLItemMapEntry( + sal_uInt16 nameSpace, + enum ::xmloff::token::XMLTokenEnum localName, + sal_uInt16 whichId, + sal_uInt32 memberId) + : nNameSpace(nameSpace), nWhichId(whichId), eLocalName(localName), nMemberId(memberId) {} +}; + +class SvXMLItemMapEntries_impl; + +/** this class manages an array of SvXMLItemMapEntry. It is + used for optimizing the static array on startup of import + or export */ +class SvXMLItemMapEntries final : public SvRefBase +{ + std::unique_ptr<SvXMLItemMapEntries_impl> mpImpl; + +public: + explicit SvXMLItemMapEntries(SvXMLItemMapEntry const * pEntrys); + virtual ~SvXMLItemMapEntries() override; + + SvXMLItemMapEntry const * getByName( sal_uInt16 nNameSpace, + const OUString& rString ) const; + SvXMLItemMapEntry const & getByIndex( sal_uInt16 nIndex ) const; + + sal_uInt16 getCount() const; +}; + +typedef tools::SvRef<SvXMLItemMapEntries> SvXMLItemMapEntriesRef; + +extern SvXMLItemMapEntry const aXMLTableItemMap[]; +extern SvXMLItemMapEntry const aXMLTableColItemMap[]; +extern SvXMLItemMapEntry const aXMLTableRowItemMap[]; +extern SvXMLItemMapEntry const aXMLTableCellItemMap[]; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLITMAP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlitmpr.cxx b/sw/source/filter/xml/xmlitmpr.cxx new file mode 100644 index 000000000..100fc7636 --- /dev/null +++ b/sw/source/filter/xml/xmlitmpr.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 <rtl/ustring.hxx> +#include "xmlitmap.hxx" + +using ::xmloff::token::IsXMLToken; +using ::xmloff::token::XML_TOKEN_INVALID; + +// TODO: optimize this! +class SvXMLItemMapEntries_impl +{ +public: + SvXMLItemMapEntry const * mpEntries; + sal_uInt16 mnCount; +}; + +SvXMLItemMapEntries::SvXMLItemMapEntries( SvXMLItemMapEntry const * pEntries ) + : mpImpl( new SvXMLItemMapEntries_impl ) +{ + mpImpl->mpEntries = pEntries; + + mpImpl->mnCount = 0; + while( pEntries->eLocalName != XML_TOKEN_INVALID ) + { + pEntries++; + mpImpl->mnCount++; + } +} + +SvXMLItemMapEntries::~SvXMLItemMapEntries() +{ +} + +SvXMLItemMapEntry const * SvXMLItemMapEntries::getByName( sal_uInt16 nNameSpace, + const OUString& rString ) const +{ + SvXMLItemMapEntry const * pMap = mpImpl->mpEntries; + while( pMap && (pMap->eLocalName != XML_TOKEN_INVALID) ) + { + if( pMap->nNameSpace == nNameSpace && + IsXMLToken( rString, pMap->eLocalName ) ) + break; + pMap++; + } + + return (pMap && (pMap->eLocalName != XML_TOKEN_INVALID)) ? pMap : nullptr; +} + +SvXMLItemMapEntry const & SvXMLItemMapEntries::getByIndex( sal_uInt16 nIndex ) const +{ + return mpImpl->mpEntries[nIndex]; +} + +sal_uInt16 SvXMLItemMapEntries::getCount() const +{ + return mpImpl->mnCount; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlmeta.cxx b/sw/source/filter/xml/xmlmeta.cxx new file mode 100644 index 000000000..10079d9f6 --- /dev/null +++ b/sw/source/filter/xml/xmlmeta.cxx @@ -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 . + */ + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <osl/diagnose.h> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/ProgressBarHelper.hxx> +#include <o3tl/safeint.hxx> +#include <xmloff/xmluconv.hxx> +#include <docstat.hxx> +#include <doc.hxx> +#include <IDocumentStatistics.hxx> +#include "xmlimp.hxx" +#include "xmlexp.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::xmloff::token; + +uno::Reference<document::XDocumentProperties> +SwXMLImport::GetDocumentProperties() const +{ + if (m_bOrganizerMode || IsStylesOnlyMode() || + IsBlockMode() || IsInsertMode()) + { + return nullptr; + } + uno::Reference<document::XDocumentPropertiesSupplier> const xDPS( + GetModel(), UNO_QUERY_THROW); + return xDPS->getDocumentProperties(); +} + +SvXMLImportContext *SwXMLImport::CreateMetaContext( + const sal_Int32 /*nElement*/ ) +{ + SvXMLImportContext *pContext = nullptr; + + if (getImportFlags() & SvXMLImportFlags::META) + { + uno::Reference<document::XDocumentProperties> const xDocProps( + GetDocumentProperties()); + pContext = new SvXMLMetaDocumentContext(*this, xDocProps); + } + + return pContext; +} + +namespace { + +enum SvXMLTokenMapAttrs +{ + XML_TOK_META_STAT_TABLE = 1, + XML_TOK_META_STAT_IMAGE = 2, + XML_TOK_META_STAT_OLE = 4, + XML_TOK_META_STAT_PAGE = 8, + XML_TOK_META_STAT_PARA = 16, + XML_TOK_META_STAT_WORD = 32, + XML_TOK_META_STAT_CHAR = 64, + XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR = 128, + XML_TOK_META_STAT_END=XML_TOK_UNKNOWN +}; + +struct statistic { + SvXMLTokenMapAttrs token; + const char* name; + sal_uInt16 SwDocStat::* target16; + sal_uLong SwDocStat::* target32; /* or 64, on LP64 platforms */ +}; + +} + +static const struct statistic s_stats [] = { + { XML_TOK_META_STAT_TABLE, "TableCount", &SwDocStat::nTable, nullptr }, + { XML_TOK_META_STAT_IMAGE, "ImageCount", &SwDocStat::nGrf, nullptr }, + { XML_TOK_META_STAT_OLE, "ObjectCount", &SwDocStat::nOLE, nullptr }, + { XML_TOK_META_STAT_PAGE, "PageCount", nullptr, &SwDocStat::nPage }, + { XML_TOK_META_STAT_PARA, "ParagraphCount", nullptr, &SwDocStat::nPara }, + { XML_TOK_META_STAT_WORD, "WordCount", nullptr, &SwDocStat::nWord }, + { XML_TOK_META_STAT_CHAR, "CharacterCount", nullptr, &SwDocStat::nChar }, + { XML_TOK_META_STAT_NON_WHITE_SPACE_CHAR, "NonWhitespaceCharacterCount", nullptr, &SwDocStat::nCharExcludingSpaces }, + { XML_TOK_META_STAT_END, nullptr, nullptr, nullptr } +}; + +void SwXMLImport::SetStatistics( + const Sequence< beans::NamedValue > & i_rStats) +{ + if( IsStylesOnlyMode() || IsInsertMode() ) + return; + + SvXMLImport::SetStatistics(i_rStats); + + SwDoc *pDoc = SwImport::GetDocFromXMLImport( *this ); + SwDocStat aDocStat( pDoc->getIDocumentStatistics().GetDocStat() ); + + sal_uInt32 nTokens = 0; + + for (const auto& rStat : i_rStats) { + for (struct statistic const* pStat = s_stats; pStat->name != nullptr; + ++pStat) { + if (rStat.Name.equalsAscii(pStat->name)) { + sal_Int32 val = 0; + if (rStat.Value >>= val) { + if (pStat->target16 != nullptr) { + aDocStat.*(pStat->target16) + = static_cast<sal_uInt16> (val); + } else { + aDocStat.*(pStat->target32) + = static_cast<sal_uInt32> (val); + } + nTokens |= pStat->token; + } else { + OSL_FAIL("SwXMLImport::SetStatistics: invalid entry"); + } + } + } + } + + if( nTokens ) + pDoc->getIDocumentStatistics().SetDocStat( aDocStat ); + + // set progress bar reference to #paragraphs. If not available, + // use #pages*10, or guesstimate 250 paragraphs. Additionally + // guesstimate PROGRESS_BAR_STEPS each for meta+settings, styles, + // and autostyles. + bool bSetFallback = true; + sal_Int32 nProgressReference = sal_Int32(); // silence C4701 + if (nTokens & XML_TOK_META_STAT_PARA) + { + nProgressReference = static_cast<sal_Int32>(aDocStat.nPara); + bSetFallback = false; + } + else if (nTokens & XML_TOK_META_STAT_PAGE) + bSetFallback = o3tl::checked_multiply<sal_Int32>(aDocStat.nPage, 10, nProgressReference); + if (bSetFallback) + nProgressReference = 250; + ProgressBarHelper* pProgress = GetProgressBarHelper(); + pProgress->SetReference( nProgressReference + 3*PROGRESS_BAR_STEP ); + pProgress->SetValue( 0 ); +} + +void SwXMLExport::ExportMeta_() +{ + SvXMLExport::ExportMeta_(); + + if( !m_bBlock && IsShowProgress() ) + { + ProgressBarHelper *pProgress = GetProgressBarHelper(); + pProgress->SetValue( pProgress->GetValue() + 2 ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmlscript.cxx b/sw/source/filter/xml/xmlscript.cxx new file mode 100644 index 000000000..a0bd5096f --- /dev/null +++ b/sw/source/filter/xml/xmlscript.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 <xmloff/xmlnmspe.hxx> + +#include <xmloff/xmlscripti.hxx> +#include "xmlimp.hxx" + +using namespace ::com::sun::star; + +SvXMLImportContext *SwXMLImport::CreateScriptContext() +{ + SvXMLImportContext *pContext = nullptr; + + if( !(IsStylesOnlyMode() || IsInsertMode()) ) + { + pContext = new XMLScriptContext( *this, GetModel() ); + } + + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltble.cxx b/sw/source/filter/xml/xmltble.cxx new file mode 100644 index 000000000..fcc386c8c --- /dev/null +++ b/sw/source/filter/xml/xmltble.cxx @@ -0,0 +1,1245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XTextTable.hpp> +#include <com/sun/star/text/XTextSection.hpp> + +#include <hintids.hxx> +#include <rtl/ustrbuf.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/numehelp.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/xmlcnitm.hxx> +#include <fmtrowsplt.hxx> +#include <editeng/frmdiritem.hxx> +#include <swtable.hxx> +#include <doc.hxx> +#include <frmfmt.hxx> +#include <wrtswtbl.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <cellatr.hxx> +#include <ddefld.hxx> +#include <swddetbl.hxx> +#include <xmloff/nmspmap.hxx> +#include <sfx2/linkmgr.hxx> +#include <unotbl.hxx> +#include "xmltexte.hxx" +#include "xmlexp.hxx" +#include <o3tl/any.hxx> +#include <o3tl/sorted_vector.hxx> +#include <textboxhelper.hxx> +#include <SwStyleNameMapper.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::xmloff::token; +using table::XCell; +using std::vector; +using std::advance; + + +class SwXMLTableColumn_Impl : public SwWriteTableCol +{ + OUString sStyleName; + sal_uInt32 nRelWidth; + +public: + + explicit SwXMLTableColumn_Impl(sal_uInt32 nPosition) + : SwWriteTableCol(nPosition) + , nRelWidth(0) + {}; + + void SetStyleName( const OUString& rName ) { sStyleName = rName; } + const OUString& GetStyleName() const { return sStyleName; } + + void SetRelWidth( sal_uInt32 nSet ) { nRelWidth = nSet; } + sal_uInt32 GetRelWidth() const { return nRelWidth; } +}; + +namespace { + +struct SwXMLTableColumnCmpWidth_Impl +{ + bool operator()( SwXMLTableColumn_Impl* const& lhs, SwXMLTableColumn_Impl* const& rhs ) const + { + sal_Int32 n = static_cast<sal_Int32>(lhs->GetWidthOpt()) - static_cast<sal_Int32>(rhs->GetWidthOpt()); + if( !n ) + n = static_cast<sal_Int32>(lhs->GetRelWidth()) - static_cast<sal_Int32>(rhs->GetRelWidth()); + return n < 0; + } +}; + +class SwXMLTableColumns_Impl : public o3tl::sorted_vector<std::unique_ptr<SwXMLTableColumn_Impl>, o3tl::less_uniqueptr_to<SwXMLTableColumn_Impl> > { +}; + +} + +class SwXMLTableColumnsSortByWidth_Impl : public o3tl::sorted_vector<SwXMLTableColumn_Impl*, SwXMLTableColumnCmpWidth_Impl> {}; + +class SwXMLTableLines_Impl +{ + SwXMLTableColumns_Impl aCols; + const SwTableLines *pLines; + sal_uInt32 nWidth; + +public: + + explicit SwXMLTableLines_Impl( const SwTableLines& rLines ); + + sal_uInt32 GetWidth() const { return nWidth; } + const SwTableLines *GetLines() const { return pLines; } + + const SwXMLTableColumns_Impl& GetColumns() const { return aCols; } +}; + +SwXMLTableLines_Impl::SwXMLTableLines_Impl( const SwTableLines& rLines ) : + pLines( &rLines ), + nWidth( 0 ) +{ +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nEndCPos = 0U; +#endif + const size_t nLines = rLines.size(); + for( size_t nLine=0U; nLine<nLines; ++nLine ) + { + const SwTableLine *pLine = rLines[nLine]; + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + for( size_t nBox=0U; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + if( nBox < nBoxes-1U || nWidth==0 ) + { + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + std::unique_ptr<SwXMLTableColumn_Impl> pCol( + new SwXMLTableColumn_Impl( nCPos )); + + aCols.insert( std::move(pCol) ); + + if( nBox==nBoxes-1U ) + { + OSL_ENSURE( nLine==0U && nWidth==0, + "parent width will be lost" ); + nWidth = nCPos; + } + } + else + { +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nCheckPos = + nCPos + SwWriteTable::GetBoxWidth( pBox ); + if( !nEndCPos ) + { + nEndCPos = nCheckPos; + } +#endif + nCPos = nWidth; +#if OSL_DEBUG_LEVEL > 0 + SwXMLTableColumn_Impl aCol( nWidth ); + OSL_ENSURE( aCols.find(&aCol) != aCols.end(), "couldn't find last column" ); + OSL_ENSURE( SwXMLTableColumn_Impl(nCheckPos) == + SwXMLTableColumn_Impl(nCPos), + "rows have different total widths" ); +#endif + } + } + } +} + +typedef vector< SwFrameFormat* > SwXMLFrameFormats_Impl; + +class SwXMLTableFrameFormatsSort_Impl +{ +private: + SwXMLFrameFormats_Impl aFormatList; +public: + bool AddRow( SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, sal_uInt32 nLine ); + bool AddCell( SwFrameFormat& rFrameFormat, const OUString& rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ); +}; + +bool SwXMLTableFrameFormatsSort_Impl::AddRow( SwFrameFormat& rFrameFormat, + const OUString& rNamePrefix, + sal_uInt32 nLine ) +{ + const SwFormatFrameSize *pFrameSize = nullptr; + const SwFormatRowSplit* pRowSplit = nullptr; + const SvxBrushItem *pBrush = nullptr; + + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_FRM_SIZE, false, &pItem ) ) + pFrameSize = static_cast<const SwFormatFrameSize *>(pItem); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_ROW_SPLIT, false, &pItem ) ) + pRowSplit = static_cast<const SwFormatRowSplit *>(pItem); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + pBrush = static_cast<const SvxBrushItem *>(pItem); + + // empty styles have not to be exported + if( !pFrameSize && !pBrush && !pRowSplit ) + return false; + + // order is: -/brush, size/-, size/brush + bool bInsert = true; + SwXMLFrameFormats_Impl::iterator i; + for( i = aFormatList.begin(); i < aFormatList.end(); ++i ) + { + const SwFormatFrameSize *pTestFrameSize = nullptr; + const SwFormatRowSplit* pTestRowSplit = nullptr; + const SvxBrushItem *pTestBrush = nullptr; + const SwFrameFormat *pTestFormat = *i; + const SfxItemSet& rTestSet = pTestFormat->GetAttrSet(); + if( SfxItemState::SET == rTestSet.GetItemState( RES_FRM_SIZE, false, + &pItem ) ) + { + if( !pFrameSize ) + break; + + pTestFrameSize = static_cast<const SwFormatFrameSize *>(pItem); + } + else + { + if( pFrameSize ) + continue; + } + + if( SfxItemState::SET == rTestSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + if( !pBrush ) + break; + + pTestBrush = static_cast<const SvxBrushItem *>(pItem); + } + else + { + if( pBrush ) + continue; + } + + if( SfxItemState::SET == rTestSet.GetItemState( RES_ROW_SPLIT, false, + &pItem ) ) + { + if( !pRowSplit ) + break; + + pTestRowSplit = static_cast<const SwFormatRowSplit *>(pItem); + } + else + { + if( pRowSplit ) + continue; + } + + if( pFrameSize && + ( pFrameSize->GetHeightSizeType() != pTestFrameSize->GetHeightSizeType() || + pFrameSize->GetHeight() != pTestFrameSize->GetHeight() ) ) + continue; + + if( pBrush && (*pBrush != *pTestBrush) ) + continue; + + if( pRowSplit && (!pRowSplit->GetValue() != !pTestRowSplit->GetValue()) ) + continue; + + // found! + rFrameFormat.SetName( pTestFormat->GetName() ); + bInsert = false; + break; + } + + if( bInsert ) + { + rFrameFormat.SetName( rNamePrefix + "." + OUString::number(nLine+1) ); + if ( i != aFormatList.end() ) ++i; + aFormatList.insert( i, &rFrameFormat ); + } + + return bInsert; +} + +static OUString lcl_xmltble_appendBoxPrefix(const OUString& rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ) +{ + if( bTop ) + { + OUString sTmp; + sw_GetTableBoxColStr( static_cast<sal_uInt16>(nCol), sTmp ); + return rNamePrefix + "." + sTmp + OUString::number(nRow + 1); + } + return rNamePrefix + + "." + OUString::number(nCol + 1) + + "." + OUString::number(nRow + 1); +} + +bool SwXMLTableFrameFormatsSort_Impl::AddCell( SwFrameFormat& rFrameFormat, + const OUString& rNamePrefix, + sal_uInt32 nCol, sal_uInt32 nRow, bool bTop ) +{ + const SwFormatVertOrient *pVertOrient = nullptr; + const SvxBrushItem *pBrush = nullptr; + const SvxBoxItem *pBox = nullptr; + const SwTableBoxNumFormat *pNumFormat = nullptr; + const SvxFrameDirectionItem *pFrameDir = nullptr; + const SvXMLAttrContainerItem *pAttCnt = nullptr; + + const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet(); + const SfxPoolItem *pItem; + if( SfxItemState::SET == rItemSet.GetItemState( RES_VERT_ORIENT, false, + &pItem ) ) + pVertOrient = static_cast<const SwFormatVertOrient *>(pItem); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) + pBrush = static_cast<const SvxBrushItem *>(pItem); + + if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false, &pItem ) ) + pBox = static_cast<const SvxBoxItem *>(pItem); + + if ( SfxItemState::SET == rItemSet.GetItemState( RES_BOXATR_FORMAT, + false, &pItem ) ) + pNumFormat = static_cast<const SwTableBoxNumFormat *>(pItem); + if ( SfxItemState::SET == rItemSet.GetItemState( RES_FRAMEDIR, + false, &pItem ) ) + pFrameDir = static_cast<const SvxFrameDirectionItem *>(pItem); + if ( SfxItemState::SET == rItemSet.GetItemState( RES_UNKNOWNATR_CONTAINER, + false, &pItem ) ) + pAttCnt = static_cast<const SvXMLAttrContainerItem *>(pItem); + + // empty styles have not to be exported + if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt ) + return false; + + // order is: -/-/-/num, + // -/-/box/-, -/-/box/num, + // -/brush/-/-, -/brush/-/num, -/brush/box/-, -/brush/box/num, + // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num, + // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-, + // vert/brush/box/num + bool bInsert = true; + SwXMLFrameFormats_Impl::iterator i; + for( i = aFormatList.begin(); i < aFormatList.end(); ++i ) + { + const SwFormatVertOrient *pTestVertOrient = nullptr; + const SvxBrushItem *pTestBrush = nullptr; + const SvxBoxItem *pTestBox = nullptr; + const SwTableBoxNumFormat *pTestNumFormat = nullptr; + const SvxFrameDirectionItem *pTestFrameDir = nullptr; + const SvXMLAttrContainerItem *pTestAttCnt = nullptr; + const SwFrameFormat* pTestFormat = *i; + const SfxItemSet& rTestSet = pTestFormat->GetAttrSet(); + if( SfxItemState::SET == rTestSet.GetItemState( RES_VERT_ORIENT, false, + &pItem ) ) + { + if( !pVertOrient ) + break; + + pTestVertOrient = static_cast<const SwFormatVertOrient *>(pItem); + } + else + { + if( pVertOrient ) + continue; + } + + if( SfxItemState::SET == rTestSet.GetItemState( RES_BACKGROUND, false, + &pItem ) ) + { + if( !pBrush ) + break; + + pTestBrush = static_cast<const SvxBrushItem *>(pItem); + } + else + { + if( pBrush ) + continue; + } + + if( SfxItemState::SET == rTestSet.GetItemState( RES_BOX, false, &pItem ) ) + { + if( !pBox ) + break; + + pTestBox = static_cast<const SvxBoxItem *>(pItem); + } + else + { + if( pBox ) + continue; + } + + if ( SfxItemState::SET == rTestSet.GetItemState( RES_BOXATR_FORMAT, + false, &pItem ) ) + { + if( !pNumFormat ) + break; + + pTestNumFormat = static_cast<const SwTableBoxNumFormat *>(pItem); + } + else + { + if( pNumFormat ) + continue; + + } + + if ( SfxItemState::SET == rTestSet.GetItemState( RES_FRAMEDIR, + false, &pItem ) ) + { + if( !pFrameDir ) + break; + + pTestFrameDir = static_cast<const SvxFrameDirectionItem *>(pItem); + } + else + { + if( pFrameDir ) + continue; + + } + + if ( SfxItemState::SET == rTestSet.GetItemState( RES_UNKNOWNATR_CONTAINER, + false, &pItem ) ) + { + if( !pAttCnt ) + break; + + pTestAttCnt = static_cast<const SvXMLAttrContainerItem *>(pItem); + } + else + { + if ( pAttCnt ) + continue; + + } + + if( pVertOrient && + pVertOrient->GetVertOrient() != pTestVertOrient->GetVertOrient() ) + continue; + + if( pBrush && ( *pBrush != *pTestBrush ) ) + continue; + + if( pBox && ( *pBox != *pTestBox ) ) + continue; + + if( pNumFormat && pNumFormat->GetValue() != pTestNumFormat->GetValue() ) + continue; + + if( pFrameDir && pFrameDir->GetValue() != pTestFrameDir->GetValue() ) + continue; + + if( pAttCnt && ( *pAttCnt != *pTestAttCnt ) ) + continue; + + // found! + rFrameFormat.SetName( pTestFormat->GetName() ); + bInsert = false; + break; + } + + if( bInsert ) + { + rFrameFormat.SetName( lcl_xmltble_appendBoxPrefix( rNamePrefix, nCol, nRow, bTop ) ); + if ( i != aFormatList.end() ) ++i; + aFormatList.insert( i, &rFrameFormat ); + } + + return bInsert; +} + +class SwXMLTableInfo_Impl +{ + const SwTable *m_pTable; + Reference<XTextSection> m_xBaseSection; + bool m_bBaseSectionValid; + sal_uInt32 m_nPrefix; + +public: + + inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix ); + + const SwTable *GetTable() const { return m_pTable; } + const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); } + + bool IsBaseSectionValid() const { return m_bBaseSectionValid; } + const Reference<XTextSection>& GetBaseSection() const { return m_xBaseSection; } + inline void SetBaseSection( const Reference < XTextSection >& rBase ); + /// The namespace (table or loext) that should be used for the elements. + sal_uInt16 GetPrefix() const { return m_nPrefix; } +}; + +inline SwXMLTableInfo_Impl::SwXMLTableInfo_Impl(const SwTable *pTable, sal_uInt16 nPrefix) : + m_pTable(pTable), + m_bBaseSectionValid(false), + m_nPrefix(nPrefix) +{ +} + +inline void SwXMLTableInfo_Impl::SetBaseSection( + const Reference < XTextSection >& rBaseSection ) +{ + m_xBaseSection = rBaseSection; + m_bBaseSectionValid = true; +} + +void SwXMLExport::ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol ) +{ + // <style:style ...> + CheckAttrList(); + + // style:name="..." + bool bEncoded = false; + AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, + EncodeStyleName( rCol.GetStyleName(), &bEncoded ) ); + if( bEncoded ) + AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCol.GetStyleName() ); + + // style:family="table-column" + AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE_COLUMN ); + + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, true, + true ); + OUStringBuffer sValue; + if( rCol.GetWidthOpt() ) + { + GetTwipUnitConverter().convertMeasureToXML( sValue, + rCol.GetWidthOpt() ); + AddAttribute( XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH, + sValue.makeStringAndClear() ); + } + if( rCol.GetRelWidth() ) + { + sValue.append( static_cast<sal_Int32>(rCol.GetRelWidth()) ); + sValue.append( '*' ); + AddAttribute( XML_NAMESPACE_STYLE, XML_REL_COLUMN_WIDTH, + sValue.makeStringAndClear() ); + } + + { + SvXMLElementExport aElemExport( *this, XML_NAMESPACE_STYLE, + XML_TABLE_COLUMN_PROPERTIES, + true, true ); + } + } +} + +void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines, + sal_uInt32 nAbsWidth, sal_uInt32 nBaseWidth, + const OUString& rNamePrefix, + SwXMLTableColumnsSortByWidth_Impl& rExpCols, + SwXMLTableFrameFormatsSort_Impl& rExpRows, + SwXMLTableFrameFormatsSort_Impl& rExpCells, + SwXMLTableInfo_Impl& rTableInfo, + bool bTop ) +{ + // pass 1: calculate columns + SwXMLTableLines_Impl *pLines = new SwXMLTableLines_Impl( rLines ); + if( !m_pTableLines ) + m_pTableLines.reset(new SwXMLTableLinesCache_Impl); + + m_pTableLines->push_back( pLines ); + + // pass 2: export column styles + { + const SwXMLTableColumns_Impl& rCols = pLines->GetColumns(); + sal_uInt32 nCPos = 0U; + const size_t nColumns = rCols.size(); + for( size_t nColumn=0U; nColumn<nColumns; ++nColumn ) + { + SwXMLTableColumn_Impl *pColumn = rCols[nColumn].get(); + + sal_uInt32 nOldCPos = nCPos; + nCPos = pColumn->GetPos(); + + sal_uInt32 nWidth = nCPos - nOldCPos; + + // If a base width is given, the table has either an automatic + // or margin alignment, or a percentage width. In either case, + // relative widths should be exported. + if( nBaseWidth ) + { + pColumn->SetRelWidth( nWidth ); + } + + // If an absolute width is given, the table either has a fixed + // width, or the current width is known from the layout. In the + // later case, a base width is set in addition and must be used + // to "absolutize" the relative column width. + if( nAbsWidth ) + { + sal_uInt32 nColAbsWidth = nWidth; + if( nBaseWidth ) + { + nColAbsWidth *= nAbsWidth; + nColAbsWidth += (nBaseWidth/2UL); + nColAbsWidth /= nBaseWidth; + } + pColumn->SetWidthOpt( nColAbsWidth, false ); + } + + SwXMLTableColumnsSortByWidth_Impl::const_iterator it = rExpCols.find( pColumn ); + if( it != rExpCols.end() ) + { + pColumn->SetStyleName( (*it)->GetStyleName() ); + } + else + { + if( bTop ) + { + OUString sTmp; + sw_GetTableBoxColStr( nColumn, sTmp ); + pColumn->SetStyleName( rNamePrefix + "." + sTmp ); + } + else + { + pColumn->SetStyleName( rNamePrefix + "." + OUString::number(nColumn + 1U) ); + } + ExportTableColumnStyle( *pColumn ); + rExpCols.insert( pColumn ); + } + } + } + + // pass 3: export line/rows + const size_t nLines = rLines.size(); + for( size_t nLine=0U; nLine<nLines; ++nLine ) + { + SwTableLine *pLine = rLines[nLine]; + + SwFrameFormat *pFrameFormat = pLine->GetFrameFormat(); + if( rExpRows.AddRow( *pFrameFormat, rNamePrefix, nLine ) ) + ExportFormat( *pFrameFormat, XML_TABLE_ROW ); + + const SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + size_t nCol = 0U; + for( size_t nBox=0U; nBox<nBoxes; nBox++ ) + { + SwTableBox *pBox = rBoxes[nBox]; + + if( nBox < nBoxes-1U ) + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + else + nCPos = pLines->GetWidth(); + + // and their index + const size_t nOldCol = nCol; + SwXMLTableColumn_Impl aCol( nCPos ); + SwXMLTableColumns_Impl::const_iterator it = pLines->GetColumns().find( &aCol ); + OSL_ENSURE( it != pLines->GetColumns().end(), "couldn't find column" ); + nCol = it - pLines->GetColumns().begin(); + + const SwStartNode *pBoxSttNd = pBox->GetSttNd(); + if( pBoxSttNd ) + { + SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat(); + if( rExpCells.AddCell( *pFrameFormat2, rNamePrefix, nOldCol, nLine, + bTop) ) + ExportFormat( *pFrameFormat2, XML_TABLE_CELL ); + + Reference < XCell > xCell = SwXCell::CreateXCell( + const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()), + pBox, + const_cast<SwTable *>(rTableInfo.GetTable()) ); + if (xCell.is()) + { + Reference < XText > xText( xCell, UNO_QUERY ); + if( !rTableInfo.IsBaseSectionValid() ) + { + Reference<XPropertySet> xCellPropertySet( xCell, + UNO_QUERY ); + Any aAny = xCellPropertySet->getPropertyValue("TextSection"); + Reference < XTextSection > xTextSection; + aAny >>= xTextSection; + rTableInfo.SetBaseSection( xTextSection ); + } + + const bool bExportContent = bool(getExportFlags() & SvXMLExportFlags::CONTENT ); + if ( !bExportContent ) + { + // AUTOSTYLES - not needed anymore if we are currently exporting content.xml + GetTextParagraphExport()->collectTextAutoStyles( + xText, rTableInfo.GetBaseSection(), IsShowProgress() ); + } + } + else { + OSL_FAIL("here should be a XCell"); + } + } + else + { + ExportTableLinesAutoStyles( pBox->GetTabLines(), + nAbsWidth, nBaseWidth, + lcl_xmltble_appendBoxPrefix( rNamePrefix, + nOldCol, nLine, bTop ), + rExpCols, rExpRows, rExpCells, + rTableInfo ); + } + + nCol++; + } + } +} + +void SwXMLExport::ExportTableAutoStyles( const SwTableNode& rTableNd ) +{ + const SwTable& rTable = rTableNd.GetTable(); + const SwFrameFormat *pTableFormat = rTable.GetFrameFormat(); + + if( !pTableFormat ) + return; + + sal_Int16 eTabHoriOri = pTableFormat->GetHoriOrient().GetHoriOrient(); + const SwFormatFrameSize& rFrameSize = pTableFormat->GetFrameSize(); + + sal_uInt32 nAbsWidth = rFrameSize.GetSize().Width(); + sal_uInt32 nBaseWidth = 0; + sal_Int8 nPercentWidth = rFrameSize.GetWidthPercent(); + + bool bFixAbsWidth = nPercentWidth != 0 || /*text::*/HoriOrientation::NONE == eTabHoriOri + || /*text::*/HoriOrientation::FULL == eTabHoriOri; + if( bFixAbsWidth ) + { + nBaseWidth = nAbsWidth; + nAbsWidth = pTableFormat->FindLayoutRect(true).Width(); + if( !nAbsWidth ) + { + // TODO? + } + } + ExportTableFormat( *pTableFormat, nAbsWidth ); + + SwXMLTableColumnsSortByWidth_Impl aExpCols; + SwXMLTableFrameFormatsSort_Impl aExpRows; + SwXMLTableFrameFormatsSort_Impl aExpCells; + SwXMLTableInfo_Impl aTableInfo( &rTable, XML_NAMESPACE_TABLE ); + ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth, + pTableFormat->GetName(), aExpCols, aExpRows, aExpCells, + aTableInfo, true); + +} + +void SwXMLExport::ExportTableBox( const SwTableBox& rBox, + sal_uInt32 nColSpan, + sal_uInt32 nRowSpan, + SwXMLTableInfo_Impl& rTableInfo ) +{ + const SwStartNode *pBoxSttNd = rBox.GetSttNd(); + if( pBoxSttNd ) + { + const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat(); + if( pFrameFormat ) + { + const OUString& sName = pFrameFormat->GetName(); + if( !sName.isEmpty() ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(sName) ); + } + } + } + + if( nRowSpan != 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, + OUString::number(nRowSpan) ); + } + + if( nColSpan != 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, + OUString::number(nColSpan) ); + } + + { + if( pBoxSttNd ) + { + // start node -> normal cell + // get cell range for table + Reference<XCell> xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()), + const_cast<SwTableBox *>(&rBox), + const_cast<SwTable *>(rTableInfo.GetTable()) ); + + if (xCell.is()) + { + Reference<XText> xText( xCell, UNO_QUERY ); + + // get formula (and protection) + const OUString sCellFormula = xCell->getFormula(); + + // if this cell has a formula, export it + // (with value and number format) + if (!sCellFormula.isEmpty()) + { + const OUString sQValue = + GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOOW, sCellFormula, false ); + // formula + AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue ); + } + + // value and format (if NumberFormat != -1) + Reference<XPropertySet> xCellPropertySet(xCell, + UNO_QUERY); + if (xCellPropertySet.is()) + { + sal_Int32 nNumberFormat = 0; + Any aAny = xCellPropertySet->getPropertyValue("NumberFormat"); + aAny >>= nNumberFormat; + + if (static_cast<sal_Int32>(getSwDefaultTextFormat()) == nNumberFormat) + { + // text format + AddAttribute( XML_NAMESPACE_OFFICE, + XML_VALUE_TYPE, XML_STRING ); + } + else if ( (-1 != nNumberFormat) && !xText->getString().isEmpty() ) + { + // number format key: + // (export values only if cell contains text;) + XMLNumberFormatAttributesExportHelper:: + SetNumberFormatAttributes( + *this, nNumberFormat, xCell->getValue() ); + } + // else: invalid key; ignore + + // cell protection + aAny = xCellPropertySet->getPropertyValue("IsProtected"); + if (*o3tl::doAccess<bool>(aAny)) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, + XML_TRUE ); + } + + if( !rTableInfo.IsBaseSectionValid() ) + { + aAny = xCellPropertySet->getPropertyValue("TextSection"); + Reference < XTextSection > xTextSection; + aAny >>= xTextSection; + rTableInfo.SetBaseSection( xTextSection ); + } + } + + // export cell element + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), + XML_TABLE_CELL, true, true ); + + // export cell content + GetTextParagraphExport()->exportText( xText, + rTableInfo.GetBaseSection(), + IsShowProgress() ); + } + else + { + OSL_FAIL("here should be a XCell"); + ClearAttrList(); + } + } + else + { + // no start node -> merged cells: export subtable in cell + SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, + XML_TABLE_CELL, true, true ); + { + AddAttribute( XML_NAMESPACE_TABLE, XML_IS_SUB_TABLE, + GetXMLToken( XML_TRUE ) ); + + SvXMLElementExport aElemExport( *this, XML_NAMESPACE_TABLE, + XML_TABLE, true, true ); + ExportTableLines( rBox.GetTabLines(), rTableInfo ); + } + } + } +} + +void SwXMLExport::ExportTableLine( const SwTableLine& rLine, + const SwXMLTableLines_Impl& rLines, + SwXMLTableInfo_Impl& rTableInfo ) +{ + if( rLine.hasSoftPageBreak() ) + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT, + XML_SOFT_PAGE_BREAK, true, true ); + } + const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat(); + if( pFrameFormat ) + { + const OUString& sName = pFrameFormat->GetName(); + if( !sName.isEmpty() ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(sName) ); + } + } + + { + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_ROW, true, true ); + const SwTableBoxes& rBoxes = rLine.GetTabBoxes(); + const size_t nBoxes = rBoxes.size(); + + sal_uInt32 nCPos = 0U; + size_t nCol = 0U; + for( size_t nBox=0U; nBox<nBoxes; ++nBox ) + { + const SwTableBox *pBox = rBoxes[nBox]; + + // NEW TABLES + const long nRowSpan = pBox->getRowSpan(); + if( nRowSpan < 1 ) + { + SvXMLElementExport aElem2( *this, rTableInfo.GetPrefix(), + XML_COVERED_TABLE_CELL, true, + false ); + } + + if( nBox < nBoxes-1U ) + nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox ); + else + nCPos = rLines.GetWidth(); + + // and their index + const size_t nOldCol = nCol; + SwXMLTableColumn_Impl aCol( nCPos ); + SwXMLTableColumns_Impl::const_iterator it = rLines.GetColumns().find( &aCol ); + OSL_ENSURE( it != rLines.GetColumns().end(), "couldn't find column" ); + nCol = it - rLines.GetColumns().begin(); + + // #i95726# - Some fault tolerance, if table is somehow corrupted. + if ( nCol < nOldCol ) + { + OSL_FAIL( "table and/or table information seems to be corrupted." ); + // NOTE: nOldCol is not necessarily a valid index into + // GetColumns(), but that doesn't matter here + nCol = nOldCol; + } + + const sal_uInt32 nColSpan = nCol - nOldCol + 1U; + + if ( nRowSpan >= 1 ) + ExportTableBox( *pBox, nColSpan, static_cast< sal_uInt32 >(nRowSpan), rTableInfo ); + + for( size_t i=nOldCol; i<nCol; ++i ) + { + SvXMLElementExport aElemExport( *this, rTableInfo.GetPrefix(), + XML_COVERED_TABLE_CELL, true, + false ); + } + + nCol++; + } + } +} + +void SwXMLExport::ExportTableLines( const SwTableLines& rLines, + SwXMLTableInfo_Impl& rTableInfo, + sal_uInt32 nHeaderRows ) +{ + OSL_ENSURE( m_pTableLines && !m_pTableLines->empty(), + "SwXMLExport::ExportTableLines: table columns infos missing" ); + if( !m_pTableLines || m_pTableLines->empty() ) + return; + + SwXMLTableLines_Impl* pLines = nullptr; + size_t nInfoPos; + for( nInfoPos=0; nInfoPos < m_pTableLines->size(); nInfoPos++ ) + { + if( m_pTableLines->at( nInfoPos )->GetLines() == &rLines ) + { + pLines = m_pTableLines->at( nInfoPos ); + break; + } + } + OSL_ENSURE( pLines, + "SwXMLExport::ExportTableLines: table columns info missing" ); + OSL_ENSURE( 0==nInfoPos, + "SwXMLExport::ExportTableLines: table columns infos are unsorted" ); + if( !pLines ) + return; + + SwXMLTableLinesCache_Impl::iterator it = m_pTableLines->begin(); + advance( it, nInfoPos ); + m_pTableLines->erase( it ); + + if( m_pTableLines->empty() ) + m_pTableLines.reset(); + + // pass 2: export columns + const SwXMLTableColumns_Impl& rCols = pLines->GetColumns(); + size_t nColumn = 0U; + const size_t nColumns = rCols.size(); + sal_Int32 nColRep = 1; + SwXMLTableColumn_Impl *pColumn = (nColumns > 0) ? rCols.front().get() : nullptr; + while( pColumn ) + { + nColumn++; + SwXMLTableColumn_Impl *pNextColumn = + (nColumn < nColumns) ? rCols[nColumn].get() : nullptr; + if( pNextColumn && + pNextColumn->GetStyleName() == pColumn->GetStyleName() ) + { + nColRep++; + } + else + { + AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME, + EncodeStyleName(pColumn->GetStyleName()) ); + + if( nColRep > 1 ) + { + AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, + OUString::number(nColRep) ); + } + + { + SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_COLUMN, true, true ); + } + + nColRep = 1; + } + pColumn = pNextColumn; + } + + // pass 3: export line/rows + const size_t nLines = rLines.size(); + // export header rows, if present + if( nHeaderRows > 0 ) + { + SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, + XML_TABLE_HEADER_ROWS, true, true ); + + OSL_ENSURE( nHeaderRows <= nLines, "more headers then lines?" ); + for( size_t nLine = 0U; nLine < nHeaderRows; ++nLine ) + ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo ); + } + // export remaining rows + for( size_t nLine = nHeaderRows; nLine < nLines; ++nLine ) + { + ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo ); + } + + delete pLines; +} + +static void lcl_xmltble_ClearName_Line( SwTableLine* pLine ); + +static void lcl_xmltble_ClearName_Box( SwTableBox* pBox ) +{ + if( !pBox->GetSttNd() ) + { + for( SwTableLine* pLine : pBox->GetTabLines() ) + lcl_xmltble_ClearName_Line( pLine ); + } + else + { + SwFrameFormat *pFrameFormat = pBox->GetFrameFormat(); + if( pFrameFormat && !pFrameFormat->GetName().isEmpty() ) + pFrameFormat->SetName( OUString() ); + } +} + +void lcl_xmltble_ClearName_Line( SwTableLine* pLine ) +{ + for( SwTableBox* pBox : pLine->GetTabBoxes() ) + lcl_xmltble_ClearName_Box( pBox ); +} + +void SwXMLExport::ExportTable( const SwTableNode& rTableNd ) +{ + ::std::optional<sal_uInt16> oPrefix = XML_NAMESPACE_TABLE; + if (const SwFrameFormat* pFlyFormat = rTableNd.GetFlyFormat()) + { + if (SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT)) + { + // TODO ODF 1.4 OFFICE-3761 + if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + oPrefix = XML_NAMESPACE_LO_EXT; + } + else + { + oPrefix.reset(); // no export to OASIS namespace yet + } + } + } + + if (oPrefix) + { + const SwTable& rTable = rTableNd.GetTable(); + const SwFrameFormat *pTableFormat = rTable.GetFrameFormat(); + if (pTableFormat && !pTableFormat->GetName().isEmpty()) + { + AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pTableFormat->GetName()); + AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, + EncodeStyleName(pTableFormat->GetName())); + } + + // table:template-name= + if (!rTable.GetTableStyleName().isEmpty()) + { + OUString sStyleName; + SwStyleNameMapper::FillProgName(rTable.GetTableStyleName(), sStyleName, SwGetPoolIdFromName::TabStyle); + AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sStyleName); + } + + SvXMLElementExport aElem(*this, *oPrefix, XML_TABLE, true, true); + + // export DDE source (if this is a DDE table) + if ( dynamic_cast<const SwDDETable*>( &rTable) != nullptr ) + { + // get DDE Field Type (contains the DDE connection) + const SwDDEFieldType* pDDEFieldType = + static_cast<const SwDDETable&>(rTable).GetDDEFieldType(); + + // connection name + AddAttribute( XML_NAMESPACE_OFFICE, XML_NAME, + pDDEFieldType->GetName() ); + + // DDE command + const OUString& sCmd = pDDEFieldType->GetCmd(); + sal_Int32 nIdx{ 0 }; + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_ITEM, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_TOPIC, + sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) ); + + // auto update + if (pDDEFieldType->GetType() == SfxLinkUpdateMode::ALWAYS) + { + AddAttribute( XML_NAMESPACE_OFFICE, + XML_AUTOMATIC_UPDATE, XML_TRUE ); + } + + // DDE source element (always empty) + SvXMLElementExport aSource(*this, XML_NAMESPACE_OFFICE, + XML_DDE_SOURCE, true, false); + } + + SwXMLTableInfo_Impl aTableInfo(&rTable, *oPrefix); + ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() ); + + for( SwTableLine *pLine : const_cast<SwTable &>(rTable).GetTabLines() ) + lcl_xmltble_ClearName_Line( pLine ); + } +} + +void SwXMLTextParagraphExport::exportTableAutoStyles() { + for (const auto* pTableNode : maTableNodes) + { + static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode); + } +} + +void SwXMLTextParagraphExport::exportTable( + const Reference < XTextContent > & rTextContent, + bool bAutoStyles, bool _bProgress ) +{ + bool bOldShowProgress = static_cast<SwXMLExport&>(GetExport()).IsShowProgress(); + static_cast<SwXMLExport&>(GetExport()).SetShowProgress( _bProgress ); + + Reference < XTextTable > xTextTable( rTextContent, UNO_QUERY ); + OSL_ENSURE( xTextTable.is(), "text table missing" ); + if( xTextTable.is() ) + { + SwXTextTable *pXTable = nullptr; + Reference<XUnoTunnel> xTableTunnel( rTextContent, UNO_QUERY); + if( xTableTunnel.is() ) + { + pXTable = reinterpret_cast< SwXTextTable * >( + sal::static_int_cast< sal_IntPtr >( xTableTunnel->getSomething( SwXTextTable::getUnoTunnelId() ))); + OSL_ENSURE( pXTable, "SwXTextTable missing" ); + } + if( pXTable ) + { + SwFrameFormat *const pFormat = pXTable->GetFrameFormat(); + OSL_ENSURE( pFormat, "table format missing" ); + const SwTable *pTable = SwTable::FindTable( pFormat ); + OSL_ENSURE( pTable, "table missing" ); + const SwTableNode *pTableNd = pTable->GetTableNode(); + OSL_ENSURE( pTableNd, "table node missing" ); + if( bAutoStyles ) + { + SwNodeIndex aIdx( *pTableNd ); + // AUTOSTYLES: Optimization: Do not export table autostyle if + // we are currently exporting the content.xml stuff and + // the table is located in header/footer: + // During the flat XML export (used e.g. by .sdw-export) + // ALL flags are set at the same time. + const bool bExportStyles = bool( GetExport().getExportFlags() & SvXMLExportFlags::STYLES ); + if (!isAutoStylesCollected() + && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(aIdx))) + { + maTableNodes.push_back(pTableNd); + // Collect all tables inside cells of this table, too + const auto aCellNames = pXTable->getCellNames(); + for (const OUString& rCellName : aCellNames) + { + css::uno::Reference<css::container::XEnumerationAccess> xCell( + pXTable->getCellByName(rCellName), css::uno::UNO_QUERY); + if (!xCell) + continue; + auto xEnumeration = xCell->createEnumeration(); + while (xEnumeration->hasMoreElements()) + { + if (css::uno::Reference<css::text::XTextTable> xInnerTable{ + xEnumeration->nextElement(), css::uno::UNO_QUERY }) + exportTable(xInnerTable, bAutoStyles, _bProgress); + } + } + } + } + else + { + static_cast<SwXMLExport&>(GetExport()).ExportTable( *pTableNd ); + } + } + } + + static_cast<SwXMLExport&>(GetExport()).SetShowProgress( bOldShowProgress ); +} + +void SwXMLExport::DeleteTableLines() +{ + if ( m_pTableLines ) + { + for (SwXMLTableLines_Impl* p : *m_pTableLines) + delete p; + m_pTableLines->clear(); + m_pTableLines.reset(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltbli.cxx b/sw/source/filter/xml/xmltbli.cxx new file mode 100644 index 000000000..5a95befac --- /dev/null +++ b/sw/source/filter/xml/xmltbli.cxx @@ -0,0 +1,2875 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/frame/XModel.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <o3tl/numeric.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <svl/itemset.hxx> +#include <svl/zformat.hxx> +#include <sax/tools/converter.hxx> +#include <unotools/configmgr.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltkmap.hxx> +#include <xmloff/nmspmap.hxx> + +#include <xmloff/families.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/i18nmap.hxx> +#include <editeng/protitem.hxx> +#include <editeng/lrspitem.hxx> +#include <poolfmt.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtfordr.hxx> +#include <doc.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <swtable.hxx> +#include <swtblfmt.hxx> +#include <pam.hxx> +#include <unotbl.hxx> +#include <unotextrange.hxx> +#include <cellatr.hxx> +#include <swddetbl.hxx> +#include <ddefld.hxx> +#include <sfx2/linkmgr.hxx> +#include "xmlimp.hxx" +#include "xmltbli.hxx" +#include <vcl/svapp.hxx> +#include <ndtxt.hxx> +#include <SwStyleNameMapper.hxx> + +#include <algorithm> +#include <vector> +#include <memory> + +#include <limits.h> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::xml::sax; +using namespace ::xmloff::token; + +namespace { + +enum SwXMLTableElemTokens +{ + XML_TOK_TABLE_HEADER_COLS, + XML_TOK_TABLE_COLS, + XML_TOK_TABLE_COL, + XML_TOK_TABLE_HEADER_ROWS, + XML_TOK_TABLE_ROWS, + XML_TOK_TABLE_ROW, + XML_TOK_OFFICE_DDE_SOURCE, +}; + +enum SwXMLTableCellAttrTokens +{ + XML_TOK_TABLE_XMLID, + XML_TOK_TABLE_STYLE_NAME, + XML_TOK_TABLE_NUM_COLS_SPANNED, + XML_TOK_TABLE_NUM_ROWS_SPANNED, + XML_TOK_TABLE_NUM_COLS_REPEATED, + XML_TOK_TABLE_FORMULA, + XML_TOK_TABLE_VALUE, + XML_TOK_TABLE_TIME_VALUE, + XML_TOK_TABLE_DATE_VALUE, + XML_TOK_TABLE_BOOLEAN_VALUE, + XML_TOK_TABLE_PROTECTED, + XML_TOK_TABLE_STRING_VALUE, + XML_TOK_TABLE_VALUE_TYPE, +}; + +} + +static const SvXMLTokenMapEntry aTableElemTokenMap[] = +{ + { XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, + XML_TOK_TABLE_HEADER_COLS }, + { XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, XML_TOK_TABLE_COLS }, + { XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, XML_TOK_TABLE_COL }, + { XML_NAMESPACE_LO_EXT, XML_TABLE_COLUMN, XML_TOK_TABLE_COL }, + { XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, + XML_TOK_TABLE_HEADER_ROWS }, + { XML_NAMESPACE_TABLE, XML_TABLE_ROWS, XML_TOK_TABLE_ROWS }, + { XML_NAMESPACE_TABLE, XML_TABLE_ROW, XML_TOK_TABLE_ROW }, + { XML_NAMESPACE_LO_EXT, XML_TABLE_ROW, XML_TOK_TABLE_ROW }, + { XML_NAMESPACE_OFFICE, XML_DDE_SOURCE, + XML_TOK_OFFICE_DDE_SOURCE }, + + // There are slight differences between <table:table-columns> and + // <table:table-columns-groups>. However, none of these are + // supported in Writer (they are Calc-only features), so we + // support column groups by simply using the <table:table-columns> + // token for column groups, too. + { XML_NAMESPACE_TABLE, XML_TABLE_COLUMN_GROUP, XML_TOK_TABLE_COLS }, + + XML_TOKEN_MAP_END +}; + +static const SvXMLTokenMapEntry aTableCellAttrTokenMap[] = +{ + { XML_NAMESPACE_XML, XML_ID, XML_TOK_TABLE_XMLID }, + { XML_NAMESPACE_TABLE, XML_STYLE_NAME, XML_TOK_TABLE_STYLE_NAME }, + { XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, XML_TOK_TABLE_NUM_COLS_SPANNED }, + { XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, XML_TOK_TABLE_NUM_ROWS_SPANNED }, + { XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, XML_TOK_TABLE_NUM_COLS_REPEATED }, + { XML_NAMESPACE_TABLE, XML_FORMULA, XML_TOK_TABLE_FORMULA }, + { XML_NAMESPACE_OFFICE, XML_VALUE, XML_TOK_TABLE_VALUE }, + { XML_NAMESPACE_OFFICE, XML_TIME_VALUE, XML_TOK_TABLE_TIME_VALUE }, + { XML_NAMESPACE_OFFICE, XML_DATE_VALUE, XML_TOK_TABLE_DATE_VALUE }, + { XML_NAMESPACE_OFFICE, XML_BOOLEAN_VALUE, XML_TOK_TABLE_BOOLEAN_VALUE }, + { XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TOK_TABLE_PROTECTED }, + { XML_NAMESPACE_TABLE, XML_PROTECT, XML_TOK_TABLE_PROTECTED }, // for backwards compatibility with SRC629 (and before) + { XML_NAMESPACE_OFFICE, XML_STRING_VALUE, XML_TOK_TABLE_STRING_VALUE }, + { XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_TOK_TABLE_VALUE_TYPE }, + XML_TOKEN_MAP_END +}; + +const SvXMLTokenMap& SwXMLImport::GetTableElemTokenMap() +{ + if( !m_pTableElemTokenMap ) + m_pTableElemTokenMap.reset(new SvXMLTokenMap( aTableElemTokenMap )); + + return *m_pTableElemTokenMap; +} + +const SvXMLTokenMap& SwXMLImport::GetTableCellAttrTokenMap() +{ + if( !m_pTableCellAttrTokenMap ) + m_pTableCellAttrTokenMap.reset(new SvXMLTokenMap( aTableCellAttrTokenMap )); + + return *m_pTableCellAttrTokenMap; +} + +class SwXMLTableCell_Impl +{ + OUString aStyleName; + + OUString m_StringValue; + + OUString sFormula; // cell formula; valid if length > 0 + double dValue; // formula value + + SvXMLImportContextRef xSubTable; + + const SwStartNode *pStartNode; + sal_uInt32 nRowSpan; + sal_uInt32 nColSpan; + + bool bProtected : 1; + bool bHasValue; // determines whether dValue attribute is valid + bool mbCovered; + bool m_bHasStringValue; + +public: + + SwXMLTableCell_Impl( sal_uInt32 nRSpan=1, sal_uInt32 nCSpan=1 ) : + dValue( 0.0 ), + pStartNode( nullptr ), + nRowSpan( nRSpan ), + nColSpan( nCSpan ), + bProtected( false ), + bHasValue( false ), + mbCovered( false ) + , m_bHasStringValue(false) + {} + + inline void Set( const OUString& rStyleName, + sal_uInt32 nRSpan, sal_uInt32 nCSpan, + const SwStartNode *pStNd, SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasValue, + bool bCovered, + double dVal, + OUString const*const pStringValue); + + bool IsUsed() const { return pStartNode!=nullptr || + xSubTable.is() || bProtected;} + + sal_uInt32 GetRowSpan() const { return nRowSpan; } + void SetRowSpan( sal_uInt32 nSet ) { nRowSpan = nSet; } + sal_uInt32 GetColSpan() const { return nColSpan; } + const OUString& GetStyleName() const { return aStyleName; } + const OUString& GetFormula() const { return sFormula; } + double GetValue() const { return dValue; } + bool HasValue() const { return bHasValue; } + bool IsProtected() const { return bProtected; } + bool IsCovered() const { return mbCovered; } + bool HasStringValue() const { return m_bHasStringValue; } + OUString const* GetStringValue() const { + return m_bHasStringValue ? &m_StringValue : nullptr; + } + + const SwStartNode *GetStartNode() const { return pStartNode; } + inline void SetStartNode( const SwStartNode *pSttNd ); + + inline SwXMLTableContext *GetSubTable() const; + + inline void Dispose(); +}; + +inline void SwXMLTableCell_Impl::Set( const OUString& rStyleName, + sal_uInt32 nRSpan, sal_uInt32 nCSpan, + const SwStartNode *pStNd, + SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasVal, + bool bCov, + double dVal, + OUString const*const pStringValue ) +{ + aStyleName = rStyleName; + nRowSpan = nRSpan; + nColSpan = nCSpan; + pStartNode = pStNd; + xSubTable = pTable; + dValue = dVal; + bHasValue = bHasVal; + mbCovered = bCov; + if (pStringValue) + { + m_StringValue = *pStringValue; + } + m_bHasStringValue = (pStringValue != nullptr); + bProtected = bProtect; + + // set formula, if valid + if (pFormula != nullptr) + { + sFormula = *pFormula; + } +} + +inline void SwXMLTableCell_Impl::SetStartNode( const SwStartNode *pSttNd ) +{ + pStartNode = pSttNd; + xSubTable = nullptr; +} + +inline SwXMLTableContext *SwXMLTableCell_Impl::GetSubTable() const +{ + return static_cast<SwXMLTableContext *>(xSubTable.get()); +} + +inline void SwXMLTableCell_Impl::Dispose() +{ + if( xSubTable.is() ) + xSubTable = nullptr; +} + +class SwXMLTableRow_Impl +{ + OUString aStyleName; + OUString aDfltCellStyleName; + std::vector<std::unique_ptr<SwXMLTableCell_Impl>> m_Cells; + bool bSplitable; + +public: + + SwXMLTableRow_Impl( const OUString& rStyleName, sal_uInt32 nCells, + const OUString *pDfltCellStyleName = nullptr ); + + inline SwXMLTableCell_Impl *GetCell( sal_uInt32 nCol ); + + inline void Set( const OUString& rStyleName, + const OUString& rDfltCellStyleName ); + + void Expand( sal_uInt32 nCells, bool bOneCell ); + + void SetSplitable( bool bSet ) { bSplitable = bSet; } + bool IsSplitable() const { return bSplitable; } + + const OUString& GetStyleName() const { return aStyleName; } + const OUString& GetDefaultCellStyleName() const { return aDfltCellStyleName; } + + void Dispose(); +}; + +SwXMLTableRow_Impl::SwXMLTableRow_Impl( const OUString& rStyleName, + sal_uInt32 nCells, + const OUString *pDfltCellStyleName ) : + aStyleName( rStyleName ), + bSplitable( false ) +{ + if( pDfltCellStyleName ) + aDfltCellStyleName = *pDfltCellStyleName; + OSL_ENSURE( nCells <= USHRT_MAX, + "SwXMLTableRow_Impl::SwXMLTableRow_Impl: too many cells" ); + if( nCells > USHRT_MAX ) + nCells = USHRT_MAX; + + for( sal_uInt32 i=0U; i<nCells; ++i ) + { + m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>()); + } +} + +inline SwXMLTableCell_Impl *SwXMLTableRow_Impl::GetCell( sal_uInt32 nCol ) +{ + OSL_ENSURE( nCol < USHRT_MAX, + "SwXMLTableRow_Impl::GetCell: column number is too big" ); + // #i95726# - some fault tolerance + OSL_ENSURE( nCol < m_Cells.size(), + "SwXMLTableRow_Impl::GetCell: column number is out of bound" ); + return nCol < m_Cells.size() ? m_Cells[nCol].get() : nullptr; +} + +void SwXMLTableRow_Impl::Expand( sal_uInt32 nCells, bool bOneCell ) +{ + OSL_ENSURE( nCells <= USHRT_MAX, + "SwXMLTableRow_Impl::Expand: too many cells" ); + if( nCells > USHRT_MAX ) + nCells = USHRT_MAX; + + sal_uInt32 nColSpan = nCells - m_Cells.size(); + for (size_t i = m_Cells.size(); i < nCells; ++i) + { + m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>( + 1UL, bOneCell ? nColSpan : 1UL)); + nColSpan--; + } + + OSL_ENSURE( nCells <= m_Cells.size(), + "SwXMLTableRow_Impl::Expand: wrong number of cells" ); +} + +inline void SwXMLTableRow_Impl::Set( const OUString& rStyleName, + const OUString& rDfltCellStyleName ) +{ + aStyleName = rStyleName; + aDfltCellStyleName = rDfltCellStyleName; +} + +void SwXMLTableRow_Impl::Dispose() +{ + for (auto & pCell : m_Cells) + { + pCell->Dispose(); + } +} + +namespace { + +class SwXMLTableCellContext_Impl : public SvXMLImportContext +{ + OUString m_aStyleName; + OUString m_sFormula; + OUString m_sSaveParaDefault; + OUString m_StringValue; + + SvXMLImportContextRef m_xMyTable; + + double m_fValue; + bool m_bHasValue; + bool m_bHasStringValue; + bool m_bValueTypeIsString; + bool m_bProtect; + + sal_uInt32 m_nRowSpan; + sal_uInt32 m_nColSpan; + sal_uInt32 m_nColRepeat; + + bool m_bHasTextContent : 1; + bool m_bHasTableContent : 1; + + SwXMLTableContext *GetTable() { return static_cast<SwXMLTableContext *>(m_xMyTable.get()); } + + bool HasContent() const { return m_bHasTextContent || m_bHasTableContent; } + inline void InsertContent_(); + inline void InsertContent(); + inline void InsertContent( SwXMLTableContext *pTable ); + +public: + + SwXMLTableCellContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable ); + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + virtual void EndElement() override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableCellContext_Impl::SwXMLTableCellContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + m_sFormula(), + m_xMyTable( pTable ), + m_fValue( 0.0 ), + m_bHasValue( false ), + m_bHasStringValue(false), + m_bValueTypeIsString(false), + m_bProtect( false ), + m_nRowSpan( 1 ), + m_nColSpan( 1 ), + m_nColRepeat( 1 ), + m_bHasTextContent( false ), + m_bHasTableContent( false ) +{ + m_sSaveParaDefault = GetImport().GetTextImport()->GetCellParaStyleDefault(); + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + const SvXMLTokenMap& rTokenMap = + GetSwImport().GetTableCellAttrTokenMap(); + switch( rTokenMap.Get( nPrefix, aLocalName ) ) + { + case XML_TOK_TABLE_STYLE_NAME: + m_aStyleName = rValue; + GetImport().GetTextImport()->SetCellParaStyleDefault(rValue); + break; + case XML_TOK_TABLE_NUM_COLS_SPANNED: + m_nColSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, rValue.toInt32())); + if (m_nColSpan > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-spanned " << m_nColSpan); + m_nColSpan = 1; + } + break; + case XML_TOK_TABLE_NUM_ROWS_SPANNED: + m_nRowSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, rValue.toInt32())); + if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing())) + { + SAL_INFO("sw.xml", "ignoring huge table:number-rows-spanned " << m_nRowSpan); + m_nRowSpan = 1; + } + break; + case XML_TOK_TABLE_NUM_COLS_REPEATED: + m_nColRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, rValue.toInt32())); + if (m_nColRepeat > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << m_nColRepeat); + m_nColRepeat = 1; + } + break; + case XML_TOK_TABLE_FORMULA: + { + OUString sTmp; + const sal_uInt16 nPrefix2 = GetImport().GetNamespaceMap(). + GetKeyByAttrValueQName(rValue, &sTmp); + m_sFormula = XML_NAMESPACE_OOOW == nPrefix2 ? sTmp : rValue; + } + break; + case XML_TOK_TABLE_VALUE: + { + double fTmp; + if (::sax::Converter::convertDouble(fTmp, rValue)) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_TOK_TABLE_TIME_VALUE: + { + double fTmp; + if (::sax::Converter::convertDuration(fTmp, rValue)) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_TOK_TABLE_DATE_VALUE: + { + double fTmp; + if (GetImport().GetMM100UnitConverter().convertDateTime(fTmp, + rValue)) + { + m_fValue = fTmp; + m_bHasValue = true; + } + } + break; + case XML_TOK_TABLE_BOOLEAN_VALUE: + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, rValue)) + { + m_fValue = (bTmp ? 1.0 : 0.0); + m_bHasValue = true; + } + } + break; + case XML_TOK_TABLE_PROTECTED: + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, rValue)) + { + m_bProtect = bTmp; + } + } + break; + case XML_TOK_TABLE_STRING_VALUE: + { + m_StringValue = rValue; + m_bHasStringValue = true; + } + break; + case XML_TOK_TABLE_VALUE_TYPE: + { + if ("string" == rValue) + { + m_bValueTypeIsString = true; + } + // ignore other types - it would be correct to require + // matching value-type and $type-value attributes, + // but we've been reading those without checking forever. + } + break; + } + } +} + +inline void SwXMLTableCellContext_Impl::InsertContent_() +{ + SwStartNode const*const pStartNode( GetTable()->InsertTableSection(nullptr, + (m_bHasStringValue && m_bValueTypeIsString && + !m_aStyleName.isEmpty()) ? & m_aStyleName : nullptr) ); + GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, + pStartNode, + nullptr, m_bProtect, &m_sFormula, m_bHasValue, m_fValue, + (m_bHasStringValue && m_bValueTypeIsString) ? &m_StringValue : nullptr); +} + +inline void SwXMLTableCellContext_Impl::InsertContent() +{ + OSL_ENSURE( !HasContent(), "content already there" ); + m_bHasTextContent = true; + InsertContent_(); +} + +inline void SwXMLTableCellContext_Impl::InsertContent( + SwXMLTableContext *pTable ) +{ + GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, nullptr, pTable, m_bProtect ); + m_bHasTableContent = true; +} + +SvXMLImportContextRef SwXMLTableCellContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + bool bSubTable = false; + if( XML_NAMESPACE_TABLE == nPrefix && + IsXMLToken( rLocalName, XML_TABLE ) ) + { + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix2 = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + if( XML_NAMESPACE_TABLE == nPrefix2 && + IsXMLToken( aLocalName, XML_IS_SUB_TABLE ) && + IsXMLToken( xAttrList->getValueByIndex( i ), XML_TRUE ) ) + { + bSubTable = true; + } + //FIXME: RDFa + } + } + + if( bSubTable ) + { + if( !HasContent() ) + { + SwXMLTableContext *pTableContext = + new SwXMLTableContext( GetSwImport(), nPrefix, rLocalName, + GetTable() ); + pContext = pTableContext; + if( GetTable()->IsValid() ) + InsertContent( pTableContext ); + + GetTable()->SetHasSubTables( true ); + } + } + else + { + if( GetTable()->IsValid() && !HasContent() ) + InsertContent(); + // fdo#60842: "office:string-value" overrides text content -> no import + if (!(m_bValueTypeIsString && m_bHasStringValue)) + { + pContext = GetImport().GetTextImport()->CreateTextChildContext( + GetImport(), nPrefix, rLocalName, xAttrList, + XMLTextType::Cell ); + } + } + + return pContext; +} + +void SwXMLTableCellContext_Impl::EndElement() +{ + if( GetTable()->IsValid() ) + { + if( m_bHasTextContent ) + { + GetImport().GetTextImport()->DeleteParagraph(); + if( m_nColRepeat > 1 && m_nColSpan == 1 ) + { + // The original text is invalid after deleting the last + // paragraph + Reference < XTextCursor > xSrcTextCursor = + GetImport().GetTextImport()->GetText()->createTextCursor(); + xSrcTextCursor->gotoEnd( true ); + + // Until we have an API for copying we have to use the core. + Reference<XUnoTunnel> xSrcCursorTunnel( xSrcTextCursor, UNO_QUERY); + assert(xSrcCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pSrcTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xSrcCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + assert(pSrcTextCursor && "SwXTextCursor missing"); + SwDoc *pDoc = pSrcTextCursor->GetDoc(); + const SwPaM *pSrcPaM = pSrcTextCursor->GetPaM(); + + while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() ) + { + InsertContent_(); + + Reference<XUnoTunnel> xDstCursorTunnel( + GetImport().GetTextImport()->GetCursor(), UNO_QUERY); + assert(xDstCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pDstTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xDstCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() )) ); + assert(pDstTextCursor && "SwXTextCursor missing"); + SwPaM aSrcPaM(*pSrcPaM->GetMark(), *pSrcPaM->GetPoint()); + SwPosition aDstPos( *pDstTextCursor->GetPaM()->GetPoint() ); + pDoc->getIDocumentContentOperations().CopyRange(aSrcPaM, aDstPos, SwCopyFlags::CheckPosInFly); + + m_nColRepeat--; + } + } + } + else if( !m_bHasTableContent ) + { + InsertContent(); + if( m_nColRepeat > 1 && m_nColSpan == 1 ) + { + while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() ) + { + InsertContent_(); + m_nColRepeat--; + } + } + } + } + GetImport().GetTextImport()->SetCellParaStyleDefault(m_sSaveParaDefault); +} + +namespace { + +class SwXMLTableColContext_Impl : public SvXMLImportContext +{ + SvXMLImportContextRef xMyTable; + + SwXMLTableContext *GetTable() { return static_cast<SwXMLTableContext *>(xMyTable.get()); } + +public: + + SwXMLTableColContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable ); + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableColContext_Impl::SwXMLTableColContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + xMyTable( pTable ) +{ + sal_uInt32 nColRep = 1; + OUString aStyleName, aDfltCellStyleName; + + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + if( XML_NAMESPACE_TABLE == nPrefix ) + { + if( IsXMLToken( aLocalName, XML_STYLE_NAME ) ) + aStyleName = rValue; + else if( IsXMLToken( aLocalName, XML_NUMBER_COLUMNS_REPEATED ) ) + { + nColRep = static_cast<sal_uInt32>(std::max<sal_Int32>(1, rValue.toInt32())); + if (nColRep > 256) + { + SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << nColRep); + nColRep = 1; + } + } + else if( IsXMLToken( aLocalName, XML_DEFAULT_CELL_STYLE_NAME ) ) + aDfltCellStyleName = rValue; + } + else if ( (XML_NAMESPACE_XML == nPrefix) && + IsXMLToken( aLocalName, XML_ID ) ) + { + //FIXME where to put this??? columns do not actually exist in writer... + } + } + + sal_Int32 nWidth = MINLAY; + bool bRelWidth = true; + if( !aStyleName.isEmpty() ) + { + const SfxPoolItem *pItem; + const SfxItemSet *pAutoItemSet = nullptr; + if( GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_COLUMN, + aStyleName, &pAutoItemSet ) && + pAutoItemSet && + SfxItemState::SET == pAutoItemSet->GetItemState( RES_FRM_SIZE, false, + &pItem ) ) + { + const SwFormatFrameSize *pSize = static_cast<const SwFormatFrameSize *>(pItem); + nWidth = pSize->GetWidth(); + bRelWidth = SwFrameSize::Variable == pSize->GetHeightSizeType(); + } + } + + if( nWidth ) + { + while( nColRep-- && GetTable()->IsInsertColPossible() ) + GetTable()->InsertColumn( nWidth, bRelWidth, &aDfltCellStyleName ); + } +} + +namespace { + +class SwXMLTableColsContext_Impl : public SvXMLImportContext +{ + SvXMLImportContextRef xMyTable; + + SwXMLTableContext *GetTable() { return static_cast<SwXMLTableContext *>(xMyTable.get()); } + +public: + + SwXMLTableColsContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + SwXMLTableContext *pTable ); + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableColsContext_Impl::SwXMLTableColsContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + SwXMLTableContext *pTable ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + xMyTable( pTable ) +{ +} + +SvXMLImportContextRef SwXMLTableColsContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + if( XML_NAMESPACE_TABLE == nPrefix && + IsXMLToken( rLocalName, XML_TABLE_COLUMN ) && + GetTable()->IsInsertColPossible() ) + pContext = new SwXMLTableColContext_Impl( GetSwImport(), nPrefix, + rLocalName, xAttrList, + GetTable() ); + + return pContext; +} + +namespace { + +class SwXMLTableRowContext_Impl : public SvXMLImportContext +{ + SvXMLImportContextRef xMyTable; + + sal_uInt32 nRowRepeat; + + SwXMLTableContext *GetTable() { return static_cast<SwXMLTableContext *>(xMyTable.get()); } + +public: + + SwXMLTableRowContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable, bool bInHead=false ); + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + + virtual void EndElement() override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableRowContext_Impl::SwXMLTableRowContext_Impl( SwXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList, + SwXMLTableContext *pTable, + bool bInHead ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + xMyTable( pTable ), + nRowRepeat( 1 ) +{ + OUString aStyleName, aDfltCellStyleName; + OUString sXmlId; + + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + if( XML_NAMESPACE_TABLE == nPrefix ) + { + if( IsXMLToken( aLocalName, XML_STYLE_NAME ) ) + { + aStyleName = rValue; + } + else if( IsXMLToken( aLocalName, XML_NUMBER_ROWS_REPEATED ) ) + { + nRowRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, rValue.toInt32())); + if (nRowRepeat > 8192 || (nRowRepeat > 256 && utl::ConfigManager::IsFuzzing())) + { + SAL_INFO("sw.xml", "ignoring huge table:number-rows-repeated " << nRowRepeat); + nRowRepeat = 1; + } + } + else if( IsXMLToken( aLocalName, XML_DEFAULT_CELL_STYLE_NAME ) ) + { + aDfltCellStyleName = rValue; + } + } + else if ( (XML_NAMESPACE_XML == nPrefix) && + IsXMLToken( aLocalName, XML_ID ) ) + { + sXmlId = rValue; + } + } + if( GetTable()->IsValid() ) + GetTable()->InsertRow( aStyleName, aDfltCellStyleName, bInHead ); +} + +void SwXMLTableRowContext_Impl::EndElement() +{ + if( GetTable()->IsValid() ) + { + GetTable()->FinishRow(); + + if( nRowRepeat > 1 ) + GetTable()->InsertRepRows( nRowRepeat ); + } +} + +SvXMLImportContextRef SwXMLTableRowContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + if( XML_NAMESPACE_TABLE == nPrefix || XML_NAMESPACE_LO_EXT == nPrefix ) + { + if( IsXMLToken( rLocalName, XML_TABLE_CELL ) ) + { + if( !GetTable()->IsValid() || GetTable()->IsInsertCellPossible() ) + pContext = new SwXMLTableCellContext_Impl( GetSwImport(), + nPrefix, + rLocalName, + xAttrList, + GetTable() ); + } + else if( IsXMLToken( rLocalName, XML_COVERED_TABLE_CELL ) ) + pContext = new SvXMLImportContext( GetImport(), nPrefix, + rLocalName ); + } + + return pContext; +} + +namespace { + +class SwXMLTableRowsContext_Impl : public SvXMLImportContext +{ + SvXMLImportContextRef xMyTable; + + bool bHeader; + + SwXMLTableContext *GetTable() { return static_cast<SwXMLTableContext *>(xMyTable.get()); } + +public: + + SwXMLTableRowsContext_Impl( SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + SwXMLTableContext *pTable, + bool bHead ); + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } +}; + +} + +SwXMLTableRowsContext_Impl::SwXMLTableRowsContext_Impl( SwXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + SwXMLTableContext *pTable, + bool bHead ) : + SvXMLImportContext( rImport, nPrfx, rLName ), + xMyTable( pTable ), + bHeader( bHead ) +{ +} + +SvXMLImportContextRef SwXMLTableRowsContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + if( XML_NAMESPACE_TABLE == nPrefix && + IsXMLToken( rLocalName, XML_TABLE_ROW ) && + GetTable()->IsInsertRowPossible() ) + pContext = new SwXMLTableRowContext_Impl( GetSwImport(), nPrefix, + rLocalName, xAttrList, + GetTable(), + bHeader ); + + return pContext; +} + +class SwXMLDDETableContext_Impl : public SvXMLImportContext +{ + OUString sConnectionName; + OUString sDDEApplication; + OUString sDDEItem; + OUString sDDETopic; + bool bIsAutomaticUpdate; + +public: + + + SwXMLDDETableContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName); + + virtual void StartElement( + const Reference<xml::sax::XAttributeList> & xAttrList) override; + + OUString& GetConnectionName() { return sConnectionName; } + OUString& GetDDEApplication() { return sDDEApplication; } + OUString& GetDDEItem() { return sDDEItem; } + OUString& GetDDETopic() { return sDDETopic; } + bool GetIsAutomaticUpdate() const { return bIsAutomaticUpdate; } +}; + + +SwXMLDDETableContext_Impl::SwXMLDDETableContext_Impl( + SwXMLImport& rImport, sal_uInt16 nPrfx, const OUString& rLName) : + SvXMLImportContext(rImport, nPrfx, rLName), + sConnectionName(), + sDDEApplication(), + sDDEItem(), + sDDETopic(), + bIsAutomaticUpdate(false) +{ +} + +void SwXMLDDETableContext_Impl::StartElement( + const Reference<xml::sax::XAttributeList> & xAttrList) +{ + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i = 0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + + if (XML_NAMESPACE_OFFICE == nPrefix) + { + if ( IsXMLToken( aLocalName, XML_DDE_APPLICATION ) ) + { + sDDEApplication = rValue; + } + else if ( IsXMLToken( aLocalName, XML_DDE_TOPIC ) ) + { + sDDETopic = rValue; + } + else if ( IsXMLToken( aLocalName, XML_DDE_ITEM ) ) + { + sDDEItem = rValue; + } + else if ( IsXMLToken( aLocalName, XML_NAME ) ) + { + sConnectionName = rValue; + } + else if ( IsXMLToken( aLocalName, XML_AUTOMATIC_UPDATE ) ) + { + bool bTmp(false); + if (::sax::Converter::convertBool(bTmp, rValue)) + { + bIsAutomaticUpdate = bTmp; + } + } + // else: unknown attribute + } + // else: unknown attribute namespace + } +} + +// generate a new name for DDE field type (called by lcl_GetDDEFieldType below) +static OUString lcl_GenerateFieldTypeName(const OUString& sPrefix, SwTableNode* pTableNode) +{ + const OUString sPrefixStr(sPrefix.isEmpty() ? OUString("_") : sPrefix); + + // increase count until we find a name that is not yet taken + OUString sName; + sal_Int32 nCount = 0; + do + { + // this is crazy, but just in case all names are taken: exit gracefully + if (nCount == SAL_MAX_INT32) + return sName; + + ++nCount; + sName = sPrefixStr + OUString::number(nCount); + } + while (nullptr != pTableNode->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false)); + + return sName; +} + +// set table properties +static SwDDEFieldType* lcl_GetDDEFieldType(SwXMLDDETableContext_Impl* pContext, + SwTableNode* pTableNode) +{ + // make command string + const OUString sCommand(pContext->GetDDEApplication() + + OUStringChar(sfx2::cTokenSeparator) + + pContext->GetDDEItem() + + OUStringChar(sfx2::cTokenSeparator) + + pContext->GetDDETopic()); + + const SfxLinkUpdateMode nType = pContext->GetIsAutomaticUpdate() + ? SfxLinkUpdateMode::ALWAYS + : SfxLinkUpdateMode::ONCALL; + + OUString sName(pContext->GetConnectionName()); + + // field type to be returned + SwDDEFieldType* pType = nullptr; + + // valid name? + if (sName.isEmpty()) + { + sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(), + pTableNode); + } + else + { + // check for existing DDE field type with the same name + SwDDEFieldType* pOldType = static_cast<SwDDEFieldType*>(pTableNode->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false)); + if (nullptr != pOldType) + { + // same values -> return old type + if ( (pOldType->GetCmd() == sCommand) && + (pOldType->GetType() == nType) ) + { + // same name, same values -> return old type! + pType = pOldType; + } + else + { + // same name, different values -> think of new name + sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(), + pTableNode); + } + } + // no old type -> create new one + } + + // create new field type (unless we already have one) + if (nullptr == pType) + { + // create new field type and return + SwDDEFieldType aDDEFieldType(sName, sCommand, nType); + pType = static_cast<SwDDEFieldType*>(pTableNode-> + GetDoc()->getIDocumentFieldsAccess().InsertFieldType(aDDEFieldType)); + } + + OSL_ENSURE(nullptr != pType, "We really want a SwDDEFieldType here!"); + return pType; +} + +class TableBoxIndex +{ +public: + OUString msName; + sal_Int32 mnWidth; + bool mbProtected; + + TableBoxIndex( const OUString& rName, sal_Int32 nWidth, + bool bProtected ) : + msName( rName ), + mnWidth( nWidth ), + mbProtected( bProtected ) + { } + + bool operator== ( const TableBoxIndex& rArg ) const + { + return (rArg.mnWidth == mnWidth) && + (rArg.mbProtected == mbProtected) && + (rArg.msName == msName); + } +}; + +class TableBoxIndexHasher +{ +public: + size_t operator() (const TableBoxIndex& rArg) const + { + return rArg.msName.hashCode() + rArg.mnWidth + (rArg.mbProtected ? 1 : 0); + } +}; + +const SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow, + sal_uInt32 nCol ) const +{ + return (*m_pRows)[nRow]->GetCell( nCol ); +} + +SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow, + sal_uInt32 nCol ) +{ + return (*m_pRows)[nRow]->GetCell( nCol ); +} + + +SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + const Reference< xml::sax::XAttributeList > & xAttrList ) : + XMLTextTableContext( rImport, nPrfx, rLName ), + m_pRows( new SwXMLTableRows_Impl ), + m_pTableNode( nullptr ), + m_pBox1( nullptr ), + m_bOwnsBox1( false ), + m_pSttNd1( nullptr ), + m_pBoxFormat( nullptr ), + m_pLineFormat( nullptr ), + m_bFirstSection( true ), + m_bRelWidth( true ), + m_bHasSubTables( false ), + m_nHeaderRows( 0 ), + m_nCurRow( 0 ), + m_nCurCol( 0 ), + m_nWidth( 0 ) +{ + OUString aName; + OUString sXmlId; + + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = xAttrList->getNameByIndex( i ); + + OUString aLocalName; + const sal_uInt16 nPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrName( rAttrName, + &aLocalName ); + const OUString& rValue = xAttrList->getValueByIndex( i ); + if( XML_NAMESPACE_TABLE == nPrefix ) + { + if( IsXMLToken( aLocalName, XML_STYLE_NAME ) ) + m_aStyleName = rValue; + else if( IsXMLToken( aLocalName, XML_NAME ) ) + aName = rValue; + else if( IsXMLToken( aLocalName, XML_DEFAULT_CELL_STYLE_NAME ) ) + m_aDfltCellStyleName = rValue; + else if( IsXMLToken( aLocalName, XML_TEMPLATE_NAME ) ) + m_aTemplateName = rValue; + } + else if ( (XML_NAMESPACE_XML == nPrefix) && + IsXMLToken( aLocalName, XML_ID ) ) + { + sXmlId = rValue; + } + } + + SwDoc *pDoc = SwImport::GetDocFromXMLImport( GetSwImport() ); + + OUString sTableName; + if( !aName.isEmpty() ) + { + const SwTableFormat *pTableFormat = pDoc->FindTableFormatByName( aName ); + if( !pTableFormat ) + sTableName = aName; + } + if( sTableName.isEmpty() ) + { + sTableName = pDoc->GetUniqueTableName(); + GetImport().GetTextImport() + ->GetRenameMap().Add( XML_TEXT_RENAME_TYPE_TABLE, aName, sTableName ); + } + + Reference< XTextTable > xTable; + SwXTextTable *pXTable = nullptr; + Reference<XMultiServiceFactory> xFactory( GetImport().GetModel(), + UNO_QUERY ); + OSL_ENSURE( xFactory.is(), "factory missing" ); + if( xFactory.is() ) + { + Reference<XInterface> xIfc = xFactory->createInstance( "com.sun.star.text.TextTable" ); + OSL_ENSURE( xIfc.is(), "Couldn't create a table" ); + + if( xIfc.is() ) + xTable.set( xIfc, UNO_QUERY ); + } + + if( xTable.is() ) + { + xTable->initialize( 1, 1 ); + + try + { + m_xTextContent = xTable; + GetImport().GetTextImport()->InsertTextContent( m_xTextContent ); + } + catch( IllegalArgumentException& ) + { + xTable = nullptr; + } + } + + if( xTable.is() ) + { + //FIXME + // xml:id for RDF metadata + GetImport().SetXmlId(xTable, sXmlId); + + Reference<XUnoTunnel> xTableTunnel( xTable, UNO_QUERY); + if( xTableTunnel.is() ) + { + pXTable = reinterpret_cast< SwXTextTable * >( + sal::static_int_cast< sal_IntPtr >( xTableTunnel->getSomething( SwXTextTable::getUnoTunnelId() ))); + OSL_ENSURE( pXTable, "SwXTextTable missing" ); + } + + Reference < XCellRange > xCellRange( xTable, UNO_QUERY ); + Reference < XCell > xCell = xCellRange->getCellByPosition( 0, 0 ); + Reference < XText> xText( xCell, UNO_QUERY ); + m_xOldCursor = GetImport().GetTextImport()->GetCursor(); + GetImport().GetTextImport()->SetCursor( xText->createTextCursor() ); + + // take care of open redlines for tables + GetImport().GetTextImport()->RedlineAdjustStartNodeCursor(); + } + if( pXTable ) + { + SwFrameFormat *const pTableFrameFormat = pXTable->GetFrameFormat(); + OSL_ENSURE( pTableFrameFormat, "table format missing" ); + SwTable *pTable = SwTable::FindTable( pTableFrameFormat ); + OSL_ENSURE( pTable, "table missing" ); + m_pTableNode = pTable->GetTableNode(); + OSL_ENSURE( m_pTableNode, "table node missing" ); + + pTableFrameFormat->SetName( sTableName ); + + SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U]; + m_pBox1 = pLine1->GetTabBoxes()[0U]; + m_pSttNd1 = m_pBox1->GetSttNd(); + } +} + +SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport, + sal_uInt16 nPrfx, + const OUString& rLName, + SwXMLTableContext *pTable ) : + XMLTextTableContext( rImport, nPrfx, rLName ), + m_pRows( new SwXMLTableRows_Impl ), + m_pTableNode( pTable->m_pTableNode ), + m_pBox1( nullptr ), + m_bOwnsBox1( false ), + m_pSttNd1( nullptr ), + m_pBoxFormat( nullptr ), + m_pLineFormat( nullptr ), + m_xParentTable( pTable ), + m_bFirstSection( false ), + m_bRelWidth( true ), + m_bHasSubTables( false ), + m_nHeaderRows( 0 ), + m_nCurRow( 0 ), + m_nCurCol( 0 ), + m_nWidth( 0 ) +{ +} + +SwXMLTableContext::~SwXMLTableContext() +{ + if (m_bOwnsBox1) + delete m_pBox1; + m_pColumnDefaultCellStyleNames.reset(); + m_pSharedBoxFormats.reset(); + m_pRows.reset(); + + // close redlines on table end nodes + GetImport().GetTextImport()->RedlineAdjustStartNodeCursor(); +} + +SvXMLImportContextRef SwXMLTableContext::CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = nullptr; + + const SvXMLTokenMap& rTokenMap = GetSwImport().GetTableElemTokenMap(); + bool bHeader = false; + switch( rTokenMap.Get( nPrefix, rLocalName ) ) + { + case XML_TOK_TABLE_HEADER_COLS: + case XML_TOK_TABLE_COLS: + if( IsValid() ) + pContext = new SwXMLTableColsContext_Impl( GetSwImport(), nPrefix, + rLocalName, + this ); + break; + case XML_TOK_TABLE_COL: + if( IsValid() && IsInsertColPossible() ) + pContext = new SwXMLTableColContext_Impl( GetSwImport(), nPrefix, + rLocalName, xAttrList, + this ); + break; + case XML_TOK_TABLE_HEADER_ROWS: + bHeader = true; + [[fallthrough]]; + case XML_TOK_TABLE_ROWS: + pContext = new SwXMLTableRowsContext_Impl( GetSwImport(), nPrefix, + rLocalName, + this, bHeader ); + break; + case XML_TOK_TABLE_ROW: + if( IsInsertRowPossible() ) + pContext = new SwXMLTableRowContext_Impl( GetSwImport(), nPrefix, + rLocalName, xAttrList, + this ); + break; + case XML_TOK_OFFICE_DDE_SOURCE: + // save context for later processing (discard old context, if approp.) + if( IsValid() ) + { + m_xDDESource.set(new SwXMLDDETableContext_Impl( GetSwImport(), nPrefix, + rLocalName )); + pContext = m_xDDESource.get(); + } + break; + } + + return pContext; +} + +void SwXMLTableContext::InsertColumn( sal_Int32 nWidth2, bool bRelWidth2, + const OUString *pDfltCellStyleName ) +{ + OSL_ENSURE( m_nCurCol < USHRT_MAX, + "SwXMLTableContext::InsertColumn: no space left" ); + if( m_nCurCol >= USHRT_MAX ) + return; + + if( nWidth2 < MINLAY ) + nWidth2 = MINLAY; + else if( nWidth2 > MAX_WIDTH ) + nWidth2 = MAX_WIDTH; + m_aColumnWidths.emplace_back(nWidth2, bRelWidth2 ); + if( (pDfltCellStyleName && !pDfltCellStyleName->isEmpty()) || + m_pColumnDefaultCellStyleNames ) + { + if( !m_pColumnDefaultCellStyleNames ) + { + m_pColumnDefaultCellStyleNames.reset(new std::vector<OUString>); + sal_uLong nCount = m_aColumnWidths.size() - 1; + while( nCount-- ) + m_pColumnDefaultCellStyleNames->push_back(OUString()); + } + + if(pDfltCellStyleName) + m_pColumnDefaultCellStyleNames->push_back(*pDfltCellStyleName); + else + m_pColumnDefaultCellStyleNames->push_back(OUString()); + } +} + +sal_Int32 SwXMLTableContext::GetColumnWidth( sal_uInt32 nCol, + sal_uInt32 nColSpan ) const +{ + sal_uInt32 nLast = nCol+nColSpan; + if( nLast > m_aColumnWidths.size() ) + nLast = m_aColumnWidths.size(); + + sal_Int32 nWidth2 = 0; + for( sal_uInt32 i=nCol; i < nLast; ++i ) + nWidth2 += m_aColumnWidths[i].width; + + return nWidth2; +} + +OUString SwXMLTableContext::GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const +{ + if( m_pColumnDefaultCellStyleNames && nCol < m_pColumnDefaultCellStyleNames->size()) + return (*m_pColumnDefaultCellStyleNames)[static_cast<size_t>(nCol)]; + + return OUString(); +} + +void SwXMLTableContext::InsertCell( const OUString& rStyleName, + sal_uInt32 nRowSpan, sal_uInt32 nColSpan, + const SwStartNode *pStartNode, + SwXMLTableContext *pTable, + bool bProtect, + const OUString* pFormula, + bool bHasValue, + double fValue, + OUString const*const pStringValue ) +{ + OSL_ENSURE( m_nCurCol < GetColumnCount(), + "SwXMLTableContext::InsertCell: row is full" ); + OSL_ENSURE( m_nCurRow < USHRT_MAX, + "SwXMLTableContext::InsertCell: table is full" ); + if( m_nCurCol >= USHRT_MAX || m_nCurRow > USHRT_MAX ) + return; + + OSL_ENSURE( nRowSpan >=1, "SwXMLTableContext::InsertCell: row span is 0" ); + if( 0 == nRowSpan ) + nRowSpan = 1; + OSL_ENSURE( nColSpan >=1, "SwXMLTableContext::InsertCell: col span is 0" ); + if( 0 == nColSpan ) + nColSpan = 1; + + // Until it is possible to add columns here, fix the column span. + sal_uInt32 nColsReq = m_nCurCol + nColSpan; + if( nColsReq > GetColumnCount() ) + { + nColSpan = GetColumnCount() - m_nCurCol; + nColsReq = GetColumnCount(); + } + + // Check whether there are cells from a previous line already that reach + // into the current row. + if( m_nCurRow > 0 && nColSpan > 1 ) + { + SwXMLTableRow_Impl *pCurRow = (*m_pRows)[m_nCurRow].get(); + sal_uInt32 nLastCol = GetColumnCount() < nColsReq ? GetColumnCount() + : nColsReq; + for( sal_uInt32 i=m_nCurCol+1; i<nLastCol; ++i ) + { + if( pCurRow->GetCell(i)->IsUsed() ) + { + // If this cell is used, the column span is truncated + nColSpan = i - m_nCurCol; + nColsReq = i; + break; + } + } + } + + sal_uInt32 nRowsReq = m_nCurRow + nRowSpan; + if( nRowsReq > USHRT_MAX ) + { + nRowSpan = USHRT_MAX - m_nCurRow; + nRowsReq = USHRT_MAX; + } + + // Add columns (if # required columns greater than # columns): + // This should never happen, since we require column definitions! + if ( nColsReq > GetColumnCount() ) + { + for( sal_uInt32 i=GetColumnCount(); i<nColsReq; ++i ) + { + m_aColumnWidths.emplace_back(MINLAY, true ); + } + // adjust columns in *all* rows, if columns must be inserted + for (size_t i = 0; i < m_pRows->size(); ++i) + (*m_pRows)[i]->Expand( nColsReq, i<m_nCurRow ); + } + + // Add rows + if (m_pRows->size() < nRowsReq) + { + for (size_t i = m_pRows->size(); i < nRowsReq; ++i) + m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>( + "", GetColumnCount())); + } + + OUString sStyleName( rStyleName ); + if( sStyleName.isEmpty() ) + { + sStyleName = (*m_pRows)[m_nCurRow]->GetDefaultCellStyleName(); + if( sStyleName.isEmpty() && m_pColumnDefaultCellStyleNames ) + { + sStyleName = GetColumnDefaultCellStyleName( m_nCurCol ); + if( sStyleName.isEmpty() ) + sStyleName = m_aDfltCellStyleName; + } + } + + // Fill the cells + for( sal_uInt32 i=nColSpan; i>0; --i ) + { + for( sal_uInt32 j=nRowSpan; j>0; --j ) + { + const bool bCovered = i != nColSpan || j != nRowSpan; + SwXMLTableCell_Impl *pCell = GetCell( nRowsReq-j, nColsReq-i ); + if (!pCell) + throw css::lang::IndexOutOfBoundsException(); + pCell->Set( sStyleName, j, i, pStartNode, + pTable, bProtect, pFormula, bHasValue, bCovered, fValue, + pStringValue ); + } + } + + // Set current col to the next (free) column + m_nCurCol = nColsReq; + while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + m_nCurCol++; +} + +void SwXMLTableContext::InsertRow( const OUString& rStyleName, + const OUString& rDfltCellStyleName, + bool bInHead ) +{ + OSL_ENSURE( m_nCurRow < USHRT_MAX, + "SwXMLTableContext::InsertRow: no space left" ); + if( m_nCurRow >= USHRT_MAX ) + return; + + // Make sure there is at least one column. + if( 0==m_nCurRow && 0 == GetColumnCount() ) + InsertColumn( USHRT_MAX, true ); + + if (m_nCurRow < m_pRows->size()) + { + // The current row has already been inserted because of a row span + // of a previous row. + (*m_pRows)[m_nCurRow]->Set( + rStyleName, rDfltCellStyleName ); + } + else + { + // add a new row + m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>( + rStyleName, GetColumnCount(), + &rDfltCellStyleName)); + } + + // We start at the first column ... + m_nCurCol=0; + + // ... but this cell may be occupied already. + while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + m_nCurCol++; + + if( bInHead && m_nHeaderRows == m_nCurRow ) + m_nHeaderRows++; +} + +void SwXMLTableContext::InsertRepRows( sal_uInt32 nCount ) +{ + const SwXMLTableRow_Impl *pSrcRow = (*m_pRows)[m_nCurRow-1].get(); + while( nCount > 1 && IsInsertRowPossible() ) + { + InsertRow( pSrcRow->GetStyleName(), pSrcRow->GetDefaultCellStyleName(), + false ); + while( m_nCurCol < GetColumnCount() ) + { + if( !GetCell(m_nCurRow,m_nCurCol)->IsUsed() ) + { + const SwXMLTableCell_Impl *pSrcCell = + GetCell( m_nCurRow-1, m_nCurCol ); + InsertCell( pSrcCell->GetStyleName(), 1U, + pSrcCell->GetColSpan(), + InsertTableSection(), + nullptr, pSrcCell->IsProtected(), + &pSrcCell->GetFormula(), + pSrcCell->HasValue(), pSrcCell->GetValue(), + pSrcCell->GetStringValue() ); + } + } + FinishRow(); + nCount--; + } +} + +void SwXMLTableContext::FinishRow() +{ + // Insert an empty cell at the end of the line if the row is not complete + if( m_nCurCol < GetColumnCount() ) + { + InsertCell( "", 1U, GetColumnCount() - m_nCurCol, + InsertTableSection() ); + } + + // Move to the next row. + m_nCurRow++; +} + +const SwStartNode *SwXMLTableContext::GetPrevStartNode( sal_uInt32 nRow, + sal_uInt32 nCol ) const +{ + const SwXMLTableCell_Impl *pPrevCell = nullptr; + if( GetColumnCount() == nCol ) + { + // The last cell is the right one here. + pPrevCell = GetCell( m_pRows->size() - 1U, GetColumnCount() - 1 ); + } + else if( nCol > 0 ) + { + // The previous cell in this row. + pPrevCell = GetCell( nRow, nCol-1 ); + } + else if( nRow > 0 ) + { + // The last cell from the previous row. + pPrevCell = GetCell( nRow-1, GetColumnCount()-1 ); + } + + const SwStartNode *pSttNd = nullptr; + if( pPrevCell ) + { + if( pPrevCell->GetStartNode() ) + pSttNd = pPrevCell->GetStartNode(); + // #i95726# - Some fault tolerance +// else + else if ( pPrevCell->GetSubTable() ) + pSttNd = pPrevCell->GetSubTable()->GetLastStartNode(); + + OSL_ENSURE( pSttNd != nullptr, + "table corrupt" ); + } + + return pSttNd; +} + +void SwXMLTableContext::FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol, + sal_uInt32 nColSpan ) +{ + sal_uInt32 nLastCol = nCol + nColSpan; + for( sal_uInt32 i = nCol; i < nLastCol; i++ ) + { + sal_uInt32 j = nRow; + sal_uInt32 nRowSpan = 1; + SwXMLTableCell_Impl *pCell = GetCell( j, i ); + while( pCell && pCell->GetRowSpan() > 1 ) + { + pCell->SetRowSpan( nRowSpan++ ); + pCell = j > 0 ? GetCell( --j, i ) : nullptr; + } + } +} + +void SwXMLTableContext::ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows ) +{ + const SwStartNode *pPrevSttNd = GetPrevStartNode( nRow, nCol ); + const SwStartNode *pSttNd = InsertTableSection( pPrevSttNd ); + + const SwXMLTableCell_Impl *pCell = GetCell( nRow, nCol ); + sal_uInt32 nLastRow = bRows ? nRow + pCell->GetRowSpan() : nRow + 1; + sal_uInt32 nLastCol = nCol + pCell->GetColSpan(); + + for( sal_uInt32 i=nRow; i<nLastRow; i++ ) + { + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=nCol; j<nLastCol; j++ ) + pRow->GetCell( j )->SetStartNode( pSttNd ); + } + +} + +SwTableBox *SwXMLTableContext::NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ) +{ + // The topmost table is the only table that maintains the two members + // pBox1 and bFirstSection. + if( m_xParentTable.is() ) + return static_cast<SwXMLTableContext *>(m_xParentTable.get())->NewTableBox( pStNd, + pUpper ); + + SwTableBox *pBox; + + if( m_pBox1 && + m_pBox1->GetSttNd() == pStNd ) + { + // if the StartNode is equal to the StartNode of the initially + // created box, we use this box + pBox = m_pBox1; + pBox->SetUpper( pUpper ); + m_pBox1 = nullptr; + m_bOwnsBox1 = false; + } + else + pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper ); + + return pBox; +} + +SwTableBoxFormat* SwXMLTableContext::GetSharedBoxFormat( + SwTableBox* pBox, + const OUString& rStyleName, + sal_Int32 nColumnWidth, + bool bProtected, + bool bMayShare, + bool& bNew, + bool* pModifyLocked ) +{ + if ( m_pSharedBoxFormats == nullptr ) + m_pSharedBoxFormats.reset(new map_BoxFormat); + + SwTableBoxFormat* pBoxFormat2; + + TableBoxIndex aKey( rStyleName, nColumnWidth, bProtected ); + map_BoxFormat::iterator aIter = m_pSharedBoxFormats->find( aKey ); + if ( aIter == m_pSharedBoxFormats->end() ) + { + // unknown format so far -> construct a new one + + // get the old format, and reset all attributes + // (but preserve FillOrder) + pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()); + SwFormatFillOrder aFillOrder( pBoxFormat2->GetFillOrder() ); + pBoxFormat2->ResetAllFormatAttr(); // #i73790# - method renamed + pBoxFormat2->SetFormatAttr( aFillOrder ); + bNew = true; // it's a new format now + + // share this format, if allowed + if ( bMayShare ) + (*m_pSharedBoxFormats)[ aKey ] = pBoxFormat2; + } + else + { + // set the shared format + pBoxFormat2 = aIter->second; + pBox->ChgFrameFormat( pBoxFormat2, /*bNeedToReregister*/false ); + bNew = false; // copied from an existing format + + // claim it, if we are not allowed to share + if ( !bMayShare ) + pBoxFormat2 = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()); + } + + // lock format (if so desired) + if ( pModifyLocked != nullptr ) + { + (*pModifyLocked) = pBoxFormat2->IsModifyLocked(); + pBoxFormat2->LockModify(); + } + + return pBoxFormat2; +} + +SwTableBox *SwXMLTableContext::MakeTableBox( SwTableLine *pUpper, + sal_uInt32 nTopRow, + sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, + sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for cell + SwTableBox *pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + + sal_uInt32 nColSpan = nRightCol - nLeftCol; + sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan ); + + // TODO: Share formats! + SwFrameFormat *pFrameFormat = pBox->ClaimFrameFormat(); + SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() ); + pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed + pFrameFormat->SetFormatAttr( aFillOrder ); + + pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) ); + + SwTableLines& rLines = pBox->GetTabLines(); + bool bSplitted = false; + + while( !bSplitted ) + { + sal_uInt32 nStartRow = nTopRow; + sal_uInt32 i; + + for( i = nTopRow; i < nBottomRow; i++ ) + { + // Could the table be split behind the current row? + bool bSplit = true; + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=nLeftCol; j<nRightCol; j++ ) + { + bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() ); + if( !bSplit ) + break; + } + if( bSplit && (nStartRow>nTopRow || i+1<nBottomRow) ) + { + SwTableLine *pLine = + MakeTableLine( pBox, nStartRow, nLeftCol, i+1, + nRightCol ); + + rLines.push_back( pLine ); + + nStartRow = i+1; + bSplitted = true; + } + } + if( !bSplitted ) + { + // No splitting was possible. That for, we have to force it. + // Ruthless! + + nStartRow = nTopRow; + while( nStartRow < nBottomRow ) + { + sal_uInt32 nMaxRowSpan = 0; + SwXMLTableRow_Impl *pStartRow = (*m_pRows)[nStartRow].get(); + const SwXMLTableCell_Impl *pCell; + for( i=nLeftCol; i<nRightCol; i++ ) + { + pCell = pStartRow->GetCell(i); + if( pCell->GetRowSpan() > nMaxRowSpan ) + nMaxRowSpan = pCell->GetRowSpan(); + } + + nStartRow += nMaxRowSpan; + if( nStartRow<nBottomRow ) + { + SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[nStartRow - 1U].get(); + i = nLeftCol; + while( i < nRightCol ) + { + if( pPrevRow->GetCell(i)->GetRowSpan() > 1 ) + { + const SwXMLTableCell_Impl *pCell2 = + GetCell( nStartRow, i ); + const sal_uInt32 nColSpan2 = pCell2->GetColSpan(); + FixRowSpan( nStartRow-1, i, nColSpan2 ); + ReplaceWithEmptyCell( nStartRow, i, true ); + i += nColSpan2; + } + else + { + i++; + } + } + } + } + // and now start over again... + } + } + + return pBox; +} + +SwTableBox *SwXMLTableContext::MakeTableBox( + SwTableLine *pUpper, const SwXMLTableCell_Impl *pCell, + sal_uInt32 nLeftCol, sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for cell + SwTableBox *pBox; + sal_uInt32 nColSpan = nRightCol - nLeftCol; + sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan ); + + if( pCell->GetStartNode() ) + { + pBox = NewTableBox( pCell->GetStartNode(), pUpper ); + } + else + { + // and it is a table: therefore we build a new box and + // put the rows of the table into the rows of the box + pBox = new SwTableBox( m_pBoxFormat, 0, pUpper ); + pCell->GetSubTable()->MakeTable( pBox, nColWidth ); + } + + // Share formats! + const OUString sStyleName = pCell->GetStyleName(); + bool bModifyLocked; + bool bNew; + SwTableBoxFormat *pBoxFormat2 = GetSharedBoxFormat( + pBox, sStyleName, nColWidth, pCell->IsProtected(), + pCell->GetStartNode() && pCell->GetFormula().isEmpty() && + ! pCell->HasValue(), + bNew, &bModifyLocked ); + + // if a new format was created, then we need to set the style + if ( bNew ) + { + // set style + const SfxItemSet *pAutoItemSet = nullptr; + if( pCell->GetStartNode() && !sStyleName.isEmpty() && + GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_CELL, sStyleName, &pAutoItemSet ) ) + { + if( pAutoItemSet ) + pBoxFormat2->SetFormatAttr( *pAutoItemSet ); + } + } + + if( pCell->GetStartNode() ) + { + if (pCell->HasStringValue()) + { + SwNodeIndex const aNodeIndex(*(pCell->GetStartNode()), 1); + SwTextNode *const pTextNode(aNodeIndex.GetNode().GetTextNode()); + SAL_WARN_IF(!pTextNode, "sw", "Should have a text node in cell?"); + if (pTextNode) + { + SAL_WARN_IF(!pTextNode->GetText().isEmpty(), "sw", + "why text here?"); + pTextNode->InsertText(*pCell->GetStringValue(), + SwIndex(pTextNode, 0)); + } + } + + // try to rescue broken documents with a certain pattern + // if: 1) the cell has a default number format (number 0) + // 2) the call has no formula + // 3) the value is 0.0 + // 4) the text doesn't look anything like 0.0 + // [read: length > 10, or length smaller 10 and no 0 in it] + // then make it a text cell! + bool bSuppressNumericContent = false; + if( pCell->HasValue() && (pCell->GetValue() == 0.0) && + pCell->GetFormula().isEmpty() && + !sStyleName.isEmpty() ) + { + // default num format? + const SfxPoolItem* pItem = nullptr; + if( pBoxFormat2->GetItemState( RES_BOXATR_FORMAT, false, &pItem ) + == SfxItemState::SET ) + { + const SwTableBoxNumFormat* pNumFormat = + static_cast<const SwTableBoxNumFormat*>( pItem ); + if( ( pNumFormat != nullptr ) && ( pNumFormat->GetValue() == 0 ) ) + { + // only one text node? + SwNodeIndex aNodeIndex( *(pCell->GetStartNode()), 1 ); + if( ( aNodeIndex.GetNode().EndOfSectionIndex() - + aNodeIndex.GetNode().StartOfSectionIndex() ) == 2 ) + { + SwTextNode* pTextNode= aNodeIndex.GetNode().GetTextNode(); + if( pTextNode != nullptr ) + { + // check text: does it look like some form of 0.0? + const OUString& rText = pTextNode->GetText(); + if( ( rText.getLength() > 10 ) || + ( rText.indexOf( '0' ) == -1 ) ) + { + bSuppressNumericContent = true; + } + } + } + else + bSuppressNumericContent = true; // several nodes + } + } + } + + if( bSuppressNumericContent ) + { + // suppress numeric content? Then reset number format! + pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMULA ); + pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMAT ); + pBoxFormat2->ResetFormatAttr( RES_BOXATR_VALUE ); + } + else + { + // the normal case: set formula and value (if available) + + const OUString& rFormula = pCell->GetFormula(); + if (!rFormula.isEmpty()) + { + // formula cell: insert formula if valid + SwTableBoxFormula aFormulaItem( rFormula ); + pBoxFormat2->SetFormatAttr( aFormulaItem ); + } + else if (!pCell->HasValue() && pCell->HasStringValue()) + { + // Check for another inconsistency: + // No value but a non-textual format, i.e. a number format + // Solution: the number format will be removed, + // the cell gets the default text format. + const SfxPoolItem* pItem = nullptr; + if( m_pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false, &pItem ) + == SfxItemState::SET ) + { + const SwDoc* pDoc = m_pBoxFormat->GetDoc(); + const SvNumberFormatter* pNumberFormatter = pDoc ? + pDoc->GetNumberFormatter() : nullptr; + const SwTableBoxNumFormat* pNumFormat = + static_cast<const SwTableBoxNumFormat*>( pItem ); + if( pNumFormat != nullptr && pNumberFormatter && + !pNumberFormatter->GetEntry( pNumFormat->GetValue() )->IsTextFormat() ) + m_pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT ); + } + } + // always insert value, even if default + if( pCell->HasValue() ) + { + SwTableBoxValue aValueItem( pCell->GetValue() ); + pBoxFormat2->SetFormatAttr( aValueItem ); + } + } + + // update cell content depend on the default language + pBox->ActualiseValueBox(); + } + + // table cell protection + if( pCell->IsProtected() ) + { + SvxProtectItem aProtectItem( RES_PROTECT ); + aProtectItem.SetContentProtect( true ); + pBoxFormat2->SetFormatAttr( aProtectItem ); + } + + // restore old modify-lock state + if (! bModifyLocked) + pBoxFormat2->UnlockModify(); + + pBoxFormat2->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) ); + + return pBox; +} + +SwTableLine *SwXMLTableContext::MakeTableLine( SwTableBox *pUpper, + sal_uInt32 nTopRow, + sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, + sal_uInt32 nRightCol ) +{ + //FIXME: here would be a great place to handle XmlId for row + SwTableLine *pLine; + if( !pUpper && 0UL==nTopRow ) + { + pLine = m_pTableNode->GetTable().GetTabLines()[0U]; + } + else + { + pLine = new SwTableLine( m_pLineFormat, 0, pUpper ); + } + + // TODO: Share formats! + SwFrameFormat *pFrameFormat = pLine->ClaimFrameFormat(); + SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() ); + pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed + pFrameFormat->SetFormatAttr( aFillOrder ); + + const SfxItemSet *pAutoItemSet = nullptr; + const OUString& rStyleName = (*m_pRows)[nTopRow]->GetStyleName(); + if( 1 == (nBottomRow - nTopRow) && + !rStyleName.isEmpty() && + GetSwImport().FindAutomaticStyle( + XmlStyleFamily::TABLE_ROW, rStyleName, &pAutoItemSet ) ) + { + if( pAutoItemSet ) + pFrameFormat->SetFormatAttr( *pAutoItemSet ); + } + + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + + sal_uInt32 nStartCol = nLeftCol; + while( nStartCol < nRightCol ) + { + for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ ) + (*m_pRows)[nRow]->SetSplitable( true ); + + sal_uInt32 nCol = nStartCol; + sal_uInt32 nSplitCol = nRightCol; + bool bSplitted = false; + while( !bSplitted ) + { + OSL_ENSURE( nCol < nRightCol, "Ran too far" ); + + // Can be split after current HTML table column? + // If yes, can the created region still be split to + // rows if the next column is added to it? + bool bSplit = true; + bool bHoriSplitMayContinue = false; + bool bHoriSplitPossible = false; + + if ( m_bHasSubTables ) + { + // Convert row spans if the table has subtables: + for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ ) + { + SwXMLTableCell_Impl *pCell = GetCell(nRow,nCol); + // Could the table fragment be split horizontally behind + // the current line? + bool bHoriSplit = (*m_pRows)[nRow]->IsSplitable() && + nRow+1 < nBottomRow && + 1 == pCell->GetRowSpan(); + (*m_pRows)[nRow]->SetSplitable( bHoriSplit ); + + // Could the table fragment be split vertically behind the + // current column (uptp the current line? + bSplit &= ( 1 == pCell->GetColSpan() ); + if( bSplit ) + { + bHoriSplitPossible |= bHoriSplit; + + // Could the current table fragment be split + // horizontally behind the next column, too? + bHoriSplit &= (nCol+1 < nRightCol && + 1 == GetCell(nRow,nCol+1)->GetRowSpan()); + bHoriSplitMayContinue |= bHoriSplit; + } + } + } + else + { + // No subtables: we use the new table model. + SwXMLTableCell_Impl *pCell = GetCell(nTopRow,nCol); + + // #i95726# - some fault tolerance + if ( pCell == nullptr ) + { + OSL_FAIL( "table seems to be corrupt." ); + break; + } + + // Could the table fragment be split vertically behind the + // current column (uptp the current line? + bSplit = 1 == pCell->GetColSpan(); + } + +#if OSL_DEBUG_LEVEL > 0 + if( nCol == nRightCol-1 ) + { + OSL_ENSURE( bSplit, "Split-Flag wrong" ); + if ( m_bHasSubTables ) + { + OSL_ENSURE( !bHoriSplitMayContinue, + "HoriSplitMayContinue-Flag wrong" ); + SwXMLTableCell_Impl *pTmpCell = GetCell( nTopRow, nStartCol ); + OSL_ENSURE( pTmpCell->GetRowSpan() != (nBottomRow-nTopRow) || + !bHoriSplitPossible, "HoriSplitPossible-Flag wrong" ); + } + } +#endif + + OSL_ENSURE( !m_bHasSubTables || !bHoriSplitMayContinue || bHoriSplitPossible, + "bHoriSplitMayContinue, but not bHoriSplitPossible" ); + + if( bSplit ) + { + SwTableBox* pBox = nullptr; + SwXMLTableCell_Impl *pCell = GetCell( nTopRow, nStartCol ); + // #i95726# - some fault tolerance + if( ( !m_bHasSubTables || ( pCell->GetRowSpan() == (nBottomRow-nTopRow) ) ) && + pCell->GetColSpan() == (nCol+1-nStartCol) && + ( pCell->GetStartNode() || pCell->GetSubTable() ) ) + { + // insert new empty cell for covered cells: + long nBoxRowSpan = 1; + if ( !m_bHasSubTables ) + { + nBoxRowSpan = pCell->GetRowSpan(); + if ( pCell->IsCovered() ) + { + nBoxRowSpan = -1 * nBoxRowSpan; + ReplaceWithEmptyCell( nTopRow, nStartCol, false ); + } + } + + // The remaining box neither contains lines nor rows (i.e. + // is a content box + nSplitCol = nCol + 1; + + pBox = MakeTableBox( pLine, pCell, nStartCol, nSplitCol ); + + if ( 1 != nBoxRowSpan ) + pBox->setRowSpan( nBoxRowSpan ); + + bSplitted = true; + } + else if( m_bHasSubTables && bHoriSplitPossible && bHoriSplitMayContinue ) + { + // The table fragment could be split behind the current + // column, and the remaining fragment could be divided + // into lines. Anyway, it could be that this applies to + // the next column, too. That for, we check the next + // column but remember the current one as a good place to + // split. + nSplitCol = nCol + 1; + } + else if ( m_bHasSubTables ) + { + // If the table resulting table fragment could be divided + // into lines if splitting behind the current column, but + // this doesn't apply for thr next column, we split begind + // the current column. This applies for the last column, + // too. + // If the resulting box cannot be split into rows, + // the split at the last split position we remembered. + if( bHoriSplitPossible || nSplitCol > nCol+1 ) + { + OSL_ENSURE( !bHoriSplitMayContinue, + "bHoriSplitMayContinue==true" ); + OSL_ENSURE( bHoriSplitPossible || nSplitCol == nRightCol, + "bHoriSplitPossible flag should be set" ); + + nSplitCol = nCol + 1; + } + + pBox = MakeTableBox( pLine, nTopRow, nStartCol, + nBottomRow, nSplitCol ); + bSplitted = true; + } + + OSL_ENSURE( m_bHasSubTables || pBox, "Colspan trouble" ); + + if( pBox ) + rBoxes.push_back( pBox ); + } + nCol++; + } + nStartCol = nSplitCol; + } + + return pLine; +} + +void SwXMLTableContext::MakeTable_( SwTableBox *pBox ) +{ + // fix column widths + std::vector<ColumnWidthInfo>::iterator colIter; + sal_uInt32 nCols = GetColumnCount(); + + // If there are empty rows (because of some row span of previous rows) + // the have to be deleted. The previous rows have to be truncated. + + if (m_pRows->size() > m_nCurRow) + { + SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[m_nCurRow - 1U].get(); + const SwXMLTableCell_Impl *pCell; + for( sal_uLong i = 0; i < m_aColumnWidths.size(); ++i ) + { + pCell = pPrevRow->GetCell(i); + if( pCell->GetRowSpan() > 1 ) + { + FixRowSpan( m_nCurRow-1, i, 1UL ); + } + } + for (sal_uLong i = m_pRows->size() - 1; i >= m_nCurRow; --i) + m_pRows->pop_back(); + } + + if (m_pRows->empty()) + { + InsertCell( "", 1U, nCols, InsertTableSection() ); + } + + // TODO: Do we have to keep both values, the relative and the absolute + // width? + sal_Int32 nAbsWidth = 0; + sal_Int32 nMinAbsColWidth = 0; + sal_Int32 nRelWidth = 0; + sal_Int32 nMinRelColWidth = 0; + sal_uInt32 nRelCols = 0; + for( const auto& rCol : m_aColumnWidths) + { + if( rCol.isRelative ) + { + nRelWidth += rCol.width; + if( 0 == nMinRelColWidth || rCol.width < nMinRelColWidth ) + nMinRelColWidth = rCol.width; + nRelCols++; + } + else + { + nAbsWidth += rCol.width; + if( 0 == nMinAbsColWidth || rCol.width < nMinAbsColWidth ) + nMinAbsColWidth = rCol.width; + } + } + sal_uInt32 nAbsCols = nCols - nRelCols; + + if( m_bRelWidth ) + { + // If there a columns that have an absolute width, we have to + // calculate a relative one for them. + if( nAbsCols > 0 ) + { + // All column that have absolute widths get relative widths; + // these widths relate to each over like the original absolute + // widths. The smallest column gets a width that has the same + // value as the smallest column that has a relative width + // already. + if( 0 == nMinRelColWidth ) + nMinRelColWidth = nMinAbsColWidth; + + for( auto& rCol : m_aColumnWidths) + { + if( !rCol.isRelative ) + { + if (nMinAbsColWidth == 0) + throw o3tl::divide_by_zero(); + sal_Int32 nVal; + if (o3tl::checked_multiply<sal_Int32>(rCol.width, nMinRelColWidth, nVal)) + throw std::overflow_error("overflow in multiply"); + sal_Int32 nRelCol = nVal / nMinAbsColWidth; + rCol.width = nRelCol; + rCol.isRelative = true; + nRelWidth += nRelCol; + nAbsCols--; + if (nAbsCols <= 0) + break; + } + } + } + + if( !m_nWidth ) + { + // This happens only for percentage values for the table itself. + // In this case, the columns get the correct width even if + // the sum of the relative widths is smaller than the available + // width in TWIP. Therefore, we can use the relative width. + m_nWidth = std::min(nRelWidth, MAX_WIDTH); + } + if( nRelWidth != m_nWidth && nRelWidth && nCols ) + { + double n = static_cast<double>(m_nWidth) / static_cast<double>(nRelWidth); + nRelWidth = 0; + for( colIter = m_aColumnWidths.begin(); colIter < m_aColumnWidths.end() - 1; ++colIter) + { + sal_Int32 nW = static_cast<sal_Int32>( colIter->width * n); + colIter->width = static_cast<sal_uInt16>(nW); + nRelWidth += nW; + } + m_aColumnWidths.back().width = (m_nWidth-nRelWidth); + } + } + else + { + // If there are columns that have relative widths, we have to + // calculate an absolute widths for them. + if( nRelCols > 0 ) + { + // The absolute space that is available for all columns with a + // relative width. + sal_Int32 nAbsForRelWidth = + m_nWidth > nAbsWidth ? m_nWidth - nAbsWidth : sal_Int32(0L); + + // The relative width that has to be distributed in addition to + // equally widthed columns. + sal_Int32 nExtraRel = nRelWidth - (nRelCols * nMinRelColWidth); + + // The absolute space that may be distributed in addition to + // minimum widthed columns. + sal_Int32 nMinAbs = nRelCols * MINLAY; + sal_Int32 nExtraAbs = + nAbsForRelWidth > nMinAbs ? nAbsForRelWidth - nMinAbs : sal_Int32(0L); + + bool bMin = false; // Do all columns get the minimum width? + bool bMinExtra = false; // Do all columns get the minimum width plus + // some extra space? + + if( nAbsForRelWidth <= nMinAbs ) + { + // If there is not enough space left for all columns to + // get the minimum width, they get the minimum width, anyway. + nAbsForRelWidth = nMinAbs; + bMin = true; + } + else if( nAbsForRelWidth <= (nRelWidth * MINLAY) / + nMinRelColWidth ) + { + // If there is enough space for all columns to get the + // minimum width, but not to get a width that takes the + // relative width into account, each column gets the minimum + // width plus some extra space that is based on the additional + // space that is available. + bMinExtra = true; + } + // Otherwise, if there is enough space for every column, every + // column gets this space. + + for( auto& rCol : m_aColumnWidths ) + { + if( rCol.isRelative ) + { + sal_Int32 nAbsCol; + if( 1 == nRelCols ) + { + // The last column that has a relative width gets + // all absolute space that is left. + nAbsCol = nAbsForRelWidth; + } + else + { + if( bMin ) + { + nAbsCol = MINLAY; + } + else if( bMinExtra ) + { + sal_Int32 nExtraRelCol = rCol.width - nMinRelColWidth; + nAbsCol = MINLAY + (nExtraRelCol * nExtraAbs) / + nExtraRel; + } + else + { + nAbsCol = ( rCol.width * nAbsForRelWidth) / nRelWidth; + } + } + rCol.width = nAbsCol; + rCol.isRelative = false; + nAbsForRelWidth -= nAbsCol; + nAbsWidth += nAbsCol; + nRelCols--; + if (nRelCols <= 0) + break; + } + } + } + + if( nCols && nAbsWidth ) + { + if( nAbsWidth < m_nWidth ) + { + // If the table's width is larger than the sum of the absolute + // column widths, every column get some extra width. + sal_Int32 nExtraAbs = m_nWidth - nAbsWidth; + sal_Int32 nAbsLastCol = m_aColumnWidths.back().width + nExtraAbs; + for( colIter = m_aColumnWidths.begin(); colIter < m_aColumnWidths.end()-1; ++colIter ) + { + sal_Int32 nAbsCol = colIter->width; + sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) / + nAbsWidth; + nAbsCol += nExtraAbsCol; + colIter->width = nAbsCol; + nAbsLastCol -= nExtraAbsCol; + } + m_aColumnWidths.back().width = nAbsLastCol; + } + else if( nAbsWidth > m_nWidth ) + { + // If the table's width is smaller than the sum of the absolute + // column widths, every column needs to shrink. + // Every column gets the minimum width plus some extra width. + sal_Int32 nExtraAbs = m_nWidth - (nCols * MINLAY); + sal_Int32 nAbsLastCol = MINLAY + nExtraAbs; + for( colIter = m_aColumnWidths.begin(); colIter < m_aColumnWidths.end()-1; ++colIter ) + { + sal_Int32 nAbsCol = colIter->width; + sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) / + nAbsWidth; + nAbsCol = MINLAY + nExtraAbsCol; + colIter->width = nAbsCol; + nAbsLastCol -= nExtraAbsCol; + } + m_aColumnWidths.back().width = nAbsLastCol; + } + } + } + + SwTableLines& rLines = + pBox ? pBox->GetTabLines() + : m_pTableNode->GetTable().GetTabLines(); + + sal_uInt32 nStartRow = 0; + sal_uInt32 nRows = m_pRows->size(); + for(sal_uInt32 i=0; i<nRows; ++i ) + { + // Could we split the table behind the current line? + bool bSplit = true; + if ( m_bHasSubTables ) + { + SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get(); + for( sal_uInt32 j=0; j<nCols; j++ ) + { + bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() ); + if( !bSplit ) + break; + } + } + + if( bSplit ) + { + SwTableLine *pLine = + MakeTableLine( pBox, nStartRow, 0UL, i+1, nCols ); + if( pBox || nStartRow>0 ) + rLines.push_back( pLine ); + nStartRow = i+1; + } + } +} + +void SwXMLTableContext::MakeTable() +{ + // this method will modify the document directly -> lock SolarMutex + // This will call all other MakeTable*(..) methods, so + // those don't need to be locked separately. + SolarMutexGuard aGuard; + + // #i97274# handle invalid tables + if (!m_pRows || m_pRows->empty() || !GetColumnCount()) + { + OSL_FAIL("invalid table: no cells; deleting..."); + m_pTableNode->GetDoc()->getIDocumentContentOperations().DeleteSection( m_pTableNode ); + m_pTableNode = nullptr; + m_pBox1 = nullptr; + m_bOwnsBox1 = false; + m_pSttNd1 = nullptr; + return; + } + + SwXMLImport& rSwImport = GetSwImport(); + + SwFrameFormat *pFrameFormat = m_pTableNode->GetTable().GetFrameFormat(); + + sal_Int16 eHoriOrient = text::HoriOrientation::FULL; + bool bSetHoriOrient = false; + + sal_uInt8 nPercentWidth = 0U; + + OUString sStyleName; + SwStyleNameMapper::FillUIName( m_aTemplateName, sStyleName, SwGetPoolIdFromName::TabStyle ); + m_pTableNode->GetTable().SetTableStyleName( sStyleName ); + m_pTableNode->GetTable().SetRowsToRepeat( m_nHeaderRows ); + m_pTableNode->GetTable().SetTableModel( !m_bHasSubTables ); + + const SfxItemSet *pAutoItemSet = nullptr; + if( !m_aStyleName.isEmpty() && + rSwImport.FindAutomaticStyle( + XmlStyleFamily::TABLE_TABLE, m_aStyleName, &pAutoItemSet ) && + pAutoItemSet ) + { + const SfxPoolItem *pItem; + const SvxLRSpaceItem *pLRSpace = nullptr; + if( SfxItemState::SET == pAutoItemSet->GetItemState( RES_LR_SPACE, false, + &pItem ) ) + pLRSpace = static_cast<const SvxLRSpaceItem *>(pItem); + + if( SfxItemState::SET == pAutoItemSet->GetItemState( RES_HORI_ORIENT, false, + &pItem ) ) + { + eHoriOrient = static_cast<const SwFormatHoriOrient *>(pItem)->GetHoriOrient(); + switch( eHoriOrient ) + { + case text::HoriOrientation::FULL: + if( pLRSpace ) + { + eHoriOrient = text::HoriOrientation::NONE; + bSetHoriOrient = true; + } + break; + case text::HoriOrientation::LEFT: + if( pLRSpace ) + { + eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH; + bSetHoriOrient = true; + } + break; + default: + ; + } + } + else + { + bSetHoriOrient = true; + } + + const SwFormatFrameSize *pSize = nullptr; + if( SfxItemState::SET == pAutoItemSet->GetItemState( RES_FRM_SIZE, false, + &pItem ) ) + pSize = static_cast<const SwFormatFrameSize *>(pItem); + + switch( eHoriOrient ) + { + case text::HoriOrientation::FULL: + case text::HoriOrientation::NONE: + // For text::HoriOrientation::NONE we would prefer to use the sum + // of the relative column widths as reference width. + // Unfortunately this works only if this sum interpreted as + // twip value is larger than the space that is available. + // We don't know that space, so we have to use MAX_WIDTH, too. + // Even if a size is specified, it will be ignored! + m_nWidth = MAX_WIDTH; + break; + default: + if( pSize ) + { + if( pSize->GetWidthPercent() ) + { + // The width will be set in MakeTable_ + nPercentWidth = pSize->GetWidthPercent(); + } + else + { + m_nWidth = pSize->GetWidth(); + sal_Int32 const min = static_cast<sal_Int32>( + std::min<sal_uInt32>(GetColumnCount() * MINLAY, MAX_WIDTH)); + if( m_nWidth < min ) + { + m_nWidth = min; + } + else if( m_nWidth > MAX_WIDTH ) + { + m_nWidth = MAX_WIDTH; + } + m_bRelWidth = false; + } + } + else + { + eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient + ? text::HoriOrientation::NONE : text::HoriOrientation::FULL; + bSetHoriOrient = true; + m_nWidth = MAX_WIDTH; + } + break; + } + + pFrameFormat->SetFormatAttr( *pAutoItemSet ); + } + else + { + bSetHoriOrient = true; + m_nWidth = MAX_WIDTH; + } + + SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U]; + assert(m_pBox1 == pLine1->GetTabBoxes()[0] && !m_bOwnsBox1 && "Why is box 1 change?"); + m_pBox1->m_pStartNode = m_pSttNd1; + pLine1->GetTabBoxes().erase( pLine1->GetTabBoxes().begin() ); + m_bOwnsBox1 = true; + + m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat()); + m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_pBox1->GetFrameFormat()); + + MakeTable_(); + + if( bSetHoriOrient ) + pFrameFormat->SetFormatAttr( SwFormatHoriOrient( 0, eHoriOrient ) ); + + // This must be after the call to MakeTable_, because nWidth might be + // changed there. + pFrameFormat->LockModify(); + SwFormatFrameSize aSize( SwFrameSize::Variable, m_nWidth ); + aSize.SetWidthPercent( nPercentWidth ); + pFrameFormat->SetFormatAttr( aSize ); + pFrameFormat->UnlockModify(); + + for (std::unique_ptr<SwXMLTableRow_Impl> & rRow : *m_pRows) + rRow->Dispose(); + + // now that table is complete, change into DDE table (if appropriate) + if (m_xDDESource.is()) + { + // change existing table into DDE table: + // 1) Get DDE field type (get data from dde-source context), + SwDDEFieldType* pFieldType = lcl_GetDDEFieldType( m_xDDESource.get(), + m_pTableNode ); + + // 2) release the DDE source context, + m_xDDESource.set(nullptr); + + // 3) create new DDE table, and + std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( m_pTableNode->GetTable(), + pFieldType, false ) ); + + // 4) set new (DDE)table at node. + m_pTableNode->SetNewTable(std::move(pDDETable), false); + } + + // ??? this is always false: root frame is only created in SwViewShell::Init + if( m_pTableNode->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + m_pTableNode->DelFrames(); + SwNodeIndex aIdx( *m_pTableNode->EndOfSectionNode(), 1 ); + m_pTableNode->MakeOwnFrames(&aIdx); + } +} + +void SwXMLTableContext::MakeTable( SwTableBox *pBox, sal_Int32 nW ) +{ + //FIXME: here would be a great place to handle XmlId for subtable + m_pLineFormat = GetParentTable()->m_pLineFormat; + m_pBoxFormat = GetParentTable()->m_pBoxFormat; + m_nWidth = nW; + m_bRelWidth = GetParentTable()->m_bRelWidth; + + MakeTable_( pBox ); + + for (std::unique_ptr<SwXMLTableRow_Impl> & rpRow : *m_pRows) + { + // i#113600, to break the cyclic reference to SwXMLTableContext object + rpRow->Dispose(); + } +} + +const SwStartNode *SwXMLTableContext::InsertTableSection( + const SwStartNode *const pPrevSttNd, + OUString const*const pStringValueStyleName) +{ + // The topmost table is the only table that maintains the two members + // pBox1 and bFirstSection. + if( m_xParentTable.is() ) + return static_cast<SwXMLTableContext *>(m_xParentTable.get()) + ->InsertTableSection(pPrevSttNd, pStringValueStyleName); + + const SwStartNode *pStNd; + Reference<XUnoTunnel> xCursorTunnel( GetImport().GetTextImport()->GetCursor(), + UNO_QUERY); + OSL_ENSURE( xCursorTunnel.is(), "missing XUnoTunnel for Cursor" ); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + + if( m_bFirstSection ) + { + // The Cursor already is in the first section + pStNd = pTextCursor->GetPaM()->GetNode().FindTableBoxStartNode(); + m_bFirstSection = false; + GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(), + GetImport().GetTextImport()->GetCursor(), "Standard", true ); + } + else + { + SwDoc* pDoc = SwImport::GetDocFromXMLImport( GetSwImport() ); + const SwEndNode *pEndNd = pPrevSttNd ? pPrevSttNd->EndOfSectionNode() + : m_pTableNode->EndOfSectionNode(); + // #i78921# - make code robust + OSL_ENSURE( pDoc, "<SwXMLTableContext::InsertTableSection(..)> - no <pDoc> at <SwXTextCursor> instance - <SwXTextCurosr> doesn't seem to be registered at a <SwUnoCursor> instance." ); + if ( !pDoc ) + { + pDoc = const_cast<SwDoc*>(pEndNd->GetDoc()); + } + sal_uInt32 nOffset = pPrevSttNd ? 1UL : 0UL; + SwNodeIndex aIdx( *pEndNd, nOffset ); + SwTextFormatColl *pColl = + pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false ); + pStNd = pDoc->GetNodes().MakeTextSection( aIdx, SwTableBoxStartNode, + pColl ); + // Consider the case that a table is defined without a row. + if( !pPrevSttNd && m_pBox1 != nullptr ) + + { + m_pBox1->m_pStartNode = pStNd; + SwContentNode *pCNd = pDoc->GetNodes()[ pStNd->GetIndex() + 1 ] + ->GetContentNode(); + SwPosition aPos( *pCNd ); + aPos.nContent.Assign( pCNd, 0U ); + + const uno::Reference< text::XTextRange > xTextRange = + SwXTextRange::CreateXTextRange( *pDoc, aPos, nullptr ); + Reference < XText > xText = xTextRange->getText(); + Reference < XTextCursor > xTextCursor = + xText->createTextCursorByRange( xTextRange ); + GetImport().GetTextImport()->SetCursor( xTextCursor ); + } + } + + if (pStringValueStyleName) + { // fdo#62147: apply style to paragraph on string-value cell + GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(), + GetImport().GetTextImport()->GetCursor(), *pStringValueStyleName, + true, false, -1, false); // parameters same as sCellParaStyleName + } + + return pStNd; +} + +void SwXMLTableContext::EndElement() +{ + if( IsValid() && !m_xParentTable.is() ) + { + MakeTable(); + GetImport().GetTextImport()->SetCursor( m_xOldCursor ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltbli.hxx b/sw/source/filter/xml/xmltbli.hxx new file mode 100644 index 000000000..445cd4b05 --- /dev/null +++ b/sw/source/filter/xml/xmltbli.hxx @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLTBLI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTBLI_HXX + +#include <xmloff/XMLTextTableContext.hxx> + +#include "xmlimp.hxx" + +#include <memory> +#include <unordered_map> +#include <vector> + +class SwXMLImport; +class SwTableNode; +class SwTableBox; +class SwTableLine; +class SwStartNode; +class SwTableBoxFormat; +class SwTableLineFormat; +class SwXMLTableCell_Impl; +class SwXMLTableRow_Impl; +typedef std::vector<std::unique_ptr<SwXMLTableRow_Impl>> SwXMLTableRows_Impl; +class SwXMLDDETableContext_Impl; +class TableBoxIndexHasher; +class TableBoxIndex; + +namespace com::sun::star { + namespace text { class XTextContent; } + namespace text { class XTextCursor; } +} + +class SwXMLTableContext : public XMLTextTableContext +{ + OUString m_aStyleName; + OUString m_aDfltCellStyleName; + OUString m_aTemplateName; + + //! Holds basic information about a column's width. + struct ColumnWidthInfo { + sal_uInt16 width; //!< Column width (absolute or relative). + bool isRelative; //!< True for a relative width, false for absolute. + ColumnWidthInfo(sal_uInt16 wdth, bool isRel) : width(wdth), isRelative(isRel) {}; + }; + std::vector<ColumnWidthInfo> m_aColumnWidths; + std::unique_ptr<std::vector<OUString>> m_pColumnDefaultCellStyleNames; + + css::uno::Reference< css::text::XTextCursor > m_xOldCursor; + css::uno::Reference< css::text::XTextContent > m_xTextContent; + + std::unique_ptr<SwXMLTableRows_Impl> m_pRows; + + SwTableNode *m_pTableNode; + SwTableBox *m_pBox1; + bool m_bOwnsBox1; + const SwStartNode *m_pSttNd1; + + SwTableBoxFormat *m_pBoxFormat; + SwTableLineFormat *m_pLineFormat; + + // hash map of shared format, indexed by the (XML) style name, + // the column width, and protection flag + typedef std::unordered_map<TableBoxIndex,SwTableBoxFormat*, + TableBoxIndexHasher> map_BoxFormat; + std::unique_ptr<map_BoxFormat> m_pSharedBoxFormats; + + SvXMLImportContextRef m_xParentTable; // if table is a sub table + + rtl::Reference<SwXMLDDETableContext_Impl> m_xDDESource; + + bool m_bFirstSection : 1; + bool m_bRelWidth : 1; + bool m_bHasSubTables : 1; + + sal_uInt16 m_nHeaderRows; + sal_uInt32 m_nCurRow; + sal_uInt32 m_nCurCol; + sal_Int32 m_nWidth; + + // The maximum table width (i.e., maximum value for m_nWidth); must be >= MINLAY and must also + // fit into ColumnWidthInfo::width (of type sal_uInt16), see e.g. the emplacement of + // MINLAY<=nWidth2<=MAX_WIDTH into m_aColumnWidths in SwXMLTableContext::InsertColumn: + static constexpr sal_Int32 MAX_WIDTH = SAL_MAX_UINT16; + + SwTableBox *NewTableBox( const SwStartNode *pStNd, + SwTableLine *pUpper ); + SwTableBox *MakeTableBox( SwTableLine *pUpper, + const SwXMLTableCell_Impl *pStartNode, + sal_uInt32 nLeftCol, sal_uInt32 nRightCol ); + SwTableBox *MakeTableBox( SwTableLine *pUpper, + sal_uInt32 nTopRow, sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, sal_uInt32 nRightCol ); + SwTableLine *MakeTableLine( SwTableBox *pUpper, + sal_uInt32 nTopRow, sal_uInt32 nLeftCol, + sal_uInt32 nBottomRow, sal_uInt32 nRightCol ); + + void MakeTable_( SwTableBox *pBox=nullptr ); + void MakeTable( SwTableBox *pBox, sal_Int32 nWidth ); + void MakeTable(); + + inline SwXMLTableContext *GetParentTable() const; + + const SwStartNode *GetPrevStartNode( sal_uInt32 nRow, + sal_uInt32 nCol ) const; + inline const SwStartNode *GetLastStartNode() const; + void FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol, sal_uInt32 nColSpan ); + void ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows ); + + /** sets the appropriate SwTableBoxFormat at pBox. */ + SwTableBoxFormat* GetSharedBoxFormat( + SwTableBox* pBox, /// the table box + const OUString& rStyleName, /// XML style name + sal_Int32 nColumnWidth, /// width of column + bool bProtected, /// is cell protected? + bool bMayShare, /// may the format be shared (no value, formula...) + bool& bNew, /// true, if the format it not from the cache + bool* pModifyLocked ); /// if set, call pBox->LockModify() and return old lock status + +public: + + + SwXMLTableContext( SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ); + SwXMLTableContext( SwXMLImport& rImport, sal_uInt16 nPrfx, + const OUString& rLName, + SwXMLTableContext *pTable ); + + virtual ~SwXMLTableContext() override; + + virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ) override; + + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + + void InsertColumn( sal_Int32 nWidth, bool bRelWidth, + const OUString *pDfltCellStyleName = nullptr ); + sal_Int32 GetColumnWidth( sal_uInt32 nCol, sal_uInt32 nColSpan ) const; + OUString GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const; + inline sal_uInt32 GetColumnCount() const; + + bool IsInsertCellPossible() const { return m_nCurCol < GetColumnCount(); } + bool IsInsertColPossible() const { return m_nCurCol < USHRT_MAX; } + bool IsInsertRowPossible() const { return m_nCurRow < USHRT_MAX; } + bool IsValid() const { return m_pTableNode != nullptr; } + + void InsertCell( const OUString& rStyleName, + sal_uInt32 nRowSpan, sal_uInt32 nColSpan, + const SwStartNode *pStNd, + SwXMLTableContext *pTable=nullptr, + bool bIsProtected = false, + const OUString *pFormula=nullptr, + bool bHasValue = false, + double fValue = 0.0, + OUString const*const pStringValue = nullptr); + void InsertRow( const OUString& rStyleName, + const OUString& rDfltCellStyleName, + bool bInHead ); + void FinishRow(); + void InsertRepRows( sal_uInt32 nCount ); + const SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol ) const; + SwXMLTableCell_Impl *GetCell( sal_uInt32 nRow, sal_uInt32 nCol ); + const SwStartNode *InsertTableSection(const SwStartNode *pPrevSttNd = nullptr, + OUString const* pStringValueStyleName = nullptr); + + virtual void EndElement() override; + + void SetHasSubTables( bool bNew ) { m_bHasSubTables = bNew; } +}; + +inline SwXMLTableContext *SwXMLTableContext::GetParentTable() const +{ + return static_cast<SwXMLTableContext *>(m_xParentTable.get()); +} + +inline sal_uInt32 SwXMLTableContext::GetColumnCount() const +{ + return m_aColumnWidths.size(); +} + +inline const SwStartNode *SwXMLTableContext::GetLastStartNode() const +{ + return GetPrevStartNode( 0UL, GetColumnCount() ); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltext.cxx b/sw/source/filter/xml/xmltext.cxx new file mode 100644 index 000000000..e4bf212a3 --- /dev/null +++ b/sw/source/filter/xml/xmltext.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 <xmloff/xmlnmspe.hxx> +#include "xmlimp.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; + +namespace { + +class SwXMLBodyContentContext_Impl : public SvXMLImportContext +{ + SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); } + +public: + + SwXMLBodyContentContext_Impl( SwXMLImport& rImport ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 /*nElement*/, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) override + { return nullptr; } + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) override; + + virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override {} + + // The body element's text:global attribute can be ignored, because + // we must have the correct object shell already. + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +SwXMLBodyContentContext_Impl::SwXMLBodyContentContext_Impl( SwXMLImport& rImport ) : + SvXMLImportContext( rImport ) +{ +} + +SvXMLImportContextRef SwXMLBodyContentContext_Impl::CreateChildContext( + sal_uInt16 nPrefix, const OUString& rLocalName, + const Reference< xml::sax::XAttributeList > & xAttrList ) +{ + SvXMLImportContext *pContext = GetSwImport().GetTextImport()->CreateTextChildContext( + GetImport(), nPrefix, rLocalName, xAttrList, + XMLTextType::Body ); + + return pContext; +} + +void SwXMLBodyContentContext_Impl::endFastElement(sal_Int32 ) +{ + /* Code moved to SwXMLOmport::endDocument */ + GetImport().GetTextImport()->SetOutlineStyles( false ); +} + +SvXMLImportContext *SwXMLImport::CreateBodyContentContext() +{ + SvXMLImportContext *pContext = nullptr; + + if( !IsStylesOnlyMode() ) + pContext = new SwXMLBodyContentContext_Impl( *this ); + + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexte.cxx b/sw/source/filter/xml/xmltexte.cxx new file mode 100644 index 000000000..7645de372 --- /dev/null +++ b/sw/source/filter/xml/xmltexte.cxx @@ -0,0 +1,583 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/classids.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp> +#include <xmloff/families.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/txtprmap.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlexppr.hxx> + +#include <ndole.hxx> +#include <fmtcntnt.hxx> +#include <unoframe.hxx> +#include "xmlexp.hxx" +#include "xmltexte.hxx" +#include <SwAppletImpl.hxx> +#include <ndindex.hxx> + +#include <sot/exchange.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/frmdescr.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::io; +using namespace ::xmloff::token; + +namespace { + +enum SvEmbeddedObjectTypes +{ + SV_EMBEDDED_OWN, + SV_EMBEDDED_OUTPLACE, + SV_EMBEDDED_APPLET, + SV_EMBEDDED_PLUGIN, + SV_EMBEDDED_FRAME +}; + +} + +SwNoTextNode *SwXMLTextParagraphExport::GetNoTextNode( + const Reference < XPropertySet >& rPropSet ) +{ + Reference<XUnoTunnel> xCursorTunnel( rPropSet, UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for embedded"); + SwXFrame *pFrame = reinterpret_cast< SwXFrame * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( SwXFrame::getUnoTunnelId() ))); + assert(pFrame && "SwXFrame missing"); + SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat(); + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + return pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode(); +} + +static const OUStringLiteral gsEmbeddedObjectProtocol( "vnd.sun.star.EmbeddedObject:" ); + +SwXMLTextParagraphExport::SwXMLTextParagraphExport( + SwXMLExport& rExp, + SvXMLAutoStylePoolP& _rAutoStylePool ) : + XMLTextParagraphExport( rExp, _rAutoStylePool ), + aAppletClassId( SO3_APPLET_CLASSID ), + aPluginClassId( SO3_PLUGIN_CLASSID ), + aIFrameClassId( SO3_IFRAME_CLASSID ) +{ +} + +SwXMLTextParagraphExport::~SwXMLTextParagraphExport() +{ +} + +static void lcl_addURL ( SvXMLExport &rExport, const OUString &rURL, + bool bToRel = true ) +{ + const OUString sRelURL = ( bToRel && !rURL.isEmpty() ) + ? URIHelper::simpleNormalizedMakeRelative(rExport.GetOrigFileName(), rURL) + : rURL; + + if (!sRelURL.isEmpty()) + { + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_HREF, sRelURL ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_SHOW, XML_EMBED ); + rExport.AddAttribute ( XML_NAMESPACE_XLINK, XML_ACTUATE, XML_ONLOAD ); + } +} + +static void lcl_addAspect( + const svt::EmbeddedObjectRef& rObj, + const XMLPropertyState **pStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + sal_Int64 nAspect = rObj.GetViewAspect(); + if ( nAspect ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_OLE_DRAW_ASPECT ), uno::makeAny( nAspect ) ); + } +} + +static void lcl_addOutplaceProperties( + const svt::EmbeddedObjectRef& rObj, + const XMLPropertyState **pStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + MapMode aMode( MapUnit::Map100thMM ); // the API expects this map mode for the embedded objects + Size aSize = rObj.GetSize( &aMode ); // get the size in the requested map mode + + if( aSize.Width() && aSize.Height() ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_LEFT ), Any(sal_Int32(0)) ); + pStates++; + + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_TOP ), Any(sal_Int32(0)) ); + pStates++; + + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_WIDTH ), Any(static_cast<sal_Int32>(aSize.Width())) ); + pStates++; + + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_OLE_VIS_AREA_HEIGHT ), Any(static_cast<sal_Int32>(aSize.Height())) ); + } +} + +static void lcl_addFrameProperties( + const uno::Reference < embed::XEmbeddedObject >& xObj, + const XMLPropertyState **pStates, + const rtl::Reference < XMLPropertySetMapper >& rMapper ) +{ + if ( !::svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + return; + + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( !xSet.is() ) + return; + + bool bIsAutoScroll = false, bIsScrollingMode = false; + Any aAny = xSet->getPropertyValue("FrameIsAutoScroll"); + aAny >>= bIsAutoScroll; + if ( !bIsAutoScroll ) + { + aAny = xSet->getPropertyValue("FrameIsScrollingMode"); + aAny >>= bIsScrollingMode; + } + + bool bIsBorderSet = false, bIsAutoBorder = false; + aAny = xSet->getPropertyValue("FrameIsAutoBorder"); + aAny >>= bIsAutoBorder; + if ( !bIsAutoBorder ) + { + aAny = xSet->getPropertyValue("FrameIsBorder"); + aAny >>= bIsBorderSet; + } + + sal_Int32 nWidth, nHeight; + aAny = xSet->getPropertyValue("FrameMarginWidth"); + aAny >>= nWidth; + aAny = xSet->getPropertyValue("FrameMarginHeight"); + aAny >>= nHeight; + + if( !bIsAutoScroll ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_SCROLLBAR ), makeAny(bIsScrollingMode) ); + pStates++; + } + if( !bIsAutoBorder ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_FRAME_DISPLAY_BORDER ), makeAny(bIsBorderSet) ); + pStates++; + } + if( SIZE_NOT_SET != nWidth ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_HORI ), Any(nWidth) ); + pStates++; + } + if( SIZE_NOT_SET != nHeight ) + { + *pStates = new XMLPropertyState( rMapper->FindEntryIndex( CTF_FRAME_MARGIN_VERT ), Any(nHeight) ); + } +} + +void SwXMLTextParagraphExport::_collectTextEmbeddedAutoStyles( + const Reference < XPropertySet > & rPropSet ) +{ + SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode(); + svt::EmbeddedObjectRef& rObjRef = pOLENd->GetOLEObj().GetObject(); + if( !rObjRef.is() ) + return; + + const XMLPropertyState *aStates[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + SvGlobalName aClassId( rObjRef->getClassID() ); + + if( aIFrameClassId == aClassId ) + { + lcl_addFrameProperties( rObjRef.GetObject(), aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + } + else if ( !SotExchange::IsInternal( aClassId ) ) + { + lcl_addOutplaceProperties( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + } + + lcl_addAspect( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + + Add( XmlStyleFamily::TEXT_FRAME, rPropSet, aStates ); + + const XMLPropertyState **pStates = aStates; + while( *pStates ) + { + delete *pStates; + pStates++; + } +} + +void SwXMLTextParagraphExport::_exportTextEmbedded( + const Reference < XPropertySet > & rPropSet, + const Reference < XPropertySetInfo > & rPropSetInfo ) +{ + SwOLENode *pOLENd = GetNoTextNode( rPropSet )->GetOLENode(); + SwOLEObj& rOLEObj = pOLENd->GetOLEObj(); + svt::EmbeddedObjectRef& rObjRef = rOLEObj.GetObject(); + if( !rObjRef.is() ) + return; + + SvGlobalName aClassId( rObjRef->getClassID() ); + + SvEmbeddedObjectTypes nType = SV_EMBEDDED_OWN; + if( aPluginClassId == aClassId ) + { + nType = SV_EMBEDDED_PLUGIN; + } + else if( aAppletClassId == aClassId ) + { + nType = SV_EMBEDDED_APPLET; + } + else if( aIFrameClassId == aClassId ) + { + nType = SV_EMBEDDED_FRAME; + } + else if ( !SotExchange::IsInternal( aClassId ) ) + { + nType = SV_EMBEDDED_OUTPLACE; + } + + enum XMLTokenEnum eElementName = XML__UNKNOWN_; + SvXMLExport &rXMLExport = GetExport(); + + // First the stuff common to each of Applet/Plugin/Floating Frame + OUString sStyle; + Any aAny; + if( rPropSetInfo->hasPropertyByName( gsFrameStyleName ) ) + { + aAny = rPropSet->getPropertyValue( gsFrameStyleName ); + aAny >>= sStyle; + } + + const XMLPropertyState *aStates[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + switch( nType ) + { + case SV_EMBEDDED_FRAME: + lcl_addFrameProperties( rObjRef.GetObject(), aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + break; + case SV_EMBEDDED_OUTPLACE: + lcl_addOutplaceProperties( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + break; + default: + ; + } + + lcl_addAspect( rObjRef, aStates, + GetAutoFramePropMapper()->getPropertySetMapper() ); + + const OUString sAutoStyle = Find( XmlStyleFamily::TEXT_FRAME, + rPropSet, sStyle, aStates ); + const XMLPropertyState **pStates = aStates; + while( *pStates ) + { + delete *pStates; + pStates++; + } + + if( !sAutoStyle.isEmpty() ) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE_NAME, sAutoStyle ); + addTextFrameAttributes( rPropSet, false ); + + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_DRAW, + XML_FRAME, false, true ); + + switch (nType) + { + case SV_EMBEDDED_OUTPLACE: + case SV_EMBEDDED_OWN: + if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) ) + { + OUString sURL; + + bool bIsOwnLink = false; + if( SV_EMBEDDED_OWN == nType ) + { + try + { + uno::Reference< embed::XLinkageSupport > xLinkage( rObjRef.GetObject(), uno::UNO_QUERY ); + bIsOwnLink = xLinkage.is() && xLinkage->isLink(); + if ( bIsOwnLink ) + sURL = xLinkage->getLinkURL(); + } + catch(const uno::Exception&) + { + // TODO/LATER: error handling + OSL_FAIL( "Link detection or retrieving of the URL of OOo link is failed!" ); + } + } + + if ( !bIsOwnLink ) + { + sURL = gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName(); + } + + sURL = GetExport().AddEmbeddedObject( sURL ); + lcl_addURL( rXMLExport, sURL, false ); + } + if( SV_EMBEDDED_OWN == nType && !pOLENd->GetChartTableName().isEmpty() ) + { + OUString sRange( pOLENd->GetChartTableName() ); + OUStringBuffer aBuffer( sRange.getLength() + 2 ); + for( sal_Int32 i=0; i < sRange.getLength(); i++ ) + { + sal_Unicode c = sRange[i]; + switch( c ) + { + case ' ': + case '.': + case '\'': + case '\\': + if( aBuffer.isEmpty() ) + { + aBuffer.append( '\'' ); + aBuffer.append( std::u16string_view(sRange).substr(0, i) ); + } + if( '\'' == c || '\\' == c ) + aBuffer.append( '\\' ); + [[fallthrough]]; + default: + if( !aBuffer.isEmpty() ) + aBuffer.append( c ); + } + } + if( !aBuffer.isEmpty() ) + { + aBuffer.append( '\'' ); + sRange = aBuffer.makeStringAndClear(); + } + + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NOTIFY_ON_UPDATE_OF_RANGES, + sRange ); + } + eElementName = SV_EMBEDDED_OUTPLACE==nType ? XML_OBJECT_OLE + : XML_OBJECT; + break; + case SV_EMBEDDED_APPLET: + { + // It's an applet! + if( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("AppletCodeBase"); + aAny2 >>= aStr; + if (!aStr.isEmpty() ) + lcl_addURL(rXMLExport, aStr); + + aAny2 = xSet->getPropertyValue("AppletName"); + aAny2 >>= aStr; + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_APPLET_NAME, aStr ); + + aAny2 = xSet->getPropertyValue("AppletCode"); + aAny2 >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CODE, aStr ); + + bool bScript = false; + aAny2 = xSet->getPropertyValue("AppletIsScript"); + aAny2 >>= bScript; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MAY_SCRIPT, bScript ? XML_TRUE : XML_FALSE ); + + uno::Sequence < beans::PropertyValue > aProps; + aAny2 = xSet->getPropertyValue("AppletCommands"); + aAny2 >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true ); + if ( nType2 == SwHtmlOptType::TAG) + { + OUString aStr2; + aProp.Value >>= aStr2; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, aProp.Name, aStr2); + } + } + + eElementName = XML_APPLET; + } + } + break; + case SV_EMBEDDED_PLUGIN: + { + // It's a plugin! + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("PluginURL"); + aAny2 >>= aStr; + lcl_addURL( rXMLExport, aStr ); + + aAny2 = xSet->getPropertyValue("PluginMimeType"); + aAny2 >>= aStr; + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_MIME_TYPE, aStr ); + eElementName = XML_PLUGIN; + } + } + break; + case SV_EMBEDDED_FRAME: + { + // It's a floating frame! + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + OUString aStr; + Any aAny2 = xSet->getPropertyValue("FrameURL"); + aAny2 >>= aStr; + + lcl_addURL( rXMLExport, aStr ); + + aAny2 = xSet->getPropertyValue("FrameName"); + aAny2 >>= aStr; + + if (!aStr.isEmpty()) + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_FRAME_NAME, aStr ); + eElementName = XML_FLOATING_FRAME; + } + } + break; + default: + OSL_ENSURE( false, "unknown object type! Base class should have been called!" ); + } + + { + SvXMLElementExport aElementExport( rXMLExport, XML_NAMESPACE_DRAW, eElementName, + false, true ); + switch( nType ) + { + case SV_EMBEDDED_OWN: + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + { + Reference < XEmbeddedObjectSupplier > xEOS( rPropSet, UNO_QUERY ); + OSL_ENSURE( xEOS.is(), "no embedded object supplier for own object" ); + Reference < XComponent > xComp = xEOS->getEmbeddedObject(); + rXMLExport.ExportEmbeddedOwnObject( xComp ); + } + break; + case SV_EMBEDDED_OUTPLACE: + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + { + OUString sURL( gsEmbeddedObjectProtocol + rOLEObj.GetCurrentPersistName() ); + + if ( !( rXMLExport.getExportFlags() & SvXMLExportFlags::OASIS ) ) + sURL += "?oasis=false"; + + rXMLExport.AddEmbeddedObjectAsBase64( sURL ); + } + break; + case SV_EMBEDDED_APPLET: + { + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("AppletCommands"); + aAny >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, true ); + if (SwHtmlOptType::PARAM == nType2 || SwHtmlOptType::SIZE == nType2 ) + { + OUString aStr; + aProp.Value >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name ); + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + } + } + break; + case SV_EMBEDDED_PLUGIN: + { + if ( svt::EmbeddedObjectRef::TryRunningState( rObjRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( rObjRef->getComponent(), uno::UNO_QUERY ); + uno::Sequence < beans::PropertyValue > aProps; + aAny = xSet->getPropertyValue("PluginCommands"); + aAny >>= aProps; + + sal_Int32 i = aProps.getLength(); + while ( i > 0 ) + { + beans::PropertyValue& aProp = aProps[--i]; + const SwHtmlOptType nType2 = SwApplet_Impl::GetOptionType( aProp.Name, false ); + if ( nType2 == SwHtmlOptType::TAG) + { + OUString aStr; + aProp.Value >>= aStr; + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, aProp.Name ); + rXMLExport.AddAttribute( XML_NAMESPACE_DRAW, XML_VALUE, aStr ); + SvXMLElementExport aElementExport2( rXMLExport, XML_NAMESPACE_DRAW, XML_PARAM, false, true ); + } + } + } + } + break; + default: + break; + } + } + if( SV_EMBEDDED_OUTPLACE==nType || SV_EMBEDDED_OWN==nType ) + { + OUString sURL = XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE + rOLEObj.GetCurrentPersistName(); + if( !(rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED) ) + { + sURL = GetExport().AddEmbeddedObject( sURL ); + lcl_addURL( rXMLExport, sURL, false ); + } + + SvXMLElementExport aElementExport( GetExport(), XML_NAMESPACE_DRAW, + XML_IMAGE, false, true ); + + if( rXMLExport.getExportFlags() & SvXMLExportFlags::EMBEDDED ) + GetExport().AddEmbeddedObjectAsBase64( sURL ); + } + + // Lastly the stuff common to each of Applet/Plugin/Floating Frame + exportEvents( rPropSet ); + exportTitleAndDescription( rPropSet, rPropSetInfo ); // #i73249# + exportContour( rPropSet, rPropSetInfo ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexte.hxx b/sw/source/filter/xml/xmltexte.hxx new file mode 100644 index 000000000..3fc9530e6 --- /dev/null +++ b/sw/source/filter/xml/xmltexte.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_FILTER_XML_XMLTEXTE_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX + +#include <xmloff/txtparae.hxx> +#include <tools/globname.hxx> + +#define XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE "vnd.sun.star.GraphicObject:" + +class SwXMLExport; +class SvXMLAutoStylePoolP; +class SwNoTextNode; +class SwTableNode; +namespace com::sun::star::style { class XStyle; } + +class SwXMLTextParagraphExport : public XMLTextParagraphExport +{ + const SvGlobalName aAppletClassId; + const SvGlobalName aPluginClassId; + const SvGlobalName aIFrameClassId; + + // Collected autostyles for use in exportTextAutoStyles + std::vector<const SwTableNode*> maTableNodes; + + static SwNoTextNode *GetNoTextNode( + const css::uno::Reference < css::beans::XPropertySet >& rPropSet ); + +protected: + virtual void _collectTextEmbeddedAutoStyles( + const css::uno::Reference< css::beans::XPropertySet > & rPropSet ) override; + virtual void _exportTextEmbedded( + const css::uno::Reference< css::beans::XPropertySet > & rPropSet, + const css::uno::Reference< css::beans::XPropertySetInfo > & rPropSetInfo ) override; + + virtual void exportTable( + const css::uno::Reference< css::text::XTextContent > & rTextContent, + bool bAutoStyles, bool bProgress ) override; + + virtual void exportTableAutoStyles() override; + +public: + SwXMLTextParagraphExport( + SwXMLExport& rExp, + SvXMLAutoStylePoolP& rAutoStylePool ); + virtual ~SwXMLTextParagraphExport() override; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexti.cxx b/sw/source/filter/xml/xmltexti.cxx new file mode 100644 index 000000000..9f9995868 --- /dev/null +++ b/sw/source/filter/xml/xmltexti.cxx @@ -0,0 +1,1041 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/storagehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/embed/EmbeddedObjectCreator.hpp> +#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <o3tl/any.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <comphelper/classids.hxx> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <xmloff/prstylei.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/txtprmap.hxx> +#include <xmloff/i18nmap.hxx> +#include <xmloff/xmlimppr.hxx> +#include <TextCursorHelper.hxx> +#include <unoframe.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentContentOperations.hxx> +#include <fmtfsize.hxx> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include "xmlimp.hxx" +#include "xmltbli.hxx" +#include "xmltexti.hxx" +#include "XMLRedlineImportHelper.hxx" +#include <xmloff/XMLFilterServiceNames.h> +#include <SwAppletImpl.hxx> +#include <ndole.hxx> +#include <docsh.hxx> +#include <sfx2/docfile.hxx> +#include <vcl/svapp.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svtools/embedhlp.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/frmdescr.hxx> +#include <tools/globname.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace xml::sax; + +namespace { + +struct XMLServiceMapEntry_Impl +{ + const char *sFilterService; + sal_Int32 nFilterServiceLen; + + sal_uInt32 n1; + sal_uInt16 n2, n3; + sal_uInt8 n4, n5, n6, n7, n8, n9, n10, n11; +}; + +} + +#define SERVICE_MAP_ENTRY( app, s ) \ + { XML_IMPORT_FILTER_##app, sizeof(XML_IMPORT_FILTER_##app)-1, \ + SO3_##s##_CLASSID } + +const XMLServiceMapEntry_Impl aServiceMap[] = +{ + SERVICE_MAP_ENTRY( WRITER, SW ), + SERVICE_MAP_ENTRY( CALC, SC ), + SERVICE_MAP_ENTRY( DRAW, SDRAW ), + SERVICE_MAP_ENTRY( IMPRESS, SIMPRESS ), + SERVICE_MAP_ENTRY( CHART, SCH ), + SERVICE_MAP_ENTRY( MATH, SM ), + { nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; +static void lcl_putHeightAndWidth ( SfxItemSet &rItemSet, + sal_Int32 nHeight, sal_Int32 nWidth, + Size *pTwipSize = nullptr ) +{ + if( nWidth > 0 && nHeight > 0 ) + { + nWidth = convertMm100ToTwip( nWidth ); + if( nWidth < MINFLY ) + nWidth = MINFLY; + nHeight = convertMm100ToTwip( nHeight ); + if( nHeight < MINFLY ) + nHeight = MINFLY; + rItemSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, nWidth, nHeight ) ); + } + + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_CHAR ); + rItemSet.Put( aAnchor ); + + if( pTwipSize ) + { + pTwipSize->setWidth( nWidth ); + pTwipSize->setHeight( nHeight); + } +} + +static void lcl_setObjectVisualArea( const uno::Reference< embed::XEmbeddedObject >& xObj, + sal_Int64 nAspect, + const Size& aVisSize, + const MapUnit& aUnit ) +{ + if( xObj.is() && nAspect != embed::Aspects::MSOLE_ICON ) + { + // convert the visual area to the objects units + MapUnit aObjUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + Size aObjVisSize = OutputDevice::LogicToLogic(aVisSize, MapMode(aUnit), MapMode(aObjUnit)); + awt::Size aSz; + aSz.Width = aObjVisSize.Width(); + aSz.Height = aObjVisSize.Height(); + + try + { + xObj->setVisualAreaSize( nAspect, aSz ); + } + catch( uno::Exception& ) + { + OSL_FAIL( "Couldn't set visual area of the object!" ); + } + } +} + +SwXMLTextImportHelper::SwXMLTextImportHelper( + const uno::Reference < XModel>& rModel, + SvXMLImport& rImport, + const uno::Reference<XPropertySet> & rInfoSet, + bool bInsertM, bool bStylesOnlyM, + bool bBlockM, bool bOrganizerM ) : + XMLTextImportHelper( rModel, rImport, bInsertM, bStylesOnlyM, true/*bProgress*/, + bBlockM, bOrganizerM ), + pRedlineHelper( nullptr ) +{ + uno::Reference<XPropertySet> xDocPropSet( rModel, UNO_QUERY ); + pRedlineHelper = new XMLRedlineImportHelper(rImport, + bInsertM || bBlockM, xDocPropSet, rInfoSet ); +} + +SwXMLTextImportHelper::~SwXMLTextImportHelper() +{ + // the redline helper destructor sets properties on the document + // and may throw an exception while doing so... catch this + try + { + delete pRedlineHelper; + } + catch ( const RuntimeException& ) + { + // ignore + } +} + +SvXMLImportContext *SwXMLTextImportHelper::CreateTableChildContext( + SvXMLImport& rImport, + sal_uInt16 nPrefix, const OUString& rLocalName, + const uno::Reference< XAttributeList > & xAttrList ) +{ + return new SwXMLTableContext( + static_cast<SwXMLImport&>(rImport), nPrefix, rLocalName, xAttrList ); +} + +bool SwXMLTextImportHelper::IsInHeaderFooter() const +{ + uno::Reference<XUnoTunnel> xCursorTunnel( + const_cast<SwXMLTextImportHelper *>(this)->GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing"); + SwDoc *pDoc = pTextCursor ? pTextCursor->GetDoc() : nullptr; + + return pDoc && pDoc->IsInHeaderFooter( pTextCursor->GetPaM()->GetPoint()->nNode ); +} + +static SwOLENode *lcl_GetOLENode( const SwFrameFormat *pFrameFormat ) +{ + SwOLENode *pOLENd = nullptr; + if( pFrameFormat ) + { + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode(); + } + OSL_ENSURE( pOLENd, "Where is the OLE node" ); + return pOLENd; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOLEObject( + SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + + sal_Int32 nPos = rHRef.indexOf( ':' ); + if( -1 == nPos ) + return xPropSet; + + OUString aObjName( rHRef.copy( nPos+1) ); + + if( aObjName.isEmpty() ) + return xPropSet; + + uno::Reference<XUnoTunnel> xCursorTunnel( GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + SAL_WARN_IF(!pTextCursor, "sw.uno", "SwXTextCursor missing"); + SwDoc *pDoc = SwImport::GetDocFromXMLImport( rImport ); + + SfxItemSet aItemSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END>{} ); + Size aTwipSize( 0, 0 ); + tools::Rectangle aVisArea( 0, 0, nWidth, nHeight ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth, + &aTwipSize ); + + SwFrameFormat *pFrameFormat = nullptr; + SwOLENode *pOLENd = nullptr; + if( rHRef.startsWith("vnd.sun.star.ServiceName:") ) + { + bool bInsert = false; + SvGlobalName aClassName; + const XMLServiceMapEntry_Impl *pEntry = aServiceMap; + while( pEntry->sFilterService ) + { + if( aObjName.equalsAsciiL( pEntry->sFilterService, + pEntry->nFilterServiceLen ) ) + { + aClassName = SvGlobalName( pEntry->n1, pEntry->n2, + pEntry->n3, pEntry->n4, + pEntry->n5, pEntry->n6, + pEntry->n7, pEntry->n8, + pEntry->n9, pEntry->n10, + pEntry->n11 ); + bInsert = true; + break; + } + pEntry++; + } + + if( bInsert ) + { + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( aClassName.GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Sequence<beans::PropertyValue> aObjArgs( comphelper::InitPropertySequence({ + { "DefaultParentBaseURL", Any(GetXMLImport().GetBaseURL()) } + })); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", aObjArgs), uno::UNO_QUERY ); + if ( xObj.is() ) + { + //TODO/LATER: is it enough to only set the VisAreaSize? + lcl_setObjectVisualArea( xObj, embed::Aspects::MSOLE_CONTENT, aTwipSize, MapUnit::MapTwip ); + } + + if( pTextCursor ) + { + pFrameFormat = pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + pOLENd = lcl_GetOLENode( pFrameFormat ); + } + + if( pOLENd ) + aObjName = pOLENd->GetOLEObj().GetCurrentPersistName(); + } + catch ( uno::Exception& ) + { + } + } + } + else + { + // check whether an object with this name already exists in the document + OUString aName; + SwIterator<SwContentNode,SwFormatColl> aIter( *pDoc->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + { + SwOLENode* pExistingOLENd = pNd->GetOLENode(); + if( pExistingOLENd ) + { + OUString aExistingName = pExistingOLENd->GetOLEObj().GetCurrentPersistName(); + if ( aExistingName == aObjName ) + { + OSL_FAIL( "The document contains duplicate object references, means it is partially broken, please let developers know how this document was generated!" ); + + OUString aTmpName = pDoc->GetPersist()->GetEmbeddedObjectContainer().CreateUniqueObjectName(); + try + { + pDoc->GetPersist()->GetStorage()->copyElementTo( aObjName, + pDoc->GetPersist()->GetStorage(), + aTmpName ); + aName = aTmpName; + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Couldn't create a copy of the object!" ); + } + + break; + } + } + } + + if ( aName.isEmpty() ) + aName = aObjName; + + // the correct aspect will be set later + // TODO/LATER: Actually it should be set here + if( pTextCursor ) + { + pFrameFormat = pDoc->getIDocumentContentOperations().InsertOLE( *pTextCursor->GetPaM(), aName, embed::Aspects::MSOLE_CONTENT, &aItemSet, nullptr ); + pOLENd = lcl_GetOLENode( pFrameFormat ); + } + aObjName = aName; + } + + if( !pFrameFormat ) + return xPropSet; + + if( IsInsertMode() ) + { + if( !pOLENd ) + pOLENd = lcl_GetOLENode( pFrameFormat ); + if( pOLENd ) + pOLENd->SetOLESizeInvalid( true ); + } + + xPropSet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat), uno::UNO_QUERY); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + if( !rTableName.isEmpty() ) + { + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + SwOLENode *pOLENode = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetOLENode(); + OSL_ENSURE( pOLENode, "Where is the OLE node" ); + + OUStringBuffer aBuffer( rTableName.getLength() ); + bool bQuoted = false; + bool bEscape = false; + bool bError = false; + for( sal_Int32 i=0; i < rTableName.getLength(); i++ ) + { + bool bEndOfNameFound = false; + sal_Unicode c = rTableName[i]; + switch( c ) + { + case '\'': + if( bEscape ) + { + aBuffer.append( c ); + bEscape = false; + } + else if( bQuoted ) + { + bEndOfNameFound = true; + } + else if( 0 == i ) + { + bQuoted = true; + } + else + { + bError = true; + } + break; + case '\\': + if( bEscape ) + { + aBuffer.append( c ); + bEscape = false; + } + else + { + bEscape = true; + } + break; + case ' ': + case '.': + if( !bQuoted ) + { + bEndOfNameFound = true; + } + else + { + aBuffer.append( c ); + bEscape = false; + } + break; + default: + { + aBuffer.append( c ); + bEscape = false; + } + break; + } + if( bError || bEndOfNameFound ) + break; + } + if( !bError ) + { + OUString sTableName( aBuffer.makeStringAndClear() ); + pOLENode->SetChartTableName( GetRenameMap().Get( XML_TEXT_RENAME_TYPE_TABLE, sTableName ) ); + } + } + + sal_Int64 nDrawAspect = 0; + const XMLPropStyleContext *pStyle = nullptr; + bool bHasSizeProps = false; + if( !rStyleName.isEmpty() ) + { + pStyle = FindAutoFrameStyle( rStyleName ); + if( pStyle ) + { + rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap = + pStyle->GetStyles() + ->GetImportPropertyMapper(pStyle->GetFamily()); + OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" ); + if( xImpPrMap.is() ) + { + rtl::Reference<XMLPropertySetMapper> rPropMapper = + xImpPrMap->getPropertySetMapper(); + + sal_Int32 nCount = pStyle->GetProperties().size(); + for( sal_Int32 i=0; i < nCount; i++ ) + { + const XMLPropertyState& rProp = pStyle->GetProperties()[i]; + sal_Int32 nIdx = rProp.mnIndex; + if( -1 == nIdx ) + continue; + + switch( rPropMapper->GetEntryContextId(nIdx) ) + { + case CTF_OLE_VIS_AREA_LEFT: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setX( nVal ); + } + break; + case CTF_OLE_VIS_AREA_TOP: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setY( nVal ); + } + break; + case CTF_OLE_VIS_AREA_WIDTH: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setWidth( nVal ); + bHasSizeProps = true; + } + break; + case CTF_OLE_VIS_AREA_HEIGHT: + { + sal_Int32 nVal = 0; + rProp.maValue >>= nVal; + aVisArea.setHeight( nVal ); + bHasSizeProps = true; + } + break; + case CTF_OLE_DRAW_ASPECT: + { + rProp.maValue >>= nDrawAspect; + + if ( !nDrawAspect ) + nDrawAspect = embed::Aspects::MSOLE_CONTENT; + + if ( pOLENd ) + pOLENd->GetOLEObj().GetObject().SetViewAspect( nDrawAspect ); + } + break; + } + } + } + } + } + + if ( bHasSizeProps ) + { + uno::Reference < embed::XEmbeddedObject > xObj = + pDoc->GetPersist()->GetEmbeddedObjectContainer().GetEmbeddedObject( aObjName ); + if( xObj.is() ) + lcl_setObjectVisualArea( xObj, ( nDrawAspect ? nDrawAspect : embed::Aspects::MSOLE_CONTENT ), + aVisArea.GetSize(), MapUnit::Map100thMM ); + } + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertOOoLink( + SvXMLImport& rImport, + const OUString& rHRef, + const OUString& /*rStyleName*/, + const OUString& /*rTableName*/, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + + uno::Reference<XUnoTunnel> xCursorTunnel( GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + SwDoc *pDoc = SwImport::GetDocFromXMLImport( rImport ); + + SfxItemSet aItemSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END>{} ); + Size aTwipSize( 0, 0 ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth, + &aTwipSize ); + + // We'll need a (valid) URL. If we don't have do not insert the link and return early. + // Copy URL into URL object on the way. + INetURLObject aURLObj; + bool bValidURL = !rHRef.isEmpty() && + aURLObj.SetURL( URIHelper::SmartRel2Abs( + INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ); + if( !bValidURL ) + return xPropSet; + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = + embed::OOoEmbeddedObjectFactory::create(::comphelper::getProcessComponentContext()); + + uno::Sequence< beans::PropertyValue > aMediaDescriptor( 1 ); + aMediaDescriptor[0].Name = "URL"; + aMediaDescriptor[0].Value <<= aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if (SfxMedium* pMedium = pDoc->GetDocShell() ? pDoc->GetDocShell()->GetMedium() : nullptr) + { + uno::Reference< task::XInteractionHandler > xInteraction = pMedium->GetInteractionHandler(); + if ( xInteraction.is() ) + { + aMediaDescriptor.realloc( 2 ); + aMediaDescriptor[1].Name = "InteractionHandler"; + aMediaDescriptor[1].Value <<= xInteraction; + } + + const auto nLen = aMediaDescriptor.getLength() + 1; + aMediaDescriptor.realloc(nLen); + aMediaDescriptor[nLen - 1].Name = "Referer"; + aMediaDescriptor[nLen - 1].Value <<= pMedium->GetName(); + } + + uno::Reference < embed::XEmbeddedObject > xObj( + xFactory->createInstanceLink( + xStorage, "DummyName", aMediaDescriptor, uno::Sequence< beans::PropertyValue >() ), + uno::UNO_QUERY_THROW ); + + { + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet ); + + // TODO/LATER: in future may need a way to set replacement image url to the link ( may be even to the object ), needs oasis cws??? + + xPropSet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat), uno::UNO_QUERY); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order + } + } + } + catch ( uno::Exception& ) + { + } + + // TODO/LATER: should the rStyleName and rTableName be handled as for usual embedded object? + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertApplet( + const OUString &rName, + const OUString &rCode, + bool bMayScript, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + uno::Reference<XUnoTunnel> xCursorTunnel( GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSet aItemSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END>{} ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + SwApplet_Impl aAppletImpl ( aItemSet ); + + OUString sCodeBase; + if( !rHRef.isEmpty() ) + sCodeBase = GetXMLImport().GetAbsoluteReference( rHRef ); + + aAppletImpl.CreateApplet ( rCode, rName, bMayScript, sCodeBase, GetXMLImport().GetDocumentBase() ); + + // set the size of the applet + lcl_setObjectVisualArea( aAppletImpl.GetApplet(), + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(aAppletImpl.GetApplet(), embed::Aspects::MSOLE_CONTENT), + &aAppletImpl.GetItemSet()); + xPropSet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat), uno::UNO_QUERY); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(*static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + + return xPropSet; +} + +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertPlugin( + const OUString &rMimeType, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + uno::Reference < XPropertySet > xPropSet; + uno::Reference<XUnoTunnel> xCursorTunnel( GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSet aItemSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END>{} ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + // We'll need a (valid) URL, or we need a MIME type. If we don't have + // either, do not insert plugin and return early. Copy URL into URL object + // on the way. + INetURLObject aURLObj; + + bool bValidURL = !rHRef.isEmpty() && + aURLObj.SetURL( URIHelper::SmartRel2Abs( INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ); + bool bValidMimeType = !rMimeType.isEmpty(); + if( !bValidURL && !bValidMimeType ) + return xPropSet; + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_PLUGIN_CLASSID ).GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", + uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY ); + + // set size to the object + lcl_setObjectVisualArea( xObj, + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + if( bValidURL ) + xSet->setPropertyValue("PluginURL", + makeAny( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ); + if( bValidMimeType ) + xSet->setPropertyValue("PluginMimeType", + makeAny( rMimeType ) ); + } + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + xPropSet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat), uno::UNO_QUERY); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); // req for z-order + } + } + } + catch ( uno::Exception& ) + { + } + + return xPropSet; +} +uno::Reference< XPropertySet > SwXMLTextImportHelper::createAndInsertFloatingFrame( + const OUString& rName, + const OUString& rHRef, + const OUString& rStyleName, + sal_Int32 nWidth, sal_Int32 nHeight ) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference < XPropertySet > xPropSet; + uno::Reference<XUnoTunnel> xCursorTunnel( GetCursor(), UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for Cursor"); + OTextCursorHelper *pTextCursor = reinterpret_cast< OTextCursorHelper * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ))); + OSL_ENSURE( pTextCursor, "SwXTextCursor missing" ); + SwDoc *pDoc = pTextCursor->GetDoc(); + + SfxItemSet aItemSet( pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, + RES_FRMATR_END>{} ); + lcl_putHeightAndWidth( aItemSet, nHeight, nWidth); + + ScrollingMode eScrollMode = ScrollingMode::Auto; + bool bHasBorder = false; + bool bIsBorderSet = false; + Size aMargin( SIZE_NOT_SET, SIZE_NOT_SET ); + const XMLPropStyleContext *pStyle = nullptr; + if( !rStyleName.isEmpty() ) + { + pStyle = FindAutoFrameStyle( rStyleName ); + if( pStyle ) + { + rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap = + pStyle->GetStyles() + ->GetImportPropertyMapper(pStyle->GetFamily()); + OSL_ENSURE( xImpPrMap.is(), "Where is the import prop mapper?" ); + if( xImpPrMap.is() ) + { + rtl::Reference<XMLPropertySetMapper> rPropMapper = + xImpPrMap->getPropertySetMapper(); + + sal_Int32 nCount = pStyle->GetProperties().size(); + for( sal_Int32 i=0; i < nCount; i++ ) + { + const XMLPropertyState& rProp = pStyle->GetProperties()[i]; + sal_Int32 nIdx = rProp.mnIndex; + if( -1 == nIdx ) + continue; + + switch( rPropMapper->GetEntryContextId(nIdx) ) + { + case CTF_FRAME_DISPLAY_SCROLLBAR: + { + bool bYes = *o3tl::doAccess<bool>(rProp.maValue); + eScrollMode = bYes ? ScrollingMode::Yes : ScrollingMode::No; + } + break; + case CTF_FRAME_DISPLAY_BORDER: + { + bHasBorder = *o3tl::doAccess<bool>(rProp.maValue); + bIsBorderSet = true; + } + break; + case CTF_FRAME_MARGIN_HORI: + { + sal_Int32 nVal = SIZE_NOT_SET; + rProp.maValue >>= nVal; + aMargin.setWidth( nVal ); + } + break; + case CTF_FRAME_MARGIN_VERT: + { + sal_Int32 nVal = SIZE_NOT_SET; + rProp.maValue >>= nVal; + aMargin.setHeight( nVal ); + } + break; + } + } + } + } + } + + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + try + { + // create object with desired ClassId + uno::Sequence < sal_Int8 > aClass( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence() ); + uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); + uno::Reference < embed::XEmbeddedObject > xObj( xFactory->createInstanceInitNew( + aClass, OUString(), xStorage, "DummyName", + uno::Sequence < beans::PropertyValue >() ), uno::UNO_QUERY ); + + // set size to the object + lcl_setObjectVisualArea( xObj, + embed::Aspects::MSOLE_CONTENT, + Size( nWidth, nHeight ), + MapUnit::Map100thMM ); + + if ( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + xSet->setPropertyValue("FrameURL", + makeAny( URIHelper::SmartRel2Abs( + INetURLObject( GetXMLImport().GetBaseURL() ), rHRef ) ) ); + + xSet->setPropertyValue("FrameName", + makeAny( rName ) ); + + if ( eScrollMode == ScrollingMode::Auto ) + xSet->setPropertyValue("FrameIsAutoScroll", + makeAny( true ) ); + else + xSet->setPropertyValue("FrameIsScrollingMode", + makeAny( eScrollMode == ScrollingMode::Yes ) ); + + if ( bIsBorderSet ) + xSet->setPropertyValue("FrameIsBorder", + makeAny( bHasBorder ) ); + else + xSet->setPropertyValue("FrameIsAutoBorder", + makeAny( true ) ); + + xSet->setPropertyValue("FrameMarginWidth", + makeAny( sal_Int32( aMargin.Width() ) ) ); + + xSet->setPropertyValue("FrameMarginHeight", + makeAny( sal_Int32( aMargin.Height() ) ) ); + } + + SwFrameFormat *const pFrameFormat = + pDoc->getIDocumentContentOperations().InsertEmbObject( + *pTextCursor->GetPaM(), + ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT), + &aItemSet); + xPropSet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pDoc, pFrameFormat), uno::UNO_QUERY); + if( pDoc->getIDocumentDrawModelAccess().GetDrawModel() ) + { + // req for z-order + SwXFrame::GetOrCreateSdrObject(* + static_cast<SwFlyFrameFormat*>(pFrameFormat)); + } + } + } + catch ( uno::Exception& ) + { + } + + return xPropSet; +} + +void SwXMLTextImportHelper::endAppletOrPlugin( + const uno::Reference < XPropertySet > &rPropSet, + std::map < const OUString, OUString > &rParamMap) +{ + // this method will modify the document directly -> lock SolarMutex + SolarMutexGuard aGuard; + + uno::Reference<XUnoTunnel> xCursorTunnel( rPropSet, UNO_QUERY ); + assert(xCursorTunnel.is() && "missing XUnoTunnel for embedded"); + SwXFrame *pFrame = reinterpret_cast< SwXFrame * >( + sal::static_int_cast< sal_IntPtr >( xCursorTunnel->getSomething( SwXFrame::getUnoTunnelId() ))); + OSL_ENSURE( pFrame, "SwXFrame missing" ); + SwFrameFormat *pFrameFormat = pFrame->GetFrameFormat(); + const SwFormatContent& rContent = pFrameFormat->GetContent(); + const SwNodeIndex *pNdIdx = rContent.GetContentIdx(); + SwOLENode *pOLENd = pNdIdx->GetNodes()[pNdIdx->GetIndex() + 1]->GetNoTextNode()->GetOLENode(); + SwOLEObj& rOLEObj = pOLENd->GetOLEObj(); + + uno::Reference < embed::XEmbeddedObject > xEmbObj( rOLEObj.GetOleRef() ); + if ( svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xEmbObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + const sal_Int32 nCount = rParamMap.size(); + uno::Sequence< beans::PropertyValue > aCommandSequence( nCount ); + + sal_Int32 nIndex=0; + for (const auto& rParam : rParamMap ) + { + aCommandSequence[nIndex].Name = rParam.first; + aCommandSequence[nIndex].Handle = -1; + aCommandSequence[nIndex].Value <<= rParam.second; + aCommandSequence[nIndex].State = beans::PropertyState_DIRECT_VALUE; + ++nIndex; + } + + // unfortunately the names of the properties are depending on the object + OUString aParaName("AppletCommands"); + try + { + xSet->setPropertyValue( aParaName, makeAny( aCommandSequence ) ); + } + catch ( uno::Exception& ) + { + aParaName = "PluginCommands"; + try + { + xSet->setPropertyValue( aParaName, makeAny( aCommandSequence ) ); + } + catch ( uno::Exception& ) + { + } + } + } + } +} + +// redlining helper methods +// (override to provide the real implementation) +void SwXMLTextImportHelper::RedlineAdd( + const OUString& rType, + const OUString& rId, + const OUString& rAuthor, + const OUString& rComment, + const util::DateTime& rDateTime, + bool bMergeLastPara) +{ + // create redline helper on demand + OSL_ENSURE(nullptr != pRedlineHelper, "helper should have been created in constructor"); + if (nullptr != pRedlineHelper) + pRedlineHelper->Add(rType, rId, rAuthor, rComment, rDateTime, + bMergeLastPara); +} + +uno::Reference<XTextCursor> SwXMLTextImportHelper::RedlineCreateText( + uno::Reference<XTextCursor> & rOldCursor, + const OUString& rId) +{ + uno::Reference<XTextCursor> xRet; + + if (nullptr != pRedlineHelper) + { + xRet = pRedlineHelper->CreateRedlineTextSection(rOldCursor, rId); + } + + return xRet; +} + +void SwXMLTextImportHelper::RedlineSetCursor( + const OUString& rId, + bool bStart, + bool bIsOutsideOfParagraph) +{ + if (nullptr != pRedlineHelper) { + uno::Reference<XTextRange> xTextRange( GetCursor()->getStart() ); + pRedlineHelper->SetCursor(rId, bStart, xTextRange, + bIsOutsideOfParagraph); + } + // else: ignore redline (wasn't added before, else we'd have a helper) +} + +void SwXMLTextImportHelper::RedlineAdjustStartNodeCursor() +{ + OUString rId = GetOpenRedlineId(); + if ((nullptr != pRedlineHelper) && !rId.isEmpty()) + { + pRedlineHelper->AdjustStartNodeCursor(rId); + ResetOpenRedlineId(); + } + // else: ignore redline (wasn't added before, or no open redline ID +} + +void SwXMLTextImportHelper::SetShowChanges( bool bShowChanges ) +{ + if ( nullptr != pRedlineHelper ) + pRedlineHelper->SetShowChanges( bShowChanges ); +} + +void SwXMLTextImportHelper::SetRecordChanges( bool bRecordChanges ) +{ + if ( nullptr != pRedlineHelper ) + pRedlineHelper->SetRecordChanges( bRecordChanges ); +} + +void SwXMLTextImportHelper::SetChangesProtectionKey( + const Sequence<sal_Int8> & rKey ) +{ + if ( nullptr != pRedlineHelper ) + pRedlineHelper->SetProtectionKey( rKey ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/xml/xmltexti.hxx b/sw/source/filter/xml/xmltexti.hxx new file mode 100644 index 000000000..1aefcfe64 --- /dev/null +++ b/sw/source/filter/xml/xmltexti.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_FILTER_XML_XMLTEXTI_HXX +#define INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX + +#include <xmloff/txtimp.hxx> + +class XMLRedlineImportHelper; +class SvXMLImport; + +class SwXMLTextImportHelper : public XMLTextImportHelper +{ + XMLRedlineImportHelper *pRedlineHelper; + +protected: + virtual SvXMLImportContext *CreateTableChildContext( + SvXMLImport& rImport, + sal_uInt16 nPrefix, const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList > & xAttrList ) override; + +public: + SwXMLTextImportHelper( + const css::uno::Reference<css::frame::XModel>& rModel, + SvXMLImport& rImport, + const css::uno::Reference<css::beans::XPropertySet>& rInfoSet, + bool bInsertM, bool bStylesOnlyM, + bool bBlockM, bool bOrganizerM ); + virtual ~SwXMLTextImportHelper() override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertOLEObject( SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertOOoLink( SvXMLImport& rImport, + const OUString& rHRef, + const OUString& rStyleName, + const OUString& rTableName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertApplet( + const OUString &rName, + const OUString &rCode, + bool bMayScript, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertPlugin( + const OUString &rMimeType, + const OUString& rHRef, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual css::uno::Reference<css::beans::XPropertySet> + createAndInsertFloatingFrame( + const OUString &rName, + const OUString &rHRef, + const OUString &rStyleName, + sal_Int32 nWidth, sal_Int32 nHeight ) override; + + virtual void endAppletOrPlugin( + const css::uno::Reference < css::beans::XPropertySet > &rPropSet, + std::map < const OUString, OUString > &rParamMap) override; + + virtual bool IsInHeaderFooter() const override; + + // redlining helper methods + // (here is the real implementation) + virtual void RedlineAdd( + const OUString& rType, /// redline type (insert, del,... ) + const OUString& rId, /// use to identify this redline + const OUString& rAuthor, /// name of the author + const OUString& rComment, /// redline comment + const css::util::DateTime& rDateTime, /// date+time + bool bMergeLastPara) override; /// merge last paragraph + virtual css::uno::Reference<css::text::XTextCursor> RedlineCreateText( + css::uno::Reference<css::text::XTextCursor> & rOldCursor, /// needed to get the document + const OUString& rId) override; /// ID used to RedlineAdd() call + virtual void RedlineSetCursor( + const OUString& rId, /// ID used to RedlineAdd() call + bool bStart, /// start or end Cursor + bool bIsOutsideOfParagraph) override; + virtual void RedlineAdjustStartNodeCursor() override; + virtual void SetShowChanges( bool bShowChanges ) override; + virtual void SetRecordChanges( bool bRecordChanges ) override; + virtual void SetChangesProtectionKey( + const css::uno::Sequence<sal_Int8> & rKey ) override; +}; + +#endif // INCLUDED_SW_SOURCE_FILTER_XML_XMLTEXTI_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/break.cxx b/sw/source/ui/chrdlg/break.cxx new file mode 100644 index 000000000..80f0d19fd --- /dev/null +++ b/sw/source/ui/chrdlg/break.cxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <uitool.hxx> +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <break.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> + +#include <strings.hrc> +#include <SwStyleNameMapper.hxx> + +short SwBreakDlg::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + { + nKind = 0; + if (m_xLineBtn->get_active()) + nKind = 1; + else if(m_xColumnBtn->get_active()) + nKind = 2; + else if(m_xPageBtn->get_active()) + { + nKind = 3; + const int nPos = m_xPageCollBox->get_active(); + if (nPos != 0 && nPos != -1) + { + m_aTemplate = m_xPageCollBox->get_active_text(); + oPgNum.reset(); + if (m_xPageNumBox->get_active()) + { + oPgNum = static_cast<sal_uInt16>(m_xPageNumEdit->get_value()); + } + } + } + } + return nRet; +} + +IMPL_LINK_NOARG(SwBreakDlg, ToggleHdl, weld::ToggleButton&, void) +{ + CheckEnable(); +} + +IMPL_LINK_NOARG(SwBreakDlg, ChangeHdl, weld::ComboBox&, void) +{ + CheckEnable(); +} + +// Handler for Change Page Number +IMPL_LINK(SwBreakDlg, PageNumHdl, weld::ToggleButton&, rBox, void) +{ + if (rBox.get_active()) + m_xPageNumEdit->set_value(1); + else + m_xPageNumEdit->set_text(OUString()); +} + +// By changing the Page number the checkbox is checked. +IMPL_LINK_NOARG(SwBreakDlg, PageNumModifyHdl, weld::SpinButton&, void) +{ + m_xPageNumBox->set_active(true); +} + +/* + * Ok-Handler; + * checks whether pagenumber nPage is a legal pagenumber (left pages with even + * numbers etc. for a page template with alternating pages) + */ +IMPL_LINK_NOARG(SwBreakDlg, OkHdl, weld::Button&, void) +{ + if (m_xPageNumBox->get_active()) + { + // In case of differing page descriptions, test validity + const int nPos = m_xPageCollBox->get_active(); + // position 0 says 'Without'. + const SwPageDesc *pPageDesc; + if (nPos != 0 && nPos != -1) + pPageDesc = rSh.FindPageDescByName(m_xPageCollBox->get_active_text(), true); + else + pPageDesc = &rSh.GetPageDesc(rSh.GetCurPageDesc()); + + OSL_ENSURE(pPageDesc, "Page description not found."); + const sal_uInt16 nUserPage = sal_uInt16(m_xPageNumEdit->get_value()); + bool bOk = true; + switch(pPageDesc->GetUseOn()) + { + case UseOnPage::Mirror: + case UseOnPage::All: break; + case UseOnPage::Left: bOk = 0 == nUserPage % 2; break; + case UseOnPage::Right: bOk = 1 == nUserPage % 2; break; + default:; //prevent warning + } + if(!bOk) + { + std::unique_ptr<weld::Dialog> xDialog(Application::CreateMessageDialog(m_xPageNumEdit.get(), VclMessageType::Info, + VclButtonsType::Ok, SwResId(STR_ILLEGAL_PAGENUM))); + xDialog->run(); + m_xPageNumEdit->grab_focus(); + return; + } + } + m_xDialog->response(RET_OK); +} + +SwBreakDlg::SwBreakDlg(weld::Window *pParent, SwWrtShell &rS) + : GenericDialogController(pParent, "modules/swriter/ui/insertbreak.ui", "BreakDialog") + , m_xLineBtn(m_xBuilder->weld_radio_button("linerb")) + , m_xColumnBtn(m_xBuilder->weld_radio_button("columnrb")) + , m_xPageBtn(m_xBuilder->weld_radio_button("pagerb")) + , m_xPageCollText(m_xBuilder->weld_label("styleft")) + , m_xPageCollBox(m_xBuilder->weld_combo_box("stylelb")) + , m_xPageNumBox(m_xBuilder->weld_check_button("pagenumcb")) + , m_xPageNumEdit(m_xBuilder->weld_spin_button("pagenumsb")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , rSh(rS) + , nKind(0) + , bHtmlMode(0 != ::GetHtmlMode(rS.GetView().GetDocShell())) +{ + Link<weld::ToggleButton&,void> aLk = LINK(this, SwBreakDlg, ToggleHdl); + m_xPageBtn->connect_toggled(aLk); + m_xLineBtn->connect_toggled(aLk); + m_xColumnBtn->connect_toggled(aLk); + m_xPageCollBox->connect_changed(LINK(this, SwBreakDlg, ChangeHdl)); + + m_xOkBtn->connect_clicked(LINK(this, SwBreakDlg, OkHdl)); + m_xPageNumBox->connect_toggled(LINK(this, SwBreakDlg, PageNumHdl)); + m_xPageNumEdit->connect_value_changed(LINK(this, SwBreakDlg, PageNumModifyHdl)); + + // Insert page description to Listbox + const size_t nCount = rSh.GetPageDescCnt(); + for (size_t i = 0; i < nCount; ++i) + { + const SwPageDesc &rPageDesc = rSh.GetPageDesc(i); + ::InsertStringSorted("", rPageDesc.GetName(), *m_xPageCollBox, 1 ); + } + + OUString aFormatName; + for (sal_uInt16 i = RES_POOLPAGE_BEGIN; i < RES_POOLPAGE_END; ++i) + { + aFormatName = SwStyleNameMapper::GetUIName( i, aFormatName ); + if (m_xPageCollBox->find_text(aFormatName) == -1) + ::InsertStringSorted("", aFormatName, *m_xPageCollBox, 1 ); + } + //add landscape page + aFormatName = SwStyleNameMapper::GetUIName( RES_POOLPAGE_LANDSCAPE, aFormatName ); + if (m_xPageCollBox->find_text(aFormatName) == -1) + ::InsertStringSorted("", aFormatName, *m_xPageCollBox, 1); + CheckEnable(); + m_xPageNumEdit->set_text(OUString()); +} + +void SwBreakDlg::CheckEnable() +{ + bool bEnable = true; + if ( bHtmlMode ) + { + m_xColumnBtn->set_sensitive(false); + m_xPageCollBox->set_sensitive(false); + bEnable = false; + } + else if(rSh.GetFrameType(nullptr,true) + & (FrameTypeFlags::FLY_ANY | FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE)) + { + m_xPageBtn->set_sensitive(false); + if (m_xPageBtn->get_active()) + m_xLineBtn->set_active(true); + bEnable = false; + } + const bool bPage = m_xPageBtn->get_active(); + m_xPageCollText->set_sensitive(bPage); + m_xPageCollBox->set_sensitive(bPage); + + bEnable &= bPage; + if ( bEnable ) + { + // position 0 says 'Without' page template. + const int nPos = m_xPageCollBox->get_active(); + if (nPos == 0 || nPos == -1) + bEnable = false; + } + m_xPageNumBox->set_sensitive(bEnable); + m_xPageNumEdit->set_sensitive(bEnable); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/chardlg.cxx b/sw/source/ui/chrdlg/chardlg.cxx new file mode 100644 index 000000000..5e89cfdaa --- /dev/null +++ b/sw/source/ui/chrdlg/chardlg.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 <memory> +#include <hintids.hxx> + +#include <comphelper/fileurl.hxx> +#include <svl/urihelper.hxx> +#include <svl/stritem.hxx> +#include <editeng/flstitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/macitem.hxx> + +#include <cmdid.h> +#include <swtypes.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <uitool.hxx> +#include <fmtinfmt.hxx> +#include <macassgn.hxx> +#include <chrdlg.hxx> +#include <swmodule.hxx> +#include <poolfmt.hxx> + +#include <strings.hrc> +#include <chrdlgmodes.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <SwStyleNameMapper.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/frame.hxx> + +#include <svx/svxdlg.hxx> +#include <svx/flagsdef.hxx> +#include <svx/dialogs.hrc> + +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::sfx2; + +SwCharDlg::SwCharDlg(weld::Window* pParent, SwView& rVw, const SfxItemSet& rCoreSet, + SwCharDlgMode nDialogMode, const OUString* pStr) + : SfxTabDialogController(pParent, "modules/swriter/ui/characterproperties.ui", + "CharacterPropertiesDialog", &rCoreSet, pStr != nullptr) + , m_rView(rVw) + , m_nDialogMode(nDialogMode) +{ + if (pStr) + { + m_xDialog->set_title(m_xDialog->get_title() + SwResId(STR_TEXTCOLL_HEADER) + *pStr + ")"); + } + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + AddTabPage("font", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_NAME), nullptr); + AddTabPage("fonteffects", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_EFFECTS), nullptr); + AddTabPage("position", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_POSITION ), nullptr ); + AddTabPage("asianlayout", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_TWOLINES ), nullptr ); + AddTabPage("hyperlink", SwCharURLPage::Create, nullptr); + AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr ); + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), nullptr ); + + if (m_nDialogMode == SwCharDlgMode::Draw || m_nDialogMode == SwCharDlgMode::Ann) + { + RemoveTabPage("hyperlink"); + RemoveTabPage("asianlayout"); + } + else + { + SvtCJKOptions aCJKOptions; + if (!aCJKOptions.IsDoubleLinesEnabled()) + RemoveTabPage("asianlayout"); + } + + if (m_nDialogMode != SwCharDlgMode::Std) + RemoveTabPage("borders"); +} + +SwCharDlg::~SwCharDlg() +{ +} + +// set FontList +void SwCharDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "font") + { + SvxFontListItem aFontListItem( *static_cast<const SvxFontListItem*>( + ( m_rView.GetDocShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) ) ) ); + aSet.Put (SvxFontListItem( aFontListItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + if(m_nDialogMode != SwCharDlgMode::Draw && m_nDialogMode != SwCharDlgMode::Ann) + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE,SVX_PREVIEW_CHARACTER)); + rPage.PageCreated(aSet); + } + else if (rId == "fonteffects") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE, SVX_PREVIEW_CHARACTER | SVX_ENABLE_FLASH + | SVX_ENABLE_CHAR_TRANSPARENCY)); + rPage.PageCreated(aSet); + } + else if (rId == "position") + { + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE,SVX_PREVIEW_CHARACTER)); + rPage.PageCreated(aSet); + } + else if (rId == "asianlayout") + { + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE,SVX_PREVIEW_CHARACTER)); + rPage.PageCreated(aSet); + } + else if (rId == "background") + { + SvxBackgroundTabFlags eFlags(SvxBackgroundTabFlags::SHOW_HIGHLIGHTING); + if (m_nDialogMode == SwCharDlgMode::Draw || m_nDialogMode == SwCharDlgMode::Ann) + eFlags = SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR; + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE,static_cast<sal_uInt32>(eFlags))); + rPage.PageCreated(aSet); + } +} + +SwCharURLPage::SwCharURLPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/charurlpage.ui", "CharURLPage", &rCoreSet) + , bModified(false) + , m_xURLED(m_xBuilder->weld_entry("urled")) + , m_xTextFT(m_xBuilder->weld_label("textft")) + , m_xTextED(m_xBuilder->weld_entry("texted")) + , m_xNameED(m_xBuilder->weld_entry("nameed")) + , m_xTargetFrameLB(m_xBuilder->weld_combo_box("targetfrmlb")) + , m_xURLPB(m_xBuilder->weld_button("urlpb")) + , m_xEventPB(m_xBuilder->weld_button("eventpb")) + , m_xVisitedLB(m_xBuilder->weld_combo_box("visitedlb")) + , m_xNotVisitedLB(m_xBuilder->weld_combo_box("unvisitedlb")) + , m_xCharStyleContainer(m_xBuilder->weld_widget("charstyle")) +{ + // tdf#120188 like SfxManageStyleSheetPage limit the width of the style combos + const int nMaxWidth(m_xVisitedLB->get_approximate_digit_width() * 50); + m_xVisitedLB->set_size_request(nMaxWidth , -1); + m_xNotVisitedLB->set_size_request(nMaxWidth , -1); + + const SfxPoolItem* pItem; + SfxObjectShell* pShell; + if(SfxItemState::SET == rCoreSet.GetItemState(SID_HTML_MODE, false, &pItem) || + ( nullptr != ( pShell = SfxObjectShell::Current()) && + nullptr != (pItem = pShell->GetItem(SID_HTML_MODE)))) + { + sal_uInt16 nHtmlMode = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + if (HTMLMODE_ON & nHtmlMode) + m_xCharStyleContainer->hide(); + } + + m_xURLPB->connect_clicked(LINK( this, SwCharURLPage, InsertFileHdl)); + m_xEventPB->connect_clicked(LINK( this, SwCharURLPage, EventHdl)); + + SwView *pView = ::GetActiveView(); + ::FillCharStyleListBox(*m_xVisitedLB, pView->GetDocShell()); + ::FillCharStyleListBox(*m_xNotVisitedLB, pView->GetDocShell()); + m_xVisitedLB->set_active_id(OUString::number(RES_POOLCHR_INET_VISIT)); + m_xVisitedLB->save_value(); + m_xNotVisitedLB->set_active_id(OUString::number(RES_POOLCHR_INET_NORMAL)); + m_xNotVisitedLB->save_value(); + + std::unique_ptr<TargetList> pList( new TargetList ); + SfxFrame::GetDefaultTargetList(*pList); + + m_xTargetFrameLB->freeze(); + size_t nCount = pList->size(); + for (size_t i = 0; i < nCount; ++i) + { + m_xTargetFrameLB->append_text(pList->at(i)); + } + m_xTargetFrameLB->thaw(); +} + +SwCharURLPage::~SwCharURLPage() +{ + pINetItem.reset(); +} + +void SwCharURLPage::Reset(const SfxItemSet* rSet) +{ + const SfxPoolItem* pItem; + if (SfxItemState::SET == rSet->GetItemState(RES_TXTATR_INETFMT, false, &pItem)) + { + const SwFormatINetFormat* pINetFormat = static_cast<const SwFormatINetFormat*>( pItem); + m_xURLED->set_text(INetURLObject::decode(pINetFormat->GetValue(), + INetURLObject::DecodeMechanism::Unambiguous)); + m_xURLED->save_value(); + m_xNameED->set_text(pINetFormat->GetName()); + m_xNameED->save_value(); + + OUString sEntry = pINetFormat->GetVisitedFormat(); + if (sEntry.isEmpty()) + { + OSL_ENSURE( false, "<SwCharURLPage::Reset(..)> - missing visited character format at hyperlink attribute" ); + SwStyleNameMapper::FillUIName(RES_POOLCHR_INET_VISIT, sEntry); + } + m_xVisitedLB->set_active_text(sEntry); + + sEntry = pINetFormat->GetINetFormat(); + if (sEntry.isEmpty()) + { + OSL_ENSURE( false, "<SwCharURLPage::Reset(..)> - missing unvisited character format at hyperlink attribute" ); + SwStyleNameMapper::FillUIName(RES_POOLCHR_INET_NORMAL, sEntry); + } + m_xNotVisitedLB->set_active_text(sEntry); + + m_xTargetFrameLB->set_entry_text(pINetFormat->GetTargetFrame()); + m_xVisitedLB->save_value(); + m_xNotVisitedLB->save_value(); + m_xTargetFrameLB->save_value(); + pINetItem.reset( new SvxMacroItem(FN_INET_FIELD_MACRO) ); + + if( pINetFormat->GetMacroTable() ) + pINetItem->SetMacroTable(*pINetFormat->GetMacroTable()); + } + if (SfxItemState::SET == rSet->GetItemState(FN_PARAM_SELECTION, false, &pItem)) + { + m_xTextED->set_text(static_cast<const SfxStringItem*>(pItem)->GetValue()); + m_xTextFT->set_sensitive(false); + m_xTextED->set_sensitive(false); + } +} + +bool SwCharURLPage::FillItemSet(SfxItemSet* rSet) +{ + OUString sURL = m_xURLED->get_text(); + if (!sURL.isEmpty()) + { + sURL = URIHelper::SmartRel2Abs(INetURLObject(), sURL, Link<OUString *, bool>(), false ); + // #i100683# file URLs should be normalized in the UI + if ( comphelper::isFileUrl(sURL) ) + sURL = URIHelper::simpleNormalizedMakeRelative(OUString(), sURL); + } + + SwFormatINetFormat aINetFormat(sURL, m_xTargetFrameLB->get_active_text()); + aINetFormat.SetName(m_xNameED->get_text()); + bool bURLModified = m_xURLED->get_value_changed_from_saved(); + bool bNameModified = m_xNameED->get_value_changed_from_saved(); + bool bTargetModified = m_xTargetFrameLB->get_value_changed_from_saved(); + bModified = bURLModified || bNameModified || bTargetModified; + + // set valid settings first + OUString sEntry = m_xVisitedLB->get_active_text(); + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( sEntry, SwGetPoolIdFromName::ChrFmt); + aINetFormat.SetVisitedFormatAndId( sEntry, nId ); + + sEntry = m_xNotVisitedLB->get_active_text(); + nId = SwStyleNameMapper::GetPoolIdFromUIName( sEntry, SwGetPoolIdFromName::ChrFmt); + aINetFormat.SetINetFormatAndId( sEntry, nId ); + + if (pINetItem && !pINetItem->GetMacroTable().empty()) + aINetFormat.SetMacroTable(&pINetItem->GetMacroTable()); + + if (m_xVisitedLB->get_value_changed_from_saved()) + bModified = true; + + if (m_xNotVisitedLB->get_value_changed_from_saved()) + bModified = true; + + if (bNameModified) + { + bModified = true; + rSet->Put(SfxStringItem(FN_PARAM_SELECTION, m_xTextED->get_text())); + } + if(bModified) + rSet->Put(aINetFormat); + return bModified; +} + +std::unique_ptr<SfxTabPage> SwCharURLPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwCharURLPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK_NOARG(SwCharURLPage, InsertFileHdl, weld::Button&, void) +{ + FileDialogHelper aDlgHelper(TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, GetFrameWeld()); + if( aDlgHelper.Execute() == ERRCODE_NONE ) + { + const Reference<XFilePicker3>& xFP = aDlgHelper.GetFilePicker(); + m_xURLED->set_text(xFP->getSelectedFiles().getConstArray()[0]); + } +} + +IMPL_LINK_NOARG(SwCharURLPage, EventHdl, weld::Button&, void) +{ + bModified |= SwMacroAssignDlg::INetFormatDlg(GetFrameWeld(), + ::GetActiveView()->GetWrtShell(), pINetItem); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/drpcps.cxx b/sw/source/ui/chrdlg/drpcps.cxx new file mode 100644 index 000000000..a02cd8dfe --- /dev/null +++ b/sw/source/ui/chrdlg/drpcps.cxx @@ -0,0 +1,742 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmdid.h> +#include <docsh.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <strings.hrc> + +#include <vcl/metric.hxx> +#include <vcl/settings.hxx> + +#include <rtl/ustrbuf.hxx> +#include <svl/stritem.hxx> +#include <editeng/fontitem.hxx> +#include <sfx2/dialoghelper.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/printer.hxx> +#include <svtools/unitconv.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <comphelper/processfactory.hxx> + +#include <charatr.hxx> +#include <viewopt.hxx> +#include <drpcps.hxx> +#include <paratr.hxx> +#include <uitool.hxx> +#include <charfmt.hxx> + +using namespace css; +using namespace css::uno; +using namespace css::lang; + +const sal_uInt16 SwDropCapsPage::aPageRg[] = { + RES_PARATR_DROP, RES_PARATR_DROP, + 0 +}; + +void SwDropCapsPict::SetText( const OUString& rT ) +{ + maText = rT; + UpdatePaintSettings(); +} + +void SwDropCapsPict::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aPrefSize(getParagraphPreviewOptimalSize(pDrawingArea->get_ref_device())); + pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height()); +} + +void SwDropCapsPict::Resize() +{ + CustomWidgetController::Resize(); + UpdatePaintSettings(); +} + +void SwDropCapsPict::SetLines( sal_uInt8 nL ) +{ + mnLines = nL; + UpdatePaintSettings(); +} + +void SwDropCapsPict::SetDistance( sal_uInt16 nD ) +{ + mnDistance = nD; + UpdatePaintSettings(); +} + +void SwDropCapsPict::SetValues( const OUString& rText, sal_uInt8 nLines, sal_uInt16 nDistance ) +{ + maText = rText; + mnLines = nLines; + mnDistance = nDistance; + + UpdatePaintSettings(); +} + +void SwDropCapsPict::InitPrinter() +{ + if( !mpPrinter ) + InitPrinter_(); +} + +// Create Default-String from character-count (A, AB, ABC, ...) +static OUString GetDefaultString(sal_Int32 nChars) +{ + OUStringBuffer aStr(nChars); + for (sal_Int32 i = 0; i < nChars; i++) + aStr.append(static_cast<sal_Unicode>(i + 65)); + return aStr.makeStringAndClear(); +} + +static void calcFontHeightAnyAscent(vcl::RenderContext& rWin, vcl::Font const & _rFont, long& _nHeight, long& _nAscent) +{ + if ( !_nHeight ) + { + rWin.Push(PushFlags::FONT); + rWin.SetFont(_rFont); + FontMetric aMetric(rWin.GetFontMetric()); + _nHeight = aMetric.GetLineHeight(); + _nAscent = aMetric.GetAscent(); + rWin.Pop(); + } +} + +SwDropCapsPict::~SwDropCapsPict() +{ + if (mbDelPrinter) + mpPrinter.disposeAndClear(); +} + +/// Get the details of the first script change. +/// @param[out] start The character position of the start of the segment. +/// @param[out] end The character position of the end of the segment. +/// @param[out] scriptType The script type (Latin, Asian, Complex etc.) +void SwDropCapsPict::GetFirstScriptSegment(sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType) +{ + start = 0; + if( maScriptChanges.empty() ) + { + end = maText.getLength(); + scriptType = css::i18n::ScriptType::LATIN; + } + else + { + end = maScriptChanges[ 0 ].changePos; + scriptType = maScriptChanges[ 0 ].scriptType; + } +} + +/// Get the details of the first script change. +/// @param[in,out] nIdx Index of the current script change. +/// @param[out] start The character position of the start of the segment. +/// @param[in,out] end The character position of the end of the segment. +/// @param[out] scriptType The script type (Latin, Asian, Complex etc.) +/// @returns True if there was a next segment, false if not. +bool SwDropCapsPict::GetNextScriptSegment(size_t &nIdx, sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType) +{ + if (maScriptChanges.empty() || nIdx >= maScriptChanges.size() - 1 || end >= maText.getLength()) + return false; + start = maScriptChanges[nIdx++].changePos; + end = maScriptChanges[ nIdx ].changePos; + scriptType = maScriptChanges[ nIdx ].scriptType; + return true; +} + +#define LINES 10 +#define BORDER 2 + +void SwDropCapsPict::GetFontSettings( const SwDropCapsPage& _rPage, vcl::Font& _rFont, sal_uInt16 _nWhich ) +{ + SfxItemSet aSet( _rPage.rSh.GetAttrPool(), {{_nWhich, _nWhich}}); + _rPage.rSh.GetCurAttr(aSet); + SvxFontItem aFormatFont(static_cast<const SvxFontItem &>( aSet.Get(_nWhich))); + + _rFont.SetFamily(aFormatFont.GetFamily()); + _rFont.SetFamilyName(aFormatFont.GetFamilyName()); + _rFont.SetPitch(aFormatFont.GetPitch()); + _rFont.SetCharSet(aFormatFont.GetCharSet()); +} + +void SwDropCapsPict::UpdatePaintSettings() +{ + maBackColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + maTextLineColor = COL_LIGHTGRAY; + + // gray lines + mnTotLineH = (GetOutputSizePixel().Height() - 2 * BORDER) / LINES; + mnLineH = mnTotLineH - 2; + + vcl::Font aFont; + if (mpPage) + { + if (!mpPage->m_xTemplateBox->get_active()) + { + // query the Font at paragraph's beginning + mpPage->rSh.Push(); + mpPage->rSh.SttCursorMove(); + mpPage->rSh.ClearMark(); + SwWhichPara pSwuifnParaCurr = GoCurrPara; + SwMoveFnCollection const & pSwuifnParaStart = fnParaStart; + mpPage->rSh.MovePara(pSwuifnParaCurr,pSwuifnParaStart); + // normal + GetFontSettings( *mpPage, aFont, RES_CHRATR_FONT ); + + // CJK + GetFontSettings( *mpPage, maCJKFont, RES_CHRATR_CJK_FONT ); + + // CTL + GetFontSettings( *mpPage, maCTLFont, RES_CHRATR_CTL_FONT ); + + mpPage->rSh.EndCursorMove(); + mpPage->rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + // query Font at character template + SwCharFormat *pFormat = mpPage->rSh.GetCharStyle( + mpPage->m_xTemplateBox->get_active_text(), + SwWrtShell::GETSTYLE_CREATEANY ); + OSL_ENSURE(pFormat, "character style doesn't exist!"); + const SvxFontItem &rFormatFont = pFormat->GetFont(); + + aFont.SetFamily(rFormatFont.GetFamily()); + aFont.SetFamilyName(rFormatFont.GetFamilyName()); + aFont.SetPitch(rFormatFont.GetPitch()); + aFont.SetCharSet(rFormatFont.GetCharSet()); + } + } + + mnTextH = mnLines * mnTotLineH; + aFont.SetFontSize(Size(0, mnTextH)); + maCJKFont.SetFontSize(Size(0, mnTextH)); + maCTLFont.SetFontSize(Size(0, mnTextH)); + + aFont.SetTransparent(true); + maCJKFont.SetTransparent(true); + maCTLFont.SetTransparent(true); + + aFont.SetColor( SwViewOption::GetFontColor() ); + maCJKFont.SetColor( SwViewOption::GetFontColor() ); + maCTLFont.SetColor( SwViewOption::GetFontColor() ); + + aFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + maCJKFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + maCTLFont.SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + + maCJKFont.SetFontSize(Size(0, maCJKFont.GetFontSize().Height())); + maCTLFont.SetFontSize(Size(0, maCTLFont.GetFontSize().Height())); + + aFont.SetFontSize(Size(0, aFont.GetFontSize().Height())); + maFont = aFont; + + CheckScript(); + + maTextSize = CalcTextSize(); + + Invalidate(); +} + +void SwDropCapsPict::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) +{ + if (!IsVisible()) + return; + + rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); + rRenderContext.SetLineColor(); + + rRenderContext.SetFillColor(maBackColor); + + Size aOutputSizePixel(GetOutputSizePixel()); + + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aOutputSizePixel)); + rRenderContext.SetClipRegion(vcl::Region(tools::Rectangle(Point(BORDER, BORDER), + Size(aOutputSizePixel.Width () - 2 * BORDER, + aOutputSizePixel.Height() - 2 * BORDER)))); + + OSL_ENSURE(mnLineH > 0, "We cannot make it that small"); + long nY0 = (aOutputSizePixel.Height() - (LINES * mnTotLineH)) / 2; + + rRenderContext.SetFillColor(maTextLineColor); + + for (int i = 0; i < LINES; ++i) + { + rRenderContext.DrawRect(tools::Rectangle(Point(BORDER, nY0 + i * mnTotLineH), + Size(aOutputSizePixel.Width() - 2 * BORDER, mnLineH))); + } + + // Text background with gap (240 twips ~ 1 line height) + const long nDistW = (((static_cast<long>(mnDistance) * 100) / 240) * mnTotLineH) / 100; + rRenderContext.SetFillColor(maBackColor); + if (mpPage && mpPage->m_xDropCapsBox->get_active()) + { + const Size aTextSize(maTextSize.Width() + nDistW, maTextSize.Height()); + rRenderContext.DrawRect(tools::Rectangle(Point(BORDER, nY0), aTextSize)); + + // draw Text + DrawPrev(rRenderContext, Point(BORDER, nY0)); + } + rRenderContext.SetClipRegion(); +} + +void SwDropCapsPict::DrawPrev(vcl::RenderContext& rRenderContext, const Point& rPt) +{ + Point aPt(rPt); + InitPrinter(); + + vcl::Font aOldFont = mpPrinter->GetFont(); + sal_uInt16 nScript; + size_t nIdx = 0; + sal_Int32 nStart; + sal_Int32 nEnd; + + GetFirstScriptSegment(nStart, nEnd, nScript); + + do + { + SvxFont& rFnt = (nScript == css::i18n::ScriptType::ASIAN) + ? maCJKFont + : ((nScript == css::i18n::ScriptType::COMPLEX) + ? maCTLFont + : maFont); + mpPrinter->SetFont(rFnt); + + rFnt.DrawPrev(&rRenderContext, mpPrinter, aPt, maText, nStart, nEnd - nStart); + + if (!maScriptChanges.empty()) + aPt.AdjustX(maScriptChanges[nIdx].textWidth ); + + if (!GetNextScriptSegment(nIdx, nStart, nEnd, nScript)) + break; + } + while(true); + + mpPrinter->SetFont(aOldFont); +} + +void SwDropCapsPict::CheckScript() +{ + if( maScriptText == maText ) + return; + + maScriptText = maText; + maScriptChanges.clear(); + if( !xBreak.is() ) + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + xBreak = css::i18n::BreakIterator::create(xContext); + } + sal_Int16 nScript = xBreak->getScriptType( maText, 0 ); + sal_Int32 nChg = 0; + if( css::i18n::ScriptType::WEAK == nScript ) + { + nChg = xBreak->endOfScript( maText, nChg, nScript ); + if( nChg < maText.getLength() ) + nScript = xBreak->getScriptType( maText, nChg ); + else + nScript = css::i18n::ScriptType::LATIN; + } + + for(;;) + { + nChg = xBreak->endOfScript( maText, nChg, nScript ); + maScriptChanges.emplace_back(nScript, nChg ); + if( nChg >= maText.getLength() || nChg < 0 ) + break; + nScript = xBreak->getScriptType( maText, nChg ); + } +} + +Size SwDropCapsPict::CalcTextSize() +{ + InitPrinter(); + + sal_uInt16 nScript; + size_t nIdx = 0; + sal_Int32 nStart; + sal_Int32 nEnd; + GetFirstScriptSegment(nStart, nEnd, nScript); + long nTextWidth = 0; + long nCJKHeight = 0; + long nCTLHeight = 0; + long nHeight = 0; + long nAscent = 0; + long nCJKAscent = 0; + long nCTLAscent = 0; + do + { + SvxFont& rFnt = (nScript == css::i18n::ScriptType::ASIAN) + ? maCJKFont + : ((nScript == css::i18n::ScriptType::COMPLEX) + ? maCTLFont + : maFont); + + sal_uLong nWidth = rFnt.GetTextSize(mpPrinter, maText, nStart, nEnd-nStart ).Width(); + + if (nIdx < maScriptChanges.size()) + maScriptChanges[nIdx].textWidth = nWidth; + nTextWidth += nWidth; + switch(nScript) + { + case css::i18n::ScriptType::ASIAN: + calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maCJKFont, nCJKHeight, nCJKAscent); + break; + case css::i18n::ScriptType::COMPLEX: + calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maCTLFont, nCTLHeight, nCTLAscent); + break; + default: + calcFontHeightAnyAscent(GetDrawingArea()->get_ref_device(), maFont, nHeight, nAscent); + } + + if (!GetNextScriptSegment(nIdx, nStart, nEnd, nScript)) + break; + } + while(true); + + nHeight -= nAscent; + nCJKHeight -= nCJKAscent; + nCTLHeight -= nCTLAscent; + if (nHeight < nCJKHeight) + nHeight = nCJKHeight; + if (nAscent < nCJKAscent) + nAscent = nCJKAscent; + if (nHeight < nCTLHeight) + nHeight = nCTLHeight; + if (nAscent < nCTLAscent) + nAscent = nCTLAscent; + nHeight += nAscent; + + Size aTextSize(nTextWidth, nHeight); + return aTextSize; +} + +void SwDropCapsPict::InitPrinter_() +{ + SfxViewShell* pSh = SfxViewShell::Current(); + + if (pSh) + mpPrinter = pSh->GetPrinter(); + + if (!mpPrinter) + { + mpPrinter = VclPtr<Printer>::Create(); + mbDelPrinter = true; + } +} + +SwDropCapsDlg::SwDropCapsDlg(weld::Window *pParent, const SfxItemSet &rSet) + : SfxSingleTabDialogController(pParent, &rSet) +{ + auto xNewPage(SwDropCapsPage::Create(get_content_area(), this, &rSet)); + static_cast<SwDropCapsPage*>(xNewPage.get())->SetFormat(false); + SetTabPage(std::move(xNewPage)); +} + +SwDropCapsPage::SwDropCapsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/dropcapspage.ui", "DropCapPage", &rSet) + , bModified(false) + , bFormat(true) + , rSh(::GetActiveView()->GetWrtShell()) + , m_xDropCapsBox(m_xBuilder->weld_check_button("checkCB_SWITCH")) + , m_xWholeWordCB(m_xBuilder->weld_check_button("checkCB_WORD")) + , m_xSwitchText(m_xBuilder->weld_label("labelFT_DROPCAPS")) + , m_xDropCapsField(m_xBuilder->weld_spin_button("spinFLD_DROPCAPS")) + , m_xLinesText(m_xBuilder->weld_label("labelTXT_LINES")) + , m_xLinesField(m_xBuilder->weld_spin_button("spinFLD_LINES")) + , m_xDistanceText(m_xBuilder->weld_label("labelTXT_DISTANCE")) + , m_xDistanceField(m_xBuilder->weld_metric_spin_button("spinFLD_DISTANCE", FieldUnit::CM)) + , m_xTextText(m_xBuilder->weld_label("labelTXT_TEXT")) + , m_xTextEdit(m_xBuilder->weld_entry("entryEDT_TEXT")) + , m_xTemplateText(m_xBuilder->weld_label("labelTXT_TEMPLATE")) + , m_xTemplateBox(m_xBuilder->weld_combo_box("comboBOX_TEMPLATE")) + , m_xPict(new weld::CustomWeld(*m_xBuilder, "drawingareaWN_EXAMPLE", m_aPict)) +{ + m_aPict.SetDropCapsPage(this); + + SetExchangeSupport(); + + const sal_uInt16 nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + bHtmlMode = (nHtmlMode & HTMLMODE_ON) != 0; + + // tdf#92154 limit comboBOX_TEMPLATE length + const int nMaxWidth(m_xTemplateBox->get_approximate_digit_width() * 50); + m_xTemplateBox->set_size_request(nMaxWidth , -1); + + // In the template dialog the text is not influenceable + m_xTextText->set_sensitive(!bFormat); + m_xTextEdit->set_sensitive(!bFormat); + + // Metrics + SetFieldUnit(*m_xDistanceField, GetDfltMetric(bHtmlMode)); + + // Install handler + Link<weld::SpinButton&,void> aValueChangedLk = LINK(this, SwDropCapsPage, ValueChangedHdl); + m_xDropCapsField->connect_value_changed(aValueChangedLk); + m_xLinesField->connect_value_changed(aValueChangedLk); + Link<weld::MetricSpinButton&,void> aMetricValueChangedLk = LINK(this, SwDropCapsPage, MetricValueChangedHdl); + m_xDistanceField->connect_value_changed(aMetricValueChangedLk); + m_xTextEdit->connect_changed(LINK(this, SwDropCapsPage, ModifyHdl)); + m_xDropCapsBox->connect_toggled(LINK(this, SwDropCapsPage, ClickHdl)); + m_xTemplateBox->connect_changed(LINK(this, SwDropCapsPage, SelectHdl)); + m_xWholeWordCB->connect_toggled(LINK(this, SwDropCapsPage, WholeWordHdl)); +} + +SwDropCapsPage::~SwDropCapsPage() +{ +} + +DeactivateRC SwDropCapsPage::DeactivatePage(SfxItemSet * _pSet) +{ + if (_pSet) + FillSet(*_pSet); + + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SwDropCapsPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwDropCapsPage>(pPage, pController, *rSet); +} + +bool SwDropCapsPage::FillItemSet(SfxItemSet *rSet) +{ + if (bModified) + FillSet(*rSet); + return bModified; +} + +void SwDropCapsPage::Reset(const SfxItemSet *rSet) +{ + // Characters, lines, gap and text + SwFormatDrop aFormatDrop( rSet->Get(RES_PARATR_DROP) ); + if (aFormatDrop.GetLines() > 1) + { + m_xDropCapsField->set_value(aFormatDrop.GetChars()); + m_xLinesField->set_value(aFormatDrop.GetLines()); + m_xDistanceField->set_value(m_xDistanceField->normalize(aFormatDrop.GetDistance()), FieldUnit::TWIP); + m_xWholeWordCB->set_active(aFormatDrop.GetWholeWord()); + } + else + { + m_xDropCapsField->set_value(1); + m_xLinesField->set_value(3); + m_xDistanceField->set_value(0, FieldUnit::TWIP); + } + + ::FillCharStyleListBox(*m_xTemplateBox, rSh.GetView().GetDocShell(), true); + + m_xTemplateBox->insert_text(0, SwResId(SW_STR_NONE)); + + // Reset format + int nSelect = 0; + if (aFormatDrop.GetCharFormat()) + { + int nPos = m_xTemplateBox->find_text(aFormatDrop.GetCharFormat()->GetName()); + if (nPos != -1) + nSelect = nPos; + } + m_xTemplateBox->set_active(nSelect); + + // Enable controls + m_xDropCapsBox->set_active(aFormatDrop.GetLines() > 1); + const sal_Int32 nVal = m_xDropCapsField->get_value(); + if (bFormat) + m_xTextEdit->set_text(GetDefaultString(nVal)); + else + { + m_xTextEdit->set_text(rSh.GetDropText(nVal)); + m_xTextEdit->set_sensitive(true); + m_xTextText->set_sensitive(true); + } + + // Preview + m_aPict.SetValues(m_xTextEdit->get_text(), + sal_uInt8(m_xLinesField->get_value()), + sal_uInt16(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP)))); + + ClickHdl(*m_xDropCapsBox); + bModified = false; +} + +IMPL_LINK_NOARG(SwDropCapsPage, ClickHdl, weld::ToggleButton&, void) +{ + bool bChecked = m_xDropCapsBox->get_active(); + + m_xWholeWordCB->set_sensitive(bChecked && !bHtmlMode); + + m_xSwitchText->set_sensitive(bChecked && !m_xWholeWordCB->get_active()); + m_xDropCapsField->set_sensitive(bChecked && !m_xWholeWordCB->get_active()); + m_xLinesText->set_sensitive( bChecked ); + m_xLinesField->set_sensitive( bChecked ); + m_xDistanceText->set_sensitive( bChecked ); + m_xDistanceField->set_sensitive( bChecked ); + m_xTemplateText->set_sensitive( bChecked ); + m_xTemplateBox->set_sensitive( bChecked ); + m_xTextEdit->set_sensitive( bChecked && !bFormat ); + m_xTextText->set_sensitive( bChecked && !bFormat ); + + if ( bChecked ) + { + ValueChangedHdl(*m_xDropCapsField); + m_xDropCapsField->grab_focus(); + } + else + m_aPict.SetText(""); + + bModified = true; +} + +IMPL_LINK_NOARG(SwDropCapsPage, WholeWordHdl, weld::ToggleButton&, void) +{ + m_xDropCapsField->set_sensitive(!m_xWholeWordCB->get_active()); + m_xSwitchText->set_sensitive(!m_xWholeWordCB->get_active()); + + ValueChangedHdl(*m_xDropCapsField); + + bModified = true; +} + +void SwDropCapsPage::ModifyEntry(weld::Entry& rEdit) +{ + OUString sPreview; + + // set text if applicable + if (&rEdit == m_xDropCapsField.get()) + { + const sal_Int32 nVal = !m_xWholeWordCB->get_active() + ? static_cast<sal_Int32>(m_xDropCapsField->get_value()) + : 0; + bool bSetText = false; + + if (bFormat || rSh.GetDropText(1).isEmpty()) + sPreview = GetDefaultString(nVal); + else + { + bSetText = true; + sPreview = rSh.GetDropText(nVal); + } + + OUString sEdit(m_xTextEdit->get_text()); + + if (!sEdit.isEmpty() && !sPreview.startsWith(sEdit)) + { + sPreview = sEdit.copy(0, std::min(sEdit.getLength(), sPreview.getLength())); + bSetText = false; + } + + if (bSetText) + m_xTextEdit->set_text(sPreview); + } + else if (&rEdit == m_xTextEdit.get()) // set quantity if applicable + { + const sal_Int32 nTmp = m_xTextEdit->get_text().getLength(); + m_xDropCapsField->set_value(std::max<sal_Int32>(1, nTmp)); + sPreview = m_xTextEdit->get_text(); + } + + // adjust image + if (&rEdit == m_xDropCapsField.get() || &rEdit == m_xTextEdit.get()) + m_aPict.SetText(sPreview); + else if (&rEdit == m_xLinesField.get()) + m_aPict.SetLines(static_cast<sal_uInt8>(m_xLinesField->get_value())); + else + m_aPict.SetDistance(static_cast<sal_uInt16>(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP)))); + + bModified = true; +} + +IMPL_LINK(SwDropCapsPage, ModifyHdl, weld::Entry&, rEdit, void) +{ + ModifyEntry(rEdit); +} + +IMPL_LINK(SwDropCapsPage, ValueChangedHdl, weld::SpinButton&, rEdit, void) +{ + ModifyEntry(rEdit); +} + +IMPL_LINK(SwDropCapsPage, MetricValueChangedHdl, weld::MetricSpinButton&, rEdit, void) +{ + ModifyEntry(rEdit.get_widget()); +} + +IMPL_LINK_NOARG(SwDropCapsPage, SelectHdl, weld::ComboBox&, void) +{ + m_aPict.UpdatePaintSettings(); + bModified = true; +} + +void SwDropCapsPage::FillSet( SfxItemSet &rSet ) +{ + if(bModified) + { + SwFormatDrop aFormat; + + bool bOn = m_xDropCapsBox->get_active(); + if (bOn) + { + // quantity, lines, gap + aFormat.GetChars() = static_cast<sal_uInt8>(m_xDropCapsField->get_value()); + aFormat.GetLines() = static_cast<sal_uInt8>(m_xLinesField->get_value()); + aFormat.GetDistance() = static_cast<sal_uInt16>(m_xDistanceField->denormalize(m_xDistanceField->get_value(FieldUnit::TWIP))); + aFormat.GetWholeWord() = m_xWholeWordCB->get_active(); + + // template + if (m_xTemplateBox->get_active()) + aFormat.SetCharFormat(rSh.GetCharStyle(m_xTemplateBox->get_active_text())); + } + else + { + aFormat.GetChars() = 1; + aFormat.GetLines() = 1; + aFormat.GetDistance() = 0; + } + + // set attributes + const SfxPoolItem* pOldItem; + if (nullptr == (pOldItem = GetOldItem(rSet, FN_FORMAT_DROPCAPS)) || aFormat != *pOldItem) + rSet.Put(aFormat); + + // hard text formatting + // Bug 24974: in designer/template catalog this doesn't make sense!! + if (!bFormat && m_xDropCapsBox->get_active()) + { + OUString sText(m_xTextEdit->get_text()); + + if (!m_xWholeWordCB->get_active()) + { + sText = sText.copy(0, std::min<sal_Int32>(sText.getLength(), m_xDropCapsField->get_value())); + } + + SfxStringItem aStr(FN_PARAM_1, sText); + rSet.Put(aStr); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/numpara.cxx b/sw/source/ui/chrdlg/numpara.cxx new file mode 100644 index 000000000..17b436d51 --- /dev/null +++ b/sw/source/ui/chrdlg/numpara.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 <cmdid.h> +#include <swtypes.hxx> +#include <hintids.hxx> +#include <strings.hrc> +#include <sfx2/objsh.hxx> +#include <sfx2/htmlmode.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <fmtline.hxx> +#include <numpara.hxx> + +#include <sfx2/dispatch.hxx> +#include <sfx2/viewsh.hxx> + +const sal_uInt16 SwParagraphNumTabPage::aPageRg[] = { + FN_NUMBER_NEWSTART, FN_NUMBER_NEWSTART_AT, + 0 +}; + +SwParagraphNumTabPage::SwParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr) + : SfxTabPage(pPage, pController, "modules/swriter/ui/numparapage.ui", "NumParaPage", &rAttr) + , msOutlineNumbering(SwResId(STR_OUTLINE_NUMBERING )) + , bModified(false) + , bCurNumrule(false) + , m_xOutlineStartBX(m_xBuilder->weld_widget("boxOUTLINE")) + , m_xOutlineLvLB(m_xBuilder->weld_combo_box("comboLB_OUTLINE_LEVEL")) + , m_xNumberStyleBX(m_xBuilder->weld_widget("boxNUMBER_STYLE")) + , m_xNumberStyleLB(m_xBuilder->weld_combo_box("comboLB_NUMBER_STYLE")) + , m_xEditNumStyleBtn(m_xBuilder->weld_button("editnumstyle")) + , m_xNewStartCB(m_xBuilder->weld_check_button("checkCB_NEW_START")) + , m_xNewStartBX(m_xBuilder->weld_widget("boxNEW_START")) + , m_xNewStartNumberCB(m_xBuilder->weld_check_button("checkCB_NUMBER_NEW_START")) + , m_xNewStartNF(m_xBuilder->weld_spin_button("spinNF_NEW_START")) + , m_xCountParaFram(m_xBuilder->weld_widget("frameFL_COUNT_PARA")) + , m_xCountParaCB(m_xBuilder->weld_check_button("checkCB_COUNT_PARA")) + , m_xRestartParaCountCB(m_xBuilder->weld_check_button("checkCB_RESTART_PARACOUNT")) + , m_xRestartBX(m_xBuilder->weld_widget("boxRESTART_NO")) + , m_xRestartNF(m_xBuilder->weld_spin_button("spinNF_RESTART_PARA")) +{ + m_xNewStartCB->set_state(TRISTATE_FALSE); + m_xNewStartNumberCB->set_state(TRISTATE_FALSE); + m_xCountParaCB->set_state(TRISTATE_FALSE); + m_xRestartParaCountCB->set_state(TRISTATE_FALSE); + m_xEditNumStyleBtn->set_sensitive(false); + + const SfxPoolItem* pItem; + SfxObjectShell* pObjSh; + if(SfxItemState::SET == rAttr.GetItemState(SID_HTML_MODE, false, &pItem) || + ( nullptr != ( pObjSh = SfxObjectShell::Current()) && + nullptr != (pItem = pObjSh->GetItem(SID_HTML_MODE)))) + { + const sal_uInt16 nHtmlMode = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + + if (HTMLMODE_ON & nHtmlMode) + m_xCountParaFram->hide(); + } + + m_xNewStartCB->connect_toggled(LINK(this, SwParagraphNumTabPage, NewStartHdl_Impl)); + m_xNewStartNumberCB->connect_toggled(LINK(this, SwParagraphNumTabPage, NewStartHdl_Impl)); + m_xNumberStyleLB->connect_changed(LINK(this, SwParagraphNumTabPage, StyleHdl_Impl)); + m_xCountParaCB->connect_toggled(LINK(this, SwParagraphNumTabPage, LineCountHdl_Impl)); + m_xRestartParaCountCB->connect_toggled(LINK(this, SwParagraphNumTabPage, LineCountHdl_Impl)); + m_xNumberStyleLB->connect_changed(LINK(this, SwParagraphNumTabPage, EditNumStyleSelectHdl_Impl)); + m_xEditNumStyleBtn->connect_clicked(LINK(this, SwParagraphNumTabPage, EditNumStyleHdl_Impl)); +} + +SwParagraphNumTabPage::~SwParagraphNumTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwParagraphNumTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwParagraphNumTabPage>(pPage, pController, *rSet); +} + +bool SwParagraphNumTabPage::FillItemSet( SfxItemSet* rSet ) +{ + if (m_xOutlineLvLB->get_value_changed_from_saved()) + { + const sal_uInt16 aOutlineLv = m_xOutlineLvLB->get_active(); + const SfxUInt16Item* pOldOutlineLv = static_cast<const SfxUInt16Item*>(GetOldItem( *rSet, SID_ATTR_PARA_OUTLINE_LEVEL)); + if (pOldOutlineLv) + { + std::unique_ptr<SfxUInt16Item> pOutlineLv(pOldOutlineLv->Clone()); + pOutlineLv->SetValue( aOutlineLv ); + rSet->Put(std::move(pOutlineLv)); + bModified = true; + } + } + + if (m_xNumberStyleLB->get_value_changed_from_saved()) + { + OUString aStyle; + if (m_xNumberStyleLB->get_active()) + aStyle = m_xNumberStyleLB->get_active_text(); + const SfxStringItem* pOldRule = static_cast<const SfxStringItem*>(GetOldItem( *rSet, SID_ATTR_PARA_NUMRULE)); + if (pOldRule) + { + std::unique_ptr<SfxStringItem> pRule(pOldRule->Clone()); + pRule->SetValue(aStyle); + rSet->Put(std::move(pRule)); + bModified = true; + } + } + if (m_xNewStartCB->get_state_changed_from_saved() || + m_xNewStartNumberCB->get_state_changed_from_saved()|| + m_xNewStartNF->get_value_changed_from_saved()) + { + bModified = true; + bool bNewStartChecked = TRISTATE_TRUE == m_xNewStartCB->get_state(); + bool bNumberNewStartChecked = TRISTATE_TRUE == m_xNewStartNumberCB->get_state(); + rSet->Put(SfxBoolItem(FN_NUMBER_NEWSTART, bNewStartChecked)); + rSet->Put(SfxUInt16Item(FN_NUMBER_NEWSTART_AT, + bNumberNewStartChecked && bNewStartChecked ? static_cast<sal_uInt16>(m_xNewStartNF->get_value()) : USHRT_MAX)); + } + + if (m_xCountParaCB->get_state_changed_from_saved()|| + m_xRestartParaCountCB->get_state_changed_from_saved() || + m_xRestartNF->get_value_changed_from_saved()) + { + SwFormatLineNumber aFormat; + aFormat.SetStartValue( static_cast< sal_uLong >(m_xRestartParaCountCB->get_state() == TRISTATE_TRUE ? + m_xRestartNF->get_value() : 0 )); + aFormat.SetCountLines(m_xCountParaCB->get_active()); + rSet->Put(aFormat); + bModified = true; + } + return bModified; +} + +void SwParagraphNumTabPage::ChangesApplied() +{ + m_xOutlineLvLB->save_value(); + m_xNumberStyleLB->save_value(); + m_xNewStartCB->save_state(); + m_xNewStartNumberCB->save_state(); + m_xCountParaCB->save_state(); + m_xRestartParaCountCB->save_state(); + m_xRestartNF->save_value(); +} + +void SwParagraphNumTabPage::Reset(const SfxItemSet* rSet) +{ + bool bHasNumberStyle = false; + + SfxItemState eItemState = rSet->GetItemState( GetWhich(SID_ATTR_PARA_OUTLINE_LEVEL) ); + + sal_Int16 nOutlineLv; + if( eItemState >= SfxItemState::DEFAULT ) + { + nOutlineLv = static_cast<const SfxUInt16Item &>(rSet->Get( GetWhich(SID_ATTR_PARA_OUTLINE_LEVEL) )).GetValue(); + m_xOutlineLvLB->set_active(nOutlineLv) ; + } + else + { + m_xOutlineLvLB->set_active(-1); + } + m_xOutlineLvLB->save_value(); + + eItemState = rSet->GetItemState( GetWhich(SID_ATTR_PARA_NUMRULE) ); + + if( eItemState >= SfxItemState::DEFAULT ) + { + OUString aStyle = static_cast<const SfxStringItem &>(rSet->Get( GetWhich(SID_ATTR_PARA_NUMRULE) )).GetValue(); + if(aStyle.isEmpty()) + aStyle = m_xNumberStyleLB->get_text(0); + + if( aStyle == "Outline") + { + //add it, select it, remove it ? do we really want set_active(-1) instead ? + m_xNumberStyleLB->append_text(msOutlineNumbering); + m_xNumberStyleLB->set_active_text(msOutlineNumbering); + m_xNumberStyleLB->remove_text(msOutlineNumbering); + m_xNumberStyleLB->save_value(); + } + else + m_xNumberStyleLB->set_active_text(aStyle); + + bHasNumberStyle = true; + } + else + { + m_xNumberStyleLB->set_active(-1); + } + + EditNumStyleSelectHdl_Impl(*m_xNumberStyleLB); + + m_xNumberStyleLB->save_value(); + + eItemState = rSet->GetItemState( FN_NUMBER_NEWSTART ); + if(eItemState > SfxItemState::DEFAULT ) + { + bCurNumrule = true; + const SfxBoolItem& rStart = static_cast<const SfxBoolItem&>(rSet->Get(FN_NUMBER_NEWSTART)); + + m_xNewStartCB->set_state(rStart.GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE ); + } + else + m_xNewStartCB->set_state(bHasNumberStyle ? TRISTATE_FALSE : TRISTATE_INDET); + + m_xNewStartCB->save_state(); + + eItemState = rSet->GetItemState( FN_NUMBER_NEWSTART_AT); + if( eItemState > SfxItemState::DEFAULT ) + { + const sal_uInt16 nNewStart = static_cast<const SfxUInt16Item&>(rSet->Get(FN_NUMBER_NEWSTART_AT)).GetValue(); + const bool bNotMax = USHRT_MAX != nNewStart; + m_xNewStartNumberCB->set_active(bNotMax); + m_xNewStartNF->set_value(bNotMax ? nNewStart : 1); + } + else + m_xNewStartCB->set_state(TRISTATE_INDET); + NewStartHdl_Impl(*m_xNewStartCB); + m_xNewStartNF->save_value(); + m_xNewStartNumberCB->save_state(); + StyleHdl_Impl(*m_xNumberStyleLB); + if( SfxItemState::DEFAULT <= rSet->GetItemState(RES_LINENUMBER)) + { + const SwFormatLineNumber& rNum = rSet->Get(RES_LINENUMBER); + sal_uLong nStartValue = rNum.GetStartValue(); + bool bCount = rNum.IsCount(); + m_xCountParaCB->set_state(bCount ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xRestartParaCountCB->set_state(0 != nStartValue ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xRestartNF->set_value(nStartValue == 0 ? 1 : nStartValue); + LineCountHdl_Impl(*m_xCountParaCB); + } + else + { + m_xCountParaCB->set_state(TRISTATE_INDET); + m_xRestartParaCountCB->set_state(TRISTATE_INDET); + } + m_xCountParaCB->save_state(); + m_xRestartParaCountCB->save_state(); + m_xRestartNF->save_value(); + + bModified = false; +} + +void SwParagraphNumTabPage::DisableOutline() +{ + m_xOutlineStartBX->set_sensitive(false); + m_xOutlineStartBX->set_tooltip_text( SwResId(STR_OUTLINENUMBERING_DISABLED) ); +} + +void SwParagraphNumTabPage::DisableNumbering() +{ + m_xNumberStyleBX->set_sensitive(false); + m_xNumberStyleBX->set_tooltip_text( SwResId(STR_OUTLINENUMBERING_DISABLED) ); +} + +void SwParagraphNumTabPage::EnableNewStart() +{ + m_xNewStartCB->show(); + m_xNewStartBX->show(); +} + +IMPL_LINK_NOARG(SwParagraphNumTabPage, NewStartHdl_Impl, weld::ToggleButton&, void) +{ + bool bEnable = m_xNewStartCB->get_active(); + m_xNewStartNumberCB->set_sensitive(bEnable); + m_xNewStartNF->set_sensitive(bEnable && m_xNewStartNumberCB->get_active()); +} + +IMPL_LINK_NOARG(SwParagraphNumTabPage, LineCountHdl_Impl, weld::ToggleButton&, void) +{ + m_xRestartParaCountCB->set_sensitive(m_xCountParaCB->get_active()); + + bool bEnableRestartValue = m_xRestartParaCountCB->get_sensitive() && + m_xRestartParaCountCB->get_active(); + m_xRestartBX->set_sensitive(bEnableRestartValue); +} + +IMPL_LINK_NOARG(SwParagraphNumTabPage, EditNumStyleSelectHdl_Impl, weld::ComboBox&, void) +{ + int numSelectPos = m_xNumberStyleLB->get_active(); + // 0 is "None" and -1 is unselected state + if (numSelectPos == 0 || numSelectPos == -1) + m_xEditNumStyleBtn->set_sensitive(false); + else + m_xEditNumStyleBtn->set_sensitive(true); +} + +IMPL_LINK_NOARG(SwParagraphNumTabPage, EditNumStyleHdl_Impl, weld::Button&, void) +{ + OUString aTemplName(m_xNumberStyleLB->get_active_text()); + ExecuteEditNumStyle_Impl( SID_STYLE_EDIT, aTemplName, SfxStyleFamily::Pseudo ); +} + +// Internal: Perform functions through the Dispatcher +bool SwParagraphNumTabPage::ExecuteEditNumStyle_Impl( + sal_uInt16 nId, const OUString &rStr, SfxStyleFamily nFamily) +{ + + SfxDispatcher &rDispatcher = *SfxViewShell::Current()->GetDispatcher(); + SfxStringItem aItem(nId, rStr); + SfxUInt16Item aFamily(SID_STYLE_FAMILY, static_cast<sal_uInt16>(nFamily)); + const SfxPoolItem* pItems[ 3 ]; + sal_uInt16 nCount = 0; + if( !rStr.isEmpty() ) + pItems[ nCount++ ] = &aItem; + pItems[ nCount++ ] = &aFamily; + + pItems[ nCount++ ] = nullptr; + + const SfxPoolItem* pItem = rDispatcher.Execute( + nId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + pItems ); + + return pItem != nullptr; + +} + +IMPL_LINK(SwParagraphNumTabPage, StyleHdl_Impl, weld::ComboBox&, rBox, void) +{ + bool bEnable = bCurNumrule || rBox.get_active() > 0; + m_xNewStartCB->set_sensitive(bEnable); + NewStartHdl_Impl(*m_xNewStartCB); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/pardlg.cxx b/sw/source/ui/chrdlg/pardlg.cxx new file mode 100644 index 000000000..e64932300 --- /dev/null +++ b/sw/source/ui/chrdlg/pardlg.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/htmlmode.hxx> +#include <sfx2/tabdlg.hxx> +#include <svl/style.hxx> +#include <svtools/htmlcfg.hxx> +#include <svl/cjkoptions.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swuipardlg.hxx> +#include <pardlg.hxx> +#include <drpcps.hxx> +#include <viewopt.hxx> +#include <numpara.hxx> +#include <strings.hrc> +#include <svx/svxids.hrc> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> + +SwParaDlg::SwParaDlg(weld::Window *pParent, + SwView& rVw, + const SfxItemSet& rCoreSet, + sal_uInt8 nDialogMode, + const OUString *pTitle, + bool bDraw, + const OString& sDefPage) + : SfxTabDialogController(pParent, + "modules/swriter/ui/paradialog.ui", + "ParagraphPropertiesDialog", + &rCoreSet, nullptr != pTitle) + , rView(rVw) + , bDrawParaDlg(bDraw) +{ + sal_uInt16 nHtmlMode = ::GetHtmlMode(rVw.GetDocShell()); + bool bHtmlMode = (nHtmlMode & HTMLMODE_ON) == HTMLMODE_ON; + if(pTitle) + { + // Update title + m_xDialog->set_title(m_xDialog->get_title() + SwResId(STR_TEXTCOLL_HEADER) + *pTitle + ")"); + } + // tabs common to paragraph and draw paragraphs (paragraphs inside a text box) + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_STD_PARAGRAPH), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc(RID_SVXPAGE_STD_PARAGRAPH), "GetTabPageRangesFunc fail!"); + AddTabPage("labelTP_PARA_STD", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_STD_PARAGRAPH), + pFact->GetTabPageRangesFunc(RID_SVXPAGE_STD_PARAGRAPH) ); + + OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_ALIGN_PARAGRAPH), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc(RID_SVXPAGE_ALIGN_PARAGRAPH), "GetTabPageRangesFunc fail!"); + AddTabPage("labelTP_PARA_ALIGN", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_ALIGN_PARAGRAPH), + pFact->GetTabPageRangesFunc(RID_SVXPAGE_ALIGN_PARAGRAPH)); + + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + if (!bDrawParaDlg && (!bHtmlMode || rHtmlOpt.IsPrintLayoutExtension())) + { + OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_EXT_PARAGRAPH), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc(RID_SVXPAGE_EXT_PARAGRAPH), "GetTabPageRangesFunc fail!"); + AddTabPage("textflow", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_EXT_PARAGRAPH), + pFact->GetTabPageRangesFunc(RID_SVXPAGE_EXT_PARAGRAPH)); + + } + else + RemoveTabPage("textflow"); + + SvtCJKOptions aCJKOptions; + if(!bHtmlMode && aCJKOptions.IsAsianTypographyEnabled()) + { + OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc(RID_SVXPAGE_PARA_ASIAN), "GetTabPageRangesFunc fail!"); + AddTabPage( "labelTP_PARA_ASIAN", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), + pFact->GetTabPageRangesFunc(RID_SVXPAGE_PARA_ASIAN) ); + } + else + RemoveTabPage("labelTP_PARA_ASIAN"); + + const sal_uInt16 nWhich(rCoreSet.GetPool()->GetWhich(SID_ATTR_LRSPACE)); + bool bLRValid = SfxItemState::DEFAULT <= rCoreSet.GetItemState(nWhich); + if(bHtmlMode || !bLRValid) + RemoveTabPage("labelTP_TABULATOR"); + else + { + OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_TABULATOR), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc(RID_SVXPAGE_TABULATOR), "GetTabPageRangesFunc fail!"); + AddTabPage( "labelTP_TABULATOR", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_TABULATOR), pFact->GetTabPageRangesFunc(RID_SVXPAGE_TABULATOR) ); + } + + // remove unwanted tabs for draw text box paragraph properties + if (bDrawParaDlg) + { + RemoveTabPage("labelTP_NUMPARA"); + RemoveTabPage("labelTP_DROPCAPS"); + RemoveTabPage("labelTP_BORDER"); + RemoveTabPage("area"); + RemoveTabPage("transparence"); + } + else + { + if(!(nDialogMode & DLG_ENVELOP)) + AddTabPage("labelTP_NUMPARA", SwParagraphNumTabPage::Create, SwParagraphNumTabPage::GetRanges); + else + RemoveTabPage("labelTP_NUMPARA"); + + AddTabPage("labelTP_DROPCAPS", SwDropCapsPage::Create, SwDropCapsPage::GetRanges); + + if(!bHtmlMode || (nHtmlMode & (HTMLMODE_SOME_STYLES|HTMLMODE_FULL_STYLES))) + { + // add Area and Transparence TabPages + AddTabPage("area", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_AREA )); + AddTabPage("transparence", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_TRANSPARENCE ) ); + } + else + { + RemoveTabPage("area"); + RemoveTabPage("transparence"); + } + + OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), "GetTabPageCreatorFunc fail!"); + OSL_ENSURE(pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ), "GetTabPageRangesFunc fail!"); + AddTabPage("labelTP_BORDER", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ) ); + } + + if (!sDefPage.isEmpty()) + SetCurPageId(sDefPage); +} + +SwParaDlg::~SwParaDlg() +{ +} + +void SwParaDlg::PageCreated(const OString& rId, SfxTabPage& rPage) +{ + SwWrtShell& rSh = rView.GetWrtShell(); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + + // Table borders cannot get any shade in Writer + if (rId == "labelTP_BORDER") + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::PARA))); + rPage.PageCreated(aSet); + } + else if (rId == "labelTP_PARA_STD") + { + aSet.Put(SfxUInt16Item(SID_SVXSTDPARAGRAPHTABPAGE_PAGEWIDTH, + static_cast< sal_uInt16 >(rSh.GetAnyCurRect(CurRectType::PagePrt).Width()) )); + + if (!bDrawParaDlg) + { + // See SvxStdParagraphTabPage::PageCreated: enable RegisterMode, AutoFirstLine, NegativeMode, ContextualMode + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_FLAGSET,0x0002|0x0004|0x0008|0x0010)); + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, MM50/10)); + + } + rPage.PageCreated(aSet); + } + else if (rId == "labelTP_PARA_ALIGN") + { + if (!bDrawParaDlg) + { + aSet.Put(SfxBoolItem(SID_SVXPARAALIGNTABPAGE_ENABLEJUSTIFYEXT,true)); + rPage.PageCreated(aSet); + } + } + else if (rId == "textflow") + { + // pagebreak only when the cursor is in the body-area and not in a table + const FrameTypeFlags eType = rSh.GetFrameType(nullptr,true); + if( !(FrameTypeFlags::BODY & eType) || + rSh.GetSelectionType() & SelectionType::Table ) + { + aSet.Put(SfxBoolItem(SID_DISABLE_SVXEXTPARAGRAPHTABPAGE_PAGEBREAK,true)); + rPage.PageCreated(aSet); + } + } + else if (rId == "labelTP_DROPCAPS") + { + static_cast<SwDropCapsPage&>(rPage).SetFormat(false); + } + else if (rId == "labelTP_NUMPARA") + { + SwTextFormatColl* pTmpColl = rSh.GetCurTextFormatColl(); + if( pTmpColl && pTmpColl->IsAssignedToListLevelOfOutlineStyle() ) + { + static_cast<SwParagraphNumTabPage&>(rPage).DisableOutline() ; + } + + static_cast<SwParagraphNumTabPage&>(rPage).EnableNewStart(); + weld::ComboBox& rBox = static_cast<SwParagraphNumTabPage&>(rPage).GetStyleBox(); + SfxStyleSheetBasePool* pPool = rView.GetDocShell()->GetStyleSheetPool(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Pseudo); + std::set<OUString> aNames; + while(pBase) + { + aNames.insert(pBase->GetName()); + pBase = pPool->Next(); + } + for(const auto& rName : aNames) + rBox.append_text(rName); + } + // inits for Area and Transparency TabPages + // The selection attribute lists (XPropertyList derivates, e.g. XColorList for + // the color table) need to be added as items (e.g. SvxColorTableItem) to make + // these pages find the needed attributes for fill style suggestions. + // These are added in SwDocStyleSheet::GetItemSet() for the SfxStyleFamily::Para on + // demand, but could also be directly added from the DrawModel. + else if (rId == "area") + { + SfxItemSet aNew(*aSet.GetPool(), + svl::Items<SID_COLOR_TABLE, SID_PATTERN_LIST, + SID_OFFER_IMPORT, SID_OFFER_IMPORT>{}); + + aNew.Put(*GetInputSetImpl()); + + // add flag for direct graphic content selection + aNew.Put(SfxBoolItem(SID_OFFER_IMPORT, true)); + + rPage.PageCreated(aNew); + } + else if (rId == "transparence") + { + rPage.PageCreated(*GetInputSetImpl()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/swuiccoll.cxx b/sw/source/ui/chrdlg/swuiccoll.cxx new file mode 100644 index 000000000..b4b722f63 --- /dev/null +++ b/sw/source/ui/chrdlg/swuiccoll.cxx @@ -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 . + */ + +#include <memory> +#include <cmdid.h> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> + +#include <sfx2/styfitem.hxx> + +#include <ccoll.hxx> +#include <fmtcol.hxx> +#include <hintids.hxx> +#include <docsh.hxx> + +#include <swuiccoll.hxx> + +const sal_uInt16 SwCondCollPage::m_aPageRg[] = { + FN_COND_COLL, FN_COND_COLL, + 0 +}; + +SwCondCollPage::SwCondCollPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/conditionpage.ui", "ConditionPage", &rSet) + , m_rSh(::GetActiveView()->GetWrtShell()) + , m_pCmds(SwCondCollItem::GetCmds()) + , m_pFormat(nullptr) + , m_bNewTemplate(false) + , m_xConditionCB(m_xBuilder->weld_check_button("condstyle")) + , m_xTbLinks(m_xBuilder->weld_tree_view("links")) + , m_xStyleLB(m_xBuilder->weld_tree_view("styles")) + , m_xFilterLB(m_xBuilder->weld_combo_box("filter")) + , m_xRemovePB(m_xBuilder->weld_button("remove")) + , m_xAssignPB(m_xBuilder->weld_button("apply")) +{ + m_xStyleLB->make_sorted(); + const auto nHeightRequest = m_xStyleLB->get_height_rows(12); + m_xStyleLB->set_size_request(-1, nHeightRequest); + m_xTbLinks->set_size_request(-1, nHeightRequest); + std::vector<int> aWidths; + aWidths.push_back(m_xTbLinks->get_approximate_digit_width() * 40); + m_xTbLinks->set_column_fixed_widths(aWidths); + + const sal_Int32 nStrCount = m_xFilterLB->get_count(); + for (sal_Int32 i = 0; i < nStrCount; ++i) + m_aStrArr.push_back(m_xFilterLB->get_text(i)); + m_xFilterLB->clear(); + + SetExchangeSupport(); + + // Install handlers + m_xConditionCB->connect_toggled(LINK(this, SwCondCollPage, OnOffHdl)); + m_xTbLinks->connect_row_activated(LINK(this, SwCondCollPage, AssignRemoveTreeListBoxHdl)); + m_xStyleLB->connect_row_activated(LINK(this, SwCondCollPage, AssignRemoveTreeListBoxHdl)); + m_xRemovePB->connect_clicked(LINK(this, SwCondCollPage, AssignRemoveClickHdl)); + m_xAssignPB->connect_clicked(LINK(this, SwCondCollPage, AssignRemoveClickHdl)); + m_xTbLinks->connect_changed(LINK(this, SwCondCollPage, SelectTreeListBoxHdl)); + m_xStyleLB->connect_changed(LINK(this, SwCondCollPage, SelectTreeListBoxHdl)); + m_xFilterLB->connect_changed(LINK(this, SwCondCollPage, SelectListBoxHdl)); + + std::unique_ptr<SfxStyleFamilies> xFamilies(SW_MOD()->CreateStyleFamilies()); + size_t nCount = xFamilies->size(); + for (size_t j = 0; j < nCount; ++j) + { + const SfxStyleFamilyItem &rFamilyItem = xFamilies->at(j); + if (SfxStyleFamily::Para == rFamilyItem.GetFamily()) + { + const SfxStyleFilter& rFilterList = rFamilyItem.GetFilterList(); + for (size_t i = 0; i < rFilterList.size(); ++i) + m_xFilterLB->append(OUString::number(int(rFilterList[i].nFlags)), rFilterList[i].aName); + break; + } + } + + m_xFilterLB->set_active(1); + m_xTbLinks->show(); +} + +SwCondCollPage::~SwCondCollPage() +{ +} + +DeactivateRC SwCondCollPage::DeactivatePage(SfxItemSet * _pSet) +{ + if( _pSet ) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SwCondCollPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwCondCollPage>(pPage, pController, *rSet); +} + +bool SwCondCollPage::FillItemSet(SfxItemSet *rSet) +{ + SwCondCollItem aCondItem; + for (size_t i = 0; i < m_aStrArr.size(); ++i) + { + const OUString sEntry = m_xTbLinks->get_text(i, 1); + aCondItem.SetStyle( &sEntry, i); + } + rSet->Put(aCondItem); + return true; +} + +void SwCondCollPage::Reset(const SfxItemSet *) +{ + if (m_bNewTemplate) + m_xConditionCB->set_sensitive(true); + if (RES_CONDTXTFMTCOLL == m_pFormat->Which()) + m_xConditionCB->set_active(true); + OnOffHdl(*m_xConditionCB); + + m_xTbLinks->clear(); + + SfxStyleSheetBasePool* pPool = m_rSh.GetView().GetDocShell()->GetStyleSheetPool(); + m_xStyleLB->clear(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Para); + while (pBase) + { + if (!m_pFormat || pBase->GetName() != m_pFormat->GetName()) + m_xStyleLB->append_text(pBase->GetName()); + pBase = pPool->Next(); + } + m_xStyleLB->select(0); + + for (size_t n = 0; n < m_aStrArr.size(); ++n) + { + m_xTbLinks->append_text(m_aStrArr[n]); + + const SwCollCondition* pCond = nullptr; + if( m_pFormat && RES_CONDTXTFMTCOLL == m_pFormat->Which() && + nullptr != ( pCond = static_cast<SwConditionTextFormatColl*>(m_pFormat)-> + HasCondition( SwCollCondition( nullptr, m_pCmds[n].nCnd, m_pCmds[n].nSubCond ) ) ) + && pCond->GetTextFormatColl() ) + { + m_xTbLinks->set_text(n, pCond->GetTextFormatColl()->GetName(), 1); + } + + if (0 == n) + { + m_xTbLinks->select(0); + SelectTreeListBoxHdl(*m_xTbLinks); + } + } +} + +IMPL_LINK(SwCondCollPage, OnOffHdl, weld::ToggleButton&, rBox, void) +{ + const bool bEnable = rBox.get_active(); + m_xTbLinks->set_sensitive(bEnable); + m_xStyleLB->set_sensitive(bEnable); + m_xFilterLB->set_sensitive(bEnable); + m_xRemovePB->set_sensitive(bEnable); + m_xAssignPB->set_sensitive(bEnable); + if (bEnable) + SelectHdl(nullptr); +} + +IMPL_LINK(SwCondCollPage, AssignRemoveClickHdl, weld::Button&, rBtn, void) +{ + AssignRemove(&rBtn); +} + +IMPL_LINK(SwCondCollPage, AssignRemoveTreeListBoxHdl, weld::TreeView&, rBtn, bool) +{ + AssignRemove(&rBtn); + return true; +} + +void SwCondCollPage::AssignRemove(const weld::Widget* pBtn) +{ + int nPos = m_xTbLinks->get_selected_index(); + if (nPos == -1) + { + return; + } + + const bool bAssEnabled = pBtn != m_xRemovePB.get() && m_xAssignPB->get_sensitive(); + m_xAssignPB->set_sensitive(!bAssEnabled); + m_xRemovePB->set_sensitive(bAssEnabled); + if (bAssEnabled) + m_xTbLinks->set_text(nPos, m_xStyleLB->get_selected_text(), 1); + else + m_xTbLinks->set_text(nPos, "", 1); +} + +IMPL_LINK(SwCondCollPage, SelectTreeListBoxHdl, weld::TreeView&, rBox, void) +{ + SelectHdl(&rBox); +} + +IMPL_LINK(SwCondCollPage, SelectListBoxHdl, weld::ComboBox&, rBox, void) +{ + SelectHdl(&rBox); +} + +void SwCondCollPage::SelectHdl(const weld::Widget* pBox) +{ + if (pBox == m_xFilterLB.get()) + { + m_xStyleLB->clear(); + const sal_Int32 nSelPos = m_xFilterLB->get_active(); + const SfxStyleSearchBits nSearchFlags = static_cast<SfxStyleSearchBits>(m_xFilterLB->get_id(nSelPos).toInt32()); + SfxStyleSheetBasePool* pPool = m_rSh.GetView().GetDocShell()->GetStyleSheetPool(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Para, nSearchFlags); + + bool bEmpty = true; + while (pBase) + { + if (!m_pFormat || pBase->GetName() != m_pFormat->GetName()) + { + m_xStyleLB->append_text(pBase->GetName()); + bEmpty = false; + } + pBase = pPool->Next(); + } + m_xStyleLB->select(bEmpty ? -1 : 0); + SelectHdl(m_xStyleLB.get()); + } + else + { + int nSelected = m_xTbLinks->get_selected_index(); + const OUString sTbEntry = nSelected != -1 + ? m_xTbLinks->get_text(nSelected, 1) + : OUString(); + const OUString sStyle = m_xStyleLB->get_selected_text(); + + m_xAssignPB->set_sensitive(sStyle != sTbEntry && m_xConditionCB->get_active()); + + if (pBox != m_xStyleLB.get()) + m_xRemovePB->set_sensitive(m_xConditionCB->get_active() && !sTbEntry.isEmpty()); + } +} + +void SwCondCollPage::SetCollection(SwFormat* pFormat, bool bNew) +{ + m_pFormat = pFormat; + m_bNewTemplate = bNew; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/chrdlg/tblnumfm.cxx b/sw/source/ui/chrdlg/tblnumfm.cxx new file mode 100644 index 000000000..157660214 --- /dev/null +++ b/sw/source/ui/chrdlg/tblnumfm.cxx @@ -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 . + */ + +#include <svx/svxids.hrc> +#include <svx/numinf.hxx> +#include <tblnumfm.hxx> + +#include <svx/dialogs.hrc> +#include <svl/itemset.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/tabdlg.hxx> + +SwNumFormatDlg::SwNumFormatDlg(weld::Widget* pParent, const SfxItemSet& rSet) + : SfxSingleTabDialogController(pParent, &rSet, "cui/ui/formatnumberdialog.ui", "FormatNumberDialog") +{ + // Create TabPage + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc(RID_SVXPAGE_NUMBERFORMAT); + if ( fnCreatePage ) + { + std::unique_ptr<SfxTabPage> xNewPage = (*fnCreatePage)(get_content_area(), this, &rSet); + SfxAllItemSet aSet(*(rSet.GetPool())); + aSet.Put(xNewPage->GetItemSet().Get( SID_ATTR_NUMBERFORMAT_INFO)); + xNewPage->PageCreated(aSet); + SetTabPage(std::move(xNewPage)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/config/mailconfigpage.cxx b/sw/source/ui/config/mailconfigpage.cxx new file mode 100644 index 000000000..e51d30195 --- /dev/null +++ b/sw/source/ui/config/mailconfigpage.cxx @@ -0,0 +1,484 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <mailconfigpage.hxx> +#include <mmconfigitem.hxx> +#include <mailmergehelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/mail/MailServiceType.hpp> +#include <com/sun/star/mail/XMailService.hpp> +#include <com/sun/star/mail/MailServiceProvider.hpp> +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::mail; +using namespace ::com::sun::star::beans; + +class SwTestAccountSettingsDialog : public SfxDialogController +{ + ImplSVEvent* m_pPostedEvent; + OUString m_sCompleted; + OUString m_sFailed; + OUString m_sErrorServer; + bool m_bStop; + + SwMailConfigPage* m_pParent; + + std::unique_ptr<weld::Button> m_xStopPB; + std::unique_ptr<weld::TextView> m_xErrorsED; + std::unique_ptr<weld::Label> m_xEstablish; + std::unique_ptr<weld::Label> m_xFind; + std::unique_ptr<weld::Label> m_xResult1; + std::unique_ptr<weld::Label> m_xResult2; + std::unique_ptr<weld::Image> m_xImage1; + std::unique_ptr<weld::Image> m_xImage2; + std::unique_ptr<weld::Image> m_xImage3; + std::unique_ptr<weld::Image> m_xImage4; + + void Test(); + DECL_LINK(StopHdl, weld::Button&, void); + DECL_LINK(TestHdl, void*, void); +public: + explicit SwTestAccountSettingsDialog(SwMailConfigPage* pParent); + virtual ~SwTestAccountSettingsDialog() override; +}; + +namespace { + +class SwAuthenticationSettingsDialog : public SfxDialogController +{ + SwMailMergeConfigItem& m_rConfigItem; + + std::unique_ptr<weld::CheckButton> m_xAuthenticationCB; + std::unique_ptr<weld::RadioButton> m_xSeparateAuthenticationRB; + std::unique_ptr<weld::RadioButton> m_xSMTPAfterPOPRB; + std::unique_ptr<weld::Label> m_xOutgoingServerFT; + std::unique_ptr<weld::Label> m_xUserNameFT; + std::unique_ptr<weld::Entry> m_xUserNameED; + std::unique_ptr<weld::Label> m_xOutPasswordFT; + std::unique_ptr<weld::Entry> m_xOutPasswordED; + std::unique_ptr<weld::Label> m_xIncomingServerFT; + std::unique_ptr<weld::Label> m_xServerFT; + std::unique_ptr<weld::Entry> m_xServerED; + std::unique_ptr<weld::Label> m_xPortFT; + std::unique_ptr<weld::SpinButton> m_xPortNF; + std::unique_ptr<weld::Label> m_xProtocolFT; + std::unique_ptr<weld::RadioButton> m_xPOP3RB; + std::unique_ptr<weld::RadioButton> m_xIMAPRB; + std::unique_ptr<weld::Label> m_xInUsernameFT; + std::unique_ptr<weld::Entry> m_xInUsernameED; + std::unique_ptr<weld::Label> m_xInPasswordFT; + std::unique_ptr<weld::Entry> m_xInPasswordED; + std::unique_ptr<weld::Button> m_xOKPB; + + DECL_LINK(OKHdl_Impl, weld::Button&, void); + DECL_LINK(CheckBoxHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(RadioButtonHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(InServerHdl_Impl, weld::Button&, void); + +public: + SwAuthenticationSettingsDialog(weld::Window* pParent, SwMailMergeConfigItem& rItem); +}; + +} + +SwMailConfigPage::SwMailConfigPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/mailconfigpage.ui", "MailConfigPage", &rSet) + , m_pConfigItem(new SwMailMergeConfigItem) + , m_xDisplayNameED(m_xBuilder->weld_entry("displayname")) + , m_xAddressED(m_xBuilder->weld_entry("address")) + , m_xReplyToCB(m_xBuilder->weld_check_button("replytocb")) + , m_xReplyToFT(m_xBuilder->weld_label("replyto_label")) + , m_xReplyToED(m_xBuilder->weld_entry("replyto")) + , m_xServerED(m_xBuilder->weld_entry("server")) + , m_xPortNF(m_xBuilder->weld_spin_button("port")) + , m_xSecureCB(m_xBuilder->weld_check_button("secure")) + , m_xServerAuthenticationPB(m_xBuilder->weld_button("serverauthentication")) + , m_xTestPB(m_xBuilder->weld_button("test")) +{ + m_xReplyToCB->connect_toggled(LINK(this, SwMailConfigPage, ReplyToHdl)); + m_xServerAuthenticationPB->connect_clicked(LINK(this, SwMailConfigPage, AuthenticationHdl)); + m_xTestPB->connect_clicked(LINK(this, SwMailConfigPage, TestHdl)); + m_xSecureCB->connect_toggled(LINK(this, SwMailConfigPage, SecureHdl)); +} + +SwMailConfigPage::~SwMailConfigPage() +{ + m_pConfigItem.reset(); +} + +std::unique_ptr<SfxTabPage> SwMailConfigPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwMailConfigPage>(pPage, pController, *rAttrSet); +} + +bool SwMailConfigPage::FillItemSet( SfxItemSet* /*rSet*/ ) +{ + if (m_xDisplayNameED->get_value_changed_from_saved()) + m_pConfigItem->SetMailDisplayName(m_xDisplayNameED->get_text()); + if (m_xAddressED->get_value_changed_from_saved()) + m_pConfigItem->SetMailAddress(m_xAddressED->get_text()); + if (m_xReplyToCB->get_state_changed_from_saved() ) + m_pConfigItem->SetMailReplyTo(m_xReplyToCB->get_active()); + if (m_xReplyToED->get_value_changed_from_saved()) + m_pConfigItem->SetMailReplyTo(m_xReplyToED->get_text()); + if (m_xServerED->get_value_changed_from_saved()) + m_pConfigItem->SetMailServer(m_xServerED->get_text()); + + m_pConfigItem->SetMailPort(m_xPortNF->get_value()); + m_pConfigItem->SetSecureConnection(m_xSecureCB->get_active()); + + m_pConfigItem->Commit(); + return true; +} + +void SwMailConfigPage::Reset( const SfxItemSet* /*rSet*/ ) +{ + m_xDisplayNameED->set_text(m_pConfigItem->GetMailDisplayName()); + m_xAddressED->set_text(m_pConfigItem->GetMailAddress()); + + m_xReplyToED->set_text(m_pConfigItem->GetMailReplyTo()) ; + m_xReplyToCB->set_active(m_pConfigItem->IsMailReplyTo()); + ReplyToHdl(*m_xReplyToCB); + + m_xServerED->set_text(m_pConfigItem->GetMailServer()); + m_xPortNF->set_value(m_pConfigItem->GetMailPort()); + + m_xSecureCB->set_active(m_pConfigItem->IsSecureConnection()); + + m_xDisplayNameED->save_value(); + m_xAddressED->save_value(); + m_xReplyToCB->save_state(); + m_xReplyToED->save_value(); + m_xServerED->save_value(); + m_xPortNF->save_value(); + m_xSecureCB->save_state(); +} + +IMPL_LINK(SwMailConfigPage, ReplyToHdl, weld::ToggleButton&, rBox, void) +{ + bool bEnable = rBox.get_active(); + m_xReplyToFT->set_sensitive(bEnable); + m_xReplyToED->set_sensitive(bEnable); +} + +IMPL_LINK_NOARG(SwMailConfigPage, AuthenticationHdl, weld::Button&, void) +{ + m_pConfigItem->SetMailAddress(m_xAddressED->get_text()); + + SwAuthenticationSettingsDialog aDlg(GetFrameWeld(), *m_pConfigItem); + aDlg.run(); +} + +IMPL_LINK_NOARG(SwMailConfigPage, TestHdl, weld::Button&, void) +{ + SwTestAccountSettingsDialog aDlg(this); + aDlg.run(); +} + +IMPL_LINK(SwMailConfigPage, SecureHdl, weld::ToggleButton&, rBox, void) +{ + bool bEnable = rBox.get_active(); + m_pConfigItem->SetSecureConnection(bEnable); + m_pConfigItem->SetMailPort(m_xPortNF->get_value()); + m_xPortNF->set_value(m_pConfigItem->GetMailPort()); +} + +SwTestAccountSettingsDialog::SwTestAccountSettingsDialog(SwMailConfigPage* pParent) + : SfxDialogController(pParent->GetFrameWeld(), "modules/swriter/ui/testmailsettings.ui", "TestMailSettings") + , m_bStop(false) + , m_pParent(pParent) + , m_xStopPB(m_xBuilder->weld_button("stop")) + , m_xErrorsED(m_xBuilder->weld_text_view("errors")) + , m_xEstablish(m_xBuilder->weld_label("establish")) + , m_xFind(m_xBuilder->weld_label("find")) + , m_xResult1(m_xBuilder->weld_label("result1")) + , m_xResult2(m_xBuilder->weld_label("result2")) + , m_xImage1(m_xBuilder->weld_image("image1")) + , m_xImage2(m_xBuilder->weld_image("image2")) + , m_xImage3(m_xBuilder->weld_image("image3")) + , m_xImage4(m_xBuilder->weld_image("image4")) +{ + m_xErrorsED->set_size_request(m_xErrorsED->get_approximate_digit_width() * 72, + m_xErrorsED->get_height_rows(8)); + m_sErrorServer = m_xErrorsED->get_text(); + m_xErrorsED->set_text(""); + m_sCompleted = m_xResult1->get_label(); + m_sFailed = m_xResult2->get_label(); + + m_xStopPB->connect_clicked(LINK(this, SwTestAccountSettingsDialog, StopHdl)); + + m_pPostedEvent = Application::PostUserEvent(LINK(this, SwTestAccountSettingsDialog, TestHdl)); +} + +SwTestAccountSettingsDialog::~SwTestAccountSettingsDialog() +{ + if (m_pPostedEvent) + { + Application::RemoveUserEvent(m_pPostedEvent); + } +} + +IMPL_LINK_NOARG(SwTestAccountSettingsDialog, StopHdl, weld::Button&, void) +{ + m_bStop = true; +} + +IMPL_LINK_NOARG(SwTestAccountSettingsDialog, TestHdl, void*, void) +{ + m_pPostedEvent = nullptr; + weld::WaitObject aWait(m_xDialog.get()); + Test(); +} + +void SwTestAccountSettingsDialog::Test() +{ + uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + + OUString sException; + + bool bIsLoggedIn = false; + bool bIsServer = false; + try + { + uno::Reference< mail::XMailService > xInMailService; + uno::Reference< mail::XMailServiceProvider > xMailServiceProvider( + mail::MailServiceProvider::create(xContext) ); + uno::Reference< mail::XMailService > xMailService = + xMailServiceProvider->create( + mail::MailServiceType_SMTP); + if(m_bStop) + return; + uno::Reference<XConnectionListener> xConnectionListener(new SwConnectionListener()); + + if(m_pParent->m_pConfigItem->IsAuthentication() && + m_pParent->m_pConfigItem->IsSMTPAfterPOP()) + { + xInMailService = xMailServiceProvider->create( + m_pParent->m_pConfigItem->IsInServerPOP() ? + mail::MailServiceType_POP3 : mail::MailServiceType_IMAP); + if(m_bStop) + return; + //authenticate at the POP or IMAP server first + uno::Reference<XAuthenticator> xAuthenticator = + new SwAuthenticator( + m_pParent->m_pConfigItem->GetInServerUserName(), + m_pParent->m_pConfigItem->GetInServerPassword(), + m_xDialog.get()); + + xInMailService->addConnectionListener(xConnectionListener); + //check connection + uno::Reference< uno::XCurrentContext> xConnectionContext = + new SwConnectionContext( + m_pParent->m_pConfigItem->GetInServerName(), + m_pParent->m_pConfigItem->GetInServerPort(), + "Insecure"); + xInMailService->connect(xConnectionContext, xAuthenticator); + } + if(m_bStop) + return; + uno::Reference<XAuthenticator> xAuthenticator; + if(m_pParent->m_pConfigItem->IsAuthentication() && + !m_pParent->m_pConfigItem->IsSMTPAfterPOP() && + !m_pParent->m_pConfigItem->GetMailUserName().isEmpty()) + xAuthenticator = + new SwAuthenticator( + m_pParent->m_pConfigItem->GetMailUserName(), + m_pParent->m_pConfigItem->GetMailPassword(), + m_xDialog.get()); + else + xAuthenticator = new SwAuthenticator(); + + xMailService->addConnectionListener(xConnectionListener); + if(m_bStop) + return; + //just to check if the server exists + xMailService->getSupportedConnectionTypes(); + if(m_bStop) + return; + bIsServer = true; + //check connection + uno::Reference< uno::XCurrentContext> xConnectionContext = + new SwConnectionContext( + m_pParent->m_xServerED->get_text(), + m_pParent->m_xPortNF->get_value(), + m_pParent->m_xSecureCB->get_active() ? OUString("Ssl") : OUString("Insecure")); + xMailService->connect(xConnectionContext, xAuthenticator); + bIsLoggedIn = xMailService->isConnected(); + if( xInMailService.is() ) + xInMailService->disconnect(); + if( xMailService->isConnected()) + xMailService->disconnect(); + } + catch (const uno::Exception& e) + { + sException = e.Message; + } + + m_xResult1->set_label(bIsServer ? m_sCompleted : m_sFailed); + m_xImage1->set_visible(!bIsServer); + m_xImage3->set_visible(bIsServer); + + m_xResult2->set_label(bIsLoggedIn ? m_sCompleted : m_sFailed); + m_xImage2->set_visible(!bIsLoggedIn); + m_xImage4->set_visible(bIsLoggedIn); + + if (!bIsServer || !bIsLoggedIn) + { + OUString aErrorMessage(m_sErrorServer); + if (!sException.isEmpty()) + aErrorMessage += "\n--\n" + sException; + m_xErrorsED->set_text(aErrorMessage); + } +} + +SwMailConfigDlg::SwMailConfigDlg(weld::Window* pParent, SfxItemSet& rSet) + : SfxSingleTabDialogController(pParent, &rSet) +{ + // create TabPage + SetTabPage(SwMailConfigPage::Create(get_content_area(), this, &rSet)); + m_xDialog->set_title(SwResId(STR_MAILCONFIG_DLG_TITLE)); +} + +SwAuthenticationSettingsDialog::SwAuthenticationSettingsDialog( + weld::Window* pParent, SwMailMergeConfigItem& rItem) + : SfxDialogController(pParent, "modules/swriter/ui/authenticationsettingsdialog.ui", "AuthenticationSettingsDialog") + , m_rConfigItem(rItem) + , m_xAuthenticationCB(m_xBuilder->weld_check_button("authentication")) + , m_xSeparateAuthenticationRB(m_xBuilder->weld_radio_button("separateauthentication")) + , m_xSMTPAfterPOPRB(m_xBuilder->weld_radio_button("smtpafterpop")) + , m_xOutgoingServerFT(m_xBuilder->weld_label("label1")) + , m_xUserNameFT(m_xBuilder->weld_label("username_label")) + , m_xUserNameED(m_xBuilder->weld_entry("username")) + , m_xOutPasswordFT(m_xBuilder->weld_label("outpassword_label")) + , m_xOutPasswordED(m_xBuilder->weld_entry("outpassword")) + , m_xIncomingServerFT(m_xBuilder->weld_label("label2")) + , m_xServerFT(m_xBuilder->weld_label("server_label")) + , m_xServerED(m_xBuilder->weld_entry("server")) + , m_xPortFT(m_xBuilder->weld_label("port_label")) + , m_xPortNF(m_xBuilder->weld_spin_button("port")) + , m_xProtocolFT(m_xBuilder->weld_label("label3")) + , m_xPOP3RB(m_xBuilder->weld_radio_button("pop3")) + , m_xIMAPRB(m_xBuilder->weld_radio_button("imap")) + , m_xInUsernameFT(m_xBuilder->weld_label("inusername_label")) + , m_xInUsernameED(m_xBuilder->weld_entry("inusername")) + , m_xInPasswordFT(m_xBuilder->weld_label("inpassword_label")) + , m_xInPasswordED(m_xBuilder->weld_entry("inpassword")) + , m_xOKPB(m_xBuilder->weld_button("ok")) +{ + m_xAuthenticationCB->connect_toggled( LINK( this, SwAuthenticationSettingsDialog, CheckBoxHdl_Impl)); + Link<weld::ToggleButton&,void> aRBLink = LINK( this, SwAuthenticationSettingsDialog, RadioButtonHdl_Impl ); + m_xSeparateAuthenticationRB->connect_toggled( aRBLink ); + m_xSMTPAfterPOPRB->connect_toggled( aRBLink ); + m_xOKPB->connect_clicked( LINK( this, SwAuthenticationSettingsDialog, OKHdl_Impl)); + Link<weld::Button&,void> aInServerLink = LINK( this, SwAuthenticationSettingsDialog, InServerHdl_Impl ); + m_xPOP3RB->connect_clicked( aInServerLink ); + m_xIMAPRB->connect_clicked( aInServerLink ); + + m_xAuthenticationCB->set_active(m_rConfigItem.IsAuthentication()); + if (m_rConfigItem.IsSMTPAfterPOP()) + m_xSMTPAfterPOPRB->set_active(true); + else + m_xSeparateAuthenticationRB->set_active(true); + m_xUserNameED->set_text(m_rConfigItem.GetMailUserName()); + m_xOutPasswordED->set_text(m_rConfigItem.GetMailPassword()); + + m_xServerED->set_text(m_rConfigItem.GetInServerName()); + m_xPortNF->set_value(m_rConfigItem.GetInServerPort()); + if (m_rConfigItem.IsInServerPOP()) + m_xPOP3RB->set_active(true); + else + m_xIMAPRB->set_active(true); + m_xInUsernameED->set_text(m_rConfigItem.GetInServerUserName()); + m_xInPasswordED->set_text(m_rConfigItem.GetInServerPassword()); + + CheckBoxHdl_Impl(*m_xAuthenticationCB); +} + +IMPL_LINK_NOARG(SwAuthenticationSettingsDialog, OKHdl_Impl, weld::Button&, void) +{ + m_rConfigItem.SetAuthentication( m_xAuthenticationCB->get_active() ); + m_rConfigItem.SetSMTPAfterPOP(m_xSMTPAfterPOPRB->get_active()); + m_rConfigItem.SetMailUserName(m_xUserNameED->get_text()); + m_rConfigItem.SetMailPassword(m_xOutPasswordED->get_text()); + m_rConfigItem.SetInServerName(m_xServerED->get_text()); + m_rConfigItem.SetInServerPort(m_xPortNF->get_value()); + m_rConfigItem.SetInServerPOP(m_xPOP3RB->get_active()); + m_rConfigItem.SetInServerUserName(m_xInUsernameED->get_text()); + m_rConfigItem.SetInServerPassword(m_xInPasswordED->get_text()); + m_xDialog->response(RET_OK); +} + +IMPL_LINK( SwAuthenticationSettingsDialog, CheckBoxHdl_Impl, weld::ToggleButton&, rBox, void) +{ + bool bChecked = rBox.get_active(); + m_xSeparateAuthenticationRB->set_sensitive(bChecked); + m_xSMTPAfterPOPRB->set_sensitive(bChecked); + RadioButtonHdl_Impl(*m_xSeparateAuthenticationRB); +} + +IMPL_LINK_NOARG(SwAuthenticationSettingsDialog, RadioButtonHdl_Impl, weld::ToggleButton&, void) +{ + bool bSeparate = m_xSeparateAuthenticationRB->get_active(); + bool bIsEnabled = m_xSeparateAuthenticationRB->get_sensitive(); + bool bNotSeparate = !bSeparate && bIsEnabled; + bSeparate &= bIsEnabled; + + if (bSeparate && m_xUserNameED->get_text().isEmpty()) + m_xUserNameED->set_text(m_rConfigItem.GetMailAddress()); + else if (!bSeparate && m_xUserNameED->get_text() == m_rConfigItem.GetMailAddress()) + m_xUserNameED->set_text(""); + + if (bNotSeparate && m_xInUsernameED->get_text().isEmpty()) + m_xInUsernameED->set_text(m_rConfigItem.GetMailAddress()); + else if (!bNotSeparate && m_xInUsernameED->get_text() == m_rConfigItem.GetMailAddress()) + m_xInUsernameED->set_text(""); + + m_xOutgoingServerFT->set_sensitive(bSeparate); + m_xUserNameFT->set_sensitive(bSeparate); + m_xUserNameED->set_sensitive(bSeparate); + m_xOutPasswordFT->set_sensitive(bSeparate); + m_xOutPasswordED->set_sensitive(bSeparate); + + m_xIncomingServerFT->set_sensitive(bNotSeparate); + m_xServerFT->set_sensitive(bNotSeparate); + m_xServerED->set_sensitive(bNotSeparate); + m_xPortFT->set_sensitive(bNotSeparate); + m_xPortNF->set_sensitive(bNotSeparate); + m_xInUsernameFT->set_sensitive(bNotSeparate); + m_xInUsernameED->set_sensitive(bNotSeparate); + m_xProtocolFT->set_sensitive(bNotSeparate); + m_xPOP3RB->set_sensitive(bNotSeparate); + m_xIMAPRB->set_sensitive(bNotSeparate); + m_xInPasswordFT->set_sensitive(bNotSeparate); + m_xInPasswordED->set_sensitive(bNotSeparate); +} + +IMPL_LINK_NOARG( SwAuthenticationSettingsDialog, InServerHdl_Impl, weld::Button&, void) +{ + bool bPOP = m_xPOP3RB->get_active(); + m_rConfigItem.SetInServerPOP(bPOP); + m_xPortNF->set_value(m_rConfigItem.GetInServerPort()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/config/optcomp.cxx b/sw/source/ui/config/optcomp.cxx new file mode 100644 index 000000000..855aef0a3 --- /dev/null +++ b/sw/source/ui/config/optcomp.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 <optcomp.hxx> + +#include <cmdid.h> +#include <docsh.hxx> +#include <uiitems.hxx> +#include <view.hxx> +#include <wrtsh.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <IDocumentSettingAccess.hxx> +#include <vector> +#include <svtools/restartdialog.hxx> +#include <comphelper/processfactory.hxx> +#include <officecfg/Office/Compatibility.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::uno; +using namespace ::std; + +struct SwCompatibilityOptPage_Impl +{ + std::vector< SvtCompatibilityEntry > m_aList; +}; + +SwCompatibilityOptPage::SwCompatibilityOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optcompatpage.ui", "OptCompatPage", &rSet) + , m_pWrtShell(nullptr) + , m_pImpl(new SwCompatibilityOptPage_Impl) + , m_nSavedOptions(0) + , m_bSavedMSFormsMenuOption(false) + , m_xMain(m_xBuilder->weld_frame("compatframe")) + , m_xGlobalOptionsFrame(m_xBuilder->weld_frame("globalcompatframe")) + , m_xFormattingLB(m_xBuilder->weld_combo_box("format")) + , m_xGlobalOptionsLB(m_xBuilder->weld_combo_box("globaloptions")) + , m_xOptionsLB(m_xBuilder->weld_tree_view("options")) + , m_xGlobalOptionsCLB(m_xBuilder->weld_tree_view("globaloptioncheckbox")) + , m_xDefaultPB(m_xBuilder->weld_button("default")) +{ + std::vector<int> aWidths; + aWidths.push_back(m_xOptionsLB->get_checkbox_column_width()); + m_xOptionsLB->set_column_fixed_widths(aWidths); + m_xGlobalOptionsCLB->set_column_fixed_widths(aWidths); + + int nPos = 0; + for (int i = static_cast<int>(SvtCompatibilityEntry::Index::Module) + 1; + i < static_cast<int>(SvtCompatibilityEntry::Index::INVALID) - 1; // omit AddTableLineSpacing + ++i) + { + int nCoptIdx = i - 2; /* Do not consider "Name" & "Module" indexes */ + + const OUString sEntry = m_xFormattingLB->get_text(nCoptIdx); + m_xOptionsLB->append(); + m_xOptionsLB->set_toggle(nPos, TRISTATE_FALSE, 0); + m_xOptionsLB->set_text(nPos, sEntry, 1); + ++nPos; + } + + m_sUserEntry = m_xFormattingLB->get_text(m_xFormattingLB->get_count() - 1); + + m_xFormattingLB->clear(); + + // Set MSOCompatibleFormsMenu entry attributes + const bool bReadOnly = officecfg::Office::Compatibility::View::MSCompatibleFormsMenu::isReadOnly(); + m_xGlobalOptionsCLB->set_sensitive(!bReadOnly); + + m_xGlobalOptionsCLB->append(); + const bool bChecked = m_aViewConfigItem.HasMSOCompatibleFormsMenu(); + m_xGlobalOptionsCLB->set_toggle(0, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE, 0); + m_xGlobalOptionsCLB->set_text(0, m_xGlobalOptionsLB->get_text(0), 1); + + m_xGlobalOptionsLB->clear(); + + // tdf#125799, we let only the doc options grow/shrink but give this one more than its bare + // min request height because there's only one row in it and that looks somewhat abrupt + m_xGlobalOptionsCLB->set_size_request(-1, m_xGlobalOptionsCLB->get_preferred_size().Height() * 2); + + InitControls( rSet ); + + // set handler + m_xFormattingLB->connect_changed( LINK( this, SwCompatibilityOptPage, SelectHdl ) ); + m_xDefaultPB->connect_clicked( LINK( this, SwCompatibilityOptPage, UseAsDefaultHdl ) ); +} + +SwCompatibilityOptPage::~SwCompatibilityOptPage() +{ +} + +static sal_uInt32 convertBools2Ulong_Impl +( + bool _bUsePrtMetrics, + bool _bAddSpacing, + bool _bAddSpacingAtPages, + bool _bUseOurTabStops, + bool _bNoExtLeading, + bool _bUseLineSpacing, + bool _bAddTableSpacing, + bool _bAddTableLineSpacing, + bool _bUseObjPos, + bool _bUseOurTextWrapping, + bool _bConsiderWrappingStyle, + bool _bExpandWordSpace, + bool _bProtectForm, + bool _bMsWordCompTrailingBlanks, + bool bSubtractFlysAnchoredAtFlys, + bool bEmptyDbFieldHidesPara +) +{ + sal_uInt32 nRet = 0; + sal_uInt32 nSetBit = 1; + + if ( _bUsePrtMetrics ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bAddSpacing ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bAddSpacingAtPages ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bUseOurTabStops ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bNoExtLeading ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bUseLineSpacing ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bAddTableSpacing ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if (_bAddTableLineSpacing) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bUseObjPos ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bUseOurTextWrapping ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bConsiderWrappingStyle ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bExpandWordSpace ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bProtectForm ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if ( _bMsWordCompTrailingBlanks ) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if (bSubtractFlysAnchoredAtFlys) + nRet |= nSetBit; + nSetBit = nSetBit << 1; + if (bEmptyDbFieldHidesPara) + nRet |= nSetBit; + + return nRet; +} + +void SwCompatibilityOptPage::InitControls( const SfxItemSet& rSet ) +{ + // init objectshell and detect document name + OUString sDocTitle; + const SfxPoolItem* pItem = nullptr; + SfxObjectShell* pObjShell = nullptr; + if ( SfxItemState::SET == rSet.GetItemState( FN_PARAM_WRTSHELL, false, &pItem ) ) + m_pWrtShell = static_cast<SwWrtShell*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + if ( m_pWrtShell ) + { + pObjShell = m_pWrtShell->GetView().GetDocShell(); + if ( pObjShell ) + sDocTitle = pObjShell->GetTitle(); + } + else + { + m_xMain->set_sensitive(false); + m_xGlobalOptionsFrame->set_sensitive(false); + } + const OUString& rText = m_xMain->get_label(); + m_xMain->set_label(rText.replaceAll("%DOCNAME", sDocTitle)); + + // loading file formats + const Sequence< Sequence< PropertyValue > > aList = m_aConfigItem.GetList(); + + SvtCompatibilityEntry aEntry; + aEntry.setValue<bool>( SvtCompatibilityEntry::Index::ExpandWordSpace, false ); + + for ( const Sequence< PropertyValue >& rEntry : aList ) + { + for ( const PropertyValue& aValue : rEntry ) + { + aEntry.setValue( SvtCompatibilityEntry::getIndex(aValue.Name), aValue.Value ); + } + + const OUString sEntryName = aEntry.getValue<OUString>( SvtCompatibilityEntry::Index::Name ); + + const bool bIsUserEntry = ( sEntryName == SvtCompatibilityEntry::getUserEntryName() ); + const bool bIsDefaultEntry = ( sEntryName == SvtCompatibilityEntry::getDefaultEntryName() ); + + aEntry.setDefaultEntry( bIsDefaultEntry ); + + m_pImpl->m_aList.push_back( aEntry ); + + if ( aEntry.isDefaultEntry() ) + continue; + + OUString sNewEntry; + if ( bIsUserEntry ) + sNewEntry = m_sUserEntry; + + else if ( pObjShell && !sEntryName.isEmpty() ) + { + SfxFilterContainer* pFacCont = pObjShell->GetFactory().GetFilterContainer(); + std::shared_ptr<const SfxFilter> pFilter = pFacCont->GetFilter4FilterName( sEntryName ); + if ( pFilter ) + sNewEntry = pFilter->GetUIName(); + } + + if ( sNewEntry.isEmpty() ) + sNewEntry = sEntryName; + + sal_uInt32 nOptions = convertBools2Ulong_Impl( + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::UsePrtMetrics ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::AddSpacing ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::AddSpacingAtPages ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::UseOurTabStops ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::NoExtLeading ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::UseLineSpacing ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::AddTableSpacing ), + aEntry.getValue<bool>(SvtCompatibilityEntry::Index::AddTableLineSpacing), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::UseObjectPositioning ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::UseOurTextWrapping ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::ConsiderWrappingStyle ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::ExpandWordSpace ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::ProtectForm ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::MsWordTrailingBlanks ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys ), + aEntry.getValue<bool>( SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara ) ); + m_xFormattingLB->append(OUString::number(nOptions), sNewEntry); + } +} + +IMPL_LINK_NOARG(SwCompatibilityOptPage, SelectHdl, weld::ComboBox&, void) +{ + sal_uInt32 nOptions = m_xFormattingLB->get_active_id().toUInt32(); + SetCurrentOptions(nOptions); +} + +IMPL_LINK_NOARG(SwCompatibilityOptPage, UseAsDefaultHdl, weld::Button&, void) +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/querydefaultcompatdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQueryBox(xBuilder->weld_message_dialog("QueryDefaultCompatDialog")); + if (xQueryBox->run() == RET_YES) + { + auto pItem = std::find_if(m_pImpl->m_aList.begin(), m_pImpl->m_aList.end(), + [](const SvtCompatibilityEntry& rItem) { return rItem.isDefaultEntry(); }); + if (pItem != m_pImpl->m_aList.end()) + { + const sal_Int32 nCount = m_xOptionsLB->n_children(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + bool bChecked = m_xOptionsLB->get_toggle(i, 0); + + int nCoptIdx = i + 2; /* Consider "Name" & "Module" indexes */ + pItem->setValue<bool>( SvtCompatibilityEntry::Index(nCoptIdx), bChecked ); + if (nCoptIdx == int(SvtCompatibilityEntry::Index::AddTableSpacing)) + { + bool const isLineSpacing = m_xOptionsLB->get_toggle(i, 0) == TRISTATE_TRUE; + pItem->setValue<bool>(SvtCompatibilityEntry::Index::AddTableLineSpacing, isLineSpacing); + } + else + { + assert(m_xOptionsLB->get_toggle(i, 0) != TRISTATE_INDET); + } + } + } + + WriteOptions(); + } +} + +void SwCompatibilityOptPage::SetCurrentOptions( sal_uInt32 nOptions ) +{ + const int nCount = m_xOptionsLB->n_children(); + OSL_ENSURE( nCount <= 32, "SwCompatibilityOptPage::Reset(): entry overflow" ); + for (int i = 0; i < nCount; ++i) + { + bool bChecked = ( ( nOptions & 0x00000001 ) == 0x00000001 ); + TriState value = bChecked ? TRISTATE_TRUE : TRISTATE_FALSE; + if (i == int(SvtCompatibilityEntry::Index::AddTableSpacing) - 2) + { // hack: map 2 bools to 1 tristate + nOptions = nOptions >> 1; + if (value == TRISTATE_TRUE + && (nOptions & 0x00000001) != 0x00000001) // ADD_PARA_LINE_SPACING_TO_TABLE_CELLS + { + value = TRISTATE_INDET; // 3 values possible here + } + } + m_xOptionsLB->set_toggle(i, value, 0); + nOptions = nOptions >> 1; + } +} + +sal_uInt32 SwCompatibilityOptPage::GetDocumentOptions() const +{ + sal_uInt32 nRet = 0; + if ( m_pWrtShell ) + { + const IDocumentSettingAccess& rIDocumentSettingAccess = m_pWrtShell->getIDocumentSettingAccess(); + nRet = convertBools2Ulong_Impl( + !rIDocumentSettingAccess.get( DocumentSettingId::USE_VIRTUAL_DEVICE ), + rIDocumentSettingAccess.get( DocumentSettingId::PARA_SPACE_MAX ), + rIDocumentSettingAccess.get( DocumentSettingId::PARA_SPACE_MAX_AT_PAGES ), + !rIDocumentSettingAccess.get( DocumentSettingId::TAB_COMPAT ), + !rIDocumentSettingAccess.get( DocumentSettingId::ADD_EXT_LEADING ), + rIDocumentSettingAccess.get( DocumentSettingId::OLD_LINE_SPACING ), + rIDocumentSettingAccess.get( DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS ), + rIDocumentSettingAccess.get( DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS ), + rIDocumentSettingAccess.get( DocumentSettingId::USE_FORMER_OBJECT_POS ), + rIDocumentSettingAccess.get( DocumentSettingId::USE_FORMER_TEXT_WRAPPING ), + rIDocumentSettingAccess.get( DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION ), + !rIDocumentSettingAccess.get( DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK ), + rIDocumentSettingAccess.get( DocumentSettingId::PROTECT_FORM ), + rIDocumentSettingAccess.get( DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS ), + rIDocumentSettingAccess.get( DocumentSettingId::SUBTRACT_FLYS ), + rIDocumentSettingAccess.get( DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA ) ); + } + return nRet; +} + +void SwCompatibilityOptPage::WriteOptions() +{ + m_aConfigItem.Clear(); + for ( const auto& rItem : m_pImpl->m_aList ) + m_aConfigItem.AppendItem(rItem); +} + +std::unique_ptr<SfxTabPage> SwCompatibilityOptPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwCompatibilityOptPage>(pPage, pController, *rAttrSet); +} + +bool SwCompatibilityOptPage::FillItemSet( SfxItemSet* ) +{ + bool bModified = false; + if ( m_pWrtShell ) + { + sal_uInt32 nSavedOptions = m_nSavedOptions; + const int nCount = m_xOptionsLB->n_children(); + OSL_ENSURE( nCount <= 32, "SwCompatibilityOptPage::Reset(): entry overflow" ); + + for (int i = 0; i < nCount; ++i) + { + TriState const current = m_xOptionsLB->get_toggle(i, 0); + TriState saved = ((nSavedOptions & 0x00000001) == 0x00000001) ? TRISTATE_TRUE : TRISTATE_FALSE; + if (i == int(SvtCompatibilityEntry::Index::AddTableSpacing) - 2) + { // hack: map 2 bools to 1 tristate + nSavedOptions = nSavedOptions >> 1; + if (saved == TRISTATE_TRUE + && ((nSavedOptions & 0x00000001) != 0x00000001)) + { + saved = TRISTATE_INDET; + } + } + if (current != saved) + { + bool const bChecked(current != TRISTATE_FALSE); + assert(current != TRISTATE_INDET); // can't *change* it to that + int nCoptIdx = i + 2; /* Consider "Name" & "Module" indexes */ + switch ( SvtCompatibilityEntry::Index(nCoptIdx) ) + { + case SvtCompatibilityEntry::Index::UsePrtMetrics: + m_pWrtShell->SetUseVirDev( !bChecked ); + break; + + case SvtCompatibilityEntry::Index::AddSpacing: + m_pWrtShell->SetParaSpaceMax( bChecked ); + break; + + case SvtCompatibilityEntry::Index::AddSpacingAtPages: + m_pWrtShell->SetParaSpaceMaxAtPages( bChecked ); + break; + + case SvtCompatibilityEntry::Index::UseOurTabStops: + m_pWrtShell->SetTabCompat( !bChecked ); + break; + + case SvtCompatibilityEntry::Index::NoExtLeading: + m_pWrtShell->SetAddExtLeading( !bChecked ); + break; + + case SvtCompatibilityEntry::Index::UseLineSpacing: + m_pWrtShell->SetUseFormerLineSpacing( bChecked ); + break; + + case SvtCompatibilityEntry::Index::AddTableSpacing: + m_pWrtShell->SetAddParaSpacingToTableCells( bChecked ); + break; + + case SvtCompatibilityEntry::Index::UseObjectPositioning: + m_pWrtShell->SetUseFormerObjectPositioning( bChecked ); + break; + + case SvtCompatibilityEntry::Index::UseOurTextWrapping: + m_pWrtShell->SetUseFormerTextWrapping( bChecked ); + break; + + case SvtCompatibilityEntry::Index::ConsiderWrappingStyle: + m_pWrtShell->SetConsiderWrapOnObjPos( bChecked ); + break; + + case SvtCompatibilityEntry::Index::ExpandWordSpace: + m_pWrtShell->SetDoNotJustifyLinesWithManualBreak( !bChecked ); + break; + + case SvtCompatibilityEntry::Index::ProtectForm: + m_pWrtShell->SetProtectForm( bChecked ); + break; + + case SvtCompatibilityEntry::Index::MsWordTrailingBlanks: + m_pWrtShell->SetMsWordCompTrailingBlanks( bChecked ); + break; + + case SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys: + m_pWrtShell->SetSubtractFlysAnchoredAtFlys(bChecked); + break; + + case SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara: + m_pWrtShell->SetEmptyDbFieldHidesPara(bChecked); + break; + + default: + break; + } + bModified = true; + } + + nSavedOptions = nSavedOptions >> 1; + } + } + + if ( bModified ) + WriteOptions(); + + bool bNewMSFormsMenuOption = m_xGlobalOptionsCLB->get_toggle(0, 0); + if (m_bSavedMSFormsMenuOption != bNewMSFormsMenuOption) + { + m_aViewConfigItem.SetMSOCompatibleFormsMenu(bNewMSFormsMenuOption); + m_bSavedMSFormsMenuOption = bNewMSFormsMenuOption; + bModified = true; + + // Show a message about that the option needs a restart to be applied + { + SolarMutexGuard aGuard; + if (svtools::executeRestartDialog(comphelper::getProcessComponentContext(), + GetFrameWeld(), svtools::RESTART_REASON_MSCOMPATIBLE_FORMS_MENU)) + { + GetDialogController()->response(RET_OK); + } + } + } + + return bModified; +} + +void SwCompatibilityOptPage::Reset( const SfxItemSet* ) +{ + m_xOptionsLB->select(0); + + sal_uInt32 nOptions = GetDocumentOptions(); + SetCurrentOptions( nOptions ); + m_nSavedOptions = nOptions; + + m_xGlobalOptionsCLB->set_toggle(0, m_aViewConfigItem.HasMSOCompatibleFormsMenu() ? TRISTATE_TRUE : TRISTATE_FALSE, 0); + m_bSavedMSFormsMenuOption = m_aViewConfigItem.HasMSOCompatibleFormsMenu(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/config/optload.cxx b/sw/source/ui/config/optload.cxx new file mode 100644 index 000000000..97f24f093 --- /dev/null +++ b/sw/source/ui/config/optload.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 <officecfg/Office/Writer.hxx> +#include <comphelper/string.hxx> +#include <swtypes.hxx> +#include <uiitems.hxx> +#include <modcfg.hxx> +#include <swmodule.hxx> +#include <usrpref.hxx> +#include <wrtsh.hxx> +#include <linkenum.hxx> +#include <uitool.hxx> +#include <view.hxx> + +#include <strings.hrc> +#include <optload.hrc> +#include <cmdid.h> + +#include <optload.hxx> +#include <sfx2/htmlmode.hxx> +#include <fldmgr.hxx> +#include <poolfmt.hxx> +#include <expfld.hxx> + +#include <svtools/insdlg.hxx> +#include <svtools/unitconv.hxx> +#include <tools/resary.hxx> +#include <vcl/settings.hxx> + +#include <comphelper/classids.hxx> +#include <unotools/configmgr.hxx> +#include <SwStyleNameMapper.hxx> +#include <numrule.hxx> + +#include <doc.hxx> +#include <svl/cjkoptions.hxx> + +using namespace ::com::sun::star; + +#include <svl/eitem.hxx> + +sal_uInt32 SwFieldUnitTable::Count() +{ + return SAL_N_ELEMENTS(STR_ARR_METRIC); +} + +OUString SwFieldUnitTable::GetString(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return SwResId(STR_ARR_METRIC[nPos].first); + return OUString(); +} + +FieldUnit SwFieldUnitTable::GetValue(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return STR_ARR_METRIC[nPos].second; + return FieldUnit::NONE; +} + +SwLoadOptPage::SwLoadOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optgeneralpage.ui", "OptGeneralPage", &rSet) + , m_pWrtShell(nullptr) + , m_nLastTab(0) + , m_nOldLinkMode(MANUAL) + , m_xAlwaysRB(m_xBuilder->weld_radio_button("always")) + , m_xRequestRB(m_xBuilder->weld_radio_button("onrequest")) + , m_xNeverRB(m_xBuilder->weld_radio_button("never")) + , m_xAutoUpdateFields(m_xBuilder->weld_check_button("updatefields")) + , m_xAutoUpdateCharts(m_xBuilder->weld_check_button("updatecharts")) + , m_xMetricLB(m_xBuilder->weld_combo_box("metric")) + , m_xTabFT(m_xBuilder->weld_label("tablabel")) + , m_xTabMF(m_xBuilder->weld_metric_spin_button("tab", FieldUnit::CM)) + , m_xUseSquaredPageMode(m_xBuilder->weld_check_button("squaremode")) + , m_xUseCharUnit(m_xBuilder->weld_check_button("usecharunit")) + , m_xWordCountED(m_xBuilder->weld_entry("wordcount")) + , m_xShowStandardizedPageCount(m_xBuilder->weld_check_button("standardizedpageshow")) + , m_xStandardizedPageSizeNF(m_xBuilder->weld_spin_button("standardpagesize")) +{ + for (sal_uInt32 i = 0; i < SwFieldUnitTable::Count(); ++i) + { + const OUString sMetric = SwFieldUnitTable::GetString(i); + FieldUnit eFUnit = SwFieldUnitTable::GetValue(i); + + switch ( eFUnit ) + { + case FieldUnit::MM: + case FieldUnit::CM: + case FieldUnit::POINT: + case FieldUnit::PICA: + case FieldUnit::INCH: + { + // use only these metrics + m_xMetricLB->append(OUString::number(static_cast<sal_uInt32>(eFUnit)), sMetric); + break; + } + default:; //prevent warning + } + } + m_xMetricLB->connect_changed(LINK(this, SwLoadOptPage, MetricHdl)); + + const SfxPoolItem* pItem; + if (SfxItemState::SET == rSet.GetItemState(SID_HTML_MODE, false, &pItem) + && static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON) + { + m_xTabFT->hide(); + m_xTabMF->hide(); + } + + SvtCJKOptions aCJKOptions; + if(!aCJKOptions.IsAsianTypographyEnabled()) + { + m_xUseSquaredPageMode->hide(); + m_xUseCharUnit->hide(); + } + + Link<weld::Button&,void> aLink = LINK(this, SwLoadOptPage, StandardizedPageCountCheckHdl); + m_xShowStandardizedPageCount->connect_clicked(aLink); +} + +SwLoadOptPage::~SwLoadOptPage() +{ +} + +std::unique_ptr<SfxTabPage> SwLoadOptPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwLoadOptPage>(pPage, pController, *rAttrSet ); +} + +IMPL_LINK_NOARG(SwLoadOptPage, StandardizedPageCountCheckHdl, weld::Button&, void) +{ + m_xStandardizedPageSizeNF->set_sensitive(m_xShowStandardizedPageCount->get_active()); +} + +bool SwLoadOptPage::FillItemSet( SfxItemSet* rSet ) +{ + bool bRet = false; + SwModule* pMod = SW_MOD(); + + sal_Int32 nNewLinkMode = AUTOMATIC; + if (m_xNeverRB->get_active()) + nNewLinkMode = NEVER; + else if (m_xRequestRB->get_active()) + nNewLinkMode = MANUAL; + + SwFieldUpdateFlags eFieldFlags = m_xAutoUpdateFields->get_active() ? + m_xAutoUpdateCharts->get_active() ? AUTOUPD_FIELD_AND_CHARTS : AUTOUPD_FIELD_ONLY : AUTOUPD_OFF; + + if (m_xAutoUpdateFields->get_state_changed_from_saved() || + m_xAutoUpdateCharts->get_state_changed_from_saved()) + { + pMod->ApplyFieldUpdateFlags(eFieldFlags); + if(m_pWrtShell) + { + m_pWrtShell->SetFieldUpdateFlags(eFieldFlags); + m_pWrtShell->SetModified(); + } + } + + if (nNewLinkMode != m_nOldLinkMode) + { + pMod->ApplyLinkMode(nNewLinkMode); + if (m_pWrtShell) + { + m_pWrtShell->SetLinkUpdMode( nNewLinkMode ); + m_pWrtShell->SetModified(); + } + + bRet = true; + } + + const sal_Int32 nMPos = m_xMetricLB->get_active(); + if (m_xMetricLB->get_value_changed_from_saved()) + { + // Double-Cast for VA3.0 + const sal_uInt16 nFieldUnit = m_xMetricLB->get_id(nMPos).toUInt32(); + rSet->Put( SfxUInt16Item( SID_ATTR_METRIC, nFieldUnit ) ); + bRet = true; + } + + if (m_xTabMF->get_visible() && m_xTabMF->get_value_changed_from_saved()) + { + rSet->Put(SfxUInt16Item(SID_ATTR_DEFTABSTOP, + static_cast<sal_uInt16>(m_xTabMF->denormalize(m_xTabMF->get_value(FieldUnit::TWIP))))); + bRet = true; + } + + bool bIsUseCharUnitFlag = m_xUseCharUnit->get_active(); + SvtCJKOptions aCJKOptions; + bIsUseCharUnitFlag = bIsUseCharUnitFlag && aCJKOptions.IsAsianTypographyEnabled(); + if( (bIsUseCharUnitFlag ? 1 : 0) != m_xUseCharUnit->get_saved_state()) + { + rSet->Put(SfxBoolItem(SID_ATTR_APPLYCHARUNIT, bIsUseCharUnitFlag )); + bRet = true; + } + + if (m_xWordCountED->get_value_changed_from_saved()) + { + std::shared_ptr< comphelper::ConfigurationChanges > batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::WordCount::AdditionalSeparators::set(m_xWordCountED->get_text(), batch); + batch->commit(); + bRet = true; + } + + if (m_xShowStandardizedPageCount->get_state_changed_from_saved()) + { + std::shared_ptr< comphelper::ConfigurationChanges > batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::WordCount::ShowStandardizedPageCount::set( + m_xShowStandardizedPageCount->get_active(), + batch); + batch->commit(); + bRet = true; + } + + if (m_xStandardizedPageSizeNF->get_value_changed_from_saved()) + { + std::shared_ptr< comphelper::ConfigurationChanges > batch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Writer::WordCount::StandardizedPageSize::set( + m_xStandardizedPageSizeNF->get_value(), + batch); + batch->commit(); + bRet = true; + } + + if (m_xUseSquaredPageMode->get_state_changed_from_saved()) + { + bool bIsSquaredPageModeFlag = m_xUseSquaredPageMode->get_active(); + pMod->ApplyDefaultPageMode( bIsSquaredPageModeFlag ); + if ( m_pWrtShell ) + { + SwDoc* pDoc = m_pWrtShell->GetDoc(); + pDoc->SetDefaultPageMode( bIsSquaredPageModeFlag ); + m_pWrtShell->SetModified(); + } + bRet = true; + } + + return bRet; +} + +void SwLoadOptPage::Reset( const SfxItemSet* rSet) +{ + const SwMasterUsrPref* pUsrPref = SW_MOD()->GetUsrPref(false); + const SfxPoolItem* pItem; + + if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_WRTSHELL, false, &pItem)) + m_pWrtShell = static_cast<SwWrtShell*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + + SwFieldUpdateFlags eFieldFlags = AUTOUPD_GLOBALSETTING; + m_nOldLinkMode = GLOBALSETTING; + if (m_pWrtShell) + { + eFieldFlags = m_pWrtShell->GetFieldUpdateFlags(); + m_nOldLinkMode = m_pWrtShell->GetLinkUpdMode(); + } + if(GLOBALSETTING == m_nOldLinkMode) + m_nOldLinkMode = pUsrPref->GetUpdateLinkMode(); + if(AUTOUPD_GLOBALSETTING == eFieldFlags) + eFieldFlags = pUsrPref->GetFieldUpdateFlags(); + + m_xAutoUpdateFields->set_active(eFieldFlags != AUTOUPD_OFF); + m_xAutoUpdateCharts->set_active(eFieldFlags == AUTOUPD_FIELD_AND_CHARTS); + + switch (m_nOldLinkMode) + { + case NEVER: m_xNeverRB->set_active(true); break; + case MANUAL: m_xRequestRB->set_active(true); break; + case AUTOMATIC: m_xAlwaysRB->set_active(true); break; + } + + m_xAutoUpdateFields->save_state(); + m_xAutoUpdateCharts->save_state(); + m_xMetricLB->set_active(-1); + if ( rSet->GetItemState( SID_ATTR_METRIC ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = rSet->Get( SID_ATTR_METRIC ); + FieldUnit eFieldUnit = static_cast<FieldUnit>(rItem.GetValue()); + + for (sal_Int32 i = 0, nEntryCount = m_xMetricLB->get_count(); i < nEntryCount; ++i) + { + if (m_xMetricLB->get_id(i).toUInt32() == static_cast<sal_uInt32>(eFieldUnit)) + { + m_xMetricLB->set_active(i); + break; + } + } + ::SetFieldUnit(*m_xTabMF, eFieldUnit); + } + m_xMetricLB->save_value(); + if(SfxItemState::SET == rSet->GetItemState(SID_ATTR_DEFTABSTOP, false, &pItem)) + { + m_nLastTab = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + m_xTabMF->set_value(m_xTabMF->normalize(m_nLastTab), FieldUnit::TWIP); + } + m_xTabMF->save_value(); + + //default page mode loading + if(m_pWrtShell) + { + bool bSquaredPageMode = m_pWrtShell->GetDoc()->IsSquaredPageMode(); + m_xUseSquaredPageMode->set_active( bSquaredPageMode ); + m_xUseSquaredPageMode->save_state(); + } + + if(SfxItemState::SET == rSet->GetItemState(SID_ATTR_APPLYCHARUNIT, false, &pItem)) + { + bool bUseCharUnit = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + m_xUseCharUnit->set_active(bUseCharUnit); + } + else + { + m_xUseCharUnit->set_active(pUsrPref->IsApplyCharUnit()); + } + m_xUseCharUnit->save_state(); + + m_xWordCountED->set_text(officecfg::Office::Writer::WordCount::AdditionalSeparators::get()); + m_xWordCountED->set_sensitive(!officecfg::Office::Writer::WordCount::AdditionalSeparators::isReadOnly()); + m_xWordCountED->save_value(); + m_xShowStandardizedPageCount->set_active(officecfg::Office::Writer::WordCount::ShowStandardizedPageCount::get()); + m_xShowStandardizedPageCount->set_sensitive(!officecfg::Office::Writer::WordCount::ShowStandardizedPageCount::isReadOnly()); + m_xShowStandardizedPageCount->save_state(); + m_xStandardizedPageSizeNF->set_value(officecfg::Office::Writer::WordCount::StandardizedPageSize::get()); + m_xStandardizedPageSizeNF->set_sensitive(!officecfg::Office::Writer::WordCount::StandardizedPageSize::isReadOnly()); + m_xStandardizedPageSizeNF->save_value(); + m_xStandardizedPageSizeNF->set_sensitive(m_xShowStandardizedPageCount->get_active()); +} + +IMPL_LINK_NOARG(SwLoadOptPage, MetricHdl, weld::ComboBox&, void) +{ + const sal_Int32 nMPos = m_xMetricLB->get_active(); + if(nMPos != -1) + { + // Double-Cast for VA3.0 + FieldUnit eFieldUnit = static_cast<FieldUnit>(m_xMetricLB->get_id(nMPos).toUInt32()); + bool bModified = m_xTabMF->get_value_changed_from_saved(); + long nVal = bModified ? + sal::static_int_cast<sal_Int32, sal_Int64 >( m_xTabMF->denormalize( m_xTabMF->get_value( FieldUnit::TWIP ) )) : + m_nLastTab; + ::SetFieldUnit( *m_xTabMF, eFieldUnit ); + m_xTabMF->set_value( m_xTabMF->normalize( nVal ), FieldUnit::TWIP ); + if (!bModified) + m_xTabMF->save_value(); + } +} + +SwCaptionOptDlg::SwCaptionOptDlg(weld::Window* pParent, const SfxItemSet& rSet) + : SfxSingleTabDialogController(pParent, &rSet, "modules/swriter/ui/captiondialog.ui", + "CaptionDialog") +{ + // create TabPage + SetTabPage(SwCaptionOptPage::Create(get_content_area(), this, &rSet)); +} + +SwCaptionPreview::SwCaptionPreview() + : mbFontInitialized(false) +{ +} + +void SwCaptionPreview::ApplySettings(vcl::RenderContext& rRenderContext) +{ + Wallpaper aBack(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + rRenderContext.SetBackground(aBack); + rRenderContext.SetFillColor(aBack.GetColor()); + rRenderContext.SetLineColor(aBack.GetColor()); + + if (!mbFontInitialized) + { + maFont = rRenderContext.GetFont(); + maFont.SetFontHeight(maFont.GetFontHeight() * 120 / 100); + mbFontInitialized = true; + } + rRenderContext.SetFont(maFont); +} + +void SwCaptionPreview::SetPreviewText(const OUString& rText) +{ + if (rText != maText) + { + maText = rText; + Invalidate(); + } +} + +void SwCaptionPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(106 , 20), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); +} + +void SwCaptionPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + ApplySettings(rRenderContext); + + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), GetOutputSizePixel())); + rRenderContext.DrawText(Point(4, 6), maText); +} + +IMPL_LINK(SwCaptionOptPage, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aTextFilter.filter(rTest); + return true; +} + +SwCaptionOptPage::SwCaptionOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optcaptionpage.ui", "OptCaptionPage", &rSet) + , m_sSWTable(SwResId(STR_CAPTION_TABLE)) + , m_sSWFrame(SwResId(STR_CAPTION_FRAME)) + , m_sSWGraphic(SwResId(STR_CAPTION_GRAPHIC)) + , m_sOLE(SwResId(STR_CAPTION_OLE)) + , m_sBegin(SwResId(STR_CAPTION_BEGINNING)) + , m_sEnd(SwResId(STR_CAPTION_END)) + , m_sAbove(SwResId(STR_CAPTION_ABOVE)) + , m_sBelow(SwResId(STR_CAPTION_BELOW)) + , m_sNone(SwResId(SW_STR_NONE)) + , m_nPrevSelectedEntry(-1) + , pMgr(new SwFieldMgr) + , bHTMLMode(false) + , m_aTextFilter(m_sNone) + , m_xCheckLB(m_xBuilder->weld_tree_view("objects")) + , m_xLbCaptionOrder(m_xBuilder->weld_combo_box("captionorder")) + , m_xSettingsGroup(m_xBuilder->weld_widget("settings")) + , m_xCategoryBox(m_xBuilder->weld_combo_box("category")) + , m_xFormatText(m_xBuilder->weld_label("numberingft")) + , m_xFormatBox(m_xBuilder->weld_combo_box("numbering")) + , m_xNumberingSeparatorFT(m_xBuilder->weld_label("numseparatorft")) + , m_xNumberingSeparatorED(m_xBuilder->weld_entry("numseparator")) + , m_xTextText(m_xBuilder->weld_label("separatorft")) + , m_xTextEdit(m_xBuilder->weld_entry("separator")) + , m_xPosBox(m_xBuilder->weld_combo_box("position")) + , m_xNumCapt(m_xBuilder->weld_widget("numcaption")) + , m_xLbLevel(m_xBuilder->weld_combo_box("level")) + , m_xEdDelim(m_xBuilder->weld_entry("chapseparator")) + , m_xCategory(m_xBuilder->weld_widget("categoryformat")) + , m_xCharStyleLB(m_xBuilder->weld_combo_box("charstyle")) + , m_xApplyBorderCB(m_xBuilder->weld_check_button("applyborder")) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreview)) +{ + m_xCategoryBox->connect_entry_insert_text(LINK(this, SwCaptionOptPage, TextFilterHdl)); + + std::vector<int> aWidths; + aWidths.push_back(m_xCheckLB->get_checkbox_column_width()); + m_xCheckLB->set_column_fixed_widths(aWidths); + + SwStyleNameMapper::FillUIName(RES_POOLCOLL_LABEL_ABB, m_sIllustration); + SwStyleNameMapper::FillUIName(RES_POOLCOLL_LABEL_TABLE, m_sTable); + SwStyleNameMapper::FillUIName(RES_POOLCOLL_LABEL_FRAME, m_sText); + SwStyleNameMapper::FillUIName(RES_POOLCOLL_LABEL_DRAWING, m_sDrawing); + + SwWrtShell* pSh = ::GetActiveWrtShell(); + + // m_xFormatBox + sal_uInt16 nSelFormat = SVX_NUM_ARABIC; + if (pSh) + { + for ( auto i = pMgr->GetFieldTypeCount(); i; ) + { + SwFieldType* pFieldType = pMgr->GetFieldType(SwFieldIds::Unknown, --i); + if (!pFieldType->GetName().isEmpty() + && pFieldType->GetName() == m_xCategoryBox->get_active_text()) + { + nSelFormat = static_cast<sal_uInt16>(static_cast<SwSetExpFieldType*>(pFieldType)->GetSeqFormat()); + break; + } + } + + ::FillCharStyleListBox( *m_xCharStyleLB, pSh->GetView().GetDocShell(), true, true ); + } + + const sal_uInt16 nCount = pMgr->GetFormatCount(SwFieldTypesEnum::Sequence, false); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + { + const sal_uInt16 nFormatId = pMgr->GetFormatId(SwFieldTypesEnum::Sequence, i); + m_xFormatBox->append(OUString::number(nFormatId), pMgr->GetFormatStr(SwFieldTypesEnum::Sequence, i)); + if (nFormatId == nSelFormat) + m_xFormatBox->set_active(i); + } + + for (int i = 0; i < MAXLEVEL; ++i) + m_xLbLevel->append_text(OUString::number(i + 1)); + + sal_Unicode nLvl = MAXLEVEL; + OUString sDelim(": "); + + if (pSh) + { + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(pMgr->GetFieldType( + SwFieldIds::SetExp, m_xCategoryBox->get_active_text() )); + if( pFieldType ) + { + sDelim = pFieldType->GetDelimiter(); + nLvl = pFieldType->GetOutlineLvl(); + } + } + + m_xLbLevel->set_active(nLvl < MAXLEVEL ? nLvl + 1 : 0); + m_xEdDelim->set_text(sDelim); + + m_xCategoryBox->connect_changed(LINK(this, SwCaptionOptPage, ModifyComboHdl)); + Link<weld::Entry&,void> aLk = LINK(this, SwCaptionOptPage, ModifyEntryHdl); + m_xNumberingSeparatorED->connect_changed(aLk); + m_xTextEdit->connect_changed(aLk); + + m_xCategoryBox->connect_changed(LINK(this, SwCaptionOptPage, SelectHdl)); + m_xFormatBox->connect_changed(LINK(this, SwCaptionOptPage, SelectListBoxHdl)); + + m_xLbCaptionOrder->connect_changed(LINK(this, SwCaptionOptPage, OrderHdl)); + + m_xCheckLB->connect_changed(LINK(this, SwCaptionOptPage, ShowEntryHdl)); + m_xCheckLB->connect_toggled(LINK(this, SwCaptionOptPage, ToggleEntryHdl)); +} + +SwCaptionOptPage::~SwCaptionOptPage() +{ + DelUserData(); + pMgr.reset(); + m_xPreview.reset(); +} + +std::unique_ptr<SfxTabPage> SwCaptionOptPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwCaptionOptPage>(pPage, pController, *rAttrSet); +} + +bool SwCaptionOptPage::FillItemSet( SfxItemSet* ) +{ + bool bRet = false; + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + SaveEntry(m_xCheckLB->get_selected_index()); // apply current entry + + int nCheckCount = 0; + for (int i = 0, nCount = m_xCheckLB->n_children(); i < nCount; ++i) + { + if (m_xCheckLB->get_toggle(i, 0) == TRISTATE_TRUE) + ++nCheckCount; + InsCaptionOpt* pData = reinterpret_cast<InsCaptionOpt*>(m_xCheckLB->get_id(i).toInt64()); + bRet |= pModOpt->SetCapOption(bHTMLMode, pData); + } + + pModOpt->SetInsWithCaption(bHTMLMode, nCheckCount > 0); + + int nPos = m_xLbCaptionOrder->get_active(); + pModOpt->SetCaptionOrderNumberingFirst(nPos == 1); + + return bRet; +} + +void SwCaptionOptPage::Reset( const SfxItemSet* rSet) +{ + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet->GetItemState(SID_HTML_MODE, false, &pItem)) + { + bHTMLMode = 0 != (static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON); + } + + DelUserData(); + m_xCheckLB->clear(); // remove all entries + + // Writer objects + int nPos = 0; + m_xCheckLB->append(); + m_xCheckLB->set_toggle(nPos, TRISTATE_FALSE, 0); + m_xCheckLB->set_text(nPos, m_sSWTable, 1); + SetOptions(nPos++, TABLE_CAP); + m_xCheckLB->append(); + m_xCheckLB->set_toggle(nPos, TRISTATE_FALSE, 0); + m_xCheckLB->set_text(nPos, m_sSWFrame, 1); + SetOptions(nPos++, FRAME_CAP); + m_xCheckLB->append(); + m_xCheckLB->set_toggle(nPos, TRISTATE_FALSE, 0); + m_xCheckLB->set_text(nPos, m_sSWGraphic, 1); + SetOptions(nPos++, GRAPHIC_CAP); + + // get Productname and -version + const OUString sWithoutVersion( utl::ConfigManager::getProductName() ); + const OUString sComplete( + sWithoutVersion + " " + + utl::ConfigManager::getProductVersion() ); + + SvObjectServerList aObjS; + aObjS.FillInsertObjects(); + aObjS.Remove( SvGlobalName( SO3_SW_CLASSID ) ); // remove Writer-ID + + for ( sal_uLong i = 0; i < aObjS.Count(); ++i ) + { + const SvGlobalName &rOleId = aObjS[i].GetClassName(); + OUString sClass; + if (rOleId == SvGlobalName(SO3_OUT_CLASSID)) + sClass = m_sOLE; + else + sClass = aObjS[i].GetHumanName(); + // don't show product version + sClass = sClass.replaceFirst( sComplete, sWithoutVersion ); + m_xCheckLB->append(); + m_xCheckLB->set_toggle(nPos, TRISTATE_FALSE, 0); + m_xCheckLB->set_text(nPos, sClass, 1); + SetOptions( nPos++, OLE_CAP, &rOleId ); + } + m_xLbCaptionOrder->set_active( + SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst() ? 1 : 0); + m_xCheckLB->select(0); + ShowEntryHdl(*m_xCheckLB); +} + +void SwCaptionOptPage::SetOptions(const sal_uLong nPos, + const SwCapObjType eObjType, const SvGlobalName *pOleId) +{ + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + const InsCaptionOpt* pOpt = pModOpt->GetCapOption(bHTMLMode, eObjType, pOleId); + + if (pOpt) + { + InsCaptionOpt* pIns = new InsCaptionOpt(*pOpt); + m_xCheckLB->set_id(nPos, OUString::number(reinterpret_cast<sal_Int64>(pIns))); + m_xCheckLB->set_toggle(nPos, pOpt->UseCaption() ? TRISTATE_TRUE : TRISTATE_FALSE, 0); + } + else + { + InsCaptionOpt* pIns = new InsCaptionOpt(eObjType, pOleId); + m_xCheckLB->set_id(nPos, OUString::number(reinterpret_cast<sal_Int64>(pIns))); + } +} + +void SwCaptionOptPage::DelUserData() +{ + for (int i = 0, nCount = m_xCheckLB->n_children(); i < nCount; ++i) + { + delete reinterpret_cast<InsCaptionOpt*>(m_xCheckLB->get_id(i).toInt64()); + m_xCheckLB->set_id(i, "0"); + } +} + +void SwCaptionOptPage::UpdateEntry(int nSelEntry) +{ + if (nSelEntry != -1) + { + bool bChecked = m_xCheckLB->get_toggle(nSelEntry, 0) == TRISTATE_TRUE; + + m_xSettingsGroup->set_sensitive(bChecked); + bool bNumSep = bChecked && m_xLbCaptionOrder->get_active() == 1; + m_xNumberingSeparatorED->set_sensitive( bNumSep ); + m_xNumberingSeparatorFT->set_sensitive( bNumSep ); + + m_xNumCapt->set_sensitive(bChecked); + m_xCategory->set_sensitive(bChecked); + m_xPreview->set_sensitive(bChecked); + + SwWrtShell *pSh = ::GetActiveWrtShell(); + + InsCaptionOpt* pOpt = reinterpret_cast<InsCaptionOpt*>(m_xCheckLB->get_id(nSelEntry).toInt64()); + + m_xCategoryBox->clear(); + m_xCategoryBox->append_text(m_sNone); + if (pSh) + { + const size_t nCount = pMgr->GetFieldTypeCount(); + + for (size_t i = 0; i < nCount; ++i) + { + SwFieldType *pType = pMgr->GetFieldType( SwFieldIds::Unknown, i ); + if( pType->Which() == SwFieldIds::SetExp && + static_cast<SwSetExpFieldType *>( pType)->GetType() & nsSwGetSetExpType::GSE_SEQ ) + { + m_xCategoryBox->append_text(pType->GetName()); + } + } + } + else + { + m_xCategoryBox->append_text(m_sIllustration); + m_xCategoryBox->append_text(m_sTable); + m_xCategoryBox->append_text(m_sText); + m_xCategoryBox->append_text(m_sDrawing); + } + + if (!pOpt->GetCategory().isEmpty()) + { + if (m_xCategoryBox->find_text(pOpt->GetCategory()) == -1) + m_xCategoryBox->insert_text(0, pOpt->GetCategory()); + m_xCategoryBox->set_active_text(pOpt->GetCategory()); + } + else + m_xCategoryBox->set_active_text(m_sNone); + + if (m_xCategoryBox->get_active_text().isEmpty()) + { + sal_Int32 nPos = 0; + switch(pOpt->GetObjType()) + { + case OLE_CAP: + case GRAPHIC_CAP: nPos = 1; break; + case TABLE_CAP: nPos = 2; break; + case FRAME_CAP: nPos = 3; break; + } + m_xCategoryBox->set_active(nPos); + } + + for (sal_Int32 i = 0; i < m_xFormatBox->get_count(); i++) + { + if (pOpt->GetNumType() == m_xFormatBox->get_id(i).toUInt32()) + { + m_xFormatBox->set_active(i); + break; + } + } + m_xTextEdit->set_text(pOpt->GetCaption()); + + m_xPosBox->clear(); + switch (pOpt->GetObjType()) + { + case GRAPHIC_CAP: + case TABLE_CAP: + case OLE_CAP: + m_xPosBox->append_text(m_sAbove); + m_xPosBox->append_text(m_sBelow); + break; + case FRAME_CAP: + m_xPosBox->append_text(m_sBegin); + m_xPosBox->append_text(m_sEnd); + break; + } + m_xPosBox->set_active(pOpt->GetPos()); + + sal_Int32 nLevelPos = ( pOpt->GetLevel() < MAXLEVEL ) ? pOpt->GetLevel() + 1 : 0; + m_xLbLevel->set_active(nLevelPos); + m_xEdDelim->set_text(pOpt->GetSeparator()); + m_xNumberingSeparatorED->set_text(pOpt->GetNumSeparator()); + if (!pOpt->GetCharacterStyle().isEmpty()) + m_xCharStyleLB->set_active_text(pOpt->GetCharacterStyle()); + else + m_xCharStyleLB->set_active(0); + m_xApplyBorderCB->set_sensitive(m_xCategoryBox->get_sensitive() && + pOpt->GetObjType() != TABLE_CAP && pOpt->GetObjType() != FRAME_CAP ); + m_xApplyBorderCB->set_active(pOpt->CopyAttributes()); + } + + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwCaptionOptPage, ShowEntryHdl, weld::TreeView&, void) +{ + if (m_nPrevSelectedEntry != -1) + SaveEntry(m_nPrevSelectedEntry); + UpdateEntry(m_xCheckLB->get_selected_index()); + m_nPrevSelectedEntry = m_xCheckLB->get_selected_index(); +} + +IMPL_LINK(SwCaptionOptPage, ToggleEntryHdl, const row_col&, rRowCol, void) +{ + UpdateEntry(rRowCol.first); +} + +void SwCaptionOptPage::SaveEntry(int nEntry) +{ + if (nEntry == -1) + return; + + InsCaptionOpt* pOpt = reinterpret_cast<InsCaptionOpt*>(m_xCheckLB->get_id(nEntry).toInt64()); + + pOpt->UseCaption() = m_xCheckLB->get_toggle(nEntry, 0) == TRISTATE_TRUE; + const OUString aName(m_xCategoryBox->get_active_text()); + if (aName == m_sNone) + pOpt->SetCategory(""); + else + pOpt->SetCategory(comphelper::string::strip(aName, ' ')); + pOpt->SetNumType(m_xFormatBox->get_active_id().toUInt32()); + pOpt->SetCaption(m_xTextEdit->get_sensitive() ? m_xTextEdit->get_text() : OUString() ); + pOpt->SetPos(m_xPosBox->get_active()); + int nPos = m_xLbLevel->get_active(); + sal_Int32 nLevel = (nPos > 0) ? nPos - 1 : MAXLEVEL; + pOpt->SetLevel(nLevel); + pOpt->SetSeparator(m_xEdDelim->get_text()); + pOpt->SetNumSeparator(m_xNumberingSeparatorED->get_text()); + if (m_xCharStyleLB->get_active() == -1) + pOpt->SetCharacterStyle(""); + else + pOpt->SetCharacterStyle(m_xCharStyleLB->get_active_text()); + pOpt->CopyAttributes() = m_xApplyBorderCB->get_active(); +} + +void SwCaptionOptPage::ModifyHdl() +{ + const OUString sFieldTypeName = m_xCategoryBox->get_active_text(); + + if (SfxSingleTabDialogController* pDlg = dynamic_cast<SfxSingleTabDialogController*>(GetDialogController())) + pDlg->GetOKButton().set_sensitive(!sFieldTypeName.isEmpty()); + bool bEnable = m_xCategoryBox->get_sensitive() && sFieldTypeName != m_sNone; + + m_xFormatText->set_sensitive(bEnable); + m_xFormatBox->set_sensitive(bEnable); + m_xTextText->set_sensitive(bEnable); + m_xTextEdit->set_sensitive(bEnable); + + InvalidatePreview(); +} + +IMPL_LINK_NOARG(SwCaptionOptPage, ModifyEntryHdl, weld::Entry&, void) +{ + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwCaptionOptPage, ModifyComboHdl, weld::ComboBox&, void) +{ + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwCaptionOptPage, SelectHdl, weld::ComboBox&, void) +{ + InvalidatePreview(); +} + +IMPL_LINK_NOARG(SwCaptionOptPage, SelectListBoxHdl, weld::ComboBox&, void) +{ + InvalidatePreview(); +} + +IMPL_LINK(SwCaptionOptPage, OrderHdl, weld::ComboBox&, rBox, void) +{ + InvalidatePreview(); + + int nSelEntry = m_xCheckLB->get_selected_index(); + bool bChecked = false; + if (nSelEntry != -1) + { + bChecked = m_xCheckLB->get_toggle(nSelEntry, 0) == TRISTATE_TRUE; + } + + int nPos = rBox.get_active(); + m_xNumberingSeparatorFT->set_sensitive(bChecked && nPos == 1); + m_xNumberingSeparatorED->set_sensitive(bChecked && nPos == 1); +} + +void SwCaptionOptPage::InvalidatePreview() +{ + OUString aStr; + + if (m_xCategoryBox->get_active_text() != m_sNone) + { + //#i61007# order of captions + bool bOrderNumberingFirst = m_xLbCaptionOrder->get_active() == 1; + // number + const sal_uInt16 nNumFormat = m_xFormatBox->get_active_id().toUInt32(); + if (SVX_NUM_NUMBER_NONE != nNumFormat) + { + //#i61007# order of captions + if( !bOrderNumberingFirst ) + { + // category + aStr += m_xCategoryBox->get_active_text() + " "; + } + + SwWrtShell *pSh = ::GetActiveWrtShell(); + if (pSh) + { + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(pMgr->GetFieldType( + SwFieldIds::SetExp, m_xCategoryBox->get_active_text() )); + if( pFieldType && pFieldType->GetOutlineLvl() < MAXLEVEL ) + { + sal_uInt8 nLvl = pFieldType->GetOutlineLvl(); + SwNumberTree::tNumberVector aNumVector; + for( sal_uInt8 i = 0; i <= nLvl; ++i ) + aNumVector.push_back(1); + + const OUString sNumber( pSh->GetOutlineNumRule()->MakeNumString( + aNumVector, false )); + if( !sNumber.isEmpty() ) + aStr += sNumber + pFieldType->GetDelimiter(); + } + } + + switch( nNumFormat ) + { + case SVX_NUM_CHARS_UPPER_LETTER: aStr += "A"; break; + case SVX_NUM_CHARS_UPPER_LETTER_N: aStr += "A"; break; + case SVX_NUM_CHARS_LOWER_LETTER: aStr += "a"; break; + case SVX_NUM_CHARS_LOWER_LETTER_N: aStr += "a"; break; + case SVX_NUM_ROMAN_UPPER: aStr += "I"; break; + case SVX_NUM_ROMAN_LOWER: aStr += "i"; break; + //case ARABIC: + default: aStr += "1"; break; + } + } + //#i61007# order of captions + if( bOrderNumberingFirst ) + { + aStr += m_xNumberingSeparatorED->get_text() + m_xCategoryBox->get_active_text(); + } + aStr += m_xTextEdit->get_text(); + } + m_aPreview.SetPreviewText(aStr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/config/optpage.cxx b/sw/source/ui/config/optpage.cxx new file mode 100644 index 000000000..7c6140ea3 --- /dev/null +++ b/sw/source/ui/config/optpage.cxx @@ -0,0 +1,2200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <optpage.hxx> +#include <doc.hxx> +#include <hintids.hxx> +#include <cmdid.h> +#include <fmtcol.hxx> +#include <charatr.hxx> +#include <swtypes.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentSettingAccess.hxx> + +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <cfgitems.hxx> +#include <poolfmt.hxx> +#include <uiitems.hxx> +#include <printdata.hxx> +#include <modcfg.hxx> +#include <crstate.hxx> +#include <viewopt.hxx> +#include <globals.hrc> +#include <strings.hrc> +#include <swwrtshitem.hxx> + +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/svxenum.hxx> +#include <sal/macros.h> +#include <sfx2/dialoghelper.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/ctloptions.hxx> +#include <svl/eitem.hxx> +#include <svl/cjkoptions.hxx> +#include <svtools/ctrltool.hxx> +#include <svtools/unitconv.hxx> +#include <sfx2/htmlmode.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <optload.hxx> + +using namespace ::com::sun::star; + +namespace { + +void drawRect(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, const Color &rFillColor, const Color &rLineColor) +{ + rRenderContext.SetFillColor(rFillColor); + rRenderContext.SetLineColor(rLineColor); + rRenderContext.DrawRect(rRect); +} + +} + +// Tools->Options->Writer->View +// Tools->Options->Writer/Web->View +SwContentOptPage::SwContentOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/viewoptionspage.ui", "ViewOptionsPage", &rCoreSet) + , m_xCrossCB(m_xBuilder->weld_check_button("helplines")) + , m_xHMetric(m_xBuilder->weld_combo_box("hrulercombobox")) + , m_xVRulerCBox(m_xBuilder->weld_check_button("vruler")) + , m_xVRulerRightCBox(m_xBuilder->weld_check_button("vrulerright")) + , m_xVMetric(m_xBuilder->weld_combo_box("vrulercombobox")) + , m_xSmoothCBox(m_xBuilder->weld_check_button("smoothscroll")) + , m_xGrfCB(m_xBuilder->weld_check_button("graphics")) + , m_xTableCB(m_xBuilder->weld_check_button("tables")) + , m_xDrwCB(m_xBuilder->weld_check_button("drawings")) + , m_xPostItCB(m_xBuilder->weld_check_button("comments")) + , m_xSettingsFrame(m_xBuilder->weld_frame("settingsframe")) + , m_xSettingsLabel(m_xBuilder->weld_label("settingslabel")) + , m_xMetricLabel(m_xBuilder->weld_label("measureunitlabel")) + , m_xMetricLB(m_xBuilder->weld_combo_box("measureunit")) + , m_xShowInlineTooltips(m_xBuilder->weld_check_button("changestooltip")) + , m_xFieldHiddenCB(m_xBuilder->weld_check_button("hiddentextfield")) + , m_xFieldHiddenParaCB(m_xBuilder->weld_check_button("hiddenparafield")) +{ + /* This part is visible only with Writer/Web->View dialogue. */ + const SfxPoolItem* pItem; + if (! (SfxItemState::SET == rCoreSet.GetItemState(SID_HTML_MODE, false, &pItem ) + && static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON)) + { + m_xSettingsFrame->hide(); + m_xSettingsLabel->hide(); + m_xMetricLabel->hide(); + m_xMetricLB->hide(); + } + + SvtCJKOptions aCJKOptions; + if(!aCJKOptions.IsVerticalTextEnabled() ) + m_xVRulerRightCBox->hide(); + m_xVRulerCBox->connect_toggled(LINK(this, SwContentOptPage, VertRulerHdl )); + + for (size_t i = 0; i < SwFieldUnitTable::Count(); ++i) + { + const OUString sMetric = SwFieldUnitTable::GetString(i); + FieldUnit eFUnit = SwFieldUnitTable::GetValue(i); + + switch ( eFUnit ) + { + case FieldUnit::MM: + case FieldUnit::CM: + case FieldUnit::POINT: + case FieldUnit::PICA: + case FieldUnit::INCH: + case FieldUnit::CHAR: // add two units , 'character' and 'line' , their ticks are not fixed + case FieldUnit::LINE: + { + // only use these metrics + // a horizontal ruler has not the 'line' unit + // there isn't 'line' unit in HTML format + if (eFUnit != FieldUnit::LINE) + { + m_xMetricLB->append(OUString::number(static_cast<sal_uInt32>(eFUnit)), sMetric); + m_xHMetric->append(OUString::number(static_cast<sal_uInt32>(eFUnit)), sMetric); + } + // a vertical ruler has not the 'character' unit + if (eFUnit != FieldUnit::CHAR) + { + m_xVMetric->append(OUString::number(static_cast<sal_uInt32>(eFUnit)), sMetric); + } + break; + } + default:;//prevent warning + } + } +} + +SwContentOptPage::~SwContentOptPage() +{ +} + +std::unique_ptr<SfxTabPage> SwContentOptPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwContentOptPage>(pPage, pController, *rAttrSet); +} + +static void lcl_SelectMetricLB(weld::ComboBox& rMetric, sal_uInt16 nSID, const SfxItemSet& rSet) +{ + const SfxPoolItem* pItem; + if( rSet.GetItemState( nSID, false, &pItem ) >= SfxItemState::DEFAULT ) + { + FieldUnit eFieldUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + for (sal_Int32 i = 0, nEntryCount = rMetric.get_count(); i < nEntryCount; ++i) + { + if (rMetric.get_id(i).toUInt32() == static_cast<sal_uInt32>(eFieldUnit)) + { + rMetric.set_active(i); + break; + } + } + } + rMetric.save_value(); +} + +void SwContentOptPage::Reset(const SfxItemSet* rSet) +{ + const SwElemItem* pElemAttr = nullptr; + + rSet->GetItemState( FN_PARAM_ELEM , false, + reinterpret_cast<const SfxPoolItem**>(&pElemAttr) ); + if(pElemAttr) + { + m_xTableCB->set_active(pElemAttr->m_bTable); + m_xGrfCB->set_active(pElemAttr->m_bGraphic); + m_xDrwCB->set_active(pElemAttr->m_bDrawing); + m_xPostItCB->set_active(pElemAttr->m_bNotes); + m_xCrossCB->set_active(pElemAttr->m_bCrosshair); + m_xVRulerCBox->set_active(pElemAttr->m_bVertRuler); + m_xVRulerRightCBox->set_active(pElemAttr->m_bVertRulerRight); + m_xSmoothCBox->set_active(pElemAttr->m_bSmoothScroll); + m_xShowInlineTooltips->set_active(pElemAttr->m_bShowInlineTooltips); + m_xFieldHiddenCB->set_active( pElemAttr->m_bFieldHiddenText ); + m_xFieldHiddenParaCB->set_active( pElemAttr->m_bShowHiddenPara ); + } + m_xMetricLB->set_active(-1); + lcl_SelectMetricLB(*m_xMetricLB, SID_ATTR_METRIC, *rSet); + lcl_SelectMetricLB(*m_xHMetric, FN_HSCROLL_METRIC, *rSet); + lcl_SelectMetricLB(*m_xVMetric, FN_VSCROLL_METRIC, *rSet); +} + +bool SwContentOptPage::FillItemSet(SfxItemSet* rSet) +{ + const SwElemItem* pOldAttr = static_cast<const SwElemItem*>( + GetOldItem(GetItemSet(), FN_PARAM_ELEM)); + + SwElemItem aElem; + aElem.m_bTable = m_xTableCB->get_active(); + aElem.m_bGraphic = m_xGrfCB->get_active(); + aElem.m_bDrawing = m_xDrwCB->get_active(); + aElem.m_bNotes = m_xPostItCB->get_active(); + aElem.m_bCrosshair = m_xCrossCB->get_active(); + aElem.m_bVertRuler = m_xVRulerCBox->get_active(); + aElem.m_bVertRulerRight = m_xVRulerRightCBox->get_active(); + aElem.m_bSmoothScroll = m_xSmoothCBox->get_active(); + aElem.m_bShowInlineTooltips = m_xShowInlineTooltips->get_active(); + aElem.m_bFieldHiddenText = m_xFieldHiddenCB->get_active(); + aElem.m_bShowHiddenPara = m_xFieldHiddenParaCB->get_active(); + + bool bRet = !pOldAttr || aElem != *pOldAttr; + if(bRet) + bRet = nullptr != rSet->Put(aElem); + + sal_Int32 nMPos = m_xMetricLB->get_active(); + sal_Int32 nGlobalMetricPos = nMPos; + if ( m_xMetricLB->get_value_changed_from_saved() ) + { + const sal_uInt16 nFieldUnit = m_xMetricLB->get_id(nMPos).toUInt32(); + rSet->Put( SfxUInt16Item( SID_ATTR_METRIC, nFieldUnit ) ); + bRet = true; + } + + nMPos = m_xHMetric->get_active(); + if ( m_xHMetric->get_value_changed_from_saved() || nMPos != nGlobalMetricPos ) + { + const sal_uInt16 nFieldUnit = m_xHMetric->get_id(nMPos).toUInt32(); + rSet->Put( SfxUInt16Item( FN_HSCROLL_METRIC, nFieldUnit ) ); + bRet = true; + } + nMPos = m_xVMetric->get_active(); + if ( m_xVMetric->get_value_changed_from_saved() || nMPos != nGlobalMetricPos ) + { + const sal_uInt16 nFieldUnit = m_xVMetric->get_id(nMPos).toUInt32(); + rSet->Put( SfxUInt16Item( FN_VSCROLL_METRIC, nFieldUnit ) ); + bRet = true; + } + + return bRet; +} + +IMPL_LINK(SwContentOptPage, VertRulerHdl, weld::ToggleButton&, rBox, void) +{ + m_xVRulerRightCBox->set_sensitive(rBox.get_sensitive() && rBox.get_active()); +} + +// TabPage Printer additional settings +SwAddPrinterTabPage::SwAddPrinterTabPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rCoreSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/printoptionspage.ui", "PrintOptionsPage", &rCoreSet) + , sNone(SwResId(SW_STR_NONE)) + , bAttrModified(false) + , bPreview(false) + , m_xGrfCB(m_xBuilder->weld_check_button("graphics")) + , m_xCtrlFieldCB(m_xBuilder->weld_check_button("formcontrols")) + , m_xBackgroundCB(m_xBuilder->weld_check_button("background")) + , m_xBlackFontCB(m_xBuilder->weld_check_button("inblack")) + , m_xPrintHiddenTextCB(m_xBuilder->weld_check_button("hiddentext")) + , m_xPrintTextPlaceholderCB(m_xBuilder->weld_check_button("textplaceholder")) + , m_xPagesFrame(m_xBuilder->weld_widget("pagesframe")) + , m_xLeftPageCB(m_xBuilder->weld_check_button("leftpages")) + , m_xRightPageCB(m_xBuilder->weld_check_button("rightpages")) + , m_xProspectCB(m_xBuilder->weld_check_button("brochure")) + , m_xProspectCB_RTL(m_xBuilder->weld_check_button("rtl")) + , m_xCommentsFrame(m_xBuilder->weld_widget("commentsframe")) + , m_xNoRB(m_xBuilder->weld_radio_button("none")) + , m_xOnlyRB(m_xBuilder->weld_radio_button("only")) + , m_xEndRB(m_xBuilder->weld_radio_button("end")) + , m_xEndPageRB(m_xBuilder->weld_radio_button("endpage")) + , m_xInMarginsRB(m_xBuilder->weld_radio_button("inmargins")) + , m_xPrintEmptyPagesCB(m_xBuilder->weld_check_button("blankpages")) + , m_xPaperFromSetupCB(m_xBuilder->weld_check_button("papertray")) + , m_xFaxLB(m_xBuilder->weld_combo_box("fax")) +{ + Link<weld::ToggleButton&,void> aLk = LINK( this, SwAddPrinterTabPage, AutoClickHdl); + m_xGrfCB->connect_toggled( aLk ); + m_xRightPageCB->connect_toggled( aLk ); + m_xLeftPageCB->connect_toggled( aLk ); + m_xCtrlFieldCB->connect_toggled( aLk ); + m_xBackgroundCB->connect_toggled( aLk ); + m_xBlackFontCB->connect_toggled( aLk ); + m_xPrintHiddenTextCB->connect_toggled( aLk ); + m_xPrintTextPlaceholderCB->connect_toggled( aLk ); + m_xProspectCB->connect_toggled( aLk ); + m_xProspectCB_RTL->connect_toggled( aLk ); + m_xPaperFromSetupCB->connect_toggled( aLk ); + m_xPrintEmptyPagesCB->connect_toggled( aLk ); + m_xEndPageRB->connect_toggled( aLk ); + m_xInMarginsRB->connect_toggled( aLk ); + m_xEndRB->connect_toggled( aLk ); + m_xOnlyRB->connect_toggled( aLk ); + m_xNoRB->connect_toggled( aLk ); + m_xFaxLB->connect_changed( LINK( this, SwAddPrinterTabPage, SelectHdl ) ); + + const SfxPoolItem* pItem; + if(SfxItemState::SET == rCoreSet.GetItemState(SID_HTML_MODE, false, &pItem ) + && static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON) + { + m_xLeftPageCB->hide(); + m_xRightPageCB->hide(); + m_xPrintHiddenTextCB->hide(); + m_xPrintTextPlaceholderCB->hide(); + m_xPrintEmptyPagesCB->hide(); + } + m_xProspectCB_RTL->set_sensitive(false); + SvtCTLOptions aCTLOptions; + m_xProspectCB_RTL->set_visible(aCTLOptions.IsCTLFontEnabled()); +} + +SwAddPrinterTabPage::~SwAddPrinterTabPage() +{ +} + +void SwAddPrinterTabPage::SetPreview(bool bPrev) +{ + bPreview = bPrev; + m_xCommentsFrame->set_sensitive(!bPreview); + m_xPagesFrame->set_sensitive(!bPreview); +} + +std::unique_ptr<SfxTabPage> SwAddPrinterTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwAddPrinterTabPage>(pPage, pController, *rAttrSet); +} + +bool SwAddPrinterTabPage::FillItemSet( SfxItemSet* rCoreSet ) +{ + if ( bAttrModified ) + { + SwAddPrinterItem aAddPrinterAttr; + aAddPrinterAttr.m_bPrintGraphic = m_xGrfCB->get_active(); + aAddPrinterAttr.m_bPrintTable = true; // always enabled since CWS printerpullgpages /*m_xTabCB->get_active();*/ + aAddPrinterAttr.m_bPrintDraw = m_xGrfCB->get_active(); // UI merged with m_xGrfCB in CWS printerpullgpages + aAddPrinterAttr.m_bPrintControl = m_xCtrlFieldCB->get_active(); + aAddPrinterAttr.m_bPrintPageBackground = m_xBackgroundCB->get_active(); + aAddPrinterAttr.m_bPrintBlackFont = m_xBlackFontCB->get_active(); + aAddPrinterAttr.m_bPrintHiddenText = m_xPrintHiddenTextCB->get_active(); + aAddPrinterAttr.m_bPrintTextPlaceholder = m_xPrintTextPlaceholderCB->get_active(); + + aAddPrinterAttr.m_bPrintLeftPages = m_xLeftPageCB->get_active(); + aAddPrinterAttr.m_bPrintRightPages = m_xRightPageCB->get_active(); + aAddPrinterAttr.m_bPrintReverse = false; // handled by vcl itself since CWS printerpullpages /*m_xReverseCB->get_active()*/; + aAddPrinterAttr.m_bPrintProspect = m_xProspectCB->get_active(); + aAddPrinterAttr.m_bPrintProspectRTL = m_xProspectCB_RTL->get_active(); + aAddPrinterAttr.m_bPaperFromSetup = m_xPaperFromSetupCB->get_active(); + aAddPrinterAttr.m_bPrintEmptyPages = m_xPrintEmptyPagesCB->get_active(); + aAddPrinterAttr.m_bPrintSingleJobs = true; // handled by vcl in new print dialog since CWS printerpullpages /*m_xSingleJobsCB->get_active()*/; + + if (m_xNoRB->get_active()) aAddPrinterAttr.m_nPrintPostIts = + SwPostItMode::NONE; + if (m_xOnlyRB->get_active()) aAddPrinterAttr.m_nPrintPostIts = + SwPostItMode::Only; + if (m_xEndRB->get_active()) aAddPrinterAttr.m_nPrintPostIts = + SwPostItMode::EndDoc; + if (m_xEndPageRB->get_active()) aAddPrinterAttr.m_nPrintPostIts = + SwPostItMode::EndPage; + if (m_xInMarginsRB->get_active()) aAddPrinterAttr.m_nPrintPostIts = + SwPostItMode::InMargins; + + const OUString sFax = m_xFaxLB->get_active_text(); + aAddPrinterAttr.m_sFaxName = sNone == sFax ? OUString() : sFax; + rCoreSet->Put(aAddPrinterAttr); + } + return bAttrModified; +} + +void SwAddPrinterTabPage::Reset( const SfxItemSet* ) +{ + const SfxItemSet& rSet = GetItemSet(); + const SwAddPrinterItem* pAddPrinterAttr = nullptr; + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_ADDPRINTER , false, + reinterpret_cast<const SfxPoolItem**>(&pAddPrinterAttr) )) + { + m_xGrfCB->set_active(pAddPrinterAttr->m_bPrintGraphic || pAddPrinterAttr->m_bPrintDraw); + m_xCtrlFieldCB->set_active( pAddPrinterAttr->m_bPrintControl); + m_xBackgroundCB->set_active( pAddPrinterAttr->m_bPrintPageBackground); + m_xBlackFontCB->set_active( pAddPrinterAttr->m_bPrintBlackFont); + m_xPrintHiddenTextCB->set_active( pAddPrinterAttr->m_bPrintHiddenText); + m_xPrintTextPlaceholderCB->set_active(pAddPrinterAttr->m_bPrintTextPlaceholder); + m_xLeftPageCB->set_active( pAddPrinterAttr->m_bPrintLeftPages); + m_xRightPageCB->set_active( pAddPrinterAttr->m_bPrintRightPages); + m_xPaperFromSetupCB->set_active(pAddPrinterAttr->m_bPaperFromSetup); + m_xPrintEmptyPagesCB->set_active(pAddPrinterAttr->m_bPrintEmptyPages); + m_xProspectCB->set_active( pAddPrinterAttr->m_bPrintProspect); + m_xProspectCB_RTL->set_active( pAddPrinterAttr->m_bPrintProspectRTL); + + m_xNoRB->set_active(pAddPrinterAttr->m_nPrintPostIts== SwPostItMode::NONE ) ; + m_xOnlyRB->set_active(pAddPrinterAttr->m_nPrintPostIts== SwPostItMode::Only ) ; + m_xEndRB->set_active(pAddPrinterAttr->m_nPrintPostIts== SwPostItMode::EndDoc ) ; + m_xEndPageRB->set_active(pAddPrinterAttr->m_nPrintPostIts== SwPostItMode::EndPage ) ; + m_xInMarginsRB->set_active(pAddPrinterAttr->m_nPrintPostIts== SwPostItMode::InMargins ) ; + auto nFound = m_xFaxLB->find_text(pAddPrinterAttr->m_sFaxName); + if (nFound != -1) + m_xFaxLB->set_active(nFound); + else + m_xFaxLB->set_active(0); + } + if (m_xProspectCB->get_active()) + { + m_xProspectCB_RTL->set_sensitive(true); + m_xNoRB->set_sensitive( false ); + m_xOnlyRB->set_sensitive( false ); + m_xEndRB->set_sensitive( false ); + m_xEndPageRB->set_sensitive( false ); + } + else + m_xProspectCB_RTL->set_sensitive( false ); +} + +IMPL_LINK_NOARG(SwAddPrinterTabPage, AutoClickHdl, weld::ToggleButton&, void) +{ + bAttrModified = true; + bool bIsProspect = m_xProspectCB->get_active(); + if (!bIsProspect) + m_xProspectCB_RTL->set_active( false ); + m_xProspectCB_RTL->set_sensitive( bIsProspect ); + m_xNoRB->set_sensitive( !bIsProspect ); + m_xOnlyRB->set_sensitive( !bIsProspect ); + m_xEndRB->set_sensitive( !bIsProspect ); + m_xEndPageRB->set_sensitive( !bIsProspect ); + m_xInMarginsRB->set_sensitive( !bIsProspect ); +} + +void SwAddPrinterTabPage::SetFax( const std::vector<OUString>& rFaxLst ) +{ + m_xFaxLB->append_text(sNone); + for(const auto & i : rFaxLst) + { + m_xFaxLB->append_text(i); + } + m_xFaxLB->set_active(0); +} + +IMPL_LINK_NOARG(SwAddPrinterTabPage, SelectHdl, weld::ComboBox&, void) +{ + bAttrModified=true; +} + +void SwAddPrinterTabPage::PageCreated( const SfxAllItemSet& aSet) +{ + const SfxBoolItem* pListItem = aSet.GetItem<SfxBoolItem>(SID_FAX_LIST, false); + const SfxBoolItem* pPreviewItem = aSet.GetItem<SfxBoolItem>(SID_PREVIEWFLAG_TYPE, false); + if (pPreviewItem) + { + SetPreview(pPreviewItem->GetValue()); + Reset(&aSet); + } + if (pListItem && pListItem->GetValue()) + { + std::vector<OUString> aFaxList; + const std::vector<OUString>& rPrinters = Printer::GetPrinterQueues(); + for (const auto & rPrinter : rPrinters) + aFaxList.insert(aFaxList.begin(), rPrinter); + SetFax( aFaxList ); + } +} + +// Tabpage Standardfonts +SwStdFontTabPage::SwStdFontTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optfonttabpage.ui", "OptFontTabPage", &rSet) + , m_pPrt(nullptr) + , m_pFontConfig(nullptr) + , m_pWrtShell(nullptr) + , m_eLanguage( GetAppLanguage() ) + , m_bListDefault(false) + , m_bSetListDefault(true) + , m_bLabelDefault(false) + , m_bSetLabelDefault(true) + , m_bIdxDefault(false) + , m_bSetIdxDefault(true) + , m_bDisposePrinter(false) + , m_bListHeightDefault(false) + , m_bLabelHeightDefault(false) + , m_bIndexHeightDefault(false) + , m_nFontGroup(FONT_GROUP_DEFAULT) + , m_sScriptWestern(SwResId(ST_SCRIPT_WESTERN)) + , m_sScriptAsian(SwResId(ST_SCRIPT_ASIAN)) + , m_sScriptComplex(SwResId(ST_SCRIPT_CTL)) + , m_xLabelFT(m_xBuilder->weld_label("label1")) + , m_xStandardBox(m_xBuilder->weld_combo_box("standardbox")) + , m_xStandardHeightLB(new FontSizeBox(m_xBuilder->weld_combo_box("standardheight"))) + , m_xTitleBox(m_xBuilder->weld_combo_box("titlebox")) + , m_xTitleHeightLB(new FontSizeBox(m_xBuilder->weld_combo_box("titleheight"))) + , m_xListBox(m_xBuilder->weld_combo_box("listbox")) + , m_xListHeightLB(new FontSizeBox(m_xBuilder->weld_combo_box("listheight"))) + , m_xLabelBox(m_xBuilder->weld_combo_box("labelbox")) + , m_xLabelHeightLB(new FontSizeBox(m_xBuilder->weld_combo_box("labelheight"))) + , m_xIdxBox(m_xBuilder->weld_combo_box("idxbox")) + , m_xIndexHeightLB(new FontSizeBox(m_xBuilder->weld_combo_box("indexheight"))) + , m_xStandardPB(m_xBuilder->weld_button("standard")) +{ + m_xStandardBox->make_sorted(); + m_xTitleBox->make_sorted(); + m_xListBox->make_sorted(); + m_xLabelBox->make_sorted(); + m_xIdxBox->make_sorted(); + + m_xStandardPB->connect_clicked(LINK(this, SwStdFontTabPage, StandardHdl)); + m_xStandardBox->connect_changed( LINK(this, SwStdFontTabPage, ModifyHdl)); + m_xListBox->connect_changed( LINK(this, SwStdFontTabPage, ModifyHdl)); + m_xLabelBox->connect_changed( LINK(this, SwStdFontTabPage, ModifyHdl)); + m_xIdxBox->connect_changed( LINK(this, SwStdFontTabPage, ModifyHdl)); + Link<weld::Widget&,void> aFocusLink = LINK( this, SwStdFontTabPage, LoseFocusHdl); + m_xStandardBox->connect_focus_out( aFocusLink ); + m_xTitleBox->connect_focus_out( aFocusLink ); + m_xListBox->connect_focus_out( aFocusLink ); + m_xLabelBox->connect_focus_out( aFocusLink ); + m_xIdxBox->connect_focus_out( aFocusLink ); +} + +SwStdFontTabPage::~SwStdFontTabPage() +{ + m_xIndexHeightLB.reset(); + m_xLabelHeightLB.reset(); + m_xListHeightLB.reset(); + m_xTitleHeightLB.reset(); + m_xStandardHeightLB.reset(); + m_pFontList.reset(); + if (m_bDisposePrinter) + m_pPrt.disposeAndClear(); + else + m_pPrt.clear(); +} + +std::unique_ptr<SfxTabPage> SwStdFontTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwStdFontTabPage>(pPage, pController, *rAttrSet); +} + +static void lcl_SetColl(SwWrtShell* pWrtShell, sal_uInt16 nType, + SfxPrinter const * pPrt, const OUString& rStyle, + sal_uInt16 nFontWhich) +{ + vcl::Font aFont( rStyle, Size( 0, 10 ) ); + if( pPrt ) + aFont = pPrt->GetFontMetric( aFont ); + SwTextFormatColl *pColl = pWrtShell->GetTextCollFromPool(nType); + pColl->SetFormatAttr(SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(), + OUString(), aFont.GetPitch(), aFont.GetCharSet(), nFontWhich)); +} + +static void lcl_SetColl(SwWrtShell* pWrtShell, sal_uInt16 nType, + sal_Int32 nHeight, sal_uInt16 nFontHeightWhich) +{ + float fSize = static_cast<float>(nHeight) / 10; + nHeight = CalcToUnit( fSize, MapUnit::MapTwip ); + SwTextFormatColl *pColl = pWrtShell->GetTextCollFromPool(nType); + pColl->SetFormatAttr(SvxFontHeightItem(nHeight, 100, nFontHeightWhich)); +} + +bool SwStdFontTabPage::FillItemSet( SfxItemSet* ) +{ + SW_MOD()->GetModuleConfig()->SetDefaultFontInCurrDocOnly(false); + + const OUString sStandard = m_xStandardBox->get_active_text(); + const OUString sTitle = m_xTitleBox->get_active_text(); + const OUString sList = m_xListBox->get_active_text(); + const OUString sLabel = m_xLabelBox->get_active_text(); + const OUString sIdx = m_xIdxBox->get_active_text(); + + bool bStandardHeightChanged = m_xStandardHeightLB->get_value_changed_from_saved(); + bool bTitleHeightChanged = m_xTitleHeightLB->get_value_changed_from_saved(); + bool bListHeightChanged = m_xListHeightLB->get_value_changed_from_saved() && !m_bListHeightDefault; + bool bLabelHeightChanged = m_xLabelHeightLB->get_value_changed_from_saved() && !m_bLabelHeightDefault; + bool bIndexHeightChanged = m_xIndexHeightLB->get_value_changed_from_saved() && !m_bIndexHeightDefault; + + m_pFontConfig->SetFontStandard(sStandard, m_nFontGroup); + m_pFontConfig->SetFontOutline(sTitle, m_nFontGroup); + m_pFontConfig->SetFontList(sList, m_nFontGroup); + m_pFontConfig->SetFontCaption(sLabel, m_nFontGroup); + m_pFontConfig->SetFontIndex(sIdx, m_nFontGroup); + if(bStandardHeightChanged) + { + float fSize = static_cast<float>(m_xStandardHeightLB->get_value()) / 10; + m_pFontConfig->SetFontHeight( CalcToUnit( fSize, MapUnit::MapTwip ), FONT_STANDARD, m_nFontGroup ); + } + if(bTitleHeightChanged) + { + float fSize = static_cast<float>(m_xTitleHeightLB->get_value()) / 10; + m_pFontConfig->SetFontHeight( CalcToUnit( fSize, MapUnit::MapTwip ), FONT_OUTLINE, m_nFontGroup ); + } + if(bListHeightChanged) + { + float fSize = static_cast<float>(m_xListHeightLB->get_value()) / 10; + m_pFontConfig->SetFontHeight( CalcToUnit( fSize, MapUnit::MapTwip ), FONT_LIST, m_nFontGroup ); + } + if(bLabelHeightChanged) + { + float fSize = static_cast<float>(m_xLabelHeightLB->get_value()) / 10; + m_pFontConfig->SetFontHeight( CalcToUnit( fSize, MapUnit::MapTwip ), FONT_CAPTION, m_nFontGroup ); + } + if(bIndexHeightChanged) + { + float fSize = static_cast<float>(m_xIndexHeightLB->get_value()) / 10; + m_pFontConfig->SetFontHeight( CalcToUnit( fSize, MapUnit::MapTwip ), FONT_INDEX, m_nFontGroup ); + } + + if(m_pWrtShell) + { + m_pWrtShell->StartAllAction(); + SfxPrinter* pPrinter = m_pWrtShell->getIDocumentDeviceAccess().getPrinter( false ); + bool bMod = false; + const sal_uInt16 nFontWhich = + m_nFontGroup == FONT_GROUP_DEFAULT ? RES_CHRATR_FONT : + FONT_GROUP_CJK == m_nFontGroup ? RES_CHRATR_CJK_FONT : RES_CHRATR_CTL_FONT; + const sal_uInt16 nFontHeightWhich = + m_nFontGroup == FONT_GROUP_DEFAULT ? RES_CHRATR_FONTSIZE : + FONT_GROUP_CJK == m_nFontGroup ? RES_CHRATR_CJK_FONTSIZE : RES_CHRATR_CTL_FONTSIZE; + if(sStandard != m_sShellStd) + { + vcl::Font aFont( sStandard, Size( 0, 10 ) ); + if( pPrinter ) + aFont = pPrinter->GetFontMetric( aFont ); + m_pWrtShell->SetDefault(SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(), + OUString(), aFont.GetPitch(), aFont.GetCharSet(), nFontWhich)); + SwTextFormatColl *pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_STANDARD); + pColl->ResetFormatAttr(nFontWhich); + bMod = true; + } + if(bStandardHeightChanged) + { + float fSize = static_cast<float>(m_xStandardHeightLB->get_value()) / 10; + m_pWrtShell->SetDefault(SvxFontHeightItem( CalcToUnit( fSize, MapUnit::MapTwip ), 100, nFontHeightWhich ) ); + SwTextFormatColl *pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_STANDARD); + pColl->ResetFormatAttr(nFontHeightWhich); + bMod = true; + } + + if(sTitle != m_sShellTitle ) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_HEADLINE_BASE, pPrinter, sTitle, nFontWhich); + bMod = true; + } + if(bTitleHeightChanged) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_HEADLINE_BASE, + sal::static_int_cast< sal_uInt16, sal_Int64 >(m_xTitleHeightLB->get_value()), nFontHeightWhich); + bMod = true; + } + if(sList != m_sShellList && (!m_bListDefault || !m_bSetListDefault )) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_NUMBER_BULLET_BASE, pPrinter, sList, nFontWhich); + bMod = true; + } + if(bListHeightChanged) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_NUMBER_BULLET_BASE, + sal::static_int_cast< sal_uInt16, sal_Int64 >(m_xListHeightLB->get_value()), nFontHeightWhich); + bMod = true; + } + if(sLabel != m_sShellLabel && (!m_bLabelDefault || !m_bSetLabelDefault)) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_LABEL, pPrinter, sLabel, nFontWhich); + bMod = true; + } + if(bLabelHeightChanged) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_LABEL, + sal::static_int_cast< sal_uInt16, sal_Int64 >(m_xLabelHeightLB->get_value()), nFontHeightWhich); + bMod = true; + } + if(sIdx != m_sShellIndex && (!m_bIdxDefault || !m_bSetIdxDefault)) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_REGISTER_BASE, pPrinter, sIdx, nFontWhich); + bMod = true; + } + if(bIndexHeightChanged) + { + lcl_SetColl(m_pWrtShell, RES_POOLCOLL_REGISTER_BASE, + sal::static_int_cast< sal_uInt16, sal_Int64 >(m_xIndexHeightLB->get_value()), nFontHeightWhich); + bMod = true; + } + if ( bMod ) + m_pWrtShell->SetModified(); + m_pWrtShell->EndAllAction(); + } + return false; +} + +void SwStdFontTabPage::Reset( const SfxItemSet* rSet) +{ + const SfxPoolItem* pLang; + const sal_uInt16 nLangSlot = m_nFontGroup == FONT_GROUP_DEFAULT ? SID_ATTR_LANGUAGE : + FONT_GROUP_CJK == m_nFontGroup ? SID_ATTR_CHAR_CJK_LANGUAGE : SID_ATTR_CHAR_CTL_LANGUAGE; + + if( SfxItemState::SET == rSet->GetItemState(nLangSlot, false, &pLang)) + m_eLanguage = static_cast<const SvxLanguageItem*>(pLang)->GetValue(); + + OUString sToReplace = m_sScriptWestern; + if(FONT_GROUP_CJK == m_nFontGroup ) + sToReplace = m_sScriptAsian; + else if(FONT_GROUP_CTL == m_nFontGroup ) + sToReplace = m_sScriptComplex; + m_xLabelFT->set_label(m_xLabelFT->get_label().replaceFirst("%1", sToReplace)); + + const SfxPoolItem* pItem; + + if (m_bDisposePrinter) + { + m_pPrt.disposeAndClear(); + m_bDisposePrinter = false; + } + + if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_PRINTER, false, &pItem)) + { + m_pPrt = static_cast<SfxPrinter*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + } + else + { + auto pPrinterSet = std::make_unique<SfxItemSet>( *rSet->GetPool(), + svl::Items<SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC>{} ); + m_pPrt = VclPtr<SfxPrinter>::Create(std::move(pPrinterSet)); + m_bDisposePrinter = true; + } + m_pFontList.reset(new FontList( m_pPrt )); + // #i94536# prevent duplication of font entries when 'reset' button is pressed + if( !m_xStandardBox->get_count() ) + { + // get the set of distinct available family names + std::set< OUString > aFontNames; + int nFontNames = m_pPrt->GetDevFontCount(); + for( int i = 0; i < nFontNames; i++ ) + { + FontMetric aFontMetric( m_pPrt->GetDevFont( i ) ); + aFontNames.insert( aFontMetric.GetFamilyName() ); + } + + // insert to listboxes + for( const auto& rFontName : aFontNames ) + { + m_xStandardBox->append_text( rFontName ); + m_xTitleBox->append_text( rFontName ); + m_xListBox->append_text( rFontName ); + m_xLabelBox->append_text( rFontName ); + m_xIdxBox->append_text( rFontName ); + } + } + if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_STDFONTS, false, &pItem)) + { + m_pFontConfig = static_cast<SwStdFontConfig*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + } + + if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_WRTSHELL, false, &pItem)) + { + m_pWrtShell = static_cast<SwWrtShell*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + } + OUString sStdBackup; + OUString sOutBackup; + OUString sListBackup; + OUString sCapBackup; + OUString sIdxBackup; + sal_Int32 nStandardHeight = -1; + sal_Int32 nTitleHeight = -1; + sal_Int32 nListHeight = -1; + sal_Int32 nLabelHeight = -1; + sal_Int32 nIndexHeight = -1; + + if(!m_pWrtShell) + { + sStdBackup = m_pFontConfig->GetFontStandard(m_nFontGroup); + sOutBackup = m_pFontConfig->GetFontOutline(m_nFontGroup); + sListBackup= m_pFontConfig->GetFontList(m_nFontGroup); + sCapBackup = m_pFontConfig->GetFontCaption(m_nFontGroup); + sIdxBackup = m_pFontConfig->GetFontIndex(m_nFontGroup); + nStandardHeight = m_pFontConfig->GetFontHeight( FONT_STANDARD, m_nFontGroup, m_eLanguage ); + nTitleHeight = m_pFontConfig->GetFontHeight( FONT_OUTLINE , m_nFontGroup, m_eLanguage ); + nListHeight = m_pFontConfig->GetFontHeight( FONT_LIST , m_nFontGroup, m_eLanguage ); + nLabelHeight = m_pFontConfig->GetFontHeight( FONT_CAPTION , m_nFontGroup, m_eLanguage ); + nIndexHeight = m_pFontConfig->GetFontHeight( FONT_INDEX , m_nFontGroup, m_eLanguage ); + if( nStandardHeight <= 0) + nStandardHeight = SwStdFontConfig::GetDefaultHeightFor( FONT_STANDARD + m_nFontGroup * FONT_PER_GROUP, m_eLanguage); + if( nTitleHeight <= 0) + nTitleHeight = SwStdFontConfig::GetDefaultHeightFor( FONT_OUTLINE + m_nFontGroup * FONT_PER_GROUP, m_eLanguage); + if( nListHeight <= 0) + nListHeight = SwStdFontConfig::GetDefaultHeightFor( FONT_LIST + m_nFontGroup * FONT_PER_GROUP, m_eLanguage); + if( nLabelHeight <= 0) + nLabelHeight = SwStdFontConfig::GetDefaultHeightFor( FONT_CAPTION + m_nFontGroup * FONT_PER_GROUP, m_eLanguage); + if( nIndexHeight <= 0) + nIndexHeight = SwStdFontConfig::GetDefaultHeightFor( FONT_INDEX + m_nFontGroup * FONT_PER_GROUP, m_eLanguage); + } + else + { + SwTextFormatColl *pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_STANDARD); + const SvxFontItem& rFont = !m_nFontGroup ? pColl->GetFont() : + FONT_GROUP_CJK == m_nFontGroup ? pColl->GetCJKFont() : pColl->GetCTLFont(); + m_sShellStd = sStdBackup = rFont.GetFamilyName(); + + const sal_uInt16 nFontHeightWhich = + m_nFontGroup == FONT_GROUP_DEFAULT ? RES_CHRATR_FONTSIZE : + FONT_GROUP_CJK == m_nFontGroup ? RES_CHRATR_CJK_FONTSIZE : RES_CHRATR_CTL_FONTSIZE; + const SvxFontHeightItem& rFontHeightStandard = static_cast<const SvxFontHeightItem& >(pColl->GetFormatAttr(nFontHeightWhich)); + nStandardHeight = static_cast<sal_Int32>(rFontHeightStandard.GetHeight()); + + pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_HEADLINE_BASE); + const SvxFontItem& rFontHL = !m_nFontGroup ? pColl->GetFont() : + FONT_GROUP_CJK == m_nFontGroup ? pColl->GetCJKFont() : pColl->GetCTLFont(); + m_sShellTitle = sOutBackup = rFontHL.GetFamilyName(); + + const SvxFontHeightItem& rFontHeightTitle = static_cast<const SvxFontHeightItem&>(pColl->GetFormatAttr( nFontHeightWhich )); + nTitleHeight = static_cast<sal_Int32>(rFontHeightTitle.GetHeight()); + + const sal_uInt16 nFontWhich = + m_nFontGroup == FONT_GROUP_DEFAULT ? RES_CHRATR_FONT : + FONT_GROUP_CJK == m_nFontGroup ? RES_CHRATR_CJK_FONT : RES_CHRATR_CTL_FONT; + pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_NUMBER_BULLET_BASE); + const SvxFontItem& rFontLS = !m_nFontGroup ? pColl->GetFont() : + FONT_GROUP_CJK == m_nFontGroup ? pColl->GetCJKFont() : pColl->GetCTLFont(); + m_bListDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + m_sShellList = sListBackup = rFontLS.GetFamilyName(); + + const SvxFontHeightItem& rFontHeightList = static_cast<const SvxFontHeightItem&>(pColl->GetFormatAttr(nFontHeightWhich)); + nListHeight = static_cast<sal_Int32>(rFontHeightList.GetHeight()); + m_bListHeightDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + + pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_LABEL); + m_bLabelDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + const SvxFontItem& rFontCP = !m_nFontGroup ? pColl->GetFont() : + FONT_GROUP_CJK == m_nFontGroup ? pColl->GetCJKFont() : pColl->GetCTLFont(); + m_sShellLabel = sCapBackup = rFontCP.GetFamilyName(); + const SvxFontHeightItem& rFontHeightLabel = static_cast<const SvxFontHeightItem&>(pColl->GetFormatAttr(nFontHeightWhich)); + nLabelHeight = static_cast<sal_Int32>(rFontHeightLabel.GetHeight()); + m_bLabelHeightDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + + pColl = m_pWrtShell->GetTextCollFromPool(RES_POOLCOLL_REGISTER_BASE); + m_bIdxDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + const SvxFontItem& rFontIDX = !m_nFontGroup ? pColl->GetFont() : + FONT_GROUP_CJK == m_nFontGroup ? pColl->GetCJKFont() : pColl->GetCTLFont(); + m_sShellIndex = sIdxBackup = rFontIDX.GetFamilyName(); + const SvxFontHeightItem& rFontHeightIndex = static_cast<const SvxFontHeightItem&>(pColl->GetFormatAttr(nFontHeightWhich)); + nIndexHeight = static_cast<sal_Int32>(rFontHeightIndex.GetHeight()); + m_bIndexHeightDefault = SfxItemState::DEFAULT == pColl->GetAttrSet().GetItemState(nFontWhich, false); + } + m_xStandardBox->set_entry_text(sStdBackup ); + m_xTitleBox->set_entry_text(sOutBackup ); + m_xListBox->set_entry_text(sListBackup); + m_xLabelBox->set_entry_text(sCapBackup ); + m_xIdxBox->set_entry_text(sIdxBackup ); + + FontMetric aFontMetric( m_pFontList->Get(sStdBackup, sStdBackup) ); + m_xStandardHeightLB->Fill( &aFontMetric, m_pFontList.get() ); + aFontMetric = m_pFontList->Get(sOutBackup, sOutBackup ); + m_xTitleHeightLB->Fill( &aFontMetric, m_pFontList.get() ); + aFontMetric = m_pFontList->Get(sListBackup,sListBackup); + m_xListHeightLB->Fill( &aFontMetric, m_pFontList.get() ); + aFontMetric = m_pFontList->Get(sCapBackup, sCapBackup ); + m_xLabelHeightLB->Fill( &aFontMetric, m_pFontList.get() ); + aFontMetric = m_pFontList->Get(sIdxBackup, sIdxBackup ); + m_xIndexHeightLB->Fill( &aFontMetric, m_pFontList.get() ); + + m_xStandardHeightLB->set_value( CalcToPoint( nStandardHeight, MapUnit::MapTwip, 10 ) ); + m_xTitleHeightLB->set_value( CalcToPoint( nTitleHeight , MapUnit::MapTwip, 10 ) ); + m_xListHeightLB->set_value( CalcToPoint( nListHeight , MapUnit::MapTwip, 10 ) ); + m_xLabelHeightLB->set_value( CalcToPoint( nLabelHeight , MapUnit::MapTwip, 10 )); + m_xIndexHeightLB->set_value( CalcToPoint( nIndexHeight , MapUnit::MapTwip, 10 )); + + m_xStandardBox->save_value(); + m_xTitleBox->save_value(); + m_xListBox->save_value(); + m_xLabelBox->save_value(); + m_xIdxBox->save_value(); + + m_xStandardHeightLB->save_value(); + m_xTitleHeightLB->save_value(); + m_xListHeightLB->save_value(); + m_xLabelHeightLB->save_value(); + m_xIndexHeightLB->save_value(); +} + +IMPL_LINK_NOARG(SwStdFontTabPage, StandardHdl, weld::Button&, void) +{ + sal_uInt8 nFontOffset = m_nFontGroup * FONT_PER_GROUP; + m_xStandardBox->set_entry_text(SwStdFontConfig::GetDefaultFor(FONT_STANDARD + nFontOffset, m_eLanguage)); + m_xTitleBox->set_entry_text(SwStdFontConfig::GetDefaultFor(FONT_OUTLINE + nFontOffset, m_eLanguage)); + m_xListBox->set_entry_text(SwStdFontConfig::GetDefaultFor(FONT_LIST + nFontOffset, m_eLanguage)); + m_xLabelBox->set_entry_text(SwStdFontConfig::GetDefaultFor(FONT_CAPTION + nFontOffset, m_eLanguage)); + m_xIdxBox->set_entry_text(SwStdFontConfig::GetDefaultFor(FONT_INDEX + nFontOffset, m_eLanguage)); + + m_xStandardBox->save_value(); + m_xTitleBox->save_value(); + m_xListBox->save_value(); + m_xLabelBox->save_value(); + m_xIdxBox->save_value(); + + m_xStandardHeightLB->set_value( CalcToPoint( + SwStdFontConfig::GetDefaultHeightFor(FONT_STANDARD + nFontOffset, m_eLanguage), + MapUnit::MapTwip, 10 )); + m_xTitleHeightLB->set_value(CalcToPoint( + SwStdFontConfig::GetDefaultHeightFor(FONT_OUTLINE + + nFontOffset, m_eLanguage), MapUnit::MapTwip, 10 )); + m_xListHeightLB->set_value(CalcToPoint( + SwStdFontConfig::GetDefaultHeightFor(FONT_LIST + nFontOffset, m_eLanguage), + MapUnit::MapTwip, 10 )); + m_xLabelHeightLB->set_value(CalcToPoint( + SwStdFontConfig::GetDefaultHeightFor(FONT_CAPTION + nFontOffset, m_eLanguage), + MapUnit::MapTwip, 10 )); + m_xIndexHeightLB->set_value(CalcToPoint( + SwStdFontConfig::GetDefaultHeightFor(FONT_INDEX + nFontOffset, m_eLanguage), + MapUnit::MapTwip, 10 )); +} + +IMPL_LINK( SwStdFontTabPage, ModifyHdl, weld::ComboBox&, rBox, void ) +{ + if (&rBox == m_xStandardBox.get()) + { + const OUString sEntry = rBox.get_active_text(); + if(m_bSetListDefault && m_bListDefault) + m_xListBox->set_entry_text(sEntry); + if(m_bSetLabelDefault && m_bLabelDefault) + m_xLabelBox->set_entry_text(sEntry); + if(m_bSetIdxDefault && m_bIdxDefault) + m_xIdxBox->set_entry_text(sEntry); + } + else if (&rBox == m_xListBox.get()) + { + m_bSetListDefault = false; + } + else if (&rBox == m_xLabelBox.get()) + { + m_bSetLabelDefault = false; + } + else if (&rBox == m_xIdxBox.get()) + { + m_bSetIdxDefault = false; + } +} + +IMPL_LINK( SwStdFontTabPage, LoseFocusHdl, weld::Widget&, rControl, void ) +{ + weld::ComboBox& rBox = dynamic_cast<weld::ComboBox&>(rControl); + FontSizeBox* pHeightLB = nullptr; + const OUString sEntry = rBox.get_active_text(); + if (&rBox == m_xStandardBox.get()) + { + pHeightLB = m_xStandardHeightLB.get(); + } + else if (&rBox == m_xTitleBox.get()) + { + pHeightLB = m_xTitleHeightLB.get(); + } + else if (&rBox == m_xListBox.get()) + { + pHeightLB = m_xListHeightLB.get(); + } + else if (&rBox == m_xLabelBox.get()) + { + pHeightLB = m_xLabelHeightLB.get(); + } + else /*if (&rBox == m_xIndexHeightLB.get())*/ + { + pHeightLB = m_xIndexHeightLB.get(); + } + FontMetric aFontMetric( m_pFontList->Get(sEntry, sEntry) ); + pHeightLB->Fill( &aFontMetric, m_pFontList.get() ); +} + +void SwStdFontTabPage::PageCreated( const SfxAllItemSet& aSet) +{ + const SfxUInt16Item* pFlagItem = aSet.GetItem<SfxUInt16Item>(SID_FONTMODE_TYPE, false); + if (pFlagItem) + m_nFontGroup = sal::static_int_cast< sal_uInt8, sal_uInt16>( pFlagItem->GetValue() ); +} + +SwTableOptionsTabPage::SwTableOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/opttablepage.ui", "OptTablePage", &rSet) + , m_pWrtShell(nullptr) + , m_bHTMLMode(false) + , m_xHeaderCB(m_xBuilder->weld_check_button("header")) + , m_xRepeatHeaderCB(m_xBuilder->weld_check_button("repeatheader")) + , m_xDontSplitCB(m_xBuilder->weld_check_button("dontsplit")) + , m_xBorderCB(m_xBuilder->weld_check_button("border")) + , m_xNumFormattingCB(m_xBuilder->weld_check_button("numformatting")) + , m_xNumFormatFormattingCB(m_xBuilder->weld_check_button("numfmtformatting")) + , m_xNumAlignmentCB(m_xBuilder->weld_check_button("numalignment")) + , m_xRowMoveMF(m_xBuilder->weld_metric_spin_button("rowmove", FieldUnit::CM)) + , m_xColMoveMF(m_xBuilder->weld_metric_spin_button("colmove", FieldUnit::CM)) + , m_xRowInsertMF(m_xBuilder->weld_metric_spin_button("rowinsert", FieldUnit::CM)) + , m_xColInsertMF(m_xBuilder->weld_metric_spin_button("colinsert", FieldUnit::CM)) + , m_xFixRB(m_xBuilder->weld_radio_button("fix")) + , m_xFixPropRB(m_xBuilder->weld_radio_button("fixprop")) + , m_xVarRB(m_xBuilder->weld_radio_button("var")) +{ + Link<weld::Button&,void> aLnk(LINK(this, SwTableOptionsTabPage, CheckBoxHdl)); + m_xNumFormattingCB->connect_clicked(aLnk); + m_xNumFormatFormattingCB->connect_clicked(aLnk); + m_xHeaderCB->connect_clicked(aLnk); +} + +SwTableOptionsTabPage::~SwTableOptionsTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwTableOptionsTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwTableOptionsTabPage>(pPage, pController, *rAttrSet); +} + +bool SwTableOptionsTabPage::FillItemSet( SfxItemSet* ) +{ + bool bRet = false; + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + if (m_xRowMoveMF->get_value_changed_from_saved()) + pModOpt->SetTableHMove( static_cast<sal_uInt16>(m_xRowMoveMF->denormalize( m_xRowMoveMF->get_value(FieldUnit::TWIP)))); + + if (m_xColMoveMF->get_value_changed_from_saved()) + pModOpt->SetTableVMove( static_cast<sal_uInt16>(m_xColMoveMF->denormalize( m_xColMoveMF->get_value(FieldUnit::TWIP)))); + + if (m_xRowInsertMF->get_value_changed_from_saved()) + pModOpt->SetTableHInsert(static_cast<sal_uInt16>(m_xRowInsertMF->denormalize( m_xRowInsertMF->get_value(FieldUnit::TWIP)))); + + if (m_xColInsertMF->get_value_changed_from_saved()) + pModOpt->SetTableVInsert(static_cast<sal_uInt16>(m_xColInsertMF->denormalize( m_xColInsertMF->get_value(FieldUnit::TWIP)))); + + TableChgMode eMode; + if (m_xFixRB->get_active()) + eMode = TableChgMode::FixedWidthChangeAbs; + else if(m_xFixPropRB->get_active()) + eMode = TableChgMode::FixedWidthChangeProp; + else + eMode = TableChgMode::VarWidthChangeAbs; + if(eMode != pModOpt->GetTableMode()) + { + pModOpt->SetTableMode(eMode); + // the table-keyboard-mode has changed, now the current + // table should know about that too. + if(m_pWrtShell && SelectionType::Table & m_pWrtShell->GetSelectionType()) + { + m_pWrtShell->SetTableChgMode(eMode); + static sal_uInt16 aInva[] = + { FN_TABLE_MODE_FIX, + FN_TABLE_MODE_FIX_PROP, + FN_TABLE_MODE_VARIABLE, + 0 + }; + m_pWrtShell->GetView().GetViewFrame()->GetBindings().Invalidate( aInva ); + } + + bRet = true; + } + + SwInsertTableOptions aInsOpts( SwInsertTableFlags::NONE, 0 ); + + if (m_xHeaderCB->get_active()) + aInsOpts.mnInsMode |= SwInsertTableFlags::Headline; + + if (m_xRepeatHeaderCB->get_sensitive()) + aInsOpts.mnRowsToRepeat = m_xRepeatHeaderCB->get_active() ? 1 : 0; + + if (!m_xDontSplitCB->get_active()) + aInsOpts.mnInsMode |= SwInsertTableFlags::SplitLayout; + + if (m_xBorderCB->get_active()) + aInsOpts.mnInsMode |= SwInsertTableFlags::DefaultBorder; + + if (m_xHeaderCB->get_state_changed_from_saved() || + m_xRepeatHeaderCB->get_state_changed_from_saved() || + m_xDontSplitCB->get_state_changed_from_saved() || + m_xBorderCB->get_state_changed_from_saved()) + { + pModOpt->SetInsTableFlags(m_bHTMLMode, aInsOpts); + } + + if (m_xNumFormattingCB->get_state_changed_from_saved()) + { + pModOpt->SetInsTableFormatNum(m_bHTMLMode, m_xNumFormattingCB->get_active()); + bRet = true; + } + + if (m_xNumFormatFormattingCB->get_state_changed_from_saved()) + { + pModOpt->SetInsTableChangeNumFormat(m_bHTMLMode, m_xNumFormatFormattingCB->get_active()); + bRet = true; + } + + if (m_xNumAlignmentCB->get_state_changed_from_saved()) + { + pModOpt->SetInsTableAlignNum(m_bHTMLMode, m_xNumAlignmentCB->get_active()); + bRet = true; + } + + return bRet; +} + +void SwTableOptionsTabPage::Reset( const SfxItemSet* rSet) +{ + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + if ( rSet->GetItemState( SID_ATTR_METRIC ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = rSet->Get( SID_ATTR_METRIC ); + FieldUnit eFieldUnit = static_cast<FieldUnit>(rItem.GetValue()); + ::SetFieldUnit( *m_xRowMoveMF, eFieldUnit ); + ::SetFieldUnit( *m_xColMoveMF, eFieldUnit ); + ::SetFieldUnit( *m_xRowInsertMF, eFieldUnit ); + ::SetFieldUnit( *m_xColInsertMF, eFieldUnit ); + } + + m_xRowMoveMF->set_value(m_xRowMoveMF->normalize(pModOpt->GetTableHMove()), FieldUnit::TWIP); + m_xColMoveMF->set_value(m_xColMoveMF->normalize(pModOpt->GetTableVMove()), FieldUnit::TWIP); + m_xRowInsertMF->set_value(m_xRowInsertMF->normalize(pModOpt->GetTableHInsert()), FieldUnit::TWIP); + m_xColInsertMF->set_value(m_xColInsertMF->normalize(pModOpt->GetTableVInsert()), FieldUnit::TWIP); + + switch(pModOpt->GetTableMode()) + { + case TableChgMode::FixedWidthChangeAbs: m_xFixRB->set_active(true); break; + case TableChgMode::FixedWidthChangeProp: m_xFixPropRB->set_active(true); break; + case TableChgMode::VarWidthChangeAbs: m_xVarRB->set_active(true); break; + } + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet->GetItemState(SID_HTML_MODE, false, &pItem)) + { + m_bHTMLMode = 0 != (static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON); + } + + // hide certain controls for html + if (m_bHTMLMode) + { + m_xRepeatHeaderCB->hide(); + m_xDontSplitCB->hide(); + } + + SwInsertTableOptions aInsOpts = pModOpt->GetInsTableFlags(m_bHTMLMode); + const SwInsertTableFlags nInsTableFlags = aInsOpts.mnInsMode; + + m_xHeaderCB->set_active(bool(nInsTableFlags & SwInsertTableFlags::Headline)); + m_xRepeatHeaderCB->set_active((!m_bHTMLMode) && (aInsOpts.mnRowsToRepeat > 0)); + m_xDontSplitCB->set_active(!(nInsTableFlags & SwInsertTableFlags::SplitLayout)); + m_xBorderCB->set_active(bool(nInsTableFlags & SwInsertTableFlags::DefaultBorder)); + + m_xNumFormattingCB->set_active(pModOpt->IsInsTableFormatNum(m_bHTMLMode)); + m_xNumFormatFormattingCB->set_active(pModOpt->IsInsTableChangeNumFormat(m_bHTMLMode)); + m_xNumAlignmentCB->set_active(pModOpt->IsInsTableAlignNum(m_bHTMLMode)); + + m_xHeaderCB->save_state(); + m_xRepeatHeaderCB->save_state(); + m_xDontSplitCB->save_state(); + m_xBorderCB->save_state(); + m_xNumFormattingCB->save_state(); + m_xNumFormatFormattingCB->save_state(); + m_xNumAlignmentCB->save_state(); + m_xRowMoveMF->save_value(); + m_xColMoveMF->save_value(); + m_xRowInsertMF->save_value(); + m_xColInsertMF->save_value(); + + CheckBoxHdl(*m_xHeaderCB); +} + +IMPL_LINK_NOARG(SwTableOptionsTabPage, CheckBoxHdl, weld::Button&, void) +{ + m_xNumFormatFormattingCB->set_sensitive(m_xNumFormattingCB->get_active()); + m_xNumAlignmentCB->set_sensitive(m_xNumFormattingCB->get_active()); + m_xRepeatHeaderCB->set_sensitive(m_xHeaderCB->get_active()); +} + +void SwTableOptionsTabPage::PageCreated( const SfxAllItemSet& aSet) +{ + const SwWrtShellItem* pWrtSh = aSet.GetItem<SwWrtShellItem>(SID_WRT_SHELL, false); + if (pWrtSh) + m_pWrtShell = pWrtSh->GetValue(); +} + +SwShdwCursorOptionsTabPage::SwShdwCursorOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optformataidspage.ui", "OptFormatAidsPage", &rSet) + , m_pWrtShell(nullptr) + , m_xParaCB(m_xBuilder->weld_check_button("paragraph")) + , m_xSHyphCB(m_xBuilder->weld_check_button("hyphens")) + , m_xSpacesCB(m_xBuilder->weld_check_button("spaces")) + , m_xHSpacesCB(m_xBuilder->weld_check_button("nonbreak")) + , m_xTabCB(m_xBuilder->weld_check_button("tabs")) + , m_xTabLabel(m_xBuilder->weld_label("tabs_label")) + , m_xBreakCB(m_xBuilder->weld_check_button("break")) + , m_xCharHiddenCB(m_xBuilder->weld_check_button("hiddentext")) + , m_xBookmarkCB(m_xBuilder->weld_check_button("bookmarks")) + , m_xBookmarkLabel(m_xBuilder->weld_label("bookmarks_label")) + , m_xDirectCursorFrame(m_xBuilder->weld_frame("directcrsrframe")) + , m_xOnOffCB(m_xBuilder->weld_check_button("cursoronoff")) + , m_xDirectCursorFillMode(m_xBuilder->weld_combo_box("cxDirectCursorFillMode")) + , m_xCursorProtFrame(m_xBuilder->weld_frame("crsrprotframe")) + , m_xCursorInProtCB(m_xBuilder->weld_check_button("cursorinprot")) + , m_xMathBaselineAlignmentCB(m_xBuilder->weld_check_button("mathbaseline")) +{ + const SfxPoolItem* pItem = nullptr; + SwFillMode eMode = SwFillMode::Tab; + bool bIsOn = false; + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_SHADOWCURSOR, false, &pItem )) + { + auto& aOpt = *static_cast<const SwShadowCursorItem*>(pItem); + eMode = aOpt.GetMode(); + bIsOn = aOpt.IsOn(); + } + m_xOnOffCB->set_active( bIsOn ); + + m_xDirectCursorFillMode->set_active( static_cast<int>(eMode) ); + if(SfxItemState::SET != rSet.GetItemState(SID_HTML_MODE, false, &pItem ) + || !(static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON)) + return; + + m_xTabCB->hide(); + m_xTabLabel->hide(); + m_xCharHiddenCB->hide(); + m_xBookmarkCB->hide(); + m_xBookmarkLabel->hide(); + + m_xDirectCursorFrame->hide(); + m_xOnOffCB->hide(); + m_xDirectCursorFillMode->hide(); + m_xCursorProtFrame->hide(); + m_xCursorInProtCB->hide(); +} + +SwShdwCursorOptionsTabPage::~SwShdwCursorOptionsTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwShdwCursorOptionsTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet ) +{ + return std::make_unique<SwShdwCursorOptionsTabPage>(pPage, pController, *rSet); +} + +void SwShdwCursorOptionsTabPage::PageCreated( const SfxAllItemSet& aSet ) +{ + const SwWrtShellItem* pWrtSh = aSet.GetItem<SwWrtShellItem>(SID_WRT_SHELL, false); + if (pWrtSh) + m_pWrtShell = pWrtSh->GetValue(); +} + +bool SwShdwCursorOptionsTabPage::FillItemSet( SfxItemSet* rSet ) +{ + SwShadowCursorItem aOpt; + aOpt.SetOn( m_xOnOffCB->get_active() ); + + SwFillMode eMode = static_cast<SwFillMode>(m_xDirectCursorFillMode->get_active()); + aOpt.SetMode( eMode ); + + bool bRet = false; + const SfxPoolItem* pItem = nullptr; + if( SfxItemState::SET != rSet->GetItemState( FN_PARAM_SHADOWCURSOR, false, &pItem ) + || static_cast<const SwShadowCursorItem&>(*pItem) != aOpt ) + { + rSet->Put( aOpt ); + bRet = true; + } + + if (m_pWrtShell) { + m_pWrtShell->GetDoc()->getIDocumentSettingAccess().set( DocumentSettingId::MATH_BASELINE_ALIGNMENT, + m_xMathBaselineAlignmentCB->get_active() ); + bRet |= m_xMathBaselineAlignmentCB->get_state_changed_from_saved(); + } + + if( m_xCursorInProtCB->get_state_changed_from_saved()) + { + rSet->Put(SfxBoolItem(FN_PARAM_CRSR_IN_PROTECTED, m_xCursorInProtCB->get_active())); + bRet = true; + } + + const SwDocDisplayItem* pOldAttr = static_cast<const SwDocDisplayItem*>( + GetOldItem(GetItemSet(), FN_PARAM_DOCDISP)); + + SwDocDisplayItem aDisp; + + aDisp.bParagraphEnd = m_xParaCB->get_active(); + aDisp.bTab = m_xTabCB->get_active(); + aDisp.bSpace = m_xSpacesCB->get_active(); + aDisp.bNonbreakingSpace = m_xHSpacesCB->get_active(); + aDisp.bSoftHyphen = m_xSHyphCB->get_active(); + aDisp.bCharHiddenText = m_xCharHiddenCB->get_active(); + aDisp.bBookmarks = m_xBookmarkCB->get_active(); + aDisp.bManualBreak = m_xBreakCB->get_active(); + + bRet |= (!pOldAttr || aDisp != *pOldAttr); + if(bRet) + bRet = nullptr != rSet->Put(aDisp); + + return bRet; +} + +void SwShdwCursorOptionsTabPage::Reset( const SfxItemSet* rSet ) +{ + const SfxPoolItem* pItem = nullptr; + SwFillMode eMode = SwFillMode::Tab; + bool bIsOn = false; + + if( SfxItemState::SET == rSet->GetItemState( FN_PARAM_SHADOWCURSOR, false, &pItem )) + { + auto& aOpt = *static_cast<const SwShadowCursorItem*>(pItem); + eMode = aOpt.GetMode(); + bIsOn = aOpt.IsOn(); + } + m_xOnOffCB->set_active( bIsOn ); + + m_xDirectCursorFillMode->set_active( static_cast<int>(eMode) ); + if (m_pWrtShell) { + m_xMathBaselineAlignmentCB->set_active( m_pWrtShell->GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ) ); + m_xMathBaselineAlignmentCB->save_state(); + } else { + m_xMathBaselineAlignmentCB->hide(); + } + + if( SfxItemState::SET == rSet->GetItemState( FN_PARAM_CRSR_IN_PROTECTED, false, &pItem )) + m_xCursorInProtCB->set_active(static_cast<const SfxBoolItem*>(pItem)->GetValue()); + m_xCursorInProtCB->save_state(); + + const SwDocDisplayItem* pDocDisplayAttr = nullptr; + + rSet->GetItemState( FN_PARAM_DOCDISP, false, + reinterpret_cast<const SfxPoolItem**>(&pDocDisplayAttr) ); + if(pDocDisplayAttr) + { + m_xParaCB->set_active( pDocDisplayAttr->bParagraphEnd ); + m_xTabCB->set_active( pDocDisplayAttr->bTab ); + m_xSpacesCB->set_active( pDocDisplayAttr->bSpace ); + m_xHSpacesCB->set_active( pDocDisplayAttr->bNonbreakingSpace ); + m_xSHyphCB->set_active( pDocDisplayAttr->bSoftHyphen ); + m_xCharHiddenCB->set_active( pDocDisplayAttr->bCharHiddenText ); + m_xBookmarkCB->set_active(pDocDisplayAttr->bBookmarks); + m_xBreakCB->set_active( pDocDisplayAttr->bManualBreak ); + } +} + +namespace { + +// TabPage for Redlining +struct CharAttr +{ + sal_uInt16 nItemId; + sal_uInt16 nAttr; +}; + +} + +// Edit corresponds to Paste-attributes +static CharAttr const aRedlineAttr[] = +{ + { SID_ATTR_CHAR_CASEMAP, sal_uInt16(SvxCaseMap::NotMapped) }, + { SID_ATTR_CHAR_WEIGHT, WEIGHT_BOLD }, + { SID_ATTR_CHAR_POSTURE, ITALIC_NORMAL }, + { SID_ATTR_CHAR_UNDERLINE, LINESTYLE_SINGLE }, + { SID_ATTR_CHAR_UNDERLINE, LINESTYLE_DOUBLE }, + { SID_ATTR_CHAR_STRIKEOUT, STRIKEOUT_SINGLE }, + { SID_ATTR_CHAR_CASEMAP, sal_uInt16(SvxCaseMap::Uppercase) }, + { SID_ATTR_CHAR_CASEMAP, sal_uInt16(SvxCaseMap::Lowercase) }, + { SID_ATTR_CHAR_CASEMAP, sal_uInt16(SvxCaseMap::SmallCaps) }, + { SID_ATTR_CHAR_CASEMAP, sal_uInt16(SvxCaseMap::Capitalize) }, + { SID_ATTR_BRUSH, 0 } +}; +// Items from aRedlineAttr relevant for InsertAttr: strikethrough is +// not used +static sal_uInt16 aInsertAttrMap[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 }; + +// Items from aRedlineAttr relevant for DeleteAttr: underline and +// double underline is not used +static sal_uInt16 aDeletedAttrMap[] = { 0, 1, 2, 5, 6, 7, 8, 9, 10 }; + +// Items from aRedlineAttr relevant for ChangeAttr: strikethrough is +// not used +static sal_uInt16 aChangedAttrMap[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 }; + +// Preview of selection +SwMarkPreview::SwMarkPreview() + : m_aTransCol(COL_TRANSPARENT) + , m_aMarkCol(COL_LIGHTRED) + , nMarkPos(0) + +{ + InitColors(); +} + +SwMarkPreview::~SwMarkPreview() +{ +} + +void SwMarkPreview::InitColors() +{ + // m_aTransCol and m_aMarkCol are _not_ changed because they are set from outside! + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_aBgCol = rSettings.GetWindowColor(); + + bool bHC = rSettings.GetHighContrastMode(); + m_aLineCol = bHC? SwViewOption::GetFontColor() : COL_BLACK; + m_aShadowCol = bHC? m_aBgCol : rSettings.GetShadowColor(); + m_aTextCol = bHC? SwViewOption::GetFontColor() : COL_GRAY; + m_aPrintAreaCol = m_aTextCol; +} + +void SwMarkPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &/*rRect*/) +{ + const Size aSz(GetOutputSizePixel()); + + // Page + aPage.SetSize(Size(aSz.Width() - 3, aSz.Height() - 3)); + + const long nOutWPix = aPage.GetWidth(); + const long nOutHPix = aPage.GetHeight(); + + // PrintArea + const long nLBorder = 8; + const long nRBorder = 8; + const long nTBorder = 4; + const long nBBorder = 4; + + aLeftPagePrtArea = tools::Rectangle(Point(nLBorder, nTBorder), Point((nOutWPix - 1) - nRBorder, (nOutHPix - 1) - nBBorder)); + const long nWidth = aLeftPagePrtArea.GetWidth(); + const long nCorr = (nWidth & 1) != 0 ? 0 : 1; + aLeftPagePrtArea.SetSize(Size(nWidth / 2 - (nLBorder + nRBorder) / 2 + nCorr, aLeftPagePrtArea.GetHeight())); + + aRightPagePrtArea = aLeftPagePrtArea; + aRightPagePrtArea.Move(aLeftPagePrtArea.GetWidth() + nLBorder + nRBorder + 1, 0); + + // draw shadow + tools::Rectangle aShadow(aPage); + aShadow += Point(3, 3); + drawRect(rRenderContext, aShadow, m_aShadowCol, m_aTransCol); + + // draw page + drawRect(rRenderContext, aPage, m_aBgCol, m_aLineCol); + + // draw separator + tools::Rectangle aPageSeparator(aPage); + aPageSeparator.SetSize(Size(2, aPageSeparator.GetHeight())); + aPageSeparator.Move(aPage.GetWidth() / 2 - 1, 0); + drawRect(rRenderContext, aPageSeparator, m_aLineCol, m_aTransCol); + + PaintPage(rRenderContext, aLeftPagePrtArea); + PaintPage(rRenderContext, aRightPagePrtArea); + + tools::Rectangle aLeftMark(Point(aPage.Left() + 2, aLeftPagePrtArea.Top() + 4), Size(aLeftPagePrtArea.Left() - 4, 2)); + tools::Rectangle aRightMark(Point(aRightPagePrtArea.Right() + 2, aRightPagePrtArea.Bottom() - 6), Size(aLeftPagePrtArea.Left() - 4, 2)); + + switch (nMarkPos) + { + case 1: // left + aRightMark.SetPos(Point(aRightPagePrtArea.Left() - 2 - aRightMark.GetWidth(), aRightMark.Top())); + break; + + case 2: // right + aLeftMark.SetPos(Point(aLeftPagePrtArea.Right() + 2, aLeftMark.Top())); + break; + + case 3: // outside + break; + + case 4: // inside + aLeftMark.SetPos(Point(aLeftPagePrtArea.Right() + 2, aLeftMark.Top())); + aRightMark.SetPos(Point(aRightPagePrtArea.Left() - 2 - aRightMark.GetWidth(), aRightMark.Top())); + break; + + case 0: // none + default: + return; + } + drawRect(rRenderContext, aLeftMark, m_aMarkCol, m_aTransCol); + drawRect(rRenderContext, aRightMark, m_aMarkCol, m_aTransCol); +} + +void SwMarkPreview::PaintPage(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect) +{ + // draw PrintArea + drawRect(rRenderContext, rRect, m_aTransCol, m_aPrintAreaCol); + + // draw Testparagraph + + tools::Rectangle aTextLine = rRect; + aTextLine.SetSize(Size(aTextLine.GetWidth(), 2)); + aTextLine.AdjustLeft(4 ); + aTextLine.AdjustRight( -4 ); + aTextLine.Move(0, 4); + + const long nStep = aTextLine.GetHeight() + 2; + const long nLines = rRect.GetHeight() / (aTextLine.GetHeight() + 2) - 1; + + // simulate text + for (long i = 0; i < nLines; ++i) + { + if (i == (nLines - 1)) + aTextLine.SetSize(Size(aTextLine.GetWidth() / 2, aTextLine.GetHeight())); + + if (aPage.IsInside(aTextLine)) + drawRect(rRenderContext, aTextLine, m_aTextCol, m_aTransCol); + + aTextLine.Move(0, nStep); + } + aTextLine.Move(0, -nStep); +} + +void SwMarkPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aInitialSize = getPreviewOptionsSize(pDrawingArea->get_ref_device()); + pDrawingArea->set_size_request(aInitialSize.Width(), aInitialSize.Height()); + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aInitialSize); +} + +namespace +{ + void lcl_FillRedlineAttrListBox( + weld::ComboBox& rLB, const AuthorCharAttr& rAttrToSelect, + const sal_uInt16* pAttrMap, const size_t nAttrMapSize) + { + for (size_t i = 0; i != nAttrMapSize; ++i) + { + CharAttr const & rAttr(aRedlineAttr[pAttrMap[i]]); + rLB.set_id(i, OUString::number(reinterpret_cast<sal_Int64>(&rAttr))); + if (rAttr.nItemId == rAttrToSelect.m_nItemId && + rAttr.nAttr == rAttrToSelect.m_nAttr) + rLB.set_active(i); + } + } +} + +SwRedlineOptionsTabPage::SwRedlineOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optredlinepage.ui", "OptRedLinePage", &rSet) + , m_xInsertLB(m_xBuilder->weld_combo_box("insert")) + , m_xInsertColorLB(new ColorListBox(m_xBuilder->weld_menu_button("insertcolor"), pController->getDialog())) + , m_xInsertedPreviewWN(new SvxFontPrevWindow) + , m_xInsertedPreview(new weld::CustomWeld(*m_xBuilder, "insertedpreview", *m_xInsertedPreviewWN)) + , m_xDeletedLB(m_xBuilder->weld_combo_box("deleted")) + , m_xDeletedColorLB(new ColorListBox(m_xBuilder->weld_menu_button("deletedcolor"), pController->getDialog())) + , m_xDeletedPreviewWN(new SvxFontPrevWindow) + , m_xDeletedPreview(new weld::CustomWeld(*m_xBuilder, "deletedpreview", *m_xDeletedPreviewWN)) + , m_xChangedLB(m_xBuilder->weld_combo_box("changed")) + , m_xChangedColorLB(new ColorListBox(m_xBuilder->weld_menu_button("changedcolor"), pController->getDialog())) + , m_xChangedPreviewWN(new SvxFontPrevWindow) + , m_xChangedPreview(new weld::CustomWeld(*m_xBuilder, "changedpreview", *m_xChangedPreviewWN)) + , m_xMarkPosLB(m_xBuilder->weld_combo_box("markpos")) + , m_xMarkColorLB(new ColorListBox(m_xBuilder->weld_menu_button("markcolor"), pController->getDialog())) + , m_xMarkPreviewWN(new SwMarkPreview) + , m_xMarkPreview(new weld::CustomWeld(*m_xBuilder, "markpreview", *m_xMarkPreviewWN)) +{ + Size aPreviewSize(getPreviewOptionsSize(m_xMarkPreviewWN->GetDrawingArea()->get_ref_device())); + + m_xInsertColorLB->SetSlotId(SID_AUTHOR_COLOR, true); + m_xDeletedColorLB->SetSlotId(SID_AUTHOR_COLOR, true); + m_xChangedColorLB->SetSlotId(SID_AUTHOR_COLOR, true); + + m_xInsertedPreviewWN->set_size_request(aPreviewSize.Width(), aPreviewSize.Height()); + m_xDeletedPreviewWN->set_size_request(aPreviewSize.Width(), aPreviewSize.Height()); + m_xChangedPreviewWN->set_size_request(aPreviewSize.Width(), aPreviewSize.Height()); + m_xMarkPreviewWN->set_size_request(aPreviewSize.Width(), aPreviewSize.Height()); + + for (sal_Int32 i = 0, nEntryCount = m_xInsertLB->get_count(); i < nEntryCount; ++i) + { + const OUString sEntry(m_xInsertLB->get_text(i)); + m_xDeletedLB->append_text(sEntry); + m_xChangedLB->append_text(sEntry); + }; + + // remove strikethrough from insert and change and underline + double + // underline from delete + m_xInsertLB->remove(5); + m_xChangedLB->remove(5); + m_xDeletedLB->remove(4); + m_xDeletedLB->remove(3); + + Link<weld::ComboBox&,void> aLk = LINK(this, SwRedlineOptionsTabPage, AttribHdl); + m_xInsertLB->connect_changed( aLk ); + m_xDeletedLB->connect_changed( aLk ); + m_xChangedLB->connect_changed( aLk ); + + Link<ColorListBox&,void> aLk2 = LINK(this, SwRedlineOptionsTabPage, ColorHdl); + m_xInsertColorLB->SetSelectHdl( aLk2 ); + m_xDeletedColorLB->SetSelectHdl( aLk2 ); + m_xChangedColorLB->SetSelectHdl( aLk2 ); + + m_xMarkPosLB->connect_changed(LINK(this, SwRedlineOptionsTabPage, ChangedMaskPrevHdl)); + m_xMarkColorLB->SetSelectHdl(LINK(this, SwRedlineOptionsTabPage, ChangedMaskColorPrevHdl)); +} + +SwRedlineOptionsTabPage::~SwRedlineOptionsTabPage() +{ + m_xInsertColorLB.reset(); + m_xInsertedPreview.reset(); + m_xInsertedPreviewWN.reset(); + m_xDeletedColorLB.reset(); + m_xDeletedPreview.reset(); + m_xDeletedPreviewWN.reset(); + m_xChangedColorLB.reset(); + m_xChangedPreview.reset(); + m_xChangedPreviewWN.reset(); + m_xMarkColorLB.reset(); + m_xMarkPreview.reset(); + m_xMarkPreviewWN.reset(); +} + +std::unique_ptr<SfxTabPage> SwRedlineOptionsTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwRedlineOptionsTabPage>(pPage, pController, *rSet); +} + +bool SwRedlineOptionsTabPage::FillItemSet( SfxItemSet* ) +{ + CharAttr *pAttr; + SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig(); + + AuthorCharAttr aInsertedAttr; + AuthorCharAttr aDeletedAttr; + AuthorCharAttr aChangedAttr; + + AuthorCharAttr aOldInsertAttr(pOpt->GetInsertAuthorAttr()); + AuthorCharAttr aOldDeletedAttr(pOpt->GetDeletedAuthorAttr()); + AuthorCharAttr aOldChangedAttr(pOpt->GetFormatAuthorAttr()); + + Color nOldMarkColor = pOpt->GetMarkAlignColor(); + sal_uInt16 nOldMarkMode = pOpt->GetMarkAlignMode(); + + sal_Int32 nPos = m_xInsertLB->get_active(); + if (nPos != -1) + { + pAttr = reinterpret_cast<CharAttr*>(m_xInsertLB->get_id(nPos).toInt64()); + aInsertedAttr.m_nItemId = pAttr->nItemId; + aInsertedAttr.m_nAttr = pAttr->nAttr; + aInsertedAttr.m_nColor = m_xInsertColorLB->GetSelectEntryColor(); + pOpt->SetInsertAuthorAttr(aInsertedAttr); + } + + nPos = m_xDeletedLB->get_active(); + if (nPos != -1) + { + pAttr = reinterpret_cast<CharAttr*>(m_xDeletedLB->get_id(nPos).toInt64()); + aDeletedAttr.m_nItemId = pAttr->nItemId; + aDeletedAttr.m_nAttr = pAttr->nAttr; + aDeletedAttr.m_nColor = m_xDeletedColorLB->GetSelectEntryColor(); + pOpt->SetDeletedAuthorAttr(aDeletedAttr); + } + + nPos = m_xChangedLB->get_active(); + if (nPos != -1) + { + pAttr = reinterpret_cast<CharAttr*>(m_xChangedLB->get_id(nPos).toInt64()); + aChangedAttr.m_nItemId = pAttr->nItemId; + aChangedAttr.m_nAttr = pAttr->nAttr; + aChangedAttr.m_nColor = m_xChangedColorLB->GetSelectEntryColor(); + pOpt->SetFormatAuthorAttr(aChangedAttr); + } + + nPos = 0; + switch (m_xMarkPosLB->get_active()) + { + case 0: nPos = text::HoriOrientation::NONE; break; + case 1: nPos = text::HoriOrientation::LEFT; break; + case 2: nPos = text::HoriOrientation::RIGHT; break; + case 3: nPos = text::HoriOrientation::OUTSIDE; break; + case 4: nPos = text::HoriOrientation::INSIDE; break; + } + pOpt->SetMarkAlignMode(nPos); + pOpt->SetMarkAlignColor(m_xMarkColorLB->GetSelectEntryColor()); + + if (!(aInsertedAttr == aOldInsertAttr) || + !(aDeletedAttr == aOldDeletedAttr) || + !(aChangedAttr == aOldChangedAttr) || + nOldMarkColor != pOpt->GetMarkAlignColor() || + nOldMarkMode != pOpt->GetMarkAlignMode() ) + { + // update all documents + SwDocShell* pDocShell = static_cast<SwDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<SwDocShell>)); + + while( pDocShell ) + { + pDocShell->GetWrtShell()->UpdateRedlineAttr(); + pDocShell = static_cast<SwDocShell*>(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell<SwDocShell>)); + } + } + + return false; +} + +void SwRedlineOptionsTabPage::Reset( const SfxItemSet* ) +{ + const SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig(); + + const AuthorCharAttr &rInsertAttr = pOpt->GetInsertAuthorAttr(); + const AuthorCharAttr &rDeletedAttr = pOpt->GetDeletedAuthorAttr(); + const AuthorCharAttr &rChangedAttr = pOpt->GetFormatAuthorAttr(); + + // initialise preview + InitFontStyle(*m_xInsertedPreviewWN, SwResId(STR_OPT_PREVIEW_INSERTED)); + InitFontStyle(*m_xDeletedPreviewWN, SwResId(STR_OPT_PREVIEW_DELETED)); + InitFontStyle(*m_xChangedPreviewWN, SwResId(STR_OPT_PREVIEW_CHANGED)); + + Color nColor = rInsertAttr.m_nColor; + m_xInsertColorLB->SelectEntry(nColor); + + nColor = rDeletedAttr.m_nColor; + m_xDeletedColorLB->SelectEntry(nColor); + + nColor = rChangedAttr.m_nColor; + m_xChangedColorLB->SelectEntry(nColor); + + m_xMarkColorLB->SelectEntry(pOpt->GetMarkAlignColor()); + + m_xInsertLB->set_active(0); + m_xDeletedLB->set_active(0); + m_xChangedLB->set_active(0); + + lcl_FillRedlineAttrListBox(*m_xInsertLB, rInsertAttr, aInsertAttrMap, SAL_N_ELEMENTS(aInsertAttrMap)); + lcl_FillRedlineAttrListBox(*m_xDeletedLB, rDeletedAttr, aDeletedAttrMap, SAL_N_ELEMENTS(aDeletedAttrMap)); + lcl_FillRedlineAttrListBox(*m_xChangedLB, rChangedAttr, aChangedAttrMap, SAL_N_ELEMENTS(aChangedAttrMap)); + + sal_Int32 nPos = 0; + switch (pOpt->GetMarkAlignMode()) + { + case text::HoriOrientation::NONE: nPos = 0; break; + case text::HoriOrientation::LEFT: nPos = 1; break; + case text::HoriOrientation::RIGHT: nPos = 2; break; + case text::HoriOrientation::OUTSIDE: nPos = 3; break; + case text::HoriOrientation::INSIDE: nPos = 4; break; + } + m_xMarkPosLB->set_active(nPos); + + // show settings in preview + AttribHdl(*m_xInsertLB); + ColorHdl(*m_xInsertColorLB); + AttribHdl(*m_xDeletedLB); + ColorHdl(*m_xInsertColorLB); + AttribHdl(*m_xChangedLB); + ColorHdl(*m_xChangedColorLB); + + ChangedMaskPrev(); +} + +IMPL_LINK( SwRedlineOptionsTabPage, AttribHdl, weld::ComboBox&, rLB, void ) +{ + SvxFontPrevWindow *pPrev = nullptr; + ColorListBox *pColorLB; + + if (&rLB == m_xInsertLB.get()) + { + pColorLB = m_xInsertColorLB.get(); + pPrev = m_xInsertedPreviewWN.get(); + } + else if (&rLB == m_xDeletedLB.get()) + { + pColorLB = m_xDeletedColorLB.get(); + pPrev = m_xDeletedPreviewWN.get(); + } + else + { + pColorLB = m_xChangedColorLB.get(); + pPrev = m_xChangedPreviewWN.get(); + } + + SvxFont& rFont = pPrev->GetFont(); + SvxFont& rCJKFont = pPrev->GetCJKFont(); + + rFont.SetWeight(WEIGHT_NORMAL); + rCJKFont.SetWeight(WEIGHT_NORMAL); + rFont.SetItalic(ITALIC_NONE); + rCJKFont.SetItalic(ITALIC_NONE); + rFont.SetUnderline(LINESTYLE_NONE); + rCJKFont.SetUnderline(LINESTYLE_NONE); + rFont.SetStrikeout(STRIKEOUT_NONE); + rCJKFont.SetStrikeout(STRIKEOUT_NONE); + rFont.SetCaseMap(SvxCaseMap::NotMapped); + rCJKFont.SetCaseMap(SvxCaseMap::NotMapped); + + Color aColor = pColorLB->GetSelectEntryColor(); + + if (aColor == COL_NONE_COLOR) + { + rFont.SetColor( COL_BLACK ); + rCJKFont.SetColor( COL_BLACK ); + } + else if (aColor == COL_TRANSPARENT) + { + rFont.SetColor( COL_RED ); + rCJKFont.SetColor( COL_RED ); + } + else + { + rFont.SetColor(aColor); + rCJKFont.SetColor(aColor); + } + + sal_Int32 nPos = rLB.get_active(); + if( nPos == -1) + nPos = 0; + + CharAttr* pAttr = reinterpret_cast<CharAttr*>(rLB.get_id(nPos).toInt64()); + //switch off preview background color + pPrev->ResetColor(); + switch (pAttr->nItemId) + { + case SID_ATTR_CHAR_WEIGHT: + rFont.SetWeight( static_cast<FontWeight>(pAttr->nAttr) ); + rCJKFont.SetWeight( static_cast<FontWeight>(pAttr->nAttr) ); + break; + + case SID_ATTR_CHAR_POSTURE: + rFont.SetItalic( static_cast<FontItalic>(pAttr->nAttr) ); + rCJKFont.SetItalic( static_cast<FontItalic>(pAttr->nAttr) ); + break; + + case SID_ATTR_CHAR_UNDERLINE: + rFont.SetUnderline( static_cast<FontLineStyle>(pAttr->nAttr) ); + rCJKFont.SetUnderline( static_cast<FontLineStyle>(pAttr->nAttr) ); + break; + + case SID_ATTR_CHAR_STRIKEOUT: + rFont.SetStrikeout( static_cast<FontStrikeout>(pAttr->nAttr) ); + rCJKFont.SetStrikeout( static_cast<FontStrikeout>(pAttr->nAttr) ); + break; + + case SID_ATTR_CHAR_CASEMAP: + rFont.SetCaseMap( static_cast<SvxCaseMap>(pAttr->nAttr) ); + rCJKFont.SetCaseMap( static_cast<SvxCaseMap>(pAttr->nAttr) ); + break; + + case SID_ATTR_BRUSH: + { + Color aBgColor = pColorLB->GetSelectEntryColor(); + if (aBgColor != COL_NONE_COLOR) + pPrev->SetColor(aBgColor); + else + pPrev->SetColor(COL_LIGHTGRAY); + rFont.SetColor( COL_BLACK ); + rCJKFont.SetColor( COL_BLACK ); + } + break; + } + + pPrev->Invalidate(); +} + +IMPL_LINK(SwRedlineOptionsTabPage, ColorHdl, ColorListBox&, rListBox, void) +{ + ColorListBox* pColorLB = &rListBox; + SvxFontPrevWindow *pPrev = nullptr; + weld::ComboBox* pLB; + + if (pColorLB == m_xInsertColorLB.get()) + { + pLB = m_xInsertLB.get(); + pPrev = m_xInsertedPreviewWN.get(); + } + else if (pColorLB == m_xDeletedColorLB.get()) + { + pLB = m_xDeletedLB.get(); + pPrev = m_xDeletedPreviewWN.get(); + } + else + { + pLB = m_xChangedLB.get(); + pPrev = m_xChangedPreviewWN.get(); + } + + SvxFont& rFont = pPrev->GetFont(); + SvxFont& rCJKFont = pPrev->GetCJKFont(); + sal_Int32 nPos = pLB->get_active(); + if( nPos == -1) + nPos = 0; + + CharAttr* pAttr = reinterpret_cast<CharAttr*>(pLB->get_id(nPos).toInt64()); + + if( pAttr->nItemId == SID_ATTR_BRUSH ) + { + rFont.SetColor( COL_BLACK ); + rCJKFont.SetColor( COL_BLACK ); + + Color aBgColor = pColorLB->GetSelectEntryColor(); + if (aBgColor != COL_NONE_COLOR) + pPrev->SetColor(aBgColor); + else + pPrev->SetColor(COL_LIGHTGRAY); + } + else + { + Color aColor = pColorLB->GetSelectEntryColor(); + + if (aColor == COL_NONE_COLOR) + { + rFont.SetColor( COL_BLACK ); + rCJKFont.SetColor( COL_BLACK ); + } + else if (aColor == COL_TRANSPARENT) + { + rFont.SetColor( COL_RED ); + rCJKFont.SetColor( COL_RED ); + } + else + { + rFont.SetColor(aColor); + rCJKFont.SetColor(aColor); + } + } + + pPrev->Invalidate(); +} + +void SwRedlineOptionsTabPage::ChangedMaskPrev() +{ + m_xMarkPreviewWN->SetMarkPos(m_xMarkPosLB->get_active()); + m_xMarkPreviewWN->SetColor(m_xMarkColorLB->GetSelectEntryColor()); + + m_xMarkPreviewWN->Invalidate(); +} + +IMPL_LINK_NOARG(SwRedlineOptionsTabPage, ChangedMaskPrevHdl, weld::ComboBox&, void) +{ + ChangedMaskPrev(); +} + +IMPL_LINK_NOARG(SwRedlineOptionsTabPage, ChangedMaskColorPrevHdl, ColorListBox&, void) +{ + ChangedMaskPrev(); +} + +void SwRedlineOptionsTabPage::InitFontStyle(SvxFontPrevWindow& rExampleWin, const OUString& rText) +{ + const AllSettings& rAllSettings = Application::GetSettings(); + LanguageType eLangType = rAllSettings.GetUILanguageTag().getLanguageType(); + Color aBackCol( rAllSettings.GetStyleSettings().GetWindowColor() ); + SvxFont& rFont = rExampleWin.GetFont(); + SvxFont& rCJKFont = rExampleWin.GetCJKFont(); + SvxFont& rCTLFont = rExampleWin.GetCTLFont(); + + OutputDevice& rDevice = rExampleWin.GetDrawingArea()->get_ref_device(); + + vcl::Font aFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, eLangType, + GetDefaultFontFlags::OnlyOne, &rDevice ) ); + vcl::Font aCJKFont( OutputDevice::GetDefaultFont( DefaultFontType::CJK_TEXT, eLangType, + GetDefaultFontFlags::OnlyOne, &rDevice ) ); + vcl::Font aCTLFont( OutputDevice::GetDefaultFont( DefaultFontType::CTL_TEXT, eLangType, + GetDefaultFontFlags::OnlyOne, &rDevice ) ); + const Size aDefSize( 0, 12 ); + aFont.SetFontSize( aDefSize ); + aCJKFont.SetFontSize( aDefSize ); + aCTLFont.SetFontSize( aDefSize ); + + aFont.SetFillColor( aBackCol ); + aCJKFont.SetFillColor( aBackCol ); + aCTLFont.SetFillColor( aBackCol ); + + aFont.SetWeight( WEIGHT_NORMAL ); + aCJKFont.SetWeight( WEIGHT_NORMAL ); + aCTLFont.SetWeight( WEIGHT_NORMAL ); + + rFont = aFont; + rCJKFont = aCJKFont; + rCTLFont = aCTLFont; + + const Size aNewSize( 0, rExampleWin.GetOutputSizePixel().Height() * 2 / 3 ); + rFont.SetFontSize( aNewSize ); + rCJKFont.SetFontSize( aNewSize ); + + rExampleWin.SetFont( rFont, rCJKFont,rCTLFont ); + rExampleWin.SetPreviewText(rText); + + rExampleWin.SetBackColor(aBackCol); +} + +SwCompareOptionsTabPage::SwCompareOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/optcomparison.ui", "OptComparison", &rSet) + , m_xAutoRB(m_xBuilder->weld_radio_button("auto")) + , m_xWordRB(m_xBuilder->weld_radio_button("byword")) + , m_xCharRB(m_xBuilder->weld_radio_button("bycharacter")) + , m_xRsidCB(m_xBuilder->weld_check_button("useRSID")) + , m_xIgnoreCB(m_xBuilder->weld_check_button("ignore")) + , m_xLenNF(m_xBuilder->weld_spin_button("ignorelen")) + , m_xStoreRsidCB(m_xBuilder->weld_check_button("storeRSID")) +{ + Link<weld::Button&,void> aLnk( LINK( this, SwCompareOptionsTabPage, ComparisonHdl ) ); + m_xAutoRB->connect_clicked( aLnk ); + m_xWordRB->connect_clicked( aLnk ); + m_xCharRB->connect_clicked( aLnk ); + + m_xIgnoreCB->connect_clicked( LINK( this, SwCompareOptionsTabPage, IgnoreHdl) ); +} + +SwCompareOptionsTabPage::~SwCompareOptionsTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwCompareOptionsTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwCompareOptionsTabPage>(pPage, pController, *rAttrSet); +} + +bool SwCompareOptionsTabPage::FillItemSet( SfxItemSet* ) +{ + bool bRet = false; + SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig(); + + if( m_xAutoRB->get_state_changed_from_saved() || + m_xWordRB->get_state_changed_from_saved() || + m_xCharRB->get_state_changed_from_saved() ) + { + SwCompareMode eCmpMode = SwCompareMode::Auto; + + if ( m_xAutoRB->get_active() ) eCmpMode = SwCompareMode::Auto; + if ( m_xWordRB->get_active() ) eCmpMode = SwCompareMode::ByWord; + if ( m_xCharRB->get_active() ) eCmpMode = SwCompareMode::ByChar; + + pOpt->SetCompareMode( eCmpMode ); + bRet = true; + } + + if( m_xRsidCB->get_state_changed_from_saved() ) + { + pOpt->SetUseRsid( m_xRsidCB->get_active() ); + bRet = true; + } + + if( m_xIgnoreCB->get_state_changed_from_saved() ) + { + pOpt->SetIgnorePieces( m_xIgnoreCB->get_active() ); + bRet = true; + } + + if( m_xLenNF->get_value_changed_from_saved() ) + { + pOpt->SetPieceLen( m_xLenNF->get_value() ); + bRet = true; + } + + if (m_xStoreRsidCB->get_state_changed_from_saved()) + { + pOpt->SetStoreRsid(m_xStoreRsidCB->get_active()); + bRet = true; + } + + return bRet; +} + +void SwCompareOptionsTabPage::Reset( const SfxItemSet* ) +{ + SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig(); + + SwCompareMode eCmpMode = pOpt->GetCompareMode(); + if( eCmpMode == SwCompareMode::Auto ) + { + m_xAutoRB->set_active(true); + m_xRsidCB->set_sensitive(false); + m_xIgnoreCB->set_sensitive(false); + m_xLenNF->set_sensitive(false); + } + else if( eCmpMode == SwCompareMode::ByWord ) + { + m_xWordRB->set_active(true); + m_xRsidCB->set_sensitive(true); + m_xIgnoreCB->set_sensitive(true); + m_xLenNF->set_sensitive(true); + } + else if( eCmpMode == SwCompareMode::ByChar) + { + m_xCharRB->set_active(true); + m_xRsidCB->set_sensitive(true); + m_xIgnoreCB->set_sensitive(true); + m_xLenNF->set_sensitive(true); + } + m_xAutoRB->save_state(); + m_xWordRB->save_state(); + m_xCharRB->save_state(); + + m_xRsidCB->set_active( pOpt->IsUseRsid() ); + m_xRsidCB->save_state(); + + m_xIgnoreCB->set_active( pOpt->IsIgnorePieces() ); + m_xIgnoreCB->save_state(); + + m_xLenNF->set_sensitive( m_xIgnoreCB->get_active() && eCmpMode != SwCompareMode::Auto ); + + m_xLenNF->set_value( pOpt->GetPieceLen() ); + m_xLenNF->save_value(); + + m_xStoreRsidCB->set_active(pOpt->IsStoreRsid()); + m_xStoreRsidCB->save_state(); +} + +IMPL_LINK_NOARG(SwCompareOptionsTabPage, ComparisonHdl, weld::Button&, void) +{ + bool bChecked = !m_xAutoRB->get_active(); + m_xRsidCB->set_sensitive( bChecked ); + m_xIgnoreCB->set_sensitive( bChecked ); + m_xLenNF->set_sensitive( bChecked && m_xIgnoreCB->get_active() ); +} + +IMPL_LINK_NOARG(SwCompareOptionsTabPage, IgnoreHdl, weld::Button&, void) +{ + m_xLenNF->set_sensitive(m_xIgnoreCB->get_active()); +} + +#ifdef DBG_UTIL + +SwTestTabPage::SwTestTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/opttestpage.ui", "OptTestPage", &rCoreSet) + , bAttrModified( false ) + , m_xTest1CBox(m_xBuilder->weld_check_button("unused")) + , m_xTest2CBox(m_xBuilder->weld_check_button("dynamic")) + , m_xTest3CBox(m_xBuilder->weld_check_button("nocalm")) + , m_xTest4CBox(m_xBuilder->weld_check_button("wysiwygdbg")) + , m_xTest5CBox(m_xBuilder->weld_check_button("noidle")) + , m_xTest6CBox(m_xBuilder->weld_check_button("noscreenadj")) + , m_xTest7CBox(m_xBuilder->weld_check_button("winformat")) + , m_xTest8CBox(m_xBuilder->weld_check_button("noscroll")) + , m_xTest9CBox(m_xBuilder->weld_check_button("DrawingLayerNotLoading")) + , m_xTest10CBox(m_xBuilder->weld_check_button("AutoFormatByInput")) +{ + Init(); +} + +SwTestTabPage::~SwTestTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwTestTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet ) +{ + return std::make_unique<SwTestTabPage>(pPage, pController, *rAttrSet); +} + +bool SwTestTabPage::FillItemSet( SfxItemSet* rCoreSet ) +{ + + if ( bAttrModified ) + { + SwTestItem aTestItem; + aTestItem.m_bTest1=m_xTest1CBox->get_active(); + aTestItem.m_bTest2=m_xTest2CBox->get_active(); + aTestItem.m_bTest3=m_xTest3CBox->get_active(); + aTestItem.m_bTest4=m_xTest4CBox->get_active(); + aTestItem.m_bTest5=m_xTest5CBox->get_active(); + aTestItem.m_bTest6=m_xTest6CBox->get_active(); + aTestItem.m_bTest7=m_xTest7CBox->get_active(); + aTestItem.m_bTest8=m_xTest8CBox->get_active(); + aTestItem.m_bTest9=m_xTest9CBox->get_active(); + aTestItem.m_bTest10=m_xTest10CBox->get_active(); + rCoreSet->Put(aTestItem); + } + return bAttrModified; +} + +void SwTestTabPage::Reset( const SfxItemSet* ) +{ + const SfxItemSet& rSet = GetItemSet(); + const SwTestItem* pTestAttr = nullptr; + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_SWTEST , false, + reinterpret_cast<const SfxPoolItem**>(&pTestAttr) )) + { + m_xTest1CBox->set_active(pTestAttr->m_bTest1); + m_xTest2CBox->set_active(pTestAttr->m_bTest2); + m_xTest3CBox->set_active(pTestAttr->m_bTest3); + m_xTest4CBox->set_active(pTestAttr->m_bTest4); + m_xTest5CBox->set_active(pTestAttr->m_bTest5); + m_xTest6CBox->set_active(pTestAttr->m_bTest6); + m_xTest7CBox->set_active(pTestAttr->m_bTest7); + m_xTest8CBox->set_active(pTestAttr->m_bTest8); + m_xTest9CBox->set_active(pTestAttr->m_bTest9); + m_xTest10CBox->set_active(pTestAttr->m_bTest10); + } +} + +void SwTestTabPage::Init() +{ + // handler + Link<weld::Button&,void> aLk = LINK( this, SwTestTabPage, AutoClickHdl ); + m_xTest1CBox->connect_clicked( aLk ); + m_xTest2CBox->connect_clicked( aLk ); + m_xTest3CBox->connect_clicked( aLk ); + m_xTest4CBox->connect_clicked( aLk ); + m_xTest5CBox->connect_clicked( aLk ); + m_xTest6CBox->connect_clicked( aLk ); + m_xTest7CBox->connect_clicked( aLk ); + m_xTest8CBox->connect_clicked( aLk ); + m_xTest9CBox->connect_clicked( aLk ); + m_xTest10CBox->connect_clicked( aLk ); +} + +IMPL_LINK_NOARG(SwTestTabPage, AutoClickHdl, weld::Button&, void) +{ + bAttrModified = true; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/addresslistdialog.cxx b/sw/source/ui/dbui/addresslistdialog.cxx new file mode 100644 index 000000000..77291826e --- /dev/null +++ b/sw/source/ui/dbui/addresslistdialog.cxx @@ -0,0 +1,639 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "addresslistdialog.hxx" +#include "selectdbtabledialog.hxx" +#include "createaddresslistdialog.hxx" +#include <mailmergewizard.hxx> +#include <mmconfigitem.hxx> +#include "mmaddressblockpage.hxx" +#include <dbmgr.hxx> +#include <dbconfig.hxx> +#include <unotools/tempfile.hxx> +#include <vcl/svapp.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdbc/XCloseable.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/XCompletedConnection.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/FilterDialog.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <swunohelper.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/urihelper.hxx> +#include <strings.hrc> +#include <view.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::ui::dialogs; + +struct AddressUserData_Impl +{ + uno::Reference<XDataSource> xSource; + SharedConnection xConnection; + uno::Reference< XColumnsSupplier> xColumnsSupplier; + uno::Reference< sdbc::XResultSet> xResultSet; + OUString sFilter; + OUString sURL; // data is editable + sal_Int32 nCommandType; + sal_Int32 nTableAndQueryCount; + AddressUserData_Impl() : + nCommandType(0), + nTableAndQueryCount(-1) + {} +}; + +static OUString lcl_getFlatURL( uno::Reference<beans::XPropertySet> const & xSourceProperties ) +{ + if(xSourceProperties.is()) + { + OUString sDBURL; + xSourceProperties->getPropertyValue("URL") >>= sDBURL; + if (sDBURL.startsWith("sdbc:flat:")) + { + uno::Sequence<OUString> aFilters; + xSourceProperties->getPropertyValue("TableFilter") >>= aFilters; + uno::Sequence<PropertyValue> aInfo; + xSourceProperties->getPropertyValue("Info") >>= aInfo; + if(aFilters.getLength() == 1 && aInfo.hasElements() ) + { + OUString sExtension; + OUString sCharSet; + for(const auto& rInfo : std::as_const(aInfo)) + { + if(rInfo.Name == "Extension") + rInfo.Value >>= sExtension; + else if(rInfo.Name == "CharSet") + rInfo.Value >>= sCharSet; + } + if (sCharSet=="UTF-8") + { + //#i97577# at this point the 'URL' can also be a file name! + return URIHelper::SmartRel2Abs( INetURLObject(), sDBURL.copy(10) ) + + "/" + aFilters[0] + "." + sExtension; + } + } + } + } + return OUString(); +} + +SwAddressListDialog::SwAddressListDialog(SwMailMergeAddressBlockPage* pParent) + : SfxDialogController(pParent->GetWizard()->getDialog(), "modules/swriter/ui/selectaddressdialog.ui", "SelectAddressDialog") + , m_bInSelectHdl(false) + , m_pAddressPage(pParent) + , m_xDescriptionFI(m_xBuilder->weld_label("desc")) + , m_xConnecting(m_xBuilder->weld_label("connecting")) + , m_xListLB(m_xBuilder->weld_tree_view("sources")) + , m_xLoadListPB(m_xBuilder->weld_button("add")) + , m_xRemovePB(m_xBuilder->weld_button("remove")) + , m_xCreateListPB(m_xBuilder->weld_button("create")) + , m_xFilterPB(m_xBuilder->weld_button("filter")) + , m_xEditPB(m_xBuilder->weld_button("edit")) + , m_xTablePB(m_xBuilder->weld_button("changetable")) + , m_xOK(m_xBuilder->weld_button("ok")) + , m_xIter(m_xListLB->make_iterator()) +{ + m_sConnecting = m_xConnecting->get_label(); + + const OUString sTemp(m_xDescriptionFI->get_label() + .replaceFirst("%1", m_xLoadListPB->strip_mnemonic(m_xLoadListPB->get_label())) + .replaceFirst("%2", m_xCreateListPB->strip_mnemonic(m_xCreateListPB->get_label()))); + m_xDescriptionFI->set_label(sTemp); + m_xFilterPB->connect_clicked( LINK( this, SwAddressListDialog, FilterHdl_Impl )); + m_xLoadListPB->connect_clicked( LINK( this, SwAddressListDialog, LoadHdl_Impl )); + m_xRemovePB->connect_clicked( LINK(this, SwAddressListDialog, RemoveHdl_Impl )); + m_xCreateListPB->connect_clicked( LINK( this, SwAddressListDialog,CreateHdl_Impl )); + m_xEditPB->connect_clicked(LINK( this, SwAddressListDialog, EditHdl_Impl)); + m_xTablePB->connect_clicked(LINK( this, SwAddressListDialog, TableSelectHdl_Impl)); + + m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 52, + m_xListLB->get_height_rows(9)); + + std::vector<int> aWidths; + aWidths.push_back(m_xListLB->get_approximate_digit_width() * 26); + m_xListLB->set_column_fixed_widths(aWidths); + + m_xListLB->make_sorted(); + m_xOK->connect_clicked(LINK(this, SwAddressListDialog, OKHdl_Impl)); + + uno::Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + m_xDBContext = DatabaseContext::create(xContext); + + SwMailMergeConfigItem& rConfigItem = m_pAddressPage->GetWizard()->GetConfigItem(); + const SwDBData& rCurrentData = rConfigItem.GetCurrentDBData(); + + bool bEnableEdit = false; + bool bEnableOK = true; + bool bSelected = false; + m_xListLB->unselect_all(); + + SwDBConfig aDb; + const OUString sBibliography = aDb.GetBibliographySource().sDataSource; + const uno::Sequence< OUString> aNames = m_xDBContext->getElementNames(); + for(const OUString& rName : aNames) + { + if ( rName == sBibliography ) + continue; + m_xListLB->append(m_xIter.get()); + m_xListLB->set_text(*m_xIter, rName, 0); + m_aUserData.emplace_back(new AddressUserData_Impl); + AddressUserData_Impl* pUserData = m_aUserData.back().get(); + m_xListLB->set_id(*m_xIter, OUString::number(reinterpret_cast<sal_Int64>(pUserData))); + if (rName == rCurrentData.sDataSource) + { + m_xListLB->select(*m_xIter); + bSelected = true; + m_xListLB->set_text(*m_xIter, rCurrentData.sCommand, 1); + pUserData->nCommandType = rCurrentData.nCommandType; + pUserData->xSource = rConfigItem.GetSource(); + pUserData->xConnection = rConfigItem.GetConnection(); + pUserData->xColumnsSupplier = rConfigItem.GetColumnsSupplier(); + pUserData->xResultSet = rConfigItem.GetResultSet(); + pUserData->sFilter = rConfigItem.GetFilter(); + //is the data source editable (csv, Unicode, single table) + uno::Reference<beans::XPropertySet> xSourceProperties; + try + { + m_xDBContext->getByName(rName) >>= xSourceProperties; + pUserData->sURL = lcl_getFlatURL( xSourceProperties ); + bEnableEdit = !pUserData->sURL.isEmpty() && + SWUnoHelper::UCB_IsFile( pUserData->sURL ) && //#i97577# + !SWUnoHelper::UCB_IsReadOnlyFileName( pUserData->sURL ); + } + catch (const uno::Exception&) + { + bEnableOK = false; + } + m_aDBData = rCurrentData; + } + } + + bool bHasChildren = m_xListLB->n_children() > 0; + if (bHasChildren && !bSelected) + m_xListLB->select(0); // select the first entry if nothing else selected + m_xOK->set_sensitive(bHasChildren && bEnableOK); + m_xEditPB->set_sensitive(bEnableEdit); + m_xRemovePB->set_sensitive(m_xListLB->n_children() > 0); + m_xFilterPB->set_sensitive(m_xListLB->n_children() > 0); + m_xTablePB->set_sensitive(m_xListLB->n_children() > 0); + m_xListLB->connect_changed(LINK(this, SwAddressListDialog, ListBoxSelectHdl_Impl)); + TableSelectHdl(nullptr); +} + +SwAddressListDialog::~SwAddressListDialog() +{ +} + +IMPL_LINK_NOARG(SwAddressListDialog, FilterHdl_Impl, weld::Button&, void) +{ + int nSelect = m_xListLB->get_selected_index(); + uno::Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); + if (nSelect != -1) + { + const OUString sCommand = m_xListLB->get_text(nSelect, 1); + if (sCommand.isEmpty()) + return; + + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + if (pUserData->xConnection.is() ) + { + try + { + uno::Reference<lang::XMultiServiceFactory> xConnectFactory(pUserData->xConnection, UNO_QUERY_THROW); + uno::Reference<XSingleSelectQueryComposer> xComposer( + xConnectFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY_THROW); + + uno::Reference<XRowSet> xRowSet( + xMgr->createInstance("com.sun.star.sdb.RowSet"), UNO_QUERY); + uno::Reference<XPropertySet> xRowProperties(xRowSet, UNO_QUERY); + xRowProperties->setPropertyValue("DataSourceName", + makeAny(m_xListLB->get_text(nSelect, 0))); + xRowProperties->setPropertyValue("Command", makeAny(sCommand)); + xRowProperties->setPropertyValue("CommandType", makeAny(pUserData->nCommandType)); + xRowProperties->setPropertyValue("ActiveConnection", makeAny(pUserData->xConnection.getTyped())); + xRowSet->execute(); + + OUString sQuery; + xRowProperties->getPropertyValue("ActiveCommand")>>= sQuery; + xComposer->setQuery(sQuery); + if(!pUserData->sFilter.isEmpty()) + xComposer->setFilter(pUserData->sFilter); + + uno::Reference< XExecutableDialog> xDialog = sdb::FilterDialog::createWithQuery( comphelper::getComponentContext(xMgr), + xComposer,xRowSet, uno::Reference<awt::XWindow>() ); + + if ( RET_OK == xDialog->execute() ) + { + weld::WaitObject aWait(m_xDialog.get()); + pUserData->sFilter = xComposer->getFilter(); + } + ::comphelper::disposeComponent(xRowSet); + } + catch (const Exception&) + { + OSL_FAIL("exception caught in SwAddressListDialog::FilterHdl_Impl"); + } + } + } +} + +IMPL_LINK_NOARG(SwAddressListDialog, LoadHdl_Impl, weld::Button&, void) +{ + SwView* pView = m_pAddressPage->GetWizard()->GetSwView(); + + const OUString sNewSource = SwDBManager::LoadAndRegisterDataSource(m_xDialog.get(), pView ? pView->GetDocShell() : nullptr); + if(!sNewSource.isEmpty()) + { + m_xListLB->append(m_xIter.get()); + m_xListLB->set_text(*m_xIter, sNewSource, 0); + m_aUserData.emplace_back(new AddressUserData_Impl); + AddressUserData_Impl* pUserData = m_aUserData.back().get(); + m_xListLB->set_id(*m_xIter, OUString::number(reinterpret_cast<sal_Int64>(pUserData))); + m_xListLB->select(*m_xIter); + ListBoxSelectHdl_Impl(*m_xListLB); + m_xRemovePB->set_sensitive(true); + } +} + +IMPL_LINK_NOARG(SwAddressListDialog, RemoveHdl_Impl, weld::Button&, void) +{ + int nEntry = m_xListLB->get_selected_index(); + if (nEntry != -1) + { + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(getDialog(), + VclMessageType::Question, VclButtonsType::YesNo, SwResId(ST_DELETE_CONFIRM))); + if (xQuery->run() == RET_YES) + { // Remove data source connection + SwDBManager::RevokeDataSource(m_xListLB->get_selected_text()); + // Remove item from the list + m_xListLB->remove(nEntry); + // If this was the last item, disable the Remove & Edit buttons and enable Create + if (m_xListLB->n_children() < 1 ) + { + m_xRemovePB->set_sensitive(false); + m_xEditPB->set_sensitive(false); + m_xFilterPB->set_sensitive(false); + m_xCreateListPB->set_sensitive(true); + } + } + } + + +} + +IMPL_LINK_NOARG(SwAddressListDialog, CreateHdl_Impl, weld::Button&, void) +{ + SwCreateAddressListDialog aDlg(m_xDialog.get(), /*sInputURL*/OUString(), m_pAddressPage->GetWizard()->GetConfigItem()); + if (RET_OK == aDlg.run()) + { + //register the URL a new datasource + const OUString sURL = aDlg.GetURL(); + try + { + uno::Reference<XInterface> xNewInstance = m_xDBContext->createInstance(); + INetURLObject aURL( sURL ); + const OUString sNewName = aURL.getBase(); + //find a unique name if sNewName already exists + OUString sFind(sNewName); + sal_Int32 nIndex = 0; + while(m_xDBContext->hasByName(sFind)) + { + sFind = sNewName + OUString::number(++nIndex); + } + uno::Reference<XPropertySet> xDataProperties(xNewInstance, UNO_QUERY); + + //only the 'path' has to be added + INetURLObject aTempURL(aURL); + aTempURL.removeSegment(); + aTempURL.removeFinalSlash(); + const OUString sDBURL("sdbc:flat:" + aTempURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + xDataProperties->setPropertyValue("URL", Any(sDBURL)); + //set the filter to the file name without extension + uno::Sequence<OUString> aFilters { sNewName }; + xDataProperties->setPropertyValue("TableFilter", Any(aFilters)); + + uno::Sequence<PropertyValue> aInfo(4); + PropertyValue* pInfo = aInfo.getArray(); + pInfo[0].Name = "FieldDelimiter"; + pInfo[0].Value <<= OUString('\t'); + pInfo[1].Name = "StringDelimiter"; + pInfo[1].Value <<= OUString('"'); + pInfo[2].Name = "Extension"; + pInfo[2].Value <<= aURL.getExtension();//"csv"; + pInfo[3].Name = "CharSet"; + pInfo[3].Value <<= OUString("UTF-8"); + xDataProperties->setPropertyValue("Info", Any(aInfo)); + + uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, UNO_QUERY_THROW); + uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), UNO_QUERY_THROW); + OUString const sExt(".odb"); + OUString sTmpName; + { + OUString sHomePath(SvtPathOptions().GetWorkPath()); + utl::TempFile aTempFile(sFind, true, &sExt, &sHomePath); + aTempFile.EnableKillingFile(); + sTmpName = aTempFile.GetURL(); + } + xStore->storeAsURL(sTmpName, Sequence< PropertyValue >()); + + m_xDBContext->registerObject( sFind, xNewInstance ); + //now insert the new source into the ListBox + m_xListLB->append(m_xIter.get()); + m_xListLB->set_text(*m_xIter, sFind, 0); + m_xListLB->set_text(*m_xIter, aFilters[0], 1); + m_aUserData.emplace_back(new AddressUserData_Impl); + AddressUserData_Impl* pUserData = m_aUserData.back().get(); + m_xListLB->set_id(*m_xIter, OUString::number(reinterpret_cast<sal_Int64>(pUserData))); + m_xListLB->select(*m_xIter); + ListBoxSelectHdl_Impl(*m_xListLB); + m_xCreateListPB->set_sensitive(false); + m_xRemovePB->set_sensitive(true); + } + catch (const Exception&) + { + } + } +} + +IMPL_LINK_NOARG(SwAddressListDialog, EditHdl_Impl, weld::Button&, void) +{ + int nEntry = m_xListLB->get_selected_index(); + AddressUserData_Impl* pUserData = nEntry != -1 ? reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nEntry).toInt64()) : nullptr; + if (pUserData && !pUserData->sURL.isEmpty()) + { + if(pUserData->xResultSet.is()) + { + SwMailMergeConfigItem& rConfigItem = m_pAddressPage->GetWizard()->GetConfigItem(); + if(rConfigItem.GetResultSet() != pUserData->xResultSet) + ::comphelper::disposeComponent( pUserData->xResultSet ); + pUserData->xResultSet = nullptr; + + rConfigItem.DisposeResultSet(); + } + pUserData->xSource.clear(); + pUserData->xColumnsSupplier.clear(); + pUserData->xConnection.clear(); + // will automatically close if it was the las reference + SwCreateAddressListDialog aDlg(m_xDialog.get(), pUserData->sURL, + m_pAddressPage->GetWizard()->GetConfigItem()); + aDlg.run(); + } +}; + +IMPL_LINK_NOARG(SwAddressListDialog, ListBoxSelectHdl_Impl, weld::TreeView&, void) +{ + int nSelect = m_xListLB->get_selected_index(); + Application::PostUserEvent( LINK( this, SwAddressListDialog, + StaticListBoxSelectHdl_Impl ), reinterpret_cast<void*>(nSelect) ); +} + +IMPL_LINK(SwAddressListDialog, StaticListBoxSelectHdl_Impl, void*, p, void) +{ + int nSelect = reinterpret_cast<sal_IntPtr>(p); + //prevent nested calls of the select handler + if (m_bInSelectHdl) + return; + weld::WaitObject aWait(m_xDialog.get()); + m_bInSelectHdl = true; + AddressUserData_Impl* pUserData = nullptr; + if (nSelect != -1) + { + const OUString sTable(m_xListLB->get_text(nSelect, 1)); + if (sTable.isEmpty()) + { + m_xListLB->set_text(nSelect, m_sConnecting, 1); + } + + pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + if(pUserData->nTableAndQueryCount > 1 || pUserData->nTableAndQueryCount == -1) + { + DetectTablesAndQueries(nSelect, sTable.isEmpty()); + } + else + { + //otherwise set the selected db-data + m_aDBData.sDataSource = m_xListLB->get_text(nSelect, 0); + m_aDBData.sCommand = m_xListLB->get_text(nSelect, 1); + m_aDBData.nCommandType = pUserData->nCommandType; + m_xOK->set_sensitive(true); + } + if (m_xListLB->get_text(nSelect, 1) == m_sConnecting) + m_xListLB->set_text(nSelect, OUString(), 1); + } + m_xEditPB->set_sensitive(pUserData && !pUserData->sURL.isEmpty() && + SWUnoHelper::UCB_IsFile( pUserData->sURL ) && //#i97577# + !SWUnoHelper::UCB_IsReadOnlyFileName( pUserData->sURL ) ); + m_bInSelectHdl = false; +} + +// detect the number of tables for a data source +// if only one is available then set it at the entry +void SwAddressListDialog::DetectTablesAndQueries( + int nSelect, + bool bWidthDialog) +{ + try + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + uno::Reference<XCompletedConnection> xComplConnection; + if(!pUserData->xConnection.is()) + { + m_aDBData.sDataSource = m_xListLB->get_text(nSelect, 0); + m_xDBContext->getByName(m_aDBData.sDataSource) >>= xComplConnection; + pUserData->xSource.set(xComplConnection, UNO_QUERY); + + uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference< XInteractionHandler > xHandler = InteractionHandler::createWithParent(xContext, nullptr); + pUserData->xConnection = SharedConnection( xComplConnection->connectWithCompletion( xHandler ) ); + } + if(pUserData->xConnection.is()) + { + sal_Int32 nTables = 0; + uno::Sequence<OUString> aTables; + uno::Sequence<OUString> aQueries; + uno::Reference<XTablesSupplier> xTSupplier(pUserData->xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + uno::Reference<XNameAccess> xTables = xTSupplier->getTables(); + aTables = xTables->getElementNames(); + nTables += aTables.getLength(); + } + uno::Reference<XQueriesSupplier> xQSupplier(pUserData->xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + uno::Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + aQueries = xQueries->getElementNames(); + nTables += aQueries.getLength(); + } + pUserData->nTableAndQueryCount = nTables; + if(nTables > 1 && bWidthDialog) + { + //now call the table select dialog - if more than one table exists + SwSelectDBTableDialog aDlg(m_xDialog.get(), pUserData->xConnection); + const OUString sTable = m_xListLB->get_text(nSelect, 1); + if(!sTable.isEmpty()) + aDlg.SetSelectedTable(sTable, pUserData->nCommandType == CommandType::TABLE); + if(RET_OK == aDlg.run()) + { + bool bIsTable; + m_aDBData.sCommand = aDlg.GetSelectedTable(bIsTable); + m_aDBData.nCommandType = bIsTable ? CommandType::TABLE : CommandType::QUERY; + pUserData->nCommandType = m_aDBData.nCommandType; + } + } + else if(nTables == 1) + { + if(aTables.hasElements()) + { + m_aDBData.sCommand = aTables[0]; + m_aDBData.nCommandType = CommandType::TABLE; + } + else + { + m_aDBData.sCommand = aQueries[0]; + m_aDBData.nCommandType = CommandType::QUERY; + } + } + } + if ( !m_aDBData.sCommand.isEmpty() ) + { + uno::Reference<beans::XPropertySet> xSourceProperties; + m_xDBContext->getByName(m_aDBData.sDataSource) >>= xSourceProperties; + pUserData->sURL = lcl_getFlatURL( xSourceProperties ); + + pUserData->xColumnsSupplier = SwDBManager::GetColumnSupplier(pUserData->xConnection, + m_aDBData.sCommand, + m_aDBData.nCommandType == CommandType::TABLE ? + SwDBSelect::TABLE : SwDBSelect::QUERY ); + //#i97577# + if( pUserData->xColumnsSupplier.is() ) + m_xListLB->set_text(nSelect, m_aDBData.sCommand, 1); + else + m_xListLB->set_text(nSelect, OUString(), 1); + } + const OUString sCommand = m_xListLB->get_text(nSelect, 1); + m_xOK->set_sensitive(!sCommand.isEmpty()); + m_xFilterPB->set_sensitive( pUserData->xConnection.is() && !sCommand.isEmpty() ); + m_xTablePB->set_sensitive( pUserData->nTableAndQueryCount > 1 ); + } + catch (const Exception&) + { + OSL_FAIL("exception caught in SwAddressListDialog::DetectTablesAndQueries"); + m_xOK->set_sensitive(false); + } +} + +IMPL_LINK(SwAddressListDialog, TableSelectHdl_Impl, weld::Button&, rButton, void) +{ + TableSelectHdl(&rButton); +} + +void SwAddressListDialog::TableSelectHdl(const weld::Button* pButton) +{ + weld::WaitObject aWait(m_xDialog.get()); + + int nSelect = m_xListLB->get_selected_index(); + if (nSelect != -1) + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + //only call the table select dialog if tables have not been searched for or there + //are more than 1 + const OUString sTable = m_xListLB->get_text(nSelect, 1); + if( pUserData->nTableAndQueryCount > 1 || pUserData->nTableAndQueryCount == -1) + { + DetectTablesAndQueries(nSelect, (pButton != nullptr) || sTable.isEmpty()); + } + } +} + +IMPL_LINK_NOARG(SwAddressListDialog, OKHdl_Impl, weld::Button&, void) +{ + m_xDialog->response(RET_OK); +} + +uno::Reference< XDataSource> SwAddressListDialog::GetSource() const +{ + uno::Reference< XDataSource> xRet; + int nSelect = m_xListLB->get_selected_index(); + if (nSelect != -1) + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + xRet = pUserData->xSource; + } + return xRet; + +} + +SharedConnection SwAddressListDialog::GetConnection() const +{ + SharedConnection xRet; + int nSelect = m_xListLB->get_selected_index(); + if (nSelect != -1) + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + xRet = pUserData->xConnection; + } + return xRet; +} + +uno::Reference< XColumnsSupplier> SwAddressListDialog::GetColumnsSupplier() const +{ + uno::Reference< XColumnsSupplier> xRet; + int nSelect = m_xListLB->get_selected_index(); + if (nSelect != -1) + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + xRet = pUserData->xColumnsSupplier; + } + return xRet; +} + +OUString SwAddressListDialog::GetFilter() const +{ + int nSelect = m_xListLB->get_selected_index(); + if (nSelect != -1) + { + AddressUserData_Impl* pUserData = reinterpret_cast<AddressUserData_Impl*>(m_xListLB->get_id(nSelect).toInt64()); + return pUserData->sFilter; + } + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/addresslistdialog.hxx b/sw/source/ui/dbui/addresslistdialog.hxx new file mode 100644 index 000000000..dc0873af0 --- /dev/null +++ b/sw/source/ui/dbui/addresslistdialog.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_DBUI_ADDRESSLISTDIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_ADDRESSLISTDIALOG_HXX + +#include <sfx2/basedlgs.hxx> +#include <swdbdata.hxx> +#include <sharedconnection.hxx> + +namespace com::sun::star{ + namespace container{ + class XNameAccess; + } + namespace sdb{ + class XDatabaseContext; + } + namespace sdbc{ + class XDataSource; + } + namespace sdbcx{ + class XColumnsSupplier; + } +} +class SwMailMergeAddressBlockPage; + +struct AddressUserData_Impl; + +class SwAddressListDialog : public SfxDialogController +{ + OUString m_sConnecting; + + bool m_bInSelectHdl; + + SwMailMergeAddressBlockPage* m_pAddressPage; + + css::uno::Reference< css::sdb::XDatabaseContext> m_xDBContext; + + SwDBData m_aDBData; + + std::vector<std::unique_ptr<AddressUserData_Impl>> m_aUserData; + + std::unique_ptr<weld::Label> m_xDescriptionFI; + std::unique_ptr<weld::Label> m_xConnecting; + std::unique_ptr<weld::TreeView> m_xListLB; + std::unique_ptr<weld::Button> m_xLoadListPB; + std::unique_ptr<weld::Button> m_xRemovePB; + std::unique_ptr<weld::Button> m_xCreateListPB; + std::unique_ptr<weld::Button> m_xFilterPB; + std::unique_ptr<weld::Button> m_xEditPB; + std::unique_ptr<weld::Button> m_xTablePB; + std::unique_ptr<weld::Button> m_xOK; + std::unique_ptr<weld::TreeIter> m_xIter; + + void DetectTablesAndQueries(int Select, bool bWidthDialog); + + DECL_LINK(FilterHdl_Impl, weld::Button&, void); + DECL_LINK(LoadHdl_Impl, weld::Button&, void); + DECL_LINK(CreateHdl_Impl, weld::Button&, void); + DECL_LINK(RemoveHdl_Impl, weld::Button&, void); + DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void); + DECL_LINK(EditHdl_Impl, weld::Button&, void); + DECL_LINK(TableSelectHdl_Impl, weld::Button&, void); + void TableSelectHdl(const weld::Button* pButton); + DECL_LINK(OKHdl_Impl, weld::Button&, void); + + DECL_LINK(StaticListBoxSelectHdl_Impl, void*, void); + +public: + SwAddressListDialog(SwMailMergeAddressBlockPage* pParent); + virtual ~SwAddressListDialog() override; + + css::uno::Reference< css::sdbc::XDataSource> + GetSource() const; + + SharedConnection GetConnection() const; + + css::uno::Reference< css::sdbcx::XColumnsSupplier> + GetColumnsSupplier() const; + + const SwDBData& GetDBData() const {return m_aDBData;} + OUString GetFilter() const; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/createaddresslistdialog.cxx b/sw/source/ui/dbui/createaddresslistdialog.cxx new file mode 100644 index 000000000..4f0a49acd --- /dev/null +++ b/sw/source/ui/dbui/createaddresslistdialog.cxx @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cstddef> + +#include <osl/diagnose.h> +#include <swtypes.hxx> +#include "createaddresslistdialog.hxx" +#include "customizeaddresslistdialog.hxx" +#include <mmconfigitem.hxx> +#include <vcl/svapp.hxx> +#include <unotools/pathoptions.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/docfile.hxx> +#include <rtl/textenc.h> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <tools/urlobj.hxx> +#include <strings.hrc> +#include <map> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::ui::dialogs; + +namespace { + +struct SwAddressFragment +{ + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Label> m_xLabel; + std::unique_ptr<weld::Entry> m_xEntry; + weld::Container* m_pGrid; + + SwAddressFragment(weld::Container* pGrid, int nLine) + : m_xBuilder(Application::CreateBuilder(pGrid, "modules/swriter/ui/addressfragment.ui")) + , m_xLabel(m_xBuilder->weld_label("label")) + , m_xEntry(m_xBuilder->weld_entry("entry")) + , m_pGrid(pGrid) + { + m_xLabel->set_grid_left_attach(0); + m_xLabel->set_grid_top_attach(nLine); + + m_xEntry->set_grid_left_attach(1); + m_xEntry->set_grid_top_attach(nLine); + } + + virtual ~SwAddressFragment() + { + m_pGrid->move(m_xEntry.get(), nullptr); + m_pGrid->move(m_xLabel.get(), nullptr); + } +}; + +} + +class SwAddressControl_Impl +{ + std::map<weld::Entry*, sal_Int32> m_aEditLines; + + SwCSVData* m_pData; + sal_uInt32 m_nCurrentDataSet; + + bool m_bNoDataSet; + + std::unique_ptr<weld::ScrolledWindow> m_xScrollBar; + std::unique_ptr<weld::Container> m_xWindow; + std::vector<std::unique_ptr<SwAddressFragment>> m_aLines; + + DECL_LINK(GotFocusHdl_Impl, weld::Widget&, void); + DECL_LINK(EditModifyHdl_Impl, weld::Entry&, void); + + void MakeVisible(const tools::Rectangle& aRect); + +public: + SwAddressControl_Impl(weld::Builder& rBuilder); + + void SetData(SwCSVData& rDBData); + + void SetCurrentDataSet(sal_uInt32 nSet); + void CurrentDataSetInvalidated() { m_nCurrentDataSet = std::numeric_limits<sal_uInt32>::max(); } + sal_uInt32 GetCurrentDataSet() const { return m_nCurrentDataSet; } + void SetCursorTo(std::size_t nElement); +}; + +SwAddressControl_Impl::SwAddressControl_Impl(weld::Builder& rBuilder) + : m_pData(nullptr) + , m_nCurrentDataSet(0) + , m_bNoDataSet(true) + , m_xScrollBar(rBuilder.weld_scrolled_window("scrollwin")) + , m_xWindow(rBuilder.weld_container("CONTAINER")) +{ +} + +void SwAddressControl_Impl::SetData(SwCSVData& rDBData) +{ + m_pData = &rDBData; + //when the address data is updated then remove the controls and build again + if (!m_aLines.empty()) + { + m_aLines.clear(); + m_bNoDataSet = true; + } + + Link<weld::Widget&,void> aFocusLink = LINK(this, SwAddressControl_Impl, GotFocusHdl_Impl); + Link<weld::Entry&,void> aEditModifyLink = LINK(this, SwAddressControl_Impl, EditModifyHdl_Impl); + sal_Int32 nLines = 0; + for (const auto& rHeader : m_pData->aDBColumnHeaders) + { + m_aLines.emplace_back(new SwAddressFragment(m_xWindow.get(), nLines)); + + // when we have one line, measure it to get the line height to use as + // the basis for overall size request + if (nLines == 0) + { + auto nLineHeight = m_xWindow->get_preferred_size().Height(); + m_xScrollBar->set_size_request(m_xScrollBar->get_approximate_digit_width() * 65, + nLineHeight * 10); + } + + weld::Label* pNewFT = m_aLines.back()->m_xLabel.get(); + weld::Entry* pNewED = m_aLines.back()->m_xEntry.get(); + //set nLines a position identifier - used in the ModifyHdl + m_aEditLines[pNewED] = nLines; + pNewED->connect_focus_in(aFocusLink); + pNewED->connect_changed(aEditModifyLink); + + pNewFT->set_label(rHeader); + + nLines++; + } +} + +void SwAddressControl_Impl::SetCurrentDataSet(sal_uInt32 nSet) +{ + if(m_bNoDataSet || m_nCurrentDataSet != nSet) + { + m_bNoDataSet = false; + m_nCurrentDataSet = nSet; + OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index"); + if(m_pData->aDBData.size() > m_nCurrentDataSet) + { + sal_uInt32 nIndex = 0; + for(auto& rLine : m_aLines) + { + OSL_ENSURE(nIndex < m_pData->aDBData[m_nCurrentDataSet].size(), + "number of columns doesn't match number of Edits"); + rLine->m_xEntry->set_text(m_pData->aDBData[m_nCurrentDataSet][nIndex]); + ++nIndex; + } + } + } +} + +IMPL_LINK(SwAddressControl_Impl, GotFocusHdl_Impl, weld::Widget&, rEdit, void) +{ + int x, y, width, height; + rEdit.get_extents_relative_to(*m_xWindow, x, y, width, height); + // the container has a border of 3 in the .ui + tools::Rectangle aRect(Point(x - 3, y - 3), Size(width + 6, height + 6)); + MakeVisible(aRect); +} + +void SwAddressControl_Impl::MakeVisible(const tools::Rectangle & rRect) +{ + //determine range of visible positions + auto nMinVisiblePos = m_xScrollBar->vadjustment_get_value(); + auto nMaxVisiblePos = nMinVisiblePos + m_xScrollBar->vadjustment_get_page_size(); + if (rRect.Top() < nMinVisiblePos || rRect.Bottom() > nMaxVisiblePos) + m_xScrollBar->vadjustment_set_value(rRect.Top()); +} + +// copy data changes into database +IMPL_LINK(SwAddressControl_Impl, EditModifyHdl_Impl, weld::Entry&, rEdit, void) +{ + //get the data element number of the current set + sal_Int32 nIndex = m_aEditLines[&rEdit]; + //get the index of the set + OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index" ); + if (m_pData->aDBData.size() > m_nCurrentDataSet) + { + m_pData->aDBData[m_nCurrentDataSet][nIndex] = rEdit.get_text(); + } +} + +void SwAddressControl_Impl::SetCursorTo(std::size_t nElement) +{ + if (nElement < m_aLines.size()) + { + weld::Entry* pEdit = m_aLines[nElement]->m_xEntry.get(); + pEdit->grab_focus(); + GotFocusHdl_Impl(*pEdit); + } + +} + +SwCreateAddressListDialog::SwCreateAddressListDialog( + weld::Window* pParent, const OUString& rURL, SwMailMergeConfigItem const & rConfig) + : SfxDialogController(pParent, "modules/swriter/ui/createaddresslist.ui", "CreateAddressList") + , m_sAddressListFilterName(SwResId(ST_FILTERNAME)) + , m_sURL(rURL) + , m_pCSVData(new SwCSVData) + , m_xAddressControl(new SwAddressControl_Impl(*m_xBuilder)) + , m_xNewPB(m_xBuilder->weld_button("NEW")) + , m_xDeletePB(m_xBuilder->weld_button("DELETE")) + , m_xFindPB(m_xBuilder->weld_button("FIND")) + , m_xCustomizePB(m_xBuilder->weld_button("CUSTOMIZE")) + , m_xStartPB(m_xBuilder->weld_button("START")) + , m_xPrevPB(m_xBuilder->weld_button("PREV")) + , m_xSetNoED(m_xBuilder->weld_entry("SETNOED")) + , m_xSetNoNF(m_xBuilder->weld_spin_button("SETNOSB")) + , m_xNextPB(m_xBuilder->weld_button("NEXT")) + , m_xEndPB(m_xBuilder->weld_button("END")) + , m_xOK(m_xBuilder->weld_button("ok")) +{ + m_xSetNoNF->set_min(1); + + m_xNewPB->connect_clicked(LINK(this, SwCreateAddressListDialog, NewHdl_Impl)); + m_xDeletePB->connect_clicked(LINK(this, SwCreateAddressListDialog, DeleteHdl_Impl)); + m_xFindPB->connect_clicked(LINK(this, SwCreateAddressListDialog, FindHdl_Impl)); + m_xCustomizePB->connect_clicked(LINK(this, SwCreateAddressListDialog, CustomizeHdl_Impl)); + m_xOK->connect_clicked(LINK(this, SwCreateAddressListDialog, OkHdl_Impl)); + + Link<weld::Button&,void> aLk = LINK(this, SwCreateAddressListDialog, DBCursorHdl_Impl); + m_xStartPB->connect_clicked(aLk); + m_xPrevPB->connect_clicked(aLk); + m_xSetNoED->connect_changed(LINK(this, SwCreateAddressListDialog, DBNumCursorHdl_Impl)); + m_xSetNoED->connect_focus_out(LINK(this, SwCreateAddressListDialog, RefreshNum_Impl)); + m_xNextPB->connect_clicked(aLk); + m_xEndPB->connect_clicked(aLk); + + if (!m_sURL.isEmpty()) + { + //file exists, has to be loaded here + SfxMedium aMedium( m_sURL, StreamMode::READ ); + SvStream* pStream = aMedium.GetInStream(); + if(pStream) + { + pStream->SetLineDelimiter( LINEEND_LF ); + pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); + + OUString sLine; + bool bRead = pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 ); + + if(bRead && !sLine.isEmpty()) + { + sal_Int32 nIndex = 0; + do + { + const OUString sHeader = sLine.getToken( 0, '\t', nIndex ); + OSL_ENSURE(sHeader.getLength() > 2 && + sHeader.startsWith("\"") && sHeader.endsWith("\""), + "Wrong format of header"); + if(sHeader.getLength() > 2) + { + m_pCSVData->aDBColumnHeaders.push_back( sHeader.copy(1, sHeader.getLength() -2)); + } + } + while (nIndex > 0); + } + while(pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 )) + { + std::vector<OUString> aNewData; + //analyze data line + sal_Int32 nIndex = { sLine.isEmpty() ? -1 : 0 }; + while (nIndex >= 0) + { + const OUString sData = sLine.getToken( 0, '\t', nIndex ); + OSL_ENSURE( sData.startsWith("\"") && sData.endsWith("\""), + "Wrong format of line"); + if(sData.getLength() >= 2) + aNewData.push_back(sData.copy(1, sData.getLength() - 2)); + else + aNewData.push_back(sData); + } + m_pCSVData->aDBData.push_back( aNewData ); + } + } + } + else + { + //database has to be created + const std::vector<std::pair<OUString, int>>& rAddressHeader = rConfig.GetDefaultAddressHeaders(); + const sal_uInt32 nCount = rAddressHeader.size(); + for(sal_uInt32 nHeader = 0; nHeader < nCount; ++nHeader) + m_pCSVData->aDBColumnHeaders.push_back(rAddressHeader[nHeader].first); + std::vector<OUString> aNewData; + aNewData.insert(aNewData.begin(), nCount, OUString()); + m_pCSVData->aDBData.push_back(aNewData); + } + //now fill the address control + m_xAddressControl->SetData(*m_pCSVData); + m_xAddressControl->SetCurrentDataSet(0); + m_xSetNoNF->set_max(m_pCSVData->aDBData.size()); + + m_xSetNoNF->set_value(1); + RefreshNum_Impl(*m_xSetNoED); + + UpdateButtons(); +} + +SwCreateAddressListDialog::~SwCreateAddressListDialog() +{ +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, NewHdl_Impl, weld::Button&, void) +{ + sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet(); + std::vector<OUString> aNewData; + aNewData.insert(aNewData.begin(), m_pCSVData->aDBColumnHeaders.size(), OUString()); + m_pCSVData->aDBData.insert(m_pCSVData->aDBData.begin() + ++nCurrent, aNewData); + m_xSetNoNF->set_max(m_pCSVData->aDBData.size()); + //the NumericField start at 1 + m_xSetNoNF->set_value(nCurrent + 1); + RefreshNum_Impl(*m_xSetNoED); + //the address control starts at 0 + m_xAddressControl->SetCurrentDataSet(nCurrent); + UpdateButtons(); +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, DeleteHdl_Impl, weld::Button&, void) +{ + sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet(); + if (m_pCSVData->aDBData.size() > 1) + { + m_pCSVData->aDBData.erase(m_pCSVData->aDBData.begin() + nCurrent); + if (nCurrent) + --nCurrent; + } + else + { + // if only one set is available then clear the data + m_pCSVData->aDBData[0].assign(m_pCSVData->aDBData[0].size(), OUString()); + m_xDeletePB->set_sensitive(false); + } + m_xAddressControl->CurrentDataSetInvalidated(); + m_xAddressControl->SetCurrentDataSet(nCurrent); + m_xSetNoNF->set_max(m_pCSVData->aDBData.size()); + UpdateButtons(); +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, FindHdl_Impl, weld::Button&, void) +{ + if (!m_xFindDlg) + { + m_xFindDlg.reset(new SwFindEntryDialog(this)); + weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox(); + for(const auto& rHeader : m_pCSVData->aDBColumnHeaders) + rColumnBox.append_text(rHeader); + rColumnBox.set_active(0); + m_xFindDlg->show(); + } + else + m_xFindDlg->set_visible(!m_xFindDlg->get_visible()); +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, CustomizeHdl_Impl, weld::Button&, void) +{ + SwCustomizeAddressListDialog aDlg(m_xDialog.get(), *m_pCSVData); + if (aDlg.run() == RET_OK) + { + m_pCSVData = aDlg.ReleaseNewData(); + m_xAddressControl->SetData(*m_pCSVData); + m_xAddressControl->SetCurrentDataSet(m_xAddressControl->GetCurrentDataSet()); + } + + //update find dialog + if (m_xFindDlg) + { + weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox(); + rColumnBox.clear(); + for(const auto& rHeader : m_pCSVData->aDBColumnHeaders) + rColumnBox.append_text(rHeader); + } +} + +namespace +{ + +void lcl_WriteValues(const std::vector<OUString> *pFields, SvStream* pStream) +{ + OUStringBuffer sLine; + const std::vector< OUString >::const_iterator aBegin = pFields->begin(); + const std::vector< OUString >::const_iterator aEnd = pFields->end(); + for(std::vector< OUString >::const_iterator aIter = aBegin; aIter != aEnd; ++aIter) + { + if (aIter==aBegin) + { + sLine.append("\"").append(*aIter).append("\""); + } + else + { + sLine.append("\t\"").append(*aIter).append("\""); + } + } + pStream->WriteByteStringLine( sLine.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); +} + +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, OkHdl_Impl, weld::Button&, void) +{ + if(m_sURL.isEmpty()) + { + sfx2::FileDialogHelper aDlgHelper(TemplateDescription::FILESAVE_SIMPLE, + FileDialogFlags::NONE, m_xDialog.get()); + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + const OUString sPath( SvtPathOptions().SubstituteVariable("$(userurl)/database") ); + aDlgHelper.SetDisplayDirectory( sPath ); + xFP->appendFilter( m_sAddressListFilterName, "*.csv" ); + xFP->setCurrentFilter( m_sAddressListFilterName ) ; + + if( ERRCODE_NONE == aDlgHelper.Execute() ) + { + m_sURL = xFP->getSelectedFiles().getConstArray()[0]; + INetURLObject aResult( m_sURL ); + aResult.setExtension("csv"); + m_sURL = aResult.GetMainURL(INetURLObject::DecodeMechanism::NONE); + } + } + if(!m_sURL.isEmpty()) + { + SfxMedium aMedium( m_sURL, StreamMode::READWRITE|StreamMode::TRUNC ); + SvStream* pStream = aMedium.GetOutStream(); + pStream->SetLineDelimiter( LINEEND_LF ); + pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); + + lcl_WriteValues(&(m_pCSVData->aDBColumnHeaders), pStream); + + for(const auto& rData : m_pCSVData->aDBData) + { + lcl_WriteValues(&rData, pStream); + } + aMedium.Commit(); + m_xDialog->response(RET_OK); + } +} + +IMPL_LINK(SwCreateAddressListDialog, DBCursorHdl_Impl, weld::Button&, rButton, void) +{ + int nValue = m_xSetNoNF->get_value(); + + if (&rButton == m_xStartPB.get()) + nValue = 1; + else if (&rButton == m_xPrevPB.get()) + { + if (nValue > 1) + --nValue; + } + else if (&rButton == m_xNextPB.get()) + { + if (nValue < m_xSetNoNF->get_max()) + ++nValue; + } + else //m_aEndPB + nValue = m_xSetNoNF->get_max(); + if (nValue != m_xSetNoNF->get_value()) + { + m_xSetNoNF->set_value(nValue); + RefreshNum_Impl(*m_xSetNoED); + DBNumCursor(); + } +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, DBNumCursorHdl_Impl, weld::Entry&, void) +{ + m_xSetNoNF->set_text(m_xSetNoED->get_text()); + DBNumCursor(); +} + +IMPL_LINK_NOARG(SwCreateAddressListDialog, RefreshNum_Impl, weld::Widget&, void) +{ + m_xSetNoED->set_text(OUString::number(m_xSetNoNF->get_value())); +} + +void SwCreateAddressListDialog::DBNumCursor() +{ + m_xAddressControl->SetCurrentDataSet(m_xSetNoNF->get_value() - 1); + UpdateButtons(); +} + +void SwCreateAddressListDialog::UpdateButtons() +{ + sal_uInt32 nCurrent = static_cast< sal_uInt32 >(m_xSetNoNF->get_value() ); + sal_uInt32 nSize = static_cast<sal_uInt32>(m_pCSVData->aDBData.size()); + m_xStartPB->set_sensitive(nCurrent != 1); + m_xPrevPB->set_sensitive(nCurrent != 1); + m_xNextPB->set_sensitive(nCurrent != nSize); + m_xEndPB->set_sensitive(nCurrent != nSize); + m_xDeletePB->set_sensitive(nSize > 0); +} + +void SwCreateAddressListDialog::Find(const OUString& rSearch, sal_Int32 nColumn) +{ + const OUString sSearch = rSearch.toAsciiLowerCase(); + sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet(); + //search forward + bool bFound = false; + sal_uInt32 nStart = nCurrent + 1; + sal_uInt32 nEnd = m_pCSVData->aDBData.size(); + std::size_t nElement = 0; + sal_uInt32 nPos = 0; + for(short nTemp = 0; nTemp < 2 && !bFound; nTemp++) + { + for(nPos = nStart; nPos < nEnd; ++nPos) + { + std::vector< OUString> const & aData = m_pCSVData->aDBData[nPos]; + if(nColumn >=0) + bFound = -1 != aData[static_cast<sal_uInt32>(nColumn)].toAsciiLowerCase().indexOf(sSearch); + else + { + for( nElement = 0; nElement < aData.size(); ++nElement) + { + bFound = -1 != aData[nElement].toAsciiLowerCase().indexOf(sSearch); + if(bFound) + { + nColumn = nElement; //TODO: std::size_t -> sal_Int32! + break; + } + } + } + if(bFound) + break; + } + nStart = 0; + nEnd = nCurrent + 1; + } + if(bFound) + { + m_xAddressControl->SetCurrentDataSet(nPos); + m_xSetNoNF->set_value( nPos + 1 ); + RefreshNum_Impl(*m_xSetNoED); + UpdateButtons(); + m_xAddressControl->SetCursorTo(nElement); + } +} + +SwFindEntryDialog::SwFindEntryDialog(SwCreateAddressListDialog* pParent) + : GenericDialogController(pParent->getDialog(), "modules/swriter/ui/findentrydialog.ui", "FindEntryDialog") + , m_pParent(pParent) + , m_xFindED(m_xBuilder->weld_entry("entry")) + , m_xFindOnlyCB(m_xBuilder->weld_check_button("findin")) + , m_xFindOnlyLB(m_xBuilder->weld_combo_box("area")) + , m_xFindPB(m_xBuilder->weld_button("find")) + , m_xCancel(m_xBuilder->weld_button("cancel")) +{ + m_xFindPB->connect_clicked(LINK(this, SwFindEntryDialog, FindHdl_Impl)); + m_xFindED->connect_changed(LINK(this, SwFindEntryDialog, FindEnableHdl_Impl)); + m_xCancel->connect_clicked(LINK(this, SwFindEntryDialog, CloseHdl_Impl)); +} + +SwFindEntryDialog::~SwFindEntryDialog() +{ +} + +IMPL_LINK_NOARG(SwFindEntryDialog, FindHdl_Impl, weld::Button&, void) +{ + sal_Int32 nColumn = -1; + if (m_xFindOnlyCB->get_active()) + nColumn = m_xFindOnlyLB->get_active(); + m_pParent->Find(m_xFindED->get_text(), nColumn); +} + +IMPL_LINK_NOARG(SwFindEntryDialog, FindEnableHdl_Impl, weld::Entry&, void) +{ + m_xFindPB->set_sensitive(!m_xFindED->get_text().isEmpty()); +} + +IMPL_LINK_NOARG(SwFindEntryDialog, CloseHdl_Impl, weld::Button&, void) +{ + m_xDialog->hide(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/createaddresslistdialog.hxx b/sw/source/ui/dbui/createaddresslistdialog.hxx new file mode 100644 index 000000000..a5fd9bd0d --- /dev/null +++ b/sw/source/ui/dbui/createaddresslistdialog.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_DBUI_CREATEADDRESSLISTDIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_CREATEADDRESSLISTDIALOG_HXX + +#include <sfx2/basedlgs.hxx> +#include <vcl/weld.hxx> +#include <vector> + +class SwAddressControl_Impl; +class SwMailMergeConfigItem; + +// container of the created database +struct SwCSVData +{ + std::vector< OUString > aDBColumnHeaders; + std::vector< std::vector< OUString> > aDBData; +}; + +class SwFindEntryDialog; +class SwCreateAddressListDialog : public SfxDialogController +{ + OUString m_sAddressListFilterName; + OUString m_sURL; + + std::unique_ptr<SwCSVData> m_pCSVData; + std::unique_ptr<SwFindEntryDialog> m_xFindDlg; + + std::unique_ptr<SwAddressControl_Impl> m_xAddressControl; + std::unique_ptr<weld::Button> m_xNewPB; + std::unique_ptr<weld::Button> m_xDeletePB; + std::unique_ptr<weld::Button> m_xFindPB; + std::unique_ptr<weld::Button> m_xCustomizePB; + std::unique_ptr<weld::Button> m_xStartPB; + std::unique_ptr<weld::Button> m_xPrevPB; + std::unique_ptr<weld::Entry> m_xSetNoED; + std::unique_ptr<weld::SpinButton> m_xSetNoNF; + std::unique_ptr<weld::Button> m_xNextPB; + std::unique_ptr<weld::Button> m_xEndPB; + std::unique_ptr<weld::Button> m_xOK; + + DECL_LINK(NewHdl_Impl, weld::Button&, void); + DECL_LINK(DeleteHdl_Impl, weld::Button&, void); + DECL_LINK(FindHdl_Impl, weld::Button&, void); + DECL_LINK(CustomizeHdl_Impl, weld::Button&, void); + DECL_LINK(OkHdl_Impl, weld::Button&, void); + DECL_LINK(DBCursorHdl_Impl, weld::Button&, void); + DECL_LINK(DBNumCursorHdl_Impl, weld::Entry&, void); + DECL_LINK(RefreshNum_Impl, weld::Widget&, void); + void DBNumCursor(); + + void UpdateButtons(); + +public: + SwCreateAddressListDialog(weld::Window* pParent, const OUString& rURL, SwMailMergeConfigItem const & rConfig); + virtual ~SwCreateAddressListDialog() override; + + const OUString& GetURL() const { return m_sURL; } + void Find( const OUString& rSearch, sal_Int32 nColumn); +}; + +class SwFindEntryDialog : public weld::GenericDialogController +{ + SwCreateAddressListDialog* m_pParent; + + std::unique_ptr<weld::Entry> m_xFindED; + std::unique_ptr<weld::CheckButton> m_xFindOnlyCB; + std::unique_ptr<weld::ComboBox> m_xFindOnlyLB; + std::unique_ptr<weld::Button> m_xFindPB; + std::unique_ptr<weld::Button> m_xCancel; + + DECL_LINK(FindHdl_Impl, weld::Button&, void); + DECL_LINK(FindEnableHdl_Impl, weld::Entry&, void); + DECL_LINK(CloseHdl_Impl, weld::Button&, void); + +public: + SwFindEntryDialog(SwCreateAddressListDialog* pParent); + virtual ~SwFindEntryDialog() override; + + void show() { m_xDialog->show(); } + void set_visible(bool bVisible) { m_xDialog->set_visible(bVisible); } + void hide() { m_xDialog->hide(); } + bool get_visible() const { return m_xDialog->get_visible(); } + + weld::ComboBox& GetFieldsListBox() + { + return *m_xFindOnlyLB; + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/customizeaddresslistdialog.cxx b/sw/source/ui/dbui/customizeaddresslistdialog.cxx new file mode 100644 index 000000000..d5b9ffd68 --- /dev/null +++ b/sw/source/ui/dbui/customizeaddresslistdialog.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 "customizeaddresslistdialog.hxx" +#include "createaddresslistdialog.hxx" + +SwCustomizeAddressListDialog::SwCustomizeAddressListDialog( + weld::Window* pParent, const SwCSVData& rOldData) + : SfxDialogController(pParent, "modules/swriter/ui/customizeaddrlistdialog.ui", + "CustomizeAddrListDialog") + , m_xNewData(new SwCSVData(rOldData)) + , m_xFieldsLB(m_xBuilder->weld_tree_view("treeview")) + , m_xAddPB(m_xBuilder->weld_button("add")) + , m_xDeletePB(m_xBuilder->weld_button("delete")) + , m_xRenamePB(m_xBuilder->weld_button("rename")) + , m_xUpPB(m_xBuilder->weld_button("up")) + , m_xDownPB(m_xBuilder->weld_button("down")) +{ + m_xFieldsLB->set_size_request(-1, m_xFieldsLB->get_height_rows(14)); + + m_xFieldsLB->connect_changed(LINK(this, SwCustomizeAddressListDialog, ListBoxSelectHdl_Impl)); + Link<weld::Button&,void> aAddRenameLk = LINK(this, SwCustomizeAddressListDialog, AddRenameHdl_Impl ); + m_xAddPB->connect_clicked(aAddRenameLk); + m_xRenamePB->connect_clicked(aAddRenameLk); + m_xDeletePB->connect_clicked(LINK(this, SwCustomizeAddressListDialog, DeleteHdl_Impl )); + Link<weld::Button&,void> aUpDownLk = LINK(this, SwCustomizeAddressListDialog, UpDownHdl_Impl); + m_xUpPB->connect_clicked(aUpDownLk); + m_xDownPB->connect_clicked(aUpDownLk); + + for (const auto& rHeader : m_xNewData->aDBColumnHeaders) + m_xFieldsLB->append_text(rHeader); + + m_xFieldsLB->select(0); + UpdateButtons(); +} + +SwCustomizeAddressListDialog::~SwCustomizeAddressListDialog() +{ +} + +IMPL_LINK_NOARG(SwCustomizeAddressListDialog, ListBoxSelectHdl_Impl, weld::TreeView&, void) +{ + UpdateButtons(); +} + +IMPL_LINK(SwCustomizeAddressListDialog, AddRenameHdl_Impl, weld::Button&, rButton, void) +{ + bool bRename = &rButton == m_xRenamePB.get(); + auto nPos = m_xFieldsLB->get_selected_index(); + if (nPos == -1) + nPos = 0; + + std::unique_ptr<SwAddRenameEntryDialog> xDlg; + if (bRename) + xDlg.reset(new SwRenameEntryDialog(m_xDialog.get(), m_xNewData->aDBColumnHeaders)); + else + xDlg.reset(new SwAddEntryDialog(m_xDialog.get(), m_xNewData->aDBColumnHeaders)); + if (bRename) + { + OUString aTemp = m_xFieldsLB->get_text(nPos); + xDlg->SetFieldName(aTemp); + } + if (xDlg->run() == RET_OK) + { + OUString sNew = xDlg->GetFieldName(); + if(bRename) + { + m_xNewData->aDBColumnHeaders[nPos] = sNew; + m_xFieldsLB->remove(nPos); + } + else + { + if (m_xFieldsLB->get_selected_index() != -1) + ++nPos; // append the new entry behind the selected + //add the new column + m_xNewData->aDBColumnHeaders.insert(m_xNewData->aDBColumnHeaders.begin() + nPos, sNew); + //add a new entry into all data arrays + for (auto& rData : m_xNewData->aDBData) + rData.insert(rData.begin() + nPos, OUString()); + + } + + m_xFieldsLB->insert_text(nPos, sNew); + m_xFieldsLB->select(nPos); + } + UpdateButtons(); +} + +IMPL_LINK_NOARG(SwCustomizeAddressListDialog, DeleteHdl_Impl, weld::Button&, void) +{ + auto nPos = m_xFieldsLB->get_selected_index(); + m_xFieldsLB->remove(nPos); + m_xFieldsLB->select(nPos > m_xFieldsLB->n_children() - 1 ? nPos - 1 : nPos); + + //remove the column + m_xNewData->aDBColumnHeaders.erase(m_xNewData->aDBColumnHeaders.begin() + nPos); + //remove the data + for (auto& rData : m_xNewData->aDBData) + rData.erase(rData.begin() + nPos); + + UpdateButtons(); +} + +IMPL_LINK(SwCustomizeAddressListDialog, UpDownHdl_Impl, weld::Button&, rButton, void) +{ + auto nPos = m_xFieldsLB->get_selected_index(); + auto nOldPos = nPos; + OUString aTemp = m_xFieldsLB->get_text(nPos); + m_xFieldsLB->remove(nPos); + if (&rButton == m_xUpPB.get()) + --nPos; + else + ++nPos; + m_xFieldsLB->insert_text(nPos, aTemp); + m_xFieldsLB->select(nPos); + //align m_xNewData + OUString sHeader = m_xNewData->aDBColumnHeaders[nOldPos]; + m_xNewData->aDBColumnHeaders.erase(m_xNewData->aDBColumnHeaders.begin() + nOldPos); + m_xNewData->aDBColumnHeaders.insert(m_xNewData->aDBColumnHeaders.begin() + nPos, sHeader); + for (auto& rData : m_xNewData->aDBData) + { + OUString sData = rData[nOldPos]; + rData.erase(rData.begin() + nOldPos); + rData.insert(rData.begin() + nPos, sData); + } + + UpdateButtons(); +} + +void SwCustomizeAddressListDialog::UpdateButtons() +{ + auto nPos = m_xFieldsLB->get_selected_index(); + auto nEntries = m_xFieldsLB->n_children(); + m_xUpPB->set_sensitive(nPos > 0 && nEntries > 0); + m_xDownPB->set_sensitive(nPos < nEntries -1); + m_xDeletePB->set_sensitive(nEntries > 0); + m_xRenamePB->set_sensitive(nEntries > 0); +} + +SwAddRenameEntryDialog::SwAddRenameEntryDialog( + weld::Window* pParent, const OUString& rUIXMLDescription, const OString& rID, + const std::vector< OUString >& rCSVHeader) + : SfxDialogController(pParent, rUIXMLDescription, rID) + , m_rCSVHeader(rCSVHeader) + , m_xFieldNameED(m_xBuilder->weld_entry("entry")) + , m_xOK(m_xBuilder->weld_button("ok")) +{ + m_xFieldNameED->connect_changed(LINK(this, SwAddRenameEntryDialog, ModifyHdl_Impl)); + ModifyHdl_Impl(*m_xFieldNameED); +} + +IMPL_LINK(SwAddRenameEntryDialog, ModifyHdl_Impl, weld::Entry&, rEdit, void) +{ + OUString sEntry = rEdit.get_text(); + bool bFound = sEntry.isEmpty(); + + if(!bFound) + { + bFound = std::any_of(m_rCSVHeader.begin(), m_rCSVHeader.end(), + [&sEntry](const OUString& rHeader) { return rHeader == sEntry; }); + } + m_xOK->set_sensitive(!bFound); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/customizeaddresslistdialog.hxx b/sw/source/ui/dbui/customizeaddresslistdialog.hxx new file mode 100644 index 000000000..107d7b855 --- /dev/null +++ b/sw/source/ui/dbui/customizeaddresslistdialog.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_UI_DBUI_CUSTOMIZEADDRESSLISTDIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_CUSTOMIZEADDRESSLISTDIALOG_HXX + +#include <sfx2/basedlgs.hxx> +#include <vcl/weld.hxx> + +#include "createaddresslistdialog.hxx" + +struct SwCSVData; + +class SwCustomizeAddressListDialog : public SfxDialogController +{ + std::unique_ptr<SwCSVData> m_xNewData; + std::unique_ptr<weld::TreeView> m_xFieldsLB; + std::unique_ptr<weld::Button> m_xAddPB; + std::unique_ptr<weld::Button> m_xDeletePB; + std::unique_ptr<weld::Button> m_xRenamePB; + std::unique_ptr<weld::Button> m_xUpPB; + std::unique_ptr<weld::Button> m_xDownPB; + + DECL_LINK(AddRenameHdl_Impl, weld::Button&, void); + DECL_LINK(DeleteHdl_Impl, weld::Button&, void); + DECL_LINK(UpDownHdl_Impl, weld::Button&, void); + DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void); + + void UpdateButtons(); +public: + SwCustomizeAddressListDialog(weld::Window* pParent, const SwCSVData& rOldData); + virtual ~SwCustomizeAddressListDialog() override; + + std::unique_ptr<SwCSVData> ReleaseNewData() { return std::move(m_xNewData);} +}; + +class SwAddRenameEntryDialog : public SfxDialogController +{ + const std::vector< OUString >& m_rCSVHeader; + std::unique_ptr<weld::Entry> m_xFieldNameED; + std::unique_ptr<weld::Button> m_xOK; + + DECL_LINK(ModifyHdl_Impl, weld::Entry&, void); +protected: + SwAddRenameEntryDialog(weld::Window* pParent, const OUString& rUIXMLDescription, + const OString& rID, const std::vector< OUString >& rCSVHeader); + +public: + void SetFieldName(const OUString& rName) { m_xFieldNameED->set_text(rName); } + OUString GetFieldName() const { return m_xFieldNameED->get_text(); } + +}; + +class SwAddEntryDialog : public SwAddRenameEntryDialog +{ +public: + SwAddEntryDialog(weld::Window* pParent, const std::vector< OUString >& rCSVHeader) + : SwAddRenameEntryDialog(pParent, "modules/swriter/ui/addentrydialog.ui", + "AddEntryDialog", rCSVHeader) + { + } +}; + +class SwRenameEntryDialog : public SwAddRenameEntryDialog +{ +public: + SwRenameEntryDialog(weld::Window* pParent, const std::vector< OUString >& rCSVHeader) + : SwAddRenameEntryDialog(pParent, "modules/swriter/ui/renameentrydialog.ui", + "RenameEntryDialog", rCSVHeader) + { + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/dbinsdlg.cxx b/sw/source/ui/dbui/dbinsdlg.cxx new file mode 100644 index 000000000..53bbc0b61 --- /dev/null +++ b/sw/source/ui/dbui/dbinsdlg.cxx @@ -0,0 +1,1751 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <dbinsdlg.hxx> + +#include <float.h> + +#include <hintids.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/types.hxx> +#include <svl/numuno.hxx> +#include <svl/stritem.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/mnemonic.hxx> +#include <svl/style.hxx> +#include <svl/zformat.hxx> +#include <sfx2/htmlmode.hxx> +#include <svl/itemset.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <unotools/collatorwrapper.hxx> +#include <fmtclds.hxx> +#include <tabcol.hxx> +#include <uiitems.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <dbmgr.hxx> +#include <tblafmt.hxx> +#include <cellatr.hxx> +#include <swtablerep.hxx> +#include <dbfld.hxx> +#include <fmtcol.hxx> +#include <swwait.hxx> +#include <modcfg.hxx> +#include <swmodule.hxx> +#include <poolfmt.hxx> +#include <connectivity/dbtools.hxx> + +#include <cmdid.h> +#include <SwStyleNameMapper.hxx> +#include <tabsh.hxx> +#include <swabstdlg.hxx> +#include <strings.hrc> +#include <IDocumentMarkAccess.hxx> + +#include <o3tl/any.hxx> + +#include <memory> +#include <swuiexp.hxx> + +using namespace ::dbtools; +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; + +const char cDBFieldStart = '<'; +const char cDBFieldEnd = '>'; + +// Helper structure for adding database rows as fields or text +struct DB_Column +{ + const enum class Type { FILLTEXT, COL_FIELD, COL_TEXT, SPLITPARA } eColType; + + union { + OUString* pText; + SwField* pField; + sal_uInt32 nFormat; + }; + const SwInsDBColumn* pColInfo; + + DB_Column() : eColType(Type::SPLITPARA), + pText(nullptr), + pColInfo(nullptr) + {} + + explicit DB_Column( const OUString& rText ) + : eColType(Type::FILLTEXT), + pText(new OUString(rText)), + pColInfo(nullptr) + {} + + DB_Column( const SwInsDBColumn& rInfo, sal_uInt32 nFormat_ ) + : eColType(Type::COL_TEXT), + nFormat(nFormat_), + pColInfo(&rInfo) + {} + + DB_Column( const SwInsDBColumn& rInfo, SwDBField& rField ) + : eColType(Type::COL_FIELD), + pField(&rField), + pColInfo(&rInfo) + {} + + ~DB_Column() + { + if( Type::COL_FIELD == eColType ) + delete pField; + else if( Type::FILLTEXT == eColType ) + delete pText; + } +}; + +namespace { + +struct DB_ColumnConfigData +{ + SwInsDBColumns aDBColumns; + OUString sEdit; + OUString sTableList; + OUString sTmplNm; + OUString sTAutoFormatNm; + bool bIsTable : 1, + bIsField : 1, + bIsHeadlineOn : 1, + bIsEmptyHeadln : 1; + + DB_ColumnConfigData(DB_ColumnConfigData const&) = delete; + DB_ColumnConfigData& operator=(DB_ColumnConfigData const&) = delete; + + DB_ColumnConfigData() + { + bIsTable = bIsHeadlineOn = true; + bIsField = bIsEmptyHeadln = false; + } +}; + +} + +bool SwInsDBColumn::operator<( const SwInsDBColumn& rCmp ) const +{ + return 0 > GetAppCollator().compareString( sColumn, rCmp.sColumn ); +} + +SwInsertDBColAutoPilot::SwInsertDBColAutoPilot( SwView& rView, + Reference<XDataSource> const & xDataSource, + Reference<sdbcx::XColumnsSupplier> const & xColSupp, + const SwDBData& rData ) + : SfxDialogController(rView.GetWindow()->GetFrameWeld(), "modules/swriter/ui/insertdbcolumnsdialog.ui", "InsertDbColumnsDialog") + , ConfigItem("Office.Writer/InsertData/DataSet", ConfigItemMode::NONE) + , aDBData(rData) + , sNoTmpl(SwResId(SW_STR_NONE)) + , pView(&rView) + , m_xRbAsTable(m_xBuilder->weld_radio_button("astable")) + , m_xRbAsField(m_xBuilder->weld_radio_button("asfields")) + , m_xRbAsText(m_xBuilder->weld_radio_button("astext")) + , m_xHeadFrame(m_xBuilder->weld_frame("dbframe")) + , m_xLbTableDbColumn(m_xBuilder->weld_tree_view("tabledbcols")) + , m_xLbTextDbColumn(m_xBuilder->weld_tree_view("tabletxtcols")) + , m_xFormatFrame(m_xBuilder->weld_frame("formatframe")) + , m_xRbDbFormatFromDb(m_xBuilder->weld_radio_button("fromdatabase")) + , m_xRbDbFormatFromUsr(m_xBuilder->weld_radio_button("userdefined")) + , m_xLbDbFormatFromUsr(new NumFormatListBox(m_xBuilder->weld_combo_box("numformat"))) + , m_xIbDbcolToEdit(m_xBuilder->weld_button("toedit")) + , m_xEdDbText(m_xBuilder->weld_text_view("textview")) + , m_xFtDbParaColl(m_xBuilder->weld_label("parastylelabel")) + , m_xLbDbParaColl(m_xBuilder->weld_combo_box("parastyle")) + , m_xIbDbcolAllTo(m_xBuilder->weld_button("oneright")) + , m_xIbDbcolOneTo(m_xBuilder->weld_button("allright")) + , m_xIbDbcolOneFrom(m_xBuilder->weld_button("oneleft")) + , m_xIbDbcolAllFrom(m_xBuilder->weld_button("allleft")) + , m_xFtTableCol(m_xBuilder->weld_label("tablecolft")) + , m_xLbTableCol(m_xBuilder->weld_tree_view("tablecols")) + , m_xCbTableHeadon(m_xBuilder->weld_check_button("tableheading")) + , m_xRbHeadlColnms(m_xBuilder->weld_radio_button("columnname")) + , m_xRbHeadlEmpty(m_xBuilder->weld_radio_button("rowonly")) + , m_xPbTableFormat(m_xBuilder->weld_button("tableformat")) + , m_xPbTableAutofmt(m_xBuilder->weld_button("autoformat")) +{ + m_xEdDbText->set_size_request(m_xEdDbText->get_approximate_digit_width() * 40, -1); + m_xLbDbParaColl->make_sorted(); + + nGBFormatLen = m_xFormatFrame->get_label().getLength(); + + if (xColSupp.is()) + { + SwWrtShell& rSh = pView->GetWrtShell(); + SvNumberFormatter* pNumFormatr = rSh.GetNumberFormatter(); + SvNumberFormatsSupplierObj* pNumFormat = new SvNumberFormatsSupplierObj( pNumFormatr ); + Reference< util::XNumberFormatsSupplier > xDocNumFormatsSupplier = pNumFormat; + Reference< util::XNumberFormats > xDocNumberFormats = xDocNumFormatsSupplier->getNumberFormats(); + Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, UNO_QUERY); + + Reference<XPropertySet> xSourceProps(xDataSource, UNO_QUERY); + Reference< util::XNumberFormats > xNumberFormats; + if(xSourceProps.is()) + { + Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier"); + if(aFormats.hasValue()) + { + Reference< util::XNumberFormatsSupplier> xSuppl; + aFormats >>= xSuppl; + if(xSuppl.is()) + { + xNumberFormats = xSuppl->getNumberFormats( ); + } + } + } + Reference <XNameAccess> xCols = xColSupp->getColumns(); + const Sequence<OUString> aColNames = xCols->getElementNames(); + for (const OUString& rColName : aColNames) + { + std::unique_ptr<SwInsDBColumn> pNew(new SwInsDBColumn( rColName )); + Any aCol = xCols->getByName(rColName); + Reference <XPropertySet> xCol; + aCol >>= xCol; + Any aType = xCol->getPropertyValue("Type"); + sal_Int32 eDataType = 0; + aType >>= eDataType; + switch(eDataType) + { + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + case DataType::FLOAT: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + { + pNew->bHasFormat = true; + Any aFormat = xCol->getPropertyValue("FormatKey"); + if(aFormat.hasValue()) + { + sal_Int32 nFormat = 0; + aFormat >>= nFormat; + if(xNumberFormats.is()) + { + try + { + Reference<XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat ); + Any aFormatVal = xNumProps->getPropertyValue("FormatString"); + Any aLocale = xNumProps->getPropertyValue("Locale"); + OUString sFormat; + aFormatVal >>= sFormat; + lang::Locale aLoc; + aLocale >>= aLoc; + sal_Int32 nKey = xDocNumberFormats->queryKey( sFormat, aLoc, true); + if(nKey < 0) + { + nKey = xDocNumberFormats->addNew( sFormat, aLoc ); + } + pNew->nDBNumFormat = nKey; + } + catch (const Exception&) + { + OSL_FAIL("illegal number format key"); + } + } + } + else + { + pNew->nDBNumFormat = getDefaultNumberFormat(xCol, + xDocNumberFormatTypes, LanguageTag( rSh.GetCurLang() ).getLocale()); + } + + } + break; + } + if( !aDBColumns.insert( std::move(pNew) ).second ) + { + OSL_ENSURE( false, "Spaltenname mehrfach vergeben?" ); + } + } + } + + // fill paragraph templates-ListBox + { + SfxStyleSheetBasePool* pPool = pView->GetDocShell()->GetStyleSheetPool(); + m_xLbDbParaColl->append_text( sNoTmpl ); + + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Para); + while( pBase ) + { + m_xLbDbParaColl->append_text( pBase->GetName() ); + pBase = pPool->Next(); + } + m_xLbDbParaColl->set_active( 0 ); + } + + // when the cursor is inside of a table, table must NEVER be selectable + if( pView->GetWrtShell().GetTableFormat() ) + { + m_xRbAsTable->set_sensitive( false ); + m_xRbAsField->set_active(true); + m_xRbDbFormatFromDb->set_active(true); + } + else + { + m_xRbAsTable->set_active(true); + m_xRbDbFormatFromDb->set_active(true); + m_xIbDbcolOneFrom->set_sensitive( false ); + m_xIbDbcolAllFrom->set_sensitive( false ); + } + + // by default, select header button + m_xRbHeadlColnms->set_active(true); + m_xRbHeadlEmpty->set_active(false); + + m_xRbAsTable->connect_clicked( LINK(this, SwInsertDBColAutoPilot, PageHdl )); + m_xRbAsField->connect_clicked( LINK(this, SwInsertDBColAutoPilot, PageHdl )); + m_xRbAsText->connect_clicked( LINK(this, SwInsertDBColAutoPilot, PageHdl )); + + m_xRbDbFormatFromDb->connect_clicked( LINK(this, SwInsertDBColAutoPilot, DBFormatHdl )); + m_xRbDbFormatFromUsr->connect_clicked( LINK(this, SwInsertDBColAutoPilot, DBFormatHdl )); + + m_xPbTableFormat->connect_clicked(LINK(this, SwInsertDBColAutoPilot, TableFormatHdl )); + m_xPbTableAutofmt->connect_clicked(LINK(this, SwInsertDBColAutoPilot, AutoFormatHdl )); + + m_xIbDbcolAllTo->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl )); + m_xIbDbcolOneTo->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl )); + m_xIbDbcolOneFrom->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl )); + m_xIbDbcolAllFrom->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl )); + m_xIbDbcolToEdit->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl )); + + m_xCbTableHeadon->connect_clicked( LINK(this, SwInsertDBColAutoPilot, HeaderHdl )); + m_xRbHeadlColnms->connect_clicked( LINK(this, SwInsertDBColAutoPilot, HeaderHdl )); + m_xRbHeadlEmpty->connect_clicked( LINK(this, SwInsertDBColAutoPilot, HeaderHdl )); + + m_xLbTextDbColumn->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl )); + m_xLbTableDbColumn->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl )); + m_xLbDbFormatFromUsr->connect_changed( LINK( this, SwInsertDBColAutoPilot, CBSelectHdl )); + m_xLbTableCol->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl )); + + m_xLbTextDbColumn->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl )); + m_xLbTableDbColumn->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl )); + m_xLbTableCol->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl )); + + for( size_t n = 0; n < aDBColumns.size(); ++n ) + { + const OUString& rS = aDBColumns[ n ]->sColumn; + m_xLbTableDbColumn->append_text(rS); + m_xLbTextDbColumn->append_text(rS); + } + m_xLbTextDbColumn->select(0); + m_xLbTableDbColumn->select(0); + + // read configuration + Load(); + + // lock size to widest config + m_xHeadFrame->set_size_request(m_xHeadFrame->get_preferred_size().Width(), -1); + // initialise Controls: + PageHdl(m_xRbAsTable->get_active() ? *m_xRbAsTable : *m_xRbAsField); +} + +SwInsertDBColAutoPilot::~SwInsertDBColAutoPilot() +{ +} + +IMPL_LINK( SwInsertDBColAutoPilot, PageHdl, weld::Button&, rButton, void ) +{ + bool bShowTable = &rButton == m_xRbAsTable.get(); + + m_xHeadFrame->set_label(MnemonicGenerator::EraseAllMnemonicChars(rButton.get_label().replace('_', '~'))); + + m_xLbTextDbColumn->set_visible( !bShowTable ); + m_xIbDbcolToEdit->set_visible( !bShowTable ); + m_xEdDbText->set_visible( !bShowTable ); + m_xFtDbParaColl->set_visible( !bShowTable ); + m_xLbDbParaColl->set_visible( !bShowTable ); + + m_xLbTableDbColumn->set_visible( bShowTable ); + m_xIbDbcolAllTo->set_visible( bShowTable ); + m_xIbDbcolOneTo->set_visible( bShowTable ); + m_xIbDbcolOneFrom->set_visible( bShowTable ); + m_xIbDbcolAllFrom->set_visible( bShowTable ); + m_xFtTableCol->set_visible( bShowTable ); + m_xLbTableCol->set_visible( bShowTable ); + m_xCbTableHeadon->set_visible( bShowTable ); + m_xRbHeadlColnms->set_visible( bShowTable ); + m_xRbHeadlEmpty->set_visible( bShowTable ); + m_xPbTableFormat->set_visible( bShowTable ); + m_xPbTableAutofmt->set_visible( bShowTable ); + + if( bShowTable ) + m_xPbTableFormat->set_sensitive( 0 != m_xLbTableCol->n_children() ); + + TVSelectHdl( bShowTable ? *m_xLbTableDbColumn : *m_xLbTextDbColumn ); +} + +IMPL_LINK( SwInsertDBColAutoPilot, DBFormatHdl, weld::Button&, rButton, void ) +{ + weld::TreeView& rBox = m_xRbAsTable->get_active() + ? ( m_xLbTableCol->get_id(0).isEmpty() + ? *m_xLbTableDbColumn + : *m_xLbTableCol ) + : *m_xLbTextDbColumn; + + SwInsDBColumn aSrch(rBox.get_selected_text()); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + + bool bFromDB = m_xRbDbFormatFromDb.get() == &rButton; + (*it)->bIsDBFormat = bFromDB; + m_xLbDbFormatFromUsr->set_sensitive( !bFromDB ); +} + +IMPL_LINK( SwInsertDBColAutoPilot, TableToFromHdl, weld::Button&, rButton, void ) +{ + bool bChgEnable = true, bEnableTo = true, bEnableFrom = true; + + if( &rButton == m_xIbDbcolAllTo.get() ) + { + bEnableTo = false; + + sal_Int32 n, nInsPos = m_xLbTableCol->get_selected_index(), + nCnt = m_xLbTableDbColumn->n_children(); + + m_xLbTableDbColumn->unselect_all(); + + m_xLbTableDbColumn->freeze(); + m_xLbTableCol->freeze(); + + if (nInsPos == -1) + for( n = 0; n < nCnt; ++n ) + m_xLbTableCol->append_text(m_xLbTableDbColumn->get_text(n)); + else + for( n = 0; n < nCnt; ++n, ++nInsPos ) + m_xLbTableCol->insert_text(nInsPos, m_xLbTableDbColumn->get_text(n)); + m_xLbTableDbColumn->clear(); + + m_xLbTableDbColumn->thaw(); + m_xLbTableCol->thaw(); + + m_xLbTableCol->select(nInsPos); + } + else if( &rButton == m_xIbDbcolOneTo.get() && + m_xLbTableDbColumn->get_selected_index() != -1 ) + { + sal_Int32 nInsPos = m_xLbTableCol->get_selected_index(), + nDelPos = m_xLbTableDbColumn->get_selected_index(); + m_xLbTableCol->insert_text(nInsPos, m_xLbTableDbColumn->get_text(nDelPos)); + m_xLbTableDbColumn->remove(nDelPos); + + m_xLbTableCol->select(nInsPos); + if (nDelPos >= m_xLbTableDbColumn->n_children()) + nDelPos = m_xLbTableDbColumn->n_children() - 1; + m_xLbTableDbColumn->select(nDelPos); + + bEnableTo = 0 != m_xLbTableDbColumn->n_children(); + } + else if( &rButton == m_xIbDbcolOneFrom.get() ) + { + if (m_xLbTableCol->get_selected_index() != -1) + { + sal_Int32 nInsPos, + nDelPos = m_xLbTableCol->get_selected_index(); + + // look for the right InsertPos!! + SwInsDBColumn aSrch(m_xLbTableCol->get_text(nDelPos)); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + if( it == aDBColumns.begin() || (it+1) == aDBColumns.end() ) + nInsPos = it - aDBColumns.begin(); + else + { + nInsPos = -1; + while( ++it != aDBColumns.end() && + -1 == (nInsPos = m_xLbTableDbColumn-> + find_text( (*it)->sColumn )) ) + ; + } + + m_xLbTableDbColumn->insert_text(nInsPos, aSrch.sColumn); + m_xLbTableCol->remove( nDelPos ); + + if (nInsPos >= m_xLbTableDbColumn->n_children()) + nInsPos = m_xLbTableDbColumn->n_children() - 1; + m_xLbTableDbColumn->select(nInsPos); + + if (nDelPos >= m_xLbTableCol->n_children()) + nDelPos = m_xLbTableCol->n_children() - 1; + m_xLbTableCol->select(nDelPos); + } + else + bEnableTo = 0 != m_xLbTableDbColumn->n_children(); + + bEnableFrom = 0 != m_xLbTableCol->n_children(); + } + else if( &rButton == m_xIbDbcolAllFrom.get() ) + { + bEnableFrom = false; + + m_xLbTableDbColumn->freeze(); + m_xLbTableCol->freeze(); + + m_xLbTableDbColumn->clear(); + m_xLbTableCol->clear(); + for (size_t n = 0; n < aDBColumns.size(); ++n) + m_xLbTableDbColumn->append_text(aDBColumns[n]->sColumn); + + m_xLbTableDbColumn->thaw(); + m_xLbTableCol->thaw(); + + m_xLbTableDbColumn->select(0); + } + else if( &rButton == m_xIbDbcolToEdit.get() ) + { + bChgEnable = false; + // move data to Edit: + OUString aField(m_xLbTextDbColumn->get_selected_text()); + if( !aField.isEmpty() ) + { + OUString aStr( m_xEdDbText->get_text() ); + int nStartPos, nEndPos; + m_xEdDbText->get_selection_bounds(nStartPos, nEndPos); + sal_Int32 nPos = std::min(nStartPos, nEndPos); + sal_Int32 nMax = std::max(nStartPos, nEndPos); + const sal_Int32 nSel = nMax - nPos; + if( nSel ) + // first delete the existing selection + aStr = aStr.replaceAt( nPos, nSel, "" ); + + aField = OUStringChar(cDBFieldStart) + aField + OUStringChar(cDBFieldEnd); + if( !aStr.isEmpty() ) + { + if( nPos ) // one blank in front + { + sal_Unicode c = aStr[ nPos-1 ]; + if( '\n' != c && '\r' != c ) + aField = " " + aField; + } + if( nPos < aStr.getLength() ) // one blank behind + { + sal_Unicode c = aStr[ nPos ]; + if( '\n' != c && '\r' != c ) + aField += " "; + } + } + + m_xEdDbText->set_text( aStr.replaceAt( nPos, 0, aField ) ); + nPos += aField.getLength(); + m_xEdDbText->select_region(nPos, nPos); + } + } + + if( bChgEnable ) + { + m_xIbDbcolOneTo->set_sensitive( bEnableTo ); + m_xIbDbcolAllTo->set_sensitive( bEnableTo ); + m_xIbDbcolOneFrom->set_sensitive( bEnableFrom ); + m_xIbDbcolAllFrom->set_sensitive( bEnableFrom ); + + m_xRbDbFormatFromDb->set_sensitive( false ); + m_xRbDbFormatFromUsr->set_sensitive( false ); + m_xLbDbFormatFromUsr->set_sensitive( false ); + + m_xPbTableFormat->set_sensitive( bEnableFrom ); + } +} + +IMPL_LINK(SwInsertDBColAutoPilot, DblClickHdl, weld::TreeView&, rBox, bool) +{ + weld::Button* pButton = nullptr; + if( &rBox == m_xLbTextDbColumn.get() ) + pButton = m_xIbDbcolToEdit.get(); + else if( &rBox == m_xLbTableDbColumn.get() && m_xIbDbcolOneTo->get_sensitive() ) + pButton = m_xIbDbcolOneTo.get(); + else if( &rBox == m_xLbTableCol.get() && m_xIbDbcolOneFrom->get_sensitive() ) + pButton = m_xIbDbcolOneFrom.get(); + + if (pButton) + TableToFromHdl(*pButton); + + return true; +} + +IMPL_LINK_NOARG(SwInsertDBColAutoPilot, TableFormatHdl, weld::Button&, void) +{ + SwWrtShell& rSh = pView->GetWrtShell(); + bool bNewSet = false; + if( !pTableSet ) + { + bNewSet = true; + pTableSet.reset(new SfxItemSet( rSh.GetAttrPool(), SwuiGetUITableAttrRange() )); + + // At first acquire the simple attributes + pTableSet->Put( SfxStringItem( FN_PARAM_TABLE_NAME, rSh.GetUniqueTableName() )); + pTableSet->Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE, 1 ) ); + + pTableSet->Put( SfxUInt16Item( SID_BACKGRND_DESTINATION, + rSh.GetViewOptions()->GetTableDest() )); + + SvxBrushItem aBrush( RES_BACKGROUND ); + pTableSet->Put( aBrush ); + aBrush.SetWhich(SID_ATTR_BRUSH_ROW); + pTableSet->Put( aBrush ); + aBrush.SetWhich(SID_ATTR_BRUSH_TABLE); + pTableSet->Put( aBrush ); + + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + // table variant, when multiple table cells are selected + aBoxInfo.SetTable( true ); + // always show gap field + aBoxInfo.SetDist( true); + // set minimum size in tables and paragraphs + aBoxInfo.SetMinDist( false ); + // always set default-gap + aBoxInfo.SetDefDist( MIN_BORDER_DIST ); + // Single lines can have DontCare-status only in tables + aBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISABLE ); + pTableSet->Put( aBoxInfo ); + + SwGetCurColNumPara aPara; + const sal_uInt16 nNum = rSh.GetCurColNum( &aPara ); + long nWidth; + + if( nNum ) + { + nWidth = aPara.pPrtRect->Width(); + const SwFormatCol& rCol = aPara.pFrameFormat->GetCol(); + const SwColumns& rCols = rCol.GetColumns(); + + // initialise nStart and nEnd for nNum == 0 + long nWidth1 = 0, + nStart1 = 0, + nEnd1 = nWidth; + for( sal_uInt16 i = 0; i < nNum; ++i ) + { + const SwColumn* pCol = &rCols[i]; + nStart1 = pCol->GetLeft() + nWidth1; + nWidth1 += static_cast<long>(rCol.CalcColWidth( i, static_cast<sal_uInt16>(nWidth) )); + nEnd1 = nWidth1 - pCol->GetRight(); + } + if(nStart1 || nEnd1 != nWidth) + nWidth = nEnd1 - nStart1; + } + else + nWidth = rSh.GetAnyCurRect( + (FrameTypeFlags::FLY_ANY & rSh.GetFrameType( nullptr, true )) + ? CurRectType::FlyEmbeddedPrt + : CurRectType::PagePrt ).Width(); + + SwTabCols aTabCols; + aTabCols.SetRight( nWidth ); + aTabCols.SetRightMax( nWidth ); + pRep.reset(new SwTableRep( aTabCols )); + pRep->SetAlign( text::HoriOrientation::NONE ); + pRep->SetSpace( nWidth ); + pRep->SetWidth( nWidth ); + pRep->SetWidthPercent( 100 ); + pTableSet->Put( SwPtrItem( FN_TABLE_REP, pRep.get() )); + + pTableSet->Put( SfxUInt16Item( SID_HTML_MODE, + ::GetHtmlMode( pView->GetDocShell() ))); + } + + sal_Int32 nCols = m_xLbTableCol->n_children(); + if (nCols != pRep->GetAllColCount() && nCols > 0) + { + // Number of columns has changed: then the TabCols have to be adjusted + long nWidth = pRep->GetWidth(); + --nCols; + SwTabCols aTabCols( nCols ); + aTabCols.SetRight( nWidth ); + aTabCols.SetRightMax( nWidth ); + if( nCols ) + { + const sal_Int32 nStep = nWidth / (nCols+1); + for( sal_Int32 n = 0; n < nCols; ++n ) + { + aTabCols.Insert( nStep*(n+1), false, n ); + } + } + pRep.reset(new SwTableRep( aTabCols )); + pRep->SetAlign( text::HoriOrientation::NONE ); + pRep->SetSpace( nWidth ); + pRep->SetWidth( nWidth ); + pRep->SetWidthPercent( 100 ); + pTableSet->Put( SwPtrItem( FN_TABLE_REP, pRep.get() )); + } + + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<SfxAbstractTabDialog> pDlg(rFact.CreateSwTableTabDlg(m_xDialog.get(), pTableSet.get(), &rSh)); + if( RET_OK == pDlg->Execute() ) + pTableSet->Put( *pDlg->GetOutputItemSet() ); + else if( bNewSet ) + { + pTableSet.reset(); + pRep.reset(); + } +} + +IMPL_LINK_NOARG(SwInsertDBColAutoPilot, AutoFormatHdl, weld::Button&, void) +{ + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<AbstractSwAutoFormatDlg> pDlg(rFact.CreateSwAutoFormatDlg(m_xDialog.get(), pView->GetWrtShellPtr(), false, m_xTAutoFormat.get())); + if( RET_OK == pDlg->Execute()) + m_xTAutoFormat = pDlg->FillAutoFormatOfIndex(); +} + +IMPL_LINK(SwInsertDBColAutoPilot, TVSelectHdl, weld::TreeView&, rBox, void) +{ + weld::TreeView* pGetBox = &rBox; + + SwInsDBColumn aSrch(pGetBox->get_selected_text()); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + + // set the selected FieldName at the FormatGroupBox, so that + // it's clear what field is configured by the format! + OUString sText( m_xFormatFrame->get_label().copy( 0, nGBFormatLen )); + if( aSrch.sColumn.isEmpty() ) + { + m_xRbDbFormatFromDb->set_sensitive( false ); + m_xRbDbFormatFromUsr->set_sensitive( false ); + m_xLbDbFormatFromUsr->set_sensitive( false ); + } + else + { + bool bEnableFormat = (*it)->bHasFormat; + m_xRbDbFormatFromDb->set_sensitive( bEnableFormat ); + m_xRbDbFormatFromUsr->set_sensitive( bEnableFormat ); + + if( bEnableFormat ) + { + sText += " (" + aSrch.sColumn + ")"; + } + + bool bIsDBFormat = (*it)->bIsDBFormat; + m_xRbDbFormatFromDb->set_active( bIsDBFormat ); + m_xRbDbFormatFromUsr->set_active( !bIsDBFormat ); + m_xLbDbFormatFromUsr->set_sensitive( !bIsDBFormat ); + if( !bIsDBFormat ) + m_xLbDbFormatFromUsr->SetDefFormat( (*it)->nUsrNumFormat ); + } + + m_xFormatFrame->set_label(sText); + + if (m_xLbTableCol->n_children()) + { + // to know later on, what ListBox was the "active", a Flag + // is remembered in the 1st entry + if (&rBox == m_xLbTableCol.get()) + m_xLbTableCol->set_id(0, "tablecols"); + else + m_xLbTableCol->set_id(0, OUString()); + } +} + +IMPL_LINK_NOARG(SwInsertDBColAutoPilot, CBSelectHdl, weld::ComboBox&, void) +{ + weld::TreeView* pGetBox = m_xRbAsTable->get_active() + ? ( m_xLbTableCol->get_id(0).isEmpty() + ? m_xLbTableDbColumn.get() + : m_xLbTableCol.get() ) + : m_xLbTextDbColumn.get(); + + SwInsDBColumn aSrch(pGetBox->get_selected_text()); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + + if( !aSrch.sColumn.isEmpty() ) + { + m_xLbDbFormatFromUsr->CallSelectHdl(); + (*it)->nUsrNumFormat = m_xLbDbFormatFromUsr->GetFormat(); + } +} + +IMPL_LINK( SwInsertDBColAutoPilot, HeaderHdl, weld::Button&, rButton, void ) +{ + if (&rButton == m_xCbTableHeadon.get()) + { + bool bEnable = m_xCbTableHeadon->get_active(); + + m_xRbHeadlColnms->set_sensitive( bEnable ); + m_xRbHeadlEmpty->set_sensitive( bEnable ); + } +} + +static void lcl_InsTextInArr( const OUString& rText, DB_Columns& rColArr ) +{ + sal_Int32 nSttPos = 0, nFndPos; + while( -1 != ( nFndPos = rText.indexOf( '\x0A', nSttPos )) ) + { + if( 1 < nFndPos ) + { + rColArr.push_back(std::make_unique<DB_Column>(rText.copy(nSttPos, nFndPos -1))); + } + rColArr.push_back(std::make_unique<DB_Column>()); + nSttPos = nFndPos + 1; + } + if( nSttPos < rText.getLength() ) + { + rColArr.push_back(std::make_unique<DB_Column>(rText.copy(nSttPos))); + } +} + +bool SwInsertDBColAutoPilot::SplitTextToColArr( const OUString& rText, + DB_Columns& rColArr, + bool bInsField ) +{ + // create each of the database columns from the text again + // and then save in an array + // database columns are in <> and must be present in the columns' array: + OUString sText( rText ); + sal_Int32 nFndPos, nEndPos, nSttPos = 0; + + while( -1 != ( nFndPos = sText.indexOf( cDBFieldStart, nSttPos ))) + { + nSttPos = nFndPos + 1; + if( -1 != ( nEndPos = sText.indexOf( cDBFieldEnd, nSttPos+1 ))) + { + // Text in <> brackets found: what is it: + SwInsDBColumn aSrch( sText.copy( nSttPos, nEndPos - nSttPos )); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + if( it != aDBColumns.end() ) + { + // that is a valid field + // so surely the text "before": + const SwInsDBColumn& rFndCol = **it; + + DB_Column* pNew; + + if( 1 < nSttPos ) + { + ::lcl_InsTextInArr( sText.copy( 0, nSttPos-1 ), rColArr ); + sText = sText.copy( nSttPos-1 ); + } + + sText = sText.copy( rFndCol.sColumn.getLength() + 2 ); + nSttPos = 0; + + sal_uInt16 nSubType = 0; + sal_uInt32 nFormat; + if( rFndCol.bHasFormat ) + { + if( rFndCol.bIsDBFormat ) + nFormat = static_cast<sal_uInt32>(rFndCol.nDBNumFormat); + else + { + nFormat = rFndCol.nUsrNumFormat; + nSubType = nsSwExtendedSubType::SUB_OWN_FMT; + } + } + else + nFormat = 0; + + if( bInsField ) + { + SwWrtShell& rSh = pView->GetWrtShell(); + SwDBFieldType aFieldType( rSh.GetDoc(), aSrch.sColumn, + aDBData ); + pNew = new DB_Column( rFndCol, *new SwDBField( + static_cast<SwDBFieldType*>(rSh.InsertFieldType( aFieldType )), + nFormat ) ); + if( nSubType ) + pNew->pField->SetSubType( nSubType ); + } + else + pNew = new DB_Column( rFndCol, nFormat ); + + rColArr.push_back( std::unique_ptr<DB_Column>(pNew) ); + } + } + } + + // don't forget the last text + if( !sText.isEmpty() ) + ::lcl_InsTextInArr( sText, rColArr ); + + return !rColArr.empty(); +} + +void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, + Reference< XDataSource> const & xSource, + Reference< XConnection> const & xConnection, + Reference< sdbc::XResultSet > const & xResultSet_in ) +{ + auto xResultSet = xResultSet_in; + + const Any* pSelection = rSelection.hasElements() ? rSelection.getConstArray() : nullptr; + SwWrtShell& rSh = pView->GetWrtShell(); + + //with the drag and drop interface no result set is initially available + bool bDisposeResultSet = false; + // we don't have a cursor, so we have to create our own RowSet + if ( !xResultSet.is() ) + { + xResultSet = SwDBManager::createCursor(aDBData.sDataSource,aDBData.sCommand,aDBData.nCommandType,xConnection,pView); + bDisposeResultSet = xResultSet.is(); + } + + Reference< sdbc::XRow > xRow(xResultSet, UNO_QUERY); + if ( !xRow.is() ) + return; + + rSh.StartAllAction(); + bool bUndo = rSh.DoesUndo(); + if( bUndo ) + rSh.StartUndo(); + + bool bAsTable = m_xRbAsTable->get_active(); + SvNumberFormatter& rNumFormatr = *rSh.GetNumberFormatter(); + + if( rSh.HasSelection() ) + rSh.DelRight(); + + std::unique_ptr<SwWait> pWait; + + Reference< XColumnsSupplier > xColsSupp( xResultSet, UNO_QUERY ); + Reference <XNameAccess> xCols = xColsSupp->getColumns(); + + uno::Reference<sdbcx::XRowLocate> xRowLocate(xResultSet, uno::UNO_QUERY_THROW); + + do{ // middle checked loop!! + if( bAsTable ) // fill in data as table + { + rSh.DoUndo( false ); + + sal_Int32 nCols = m_xLbTableCol->n_children(); + sal_Int32 nRows = 0; + if( m_xCbTableHeadon->get_active() ) + nRows++; + + if( pSelection ) + nRows += rSelection.getLength(); + else + ++nRows; + + // prepare the array for the selected columns + std::vector<SwInsDBColumn*> aColFields; + for( sal_Int32 n = 0; n < nCols; ++n ) + { + SwInsDBColumn aSrch(m_xLbTableCol->get_text(n)); + SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch ); + if (it != aDBColumns.end()) + aColFields.push_back(it->get()); + else { + OSL_ENSURE( false, "database column not found" ); + } + } + + if( static_cast<size_t>(nCols) != aColFields.size() ) + { + OSL_ENSURE( false, "not all database columns found" ); + nCols = static_cast<sal_Int32>(aColFields.size()); + } + + if(!nRows || !nCols) + { + OSL_ENSURE( false, "wrong parameters" ); + break; + } + + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + bool bHTML = 0 != (::GetHtmlMode( pView->GetDocShell() ) & HTMLMODE_ON); + rSh.InsertTable( + pModOpt->GetInsTableFlags(bHTML), + nRows, nCols, (pSelection ? m_xTAutoFormat.get(): nullptr) ); + rSh.MoveTable( GotoPrevTable, fnTableStart ); + + if( pSelection && pTableSet ) + SetTabSet(); + + SfxItemSet aTableSet( rSh.GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, + RES_BOXATR_VALUE>{} ); + bool bIsAutoUpdateCells = rSh.IsAutoUpdateCells(); + rSh.SetAutoUpdateCells( false ); + + if( m_xCbTableHeadon->get_active() ) + { + for( sal_Int32 n = 0; n < nCols; ++n ) + { + if( m_xRbHeadlColnms->get_active() ) + { + rSh.SwEditShell::Insert2( aColFields[ n ]->sColumn ); + } + rSh.GoNextCell(); + } + } + else + rSh.SetRowsToRepeat( 0 ); + + for( sal_Int32 i = 0 ; ; ++i ) + { + bool bBreak = false; + try + { + if(pSelection) + { + bBreak = !xRowLocate->moveToBookmark(pSelection[i]); + } + else if(!i) + bBreak = !xResultSet->first(); + } + catch (const Exception&) + { + bBreak = true; + } + if(bBreak) + break; + + for( sal_Int32 n = 0; n < nCols; ++n ) + { + // at the very first time, NO GoNextCell, because we're + // already in it. Also no GoNextCell after the Insert, + // because an empty line is added at the end. + if( i || n ) + rSh.GoNextCell(); + + const SwInsDBColumn* pEntry = aColFields[ n ]; + + Reference< XColumn > xColumn; + xCols->getByName(pEntry->sColumn) >>= xColumn; + Reference< XPropertySet > xColumnProps( xColumn, UNO_QUERY ); + sal_Int32 eDataType = 0; + if( xColumnProps.is() ) + { + Any aType = xColumnProps->getPropertyValue("Type"); + aType >>= eDataType; + } + try + { + if( pEntry->bHasFormat ) + { + SwTableBoxNumFormat aNumFormat( + pEntry->bIsDBFormat ? static_cast<sal_uInt32>(pEntry->nDBNumFormat) + : pEntry->nUsrNumFormat ); + aTableSet.Put(aNumFormat); + if( xColumn.is() ) + { + double fVal = xColumn->getDouble(); + if( xColumn->wasNull() ) + aTableSet.ClearItem( RES_BOXATR_VALUE ); + else + { + if(rNumFormatr.GetType(aNumFormat.GetValue()) & SvNumFormatType::DATE) + { + ::Date aStandard(1,1,1900); + if (rNumFormatr.GetNullDate() != aStandard) + fVal += (aStandard - rNumFormatr.GetNullDate()); + } + aTableSet.Put( SwTableBoxValue( fVal )); + } + } + else + aTableSet.ClearItem( RES_BOXATR_VALUE ); + rSh.SetTableBoxFormulaAttrs( aTableSet ); + } + //#i60207# don't insert binary data as string - creates a loop + else if( DataType::BINARY == eDataType || + DataType::VARBINARY == eDataType || + DataType::LONGVARBINARY== eDataType || + DataType::SQLNULL == eDataType || + DataType::OTHER == eDataType || + DataType::OBJECT == eDataType || + DataType::DISTINCT == eDataType || + DataType::STRUCT == eDataType || + DataType::ARRAY == eDataType || + DataType::BLOB == eDataType || + DataType::CLOB == eDataType || + DataType::REF == eDataType + ) + { + // do nothing + } + else + { + const OUString sVal = xColumn->getString(); + if(!xColumn->wasNull()) + { + rSh.SwEditShell::Insert2( sVal ); + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sw", ""); + } + } + + if( !pSelection ) + { + if ( !xResultSet->next() ) + break; + } + else if( i+1 >= rSelection.getLength() ) + break; + + if( 10 == i ) + pWait.reset(new SwWait( *pView->GetDocShell(), true )); + } + + rSh.MoveTable( GotoCurrTable, fnTableStart ); + if( !pSelection && ( pTableSet || m_xTAutoFormat )) + { + if( pTableSet ) + SetTabSet(); + + if (m_xTAutoFormat) + rSh.SetTableStyle(*m_xTAutoFormat); + } + rSh.SetAutoUpdateCells( bIsAutoUpdateCells ); + } + else // add data as fields/text + { + DB_Columns aColArr; + if( SplitTextToColArr( m_xEdDbText->get_text(), aColArr, m_xRbAsField->get_active() ) ) + { + // now for each data set, we can iterate over the array + // and add the data + + if( !rSh.IsSttPara() ) + rSh.SwEditShell::SplitNode(); + if( !rSh.IsEndPara() ) + { + rSh.SwEditShell::SplitNode(); + rSh.SwCursorShell::Left(1,CRSR_SKIP_CHARS); + } + + rSh.DoUndo( false ); + + SwTextFormatColl* pColl = nullptr; + { + const OUString sTmplNm(m_xLbDbParaColl->get_active_text()); + if( sNoTmpl != sTmplNm ) + { + pColl = rSh.FindTextFormatCollByName( sTmplNm ); + if( !pColl ) + { + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( + sTmplNm, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId ) + pColl = rSh.GetTextCollFromPool( nId ); + else + pColl = rSh.MakeTextFormatColl( sTmplNm ); + } + rSh.SetTextFormatColl( pColl ); + } + } + + // for adding as fields -> insert a "NextField" after + // every data set + SwDBFormatData aDBFormatData; + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + aDBFormatData.xFormatter.set(util::NumberFormatter::create(xContext), UNO_QUERY_THROW) ; + + Reference<XPropertySet> xSourceProps(xSource, UNO_QUERY); + if(xSourceProps.is()) + { + Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier"); + if(aFormats.hasValue()) + { + Reference< util::XNumberFormatsSupplier> xSuppl; + aFormats >>= xSuppl; + if(xSuppl.is()) + { + Reference< XPropertySet > xSettings = xSuppl->getNumberFormatSettings(); + Any aNull = xSettings->getPropertyValue("NullDate"); + aNull >>= aDBFormatData.aNullDate; + if(aDBFormatData.xFormatter.is()) + aDBFormatData.xFormatter->attachNumberFormatsSupplier(xSuppl); + } + } + } + aDBFormatData.aLocale = LanguageTag( rSh.GetCurLang() ).getLocale(); + SwDBNextSetField aNxtDBField( static_cast<SwDBNextSetFieldType*>(rSh. + GetFieldType( 0, SwFieldIds::DbNextSet )), + "1", aDBData ); + + bool bSetCursor = true; + const size_t nCols = aColArr.size(); + ::sw::mark::IMark* pMark = nullptr; + for( sal_Int32 i = 0 ; ; ++i ) + { + bool bBreak = false; + try + { + if(pSelection) + { + bBreak = !xRowLocate->moveToBookmark(pSelection[i]); + } + else if(!i) + bBreak = !xResultSet->first(); + } + catch (const Exception&) + { + bBreak = true; + } + + if(bBreak) + break; + + for( size_t n = 0; n < nCols; ++n ) + { + DB_Column* pDBCol = aColArr[ n ].get(); + OUString sIns; + switch( pDBCol->eColType ) + { + case DB_Column::Type::FILLTEXT: + sIns = *pDBCol->pText; + break; + + case DB_Column::Type::SPLITPARA: + rSh.SplitNode(); + // when the template is not the same as the follow template, + // the selected has to be set newly + if( pColl && &pColl->GetNextTextFormatColl() != pColl ) + rSh.SetTextFormatColl( pColl ); + break; + + case DB_Column::Type::COL_FIELD: + { + std::unique_ptr<SwDBField> pField(static_cast<SwDBField *>( + pDBCol->pField->CopyField().release())); + double nValue = DBL_MAX; + + Reference< XPropertySet > xColumnProps; + xCols->getByName(pDBCol->pColInfo->sColumn) >>= xColumnProps; + + pField->SetExpansion( SwDBManager::GetDBField( + xColumnProps, + aDBFormatData, + &nValue ) ); + if( DBL_MAX != nValue ) + { + Any aType = xColumnProps->getPropertyValue("Type"); + sal_Int32 eDataType = 0; + aType >>= eDataType; + if( DataType::DATE == eDataType || DataType::TIME == eDataType || + DataType::TIMESTAMP == eDataType) + + { + ::Date aStandard(1,1,1900); + ::Date aCompare(aDBFormatData.aNullDate.Day , + aDBFormatData.aNullDate.Month, + aDBFormatData.aNullDate.Year); + if(aStandard != aCompare) + nValue += (aStandard - aCompare); + } + pField->ChgValue( nValue, true ); + } + pField->SetInitialized(); + + rSh.Insert( *pField ); + } + break; + + case DB_Column::Type::COL_TEXT: + { + double nValue = DBL_MAX; + Reference< XPropertySet > xColumnProps; + xCols->getByName(pDBCol->pColInfo->sColumn) >>= xColumnProps; + sIns = SwDBManager::GetDBField( + xColumnProps, + aDBFormatData, + &nValue ); + if( pDBCol->nFormat && + DBL_MAX != nValue ) + { + Color* pCol; + if(rNumFormatr.GetType(pDBCol->nFormat) & SvNumFormatType::DATE) + { + ::Date aStandard(1,1,1900); + if (rNumFormatr.GetNullDate() != aStandard) + nValue += (aStandard - rNumFormatr.GetNullDate()); + } + rNumFormatr.GetOutputString( nValue, + pDBCol->nFormat, + sIns, &pCol ); + } + } + break; + } + + if( !sIns.isEmpty() ) + { + rSh.Insert( sIns ); + + if( bSetCursor) + { + // to the beginning and set a mark, so that + // the cursor can be set to the initial position + // at the end. + + rSh.SwCursorShell::MovePara( + GoCurrPara, fnParaStart ); + pMark = rSh.SetBookmark( + vcl::KeyCode(), + OUString(), + IDocumentMarkAccess::MarkType::UNO_BOOKMARK ); + rSh.SwCursorShell::MovePara( + GoCurrPara, fnParaEnd ); + bSetCursor = false; + } + } + } + + if( !pSelection ) + { + bool bNext = xResultSet->next(); + if(!bNext) + break; + } + else if( i+1 >= rSelection.getLength() ) + break; + + if( m_xRbAsField->get_active() ) + rSh.Insert( aNxtDBField ); + + if( !rSh.IsSttPara() ) + rSh.SwEditShell::SplitNode(); + + if( 10 == i ) + pWait.reset(new SwWait( *pView->GetDocShell(), true )); + } + + if( !bSetCursor && pMark != nullptr) + { + rSh.SetMark(); + rSh.GotoMark( pMark ); + rSh.getIDocumentMarkAccess()->deleteMark( pMark ); + break; + } + } + } + // write configuration + Commit(); + }while( false ); // middle checked loop + + if( bUndo ) + { + rSh.DoUndo(); + rSh.AppendUndoForInsertFromDB( bAsTable ); + rSh.EndUndo(); + } + rSh.ClearMark(); + rSh.EndAllAction(); + + if ( bDisposeResultSet ) + ::comphelper::disposeComponent(xResultSet); +} + +void SwInsertDBColAutoPilot::SetTabSet() +{ + SwWrtShell& rSh = pView->GetWrtShell(); + const SfxPoolItem* pItem; + + if (m_xTAutoFormat) + { + if (m_xTAutoFormat->IsFrame()) + { + // border is from AutoFormat + pTableSet->ClearItem( RES_BOX ); + pTableSet->ClearItem( SID_ATTR_BORDER_INNER ); + } + if (m_xTAutoFormat->IsBackground()) + { + pTableSet->ClearItem( RES_BACKGROUND ); + pTableSet->ClearItem( SID_ATTR_BRUSH_ROW ); + pTableSet->ClearItem( SID_ATTR_BRUSH_TABLE ); + } + } + else + { + // remove the defaults again, it makes no sense to set them + SvxBrushItem aBrush( RES_BACKGROUND ); + static const sal_uInt16 aIds[3] = + { RES_BACKGROUND, SID_ATTR_BRUSH_ROW, SID_ATTR_BRUSH_TABLE }; + for(sal_uInt16 i : aIds) + if( SfxItemState::SET == pTableSet->GetItemState( i, + false, &pItem ) && *pItem == aBrush ) + pTableSet->ClearItem( i ); + } + + if( SfxItemState::SET == pTableSet->GetItemState( FN_PARAM_TABLE_NAME, false, + &pItem ) && static_cast<const SfxStringItem*>(pItem)->GetValue() == + rSh.GetTableFormat()->GetName() ) + pTableSet->ClearItem( FN_PARAM_TABLE_NAME ); + + rSh.MoveTable( GotoCurrTable, fnTableStart ); + rSh.SetMark(); + rSh.MoveTable( GotoCurrTable, fnTableEnd ); + + ItemSetToTableParam( *pTableSet, rSh ); + + rSh.ClearMark(); + rSh.MoveTable( GotoCurrTable, fnTableStart ); +} + +static Sequence<OUString> lcl_createSourceNames(const OUString& rNodeName) +{ + Sequence<OUString> aSourceNames(11); + OUString* pNames = aSourceNames.getArray(); + pNames[0] = rNodeName + "/DataSource"; + pNames[1] = rNodeName + "/Command"; + pNames[2] = rNodeName + "/CommandType"; + pNames[3] = rNodeName + "/ColumnsToText"; + pNames[4] = rNodeName + "/ColumnsToTable"; + pNames[5] = rNodeName + "/ParaStyle"; + pNames[6] = rNodeName + "/TableAutoFormat"; + pNames[7] = rNodeName + "/IsTable"; + pNames[8] = rNodeName + "/IsField"; + pNames[9] = rNodeName + "/IsHeadlineOn"; + pNames[10] = rNodeName + "/IsEmptyHeadline"; + return aSourceNames; +} + +static Sequence<OUString> lcl_CreateSubNames(const OUString& rSubNodeName) +{ + Sequence<OUString> aSubSourceNames(6); + OUString* pNames = aSubSourceNames.getArray(); + pNames[0] = rSubNodeName + "/ColumnName"; + pNames[1] = rSubNodeName + "/ColumnIndex"; + pNames[2] = rSubNodeName + "/IsNumberFormat"; + pNames[3] = rSubNodeName + "/IsNumberFormatFromDataBase"; + pNames[4] = rSubNodeName + "/NumberFormat"; + pNames[5] = rSubNodeName + "/NumberFormatLocale"; + return aSubSourceNames; +} + +static OUString lcl_CreateUniqueName(const Sequence<OUString>& aNames) +{ + sal_Int32 nIdx = aNames.getLength(); + while(true) + { + const OUString sRet = "_" + OUString::number(nIdx++); + if ( comphelper::findValue(aNames, sRet) == -1 ) + return sRet; // No match found, return unique name + } +} + +void SwInsertDBColAutoPilot::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwInsertDBColAutoPilot::ImplCommit() +{ + Sequence <OUString> aNames = GetNodeNames(OUString()); + //remove entries that contain this data source + table at first + for(OUString const & nodeName : std::as_const(aNames)) + { + Sequence<OUString> aSourceNames(2); + OUString* pSourceNames = aSourceNames.getArray(); + pSourceNames[0] = nodeName + "/DataSource"; + pSourceNames[1] = nodeName + "/Command"; + Sequence<Any> aSourceProperties = GetProperties(aSourceNames); + const Any* pSourceProps = aSourceProperties.getArray(); + OUString sSource, sCommand; + pSourceProps[0] >>= sSource; + pSourceProps[1] >>= sCommand; + if(sSource==aDBData.sDataSource && sCommand==aDBData.sCommand) + { + Sequence<OUString> aElements { nodeName }; + ClearNodeElements(OUString(), aElements); + } + } + + aNames = GetNodeNames(OUString()); + OUString sNewNode = lcl_CreateUniqueName(aNames); + Sequence<OUString> aNodeNames = lcl_createSourceNames(sNewNode); + Sequence<PropertyValue> aValues(aNodeNames.getLength()); + PropertyValue* pValues = aValues.getArray(); + const OUString* pNodeNames = aNodeNames.getConstArray(); + for(sal_Int32 i = 0; i < aNodeNames.getLength(); i++) + { + pValues[i].Name = "/" + pNodeNames[i]; + } + + pValues[0].Value <<= aDBData.sDataSource; + pValues[1].Value <<= aDBData.sCommand; + pValues[2].Value <<= aDBData.nCommandType; + pValues[3].Value <<= m_xEdDbText->get_text(); + + OUString sTmp; + const sal_Int32 nCnt = m_xLbTableCol->n_children(); + for( sal_Int32 n = 0; n < nCnt; ++n ) + sTmp += m_xLbTableCol->get_text(n) + "\x0a"; + + if (!sTmp.isEmpty()) + pValues[4].Value <<= sTmp; + + if( sNoTmpl != (sTmp = m_xLbDbParaColl->get_active_text()) ) + pValues[5].Value <<= sTmp; + + if (m_xTAutoFormat) + pValues[6].Value <<= m_xTAutoFormat->GetName(); + + pValues[7].Value <<= m_xRbAsTable->get_active(); + pValues[8].Value <<= m_xRbAsField->get_active(); + pValues[9].Value <<= m_xCbTableHeadon->get_active(); + pValues[10].Value <<= m_xRbHeadlEmpty->get_active(); + + SetSetProperties(OUString(), aValues); + + sNewNode += "/ColumnSet"; + + LanguageType ePrevLang(0xffff); + + SvNumberFormatter& rNFormatr = *pView->GetWrtShell().GetNumberFormatter(); + for(size_t nCol = 0; nCol < aDBColumns.size(); nCol++) + { + SwInsDBColumn* pColumn = aDBColumns[nCol].get(); + OUString sColumnInsertNode(sNewNode + "/__"); + if( nCol < 10 ) + sColumnInsertNode += "00"; + else if( nCol < 100 ) + sColumnInsertNode += "0"; + sColumnInsertNode += OUString::number( nCol ); + + const Sequence <OUString> aSubNodeNames = lcl_CreateSubNames(sColumnInsertNode); + Sequence<PropertyValue> aSubValues(aSubNodeNames.getLength()); + PropertyValue* pSubValues = aSubValues.getArray(); + sal_Int32 i = 0; + + for( const OUString& rSubNodeName : aSubNodeNames) + pSubValues[i++].Name = rSubNodeName; + pSubValues[0].Value <<= pColumn->sColumn; + pSubValues[1].Value <<= i; + pSubValues[2].Value <<= pColumn->bHasFormat; + pSubValues[3].Value <<= pColumn->bIsDBFormat; + + SwStyleNameMapper::FillUIName( RES_POOLCOLL_STANDARD, sTmp ); + const SvNumberformat* pNF = rNFormatr.GetEntry( pColumn->nUsrNumFormat ); + LanguageType eLang; + if( pNF ) + { + pSubValues[4].Value <<= pNF->GetFormatstring(); + eLang = pNF->GetLanguage(); + } + else + { + pSubValues[4].Value <<= sTmp; + eLang = GetAppLanguage(); + } + + OUString sPrevLang; + if( eLang != ePrevLang ) + { + sPrevLang = LanguageTag::convertToBcp47( eLang ); + ePrevLang = eLang; + } + + pSubValues[5].Value <<= sPrevLang; + SetSetProperties(sNewNode, aSubValues); + } +} + +void SwInsertDBColAutoPilot::Load() +{ + const Sequence<OUString> aNames = GetNodeNames(OUString()); + SvNumberFormatter& rNFormatr = *pView->GetWrtShell().GetNumberFormatter(); + for(OUString const & nodeName : aNames) + { + //search for entries with the appropriate data source and table + Sequence<OUString> aSourceNames = lcl_createSourceNames(nodeName); + + Sequence< Any> aDataSourceProps = GetProperties(aSourceNames); + const Any* pDataSourceProps = aDataSourceProps.getConstArray(); + OUString sSource, sCommand; + sal_Int16 nCommandType; + pDataSourceProps[0] >>= sSource; + pDataSourceProps[1] >>= sCommand; + pDataSourceProps[2] >>= nCommandType; + if(sSource == aDBData.sDataSource && sCommand == aDBData.sCommand) + { + std::unique_ptr<DB_ColumnConfigData> pNewData(new DB_ColumnConfigData); + + pDataSourceProps[3] >>= pNewData->sEdit; + pDataSourceProps[4] >>= pNewData->sTableList; + pDataSourceProps[5] >>= pNewData->sTmplNm; + pDataSourceProps[6] >>= pNewData->sTAutoFormatNm; + if(pDataSourceProps[7].hasValue()) + pNewData->bIsTable = *o3tl::doAccess<bool>(pDataSourceProps[7]); + if(pDataSourceProps[8].hasValue()) + pNewData->bIsField = *o3tl::doAccess<bool>(pDataSourceProps[8]); + if(pDataSourceProps[9].hasValue()) + pNewData->bIsHeadlineOn = *o3tl::doAccess<bool>(pDataSourceProps[9]); + if(pDataSourceProps[10].hasValue()) + pNewData->bIsEmptyHeadln = *o3tl::doAccess<bool>(pDataSourceProps[10]); + + const OUString sSubNodeName(nodeName + "/ColumnSet/"); + const Sequence <OUString> aSubNames = GetNodeNames(sSubNodeName); + for(const OUString& rSubName : aSubNames) + { + Sequence <OUString> aSubNodeNames = + lcl_CreateSubNames(sSubNodeName + rSubName); + Sequence< Any> aSubProps = GetProperties(aSubNodeNames); + const Any* pSubProps = aSubProps.getConstArray(); + + OUString sColumn; + pSubProps[0] >>= sColumn; + // check for existence of the loaded column name + bool bFound = false; + for(size_t nRealColumn = 0; nRealColumn < aDBColumns.size(); ++nRealColumn) + { + if(aDBColumns[nRealColumn]->sColumn == sColumn) + { + bFound = true; + break; + } + } + if(!bFound) + continue; + sal_Int16 nIndex = 0; + pSubProps[1] >>= nIndex; + std::unique_ptr<SwInsDBColumn> pInsDBColumn(new SwInsDBColumn(sColumn)); + if(pSubProps[2].hasValue()) + pInsDBColumn->bHasFormat = *o3tl::doAccess<bool>(pSubProps[2]); + if(pSubProps[3].hasValue()) + pInsDBColumn->bIsDBFormat = *o3tl::doAccess<bool>(pSubProps[3]); + + pSubProps[4] >>= pInsDBColumn->sUsrNumFormat; + OUString sNumberFormatLocale; + pSubProps[5] >>= sNumberFormatLocale; + + /* XXX Earlier versions wrote a Country-Language string in + * SwInsertDBColAutoPilot::Commit() that here was read as + * Language-Country with 2 characters copied to language, + * 1 character separator and unconditionally 2 characters read + * as country. So for 'DE-de' and locales that have similar + * case-insensitive equal language/country combos that may have + * worked, for all others not. FIXME if you need to read old + * data that you were never able to read before. */ + pInsDBColumn->eUsrNumFormatLng = LanguageTag::convertToLanguageType( sNumberFormatLocale ); + + pInsDBColumn->nUsrNumFormat = rNFormatr.GetEntryKey( pInsDBColumn->sUsrNumFormat, + pInsDBColumn->eUsrNumFormatLng ); + + pNewData->aDBColumns.insert(std::move(pInsDBColumn)); + } + OUString sTmp( pNewData->sTableList ); + if( !sTmp.isEmpty() ) + { + sal_Int32 n = 0; + do { + const OUString sEntry( sTmp.getToken( 0, '\x0a', n ) ); + //preselect column - if they still exist! + if (m_xLbTableDbColumn->find_text(sEntry) != -1) + { + m_xLbTableCol->append_text(sEntry); + m_xLbTableDbColumn->remove_text(sEntry); + } + } while( n>=0 ); + + if (!m_xLbTableDbColumn->n_children()) + { + m_xIbDbcolAllTo->set_sensitive( false ); + m_xIbDbcolOneTo->set_sensitive( false ); + } + m_xIbDbcolOneFrom->set_sensitive(true); + m_xIbDbcolAllFrom->set_sensitive(true); + } + m_xEdDbText->set_text( pNewData->sEdit ); + + sTmp = pNewData->sTmplNm; + if( !sTmp.isEmpty() ) + m_xLbDbParaColl->set_active_text(sTmp); + else + m_xLbDbParaColl->set_active(0); + + m_xTAutoFormat.reset(); + sTmp = pNewData->sTAutoFormatNm; + if( !sTmp.isEmpty() ) + { + // then load the AutoFormat file and look for Autoformat first + SwTableAutoFormatTable aAutoFormatTable; + aAutoFormatTable.Load(); + for( size_t nAutoFormat = aAutoFormatTable.size(); nAutoFormat; ) + if( sTmp == aAutoFormatTable[ --nAutoFormat ].GetName() ) + { + m_xTAutoFormat.reset(new SwTableAutoFormat(aAutoFormatTable[nAutoFormat])); + break; + } + } + + m_xRbAsTable->set_active( pNewData->bIsTable ); + m_xRbAsField->set_active( pNewData->bIsField ); + m_xRbAsText->set_active( !pNewData->bIsTable && !pNewData->bIsField ); + + m_xCbTableHeadon->set_active( pNewData->bIsHeadlineOn ); + m_xRbHeadlColnms->set_active( !pNewData->bIsEmptyHeadln ); + m_xRbHeadlEmpty->set_active( pNewData->bIsEmptyHeadln ); + HeaderHdl(*m_xCbTableHeadon); + + // now copy the user defined Numberformat strings to the + // Shell. Then only these are available as ID + for( size_t n = 0; n < aDBColumns.size() ; ++n ) + { + SwInsDBColumn& rSet = *aDBColumns[ n ]; + for( size_t m = 0; m < pNewData->aDBColumns.size() ; ++m ) + { + SwInsDBColumn& rGet = *pNewData->aDBColumns[ m ]; + if(rGet.sColumn == rSet.sColumn) + { + if( rGet.bHasFormat && !rGet.bIsDBFormat ) + { + rSet.bIsDBFormat = false; + rSet.nUsrNumFormat = rNFormatr.GetEntryKey( rGet.sUsrNumFormat, + rGet.eUsrNumFormatLng ); + if( NUMBERFORMAT_ENTRY_NOT_FOUND == rSet.nUsrNumFormat ) + { + sal_Int32 nCheckPos; + SvNumFormatType nType; + rNFormatr.PutEntry( rGet.sUsrNumFormat, nCheckPos, nType, + rSet.nUsrNumFormat, rGet.eUsrNumFormatLng ); + } + } + break; + } + } + } + + // when the cursor is inside of a table, table must NEVER be selectable + if( !m_xRbAsTable->get_sensitive() && m_xRbAsTable->get_active() ) + m_xRbAsField->set_active(true); + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/dbtablepreviewdialog.cxx b/sw/source/ui/dbui/dbtablepreviewdialog.cxx new file mode 100644 index 000000000..b8c755d83 --- /dev/null +++ b/sw/source/ui/dbui/dbtablepreviewdialog.cxx @@ -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 . + */ + +#include "dbtablepreviewdialog.hxx" +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/frame/Frame.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + +class DBTablePreviewFrame + : public cppu::WeakImplHelper<lang::XEventListener> +{ +private: + css::uno::Reference<css::frame::XFrame2> m_xFrame; + + virtual void SAL_CALL disposing(const lang::EventObject& /*Source*/) override + { + m_xFrame.clear(); + } + +public: + DBTablePreviewFrame(css::uno::Reference<css::frame::XFrame2>& rFrame) + : m_xFrame(rFrame) + { + } + + void cleanup() + { + if (m_xFrame.is()) + { + m_xFrame->setComponent(nullptr, nullptr); + m_xFrame->dispose(); + m_xFrame.clear(); + } + } +}; + +SwDBTablePreviewDialog::SwDBTablePreviewDialog(weld::Window* pParent, uno::Sequence< beans::PropertyValue> const & rValues) + : SfxDialogController(pParent, "modules/swriter/ui/tablepreviewdialog.ui", "TablePreviewDialog") + , m_xDescriptionFI(m_xBuilder->weld_label("description")) + , m_xBeamerWIN(m_xBuilder->weld_container("beamer")) +{ + Size aSize(m_xBeamerWIN->get_approximate_digit_width() * 80, + m_xBeamerWIN->get_text_height() * 18); + m_xBeamerWIN->set_size_request(aSize.Width(), aSize.Height()); + + auto pValue = std::find_if(rValues.begin(), rValues.end(), + [](const beans::PropertyValue& rValue) { return rValue.Name == "Command"; }); + if (pValue != rValues.end()) + { + OUString sDescription = m_xDescriptionFI->get_label(); + OUString sTemp; + pValue->Value >>= sTemp; + m_xDescriptionFI->set_label(sDescription.replaceFirst("%1", sTemp)); + } + + css::uno::Reference<css::frame::XFrame2> xFrame; + try + { + // create a frame wrapper for myself + xFrame = frame::Frame::create( comphelper::getProcessComponentContext() ); + xFrame->initialize(m_xBeamerWIN->CreateChildFrame()); + } + catch (uno::Exception const &) + { + xFrame.clear(); + } + if (xFrame.is()) + { + m_xFrameListener.set(new DBTablePreviewFrame(xFrame)); + xFrame->addEventListener(m_xFrameListener.get()); + + util::URL aURL; + aURL.Complete = ".component:DB/DataSourceBrowser"; + uno::Reference<frame::XDispatch> xD = xFrame->queryDispatch(aURL, "", 0x0C); + if (xD.is()) + { + xD->dispatch(aURL, rValues); + m_xBeamerWIN->show(); + } + } +} + +SwDBTablePreviewDialog::~SwDBTablePreviewDialog() +{ + if (m_xFrameListener) + { + m_xFrameListener->cleanup(); + m_xFrameListener.clear(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/dbtablepreviewdialog.hxx b/sw/source/ui/dbui/dbtablepreviewdialog.hxx new file mode 100644 index 000000000..99315fa01 --- /dev/null +++ b/sw/source/ui/dbui/dbtablepreviewdialog.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_UI_DBUI_DBTABLEPREVIEWDIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_DBTABLEPREVIEWDIALOG_HXX + +#include <sfx2/basedlgs.hxx> +#include <com/sun/star/uno/Sequence.h> + +namespace com::sun::star{ + namespace beans{ struct PropertyValue; } + namespace frame{ class XFrame2; } +} + +class DBTablePreviewFrame; + +class SwDBTablePreviewDialog : public SfxDialogController +{ + std::unique_ptr<weld::Label> m_xDescriptionFI; + std::unique_ptr<weld::Container> m_xBeamerWIN; + + rtl::Reference<DBTablePreviewFrame> m_xFrameListener; +public: + SwDBTablePreviewDialog(weld::Window* pParent, + css::uno::Sequence< css::beans::PropertyValue> const & rValues ); + virtual ~SwDBTablePreviewDialog() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mailmergewizard.cxx b/sw/source/ui/dbui/mailmergewizard.cxx new file mode 100644 index 000000000..bb10a2a9a --- /dev/null +++ b/sw/source/ui/dbui/mailmergewizard.cxx @@ -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 . + */ + +#include <mailmergewizard.hxx> +#include "mmdocselectpage.hxx" +#include "mmoutputtypepage.hxx" +#include "mmaddressblockpage.hxx" +#include "mmgreetingspage.hxx" +#include "mmlayoutpage.hxx" +#include <mmconfigitem.hxx> +#include <swabstdlg.hxx> +#include <strings.hrc> +#include <view.hxx> + +#include <helpids.h> + +using namespace svt; +using namespace ::com::sun::star; + +SwMailMergeWizard::SwMailMergeWizard(SwView& rView, std::shared_ptr<SwMailMergeConfigItem> const & rItem) + : RoadmapWizardMachine(rView.GetFrameWeld()) + , m_pSwView(&rView) + , m_bDocumentLoad(false) + , m_xConfigItem(rItem) + , m_sStarting(SwResId(ST_STARTING)) + , m_sDocumentType(SwResId(ST_DOCUMENTTYPE)) + , m_sAddressBlock(SwResId(ST_ADDRESSBLOCK)) + , m_sAddressList(SwResId(ST_ADDRESSLIST)) + , m_sGreetingsLine(SwResId(ST_GREETINGSLINE)) + , m_sLayout(SwResId(ST_LAYOUT)) + , m_nRestartPage(MM_DOCUMENTSELECTPAGE) +{ + defaultButton(WizardButtonFlags::NEXT); + enableButtons(WizardButtonFlags::FINISH, false); + + setTitleBase(SwResId(ST_MMWTITLE)); + + m_xFinish->set_label(SwResId( ST_FINISH )); + m_xNextPage->set_help_id(HID_MM_NEXT_PAGE); + m_xPrevPage->set_help_id(HID_MM_PREV_PAGE); + + //#i51949# no output type page visible if e-Mail is not supported + if (m_xConfigItem->IsMailAvailable()) + declarePath( + 0, + {MM_DOCUMENTSELECTPAGE, + MM_OUTPUTTYPETPAGE, + MM_ADDRESSBLOCKPAGE, + MM_GREETINGSPAGE, + MM_LAYOUTPAGE} + ); + else + declarePath( + 0, + {MM_DOCUMENTSELECTPAGE, + MM_ADDRESSBLOCKPAGE, + MM_GREETINGSPAGE, + MM_LAYOUTPAGE} + ); + + ActivatePage(); + m_xAssistant->set_current_page(0); + UpdateRoadmap(); +} + +SwMailMergeWizard::~SwMailMergeWizard() +{ +} + +std::unique_ptr<BuilderPage> SwMailMergeWizard::createPage(WizardState _nState) +{ + OString sIdent(OString::number(_nState)); + weld::Container* pPageContainer = m_xAssistant->append_page(sIdent); + + std::unique_ptr<vcl::OWizardPage> xRet; + switch(_nState) + { + case MM_DOCUMENTSELECTPAGE : + xRet = std::make_unique<SwMailMergeDocSelectPage>(pPageContainer, this); + + /* tdf#52986 Set help ID using SetRoadmapHelpId for all pages + so that when by default the focus is on the left side pane of + the wizard the relevant help page is displayed when hitting + the Help / F1 button */ + SetRoadmapHelpId("modules/swriter/ui/mmselectpage/MMSelectPage"); + break; + case MM_OUTPUTTYPETPAGE : + xRet = std::make_unique<SwMailMergeOutputTypePage>(pPageContainer, this); + SetRoadmapHelpId("modules/swriter/ui/mmoutputtypepage/MMOutputTypePage"); + break; + case MM_ADDRESSBLOCKPAGE : + xRet = std::make_unique<SwMailMergeAddressBlockPage>(pPageContainer, this); + SetRoadmapHelpId("modules/swriter/ui/mmaddressblockpage/MMAddressBlockPage"); + break; + case MM_GREETINGSPAGE : + xRet = std::make_unique<SwMailMergeGreetingsPage>(pPageContainer, this); + SetRoadmapHelpId("modules/swriter/ui/mmsalutationpage/MMSalutationPage"); + break; + case MM_LAYOUTPAGE : + xRet = std::make_unique<SwMailMergeLayoutPage>(pPageContainer, this); + SetRoadmapHelpId("modules/swriter/ui/mmlayoutpage/MMLayoutPage"); + break; + } + + m_xAssistant->set_page_title(sIdent, getStateDisplayName(_nState)); + + OSL_ENSURE(xRet, "no page created in ::createPage"); + return xRet; +} + +void SwMailMergeWizard::enterState( WizardState _nState ) +{ + ::vcl::RoadmapWizardMachine::enterState( _nState ); + + if (m_xConfigItem->GetTargetView()) + { + //close the dialog, remove the target view, show the source view + m_nRestartPage = _nState; + //set ResultSet back to start + m_xConfigItem->MoveResultSet(1); + m_xAssistant->response(RET_REMOVE_TARGET); + return; + } + bool bEnablePrev = true; + bool bEnableNext = true; + switch(_nState) + { + case MM_DOCUMENTSELECTPAGE: + bEnablePrev = false; // the first page + break; + case MM_ADDRESSBLOCKPAGE : + bEnableNext = m_xConfigItem->GetResultSet().is(); + break; + case MM_LAYOUTPAGE: + bEnableNext = false; // the last page + break; + } + enableButtons( WizardButtonFlags::PREVIOUS, bEnablePrev); + enableButtons( WizardButtonFlags::NEXT, bEnableNext); + + UpdateRoadmap(); +} + +OUString SwMailMergeWizard::getStateDisplayName( WizardState _nState ) const +{ + switch(_nState) + { + case MM_DOCUMENTSELECTPAGE: + return m_sStarting; + case MM_OUTPUTTYPETPAGE: + return m_sDocumentType; + case MM_ADDRESSBLOCKPAGE: + return m_xConfigItem->IsOutputToLetter() ? + m_sAddressBlock : m_sAddressList; + case MM_GREETINGSPAGE: + return m_sGreetingsLine; + case MM_LAYOUTPAGE: + return m_sLayout; + } + return OUString(); +} + +// enables/disables pages in the roadmap depending on the current page and state +void SwMailMergeWizard::UpdateRoadmap() +{ +/* + MM_DOCUMENTSELECTPAGE > inactive after the layoutpage + MM_OUTPUTTYPETPAGE : > inactive after the layoutpage + MM_ADDRESSBLOCKPAGE > inactive after the layoutpage + MM_GREETINGSPAGE > inactive after the layoutpage + MM_LAYOUTPAGE > inactive after the layoutpage + inactive if address block and greeting are switched off + or are already inserted into the source document +*/ + + // enableState( <page id>, false ); + const sal_uInt16 nCurPage = m_xAssistant->get_current_page(); + BuilderPage* pCurPage = GetPage( nCurPage ); + if(!pCurPage) + return; + bool bAddressFieldsConfigured = !m_xConfigItem->IsOutputToLetter() || + !m_xConfigItem->IsAddressBlock() || + m_xConfigItem->IsAddressFieldsAssigned(); + bool bGreetingFieldsConfigured = !m_xConfigItem->IsGreetingLine(false) || + !m_xConfigItem->IsIndividualGreeting(false) || + m_xConfigItem->IsGreetingFieldsAssigned(); + + //#i97436# if a document has to be loaded then enable output type page only + m_bDocumentLoad = false; + bool bEnableOutputTypePage = (nCurPage != MM_DOCUMENTSELECTPAGE) || + static_cast<vcl::OWizardPage*>(pCurPage)->commitPage( ::vcl::WizardTypes::eValidate ); + + // handle the Finish button + bool bCanFinish = !m_bDocumentLoad && bEnableOutputTypePage && + m_xConfigItem->GetResultSet().is() && + bAddressFieldsConfigured && + bGreetingFieldsConfigured; + enableButtons(WizardButtonFlags::FINISH, (nCurPage != MM_DOCUMENTSELECTPAGE) && bCanFinish); + + for(sal_uInt16 nPage = MM_DOCUMENTSELECTPAGE; nPage <= MM_LAYOUTPAGE; ++nPage) + { + bool bEnable = true; + switch(nPage) + { + case MM_DOCUMENTSELECTPAGE: + bEnable = true; + break; + case MM_OUTPUTTYPETPAGE: + bEnable = bEnableOutputTypePage; + break; + case MM_ADDRESSBLOCKPAGE: + bEnable = !m_bDocumentLoad && bEnableOutputTypePage; + // update page title for email vs letter + m_xAssistant->set_page_title(OString::number(MM_ADDRESSBLOCKPAGE), getStateDisplayName(MM_ADDRESSBLOCKPAGE)); + break; + case MM_GREETINGSPAGE: + bEnable = !m_bDocumentLoad && bEnableOutputTypePage && + m_xConfigItem->GetResultSet().is() && + bAddressFieldsConfigured; + break; + case MM_LAYOUTPAGE: + bEnable = bCanFinish && + ((m_xConfigItem->IsAddressBlock() && !m_xConfigItem->IsAddressInserted()) || + (m_xConfigItem->IsGreetingLine(false) && !m_xConfigItem->IsGreetingInserted() )); + break; + } + enableState( nPage, bEnable ); + } +} + +short SwMailMergeWizard::run() +{ + OSL_FAIL("SwMailMergeWizard cannot be executed via Dialog::Execute!\n" + "It creates a thread (MailDispatcher instance) that will call" + "back to VCL apartment => deadlock!\n" + "Use Dialog::StartExecuteAsync to execute the dialog!" ); + return RET_CANCEL; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmaddressblockpage.cxx b/sw/source/ui/dbui/mmaddressblockpage.cxx new file mode 100644 index 000000000..7c676277c --- /dev/null +++ b/sw/source/ui/dbui/mmaddressblockpage.cxx @@ -0,0 +1,1568 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "mmaddressblockpage.hxx" +#include <mailmergewizard.hxx> +#include <swtypes.hxx> +#include "addresslistdialog.hxx" +#include <editeng/eeitem.hxx> +#include <o3tl/safeint.hxx> +#include <svl/grabbagitem.hxx> +#include <svl/itemset.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/transfer.hxx> +#include <mmconfigitem.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/string.hxx> +#include <tools/diagnose_ex.h> + +#include <vector> +#include <strings.hrc> +#include <mmaddressblockpage.hrc> +#include <helpids.h> + +using namespace svt; +using namespace ::com::sun::star; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; + +SwMailMergeAddressBlockPage::SwMailMergeAddressBlockPage(weld::Container* pPage, SwMailMergeWizard* pWizard) + : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmaddressblockpage.ui", "MMAddressBlockPage") + , m_pWizard(pWizard) + , m_xAddressListPB(m_xBuilder->weld_button("addresslist")) + , m_xCurrentAddressFI(m_xBuilder->weld_label("currentaddress")) + , m_xStep2(m_xBuilder->weld_container("step2")) + , m_xStep3(m_xBuilder->weld_container("step3")) + , m_xStep4(m_xBuilder->weld_container("step4")) + , m_xSettingsFI(m_xBuilder->weld_label("settingsft")) + , m_xAddressCB(m_xBuilder->weld_check_button("address")) + , m_xSettingsPB(m_xBuilder->weld_button("settings")) + , m_xHideEmptyParagraphsCB(m_xBuilder->weld_check_button("hideempty")) + , m_xAssignPB(m_xBuilder->weld_button("assign")) + , m_xDocumentIndexFI(m_xBuilder->weld_label("documentindex")) + , m_xPrevSetIB(m_xBuilder->weld_button("prev")) + , m_xNextSetIB(m_xBuilder->weld_button("next")) + , m_xDifferentlist(m_xBuilder->weld_button("differentlist")) + , m_xSettings(new SwAddressPreview(m_xBuilder->weld_scrolled_window("settingspreviewwin"))) + , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("addresspreviewwin"))) + , m_xSettingsWIN(new weld::CustomWeld(*m_xBuilder, "settingspreview", *m_xSettings)) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "addresspreview", *m_xPreview)) +{ + m_xSettingsWIN->set_size_request(m_xDifferentlist->get_approximate_digit_width() * 40, + m_xDifferentlist->get_text_height() * 6); + m_xPreviewWIN->set_size_request(m_xDifferentlist->get_approximate_digit_width() * 44, + m_xDifferentlist->get_text_height() * 6); + m_sChangeAddress = m_xDifferentlist->get_label(); + m_sDocument = m_xDocumentIndexFI->get_label(); + + m_sCurrentAddress = m_xCurrentAddressFI->get_label(); + m_xAddressListPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, AddressListHdl_Impl)); + m_xSettingsPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, SettingsHdl_Impl)); + m_xAssignPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, AssignHdl_Impl )); + m_xAddressCB->connect_toggled(LINK(this, SwMailMergeAddressBlockPage, AddressBlockHdl_Impl)); + m_xSettings->SetSelectHdl(LINK(this, SwMailMergeAddressBlockPage, AddressBlockSelectHdl_Impl)); + m_xHideEmptyParagraphsCB->connect_toggled(LINK(this, SwMailMergeAddressBlockPage, HideParagraphsHdl_Impl)); + + Link<weld::Button&,void> aLink = LINK(this, SwMailMergeAddressBlockPage, InsertDataHdl_Impl); + m_xPrevSetIB->connect_clicked(aLink); + m_xNextSetIB->connect_clicked(aLink); + + // lock in preferred size including current address line + Size aSize1(m_xContainer->get_preferred_size()); + + OUString sOrigLabel = m_xAddressListPB->get_label(); + m_xAddressListPB->set_label(m_sChangeAddress); + Size aSize2(m_xContainer->get_preferred_size()); + m_xAddressListPB->set_label(sOrigLabel); + + m_xCurrentAddressFI->hide(); + + m_xContainer->set_size_request(std::max(aSize1.Width(), aSize2.Width()), + std::max(aSize1.Height(), aSize2.Height())); +} + +SwMailMergeAddressBlockPage::~SwMailMergeAddressBlockPage() +{ + m_xPreviewWIN.reset(); + m_xSettingsWIN.reset(); + m_xPreview.reset(); + m_xSettings.reset(); +} + +bool SwMailMergeAddressBlockPage::canAdvance() const +{ + return m_pWizard->GetConfigItem().GetResultSet().is(); +} + +void SwMailMergeAddressBlockPage::Activate() +{ + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + bool bIsLetter = rConfigItem.IsOutputToLetter(); + + //no address block is created for e-Mail + m_xStep2->set_visible(bIsLetter); + m_xStep3->set_visible(bIsLetter); + m_xStep4->set_visible(bIsLetter); + + if (bIsLetter) + { + m_xHideEmptyParagraphsCB->set_active( rConfigItem.IsHideEmptyParagraphs() ); + m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", "1")); + + m_xSettings->Clear(); + const uno::Sequence< OUString> aBlocks = + m_pWizard->GetConfigItem().GetAddressBlocks(); + for(const auto& rAddress : aBlocks) + m_xSettings->AddAddress(rAddress); + m_xSettings->SelectAddress(static_cast<sal_uInt16>(rConfigItem.GetCurrentAddressBlockIndex())); + m_xAddressCB->set_active(rConfigItem.IsAddressBlock()); + AddressBlockHdl_Impl(*m_xAddressCB); + m_xSettings->SetLayout(1, 2); + InsertDataHdl(nullptr); + } +} + +bool SwMailMergeAddressBlockPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) +{ + return ::vcl::WizardTypes::eTravelForward != _eReason || m_pWizard->GetConfigItem().GetResultSet().is(); +} + +IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AddressListHdl_Impl, weld::Button&, void) +{ + try + { + SwAddressListDialog aAddrDialog(this); + if (RET_OK == aAddrDialog.run()) + { + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + rConfigItem.SetCurrentConnection( + aAddrDialog.GetSource(), + aAddrDialog.GetConnection(), + aAddrDialog.GetColumnsSupplier(), + aAddrDialog.GetDBData()); + OUString sFilter = aAddrDialog.GetFilter(); + rConfigItem.SetFilter( sFilter ); + InsertDataHdl(nullptr); + GetWizard()->UpdateRoadmap(); + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); + } + } + catch (const uno::Exception& e) + { + TOOLS_WARN_EXCEPTION("sw", ""); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pWizard->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, e.Message)); + xBox->run(); + } +} + +IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, SettingsHdl_Impl, weld::Button&, void) +{ + SwSelectAddressBlockDialog aDlg(m_pWizard->getDialog(), m_pWizard->GetConfigItem()); + SwMailMergeConfigItem& rConfig = m_pWizard->GetConfigItem(); + aDlg.SetAddressBlocks(rConfig.GetAddressBlocks(), m_xSettings->GetSelectedAddress()); + aDlg.SetSettings(rConfig.IsIncludeCountry(), rConfig.GetExcludeCountry()); + if (aDlg.run() == RET_OK) + { + //the dialog provides the selected address at the first position! + const uno::Sequence< OUString> aBlocks = aDlg.GetAddressBlocks(); + rConfig.SetAddressBlocks(aBlocks); + m_xSettings->Clear(); + for(const auto& rAddress : aBlocks) + m_xSettings->AddAddress(rAddress); + m_xSettings->SelectAddress(0); + m_xSettings->Invalidate(); // #i40408 + rConfig.SetCountrySettings(aDlg.IsIncludeCountry(), aDlg.GetCountry()); + InsertDataHdl(nullptr); + } + GetWizard()->UpdateRoadmap(); + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); +} + +IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AssignHdl_Impl, weld::Button&, void) +{ + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + const sal_uInt16 nSel = m_xSettings->GetSelectedAddress(); + const uno::Sequence< OUString> aBlocks = rConfigItem.GetAddressBlocks(); + SwAssignFieldsDialog aDlg(m_pWizard->getDialog(), m_pWizard->GetConfigItem(), aBlocks[nSel], true); + if(RET_OK == aDlg.run()) + { + //preview update + InsertDataHdl(nullptr); + GetWizard()->UpdateRoadmap(); + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); + } +} + +void SwMailMergeAddressBlockPage::EnableAddressBlock(bool bAll, bool bSelective) +{ + m_xSettingsFI->set_sensitive(bAll); + m_xAddressCB->set_sensitive(bAll); + bSelective &= bAll; + m_xHideEmptyParagraphsCB->set_sensitive(bSelective); + m_xSettingsWIN->set_sensitive(bSelective); + m_xSettingsPB->set_sensitive(bSelective); + m_xStep3->set_sensitive(bSelective); + m_xStep4->set_sensitive(bSelective); +} + +IMPL_LINK(SwMailMergeAddressBlockPage, AddressBlockHdl_Impl, weld::ToggleButton&, rBox, void) +{ + EnableAddressBlock(rBox.get_sensitive(), rBox.get_active()); + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + rConfigItem.SetAddressBlock(m_xAddressCB->get_active()); + m_pWizard->UpdateRoadmap(); + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); +} + +IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AddressBlockSelectHdl_Impl, LinkParamNone*, void) +{ + const sal_uInt16 nSel = m_xSettings->GetSelectedAddress(); + const uno::Sequence< OUString> aBlocks = + m_pWizard->GetConfigItem().GetAddressBlocks(); + m_xPreview->SetAddress(SwAddressPreview::FillData(aBlocks[nSel], + m_pWizard->GetConfigItem())); + m_pWizard->GetConfigItem().SetCurrentAddressBlockIndex( nSel ); + GetWizard()->UpdateRoadmap(); + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); +} + +IMPL_LINK(SwMailMergeAddressBlockPage, HideParagraphsHdl_Impl, weld::ToggleButton&, rBox, void) +{ + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + rConfigItem.SetHideEmptyParagraphs(rBox.get_active()); +} + +void SwMailMergeAddressBlockPage::InsertDataHdl(const weld::Button* pButton) +{ + //if no pButton is given, the first set has to be pre-set + SwMailMergeConfigItem& rConfig = m_pWizard->GetConfigItem(); + std::unique_ptr<weld::WaitObject> xWaitObj(new weld::WaitObject(m_pWizard->getDialog())); + if(!pButton) + { + rConfig.GetResultSet(); + } + else + { + bool bNext = pButton == m_xNextSetIB.get(); + sal_Int32 nPos = rConfig.GetResultSetPosition(); + rConfig.MoveResultSet( bNext ? ++nPos : --nPos); + } + xWaitObj.reset(); + sal_Int32 nPos = rConfig.GetResultSetPosition(); + bool bEnable = true; + if(nPos < 1) + { + bEnable = false; + nPos = 1; + } + else + { + //if output type is letter + if (m_xSettings->IsVisible()) + { + //Fill data into preview + const sal_uInt16 nSel = m_xSettings->GetSelectedAddress(); + const uno::Sequence< OUString> aBlocks = + m_pWizard->GetConfigItem().GetAddressBlocks(); + m_xPreview->SetAddress(SwAddressPreview::FillData(aBlocks[nSel], rConfig)); + } + } + m_xPrevSetIB->set_sensitive(bEnable); + m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", OUString::number(nPos))); + + GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE)); + bool bHasResultSet = rConfig.GetResultSet().is(); + m_xCurrentAddressFI->set_visible(bHasResultSet); + if(bHasResultSet) + { + m_xCurrentAddressFI->set_label(m_sCurrentAddress.replaceFirst("%1", rConfig.GetCurrentDBData().sDataSource)); + m_xAddressListPB->set_label(m_sChangeAddress); + } + EnableAddressBlock(bHasResultSet, m_xAddressCB->get_active()); +} + +IMPL_LINK(SwMailMergeAddressBlockPage, InsertDataHdl_Impl, weld::Button&, rButton, void) +{ + InsertDataHdl(&rButton); +} + +SwSelectAddressBlockDialog::SwSelectAddressBlockDialog(weld::Window* pParent, SwMailMergeConfigItem& rConfig) + : SfxDialogController(pParent, "modules/swriter/ui/selectblockdialog.ui", "SelectBlockDialog") + , m_rConfig(rConfig) + , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin"))) + , m_xNewPB(m_xBuilder->weld_button("new")) + , m_xCustomizePB(m_xBuilder->weld_button("edit")) + , m_xDeletePB(m_xBuilder->weld_button("delete")) + , m_xNeverRB(m_xBuilder->weld_radio_button("never")) + , m_xAlwaysRB(m_xBuilder->weld_radio_button("always")) + , m_xDependentRB(m_xBuilder->weld_radio_button("dependent")) + , m_xCountryED(m_xBuilder->weld_entry("country")) + , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreview)) +{ + m_xPreviewWin->set_size_request(m_xCountryED->get_approximate_digit_width() * 45, + m_xCountryED->get_text_height() * 12); + + Link<weld::Button&,void> aCustomizeHdl = LINK(this, SwSelectAddressBlockDialog, NewCustomizeHdl_Impl); + m_xNewPB->connect_clicked(aCustomizeHdl); + m_xCustomizePB->connect_clicked(aCustomizeHdl); + + m_xDeletePB->connect_clicked(LINK(this, SwSelectAddressBlockDialog, DeleteHdl_Impl)); + + Link<weld::ToggleButton&,void> aLk = LINK(this, SwSelectAddressBlockDialog, IncludeHdl_Impl); + m_xNeverRB->connect_toggled(aLk); + m_xAlwaysRB->connect_toggled(aLk); + m_xDependentRB->connect_toggled(aLk); + m_xPreview->SetLayout(2, 2); + m_xPreview->EnableScrollBar(); +} + +SwSelectAddressBlockDialog::~SwSelectAddressBlockDialog() +{ +} + +void SwSelectAddressBlockDialog::SetAddressBlocks(const uno::Sequence< OUString>& rBlocks, + sal_uInt16 nSelectedAddress) +{ + m_aAddressBlocks = rBlocks; + for (const auto& rAddressBlock : std::as_const(m_aAddressBlocks)) + m_xPreview->AddAddress(rAddressBlock); + m_xPreview->SelectAddress(nSelectedAddress); +} + +// return the address blocks and put the selected one to the first position +const uno::Sequence< OUString >& SwSelectAddressBlockDialog::GetAddressBlocks() +{ + //put the selected block to the first position + const sal_Int32 nSelect = static_cast<sal_Int32>(m_xPreview->GetSelectedAddress()); + if(nSelect) + { + uno::Sequence< OUString >aTemp = m_aAddressBlocks; + aTemp[0] = m_aAddressBlocks[nSelect]; + std::copy(m_aAddressBlocks.begin(), std::next(m_aAddressBlocks.begin(), nSelect), std::next(aTemp.begin())); + std::copy(std::next(m_aAddressBlocks.begin(), nSelect + 1), m_aAddressBlocks.end(), std::next(aTemp.begin(), nSelect + 1)); + m_aAddressBlocks = aTemp; + } + return m_aAddressBlocks; +} + +void SwSelectAddressBlockDialog::SetSettings( + bool bIsCountry, const OUString& rCountry) +{ + weld::RadioButton *pActive = m_xNeverRB.get(); + if(bIsCountry) + { + pActive = !rCountry.isEmpty() ? m_xDependentRB.get() : m_xAlwaysRB.get(); + m_xCountryED->set_text(rCountry); + } + pActive->set_active(true); + IncludeHdl_Impl(*pActive); + m_xDeletePB->set_sensitive(m_aAddressBlocks.getLength() > 1); +} + +OUString SwSelectAddressBlockDialog::GetCountry() const +{ + if (m_xDependentRB->get_active()) + return m_xCountryED->get_text(); + return OUString(); +} + +IMPL_LINK(SwSelectAddressBlockDialog, DeleteHdl_Impl, weld::Button&, rButton, void) +{ + if (m_aAddressBlocks.getLength()) + { + const sal_Int32 nSelected = static_cast<sal_Int32>(m_xPreview->GetSelectedAddress()); + comphelper::removeElementAt(m_aAddressBlocks, nSelected); + if (m_aAddressBlocks.getLength() <= 1) + rButton.set_sensitive(false); + m_xPreview->RemoveSelectedAddress(); + } +} + +IMPL_LINK(SwSelectAddressBlockDialog, NewCustomizeHdl_Impl, weld::Button&, rButton, void) +{ + bool bCustomize = &rButton == m_xCustomizePB.get(); + SwCustomizeAddressBlockDialog::DialogType nType = bCustomize ? + SwCustomizeAddressBlockDialog::ADDRESSBLOCK_EDIT : + SwCustomizeAddressBlockDialog::ADDRESSBLOCK_NEW; + std::unique_ptr<SwCustomizeAddressBlockDialog> xDlg(new SwCustomizeAddressBlockDialog(&rButton, + m_rConfig, nType)); + if(bCustomize) + { + xDlg->SetAddress(m_aAddressBlocks[m_xPreview->GetSelectedAddress()]); + } + if (RET_OK == xDlg->run()) + { + const OUString sNew = xDlg->GetAddress(); + if(bCustomize) + { + m_xPreview->ReplaceSelectedAddress(sNew); + m_aAddressBlocks[m_xPreview->GetSelectedAddress()] = sNew; + } + else + { + m_xPreview->AddAddress(sNew); + m_aAddressBlocks.realloc(m_aAddressBlocks.getLength() + 1); + const sal_Int32 nSelect = m_aAddressBlocks.getLength() - 1; + m_aAddressBlocks[nSelect] = sNew; + m_xPreview->SelectAddress(static_cast<sal_uInt16>(nSelect)); + } + m_xDeletePB->set_sensitive(m_aAddressBlocks.getLength() > 1); + } +} + +IMPL_LINK_NOARG(SwSelectAddressBlockDialog, IncludeHdl_Impl, weld::ToggleButton&, void) +{ + m_xCountryED->set_sensitive(m_xDependentRB->get_active()); +} + +#define USER_DATA_SALUTATION -1 +#define USER_DATA_PUNCTUATION -2 +#define USER_DATA_TEXT -3 +#define USER_DATA_NONE -4 + +IMPL_LINK(SwCustomizeAddressBlockDialog, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aTextFilter.filter(rTest); + return true; +} + +SwCustomizeAddressBlockDialog::SwCustomizeAddressBlockDialog( + weld::Widget* pParent, SwMailMergeConfigItem& rConfig, DialogType eType) + : SfxDialogController(pParent, "modules/swriter/ui/addressblockdialog.ui", + "AddressBlockDialog") + , m_aTextFilter("<>") + , m_rConfigItem(rConfig) + , m_eType(eType) + , m_xAddressElementsFT(m_xBuilder->weld_label("addressesft")) + , m_xAddressElementsLB(m_xBuilder->weld_tree_view("addresses")) + , m_xInsertFieldIB(m_xBuilder->weld_button("toaddr")) + , m_xRemoveFieldIB(m_xBuilder->weld_button("fromaddr")) + , m_xDragFT(m_xBuilder->weld_label("addressdestft")) + , m_xUpIB(m_xBuilder->weld_button("up")) + , m_xLeftIB(m_xBuilder->weld_button("left")) + , m_xRightIB(m_xBuilder->weld_button("right")) + , m_xDownIB(m_xBuilder->weld_button("down")) + , m_xFieldFT(m_xBuilder->weld_label("customft")) + , m_xFieldCB(m_xBuilder->weld_combo_box("custom")) + , m_xOK(m_xBuilder->weld_button("ok")) + , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin"))) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "addrpreview", *m_xPreview)) + , m_xDragED(new AddressMultiLineEdit(this)) + , m_xDragWIN(new weld::CustomWeld(*m_xBuilder, "addressdest", *m_xDragED)) +{ + m_aSelectionChangedIdle.SetInvokeHandler( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedIdleHdl ) ); + + Size aSize(m_xDragED->GetDrawingArea()->get_size_request()); + m_xPreview->set_size_request(aSize.Width(), aSize.Height()); + + m_xFieldCB->connect_entry_insert_text(LINK(this, SwCustomizeAddressBlockDialog, TextFilterHdl)); + m_xAddressElementsLB->set_size_request(-1, m_xAddressElementsLB->get_height_rows(16)); + + if( eType >= GREETING_FEMALE ) + { + m_xFieldFT->show(); + m_xFieldCB->show(); + m_xAddressElementsLB->append(OUString::number(USER_DATA_SALUTATION), SwResId(ST_SALUTATION)); + m_xAddressElementsLB->append(OUString::number(USER_DATA_PUNCTUATION), SwResId(ST_PUNCTUATION)); + m_xAddressElementsLB->append(OUString::number(USER_DATA_TEXT), SwResId(ST_TEXT)); + for (size_t i = 0; i < SAL_N_ELEMENTS(RA_SALUTATION); ++i) + m_aSalutations.push_back(SwResId(RA_SALUTATION[i])); + for (size_t i = 0; i < SAL_N_ELEMENTS(RA_PUNCTUATION); ++i) + m_aPunctuations.push_back(SwResId(RA_PUNCTUATION[i])); + m_xDragED->SetText(" "); + m_xDialog->set_title(SwResId(eType == GREETING_MALE ? ST_TITLE_MALE : ST_TITLE_FEMALE)); + m_xAddressElementsFT->set_label(SwResId(ST_SALUTATIONELEMENTS)); + m_xInsertFieldIB->set_tooltip_text(SwResId(ST_INSERTSALUTATIONFIELD)); + m_xRemoveFieldIB->set_tooltip_text(SwResId(ST_REMOVESALUTATIONFIELD)); + m_xDragFT->set_label(SwResId(ST_DRAGSALUTATION)); + } + else + { + if (eType == ADDRESSBLOCK_EDIT) + m_xDialog->set_title(SwResId(ST_TITLE_EDIT)); + m_xDragED->SetText("\n\n\n\n\n"); + /* Set custom HIDs for swriter/01/mm_newaddblo.xhp */ + m_xAddressElementsLB->set_help_id( HID_MM_ADDBLOCK_ELEMENTS ); + m_xInsertFieldIB->set_help_id( HID_MM_ADDBLOCK_INSERT ); + m_xRemoveFieldIB->set_help_id( HID_MM_ADDBLOCK_REMOVE ); + m_xDragWIN->set_help_id( HID_MM_ADDBLOCK_DRAG ); + m_xPreviewWIN->set_help_id( HID_MM_ADDBLOCK_PREVIEW ); + m_xRightIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS ); + m_xLeftIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS ); + m_xDownIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS ); + m_xUpIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS ); + } + + const std::vector<std::pair<OUString, int>>& rHeaders = m_rConfigItem.GetDefaultAddressHeaders(); + for (size_t i = 0; i < rHeaders.size(); ++i) + m_xAddressElementsLB->append(OUString::number(i), rHeaders[i].first); + m_xOK->connect_clicked(LINK(this, SwCustomizeAddressBlockDialog, OKHdl_Impl)); + m_xAddressElementsLB->connect_changed(LINK(this, SwCustomizeAddressBlockDialog, ListBoxSelectHdl_Impl)); + if (m_xAddressElementsLB->n_children()) + m_xAddressElementsLB->select(0); + m_xDragED->SetModifyHdl(LINK(this, SwCustomizeAddressBlockDialog, EditModifyHdl_Impl)); + m_xDragED->SetSelectionChangedHdl( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl)); + m_xFieldCB->connect_changed(LINK(this, SwCustomizeAddressBlockDialog, FieldChangeComboBoxHdl_Impl)); + Link<weld::Button&,void> aImgButtonHdl = LINK(this, SwCustomizeAddressBlockDialog, ImageButtonHdl_Impl); + m_xInsertFieldIB->connect_clicked(aImgButtonHdl); + m_xRemoveFieldIB->connect_clicked(aImgButtonHdl); + m_xUpIB->connect_clicked(aImgButtonHdl); + m_xLeftIB->connect_clicked(aImgButtonHdl); + m_xRightIB->connect_clicked(aImgButtonHdl); + m_xDownIB->connect_clicked(aImgButtonHdl); + UpdateImageButtons_Impl(); +} + +bool SwCustomizeAddressBlockDialog::SetCursorLogicPosition(const Point& rPosition) +{ + return m_xDragED->SetCursorLogicPosition(rPosition); +} + +void SwCustomizeAddressBlockDialog::UpdateFields() +{ + m_xDragED->UpdateFields(); +} + +SwCustomizeAddressBlockDialog::~SwCustomizeAddressBlockDialog() +{ + m_xDragED->EndDropTarget(); +} + +IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, OKHdl_Impl, weld::Button&, void) +{ + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwCustomizeAddressBlockDialog, ListBoxSelectHdl_Impl, weld::TreeView&, rBox, void) +{ + sal_Int32 nUserData = rBox.get_selected_id().toInt32(); + // Check if the selected entry is already in the address and then forbid inserting + m_xInsertFieldIB->set_sensitive(nUserData >= 0 || !HasItem(nUserData)); +} + +IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, EditModifyHdl_Impl, AddressMultiLineEdit&, void) +{ + m_xPreview->SetAddress(SwAddressPreview::FillData(GetAddress(), m_rConfigItem)); + UpdateImageButtons_Impl(); +} + +IMPL_LINK(SwCustomizeAddressBlockDialog, ImageButtonHdl_Impl, weld::Button&, rButton, void) +{ + if (m_xInsertFieldIB.get() == &rButton) + { + int nEntry = m_xAddressElementsLB->get_selected_index(); + if (nEntry != -1) + { + m_xDragED->InsertNewEntry("<" + m_xAddressElementsLB->get_text(nEntry) + ">"); + } + } + else if (m_xRemoveFieldIB.get() == &rButton) + { + m_xDragED->RemoveCurrentEntry(); + } + else + { + MoveItemFlags nMove = MoveItemFlags::Down; + if (m_xUpIB.get() == &rButton) + nMove = MoveItemFlags::Up; + else if (m_xLeftIB.get() == &rButton) + nMove = MoveItemFlags::Left; + else if (m_xRightIB.get() == &rButton) + nMove = MoveItemFlags::Right; + m_xDragED->MoveCurrentItem(nMove); + } + UpdateImageButtons_Impl(); +} + +sal_Int32 SwCustomizeAddressBlockDialog::GetSelectedItem_Impl() const +{ + sal_Int32 nRet = USER_DATA_NONE; + const OUString sSelected = m_xDragED->GetCurrentItem(); + if(!sSelected.isEmpty()) + { + for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i) + { + const OUString sEntry = m_xAddressElementsLB->get_text(i); + if( sEntry == sSelected.copy( 1, sSelected.getLength() - 2 ) ) + { + nRet = m_xAddressElementsLB->get_id(i).toInt32(); + break; + } + } + } + return nRet; +} + +bool SwCustomizeAddressBlockDialog::HasItem(sal_Int32 nUserData) +{ + //get the entry from the ListBox + OUString sEntry; + for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i) + { + if (m_xAddressElementsLB->get_id(i).toInt32() == nUserData) + { + sEntry = m_xAddressElementsLB->get_text(i); + break; + } + } + //search for this entry in the content + return m_xDragED->GetText().indexOf("<" + sEntry + ">") >= 0; +} + +IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, SelectionChangedIdleHdl, Timer*, void) +{ + // called in case the selection of the edit field changes. + // determine selection - if it's one of the editable fields then + // enable the related ComboBox and fill it + + // don't trigger outself again + m_xDragED->SetSelectionChangedHdl(Link<bool, void>()); + + sal_Int32 nSelected = GetSelectedItem_Impl(); + if (USER_DATA_NONE != nSelected) + m_xDragED->SelectCurrentItem(); + + if(m_xFieldCB->get_visible() && (USER_DATA_NONE != nSelected) && (nSelected < 0)) + { + //search in ListBox if it's one of the first entries + OUString sSelect; + std::vector<OUString>* pVector = nullptr; + switch(nSelected) { + case USER_DATA_SALUTATION: + sSelect = m_sCurrentSalutation; + pVector = &m_aSalutations; + break; + case USER_DATA_PUNCTUATION: + sSelect = m_sCurrentPunctuation; + pVector = &m_aPunctuations; + break; + case USER_DATA_TEXT: + sSelect = m_sCurrentText; + break; + } + m_xFieldCB->clear(); + if(pVector) { + for (const auto& rItem : *pVector) + m_xFieldCB->append_text(rItem); + } + m_xFieldCB->set_entry_text(sSelect); + m_xFieldCB->set_sensitive(true); + m_xFieldFT->set_sensitive(true); + } + else + { + m_xFieldCB->set_sensitive(false); + m_xFieldFT->set_sensitive(false); + } + + UpdateImageButtons_Impl(); + m_xDragED->SetSelectionChangedHdl( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl)); +} + +IMPL_LINK(SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl, bool, bIdle, void) +{ + if (bIdle) + m_aSelectionChangedIdle.Start(); + else + { + m_aSelectionChangedIdle.Stop(); + SelectionChangedIdleHdl(nullptr); + } +} + +IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, FieldChangeComboBoxHdl_Impl, weld::ComboBox&, void) +{ + //changing the field content changes the related members, too + sal_Int32 nSelected = GetSelectedItem_Impl(); + const OUString sContent = m_xFieldCB->get_active_text(); + switch(nSelected) { + case USER_DATA_SALUTATION: + m_sCurrentSalutation = sContent; + break; + case USER_DATA_PUNCTUATION: + m_sCurrentPunctuation = sContent; + break; + case USER_DATA_TEXT: + m_sCurrentText = sContent; + break; + } + UpdateImageButtons_Impl(); + m_xPreview->SetAddress(GetAddress()); + EditModifyHdl_Impl(*m_xDragED); +} + +void SwCustomizeAddressBlockDialog::UpdateImageButtons_Impl() +{ + MoveItemFlags nMove = m_xDragED->IsCurrentItemMoveable(); + m_xUpIB->set_sensitive( bool(nMove & MoveItemFlags::Up) ); + m_xLeftIB->set_sensitive( bool(nMove & MoveItemFlags::Left) ); + m_xRightIB->set_sensitive( bool(nMove & MoveItemFlags::Right) ); + m_xDownIB->set_sensitive( bool(nMove & MoveItemFlags::Down) ); + m_xRemoveFieldIB->set_sensitive(m_xDragED->HasCurrentItem()); + int nEntry = m_xAddressElementsLB->get_selected_index(); + m_xInsertFieldIB->set_sensitive( nEntry != -1 && + (m_xAddressElementsLB->get_id(nEntry).toInt32() >= 0 || !m_xFieldCB->get_active_text().isEmpty())); +} + +void SwCustomizeAddressBlockDialog::SetAddress(const OUString& rAddress) +{ + m_xDragED->SetText(rAddress); + UpdateImageButtons_Impl(); + EditModifyHdl_Impl(*m_xDragED); +} + +OUString SwCustomizeAddressBlockDialog::GetAddress() const +{ + OUString sAddress(m_xDragED->GetAddress()); + //remove placeholders by the actual content + if (m_xFieldFT->get_visible()) + { + for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i) + { + const OUString sEntry = "<" + m_xAddressElementsLB->get_text(i) + ">"; + sal_Int32 nUserData = m_xAddressElementsLB->get_id(i).toInt32(); + switch(nUserData) + { + case USER_DATA_SALUTATION: + sAddress = sAddress.replaceFirst(sEntry, m_sCurrentSalutation); + break; + case USER_DATA_PUNCTUATION: + sAddress = sAddress.replaceFirst(sEntry, m_sCurrentPunctuation); + break; + case USER_DATA_TEXT: + sAddress = sAddress.replaceFirst(sEntry, m_sCurrentText); + break; + } + } + } + return sAddress; +} + +namespace { + +struct SwAssignFragment +{ + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Label> m_xLabel; + std::unique_ptr<weld::ComboBox> m_xComboBox; + std::unique_ptr<weld::Label> m_xPreview; + + SwAssignFragment(weld::Container* pGrid, int nLine) + : m_xBuilder(Application::CreateBuilder(pGrid, "modules/swriter/ui/assignfragment.ui")) + , m_xLabel(m_xBuilder->weld_label("label")) + , m_xComboBox(m_xBuilder->weld_combo_box("combobox")) + , m_xPreview(m_xBuilder->weld_label("preview")) + { + m_xLabel->set_grid_left_attach(0); + m_xLabel->set_grid_top_attach(nLine); + + m_xComboBox->set_grid_left_attach(1); + m_xComboBox->set_grid_top_attach(nLine); + + m_xPreview->set_grid_left_attach(2); + m_xPreview->set_grid_top_attach(nLine); + } +}; + +} + +class SwAssignFieldsControl +{ + friend class SwAssignFieldsDialog; + std::unique_ptr<weld::ScrolledWindow> m_xVScroll; + std::unique_ptr<weld::Container> m_xGrid; + + std::vector<SwAssignFragment> m_aFields; + + SwMailMergeConfigItem* m_rConfigItem; + + Link<LinkParamNone*,void> m_aModifyHdl; + + DECL_LINK(MatchHdl_Impl, weld::ComboBox&, void); + DECL_LINK(GotFocusHdl_Impl, weld::Widget&, void); + + void MakeVisible(const tools::Rectangle & rRect); +public: + SwAssignFieldsControl(std::unique_ptr<weld::ScrolledWindow> xWindow, + std::unique_ptr<weld::Container> xGrid); + + void Init(SwAssignFieldsDialog* pDialog, SwMailMergeConfigItem& rConfigItem); + void SetModifyHdl(const Link<LinkParamNone*,void>& rModifyHdl) + { + m_aModifyHdl = rModifyHdl; + m_aModifyHdl.Call(nullptr); + } +}; + +SwAssignFieldsControl::SwAssignFieldsControl(std::unique_ptr<weld::ScrolledWindow> xWindow, + std::unique_ptr<weld::Container> xGrid) + : m_xVScroll(std::move(xWindow)) + , m_xGrid(std::move(xGrid)) + , m_rConfigItem(nullptr) +{ +} + +void SwAssignFieldsControl::Init(SwAssignFieldsDialog* pDialog, SwMailMergeConfigItem& rConfigItem) +{ + m_rConfigItem = &rConfigItem; + + //get the name of the default headers + const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders(); + //get the actual data + uno::Reference< XColumnsSupplier > xColsSupp( rConfigItem.GetResultSet(), uno::UNO_QUERY); + //get the name of the actual columns + uno::Reference <XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + uno::Sequence< OUString > aFields; + if(xColAccess.is()) + aFields = xColAccess->getElementNames(); + + //get the current assignment list + //each position in this sequence matches the position in the header array rHeaders + //if no assignment is available an empty sequence will be returned + uno::Sequence< OUString> aAssignments = rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() ); + Link<weld::ComboBox&,void> aMatchHdl = LINK(this, SwAssignFieldsControl, MatchHdl_Impl); + Link<weld::Widget&,void> aFocusHdl = LINK(this, SwAssignFieldsControl, GotFocusHdl_Impl); + + int nLabelWidth(0), nComboBoxWidth(0), nPreviewWidth(0); + + //fill the controls + for (size_t i = 0; i < rHeaders.size(); ++i) + { + m_aFields.emplace_back(m_xGrid.get(), i); + + const OUString rHeader = rHeaders[i].first; + weld::ComboBox& rNewLB = *m_aFields.back().m_xComboBox; + rNewLB.append_text(SwResId(SW_STR_NONE)); + rNewLB.set_active(0); + + for (const OUString& rField : std::as_const(aFields)) + rNewLB.append_text(rField); + //select the ListBox + //if there is an assignment + if(o3tl::make_unsigned(aAssignments.getLength()) > i && !aAssignments[i].isEmpty()) + rNewLB.set_active_text(aAssignments[i]); + else //otherwise the current column name may match one of the db columns + rNewLB.set_active_text(rHeader); + + weld::Label& rNewText = *m_aFields.back().m_xLabel; + rNewText.set_label("<" + rHeader + ">"); + + weld::Label& rNewPreview = *m_aFields.back().m_xPreview; + //then the preview can be filled accordingly + if (xColAccess.is() && rNewLB.get_active() > 0 && + xColAccess->hasByName(rNewLB.get_active_text())) + { + uno::Any aCol = xColAccess->getByName(rNewLB.get_active_text()); + uno::Reference< XColumn > xColumn; + aCol >>= xColumn; + if(xColumn.is()) + { + try + { + rNewPreview.set_label(xColumn->getString()); + } + catch (const SQLException&) + { + } + } + } + + if (i == 0) + { + auto nLineHeight = m_xGrid->get_preferred_size().Height(); + m_xVScroll->set_size_request(m_xVScroll->get_approximate_digit_width() * 65, + nLineHeight * 6); + nComboBoxWidth = rNewLB.get_preferred_size().Width(); + } + + nLabelWidth = std::max<int>(nLabelWidth, rNewText.get_preferred_size().Width()); + nPreviewWidth = std::max<int>(nPreviewWidth, rNewPreview.get_preferred_size().Width()); + + rNewLB.connect_changed(aMatchHdl); + rNewLB.connect_focus_in(aFocusHdl); + rNewText.show(); + rNewLB.show(); + rNewPreview.show(); + } + pDialog->ConnectSizeGroups(nLabelWidth, nComboBoxWidth, nPreviewWidth); +} + +void SwAssignFieldsControl::MakeVisible(const tools::Rectangle & rRect) +{ + //determine range of visible positions + auto nMinVisiblePos = m_xVScroll->vadjustment_get_value(); + auto nMaxVisiblePos = nMinVisiblePos + m_xVScroll->vadjustment_get_page_size(); + if (rRect.Top() < nMinVisiblePos || rRect.Bottom() > nMaxVisiblePos) + m_xVScroll->vadjustment_set_value(rRect.Top()); +} + +IMPL_LINK(SwAssignFieldsControl, MatchHdl_Impl, weld::ComboBox&, rBox, void) +{ + const OUString sColumn = rBox.get_active_text(); + uno::Reference< XColumnsSupplier > xColsSupp( m_rConfigItem->GetResultSet(), uno::UNO_QUERY); + uno::Reference <XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + OUString sPreview; + if(xColAccess.is() && xColAccess->hasByName(sColumn)) + { + uno::Any aCol = xColAccess->getByName(sColumn); + uno::Reference< XColumn > xColumn; + aCol >>= xColumn; + if(xColumn.is()) + { + try + { + sPreview = xColumn->getString(); + } + catch (const sdbc::SQLException&) + { + } + } + } + auto aLBIter = std::find_if(m_aFields.begin(), m_aFields.end(), [&rBox](const SwAssignFragment& rFragment){ + return &rBox == rFragment.m_xComboBox.get(); }); + if (aLBIter != m_aFields.end()) + { + auto nIndex = static_cast<sal_Int32>(std::distance(m_aFields.begin(), aLBIter)); + m_aFields[nIndex].m_xPreview->set_label(sPreview); + } + m_aModifyHdl.Call(nullptr); +} + +IMPL_LINK(SwAssignFieldsControl, GotFocusHdl_Impl, weld::Widget&, rBox, void) +{ + int x, y, width, height; + rBox.get_extents_relative_to(*m_xGrid, x, y, width, height); + // the container has a border of 3 in the .ui + tools::Rectangle aRect(Point(x - 3, y - 3), Size(width + 6, height + 6)); + MakeVisible(aRect); +} + +SwAssignFieldsDialog::SwAssignFieldsDialog( + weld::Window* pParent, SwMailMergeConfigItem& rConfigItem, + const OUString& rPreview, + bool bIsAddressBlock) + : SfxDialogController(pParent, "modules/swriter/ui/assignfieldsdialog.ui", "AssignFieldsDialog") + , m_sNone(SwResId(SW_STR_NONE)) + , m_rPreviewString(rPreview) + , m_rConfigItem(rConfigItem) + , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin"))) + , m_xMatchingFI(m_xBuilder->weld_label("MATCHING_LABEL")) + , m_xAddressTitle(m_xBuilder->weld_label("addresselem")) + , m_xMatchTitle(m_xBuilder->weld_label("matchelem")) + , m_xPreviewTitle(m_xBuilder->weld_label("previewelem")) + , m_xPreviewFI(m_xBuilder->weld_label("PREVIEW_LABEL")) + , m_xOK(m_xBuilder->weld_button("ok")) + , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "PREVIEW", *m_xPreview)) + , m_xFieldsControl(new SwAssignFieldsControl(m_xBuilder->weld_scrolled_window("scroll"), + m_xBuilder->weld_container("FIELDS"))) +{ + m_xPreviewWin->set_size_request(m_xMatchingFI->get_approximate_digit_width() * 45, + m_xMatchingFI->get_text_height() * 5); + m_xFieldsControl->Init(this, rConfigItem); + + const OUString sMatchesTo( SwResId(ST_MATCHESTO) ); + if (!bIsAddressBlock) + { + m_xPreviewFI->set_label(SwResId(ST_SALUTATIONPREVIEW)); + m_xMatchingFI->set_label(SwResId(ST_SALUTATIONMATCHING)); + m_xAddressTitle->set_label(SwResId(ST_SALUTATIONELEMENT)); + } + + m_xFieldsControl->SetModifyHdl(LINK(this, SwAssignFieldsDialog, AssignmentModifyHdl_Impl )); + m_xMatchingFI->set_label(m_xMatchingFI->get_label().replaceAll("%1", sMatchesTo)); + m_xOK->connect_clicked(LINK(this, SwAssignFieldsDialog, OkHdl_Impl)); +} + +SwAssignFieldsDialog::~SwAssignFieldsDialog() +{ +} + +uno::Sequence< OUString > SwAssignFieldsDialog::CreateAssignments() +{ + uno::Sequence< OUString > aAssignments( + m_rConfigItem.GetDefaultAddressHeaders().size()); + OUString* pAssignments = aAssignments.getArray(); + sal_Int32 nIndex = 0; + for (const auto& rLBItem : m_xFieldsControl->m_aFields) + { + const OUString sSelect = rLBItem.m_xComboBox->get_active_text(); + pAssignments[nIndex] = (m_sNone != sSelect) ? sSelect : OUString(); + ++nIndex; + } + return aAssignments; +} + +IMPL_LINK_NOARG(SwAssignFieldsDialog, OkHdl_Impl, weld::Button&, void) +{ + m_rConfigItem.SetColumnAssignment( + m_rConfigItem.GetCurrentDBData(), + CreateAssignments() ); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SwAssignFieldsDialog, AssignmentModifyHdl_Impl, LinkParamNone*, void) +{ + uno::Sequence< OUString > aAssignments = CreateAssignments(); + const OUString sPreview = SwAddressPreview::FillData( + m_rPreviewString, m_rConfigItem, &aAssignments); + m_xPreview->SetAddress(sPreview); +} + +void SwAssignFieldsDialog::ConnectSizeGroups(int nLabelWidth, int nComboBoxWidth, int nPreviewWidth) +{ + m_xAddressTitle->set_size_request(nLabelWidth, -1); + m_xMatchTitle->set_size_request(nComboBoxWidth, -1); + m_xPreviewTitle->set_size_request(nPreviewWidth, -1); +} + +namespace +{ + const EECharAttrib* FindCharAttrib(int nStartPosition, std::vector<EECharAttrib>& rAttribList) + { + for (auto it = rAttribList.rbegin(); it != rAttribList.rend(); ++it) + { + const auto& rTextAtr = *it; + if (rTextAtr.pAttr->Which() != EE_CHAR_GRABBAG) + continue; + if (rTextAtr.nStart <= nStartPosition && rTextAtr.nEnd >= nStartPosition) + { + return &rTextAtr; + } + } + + return nullptr; + } +} + +AddressMultiLineEdit::AddressMultiLineEdit(SwCustomizeAddressBlockDialog *pParent) + : m_pParentDialog(pParent) +{ +} + +void AddressMultiLineEdit::EndDropTarget() +{ + if (m_xDropTarget.is()) + { + auto xRealDropTarget = GetDrawingArea()->get_drop_target(); + uno::Reference<css::datatransfer::dnd::XDropTargetListener> xListener(m_xDropTarget, uno::UNO_QUERY); + xRealDropTarget->removeDropTargetListener(xListener); + m_xDropTarget.clear(); + } +} + +AddressMultiLineEdit::~AddressMultiLineEdit() +{ + assert(!m_xDropTarget.is()); +} + +void AddressMultiLineEdit::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(160, 60), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + WeldEditView::SetDrawingArea(pDrawingArea); +} + +bool AddressMultiLineEdit::KeyInput(const KeyEvent& rKEvt) +{ + if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + return false; // we want default esc behaviour + if (rKEvt.GetCharCode()) + return true; // handled + return WeldEditView::KeyInput(rKEvt); +} + +bool AddressMultiLineEdit::MouseButtonDown(const MouseEvent& rMEvt) +{ + if (rMEvt.GetClicks() >= 2) + return true; // handled + return WeldEditView::MouseButtonDown(rMEvt); +} + +OUString AddressMultiLineEdit::GetText() const +{ + return m_xEditEngine->GetText(); +} + +void AddressMultiLineEdit::SetText( const OUString& rStr ) +{ + m_xEditEngine->SetText(rStr); + //set attributes to all address tokens + + sal_Int32 nSequence(0); + SfxGrabBagItem aProtectAttr(EE_CHAR_GRABBAG); + const sal_uInt32 nParaCount = m_xEditEngine->GetParagraphCount(); + for(sal_uInt32 nPara = 0; nPara < nParaCount; ++nPara) + { + sal_Int32 nIndex = 0; + const OUString sPara = m_xEditEngine->GetText( nPara ); + if (!sPara.isEmpty() && !sPara.endsWith(" ")) + { + ESelection aPaM(nPara, sPara.getLength(), nPara, sPara.getLength()); + m_xEditEngine->QuickInsertText(" ", aPaM); + } + for(;;) + { + const sal_Int32 nStart = sPara.indexOf( '<', nIndex ); + if (nStart < 0) + break; + const sal_Int32 nEnd = sPara.indexOf( '>', nStart ); + if (nEnd < 0) + break; + nIndex = nEnd; + SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet()); + // make each one different, so they are not collapsed together + // as one attribute + aProtectAttr.GetGrabBag()["Index"] <<= nSequence++; + aSet.Put(aProtectAttr); + m_xEditEngine->QuickSetAttribs(aSet, ESelection(nPara, nStart, nPara, nEnd + 1)); + } + + } + // add two empty paragraphs at the end + if(m_pParentDialog->m_eType == SwCustomizeAddressBlockDialog::ADDRESSBLOCK_NEW || + m_pParentDialog->m_eType == SwCustomizeAddressBlockDialog::ADDRESSBLOCK_EDIT) + { + sal_Int32 nLastLen = m_xEditEngine->GetText(nParaCount - 1).getLength(); + if(nLastLen) + { + int nPara = nParaCount ? nParaCount - 1 : 0; + ESelection aPaM(nPara, nLastLen, nPara, nLastLen); + m_xEditEngine->QuickInsertText("\n \n ", aPaM); + } + } + + m_xEditView->SetSelection(ESelection(0, 0, 0, 0)); +} + +// Insert the new entry in front of the entry at the beginning of the selection +void AddressMultiLineEdit::InsertNewEntry( const OUString& rStr ) +{ + // insert new entry after current selected one. + ESelection aSelection = m_xEditView->GetSelection(); + const sal_uInt32 nPara = aSelection.nStartPara; + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(nPara, aAttribList); + + sal_Int32 nIndex = aSelection.nEndPara; + const EECharAttrib* pAttrib; + if(nullptr != (pAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList))) + nIndex = pAttrib->nEnd; + InsertNewEntryAtPosition( rStr, nPara, nIndex ); + + // select the new entry + m_xEditEngine->GetCharAttribs(nPara, aAttribList); + pAttrib = FindCharAttrib(nIndex, aAttribList); + const sal_Int32 nEnd = pAttrib ? pAttrib->nEnd : nIndex; + ESelection aEntrySel(nPara, nIndex, nPara, nEnd); + m_xEditView->SetSelection(aEntrySel); + Invalidate(); + m_aModifyLink.Call(*this); +} + +void AddressMultiLineEdit::InsertNewEntryAtPosition( const OUString& rStr, sal_uLong nPara, sal_uInt16 nIndex ) +{ + ESelection aInsertPos(nPara, nIndex, nPara, nIndex); + m_xEditEngine->QuickInsertText(rStr, aInsertPos); + + //restore the attributes + SetText( GetAddress() ); + + //select the newly inserted/moved element + m_xEditView->SetSelection(aInsertPos); + m_aSelectionLink.Call(false); +} + +void AddressMultiLineEdit::RemoveCurrentEntry() +{ + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if(pBeginAttrib && + (pBeginAttrib->nStart <= aSelection.nStartPos + && pBeginAttrib->nEnd >= aSelection.nEndPos)) + { + const sal_uInt32 nPara = aSelection.nStartPara; + ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd); + m_xEditEngine->QuickInsertText(OUString(), aEntrySel); + //restore the attributes + SetText( GetAddress() ); + m_aModifyLink.Call(*this); + } +} + +void AddressMultiLineEdit::MoveCurrentItem(MoveItemFlags nMove) +{ + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if(!pBeginAttrib || + !(pBeginAttrib->nStart <= aSelection.nStartPos && + pBeginAttrib->nEnd >= aSelection.nEndPos)) + return; + + //current item has been found + sal_Int32 nPara = aSelection.nStartPara; + sal_Int32 nIndex = pBeginAttrib->nStart; + ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd); + const OUString sCurrentItem = m_xEditEngine->GetText(aEntrySel); + m_xEditEngine->RemoveAttribs(aEntrySel, false, EE_CHAR_GRABBAG); + m_xEditEngine->QuickInsertText(OUString(), aEntrySel); + m_xEditEngine->GetCharAttribs(nPara, aAttribList); + switch (nMove) + { + case MoveItemFlags::Left : + if(nIndex) + { + //go left to find a predecessor or simple text + --nIndex; + const OUString sPara = m_xEditEngine->GetText( nPara ); + sal_Int32 nSearchIndex = sPara.lastIndexOf( '>', nIndex+1 ); + if( nSearchIndex != -1 && nSearchIndex == nIndex ) + { + nSearchIndex = sPara.lastIndexOf( '<', nIndex ); + if( nSearchIndex != -1 ) + nIndex = nSearchIndex; + } + } + break; + case MoveItemFlags::Right: + { + //go right to find a successor or simple text + ++nIndex; + const EECharAttrib* pEndAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if(pEndAttrib && pEndAttrib->nEnd >= nIndex) + { + nIndex = pEndAttrib->nEnd; + } + } + break; + case MoveItemFlags::Up : + --nPara; + nIndex = 0; + break; + case MoveItemFlags::Down : + ++nPara; + nIndex = 0; + break; + default: break; + } + //add a new paragraph if there is none yet + if (nPara >= m_xEditEngine->GetParagraphCount()) + { + auto nInsPara = nPara - 1; + auto nInsPos = m_xEditEngine->GetTextLen( nPara - 1 ); + ESelection aTemp(nInsPara, nInsPos, nInsPara, nInsPos); + m_xEditEngine->QuickInsertText("\n", aTemp); + } + InsertNewEntryAtPosition( sCurrentItem, nPara, nIndex ); + + // select the new entry [#i40817] + m_xEditEngine->GetCharAttribs(nPara, aAttribList); + const EECharAttrib* pAttrib = FindCharAttrib(nIndex, aAttribList); + if (pAttrib) + aEntrySel = ESelection(nPara, nIndex, nPara, pAttrib->nEnd); + m_xEditView->SetSelection(aEntrySel); + Invalidate(); + m_aModifyLink.Call(*this); +} + +MoveItemFlags AddressMultiLineEdit::IsCurrentItemMoveable() const +{ + MoveItemFlags nRet = MoveItemFlags::NONE; + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if (pBeginAttrib && + (pBeginAttrib->nStart <= aSelection.nStartPos + && pBeginAttrib->nEnd >= aSelection.nEndPos)) + { + if (pBeginAttrib->nStart) + nRet |= MoveItemFlags::Left; + //if there is an entry it can always be move to the right and down + nRet |= MoveItemFlags::Right | MoveItemFlags::Down; + if (aSelection.nStartPara > 0) + nRet |= MoveItemFlags::Up; + } + return nRet; +} + +bool AddressMultiLineEdit::HasCurrentItem() const +{ + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + return (pBeginAttrib && + (pBeginAttrib->nStart <= aSelection.nStartPos + && pBeginAttrib->nEnd >= aSelection.nEndPos)); +} + +OUString AddressMultiLineEdit::GetCurrentItem() const +{ + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if (pBeginAttrib && + (pBeginAttrib->nStart <= aSelection.nStartPos + && pBeginAttrib->nEnd >= aSelection.nEndPos)) + { + const sal_uInt32 nPara = aSelection.nStartPara; + ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd); + return m_xEditEngine->GetText( aEntrySel ); + } + return OUString(); +} + +void AddressMultiLineEdit::SelectCurrentItem() +{ + ESelection aSelection = m_xEditView->GetSelection(); + + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + + const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList); + if (pBeginAttrib && + (pBeginAttrib->nStart <= aSelection.nStartPos + && pBeginAttrib->nEnd >= aSelection.nEndPos)) + { + const sal_uInt32 nPara = aSelection.nStartPara; + ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd); + m_xEditView->SetSelection(aEntrySel); + Invalidate(); + } +} + +OUString AddressMultiLineEdit::GetAddress() const +{ + OUString sRet; + const sal_uInt32 nParaCount = m_xEditEngine->GetParagraphCount(); + for(sal_uInt32 nPara = nParaCount; nPara; --nPara) + { + const OUString sPara = comphelper::string::stripEnd(m_xEditEngine->GetText(nPara - 1), ' '); + //don't add empty trailing paragraphs + if(!sRet.isEmpty() || !sPara.isEmpty()) + { + sRet = sPara + sRet; + //insert the para break + if(nPara > 1) + sRet = "\n" + sRet; + } + } + return sRet; +} + +void AddressMultiLineEdit::UpdateFields() +{ + ESelection aSelection = m_xEditView->GetSelection(); + + //restore the attributes + SetText( GetAddress() ); + + //reselect the element + m_xEditView->SetSelection(aSelection); + m_aSelectionLink.Call(false); +} + +void AddressMultiLineEdit::EditViewSelectionChange() +{ + WeldEditView::EditViewSelectionChange(); + m_aSelectionLink.Call(true); +} + +namespace +{ + // sit between the tree as drag source and the editview as drop target and translate + // the tree dnd data to the simple string the editview wants + class DropTargetListener : public cppu::WeakImplHelper< css::datatransfer::dnd::XDropTargetListener, + css::datatransfer::dnd::XDropTarget > + { + private: + css::uno::Reference<css::datatransfer::dnd::XDropTarget> m_xRealDropTarget; + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> m_aListeners; + SwCustomizeAddressBlockDialog* m_pParentDialog; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& ) override + { + m_xRealDropTarget.clear(); + m_aListeners.clear(); + } + + // XDropTargetListener + virtual void SAL_CALL drop( const css::datatransfer::dnd::DropTargetDropEvent& dtde ) override + { + SolarMutexGuard aGuard; + + auto aReplacement(dtde); + + Point aMousePos(dtde.LocationX, dtde.LocationY); + bool bAllowed = m_pParentDialog->SetCursorLogicPosition(aMousePos); + if (bAllowed) + { + if (weld::TreeView* pTree = m_pParentDialog->get_drag_source()) + { + int nEntry = pTree->get_selected_index(); + if (nEntry != -1) + { + sal_Int32 nUserData = pTree->get_id(nEntry).toInt32(); + //special entries can only be once in the address / greeting + if (nUserData >= 0 || !m_pParentDialog->HasItem(nUserData)) + { + rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer; + xContainer->CopyString( "<" + pTree->get_text(nEntry) + ">" ); + + // replace what the treeview is offering with what ImpEditView::drop wants + aReplacement.Transferable = xContainer.get(); + } + } + } + } + + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners); + for (auto const& listener : aListeners) + listener->drop(aReplacement); + + if (bAllowed) + m_pParentDialog->UpdateFields(); + } + + virtual void SAL_CALL dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& dtdee ) override + { + auto aReplacement(dtdee); + // replace what the treeview is offering with what ImpEditView::dragEnter wants + aReplacement.SupportedDataFlavors.realloc(1); + SotExchange::GetFormatDataFlavor(SotClipboardFormatId::STRING, aReplacement.SupportedDataFlavors[0]); + + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners); + for (auto const& listener : aListeners) + listener->dragEnter(aReplacement); + } + + virtual void SAL_CALL dragExit( const css::datatransfer::dnd::DropTargetEvent& dte ) override + { + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners); + for (auto const& listener : aListeners) + listener->dragExit( dte ); + } + + virtual void SAL_CALL dragOver( const css::datatransfer::dnd::DropTargetDragEvent& dtde ) override + { + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners); + for (auto const& listener : aListeners) + listener->dragOver( dtde ); + } + + virtual void SAL_CALL dropActionChanged( const css::datatransfer::dnd::DropTargetDragEvent& dtde ) override + { + std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners); + for (auto const& listener : aListeners) + listener->dropActionChanged( dtde ); + } + + // XDropTarget + virtual void SAL_CALL addDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>& xListener) override + { + m_aListeners.push_back(xListener); + } + + virtual void SAL_CALL removeDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>& xListener) override + { + m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), xListener), m_aListeners.end()); + } + + virtual sal_Bool SAL_CALL isActive() override + { + return m_xRealDropTarget->isActive(); + } + + virtual void SAL_CALL setActive(sal_Bool active) override + { + m_xRealDropTarget->setActive(active); + } + + virtual sal_Int8 SAL_CALL getDefaultActions() override + { + return m_xRealDropTarget->getDefaultActions(); + } + + virtual void SAL_CALL setDefaultActions(sal_Int8 actions) override + { + m_xRealDropTarget->setDefaultActions(actions); + } + + public: + DropTargetListener(css::uno::Reference<css::datatransfer::dnd::XDropTarget> xRealDropTarget, + SwCustomizeAddressBlockDialog* pParentDialog) + : m_xRealDropTarget(xRealDropTarget) + , m_pParentDialog(pParentDialog) + { + } + }; +} + +css::uno::Reference<css::datatransfer::dnd::XDropTarget> AddressMultiLineEdit::GetDropTarget() +{ + if (!m_xDropTarget.is()) + { + auto xRealDropTarget = GetDrawingArea()->get_drop_target(); + DropTargetListener* pProxy = new DropTargetListener(xRealDropTarget, m_pParentDialog); + uno::Reference<css::datatransfer::dnd::XDropTargetListener> xListener(pProxy); + xRealDropTarget->addDropTargetListener(xListener); + m_xDropTarget = uno::Reference<css::datatransfer::dnd::XDropTarget>(pProxy); + } + return m_xDropTarget; +} + +bool AddressMultiLineEdit::SetCursorLogicPosition(const Point& rPosition) +{ + Point aMousePos = EditViewOutputDevice().PixelToLogic(rPosition); + m_xEditView->SetCursorLogicPosition(aMousePos, false, true); + + ESelection aSelection = m_xEditView->GetSelection(); + std::vector<EECharAttrib> aAttribList; + m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList); + return FindCharAttrib(aSelection.nStartPos, aAttribList) == nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmaddressblockpage.hxx b/sw/source/ui/dbui/mmaddressblockpage.hxx new file mode 100644 index 000000000..c1fc32240 --- /dev/null +++ b/sw/source/ui/dbui/mmaddressblockpage.hxx @@ -0,0 +1,284 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_DBUI_MMADDRESSBLOCKPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMADDRESSBLOCKPAGE_HXX + +#include <svx/weldeditview.hxx> +#include <vcl/wizardmachine.hxx> +#include <mailmergehelper.hxx> +#include <sfx2/basedlgs.hxx> +#include <vcl/textfilter.hxx> +#include <svl/lstner.hxx> +#include <o3tl/typed_flags_set.hxx> + +class SwMailMergeWizard; +class SwMailMergeConfigItem; + +class SwMailMergeAddressBlockPage : public vcl::OWizardPage +{ + OUString m_sDocument; + OUString m_sCurrentAddress; + OUString m_sChangeAddress; + + SwMailMergeWizard* m_pWizard; + + std::unique_ptr<weld::Button> m_xAddressListPB; + std::unique_ptr<weld::Label> m_xCurrentAddressFI; + + std::unique_ptr<weld::Container> m_xStep2; + std::unique_ptr<weld::Container> m_xStep3; + std::unique_ptr<weld::Container> m_xStep4; + + std::unique_ptr<weld::Label> m_xSettingsFI; + std::unique_ptr<weld::CheckButton> m_xAddressCB; + std::unique_ptr<weld::Button> m_xSettingsPB; + std::unique_ptr<weld::CheckButton> m_xHideEmptyParagraphsCB; + + std::unique_ptr<weld::Button> m_xAssignPB; + + std::unique_ptr<weld::Label> m_xDocumentIndexFI; + std::unique_ptr<weld::Button> m_xPrevSetIB; + std::unique_ptr<weld::Button> m_xNextSetIB; + + std::unique_ptr<weld::Button> m_xDifferentlist; + + std::unique_ptr<SwAddressPreview> m_xSettings; + std::unique_ptr<SwAddressPreview> m_xPreview; + std::unique_ptr<weld::CustomWeld> m_xSettingsWIN; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + + void InsertDataHdl(const weld::Button* pButton); + + DECL_LINK(AddressListHdl_Impl, weld::Button&, void); + DECL_LINK(SettingsHdl_Impl, weld::Button&, void); + DECL_LINK(AssignHdl_Impl, weld::Button&, void); + DECL_LINK(AddressBlockHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(InsertDataHdl_Impl, weld::Button&, void); + DECL_LINK(AddressBlockSelectHdl_Impl, LinkParamNone*, void); + DECL_LINK(HideParagraphsHdl_Impl, weld::ToggleButton&, void); + + void EnableAddressBlock(bool bAll, bool bSelective); + + virtual void Activate() override; + virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override; + virtual bool canAdvance() const override; + +public: + SwMailMergeAddressBlockPage(weld::Container* pPage, SwMailMergeWizard* pWizard); + virtual ~SwMailMergeAddressBlockPage() override; + SwMailMergeWizard* GetWizard() { return m_pWizard; } +}; + +class SwSelectAddressBlockDialog : public SfxDialogController +{ + css::uno::Sequence< OUString> m_aAddressBlocks; + SwMailMergeConfigItem& m_rConfig; + + std::unique_ptr<SwAddressPreview> m_xPreview; + std::unique_ptr<weld::Button> m_xNewPB; + std::unique_ptr<weld::Button> m_xCustomizePB; + std::unique_ptr<weld::Button> m_xDeletePB; + std::unique_ptr<weld::RadioButton> m_xNeverRB; + std::unique_ptr<weld::RadioButton> m_xAlwaysRB; + std::unique_ptr<weld::RadioButton> m_xDependentRB; + std::unique_ptr<weld::Entry> m_xCountryED; + std::unique_ptr<weld::CustomWeld> m_xPreviewWin; + + DECL_LINK(NewCustomizeHdl_Impl, weld::Button&, void); + DECL_LINK(DeleteHdl_Impl, weld::Button&, void); + DECL_LINK(IncludeHdl_Impl, weld::ToggleButton&, void); + +public: + SwSelectAddressBlockDialog(weld::Window* pParent, SwMailMergeConfigItem& rConfig); + virtual ~SwSelectAddressBlockDialog() override; + + void SetAddressBlocks(const css::uno::Sequence< OUString>& rBlocks, + sal_uInt16 nSelected); + const css::uno::Sequence< OUString>& GetAddressBlocks(); + + void SetSettings(bool bIsCountry, const OUString& sCountry); + bool IsIncludeCountry() const {return !m_xNeverRB->get_active();} + OUString GetCountry() const; +}; + +class SwCustomizeAddressBlockDialog; + +enum class MoveItemFlags { + NONE = 0, + Left = 1, + Right = 2, + Up = 4, + Down = 8, +}; +namespace o3tl { + template<> struct typed_flags<MoveItemFlags> : is_typed_flags<MoveItemFlags, 0x0f> {}; +} + +class AddressMultiLineEdit; + +class AddressMultiLineEdit : public WeldEditView + , public SfxListener +{ + Link<bool,void> m_aSelectionLink; + Link<AddressMultiLineEdit&,void> m_aModifyLink; + SwCustomizeAddressBlockDialog* m_pParentDialog; + + css::uno::Reference<css::datatransfer::dnd::XDropTarget> m_xDropTarget; + + virtual void EditViewSelectionChange() override; + virtual css::uno::Reference<css::datatransfer::dnd::XDropTarget> GetDropTarget() override; + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + +public: + AddressMultiLineEdit(SwCustomizeAddressBlockDialog *pParent); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + void EndDropTarget(); + bool SetCursorLogicPosition(const Point& rPosition); + void UpdateFields(); + virtual ~AddressMultiLineEdit() override; + + void SetSelectionChangedHdl( const Link<bool,void>& rLink ) { m_aSelectionLink = rLink; } + void SetModifyHdl( const Link<AddressMultiLineEdit&,void>& rLink ) { m_aModifyLink = rLink; } + + void SetText( const OUString& rStr ); + OUString GetText() const; + OUString GetAddress() const; + + void InsertNewEntry( const OUString& rStr ); + void InsertNewEntryAtPosition( const OUString& rStr, sal_uLong nPara, sal_uInt16 nIndex ); + void RemoveCurrentEntry(); + + void MoveCurrentItem(MoveItemFlags nMove); + MoveItemFlags IsCurrentItemMoveable() const; + bool HasCurrentItem() const; + OUString GetCurrentItem() const; + void SelectCurrentItem(); +}; + +class SwCustomizeAddressBlockDialog : public SfxDialogController +{ + friend class AddressMultiLineEdit; +public: + enum DialogType + { + ADDRESSBLOCK_NEW, + ADDRESSBLOCK_EDIT, + GREETING_FEMALE, + GREETING_MALE + }; +private: + TextFilter m_aTextFilter; + + std::vector<OUString> m_aSalutations; + std::vector<OUString> m_aPunctuations; + + OUString m_sCurrentSalutation; + OUString m_sCurrentPunctuation; + OUString m_sCurrentText; + + SwMailMergeConfigItem& m_rConfigItem; + DialogType m_eType; + + Idle m_aSelectionChangedIdle; + + std::unique_ptr<weld::Label> m_xAddressElementsFT; + std::unique_ptr<weld::TreeView> m_xAddressElementsLB; + std::unique_ptr<weld::Button> m_xInsertFieldIB; + std::unique_ptr<weld::Button> m_xRemoveFieldIB; + std::unique_ptr<weld::Label> m_xDragFT; + std::unique_ptr<weld::Button> m_xUpIB; + std::unique_ptr<weld::Button> m_xLeftIB; + std::unique_ptr<weld::Button> m_xRightIB; + std::unique_ptr<weld::Button> m_xDownIB; + std::unique_ptr<weld::Label> m_xFieldFT; + std::unique_ptr<weld::ComboBox> m_xFieldCB; + std::unique_ptr<weld::Button> m_xOK; + std::unique_ptr<SwAddressPreview> m_xPreview; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + std::unique_ptr<AddressMultiLineEdit> m_xDragED; + std::unique_ptr<weld::CustomWeld> m_xDragWIN; + + DECL_LINK(OKHdl_Impl, weld::Button&, void); + DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void); + DECL_LINK(EditModifyHdl_Impl, AddressMultiLineEdit&, void); + DECL_LINK(ImageButtonHdl_Impl, weld::Button&, void); + DECL_LINK(SelectionChangedHdl_Impl, bool, void); + DECL_LINK(FieldChangeComboBoxHdl_Impl, weld::ComboBox&, void); + DECL_LINK(TextFilterHdl, OUString&, bool); + DECL_LINK(SelectionChangedIdleHdl, Timer*, void); + + sal_Int32 GetSelectedItem_Impl() const; + void UpdateImageButtons_Impl(); + +public: + SwCustomizeAddressBlockDialog(weld::Widget* pParent, SwMailMergeConfigItem& rConfig, DialogType); + virtual ~SwCustomizeAddressBlockDialog() override; + + bool SetCursorLogicPosition(const Point& rPosition); + void UpdateFields(); + + // for dragging from the TreeViews, return the active source + weld::TreeView* get_drag_source() const { return m_xAddressElementsLB->get_drag_source(); } + bool HasItem(sal_Int32 nUserData); + + void SetAddress(const OUString& rAddress); + OUString GetAddress() const; +}; + +class SwAssignFieldsControl; +class SwAssignFieldsDialog : public SfxDialogController +{ + OUString m_sNone; + OUString m_rPreviewString; + + SwMailMergeConfigItem& m_rConfigItem; + + std::unique_ptr<SwAddressPreview> m_xPreview; + std::unique_ptr<weld::Label> m_xMatchingFI; + std::unique_ptr<weld::Label> m_xAddressTitle; + std::unique_ptr<weld::Label> m_xMatchTitle; + std::unique_ptr<weld::Label> m_xPreviewTitle; + std::unique_ptr<weld::Label> m_xPreviewFI; + std::unique_ptr<weld::Button> m_xOK; + std::unique_ptr<weld::CustomWeld> m_xPreviewWin; + std::unique_ptr<SwAssignFieldsControl> m_xFieldsControl; + + std::unique_ptr<weld::SizeGroup> m_xLabelGroup; + std::unique_ptr<weld::SizeGroup> m_xComboGroup; + std::unique_ptr<weld::SizeGroup> m_xPreviewGroup; + + css::uno::Sequence< OUString > CreateAssignments(); + DECL_LINK(OkHdl_Impl, weld::Button&, void); + DECL_LINK(AssignmentModifyHdl_Impl, LinkParamNone*, void); + +public: + SwAssignFieldsDialog(weld::Window* pParent, + SwMailMergeConfigItem& rConfigItem, + const OUString& rPreview, + bool bIsAddressBlock); + + void ConnectSizeGroups(int nLabelWidth, int nComboBoxWidth, int nPreviewWidth); + + virtual ~SwAssignFieldsDialog() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmdocselectpage.cxx b/sw/source/ui/dbui/mmdocselectpage.cxx new file mode 100644 index 000000000..f19089126 --- /dev/null +++ b/sw/source/ui/dbui/mmdocselectpage.cxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unotools/pathoptions.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/new.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfac.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include "mmdocselectpage.hxx" +#include <mailmergewizard.hxx> +#include <swabstdlg.hxx> +#include <mmconfigitem.hxx> + +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> + +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace svt; + +SwMailMergeDocSelectPage::SwMailMergeDocSelectPage(weld::Container* pPage, SwMailMergeWizard* pWizard) + : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmselectpage.ui", "MMSelectPage") + , m_pWizard(pWizard) + , m_xCurrentDocRB(m_xBuilder->weld_radio_button("currentdoc")) + , m_xNewDocRB(m_xBuilder->weld_radio_button("newdoc")) + , m_xLoadDocRB(m_xBuilder->weld_radio_button("loaddoc")) + , m_xLoadTemplateRB(m_xBuilder->weld_radio_button("template")) + , m_xRecentDocRB(m_xBuilder->weld_radio_button("recentdoc")) + , m_xBrowseDocPB(m_xBuilder->weld_button("browsedoc")) + , m_xBrowseTemplatePB(m_xBuilder->weld_button("browsetemplate")) + , m_xRecentDocLB(m_xBuilder->weld_combo_box("recentdoclb")) +{ + m_xCurrentDocRB->set_active(true); + DocSelectHdl(*m_xNewDocRB); + + Link<weld::ToggleButton&,void> aDocSelectLink = LINK(this, SwMailMergeDocSelectPage, DocSelectHdl); + m_xCurrentDocRB->connect_toggled(aDocSelectLink); + m_xNewDocRB->connect_toggled(aDocSelectLink); + m_xLoadDocRB->connect_toggled(aDocSelectLink); + m_xLoadTemplateRB->connect_toggled(aDocSelectLink); + m_xRecentDocRB->connect_toggled(aDocSelectLink); + + Link<weld::Button&,void> aFileSelectHdl = LINK(this, SwMailMergeDocSelectPage, FileSelectHdl); + m_xBrowseDocPB->connect_clicked(aFileSelectHdl); + m_xBrowseTemplatePB->connect_clicked(aFileSelectHdl); + + const uno::Sequence< OUString >& rDocs = + m_pWizard->GetConfigItem().GetSavedDocuments(); + for(const auto& rDoc : rDocs) + { + //insert in reverse order + m_xRecentDocLB->insert_text(0, rDoc); + } + if (!rDocs.hasElements()) + m_xRecentDocRB->set_sensitive(false); + else + m_xRecentDocLB->set_active(0); +} + +SwMailMergeDocSelectPage::~SwMailMergeDocSelectPage() +{ +} + +IMPL_LINK_NOARG(SwMailMergeDocSelectPage, DocSelectHdl, weld::ToggleButton&, void) +{ + m_xRecentDocLB->set_sensitive(m_xRecentDocRB->get_active()); + + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_OUTPUTTYPETPAGE)); +} + +IMPL_LINK(SwMailMergeDocSelectPage, FileSelectHdl, weld::Button&, rButton, void) +{ + bool bTemplate = m_xBrowseTemplatePB.get() == &rButton; + + if(bTemplate) + { + m_xLoadTemplateRB->set_active(true); + SfxNewFileDialog aNewFileDlg(m_pWizard->getDialog(), SfxNewFileDialogMode::NONE); + sal_uInt16 nRet = aNewFileDlg.run(); + if(RET_TEMPLATE_LOAD == nRet) + bTemplate = false; + else if(RET_CANCEL != nRet) + m_sLoadTemplateName = aNewFileDlg.GetTemplateFileName(); + } + else + m_xLoadDocRB->set_active(true); + + if(!bTemplate) + { + sfx2::FileDialogHelper aDlgHelper(TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, m_pWizard->getDialog()); + Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + xFP->setDisplayDirectory( SvtPathOptions().GetWorkPath() ); + + SfxObjectFactory &rFact = m_pWizard->GetSwView()->GetDocShell()->GetFactory(); + SfxFilterMatcher aMatcher( rFact.GetFactoryName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFlt = aIter.First(); + while( pFlt ) + { + if( pFlt && pFlt->IsAllowedAsTemplate() ) + { + const OUString sWild = pFlt->GetWildcard().getGlob(); + xFP->appendFilter( pFlt->GetUIName(), sWild ); + + // #i40125 + if(pFlt->GetFilterFlags() & SfxFilterFlags::DEFAULT) + xFP->setCurrentFilter( pFlt->GetUIName() ) ; + } + + pFlt = aIter.Next(); + } + + if( ERRCODE_NONE == aDlgHelper.Execute() ) + { + m_sLoadFileName = xFP->getSelectedFiles().getConstArray()[0]; + } + } + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_OUTPUTTYPETPAGE)); +} + +bool SwMailMergeDocSelectPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) +{ + bool bReturn = false; + bool bNext = _eReason == ::vcl::WizardTypes::eTravelForward; + if(bNext || _eReason == ::vcl::WizardTypes::eValidate ) + { + OUString sReloadDocument; + bReturn = m_xCurrentDocRB->get_active() || + m_xNewDocRB->get_active(); + if (!bReturn) + { + sReloadDocument = m_sLoadFileName; + bReturn = !sReloadDocument.isEmpty() && m_xLoadDocRB->get_active(); + } + if (!bReturn) + { + sReloadDocument = m_sLoadTemplateName; + bReturn = !sReloadDocument.isEmpty() && m_xLoadTemplateRB->get_active(); + } + if (!bReturn) + { + bReturn = m_xRecentDocRB->get_active(); + if (bReturn) + { + sReloadDocument = m_xRecentDocLB->get_active_text(); + bReturn = !sReloadDocument.isEmpty(); + } + } + if( _eReason == ::vcl::WizardTypes::eValidate ) + m_pWizard->SetDocumentLoad(!m_xCurrentDocRB->get_active()); + + if(bNext && !m_xCurrentDocRB->get_active()) + { + if(!sReloadDocument.isEmpty()) + m_pWizard->SetReloadDocument( sReloadDocument ); + m_pWizard->SetRestartPage(MM_OUTPUTTYPETPAGE); + m_pWizard->response(RET_LOAD_DOC); + } + } + return bReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmdocselectpage.hxx b/sw/source/ui/dbui/mmdocselectpage.hxx new file mode 100644 index 000000000..0930caf03 --- /dev/null +++ b/sw/source/ui/dbui/mmdocselectpage.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_UI_DBUI_MMDOCSELECTPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMDOCSELECTPAGE_HXX + +#include <vcl/wizardmachine.hxx> +#include <vcl/weld.hxx> + +class SwMailMergeWizard; + +class SwMailMergeDocSelectPage : public vcl::OWizardPage +{ + OUString m_sLoadFileName; + OUString m_sLoadTemplateName; + + SwMailMergeWizard* m_pWizard; + + std::unique_ptr<weld::RadioButton> m_xCurrentDocRB; + std::unique_ptr<weld::RadioButton> m_xNewDocRB; + std::unique_ptr<weld::RadioButton> m_xLoadDocRB; + std::unique_ptr<weld::RadioButton> m_xLoadTemplateRB; + std::unique_ptr<weld::RadioButton> m_xRecentDocRB; + std::unique_ptr<weld::Button> m_xBrowseDocPB; + std::unique_ptr<weld::Button> m_xBrowseTemplatePB; + std::unique_ptr<weld::ComboBox> m_xRecentDocLB; + + DECL_LINK(DocSelectHdl, weld::ToggleButton&, void); + DECL_LINK(FileSelectHdl, weld::Button&, void); + + virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override; + +public: + SwMailMergeDocSelectPage(weld::Container* pPage, SwMailMergeWizard* pWizard); + virtual ~SwMailMergeDocSelectPage() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmgreetingspage.cxx b/sw/source/ui/dbui/mmgreetingspage.cxx new file mode 100644 index 000000000..c09f087b6 --- /dev/null +++ b/sw/source/ui/dbui/mmgreetingspage.cxx @@ -0,0 +1,421 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "mmgreetingspage.hxx" +#include <mailmergewizard.hxx> +#include <mmconfigitem.hxx> +#include "mmaddressblockpage.hxx" +#include <dbui.hrc> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <swmodule.hxx> +#include <view.hxx> + +using namespace svt; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +static void lcl_FillGreetingsBox(weld::ComboBox& rBox, + SwMailMergeConfigItem const & rConfig, + SwMailMergeConfigItem::Gender eType) +{ + const Sequence< OUString> rEntries = rConfig.GetGreetings(eType); + for(const auto& rEntry : rEntries) + rBox.append_text(rEntry); + rBox.set_active(rConfig.GetCurrentGreeting(eType)); +} + +static void lcl_StoreGreetingsBox(const weld::ComboBox& rBox, + SwMailMergeConfigItem& rConfig, + SwMailMergeConfigItem::Gender eType) +{ + Sequence< OUString> aEntries(rBox.get_count()); + OUString* pEntries = aEntries.getArray(); + for(sal_Int32 nEntry = 0; nEntry < rBox.get_count(); ++nEntry) + pEntries[nEntry] = rBox.get_text(nEntry); + rConfig.SetGreetings(eType, aEntries); + rConfig.SetCurrentGreeting(eType, rBox.get_active()); +} + +IMPL_LINK_NOARG(SwGreetingsHandler, IndividualHdl_Impl, weld::ToggleButton&, void) +{ + bool bIndividual = m_xPersonalizedCB->get_sensitive() && m_xPersonalizedCB->get_active(); + m_xFemaleFT->set_sensitive(bIndividual); + m_xFemaleLB->set_sensitive(bIndividual); + m_xFemalePB->set_sensitive(bIndividual); + m_xMaleFT->set_sensitive(bIndividual); + m_xMaleLB->set_sensitive(bIndividual); + m_xMalePB->set_sensitive(bIndividual); + m_xFemaleFI->set_sensitive(bIndividual); + m_xFemaleColumnFT->set_sensitive(bIndividual); + m_xFemaleColumnLB->set_sensitive(bIndividual); + m_xFemaleFieldFT->set_sensitive(bIndividual); + m_xFemaleFieldCB->set_sensitive(bIndividual); + + if( m_bIsTabPage ) + { + m_rConfigItem.SetIndividualGreeting(bIndividual, false); + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE)); + } + UpdatePreview(); +} + +IMPL_LINK(SwGreetingsHandler, GreetingHdl_Impl, weld::Button&, rButton, void) +{ + std::unique_ptr<SwCustomizeAddressBlockDialog> xDlg(new SwCustomizeAddressBlockDialog(&rButton, m_rConfigItem, + &rButton == m_xMalePB.get() ? + SwCustomizeAddressBlockDialog::GREETING_MALE : + SwCustomizeAddressBlockDialog::GREETING_FEMALE )); + if (RET_OK == xDlg->run()) + { + weld::ComboBox* pToInsert = &rButton == m_xMalePB.get() ? m_xMaleLB.get() : m_xFemaleLB.get(); + pToInsert->append_text(xDlg->GetAddress()); + pToInsert->set_active(pToInsert->get_count() - 1); + if(m_bIsTabPage) + { + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE)); + } + UpdatePreview(); + } +} + +void SwGreetingsHandler::UpdatePreview() +{ + //the base class does nothing +} + +IMPL_LINK_NOARG(SwMailMergeGreetingsPage, AssignHdl_Impl, weld::Button&, void) +{ + const OUString sPreview(m_xFemaleLB->get_active_text() + "\n" + m_xMaleLB->get_active_text()); + SwAssignFieldsDialog aDlg(m_pWizard->getDialog(), m_rConfigItem, sPreview, false); + if (RET_OK == aDlg.run()) + { + UpdatePreview(); + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE)); + } +} + +IMPL_LINK_NOARG(SwMailMergeGreetingsPage, GreetingSelectListBoxHdl_Impl, weld::ComboBox&, void) +{ + UpdatePreview(); +} + +IMPL_LINK_NOARG(SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl, weld::ComboBox&, void) +{ + UpdatePreview(); +} + +void SwMailMergeGreetingsPage::UpdatePreview() +{ + //find out which type of greeting should be selected: + bool bFemale = false; + bool bNoValue = !m_xFemaleColumnLB->get_sensitive(); + if( !bNoValue ) + { + const OUString sFemaleValue = m_xFemaleFieldCB->get_active_text(); + const OUString sFemaleColumn = m_xFemaleColumnLB->get_active_text(); + Reference< sdbcx::XColumnsSupplier > xColsSupp( m_rConfigItem.GetResultSet(), UNO_QUERY); + Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + if(!sFemaleValue.isEmpty() && !sFemaleColumn.isEmpty() && + xColAccess.is() && + xColAccess->hasByName(sFemaleColumn)) + { + //get the content and exchange it in the address string + Any aCol = xColAccess->getByName(sFemaleColumn); + Reference< sdb::XColumn > xColumn; + aCol >>= xColumn; + if(xColumn.is()) + { + try + { + bFemale = xColumn->getString() == sFemaleValue; + + //no last name value marks the greeting also as neutral + const OUString sLastNameColumn = + m_rConfigItem.GetAssignedColumn(MM_PART_LASTNAME); + if ( xColAccess->hasByName(sLastNameColumn) ) + { + aCol = xColAccess->getByName(sLastNameColumn); + aCol >>= xColumn; + bNoValue = xColumn->getString().isEmpty(); + } + } + catch (const sdbc::SQLException&) + { + OSL_FAIL("SQLException caught"); + } + } + } + } + + OUString sPreview = bFemale ? m_xFemaleLB->get_active_text() : + bNoValue ? m_xNeutralCB->get_active_text() : m_xMaleLB->get_active_text(); + + sPreview = SwAddressPreview::FillData(sPreview, m_rConfigItem); + m_xPreview->SetAddress(sPreview); +} + +void SwGreetingsHandler::Contains(bool bContainsGreeting) +{ + m_xPersonalizedCB->set_sensitive(bContainsGreeting); + bool bEnablePersonal = bContainsGreeting && m_xPersonalizedCB->get_active(); + m_xFemaleFT->set_sensitive(bEnablePersonal); + m_xFemaleLB->set_sensitive(bEnablePersonal); + m_xFemalePB->set_sensitive(bEnablePersonal); + m_xMaleFT->set_sensitive(bEnablePersonal); + m_xMaleLB->set_sensitive(bEnablePersonal); + m_xMalePB->set_sensitive(bEnablePersonal); + m_xFemaleFI->set_sensitive(bEnablePersonal); + m_xFemaleColumnFT->set_sensitive(bEnablePersonal); + m_xFemaleColumnLB->set_sensitive(bEnablePersonal); + m_xFemaleFieldFT->set_sensitive(bEnablePersonal); + m_xFemaleFieldCB->set_sensitive(bEnablePersonal); + m_xNeutralFT->set_sensitive(bContainsGreeting); + m_xNeutralCB->set_sensitive(bContainsGreeting); +} + +SwMailMergeGreetingsPage::SwMailMergeGreetingsPage(weld::Container* pPage, SwMailMergeWizard* pWizard) + : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmsalutationpage.ui", "MMSalutationPage") + , SwGreetingsHandler(pWizard->GetConfigItem(), *m_xBuilder) + , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin"))) + , m_xPreviewFI(m_xBuilder->weld_label("previewft")) + , m_xAssignPB(m_xBuilder->weld_button("assign")) + , m_xDocumentIndexFI(m_xBuilder->weld_label("documentindex")) + , m_xPrevSetIB(m_xBuilder->weld_button("prev")) + , m_xNextSetIB(m_xBuilder->weld_button("next")) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreview)) +{ + m_pWizard = pWizard; + + Size aSize(m_xPreview->GetDrawingArea()->get_ref_device().LogicToPixel(Size(186, 21), MapMode(MapUnit::MapAppFont))); + m_xPreviewWIN->set_size_request(aSize.Width(), aSize.Height()); + m_sDocument = m_xDocumentIndexFI->get_label(); + + m_bIsTabPage = true; + + m_xGreetingLineCB->connect_toggled(LINK(this, SwMailMergeGreetingsPage, ContainsHdl_Impl)); + Link<weld::ToggleButton&,void> aIndividualLink = LINK(this, SwGreetingsHandler, IndividualHdl_Impl); + m_xPersonalizedCB->connect_toggled(aIndividualLink); + Link<weld::Button&,void> aGreetingLink = LINK(this, SwGreetingsHandler, GreetingHdl_Impl); + m_xFemalePB->connect_clicked(aGreetingLink); + m_xMalePB->connect_clicked(aGreetingLink); + m_xAssignPB->connect_clicked(LINK(this, SwMailMergeGreetingsPage, AssignHdl_Impl)); + Link<weld::ComboBox&,void> aLBoxLink2 = LINK(this, SwMailMergeGreetingsPage, GreetingSelectListBoxHdl_Impl); + m_xFemaleLB->connect_changed(aLBoxLink2); + m_xMaleLB->connect_changed(aLBoxLink2); + m_xFemaleColumnLB->connect_changed(aLBoxLink2); + m_xFemaleFieldCB->connect_changed(LINK(this, SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl)); + m_xNeutralCB->connect_changed(LINK(this, SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl)); + + Link<weld::Button&,void> aDataLink = LINK(this, SwMailMergeGreetingsPage, InsertDataHdl_Impl); + m_xPrevSetIB->connect_clicked(aDataLink); + m_xNextSetIB->connect_clicked(aDataLink); + + m_xGreetingLineCB->set_active(m_rConfigItem.IsGreetingLine(false)); + m_xPersonalizedCB->set_active(m_rConfigItem.IsIndividualGreeting(false)); + ContainsHdl_Impl(*m_xGreetingLineCB); + aIndividualLink.Call(*m_xPersonalizedCB); + + lcl_FillGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE); + lcl_FillGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE); + lcl_FillGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL); + + m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", "1")); +} + +SwMailMergeGreetingsPage::~SwMailMergeGreetingsPage() +{ + m_xPreviewWIN.reset(); + m_xPreview.reset(); +} + +void SwMailMergeGreetingsPage::Activate() +{ + //try to find the gender setting + m_xFemaleColumnLB->clear(); + Reference< sdbcx::XColumnsSupplier > xColsSupp = m_rConfigItem.GetColumnsSupplier(); + if(xColsSupp.is()) + { + Reference < container::XNameAccess> xColAccess = xColsSupp->getColumns(); + const Sequence< OUString > aColumns = xColAccess->getElementNames(); + for(const auto& rColumn : aColumns) + m_xFemaleColumnLB->append_text(rColumn); + } + + m_xFemaleColumnLB->set_active_text(m_rConfigItem.GetAssignedColumn(MM_PART_GENDER)); + m_xFemaleColumnLB->save_value(); + + m_xFemaleFieldCB->set_entry_text(m_rConfigItem.GetFemaleGenderValue()); + m_xFemaleFieldCB->save_value(); + + UpdatePreview(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE)); +} + +bool SwMailMergeGreetingsPage::commitPage( ::vcl::WizardTypes::CommitPageReason ) +{ + if (m_xFemaleColumnLB->get_value_changed_from_saved()) + { + const SwDBData& rDBData = m_rConfigItem.GetCurrentDBData(); + Sequence< OUString> aAssignment = m_rConfigItem.GetColumnAssignment( rDBData ); + if(aAssignment.getLength() <= MM_PART_GENDER) + aAssignment.realloc(MM_PART_GENDER + 1); + aAssignment[MM_PART_GENDER] = m_xFemaleColumnLB->get_active_text(); + m_rConfigItem.SetColumnAssignment( rDBData, aAssignment ); + } + if (m_xFemaleFieldCB->get_value_changed_from_saved()) + m_rConfigItem.SetFemaleGenderValue(m_xFemaleFieldCB->get_active_text()); + + lcl_StoreGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE); + lcl_StoreGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE); + + sal_Int32 nCurrentTextPos = m_xNeutralCB->find_text(m_xNeutralCB->get_active_text()); + if (nCurrentTextPos == -1) + { + m_xNeutralCB->append_text(m_xNeutralCB->get_active_text()); + m_xNeutralCB->set_active(m_xNeutralCB->get_count() - 1); + } + lcl_StoreGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL); + m_rConfigItem.SetGreetingLine(m_xGreetingLineCB->get_active(), false); + m_rConfigItem.SetIndividualGreeting(m_xPersonalizedCB->get_active(), false); + return true; +} + +IMPL_LINK(SwMailMergeGreetingsPage, ContainsHdl_Impl, weld::ToggleButton&, rBox, void) +{ + bool bContainsGreeting = rBox.get_active(); + SwGreetingsHandler::Contains(bContainsGreeting); + m_xPreviewFI->set_sensitive(bContainsGreeting); + m_xPreviewWIN->set_sensitive(bContainsGreeting); + m_xAssignPB->set_sensitive(bContainsGreeting); + m_xDocumentIndexFI->set_sensitive(bContainsGreeting); + m_xPrevSetIB->set_sensitive(bContainsGreeting); + m_xNextSetIB->set_sensitive(bContainsGreeting); + m_rConfigItem.SetGreetingLine(m_xGreetingLineCB->get_active(), false); + m_pWizard->UpdateRoadmap(); + m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE)); +} + +IMPL_LINK(SwMailMergeGreetingsPage, InsertDataHdl_Impl, weld::Button&, rButton, void) +{ + bool bNext = &rButton == m_xNextSetIB.get(); + sal_Int32 nPos = m_rConfigItem.GetResultSetPosition(); + m_rConfigItem.MoveResultSet( bNext ? ++nPos : --nPos); + nPos = m_rConfigItem.GetResultSetPosition(); + bool bEnable = true; + if(nPos < 1) + { + bEnable = false; + nPos = 1; + } + else + UpdatePreview(); + m_xPrevSetIB->set_sensitive(bEnable); + m_xNextSetIB->set_sensitive(bEnable); + m_xDocumentIndexFI->set_sensitive(bEnable); + m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", OUString::number(nPos))); +} + +SwMailBodyDialog::SwMailBodyDialog(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/mmmailbody.ui", "MailBodyDialog") + , SwGreetingsHandler(*GetActiveView()->GetMailMergeConfigItem(), *m_xBuilder) + , m_xBodyFT(m_xBuilder->weld_label("bodyft")) + , m_xBodyMLE(m_xBuilder->weld_text_view("bodymle")) + , m_xOK(m_xBuilder->weld_button("ok")) +{ + m_bIsTabPage = false; + m_xBodyMLE->set_size_request(m_xBodyMLE->get_approximate_digit_width() * 45, + m_xBodyMLE->get_height_rows(6)); + m_xGreetingLineCB->connect_toggled(LINK(this, SwMailBodyDialog, ContainsHdl_Impl)); + Link<weld::ToggleButton&,void> aIndividualLink = LINK(this, SwGreetingsHandler, IndividualHdl_Impl); + m_xPersonalizedCB->connect_toggled(aIndividualLink); + Link<weld::Button&,void> aGreetingLink = LINK(this, SwGreetingsHandler, GreetingHdl_Impl); + m_xFemalePB->connect_clicked(aGreetingLink); + m_xMalePB->connect_clicked(aGreetingLink); + m_xOK->connect_clicked(LINK(this, SwMailBodyDialog, OKHdl)); + + m_xGreetingLineCB->set_active(m_rConfigItem.IsGreetingLine(true)); + m_xPersonalizedCB->set_active(m_rConfigItem.IsIndividualGreeting(true)); + ContainsHdl_Impl(*m_xGreetingLineCB); + aIndividualLink.Call(*m_xPersonalizedCB); + + lcl_FillGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE); + lcl_FillGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE); + lcl_FillGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL); + + //try to find the gender setting + m_xFemaleColumnLB->clear(); + Reference< sdbcx::XColumnsSupplier > xColsSupp = m_rConfigItem.GetColumnsSupplier(); + if(xColsSupp.is()) + { + Reference < container::XNameAccess> xColAccess = xColsSupp->getColumns(); + const Sequence< OUString > aColumns = xColAccess->getElementNames(); + for(const auto& rColumn : aColumns) + m_xFemaleColumnLB->append_text(rColumn); + } + + m_xFemaleColumnLB->set_active_text(m_rConfigItem.GetAssignedColumn(MM_PART_GENDER)); + m_xFemaleColumnLB->save_value(); + + m_xFemaleFieldCB->set_entry_text(m_rConfigItem.GetFemaleGenderValue()); + m_xFemaleFieldCB->save_value(); +} + +SwMailBodyDialog::~SwMailBodyDialog() +{ +} + +IMPL_LINK(SwMailBodyDialog, ContainsHdl_Impl, weld::ToggleButton&, rBox, void) +{ + SwGreetingsHandler::Contains(rBox.get_active()); + m_rConfigItem.SetGreetingLine(rBox.get_active(), true); +} + +IMPL_LINK_NOARG(SwMailBodyDialog, OKHdl, weld::Button&, void) +{ + m_rConfigItem.SetGreetingLine( + m_xGreetingLineCB->get_active(), false); + m_rConfigItem.SetIndividualGreeting( + m_xPersonalizedCB->get_active(), false); + + if (m_xFemaleColumnLB->get_value_changed_from_saved()) + { + const SwDBData& rDBData = m_rConfigItem.GetCurrentDBData(); + Sequence< OUString> aAssignment = m_rConfigItem.GetColumnAssignment( rDBData ); + sal_Int32 nPos = m_xFemaleColumnLB->get_active(); + if(aAssignment.getLength() < MM_PART_GENDER) + aAssignment.realloc(MM_PART_GENDER); + if( nPos > 0 ) + aAssignment[MM_PART_GENDER] = m_xFemaleColumnLB->get_active_text(); + else + aAssignment[MM_PART_GENDER].clear(); + m_rConfigItem.SetColumnAssignment( rDBData, aAssignment ); + } + if (m_xFemaleFieldCB->get_value_changed_from_saved()) + m_rConfigItem.SetFemaleGenderValue(m_xFemaleFieldCB->get_active_text()); + + m_xDialog->response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmgreetingspage.hxx b/sw/source/ui/dbui/mmgreetingspage.hxx new file mode 100644 index 000000000..14284d322 --- /dev/null +++ b/sw/source/ui/dbui/mmgreetingspage.hxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_DBUI_MMGREETINGSPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMGREETINGSPAGE_HXX + +#include <vcl/wizardmachine.hxx> +#include <sfx2/basedlgs.hxx> +#include <mailmergehelper.hxx> +#include <vcl/weld.hxx> + +#include <mailmergewizard.hxx> + +class SwMailMergeWizard; + +class SwGreetingsHandler +{ +protected: + SwMailMergeWizard* m_pWizard; + /// The mail merge state, available even when m_pWizard is nullptr. + SwMailMergeConfigItem& m_rConfigItem; + bool m_bIsTabPage; + + std::unique_ptr<weld::CheckButton> m_xGreetingLineCB; + std::unique_ptr<weld::CheckButton> m_xPersonalizedCB; + std::unique_ptr<weld::Label> m_xFemaleFT; + std::unique_ptr<weld::ComboBox> m_xFemaleLB; + std::unique_ptr<weld::Button> m_xFemalePB; + std::unique_ptr<weld::Label> m_xMaleFT; + std::unique_ptr<weld::ComboBox> m_xMaleLB; + std::unique_ptr<weld::Button> m_xMalePB; + std::unique_ptr<weld::Label> m_xFemaleFI; + std::unique_ptr<weld::Label> m_xFemaleColumnFT; + std::unique_ptr<weld::ComboBox> m_xFemaleColumnLB; + std::unique_ptr<weld::Label> m_xFemaleFieldFT; + std::unique_ptr<weld::ComboBox> m_xFemaleFieldCB; + std::unique_ptr<weld::Label> m_xNeutralFT; + std::unique_ptr<weld::ComboBox> m_xNeutralCB; + + SwGreetingsHandler(SwMailMergeConfigItem& rConfigItem, weld::Builder& rBuilder) + : m_pWizard(nullptr) + , m_rConfigItem(rConfigItem) + , m_bIsTabPage(false) + , m_xGreetingLineCB(rBuilder.weld_check_button("greeting")) + , m_xPersonalizedCB(rBuilder.weld_check_button("personalized")) + , m_xFemaleFT(rBuilder.weld_label("femaleft")) + , m_xFemaleLB(rBuilder.weld_combo_box("female")) + , m_xFemalePB(rBuilder.weld_button("newfemale")) + , m_xMaleFT(rBuilder.weld_label("maleft")) + , m_xMaleLB(rBuilder.weld_combo_box("male")) + , m_xMalePB(rBuilder.weld_button("newmale")) + , m_xFemaleFI(rBuilder.weld_label("femalefi")) + , m_xFemaleColumnFT(rBuilder.weld_label("femalecolft")) + , m_xFemaleColumnLB(rBuilder.weld_combo_box("femalecol")) + , m_xFemaleFieldFT(rBuilder.weld_label("femalefieldft")) + , m_xFemaleFieldCB(rBuilder.weld_combo_box("femalefield")) + , m_xNeutralFT(rBuilder.weld_label("generalft")) + , m_xNeutralCB(rBuilder.weld_combo_box("general")) + { + } + + ~SwGreetingsHandler() {} + + DECL_LINK(IndividualHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(GreetingHdl_Impl, weld::Button&, void); + + void Contains(bool bContainsGreeting); + virtual void UpdatePreview(); +}; + +class SwMailMergeGreetingsPage : public vcl::OWizardPage + , public SwGreetingsHandler +{ + std::unique_ptr<SwAddressPreview> m_xPreview; + std::unique_ptr<weld::Label> m_xPreviewFI; + std::unique_ptr<weld::Button> m_xAssignPB; + std::unique_ptr<weld::Label> m_xDocumentIndexFI; + std::unique_ptr<weld::Button> m_xPrevSetIB; + std::unique_ptr<weld::Button> m_xNextSetIB; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + + OUString m_sDocument; + + DECL_LINK(ContainsHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(InsertDataHdl_Impl, weld::Button&, void); + DECL_LINK(GreetingSelectComboBoxHdl_Impl, weld::ComboBox&, void); + DECL_LINK(GreetingSelectListBoxHdl_Impl, weld::ComboBox&, void); + DECL_LINK(AssignHdl_Impl, weld::Button&, void); + + virtual void UpdatePreview() override; + virtual void Activate() override; + virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override; +public: + SwMailMergeGreetingsPage(weld::Container* pPage, SwMailMergeWizard* pWizard); + virtual ~SwMailMergeGreetingsPage() override; +}; + +class SwMailBodyDialog : public SfxDialogController, public SwGreetingsHandler +{ + std::unique_ptr<weld::Label> m_xBodyFT; + std::unique_ptr<weld::TextView> m_xBodyMLE; + std::unique_ptr<weld::Button> m_xOK; + + DECL_LINK(ContainsHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(OKHdl, weld::Button&, void); +public: + SwMailBodyDialog(weld::Window* pParent); + virtual ~SwMailBodyDialog() override; + + void SetBody(const OUString& rBody ) { m_xBodyMLE->set_text(rBody); } + OUString GetBody() const { return m_xBodyMLE->get_text(); } +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmlayoutpage.cxx b/sw/source/ui/dbui/mmlayoutpage.cxx new file mode 100644 index 000000000..bd8070c5e --- /dev/null +++ b/sw/source/ui/dbui/mmlayoutpage.cxx @@ -0,0 +1,695 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "mmlayoutpage.hxx" +#include <mailmergewizard.hxx> +#include <mmconfigitem.hxx> +#include <mailmergehelper.hxx> +#include <unotools.hxx> +#include <comphelper/string.hxx> +#include <i18nutil/unicode.hxx> +#include <unotools/tempfile.hxx> +#include <uitool.hxx> +#include <view.hxx> +#include <swundo.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <svtools/unitconv.hxx> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/view/DocumentZoomType.hpp> +#include <fldmgr.hxx> +#include <fldbas.hxx> +#include <unotxdoc.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <wrtsh.hxx> +#include <fmtsrnd.hxx> +#include <pagedesc.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmtfsize.hxx> +#include <editeng/boxitem.hxx> +#include <osl/file.hxx> +#include <vcl/settings.hxx> +#include <unoprnms.hxx> +#include <iodetect.hxx> + +#include <dbui.hrc> + +using namespace osl; +using namespace svt; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::view; + +#define DEFAULT_LEFT_DISTANCE (MM50*5) // 2,5 cm +#define DEFAULT_TOP_DISTANCE (MM50*11) // 5,5 cm +#define GREETING_TOP_DISTANCE (MM50*25) //12,5 cm +#define DEFAULT_ADDRESS_WIDTH (MM50*15)// 7,5 cm +#define DEFAULT_ADDRESS_HEIGHT (MM50*7) // 3,5cm + +SwMailMergeLayoutPage::SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard) + : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmlayoutpage.ui", "MMLayoutPage") + , m_pExampleWrtShell(nullptr) + , m_pAddressBlockFormat(nullptr) + , m_bIsGreetingInserted(false) + , m_pWizard(pWizard) + , m_xPosition(m_xBuilder->weld_container("addresspos")) + , m_xAlignToBodyCB(m_xBuilder->weld_check_button("align")) + , m_xLeftFT(m_xBuilder->weld_label("leftft")) + , m_xLeftMF(m_xBuilder->weld_metric_spin_button("left", FieldUnit::CM)) + , m_xTopMF(m_xBuilder->weld_metric_spin_button("top", FieldUnit::CM)) + , m_xGreetingLine(m_xBuilder->weld_container("greetingspos")) + , m_xUpPB(m_xBuilder->weld_button("up")) + , m_xDownPB(m_xBuilder->weld_button("down")) + , m_xZoomLB(m_xBuilder->weld_combo_box("zoom")) +{ + std::shared_ptr<const SfxFilter> pSfxFlt = + SwDocShell::Factory().GetFilterContainer()-> + GetFilter4FilterName("writer8", SfxFilterFlags::EXPORT); + + //save the current document into a temporary file + { + //temp file needs its own block + //creating with extension is not supported by a static method :-( + OUString const sExt( + comphelper::string::stripStart(pSfxFlt->GetDefaultExtension(),'*')); + utl::TempFile aTempFile( OUString(), true, &sExt ); + m_sExampleURL = aTempFile.GetURL(); + aTempFile.EnableKillingFile(); + } + SwView* pView = m_pWizard->GetSwView(); + uno::Sequence< beans::PropertyValue > aValues(2); + beans::PropertyValue* pValues = aValues.getArray(); + pValues[0].Name = "FilterName"; + pValues[0].Value <<= pSfxFlt->GetFilterName(); + // Don't save embedded data set! It would steal it from current document. + pValues[1].Name = "NoEmbDataSet"; + pValues[1].Value <<= true; + + uno::Reference< frame::XStorable > xStore( pView->GetDocShell()->GetModel(), uno::UNO_QUERY); + xStore->storeToURL( m_sExampleURL, aValues ); + + Link<SwOneExampleFrame&,void> aLink(LINK(this, SwMailMergeLayoutPage, PreviewLoadedHdl_Impl)); + m_xExampleFrame.reset(new SwOneExampleFrame(EX_SHOW_DEFAULT_PAGE, &aLink, &m_sExampleURL)); + m_xExampleContainerWIN.reset(new weld::CustomWeld(*m_xBuilder, "example", *m_xExampleFrame)); + + Size aSize = m_xExampleFrame->GetDrawingArea()->get_ref_device().LogicToPixel( + Size(124, 159), MapMode(MapUnit::MapAppFont)); + m_xExampleFrame->set_size_request(aSize.Width(), aSize.Height()); + + m_xExampleContainerWIN->hide(); + + m_xLeftMF->set_value(m_xLeftMF->normalize(DEFAULT_LEFT_DISTANCE), FieldUnit::TWIP); + m_xTopMF->set_value(m_xTopMF->normalize(DEFAULT_TOP_DISTANCE), FieldUnit::TWIP); + + const LanguageTag& rLang = Application::GetSettings().GetUILanguageTag(); + m_xZoomLB->append_text(unicode::formatPercent(50, rLang)); + m_xZoomLB->append_text(unicode::formatPercent(75, rLang)); + m_xZoomLB->append_text(unicode::formatPercent(100, rLang)); + m_xZoomLB->set_active(0); //page size + m_xZoomLB->connect_changed(LINK(this, SwMailMergeLayoutPage, ZoomHdl_Impl)); + + Link<weld::MetricSpinButton&,void> aFrameHdl = LINK(this, SwMailMergeLayoutPage, ChangeAddressHdl_Impl); + m_xLeftMF->connect_value_changed(aFrameHdl); + m_xTopMF->connect_value_changed(aFrameHdl); + + FieldUnit eFieldUnit = ::GetDfltMetric(false); + ::SetFieldUnit( *m_xLeftMF, eFieldUnit ); + ::SetFieldUnit( *m_xTopMF, eFieldUnit ); + + Link<weld::Button&,void> aUpDownHdl = LINK(this, SwMailMergeLayoutPage, GreetingsHdl_Impl ); + m_xUpPB->connect_clicked(aUpDownHdl); + m_xDownPB->connect_clicked(aUpDownHdl); + m_xAlignToBodyCB->connect_toggled(LINK(this, SwMailMergeLayoutPage, AlignToTextHdl_Impl)); + m_xAlignToBodyCB->set_active(true); +} + +SwMailMergeLayoutPage::~SwMailMergeLayoutPage() +{ + File::remove( m_sExampleURL ); +} + +void SwMailMergeLayoutPage::Activate() +{ + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + bool bGreetingLine = rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted(); + bool bAddressBlock = rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted(); + + m_xPosition->set_sensitive(bAddressBlock); + AlignToTextHdl_Impl(*m_xAlignToBodyCB); + + m_xGreetingLine->set_sensitive(bGreetingLine); + + //check if greeting and/or address frame have to be inserted/removed + if(m_pExampleWrtShell) // initially there's nothing to check + { + if(!rConfigItem.IsGreetingInserted() && + m_bIsGreetingInserted != bGreetingLine ) + { + if( m_bIsGreetingInserted ) + { + m_pExampleWrtShell->DelFullPara(); + m_bIsGreetingInserted = false; + } + else + { + InsertGreeting(*m_pExampleWrtShell, m_pWizard->GetConfigItem(), true); + m_bIsGreetingInserted = true; + } + } + if(!rConfigItem.IsAddressInserted() && + rConfigItem.IsAddressBlock() != ( nullptr != m_pAddressBlockFormat )) + { + if( m_pAddressBlockFormat ) + { + m_pExampleWrtShell->Push(); + m_pExampleWrtShell->GotoFly( m_pAddressBlockFormat->GetName() ); + m_pExampleWrtShell->DelRight(); + m_pAddressBlockFormat = nullptr; + m_pExampleWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + long nLeft = static_cast< long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP))); + long nTop = static_cast< long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP))); + m_pAddressBlockFormat = InsertAddressFrame( + *m_pExampleWrtShell, m_pWizard->GetConfigItem(), + Point(nLeft, nTop), + m_xAlignToBodyCB->get_active(), true); + } + } + m_xExampleFrame->Invalidate(); + } +} + +bool SwMailMergeLayoutPage::commitPage(::vcl::WizardTypes::CommitPageReason eReason) +{ + //now insert the frame and the greeting + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + if (eReason == ::vcl::WizardTypes::eTravelForward || eReason == ::vcl::WizardTypes::eFinish) + { + long nLeft = static_cast< long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP))); + long nTop = static_cast< long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP))); + InsertAddressAndGreeting( + m_pWizard->GetSwView(), + rConfigItem, + Point(nLeft, nTop), + m_xAlignToBodyCB->get_active()); + } + return true; +} + +SwFrameFormat* SwMailMergeLayoutPage::InsertAddressAndGreeting(SwView const * pView, + SwMailMergeConfigItem& rConfigItem, + const Point& rAddressPosition, + bool bAlignToBody) +{ + SwFrameFormat* pAddressBlockFormat = nullptr; + pView->GetWrtShell().StartUndo(SwUndoId::INSERT); + if(rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted()) + { + //insert the frame + Point aAddressPosition(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE); + if(rAddressPosition.X() > 0 && rAddressPosition.Y() > 0) + aAddressPosition = rAddressPosition; + pAddressBlockFormat = InsertAddressFrame( pView->GetWrtShell(), + rConfigItem, + aAddressPosition, bAlignToBody, false); + rConfigItem.SetAddressInserted(); + } + //now the greeting + if(rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted()) + { + InsertGreeting( pView->GetWrtShell(), rConfigItem, false); + rConfigItem.SetGreetingInserted(); + } + pView->GetWrtShell().EndUndo(SwUndoId::INSERT); + return pAddressBlockFormat; +} + +SwFrameFormat* SwMailMergeLayoutPage::InsertAddressFrame( + SwWrtShell& rShell, + SwMailMergeConfigItem const & rConfigItem, + const Point& rDestination, + bool bAlignLeft, + bool bExample) +{ + // insert the address block and the greeting line + SfxItemSet aSet( + rShell.GetAttrPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR, + RES_BOX, RES_BOX>{} ); + aSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1)); + if(bAlignLeft) + aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA )); + else + aSet.Put(SwFormatHoriOrient( rDestination.X(), text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME )); + aSet.Put(SwFormatVertOrient( rDestination.Y(), text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME )); + aSet.Put(SwFormatFrameSize( SwFrameSize::Minimum, DEFAULT_ADDRESS_WIDTH, DEFAULT_ADDRESS_HEIGHT )); + // the example gets a border around the frame, the real document doesn't get one + if(!bExample) + aSet.Put(SvxBoxItem( RES_BOX )); + aSet.Put(SwFormatSurround( css::text::WrapTextMode_NONE )); + + rShell.NewFlyFrame(aSet, true ); + SwFrameFormat* pRet = rShell.GetFlyFrameFormat(); + OSL_ENSURE( pRet, "Fly not inserted" ); + + rShell.UnSelectFrame(); + const Sequence< OUString> aBlocks = rConfigItem.GetAddressBlocks(); + if(bExample) + { + rShell.Insert(aBlocks[0]); + } + else + { + //the placeholders should be replaced by the appropriate fields + SwFieldMgr aFieldMgr(&rShell); + //create a database string source.command.commandtype.column + const SwDBData& rData = rConfigItem.GetCurrentDBData(); + OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM) + + rData.sCommand + OUStringChar(DB_DELIM)); + const OUString sDatabaseConditionPrefix(sDBName.replace(DB_DELIM, '.')); + sDBName += OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM); + + // if only the country is in an address line the + // paragraph has to be hidden depending on the + // IsIncludeCountry()/GetExcludeCountry() settings + + bool bIncludeCountry = rConfigItem.IsIncludeCountry(); + bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs(); + const OUString rExcludeCountry = rConfigItem.GetExcludeCountry(); + bool bSpecialReplacementForCountry = (!bIncludeCountry || !rExcludeCountry.isEmpty()); + + const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders(); + Sequence< OUString> aAssignment = + rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() ); + const OUString* pAssignment = aAssignment.getConstArray(); + const OUString sCountryColumn( + (aAssignment.getLength() > MM_PART_COUNTRY && !aAssignment[MM_PART_COUNTRY].isEmpty()) + ? aAssignment[MM_PART_COUNTRY] + : rHeaders[MM_PART_COUNTRY].first); + + OUString sHideParagraphsExpression; + SwAddressIterator aIter(aBlocks[0]); + while(aIter.HasMore()) + { + SwMergeAddressItem aItem = aIter.Next(); + if(aItem.bIsColumn) + { + OUString sConvertedColumn = aItem.sText; + auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()), + static_cast<sal_uInt32>(aAssignment.getLength())); + for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn) + { + if (rHeaders[nColumn].first == aItem.sText && + !pAssignment[nColumn].isEmpty()) + { + sConvertedColumn = pAssignment[nColumn]; + break; + } + } + const OUString sDB(sDBName + sConvertedColumn); + + if(!sHideParagraphsExpression.isEmpty()) + sHideParagraphsExpression += " AND "; + sHideParagraphsExpression += "![" + sDatabaseConditionPrefix + sConvertedColumn + "]"; + + if( bSpecialReplacementForCountry && sCountryColumn == sConvertedColumn ) + { + // now insert a hidden paragraph field + if( !rExcludeCountry.isEmpty() ) + { + const OUString sExpression("[" + sDatabaseConditionPrefix + sCountryColumn + "]"); + SwInsertField_Data aData(SwFieldTypesEnum::ConditionalText, 0, + sExpression + " != \"" + rExcludeCountry + "\"", + sExpression, + 0, &rShell ); + aFieldMgr.InsertField( aData ); + } + else + { + SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, "", "", 0, &rShell ); + aFieldMgr.InsertField( aData ); + } + } + else + { + SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, sDB, OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + } + else if(!aItem.bIsReturn) + { + rShell.Insert(aItem.sText); + } + else + { + if(bHideEmptyParagraphs) + { + SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + sHideParagraphsExpression.clear(); + //now add a new paragraph + rShell.SplitNode(); + } + } + if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty()) + { + SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + } + return pRet; +} + +void SwMailMergeLayoutPage::InsertGreeting(SwWrtShell& rShell, SwMailMergeConfigItem const & rConfigItem, bool bExample) +{ + //set the cursor to the desired position - if no text content is here then + //new paragraphs are inserted + const SwRect& rPageRect = rShell.GetAnyCurRect(CurRectType::Page); + const Point aGreetingPos( DEFAULT_LEFT_DISTANCE + rPageRect.Left(), GREETING_TOP_DISTANCE ); + + const bool bRet = rShell.SetShadowCursorPos( aGreetingPos, SwFillMode::TabSpace ); + + if(!bRet) + { + //there's already text at the desired position + //go to start of the doc, directly! + rShell.SttEndDoc(true); + //and go by paragraph until the position is reached + long nYPos = rShell.GetCharRect().Top(); + while(nYPos < GREETING_TOP_DISTANCE) + { + if(!rShell.FwdPara()) + break; + nYPos = rShell.GetCharRect().Top(); + } + //text needs to be appended + while(nYPos < GREETING_TOP_DISTANCE) + { + if(!rShell.AppendTextNode()) + break; + nYPos = rShell.GetCharRect().Top(); + } + } + else + { + //we may end up inside of a paragraph if the left margin is not at DEFAULT_LEFT_DISTANCE + rShell.MovePara(GoCurrPara, fnParaStart); + } + bool bSplitNode = !rShell.IsEndPara(); + sal_Int32 nMoves = rConfigItem.GetGreetingMoves(); + if( !bExample && 0 != nMoves ) + { + if(nMoves < 0) + { + rShell.MoveParagraph( nMoves ); + } + else + while(nMoves) + { + bool bMoved = rShell.MoveParagraph(); + if(!bMoved) + { + //insert a new paragraph before the greeting line + rShell.SplitNode(); + } + --nMoves; + } + } + //now insert the greeting text - if we have any? + const bool bIndividual = rConfigItem.IsIndividualGreeting(false); + if(bIndividual) + { + //lock expression fields - prevents hiding of the paragraph to insert into + rShell.LockExpFields(); + if(bExample) + { + for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE; + eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender) + { + Sequence< OUString > aEntries = + rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender)); + sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender)); + if( nCurrent >= 0 && nCurrent < aEntries.getLength()) + { + // Greeting + rShell.Insert(aEntries[nCurrent]); + break; + } + } + } + else + { + SwFieldMgr aFieldMgr(&rShell); + //three paragraphs, each with an appropriate hidden paragraph field + //are to be inserted + + //name of the gender column + const OUString sGenderColumn = rConfigItem.GetAssignedColumn(MM_PART_GENDER); + const OUString sNameColumn = rConfigItem.GetAssignedColumn(MM_PART_LASTNAME); + + const OUString& rFemaleGenderValue = rConfigItem.GetFemaleGenderValue(); + bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs(); + const SwDBData& rData = rConfigItem.GetCurrentDBData(); + const OUString sCommonBase(rData.sDataSource + "." + rData.sCommand + "."); + const OUString sConditionBase("[" + sCommonBase + sGenderColumn + "]"); + const OUString sNameColumnBase("[" + sCommonBase + sNameColumn + "]"); + + const OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM) + + rData.sCommand + OUStringChar(DB_DELIM) + + OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM)); + +// Female: [database.sGenderColumn] != "rFemaleGenderValue" && [database.NameColumn] +// Male: [database.sGenderColumn] == "rFemaleGenderValue" && [database.rGenderColumn] +// Neutral: [database.sNameColumn] + OSL_ENSURE(!sGenderColumn.isEmpty() && !rFemaleGenderValue.isEmpty(), + "gender settings not available - how to form the condition?"); + //column used as lastname + for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE; + eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender) + { + Sequence< OUString> aEntries = rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender)); + sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender)); + if( nCurrent >= 0 && nCurrent < aEntries.getLength()) + { + const OUString sGreeting = aEntries[nCurrent]; + OUString sCondition; + OUString sHideParagraphsExpression; + switch(eGender) + { + case SwMailMergeConfigItem::FEMALE: + sCondition = sConditionBase + " != \"" + rFemaleGenderValue + + "\" OR NOT " + sNameColumnBase; + sHideParagraphsExpression = "!" + sNameColumnBase; + break; + case SwMailMergeConfigItem::MALE: + sCondition = sConditionBase + " == \"" + rFemaleGenderValue + + "\" OR NOT " + sNameColumnBase; + break; + case SwMailMergeConfigItem::NEUTRAL: + sCondition = sNameColumnBase; + break; + } + + if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty()) + { + OUString sComplete = "(" + sCondition + ") OR (" + sHideParagraphsExpression + ")"; + SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sComplete, OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + else + { + SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sCondition, OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + //now the text has to be inserted + const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders(); + Sequence< OUString> aAssignment = + rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() ); + const OUString* pAssignment = aAssignment.getConstArray(); + SwAddressIterator aIter(sGreeting); + while(aIter.HasMore()) + { + SwMergeAddressItem aItem = aIter.Next(); + if(aItem.bIsColumn) + { + OUString sConvertedColumn = aItem.sText; + auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()), + static_cast<sal_uInt32>(aAssignment.getLength())); + for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn) + { + if (rHeaders[nColumn].first == aItem.sText && + !pAssignment[nColumn].isEmpty()) + { + sConvertedColumn = pAssignment[nColumn]; + break; + } + } + SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, + sDBName + sConvertedColumn, + OUString(), 0, &rShell); + aFieldMgr.InsertField( aData ); + } + else + { + rShell.Insert(aItem.sText); + } + } + //now add a new paragraph + rShell.SplitNode(); + } + } + + } + rShell.UnlockExpFields(); + } + else + { + Sequence< OUString> aEntries = rConfigItem.GetGreetings(SwMailMergeConfigItem::NEUTRAL); + sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(SwMailMergeConfigItem::NEUTRAL); + // Greeting + rShell.Insert(( nCurrent >= 0 && nCurrent < aEntries.getLength() ) + ? aEntries[nCurrent] : OUString()); + } + // now insert a new paragraph here if necessary + if(bSplitNode) + { + rShell.Push(); + rShell.SplitNode(); + rShell.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + //put the cursor to the start of the paragraph + rShell.SttPara(); + + OSL_ENSURE(nullptr == rShell.GetTableFormat(), "What to do with a table here?"); +} + +IMPL_LINK_NOARG(SwMailMergeLayoutPage, PreviewLoadedHdl_Impl, SwOneExampleFrame&, void) +{ + m_xExampleContainerWIN->show(); + + Reference< XModel > & xModel = m_xExampleFrame->GetModel(); + //now the ViewOptions should be set properly + Reference< XViewSettingsSupplier > xSettings(xModel->getCurrentController(), UNO_QUERY); + m_xViewProperties = xSettings->getViewSettings(); + auto pXDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(xModel); + SwDocShell* pDocShell = pXDoc->GetDocShell(); + m_pExampleWrtShell = pDocShell->GetWrtShell(); + OSL_ENSURE(m_pExampleWrtShell, "No SwWrtShell found!"); + if(!m_pExampleWrtShell) + return; + + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + if(rConfigItem.IsAddressBlock()) + { + m_pAddressBlockFormat = InsertAddressFrame( + *m_pExampleWrtShell, rConfigItem, + Point(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE), + m_xAlignToBodyCB->get_active(), true); + } + if(rConfigItem.IsGreetingLine(false)) + { + InsertGreeting(*m_pExampleWrtShell, rConfigItem, true); + m_bIsGreetingInserted = true; + } + + ZoomHdl_Impl(*m_xZoomLB); + + const SwFormatFrameSize& rPageSize = m_pExampleWrtShell->GetPageDesc( + m_pExampleWrtShell->GetCurPageDesc()).GetMaster().GetFrameSize(); + m_xLeftMF->set_max(rPageSize.GetWidth() - DEFAULT_LEFT_DISTANCE, FieldUnit::NONE); + m_xTopMF->set_max(rPageSize.GetHeight() - DEFAULT_TOP_DISTANCE, FieldUnit::NONE); +} + +IMPL_LINK(SwMailMergeLayoutPage, ZoomHdl_Impl, weld::ComboBox&, rBox, void) +{ + if (m_pExampleWrtShell) + { + sal_Int16 eType = DocumentZoomType::BY_VALUE; + short nZoom = 50; + switch (rBox.get_active()) + { + case 0 : eType = DocumentZoomType::ENTIRE_PAGE; break; + case 1 : nZoom = 50; break; + case 2 : nZoom = 75; break; + case 3 : nZoom = 100; break; + } + Any aZoom; + aZoom <<= eType; + m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + aZoom <<= nZoom; + m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom); + + m_xExampleFrame->Invalidate(); + } +} + +IMPL_LINK_NOARG(SwMailMergeLayoutPage, ChangeAddressHdl_Impl, weld::MetricSpinButton&, void) +{ + if(m_pExampleWrtShell && m_pAddressBlockFormat) + { + long nLeft = static_cast< long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP))); + long nTop = static_cast< long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP))); + + SfxItemSet aSet( + m_pExampleWrtShell->GetAttrPool(), + svl::Items<RES_VERT_ORIENT, RES_ANCHOR>{}); + if (m_xAlignToBodyCB->get_active()) + aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA )); + else + aSet.Put(SwFormatHoriOrient( nLeft, text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME )); + aSet.Put(SwFormatVertOrient( nTop, text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME )); + m_pExampleWrtShell->GetDoc()->SetFlyFrameAttr( *m_pAddressBlockFormat, aSet ); + m_xExampleFrame->Invalidate(); + } +} + +IMPL_LINK(SwMailMergeLayoutPage, GreetingsHdl_Impl, weld::Button&, rButton, void) +{ + bool bDown = &rButton == m_xDownPB.get(); + bool bMoved = m_pExampleWrtShell->MoveParagraph( bDown ? 1 : -1 ); + if (bMoved || bDown) + m_pWizard->GetConfigItem().MoveGreeting(bDown ? 1 : -1 ); + if(!bMoved && bDown) + { + //insert a new paragraph before the greeting line + m_pExampleWrtShell->SplitNode(); + } + m_xExampleFrame->Invalidate(); +} + +IMPL_LINK(SwMailMergeLayoutPage, AlignToTextHdl_Impl, weld::ToggleButton&, rBox, void) +{ + bool bCheck = rBox.get_active() && rBox.get_sensitive(); + m_xLeftFT->set_sensitive(!bCheck); + m_xLeftMF->set_sensitive(!bCheck); + ChangeAddressHdl_Impl( *m_xLeftMF ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmlayoutpage.hxx b/sw/source/ui/dbui/mmlayoutpage.hxx new file mode 100644 index 000000000..a0f732e8a --- /dev/null +++ b/sw/source/ui/dbui/mmlayoutpage.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_UI_DBUI_MMLAYOUTPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMLAYOUTPAGE_HXX + +#include <vcl/wizardmachine.hxx> +#include <mailmergehelper.hxx> +#include <com/sun/star/uno/Reference.h> + +class SwMailMergeWizard; +class SwFrameFormat; +class SwOneExampleFrame; +class SwWrtShell; +class SwView; + +namespace com::sun::star::beans{ class XPropertySet;} + +class SwMailMergeLayoutPage : public vcl::OWizardPage +{ + SwWrtShell* m_pExampleWrtShell; + + OUString m_sExampleURL; + SwFrameFormat* m_pAddressBlockFormat; + + bool m_bIsGreetingInserted; + + SwMailMergeWizard* m_pWizard; + + css::uno::Reference< css::beans::XPropertySet > m_xViewProperties; + + std::unique_ptr<weld::Container> m_xPosition; + std::unique_ptr<weld::CheckButton> m_xAlignToBodyCB; + std::unique_ptr<weld::Label> m_xLeftFT; + std::unique_ptr<weld::MetricSpinButton> m_xLeftMF; + std::unique_ptr<weld::MetricSpinButton> m_xTopMF; + std::unique_ptr<weld::Container> m_xGreetingLine; + std::unique_ptr<weld::Button> m_xUpPB; + std::unique_ptr<weld::Button> m_xDownPB; + std::unique_ptr<weld::ComboBox> m_xZoomLB; + std::unique_ptr<SwOneExampleFrame> m_xExampleFrame; + std::unique_ptr<weld::CustomWeld> m_xExampleContainerWIN; + + DECL_LINK(PreviewLoadedHdl_Impl, SwOneExampleFrame&, void); + DECL_LINK(ZoomHdl_Impl, weld::ComboBox&, void); + DECL_LINK(ChangeAddressHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(GreetingsHdl_Impl, weld::Button&, void); + DECL_LINK(AlignToTextHdl_Impl, weld::ToggleButton&, void); + + static SwFrameFormat* InsertAddressFrame( + SwWrtShell& rShell, + SwMailMergeConfigItem const & rConfigItem, + const Point& rDestination, + bool bAlignToBody, + bool bExample); + static void InsertGreeting(SwWrtShell& rShell, SwMailMergeConfigItem const & rConfigItem, bool bExample); + + virtual void Activate() override; + virtual bool commitPage(::vcl::WizardTypes::CommitPageReason _eReason) override; +public: + SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard); + virtual ~SwMailMergeLayoutPage() override; + + static SwFrameFormat* InsertAddressAndGreeting(SwView const * pView, + SwMailMergeConfigItem& rConfigItem, + const Point& rAddressPos, + bool bAlignToBody); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmoutputtypepage.cxx b/sw/source/ui/dbui/mmoutputtypepage.cxx new file mode 100644 index 000000000..f7153c3bc --- /dev/null +++ b/sw/source/ui/dbui/mmoutputtypepage.cxx @@ -0,0 +1,521 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "mmoutputtypepage.hxx" +#include <mailmergewizard.hxx> +#include <mmconfigitem.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <swtypes.hxx> + +#include <rtl/ref.hxx> +#include <com/sun/star/mail/XSmtpService.hpp> +#include <vcl/idle.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <swunohelper.hxx> +#include <mmresultdialogs.hxx> +#include <maildispatcher.hxx> +#include <imaildsplistener.hxx> + +using namespace ::com::sun::star; + +SwMailMergeOutputTypePage::SwMailMergeOutputTypePage(weld::Container* pPage, SwMailMergeWizard* pWizard) + : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage") + , m_pWizard(pWizard) + , m_xLetterRB(m_xBuilder->weld_radio_button("letter")) + , m_xMailRB(m_xBuilder->weld_radio_button("email")) + , m_xLetterHint(m_xBuilder->weld_label("letterft")) + , m_xMailHint(m_xBuilder->weld_label("emailft")) +{ + Link<weld::ToggleButton&,void> aLink = LINK(this, SwMailMergeOutputTypePage, TypeHdl_Impl); + m_xLetterRB->connect_toggled(aLink); + m_xMailRB->connect_toggled(aLink); + + SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem(); + if(rConfigItem.IsOutputToLetter()) + m_xLetterRB->set_active(true); + else + m_xMailRB->set_active(true); + TypeHdl_Impl(*m_xLetterRB); +} + +SwMailMergeOutputTypePage::~SwMailMergeOutputTypePage() +{ +} + +IMPL_LINK_NOARG(SwMailMergeOutputTypePage, TypeHdl_Impl, weld::ToggleButton&, void) +{ + bool bLetter = m_xLetterRB->get_active(); + m_xLetterHint->set_visible(bLetter); + m_xMailHint->set_visible(!bLetter); + m_pWizard->GetConfigItem().SetOutputToLetter(bLetter); + m_pWizard->UpdateRoadmap(); +} + +struct SwSendMailDialog_Impl +{ + friend class SwSendMailDialog; + ::osl::Mutex aDescriptorMutex; + + std::vector< SwMailDescriptor > aDescriptors; + sal_uInt32 nCurrentDescriptor; + ::rtl::Reference< MailDispatcher > xMailDispatcher; + ::rtl::Reference< IMailDispatcherListener> xMailListener; + uno::Reference< mail::XMailService > xConnectedInMailService; + Idle aRemoveIdle; + + SwSendMailDialog_Impl() : + nCurrentDescriptor(0) + { + aRemoveIdle.SetPriority(TaskPriority::LOWEST); + } + + ~SwSendMailDialog_Impl() + { + // Shutdown must be called when the last reference to the + // mail dispatcher will be released in order to force a + // shutdown of the mail dispatcher thread. + // 'join' with the mail dispatcher thread leads to a + // deadlock (SolarMutex). + if( xMailDispatcher.is() && !xMailDispatcher->isShutdownRequested() ) + xMailDispatcher->shutdown(); + } + const SwMailDescriptor* GetNextDescriptor(); +}; + +const SwMailDescriptor* SwSendMailDialog_Impl::GetNextDescriptor() +{ + ::osl::MutexGuard aGuard(aDescriptorMutex); + if(nCurrentDescriptor < aDescriptors.size()) + { + ++nCurrentDescriptor; + return &aDescriptors[nCurrentDescriptor - 1]; + } + return nullptr; +} + +namespace { + +class SwMailDispatcherListener_Impl : public IMailDispatcherListener +{ + SwSendMailDialog& m_rSendMailDialog; + +public: + explicit SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg); + + virtual void idle() override; + virtual void mailDelivered(uno::Reference< mail::XMailMessage> xMailMessage) override; + virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher, + uno::Reference< mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) override; + + static void DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage ); +}; + +} + +SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg) + : m_rSendMailDialog(rParentDlg) +{ +} + +void SwMailDispatcherListener_Impl::idle() +{ + SolarMutexGuard aGuard; + m_rSendMailDialog.AllMailsSent(); +} + +void SwMailDispatcherListener_Impl::mailDelivered( + uno::Reference< mail::XMailMessage> xMailMessage) +{ + SolarMutexGuard aGuard; + m_rSendMailDialog.DocumentSent( xMailMessage, true, nullptr ); + DeleteAttachments( xMailMessage ); +} + +void SwMailDispatcherListener_Impl::mailDeliveryError( + ::rtl::Reference<MailDispatcher> /*xMailDispatcher*/, + uno::Reference< mail::XMailMessage> xMailMessage, + const OUString& sErrorMessage) +{ + SolarMutexGuard aGuard; + m_rSendMailDialog.DocumentSent( xMailMessage, false, &sErrorMessage ); + DeleteAttachments( xMailMessage ); +} + +void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage ) +{ + const uno::Sequence< mail::MailAttachment > aAttachments = xMessage->getAttachments(); + + for(const auto& rAttachment : aAttachments) + { + try + { + uno::Reference< beans::XPropertySet > xTransferableProperties( rAttachment.Data, uno::UNO_QUERY_THROW); + OUString sURL; + xTransferableProperties->getPropertyValue("URL") >>= sURL; + if(!sURL.isEmpty()) + SWUnoHelper::UCB_DeleteFile( sURL ); + } + catch (const uno::Exception&) + { + } + } +} + +namespace { + +class SwSendWarningBox_Impl : public weld::MessageDialogController +{ + std::unique_ptr<weld::TextView> m_xDetailED; +public: + SwSendWarningBox_Impl(weld::Window* pParent, const OUString& rDetails) + : MessageDialogController(pParent, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid") + , m_xDetailED(m_xBuilder->weld_text_view("errors")) + { + m_xDetailED->set_size_request(80 * m_xDetailED->get_approximate_digit_width(), + 8 * m_xDetailED->get_text_height()); + m_xDetailED->set_text(rDetails); + } +}; + +} + +SwSendMailDialog::SwSendMailDialog(weld::Window *pParent, SwMailMergeConfigItem& rConfigItem) + : GenericDialogController(pParent, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog") + , m_sContinue(SwResId( ST_CONTINUE )) + , m_sSendingTo( SwResId(ST_SENDINGTO )) + , m_sCompleted( SwResId(ST_COMPLETED )) + , m_sFailed( SwResId(ST_FAILED )) + , m_bCancel(false) + , m_bDestructionEnabled(false) + , m_pImpl(new SwSendMailDialog_Impl) + , m_pConfigItem(&rConfigItem) + , m_nExpectedCount(0) + , m_nSendCount(0) + , m_nErrorCount(0) + , m_xTransferStatus(m_xBuilder->weld_label("transferstatus")) + , m_xPaused(m_xBuilder->weld_label("paused")) + , m_xProgressBar(m_xBuilder->weld_progress_bar("progress")) + , m_xErrorStatus(m_xBuilder->weld_label("errorstatus")) + , m_xStatus(m_xBuilder->weld_tree_view("container")) + , m_xStop(m_xBuilder->weld_button("stop")) + , m_xClose(m_xBuilder->weld_button("cancel")) + , m_xExpander(m_xBuilder->weld_expander("details")) +{ + m_sStop = m_xStop->get_label(); + m_sTransferStatus = m_xTransferStatus->get_label(); + m_sErrorStatus = m_xErrorStatus->get_label(); + + Size aSize(m_xStatus->get_approximate_digit_width() * 28, + m_xStatus->get_height_rows(20)); + m_xStatus->set_size_request(aSize.Width(), aSize.Height()); + + m_xStop->connect_clicked(LINK( this, SwSendMailDialog, StopHdl_Impl)); + m_xClose->connect_clicked(LINK( this, SwSendMailDialog, CloseHdl_Impl)); + + std::vector<int> aWidths; + aWidths.push_back(m_xStatus->get_checkbox_column_width()); + aWidths.push_back(aSize.Width()/3 * 2); + m_xStatus->set_column_fixed_widths(aWidths); + + m_xPaused->set_visible(false); + UpdateTransferStatus(); +} + +SwSendMailDialog::~SwSendMailDialog() +{ + if(m_pImpl->xMailDispatcher.is()) + { + try + { + if(m_pImpl->xMailDispatcher->isStarted()) + m_pImpl->xMailDispatcher->stop(); + if(m_pImpl->xConnectedInMailService.is() && m_pImpl->xConnectedInMailService->isConnected()) + m_pImpl->xConnectedInMailService->disconnect(); + + uno::Reference<mail::XMailMessage> xMessage = + m_pImpl->xMailDispatcher->dequeueMailMessage(); + while(xMessage.is()) + { + SwMailDispatcherListener_Impl::DeleteAttachments( xMessage ); + xMessage = m_pImpl->xMailDispatcher->dequeueMailMessage(); + } + } + catch (const uno::Exception&) + { + } + } +} + +void SwSendMailDialog::AddDocument( SwMailDescriptor const & rDesc ) +{ + ::osl::MutexGuard aGuard(m_pImpl->aDescriptorMutex); + m_pImpl->aDescriptors.push_back(rDesc); + // if the dialog is already running then continue sending of documents + if(m_pImpl->xMailDispatcher.is()) + { + IterateMails(); + } +} + +IMPL_LINK( SwSendMailDialog, StopHdl_Impl, weld::Button&, rButton, void ) +{ + m_bCancel = true; + if(m_pImpl->xMailDispatcher.is()) + { + if(m_pImpl->xMailDispatcher->isStarted()) + { + m_pImpl->xMailDispatcher->stop(); + rButton.set_label(m_sContinue); + m_xPaused->show(); + } + else + { + m_pImpl->xMailDispatcher->start(); + rButton.set_label(m_sStop); + m_xPaused->hide(); + } + } +} + +IMPL_LINK_NOARG(SwSendMailDialog, CloseHdl_Impl, weld::Button&, void) +{ + m_xDialog->hide(); + + if (m_bDestructionEnabled) + m_xDialog->response(RET_CANCEL); + else + { + m_pImpl->aRemoveIdle.SetInvokeHandler( LINK( this, SwSendMailDialog, RemoveThis ) ); + m_pImpl->aRemoveIdle.Start(); + } +} + +IMPL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, pDialog, void ) +{ + static_cast<SwSendMailDialog*>(pDialog)->SendMails(); +} + +IMPL_LINK( SwSendMailDialog, RemoveThis, Timer*, pTimer, void ) +{ + if( m_pImpl->xMailDispatcher.is() ) + { + if(m_pImpl->xMailDispatcher->isStarted()) + m_pImpl->xMailDispatcher->stop(); + if(!m_pImpl->xMailDispatcher->isShutdownRequested()) + m_pImpl->xMailDispatcher->shutdown(); + } + + if( m_bDestructionEnabled && + (!m_pImpl->xMailDispatcher.is() || + !m_pImpl->xMailDispatcher->isRunning())) + { + m_xDialog->response(RET_CANCEL); + } + else + { + pTimer->Start(); + } +} + +IMPL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, p, void ) +{ + SwSendMailDialog* pDialog = static_cast<SwSendMailDialog*>(p); + if(pDialog->m_pImpl->xMailDispatcher.is() && + pDialog->m_pImpl->xMailDispatcher->isStarted()) + { + pDialog->m_pImpl->xMailDispatcher->stop(); + pDialog->m_xStop->set_label(pDialog->m_sContinue); + pDialog->m_xPaused->show(); + } +} + +void SwSendMailDialog::SendMails() +{ + if(!m_pConfigItem) + { + OSL_FAIL("config item not set"); + return; + } + auto xWait(std::make_unique<weld::WaitObject>(m_xDialog.get())); + //get a mail server connection + uno::Reference< mail::XSmtpService > xSmtpServer = + SwMailMergeHelper::ConnectToSmtpServer( *m_pConfigItem, + m_pImpl->xConnectedInMailService, + OUString(), OUString(), m_xDialog.get()); + bool bIsLoggedIn = xSmtpServer.is() && xSmtpServer->isConnected(); + xWait.reset(); + if(!bIsLoggedIn) + { + OSL_FAIL("create error message"); + return; + } + m_pImpl->xMailDispatcher.set( new MailDispatcher(xSmtpServer)); + IterateMails(); + m_pImpl->xMailListener = new SwMailDispatcherListener_Impl(*this); + m_pImpl->xMailDispatcher->addListener(m_pImpl->xMailListener); + if(!m_bCancel) + { + m_pImpl->xMailDispatcher->start(); + } +} + +void SwSendMailDialog::IterateMails() +{ + const SwMailDescriptor* pCurrentMailDescriptor = m_pImpl->GetNextDescriptor(); + while( pCurrentMailDescriptor ) + { + if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor->sEMail)) + { + OUString sMessage = m_sSendingTo; + m_xStatus->append(); + m_xStatus->set_image(m_nSendCount, RID_BMP_FORMULA_CANCEL, 0); + m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", pCurrentMailDescriptor->sEMail), 1); + m_xStatus->set_text(m_nSendCount, m_sFailed, 1); + ++m_nSendCount; + ++m_nErrorCount; + UpdateTransferStatus( ); + pCurrentMailDescriptor = m_pImpl->GetNextDescriptor(); + continue; + } + SwMailMessage* pMessage = new SwMailMessage; + uno::Reference< mail::XMailMessage > xMessage = pMessage; + if(m_pConfigItem->IsMailReplyTo()) + pMessage->setReplyToAddress(m_pConfigItem->GetMailReplyTo()); + pMessage->addRecipient( pCurrentMailDescriptor->sEMail ); + pMessage->SetSenderName( m_pConfigItem->GetMailDisplayName() ); + pMessage->SetSenderAddress( m_pConfigItem->GetMailAddress() ); + if(!pCurrentMailDescriptor->sAttachmentURL.isEmpty()) + { + mail::MailAttachment aAttach; + aAttach.Data = + new SwMailTransferable( + pCurrentMailDescriptor->sAttachmentURL, + pCurrentMailDescriptor->sAttachmentName, + pCurrentMailDescriptor->sMimeType ); + aAttach.ReadableName = pCurrentMailDescriptor->sAttachmentName; + pMessage->addAttachment( aAttach ); + } + pMessage->setSubject( pCurrentMailDescriptor->sSubject ); + uno::Reference< datatransfer::XTransferable> xBody = + new SwMailTransferable( + pCurrentMailDescriptor->sBodyContent, + pCurrentMailDescriptor->sBodyMimeType); + pMessage->setBody( xBody ); + + //CC and BCC are tokenized by ';' + if(!pCurrentMailDescriptor->sCC.isEmpty()) + { + sal_Int32 nPos = 0; + do + { + OUString sTmp = pCurrentMailDescriptor->sCC.getToken( 0, ';', nPos ); + if( !sTmp.isEmpty() ) + pMessage->addCcRecipient( sTmp ); + } + while (nPos >= 0); + } + if(!pCurrentMailDescriptor->sBCC.isEmpty()) + { + sal_Int32 nPos = 0; + do + { + OUString sTmp = pCurrentMailDescriptor->sBCC.getToken( 0, ';', nPos ); + if( !sTmp.isEmpty() ) + pMessage->addBccRecipient( sTmp ); + } + while (nPos >= 0); + } + m_pImpl->xMailDispatcher->enqueueMailMessage( xMessage ); + pCurrentMailDescriptor = m_pImpl->GetNextDescriptor(); + } + UpdateTransferStatus(); +} + +void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount) +{ + Application::PostUserEvent( LINK( this, SwSendMailDialog, + StartSendMails ), this ); + m_nExpectedCount = nExpectedCount > 0 ? nExpectedCount : 1; +} + +void SwSendMailDialog::DocumentSent( uno::Reference< mail::XMailMessage> const & xMessage, + bool bResult, + const OUString* pError ) +{ + //sending should stop on send errors + if(pError && + m_pImpl->xMailDispatcher.is() && m_pImpl->xMailDispatcher->isStarted()) + { + Application::PostUserEvent( LINK( this, SwSendMailDialog, + StopSendMails ), this ); + } + OUString sInsertImg(bResult ? OUString(RID_BMP_FORMULA_APPLY) : OUString(RID_BMP_FORMULA_CANCEL)); + + OUString sMessage = m_sSendingTo; + m_xStatus->append(); + m_xStatus->set_image(m_nSendCount, sInsertImg, 0); + m_xStatus->set_text(m_nSendCount, sMessage.replaceFirst("%1", xMessage->getRecipients()[0]), 1); + m_xStatus->set_text(m_nSendCount, bResult ? m_sCompleted : m_sFailed, 1); + ++m_nSendCount; + if(!bResult) + ++m_nErrorCount; + + UpdateTransferStatus( ); + + if (pError) + { + SwSendWarningBox_Impl aDlg(m_xDialog.get(), *pError); + aDlg.run(); + } +} + +void SwSendMailDialog::UpdateTransferStatus() +{ + OUString sStatus( m_sTransferStatus ); + sStatus = sStatus.replaceFirst("%1", OUString::number(m_nSendCount) ); + sStatus = sStatus.replaceFirst("%2", OUString::number(m_nExpectedCount)); + m_xTransferStatus->set_label(sStatus); + + sStatus = m_sErrorStatus.replaceFirst("%1", OUString::number(m_nErrorCount) ); + m_xErrorStatus->set_label(sStatus); + + if (!m_pImpl->aDescriptors.empty()) + { + assert(m_nExpectedCount && "div-by-zero"); + m_xProgressBar->set_percentage(m_nSendCount * 100 / m_nExpectedCount); + } + else + m_xProgressBar->set_percentage(0); +} + +void SwSendMailDialog::AllMailsSent() +{ + // Leave open if some kind of error occurred + if (m_nSendCount == m_nExpectedCount) + { + m_xStop->set_sensitive(false); + m_xDialog->hide(); + m_xDialog->response(RET_CANCEL); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmoutputtypepage.hxx b/sw/source/ui/dbui/mmoutputtypepage.hxx new file mode 100644 index 000000000..92a60da30 --- /dev/null +++ b/sw/source/ui/dbui/mmoutputtypepage.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_UI_DBUI_MMOUTPUTTYPEPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMOUTPUTTYPEPAGE_HXX + +#include <vcl/wizardmachine.hxx> +#include <vcl/weld.hxx> +class SwMailMergeWizard; + +class SwMailMergeOutputTypePage : public vcl::OWizardPage +{ + SwMailMergeWizard* m_pWizard; + + std::unique_ptr<weld::RadioButton> m_xLetterRB; + std::unique_ptr<weld::RadioButton> m_xMailRB; + std::unique_ptr<weld::Label> m_xLetterHint; + std::unique_ptr<weld::Label> m_xMailHint; + + DECL_LINK(TypeHdl_Impl, weld::ToggleButton&, void); + +public: + SwMailMergeOutputTypePage(weld::Container* pPage, SwMailMergeWizard* pWizard); + virtual ~SwMailMergeOutputTypePage() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/mmresultdialogs.cxx b/sw/source/ui/dbui/mmresultdialogs.cxx new file mode 100644 index 000000000..2702afde2 --- /dev/null +++ b/sw/source/ui/dbui/mmresultdialogs.cxx @@ -0,0 +1,1203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <mmresultdialogs.hxx> +#include <mmconfigitem.hxx> +#include <mailconfigpage.hxx> +#include "mmgreetingspage.hxx" +#include <printdata.hxx> +#include <swmessdialog.hxx> +#include <cmdid.h> +#include <swtypes.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <hintids.hxx> +#include <swmodule.hxx> + +#include <editeng/langitem.hxx> +#include <o3tl/temporary.hxx> +#include <svl/itemset.hxx> +#include <svl/stritem.hxx> +#include <svtools/ehdl.hxx> +#include <svtools/sfxecode.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/scheduler.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <tools/urlobj.hxx> +#include <svl/urihelper.hxx> +#include <vcl/print.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> + +#include <unotools/tempfile.hxx> +#include <osl/file.hxx> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/task/ErrorCodeIOException.hpp> +#include <dbmgr.hxx> +#include <swunohelper.hxx> +#include <shellio.hxx> +#include <svtools/htmlcfg.hxx> +#include <sfx2/event.hxx> +#include <swevent.hxx> +#include <dbui.hxx> +#include <dbui.hrc> +#include <doc.hxx> +#include <sfx2/app.hxx> +#include <strings.hrc> +#include <comphelper/string.hxx> +#include <iodetect.hxx> + +using namespace svt; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +#define MM_DOCTYPE_OOO 1 +#define MM_DOCTYPE_PDF 2 +#define MM_DOCTYPE_WORD 3 +#define MM_DOCTYPE_HTML 4 +#define MM_DOCTYPE_TEXT 5 + +static OUString lcl_GetExtensionForDocType(sal_uLong nDocType) +{ + OUString sExtension; + switch( nDocType ) + { + case MM_DOCTYPE_OOO : sExtension = "odt"; break; + case MM_DOCTYPE_PDF : sExtension = "pdf"; break; + case MM_DOCTYPE_WORD: sExtension = "doc"; break; + case MM_DOCTYPE_HTML: sExtension = "html"; break; + case MM_DOCTYPE_TEXT: sExtension = "txt"; break; + } + return sExtension; +} + +static OUString lcl_GetColumnValueOf(const OUString& rColumn, Reference < container::XNameAccess> const & rxColAccess ) +{ + OUString sRet; + try + { + if (rxColAccess->hasByName(rColumn)) + { + Any aCol = rxColAccess->getByName(rColumn); + Reference< sdb::XColumn > xColumn; + aCol >>= xColumn; + if(xColumn.is()) + sRet = xColumn->getString(); + } + } + catch (const uno::Exception&) + { + } + return sRet; +} + +/** + * Replace email server settings in rConfigItem with those set in Writer's global + * mail merge config settings. + */ +static void lcl_UpdateEmailSettingsFromGlobalConfig(SwMailMergeConfigItem& rConfigItem) +{ + // newly created SwMailMergeConfigItem is initialized with values from (global) config + SwMailMergeConfigItem aConfigItem; + + // take over email-related settings + rConfigItem.SetMailDisplayName(aConfigItem.GetMailDisplayName()); + rConfigItem.SetMailAddress(aConfigItem.GetMailAddress()); + rConfigItem.SetMailReplyTo(aConfigItem.GetMailReplyTo()); + rConfigItem.SetMailReplyTo(aConfigItem.IsMailReplyTo()); + rConfigItem.SetMailServer(aConfigItem.GetMailServer()); + rConfigItem.SetMailPort(aConfigItem.GetMailPort()); + rConfigItem.SetSecureConnection(aConfigItem.IsSecureConnection()); + // authentication settings + rConfigItem.SetAuthentication(aConfigItem.IsAuthentication()); + rConfigItem.SetSMTPAfterPOP(aConfigItem.IsSMTPAfterPOP()); + rConfigItem.SetMailUserName(aConfigItem.GetMailUserName()); + rConfigItem.SetMailPassword(aConfigItem.GetMailPassword()); + rConfigItem.SetInServerName(aConfigItem.GetInServerName()); + rConfigItem.SetInServerPort(aConfigItem.GetInServerPort()); + rConfigItem.SetInServerPOP(aConfigItem.IsInServerPOP()); + rConfigItem.SetInServerUserName(aConfigItem.GetInServerUserName()); + rConfigItem.SetInServerPassword(aConfigItem.GetInServerPassword()); +} + +namespace { + +class SwSaveWarningBox_Impl : public SwMessageAndEditDialog +{ + DECL_LINK( ModifyHdl, weld::Entry&, void); +public: + SwSaveWarningBox_Impl(weld::Window* pParent, const OUString& rFileName); + + OUString GetFileName() const + { + return m_xEdit->get_text(); + } +}; + +class SwSendQueryBox_Impl : public SwMessageAndEditDialog +{ + bool bIsEmptyAllowed; + DECL_LINK( ModifyHdl, weld::Entry&, void); +public: + SwSendQueryBox_Impl(weld::Window* pParent, const OString& rID, + const OUString& rUIXMLDescription); + + void SetValue(const OUString& rSet) + { + m_xEdit->set_text(rSet); + ModifyHdl(*m_xEdit); + } + + OUString GetValue() const + { + return m_xEdit->get_text(); + } + + void SetIsEmptyTextAllowed(bool bSet) + { + bIsEmptyAllowed = bSet; + ModifyHdl(*m_xEdit); + } +}; + +} + +SwSaveWarningBox_Impl::SwSaveWarningBox_Impl(weld::Window* pParent, const OUString& rFileName) + : SwMessageAndEditDialog(pParent, "AlreadyExistsDialog", + "modules/swriter/ui/alreadyexistsdialog.ui") +{ + m_xEdit->set_text(rFileName); + m_xEdit->connect_changed(LINK(this, SwSaveWarningBox_Impl, ModifyHdl)); + + INetURLObject aTmp(rFileName); + m_xDialog->set_primary_text(m_xDialog->get_primary_text().replaceAll("%1", aTmp.getName( + INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset))); + + ModifyHdl(*m_xEdit); +} + +IMPL_LINK( SwSaveWarningBox_Impl, ModifyHdl, weld::Entry&, rEdit, void) +{ + m_xOKPB->set_sensitive(!rEdit.get_text().isEmpty()); +} + +SwSendQueryBox_Impl::SwSendQueryBox_Impl(weld::Window* pParent, const OString& rID, + const OUString& rUIXMLDescription) + : SwMessageAndEditDialog(pParent, rID, rUIXMLDescription) + , bIsEmptyAllowed(true) +{ + m_xEdit->connect_changed(LINK(this, SwSendQueryBox_Impl, ModifyHdl)); + ModifyHdl(*m_xEdit); +} + +IMPL_LINK( SwSendQueryBox_Impl, ModifyHdl, weld::Entry&, rEdit, void) +{ + m_xOKPB->set_sensitive(bIsEmptyAllowed || !rEdit.get_text().isEmpty()); +} + +namespace { + +class SwCopyToDialog : public SfxDialogController +{ + std::unique_ptr<weld::Entry> m_xCCED; + std::unique_ptr<weld::Entry> m_xBCCED; + +public: + explicit SwCopyToDialog(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/ccdialog.ui", "CCDialog") + , m_xCCED(m_xBuilder->weld_entry("cc")) + , m_xBCCED(m_xBuilder->weld_entry("bcc")) + { + } + + OUString GetCC() const {return m_xCCED->get_text();} + void SetCC(const OUString& rSet) {m_xCCED->set_text(rSet);} + + OUString GetBCC() const {return m_xBCCED->get_text();} + void SetBCC(const OUString& rSet) {m_xBCCED->set_text(rSet);} +}; + +} + +SwMMResultSaveDialog::SwMMResultSaveDialog(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/mmresultsavedialog.ui", "MMResultSaveDialog") + , m_bCancelSaving(false) + , m_xSaveAsOneRB(m_xBuilder->weld_radio_button("singlerb")) + , m_xSaveIndividualRB(m_xBuilder->weld_radio_button("individualrb")) + , m_xFromRB(m_xBuilder->weld_radio_button("fromrb")) + , m_xFromNF(m_xBuilder->weld_spin_button("from")) + , m_xToFT(m_xBuilder->weld_label("toft")) + , m_xToNF(m_xBuilder->weld_spin_button("to")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + Link<weld::ToggleButton&,void> aLink = LINK(this, SwMMResultSaveDialog, DocumentSelectionHdl_Impl); + m_xSaveAsOneRB->connect_toggled(aLink); + m_xSaveIndividualRB->connect_toggled(aLink); + m_xFromRB->connect_toggled(aLink); + // m_pSaveAsOneRB is the default, so disable m_xFromNF and m_xToNF initially. + aLink.Call(*m_xSaveAsOneRB); + SwView* pView = ::GetActiveView(); + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + sal_Int32 nCount = xConfigItem->GetMergedDocumentCount(); + m_xFromNF->set_max(nCount); + m_xToNF->set_max(nCount); + m_xToNF->set_value(nCount); + + m_xOKButton->connect_clicked(LINK(this, SwMMResultSaveDialog, SaveOutputHdl_Impl)); +} + +SwMMResultSaveDialog::~SwMMResultSaveDialog() +{ +} + +SwMMResultPrintDialog::SwMMResultPrintDialog(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/mmresultprintdialog.ui", "MMResultPrintDialog") + , m_xPrinterFT(m_xBuilder->weld_label("printerft")) + , m_xPrinterLB(m_xBuilder->weld_combo_box("printers")) + , m_xPrinterSettingsPB(m_xBuilder->weld_button("printersettings")) + , m_xPrintAllRB(m_xBuilder->weld_radio_button("printallrb")) + , m_xFromRB(m_xBuilder->weld_radio_button("fromrb")) + , m_xFromNF(m_xBuilder->weld_spin_button("from")) + , m_xToFT(m_xBuilder->weld_label("toft")) + , m_xToNF(m_xBuilder->weld_spin_button("to")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + m_xPrinterLB->make_sorted(); + + m_xPrinterLB->connect_changed(LINK(this, SwMMResultPrintDialog, PrinterChangeHdl_Impl)); + m_xPrinterSettingsPB->connect_clicked(LINK(this, SwMMResultPrintDialog, PrinterSetupHdl_Impl)); + + Link<weld::ToggleButton&,void> aLink = LINK(this, SwMMResultPrintDialog, DocumentSelectionHdl_Impl); + m_xPrintAllRB->connect_toggled(aLink); + m_xFromRB->connect_toggled(aLink); + // m_pPrintAllRB is the default, so disable m_xFromNF and m_xToNF initially. + aLink.Call(*m_xPrintAllRB); + + m_xOKButton->connect_clicked(LINK(this, SwMMResultPrintDialog, PrintHdl_Impl)); + + FillInPrinterSettings(); +} + +SwMMResultPrintDialog::~SwMMResultPrintDialog() +{ +} + +SwMMResultEmailDialog::SwMMResultEmailDialog(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/mmresultemaildialog.ui", "MMResultEmailDialog") + , m_sConfigureMail(SwResId(ST_CONFIGUREMAIL)) + , m_xMailToFT(m_xBuilder->weld_label("mailtoft")) + , m_xMailToLB(m_xBuilder->weld_combo_box("mailto")) + , m_xCopyToPB(m_xBuilder->weld_button("copyto")) + , m_xSubjectFT(m_xBuilder->weld_label("subjectft")) + , m_xSubjectED(m_xBuilder->weld_entry("subject")) + , m_xSendAsFT(m_xBuilder->weld_label("sendasft")) + , m_xSendAsLB(m_xBuilder->weld_combo_box("sendas")) + , m_xSendAsPB(m_xBuilder->weld_button("sendassettings")) + , m_xAttachmentGroup(m_xBuilder->weld_widget("attachgroup")) + , m_xAttachmentED(m_xBuilder->weld_entry("attach")) + , m_xSendAllRB(m_xBuilder->weld_radio_button("sendallrb")) + , m_xFromRB(m_xBuilder->weld_radio_button("fromrb")) + , m_xFromNF(m_xBuilder->weld_spin_button("from")) + , m_xToFT(m_xBuilder->weld_label("toft")) + , m_xToNF(m_xBuilder->weld_spin_button("to")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + m_xCopyToPB->connect_clicked(LINK(this, SwMMResultEmailDialog, CopyToHdl_Impl)); + m_xSendAsPB->connect_clicked(LINK(this, SwMMResultEmailDialog, SendAsHdl_Impl)); + m_xSendAsLB->connect_changed(LINK(this, SwMMResultEmailDialog, SendTypeHdl_Impl)); + + Link<weld::ToggleButton&,void> aLink = LINK(this, SwMMResultEmailDialog, DocumentSelectionHdl_Impl); + m_xSendAllRB->connect_toggled(aLink); + m_xFromRB->connect_toggled(aLink); + // m_xSendAllRB is the default, so disable m_xFromNF and m_xToNF initially. + aLink.Call(*m_xSendAllRB); + + m_xOKButton->connect_clicked(LINK(this, SwMMResultEmailDialog, SendDocumentsHdl_Impl)); + + FillInEmailSettings(); +} + +SwMMResultEmailDialog::~SwMMResultEmailDialog() +{ +} + +void SwMMResultPrintDialog::FillInPrinterSettings() +{ + //fill printer ListBox + SwView* pView = ::GetActiveView(); + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + const std::vector<OUString>& rPrinters = Printer::GetPrinterQueues(); + unsigned int nCount = rPrinters.size(); + bool bMergePrinterExists = false; + + for (unsigned int i = 0; i < nCount; ++i) + { + m_xPrinterLB->append_text( rPrinters[i] ); + if( !bMergePrinterExists && rPrinters[i] == xConfigItem->GetSelectedPrinter() ) + bMergePrinterExists = true; + } + + assert(xConfigItem); + if(!bMergePrinterExists) + { + SfxPrinter* pPrinter = pView->GetWrtShell().getIDocumentDeviceAccess().getPrinter( true ); + m_xPrinterLB->set_active_text(pPrinter->GetName()); + } + else + { + m_xPrinterLB->set_active_text(xConfigItem->GetSelectedPrinter()); + } + PrinterChangeHdl_Impl(*m_xPrinterLB); + + sal_Int32 count = xConfigItem->GetMergedDocumentCount(); + m_xFromNF->set_max(count); + m_xToNF->set_value(count); + m_xToNF->set_max(count); +} + +void SwMMResultEmailDialog::FillInEmailSettings() +{ + SwView* pView = ::GetActiveView(); + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + + SwView* pSourceView = xConfigItem->GetSourceView(); + OSL_ENSURE(pSourceView, "no source view exists"); + if (pSourceView) + { + SwDocShell* pDocShell = pSourceView->GetDocShell(); + if (pDocShell->HasName()) + { + INetURLObject aTmp(pDocShell->GetMedium()->GetName()); + m_xAttachmentED->set_text(aTmp.getName( + INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )); + } + } + + if (m_xAttachmentED->get_text().isEmpty()) + { + OUString sAttach = "." + lcl_GetExtensionForDocType(m_xSendAsLB->get_active_id().toUInt32()); + m_xAttachmentED->set_text(sAttach); + + } + + //select first column + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp(xConfigItem->GetResultSet(), uno::UNO_QUERY); + //get the name of the actual columns + uno::Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + uno::Sequence< OUString > aFields; + if (xColAccess.is()) + aFields = xColAccess->getElementNames(); + + // fill mail address ListBox + assert(m_xMailToLB->get_count() == 0); + for (const OUString& rField : std::as_const(aFields)) + m_xMailToLB->append_text(rField); + + m_xMailToLB->set_active(0); + // then select the right one - may not be available + const std::vector<std::pair<OUString, int>>& rHeaders = xConfigItem->GetDefaultAddressHeaders(); + OUString sEMailColumn = rHeaders[MM_PART_E_MAIL].first; + Sequence< OUString> aAssignment = xConfigItem->GetColumnAssignment(xConfigItem->GetCurrentDBData()); + if (aAssignment.getLength() > MM_PART_E_MAIL && !aAssignment[MM_PART_E_MAIL].isEmpty()) + sEMailColumn = aAssignment[MM_PART_E_MAIL]; + m_xMailToLB->set_active_text(sEMailColumn); + + // HTML format pre-selected + m_xSendAsLB->set_active(3); + SendTypeHdl_Impl(*m_xSendAsLB); + + const sal_Int32 nCount = xConfigItem->GetMergedDocumentCount(); + m_xFromNF->set_max(nCount); + m_xToNF->set_max(nCount); + m_xToNF->set_value(nCount); +} + +IMPL_LINK_NOARG(SwMMResultSaveDialog, DocumentSelectionHdl_Impl, weld::ToggleButton&, void) +{ + bool bEnableFromTo = m_xFromRB->get_active(); + m_xFromNF->set_sensitive(bEnableFromTo); + m_xToFT->set_sensitive(bEnableFromTo); + m_xToNF->set_sensitive(bEnableFromTo); +} + +IMPL_LINK_NOARG(SwMMResultPrintDialog, DocumentSelectionHdl_Impl, weld::ToggleButton&, void) +{ + bool bEnableFromTo = m_xFromRB->get_active(); + m_xFromNF->set_sensitive(bEnableFromTo); + m_xToFT->set_sensitive(bEnableFromTo); + m_xToNF->set_sensitive(bEnableFromTo); +} + +IMPL_LINK_NOARG(SwMMResultEmailDialog, DocumentSelectionHdl_Impl, weld::ToggleButton&, void) +{ + bool bEnableFromTo = m_xFromRB->get_active(); + m_xFromNF->set_sensitive(bEnableFromTo); + m_xToFT->set_sensitive(bEnableFromTo); + m_xToNF->set_sensitive(bEnableFromTo); +} + +IMPL_LINK_NOARG(SwMMResultEmailDialog, CopyToHdl_Impl, weld::Button&, void) +{ + SwCopyToDialog aDlg(m_xDialog.get()); + aDlg.SetCC(m_sCC ); + aDlg.SetBCC(m_sBCC); + if (aDlg.run() == RET_OK) + { + m_sCC = aDlg.GetCC() ; + m_sBCC = aDlg.GetBCC(); + } +} + +namespace { + +int documentStartPageNumber(SwMailMergeConfigItem* pConfigItem, int document, bool bIgnoreEmpty) +{ + SwView* pTargetView = pConfigItem->GetTargetView(); + assert( pTargetView ); + SwCursorShell& shell = pTargetView->GetWrtShell(); + const SwDocMergeInfo& info = pConfigItem->GetDocumentMergeInfo(document); + sal_uInt16 page; + shell.Push(); + shell.GotoMark( info.startPageInTarget ); + if (!bIgnoreEmpty) + shell.GetPageNum(page, o3tl::temporary(sal_uInt16())); + else + page = shell.GetPageNumSeqNonEmpty(); + shell.Pop(SwCursorShell::PopMode::DeleteCurrent); + return page; +} + +int documentEndPageNumber(SwMailMergeConfigItem* pConfigItem, int document, bool bIgnoreEmpty) +{ + SwView* pTargetView = pConfigItem->GetTargetView(); + assert( pTargetView ); + SwWrtShell& shell = pTargetView->GetWrtShell(); + shell.Push(); + if (document < int(pConfigItem->GetMergedDocumentCount()) - 1) + { + // Go to the page before the starting page of the next merged document. + const SwDocMergeInfo& info = pConfigItem->GetDocumentMergeInfo( document + 1 ); + shell.GotoMark( info.startPageInTarget ); + shell.EndPrvPg(); + } + else + { // This is the last merged document, so it ends on the page at which the document ends. + shell.SttEndDoc( false ); // go to doc end + } + sal_uInt16 page; + if (!bIgnoreEmpty) + shell.GetPageNum(page, o3tl::temporary(sal_uInt16())); + else + page = shell.GetPageNumSeqNonEmpty(); + shell.Pop(SwCursorShell::PopMode::DeleteCurrent); + return page; +} + +} // anonymous namespace + +IMPL_LINK_NOARG(SwMMResultSaveDialog, SaveOutputHdl_Impl, weld::Button&, void) +{ + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + if (!xConfigItem->GetTargetView()) + SwDBManager::PerformMailMerge(pView); + + SwView* pTargetView = xConfigItem->GetTargetView(); + assert(pTargetView); + + OUString sFilter; + OUString sPath = SwMailMergeHelper::CallSaveAsDialog(m_xDialog.get(), sFilter); + if (sPath.isEmpty()) + { + // just return back to the dialog + return; + } + + if (m_xSaveAsOneRB->get_active()) + { + uno::Sequence< beans::PropertyValue > aValues(1); + beans::PropertyValue* pValues = aValues.getArray(); + pValues[0].Name = "FilterName"; + pValues[0].Value <<= sFilter; + + uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY); + ErrCode nErrorCode = ERRCODE_NONE; + try + { + xStore->storeToURL( sPath, aValues ); + } + catch (const task::ErrorCodeIOException& rErrorEx) + { + nErrorCode = ErrCode(rErrorEx.ErrCode); + } + catch (const Exception&) + { + nErrorCode = ERRCODE_IO_GENERAL; + } + if( nErrorCode != ERRCODE_NONE ) + { + SfxErrorContext aEc(ERRCTX_SFX_SAVEASDOC, pTargetView->GetDocShell()->GetTitle()); + ErrorHandler::HandleError( nErrorCode ); + } + } + else + { + const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount(); + sal_uInt32 nBegin = 0; + sal_uInt32 nEnd = nDocumentCount; + + if (!m_xSaveIndividualRB->get_active()) + { + nBegin = static_cast< sal_Int32 >(m_xFromNF->get_value() - 1); + nEnd = static_cast< sal_Int32 >(m_xToNF->get_value()); + if(nEnd > nDocumentCount) + nEnd = nDocumentCount; + } + + OUString sTargetTempURL = URIHelper::SmartRel2Abs( + INetURLObject(), utl::TempFile::CreateTempName(), + URIHelper::GetMaybeFileHdl()); + std::shared_ptr<const SfxFilter> pSfxFlt = SwIoSystem::GetFilterOfFormat( + FILTER_XML, + SwDocShell::Factory().GetFilterContainer() ); + + uno::Sequence< beans::PropertyValue > aValues(1); + beans::PropertyValue* pValues = aValues.getArray(); + pValues[0].Name = "FilterName"; + pValues[0].Value <<= pSfxFlt->GetFilterName(); + + uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY); + ErrCode nErrorCode = ERRCODE_NONE; + try + { + xStore->storeToURL( sTargetTempURL, aValues ); + } + catch (const task::ErrorCodeIOException& rErrorEx) + { + nErrorCode = ErrCode(rErrorEx.ErrCode); + } + catch (const Exception&) + { + nErrorCode = ERRCODE_IO_GENERAL; + } + if( nErrorCode != ERRCODE_NONE ) + { + SfxErrorContext aEc(ERRCTX_SFX_SAVEASDOC, pTargetView->GetDocShell()->GetTitle()); + ErrorHandler::HandleError( nErrorCode ); + } + + SwView* pSourceView = xConfigItem->GetSourceView(); + auto xSaveMonitor = std::make_shared<SaveMonitor>(m_xDialog.get()); + xSaveMonitor->m_xDocName->set_label(pSourceView->GetDocShell()->GetTitle(22)); + xSaveMonitor->m_xPrinter->set_label( INetURLObject( sPath ).getFSysPath( FSysStyle::Detect ) ); + m_bCancelSaving = false; + weld::DialogController::runAsync(xSaveMonitor, [this, &xSaveMonitor](sal_Int32 nResult){ + if (nResult == RET_CANCEL) + m_bCancelSaving = true; + xSaveMonitor.reset(); + }); + + for(sal_uInt32 nDoc = nBegin; nDoc < nEnd && !m_bCancelSaving; ++nDoc) + { + INetURLObject aURL(sPath); + OUString sExtension = aURL.getExtension(); + if (sExtension.isEmpty()) + { + sExtension = pSfxFlt->GetWildcard().getGlob().getToken(1, '.'); + sPath += "." + sExtension; + } + OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number( nDoc ); + xSaveMonitor->m_xPrintInfo->set_label(sStat); + + //now extract a document from the target document + // the shell will be closed at the end, but it is more safe to use SfxObjectShellLock here + SfxObjectShellLock xTempDocShell( new SwDocShell( SfxObjectCreateMode::STANDARD ) ); + xTempDocShell->DoInitNew(); + SfxViewFrame* pTempFrame = SfxViewFrame::LoadHiddenDocument( *xTempDocShell, SFX_INTERFACE_NONE ); + SwView* pTempView = static_cast<SwView*>( pTempFrame->GetViewShell() ); + pTargetView->GetWrtShell().StartAction(); + SwgReaderOption aOpt; + aOpt.SetTextFormats( true ); + aOpt.SetFrameFormats( true ); + aOpt.SetPageDescs( true ); + aOpt.SetNumRules( true ); + aOpt.SetMerge( false ); + pTempView->GetDocShell()->LoadStylesFromFile( + sTargetTempURL, aOpt, true ); + pTempView->GetDocShell()->GetDoc()->ReplaceCompatibilityOptions( *pTargetView->GetDocShell()->GetDoc()); + pTempView->GetDocShell()->GetDoc()->ReplaceDefaults( *pTargetView->GetDocShell()->GetDoc()); + pTempView->GetDocShell()->GetDoc()->ReplaceDocumentProperties( *pTargetView->GetDocShell()->GetDoc(), true ); + + pTargetView->GetWrtShell().PastePages( + pTempView->GetWrtShell(), documentStartPageNumber(xConfigItem.get(), nDoc, false), + documentEndPageNumber(xConfigItem.get(), nDoc, false)); + pTargetView->GetWrtShell().EndAction(); + //then save it + OUString sOutPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri); + OUString sCounter = "_" + OUString::number(nDoc + 1); + sOutPath = sOutPath.replaceAt( sOutPath.getLength() - sExtension.getLength() - 1, 0, sCounter); + + while(true) + { + //time for other slots is needed + Scheduler::ProcessEventsToIdle(); + + bool bFailed = false; + try + { + pValues[0].Value <<= sFilter; + uno::Reference< frame::XStorable > xTempStore( xTempDocShell->GetModel(), uno::UNO_QUERY); + xTempStore->storeToURL( sOutPath, aValues ); + } + catch (const uno::Exception&) + { + bFailed = true; + } + + if(bFailed) + { + std::unique_ptr<SwSaveWarningBox_Impl> xWarning(new SwSaveWarningBox_Impl(m_xDialog.get(), sOutPath)); + if (RET_OK == xWarning->run()) + sOutPath = xWarning->GetFileName(); + else + { + xTempDocShell->DoClose(); + m_xDialog->response(RET_OK); + return; + } + } + else + { + xTempDocShell->DoClose(); + m_xDialog->response(RET_OK); + break; + } + } + } + if (xSaveMonitor) + xSaveMonitor->response(RET_OK); + ::osl::File::remove( sTargetTempURL ); + } + + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwMMResultPrintDialog, PrinterChangeHdl_Impl, weld::ComboBox&, rBox, void) +{ + SwView* pView = ::GetActiveView(); + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + if (rBox.get_active() != -1) + { + const QueueInfo* pInfo = Printer::GetQueueInfo( rBox.get_active_text(), false ); + + if( pInfo ) + { + if ( !m_pTempPrinter ) + { + m_pTempPrinter = VclPtr<Printer>::Create( *pInfo ); + } + else + { + if( (m_pTempPrinter->GetName() != pInfo->GetPrinterName()) || + (m_pTempPrinter->GetDriverName() != pInfo->GetDriver()) ) + { + m_pTempPrinter.disposeAndClear(); + m_pTempPrinter = VclPtr<Printer>::Create( *pInfo ); + } + } + } + else if( ! m_pTempPrinter ) + m_pTempPrinter = VclPtr<Printer>::Create(); + + m_xPrinterSettingsPB->set_sensitive(m_pTempPrinter->HasSupport(PrinterSupport::SetupDialog)); + } + else + m_xPrinterSettingsPB->set_sensitive(false); + + xConfigItem->SetSelectedPrinter(rBox.get_active_text()); +} + +IMPL_LINK_NOARG(SwMMResultPrintDialog, PrintHdl_Impl, weld::Button&, void) +{ + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + if(!xConfigItem->GetTargetView()) + SwDBManager::PerformMailMerge(pView); + + SwView* pTargetView = xConfigItem->GetTargetView(); + assert(pTargetView); + + const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount(); + sal_uInt32 nBegin = 0; + sal_uInt32 nEnd = nDocumentCount; + + if (!m_xPrintAllRB->get_active()) + { + nBegin = m_xFromNF->get_value() - 1; + nEnd = m_xToNF->get_value(); + if(nEnd > nDocumentCount) + nEnd = nDocumentCount; + } + + // If we skip autoinserted blanks, then the page numbers used in the print range string + // refer to the non-blank pages as they appear in the document (see tdf#89708). + const bool bIgnoreEmptyPages = + !pTargetView->GetDocShell()->GetDoc()->getIDocumentDeviceAccess().getPrintData().IsPrintEmptyPages(); + const int nStartPage = documentStartPageNumber(xConfigItem.get(), nBegin, bIgnoreEmptyPages); + const int nEndPage = documentEndPageNumber(xConfigItem.get(), nEnd - 1, bIgnoreEmptyPages); + + const OUString sPages(OUString::number(nStartPage) + "-" + OUString::number(nEndPage)); + + pTargetView->SetMailMergeConfigItem(xConfigItem); + if(m_pTempPrinter) + { + SfxPrinter *const pDocumentPrinter = pTargetView->GetWrtShell() + .getIDocumentDeviceAccess().getPrinter(true); + pDocumentPrinter->SetPrinterProps(m_pTempPrinter); + // this should be able to handle setting its own printer + pTargetView->SetPrinter(pDocumentPrinter); + } + + SfxObjectShell* pObjSh = pTargetView->GetViewFrame()->GetObjectShell(); + SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMerge, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), pObjSh)); + + uno::Sequence < beans::PropertyValue > aProps( 2 ); + aProps[0]. Name = "MonitorVisible"; + aProps[0].Value <<= true; + aProps[1]. Name = "Pages"; + aProps[1]. Value <<= sPages; + + pTargetView->ExecPrint( aProps, false, true ); + SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMergeEnd, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), pObjSh)); + + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SwMMResultPrintDialog, PrinterSetupHdl_Impl, weld::Button&, void) +{ + if (m_pTempPrinter) + m_pTempPrinter->Setup(m_xDialog.get()); +} + +IMPL_LINK(SwMMResultEmailDialog, SendTypeHdl_Impl, weld::ComboBox&, rBox, void) +{ + auto nDocType = rBox.get_active_id().toUInt32(); + bool bEnable = MM_DOCTYPE_HTML != nDocType && MM_DOCTYPE_TEXT != nDocType; + m_xSendAsPB->set_sensitive(bEnable); + m_xAttachmentGroup->set_sensitive(bEnable); + if(bEnable) + { + //add the correct extension + OUString sAttach(m_xAttachmentED->get_text()); + //do nothing if the user has removed the name - the warning will come early enough + if (!sAttach.isEmpty()) + { + sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttach, '.'); + if( 2 > nTokenCount) + { + sAttach += "."; + ++nTokenCount; + } + sAttach = comphelper::string::setToken(sAttach, nTokenCount - 1, '.', lcl_GetExtensionForDocType( nDocType )); + m_xAttachmentED->set_text(sAttach); + } + } +} + +IMPL_LINK_NOARG(SwMMResultEmailDialog, SendAsHdl_Impl, weld::Button&, void) +{ + SwMailBodyDialog aDlg(m_xDialog.get()); + aDlg.SetBody(m_sBody); + if (RET_OK == aDlg.run()) + { + m_sBody = aDlg.GetBody(); + } +} + +// Send documents as e-mail +IMPL_LINK_NOARG(SwMMResultEmailDialog, SendDocumentsHdl_Impl, weld::Button&, void) +{ + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem(); + assert(xConfigItem); + if (!xConfigItem->GetTargetView()) + SwDBManager::PerformMailMerge(pView); + + //get the composed document + SwView* pTargetView = xConfigItem->GetTargetView(); + SAL_WARN_IF(!pTargetView, "sw.ui", "No TargetView in SwMailMergeConfigItem"); + + if (xConfigItem->GetMailServer().isEmpty() || + !SwMailMergeHelper::CheckMailAddress(xConfigItem->GetMailAddress()) ) + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + m_sConfigureMail)); + xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + sal_uInt16 nRet = xQueryBox->run(); + if (RET_YES == nRet ) + { + SwView* pConfigView = pTargetView ? pTargetView : pView; + SfxAllItemSet aSet(pConfigView->GetPool()); + SwMailConfigDlg aDlg(m_xDialog.get(), aSet); + nRet = aDlg.run(); + } + + if(nRet != RET_OK && nRet != RET_YES) + return; // back to the dialog + + // SwMailConfigDlg writes mail merge email settings only to (global) config, + // so copy them to the existing config item + lcl_UpdateEmailSettingsFromGlobalConfig(*xConfigItem); + } + //add the documents + const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount(); + sal_uInt32 nBegin = 0; + sal_uInt32 nEnd = nDocumentCount; + if (!m_xSendAllRB->get_active()) + { + nBegin = static_cast< sal_Int32 >(m_xFromNF->get_value() - 1); + nEnd = static_cast< sal_Int32 >(m_xToNF->get_value()); + if(nEnd > nDocumentCount) + nEnd = nDocumentCount; + } + bool bAsBody = false; + rtl_TextEncoding eEncoding = ::osl_getThreadTextEncoding(); + SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer(); + std::shared_ptr<const SfxFilter> pSfxFlt; + auto nDocType = m_xSendAsLB->get_active_id().toUInt32(); + OUString sExtension = lcl_GetExtensionForDocType(nDocType); + switch( nDocType ) + { + case MM_DOCTYPE_OOO: + { + //Make sure we don't pick e.g. the flat xml filter + //for this format + pSfxFlt = pFilterContainer->GetFilter4FilterName( + "writer8", + SfxFilterFlags::EXPORT); + } + break; + case MM_DOCTYPE_PDF: + { + pSfxFlt = pFilterContainer->GetFilter4FilterName( + "writer_pdf_Export", + SfxFilterFlags::EXPORT); + } + break; + case MM_DOCTYPE_WORD: + { + //the method SwIOSystemGetFilterOfFormat( ) returns the template filter + //because it uses the same user data :-( + SfxFilterMatcher aMatcher( pFilterContainer->GetName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFilter = aIter.First(); + while ( pFilter ) + { + if( pFilter->GetUserData() == FILTER_WW8 && pFilter->CanExport() ) + { + pSfxFlt = pFilter; + break; + } + pFilter = aIter.Next(); + } + + } + break; + case MM_DOCTYPE_HTML: + { + bAsBody = true; + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + eEncoding = rHtmlOptions.GetTextEncoding(); + } + break; + case MM_DOCTYPE_TEXT: + { + bAsBody = true; + pSfxFlt = pFilterContainer->GetFilter4FilterName( + "Text (encoded)", SfxFilterFlags::EXPORT); + } + break; + } + if(!pSfxFlt) + pSfxFlt = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT); + + if(!pSfxFlt) + { + m_xDialog->response(RET_OK); + return; + } + OUString sMimeType = pSfxFlt->GetMimeType(); + + if (m_xSubjectED->get_text().isEmpty()) + { + std::unique_ptr<SwSendQueryBox_Impl> xQuery(new SwSendQueryBox_Impl(m_xDialog.get(), "SubjectDialog", + "modules/swriter/ui/subjectdialog.ui")); + xQuery->SetIsEmptyTextAllowed(true); + xQuery->SetValue(""); + if(RET_OK == xQuery->run()) + { + if (!xQuery->GetValue().isEmpty()) + m_xSubjectED->set_text(xQuery->GetValue()); + } + else + return; // back to the dialog + } + if(!bAsBody && m_xAttachmentED->get_text().isEmpty()) + { + std::unique_ptr<SwSendQueryBox_Impl> xQuery(new SwSendQueryBox_Impl(m_xDialog.get(), "AttachNameDialog", + "modules/swriter/ui/attachnamedialog.ui")); + xQuery->SetIsEmptyTextAllowed(false); + if (RET_OK == xQuery->run()) + { + OUString sAttach(xQuery->GetValue()); + sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttach, '.'); + if (2 > nTokenCount) + { + sAttach += "."; + ++nTokenCount; + } + sAttach = comphelper::string::setToken(sAttach, nTokenCount - 1, '.', lcl_GetExtensionForDocType( + m_xSendAsLB->get_active_id().toUInt32())); + m_xAttachmentED->set_text(sAttach); + } + else + return; // back to the dialog + } + OUString sEMailColumn = m_xMailToLB->get_active_text(); + OSL_ENSURE( !sEMailColumn.isEmpty(), "No email column selected"); + Reference< sdbcx::XColumnsSupplier > xColsSupp( xConfigItem->GetResultSet(), UNO_QUERY); + Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + if(sEMailColumn.isEmpty() || !xColAccess.is() || !xColAccess->hasByName(sEMailColumn)) + { + m_xDialog->response(RET_OK); + return; + } + + OUString sFilterOptions; + if(MM_DOCTYPE_TEXT == nDocType) + { + SwAsciiOptions aOpt; + sal_uInt16 nAppScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ); + sal_uInt16 nWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nAppScriptType); + aOpt.SetLanguage( static_cast<const SvxLanguageItem&>(pTargetView->GetWrtShell(). + GetDefault( nWhich )).GetLanguage()); + aOpt.SetParaFlags( LINEEND_CR ); + aOpt.WriteUserData( sFilterOptions ); + } + else if(MM_DOCTYPE_HTML == nDocType) + { + sFilterOptions = "EmbedImages"; + } + OUString sTargetTempURL = URIHelper::SmartRel2Abs( + INetURLObject(), utl::TempFile::CreateTempName(), + URIHelper::GetMaybeFileHdl()); + std::shared_ptr<const SfxFilter> pTargetSfxFlt = SwIoSystem::GetFilterOfFormat( + FILTER_XML, + SwDocShell::Factory().GetFilterContainer() ); + + uno::Sequence< beans::PropertyValue > aValues(1); + beans::PropertyValue* pValues = aValues.getArray(); + pValues[0].Name = "FilterName"; + pValues[0].Value <<= pTargetSfxFlt->GetFilterName(); + + uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY); + xStore->storeToURL( sTargetTempURL, aValues ); + + //create the send dialog + vcl::Window* pParent = Application::GetDefDialogParent(); + std::shared_ptr<SwSendMailDialog> xDlg = std::make_shared<SwSendMailDialog>(pParent ? pParent->GetFrameWeld() : nullptr, *xConfigItem); + + xDlg->StartSend(nEnd - nBegin); + weld::DialogController::runAsync(xDlg, [](sal_Int32 /*nResult*/){}); + + //help to force painting the dialog + //TODO/CLEANUP + //predetermined breaking point + Application::Reschedule( true ); + m_xDialog->response(RET_OK); + for(sal_uInt32 nDoc = nBegin; nDoc < nEnd; ++nDoc) + { + SwDocMergeInfo& rInfo = xConfigItem->GetDocumentMergeInfo(nDoc); + + //now extract a document from the target document + // the shell will be closed at the end, but it is more safe to use SfxObjectShellLock here + SfxObjectShellLock xTempDocShell( new SwDocShell( SfxObjectCreateMode::STANDARD ) ); + xTempDocShell->DoInitNew(); + SfxViewFrame* pTempFrame = SfxViewFrame::LoadHiddenDocument( *xTempDocShell, SFX_INTERFACE_NONE ); + SwView* pTempView = static_cast<SwView*>( pTempFrame->GetViewShell() ); + pTargetView->GetWrtShell().StartAction(); + SwgReaderOption aOpt; + aOpt.SetTextFormats( true ); + aOpt.SetFrameFormats( true ); + aOpt.SetPageDescs( true ); + aOpt.SetNumRules( true ); + aOpt.SetMerge( false ); + pTempView->GetDocShell()->LoadStylesFromFile( + sTargetTempURL, aOpt, true ); + pTempView->GetDocShell()->GetDoc()->ReplaceCompatibilityOptions( *pTargetView->GetDocShell()->GetDoc()); + pTempView->GetDocShell()->GetDoc()->ReplaceDefaults( *pTargetView->GetDocShell()->GetDoc()); + pTempView->GetDocShell()->GetDoc()->ReplaceDocumentProperties( *pTargetView->GetDocShell()->GetDoc(), true ); + pTargetView->GetWrtShell().PastePages( + pTempView->GetWrtShell(), documentStartPageNumber(xConfigItem.get(), nDoc, false), + documentEndPageNumber(xConfigItem.get(), nDoc, false)); + pTargetView->GetWrtShell().EndAction(); + + //then save it + SfxStringItem aName(SID_FILE_NAME, + URIHelper::SmartRel2Abs( + INetURLObject(), utl::TempFile::CreateTempName(), + URIHelper::GetMaybeFileHdl()) ); + + { + bool withFilterOptions = MM_DOCTYPE_TEXT == nDocType || MM_DOCTYPE_HTML == nDocType; + uno::Sequence< beans::PropertyValue > aFilterValues(withFilterOptions ? 2 : 1); + beans::PropertyValue* pFilterValues = aFilterValues.getArray(); + pFilterValues[0].Name = "FilterName"; + pFilterValues[0].Value <<= pSfxFlt->GetFilterName(); + if(withFilterOptions) + { + pFilterValues[1].Name = "FilterOptions"; + pFilterValues[1].Value <<= sFilterOptions; + } + + uno::Reference< frame::XStorable > xTempStore( pTempView->GetDocShell()->GetModel(), uno::UNO_QUERY); + xTempStore->storeToURL( aName.GetValue(), aFilterValues ); + } + xTempDocShell->DoClose(); + + sal_Int32 nTarget = xConfigItem->MoveResultSet(rInfo.nDBRow); + OSL_ENSURE( nTarget == rInfo.nDBRow, "row of current document could not be selected"); + OSL_ENSURE( !sEMailColumn.isEmpty(), "No email column selected"); + OUString sEMail = lcl_GetColumnValueOf(sEMailColumn, xColAccess); + SwMailDescriptor aDesc; + aDesc.sEMail = sEMail; + OUStringBuffer sBody; + if(bAsBody) + { + { + //read in the temporary file and use it as mail body + SfxMedium aMedium( aName.GetValue(), StreamMode::READ); + SvStream* pInStream = aMedium.GetInStream(); + if(pInStream) + pInStream->SetStreamCharSet( eEncoding ); + else + { + OSL_FAIL("no output file created?"); + continue; + } + OString sLine; + bool bDone = pInStream->ReadLine( sLine ); + while ( bDone ) + { + sBody.append( OStringToOUString(sLine, eEncoding) ); + sBody.append("\n"); + bDone = pInStream->ReadLine( sLine ); + } + } + //remove the temporary file + SWUnoHelper::UCB_DeleteFile( aName.GetValue() ); + } + else + { + sBody = m_sBody; + aDesc.sAttachmentURL = aName.GetValue(); + OUString sAttachment(m_xAttachmentED->get_text()); + sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttachment, '.'); + if (2 > nTokenCount) + { + sAttachment += "."; + sAttachment = comphelper::string::setToken(sAttachment, nTokenCount, '.', sExtension); + } + else if (sAttachment.getToken( nTokenCount - 1, '.') != sExtension) + sAttachment += sExtension; + aDesc.sAttachmentName = sAttachment; + aDesc.sMimeType = sMimeType; + + if (xConfigItem->IsGreetingLine(true)) + { + OUString sNameColumn = xConfigItem->GetAssignedColumn(MM_PART_LASTNAME); + OUString sName = lcl_GetColumnValueOf(sNameColumn, xColAccess); + OUString sGreeting; + if(!sName.isEmpty() && xConfigItem->IsIndividualGreeting(true)) + { + OUString sGenderColumn = xConfigItem->GetAssignedColumn(MM_PART_GENDER); + const OUString& sFemaleValue = xConfigItem->GetFemaleGenderValue(); + OUString sGenderValue = lcl_GetColumnValueOf(sGenderColumn, xColAccess); + SwMailMergeConfigItem::Gender eGenderType = sGenderValue == sFemaleValue ? + SwMailMergeConfigItem::FEMALE : + SwMailMergeConfigItem::MALE; + + sGreeting = SwAddressPreview::FillData( + xConfigItem->GetGreetings(eGenderType) + [xConfigItem->GetCurrentGreeting(eGenderType)], + *xConfigItem); + } + else + { + sGreeting = + xConfigItem->GetGreetings(SwMailMergeConfigItem::NEUTRAL) + [xConfigItem->GetCurrentGreeting(SwMailMergeConfigItem::NEUTRAL)]; + + } + sGreeting += "\n"; + sBody.insert(0, sGreeting); + } + } + aDesc.sBodyContent = sBody.makeStringAndClear(); + if(MM_DOCTYPE_HTML == nDocType) + { + aDesc.sBodyMimeType = "text/html; charset=" + + OUString::createFromAscii(rtl_getBestMimeCharsetFromTextEncoding( eEncoding )); + } + else + aDesc.sBodyMimeType = "text/plain; charset=UTF-8; format=flowed"; + + aDesc.sSubject = m_xSubjectED->get_text(); + aDesc.sCC = m_sCC; + aDesc.sBCC = m_sBCC; + xDlg->AddDocument( aDesc ); + //help to force painting the dialog + Application::Reschedule( true ); + //stop creating of data when dialog has been closed + if (!xDlg->getDialog()->get_visible()) + { + break; + } + } + xDlg->EnableDestruction(); + ::osl::File::remove( sTargetTempURL ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/selectdbtabledialog.cxx b/sw/source/ui/dbui/selectdbtabledialog.cxx new file mode 100644 index 000000000..096c1472f --- /dev/null +++ b/sw/source/ui/dbui/selectdbtabledialog.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 <swtypes.hxx> +#include "selectdbtabledialog.hxx" +#include "dbtablepreviewdialog.hxx" +#include <osl/diagnose.h> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> + +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; + +SwSelectDBTableDialog::SwSelectDBTableDialog(weld::Window* pParent, + const uno::Reference< sdbc::XConnection>& rConnection) + : SfxDialogController(pParent, "modules/swriter/ui/selecttabledialog.ui", "SelectTableDialog") + , m_xConnection(rConnection) + , m_xTable(m_xBuilder->weld_tree_view("table")) + , m_xPreviewPB(m_xBuilder->weld_button("preview")) +{ + m_xTable->set_size_request(m_xTable->get_approximate_digit_width() * 60, + m_xTable->get_height_rows(6)); + + std::vector<int> aWidths; + aWidths.push_back(m_xTable->get_approximate_digit_width() * 30); + m_xTable->set_column_fixed_widths(aWidths); + + m_xPreviewPB->connect_clicked(LINK(this, SwSelectDBTableDialog, PreviewHdl)); + + Reference<XTablesSupplier> xTSupplier(m_xConnection, UNO_QUERY); + if (xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + Sequence<OUString> aTables = xTables->getElementNames(); + const OUString* pTables = aTables.getConstArray(); + for (int i = 0; i < aTables.getLength(); i++) + { + OUString sEntry = pTables[i]; + m_xTable->append_text(sEntry); + m_xTable->set_text(i, SwResId(ST_TABLE), 1); + } + } + Reference<XQueriesSupplier> xQSupplier(m_xConnection, UNO_QUERY); + if (xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + const Sequence<OUString> aQueries = xQueries->getElementNames(); + int nPos = m_xTable->n_children(); + for (const OUString& rQuery : aQueries) + { + m_xTable->append_text(rQuery); + m_xTable->set_text(nPos, SwResId(ST_QUERY), 1); + m_xTable->set_id(nPos, OUString::number(1)); + ++nPos; + } + } +} + +SwSelectDBTableDialog::~SwSelectDBTableDialog() +{ +} + +IMPL_LINK_NOARG(SwSelectDBTableDialog, PreviewHdl, weld::Button&, void) +{ + int nEntry = m_xTable->get_selected_index(); + if (nEntry == -1) + return; + + OUString sTableOrQuery = m_xTable->get_text(nEntry, 0); + sal_Int32 nCommandType = m_xTable->get_id(nEntry).isEmpty() ? 0 : 1; + + OUString sDataSourceName; + Reference<XChild> xChild(m_xConnection, UNO_QUERY); + if(xChild.is()) + { + Reference<XDataSource> xSource(xChild->getParent(), UNO_QUERY); + Reference<XPropertySet> xPrSet(xSource, UNO_QUERY); + xPrSet->getPropertyValue("Name") >>= sDataSourceName; + } + OSL_ENSURE(!sDataSourceName.isEmpty(), "no data source found"); + Sequence<PropertyValue> aProperties(5); + PropertyValue* pProperties = aProperties.getArray(); + pProperties[0].Name = "DataSourceName"; + pProperties[0].Value <<= sDataSourceName; + pProperties[1].Name = "Command"; + pProperties[1].Value <<= sTableOrQuery; + pProperties[2].Name = "CommandType"; + pProperties[2].Value <<= nCommandType; + pProperties[3].Name = "ShowTreeView"; + pProperties[3].Value <<= false; + pProperties[4].Name = "ShowTreeViewButton"; + pProperties[4].Value <<= false; + + SwDBTablePreviewDialog aDlg(m_xDialog.get(), aProperties); + aDlg.run(); +} + +OUString SwSelectDBTableDialog::GetSelectedTable(bool& bIsTable) +{ + int nEntry = m_xTable->get_selected_index(); + if (nEntry != -1) + { + bIsTable = m_xTable->get_id(nEntry).isEmpty(); + return m_xTable->get_text(nEntry, 0); + } + bIsTable = false; + return OUString(); +} + +void SwSelectDBTableDialog::SetSelectedTable(const OUString& rTable, bool bIsTable) +{ + for (int i = 0, nCount = m_xTable->n_children(); i < nCount; ++i) + { + if (m_xTable->get_text(i, 0) == rTable && + m_xTable->get_id(i).isEmpty() == bIsTable) + { + m_xTable->select(i); + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dbui/selectdbtabledialog.hxx b/sw/source/ui/dbui/selectdbtabledialog.hxx new file mode 100644 index 000000000..852a7570d --- /dev/null +++ b/sw/source/ui/dbui/selectdbtabledialog.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_UI_DBUI_SELECTDBTABLEDIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_SELECTDBTABLEDIALOG_HXX + +#include <sfx2/basedlgs.hxx> + +namespace com::sun::star::sdbc { class XConnection; } + +class SwSelectDBTableDialog : public SfxDialogController +{ + css::uno::Reference<css::sdbc::XConnection> m_xConnection; + + std::unique_ptr<weld::TreeView> m_xTable; + std::unique_ptr<weld::Button> m_xPreviewPB; + + DECL_LINK(PreviewHdl, weld::Button&, void); +public: + SwSelectDBTableDialog(weld::Window* pParent, + const css::uno::Reference<css::sdbc::XConnection>& xConnection); + virtual ~SwSelectDBTableDialog() override; + + OUString GetSelectedTable(bool& bIsTable); + void SetSelectedTable(const OUString& rTable, bool bIsTable); +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/abstract.cxx b/sw/source/ui/dialog/abstract.cxx new file mode 100644 index 000000000..01f4b1ea2 --- /dev/null +++ b/sw/source/ui/dialog/abstract.cxx @@ -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 . + */ + +#include <abstract.hxx> + +SwInsertAbstractDlg::SwInsertAbstractDlg(weld::Window* pParent) + : SfxDialogController(pParent, "modules/swriter/ui/abstractdialog.ui", "AbstractDialog") + , m_xLevelNF(m_xBuilder->weld_spin_button("outlines")) + , m_xParaNF(m_xBuilder->weld_spin_button("paras")) +{ +} + +SwInsertAbstractDlg::~SwInsertAbstractDlg() +{ +} + +sal_uInt8 SwInsertAbstractDlg::GetLevel() const +{ + return static_cast<sal_uInt8>(m_xLevelNF->get_value() - 1); +} + +sal_uInt8 SwInsertAbstractDlg::GetPara() const +{ + return static_cast<sal_uInt8>(m_xParaNF->get_value()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/addrdlg.cxx b/sw/source/ui/dialog/addrdlg.cxx new file mode 100644 index 000000000..3c16519ab --- /dev/null +++ b/sw/source/ui/dialog/addrdlg.cxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <addrdlg.hxx> +#include <sfx2/pageids.hxx> +#include <sfx2/sfxdlg.hxx> + +SwAddrDlg::SwAddrDlg(weld::Window* pParent, const SfxItemSet& rSet) + : SfxSingleTabDialogController(pParent, &rSet) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SFXPAGE_GENERAL ); + if ( fnCreatePage ) + { + // create TabPage + SetTabPage(fnCreatePage(get_content_area(), this, &rSet)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/ascfldlg.cxx b/sw/source/ui/dialog/ascfldlg.cxx new file mode 100644 index 000000000..327c5573e --- /dev/null +++ b/sw/source/ui/dialog/ascfldlg.cxx @@ -0,0 +1,434 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <rtl/textenc.h> +#include <i18nlangtag/mslangid.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <unotools/lingucfg.hxx> +#include <unotools/viewoptions.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/printer.hxx> +#include <svl/languageoptions.hxx> +#include <editeng/langitem.hxx> +#include <swtypes.hxx> +#include <ascfldlg.hxx> +#include <shellio.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <vcl/metric.hxx> + +using namespace ::com::sun::star; + +namespace +{ + +const sal_Unicode cDialogExtraDataClose = '}'; +const char sDialogImpExtraData[] = "EncImpDlg:{"; +const char sDialogExpExtraData[] = "EncExpDlg:{"; +const sal_Int32 nDialogExtraDataLen = 11; // 12345678901 + +} + +SwAsciiFilterDlg::SwAsciiFilterDlg( weld::Window* pParent, SwDocShell& rDocSh, + SvStream* pStream ) + : SfxDialogController(pParent, "modules/swriter/ui/asciifilterdialog.ui", "AsciiFilterDialog") + , m_bSaveLineStatus(true) + , m_xCharSetLB(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charset"))) + , m_xFontFT(m_xBuilder->weld_label("fontft")) + , m_xFontLB(m_xBuilder->weld_combo_box("font")) + , m_xLanguageFT(m_xBuilder->weld_label("languageft")) + , m_xLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box("language"))) + , m_xCRLF_RB(m_xBuilder->weld_radio_button("crlf")) + , m_xCR_RB(m_xBuilder->weld_radio_button("cr")) + , m_xLF_RB(m_xBuilder->weld_radio_button("lf")) + , m_xIncludeBOM_CB(m_xBuilder->weld_check_button("includebom")) +{ + m_xFontLB->make_sorted(); + + SwAsciiOptions aOpt; + { + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + if (aDlgOpt.Exists()) + { + css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem"); + aUserItem >>= m_sExtraData; + } + + const OUString sFindNm = OUString::createFromAscii( + pStream ? sDialogImpExtraData + : sDialogExpExtraData); + sal_Int32 nEnd, nStt = m_sExtraData.indexOf( sFindNm ); + if( -1 != nStt ) + { + nStt += nDialogExtraDataLen; + nEnd = m_sExtraData.indexOf( cDialogExtraDataClose, nStt ); + if( -1 != nEnd ) + { + aOpt.ReadUserData(m_sExtraData.copy(nStt, nEnd - nStt)); + nStt -= nDialogExtraDataLen; + m_sExtraData = m_sExtraData.replaceAt(nStt, nEnd - nStt + 1, ""); + } + } + } + + // read the first chars and check the charset, (language - with L&H) + if( pStream ) + { + char aBuffer[ 4098 ]; + const sal_uLong nOldPos = pStream->Tell(); + const size_t nBytesRead = pStream->ReadBytes(aBuffer, 4096); + pStream->Seek( nOldPos ); + + if( nBytesRead <= 4096 ) + { + aBuffer[ nBytesRead ] = '0'; + aBuffer[ nBytesRead+1 ] = '0'; + } + + bool bCR = false, bLF = false, bNullChar = false; + for( sal_uLong nCnt = 0; nCnt < nBytesRead; ++nCnt ) + switch( aBuffer[ nCnt ] ) + { + case 0x0: bNullChar = true; break; + case 0xA: bLF = true; break; + case 0xD: bCR = true; break; + case 0xC: + case 0x1A: + case 0x9: break; + default: break; + } + + if( !bNullChar ) + { + if( bCR ) + { + if( bLF ) + { + aOpt.SetParaFlags( LINEEND_CRLF ); + } + else + { + aOpt.SetParaFlags( LINEEND_CR ); + } + } + else if( bLF ) + { + aOpt.SetParaFlags( LINEEND_LF ); + } + } + + const sal_uInt16 nAppScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ); + SwDoc* pDoc = rDocSh.GetDoc(); + + // initialize language + { + if( !aOpt.GetLanguage() ) + { + if(pDoc) + { + const sal_uInt16 nWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nAppScriptType); + aOpt.SetLanguage( static_cast<const SvxLanguageItem&>(pDoc-> + GetDefault( nWhich )).GetLanguage()); + } + else + { + SvtLinguOptions aLinguOpt; + SvtLinguConfig().GetOptions( aLinguOpt ); + switch(nAppScriptType) + { + case css::i18n::ScriptType::ASIAN: + aOpt.SetLanguage(MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN)); + break; + case css::i18n::ScriptType::COMPLEX: + aOpt.SetLanguage(MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX)); + break; + //SvtScriptType::LATIN: + default: + aOpt.SetLanguage(MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN)); + } + } + } + + m_xLanguageLB->SetLanguageList( SvxLanguageListFlags::ALL, true ); + m_xLanguageLB->set_active_id(aOpt.GetLanguage()); + } + + { + bool bDelPrinter = false; + VclPtr<SfxPrinter> pPrt = pDoc ? pDoc->getIDocumentDeviceAccess().getPrinter(false) : nullptr; + if( !pPrt ) + { + auto pSet = std::make_unique<SfxItemSet>( rDocSh.GetPool(), + svl::Items<SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC>{} ); + pPrt = VclPtr<SfxPrinter>::Create( std::move(pSet) ); + bDelPrinter = true; + } + + // get the set of distinct available family names + std::set< OUString > aFontNames; + int nFontNames = pPrt->GetDevFontCount(); + for( int i = 0; i < nFontNames; i++ ) + { + FontMetric aFontMetric( pPrt->GetDevFont( i ) ); + aFontNames.insert( aFontMetric.GetFamilyName() ); + } + + // insert into listbox + for( const auto& rFontName : aFontNames ) + { + m_xFontLB->append_text(rFontName); + } + + if( aOpt.GetFontName().isEmpty() ) + { + LanguageType eLang = aOpt.GetLanguage(); + vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED, eLang, GetDefaultFontFlags::OnlyOne, pPrt)); + aOpt.SetFontName(aTmpFont.GetFamilyName()); + } + + m_xFontLB->set_active_text(aOpt.GetFontName()); + + if( bDelPrinter ) + pPrt.disposeAndClear(); + } + + // hide the unused Controls for Export + m_xIncludeBOM_CB->hide(); + } + else + { + // hide the unused Controls for Export + m_xFontFT->hide(); + m_xFontLB->hide(); + m_xLanguageFT->hide(); + m_xLanguageLB->hide(); + + + SetIncludeBOM(aOpt.GetIncludeBOM()); + m_xIncludeBOM_CB->save_state(); + } + + // initialize character set + m_xCharSetLB->FillFromTextEncodingTable( pStream != nullptr ); + m_xCharSetLB->SelectTextEncoding( aOpt.GetCharSet() ); + + m_xCharSetLB->connect_changed( LINK( this, SwAsciiFilterDlg, CharSetSelHdl )); + m_xCRLF_RB->connect_toggled( LINK( this, SwAsciiFilterDlg, LineEndHdl )); + m_xLF_RB->connect_toggled( LINK( this, SwAsciiFilterDlg, LineEndHdl )); + m_xCR_RB->connect_toggled( LINK( this, SwAsciiFilterDlg, LineEndHdl )); + + SetCRLF( aOpt.GetParaFlags() ); + + m_xCRLF_RB->save_state(); + m_xLF_RB->save_state(); + m_xCR_RB->save_state(); + + UpdateIncludeBOMSensitiveState(); +} + +SwAsciiFilterDlg::~SwAsciiFilterDlg() +{ + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + aDlgOpt.SetUserItem("UserItem", uno::makeAny(m_sExtraData)); +} + +void SwAsciiFilterDlg::FillOptions( SwAsciiOptions& rOptions ) +{ + sal_uLong nCCode = m_xCharSetLB->GetSelectTextEncoding(); + OUString sFont; + LanguageType nLng = LANGUAGE_SYSTEM; + if (m_xFontLB->get_visible()) + { + sFont = m_xFontLB->get_active_text(); + nLng = m_xLanguageLB->get_active_id(); + } + + rOptions.SetFontName( sFont ); + rOptions.SetCharSet( rtl_TextEncoding( nCCode ) ); + rOptions.SetLanguage( nLng ); + rOptions.SetParaFlags( GetCRLF() ); + rOptions.SetIncludeBOM( GetIncludeBOM() ); + + // save the user settings + OUString sData; + rOptions.WriteUserData( sData ); + if (!sData.isEmpty()) + { + const OUString sFindNm = OUString::createFromAscii( + m_xFontLB->get_visible() ? sDialogImpExtraData + : sDialogExpExtraData); + sal_Int32 nEnd, nStt = m_sExtraData.indexOf( sFindNm ); + if( -1 != nStt ) + { + // called twice, so remove "old" settings + nEnd = m_sExtraData.indexOf( cDialogExtraDataClose, + nStt + nDialogExtraDataLen ); + if( -1 != nEnd ) + m_sExtraData = m_sExtraData.replaceAt( nStt, nEnd - nStt + 1, "" ); + } + m_sExtraData += sFindNm + sData + OUStringChar(cDialogExtraDataClose); + } +} + +void SwAsciiFilterDlg::SetCRLF( LineEnd eEnd ) +{ + switch (eEnd) + { + case LINEEND_CR: + m_xCR_RB->set_active(true); + break; + case LINEEND_CRLF: + m_xCRLF_RB->set_active(true); + break; + case LINEEND_LF: + m_xLF_RB->set_active(true); + break; + } +} + +LineEnd SwAsciiFilterDlg::GetCRLF() const +{ + LineEnd eEnd; + if(m_xCR_RB->get_active()) + eEnd = LINEEND_CR; + else if (m_xLF_RB->get_active()) + eEnd = LINEEND_LF; + else + eEnd = LINEEND_CRLF; + return eEnd; +} + +void SwAsciiFilterDlg::SetIncludeBOM( bool bIncludeBOM ) +{ + m_xIncludeBOM_CB->set_state(bIncludeBOM ? TRISTATE_TRUE : TRISTATE_FALSE); +} + +bool SwAsciiFilterDlg::GetIncludeBOM() const +{ + return m_xIncludeBOM_CB->get_state() != TRISTATE_FALSE; +} + +void SwAsciiFilterDlg::UpdateIncludeBOMSensitiveState() +{ + if (m_xIncludeBOM_CB->get_visible()) + { + switch (m_xCharSetLB->GetSelectTextEncoding()) + { + case RTL_TEXTENCODING_UTF8: + case RTL_TEXTENCODING_UCS2: + m_xIncludeBOM_CB->set_sensitive(true); + break; + default: + m_xIncludeBOM_CB->set_sensitive(false); + break; + } + } +} + +IMPL_LINK_NOARG(SwAsciiFilterDlg, CharSetSelHdl, weld::ComboBox&, void) +{ + LineEnd eOldEnd = GetCRLF(), eEnd = LineEnd(-1); + LanguageType nLng = m_xFontLB->get_visible() + ? m_xLanguageLB->get_active_id() + : LANGUAGE_SYSTEM, + nOldLng = nLng; + + rtl_TextEncoding nChrSet = m_xCharSetLB->GetSelectTextEncoding(); + if( nChrSet == osl_getThreadTextEncoding() ) + eEnd = GetSystemLineEnd(); + else + { + switch( nChrSet ) + { + case RTL_TEXTENCODING_MS_1252: +#ifdef UNX + eEnd = LINEEND_LF; +#else + eEnd = LINEEND_CRLF; // ANSI +#endif + break; + + case RTL_TEXTENCODING_APPLE_ROMAN: // MAC + eEnd = LINEEND_CR; + break; + + case RTL_TEXTENCODING_IBM_850: // DOS + eEnd = LINEEND_CRLF; + break; + + case RTL_TEXTENCODING_APPLE_ARABIC: + case RTL_TEXTENCODING_APPLE_CENTEURO: + case RTL_TEXTENCODING_APPLE_CROATIAN: + case RTL_TEXTENCODING_APPLE_CYRILLIC: + case RTL_TEXTENCODING_APPLE_DEVANAGARI: + case RTL_TEXTENCODING_APPLE_FARSI: + case RTL_TEXTENCODING_APPLE_GREEK: + case RTL_TEXTENCODING_APPLE_GUJARATI: + case RTL_TEXTENCODING_APPLE_GURMUKHI: + case RTL_TEXTENCODING_APPLE_HEBREW: + case RTL_TEXTENCODING_APPLE_ICELAND: + case RTL_TEXTENCODING_APPLE_ROMANIAN: + case RTL_TEXTENCODING_APPLE_THAI: + case RTL_TEXTENCODING_APPLE_TURKISH: + case RTL_TEXTENCODING_APPLE_UKRAINIAN: + case RTL_TEXTENCODING_APPLE_CHINSIMP: + case RTL_TEXTENCODING_APPLE_CHINTRAD: + case RTL_TEXTENCODING_APPLE_JAPANESE: + case RTL_TEXTENCODING_APPLE_KOREAN: + eEnd = LINEEND_CR; + break; + } + } + + m_bSaveLineStatus = false; + if( eEnd != LineEnd(-1) ) // changed? + { + if( eOldEnd != eEnd ) + SetCRLF( eEnd ); + } + else + { + // restore old user choice (not the automatic!) + m_xCRLF_RB->set_state(m_xCRLF_RB->get_saved_state()); + m_xCR_RB->set_state(m_xCR_RB->get_saved_state()); + m_xLF_RB->set_state(m_xLF_RB->get_saved_state()); + } + m_bSaveLineStatus = true; + + if (nOldLng != nLng && m_xFontLB->get_visible()) + m_xLanguageLB->set_active_id(nLng); + + UpdateIncludeBOMSensitiveState(); +} + +IMPL_LINK(SwAsciiFilterDlg, LineEndHdl, weld::ToggleButton&, rBtn, void) +{ + if (m_bSaveLineStatus) + rBtn.save_state(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/docstdlg.cxx b/sw/source/ui/dialog/docstdlg.cxx new file mode 100644 index 000000000..7aacfd94c --- /dev/null +++ b/sw/source/ui/dialog/docstdlg.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 <swwait.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <pview.hxx> +#include <doc.hxx> +#include <docstdlg.hxx> +#include <IDocumentStatistics.hxx> + +#include <unotools/localedatawrapper.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +std::unique_ptr<SfxTabPage> SwDocStatPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwDocStatPage>(pPage, pController, *rSet); +} + +SwDocStatPage::SwDocStatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/statisticsinfopage.ui", "StatisticsInfoPage", &rSet) + , m_xPageNo(m_xBuilder->weld_label("nopages")) + , m_xTableNo(m_xBuilder->weld_label("notables")) + , m_xGrfNo(m_xBuilder->weld_label("nogrfs")) + , m_xOLENo(m_xBuilder->weld_label("nooles")) + , m_xParaNo(m_xBuilder->weld_label("noparas")) + , m_xWordNo(m_xBuilder->weld_label("nowords")) + , m_xCharNo(m_xBuilder->weld_label("nochars")) + , m_xCharExclSpacesNo(m_xBuilder->weld_label("nocharsexspaces")) + , m_xLineLbl(m_xBuilder->weld_label("lineft")) + , m_xLineNo(m_xBuilder->weld_label("nolines")) + , m_xUpdatePB(m_xBuilder->weld_button("update")) +{ + Update(); + m_xUpdatePB->connect_clicked(LINK(this, SwDocStatPage, UpdateHdl)); + //#111684# is the current view a page preview no SwFEShell can be found -> hide the update button + SwDocShell* pDocShell = static_cast<SwDocShell*>( SfxObjectShell::Current() ); + SwFEShell* pFEShell = pDocShell->GetFEShell(); + if(!pFEShell) + { + m_xUpdatePB->hide(); + m_xLineLbl->hide(); + m_xLineNo->hide(); + } +} + +SwDocStatPage::~SwDocStatPage() +{ +} + +// Description: fill ItemSet when changed +bool SwDocStatPage::FillItemSet(SfxItemSet * /*rSet*/) +{ + return false; +} + +void SwDocStatPage::Reset(const SfxItemSet *) +{ +} + +// Description: update / set data +void SwDocStatPage::SetData(const SwDocStat &rStat) +{ + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetUILocaleDataWrapper(); + m_xTableNo->set_label(rLocaleData.getNum(rStat.nTable, 0)); + m_xGrfNo->set_label(rLocaleData.getNum(rStat.nGrf, 0)); + m_xOLENo->set_label(rLocaleData.getNum(rStat.nOLE, 0)); + m_xPageNo->set_label(rLocaleData.getNum(rStat.nPage, 0)); + m_xParaNo->set_label(rLocaleData.getNum(rStat.nPara, 0)); + m_xWordNo->set_label(rLocaleData.getNum(rStat.nWord, 0)); + m_xCharNo->set_label(rLocaleData.getNum(rStat.nChar, 0)); + m_xCharExclSpacesNo->set_label(rLocaleData.getNum(rStat.nCharExcludingSpaces, 0)); +} + +// Description: update statistics +void SwDocStatPage::Update() +{ + SfxViewShell *pVSh = SfxViewShell::Current(); + SwViewShell *pSh = nullptr; + if ( dynamic_cast< const SwView *>( pVSh ) != nullptr ) + pSh = static_cast<SwView*>(pVSh)->GetWrtShellPtr(); + else if ( dynamic_cast< const SwPagePreview *>( pVSh ) != nullptr ) + pSh = static_cast<SwPagePreview*>(pVSh)->GetViewShell(); + + OSL_ENSURE( pSh, "Shell not found" ); + + if (!pSh) + return; + + SwWait aWait( *pSh->GetDoc()->GetDocShell(), true ); + pSh->StartAction(); + aDocStat = pSh->GetDoc()->getIDocumentStatistics().GetUpdatedDocStat( false, true ); + pSh->EndAction(); + + SetData(aDocStat); +} + +IMPL_LINK_NOARG(SwDocStatPage, UpdateHdl, weld::Button&, void) +{ + Update(); + SwDocShell* pDocShell = static_cast<SwDocShell*>( SfxObjectShell::Current()); + SwFEShell* pFEShell = pDocShell->GetFEShell(); + if (pFEShell) + m_xLineNo->set_label(OUString::number(pFEShell->GetLineCount())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/macassgn.cxx b/sw/source/ui/dialog/macassgn.cxx new file mode 100644 index 000000000..3edef40ea --- /dev/null +++ b/sw/source/ui/dialog/macassgn.cxx @@ -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 . + */ + +#include <hintids.hxx> + +#include <sfx2/htmlmode.hxx> +#include <svx/svxids.hrc> +#include <svl/macitem.hxx> + +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <macassgn.hxx> +#include <docsh.hxx> +#include <strings.hrc> +#include <view.hxx> +#include <sfx2/viewfrm.hxx> + +#include <svx/svxdlg.hxx> + + +SfxEventNamesItem SwMacroAssignDlg::AddEvents( DlgEventType eType ) +{ + SfxEventNamesItem aItem(SID_EVENTCONFIG); + + sal_uInt16 nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + bool bHtmlMode = nHtmlMode & HTMLMODE_ON; + + switch( eType ) + { + case MACASSGN_AUTOTEXT: + aItem.AddEvent( SwResId(STR_EVENT_START_INS_GLOSSARY), OUString(), + SvMacroItemId::SwStartInsGlossary ); + aItem.AddEvent( SwResId(STR_EVENT_END_INS_GLOSSARY), OUString(), + SvMacroItemId::SwEndInsGlossary); + // in order for the new handler to become active! + break; + case MACASSGN_ALLFRM: + case MACASSGN_GRAPHIC: // graphics + { + aItem.AddEvent( SwResId(STR_EVENT_IMAGE_ERROR), OUString(), + SvMacroItemId::OnImageLoadError); + aItem.AddEvent( SwResId(STR_EVENT_IMAGE_ABORT), OUString(), + SvMacroItemId::OnImageLoadCancel); + aItem.AddEvent( SwResId(STR_EVENT_IMAGE_LOAD), OUString(), + SvMacroItemId::OnImageLoadDone); + } + [[fallthrough]]; + case MACASSGN_FRMURL: // Frame - URL-Attributes + { + if( !bHtmlMode && + (MACASSGN_FRMURL == eType || MACASSGN_ALLFRM == eType)) + { + aItem.AddEvent( SwResId( STR_EVENT_FRM_KEYINPUT_A ), OUString(), + SvMacroItemId::SwFrmKeyInputAlpha ); + aItem.AddEvent( SwResId( STR_EVENT_FRM_KEYINPUT_NOA ), OUString(), + SvMacroItemId::SwFrmKeyInputNoAlpha ); + aItem.AddEvent( SwResId( STR_EVENT_FRM_RESIZE ), OUString(), + SvMacroItemId::SwFrmResize ); + aItem.AddEvent( SwResId( STR_EVENT_FRM_MOVE ), OUString(), + SvMacroItemId::SwFrmMove ); + } + } + [[fallthrough]]; + case MACASSGN_OLE: // OLE + { + if( !bHtmlMode ) + aItem.AddEvent( SwResId(STR_EVENT_OBJECT_SELECT), OUString(), + SvMacroItemId::SwObjectSelect ); + } + [[fallthrough]]; + case MACASSGN_INETFMT: // INetFormat-Attributes + { + aItem.AddEvent( SwResId(STR_EVENT_MOUSEOVER_OBJECT), OUString(), + SvMacroItemId::OnMouseOver ); + aItem.AddEvent( SwResId(STR_EVENT_MOUSECLICK_OBJECT), OUString(), + SvMacroItemId::OnClick); + aItem.AddEvent( SwResId(STR_EVENT_MOUSEOUT_OBJECT), OUString(), + SvMacroItemId::OnMouseOut); + } + break; + } + + return aItem; +} + +bool SwMacroAssignDlg::INetFormatDlg(weld::Window* pParent, SwWrtShell& rSh, + std::unique_ptr<SvxMacroItem>& rpINetItem ) +{ + bool bRet = false; + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_FRMMACRO, RES_FRMMACRO, SID_EVENTCONFIG, SID_EVENTCONFIG>{} ); + SvxMacroItem aItem( RES_FRMMACRO ); + if( !rpINetItem ) + rpINetItem.reset(new SvxMacroItem( RES_FRMMACRO )); + else + aItem.SetMacroTable( rpINetItem->GetMacroTable() ); + + aSet.Put( aItem ); + aSet.Put( AddEvents( MACASSGN_INETFMT ) ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pMacroDlg( pFact->CreateEventConfigDialog(pParent, aSet, + rSh.GetView().GetViewFrame()->GetFrame().GetFrameInterface() ) ); + if ( pMacroDlg && pMacroDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pMacroDlg->GetOutputItemSet(); + const SfxPoolItem* pItem; + if( SfxItemState::SET == pOutSet->GetItemState( RES_FRMMACRO, false, &pItem )) + { + rpINetItem->SetMacroTable( static_cast<const SvxMacroItem*>(pItem)->GetMacroTable() ); + bRet = true; + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/swdlgfact.cxx b/sw/source/ui/dialog/swdlgfact.cxx new file mode 100644 index 000000000..163d315bb --- /dev/null +++ b/sw/source/ui/dialog/swdlgfact.cxx @@ -0,0 +1,1258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "swdlgfact.hxx" +#include <svl/style.hxx> +#include <globals.hrc> + +#include <wordcountdialog.hxx> +#include <abstract.hxx> +#include <addrdlg.hxx> +#include <ascfldlg.hxx> +#include <bookmark.hxx> +#include <break.hxx> +#include <changedb.hxx> +#include <chrdlg.hxx> +#include <colwd.hxx> +#include <convert.hxx> +#include <cption.hxx> +#include <dbinsdlg.hxx> +#include <docfnote.hxx> +#include <docstdlg.hxx> +#include <DateFormFieldDialog.hxx> +#include <DropDownFieldDialog.hxx> +#include <DropDownFormFieldDialog.hxx> +#include <envlop.hxx> +#include <label.hxx> +#include <drpcps.hxx> +#include <swuipardlg.hxx> +#include <pattern.hxx> +#include <pardlg.hxx> +#include <rowht.hxx> +#include <selglos.hxx> +#include <splittbl.hxx> +#include <srtdlg.hxx> +#include <tautofmt.hxx> +#include <tblnumfm.hxx> +#include <wrap.hxx> +#include <tabledlg.hxx> +#include <fldtdlg.hxx> +#include <fldedt.hxx> +#include <swrenamexnameddlg.hxx> +#include <swmodalredlineacceptdlg.hxx> +#include <frmdlg.hxx> +#include <tmpdlg.hxx> +#include <glossary.hxx> +#include <inpdlg.hxx> +#include <insfnote.hxx> +#include <instable.hxx> +#include <javaedit.hxx> +#include <linenum.hxx> +#include <titlepage.hxx> +#include <mailmrge.hxx> +#include <mergetbl.hxx> +#include <multmrk.hxx> +#include <num.hxx> +#include <outline.hxx> +#include <column.hxx> +#include <cnttab.hxx> +#include <swuicnttab.hxx> +#include <regionsw.hxx> +#include <optcomp.hxx> +#include <optload.hxx> +#include <optpage.hxx> +#include <swuiidxmrk.hxx> +#include <svx/dialogs.hrc> +#include <mailmergewizard.hxx> +#include <mailconfigpage.hxx> +#include <uiborder.hxx> +#include <mmresultdialogs.hxx> + + +using namespace ::com::sun::star; +using namespace css::frame; +using namespace css::uno; + +short AbstractSwWordCountFloatDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwInsertAbstractDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SwAbstractSfxController_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwAsciiFilterDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSplitTableDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwBreakDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwTableWidthDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwTableHeightDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwMergeTableDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractGenericDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractGenericDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return weld::GenericDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractSwSortDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMultiTOXMarkDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractTabController_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwConvertTableDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwInsertDBColAutoPilot_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractDropDownFieldDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractDropDownFormFieldDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractDateFormFieldDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwLabDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwSelGlossaryDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwAutoFormatDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwFieldDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwRenameXNamedDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSwModalRedlineAcceptDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractGlossaryDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractFieldInputDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractInsFootNoteDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractJavaEditDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMailMergeDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMailMergeCreateFromDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMailMergeFieldConnectionsDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMultiTOXTabDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractMultiTOXTabDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractEditRegionDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractInsertSectionTabDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractInsertSectionTabDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractIndexMarkFloatDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractAuthMarkFloatDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +void AbstractTabController_Impl::SetCurPageId( const OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* AbstractTabController_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +const sal_uInt16* AbstractTabController_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void AbstractTabController_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +bool AbstractTabController_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +//From class Window. +void AbstractTabController_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +IMPL_LINK_NOARG(AbstractApplyTabController_Impl, ApplyHdl, weld::Button&, void) +{ + if (m_xDlg->Apply()) + { + m_aHandler.Call(nullptr); + m_xDlg->Applied(); + } +} + +void AbstractApplyTabController_Impl::SetApplyHdl( const Link<LinkParamNone*,void>& rLink ) +{ + m_aHandler = rLink; + m_xDlg->SetApplyHandler(LINK(this, AbstractApplyTabController_Impl, ApplyHdl)); +} + +sal_uInt8 AbstractSwInsertAbstractDlg_Impl::GetLevel() const +{ + return m_xDlg->GetLevel(); +} + +sal_uInt8 AbstractSwInsertAbstractDlg_Impl::GetPara() const +{ + return m_xDlg->GetPara(); +} + +const SfxItemSet* SwAbstractSfxController_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +void SwAbstractSfxController_Impl::SetText(const OUString& rStr) +{ + m_xDlg->set_title(rStr); +} + +void AbstractSwAsciiFilterDlg_Impl::FillOptions( SwAsciiOptions& rOptions ) +{ + m_xDlg->FillOptions(rOptions); +} + +SplitTable_HeadlineOption AbstractSplitTableDialog_Impl::GetSplitMode() +{ + return m_xDlg->GetSplitMode(); +} + +OUString AbstractSwBreakDlg_Impl::GetTemplateName() +{ + return m_xDlg->GetTemplateName(); +} + +sal_uInt16 AbstractSwBreakDlg_Impl:: GetKind() +{ + return m_xDlg->GetKind(); +} + +::std::optional<sal_uInt16> AbstractSwBreakDlg_Impl:: GetPageNumber() +{ + return m_xDlg->GetPageNumber(); +} + +void AbstractSwConvertTableDlg_Impl::GetValues( sal_Unicode& rDelim,SwInsertTableOptions& rInsTableFlags, + SwTableAutoFormat const*& prTAFormat) +{ + m_xDlg->GetValues(rDelim,rInsTableFlags, prTAFormat); +} + +void AbstractSwInsertDBColAutoPilot_Impl::DataToDoc( const uno::Sequence< uno::Any >& rSelection, + uno::Reference< sdbc::XDataSource> rxSource, + uno::Reference< sdbc::XConnection> xConnection, + uno::Reference< sdbc::XResultSet > xResultSet) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + m_xDlg->DataToDoc(rSelection, rxSource, xConnection, xResultSet); +#else + (void) rSelection; + (void) rxSource; + (void) xConnection; + (void) xResultSet; +#endif +} + +bool AbstractDropDownFieldDialog_Impl::PrevButtonPressed() const +{ + return m_xDlg->PrevButtonPressed(); +} + +bool AbstractDropDownFieldDialog_Impl::NextButtonPressed() const +{ + return m_xDlg->NextButtonPressed(); +} + +void AbstractSwLabDlg_Impl::SetCurPageId( const OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* AbstractSwLabDlg_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +const sal_uInt16* AbstractSwLabDlg_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void AbstractSwLabDlg_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +void AbstractSwLabDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +const OUString& AbstractSwLabDlg_Impl::GetBusinessCardStr() const +{ + return m_xDlg->GetBusinessCardStr(); +} + +Printer * AbstractSwLabDlg_Impl::GetPrt() +{ + return m_xDlg->GetPrt(); +} + +void AbstractSwSelGlossaryDlg_Impl::InsertGlos(const OUString &rRegion, const OUString &rGlosName) +{ + m_xDlg->InsertGlos( rRegion, rGlosName ); +} + +sal_Int32 AbstractSwSelGlossaryDlg_Impl::GetSelectedIdx() const +{ + return m_xDlg->GetSelectedIdx(); +} + +void AbstractSwSelGlossaryDlg_Impl::SelectEntryPos(sal_Int32 nIdx) +{ + m_xDlg->SelectEntryPos( nIdx ); +} + +std::unique_ptr<SwTableAutoFormat> AbstractSwAutoFormatDlg_Impl::FillAutoFormatOfIndex() const +{ + return m_xDlg->FillAutoFormatOfIndex(); +} + +void AbstractSwFieldDlg_Impl::SetCurPageId( const OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* AbstractSwFieldDlg_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +const sal_uInt16* AbstractSwFieldDlg_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void AbstractSwFieldDlg_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +void AbstractSwFieldDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +bool AbstractSwFieldDlg_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + auto xDlg = m_xDlg; + return SfxTabDialogController::runAsync(m_xDlg, [=](sal_Int32 nResult){ + xDlg->Close(); + if (rCtx.isSet()) + rCtx.maEndDialogFn(nResult); + }); +} + +void AbstractSwFieldDlg_Impl::Initialize(SfxChildWinInfo *pInfo) +{ + m_xDlg->Initialize( pInfo ); +} + +void AbstractSwFieldDlg_Impl::ReInitDlg() +{ + m_xDlg->ReInitDlg(); +} + +void AbstractSwFieldDlg_Impl::ActivateDatabasePage() +{ + m_xDlg->ActivateDatabasePage(); +} + +void AbstractSwFieldDlg_Impl::ShowReferencePage() +{ + m_xDlg->ShowReferencePage(); +} + +std::shared_ptr<SfxDialogController> AbstractSwFieldDlg_Impl::GetController() +{ + return m_xDlg; +} + +void AbstractSwRenameXNamedDlg_Impl::SetForbiddenChars( const OUString& rSet ) +{ + m_xDlg->SetForbiddenChars( rSet ); +} + +void AbstractSwRenameXNamedDlg_Impl::SetAlternativeAccess( + css::uno::Reference< css::container::XNameAccess > & xSecond, + css::uno::Reference< css::container::XNameAccess > & xThird ) +{ + m_xDlg->SetAlternativeAccess( xSecond, xThird); +} + +void AbstractSwModalRedlineAcceptDlg_Impl::AcceptAll( bool bAccept ) +{ + m_xDlg->AcceptAll( bAccept); +} + +OUString AbstractGlossaryDlg_Impl::GetCurrGrpName() const +{ + return m_xDlg->GetCurrGrpName(); +} + +OUString AbstractGlossaryDlg_Impl::GetCurrShortName() const +{ + return m_xDlg->GetCurrShortName(); +} + +void AbstractFieldInputDlg_Impl::EndDialog(sal_Int32 n) +{ + m_xDlg->response(n); +} + +bool AbstractFieldInputDlg_Impl::PrevButtonPressed() const +{ + return m_xDlg->PrevButtonPressed(); +} + +bool AbstractFieldInputDlg_Impl::NextButtonPressed() const +{ + return m_xDlg->NextButtonPressed(); +} + +OUString AbstractInsFootNoteDlg_Impl::GetFontName() +{ + return m_xDlg->GetFontName(); +} + +bool AbstractInsFootNoteDlg_Impl::IsEndNote() +{ + return m_xDlg->IsEndNote(); +} + +OUString AbstractInsFootNoteDlg_Impl::GetStr() +{ + return m_xDlg->GetStr(); +} + +void AbstractInsFootNoteDlg_Impl::SetHelpId(const OString& rHelpId) +{ + m_xDlg->set_help_id(rHelpId); +} + +void AbstractInsFootNoteDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +void AbstractInsTableDlg_Impl::GetValues( OUString& rName, sal_uInt16& rRow, sal_uInt16& rCol, + SwInsertTableOptions& rInsTableFlags, OUString& rTableAutoFormatName, + std::unique_ptr<SwTableAutoFormat>& prTAFormat ) +{ + SwInsTableDlg* pDlg = dynamic_cast<SwInsTableDlg*>(m_xDlg.get()); + if (pDlg) + pDlg->GetValues(rName, rRow, rCol, rInsTableFlags, rTableAutoFormatName, prTAFormat); +} + +OUString AbstractJavaEditDialog_Impl::GetScriptText() const +{ + return m_xDlg->GetScriptText(); +} + +OUString AbstractJavaEditDialog_Impl::GetScriptType() const +{ + return m_xDlg->GetScriptType(); +} + +bool AbstractJavaEditDialog_Impl::IsUrl() const +{ + return m_xDlg->IsUrl(); +} + +bool AbstractJavaEditDialog_Impl::IsNew() const +{ + return m_xDlg->IsNew(); +} + +bool AbstractJavaEditDialog_Impl::IsUpdate() const +{ + return m_xDlg->IsUpdate(); +} + +DBManagerOptions AbstractMailMergeDlg_Impl::GetMergeType() +{ + return m_xDlg->GetMergeType(); +} + +const OUString& AbstractMailMergeDlg_Impl::GetSaveFilter() const +{ + return m_xDlg->GetSaveFilter(); +} + +css::uno::Sequence< css::uno::Any > AbstractMailMergeDlg_Impl::GetSelection() const +{ + return m_xDlg->GetSelection(); +} + +uno::Reference< sdbc::XResultSet> AbstractMailMergeDlg_Impl::GetResultSet() const +{ + return m_xDlg->GetResultSet(); +} + +bool AbstractMailMergeDlg_Impl::IsSaveSingleDoc() const +{ + return m_xDlg->IsSaveSingleDoc(); +} + +bool AbstractMailMergeDlg_Impl::IsGenerateFromDataBase() const +{ + return m_xDlg->IsGenerateFromDataBase(); +} + +bool AbstractMailMergeDlg_Impl::IsFileEncryptedFromDataBase() const +{ + return m_xDlg->IsFileEncryptedFromDataBase(); +} + +OUString AbstractMailMergeDlg_Impl::GetColumnName() const +{ + return m_xDlg->GetColumnName(); +} + +OUString AbstractMailMergeDlg_Impl::GetPasswordColumnName() const +{ + return m_xDlg->GetPasswordColumnName(); +} + +OUString AbstractMailMergeDlg_Impl::GetTargetURL() const +{ + return m_xDlg->GetTargetURL(); +} + +bool AbstractMailMergeCreateFromDlg_Impl::IsThisDocument() const +{ + return m_xDlg->IsThisDocument(); +} + +bool AbstractMailMergeFieldConnectionsDlg_Impl::IsUseExistingConnections() const +{ + return m_xDlg->IsUseExistingConnections(); +} + +CurTOXType AbstractMultiTOXTabDialog_Impl::GetCurrentTOXType() const +{ + return m_xDlg->GetCurrentTOXType(); +} + +SwTOXDescription& AbstractMultiTOXTabDialog_Impl::GetTOXDescription(CurTOXType eTOXTypes) +{ + return m_xDlg->GetTOXDescription(eTOXTypes); +} + +const SfxItemSet* AbstractMultiTOXTabDialog_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +void AbstractEditRegionDlg_Impl::SelectSection(const OUString& rSectionName) +{ + m_xDlg->SelectSection(rSectionName); +} + +void +AbstractInsertSectionTabDialog_Impl::SetSectionData(SwSectionData const& rSect) +{ + m_xDlg->SetSectionData(rSect); +} + +void AbstractIndexMarkFloatDlg_Impl::ReInitDlg(SwWrtShell& rWrtShell) +{ + m_xDlg->ReInitDlg( rWrtShell); +} + +std::shared_ptr<SfxDialogController> AbstractIndexMarkFloatDlg_Impl::GetController() +{ + return m_xDlg; +} + +void AbstractAuthMarkFloatDlg_Impl::ReInitDlg(SwWrtShell& rWrtShell) +{ + m_xDlg->ReInitDlg(rWrtShell); +} + +std::shared_ptr<SfxDialogController> AbstractAuthMarkFloatDlg_Impl::GetController() +{ + return m_xDlg; +} + +std::shared_ptr<SfxDialogController> AbstractSwWordCountFloatDlg_Impl::GetController() +{ + return m_xDlg; +} + +void AbstractSwWordCountFloatDlg_Impl::UpdateCounts() +{ + m_xDlg->UpdateCounts(); +} + +void AbstractSwWordCountFloatDlg_Impl::SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat) +{ + m_xDlg->SetCounts(rCurrCnt, rDocStat); +} + +AbstractMailMergeWizard_Impl::~AbstractMailMergeWizard_Impl() +{ +} + +bool AbstractMailMergeWizard_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + // SwMailMergeWizardExecutor wants to run the lifecycle of this dialog + // so clear mxOwner here and leave it up to SwMailMergeWizardExecutor + rCtx.mxOwner.clear(); + return weld::GenericDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractMailMergeWizard_Impl::Execute() +{ + return m_xDlg->run(); +} + +OUString AbstractMailMergeWizard_Impl::GetReloadDocument() const +{ + return m_xDlg->GetReloadDocument(); +} + +void AbstractMailMergeWizard_Impl::ShowPage( sal_uInt16 nLevel ) +{ + m_xDlg->skipUntil(nLevel); +} + +sal_uInt16 AbstractMailMergeWizard_Impl::GetRestartPage() const +{ + return m_xDlg->GetRestartPage(); +} + +VclPtr<AbstractSwInsertAbstractDlg> SwAbstractDialogFactory_Impl::CreateSwInsertAbstractDlg(weld::Window* pParent) +{ + return VclPtr<AbstractSwInsertAbstractDlg_Impl>::Create(std::make_unique<SwInsertAbstractDlg>(pParent)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwAddressAbstractDlg(weld::Window* pParent, + const SfxItemSet& rSet) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwAddrDlg>(pParent, rSet)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwDropCapsDialog(weld::Window* pParent, + const SfxItemSet& rSet) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwDropCapsDlg>(pParent, rSet)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwBackgroundDialog(weld::Window* pParent, + const SfxItemSet& rSet) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwBackgroundDlg>(pParent, rSet)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateNumFormatDialog(weld::Widget* pParent, const SfxItemSet& rSet) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwNumFormatDlg>(pParent, rSet)); +} + +VclPtr<AbstractSwAsciiFilterDlg> SwAbstractDialogFactory_Impl::CreateSwAsciiFilterDlg(weld::Window* pParent, + SwDocShell& rDocSh, SvStream* pStream) +{ + return VclPtr<AbstractSwAsciiFilterDlg_Impl>::Create(std::make_unique<SwAsciiFilterDlg>(pParent, rDocSh, pStream)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwInsertBookmarkDlg(weld::Window *pParent, + SwWrtShell &rSh, SfxRequest& rReq) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwInsertBookmarkDlg>(pParent, rSh, rReq)); +} + +VclPtr<AbstractSwBreakDlg> SwAbstractDialogFactory_Impl::CreateSwBreakDlg(weld::Window* pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractSwBreakDlg_Impl>::Create(std::make_unique<SwBreakDlg>(pParent, rSh)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwChangeDBDlg(SwView& rVw) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwChangeDBDlg>(rVw)); +#else + (void) rVw; + return nullptr; +#endif +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSwCharDlg(weld::Window* pParent, SwView& pVw, + const SfxItemSet& rCoreSet, SwCharDlgMode nDialogMode, const OUString* pFormatStr) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwCharDlg>(pParent, pVw, rCoreSet, nDialogMode, pFormatStr)); +} + +VclPtr<AbstractSwConvertTableDlg> SwAbstractDialogFactory_Impl::CreateSwConvertTableDlg(SwView& rView, bool bToTable) +{ + return VclPtr<AbstractSwConvertTableDlg_Impl>::Create(std::make_unique<SwConvertTableDlg>(rView, bToTable)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwCaptionDialog(weld::Window *pParent, SwView &rV) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwCaptionDialog>(pParent, rV)); +} + +VclPtr<AbstractSwInsertDBColAutoPilot> SwAbstractDialogFactory_Impl::CreateSwInsertDBColAutoPilot( SwView& rView, + uno::Reference< sdbc::XDataSource> rxSource, + uno::Reference<sdbcx::XColumnsSupplier> xColSupp, + const SwDBData& rData) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + return VclPtr<AbstractSwInsertDBColAutoPilot_Impl>::Create(std::make_unique<SwInsertDBColAutoPilot>(rView, rxSource, xColSupp, rData)); +#else + (void) rView; + (void) rxSource; + (void) xColSupp; + (void) rData; + return nullptr; +#endif +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSwFootNoteOptionDlg(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwFootNoteOptionDlg>(pParent, rSh)); +} + +VclPtr<AbstractDropDownFieldDialog> SwAbstractDialogFactory_Impl::CreateDropDownFieldDialog(weld::Widget *pParent, + SwWrtShell &rSh, SwField* pField, bool bPrevButton, bool bNextButton) +{ + return VclPtr<AbstractDropDownFieldDialog_Impl>::Create(std::make_unique<sw::DropDownFieldDialog>(pParent, rSh, pField, bPrevButton, bNextButton)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateDropDownFormFieldDialog(weld::Widget *pParent, sw::mark::IFieldmark* pDropDownField) +{ + return VclPtr<AbstractDropDownFormFieldDialog_Impl>::Create(std::make_unique<sw::DropDownFormFieldDialog>(pParent, pDropDownField)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateDateFormFieldDialog(weld::Widget *pParent, sw::mark::IDateFieldmark* pDateField, SwDoc* pDoc) +{ + return VclPtr<AbstractDateFormFieldDialog_Impl>::Create(std::make_unique<sw::DateFormFieldDialog>(pParent, pDateField, pDoc)); +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSwEnvDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell* pWrtSh, Printer* pPrt, + bool bInsert) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwEnvDlg>(pParent, rSet, pWrtSh,pPrt, bInsert)); +} + +VclPtr<AbstractSwLabDlg> SwAbstractDialogFactory_Impl::CreateSwLabDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwDBManager* pDBManager, bool bLabel) +{ + return VclPtr<AbstractSwLabDlg_Impl>::Create(std::make_unique<SwLabDlg>(pParent, rSet, pDBManager, bLabel)); +} + +SwLabDlgMethod SwAbstractDialogFactory_Impl::GetSwLabDlgStaticMethod () +{ + return SwLabDlg::UpdateFieldInformation; +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSwParaDlg(weld::Window *pParent, SwView& rVw, + const SfxItemSet& rCoreSet, + bool bDraw , + const OString& sDefPage) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwParaDlg>(pParent, rVw, rCoreSet, DLG_STD, nullptr, bDraw, sDefPage)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwAutoMarkDialog(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwAuthMarkModalDlg>(pParent, rSh)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwColumnDialog(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwColumnDlg>(pParent, rSh)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwTableHeightDialog(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractSwTableHeightDlg_Impl>::Create(std::make_unique<SwTableHeightDlg>(pParent, rSh)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwSortingDialog(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractSwSortDlg_Impl>::Create(std::make_unique<SwSortDlg>(pParent, rSh)); +} + +VclPtr<AbstractSplitTableDialog> SwAbstractDialogFactory_Impl::CreateSplitTableDialog(weld::Window *pParent, SwWrtShell &rSh) +{ + return VclPtr<AbstractSplitTableDialog_Impl>::Create(std::make_unique<SwSplitTableDlg>(pParent, rSh)); +} + +VclPtr<AbstractSwSelGlossaryDlg> SwAbstractDialogFactory_Impl::CreateSwSelGlossaryDlg(weld::Window *pParent, const OUString &rShortName) +{ + return VclPtr<AbstractSwSelGlossaryDlg_Impl>::Create(std::make_unique<SwSelGlossaryDlg>(pParent, rShortName)); +} + +VclPtr<AbstractSwAutoFormatDlg> SwAbstractDialogFactory_Impl::CreateSwAutoFormatDlg(weld::Window* pParent, + SwWrtShell* pShell, bool bSetAutoFormat, const SwTableAutoFormat* pSelFormat) +{ + return VclPtr<AbstractSwAutoFormatDlg_Impl>::Create( + std::unique_ptr<SwAutoFormatDlg, o3tl::default_delete<SwAutoFormatDlg>>( + new SwAutoFormatDlg(pParent, pShell, bSetAutoFormat, pSelFormat))); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwBorderDlg(weld::Window* pParent, SfxItemSet& rSet, SwBorderModes nType ) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwBorderDlg>(pParent, rSet, nType)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwWrapDlg(weld::Window* pParent, SfxItemSet& rSet, SwWrtShell* pSh) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwWrapDlg>(pParent, rSet, pSh, true/*bDrawMode*/)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwTableWidthDlg(weld::Window *pParent, SwTableFUNC &rFnc) +{ + return VclPtr<AbstractSwTableWidthDlg_Impl>::Create(std::make_unique<SwTableWidthDlg>(pParent, rFnc)); +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSwTableTabDlg(weld::Window* pParent, + const SfxItemSet* pItemSet, SwWrtShell* pSh) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwTableTabDlg>(pParent, pItemSet, pSh)); +} + +VclPtr<AbstractSwFieldDlg> SwAbstractDialogFactory_Impl::CreateSwFieldDlg(SfxBindings* pB, SwChildWinWrapper* pCW, weld::Window *pParent) +{ + return VclPtr<AbstractSwFieldDlg_Impl>::Create(std::make_shared<SwFieldDlg>(pB, pCW, pParent)); +} + +VclPtr<SfxAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwFieldEditDlg(SwView& rVw) +{ + return VclPtr<SwAbstractSfxController_Impl>::Create(std::make_unique<SwFieldEditDlg>(rVw)); +} + +VclPtr<AbstractSwRenameXNamedDlg> SwAbstractDialogFactory_Impl::CreateSwRenameXNamedDlg(weld::Window* pParent, + css::uno::Reference< css::container::XNamed > & xNamed, + css::uno::Reference< css::container::XNameAccess > & xNameAccess) +{ + return VclPtr<AbstractSwRenameXNamedDlg_Impl>::Create(std::make_unique<SwRenameXNamedDlg>(pParent,xNamed, xNameAccess)); +} + +VclPtr<AbstractSwModalRedlineAcceptDlg> SwAbstractDialogFactory_Impl::CreateSwModalRedlineAcceptDlg(weld::Window *pParent) +{ + return VclPtr<AbstractSwModalRedlineAcceptDlg_Impl>::Create(std::make_unique<SwModalRedlineAcceptDlg>(pParent)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateTableMergeDialog(weld::Window* pParent, bool& rWithPrev) +{ + return VclPtr<AbstractSwMergeTableDlg_Impl>::Create(std::make_unique<SwMergeTableDlg>(pParent, rWithPrev)); +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateFrameTabDialog(const OUString &rDialogType, + SfxViewFrame *pFrame, weld::Window *pParent, + const SfxItemSet& rCoreSet, + bool bNewFrame, + const OString& sDefPage ) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwFrameDlg>(pFrame, pParent, rCoreSet, bNewFrame, rDialogType, false/*bFormat*/, sDefPage, nullptr)); +} + +VclPtr<SfxAbstractApplyTabDialog> SwAbstractDialogFactory_Impl::CreateTemplateDialog( + weld::Window *pParent, + SfxStyleSheetBase& rBase, + SfxStyleFamily nRegion, + const OString& sPage, + SwWrtShell* pActShell, + bool bNew ) +{ + return VclPtr<AbstractApplyTabController_Impl>::Create(std::make_shared<SwTemplateDlgController>(pParent, rBase, nRegion, + sPage, pActShell, bNew)); +} + +VclPtr<AbstractGlossaryDlg> SwAbstractDialogFactory_Impl::CreateGlossaryDlg(SfxViewFrame* pViewFrame, SwGlossaryHdl* pGlosHdl, + SwWrtShell *pWrtShell) +{ + return VclPtr<AbstractGlossaryDlg_Impl>::Create(std::make_unique<SwGlossaryDlg>(pViewFrame, pGlosHdl, pWrtShell)); +} + +VclPtr<AbstractFieldInputDlg> SwAbstractDialogFactory_Impl::CreateFieldInputDlg(weld::Widget *pParent, + SwWrtShell &rSh, SwField* pField, bool bPrevButton, bool bNextButton) +{ + return VclPtr<AbstractFieldInputDlg_Impl>::Create(std::make_unique<SwFieldInputDlg>(pParent, rSh, pField, bPrevButton, bNextButton)); +} + +VclPtr<AbstractInsFootNoteDlg> SwAbstractDialogFactory_Impl::CreateInsFootNoteDlg( + weld::Window * pParent, SwWrtShell &rSh, bool bEd ) +{ + return VclPtr<AbstractInsFootNoteDlg_Impl>::Create(std::make_unique<SwInsFootNoteDlg>(pParent, rSh, bEd)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateTitlePageDlg(weld::Window *pParent) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwTitlePageDlg>(pParent)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateVclSwViewDialog(SwView& rView) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwLineNumberingDlg>(rView)); +} + +std::shared_ptr<AbstractInsTableDlg> SwAbstractDialogFactory_Impl::CreateInsTableDlg(SwView& rView) +{ + return std::make_shared<AbstractInsTableDlg_Impl>(std::make_shared<SwInsTableDlg>(rView)); +} + +VclPtr<AbstractJavaEditDialog> SwAbstractDialogFactory_Impl::CreateJavaEditDialog( + weld::Window* pParent, SwWrtShell* pWrtSh) +{ + return VclPtr<AbstractJavaEditDialog_Impl>::Create(std::make_unique<SwJavaEditDialog>(pParent, pWrtSh)); +} + +VclPtr<AbstractMailMergeDlg> SwAbstractDialogFactory_Impl::CreateMailMergeDlg( + weld::Window* pParent, SwWrtShell& rSh, + const OUString& rSourceName, + const OUString& rTableName, + sal_Int32 nCommandType, + const uno::Reference< sdbc::XConnection>& xConnection ) +{ + return VclPtr<AbstractMailMergeDlg_Impl>::Create(std::make_unique<SwMailMergeDlg>(pParent, rSh, rSourceName, rTableName, nCommandType, xConnection, nullptr)); +} + +VclPtr<AbstractMailMergeCreateFromDlg> SwAbstractDialogFactory_Impl::CreateMailMergeCreateFromDlg(weld::Window* pParent) +{ + return VclPtr<AbstractMailMergeCreateFromDlg_Impl>::Create(std::make_unique<SwMailMergeCreateFromDlg>(pParent)); +} + +VclPtr<AbstractMailMergeFieldConnectionsDlg> SwAbstractDialogFactory_Impl::CreateMailMergeFieldConnectionsDlg(weld::Window* pParent) +{ + return VclPtr<AbstractMailMergeFieldConnectionsDlg_Impl>::Create(std::make_unique<SwMailMergeFieldConnectionsDlg>(pParent)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateMultiTOXMarkDlg(weld::Window* pParent, SwTOXMgr &rTOXMgr) +{ + return VclPtr<AbstractMultiTOXMarkDlg_Impl>::Create(std::make_unique<SwMultiTOXMarkDlg>(pParent, rTOXMgr)); +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateSvxNumBulletTabDialog(weld::Window* pParent, + const SfxItemSet* pSwItemSet, + SwWrtShell & rWrtSh) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwSvxNumBulletTabDialog>(pParent, pSwItemSet, rWrtSh)); +} + +VclPtr<SfxAbstractTabDialog> SwAbstractDialogFactory_Impl::CreateOutlineTabDialog(weld::Window* pParent, + const SfxItemSet* pSwItemSet, + SwWrtShell & rWrtSh ) +{ + return VclPtr<AbstractTabController_Impl>::Create(std::make_shared<SwOutlineTabDialog>(pParent, pSwItemSet, rWrtSh)); +} + +VclPtr<AbstractMultiTOXTabDialog> SwAbstractDialogFactory_Impl::CreateMultiTOXTabDialog(weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell &rShell, SwTOXBase* pCurTOX, bool bGlobal) +{ + return VclPtr<AbstractMultiTOXTabDialog_Impl>::Create(std::make_shared<SwMultiTOXTabDialog>(pParent, rSet, rShell, pCurTOX, USHRT_MAX, bGlobal)); +} + +VclPtr<AbstractEditRegionDlg> SwAbstractDialogFactory_Impl::CreateEditRegionDlg(weld::Window* pParent, SwWrtShell& rWrtSh) +{ + return VclPtr<AbstractEditRegionDlg_Impl>::Create(std::make_shared<SwEditRegionDlg>(pParent, rWrtSh)); +} + +VclPtr<AbstractInsertSectionTabDialog> SwAbstractDialogFactory_Impl::CreateInsertSectionTabDialog(weld::Window* pParent, + const SfxItemSet& rSet, SwWrtShell& rSh) +{ + return VclPtr<AbstractInsertSectionTabDialog_Impl>::Create(std::make_shared<SwInsertSectionTabDialog>(pParent, rSet, rSh)); +} + +VclPtr<AbstractMarkFloatDlg> SwAbstractDialogFactory_Impl::CreateIndexMarkFloatDlg( + SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo* pInfo ) +{ + return VclPtr<AbstractIndexMarkFloatDlg_Impl>::Create(std::make_shared<SwIndexMarkFloatDlg>(pBindings, pChild, pParent, pInfo, true/*bNew*/)); +} + +VclPtr<AbstractMarkFloatDlg> SwAbstractDialogFactory_Impl::CreateAuthMarkFloatDlg( + SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo* pInfo) +{ + return VclPtr<AbstractAuthMarkFloatDlg_Impl>::Create(std::make_shared<SwAuthMarkFloatDlg>(pBindings, pChild, pParent, pInfo, true/*bNew*/)); +} + +VclPtr<AbstractSwWordCountFloatDlg> SwAbstractDialogFactory_Impl::CreateSwWordCountDialog( + SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo* pInfo) +{ + return VclPtr<AbstractSwWordCountFloatDlg_Impl>::Create(std::make_shared<SwWordCountFloatDlg>(pBindings, pChild, pParent, pInfo)); +} + +VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateIndexMarkModalDlg(weld::Window *pParent, SwWrtShell& rSh, SwTOXMark* pCurTOXMark ) +{ + return VclPtr<AbstractGenericDialog_Impl>::Create(std::make_shared<SwIndexMarkModalDlg>(pParent, rSh, pCurTOXMark)); +} + +VclPtr<AbstractMailMergeWizard> SwAbstractDialogFactory_Impl::CreateMailMergeWizard( + SwView& rView, std::shared_ptr<SwMailMergeConfigItem>& rConfigItem) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + return VclPtr<AbstractMailMergeWizard_Impl>::Create(std::make_shared<SwMailMergeWizard>(rView, rConfigItem)); +#else + (void) rView; + (void) rConfigItem; + return nullptr; +#endif +} + +GlossaryGetCurrGroup SwAbstractDialogFactory_Impl::GetGlossaryCurrGroupFunc() +{ + return SwGlossaryDlg::GetCurrGroup; +} + +GlossarySetActGroup SwAbstractDialogFactory_Impl::SetGlossaryActGroupFunc() +{ + return SwGlossaryDlg::SetActGroup; +} + +// Factories for TabPages +CreateTabPage SwAbstractDialogFactory_Impl::GetTabPageCreatorFunc( sal_uInt16 nId ) +{ + CreateTabPage pRet = nullptr; + switch ( nId ) + { + case RID_SW_TP_OPTCOMPATIBILITY_PAGE : + pRet = SwCompatibilityOptPage::Create; + break; + case RID_SW_TP_OPTLOAD_PAGE : + pRet = SwLoadOptPage::Create; + break; + case RID_SW_TP_OPTCAPTION_PAGE: + return SwCaptionOptPage::Create; + case RID_SW_TP_CONTENT_OPT: + case RID_SW_TP_HTML_CONTENT_OPT: + pRet = SwContentOptPage::Create; + break; + case RID_SW_TP_OPTSHDWCRSR: + case RID_SW_TP_HTML_OPTSHDWCRSR: + pRet = SwShdwCursorOptionsTabPage::Create; + break; + case RID_SW_TP_REDLINE_OPT : + pRet = SwRedlineOptionsTabPage::Create; + break; + case RID_SW_TP_OPTTEST_PAGE : +#ifdef DBG_UTIL + pRet = SwTestTabPage::Create; +#endif + break; + case TP_OPTPRINT_PAGE : + case RID_SW_TP_HTML_OPTPRINT_PAGE: + case RID_SW_TP_OPTPRINT_PAGE: + pRet = SwAddPrinterTabPage::Create; + break; + case RID_SW_TP_STD_FONT: + case RID_SW_TP_STD_FONT_CJK: + case RID_SW_TP_STD_FONT_CTL: + pRet = SwStdFontTabPage::Create; + break; + case RID_SW_TP_HTML_OPTTABLE_PAGE: + case RID_SW_TP_OPTTABLE_PAGE: + pRet = SwTableOptionsTabPage::Create; + break; + case RID_SW_TP_DOC_STAT : + pRet = SwDocStatPage::Create; + break; + case RID_SW_TP_MAILCONFIG: + pRet = SwMailConfigPage::Create; + break; + case RID_SW_TP_COMPARISON_OPT : + pRet = SwCompareOptionsTabPage::Create; + break; + } + + return pRet; +} + +void SwAbstractDialogFactory_Impl::ExecuteMMResultSaveDialog(weld::Window* pParent) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SwMMResultSaveDialog aDialog(pParent); + aDialog.run(); +#else + (void) pParent; +#endif +} + +void SwAbstractDialogFactory_Impl::ExecuteMMResultPrintDialog(weld::Window* pParent) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SwMMResultPrintDialog aDialog(pParent); + aDialog.run(); +#else + (void) pParent; +#endif +} + +void SwAbstractDialogFactory_Impl::ExecuteMMResultEmailDialog(weld::Window* pParent) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SwMMResultEmailDialog aDialog(pParent); + aDialog.run(); +#else + (void) pParent; +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/swdlgfact.hxx b/sw/source/ui/dialog/swdlgfact.hxx new file mode 100644 index 000000000..0f1a7c94f --- /dev/null +++ b/sw/source/ui/dialog/swdlgfact.hxx @@ -0,0 +1,787 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_DIALOG_SWDLGFACT_HXX +#define INCLUDED_SW_SOURCE_UI_DIALOG_SWDLGFACT_HXX + +#include <swabstdlg.hxx> + +#include <abstract.hxx> +#include <ascfldlg.hxx> +#include <break.hxx> +#include <cnttab.hxx> +#include <colwd.hxx> +#include <convert.hxx> +#include <DateFormFieldDialog.hxx> +#include <dbinsdlg.hxx> +#include <DropDownFieldDialog.hxx> +#include <DropDownFormFieldDialog.hxx> +#include <fldtdlg.hxx> +#include <glossary.hxx> +#include <inpdlg.hxx> +#include <insfnote.hxx> +#include <instable.hxx> +#include <javaedit.hxx> +#include <label.hxx> +#include <mailmergewizard.hxx> +#include <mailmrge.hxx> +#include <mergetbl.hxx> +#include <multmrk.hxx> +#include <regionsw.hxx> +#include <rowht.hxx> +#include <selglos.hxx> +#include <splittbl.hxx> +#include <srtdlg.hxx> +#include <swmodalredlineacceptdlg.hxx> +#include <swrenamexnameddlg.hxx> +#include <swuicnttab.hxx> +#include <swuiidxmrk.hxx> +#include <tautofmt.hxx> +#include <wordcountdialog.hxx> + +class SwInsertAbstractDlg; +class SwAsciiFilterDlg; +class SwBreakDlg; +class SwMultiTOXMarkDlg; +class SwSortDlg; +class SwTableHeightDlg; +class SwTableWidthDlg; +class SwMergeTableDlg; +class SfxTabDialog; +class SwConvertTableDlg; +class SwInsertDBColAutoPilot; +class SwLabDlg; +class SwSelGlossaryDlg; +class SwAutoFormatDlg; +class SwFieldDlg; +class SwRenameXNamedDlg; +class SwModalRedlineAcceptDlg; +class SwTOXMark; +class SwSplitTableDlg; + +#include <itabenum.hxx> +#include <optional> +#include <o3tl/deleter.hxx> + +namespace sw +{ +class DropDownFieldDialog; +class DropDownFormFieldDialog; +class DateFormFieldDialog; +} + +class SwWordCountFloatDlg; +class AbstractSwWordCountFloatDlg_Impl : public AbstractSwWordCountFloatDlg +{ + std::shared_ptr<SwWordCountFloatDlg> m_xDlg; +public: + explicit AbstractSwWordCountFloatDlg_Impl(std::shared_ptr<SwWordCountFloatDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void UpdateCounts() override; + virtual void SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat) override; + virtual std::shared_ptr<SfxDialogController> GetController() override; +}; + +class AbstractSwInsertAbstractDlg_Impl : public AbstractSwInsertAbstractDlg +{ + std::unique_ptr<SwInsertAbstractDlg> m_xDlg; +public: + explicit AbstractSwInsertAbstractDlg_Impl(std::unique_ptr<SwInsertAbstractDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual sal_uInt8 GetLevel() const override ; + virtual sal_uInt8 GetPara() const override ; +}; + +class SwAbstractSfxController_Impl : public SfxAbstractDialog +{ + std::unique_ptr<SfxSingleTabDialogController> m_xDlg; +public: + explicit SwAbstractSfxController_Impl(std::unique_ptr<SfxSingleTabDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual void SetText(const OUString& rStr) override; +}; + +class AbstractSwAsciiFilterDlg_Impl : public AbstractSwAsciiFilterDlg +{ + std::unique_ptr<SwAsciiFilterDlg> m_xDlg; +public: + explicit AbstractSwAsciiFilterDlg_Impl(std::unique_ptr<SwAsciiFilterDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void FillOptions( SwAsciiOptions& rOptions ) override; +}; + +class AbstractGenericDialog_Impl : public VclAbstractDialog +{ + std::shared_ptr<weld::GenericDialogController> m_xDlg; +public: + explicit AbstractGenericDialog_Impl(std::shared_ptr<weld::GenericDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; +}; + +class AbstractSwSortDlg_Impl : public VclAbstractDialog +{ + std::unique_ptr<SwSortDlg> m_xDlg; +public: + explicit AbstractSwSortDlg_Impl(std::unique_ptr<SwSortDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractMultiTOXMarkDlg_Impl : public VclAbstractDialog +{ + std::unique_ptr<SwMultiTOXMarkDlg> m_xDlg; +public: + explicit AbstractMultiTOXMarkDlg_Impl(std::unique_ptr<SwMultiTOXMarkDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractSwBreakDlg_Impl : public AbstractSwBreakDlg +{ + std::unique_ptr<SwBreakDlg> m_xDlg; +public: + explicit AbstractSwBreakDlg_Impl(std::unique_ptr<SwBreakDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetTemplateName() override; + virtual sal_uInt16 GetKind() override; + virtual ::std::optional<sal_uInt16> GetPageNumber() override; +}; + +class AbstractSwTableWidthDlg_Impl : public VclAbstractDialog +{ + std::unique_ptr<SwTableWidthDlg> m_xDlg; +public: + explicit AbstractSwTableWidthDlg_Impl(std::unique_ptr<SwTableWidthDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractSwTableHeightDlg_Impl : public VclAbstractDialog +{ + std::unique_ptr<SwTableHeightDlg> m_xDlg; +public: + explicit AbstractSwTableHeightDlg_Impl(std::unique_ptr<SwTableHeightDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractSwMergeTableDlg_Impl : public VclAbstractDialog +{ + std::unique_ptr<SwMergeTableDlg> m_xDlg; +public: + explicit AbstractSwMergeTableDlg_Impl(std::unique_ptr<SwMergeTableDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractSplitTableDialog_Impl : public AbstractSplitTableDialog // add for +{ + std::unique_ptr<SwSplitTableDlg> m_xDlg; +public: + explicit AbstractSplitTableDialog_Impl(std::unique_ptr<SwSplitTableDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual SplitTable_HeadlineOption GetSplitMode() override; +}; + +class AbstractTabController_Impl : virtual public SfxAbstractTabDialog +{ +protected: + std::shared_ptr<SfxTabDialogController> m_xDlg; +public: + explicit AbstractTabController_Impl(std::shared_ptr<SfxTabDialogController> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual const sal_uInt16* GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + //From class Window. + virtual void SetText( const OUString& rStr ) override; +}; + +class AbstractApplyTabController_Impl : public AbstractTabController_Impl, virtual public SfxAbstractApplyTabDialog +{ +public: + explicit AbstractApplyTabController_Impl(std::shared_ptr<SfxTabDialogController> p) + : AbstractTabController_Impl(std::move(p)) + { + } + DECL_LINK(ApplyHdl, weld::Button&, void); +private: + Link<LinkParamNone*,void> m_aHandler; + virtual void SetApplyHdl( const Link<LinkParamNone*,void>& rLink ) override; +}; + +class AbstractSwConvertTableDlg_Impl : public AbstractSwConvertTableDlg +{ + std::unique_ptr<SwConvertTableDlg> m_xDlg; +public: + explicit AbstractSwConvertTableDlg_Impl(std::unique_ptr<SwConvertTableDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void GetValues( sal_Unicode& rDelim,SwInsertTableOptions& rInsTableFlags, + SwTableAutoFormat const*& prTAFormat) override; +}; + +class AbstractSwInsertDBColAutoPilot_Impl : public AbstractSwInsertDBColAutoPilot +{ + std::unique_ptr<SwInsertDBColAutoPilot> m_xDlg; +public: + explicit AbstractSwInsertDBColAutoPilot_Impl(std::unique_ptr<SwInsertDBColAutoPilot> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void DataToDoc( const css::uno::Sequence< css::uno::Any >& rSelection, + css::uno::Reference< css::sdbc::XDataSource> rxSource, + css::uno::Reference< css::sdbc::XConnection> xConnection, + css::uno::Reference< css::sdbc::XResultSet > xResultSet) override; +}; + +class AbstractDropDownFieldDialog_Impl : public AbstractDropDownFieldDialog +{ + std::unique_ptr<sw::DropDownFieldDialog> m_xDlg; +public: + explicit AbstractDropDownFieldDialog_Impl(std::unique_ptr<sw::DropDownFieldDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool PrevButtonPressed() const override; + virtual bool NextButtonPressed() const override; +}; + +class AbstractDropDownFormFieldDialog_Impl : public VclAbstractDialog +{ + std::unique_ptr<sw::DropDownFormFieldDialog> m_xDlg; +public: + explicit AbstractDropDownFormFieldDialog_Impl(std::unique_ptr<sw::DropDownFormFieldDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractDateFormFieldDialog_Impl : public VclAbstractDialog +{ + std::unique_ptr<sw::DateFormFieldDialog> m_xDlg; +public: + explicit AbstractDateFormFieldDialog_Impl(std::unique_ptr<sw::DateFormFieldDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractSwLabDlg_Impl : public AbstractSwLabDlg +{ + std::unique_ptr<SwLabDlg> m_xDlg; +public: + explicit AbstractSwLabDlg_Impl(std::unique_ptr<SwLabDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual const sal_uInt16* GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + //From class Window. + virtual void SetText( const OUString& rStr ) override; + virtual const OUString& GetBusinessCardStr() const override; + virtual Printer *GetPrt() override; +}; + +class AbstractSwSelGlossaryDlg_Impl : public AbstractSwSelGlossaryDlg +{ + std::unique_ptr<SwSelGlossaryDlg> m_xDlg; +public: + explicit AbstractSwSelGlossaryDlg_Impl(std::unique_ptr<SwSelGlossaryDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void InsertGlos(const OUString &rRegion, const OUString &rGlosName) override; // inline + virtual sal_Int32 GetSelectedIdx() const override; // inline + virtual void SelectEntryPos(sal_Int32 nIdx) override; // inline +}; + +class AbstractSwAutoFormatDlg_Impl : public AbstractSwAutoFormatDlg +{ + std::unique_ptr<SwAutoFormatDlg, o3tl::default_delete<SwAutoFormatDlg>> m_xDlg; +public: + explicit AbstractSwAutoFormatDlg_Impl(std::unique_ptr<SwAutoFormatDlg, o3tl::default_delete<SwAutoFormatDlg>> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual std::unique_ptr<SwTableAutoFormat> FillAutoFormatOfIndex() const override; +}; + +class AbstractSwFieldDlg_Impl : public AbstractSwFieldDlg +{ + std::shared_ptr<SwFieldDlg> m_xDlg; +public: + explicit AbstractSwFieldDlg_Impl(std::shared_ptr<SwFieldDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual const sal_uInt16* GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + //From class Window. + virtual void SetText( const OUString& rStr ) override; + virtual void ShowReferencePage() override; + virtual void Initialize(SfxChildWinInfo *pInfo) override; + virtual void ReInitDlg() override; + virtual void ActivateDatabasePage() override; + virtual std::shared_ptr<SfxDialogController> GetController() override; +}; + +class AbstractSwRenameXNamedDlg_Impl : public AbstractSwRenameXNamedDlg +{ + std::unique_ptr<SwRenameXNamedDlg> m_xDlg; +public: + explicit AbstractSwRenameXNamedDlg_Impl(std::unique_ptr<SwRenameXNamedDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void SetForbiddenChars( const OUString& rSet ) override; + virtual void SetAlternativeAccess( + css::uno::Reference< css::container::XNameAccess > & xSecond, + css::uno::Reference< css::container::XNameAccess > & xThird ) override; +}; + +class AbstractSwModalRedlineAcceptDlg_Impl : public AbstractSwModalRedlineAcceptDlg +{ + std::unique_ptr<SwModalRedlineAcceptDlg> m_xDlg; +public: + explicit AbstractSwModalRedlineAcceptDlg_Impl(std::unique_ptr<SwModalRedlineAcceptDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void AcceptAll(bool bAccept) override; +}; + +class SwGlossaryDlg; +class AbstractGlossaryDlg_Impl : public AbstractGlossaryDlg +{ + std::unique_ptr<SwGlossaryDlg> m_xDlg; +public: + explicit AbstractGlossaryDlg_Impl(std::unique_ptr<SwGlossaryDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetCurrGrpName() const override; + virtual OUString GetCurrShortName() const override; +}; + +class SwFieldInputDlg; +class AbstractFieldInputDlg_Impl : public AbstractFieldInputDlg +{ + std::unique_ptr<SwFieldInputDlg> m_xDlg; +public: + explicit AbstractFieldInputDlg_Impl(std::unique_ptr<SwFieldInputDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void EndDialog(sal_Int32) override; + virtual bool PrevButtonPressed() const override; + virtual bool NextButtonPressed() const override; +}; + +class SwInsFootNoteDlg; +class AbstractInsFootNoteDlg_Impl : public AbstractInsFootNoteDlg +{ + std::unique_ptr<SwInsFootNoteDlg> m_xDlg; +public: + explicit AbstractInsFootNoteDlg_Impl(std::unique_ptr<SwInsFootNoteDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetFontName() override; + virtual bool IsEndNote() override; + virtual OUString GetStr() override; + //from class Window + virtual void SetHelpId( const OString& sHelpId ) override; + virtual void SetText( const OUString& rStr ) override; +}; + +class SwInsTableDlg; +class AbstractInsTableDlg_Impl : public AbstractInsTableDlg +{ +protected: + std::shared_ptr<weld::DialogController> m_xDlg; +public: + explicit AbstractInsTableDlg_Impl(std::shared_ptr<weld::DialogController> p) + : m_xDlg(p) + { + } + virtual void GetValues( OUString& rName, sal_uInt16& rRow, sal_uInt16& rCol, + SwInsertTableOptions& rInsTableFlags, OUString& rTableAutoFormatName, + std::unique_ptr<SwTableAutoFormat>& prTAFormat ) override; + virtual std::shared_ptr<weld::DialogController> getDialogController() override { return m_xDlg; } +}; + +class SwJavaEditDialog; +class AbstractJavaEditDialog_Impl : public AbstractJavaEditDialog +{ + std::unique_ptr<SwJavaEditDialog> m_xDlg; +public: + explicit AbstractJavaEditDialog_Impl(std::unique_ptr<SwJavaEditDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetScriptText() const override; + virtual OUString GetScriptType() const override; + virtual bool IsUrl() const override; + virtual bool IsNew() const override; + virtual bool IsUpdate() const override; +}; + +class SwMailMergeDlg; +class AbstractMailMergeDlg_Impl : public AbstractMailMergeDlg +{ + std::unique_ptr<SwMailMergeDlg> m_xDlg; +public: + explicit AbstractMailMergeDlg_Impl(std::unique_ptr<SwMailMergeDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual DBManagerOptions GetMergeType() override ; + virtual const OUString& GetSaveFilter() const override; + virtual css::uno::Sequence< css::uno::Any > GetSelection() const override ; + virtual css::uno::Reference< css::sdbc::XResultSet> GetResultSet() const override; + virtual bool IsSaveSingleDoc() const override; + virtual bool IsGenerateFromDataBase() const override; + virtual bool IsFileEncryptedFromDataBase() const override; + virtual OUString GetColumnName() const override; + virtual OUString GetPasswordColumnName() const override; + virtual OUString GetTargetURL() const override; +}; + +class SwMailMergeCreateFromDlg; +class AbstractMailMergeCreateFromDlg_Impl : public AbstractMailMergeCreateFromDlg +{ + std::unique_ptr<SwMailMergeCreateFromDlg> m_xDlg; +public: + explicit AbstractMailMergeCreateFromDlg_Impl(std::unique_ptr<SwMailMergeCreateFromDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool IsThisDocument() const override ; +}; + +class SwMailMergeFieldConnectionsDlg; +class AbstractMailMergeFieldConnectionsDlg_Impl : public AbstractMailMergeFieldConnectionsDlg +{ + std::unique_ptr<SwMailMergeFieldConnectionsDlg> m_xDlg; +public: + explicit AbstractMailMergeFieldConnectionsDlg_Impl(std::unique_ptr<SwMailMergeFieldConnectionsDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool IsUseExistingConnections() const override ; +}; + +class SwMultiTOXTabDialog; +class AbstractMultiTOXTabDialog_Impl : public AbstractMultiTOXTabDialog +{ +protected: + std::shared_ptr<SwMultiTOXTabDialog> m_xDlg; +public: + explicit AbstractMultiTOXTabDialog_Impl(std::shared_ptr<SwMultiTOXTabDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override; + virtual CurTOXType GetCurrentTOXType() const override ; + virtual SwTOXDescription& GetTOXDescription(CurTOXType eTOXTypes) override; + //from SfxTabDialog + virtual const SfxItemSet* GetOutputItemSet() const override; +}; + +class SwEditRegionDlg; +class AbstractEditRegionDlg_Impl : public AbstractEditRegionDlg +{ + std::shared_ptr<SwEditRegionDlg> m_xDlg; +public: + explicit AbstractEditRegionDlg_Impl(std::shared_ptr<SwEditRegionDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void SelectSection(const OUString& rSectionName) override; +}; + +class SwInsertSectionTabDialog; +class AbstractInsertSectionTabDialog_Impl : public AbstractInsertSectionTabDialog +{ + std::shared_ptr<SwInsertSectionTabDialog> m_xDlg; +public: + explicit AbstractInsertSectionTabDialog_Impl(std::shared_ptr<SwInsertSectionTabDialog> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetSectionData(SwSectionData const& rSect) override; +}; + +class SwIndexMarkFloatDlg; +class AbstractIndexMarkFloatDlg_Impl : public AbstractMarkFloatDlg +{ + std::shared_ptr<SwIndexMarkFloatDlg> m_xDlg; +public: + explicit AbstractIndexMarkFloatDlg_Impl(std::shared_ptr<SwIndexMarkFloatDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void ReInitDlg(SwWrtShell& rWrtShell) override; + virtual std::shared_ptr<SfxDialogController> GetController() override; +}; + +class SwAuthMarkFloatDlg; +class AbstractAuthMarkFloatDlg_Impl : public AbstractMarkFloatDlg +{ + std::shared_ptr<SwAuthMarkFloatDlg> m_xDlg; +public: + explicit AbstractAuthMarkFloatDlg_Impl(std::shared_ptr<SwAuthMarkFloatDlg> p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void ReInitDlg(SwWrtShell& rWrtShell) override; + virtual std::shared_ptr<SfxDialogController> GetController() override; +}; + +class SwMailMergeWizard; +class AbstractMailMergeWizard_Impl : public AbstractMailMergeWizard +{ + std::shared_ptr<SwMailMergeWizard> m_xDlg; + +public: + explicit AbstractMailMergeWizard_Impl(std::shared_ptr<SwMailMergeWizard> p) + : m_xDlg(std::move(p)) + { + } + virtual ~AbstractMailMergeWizard_Impl() override; + virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override; + virtual short Execute() override; + + virtual OUString GetReloadDocument() const override; + virtual void ShowPage( sal_uInt16 nLevel ) override; + virtual sal_uInt16 GetRestartPage() const override; +}; + +//AbstractDialogFactory_Impl implementations +class SwAbstractDialogFactory_Impl : public SwAbstractDialogFactory +{ + +public: + virtual ~SwAbstractDialogFactory_Impl() {} + + virtual VclPtr<SfxAbstractDialog> CreateNumFormatDialog(weld::Widget* pParent, const SfxItemSet& rAttr) override; + virtual VclPtr<SfxAbstractDialog> CreateSwDropCapsDialog(weld::Window* pParent, const SfxItemSet& rSet) override; + virtual VclPtr<SfxAbstractDialog> CreateSwBackgroundDialog(weld::Window* pParent, const SfxItemSet& rSet) override; + virtual VclPtr<AbstractSwWordCountFloatDlg> CreateSwWordCountDialog(SfxBindings* pBindings, + SfxChildWindow* pChild, weld::Window *pParent, SfxChildWinInfo* pInfo) override; + virtual VclPtr<AbstractSwInsertAbstractDlg> CreateSwInsertAbstractDlg(weld::Window* pParent) override; + virtual VclPtr<SfxAbstractDialog> CreateSwAddressAbstractDlg(weld::Window* pParent, const SfxItemSet& rSet) override; + virtual VclPtr<AbstractSwAsciiFilterDlg> CreateSwAsciiFilterDlg(weld::Window* pParent, SwDocShell& rDocSh, + SvStream* pStream) override; + virtual VclPtr<VclAbstractDialog> CreateSwInsertBookmarkDlg(weld::Window *pParent, SwWrtShell &rSh, SfxRequest& rReq) override; + virtual VclPtr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window *pParent, SwWrtShell &rSh) override; + virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSwCharDlg(weld::Window* pParent, SwView& pVw, const SfxItemSet& rCoreSet, + SwCharDlgMode nDialogMode, const OUString* pFormatStr = nullptr) override; + virtual VclPtr<AbstractSwConvertTableDlg> CreateSwConvertTableDlg(SwView& rView, bool bToTable) override; + virtual VclPtr<VclAbstractDialog> CreateSwCaptionDialog(weld::Window *pParent, SwView &rV) override; + virtual VclPtr<AbstractSwInsertDBColAutoPilot> CreateSwInsertDBColAutoPilot(SwView& rView, + css::uno::Reference< css::sdbc::XDataSource> rxSource, + css::uno::Reference<css::sdbcx::XColumnsSupplier> xColSupp, + const SwDBData& rData) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSwFootNoteOptionDlg(weld::Window *pParent, SwWrtShell &rSh) override; + + virtual VclPtr<AbstractDropDownFieldDialog> CreateDropDownFieldDialog(weld::Widget* pParent, SwWrtShell &rSh, + SwField* pField, bool bPrevButton, bool bNextButton) override; + virtual VclPtr<VclAbstractDialog> CreateDropDownFormFieldDialog(weld::Widget* pParent, sw::mark::IFieldmark* pDropDownField) override; + virtual VclPtr<VclAbstractDialog> CreateDateFormFieldDialog(weld::Widget* pParent, sw::mark::IDateFieldmark* pDateField, SwDoc* pDoc) override; + + virtual VclPtr<SfxAbstractTabDialog> CreateSwEnvDlg(weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell* pWrtSh, Printer* pPrt, bool bInsert) override; + virtual VclPtr<AbstractSwLabDlg> CreateSwLabDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwDBManager* pDBManager, bool bLabel) override; + + virtual SwLabDlgMethod GetSwLabDlgStaticMethod () override; + virtual VclPtr<SfxAbstractTabDialog> CreateSwParaDlg(weld::Window *pParent, + SwView& rVw, + const SfxItemSet& rCoreSet, + bool bDraw, + const OString& sDefPage = OString()) override; + + virtual VclPtr<VclAbstractDialog> CreateSwAutoMarkDialog(weld::Window *pParent, SwWrtShell &rSh) override; + virtual VclPtr<AbstractSwSelGlossaryDlg> CreateSwSelGlossaryDlg(weld::Window *pParent, const OUString &rShortName) override; + virtual VclPtr<VclAbstractDialog> CreateSwSortingDialog(weld::Window *pParent, SwWrtShell &rSh) override; + virtual VclPtr<VclAbstractDialog> CreateSwTableHeightDialog(weld::Window *pParent, SwWrtShell &rSh) override; + virtual VclPtr<VclAbstractDialog> CreateSwColumnDialog(weld::Window *pParent, SwWrtShell &rSh) override; + virtual VclPtr<AbstractSplitTableDialog> CreateSplitTableDialog(weld::Window* pParent, SwWrtShell &rSh) override; + + virtual VclPtr<AbstractSwAutoFormatDlg> CreateSwAutoFormatDlg(weld::Window* pParent, SwWrtShell* pShell, + bool bSetAutoFormat = true, + const SwTableAutoFormat* pSelFormat = nullptr) override; + virtual VclPtr<SfxAbstractDialog> CreateSwBorderDlg(weld::Window* pParent, SfxItemSet& rSet, SwBorderModes nType) override; + + virtual VclPtr<SfxAbstractDialog> CreateSwWrapDlg(weld::Window* pParent, SfxItemSet& rSet, SwWrtShell* pSh) override; + virtual VclPtr<VclAbstractDialog> CreateSwTableWidthDlg(weld::Window *pParent, SwTableFUNC &rFnc) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSwTableTabDlg(weld::Window* pParent, + const SfxItemSet* pItemSet, SwWrtShell* pSh) override; + virtual VclPtr<AbstractSwFieldDlg> CreateSwFieldDlg(SfxBindings* pB, SwChildWinWrapper* pCW, weld::Window *pParent) override; + virtual VclPtr<SfxAbstractDialog> CreateSwFieldEditDlg ( SwView& rVw ) override; + virtual VclPtr<AbstractSwRenameXNamedDlg> CreateSwRenameXNamedDlg(weld::Window* pParent, + css::uno::Reference< css::container::XNamed > & xNamed, + css::uno::Reference< css::container::XNameAccess > & xNameAccess) override; + virtual VclPtr<AbstractSwModalRedlineAcceptDlg> CreateSwModalRedlineAcceptDlg(weld::Window *pParent) override; + + virtual VclPtr<VclAbstractDialog> CreateTableMergeDialog(weld::Window* pParent, bool& rWithPrev) override; + virtual VclPtr<SfxAbstractTabDialog> CreateFrameTabDialog( const OUString &rDialogType, + SfxViewFrame *pFrame, weld::Window *pParent, + const SfxItemSet& rCoreSet, + bool bNewFrame = true, + const OString& sDefPage = OString()) override; + virtual VclPtr<SfxAbstractApplyTabDialog> CreateTemplateDialog( + weld::Window *pParent, + SfxStyleSheetBase& rBase, + SfxStyleFamily nRegion, + const OString& sPage, + SwWrtShell* pActShell, + bool bNew) override; + virtual VclPtr<AbstractGlossaryDlg> CreateGlossaryDlg(SfxViewFrame* pViewFrame, + SwGlossaryHdl* pGlosHdl, + SwWrtShell *pWrtShell) override; + virtual VclPtr<AbstractFieldInputDlg> CreateFieldInputDlg(weld::Widget *pParent, + SwWrtShell &rSh, SwField* pField, bool bPrevButton, bool bNextButton) override; + virtual VclPtr<AbstractInsFootNoteDlg> CreateInsFootNoteDlg( + weld::Window * pParent, SwWrtShell &rSh, bool bEd = false) override; + virtual VclPtr<VclAbstractDialog> CreateTitlePageDlg(weld::Window* pParent) override; + virtual VclPtr<VclAbstractDialog> CreateVclSwViewDialog(SwView& rView) override; + virtual std::shared_ptr<AbstractInsTableDlg> CreateInsTableDlg(SwView& rView) override; + virtual VclPtr<AbstractJavaEditDialog> CreateJavaEditDialog(weld::Window* pParent, + SwWrtShell* pWrtSh) override; + virtual VclPtr<AbstractMailMergeDlg> CreateMailMergeDlg( + weld::Window* pParent, SwWrtShell& rSh, + const OUString& rSourceName, + const OUString& rTableName, + sal_Int32 nCommandType, + const css::uno::Reference< css::sdbc::XConnection>& xConnection ) override; + virtual VclPtr<AbstractMailMergeCreateFromDlg> CreateMailMergeCreateFromDlg(weld::Window* pParent) override; + virtual VclPtr<AbstractMailMergeFieldConnectionsDlg> CreateMailMergeFieldConnectionsDlg(weld::Window* pParent) override; + virtual VclPtr<VclAbstractDialog> CreateMultiTOXMarkDlg(weld::Window* pParent, SwTOXMgr &rTOXMgr) override; + virtual VclPtr<SfxAbstractTabDialog> CreateOutlineTabDialog(weld::Window* pParent, const SfxItemSet* pSwItemSet, + SwWrtShell &) override; + virtual VclPtr<SfxAbstractTabDialog> CreateSvxNumBulletTabDialog(weld::Window* pParent, + const SfxItemSet* pSwItemSet, + SwWrtShell &) override; + virtual VclPtr<AbstractMultiTOXTabDialog> CreateMultiTOXTabDialog( + weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell &rShell, + SwTOXBase* pCurTOX, + bool bGlobal) override; + virtual VclPtr<AbstractEditRegionDlg> CreateEditRegionDlg(weld::Window* pParent, SwWrtShell& rWrtSh) override; + virtual VclPtr<AbstractInsertSectionTabDialog> CreateInsertSectionTabDialog( + weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell& rSh) override; + virtual VclPtr<AbstractMarkFloatDlg> CreateIndexMarkFloatDlg( + SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo* pInfo) override; + virtual VclPtr<AbstractMarkFloatDlg> CreateAuthMarkFloatDlg( + SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo* pInfo) override; + virtual VclPtr<VclAbstractDialog> CreateIndexMarkModalDlg( + weld::Window *pParent, SwWrtShell& rSh, SwTOXMark* pCurTOXMark ) override; + + virtual VclPtr<AbstractMailMergeWizard> CreateMailMergeWizard(SwView& rView, std::shared_ptr<SwMailMergeConfigItem>& rConfigItem) override; + + virtual GlossaryGetCurrGroup GetGlossaryCurrGroupFunc() override; + virtual GlossarySetActGroup SetGlossaryActGroupFunc() override; + + // For TabPage + virtual CreateTabPage GetTabPageCreatorFunc( sal_uInt16 nId ) override; + + virtual void ExecuteMMResultSaveDialog(weld::Window* pParent) override; + virtual void ExecuteMMResultPrintDialog(weld::Window* pParent) override; + virtual void ExecuteMMResultEmailDialog(weld::Window* pParent) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/swmessdialog.cxx b/sw/source/ui/dialog/swmessdialog.cxx new file mode 100644 index 000000000..d244dd7b8 --- /dev/null +++ b/sw/source/ui/dialog/swmessdialog.cxx @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <swmessdialog.hxx> + +SwMessageAndEditDialog::SwMessageAndEditDialog(weld::Window* pParent, const OString& rID, + const OUString& rUIXMLDescription) + : MessageDialogController(pParent, rUIXMLDescription, rID, "grid") + , m_xEdit(m_xBuilder->weld_entry("edit")) + , m_xOKPB(m_xBuilder->weld_button("ok")) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/swuiexp.cxx b/sw/source/ui/dialog/swuiexp.cxx new file mode 100644 index 000000000..846618ac0 --- /dev/null +++ b/sw/source/ui/dialog/swuiexp.cxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "swdlgfact.hxx" + +#include <swuiexp.hxx> + +namespace swui +{ + SwAbstractDialogFactory& GetFactory() + { + static SwAbstractDialogFactory_Impl aFactory; + return aFactory; + } +} + +extern "C" +{ + SAL_DLLPUBLIC_EXPORT SwAbstractDialogFactory* SwCreateDialogFactory() + { + return &::swui::GetFactory(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/uiregionsw.cxx b/sw/source/ui/dialog/uiregionsw.cxx new file mode 100644 index 000000000..ac8c987ea --- /dev/null +++ b/sw/source/ui/dialog/uiregionsw.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 <hintids.hxx> +#include <regionsw.hxx> +#include <svl/urihelper.hxx> +#include <svl/PasswordHelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/passwd.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/request.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/docinsert.hxx> +#include <sfx2/filedlghelper.hxx> +#include <editeng/sizeitem.hxx> +#include <svtools/htmlcfg.hxx> + +#include <uitool.hxx> +#include <IMark.hxx> +#include <section.hxx> +#include <docary.hxx> +#include <doc.hxx> +#include <wdocsh.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <column.hxx> +#include <fmtclbl.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <shellio.hxx> + +#include <cmdid.h> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <sfx2/bindings.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> +#include <memory> + +using namespace ::com::sun::star; + +namespace { + +OUString BuildBitmap(bool bProtect, bool bHidden) +{ + if (bProtect) + return bHidden ? OUString(RID_BMP_PROT_HIDE) : OUString(RID_BMP_PROT_NO_HIDE); + return bHidden ? OUString(RID_BMP_HIDE) : OUString(RID_BMP_NO_HIDE); +} + +} + +static void lcl_ReadSections( SfxMedium& rMedium, weld::ComboBox& rBox ); + +static void lcl_FillList( SwWrtShell& rSh, weld::ComboBox& rSubRegions, weld::ComboBox* pAvailNames, const SwSectionFormat* pNewFormat ) +{ + if( !pNewFormat ) + { + const size_t nCount = rSh.GetSectionFormatCount(); + for (size_t i = 0; i<nCount; i++) + { + SectionType eTmpType; + const SwSectionFormat* pFormat = &rSh.GetSectionFormat(i); + if( !pFormat->GetParent() && + pFormat->IsInNodesArr() && + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + const OUString sString(pFormat->GetSection()->GetSectionName()); + if (pAvailNames) + pAvailNames->append_text(sString); + rSubRegions.append_text(sString); + lcl_FillList( rSh, rSubRegions, pAvailNames, pFormat ); + } + } + } + else + { + SwSections aTmpArr; + pNewFormat->GetChildSections(aTmpArr, SectionSort::Pos); + if( !aTmpArr.empty() ) + { + SectionType eTmpType; + for( const auto pSect : aTmpArr ) + { + const SwSectionFormat* pFormat = pSect->GetFormat(); + if( pFormat->IsInNodesArr()&& + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + const OUString sString(pFormat->GetSection()->GetSectionName()); + if (pAvailNames) + pAvailNames->append_text(sString); + rSubRegions.append_text(sString); + lcl_FillList( rSh, rSubRegions, pAvailNames, pFormat ); + } + } + } + } +} + +static void lcl_FillSubRegionList( SwWrtShell& rSh, weld::ComboBox& rSubRegions, weld::ComboBox* pAvailNames ) +{ + rSubRegions.clear(); + lcl_FillList( rSh, rSubRegions, pAvailNames, nullptr ); + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + for( IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin(); + ppMark != pMarkAccess->getBookmarksEnd(); + ++ppMark) + { + const ::sw::mark::IMark* pBkmk = *ppMark; + if( pBkmk->IsExpanded() ) + rSubRegions.append_text( pBkmk->GetName() ); + } +} + +// user data class for region information +class SectRepr +{ +private: + SwSectionData m_SectionData; + SwFormatCol m_Col; + std::unique_ptr<SvxBrushItem> m_Brush; + SwFormatFootnoteAtTextEnd m_FootnoteNtAtEnd; + SwFormatEndAtTextEnd m_EndNtAtEnd; + SwFormatNoBalancedColumns m_Balance; + std::shared_ptr<SvxFrameDirectionItem> m_FrameDirItem; + std::shared_ptr<SvxLRSpaceItem> m_LRSpaceItem; + const size_t m_nArrPos; + // shows, if maybe textcontent is in the region + bool m_bContent : 1; + // for multiselection, mark at first, then work with TreeListBox! + bool m_bSelected : 1; + uno::Sequence<sal_Int8> m_TempPasswd; + +public: + SectRepr(size_t nPos, SwSection& rSect); + + SwSectionData & GetSectionData() { return m_SectionData; } + SwFormatCol& GetCol() { return m_Col; } + std::unique_ptr<SvxBrushItem>& GetBackground() { return m_Brush; } + SwFormatFootnoteAtTextEnd& GetFootnoteNtAtEnd() { return m_FootnoteNtAtEnd; } + SwFormatEndAtTextEnd& GetEndNtAtEnd() { return m_EndNtAtEnd; } + SwFormatNoBalancedColumns& GetBalance() { return m_Balance; } + std::shared_ptr<SvxFrameDirectionItem>& GetFrameDir() { return m_FrameDirItem; } + std::shared_ptr<SvxLRSpaceItem>& GetLRSpace() { return m_LRSpaceItem; } + + size_t GetArrPos() const { return m_nArrPos; } + OUString GetFile() const; + OUString GetSubRegion() const; + void SetFile(OUString const& rFile); + void SetFilter(OUString const& rFilter); + void SetSubRegion(OUString const& rSubRegion); + + bool IsContent() const { return m_bContent; } + void SetContent(bool const bValue) { m_bContent = bValue; } + + void SetSelected() { m_bSelected = true; } + bool IsSelected() const { return m_bSelected; } + + uno::Sequence<sal_Int8> & GetTempPasswd() { return m_TempPasswd; } + void SetTempPasswd(const uno::Sequence<sal_Int8> & rPasswd) + { m_TempPasswd = rPasswd; } +}; + +SectRepr::SectRepr( size_t nPos, SwSection& rSect ) + : m_SectionData( rSect ) + , m_Brush(std::make_unique<SvxBrushItem>(RES_BACKGROUND)) + , m_FrameDirItem(std::make_shared<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)) + , m_LRSpaceItem(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE)) + , m_nArrPos(nPos) + , m_bContent(m_SectionData.GetLinkFileName().isEmpty()) + , m_bSelected(false) +{ + SwSectionFormat *pFormat = rSect.GetFormat(); + if( pFormat ) + { + m_Col = pFormat->GetCol(); + m_Brush = pFormat->makeBackgroundBrushItem(); + m_FootnoteNtAtEnd = pFormat->GetFootnoteAtTextEnd(); + m_EndNtAtEnd = pFormat->GetEndAtTextEnd(); + m_Balance.SetValue(pFormat->GetBalancedColumns().GetValue()); + m_FrameDirItem.reset(pFormat->GetFrameDir().Clone()); + m_LRSpaceItem.reset(pFormat->GetLRSpace().Clone()); + } +} + +void SectRepr::SetFile( const OUString& rFile ) +{ + OUString sNewFile( INetURLObject::decode( rFile, + INetURLObject::DecodeMechanism::Unambiguous )); + const OUString sOldFileName( m_SectionData.GetLinkFileName() ); + const OUString sSub( sOldFileName.getToken( 2, sfx2::cTokenSeparator ) ); + + if( !rFile.isEmpty() || !sSub.isEmpty() ) + { + sNewFile += OUStringChar(sfx2::cTokenSeparator); + if( !rFile.isEmpty() ) // Filter only with FileName + sNewFile += sOldFileName.getToken( 1, sfx2::cTokenSeparator ); + + sNewFile += OUStringChar(sfx2::cTokenSeparator) + sSub; + } + + m_SectionData.SetLinkFileName( sNewFile ); + + if( !rFile.isEmpty() || !sSub.isEmpty() ) + { + m_SectionData.SetType( SectionType::FileLink ); + } + else + { + m_SectionData.SetType( SectionType::Content ); + } +} + +void SectRepr::SetFilter( const OUString& rFilter ) +{ + OUString sNewFile; + const OUString sOldFileName( m_SectionData.GetLinkFileName() ); + sal_Int32 nIdx{ 0 }; + const OUString sFile( sOldFileName.getToken( 0, sfx2::cTokenSeparator, nIdx ) ); // token 0 + const OUString sSub( sOldFileName.getToken( 1, sfx2::cTokenSeparator, nIdx ) ); // token 2 + + if( !sFile.isEmpty() ) + sNewFile = sFile + OUStringChar(sfx2::cTokenSeparator) + + rFilter + OUStringChar(sfx2::cTokenSeparator) + sSub; + else if( !sSub.isEmpty() ) + sNewFile = OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator) + sSub; + + m_SectionData.SetLinkFileName( sNewFile ); + + if( !sNewFile.isEmpty() ) + { + m_SectionData.SetType( SectionType::FileLink ); + } +} + +void SectRepr::SetSubRegion(const OUString& rSubRegion) +{ + OUString sNewFile; + sal_Int32 n(0); + const OUString sLinkFileName(m_SectionData.GetLinkFileName()); + const OUString sOldFileName( sLinkFileName.getToken( 0, sfx2::cTokenSeparator, n ) ); + const OUString sFilter( sLinkFileName.getToken( 0, sfx2::cTokenSeparator, n ) ); + + if( !rSubRegion.isEmpty() || !sOldFileName.isEmpty() ) + sNewFile = sOldFileName + OUStringChar(sfx2::cTokenSeparator) + + sFilter + OUStringChar(sfx2::cTokenSeparator) + rSubRegion; + + m_SectionData.SetLinkFileName( sNewFile ); + + if( !rSubRegion.isEmpty() || !sOldFileName.isEmpty() ) + { + m_SectionData.SetType( SectionType::FileLink ); + } + else + { + m_SectionData.SetType( SectionType::Content ); + } +} + +OUString SectRepr::GetFile() const +{ + const OUString sLinkFile( m_SectionData.GetLinkFileName() ); + + if( sLinkFile.isEmpty() ) + { + return sLinkFile; + } + if (SectionType::DdeLink == m_SectionData.GetType()) + { + sal_Int32 n = 0; + return sLinkFile.replaceFirst( OUStringChar(sfx2::cTokenSeparator), " ", &n ) + .replaceFirst( OUStringChar(sfx2::cTokenSeparator), " ", &n ); + } + return INetURLObject::decode( sLinkFile.getToken( 0, sfx2::cTokenSeparator ), + INetURLObject::DecodeMechanism::Unambiguous ); +} + +OUString SectRepr::GetSubRegion() const +{ + const OUString sLinkFile( m_SectionData.GetLinkFileName() ); + if( !sLinkFile.isEmpty() ) + return sLinkFile.getToken( 2, sfx2::cTokenSeparator ); + return sLinkFile; +} + +// dialog edit regions +SwEditRegionDlg::SwEditRegionDlg(weld::Window* pParent, SwWrtShell& rWrtSh) + : SfxDialogController(pParent, "modules/swriter/ui/editsectiondialog.ui", + "EditSectionDialog") + , m_bSubRegionsFilled(false) + , rSh(rWrtSh) + , bDontCheckPasswd(true) + , m_xCurName(m_xBuilder->weld_entry("curname")) + , m_xTree(m_xBuilder->weld_tree_view("tree")) + , m_xFileCB(m_xBuilder->weld_check_button("link")) + , m_xDDECB(m_xBuilder->weld_check_button("dde")) + , m_xDDEFrame(m_xBuilder->weld_widget("ddedepend")) + , m_xFileNameFT(m_xBuilder->weld_label("filenameft")) + , m_xDDECommandFT(m_xBuilder->weld_label("ddeft")) + , m_xFileNameED(m_xBuilder->weld_entry("filename")) + , m_xFilePB(m_xBuilder->weld_button("file")) + , m_xSubRegionFT(m_xBuilder->weld_label("sectionft")) + , m_xSubRegionED(m_xBuilder->weld_combo_box("section")) + , m_xProtectCB(m_xBuilder->weld_check_button("protect")) + , m_xPasswdCB(m_xBuilder->weld_check_button("withpassword")) + , m_xPasswdPB(m_xBuilder->weld_button("password")) + , m_xHideCB(m_xBuilder->weld_check_button("hide")) + , m_xConditionFT(m_xBuilder->weld_label("conditionft")) + , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("condition"))) + , m_xEditInReadonlyCB(m_xBuilder->weld_check_button("editinro")) + , m_xOK(m_xBuilder->weld_button("ok")) + , m_xOptionsPB(m_xBuilder->weld_button("options")) + , m_xDismiss(m_xBuilder->weld_button("remove")) + , m_xHideFrame(m_xBuilder->weld_widget("hideframe")) +{ + m_xTree->set_size_request(-1, m_xTree->get_height_rows(16)); + m_xFileCB->set_state(TRISTATE_FALSE); + m_xSubRegionED->make_sorted(); + m_xProtectCB->set_state(TRISTATE_FALSE); + m_xHideCB->set_state(TRISTATE_FALSE); + // edit in readonly sections + m_xEditInReadonlyCB->set_state(TRISTATE_FALSE); + + bool bWeb = dynamic_cast<SwWebDocShell*>( rSh.GetView().GetDocShell() ) != nullptr; + + m_xTree->connect_changed(LINK(this, SwEditRegionDlg, GetFirstEntryHdl)); + m_xCurName->connect_changed(LINK(this, SwEditRegionDlg, NameEditHdl)); + m_xConditionED->connect_changed( LINK( this, SwEditRegionDlg, ConditionEditHdl)); + m_xOK->connect_clicked( LINK( this, SwEditRegionDlg, OkHdl)); + m_xPasswdCB->connect_toggled(LINK(this, SwEditRegionDlg, TogglePasswdHdl)); + m_xPasswdPB->connect_clicked(LINK(this, SwEditRegionDlg, ChangePasswdHdl)); + m_xHideCB->connect_toggled(LINK(this, SwEditRegionDlg, ChangeHideHdl)); + // edit in readonly sections + m_xEditInReadonlyCB->connect_toggled(LINK(this, SwEditRegionDlg, ChangeEditInReadonlyHdl)); + + m_xOptionsPB->connect_clicked(LINK(this, SwEditRegionDlg, OptionsHdl)); + m_xProtectCB->connect_toggled(LINK(this, SwEditRegionDlg, ChangeProtectHdl)); + m_xDismiss->connect_clicked( LINK( this, SwEditRegionDlg, ChangeDismissHdl)); + m_xFileCB->connect_toggled(LINK(this, SwEditRegionDlg, UseFileHdl)); + m_xFilePB->connect_clicked(LINK(this, SwEditRegionDlg, FileSearchHdl)); + m_xFileNameED->connect_changed(LINK(this, SwEditRegionDlg, FileNameEntryHdl)); + m_xSubRegionED->connect_changed(LINK(this, SwEditRegionDlg, FileNameComboBoxHdl)); + m_xSubRegionED->connect_popup_toggled(LINK(this, SwEditRegionDlg, SubRegionEventHdl)); + m_xSubRegionED->set_entry_completion(true, true); + + m_xTree->set_selection_mode(SelectionMode::Multiple); + + if (bWeb) + { + m_xDDECB->hide(); + m_xHideFrame->hide(); + m_xPasswdCB->hide(); + } + + m_xDDECB->connect_toggled(LINK(this, SwEditRegionDlg, DDEHdl)); + + pCurrSect = rSh.GetCurrSection(); + RecurseList( nullptr, nullptr ); + + // if the cursor is not in a region the first one will always be selected + if (!m_xTree->get_selected(nullptr)) + { + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (m_xTree->get_iter_first(*xIter)) + { + m_xTree->select(*xIter); + GetFirstEntryHdl(*m_xTree); + } + } + + m_xTree->show(); + bDontCheckPasswd = false; +} + +bool SwEditRegionDlg::CheckPasswd(weld::ToggleButton* pBox) +{ + if (bDontCheckPasswd) + return true; + bool bRet = true; + + m_xTree->selected_foreach([this, &bRet](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + if (!pRepr->GetTempPasswd().hasElements() + && pRepr->GetSectionData().GetPassword().hasElements()) + { + SfxPasswordDialog aPasswdDlg(m_xDialog.get()); + bRet = false; + if (aPasswdDlg.run()) + { + const OUString sNewPasswd(aPasswdDlg.GetPassword()); + css::uno::Sequence <sal_Int8 > aNewPasswd; + SvPasswordHelper::GetHashPassword( aNewPasswd, sNewPasswd ); + if (SvPasswordHelper::CompareHashPassword( + pRepr->GetSectionData().GetPassword(), sNewPasswd)) + { + pRepr->SetTempPasswd(aNewPasswd); + bRet = true; + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_WRONG_PASSWORD))); + xInfoBox->run(); + } + } + } + return false; + }); + if (!bRet && pBox) + { + //reset old button state + if (pBox->get_state() != TRISTATE_INDET) + pBox->set_active(!pBox->get_active()); + } + + return bRet; +} + +// recursively look for child-sections +void SwEditRegionDlg::RecurseList(const SwSectionFormat* pFormat, const weld::TreeIter* pEntry) +{ + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (!pFormat) + { + const size_t nCount=rSh.GetSectionFormatCount(); + for ( size_t n = 0; n < nCount; n++ ) + { + SectionType eTmpType; + if( !( pFormat = &rSh.GetSectionFormat(n))->GetParent() && + pFormat->IsInNodesArr() && + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + SwSection *pSect = pFormat->GetSection(); + SectRepr* pSectRepr = new SectRepr( n, *pSect ); + + OUString sText(pSect->GetSectionName()); + OUString sImage(BuildBitmap(pSect->IsProtect(),pSect->IsHidden())); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pSectRepr))); + m_xTree->insert(nullptr, -1, &sText, &sId, nullptr, nullptr, &sImage, false, xIter.get()); + + RecurseList(pFormat, xIter.get()); + if (m_xTree->iter_has_child(*xIter)) + m_xTree->expand_row(*xIter); + if (pCurrSect==pSect) + { + m_xTree->select(*xIter); + m_xTree->scroll_to_row(*xIter); + GetFirstEntryHdl(*m_xTree); + } + } + } + } + else + { + SwSections aTmpArr; + pFormat->GetChildSections(aTmpArr, SectionSort::Pos); + for( const auto pSect : aTmpArr ) + { + SectionType eTmpType; + pFormat = pSect->GetFormat(); + if( pFormat->IsInNodesArr() && + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + SectRepr* pSectRepr=new SectRepr( + FindArrPos( pSect->GetFormat() ), *pSect ); + + OUString sText(pSect->GetSectionName()); + OUString sImage = BuildBitmap(pSect->IsProtect(), pSect->IsHidden()); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pSectRepr))); + m_xTree->insert(pEntry, -1, &sText, &sId, nullptr, nullptr, &sImage, false, xIter.get()); + + RecurseList(pSect->GetFormat(), xIter.get()); + if (m_xTree->iter_has_child(*xIter)) + m_xTree->expand_row(*xIter); + if (pCurrSect==pSect) + { + m_xTree->select(*xIter); + m_xTree->scroll_to_row(*xIter); + GetFirstEntryHdl(*m_xTree); + } + } + } + } +} + +size_t SwEditRegionDlg::FindArrPos(const SwSectionFormat* pFormat ) +{ + const size_t nCount=rSh.GetSectionFormatCount(); + for ( size_t i = 0; i < nCount; i++ ) + if ( pFormat == &rSh.GetSectionFormat(i) ) + return i; + + OSL_FAIL("SectionFormat not on the list" ); + return SIZE_MAX; +} + +SwEditRegionDlg::~SwEditRegionDlg( ) +{ + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (m_xTree->get_iter_first(*xIter)) + { + do + { + delete reinterpret_cast<SectRepr*>(m_xTree->get_id(*xIter).toInt64()); + } while (m_xTree->iter_next(*xIter)); + } +} + +void SwEditRegionDlg::SelectSection(const OUString& rSectionName) +{ + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (m_xTree->get_iter_first(*xIter)) + { + do + { + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(*xIter).toInt64()); + if (pRepr->GetSectionData().GetSectionName() == rSectionName) + { + m_xTree->unselect_all(); + m_xTree->select(*xIter); + m_xTree->scroll_to_row(*xIter); + GetFirstEntryHdl(*m_xTree); + break; + } + } while (m_xTree->iter_next(*xIter)); + } +} + +// selected entry in TreeListBox is showed in Edit window in case of +// multiselection some controls are disabled +IMPL_LINK(SwEditRegionDlg, GetFirstEntryHdl, weld::TreeView&, rBox, void) +{ + bDontCheckPasswd = true; + std::unique_ptr<weld::TreeIter> xIter(rBox.make_iterator()); + bool bEntry = rBox.get_selected(xIter.get()); + m_xHideCB->set_sensitive(true); + // edit in readonly sections + m_xEditInReadonlyCB->set_sensitive(true); + + m_xProtectCB->set_sensitive(true); + m_xFileCB->set_sensitive(true); + css::uno::Sequence <sal_Int8> aCurPasswd; + if (1 < rBox.count_selected_rows()) + { + m_xHideCB->set_state(TRISTATE_INDET); + m_xProtectCB->set_state(TRISTATE_INDET); + // edit in readonly sections + m_xEditInReadonlyCB->set_state(TRISTATE_INDET); + m_xFileCB->set_state(TRISTATE_INDET); + + bool bHiddenValid = true; + bool bProtectValid = true; + bool bConditionValid = true; + // edit in readonly sections + bool bEditInReadonlyValid = true; + bool bEditInReadonly = true; + + bool bHidden = true; + bool bProtect = true; + OUString sCondition; + bool bFirst = true; + bool bFileValid = true; + bool bFile = true; + bool bPasswdValid = true; + + m_xTree->selected_foreach([&](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + SwSectionData const& rData( pRepr->GetSectionData() ); + if(bFirst) + { + sCondition = rData.GetCondition(); + bHidden = rData.IsHidden(); + bProtect = rData.IsProtectFlag(); + // edit in readonly sections + bEditInReadonly = rData.IsEditInReadonlyFlag(); + + bFile = (rData.GetType() != SectionType::Content); + aCurPasswd = rData.GetPassword(); + } + else + { + if(sCondition != rData.GetCondition()) + bConditionValid = false; + bHiddenValid = (bHidden == rData.IsHidden()); + bProtectValid = (bProtect == rData.IsProtectFlag()); + // edit in readonly sections + bEditInReadonlyValid = + (bEditInReadonly == rData.IsEditInReadonlyFlag()); + + bFileValid = (bFile == + (rData.GetType() != SectionType::Content)); + bPasswdValid = (aCurPasswd == rData.GetPassword()); + } + bFirst = false; + return false; + }); + + m_xHideCB->set_state(!bHiddenValid ? TRISTATE_INDET : + bHidden ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xProtectCB->set_state(!bProtectValid ? TRISTATE_INDET : + bProtect ? TRISTATE_TRUE : TRISTATE_FALSE); + // edit in readonly sections + m_xEditInReadonlyCB->set_state(!bEditInReadonlyValid ? TRISTATE_INDET : + bEditInReadonly ? TRISTATE_TRUE : TRISTATE_FALSE); + + m_xFileCB->set_state(!bFileValid ? TRISTATE_INDET : + bFile ? TRISTATE_TRUE : TRISTATE_FALSE); + + if (bConditionValid) + m_xConditionED->set_text(sCondition); + else + { + m_xConditionFT->set_sensitive(false); + m_xConditionED->set_sensitive(false); + } + + m_xCurName->set_sensitive(false); + m_xDDECB->set_sensitive(false); + m_xDDEFrame->set_sensitive(false); + m_xOptionsPB->set_sensitive(false); + bool bPasswdEnabled = m_xProtectCB->get_state() == TRISTATE_TRUE; + m_xPasswdCB->set_sensitive(bPasswdEnabled); + m_xPasswdPB->set_sensitive(bPasswdEnabled); + if(!bPasswdValid) + { + rBox.get_selected(xIter.get()); + rBox.unselect_all(); + rBox.select(*xIter); + GetFirstEntryHdl(rBox); + return; + } + else + m_xPasswdCB->set_active(aCurPasswd.hasElements()); + } + else if (bEntry ) + { + m_xCurName->set_sensitive(true); + m_xOptionsPB->set_sensitive(true); + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(*xIter).toInt64()); + SwSectionData const& rData( pRepr->GetSectionData() ); + m_xConditionED->set_text(rData.GetCondition()); + m_xHideCB->set_sensitive(true); + m_xHideCB->set_state((rData.IsHidden()) ? TRISTATE_TRUE : TRISTATE_FALSE); + bool bHide = TRISTATE_TRUE == m_xHideCB->get_state(); + m_xConditionED->set_sensitive(bHide); + m_xConditionFT->set_sensitive(bHide); + m_xPasswdCB->set_active(rData.GetPassword().hasElements()); + + m_xOK->set_sensitive(true); + m_xPasswdCB->set_sensitive(true); + m_xCurName->set_text(rBox.get_text(*xIter)); + m_xCurName->set_sensitive(true); + m_xDismiss->set_sensitive(true); + const OUString aFile = pRepr->GetFile(); + const OUString sSub = pRepr->GetSubRegion(); + m_xSubRegionED->clear(); + m_xSubRegionED->append_text(""); // put in a dummy entry, which is replaced when m_bSubRegionsFilled is set + m_bSubRegionsFilled = false; + if( !aFile.isEmpty() || !sSub.isEmpty() ) + { + m_xFileCB->set_active(true); + m_xFileNameED->set_text(aFile); + m_xSubRegionED->set_entry_text(sSub); + m_xDDECB->set_active(rData.GetType() == SectionType::DdeLink); + } + else + { + m_xFileCB->set_active(false); + m_xFileNameED->set_text(aFile); + m_xDDECB->set_sensitive(false); + m_xDDECB->set_active(false); + } + UseFileHdl(*m_xFileCB); + DDEHdl(*m_xDDECB); + m_xProtectCB->set_state((rData.IsProtectFlag()) + ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xProtectCB->set_sensitive(true); + + // edit in readonly sections + m_xEditInReadonlyCB->set_state((rData.IsEditInReadonlyFlag()) + ? TRISTATE_TRUE : TRISTATE_FALSE); + m_xEditInReadonlyCB->set_sensitive(true); + + bool bPasswdEnabled = m_xProtectCB->get_active(); + m_xPasswdCB->set_sensitive(bPasswdEnabled); + m_xPasswdPB->set_sensitive(bPasswdEnabled); + } + bDontCheckPasswd = false; +} + +// in OkHdl the modified settings are being applied and reversed regions are deleted +IMPL_LINK_NOARG(SwEditRegionDlg, OkHdl, weld::Button&, void) +{ + // temp. Array because during changing of a region the position + // inside of the "Core-Arrays" can be shifted: + // - at linked regions, when they have more SubRegions or get + // new ones. + // StartUndo must certainly also happen not before the formats + // are copied (ClearRedo!) + + const SwSectionFormats& rDocFormats = rSh.GetDoc()->GetSections(); + SwSectionFormats aOrigArray(rDocFormats); + + rSh.StartAllAction(); + rSh.StartUndo(); + rSh.ResetSelect( nullptr,false ); + + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (m_xTree->get_iter_first(*xIter)) + { + do + { + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(*xIter).toInt64()); + SwSectionFormat* pFormat = aOrigArray[ pRepr->GetArrPos() ]; + if (!pRepr->GetSectionData().IsProtectFlag()) + { + pRepr->GetSectionData().SetPassword(uno::Sequence<sal_Int8 >()); + } + size_t nNewPos = rDocFormats.GetPos(pFormat); + if ( SIZE_MAX != nNewPos ) + { + std::unique_ptr<SfxItemSet> pSet(pFormat->GetAttrSet().Clone( false )); + if( pFormat->GetCol() != pRepr->GetCol() ) + pSet->Put( pRepr->GetCol() ); + + std::unique_ptr<SvxBrushItem> aBrush(pFormat->makeBackgroundBrushItem(false)); + if( aBrush && pRepr->GetBackground() && *aBrush != *pRepr->GetBackground() ) + pSet->Put( *pRepr->GetBackground() ); + + if( pFormat->GetFootnoteAtTextEnd(false) != pRepr->GetFootnoteNtAtEnd() ) + pSet->Put( pRepr->GetFootnoteNtAtEnd() ); + + if( pFormat->GetEndAtTextEnd(false) != pRepr->GetEndNtAtEnd() ) + pSet->Put( pRepr->GetEndNtAtEnd() ); + + if( pFormat->GetBalancedColumns() != pRepr->GetBalance() ) + pSet->Put( pRepr->GetBalance() ); + + if( pFormat->GetFrameDir() != *pRepr->GetFrameDir() ) + pSet->Put( *pRepr->GetFrameDir() ); + + if( pFormat->GetLRSpace() != *pRepr->GetLRSpace()) + pSet->Put( *pRepr->GetLRSpace()); + + rSh.UpdateSection( nNewPos, pRepr->GetSectionData(), + pSet->Count() ? pSet.get() : nullptr ); + } + } while (m_xTree->iter_next(*xIter)); + } + + for (SectReprs_t::reverse_iterator it = m_SectReprs.rbegin(), aEnd = m_SectReprs.rend(); it != aEnd; ++it) + { + assert(it->first == it->second->GetArrPos()); + SwSectionFormat* pFormat = aOrigArray[ it->second->GetArrPos() ]; + const size_t nNewPos = rDocFormats.GetPos( pFormat ); + if( SIZE_MAX != nNewPos ) + rSh.DelSectionFormat( nNewPos ); + } + + aOrigArray.clear(); + + // response must be called ahead of EndAction's end, + // otherwise ScrollError can occur. + m_xDialog->response(RET_OK); + + rSh.EndUndo(); + rSh.EndAllAction(); +} + +// Toggle protect +IMPL_LINK(SwEditRegionDlg, ChangeProtectHdl, weld::ToggleButton&, rButton, void) +{ + if (!CheckPasswd(&rButton)) + return; + bool bCheck = TRISTATE_TRUE == rButton.get_state(); + m_xTree->selected_foreach([this, bCheck](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + pRepr->GetSectionData().SetProtectFlag(bCheck); + OUString aImage = BuildBitmap(bCheck, TRISTATE_TRUE == m_xHideCB->get_state()); + m_xTree->set_image(rEntry, aImage); + return false; + }); + m_xPasswdCB->set_sensitive(bCheck); + m_xPasswdPB->set_sensitive(bCheck); +} + +// Toggle hide +IMPL_LINK( SwEditRegionDlg, ChangeHideHdl, weld::ToggleButton&, rButton, void) +{ + if (!CheckPasswd(&rButton)) + return; + m_xTree->selected_foreach([this, &rButton](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + pRepr->GetSectionData().SetHidden(TRISTATE_TRUE == rButton.get_state()); + OUString aImage = BuildBitmap(TRISTATE_TRUE == m_xProtectCB->get_state(), + TRISTATE_TRUE == rButton.get_state()); + m_xTree->set_image(rEntry, aImage); + return false; + }); + bool bHide = TRISTATE_TRUE == rButton.get_state(); + m_xConditionED->set_sensitive(bHide); + m_xConditionFT->set_sensitive(bHide); +} + +// Toggle edit in readonly +IMPL_LINK(SwEditRegionDlg, ChangeEditInReadonlyHdl, weld::ToggleButton&, rButton, void) +{ + if (!CheckPasswd(&rButton)) + return; + m_xTree->selected_foreach([this, &rButton](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + pRepr->GetSectionData().SetEditInReadonlyFlag( + TRISTATE_TRUE == rButton.get_state()); + return false; + }); +} + +// clear selected region +IMPL_LINK_NOARG(SwEditRegionDlg, ChangeDismissHdl, weld::Button&, void) +{ + if(!CheckPasswd()) + return; + // at first mark all selected + m_xTree->selected_foreach([this](weld::TreeIter& rEntry){ + SectRepr* const pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + pSectRepr->SetSelected(); + return false; + }); + + std::unique_ptr<weld::TreeIter> xEntry(m_xTree->make_iterator()); + bool bEntry(m_xTree->get_selected(xEntry.get())); + // then delete + while (bEntry) + { + SectRepr* const pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(*xEntry).toInt64()); + std::unique_ptr<weld::TreeIter> xRemove; + bool bRestart = false; + if (pSectRepr->IsSelected()) + { + m_SectReprs.insert(std::make_pair(pSectRepr->GetArrPos(), + std::unique_ptr<SectRepr>(pSectRepr))); + if (m_xTree->iter_has_child(*xEntry)) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTree->make_iterator(xEntry.get())); + (void)m_xTree->iter_children(*xChild); + std::unique_ptr<weld::TreeIter> xParent(m_xTree->make_iterator(xEntry.get())); + if (!m_xTree->iter_parent(*xParent)) + xParent.reset(); + bool bChild = true; + do + { + // because of the repositioning we have to start at the beginning again + bRestart = true; + std::unique_ptr<weld::TreeIter> xMove(m_xTree->make_iterator(xChild.get())); + bChild = m_xTree->iter_next_sibling(*xChild); + m_xTree->move_subtree(*xMove, xParent.get(), m_xTree->get_iter_index_in_parent(*xEntry)); + } while (bChild); + } + xRemove = m_xTree->make_iterator(xEntry.get()); + } + if (bRestart) + bEntry = m_xTree->get_iter_first(*xEntry); + else + bEntry = m_xTree->iter_next(*xEntry); + if (xRemove) + m_xTree->remove(*xRemove); + } + + if (m_xTree->get_selected(nullptr)) + return; + + m_xConditionFT->set_sensitive(false); + m_xConditionED->set_sensitive(false); + m_xDismiss->set_sensitive(false); + m_xCurName->set_sensitive(false); + m_xProtectCB->set_sensitive(false); + m_xPasswdCB->set_sensitive(false); + m_xHideCB->set_sensitive(false); + // edit in readonly sections + m_xEditInReadonlyCB->set_sensitive(false); + m_xEditInReadonlyCB->set_state(TRISTATE_FALSE); + m_xProtectCB->set_state(TRISTATE_FALSE); + m_xPasswdCB->set_active(false); + m_xHideCB->set_state(TRISTATE_FALSE); + m_xFileCB->set_active(false); + // otherwise the focus would be on HelpButton + m_xOK->grab_focus(); + UseFileHdl(*m_xFileCB); +} + +// link CheckBox to file? +IMPL_LINK(SwEditRegionDlg, UseFileHdl, weld::ToggleButton&, rButton, void) +{ + if (!CheckPasswd(&rButton)) + return; + bool bMulti = 1 < m_xTree->count_selected_rows(); + bool bFile = rButton.get_active(); + if (m_xTree->get_selected(nullptr)) + { + m_xTree->selected_foreach([&](weld::TreeIter& rEntry){ + SectRepr* const pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + bool bContent = pSectRepr->IsContent(); + if( rButton.get_active() && bContent && rSh.HasSelection() ) + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + SwResId(STR_QUERY_CONNECT))); + if (RET_NO == xQueryBox->run()) + rButton.set_active( false ); + } + if( bFile ) + pSectRepr->SetContent(false); + else + { + pSectRepr->SetFile(OUString()); + pSectRepr->SetSubRegion(OUString()); + pSectRepr->GetSectionData().SetLinkFilePassword(OUString()); + } + return false; + }); + m_xDDECB->set_sensitive(bFile && !bMulti); + m_xDDEFrame->set_sensitive(bFile && !bMulti); + if( bFile ) + { + m_xProtectCB->set_state(TRISTATE_TRUE); + ChangeProtectHdl(*m_xProtectCB); + m_xFileNameED->grab_focus(); + + } + else + { + m_xDDECB->set_active(false); + m_xSubRegionED->set_entry_text(OUString()); + } + DDEHdl(*m_xDDECB); + } + else + { + rButton.set_active(false); + rButton.set_sensitive(false); + m_xDDECB->set_active(false); + m_xDDECB->set_sensitive(false); + m_xDDEFrame->set_sensitive(false); + } +} + +// call dialog paste file +IMPL_LINK_NOARG(SwEditRegionDlg, FileSearchHdl, weld::Button&, void) +{ + if(!CheckPasswd()) + return; + m_pDocInserter.reset(new ::sfx2::DocumentInserter(m_xDialog.get(), "swriter")); + m_pDocInserter->StartExecuteModal( LINK( this, SwEditRegionDlg, DlgClosedHdl ) ); +} + +IMPL_LINK_NOARG(SwEditRegionDlg, OptionsHdl, weld::Button&, void) +{ + if(!CheckPasswd()) + return; + SectRepr* pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_selected_id().toInt64()); + if (!pSectRepr) + return; + + SfxItemSet aSet( + rSh.GetView().GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + RES_FTN_AT_TXTEND, RES_FRAMEDIR, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE>{}); + + aSet.Put( pSectRepr->GetCol() ); + aSet.Put( *pSectRepr->GetBackground() ); + aSet.Put( pSectRepr->GetFootnoteNtAtEnd() ); + aSet.Put( pSectRepr->GetEndNtAtEnd() ); + aSet.Put( pSectRepr->GetBalance() ); + aSet.Put( *pSectRepr->GetFrameDir() ); + aSet.Put( *pSectRepr->GetLRSpace() ); + + const SwSectionFormats& rDocFormats = rSh.GetDoc()->GetSections(); + SwSectionFormats aOrigArray(rDocFormats); + + SwSectionFormat* pFormat = aOrigArray[pSectRepr->GetArrPos()]; + long nWidth = rSh.GetSectionWidth(*pFormat); + aOrigArray.clear(); + if (!nWidth) + nWidth = USHRT_MAX; + + aSet.Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth)); + aSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(nWidth, nWidth))); + + SwSectionPropertyTabDialog aTabDlg(m_xDialog.get(), aSet, rSh); + if (RET_OK == aTabDlg.run()) + { + const SfxItemSet* pOutSet = aTabDlg.GetOutputItemSet(); + if( pOutSet && pOutSet->Count() ) + { + const SfxPoolItem *pColItem, *pBrushItem, + *pFootnoteItem, *pEndItem, *pBalanceItem, + *pFrameDirItem, *pLRSpaceItem; + SfxItemState eColState = pOutSet->GetItemState( + RES_COL, false, &pColItem ); + SfxItemState eBrushState = pOutSet->GetItemState( + RES_BACKGROUND, false, &pBrushItem ); + SfxItemState eFootnoteState = pOutSet->GetItemState( + RES_FTN_AT_TXTEND, false, &pFootnoteItem ); + SfxItemState eEndState = pOutSet->GetItemState( + RES_END_AT_TXTEND, false, &pEndItem ); + SfxItemState eBalanceState = pOutSet->GetItemState( + RES_COLUMNBALANCE, false, &pBalanceItem ); + SfxItemState eFrameDirState = pOutSet->GetItemState( + RES_FRAMEDIR, false, &pFrameDirItem ); + SfxItemState eLRState = pOutSet->GetItemState( + RES_LR_SPACE, false, &pLRSpaceItem); + + if( SfxItemState::SET == eColState || + SfxItemState::SET == eBrushState || + SfxItemState::SET == eFootnoteState || + SfxItemState::SET == eEndState || + SfxItemState::SET == eBalanceState|| + SfxItemState::SET == eFrameDirState|| + SfxItemState::SET == eLRState) + { + m_xTree->selected_foreach([&](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + if( SfxItemState::SET == eColState ) + pRepr->GetCol() = *static_cast<const SwFormatCol*>(pColItem); + if( SfxItemState::SET == eBrushState ) + pRepr->GetBackground().reset(static_cast<SvxBrushItem*>(pBrushItem->Clone())); + if( SfxItemState::SET == eFootnoteState ) + pRepr->GetFootnoteNtAtEnd() = *static_cast<const SwFormatFootnoteAtTextEnd*>(pFootnoteItem); + if( SfxItemState::SET == eEndState ) + pRepr->GetEndNtAtEnd() = *static_cast<const SwFormatEndAtTextEnd*>(pEndItem); + if( SfxItemState::SET == eBalanceState ) + pRepr->GetBalance().SetValue(static_cast<const SwFormatNoBalancedColumns*>(pBalanceItem)->GetValue()); + if( SfxItemState::SET == eFrameDirState ) + pRepr->GetFrameDir()->SetValue(static_cast<const SvxFrameDirectionItem*>(pFrameDirItem)->GetValue()); + if( SfxItemState::SET == eLRState ) + pRepr->GetLRSpace().reset(static_cast<SvxLRSpaceItem*>(pLRSpaceItem->Clone())); + return false; + }); + } + } + } +} + +IMPL_LINK(SwEditRegionDlg, FileNameComboBoxHdl, weld::ComboBox&, rEdit, void) +{ + int nStartPos, nEndPos; + rEdit.get_entry_selection_bounds(nStartPos, nEndPos); + if (!CheckPasswd()) + return; + rEdit.select_entry_region(nStartPos, nEndPos); + SectRepr* pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_selected_id().toInt64()); + pSectRepr->SetSubRegion( rEdit.get_active_text() ); +} + +// Applying of the filename or the linked region +IMPL_LINK(SwEditRegionDlg, FileNameEntryHdl, weld::Entry&, rEdit, void) +{ + int nStartPos, nEndPos; + rEdit.get_selection_bounds(nStartPos, nEndPos); + if (!CheckPasswd()) + return; + rEdit.select_region(nStartPos, nEndPos); + SectRepr* pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_selected_id().toInt64()); + m_xSubRegionED->clear(); + m_xSubRegionED->append_text(""); // put in a dummy entry, which is replaced when m_bSubRegionsFilled is set + m_bSubRegionsFilled = false; + if (m_xDDECB->get_active()) + { + OUString sLink( SwSectionData::CollapseWhiteSpaces(rEdit.get_text()) ); + sal_Int32 nPos = 0; + sLink = sLink.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nPos ); + if (nPos>=0) + { + sLink = sLink.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nPos ); + } + + pSectRepr->GetSectionData().SetLinkFileName( sLink ); + pSectRepr->GetSectionData().SetType( SectionType::DdeLink ); + } + else + { + OUString sTmp(rEdit.get_text()); + if(!sTmp.isEmpty()) + { + SfxMedium* pMedium = rSh.GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + sTmp = URIHelper::SmartRel2Abs( + aAbs, sTmp, URIHelper::GetMaybeFileHdl() ); + } + pSectRepr->SetFile( sTmp ); + pSectRepr->GetSectionData().SetLinkFilePassword(OUString()); + } +} + +IMPL_LINK(SwEditRegionDlg, DDEHdl, weld::ToggleButton&, rButton, void) +{ + if (!CheckPasswd(&rButton)) + return; + SectRepr* pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_selected_id().toInt64()); + if (pSectRepr) + { + bool bFile = m_xFileCB->get_active(); + SwSectionData & rData( pSectRepr->GetSectionData() ); + bool bDDE = rButton.get_active(); + if(bDDE) + { + m_xFileNameFT->hide(); + m_xDDECommandFT->set_sensitive(true); + m_xDDECommandFT->show(); + m_xSubRegionFT->hide(); + m_xSubRegionED->hide(); + if (SectionType::FileLink == rData.GetType()) + { + pSectRepr->SetFile(OUString()); + m_xFileNameED->set_text(OUString()); + rData.SetLinkFilePassword(OUString()); + } + rData.SetType(SectionType::DdeLink); + } + else + { + m_xDDECommandFT->hide(); + m_xFileNameFT->set_sensitive(bFile); + m_xFileNameFT->show(); + m_xSubRegionED->show(); + m_xSubRegionFT->show(); + m_xSubRegionED->set_sensitive(bFile); + m_xSubRegionFT->set_sensitive(bFile); + m_xSubRegionED->set_sensitive(bFile); + if (SectionType::DdeLink == rData.GetType()) + { + rData.SetType(SectionType::FileLink); + pSectRepr->SetFile(OUString()); + rData.SetLinkFilePassword(OUString()); + m_xFileNameED->set_text(OUString()); + } + } + m_xFilePB->set_sensitive(bFile && !bDDE); + } +} + +void SwEditRegionDlg::ChangePasswd(bool bChange) +{ + if (!CheckPasswd()) + { + if (!bChange) + m_xPasswdCB->set_active(!m_xPasswdCB->get_active()); + return; + } + + bool bSet = bChange ? bChange : m_xPasswdCB->get_active(); + + m_xTree->selected_foreach([this, bChange, bSet](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + if(bSet) + { + if(!pRepr->GetTempPasswd().hasElements() || bChange) + { + SfxPasswordDialog aPasswdDlg(m_xDialog.get()); + aPasswdDlg.ShowExtras(SfxShowExtras::CONFIRM); + if (RET_OK == aPasswdDlg.run()) + { + const OUString sNewPasswd(aPasswdDlg.GetPassword()); + if (aPasswdDlg.GetConfirm() == sNewPasswd) + { + SvPasswordHelper::GetHashPassword( pRepr->GetTempPasswd(), sNewPasswd ); + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_WRONG_PASSWD_REPEAT))); + xInfoBox->run(); + ChangePasswd(bChange); + return true; + } + } + else + { + if(!bChange) + m_xPasswdCB->set_active(false); + return true; + } + } + pRepr->GetSectionData().SetPassword(pRepr->GetTempPasswd()); + } + else + { + pRepr->GetSectionData().SetPassword(uno::Sequence<sal_Int8 >()); + } + return false; + }); +} + +IMPL_LINK_NOARG(SwEditRegionDlg, TogglePasswdHdl, weld::ToggleButton&, void) +{ + ChangePasswd(false); +} + +IMPL_LINK_NOARG(SwEditRegionDlg, ChangePasswdHdl, weld::Button&, void) +{ + ChangePasswd(true); +} + +// the current region name is being added to the TreeListBox immediately during +// editing, with empty string no Ok() +IMPL_LINK_NOARG(SwEditRegionDlg, NameEditHdl, weld::Entry&, void) +{ + if(!CheckPasswd()) + return; + std::unique_ptr<weld::TreeIter> xIter(m_xTree->make_iterator()); + if (m_xTree->get_selected(xIter.get())) + { + const OUString aName = m_xCurName->get_text(); + m_xTree->set_text(*xIter, aName); + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(*xIter).toInt64()); + pRepr->GetSectionData().SetSectionName(aName); + + m_xOK->set_sensitive(!aName.isEmpty()); + } +} + +IMPL_LINK( SwEditRegionDlg, ConditionEditHdl, weld::Entry&, rEdit, void ) +{ + int nStartPos, nEndPos; + rEdit.get_selection_bounds(nStartPos, nEndPos); + if(!CheckPasswd()) + return; + rEdit.select_region(nStartPos, nEndPos); + + m_xTree->selected_foreach([this, &rEdit](weld::TreeIter& rEntry){ + SectRepr* pRepr = reinterpret_cast<SectRepr*>(m_xTree->get_id(rEntry).toInt64()); + pRepr->GetSectionData().SetCondition(rEdit.get_text()); + return false; + }); +} + +IMPL_LINK( SwEditRegionDlg, DlgClosedHdl, sfx2::FileDialogHelper *, _pFileDlg, void ) +{ + OUString sFileName, sFilterName, sPassword; + if ( _pFileDlg->GetError() == ERRCODE_NONE ) + { + std::unique_ptr<SfxMedium> pMedium(m_pDocInserter->CreateMedium("sglobal")); + if ( pMedium ) + { + sFileName = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ); + sFilterName = pMedium->GetFilter()->GetFilterName(); + const SfxPoolItem* pItem; + if ( SfxItemState::SET == pMedium->GetItemSet()->GetItemState( SID_PASSWORD, false, &pItem ) ) + sPassword = static_cast<const SfxStringItem*>(pItem )->GetValue(); + ::lcl_ReadSections(*pMedium, *m_xSubRegionED); + } + } + + SectRepr* pSectRepr = reinterpret_cast<SectRepr*>(m_xTree->get_selected_id().toInt64()); + if (pSectRepr) + { + pSectRepr->SetFile( sFileName ); + pSectRepr->SetFilter( sFilterName ); + pSectRepr->GetSectionData().SetLinkFilePassword(sPassword); + m_xFileNameED->set_text(pSectRepr->GetFile()); + } +} + +IMPL_LINK_NOARG(SwEditRegionDlg, SubRegionEventHdl, weld::ComboBox&, void) +{ + if (!m_bSubRegionsFilled) + { + //if necessary fill the names bookmarks/sections/tables now + + OUString sFileName = m_xFileNameED->get_text(); + if(!sFileName.isEmpty()) + { + SfxMedium* pMedium = rSh.GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + sFileName = URIHelper::SmartRel2Abs( + aAbs, sFileName, URIHelper::GetMaybeFileHdl() ); + + //load file and set the shell + SfxMedium aMedium( sFileName, StreamMode::STD_READ ); + sFileName = aMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ); + ::lcl_ReadSections(aMedium, *m_xSubRegionED); + } + else + lcl_FillSubRegionList(rSh, *m_xSubRegionED, nullptr); + m_bSubRegionsFilled = true; + } +} + +// helper function - read section names from medium +static void lcl_ReadSections( SfxMedium& rMedium, weld::ComboBox& rBox ) +{ + rBox.clear(); + uno::Reference < embed::XStorage > xStg; + if( rMedium.IsStorage() && (xStg = rMedium.GetStorage()).is() ) + { + std::vector<OUString> aArr; + SotClipboardFormatId nFormat = SotStorage::GetFormatID( xStg ); + if ( nFormat == SotClipboardFormatId::STARWRITER_60 || nFormat == SotClipboardFormatId::STARWRITERGLOB_60 || + nFormat == SotClipboardFormatId::STARWRITER_8 || nFormat == SotClipboardFormatId::STARWRITERGLOB_8) + SwGetReaderXML()->GetSectionList( rMedium, aArr ); + + for (auto const& it : aArr) + { + rBox.append_text(it); + } + } +} + +SwInsertSectionTabDialog::SwInsertSectionTabDialog( + weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell& rSh) + : SfxTabDialogController(pParent, "modules/swriter/ui/insertsectiondialog.ui", + "InsertSectionDialog",&rSet) + , rWrtSh(rSh) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + AddTabPage("section", SwInsertSectionTabPage::Create, nullptr); + AddTabPage("columns", SwColumnPage::Create, nullptr); + AddTabPage("background", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); + AddTabPage("notes", SwSectionFootnoteEndTabPage::Create, nullptr); + AddTabPage("indents", SwSectionIndentTabPage::Create, nullptr); + + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + long nHtmlMode = rHtmlOpt.GetExportMode(); + + bool bWeb = dynamic_cast<SwWebDocShell*>( rSh.GetView().GetDocShell() ) != nullptr ; + if(bWeb) + { + RemoveTabPage("notes"); + RemoveTabPage("indents"); + if( HTML_CFG_NS40 != nHtmlMode && HTML_CFG_WRITER != nHtmlMode) + RemoveTabPage("columns"); + } + SetCurPageId("section"); +} + +SwInsertSectionTabDialog::~SwInsertSectionTabDialog() +{ +} + +void SwInsertSectionTabDialog::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (rId == "section") + static_cast<SwInsertSectionTabPage&>(rPage).SetWrtShell(rWrtSh); + else if (rId == "background") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_SELECTOR))); + rPage.PageCreated(aSet); + } + else if (rId == "columns") + { + const SwFormatFrameSize& rSize = GetInputSetImpl()->Get(RES_FRM_SIZE); + static_cast<SwColumnPage&>(rPage).SetPageWidth(rSize.GetWidth()); + static_cast<SwColumnPage&>(rPage).ShowBalance(true); + static_cast<SwColumnPage&>(rPage).SetInSection(true); + } + else if (rId == "indents") + static_cast<SwSectionIndentTabPage&>(rPage).SetWrtShell(rWrtSh); +} + +void SwInsertSectionTabDialog::SetSectionData(SwSectionData const& rSect) +{ + m_pSectionData.reset( new SwSectionData(rSect) ); +} + +short SwInsertSectionTabDialog::Ok() +{ + short nRet = SfxTabDialogController::Ok(); + OSL_ENSURE(m_pSectionData, "SwInsertSectionTabDialog: no SectionData?"); + const SfxItemSet* pOutputItemSet = GetOutputItemSet(); + rWrtSh.InsertSection(*m_pSectionData, pOutputItemSet); + SfxViewFrame* pViewFrame = rWrtSh.GetView().GetViewFrame(); + uno::Reference< frame::XDispatchRecorder > xRecorder = + pViewFrame->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + SfxRequest aRequest( pViewFrame, FN_INSERT_REGION); + const SfxPoolItem* pCol; + if(SfxItemState::SET == pOutputItemSet->GetItemState(RES_COL, false, &pCol)) + { + aRequest.AppendItem(SfxUInt16Item(SID_ATTR_COLUMNS, + static_cast<const SwFormatCol*>(pCol)->GetColumns().size())); + } + aRequest.AppendItem(SfxStringItem( FN_PARAM_REGION_NAME, + m_pSectionData->GetSectionName())); + aRequest.AppendItem(SfxStringItem( FN_PARAM_REGION_CONDITION, + m_pSectionData->GetCondition())); + aRequest.AppendItem(SfxBoolItem( FN_PARAM_REGION_HIDDEN, + m_pSectionData->IsHidden())); + aRequest.AppendItem(SfxBoolItem( FN_PARAM_REGION_PROTECT, + m_pSectionData->IsProtectFlag())); + // edit in readonly sections + aRequest.AppendItem(SfxBoolItem( FN_PARAM_REGION_EDIT_IN_READONLY, + m_pSectionData->IsEditInReadonlyFlag())); + + const OUString sLinkFileName( m_pSectionData->GetLinkFileName() ); + sal_Int32 n = 0; + aRequest.AppendItem(SfxStringItem( FN_PARAM_1, sLinkFileName.getToken( 0, sfx2::cTokenSeparator, n ))); + aRequest.AppendItem(SfxStringItem( FN_PARAM_2, sLinkFileName.getToken( 0, sfx2::cTokenSeparator, n ))); + aRequest.AppendItem(SfxStringItem( FN_PARAM_3, sLinkFileName.getToken( 0, sfx2::cTokenSeparator, n ))); + aRequest.Done(); + } + return nRet; +} + +SwInsertSectionTabPage::SwInsertSectionTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/sectionpage.ui", "SectionPage", &rAttrSet) + , m_pWrtSh(nullptr) + , m_xCurName(m_xBuilder->weld_entry_tree_view("sectionnames", "sectionnames-entry", + "sectionnames-list")) + , m_xFileCB(m_xBuilder->weld_check_button("link")) + , m_xDDECB(m_xBuilder->weld_check_button("dde")) + , m_xDDECommandFT(m_xBuilder->weld_label("ddelabel")) + , m_xFileNameFT(m_xBuilder->weld_label("filelabel")) + , m_xFileNameED(m_xBuilder->weld_entry("filename")) + , m_xFilePB(m_xBuilder->weld_button("selectfile")) + , m_xSubRegionFT(m_xBuilder->weld_label("sectionlabel")) + , m_xSubRegionED(m_xBuilder->weld_combo_box("sectionname")) + , m_xProtectCB(m_xBuilder->weld_check_button("protect")) + , m_xPasswdCB(m_xBuilder->weld_check_button("withpassword")) + , m_xPasswdPB(m_xBuilder->weld_button("selectpassword")) + , m_xHideCB(m_xBuilder->weld_check_button("hide")) + , m_xConditionFT(m_xBuilder->weld_label("condlabel")) + , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("withcond"))) + // edit in readonly sections + , m_xEditInReadonlyCB(m_xBuilder->weld_check_button("editable")) +{ + m_xCurName->make_sorted(); + m_xCurName->set_height_request_by_rows(12); + m_xSubRegionED->make_sorted(); + + m_xProtectCB->connect_toggled( LINK( this, SwInsertSectionTabPage, ChangeProtectHdl)); + m_xPasswdCB->connect_toggled( LINK( this, SwInsertSectionTabPage, TogglePasswdHdl)); + m_xPasswdPB->connect_clicked( LINK( this, SwInsertSectionTabPage, ChangePasswdHdl)); + m_xHideCB->connect_toggled( LINK( this, SwInsertSectionTabPage, ChangeHideHdl)); + m_xFileCB->connect_toggled( LINK( this, SwInsertSectionTabPage, UseFileHdl )); + m_xFilePB->connect_clicked( LINK( this, SwInsertSectionTabPage, FileSearchHdl )); + m_xCurName->connect_changed( LINK( this, SwInsertSectionTabPage, NameEditHdl)); + m_xDDECB->connect_toggled( LINK( this, SwInsertSectionTabPage, DDEHdl )); + ChangeProtectHdl(*m_xProtectCB); + m_xSubRegionED->set_entry_completion(true, true); +} + +SwInsertSectionTabPage::~SwInsertSectionTabPage() +{ +} + +void SwInsertSectionTabPage::SetWrtShell(SwWrtShell& rSh) +{ + m_pWrtSh = &rSh; + + bool bWeb = dynamic_cast<SwWebDocShell*>( m_pWrtSh->GetView().GetDocShell() )!= nullptr; + if(bWeb) + { + m_xHideCB->hide(); + m_xConditionED->hide(); + m_xConditionFT->hide(); + m_xDDECB->hide(); + m_xDDECommandFT->hide(); + } + + lcl_FillSubRegionList(*m_pWrtSh, *m_xSubRegionED, m_xCurName.get()); + + SwSectionData *const pSectionData = + static_cast<SwInsertSectionTabDialog*>(GetDialogController()) + ->GetSectionData(); + if (pSectionData) // something set? + { + const OUString sSectionName(pSectionData->GetSectionName()); + m_xCurName->set_entry_text(rSh.GetUniqueSectionName(&sSectionName)); + m_xProtectCB->set_active( pSectionData->IsProtectFlag() ); + ChangeProtectHdl(*m_xProtectCB); + m_sFileName = pSectionData->GetLinkFileName(); + m_sFilePasswd = pSectionData->GetLinkFilePassword(); + m_xFileCB->set_active( !m_sFileName.isEmpty() ); + m_xFileNameED->set_text( m_sFileName ); + UseFileHdl(*m_xFileCB); + } + else + { + m_xCurName->set_entry_text(rSh.GetUniqueSectionName()); + } +} + +bool SwInsertSectionTabPage::FillItemSet( SfxItemSet* ) +{ + SwSectionData aSection(SectionType::Content, m_xCurName->get_active_text()); + aSection.SetCondition(m_xConditionED->get_text()); + bool bProtected = m_xProtectCB->get_active(); + aSection.SetProtectFlag(bProtected); + aSection.SetHidden(m_xHideCB->get_active()); + // edit in readonly sections + aSection.SetEditInReadonlyFlag(m_xEditInReadonlyCB->get_active()); + + if(bProtected) + { + aSection.SetPassword(m_aNewPasswd); + } + const OUString sFileName = m_xFileNameED->get_text(); + const OUString sSubRegion = m_xSubRegionED->get_active_text(); + bool bDDe = m_xDDECB->get_active(); + if (m_xFileCB->get_active() && (!sFileName.isEmpty() || !sSubRegion.isEmpty() || bDDe)) + { + OUString aLinkFile; + if( bDDe ) + { + aLinkFile = SwSectionData::CollapseWhiteSpaces(sFileName); + sal_Int32 nPos = 0; + aLinkFile = aLinkFile.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nPos ); + if (nPos>=0) + { + aLinkFile = aLinkFile.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nPos ); + } + } + else + { + if(!sFileName.isEmpty()) + { + SfxMedium* pMedium = m_pWrtSh->GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + aLinkFile = URIHelper::SmartRel2Abs( + aAbs, sFileName, URIHelper::GetMaybeFileHdl() ); + aSection.SetLinkFilePassword( m_sFilePasswd ); + } + + aLinkFile += OUStringChar(sfx2::cTokenSeparator) + m_sFilterName + + OUStringChar(sfx2::cTokenSeparator) + sSubRegion; + } + + aSection.SetLinkFileName(aLinkFile); + if (!aLinkFile.isEmpty()) + { + aSection.SetType( m_xDDECB->get_active() ? + SectionType::DdeLink : + SectionType::FileLink); + } + } + static_cast<SwInsertSectionTabDialog*>(GetDialogController())->SetSectionData(aSection); + return true; +} + +void SwInsertSectionTabPage::Reset( const SfxItemSet* ) +{ +} + +std::unique_ptr<SfxTabPage> SwInsertSectionTabPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwInsertSectionTabPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK(SwInsertSectionTabPage, ChangeHideHdl, weld::ToggleButton&, rBox, void) +{ + bool bHide = rBox.get_active(); + m_xConditionED->set_sensitive(bHide); + m_xConditionFT->set_sensitive(bHide); +} + +IMPL_LINK(SwInsertSectionTabPage, ChangeProtectHdl, weld::ToggleButton&, rBox, void) +{ + bool bCheck = rBox.get_active(); + m_xPasswdCB->set_sensitive(bCheck); + m_xPasswdPB->set_sensitive(bCheck); +} + +void SwInsertSectionTabPage::ChangePasswd(bool bChange) +{ + bool bSet = bChange ? bChange : m_xPasswdCB->get_active(); + if (bSet) + { + if(!m_aNewPasswd.hasElements() || bChange) + { + SfxPasswordDialog aPasswdDlg(GetFrameWeld()); + aPasswdDlg.ShowExtras(SfxShowExtras::CONFIRM); + if (RET_OK == aPasswdDlg.run()) + { + const OUString sNewPasswd(aPasswdDlg.GetPassword()); + if (aPasswdDlg.GetConfirm() == sNewPasswd) + { + SvPasswordHelper::GetHashPassword( m_aNewPasswd, sNewPasswd ); + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_WRONG_PASSWD_REPEAT))); + xInfoBox->run(); + } + } + else if(!bChange) + m_xPasswdCB->set_active(false); + } + } + else + m_aNewPasswd.realloc(0); +} + +IMPL_LINK_NOARG(SwInsertSectionTabPage, TogglePasswdHdl, weld::ToggleButton&, void) +{ + ChangePasswd(false); +} + +IMPL_LINK_NOARG(SwInsertSectionTabPage, ChangePasswdHdl, weld::Button&, void) +{ + ChangePasswd(true); +} + + +IMPL_LINK_NOARG(SwInsertSectionTabPage, NameEditHdl, weld::ComboBox&, void) +{ + const OUString aName = m_xCurName->get_active_text(); + GetDialogController()->GetOKButton().set_sensitive(!aName.isEmpty() && + m_xCurName->find_text(aName) == -1); +} + +IMPL_LINK(SwInsertSectionTabPage, UseFileHdl, weld::ToggleButton&, rButton, void) +{ + if (rButton.get_active()) + { + if (m_pWrtSh->HasSelection()) + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SwResId(STR_QUERY_CONNECT))); + if (RET_NO == xQueryBox->run()) + rButton.set_active(false); + } + } + + bool bFile = rButton.get_active(); + m_xFileNameFT->set_sensitive(bFile); + m_xFileNameED->set_sensitive(bFile); + m_xFilePB->set_sensitive(bFile); + m_xSubRegionFT->set_sensitive(bFile); + m_xSubRegionED->set_sensitive(bFile); + m_xDDECommandFT->set_sensitive(bFile); + m_xDDECB->set_sensitive(bFile); + if (bFile) + { + m_xFileNameED->grab_focus(); + m_xProtectCB->set_active(true); + ChangeProtectHdl(*m_xProtectCB); + } + else + { + m_xDDECB->set_active(false); + DDEHdl(*m_xDDECB); + } +} + +IMPL_LINK_NOARG(SwInsertSectionTabPage, FileSearchHdl, weld::Button&, void) +{ + m_pDocInserter.reset(new ::sfx2::DocumentInserter(GetFrameWeld(), "swriter")); + m_pDocInserter->StartExecuteModal( LINK( this, SwInsertSectionTabPage, DlgClosedHdl ) ); +} + +IMPL_LINK( SwInsertSectionTabPage, DDEHdl, weld::ToggleButton&, rButton, void ) +{ + bool bDDE = rButton.get_active(); + bool bFile = m_xFileCB->get_active(); + m_xFilePB->set_sensitive(!bDDE && bFile); + if (bDDE) + { + m_xFileNameFT->hide(); + m_xDDECommandFT->set_sensitive(bDDE); + m_xDDECommandFT->show(); + m_xSubRegionFT->hide(); + m_xSubRegionED->hide(); + m_xFileNameED->set_accessible_name(m_xDDECommandFT->get_label()); + } + else + { + m_xDDECommandFT->hide(); + m_xFileNameFT->set_sensitive(bFile); + m_xFileNameFT->show(); + m_xSubRegionFT->show(); + m_xSubRegionED->show(); + m_xSubRegionED->set_sensitive(bFile); + m_xFileNameED->set_accessible_name(m_xFileNameFT->get_label()); + } +} + +IMPL_LINK( SwInsertSectionTabPage, DlgClosedHdl, sfx2::FileDialogHelper *, _pFileDlg, void ) +{ + if ( _pFileDlg->GetError() == ERRCODE_NONE ) + { + std::unique_ptr<SfxMedium> pMedium(m_pDocInserter->CreateMedium("sglobal")); + if ( pMedium ) + { + m_sFileName = pMedium->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ); + m_sFilterName = pMedium->GetFilter()->GetFilterName(); + const SfxPoolItem* pItem; + if ( SfxItemState::SET == pMedium->GetItemSet()->GetItemState( SID_PASSWORD, false, &pItem ) ) + m_sFilePasswd = static_cast<const SfxStringItem*>(pItem)->GetValue(); + m_xFileNameED->set_text( INetURLObject::decode( + m_sFileName, INetURLObject::DecodeMechanism::Unambiguous ) ); + ::lcl_ReadSections(*pMedium, *m_xSubRegionED); + } + } + else + { + m_sFilterName.clear(); + m_sFilePasswd.clear(); + } +} + +SwSectionFootnoteEndTabPage::SwSectionFootnoteEndTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/footnotesendnotestabpage.ui", "FootnotesEndnotesTabPage", &rAttrSet) + , m_xFootnoteNtAtTextEndCB(m_xBuilder->weld_check_button("ftnntattextend")) + , m_xFootnoteNtNumCB(m_xBuilder->weld_check_button("ftnntnum")) + , m_xFootnoteOffsetLbl(m_xBuilder->weld_label("ftnoffset_label")) + , m_xFootnoteOffsetField(m_xBuilder->weld_spin_button("ftnoffset")) + , m_xFootnoteNtNumFormatCB(m_xBuilder->weld_check_button("ftnntnumfmt")) + , m_xFootnotePrefixFT(m_xBuilder->weld_label("ftnprefix_label")) + , m_xFootnotePrefixED(m_xBuilder->weld_entry("ftnprefix")) + , m_xFootnoteNumViewBox(new SwNumberingTypeListBox(m_xBuilder->weld_combo_box("ftnnumviewbox"))) + , m_xFootnoteSuffixFT(m_xBuilder->weld_label("ftnsuffix_label")) + , m_xFootnoteSuffixED(m_xBuilder->weld_entry("ftnsuffix")) + , m_xEndNtAtTextEndCB(m_xBuilder->weld_check_button("endntattextend")) + , m_xEndNtNumCB(m_xBuilder->weld_check_button("endntnum")) + , m_xEndOffsetLbl(m_xBuilder->weld_label("endoffset_label")) + , m_xEndOffsetField(m_xBuilder->weld_spin_button("endoffset")) + , m_xEndNtNumFormatCB(m_xBuilder->weld_check_button("endntnumfmt")) + , m_xEndPrefixFT(m_xBuilder->weld_label("endprefix_label")) + , m_xEndPrefixED(m_xBuilder->weld_entry("endprefix")) + , m_xEndNumViewBox(new SwNumberingTypeListBox(m_xBuilder->weld_combo_box("endnumviewbox"))) + , m_xEndSuffixFT(m_xBuilder->weld_label("endsuffix_label")) + , m_xEndSuffixED(m_xBuilder->weld_entry("endsuffix")) +{ + m_xFootnoteNumViewBox->Reload(SwInsertNumTypes::Extended); + m_xEndNumViewBox->Reload(SwInsertNumTypes::Extended); + + Link<weld::ToggleButton&,void> aLk( LINK( this, SwSectionFootnoteEndTabPage, FootEndHdl)); + m_xFootnoteNtAtTextEndCB->connect_toggled( aLk ); + m_xFootnoteNtNumCB->connect_toggled( aLk ); + m_xEndNtAtTextEndCB->connect_toggled( aLk ); + m_xEndNtNumCB->connect_toggled( aLk ); + m_xFootnoteNtNumFormatCB->connect_toggled( aLk ); + m_xEndNtNumFormatCB->connect_toggled( aLk ); +} + +SwSectionFootnoteEndTabPage::~SwSectionFootnoteEndTabPage() +{ +} + +bool SwSectionFootnoteEndTabPage::FillItemSet( SfxItemSet* rSet ) +{ + SwFormatFootnoteAtTextEnd aFootnote( m_xFootnoteNtAtTextEndCB->get_active() + ? ( m_xFootnoteNtNumCB->get_active() + ? ( m_xFootnoteNtNumFormatCB->get_active() + ? FTNEND_ATTXTEND_OWNNUMANDFMT + : FTNEND_ATTXTEND_OWNNUMSEQ ) + : FTNEND_ATTXTEND ) + : FTNEND_ATPGORDOCEND ); + + switch( aFootnote.GetValue() ) + { + case FTNEND_ATTXTEND_OWNNUMANDFMT: + aFootnote.SetNumType( m_xFootnoteNumViewBox->GetSelectedNumberingType() ); + aFootnote.SetPrefix( m_xFootnotePrefixED->get_text().replaceAll("\\t", "\t") ); // fdo#65666 + aFootnote.SetSuffix( m_xFootnoteSuffixED->get_text().replaceAll("\\t", "\t") ); + [[fallthrough]]; + + case FTNEND_ATTXTEND_OWNNUMSEQ: + aFootnote.SetOffset( static_cast< sal_uInt16 >( m_xFootnoteOffsetField->get_value()-1 ) ); + break; + default: break; + } + + SwFormatEndAtTextEnd aEnd( m_xEndNtAtTextEndCB->get_active() + ? ( m_xEndNtNumCB->get_active() + ? ( m_xEndNtNumFormatCB->get_active() + ? FTNEND_ATTXTEND_OWNNUMANDFMT + : FTNEND_ATTXTEND_OWNNUMSEQ ) + : FTNEND_ATTXTEND ) + : FTNEND_ATPGORDOCEND ); + + switch( aEnd.GetValue() ) + { + case FTNEND_ATTXTEND_OWNNUMANDFMT: + aEnd.SetNumType( m_xEndNumViewBox->GetSelectedNumberingType() ); + aEnd.SetPrefix( m_xEndPrefixED->get_text().replaceAll("\\t", "\t") ); + aEnd.SetSuffix( m_xEndSuffixED->get_text().replaceAll("\\t", "\t") ); + [[fallthrough]]; + + case FTNEND_ATTXTEND_OWNNUMSEQ: + aEnd.SetOffset( static_cast< sal_uInt16 >( m_xEndOffsetField->get_value()-1 ) ); + break; + default: break; + } + + rSet->Put( aFootnote ); + rSet->Put( aEnd ); + + return true; +} + +void SwSectionFootnoteEndTabPage::ResetState( bool bFootnote, + const SwFormatFootnoteEndAtTextEnd& rAttr ) +{ + weld::CheckButton *pNtAtTextEndCB, *pNtNumCB, *pNtNumFormatCB; + weld::Label *pPrefixFT, *pSuffixFT; + weld::Entry *pPrefixED, *pSuffixED; + SwNumberingTypeListBox *pNumViewBox; + weld::Label *pOffsetText; + weld::SpinButton *pOffsetField; + + if( bFootnote ) + { + pNtAtTextEndCB = m_xFootnoteNtAtTextEndCB.get(); + pNtNumCB = m_xFootnoteNtNumCB.get(); + pNtNumFormatCB = m_xFootnoteNtNumFormatCB.get(); + pPrefixFT = m_xFootnotePrefixFT.get(); + pPrefixED = m_xFootnotePrefixED.get(); + pSuffixFT = m_xFootnoteSuffixFT.get(); + pSuffixED = m_xFootnoteSuffixED.get(); + pNumViewBox = m_xFootnoteNumViewBox.get(); + pOffsetText = m_xFootnoteOffsetLbl.get(); + pOffsetField = m_xFootnoteOffsetField.get(); + } + else + { + pNtAtTextEndCB = m_xEndNtAtTextEndCB.get(); + pNtNumCB = m_xEndNtNumCB.get(); + pNtNumFormatCB = m_xEndNtNumFormatCB.get(); + pPrefixFT = m_xEndPrefixFT.get(); + pPrefixED = m_xEndPrefixED.get(); + pSuffixFT = m_xEndSuffixFT.get(); + pSuffixED = m_xEndSuffixED.get(); + pNumViewBox = m_xEndNumViewBox.get(); + pOffsetText = m_xEndOffsetLbl.get(); + pOffsetField = m_xEndOffsetField.get(); + } + + const sal_uInt16 eState = rAttr.GetValue(); + switch( eState ) + { + case FTNEND_ATTXTEND_OWNNUMANDFMT: + pNtNumFormatCB->set_state( TRISTATE_TRUE ); + [[fallthrough]]; + + case FTNEND_ATTXTEND_OWNNUMSEQ: + pNtNumCB->set_state( TRISTATE_TRUE ); + [[fallthrough]]; + + case FTNEND_ATTXTEND: + pNtAtTextEndCB->set_state( TRISTATE_TRUE ); + // no break; + } + + pNumViewBox->SelectNumberingType( rAttr.GetNumType() ); + pOffsetField->set_value( rAttr.GetOffset() + 1 ); + pPrefixED->set_text( rAttr.GetPrefix().replaceAll("\t", "\\t") ); + pSuffixED->set_text( rAttr.GetSuffix().replaceAll("\t", "\\t") ); + + switch( eState ) + { + case FTNEND_ATPGORDOCEND: + pNtNumCB->set_sensitive( false ); + [[fallthrough]]; + + case FTNEND_ATTXTEND: + pNtNumFormatCB->set_sensitive( false ); + pOffsetField->set_sensitive( false ); + pOffsetText->set_sensitive( false ); + [[fallthrough]]; + + case FTNEND_ATTXTEND_OWNNUMSEQ: + pNumViewBox->set_sensitive( false ); + pPrefixFT->set_sensitive( false ); + pPrefixED->set_sensitive( false ); + pSuffixFT->set_sensitive( false ); + pSuffixED->set_sensitive( false ); + // no break; + } +} + +void SwSectionFootnoteEndTabPage::Reset( const SfxItemSet* rSet ) +{ + ResetState( true, rSet->Get( RES_FTN_AT_TXTEND, false )); + ResetState( false, rSet->Get( RES_END_AT_TXTEND, false )); +} + +std::unique_ptr<SfxTabPage> SwSectionFootnoteEndTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwSectionFootnoteEndTabPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK( SwSectionFootnoteEndTabPage, FootEndHdl, weld::ToggleButton&, rBox, void ) +{ + bool bFoot = m_xFootnoteNtAtTextEndCB.get() == &rBox || m_xFootnoteNtNumCB.get() == &rBox || + m_xFootnoteNtNumFormatCB.get() == &rBox ; + + weld::CheckButton *pNumBox, *pNumFormatBox, *pEndBox; + SwNumberingTypeListBox* pNumViewBox; + weld::Label *pOffsetText; + weld::SpinButton *pOffsetField; + weld::Label *pPrefixFT, *pSuffixFT; + weld::Entry *pPrefixED, *pSuffixED; + + if( bFoot ) + { + pEndBox = m_xFootnoteNtAtTextEndCB.get(); + pNumBox = m_xFootnoteNtNumCB.get(); + pNumFormatBox = m_xFootnoteNtNumFormatCB.get(); + pNumViewBox = m_xFootnoteNumViewBox.get(); + pOffsetText = m_xFootnoteOffsetLbl.get(); + pOffsetField = m_xFootnoteOffsetField.get(); + pPrefixFT = m_xFootnotePrefixFT.get(); + pSuffixFT = m_xFootnoteSuffixFT.get(); + pPrefixED = m_xFootnotePrefixED.get(); + pSuffixED = m_xFootnoteSuffixED.get(); + } + else + { + pEndBox = m_xEndNtAtTextEndCB.get(); + pNumBox = m_xEndNtNumCB.get(); + pNumFormatBox = m_xEndNtNumFormatCB.get(); + pNumViewBox = m_xEndNumViewBox.get(); + pOffsetText = m_xEndOffsetLbl.get(); + pOffsetField = m_xEndOffsetField.get(); + pPrefixFT = m_xEndPrefixFT.get(); + pSuffixFT = m_xEndSuffixFT.get(); + pPrefixED = m_xEndPrefixED.get(); + pSuffixED = m_xEndSuffixED.get(); + } + + bool bEnableAtEnd = TRISTATE_TRUE == pEndBox->get_state(); + bool bEnableNum = bEnableAtEnd && TRISTATE_TRUE == pNumBox->get_state(); + bool bEnableNumFormat = bEnableNum && TRISTATE_TRUE == pNumFormatBox->get_state(); + + pNumBox->set_sensitive( bEnableAtEnd ); + pOffsetText->set_sensitive( bEnableNum ); + pOffsetField->set_sensitive( bEnableNum ); + pNumFormatBox->set_sensitive( bEnableNum ); + pNumViewBox->set_sensitive( bEnableNumFormat ); + pPrefixED->set_sensitive( bEnableNumFormat ); + pSuffixED->set_sensitive( bEnableNumFormat ); + pPrefixFT->set_sensitive( bEnableNumFormat ); + pSuffixFT->set_sensitive( bEnableNumFormat ); +} + +SwSectionPropertyTabDialog::SwSectionPropertyTabDialog( + weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell& rSh) + : SfxTabDialogController(pParent, "modules/swriter/ui/formatsectiondialog.ui", + "FormatSectionDialog", &rSet) + , rWrtSh(rSh) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + AddTabPage("columns", SwColumnPage::Create, nullptr); + AddTabPage("background", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); + AddTabPage("notes", SwSectionFootnoteEndTabPage::Create, nullptr); + AddTabPage("indents", SwSectionIndentTabPage::Create, nullptr); + + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + long nHtmlMode = rHtmlOpt.GetExportMode(); + bool bWeb = dynamic_cast<SwWebDocShell*>( rSh.GetView().GetDocShell() ) != nullptr ; + if(bWeb) + { + RemoveTabPage("notes"); + RemoveTabPage("indents"); + if( HTML_CFG_NS40 != nHtmlMode && HTML_CFG_WRITER != nHtmlMode) + RemoveTabPage("columns"); + } +} + +SwSectionPropertyTabDialog::~SwSectionPropertyTabDialog() +{ +} + +void SwSectionPropertyTabDialog::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (rId == "background") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_SELECTOR))); + rPage.PageCreated(aSet); + } + else if (rId == "columns") + { + static_cast<SwColumnPage&>(rPage).ShowBalance(true); + static_cast<SwColumnPage&>(rPage).SetInSection(true); + } + else if (rId == "indents") + static_cast<SwSectionIndentTabPage&>(rPage).SetWrtShell(rWrtSh); +} + +SwSectionIndentTabPage::SwSectionIndentTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/indentpage.ui", "IndentPage", &rAttrSet) + , m_xBeforeMF(m_xBuilder->weld_metric_spin_button("before", FieldUnit::CM)) + , m_xAfterMF(m_xBuilder->weld_metric_spin_button("after", FieldUnit::CM)) + , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWin)) +{ + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwSectionIndentTabPage, IndentModifyHdl); + m_xBeforeMF->connect_value_changed(aLk); + m_xAfterMF->connect_value_changed(aLk); +} + +SwSectionIndentTabPage::~SwSectionIndentTabPage() +{ +} + +bool SwSectionIndentTabPage::FillItemSet(SfxItemSet* rSet) +{ + if (m_xBeforeMF->get_value_changed_from_saved() || m_xAfterMF->get_value_changed_from_saved()) + { + SvxLRSpaceItem aLRSpace( + m_xBeforeMF->denormalize(m_xBeforeMF->get_value(FieldUnit::TWIP)) , + m_xAfterMF->denormalize(m_xAfterMF->get_value(FieldUnit::TWIP)), 0, 0, RES_LR_SPACE); + rSet->Put(aLRSpace); + } + return true; +} + +void SwSectionIndentTabPage::Reset( const SfxItemSet* rSet) +{ + //this page doesn't show up in HTML mode + FieldUnit aMetric = ::GetDfltMetric(false); + SetFieldUnit(*m_xBeforeMF, aMetric); + SetFieldUnit(*m_xAfterMF , aMetric); + + SfxItemState eItemState = rSet->GetItemState( RES_LR_SPACE ); + if ( eItemState >= SfxItemState::DEFAULT ) + { + const SvxLRSpaceItem& rSpace = + rSet->Get( RES_LR_SPACE ); + + m_xBeforeMF->set_value(m_xBeforeMF->normalize(rSpace.GetLeft()), FieldUnit::TWIP); + m_xAfterMF->set_value(m_xAfterMF->normalize(rSpace.GetRight()), FieldUnit::TWIP); + } + else + { + m_xBeforeMF->set_text(""); + m_xAfterMF->set_text(""); + } + m_xBeforeMF->save_value(); + m_xAfterMF->save_value(); + IndentModifyHdl(*m_xBeforeMF); +} + +std::unique_ptr<SfxTabPage> SwSectionIndentTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwSectionIndentTabPage>(pPage, pController, *rAttrSet); +} + +void SwSectionIndentTabPage::SetWrtShell(SwWrtShell const & rSh) +{ + //set sensible values at the preview + m_aPreviewWin.SetAdjust(SvxAdjust::Block); + m_aPreviewWin.SetLastLine(SvxAdjust::Block); + const SwRect& rPageRect = rSh.GetAnyCurRect( CurRectType::Page ); + Size aPageSize(rPageRect.Width(), rPageRect.Height()); + m_aPreviewWin.SetSize(aPageSize); +} + +IMPL_LINK_NOARG(SwSectionIndentTabPage, IndentModifyHdl, weld::MetricSpinButton&, void) +{ + m_aPreviewWin.SetLeftMargin(m_xBeforeMF->denormalize(m_xBeforeMF->get_value(FieldUnit::TWIP))); + m_aPreviewWin.SetRightMargin(m_xAfterMF->denormalize(m_xAfterMF->get_value(FieldUnit::TWIP))); + m_aPreviewWin.Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dialog/wordcountdialog.cxx b/sw/source/ui/dialog/wordcountdialog.cxx new file mode 100644 index 000000000..ba159d4e6 --- /dev/null +++ b/sw/source/ui/dialog/wordcountdialog.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 <officecfg/Office/Writer.hxx> +#include <wordcountdialog.hxx> +#include <docstat.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <swwait.hxx> +#include <wrtsh.hxx> +#include <rtl/math.hxx> +#include <svl/cjkoptions.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#define IS_MOBILE_PHONE (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone()) + +SwWordCountFloatDlg::~SwWordCountFloatDlg() +{ + SwViewShell::SetCareDialog(nullptr); +} + +namespace +{ + void setValue(weld::Label& rWidget, sal_uLong nValue, const LocaleDataWrapper& rLocaleData) + { + rWidget.set_label(rLocaleData.getNum(nValue, 0)); + } + + void setDoubleValue(weld::Label& rWidget, double fValue) + { + OUString sValue(OUString::number(::rtl::math::round(fValue, 1))); + rWidget.set_label(sValue); + } +} + +void SwWordCountFloatDlg::SetValues(const SwDocStat& rCurrent, const SwDocStat& rDoc) +{ + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetUILocaleDataWrapper(); + setValue(*m_xCurrentWordFT, rCurrent.nWord, rLocaleData); + setValue(*m_xCurrentCharacterFT, rCurrent.nChar, rLocaleData); + setValue(*m_xCurrentCharacterExcludingSpacesFT, rCurrent.nCharExcludingSpaces, rLocaleData); + setValue(*m_xCurrentCjkcharsFT, rCurrent.nAsianWord, rLocaleData); + setValue(*m_xDocWordFT, rDoc.nWord, rLocaleData); + setValue(*m_xDocCharacterFT, rDoc.nChar, rLocaleData); + setValue(*m_xDocCharacterExcludingSpacesFT, rDoc.nCharExcludingSpaces, rLocaleData); + setValue(*m_xDocCjkcharsFT, rDoc.nAsianWord, rLocaleData); + + if (m_xStandardizedPagesLabelFT->get_visible()) + { + sal_Int64 nCharsPerStandardizedPage = officecfg::Office::Writer::WordCount::StandardizedPageSize::get(); + setDoubleValue(*m_xCurrentStandardizedPagesFT, + static_cast<double>(rCurrent.nChar) / nCharsPerStandardizedPage); + setDoubleValue(*m_xDocStandardizedPagesFT, + static_cast<double>(rDoc.nChar) / nCharsPerStandardizedPage); + } + + bool bShowCJK = (SvtCJKOptions().IsAnyEnabled() || rDoc.nAsianWord); + bool bToggleCJK = m_xCurrentCjkcharsFT->get_visible() != bShowCJK; + if (bToggleCJK) + { + showCJK(bShowCJK); + m_xDialog->resize_to_request(); //force resize of dialog + } +} + +void SwWordCountFloatDlg::showCJK(bool bShowCJK) +{ + m_xCurrentCjkcharsFT->set_visible(bShowCJK); + m_xDocCjkcharsFT->set_visible(bShowCJK); + if (m_xCjkcharsLabelFT2) + m_xCjkcharsLabelFT2->set_visible(bShowCJK); + m_xCjkcharsLabelFT->set_visible(bShowCJK); +} + +void SwWordCountFloatDlg::showStandardizedPages(bool bShowStandardizedPages) +{ + m_xCurrentStandardizedPagesFT->set_visible(bShowStandardizedPages); + m_xDocStandardizedPagesFT->set_visible(bShowStandardizedPages); + if (m_xStandardizedPagesLabelFT2) + m_xStandardizedPagesLabelFT2->set_visible(bShowStandardizedPages); + m_xStandardizedPagesLabelFT->set_visible(bShowStandardizedPages); +} + +SwWordCountFloatDlg::SwWordCountFloatDlg(SfxBindings* _pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo const * pInfo) + : SfxModelessDialogController(_pBindings, pChild, pParent, IS_MOBILE_PHONE ? OUString("modules/swriter/ui/wordcount-mobile.ui") : OUString("modules/swriter/ui/wordcount.ui"), "WordCountDialog") + , m_xCurrentWordFT(m_xBuilder->weld_label("selectwords")) + , m_xCurrentCharacterFT(m_xBuilder->weld_label("selectchars")) + , m_xCurrentCharacterExcludingSpacesFT(m_xBuilder->weld_label("selectcharsnospaces")) + , m_xCurrentCjkcharsFT(m_xBuilder->weld_label("selectcjkchars")) + , m_xCurrentStandardizedPagesFT(m_xBuilder->weld_label("selectstandardizedpages")) + , m_xDocWordFT(m_xBuilder->weld_label("docwords")) + , m_xDocCharacterFT(m_xBuilder->weld_label("docchars")) + , m_xDocCharacterExcludingSpacesFT(m_xBuilder->weld_label("doccharsnospaces")) + , m_xDocCjkcharsFT(m_xBuilder->weld_label("doccjkchars")) + , m_xDocStandardizedPagesFT(m_xBuilder->weld_label("docstandardizedpages")) + , m_xCjkcharsLabelFT(m_xBuilder->weld_label("cjkcharsft")) + , m_xCjkcharsLabelFT2(m_xBuilder->weld_label("cjkcharsft2")) + , m_xStandardizedPagesLabelFT(m_xBuilder->weld_label("standardizedpages")) + , m_xStandardizedPagesLabelFT2(m_xBuilder->weld_label("standardizedpages2")) +{ + showCJK(SvtCJKOptions().IsAnyEnabled()); + showStandardizedPages(officecfg::Office::Writer::WordCount::ShowStandardizedPageCount::get()); + + Initialize(pInfo); +} + +void SwWordCountFloatDlg::UpdateCounts() +{ + SwWrtShell &rSh = ::GetActiveView()->GetWrtShell(); + SwDocStat aCurrCnt; + SwDocStat aDocStat; + { + SwWait aWait( *::GetActiveView()->GetDocShell(), true ); + rSh.StartAction(); + rSh.CountWords( aCurrCnt ); + aDocStat = rSh.GetUpdatedDocStat(); + rSh.EndAction(); + } + SetValues(aCurrCnt, aDocStat); +} + +void SwWordCountFloatDlg::SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat) +{ + SetValues(rCurrCnt, rDocStat); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/dochdl/selglos.cxx b/sw/source/ui/dochdl/selglos.cxx new file mode 100644 index 000000000..46acd407f --- /dev/null +++ b/sw/source/ui/dochdl/selglos.cxx @@ -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 . + */ + +#include <selglos.hxx> + +SwSelGlossaryDlg::SwSelGlossaryDlg(weld::Window * pParent, const OUString &rShortName) + : GenericDialogController(pParent, "modules/swriter/ui/insertautotextdialog.ui", "InsertAutoTextDialog") + , m_xFrame(m_xBuilder->weld_frame("frame")) + , m_xGlosBox(m_xBuilder->weld_tree_view("treeview")) +{ + m_xFrame->set_label(m_xFrame->get_label() + rShortName); + m_xGlosBox->set_size_request(-1, m_xGlosBox->get_height_rows(10)); + m_xGlosBox->connect_row_activated(LINK(this, SwSelGlossaryDlg, DoubleClickHdl)); +} + +SwSelGlossaryDlg::~SwSelGlossaryDlg() +{ +} + +IMPL_LINK_NOARG(SwSelGlossaryDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/envfmt.cxx b/sw/source/ui/envelp/envfmt.cxx new file mode 100644 index 000000000..9420a3b5f --- /dev/null +++ b/sw/source/ui/envelp/envfmt.cxx @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/paperinf.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/brushitem.hxx> +#include <svtools/unitconv.hxx> + +#include <cmdid.h> +#include <wrtsh.hxx> +#include <view.hxx> +#include "envfmt.hxx" +#include <fmtcol.hxx> +#include <swuipardlg.hxx> +#include <chrdlgmodes.hxx> +#include <pardlg.hxx> +#include <poolfmt.hxx> +#include <uitool.hxx> + +#include <vector> +#include <algorithm> + +#include <memory> + +#include <swabstdlg.hxx> +#include <swuiexp.hxx> + +namespace { + /// Converts a ranges array to a list containing one entry for each + /// element covered by the ranges. + /// @param aRanges An array containing zero or more range specifications and + /// terminated by one or more zero entries. A range + /// specification is two consecutive entries that specify + /// the start and end points of the range. + /// @returns A vector containing one element for each item covered by the + /// ranges. This is not guaranteed to be sorted and may contain + /// duplicates if the original ranges contained overlaps. + std::vector<sal_uInt16> lcl_convertRangesToList(const sal_uInt16 aRanges[]) { + std::vector<sal_uInt16> aVec; + int i = 0; + while (aRanges[i]) + { + for (sal_uInt16 n = aRanges[i]; n <= aRanges[i+1]; ++n) + { + aVec.push_back(n); + } + i += 2; + } + return aVec; + } + + /// Converts a list of elements to a ranges array. + /// @param rElements Vector of the initial elements, this need not be sorted, + /// and may contain duplicate items. The vector is sorted + /// on exit from this function but may still contain duplicates. + /// @returns An array containing zero or more range specifications and + /// terminated by one or more zero entries. A range specification + /// is two consecutive entries that specify the start and end + /// points of the range. This list will be sorted and will not + /// contain any overlapping ranges. + sal_uInt16* lcl_convertListToRanges(std::vector<sal_uInt16> &rElements) { + std::sort(rElements.begin(), rElements.end()); + std::vector<sal_uInt16> aRanges; + size_t i; + for (i = 0; i < rElements.size(); ++i) + { + //Push the start of the this range. + aRanges.push_back(rElements[i]); + //Seek to the end of this range. + while (i + 1 < rElements.size() && rElements[i+1] - rElements[i] <= 1) + { + ++i; + } + //Push the end of this range (may be the same as the start). + aRanges.push_back( rElements[i] ); + } + + // Convert the vector to an array with terminating zero + sal_uInt16 *pNewRanges = new sal_uInt16[aRanges.size() + 1]; + for (i = 0; i < aRanges.size(); ++i) + { + pNewRanges[i] = aRanges[i]; + } + pNewRanges[i] = 0; + return pNewRanges; + } + +} + +static long lUserW = 5669; // 10 cm +static long lUserH = 5669; // 10 cm + +SwEnvFormatPage::SwEnvFormatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/envformatpage.ui", "EnvFormatPage", &rSet) + , m_pDialog(nullptr) + , m_xAddrLeftField(m_xBuilder->weld_metric_spin_button("leftaddr", FieldUnit::CM)) + , m_xAddrTopField(m_xBuilder->weld_metric_spin_button("topaddr", FieldUnit::CM)) + , m_xAddrEditButton(m_xBuilder->weld_menu_button("addredit")) + , m_xSendLeftField(m_xBuilder->weld_metric_spin_button("leftsender", FieldUnit::CM)) + , m_xSendTopField(m_xBuilder->weld_metric_spin_button("topsender", FieldUnit::CM)) + , m_xSendEditButton(m_xBuilder->weld_menu_button("senderedit")) + , m_xSizeFormatBox(m_xBuilder->weld_combo_box("format")) + , m_xSizeWidthField(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM)) + , m_xSizeHeightField(m_xBuilder->weld_metric_spin_button("height", FieldUnit::CM)) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreview)) +{ + SetExchangeSupport(); + + // Metrics + FieldUnit aMetric = ::GetDfltMetric(false); + ::SetFieldUnit(*m_xAddrLeftField, aMetric); + ::SetFieldUnit(*m_xAddrTopField, aMetric); + ::SetFieldUnit(*m_xSendLeftField, aMetric); + ::SetFieldUnit(*m_xSendTopField, aMetric); + ::SetFieldUnit(*m_xSizeWidthField, aMetric); + ::SetFieldUnit(*m_xSizeHeightField, aMetric); + + // Install handlers + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwEnvFormatPage, ModifyHdl); + m_xAddrLeftField->connect_value_changed( aLk ); + m_xAddrTopField->connect_value_changed( aLk ); + m_xSendLeftField->connect_value_changed( aLk ); + m_xSendTopField->connect_value_changed( aLk ); + m_xSizeWidthField->connect_value_changed( aLk ); + m_xSizeHeightField->connect_value_changed( aLk ); + + m_xAddrEditButton->connect_selected(LINK(this, SwEnvFormatPage, AddrEditHdl)); + m_xSendEditButton->connect_selected(LINK(this, SwEnvFormatPage, SendEditHdl)); + + m_xSizeFormatBox->connect_changed(LINK(this, SwEnvFormatPage, FormatHdl)); + + // m_xSizeFormatBox + for (sal_uInt16 i = PAPER_A3; i <= PAPER_KAI32BIG; i++) + { + if (i != PAPER_USER) + { + const OUString aPaperName = SvxPaperInfo::GetName(static_cast<Paper>(i)); + + if (aPaperName.isEmpty()) + continue; + + sal_Int32 nPos = 0; + while (nPos < m_xSizeFormatBox->get_count() && + m_xSizeFormatBox->get_text(nPos) < aPaperName) + { + ++nPos; + } + m_xSizeFormatBox->insert_text(nPos, aPaperName); + m_aIDs.insert( m_aIDs.begin() + nPos, i); + } + } + m_xSizeFormatBox->append_text(SvxPaperInfo::GetName(PAPER_USER)); + m_aIDs.push_back( sal_uInt16(PAPER_USER) ); +} + +void SwEnvFormatPage::Init(SwEnvDlg* pDialog) +{ + m_pDialog = pDialog; + m_aPreview.SetDialog(m_pDialog); +} + +SwEnvFormatPage::~SwEnvFormatPage() +{ +} + +IMPL_LINK( SwEnvFormatPage, ModifyHdl, weld::MetricSpinButton&, rEdit, void ) +{ + int lWVal = getfieldval(*m_xSizeWidthField); + int lHVal = getfieldval(*m_xSizeHeightField); + + int lWidth = std::max(lWVal, lHVal); + int lHeight = std::min(lWVal, lHVal); + + if (&rEdit == m_xSizeWidthField.get() || &rEdit == m_xSizeHeightField.get()) + { + int nRotatedWidth = lHeight; + int nRotatedHeight = lWidth; + Paper ePaper = SvxPaperInfo::GetSvxPaper( + Size(nRotatedWidth, nRotatedHeight), MapUnit::MapTwip); + for (size_t i = 0; i < m_aIDs.size(); ++i) + if (m_aIDs[i] == static_cast<sal_uInt16>(ePaper)) + m_xSizeFormatBox->set_active(i); + + // remember user size + if (m_aIDs[m_xSizeFormatBox->get_active()] == sal_uInt16(PAPER_USER)) + { + lUserW = lWidth ; + lUserH = lHeight; + } + + FormatHdl(*m_xSizeFormatBox); + } + else + { + FillItem(GetParentSwEnvDlg()->aEnvItem); + SetMinMax(); + m_xPreview->queue_draw(); + } +} + +IMPL_LINK(SwEnvFormatPage, AddrEditHdl, const OString&, rIdent, void) +{ + Edit(rIdent, false); +} + +IMPL_LINK(SwEnvFormatPage, SendEditHdl, const OString&, rIdent, void) +{ + Edit(rIdent, true); +} + +void SwEnvFormatPage::Edit(const OString& rIdent, bool bSender) +{ + SwWrtShell* pSh = GetParentSwEnvDlg()->pSh; + OSL_ENSURE(pSh, "Shell missing"); + + SwTextFormatColl* pColl = pSh->GetTextCollFromPool( static_cast< sal_uInt16 >( + bSender ? RES_POOLCOLL_SENDADRESS : RES_POOLCOLL_JAKETADRESS)); + OSL_ENSURE(pColl, "Text collection missing"); + + if (rIdent.startsWith("character")) + { + SfxItemSet *pCollSet = GetCollItemSet(pColl, bSender); + + // In order for the background color not to get ironed over: + SfxAllItemSet aTmpSet(*pCollSet); + ::ConvertAttrCharToGen(aTmpSet); + + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + const OUString sFormatStr = pColl->GetName(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(rFact.CreateSwCharDlg(GetFrameWeld(), pSh->GetView(), aTmpSet, SwCharDlgMode::Env, &sFormatStr)); + if (pDlg->Execute() == RET_OK) + { + SfxItemSet aOutputSet( *pDlg->GetOutputItemSet() ); + ::ConvertAttrGenToChar(aOutputSet, aTmpSet); + pCollSet->Put(aOutputSet); + } + } + else if (rIdent.startsWith("paragraph")) + { + SfxItemSet *pCollSet = GetCollItemSet(pColl, bSender); + + // In order for the tabulators not to get ironed over: + SfxAllItemSet aTmpSet(*pCollSet); + + // Insert tabs, default tabs into ItemSet + const SvxTabStopItem& rDefTabs = + pSh->GetView().GetCurShell()->GetPool().GetDefaultItem(RES_PARATR_TABSTOP); + + const sal_uInt16 nDefDist = static_cast<sal_uInt16>(::GetTabDist( rDefTabs )); + SfxUInt16Item aDefDistItem( SID_ATTR_TABSTOP_DEFAULTS, nDefDist ); + aTmpSet.Put( aDefDistItem ); + + // Current tab + SfxUInt16Item aTabPos( SID_ATTR_TABSTOP_POS, 0 ); + aTmpSet.Put( aTabPos ); + + // left border as offset + const long nOff = aTmpSet.Get( RES_LR_SPACE ).GetTextLeft(); + SfxInt32Item aOff( SID_ATTR_TABSTOP_OFFSET, nOff ); + aTmpSet.Put( aOff ); + + // set BoxInfo + ::PrepareBoxInfo( aTmpSet, *pSh ); + + const OUString sFormatStr = pColl->GetName(); + SwParaDlg aDlg(GetFrameWeld(), pSh->GetView(), aTmpSet, DLG_ENVELOP, &sFormatStr); + + if (aDlg.run() == RET_OK) + { + // maybe relocate defaults + const SfxPoolItem* pItem = nullptr; + SfxItemSet* pOutputSet = const_cast<SfxItemSet*>(aDlg.GetOutputItemSet()); + sal_uInt16 nNewDist; + + if( SfxItemState::SET == pOutputSet->GetItemState( SID_ATTR_TABSTOP_DEFAULTS, + false, &pItem ) && + nDefDist != (nNewDist = static_cast<const SfxUInt16Item*>(pItem)->GetValue()) ) + { + SvxTabStopItem aDefTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + MakeDefTabs( nNewDist, aDefTabs ); + pSh->SetDefault( aDefTabs ); + pOutputSet->ClearItem( SID_ATTR_TABSTOP_DEFAULTS ); + } + if( pOutputSet->Count() ) + { + pCollSet->Put(*pOutputSet); + } + } + } +} + +// A temporary Itemset that gets discarded at abort +SfxItemSet *SwEnvFormatPage::GetCollItemSet(SwTextFormatColl const * pColl, bool bSender) +{ + std::unique_ptr<SfxItemSet>& pAddrSet = bSender ? GetParentSwEnvDlg()->pSenderSet : GetParentSwEnvDlg()->pAddresseeSet; + if (!pAddrSet) + { + // determine range (merge both Itemsets' ranges) + const sal_uInt16 *pRanges = pColl->GetAttrSet().GetRanges(); + + static sal_uInt16 const aRanges[] = + { + RES_PARATR_BEGIN, RES_PARATR_ADJUST, + RES_PARATR_TABSTOP, RES_PARATR_END-1, + RES_LR_SPACE, RES_UL_SPACE, + RES_BACKGROUND, RES_SHADOW, + SID_ATTR_TABSTOP_POS, SID_ATTR_TABSTOP_POS, + SID_ATTR_TABSTOP_DEFAULTS, SID_ATTR_TABSTOP_DEFAULTS, + SID_ATTR_TABSTOP_OFFSET, SID_ATTR_TABSTOP_OFFSET, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + 0, 0 + }; + + // BruteForce merge because MergeRange in SvTools is buggy: + std::vector<sal_uInt16> aVec2 = ::lcl_convertRangesToList(pRanges); + std::vector<sal_uInt16> aVec = ::lcl_convertRangesToList(aRanges); + aVec2.insert(aVec2.end(), aVec.begin(), aVec.end()); + std::unique_ptr<sal_uInt16[]> pNewRanges(::lcl_convertListToRanges(aVec2)); + + pAddrSet.reset(new SfxItemSet(GetParentSwEnvDlg()->pSh->GetView().GetCurShell()->GetPool(), + pNewRanges.get())); + pAddrSet->Put(pColl->GetAttrSet()); + } + + return pAddrSet.get(); +} + +IMPL_LINK_NOARG(SwEnvFormatPage, FormatHdl, weld::ComboBox&, void) +{ + long lWidth; + long lHeight; + long lSendFromLeft; + long lSendFromTop; + long lAddrFromLeft; + long lAddrFromTop; + + const sal_uInt16 nPaper = m_aIDs[m_xSizeFormatBox->get_active()]; + if (nPaper != sal_uInt16(PAPER_USER)) + { + Size aSz = SvxPaperInfo::GetPaperSize(static_cast<Paper>(nPaper)); + lWidth = std::max(aSz.Width(), aSz.Height()); + lHeight = std::min(aSz.Width(), aSz.Height()); + } + else + { + lWidth = lUserW; + lHeight = lUserH; + } + + lSendFromLeft = 566; // 1cm + lSendFromTop = 566; // 1cm + lAddrFromLeft = lWidth / 2; + lAddrFromTop = lHeight / 2; + + setfieldval(*m_xAddrLeftField, lAddrFromLeft); + setfieldval(*m_xAddrTopField , lAddrFromTop ); + setfieldval(*m_xSendLeftField, lSendFromLeft); + setfieldval(*m_xSendTopField , lSendFromTop ); + + setfieldval(*m_xSizeWidthField , lWidth ); + setfieldval(*m_xSizeHeightField, lHeight); + + SetMinMax(); + + FillItem(GetParentSwEnvDlg()->aEnvItem); + m_xPreview->queue_draw(); +} + +void SwEnvFormatPage::SetMinMax() +{ + long lWVal = static_cast< long >(getfieldval(*m_xSizeWidthField )); + long lHVal = static_cast< long >(getfieldval(*m_xSizeHeightField)); + + long lWidth = std::max(lWVal, lHVal), + lHeight = std::min(lWVal, lHVal); + + // Min and Max + m_xAddrLeftField->set_range(100 * (getfieldval(*m_xSendLeftField) + 566), + 100 * (lWidth - 2 * 566), FieldUnit::TWIP); + m_xAddrTopField->set_range(100 * (getfieldval(*m_xSendTopField ) + 2 * 566), + 100 * (lHeight - 2 * 566), FieldUnit::TWIP); + m_xSendLeftField->set_range(100 * 566, + 100 * (getfieldval(*m_xAddrLeftField) - 566), FieldUnit::TWIP); + m_xSendTopField->set_range(100 * 566, + 100 * (getfieldval(*m_xAddrTopField ) - 2 * 566), FieldUnit::TWIP); +} + +std::unique_ptr<SfxTabPage> SwEnvFormatPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwEnvFormatPage>(pPage, pController, *rSet); +} + +void SwEnvFormatPage::ActivatePage(const SfxItemSet& rSet) +{ + SfxItemSet aSet(rSet); + aSet.Put(GetParentSwEnvDlg()->aEnvItem); + Reset(&aSet); +} + +DeactivateRC SwEnvFormatPage::DeactivatePage(SfxItemSet* _pSet) +{ + if( _pSet ) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; +} + +void SwEnvFormatPage::FillItem(SwEnvItem& rItem) +{ + rItem.m_nAddrFromLeft = static_cast< sal_Int32 >(getfieldval(*m_xAddrLeftField)); + rItem.m_nAddrFromTop = static_cast< sal_Int32 >(getfieldval(*m_xAddrTopField )); + rItem.m_nSendFromLeft = static_cast< sal_Int32 >(getfieldval(*m_xSendLeftField)); + rItem.m_nSendFromTop = static_cast< sal_Int32 >(getfieldval(*m_xSendTopField )); + + const sal_uInt16 nPaper = m_aIDs[m_xSizeFormatBox->get_active()]; + if (nPaper == sal_uInt16(PAPER_USER)) + { + long lWVal = static_cast< long >(getfieldval(*m_xSizeWidthField )); + long lHVal = static_cast< long >(getfieldval(*m_xSizeHeightField)); + rItem.m_nWidth = std::max(lWVal, lHVal); + rItem.m_nHeight = std::min(lWVal, lHVal); + } + else + { + long lWVal = SvxPaperInfo::GetPaperSize(static_cast<Paper>(nPaper)).Width (); + long lHVal = SvxPaperInfo::GetPaperSize(static_cast<Paper>(nPaper)).Height(); + rItem.m_nWidth = std::max(lWVal, lHVal); + rItem.m_nHeight = std::min(lWVal, lHVal); + } +} + +bool SwEnvFormatPage::FillItemSet(SfxItemSet* rSet) +{ + FillItem(GetParentSwEnvDlg()->aEnvItem); + rSet->Put(GetParentSwEnvDlg()->aEnvItem); + return true; +} + +void SwEnvFormatPage::Reset(const SfxItemSet* rSet) +{ + const SwEnvItem& rItem = static_cast<const SwEnvItem&>( rSet->Get(FN_ENVELOP)); + + Paper ePaper = SvxPaperInfo::GetSvxPaper( + Size( std::min(rItem.m_nWidth, rItem.m_nHeight), + std::max(rItem.m_nWidth, rItem.m_nHeight)), MapUnit::MapTwip); + for (size_t i = 0; i < m_aIDs.size(); ++i) + if (m_aIDs[i] == static_cast<sal_uInt16>(ePaper)) + m_xSizeFormatBox->set_active(i); + + // Metric fields + setfieldval(*m_xAddrLeftField, rItem.m_nAddrFromLeft); + setfieldval(*m_xAddrTopField, rItem.m_nAddrFromTop ); + setfieldval(*m_xSendLeftField, rItem.m_nSendFromLeft); + setfieldval(*m_xSendTopField, rItem.m_nSendFromTop ); + setfieldval(*m_xSizeWidthField , std::max(rItem.m_nWidth, rItem.m_nHeight)); + setfieldval(*m_xSizeHeightField , std::min(rItem.m_nWidth, rItem.m_nHeight)); + SetMinMax(); + + GetParentSwEnvDlg()->pSenderSet.reset(); + GetParentSwEnvDlg()->pAddresseeSet.reset(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/envfmt.hxx b/sw/source/ui/envelp/envfmt.hxx new file mode 100644 index 000000000..28da3d63d --- /dev/null +++ b/sw/source/ui/envelp/envfmt.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_UI_ENVELP_ENVFMT_HXX +#define INCLUDED_SW_SOURCE_UI_ENVELP_ENVFMT_HXX + +#include <vcl/weld.hxx> +#include <envlop.hxx> + +class SwTextFormatColl; + +class SwEnvFormatPage : public SfxTabPage +{ + SwEnvDlg* m_pDialog; + std::vector<sal_uInt16> m_aIDs; + + SwEnvPreview m_aPreview; + std::unique_ptr<weld::MetricSpinButton> m_xAddrLeftField; + std::unique_ptr<weld::MetricSpinButton> m_xAddrTopField; + std::unique_ptr<weld::MenuButton> m_xAddrEditButton; + std::unique_ptr<weld::MetricSpinButton> m_xSendLeftField; + std::unique_ptr<weld::MetricSpinButton> m_xSendTopField; + std::unique_ptr<weld::MenuButton> m_xSendEditButton; + std::unique_ptr<weld::ComboBox> m_xSizeFormatBox; + std::unique_ptr<weld::MetricSpinButton> m_xSizeWidthField; + std::unique_ptr<weld::MetricSpinButton> m_xSizeHeightField; + std::unique_ptr<weld::CustomWeld> m_xPreview; + + DECL_LINK(ModifyHdl, weld::MetricSpinButton&, void); + DECL_LINK(AddrEditHdl, const OString&, void); + DECL_LINK(SendEditHdl, const OString&, void); + DECL_LINK(FormatHdl, weld::ComboBox&, void); + + void SetMinMax(); + + SfxItemSet *GetCollItemSet(SwTextFormatColl const * pColl, bool bSender); + + void Edit(const OString& rIdent, bool bSender); + + SwEnvDlg *GetParentSwEnvDlg() { return m_pDialog; } + +public: + SwEnvFormatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + void Init(SwEnvDlg* pDialog); + virtual ~SwEnvFormatPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwEnvItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/envlop1.cxx b/sw/source/ui/envelp/envlop1.cxx new file mode 100644 index 000000000..928f67558 --- /dev/null +++ b/sw/source/ui/envelp/envlop1.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 <dbmgr.hxx> +#include <tools/lineend.hxx> +#include <vcl/print.hxx> +#include <vcl/settings.hxx> + +#include <swwait.hxx> +#include <viewopt.hxx> + +#include <wrtsh.hxx> +#include <cmdid.h> +#include "envfmt.hxx" +#include <envlop.hxx> +#include "envprt.hxx" +#include <fmtcol.hxx> +#include <poolfmt.hxx> +#include <view.hxx> + +#include <comphelper/string.hxx> + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +SwEnvPreview::SwEnvPreview() + : m_pDialog(nullptr) +{ +} + +void SwEnvPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 20, + pDrawingArea->get_text_height() * 8); +} + +void SwEnvPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); + rRenderContext.SetBackground(rRenderContext.GetSettings().GetStyleSettings().GetDialogColor()); + rRenderContext.Erase(); + + const SwEnvItem& rItem = m_pDialog->aEnvItem; + + const long nPageW = std::max(rItem.m_nWidth, rItem.m_nHeight); + const long nPageH = std::min(rItem.m_nWidth, rItem.m_nHeight); + + Size aSize(GetOutputSizePixel()); + + const double f = 0.8 * std::min( + double(aSize.Width()) / double(nPageW), + double(aSize.Height()) / double(nPageH)); + + Color aBack = rSettings.GetWindowColor(); + Color aFront = SwViewOption::GetFontColor(); + Color aMedium((aBack.GetRed() + aFront.GetRed()) / 2, + (aBack.GetGreen() + aFront.GetGreen()) / 2, + (aBack.GetBlue() + aFront.GetBlue()) / 2); + + rRenderContext.SetLineColor(aFront); + + // Envelope + const long nW = static_cast<long>(f * nPageW); + const long nH = static_cast<long>(f * nPageH); + const long nX = (aSize.Width () - nW) / 2; + const long nY = (aSize.Height() - nH) / 2; + rRenderContext.SetFillColor(aBack); + rRenderContext.DrawRect(tools::Rectangle(Point(nX, nY), Size(nW, nH))); + + // Sender + if (rItem.m_bSend) + { + const long nSendX = nX + static_cast<long>(f * rItem.m_nSendFromLeft); + const long nSendY = nY + static_cast<long>(f * rItem.m_nSendFromTop ); + const long nSendW = static_cast<long>(f * (rItem.m_nAddrFromLeft - rItem.m_nSendFromLeft)); + const long nSendH = static_cast<long>(f * (rItem.m_nAddrFromTop - rItem.m_nSendFromTop - 566)); + rRenderContext.SetFillColor(aMedium); + + rRenderContext.DrawRect(tools::Rectangle(Point(nSendX, nSendY), Size(nSendW, nSendH))); + } + + // Addressee + const long nAddrX = nX + static_cast<long>(f * rItem.m_nAddrFromLeft); + const long nAddrY = nY + static_cast<long>(f * rItem.m_nAddrFromTop ); + const long nAddrW = static_cast<long>(f * (nPageW - rItem.m_nAddrFromLeft - 566)); + const long nAddrH = static_cast<long>(f * (nPageH - rItem.m_nAddrFromTop - 566)); + rRenderContext.SetFillColor(aMedium); + rRenderContext.DrawRect(tools::Rectangle(Point(nAddrX, nAddrY), Size(nAddrW, nAddrH))); + + // Stamp + const long nStmpW = static_cast<long>(f * 1417 /* 2,5 cm */); + const long nStmpH = static_cast<long>(f * 1701 /* 3,0 cm */); + const long nStmpX = nX + nW - static_cast<long>(f * 566) - nStmpW; + const long nStmpY = nY + static_cast<long>(f * 566); + + rRenderContext.SetFillColor(aBack); + rRenderContext.DrawRect(tools::Rectangle(Point(nStmpX, nStmpY), Size(nStmpW, nStmpH))); +} + +SwEnvDlg::SwEnvDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell* pWrtSh, Printer* pPrt, bool bInsert) + : SfxTabDialogController(pParent, "modules/swriter/ui/envdialog.ui", "EnvDialog", &rSet) + , aEnvItem(static_cast<const SwEnvItem&>( rSet.Get(FN_ENVELOP))) + , pSh(pWrtSh) + , pPrinter(pPrt) + , m_xModify(m_xBuilder->weld_button("modify")) +{ + if (!bInsert) + { + GetUserButton()->set_label(m_xModify->get_label()); + } + + AddTabPage("envelope", SwEnvPage::Create, nullptr); + AddTabPage("format", SwEnvFormatPage::Create, nullptr); + AddTabPage("printer", SwEnvPrtPage::Create, nullptr); +} + +SwEnvDlg::~SwEnvDlg() +{ + pAddresseeSet.reset(); + pSenderSet.reset(); +} + +void SwEnvDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (rId == "printer") + { + static_cast<SwEnvPrtPage*>(&rPage)->SetPrt(pPrinter); + } + else if (rId == "envelope") + { + static_cast<SwEnvPage*>(&rPage)->Init(this); + } + else if (rId == "format") + { + static_cast<SwEnvFormatPage*>(&rPage)->Init(this); + } +} + +short SwEnvDlg::Ok() +{ + short nRet = SfxTabDialogController::Ok(); + + if (nRet == RET_OK || nRet == RET_USER) + { + if (pAddresseeSet) + { + SwTextFormatColl* pColl = pSh->GetTextCollFromPool(RES_POOLCOLL_JAKETADRESS); + pColl->SetFormatAttr(*pAddresseeSet); + } + if (pSenderSet) + { + SwTextFormatColl* pColl = pSh->GetTextCollFromPool(RES_POOLCOLL_SENDADRESS); + pColl->SetFormatAttr(*pSenderSet); + } + } + + return nRet; +} + +SwEnvPage::SwEnvPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/envaddresspage.ui", "EnvAddressPage", &rSet) + , m_pDialog(nullptr) + , m_pSh(nullptr) + , m_xAddrEdit(m_xBuilder->weld_text_view("addredit")) + , m_xDatabaseLB(m_xBuilder->weld_combo_box("database")) + , m_xTableLB(m_xBuilder->weld_combo_box("table")) + , m_xDBFieldLB(m_xBuilder->weld_combo_box("field")) + , m_xInsertBT(m_xBuilder->weld_button("insert")) + , m_xSenderBox(m_xBuilder->weld_check_button("sender")) + , m_xSenderEdit(m_xBuilder->weld_text_view("senderedit")) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreview)) +{ + auto nTextBoxHeight(m_xAddrEdit->get_height_rows(10)); + auto nTextBoxWidth(m_xAddrEdit->get_approximate_digit_width() * 25); + + m_xAddrEdit->set_size_request(nTextBoxWidth, nTextBoxHeight); + m_xSenderEdit->set_size_request(nTextBoxWidth, nTextBoxHeight); + + auto nListBoxWidth = m_xTableLB->get_approximate_digit_width() * 25; + m_xTableLB->set_size_request(nListBoxWidth, -1); + m_xDatabaseLB->set_size_request(nListBoxWidth, -1); + m_xDBFieldLB->set_size_request(nListBoxWidth, -1); + + SetExchangeSupport(); +} + +void SwEnvPage::Init(SwEnvDlg* pDialog) +{ + m_pDialog = pDialog; + m_pSh = m_pDialog->pSh; + m_aPreview.SetDialog(pDialog); + + // Install handlers + m_xDatabaseLB->connect_changed(LINK(this, SwEnvPage, DatabaseHdl)); + m_xTableLB->connect_changed(LINK(this, SwEnvPage, DatabaseHdl)); + m_xInsertBT->connect_clicked(LINK(this, SwEnvPage, FieldHdl)); + m_xSenderBox->connect_clicked(LINK(this, SwEnvPage, SenderHdl)); + + SwDBData aData = m_pSh->GetDBData(); + m_sActDBName = aData.sDataSource + OUStringChar(DB_DELIM) + aData.sCommand; + InitDatabaseBox(); +} + +SwEnvPage::~SwEnvPage() +{ +} + +IMPL_LINK( SwEnvPage, DatabaseHdl, weld::ComboBox&, rListBox, void ) +{ + SwWait aWait( *m_pSh->GetView().GetDocShell(), true ); + + if (&rListBox == m_xDatabaseLB.get()) + { + m_sActDBName = rListBox.get_active_text(); + m_pSh->GetDBManager()->GetTableNames(*m_xTableLB, m_sActDBName); + m_sActDBName += OUStringChar(DB_DELIM); + } + else + { + m_sActDBName = comphelper::string::setToken(m_sActDBName, 1, DB_DELIM, m_xTableLB->get_active_text()); + } + m_pSh->GetDBManager()->GetColumnNames(*m_xDBFieldLB, m_xDatabaseLB->get_active_text(), + m_xTableLB->get_active_text()); +} + +IMPL_LINK_NOARG(SwEnvPage, FieldHdl, weld::Button&, void) +{ + OUString aStr("<" + m_xDatabaseLB->get_active_text() + "." + + m_xTableLB->get_active_text() + "." + + m_xTableLB->get_active_id() + "." + + m_xDBFieldLB->get_active_text() + ">"); + m_xAddrEdit->replace_selection(aStr); + int nStartPos, nEndPos; + m_xAddrEdit->get_selection_bounds(nStartPos, nEndPos); + m_xAddrEdit->grab_focus(); + m_xAddrEdit->select_region(nStartPos, nEndPos); +} + +IMPL_LINK_NOARG(SwEnvPage, SenderHdl, weld::Button&, void) +{ + const bool bEnable = m_xSenderBox->get_active(); + GetParentSwEnvDlg()->aEnvItem.m_bSend = bEnable; + m_xSenderEdit->set_sensitive(bEnable); + if (bEnable) + { + m_xSenderEdit->grab_focus(); + if (m_xSenderEdit->get_text().isEmpty()) + m_xSenderEdit->set_text(MakeSender()); + } + m_xPreview->queue_draw(); +} + +void SwEnvPage::InitDatabaseBox() +{ + if (m_pSh->GetDBManager()) + { + m_xDatabaseLB->clear(); + const Sequence<OUString> aDataNames = SwDBManager::GetExistingDatabaseNames(); + + for (const OUString& rDataName : aDataNames) + m_xDatabaseLB->append_text(rDataName); + + sal_Int32 nIdx{ 0 }; + OUString sDBName = m_sActDBName.getToken( 0, DB_DELIM, nIdx ); + OUString sTableName = m_sActDBName.getToken( 0, DB_DELIM, nIdx ); + m_xDatabaseLB->set_active_text(sDBName); + if (m_pSh->GetDBManager()->GetTableNames(*m_xTableLB, sDBName)) + { + m_xTableLB->append_text(sTableName); + m_pSh->GetDBManager()->GetColumnNames(*m_xDBFieldLB, sDBName, sTableName); + } + else + m_xDBFieldLB->clear(); + } +} + +std::unique_ptr<SfxTabPage> SwEnvPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwEnvPage>(pPage, pController, *rSet); +} + +void SwEnvPage::ActivatePage(const SfxItemSet& rSet) +{ + SfxItemSet aSet(rSet); + aSet.Put(GetParentSwEnvDlg()->aEnvItem); + Reset(&aSet); +} + +DeactivateRC SwEnvPage::DeactivatePage(SfxItemSet* _pSet) +{ + FillItem(GetParentSwEnvDlg()->aEnvItem); + if( _pSet ) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; +} + +void SwEnvPage::FillItem(SwEnvItem& rItem) +{ + rItem.m_aAddrText = m_xAddrEdit->get_text(); + rItem.m_bSend = m_xSenderBox->get_active(); + rItem.m_aSendText = m_xSenderEdit->get_text(); +} + +bool SwEnvPage::FillItemSet(SfxItemSet* rSet) +{ + FillItem(GetParentSwEnvDlg()->aEnvItem); + rSet->Put(GetParentSwEnvDlg()->aEnvItem); + return true; +} + +void SwEnvPage::Reset(const SfxItemSet* rSet) +{ + SwEnvItem aItem = static_cast<const SwEnvItem&>( rSet->Get(FN_ENVELOP)); + m_xAddrEdit->set_text(convertLineEnd(aItem.m_aAddrText, GetSystemLineEnd())); + m_xSenderEdit->set_text(convertLineEnd(aItem.m_aSendText, GetSystemLineEnd())); + m_xSenderBox->set_active(aItem.m_bSend); + SenderHdl(*m_xSenderBox); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/envprt.cxx b/sw/source/ui/envelp/envprt.cxx new file mode 100644 index 000000000..43dd2f1a9 --- /dev/null +++ b/sw/source/ui/envelp/envprt.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/print.hxx> +#include <svtools/prnsetup.hxx> +#include <svtools/unitconv.hxx> + +#include <cmdid.h> +#include "envprt.hxx" +#include <envlop.hxx> +#include <uitool.hxx> + +SwEnvPrtPage::SwEnvPrtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/envprinterpage.ui", "EnvPrinterPage", &rSet) + , m_xUpper(m_xBuilder->weld_widget("upper")) + , m_xLower(m_xBuilder->weld_widget("lower")) + , m_xTopButton(m_xBuilder->weld_radio_button("top")) + , m_xBottomButton(m_xBuilder->weld_radio_button("bottom")) + , m_xRightField(m_xBuilder->weld_metric_spin_button("right", FieldUnit::CM)) + , m_xDownField(m_xBuilder->weld_metric_spin_button("down", FieldUnit::CM)) + , m_xPrinterInfo(m_xBuilder->weld_label("printername")) + , m_xPrtSetup(m_xBuilder->weld_button("setup")) + , m_aIdsL { m_xBuilder->weld_radio_button("horileftl"), + m_xBuilder->weld_radio_button("horicenterl"), + m_xBuilder->weld_radio_button("horirightl"), + m_xBuilder->weld_radio_button("vertleftl"), + m_xBuilder->weld_radio_button("vertcenterl"), + m_xBuilder->weld_radio_button("vertrightl") } + , m_aIdsU { m_xBuilder->weld_radio_button("horileftu"), + m_xBuilder->weld_radio_button("horicenteru"), + m_xBuilder->weld_radio_button("horirightu"), + m_xBuilder->weld_radio_button("vertleftu"), + m_xBuilder->weld_radio_button("vertcenteru"), + m_xBuilder->weld_radio_button("vertrightu") } +{ + SetExchangeSupport(); + + // Metrics + FieldUnit eUnit = ::GetDfltMetric(false); + ::SetFieldUnit(*m_xRightField, eUnit); + ::SetFieldUnit(*m_xDownField, eUnit); + + // Install handlers + m_xTopButton->connect_toggled(LINK(this, SwEnvPrtPage, ClickHdl)); + m_xBottomButton->connect_toggled(LINK(this, SwEnvPrtPage, ClickHdl)); + + m_xPrtSetup->connect_clicked(LINK(this, SwEnvPrtPage, ButtonHdl)); + + for (auto& a : m_aIdsL) + a->connect_toggled(LINK(this, SwEnvPrtPage, LowerHdl)); + for (auto& a : m_aIdsU) + a->connect_toggled(LINK(this, SwEnvPrtPage, UpperHdl)); + + // Bitmaps + ClickHdl(*m_xBottomButton); +} + +SwEnvPrtPage::~SwEnvPrtPage() +{ + m_xPrt.clear(); +} + +IMPL_LINK_NOARG(SwEnvPrtPage, ClickHdl, weld::ToggleButton&, void) +{ + // Envelope from bottom, otherwise Envelope from top + const bool bLowerActive = m_xBottomButton->get_active(); + m_xUpper->set_visible(!bLowerActive); + m_xLower->set_visible(bLowerActive); +} + +IMPL_LINK(SwEnvPrtPage, LowerHdl, weld::ToggleButton&, rButton, void) +{ + for (int i = ENV_HOR_LEFT; i <= ENV_VER_RGHT; ++i) + { + if (&rButton == m_aIdsL[i].get()) + { + m_aIdsU[i]->set_active(m_aIdsL[i]->get_active()); + break; + } + } +} + +IMPL_LINK(SwEnvPrtPage, UpperHdl, weld::ToggleButton&, rButton, void) +{ + for (int i = ENV_HOR_LEFT; i <= ENV_VER_RGHT; ++i) + { + if (&rButton == m_aIdsU[i].get()) + { + m_aIdsL[i]->set_active(m_aIdsU[i]->get_active()); + break; + } + } +} + +IMPL_LINK(SwEnvPrtPage, ButtonHdl, weld::Button&, rBtn, void) +{ + if (&rBtn == m_xPrtSetup.get()) + { + // Call printer setup + if (m_xPrt) + { + PrinterSetupDialog aDlg(GetFrameWeld()); + aDlg.SetPrinter(m_xPrt); + aDlg.run(); + rBtn.grab_focus(); + m_xPrinterInfo->set_label(m_xPrt->GetName()); + } + } +} + +std::unique_ptr<SfxTabPage> SwEnvPrtPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwEnvPrtPage>(pPage, pController, *rSet); +} + +void SwEnvPrtPage::ActivatePage(const SfxItemSet&) +{ + if (m_xPrt) + m_xPrinterInfo->set_label(m_xPrt->GetName()); +} + +DeactivateRC SwEnvPrtPage::DeactivatePage(SfxItemSet* _pSet) +{ + if( _pSet ) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; +} + +void SwEnvPrtPage::FillItem(SwEnvItem& rItem) +{ + int nOrient = 0; + for (int i = ENV_HOR_LEFT; i <= ENV_VER_RGHT; ++i) + { + assert(m_aIdsL[i]->get_active() == m_aIdsU[i]->get_active()); + if (m_aIdsL[i]->get_active()) + { + nOrient = i; + break; + } + } + + rItem.m_eAlign = static_cast<SwEnvAlign>(nOrient); + rItem.m_bPrintFromAbove = m_xTopButton->get_active(); + rItem.m_nShiftRight = getfieldval(*m_xRightField); + rItem.m_nShiftDown = getfieldval(*m_xDownField); +} + +bool SwEnvPrtPage::FillItemSet(SfxItemSet* rSet) +{ + FillItem(GetParentSwEnvDlg()->aEnvItem); + rSet->Put(GetParentSwEnvDlg()->aEnvItem); + return true; +} + +void SwEnvPrtPage::Reset(const SfxItemSet* rSet) +{ + // Read item + const SwEnvItem& rItem = static_cast<const SwEnvItem&>( rSet->Get(FN_ENVELOP) ); + m_aIdsL[rItem.m_eAlign]->set_active(true); + m_aIdsU[rItem.m_eAlign]->set_active(true); + + if (rItem.m_bPrintFromAbove) + m_xTopButton->set_active(true); + else + m_xBottomButton->set_active(true); + + setfieldval(*m_xRightField, rItem.m_nShiftRight); + setfieldval(*m_xDownField , rItem.m_nShiftDown ); + + ActivatePage(*rSet); + ClickHdl(*m_xTopButton); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/envprt.hxx b/sw/source/ui/envelp/envprt.hxx new file mode 100644 index 000000000..00a610cad --- /dev/null +++ b/sw/source/ui/envelp/envprt.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UI_ENVELP_ENVPRT_HXX +#define INCLUDED_SW_SOURCE_UI_ENVELP_ENVPRT_HXX + +#include <sfx2/tabdlg.hxx> +#include <vcl/print.hxx> +#include <vcl/weld.hxx> + +#include <envimg.hxx> +#include <envlop.hxx> + +class SwEnvDlg; + +class SwEnvPrtPage : public SfxTabPage +{ + std::unique_ptr<weld::Widget> m_xUpper; + std::unique_ptr<weld::Widget> m_xLower; + std::unique_ptr<weld::RadioButton> m_xTopButton; + std::unique_ptr<weld::RadioButton> m_xBottomButton; + std::unique_ptr<weld::MetricSpinButton> m_xRightField; + std::unique_ptr<weld::MetricSpinButton> m_xDownField; + std::unique_ptr<weld::Label> m_xPrinterInfo; + std::unique_ptr<weld::Button> m_xPrtSetup; + + std::unique_ptr<weld::RadioButton> m_aIdsL[ENV_VER_RGHT-ENV_HOR_LEFT+1]; + std::unique_ptr<weld::RadioButton> m_aIdsU[ENV_VER_RGHT-ENV_HOR_LEFT+1]; + + VclPtr<Printer> m_xPrt; + + DECL_LINK(LowerHdl, weld::ToggleButton&, void); + DECL_LINK(UpperHdl, weld::ToggleButton&, void); + DECL_LINK(ClickHdl, weld::ToggleButton&, void); + DECL_LINK(ButtonHdl, weld::Button&, void ); + + SwEnvDlg* GetParentSwEnvDlg() {return static_cast<SwEnvDlg*>(GetDialogController()); } + +public: + SwEnvPrtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwEnvPrtPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwEnvItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + + void SetPrt(Printer* pPrinter) { m_xPrt = pPrinter; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/label1.cxx b/sw/source/ui/envelp/label1.cxx new file mode 100644 index 000000000..4ac097718 --- /dev/null +++ b/sw/source/ui/envelp/label1.cxx @@ -0,0 +1,725 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/svapp.hxx> +#include <rtl/ustring.hxx> +#include <tools/lineend.hxx> +#include <svtools/unitconv.hxx> +#include <com/sun/star/uno/Sequence.h> +#include <swtypes.hxx> +#include <labimp.hxx> +#include "swuilabimp.hxx" +#include "labfmt.hxx" +#include "labprt.hxx" +#include <dbmgr.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <envimg.hxx> + +void SwLabRec::SetFromItem( const SwLabItem& rItem ) +{ + m_nHDist = rItem.m_lHDist; + m_nVDist = rItem.m_lVDist; + m_nWidth = rItem.m_lWidth; + m_nHeight = rItem.m_lHeight; + m_nLeft = rItem.m_lLeft; + m_nUpper = rItem.m_lUpper; + m_nCols = rItem.m_nCols; + m_nRows = rItem.m_nRows; + m_nPWidth = rItem.m_lPWidth; + m_nPHeight = rItem.m_lPHeight; + m_bCont = rItem.m_bCont; +} + +void SwLabRec::FillItem( SwLabItem& rItem ) const +{ + rItem.m_lHDist = m_nHDist; + rItem.m_lVDist = m_nVDist; + rItem.m_lWidth = m_nWidth; + rItem.m_lHeight = m_nHeight; + rItem.m_lLeft = m_nLeft; + rItem.m_lUpper = m_nUpper; + rItem.m_nCols = m_nCols; + rItem.m_lPWidth = m_nPWidth; + rItem.m_lPHeight = m_nPHeight; + rItem.m_nRows = m_nRows; +} + +void SwLabDlg::ReplaceGroup_( const OUString &rMake ) +{ + // Remove old entries + m_pRecs->erase(m_pRecs->begin() + 1, m_pRecs->end()); + aLabelsCfg.FillLabels(rMake, *m_pRecs); + aLstGroup = rMake; +} + +void SwLabDlg::PageCreated(const OString &rId, SfxTabPage &rPage) +{ + if (rId == "labels") + { + if (m_bLabel) + { + static_cast<SwLabPage*>(&rPage)->SetDBManager(pDBManager); + static_cast<SwLabPage*>(&rPage)->InitDatabaseBox(); + } + else + static_cast<SwLabPage*>(&rPage)->SetToBusinessCard(); + } + else if (rId == "options") + m_pPrtPage = static_cast<SwLabPrtPage*>(&rPage); +} + +SwLabDlg::SwLabDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwDBManager* pDBManager_, bool bLabel) + : SfxTabDialogController(pParent, "modules/swriter/ui/labeldialog.ui", "LabelDialog", &rSet) + , pDBManager(pDBManager_) + , m_pPrtPage(nullptr) + , aTypeIds(50, 10) + , m_pRecs(new SwLabRecs) + , m_bLabel(bLabel) +{ + weld::WaitObject aWait(pParent); + + // Read user label from writer.cfg + SwLabItem aItem(static_cast<const SwLabItem&>(rSet.Get( FN_LABEL ))); + std::unique_ptr<SwLabRec> pRec(new SwLabRec); + pRec->m_aMake = pRec->m_aType = SwResId(STR_CUSTOM_LABEL); + pRec->SetFromItem( aItem ); + + bool bDouble = false; + + for (const std::unique_ptr<SwLabRec> & i : *m_pRecs) + { + if (pRec->m_aMake == i->m_aMake && + pRec->m_aType == i->m_aType) + { + bDouble = true; + break; + } + } + + if (!bDouble) + m_pRecs->insert( m_pRecs->begin(), std::move(pRec)); + + size_t nLstGroup = 0; + const std::vector<OUString>& rMan = aLabelsCfg.GetManufacturers(); + for(size_t nMan = 0; nMan < rMan.size(); ++nMan) + { + aMakes.push_back(rMan[nMan]); + if ( rMan[nMan] == aItem.m_aLstMake ) + nLstGroup = nMan; + } + + if ( !aMakes.empty() ) + ReplaceGroup_( aMakes[nLstGroup] ); + + if (m_xExampleSet) + m_xExampleSet->Put(aItem); + + AddTabPage("format", SwLabFormatPage::Create, nullptr); + AddTabPage("options", SwLabPrtPage::Create, nullptr); + m_sBusinessCardDlg = SwResId(STR_BUSINESS_CARDS); + + if (m_bLabel) + { + RemoveTabPage("business"); + RemoveTabPage("private"); + RemoveTabPage("medium"); + AddTabPage("labels", SwLabPage::Create, nullptr); + } + else + { + RemoveTabPage("labels"); + AddTabPage("medium", SwLabPage::Create, nullptr); + AddTabPage("business", SwBusinessDataPage::Create, nullptr ); + AddTabPage("private", SwPrivateDataPage::Create, nullptr); + m_xDialog->set_title(m_sBusinessCardDlg); + } + + pParent->set_busy_cursor(false); +} + +SwLabDlg::~SwLabDlg() +{ + m_pRecs.reset(); +} + +void SwLabDlg::GetLabItem(SwLabItem &rItem) +{ + const SwLabItem& rActItem = static_cast<const SwLabItem&>(GetExampleSet()->Get(FN_LABEL)); + const SwLabItem& rOldItem = static_cast<const SwLabItem&>(GetInputSetImpl()->Get(FN_LABEL)); + + if (rActItem != rOldItem) + { + // Was already "put" with (hopefully) correct content + rItem = rActItem; + } + else + { + rItem = rOldItem; + + // In rItem there are only settings defined by users. + // Therefore get the real settings directly from Record + SwLabRec* pRec = GetRecord(rItem.m_aType, rItem.m_bCont); + pRec->FillItem( rItem ); + } +} + +SwLabRec* SwLabDlg::GetRecord(const OUString &rRecName, bool bCont) +{ + SwLabRec* pRec = nullptr; + bool bFound = false; + const OUString sCustom(SwResId(STR_CUSTOM_LABEL)); + + const size_t nCount = Recs().size(); + for (size_t i = 0; i < nCount; ++i) + { + pRec = Recs()[i].get(); + if (pRec->m_aType != sCustom && + rRecName == pRec->m_aType && bCont == pRec->m_bCont) + { + bFound = true; + break; + } + } + if (!bFound) // User defined + pRec = Recs()[0].get(); + + return pRec; +} + +Printer *SwLabDlg::GetPrt() +{ + if (m_pPrtPage) + return m_pPrtPage->GetPrt(); + else + return nullptr; +} + +SwLabPage::SwLabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/cardmediumpage.ui", "CardMediumPage", &rSet) + , pDBManager(nullptr) + , aItem(static_cast<const SwLabItem&>(rSet.Get(FN_LABEL))) + , m_xAddressFrame(m_xBuilder->weld_widget("addressframe")) + , m_xAddrBox(m_xBuilder->weld_check_button("address")) + , m_xWritingEdit(m_xBuilder->weld_text_view("textview")) + , m_xDatabaseLB(m_xBuilder->weld_combo_box("database")) + , m_xTableLB(m_xBuilder->weld_combo_box("table")) + , m_xInsertBT(m_xBuilder->weld_button("insert")) + , m_xDBFieldLB(m_xBuilder->weld_combo_box("field")) + , m_xContButton(m_xBuilder->weld_radio_button("continuous")) + , m_xSheetButton(m_xBuilder->weld_radio_button("sheet")) + , m_xMakeBox(m_xBuilder->weld_combo_box("brand")) + , m_xTypeBox(m_xBuilder->weld_combo_box("type")) + , m_xHiddenSortTypeBox(m_xBuilder->weld_combo_box("hiddentype")) + , m_xFormatInfo(m_xBuilder->weld_label("formatinfo")) +{ + weld::WaitObject aWait(GetFrameWeld()); + + m_xWritingEdit->set_size_request(m_xWritingEdit->get_approximate_digit_width() * 30, + m_xWritingEdit->get_height_rows(10)); + m_xHiddenSortTypeBox->make_sorted(); + + long nListBoxWidth = m_xWritingEdit->get_approximate_digit_width() * 25; + m_xTableLB->set_size_request(nListBoxWidth, -1); + m_xDatabaseLB->set_size_request(nListBoxWidth, -1); + m_xDBFieldLB->set_size_request(nListBoxWidth, -1); + + SetExchangeSupport(); + + // Install handlers + m_xAddrBox->connect_toggled(LINK(this, SwLabPage, AddrHdl)); + m_xDatabaseLB->connect_changed(LINK(this, SwLabPage, DatabaseHdl)); + m_xTableLB->connect_changed(LINK(this, SwLabPage, DatabaseHdl)); + m_xDBFieldLB->connect_changed(LINK(this, SwLabPage, DatabaseHdl)); + m_xInsertBT->connect_clicked(LINK(this, SwLabPage, FieldHdl)); + // Disable insert button first, + // it'll be enabled if m_xDatabaseLB, m_pTableLB and m_pInsertBT are filled + m_xInsertBT->set_sensitive(false); + m_xContButton->connect_toggled(LINK(this, SwLabPage, PageHdl)); + m_xSheetButton->connect_toggled(LINK(this, SwLabPage, PageHdl)); + auto nMaxWidth = m_xMakeBox->get_approximate_digit_width() * 32; + m_xMakeBox->set_size_request(nMaxWidth, -1); + m_xTypeBox->set_size_request(nMaxWidth, -1); + m_xMakeBox->connect_changed(LINK(this, SwLabPage, MakeHdl)); + m_xTypeBox->connect_changed(LINK(this, SwLabPage, TypeHdl)); + + InitDatabaseBox(); +} + +SwLabPage::~SwLabPage() +{ +} + +void SwLabPage::SetToBusinessCard() +{ + m_xContainer->set_help_id(HID_BUSINESS_FMT_PAGE); + m_xContButton->set_help_id(HID_BUSINESS_FMT_PAGE_CONT); + m_xSheetButton->set_help_id(HID_BUSINESS_FMT_PAGE_SHEET); + m_xMakeBox->set_help_id(HID_BUSINESS_FMT_PAGE_BRAND); + m_xTypeBox->set_help_id(HID_BUSINESS_FMT_PAGE_TYPE); + m_xAddressFrame->hide(); +}; + +IMPL_LINK_NOARG(SwLabPage, AddrHdl, weld::ToggleButton&, void) +{ + OUString aWriting; + + if (m_xAddrBox->get_active()) + aWriting = convertLineEnd(MakeSender(), GetSystemLineEnd()); + + m_xWritingEdit->set_text(aWriting); + m_xWritingEdit->grab_focus(); +} + +IMPL_LINK( SwLabPage, DatabaseHdl, weld::ComboBox&, rListBox, void ) +{ + sActDBName = m_xDatabaseLB->get_active_text(); + + weld::WaitObject aObj(GetParentSwLabDlg()->getDialog()); + + if (&rListBox == m_xDatabaseLB.get()) + GetDBManager()->GetTableNames(*m_xTableLB, sActDBName); + + if (&rListBox == m_xDatabaseLB.get() || &rListBox == m_xTableLB.get()) + GetDBManager()->GetColumnNames(*m_xDBFieldLB, sActDBName, m_xTableLB->get_active_text()); + + if (!m_xDatabaseLB->get_active_text().isEmpty() && !m_xTableLB->get_active_text().isEmpty() + && !m_xDBFieldLB->get_active_text().isEmpty()) + m_xInsertBT->set_sensitive(true); + else + m_xInsertBT->set_sensitive(false); +} + +IMPL_LINK_NOARG(SwLabPage, FieldHdl, weld::Button&, void) +{ + OUString aStr("<" + m_xDatabaseLB->get_active_text() + "." + + m_xTableLB->get_active_text() + "." + + m_xTableLB->get_active_id() + "." + + m_xDBFieldLB->get_active_text() + ">"); + m_xWritingEdit->replace_selection(aStr); + int nStartPos, nEndPos; + m_xWritingEdit->get_selection_bounds(nStartPos, nEndPos); + m_xWritingEdit->grab_focus(); + m_xWritingEdit->select_region(nStartPos, nEndPos); +} + +IMPL_LINK_NOARG(SwLabPage, PageHdl, weld::ToggleButton&, void) +{ + MakeHdl(*m_xMakeBox); +} + +IMPL_LINK_NOARG(SwLabPage, MakeHdl, weld::ComboBox&, void) +{ + weld::WaitObject aWait(GetParentSwLabDlg()->getDialog()); + + m_xTypeBox->clear(); + m_xHiddenSortTypeBox->clear(); + GetParentSwLabDlg()->TypeIds().clear(); + + const OUString aMake = m_xMakeBox->get_active_text(); + GetParentSwLabDlg()->ReplaceGroup( aMake ); + aItem.m_aLstMake = aMake; + + const bool bCont = m_xContButton->get_active(); + const size_t nCount = GetParentSwLabDlg()->Recs().size(); + size_t nLstType = 0; + + const OUString sCustom(SwResId(STR_CUSTOM_LABEL)); + //insert the entries into the sorted list box + for ( size_t i = 0; i < nCount; ++i ) + { + const OUString aType(GetParentSwLabDlg()->Recs()[i]->m_aType); + bool bInsert = false; + if (GetParentSwLabDlg()->Recs()[i]->m_aType == sCustom) + { + bInsert = true; + m_xTypeBox->append_text(aType ); + } + else if (GetParentSwLabDlg()->Recs()[i]->m_bCont == bCont) + { + if (m_xHiddenSortTypeBox->find_text(aType) == -1) + { + bInsert = true; + m_xHiddenSortTypeBox->append_text( aType ); + } + } + if(bInsert) + { + GetParentSwLabDlg()->TypeIds().push_back(i); + if ( !nLstType && aType == aItem.m_aLstType ) + nLstType = GetParentSwLabDlg()->TypeIds().size(); + } + } + for (int nEntry = 0; nEntry < m_xHiddenSortTypeBox->get_count(); ++nEntry) + { + m_xTypeBox->append_text(m_xHiddenSortTypeBox->get_text(nEntry)); + } + if (nLstType) + m_xTypeBox->set_active_text(aItem.m_aLstType); + else + m_xTypeBox->set_active(0); + TypeHdl(*m_xTypeBox); +} + +IMPL_LINK_NOARG(SwLabPage, TypeHdl, weld::ComboBox&, void) +{ + DisplayFormat(); + aItem.m_aType = m_xTypeBox->get_active_text(); +} + +void SwLabPage::DisplayFormat() +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "cui/ui/spinbox.ui")); + std::unique_ptr<weld::MetricSpinButton> xField(xBuilder->weld_metric_spin_button("spin", FieldUnit::CM)); + SetFieldUnit(*xField, ::GetDfltMetric(false)); + xField->set_digits(2); + xField->set_range(0, INT_MAX - 1, FieldUnit::NONE); + + SwLabRec* pRec = GetSelectedEntryPos(); + aItem.m_aLstType = pRec->m_aType; + setfldval(*xField, pRec->m_nWidth); + xField->reformat(); + const OUString aWString = xField->get_text(); + + setfldval(*xField, pRec->m_nHeight); + xField->reformat(); + + OUString aText = pRec->m_aType + ": " + aWString + + " x " + xField->get_text() + + " (" + OUString::number( pRec->m_nCols ) + + " x " + OUString::number( pRec->m_nRows ) + ")"; + m_xFormatInfo->set_label(aText); +} + +SwLabRec* SwLabPage::GetSelectedEntryPos() +{ + OUString sSelEntry(m_xTypeBox->get_active_text()); + + return GetParentSwLabDlg()->GetRecord(sSelEntry, m_xContButton->get_active()); +} + +void SwLabPage::InitDatabaseBox() +{ + if( GetDBManager() ) + { + m_xDatabaseLB->clear(); + const css::uno::Sequence<OUString> aDataNames = SwDBManager::GetExistingDatabaseNames(); + for (const OUString& rDataName : aDataNames) + m_xDatabaseLB->append_text(rDataName); + sal_Int32 nIdx{ 0 }; + OUString sDBName = sActDBName.getToken( 0, DB_DELIM, nIdx ); + OUString sTableName = sActDBName.getToken( 0, DB_DELIM, nIdx ); + m_xDatabaseLB->set_active_text(sDBName); + if( !sDBName.isEmpty() && GetDBManager()->GetTableNames(*m_xTableLB, sDBName)) + { + m_xTableLB->set_active_text(sTableName); + GetDBManager()->GetColumnNames(*m_xDBFieldLB, sActDBName, sTableName); + } + else + m_xDBFieldLB->clear(); + } +} + +std::unique_ptr<SfxTabPage> SwLabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwLabPage>(pPage, pController, *rSet); +} + +void SwLabPage::ActivatePage(const SfxItemSet& rSet) +{ + Reset( &rSet ); +} + +DeactivateRC SwLabPage::DeactivatePage(SfxItemSet* _pSet) +{ + if (_pSet) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +void SwLabPage::FillItem(SwLabItem& rItem) +{ + rItem.m_bAddr = m_xAddrBox->get_active(); + rItem.m_aWriting = m_xWritingEdit->get_text(); + rItem.m_bCont = m_xContButton->get_active(); + rItem.m_aMake = m_xMakeBox->get_active_text(); + rItem.m_aType = m_xTypeBox->get_active_text(); + rItem.m_sDBName = sActDBName; + + SwLabRec* pRec = GetSelectedEntryPos(); + pRec->FillItem( rItem ); + + rItem.m_aLstMake = m_xMakeBox->get_active_text(); + rItem.m_aLstType = m_xTypeBox->get_active_text(); +} + +bool SwLabPage::FillItemSet(SfxItemSet* rSet) +{ + FillItem( aItem ); + rSet->Put( aItem ); + + return true; +} + +void SwLabPage::Reset(const SfxItemSet* rSet) +{ + m_xMakeBox->clear(); + + size_t nLstGroup = 0; + + const size_t nCount = GetParentSwLabDlg()->Makes().size(); + for(size_t i = 0; i < nCount; ++i) + { + OUString& rStr = GetParentSwLabDlg()->Makes()[i]; + m_xMakeBox->append_text(rStr); + + if ( rStr == aItem.m_aLstMake) + nLstGroup = i; + } + + m_xMakeBox->set_active( nLstGroup ); + MakeHdl(*m_xMakeBox); + + aItem = static_cast<const SwLabItem&>( rSet->Get(FN_LABEL)); + OUString sDBName = aItem.m_sDBName; + + OUString aWriting(convertLineEnd(aItem.m_aWriting, GetSystemLineEnd())); + + m_xAddrBox->set_active( aItem.m_bAddr ); + m_xWritingEdit->set_text( aWriting ); + + for(const auto& rMake : GetParentSwLabDlg()->Makes()) + { + if (m_xMakeBox->find_text(rMake) == -1) + m_xMakeBox->append_text(rMake); + } + + m_xMakeBox->set_active_text(aItem.m_aMake); + //save the current type + OUString sType(aItem.m_aType); + MakeHdl(*m_xMakeBox); + aItem.m_aType = sType; + //#102806# a newly added make may not be in the type ListBox already + if (m_xTypeBox->find_text(aItem.m_aType) == -1 && !aItem.m_aMake.isEmpty()) + GetParentSwLabDlg()->UpdateGroup( aItem.m_aMake ); + if (m_xTypeBox->find_text(aItem.m_aType) != -1) + { + m_xTypeBox->set_active_text(aItem.m_aType); + TypeHdl(*m_xTypeBox); + } + if (m_xDatabaseLB->find_text(sDBName) != -1) + { + m_xDatabaseLB->set_active_text(sDBName); + DatabaseHdl(*m_xDatabaseLB); + } + + if (aItem.m_bCont) + m_xContButton->set_active(true); + else + m_xSheetButton->set_active(true); +} + +SwPrivateDataPage::SwPrivateDataPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/privateuserpage.ui", "PrivateUserPage", &rSet) + , m_xFirstNameED(m_xBuilder->weld_entry("firstname")) + , m_xNameED(m_xBuilder->weld_entry("lastname")) + , m_xShortCutED(m_xBuilder->weld_entry("shortname")) + , m_xFirstName2ED(m_xBuilder->weld_entry("firstname2")) + , m_xName2ED(m_xBuilder->weld_entry("lastname2")) + , m_xShortCut2ED(m_xBuilder->weld_entry("shortname2")) + , m_xStreetED(m_xBuilder->weld_entry("street")) + , m_xZipED(m_xBuilder->weld_entry("izip")) + , m_xCityED(m_xBuilder->weld_entry("icity")) + , m_xCountryED(m_xBuilder->weld_entry("country")) + , m_xStateED(m_xBuilder->weld_entry("state")) + , m_xTitleED(m_xBuilder->weld_entry("title")) + , m_xProfessionED(m_xBuilder->weld_entry("job")) + , m_xPhoneED(m_xBuilder->weld_entry("phone")) + , m_xMobilePhoneED(m_xBuilder->weld_entry("mobile")) + , m_xFaxED(m_xBuilder->weld_entry("fax")) + , m_xHomePageED(m_xBuilder->weld_entry("url")) + , m_xMailED(m_xBuilder->weld_entry("email")) +{ + SetExchangeSupport(); +} + +SwPrivateDataPage::~SwPrivateDataPage() +{ +} + +std::unique_ptr<SfxTabPage> SwPrivateDataPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwPrivateDataPage>(pPage, pController, *rSet); +} + +void SwPrivateDataPage::ActivatePage(const SfxItemSet& rSet) +{ + Reset(&rSet); +} + +DeactivateRC SwPrivateDataPage::DeactivatePage(SfxItemSet* _pSet) +{ + if (_pSet) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; +} + +bool SwPrivateDataPage::FillItemSet(SfxItemSet* rSet) +{ + const SfxItemSet* pExampleSet = GetDialogExampleSet(); + assert(pExampleSet); + SwLabItem aItem = static_cast<const SwLabItem&>(pExampleSet->Get(FN_LABEL)); + + aItem.m_aPrivFirstName = m_xFirstNameED->get_text(); + aItem.m_aPrivName = m_xNameED->get_text(); + aItem.m_aPrivShortCut = m_xShortCutED->get_text(); + aItem.m_aPrivFirstName2 = m_xFirstName2ED->get_text(); + aItem.m_aPrivName2 = m_xName2ED->get_text(); + aItem.m_aPrivShortCut2 = m_xShortCut2ED->get_text(); + aItem.m_aPrivStreet = m_xStreetED->get_text(); + aItem.m_aPrivZip = m_xZipED->get_text(); + aItem.m_aPrivCity = m_xCityED->get_text(); + aItem.m_aPrivCountry = m_xCountryED->get_text(); + aItem.m_aPrivState = m_xStateED->get_text(); + aItem.m_aPrivTitle = m_xTitleED->get_text(); + aItem.m_aPrivProfession= m_xProfessionED->get_text(); + aItem.m_aPrivPhone = m_xPhoneED->get_text(); + aItem.m_aPrivMobile = m_xMobilePhoneED->get_text(); + aItem.m_aPrivFax = m_xFaxED->get_text(); + aItem.m_aPrivWWW = m_xHomePageED->get_text(); + aItem.m_aPrivMail = m_xMailED->get_text(); + + rSet->Put(aItem); + return true; +} + +void SwPrivateDataPage::Reset(const SfxItemSet* rSet) +{ + const SwLabItem& aItem = static_cast<const SwLabItem&>( rSet->Get(FN_LABEL) ); + m_xFirstNameED->set_text(aItem.m_aPrivFirstName); + m_xNameED->set_text(aItem.m_aPrivName); + m_xShortCutED->set_text(aItem.m_aPrivShortCut); + m_xFirstName2ED->set_text(aItem.m_aPrivFirstName2); + m_xName2ED->set_text(aItem.m_aPrivName2); + m_xShortCut2ED->set_text(aItem.m_aPrivShortCut2); + m_xStreetED->set_text(aItem.m_aPrivStreet); + m_xZipED->set_text(aItem.m_aPrivZip); + m_xCityED->set_text(aItem.m_aPrivCity); + m_xCountryED->set_text(aItem.m_aPrivCountry); + m_xStateED->set_text(aItem.m_aPrivState); + m_xTitleED->set_text(aItem.m_aPrivTitle); + m_xProfessionED->set_text(aItem.m_aPrivProfession); + m_xPhoneED->set_text(aItem.m_aPrivPhone); + m_xMobilePhoneED->set_text(aItem.m_aPrivMobile); + m_xFaxED->set_text(aItem.m_aPrivFax); + m_xHomePageED->set_text(aItem.m_aPrivWWW); + m_xMailED->set_text(aItem.m_aPrivMail); +} + +SwBusinessDataPage::SwBusinessDataPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/businessdatapage.ui", "BusinessDataPage", &rSet) + , m_xCompanyED(m_xBuilder->weld_entry("company")) + , m_xCompanyExtED(m_xBuilder->weld_entry("company2")) + , m_xSloganED(m_xBuilder->weld_entry("slogan")) + , m_xStreetED(m_xBuilder->weld_entry("street")) + , m_xZipED(m_xBuilder->weld_entry("izip")) + , m_xCityED(m_xBuilder->weld_entry("icity")) + , m_xCountryED(m_xBuilder->weld_entry("country")) + , m_xStateED(m_xBuilder->weld_entry("state")) + , m_xPositionED(m_xBuilder->weld_entry("position")) + , m_xPhoneED(m_xBuilder->weld_entry("phone")) + , m_xMobilePhoneED(m_xBuilder->weld_entry("mobile")) + , m_xFaxED(m_xBuilder->weld_entry("fax")) + , m_xHomePageED(m_xBuilder->weld_entry("url")) + , m_xMailED(m_xBuilder->weld_entry("email")) +{ + SetExchangeSupport(); +} + +SwBusinessDataPage::~SwBusinessDataPage() +{ +} + +std::unique_ptr<SfxTabPage> SwBusinessDataPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwBusinessDataPage>(pPage, pController, *rSet); +} + +void SwBusinessDataPage::ActivatePage(const SfxItemSet& rSet) +{ + Reset(&rSet); +} + +DeactivateRC SwBusinessDataPage::DeactivatePage(SfxItemSet* _pSet) +{ + if (_pSet) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; +} + +bool SwBusinessDataPage::FillItemSet(SfxItemSet* rSet) +{ + const SfxItemSet* pExampleSet = GetDialogExampleSet(); + assert(pExampleSet); + SwLabItem aItem = static_cast<const SwLabItem&>(pExampleSet->Get(FN_LABEL)); + + aItem.m_aCompCompany = m_xCompanyED->get_text(); + aItem.m_aCompCompanyExt= m_xCompanyExtED->get_text(); + aItem.m_aCompSlogan = m_xSloganED->get_text(); + aItem.m_aCompStreet = m_xStreetED->get_text(); + aItem.m_aCompZip = m_xZipED->get_text(); + aItem.m_aCompCity = m_xCityED->get_text(); + aItem.m_aCompCountry = m_xCountryED->get_text(); + aItem.m_aCompState = m_xStateED->get_text(); + aItem.m_aCompPosition = m_xPositionED->get_text(); + aItem.m_aCompPhone = m_xPhoneED->get_text(); + aItem.m_aCompMobile = m_xMobilePhoneED->get_text(); + aItem.m_aCompFax = m_xFaxED->get_text(); + aItem.m_aCompWWW = m_xHomePageED->get_text(); + aItem.m_aCompMail = m_xMailED->get_text(); + + rSet->Put(aItem); + return true; +} + +void SwBusinessDataPage::Reset(const SfxItemSet* rSet) +{ + const SwLabItem& aItem = static_cast<const SwLabItem&>( rSet->Get(FN_LABEL) ); + m_xCompanyED->set_text(aItem.m_aCompCompany); + m_xCompanyExtED->set_text(aItem.m_aCompCompanyExt); + m_xSloganED->set_text(aItem.m_aCompSlogan); + m_xStreetED->set_text(aItem.m_aCompStreet); + m_xZipED->set_text(aItem.m_aCompZip); + m_xCityED->set_text(aItem.m_aCompCity); + m_xCountryED->set_text(aItem.m_aCompCountry); + m_xStateED->set_text(aItem.m_aCompState); + m_xPositionED->set_text(aItem.m_aCompPosition); + m_xPhoneED->set_text(aItem.m_aCompPhone); + m_xMobilePhoneED->set_text(aItem.m_aCompMobile); + m_xFaxED->set_text(aItem.m_aCompFax); + m_xHomePageED->set_text(aItem.m_aCompWWW); + m_xMailED->set_text(aItem.m_aCompMail); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/labelexp.cxx b/sw/source/ui/envelp/labelexp.cxx new file mode 100644 index 000000000..8f3cf437a --- /dev/null +++ b/sw/source/ui/envelp/labelexp.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 <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <label.hxx> +#include <labimg.hxx> +#include <unoprnms.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; + +void SwLabDlg::UpdateFieldInformation(uno::Reference< frame::XModel > const & xModel, const SwLabItem& rItem) +{ + uno::Reference< text::XTextFieldsSupplier > xFields(xModel, uno::UNO_QUERY); + uno::Reference< container::XNameAccess > xFieldMasters = xFields->getTextFieldMasters(); + + static const struct SwLabItemMap { + const char* pName; + OUString SwLabItem:: *pValue; + } aArr[] = { + { "BC_PRIV_FIRSTNAME" , &SwLabItem::m_aPrivFirstName }, + { "BC_PRIV_NAME" , &SwLabItem::m_aPrivName }, + { "BC_PRIV_INITIALS" , &SwLabItem::m_aPrivShortCut }, + { "BC_PRIV_FIRSTNAME_2", &SwLabItem::m_aPrivFirstName2 }, + { "BC_PRIV_NAME_2" , &SwLabItem::m_aPrivName2 }, + { "BC_PRIV_INITIALS_2" , &SwLabItem::m_aPrivShortCut2 }, + { "BC_PRIV_STREET" , &SwLabItem::m_aPrivStreet }, + { "BC_PRIV_ZIP" , &SwLabItem::m_aPrivZip }, + { "BC_PRIV_CITY" , &SwLabItem::m_aPrivCity }, + { "BC_PRIV_COUNTRY" , &SwLabItem::m_aPrivCountry }, + { "BC_PRIV_STATE" , &SwLabItem::m_aPrivState }, + { "BC_PRIV_TITLE" , &SwLabItem::m_aPrivTitle }, + { "BC_PRIV_PROFESSION" , &SwLabItem::m_aPrivProfession }, + { "BC_PRIV_PHONE" , &SwLabItem::m_aPrivPhone }, + { "BC_PRIV_MOBILE" , &SwLabItem::m_aPrivMobile }, + { "BC_PRIV_FAX" , &SwLabItem::m_aPrivFax }, + { "BC_PRIV_WWW" , &SwLabItem::m_aPrivWWW }, + { "BC_PRIV_MAIL" , &SwLabItem::m_aPrivMail }, + { "BC_COMP_COMPANY" , &SwLabItem::m_aCompCompany }, + { "BC_COMP_COMPANYEXT" , &SwLabItem::m_aCompCompanyExt }, + { "BC_COMP_SLOGAN" , &SwLabItem::m_aCompSlogan }, + { "BC_COMP_STREET" , &SwLabItem::m_aCompStreet }, + { "BC_COMP_ZIP" , &SwLabItem::m_aCompZip }, + { "BC_COMP_CITY" , &SwLabItem::m_aCompCity }, + { "BC_COMP_COUNTRY" , &SwLabItem::m_aCompCountry }, + { "BC_COMP_STATE" , &SwLabItem::m_aCompState }, + { "BC_COMP_POSITION" , &SwLabItem::m_aCompPosition }, + { "BC_COMP_PHONE" , &SwLabItem::m_aCompPhone }, + { "BC_COMP_MOBILE" , &SwLabItem::m_aCompMobile }, + { "BC_COMP_FAX" , &SwLabItem::m_aCompFax }, + { "BC_COMP_WWW" , &SwLabItem::m_aCompWWW }, + { "BC_COMP_MAIL" , &SwLabItem::m_aCompMail }, + { nullptr, nullptr } + }; + + try + { + for( const SwLabItemMap* p = aArr; p->pName; ++p ) + { + OUString uFieldName( + "com.sun.star.text.FieldMaster.User." + + OUString::createFromAscii(p->pName)); + if( xFieldMasters->hasByName( uFieldName )) + { + uno::Any aFirstName = xFieldMasters->getByName( uFieldName ); + uno::Reference< beans::XPropertySet > xField; + aFirstName >>= xField; + uno::Any aContent; + aContent <<= rItem.*p->pValue; + xField->setPropertyValue( UNO_NAME_CONTENT, aContent ); + } + } + } + catch (const uno::RuntimeException&) + { + + } + + uno::Reference< container::XEnumerationAccess > xFieldAcc = xFields->getTextFields(); + uno::Reference< util::XRefreshable > xRefresh(xFieldAcc, uno::UNO_QUERY); + xRefresh->refresh(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/labfmt.cxx b/sw/source/ui/envelp/labfmt.cxx new file mode 100644 index 000000000..5011c4799 --- /dev/null +++ b/sw/source/ui/envelp/labfmt.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 <svtools/unitconv.hxx> +#include <tools/poly.hxx> +#include <vcl/weld.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <sal/log.hxx> + +#include <viewopt.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <label.hxx> +#include <labimp.hxx> +#include <labimg.hxx> +#include "labfmt.hxx" +#include <uitool.hxx> + +#include <strings.hrc> + +using namespace utl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +#define ROUND(x) static_cast<long>((x) + .5) + +namespace { + +// Arrow or interval character +void DrawArrow(vcl::RenderContext& rRenderContext, const Point &rP1, const Point &rP2, bool bArrow) +{ + rRenderContext.DrawLine(rP1, rP2); + if (bArrow) + { + Point aArr[3]; + + // Arrow character + if (rP1.Y() == rP2.Y()) + { + // Horizontal + aArr[0].setX( rP2.X() - 5 ); + aArr[0].setY( rP2.Y() - 2 ); + aArr[1].setX( rP2.X() ); + aArr[1].setY( rP2.Y() ); + aArr[2].setX( rP2.X() - 5 ); + aArr[2].setY( rP2.Y() + 2 ); + } + else + { + // Vertical + aArr[0].setX( rP2.X() - 2 ); + aArr[0].setY( rP2.Y() - 5 ); + aArr[1].setX( rP2.X() + 2 ); + aArr[1].setY( rP2.Y() - 5 ); + aArr[2].setX( rP2.X() ); + aArr[2].setY( rP2.Y() ); + } + + const Color& rFieldTextColor = SwViewOption::GetFontColor(); + rRenderContext.SetFillColor(rFieldTextColor); + rRenderContext.DrawPolygon( tools::Polygon(3, aArr)); + } + else + { + // Interval symbol + if (rP1.Y() == rP2.Y()) + { + // Horizontal + rRenderContext.DrawLine(Point(rP1.X(), rP1.Y() - 2), Point(rP1.X(), rP1.Y() + 2)); + rRenderContext.DrawLine(Point(rP2.X(), rP2.Y() - 2), Point(rP2.X(), rP2.Y() + 2)); + } + else + { + // Vertical + rRenderContext.DrawLine(Point(rP1.X() - 2, rP1.Y()), Point(rP1.X() + 2, rP1.Y())); + rRenderContext.DrawLine(Point(rP2.X() - 2, rP2.Y()), Point(rP2.X() + 2, rP2.Y())); + } + } +} + +} + +SwLabPreview::SwLabPreview() + : m_aGrayColor(COL_LIGHTGRAY) + , m_aHDistStr(SwResId(STR_HDIST)) + , m_aVDistStr(SwResId(STR_VDIST)) + , m_aWidthStr(SwResId(STR_WIDTH)) + , m_aHeightStr(SwResId(STR_HEIGHT)) + , m_aLeftStr(SwResId(STR_LEFT)) + , m_aUpperStr(SwResId(STR_UPPER)) + , m_aColsStr(SwResId(STR_COLS)) + , m_aRowsStr(SwResId(STR_ROWS)) + , m_lHDistWidth(0) + , m_lVDistWidth(0) + , m_lHeightWidth(0) + , m_lLeftWidth(0) + , m_lUpperWidth(0) + , m_lColsWidth(0) + , m_lXWidth(0) + , m_lXHeight(0) +{ +} + +void SwLabPreview::SetDrawingArea(weld::DrawingArea* pWidget) +{ + CustomWidgetController::SetDrawingArea(pWidget); + + pWidget->set_size_request(pWidget->get_approximate_digit_width() * 54, + pWidget->get_text_height() * 15); + + m_lHDistWidth = pWidget->get_pixel_size(m_aHDistStr).Width(); + m_lVDistWidth = pWidget->get_pixel_size(m_aVDistStr).Width(); + m_lHeightWidth = pWidget->get_pixel_size(m_aHeightStr).Width(); + m_lLeftWidth = pWidget->get_pixel_size(m_aLeftStr).Width(); + m_lUpperWidth = pWidget->get_pixel_size(m_aUpperStr).Width(); + m_lColsWidth = pWidget->get_pixel_size(m_aColsStr).Width(); + m_lXWidth = pWidget->get_pixel_size(OUString('X')).Width(); + m_lXHeight = pWidget->get_text_height(); +} + +void SwLabPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + const Size aSize(GetOutputSizePixel()); + const long lOutWPix = aSize.Width(); + const long lOutHPix = aSize.Height(); + + // Scale factor + const double fxpix = double(lOutWPix - (2 * (m_lLeftWidth + 15))) / double(lOutWPix); + + const long lOutWPix23 = long(double(lOutWPix) * fxpix); + const long lOutHPix23 = long(double(lOutHPix) * fxpix); + + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const Color& rWinColor = rStyleSettings.GetWindowColor(); + const Color& rFieldTextColor = SwViewOption::GetFontColor(); + + vcl::Font aFont = rRenderContext.GetFont(); + aFont.SetFillColor(rWinColor); + aFont.SetColor(rFieldTextColor); + rRenderContext.SetFont(aFont); + + rRenderContext.SetBackground(Wallpaper(rWinColor)); + rRenderContext.Erase(); + + rRenderContext.SetLineColor(rWinColor); + rRenderContext.SetFillColor(m_aGrayColor); + vcl::Font aPaintFont(rRenderContext.GetFont()); + aPaintFont.SetTransparent(false); + rRenderContext.SetFont(aPaintFont); + + // size of region to be displayed + const long lDispW = m_aItem.m_lLeft + m_aItem.m_lHDist + ((m_aItem.m_nCols == 1) + ? m_aItem.m_lLeft + : ROUND(m_aItem.m_lHDist / 10.0)); + + const long lDispH = m_aItem.m_lUpper + m_aItem.m_lVDist + ((m_aItem.m_nRows == 1) + ? m_aItem.m_lUpper + : ROUND(m_aItem.m_lVDist / 10.0)); + + // Scale factor + const float fx = float(lOutWPix23) / std::max(1L, lDispW); + const float fy = float(lOutHPix23) / std::max(1L, lDispH); + const float f = std::min(fx, fy); + + // zero point + const long lOutlineW = ROUND(f * lDispW); + const long lOutlineH = ROUND(f * lDispH); + + const long lX0 = (lOutWPix - lOutlineW) / 2; + const long lY0 = (lOutHPix - lOutlineH) / 2; + const long lX1 = lX0 + ROUND(f * m_aItem.m_lLeft ); + const long lY1 = lY0 + ROUND(f * m_aItem.m_lUpper); + const long lX2 = lX0 + ROUND(f * (m_aItem.m_lLeft + m_aItem.m_lWidth )); + const long lY2 = lY0 + ROUND(f * (m_aItem.m_lUpper + m_aItem.m_lHeight)); + const long lX3 = lX0 + ROUND(f * (m_aItem.m_lLeft + m_aItem.m_lHDist )); + const long lY3 = lY0 + ROUND(f * (m_aItem.m_lUpper + m_aItem.m_lVDist )); + + // draw outline (area) + rRenderContext.DrawRect(tools::Rectangle(Point(lX0, lY0), Size(lOutlineW, lOutlineH))); + + // draw outline (border) + rRenderContext.SetLineColor(rFieldTextColor); + rRenderContext.DrawLine(Point(lX0, lY0), Point(lX0 + lOutlineW - 1, lY0)); // Up + rRenderContext.DrawLine(Point(lX0, lY0), Point(lX0, lY0 + lOutlineH - 1)); // Left + if (m_aItem.m_nCols == 1) + rRenderContext.DrawLine(Point(lX0 + lOutlineW - 1, lY0), Point(lX0 + lOutlineW - 1, lY0 + lOutlineH - 1)); // Right + if (m_aItem.m_nRows == 1) + rRenderContext.DrawLine(Point(lX0, lY0 + lOutlineH - 1), Point(lX0 + lOutlineW - 1, lY0 + lOutlineH - 1)); // Down + + // Labels + rRenderContext.SetClipRegion(vcl::Region(tools::Rectangle(Point(lX0, lY0), Size(lOutlineW, lOutlineH)))); + rRenderContext.SetFillColor(COL_LIGHTGRAYBLUE); + const sal_Int32 nRows = std::min<sal_Int32>(2, m_aItem.m_nRows); + const sal_Int32 nCols = std::min<sal_Int32>(2, m_aItem.m_nCols); + for (sal_Int32 nRow = 0; nRow < nRows; ++nRow) + for (sal_Int32 nCol = 0; nCol < nCols; ++nCol) + rRenderContext.DrawRect(tools::Rectangle(Point(lX0 + ROUND(f * (m_aItem.m_lLeft + nCol * m_aItem.m_lHDist)), + lY0 + ROUND(f * (m_aItem.m_lUpper + nRow * m_aItem.m_lVDist))), + Size(ROUND(f * m_aItem.m_lWidth), + ROUND(f * m_aItem.m_lHeight)))); + rRenderContext.SetClipRegion(); + + // annotation: left border + if (m_aItem.m_lLeft) + { + long lX = (lX0 + lX1) / 2; + DrawArrow(rRenderContext, Point(lX0, lY0 - 5), Point(lX1, lY0 - 5), false); + DrawArrow(rRenderContext, Point(lX, lY0 - 10), Point(lX, lY0 - 5), true); + rRenderContext.DrawText(Point(lX1 - m_lLeftWidth, lY0 - 10 - m_lXHeight), m_aLeftStr); + } + + // annotation: upper border + if (m_aItem.m_lUpper) + { + DrawArrow(rRenderContext, Point(lX0 - 5, lY0), Point(lX0 - 5, lY1), false); + rRenderContext.DrawText(Point(lX0 - 10 - m_lUpperWidth, lY0 + ROUND(f*m_aItem.m_lUpper/2.0 - m_lXHeight/2.0)), m_aUpperStr); + } + + // annotation: width and height + { + long lX = lX2 - m_lXWidth / 2 - m_lHeightWidth / 2; + long lY = lY1 + m_lXHeight; + + rRenderContext.DrawLine(Point(lX1, lY), Point(lX2 - 1, lY)); + rRenderContext.DrawLine(Point(lX, lY1), Point(lX, lY2 - 1)); + + rRenderContext.DrawText(Point(lX1 + m_lXWidth / 2, lY - m_lXHeight / 2), m_aWidthStr); + rRenderContext.DrawText(Point(lX - m_lHeightWidth / 2, lY2 - m_lXHeight - m_lXHeight / 2), m_aHeightStr); + } + + // annotation: horizontal gap + if (m_aItem.m_nCols > 1) + { + long lX = (lX1 + lX3) / 2; + DrawArrow(rRenderContext, Point(lX1, lY0 - 5), Point(lX3, lY0 - 5), false); + DrawArrow(rRenderContext, Point(lX, lY0 - 10), Point(lX, lY0 - 5), true); + rRenderContext.DrawText(Point(lX - m_lHDistWidth / 2, lY0 - 10 - m_lXHeight), m_aHDistStr); + } + + // annotation: vertical gap + if (m_aItem.m_nRows > 1) + { + DrawArrow(rRenderContext, Point(lX0 - 5, lY1), Point(lX0 - 5, lY3), false); + rRenderContext.DrawText(Point(lX0 - 10 - m_lVDistWidth, lY1 + ROUND(f*m_aItem.m_lVDist/2.0 - m_lXHeight/2.0)), m_aVDistStr); + } + + // annotation: columns + { + long lY = lY0 + lOutlineH + 4; + DrawArrow(rRenderContext, Point(lX0, lY), Point(lX0 + lOutlineW - 1, lY), true); + rRenderContext.DrawText(Point((lX0 + lX0 + lOutlineW - 1) / 2 - m_lColsWidth / 2, lY + 5), m_aColsStr); + } + + // annotation: lines + { + long lX = lX0 + lOutlineW + 4; + DrawArrow(rRenderContext, Point(lX, lY0), Point(lX, lY0 + lOutlineH - 1), true); + rRenderContext.DrawText(Point(lX + 5, (lY0 + lY0 + lOutlineH - 1 - m_lXHeight / 2) / 2), m_aRowsStr); + } +} + +void SwLabPreview::UpdateItem(const SwLabItem& rItem) +{ + m_aItem = rItem; + Invalidate(); +} + +SwLabFormatPage::SwLabFormatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/labelformatpage.ui", "LabelFormatPage", &rSet) + , aPreviewIdle("SwLabFormatPage Preview") + , aItem(static_cast<const SwLabItem&>( rSet.Get(FN_LABEL) )) + , bModified(false) + , m_xMakeFI(m_xBuilder->weld_label("make")) + , m_xTypeFI(m_xBuilder->weld_label("type")) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreview)) + , m_xHDistField(m_xBuilder->weld_metric_spin_button("hori", FieldUnit::CM)) + , m_xVDistField(m_xBuilder->weld_metric_spin_button("vert", FieldUnit::CM)) + , m_xWidthField(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM)) + , m_xHeightField(m_xBuilder->weld_metric_spin_button("height", FieldUnit::CM)) + , m_xLeftField(m_xBuilder->weld_metric_spin_button("left", FieldUnit::CM)) + , m_xUpperField(m_xBuilder->weld_metric_spin_button("top", FieldUnit::CM)) + , m_xColsField(m_xBuilder->weld_spin_button("cols")) + , m_xRowsField(m_xBuilder->weld_spin_button("rows")) + , m_xPWidthField(m_xBuilder->weld_metric_spin_button("pagewidth", FieldUnit::CM)) + , m_xPHeightField(m_xBuilder->weld_metric_spin_button("pageheight", FieldUnit::CM)) + , m_xSavePB(m_xBuilder->weld_button("save")) +{ + SetExchangeSupport(); + + // Metrics + FieldUnit aMetric = ::GetDfltMetric(false); + ::SetFieldUnit(*m_xHDistField, aMetric); + ::SetFieldUnit(*m_xVDistField , aMetric); + ::SetFieldUnit(*m_xWidthField , aMetric); + ::SetFieldUnit(*m_xHeightField, aMetric); + ::SetFieldUnit(*m_xLeftField , aMetric); + ::SetFieldUnit(*m_xUpperField , aMetric); + ::SetFieldUnit(*m_xPWidthField , aMetric); + ::SetFieldUnit(*m_xPHeightField, aMetric); + + // Install handlers + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwLabFormatPage, MetricModifyHdl); + m_xHDistField->connect_value_changed( aLk ); + m_xVDistField->connect_value_changed( aLk ); + m_xWidthField->connect_value_changed( aLk ); + m_xHeightField->connect_value_changed( aLk ); + m_xLeftField->connect_value_changed( aLk ); + m_xUpperField->connect_value_changed( aLk ); + m_xPWidthField->connect_value_changed( aLk ); + m_xPHeightField->connect_value_changed( aLk ); + + m_xColsField->connect_value_changed(LINK(this, SwLabFormatPage, ModifyHdl)); + m_xRowsField->connect_value_changed(LINK(this, SwLabFormatPage, ModifyHdl)); + + m_xSavePB->connect_clicked( LINK (this, SwLabFormatPage, SaveHdl)); + // Set timer + aPreviewIdle.SetPriority(TaskPriority::LOWEST); + aPreviewIdle.SetInvokeHandler(LINK(this, SwLabFormatPage, PreviewHdl)); +} + +SwLabFormatPage::~SwLabFormatPage() +{ +} + +// Modify-handler of MetricFields. start preview timer +IMPL_LINK_NOARG(SwLabFormatPage, MetricModifyHdl, weld::MetricSpinButton&, void) +{ + bModified = true; + aPreviewIdle.Start(); +} + +IMPL_LINK_NOARG(SwLabFormatPage, ModifyHdl, weld::SpinButton&, void) +{ + bModified = true; + aPreviewIdle.Start(); +} + +// Invalidate preview +IMPL_LINK_NOARG(SwLabFormatPage, PreviewHdl, Timer *, void) +{ + aPreviewIdle.Stop(); + ChangeMinMax(); + FillItem( aItem ); + m_aPreview.UpdateItem(aItem); +} + +void SwLabFormatPage::ChangeMinMax() +{ + long lMax = 31748; // 56 cm + long nMinSize = 10; // 0,1cm + + // Min and Max + + int nCols = m_xColsField->get_value(), + nRows = m_xRowsField->get_value(); + long lLeft = static_cast< long >(getfldval(*m_xLeftField )), + lUpper = static_cast< long >(getfldval(*m_xUpperField)), + lHDist = static_cast< long >(getfldval(*m_xHDistField)), + lVDist = static_cast< long >(getfldval(*m_xVDistField)), + lWidth = static_cast< long >(getfldval(*m_xWidthField)), + lHeight = static_cast< long >(getfldval(*m_xHeightField)), + lMinPWidth = lLeft + (nCols - 1) * lHDist + lWidth, + lMinPHeight = lUpper + (nRows - 1) * lVDist + lHeight; + + m_xHDistField->set_min(nMinSize, FieldUnit::CM); + m_xVDistField->set_min(nMinSize, FieldUnit::CM); + + m_xHDistField->set_max(long(100) * ((lMax - lLeft ) / std::max(1L, static_cast<long>(nCols))), FieldUnit::TWIP); + m_xVDistField->set_max(long(100) * ((lMax - lUpper) / std::max(1L, static_cast<long>(nRows))), FieldUnit::TWIP); + + m_xWidthField->set_min(nMinSize, FieldUnit::CM); + m_xHeightField->set_min(nMinSize, FieldUnit::CM); + + m_xWidthField->set_max(long(100) * lHDist, FieldUnit::TWIP); + m_xHeightField->set_max(long(100) * lVDist, FieldUnit::TWIP); + + m_xLeftField->set_max(long(100) * (lMax - nCols * lHDist), FieldUnit::TWIP); + m_xUpperField->set_max(long(100) * (lMax - nRows * lVDist), FieldUnit::TWIP); + + m_xColsField->set_range(1, (lMax - lLeft ) / std::max(1L, lHDist)); + m_xRowsField->set_range(1, (lMax - lUpper) / std::max(1L, lVDist)); + + m_xPWidthField->set_range(long(100) * lMinPWidth, long(100) * lMax, FieldUnit::TWIP); + m_xPHeightField->set_range(long(100) * lMinPHeight, long(100) * lMax, FieldUnit::TWIP); +} + +std::unique_ptr<SfxTabPage> SwLabFormatPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwLabFormatPage>(pPage, pController, *rSet); +} + +void SwLabFormatPage::ActivatePage(const SfxItemSet& rSet) +{ + SfxItemSet aSet(rSet); + Reset(&aSet); +} + +DeactivateRC SwLabFormatPage::DeactivatePage(SfxItemSet* _pSet) +{ + if (_pSet) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +void SwLabFormatPage::FillItem(SwLabItem& rItem) +{ + if (!bModified) + return; + + rItem.m_aMake = rItem.m_aType = SwResId(STR_CUSTOM_LABEL); + + SwLabRec& rRec = *GetParentSwLabDlg()->Recs()[0]; + rItem.m_lHDist = rRec.m_nHDist = static_cast< long >(getfldval(*m_xHDistField )); + rItem.m_lVDist = rRec.m_nVDist = static_cast< long >(getfldval(*m_xVDistField )); + rItem.m_lWidth = rRec.m_nWidth = static_cast< long >(getfldval(*m_xWidthField )); + rItem.m_lHeight = rRec.m_nHeight = static_cast< long >(getfldval(*m_xHeightField)); + rItem.m_lLeft = rRec.m_nLeft = static_cast< long >(getfldval(*m_xLeftField )); + rItem.m_lUpper = rRec.m_nUpper = static_cast< long >(getfldval(*m_xUpperField )); + rItem.m_nCols = rRec.m_nCols = static_cast< sal_Int32 >(m_xColsField->get_value()); + rItem.m_nRows = rRec.m_nRows = static_cast< sal_Int32 >(m_xRowsField->get_value()); + rItem.m_lPWidth = rRec.m_nPWidth = static_cast< long >(getfldval(*m_xPWidthField )); + rItem.m_lPHeight = rRec.m_nPHeight = static_cast< long >(getfldval(*m_xPHeightField)); + +} + +bool SwLabFormatPage::FillItemSet(SfxItemSet* rSet) +{ + FillItem(aItem); + rSet->Put(aItem); + + return true; +} + +void SwLabFormatPage::Reset(const SfxItemSet* ) +{ + // Initialise fields + GetParentSwLabDlg()->GetLabItem(aItem); + + m_xHDistField->set_max(100 * aItem.m_lHDist , FieldUnit::TWIP); + m_xVDistField->set_max(100 * aItem.m_lVDist , FieldUnit::TWIP); + m_xWidthField->set_max(100 * aItem.m_lWidth , FieldUnit::TWIP); + m_xHeightField->set_max(100 * aItem.m_lHeight, FieldUnit::TWIP); + m_xLeftField->set_max(100 * aItem.m_lLeft , FieldUnit::TWIP); + m_xUpperField->set_max(100 * aItem.m_lUpper , FieldUnit::TWIP); + m_xPWidthField->set_max(100 * aItem.m_lPWidth , FieldUnit::TWIP); + m_xPHeightField->set_max(100 * aItem.m_lPHeight, FieldUnit::TWIP); + + setfldval(*m_xHDistField, aItem.m_lHDist ); + setfldval(*m_xVDistField , aItem.m_lVDist ); + setfldval(*m_xWidthField , aItem.m_lWidth ); + setfldval(*m_xHeightField, aItem.m_lHeight); + setfldval(*m_xLeftField , aItem.m_lLeft ); + setfldval(*m_xUpperField , aItem.m_lUpper ); + setfldval(*m_xPWidthField , aItem.m_lPWidth ); + setfldval(*m_xPHeightField, aItem.m_lPHeight); + + m_xColsField->set_max(aItem.m_nCols); + m_xRowsField->set_max(aItem.m_nRows); + + m_xColsField->set_value(aItem.m_nCols); + m_xRowsField->set_value(aItem.m_nRows); + m_xMakeFI->set_label(aItem.m_aMake); + m_xTypeFI->set_label(aItem.m_aType); + PreviewHdl(nullptr); +} + +IMPL_LINK_NOARG(SwLabFormatPage, SaveHdl, weld::Button&, void) +{ + SwLabRec aRec; + aRec.m_nHDist = static_cast< long >(getfldval(*m_xHDistField)); + aRec.m_nVDist = static_cast< long >(getfldval(*m_xVDistField )); + aRec.m_nWidth = static_cast< long >(getfldval(*m_xWidthField )); + aRec.m_nHeight = static_cast< long >(getfldval(*m_xHeightField)); + aRec.m_nLeft = static_cast< long >(getfldval(*m_xLeftField )); + aRec.m_nUpper = static_cast< long >(getfldval(*m_xUpperField )); + aRec.m_nCols = static_cast< sal_Int32 >(m_xColsField->get_value()); + aRec.m_nRows = static_cast< sal_Int32 >(m_xRowsField->get_value()); + aRec.m_nPWidth = static_cast< long >(getfldval(*m_xPWidthField )); + aRec.m_nPHeight = static_cast< long >(getfldval(*m_xPHeightField)); + aRec.m_bCont = aItem.m_bCont; + SwSaveLabelDlg aSaveDlg(GetParentSwLabDlg(), aRec); + aSaveDlg.SetLabel(aItem.m_aLstMake, aItem.m_aLstType); + aSaveDlg.run(); + if (aSaveDlg.GetLabel(aItem)) + { + bModified = false; + const std::vector<OUString>& rMan = GetParentSwLabDlg()->GetLabelsConfig().GetManufacturers(); + std::vector<OUString>& rMakes(GetParentSwLabDlg()->Makes()); + if(rMakes.size() < rMan.size()) + { + rMakes = rMan; + } + m_xMakeFI->set_label(aItem.m_aMake); + m_xTypeFI->set_label(aItem.m_aType); + } +} + +SwSaveLabelDlg::SwSaveLabelDlg(SwLabDlg* pParent, SwLabRec& rRec) + : GenericDialogController(pParent->getDialog(), "modules/swriter/ui/savelabeldialog.ui", "SaveLabelDialog") + , bSuccess(false) + , m_pLabDialog(pParent) + , rLabRec(rRec) + , m_xMakeCB(m_xBuilder->weld_combo_box("brand")) + , m_xTypeED(m_xBuilder->weld_entry("type")) + , m_xOKPB(m_xBuilder->weld_button("ok")) +{ + m_xOKPB->connect_clicked(LINK(this, SwSaveLabelDlg, OkHdl)); + m_xMakeCB->connect_changed(LINK(this, SwSaveLabelDlg, ModifyComboHdl)); + m_xTypeED->connect_changed(LINK(this, SwSaveLabelDlg, ModifyEntryHdl)); + + SwLabelConfig& rCfg = m_pLabDialog->GetLabelsConfig(); + const std::vector<OUString>& rMan = rCfg.GetManufacturers(); + for (const auto & i : rMan) + { + m_xMakeCB->append_text(i); + } +} + +SwSaveLabelDlg::~SwSaveLabelDlg() +{ +} + +IMPL_LINK_NOARG(SwSaveLabelDlg, OkHdl, weld::Button&, void) +{ + SwLabelConfig& rCfg = m_pLabDialog->GetLabelsConfig(); + OUString sMake(m_xMakeCB->get_active_text()); + OUString sType(m_xTypeED->get_text()); + if(rCfg.HasLabel(sMake, sType)) + { + if ( rCfg.IsPredefinedLabel(sMake, sType) ) + { + SAL_WARN( "sw.envelp", "label is predefined and cannot be overwritten" ); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "modules/swriter/ui/cannotsavelabeldialog.ui")); + std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("CannotSaveLabelDialog")); + xBox->run(); + return; + } + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "modules/swriter/ui/querysavelabeldialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("QuerySaveLabelDialog")); + xQuery->set_primary_text(xQuery->get_primary_text(). + replaceAll("%1", sMake).replaceAll("%2", sType)); + xQuery->set_secondary_text(xQuery->get_secondary_text(). + replaceAll("%1", sMake).replaceAll("%2", sType)); + + if (RET_YES != xQuery->run()) + return; + } + rLabRec.m_aType = sType; + rCfg.SaveLabel(sMake, sType, rLabRec); + bSuccess = true; + m_xDialog->response(RET_OK); +} + +void SwSaveLabelDlg::Modify() +{ + m_xOKPB->set_sensitive(!m_xMakeCB->get_active_text().isEmpty() && !m_xTypeED->get_text().isEmpty()); +} + +IMPL_LINK_NOARG(SwSaveLabelDlg, ModifyComboHdl, weld::ComboBox&, void) +{ + Modify(); +} + +IMPL_LINK_NOARG(SwSaveLabelDlg, ModifyEntryHdl, weld::Entry&, void) +{ + Modify(); +} + +bool SwSaveLabelDlg::GetLabel(SwLabItem& rItem) +{ + if(bSuccess) + { + rItem.m_aMake = m_xMakeCB->get_active_text(); + rItem.m_aType = m_xTypeED->get_text(); + rItem.m_lHDist = rLabRec.m_nHDist; + rItem.m_lVDist = rLabRec.m_nVDist; + rItem.m_lWidth = rLabRec.m_nWidth; + rItem.m_lHeight = rLabRec.m_nHeight; + rItem.m_lLeft = rLabRec.m_nLeft; + rItem.m_lUpper = rLabRec.m_nUpper; + rItem.m_nCols = rLabRec.m_nCols; + rItem.m_nRows = rLabRec.m_nRows; + rItem.m_lPWidth = rLabRec.m_nPWidth; + rItem.m_lPHeight = rLabRec.m_nPHeight; + } + return bSuccess; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/labfmt.hxx b/sw/source/ui/envelp/labfmt.hxx new file mode 100644 index 000000000..8e6d6a1a2 --- /dev/null +++ b/sw/source/ui/envelp/labfmt.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_UI_ENVELP_LABFMT_HXX +#define INCLUDED_SW_SOURCE_UI_ENVELP_LABFMT_HXX + +#include <labimg.hxx> +#include <label.hxx> + +#include <sfx2/tabdlg.hxx> +#include <vcl/idle.hxx> +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> + +class SwLabFormatPage; + +class SwLabPreview : public weld::CustomWidgetController +{ + Color m_aGrayColor; + + OUString m_aHDistStr; + OUString m_aVDistStr; + OUString m_aWidthStr; + OUString m_aHeightStr; + OUString m_aLeftStr; + OUString m_aUpperStr; + OUString m_aColsStr; + OUString m_aRowsStr; + + long m_lHDistWidth; + long m_lVDistWidth; + long m_lHeightWidth; + long m_lLeftWidth; + long m_lUpperWidth; + long m_lColsWidth; + + long m_lXWidth; + long m_lXHeight; + + SwLabItem m_aItem; + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + +public: + + SwLabPreview(); + + void UpdateItem(const SwLabItem& rItem); +}; + +class SwLabFormatPage : public SfxTabPage +{ + Idle aPreviewIdle; + SwLabItem aItem; + bool bModified; + + SwLabPreview m_aPreview; + + std::unique_ptr<weld::Label> m_xMakeFI; + std::unique_ptr<weld::Label> m_xTypeFI; + std::unique_ptr<weld::CustomWeld> m_xPreview; + std::unique_ptr<weld::MetricSpinButton> m_xHDistField; + std::unique_ptr<weld::MetricSpinButton> m_xVDistField; + std::unique_ptr<weld::MetricSpinButton> m_xWidthField; + std::unique_ptr<weld::MetricSpinButton> m_xHeightField; + std::unique_ptr<weld::MetricSpinButton> m_xLeftField; + std::unique_ptr<weld::MetricSpinButton> m_xUpperField; + std::unique_ptr<weld::SpinButton> m_xColsField; + std::unique_ptr<weld::SpinButton> m_xRowsField; + std::unique_ptr<weld::MetricSpinButton> m_xPWidthField; + std::unique_ptr<weld::MetricSpinButton> m_xPHeightField; + std::unique_ptr<weld::Button> m_xSavePB; + + + DECL_LINK( ModifyHdl, weld::SpinButton&, void ); + DECL_LINK( MetricModifyHdl, weld::MetricSpinButton&, void ); + DECL_LINK( PreviewHdl, Timer *, void ); + DECL_LINK( SaveHdl, weld::Button&, void ); + + void ChangeMinMax(); + +public: + SwLabFormatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwLabFormatPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwLabItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + + SwLabDlg* GetParentSwLabDlg() {return static_cast<SwLabDlg*>(GetDialogController());} +}; + +class SwSaveLabelDlg : public weld::GenericDialogController +{ + bool bSuccess; + SwLabDlg* m_pLabDialog; + SwLabRec& rLabRec; + + std::unique_ptr<weld::ComboBox> m_xMakeCB; + std::unique_ptr<weld::Entry> m_xTypeED; + std::unique_ptr<weld::Button> m_xOKPB; + + DECL_LINK(OkHdl, weld::Button&, void); + DECL_LINK(ModifyEntryHdl, weld::Entry&, void); + DECL_LINK(ModifyComboHdl, weld::ComboBox&, void); + + void Modify(); + +public: + SwSaveLabelDlg(SwLabDlg* pParent, SwLabRec& rRec); + virtual ~SwSaveLabelDlg() override; + + void SetLabel(const OUString& rMake, const OUString& rType) + { + m_xMakeCB->set_entry_text(rMake); + m_xTypeED->set_text(rType); + } + bool GetLabel(SwLabItem& rItem); +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/labprt.cxx b/sw/source/ui/envelp/labprt.cxx new file mode 100644 index 000000000..edede9718 --- /dev/null +++ b/sw/source/ui/envelp/labprt.cxx @@ -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 . + */ + +#include <osl/diagnose.h> +#include <svtools/prnsetup.hxx> +#include <unotools/cmdoptions.hxx> +#include <vcl/print.hxx> +#include <label.hxx> +#include "labprt.hxx" +#include <labimg.hxx> + +SwLabPrtPage::SwLabPrtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/labeloptionspage.ui", "LabelOptionsPage", &rSet) + , pPrinter(nullptr) + , m_xPageButton(m_xBuilder->weld_radio_button("entirepage")) + , m_xSingleButton(m_xBuilder->weld_radio_button("singlelabel")) + , m_xSingleGrid(m_xBuilder->weld_widget("singlegrid")) + , m_xPrinterFrame(m_xBuilder->weld_widget("printerframe")) + , m_xColField(m_xBuilder->weld_spin_button("cols")) + , m_xRowField(m_xBuilder->weld_spin_button("rows")) + , m_xSynchronCB(m_xBuilder->weld_check_button("synchronize")) + , m_xPrinterInfo(m_xBuilder->weld_label("printername")) + , m_xPrtSetup(m_xBuilder->weld_button("setup")) +{ + SetExchangeSupport(); + + // Install handlers + Link<weld::Button&,void> aLk = LINK(this, SwLabPrtPage, CountHdl); + m_xPageButton->connect_clicked( aLk ); + m_xSingleButton->connect_clicked( aLk ); + m_xPrtSetup->connect_clicked( aLk ); + + SvtCommandOptions aCmdOpts; + if (aCmdOpts.Lookup(SvtCommandOptions::CMDOPTION_DISABLED, "Print")) + { + m_xPrinterFrame->hide(); + } +} + +SwLabPrtPage::~SwLabPrtPage() +{ + pPrinter.disposeAndClear(); +} + +IMPL_LINK( SwLabPrtPage, CountHdl, weld::Button&, rButton, void ) +{ + if (&rButton == m_xPrtSetup.get()) + { + // Call printer setup + if (!pPrinter) + pPrinter = VclPtr<Printer>::Create(); + + PrinterSetupDialog aDlg(GetFrameWeld()); + aDlg.SetPrinter(pPrinter); + aDlg.run(); + rButton.grab_focus(); + m_xPrinterInfo->set_label(pPrinter->GetName()); + return; + } + const bool bEnable = &rButton == m_xSingleButton.get(); + m_xSingleGrid->set_sensitive(bEnable); + m_xSynchronCB->set_sensitive(!bEnable); + + OSL_ENSURE(!bEnable || &rButton == m_xPageButton.get(), "NewButton?" ); + if ( bEnable ) + { + m_xColField->grab_focus(); + } +} + +std::unique_ptr<SfxTabPage> SwLabPrtPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet) +{ + return std::make_unique<SwLabPrtPage>(pPage, pController, *rSet ); +} + +void SwLabPrtPage::ActivatePage( const SfxItemSet& rSet ) +{ + Reset(&rSet); +} + +DeactivateRC SwLabPrtPage::DeactivatePage(SfxItemSet* _pSet) +{ + if ( _pSet ) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +void SwLabPrtPage::FillItem(SwLabItem& rItem) +{ + rItem.m_bPage = m_xPageButton->get_active(); + rItem.m_nCol = m_xColField->get_value(); + rItem.m_nRow = m_xRowField->get_value(); + rItem.m_bSynchron = m_xSynchronCB->get_active() && m_xSynchronCB->get_sensitive(); +} + +bool SwLabPrtPage::FillItemSet(SfxItemSet* rSet) +{ + SwLabItem aItem; + GetParentSwLabDlg()->GetLabItem(aItem); + FillItem(aItem); + rSet->Put(aItem); + + return true; +} + +void SwLabPrtPage::Reset(const SfxItemSet* ) +{ + SwLabItem aItem; + GetParentSwLabDlg()->GetLabItem(aItem); + + m_xColField->set_value(aItem.m_nCol); + m_xRowField->set_value(aItem.m_nRow); + + if (aItem.m_bPage) + { + m_xPageButton->set_active(true); + CountHdl(*m_xPageButton); + } + else + { + CountHdl(*m_xSingleButton); + m_xSingleButton->set_active(true); + } + + if (pPrinter) + { + // show printer + m_xPrinterInfo->set_label(pPrinter->GetName()); + } + else + m_xPrinterInfo->set_label(Printer::GetDefaultPrinterName()); + + m_xColField->set_max(aItem.m_nCols); + m_xRowField->set_max(aItem.m_nRows); + + m_xSynchronCB->set_active(aItem.m_bSynchron); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/labprt.hxx b/sw/source/ui/envelp/labprt.hxx new file mode 100644 index 000000000..c69d44ea4 --- /dev/null +++ b/sw/source/ui/envelp/labprt.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_ENVELP_LABPRT_HXX +#define INCLUDED_SW_SOURCE_UI_ENVELP_LABPRT_HXX + +#include <sfx2/tabdlg.hxx> + +#include <label.hxx> + +class SwLabDlg; +class SwLabItem; + +class SwLabPrtPage : public SfxTabPage +{ + VclPtr<Printer> pPrinter; // for the shaft setting - unfortunately + + std::unique_ptr<weld::RadioButton> m_xPageButton; + std::unique_ptr<weld::RadioButton> m_xSingleButton; + std::unique_ptr<weld::Widget> m_xSingleGrid; + std::unique_ptr<weld::Widget> m_xPrinterFrame; + std::unique_ptr<weld::SpinButton> m_xColField; + std::unique_ptr<weld::SpinButton> m_xRowField; + std::unique_ptr<weld::CheckButton> m_xSynchronCB; + std::unique_ptr<weld::Label> m_xPrinterInfo; + std::unique_ptr<weld::Button> m_xPrtSetup; + + DECL_LINK( CountHdl, weld::Button&, void ); + + SwLabDlg* GetParentSwLabDlg() {return static_cast<SwLabDlg*>(GetDialogController());} + +public: + SwLabPrtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwLabPrtPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwLabItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + Printer* GetPrt() { return pPrinter; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/mailmrge.cxx b/sw/source/ui/envelp/mailmrge.cxx new file mode 100644 index 000000000..972f7d04a --- /dev/null +++ b/sw/source/ui/envelp/mailmrge.cxx @@ -0,0 +1,616 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/settings.hxx> + +#include <tools/urlobj.hxx> +#include <svl/urihelper.hxx> +#include <unotools/pathoptions.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <printdata.hxx> +#include <wrtsh.hxx> +#include <dbmgr.hxx> +#include <swmodule.hxx> +#include <modcfg.hxx> +#include <mailmergehelper.hxx> +#include <mailmrge.hxx> +#include <sfx2/docfile.hxx> +#include <comphelper/documentconstants.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/form/runtime/XFormController.hpp> +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/sdb/XResultSetAccess.hpp> +#include <com/sun/star/ui/dialogs/FolderPicker.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase.hxx> + +#include <algorithm> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::ui::dialogs; + +struct SwMailMergeDlg_Impl { + uno::Reference<runtime::XFormController> xFController; + uno::Reference<XSelectionChangeListener> xChgLstnr; + uno::Reference<XSelectionSupplier> xSelSupp; +}; + +class SwXSelChgLstnr_Impl : public cppu::WeakImplHelper + < + view::XSelectionChangeListener + > +{ + SwMailMergeDlg& rParent; +public: + explicit SwXSelChgLstnr_Impl(SwMailMergeDlg& rParentDlg); + + virtual void SAL_CALL selectionChanged( const EventObject& aEvent ) override; + virtual void SAL_CALL disposing( const EventObject& Source ) override; +}; + +SwXSelChgLstnr_Impl::SwXSelChgLstnr_Impl(SwMailMergeDlg& rParentDlg) : + rParent(rParentDlg) +{} + +void SwXSelChgLstnr_Impl::selectionChanged( const EventObject& ) +{ + //call the parent to enable selection mode + Sequence <Any> aSelection; + if(rParent.pImpl->xSelSupp.is()) + rParent.pImpl->xSelSupp->getSelection() >>= aSelection; + + bool bEnable = aSelection.hasElements(); + rParent.m_xMarkedRB->set_sensitive(bEnable); + if(bEnable) + rParent.m_xMarkedRB->set_active(true); + else if(rParent.m_xMarkedRB->get_active()) { + rParent.m_xAllRB->set_active(true); + rParent.m_aSelection.realloc(0); + } +} + +void SwXSelChgLstnr_Impl::disposing( const EventObject& ) +{ + OSL_FAIL("disposing"); +} + +SwMailMergeDlg::SwMailMergeDlg(weld::Window* pParent, SwWrtShell& rShell, + const OUString& rSourceName, + const OUString& rTableName, + sal_Int32 nCommandType, + const uno::Reference< XConnection>& _xConnection, + Sequence< Any > const * pSelection) + : SfxDialogController(pParent, "modules/swriter/ui/mailmerge.ui", "MailmergeDialog") + , pImpl(new SwMailMergeDlg_Impl) + , rSh(rShell) + , nMergeType(DBMGR_MERGE_EMAIL) + , m_xBeamerWin(m_xBuilder->weld_container("beamer")) + , m_xAllRB(m_xBuilder->weld_radio_button("all")) + , m_xMarkedRB(m_xBuilder->weld_radio_button("selected")) + , m_xFromRB(m_xBuilder->weld_radio_button("rbfrom")) + , m_xFromNF(m_xBuilder->weld_spin_button("from")) + , m_xToNF(m_xBuilder->weld_spin_button("to")) + , m_xPrinterRB(m_xBuilder->weld_radio_button("printer")) + , m_xMailingRB(m_xBuilder->weld_radio_button("electronic")) + , m_xFileRB(m_xBuilder->weld_radio_button("file")) + , m_xSingleJobsCB(m_xBuilder->weld_check_button("singlejobs")) + , m_xPasswordCB(m_xBuilder->weld_check_button("passwd-check")) + , m_xSaveMergedDocumentFT(m_xBuilder->weld_label("savemergeddoclabel")) + , m_xSaveSingleDocRB(m_xBuilder->weld_radio_button("singledocument")) + , m_xSaveIndividualRB(m_xBuilder->weld_radio_button("individualdocuments")) + , m_xGenerateFromDataBaseCB(m_xBuilder->weld_check_button("generate")) + , m_xColumnFT(m_xBuilder->weld_label("fieldlabel")) + , m_xColumnLB(m_xBuilder->weld_combo_box("field")) + , m_xPasswordFT(m_xBuilder->weld_label("passwd-label")) + , m_xPasswordLB(m_xBuilder->weld_combo_box("passwd-combobox")) + , m_xPathFT(m_xBuilder->weld_label("pathlabel")) + , m_xPathED(m_xBuilder->weld_entry("path")) + , m_xPathPB(m_xBuilder->weld_button("pathpb")) + , m_xFilterFT(m_xBuilder->weld_label("fileformatlabel")) + , m_xFilterLB(m_xBuilder->weld_combo_box("fileformat")) + , m_xAddressFieldLB(m_xBuilder->weld_combo_box("address")) + , m_xSubjectFT(m_xBuilder->weld_label("subjectlabel")) + , m_xSubjectED(m_xBuilder->weld_entry("subject")) + , m_xFormatFT(m_xBuilder->weld_label("mailformatlabel")) + , m_xAttachFT(m_xBuilder->weld_label("attachmentslabel")) + , m_xAttachED(m_xBuilder->weld_entry("attachments")) + , m_xAttachPB(m_xBuilder->weld_button("attach")) + , m_xFormatHtmlCB(m_xBuilder->weld_check_button("html")) + , m_xFormatRtfCB(m_xBuilder->weld_check_button("rtf")) + , m_xFormatSwCB(m_xBuilder->weld_check_button("swriter")) + , m_xOkBTN(m_xBuilder->weld_button("ok")) +{ + m_xSingleJobsCB->hide(); // not supported in since cws printerpullpages anymore + //task #97066# mailing of form letters is currently not supported + m_xMailingRB->hide(); + m_xSubjectFT->hide(); + m_xSubjectED->hide(); + m_xFormatFT->hide(); + m_xFormatSwCB->hide(); + m_xFormatHtmlCB->hide(); + m_xFormatRtfCB->hide(); + m_xAttachFT->hide(); + m_xAttachED->hide(); + m_xAttachPB->hide(); + m_xPasswordCB->hide(); + m_xPasswordFT->hide(); + m_xPasswordLB->hide(); + + uno::Reference< lang::XMultiServiceFactory > xMSF = comphelper::getProcessServiceFactory(); + if(pSelection) { + m_aSelection = *pSelection; + m_xBeamerWin->hide(); + } else { + try { + // create a frame wrapper for myself + m_xFrame = frame::Frame::create( comphelper::getProcessComponentContext() ); + m_xFrame->initialize(m_xBeamerWin->CreateChildFrame()); + } catch (const Exception&) { + m_xFrame.clear(); + } + if(m_xFrame.is()) { + URL aURL; + aURL.Complete = ".component:DB/DataSourceBrowser"; + uno::Reference<XDispatch> xD = m_xFrame->queryDispatch(aURL, + "", + 0x0C); + if(xD.is()) { + Sequence<PropertyValue> aProperties(3); + PropertyValue* pProperties = aProperties.getArray(); + pProperties[0].Name = "DataSourceName"; + pProperties[0].Value <<= rSourceName; + pProperties[1].Name = "Command"; + pProperties[1].Value <<= rTableName; + pProperties[2].Name = "CommandType"; + pProperties[2].Value <<= nCommandType; + xD->dispatch(aURL, aProperties); + m_xBeamerWin->show(); + } + uno::Reference<XController> xController = m_xFrame->getController(); + pImpl->xFController.set(xController, UNO_QUERY); + if(pImpl->xFController.is()) { + uno::Reference< awt::XControl > xCtrl = pImpl->xFController->getCurrentControl( ); + pImpl->xSelSupp.set(xCtrl, UNO_QUERY); + if(pImpl->xSelSupp.is()) { + pImpl->xChgLstnr = new SwXSelChgLstnr_Impl(*this); + pImpl->xSelSupp->addSelectionChangeListener( pImpl->xChgLstnr ); + } + } + } + } + + pModOpt = SW_MOD()->GetModuleConfig(); + + MailTextFormats nMailingMode(pModOpt->GetMailingFormats()); + m_xFormatSwCB->set_active(bool(nMailingMode & MailTextFormats::OFFICE)); + m_xFormatHtmlCB->set_active(bool(nMailingMode & MailTextFormats::HTML)); + m_xFormatRtfCB->set_active(bool(nMailingMode & MailTextFormats::RTF)); + + m_xAllRB->set_active(true); + + // Install handlers + m_xOkBTN->connect_clicked(LINK(this, SwMailMergeDlg, ButtonHdl)); + + m_xPathPB->connect_clicked(LINK(this, SwMailMergeDlg, InsertPathHdl)); + + m_xPrinterRB->connect_toggled(LINK(this, SwMailMergeDlg, OutputTypeHdl)); + m_xFileRB->connect_toggled(LINK(this, SwMailMergeDlg, OutputTypeHdl)); + + //#i63267# printing might be disabled + bool bIsPrintable = !Application::GetSettings().GetMiscSettings().GetDisablePrinting(); + m_xPrinterRB->set_sensitive(bIsPrintable); + OutputTypeHdl(bIsPrintable ? *m_xPrinterRB : *m_xFileRB); + + m_xGenerateFromDataBaseCB->connect_toggled(LINK(this, SwMailMergeDlg, FilenameHdl)); + bool bColumn = pModOpt->IsNameFromColumn(); + if(bColumn) + m_xGenerateFromDataBaseCB->set_active(true); + + FilenameHdl(*m_xGenerateFromDataBaseCB); + m_xSaveSingleDocRB->set_active(true); + m_xSaveSingleDocRB->connect_toggled(LINK(this, SwMailMergeDlg, SaveTypeHdl)); + m_xSaveIndividualRB->connect_toggled(LINK(this, SwMailMergeDlg, SaveTypeHdl)); + SaveTypeHdl(*m_xSaveSingleDocRB); + + m_xFilterLB->connect_changed(LINK(this, SwMailMergeDlg, FileFormatHdl)); + + Link<weld::SpinButton&,void> aLk2 = LINK(this, SwMailMergeDlg, ModifyHdl); + m_xFromNF->connect_value_changed(aLk2); + m_xToNF->connect_value_changed(aLk2); + m_xFromNF->set_max(SAL_MAX_INT32); + m_xToNF->set_max(SAL_MAX_INT32); + + SwDBManager* pDBManager = rSh.GetDBManager(); + if(_xConnection.is()) + SwDBManager::GetColumnNames(*m_xAddressFieldLB, _xConnection, rTableName); + else + pDBManager->GetColumnNames(*m_xAddressFieldLB, rSourceName, rTableName); + for(sal_Int32 nEntry = 0, nEntryCount = m_xAddressFieldLB->get_count(); nEntry < nEntryCount; ++nEntry) + { + m_xColumnLB->append_text(m_xAddressFieldLB->get_text(nEntry)); + m_xPasswordLB->append_text(m_xAddressFieldLB->get_text(nEntry)); + } + + m_xAddressFieldLB->set_active_text("EMAIL"); + + OUString sPath(pModOpt->GetMailingPath()); + if(sPath.isEmpty()) { + SvtPathOptions aPathOpt; + sPath = aPathOpt.GetWorkPath(); + } + INetURLObject aURL(sPath); + if(aURL.GetProtocol() == INetProtocol::File) + m_xPathED->set_text(aURL.PathToFileName()); + else + m_xPathED->set_text(aURL.GetFull()); + + if (!bColumn ) + { + m_xColumnLB->set_active_text("NAME"); + m_xPasswordLB->set_active_text("PASSWORD"); + } + else + { + m_xColumnLB->set_active_text(pModOpt->GetNameFromColumn()); + m_xPasswordLB->set_active_text(pModOpt->GetPasswordFromColumn()); + } + + if (m_xAddressFieldLB->get_active() == -1) + m_xAddressFieldLB->set_active(0); + if (m_xColumnLB->get_active() == -1) + m_xColumnLB->set_active(0); + if (m_xPasswordLB->get_active() == -1) + m_xPasswordLB->set_active(0); + + const bool bEnable = m_aSelection.hasElements(); + m_xMarkedRB->set_sensitive(bEnable); + if (bEnable) + m_xMarkedRB->set_active(true); + else { + m_xAllRB->set_active(true); + m_xMarkedRB->set_sensitive(false); + } + try { + uno::Reference< container::XNameContainer> xFilterFactory( + xMSF->createInstance("com.sun.star.document.FilterFactory"), UNO_QUERY_THROW); + uno::Reference< container::XContainerQuery > xQuery(xFilterFactory, UNO_QUERY_THROW); + const OUString sCommand("matchByDocumentService=com.sun.star.text.TextDocument:iflags=" + + OUString::number(static_cast<sal_Int32>(SfxFilterFlags::EXPORT)) + + ":eflags=" + + OUString::number(static_cast<sal_Int32>(SfxFilterFlags::NOTINFILEDLG)) + + ":default_first"); + uno::Reference< container::XEnumeration > xList = xQuery->createSubSetEnumerationByQuery(sCommand); + const OUString sName("Name"); + sal_Int32 nODT = -1; + while(xList->hasMoreElements()) { + comphelper::SequenceAsHashMap aFilter(xList->nextElement()); + const OUString sFilter = aFilter.getUnpackedValueOrDefault(sName, OUString()); + + uno::Any aProps = xFilterFactory->getByName(sFilter); + uno::Sequence< beans::PropertyValue > aFilterProperties; + aProps >>= aFilterProperties; + OUString sUIName2; + auto pProp = std::find_if(aFilterProperties.begin(), aFilterProperties.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "UIName"; }); + if (pProp != aFilterProperties.end()) + pProp->Value >>= sUIName2; + if( !sUIName2.isEmpty() ) { + if( sFilter == "writer8" ) + nODT = m_xFilterLB->get_count(); + m_xFilterLB->append(sFilter, sUIName2); + } + } + m_xFilterLB->set_active( nODT ); + } catch (const uno::Exception&) { + } +} + +SwMailMergeDlg::~SwMailMergeDlg() +{ + if(m_xFrame.is()) { + m_xFrame->setComponent(nullptr, nullptr); + m_xFrame->dispose(); + } +} + +IMPL_LINK_NOARG(SwMailMergeDlg, ButtonHdl, weld::Button&, void) +{ + if (ExecQryShell()) + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SwMailMergeDlg, OutputTypeHdl, weld::ToggleButton&, void) +{ + bool bPrint = m_xPrinterRB->get_active(); + m_xSingleJobsCB->set_sensitive(bPrint); + + m_xSaveMergedDocumentFT->set_sensitive( !bPrint ); + m_xSaveSingleDocRB->set_sensitive( !bPrint ); + m_xSaveIndividualRB->set_sensitive( !bPrint ); + + if( !bPrint ) { + SaveTypeHdl( m_xSaveSingleDocRB->get_active() ? *m_xSaveSingleDocRB : *m_xSaveIndividualRB ); + } else { + m_xPathFT->set_sensitive(false); + m_xPathED->set_sensitive(false); + m_xPathPB->set_sensitive(false); + m_xColumnFT->set_sensitive(false); + m_xColumnLB->set_sensitive(false); + m_xFilterFT->set_sensitive(false); + m_xFilterLB->set_sensitive(false); + m_xGenerateFromDataBaseCB->set_sensitive(false); + m_xPasswordCB->set_sensitive( false ); + m_xPasswordFT->set_sensitive( false ); + m_xPasswordLB->set_sensitive( false ); + } +} + +IMPL_LINK_NOARG(SwMailMergeDlg, SaveTypeHdl, weld::ToggleButton&, void) +{ + bool bIndividual = m_xSaveIndividualRB->get_active(); + + m_xGenerateFromDataBaseCB->set_sensitive( bIndividual ); + if( bIndividual ) + { + FilenameHdl(*m_xGenerateFromDataBaseCB); + } + else + { + m_xColumnFT->set_sensitive(false); + m_xColumnLB->set_sensitive(false); + m_xPathFT->set_sensitive( false ); + m_xPathED->set_sensitive( false ); + m_xPathPB->set_sensitive( false ); + m_xFilterFT->set_sensitive( false ); + m_xFilterLB->set_sensitive( false ); + m_xPasswordCB->set_sensitive( false ); + m_xPasswordFT->set_sensitive( false ); + m_xPasswordLB->set_sensitive( false ); + } +} + +IMPL_LINK( SwMailMergeDlg, FilenameHdl, weld::ToggleButton&, rBox, void ) +{ + bool bEnable = rBox.get_active(); + m_xColumnFT->set_sensitive( bEnable ); + m_xColumnLB->set_sensitive(bEnable); + m_xPathFT->set_sensitive( bEnable ); + m_xPathED->set_sensitive(bEnable); + m_xPathPB->set_sensitive( bEnable ); + m_xFilterFT->set_sensitive( bEnable ); + m_xFilterLB->set_sensitive( bEnable ); + + if(m_xFilterLB->get_active_id() == "writer_pdf_Export") + { + m_xPasswordCB->show(); + m_xPasswordFT->show(); + m_xPasswordLB->show(); + + m_xPasswordCB->set_sensitive( bEnable ); + m_xPasswordFT->set_sensitive( bEnable ); + m_xPasswordLB->set_sensitive( bEnable ); + } +} + +IMPL_LINK_NOARG( SwMailMergeDlg, FileFormatHdl, weld::ComboBox&, void ) +{ + if(m_xFilterLB->get_active_id() == "writer_pdf_Export") + { + m_xPasswordCB->show(); + m_xPasswordFT->show(); + m_xPasswordLB->show(); + + m_xPasswordCB->set_sensitive( true ); + m_xPasswordFT->set_sensitive( true ); + m_xPasswordLB->set_sensitive( true ); + } + else + { + m_xPasswordCB->hide(); + m_xPasswordFT->hide(); + m_xPasswordLB->hide(); + } +} + +IMPL_LINK_NOARG(SwMailMergeDlg, ModifyHdl, weld::SpinButton&, void) +{ + m_xFromRB->set_active(true); +} + +bool SwMailMergeDlg::AskUserFilename() const +{ + return (m_xSaveSingleDocRB->get_active() || !m_xGenerateFromDataBaseCB->get_active()); +} + +OUString SwMailMergeDlg::GetURLfromPath() const +{ + SfxMedium* pMedium = rSh.GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + if( INetProtocol::NotValid == aAbs.GetProtocol() ) + { + SvtPathOptions aPathOpt; + aAbs.SetURL( aPathOpt.GetWorkPath() ); + } + return URIHelper::SmartRel2Abs( + aAbs, m_xPathED->get_text(), URIHelper::GetMaybeFileHdl()); +} + +bool SwMailMergeDlg::ExecQryShell() +{ + if(pImpl->xSelSupp.is()) { + pImpl->xSelSupp->removeSelectionChangeListener( pImpl->xChgLstnr ); + } + + if (m_xPrinterRB->get_active()) + nMergeType = DBMGR_MERGE_PRINTER; + else { + nMergeType = DBMGR_MERGE_FILE; + pModOpt->SetMailingPath( GetURLfromPath() ); + pModOpt->SetIsNameFromColumn(m_xGenerateFromDataBaseCB->get_active()); + pModOpt->SetIsFileEncryptedFromColumn(m_xPasswordCB->get_active()); + + if (!AskUserFilename()) + { + pModOpt->SetNameFromColumn(m_xColumnLB->get_active_text()); + pModOpt->SetPasswordFromColumn(m_xPasswordLB->get_active_text()); + if (m_xFilterLB->get_active() != -1) + m_sSaveFilter = m_xFilterLB->get_active_id(); + m_sFilename = OUString(); + } else { + //#i97667# reset column name - otherwise it's remembered from the last run + pModOpt->SetNameFromColumn(OUString()); + pModOpt->SetPasswordFromColumn(OUString()); + //start save as dialog + OUString sFilter; + m_sFilename = SwMailMergeHelper::CallSaveAsDialog(m_xDialog.get(), sFilter); + if (m_sFilename.isEmpty()) + return false; + m_sSaveFilter = sFilter; + } + } + + if (m_xFromRB->get_active()) { // Insert list + // Safe: the maximal value of the fields is limited + sal_Int32 nStart = m_xFromNF->get_value(); + sal_Int32 nEnd = m_xToNF->get_value(); + + if (nEnd < nStart) + std::swap(nEnd, nStart); + + m_aSelection.realloc(nEnd - nStart + 1); + Any* pSelection = m_aSelection.getArray(); + for (sal_Int32 i = nStart; i <= nEnd; ++i, ++pSelection) + *pSelection <<= i; + } else if (m_xAllRB->get_active() ) + m_aSelection.realloc(0); // Empty selection = insert all + else { + if(pImpl->xSelSupp.is()) { + //update selection + uno::Reference< XRowLocate > xRowLocate(GetResultSet(),UNO_QUERY); + uno::Reference< XResultSet > xRes(xRowLocate,UNO_QUERY); + pImpl->xSelSupp->getSelection() >>= m_aSelection; + if ( xRowLocate.is() ) { + for (Any& rRow : m_aSelection) { + if ( xRowLocate->moveToBookmark(rRow) ) + rRow <<= xRes->getRow(); + } + } + } + } + IDocumentDeviceAccess& rIDDA = rSh.getIDocumentDeviceAccess(); + SwPrintData aPrtData( rIDDA.getPrintData() ); + aPrtData.SetPrintSingleJobs(m_xSingleJobsCB->get_active()); + rIDDA.setPrintData(aPrtData); + + pModOpt->SetSinglePrintJob(m_xSingleJobsCB->get_active()); + + MailTextFormats nMailingMode = MailTextFormats::NONE; + + if (m_xFormatSwCB->get_active()) + nMailingMode |= MailTextFormats::OFFICE; + if (m_xFormatHtmlCB->get_active()) + nMailingMode |= MailTextFormats::HTML; + if (m_xFormatRtfCB->get_active()) + nMailingMode |= MailTextFormats::RTF; + pModOpt->SetMailingFormats(nMailingMode); + return true; +} + +OUString SwMailMergeDlg::GetTargetURL() const +{ + if( AskUserFilename() ) + return m_sFilename; + OUString sPath( pModOpt->GetMailingPath() ); + if( sPath.isEmpty() ) { + SvtPathOptions aPathOpt; + sPath = aPathOpt.GetWorkPath(); + } + if( !sPath.endsWith("/") ) + sPath += "/"; + return sPath; +} + +IMPL_LINK_NOARG(SwMailMergeDlg, InsertPathHdl, weld::Button&, void) +{ + uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference < XFolderPicker2 > xFP = FolderPicker::create(xContext); + xFP->setDisplayDirectory( GetURLfromPath() ); + if( xFP->execute() == RET_OK ) { + INetURLObject aURL(xFP->getDirectory()); + if(aURL.GetProtocol() == INetProtocol::File) + m_xPathED->set_text(aURL.PathToFileName()); + else + m_xPathED->set_text(aURL.GetFull()); + } +} + +uno::Reference<XResultSet> SwMailMergeDlg::GetResultSet() const +{ + uno::Reference< XResultSet > xResSetClone; + if ( pImpl->xFController.is() ) { + // we create a clone to do the task + uno::Reference< XResultSetAccess > xResultSetAccess( pImpl->xFController->getModel(),UNO_QUERY); + if ( xResultSetAccess.is() ) + xResSetClone = xResultSetAccess->createResultSet(); + } + return xResSetClone; +} + +SwMailMergeCreateFromDlg::SwMailMergeCreateFromDlg(weld::Window* pParent) + : GenericDialogController(pParent, "modules/swriter/ui/mailmergedialog.ui", "MailMergeDialog") + , m_xThisDocRB(m_xBuilder->weld_radio_button("document")) +{ +} + +SwMailMergeCreateFromDlg::~SwMailMergeCreateFromDlg() +{ +} + +SwMailMergeFieldConnectionsDlg::SwMailMergeFieldConnectionsDlg(weld::Window* pParent) + : GenericDialogController(pParent, "modules/swriter/ui/mergeconnectdialog.ui", "MergeConnectDialog") + , m_xUseExistingRB(m_xBuilder->weld_radio_button("existing")) +{ +} + +SwMailMergeFieldConnectionsDlg::~SwMailMergeFieldConnectionsDlg() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/envelp/swuilabimp.hxx b/sw/source/ui/envelp/swuilabimp.hxx new file mode 100644 index 000000000..5634b4c2c --- /dev/null +++ b/sw/source/ui/envelp/swuilabimp.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_UI_ENVELP_SWUILABIMP_HXX +#define INCLUDED_SW_SOURCE_UI_ENVELP_SWUILABIMP_HXX + +#include <label.hxx> +#include <labimg.hxx> +#include <labrec.hxx> +#include <sfx2/tabdlg.hxx> + +class SwLabPage : public SfxTabPage +{ + SwDBManager* pDBManager; + OUString sActDBName; + SwLabItem aItem; + + std::unique_ptr<weld::Widget> m_xAddressFrame; + std::unique_ptr<weld::CheckButton> m_xAddrBox; + std::unique_ptr<weld::TextView> m_xWritingEdit; + std::unique_ptr<weld::ComboBox> m_xDatabaseLB; + std::unique_ptr<weld::ComboBox> m_xTableLB; + std::unique_ptr<weld::Button> m_xInsertBT; + std::unique_ptr<weld::ComboBox> m_xDBFieldLB; + std::unique_ptr<weld::RadioButton> m_xContButton; + std::unique_ptr<weld::RadioButton> m_xSheetButton; + std::unique_ptr<weld::ComboBox> m_xMakeBox; + std::unique_ptr<weld::ComboBox> m_xTypeBox; + std::unique_ptr<weld::ComboBox> m_xHiddenSortTypeBox; + std::unique_ptr<weld::Label> m_xFormatInfo; + + DECL_LINK(AddrHdl, weld::ToggleButton&, void); + DECL_LINK(DatabaseHdl, weld::ComboBox&, void ); + DECL_LINK(FieldHdl, weld::Button&, void); + DECL_LINK(PageHdl, weld::ToggleButton&, void); + DECL_LINK(MakeHdl, weld::ComboBox&, void); + DECL_LINK(TypeHdl, weld::ComboBox&, void); + + void DisplayFormat (); + SwLabRec* GetSelectedEntryPos(); + +public: + SwLabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + + virtual ~SwLabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwLabItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + + SwLabDlg* GetParentSwLabDlg() {return static_cast<SwLabDlg*>(GetDialogController());} + + void SetToBusinessCard(); + + void InitDatabaseBox(); + void SetDBManager(SwDBManager* pDBManager_) { pDBManager = pDBManager_; } + SwDBManager* GetDBManager() const { return pDBManager; } +}; + +class SwPrivateDataPage : public SfxTabPage +{ + std::unique_ptr<weld::Entry> m_xFirstNameED; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Entry> m_xShortCutED; + std::unique_ptr<weld::Entry> m_xFirstName2ED; + std::unique_ptr<weld::Entry> m_xName2ED; + std::unique_ptr<weld::Entry> m_xShortCut2ED; + std::unique_ptr<weld::Entry> m_xStreetED; + std::unique_ptr<weld::Entry> m_xZipED; + std::unique_ptr<weld::Entry> m_xCityED; + std::unique_ptr<weld::Entry> m_xCountryED; + std::unique_ptr<weld::Entry> m_xStateED; + std::unique_ptr<weld::Entry> m_xTitleED; + std::unique_ptr<weld::Entry> m_xProfessionED; + std::unique_ptr<weld::Entry> m_xPhoneED; + std::unique_ptr<weld::Entry> m_xMobilePhoneED; + std::unique_ptr<weld::Entry> m_xFaxED; + std::unique_ptr<weld::Entry> m_xHomePageED; + std::unique_ptr<weld::Entry> m_xMailED; + +public: + SwPrivateDataPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwPrivateDataPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; +}; + +class SwBusinessDataPage : public SfxTabPage +{ + std::unique_ptr<weld::Entry> m_xCompanyED; + std::unique_ptr<weld::Entry> m_xCompanyExtED; + std::unique_ptr<weld::Entry> m_xSloganED; + std::unique_ptr<weld::Entry> m_xStreetED; + std::unique_ptr<weld::Entry> m_xZipED; + std::unique_ptr<weld::Entry> m_xCityED; + std::unique_ptr<weld::Entry> m_xCountryED; + std::unique_ptr<weld::Entry> m_xStateED; + std::unique_ptr<weld::Entry> m_xPositionED; + std::unique_ptr<weld::Entry> m_xPhoneED; + std::unique_ptr<weld::Entry> m_xMobilePhoneED; + std::unique_ptr<weld::Entry> m_xFaxED; + std::unique_ptr<weld::Entry> m_xHomePageED; + std::unique_ptr<weld::Entry> m_xMailED; + +public: + SwBusinessDataPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwBusinessDataPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/DateFormFieldDialog.cxx b/sw/source/ui/fldui/DateFormFieldDialog.cxx new file mode 100644 index 000000000..e038eaa34 --- /dev/null +++ b/sw/source/ui/fldui/DateFormFieldDialog.cxx @@ -0,0 +1,125 @@ +/* -*- 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 <DateFormFieldDialog.hxx> +#include <IMark.hxx> +#include <xmloff/odffields.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <doc.hxx> + +namespace sw +{ +DateFormFieldDialog::DateFormFieldDialog(weld::Widget* pParent, + sw::mark::IDateFieldmark* pDateField, SwDoc* pDoc) + : GenericDialogController(pParent, "modules/swriter/ui/dateformfielddialog.ui", + "DateFormFieldDialog") + , m_pDateField(pDateField) + , m_pNumberFormatter(pDoc->GetNumberFormatter()) + , m_xFormatLB(new SwNumFormatTreeView(m_xBuilder->weld_tree_view("date_formats_treeview"))) +{ + m_xFormatLB->SetFormatType(SvNumFormatType::DATE); + m_xFormatLB->SetAutomaticLanguage(true); + m_xFormatLB->SetShowLanguageControl(true); + m_xFormatLB->SetOneArea(true); + + // Set a default height + weld::TreeView& rTreeView = dynamic_cast<weld::TreeView&>(m_xFormatLB->get_widget()); + rTreeView.set_size_request(rTreeView.get_preferred_size().Width(), + rTreeView.get_height_rows(10)); + InitControls(); +} + +DateFormFieldDialog::~DateFormFieldDialog() {} + +void DateFormFieldDialog::Apply() +{ + if (m_pDateField != nullptr) + { + // Try to find out the current date value and replace the content + // with the right formatted date string + sw::mark::IFieldmark::parameter_map_t* pParameters = m_pDateField->GetParameters(); + const SvNumberformat* pFormat = m_pNumberFormatter->GetEntry(m_xFormatLB->GetFormat()); + + // Get date value first + std::pair<bool, double> aResult = m_pDateField->GetCurrentDate(); + + // Then set the date format + (*pParameters)[ODF_FORMDATE_DATEFORMAT] <<= pFormat->GetFormatstring(); + (*pParameters)[ODF_FORMDATE_DATEFORMAT_LANGUAGE] + <<= LanguageTag(pFormat->GetLanguage()).getBcp47(); + + // Update current date + if (aResult.first) + { + m_pDateField->SetCurrentDate(aResult.second); + } + else + { + (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= OUString(); + } + } +} + +void DateFormFieldDialog::InitControls() +{ + if (m_pDateField != nullptr) + { + sw::mark::IFieldmark::parameter_map_t* pParameters = m_pDateField->GetParameters(); + + OUString sFormatString; + auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT); + if (pResult != pParameters->end()) + { + pResult->second >>= sFormatString; + } + + OUString sLang; + pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE); + if (pResult != pParameters->end()) + { + pResult->second >>= sLang; + } + + if (!sFormatString.isEmpty() && !sLang.isEmpty()) + { + LanguageType aLangType = LanguageTag(sLang).getLanguageType(); + sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sFormatString, aLangType); + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + sal_Int32 nCheckPos = 0; + SvNumFormatType nType; + m_pNumberFormatter->PutEntry(sFormatString, nCheckPos, nType, nFormat, + LanguageTag(sLang).getLanguageType()); + } + + if (aLangType != LANGUAGE_DONTKNOW && nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) + { + if (m_xFormatLB->GetCurLanguage() == aLangType) + { + m_xFormatLB->SetAutomaticLanguage(true); + } + else + { + m_xFormatLB->SetAutomaticLanguage(false); + m_xFormatLB->SetLanguage(aLangType); + + // Change format and change back for regenerating the list + m_xFormatLB->SetFormatType(SvNumFormatType::ALL); + m_xFormatLB->SetFormatType(SvNumFormatType::DATE); + } + m_xFormatLB->SetDefFormat(nFormat); + } + } + } +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/ui/fldui/DropDownFieldDialog.cxx b/sw/source/ui/fldui/DropDownFieldDialog.cxx new file mode 100644 index 000000000..08c866294 --- /dev/null +++ b/sw/source/ui/fldui/DropDownFieldDialog.cxx @@ -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 . + */ + +#include <wrtsh.hxx> +#include <fldbas.hxx> +#include <DropDownFieldDialog.hxx> +#include <flddropdown.hxx> + +#include <memory> + +using namespace ::com::sun::star; + +// edit insert-field +sw::DropDownFieldDialog::DropDownFieldDialog(weld::Widget *pParent, SwWrtShell &rS, + SwField* pField, bool bPrevButton, bool bNextButton) + : GenericDialogController(pParent, "modules/swriter/ui/dropdownfielddialog.ui", "DropdownFieldDialog") + , m_rSh( rS ) + , m_pDropField(nullptr) + , m_pPressedButton(nullptr) + , m_xListItemsLB(m_xBuilder->weld_tree_view("list")) + , m_xOKPB(m_xBuilder->weld_button("ok")) + , m_xPrevPB(m_xBuilder->weld_button("prev")) + , m_xNextPB(m_xBuilder->weld_button("next")) + , m_xEditPB(m_xBuilder->weld_button("edit")) +{ + m_xListItemsLB->set_size_request(m_xListItemsLB->get_approximate_digit_width() * 24, + m_xListItemsLB->get_height_rows(12)); + Link<weld::TreeView&, bool> aDoubleLk = LINK(this, DropDownFieldDialog, DoubleClickHdl); + m_xListItemsLB->connect_row_activated( aDoubleLk ); + + Link<weld::Button&, void> aEditButtonLk = LINK(this, DropDownFieldDialog, EditHdl); + Link<weld::Button&,void> aPrevButtonLk = LINK(this, DropDownFieldDialog, PrevHdl); + Link<weld::Button&, void> aNextButtonLk = LINK(this, DropDownFieldDialog, NextHdl); + m_xEditPB->connect_clicked(aEditButtonLk); + if( bPrevButton || bNextButton ) + { + m_xPrevPB->show(); + m_xPrevPB->connect_clicked(aPrevButtonLk); + m_xPrevPB->set_sensitive(bPrevButton); + + m_xNextPB->show(); + m_xNextPB->connect_clicked(aNextButtonLk); + m_xNextPB->set_sensitive(bNextButton); + } + if( SwFieldIds::Dropdown == pField->GetTyp()->Which() ) + { + + m_pDropField = static_cast<SwDropDownField*>(pField); + OUString sTitle = m_xDialog->get_title() + + m_pDropField->GetPar2(); + m_xDialog->set_title(sTitle); + const uno::Sequence< OUString > aItems = m_pDropField->GetItemSequence(); + for (const OUString& rItem : aItems) + m_xListItemsLB->append_text(rItem); + m_xListItemsLB->select_text(m_pDropField->GetSelectedItem()); + } + + bool bEnable = !m_rSh.IsCursorReadonly(); + m_xOKPB->set_sensitive(bEnable); + + m_xListItemsLB->grab_focus(); +} + +sw::DropDownFieldDialog::~DropDownFieldDialog() +{ +} + +void sw::DropDownFieldDialog::Apply() +{ + if (m_pDropField) + { + OUString sSelect = m_xListItemsLB->get_selected_text(); + if (m_pDropField->GetPar1() != sSelect) + { + m_rSh.StartAllAction(); + + std::unique_ptr<SwDropDownField> const pCopy( + static_cast<SwDropDownField*>(m_pDropField->CopyField().release())); + + pCopy->SetPar1(sSelect); + m_rSh.SwEditShell::UpdateOneField(*pCopy); + + m_rSh.SetUndoNoResetModified(); + m_rSh.EndAllAction(); + } + } +} + +bool sw::DropDownFieldDialog::PrevButtonPressed() const +{ + return m_pPressedButton == m_xPrevPB.get(); +} + +bool sw::DropDownFieldDialog::NextButtonPressed() const +{ + return m_pPressedButton == m_xNextPB.get(); +} + +IMPL_LINK_NOARG(sw::DropDownFieldDialog, EditHdl, weld::Button&, void) +{ + m_pPressedButton = m_xEditPB.get(); + m_xDialog->response(RET_YES); +} + +IMPL_LINK_NOARG(sw::DropDownFieldDialog, PrevHdl, weld::Button&, void) +{ + m_pPressedButton = m_xPrevPB.get(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(sw::DropDownFieldDialog, NextHdl, weld::Button&, void) +{ + m_pPressedButton = m_xNextPB.get(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(sw::DropDownFieldDialog, DoubleClickHdl, weld::TreeView&, bool) +{ + // tdf#114144, when next is available make double-click accept and go to next field + if (m_xNextPB->get_visible() && m_xNextPB->get_sensitive()) + m_pPressedButton = m_xNextPB.get(); + m_xDialog->response(RET_OK); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/DropDownFormFieldDialog.cxx b/sw/source/ui/fldui/DropDownFormFieldDialog.cxx new file mode 100644 index 000000000..4457b29f5 --- /dev/null +++ b/sw/source/ui/fldui/DropDownFormFieldDialog.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <DropDownFormFieldDialog.hxx> +#include <vcl/event.hxx> +#include <IMark.hxx> +#include <xmloff/odffields.hxx> +#include <vcl/svapp.hxx> +#include <strings.hrc> +#include <swtypes.hxx> + +namespace sw +{ +DropDownFormFieldDialog::DropDownFormFieldDialog(weld::Widget* pParent, + mark::IFieldmark* pDropDownField) + : GenericDialogController(pParent, "modules/swriter/ui/dropdownformfielddialog.ui", + "DropDownFormFieldDialog") + , m_pDropDownField(pDropDownField) + , m_bListHasChanged(false) + , m_xListItemEntry(m_xBuilder->weld_entry("item_entry")) + , m_xListAddButton(m_xBuilder->weld_button("add_button")) + , m_xListItemsTreeView(m_xBuilder->weld_tree_view("items_treeview")) + , m_xListRemoveButton(m_xBuilder->weld_button("remove_button")) + , m_xListUpButton(m_xBuilder->weld_button("up_button")) + , m_xListDownButton(m_xBuilder->weld_button("down_button")) +{ + m_xListItemEntry->connect_key_press(LINK(this, DropDownFormFieldDialog, KeyPressedHdl)); + m_xListItemEntry->connect_changed(LINK(this, DropDownFormFieldDialog, EntryChangedHdl)); + + m_xListItemsTreeView->set_size_request(m_xListItemEntry->get_preferred_size().Width(), + m_xListItemEntry->get_preferred_size().Height() * 5); + m_xListItemsTreeView->connect_changed(LINK(this, DropDownFormFieldDialog, ListChangedHdl)); + + Link<weld::Button&, void> aPushButtonLink(LINK(this, DropDownFormFieldDialog, ButtonPushedHdl)); + m_xListAddButton->connect_clicked(aPushButtonLink); + m_xListRemoveButton->connect_clicked(aPushButtonLink); + m_xListUpButton->connect_clicked(aPushButtonLink); + m_xListDownButton->connect_clicked(aPushButtonLink); + + InitControls(); +} + +DropDownFormFieldDialog::~DropDownFormFieldDialog() {} + +IMPL_LINK_NOARG(DropDownFormFieldDialog, ListChangedHdl, weld::TreeView&, void) { UpdateButtons(); } + +IMPL_LINK(DropDownFormFieldDialog, KeyPressedHdl, const KeyEvent&, rEvent, bool) +{ + if (rEvent.GetKeyCode().GetCode() == KEY_RETURN && !m_xListItemEntry->get_text().isEmpty()) + { + AppendItemToList(); + return true; + } + return false; +} + +IMPL_LINK_NOARG(DropDownFormFieldDialog, EntryChangedHdl, weld::Entry&, void) { UpdateButtons(); } + +IMPL_LINK(DropDownFormFieldDialog, ButtonPushedHdl, weld::Button&, rButton, void) +{ + if (&rButton == m_xListAddButton.get()) + { + AppendItemToList(); + } + else if (m_xListItemsTreeView->get_selected_index() != -1) + { + int nSelPos = m_xListItemsTreeView->get_selected_index(); + if (&rButton == m_xListRemoveButton.get()) + { + m_xListItemsTreeView->remove(nSelPos); + if (m_xListItemsTreeView->n_children() > 0) + m_xListItemsTreeView->select(nSelPos ? nSelPos - 1 : 0); + } + else if (&rButton == m_xListUpButton.get()) + { + const OUString sEntry = m_xListItemsTreeView->get_selected_text(); + m_xListItemsTreeView->remove(nSelPos); + nSelPos--; + m_xListItemsTreeView->insert_text(nSelPos, sEntry); + m_xListItemsTreeView->select(nSelPos); + } + else if (&rButton == m_xListDownButton.get()) + { + const OUString sEntry = m_xListItemsTreeView->get_selected_text(); + m_xListItemsTreeView->remove(nSelPos); + nSelPos++; + m_xListItemsTreeView->insert_text(nSelPos, sEntry); + m_xListItemsTreeView->select(nSelPos); + } + m_bListHasChanged = true; + } + UpdateButtons(); +} + +void DropDownFormFieldDialog::InitControls() +{ + if (m_pDropDownField != nullptr) + { + const mark::IFieldmark::parameter_map_t* const pParameters + = m_pDropDownField->GetParameters(); + + auto pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY); + if (pListEntries != pParameters->end()) + { + css::uno::Sequence<OUString> vListEntries; + pListEntries->second >>= vListEntries; + for (const OUString& rItem : std::as_const(vListEntries)) + m_xListItemsTreeView->append_text(rItem); + + // Select the current one + auto pResult = pParameters->find(ODF_FORMDROPDOWN_RESULT); + if (pResult != pParameters->end()) + { + sal_Int32 nSelection = -1; + pResult->second >>= nSelection; + if (vListEntries.getLength() > nSelection) + m_xListItemsTreeView->select_text(vListEntries[nSelection]); + } + } + } + UpdateButtons(); +} + +void DropDownFormFieldDialog::AppendItemToList() +{ + if (m_xListAddButton->get_sensitive()) + { + if (m_xListItemsTreeView->n_children() >= ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog( + m_xDialog.get(), VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_DROP_DOWN_FIELD_ITEM_LIMIT))); + xInfoBox->run(); + return; + } + + const OUString sEntry(m_xListItemEntry->get_text()); + if (!sEntry.isEmpty()) + { + m_xListItemsTreeView->append_text(sEntry); + m_xListItemsTreeView->select_text(sEntry); + m_bListHasChanged = true; + + // Clear entry + m_xListItemEntry->set_text(OUString()); + m_xListItemEntry->grab_focus(); + } + UpdateButtons(); + } +} + +void DropDownFormFieldDialog::UpdateButtons() +{ + m_xListAddButton->set_sensitive(!m_xListItemEntry->get_text().isEmpty() + && m_xListItemsTreeView->find_text(m_xListItemEntry->get_text()) + == -1); + + int nSelPos = m_xListItemsTreeView->get_selected_index(); + m_xListRemoveButton->set_sensitive(nSelPos != -1); + m_xListUpButton->set_sensitive(nSelPos > 0); + m_xListDownButton->set_sensitive(nSelPos != -1 + && nSelPos < m_xListItemsTreeView->n_children() - 1); +} + +void DropDownFormFieldDialog::Apply() +{ + if (m_pDropDownField != nullptr && m_bListHasChanged) + { + mark::IFieldmark::parameter_map_t* pParameters = m_pDropDownField->GetParameters(); + + css::uno::Sequence<OUString> vListEntries(m_xListItemsTreeView->n_children()); + for (int nIndex = 0; nIndex < m_xListItemsTreeView->n_children(); ++nIndex) + { + vListEntries[nIndex] = m_xListItemsTreeView->get_text(nIndex); + } + + if (m_xListItemsTreeView->n_children() != 0) + { + (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries; + } + else + { + pParameters->erase(ODF_FORMDROPDOWN_LISTENTRY); + } + + // After editing the drop down field's list we don't specify the selected item + pParameters->erase(ODF_FORMDROPDOWN_RESULT); + } +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/ui/fldui/changedb.cxx b/sw/source/ui/fldui/changedb.cxx new file mode 100644 index 000000000..bb6d9ed53 --- /dev/null +++ b/sw/source/ui/fldui/changedb.cxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XMultiServiceFactory.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <sfx2/viewfrm.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <dbmgr.hxx> +#include <changedb.hxx> + +#include <strings.hrc> +#include <bitmaps.hlst> + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::uno; + +// edit insert-field +SwChangeDBDlg::SwChangeDBDlg(SwView const & rVw) + : SfxDialogController(rVw.GetViewFrame()->GetWindow().GetFrameWeld(), "modules/swriter/ui/exchangedatabases.ui", + "ExchangeDatabasesDialog") + , pSh(rVw.GetWrtShellPtr()) + , m_xUsedDBTLB(m_xBuilder->weld_tree_view("inuselb")) + , m_xAvailDBTLB(new SwDBTreeList(m_xBuilder->weld_tree_view("availablelb"))) + , m_xAddDBPB(m_xBuilder->weld_button("browse")) + , m_xDocDBNameFT(m_xBuilder->weld_label("dbnameft")) + , m_xDefineBT(m_xBuilder->weld_button("ok")) +{ + int nWidth = m_xUsedDBTLB->get_approximate_digit_width() * 25; + int nHeight = m_xUsedDBTLB->get_height_rows(8); + m_xUsedDBTLB->set_size_request(nWidth, nHeight); + m_xAvailDBTLB->set_size_request(nWidth, nHeight); + + m_xAvailDBTLB->SetWrtShell(*pSh); + FillDBPopup(); + + ShowDBName(pSh->GetDBData()); + m_xDefineBT->connect_clicked(LINK(this, SwChangeDBDlg, ButtonHdl)); + m_xAddDBPB->connect_clicked(LINK(this, SwChangeDBDlg, AddDBHdl)); + + m_xUsedDBTLB->set_selection_mode(SelectionMode::Multiple); + m_xUsedDBTLB->make_sorted(); + + Link<weld::TreeView&,void> aLink = LINK(this, SwChangeDBDlg, TreeSelectHdl); + + m_xUsedDBTLB->connect_changed(aLink); + m_xAvailDBTLB->connect_changed(aLink); + TreeSelect(); +} + +// initialise database listboxes +void SwChangeDBDlg::FillDBPopup() +{ + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference<XDatabaseContext> xDBContext = DatabaseContext::create(xContext); + const SwDBData& rDBData = pSh->GetDBData(); + m_xAvailDBTLB->Select(rDBData.sDataSource, rDBData.sCommand, OUString()); + TreeSelect(); + + Sequence< OUString > aDBNames = xDBContext->getElementNames(); + auto aAllDBNames = comphelper::sequenceToContainer<std::vector<OUString>>(aDBNames); + + std::vector<OUString> aDBNameList; + pSh->GetAllUsedDB( aDBNameList, &aAllDBNames ); + + size_t nCount = aDBNameList.size(); + m_xUsedDBTLB->clear(); + std::unique_ptr<weld::TreeIter> xFirst; + + for(size_t k = 0; k < nCount; k++) + { + std::unique_ptr<weld::TreeIter> xLast = Insert(aDBNameList[k].getToken(0, ';')); + if (!xFirst) + xFirst = std::move(xLast); + } + + if (xFirst) + { + m_xUsedDBTLB->expand_row(*xFirst); + m_xUsedDBTLB->scroll_to_row(*xFirst); + m_xUsedDBTLB->select(*xFirst); + } +} + +std::unique_ptr<weld::TreeIter> SwChangeDBDlg::Insert(const OUString& rDBName) +{ + sal_Int32 nIdx{ 0 }; + const OUString sDBName(rDBName.getToken(0, DB_DELIM, nIdx)); + const OUString sTableName(rDBName.getToken(0, DB_DELIM, nIdx)); + OUString sUserData = rDBName.getToken(0, DB_DELIM, nIdx); + sal_Int32 nCommandType = sUserData.toInt32(); + + OUString aTableImg(RID_BMP_DBTABLE); + OUString aDBImg(RID_BMP_DB); + OUString aQueryImg(RID_BMP_DBQUERY); + OUString& rToInsert = nCommandType ? aQueryImg : aTableImg; + + std::unique_ptr<weld::TreeIter> xIter(m_xUsedDBTLB->make_iterator()); + if (m_xUsedDBTLB->get_iter_first(*xIter)) + { + do + { + if (sDBName == m_xUsedDBTLB->get_text(*xIter)) + { + if (m_xUsedDBTLB->iter_has_child(*xIter)) + { + std::unique_ptr<weld::TreeIter> xChild(m_xUsedDBTLB->make_iterator(xIter.get())); + if (m_xUsedDBTLB->iter_children(*xChild)) + { + do + { + if (sTableName == m_xUsedDBTLB->get_text(*xChild)) + return xChild; + } while (m_xUsedDBTLB->iter_next_sibling(*xChild)); + } + } + m_xUsedDBTLB->insert(xIter.get(), -1, &sTableName, &sUserData, nullptr, nullptr, + &rToInsert, false, xIter.get()); + return xIter; + } + } while (m_xUsedDBTLB->iter_next_sibling(*xIter)); + } + + m_xUsedDBTLB->insert(nullptr, -1, &sDBName, nullptr, nullptr, nullptr, + &aDBImg, false, xIter.get()); + m_xUsedDBTLB->insert(xIter.get(), -1, &sTableName, &sUserData, nullptr, nullptr, + &rToInsert, false, xIter.get()); + return xIter; +} + +// destroy dialog +SwChangeDBDlg::~SwChangeDBDlg() +{ +} + +short SwChangeDBDlg::run() +{ + short nRet = SfxDialogController::run(); + if (nRet == RET_OK) + UpdateFields(); + return nRet; +} + +void SwChangeDBDlg::UpdateFields() +{ + std::vector<OUString> aDBNames; + + m_xUsedDBTLB->selected_foreach([this, &aDBNames](weld::TreeIter& rEntry){ + if (m_xUsedDBTLB->get_iter_depth(rEntry)) + { + std::unique_ptr<weld::TreeIter> xIter(m_xUsedDBTLB->make_iterator(&rEntry)); + m_xUsedDBTLB->iter_parent(*xIter); + OUString sTmp(m_xUsedDBTLB->get_text(*xIter) + + OUStringChar(DB_DELIM) + m_xUsedDBTLB->get_text(rEntry) + OUStringChar(DB_DELIM) + + m_xUsedDBTLB->get_id(rEntry)); + aDBNames.push_back(sTmp); + } + return false; + }); + + pSh->StartAllAction(); + OUString sTableName; + OUString sColumnName; + sal_Bool bIsTable = false; + const OUString DBName(m_xAvailDBTLB->GetDBName(sTableName, sColumnName, &bIsTable)); + const OUString sTemp = DBName + + OUStringChar(DB_DELIM) + + sTableName + + OUStringChar(DB_DELIM) + + OUString::number(bIsTable + ? CommandType::TABLE + : CommandType::QUERY); + pSh->ChangeDBFields( aDBNames, sTemp); + pSh->EndAllAction(); +} + +IMPL_LINK_NOARG(SwChangeDBDlg, ButtonHdl, weld::Button&, void) +{ + OUString sTableName; + OUString sColumnName; + SwDBData aData; + sal_Bool bIsTable = false; + aData.sDataSource = m_xAvailDBTLB->GetDBName(sTableName, sColumnName, &bIsTable); + aData.sCommand = sTableName; + aData.nCommandType = bIsTable ? 0 : 1; + pSh->ChgDBData(aData); + ShowDBName(pSh->GetDBData()); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SwChangeDBDlg, TreeSelectHdl, weld::TreeView&, void) +{ + TreeSelect(); +} + +void SwChangeDBDlg::TreeSelect() +{ + bool bEnable = false; + std::unique_ptr<weld::TreeIter> xIter(m_xAvailDBTLB->make_iterator()); + if (m_xAvailDBTLB->get_selected(xIter.get())) + { + if (m_xAvailDBTLB->get_iter_depth(*xIter)) + bEnable = true; + } + m_xDefineBT->set_sensitive(bEnable); +} + + +// convert database name for display +void SwChangeDBDlg::ShowDBName(const SwDBData& rDBData) +{ + if (rDBData.sDataSource.isEmpty() && rDBData.sCommand.isEmpty()) + { + m_xDocDBNameFT->set_label(SwResId(SW_STR_NONE)); + } + else + { + const OUString sName(rDBData.sDataSource + "." + rDBData.sCommand); + m_xDocDBNameFT->set_label(sName.replaceAll("~", "~~")); + } +} + +IMPL_LINK_NOARG(SwChangeDBDlg, AddDBHdl, weld::Button&, void) +{ + const OUString sNewDB = SwDBManager::LoadAndRegisterDataSource(m_xDialog.get()); + if (!sNewDB.isEmpty()) + { + m_xAvailDBTLB->AddDataSource(sNewDB); + TreeSelect(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddb.cxx b/sw/source/ui/fldui/flddb.cxx new file mode 100644 index 000000000..601dcadf6 --- /dev/null +++ b/sw/source/ui/fldui/flddb.cxx @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <dbfld.hxx> +#include <doc.hxx> + +#include "flddb.hxx" +#include <dbconfig.hxx> +#include <dbmgr.hxx> + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +SwFieldDBPage::SwFieldDBPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet) + : SwFieldPage(pPage, pController, "modules/swriter/ui/flddbpage.ui", "FieldDbPage", pCoreSet) + , m_nOldFormat(0) + , m_nOldSubType(0) + , m_xTypeLB(m_xBuilder->weld_tree_view("type")) + , m_xDatabaseTLB(new SwDBTreeList(m_xBuilder->weld_tree_view("select"))) + , m_xAddDBPB(m_xBuilder->weld_button("browse")) + , m_xCondition(m_xBuilder->weld_widget("condgroup")) + , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("condition"))) + , m_xValue(m_xBuilder->weld_widget("recgroup")) + , m_xValueED(m_xBuilder->weld_entry("recnumber")) + , m_xDBFormatRB(m_xBuilder->weld_radio_button("fromdatabasecb")) + , m_xNewFormatRB(m_xBuilder->weld_radio_button("userdefinedcb")) + , m_xNumFormatLB(new NumFormatListBox(m_xBuilder->weld_combo_box("numformat"))) + , m_xFormatLB(m_xBuilder->weld_combo_box("format")) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) +{ + SetTypeSel(-1); //TODO + + m_xTypeLB->make_sorted(); + m_xFormatLB->make_sorted(); + + auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + auto nHeight = m_xTypeLB->get_height_rows(14); + m_xTypeLB->set_size_request(nWidth, nHeight); + m_xDatabaseTLB->set_size_request(nWidth*2, nHeight); + + m_xNumFormatLB->connect_changed(LINK(this, SwFieldDBPage, NumSelectHdl)); + m_xDatabaseTLB->connect_changed(LINK(this, SwFieldDBPage, TreeSelectHdl)); + m_xDatabaseTLB->connect_row_activated(LINK(this, SwFieldDBPage, TreeViewInsertHdl)); + + m_xValueED->connect_changed(LINK(this, SwFieldDBPage, ModifyHdl)); + m_xAddDBPB->connect_clicked(LINK(this, SwFieldDBPage, AddDBHdl)); +} + +SwFieldDBPage::~SwFieldDBPage() +{ + // If we have no stored SwWrtShell, it means we didn't do anything useful - no need to revoke. + if (SwWrtShell* pSh = GetWrtShell()) + { + // This would cleanup in the case of cancelled dialog + SwDBManager* pDbManager = pSh->GetDoc()->GetDBManager(); + if (pDbManager) + pDbManager->RevokeLastRegistrations(); + } +} + +// initialise TabPage +void SwFieldDBPage::Reset(const SfxItemSet*) +{ + Init(); // general initialization + + const sal_Int32 nOldPos = m_xTypeLB->get_selected_index(); + m_xTypeLB->freeze(); + m_sOldDBName = m_xDatabaseTLB->GetDBName(m_sOldTableName, m_sOldColumnName); + + m_xTypeLB->clear(); + + if (!IsFieldEdit()) + { + // initialise TypeListBox + const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup()); + + for(sal_uInt16 i = rRg.nStart; i < rRg.nEnd; ++i) + { + const SwFieldTypesEnum nTypeId = SwFieldMgr::GetTypeId(i); + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i)); + } + } + else + { + const SwFieldTypesEnum nTypeId = GetCurField()->GetTypeId(); + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), + SwFieldMgr::GetTypeStr(SwFieldMgr::GetPos(nTypeId))); + } + + m_xTypeLB->thaw(); + + // select old Pos + if (GetTypeSel() != -1) + m_xTypeLB->select(GetTypeSel()); + + m_xFormatLB->clear(); + + const sal_uInt16 nSize = GetFieldMgr().GetFormatCount(SwFieldTypesEnum::DatabaseSetNumber, IsFieldDlgHtmlMode()); + for( sal_uInt16 i = 0; i < nSize; ++i ) + { + const sal_uInt16 nFormatId = GetFieldMgr().GetFormatId( SwFieldTypesEnum::DatabaseSetNumber, i ); + OUString sId(OUString::number(nFormatId)); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(SwFieldTypesEnum::DatabaseSetNumber, i)); + if (SVX_NUM_ARABIC == nFormatId) + m_xFormatLB->set_active_id(sId); + } + + if (!IsFieldEdit()) + { + if (nOldPos != -1) + m_xTypeLB->select(nOldPos); + + if (!m_sOldDBName.isEmpty()) + { + m_xDatabaseTLB->Select(m_sOldDBName, m_sOldTableName, m_sOldColumnName); + } + else + { + SwWrtShell *pSh = CheckAndGetWrtShell(); + if(pSh) + { + SwDBData aTmp(pSh->GetDBData()); + m_xDatabaseTLB->Select(aTmp.sDataSource, aTmp.sCommand, OUString()); + } + } + } + + if( !IsRefresh() ) + { + const OUString sUserData = GetUserData(); + sal_Int32 nIdx{ 0 }; + if (sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + const sal_uInt16 nVal = static_cast<sal_uInt16>(sUserData.getToken(0, ';', nIdx).toInt32()); + if (nVal != USHRT_MAX) + { + for (sal_Int32 i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; ++i) + { + if (nVal == m_xTypeLB->get_id(i).toUInt32()) + { + m_xTypeLB->select(i); + break; + } + } + } + } + } + TypeHdl(nullptr); + + m_xTypeLB->connect_changed(LINK(this, SwFieldDBPage, TypeListBoxHdl)); + m_xTypeLB->connect_row_activated(LINK(this, SwFieldDBPage, TreeViewInsertHdl)); + + if (IsFieldEdit()) + { + m_xConditionED->save_value(); + m_xValueED->save_value(); + m_sOldDBName = m_xDatabaseTLB->GetDBName(m_sOldTableName, m_sOldColumnName); + m_nOldFormat = GetCurField()->GetFormat(); + m_nOldSubType = GetCurField()->GetSubType(); + } +} + +bool SwFieldDBPage::FillItemSet(SfxItemSet* ) +{ + OUString sTableName; + OUString sColumnName; + SwDBData aData; + sal_Bool bIsTable; + aData.sDataSource = m_xDatabaseTLB->GetDBName(sTableName, sColumnName, &bIsTable); + aData.sCommand = sTableName; + aData.nCommandType = bIsTable ? 0 : 1; + SwWrtShell *pSh = CheckAndGetWrtShell(); + assert(pSh); + + SwDBManager* pDbManager = pSh->GetDoc()->GetDBManager(); + if (pDbManager) + pDbManager->CommitLastRegistrations(); + + if (aData.sDataSource.isEmpty()) + aData = pSh->GetDBData(); + + if(!aData.sDataSource.isEmpty()) // without database no new field command + { + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + sal_uLong nFormat = 0; + sal_uInt16 nSubType = 0; + + OUString sDBName = aData.sDataSource + + OUStringChar(DB_DELIM) + + aData.sCommand + + OUStringChar(DB_DELIM) + + OUString::number(aData.nCommandType) + + OUStringChar(DB_DELIM); + if (!sColumnName.isEmpty()) + { + sDBName += sColumnName + OUStringChar(DB_DELIM); + } + OUString aName = sDBName + m_xConditionED->get_text(); + + switch (nTypeId) + { + case SwFieldTypesEnum::Database: + nFormat = m_xNumFormatLB->GetFormat(); + if (m_xNewFormatRB->get_sensitive() && m_xNewFormatRB->get_active()) + nSubType = nsSwExtendedSubType::SUB_OWN_FMT; + aName = sDBName; + break; + + case SwFieldTypesEnum::DatabaseSetNumber: + nFormat = m_xFormatLB->get_active_id().toUInt32(); + break; + default: break; + } + + const OUString aVal(m_xValueED->get_text()); + OUString sTempTableName; + OUString sTempColumnName; + OUString sTempDBName = m_xDatabaseTLB->GetDBName(sTempTableName, sTempColumnName); + bool bDBListBoxChanged = m_sOldDBName != sTempDBName || + m_sOldTableName != sTempTableName || m_sOldColumnName != sTempColumnName; + if (!IsFieldEdit() || + m_xConditionED->get_value_changed_from_saved() || + m_xValueED->get_saved_value() != aVal || + bDBListBoxChanged || + m_nOldFormat != nFormat || m_nOldSubType != nSubType) + { + InsertField( nTypeId, nSubType, aName, aVal, nFormat); + } + } + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldDBPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet ) +{ + return std::make_unique<SwFieldDBPage>( pPage, pController, pAttrSet ); +} + +sal_uInt16 SwFieldDBPage::GetGroup() +{ + return GRP_DB; +} + +IMPL_LINK( SwFieldDBPage, TypeListBoxHdl, weld::TreeView&, rBox, void ) +{ + TypeHdl(&rBox); +} + +void SwFieldDBPage::TypeHdl(const weld::TreeView* pBox) +{ + // save old ListBoxPos + const sal_Int32 nOld = GetTypeSel(); + + // current ListBoxPos + SetTypeSel(m_xTypeLB->get_selected_index()); + + if (GetTypeSel() == -1) + { + SetTypeSel(0); + m_xTypeLB->select(0); + } + + if (nOld == GetTypeSel()) + return; + + SwWrtShell *pSh = CheckAndGetWrtShell(); + assert(pSh); + bool bCond = false, bSetNo = false, bFormat = false, bDBFormat = false; + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + m_xDatabaseTLB->ShowColumns(nTypeId == SwFieldTypesEnum::Database); + + if (IsFieldEdit()) + { + SwDBData aData; + OUString sColumnName; + if (nTypeId == SwFieldTypesEnum::Database) + { + aData = static_cast<SwDBField*>(GetCurField())->GetDBData(); + sColumnName = static_cast<SwDBFieldType*>(GetCurField()->GetTyp())->GetColumnName(); + } + else + { + aData = static_cast<SwDBNameInfField*>(GetCurField())->GetDBData(pSh->GetDoc()); + } + m_xDatabaseTLB->Select(aData.sDataSource, aData.sCommand, sColumnName); + } + + switch (nTypeId) + { + case SwFieldTypesEnum::Database: + { + bFormat = true; + bDBFormat = true; + m_xNumFormatLB->show(); + m_xFormatLB->hide(); + + weld::Widget& rWidget = m_xNumFormatLB->get_widget(); + m_xNewFormatRB->set_accessible_relation_label_for(&rWidget); + rWidget.set_accessible_relation_labeled_by(m_xNewFormatRB.get()); + m_xFormatLB->set_accessible_relation_label_for(nullptr); + + if (pBox) // type was changed by user + m_xDBFormatRB->set_active(true); + + if (IsFieldEdit()) + { + if (GetCurField()->GetFormat() != 0 && GetCurField()->GetFormat() != SAL_MAX_UINT32) + m_xNumFormatLB->SetDefFormat(GetCurField()->GetFormat()); + + if (GetCurField()->GetSubType() & nsSwExtendedSubType::SUB_OWN_FMT) + m_xNewFormatRB->set_active(true); + else + m_xDBFormatRB->set_active(true); + } + break; + } + case SwFieldTypesEnum::DatabaseNumberSet: + bSetNo = true; + [[fallthrough]]; + case SwFieldTypesEnum::DatabaseNextSet: + bCond = true; + if (IsFieldEdit()) + { + m_xConditionED->set_text(GetCurField()->GetPar1()); + m_xValueED->set_text(GetCurField()->GetPar2()); + } + break; + + case SwFieldTypesEnum::DatabaseName: + break; + + case SwFieldTypesEnum::DatabaseSetNumber: + { + bFormat = true; + m_xNewFormatRB->set_active(true); + m_xNumFormatLB->hide(); + m_xFormatLB->show(); + + m_xNewFormatRB->set_accessible_relation_label_for(m_xFormatLB.get()); + m_xFormatLB->set_accessible_relation_labeled_by(m_xNewFormatRB.get()); + weld::Widget& rWidget = m_xNumFormatLB->get_widget(); + rWidget.set_accessible_relation_label_for(nullptr); + + if( IsFieldEdit() ) + { + for (sal_Int32 nI = m_xFormatLB->get_count(); nI;) + { + if (GetCurField()->GetFormat() == m_xFormatLB->get_id(--nI).toUInt32()) + { + m_xFormatLB->set_active( nI ); + break; + } + } + } + break; + } + default: break; + } + + m_xCondition->set_sensitive(bCond); + m_xValue->set_sensitive(bSetNo); + if (nTypeId != SwFieldTypesEnum::Database) + { + m_xDBFormatRB->set_sensitive(bDBFormat); + m_xNewFormatRB->set_sensitive(bDBFormat || bFormat); + m_xNumFormatLB->set_sensitive(bDBFormat); + m_xFormatLB->set_sensitive(bFormat); + } + m_xFormat->set_sensitive(bDBFormat || bFormat); + + if (!IsFieldEdit()) + { + m_xValueED->set_text(OUString()); + if (bCond) + m_xConditionED->set_text("TRUE"); + else + m_xConditionED->set_text(OUString()); + } + + CheckInsert(); +} + +IMPL_LINK_NOARG(SwFieldDBPage, NumSelectHdl, weld::ComboBox&, void) +{ + m_xNewFormatRB->set_active(true); + m_xNumFormatLB->CallSelectHdl(); +} + +void SwFieldDBPage::CheckInsert() +{ + bool bInsert = true; + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + std::unique_ptr<weld::TreeIter> xIter(m_xDatabaseTLB->make_iterator()); + if (m_xDatabaseTLB->get_selected(xIter.get())) + { + bool bEntry = m_xDatabaseTLB->iter_parent(*xIter); + + if (nTypeId == SwFieldTypesEnum::Database && bEntry) + bEntry = m_xDatabaseTLB->iter_parent(*xIter); + + bInsert &= bEntry; + } + else + bInsert = false; + + if (nTypeId == SwFieldTypesEnum::DatabaseNumberSet) + { + bool bHasValue = !m_xValueED->get_text().isEmpty(); + + bInsert &= bHasValue; + } + + EnableInsert(bInsert); +} + +IMPL_LINK(SwFieldDBPage, TreeSelectHdl, weld::TreeView&, rBox, void) +{ + std::unique_ptr<weld::TreeIter> xIter(rBox.make_iterator()); + if (rBox.get_selected(xIter.get())) + { + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + bool bEntry = m_xDatabaseTLB->iter_parent(*xIter); + + if (nTypeId == SwFieldTypesEnum::Database && bEntry) + bEntry = m_xDatabaseTLB->iter_parent(*xIter); + + CheckInsert(); + + if (nTypeId == SwFieldTypesEnum::Database) + { + bool bNumFormat = false; + + if (bEntry) + { + OUString sTableName; + OUString sColumnName; + sal_Bool bIsTable; + OUString sDBName = m_xDatabaseTLB->GetDBName(sTableName, sColumnName, &bIsTable); + bNumFormat = GetFieldMgr().IsDBNumeric(sDBName, + sTableName, + bIsTable, + sColumnName); + if (!IsFieldEdit()) + m_xDBFormatRB->set_active(true); + } + + m_xDBFormatRB->set_sensitive(bNumFormat); + m_xNewFormatRB->set_sensitive(bNumFormat); + m_xNumFormatLB->set_sensitive(bNumFormat); + m_xFormat->set_sensitive(bNumFormat); + } + } +} + +IMPL_LINK_NOARG(SwFieldDBPage, AddDBHdl, weld::Button&, void) +{ + if (SwWrtShell* pSh = CheckAndGetWrtShell()) + { + OUString sNewDB + = SwDBManager::LoadAndRegisterDataSource(GetFrameWeld(), pSh->GetDoc()->GetDocShell()); + if (!sNewDB.isEmpty()) + { + m_xDatabaseTLB->AddDataSource(sNewDB); + } + } +} + +// Modify +IMPL_LINK_NOARG(SwFieldDBPage, ModifyHdl, weld::Entry&, void) +{ + CheckInsert(); +} + +void SwFieldDBPage::FillUserData() +{ + const sal_Int32 nEntryPos = m_xTypeLB->get_selected_index(); + const sal_uInt16 nTypeSel = ( -1 == nEntryPos ) + ? USHRT_MAX : m_xTypeLB->get_id(nEntryPos).toUInt32(); + SetUserData(USER_DATA_VERSION ";" + OUString::number( nTypeSel )); +} + +void SwFieldDBPage::ActivateMailMergeAddress() +{ + m_xTypeLB->select_id(OUString::number(static_cast<sal_uInt16>(SwFieldTypesEnum::Database))); + TypeListBoxHdl(*m_xTypeLB); + const SwDBData& rData = SW_MOD()->GetDBConfig()->GetAddressSource(); + m_xDatabaseTLB->Select(rData.sDataSource, rData.sCommand, OUString()); +} + +void SwFieldDBPage::SetWrtShell(SwWrtShell& rSh) +{ + // We need to remember the shell to be able to call correct SwDBManager + SwFieldPage::SetWrtShell(&rSh); + m_xDatabaseTLB->SetWrtShell(rSh); +} + +SwWrtShell* SwFieldDBPage::CheckAndGetWrtShell() +{ + SwWrtShell* pSh = GetWrtShell(); + if (!pSh) + { + pSh = ::GetActiveWrtShell(); + if (pSh) // this is not guaranteed: e.g., activating print preview with dialog active + SetWrtShell(*pSh); + } + return pSh; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddb.hxx b/sw/source/ui/fldui/flddb.hxx new file mode 100644 index 000000000..e507fcb86 --- /dev/null +++ b/sw/source/ui/fldui/flddb.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_FLDUI_FLDDB_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDDB_HXX + +#include <condedit.hxx> +#include <dbtree.hxx> +#include <numfmtlb.hxx> + +#include "fldpage.hxx" + +class SwFieldDBPage : public SwFieldPage +{ + OUString m_sOldDBName; + OUString m_sOldTableName; + OUString m_sOldColumnName; + sal_uLong m_nOldFormat; + sal_uInt16 m_nOldSubType; + + std::unique_ptr<weld::TreeView> m_xTypeLB; + std::unique_ptr<SwDBTreeList> m_xDatabaseTLB; + std::unique_ptr<weld::Button> m_xAddDBPB; + std::unique_ptr<weld::Widget> m_xCondition; + std::unique_ptr<ConditionEdit> m_xConditionED; + std::unique_ptr<weld::Widget> m_xValue; + std::unique_ptr<weld::Entry> m_xValueED; + std::unique_ptr<weld::RadioButton> m_xDBFormatRB; + std::unique_ptr<weld::RadioButton> m_xNewFormatRB; + std::unique_ptr<NumFormatListBox> m_xNumFormatLB; + std::unique_ptr<weld::ComboBox> m_xFormatLB; + std::unique_ptr<weld::Widget> m_xFormat; + + DECL_LINK( TypeListBoxHdl, weld::TreeView&, void ); + DECL_LINK( NumSelectHdl, weld::ComboBox&, void ); + DECL_LINK( TreeSelectHdl, weld::TreeView&, void ); + DECL_LINK( ModifyHdl, weld::Entry&, void ); + DECL_LINK( AddDBHdl, weld::Button&, void ); + void TypeHdl(const weld::TreeView*); + + void CheckInsert(); + + using SwFieldPage::SetWrtShell; + SwWrtShell* CheckAndGetWrtShell(); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldDBPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual ~SwFieldDBPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; + void ActivateMailMergeAddress(); + + void SetWrtShell(SwWrtShell& rSh); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddinf.cxx b/sw/source/ui/fldui/flddinf.cxx new file mode 100644 index 000000000..a1145acb0 --- /dev/null +++ b/sw/source/ui/fldui/flddinf.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 <sfx2/frame.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> + +#include <swtypes.hxx> +#include <flddinf.hrc> +#include <strings.hrc> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <wrtsh.hxx> + +#include "flddinf.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/util/Date.hpp> + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +using namespace nsSwDocInfoSubType; +using namespace com::sun::star; + +void FillFieldSelect(weld::TreeView& rListBox) +{ + for (size_t i = 0; i < SAL_N_ELEMENTS(FLD_SELECT); ++i) + rListBox.append_text(SwResId(FLD_SELECT[i])); +} + +SwFieldDokInfPage::SwFieldDokInfPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet) + : SwFieldPage(pPage, pController, "modules/swriter/ui/flddocinfopage.ui", "FieldDocInfoPage", pCoreSet) + , nOldSel(0) + , nOldFormat(0) + , m_xTypeTLB(m_xBuilder->weld_tree_view("type")) + , m_xSelection(m_xBuilder->weld_widget("selectframe")) + , m_xSelectionLB(m_xBuilder->weld_tree_view("select")) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) + , m_xFormatLB(new SwNumFormatTreeView(m_xBuilder->weld_tree_view("format"))) + , m_xFixedCB(m_xBuilder->weld_check_button("fixed")) +{ + m_xTypeTLB->make_sorted(); + FillFieldSelect(*m_xSelectionLB); + + auto nWidth = m_xTypeTLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + auto nHeight = m_xTypeTLB->get_height_rows(20); + m_xTypeTLB->set_size_request(nWidth, nHeight); + m_xFormatLB->get_widget().set_size_request(nWidth, nHeight); + m_xSelectionLB->set_size_request(nWidth, nHeight); + + //enable 'active' language selection + m_xFormatLB->SetShowLanguageControl(true); + + const SfxUnoAnyItem* pItem = pCoreSet + ? pCoreSet->GetItem<SfxUnoAnyItem>(SID_DOCINFO, false) + : nullptr; + if ( pItem ) + pItem->GetValue() >>= xCustomPropertySet; +} + +SwFieldDokInfPage::~SwFieldDokInfPage() +{ +} + +void SwFieldDokInfPage::Reset(const SfxItemSet* ) +{ + Init(); // general initialisation + + // initialise TypeListBox + m_xTypeTLB->freeze(); + m_xTypeTLB->clear(); + m_xSelEntry.reset(); + + // display SubTypes in TypeLB + sal_uInt16 nSubType = USHRT_MAX; + if (IsFieldEdit()) + { + const SwField* pCurField = GetCurField(); + nSubType = static_cast<const SwDocInfoField*>(pCurField)->GetSubType() & 0xff; + if( nSubType == DI_CUSTOM ) + { + m_sOldCustomFieldName = static_cast<const SwDocInfoField*>(pCurField)->GetName(); + } + m_xFormatLB->SetAutomaticLanguage(pCurField->IsAutomaticLanguage()); + SwWrtShell *pSh = GetWrtShell(); + if(pSh) + { + const SvNumberformat* pFormat = pSh->GetNumberFormatter()->GetEntry(pCurField->GetFormat()); + if(pFormat) + m_xFormatLB->SetLanguage(pFormat->GetLanguage()); + } + } + + sal_Int32 nSelEntryData = -1; + const OUString sUserData = GetUserData(); + sal_Int32 nIdx{ 0 }; + if (sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + nSelEntryData = sUserData.getToken(0, ';', nIdx).toInt32(); + } + + std::vector<OUString> aLst; + GetFieldMgr().GetSubTypes(SwFieldTypesEnum::DocumentInfo, aLst); + std::unique_ptr<weld::TreeIter> xEntry(m_xTypeTLB->make_iterator()); + std::unique_ptr<weld::TreeIter> xExpandEntry; + for(size_t i = 0; i < aLst.size(); ++i) + { + if (!IsFieldEdit() || nSubType == i) + { + const OUString sId(OUString::number(i)); + if (DI_CUSTOM == i) + { + if(xCustomPropertySet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xSetInfo = xCustomPropertySet->getPropertySetInfo(); + const uno::Sequence< beans::Property > rProperties = xSetInfo->getProperties(); + + if( rProperties.hasElements() ) + { + std::unique_ptr<weld::TreeIter> xInfo(m_xTypeTLB->make_iterator()); + + OUString sText(SwResId(STR_CUSTOM_FIELD)); + OUString sEntryId(OUString::number(USHRT_MAX)); + m_xTypeTLB->insert(nullptr, -1, &sText, &sEntryId, nullptr, + nullptr, nullptr, false, xInfo.get()); + for (const auto& rProperty : rProperties) + { + const OUString sEntry = rProperty.Name; + + m_xTypeTLB->insert(xInfo.get(), -1, &sEntry, &sId, + nullptr, nullptr, nullptr, false, xEntry.get()); + if (m_sOldCustomFieldName == sEntry) + { + m_xSelEntry = m_xTypeTLB->make_iterator(xEntry.get()); + xExpandEntry = m_xTypeTLB->make_iterator(xInfo.get()); + } + } + } + } + } + else + { + if (!(IsFieldDlgHtmlMode() && (i == DI_EDIT || i == DI_SUBJECT || i == DI_PRINT))) + { + m_xTypeTLB->insert(nullptr, -1, &aLst[i], &sId, + nullptr, nullptr, nullptr, false, xEntry.get()); + } + } + if (static_cast<size_t>(nSelEntryData) == i) + m_xSelEntry = std::move(xEntry); + } + } + + m_xTypeTLB->thaw(); + + if (xExpandEntry) + m_xTypeTLB->expand_row(*xExpandEntry); + + // select old Pos + if (m_xSelEntry) + { + m_xTypeTLB->select(*m_xSelEntry); + nSubType = m_xTypeTLB->get_id(*m_xSelEntry).toUInt32(); + } + else + { + m_xSelEntry = m_xTypeTLB->make_iterator(); + if (m_xTypeTLB->get_iter_first(*m_xSelEntry)) + nSubType = m_xTypeTLB->get_id(*m_xSelEntry).toUInt32(); + else + m_xSelEntry.reset(); + } + + FillSelectionLB(nSubType); + if (m_xSelEntry) + TypeHdl(*m_xTypeTLB); + + m_xTypeTLB->connect_changed(LINK(this, SwFieldDokInfPage, TypeHdl)); + m_xTypeTLB->connect_row_activated(LINK(this, SwFieldDokInfPage, TreeViewInsertHdl)); + m_xSelectionLB->connect_changed(LINK(this, SwFieldDokInfPage, SubTypeHdl)); + m_xSelectionLB->connect_row_activated(LINK(this, SwFieldDokInfPage, TreeViewInsertHdl)); + m_xFormatLB->connect_row_activated(LINK(this, SwFieldDokInfPage, TreeViewInsertHdl)); + + if (IsFieldEdit()) + { + nOldSel = m_xSelectionLB->get_selected_index(); + nOldFormat = GetCurField()->GetFormat(); + m_xFixedCB->save_state(); + } +} + +IMPL_LINK_NOARG(SwFieldDokInfPage, TypeHdl, weld::TreeView&, void) +{ + // current ListBoxPos + if (!m_xTypeTLB->get_selected(m_xSelEntry.get()) && + m_xTypeTLB->get_iter_first(*m_xSelEntry)) + { + m_xTypeTLB->select(*m_xSelEntry); + } + FillSelectionLB(m_xTypeTLB->get_id(*m_xSelEntry).toUInt32()); + SubTypeHdl(*m_xSelectionLB); +} + +IMPL_LINK_NOARG(SwFieldDokInfPage, SubTypeHdl, weld::TreeView&, void) +{ + sal_uInt16 nSubType = m_xTypeTLB->get_id(*m_xSelEntry).toUInt32(); + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + sal_uInt16 nExtSubType; + SvNumFormatType nNewType = SvNumFormatType::ALL; + + if (nSubType != DI_EDIT) + { + if (nPos == -1) + { + if (!m_xSelectionLB->n_children()) + { + m_xFormatLB->clear(); + m_xFormat->set_sensitive(false); + if( nSubType == DI_CUSTOM ) + { + //find out which type the custom field has - for a start set to DATE format + const OUString sName = m_xTypeTLB->get_text(*m_xSelEntry); + try + { + uno::Any aVal = xCustomPropertySet->getPropertyValue( sName ); + const uno::Type& rValueType = aVal.getValueType(); + if( rValueType == ::cppu::UnoType<util::DateTime>::get()) + { + nNewType = SvNumFormatType::DATETIME; + } + else if( rValueType == ::cppu::UnoType<util::Date>::get()) + { + nNewType = SvNumFormatType::DATE; + } + else if( rValueType == ::cppu::UnoType<util::Time>::get()) + { + nNewType = SvNumFormatType::TIME; + } + } + catch( const uno::Exception& ) + { + } + } + else + return; + } + nPos = 0; + } + + nExtSubType = m_xSelectionLB->get_id(nPos).toUInt32(); + } + else + nExtSubType = DI_SUB_TIME; + + SvNumFormatType nOldType = SvNumFormatType::ALL; + bool bEnable = false; + bool bOneArea = false; + + if (m_xFormatLB->get_active()) + nOldType = m_xFormatLB->GetFormatType(); + + switch (nExtSubType) + { + case DI_SUB_AUTHOR: + break; + + case DI_SUB_DATE: + nNewType = SvNumFormatType::DATE; + bOneArea = true; + break; + + case DI_SUB_TIME: + nNewType = SvNumFormatType::TIME; + bOneArea = true; + break; + } + if (nNewType == SvNumFormatType::ALL) + { + m_xFormatLB->clear(); + } + else + { + if (nOldType != nNewType) + { + m_xFormatLB->SetFormatType(nNewType); + m_xFormatLB->SetOneArea(bOneArea); + } + bEnable = true; + } + + sal_uInt32 nFormat = IsFieldEdit() ? static_cast<SwDocInfoField*>(GetCurField())->GetFormat() : 0; + + sal_uInt16 nOldSubType = IsFieldEdit() ? (static_cast<SwDocInfoField*>(GetCurField())->GetSubType() & 0xff00) : 0; + + if (IsFieldEdit()) + { + nPos = m_xSelectionLB->get_selected_index(); + if (nPos != -1) + { + nSubType = m_xSelectionLB->get_id(nPos).toUInt32(); + + nOldSubType &= ~DI_SUB_FIXED; + if (nOldSubType == nSubType) + { + if (!nFormat && (nNewType == SvNumFormatType::DATE || nNewType == SvNumFormatType::TIME)) + { + SwWrtShell *pSh = GetWrtShell(); + if(pSh) + { + SvNumberFormatter* pFormatter = pSh->GetNumberFormatter(); + LanguageType eLang = m_xFormatLB->GetCurLanguage(); + if (nNewType == SvNumFormatType::DATE) + nFormat = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLang); + else if (nNewType == SvNumFormatType::TIME) + nFormat = pFormatter->GetFormatIndex( NF_TIME_HHMM, eLang); + } + } + m_xFormatLB->SetDefFormat(nFormat); + } + } + else if( (nSubType == DI_CUSTOM) && (nNewType != SvNumFormatType::ALL) ) + { + m_xFormatLB->SetDefFormat(nFormat); + } + } + + m_xFormat->set_sensitive(bEnable); + + if (bEnable && m_xFormatLB->get_selected_index() == -1) + m_xFormatLB->select(0); +} + +sal_Int32 SwFieldDokInfPage::FillSelectionLB(sal_uInt16 nSubType) +{ + // fill Format-Listbox + SwFieldTypesEnum nTypeId = SwFieldTypesEnum::DocumentInfo; + + EnableInsert(nSubType != USHRT_MAX); + + if (nSubType == USHRT_MAX) // Info-Text + nSubType = DI_SUBTYPE_BEGIN; + + m_xSelectionLB->clear(); + + sal_uInt16 nSize = 0; + sal_Int32 nSelPos = -1; + sal_uInt16 nExtSubType = IsFieldEdit() ? (static_cast<SwDocInfoField*>(GetCurField())->GetSubType() & 0xff00) : 0; + + if (IsFieldEdit()) + { + m_xFixedCB->set_active((nExtSubType & DI_SUB_FIXED) != 0); + nExtSubType = ((nExtSubType & ~DI_SUB_FIXED) >> 8) - 1; + } + + if (nSubType < DI_CREATE || nSubType == DI_DOCNO || nSubType == DI_EDIT|| nSubType == DI_CUSTOM ) + { + // Format Box is empty for Title and Time + } + else + { + nSize = GetFieldMgr().GetFormatCount(nTypeId, IsFieldDlgHtmlMode()); + for (sal_uInt16 i = 0; i < nSize; ++i) + { + OUString sId(OUString::number(GetFieldMgr().GetFormatId(nTypeId, i))); + m_xSelectionLB->append(sId, GetFieldMgr().GetFormatStr(nTypeId, i)); + if (IsFieldEdit() && i == nExtSubType) + nSelPos = i; + } + } + + bool bEnable = nSize != 0; + + if (nSize) + { + if (m_xSelectionLB->get_selected_index() == -1) + m_xSelectionLB->select(nSelPos == USHRT_MAX ? 0 : nSelPos); + bEnable = true; + } + + m_xSelection->set_sensitive(bEnable); + + return nSize; +} + +bool SwFieldDokInfPage::FillItemSet(SfxItemSet* ) +{ + if (!m_xSelEntry) + return false; + + sal_uInt16 nSubType = m_xTypeTLB->get_id(*m_xSelEntry).toUInt32(); + if (nSubType == USHRT_MAX) + return false; + + sal_uInt32 nFormat = 0; + + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + + OUString aName; + if (DI_CUSTOM == nSubType) + aName = m_xTypeTLB->get_text(*m_xSelEntry); + + if (nPos != -1) + nSubType |= m_xSelectionLB->get_id(nPos).toUInt32(); + + if (m_xFixedCB->get_active()) + nSubType |= DI_SUB_FIXED; + + nPos = m_xFormatLB->get_selected_index(); + if(nPos != -1) + nFormat = m_xFormatLB->GetFormat(); + + if (!IsFieldEdit() || nOldSel != m_xSelectionLB->get_selected_index() || + nOldFormat != nFormat || m_xFixedCB->get_state_changed_from_saved() + || (DI_CUSTOM == nSubType && aName != m_sOldCustomFieldName )) + { + InsertField(SwFieldTypesEnum::DocumentInfo, nSubType, aName, OUString(), nFormat, + ' ', m_xFormatLB->IsAutomaticLanguage()); + } + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldDokInfPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet) +{ + return std::make_unique<SwFieldDokInfPage>(pPage, pController, pAttrSet); +} + +sal_uInt16 SwFieldDokInfPage::GetGroup() +{ + return GRP_REG; +} + +void SwFieldDokInfPage::FillUserData() +{ + int nEntry = m_xTypeTLB->get_selected_index(); + sal_uInt16 nTypeSel = nEntry != -1 ? m_xTypeTLB->get_id(nEntry).toUInt32() : USHRT_MAX; + SetUserData(USER_DATA_VERSION ";" + OUString::number( nTypeSel )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddinf.hxx b/sw/source/ui/fldui/flddinf.hxx new file mode 100644 index 000000000..fb6fffff8 --- /dev/null +++ b/sw/source/ui/fldui/flddinf.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_UI_FLDUI_FLDDINF_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDDINF_HXX + +#include <sfx2/tabdlg.hxx> +#include <numfmtlb.hxx> +#include "fldpage.hxx" + +namespace com::sun::star::beans { class XPropertySet; } + +class SwFieldDokInfPage : public SwFieldPage +{ + std::unique_ptr<weld::TreeIter> m_xSelEntry; + css::uno::Reference < css::beans::XPropertySet > xCustomPropertySet; + + sal_Int32 nOldSel; + sal_uLong nOldFormat; + OUString m_sOldCustomFieldName; + + std::unique_ptr<weld::TreeView> m_xTypeTLB; + std::unique_ptr<weld::Widget> m_xSelection; + std::unique_ptr<weld::TreeView> m_xSelectionLB; + std::unique_ptr<weld::Widget> m_xFormat; + std::unique_ptr<SwNumFormatTreeView> m_xFormatLB; + std::unique_ptr<weld::CheckButton> m_xFixedCB; + + DECL_LINK(TypeHdl, weld::TreeView&, void); + DECL_LINK(SubTypeHdl, weld::TreeView&, void); + + sal_Int32 FillSelectionLB(sal_uInt16 nSubTypeId); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldDokInfPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pSet); + virtual ~SwFieldDokInfPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; +}; + +void FillFieldSelect(weld::TreeView& rListBox); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddok.cxx b/sw/source/ui/fldui/flddok.cxx new file mode 100644 index 000000000..31bf38943 --- /dev/null +++ b/sw/source/ui/fldui/flddok.cxx @@ -0,0 +1,627 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <flddat.hxx> +#include <docufld.hxx> +#include <strings.hrc> +#include <chpfld.hxx> +#include "flddok.hxx" +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <svl/zformat.hxx> + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +SwFieldDokPage::SwFieldDokPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet) + : SwFieldPage(pPage, pController, "modules/swriter/ui/flddocumentpage.ui", + "FieldDocumentPage", pCoreSet) + , nOldSel(0) + , nOldFormat(0) + , m_xTypeLB(m_xBuilder->weld_tree_view("type")) + , m_xSelection(m_xBuilder->weld_widget("selectframe")) + , m_xSelectionLB(m_xBuilder->weld_tree_view("select")) + , m_xValueFT(m_xBuilder->weld_label("valueft")) + , m_xValueED(m_xBuilder->weld_entry("value")) + , m_xLevelFT(m_xBuilder->weld_label("levelft")) + , m_xLevelED(m_xBuilder->weld_spin_button("level")) + , m_xDateFT(m_xBuilder->weld_label("daysft")) + , m_xTimeFT(m_xBuilder->weld_label("minutesft")) + , m_xDateOffsetED(m_xBuilder->weld_spin_button("offset")) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) + , m_xFormatLB(m_xBuilder->weld_tree_view("format")) + , m_xNumFormatLB(new SwNumFormatTreeView(m_xBuilder->weld_tree_view("numformat"))) + , m_xFixedCB(m_xBuilder->weld_check_button("fixed")) +{ + m_xTypeLB->make_sorted(); + m_xFormatLB->make_sorted(); + + auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + auto nHeight = m_xTypeLB->get_height_rows(20); + + m_xTypeLB->set_size_request(nWidth, nHeight); + m_xSelectionLB->set_size_request(nWidth, nHeight); + m_xFormatLB->set_size_request(nWidth, nHeight); + + m_xSelectionLB->connect_row_activated(LINK(this, SwFieldDokPage, TreeViewInsertHdl)); + m_xFormatLB->connect_row_activated(LINK(this, SwFieldDokPage, TreeViewInsertHdl)); + m_xNumFormatLB->connect_row_activated(LINK(this, SwFieldDokPage, NumFormatHdl)); + + m_xLevelED->set_max(MAXLEVEL); + m_xDateOffsetED->set_range(INT_MIN, INT_MAX); + //enable 'active' language selection + m_xNumFormatLB->SetShowLanguageControl(true); +} + +SwFieldDokPage::~SwFieldDokPage() +{ +} + +void SwFieldDokPage::Reset(const SfxItemSet* ) +{ + SavePos(*m_xTypeLB); + Init(); // general initialisation + + // initialise TypeListBox + const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup()); + + m_xTypeLB->freeze(); + m_xTypeLB->clear(); + + if (!IsFieldEdit()) + { + bool bPage = false; + // fill Type-Listbox + for(sal_uInt16 i = rRg.nStart; i < rRg.nEnd; ++i) + { + const SwFieldTypesEnum nTypeId = SwFieldMgr::GetTypeId(i); + + switch (nTypeId) + { + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::NextPage: + case SwFieldTypesEnum::PageNumber: + if (!bPage) + { + m_xTypeLB->append(OUString::number(USHRT_MAX), SwResId(FMT_REF_PAGE)); + bPage = true; + } + break; + + default: + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i)); + break; + } + } + } + else + { + const SwField* pCurField = GetCurField(); + SwFieldTypesEnum nTypeId = pCurField->GetTypeId(); + if (nTypeId == SwFieldTypesEnum::FixedDate) + nTypeId = SwFieldTypesEnum::Date; + if (nTypeId == SwFieldTypesEnum::FixedTime) + nTypeId = SwFieldTypesEnum::Time; + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(SwFieldMgr::GetPos(nTypeId))); + m_xNumFormatLB->SetAutomaticLanguage(pCurField->IsAutomaticLanguage()); + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + const SvNumberformat* pFormat = pSh->GetNumberFormatter()->GetEntry(pCurField->GetFormat()); + if(pFormat) + m_xNumFormatLB->SetLanguage(pFormat->GetLanguage()); + } + } + + + m_xTypeLB->thaw(); + + // select old Pos + RestorePos(*m_xTypeLB); + + m_xTypeLB->connect_row_activated(LINK(this, SwFieldDokPage, TreeViewInsertHdl)); + m_xTypeLB->connect_changed(LINK(this, SwFieldDokPage, TypeHdl)); + m_xFormatLB->connect_changed(LINK(this, SwFieldDokPage, FormatHdl)); + + if( !IsRefresh() ) + { + const OUString sUserData = GetUserData(); + sal_Int32 nIdx{ 0 }; + if (sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + const sal_uInt16 nVal = static_cast< sal_uInt16 >(sUserData.getToken(0, ';', nIdx).toInt32()); + if(nVal != USHRT_MAX) + { + for (int i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; i++) + { + if (nVal == m_xTypeLB->get_id(i).toUInt32()) + { + m_xTypeLB->select(i); + break; + } + } + } + } + } + TypeHdl(*m_xTypeLB); + + if (IsFieldEdit()) + { + nOldSel = m_xSelectionLB->get_selected_index(); + nOldFormat = GetCurField()->GetFormat(); + m_xFixedCB->save_state(); + m_xValueED->save_value(); + m_xLevelED->save_value(); + m_xDateOffsetED->save_value(); + } +} + +IMPL_LINK_NOARG(SwFieldDokPage, TypeHdl, weld::TreeView&, void) +{ + // save old ListBoxPos + const sal_Int32 nOld = GetTypeSel(); + + // current ListBoxPos + SetTypeSel(m_xTypeLB->get_selected_index()); + + if(GetTypeSel() == -1) + { + SetTypeSel(0); + m_xTypeLB->select(0); + } + + if (nOld == GetTypeSel()) + return; + + size_t nCount; + + m_xDateFT->hide(); + m_xTimeFT->hide(); + + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + // fill Selection-Listbox + m_xSelectionLB->clear(); + + if (nTypeId != SwFieldTypesEnum::Unknown) + { + std::vector<OUString> aLst; + GetFieldMgr().GetSubTypes(nTypeId, aLst); + + if (nTypeId != SwFieldTypesEnum::Author) + nCount = aLst.size(); + else + nCount = GetFieldMgr().GetFormatCount(nTypeId, IsFieldDlgHtmlMode()); + + for (size_t i = 0; i < nCount; ++i) + { + if (!IsFieldEdit()) + { + OUString sId(OUString::number(i)); + if (nTypeId != SwFieldTypesEnum::Author) + m_xSelectionLB->append(sId, aLst[i]); + else + m_xSelectionLB->append(sId, GetFieldMgr().GetFormatStr(nTypeId, i)); + } + else + { + bool bInsert = false; + + OUString sId(OUString::number(i)); + + switch (nTypeId) + { + case SwFieldTypesEnum::Date: + case SwFieldTypesEnum::Time: + m_xSelectionLB->append(sId, aLst[i]); + if (static_cast<SwDateTimeField*>(GetCurField())->IsFixed() && !i) + m_xSelectionLB->select_id(sId); + if (!static_cast<SwDateTimeField*>(GetCurField())->IsFixed() && i) + m_xSelectionLB->select_id(sId); + break; + case SwFieldTypesEnum::ExtendedUser: + case SwFieldTypesEnum::DocumentStatistics: + m_xSelectionLB->append(sId, aLst[i]); + if (GetCurField()->GetSubType() == i) + m_xSelectionLB->select_id(sId); + break; + + case SwFieldTypesEnum::Author: + { + const OUString sFormat(GetFieldMgr().GetFormatStr(nTypeId, i)); + m_xSelectionLB->append(sId, sFormat); + m_xSelectionLB->select_text(GetFieldMgr().GetFormatStr(nTypeId, GetCurField()->GetFormat())); + break; + } + + default: + if (aLst[i] == GetCurField()->GetPar1()) + bInsert = true; + break; + } + if (bInsert) + { + m_xSelectionLB->append(sId, aLst[i]); + break; + } + } + } + m_xSelectionLB->connect_changed(Link<weld::TreeView&,void>()); + } + else + { + AddSubType(SwFieldTypesEnum::PageNumber); + AddSubType(SwFieldTypesEnum::PreviousPage); + AddSubType(SwFieldTypesEnum::NextPage); + nTypeId = static_cast<SwFieldTypesEnum>(m_xSelectionLB->get_id(0).toUInt32()); + nCount = 3; + m_xSelectionLB->connect_changed(LINK(this, SwFieldDokPage, SubTypeHdl)); + } + + bool bEnable = nCount != 0; + + if (bEnable && m_xSelectionLB->get_selected_index() == -1) + m_xSelectionLB->select(0); + + m_xSelection->set_sensitive( bEnable ); + + // fill Format-Listbox + sal_Int32 nSize = FillFormatLB(nTypeId); + + bool bValue = false, bLevel = false, bNumFormat = false, bOffset = false; + bool bFormat = nSize != 0; + bool bOneArea = false; + bool bFixed = false; + SvNumFormatType nFormatType = SvNumFormatType::ALL; + + switch (nTypeId) + { + case SwFieldTypesEnum::Date: + bFormat = bNumFormat = bOneArea = bOffset = true; + + nFormatType = SvNumFormatType::DATE; + + m_xDateFT->show(); + + m_xDateOffsetED->set_range(INT_MIN, INT_MAX); // no limit + + if (IsFieldEdit()) + m_xDateOffsetED->set_value( static_cast<SwDateTimeField*>(GetCurField())->GetOffset() / 24 / 60); + break; + + case SwFieldTypesEnum::Time: + bFormat = bNumFormat = bOneArea = bOffset = true; + + nFormatType = SvNumFormatType::TIME; + + m_xTimeFT->show(); + + m_xDateOffsetED->set_range(-1440, 1440); // one day + + if (IsFieldEdit()) + m_xDateOffsetED->set_value( static_cast<SwDateTimeField*>(GetCurField())->GetOffset() ); + break; + + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::NextPage: + if (IsFieldEdit()) + { + const sal_uInt16 nTmp = m_xFormatLB->get_selected_id().toUInt32(); + + if(SVX_NUM_CHAR_SPECIAL != nTmp) + { + sal_Int32 nOff = GetCurField()->GetPar2().toInt32(); + if( SwFieldTypesEnum::NextPage == nTypeId && 1 != nOff ) + m_xValueED->set_text( + OUString::number(nOff - 1) ); + else if( SwFieldTypesEnum::PreviousPage == nTypeId && -1 != nOff ) + m_xValueED->set_text( + OUString::number(nOff + 1) ); + else + m_xValueED->set_text(OUString()); + } + else + m_xValueED->set_text(static_cast<SwPageNumberField*>(GetCurField())->GetUserString()); + } + bValue = true; + break; + + case SwFieldTypesEnum::Chapter: + m_xValueFT->set_label(SwResId(STR_LEVEL)); + if (IsFieldEdit()) + m_xLevelED->set_text(OUString::number(static_cast<SwChapterField*>(GetCurField())->GetLevel(GetWrtShell()->GetLayout()) + 1)); + bLevel = true; + break; + + case SwFieldTypesEnum::PageNumber: + m_xValueFT->set_label( SwResId( STR_OFFSET )); + if (IsFieldEdit()) + m_xValueED->set_text(GetCurField()->GetPar2()); + bValue = true; + break; + + case SwFieldTypesEnum::ExtendedUser: + case SwFieldTypesEnum::Author: + case SwFieldTypesEnum::Filename: + bFixed = true; + break; + + default: + break; + } + + if (bNumFormat) + { + if (IsFieldEdit()) + { + m_xNumFormatLB->SetDefFormat(GetCurField()->GetFormat()); + + if (m_xNumFormatLB->GetFormatType() == (SvNumFormatType::DATE|SvNumFormatType::TIME)) + { + // always set Format-Type because otherwise when date/time formats are combined, + // both formats would be displayed at the same time + m_xNumFormatLB->SetFormatType(SvNumFormatType::ALL); + m_xNumFormatLB->SetFormatType(nFormatType); + // set correct format once again + m_xNumFormatLB->SetDefFormat(GetCurField()->GetFormat()); + } + } + else + m_xNumFormatLB->SetFormatType(nFormatType); + + m_xNumFormatLB->SetOneArea(bOneArea); + } + + m_xFormatLB->set_visible(!bNumFormat); + m_xNumFormatLB->set_visible(bNumFormat); + + m_xValueFT->set_visible(bValue); + m_xValueED->set_visible(bValue); + m_xLevelFT->set_visible(bLevel); + m_xLevelED->set_visible(bLevel); + m_xDateOffsetED->set_visible(bOffset); + m_xFixedCB->set_visible(!bValue && !bLevel && !bOffset); + + m_xFormat->set_sensitive(bFormat); + m_xFixedCB->set_sensitive(bFixed); + + if (IsFieldEdit()) + m_xFixedCB->set_active((GetCurField()->GetFormat() & AF_FIXED) != 0 && bFixed); + + if (m_xNumFormatLB->get_selected_index() == -1) + m_xNumFormatLB->select(0); + m_xValueFT->set_sensitive(bValue || bLevel || bOffset); + m_xValueED->set_sensitive(bValue); +} + +void SwFieldDokPage::AddSubType(SwFieldTypesEnum nTypeId) +{ + m_xSelectionLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldType::GetTypeStr(nTypeId)); +} + +IMPL_LINK_NOARG(SwFieldDokPage, SubTypeHdl, weld::TreeView&, void) +{ + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + if(nPos == -1) + nPos = 0; + + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xSelectionLB->get_id(nPos).toUInt32()); + FillFormatLB(nTypeId); + + const char* pTextRes = nullptr; + switch (nTypeId) + { + case SwFieldTypesEnum::Chapter: + pTextRes = STR_LEVEL; + break; + + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::NextPage: + pTextRes = SVX_NUM_CHAR_SPECIAL == m_xFormatLB->get_selected_id().toUInt32() + ? STR_VALUE : STR_OFFSET; + break; + + case SwFieldTypesEnum::PageNumber: + pTextRes = STR_OFFSET; + break; + default: break; + } + + if (pTextRes) + m_xValueFT->set_label(SwResId(pTextRes)); +} + +sal_Int32 SwFieldDokPage::FillFormatLB(SwFieldTypesEnum nTypeId) +{ + // fill Format-Listbox + m_xFormatLB->clear(); + + if (nTypeId == SwFieldTypesEnum::Author) + return m_xFormatLB->n_children(); + + const sal_uInt16 nSize = GetFieldMgr().GetFormatCount(nTypeId, IsFieldDlgHtmlMode()); + + for( sal_uInt16 i = 0; i < nSize; ++i ) + { + const sal_uInt16 nFormatId = GetFieldMgr().GetFormatId( nTypeId, i ); + OUString sId(OUString::number(nFormatId)); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(nTypeId, i)); + if (IsFieldEdit() && nFormatId == (GetCurField()->GetFormat() & ~AF_FIXED)) + m_xFormatLB->select_id(sId); + } + + if (nSize && m_xFormatLB->get_selected_index() == -1) + { + m_xFormatLB->select_text( SwResId(FMT_NUM_PAGEDESC) ); + if (m_xFormatLB->get_selected_index() == -1) + { + m_xFormatLB->select_text( SwResId(FMT_NUM_ARABIC) ); + if (m_xFormatLB->get_selected_index() == -1) + m_xFormatLB->select( 0 ); + } + } + + FormatHdl(*m_xFormatLB); + + return nSize; +} + +IMPL_LINK_NOARG(SwFieldDokPage, FormatHdl, weld::TreeView&, void) +{ + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + if (nTypeId == SwFieldTypesEnum::Unknown) + { + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + if(nPos == -1) + nPos = 0; + + nTypeId = static_cast<SwFieldTypesEnum>(m_xSelectionLB->get_id(nPos).toUInt32()); + } + + if (nTypeId == SwFieldTypesEnum::NextPage || nTypeId == SwFieldTypesEnum::PreviousPage) + { + // Prev/Next - PageNumFields special treatment: + sal_uInt16 nTmp = m_xFormatLB->get_selected_id().toUInt32(); + const OUString sOldText( m_xValueFT->get_label() ); + const OUString sNewText( SwResId( SVX_NUM_CHAR_SPECIAL == nTmp ? STR_VALUE + : STR_OFFSET )); + + if (sOldText != sNewText) + m_xValueFT->set_label(sNewText); + + if (sOldText != m_xValueFT->get_label()) + m_xValueED->set_text(OUString()); + } +} + +bool SwFieldDokPage::FillItemSet(SfxItemSet* ) +{ + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + if (nTypeId == SwFieldTypesEnum::Unknown) + { + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + if(nPos == -1) + nPos = 0; + nTypeId = static_cast<SwFieldTypesEnum>(m_xSelectionLB->get_id(nPos).toUInt32()); + } + + OUString aVal(m_xValueED->get_text()); + sal_uLong nFormat = 0; + sal_uInt16 nSubType = 0; + + if (m_xFormatLB->get_sensitive()) + { + sal_Int32 nPos = m_xFormatLB->get_selected_index(); + if(nPos != -1) + nFormat = m_xFormatLB->get_id(nPos).toUInt32(); + } + + if (m_xSelectionLB->get_sensitive()) + { + sal_Int32 nPos = m_xSelectionLB->get_selected_index(); + if(nPos != -1) + nSubType = m_xSelectionLB->get_id(nPos).toUInt32(); + } + + switch (nTypeId) + { + case SwFieldTypesEnum::Author: + nFormat = nSubType; + nSubType = 0; + [[fallthrough]]; + case SwFieldTypesEnum::ExtendedUser: + nFormat |= m_xFixedCB->get_active() ? AF_FIXED : 0; + break; + + case SwFieldTypesEnum::Filename: + nFormat |= m_xFixedCB->get_active() ? FF_FIXED : 0; + break; + + case SwFieldTypesEnum::Date: + case SwFieldTypesEnum::Time: + { + nFormat = m_xNumFormatLB->GetFormat(); + long nVal = static_cast< long >(m_xDateOffsetED->get_value()); + if (nTypeId == SwFieldTypesEnum::Date) + aVal = OUString::number(nVal * 60 * 24); + else + aVal = OUString::number(nVal); + break; + } + + case SwFieldTypesEnum::NextPage: + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::PageNumber: + case SwFieldTypesEnum::GetRefPage: + { + if( SVX_NUM_CHAR_SPECIAL != nFormat && + (SwFieldTypesEnum::PreviousPage == nTypeId || SwFieldTypesEnum::NextPage == nTypeId)) + { + sal_Int32 nVal = m_xValueED->get_text().toInt32(); + aVal = OUString::number(nVal); + } + break; + } + + case SwFieldTypesEnum::Chapter: + aVal = m_xLevelED->get_text(); + break; + + default: + break; + } + + if (!IsFieldEdit() || + nOldSel != m_xSelectionLB->get_selected_index() || + nOldFormat != nFormat || + m_xFixedCB->get_state_changed_from_saved() || + m_xValueED->get_value_changed_from_saved() || + m_xLevelED->get_value_changed_from_saved() || + m_xDateOffsetED->get_value_changed_from_saved()) + { + InsertField(nTypeId, nSubType, OUString(), aVal, nFormat, ' ', m_xNumFormatLB->IsAutomaticLanguage()); + } + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldDokPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet) +{ + return std::make_unique<SwFieldDokPage>(pPage, pController, pAttrSet); +} + +sal_uInt16 SwFieldDokPage::GetGroup() +{ + return GRP_DOC; +} + +void SwFieldDokPage::FillUserData() +{ + const sal_Int32 nEntryPos = m_xTypeLB->get_selected_index(); + const sal_uInt16 nTypeSel = ( -1 == nEntryPos ) + ? USHRT_MAX : m_xTypeLB->get_id(nEntryPos).toUInt32(); + SetUserData(USER_DATA_VERSION ";" + OUString::number( nTypeSel )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/flddok.hxx b/sw/source/ui/fldui/flddok.hxx new file mode 100644 index 000000000..ce61dd15e --- /dev/null +++ b/sw/source/ui/fldui/flddok.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_FLDUI_FLDDOK_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDDOK_HXX + +#include <sfx2/tabdlg.hxx> + +#include <numfmtlb.hxx> +#include "fldpage.hxx" + +class SwFieldDokPage : public SwFieldPage +{ + sal_Int32 nOldSel; + sal_uLong nOldFormat; + + std::unique_ptr<weld::TreeView> m_xTypeLB; + std::unique_ptr<weld::Widget> m_xSelection; + std::unique_ptr<weld::TreeView> m_xSelectionLB; + std::unique_ptr<weld::Label> m_xValueFT; + std::unique_ptr<weld::Entry> m_xValueED; + std::unique_ptr<weld::Label> m_xLevelFT; + std::unique_ptr<weld::SpinButton> m_xLevelED; + std::unique_ptr<weld::Label> m_xDateFT; + std::unique_ptr<weld::Label> m_xTimeFT; + std::unique_ptr<weld::SpinButton> m_xDateOffsetED; + std::unique_ptr<weld::Widget> m_xFormat; + std::unique_ptr<weld::TreeView> m_xFormatLB; + std::unique_ptr<SwNumFormatTreeView> m_xNumFormatLB; + std::unique_ptr<weld::CheckButton> m_xFixedCB; + + DECL_LINK(TypeHdl, weld::TreeView&, void); + DECL_LINK(FormatHdl, weld::TreeView&, void); + DECL_LINK(SubTypeHdl, weld::TreeView&, void); + + void AddSubType(SwFieldTypesEnum nTypeId); + sal_Int32 FillFormatLB(SwFieldTypesEnum nTypeId); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldDokPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * pSet); + + virtual ~SwFieldDokPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldedt.cxx b/sw/source/ui/fldui/fldedt.cxx new file mode 100644 index 000000000..2626d5c2d --- /dev/null +++ b/sw/source/ui/fldui/fldedt.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 <config_features.h> + +#include <sfx2/basedlgs.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/optgenrl.hxx> +#include <docufld.hxx> +#include <expfld.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include "flddb.hxx" +#include "flddinf.hxx" +#include "fldvar.hxx" +#include "flddok.hxx" +#include "fldfunc.hxx" +#include "fldref.hxx" +#include <fldedt.hxx> + +#include <cmdid.h> +#include <swabstdlg.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <memory> +#include <swuiexp.hxx> + +void SwFieldEditDlg::EnsureSelection(SwField *pCurField, SwFieldMgr &rMgr) +{ + if (pSh->CursorInsideInputField()) + { + // move cursor to start of Input Field + SwInputField* pInputField = dynamic_cast<SwInputField*>(pCurField); + if (pInputField && pInputField->GetFormatField()) + { + pSh->GotoField( *(pInputField->GetFormatField()) ); + } + else + { + SwSetExpField *const pSetField(dynamic_cast<SwSetExpField*>(pCurField)); + if (pSetField) + { + assert(pSetField->GetFormatField()); + pSh->GotoField( *(pSetField->GetFormatField()) ); + } + else + { + assert(!"what input field is this"); + } + } + } + + /* Only create selection if there is none already. + Normalize PaM instead of swapping. */ + if (!pSh->HasSelection()) + { + SwShellCursor* pCursor = pSh->getShellCursor(true); + SwPosition aOrigPos(*pCursor->GetPoint()); + + //After this attempt it is possible that rMgr.GetCurField() != pCurField if + //the field was in e.g. a zero height portion and so invisible in which + //case it will be skipped over + pSh->Right(CRSR_SKIP_CHARS, true, 1, false ); + //So (fdo#50640) if it didn't work then reposition back to the original + //location where the field was + SwField *pRealCurField = rMgr.GetCurField(); + bool bSelectionFailed = pCurField != pRealCurField; + if (bSelectionFailed) + { + pCursor->DeleteMark(); + *pCursor->GetPoint() = aOrigPos; + } + } + + pSh->NormalizePam(); + + assert(pCurField == rMgr.GetCurField()); +} + +SwFieldEditDlg::SwFieldEditDlg(SwView const & rVw) + : SfxSingleTabDialogController(rVw.GetViewFrame()->GetWindow().GetFrameWeld(), nullptr, + "modules/swriter/ui/editfielddialog.ui", "EditFieldDialog") + , pSh(rVw.GetWrtShellPtr()) + , m_xPrevBT(m_xBuilder->weld_button("prev")) + , m_xNextBT(m_xBuilder->weld_button("next")) + , m_xAddressBT(m_xBuilder->weld_button("edit")) +{ + SwFieldMgr aMgr(pSh); + + SwField *pCurField = aMgr.GetCurField(); + if (!pCurField) + return; + + SwViewShell::SetCareDialog(m_xDialog); + + EnsureSelection(pCurField, aMgr); + + sal_uInt16 nGroup = SwFieldMgr::GetGroup(pCurField->GetTypeId(), pCurField->GetSubType()); + + CreatePage(nGroup); + + GetOKButton().connect_clicked(LINK(this, SwFieldEditDlg, OKHdl)); + + m_xPrevBT->connect_clicked(LINK(this, SwFieldEditDlg, NextPrevHdl)); + m_xNextBT->connect_clicked(LINK(this, SwFieldEditDlg, NextPrevHdl)); + + m_xAddressBT->connect_clicked(LINK(this, SwFieldEditDlg, AddressHdl)); + + Init(); +} + +// initialise controls +void SwFieldEditDlg::Init() +{ + SwFieldPage* pTabPage = static_cast<SwFieldPage*>(GetTabPage()); + if (pTabPage) + { + SwFieldMgr& rMgr = pTabPage->GetFieldMgr(); + + SwField *pCurField = rMgr.GetCurField(); + + if(!pCurField) + return; + + // Traveling only when more than one field + pSh->StartAction(); + pSh->ClearMark(); + pSh->CreateCursor(); + + bool bMove = rMgr.GoNext(); + if( bMove ) + rMgr.GoPrev(); + m_xNextBT->set_sensitive(bMove); + + bMove = rMgr.GoPrev(); + if( bMove ) + rMgr.GoNext(); + m_xPrevBT->set_sensitive( bMove ); + + if (pCurField->GetTypeId() == SwFieldTypesEnum::ExtendedUser) + m_xAddressBT->set_sensitive(true); + else + m_xAddressBT->set_sensitive(false); + + pSh->DestroyCursor(); + pSh->EndAction(); + } + + GetOKButton().set_sensitive(!pSh->IsReadOnlyAvailable() || + !pSh->HasReadonlySel()); +} + +SfxTabPage* SwFieldEditDlg::CreatePage(sal_uInt16 nGroup) +{ + // create TabPage + std::unique_ptr<SfxTabPage> xTabPage; + + switch (nGroup) + { + case GRP_DOC: + xTabPage = SwFieldDokPage::Create(get_content_area(), this, nullptr); + break; + case GRP_FKT: + xTabPage = SwFieldFuncPage::Create(get_content_area(), this, nullptr); + break; + case GRP_REF: + xTabPage = SwFieldRefPage::Create(get_content_area(), this, nullptr); + break; + case GRP_REG: + { + SfxObjectShell* pDocSh = SfxObjectShell::Current(); + SfxItemSet* pSet = new SfxItemSet( pDocSh->GetPool(), svl::Items<SID_DOCINFO, SID_DOCINFO>{} ); + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocSh->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + uno::Reference< beans::XPropertySet > xUDProps( + xDocProps->getUserDefinedProperties(), + uno::UNO_QUERY_THROW); + pSet->Put( SfxUnoAnyItem( SID_DOCINFO, uno::makeAny(xUDProps) ) ); + xTabPage = SwFieldDokInfPage::Create(get_content_area(), this, pSet); + break; + } +#if HAVE_FEATURE_DBCONNECTIVITY + case GRP_DB: + xTabPage = SwFieldDBPage::Create(get_content_area(), this, nullptr); + static_cast<SwFieldDBPage*>(xTabPage.get())->SetWrtShell(*pSh); + break; +#endif + case GRP_VAR: + xTabPage = SwFieldVarPage::Create(get_content_area(), this, nullptr); + break; + + } + + assert(xTabPage); + + static_cast<SwFieldPage*>(xTabPage.get())->SetWrtShell(pSh); + SetTabPage(std::move(xTabPage)); + + return GetTabPage(); +} + +SwFieldEditDlg::~SwFieldEditDlg() +{ + SwViewShell::SetCareDialog(nullptr); + pSh->EnterStdMode(); +} + +void SwFieldEditDlg::EnableInsert(bool bEnable) +{ + if( bEnable && pSh->IsReadOnlyAvailable() && pSh->HasReadonlySel() ) + bEnable = false; + GetOKButton().set_sensitive(bEnable); +} + +void SwFieldEditDlg::InsertHdl() +{ + GetOKButton().clicked(); +} + +// kick off changing of the field +IMPL_LINK_NOARG(SwFieldEditDlg, OKHdl, weld::Button&, void) +{ + if (GetOKButton().get_sensitive()) + { + SfxTabPage* pTabPage = GetTabPage(); + if (pTabPage) + pTabPage->FillItemSet(nullptr); + m_xDialog->response(RET_OK); + } +} + +short SwFieldEditDlg::run() +{ + // without TabPage no dialog + return GetTabPage() ? SfxSingleTabDialogController::run() : static_cast<short>(RET_CANCEL); +} + +// Traveling between fields of the same type +IMPL_LINK(SwFieldEditDlg, NextPrevHdl, weld::Button&, rButton, void) +{ + bool bNext = &rButton == m_xNextBT.get(); + + pSh->EnterStdMode(); + + SwFieldType *pOldTyp = nullptr; + SwFieldPage* pTabPage = static_cast<SwFieldPage*>(GetTabPage()); + + //#112462# FillItemSet may delete the current field + //that's why it has to be called before accessing the current field + if (GetOKButton().get_sensitive()) + pTabPage->FillItemSet(nullptr); + + SwFieldMgr& rMgr = pTabPage->GetFieldMgr(); + SwField *pCurField = rMgr.GetCurField(); + if (pCurField->GetTypeId() == SwFieldTypesEnum::Database) + pOldTyp = pCurField->GetTyp(); + + rMgr.GoNextPrev( bNext, pOldTyp ); + pCurField = rMgr.GetCurField(); + + EnsureSelection(pCurField, rMgr); + + sal_uInt16 nGroup = SwFieldMgr::GetGroup(pCurField->GetTypeId(), pCurField->GetSubType()); + + if (nGroup != pTabPage->GetGroup()) + pTabPage = static_cast<SwFieldPage*>(CreatePage(nGroup)); + + pTabPage->EditNewField(); + + Init(); +} + +IMPL_LINK_NOARG(SwFieldEditDlg, AddressHdl, weld::Button&, void) +{ + SwFieldPage* pTabPage = static_cast<SwFieldPage*>(GetTabPage()); + SwFieldMgr& rMgr = pTabPage->GetFieldMgr(); + SwField *pCurField = rMgr.GetCurField(); + + SfxItemSet aSet( pSh->GetAttrPool(), + svl::Items<SID_FIELD_GRABFOCUS, SID_FIELD_GRABFOCUS>{} ); + + EditPosition nEditPos = EditPosition::UNKNOWN; + + switch(pCurField->GetSubType()) + { + case EU_FIRSTNAME: nEditPos = EditPosition::FIRSTNAME; break; + case EU_NAME: nEditPos = EditPosition::LASTNAME; break; + case EU_SHORTCUT: nEditPos = EditPosition::SHORTNAME; break; + case EU_COMPANY: nEditPos = EditPosition::COMPANY; break; + case EU_STREET: nEditPos = EditPosition::STREET; break; + case EU_TITLE: nEditPos = EditPosition::TITLE; break; + case EU_POSITION: nEditPos = EditPosition::POSITION; break; + case EU_PHONE_PRIVATE:nEditPos = EditPosition::TELPRIV; break; + case EU_PHONE_COMPANY:nEditPos = EditPosition::TELCOMPANY; break; + case EU_FAX: nEditPos = EditPosition::FAX; break; + case EU_EMAIL: nEditPos = EditPosition::EMAIL; break; + case EU_COUNTRY: nEditPos = EditPosition::COUNTRY; break; + case EU_ZIP: nEditPos = EditPosition::PLZ; break; + case EU_CITY: nEditPos = EditPosition::CITY; break; + case EU_STATE: nEditPos = EditPosition::STATE; break; + + default: nEditPos = EditPosition::UNKNOWN; break; + + } + aSet.Put(SfxUInt16Item(SID_FIELD_GRABFOCUS, static_cast<sal_uInt16>(nEditPos))); + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<SfxAbstractDialog> pDlg(rFact.CreateSwAddressAbstractDlg(m_xDialog.get(), aSet)); + if (RET_OK == pDlg->Execute()) + { + pSh->UpdateOneField(*pCurField); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldfunc.cxx b/sw/source/ui/fldui/fldfunc.cxx new file mode 100644 index 000000000..c2e6cbef2 --- /dev/null +++ b/sw/source/ui/fldui/fldfunc.cxx @@ -0,0 +1,607 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <strings.hrc> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include "fldfunc.hxx" +#include "flddinf.hxx" +#include <flddropdown.hxx> + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +using namespace ::com::sun::star; + +SwFieldFuncPage::SwFieldFuncPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet) + : SwFieldPage(pPage, pController, "modules/swriter/ui/fldfuncpage.ui", "FieldFuncPage", pCoreSet) + , nOldFormat(0) + , bDropDownLBChanged(false) + , m_xTypeLB(m_xBuilder->weld_tree_view("type")) + , m_xSelectionLB(m_xBuilder->weld_tree_view("select")) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) + , m_xFormatLB(m_xBuilder->weld_tree_view("format")) + , m_xNameFT(m_xBuilder->weld_label("nameft")) + , m_xNameED(new ConditionEdit(m_xBuilder->weld_entry("condFunction"))) + , m_xValueGroup(m_xBuilder->weld_widget("valuegroup")) + , m_xValueFT(m_xBuilder->weld_label("valueft")) + , m_xValueED(m_xBuilder->weld_entry("value")) + , m_xCond1FT(m_xBuilder->weld_label("cond1ft")) + , m_xCond1ED(new ConditionEdit(m_xBuilder->weld_entry("cond1"))) + , m_xCond2FT(m_xBuilder->weld_label("cond2ft")) + , m_xCond2ED(new ConditionEdit(m_xBuilder->weld_entry("cond2"))) + , m_xMacroBT(m_xBuilder->weld_button("macro")) + , m_xListGroup(m_xBuilder->weld_widget("listgroup")) + , m_xListItemFT(m_xBuilder->weld_label("itemft")) + , m_xListItemED(m_xBuilder->weld_entry("item")) + , m_xListAddPB(m_xBuilder->weld_button("add")) + , m_xListItemsFT(m_xBuilder->weld_label("listitemft")) + , m_xListItemsLB(m_xBuilder->weld_tree_view("listitems")) + , m_xListRemovePB(m_xBuilder->weld_button("remove")) + , m_xListUpPB(m_xBuilder->weld_button("up")) + , m_xListDownPB(m_xBuilder->weld_button("down")) + , m_xListNameFT(m_xBuilder->weld_label("listnameft")) + , m_xListNameED(m_xBuilder->weld_entry("listname")) +{ + FillFieldSelect(*m_xSelectionLB); + FillFieldSelect(*m_xFormatLB); + m_xListItemsLB->set_size_request(m_xListItemED->get_preferred_size().Width(), + m_xListItemsLB->get_height_rows(5)); + + auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + auto nHeight = m_xTypeLB->get_height_rows(20); + m_xTypeLB->set_size_request(nWidth, nHeight); + m_xFormatLB->set_size_request(nWidth, nHeight); + + m_xNameED->connect_changed(LINK(this, SwFieldFuncPage, ModifyHdl)); + + m_sOldValueFT = m_xValueFT->get_label(); + m_sOldNameFT = m_xNameFT->get_label(); + + m_xCond1ED->ShowBrackets(false); + m_xCond2ED->ShowBrackets(false); +} + +SwFieldFuncPage::~SwFieldFuncPage() +{ +} + +void SwFieldFuncPage::Reset(const SfxItemSet* ) +{ + SavePos(*m_xTypeLB); + Init(); // general initialisation + + m_xTypeLB->freeze(); + m_xTypeLB->clear(); + + if (!IsFieldEdit()) + { + // initialise TypeListBox + const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup()); + + // fill Typ-Listbox + for(sal_uInt16 i = rRg.nStart; i < rRg.nEnd; ++i) + { + const SwFieldTypesEnum nTypeId = SwFieldMgr::GetTypeId(i); + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i)); + } + } + else + { + const SwFieldTypesEnum nTypeId = GetCurField()->GetTypeId(); + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(SwFieldMgr::GetPos(nTypeId))); + + if (nTypeId == SwFieldTypesEnum::Macro) + { + GetFieldMgr().SetMacroPath(GetCurField()->GetPar1()); + } + } + + m_xTypeLB->connect_row_activated(LINK(this, SwFieldFuncPage, TreeViewInsertHdl)); + m_xTypeLB->connect_changed(LINK(this, SwFieldFuncPage, TypeHdl)); + m_xSelectionLB->connect_changed(LINK(this, SwFieldFuncPage, SelectHdl)); + m_xSelectionLB->connect_row_activated(LINK(this, SwFieldFuncPage, InsertMacroHdl)); + m_xFormatLB->connect_row_activated(LINK(this, SwFieldFuncPage, TreeViewInsertHdl)); + m_xMacroBT->connect_clicked(LINK(this, SwFieldFuncPage, MacroHdl)); + Link<weld::Button&,void> aListModifyLk( LINK(this, SwFieldFuncPage, ListModifyButtonHdl)); + m_xListAddPB->connect_clicked(aListModifyLk); + m_xListRemovePB->connect_clicked(aListModifyLk); + m_xListUpPB->connect_clicked(aListModifyLk); + m_xListDownPB->connect_clicked(aListModifyLk); + m_xListItemED->connect_activate(LINK(this, SwFieldFuncPage, ListModifyReturnActionHdl)); + Link<weld::Entry&,void> aListEnableLk = LINK(this, SwFieldFuncPage, ListEnableHdl); + m_xListItemED->connect_changed(aListEnableLk); + m_xListItemsLB->connect_changed(LINK(this, SwFieldFuncPage, ListEnableListBoxHdl)); + + int nSelect = -1; + if( !IsRefresh() ) + { + const OUString sUserData = GetUserData(); + sal_Int32 nIdx{ 0 }; + if(sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + const sal_uInt16 nVal = static_cast< sal_uInt16 >(sUserData.getToken(0, ';', nIdx).toInt32()); + if(nVal != USHRT_MAX) + { + for (sal_Int32 i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; ++i) + { + if (nVal == m_xTypeLB->get_id(i).toUInt32()) + { + nSelect = i; + break; + } + } + } + } + } + + m_xTypeLB->thaw(); + if (nSelect != -1) + m_xTypeLB->select(nSelect); + else + { + // select old Pos + RestorePos(*m_xTypeLB); + } + TypeHdl(*m_xTypeLB); + + if (IsFieldEdit()) + { + m_xNameED->save_value(); + m_xValueED->save_value(); + m_xCond1ED->save_value(); + m_xCond2ED->save_value(); + nOldFormat = GetCurField()->GetFormat(); + } +} + +static const char* FMT_MARK_ARY[] = +{ + FMT_MARK_TEXT, + FMT_MARK_TABLE, + FMT_MARK_FRAME, + FMT_MARK_GRAFIC, + FMT_MARK_OLE +}; + +IMPL_LINK_NOARG(SwFieldFuncPage, TypeHdl, weld::TreeView&, void) +{ + // save old ListBoxPos + const sal_Int32 nOld = GetTypeSel(); + + // current ListBoxPos + SetTypeSel(m_xTypeLB->get_selected_index()); + + if(GetTypeSel() == -1) + { + SetTypeSel(0); + m_xTypeLB->select(0); + } + + if (nOld == GetTypeSel()) + return; + + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + // fill Selection-Listbox + UpdateSubType(); + + // fill Format-Listbox + m_xFormatLB->clear(); + + const sal_uInt16 nSize = GetFieldMgr().GetFormatCount(nTypeId, IsFieldDlgHtmlMode()); + + for (sal_uInt16 i = 0; i < nSize; i++) + { + m_xFormatLB->append(OUString::number(GetFieldMgr().GetFormatId(nTypeId, i)), + GetFieldMgr().GetFormatStr(nTypeId, i)); + } + + if (nSize) + { + if (IsFieldEdit() && nTypeId == SwFieldTypesEnum::JumpEdit) + m_xFormatLB->select_text(SwResId(FMT_MARK_ARY[GetCurField()->GetFormat()])); + + if (m_xFormatLB->get_selected_index() == -1) + m_xFormatLB->select(0); + } + + bool bValue = false, bName = false, bMacro = false, bInsert = true; + bool bFormat = nSize != 0; + + // two controls for conditional text + bool bDropDown = SwFieldTypesEnum::Dropdown == nTypeId; + bool bCondTextField = SwFieldTypesEnum::ConditionalText == nTypeId; + + m_xCond1FT->set_visible(!bDropDown && bCondTextField); + m_xCond1ED->set_visible(!bDropDown && bCondTextField); + m_xCond2FT->set_visible(!bDropDown && bCondTextField); + m_xCond2ED->set_visible(!bDropDown && bCondTextField); + m_xValueGroup->set_visible(!bDropDown && !bCondTextField); + m_xMacroBT->set_visible(!bDropDown); + m_xNameED->set_visible(!bDropDown); + m_xNameFT->set_visible(!bDropDown); + + m_xListGroup->set_visible(bDropDown); + + m_xNameED->SetDropEnable(false); + + if (IsFieldEdit()) + { + if(bDropDown) + { + const SwDropDownField* pDrop = static_cast<const SwDropDownField*>(GetCurField()); + const uno::Sequence<OUString> aItems = pDrop->GetItemSequence(); + m_xListItemsLB->clear(); + for (const OUString& rItem : aItems) + m_xListItemsLB->append_text(rItem); + m_xListItemsLB->select_text(pDrop->GetSelectedItem()); + m_xListNameED->set_text(pDrop->GetPar2()); + m_xListNameED->save_value(); + bDropDownLBChanged = false; + } + else + { + m_xNameED->set_text(GetCurField()->GetPar1()); + m_xValueED->set_text(GetCurField()->GetPar2()); + } + } + else + { + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + } + if(bDropDown) + ListEnableHdl(*m_xListItemED); + + if (m_xNameFT->get_label() != m_sOldNameFT) + m_xNameFT->set_label(m_sOldNameFT); + if (m_xValueFT->get_label() != m_sOldValueFT) + m_xValueFT->set_label(m_sOldValueFT); + + switch (nTypeId) + { + case SwFieldTypesEnum::Macro: + bMacro = true; + if (!GetFieldMgr().GetMacroPath().isEmpty()) + bValue = true; + else + bInsert = false; + + m_xNameFT->set_label(SwResId(STR_MACNAME)); + m_xValueFT->set_label(SwResId(STR_PROMPT)); + m_xNameED->set_text(GetFieldMgr().GetMacroName()); + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + break; + + case SwFieldTypesEnum::HiddenParagraph: + m_xNameFT->set_label(SwResId(STR_COND)); + m_xNameED->SetDropEnable(true); + bName = true; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + break; + + case SwFieldTypesEnum::HiddenText: + { + m_xNameFT->set_label(SwResId(STR_COND)); + m_xNameED->SetDropEnable(true); + m_xValueFT->set_label(SwResId(STR_INSTEXT)); + SwWrtShell* pSh = GetActiveWrtShell(); + if (!IsFieldEdit() && pSh ) + m_xValueED->set_text(pSh->GetSelText()); + bName = bValue = true; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + } + break; + + case SwFieldTypesEnum::ConditionalText: + m_xNameFT->set_label(SwResId(STR_COND)); + m_xNameED->SetDropEnable(true); + if (IsFieldEdit()) + { + sal_Int32 nIdx{ 0 }; + m_xCond1ED->set_text(GetCurField()->GetPar2().getToken(0, '|', nIdx)); + m_xCond2ED->set_text(GetCurField()->GetPar2().getToken(0, '|', nIdx)); + } + + bName = bValue = true; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + break; + + case SwFieldTypesEnum::JumpEdit: + m_xNameFT->set_label(SwResId(STR_JUMPEDITFLD)); + m_xValueFT->set_label(SwResId(STR_PROMPT)); + bName = bValue = true; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + break; + + case SwFieldTypesEnum::Input: + m_xValueFT->set_label(SwResId(STR_PROMPT)); + bValue = true; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + break; + + case SwFieldTypesEnum::CombinedChars: + { + m_xNameFT->set_label(SwResId(STR_COMBCHRS_FT)); + m_xNameED->SetDropEnable(true); + bName = true; + + const sal_Int32 nLen = m_xNameED->get_text().getLength(); + if( !nLen || nLen > MAX_COMBINED_CHARACTERS ) + bInsert = false; + m_xNameED->set_accessible_name(m_xNameFT->get_label()); + m_xValueED->set_accessible_name(m_xValueFT->get_label()); + } + break; + case SwFieldTypesEnum::Dropdown : + break; + default: + break; + } + + m_xSelectionLB->hide(); + + m_xFormat->set_sensitive(bFormat); + m_xNameFT->set_sensitive(bName); + m_xNameED->set_sensitive(bName); + m_xValueGroup->set_sensitive(bValue); + m_xMacroBT->set_sensitive(bMacro); + + EnableInsert( bInsert ); +} + +IMPL_LINK_NOARG(SwFieldFuncPage, SelectHdl, weld::TreeView&, void) +{ + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + if( SwFieldTypesEnum::Macro == nTypeId ) + m_xNameED->set_text( m_xSelectionLB->get_selected_text() ); +} + +IMPL_LINK_NOARG(SwFieldFuncPage, InsertMacroHdl, weld::TreeView&, bool) +{ + SelectHdl(*m_xSelectionLB); + InsertHdl(nullptr); + return true; +} + +IMPL_LINK(SwFieldFuncPage, ListModifyButtonHdl, weld::Button&, rControl, void) +{ + ListModifyHdl(&rControl); +} + +IMPL_LINK(SwFieldFuncPage, ListModifyReturnActionHdl, weld::Entry&, rControl, bool) +{ + ListModifyHdl(&rControl); + return true; +} + +void SwFieldFuncPage::ListModifyHdl(const weld::Widget* pControl) +{ + if (pControl == m_xListAddPB.get() || + (pControl == m_xListItemED.get() && m_xListAddPB->get_sensitive())) + { + const OUString sEntry(m_xListItemED->get_text()); + m_xListItemsLB->append_text(sEntry); + m_xListItemsLB->select_text(sEntry); + } + else if (m_xListItemsLB->get_selected_index() != -1) + { + sal_Int32 nSelPos = m_xListItemsLB->get_selected_index(); + if (pControl == m_xListRemovePB.get()) + { + m_xListItemsLB->remove(nSelPos); + m_xListItemsLB->select(nSelPos ? nSelPos - 1 : 0); + } + else if (pControl == m_xListUpPB.get()) + { + if(nSelPos) + { + const OUString sEntry = m_xListItemsLB->get_selected_text(); + m_xListItemsLB->remove(nSelPos); + nSelPos--; + m_xListItemsLB->insert_text(nSelPos, sEntry); + m_xListItemsLB->select(nSelPos); + } + } + else if (pControl == m_xListDownPB.get()) + { + if( nSelPos < m_xListItemsLB->n_children() - 1) + { + const OUString sEntry = m_xListItemsLB->get_selected_text(); + m_xListItemsLB->remove(nSelPos); + nSelPos++; + m_xListItemsLB->insert_text(nSelPos, sEntry); + m_xListItemsLB->select(nSelPos); + } + } + } + bDropDownLBChanged = true; + ListEnableHdl(*m_xListItemED); +} + +IMPL_LINK_NOARG(SwFieldFuncPage, ListEnableListBoxHdl, weld::TreeView&, void) +{ + ListEnableHdl(*m_xListItemED); +} + +IMPL_LINK_NOARG(SwFieldFuncPage, ListEnableHdl, weld::Entry&, void) +{ + //enable "Add" button when text is in the Edit that's not already member of the box + m_xListAddPB->set_sensitive(!m_xListItemED->get_text().isEmpty() && + -1 == m_xListItemsLB->find_text(m_xListItemED->get_text())); + bool bEnableButtons = m_xListItemsLB->get_selected_index() != -1; + m_xListRemovePB->set_sensitive(bEnableButtons); + m_xListUpPB->set_sensitive(bEnableButtons && (m_xListItemsLB->get_selected_index() > 0)); + m_xListDownPB->set_sensitive(bEnableButtons && + (m_xListItemsLB->get_selected_index() < (m_xListItemsLB->n_children() - 1))); +} + +// renew types in SelectionBox +void SwFieldFuncPage::UpdateSubType() +{ + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + // fill Selection-Listbox + m_xSelectionLB->freeze(); + m_xSelectionLB->clear(); + + std::vector<OUString> aLst; + GetFieldMgr().GetSubTypes(nTypeId, aLst); + const size_t nCount = aLst.size(); + + for (size_t i = 0; i < nCount; ++i) + m_xSelectionLB->append(OUString::number(i), aLst[i]); + m_xSelectionLB->thaw(); + + bool bEnable = nCount != 0; + + m_xSelectionLB->set_sensitive( bEnable ); + + if (bEnable) + m_xSelectionLB->select(0); + + if (nTypeId == SwFieldTypesEnum::Macro) + { + const bool bHasMacro = !GetFieldMgr().GetMacroPath().isEmpty(); + + if (bHasMacro) + { + m_xNameED->set_text(GetFieldMgr().GetMacroName()); + m_xValueGroup->set_sensitive(true); + } + EnableInsert(bHasMacro); + } +} + +// call MacroBrowser, fill Listbox with Macros +IMPL_LINK_NOARG( SwFieldFuncPage, MacroHdl, weld::Button&, void) +{ + if (GetFieldMgr().ChooseMacro(GetFrameWeld())) + UpdateSubType(); +} + +bool SwFieldFuncPage::FillItemSet(SfxItemSet* ) +{ + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + sal_uInt16 nSubType = 0; + + const sal_Int32 nEntryPos = m_xFormatLB->get_selected_index(); + const sal_uLong nFormat = (nEntryPos == -1) + ? 0 : m_xFormatLB->get_id(nEntryPos).toUInt32(); + + OUString aVal(m_xValueED->get_text()); + OUString aName(m_xNameED->get_text()); + + switch(nTypeId) + { + case SwFieldTypesEnum::Input: + nSubType = INP_TXT; + // to prevent removal of CR/LF restore old content + if (!m_xNameED->get_value_changed_from_saved() && IsFieldEdit()) + aName = GetCurField()->GetPar1(); + + break; + + case SwFieldTypesEnum::Macro: + // use the full script URL, not the name in the Edit control + aName = GetFieldMgr().GetMacroPath(); + break; + + case SwFieldTypesEnum::ConditionalText: + aVal = m_xCond1ED->get_text() + "|" + m_xCond2ED->get_text(); + break; + case SwFieldTypesEnum::Dropdown : + { + aName = m_xListNameED->get_text(); + for (sal_Int32 i = 0, nEntryCount = m_xListItemsLB->n_children(); i < nEntryCount; ++i) + { + if(i) + aVal += OUStringChar(DB_DELIM); + aVal += m_xListItemsLB->get_text(i); + } + } + break; + default: + break; + } + + if (!IsFieldEdit() || + m_xNameED->get_value_changed_from_saved() || + m_xValueED->get_value_changed_from_saved() || + m_xCond1ED->get_value_changed_from_saved() || + m_xCond2ED->get_value_changed_from_saved() || + m_xListNameED->get_value_changed_from_saved() || + bDropDownLBChanged || + nOldFormat != nFormat) + { + InsertField( nTypeId, nSubType, aName, aVal, nFormat ); + } + + ModifyHdl(m_xNameED->get_widget()); // enable/disable Insert if applicable + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldFuncPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet) +{ + return std::make_unique<SwFieldFuncPage>(pPage, pController, pAttrSet); +} + +sal_uInt16 SwFieldFuncPage::GetGroup() +{ + return GRP_FKT; +} + +void SwFieldFuncPage::FillUserData() +{ + const sal_Int32 nEntryPos = m_xTypeLB->get_selected_index(); + const sal_uInt16 nTypeSel = ( -1 == nEntryPos ) + ? USHRT_MAX + : m_xTypeLB->get_id(nEntryPos).toUInt32(); + SetUserData(USER_DATA_VERSION ";" + OUString::number( nTypeSel )); +} + +IMPL_LINK_NOARG(SwFieldFuncPage, ModifyHdl, weld::Entry&, void) +{ + const sal_Int32 nLen = m_xNameED->get_text().getLength(); + + bool bEnable = true; + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + if( SwFieldTypesEnum::CombinedChars == nTypeId && + (!nLen || nLen > MAX_COMBINED_CHARACTERS )) + bEnable = false; + + EnableInsert( bEnable ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldfunc.hxx b/sw/source/ui/fldui/fldfunc.hxx new file mode 100644 index 000000000..364a71d8e --- /dev/null +++ b/sw/source/ui/fldui/fldfunc.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_UI_FLDUI_FLDFUNC_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDFUNC_HXX + +#include <sfx2/tabdlg.hxx> + +#include <condedit.hxx> +#include "fldpage.hxx" + +class SwFieldFuncPage : public SwFieldPage +{ + OUString m_sOldValueFT; + OUString m_sOldNameFT; + + sal_uLong nOldFormat; + bool bDropDownLBChanged; + + std::unique_ptr<weld::TreeView> m_xTypeLB; + std::unique_ptr<weld::TreeView> m_xSelectionLB; + std::unique_ptr<weld::Widget> m_xFormat; + std::unique_ptr<weld::TreeView> m_xFormatLB; + std::unique_ptr<weld::Label> m_xNameFT; + std::unique_ptr<ConditionEdit> m_xNameED; + std::unique_ptr<weld::Widget> m_xValueGroup; + std::unique_ptr<weld::Label> m_xValueFT; + std::unique_ptr<weld::Entry> m_xValueED; + std::unique_ptr<weld::Label> m_xCond1FT; + std::unique_ptr<ConditionEdit> m_xCond1ED; + std::unique_ptr<weld::Label> m_xCond2FT; + std::unique_ptr<ConditionEdit> m_xCond2ED; + std::unique_ptr<weld::Button> m_xMacroBT; + + //controls of "Input list" + std::unique_ptr<weld::Widget> m_xListGroup; + std::unique_ptr<weld::Label> m_xListItemFT; + std::unique_ptr<weld::Entry> m_xListItemED; + std::unique_ptr<weld::Button> m_xListAddPB; + std::unique_ptr<weld::Label> m_xListItemsFT; + std::unique_ptr<weld::TreeView> m_xListItemsLB; + std::unique_ptr<weld::Button> m_xListRemovePB; + std::unique_ptr<weld::Button> m_xListUpPB; + std::unique_ptr<weld::Button> m_xListDownPB; + std::unique_ptr<weld::Label> m_xListNameFT; + std::unique_ptr<weld::Entry> m_xListNameED; + + DECL_LINK( TypeHdl, weld::TreeView&, void ); + DECL_LINK( SelectHdl, weld::TreeView&, void ); + DECL_LINK( InsertMacroHdl, weld::TreeView&, bool ); + DECL_LINK( ModifyHdl, weld::Entry&, void ); + DECL_LINK( ListModifyReturnActionHdl, weld::Entry&, bool ); + DECL_LINK( ListModifyButtonHdl, weld::Button&, void ); + DECL_LINK( ListEnableHdl, weld::Entry&, void ); + DECL_LINK( ListEnableListBoxHdl, weld::TreeView&, void ); + void ListModifyHdl(const weld::Widget*); + + // select Macro + DECL_LINK( MacroHdl, weld::Button&, void ); + + void UpdateSubType(); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldFuncPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pSet); + virtual ~SwFieldFuncPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldpage.cxx b/sw/source/ui/fldui/fldpage.cxx new file mode 100644 index 000000000..606f149ab --- /dev/null +++ b/sw/source/ui/fldui/fldpage.cxx @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/stritem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/viewfrm.hxx> +#include <dbfld.hxx> +#include <flddat.hxx> +#include <fmtfld.hxx> +#include <viewopt.hxx> +#include <fldedt.hxx> +#include <docsh.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <expfld.hxx> +#include <fldtdlg.hxx> +#include "fldpage.hxx" +#include <docufld.hxx> +#include <cmdid.h> +#include <sfx2/bindings.hxx> + +using namespace ::com::sun::star; + +// note: pAttrSet may be null if the dialog is restored on startup +SwFieldPage::SwFieldPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, + const OString& rID, const SfxItemSet *pAttrSet) + : SfxTabPage(pPage, pController, rUIXMLDescription, rID, pAttrSet) + , m_pCurField(nullptr) + , m_pWrtShell(nullptr) + , m_nTypeSel(-1) + , m_nSelectionSel(-1) + , m_bFieldEdit(false) + , m_bInsert(true) + , m_bFieldDlgHtmlMode(false) + , m_bRefresh(false) + , m_bFirstHTMLInit(true) +{ +} + +SwFieldPage::~SwFieldPage() +{ +} + +// initialise TabPage +void SwFieldPage::Init() +{ + SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current()); + bool bNewMode = 0 != (::GetHtmlMode(pDocSh) & HTMLMODE_ON); + + m_bFieldEdit = nullptr == dynamic_cast<SwFieldDlg*>(GetDialogController()); + + // newly initialise FieldManager. important for + // Dok-Switch (fldtdlg:ReInitTabPage) + m_pCurField = m_aMgr.GetCurField(); + + if( bNewMode != m_bFieldDlgHtmlMode ) + { + m_bFieldDlgHtmlMode = bNewMode; + + // initialise Rangelistbox + if( m_bFieldDlgHtmlMode && m_bFirstHTMLInit ) + { + m_bFirstHTMLInit = false; + SwWrtShell *pSh = m_pWrtShell; + if(! pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + SwDoc* pDoc = pSh->GetDoc(); + pSh->InsertFieldType( SwSetExpFieldType( pDoc, + "HTML_ON", 1)); + pSh->InsertFieldType( SwSetExpFieldType(pDoc, + "HTML_OFF", 1)); + } + } + } +} + +// newly initialise page +void SwFieldPage::Activate() +{ + EnableInsert(m_bInsert); +} + +// complete reset; edit new field +void SwFieldPage::EditNewField( bool bOnlyActivate ) +{ + if (!bOnlyActivate) + m_nTypeSel = -1; + m_nSelectionSel = -1; + m_bRefresh = true; + Reset(nullptr); + m_bRefresh = false; +} + +// insert field +void SwFieldPage::InsertField(SwFieldTypesEnum nTypeId, sal_uInt16 nSubType, const OUString& rPar1, + const OUString& rPar2, sal_uInt32 nFormatId, + sal_Unicode cSeparator, bool bIsAutomaticLanguage) +{ + SwView* pView = GetActiveView(); + SwWrtShell *pSh = m_pWrtShell ? m_pWrtShell : pView->GetWrtShellPtr(); + + if (!IsFieldEdit()) // insert new field + { + SwInsertField_Data aData(nTypeId, nSubType, rPar1, rPar2, nFormatId, nullptr, cSeparator, bIsAutomaticLanguage ); + //#i26566# provide parent for SwWrtShell::StartInputFieldDlg + aData.m_pParent = &GetDialogController()->GetOKButton(); + m_aMgr.InsertField( aData ); + + uno::Reference< frame::XDispatchRecorder > xRecorder = + pView->GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + bool bRecordDB = SwFieldTypesEnum::Database == nTypeId || + SwFieldTypesEnum::DatabaseSetNumber == nTypeId || + SwFieldTypesEnum::DatabaseNumberSet == nTypeId || + SwFieldTypesEnum::DatabaseNextSet == nTypeId || + SwFieldTypesEnum::DatabaseName == nTypeId ; + + SfxRequest aReq( pView->GetViewFrame(), + bRecordDB ? FN_INSERT_DBFIELD : FN_INSERT_FIELD ); + if(bRecordDB) + { + sal_Int32 nIdx{ 0 }; + aReq.AppendItem(SfxStringItem + (FN_INSERT_DBFIELD,rPar1.getToken(0, DB_DELIM, nIdx))); + aReq.AppendItem(SfxStringItem + (FN_PARAM_1,rPar1.getToken(0, DB_DELIM, nIdx))); + aReq.AppendItem(SfxInt32Item + (FN_PARAM_3,rPar1.getToken(0, DB_DELIM, nIdx).toInt32())); + aReq.AppendItem(SfxStringItem + (FN_PARAM_2,rPar1.getToken(0, DB_DELIM, nIdx))); + } + else + { + aReq.AppendItem(SfxStringItem(FN_INSERT_FIELD, rPar1)); + aReq.AppendItem(SfxStringItem + (FN_PARAM_3, OUString(cSeparator))); + aReq.AppendItem(SfxUInt16Item(FN_PARAM_FIELD_SUBTYPE, nSubType)); + } + aReq.AppendItem(SfxUInt16Item(FN_PARAM_FIELD_TYPE , static_cast<sal_uInt16>(nTypeId))); + aReq.AppendItem(SfxStringItem(FN_PARAM_FIELD_CONTENT, rPar2)); + aReq.AppendItem(SfxUInt32Item(FN_PARAM_FIELD_FORMAT , nFormatId)); + aReq.Done(); + } + + } + else // change field + { + std::unique_ptr<SwField> pTmpField = m_pCurField->CopyField(); + + OUString sPar1(rPar1); + OUString sPar2(rPar2); + switch( nTypeId ) + { + case SwFieldTypesEnum::Date: + case SwFieldTypesEnum::Time: + nSubType = static_cast< sal_uInt16 >(((nTypeId == SwFieldTypesEnum::Date) ? DATEFLD : TIMEFLD) | + ((nSubType == DATE_VAR) ? 0 : FIXEDFLD)); + break; + + case SwFieldTypesEnum::DatabaseName: + case SwFieldTypesEnum::DatabaseNextSet: + case SwFieldTypesEnum::DatabaseNumberSet: + case SwFieldTypesEnum::DatabaseSetNumber: + { + sal_Int32 nPos = 0; + SwDBData aData; + + aData.sDataSource = rPar1.getToken(0, DB_DELIM, nPos); + aData.sCommand = rPar1.getToken(0, DB_DELIM, nPos); + aData.nCommandType = rPar1.getToken(0, DB_DELIM, nPos).toInt32(); + sPar1 = rPar1.copy(nPos); + + static_cast<SwDBNameInfField*>(pTmpField.get())->SetDBData(aData); + } + break; + + case SwFieldTypesEnum::Database: + { + SwDBData aData; + sal_Int32 nIdx{ 0 }; + aData.sDataSource = rPar1.getToken(0, DB_DELIM, nIdx); + aData.sCommand = rPar1.getToken(0, DB_DELIM, nIdx); + aData.nCommandType = rPar1.getToken(0, DB_DELIM, nIdx).toInt32(); + OUString sColumn = rPar1.getToken(0, DB_DELIM, nIdx); + + auto pOldType = static_cast<SwDBFieldType*>(pTmpField->GetTyp()); + auto pType = static_cast<SwDBFieldType*>(pSh->InsertFieldType(SwDBFieldType(pSh->GetDoc(), sColumn, aData))); + if(auto pFormatField = pOldType->FindFormatForField(m_pCurField)) + { + pFormatField->RegisterToFieldType(*pType); + pTmpField->ChgTyp(pType); + } + } + break; + + case SwFieldTypesEnum::Sequence: + { + SwSetExpFieldType* pTyp = static_cast<SwSetExpFieldType*>(pTmpField->GetTyp()); + pTyp->SetOutlineLvl( static_cast< sal_uInt8 >(nSubType & 0xff)); + pTyp->SetDelimiter(OUString(cSeparator)); + + nSubType = nsSwGetSetExpType::GSE_SEQ; + } + break; + + case SwFieldTypesEnum::Input: + { + // User- or SetField ? + if (m_aMgr.GetFieldType(SwFieldIds::User, sPar1) == nullptr && + !(pTmpField->GetSubType() & INP_TXT)) // SETEXPFLD + { + SwSetExpField* pField = static_cast<SwSetExpField*>(pTmpField.get()); + pField->SetPromptText(sPar2); + sPar2 = pField->GetPar2(); + } + } + break; + case SwFieldTypesEnum::DocumentInfo: + { + if( nSubType == nsSwDocInfoSubType::DI_CUSTOM ) + { + SwDocInfoField* pDocInfo = static_cast<SwDocInfoField*>( pTmpField.get() ); + pDocInfo->SetName( rPar1 ); + } + } + break; + default: break; + } + + pSh->StartAllAction(); + + pTmpField->SetSubType(nSubType); + pTmpField->SetAutomaticLanguage(bIsAutomaticLanguage); + + m_aMgr.UpdateCurField( nFormatId, sPar1, sPar2, std::move(pTmpField) ); + + m_pCurField = m_aMgr.GetCurField(); + + switch (nTypeId) + { + case SwFieldTypesEnum::HiddenText: + case SwFieldTypesEnum::HiddenParagraph: + m_aMgr.EvalExpFields(pSh); + break; + default: break; + } + + pSh->SetUndoNoResetModified(); + pSh->EndAllAction(); + } +} + +void SwFieldPage::SavePos( const weld::TreeView& rLst1 ) +{ + if (rLst1.n_children()) + m_aLstStrArr[ 0 ] = rLst1.get_selected_text(); + else + m_aLstStrArr[ 0 ].clear(); + m_aLstStrArr[ 1 ].clear(); + m_aLstStrArr[ 2 ].clear(); +} + +void SwFieldPage::RestorePos(weld::TreeView& rLst1) +{ + sal_Int32 nPos = 0; + if (rLst1.n_children() && !m_aLstStrArr[ 0 ].isEmpty() && + -1 != ( nPos = rLst1.find_text(m_aLstStrArr[ 0 ] ) ) ) + rLst1.select( nPos ); +} + +// Insert new fields +IMPL_LINK( SwFieldPage, TreeViewInsertHdl, weld::TreeView&, rBox, bool ) +{ + InsertHdl(&rBox); + return true; +} + +void SwFieldPage::InsertHdl(weld::Widget* pBtn) +{ + if (SwFieldDlg *pDlg = dynamic_cast<SwFieldDlg*>(GetDialogController())) + { + pDlg->InsertHdl(); + + if (pBtn) + pBtn->grab_focus(); // because of InputField-Dlg + } + else + { + SwFieldEditDlg *pEditDlg = static_cast<SwFieldEditDlg*>(GetDialogController()); + pEditDlg->InsertHdl(); + } +} + +// enable/disable "Insert"-Button +void SwFieldPage::EnableInsert(bool bEnable) +{ + if (SwFieldDlg *pDlg = dynamic_cast<SwFieldDlg*>(GetDialogController())) + { + if (pDlg->GetCurTabPage() == this) + pDlg->EnableInsert(bEnable); + } + else + { + SwFieldEditDlg *pEditDlg = static_cast<SwFieldEditDlg*>(GetDialogController()); + pEditDlg->EnableInsert(bEnable); + } + + m_bInsert = bEnable; +} + +IMPL_LINK_NOARG(SwFieldPage, NumFormatHdl, weld::TreeView&, bool) +{ + InsertHdl(nullptr); + return true; +} + +void SwFieldPage::SetWrtShell( SwWrtShell* pShell ) +{ + m_pWrtShell = pShell; + m_aMgr.SetWrtShell( pShell ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldpage.hxx b/sw/source/ui/fldui/fldpage.hxx new file mode 100644 index 000000000..9c0cecf5a --- /dev/null +++ b/sw/source/ui/fldui/fldpage.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_UI_FLDUI_FLDPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDPAGE_HXX + +#include <sfx2/tabdlg.hxx> +#include <fldmgr.hxx> + +#define FIELD_COLUMN_WIDTH 19 + +const int coLBCount = 3; + +class SwFieldPage : public SfxTabPage +{ + OUString m_aLstStrArr[ coLBCount ]; + SwFieldMgr m_aMgr; + SwField *m_pCurField; + SwWrtShell* m_pWrtShell; + sal_Int32 m_nTypeSel; + sal_Int32 m_nSelectionSel; + bool m_bFieldEdit; + bool m_bInsert; + bool m_bFieldDlgHtmlMode; + bool m_bRefresh; + bool m_bFirstHTMLInit; + +protected: + + sal_Int32 GetTypeSel() const { return m_nTypeSel;} + void SetTypeSel(sal_Int32 nSet) { m_nTypeSel = nSet;} + sal_Int32 GetSelectionSel() const { return m_nSelectionSel;} + void SetSelectionSel(sal_Int32 nSet){ m_nSelectionSel = nSet;} + bool IsFieldDlgHtmlMode() const { return m_bFieldDlgHtmlMode;} + bool IsRefresh() const { return m_bRefresh;} + SwField* GetCurField() { return m_pCurField;} + SwWrtShell* GetWrtShell() { return m_pWrtShell;} + + DECL_LINK( TreeViewInsertHdl, weld::TreeView&, bool ); + DECL_LINK( NumFormatHdl, weld::TreeView&, bool ); + void InsertHdl(weld::Widget*); + + void Init(); + void SavePos( const weld::TreeView& rLst1); + void RestorePos( weld::TreeView& rLst1 ); + void EnableInsert(bool bEnable); + bool IsFieldEdit() const { return m_bFieldEdit; } + + // insert field + void InsertField(SwFieldTypesEnum nTypeId, + sal_uInt16 nSubType, + const OUString& rPar1, + const OUString& rPar2, + sal_uInt32 nFormatId, + sal_Unicode cDelim = ' ', + bool bIsAutomaticLanguage = true); + +public: + SwFieldPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, + const OString& rID, const SfxItemSet *pAttrSet); + + virtual ~SwFieldPage() override; + + virtual void Activate() override; + + SwFieldMgr& GetFieldMgr() { return m_aMgr; } + void SetWrtShell( SwWrtShell* m_pWrtShell ); + void EditNewField( bool bOnlyActivate = false ); + virtual sal_uInt16 GetGroup() = 0; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldref.cxx b/sw/source/ui/fldui/fldref.cxx new file mode 100644 index 000000000..9a94726ad --- /dev/null +++ b/sw/source/ui/fldui/fldref.cxx @@ -0,0 +1,1126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <IMark.hxx> +#include <expfld.hxx> +#include <swmodule.hxx> +#include "fldref.hxx" +#include <reffld.hxx> +#include <wrtsh.hxx> + +#include <fldref.hrc> +#include <strings.hrc> +#include <SwNodeNum.hxx> +#include <IDocumentMarkAccess.hxx> +#include <unotools/syslocaleoptions.hxx> +#include <unotools/charclass.hxx> + +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> + +#define REFFLDFLAG 0x4000 +#define REFFLDFLAG_BOOKMARK 0x4800 +#define REFFLDFLAG_FOOTNOTE 0x5000 +#define REFFLDFLAG_ENDNOTE 0x6000 +// #i83479# +#define REFFLDFLAG_HEADING 0x7100 +#define REFFLDFLAG_NUMITEM 0x7200 + +static sal_uInt16 nFieldDlgFormatSel = 0; + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +SwFieldRefPage::SwFieldRefPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet ) + : SwFieldPage(pPage, pController, "modules/swriter/ui/fldrefpage.ui", "FieldRefPage", pCoreSet) + , maOutlineNodes() + , maNumItems() + , mpSavedSelectedTextNode(nullptr) + , mnSavedSelectedPos(0) + , m_xTypeLB(m_xBuilder->weld_tree_view("type")) + , m_xSelection(m_xBuilder->weld_widget("selectframe")) + , m_xSelectionLB(m_xBuilder->weld_tree_view("select")) + , m_xSelectionToolTipLB(m_xBuilder->weld_tree_view("selecttip")) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) + , m_xFormatLB(m_xBuilder->weld_tree_view("format")) + , m_xNameFT(m_xBuilder->weld_label("nameft")) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xValueED(m_xBuilder->weld_entry("value")) + , m_xFilterED(m_xBuilder->weld_entry("filter")) +{ + m_xSelectionLB->make_sorted(); + // #i83479# + for (size_t i = 0; i < SAL_N_ELEMENTS(FLD_REF_PAGE_TYPES); ++i) + { + m_xTypeLB->append_text(SwResId(FLD_REF_PAGE_TYPES[i])); + m_xFormatLB->append_text(SwResId(FLD_REF_PAGE_TYPES[i])); + } + + sBookmarkText = m_xTypeLB->get_text(0); + sFootnoteText = m_xTypeLB->get_text(1); + sEndnoteText = m_xTypeLB->get_text(2); + // #i83479# + sHeadingText = m_xTypeLB->get_text(3); + sNumItemText = m_xTypeLB->get_text(4); + + auto nHeight = m_xTypeLB->get_height_rows(8); + auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + m_xTypeLB->set_size_request(nWidth, nHeight); + m_xFormatLB->set_size_request(nWidth, nHeight); + m_xSelection->set_size_request(nWidth * 2, nHeight); + nHeight = m_xTypeLB->get_height_rows(20); + m_xSelectionToolTipLB->set_size_request(nHeight, nWidth*2); + + m_xTypeLB->clear(); + + m_xNameED->connect_changed(LINK(this, SwFieldRefPage, ModifyHdl)); + m_xFilterED->connect_changed( LINK( this, SwFieldRefPage, ModifyHdl_Impl ) ); + + m_xTypeLB->connect_row_activated(LINK(this, SwFieldRefPage, TreeViewInsertHdl)); + m_xTypeLB->connect_changed(LINK(this, SwFieldRefPage, TypeHdl)); + m_xSelectionLB->connect_changed(LINK(this, SwFieldRefPage, SubTypeListBoxHdl)); + m_xSelectionLB->connect_row_activated(LINK(this, SwFieldRefPage, TreeViewInsertHdl)); + m_xFormatLB->connect_row_activated(LINK(this, SwFieldRefPage, TreeViewInsertHdl)); + + // #i83479# + m_xSelectionToolTipLB->connect_changed( LINK(this, SwFieldRefPage, SubTypeTreeListBoxHdl) ); + m_xSelectionToolTipLB->connect_row_activated( LINK(this, SwFieldRefPage, TreeViewInsertHdl) ); + m_xFilterED->grab_focus(); +} + +SwFieldRefPage::~SwFieldRefPage() +{ +} + +IMPL_LINK_NOARG(SwFieldRefPage, ModifyHdl_Impl, weld::Entry&, void) +{ + UpdateSubType(comphelper::string::strip(m_xFilterED->get_text(), ' ')); +} + +// #i83479# +void SwFieldRefPage::SaveSelectedTextNode() +{ + mpSavedSelectedTextNode = nullptr; + mnSavedSelectedPos = 0; + if ( m_xSelectionToolTipLB->get_visible() ) + { + int nEntry = m_xSelectionToolTipLB->get_selected_index(); + if (nEntry != -1) + { + const sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + if ( nTypeId == REFFLDFLAG_HEADING ) + { + mnSavedSelectedPos = m_xSelectionToolTipLB->get_id(nEntry).toUInt32(); + if ( mnSavedSelectedPos < maOutlineNodes.size() ) + { + mpSavedSelectedTextNode = maOutlineNodes[mnSavedSelectedPos]; + } + } + else if ( nTypeId == REFFLDFLAG_NUMITEM ) + { + mnSavedSelectedPos = m_xSelectionToolTipLB->get_id(nEntry).toUInt32(); + if ( mnSavedSelectedPos < maNumItems.size() ) + { + mpSavedSelectedTextNode = maNumItems[mnSavedSelectedPos]->GetTextNode(); + } + } + } + } +} + +void SwFieldRefPage::Reset(const SfxItemSet* ) +{ + if (!IsFieldEdit()) + { + SavePos(*m_xTypeLB); + // #i83479# + SaveSelectedTextNode(); + } + SetSelectionSel(-1); + SetTypeSel(-1); + Init(); // general initialisation + + // initialise TypeListBox + m_xTypeLB->freeze(); + m_xTypeLB->clear(); + + // fill Type-Listbox + + // set/insert reference + const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup()); + + for (short i = rRg.nStart; i < rRg.nEnd; ++i) + { + const SwFieldTypesEnum nTypeId = SwFieldMgr::GetTypeId(i); + + if (!IsFieldEdit() || nTypeId != SwFieldTypesEnum::SetRef) + { + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i)); + } + } + + // #i83479# + // entries for headings and numbered items + m_xTypeLB->append(OUString::number(REFFLDFLAG_HEADING), sHeadingText); + m_xTypeLB->append(OUString::number(REFFLDFLAG_NUMITEM), sNumItemText); + + // fill up with the sequence types + SwWrtShell *pSh = GetWrtShell(); + if (!pSh) + pSh = ::GetActiveWrtShell(); + + if (!pSh) + return; + + const size_t nFieldTypeCnt = pSh->GetFieldTypeCount(SwFieldIds::SetExp); + + OSL_ENSURE( nFieldTypeCnt < o3tl::make_unsigned(REFFLDFLAG), "<SwFieldRefPage::Reset> - Item index will overlap flags!" ); + + for (size_t n = 0; n < nFieldTypeCnt; ++n) + { + SwSetExpFieldType* pType = static_cast<SwSetExpFieldType*>(pSh->GetFieldType(n, SwFieldIds::SetExp)); + + if ((nsSwGetSetExpType::GSE_SEQ & pType->GetType()) && pType->HasWriterListeners() && pSh->IsUsed(*pType)) + { + m_xTypeLB->append(OUString::number(REFFLDFLAG | n), pType->GetName()); + } + } + + // text marks - now always (because of globaldocuments) + m_xTypeLB->append(OUString::number(REFFLDFLAG_BOOKMARK), sBookmarkText); + + // footnotes: + if( pSh->HasFootnotes() ) + { + m_xTypeLB->append(OUString::number(REFFLDFLAG_FOOTNOTE), sFootnoteText); + } + + // endnotes: + if ( pSh->HasFootnotes(true) ) + { + m_xTypeLB->append(OUString::number(REFFLDFLAG_ENDNOTE), sEndnoteText); + } + + m_xTypeLB->thaw(); + + // select old Pos + if (!IsFieldEdit()) + RestorePos(*m_xTypeLB); + + nFieldDlgFormatSel = 0; + + sal_uInt16 nFormatBoxPosition = USHRT_MAX; + if( !IsRefresh() ) + { + sal_Int32 nIdx{ 0 }; + const OUString sUserData = GetUserData(); + if(!IsRefresh() && sUserData.getToken(0, ';', nIdx). + equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + const sal_uInt16 nVal = static_cast< sal_uInt16 >(sUserData.getToken(0, ';', nIdx).toInt32()); + if(nVal != USHRT_MAX) + { + for(sal_Int32 i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; ++i) + { + if (nVal == m_xTypeLB->get_id(i).toUInt32()) + { + m_xTypeLB->select(i); + break; + } + } + if (nIdx>=0 && nIdx<sUserData.getLength()) + { + nFormatBoxPosition = static_cast< sal_uInt16 >(sUserData.getToken(0, ';', nIdx).toInt32()); + } + } + } + } + TypeHdl(*m_xTypeLB); + if (nFormatBoxPosition < m_xFormatLB->n_children()) + { + m_xFormatLB->select(nFormatBoxPosition); + } + if (IsFieldEdit()) + { + m_xTypeLB->save_value(); + m_xSelectionLB->save_value(); + m_xFormatLB->save_value(); + m_xNameED->save_value(); + m_xValueED->save_value(); + m_xFilterED->set_text(OUString()); + } +} + +IMPL_LINK_NOARG(SwFieldRefPage, TypeHdl, weld::TreeView&, void) +{ + // save old ListBoxPos + const sal_Int32 nOld = GetTypeSel(); + + // current ListBoxPos + SetTypeSel(m_xTypeLB->get_selected_index()); + + if(GetTypeSel() == -1) + { + if (IsFieldEdit()) + { + // select positions + OUString sName; + sal_uInt16 nFlag = 0; + + switch( GetCurField()->GetSubType() ) + { + case REF_BOOKMARK: + { + // #i83479# + SwGetRefField* pRefField = dynamic_cast<SwGetRefField*>(GetCurField()); + if ( pRefField && + pRefField->IsRefToHeadingCrossRefBookmark() ) + { + sName = sHeadingText; + nFlag = REFFLDFLAG_HEADING; + } + else if ( pRefField && + pRefField->IsRefToNumItemCrossRefBookmark() ) + { + sName = sNumItemText; + nFlag = REFFLDFLAG_NUMITEM; + } + else + { + sName = sBookmarkText; + nFlag = REFFLDFLAG_BOOKMARK; + } + } + break; + + case REF_FOOTNOTE: + sName = sFootnoteText; + nFlag = REFFLDFLAG_FOOTNOTE; + break; + + case REF_ENDNOTE: + sName = sEndnoteText; + nFlag = REFFLDFLAG_ENDNOTE; + break; + + case REF_SETREFATTR: + sName = SwResId(STR_GETREFFLD); + nFlag = REF_SETREFATTR; + break; + + case REF_SEQUENCEFLD: + sName = static_cast<SwGetRefField*>(GetCurField())->GetSetRefName(); + nFlag = REFFLDFLAG; + break; + } + + if (m_xTypeLB->find_text(sName) == -1) // reference to deleted mark + { + m_xTypeLB->append(OUString::number(nFlag), sName); + } + + m_xTypeLB->select_text(sName); + SetTypeSel(m_xTypeLB->get_selected_index()); + } + else + { + SetTypeSel(0); + m_xTypeLB->select(0); + } + } + + if (nOld == GetTypeSel()) + return; + + sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + // fill selection-ListBox + UpdateSubType(comphelper::string::strip(m_xFilterED->get_text(), ' ')); + + bool bName = false; + nFieldDlgFormatSel = 0; + + if ( ( !IsFieldEdit() || m_xSelectionLB->n_children() ) && + nOld != -1 ) + { + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + m_xFilterED->set_text(OUString()); + } + + switch (nTypeId) + { + case static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef): + if (nOld != -1 && REFFLDFLAG & m_xTypeLB->get_id(nOld).toUInt32()) + // the old one stays + nFieldDlgFormatSel = m_xFormatLB->get_selected_index(); + bName = true; + break; + + case static_cast<sal_uInt16>(SwFieldTypesEnum::SetRef): + bName = true; + break; + + case REFFLDFLAG_BOOKMARK: + bName = true; + [[fallthrough]]; + default: + if( REFFLDFLAG & nTypeId ) + { + const sal_uInt16 nOldId = nOld != -1 ? m_xTypeLB->get_id(nOld).toUInt32() : 0; + if( nOldId & REFFLDFLAG || nOldId == static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef) ) + // then the old one stays + nFieldDlgFormatSel = m_xFormatLB->get_selected_index(); + } + break; + } + + m_xNameED->set_sensitive(bName); + m_xNameFT->set_sensitive(bName); + + // fill Format-Listbox + sal_Int32 nSize = FillFormatLB(nTypeId); + bool bFormat = nSize != 0; + m_xFormat->set_sensitive(bFormat); + + SubTypeHdl(); + ModifyHdl(*m_xNameED); + ModifyHdl(*m_xFilterED); +} + +IMPL_LINK_NOARG(SwFieldRefPage, SubTypeTreeListBoxHdl, weld::TreeView&, void) +{ + SubTypeHdl(); +} + +IMPL_LINK_NOARG(SwFieldRefPage, SubTypeListBoxHdl, weld::TreeView&, void) +{ + SubTypeHdl(); +} + +void SwFieldRefPage::SubTypeHdl() +{ + sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + switch(nTypeId) + { + case static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef): + if (!IsFieldEdit() || m_xSelectionLB->get_selected_index() != -1) + { + m_xNameED->set_text(m_xSelectionLB->get_selected_text()); + ModifyHdl(*m_xNameED); + } + break; + + case static_cast<sal_uInt16>(SwFieldTypesEnum::SetRef): + { + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + m_xValueED->set_text(pSh->GetSelText()); + } + + } + break; + // #i83479# + case REFFLDFLAG_HEADING: + case REFFLDFLAG_NUMITEM: + { + int nEntry = m_xSelectionToolTipLB->get_selected_index(); + if (nEntry != -1) + m_xNameED->set_text(m_xSelectionToolTipLB->get_text(nEntry)); + } + break; + + default: + if (!IsFieldEdit() || m_xSelectionLB->get_selected_index() != -1) + m_xNameED->set_text(m_xSelectionLB->get_selected_text()); + break; + } +} + +// renew types in SelectionLB after filtering +void SwFieldRefPage::UpdateSubType(const OUString& filterString) +{ + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + SwGetRefField* pRefField = static_cast<SwGetRefField*>(GetCurField()); + const sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + OUString sOldSel; + // #i83479# + if ( m_xSelectionLB->get_visible() ) + { + const sal_Int32 nSelectionSel = m_xSelectionLB->get_selected_index(); + if (nSelectionSel != -1) + sOldSel = m_xSelectionLB->get_text(nSelectionSel); + } + if (IsFieldEdit() && sOldSel.isEmpty()) + sOldSel = OUString::number( pRefField->GetSeqNo() + 1 ); + + m_xSelectionLB->freeze(); + m_xSelectionLB->clear(); + + if (REFFLDFLAG & nTypeId) + { + if (nTypeId == REFFLDFLAG_FOOTNOTE || nTypeId == REFFLDFLAG_ENDNOTE) + { + m_xSelectionLB->thaw(); + m_xSelectionLB->make_unsorted(); + m_xSelectionLB->freeze(); + } + // #i83479# + else if (nTypeId != REFFLDFLAG_HEADING && nTypeId != REFFLDFLAG_NUMITEM) + { + m_xSelectionLB->thaw(); + m_xSelectionLB->make_sorted(); + m_xSelectionLB->freeze(); + } + } + + // #i83479# + m_xSelectionToolTipLB->freeze(); + m_xSelectionToolTipLB->clear(); + OUString m_sSelectionToolTipLBId; + bool bShowSelectionToolTipLB( false ); + + if( REFFLDFLAG & nTypeId ) + { + if (nTypeId == REFFLDFLAG_BOOKMARK) // text marks! + { + // get all text marks + IDocumentMarkAccess* const pMarkAccess = pSh->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin(); + ppMark != pMarkAccess->getBookmarksEnd(); + ++ppMark) + { + const ::sw::mark::IMark* pBkmk = *ppMark; + if(IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(*pBkmk)) + { + bool isSubstring = MatchSubstring(pBkmk->GetName(), filterString); + if(isSubstring) + { + m_xSelectionLB->append_text( pBkmk->GetName() ); + } + } + } + if (IsFieldEdit()) + sOldSel = pRefField->GetSetRefName(); + } + else if (nTypeId == REFFLDFLAG_FOOTNOTE) + { + SwSeqFieldList aArr; + const size_t nCnt = pSh->GetSeqFootnoteList( aArr ); + + for( size_t n = 0; n < nCnt; ++n ) + { + bool isSubstring = MatchSubstring(aArr[ n ].sDlgEntry, filterString); + if(isSubstring) + { + m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); + } + if (IsFieldEdit() && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) + sOldSel = aArr[n].sDlgEntry; + } + } + else if (nTypeId == REFFLDFLAG_ENDNOTE) + { + SwSeqFieldList aArr; + const size_t nCnt = pSh->GetSeqFootnoteList( aArr, true ); + + for( size_t n = 0; n < nCnt; ++n ) + { + bool isSubstring = MatchSubstring(aArr[ n ].sDlgEntry, filterString); + if(isSubstring) + { + m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); + } + if (IsFieldEdit() && pRefField->GetSeqNo() == aArr[ n ].nSeqNo) + sOldSel = aArr[n].sDlgEntry; + } + } + // #i83479# + else if ( nTypeId == REFFLDFLAG_HEADING ) + { + bShowSelectionToolTipLB = true; + + const IDocumentOutlineNodes* pIDoc( pSh->getIDocumentOutlineNodesAccess() ); + pIDoc->getOutlineNodes( maOutlineNodes ); + bool bCertainTextNodeSelected( false ); + for ( size_t nOutlIdx = 0; nOutlIdx < maOutlineNodes.size(); ++nOutlIdx ) + { + if (!pIDoc->isOutlineInLayout(nOutlIdx, *pSh->GetLayout())) + { + continue; // skip it + } + bool isSubstring = MatchSubstring(pIDoc->getOutlineText(nOutlIdx, pSh->GetLayout(), true, true, false), filterString); + if(isSubstring) + { + OUString sId(OUString::number(nOutlIdx)); + m_xSelectionToolTipLB->append(sId, + pIDoc->getOutlineText(nOutlIdx, pSh->GetLayout(), true, true, false)); + if ( ( IsFieldEdit() && + pRefField->GetReferencedTextNode() == maOutlineNodes[nOutlIdx] ) || + mpSavedSelectedTextNode == maOutlineNodes[nOutlIdx] ) + { + m_sSelectionToolTipLBId = sId; + sOldSel.clear(); + bCertainTextNodeSelected = true; + } + else if ( !bCertainTextNodeSelected && mnSavedSelectedPos == nOutlIdx ) + { + m_sSelectionToolTipLBId = sId; + sOldSel.clear(); + } + } + } + } + else if ( nTypeId == REFFLDFLAG_NUMITEM ) + { + bShowSelectionToolTipLB = true; + + const IDocumentListItems* pIDoc( pSh->getIDocumentListItemsAccess() ); + pIDoc->getNumItems( maNumItems ); + bool bCertainTextNodeSelected( false ); + for ( size_t nNumItemIdx = 0; nNumItemIdx < maNumItems.size(); ++nNumItemIdx ) + { + if (!pIDoc->isNumberedInLayout(*maNumItems[nNumItemIdx], *pSh->GetLayout())) + { + continue; // skip it + } + bool isSubstring = MatchSubstring(pIDoc->getListItemText(*maNumItems[nNumItemIdx], *pSh->GetLayout()), filterString); + if(isSubstring) + { + OUString sId(OUString::number(nNumItemIdx)); + m_xSelectionToolTipLB->append(sId, + pIDoc->getListItemText(*maNumItems[nNumItemIdx], *pSh->GetLayout())); + if ( ( IsFieldEdit() && + pRefField->GetReferencedTextNode() == maNumItems[nNumItemIdx]->GetTextNode() ) || + mpSavedSelectedTextNode == maNumItems[nNumItemIdx]->GetTextNode() ) + { + m_sSelectionToolTipLBId = sId; + sOldSel.clear(); + bCertainTextNodeSelected = true; + } + else if ( !bCertainTextNodeSelected && mnSavedSelectedPos == nNumItemIdx ) + { + m_sSelectionToolTipLBId = sId; + sOldSel.clear(); + } + } + } + } + else + { + // get the fields to Seq-FieldType: + + SwSetExpFieldType* pType = static_cast<SwSetExpFieldType*>(pSh->GetFieldType( + nTypeId & ~REFFLDFLAG, SwFieldIds::SetExp )); + if( pType ) + { + SwSeqFieldList aArr; + // old selection should be kept in non-edit mode + if(IsFieldEdit()) + sOldSel.clear(); + + const size_t nCnt = pType->GetSeqFieldList(aArr, pSh->GetLayout()); + for( size_t n = 0; n < nCnt; ++n ) + { + bool isSubstring = MatchSubstring(aArr[ n ].sDlgEntry, filterString); + if(isSubstring) + { + m_xSelectionLB->append_text( aArr[ n ].sDlgEntry ); + } + if (IsFieldEdit() && sOldSel.isEmpty() && + aArr[ n ].nSeqNo == pRefField->GetSeqNo()) + sOldSel = aArr[ n ].sDlgEntry; + } + + if (IsFieldEdit() && sOldSel.isEmpty()) + sOldSel = OUString::number( pRefField->GetSeqNo() + 1); + } + } + } + else + { + std::vector<OUString> aLst; + GetFieldMgr().GetSubTypes(static_cast<SwFieldTypesEnum>(nTypeId), aLst); + for(const OUString & i : aLst) + { + bool isSubstring = MatchSubstring( i , filterString ); + if(isSubstring) + { + m_xSelectionLB->append_text(i); + } + } + + if (IsFieldEdit()) + sOldSel = pRefField->GetSetRefName(); + } + + // #i83479# + m_xSelectionLB->thaw(); + m_xSelectionToolTipLB->thaw(); + if (!m_sSelectionToolTipLBId.isEmpty()) + m_xSelectionToolTipLB->select_id(m_sSelectionToolTipLBId); + m_xSelectionToolTipLB->set_visible( bShowSelectionToolTipLB ); + m_xSelectionLB->set_visible( !bShowSelectionToolTipLB ); + if ( bShowSelectionToolTipLB ) + { + bool bEnable = m_xSelectionToolTipLB->n_children() != 0; + m_xSelection->set_sensitive( bEnable ); + + int nEntry = m_xSelectionToolTipLB->get_selected_index(); + if (nEntry != -1) + m_xSelectionToolTipLB->scroll_to_row(nEntry); + + if (IsFieldEdit() && nEntry == -1) + { + m_xNameED->set_text(sOldSel); + } + } + else + { + // enable or disable + bool bEnable = m_xSelectionLB->n_children() != 0; + m_xSelection->set_sensitive( bEnable ); + + if ( bEnable ) + { + m_xSelectionLB->select_text(sOldSel); + if (m_xSelectionLB->get_selected_index() == -1 && !IsFieldEdit()) + m_xSelectionLB->select(0); + } + + if (IsFieldEdit() && m_xSelectionLB->get_selected_index() == -1) // in case the reference was already deleted... + m_xNameED->set_text(sOldSel); + } +} + +bool SwFieldRefPage::MatchSubstring( const OUString& rListString, const OUString& rSubstr ) +{ + if(rSubstr.isEmpty()) + return true; + OUString aListString = GetAppCharClass().lowercase(rListString); + OUString aSubstr = GetAppCharClass().lowercase(rSubstr); + return aListString.indexOf(aSubstr) >= 0; +} + +namespace { + +enum FMT_REF_IDX +{ + FMT_REF_PAGE_IDX = 0, + FMT_REF_CHAPTER_IDX = 1, + FMT_REF_TEXT_IDX = 2, + FMT_REF_UPDOWN_IDX = 3, + FMT_REF_PAGE_PGDSC_IDX = 4, + FMT_REF_ONLYNUMBER_IDX = 5, + FMT_REF_ONLYCAPTION_IDX = 6, + FMT_REF_ONLYSEQNO_IDX = 7, + FMT_REF_NUMBER_IDX = 8, + FMT_REF_NUMBER_NO_CONTEXT_IDX = 9, + FMT_REF_NUMBER_FULL_CONTEXT_IDX = 10 +}; + +} + +static const char* FMT_REF_ARY[] = +{ + FMT_REF_PAGE, + FMT_REF_CHAPTER, + FMT_REF_TEXT, + FMT_REF_UPDOWN, + FMT_REF_PAGE_PGDSC, + FMT_REF_ONLYNUMBER, + FMT_REF_ONLYCAPTION, + FMT_REF_ONLYSEQNO, + FMT_REF_NUMBER, + FMT_REF_NUMBER_NO_CONTEXT, + FMT_REF_NUMBER_FULL_CONTEXT +}; + +sal_Int32 SwFieldRefPage::FillFormatLB(sal_uInt16 nTypeId) +{ + OUString sOldSel; + + sal_Int32 nFormatSel = m_xFormatLB->get_selected_index(); + if (nFormatSel != -1) + sOldSel = m_xFormatLB->get_text(nFormatSel); + + // fill Format-Listbox + m_xFormatLB->clear(); + + // reference has less that the annotation + sal_uInt16 nSize( 0 ); + bool bAddCrossRefFormats( false ); + switch (nTypeId) + { + // #i83479# + case REFFLDFLAG_HEADING: + case REFFLDFLAG_NUMITEM: + bAddCrossRefFormats = true; + [[fallthrough]]; + + case static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef): + case REFFLDFLAG_BOOKMARK: + case REFFLDFLAG_FOOTNOTE: + case REFFLDFLAG_ENDNOTE: + nSize = FMT_REF_PAGE_PGDSC_IDX + 1; + break; + + default: + // #i83479# + + if ( REFFLDFLAG & nTypeId ) + { + nSize = FMT_REF_ONLYSEQNO_IDX + 1; + } + else + { + nSize = GetFieldMgr().GetFormatCount( static_cast<SwFieldTypesEnum>(nTypeId), IsFieldDlgHtmlMode() ); + } + break; + } + + if (REFFLDFLAG & nTypeId) + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + + SwFieldTypesEnum nFieldType = static_cast<SwFieldTypesEnum>(nTypeId); + for (sal_uInt16 i = 0; i < nSize; i++) + { + OUString sId(OUString::number(GetFieldMgr().GetFormatId( nFieldType, i ))); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(nFieldType, i)); + } + // #i83479# + + sal_uInt16 nExtraSize( 0 ); + if ( bAddCrossRefFormats ) + { + sal_uInt16 nFormat = FMT_REF_NUMBER_IDX; + OUString sId(OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat))); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr( nFieldType, nFormat )); + nFormat = FMT_REF_NUMBER_NO_CONTEXT_IDX; + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr( nFieldType, nFormat )); + nFormat = FMT_REF_NUMBER_FULL_CONTEXT_IDX; + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr( nFieldType, nFormat )); + nExtraSize = 3; + } + + // extra list items optionally, depending from reference-language + SvtSysLocaleOptions aSysLocaleOptions; + static const LanguageTag& rLang = aSysLocaleOptions.GetRealLanguageTag(); + + if (rLang.getLanguage() == "hu") + { + for (sal_uInt16 i = 0; i < nSize; i++) + { + OUString sId(OUString::number(GetFieldMgr().GetFormatId( nFieldType, i + SAL_N_ELEMENTS(FMT_REF_ARY)))); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_LOWERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, i )); + } + nExtraSize += nSize; + + if ( bAddCrossRefFormats ) + { + sal_uInt16 nFormat = FMT_REF_NUMBER_IDX + SAL_N_ELEMENTS(FMT_REF_ARY); + OUString sId(OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat))); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_LOWERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nFormat = FMT_REF_NUMBER_NO_CONTEXT_IDX + SAL_N_ELEMENTS(FMT_REF_ARY); + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_LOWERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nFormat = FMT_REF_NUMBER_FULL_CONTEXT_IDX + SAL_N_ELEMENTS(FMT_REF_ARY); + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_LOWERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nExtraSize += 3; + } + // uppercase article + for (sal_uInt16 i = 0; i < nSize; i++) + { + OUString sId(OUString::number(GetFieldMgr().GetFormatId( nFieldType, i + 2 * SAL_N_ELEMENTS(FMT_REF_ARY)))); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_UPPERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, i )); + } + nExtraSize += nSize; + if ( bAddCrossRefFormats ) + { + sal_uInt16 nFormat = FMT_REF_NUMBER_IDX + 2 * SAL_N_ELEMENTS(FMT_REF_ARY); + OUString sId(OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat))); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_UPPERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nFormat = FMT_REF_NUMBER_NO_CONTEXT_IDX + 2 * SAL_N_ELEMENTS(FMT_REF_ARY); + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_UPPERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nFormat = FMT_REF_NUMBER_FULL_CONTEXT_IDX + 2 * SAL_N_ELEMENTS(FMT_REF_ARY); + sId = OUString::number(GetFieldMgr().GetFormatId(nFieldType, nFormat)); + m_xFormatLB->append(sId, SwResId(FMT_REF_WITH_UPPERCASE_HU_ARTICLE) + GetFieldMgr().GetFormatStr( nFieldType, nFormat % SAL_N_ELEMENTS(FMT_REF_ARY))); + nExtraSize += 3; + } + } + + nSize += nExtraSize; + + // select a certain entry + if (nSize) + { + if (!IsFieldEdit()) + m_xFormatLB->select_text(sOldSel); + else + m_xFormatLB->select_text(SwResId(FMT_REF_ARY[GetCurField()->GetFormat() % SAL_N_ELEMENTS(FMT_REF_ARY)])); + + if (m_xFormatLB->get_selected_index() == -1) + { + if (nFieldDlgFormatSel < m_xFormatLB->n_children()) + m_xFormatLB->select(nFieldDlgFormatSel); + else + m_xFormatLB->select(0); + } + } + + return nSize; +} + +// Modify +IMPL_LINK_NOARG(SwFieldRefPage, ModifyHdl, weld::Entry&, void) +{ + OUString aName(m_xNameED->get_text()); + const bool bEmptyName = aName.isEmpty(); + + bool bEnable = true; + sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + if ((nTypeId == static_cast<sal_uInt16>(SwFieldTypesEnum::SetRef) && !GetFieldMgr().CanInsertRefMark(aName)) || + (bEmptyName && (nTypeId == static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef) || nTypeId == static_cast<sal_uInt16>(SwFieldTypesEnum::SetRef) || + nTypeId == REFFLDFLAG_BOOKMARK))) + bEnable = false; + + EnableInsert(bEnable); + + m_xSelectionLB->select_text(aName); +} + +bool SwFieldRefPage::FillItemSet(SfxItemSet* ) +{ + bool bModified = false; + sal_uInt16 nTypeId = m_xTypeLB->get_id(GetTypeSel()).toUInt32(); + + sal_uInt16 nSubType = 0; + const sal_Int32 nEntryPos = m_xFormatLB->get_selected_index(); + const sal_uLong nFormat = (nEntryPos == -1) + ? 0 : m_xFormatLB->get_id(nEntryPos).toUInt32(); + + OUString aVal(m_xValueED->get_text()); + OUString aName(m_xNameED->get_text()); + + switch(nTypeId) + { + case static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef): + nSubType = REF_SETREFATTR; + break; + + case static_cast<sal_uInt16>(SwFieldTypesEnum::SetRef): + { + SwFieldType* pType = GetFieldMgr().GetFieldType(SwFieldIds::SetExp, aName); + + if(!pType) // Only insert when the name doesn't exist yet + { + m_xSelectionLB->append_text(aName); + m_xSelection->set_sensitive(true); + } + break; + } + } + + SwGetRefField* pRefField = static_cast<SwGetRefField*>(GetCurField()); + + if (REFFLDFLAG & nTypeId) + { + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + { + pSh = ::GetActiveWrtShell(); + } + if (nTypeId == REFFLDFLAG_BOOKMARK) // text marks! + { + aName = m_xNameED->get_text(); + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_BOOKMARK; + } + else if (REFFLDFLAG_FOOTNOTE == nTypeId) // footnotes + { + SwSeqFieldList aArr; + SeqFieldLstElem aElem( m_xSelectionLB->get_selected_text(), 0 ); + + size_t nPos = 0; + + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_FOOTNOTE; + aName.clear(); + + if (pSh->GetSeqFootnoteList(aArr) && aArr.SeekEntry(aElem, &nPos)) + { + aVal = OUString::number( aArr[nPos].nSeqNo ); + + if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + bModified = true; // can happen with fields of which the references were deleted + } + else if (IsFieldEdit()) + aVal = OUString::number( pRefField->GetSeqNo() ); + } + else if (REFFLDFLAG_ENDNOTE == nTypeId) // endnotes + { + SwSeqFieldList aArr; + SeqFieldLstElem aElem( m_xSelectionLB->get_selected_text(), 0 ); + + size_t nPos = 0; + + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_ENDNOTE; + aName.clear(); + + if (pSh->GetSeqFootnoteList(aArr, true) && aArr.SeekEntry(aElem, &nPos)) + { + aVal = OUString::number( aArr[nPos].nSeqNo ); + + if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + bModified = true; // can happen with fields of which the reference was deleted + } + else if (IsFieldEdit()) + aVal = OUString::number( pRefField->GetSeqNo() ); + } + // #i83479# + else if ( nTypeId == REFFLDFLAG_HEADING ) + { + int nEntry = m_xSelectionToolTipLB->get_selected_index(); + OSL_ENSURE( nEntry != -1, + "<SwFieldRefPage::FillItemSet(..)> - no entry selected in selection tool tip listbox!" ); + if (nEntry != -1) + { + const size_t nOutlIdx(m_xSelectionToolTipLB->get_id(nEntry).toUInt32()); + pSh->getIDocumentOutlineNodesAccess()->getOutlineNodes( maOutlineNodes ); + if ( nOutlIdx < maOutlineNodes.size() ) + { + ::sw::mark::IMark const * const pMark = pSh->getIDocumentMarkAccess()->getMarkForTextNode( + *(maOutlineNodes[nOutlIdx]), + IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK); + aName = pMark->GetName(); + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_BOOKMARK; + } + } + } + else if ( nTypeId == REFFLDFLAG_NUMITEM ) + { + int nEntry = m_xSelectionToolTipLB->get_selected_index(); + OSL_ENSURE( nEntry != -1, + "<SwFieldRefPage::FillItemSet(..)> - no entry selected in selection tool tip listbox!" ); + if (nEntry != -1) + { + const size_t nNumItemIdx(m_xSelectionToolTipLB->get_id(nEntry).toUInt32()); + pSh->getIDocumentListItemsAccess()->getNumItems(maNumItems); + if ( nNumItemIdx < maNumItems.size() ) + { + ::sw::mark::IMark const * const pMark = pSh->getIDocumentMarkAccess()->getMarkForTextNode( + *(maNumItems[nNumItemIdx]->GetTextNode()), + IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK); + aName = pMark->GetName(); + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_BOOKMARK; + } + } + } + else // SequenceFields + { + // get fields for Seq-FieldType: + SwSetExpFieldType* pType = static_cast<SwSetExpFieldType*>(pSh->GetFieldType( + nTypeId & ~REFFLDFLAG, SwFieldIds::SetExp )); + if( pType ) + { + SwSeqFieldList aArr; + SeqFieldLstElem aElem( m_xSelectionLB->get_selected_text(), 0 ); + + size_t nPos = 0; + + nTypeId = static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef); + nSubType = REF_SEQUENCEFLD; + aName = pType->GetName(); + + if (pType->GetSeqFieldList(aArr, pSh->GetLayout()) + && aArr.SeekEntry(aElem, &nPos)) + { + aVal = OUString::number( aArr[nPos].nSeqNo ); + + if (IsFieldEdit() && aArr[nPos].nSeqNo == pRefField->GetSeqNo()) + bModified = true; // can happen with fields of which the reference was deleted + } + else if (IsFieldEdit()) + aVal = OUString::number( pRefField->GetSeqNo() ); + } + } + } + + if (IsFieldEdit() && nTypeId == static_cast<sal_uInt16>(SwFieldTypesEnum::GetRef)) + { + aVal = OUString::number(nSubType) + "|" + aVal; + } + + if (!IsFieldEdit() || bModified || + m_xNameED->get_value_changed_from_saved() || + m_xValueED->get_value_changed_from_saved() || + m_xTypeLB->get_value_changed_from_saved() || + m_xSelectionLB->get_value_changed_from_saved() || + m_xFormatLB->get_value_changed_from_saved()) + { + InsertField( static_cast<SwFieldTypesEnum>(nTypeId), nSubType, aName, aVal, nFormat ); + } + + ModifyHdl(*m_xNameED); // enable/disable insert if applicable + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldRefPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet) +{ + return std::make_unique<SwFieldRefPage>(pPage, pController, pAttrSet); +} + +sal_uInt16 SwFieldRefPage::GetGroup() +{ + return GRP_REF; +} + +void SwFieldRefPage::FillUserData() +{ + const sal_Int32 nEntryPos = m_xTypeLB->get_selected_index(); + const sal_uInt16 nTypeSel = ( -1 == nEntryPos ) + ? USHRT_MAX + : m_xTypeLB->get_id(nEntryPos).toUInt32(); + const sal_Int32 nFormatEntryPos = m_xFormatLB->get_selected_index(); + const sal_uInt32 nFormatSel = -1 == nFormatEntryPos ? USHRT_MAX : nFormatEntryPos; + SetUserData( USER_DATA_VERSION ";" + + OUString::number( nTypeSel ) + ";" + + OUString::number( nFormatSel )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldref.hxx b/sw/source/ui/fldui/fldref.hxx new file mode 100644 index 000000000..8dc9a311b --- /dev/null +++ b/sw/source/ui/fldui/fldref.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_UI_FLDUI_FLDREF_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDREF_HXX + +#include <sfx2/tabdlg.hxx> + +#include "fldpage.hxx" +#include <IDocumentOutlineNodes.hxx> +#include <IDocumentListItems.hxx> +class SwTextNode; + +class SwFieldRefPage : public SwFieldPage +{ + OUString sBookmarkText; + OUString sFootnoteText; + OUString sEndnoteText; + // #i83479# + OUString sHeadingText; + OUString sNumItemText; + + IDocumentOutlineNodes::tSortedOutlineNodeList maOutlineNodes; + IDocumentListItems::tSortedNodeNumList maNumItems; + + // selected text node in the listbox for headings and numbered items + // in order to restore selection after update of selection listbox + const SwTextNode* mpSavedSelectedTextNode; + // fallback, if previously selected text node doesn't exist anymore + size_t mnSavedSelectedPos; + + std::unique_ptr<weld::TreeView> m_xTypeLB; + std::unique_ptr<weld::Widget> m_xSelection; + std::unique_ptr<weld::TreeView> m_xSelectionLB; + // #i83479# + std::unique_ptr<weld::TreeView> m_xSelectionToolTipLB; + std::unique_ptr<weld::Widget> m_xFormat; + std::unique_ptr<weld::TreeView> m_xFormatLB; + std::unique_ptr<weld::Label> m_xNameFT; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Entry> m_xValueED; + std::unique_ptr<weld::Entry> m_xFilterED; + + DECL_LINK(TypeHdl, weld::TreeView&, void); + DECL_LINK(SubTypeListBoxHdl, weld::TreeView&, void); + DECL_LINK(SubTypeTreeListBoxHdl, weld::TreeView&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ModifyHdl_Impl, weld::Entry&, void); + + void SubTypeHdl(); + + void UpdateSubType(const OUString& filterString); + + static bool MatchSubstring( const OUString& list_string, const OUString& substr ); + + sal_Int32 FillFormatLB(sal_uInt16 nTypeId); + + // #i83479# + void SaveSelectedTextNode(); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldRefPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pSet); + virtual ~SwFieldRefPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldtdlg.cxx b/sw/source/ui/fldui/fldtdlg.cxx new file mode 100644 index 000000000..4a12a7538 --- /dev/null +++ b/sw/source/ui/fldui/fldtdlg.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmdid.h> +#include <unotools/confignode.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/viewfrm.hxx> +#include <viewopt.hxx> +#include <chldwrap.hxx> +#include <docsh.hxx> +#include "flddb.hxx" +#include "flddinf.hxx" +#include "fldvar.hxx" +#include "flddok.hxx" +#include "fldfunc.hxx" +#include "fldref.hxx" +#include <wrtsh.hxx> +#include <view.hxx> +#include <fldtdlg.hxx> +#include <swmodule.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> + +// carrier of the dialog +SwFieldDlg::SwFieldDlg(SfxBindings* pB, SwChildWinWrapper* pCW, weld::Window *pParent) + : SfxTabDialogController(pParent, "modules/swriter/ui/fielddialog.ui", "FieldDialog") + , m_pChildWin(pCW) + , m_pBindings(pB) + , m_bDataBaseMode(false) + , m_bClosing(false) +{ + m_bHtmlMode = (::GetHtmlMode(static_cast<SwDocShell*>(SfxObjectShell::Current())) & HTMLMODE_ON) != 0; + + GetCancelButton().connect_clicked(LINK(this, SwFieldDlg, CancelHdl)); + GetOKButton().connect_clicked(LINK(this, SwFieldDlg, OKHdl)); + + AddTabPage("document", SwFieldDokPage::Create, nullptr); + AddTabPage("variables", SwFieldVarPage::Create, nullptr); + AddTabPage("docinfo", SwFieldDokInfPage::Create, nullptr); + + if (!m_bHtmlMode) + { + AddTabPage("ref", SwFieldRefPage::Create, nullptr); + AddTabPage("functions", SwFieldFuncPage::Create, nullptr); + + utl::OConfigurationTreeRoot aCfgRoot + = utl::OConfigurationTreeRoot::createWithComponentContext( + ::comphelper::getProcessComponentContext(), + "/org.openoffice.Office.DataAccess/Policies/Features/Writer", + -1, + utl::OConfigurationTreeRoot::CM_READONLY); + +#if HAVE_FEATURE_DBCONNECTIVITY + bool bDatabaseFields = true; + aCfgRoot.getNodeValue( + OUString("DatabaseFields")) >>= bDatabaseFields; + + if (bDatabaseFields) + AddTabPage("database", SwFieldDBPage::Create, nullptr); + else +#endif + RemoveTabPage("database"); + } + else + { + RemoveTabPage("ref"); + RemoveTabPage("functions"); + RemoveTabPage("database"); + } +} + +SwFieldDlg::~SwFieldDlg() +{ +} + +void SwFieldDlg::EndDialog() +{ + m_bClosing = true; + SfxTabDialogController::EndDialog(); + m_bClosing = false; +} + +void SwFieldDlg::Close() +{ + if (m_bClosing) + return; + m_pBindings->GetDispatcher()-> + Execute(m_bDataBaseMode ? FN_INSERT_FIELD_DATA_ONLY : FN_INSERT_FIELD, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD); +} + +void SwFieldDlg::Initialize(SfxChildWinInfo const *pInfo) +{ + OString aWinState = pInfo->aWinState; + if (aWinState.isEmpty()) + return; + m_xDialog->set_window_state(aWinState); +} + +SfxItemSet* SwFieldDlg::CreateInputItemSet(const OString& rID) +{ + SwDocShell *const pDocSh(static_cast<SwDocShell*>(SfxObjectShell::Current())); + if (rID == "docinfo" && pDocSh) // might not have a shell if the dialog is restored on startup + { + SfxItemSet* pISet = new SfxItemSet( pDocSh->GetPool(), svl::Items<SID_DOCINFO, SID_DOCINFO>{} ); + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocSh->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + uno::Reference< beans::XPropertySet > xUDProps( + xDocProps->getUserDefinedProperties(), + uno::UNO_QUERY_THROW); + pISet->Put( SfxUnoAnyItem( SID_DOCINFO, uno::makeAny(xUDProps) ) ); + return pISet; + } + else + return nullptr; +} + +// kick off inserting of new fields +IMPL_LINK_NOARG(SwFieldDlg, OKHdl, weld::Button&, void) +{ + if (GetOKButton().get_sensitive()) + { + SfxTabPage* pPage = GetTabPage(GetCurPageId()); + assert(pPage); + pPage->FillItemSet(nullptr); + + GetOKButton().grab_focus(); // because of InputField-Dlg + } +} + +IMPL_LINK_NOARG(SwFieldDlg, CancelHdl, weld::Button&, void) +{ + Close(); +} + +// newly initialise dialog after Doc-Switch +void SwFieldDlg::ReInitDlg() +{ + SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current()); + bool bNewMode = (::GetHtmlMode(pDocSh) & HTMLMODE_ON) != 0; + + if (bNewMode != m_bHtmlMode) + { + SfxViewFrame::Current()->GetDispatcher()-> + Execute(FN_INSERT_FIELD, SfxCallMode::ASYNCHRON|SfxCallMode::RECORD); + Close(); + } + + SwView* pActiveView = ::GetActiveView(); + if(!pActiveView) + return; + const SwWrtShell& rSh = pActiveView->GetWrtShell(); + GetOKButton().set_sensitive(!rSh.IsReadOnlyAvailable() || + !rSh.HasReadonlySel()); + + ReInitTabPage("document"); + ReInitTabPage("variables"); + ReInitTabPage("docinfo"); + + if (!m_bHtmlMode) + { + ReInitTabPage("ref"); + ReInitTabPage("functions"); + ReInitTabPage("database"); + } + + m_pChildWin->SetOldDocShell(pDocSh); +} + +// newly initialise TabPage after Doc-Switch +void SwFieldDlg::ReInitTabPage(const OString& rPageId, bool bOnlyActivate) +{ + SwFieldPage* pPage = static_cast<SwFieldPage*>(GetTabPage(rPageId)); + if (pPage) + pPage->EditNewField( bOnlyActivate ); // newly initialise TabPage +} + +// newly initialise after activation of a few TabPages +void SwFieldDlg::Activate() +{ + SwView* pView = ::GetActiveView(); + if( pView ) + { + bool bHtmlMode = (::GetHtmlMode(static_cast<SwDocShell*>(SfxObjectShell::Current())) & HTMLMODE_ON) != 0; + const SwWrtShell& rSh = pView->GetWrtShell(); + GetOKButton().set_sensitive(!rSh.IsReadOnlyAvailable() || + !rSh.HasReadonlySel()); + + ReInitTabPage("variables", true); + + if( !bHtmlMode ) + { + ReInitTabPage("ref", true); + ReInitTabPage("functions", true); + } + } +} + +void SwFieldDlg::EnableInsert(bool bEnable) +{ + if( bEnable ) + { + SwView* pView = ::GetActiveView(); + OSL_ENSURE(pView, "no view found"); + if( !pView || + (pView->GetWrtShell().IsReadOnlyAvailable() && + pView->GetWrtShell().HasReadonlySel()) ) + bEnable = false; + } + GetOKButton().set_sensitive(bEnable); +} + +void SwFieldDlg::InsertHdl() +{ + GetOKButton().clicked(); +} + +void SwFieldDlg::ActivateDatabasePage() +{ +#if HAVE_FEATURE_DBCONNECTIVITY + m_bDataBaseMode = true; + ShowPage("database"); + SfxTabPage* pDBPage = GetTabPage("database"); + if( pDBPage ) + { + static_cast<SwFieldDBPage*>(pDBPage)->ActivateMailMergeAddress(); + } + //remove all other pages + RemoveTabPage("document"); + RemoveTabPage("variables"); + RemoveTabPage("docinfo"); + RemoveTabPage("ref"); + RemoveTabPage("functions"); +#endif +} + +void SwFieldDlg::ShowReferencePage() +{ + ShowPage("ref"); +} + +void SwFieldDlg::PageCreated(const OString& rId, SfxTabPage& rPage) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + if (rId == "database") + { + SfxDispatcher* pDispatch = m_pBindings->GetDispatcher(); + SfxViewFrame* pViewFrame = pDispatch ? pDispatch->GetFrame() : nullptr; + if(pViewFrame) + { + SfxViewShell* pViewShell = SfxViewShell::GetFirst( true, checkSfxViewShell<SwView> ); + while(pViewShell && pViewShell->GetViewFrame() != pViewFrame) + { + pViewShell = SfxViewShell::GetNext( *pViewShell, true, checkSfxViewShell<SwView> ); + } + if(pViewShell) + static_cast<SwFieldDBPage&>(rPage).SetWrtShell(static_cast<SwView*>(pViewShell)->GetWrtShell()); + } + } +#else + (void) rId; + (void) rPage; +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldvar.cxx b/sw/source/ui/fldui/fldvar.cxx new file mode 100644 index 000000000..7697e4413 --- /dev/null +++ b/sw/source/ui/fldui/fldvar.cxx @@ -0,0 +1,1228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/linkmgr.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <usrfld.hxx> +#include <docufld.hxx> +#include <expfld.hxx> +#include <ddefld.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <swmodule.hxx> +#include "fldvar.hxx" +#include "flddinf.hxx" +#include <calc.hxx> +#include <svl/zformat.hxx> +#include <strings.hrc> + +#define USER_DATA_VERSION_1 "1" +#define USER_DATA_VERSION USER_DATA_VERSION_1 + +SwFieldVarPage::SwFieldVarPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet ) + : SwFieldPage(pPage, pController, "modules/swriter/ui/fldvarpage.ui", "FieldVarPage", pCoreSet) + , m_xTypeLB(m_xBuilder->weld_tree_view("type")) + , m_xSelection(m_xBuilder->weld_widget("selectframe")) + , m_xSelectionLB(m_xBuilder->weld_tree_view("select")) + , m_xNameFT(m_xBuilder->weld_label("nameft")) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xValueFT(m_xBuilder->weld_label("valueft")) + , m_xValueED(new ConditionEdit(m_xBuilder->weld_entry("value"))) + , m_xFormat(m_xBuilder->weld_widget("formatframe")) + , m_xNumFormatLB(new SwNumFormatTreeView(m_xBuilder->weld_tree_view("numformat"))) + , m_xFormatLB(m_xBuilder->weld_tree_view("format")) + , m_xChapterFrame(m_xBuilder->weld_widget("chapterframe")) + , m_xChapterLevelLB(m_xBuilder->weld_combo_box("level")) + , m_xInvisibleCB(m_xBuilder->weld_check_button("invisible")) + , m_xSeparatorFT(m_xBuilder->weld_label("separatorft")) + , m_xSeparatorED(m_xBuilder->weld_entry("separator")) + , m_xNewPB(m_xBuilder->weld_button("apply")) + , m_xDelPB(m_xBuilder->weld_button("delete")) + , nOldFormat(0) + , bInit(true) +{ + FillFieldSelect(*m_xTypeLB); + m_xSelectionLB->make_sorted(); + FillFieldSelect(*m_xFormatLB); + + auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH; + auto nHeight = m_xTypeLB->get_height_rows(20); + m_xTypeLB->set_size_request(nWidth, nHeight); + m_xSelectionLB->set_size_request(nWidth, nHeight); + m_xFormatLB->set_size_request(nWidth, nHeight/2); + + sOldValueFT = m_xValueFT->get_label(); + sOldNameFT = m_xNameFT->get_label(); + + for (sal_uInt16 i = 1; i <= MAXLEVEL; i++) + m_xChapterLevelLB->append_text(OUString::number(i)); + + m_xChapterLevelLB->set_active(0); + //enable 'active' language selection + m_xNumFormatLB->SetShowLanguageControl(true); +} + +SwFieldVarPage::~SwFieldVarPage() +{ +} + +void SwFieldVarPage::Reset(const SfxItemSet* ) +{ + SavePos(*m_xTypeLB); + + Init(); // general initialisation + + m_xTypeLB->freeze(); + m_xTypeLB->clear(); + + SwFieldTypesEnum nTypeId; + + if (!IsFieldEdit()) + { + // initialise TypeListBox + const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup()); + + for (short i = rRg.nStart; i < rRg.nEnd; ++i) + { + nTypeId = SwFieldMgr::GetTypeId(i); + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i)); + } + } + else + { + const SwField* pCurField = GetCurField(); + assert(pCurField && "<SwFieldVarPage::Reset(..)> - <SwField> instance missing!"); + nTypeId = pCurField->GetTypeId(); + if (nTypeId == SwFieldTypesEnum::SetInput) + nTypeId = SwFieldTypesEnum::Input; + m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(SwFieldMgr::GetPos(nTypeId))); + m_xNumFormatLB->SetAutomaticLanguage(pCurField->IsAutomaticLanguage()); + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + const SvNumberformat* pFormat = pSh->GetNumberFormatter()->GetEntry(pCurField->GetFormat()); + if(pFormat) + m_xNumFormatLB->SetLanguage(pFormat->GetLanguage()); + } + } + + m_xTypeLB->thaw(); + + // select old Pos + RestorePos(*m_xTypeLB); + + m_xTypeLB->connect_row_activated(LINK(this, SwFieldVarPage, TreeViewInsertHdl)); + m_xTypeLB->connect_changed(LINK(this, SwFieldVarPage, TypeHdl)); + m_xSelectionLB->connect_changed(LINK(this, SwFieldVarPage, SubTypeListBoxHdl)); + m_xSelectionLB->connect_row_activated(LINK(this, SwFieldVarPage, SubTypeInsertHdl)); + m_xFormatLB->connect_row_activated(LINK(this, SwFieldVarPage, TreeViewInsertHdl)); + m_xNumFormatLB->connect_row_activated(LINK(this, SwFieldVarPage, TreeViewInsertHdl)); + m_xNameED->connect_changed(LINK(this, SwFieldVarPage, ModifyHdl)); + m_xValueED->connect_changed(LINK(this, SwFieldVarPage, ModifyHdl)); + m_xNewPB->connect_clicked(LINK(this, SwFieldVarPage, TBClickHdl)); + m_xDelPB->connect_clicked(LINK(this, SwFieldVarPage, TBClickHdl)); + m_xChapterLevelLB->connect_changed(LINK(this, SwFieldVarPage, ChapterHdl)); + m_xSeparatorED->connect_changed(LINK(this, SwFieldVarPage, SeparatorHdl)); + + if( !IsRefresh() ) + { + OUString sUserData = GetUserData(); + sal_Int32 nIdx{ 0 }; + if(!IsRefresh() && sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1)) + { + OUString sVal = sUserData.getToken(0, ';', nIdx); + sal_uInt16 nVal = static_cast<sal_uInt16>(sVal.toInt32()); + if (USHRT_MAX != nVal) + { + for (sal_Int32 i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; i++) + { + if (nVal == m_xTypeLB->get_id(i).toUInt32()) + { + m_xTypeLB->select(i); + break; + } + } + } + } + } + TypeHdl(*m_xTypeLB); + + if (IsFieldEdit()) + { + m_xSelectionLB->save_value(); + m_xFormatLB->save_value(); + nOldFormat = m_xNumFormatLB->GetFormat(); + m_xNameED->save_value(); + m_xValueED->save_value(); + m_xInvisibleCB->save_state(); + m_xChapterLevelLB->save_value(); + m_xSeparatorED->save_value(); + } +} + +IMPL_LINK_NOARG(SwFieldVarPage, TypeHdl, weld::TreeView&, void) +{ + // save old ListBoxPos + const sal_Int32 nOld = GetTypeSel(); + + // current ListBoxPos + SetTypeSel(m_xTypeLB->get_selected_index()); + + if(GetTypeSel() == -1) + { + SetTypeSel(0); + m_xTypeLB->select(0); + } + + if (nOld != GetTypeSel() || nOld == -1) + { + bInit = true; + if (nOld != -1) + { + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + } + + m_xValueED->SetDropEnable(false); + UpdateSubType(); // initialise selection-listboxes + } + + bInit = false; +} + +IMPL_LINK( SwFieldVarPage, SubTypeListBoxHdl, weld::TreeView&, rBox, void ) +{ + SubTypeHdl(&rBox); +} + +void SwFieldVarPage::SubTypeHdl(const weld::TreeView* pBox) +{ + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + sal_Int32 nSelPos = m_xSelectionLB->get_selected_index(); + size_t nSelData = SIZE_MAX; + + if (nSelPos != -1) + nSelData = m_xSelectionLB->get_id(nSelPos).toUInt32(); + + if (IsFieldEdit() && (!pBox || bInit)) + { + if (nTypeId != SwFieldTypesEnum::Formel) + m_xNameED->set_text(GetFieldMgr().GetCurFieldPar1()); + + m_xValueED->set_text(GetFieldMgr().GetCurFieldPar2()); + } + + if (m_xNameFT->get_label() != sOldNameFT) + m_xNameFT->set_label(sOldNameFT); + if (m_xValueFT->get_label() != sOldValueFT) + m_xValueFT->set_label(sOldValueFT); + + FillFormatLB(nTypeId); + + sal_Int32 nSize = m_xFormatLB->n_children(); + + bool bValue = false, bName = false, bNumFormat = false, + bInvisible = false, bShowChapterFrame = false; + bool bFormat = nSize != 0; + + switch (nTypeId) + { + case SwFieldTypesEnum::User: + { + // change or create user type + SwUserFieldType* pType = static_cast<SwUserFieldType*>( + GetFieldMgr().GetFieldType(SwFieldIds::User, nSelData)); + + if (pType) + { + if (!IsFieldEdit()) + { + if (pBox || (bInit && !IsRefresh())) // only when interacting via mouse + { + m_xNameED->set_text(pType->GetName()); + + if (pType->GetType() == UF_STRING) + { + m_xValueED->set_text(pType->GetContent()); + m_xNumFormatLB->select(0); + } + else + m_xValueED->set_text(pType->GetContent()); + } + } + else + m_xValueED->set_text(pType->GetContent()); + } + else + { + if (pBox) // only when interacting via mouse + { + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + } + } + bValue = bName = bNumFormat = bInvisible = true; + + m_xValueED->SetDropEnable(true); + break; + } + + case SwFieldTypesEnum::Set: + bValue = true; + + bNumFormat = bInvisible = true; + + if (!IsFieldDlgHtmlMode()) + bName = true; + else + { + m_xNumFormatLB->clear(); + m_xNumFormatLB->append(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND), SwResId(FMT_SETVAR_TEXT)); + m_xNumFormatLB->select(0); + } + // is there a corresponding SetField + if (IsFieldEdit() || pBox) // only when interacting via mouse + { + if (nSelPos != -1) + { + OUString sName(m_xSelectionLB->get_selected_text()); + m_xNameED->set_text(sName); + + if (!IsFieldDlgHtmlMode()) + { + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + SwSetExpFieldType* pSetTyp = static_cast<SwSetExpFieldType*>( + pSh->GetFieldType(SwFieldIds::SetExp, sName)); + + if (pSetTyp && pSetTyp->GetType() == nsSwGetSetExpType::GSE_STRING) + m_xNumFormatLB->select(0); // textual + } + } + } + } + if (GetCurField() != nullptr && IsFieldEdit()) + { + // GetFormula leads to problems with date formats because + // only the numeric value without formatting is returned. + // It must be used though because otherwise in GetPar2 only + // the value calculated by Kalkulator would be displayed + // (instead of test2 = test + 1) + m_xValueED->set_text(static_cast<SwSetExpField*>(GetCurField())->GetFormula()); + } + m_xValueED->SetDropEnable(true); + break; + + case SwFieldTypesEnum::Formel: + { + bValue = true; + bNumFormat = true; + m_xValueFT->set_label(SwResId(STR_FORMULA)); + m_xValueED->SetDropEnable(true); + } + break; + + case SwFieldTypesEnum::Get: + { + if (!IsFieldEdit()) + { + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + } + + if (nSelPos != -1) + { + OUString sName(m_xSelectionLB->get_selected_text()); + if (!IsFieldEdit()) + m_xNameED->set_text(sName); + + // is there a corresponding SetField + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + SwSetExpFieldType* pSetTyp = static_cast<SwSetExpFieldType*>( + pSh->GetFieldType(SwFieldIds::SetExp, sName)); + + if(pSetTyp) + { + if (pSetTyp->GetType() & nsSwGetSetExpType::GSE_STRING) // textual? + bFormat = true; + else // numeric + bNumFormat = true; + } + } + } + else + bFormat = false; + + EnableInsert(bFormat || bNumFormat); + } + break; + + case SwFieldTypesEnum::Input: + m_xValueFT->set_label(SwResId(STR_PROMPT)); + + if (nSelPos != -1) + { + bValue = bNumFormat = true; + + OUString sName = m_xSelectionLB->get_selected_text(); + m_xNameED->set_text( sName ); + + // User- or SetField ? + if (!GetFieldMgr().GetFieldType(SwFieldIds::User, sName)) // SetExp + { + // is there a corresponding SetField + SwSetExpFieldType* pSetTyp = static_cast<SwSetExpFieldType*>( + GetFieldMgr().GetFieldType(SwFieldIds::SetExp, sName)); + + if(pSetTyp) + { + if (pSetTyp->GetType() == nsSwGetSetExpType::GSE_STRING) // textual? + { + m_xNumFormatLB->clear(); + m_xNumFormatLB->append(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND), SwResId(FMT_USERVAR_TEXT)); + m_xNumFormatLB->select(0); + } + } + if (GetCurField() && IsFieldEdit() && (!pBox || bInit) ) + m_xValueED->set_text(static_cast<SwSetExpField*>(GetCurField())->GetPromptText()); + } + else // USERFLD + bFormat = bNumFormat = false; + } + break; + + case SwFieldTypesEnum::DDE: + m_xValueFT->set_label(SwResId(STR_DDE_CMD)); + + if (IsFieldEdit() || pBox) // only when interacting via mouse + { + if (nSelPos != -1) + { + SwDDEFieldType* pType = + static_cast<SwDDEFieldType*>( GetFieldMgr().GetFieldType(SwFieldIds::Dde, nSelData) ); + + if(pType) + { + m_xNameED->set_text(pType->GetName()); + + //JP 28.08.95: DDE-Topics/-Items can have blanks in their names! + // That's not considered here yet + OUString sCmd( pType->GetCmd() ); + sal_Int32 nTmpPos = 0; + sCmd = sCmd.replaceFirst( OUStringChar(sfx2::cTokenSeparator), " ", &nTmpPos ); + sCmd = sCmd.replaceFirst( OUStringChar(sfx2::cTokenSeparator), " ", &nTmpPos ); + + m_xValueED->set_text( sCmd ); + m_xFormatLB->select(static_cast<int>(pType->GetType())); + } + } + } + bName = bValue = true; + break; + + case SwFieldTypesEnum::Sequence: + { + bName = bValue = bShowChapterFrame = true; + + SwFieldType* pFieldTyp; + if( GetCurField() && IsFieldEdit() ) + pFieldTyp = GetCurField()->GetTyp(); + else + { + OUString sFieldTypeName(m_xSelectionLB->get_text(nSelPos)); + if( !sFieldTypeName.isEmpty() ) + pFieldTyp = GetFieldMgr().GetFieldType( SwFieldIds::SetExp, + sFieldTypeName ); + else + pFieldTyp = nullptr; + } + + if( GetCurField() && IsFieldEdit() ) + m_xValueED->set_text( static_cast<SwSetExpField*>(GetCurField())-> + GetFormula() ); + + if( IsFieldEdit() || pBox ) // only when interacting via mouse + m_xNameED->set_text( m_xSelectionLB->get_selected_text() ); + + if( pFieldTyp ) + { + sal_uInt8 nLevel = static_cast<SwSetExpFieldType*>(pFieldTyp)->GetOutlineLvl(); + if( 0x7f == nLevel ) + m_xChapterLevelLB->set_active(0); + else + m_xChapterLevelLB->set_active(nLevel + 1); + OUString sDelim = static_cast<SwSetExpFieldType*>(pFieldTyp)->GetDelimiter(); + m_xSeparatorED->set_text( sDelim ); + ChapterHdl(*m_xChapterLevelLB); + } + } + break; + + case SwFieldTypesEnum::SetRefPage: + { + bValue = false; + m_xValueFT->set_label( SwResId( STR_OFFSET )); + + if (IsFieldEdit() || pBox) // only when interacting via mouse + m_xNameED->set_text(OUString()); + + if (nSelData != 0 && nSelData != SIZE_MAX) + { + bValue = true; // SubType OFF - knows no Offset + if (GetCurField() && IsFieldEdit()) + m_xValueED->set_text(OUString::number(static_cast<SwRefPageSetField*>(GetCurField())->GetOffset())); + } + } + break; + + case SwFieldTypesEnum::GetRefPage: + m_xNameED->set_text(OUString()); + m_xValueED->set_text(OUString()); + break; + + default: break; + } + + m_xNumFormatLB->set_visible(bNumFormat); + m_xFormatLB->set_visible(!bNumFormat); + + if (IsFieldEdit()) + bName = false; + + m_xFormat->set_sensitive(bFormat || bNumFormat); + m_xNameFT->set_sensitive(bName); + m_xNameED->set_sensitive(bName); + m_xValueFT->set_sensitive(bValue); + m_xValueED->set_sensitive(bValue); + + m_xInvisibleCB->set_visible(!bShowChapterFrame); + m_xChapterFrame->set_visible(bShowChapterFrame); + m_xInvisibleCB->set_sensitive(bInvisible); + + ModifyHdl(*m_xNameED); // apply/insert/delete status update +} + +IMPL_LINK(SwFieldVarPage, SubTypeInsertHdl, weld::TreeView&, rBox, bool) +{ + if (!bInit) + { + SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + if (nTypeId == SwFieldTypesEnum::Formel) + { + auto nSelPos = m_xSelectionLB->get_selected_index(); + if (nSelPos != -1) + { + m_xValueED->replace_selection(m_xSelectionLB->get_text(nSelPos)); + ModifyHdl(*m_xNameED); + return true; + } + } + } + TreeViewInsertHdl(rBox); + return true; +} + +// renew types in SelectionBox +void SwFieldVarPage::UpdateSubType() +{ + SetSelectionSel(m_xSelectionLB->get_selected_index()); + + OUString sOldSel; + if (GetSelectionSel() != -1) + sOldSel = m_xSelectionLB->get_text(GetSelectionSel()); + + // fill Selection-Listbox + m_xSelectionLB->freeze(); + m_xSelectionLB->clear(); + + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + std::vector<OUString> aList; + GetFieldMgr().GetSubTypes(nTypeId, aList); + const size_t nCount = aList.size(); + for (size_t i = 0; i < nCount; ++i) + { + if (nTypeId != SwFieldTypesEnum::Input || i) + { + if (!IsFieldEdit()) + { + m_xSelectionLB->append(OUString::number(i), aList[i]); + } + else + { + bool bInsert = false; + + switch (nTypeId) + { + case SwFieldTypesEnum::Input: + if (GetCurField() && aList[i] == GetCurField()->GetPar1()) + bInsert = true; + break; + + case SwFieldTypesEnum::Formel: + bInsert = true; + break; + + case SwFieldTypesEnum::Get: + if (GetCurField() && aList[i] == static_cast<const SwFormulaField*>(GetCurField())->GetFormula()) + bInsert = true; + break; + + case SwFieldTypesEnum::Set: + case SwFieldTypesEnum::User: + if (GetCurField() && aList[i] == GetCurField()->GetTyp()->GetName()) + { + bInsert = true; + if (GetCurField()->GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE) + m_xInvisibleCB->set_active(true); + } + break; + + case SwFieldTypesEnum::SetRefPage: + { + if (GetCurField() != nullptr + && ((static_cast<SwRefPageSetField*>(GetCurField())->IsOn() + && i) || (!static_cast<SwRefPageSetField*>(GetCurField()) + ->IsOn() && !i))) + { + sOldSel = aList[i]; + } + + // allow all entries for selection: + m_xSelectionLB->append(OUString::number(i), aList[i]); + break; + } + default: + if (GetCurField() && aList[i] == GetCurField()->GetPar1()) + bInsert = true; + break; + } + + if (bInsert) + { + m_xSelectionLB->append(OUString::number(i), aList[i]); + if (nTypeId != SwFieldTypesEnum::Formel) + break; + } + } + } + } + + m_xSelectionLB->thaw(); + + const bool bEnable = m_xSelectionLB->n_children() != 0; + weld::TreeView* pLB = nullptr; + + if (bEnable) + { + int nIndex = m_xSelectionLB->find_text(sOldSel); + if (nIndex != -1) + m_xSelectionLB->select(nIndex); + else + { + m_xSelectionLB->select(0); + pLB = m_xSelectionLB.get(); // newly initialise all controls + } + } + + m_xSelection->set_sensitive(bEnable); + + SubTypeHdl(pLB); +} + +void SwFieldVarPage::FillFormatLB(SwFieldTypesEnum nTypeId) +{ + OUString sOldSel; + const sal_Int32 nFormatSel = m_xFormatLB->get_selected_index(); + if (nFormatSel != -1) + sOldSel = m_xFormatLB->get_text(nFormatSel); + + weld::TreeView& rWidget = dynamic_cast<weld::TreeView&>(m_xNumFormatLB->get_widget()); + + OUString sOldNumSel; + sal_uInt32 nOldNumFormat = 0; + sal_Int32 nNumFormatSel = rWidget.get_selected_index(); + if (nNumFormatSel != -1) + { + sOldNumSel = rWidget.get_text(nNumFormatSel); + nOldNumFormat = m_xNumFormatLB->GetFormat(); + } + + // fill Format-Listbox + m_xFormatLB->freeze(); + m_xFormatLB->clear(); + m_xNumFormatLB->clear(); // flags list as dirty and needing refilling with stock entries + bool bSpecialFormat = false; + + if( SwFieldTypesEnum::GetRefPage != nTypeId ) + { + if (GetCurField() != nullptr && IsFieldEdit()) + { + bSpecialFormat = GetCurField()->GetFormat() == NUMBERFORMAT_ENTRY_NOT_FOUND; + + if (!bSpecialFormat) + { + m_xNumFormatLB->SetDefFormat(GetCurField()->GetFormat()); + sOldNumSel.clear(); + } + else if (nTypeId == SwFieldTypesEnum::Get || nTypeId == SwFieldTypesEnum::Formel) + { + m_xNumFormatLB->SetFormatType(SvNumFormatType::NUMBER); + } + } + else + { + if (nOldNumFormat && nOldNumFormat != NUMBERFORMAT_ENTRY_NOT_FOUND) + m_xNumFormatLB->SetDefFormat(nOldNumFormat); + else + m_xNumFormatLB->SetFormatType(SvNumFormatType::NUMBER); + } + } + + switch (nTypeId) + { + case SwFieldTypesEnum::User: + { + if (!IsFieldEdit() || bSpecialFormat) + { + OUString sId(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND)); + rWidget.insert(0, SwResId(FMT_MARK_TEXT), &sId, nullptr, nullptr); + rWidget.insert(1, SwResId(FMT_USERVAR_CMD), &sId, nullptr, nullptr); + } + } + break; + + case SwFieldTypesEnum::Set: + { + if (!IsFieldEdit() || bSpecialFormat) + { + OUString sId(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND)); + rWidget.insert(0, SwResId(FMT_SETVAR_TEXT), &sId, nullptr, nullptr); + } + } + break; + + case SwFieldTypesEnum::Formel: + { + OUString sId(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND)); + rWidget.insert(0, SwResId(FMT_GETVAR_NAME), &sId, nullptr, nullptr); + } + break; + + case SwFieldTypesEnum::Get: + { + OUString sId(OUString::number(NUMBERFORMAT_ENTRY_NOT_FOUND)); + rWidget.insert(0, SwResId(FMT_GETVAR_NAME), &sId, nullptr, nullptr); + } + break; + + default: break; + } + + if (IsFieldEdit() && bSpecialFormat) + { + if (nTypeId == SwFieldTypesEnum::User && (GetCurField()->GetSubType() & nsSwExtendedSubType::SUB_CMD)) + rWidget.select(1); + else + rWidget.select(0); + } + else + { + if (!nOldNumFormat && (nNumFormatSel = rWidget.find_text(sOldNumSel)) != -1) + rWidget.select(nNumFormatSel); + else if (nOldNumFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + rWidget.select_text(sOldSel); + } + + const sal_uInt16 nSize = GetFieldMgr().GetFormatCount(nTypeId, IsFieldDlgHtmlMode()); + + OUString sSelectId; + + for (sal_uInt16 i = 0; i < nSize; i++) + { + const sal_uInt16 nFieldId = GetFieldMgr().GetFormatId( nTypeId, i ); + OUString sId(OUString::number(nFieldId)); + m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(nTypeId, i)); + if (IsFieldEdit() && GetCurField() && nFieldId == GetCurField()->GetFormat()) + sSelectId = sId; + } + + m_xFormatLB->thaw(); + if (!sSelectId.isEmpty()) + m_xFormatLB->select_id(sSelectId); + + if (nSize && (!IsFieldEdit() || m_xFormatLB->get_selected_index() == -1)) + { + int nIndex = m_xFormatLB->find_text(sOldSel); + if (nIndex == -1) + nIndex = m_xFormatLB->find_text(SwResId(FMT_NUM_PAGEDESC)); + if (nIndex == -1) + nIndex = m_xFormatLB->find_text(SwResId(FMT_NUM_ARABIC)); + if (nIndex == -1) + nIndex = 0; + m_xFormatLB->select(nIndex); + } +} + +// Modify +IMPL_LINK_NOARG(SwFieldVarPage, ModifyHdl, weld::Entry&, void) +{ + OUString sValue(m_xValueED->get_text()); + bool bHasValue = !sValue.isEmpty(); + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + bool bInsert = false, bApply = false, bDelete = false; + + OUString sName( m_xNameED->get_text() ); + sal_Int32 nLen = sName.getLength(); + + switch( nTypeId ) + { + case SwFieldTypesEnum::DDE: + case SwFieldTypesEnum::User: + case SwFieldTypesEnum::Set: + case SwFieldTypesEnum::Sequence: + SwCalc::IsValidVarName( sName, &sName ); + if ( sName.getLength() != nLen ) + { + nLen = sName.getLength(); + int nStartPos, nEndPos; + m_xNameED->get_selection_bounds(nStartPos, nEndPos); + m_xNameED->set_text( sName ); + m_xNameED->select_region(nStartPos, nEndPos); // restore Cursorpos + } + break; + default: break; + } + + // check buttons + switch (nTypeId) + { + case SwFieldTypesEnum::DDE: + if( nLen ) + { + // is there already a corresponding type + bInsert = bApply = true; + + SwFieldType* pType = GetFieldMgr().GetFieldType(SwFieldIds::Dde, sName); + + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh && pType) + bDelete = !pSh->IsUsed( *pType ); + } + break; + + case SwFieldTypesEnum::User: + if( nLen ) + { + // is there already a corresponding type + SwFieldType* pType = GetFieldMgr().GetFieldType(SwFieldIds::User, sName); + + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh && pType) + bDelete = !pSh->IsUsed( *pType ); + + pType = GetFieldMgr().GetFieldType(SwFieldIds::SetExp, sName); + if (!pType) // no name conflict with variables + { + // user fields can also be inserted without content! + // Bug #56845 + bInsert = bApply = true; + } + } + break; + + default: + bInsert = true; + + if (nTypeId == SwFieldTypesEnum::Set || nTypeId == SwFieldTypesEnum::Sequence) + { + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>( + GetFieldMgr().GetFieldType(SwFieldIds::SetExp, sName)); + + if (pFieldType) + { + + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + const SwFieldTypes* p = pSh->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes(); + sal_uInt16 i; + + for (i = 0; i < INIT_FLDTYPES; i++) + { + SwFieldType* pType = (*p)[ i ].get(); + if (pType == pFieldType) + break; + } + + if (i >= INIT_FLDTYPES && !pSh->IsUsed(*pFieldType)) + bDelete = true; + + if (nTypeId == SwFieldTypesEnum::Sequence && !(pFieldType->GetType() & nsSwGetSetExpType::GSE_SEQ)) + bInsert = false; + + if (nTypeId == SwFieldTypesEnum::Set && (pFieldType->GetType() & nsSwGetSetExpType::GSE_SEQ)) + bInsert = false; + } + } + if (GetFieldMgr().GetFieldType(SwFieldIds::User, sName)) + bInsert = false; + } + + if (!nLen && (nTypeId == SwFieldTypesEnum::Set || nTypeId == SwFieldTypesEnum::Input || + (!IsFieldEdit() && nTypeId == SwFieldTypesEnum::Get ) ) ) + bInsert = false; + + if( (nTypeId == SwFieldTypesEnum::Set || nTypeId == SwFieldTypesEnum::Formel) && + !bHasValue ) + bInsert = false; + break; + } + + m_xNewPB->set_sensitive(bApply); + m_xDelPB->set_sensitive(bDelete); + EnableInsert(bInsert); +} + +IMPL_LINK(SwFieldVarPage, TBClickHdl, weld::Button&, rBox, void) +{ + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + if (&rBox == m_xDelPB.get()) + { + if( nTypeId == SwFieldTypesEnum::User ) + GetFieldMgr().RemoveFieldType(SwFieldIds::User, m_xSelectionLB->get_selected_text()); + else + { + SwFieldIds nWhich; + + switch(nTypeId) + { + case SwFieldTypesEnum::Set: + case SwFieldTypesEnum::Sequence: + nWhich = SwFieldIds::SetExp; + break; + default: + nWhich = SwFieldIds::Dde; + break; + } + + GetFieldMgr().RemoveFieldType(nWhich, m_xSelectionLB->get_selected_text()); + } + + UpdateSubType(); + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + pSh->SetModified(); + } + } + else if (&rBox == m_xNewPB.get()) + { + OUString sName(m_xNameED->get_text()), sValue(m_xValueED->get_text()); + SwFieldType* pType = nullptr; + SwFieldIds nId = SwFieldIds::Database; + sal_Int32 nNumFormatPos = m_xNumFormatLB->get_selected_index(); + + switch (nTypeId) + { + case SwFieldTypesEnum::User: nId = SwFieldIds::User; break; + case SwFieldTypesEnum::DDE: nId = SwFieldIds::Dde; break; + case SwFieldTypesEnum::Set: nId = SwFieldIds::SetExp;break; + default: break; + } + pType = GetFieldMgr().GetFieldType(nId, sName); + + int nFormat = m_xFormatLB->get_selected_index(); + if (nFormat != -1) + nFormat = m_xFormatLB->get_id(nFormat).toUInt32(); + + if (pType) // change + { + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + pSh->StartAllAction(); + + if (nTypeId == SwFieldTypesEnum::User) + { + if (nNumFormatPos != -1) + { + sal_uLong nNumberFormat = nNumFormatPos == 0 ? 0 : m_xNumFormatLB->GetFormat(); + if (nNumberFormat) + { // Switch language to office-language because Kalkulator expects + // String in office format and it should be fed into dialog like + // that + nNumberFormat = SwValueField::GetSystemFormat(pSh->GetNumberFormatter(), nNumberFormat); + } + static_cast<SwUserFieldType*>(pType)->SetContent(m_xValueED->get_text(), nNumberFormat); + static_cast<SwUserFieldType*>(pType)->SetType( + nNumFormatPos == 0 ? nsSwGetSetExpType::GSE_STRING : nsSwGetSetExpType::GSE_EXPR ); + } + } + else + { + if (nFormat != -1) + { + // DDE-Topics/-Items can have blanks in their names! + // That's not being considered here yet. + sal_Int32 nTmpPos = 0; + sValue = sValue.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nTmpPos ); + sValue = sValue.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nTmpPos ); + static_cast<SwDDEFieldType*>(pType)->SetCmd(sValue); + static_cast<SwDDEFieldType*>(pType)->SetType(static_cast<SfxLinkUpdateMode>(nFormat)); + } + } + pType->UpdateFields(); + + pSh->EndAllAction(); + } + } + else // new + { + if(nTypeId == SwFieldTypesEnum::User) + { + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + SwUserFieldType aType( pSh->GetDoc(), sName ); + + if (nNumFormatPos != -1) + { + aType.SetType(nNumFormatPos == 0 ? nsSwGetSetExpType::GSE_STRING : nsSwGetSetExpType::GSE_EXPR); + aType.SetContent( sValue, nNumFormatPos == 0 ? 0 : m_xNumFormatLB->GetFormat() ); + m_xSelectionLB->append_text(sName); + m_xSelectionLB->select_text(sName); + GetFieldMgr().InsertFieldType( aType ); // Userfld new + } + } + } + else + { + if (nFormat != -1) + { + // DDE-Topics/-Items can have blanks in their names! + // That's not being considered here yet. + sal_Int32 nTmpPos = 0; + sValue = sValue.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nTmpPos ); + sValue = sValue.replaceFirst( " ", OUStringChar(sfx2::cTokenSeparator), &nTmpPos ); + + SwDDEFieldType aType(sName, sValue, static_cast<SfxLinkUpdateMode>(nFormat)); + m_xSelectionLB->append_text(sName); + m_xSelectionLB->select_text(sName); + GetFieldMgr().InsertFieldType(aType); // DDE-Field new + } + } + } + if (IsFieldEdit()) + GetFieldMgr().GetCurField(); // update FieldManager + + UpdateSubType(); + } +} + +IMPL_LINK_NOARG(SwFieldVarPage, ChapterHdl, weld::ComboBox&, void) +{ + bool bEnable = m_xChapterLevelLB->get_active() != 0; + + m_xSeparatorED->set_sensitive(bEnable); + m_xSeparatorFT->set_sensitive(bEnable); + SeparatorHdl(*m_xSeparatorED); +} + +IMPL_LINK_NOARG(SwFieldVarPage, SeparatorHdl, weld::Entry&, void) +{ + bool bEnable = !m_xSeparatorED->get_text().isEmpty() || + m_xChapterLevelLB->get_active() == 0; + EnableInsert(bEnable); +} + +bool SwFieldVarPage::FillItemSet(SfxItemSet* ) +{ + const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32()); + + OUString aVal(m_xValueED->get_text()); + OUString aName(m_xNameED->get_text()); + + const sal_Int32 nSubPos = m_xSelectionLB->get_selected_index(); + sal_uInt16 nSubType = (nSubPos == -1) ? 0 : m_xSelectionLB->get_id(nSubPos).toUInt32(); + + sal_uInt32 nFormat; + + if (!m_xNumFormatLB->get_visible()) + { + sal_Int32 nFormatPos = m_xFormatLB->get_selected_index(); + + if(nFormatPos == -1) + nFormat = 0; + else + nFormat = m_xFormatLB->get_id(nFormatPos).toUInt32(); + } + else + { + nFormat = m_xNumFormatLB->GetFormat(); + + if (nFormat && nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND && m_xNumFormatLB->IsAutomaticLanguage()) + { + // Switch language to office language because Kalkulator expects + // String in office format and it should be fed into the dialog + // like that + SwWrtShell *pSh = GetWrtShell(); + if(!pSh) + pSh = ::GetActiveWrtShell(); + if(pSh) + { + nFormat = SwValueField::GetSystemFormat(pSh->GetNumberFormatter(), nFormat); + } + } + } + sal_Unicode cSeparator = ' '; + switch (nTypeId) + { + case SwFieldTypesEnum::User: + { + nSubType = (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) ? nsSwGetSetExpType::GSE_STRING : nsSwGetSetExpType::GSE_EXPR; + + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND && m_xNumFormatLB->get_selected_text() == SwResId(FMT_USERVAR_CMD)) + nSubType |= nsSwExtendedSubType::SUB_CMD; + + if (m_xInvisibleCB->get_active()) + nSubType |= nsSwExtendedSubType::SUB_INVISIBLE; + break; + } + case SwFieldTypesEnum::Formel: + { + nSubType = nsSwGetSetExpType::GSE_FORMULA; + if (m_xNumFormatLB->get_visible() && nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + nSubType |= nsSwExtendedSubType::SUB_CMD; + break; + } + case SwFieldTypesEnum::Get: + { + nSubType &= 0xff00; + if (m_xNumFormatLB->get_visible() && nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + nSubType |= nsSwExtendedSubType::SUB_CMD; + break; + } + case SwFieldTypesEnum::Input: + { + SwFieldType* pType = GetFieldMgr().GetFieldType(SwFieldIds::User, aName); + nSubType = static_cast< sal_uInt16 >((nSubType & 0xff00) | (pType ? INP_USR : INP_VAR)); + break; + } + + case SwFieldTypesEnum::Set: + { + if (IsFieldDlgHtmlMode()) + { + nSubType = 0x0100; + nSubType = (nSubType & 0xff00) | nsSwGetSetExpType::GSE_STRING; + } + else + nSubType = (nSubType & 0xff00) | ((nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) ? nsSwGetSetExpType::GSE_STRING : nsSwGetSetExpType::GSE_EXPR); + + if (m_xInvisibleCB->get_active()) + nSubType |= nsSwExtendedSubType::SUB_INVISIBLE; + break; + } + case SwFieldTypesEnum::Sequence: + { + nSubType = static_cast< sal_uInt16 >(m_xChapterLevelLB->get_active()); + if (nSubType == 0) + nSubType = 0x7f; + else + { + nSubType--; + OUString sSeparator(m_xSeparatorED->get_text()[0]); + cSeparator = !sSeparator.isEmpty() ? sSeparator[0] : ' '; + } + break; + } + case SwFieldTypesEnum::GetRefPage: + if( SVX_NUM_CHAR_SPECIAL == nFormat ) + aVal = m_xValueED->get_text(); + break; + default: break; + } + + if (!IsFieldEdit() || + m_xNameED->get_value_changed_from_saved() || + m_xValueED->get_value_changed_from_saved() || + m_xSelectionLB->get_value_changed_from_saved() || + m_xFormatLB->get_value_changed_from_saved() || + nOldFormat != m_xNumFormatLB->GetFormat() || + m_xInvisibleCB->get_state_changed_from_saved() || + m_xChapterLevelLB->get_value_changed_from_saved() || + m_xSeparatorED->get_value_changed_from_saved()) + { + InsertField( nTypeId, nSubType, aName, aVal, nFormat, + cSeparator, m_xNumFormatLB->IsAutomaticLanguage() ); + } + + UpdateSubType(); + + return false; +} + +std::unique_ptr<SfxTabPage> SwFieldVarPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet *const pAttrSet) +{ + return std::make_unique<SwFieldVarPage>( pPage, pController, pAttrSet ); +} + +sal_uInt16 SwFieldVarPage::GetGroup() +{ + return GRP_VAR; +} + +void SwFieldVarPage::FillUserData() +{ + OUString sData = USER_DATA_VERSION ";"; + sal_Int32 nTypeSel = m_xTypeLB->get_selected_index(); + if( -1 == nTypeSel ) + nTypeSel = USHRT_MAX; + else + nTypeSel = m_xTypeLB->get_id(nTypeSel).toUInt32(); + sData += OUString::number( nTypeSel ); + SetUserData(sData); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/fldvar.hxx b/sw/source/ui/fldui/fldvar.hxx new file mode 100644 index 000000000..b214e5adf --- /dev/null +++ b/sw/source/ui/fldui/fldvar.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_FLDUI_FLDVAR_HXX +#define INCLUDED_SW_SOURCE_UI_FLDUI_FLDVAR_HXX + +#include <sfx2/tabdlg.hxx> + +#include "fldpage.hxx" +#include <condedit.hxx> +#include <numfmtlb.hxx> + +class SwFieldVarPage; + +class SwFieldVarPage : public SwFieldPage +{ + std::unique_ptr<weld::TreeView> m_xTypeLB; + std::unique_ptr<weld::Widget> m_xSelection; + std::unique_ptr<weld::TreeView> m_xSelectionLB; + std::unique_ptr<weld::Label> m_xNameFT; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Label> m_xValueFT; + std::unique_ptr<ConditionEdit> m_xValueED; + std::unique_ptr<weld::Widget> m_xFormat; + std::unique_ptr<SwNumFormatTreeView> m_xNumFormatLB; + std::unique_ptr<weld::TreeView> m_xFormatLB; + std::unique_ptr<weld::Widget> m_xChapterFrame; + std::unique_ptr<weld::ComboBox> m_xChapterLevelLB; + std::unique_ptr<weld::CheckButton> m_xInvisibleCB; + std::unique_ptr<weld::Label> m_xSeparatorFT; + std::unique_ptr<weld::Entry> m_xSeparatorED; + std::unique_ptr<weld::Button> m_xNewPB; + std::unique_ptr<weld::Button> m_xDelPB; + + OUString sOldValueFT; + OUString sOldNameFT; + + sal_uInt32 nOldFormat; + bool bInit; + + DECL_LINK( TypeHdl, weld::TreeView&, void ); + DECL_LINK( SubTypeListBoxHdl, weld::TreeView&, void ); + DECL_LINK( ModifyHdl, weld::Entry&, void ); + DECL_LINK( TBClickHdl, weld::Button&, void ); + DECL_LINK( ChapterHdl, weld::ComboBox&, void ); + DECL_LINK( SeparatorHdl, weld::Entry&, void ); + DECL_LINK( SubTypeInsertHdl, weld::TreeView&, bool ); + void SubTypeHdl(const weld::TreeView*); + + void UpdateSubType(); + void FillFormatLB(SwFieldTypesEnum nTypeId); + +protected: + virtual sal_uInt16 GetGroup() override; + +public: + SwFieldVarPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + virtual ~SwFieldVarPage() override; + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void FillUserData() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/inpdlg.cxx b/sw/source/ui/fldui/inpdlg.cxx new file mode 100644 index 000000000..a1cbc81dd --- /dev/null +++ b/sw/source/ui/fldui/inpdlg.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lineend.hxx> +#include <unotools/charclass.hxx> +#include <wrtsh.hxx> +#include <fldbas.hxx> +#include <expfld.hxx> +#include <usrfld.hxx> +#include <inpdlg.hxx> + +// edit field-insert +SwFieldInputDlg::SwFieldInputDlg(weld::Widget *pParent, SwWrtShell &rS, + SwField* pField, bool bPrevButton, bool bNextButton) + : GenericDialogController(pParent, "modules/swriter/ui/inputfielddialog.ui", "InputFieldDialog") + , rSh( rS ) + , pInpField(nullptr) + , pSetField(nullptr) + , pUsrType(nullptr) + , m_pPressedButton(nullptr) + , m_xLabelED(m_xBuilder->weld_entry("name")) + , m_xEditED(m_xBuilder->weld_text_view("text")) + , m_xPrevBT(m_xBuilder->weld_button("prev")) + , m_xNextBT(m_xBuilder->weld_button("next")) + , m_xOKBT(m_xBuilder->weld_button("ok")) +{ + m_xEditED->set_size_request(-1, m_xEditED->get_height_rows(8)); + + if( bPrevButton || bNextButton ) + { + m_xPrevBT->show(); + m_xPrevBT->connect_clicked(LINK(this, SwFieldInputDlg, PrevHdl)); + m_xPrevBT->set_sensitive(bPrevButton); + + m_xNextBT->show(); + m_xNextBT->connect_clicked(LINK(this, SwFieldInputDlg, NextHdl)); + m_xNextBT->set_sensitive(bNextButton); + } + + // evaluation here + OUString aStr; + if( SwFieldIds::Input == pField->GetTyp()->Which() ) + { // it is an input field + + pInpField = static_cast<SwInputField*>(pField); + m_xLabelED->set_text(pInpField->GetPar2()); + sal_uInt16 nSubType = pInpField->GetSubType(); + + switch(nSubType & 0xff) + { + case INP_TXT: + aStr = pInpField->GetPar1(); + break; + + case INP_USR: + // user field + if( nullptr != ( pUsrType = static_cast<SwUserFieldType*>(rSh.GetFieldType( + SwFieldIds::User, pInpField->GetPar1() ) ) ) ) + aStr = pUsrType->GetContent(); + break; + } + } + else + { + // it is a SetExpression + pSetField = static_cast<SwSetExpField*>(pField); + OUString sFormula(pSetField->GetFormula()); + //values are formatted - formulas are not + CharClass aCC( LanguageTag( pSetField->GetLanguage() )); + if( aCC.isNumeric( sFormula )) + { + aStr = pSetField->ExpandField(true, rS.GetLayout()); + } + else + aStr = sFormula; + m_xLabelED->set_text(pSetField->GetPromptText()); + } + + // JP 31.3.00: Inputfields in readonly regions must be allowed to + // input any content. - 74639 + bool bEnable = !rSh.IsCursorReadonly(); + + m_xOKBT->set_sensitive( bEnable ); + m_xEditED->set_editable( bEnable ); + + if( !aStr.isEmpty() ) + m_xEditED->set_text(convertLineEnd(aStr, GetSystemLineEnd())); + m_xEditED->grab_focus(); + + // preselect all text to allow quickly changing the content + if (bEnable) + m_xEditED->select_region(0, -1); +} + +SwFieldInputDlg::~SwFieldInputDlg() +{ +} + +// Close +void SwFieldInputDlg::Apply() +{ + OUString aTmp = m_xEditED->get_text().replaceAll("\r", ""); + rSh.StartAllAction(); + bool bModified = false; + if(pInpField) + { + if(pUsrType) + { + if( aTmp != pUsrType->GetContent() ) + { + pUsrType->SetContent(aTmp); + pUsrType->UpdateFields(); + bModified = true; + } + } + else if( aTmp != pInpField->GetPar1() ) + { + pInpField->SetPar1(aTmp); + rSh.SwEditShell::UpdateOneField(*pInpField); + bModified = true; + } + } + else if( aTmp != pSetField->GetPar2()) + { + pSetField->SetPar2(aTmp); + rSh.SwEditShell::UpdateOneField(*pSetField); + bModified = true; + } + + if( bModified ) + rSh.SetUndoNoResetModified(); + + rSh.EndAllAction(); +} + +bool SwFieldInputDlg::PrevButtonPressed() const +{ + return m_pPressedButton == m_xPrevBT.get(); +} + +bool SwFieldInputDlg::NextButtonPressed() const +{ + return m_pPressedButton == m_xNextBT.get(); +} + +IMPL_LINK_NOARG(SwFieldInputDlg, PrevHdl, weld::Button&, void) +{ + m_pPressedButton = m_xPrevBT.get(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SwFieldInputDlg, NextHdl, weld::Button&, void) +{ + m_pPressedButton = m_xNextBT.get(); + m_xDialog->response(RET_OK); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fldui/javaedit.cxx b/sw/source/ui/fldui/javaedit.cxx new file mode 100644 index 000000000..bfec32023 --- /dev/null +++ b/sw/source/ui/fldui/javaedit.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/ui/dialogs/TemplateDescription.hpp> +#include <svl/urihelper.hxx> +#include <view.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/filedlghelper.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <fldbas.hxx> +#include <fldmgr.hxx> +#include <docufld.hxx> +#include <javaedit.hxx> + +#include <strings.hrc> + +using namespace ::com::sun::star; + +SwJavaEditDialog::SwJavaEditDialog(weld::Window* pParent, SwWrtShell* pWrtSh) + : GenericDialogController(pParent, "modules/swriter/ui/insertscript.ui", "InsertScriptDialog") + , m_bNew(true) + , m_bIsUrl(false) + , m_pSh(pWrtSh) + , m_xTypeED(m_xBuilder->weld_entry("scripttype")) + , m_xUrlRB(m_xBuilder->weld_radio_button("url")) + , m_xEditRB(m_xBuilder->weld_radio_button("text")) + , m_xUrlPB(m_xBuilder->weld_button("browse")) + , m_xUrlED(m_xBuilder->weld_entry("urlentry")) + , m_xEditED(m_xBuilder->weld_text_view("textentry")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) + , m_xPrevBtn(m_xBuilder->weld_button("previous")) + , m_xNextBtn(m_xBuilder->weld_button("next")) +{ + // install handler + m_xPrevBtn->connect_clicked( LINK( this, SwJavaEditDialog, PrevHdl ) ); + m_xNextBtn->connect_clicked( LINK( this, SwJavaEditDialog, NextHdl ) ); + m_xOKBtn->connect_clicked( LINK( this, SwJavaEditDialog, OKHdl ) ); + + Link<weld::Button&,void> aLk = LINK(this, SwJavaEditDialog, RadioButtonHdl); + m_xUrlRB->connect_clicked(aLk); + m_xEditRB->connect_clicked(aLk); + m_xUrlPB->connect_clicked(LINK(this, SwJavaEditDialog, InsertFileHdl)); + + m_pMgr.reset(new SwFieldMgr(m_pSh)); + m_pField = static_cast<SwScriptField*>(m_pMgr->GetCurField()); + + m_bNew = !(m_pField && m_pField->GetTyp()->Which() == SwFieldIds::Script); + + CheckTravel(); + + if (!m_bNew) + m_xDialog->set_title(SwResId(STR_JAVA_EDIT)); + + RadioButtonHdl(*m_xUrlRB); +} + +SwJavaEditDialog::~SwJavaEditDialog() +{ + m_pSh->EnterStdMode(); + m_pMgr.reset(); + m_pFileDlg.reset(); +} + +IMPL_LINK_NOARG(SwJavaEditDialog, PrevHdl, weld::Button&, void) +{ + m_pSh->EnterStdMode(); + + SetField(); + m_pMgr->GoPrev(); + m_pField = static_cast<SwScriptField*>(m_pMgr->GetCurField()); + CheckTravel(); + RadioButtonHdl(*m_xUrlRB); +} + +IMPL_LINK_NOARG(SwJavaEditDialog, NextHdl, weld::Button&, void) +{ + m_pSh->EnterStdMode(); + + SetField(); + m_pMgr->GoNext(); + m_pField = static_cast<SwScriptField*>(m_pMgr->GetCurField()); + CheckTravel(); + RadioButtonHdl(*m_xUrlRB); +} + +IMPL_LINK_NOARG(SwJavaEditDialog, OKHdl, weld::Button&, void) +{ + SetField(); + m_xDialog->response(RET_OK); +} + +void SwJavaEditDialog::CheckTravel() +{ + bool bTravel = false; + bool bNext(false), bPrev(false); + + if (!m_bNew) + { + // Traveling only when more than one field + m_pSh->StartAction(); + m_pSh->CreateCursor(); + + bNext = m_pMgr->GoNext(); + if( bNext ) + m_pMgr->GoPrev(); + + bPrev = m_pMgr->GoPrev(); + if( bPrev ) + m_pMgr->GoNext(); + bTravel |= bNext || bPrev; + + m_pSh->DestroyCursor(); + m_pSh->EndAction(); + + if (m_pField->IsCodeURL()) + { + OUString sURL(m_pField->GetPar2()); + if(!sURL.isEmpty()) + { + INetURLObject aINetURL(sURL); + if(INetProtocol::File == aINetURL.GetProtocol()) + sURL = aINetURL.PathToFileName(); + } + m_xUrlED->set_text(sURL); + m_xEditED->set_text(OUString()); + m_xUrlRB->set_active(true); + } + else + { + m_xEditED->set_text(m_pField->GetPar2()); + m_xUrlED->set_text(OUString()); + m_xEditRB->set_active(true); + } + m_xTypeED->set_text(m_pField->GetPar1()); + } + + if ( !bTravel ) + { + m_xPrevBtn->hide(); + m_xNextBtn->hide(); + } + else + { + m_xPrevBtn->set_sensitive(bPrev); + m_xNextBtn->set_sensitive(bNext); + } +} + +void SwJavaEditDialog::SetField() +{ + if( !m_xOKBtn->get_sensitive() ) + return ; + + m_aType = m_xTypeED->get_text(); + m_bIsUrl = m_xUrlRB->get_active(); + + if (m_bIsUrl) + { + m_aText = m_xUrlED->get_text(); + if (!m_aText.isEmpty()) + { + SfxMedium* pMedium = m_pSh->GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + + m_aText = URIHelper::SmartRel2Abs( + aAbs, m_aText, URIHelper::GetMaybeFileHdl()); + } + } + else + m_aText = m_xEditED->get_text(); + + if (m_aType.isEmpty()) + m_aType = "JavaScript"; +} + +bool SwJavaEditDialog::IsUpdate() const +{ + return m_pField && ( sal_uInt32(m_bIsUrl ? 1 : 0) != m_pField->GetFormat() || m_pField->GetPar2() != m_aType || m_pField->GetPar1() != m_aText ); +} + +IMPL_LINK_NOARG(SwJavaEditDialog, RadioButtonHdl, weld::Button&, void) +{ + bool bEnable = m_xUrlRB->get_active(); + m_xUrlPB->set_sensitive(bEnable); + m_xUrlED->set_sensitive(bEnable); + m_xEditED->set_sensitive(!bEnable); + + if (!m_bNew) + { + bEnable = !m_pSh->IsReadOnlyAvailable() || !m_pSh->HasReadonlySel(); + m_xOKBtn->set_sensitive(bEnable); + m_xUrlED->set_editable(bEnable); + m_xEditED->set_editable(bEnable); + m_xTypeED->set_editable(bEnable); + if( m_xUrlPB->get_sensitive() && !bEnable ) + m_xUrlPB->set_sensitive( false ); + } +} + +IMPL_LINK_NOARG( SwJavaEditDialog, InsertFileHdl, weld::Button&, void ) +{ + if (!m_pFileDlg) + { + m_pFileDlg.reset(new ::sfx2::FileDialogHelper( + ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::Insert, "swriter", SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_xDialog.get())); + } + + m_pFileDlg->StartExecuteModal( LINK( this, SwJavaEditDialog, DlgClosedHdl ) ); +} + +IMPL_LINK_NOARG(SwJavaEditDialog, DlgClosedHdl, sfx2::FileDialogHelper *, void) +{ + if (m_pFileDlg->GetError() == ERRCODE_NONE) + { + OUString sFileName = m_pFileDlg->GetPath(); + if ( !sFileName.isEmpty() ) + { + INetURLObject aINetURL( sFileName ); + if ( INetProtocol::File == aINetURL.GetProtocol() ) + sFileName = aINetURL.PathToFileName(); + } + m_xUrlED->set_text(sFileName); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/fmtui/tmpdlg.cxx b/sw/source/ui/fmtui/tmpdlg.cxx new file mode 100644 index 000000000..c040ad925 --- /dev/null +++ b/sw/source/ui/fmtui/tmpdlg.cxx @@ -0,0 +1,522 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/viewfrm.hxx> +#include <svx/hdft.hxx> +#include <editeng/flstitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/sfxdlg.hxx> +#include <svtools/htmlcfg.hxx> +#include <svl/cjkoptions.hxx> +#include <vcl/svapp.hxx> +#include <numpara.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <wdocsh.hxx> +#include <viewopt.hxx> +#include <pgfnote.hxx> +#include <pggrid.hxx> +#include <tmpdlg.hxx> +#include <column.hxx> +#include <drpcps.hxx> +#include <frmpage.hxx> +#include <wrap.hxx> +#include <swuiccoll.hxx> +#include <docstyle.hxx> +#include <fmtcol.hxx> +#include <macassgn.hxx> +#include <poolfmt.hxx> +#include <uitool.hxx> +#include <shellres.hxx> + +#include <cmdid.h> +#include <SwStyleNameMapper.hxx> +#include <svl/stritem.hxx> +#include <svl/slstitm.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> + +// the dialog's carrier +SwTemplateDlgController::SwTemplateDlgController(weld::Window* pParent, + SfxStyleSheetBase& rBase, + SfxStyleFamily nRegion, + const OString& sPage, + SwWrtShell* pActShell, + bool bNew) + : SfxStyleDialogController(pParent, + "modules/swriter/ui/templatedialog" + + OUString::number(static_cast<sal_uInt16>(nRegion)) + ".ui", + "TemplateDialog" + OString::number(static_cast<sal_uInt16>(nRegion)), + rBase) + , nType(nRegion) + , pWrtShell(pActShell) + , bNewStyle(bNew) +{ + nHtmlMode = ::GetHtmlMode(pWrtShell->GetView().GetDocShell()); + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + // stitch TabPages together + switch( nRegion ) + { + // character styles + case SfxStyleFamily::Char: + { + AddTabPage("font", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_NAME )); + AddTabPage("fonteffect", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_EFFECTS )); + AddTabPage("position", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_POSITION ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_POSITION )); + AddTabPage("asianlayout", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_TWOLINES ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_TWOLINES )); + AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BKG )); + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER )); + SvtCJKOptions aCJKOptions; + if(nHtmlMode & HTMLMODE_ON || !aCJKOptions.IsDoubleLinesEnabled()) + RemoveTabPage("asianlayout"); + } + break; + // paragraph styles + case SfxStyleFamily::Para: + { + AddTabPage("indents", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_STD_PARAGRAPH), pFact->GetTabPageRangesFunc(RID_SVXPAGE_STD_PARAGRAPH)); + + AddTabPage("alignment", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_ALIGN_PARAGRAPH), pFact->GetTabPageRangesFunc(RID_SVXPAGE_ALIGN_PARAGRAPH)); + + AddTabPage("textflow", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_EXT_PARAGRAPH), pFact->GetTabPageRangesFunc(RID_SVXPAGE_EXT_PARAGRAPH) ); + + AddTabPage("asiantypo", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), pFact->GetTabPageRangesFunc(RID_SVXPAGE_PARA_ASIAN) ); + + AddTabPage("font", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_NAME ) ); + + AddTabPage("fonteffect", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_EFFECTS ) ); + + AddTabPage("position", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_POSITION ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_POSITION ) ); + + AddTabPage("asianlayout", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_TWOLINES ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_TWOLINES ) ); + + AddTabPage("highlighting", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BKG )); + + AddTabPage("tabs", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_TABULATOR), pFact->GetTabPageRangesFunc(RID_SVXPAGE_TABULATOR) ); + + AddTabPage("outline", SwParagraphNumTabPage::Create, SwParagraphNumTabPage::GetRanges); + AddTabPage("dropcaps", SwDropCapsPage::Create, SwDropCapsPage::GetRanges ); + + // add Area and Transparence TabPages + AddTabPage("area", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_AREA )); + AddTabPage("transparence", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_TRANSPARENCE ) ); + + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ) ); + + AddTabPage("condition", SwCondCollPage::Create, + SwCondCollPage::GetRanges ); + if( (!bNewStyle && RES_CONDTXTFMTCOLL != static_cast<SwDocStyleSheet&>(rBase).GetCollection()->Which()) + || nHtmlMode & HTMLMODE_ON ) + RemoveTabPage("condition"); + + SvtCJKOptions aCJKOptions; + if(nHtmlMode & HTMLMODE_ON) + { + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + if (!rHtmlOpt.IsPrintLayoutExtension()) + RemoveTabPage("textflow"); + RemoveTabPage("asiantypo"); + RemoveTabPage("tabs"); + RemoveTabPage("outline"); + RemoveTabPage("asianlayout"); + if(!(nHtmlMode & HTMLMODE_FULL_STYLES)) + { + RemoveTabPage("background"); + RemoveTabPage("dropcaps"); + } + } + else + { + if(!aCJKOptions.IsAsianTypographyEnabled()) + RemoveTabPage("asiantypo"); + if(!aCJKOptions.IsDoubleLinesEnabled()) + RemoveTabPage("asianlayout"); + } + } + break; + // page styles + case SfxStyleFamily::Page: + { + // add Area and Transparence TabPages + AddTabPage("area", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_AREA )); + AddTabPage("transparence", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_TRANSPARENCE ) ); + AddTabPage("header", SvxHeaderPage::Create, SvxHeaderPage::GetRanges); + AddTabPage("footer", SvxFooterPage::Create, SvxFooterPage::GetRanges); + AddTabPage("page", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PAGE), pFact->GetTabPageRangesFunc(RID_SVXPAGE_PAGE)); + if (0 == ::GetHtmlMode(pWrtShell->GetView().GetDocShell())) + { + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ) ); + AddTabPage("columns", SwColumnPage::Create, SwColumnPage::GetRanges ); + AddTabPage("footnotes", SwFootNotePage::Create, SwFootNotePage::GetRanges ); + AddTabPage("textgrid", SwTextGridPage::Create, SwTextGridPage::GetRanges ); + SvtCJKOptions aCJKOptions; + if(!aCJKOptions.IsAsianTypographyEnabled()) + RemoveTabPage("textgrid"); + } + else + { + RemoveTabPage("borders"); + RemoveTabPage("columns"); + RemoveTabPage("footnotes"); + RemoveTabPage("textgrid"); + } + } + break; + // numbering styles + case SfxStyleFamily::Pseudo: + { + AddTabPage("numbering", RID_SVXPAGE_PICK_SINGLE_NUM); + AddTabPage("bullets", RID_SVXPAGE_PICK_BULLET); + AddTabPage("outline", RID_SVXPAGE_PICK_NUM); + AddTabPage("graphics", RID_SVXPAGE_PICK_BMP); + AddTabPage("customize", RID_SVXPAGE_NUM_OPTIONS ); + AddTabPage("position", RID_SVXPAGE_NUM_POSITION ); + } + break; + case SfxStyleFamily::Frame: + { + AddTabPage("type", SwFramePage::Create, SwFramePage::GetRanges); + AddTabPage("options", SwFrameAddPage::Create, SwFrameAddPage::GetRanges); + AddTabPage("wrap", SwWrapTabPage::Create, SwWrapTabPage::GetRanges); + + // add Area and Transparence TabPages + AddTabPage("area", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_AREA )); + AddTabPage("transparence", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_TRANSPARENCE ) ); + + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ) ); + + AddTabPage("columns", SwColumnPage::Create, SwColumnPage::GetRanges ); + + AddTabPage("macros", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_MACROASSIGN), nullptr); + } + break; + default: + OSL_ENSURE(false, "wrong family"); + } + + if (bNew) + SetCurPageId("organizer"); + else if (!sPage.isEmpty()) + SetCurPageId(sPage); +} + +short SwTemplateDlgController::Ok() +{ + short nRet = SfxTabDialogController::Ok(); + if( RET_OK == nRet ) + { + const SfxPoolItem *pOutItem, *pExItem; + if( SfxItemState::SET == m_xExampleSet->GetItemState( + SID_ATTR_NUMBERING_RULE, false, &pExItem ) && + ( !GetOutputItemSet() || + SfxItemState::SET != GetOutputItemSet()->GetItemState( + SID_ATTR_NUMBERING_RULE, false, &pOutItem ) || + *pExItem != *pOutItem )) + { + if( GetOutputItemSet() ) + const_cast<SfxItemSet*>(GetOutputItemSet())->Put( *pExItem ); + else + nRet = RET_CANCEL; + } + } + else + { + //JP 09.01.98 Bug #46446#: + // that's the Ok-Handler, so OK has to be default! + nRet = RET_OK; + } + return nRet; +} + +void SwTemplateDlgController::RefreshInputSet() +{ + SfxItemSet* pInSet = GetInputSetImpl(); + pInSet->ClearItem(); + pInSet->SetParent( &GetStyleSheet().GetItemSet() ); +} + +void SwTemplateDlgController::PageCreated(const OString& rId, SfxTabPage &rPage ) +{ + // set style's and metric's names + OUString sNumCharFormat, sBulletCharFormat; + SwStyleNameMapper::FillUIName( RES_POOLCHR_NUM_LEVEL, sNumCharFormat); + SwStyleNameMapper::FillUIName( RES_POOLCHR_BULLET_LEVEL, sBulletCharFormat); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + + if (rId == "font") + { + OSL_ENSURE(::GetActiveView(), "no active view"); + + SvxFontListItem aFontListItem( *static_cast<const SvxFontListItem*>(::GetActiveView()-> + GetDocShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) ) ); + + aSet.Put (SvxFontListItem( aFontListItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + sal_uInt32 nFlags = 0; + if(rPage.GetItemSet().GetParent() && 0 == (nHtmlMode & HTMLMODE_ON )) + nFlags = SVX_RELATIVE_MODE; + if( SfxStyleFamily::Char == nType ) + nFlags = nFlags|SVX_PREVIEW_CHARACTER; + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, nFlags)); + rPage.PageCreated(aSet); + } + else if (rId == "fonteffect") + { + sal_uInt32 nFlags = SVX_ENABLE_FLASH | SVX_ENABLE_CHAR_TRANSPARENCY; + if( SfxStyleFamily::Char == nType ) + nFlags = nFlags|SVX_PREVIEW_CHARACTER; + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, nFlags)); + rPage.PageCreated(aSet); + } + else if (rId == "position") + { + if( SfxStyleFamily::Char == nType ) + { + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, SVX_PREVIEW_CHARACTER)); + rPage.PageCreated(aSet); + } + else if (SfxStyleFamily::Pseudo == nType) + { + SwDocShell* pDocShell = ::GetActiveWrtShell()->GetView().GetDocShell(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebDocShell*>( pDocShell) != nullptr ); + + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM, static_cast< sal_uInt16 >(eMetric))); + rPage.PageCreated(aSet); + } + } + else if (rId == "columns") + { + if( nType == SfxStyleFamily::Frame ) + static_cast<SwColumnPage&>(rPage).SetFrameMode(true); + static_cast<SwColumnPage&>(rPage).SetFormatUsed( true ); + } + // do not remove; many other style dialog combinations still use the SfxTabPage + // for the SvxBrushItem (see RID_SVXPAGE_BKG) + else if (rId == "background" || rId == "highlighting") + { + SvxBackgroundTabFlags nFlagType = SvxBackgroundTabFlags::NONE; + if( SfxStyleFamily::Char == nType || SfxStyleFamily::Para == nType ) + nFlagType |= SvxBackgroundTabFlags::SHOW_HIGHLIGHTING; + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(nFlagType))); + rPage.PageCreated(aSet); + } + else if (rId == "condition") + { + static_cast<SwCondCollPage&>(rPage).SetCollection( + static_cast<SwDocStyleSheet&>(GetStyleSheet()).GetCollection(), bNewStyle ); + } + else if (rId == "page") + { + if(0 == (nHtmlMode & HTMLMODE_ON )) + { + std::vector<OUString> aList; + OUString aNew; + SwStyleNameMapper::FillUIName( RES_POOLCOLL_TEXT, aNew ); + aList.push_back( aNew ); + if( pWrtShell ) + { + SfxStyleSheetBasePool* pStyleSheetPool = pWrtShell-> + GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase *pFirstStyle = pStyleSheetPool->First(SfxStyleFamily::Para); + while(pFirstStyle) + { + aList.push_back( pFirstStyle->GetName() ); + pFirstStyle = pStyleSheetPool->Next(); + } + } + // set DrawingLayer FillStyles active + aSet.Put(SfxBoolItem(SID_DRAWINGLAYER_FILLSTYLES, true)); + aSet.Put(SfxStringListItem(SID_COLLECT_LIST, &aList)); + rPage.PageCreated(aSet); + } + } + else if (rId == "header") + { + if(0 == (nHtmlMode & HTMLMODE_ON )) + { + static_cast<SvxHeaderPage&>(rPage).EnableDynamicSpacing(); + } + + // set DrawingLayer FillStyles active + aSet.Put(SfxBoolItem(SID_DRAWINGLAYER_FILLSTYLES, true)); + rPage.PageCreated(aSet); + } + else if (rId == "footer") + { + if(0 == (nHtmlMode & HTMLMODE_ON )) + { + static_cast<SvxFooterPage&>(rPage).EnableDynamicSpacing(); + } + + // set DrawingLayer FillStyles active + aSet.Put(SfxBoolItem(SID_DRAWINGLAYER_FILLSTYLES, true)); + rPage.PageCreated(aSet); + } + else if (rId == "border") + { + if( SfxStyleFamily::Para == nType ) + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::PARA))); + } + else if( SfxStyleFamily::Frame == nType ) + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::FRAME))); + } + rPage.PageCreated(aSet); + } + else if (rId == "borders") + { + if( SfxStyleFamily::Para == nType ) + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::PARA))); + } + else if( SfxStyleFamily::Frame == nType ) + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::FRAME))); + } + rPage.PageCreated(aSet); + } + // inits for Area and Transparency TabPages + // The selection attribute lists (XPropertyList derivates, e.g. XColorList for + // the color table) need to be added as items (e.g. SvxColorTableItem) to make + // these pages find the needed attributes for fill style suggestions. + // These are added in SwDocStyleSheet::GetItemSet() for the SfxStyleFamily::Para on + // demand, but could also be directly added from the DrawModel. + else if (rId == "area") + { + aSet.Put(GetStyleSheet().GetItemSet()); + + // add flag for direct graphic content selection + aSet.Put(SfxBoolItem(SID_OFFER_IMPORT, true)); + + rPage.PageCreated(aSet); + } + else if (rId == "transparence") + { + rPage.PageCreated(GetStyleSheet().GetItemSet()); + } + else if (rId == "bullets") + { + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + rPage.PageCreated(aSet); + } + else if (rId == "outline") + { + if (SfxStyleFamily::Pseudo == nType) + { + aSet.Put (SfxStringItem(SID_NUM_CHAR_FMT,sNumCharFormat)); + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + rPage.PageCreated(aSet); + } + else if (SfxStyleFamily::Para == nType) + { + // handle if the current paragraph style is assigned to a list level of outline style, + SwTextFormatColl* pTmpColl = pWrtShell->FindTextFormatCollByName( GetStyleSheet().GetName() ); + if( pTmpColl && pTmpColl->IsAssignedToListLevelOfOutlineStyle() ) + { + static_cast<SwParagraphNumTabPage&>(rPage).DisableOutline() ; + static_cast<SwParagraphNumTabPage&>(rPage).DisableNumbering(); + }//<-end + weld::ComboBox& rBox = static_cast<SwParagraphNumTabPage&>(rPage).GetStyleBox(); + SfxStyleSheetBasePool* pPool = pWrtShell->GetView().GetDocShell()->GetStyleSheetPool(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Pseudo); + std::set<OUString> aNames; + while(pBase) + { + aNames.insert(pBase->GetName()); + pBase = pPool->Next(); + } + for(std::set<OUString>::const_iterator it = aNames.begin(); it != aNames.end(); ++it) + rBox.append_text(*it); + } + } + else if (rId == "customize") + { + aSet.Put (SfxStringItem(SID_NUM_CHAR_FMT,sNumCharFormat)); + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + + // collect character styles + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/comboboxfragment.ui")); + std::unique_ptr<weld::ComboBox> xCharFormatLB(xBuilder->weld_combo_box("combobox")); + xCharFormatLB->clear(); + xCharFormatLB->append_text(SwViewShell::GetShellRes()->aStrNone); + SwDocShell* pDocShell = ::GetActiveWrtShell()->GetView().GetDocShell(); + ::FillCharStyleListBox(*xCharFormatLB, pDocShell); + + std::vector<OUString> aList; + aList.reserve(xCharFormatLB->get_count()); + for (sal_Int32 j = 0; j < xCharFormatLB->get_count(); j++) + aList.push_back(xCharFormatLB->get_text(j)); + + aSet.Put( SfxStringListItem( SID_CHAR_FMT_LIST_BOX,&aList ) ) ; + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast< const SwWebDocShell *>( pDocShell ) != nullptr); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM, static_cast< sal_uInt16 >(eMetric))); + rPage.PageCreated(aSet); + } + else if (rId == "indents") + { + if( rPage.GetItemSet().GetParent() ) + { + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST,MM50/10)); + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_FLAGSET,0x000F)); + rPage.PageCreated(aSet); + } + } + else if (rId == "alignment") + { + aSet.Put(SfxBoolItem(SID_SVXPARAALIGNTABPAGE_ENABLEJUSTIFYEXT,true)); + rPage.PageCreated(aSet); + } + else if (rId == "asianlayout") + { + if( SfxStyleFamily::Char == nType ) + { + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, SVX_PREVIEW_CHARACTER)); + rPage.PageCreated(aSet); + } + } + else if (rId == "type") + { + static_cast<SwFramePage&>(rPage).SetNewFrame( true ); + static_cast<SwFramePage&>(rPage).SetFormatUsed( true ); + } + else if (rId == "options") + { + static_cast<SwFrameAddPage&>(rPage).SetFormatUsed(true); + static_cast<SwFrameAddPage&>(rPage).SetNewFrame(true); + } + else if (rId == "wrap") + { + static_cast<SwWrapTabPage&>(rPage).SetFormatUsed( true, false ); + } + else if (rId == "macros") + { + SfxAllItemSet aNewSet(*aSet.GetPool()); + aNewSet.Put( SwMacroAssignDlg::AddEvents(MACASSGN_ALLFRM) ); + if ( pWrtShell ) + rPage.SetFrame( pWrtShell->GetView().GetViewFrame()->GetFrame().GetFrameInterface() ); + rPage.PageCreated(aNewSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/column.cxx b/sw/source/ui/frmdlg/column.cxx new file mode 100644 index 000000000..343ce664a --- /dev/null +++ b/sw/source/ui/frmdlg/column.cxx @@ -0,0 +1,1387 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <column.hxx> + +#include <hintids.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <sfx2/htmlmode.hxx> +#include <svx/colorbox.hxx> +#include <editeng/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/ulspitem.hxx> +#include <svl/ctloptions.hxx> +#include <svl/itemset.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/event.hxx> +#include <vcl/fieldvalues.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <swmodule.hxx> + +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <viewopt.hxx> +#include <fmtclbl.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <colmgr.hxx> +#include <prcntfld.hxx> +#include <strings.hrc> +#include <section.hxx> +#include <pagedesc.hxx> + +//to match associated data in ColumnPage.ui +#define LISTBOX_SELECTION 0 +#define LISTBOX_SECTION 1 +#define LISTBOX_SECTIONS 2 +#define LISTBOX_PAGE 3 +#define LISTBOX_FRAME 4 + +using namespace ::com::sun::star; + +#define FRAME_FORMAT_WIDTH 1000 + +// static data +static const sal_uInt16 nVisCols = 3; + +static bool IsMarkInSameSection( SwWrtShell& rWrtSh, const SwSection* pSect ) +{ + rWrtSh.SwapPam(); + bool bRet = pSect == rWrtSh.GetCurrSection(); + rWrtSh.SwapPam(); + return bRet; +} + +SwColumnDlg::SwColumnDlg(weld::Window* pParent, SwWrtShell& rSh) + : SfxDialogController(pParent, "modules/swriter/ui/columndialog.ui", "ColumnDialog") + , m_rWrtShell(rSh) + , m_pFrameSet(nullptr) + , m_nOldSelection(0) + , m_nSelectionWidth(0) + , m_bPageChanged(false) + , m_bSectionChanged(false) + , m_bSelSectionChanged(false) + , m_bFrameChanged(false) + , m_xContentArea(m_xDialog->weld_content_area()) + , m_xOkButton(m_xBuilder->weld_button("ok")) +{ + SwRect aRect; + m_rWrtShell.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + + m_nSelectionWidth = aRect.Width(); + + SfxItemSet* pColPgSet = nullptr; + static sal_uInt16 const aSectIds[] = { RES_COL, RES_COL, + RES_FRM_SIZE, RES_FRM_SIZE, + RES_COLUMNBALANCE, RES_FRAMEDIR, + 0 }; + + const SwSection* pCurrSection = m_rWrtShell.GetCurrSection(); + const sal_uInt16 nFullSectCnt = m_rWrtShell.GetFullSelectedSectionCount(); + if( pCurrSection && ( !m_rWrtShell.HasSelection() || 0 != nFullSectCnt )) + { + m_nSelectionWidth = rSh.GetSectionWidth(*pCurrSection->GetFormat()); + if ( !m_nSelectionWidth ) + m_nSelectionWidth = USHRT_MAX; + m_pSectionSet.reset( new SfxItemSet( m_rWrtShell.GetAttrPool(), aSectIds ) ); + m_pSectionSet->Put( pCurrSection->GetFormat()->GetAttrSet() ); + pColPgSet = m_pSectionSet.get(); + } + + if( m_rWrtShell.HasSelection() && m_rWrtShell.IsInsRegionAvailable() && + ( !pCurrSection || ( 1 != nFullSectCnt && + IsMarkInSameSection( m_rWrtShell, pCurrSection ) ))) + { + m_pSelectionSet.reset( new SfxItemSet( m_rWrtShell.GetAttrPool(), aSectIds ) ); + pColPgSet = m_pSelectionSet.get(); + } + + if( m_rWrtShell.GetFlyFrameFormat() ) + { + const SwFrameFormat* pFormat = rSh.GetFlyFrameFormat() ; + m_pFrameSet = new SfxItemSet(m_rWrtShell.GetAttrPool(), aSectIds ); + m_pFrameSet->Put(pFormat->GetFrameSize()); + m_pFrameSet->Put(pFormat->GetCol()); + pColPgSet = m_pFrameSet; + } + + const SwPageDesc* pPageDesc = m_rWrtShell.GetSelectedPageDescs(); + if( pPageDesc ) + { + m_pPageSet.reset( new SfxItemSet( + m_rWrtShell.GetAttrPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_COL, RES_COL>{}) ); + + const SwFrameFormat &rFormat = pPageDesc->GetMaster(); + m_nPageWidth = rFormat.GetFrameSize().GetSize().Width(); + + const SvxLRSpaceItem& rLRSpace = rFormat.GetLRSpace(); + const SvxBoxItem& rBox = rFormat.GetBox(); + m_nPageWidth -= rLRSpace.GetLeft() + rLRSpace.GetRight() + rBox.GetSmallestDistance(); + + m_pPageSet->Put(rFormat.GetCol()); + m_pPageSet->Put(rFormat.GetLRSpace()); + pColPgSet = m_pPageSet.get(); + } + + assert(pColPgSet); + + // create TabPage + m_xTabPage = std::make_unique<SwColumnPage>(m_xContentArea.get(), this, *pColPgSet); + m_xTabPage->GetApplyLabel()->show(); + weld::ComboBox* pApplyToLB = m_xTabPage->GetApplyComboBox(); + pApplyToLB->show(); + + if (pCurrSection && (!m_rWrtShell.HasSelection() || 0 != nFullSectCnt)) + { + pApplyToLB->remove_id(1 >= nFullSectCnt ? OUString::number(LISTBOX_SECTIONS) : OUString::number(LISTBOX_SECTION)); + } + else + { + pApplyToLB->remove_id(OUString::number(LISTBOX_SECTION)); + pApplyToLB->remove_id(OUString::number(LISTBOX_SECTIONS)); + } + + if (!( m_rWrtShell.HasSelection() && m_rWrtShell.IsInsRegionAvailable() && + ( !pCurrSection || ( 1 != nFullSectCnt && + IsMarkInSameSection( m_rWrtShell, pCurrSection ) )))) + pApplyToLB->remove_id(OUString::number(LISTBOX_SELECTION)); + + if (!m_rWrtShell.GetFlyFrameFormat()) + pApplyToLB->remove_id(OUString::number(LISTBOX_FRAME)); + + const int nPagePos = pApplyToLB->find_id(OUString::number(LISTBOX_PAGE)); + if (m_pPageSet && pPageDesc) + { + const OUString sPageStr = pApplyToLB->get_text(nPagePos) + pPageDesc->GetName(); + pApplyToLB->remove(nPagePos); + OUString sId(OUString::number(LISTBOX_PAGE)); + pApplyToLB->insert(nPagePos, sPageStr, &sId, nullptr, nullptr); + } + else + pApplyToLB->remove( nPagePos ); + + pApplyToLB->set_active(0); + ObjectHdl(nullptr); + + pApplyToLB->connect_changed(LINK(this, SwColumnDlg, ObjectListBoxHdl)); + m_xOkButton->connect_clicked(LINK(this, SwColumnDlg, OkHdl)); + //#i80458# if no columns can be set then disable OK + if (!pApplyToLB->get_count()) + m_xOkButton->set_sensitive(false); + //#i97810# set focus to the TabPage + m_xTabPage->ActivateColumnControl(); +} + +SwColumnDlg::~SwColumnDlg() +{ + m_xTabPage.reset(); +} + +IMPL_LINK(SwColumnDlg, ObjectListBoxHdl, weld::ComboBox&, rBox, void) +{ + ObjectHdl(&rBox); +} + +void SwColumnDlg::ObjectHdl(const weld::ComboBox* pBox) +{ + SfxItemSet* pSet = EvalCurrentSelection(); + + if (pBox) + { + m_xTabPage->FillItemSet(pSet); + } + weld::ComboBox* pApplyToLB = m_xTabPage->GetApplyComboBox(); + m_nOldSelection = pApplyToLB->get_active_id().toInt32(); + long nWidth = m_nSelectionWidth; + switch(m_nOldSelection) + { + case LISTBOX_SELECTION : + pSet = m_pSelectionSet.get(); + if( m_pSelectionSet ) + pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth)); + break; + case LISTBOX_SECTION : + case LISTBOX_SECTIONS : + pSet = m_pSectionSet.get(); + pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth)); + break; + case LISTBOX_PAGE : + nWidth = m_nPageWidth; + pSet = m_pPageSet.get(); + pSet->Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth, nWidth)); + break; + case LISTBOX_FRAME: + pSet = m_pFrameSet; + break; + } + + bool bIsSection = pSet == m_pSectionSet.get() || pSet == m_pSelectionSet.get(); + m_xTabPage->ShowBalance(bIsSection); + m_xTabPage->SetInSection(bIsSection); + m_xTabPage->SetFrameMode(true); + m_xTabPage->SetPageWidth(nWidth); + if( pSet ) + m_xTabPage->Reset(pSet); +} + +IMPL_LINK_NOARG(SwColumnDlg, OkHdl, weld::Button&, void) +{ + // evaluate current selection + SfxItemSet* pSet = EvalCurrentSelection(); + m_xTabPage->FillItemSet(pSet); + + if(m_pSelectionSet && SfxItemState::SET == m_pSelectionSet->GetItemState(RES_COL)) + { + //insert region with columns + const SwFormatCol& rColItem = m_pSelectionSet->Get(RES_COL); + //only if there actually are columns! + if(rColItem.GetNumCols() > 1) + m_rWrtShell.GetView().GetViewFrame()->GetDispatcher()->Execute( + FN_INSERT_REGION, SfxCallMode::ASYNCHRON, *m_pSelectionSet ); + } + + if(m_pSectionSet && m_pSectionSet->Count() && m_bSectionChanged ) + { + const SwSection* pCurrSection = m_rWrtShell.GetCurrSection(); + const SwSectionFormat* pFormat = pCurrSection->GetFormat(); + const size_t nNewPos = m_rWrtShell.GetSectionFormatPos( *pFormat ); + SwSectionData aData(*pCurrSection); + m_rWrtShell.UpdateSection( nNewPos, aData, m_pSectionSet.get() ); + } + + if(m_pSectionSet && m_pSectionSet->Count() && m_bSelSectionChanged ) + { + m_rWrtShell.SetSectionAttr( *m_pSectionSet ); + } + + if(m_pPageSet && SfxItemState::SET == m_pPageSet->GetItemState(RES_COL) && m_bPageChanged) + { + // determine current PageDescriptor and fill the Set with it + const size_t nCurIdx = m_rWrtShell.GetCurPageDesc(); + SwPageDesc aPageDesc(m_rWrtShell.GetPageDesc(nCurIdx)); + SwFrameFormat &rFormat = aPageDesc.GetMaster(); + rFormat.SetFormatAttr(m_pPageSet->Get(RES_COL)); + m_rWrtShell.ChgPageDesc(nCurIdx, aPageDesc); + } + if(m_pFrameSet && SfxItemState::SET == m_pFrameSet->GetItemState(RES_COL) && m_bFrameChanged) + { + SfxItemSet aTmp(*m_pFrameSet->GetPool(), svl::Items<RES_COL, RES_COL>{}); + aTmp.Put(*m_pFrameSet); + m_rWrtShell.StartAction(); + m_rWrtShell.Push(); + m_rWrtShell.SetFlyFrameAttr( aTmp ); + // undo the frame selection again + if(m_rWrtShell.IsFrameSelected()) + { + m_rWrtShell.UnSelectFrame(); + m_rWrtShell.LeaveSelFrameMode(); + } + m_rWrtShell.Pop(); + m_rWrtShell.EndAction(); + } + m_xDialog->response(RET_OK); +} + +SfxItemSet* SwColumnDlg::EvalCurrentSelection() +{ + SfxItemSet* pSet = nullptr; + + switch(m_nOldSelection) + { + case LISTBOX_SELECTION : + pSet = m_pSelectionSet.get(); + break; + case LISTBOX_SECTION : + pSet = m_pSectionSet.get(); + m_bSectionChanged = true; + break; + case LISTBOX_SECTIONS : + pSet = m_pSectionSet.get(); + m_bSelSectionChanged = true; + break; + case LISTBOX_PAGE : + pSet = m_pPageSet.get(); + m_bPageChanged = true; + break; + case LISTBOX_FRAME: + pSet = m_pFrameSet; + m_bFrameChanged = true; + break; + } + + return pSet; +} + +static +sal_uInt16 GetMaxWidth( SwColMgr const * pColMgr, sal_uInt16 nCols ) +{ + sal_uInt16 nMax = pColMgr->GetActualSize(); + if( --nCols ) + nMax -= pColMgr->GetGutterWidth() * nCols; + return nMax; +} + +const sal_uInt16 SwColumnPage::aPageRg[] = { + RES_COL, RES_COL, + 0 +}; + +void SwColumnPage::ResetColWidth() +{ + if( m_nCols ) + { + const sal_uInt16 nWidth = GetMaxWidth( m_xColMgr.get(), m_nCols ) / m_nCols; + + for(sal_uInt16 i = 0; i < m_nCols; ++i) + m_nColWidth[i] = static_cast<long>(nWidth); + } + +} + +constexpr sal_uInt16 g_nMinWidth(MINLAY); + +// Now as TabPage +SwColumnPage::SwColumnPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/columnpage.ui", "ColumnPage", &rSet) + , m_nFirstVis(0) + , m_pModifiedField(nullptr) + , m_bFormat(false) + , m_bFrame(false) + , m_bHtmlMode(false) + , m_bLockUpdate(false) + , m_xCLNrEdt(m_xBuilder->weld_spin_button("colsnf")) + , m_xBalanceColsCB(m_xBuilder->weld_check_button("balance")) + , m_xBtnBack(m_xBuilder->weld_button("back")) + , m_xLbl1(m_xBuilder->weld_label("1")) + , m_xLbl2(m_xBuilder->weld_label("2")) + , m_xLbl3(m_xBuilder->weld_label("3")) + , m_xBtnNext(m_xBuilder->weld_button("next")) + , m_xAutoWidthBox(m_xBuilder->weld_check_button("autowidth")) + , m_xLineTypeLbl(m_xBuilder->weld_label("linestyleft")) + , m_xLineWidthLbl(m_xBuilder->weld_label("linewidthft")) + , m_xLineWidthEdit(m_xBuilder->weld_metric_spin_button("linewidthmf", FieldUnit::POINT)) + , m_xLineColorLbl(m_xBuilder->weld_label("linecolorft")) + , m_xLineHeightLbl(m_xBuilder->weld_label("lineheightft")) + , m_xLineHeightEdit(m_xBuilder->weld_metric_spin_button("lineheightmf", FieldUnit::PERCENT)) + , m_xLinePosLbl(m_xBuilder->weld_label("lineposft")) + , m_xLinePosDLB(m_xBuilder->weld_combo_box("lineposlb")) + , m_xTextDirectionFT(m_xBuilder->weld_label("textdirectionft")) + , m_xTextDirectionLB(new svx::FrameDirectionListBox(m_xBuilder->weld_combo_box("textdirectionlb"))) + , m_xLineColorDLB(new ColorListBox(m_xBuilder->weld_menu_button("colorlb"), pController->getDialog())) + , m_xLineTypeDLB(new SvtLineListBox(m_xBuilder->weld_menu_button("linestylelb"))) + , m_xEd1(new SwPercentField(m_xBuilder->weld_metric_spin_button("width1mf", FieldUnit::CM))) + , m_xEd2(new SwPercentField(m_xBuilder->weld_metric_spin_button("width2mf", FieldUnit::CM))) + , m_xEd3(new SwPercentField(m_xBuilder->weld_metric_spin_button("width3mf", FieldUnit::CM))) + , m_xDistEd1(new SwPercentField(m_xBuilder->weld_metric_spin_button("spacing1mf", FieldUnit::CM))) + , m_xDistEd2(new SwPercentField(m_xBuilder->weld_metric_spin_button("spacing2mf", FieldUnit::CM))) + , m_xDefaultVS(new weld::CustomWeld(*m_xBuilder, "valueset", m_aDefaultVS)) + , m_xPgeExampleWN(new weld::CustomWeld(*m_xBuilder, "pageexample", m_aPgeExampleWN)) + , m_xFrameExampleWN(new weld::CustomWeld(*m_xBuilder, "frameexample", m_aFrameExampleWN)) + , m_xApplyToFT(m_xBuilder->weld_label("applytoft")) + , m_xApplyToLB(m_xBuilder->weld_combo_box("applytolb")) +{ + connectPercentField(*m_xEd1); + connectPercentField(*m_xEd2); + connectPercentField(*m_xEd3); + connectPercentField(*m_xDistEd1); + connectPercentField(*m_xDistEd2); + + m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_LR_TB, SvxResId(RID_SVXSTR_FRAMEDIR_LTR)); + m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_RL_TB, SvxResId(RID_SVXSTR_FRAMEDIR_RTL)); + m_xTextDirectionLB->append(SvxFrameDirection::Environment, SvxResId(RID_SVXSTR_FRAMEDIR_SUPER)); + + SetExchangeSupport(); + + m_aDefaultVS.SetColCount(5); + + for (int i = 0; i < 5; ++i) + //Set accessible name one by one + { + OUString aItemText; + switch( i ) + { + case 0: + aItemText = SwResId( STR_COLUMN_VALUESET_ITEM0 ) ; + break; + case 1: + aItemText = SwResId( STR_COLUMN_VALUESET_ITEM1 ) ; + break; + case 2: + aItemText = SwResId( STR_COLUMN_VALUESET_ITEM2 ) ; + break; + case 3: + aItemText = SwResId( STR_COLUMN_VALUESET_ITEM3 ); + break; + default: + aItemText = SwResId( STR_COLUMN_VALUESET_ITEM4 ); + break; + } + m_aDefaultVS.InsertItem( i + 1, aItemText, i ); + } + + m_aDefaultVS.SetSelectHdl(LINK(this, SwColumnPage, SetDefaultsHdl)); + + Link<weld::SpinButton&,void> aCLNrLk = LINK(this, SwColumnPage, ColModify); + m_xCLNrEdt->connect_value_changed(aCLNrLk); + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwColumnPage, GapModify); + m_xDistEd1->connect_value_changed(aLk); + m_xDistEd2->connect_value_changed(aLk); + + aLk = LINK(this, SwColumnPage, EdModify); + + m_xEd1->connect_value_changed(aLk); + m_xEd2->connect_value_changed(aLk); + m_xEd3->connect_value_changed(aLk); + + m_xBtnBack->connect_clicked(LINK(this, SwColumnPage, Up)); + m_xBtnNext->connect_clicked(LINK(this, SwColumnPage, Down)); + m_xAutoWidthBox->connect_toggled(LINK(this, SwColumnPage, AutoWidthHdl)); + + Link<weld::MetricSpinButton&,void> aLk2 = LINK( this, SwColumnPage, UpdateColMgr ); + m_xLineTypeDLB->SetSelectHdl(LINK(this, SwColumnPage, UpdateColMgrLineBox)); + m_xLineWidthEdit->connect_value_changed(aLk2); + m_xLineColorDLB->SetSelectHdl(LINK( this, SwColumnPage, UpdateColMgrColorBox)); + m_xLineHeightEdit->connect_value_changed(aLk2); + m_xLinePosDLB->connect_changed(LINK(this, SwColumnPage, UpdateColMgrListBox)); + + // Separator line + m_xLineTypeDLB->SetSourceUnit( FieldUnit::TWIP ); + + // Fill the line styles listbox + m_xLineTypeDLB->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::SOLID), + SvxBorderLineStyle::SOLID ); + m_xLineTypeDLB->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DOTTED), + SvxBorderLineStyle::DOTTED ); + m_xLineTypeDLB->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DASHED), + SvxBorderLineStyle::DASHED ); + + sal_Int64 nLineWidth = m_xLineWidthEdit->get_value(FieldUnit::POINT); + nLineWidth = static_cast<long>(vcl::ConvertDoubleValue( + nLineWidth, + m_xLineWidthEdit->get_digits(), + FieldUnit::POINT, MapUnit::MapTwip )); + m_xLineTypeDLB->SetWidth(nLineWidth); + m_xLineColorDLB->SelectEntry(COL_BLACK); +} + +SwColumnPage::~SwColumnPage() +{ + m_xFrameExampleWN.reset(); + m_xPgeExampleWN.reset(); + m_xDefaultVS.reset(); + m_xDistEd2.reset(); + m_xDistEd1.reset(); + m_xEd3.reset(); + m_xEd2.reset(); + m_xEd1.reset(); + m_xLineTypeDLB.reset(); + m_xLineColorDLB.reset(); + m_xTextDirectionLB.reset(); +} + +void SwColumnPage::SetPageWidth(long nPageWidth) +{ + long nNewMaxWidth = static_cast< long >(m_xEd1->NormalizePercent(nPageWidth)); + + m_xDistEd1->set_max(nNewMaxWidth, FieldUnit::TWIP); + m_xDistEd2->set_max(nNewMaxWidth, FieldUnit::TWIP); + m_xEd1->set_max(nNewMaxWidth, FieldUnit::TWIP); + m_xEd2->set_max(nNewMaxWidth, FieldUnit::TWIP); + m_xEd3->set_max(nNewMaxWidth, FieldUnit::TWIP); +} + +void SwColumnPage::connectPercentField(SwPercentField &rWrap) +{ + weld::MetricSpinButton *pField = rWrap.get(); + assert(pField); + m_aPercentFieldsMap[pField] = &rWrap; +} + +void SwColumnPage::Reset(const SfxItemSet *rSet) +{ + const sal_uInt16 nHtmlMode = + ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + if(nHtmlMode & HTMLMODE_ON) + { + m_bHtmlMode = true; + m_xAutoWidthBox->set_sensitive(false); + } + FieldUnit aMetric = ::GetDfltMetric(m_bHtmlMode); + m_xEd1->SetMetric(aMetric); + m_xEd2->SetMetric(aMetric); + m_xEd3->SetMetric(aMetric); + m_xDistEd1->SetMetric(aMetric); + m_xDistEd2->SetMetric(aMetric); + //default spacing between cols = 0.5cm + m_xDistEd1->set_value(50, FieldUnit::CM); + m_xDistEd2->set_value(50, FieldUnit::CM); + + m_xColMgr.reset(new SwColMgr(*rSet)); + m_nCols = m_xColMgr->GetCount() ; + m_xCLNrEdt->set_max(std::max(static_cast<sal_uInt16>(m_xCLNrEdt->get_max()), m_nCols)); + + if(m_bFrame) + { + if(m_bFormat) // there is no size here + m_xColMgr->SetActualWidth(FRAME_FORMAT_WIDTH); + else + { + const SwFormatFrameSize& rSize = rSet->Get(RES_FRM_SIZE); + const SvxBoxItem& rBox = rSet->Get(RES_BOX); + m_xColMgr->SetActualWidth(static_cast<sal_uInt16>(rSize.GetSize().Width()) - rBox.GetSmallestDistance()); + } + } + if (m_xBalanceColsCB->get_visible()) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet->GetItemState( RES_COLUMNBALANCE, false, &pItem )) + m_xBalanceColsCB->set_active(!static_cast<const SwFormatNoBalancedColumns*>(pItem)->GetValue()); + else + m_xBalanceColsCB->set_active(true); + } + + //text direction + if( SfxItemState::DEFAULT <= rSet->GetItemState( RES_FRAMEDIR ) ) + { + const SvxFrameDirectionItem& rItem = rSet->Get(RES_FRAMEDIR); + SvxFrameDirection nVal = rItem.GetValue(); + m_xTextDirectionLB->set_active_id(nVal); + m_xTextDirectionLB->save_value(); + } + + Init(); + ActivatePage( *rSet ); +} + +// create TabPage +std::unique_ptr<SfxTabPage> SwColumnPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwColumnPage>(pPage, pController, *rSet); +} + +// stuff attributes into the Set when OK +bool SwColumnPage::FillItemSet(SfxItemSet *rSet) +{ + // set in ItemSet; + // the current settings are already present + + const SfxPoolItem* pOldItem; + const SwFormatCol& rCol = m_xColMgr->GetColumns(); + if(nullptr == (pOldItem = GetOldItem( *rSet, RES_COL )) || + rCol != *pOldItem ) + rSet->Put(rCol); + + if (m_xBalanceColsCB->get_visible()) + { + rSet->Put(SwFormatNoBalancedColumns(!m_xBalanceColsCB->get_active())); + } + if (m_xTextDirectionLB->get_visible()) + { + if (m_xTextDirectionLB->get_value_changed_from_saved()) + { + rSet->Put(SvxFrameDirectionItem(m_xTextDirectionLB->get_active_id(), RES_FRAMEDIR) ); + } + } + return true; +} + +// update ColumnManager +IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrListBox, weld::ComboBox&, void ) +{ + UpdateColMgr(*m_xLineWidthEdit); +} + +IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrLineBox, SvtLineListBox&, void ) +{ + UpdateColMgr(*m_xLineWidthEdit); +} + +IMPL_LINK_NOARG( SwColumnPage, UpdateColMgrColorBox, ColorListBox&, void ) +{ + UpdateColMgr(*m_xLineWidthEdit); +} + +IMPL_LINK_NOARG( SwColumnPage, UpdateColMgr, weld::MetricSpinButton&, void ) +{ + if (!m_xColMgr) + return; + long nGutterWidth = m_xColMgr->GetGutterWidth(); + if (m_nCols > 1) + { + // Determine whether the most narrow column is too narrow + // for the adjusted column gap + long nMin = m_nColWidth[0]; + + for( sal_uInt16 i = 1; i < m_nCols; ++i ) + nMin = std::min(nMin, m_nColWidth[i]); + + bool bAutoWidth = m_xAutoWidthBox->get_active(); + if(!bAutoWidth) + { + m_xColMgr->SetAutoWidth(false); + // when the user didn't allocate the whole width, + // add the missing amount to the last column. + long nSum = 0; + for(sal_uInt16 i = 0; i < m_nCols; ++i) + nSum += m_nColWidth[i]; + nGutterWidth = 0; + for(sal_uInt16 i = 0; i < m_nCols - 1; ++i) + nGutterWidth += m_nColDist[i]; + nSum += nGutterWidth; + + long nMaxW = m_xColMgr->GetActualSize(); + + if( nSum < nMaxW ) + m_nColWidth[m_nCols - 1] += nMaxW - nSum; + + m_xColMgr->SetColWidth( 0, static_cast< sal_uInt16 >(m_nColWidth[0] + m_nColDist[0]/2) ); + for( sal_uInt16 i = 1; i < m_nCols-1; ++i ) + { + long nActDist = (m_nColDist[i] + m_nColDist[i - 1]) / 2; + m_xColMgr->SetColWidth( i, static_cast< sal_uInt16 >(m_nColWidth[i] + nActDist )); + } + m_xColMgr->SetColWidth( m_nCols-1, static_cast< sal_uInt16 >(m_nColWidth[m_nCols-1] + m_nColDist[m_nCols -2]/2) ); + + } + + bool bEnable = isLineNotNone(); + m_xLineHeightEdit->set_sensitive(bEnable); + m_xLineHeightLbl->set_sensitive(bEnable); + m_xLineWidthLbl->set_sensitive(bEnable); + m_xLineWidthEdit->set_sensitive(bEnable); + m_xLineColorDLB->set_sensitive(bEnable); + m_xLineColorLbl->set_sensitive(bEnable); + + sal_Int64 nLineWidth = m_xLineWidthEdit->get_value(FieldUnit::PERCENT); + nLineWidth = static_cast<long>(vcl::ConvertDoubleValue( + nLineWidth, + m_xLineWidthEdit->get_digits(), + m_xLineWidthEdit->get_unit(), MapUnit::MapTwip )); + if( !bEnable ) + m_xColMgr->SetNoLine(); + else + { + m_xColMgr->SetLineWidthAndColor( + m_xLineTypeDLB->GetSelectEntryStyle(), + nLineWidth, + m_xLineColorDLB->GetSelectEntryColor() ); + m_xColMgr->SetAdjust(SwColLineAdj(m_xLinePosDLB->get_active() + 1)); + m_xColMgr->SetLineHeightPercent(static_cast<short>(m_xLineHeightEdit->get_value(FieldUnit::PERCENT))); + bEnable = m_xColMgr->GetLineHeightPercent() != 100; + } + m_xLinePosLbl->set_sensitive(bEnable); + m_xLinePosDLB->set_sensitive(bEnable); + + //fdo#66815 if the values are going to be the same, don't update + //them to avoid the listbox selection resetting + if (nLineWidth != m_xLineTypeDLB->GetWidth()) + m_xLineTypeDLB->SetWidth(nLineWidth); + Color aColor(m_xLineColorDLB->GetSelectEntryColor()); + if (aColor != m_xLineTypeDLB->GetColor()) + m_xLineTypeDLB->SetColor(aColor); + } + else + { + m_xColMgr->NoCols(); + m_nCols = 0; + } + + //set maximum values + m_xCLNrEdt->set_max(std::max(1L, + std::min(long(nMaxCols), long( m_xColMgr->GetActualSize() / (nGutterWidth + MINLAY)) ))); + + //prompt example window + if(!m_bLockUpdate) + { + if(m_bFrame) + { + m_aFrameExampleWN.SetColumns(m_xColMgr->GetColumns()); + m_aFrameExampleWN.Invalidate(); + } + else + m_aPgeExampleWN.Invalidate(); + } +} + +void SwColumnPage::Init() +{ + m_xCLNrEdt->set_value(m_nCols); + + bool bAutoWidth = m_xColMgr->IsAutoWidth() || m_bHtmlMode; + m_xAutoWidthBox->set_active(bAutoWidth); + + sal_Int32 nColumnWidthSum = 0; + // set the widths + for(sal_uInt16 i = 0; i < m_nCols; ++i) + { + m_nColWidth[i] = m_xColMgr->GetColWidth(i); + nColumnWidthSum += m_nColWidth[i]; + if(i < m_nCols - 1) + m_nColDist[i] = m_xColMgr->GetGutterWidth(i); + } + + if( 1 < m_nCols ) + { + // #97495# make sure that the automatic column width's are always equal + if(bAutoWidth) + { + nColumnWidthSum /= m_nCols; + for(sal_uInt16 i = 0; i < m_nCols; ++i) + m_nColWidth[i] = nColumnWidthSum; + } + SwColLineAdj eAdj = m_xColMgr->GetAdjust(); + if( COLADJ_NONE == eAdj ) // the dialog doesn't know a NONE! + { + eAdj = COLADJ_TOP; + //without Adjust no line type + m_xLineTypeDLB->SelectEntry(SvxBorderLineStyle::NONE); + m_xLineHeightEdit->set_value(100, FieldUnit::PERCENT); + } + else + { + // Need to multiply by 100 because of the 2 decimals + m_xLineWidthEdit->set_value( m_xColMgr->GetLineWidth() * 100, FieldUnit::TWIP); + m_xLineColorDLB->SelectEntry( m_xColMgr->GetLineColor() ); + m_xLineTypeDLB->SelectEntry( m_xColMgr->GetLineStyle() ); + m_xLineTypeDLB->SetWidth( m_xColMgr->GetLineWidth( ) ); + m_xLineHeightEdit->set_value(m_xColMgr->GetLineHeightPercent(), FieldUnit::PERCENT); + + } + m_xLinePosDLB->set_active( static_cast< sal_Int32 >(eAdj - 1) ); + } + else + { + m_xLinePosDLB->set_active(0); + m_xLineTypeDLB->SelectEntry(SvxBorderLineStyle::NONE); + m_xLineHeightEdit->set_value(100, FieldUnit::PERCENT); + } + + UpdateCols(); + Update(nullptr); + + // set maximum number of columns + // values below 1 are not allowed + m_xCLNrEdt->set_max(std::max(1L, + std::min(long(nMaxCols), long( m_xColMgr->GetActualSize() / g_nMinWidth) ))); +} + +bool SwColumnPage::isLineNotNone() const +{ + // nothing is turned off + return m_xLineTypeDLB->GetSelectEntryStyle() != SvxBorderLineStyle::NONE; +} + +/* + * The number of columns has changed -- here the controls for editing of the + * columns are en- or disabled according to the column number. In case there are + * more than nVisCols (=3) all Edit are being enabled and the buttons for + * scrolling too. Otherwise Edits are being enabled according to the column + * numbers; one column can not be edited. + */ +void SwColumnPage::UpdateCols() +{ + bool bEnableBtns= false; + bool bEnable12 = false; + bool bEnable3 = false; + const bool bEdit = !m_xAutoWidthBox->get_active(); + if ( m_nCols > nVisCols ) + { + bEnableBtns = !m_bHtmlMode; + bEnable12 = bEnable3 = bEdit; + } + else if( bEdit ) + { + // here are purposely hardly any breaks + switch(m_nCols) + { + case 3: bEnable3 = true; + [[fallthrough]]; + case 2: bEnable12= true; break; + default: /* do nothing */; + } + } + m_xEd1->set_sensitive(bEnable12); + bool bEnable = m_nCols > 1; + m_xDistEd1->set_sensitive(bEnable); + m_xAutoWidthBox->set_sensitive(bEnable && !m_bHtmlMode); + m_xEd2->set_sensitive(bEnable12); + m_xDistEd2->set_sensitive(bEnable3); + m_xEd3->set_sensitive(bEnable3); + m_xLbl1->set_sensitive(bEnable12); + m_xLbl2->set_sensitive(bEnable12); + m_xLbl3->set_sensitive(bEnable3); + m_xBtnBack->set_sensitive(bEnableBtns); + m_xBtnNext->set_sensitive(bEnableBtns); + + m_xLineTypeDLB->set_sensitive( bEnable ); + m_xLineTypeLbl->set_sensitive( bEnable ); + + if (bEnable) + { + bEnable = isLineNotNone(); + } + + //all these depend on > 1 column and line style != none + m_xLineHeightEdit->set_sensitive(bEnable); + m_xLineHeightLbl->set_sensitive(bEnable); + m_xLineWidthLbl->set_sensitive(bEnable); + m_xLineWidthEdit->set_sensitive(bEnable); + m_xLineColorDLB->set_sensitive(bEnable); + m_xLineColorLbl->set_sensitive(bEnable); + + if (bEnable) + bEnable = m_xColMgr->GetLineHeightPercent() != 100; + + //and these additionally depend on line height != 100% + m_xLinePosDLB->set_sensitive(bEnable); + m_xLinePosLbl->set_sensitive(bEnable); +} + +void SwColumnPage::SetLabels( sal_uInt16 nVis ) +{ + //insert ~ before the last character, e.g. 1 -> ~1, 10 -> 1~0 + const OUString sLbl( '~' ); + + const OUString sLbl1(OUString::number( nVis + 1 )); + m_xLbl1->set_label(sLbl1.replaceAt(sLbl1.getLength()-1, 0, sLbl)); + + const OUString sLbl2(OUString::number( nVis + 2 )); + m_xLbl2->set_label(sLbl2.replaceAt(sLbl2.getLength()-1, 0, sLbl)); + + const OUString sLbl3(OUString::number( nVis + 3 )); + m_xLbl3->set_label(sLbl3.replaceAt(sLbl3.getLength()-1, 0, sLbl)); + + const OUString sColumnWidth = SwResId( STR_ACCESS_COLUMN_WIDTH ) ; + m_xEd1->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl1)); + m_xEd2->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl2)); + m_xEd3->set_accessible_name(sColumnWidth.replaceFirst("%1", sLbl3)); + + const OUString sDist = SwResId( STR_ACCESS_PAGESETUP_SPACING ) ; + m_xDistEd1->set_accessible_name( + sDist.replaceFirst("%1", sLbl1).replaceFirst("%2", sLbl2)); + + m_xDistEd2->set_accessible_name( + sDist.replaceFirst("%1", sLbl2).replaceFirst("%2", sLbl3)); +} + +/* + * Handler that is called at alteration of the column number. An alteration of + * the column number overwrites potential user's width settings; all columns + * are equally wide. + */ +IMPL_LINK(SwColumnPage, ColModify, weld::SpinButton&, rEdit, void) +{ + ColModify(&rEdit); +} + +void SwColumnPage::ColModify(const weld::SpinButton* pNF) +{ + m_nCols = static_cast<sal_uInt16>(m_xCLNrEdt->get_value()); + //#107890# the handler is also called from LoseFocus() + //then no change has been made and thus no action should be taken + // #i17816# changing the displayed types within the ValueSet + //from two columns to two columns with different settings doesn't invalidate the + // example windows in ::ColModify() + if (!pNF || m_xColMgr->GetCount() != m_nCols) + { + if (pNF) + m_aDefaultVS.SetNoSelection(); + long nDist = static_cast< long >(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP))); + m_xColMgr->SetCount(m_nCols, static_cast<sal_uInt16>(nDist)); + for(sal_uInt16 i = 0; i < m_nCols; i++) + m_nColDist[i] = nDist; + m_nFirstVis = 0; + SetLabels( m_nFirstVis ); + UpdateCols(); + ResetColWidth(); + Update(nullptr); + } +} + +/* + * Modify handler for an alteration of the column width or the column gap. + * These changes take effect time-displaced. With an alteration of the column + * width the automatic calculation of the column width is overruled; only an + * alteration of the column number leads back to that default. + */ +IMPL_LINK(SwColumnPage, GapModify, weld::MetricSpinButton&, rMetricField, void) +{ + if (m_nCols < 2) + return; + SwPercentField *pField = m_aPercentFieldsMap[&rMetricField]; + assert(pField); + long nActValue = static_cast< long >(pField->DenormalizePercent(pField->get_value(FieldUnit::TWIP))); + if (m_xAutoWidthBox->get_active()) + { + const long nMaxGap = static_cast< long > + ((m_xColMgr->GetActualSize() - m_nCols * MINLAY)/(m_nCols - 1)); + if(nActValue > nMaxGap) + { + nActValue = nMaxGap; + m_xDistEd1->set_value(m_xDistEd1->NormalizePercent(nMaxGap), FieldUnit::TWIP); + } + m_xColMgr->SetGutterWidth(static_cast<sal_uInt16>(nActValue)); + for(sal_uInt16 i = 0; i < m_nCols; i++) + m_nColDist[i] = nActValue; + + ResetColWidth(); + UpdateCols(); + } + else + { + const sal_uInt16 nVis = m_nFirstVis + ((pField == m_xDistEd2.get()) ? 1 : 0); + long nDiff = nActValue - m_nColDist[nVis]; + if(nDiff) + { + long nLeft = m_nColWidth[nVis]; + long nRight = m_nColWidth[nVis + 1]; + if(nLeft + nRight + 2 * MINLAY < nDiff) + nDiff = nLeft + nRight - 2 * MINLAY; + if(nDiff < nRight - MINLAY) + { + nRight -= nDiff; + } + else + { + long nTemp = nDiff - nRight + MINLAY; + nRight = MINLAY; + if(nLeft > nTemp - MINLAY) + { + nLeft -= nTemp; + nTemp = 0; + } + else + { + nTemp -= nLeft + MINLAY; + nLeft = MINLAY; + } + nDiff = nTemp; + } + m_nColWidth[nVis] = nLeft; + m_nColWidth[nVis + 1] = nRight; + m_nColDist[nVis] += nDiff; + + m_xColMgr->SetColWidth( nVis, sal_uInt16(nLeft) ); + m_xColMgr->SetColWidth( nVis + 1, sal_uInt16(nRight) ); + m_xColMgr->SetGutterWidth( sal_uInt16(m_nColDist[nVis]), nVis ); + } + + } + Update(&rMetricField); +} + +IMPL_LINK(SwColumnPage, EdModify, weld::MetricSpinButton&, rEdit, void) +{ + SwPercentField *pField = m_aPercentFieldsMap[&rEdit]; + assert(pField); + m_pModifiedField = pField; + Timeout(); +} + +// Handler behind the Checkbox for automatic width. When the box is checked +// no explicit values for the column width can be entered. +IMPL_LINK(SwColumnPage, AutoWidthHdl, weld::ToggleButton&, rBox, void) +{ + long nDist = static_cast< long >(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP))); + m_xColMgr->SetCount(m_nCols, static_cast<sal_uInt16>(nDist)); + for(sal_uInt16 i = 0; i < m_nCols; i++) + m_nColDist[i] = nDist; + if (rBox.get_active()) + { + m_xColMgr->SetGutterWidth(sal_uInt16(nDist)); + ResetColWidth(); + } + m_xColMgr->SetAutoWidth(rBox.get_active(), sal_uInt16(nDist)); + UpdateCols(); + Update(nullptr); +} + +// scroll up the contents of the edits +IMPL_LINK_NOARG(SwColumnPage, Up, weld::Button&, void) +{ + if( m_nFirstVis ) + { + --m_nFirstVis; + SetLabels( m_nFirstVis ); + Update(nullptr); + } +} + +// scroll down the contents of the edits. +IMPL_LINK_NOARG(SwColumnPage, Down, weld::Button&, void) +{ + if( m_nFirstVis + nVisCols < m_nCols ) + { + ++m_nFirstVis; + SetLabels( m_nFirstVis ); + Update(nullptr); + } +} + +// relict from ancient times - now directly without time handler; triggered by +// an alteration of the column width or the column gap. +void SwColumnPage::Timeout() +{ + SwPercentField *pField = m_pModifiedField; + if (m_pModifiedField) + { + // find the changed column + sal_uInt16 nChanged = m_nFirstVis; + if (m_pModifiedField == m_xEd2.get()) + ++nChanged; + else if (m_pModifiedField == m_xEd3.get()) + nChanged += 2; + + long nNewWidth = static_cast< long > + (m_pModifiedField->DenormalizePercent(m_pModifiedField->get_value(FieldUnit::TWIP))); + long nDiff = nNewWidth - m_nColWidth[nChanged]; + + // when it's the last column + if(nChanged == m_nCols - 1) + { + m_nColWidth[0] -= nDiff; + if(m_nColWidth[0] < static_cast<long>(g_nMinWidth)) + { + nNewWidth -= g_nMinWidth - m_nColWidth[0]; + m_nColWidth[0] = g_nMinWidth; + } + + } + else if(nDiff) + { + m_nColWidth[nChanged + 1] -= nDiff; + if(m_nColWidth[nChanged + 1] < static_cast<long>(g_nMinWidth)) + { + nNewWidth -= g_nMinWidth - m_nColWidth[nChanged + 1]; + m_nColWidth[nChanged + 1] = g_nMinWidth; + } + } + m_nColWidth[nChanged] = nNewWidth; + m_pModifiedField = nullptr; + } + + Update(pField ? pField->get() : nullptr); +} + +// Update the view +void SwColumnPage::Update(const weld::MetricSpinButton* pInteractiveField) +{ + m_xBalanceColsCB->set_sensitive(m_nCols > 1); + if(m_nCols >= 2) + { + sal_Int64 nCurrentValue, nNewValue; + + nCurrentValue = m_xEd1->NormalizePercent(m_xEd1->DenormalizePercent(m_xEd1->get_value(FieldUnit::TWIP))); + nNewValue = m_xEd1->NormalizePercent(m_nColWidth[m_nFirstVis]); + + //fdo#87612 if we're interacting with this widget and the value will be the same + //then leave it alone (i.e. don't change equivalent values of e.g. .8 -> 0.8) + if (nNewValue != nCurrentValue || pInteractiveField != m_xEd1->get()) + m_xEd1->set_value(nNewValue, FieldUnit::TWIP); + + nCurrentValue = m_xDistEd1->NormalizePercent(m_xDistEd1->DenormalizePercent(m_xDistEd1->get_value(FieldUnit::TWIP))); + nNewValue = m_xDistEd1->NormalizePercent(m_nColDist[m_nFirstVis]); + if (nNewValue != nCurrentValue || pInteractiveField != m_xDistEd1->get()) + m_xDistEd1->set_value(nNewValue, FieldUnit::TWIP); + + nCurrentValue = m_xEd2->NormalizePercent(m_xEd2->DenormalizePercent(m_xEd2->get_value(FieldUnit::TWIP))); + nNewValue = m_xEd2->NormalizePercent(m_nColWidth[m_nFirstVis+1]); + if (nNewValue != nCurrentValue || pInteractiveField != m_xEd2->get()) + m_xEd2->set_value(nNewValue, FieldUnit::TWIP); + + if(m_nCols >= 3) + { + nCurrentValue = m_xDistEd2->NormalizePercent(m_xDistEd2->DenormalizePercent(m_xDistEd2->get_value(FieldUnit::TWIP))); + nNewValue = m_xDistEd2->NormalizePercent(m_nColDist[m_nFirstVis+1]); + if (nNewValue != nCurrentValue || pInteractiveField != m_xDistEd2->get()) + m_xDistEd2->set_value(nNewValue, FieldUnit::TWIP); + + nCurrentValue = m_xEd3->NormalizePercent(m_xEd3->DenormalizePercent(m_xEd3->get_value(FieldUnit::TWIP))); + nNewValue = m_xEd3->NormalizePercent(m_nColWidth[m_nFirstVis+2]); + if (nNewValue != nCurrentValue || pInteractiveField != m_xEd3->get()) + m_xEd3->set_value(nNewValue, FieldUnit::TWIP); + } + else + { + m_xEd3->set_text(OUString()); + m_xDistEd2->set_text(OUString()); + } + } + else + { + m_xEd1->set_text(OUString()); + m_xEd2->set_text(OUString()); + m_xEd3->set_text(OUString()); + m_xDistEd1->set_text(OUString()); + m_xDistEd2->set_text(OUString()); + } + UpdateColMgr(*m_xLineWidthEdit); +} + +// Update Bsp +void SwColumnPage::ActivatePage(const SfxItemSet& rSet) +{ + bool bVertical = false; + if (SfxItemState::DEFAULT <= rSet.GetItemState(RES_FRAMEDIR)) + { + const SvxFrameDirectionItem& rDirItem = + rSet.Get(RES_FRAMEDIR); + bVertical = rDirItem.GetValue() == SvxFrameDirection::Vertical_RL_TB|| + rDirItem.GetValue() == SvxFrameDirection::Vertical_LR_TB; + } + + if (!m_bFrame) + { + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_PAGE_SIZE )) + { + const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE); + + sal_uInt16 nActWidth; + + if (!bVertical) + { + const SvxLRSpaceItem& rLRSpace = rSet.Get(RES_LR_SPACE); + const SvxBoxItem& rBox = rSet.Get(RES_BOX); + nActWidth = rSize.GetSize().Width() + - rLRSpace.GetLeft() - rLRSpace.GetRight() - rBox.GetSmallestDistance(); + } + else + { + const SvxULSpaceItem& rULSpace = rSet.Get( RES_UL_SPACE ); + const SvxBoxItem& rBox = rSet.Get(RES_BOX); + nActWidth = rSize.GetSize().Height() + - rULSpace.GetUpper() - rULSpace.GetLower() - rBox.GetSmallestDistance(); + + } + + if( m_xColMgr->GetActualSize() != nActWidth) + { + m_xColMgr->SetActualWidth(nActWidth); + ColModify( nullptr ); + UpdateColMgr( *m_xLineWidthEdit ); + } + } + m_xFrameExampleWN->hide(); + m_aPgeExampleWN.UpdateExample(rSet, m_xColMgr.get()); + m_xPgeExampleWN->show(); + + } + else + { + m_xPgeExampleWN->hide(); + m_xFrameExampleWN->show(); + + // Size + const SwFormatFrameSize& rSize = rSet.Get(RES_FRM_SIZE); + const SvxBoxItem& rBox = rSet.Get(RES_BOX); + + sal_uInt16 nTotalWish; + if (m_bFormat) + nTotalWish = FRAME_FORMAT_WIDTH; + else + { + long const nDistance = rBox.GetSmallestDistance(); + nTotalWish = (!bVertical ? rSize.GetWidth() : rSize.GetHeight()) - 2 * nDistance; + } + + // set maximum values of column width + SetPageWidth(nTotalWish); + + if(m_xColMgr->GetActualSize() != nTotalWish) + { + m_xColMgr->SetActualWidth(nTotalWish); + Init(); + } + bool bPercent; + // only relative data in frame format + if ( m_bFormat || (rSize.GetWidthPercent() && rSize.GetWidthPercent() != SwFormatFrameSize::SYNCED) ) + { + // set value for 100% + m_xEd1->SetRefValue(nTotalWish); + m_xEd2->SetRefValue(nTotalWish); + m_xEd3->SetRefValue(nTotalWish); + m_xDistEd1->SetRefValue(nTotalWish); + m_xDistEd2->SetRefValue(nTotalWish); + + // switch to %-view + bPercent = true; + } + else + bPercent = false; + + m_xEd1->ShowPercent(bPercent); + m_xEd2->ShowPercent(bPercent); + m_xEd3->ShowPercent(bPercent); + m_xDistEd1->ShowPercent(bPercent); + m_xDistEd2->ShowPercent(bPercent); + m_xDistEd1->SetMetricFieldMin(0); + m_xDistEd2->SetMetricFieldMin(0); + } + Update(nullptr); +} + +DeactivateRC SwColumnPage::DeactivatePage(SfxItemSet *_pSet) +{ + if(_pSet) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +IMPL_LINK(SwColumnPage, SetDefaultsHdl, ValueSet *, pVS, void) +{ + const sal_uInt16 nItem = pVS->GetSelectedItemId(); + if( nItem < 4 ) + { + m_xCLNrEdt->set_value(nItem); + m_xAutoWidthBox->set_active(true); + m_xDistEd1->set_value(50, FieldUnit::CM); + ColModify(nullptr); + } + else + { + m_bLockUpdate = true; + m_xCLNrEdt->set_value(2); + m_xAutoWidthBox->set_active(false); + m_xDistEd1->set_value(50, FieldUnit::CM); + ColModify(nullptr); + // now set the width ratio to 2 : 1 or 1 : 2 respectively + const long nSmall = static_cast< long >(m_xColMgr->GetActualSize() / 3); + if(nItem == 4) + { + m_xEd2->set_value(m_xEd2->NormalizePercent(nSmall), FieldUnit::TWIP); + m_pModifiedField = m_xEd2.get(); + } + else + { + m_xEd1->set_value(m_xEd1->NormalizePercent(nSmall), FieldUnit::TWIP); + m_pModifiedField = m_xEd1.get(); + } + m_bLockUpdate = false; + Timeout(); + + } +} + +void SwColumnPage::SetFrameMode(bool bMod) +{ + m_bFrame = bMod; +} + +void SwColumnPage::SetInSection(bool bSet) +{ + if(!SW_MOD()->GetCTLOptions().IsCTLFontEnabled()) + return; + + m_xTextDirectionFT->set_visible(bSet); + m_xTextDirectionLB->set_visible(bSet); +} + +void ColumnValueSet::UserDraw(const UserDrawEvent& rUDEvt) +{ + vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + tools::Rectangle aRect = rUDEvt.GetRect(); + const sal_uInt16 nItemId = rUDEvt.GetItemId(); + long nRectWidth = aRect.GetWidth(); + long nRectHeight = aRect.GetHeight(); + + Point aBLPos = aRect.TopLeft(); + Color aFillColor(pDev->GetFillColor()); + Color aLineColor(pDev->GetLineColor()); + pDev->SetFillColor(rStyleSettings.GetFieldColor()); + pDev->SetLineColor(rStyleSettings.GetFieldTextColor()); + + long nStep = std::abs(std::abs(nRectHeight * 95 /100) / 11); + long nTop = (nRectHeight - 11 * nStep ) / 2; + sal_uInt16 nCols = 0; + long nStarts[3]; + long nEnds[3]; + nStarts[0] = nRectWidth * 10 / 100; + switch( nItemId ) + { + case 1: + nEnds[0] = nRectWidth * 9 / 10; + nCols = 1; + break; + case 2: nCols = 2; + nEnds[0] = nRectWidth * 45 / 100; + nStarts[1] = nEnds[0] + nStep; + nEnds[1] = nRectWidth * 9 / 10; + break; + case 3: nCols = 3; + nEnds[0] = nRectWidth * 30 / 100; + nStarts[1] = nEnds[0] + nStep; + nEnds[1] = nRectWidth * 63 / 100; + nStarts[2] = nEnds[1] + nStep; + nEnds[2] = nRectWidth * 9 / 10; + break; + case 4: nCols = 2; + nEnds[0] = nRectWidth * 63 / 100; + nStarts[1] = nEnds[0] + nStep; + nEnds[1] = nRectWidth * 9 / 10; + break; + case 5: nCols = 2; + nEnds[0] = nRectWidth * 30 / 100; + nStarts[1] = nEnds[0] + nStep; + nEnds[1] = nRectWidth * 9 / 10; + break; + } + for(sal_uInt16 j = 0; j < nCols; j++ ) + { + Point aStart(aBLPos.X() + nStarts[j], 0); + Point aEnd(aBLPos.X() + nEnds[j], 0); + for( sal_uInt16 i = 0; i < 12; i ++) + { + aStart.setY( aBLPos.Y() + nTop + i * nStep); + aEnd.setY( aStart.Y() ); + pDev->DrawLine(aStart, aEnd); + } + } + pDev->SetFillColor(aFillColor); + pDev->SetLineColor(aLineColor); +} + +void ColumnValueSet::StyleUpdated() +{ + SetFormat(); + Invalidate(); + ValueSet::StyleUpdated(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/cption.cxx b/sw/source/ui/frmdlg/cption.cxx new file mode 100644 index 000000000..6f4f8208b --- /dev/null +++ b/sw/source/ui/frmdlg/cption.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 <view.hxx> +#include <wrtsh.hxx> +#include <cption.hxx> +#include <fldmgr.hxx> +#include <expfld.hxx> +#include <numrule.hxx> +#include <poolfmt.hxx> +#include <docsh.hxx> +#include <calc.hxx> +#include <uitool.hxx> +#include <doc.hxx> +#include <modcfg.hxx> +#include <swmodule.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/text/XTextFramesSupplier.hpp> +#include <comphelper/string.hxx> +#include <vcl/weld.hxx> +#include <strings.hrc> +#include <SwStyleNameMapper.hxx> + +using namespace ::com::sun::star; + +namespace { + +class SwSequenceOptionDialog : public weld::GenericDialogController +{ + SwView& m_rView; + OUString m_aFieldTypeName; + + std::unique_ptr<weld::ComboBox> m_xLbLevel; + std::unique_ptr<weld::Entry> m_xEdDelim; + + std::unique_ptr<weld::ComboBox> m_xLbCharStyle; + std::unique_ptr<weld::CheckButton> m_xApplyBorderAndShadowCB; + + //#i61007# order of captions + std::unique_ptr<weld::ComboBox> m_xLbCaptionOrder; + +public: + SwSequenceOptionDialog(weld::Window *pParent, SwView &rV, const OUString& rSeqFieldType); + void Apply(); + + bool IsApplyBorderAndShadow() const { return m_xApplyBorderAndShadowCB->get_active(); } + void SetApplyBorderAndShadow( bool bSet ) { m_xApplyBorderAndShadowCB->set_active(bSet); } + + //#i61007# order of captions + bool IsOrderNumberingFirst() const { return m_xLbCaptionOrder->get_active() == 1; } + void SetOrderNumberingFirst(bool bSet) { m_xLbCaptionOrder->set_active(bSet ? 1 : 0); } + + void SetCharacterStyle(const OUString& rStyle); + OUString GetCharacterStyle() const; + + virtual short run() override + { + int nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } +}; + +} + +OUString SwCaptionDialog::our_aSepTextSave(": "); // Caption separator text + +//Resolves: tdf#47427 disallow typing *or* pasting invalid content into the category box +OUString TextFilterAutoConvert::filter(const OUString &rText) +{ + if (!rText.isEmpty() && rText != m_sNone && !SwCalc::IsValidVarName(rText)) + return m_sLastGoodText; + m_sLastGoodText = rText; + return rText; +} + +SwCaptionDialog::SwCaptionDialog(weld::Window *pParent, SwView &rV) + : SfxDialogController(pParent, "modules/swriter/ui/insertcaption.ui", "InsertCaptionDialog") + , m_sNone(SwResId(SW_STR_NONE)) + , m_aTextFilter(m_sNone) + , rView(rV) + , pMgr(new SwFieldMgr(rView.GetWrtShellPtr())) + , bCopyAttributes(false) + , bOrderNumberingFirst(SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst()) + , m_xTextEdit(m_xBuilder->weld_entry("caption_edit")) + , m_xCategoryBox(m_xBuilder->weld_combo_box("category")) + , m_xFormatText(m_xBuilder->weld_label("numbering_label")) + , m_xFormatBox(m_xBuilder->weld_combo_box("numbering")) + , m_xNumberingSeparatorFT(m_xBuilder->weld_label("num_separator")) + , m_xNumberingSeparatorED(m_xBuilder->weld_entry("num_separator_edit")) + , m_xSepText(m_xBuilder->weld_label("separator_label")) + , m_xSepEdit(m_xBuilder->weld_entry("separator_edit")) + , m_xPosText(m_xBuilder->weld_label("position_label")) + , m_xPosBox(m_xBuilder->weld_combo_box("position")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_xAutoCaptionButton(m_xBuilder->weld_button("auto")) + , m_xOptionButton(m_xBuilder->weld_button("options")) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreview)) +{ + //#i61007# order of captions + ApplyCaptionOrder(); + SwWrtShell &rSh = rView.GetWrtShell(); + uno::Reference< frame::XModel > xModel = rView.GetDocShell()->GetBaseModel(); + + SelectionType eType = rSh.GetSelectionType(); + if ( eType & SelectionType::Ole ) + { + eType = SelectionType::Graphic; + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + xNameAccess = xObjs->getEmbeddedObjects(); + } + + m_xCategoryBox->connect_changed(LINK(this, SwCaptionDialog, ModifyComboHdl)); + Link<weld::Entry&,void> aLk = LINK(this, SwCaptionDialog, ModifyEntryHdl); + m_xTextEdit->connect_changed(aLk); + m_xNumberingSeparatorED->connect_changed(aLk); + m_xSepEdit->connect_changed(aLk); + + m_xFormatBox->connect_changed(LINK(this, SwCaptionDialog, SelectListBoxHdl)); + m_xOptionButton->connect_clicked(LINK(this, SwCaptionDialog, OptionHdl)); + m_xAutoCaptionButton->connect_clicked(LINK(this, SwCaptionDialog, CaptionHdl)); + + m_xCategoryBox->append_text(m_sNone); + size_t nCount = pMgr->GetFieldTypeCount(); + for (size_t i = 0; i < nCount; ++i) + { + SwFieldType *pType = pMgr->GetFieldType( SwFieldIds::Unknown, i ); + if( pType->Which() == SwFieldIds::SetExp && + static_cast<SwSetExpFieldType *>( pType)->GetType() & nsSwGetSetExpType::GSE_SEQ ) + m_xCategoryBox->append_text(pType->GetName()); + } + + OUString sString; + sal_uInt16 nPoolId = 0; + if (eType & SelectionType::Graphic) + { + nPoolId = RES_POOLCOLL_LABEL_FIGURE; + + SwSetExpFieldType* pTypeIll= static_cast<SwSetExpFieldType*>(rSh.GetFieldType(SwFieldIds::SetExp, SwResId(STR_POOLCOLL_LABEL_ABB))); + if(rSh.IsUsed(*pTypeIll)) //default to illustration for legacy docs + { + nPoolId = RES_POOLCOLL_LABEL_ABB; + + } + + sString = rView.GetOldGrfCat(); + bCopyAttributes = true; + //if not OLE + if(!xNameAccess.is()) + { + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xNameAccess = xGraphics->getGraphicObjects(); + } + + } + else if( eType & SelectionType::Table ) + { + nPoolId = RES_POOLCOLL_LABEL_TABLE; + sString = rView.GetOldTabCat(); + uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY); + xNameAccess = xTables->getTextTables(); + } + else if( eType & SelectionType::Frame ) + { + nPoolId = RES_POOLCOLL_LABEL_FRAME; + sString = rView.GetOldFrameCat(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + xNameAccess = xFrames->getTextFrames(); + } + else if( eType == SelectionType::Text ) + { + nPoolId = RES_POOLCOLL_LABEL_FRAME; + sString = rView.GetOldFrameCat(); + } + else if( eType & SelectionType::DrawObject ) + { + nPoolId = RES_POOLCOLL_LABEL_DRAWING; + sString = rView.GetOldDrwCat(); + } + if( nPoolId ) + { + if (sString.isEmpty()) + sString = SwStyleNameMapper::GetUIName(nPoolId, OUString()); + auto nIndex = m_xCategoryBox->find_text(sString); + if (nIndex != -1) + m_xCategoryBox->set_active(nIndex); + else + m_xCategoryBox->set_entry_text(sString); + } + + // aFormatBox + sal_uInt16 nSelFormat = SVX_NUM_ARABIC; + nCount = pMgr->GetFieldTypeCount(); + for ( size_t i = nCount; i; ) + { + SwFieldType* pFieldType = pMgr->GetFieldType(SwFieldIds::Unknown, --i); + if (pFieldType->GetName() == m_xCategoryBox->get_active_text()) + { + nSelFormat = static_cast<sal_uInt16>(static_cast<SwSetExpFieldType*>(pFieldType)->GetSeqFormat()); + break; + } + } + + sal_uInt16 nFormatCount = pMgr->GetFormatCount(SwFieldTypesEnum::Sequence, false); + for ( sal_uInt16 i = 0; i < nFormatCount; ++i ) + { + const sal_uInt16 nFormatId = pMgr->GetFormatId(SwFieldTypesEnum::Sequence, i); + m_xFormatBox->append(OUString::number(nFormatId), pMgr->GetFormatStr(SwFieldTypesEnum::Sequence, i)); + if (nFormatId == nSelFormat) + m_xFormatBox->set_active(i); + } + + // aPosBox + if (eType == SelectionType::Graphic + || eType == SelectionType::Table + || eType == (SelectionType::Table | SelectionType::NumberList) + || eType == (SelectionType::Table | SelectionType::Text) + || eType == (SelectionType::Table | SelectionType::NumberList | SelectionType::Text) + || eType == SelectionType::DrawObject + || eType == (SelectionType::DrawObject | SelectionType::Ornament)) + { + m_xPosBox->append_text(SwResId(STR_CAPTION_ABOVE)); + m_xPosBox->append_text(SwResId(STR_CAPTION_BELOW)); + } + else if(eType == SelectionType::Frame + || eType == SelectionType::Text) + { + m_xPosBox->append_text(SwResId(STR_CAPTION_BEGINNING)); + m_xPosBox->append_text(SwResId(STR_CAPTION_END)); + } + + if (eType & SelectionType::Table) + { + m_xPosBox->set_active(0); + } + else + { + m_xPosBox->set_active(1); + } + + ModifyHdl(); + + m_xSepEdit->set_text(our_aSepTextSave); + m_xTextEdit->grab_focus(); + DrawSample(); +} + +void SwCaptionDialog::Apply() +{ + InsCaptionOpt aOpt; + aOpt.UseCaption() = true; + OUString aName(m_xCategoryBox->get_active_text()); + if ( aName == m_sNone ) + { + aOpt.SetCategory( OUString() ); + aOpt.SetNumSeparator( OUString() ); + } + else + { + aOpt.SetCategory(comphelper::string::strip(aName, ' ')); + aOpt.SetNumSeparator(m_xNumberingSeparatorED->get_text()); + } + aOpt.SetNumType(m_xFormatBox->get_active_id().toUInt32()); + aOpt.SetSeparator(m_xSepEdit->get_sensitive() ? m_xSepEdit->get_text() : OUString()); + aOpt.SetCaption(m_xTextEdit->get_text()); + aOpt.SetPos(m_xPosBox->get_active()); + aOpt.IgnoreSeqOpts() = true; + aOpt.CopyAttributes() = bCopyAttributes; + aOpt.SetCharacterStyle( sCharacterStyle ); + rView.InsertCaption( &aOpt ); + our_aSepTextSave = m_xSepEdit->get_text(); +} + +short SwCaptionDialog::run() +{ + short nRet = SfxDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +IMPL_LINK_NOARG(SwCaptionDialog, OptionHdl, weld::Button&, void) +{ + OUString sFieldTypeName = m_xCategoryBox->get_active_text(); + if(sFieldTypeName == m_sNone) + sFieldTypeName.clear(); + SwSequenceOptionDialog aDlg(m_xDialog.get(), rView, sFieldTypeName); + aDlg.SetApplyBorderAndShadow(bCopyAttributes); + aDlg.SetCharacterStyle( sCharacterStyle ); + aDlg.SetOrderNumberingFirst( bOrderNumberingFirst ); + aDlg.run(); + bCopyAttributes = aDlg.IsApplyBorderAndShadow(); + sCharacterStyle = aDlg.GetCharacterStyle(); + //#i61007# order of captions + if( bOrderNumberingFirst != aDlg.IsOrderNumberingFirst() ) + { + bOrderNumberingFirst = aDlg.IsOrderNumberingFirst(); + SW_MOD()->GetModuleConfig()->SetCaptionOrderNumberingFirst(bOrderNumberingFirst); + ApplyCaptionOrder(); + } + DrawSample(); +} + +IMPL_LINK_NOARG(SwCaptionDialog, SelectListBoxHdl, weld::ComboBox&, void) +{ + DrawSample(); +} + +void SwCaptionDialog::ModifyHdl() +{ + SwWrtShell &rSh = rView.GetWrtShell(); + OUString sFieldTypeName = m_xCategoryBox->get_active_text(); + bool bCorrectFieldName = !sFieldTypeName.isEmpty(); + bool bNone = sFieldTypeName == m_sNone; + SwFieldType* pType = (bCorrectFieldName && !bNone) + ? rSh.GetFieldType( SwFieldIds::SetExp, sFieldTypeName ) + : nullptr; + m_xOKButton->set_sensitive( bCorrectFieldName && + (!pType || + static_cast<SwSetExpFieldType*>(pType)->GetType() == nsSwGetSetExpType::GSE_SEQ) ); + m_xOptionButton->set_sensitive(m_xOKButton->get_sensitive() && !bNone); + m_xNumberingSeparatorFT->set_sensitive(bOrderNumberingFirst && !bNone); + m_xNumberingSeparatorED->set_sensitive(bOrderNumberingFirst && !bNone); + m_xFormatText->set_sensitive(!bNone); + m_xFormatBox->set_sensitive(!bNone); + m_xSepText->set_sensitive(!bNone); + m_xSepEdit->set_sensitive(!bNone); + DrawSample(); +} + +IMPL_LINK_NOARG(SwCaptionDialog, ModifyEntryHdl, weld::Entry&, void) +{ + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwCaptionDialog, ModifyComboHdl, weld::ComboBox&, void) +{ + OUString sText = m_xCategoryBox->get_active_text(); + OUString sAllowedText = m_aTextFilter.filter(sText); + if (sText != sAllowedText) + { + m_xCategoryBox->set_entry_text(sAllowedText); + m_xCategoryBox->select_entry_region(sAllowedText.getLength(), sAllowedText.getLength()); + } + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwCaptionDialog, CaptionHdl, weld::Button&, void) +{ + SfxItemSet aSet(rView.GetDocShell()->GetDoc()->GetAttrPool()); + SwCaptionOptDlg aDlg(m_xDialog.get(), aSet); + aDlg.run(); +} + +void SwCaptionDialog::DrawSample() +{ + OUString aStr; + OUString sCaption = m_xTextEdit->get_text(); + + // number + OUString sFieldTypeName = m_xCategoryBox->get_active_text(); + bool bNone = sFieldTypeName == m_sNone; + if( !bNone ) + { + const sal_uInt16 nNumFormat = m_xFormatBox->get_active_id().toUInt32(); + if (SVX_NUM_NUMBER_NONE != nNumFormat) + { + // category + //#i61007# order of captions + if( !bOrderNumberingFirst ) + { + aStr = sFieldTypeName; + if ( !aStr.isEmpty() ) + aStr += " "; + } + + SwWrtShell &rSh = rView.GetWrtShell(); + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(rSh.GetFieldType( + SwFieldIds::SetExp, sFieldTypeName )); + if( pFieldType && pFieldType->GetOutlineLvl() < MAXLEVEL ) + { + SwNumberTree::tNumberVector aNumVector; + aNumVector.insert(aNumVector.end(), pFieldType->GetOutlineLvl() + 1, 1); + + OUString sNumber( rSh.GetOutlineNumRule()-> + MakeNumString(aNumVector, false )); + if( !sNumber.isEmpty() ) + aStr += sNumber + pFieldType->GetDelimiter(); + } + + switch( nNumFormat ) + { + case SVX_NUM_CHARS_UPPER_LETTER: aStr += "A"; break; + case SVX_NUM_CHARS_UPPER_LETTER_N: aStr += "A"; break; + case SVX_NUM_CHARS_LOWER_LETTER: aStr += "a"; break; + case SVX_NUM_CHARS_LOWER_LETTER_N: aStr += "a"; break; + case SVX_NUM_ROMAN_UPPER: aStr += "I"; break; + case SVX_NUM_ROMAN_LOWER: aStr += "i"; break; + default: aStr += "1"; break; + } + //#i61007# order of captions + if( bOrderNumberingFirst ) + { + aStr += m_xNumberingSeparatorED->get_text() + sFieldTypeName; + } + + } + if( !sCaption.isEmpty() ) + { + aStr += m_xSepEdit->get_text(); + } + } + aStr += sCaption; + // do preview! + m_aPreview.SetPreviewText(aStr); +} + +SwCaptionDialog::~SwCaptionDialog() +{ +} + +SwSequenceOptionDialog::SwSequenceOptionDialog(weld::Window *pParent, SwView &rV, const OUString& rSeqFieldType ) + : GenericDialogController(pParent, "modules/swriter/ui/captionoptions.ui", "CaptionOptionsDialog") + , m_rView(rV) + , m_aFieldTypeName(rSeqFieldType) + , m_xLbLevel(m_xBuilder->weld_combo_box("level")) + , m_xEdDelim(m_xBuilder->weld_entry("separator")) + , m_xLbCharStyle(m_xBuilder->weld_combo_box("style")) + , m_xApplyBorderAndShadowCB(m_xBuilder->weld_check_button("border_and_shadow")) + , m_xLbCaptionOrder(m_xBuilder->weld_combo_box("caption_order")) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + + const OUString sNone(SwResId(SW_STR_NONE)); + + m_xLbLevel->append_text(sNone); + for (sal_uInt16 n = 0; n < MAXLEVEL; ++n) + m_xLbLevel->append_text(OUString::number(n + 1)); + + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(rSh.GetFieldType( + SwFieldIds::SetExp, m_aFieldTypeName )); + + sal_Unicode nLvl = MAXLEVEL; + OUString sDelim(": "); + if( pFieldType ) + { + sDelim = pFieldType->GetDelimiter(); + nLvl = pFieldType->GetOutlineLvl(); + } + + m_xLbLevel->set_active(nLvl < MAXLEVEL ? nLvl + 1 : 0); + m_xEdDelim->set_text(sDelim); + + m_xLbCharStyle->append_text(sNone); + ::FillCharStyleListBox(*m_xLbCharStyle, m_rView.GetDocShell(), true, true); + m_xLbCharStyle->set_active(0); +} + +void SwSequenceOptionDialog::Apply() +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(rSh.GetFieldType( + SwFieldIds::SetExp, m_aFieldTypeName )); + + sal_Int8 nLvl = static_cast<sal_Int8>(m_xLbLevel->get_active() - 1); + sal_Unicode cDelim = m_xEdDelim->get_text()[0]; + + bool bUpdate = true; + if( pFieldType ) + { + pFieldType->SetDelimiter( OUString(cDelim) ); + pFieldType->SetOutlineLvl( nLvl ); + } + else if( !m_aFieldTypeName.isEmpty() && nLvl < MAXLEVEL ) + { + // then we have to insert that + SwSetExpFieldType aFieldType( rSh.GetDoc(), m_aFieldTypeName, nsSwGetSetExpType::GSE_SEQ ); + aFieldType.SetDelimiter( OUString(cDelim) ); + aFieldType.SetOutlineLvl( nLvl ); + rSh.InsertFieldType( aFieldType ); + } + else + bUpdate = false; + + if( bUpdate ) + rSh.UpdateExpFields(); +} + +OUString SwSequenceOptionDialog::GetCharacterStyle() const +{ + if (m_xLbCharStyle->get_active() != -1) + return m_xLbCharStyle->get_active_text(); + return OUString(); +} + +void SwSequenceOptionDialog::SetCharacterStyle(const OUString& rStyle) +{ + const int nPos = m_xLbCharStyle->find_text(rStyle); + if (nPos == -1) + m_xLbCharStyle->set_active(0); + else + m_xLbCharStyle->set_active(nPos); +} + +// #i61007# order of captions +void SwCaptionDialog::ApplyCaptionOrder() +{ + m_xNumberingSeparatorFT->set_sensitive(bOrderNumberingFirst); + m_xNumberingSeparatorED->set_sensitive(bOrderNumberingFirst); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/frmdlg.cxx b/sw/source/ui/frmdlg/frmdlg.cxx new file mode 100644 index 000000000..f2f13a682 --- /dev/null +++ b/sw/source/ui/frmdlg/frmdlg.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/dialogs.hrc> +#include <hintids.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/htmlmode.hxx> +#include <fmtfsize.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <frmdlg.hxx> +#include <frmpage.hxx> +#include <wrap.hxx> +#include <column.hxx> +#include <macassgn.hxx> + +#include <strings.hrc> +#include <svl/eitem.hxx> +#include <svx/svxids.hrc> +#include <svx/flagsdef.hxx> +#include <svx/drawitem.hxx> +#include <comphelper/lok.hxx> + +// the dialog's carrier +SwFrameDlg::SwFrameDlg(SfxViewFrame const * pViewFrame, + weld::Window* pParent, + const SfxItemSet& rCoreSet, + bool bNewFrame, + const OUString& sResType, + bool bFormat, + const OString& sDefPage, + const OUString* pStr) + + : SfxTabDialogController(pParent, "modules/swriter/ui/" + sResType.toAsciiLowerCase() + ".ui", + sResType.toUtf8(), &rCoreSet, pStr != nullptr) + , m_bFormat(bFormat) + , m_bNew(bNewFrame) + , m_rSet(rCoreSet) + , m_sDlgType(sResType) + , m_pWrtShell(static_cast<SwView*>(pViewFrame->GetViewShell())->GetWrtShellPtr()) +{ + sal_uInt16 nHtmlMode = ::GetHtmlMode(m_pWrtShell->GetView().GetDocShell()); + bool bHTMLMode = (nHtmlMode & HTMLMODE_ON) != 0; + + // example font for both example TabPages + + if (pStr) + { + m_xDialog->set_title(m_xDialog->get_title() + SwResId(STR_FRMUI_COLL_HEADER) + *pStr + ")"); + } + + AddTabPage("type", SwFramePage::Create, nullptr); + AddTabPage("options", SwFrameAddPage::Create, nullptr); + AddTabPage("wrap", SwWrapTabPage::Create, nullptr); + AddTabPage("hyperlink", SwFrameURLPage::Create, nullptr); + if (m_sDlgType == "PictureDialog") + { + AddTabPage("picture", SwGrfExtPage::Create, nullptr); + AddTabPage("crop", RID_SVXPAGE_GRFCROP); + } + if (m_sDlgType == "FrameDialog") + { + AddTabPage("columns", SwColumnPage::Create, nullptr); + } + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + // add Area and Transparence TabPages + AddTabPage("area", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_AREA )); + AddTabPage("transparence", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_TRANSPARENCE ) ); + + AddTabPage("macro", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_MACROASSIGN), nullptr); + AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), nullptr); + + if(bHTMLMode) + { + if (m_sDlgType == "FrameDialog" || m_sDlgType == "ObjectDialog") + { + if (m_sDlgType == "FrameDialog") + RemoveTabPage("columns"); + RemoveTabPage("hyperlink"); + RemoveTabPage("macro"); + } + else if (m_sDlgType == "PictureDialog") + RemoveTabPage("crop"); + if( m_sDlgType != "FrameDialog" ) + { + // RemoveTabPage("background"); + RemoveTabPage("area"); + RemoveTabPage("transparence"); + } + } + + if(comphelper::LibreOfficeKit::isActive()) + RemoveTabPage("macro"); + + if (m_bNew) + SetCurPageId("type"); + + if (!sDefPage.isEmpty()) + SetCurPageId(sDefPage); +} + +SwFrameDlg::~SwFrameDlg() +{ +} + +void SwFrameDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "type") + { + static_cast<SwFramePage&>(rPage).SetNewFrame(m_bNew); + static_cast<SwFramePage&>(rPage).SetFormatUsed(m_bFormat); + static_cast<SwFramePage&>(rPage).SetFrameType(m_sDlgType); + } + else if (rId == "options") + { + static_cast<SwFrameAddPage&>(rPage).SetFormatUsed(m_bFormat); + static_cast<SwFrameAddPage&>(rPage).SetFrameType(m_sDlgType); + static_cast<SwFrameAddPage&>(rPage).SetNewFrame(m_bNew); + static_cast<SwFrameAddPage&>(rPage).SetShell(m_pWrtShell); + } + else if (rId == "wrap") + { + static_cast<SwWrapTabPage&>(rPage).SetNewFrame(m_bNew); + static_cast<SwWrapTabPage&>(rPage).SetFormatUsed(m_bFormat, false); + static_cast<SwWrapTabPage&>(rPage).SetShell(m_pWrtShell); + } + else if (rId == "columns") + { + static_cast<SwColumnPage&>(rPage).SetFrameMode(true); + static_cast<SwColumnPage&>(rPage).SetFormatUsed(m_bFormat); + + const SwFormatFrameSize& rSize = m_rSet.Get( RES_FRM_SIZE ); + static_cast<SwColumnPage&>(rPage).SetPageWidth( rSize.GetWidth() ); + } + else if (rId == "macro") + { + SfxAllItemSet aNewSet(*aSet.GetPool()); + aNewSet.Put( SwMacroAssignDlg::AddEvents( + m_sDlgType == "PictureDialog" ? MACASSGN_GRAPHIC : m_sDlgType == "ObjectDialog" ? MACASSGN_OLE : MACASSGN_FRMURL ) ); + if (m_pWrtShell) + rPage.SetFrame( m_pWrtShell->GetView().GetViewFrame()->GetFrame().GetFrameInterface() ); + rPage.PageCreated(aNewSet); + } + else if (rId == "borders") + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE,static_cast<sal_uInt16>(SwBorderModes::FRAME))); + rPage.PageCreated(aSet); + } + // inits for Area and Transparency TabPages + // The selection attribute lists (XPropertyList derivates, e.g. XColorList for + // the color table) need to be added as items (e.g. SvxColorListItem) to make + // these pages find the needed attributes for fill style suggestions. + // These are set in preparation to trigger this dialog (FN_FORMAT_FRAME_DLG and + // FN_DRAW_WRAP_DLG), but could also be directly added from the DrawModel. + else if (rId == "area") + { + SfxItemSet aNew(*GetInputSetImpl()->GetPool(), + svl::Items<SID_COLOR_TABLE, SID_PATTERN_LIST, + SID_OFFER_IMPORT, SID_OFFER_IMPORT>{}); + + aNew.Put(m_rSet); + + // add flag for direct graphic content selection + aNew.Put(SfxBoolItem(SID_OFFER_IMPORT, true)); + + rPage.PageCreated(aNew); + } + else if (rId == "transparence") + { + rPage.PageCreated(m_rSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/frmpage.cxx b/sw/source/ui/frmdlg/frmpage.cxx new file mode 100644 index 000000000..c9fd24297 --- /dev/null +++ b/sw/source/ui/frmdlg/frmpage.cxx @@ -0,0 +1,3142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/Aspects.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <cmdid.h> +#include <hintids.hxx> +#include <bitmaps.hlst> +#include <o3tl/safeint.hxx> +#include <vcl/mnemonic.hxx> +#include <svl/stritem.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/prntitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/swframeposstrings.hxx> +#include <svx/swframevalidation.hxx> +#include <comphelper/classids.hxx> +#include <tools/globname.hxx> +#include <tools/urlobj.hxx> +#include <fmturl.hxx> +#include <fmteiro.hxx> +#include <fmtcnct.hxx> +#include <fmtsrnd.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <uitool.hxx> +#include <docsh.hxx> +#include <viewopt.hxx> +#include <frmdlg.hxx> +#include <frmmgr.hxx> +#include <frmpage.hxx> +#include <colmgr.hxx> +#include <grfatr.hxx> +#include <fmtfollowtextflow.hxx> +#include <svx/sdtaitm.hxx> +#include <sal/macros.h> + +#include <strings.hrc> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <sfx2/filedlghelper.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <vcl/graphicfilter.hxx> +#include <svtools/embedhlp.hxx> +#include <comphelper/lok.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::sfx2; + +#define SwFPos SvxSwFramePosString + +namespace { + +struct StringIdPair_Impl +{ + SvxSwFramePosString::StringId eHori; + SvxSwFramePosString::StringId eVert; +}; + +} + +#define MAX_PERCENT_WIDTH 254 +#define MAX_PERCENT_HEIGHT 254 + +namespace { + +enum class LB { + NONE = 0x00000000L, + Frame = 0x00000001L, // text region of the paragraph + PrintArea = 0x00000002L, // text region of the paragraph + indentions + VertFrame = 0x00000004L, // vertical text region of the paragraph + VertPrintArea = 0x00000008L, // vertical text region of the paragraph + indentions + RelFrameLeft = 0x00000010L, // left paragraph edge + RelFrameRight = 0x00000020L, // right paragraph edge + + RelPageLeft = 0x00000040L, // left page edge + RelPageRight = 0x00000080L, // right page edge + RelPageFrame = 0x00000100L, // whole page + RelPagePrintArea = 0x00000200L, // text region of the page + + FlyRelPageLeft = 0x00000400L, // left frame edge + FlyRelPageRight = 0x00000800L, // right frame edge + FlyRelPageFrame = 0x00001000L, // whole frame + FlyRelPagePrintArea = 0x00002000L, // inside of the frame + + RelBase = 0x00010000L, // character alignment Base + RelChar = 0x00020000L, // character alignment Character + RelRow = 0x00040000L, // character alignment Row + + FlyVertFrame = 0x00100000L, // vertical entire frame + FlyVertPrintArea = 0x00200000L, // vertical frame text area + + VertLine = 0x00400000L, // vertical text line +}; + +} + +namespace o3tl { + template<> struct typed_flags<LB> : is_typed_flags<LB, 0x00773fffL> {}; +} + +namespace { + +struct RelationMap +{ + SvxSwFramePosString::StringId eStrId; + SvxSwFramePosString::StringId eMirrorStrId; + LB nLBRelation; + sal_Int16 nRelation; +}; + +} + +struct FrameMap +{ + SvxSwFramePosString::StringId eStrId; + SvxSwFramePosString::StringId eMirrorStrId; + sal_Int16 nAlign; + LB nLBRelations; +}; + + +static RelationMap const aRelationMap[] = +{ + {SwFPos::FRAME, SwFPos::FRAME, LB::Frame, text::RelOrientation::FRAME}, + {SwFPos::PRTAREA, SwFPos::PRTAREA, LB::PrintArea, text::RelOrientation::PRINT_AREA}, + {SwFPos::REL_PG_LEFT, SwFPos::MIR_REL_PG_LEFT, LB::RelPageLeft, text::RelOrientation::PAGE_LEFT}, + {SwFPos::REL_PG_RIGHT, SwFPos::MIR_REL_PG_RIGHT, LB::RelPageRight, text::RelOrientation::PAGE_RIGHT}, + {SwFPos::REL_FRM_LEFT, SwFPos::MIR_REL_FRM_LEFT, LB::RelFrameLeft, text::RelOrientation::FRAME_LEFT}, + {SwFPos::REL_FRM_RIGHT, SwFPos::MIR_REL_FRM_RIGHT, LB::RelFrameRight, text::RelOrientation::FRAME_RIGHT}, + {SwFPos::REL_PG_FRAME, SwFPos::REL_PG_FRAME, LB::RelPageFrame, text::RelOrientation::PAGE_FRAME}, + {SwFPos::REL_PG_PRTAREA, SwFPos::REL_PG_PRTAREA, LB::RelPagePrintArea, text::RelOrientation::PAGE_PRINT_AREA}, + {SwFPos::REL_CHAR, SwFPos::REL_CHAR, LB::RelChar, text::RelOrientation::CHAR}, + + {SwFPos::FLY_REL_PG_LEFT, SwFPos::FLY_MIR_REL_PG_LEFT, LB::FlyRelPageLeft, text::RelOrientation::PAGE_LEFT}, + {SwFPos::FLY_REL_PG_RIGHT, SwFPos::FLY_MIR_REL_PG_RIGHT, LB::FlyRelPageRight, text::RelOrientation::PAGE_RIGHT}, + {SwFPos::FLY_REL_PG_FRAME, SwFPos::FLY_REL_PG_FRAME, LB::FlyRelPageFrame, text::RelOrientation::PAGE_FRAME}, + {SwFPos::FLY_REL_PG_PRTAREA, SwFPos::FLY_REL_PG_PRTAREA, LB::FlyRelPagePrintArea, text::RelOrientation::PAGE_PRINT_AREA}, + + {SwFPos::REL_BORDER, SwFPos::REL_BORDER, LB::VertFrame, text::RelOrientation::FRAME}, + {SwFPos::REL_PRTAREA, SwFPos::REL_PRTAREA, LB::VertPrintArea, text::RelOrientation::PRINT_AREA}, + + {SwFPos::FLY_REL_PG_FRAME, SwFPos::FLY_REL_PG_FRAME, LB::FlyVertFrame, text::RelOrientation::FRAME}, + {SwFPos::FLY_REL_PG_PRTAREA, SwFPos::FLY_REL_PG_PRTAREA, LB::FlyVertPrintArea, text::RelOrientation::PRINT_AREA}, + + {SwFPos::REL_LINE, SwFPos::REL_LINE, LB::VertLine, text::RelOrientation::TEXT_LINE} +}; + +static RelationMap const aAsCharRelationMap[] = +{ + {SwFPos::REL_BASE, SwFPos::REL_BASE, LB::RelBase, text::RelOrientation::FRAME}, + {SwFPos::REL_CHAR, SwFPos::REL_CHAR, LB::RelChar, text::RelOrientation::FRAME}, + {SwFPos::REL_ROW, SwFPos::REL_ROW, LB::RelRow, text::RelOrientation::FRAME} +}; + +// site anchored +static constexpr auto HORI_PAGE_REL = LB::RelPageFrame | LB::RelPagePrintArea | LB::RelPageLeft | + LB::RelPageRight; + +static FrameMap const aHPageMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, HORI_PAGE_REL}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, HORI_PAGE_REL}, + {SwFPos::CENTER_HORI, SwFPos::CENTER_HORI, text::HoriOrientation::CENTER, HORI_PAGE_REL}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, HORI_PAGE_REL} +}; + +static FrameMap const aHPageHtmlMap[] = +{ + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, LB::RelPageFrame} +}; + +#define VERT_PAGE_REL (LB::RelPageFrame|LB::RelPagePrintArea) + +static FrameMap const aVPageMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, VERT_PAGE_REL}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::BOTTOM, VERT_PAGE_REL}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, VERT_PAGE_REL}, + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, VERT_PAGE_REL} +}; + +static FrameMap const aVPageHtmlMap[] = +{ + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, LB::RelPageFrame} +}; + +// frame anchored +static constexpr auto HORI_FRAME_REL = LB::FlyRelPageFrame | LB::FlyRelPagePrintArea | + LB::FlyRelPageLeft | LB::FlyRelPageRight; + +static FrameMap const aHFrameMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, HORI_FRAME_REL}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, HORI_FRAME_REL}, + {SwFPos::CENTER_HORI, SwFPos::CENTER_HORI, text::HoriOrientation::CENTER, HORI_FRAME_REL}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, HORI_FRAME_REL} +}; + +static FrameMap const aHFlyHtmlMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, LB::FlyRelPageFrame}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, LB::FlyRelPageFrame} +}; + +// own vertical alignment map for objects anchored to frame +#define VERT_FRAME_REL (LB::FlyVertFrame|LB::FlyVertPrintArea) + +static FrameMap const aVFrameMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, VERT_FRAME_REL}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::BOTTOM, VERT_FRAME_REL}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, VERT_FRAME_REL}, + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, VERT_FRAME_REL} +}; + +static FrameMap const aVFlyHtmlMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, LB::FlyVertFrame}, + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, LB::FlyVertFrame} +}; + +// paragraph anchored +static constexpr auto HORI_PARA_REL = LB::Frame | LB::PrintArea | LB::RelPageLeft | LB::RelPageRight | + LB::RelPageFrame | LB::RelPagePrintArea | LB::RelFrameLeft | + LB::RelFrameRight; + +static FrameMap const aHParaMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, HORI_PARA_REL}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, HORI_PARA_REL}, + {SwFPos::CENTER_HORI, SwFPos::CENTER_HORI, text::HoriOrientation::CENTER, HORI_PARA_REL}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, HORI_PARA_REL} +}; + +#define HTML_HORI_PARA_REL (LB::Frame|LB::PrintArea) + +static FrameMap const aHParaHtmlMap[] = +{ + {SwFPos::LEFT, SwFPos::LEFT, text::HoriOrientation::LEFT, HTML_HORI_PARA_REL}, + {SwFPos::RIGHT, SwFPos::RIGHT, text::HoriOrientation::RIGHT, HTML_HORI_PARA_REL} +}; + +static FrameMap const aHParaHtmlAbsMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, HTML_HORI_PARA_REL}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, HTML_HORI_PARA_REL} +}; + +// allow vertical alignment at page areas +static constexpr auto VERT_PARA_REL = LB::VertFrame | LB::VertPrintArea | + LB::RelPageFrame | LB::RelPagePrintArea; + +static FrameMap const aVParaMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, VERT_PARA_REL}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::BOTTOM, VERT_PARA_REL}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, VERT_PARA_REL}, + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, VERT_PARA_REL} +}; + +static FrameMap const aVParaHtmlMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, LB::VertPrintArea} +}; + +// anchored relative to the character +static constexpr auto HORI_CHAR_REL = LB::Frame|LB::PrintArea | LB::RelPageLeft | LB::RelPageRight | + LB::RelPageFrame | LB::RelPagePrintArea | LB::RelFrameLeft | + LB::RelFrameRight | LB::RelChar; + +static FrameMap const aHCharMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, HORI_CHAR_REL}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, HORI_CHAR_REL}, + {SwFPos::CENTER_HORI, SwFPos::CENTER_HORI, text::HoriOrientation::CENTER, HORI_CHAR_REL}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, HORI_CHAR_REL} +}; + +#define HTML_HORI_CHAR_REL (LB::Frame|LB::PrintArea|LB::RelChar) + +static FrameMap const aHCharHtmlMap[] = +{ + {SwFPos::LEFT, SwFPos::LEFT, text::HoriOrientation::LEFT, HTML_HORI_CHAR_REL}, + {SwFPos::RIGHT, SwFPos::RIGHT, text::HoriOrientation::RIGHT, HTML_HORI_CHAR_REL} +}; + +static FrameMap const aHCharHtmlAbsMap[] = +{ + {SwFPos::LEFT, SwFPos::MIR_LEFT, text::HoriOrientation::LEFT, LB::PrintArea|LB::RelChar}, + {SwFPos::RIGHT, SwFPos::MIR_RIGHT, text::HoriOrientation::RIGHT, LB::PrintArea}, + {SwFPos::FROMLEFT, SwFPos::MIR_FROMLEFT, text::HoriOrientation::NONE, LB::RelPageFrame} +}; + +// allow vertical alignment at page areas +static constexpr auto VERT_CHAR_REL = LB::VertFrame | LB::VertPrintArea | + LB::RelPageFrame | LB::RelPagePrintArea; + +static FrameMap const aVCharMap[] = +{ + // introduce mappings for new vertical alignment at top of line <LB::VertLine> + // and correct mapping for vertical alignment at character for position <FROM_BOTTOM> + // Note: Because of these adjustments the map becomes ambiguous in its values + // <eStrId>/<eMirrorStrId> and <nAlign>. These ambiguities are considered + // in the methods <SwFramePage::FillRelLB(..)>, <SwFramePage::GetAlignment(..)> + // and <SwFramePage::FillPosLB(..)> + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, VERT_CHAR_REL|LB::RelChar}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::BOTTOM, VERT_CHAR_REL|LB::RelChar}, + {SwFPos::BELOW, SwFPos::BELOW, text::VertOrientation::CHAR_BOTTOM, LB::RelChar}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, VERT_CHAR_REL|LB::RelChar}, + {SwFPos::FROMTOP, SwFPos::FROMTOP, text::VertOrientation::NONE, VERT_CHAR_REL}, + {SwFPos::FROMBOTTOM, SwFPos::FROMBOTTOM, text::VertOrientation::NONE, LB::RelChar|LB::VertLine}, + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::LINE_TOP, LB::VertLine}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::LINE_BOTTOM, LB::VertLine}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::LINE_CENTER, LB::VertLine} +}; + +static FrameMap const aVCharHtmlMap[] = +{ + {SwFPos::BELOW, SwFPos::BELOW, text::VertOrientation::CHAR_BOTTOM, LB::RelChar} +}; + +static FrameMap const aVCharHtmlAbsMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, LB::RelChar}, + {SwFPos::BELOW, SwFPos::BELOW, text::VertOrientation::CHAR_BOTTOM, LB::RelChar} +}; + +// anchored as character +static FrameMap const aVAsCharMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, LB::RelBase}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::BOTTOM, LB::RelBase}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, LB::RelBase}, + + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::CHAR_TOP, LB::RelChar}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::CHAR_BOTTOM, LB::RelChar}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CHAR_CENTER, LB::RelChar}, + + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::LINE_TOP, LB::RelRow}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::LINE_BOTTOM, LB::RelRow}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::LINE_CENTER, LB::RelRow}, + + {SwFPos::FROMBOTTOM, SwFPos::FROMBOTTOM, text::VertOrientation::NONE, LB::RelBase} +}; + +static FrameMap const aVAsCharHtmlMap[] = +{ + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::TOP, LB::RelBase}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::CENTER, LB::RelBase}, + + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::CHAR_TOP, LB::RelChar}, + + {SwFPos::TOP, SwFPos::TOP, text::VertOrientation::LINE_TOP, LB::RelRow}, + {SwFPos::BOTTOM, SwFPos::BOTTOM, text::VertOrientation::LINE_BOTTOM, LB::RelRow}, + {SwFPos::CENTER_VERT, SwFPos::CENTER_VERT, text::VertOrientation::LINE_CENTER, LB::RelRow} +}; + +const sal_uInt16 SwFramePage::aPageRg[] = { + RES_FRM_SIZE, RES_FRM_SIZE, + RES_VERT_ORIENT, RES_ANCHOR, + RES_COL, RES_COL, + RES_FOLLOW_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW, + 0 +}; +const sal_uInt16 SwFrameAddPage::aAddPgRg[] = { + RES_PROTECT, RES_PROTECT, + RES_PRINT, RES_PRINT, + FN_SET_FRM_NAME, FN_SET_FRM_NAME, + FN_SET_FRM_ALT_NAME, FN_SET_FRM_ALT_NAME, + FN_UNO_DESCRIPTION, FN_UNO_DESCRIPTION, + 0 +}; + +static size_t lcl_GetFrameMapCount( const FrameMap* pMap) +{ + if ( pMap ) + { + if( pMap == aVParaHtmlMap) + return SAL_N_ELEMENTS(aVParaHtmlMap); + if( pMap == aVAsCharHtmlMap) + return SAL_N_ELEMENTS(aVAsCharHtmlMap); + if( pMap == aHParaHtmlMap) + return SAL_N_ELEMENTS(aHParaHtmlMap); + if( pMap == aHParaHtmlAbsMap) + return SAL_N_ELEMENTS(aHParaHtmlAbsMap); + if ( pMap == aVPageMap ) + return SAL_N_ELEMENTS(aVPageMap); + if ( pMap == aVPageHtmlMap ) + return SAL_N_ELEMENTS(aVPageHtmlMap); + if ( pMap == aVAsCharMap ) + return SAL_N_ELEMENTS(aVAsCharMap); + if ( pMap == aVParaMap ) + return SAL_N_ELEMENTS(aVParaMap); + if ( pMap == aHParaMap ) + return SAL_N_ELEMENTS(aHParaMap); + if ( pMap == aHFrameMap ) + return SAL_N_ELEMENTS(aHFrameMap); + if ( pMap == aVFrameMap ) + return SAL_N_ELEMENTS(aVFrameMap); + if ( pMap == aHCharMap ) + return SAL_N_ELEMENTS(aHCharMap); + if ( pMap == aHCharHtmlMap ) + return SAL_N_ELEMENTS(aHCharHtmlMap); + if ( pMap == aHCharHtmlAbsMap ) + return SAL_N_ELEMENTS(aHCharHtmlAbsMap); + if ( pMap == aVCharMap ) + return SAL_N_ELEMENTS(aVCharMap); + if ( pMap == aVCharHtmlMap ) + return SAL_N_ELEMENTS(aVCharHtmlMap); + if ( pMap == aVCharHtmlAbsMap ) + return SAL_N_ELEMENTS(aVCharHtmlAbsMap); + if ( pMap == aHPageHtmlMap ) + return SAL_N_ELEMENTS(aHPageHtmlMap); + if ( pMap == aHFlyHtmlMap ) + return SAL_N_ELEMENTS(aHFlyHtmlMap); + if ( pMap == aVFlyHtmlMap ) + return SAL_N_ELEMENTS(aVFlyHtmlMap); + return SAL_N_ELEMENTS(aHPageMap); + } + return 0; +} + +static void lcl_InsertVectors(weld::ComboBox& rBox, + const std::vector< OUString >& rPrev, const std::vector< OUString >& rThis, + const std::vector< OUString >& rNext, const std::vector< OUString >& rRemain) +{ + for(const auto& rItem : rPrev) + rBox.append_text(rItem); + for(const auto& rItem : rThis) + rBox.append_text(rItem); + for(const auto& rItem : rNext) + rBox.append_text(rItem); + rBox.append_separator(""); + //now insert all strings sorted + const auto nStartPos = rBox.get_count(); + + for(const auto& rItem : rPrev) + ::InsertStringSorted("", rItem, rBox, nStartPos ); + for(const auto& rItem : rThis) + ::InsertStringSorted("", rItem, rBox, nStartPos ); + for(const auto& rItem : rNext) + ::InsertStringSorted("", rItem, rBox, nStartPos ); + for(const auto& rItem : rRemain) + ::InsertStringSorted("", rItem, rBox, nStartPos ); +} + +// --> OD 2009-08-31 #mongolianlayout# +// add input parameter +static SvxSwFramePosString::StringId lcl_ChangeResIdToVerticalOrRTL(SvxSwFramePosString::StringId eStringId, bool bVertical, bool bVerticalL2R, bool bRTL) +{ + //special handling of STR_FROMLEFT + if ( SwFPos::FROMLEFT == eStringId ) + { + eStringId = bVertical + ? ( bRTL + ? SwFPos::FROMBOTTOM + : SwFPos::FROMTOP ) + : ( bRTL + ? SwFPos::FROMRIGHT + : SwFPos::FROMLEFT ); + return eStringId; + } + // --> OD 2009-08-31 #mongolianlayout# + // special handling of STR_FROMTOP in case of mongolianlayout (vertical left-to-right) + if ( SwFPos::FROMTOP == eStringId && + bVertical && bVerticalL2R ) + { + eStringId = SwFPos::FROMLEFT; + return eStringId; + } + if ( bVertical ) + { + //exchange horizontal strings with vertical strings and vice versa + static const StringIdPair_Impl aHoriIds[] = + { + {SwFPos::LEFT, SwFPos::TOP}, + {SwFPos::RIGHT, SwFPos::BOTTOM}, + {SwFPos::CENTER_HORI, SwFPos::CENTER_VERT}, + {SwFPos::FROMTOP, SwFPos::FROMRIGHT}, + {SwFPos::REL_PG_LEFT, SwFPos::REL_PG_TOP}, + {SwFPos::REL_PG_RIGHT, SwFPos::REL_PG_BOTTOM} , + {SwFPos::REL_FRM_LEFT, SwFPos::REL_FRM_TOP}, + {SwFPos::REL_FRM_RIGHT, SwFPos::REL_FRM_BOTTOM} + }; + static const StringIdPair_Impl aVertIds[] = + { + {SwFPos::TOP, SwFPos::RIGHT}, + {SwFPos::BOTTOM, SwFPos::LEFT }, + {SwFPos::CENTER_VERT, SwFPos::CENTER_HORI}, + {SwFPos::FROMTOP, SwFPos::FROMRIGHT }, + {SwFPos::REL_PG_TOP, SwFPos::REL_PG_LEFT }, + {SwFPos::REL_PG_BOTTOM, SwFPos::REL_PG_RIGHT } , + {SwFPos::REL_FRM_TOP, SwFPos::REL_FRM_LEFT }, + {SwFPos::REL_FRM_BOTTOM, SwFPos::REL_FRM_RIGHT } + }; + // --> OD 2009-08-31 #monglianlayout# + static const StringIdPair_Impl aVertL2RIds[] = + { + {SwFPos::TOP, SwFPos::LEFT }, + {SwFPos::BOTTOM, SwFPos::RIGHT }, + {SwFPos::CENTER_VERT, SwFPos::CENTER_HORI }, + {SwFPos::FROMTOP, SwFPos::FROMLEFT }, + {SwFPos::REL_PG_TOP, SwFPos::REL_PG_LEFT }, + {SwFPos::REL_PG_BOTTOM, SwFPos::REL_PG_RIGHT } , + {SwFPos::REL_FRM_TOP, SwFPos::REL_FRM_LEFT }, + {SwFPos::REL_FRM_BOTTOM, SwFPos::REL_FRM_RIGHT } + }; + for(const StringIdPair_Impl & rHoriId : aHoriIds) + { + if(rHoriId.eHori == eStringId) + { + eStringId = rHoriId.eVert; + return eStringId; + } + } + for(size_t nIndex = 0; nIndex < SAL_N_ELEMENTS(aVertIds); ++nIndex) + { + // --> OD 2009-08-31 #mongolianlayout# + if ( !bVerticalL2R ) + { + if(aVertIds[nIndex].eHori == eStringId) + { + eStringId = aVertIds[nIndex].eVert; + break; + } + } + else + { + if(aVertL2RIds[nIndex].eHori == eStringId) + { + eStringId = aVertL2RIds[nIndex].eVert; + break; + } + } + } + } + return eStringId; +} + +// helper method in order to determine all possible +// listbox relations in a relation map for a given relation +static LB lcl_GetLBRelationsForRelations( const sal_Int16 _nRel ) +{ + LB nLBRelations = LB::NONE; + + for (RelationMap const & i : aRelationMap) + { + if ( i.nRelation == _nRel ) + { + nLBRelations |= i.nLBRelation; + } + } + + return nLBRelations; +} + +// helper method on order to determine all possible +// listbox relations in a relation map for a given string ID +static LB lcl_GetLBRelationsForStrID( const FrameMap* _pMap, + const SvxSwFramePosString::StringId _eStrId, + const bool _bUseMirrorStr ) +{ + LB nLBRelations = LB::NONE; + + size_t nRelMapSize = lcl_GetFrameMapCount( _pMap ); + for ( size_t nRelMapPos = 0; nRelMapPos < nRelMapSize; ++nRelMapPos ) + { + if ( ( !_bUseMirrorStr && _pMap[nRelMapPos].eStrId == _eStrId ) || + ( _bUseMirrorStr && _pMap[nRelMapPos].eMirrorStrId == _eStrId ) ) + { + nLBRelations |= _pMap[nRelMapPos].nLBRelations; + } + } + + return nLBRelations; +} + +// standard frame TabPage +namespace +{ + void HandleAutoCB(bool _bChecked, weld::Label& _rFT_man, weld::Label& _rFT_auto, weld::MetricSpinButton& _rPF_Edit) + { + _rFT_man.set_visible( !_bChecked ); + _rFT_auto.set_visible( _bChecked ); + OUString accName = _bChecked ? _rFT_auto.get_label() : _rFT_man.get_label(); + _rPF_Edit.set_accessible_name(accName); + } +} + +SwFramePage::SwFramePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/frmtypepage.ui", "FrameTypePage", &rSet) + , m_bAtHorzPosModified(false) + , m_bAtVertPosModified(false) + , m_bFormat(false) + , m_bNew(true) + , m_bNoModifyHdl(true) + , m_bIsVerticalFrame(false) + , m_bIsVerticalL2R(false) + , m_bIsInRightToLeft(false) + , m_bHtmlMode(false) + , m_nHtmlMode(0) + , m_nUpperBorder(0) + , m_nLowerBorder(0) + , m_fWidthHeightRatio(1.0) + , mpToCharContentPos(nullptr) + , m_nOldH(text::HoriOrientation::CENTER) + , m_nOldHRel(text::RelOrientation::FRAME) + , m_nOldV(text::VertOrientation::TOP) + , m_nOldVRel(text::RelOrientation::PRINT_AREA) + , m_pVMap(nullptr) + , m_pHMap(nullptr) + , m_bAllowVertPositioning( true ) + , m_bIsMathOLE(false) + , m_bIsMathBaselineAlignment(true) + , m_xWidthFT(m_xBuilder->weld_label("widthft")) + , m_xWidthAutoFT(m_xBuilder->weld_label("autowidthft")) + , m_xRelWidthCB(m_xBuilder->weld_check_button("relwidth")) + , m_xRelWidthRelationLB(m_xBuilder->weld_combo_box("relwidthrelation")) + , m_xAutoWidthCB(m_xBuilder->weld_check_button("autowidth")) + , m_xHeightFT(m_xBuilder->weld_label("heightft")) + , m_xHeightAutoFT(m_xBuilder->weld_label("autoheightft")) + , m_xRelHeightCB(m_xBuilder->weld_check_button("relheight")) + , m_xRelHeightRelationLB(m_xBuilder->weld_combo_box("relheightrelation")) + , m_xAutoHeightCB(m_xBuilder->weld_check_button("autoheight")) + , m_xFixedRatioCB(m_xBuilder->weld_check_button("ratio")) + , m_xRealSizeBT(m_xBuilder->weld_button("origsize")) + , m_xAnchorFrame(m_xBuilder->weld_widget("anchorframe")) + , m_xAnchorAtPageRB(m_xBuilder->weld_radio_button("topage")) + , m_xAnchorAtParaRB(m_xBuilder->weld_radio_button("topara")) + , m_xAnchorAtCharRB(m_xBuilder->weld_radio_button("tochar")) + , m_xAnchorAsCharRB(m_xBuilder->weld_radio_button("aschar")) + , m_xAnchorAtFrameRB(m_xBuilder->weld_radio_button("toframe")) + , m_xHorizontalFT(m_xBuilder->weld_label("horiposft")) + , m_xHorizontalDLB(m_xBuilder->weld_combo_box("horipos")) + , m_xAtHorzPosFT(m_xBuilder->weld_label("horibyft")) + , m_xAtHorzPosED(m_xBuilder->weld_metric_spin_button("byhori", FieldUnit::CM)) + , m_xHoriRelationFT(m_xBuilder->weld_label("horitoft")) + , m_xHoriRelationLB(m_xBuilder->weld_combo_box("horianchor")) + , m_xMirrorPagesCB(m_xBuilder->weld_check_button("mirror")) + , m_xVerticalFT(m_xBuilder->weld_label("vertposft")) + , m_xVerticalDLB(m_xBuilder->weld_combo_box("vertpos")) + , m_xAtVertPosFT(m_xBuilder->weld_label("vertbyft")) + , m_xAtVertPosED(m_xBuilder->weld_metric_spin_button("byvert", FieldUnit::CM)) + , m_xVertRelationFT(m_xBuilder->weld_label("verttoft")) + , m_xVertRelationLB(m_xBuilder->weld_combo_box("vertanchor")) + , m_xFollowTextFlowCB(m_xBuilder->weld_check_button("followtextflow")) + , m_xExampleWN(new weld::CustomWeld(*m_xBuilder, "preview", m_aExampleWN)) + , m_xWidthED(new SwPercentField(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM))) + , m_xHeightED(new SwPercentField(m_xBuilder->weld_metric_spin_button("height", FieldUnit::CM))) +{ + const auto nWidthRequest = m_xAtHorzPosED->get_preferred_size().Width(); + m_xAtHorzPosED->set_size_request(nWidthRequest, -1); + m_xAtVertPosED->set_size_request(nWidthRequest, -1); + + setOptimalFrameWidth(); + setOptimalRelWidth(); + + SetExchangeSupport(); + + Link<weld::MetricSpinButton&,void> aLk3 = LINK(this, SwFramePage, ModifyHdl); + m_xWidthED->connect_value_changed( aLk3 ); + m_xHeightED->connect_value_changed( aLk3 ); + m_xAtHorzPosED->connect_value_changed( aLk3 ); + m_xAtVertPosED->connect_value_changed( aLk3 ); + m_xFollowTextFlowCB->connect_toggled(LINK(this, SwFramePage, RangeModifyClickHdl)); + + Link<weld::ToggleButton&,void> aLk2 = LINK(this, SwFramePage, AnchorTypeHdl); + m_xAnchorAtPageRB->connect_toggled( aLk2 ); + m_xAnchorAtParaRB->connect_toggled( aLk2 ); + m_xAnchorAtCharRB->connect_toggled( aLk2 ); + m_xAnchorAsCharRB->connect_toggled( aLk2 ); + m_xAnchorAtFrameRB->connect_toggled( aLk2 ); + + m_xHorizontalDLB->connect_changed(LINK(this, SwFramePage, PosHdl)); + m_xVerticalDLB->connect_changed(LINK(this, SwFramePage, PosHdl)); + + m_xHoriRelationLB->connect_changed(LINK(this, SwFramePage, RelHdl)); + m_xVertRelationLB->connect_changed(LINK(this, SwFramePage, RelHdl)); + + m_xMirrorPagesCB->connect_toggled(LINK(this, SwFramePage, MirrorHdl)); + + aLk2 = LINK(this, SwFramePage, RelSizeClickHdl); + m_xRelWidthCB->connect_toggled(aLk2); + m_xRelHeightCB->connect_toggled(aLk2); + + m_xAutoWidthCB->connect_toggled(LINK(this, SwFramePage, AutoWidthClickHdl)); + m_xAutoHeightCB->connect_toggled(LINK(this, SwFramePage, AutoHeightClickHdl)); + + if (comphelper::LibreOfficeKit::isActive()) + { + m_xAnchorAtPageRB->hide(); + m_xAnchorAtParaRB->hide(); + m_xAnchorAtFrameRB->hide(); + } +} + +SwFramePage::~SwFramePage() +{ +} + +namespace +{ + struct FrameMaps + { + FrameMap const * pMap; + size_t nCount; + }; +} + +void SwFramePage::setOptimalFrameWidth() +{ + static FrameMaps const aMaps[] = { + { aHPageMap, SAL_N_ELEMENTS(aHPageMap) }, + { aHPageHtmlMap, SAL_N_ELEMENTS(aHPageHtmlMap) }, + { aVPageMap, SAL_N_ELEMENTS(aVPageMap) }, + { aVPageHtmlMap, SAL_N_ELEMENTS(aVPageHtmlMap) }, + { aHFrameMap, SAL_N_ELEMENTS(aHFrameMap) }, + { aHFlyHtmlMap, SAL_N_ELEMENTS(aHFlyHtmlMap) }, + { aVFrameMap, SAL_N_ELEMENTS(aVFrameMap) }, + { aVFlyHtmlMap, SAL_N_ELEMENTS(aVFlyHtmlMap) }, + { aHParaMap, SAL_N_ELEMENTS(aHParaMap) }, + { aHParaHtmlMap, SAL_N_ELEMENTS(aHParaHtmlMap) }, + { aHParaHtmlAbsMap, SAL_N_ELEMENTS(aHParaHtmlAbsMap) }, + { aVParaMap, SAL_N_ELEMENTS(aVParaMap) }, + { aVParaHtmlMap, SAL_N_ELEMENTS(aVParaHtmlMap) }, + { aHCharMap, SAL_N_ELEMENTS(aHCharMap) }, + { aHCharHtmlMap, SAL_N_ELEMENTS(aHCharHtmlMap) }, + { aHCharHtmlAbsMap, SAL_N_ELEMENTS(aHCharHtmlAbsMap) }, + { aVCharMap, SAL_N_ELEMENTS(aVCharMap) }, + { aVCharHtmlMap, SAL_N_ELEMENTS(aVCharHtmlMap) }, + { aVCharHtmlAbsMap, SAL_N_ELEMENTS(aVCharHtmlAbsMap) }, + { aVAsCharMap, SAL_N_ELEMENTS(aVAsCharMap) }, + { aVAsCharHtmlMap, SAL_N_ELEMENTS(aVAsCharHtmlMap) } + }; + + std::vector<SvxSwFramePosString::StringId> aFrames; + for (const FrameMaps & rMap : aMaps) + { + for (size_t j = 0; j < rMap.nCount; ++j) + { + aFrames.push_back(rMap.pMap[j].eStrId); + aFrames.push_back(rMap.pMap[j].eMirrorStrId); + } + } + + std::sort(aFrames.begin(), aFrames.end()); + aFrames.erase(std::unique(aFrames.begin(), aFrames.end()), aFrames.end()); + + for (const auto& rFrame : aFrames) + { + m_xHorizontalDLB->append_text(SvxSwFramePosString::GetString(rFrame)); + } + + Size aBiggest(m_xHorizontalDLB->get_preferred_size()); + m_xHorizontalDLB->set_size_request(aBiggest.Width(), -1); + m_xVerticalDLB->set_size_request(aBiggest.Width(), -1); + m_xHorizontalDLB->clear(); +} + +namespace +{ + struct RelationMaps + { + RelationMap const * pMap; + size_t nCount; + }; +} + +void SwFramePage::setOptimalRelWidth() +{ + static const RelationMaps aMaps[] = { + { aRelationMap, SAL_N_ELEMENTS(aRelationMap) }, + { aAsCharRelationMap, SAL_N_ELEMENTS(aAsCharRelationMap) } + }; + + std::vector<SvxSwFramePosString::StringId> aRels; + for (const RelationMaps & rMap : aMaps) + { + for (size_t j = 0; j < rMap.nCount; ++j) + { + aRels.push_back(rMap.pMap[j].eStrId); + aRels.push_back(rMap.pMap[j].eMirrorStrId); + } + } + + std::sort(aRels.begin(), aRels.end()); + aRels.erase(std::unique(aRels.begin(), aRels.end()), aRels.end()); + + for (const auto& rRel : aRels) + { + m_xHoriRelationLB->append_text(SvxSwFramePosString::GetString(rRel)); + } + + Size aBiggest(m_xHoriRelationLB->get_preferred_size()); + m_xHoriRelationLB->set_size_request(aBiggest.Width(), -1); + m_xVertRelationLB->set_size_request(aBiggest.Width(), -1); + m_xRelWidthRelationLB->set_size_request(aBiggest.Width(), -1); + m_xRelHeightRelationLB->set_size_request(aBiggest.Width(), -1); + m_xHoriRelationLB->clear(); +} + +std::unique_ptr<SfxTabPage> SwFramePage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwFramePage>(pPage, pController, *rSet); +} + +void SwFramePage::EnableGraficMode() +{ + // i#39692 - mustn't be called more than once + if (!m_xRealSizeBT->get_visible()) + { + m_xWidthFT->show(); + m_xWidthAutoFT->hide(); + m_xAutoHeightCB->hide(); + + m_xHeightFT->show(); + m_xHeightAutoFT->hide(); + m_xAutoWidthCB->hide(); + + m_xRealSizeBT->show(); + } +} + +SwWrtShell *SwFramePage::getFrameDlgParentShell() +{ + return static_cast<SwFrameDlg*>(GetDialogController())->GetWrtShell(); +} + +void SwFramePage::Reset( const SfxItemSet *rSet ) +{ + SwWrtShell* pSh = m_bFormat ? ::GetActiveWrtShell() : + getFrameDlgParentShell(); + + m_nHtmlMode = ::GetHtmlMode(pSh->GetView().GetDocShell()); + m_bHtmlMode = (m_nHtmlMode & HTMLMODE_ON) != 0; + + FieldUnit aMetric = ::GetDfltMetric(m_bHtmlMode); + m_xWidthED->SetMetric(aMetric); + m_xHeightED->SetMetric(aMetric); + ::SetFieldUnit(*m_xAtHorzPosED, aMetric); + ::SetFieldUnit(*m_xAtVertPosED, aMetric); + + const SfxPoolItem* pItem = nullptr; + const SwFormatAnchor& rAnchor = rSet->Get(RES_ANCHOR); + + if (SfxItemState::SET == rSet->GetItemState(FN_OLE_IS_MATH, false, &pItem)) + m_bIsMathOLE = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + if (SfxItemState::SET == rSet->GetItemState(FN_MATH_BASELINE_ALIGNMENT, false, &pItem)) + m_bIsMathBaselineAlignment = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + EnableVerticalPositioning( !(m_bIsMathOLE && m_bIsMathBaselineAlignment + && RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ); + + if (m_bFormat) + { + // at formats no anchor editing + m_xAnchorFrame->set_sensitive(false); + m_xFixedRatioCB->set_sensitive(false); + } + else + { + if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_FLY && !pSh->IsFlyInFly()) + m_xAnchorAtFrameRB->hide(); + if ( pSh->IsFrameVertical( true, m_bIsInRightToLeft, m_bIsVerticalL2R ) ) + { + OUString sHLabel = m_xHorizontalFT->get_label(); + m_xHorizontalFT->set_label(m_xVerticalFT->get_label()); + m_xVerticalFT->set_label(sHLabel); + m_bIsVerticalFrame = true; + } + } + + if ( m_sDlgType == "PictureDialog" || m_sDlgType == "ObjectDialog" ) + { + OSL_ENSURE(pSh , "shell not found"); + //OS: only for the variant Insert/Graphic/Properties + if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_GRF_REALSIZE, false, &pItem)) + m_aGrfSize = static_cast<const SvxSizeItem*>(pItem)->GetSize(); + else + pSh->GetGrfSize( m_aGrfSize ); + + if ( !m_bNew ) + { + m_xRealSizeBT->connect_clicked(LINK(this, SwFramePage, RealSizeHdl)); + EnableGraficMode(); + } + + if (m_sDlgType == "PictureDialog") + m_xFixedRatioCB->set_active(false); + else + { + if ( m_bNew ) + SetPageTitle(SwResId(STR_FRMUI_OLE_INSERT)); + else + SetPageTitle(SwResId(STR_FRMUI_OLE_EDIT)); + } + } + else + { + m_aGrfSize = rSet->Get(RES_FRM_SIZE).GetSize(); + } + + // entering percent value made possible + + // the available space is not yet known so the RefValue has to be calculated from size and relative size values + // this is needed only if relative values are already set + const SwFormatFrameSize& rFrameSize = rSet->Get(RES_FRM_SIZE); + + m_xRelWidthRelationLB->append_text(SvxSwFramePosString::GetString(SwFPos::FRAME)); + m_xRelWidthRelationLB->append_text(SvxSwFramePosString::GetString(SwFPos::REL_PG_FRAME)); + if (rFrameSize.GetWidthPercent() != SwFormatFrameSize::SYNCED && rFrameSize.GetWidthPercent() != 0) + { + //calculate the reference value from the width and relative width values + sal_Int32 nSpace = rFrameSize.GetWidth() * 100 / rFrameSize.GetWidthPercent(); + m_xWidthED->SetRefValue( nSpace ); + + m_xRelWidthRelationLB->set_sensitive(true); + } + else + m_xRelWidthRelationLB->set_sensitive(false); + + m_xRelHeightRelationLB->append_text(SvxSwFramePosString::GetString(SwFPos::FRAME)); + m_xRelHeightRelationLB->append_text(SvxSwFramePosString::GetString(SwFPos::REL_PG_FRAME)); + if (rFrameSize.GetHeightPercent() != SwFormatFrameSize::SYNCED && rFrameSize.GetHeightPercent() != 0) + { + //calculate the reference value from the with and relative width values + sal_Int32 nSpace = rFrameSize.GetHeight() * 100 / rFrameSize.GetHeightPercent(); + m_xHeightED->SetRefValue( nSpace ); + + m_xRelHeightRelationLB->set_sensitive(true); + } + else + m_xRelHeightRelationLB->set_sensitive(false); + + // general initialisation part + switch(rAnchor.GetAnchorId()) + { + case RndStdIds::FLY_AT_PAGE: m_xAnchorAtPageRB->set_active(true); break; + case RndStdIds::FLY_AT_PARA: m_xAnchorAtParaRB->set_active(true); break; + case RndStdIds::FLY_AT_CHAR: m_xAnchorAtCharRB->set_active(true); break; + case RndStdIds::FLY_AS_CHAR: m_xAnchorAsCharRB->set_active(true); break; + case RndStdIds::FLY_AT_FLY: m_xAnchorAtFrameRB->set_active(true);break; + default:; //prevent warning + } + + // i#22341 - determine content position of character + // Note: content position can be NULL + mpToCharContentPos = rAnchor.GetContentAnchor(); + + // i#18732 - init checkbox value + { + const bool bFollowTextFlow = + rSet->Get(RES_FOLLOW_TEXT_FLOW).GetValue(); + m_xFollowTextFlowCB->set_active(bFollowTextFlow); + } + + if(m_bHtmlMode) + { + m_xAutoHeightCB->set_sensitive(false); + m_xAutoWidthCB->set_sensitive(false); + m_xMirrorPagesCB->hide(); + if (m_sDlgType == "FrameDialog") + m_xFixedRatioCB->set_sensitive(false); + // i#18732 hide checkbox in HTML mode + m_xFollowTextFlowCB->hide(); + } + else + { + // enable/disable of check box 'Mirror on..' + m_xMirrorPagesCB->set_sensitive(!m_xAnchorAsCharRB->get_active()); + + // enable/disable check box 'Follow text flow'. + // enable check box 'Follow text + // flow' also for anchor type to-frame. + m_xFollowTextFlowCB->set_sensitive(m_xAnchorAtParaRB->get_active() || + m_xAnchorAtCharRB->get_active() || + m_xAnchorAtFrameRB->get_active()); + } + + Init(*rSet); + m_xAtVertPosED->save_value(); + m_xAtHorzPosED->save_value(); + m_xFollowTextFlowCB->save_state(); + + m_xWidthED->save_value(); + m_xHeightED->save_value(); + + m_bNoModifyHdl = false; + //lock PercentFields + m_xWidthED->LockAutoCalculation(true); + m_xHeightED->LockAutoCalculation(true); + RangeModifyHdl(); // set all maximum values initially + m_xHeightED->LockAutoCalculation(false); + m_xWidthED->LockAutoCalculation(false); + + m_xAutoHeightCB->save_state(); + m_xAutoWidthCB->save_state(); + + SwTwips nWidth = static_cast< SwTwips >(m_xWidthED->DenormalizePercent(m_xWidthED->get_value(FieldUnit::TWIP))); + SwTwips nHeight = static_cast< SwTwips >(m_xHeightED->DenormalizePercent(m_xHeightED->get_value(FieldUnit::TWIP))); + m_fWidthHeightRatio = nHeight ? double(nWidth) / double(nHeight) : 1.0; +} + +// stuff attributes into the set when OK +bool SwFramePage::FillItemSet(SfxItemSet *rSet) +{ + bool bRet = false; + SwWrtShell* pSh = m_bFormat ? ::GetActiveWrtShell() + : getFrameDlgParentShell(); + OSL_ENSURE( pSh , "shell not found"); + const SfxItemSet& rOldSet = GetItemSet(); + const SfxPoolItem* pOldItem = nullptr; + + RndStdIds eAnchorId = GetAnchor(); + + if ( !m_bFormat ) + { + pOldItem = GetOldItem(*rSet, RES_ANCHOR); + if (m_bNew || !pOldItem || eAnchorId != static_cast<const SwFormatAnchor*>(pOldItem)->GetAnchorId()) + { + SwFormatAnchor aAnc( eAnchorId, pSh->GetPhyPageNum() ); + bRet = nullptr != rSet->Put( aAnc ); + } + } + + if ( m_pHMap ) + { + SwFormatHoriOrient aHoriOrient( rOldSet.Get(RES_HORI_ORIENT) ); + + const sal_Int32 nMapPos = GetMapPos(m_pHMap, *m_xHorizontalDLB); + const sal_Int16 eHOri = GetAlignment(m_pHMap, nMapPos, *m_xHoriRelationLB); + const sal_Int16 eRel = GetRelation(*m_xHoriRelationLB); + + aHoriOrient.SetHoriOrient( eHOri ); + aHoriOrient.SetRelationOrient( eRel ); + aHoriOrient.SetPosToggle(m_xMirrorPagesCB->get_active()); + + bool bMod = m_xAtHorzPosED->get_value_changed_from_saved(); + bMod |= m_xMirrorPagesCB->get_state_changed_from_saved(); + + if ( eHOri == text::HoriOrientation::NONE && + (m_bNew || (m_bAtHorzPosModified || bMod) || m_nOldH != eHOri ) ) + { + SwTwips nX = static_cast< SwTwips >(m_xAtHorzPosED->denormalize(m_xAtHorzPosED->get_value(FieldUnit::TWIP))); + aHoriOrient.SetPos( nX ); + } + + pOldItem = GetOldItem(*rSet, FN_HORI_ORIENT); + bool bSame = false; + if ((m_bNew == m_bFormat) && pOldItem) + { + bSame = aHoriOrient == static_cast<const SwFormatHoriOrient&>(*pOldItem); + } + if ((m_bNew && !m_bFormat) || ((m_bAtHorzPosModified || bMod) && !bSame)) + { + bRet |= nullptr != rSet->Put( aHoriOrient ); + } + } + + if ( m_pVMap ) + { + // alignment vertical + SwFormatVertOrient aVertOrient( rOldSet.Get(RES_VERT_ORIENT) ); + + const sal_Int32 nMapPos = GetMapPos(m_pVMap, *m_xVerticalDLB); + const sal_Int16 eVOri = GetAlignment(m_pVMap, nMapPos, *m_xVertRelationLB); + const sal_Int16 eRel = GetRelation(*m_xVertRelationLB); + + aVertOrient.SetVertOrient ( eVOri); + aVertOrient.SetRelationOrient( eRel ); + + bool bMod = m_xAtVertPosED->get_value_changed_from_saved(); + + if ( eVOri == text::VertOrientation::NONE && + ( m_bNew || (m_bAtVertPosModified || bMod) || m_nOldV != eVOri) ) + { + // vertical position + // recalculate offset for character bound frames + SwTwips nY = static_cast< SwTwips >(m_xAtVertPosED->denormalize(m_xAtVertPosED->get_value(FieldUnit::TWIP))); + if (eAnchorId == RndStdIds::FLY_AS_CHAR) + { + nY *= -1; + } + aVertOrient.SetPos( nY ); + } + pOldItem = GetOldItem(*rSet, FN_VERT_ORIENT); + bool bSame = false; + if((m_bNew == m_bFormat) && pOldItem) + { + bSame = m_bFormat ? + aVertOrient.GetVertOrient() == static_cast<const SwFormatVertOrient*>(pOldItem)->GetVertOrient() && + aVertOrient.GetRelationOrient() == static_cast<const SwFormatVertOrient*>(pOldItem)->GetRelationOrient() && + aVertOrient.GetPos() == static_cast<const SwFormatVertOrient*>(pOldItem)->GetPos() + : aVertOrient == static_cast<const SwFormatVertOrient&>(*pOldItem); + } + if( ( m_bNew && !m_bFormat ) || ((m_bAtVertPosModified || bMod) && !bSame )) + { + bRet |= nullptr != rSet->Put( aVertOrient ); + } + } + + // set size + // new exception: when the size of pMgr(, 0), then the properties + // for a graphic that isn't even loaded, are set. Then no SetSize + // is done here when the size settings were not changed by the + // user. + const SwFormatFrameSize& rOldSize = rOldSet.Get(RES_FRM_SIZE); + SwFormatFrameSize aSz( rOldSize ); + + auto nRelWidthRelation = m_xRelWidthRelationLB->get_active(); + if (nRelWidthRelation != -1) + { + if (nRelWidthRelation == 0) + aSz.SetWidthPercentRelation(text::RelOrientation::FRAME); + else if (nRelWidthRelation == 1) + aSz.SetWidthPercentRelation(text::RelOrientation::PAGE_FRAME); + } + auto nRelHeightRelation = m_xRelHeightRelationLB->get_active(); + if (nRelHeightRelation != -1) + { + if (nRelHeightRelation == 0) + aSz.SetHeightPercentRelation(text::RelOrientation::FRAME); + else if (nRelHeightRelation == 1) + aSz.SetHeightPercentRelation(text::RelOrientation::PAGE_FRAME); + } + + bool bValueModified = m_xWidthED->get_value_changed_from_saved() || + m_xHeightED->get_value_changed_from_saved(); + bool bCheckChanged = m_xRelWidthCB->get_state_changed_from_saved() || + m_xRelHeightCB->get_state_changed_from_saved(); + + bool bLegalValue = !(!rOldSize.GetWidth () && !rOldSize.GetHeight() && + m_xWidthED->get_value() == m_xWidthED->get_min() && + m_xHeightED->get_value() == m_xHeightED->get_min()); + + if ((m_bNew && !m_bFormat) || ((bValueModified || bCheckChanged) && bLegalValue)) + { + sal_Int64 nNewWidth = m_xWidthED->DenormalizePercent(m_xWidthED->GetRealValue(FieldUnit::TWIP)); + sal_Int64 nNewHeight = m_xHeightED->DenormalizePercent(m_xHeightED->GetRealValue(FieldUnit::TWIP)); + aSz.SetWidth (static_cast< SwTwips >(nNewWidth)); + aSz.SetHeight(static_cast< SwTwips >(nNewHeight)); + + if (m_xRelWidthCB->get_active()) + { + aSz.SetWidthPercent(static_cast<sal_uInt8>(std::min(MAX_PERCENT_WIDTH, m_xWidthED->Convert(m_xWidthED->NormalizePercent(nNewWidth), FieldUnit::TWIP, FieldUnit::PERCENT)))); + } + else + aSz.SetWidthPercent(0); + if (m_xRelHeightCB->get_active()) + aSz.SetHeightPercent(static_cast<sal_uInt8>(std::min(MAX_PERCENT_HEIGHT, m_xHeightED->Convert(m_xHeightED->NormalizePercent(nNewHeight), FieldUnit::TWIP, FieldUnit::PERCENT)))); + else + aSz.SetHeightPercent(0); + + if (m_xFixedRatioCB->get_active() && (m_xRelWidthCB->get_active() != m_xRelHeightCB->get_active())) + { + if (m_xRelWidthCB->get_active()) + aSz.SetHeightPercent(SwFormatFrameSize::SYNCED); + else + aSz.SetWidthPercent(SwFormatFrameSize::SYNCED); + } + } + if( !IsInGraficMode() ) + { + if (m_xAutoHeightCB->get_state_changed_from_saved()) + { + SwFrameSize eFrameSize = m_xAutoHeightCB->get_active()? SwFrameSize::Minimum : SwFrameSize::Fixed; + if( eFrameSize != aSz.GetHeightSizeType() ) + aSz.SetHeightSizeType(eFrameSize); + } + if (m_xAutoWidthCB->get_state_changed_from_saved()) + { + SwFrameSize eFrameSize = m_xAutoWidthCB->get_active()? SwFrameSize::Minimum : SwFrameSize::Fixed; + if( eFrameSize != aSz.GetWidthSizeType() ) + aSz.SetWidthSizeType( eFrameSize ); + } + } + if (!m_bFormat && m_xFixedRatioCB->get_state_changed_from_saved()) + bRet |= nullptr != rSet->Put(SfxBoolItem(FN_KEEP_ASPECT_RATIO, m_xFixedRatioCB->get_active())); + + pOldItem = GetOldItem(*rSet, RES_FRM_SIZE); + + if ((pOldItem && aSz != *pOldItem) || (!pOldItem && !m_bFormat) || + (m_bFormat && + (aSz.GetWidth() > 0 || aSz.GetWidthPercent() > 0) && + (aSz.GetHeight() > 0 || aSz.GetHeightPercent() > 0))) + { + if (aSz.GetHeightSizeType() == SwFrameSize::Variable) // there is no VAR_SIZE in frames + aSz.SetHeightSizeType(SwFrameSize::Minimum); + + bRet |= nullptr != rSet->Put( aSz ); + } + if (m_xFollowTextFlowCB->get_state_changed_from_saved()) + { + bRet |= nullptr != rSet->Put(SwFormatFollowTextFlow(m_xFollowTextFlowCB->get_active())); + } + return bRet; +} + +// initialise horizontal and vertical Pos +void SwFramePage::InitPos(RndStdIds eId, + sal_Int16 nH, + sal_Int16 nHRel, + sal_Int16 nV, + sal_Int16 nVRel, + long nX, + long nY) +{ + auto nPos = m_xVerticalDLB->get_active(); + if (nPos != -1 && m_pVMap) + { + m_nOldV = m_pVMap[nPos].nAlign; + + nPos = m_xVertRelationLB->get_active(); + if (nPos != -1) + m_nOldVRel = reinterpret_cast<RelationMap*>(m_xVertRelationLB->get_id(nPos).toInt64())->nRelation; + } + + nPos = m_xHorizontalDLB->get_active(); + if (nPos != -1 && m_pHMap) + { + m_nOldH = m_pHMap[nPos].nAlign; + + nPos = m_xHoriRelationLB->get_active(); + if (nPos != -1) + m_nOldHRel = reinterpret_cast<RelationMap*>(m_xHoriRelationLB->get_id(nPos).toInt64())->nRelation; + } + + bool bEnable = true; + if ( eId == RndStdIds::FLY_AT_PAGE ) + { + m_pVMap = m_bHtmlMode ? aVPageHtmlMap : aVPageMap; + m_pHMap = m_bHtmlMode ? aHPageHtmlMap : aHPageMap; + } + else if ( eId == RndStdIds::FLY_AT_FLY ) + { + // own vertical alignment map for to frame + // anchored objects. + m_pVMap = m_bHtmlMode ? aVFlyHtmlMap : aVFrameMap; + m_pHMap = m_bHtmlMode ? aHFlyHtmlMap : aHFrameMap; + } + else if ( eId == RndStdIds::FLY_AT_PARA ) + { + if(m_bHtmlMode) + { + m_pVMap = aVParaHtmlMap; + m_pHMap = aHParaHtmlAbsMap; + } + else + { + m_pVMap = aVParaMap; + m_pHMap = aHParaMap; + } + } + else if ( eId == RndStdIds::FLY_AT_CHAR ) + { + if(m_bHtmlMode) + { + m_pVMap = aVCharHtmlAbsMap; + m_pHMap = aHCharHtmlAbsMap; + } + else + { + m_pVMap = aVCharMap; + m_pHMap = aHCharMap; + } + } + else if ( eId == RndStdIds::FLY_AS_CHAR ) + { + m_pVMap = m_bHtmlMode ? aVAsCharHtmlMap : aVAsCharMap; + m_pHMap = nullptr; + bEnable = false; + } + m_xHorizontalDLB->set_sensitive( bEnable ); + m_xHorizontalFT->set_sensitive( bEnable ); + + // select current Pos + // horizontal + if ( nH < 0 ) + { + nH = m_nOldH; + nHRel = m_nOldHRel; + } + sal_Int32 nMapPos = FillPosLB(m_pHMap, nH, nHRel, *m_xHorizontalDLB); + FillRelLB(m_pHMap, nMapPos, nH, nHRel, *m_xHoriRelationLB, *m_xHoriRelationFT); + + // vertical + if ( nV < 0 ) + { + nV = m_nOldV; + nVRel = m_nOldVRel; + } + nMapPos = FillPosLB(m_pVMap, nV, nVRel, *m_xVerticalDLB); + FillRelLB(m_pVMap, nMapPos, nV, nVRel, *m_xVertRelationLB, *m_xVertRelationFT); + + bEnable = nH == text::HoriOrientation::NONE && eId != RndStdIds::FLY_AS_CHAR; + if (!bEnable) + m_xAtHorzPosED->set_value(0, FieldUnit::TWIP); + else + { + if (nX != LONG_MAX) + m_xAtHorzPosED->set_value(m_xAtHorzPosED->normalize(nX), FieldUnit::TWIP); + } + m_xAtHorzPosFT->set_sensitive( bEnable ); + m_xAtHorzPosED->set_sensitive( bEnable ); + + bEnable = nV == text::VertOrientation::NONE; + if ( !bEnable ) + m_xAtVertPosED->set_value(0, FieldUnit::TWIP); + else + { + if (eId == RndStdIds::FLY_AS_CHAR) + { + if ( nY == LONG_MAX ) + nY = 0; + else + nY *= -1; + } + if ( nY != LONG_MAX ) + m_xAtVertPosED->set_value(m_xAtVertPosED->normalize(nY), FieldUnit::TWIP); + } + m_xAtVertPosFT->set_sensitive( bEnable && m_bAllowVertPositioning ); + m_xAtVertPosED->set_sensitive( bEnable && m_bAllowVertPositioning ); + UpdateExample(); +} + +sal_Int32 SwFramePage::FillPosLB(const FrameMap* _pMap, + const sal_Int16 _nAlign, + const sal_Int16 _nRel, + weld::ComboBox& _rLB ) +{ + OUString sSelEntry; + const OUString sOldEntry = _rLB.get_active_text(); + + _rLB.clear(); + + // i#22341 determine all possible listbox relations for + // given relation for map <aVCharMap> + const LB nLBRelations = (_pMap != aVCharMap) + ? LB::NONE + : ::lcl_GetLBRelationsForRelations( _nRel ); + + // fill Listbox + size_t nCount = ::lcl_GetFrameMapCount(_pMap); + for (size_t i = 0; _pMap && i < nCount; ++i) + { +// Why not from the left/from inside or from above? + SvxSwFramePosString::StringId eStrId = m_xMirrorPagesCB->get_active() ? _pMap[i].eMirrorStrId : _pMap[i].eStrId; + // --> OD 2009-08-31 #mongolianlayout# + eStrId = lcl_ChangeResIdToVerticalOrRTL( eStrId, + m_bIsVerticalFrame, + m_bIsVerticalL2R, + m_bIsInRightToLeft); + OUString sEntry(SvxSwFramePosString::GetString(eStrId)); + if (_rLB.find_text(sEntry) == -1) + { + // don't insert entries when frames are character bound + _rLB.append_text(sEntry); + } + // i#22341 - add condition to handle map <aVCharMap> + // that is ambiguous in the alignment. + if ( _pMap[i].nAlign == _nAlign && + ( (_pMap != aVCharMap) || _pMap[i].nLBRelations & nLBRelations ) ) + { + sSelEntry = sEntry; + } + } + + _rLB.set_active_text(sSelEntry); + if (_rLB.get_active() == -1) + _rLB.set_active_text(sOldEntry); + + if (_rLB.get_active() == -1) + _rLB.set_active(0); + + PosHdl(_rLB); + + return GetMapPos(_pMap, _rLB); +} + +void SwFramePage::FillRelLB(const FrameMap* _pMap, + const sal_uInt16 _nLBSelPos, + const sal_Int16 _nAlign, + const sal_Int16 _nRel, + weld::ComboBox& _rLB, + weld::Label& _rFT) +{ + OUString sSelEntry; + LB nLBRelations = LB::NONE; + size_t nMapCount = ::lcl_GetFrameMapCount(_pMap); + + _rLB.clear(); + + if (_nLBSelPos < nMapCount) + { + if (_pMap == aVAsCharHtmlMap || _pMap == aVAsCharMap) + { + const OUString sOldEntry(_rLB.get_active_text()); + SvxSwFramePosString::StringId eStrId = _pMap[_nLBSelPos].eStrId; + + for (size_t nMapPos = 0; nMapPos < nMapCount; nMapPos++) + { + if (_pMap[nMapPos].eStrId == eStrId) + { + nLBRelations = _pMap[nMapPos].nLBRelations; + for (RelationMap const & rCharMap : aAsCharRelationMap) + { + if (nLBRelations & rCharMap.nLBRelation) + { + // --> OD 2009-08-31 #mongolianlayout# + SvxSwFramePosString::StringId sStrId1 = + lcl_ChangeResIdToVerticalOrRTL( rCharMap.eStrId, + m_bIsVerticalFrame, + m_bIsVerticalL2R, + m_bIsInRightToLeft); + const OUString sEntry = SvxSwFramePosString::GetString(sStrId1); + _rLB.append(OUString::number(reinterpret_cast<sal_Int64>(&rCharMap)), sEntry); + if (_pMap[nMapPos].nAlign == _nAlign) + sSelEntry = sEntry; + break; + } + } + } + } + if (!sSelEntry.isEmpty()) + _rLB.set_active_text(sSelEntry); + else + { + _rLB.set_active_text(sOldEntry); + + if (_rLB.get_active() == -1) + { + for (int i = 0; i < _rLB.get_count(); i++) + { + RelationMap *pEntry = reinterpret_cast<RelationMap*>(_rLB.get_id(i).toInt64()); + if (pEntry->nLBRelation == LB::RelChar) // default + { + _rLB.set_active(i); + break; + } + } + } + } + } + else + { + // special handling for map <aVCharMap>, + // because its ambiguous in its <eStrId>/<eMirrorStrId>. + if ( _pMap == aVCharMap ) + { + nLBRelations = ::lcl_GetLBRelationsForStrID( _pMap, + ( m_xMirrorPagesCB->get_active() + ? _pMap[_nLBSelPos].eMirrorStrId + : _pMap[_nLBSelPos].eStrId), + m_xMirrorPagesCB->get_active() ); + } + else + { + nLBRelations = _pMap[_nLBSelPos].nLBRelations; + } + + for (sal_uLong nBit = 1; nBit < 0x80000000; nBit <<= 1) + { + if (nLBRelations & static_cast<LB>(nBit)) + { + for (RelationMap const & rMap : aRelationMap) + { + if (rMap.nLBRelation == static_cast<LB>(nBit)) + { + SvxSwFramePosString::StringId eStrId1 = m_xMirrorPagesCB->get_active() ? + rMap.eMirrorStrId : rMap.eStrId; + // --> OD 2009-08-31 #mongolianlayout# + eStrId1 = + lcl_ChangeResIdToVerticalOrRTL( eStrId1, + m_bIsVerticalFrame, + m_bIsVerticalL2R, + m_bIsInRightToLeft); + const OUString sEntry = SvxSwFramePosString::GetString(eStrId1); + _rLB.append(OUString::number(reinterpret_cast<sal_Int64>(&rMap)), sEntry); + if (sSelEntry.isEmpty() && rMap.nRelation == _nRel) + sSelEntry = sEntry; + } + } + } + } + if (!sSelEntry.isEmpty()) + _rLB.set_active_text(sSelEntry); + else + { + // Probably anchor switch. So look for similar relation + sal_Int16 nSimRel = -1; + switch (_nRel) + { + case text::RelOrientation::FRAME: + nSimRel = text::RelOrientation::PAGE_FRAME; + break; + case text::RelOrientation::PRINT_AREA: + nSimRel = text::RelOrientation::PAGE_PRINT_AREA; + break; + case text::RelOrientation::PAGE_LEFT: + nSimRel = text::RelOrientation::FRAME_LEFT; + break; + case text::RelOrientation::PAGE_RIGHT: + nSimRel = text::RelOrientation::FRAME_RIGHT; + break; + case text::RelOrientation::FRAME_LEFT: + nSimRel = text::RelOrientation::PAGE_LEFT; + break; + case text::RelOrientation::FRAME_RIGHT: + nSimRel = text::RelOrientation::PAGE_RIGHT; + break; + case text::RelOrientation::PAGE_FRAME: + nSimRel = text::RelOrientation::FRAME; + break; + case text::RelOrientation::PAGE_PRINT_AREA: + nSimRel = text::RelOrientation::PRINT_AREA; + break; + + default: + if (_rLB.get_active() != -1) + { + RelationMap *pEntry = reinterpret_cast<RelationMap*>(_rLB.get_id(_rLB.get_count() - 1).toInt64()); + nSimRel = pEntry->nRelation; + } + break; + } + + for (int i = 0; i < _rLB.get_count(); i++) + { + RelationMap *pEntry = reinterpret_cast<RelationMap*>(_rLB.get_id(i).toInt64()); + if (pEntry->nRelation == nSimRel) + { + _rLB.set_active(i); + break; + } + } + + if (_rLB.get_active() == -1) + _rLB.set_active(0); + } + } + } + + const bool bEnable = _rLB.get_count() != 0 + && (&_rLB != m_xVertRelationLB.get() || m_bAllowVertPositioning); + _rLB.set_sensitive( bEnable ); + _rFT.set_sensitive( bEnable ); + + RelHdl(_rLB); +} + +sal_Int16 SwFramePage::GetRelation(const weld::ComboBox& rRelationLB) +{ + const auto nPos = rRelationLB.get_active(); + if (nPos != -1) + { + RelationMap *pEntry = reinterpret_cast<RelationMap *>(rRelationLB.get_id(nPos).toInt64()); + return pEntry->nRelation; + } + + return 0; +} + +sal_Int16 SwFramePage::GetAlignment(FrameMap const *pMap, sal_Int32 nMapPos, + const weld::ComboBox& rRelationLB) +{ + if (!pMap || nMapPos < 0) + return 0; + + const size_t nMapCount = ::lcl_GetFrameMapCount(pMap); + + if (o3tl::make_unsigned(nMapPos) >= nMapCount) + return 0; + + // i#22341 special handling also for map <aVCharMap>, + // because it contains ambiguous items for alignment + if ( pMap != aVAsCharHtmlMap && pMap != aVAsCharMap && pMap != aVCharMap ) + return pMap[nMapPos].nAlign; + + if (rRelationLB.get_active() == -1) + return 0; + + const RelationMap *const pRelationMap = reinterpret_cast<const RelationMap *>( + rRelationLB.get_active_id().toInt64()); + const LB nRel = pRelationMap->nLBRelation; + const SvxSwFramePosString::StringId eStrId = pMap[nMapPos].eStrId; + + for (size_t i = 0; i < nMapCount; ++i) + { + if (pMap[i].eStrId == eStrId && (pMap[i].nLBRelations & nRel)) + return pMap[i].nAlign; + } + + return 0; +} + +sal_Int32 SwFramePage::GetMapPos(const FrameMap *pMap, const weld::ComboBox& rAlignLB) +{ + sal_Int32 nMapPos = 0; + auto nLBSelPos = rAlignLB.get_active(); + + if (nLBSelPos != -1) + { + if (pMap == aVAsCharHtmlMap || pMap == aVAsCharMap) + { + const size_t nMapCount = ::lcl_GetFrameMapCount(pMap); + const OUString sSelEntry(rAlignLB.get_active_text()); + + for (size_t i = 0; i < nMapCount; i++) + { + SvxSwFramePosString::StringId eResId = pMap[i].eStrId; + + OUString sEntry = SvxSwFramePosString::GetString(eResId); + sEntry = MnemonicGenerator::EraseAllMnemonicChars( sEntry ); + + if (sEntry == sSelEntry) + { + nMapPos = static_cast< sal_Int32 >(i); + break; + } + } + } + else + nMapPos = nLBSelPos; + } + + return nMapPos; +} + +RndStdIds SwFramePage::GetAnchor() const +{ + RndStdIds nRet = RndStdIds::FLY_AT_PAGE; + if (m_xAnchorAtParaRB->get_active()) + { + nRet = RndStdIds::FLY_AT_PARA; + } + else if (m_xAnchorAtCharRB->get_active()) + { + nRet = RndStdIds::FLY_AT_CHAR; + } + else if (m_xAnchorAsCharRB->get_active()) + { + nRet = RndStdIds::FLY_AS_CHAR; + } + else if (m_xAnchorAtFrameRB->get_active()) + { + nRet = RndStdIds::FLY_AT_FLY; + } + return nRet; +} + +// Bsp - Update +void SwFramePage::ActivatePage(const SfxItemSet& rSet) +{ + m_bNoModifyHdl = true; + Init(rSet); + m_bNoModifyHdl = false; + //lock PercentFields + m_xWidthED->LockAutoCalculation(true); + m_xHeightED->LockAutoCalculation(true); + RangeModifyHdl(); // set all maximum values initially + m_xHeightED->LockAutoCalculation(false); + m_xWidthED->LockAutoCalculation(false); + m_xFollowTextFlowCB->save_state(); +} + +DeactivateRC SwFramePage::DeactivatePage(SfxItemSet * _pSet) +{ + if ( _pSet ) + { + FillItemSet( _pSet ); + + if (!m_bFormat) // tdf#112574 no anchor in styles + { + //FillItemSet doesn't set the anchor into the set when it matches + //the original. But for the other pages we need the current anchor. + SwWrtShell* pSh = m_bFormat ? ::GetActiveWrtShell() + : getFrameDlgParentShell(); + RndStdIds eAnchorId = GetAnchor(); + SwFormatAnchor aAnc( eAnchorId, pSh->GetPhyPageNum() ); + _pSet->Put( aAnc ); + } + } + + return DeactivateRC::LeavePage; +} + +// swap left/right with inside/outside +IMPL_LINK_NOARG(SwFramePage, MirrorHdl, weld::ToggleButton&, void) +{ + RndStdIds eId = GetAnchor(); + InitPos(eId, -1, 0, -1, 0, LONG_MAX, LONG_MAX); +} + +IMPL_LINK( SwFramePage, RelSizeClickHdl, weld::ToggleButton&, rBtn, void ) +{ + if (&rBtn == m_xRelWidthCB.get()) + { + m_xWidthED->ShowPercent(rBtn.get_active()); + m_xRelWidthRelationLB->set_sensitive(rBtn.get_active()); + if (rBtn.get_active()) + m_xWidthED->get()->set_max(MAX_PERCENT_WIDTH, FieldUnit::NONE); + } + else // rBtn == m_xRelHeightCB.get() + { + m_xHeightED->ShowPercent(rBtn.get_active()); + m_xRelHeightRelationLB->set_sensitive(rBtn.get_active()); + if (rBtn.get_active()) + m_xHeightED->get()->set_max(MAX_PERCENT_HEIGHT, FieldUnit::NONE); + } + + RangeModifyHdl(); // correct the values again + + if (&rBtn == m_xRelWidthCB.get()) + ModifyHdl(*m_xWidthED->get()); + else // rBtn == m_xRelHeightCB.get() + ModifyHdl(*m_xHeightED->get()); +} + +// range check +IMPL_LINK_NOARG(SwFramePage, RangeModifyClickHdl, weld::ToggleButton&, void) +{ + RangeModifyHdl(); +} + +void SwFramePage::RangeModifyHdl() +{ + if (m_bNoModifyHdl) + return; + + SwWrtShell* pSh = m_bFormat ? ::GetActiveWrtShell() + : getFrameDlgParentShell(); + OSL_ENSURE(pSh , "shell not found"); + SwFlyFrameAttrMgr aMgr( m_bNew, pSh, GetItemSet() ); + SvxSwFrameValidation aVal; + + aVal.nAnchorType = GetAnchor(); + aVal.bAutoHeight = m_xAutoHeightCB->get_active(); + aVal.bMirror = m_xMirrorPagesCB->get_active(); + aVal.bFollowTextFlow = m_xFollowTextFlowCB->get_active(); + + if ( m_pHMap ) + { + // alignment horizontal + const sal_Int32 nMapPos = GetMapPos(m_pHMap, *m_xHorizontalDLB); + aVal.nHoriOrient = GetAlignment(m_pHMap, nMapPos, *m_xHoriRelationLB); + aVal.nHRelOrient = GetRelation(*m_xHoriRelationLB); + } + else + aVal.nHoriOrient = text::HoriOrientation::NONE; + + if ( m_pVMap ) + { + // alignment vertical + const sal_Int32 nMapPos = GetMapPos(m_pVMap, *m_xVerticalDLB); + aVal.nVertOrient = GetAlignment(m_pVMap, nMapPos, *m_xVertRelationLB); + aVal.nVRelOrient = GetRelation(*m_xVertRelationLB); + } + else + aVal.nVertOrient = text::VertOrientation::NONE; + + const long nAtHorzPosVal = static_cast< long >( + m_xAtHorzPosED->denormalize(m_xAtHorzPosED->get_value(FieldUnit::TWIP)) ); + const long nAtVertPosVal = static_cast< long >( + m_xAtVertPosED->denormalize(m_xAtVertPosED->get_value(FieldUnit::TWIP)) ); + + aVal.nHPos = nAtHorzPosVal; + aVal.nVPos = nAtVertPosVal; + + aMgr.ValidateMetrics(aVal, mpToCharContentPos, true); // one time, to get reference values for percental values + + // set reference values for percental values (100%) ... + m_xWidthED->SetRefValue(aVal.aPercentSize.Width()); + m_xHeightED->SetRefValue(aVal.aPercentSize.Height()); + + // ... and correctly convert width and height with it + SwTwips nWidth = static_cast< SwTwips >(m_xWidthED->DenormalizePercent(m_xWidthED->get_value(FieldUnit::TWIP))); + SwTwips nHeight = static_cast< SwTwips >(m_xHeightED->DenormalizePercent(m_xHeightED->get_value(FieldUnit::TWIP))); + aVal.nWidth = nWidth; + aVal.nHeight = nHeight; + + aMgr.ValidateMetrics(aVal, mpToCharContentPos); // one more time, to determine all remaining values with correct width and height. + + // all columns have to be correct + const SfxItemSet* pExampleSet = GetDialogExampleSet(); + if (pExampleSet && SfxItemState::DEFAULT <= pExampleSet->GetItemState(RES_COL)) + { + const SwFormatCol& rCol = pExampleSet->Get(RES_COL); + if ( rCol.GetColumns().size() > 1 ) + { + for (const SwColumn & i : rCol.GetColumns()) + { + aVal.nMinWidth += i.GetLeft() + + i.GetRight() + + MINFLY; + } + aVal.nMinWidth -= MINFLY;//one was already in there! + } + } + + nWidth = aVal.nWidth; + nHeight = aVal.nHeight; + + // minimum range also for template + m_xHeightED->set_min(m_xHeightED->NormalizePercent(aVal.nMinHeight), FieldUnit::TWIP); + m_xWidthED->set_min(m_xWidthED->NormalizePercent(aVal.nMinWidth), FieldUnit::TWIP); + + SwTwips nMaxWidth(aVal.nMaxWidth); + SwTwips nMaxHeight(aVal.nMaxHeight); + + if (aVal.bAutoHeight && (m_sDlgType == "PictureDialog" || m_sDlgType == "ObjectDialog")) + { + SwTwips nTmp = std::min(nWidth * nMaxHeight / std::max(nHeight, 1L), nMaxHeight); + m_xWidthED->set_max(m_xWidthED->NormalizePercent(nTmp), FieldUnit::TWIP); + + nTmp = std::min(nHeight * nMaxWidth / std::max(nWidth, 1L), nMaxWidth); + m_xHeightED->set_max(m_xWidthED->NormalizePercent(nTmp), FieldUnit::TWIP); + } + else + { + SwTwips nTmp = static_cast< SwTwips >(m_xHeightED->NormalizePercent(nMaxHeight)); + m_xHeightED->set_max(nTmp, FieldUnit::TWIP); + + nTmp = static_cast< SwTwips >(m_xWidthED->NormalizePercent(nMaxWidth)); + m_xWidthED->set_max(nTmp, FieldUnit::TWIP); + } + + m_xAtHorzPosED->set_range(m_xAtHorzPosED->normalize(aVal.nMinHPos), + m_xAtHorzPosED->normalize(aVal.nMaxHPos), + FieldUnit::TWIP); + if (aVal.nHPos != nAtHorzPosVal) + m_xAtHorzPosED->set_value(m_xAtHorzPosED->normalize(aVal.nHPos), FieldUnit::TWIP); + + const SwTwips nUpperOffset = (aVal.nAnchorType == RndStdIds::FLY_AS_CHAR) + ? m_nUpperBorder : 0; + const SwTwips nLowerOffset = (aVal.nAnchorType == RndStdIds::FLY_AS_CHAR) + ? m_nLowerBorder : 0; + + m_xAtVertPosED->set_range(m_xAtVertPosED->normalize(aVal.nMinVPos + nLowerOffset + nUpperOffset), + m_xAtVertPosED->normalize(aVal.nMaxVPos), + FieldUnit::TWIP); + if (aVal.nVPos != nAtVertPosVal) + m_xAtVertPosED->set_value(m_xAtVertPosED->normalize(aVal.nVPos), FieldUnit::TWIP); +} + +IMPL_LINK_NOARG(SwFramePage, AnchorTypeHdl, weld::ToggleButton&, void) +{ + m_xMirrorPagesCB->set_sensitive(!m_xAnchorAsCharRB->get_active()); + + // i#18732 - enable check box 'Follow text flow' for anchor + // type to-paragraph' and to-character + // i#22305 - enable check box 'Follow text + // flow' also for anchor type to-frame. + m_xFollowTextFlowCB->set_sensitive(m_xAnchorAtParaRB->get_active() || + m_xAnchorAtCharRB->get_active() || + m_xAnchorAtFrameRB->get_active()); + + RndStdIds eId = GetAnchor(); + + InitPos( eId, -1, 0, -1, 0, LONG_MAX, LONG_MAX); + RangeModifyHdl(); + + if(m_bHtmlMode) + { + PosHdl(*m_xHorizontalDLB); + PosHdl(*m_xVerticalDLB); + } + + EnableVerticalPositioning( !(m_bIsMathOLE && m_bIsMathBaselineAlignment + && RndStdIds::FLY_AS_CHAR == eId) ); +} + +IMPL_LINK( SwFramePage, PosHdl, weld::ComboBox&, rLB, void ) +{ + bool bHori = &rLB == m_xHorizontalDLB.get(); + weld::ComboBox *pRelLB = bHori ? m_xHoriRelationLB.get() : m_xVertRelationLB.get(); + weld::Label *pRelFT = bHori ? m_xHoriRelationFT.get() : m_xVertRelationFT.get(); + FrameMap const *pMap = bHori ? m_pHMap : m_pVMap; + + const sal_Int32 nMapPos = GetMapPos(pMap, rLB); + const sal_Int16 nAlign = GetAlignment(pMap, nMapPos, *pRelLB); + + if (bHori) + { + bool bEnable = text::HoriOrientation::NONE == nAlign; + m_xAtHorzPosED->set_sensitive( bEnable ); + m_xAtHorzPosFT->set_sensitive( bEnable ); + } + else + { + bool bEnable = text::VertOrientation::NONE == nAlign && m_bAllowVertPositioning; + m_xAtVertPosED->set_sensitive( bEnable ); + m_xAtVertPosFT->set_sensitive( bEnable ); + } + + RangeModifyHdl(); + + sal_Int16 nRel = 0; + if (rLB.get_active() != -1) + { + if (pRelLB->get_active() != -1) + nRel = reinterpret_cast<RelationMap*>(pRelLB->get_active_id().toInt64())->nRelation; + FillRelLB(pMap, nMapPos, nAlign, nRel, *pRelLB, *pRelFT); + } + else + pRelLB->clear(); + + UpdateExample(); + + if (bHori) + m_bAtHorzPosModified = true; + else + m_bAtVertPosModified = true; + + // special treatment for HTML-Mode with horizontal-vertical-dependencies + if(m_bHtmlMode && (RndStdIds::FLY_AT_CHAR == GetAnchor())) + { + bool bSet = false; + if(bHori) + { + // right is allowed only above - from the left only above + // from the left at character -> below + if((text::HoriOrientation::LEFT == nAlign || text::HoriOrientation::RIGHT == nAlign) && + 0 == m_xVerticalDLB->get_active()) + { + if(text::RelOrientation::FRAME == nRel) + m_xVerticalDLB->set_active(1); + else + m_xVerticalDLB->set_active(0); + bSet = true; + } + else if(text::HoriOrientation::LEFT == nAlign && 1 == m_xVerticalDLB->get_active()) + { + m_xVerticalDLB->set_active(0); + bSet = true; + } + else if(text::HoriOrientation::NONE == nAlign && 1 == m_xVerticalDLB->get_active()) + { + m_xVerticalDLB->set_active(0); + bSet = true; + } + if(bSet) + PosHdl(*m_xVerticalDLB); + } + else + { + if(text::VertOrientation::TOP == nAlign) + { + if (1 == m_xHorizontalDLB->get_active()) + { + m_xHorizontalDLB->set_active(0); + bSet = true; + } + m_xHoriRelationLB->set_active(1); + } + else if(text::VertOrientation::CHAR_BOTTOM == nAlign) + { + if (2 == m_xHorizontalDLB->get_active()) + { + m_xHorizontalDLB->set_active(0); + bSet = true; + } + m_xHoriRelationLB->set_active(0) ; + } + if(bSet) + PosHdl(*m_xHorizontalDLB); + } + + } +} + +// horizontal Pos +IMPL_LINK( SwFramePage, RelHdl, weld::ComboBox&, rLB, void ) +{ + bool bHori = &rLB == m_xHoriRelationLB.get(); + + UpdateExample(); + + if (bHori) + m_bAtHorzPosModified = true; + else + m_bAtVertPosModified = true; + + if (m_bHtmlMode && (RndStdIds::FLY_AT_CHAR == GetAnchor())) + { + if(bHori) + { + const sal_Int16 nRel = GetRelation(*m_xHoriRelationLB); + if(text::RelOrientation::PRINT_AREA == nRel && 0 == m_xVerticalDLB->get_active()) + { + m_xVerticalDLB->set_active(1); + } + else if(text::RelOrientation::CHAR == nRel && 1 == m_xVerticalDLB->get_active()) + { + m_xVerticalDLB->set_active(0); + } + } + } + RangeModifyHdl(); +} + +IMPL_LINK_NOARG(SwFramePage, RealSizeHdl, weld::Button&, void) +{ + m_xWidthED->set_value(m_xWidthED->NormalizePercent(m_aGrfSize.Width()), FieldUnit::TWIP); + m_xHeightED->set_value(m_xHeightED->NormalizePercent(m_aGrfSize.Height()), FieldUnit::TWIP); + m_fWidthHeightRatio = m_aGrfSize.Height() ? double(m_aGrfSize.Width()) / double(m_aGrfSize.Height()) : 1.0; + UpdateExample(); +} + +IMPL_LINK_NOARG(SwFramePage, AutoWidthClickHdl, weld::ToggleButton&, void) +{ + if( !IsInGraficMode() ) + HandleAutoCB( m_xAutoWidthCB->get_active(), *m_xWidthFT, *m_xWidthAutoFT, *m_xWidthED->get() ); +} + +IMPL_LINK_NOARG(SwFramePage, AutoHeightClickHdl, weld::ToggleButton&, void) +{ + if (!IsInGraficMode()) + HandleAutoCB(m_xAutoHeightCB->get_active(), *m_xHeightFT, *m_xHeightAutoFT, *m_xWidthED->get()); +} + +IMPL_LINK( SwFramePage, ModifyHdl, weld::MetricSpinButton&, rEdit, void ) +{ + SwTwips nWidth = static_cast< SwTwips >(m_xWidthED->DenormalizePercent(m_xWidthED->get_value(FieldUnit::TWIP))); + SwTwips nHeight = static_cast< SwTwips >(m_xHeightED->DenormalizePercent(m_xHeightED->get_value(FieldUnit::TWIP))); + if (m_xFixedRatioCB->get_active()) + { + if (&rEdit == m_xWidthED->get()) + { + nHeight = SwTwips(static_cast<double>(nWidth) / m_fWidthHeightRatio); + m_xHeightED->set_value(m_xHeightED->NormalizePercent(nHeight), FieldUnit::TWIP); + } + else if (&rEdit == m_xHeightED->get()) + { + nWidth = SwTwips(static_cast<double>(nHeight) * m_fWidthHeightRatio); + m_xWidthED->set_value(m_xWidthED->NormalizePercent(nWidth), FieldUnit::TWIP); + } + } + m_fWidthHeightRatio = nHeight ? double(nWidth) / double(nHeight) : 1.0; + UpdateExample(); +} + +void SwFramePage::UpdateExample() +{ + auto nPos = m_xHorizontalDLB->get_active(); + if (m_pHMap && nPos != -1) + { + const sal_Int32 nMapPos = GetMapPos(m_pHMap, *m_xHorizontalDLB); + m_aExampleWN.SetHAlign(GetAlignment(m_pHMap, nMapPos, *m_xHoriRelationLB)); + m_aExampleWN.SetHoriRel(GetRelation(*m_xHoriRelationLB)); + } + + nPos = m_xVerticalDLB->get_active(); + if (m_pVMap && nPos != -1) + { + const sal_Int32 nMapPos = GetMapPos(m_pVMap, *m_xVerticalDLB); + m_aExampleWN.SetVAlign(GetAlignment(m_pVMap, nMapPos, *m_xVertRelationLB)); + m_aExampleWN.SetVertRel(GetRelation(*m_xVertRelationLB)); + } + + // size + auto nXPos = m_xAtHorzPosED->denormalize(m_xAtHorzPosED->get_value(FieldUnit::TWIP)); + auto nYPos = m_xAtVertPosED->denormalize(m_xAtVertPosED->get_value(FieldUnit::TWIP)); + m_aExampleWN.SetRelPos(Point(nXPos, nYPos)); + + m_aExampleWN.SetAnchor(GetAnchor()); + m_aExampleWN.Invalidate(); +} + +void SwFramePage::Init(const SfxItemSet& rSet) +{ + if(!m_bFormat) + { + SwWrtShell* pSh = getFrameDlgParentShell(); + + // size + const bool bSizeFixed = pSh->IsSelObjProtected( FlyProtectFlags::Fixed ) != FlyProtectFlags::NONE; + + m_xWidthED->set_sensitive( !bSizeFixed ); + m_xHeightED->set_sensitive( !bSizeFixed ); + + // size controls for math OLE objects + if ( m_sDlgType == "ObjectDialog" && ! m_bNew ) + { + // disable width and height for math objects + const SvGlobalName& rFactNm( pSh->GetOLEObject()->getClassID() ); + + static struct GlobalNameId { + sal_uInt32 n1; + sal_uInt16 n2, n3; + sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15; + } const aGlbNmIds[] = { { SO3_SM_CLASSID_60 }, { SO3_SM_CLASSID_50 }, + { SO3_SM_CLASSID_40 }, { SO3_SM_CLASSID_30 } }; + + for (const GlobalNameId & rId : aGlbNmIds) { + 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( rFactNm == aGlbNm ) + { + // disable size controls for math OLE objects + m_xWidthFT->set_sensitive(false); + m_xWidthED->set_sensitive(false); + m_xRelWidthCB->set_sensitive(false); + m_xHeightFT->set_sensitive(false); + m_xHeightED->set_sensitive(false); + m_xRelHeightCB->set_sensitive(false); + m_xFixedRatioCB->set_sensitive(false); + m_xRealSizeBT->set_sensitive(false); + break; + } + } + + // TODO/LATER: get correct aspect + if(0 != (pSh->GetOLEObject()->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) ) + m_xRealSizeBT->set_sensitive(false); + } + } + + const SwFormatFrameSize& rSize = rSet.Get(RES_FRM_SIZE); + sal_Int64 nWidth = m_xWidthED->NormalizePercent(rSize.GetWidth()); + sal_Int64 nHeight = m_xHeightED->NormalizePercent(rSize.GetHeight()); + + if (nWidth != m_xWidthED->get_value(FieldUnit::TWIP)) + m_xWidthED->set_value(nWidth, FieldUnit::TWIP); + + if (nHeight != m_xHeightED->get_value(FieldUnit::TWIP)) + m_xHeightED->set_value(nHeight, FieldUnit::TWIP); + + if (!IsInGraficMode()) + { + SwFrameSize eSize = rSize.GetHeightSizeType(); + bool bCheck = eSize != SwFrameSize::Fixed; + m_xAutoHeightCB->set_active(bCheck); + HandleAutoCB( bCheck, *m_xHeightFT, *m_xHeightAutoFT, *m_xWidthED->get() ); + if( eSize == SwFrameSize::Variable ) + m_xHeightED->set_value(m_xHeightED->get_min()); + + eSize = rSize.GetWidthSizeType(); + bCheck = eSize != SwFrameSize::Fixed; + m_xAutoWidthCB->set_active(bCheck); + HandleAutoCB( bCheck, *m_xWidthFT, *m_xWidthAutoFT, *m_xWidthED->get() ); + if( eSize == SwFrameSize::Variable ) + m_xWidthED->set_value(m_xWidthED->get_min()); + + if ( !m_bFormat ) + { + SwWrtShell* pSh = getFrameDlgParentShell(); + const SwFrameFormat* pFormat = pSh->GetFlyFrameFormat(); + if( pFormat && pFormat->GetChain().GetNext() ) + m_xAutoHeightCB->set_sensitive( false ); + } + } + else + m_xAutoHeightCB->hide(); + + // organise circulation-gap for character bound frames + const SvxULSpaceItem &rUL = rSet.Get(RES_UL_SPACE); + m_nUpperBorder = rUL.GetUpper(); + m_nLowerBorder = rUL.GetLower(); + + if(SfxItemState::SET == rSet.GetItemState(FN_KEEP_ASPECT_RATIO)) + { + m_xFixedRatioCB->set_active(static_cast<const SfxBoolItem&>(rSet.Get(FN_KEEP_ASPECT_RATIO)).GetValue()); + m_xFixedRatioCB->save_state(); + } + + // columns + SwFormatCol aCol( rSet.Get(RES_COL) ); + ::FitToActualSize( aCol, static_cast<sal_uInt16>(rSize.GetWidth()) ); + + RndStdIds eAnchorId = GetAnchor(); + + if ( m_bNew && !m_bFormat ) + InitPos(eAnchorId, -1, 0, -1, 0, LONG_MAX, LONG_MAX); + else + { + const SwFormatHoriOrient& rHori = rSet.Get(RES_HORI_ORIENT); + const SwFormatVertOrient& rVert = rSet.Get(RES_VERT_ORIENT); + m_nOldH = rHori.GetHoriOrient(); + m_nOldHRel = rHori.GetRelationOrient(); + m_nOldV = rVert.GetVertOrient(); + m_nOldVRel = rVert.GetRelationOrient(); + + if (eAnchorId == RndStdIds::FLY_AT_PAGE) + { + if (m_nOldHRel == text::RelOrientation::FRAME) + m_nOldHRel = text::RelOrientation::PAGE_FRAME; + else if (m_nOldHRel == text::RelOrientation::PRINT_AREA) + m_nOldHRel = text::RelOrientation::PAGE_PRINT_AREA; + if (m_nOldVRel == text::RelOrientation::FRAME) + m_nOldVRel = text::RelOrientation::PAGE_FRAME; + else if (m_nOldVRel == text::RelOrientation::PRINT_AREA) + m_nOldVRel = text::RelOrientation::PAGE_PRINT_AREA; + } + + m_xMirrorPagesCB->set_active(rHori.IsPosToggle()); + m_xMirrorPagesCB->save_state(); + + InitPos(eAnchorId, + m_nOldH, + m_nOldHRel, + m_nOldV, + m_nOldVRel, + rHori.GetPos(), + rVert.GetPos()); + } + + // transparent for example + // circulation for example + const SwFormatSurround& rSurround = rSet.Get(RES_SURROUND); + m_aExampleWN.SetWrap( rSurround.GetSurround() ); + + if ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) + { + const SvxOpaqueItem& rOpaque = rSet.Get(RES_OPAQUE); + m_aExampleWN.SetTransparent(!rOpaque.GetValue()); + } + + // switch to percent if applicable + RangeModifyHdl(); // set reference values (for 100%) + + if (rSize.GetWidthPercent() == SwFormatFrameSize::SYNCED || rSize.GetHeightPercent() == SwFormatFrameSize::SYNCED) + m_xFixedRatioCB->set_active(true); + if (rSize.GetWidthPercent() && rSize.GetWidthPercent() != SwFormatFrameSize::SYNCED && + !m_xRelWidthCB->get_active()) + { + m_xRelWidthCB->set_active(true); + RelSizeClickHdl(*m_xRelWidthCB); + m_xWidthED->set_value(rSize.GetWidthPercent(), FieldUnit::PERCENT); + } + if (rSize.GetHeightPercent() && rSize.GetHeightPercent() != SwFormatFrameSize::SYNCED && + !m_xRelHeightCB->get_active()) + { + m_xRelHeightCB->set_active(true); + RelSizeClickHdl(*m_xRelHeightCB); + m_xHeightED->set_value(rSize.GetHeightPercent(), FieldUnit::PERCENT); + } + m_xRelWidthCB->save_state(); + m_xRelHeightCB->save_state(); + + if (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME) + m_xRelWidthRelationLB->set_active(1); + else + m_xRelWidthRelationLB->set_active(0); + + if (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME) + m_xRelHeightRelationLB->set_active(1); + else + m_xRelHeightRelationLB->set_active(0); +} + +void SwFramePage::SetFormatUsed(bool bFormatUsed) +{ + m_bFormat = bFormatUsed; + if (m_bFormat) + m_xAnchorFrame->hide(); +} + +void SwFramePage::EnableVerticalPositioning( bool bEnable ) +{ + m_bAllowVertPositioning = bEnable; + m_xVerticalFT->set_sensitive( bEnable ); + m_xVerticalDLB->set_sensitive( bEnable ); + m_xAtVertPosFT->set_sensitive( bEnable ); + m_xAtVertPosED->set_sensitive( bEnable ); + m_xVertRelationFT->set_sensitive( bEnable ); + m_xVertRelationLB->set_sensitive( bEnable ); +} + +SwGrfExtPage::SwGrfExtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/picturepage.ui", "PicturePage", &rSet) + , m_bHtmlMode(false) + , m_xMirror(m_xBuilder->weld_widget("flipframe")) + , m_xMirrorVertBox(m_xBuilder->weld_check_button("vert")) + , m_xMirrorHorzBox(m_xBuilder->weld_check_button("hori")) + , m_xAllPagesRB(m_xBuilder->weld_radio_button("allpages")) + , m_xLeftPagesRB(m_xBuilder->weld_radio_button("leftpages")) + , m_xRightPagesRB(m_xBuilder->weld_radio_button("rightpages")) + , m_xConnectED(m_xBuilder->weld_entry("entry")) + , m_xBrowseBT(m_xBuilder->weld_button("browse")) + , m_xLinkFrame(m_xBuilder->weld_frame("linkframe")) + // RotGrfFlyFrame: Need Angle and RotateControls now + , m_xFlAngle(m_xBuilder->weld_frame("FL_ANGLE")) + , m_xNfAngle(m_xBuilder->weld_metric_spin_button("NF_ANGLE", FieldUnit::DEGREE)) + , m_xCtlAngle(new svx::DialControl) + , m_xCtlAngleWin(new weld::CustomWeld(*m_xBuilder, "CTL_ANGLE", *m_xCtlAngle)) + , m_xBmpWin(new weld::CustomWeld(*m_xBuilder, "preview", m_aBmpWin)) +{ + m_aBmpWin.SetBitmapEx(BitmapEx(RID_BMP_PREVIEW_FALLBACK)); + + m_xCtlAngle->SetLinkedField(m_xNfAngle.get(), 2); + + SetExchangeSupport(); + m_xMirrorHorzBox->connect_toggled(LINK(this, SwGrfExtPage, MirrorHdl)); + m_xMirrorVertBox->connect_toggled(LINK(this, SwGrfExtPage, MirrorHdl)); + m_xBrowseBT->connect_clicked(LINK(this, SwGrfExtPage, BrowseHdl)); +} + +SwGrfExtPage::~SwGrfExtPage() +{ + m_xBmpWin.reset(); + m_xCtlAngleWin.reset(); + m_xCtlAngle.reset(); + m_xGrfDlg.reset(); +} + +std::unique_ptr<SfxTabPage> SwGrfExtPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwGrfExtPage>(pPage, pController, *rSet); +} + +void SwGrfExtPage::Reset(const SfxItemSet *rSet) +{ + const SfxPoolItem* pItem; + const sal_uInt16 nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + m_bHtmlMode = (nHtmlMode & HTMLMODE_ON) != 0; + + if( SfxItemState::SET == rSet->GetItemState( FN_PARAM_GRF_CONNECT, true, &pItem) + && static_cast<const SfxBoolItem *>(pItem)->GetValue() ) + { + m_xBrowseBT->set_sensitive(true); + m_xConnectED->set_editable(true); + } + + // RotGrfFlyFrame: Get RotationAngle and set at control + if(SfxItemState::SET == rSet->GetItemState( SID_ATTR_TRANSFORM_ANGLE, false, &pItem)) + { + m_xCtlAngle->SetRotation(static_cast<const SfxInt32Item*>(pItem)->GetValue()); + } + else + { + m_xCtlAngle->SetRotation(0); + } + m_xCtlAngle->SaveValue(); + + ActivatePage(*rSet); +} + +void SwGrfExtPage::ActivatePage(const SfxItemSet& rSet) +{ + const SvxProtectItem& rProt = rSet.Get(RES_PROTECT); + bool bProtContent = rProt.IsContentProtected(); + + const SfxPoolItem* pItem = nullptr; + bool bEnable = false; + bool bEnableMirrorRB = false; + + SfxItemState eState = rSet.GetItemState(RES_GRFATR_MIRRORGRF, true, &pItem); + if (SfxItemState::UNKNOWN != eState && !bProtContent && !m_bHtmlMode) + { + if( SfxItemState::SET != eState ) + pItem = &rSet.Get( RES_GRFATR_MIRRORGRF ); + + bEnable = true; + + MirrorGraph eMirror = static_cast<const SwMirrorGrf* >(pItem)->GetValue(); + switch( eMirror ) + { + case MirrorGraph::Dont: break; + case MirrorGraph::Vertical: m_xMirrorHorzBox->set_active(true); break; + case MirrorGraph::Horizontal: m_xMirrorVertBox->set_active(true); break; + case MirrorGraph::Both: m_xMirrorHorzBox->set_active(true); + m_xMirrorVertBox->set_active(true); + break; + default: + ; + } + + const int nPos = (static_cast<const SwMirrorGrf* >(pItem)->IsGrfToggle() ? 1 : 0) + + ((eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both) ? 2 : 0); + + bEnableMirrorRB = nPos != 0; + + switch (nPos) + { + case 1: // mirror at left / even pages + m_xLeftPagesRB->set_active(true); + m_xMirrorHorzBox->set_active(true); + break; + case 2: // mirror on all pages + m_xAllPagesRB->set_active(true); + break; + case 3: // mirror on right / odd pages + m_xRightPagesRB->set_active(true); + break; + default: + m_xAllPagesRB->set_active(true); + break; + } + } + + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_GRAF_GRAPHIC, false, &pItem ) ) + { + const SvxBrushItem& rBrush = *static_cast<const SvxBrushItem*>(pItem); + if( !rBrush.GetGraphicLink().isEmpty() ) + { + aGrfName = aNewGrfName = rBrush.GetGraphicLink(); + m_xConnectED->set_text(aNewGrfName); + } + OUString referer; + SfxStringItem const * it = static_cast<SfxStringItem const *>( + rSet.GetItem(SID_REFERER)); + if (it != nullptr) { + referer = it->GetValue(); + } + const Graphic* pGrf = rBrush.GetGraphic(referer); + if( pGrf ) + m_aBmpWin.SetGraphic( *pGrf ); + } + + m_xConnectED->save_value(); + + m_xMirror->set_sensitive(bEnable); + m_xAllPagesRB->set_sensitive(bEnableMirrorRB); + m_xLeftPagesRB->set_sensitive(bEnableMirrorRB); + m_xRightPagesRB->set_sensitive(bEnableMirrorRB); + + m_xAllPagesRB->save_state(); + m_xLeftPagesRB->save_state(); + m_xRightPagesRB->save_state(); + m_xMirrorHorzBox->save_state(); + m_xMirrorVertBox->save_state(); + + m_aBmpWin.MirrorHorz( m_xMirrorVertBox->get_active() ); + m_aBmpWin.MirrorVert( m_xMirrorHorzBox->get_active() ); + m_aBmpWin.Invalidate(); +} + +bool SwGrfExtPage::FillItemSet( SfxItemSet *rSet ) +{ + bool bModified = false; + if ( m_xMirrorHorzBox->get_state_changed_from_saved() || + m_xMirrorVertBox->get_state_changed_from_saved() || + m_xAllPagesRB->get_state_changed_from_saved() || + m_xLeftPagesRB->get_state_changed_from_saved() || + m_xRightPagesRB->get_state_changed_from_saved() ) + { + bModified = true; + + bool bHori = false; + + if (m_xMirrorHorzBox->get_active() && + !m_xLeftPagesRB->get_active()) + bHori = true; + + MirrorGraph eMirror; + eMirror = m_xMirrorVertBox->get_active() && bHori ? + MirrorGraph::Both : bHori ? + MirrorGraph::Vertical : m_xMirrorVertBox->get_active() ? + MirrorGraph::Horizontal : MirrorGraph::Dont; + + bool bMirror = !m_xAllPagesRB->get_active(); + SwMirrorGrf aMirror( eMirror ); + aMirror.SetGrfToggle(bMirror ); + rSet->Put( aMirror ); + } + + if (aGrfName != aNewGrfName || m_xConnectED->get_value_changed_from_saved()) + { + bModified = true; + aGrfName = m_xConnectED->get_text(); + rSet->Put( SvxBrushItem( aGrfName, aFilterName, GPOS_LT, + SID_ATTR_GRAF_GRAPHIC )); + } + + // RotGrfFlyFrame: Safe rotation if modified + if(m_xCtlAngle->IsValueModified()) + { + rSet->Put(SfxInt32Item(GetWhich(SID_ATTR_TRANSFORM_ANGLE), m_xCtlAngle->GetRotation())); + bModified = true; + } + + return bModified; +} + +DeactivateRC SwGrfExtPage::DeactivatePage(SfxItemSet *_pSet) +{ + if( _pSet ) + FillItemSet( _pSet ); + return DeactivateRC::LeavePage; +} + +IMPL_LINK_NOARG(SwGrfExtPage, BrowseHdl, weld::Button&, void) +{ + if(!m_xGrfDlg) + { + m_xGrfDlg.reset(new FileDialogHelper( + ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW, + FileDialogFlags::Graphic, GetFrameWeld())); + m_xGrfDlg->SetTitle(m_xLinkFrame->get_label()); + } + m_xGrfDlg->SetDisplayDirectory(m_xConnectED->get_text()); + uno::Reference < ui::dialogs::XFilePicker3 > xFP = m_xGrfDlg->GetFilePicker(); + uno::Reference < ui::dialogs::XFilePickerControlAccess > xCtrlAcc(xFP, uno::UNO_QUERY); + xCtrlAcc->setValue( ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, uno::makeAny(true) ); + + if ( m_xGrfDlg->Execute() != ERRCODE_NONE ) + return; + +// remember selected filter + aFilterName = m_xGrfDlg->GetCurrentFilter(); + aNewGrfName = INetURLObject::decode( m_xGrfDlg->GetPath(), + INetURLObject::DecodeMechanism::Unambiguous ); + m_xConnectED->set_text(aNewGrfName); + //reset mirrors because maybe a Bitmap was swapped with + //another type of graphic that cannot be mirrored. + m_xMirrorVertBox->set_active(false); + m_xMirrorHorzBox->set_active(false); + m_xAllPagesRB->set_sensitive(false); + m_xLeftPagesRB->set_sensitive(false); + m_xRightPagesRB->set_sensitive(false); + m_aBmpWin.MirrorHorz(false); + m_aBmpWin.MirrorVert(false); + + Graphic aGraphic; + (void)GraphicFilter::LoadGraphic(m_xGrfDlg->GetPath(), OUString(), aGraphic); + m_aBmpWin.SetGraphic(aGraphic); + + bool bEnable = GraphicType::Bitmap == aGraphic.GetType() || + GraphicType::GdiMetafile == aGraphic.GetType(); + m_xMirrorVertBox->set_sensitive(bEnable); + m_xMirrorHorzBox->set_sensitive(bEnable); + m_xAllPagesRB->set_sensitive(bEnable); + m_xLeftPagesRB->set_sensitive(bEnable); + m_xRightPagesRB->set_sensitive(bEnable); + +} + +IMPL_LINK_NOARG(SwGrfExtPage, MirrorHdl, weld::ToggleButton&, void) +{ + bool bEnable = m_xMirrorHorzBox->get_active(); + + m_aBmpWin.MirrorHorz( m_xMirrorVertBox->get_active() ); + m_aBmpWin.MirrorVert( bEnable ); + + m_xAllPagesRB->set_sensitive(bEnable); + m_xLeftPagesRB->set_sensitive(bEnable); + m_xRightPagesRB->set_sensitive(bEnable); + + if (!m_xAllPagesRB->get_active() && !m_xLeftPagesRB->get_active() && !m_xRightPagesRB->get_active()) + m_xAllPagesRB->set_active(true); +} + +// example window +BmpWindow::BmpWindow() + : bHorz(false) + , bVert(false) + , bGraphic(false) +{ +} + +void BmpWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(127 , 66), MapMode(MapUnit::MapAppFont)); + set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); +} + +void BmpWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + // Setup + rRenderContext.SetBackground(); + // #i119307# the graphic might have transparency, set up white as the color + // to use when drawing a rectangle under the image + rRenderContext.SetLineColor(COL_WHITE); + rRenderContext.SetFillColor(COL_WHITE); + + // Paint + Point aPntPos; + Size aPntSz(GetOutputSizePixel()); + Size aGrfSize; + if (bGraphic) + aGrfSize = ::GetGraphicSizeTwip(aGraphic, &rRenderContext); + //it should show the default bitmap also if no graphic can be found + if (!aGrfSize.Width() && !aGrfSize.Height()) + aGrfSize = rRenderContext.PixelToLogic(aBmp.GetSizePixel()); + + long nRelGrf = aGrfSize.Width() * 100 / aGrfSize.Height(); + long nRelWin = aPntSz.Width() * 100 / aPntSz.Height(); + if (nRelGrf < nRelWin) + { + const long nWidth = aPntSz.Width(); + // if we use a replacement preview, try to draw at original size + if (!bGraphic && (aGrfSize.Width() <= aPntSz.Width()) + && (aGrfSize.Height() <= aPntSz.Height())) + { + const long nHeight = aPntSz.Height(); + aPntSz.setWidth( aGrfSize.Width() ); + aPntSz.setHeight( aGrfSize.Height() ); + aPntPos.AdjustY((nHeight - aPntSz.Height()) / 2 ); + } + else + aPntSz.setWidth( aPntSz.Height() * nRelGrf /100 ); + + aPntPos.AdjustX(nWidth - aPntSz.Width() ) ; + } + + // #i119307# clear window background, the graphic might have transparency + rRenderContext.DrawRect(tools::Rectangle(aPntPos, aPntSz)); + + if (bHorz || bVert) + { + BitmapEx aTmpBmp(bGraphic ? aGraphic.GetBitmapEx() : aBmp); + BmpMirrorFlags nMirrorFlags(BmpMirrorFlags::NONE); + if (bHorz) + nMirrorFlags |= BmpMirrorFlags::Vertical; + if (bVert) + nMirrorFlags |= BmpMirrorFlags::Horizontal; + aTmpBmp.Mirror(nMirrorFlags); + rRenderContext.DrawBitmapEx(aPntPos, aPntSz, aTmpBmp); + } + else if (bGraphic) //draw unmirrored preview graphic + { + aGraphic.Draw(&rRenderContext, aPntPos, aPntSz); + } + else //draw unmirrored stock sample image + { + rRenderContext.DrawBitmapEx(aPntPos, aPntSz, aBmp); + } +} + +BmpWindow::~BmpWindow() +{ +} + +void BmpWindow::SetGraphic(const Graphic& rGraphic) +{ + aGraphic = rGraphic; + Size aSize = aGraphic.GetPrefSize(); + bGraphic = aSize.Width() && aSize.Height(); + Invalidate(); +} + +void BmpWindow::SetBitmapEx(const BitmapEx& rBmp) +{ + aBmp = rBmp; + Invalidate(); +} + +// set URL and ImageMap at frames +SwFrameURLPage::SwFrameURLPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/frmurlpage.ui", "FrameURLPage", &rSet) + , m_xURLED(m_xBuilder->weld_entry("url")) + , m_xSearchPB(m_xBuilder->weld_button("search")) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xFrameCB(m_xBuilder->weld_combo_box("frame")) + , m_xServerCB(m_xBuilder->weld_check_button("server")) + , m_xClientCB(m_xBuilder->weld_check_button("client")) +{ + m_xSearchPB->connect_clicked(LINK(this, SwFrameURLPage, InsertFileHdl)); +} + +SwFrameURLPage::~SwFrameURLPage() +{ +} + +void SwFrameURLPage::Reset( const SfxItemSet *rSet ) +{ + const SfxPoolItem* pItem; + if ( SfxItemState::SET == rSet->GetItemState( SID_DOCFRAME, true, &pItem)) + { + std::unique_ptr<TargetList> pList(new TargetList); + SfxFrame::GetDefaultTargetList(*pList); + size_t nCount = pList->size(); + for (size_t i = 0; i < nCount; ++i) + { + m_xFrameCB->append_text(pList->at(i)); + } + } + + if ( SfxItemState::SET == rSet->GetItemState( RES_URL, true, &pItem ) ) + { + const SwFormatURL* pFormatURL = static_cast<const SwFormatURL*>(pItem); + m_xURLED->set_text(INetURLObject::decode(pFormatURL->GetURL(), + INetURLObject::DecodeMechanism::Unambiguous)); + m_xNameED->set_text(pFormatURL->GetName()); + + m_xClientCB->set_sensitive(pFormatURL->GetMap() != nullptr); + m_xClientCB->set_active(pFormatURL->GetMap() != nullptr); + m_xServerCB->set_active(pFormatURL->IsServerMap()); + + m_xFrameCB->set_entry_text(pFormatURL->GetTargetFrameName()); + m_xFrameCB->save_value(); + } + else + m_xClientCB->set_sensitive(false); + + m_xServerCB->save_state(); + m_xClientCB->save_state(); +} + +bool SwFrameURLPage::FillItemSet(SfxItemSet *rSet) +{ + bool bModified = false; + const SwFormatURL* pOldURL = GetOldItem(*rSet, RES_URL); + std::unique_ptr<SwFormatURL> pFormatURL; + if(pOldURL) + pFormatURL.reset(pOldURL->Clone()); + else + pFormatURL.reset(new SwFormatURL()); + + { + const OUString sText = m_xURLED->get_text(); + + if( pFormatURL->GetURL() != sText || + pFormatURL->GetName() != m_xNameED->get_text() || + m_xServerCB->get_active() != pFormatURL->IsServerMap() ) + { + pFormatURL->SetURL(sText, m_xServerCB->get_active()); + pFormatURL->SetName(m_xNameED->get_text()); + bModified = true; + } + } + + if (!m_xClientCB->get_active() && pFormatURL->GetMap() != nullptr) + { + pFormatURL->SetMap(nullptr); + bModified = true; + } + + if(pFormatURL->GetTargetFrameName() != m_xFrameCB->get_active_text()) + { + pFormatURL->SetTargetFrameName(m_xFrameCB->get_active_text()); + bModified = true; + } + rSet->Put(*pFormatURL); + return bModified; +} + +std::unique_ptr<SfxTabPage> SwFrameURLPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwFrameURLPage>(pPage, pController, *rSet); +} + +IMPL_LINK_NOARG(SwFrameURLPage, InsertFileHdl, weld::Button&, void) +{ + FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, GetFrameWeld()); + uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + try + { + const OUString sTemp(m_xURLED->get_text()); + if(!sTemp.isEmpty()) + xFP->setDisplayDirectory(sTemp); + } + catch( const uno::Exception& ) {} + if( aDlgHelper.Execute() == ERRCODE_NONE ) + { + m_xURLED->set_text(xFP->getSelectedFiles().getConstArray()[0]); + } +} + +SwFrameAddPage::SwFrameAddPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/frmaddpage.ui", "FrameAddPage", &rSet) + , m_pWrtSh(nullptr) + , m_bHtmlMode(false) + , m_bFormat(false) + , m_bNew(false) + , m_xNameFrame(m_xBuilder->weld_widget("nameframe")) + , m_xNameFT(m_xBuilder->weld_label("name_label")) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xAltNameFT(m_xBuilder->weld_label("altname_label")) + , m_xAltNameED(m_xBuilder->weld_entry("altname")) + , m_xDescriptionFT(m_xBuilder->weld_label("description_label")) + , m_xDescriptionED(m_xBuilder->weld_text_view("description")) + , m_xPrevFT(m_xBuilder->weld_label("prev_label")) + , m_xPrevLB(m_xBuilder->weld_combo_box("prev")) + , m_xNextFT(m_xBuilder->weld_label("next_label")) + , m_xNextLB(m_xBuilder->weld_combo_box("next")) + , m_xProtectFrame(m_xBuilder->weld_widget("protect")) + , m_xProtectContentCB(m_xBuilder->weld_check_button("protectcontent")) + , m_xProtectFrameCB(m_xBuilder->weld_check_button("protectframe")) + , m_xProtectSizeCB(m_xBuilder->weld_check_button("protectsize")) + , m_xContentAlignFrame(m_xBuilder->weld_widget("contentalign")) + , m_xVertAlignLB(m_xBuilder->weld_combo_box("vertalign")) + , m_xPropertiesFrame(m_xBuilder->weld_widget("properties")) + , m_xEditInReadonlyCB(m_xBuilder->weld_check_button("editinreadonly")) + , m_xPrintFrameCB(m_xBuilder->weld_check_button("printframe")) + , m_xTextFlowFT(m_xBuilder->weld_label("textflow_label")) + , m_xTextFlowLB(new svx::FrameDirectionListBox(m_xBuilder->weld_combo_box("textflow"))) +{ + m_xTextFlowLB->append(SvxFrameDirection::Horizontal_LR_TB, SvxResId(RID_SVXSTR_FRAMEDIR_LTR)); + m_xTextFlowLB->append(SvxFrameDirection::Horizontal_RL_TB, SvxResId(RID_SVXSTR_FRAMEDIR_RTL)); + m_xTextFlowLB->append(SvxFrameDirection::Vertical_RL_TB, SvxResId(RID_SVXSTR_PAGEDIR_RTL_VERT)); + m_xTextFlowLB->append(SvxFrameDirection::Vertical_LR_TB, SvxResId(RID_SVXSTR_PAGEDIR_LTR_VERT)); + m_xTextFlowLB->append(SvxFrameDirection::Vertical_LR_BT, SvxResId(RID_SVXSTR_PAGEDIR_LTR_BTT_VERT)); + m_xTextFlowLB->append(SvxFrameDirection::Environment, SvxResId(RID_SVXSTR_FRAMEDIR_SUPER)); + m_xDescriptionED->set_size_request(-1, m_xDescriptionED->get_preferred_size().Height()); +} + +SwFrameAddPage::~SwFrameAddPage() +{ + m_xTextFlowLB.reset(); +} + +std::unique_ptr<SfxTabPage> SwFrameAddPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwFrameAddPage>(pPage, pController, *rSet); +} + +void SwFrameAddPage::Reset(const SfxItemSet *rSet ) +{ + const SfxPoolItem* pItem; + sal_uInt16 nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + m_bHtmlMode = (nHtmlMode & HTMLMODE_ON) != 0; + if (m_bHtmlMode) + { + m_xProtectFrame->hide(); + m_xEditInReadonlyCB->hide(); + m_xPrintFrameCB->hide(); + } + if (m_sDlgType == "PictureDialog" || m_sDlgType == "ObjectDialog") + { + m_xEditInReadonlyCB->hide(); + if (m_bHtmlMode) + { + m_xPropertiesFrame->hide(); + } + m_xContentAlignFrame->hide(); + } + + if(SfxItemState::SET == rSet->GetItemState(FN_SET_FRM_ALT_NAME, false, &pItem)) + { + m_xAltNameED->set_text(static_cast<const SfxStringItem*>(pItem)->GetValue()); + m_xAltNameED->save_value(); + } + + if(SfxItemState::SET == rSet->GetItemState(FN_UNO_DESCRIPTION, false, &pItem)) + { + m_xDescriptionED->set_text(static_cast<const SfxStringItem*>(pItem)->GetValue()); + m_xDescriptionED->save_value(); + } + + if(!m_bFormat) + { + // insert graphic - properties + // bNew is not set, so recognise by selection + OUString aTmpName1; + if(SfxItemState::SET == rSet->GetItemState(FN_SET_FRM_NAME, false, &pItem)) + { + aTmpName1 = static_cast<const SfxStringItem*>(pItem)->GetValue(); + } + + OSL_ENSURE(m_pWrtSh, "no Shell?"); + if( m_bNew || aTmpName1.isEmpty() ) + { + if (m_sDlgType == "PictureDialog") + aTmpName1 = m_pWrtSh->GetUniqueGrfName(); + else if (m_sDlgType == "ObjectDialog") + aTmpName1 = m_pWrtSh->GetUniqueOLEName(); + else + aTmpName1 = m_pWrtSh->GetUniqueFrameName(); + + m_pWrtSh->SetFlyName(aTmpName1); + } + + m_xNameED->set_text( aTmpName1 ); + m_xNameED->save_value(); + } + else + { + m_xNameED->set_sensitive( false ); + m_xAltNameED->set_sensitive(false); + m_xNameFT->set_sensitive( false ); + m_xAltNameFT->set_sensitive(false); + } + if (m_sDlgType == "FrameDialog" && m_xAltNameFT->get_visible()) + { + m_xAltNameFT->hide(); + m_xAltNameED->hide(); + } + else + { + m_xNameED->connect_changed(LINK(this, SwFrameAddPage, EditModifyHdl)); + } + + if (!m_bNew) + { + SwFrameFormat* pFormat = m_pWrtSh->GetFlyFrameFormat(); + + if (pFormat) + { + const SwFormatChain &rChain = pFormat->GetChain(); + const SwFlyFrameFormat* pFlyFormat; + OUString sNextChain, sPrevChain; + if ((pFlyFormat = rChain.GetPrev()) != nullptr) + { + sPrevChain = pFlyFormat->GetName(); + } + + if ((pFlyFormat = rChain.GetNext()) != nullptr) + { + sNextChain = pFlyFormat->GetName(); + } + //determine chainable frames + std::vector< OUString > aPrevPageFrames; + std::vector< OUString > aThisPageFrames; + std::vector< OUString > aNextPageFrames; + std::vector< OUString > aRemainFrames; + m_pWrtSh->GetConnectableFrameFormats(*pFormat, sNextChain, false, + aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames ); + for (sal_Int32 nEntry = m_xPrevLB->get_count(); nEntry > 1; nEntry--) + m_xPrevLB->remove(nEntry - 1); + lcl_InsertVectors(*m_xPrevLB, aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames); + if(!sPrevChain.isEmpty()) + { + if (m_xPrevLB->find_text(sPrevChain) == -1) + m_xPrevLB->insert_text(1, sPrevChain); + m_xPrevLB->set_active_text(sPrevChain); + } + else + m_xPrevLB->set_active(0); + aPrevPageFrames.clear(); + aNextPageFrames.clear(); + aThisPageFrames.clear(); + aRemainFrames.clear(); + + m_pWrtSh->GetConnectableFrameFormats(*pFormat, sPrevChain, true, + aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames ); + for (sal_Int32 nEntry = m_xNextLB->get_count(); nEntry > 1; nEntry--) + m_xNextLB->remove(nEntry - 1); + lcl_InsertVectors(*m_xNextLB, aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames); + if(!sNextChain.isEmpty()) + { + if (m_xNextLB->find_text(sNextChain) == -1) + m_xNextLB->insert_text(1, sNextChain); + m_xNextLB->set_active_text(sNextChain); + } + else + m_xNextLB->set_active(0); + Link<weld::ComboBox&,void> aLink(LINK(this, SwFrameAddPage, ChainModifyHdl)); + m_xPrevLB->connect_changed(aLink); + m_xNextLB->connect_changed(aLink); + } + } + // Pos Protected + const SvxProtectItem& rProt = rSet->Get(RES_PROTECT); + m_xProtectFrameCB->set_active(rProt.IsPosProtected()); + m_xProtectContentCB->set_active(rProt.IsContentProtected()); + m_xProtectSizeCB->set_active(rProt.IsSizeProtected()); + + const SwFormatEditInReadonly& rEdit = rSet->Get(RES_EDIT_IN_READONLY); + m_xEditInReadonlyCB->set_active(rEdit.GetValue()); + m_xEditInReadonlyCB->save_state(); + + // print + const SvxPrintItem& rPrt = rSet->Get(RES_PRINT); + m_xPrintFrameCB->set_active(rPrt.GetValue()); + m_xPrintFrameCB->save_state(); + + // textflow + if( (!m_bHtmlMode || (0 != (nHtmlMode&HTMLMODE_SOME_STYLES))) + && m_sDlgType != "PictureDialog" && m_sDlgType != "ObjectDialog" + && SfxItemState::UNKNOWN != rSet->GetItemState( RES_FRAMEDIR ) ) + { + m_xTextFlowFT->show(); + m_xTextFlowLB->show(); + + //vertical text flow is not possible in HTML + if(m_bHtmlMode) + { + m_xTextFlowLB->remove_id(SvxFrameDirection::Vertical_RL_TB); + } + SvxFrameDirection nVal = rSet->Get(RES_FRAMEDIR).GetValue(); + m_xTextFlowLB->set_active_id(nVal); + m_xTextFlowLB->save_value(); + } + else + { + m_xTextFlowFT->hide(); + m_xTextFlowLB->hide(); + } + + // Content alignment + if ( rSet->GetItemState(RES_TEXT_VERT_ADJUST) > SfxItemState::DEFAULT ) + { + SdrTextVertAdjust nAdjust = rSet->Get(RES_TEXT_VERT_ADJUST).GetValue(); + sal_Int32 nPos = 0; + switch(nAdjust) + { + case SDRTEXTVERTADJUST_TOP: nPos = 0; break; + case SDRTEXTVERTADJUST_CENTER: + case SDRTEXTVERTADJUST_BLOCK: nPos = 1; break; + case SDRTEXTVERTADJUST_BOTTOM: nPos = 2; break; + } + m_xVertAlignLB->set_active(nPos); + } + m_xVertAlignLB->save_value(); +} + +bool SwFrameAddPage::FillItemSet(SfxItemSet *rSet) +{ + bool bRet = false; + if (m_xNameED->get_value_changed_from_saved()) + bRet |= nullptr != rSet->Put(SfxStringItem(FN_SET_FRM_NAME, m_xNameED->get_text())); + if (m_xAltNameED->get_value_changed_from_saved()) + bRet |= nullptr != rSet->Put(SfxStringItem(FN_SET_FRM_ALT_NAME, m_xAltNameED->get_text())); + if (m_xDescriptionED->get_value_changed_from_saved()) + bRet |= nullptr != rSet->Put(SfxStringItem(FN_UNO_DESCRIPTION, m_xDescriptionED->get_text())); + + const SfxPoolItem* pOldItem; + SvxProtectItem aProt ( GetItemSet().Get(RES_PROTECT) ); + aProt.SetContentProtect( m_xProtectContentCB->get_active() ); + aProt.SetSizeProtect ( m_xProtectSizeCB->get_active() ); + aProt.SetPosProtect ( m_xProtectFrameCB->get_active() ); + if ( nullptr == (pOldItem = GetOldItem(*rSet, FN_SET_PROTECT)) || + aProt != *pOldItem ) + bRet |= nullptr != rSet->Put( aProt); + + if ( m_xEditInReadonlyCB->get_state_changed_from_saved() ) + bRet |= nullptr != rSet->Put( SwFormatEditInReadonly( RES_EDIT_IN_READONLY, m_xEditInReadonlyCB->get_active())); + + if ( m_xPrintFrameCB->get_state_changed_from_saved() ) + bRet |= nullptr != rSet->Put( SvxPrintItem( RES_PRINT, m_xPrintFrameCB->get_active())); + + // textflow + if (m_xTextFlowLB->get_visible() && m_xTextFlowLB->get_value_changed_from_saved()) + { + SvxFrameDirection eDirection = m_xTextFlowLB->get_active_id(); + bRet |= nullptr != rSet->Put( SvxFrameDirectionItem(eDirection, RES_FRAMEDIR )); + } + if(m_pWrtSh) + { + const SwFrameFormat* pFormat = m_pWrtSh->GetFlyFrameFormat(); + if (pFormat) + { + OUString sCurrentPrevChain, sCurrentNextChain; + if (m_xPrevLB->get_active()) + sCurrentPrevChain = m_xPrevLB->get_active_text(); + if (m_xNextLB->get_active()) + sCurrentNextChain = m_xNextLB->get_active_text(); + const SwFormatChain &rChain = pFormat->GetChain(); + const SwFlyFrameFormat* pFlyFormat; + OUString sNextChain, sPrevChain; + if ((pFlyFormat = rChain.GetPrev()) != nullptr) + sPrevChain = pFlyFormat->GetName(); + + if ((pFlyFormat = rChain.GetNext()) != nullptr) + sNextChain = pFlyFormat->GetName(); + if(sPrevChain != sCurrentPrevChain) + bRet |= nullptr != rSet->Put(SfxStringItem(FN_PARAM_CHAIN_PREVIOUS, sCurrentPrevChain)); + if(sNextChain != sCurrentNextChain) + bRet |= nullptr != rSet->Put(SfxStringItem(FN_PARAM_CHAIN_NEXT, sCurrentNextChain)); + } + } + + if (m_xVertAlignLB->get_value_changed_from_saved()) + { + SdrTextVertAdjust nAdjust; + switch (m_xVertAlignLB->get_active()) + { + default: + case 0 : nAdjust = SDRTEXTVERTADJUST_TOP; break; + case 1 : nAdjust = SDRTEXTVERTADJUST_CENTER; break; + case 2 : nAdjust = SDRTEXTVERTADJUST_BOTTOM; break; + } + bRet |= nullptr != rSet->Put(SdrTextVertAdjustItem(nAdjust, RES_TEXT_VERT_ADJUST)); + } + + return bRet; +} + +IMPL_LINK_NOARG(SwFrameAddPage, EditModifyHdl, weld::Entry&, void) +{ + bool bEnable = !m_xNameED->get_text().isEmpty(); + m_xAltNameED->set_sensitive(bEnable); + m_xAltNameFT->set_sensitive(bEnable); +} + +void SwFrameAddPage::SetFormatUsed(bool bFormatUsed) +{ + m_bFormat = bFormatUsed; + if (m_bFormat) + { + m_xNameFrame->hide(); + } +} + +IMPL_LINK(SwFrameAddPage, ChainModifyHdl, weld::ComboBox&, rBox, void) +{ + OUString sCurrentPrevChain, sCurrentNextChain; + if (m_xPrevLB->get_active()) + sCurrentPrevChain = m_xPrevLB->get_active_text(); + if (m_xNextLB->get_active()) + sCurrentNextChain = m_xNextLB->get_active_text(); + SwFrameFormat* pFormat = m_pWrtSh->GetFlyFrameFormat(); + if (!pFormat) + return; + + bool bNextBox = m_xNextLB.get() == &rBox; + weld::ComboBox& rChangeLB = bNextBox ? *m_xPrevLB : *m_xNextLB; + for (sal_Int32 nEntry = rChangeLB.get_count(); nEntry > 1; nEntry--) + rChangeLB.remove(nEntry - 1); + //determine chainable frames + std::vector< OUString > aPrevPageFrames; + std::vector< OUString > aThisPageFrames; + std::vector< OUString > aNextPageFrames; + std::vector< OUString > aRemainFrames; + m_pWrtSh->GetConnectableFrameFormats(*pFormat, bNextBox ? sCurrentNextChain : sCurrentPrevChain, !bNextBox, + aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames ); + lcl_InsertVectors(rChangeLB, + aPrevPageFrames, aThisPageFrames, aNextPageFrames, aRemainFrames); + const OUString sToSelect = bNextBox ? sCurrentPrevChain : sCurrentNextChain; + if (rChangeLB.find_text(sToSelect) != -1) + rChangeLB.set_active_text(sToSelect); + else + rChangeLB.set_active(0); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/pattern.cxx b/sw/source/ui/frmdlg/pattern.cxx new file mode 100644 index 000000000..a9de137b2 --- /dev/null +++ b/sw/source/ui/frmdlg/pattern.cxx @@ -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 . + */ + +#include <sfx2/sfxdlg.hxx> +#include <svx/dialogs.hrc> +#include <swtypes.hxx> +#include <pattern.hxx> +#include <strings.hrc> + +SwBackgroundDlg::SwBackgroundDlg(weld::Window* pParent, const SfxItemSet& rSet) + : SfxSingleTabDialogController(pParent, &rSet) + +{ + m_xDialog->set_title(SwResId(STR_FRMUI_PATTERN)); + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ); + if ( fnCreatePage ) + { + std::unique_ptr<SfxTabPage> xRet = (*fnCreatePage)(get_content_area(), this, &rSet); + xRet->PageCreated(rSet); + xRet->ActivatePage(rSet); + SetTabPage(std::move(xRet)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/uiborder.cxx b/sw/source/ui/frmdlg/uiborder.cxx new file mode 100644 index 000000000..5635b06ed --- /dev/null +++ b/sw/source/ui/frmdlg/uiborder.cxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/dialogs.hrc> +#include <svl/itemset.hxx> +#include <svx/flagsdef.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/tabdlg.hxx> +#include <svl/intitem.hxx> + +#include <swtypes.hxx> +#include <uiborder.hxx> +#include <strings.hrc> + +SwBorderDlg::SwBorderDlg(weld::Window* pParent, SfxItemSet& rSet, SwBorderModes nType) + : SfxSingleTabDialogController(pParent, &rSet) +{ + m_xDialog->set_title(SwResId(STR_FRMUI_BORDER)); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ); + + if (fnCreatePage) + { + std::unique_ptr<SfxTabPage> xNewPage = (*fnCreatePage)(get_content_area(), this, &rSet); + SfxAllItemSet aSet(*(rSet.GetPool())); + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE, static_cast<sal_uInt16>(nType))); + if(SwBorderModes::TABLE == nType) + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE,SVX_HIDESHADOWCTL)); + xNewPage->PageCreated(aSet); + SetTabPage(std::move(xNewPage)); + } +} + +SwBorderDlg::~SwBorderDlg() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/frmdlg/wrap.cxx b/sw/source/ui/frmdlg/wrap.cxx new file mode 100644 index 000000000..a09c8475c --- /dev/null +++ b/sw/source/ui/frmdlg/wrap.cxx @@ -0,0 +1,627 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/graph.hxx> + +#include <sfx2/htmlmode.hxx> +#include <sfx2/objsh.hxx> +#include <svl/intitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <fmtfollowtextflow.hxx> +#include <svtools/unitconv.hxx> +#include <svx/swframevalidation.hxx> + +#include <cmdid.h> +#include <docsh.hxx> +#include <uitool.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <viewopt.hxx> +#include <fmtsrnd.hxx> +#include <frmmgr.hxx> +#include <wrap.hxx> +#include <bitmaps.hlst> +#include <fmtwrapinfluenceonobjpos.hxx> + +using namespace ::com::sun::star; + +const sal_uInt16 SwWrapTabPage::m_aWrapPageRg[] = { + RES_LR_SPACE, RES_UL_SPACE, + RES_PROTECT, RES_SURROUND, + RES_PRINT, RES_PRINT, + 0 +}; + +SwWrapDlg::SwWrapDlg(weld::Window* pParent, SfxItemSet& rSet, SwWrtShell* pWrtShell, bool bDrawMode) + : SfxSingleTabDialogController(pParent, &rSet, "modules/swriter/ui/wrapdialog.ui", "WrapDialog") +{ + // create TabPage + auto xNewPage = SwWrapTabPage::Create(get_content_area(), this, &rSet); + SwWrapTabPage* pWrapPage = static_cast<SwWrapTabPage*>(xNewPage.get()); + pWrapPage->SetFormatUsed(false, bDrawMode); + pWrapPage->SetShell(pWrtShell); + SetTabPage(std::move(xNewPage)); +} + +SwWrapTabPage::SwWrapTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/wrappage.ui", "WrapPage", &rSet) + , m_nAnchorId(RndStdIds::FLY_AT_PARA) + , m_nHtmlMode(0) + , m_pWrtSh(nullptr) + , m_bFormat(false) + , m_bNew(true) + , m_bHtmlMode(false) + , m_bDrawMode(false) + , m_bContourImage(false) + , m_xNoWrapRB(m_xBuilder->weld_radio_button("none")) + , m_xWrapLeftRB(m_xBuilder->weld_radio_button("before")) + , m_xWrapRightRB(m_xBuilder->weld_radio_button("after")) + , m_xWrapParallelRB(m_xBuilder->weld_radio_button("parallel")) + , m_xWrapThroughRB(m_xBuilder->weld_radio_button("through")) + , m_xIdealWrapRB(m_xBuilder->weld_radio_button("optimal")) + , m_xLeftMarginED(m_xBuilder->weld_metric_spin_button("left", FieldUnit::CM)) + , m_xRightMarginED(m_xBuilder->weld_metric_spin_button("right", FieldUnit::CM)) + , m_xTopMarginED(m_xBuilder->weld_metric_spin_button("top", FieldUnit::CM)) + , m_xBottomMarginED(m_xBuilder->weld_metric_spin_button("bottom", FieldUnit::CM)) + , m_xWrapAnchorOnlyCB(m_xBuilder->weld_check_button("anchoronly")) + , m_xWrapTransparentCB(m_xBuilder->weld_check_button("transparent")) + , m_xWrapOutlineCB(m_xBuilder->weld_check_button("outline")) + , m_xWrapOutsideCB(m_xBuilder->weld_check_button("outside")) + , m_xAllowOverlapCB(m_xBuilder->weld_check_button("allowoverlap")) +{ + SetExchangeSupport(); + + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwWrapTabPage, RangeModifyHdl); + m_xLeftMarginED->connect_value_changed(aLk); + m_xRightMarginED->connect_value_changed(aLk); + m_xTopMarginED->connect_value_changed(aLk); + m_xBottomMarginED->connect_value_changed(aLk); + + Link<weld::ToggleButton&,void> aLk2 = LINK(this, SwWrapTabPage, WrapTypeHdl); + m_xNoWrapRB->connect_toggled(aLk2); + m_xWrapLeftRB->connect_toggled(aLk2); + m_xWrapRightRB->connect_toggled(aLk2); + m_xWrapParallelRB->connect_toggled(aLk2); + m_xWrapThroughRB->connect_toggled(aLk2); + m_xIdealWrapRB->connect_toggled(aLk2); + SetImages(); + m_xWrapOutlineCB->connect_toggled(LINK(this, SwWrapTabPage, ContourHdl)); +} + +SwWrapTabPage::~SwWrapTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SwWrapTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwWrapTabPage>(pPage, pController, *rSet); +} + +void SwWrapTabPage::Reset(const SfxItemSet *rSet) +{ + // contour for Draw, Graphic and OLE (Insert/Graphic/Properties still missing!) + if( m_bDrawMode ) + { + m_xWrapOutlineCB->show(); + m_xWrapOutsideCB->show(); + + m_xWrapTransparentCB->set_active( 0 == static_cast<const SfxInt16Item&>(rSet->Get( + FN_DRAW_WRAP_DLG)).GetValue() ); + m_xWrapTransparentCB->save_state(); + } + else + { + bool bShowCB = m_bFormat; + if( !m_bFormat ) + { + SelectionType nSelType = m_pWrtSh->GetSelectionType(); + if( ( nSelType & SelectionType::Graphic ) || + ( nSelType & SelectionType::Ole && GraphicType::NONE != + m_pWrtSh->GetIMapGraphic().GetType() )) + bShowCB = true; + } + if( bShowCB ) + { + m_xWrapOutlineCB->show(); + m_xWrapOutsideCB->show(); + } + } + + m_nHtmlMode = ::GetHtmlMode(static_cast<const SwDocShell*>(SfxObjectShell::Current())); + m_bHtmlMode = (m_nHtmlMode & HTMLMODE_ON) != 0; + + FieldUnit aMetric = ::GetDfltMetric(m_bHtmlMode); + SetFieldUnit(*m_xLeftMarginED, aMetric); + SetFieldUnit(*m_xRightMarginED, aMetric); + SetFieldUnit(*m_xTopMarginED, aMetric); + SetFieldUnit(*m_xBottomMarginED, aMetric); + + const SwFormatSurround& rSurround = rSet->Get(RES_SURROUND); + + css::text::WrapTextMode nSur = rSurround.GetSurround(); + const SwFormatAnchor &rAnch = rSet->Get(RES_ANCHOR); + m_nAnchorId = rAnch.GetAnchorId(); + + if (((m_nAnchorId == RndStdIds::FLY_AT_PARA) || (m_nAnchorId == RndStdIds::FLY_AT_CHAR)) + && (nSur != css::text::WrapTextMode_NONE)) + { + m_xWrapAnchorOnlyCB->set_active(rSurround.IsAnchorOnly()); + } + else + { + m_xWrapAnchorOnlyCB->set_sensitive(false); + } + + const bool bContour = rSurround.IsContour(); + m_xWrapOutlineCB->set_active(bContour); + m_xWrapOutsideCB->set_active(rSurround.IsOutside()); + m_xWrapThroughRB->set_sensitive(!m_xWrapOutlineCB->get_active()); + m_bContourImage = !bContour; + + weld::RadioButton* pBtn = nullptr; + + switch (nSur) + { + case css::text::WrapTextMode_NONE: + { + pBtn = m_xNoWrapRB.get(); + break; + } + + case css::text::WrapTextMode_THROUGH: + { + // transparent ? + pBtn = m_xWrapThroughRB.get(); + + if (!m_bDrawMode) + { + const SvxOpaqueItem& rOpaque = rSet->Get(RES_OPAQUE); + m_xWrapTransparentCB->set_active(!rOpaque.GetValue()); + } + break; + } + + case css::text::WrapTextMode_PARALLEL: + { + pBtn = m_xWrapParallelRB.get(); + break; + } + + case css::text::WrapTextMode_DYNAMIC: + { + pBtn = m_xIdealWrapRB.get(); + break; + } + + default: + { + if (nSur == css::text::WrapTextMode_LEFT) + pBtn = m_xWrapLeftRB.get(); + else if (nSur == css::text::WrapTextMode_RIGHT) + pBtn = m_xWrapRightRB.get(); + } + } + if (pBtn) + { + pBtn->set_active(true); + WrapTypeHdl(*pBtn); + // For character objects that currently are in passage, the default + // "contour on" is prepared here, in case we switch to any other + // passage later. + if (m_bDrawMode && !m_xWrapOutlineCB->get_sensitive()) + m_xWrapOutlineCB->set_active(true); + } + m_xWrapTransparentCB->set_sensitive(pBtn == m_xWrapThroughRB.get() && !m_bHtmlMode); + + const SvxULSpaceItem& rUL = rSet->Get(RES_UL_SPACE); + const SvxLRSpaceItem& rLR = rSet->Get(RES_LR_SPACE); + + // gap to text + m_xLeftMarginED->set_value(m_xLeftMarginED->normalize(rLR.GetLeft()), FieldUnit::TWIP); + m_xRightMarginED->set_value(m_xRightMarginED->normalize(rLR.GetRight()), FieldUnit::TWIP); + m_xTopMarginED->set_value(m_xTopMarginED->normalize(rUL.GetUpper()), FieldUnit::TWIP); + m_xBottomMarginED->set_value(m_xBottomMarginED->normalize(rUL.GetLower()), FieldUnit::TWIP); + + m_xLeftMarginED->save_value(); + m_xRightMarginED->save_value(); + m_xTopMarginED->save_value(); + m_xBottomMarginED->save_value(); + + ContourHdl(*m_xWrapOutlineCB); + + const SwFormatWrapInfluenceOnObjPos& rInfluence = rSet->Get(RES_WRAP_INFLUENCE_ON_OBJPOS); + m_xAllowOverlapCB->set_active(rInfluence.GetAllowOverlap()); + + ActivatePage( *rSet ); +} + +// stuff attributes into the set when OK +bool SwWrapTabPage::FillItemSet(SfxItemSet *rSet) +{ + bool bModified = false; + const SfxPoolItem* pOldItem; + const SwFormatSurround& rOldSur = GetItemSet().Get(RES_SURROUND); + SwFormatSurround aSur( rOldSur ); + + std::shared_ptr<SvxOpaqueItem> aOp(std::make_shared<SvxOpaqueItem>(RES_OPAQUE)); + + if (!m_bDrawMode) + { + aOp.reset(GetItemSet().Get(RES_OPAQUE).Clone()); + aOp->SetValue(true); + } + + if (m_xNoWrapRB->get_active()) + aSur.SetSurround(css::text::WrapTextMode_NONE); + else if (m_xWrapLeftRB->get_active()) + aSur.SetSurround(css::text::WrapTextMode_LEFT); + else if (m_xWrapRightRB->get_active()) + aSur.SetSurround(css::text::WrapTextMode_RIGHT); + else if (m_xWrapParallelRB->get_active()) + aSur.SetSurround(css::text::WrapTextMode_PARALLEL); + else if (m_xWrapThroughRB->get_active()) + { + aSur.SetSurround(css::text::WrapTextMode_THROUGH); + if (m_xWrapTransparentCB->get_active() && !m_bDrawMode) + aOp->SetValue(false); + } + else if (m_xIdealWrapRB->get_active()) + aSur.SetSurround(css::text::WrapTextMode_DYNAMIC); + + aSur.SetAnchorOnly( m_xWrapAnchorOnlyCB->get_active() ); + bool bContour = m_xWrapOutlineCB->get_active() && m_xWrapOutlineCB->get_sensitive(); + aSur.SetContour( bContour ); + + if ( bContour ) + aSur.SetOutside(m_xWrapOutsideCB->get_active()); + + if(nullptr == (pOldItem = GetOldItem( *rSet, RES_SURROUND )) || + aSur != *pOldItem ) + { + rSet->Put(aSur); + bModified = true; + } + + if (!m_bDrawMode) + { + if(nullptr == (pOldItem = GetOldItem( *rSet, FN_OPAQUE )) || + *aOp != *pOldItem ) + { + rSet->Put(*aOp); + bModified = true; + } + } + + bool bTopMod = m_xTopMarginED->get_value_changed_from_saved(); + bool bBottomMod = m_xBottomMarginED->get_value_changed_from_saved(); + + SvxULSpaceItem aUL( RES_UL_SPACE ); + aUL.SetUpper(static_cast<sal_uInt16>(m_xTopMarginED->denormalize(m_xTopMarginED->get_value(FieldUnit::TWIP)))); + aUL.SetLower(static_cast<sal_uInt16>(m_xBottomMarginED->denormalize(m_xBottomMarginED->get_value(FieldUnit::TWIP)))); + + if ( bTopMod || bBottomMod ) + { + if(nullptr == (pOldItem = GetOldItem(*rSet, RES_UL_SPACE)) || + aUL != *pOldItem ) + { + rSet->Put( aUL ); + bModified = true; + } + } + + bool bLeftMod = m_xLeftMarginED->get_value_changed_from_saved(); + bool bRightMod = m_xRightMarginED->get_value_changed_from_saved(); + + SvxLRSpaceItem aLR( RES_LR_SPACE ); + aLR.SetLeft(static_cast<sal_uInt16>(m_xLeftMarginED->denormalize(m_xLeftMarginED->get_value(FieldUnit::TWIP)))); + aLR.SetRight(static_cast<sal_uInt16>(m_xRightMarginED->denormalize(m_xRightMarginED->get_value(FieldUnit::TWIP)))); + + if ( bLeftMod || bRightMod ) + { + if( nullptr == (pOldItem = GetOldItem(*rSet, RES_LR_SPACE)) || + aLR != *pOldItem ) + { + rSet->Put(aLR); + bModified = true; + } + } + + if ( m_bDrawMode ) + { + bool bChecked = m_xWrapTransparentCB->get_active() && m_xWrapTransparentCB->get_sensitive(); + if ((m_xWrapTransparentCB->get_saved_state() == TRISTATE_TRUE) != bChecked) + bModified |= nullptr != rSet->Put(SfxInt16Item(FN_DRAW_WRAP_DLG, bChecked ? 0 : 1)); + } + + const SwFormatWrapInfluenceOnObjPos& rOldInfluence + = GetItemSet().Get(RES_WRAP_INFLUENCE_ON_OBJPOS); + SwFormatWrapInfluenceOnObjPos aInfluence(rOldInfluence); + aInfluence.SetAllowOverlap(m_xAllowOverlapCB->get_active()); + + pOldItem = GetOldItem(*rSet, RES_WRAP_INFLUENCE_ON_OBJPOS); + if (!pOldItem || aInfluence != *pOldItem) + { + rSet->Put(aInfluence); + bModified = true; + } + + return bModified; +} + +// example update +void SwWrapTabPage::ActivatePage(const SfxItemSet& rSet) +{ + // anchor + const SwFormatAnchor &rAnch = rSet.Get(RES_ANCHOR); + m_nAnchorId = rAnch.GetAnchorId(); + bool bEnable = (m_nAnchorId != RndStdIds::FLY_AS_CHAR); + + if (!m_bDrawMode) + { + SwWrtShell* pSh = m_bFormat ? ::GetActiveWrtShell() : m_pWrtSh; + SwFlyFrameAttrMgr aMgr( m_bNew, pSh, GetItemSet() ); + SvxSwFrameValidation aVal; + + // size + const SwFormatFrameSize& rFrameSize = rSet.Get(RES_FRM_SIZE); + Size aSize = rFrameSize.GetSize(); + + // position + const SwFormatHoriOrient& rHori = rSet.Get(RES_HORI_ORIENT); + const SwFormatVertOrient& rVert = rSet.Get(RES_VERT_ORIENT); + + aVal.nAnchorType = m_nAnchorId; + aVal.bAutoHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum; + aVal.bMirror = rHori.IsPosToggle(); + // #i18732# + aVal.bFollowTextFlow = rSet.Get(RES_FOLLOW_TEXT_FLOW).GetValue(); + + aVal.nHoriOrient = static_cast<short>(rHori.GetHoriOrient()); + aVal.nVertOrient = static_cast<short>(rVert.GetVertOrient()); + + aVal.nHPos = rHori.GetPos(); + aVal.nHRelOrient = rHori.GetRelationOrient(); + aVal.nVPos = rVert.GetPos(); + aVal.nVRelOrient = rVert.GetRelationOrient(); + + if (rFrameSize.GetWidthPercent() && rFrameSize.GetWidthPercent() != SwFormatFrameSize::SYNCED) + aSize.setWidth( aSize.Width() * rFrameSize.GetWidthPercent() / 100 ); + + if (rFrameSize.GetHeightPercent() && rFrameSize.GetHeightPercent() != SwFormatFrameSize::SYNCED) + aSize.setHeight( aSize.Height() * rFrameSize.GetHeightPercent() / 100 ); + + aVal.nWidth = aSize.Width(); + aVal.nHeight = aSize.Height(); + + aMgr.ValidateMetrics(aVal, nullptr); + + SwTwips nLeft; + SwTwips nRight; + SwTwips nTop; + SwTwips nBottom; + + nLeft = aVal.nHPos - aVal.nMinHPos; + nRight = aVal.nMaxWidth - aVal.nWidth; + nTop = aVal.nVPos - aVal.nMinVPos; + nBottom = aVal.nMaxHeight - aVal.nHeight; + + { + if (aVal.nAnchorType == RndStdIds::FLY_AS_CHAR) + { + nLeft = nRight; + + if (aVal.nVPos < 0) + { + if (aVal.nVPos <= aVal.nMaxHeight) + nTop = aVal.nMaxVPos - aVal.nHeight; + else + nTop = nBottom = 0; // no passage + } + else + nTop = aVal.nMaxVPos - aVal.nHeight - aVal.nVPos; + } + else + { + nLeft += nRight; + nTop += nBottom; + } + + nBottom = nTop; + nRight = nLeft; + } + + m_xLeftMarginED->set_max(m_xLeftMarginED->normalize(nLeft), FieldUnit::TWIP); + m_xRightMarginED->set_max(m_xRightMarginED->normalize(nRight), FieldUnit::TWIP); + + m_xTopMarginED->set_max(m_xTopMarginED->normalize(nTop), FieldUnit::TWIP); + m_xBottomMarginED->set_max(m_xBottomMarginED->normalize(nBottom), FieldUnit::TWIP); + + RangeModifyHdl(*m_xLeftMarginED); + RangeModifyHdl(*m_xTopMarginED); + } + + const SwFormatSurround& rSurround = rSet.Get(RES_SURROUND); + css::text::WrapTextMode nSur = rSurround.GetSurround(); + + m_xWrapTransparentCB->set_sensitive(bEnable && !m_bHtmlMode && nSur == css::text::WrapTextMode_THROUGH); + if(m_bHtmlMode) + { + const SwFormatHoriOrient& rHori = rSet.Get(RES_HORI_ORIENT); + sal_Int16 eHOrient = rHori.GetHoriOrient(); + sal_Int16 eHRelOrient = rHori.GetRelationOrient(); + m_xWrapOutlineCB->hide(); + const bool bAllHtmlModes = + ((m_nAnchorId == RndStdIds::FLY_AT_PARA) || (m_nAnchorId == RndStdIds::FLY_AT_CHAR)) && + (eHOrient == text::HoriOrientation::RIGHT || eHOrient == text::HoriOrientation::LEFT); + m_xWrapAnchorOnlyCB->set_sensitive(bAllHtmlModes && nSur != css::text::WrapTextMode_NONE); + m_xWrapOutsideCB->hide(); + m_xIdealWrapRB->set_sensitive(false); + + m_xWrapTransparentCB->set_sensitive(false); + m_xNoWrapRB->set_sensitive(RndStdIds::FLY_AT_PARA == m_nAnchorId); + m_xWrapParallelRB->set_sensitive(false); + m_xWrapLeftRB->set_sensitive + ( (RndStdIds::FLY_AT_PARA == m_nAnchorId) + || ( (RndStdIds::FLY_AT_CHAR == m_nAnchorId) + && (eHOrient == text::HoriOrientation::RIGHT) + && (eHRelOrient == text::RelOrientation::PRINT_AREA))); + m_xWrapRightRB->set_sensitive + ( (RndStdIds::FLY_AT_PARA == m_nAnchorId) + || ( (RndStdIds::FLY_AT_CHAR == m_nAnchorId) + && (eHOrient == text::HoriOrientation::LEFT) + && (eHRelOrient == text::RelOrientation::PRINT_AREA))); + + m_xWrapThroughRB->set_sensitive + ( ( (RndStdIds::FLY_AT_PAGE == m_nAnchorId) + || ( (RndStdIds::FLY_AT_CHAR == m_nAnchorId) + && (eHRelOrient != text::RelOrientation::PRINT_AREA)) + || (RndStdIds::FLY_AT_PARA == m_nAnchorId)) + && (eHOrient != text::HoriOrientation::RIGHT)); + if (m_xNoWrapRB->get_active() && !m_xNoWrapRB->get_sensitive()) + { + if(m_xWrapThroughRB->get_sensitive()) + m_xWrapThroughRB->set_active(true); + else if(m_xWrapLeftRB->get_sensitive()) + m_xWrapLeftRB->set_active(true); + else if(m_xWrapRightRB->get_sensitive()) + m_xWrapRightRB->set_active(true); + + } + if (m_xWrapLeftRB->get_active() && !m_xWrapLeftRB->get_sensitive()) + { + if(m_xWrapRightRB->get_sensitive()) + m_xWrapRightRB->set_active(true); + else if(m_xWrapThroughRB->get_sensitive()) + m_xWrapThroughRB->set_active(true); + } + if (m_xWrapRightRB->get_active() && !m_xWrapRightRB->get_sensitive()) + { + if(m_xWrapLeftRB->get_sensitive()) + m_xWrapLeftRB->set_active(true); + else if(m_xWrapThroughRB->get_sensitive()) + m_xWrapThroughRB->set_active(true); + } + if (m_xWrapThroughRB->get_active() && !m_xWrapThroughRB->get_sensitive()) + if(m_xNoWrapRB->get_sensitive()) + m_xNoWrapRB->set_active(true); + + if (m_xWrapParallelRB->get_active() && !m_xWrapParallelRB->get_sensitive()) + m_xWrapThroughRB->set_active(true); + } + else + { + m_xNoWrapRB->set_sensitive(bEnable); + m_xWrapLeftRB->set_sensitive(bEnable); + m_xWrapRightRB->set_sensitive(bEnable); + m_xIdealWrapRB->set_sensitive(bEnable); + m_xWrapThroughRB->set_sensitive(bEnable); + m_xWrapParallelRB->set_sensitive(bEnable); + m_xWrapAnchorOnlyCB->set_sensitive( + ((m_nAnchorId == RndStdIds::FLY_AT_PARA) || (m_nAnchorId == RndStdIds::FLY_AT_CHAR)) + && nSur != css::text::WrapTextMode_NONE ); + } + ContourHdl(*m_xWrapOutlineCB); +} + +DeactivateRC SwWrapTabPage::DeactivatePage(SfxItemSet* _pSet) +{ + if(_pSet) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +IMPL_LINK(SwWrapTabPage, RangeModifyHdl, weld::MetricSpinButton&, rEdit, void) +{ + auto nValue = rEdit.get_value(FieldUnit::NONE); + weld::MetricSpinButton* pOpposite = nullptr; + if (&rEdit == m_xLeftMarginED.get()) + pOpposite = m_xRightMarginED.get(); + else if (&rEdit == m_xRightMarginED.get()) + pOpposite = m_xLeftMarginED.get(); + else if (&rEdit == m_xTopMarginED.get()) + pOpposite = m_xBottomMarginED.get(); + else if (&rEdit == m_xBottomMarginED.get()) + pOpposite = m_xTopMarginED.get(); + + assert(pOpposite); + + if (pOpposite) + { + auto nOpposite = pOpposite->get_value(FieldUnit::NONE); + + if (nValue + nOpposite > std::max(rEdit.get_max(FieldUnit::NONE), pOpposite->get_max(FieldUnit::NONE))) + pOpposite->set_value(pOpposite->get_max(FieldUnit::NONE) - nValue, FieldUnit::NONE); + } +} + +IMPL_LINK_NOARG(SwWrapTabPage, WrapTypeHdl, weld::ToggleButton&, void) +{ + bool bWrapThrough = m_xWrapThroughRB->get_active(); + m_xWrapTransparentCB->set_sensitive(bWrapThrough && !m_bHtmlMode); + bWrapThrough |= ( m_nAnchorId == RndStdIds::FLY_AS_CHAR ); + m_xWrapOutlineCB->set_sensitive(!bWrapThrough && !m_xNoWrapRB->get_active()); + m_xWrapOutsideCB->set_sensitive(!bWrapThrough && m_xWrapOutlineCB->get_active()); + m_xWrapAnchorOnlyCB->set_sensitive( + ((m_nAnchorId == RndStdIds::FLY_AT_PARA) || (m_nAnchorId == RndStdIds::FLY_AT_CHAR)) && + (!m_xNoWrapRB->get_active()) ); + + ContourHdl(*m_xWrapOutlineCB); +} + +IMPL_LINK_NOARG(SwWrapTabPage, ContourHdl, weld::ToggleButton&, void) +{ + bool bEnable = !(m_xWrapOutlineCB->get_active() && m_xWrapOutlineCB->get_sensitive()); + + m_xWrapOutsideCB->set_sensitive(!bEnable); + + bEnable = !m_xWrapOutlineCB->get_active(); + if (bEnable == m_bContourImage) // so that it doesn't always flicker + { + m_bContourImage = !bEnable; + SetImages(); + } +} + +void SwWrapTabPage::SetImages() +{ + m_xWrapThroughRB->set_from_icon_name(RID_BMP_WRAP_THROUGH); + bool bWrapOutline = !m_xWrapOutlineCB->get_active(); + if (bWrapOutline) + { + m_xNoWrapRB->set_from_icon_name(RID_BMP_WRAP_NONE); + m_xWrapLeftRB->set_from_icon_name(RID_BMP_WRAP_LEFT); + m_xWrapRightRB->set_from_icon_name(RID_BMP_WRAP_RIGHT); + m_xWrapParallelRB->set_from_icon_name(RID_BMP_WRAP_PARALLEL); + m_xIdealWrapRB->set_from_icon_name(RID_BMP_WRAP_IDEAL); + } + else + { + m_xNoWrapRB->set_from_icon_name(RID_BMP_WRAP_CONTOUR_NONE); + m_xWrapLeftRB->set_from_icon_name(RID_BMP_WRAP_CONTOUR_LEFT); + m_xWrapRightRB->set_from_icon_name(RID_BMP_WRAP_CONTOUR_RIGHT); + m_xWrapParallelRB->set_from_icon_name(RID_BMP_WRAP_CONTOUR_PARALLEL); + m_xIdealWrapRB->set_from_icon_name(RID_BMP_WRAP_CONTOUR_IDEAL); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/inc/mmresultdialogs.hxx b/sw/source/ui/inc/mmresultdialogs.hxx new file mode 100644 index 000000000..4db8e0e76 --- /dev/null +++ b/sw/source/ui/inc/mmresultdialogs.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMOUTPUTPAGE_HXX +#define INCLUDED_SW_SOURCE_UI_DBUI_MMOUTPUTPAGE_HXX + +#include <vcl/wizardmachine.hxx> +#include <vcl/weld.hxx> +#include <vcl/print.hxx> +#include <sfx2/basedlgs.hxx> +#include <mailmergehelper.hxx> + +class SwMailMergeWizard; +class SfxPrinter; +class SwSendMailDialog; +namespace com::sun::star::mail { class XMailMessage; } + + +/// Dialog implementing the saving as of the result document. +class SwMMResultSaveDialog : public SfxDialogController +{ + bool m_bCancelSaving; + + std::unique_ptr<weld::RadioButton> m_xSaveAsOneRB; + std::unique_ptr<weld::RadioButton> m_xSaveIndividualRB; + std::unique_ptr<weld::RadioButton> m_xFromRB; + std::unique_ptr<weld::SpinButton> m_xFromNF; + std::unique_ptr<weld::Label> m_xToFT; + std::unique_ptr<weld::SpinButton> m_xToNF; + std::unique_ptr<weld::Button> m_xOKButton; + + DECL_LINK(SaveOutputHdl_Impl, weld::Button& , void); + DECL_LINK(DocumentSelectionHdl_Impl, weld::ToggleButton&, void); + +public: + SwMMResultSaveDialog(weld::Window* pParent); + virtual ~SwMMResultSaveDialog() override; +}; + +/// Dialog implementing the printing of the result document. +class SwMMResultPrintDialog : public SfxDialogController +{ + VclPtr<Printer> m_pTempPrinter; + + std::unique_ptr<weld::Label> m_xPrinterFT; + std::unique_ptr<weld::ComboBox> m_xPrinterLB; + std::unique_ptr<weld::Button> m_xPrinterSettingsPB; + std::unique_ptr<weld::RadioButton> m_xPrintAllRB; + std::unique_ptr<weld::RadioButton> m_xFromRB; + std::unique_ptr<weld::SpinButton> m_xFromNF; + std::unique_ptr<weld::Label> m_xToFT; + std::unique_ptr<weld::SpinButton> m_xToNF; + std::unique_ptr<weld::Button> m_xOKButton; + + DECL_LINK(PrinterChangeHdl_Impl, weld::ComboBox&, void ); + DECL_LINK(PrintHdl_Impl, weld::Button&, void); + DECL_LINK(PrinterSetupHdl_Impl, weld::Button&, void ); + DECL_LINK(DocumentSelectionHdl_Impl, weld::ToggleButton&, void); + + void FillInPrinterSettings(); + +public: + SwMMResultPrintDialog(weld::Window* pParent); + virtual ~SwMMResultPrintDialog() override; +}; + +/// Dialog implementing the sending as email of the result document. +class SwMMResultEmailDialog : public SfxDialogController +{ + OUString m_sConfigureMail; + OUString m_sCC; + OUString m_sBCC; + OUString m_sBody; + + std::unique_ptr<weld::Label> m_xMailToFT; + std::unique_ptr<weld::ComboBox> m_xMailToLB; + std::unique_ptr<weld::Button> m_xCopyToPB; + std::unique_ptr<weld::Label> m_xSubjectFT; + std::unique_ptr<weld::Entry> m_xSubjectED; + std::unique_ptr<weld::Label> m_xSendAsFT; + std::unique_ptr<weld::ComboBox> m_xSendAsLB; + std::unique_ptr<weld::Button> m_xSendAsPB; + std::unique_ptr<weld::Widget> m_xAttachmentGroup; + std::unique_ptr<weld::Entry> m_xAttachmentED; + std::unique_ptr<weld::RadioButton> m_xSendAllRB; + std::unique_ptr<weld::RadioButton> m_xFromRB; + std::unique_ptr<weld::SpinButton> m_xFromNF; + std::unique_ptr<weld::Label> m_xToFT; + std::unique_ptr<weld::SpinButton> m_xToNF; + std::unique_ptr<weld::Button> m_xOKButton; + + DECL_LINK(CopyToHdl_Impl, weld::Button&, void); + DECL_LINK(SendTypeHdl_Impl, weld::ComboBox&, void); + DECL_LINK(SendAsHdl_Impl, weld::Button&, void); + DECL_LINK(SendDocumentsHdl_Impl, weld::Button&, void); + DECL_LINK(DocumentSelectionHdl_Impl, weld::ToggleButton&, void); + + void FillInEmailSettings(); + +public: + SwMMResultEmailDialog(weld::Window *pParent); + virtual ~SwMMResultEmailDialog() override; +}; + +struct SwMailDescriptor +{ + OUString sEMail; + OUString sAttachmentURL; + OUString sAttachmentName; + OUString sMimeType; + OUString sSubject; + OUString sBodyMimeType; + OUString sBodyContent; + + OUString sCC; + OUString sBCC; +}; +struct SwSendMailDialog_Impl; +class SwMailMergeConfigItem; +class SwSendMailDialog : public weld::GenericDialogController +{ + OUString m_sContinue; + OUString m_sStop; + OUString m_sTransferStatus; + OUString m_sErrorStatus; + OUString m_sSendingTo; + OUString m_sCompleted; + OUString m_sFailed; + + bool m_bCancel; + bool m_bDestructionEnabled; + + std::unique_ptr<SwSendMailDialog_Impl> m_pImpl; + SwMailMergeConfigItem* m_pConfigItem; + sal_Int32 m_nExpectedCount; + sal_Int32 m_nSendCount; + sal_Int32 m_nErrorCount; + + std::unique_ptr<weld::Label> m_xTransferStatus; + std::unique_ptr<weld::Label> m_xPaused; + std::unique_ptr<weld::ProgressBar> m_xProgressBar; + std::unique_ptr<weld::Label> m_xErrorStatus; + std::unique_ptr<weld::TreeView> m_xStatus; + std::unique_ptr<weld::Button> m_xStop; + std::unique_ptr<weld::Button> m_xClose; + std::unique_ptr<weld::Expander> m_xExpander; + + DECL_LINK( StopHdl_Impl, weld::Button&, void ); + DECL_LINK( CloseHdl_Impl, weld::Button& , void); + DECL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, void ); + DECL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, void ); + DECL_LINK( RemoveThis, Timer*, void ); + + void IterateMails(); + void SendMails(); + void UpdateTransferStatus(); + +public: + SwSendMailDialog( weld::Window* pParent, SwMailMergeConfigItem& ); + virtual ~SwSendMailDialog() override; + + void AddDocument( SwMailDescriptor const & rDesc ); + void EnableDestruction() {m_bDestructionEnabled = true;} + void StartSend(sal_Int32 nExpectedCount); + + void DocumentSent( css::uno::Reference< css::mail::XMailMessage> const & xMessage, + bool bResult, + const OUString* pError ); + void AllMailsSent(); + +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/inc/swuiexp.hxx b/sw/source/ui/inc/swuiexp.hxx new file mode 100644 index 000000000..4419a7029 --- /dev/null +++ b/sw/source/ui/inc/swuiexp.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_UI_INC_SWUIEXP_HXX +#define INCLUDED_SW_SOURCE_UI_INC_SWUIEXP_HXX + +#include <swabstdlg.hxx> + +namespace swui +{ + SwAbstractDialogFactory& GetFactory(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/index/cntex.cxx b/sw/source/ui/index/cntex.cxx new file mode 100644 index 000000000..3b853f804 --- /dev/null +++ b/sw/source/ui/index/cntex.cxx @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XDocumentIndexesSupplier.hpp> +#include <com/sun/star/text/XTextSectionsSupplier.hpp> +#include <com/sun/star/text/XDependentTextField.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> +#include <com/sun/star/text/ChapterFormat.hpp> +#include <com/sun/star/text/XTextSection.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <comphelper/string.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <cnttab.hxx> +#include <unoprnms.hxx> +#include <unotools.hxx> +#include <unotxdoc.hxx> +#include <docsh.hxx> + +#include <SwStyleNameMapper.hxx> +#include <swuicnttab.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::uno; +using namespace com::sun::star::ui::dialogs; + +static void lcl_SetProp( uno::Reference< XPropertySetInfo > const & xInfo, + uno::Reference< XPropertySet > const & xProps, + OUString const & aPropName, const OUString& rValue) +{ + if(xInfo->hasPropertyByName(aPropName)) + { + uno::Any aValue; + aValue <<= rValue; + xProps->setPropertyValue(aPropName, aValue); + } +} + +static void lcl_SetProp( uno::Reference< XPropertySetInfo > const & xInfo, + uno::Reference< XPropertySet > const & xProps, + OUString const & aPropName, sal_Int16 nValue ) +{ + if(xInfo->hasPropertyByName(aPropName)) + { + uno::Any aValue; + aValue <<= nValue; + xProps->setPropertyValue(aPropName, aValue); + } +} + +static void lcl_SetBOOLProp( + uno::Reference< beans::XPropertySetInfo > const & xInfo, + uno::Reference< beans::XPropertySet > const & xProps, + OUString const & aPropName, bool bValue ) +{ + if(xInfo->hasPropertyByName(aPropName)) + { + xProps->setPropertyValue(aPropName, makeAny(bValue)); + } +} + +IMPL_LINK_NOARG(SwMultiTOXTabDialog, CreateExample_Hdl, SwOneExampleFrame&, void) +{ + try + { + uno::Reference< frame::XModel > & xModel = m_xExampleFrame->GetModel(); + auto pDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(xModel); + + if( pDoc ) + pDoc->GetDocShell()->LoadStyles_( *m_rWrtShell.GetView().GetDocShell(), true ); + + uno::Reference< text::XTextSectionsSupplier > xSectionSupplier( + xModel, uno::UNO_QUERY); + uno::Reference< container::XNameAccess > xSections = + xSectionSupplier->getTextSections(); + + for(int i = 0; i < 7; ++i ) + { + OUString sTmp = "IndexSection_" + OUString::number(i); + uno::Any aSection = xSections->getByName( sTmp ); + aSection >>= m_vTypeData[i].m_pxIndexSections->xContainerSection; + } + uno::Reference< text::XDocumentIndexesSupplier > xIdxSupp(xModel, uno::UNO_QUERY); + uno::Reference< container::XIndexAccess > xIdxs = xIdxSupp->getDocumentIndexes(); + int n = xIdxs->getCount(); + while(n) + { + n--; + uno::Any aIdx = xIdxs->getByIndex(n); + uno::Reference< text::XDocumentIndex > xIdx; + aIdx >>= xIdx; + xIdx->dispose(); + } + CreateOrUpdateExample(m_eCurrentTOXType.eType); + } + catch (const Exception&) + { + OSL_FAIL("::CreateExample() - exception caught"); + } +} + +void SwMultiTOXTabDialog::CreateOrUpdateExample( + TOXTypes nTOXIndex, sal_uInt16 nPage, sal_uInt16 nCurrentLevel) +{ + if(!m_xExampleFrame || !m_xExampleFrame->IsInitialized()) + return; + + try + { + static const char* IndexServiceNames[] = + { + "com.sun.star.text.DocumentIndex", + "com.sun.star.text.UserIndex", + "com.sun.star.text.ContentIndex", + "com.sun.star.text.IllustrationsIndex", + "com.sun.star.text.ObjectIndex", + "com.sun.star.text.TableIndex", + "com.sun.star.text.Bibliography" + }; + + OSL_ENSURE(m_vTypeData[nTOXIndex].m_pxIndexSections && + m_vTypeData[nTOXIndex].m_pxIndexSections->xContainerSection.is(), + "Section not created"); + uno::Reference< frame::XModel > & xModel = m_xExampleFrame->GetModel(); + bool bInitialCreate = true; + if(!m_vTypeData[nTOXIndex].m_pxIndexSections->xDocumentIndex.is()) + { + bInitialCreate = true; + if(!m_vTypeData[nTOXIndex].m_pxIndexSections->xContainerSection.is()) + throw uno::RuntimeException(); + uno::Reference< text::XTextRange > xAnchor = m_vTypeData[nTOXIndex].m_pxIndexSections->xContainerSection->getAnchor(); + xAnchor = xAnchor->getStart(); + uno::Reference< text::XTextCursor > xCursor = xAnchor->getText()->createTextCursorByRange(xAnchor); + + uno::Reference< lang::XMultiServiceFactory > xFact(xModel, uno::UNO_QUERY); + + OUString sIndexTypeName(OUString::createFromAscii( IndexServiceNames[ + nTOXIndex <= TOX_AUTHORITIES ? nTOXIndex : TOX_USER] )); + m_vTypeData[nTOXIndex].m_pxIndexSections->xDocumentIndex.set(xFact->createInstance(sIndexTypeName), uno::UNO_QUERY); + uno::Reference< text::XTextContent > xContent = m_vTypeData[nTOXIndex].m_pxIndexSections->xDocumentIndex; + xCursor->getText()->insertTextContent(xCursor, xContent, false); + } + for(sal_uInt16 i = 0 ; i <= TOX_AUTHORITIES; i++) + { + uno::Reference< beans::XPropertySet > xSectPr(m_vTypeData[i].m_pxIndexSections->xContainerSection, uno::UNO_QUERY); + if(xSectPr.is()) + { + xSectPr->setPropertyValue(UNO_NAME_IS_VISIBLE, makeAny(i == nTOXIndex)); + } + } + // set properties + uno::Reference< beans::XPropertySet > xIdxProps(m_vTypeData[nTOXIndex].m_pxIndexSections->xDocumentIndex, uno::UNO_QUERY); + uno::Reference< beans::XPropertySetInfo > xInfo = xIdxProps->getPropertySetInfo(); + SwTOXDescription& rDesc = GetTOXDescription(m_eCurrentTOXType); + SwTOIOptions nIdxOptions = rDesc.GetIndexOptions(); + if(bInitialCreate || !nPage || nPage == TOX_PAGE_SELECT) + { + //title + if(rDesc.GetTitle()) + lcl_SetProp(xInfo, xIdxProps, UNO_NAME_TITLE, *rDesc.GetTitle()); + + //stylenames + SwTOXElement nContentOptions = rDesc.GetContentOptions(); + if(xInfo->hasPropertyByName(UNO_NAME_LEVEL_PARAGRAPH_STYLES)) + { + bool bOn( nContentOptions&SwTOXElement::Template ); + uno::Any aStyleNames(xIdxProps->getPropertyValue(UNO_NAME_LEVEL_PARAGRAPH_STYLES)); + uno::Reference< container::XIndexReplace > xAcc; + aStyleNames >>= xAcc; + + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + OUString sLevel; + if(bOn) + sLevel = rDesc.GetStyleNames(i); + const sal_Int32 nStyles = + comphelper::string::getTokenCount(sLevel, TOX_STYLE_DELIMITER); + uno::Sequence<OUString> aStyles(nStyles); + OUString* pArr = aStyles.getArray(); + sal_Int32 nPos {0}; + for(sal_Int32 nStyle = 0; nStyle < nStyles; ++nStyle) + pArr[nStyle] = sLevel.getToken(0, TOX_STYLE_DELIMITER, nPos); + uno::Any aAny(&aStyles, cppu::UnoType<uno::Sequence<OUString>>::get()); + xAcc->replaceByIndex(i, aAny); + } + } + lcl_SetProp(xInfo, xIdxProps, UNO_NAME_LEVEL, static_cast<sal_Int16>(rDesc.GetLevel())); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_MARKS, bool(nContentOptions & SwTOXElement::Mark )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_OUTLINE, bool(nContentOptions & SwTOXElement::OutlineLevel)); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_EMBEDDED_OBJECTS,bool(nContentOptions & SwTOXElement::Ole )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_TABLES , bool(nContentOptions & SwTOXElement::Table )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_GRAPHIC_OBJECTS, bool(nContentOptions & SwTOXElement::Graphic )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_TEXT_FRAMES, bool(nContentOptions & SwTOXElement::Frame )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_LABELS, bool(nContentOptions & SwTOXElement::Sequence )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS, bool(nContentOptions & SwTOXElement::TableLeader )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_TAB_IN_TOC, bool(nContentOptions & SwTOXElement::TableInToc )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_TOC_NEWLINE, bool(nContentOptions & SwTOXElement::Newline)); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_TOC_PARAGRAPH_OUTLINE_LEVEL, bool(nContentOptions & SwTOXElement::ParagraphOutlineLevel)); + + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_CHAPTER, rDesc.IsFromChapter()); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_IS_PROTECTED, rDesc.IsReadonly()); + + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_COMBINED_ENTRIES, bool(nIdxOptions & SwTOIOptions::SameEntry )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_P_P, bool(nIdxOptions & SwTOIOptions::FF )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_IS_CASE_SENSITIVE, bool(nIdxOptions & SwTOIOptions::CaseSensitive )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_KEY_AS_ENTRY, bool(nIdxOptions & SwTOIOptions::KeyAsEntry )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_ALPHABETICAL_SEPARATORS, bool(nIdxOptions & SwTOIOptions::AlphaDelimiter)); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_DASH, bool(nIdxOptions & SwTOIOptions::Dash )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_UPPER_CASE, bool(nIdxOptions & SwTOIOptions::InitialCaps )); + + OUString aTmpName( SwStyleNameMapper::GetSpecialExtraProgName( rDesc.GetSequenceName() ) ); + lcl_SetProp(xInfo, xIdxProps, UNO_NAME_LABEL_CATEGORY, aTmpName ); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_LABELS, !rDesc.IsCreateFromObjectNames()); + + sal_Int16 nSet = text::ChapterFormat::NAME_NUMBER; + switch (rDesc.GetCaptionDisplay()) + { + case CAPTION_COMPLETE: nSet = text::ChapterFormat::NAME_NUMBER;break; + case CAPTION_NUMBER : nSet = text::ChapterFormat::NUMBER; break; + case CAPTION_TEXT : nSet = text::ChapterFormat::NAME; break; + } + lcl_SetProp(xInfo, xIdxProps, UNO_NAME_LABEL_DISPLAY_TYPE, nSet); + + SwTOOElements nOLEOptions = rDesc.GetOLEOptions(); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_STAR_MATH, bool(SwTOOElements::Math &nOLEOptions )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_STAR_CHART, bool(SwTOOElements::Chart &nOLEOptions )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_STAR_CALC, bool(SwTOOElements::Calc &nOLEOptions )); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_STAR_DRAW, bool(SwTOOElements::DrawImpress&nOLEOptions)); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_CREATE_FROM_OTHER_EMBEDDED_OBJECTS, bool(SwTOOElements::Other & nOLEOptions)); + } + const SwForm* pForm = GetForm(m_eCurrentTOXType); + if(bInitialCreate || !nPage || nPage == TOX_PAGE_ENTRY) + { + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_IS_COMMA_SEPARATED, pForm->IsCommaSeparated()); + lcl_SetBOOLProp(xInfo, xIdxProps, UNO_NAME_USE_ALPHABETICAL_SEPARATORS, bool(nIdxOptions&SwTOIOptions::AlphaDelimiter)); + const bool bUseCurrent = nCurrentLevel < pForm->GetFormMax(); + const sal_uInt16 nStartLevel = bUseCurrent ? nCurrentLevel : 0; + const sal_uInt16 nEndLevel = bUseCurrent ? nCurrentLevel : pForm->GetFormMax() - 1; + if(xInfo->hasPropertyByName(UNO_NAME_LEVEL_FORMAT)) + { + for(sal_uInt16 nCurrLevel = nStartLevel; nCurrLevel <= nEndLevel; nCurrLevel++) + { + OUString sTokenType; + uno::Sequence< beans::PropertyValues> aSequPropVals(10); + long nTokenIndex = 0; + long nParamCount = 2; + + // #i24377# + SwFormTokens aPattern = pForm->GetPattern(nCurrLevel); + + for(const auto& aToken : aPattern) + { + if( aSequPropVals.getLength() <= nTokenIndex) + aSequPropVals.realloc(nTokenIndex + 10); + + switch(aToken.eTokenType) + { + case TOKEN_ENTRY_NO : + sTokenType = "TokenEntryNumber"; + // numbering for content index + break; + case TOKEN_ENTRY_TEXT : + case TOKEN_ENTRY : + sTokenType = "TokenEntryText"; + break; + case TOKEN_TAB_STOP : + nParamCount += 3; + sTokenType = "TokenTabStop"; + break; + case TOKEN_TEXT : + sTokenType = "TokenText"; + nParamCount += 1; + break; + case TOKEN_PAGE_NUMS : + sTokenType = "TokenPageNumber"; + break; + case TOKEN_CHAPTER_INFO : + sTokenType = "TokenChapterInfo"; + break; + case TOKEN_LINK_START : + sTokenType = "TokenHyperlinkStart"; + break; + case TOKEN_LINK_END : + sTokenType = "TokenHyperlinkEnd"; + break; + case TOKEN_AUTHORITY : + { + sTokenType = "TokenBibliographyDataField"; + } + break; + default:; //prevent warning + } + beans::PropertyValues aPropVals(nParamCount); + beans::PropertyValue* pPropValArr = aPropVals.getArray(); + pPropValArr[0].Name = "TokenType"; + pPropValArr[0].Value <<= sTokenType; + pPropValArr[1].Name = "CharacterStyleName"; + pPropValArr[1].Value <<= aToken.sCharStyleName; + if(TOKEN_TAB_STOP == aToken.eTokenType) + { + pPropValArr[2].Name = "TabStopRightAligned"; + pPropValArr[2].Value <<= SvxTabAdjust::End == aToken.eTabAlign; + pPropValArr[3].Name = "TabStopFillCharacter"; + pPropValArr[3].Value <<= OUString(aToken.cTabFillChar); + pPropValArr[4].Name = "TabStopPosition"; + SwTwips nTempPos = aToken.nTabStopPosition >= 0 ? + aToken.nTabStopPosition : 0; + nTempPos = convertTwipToMm100(nTempPos); + pPropValArr[4].Value <<= static_cast<sal_Int32>(nTempPos); + } + else if(TOKEN_TEXT == aToken.eTokenType) + { + pPropValArr[2].Name = "Text"; + pPropValArr[2].Value <<= aToken.sText; + } + beans::PropertyValues* pValues = aSequPropVals.getArray(); + pValues[nTokenIndex] = aPropVals; + nTokenIndex++; + } + aSequPropVals.realloc(nTokenIndex); + + uno::Any aFormatAccess = xIdxProps->getPropertyValue(UNO_NAME_LEVEL_FORMAT); + OSL_ENSURE(aFormatAccess.getValueType() == cppu::UnoType<container::XIndexReplace>::get(), + "wrong property type"); + + uno::Reference< container::XIndexReplace > xFormatAccess; + aFormatAccess >>= xFormatAccess; + uno::Any aLevelProp(&aSequPropVals, cppu::UnoType<uno::Sequence<beans::PropertyValues>>::get()); + xFormatAccess->replaceByIndex(nCurrLevel, aLevelProp); + } + } + } + if(bInitialCreate || !nPage || nPage == TOX_PAGE_STYLES) + { + lcl_SetProp(xInfo, xIdxProps, "ParaStyleHeading", pForm->GetTemplate(0)); + sal_uInt16 nOffset = 0; + sal_uInt16 nEndLevel = 2; + switch(m_eCurrentTOXType.eType) + { + case TOX_INDEX: + { + nOffset = 1; + nEndLevel = 4; + lcl_SetProp(xInfo, xIdxProps, "ParaStyleSeparator", pForm->GetTemplate(1)); + } + break; + case TOX_CONTENT : + nEndLevel = 11; + break; + default:; //prevent warning + } + for(sal_uInt16 i = 1; i < nEndLevel; i++) + { + lcl_SetProp(xInfo, + xIdxProps, + "ParaStyleLevel" + OUString::number( i ), + pForm->GetTemplate(i + nOffset)); + } + } + m_vTypeData[nTOXIndex].m_pxIndexSections->xDocumentIndex->update(); + + } + catch (const Exception&) + { + OSL_FAIL("::CreateExample() - exception caught"); + } + m_xExampleFrame->Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/index/cnttab.cxx b/sw/source/ui/index/cnttab.cxx new file mode 100644 index 000000000..dc24e2c3d --- /dev/null +++ b/sw/source/ui/index/cnttab.cxx @@ -0,0 +1,3932 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/log.hxx> +#include <svl/style.hxx> +#include <vcl/button.hxx> +#include <vcl/weld.hxx> +#include <svl/stritem.hxx> +#include <unotools/pathoptions.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxdlg.hxx> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <svtools/indexentryres.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <column.hxx> +#include <fmtfsize.hxx> +#include <authfld.hxx> +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <cnttab.hxx> +#include <swuicnttab.hxx> +#include <poolfmt.hxx> +#include <strings.hrc> +#include <uitool.hxx> +#include <fmtcol.hxx> +#include <fldbas.hxx> +#include <expfld.hxx> +#include <unotools.hxx> +#include <docsh.hxx> +#include <swmodule.hxx> +#include <modcfg.hxx> + +#include <cmdid.h> +#include <cnttab.hrc> +#include <SwStyleNameMapper.hxx> +#include <sfx2/filedlghelper.hxx> +#include <toxwrap.hxx> +#include <chpfld.hxx> + +#include <cmath> +#include <memory> +#include <vector> +#include <numeric> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace com::sun::star::ui::dialogs; +using namespace ::sfx2; +#include <svtools/editbrowsebox.hxx> + +static const sal_Unicode aDeliStart = '['; // for the form +static const sal_Unicode aDeliEnd = ']'; // for the form + +static OUString lcl_CreateAutoMarkFileDlg(weld::Window* pParent, const OUString& rURL, + const OUString& rFileString, bool bOpen) +{ + OUString sRet; + + FileDialogHelper aDlgHelper( bOpen ? + TemplateDescription::FILEOPEN_SIMPLE : TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, pParent); + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + xFP->appendFilter( rFileString, "*.sdi" ); + xFP->setCurrentFilter( rFileString ) ; + + if( !rURL.isEmpty() ) + xFP->setDisplayDirectory( rURL ); + else + { + SvtPathOptions aPathOpt; + xFP->setDisplayDirectory( aPathOpt.GetUserConfigPath() ); + } + + if( aDlgHelper.Execute() == ERRCODE_NONE ) + { + sRet = xFP->getSelectedFiles().getConstArray()[0]; + } + + return sRet; +} + +namespace { + +struct AutoMarkEntry +{ + OUString sSearch; + OUString sAlternative; + OUString sPrimKey; + OUString sSecKey; + OUString sComment; + bool bCase; + bool bWord; + + AutoMarkEntry() : + bCase(false), + bWord(false){} +}; + +} + +typedef ::svt::EditBrowseBox SwEntryBrowseBox_Base; + +namespace { + +class SwEntryBrowseBox : public SwEntryBrowseBox_Base +{ + VclPtr<Edit> m_aCellEdit; + VclPtr< ::svt::CheckBoxControl> m_aCellCheckBox; + + OUString m_sYes; + OUString m_sNo; + + std::vector<std::unique_ptr<AutoMarkEntry>> m_Entries; + + ::svt::CellControllerRef m_xController; + ::svt::CellControllerRef m_xCheckController; + + long m_nCurrentRow; + bool m_bModified; + +protected: + virtual bool SeekRow( long nRow ) override; + virtual void PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColId) const override; + virtual void InitController(::svt::CellControllerRef& rController, long nRow, sal_uInt16 nCol) override; + virtual ::svt::CellController* GetController(long nRow, sal_uInt16 nCol) override; + virtual bool SaveModified() override; + + std::vector<long> GetOptimalColWidths() const; + +public: + SwEntryBrowseBox(const css::uno::Reference<css::awt::XWindow> &rParent); + virtual ~SwEntryBrowseBox() override; + virtual void dispose() override; + void ReadEntries(SvStream& rInStr); + void WriteEntries(SvStream& rOutStr); + + bool IsModified()const override; + + virtual OUString GetCellText( long nRow, sal_uInt16 nColumn ) const override; + virtual void Resize() override; + virtual Size GetOptimalSize() const override; +}; + +class SwAutoMarkDlg_Impl : public weld::GenericDialogController +{ + OUString sAutoMarkURL; + bool bCreateMode; + + std::unique_ptr<weld::Button> m_xOKPB; + std::unique_ptr<weld::Container> m_xTable; + css::uno::Reference<css::awt::XWindow> m_xTableCtrlParent; + VclPtr<SwEntryBrowseBox> m_xEntriesBB; + + DECL_LINK(OkHdl, weld::Button&, void); +public: + SwAutoMarkDlg_Impl(weld::Window* pParent, const OUString& rAutoMarkURL, + bool bCreate); + virtual ~SwAutoMarkDlg_Impl() override; +}; + +} + +sal_uInt16 CurTOXType::GetFlatIndex() const +{ + return static_cast< sal_uInt16 >( (eType == TOX_USER && nIndex) + ? TOX_AUTHORITIES + nIndex : eType ); +} + +SwMultiTOXTabDialog::SwMultiTOXTabDialog(weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell &rShell, SwTOXBase* pCurTOX, + sal_uInt16 nToxType, bool bGlobal) + : SfxTabDialogController(pParent, "modules/swriter/ui/tocdialog.ui", "TocDialog", &rSet) + , m_pMgr( new SwTOXMgr( &rShell ) ) + , m_rWrtShell(rShell) + , m_pParamTOXBase(pCurTOX) + , m_sUserDefinedIndex(SwResId(STR_USER_DEFINED_INDEX)) + , m_nInitialTOXType(nToxType) + , m_bEditTOX(false) + , m_bExampleCreated(false) + , m_bGlobalFlag(bGlobal) + , m_xShowExampleCB(m_xBuilder->weld_check_button("showexample")) +{ + m_eCurrentTOXType.eType = TOX_CONTENT; + m_eCurrentTOXType.nIndex = 0; + + const sal_uInt16 nUserTypeCount = m_rWrtShell.GetTOXTypeCount(TOX_USER); + m_vTypeData.resize(nUserTypeCount + 6); + //the standard user index is on position TOX_USER + //all user indexes follow after position TOX_AUTHORITIES + if(pCurTOX) + { + m_bEditTOX = true; + } + for(int i = m_vTypeData.size() - 1; i > -1; i--) + { + m_vTypeData[i].m_pxIndexSections.reset(new SwIndexSections_Impl); + if(pCurTOX) + { + m_eCurrentTOXType.eType = pCurTOX->GetType(); + sal_uInt16 nArrayIndex = static_cast< sal_uInt16 >(m_eCurrentTOXType.eType); + if(m_eCurrentTOXType.eType == TOX_USER) + { + //which user type is it? + for(sal_uInt16 nUser = 0; nUser < nUserTypeCount; nUser++) + { + const SwTOXType* pTemp = m_rWrtShell.GetTOXType(TOX_USER, nUser); + if(pCurTOX->GetTOXType() == pTemp) + { + m_eCurrentTOXType.nIndex = nUser; + nArrayIndex = static_cast< sal_uInt16 >(nUser > 0 ? TOX_AUTHORITIES + nUser : TOX_USER); + break; + } + } + } + m_vTypeData[nArrayIndex].m_pForm.reset(new SwForm(pCurTOX->GetTOXForm())); + m_vTypeData[nArrayIndex].m_pDescription = CreateTOXDescFromTOXBase(pCurTOX); + if(TOX_AUTHORITIES == m_eCurrentTOXType.eType) + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + m_rWrtShell.GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + OUString sBrackets; + if(pFType->GetPrefix()) + sBrackets += OUStringChar(pFType->GetPrefix()); + if(pFType->GetSuffix()) + sBrackets += OUStringChar(pFType->GetSuffix()); + m_vTypeData[nArrayIndex].m_pDescription->SetAuthBrackets(sBrackets); + m_vTypeData[nArrayIndex].m_pDescription->SetAuthSequence(pFType->IsSequence()); + } + else + { + m_vTypeData[nArrayIndex].m_pDescription->SetAuthBrackets("[]"); + } + } + } + } + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + AddTabPage("index", SwTOXSelectTabPage::Create, nullptr); + AddTabPage("styles", SwTOXStylesTabPage::Create, nullptr); + AddTabPage("columns", SwColumnPage::Create, nullptr); + AddTabPage("background", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); + AddTabPage("entries", SwTOXEntryTabPage::Create, nullptr); + if (!pCurTOX) + SetCurPageId("index"); + + m_xShowExampleCB->connect_toggled(LINK(this, SwMultiTOXTabDialog, ShowPreviewHdl)); + m_xShowExampleCB->set_active(SW_MOD()->GetModuleConfig()->IsShowIndexPreview()); + + ShowPreviewHdl(*m_xShowExampleCB); +} + +SwMultiTOXTabDialog::~SwMultiTOXTabDialog() +{ + SW_MOD()->GetModuleConfig()->SetShowIndexPreview(m_xShowExampleCB->get_active()); +} + +void SwMultiTOXTabDialog::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (rId == "background") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_SELECTOR))); + rPage.PageCreated(aSet); + } + else if (rId == "columns") + { + const SwFormatFrameSize& rSize = GetInputSetImpl()->Get(RES_FRM_SIZE); + + static_cast<SwColumnPage&>(rPage).SetPageWidth(rSize.GetWidth()); + } + else if (rId == "entries") + static_cast<SwTOXEntryTabPage&>(rPage).SetWrtShell(m_rWrtShell); + else if (rId == "index") + { + static_cast<SwTOXSelectTabPage&>(rPage).SetWrtShell(m_rWrtShell); + if(USHRT_MAX != m_nInitialTOXType) + static_cast<SwTOXSelectTabPage&>(rPage).SelectType(static_cast<TOXTypes>(m_nInitialTOXType)); + } +} + +short SwMultiTOXTabDialog::Ok() +{ + short nRet = SfxTabDialogController::Ok(); + SwTOXDescription& rDesc = GetTOXDescription(m_eCurrentTOXType); + SwTOXBase aNewDef(*m_rWrtShell.GetDefaultTOXBase( m_eCurrentTOXType.eType, true )); + + const sal_uInt16 nIndex = m_eCurrentTOXType.GetFlatIndex(); + if(m_vTypeData[nIndex].m_pForm) + { + rDesc.SetForm(*m_vTypeData[nIndex].m_pForm); + aNewDef.SetTOXForm(*m_vTypeData[nIndex].m_pForm); + } + rDesc.ApplyTo(aNewDef); + if(!m_bGlobalFlag) + m_pMgr->UpdateOrInsertTOX( + rDesc, nullptr, GetOutputItemSet()); + else if(m_bEditTOX) + m_pMgr->UpdateOrInsertTOX( + rDesc, &m_pParamTOXBase, GetOutputItemSet()); + + if(!m_eCurrentTOXType.nIndex) + m_rWrtShell.SetDefaultTOXBase(aNewDef); + + return nRet; +} + +SwForm* SwMultiTOXTabDialog::GetForm(CurTOXType eType) +{ + const sal_uInt16 nIndex = eType.GetFlatIndex(); + if(!m_vTypeData[nIndex].m_pForm) + m_vTypeData[nIndex].m_pForm.reset(new SwForm(eType.eType)); + return m_vTypeData[nIndex].m_pForm.get(); +} + +SwTOXDescription& SwMultiTOXTabDialog::GetTOXDescription(CurTOXType eType) +{ + const sal_uInt16 nIndex = eType.GetFlatIndex(); + if(!m_vTypeData[nIndex].m_pDescription) + { + const SwTOXBase* pDef = m_rWrtShell.GetDefaultTOXBase( eType.eType ); + if(pDef) + m_vTypeData[nIndex].m_pDescription = CreateTOXDescFromTOXBase(pDef); + else + { + m_vTypeData[nIndex].m_pDescription.reset(new SwTOXDescription(eType.eType)); + if(eType.eType == TOX_USER) + m_vTypeData[nIndex].m_pDescription->SetTitle(m_sUserDefinedIndex); + else + m_vTypeData[nIndex].m_pDescription->SetTitle( + m_rWrtShell.GetTOXType(eType.eType, 0)->GetTypeName()); + } + if(TOX_AUTHORITIES == eType.eType) + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + m_rWrtShell.GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + m_vTypeData[nIndex].m_pDescription->SetAuthBrackets(OUStringChar(pFType->GetPrefix()) + + OUStringChar(pFType->GetSuffix())); + m_vTypeData[nIndex].m_pDescription->SetAuthSequence(pFType->IsSequence()); + } + else + { + m_vTypeData[nIndex].m_pDescription->SetAuthBrackets("[]"); + } + } + else if(TOX_INDEX == eType.eType) + m_vTypeData[nIndex].m_pDescription->SetMainEntryCharStyle(SwResId(STR_POOLCHR_IDX_MAIN_ENTRY)); + + } + return *m_vTypeData[nIndex].m_pDescription; +} + +std::unique_ptr<SwTOXDescription> SwMultiTOXTabDialog::CreateTOXDescFromTOXBase( + const SwTOXBase*pCurTOX) +{ + std::unique_ptr<SwTOXDescription> pDesc(new SwTOXDescription(pCurTOX->GetType())); + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + pDesc->SetStyleNames(pCurTOX->GetStyleNames(i), i); + pDesc->SetAutoMarkURL(m_rWrtShell.GetTOIAutoMarkURL()); + pDesc->SetTitle(pCurTOX->GetTitle()); + + pDesc->SetContentOptions(pCurTOX->GetCreateType()); + if(pDesc->GetTOXType() == TOX_INDEX) + pDesc->SetIndexOptions(pCurTOX->GetOptions()); + pDesc->SetMainEntryCharStyle(pCurTOX->GetMainEntryCharStyle()); + if(pDesc->GetTOXType() != TOX_INDEX) + pDesc->SetLevel(static_cast<sal_uInt8>(pCurTOX->GetLevel())); + pDesc->SetCreateFromObjectNames(pCurTOX->IsFromObjectNames()); + pDesc->SetSequenceName(pCurTOX->GetSequenceName()); + pDesc->SetCaptionDisplay(pCurTOX->GetCaptionDisplay()); + pDesc->SetFromChapter(pCurTOX->IsFromChapter()); + pDesc->SetReadonly(pCurTOX->IsProtected()); + pDesc->SetOLEOptions(pCurTOX->GetOLEOptions()); + pDesc->SetLevelFromChapter(pCurTOX->IsLevelFromChapter()); + pDesc->SetLanguage(pCurTOX->GetLanguage()); + pDesc->SetSortAlgorithm(pCurTOX->GetSortAlgorithm()); + return pDesc; +} + +IMPL_LINK_NOARG(SwMultiTOXTabDialog, ShowPreviewHdl, weld::ToggleButton&, void) +{ + if (m_xShowExampleCB->get_active()) + { + if(!m_xExampleFrame && !m_bExampleCreated) + { + m_bExampleCreated = true; + OUString sTemplate("internal/idxexample.odt"); + + SvtPathOptions aOpt; + bool bExist = aOpt.SearchFile( sTemplate, SvtPathOptions::PATH_TEMPLATE ); + + if(!bExist) + { + OUString sInfo(SwResId(STR_FILE_NOT_FOUND)); + sInfo = sInfo.replaceFirst( "%1", sTemplate ); + sInfo = sInfo.replaceFirst( "%2", aOpt.GetTemplatePath() ); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + sInfo)); + xInfoBox->run(); + } + else + { + Link<SwOneExampleFrame&,void> aLink(LINK(this, SwMultiTOXTabDialog, CreateExample_Hdl)); + m_xExampleFrame.reset(new SwOneExampleFrame(EX_SHOW_ONLINE_LAYOUT | EX_LOCALIZE_TOC_STRINGS, &aLink, &sTemplate)); + m_xExampleFrameWin.reset(new weld::CustomWeld(*m_xBuilder, "example", *m_xExampleFrame)); + } + m_xShowExampleCB->set_visible(m_xExampleFrame != nullptr); + } + } + + if (m_xExampleFrame) + { + const bool bSetViewWindow = m_xShowExampleCB->get_active(); + if (bSetViewWindow) + m_xExampleFrame->Show(); + else + m_xExampleFrame->Hide(); + } + + m_xDialog->resize_to_request(); +} + +bool SwMultiTOXTabDialog::IsNoNum(SwWrtShell& rSh, const OUString& rName) +{ + SwTextFormatColl* pColl = rSh.GetParaStyle(rName); + if(pColl && ! pColl->IsAssignedToListLevelOfOutlineStyle()) + return true; + + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( + rName, SwGetPoolIdFromName::TxtColl); + return nId != USHRT_MAX && + ! rSh.GetTextCollFromPool(nId)->IsAssignedToListLevelOfOutlineStyle(); +} + +namespace { + +class SwAddStylesDlg_Impl : public SfxDialogController +{ + OUString* pStyleArr; + + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::Button> m_xLeftPB; + std::unique_ptr<weld::Button> m_xRightPB; + std::unique_ptr<weld::TreeView> m_xHeaderTree; + + DECL_LINK(OkHdl, weld::Button&, void); + DECL_LINK(LeftRightHdl, weld::Button&, void); + DECL_LINK(KeyInput, const KeyEvent&, bool); + DECL_LINK(TreeSizeAllocHdl, const Size&, void); + typedef std::pair<int, int> row_col; + DECL_LINK(RadioToggleOnHdl, const row_col&, void); + +public: + SwAddStylesDlg_Impl(weld::Window* pParent, SwWrtShell const & rWrtSh, OUString rStringArr[]); +}; + +} + +SwAddStylesDlg_Impl::SwAddStylesDlg_Impl(weld::Window* pParent, + SwWrtShell const & rWrtSh, OUString rStringArr[]) + : SfxDialogController(pParent, "modules/swriter/ui/assignstylesdialog.ui", "AssignStylesDialog") + , pStyleArr(rStringArr) + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xLeftPB(m_xBuilder->weld_button("left")) + , m_xRightPB(m_xBuilder->weld_button("right")) + , m_xHeaderTree(m_xBuilder->weld_tree_view("styles")) +{ + m_xOk->connect_clicked(LINK(this, SwAddStylesDlg_Impl, OkHdl)); + m_xLeftPB->connect_clicked(LINK(this, SwAddStylesDlg_Impl, LeftRightHdl)); + m_xRightPB->connect_clicked(LINK(this, SwAddStylesDlg_Impl, LeftRightHdl)); + m_xHeaderTree->connect_size_allocate(LINK(this, SwAddStylesDlg_Impl, TreeSizeAllocHdl)); + + std::vector<int> aRadioColumns; + for (sal_uInt16 i = 0; i <= MAXLEVEL; ++i) + aRadioColumns.push_back(i + 1); + m_xHeaderTree->set_toggle_columns_as_radio(aRadioColumns); + m_xHeaderTree->connect_toggled(LINK(this, SwAddStylesDlg_Impl, RadioToggleOnHdl)); + + std::vector<int> aWidths; + aWidths.push_back(m_xHeaderTree->get_approximate_digit_width() * 30); + int nPadding = m_xHeaderTree->get_approximate_digit_width() * 2; + OUString sTitle(m_xHeaderTree->get_column_title(1)); + for (sal_uInt16 i = 0; i <= MAXLEVEL; ++i) + { + sTitle = OUString::number(i); + m_xHeaderTree->set_column_title(i + 1, sTitle); + aWidths.push_back(m_xHeaderTree->get_pixel_size(sTitle).Width() + nPadding); + } + m_xHeaderTree->set_column_fixed_widths(aWidths); + auto nWidth = std::accumulate(aWidths.begin(), aWidths.end(), 0); + m_xHeaderTree->set_size_request(nWidth, m_xHeaderTree->get_height_rows(15)); + + int nRow(0); + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + const OUString &rStyles{rStringArr[i]}; + if (rStyles.isEmpty()) + continue; + sal_Int32 nPos(0); + do + { + OUString sEntry = rStyles.getToken(0, TOX_STYLE_DELIMITER, nPos); + m_xHeaderTree->append_text(sEntry); + for (sal_uInt16 j = 0; j <= MAXLEVEL; ++j) + { + TriState eState = i == j - 1 ? TRISTATE_TRUE : TRISTATE_FALSE; + m_xHeaderTree->set_toggle(nRow, eState, j + 1); + } + ++nRow; + } while (nPos>=0); + } + // now the other styles + + const sal_uInt16 nSz = rWrtSh.GetTextFormatCollCount(); + for (sal_uInt16 j = 0; j < nSz; ++j) + { + const SwTextFormatColl& rColl = rWrtSh.GetTextFormatColl(j); + if (rColl.IsDefault()) + continue; + + const OUString aName = rColl.GetName(); + if (!aName.isEmpty()) + { + bool bEntry = false; + int nChildren = m_xHeaderTree->n_children(); + for (int i = 0; i < nChildren; ++i) + { + if (m_xHeaderTree->get_text(i, 0) == aName) + { + bEntry = true; + break; + } + } + if (!bEntry) + { + m_xHeaderTree->append_text(aName); + for (sal_uInt16 k = 0; k <= MAXLEVEL; ++k) + { + TriState eState = k == 0 ? TRISTATE_TRUE : TRISTATE_FALSE; + m_xHeaderTree->set_toggle(nRow, eState, k + 1); + } + ++nRow; + } + } + } + m_xHeaderTree->make_sorted(); + m_xHeaderTree->select(0); + m_xHeaderTree->connect_key_release(LINK(this, SwAddStylesDlg_Impl, KeyInput)); +} + +IMPL_LINK(SwAddStylesDlg_Impl, TreeSizeAllocHdl, const Size&, rSize, void) +{ + auto nWidth = rSize.Width(); + + std::vector<int> aWidths; + aWidths.push_back(0); + int nPadding = m_xHeaderTree->get_approximate_digit_width() * 2; + for (sal_uInt16 i = 0; i <= MAXLEVEL; ++i) + { + OUString sTitle(m_xHeaderTree->get_column_title(i + 1)); + aWidths.push_back(m_xHeaderTree->get_pixel_size(sTitle).Width() + nPadding); + } + auto nOtherWidth = std::accumulate(aWidths.begin(), aWidths.end(), 0); + aWidths[0] = nWidth - nOtherWidth; + m_xHeaderTree->set_column_fixed_widths(aWidths); +} + +IMPL_LINK(SwAddStylesDlg_Impl, RadioToggleOnHdl, const row_col&, rRowCol, void) +{ + for (sal_uInt16 i = 0; i <= MAXLEVEL; ++i) + { + TriState eState = rRowCol.second == i + 1 ? TRISTATE_TRUE : TRISTATE_FALSE; + m_xHeaderTree->set_toggle(rRowCol.first, eState, i + 1); + } +} + +IMPL_LINK(SwAddStylesDlg_Impl, KeyInput, const KeyEvent&, rKEvt, bool) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + bool bHandled = false; + + if (aCode.GetCode() == KEY_ADD || aCode.GetCode() == KEY_RIGHT) + { + LeftRightHdl(*m_xRightPB); + bHandled = true; + } + else if (aCode.GetCode() == KEY_SUBTRACT || aCode.GetCode() == KEY_LEFT) + { + LeftRightHdl(*m_xLeftPB); + bHandled = true; + } + + return bHandled; +} + +IMPL_LINK_NOARG(SwAddStylesDlg_Impl, OkHdl, weld::Button&, void) +{ + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + pStyleArr[i].clear(); + + int nChildren = m_xHeaderTree->n_children(); + for (int i = 0; i < nChildren; ++i) + { + int nToggleColumn = 0; + for (sal_uInt16 j = 0; j <= MAXLEVEL; ++j) + { + if (m_xHeaderTree->get_toggle(i, j + 1) == TRISTATE_TRUE) + { + nToggleColumn = j; + break; + } + } + if (nToggleColumn) + { + int nLevel = nToggleColumn - 1; + if(!pStyleArr[nLevel].isEmpty()) + pStyleArr[nLevel] += OUStringChar(TOX_STYLE_DELIMITER); + pStyleArr[nLevel] += m_xHeaderTree->get_text(i, 0); + } + } + + //TODO write back style names + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwAddStylesDlg_Impl, LeftRightHdl, weld::Button&, rBtn, void) +{ + bool bLeft = &rBtn == m_xLeftPB.get(); + int nEntry = m_xHeaderTree->get_selected_index(); + if (nEntry != -1) + { + int nToggleColumn = 0; + for (sal_uInt16 j = 0; j <= MAXLEVEL; ++j) + { + if (m_xHeaderTree->get_toggle(nEntry, j + 1) == TRISTATE_TRUE) + { + nToggleColumn = j; + break; + } + } + + if (bLeft) + { + if (nToggleColumn) + --nToggleColumn; + } + else + { + if (nToggleColumn < MAXLEVEL) + ++nToggleColumn; + } + + for (sal_uInt16 j = 0; j <= MAXLEVEL; ++j) + { + m_xHeaderTree->set_toggle(nEntry, j == nToggleColumn ? TRISTATE_TRUE : TRISTATE_FALSE, j + 1); + } + } +} + +SwTOXSelectTabPage::SwTOXSelectTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/tocindexpage.ui", "TocIndexPage", &rAttrSet) + , sAutoMarkType(SwResId(STR_AUTOMARK_TYPE)) + , m_bWaitingInitialSettings(true) + , m_xTitleED(m_xBuilder->weld_entry("title")) + , m_xTypeFT(m_xBuilder->weld_label("typeft")) + , m_xTypeLB(m_xBuilder->weld_combo_box("type")) + , m_xReadOnlyCB(m_xBuilder->weld_check_button("readonly")) + , m_xAreaFrame(m_xBuilder->weld_widget("areaframe")) + , m_xAreaLB(m_xBuilder->weld_combo_box("scope")) + , m_xLevelFT(m_xBuilder->weld_label("levelft")) + , m_xLevelNF(m_xBuilder->weld_spin_button("level")) + , m_xCreateFrame(m_xBuilder->weld_widget("createframe")) + , m_xFromHeadingsCB(m_xBuilder->weld_check_button("fromheadings")) + , m_xStylesCB(m_xBuilder->weld_check_button("stylescb")) + , m_xAddStylesCB(m_xBuilder->weld_check_button("addstylescb")) + , m_xAddStylesPB(m_xBuilder->weld_button("styles")) + , m_xFromTablesCB(m_xBuilder->weld_check_button("fromtables")) + , m_xFromFramesCB(m_xBuilder->weld_check_button("fromframes")) + , m_xFromGraphicsCB(m_xBuilder->weld_check_button("fromgraphics")) + , m_xFromOLECB(m_xBuilder->weld_check_button("fromoles")) + , m_xLevelFromChapterCB(m_xBuilder->weld_check_button("uselevel")) + , m_xFromCaptionsRB(m_xBuilder->weld_radio_button("captions")) + , m_xFromObjectNamesRB(m_xBuilder->weld_radio_button("objnames")) + , m_xCaptionSequenceFT(m_xBuilder->weld_label("categoryft")) + , m_xCaptionSequenceLB(m_xBuilder->weld_combo_box("category")) + , m_xDisplayTypeFT(m_xBuilder->weld_label("displayft")) + , m_xDisplayTypeLB(m_xBuilder->weld_combo_box("display")) + , m_xTOXMarksCB(m_xBuilder->weld_check_button("indexmarks")) + , m_xIdxOptionsFrame(m_xBuilder->weld_widget("optionsframe")) + , m_xCollectSameCB(m_xBuilder->weld_check_button("combinesame")) + , m_xUseFFCB(m_xBuilder->weld_check_button("useff")) + , m_xUseDashCB(m_xBuilder->weld_check_button("usedash")) + , m_xCaseSensitiveCB(m_xBuilder->weld_check_button("casesens")) + , m_xInitialCapsCB(m_xBuilder->weld_check_button("initcaps")) + , m_xKeyAsEntryCB(m_xBuilder->weld_check_button("keyasentry")) + , m_xFromFileCB(m_xBuilder->weld_check_button("fromfile")) + , m_xAutoMarkPB(m_xBuilder->weld_menu_button("file")) + , m_xFromObjCLB(m_xBuilder->weld_tree_view("objects")) + , m_xFromObjFrame(m_xBuilder->weld_widget("objectframe")) + , m_xSequenceCB(m_xBuilder->weld_check_button("numberentries")) + , m_xBracketLB(m_xBuilder->weld_combo_box("brackets")) + , m_xAuthorityFrame(m_xBuilder->weld_widget("authframe")) + , m_xSortFrame(m_xBuilder->weld_widget("sortframe")) + , m_xLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box("lang"))) + , m_xSortAlgorithmLB(m_xBuilder->weld_combo_box("keytype")) +{ + sAddStyleUser = m_xStylesCB->get_label(); + pIndexEntryWrapper.reset(new IndexEntrySupplierWrapper()); + + m_xLanguageLB->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, + false ); + + //Default mode is arranged to be the tallest mode + //of alphabetical index, lock that size in now + LanguageHdl(nullptr); //fill sort algorithm list + Size aPrefSize(m_xContainer->get_preferred_size()); + m_xContainer->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + + sAddStyleContent = m_xAddStylesCB->get_label(); + + std::vector<int> aWidths; + aWidths.push_back(m_xFromObjCLB->get_checkbox_column_width()); + m_xFromObjCLB->set_column_fixed_widths(aWidths); + + for (size_t i = 0; i < SAL_N_ELEMENTS(RES_SRCTYPES); ++i) + { + OUString sId(OUString::number(static_cast<sal_uInt32>(RES_SRCTYPES[i].second))); + m_xFromObjCLB->append(); + m_xFromObjCLB->set_toggle(i, TRISTATE_FALSE, 0); + m_xFromObjCLB->set_text(i, SwResId(RES_SRCTYPES[i].first), 1); + m_xFromObjCLB->set_id(i, sId); + } + + SetExchangeSupport(); + m_xTypeLB->connect_changed(LINK(this, SwTOXSelectTabPage, TOXTypeHdl)); + + m_xAddStylesPB->connect_clicked(LINK(this, SwTOXSelectTabPage, AddStylesHdl)); + + m_xAutoMarkPB->connect_toggled(LINK(this, SwTOXSelectTabPage, MenuEnableHdl)); + m_xAutoMarkPB->connect_selected(LINK(this, SwTOXSelectTabPage, MenuExecuteHdl)); + + Link<weld::ToggleButton&,void> aLk = LINK(this, SwTOXSelectTabPage, CheckBoxHdl); + m_xAddStylesCB->connect_toggled(aLk); + m_xFromHeadingsCB->connect_toggled(aLk); + m_xTOXMarksCB->connect_toggled(aLk); + m_xFromFileCB->connect_toggled(aLk); + m_xCollectSameCB->connect_toggled(aLk); + m_xUseFFCB->connect_toggled(aLk); + m_xUseDashCB->connect_toggled(aLk); + m_xInitialCapsCB->connect_toggled(aLk); + m_xKeyAsEntryCB->connect_toggled(aLk); + + m_xTitleED->connect_changed(LINK(this, SwTOXSelectTabPage, ModifyEntryHdl)); + m_xLevelNF->connect_value_changed(LINK(this, SwTOXSelectTabPage, ModifySpinHdl)); + m_xSortAlgorithmLB->connect_changed(LINK(this, SwTOXSelectTabPage, ModifyListBoxHdl)); + + aLk = LINK(this, SwTOXSelectTabPage, RadioButtonHdl); + m_xFromCaptionsRB->connect_toggled(aLk); + m_xFromObjectNamesRB->connect_toggled(aLk); + RadioButtonHdl(*m_xFromCaptionsRB); + + m_xLanguageLB->connect_changed(LINK(this, SwTOXSelectTabPage, LanguageListBoxHdl)); + m_xTypeLB->set_active(0); + m_xTitleED->save_value(); +} + +SwTOXSelectTabPage::~SwTOXSelectTabPage() +{ + pIndexRes.reset(); + pIndexEntryWrapper.reset(); + m_xLanguageLB.reset(); +} + +void SwTOXSelectTabPage::SetWrtShell(SwWrtShell const & rSh) +{ + const sal_uInt16 nUserTypeCount = rSh.GetTOXTypeCount(TOX_USER); + if(nUserTypeCount > 1) + { + //insert all new user indexes names after the standard user index + sal_Int32 nPos = m_xTypeLB->find_id(OUString::number(sal_uInt32(TO_USER))) + 1; + for (sal_uInt16 nUser = 1; nUser < nUserTypeCount; nUser++) + { + sal_uInt32 nEntryData = nUser << 8; + nEntryData |= TO_USER; + OUString sId(OUString::number(nEntryData)); + m_xTypeLB->insert(nPos++, rSh.GetTOXType(TOX_USER, nUser)->GetTypeName(), + &sId, nullptr, nullptr); + } + } +} + +bool SwTOXSelectTabPage::FillItemSet( SfxItemSet* ) +{ + return true; +} + +static long lcl_TOXTypesToUserData(CurTOXType eType) +{ + sal_uInt16 nRet = TOX_INDEX; + switch(eType.eType) + { + case TOX_INDEX : nRet = TO_INDEX; break; + case TOX_USER : + { + nRet = eType.nIndex << 8; + nRet |= TO_USER; + } + break; + case TOX_CONTENT : nRet = TO_CONTENT; break; + case TOX_ILLUSTRATIONS:nRet = TO_ILLUSTRATION; break; + case TOX_OBJECTS : nRet = TO_OBJECT; break; + case TOX_TABLES : nRet = TO_TABLE; break; + case TOX_AUTHORITIES : nRet = TO_AUTHORITIES; break; + case TOX_BIBLIOGRAPHY : nRet = TO_BIBLIOGRAPHY; break; + case TOX_CITATION :break; + } + return nRet; +} + +void SwTOXSelectTabPage::SelectType(TOXTypes eSet) +{ + CurTOXType eCurType (eSet); + + sal_uInt32 nData = lcl_TOXTypesToUserData(eCurType); + m_xTypeLB->set_active_id(OUString::number(nData)); + m_xTypeFT->set_sensitive(false); + m_xTypeLB->set_sensitive(false); + TOXTypeHdl(*m_xTypeLB); +} + +static CurTOXType lcl_UserData2TOXTypes(sal_uInt16 nData) +{ + CurTOXType eRet; + + switch(nData&0xff) + { + case TO_INDEX : eRet.eType = TOX_INDEX; break; + case TO_USER : + { + eRet.eType = TOX_USER; + eRet.nIndex = (nData&0xff00) >> 8; + } + break; + case TO_CONTENT : eRet.eType = TOX_CONTENT; break; + case TO_ILLUSTRATION: eRet.eType = TOX_ILLUSTRATIONS; break; + case TO_OBJECT : eRet.eType = TOX_OBJECTS; break; + case TO_TABLE : eRet.eType = TOX_TABLES; break; + case TO_AUTHORITIES : eRet.eType = TOX_AUTHORITIES; break; + case TO_BIBLIOGRAPHY : eRet.eType = TOX_BIBLIOGRAPHY; break; + default: OSL_FAIL("what a type?"); + } + return eRet; +} + +void SwTOXSelectTabPage::ApplyTOXDescription() +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + const CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + SwTOXDescription& rDesc = pTOXDlg->GetTOXDescription(aCurType); + m_xReadOnlyCB->set_active(rDesc.IsReadonly()); + if (!m_xTitleED->get_value_changed_from_saved()) + { + if (rDesc.GetTitle()) + m_xTitleED->set_text(*rDesc.GetTitle()); + else + m_xTitleED->set_text(OUString()); + m_xTitleED->save_value(); + } + + m_xAreaLB->set_active(rDesc.IsFromChapter() ? 1 : 0); + + if (aCurType.eType != TOX_INDEX) + m_xLevelNF->set_value(rDesc.GetLevel()); //content, user + + SwTOXElement nCreateType = rDesc.GetContentOptions(); + + //user + content + bool bHasStyleNames = false; + + for( sal_uInt16 i = 0; i < MAXLEVEL; i++) + if(!rDesc.GetStyleNames(i).isEmpty()) + { + bHasStyleNames = true; + break; + } + m_xAddStylesCB->set_active(bHasStyleNames && (nCreateType & SwTOXElement::Template)); + + m_xFromOLECB->set_active( bool(nCreateType & SwTOXElement::Ole) ); + m_xFromTablesCB->set_active( bool(nCreateType & SwTOXElement::Table) ); + m_xFromGraphicsCB->set_active( bool(nCreateType & SwTOXElement::Graphic) ); + m_xFromFramesCB->set_active( bool(nCreateType & SwTOXElement::Frame) ); + + m_xLevelFromChapterCB->set_active(rDesc.IsLevelFromChapter()); + + //all but illustration and table + m_xTOXMarksCB->set_active( bool(nCreateType & SwTOXElement::Mark) ); + + //content + if(TOX_CONTENT == aCurType.eType) + { + m_xFromHeadingsCB->set_active( bool(nCreateType & SwTOXElement::OutlineLevel) ); + m_xAddStylesCB->set_label(sAddStyleContent); + m_xAddStylesPB->set_sensitive(m_xAddStylesCB->get_active()); + } + //index only + else if(TOX_INDEX == aCurType.eType) + { + const SwTOIOptions nIndexOptions = rDesc.GetIndexOptions(); + m_xCollectSameCB->set_active( bool(nIndexOptions & SwTOIOptions::SameEntry) ); + m_xUseFFCB->set_active( bool(nIndexOptions & SwTOIOptions::FF) ); + m_xUseDashCB->set_active( bool(nIndexOptions & SwTOIOptions::Dash) ); + if (m_xUseFFCB->get_active()) + m_xUseDashCB->set_sensitive(false); + else if (m_xUseDashCB->get_active()) + m_xUseFFCB->set_sensitive(false); + + m_xCaseSensitiveCB->set_active( bool(nIndexOptions & SwTOIOptions::CaseSensitive) ); + m_xInitialCapsCB->set_active( bool(nIndexOptions & SwTOIOptions::InitialCaps) ); + m_xKeyAsEntryCB->set_active( bool(nIndexOptions & SwTOIOptions::KeyAsEntry) ); + } + else if (TOX_ILLUSTRATIONS == aCurType.eType || TOX_TABLES == aCurType.eType) + { + m_xFromObjectNamesRB->set_active(rDesc.IsCreateFromObjectNames()); + m_xFromCaptionsRB->set_active(!rDesc.IsCreateFromObjectNames()); + OUString sName(rDesc.GetSequenceName()); + int nIndex = m_xCaptionSequenceLB->find_text(sName); + if (nIndex != -1) + m_xCaptionSequenceLB->set_active(nIndex); + m_xDisplayTypeLB->set_active(static_cast<sal_Int32>(rDesc.GetCaptionDisplay())); + if (m_xDisplayTypeLB->get_active() == -1) + m_xDisplayTypeLB->set_active(0); + RadioButtonHdl(*m_xFromCaptionsRB); + + } + else if(TOX_OBJECTS == aCurType.eType) + { + SwTOOElements nOLEData = rDesc.GetOLEOptions(); + for (int nFromObj = 0, nCount = m_xFromObjCLB->n_children(); nFromObj < nCount; ++nFromObj) + { + SwTOOElements nData = static_cast<SwTOOElements>(m_xFromObjCLB->get_id(nFromObj).toInt32()); + m_xFromObjCLB->set_toggle(nFromObj, bool(nData & nOLEData) ? TRISTATE_TRUE : TRISTATE_FALSE, 0); + } + } + else if(TOX_AUTHORITIES == aCurType.eType) + { + const OUString& sBrackets(rDesc.GetAuthBrackets()); + if(sBrackets.isEmpty() || sBrackets == " ") + m_xBracketLB->set_active(0); + else + m_xBracketLB->set_active_text(sBrackets); + m_xSequenceCB->set_active(rDesc.IsAuthSequence()); + } + m_xAutoMarkPB->set_sensitive(m_xFromFileCB->get_active()); + + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + aStyleArr[i] = rDesc.GetStyleNames(i); + + m_xLanguageLB->set_active_id(rDesc.GetLanguage()); + LanguageHdl(nullptr); + for (int nCnt = 0, nEntryCount = m_xSortAlgorithmLB->get_count(); nCnt < nEntryCount; ++nCnt) + { + const OUString& rEntryData = m_xSortAlgorithmLB->get_id(nCnt); + if (rEntryData == rDesc.GetSortAlgorithm()) + { + m_xSortAlgorithmLB->set_active(nCnt); + break; + } + } +} + +void SwTOXSelectTabPage::FillTOXDescription() +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + SwTOXDescription& rDesc = pTOXDlg->GetTOXDescription(aCurType); + rDesc.SetTitle(m_xTitleED->get_text()); + rDesc.SetFromChapter(1 == m_xAreaLB->get_active()); + SwTOXElement nContentOptions = SwTOXElement::NONE; + if (m_xTOXMarksCB->get_visible() && m_xTOXMarksCB->get_active()) + nContentOptions |= SwTOXElement::Mark; + + SwTOIOptions nIndexOptions = rDesc.GetIndexOptions()&SwTOIOptions::AlphaDelimiter; + switch(rDesc.GetTOXType()) + { + case TOX_CONTENT: + if(m_xFromHeadingsCB->get_active()) + nContentOptions |= SwTOXElement::OutlineLevel; + break; + case TOX_USER: + { + rDesc.SetTOUName(m_xTypeLB->get_active_text()); + + if(m_xFromOLECB->get_active()) + nContentOptions |= SwTOXElement::Ole; + if(m_xFromTablesCB->get_active()) + nContentOptions |= SwTOXElement::Table; + if(m_xFromFramesCB->get_active()) + nContentOptions |= SwTOXElement::Frame; + if(m_xFromGraphicsCB->get_active()) + nContentOptions |= SwTOXElement::Graphic; + } + break; + case TOX_INDEX: + { + nContentOptions = SwTOXElement::Mark; + + if(m_xCollectSameCB->get_active()) + nIndexOptions |= SwTOIOptions::SameEntry; + if(m_xUseFFCB->get_active()) + nIndexOptions |= SwTOIOptions::FF; + if(m_xUseDashCB->get_active()) + nIndexOptions |= SwTOIOptions::Dash; + if(m_xCaseSensitiveCB->get_active()) + nIndexOptions |= SwTOIOptions::CaseSensitive; + if(m_xInitialCapsCB->get_active()) + nIndexOptions |= SwTOIOptions::InitialCaps; + if(m_xKeyAsEntryCB->get_active()) + nIndexOptions |= SwTOIOptions::KeyAsEntry; + if(m_xFromFileCB->get_active()) + rDesc.SetAutoMarkURL(sAutoMarkURL); + else + rDesc.SetAutoMarkURL(OUString()); + } + break; + case TOX_ILLUSTRATIONS: + case TOX_TABLES : + rDesc.SetCreateFromObjectNames(m_xFromObjectNamesRB->get_active()); + rDesc.SetSequenceName(m_xCaptionSequenceLB->get_active_text()); + rDesc.SetCaptionDisplay(static_cast<SwCaptionDisplay>(m_xDisplayTypeLB->get_active())); + break; + case TOX_OBJECTS: + { + SwTOOElements nOLEData = SwTOOElements::NONE; + for (int i = 0, nCount = m_xFromObjCLB->n_children(); i < nCount; ++i) + { + if (m_xFromObjCLB->get_toggle(i, 0) == TRISTATE_TRUE) + { + SwTOOElements nData = static_cast<SwTOOElements>(m_xFromObjCLB->get_id(i).toInt32()); + nOLEData |= nData; + } + } + rDesc.SetOLEOptions(nOLEData); + } + break; + case TOX_AUTHORITIES: + case TOX_BIBLIOGRAPHY : + { + if (m_xBracketLB->get_active()) + rDesc.SetAuthBrackets(m_xBracketLB->get_active_text()); + else + rDesc.SetAuthBrackets(OUString()); + rDesc.SetAuthSequence(m_xSequenceCB->get_active()); + } + break; + case TOX_CITATION : + break; + } + + rDesc.SetLevelFromChapter( m_xLevelFromChapterCB->get_visible() && + m_xLevelFromChapterCB->get_active()); + if (m_xTOXMarksCB->get_active() && m_xTOXMarksCB->get_visible()) + nContentOptions |= SwTOXElement::Mark; + if (m_xFromHeadingsCB->get_active() && m_xFromHeadingsCB->get_visible()) + nContentOptions |= SwTOXElement::OutlineLevel; + if (m_xAddStylesCB->get_active() && m_xAddStylesCB->get_visible()) + nContentOptions |= SwTOXElement::Template; + + rDesc.SetContentOptions(nContentOptions); + rDesc.SetIndexOptions(nIndexOptions); + rDesc.SetLevel(m_xLevelNF->get_value()); + + rDesc.SetReadonly(m_xReadOnlyCB->get_active()); + + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + rDesc.SetStyleNames(aStyleArr[i], i); + + rDesc.SetLanguage(m_xLanguageLB->get_active_id()); + const OUString& rEntryData = m_xSortAlgorithmLB->get_active_id(); + rDesc.SetSortAlgorithm(rEntryData); +} + +void SwTOXSelectTabPage::Reset( const SfxItemSet* ) +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + SwWrtShell& rSh = pTOXDlg->GetWrtShell(); + const CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + sal_uInt32 nData = lcl_TOXTypesToUserData(aCurType); + m_xTypeLB->set_active_id(OUString::number(nData)); + + sAutoMarkURL = INetURLObject::decode( rSh.GetTOIAutoMarkURL(), + INetURLObject::DecodeMechanism::Unambiguous ); + m_xFromFileCB->set_active(!sAutoMarkURL.isEmpty()); + + m_xCaptionSequenceLB->clear(); + const size_t nCount = rSh.GetFieldTypeCount(SwFieldIds::SetExp); + for (size_t i = 0; i < nCount; ++i) + { + SwFieldType *pType = rSh.GetFieldType( i, SwFieldIds::SetExp ); + if( pType->Which() == SwFieldIds::SetExp && + static_cast<SwSetExpFieldType *>( pType)->GetType() & nsSwGetSetExpType::GSE_SEQ ) + m_xCaptionSequenceLB->append_text(pType->GetName()); + } + + if(pTOXDlg->IsTOXEditMode()) + { + m_xTypeFT->set_sensitive(false); + m_xTypeLB->set_sensitive(false); + } + + if(!m_bWaitingInitialSettings) + { + // save current values into the proper TOXDescription + FillTOXDescription(); + } + m_bWaitingInitialSettings = false; + + TOXTypeHdl(*m_xTypeLB); + CheckBoxHdl(*m_xAddStylesCB); +} + +void SwTOXSelectTabPage::ActivatePage( const SfxItemSet& ) +{ + //nothing to do +} + +DeactivateRC SwTOXSelectTabPage::DeactivatePage(SfxItemSet* _pSet) +{ + if (_pSet) + _pSet->Put(SfxUInt16Item(FN_PARAM_TOX_TYPE, m_xTypeLB->get_active_id().toUInt32())); + FillTOXDescription(); + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SwTOXSelectTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwTOXSelectTabPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK(SwTOXSelectTabPage, TOXTypeHdl, weld::ComboBox&, rBox, void) +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + const sal_uInt16 nType = rBox.get_active_id().toUInt32(); + CurTOXType eCurType = lcl_UserData2TOXTypes(nType); + pTOXDlg->SetCurrentTOXType(eCurType); + + m_xAreaLB->set_visible( 0 != (nType & (TO_CONTENT|TO_ILLUSTRATION|TO_USER|TO_INDEX|TO_TABLE|TO_OBJECT)) ); + m_xLevelFT->set_visible( 0 != (nType & (TO_CONTENT)) ); + m_xLevelNF->set_visible( 0 != (nType & (TO_CONTENT)) ); + m_xLevelFromChapterCB->set_visible( 0 != (nType & (TO_USER)) ); + m_xAreaFrame->set_visible( 0 != (nType & (TO_CONTENT|TO_ILLUSTRATION|TO_USER|TO_INDEX|TO_TABLE|TO_OBJECT)) ); + + m_xFromHeadingsCB->set_visible( 0 != (nType & (TO_CONTENT)) ); + m_xAddStylesCB->set_visible( 0 != (nType & (TO_CONTENT|TO_USER)) ); + m_xAddStylesPB->set_visible( 0 != (nType & (TO_CONTENT|TO_USER)) ); + + m_xFromTablesCB->set_visible( 0 != (nType & (TO_USER)) ); + m_xFromFramesCB->set_visible( 0 != (nType & (TO_USER)) ); + m_xFromGraphicsCB->set_visible( 0 != (nType & (TO_USER)) ); + m_xFromOLECB->set_visible( 0 != (nType & (TO_USER)) ); + + m_xFromCaptionsRB->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + m_xFromObjectNamesRB->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + + m_xTOXMarksCB->set_visible( 0 != (nType & (TO_CONTENT|TO_USER)) ); + + m_xCreateFrame->set_visible( 0 != (nType & (TO_CONTENT|TO_ILLUSTRATION|TO_USER|TO_TABLE)) ); + m_xCaptionSequenceFT->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + m_xCaptionSequenceLB->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + m_xDisplayTypeFT->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + m_xDisplayTypeLB->set_visible( 0 != (nType & (TO_ILLUSTRATION|TO_TABLE)) ); + + m_xAuthorityFrame->set_visible( 0 != (nType & TO_AUTHORITIES) ); + + bool bEnableSortLanguage = 0 != (nType & (TO_INDEX|TO_AUTHORITIES)); + m_xSortFrame->set_visible(bEnableSortLanguage); + + if( nType & TO_ILLUSTRATION ) + { + OUString sName(SwStyleNameMapper::GetUIName(RES_POOLCOLL_LABEL_FIGURE, OUString())); + m_xCaptionSequenceLB->set_active_text(sName); + } + else if( nType & TO_TABLE ) + { + OUString sName(SwStyleNameMapper::GetUIName(RES_POOLCOLL_LABEL_TABLE, OUString())); + m_xCaptionSequenceLB->set_active_text(sName); + } + else if( nType & TO_USER ) + { + m_xAddStylesCB->set_label(sAddStyleUser); + } + + m_xIdxOptionsFrame->set_visible( 0 != (nType & TO_INDEX) ); + + //object index + m_xFromObjFrame->set_visible( 0 != (nType & TO_OBJECT) ); + + //set control values from the proper TOXDescription + { + ApplyTOXDescription(); + } + ModifyHdl(); +} + +void SwTOXSelectTabPage::ModifyHdl() +{ + if(!m_bWaitingInitialSettings) + { + FillTOXDescription(); + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + pTOXDlg->CreateOrUpdateExample(pTOXDlg->GetCurrentTOXType().eType, TOX_PAGE_SELECT); + } +} + +IMPL_LINK_NOARG(SwTOXSelectTabPage, ModifyListBoxHdl, weld::ComboBox&, void) +{ + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwTOXSelectTabPage, ModifyEntryHdl, weld::Entry&, void) +{ + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwTOXSelectTabPage, ModifySpinHdl, weld::SpinButton&, void) +{ + ModifyHdl(); +} + +IMPL_LINK(SwTOXSelectTabPage, CheckBoxHdl, weld::ToggleButton&, rButton, void) +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + const CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + if(TOX_CONTENT == aCurType.eType) + { + //at least one of the three CheckBoxes must be checked + if (!m_xAddStylesCB->get_active() && !m_xFromHeadingsCB->get_active() && !m_xTOXMarksCB->get_active()) + { + //TODO: InfoBox? + rButton.set_active(true); + } + m_xAddStylesPB->set_sensitive(m_xAddStylesCB->get_active()); + } + if (TOX_USER == aCurType.eType) + { + m_xAddStylesPB->set_sensitive(m_xAddStylesCB->get_active()); + } + else if (TOX_INDEX == aCurType.eType) + { + m_xAutoMarkPB->set_sensitive(m_xFromFileCB->get_active()); + m_xUseFFCB->set_sensitive(m_xCollectSameCB->get_active() && !m_xUseDashCB->get_active()); + m_xUseDashCB->set_sensitive(m_xCollectSameCB->get_active() && !m_xUseFFCB->get_active()); + m_xCaseSensitiveCB->set_sensitive(m_xCollectSameCB->get_active()); + } + ModifyHdl(); +}; + +IMPL_LINK_NOARG(SwTOXSelectTabPage, RadioButtonHdl, weld::ToggleButton&, void) +{ + bool bEnable = m_xFromCaptionsRB->get_active(); + m_xCaptionSequenceFT->set_sensitive(bEnable); + m_xCaptionSequenceLB->set_sensitive(bEnable); + m_xDisplayTypeFT->set_sensitive(bEnable); + m_xDisplayTypeLB->set_sensitive(bEnable); + ModifyHdl(); +} + +IMPL_LINK(SwTOXSelectTabPage, LanguageListBoxHdl, weld::ComboBox&, rBox, void) +{ + LanguageHdl(&rBox); +} + +void SwTOXSelectTabPage::LanguageHdl(const weld::ComboBox* pBox) +{ + lang::Locale aLcl( LanguageTag( m_xLanguageLB->get_active_id() ).getLocale() ); + Sequence< OUString > aSeq = pIndexEntryWrapper->GetAlgorithmList( aLcl ); + + if( !pIndexRes ) + pIndexRes.reset(new IndexEntryResource()); + + OUString sOldString = m_xSortAlgorithmLB->get_active_id(); + m_xSortAlgorithmLB->clear(); + + sal_Int32 nEnd = aSeq.getLength(); + for( sal_Int32 nCnt = 0; nCnt < nEnd; ++nCnt ) + { + const OUString sAlg(aSeq[ nCnt ]); + const OUString sUINm = pIndexRes->GetTranslation( sAlg ); + m_xSortAlgorithmLB->append(sAlg, sUINm); + if( sAlg == sOldString ) + m_xSortAlgorithmLB->set_active(nCnt); + } + + if (m_xSortAlgorithmLB->get_active() == -1) + m_xSortAlgorithmLB->set_active(0); + + if (pBox) + ModifyHdl(); +}; + +IMPL_LINK_NOARG(SwTOXSelectTabPage, AddStylesHdl, weld::Button&, void) +{ + SwAddStylesDlg_Impl aDlg(GetFrameWeld(), static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(), + aStyleArr); + aDlg.run(); + ModifyHdl(); +} + +IMPL_LINK_NOARG(SwTOXSelectTabPage, MenuEnableHdl, weld::ToggleButton&, void) +{ + m_xAutoMarkPB->set_item_sensitive("edit", !sAutoMarkURL.isEmpty()); +} + +IMPL_LINK(SwTOXSelectTabPage, MenuExecuteHdl, const OString&, rIdent, void) +{ + const OUString sSaveAutoMarkURL = sAutoMarkURL; + + if (rIdent == "open") + { + sAutoMarkURL = lcl_CreateAutoMarkFileDlg(GetFrameWeld(), + sAutoMarkURL, sAutoMarkType, true); + } + else if (rIdent == "new" || rIdent == "edit") + { + bool bNew = (rIdent == "new"); + if (bNew) + { + sAutoMarkURL = lcl_CreateAutoMarkFileDlg(GetFrameWeld(), + sAutoMarkURL, sAutoMarkType, false); + if (sAutoMarkURL.isEmpty()) + return; + } + + SwAutoMarkDlg_Impl aAutoMarkDlg(GetFrameWeld(), sAutoMarkURL, bNew); + if (RET_OK != aAutoMarkDlg.run() && bNew) + sAutoMarkURL = sSaveAutoMarkURL; + } +} + +class SwTOXWidget +{ +protected: + Link<SwTOXWidget&,void> aGetFocusLink; +public: + virtual WindowType GetType() const = 0; + virtual void GrabFocus() = 0; + virtual void Hide() = 0; + virtual void set_grid_left_attach(int nPos) = 0; + virtual void get_extents_relative_to(weld::Widget& rRelative, int& x, int& y, int& width, int& height) = 0; + void SetGetFocusHdl(const Link<SwTOXWidget&,void>& rLink) { aGetFocusLink = rLink; } + virtual ~SwTOXWidget() {} +}; + +class SwTOXEdit : public SwTOXWidget +{ + std::unique_ptr<weld::Builder> m_xBuilder; + SwFormToken aFormToken; + Link<SwTOXEdit&,void> aModifiedLink; + Link<SwTOXEdit&,void> aPrevNextControlLink; + bool bNextControl; + SwTokenWindow* m_pParent; + std::unique_ptr<weld::Entry> m_xEntry; + + DECL_LINK(ModifyHdl, weld::Entry&, void); +public: + SwTOXEdit(SwTokenWindow* pTokenWin, const SwFormToken& rToken) + : m_xBuilder(Application::CreateBuilder(pTokenWin->get_child_container(), "modules/swriter/ui/toxentrywidget.ui")) + , aFormToken(rToken) + , bNextControl(false) + , m_pParent(pTokenWin) + , m_xEntry(m_xBuilder->weld_entry("entry", true)) + { + m_xEntry->connect_changed(LINK(this, SwTOXEdit, ModifyHdl)); + m_xEntry->connect_key_press(LINK(this, SwTOXEdit, KeyInputHdl)); + m_xEntry->connect_focus_in(LINK(this, SwTOXEdit, FocusInHdl)); + m_xEntry->set_tooltip_text(m_pParent->CreateQuickHelp(rToken)); + } + + virtual WindowType GetType() const override + { + return WindowType::EDIT; + } + + virtual void GrabFocus() override + { + m_xEntry->grab_focus(); + } + + virtual void Hide() override + { + m_xEntry->hide(); + } + + void Show() + { + m_xEntry->show(); + } + + void SetAccessibleName(const OUString& rName) + { + m_xEntry->set_accessible_name(rName); + } + + virtual void set_grid_left_attach(int nPos) override + { + m_xEntry->set_grid_left_attach(nPos); + } + + virtual void get_extents_relative_to(weld::Widget& rRelative, int& x, int& y, int& width, int& height) override + { + m_xEntry->get_extents_relative_to(rRelative, x, y, width, height); + } + + OUString GetText() const + { + return m_xEntry->get_text(); + } + + void SetText(const OUString& rText) + { + m_xEntry->set_text(rText); + } + + void get_selection_bounds(int& rStartPos, int& rEndPos) + { + m_xEntry->get_selection_bounds(rStartPos, rEndPos); + } + + void select_region(int nStartPos, int nEndPos) + { + m_xEntry->select_region(nStartPos, nEndPos); + } + + void SetModifyHdl(const Link<SwTOXEdit&,void>& rLink) + { + aModifiedLink = rLink; + } + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(FocusInHdl, weld::Widget&, void); + + bool IsNextControl() const { return bNextControl; } + void SetPrevNextLink(const Link<SwTOXEdit&,void>& rLink) { aPrevNextControlLink = rLink; } + + const SwFormToken& GetFormToken() + { + aFormToken.sText = m_xEntry->get_text(); + return aFormToken; + } + + void SetCharStyleName(const OUString& rSet, sal_uInt16 nPoolId) + { + aFormToken.sCharStyleName = rSet; + aFormToken.nPoolId = nPoolId; + } + + void AdjustSize(); +}; + +IMPL_LINK_NOARG(SwTOXEdit, ModifyHdl, weld::Entry&, void) +{ + aModifiedLink.Call(*this); +} + +IMPL_LINK(SwTOXEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bCall = false; + int nStartPos, nEndPos; + bool bStartIsEnd = !m_xEntry->get_selection_bounds(nStartPos, nEndPos); + int nMin = std::min(nStartPos, nEndPos); + const sal_Int32 nTextLen = GetText().getLength(); + if ((bStartIsEnd && !nMin) || nMin == nTextLen) + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + if (aCode.GetCode() == KEY_RIGHT && nMin == nTextLen) + { + bNextControl = true; + bCall = true; + } + else if (aCode.GetCode() == KEY_LEFT && !nMin) + { + bNextControl = false; + bCall = true; + } + else if ( (aCode.GetCode() == KEY_F3) && aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() ) + { + if (m_pParent) + { + m_pParent->SetFocus2theAllBtn(); + } + } + if (bCall && aPrevNextControlLink.IsSet()) + aPrevNextControlLink.Call(*this); + else + bCall = false; + + } + return bCall; +} + +IMPL_LINK_NOARG(SwTOXEdit, FocusInHdl, weld::Widget&, void) +{ + aGetFocusLink.Call(*this); +} + +void SwTOXEdit::AdjustSize() +{ + auto nWidth = m_xEntry->get_pixel_size(GetText()).Width(); + float fChars = nWidth / m_xEntry->get_approximate_digit_width(); + m_xEntry->set_width_chars(std::max(1.0f, std::ceil(fChars))); +} + +class SwTOXButton : public SwTOXWidget +{ + std::unique_ptr<weld::Builder> m_xBuilder; + SwFormToken aFormToken; + Link<SwTOXButton&,void> aPrevNextControlLink; + bool bNextControl; + SwTokenWindow* m_pParent; + std::unique_ptr<weld::ToggleButton> m_xButton; +public: + SwTOXButton(SwTokenWindow* pTokenWin, const SwFormToken& rToken) + : m_xBuilder(Application::CreateBuilder(pTokenWin->get_child_container(), "modules/swriter/ui/toxbuttonwidget.ui")) + , aFormToken(rToken) + , bNextControl(false) + , m_pParent(pTokenWin) + , m_xButton(m_xBuilder->weld_toggle_button("button", true)) + { + m_xButton->connect_key_press(LINK(this, SwTOXButton, KeyInputHdl)); + m_xButton->connect_focus_in(LINK(this, SwTOXButton, FocusInHdl)); + m_xButton->set_tooltip_text(m_pParent->CreateQuickHelp(rToken)); + } + + virtual WindowType GetType() const override + { + return WindowType::PUSHBUTTON; + } + + virtual void GrabFocus() override + { + m_xButton->grab_focus(); + } + + virtual void Hide() override + { + m_xButton->hide(); + } + + void Show() + { + m_xButton->show(); + } + + void SetAccessibleName(const OUString& rName) + { + m_xButton->set_accessible_name(rName); + } + + virtual void set_grid_left_attach(int nPos) override + { + m_xButton->set_grid_left_attach(nPos); + } + + void get_extents_relative_to(weld::Widget& rRelative, int& x, int& y, int& width, int& height) override + { + m_xButton->get_extents_relative_to(rRelative, x, y, width, height); + } + + void Check(bool bCheck = true) + { + m_xButton->set_active(bCheck); + } + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(FocusInHdl, weld::Widget&, void); + + bool IsNextControl() const {return bNextControl;} + void SetPrevNextLink(const Link<SwTOXButton&,void>& rLink) {aPrevNextControlLink = rLink;} + const SwFormToken& GetFormToken() const {return aFormToken;} + + void SetCharStyleName(const OUString& rSet, sal_uInt16 nPoolId) + { + aFormToken.sCharStyleName = rSet; + aFormToken.nPoolId = nPoolId; + } + + void SetTabPosition(SwTwips nSet) + { aFormToken.nTabStopPosition = nSet; } + + void SetFillChar( sal_Unicode cSet ) + { aFormToken.cTabFillChar = cSet; } + + void SetTabAlign(SvxTabAdjust eAlign) + { aFormToken.eTabAlign = eAlign;} + +//---> i89791 + //used for entry number format, in TOC only + //needed for different UI dialog position + void SetEntryNumberFormat(sal_uInt16 nSet) { + switch(nSet) + { + default: + case 0: + aFormToken.nChapterFormat = CF_NUMBER; + break; + case 1: + aFormToken.nChapterFormat = CF_NUM_NOPREPST_TITLE; + break; + } + } + + void SetChapterInfo(sal_uInt16 nSet) { + switch(nSet) + { + default: + case 0: + aFormToken.nChapterFormat = CF_NUM_NOPREPST_TITLE; + break; + case 1: + aFormToken.nChapterFormat = CF_TITLE; + break; + case 2: + aFormToken.nChapterFormat = CF_NUMBER_NOPREPST; + break; + } + } + + void SetOutlineLevel( sal_uInt16 nSet ) { aFormToken.nOutlineLevel = nSet;}//i53420 + + void SetText(const OUString& rText) + { + m_xButton->set_label(rText); + } + + void SetLinkEnd() + { + OSL_ENSURE(TOKEN_LINK_START == aFormToken.eTokenType, + "call SetLinkEnd for link start only!"); + aFormToken.eTokenType = TOKEN_LINK_END; + aFormToken.sText = SwForm::GetFormLinkEnd(); + SetText(aFormToken.sText); + } + + void SetLinkStart() + { + OSL_ENSURE(TOKEN_LINK_END == aFormToken.eTokenType, + "call SetLinkStart for link start only!"); + aFormToken.eTokenType = TOKEN_LINK_START; + aFormToken.sText = SwForm::GetFormLinkStt(); + SetText(aFormToken.sText); + } +}; + +IMPL_LINK(SwTOXButton, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bCall = false; + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + if (aCode.GetCode() == KEY_RIGHT) + { + bNextControl = true; + bCall = true; + } + else if (aCode.GetCode() == KEY_LEFT) + { + bNextControl = false; + bCall = true; + } + else if (aCode.GetCode() == KEY_DELETE) + { + m_pParent->RemoveControl(this, true); + //this is invalid here + return true; + } + else if ( (aCode.GetCode() == KEY_F3) && aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() ) + { + if (m_pParent) + { + m_pParent->SetFocus2theAllBtn(); + } + } + if (bCall && aPrevNextControlLink.IsSet()) + aPrevNextControlLink.Call(*this); + else + bCall = false; + return bCall; +} + +IMPL_LINK_NOARG(SwTOXButton, FocusInHdl, weld::Widget&, void) +{ + aGetFocusLink.Call(*this); +} + +namespace +{ + const char* STR_AUTH_FIELD_ARY[] = + { + STR_AUTH_FIELD_IDENTIFIER, + STR_AUTH_FIELD_AUTHORITY_TYPE, + STR_AUTH_FIELD_ADDRESS, + STR_AUTH_FIELD_ANNOTE, + STR_AUTH_FIELD_AUTHOR, + STR_AUTH_FIELD_BOOKTITLE, + STR_AUTH_FIELD_CHAPTER, + STR_AUTH_FIELD_EDITION, + STR_AUTH_FIELD_EDITOR, + STR_AUTH_FIELD_HOWPUBLISHED, + STR_AUTH_FIELD_INSTITUTION, + STR_AUTH_FIELD_JOURNAL, + STR_AUTH_FIELD_MONTH, + STR_AUTH_FIELD_NOTE, + STR_AUTH_FIELD_NUMBER, + STR_AUTH_FIELD_ORGANIZATIONS, + STR_AUTH_FIELD_PAGES, + STR_AUTH_FIELD_PUBLISHER, + STR_AUTH_FIELD_SCHOOL, + STR_AUTH_FIELD_SERIES, + STR_AUTH_FIELD_TITLE, + STR_AUTH_FIELD_TYPE, + STR_AUTH_FIELD_VOLUME, + STR_AUTH_FIELD_YEAR, + STR_AUTH_FIELD_URL, + STR_AUTH_FIELD_CUSTOM1, + STR_AUTH_FIELD_CUSTOM2, + STR_AUTH_FIELD_CUSTOM3, + STR_AUTH_FIELD_CUSTOM4, + STR_AUTH_FIELD_CUSTOM5, + STR_AUTH_FIELD_ISBN + }; +} + +SwTOXEntryTabPage::SwTOXEntryTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/tocentriespage.ui", "TocEntriesPage", &rAttrSet) + , sDelimStr(SwResId(STR_DELIM)) + , sNoCharStyle(SwResId(STR_NO_CHAR_STYLE)) + , m_pCurrentForm(nullptr) + , bInLevelHdl(false) + , m_xTypeFT(m_xBuilder->weld_label("typeft")) + , m_xLevelFT(m_xBuilder->weld_label("levelft")) + , m_xLevelLB(m_xBuilder->weld_tree_view("level")) + , m_xAllLevelsPB(m_xBuilder->weld_button("all")) + , m_xEntryNoPB(m_xBuilder->weld_button("chapterno")) + , m_xEntryPB(m_xBuilder->weld_button("entrytext")) + , m_xTabPB(m_xBuilder->weld_button("tabstop")) + , m_xChapterInfoPB(m_xBuilder->weld_button("chapterinfo")) + , m_xPageNoPB(m_xBuilder->weld_button("pageno")) + , m_xHyperLinkPB(m_xBuilder->weld_button("hyperlink")) + , m_xAuthFieldsLB(m_xBuilder->weld_combo_box("authfield")) + , m_xAuthInsertPB(m_xBuilder->weld_button("insert")) + , m_xAuthRemovePB(m_xBuilder->weld_button("remove")) + , m_xCharStyleLB(m_xBuilder->weld_combo_box("charstyle")) + , m_xEditStylePB(m_xBuilder->weld_button("edit")) + , m_xChapterEntryFT(m_xBuilder->weld_label("chapterentryft")) + , m_xChapterEntryLB(m_xBuilder->weld_combo_box("chapterentry")) + , m_xNumberFormatFT(m_xBuilder->weld_label("numberformatft")) + , m_xNumberFormatLB(m_xBuilder->weld_combo_box("numberformat")) + , m_xEntryOutlineLevelFT(m_xBuilder->weld_label("entryoutlinelevelft")) + , m_xEntryOutlineLevelNF(m_xBuilder->weld_spin_button("entryoutlinelevel")) + , m_xFillCharFT(m_xBuilder->weld_label("fillcharft")) + , m_xFillCharCB(m_xBuilder->weld_combo_box("fillchar")) + , m_xTabPosFT(m_xBuilder->weld_label("tabstopposft")) + , m_xTabPosMF(m_xBuilder->weld_metric_spin_button("tabstoppos", FieldUnit::CM)) + , m_xAutoRightCB(m_xBuilder->weld_check_button("alignright")) + , m_xFormatFrame(m_xBuilder->weld_widget("formatframe")) + , m_xMainEntryStyleFT(m_xBuilder->weld_label("mainstyleft")) + , m_xMainEntryStyleLB(m_xBuilder->weld_combo_box("mainstyle")) + , m_xAlphaDelimCB(m_xBuilder->weld_check_button("alphadelim")) + , m_xCommaSeparatedCB(m_xBuilder->weld_check_button("commasep")) + , m_xRelToStyleCB(m_xBuilder->weld_check_button("reltostyle")) + , m_xSortingFrame(m_xBuilder->weld_widget("sortingframe")) + , m_xSortDocPosRB(m_xBuilder->weld_radio_button("sortpos")) + , m_xSortContentRB(m_xBuilder->weld_radio_button("sortcontents")) + , m_xSortKeyFrame(m_xBuilder->weld_widget("sortkeyframe")) + , m_xFirstKeyLB(m_xBuilder->weld_combo_box("key1lb")) + , m_xFirstSortUpRB(m_xBuilder->weld_radio_button("up1cb")) + , m_xFirstSortDownRB(m_xBuilder->weld_radio_button("down1cb")) + , m_xSecondKeyLB(m_xBuilder->weld_combo_box("key2lb")) + , m_xSecondSortUpRB(m_xBuilder->weld_radio_button("up2cb")) + , m_xSecondSortDownRB(m_xBuilder->weld_radio_button("down2cb")) + , m_xThirdKeyLB(m_xBuilder->weld_combo_box("key3lb")) + , m_xThirdSortUpRB(m_xBuilder->weld_radio_button("up3cb")) + , m_xThirdSortDownRB(m_xBuilder->weld_radio_button("down3cb")) + , m_xTokenWIN(new SwTokenWindow(m_xBuilder->weld_container("token"))) +{ + const OUString sNoCharSortKey(SwResId(STR_NOSORTKEY)); + + sAuthTypeStr = m_xTypeFT->get_label(); + sLevelStr = m_xLevelFT->get_label(); + m_xAuthFieldsLB->make_sorted(); + m_xTokenWIN->SetTabPage(this); + + aLastTOXType.eType = TOXTypes(USHRT_MAX); + aLastTOXType.nIndex = 0; + + SetExchangeSupport(); + m_xEntryNoPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xEntryPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xChapterInfoPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xPageNoPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xTabPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xHyperLinkPB->connect_clicked(LINK(this, SwTOXEntryTabPage, InsertTokenHdl)); + m_xEditStylePB->connect_clicked(LINK(this, SwTOXEntryTabPage, EditStyleHdl)); + m_xLevelLB->connect_changed(LINK(this, SwTOXEntryTabPage, LevelHdl)); + m_xTokenWIN->SetButtonSelectedHdl(LINK(this, SwTOXEntryTabPage, TokenSelectedHdl)); + m_xTokenWIN->SetModifyHdl(LINK(this, SwTOXEntryTabPage, ModifyHdl)); + m_xCharStyleLB->connect_changed(LINK(this, SwTOXEntryTabPage, StyleSelectHdl)); + m_xCharStyleLB->append_text(sNoCharStyle); + m_xChapterEntryLB->connect_changed(LINK(this, SwTOXEntryTabPage, ChapterInfoHdl)); + m_xEntryOutlineLevelNF->connect_value_changed(LINK(this, SwTOXEntryTabPage, ChapterInfoOutlineHdl)); + m_xNumberFormatLB->connect_changed(LINK(this, SwTOXEntryTabPage, NumberFormatHdl)); + + m_xTabPosMF->connect_value_changed(LINK(this, SwTOXEntryTabPage, TabPosHdl)); + m_xFillCharCB->connect_changed(LINK(this, SwTOXEntryTabPage, FillCharHdl)); + m_xAutoRightCB->connect_toggled(LINK(this, SwTOXEntryTabPage, AutoRightHdl)); + m_xAuthInsertPB->connect_clicked(LINK(this, SwTOXEntryTabPage, RemoveInsertAuthHdl)); + m_xAuthRemovePB->connect_clicked(LINK(this, SwTOXEntryTabPage, RemoveInsertAuthHdl)); + m_xSortDocPosRB->connect_toggled(LINK(this, SwTOXEntryTabPage, SortKeyHdl)); + m_xSortContentRB->connect_toggled(LINK(this, SwTOXEntryTabPage, SortKeyHdl)); + m_xAllLevelsPB->connect_clicked(LINK(this, SwTOXEntryTabPage, AllLevelsHdl)); + + m_xAlphaDelimCB->connect_toggled(LINK(this, SwTOXEntryTabPage, ModifyClickHdl)); + m_xCommaSeparatedCB->connect_toggled(LINK(this, SwTOXEntryTabPage, ModifyClickHdl)); + m_xRelToStyleCB->connect_toggled(LINK(this, SwTOXEntryTabPage, ModifyClickHdl)); + + FieldUnit aMetric = ::GetDfltMetric(false); + ::SetFieldUnit(*m_xTabPosMF, aMetric); + + m_xSortDocPosRB->set_active(true); + + m_xFillCharCB->set_entry_max_length(1); + m_xFillCharCB->append_text(OUString(' ')); + m_xFillCharCB->append_text(OUString('.')); + m_xFillCharCB->append_text(OUString('-')); + m_xFillCharCB->append_text(OUString('_')); + m_xFillCharCB->append_text(OUString(u'\x2024')); // ONE DOT LEADER + m_xFillCharCB->append_text(OUString(u'\x2025')); // TWO DOT LEADER + m_xFillCharCB->append_text(OUString(u'\x2026')); // HORIZONTAL ELLIPSIS + + m_xEditStylePB->set_sensitive(false); + + //fill the types in + for (sal_uInt16 i = 0; i < AUTH_FIELD_END; ++i) + { + OUString sId(OUString::number(i)); + m_xAuthFieldsLB->append(sId, SwResId(STR_AUTH_FIELD_ARY[i])); + } + + m_xFirstKeyLB->append(OUString::number(USHRT_MAX), sNoCharSortKey); + m_xSecondKeyLB->append(OUString::number(USHRT_MAX), sNoCharSortKey); + m_xThirdKeyLB->append(OUString::number(USHRT_MAX), sNoCharSortKey); + + for (sal_uInt16 i = 0; i < AUTH_FIELD_END; ++i) + { + const OUString sTmp(m_xAuthFieldsLB->get_text(i)); + const OUString sEntryData(m_xAuthFieldsLB->get_id(i)); + m_xFirstKeyLB->append(sEntryData, sTmp); + m_xSecondKeyLB->append(sEntryData, sTmp); + m_xThirdKeyLB->append(sEntryData, sTmp); + } + m_xFirstKeyLB->set_active(0); + m_xSecondKeyLB->set_active(0); + m_xThirdKeyLB->set_active(0); + + //lock size + Size aPrefSize(m_xContainer->get_preferred_size()); + m_xContainer->set_size_request(aPrefSize.Width(), aPrefSize.Height()); +} + +SwTOXEntryTabPage::~SwTOXEntryTabPage() +{ + m_xTokenWIN.reset(); +} + +IMPL_LINK_NOARG(SwTOXEntryTabPage, ModifyClickHdl, weld::ToggleButton&, void) +{ + OnModify(true); +} + +IMPL_LINK_NOARG(SwTOXEntryTabPage, ModifyHdl, LinkParamNone*, void) +{ + OnModify(false); +} + +// bAllLevels is used as signal to change all levels of the example +void SwTOXEntryTabPage::OnModify(bool bAllLevels) +{ + UpdateDescriptor(); + + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + if (pTOXDlg) + { + sal_uInt16 nCurLevel = m_xLevelLB->get_selected_index() + 1; + if (aLastTOXType.eType == TOX_CONTENT && bAllLevels) + nCurLevel = USHRT_MAX; + pTOXDlg->CreateOrUpdateExample( + pTOXDlg->GetCurrentTOXType().eType, TOX_PAGE_ENTRY, nCurLevel); + } +} + +bool SwTOXEntryTabPage::FillItemSet( SfxItemSet* ) +{ + // nothing to do + return true; +} + +void SwTOXEntryTabPage::Reset( const SfxItemSet* ) +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + const CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + m_pCurrentForm = pTOXDlg->GetForm(aCurType); + if(TOX_INDEX == aCurType.eType) + { + SwTOXDescription& rDesc = pTOXDlg->GetTOXDescription(aCurType); + const OUString& sMainEntryCharStyle = rDesc.GetMainEntryCharStyle(); + if(!sMainEntryCharStyle.isEmpty()) + { + if (m_xMainEntryStyleLB->find_text(sMainEntryCharStyle) == -1) + m_xMainEntryStyleLB->append_text(sMainEntryCharStyle); + m_xMainEntryStyleLB->set_active_text(sMainEntryCharStyle); + } + else + m_xMainEntryStyleLB->set_active_text(sNoCharStyle); + m_xAlphaDelimCB->set_active( bool(rDesc.GetIndexOptions() & SwTOIOptions::AlphaDelimiter) ); + } + m_xRelToStyleCB->set_active(m_pCurrentForm->IsRelTabPos()); + m_xCommaSeparatedCB->set_active(m_pCurrentForm->IsCommaSeparated()); +} + +void SwTOXEntryTabPage::ActivatePage( const SfxItemSet& /*rSet*/) +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + const CurTOXType aCurType = pTOXDlg->GetCurrentTOXType(); + + m_pCurrentForm = pTOXDlg->GetForm(aCurType); + if( !( aLastTOXType == aCurType )) + { + bool bToxIsAuthorities = TOX_AUTHORITIES == aCurType.eType; + bool bToxIsIndex = TOX_INDEX == aCurType.eType; + bool bToxIsContent = TOX_CONTENT == aCurType.eType; + bool bToxSupportsLinks = TOX_CONTENT == aCurType.eType || + TOX_ILLUSTRATIONS == aCurType.eType || + TOX_TABLES == aCurType.eType || + TOX_OBJECTS == aCurType.eType || + TOX_USER == aCurType.eType; + + m_xLevelLB->clear(); + for(sal_uInt16 i = 1; i < m_pCurrentForm->GetFormMax(); i++) + { + if(bToxIsAuthorities) + m_xLevelLB->append_text( SwAuthorityFieldType::GetAuthTypeName( + static_cast<ToxAuthorityType>(i - 1)) ); + else if( bToxIsIndex ) + { + if(i == 1) + m_xLevelLB->append_text( sDelimStr ); + else + m_xLevelLB->append_text( OUString::number(i - 1) ); + } + else + m_xLevelLB->append_text(OUString::number(i)); + } + if(bToxIsAuthorities) + { + SwWrtShell& rSh = pTOXDlg->GetWrtShell(); + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + rSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + if(pFType->IsSortByDocument()) + m_xSortDocPosRB->set_active(true); + else + { + m_xSortContentRB->set_active(true); + const sal_uInt16 nKeyCount = pFType->GetSortKeyCount(); + if(0 < nKeyCount) + { + const SwTOXSortKey* pKey = pFType->GetSortKey(0); + m_xFirstKeyLB->set_active_id(OUString::number(pKey->eField)); + m_xFirstSortUpRB->set_active(pKey->bSortAscending); + m_xFirstSortDownRB->set_active(!pKey->bSortAscending); + } + if(1 < nKeyCount) + { + const SwTOXSortKey* pKey = pFType->GetSortKey(1); + m_xSecondKeyLB->set_active_id(OUString::number(pKey->eField)); + m_xSecondSortUpRB->set_active(pKey->bSortAscending); + m_xSecondSortDownRB->set_active(!pKey->bSortAscending); + } + if(2 < nKeyCount) + { + const SwTOXSortKey* pKey = pFType->GetSortKey(2); + m_xThirdKeyLB->set_active_id(OUString::number(pKey->eField)); + m_xThirdSortUpRB->set_active(pKey->bSortAscending); + m_xThirdSortDownRB->set_active(!pKey->bSortAscending); + } + } + } + SortKeyHdl(m_xSortDocPosRB->get_active() ? *m_xSortDocPosRB : *m_xSortContentRB); + m_xLevelFT->set_label(sAuthTypeStr); + } + else + m_xLevelFT->set_label(sLevelStr); + + m_xLevelLB->select(bToxIsIndex ? 1 : 0); + + //show or hide controls + m_xEntryNoPB->set_visible(bToxIsContent); + m_xHyperLinkPB->set_visible(bToxSupportsLinks); + m_xRelToStyleCB->set_visible(!bToxIsAuthorities); + m_xChapterInfoPB->set_visible(!bToxIsContent && !bToxIsAuthorities); + m_xEntryPB->set_visible(!bToxIsAuthorities); + m_xPageNoPB->set_visible(!bToxIsAuthorities); + m_xAuthFieldsLB->set_visible(bToxIsAuthorities); + m_xAuthInsertPB->set_visible(bToxIsAuthorities); + m_xAuthRemovePB->set_visible(bToxIsAuthorities); + + m_xFormatFrame->set_visible(!bToxIsAuthorities); + + m_xSortingFrame->set_visible(bToxIsAuthorities); + m_xSortKeyFrame->set_visible(bToxIsAuthorities); + + m_xMainEntryStyleFT->set_visible(bToxIsIndex); + m_xMainEntryStyleLB->set_visible(bToxIsIndex); + m_xAlphaDelimCB->set_visible(bToxIsIndex); + m_xCommaSeparatedCB->set_visible(bToxIsIndex); + } + aLastTOXType = aCurType; + + //invalidate PatternWindow + m_xTokenWIN->SetInvalid(); + LevelHdl(*m_xLevelLB); +} + +void SwTOXEntryTabPage::UpdateDescriptor() +{ + WriteBackLevel(); + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + SwTOXDescription& rDesc = pTOXDlg->GetTOXDescription(aLastTOXType); + if(TOX_INDEX == aLastTOXType.eType) + { + const OUString sTemp(m_xMainEntryStyleLB->get_active_text()); + rDesc.SetMainEntryCharStyle(sNoCharStyle == sTemp ? OUString(): sTemp); + SwTOIOptions nIdxOptions = rDesc.GetIndexOptions() & ~SwTOIOptions::AlphaDelimiter; + if (m_xAlphaDelimCB->get_active()) + nIdxOptions |= SwTOIOptions::AlphaDelimiter; + rDesc.SetIndexOptions(nIdxOptions); + } + else if (TOX_AUTHORITIES == aLastTOXType.eType) + { + rDesc.SetSortByDocument(m_xSortDocPosRB->get_active()); + SwTOXSortKey aKey1, aKey2, aKey3; + aKey1.eField = static_cast<ToxAuthorityField>(m_xFirstKeyLB->get_active_id().toInt32()); + aKey1.bSortAscending = m_xFirstSortUpRB->get_active(); + aKey2.eField = static_cast<ToxAuthorityField>(m_xSecondKeyLB->get_active_id().toInt32()); + aKey2.bSortAscending = m_xSecondSortUpRB->get_active(); + aKey3.eField = static_cast<ToxAuthorityField>(m_xThirdKeyLB->get_active_id().toInt32()); + aKey3.bSortAscending = m_xThirdSortUpRB->get_active(); + + rDesc.SetSortKeys(aKey1, aKey2, aKey3); + } + SwForm* pCurrentForm = pTOXDlg->GetForm(aLastTOXType); + if (m_xRelToStyleCB->get_visible()) + pCurrentForm->SetRelTabPos(m_xRelToStyleCB->get_active()); + if (m_xCommaSeparatedCB->get_visible()) + pCurrentForm->SetCommaSeparated(m_xCommaSeparatedCB->get_active()); +} + +DeactivateRC SwTOXEntryTabPage::DeactivatePage( SfxItemSet* /*pSet*/) +{ + UpdateDescriptor(); + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SwTOXEntryTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwTOXEntryTabPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK_NOARG(SwTOXEntryTabPage, EditStyleHdl, weld::Button&, void) +{ + if (m_xCharStyleLB->get_active() != -1) + { + SfxStringItem aStyle(SID_STYLE_EDIT, m_xCharStyleLB->get_active_text()); + SfxUInt16Item aFamily(SID_STYLE_FAMILY, sal_uInt16(SfxStyleFamily::Char)); + static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(). + GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_STYLE_EDIT, + SfxCallMode::SYNCHRON, + { &aStyle, &aFamily }); + } +} + +IMPL_LINK(SwTOXEntryTabPage, RemoveInsertAuthHdl, weld::Button&, rButton, void) +{ + bool bInsert = &rButton == m_xAuthInsertPB.get(); + if(bInsert) + { + sal_Int32 nSelPos = m_xAuthFieldsLB->get_active(); + const OUString sToInsert(m_xAuthFieldsLB->get_active_text()); + SwFormToken aInsert(TOKEN_AUTHORITY); + aInsert.nAuthorityField = m_xAuthFieldsLB->get_id(nSelPos).toUInt32(); + m_xTokenWIN->InsertAtSelection(aInsert); + m_xAuthFieldsLB->remove_text(sToInsert); + m_xAuthFieldsLB->set_active(nSelPos ? nSelPos - 1 : 0); + } + else + { + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(WindowType::EDIT != pCtrl->GetType(), "Remove should be disabled"); + if (WindowType::EDIT != pCtrl->GetType()) + { + //fill it into the ListBox + const SwFormToken& rToken = static_cast<SwTOXButton*>(pCtrl)->GetFormToken(); + PreTokenButtonRemoved(rToken); + m_xTokenWIN->RemoveControl(static_cast<SwTOXButton*>(pCtrl)); + } + } + ModifyHdl(nullptr); +} + +void SwTOXEntryTabPage::PreTokenButtonRemoved(const SwFormToken& rToken) +{ + //fill it into the ListBox + sal_uInt32 nData = rToken.nAuthorityField; + m_xAuthFieldsLB->append(OUString::number(nData), SwResId(STR_AUTH_FIELD_ARY[nData])); +} + +void SwTOXEntryTabPage::SetFocus2theAllBtn() +{ + m_xAllLevelsPB->grab_focus(); +} + +// This function initializes the default value in the Token +// put here the UI dependent initializations +IMPL_LINK(SwTOXEntryTabPage, InsertTokenHdl, weld::Button&, rBtn, void) +{ + OUString sText; + FormTokenType eTokenType = TOKEN_ENTRY_NO; + OUString sCharStyle; + sal_uInt16 nChapterFormat = CF_NUMBER; // i89791 + if (&rBtn == m_xEntryNoPB.get()) + { + sText = SwForm::GetFormEntryNum(); + eTokenType = TOKEN_ENTRY_NO; + } + else if (&rBtn == m_xEntryPB.get()) + { + if( TOX_CONTENT == m_pCurrentForm->GetTOXType() ) + { + sText = SwForm::GetFormEntryText(); + eTokenType = TOKEN_ENTRY_TEXT; + } + else + { + sText = SwForm::GetFormEntry(); + eTokenType = TOKEN_ENTRY; + } + } + else if (&rBtn == m_xChapterInfoPB.get()) + { + sText = SwForm::GetFormChapterMark(); + eTokenType = TOKEN_CHAPTER_INFO; + nChapterFormat = CF_NUM_NOPREPST_TITLE; // i89791 + } + else if (&rBtn == m_xPageNoPB.get()) + { + sText = SwForm::GetFormPageNums(); + eTokenType = TOKEN_PAGE_NUMS; + } + else if (&rBtn == m_xHyperLinkPB.get()) + { + sText = SwForm::GetFormLinkStt(); + eTokenType = TOKEN_LINK_START; + sCharStyle = SwResId(STR_POOLCHR_TOXJUMP); + } + else if (&rBtn == m_xTabPB.get()) + { + sText = SwForm::GetFormTab(); + eTokenType = TOKEN_TAB_STOP; + } + SwFormToken aInsert(eTokenType); + aInsert.sCharStyleName = sCharStyle; + aInsert.nTabStopPosition = 0; + aInsert.nChapterFormat = nChapterFormat; // i89791 + m_xTokenWIN->InsertAtSelection(aInsert); + ModifyHdl(nullptr); +} + +IMPL_LINK_NOARG(SwTOXEntryTabPage, AllLevelsHdl, weld::Button&, void) +{ + //get current level + //write it into all levels + if(m_xTokenWIN->IsValid()) + { + const OUString sNewToken = m_xTokenWIN->GetPattern(); + for(sal_uInt16 i = 1; i < m_pCurrentForm->GetFormMax(); i++) + m_pCurrentForm->SetPattern(i, sNewToken); + + OnModify(true); + } +} + +void SwTOXEntryTabPage::WriteBackLevel() +{ + if(m_xTokenWIN->IsValid()) + { + const OUString sNewToken = m_xTokenWIN->GetPattern(); + const sal_uInt16 nLastLevel = m_xTokenWIN->GetLastLevel(); + if(nLastLevel != USHRT_MAX) + m_pCurrentForm->SetPattern(nLastLevel + 1, sNewToken); + } +} + +IMPL_LINK(SwTOXEntryTabPage, LevelHdl, weld::TreeView&, rBox, void) +{ + if(bInLevelHdl) + return; + bInLevelHdl = true; + WriteBackLevel(); + + const sal_uInt16 nLevel = rBox.get_selected_index(); + m_xTokenWIN->SetForm(*m_pCurrentForm, nLevel); + if(TOX_AUTHORITIES == m_pCurrentForm->GetTOXType()) + { + //fill the types in + m_xAuthFieldsLB->clear(); + for( sal_uInt32 i = 0; i < AUTH_FIELD_END; i++) + { + m_xAuthFieldsLB->append(OUString::number(i), SwResId(STR_AUTH_FIELD_ARY[i])); + } + + // #i21237# + SwFormTokens aPattern = m_pCurrentForm->GetPattern(nLevel + 1); + + for(const auto& aToken : aPattern) + { + if(TOKEN_AUTHORITY == aToken.eTokenType) + { + sal_uInt32 nSearch = aToken.nAuthorityField; + int nLstBoxPos = m_xAuthFieldsLB->find_id(OUString::number(nSearch)); + OSL_ENSURE(nLstBoxPos != -1, "Entry not found?"); + m_xAuthFieldsLB->remove(nLstBoxPos); + } + } + m_xAuthFieldsLB->set_active(0); + } + bInLevelHdl = false; + rBox.grab_focus(); +} + +IMPL_LINK_NOARG(SwTOXEntryTabPage, SortKeyHdl, weld::ToggleButton&, void) +{ + bool bEnable = m_xSortContentRB->get_active(); + m_xSortKeyFrame->set_sensitive(bEnable); +} + +IMPL_LINK(SwTOXEntryTabPage, TokenSelectedHdl, SwFormToken&, rToken, void) +{ + if (!rToken.sCharStyleName.isEmpty()) + m_xCharStyleLB->set_active_text(rToken.sCharStyleName); + else + m_xCharStyleLB->set_active_text(sNoCharStyle); + + const OUString sEntry = m_xCharStyleLB->get_active_text(); + m_xEditStylePB->set_sensitive(sEntry != sNoCharStyle); + + if(rToken.eTokenType == TOKEN_CHAPTER_INFO) + { +//---> i89791 + switch(rToken.nChapterFormat) + { + default: + m_xChapterEntryLB->set_active(-1);//to alert the user + break; + case CF_NUM_NOPREPST_TITLE: + m_xChapterEntryLB->set_active(0); + break; + case CF_TITLE: + m_xChapterEntryLB->set_active(1); + break; + case CF_NUMBER_NOPREPST: + m_xChapterEntryLB->set_active(2); + break; + } +//i53420 + + m_xEntryOutlineLevelNF->set_value(rToken.nOutlineLevel); + } + +//i53420 + if(rToken.eTokenType == TOKEN_ENTRY_NO) + { + m_xEntryOutlineLevelNF->set_value(rToken.nOutlineLevel); + const sal_uInt16 nFormat = + rToken.nChapterFormat == CF_NUM_NOPREPST_TITLE ? 1 : 0; + m_xNumberFormatLB->set_active(nFormat); + } + + bool bTabStop = TOKEN_TAB_STOP == rToken.eTokenType; + m_xFillCharFT->set_visible(bTabStop); + m_xFillCharCB->set_visible(bTabStop); + m_xTabPosFT->set_visible(bTabStop); + m_xTabPosMF->set_visible(bTabStop); + m_xAutoRightCB->set_visible(bTabStop); + m_xAutoRightCB->set_sensitive(bTabStop); + if(bTabStop) + { + m_xTabPosMF->set_value(m_xTabPosMF->normalize(rToken.nTabStopPosition), FieldUnit::TWIP); + m_xAutoRightCB->set_active(SvxTabAdjust::End == rToken.eTabAlign); + m_xFillCharCB->set_entry_text(OUString(rToken.cTabFillChar)); + m_xTabPosFT->set_sensitive(!m_xAutoRightCB->get_active()); + m_xTabPosMF->set_sensitive(!m_xAutoRightCB->get_active()); + } + else + { + m_xTabPosMF->set_sensitive(false); + } + + bool bIsChapterInfo = rToken.eTokenType == TOKEN_CHAPTER_INFO; + bool bIsEntryNumber = rToken.eTokenType == TOKEN_ENTRY_NO; + m_xChapterEntryFT->set_visible( bIsChapterInfo ); + m_xChapterEntryLB->set_visible( bIsChapterInfo ); + m_xEntryOutlineLevelFT->set_visible( bIsChapterInfo || bIsEntryNumber ); + m_xEntryOutlineLevelNF->set_visible( bIsChapterInfo || bIsEntryNumber ); + m_xNumberFormatFT->set_visible( bIsEntryNumber ); + m_xNumberFormatLB->set_visible( bIsEntryNumber ); + + //now enable the visible buttons + //- inserting the same type of control is not allowed + //- some types of controls can only appear once (EntryText EntryNumber) + + if (m_xEntryNoPB->get_visible()) + { + m_xEntryNoPB->set_sensitive(TOKEN_ENTRY_NO != rToken.eTokenType ); + } + if (m_xEntryPB->get_visible()) + { + m_xEntryPB->set_sensitive(TOKEN_ENTRY_TEXT != rToken.eTokenType && + !m_xTokenWIN->Contains(TOKEN_ENTRY_TEXT) + && !m_xTokenWIN->Contains(TOKEN_ENTRY)); + } + + if (m_xChapterInfoPB->get_visible()) + { + m_xChapterInfoPB->set_sensitive(TOKEN_CHAPTER_INFO != rToken.eTokenType); + } + if (m_xPageNoPB->get_visible()) + { + m_xPageNoPB->set_sensitive(TOKEN_PAGE_NUMS != rToken.eTokenType && + !m_xTokenWIN->Contains(TOKEN_PAGE_NUMS)); + } + if (m_xTabPB->get_visible()) + { + m_xTabPB->set_sensitive(!bTabStop); + } + if (m_xHyperLinkPB->get_visible()) + { + m_xHyperLinkPB->set_sensitive(TOKEN_LINK_START != rToken.eTokenType && + TOKEN_LINK_END != rToken.eTokenType); + } + //table of authorities + if (m_xAuthInsertPB->get_visible()) + { + bool bText = TOKEN_TEXT == rToken.eTokenType; + m_xAuthInsertPB->set_sensitive(bText && !m_xAuthFieldsLB->get_active_text().isEmpty()); + m_xAuthRemovePB->set_sensitive(!bText); + } +} + +IMPL_LINK(SwTOXEntryTabPage, StyleSelectHdl, weld::ComboBox&, rBox, void) +{ + OUString sEntry = rBox.get_active_text(); + const sal_uInt16 nId = rBox.get_active_id().toUInt32(); + const bool bEqualsNoCharStyle = sEntry == sNoCharStyle; + m_xEditStylePB->set_sensitive(!bEqualsNoCharStyle); + if (bEqualsNoCharStyle) + sEntry.clear(); + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl, "no active control?"); + if(pCtrl) + { + if(WindowType::EDIT == pCtrl->GetType()) + static_cast<SwTOXEdit*>(pCtrl)->SetCharStyleName(sEntry, nId); + else + static_cast<SwTOXButton*>(pCtrl)->SetCharStyleName(sEntry, nId); + + } + ModifyHdl(nullptr); +} + +IMPL_LINK(SwTOXEntryTabPage, ChapterInfoHdl, weld::ComboBox&, rBox, void) +{ + int nPos = rBox.get_active(); + if (nPos != -1) + { + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl, "no active control?"); + if(pCtrl && WindowType::EDIT != pCtrl->GetType()) + static_cast<SwTOXButton*>(pCtrl)->SetChapterInfo(nPos); + ModifyHdl(nullptr); + } +} + +IMPL_LINK(SwTOXEntryTabPage, ChapterInfoOutlineHdl, weld::SpinButton&, rEdit, void) +{ + const sal_uInt16 nLevel = rEdit.get_value(); + + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl, "no active control?"); + if(pCtrl && WindowType::EDIT != pCtrl->GetType()) + static_cast<SwTOXButton*>(pCtrl)->SetOutlineLevel(nLevel); + + ModifyHdl(nullptr); +} + +IMPL_LINK(SwTOXEntryTabPage, NumberFormatHdl, weld::ComboBox&, rBox, void) +{ + const sal_Int32 nPos = rBox.get_active(); + if (nPos != -1) + { + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl, "no active control?"); + if(pCtrl && WindowType::EDIT != pCtrl->GetType()) + { + static_cast<SwTOXButton*>(pCtrl)->SetEntryNumberFormat(nPos);//i89791 + } + ModifyHdl(nullptr); + } +} + +IMPL_LINK(SwTOXEntryTabPage, TabPosHdl, weld::MetricSpinButton&, rEdit, void) +{ + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl && WindowType::EDIT != pCtrl->GetType() && + TOKEN_TAB_STOP == static_cast<SwTOXButton*>(pCtrl)->GetFormToken().eTokenType, + "no active style::TabStop control?"); + if( pCtrl && WindowType::EDIT != pCtrl->GetType() ) + { + static_cast<SwTOXButton*>(pCtrl)->SetTabPosition( static_cast< SwTwips >( + rEdit.denormalize(rEdit.get_value(FieldUnit::TWIP)))); + } + ModifyHdl(nullptr); +} + +IMPL_LINK(SwTOXEntryTabPage, FillCharHdl, weld::ComboBox&, rBox, void) +{ + SwTOXWidget* pCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(pCtrl && WindowType::EDIT != pCtrl->GetType() && + TOKEN_TAB_STOP == static_cast<SwTOXButton*>(pCtrl)->GetFormToken().eTokenType, + "no active style::TabStop control?"); + if (pCtrl && WindowType::EDIT != pCtrl->GetType()) + { + sal_Unicode cSet; + if (!rBox.get_active_text().isEmpty()) + cSet = rBox.get_active_text()[0]; + else + cSet = ' '; + static_cast<SwTOXButton*>(pCtrl)->SetFillChar( cSet ); + } + ModifyHdl(nullptr); +} + +IMPL_LINK(SwTOXEntryTabPage, AutoRightHdl, weld::ToggleButton&, rBox, void) +{ + //the most right style::TabStop is usually right aligned + SwTOXWidget* pCurCtrl = m_xTokenWIN->GetActiveControl(); + OSL_ENSURE(WindowType::EDIT != pCurCtrl->GetType() && + static_cast<SwTOXButton*>(pCurCtrl)->GetFormToken().eTokenType == TOKEN_TAB_STOP, + "no style::TabStop selected!"); + + const SwFormToken& rToken = static_cast<SwTOXButton*>(pCurCtrl)->GetFormToken(); + bool bChecked = rBox.get_active(); + if(rToken.eTokenType == TOKEN_TAB_STOP) + static_cast<SwTOXButton*>(pCurCtrl)->SetTabAlign( + bChecked ? SvxTabAdjust::End : SvxTabAdjust::Left); + m_xTabPosFT->set_sensitive(!bChecked); + m_xTabPosMF->set_sensitive(!bChecked); + ModifyHdl(nullptr); +} + +void SwTOXEntryTabPage::SetWrtShell(SwWrtShell& rSh) +{ + SwDocShell* pDocSh = rSh.GetView().GetDocShell(); + ::FillCharStyleListBox(*m_xCharStyleLB, pDocSh, true, true); + const OUString sDefault(SwResId(STR_POOLCHR_STANDARD)); + for (int i = 0, nCount = m_xCharStyleLB->get_count(); i < nCount; ++i) + { + const OUString sEntry = m_xCharStyleLB->get_text(i); + if(sDefault != sEntry) + { + m_xMainEntryStyleLB->append(m_xCharStyleLB->get_id(i), sEntry); + } + } + m_xMainEntryStyleLB->set_active_text(SwStyleNameMapper::GetUIName( + RES_POOLCHR_IDX_MAIN_ENTRY, OUString())); +} + +static const char* STR_TOKEN_ARY[] = +{ + STR_TOKEN_ENTRY_NO, + STR_TOKEN_ENTRY, //mapped from original STR_TOKEN_ENTRY_TEXT, + STR_TOKEN_ENTRY, + STR_TOKEN_TAB_STOP, + nullptr, + STR_TOKEN_PAGE_NUMS, + STR_TOKEN_CHAPTER_INFO, + STR_TOKEN_LINK_START, + STR_TOKEN_LINK_END, + STR_TOKEN_AUTHORITY +}; + +static const char* STR_TOKEN_HELP_ARY[] = +{ + STR_TOKEN_HELP_ENTRY_NO, + STR_TOKEN_HELP_ENTRY, // mapped from original STR_TOKEN_HELP_ENTRY_TEXT, + STR_TOKEN_HELP_ENTRY, + STR_TOKEN_HELP_TAB_STOP, + STR_TOKEN_HELP_TEXT, + STR_TOKEN_HELP_PAGE_NUMS, + STR_TOKEN_HELP_CHAPTER_INFO, + STR_TOKEN_HELP_LINK_START, + STR_TOKEN_HELP_LINK_END, + STR_TOKEN_HELP_AUTHORITY +}; + +SwTokenWindow::SwTokenWindow(std::unique_ptr<weld::Container> xParent) + : m_pForm(nullptr) + , m_nLevel(0) + , m_bValid(false) + , m_sCharStyle(SwResId(STR_CHARSTYLE)) + , m_pActiveCtrl(nullptr) + , m_pParent(nullptr) + , m_xParentWidget(std::move(xParent)) + , m_xBuilder(Application::CreateBuilder(m_xParentWidget.get(), "modules/swriter/ui/tokenwidget.ui")) + , m_xContainer(m_xBuilder->weld_container("TokenWidget")) + , m_xLeftScrollWin(m_xBuilder->weld_button("left")) + , m_xCtrlParentWin(m_xBuilder->weld_container("ctrl")) + , m_xScrollWin(m_xBuilder->weld_scrolled_window("scrollwin")) + , m_xRightScrollWin(m_xBuilder->weld_button("right")) +{ + m_xScrollWin->connect_hadjustment_changed(LINK(this, SwTokenWindow, ScrollHdl)); + m_xCtrlParentWin->set_size_request(-1, Edit::GetMinimumEditSize().Height()); + m_xCtrlParentWin->connect_size_allocate(LINK(this, SwTokenWindow, AdjustPositionsHdl)); + + for (sal_uInt32 i = 0; i < TOKEN_END; ++i) + { + const char* pTextId = STR_TOKEN_ARY[i]; + if (pTextId) + m_aButtonTexts[i] = SwResId(pTextId); + + const char* pHelpId = STR_TOKEN_HELP_ARY[i]; + m_aButtonHelpTexts[i] = SwResId(pHelpId); + } + + m_sAccessibleName = SwResId(STR_STRUCTURE); + m_sAdditionalAccnameString1 = SwResId(STR_ADDITIONAL_ACCNAME_STRING1); + m_sAdditionalAccnameString2 = SwResId(STR_ADDITIONAL_ACCNAME_STRING2); + m_sAdditionalAccnameString3 = SwResId(STR_ADDITIONAL_ACCNAME_STRING3); + + Link<weld::Button&,void> aLink(LINK(this, SwTokenWindow, ScrollBtnHdl)); + m_xLeftScrollWin->connect_clicked(aLink); + m_xRightScrollWin->connect_clicked(aLink); +} + +SwTokenWindow::~SwTokenWindow() +{ +} + +void SwTokenWindow::SetForm(SwForm& rForm, sal_uInt16 nL) +{ + SetActiveControl(nullptr); + m_bValid = true; + + if (m_pForm) + { + //apply current level settings to the form + m_aControlList.clear(); + } + + m_nLevel = nL; + m_pForm = &rForm; + //now the display + if(m_nLevel < MAXLEVEL || rForm.GetTOXType() == TOX_AUTHORITIES) + { + // #i21237# + SwFormTokens aPattern = m_pForm->GetPattern(m_nLevel + 1); + bool bLastWasText = false; //assure alternating text - code - text + + SwTOXWidget* pSetActiveControl = nullptr; + for (const auto& aToken : aPattern) // #i21237# + { + if(TOKEN_TEXT == aToken.eTokenType) + { + SAL_WARN_IF(bLastWasText, "sw", "text following text is invalid"); + SwTOXWidget* pCtrl = InsertItem(aToken.sText, aToken); + bLastWasText = true; + if (!GetActiveControl()) + SetActiveControl(pCtrl); + } + else + { + if( !bLastWasText ) + { + SwFormToken aTemp(TOKEN_TEXT); + SwTOXWidget* pCtrl = InsertItem(OUString(), aTemp); + if(!pSetActiveControl) + pSetActiveControl = pCtrl; + } + + OUString sForm; + switch( aToken.eTokenType ) + { + case TOKEN_ENTRY_NO: sForm = SwForm::GetFormEntryNum(); break; + case TOKEN_ENTRY_TEXT: sForm = SwForm::GetFormEntryText(); break; + case TOKEN_ENTRY: sForm = SwForm::GetFormEntry(); break; + case TOKEN_TAB_STOP: sForm = SwForm::GetFormTab(); break; + case TOKEN_PAGE_NUMS: sForm = SwForm::GetFormPageNums(); break; + case TOKEN_CHAPTER_INFO: sForm = SwForm::GetFormChapterMark(); break; + case TOKEN_LINK_START: sForm = SwForm::GetFormLinkStt(); break; + case TOKEN_LINK_END: sForm = SwForm::GetFormLinkEnd(); break; + case TOKEN_AUTHORITY: sForm = SwForm::GetFormAuth(); break; + default:; //prevent warning + } + + InsertItem( sForm, aToken ); + bLastWasText = false; + } + } + if(!bLastWasText) + { + SwFormToken aTemp(TOKEN_TEXT); + SwTOXWidget* pCtrl = InsertItem(OUString(), aTemp); + if(!pSetActiveControl) + pSetActiveControl = pCtrl; + } + SetActiveControl(pSetActiveControl); + } + AdjustScrolling(); +} + +void SwTokenWindow::SetActiveControl(SwTOXWidget* pSet) +{ + if (pSet != m_pActiveCtrl) + { + m_pActiveCtrl = pSet; + if( m_pActiveCtrl ) + { + m_pActiveCtrl->GrabFocus(); + //it must be a SwTOXEdit + const SwFormToken* pFToken; + if( WindowType::EDIT == m_pActiveCtrl->GetType() ) + pFToken = &static_cast<SwTOXEdit*>(m_pActiveCtrl)->GetFormToken(); + else + pFToken = &static_cast<SwTOXButton*>(m_pActiveCtrl)->GetFormToken(); + + SwFormToken aTemp( *pFToken ); + m_aButtonSelectedHdl.Call( aTemp ); + } + } +} + +SwTOXWidget* SwTokenWindow::InsertItem(const OUString& rText, const SwFormToken& rToken) +{ + SwTOXWidget* pRet = nullptr; + + if (TOKEN_TEXT == rToken.eTokenType) + { + SwTOXEdit* pEdit = new SwTOXEdit(this, rToken); + pEdit->set_grid_left_attach(m_aControlList.size()); + + m_aControlList.emplace_back(pEdit); + + pEdit->SetText(rText); + sal_uInt32 nIndex = GetControlIndex( TOKEN_TEXT ); + OUString strName(m_sAccessibleName + OUString::number(nIndex)); + if ( nIndex == 1 ) + { + /*Press left or right arrow to choose the structure controls*/ + strName += " (" + m_sAdditionalAccnameString2 + ", " + /*Press Ctrl+Alt+A to move focus for more operations*/ + + m_sAdditionalAccnameString1 + ", " + /*Press Ctrl+Alt+B to move focus back to the current structure control*/ + + m_sAdditionalAccnameString3 + ")"; + } + pEdit->SetAccessibleName(strName); + pEdit->AdjustSize(); + pEdit->SetModifyHdl(LINK(this, SwTokenWindow, EditResize )); + pEdit->SetPrevNextLink(LINK(this, SwTokenWindow, NextItemHdl)); + pEdit->SetGetFocusHdl(LINK(this, SwTokenWindow, TbxFocusHdl)); + pEdit->Show(); + pRet = pEdit; + } + else + { + SwTOXButton* pButton = new SwTOXButton(this, rToken); + pButton->set_grid_left_attach(m_aControlList.size()); + + m_aControlList.emplace_back(pButton); + + pButton->SetPrevNextLink(LINK(this, SwTokenWindow, NextItemBtnHdl)); + pButton->SetGetFocusHdl(LINK(this, SwTokenWindow, TbxFocusBtnHdl)); + + if(TOKEN_AUTHORITY != rToken.eTokenType) + pButton->SetText(m_aButtonTexts[rToken.eTokenType]); + else + { + //use the first two chars as symbol + OUString sTmp(SwAuthorityFieldType::GetAuthFieldName( + static_cast<ToxAuthorityField>(rToken.nAuthorityField))); + pButton->SetText(sTmp.copy(0, 2)); + } + + sal_uInt32 nIndex = GetControlIndex( rToken.eTokenType ); + OUString sAccName = m_aButtonHelpTexts[rToken.eTokenType]; + if ( nIndex ) + { + sAccName += " " + OUString::number(nIndex); + } + pButton->SetAccessibleName( sAccName ); + + pButton->Show(); + pRet = pButton; + } + + return pRet; +} + +void SwTokenWindow::InsertAtSelection(const SwFormToken& rToken) +{ + OSL_ENSURE(m_pActiveCtrl, "no active control!"); + + if(!m_pActiveCtrl) + return; + + SwFormToken aToInsertToken(rToken); + + if(TOKEN_LINK_START == aToInsertToken.eTokenType) + { + //determine if start or end of hyperlink is appropriate + //eventually change a following link start into a link end + // groups of LS LE should be ignored + // <insert> + //LS <insert> + //LE <insert> + //<insert> LS + //<insert> LE + //<insert> + bool bPreStartLinkFound = false; + bool bPreEndLinkFound = false; + + const SwTOXWidget* pControl = nullptr; + const SwTOXWidget* pExchange = nullptr; + + auto it = m_aControlList.cbegin(); + for( ; it != m_aControlList.cend() && m_pActiveCtrl != it->get(); ++it ) + { + pControl = it->get(); + + if( WindowType::EDIT != pControl->GetType()) + { + const SwFormToken& rNewToken = + static_cast<const SwTOXButton*>(pControl)->GetFormToken(); + + if( TOKEN_LINK_START == rNewToken.eTokenType ) + { + bPreStartLinkFound = true; + pExchange = nullptr; + } + else if(TOKEN_LINK_END == rNewToken.eTokenType) + { + if( bPreStartLinkFound ) + bPreStartLinkFound = false; + else + { + bPreEndLinkFound = false; + pExchange = pControl; + } + } + } + } + + bool bPostLinkStartFound = false; + + if(!bPreStartLinkFound && !bPreEndLinkFound) + { + for( ; it != m_aControlList.cend(); ++it ) + { + pControl = it->get(); + + if( pControl != m_pActiveCtrl && + WindowType::EDIT != pControl->GetType()) + { + const SwFormToken& rNewToken = + static_cast<const SwTOXButton*>(pControl)->GetFormToken(); + + if( TOKEN_LINK_START == rNewToken.eTokenType ) + { + if(bPostLinkStartFound) + break; + bPostLinkStartFound = true; + pExchange = pControl; + } + else if(TOKEN_LINK_END == rNewToken.eTokenType ) + { + if(bPostLinkStartFound) + { + bPostLinkStartFound = false; + pExchange = nullptr; + } + break; + } + } + } + } + + if(bPreStartLinkFound) + { + aToInsertToken.eTokenType = TOKEN_LINK_END; + aToInsertToken.sText = m_aButtonTexts[TOKEN_LINK_END]; + } + + if(bPostLinkStartFound) + { + OSL_ENSURE(pExchange, "no control to exchange?"); + if(pExchange) + { + const_cast<SwTOXButton*>(static_cast<const SwTOXButton*>(pExchange))->SetLinkEnd(); + const_cast<SwTOXButton*>(static_cast<const SwTOXButton*>(pExchange))->SetText(m_aButtonTexts[TOKEN_LINK_END]); + } + } + + if(bPreEndLinkFound) + { + OSL_ENSURE(pExchange, "no control to exchange?"); + + if(pExchange) + { + const_cast<SwTOXButton*>(static_cast<const SwTOXButton*>(pExchange))->SetLinkStart(); + const_cast<SwTOXButton*>(static_cast<const SwTOXButton*>(pExchange))->SetText(m_aButtonTexts[TOKEN_LINK_START]); + } + } + } + + //if the active control is text then insert a new button at the selection + //else replace the button + auto iterActive = std::find_if(m_aControlList.begin(), m_aControlList.end(), + [this](const auto& rControl) + { + SwTOXWidget* pCtrl = rControl.get(); + return pCtrl == m_pActiveCtrl; + }); + + assert(iterActive != m_aControlList.end()); + if (iterActive == m_aControlList.end()) + return; + + if (WindowType::EDIT == m_pActiveCtrl->GetType()) + { + ++iterActive; + + int nStartPos, nEndPos; + static_cast<SwTOXEdit*>(m_pActiveCtrl)->get_selection_bounds(nStartPos, nEndPos); + + const OUString sEditText = static_cast<SwTOXEdit*>(m_pActiveCtrl)->GetText(); + const OUString sLeft = sEditText.copy( 0, std::min(nStartPos, nEndPos) ); + const OUString sRight = sEditText.copy( std::max(nStartPos, nEndPos) ); + + static_cast<SwTOXEdit*>(m_pActiveCtrl)->SetText(sLeft); + static_cast<SwTOXEdit*>(m_pActiveCtrl)->AdjustSize(); + + SwFormToken aTmpToken(TOKEN_TEXT); + SwTOXEdit* pEdit = new SwTOXEdit(this, aTmpToken); + iterActive = m_aControlList.emplace(iterActive, pEdit); + + pEdit->SetText(sRight); + sal_uInt32 nIndex = GetControlIndex( TOKEN_TEXT ); + OUString strName(m_sAccessibleName + OUString::number(nIndex)); + if ( nIndex == 1) + { + /*Press left or right arrow to choose the structure controls*/ + strName += " (" + m_sAdditionalAccnameString2 + ", " + /*Press Ctrl+Alt+A to move focus for more operations*/ + + m_sAdditionalAccnameString1 + ", " + /*Press Ctrl+Alt+B to move focus back to the current structure control*/ + + m_sAdditionalAccnameString3 + ")"; + } + pEdit->SetAccessibleName(strName); + pEdit->AdjustSize(); + pEdit->SetModifyHdl(LINK(this, SwTokenWindow, EditResize )); + pEdit->SetPrevNextLink(LINK(this, SwTokenWindow, NextItemHdl)); + pEdit->SetGetFocusHdl(LINK(this, SwTokenWindow, TbxFocusHdl)); + pEdit->Show(); + } + else + { + m_pActiveCtrl->Hide(); + m_pActiveCtrl = nullptr; + iterActive = m_aControlList.erase(iterActive); + } + + //now the new button + SwTOXButton* pButton = new SwTOXButton(this, aToInsertToken); + m_aControlList.emplace(iterActive, pButton); + + pButton->SetPrevNextLink(LINK(this, SwTokenWindow, NextItemBtnHdl)); + pButton->SetGetFocusHdl(LINK(this, SwTokenWindow, TbxFocusBtnHdl)); + + if (TOKEN_AUTHORITY != aToInsertToken.eTokenType) + { + pButton->SetText(m_aButtonTexts[aToInsertToken.eTokenType]); + } + else + { + //use the first two chars as symbol + OUString sTmp(SwAuthorityFieldType::GetAuthFieldName( + static_cast<ToxAuthorityField>(aToInsertToken.nAuthorityField))); + pButton->SetText(sTmp.copy(0, 2)); + } + + pButton->Check(); + pButton->Show(); + SetActiveControl(pButton); + + AdjustPositions(); +} + +void SwTokenWindow::RemoveControl(const SwTOXButton* pDel, bool bInternalCall) +{ + if (bInternalCall && TOX_AUTHORITIES == m_pForm->GetTOXType()) + m_pParent->PreTokenButtonRemoved(pDel->GetFormToken()); + + auto it = std::find_if(m_aControlList.begin(), m_aControlList.end(), + [pDel](const auto& rControl) + { + SwTOXWidget* pCtrl = rControl.get(); + return pCtrl == pDel; + }); + assert(it != m_aControlList.end()); //Control does not exist! + if (it == m_aControlList.end()) + return; + + // the two neighbours of the box must be merged + // the properties of the right one will be lost + assert(it != m_aControlList.begin() && it != m_aControlList.end() - 1); //Button at first or last position? + if (it == m_aControlList.begin() || it == m_aControlList.end() - 1) + return; + + auto itLeft = it, itRight = it; + --itLeft; + ++itRight; + SwTOXWidget* pLeftEdit = itLeft->get(); + SwTOXWidget* pRightEdit = itRight->get(); + + static_cast<SwTOXEdit*>(pLeftEdit)->SetText(static_cast<SwTOXEdit*>(pLeftEdit)->GetText() + + static_cast<SwTOXEdit*>(pRightEdit)->GetText()); + static_cast<SwTOXEdit*>(pLeftEdit)->AdjustSize(); + + m_pActiveCtrl->Hide(); + m_pActiveCtrl = nullptr; + + m_aControlList.erase(itRight); + m_aControlList.erase(it); + + SetActiveControl(pLeftEdit); + AdjustPositions(); + m_aModifyHdl.Call(nullptr); +} + +IMPL_LINK_NOARG(SwTokenWindow, AdjustPositionsHdl, const Size&, void) +{ + AdjustScrolling(); +} + +void SwTokenWindow::AdjustPositions() +{ + for (size_t i = 0; i < m_aControlList.size(); ++i) + m_aControlList[i]->set_grid_left_attach(i); + AdjustScrolling(); +} + +void SwTokenWindow::MoveControls(long nOffset) +{ + m_xScrollWin->hadjustment_set_value(nOffset); +} + +IMPL_LINK_NOARG(SwTokenWindow, ScrollHdl, weld::ScrolledWindow&, void) +{ + AdjustScrolling(); +} + +void SwTokenWindow::AdjustScrolling() +{ + if (m_aControlList.size() > 1) + { + //validate scroll buttons + + auto nLeft = m_xScrollWin->hadjustment_get_value(); + auto nSpace = m_xScrollWin->hadjustment_get_page_size(); + auto nWidth = m_xScrollWin->hadjustment_get_upper(); + + bool bEnable = nWidth > nSpace; + + //the active control must be visible + if (bEnable && m_pActiveCtrl) + { + int x, y, width, height; + m_pActiveCtrl->get_extents_relative_to(*m_xCtrlParentWin, x, y, width, height); + + if (x < nLeft || x + width > nLeft + nSpace) + { + MoveControls(x); + nLeft = x; + } + + m_xLeftScrollWin->set_sensitive(nLeft > 0); + m_xRightScrollWin->set_sensitive(nLeft + nSpace < nWidth); + } + else + { + //if the control fits into the space then the first control must be at position 0 + m_xRightScrollWin->set_sensitive(false); + m_xLeftScrollWin->set_sensitive(false); + } + } +} + +IMPL_LINK(SwTokenWindow, ScrollBtnHdl, weld::Button&, rBtn, void) +{ + if (m_aControlList.empty()) + return; + + const auto nSpace = m_xScrollWin->hadjustment_get_page_size(); + const auto nWidth = m_xScrollWin->hadjustment_get_upper(); + const auto nLeft = m_xScrollWin->hadjustment_get_value(); + + long nMove = nLeft; + if (&rBtn == m_xLeftScrollWin.get()) + { + //find the first completely visible control (left edge visible) + auto it = std::find_if(m_aControlList.begin(), m_aControlList.end(), + [this, nLeft](const auto& rControl) + { + SwTOXWidget* pCtrl = rControl.get(); + + int x, y, width, height; + pCtrl->get_extents_relative_to(*m_xCtrlParentWin, x, y, width, height); + + return x >= nLeft; + }); + if (it != m_aControlList.end()) + { + if (it == m_aControlList.begin()) + { + nMove = 0; + } + else + { + //move the left neighbor to the start position + auto itLeft = it; + --itLeft; + SwTOXWidget* pLeft = itLeft->get(); + + int x, y, width, height; + pLeft->get_extents_relative_to(*m_xCtrlParentWin, x, y, width, height); + + nMove = x; + } + } + } + else + { + //find the first completely visible control (right edge visible) + auto it = std::find_if(m_aControlList.rbegin(), m_aControlList.rend(), + [this, nLeft, nSpace](const auto& rControl) { + SwTOXWidget* pCtrl = rControl.get(); + + int x, y, width, height; + pCtrl->get_extents_relative_to(*m_xCtrlParentWin, x, y, width, height); + + auto nXPos = x + width; + return nXPos <= nLeft + nSpace; + }); + if (it != m_aControlList.rend() && it != m_aControlList.rbegin()) + { + //move the right neighbor to the right edge right aligned + auto itRight = it; + --itRight; + SwTOXWidget* pRight = itRight->get(); + + int x, y, width, height; + pRight->get_extents_relative_to(*m_xCtrlParentWin, x, y, width, height); + + nMove = x + width - nSpace; + } + + //move it left until it's completely visible + } + + if (nMove != nLeft) + { + // move the complete list + MoveControls(nMove); + m_xLeftScrollWin->set_sensitive(nMove > 0); + m_xRightScrollWin->set_sensitive(nMove + nSpace < nWidth); + } +} + +OUString SwTokenWindow::GetPattern() const +{ + OUStringBuffer sRet; + + for (const auto& elem : m_aControlList) + { + const SwTOXWidget* pCtrl = elem.get(); + + const SwFormToken &rNewToken = pCtrl->GetType() == WindowType::EDIT + ? const_cast<SwTOXEdit*>(static_cast<const SwTOXEdit*>(pCtrl))->GetFormToken() + : static_cast<const SwTOXButton*>(pCtrl)->GetFormToken(); + + //TODO: prevent input of TOX_STYLE_DELIMITER in KeyInput + sRet.append(rNewToken.GetString()); + } + + return sRet.makeStringAndClear(); +} + +// Check if a control of the specified TokenType is already contained in the list +bool SwTokenWindow::Contains(FormTokenType eSearchFor) const +{ + bool bRet = false; + + for (const auto& elem : m_aControlList) + { + const SwTOXWidget* pCtrl = elem.get(); + const SwFormToken &rNewToken = pCtrl->GetType() == WindowType::EDIT + ? const_cast<SwTOXEdit*>(static_cast<const SwTOXEdit*>(pCtrl))->GetFormToken() + : static_cast<const SwTOXButton*>(pCtrl)->GetFormToken(); + + if (eSearchFor == rNewToken.eTokenType) + { + bRet = true; + break; + } + } + + return bRet; +} + +OUString SwTokenWindow::CreateQuickHelp(const SwFormToken& rToken) +{ + OUString sEntry; + if (rToken.eTokenType != TOKEN_AUTHORITY) + sEntry = m_aButtonHelpTexts[rToken.eTokenType]; + else + { + sEntry += SwAuthorityFieldType::GetAuthFieldName( + static_cast<ToxAuthorityField>(rToken.nAuthorityField)); + } + + if (rToken.eTokenType != TOKEN_TAB_STOP) + { + if (!rToken.sCharStyleName.isEmpty()) + { + sEntry += " " + m_sCharStyle + rToken.sCharStyleName; + } + } + + return sEntry; +} + +IMPL_LINK(SwTokenWindow, EditResize, SwTOXEdit&, rEdit, void) +{ + rEdit.AdjustSize(); + AdjustPositions(); + m_aModifyHdl.Call(nullptr); +} + +IMPL_LINK(SwTokenWindow, NextItemHdl, SwTOXEdit&, rEdit, void) +{ + auto it = std::find_if(m_aControlList.begin(), m_aControlList.end(), + [&rEdit](const auto& rControl) + { + SwTOXWidget* pCtrl = rControl.get(); + return pCtrl == &rEdit; + }); + + if (it == m_aControlList.end()) + return; + + auto itTest = it; + ++itTest; + + if ((it != m_aControlList.begin() && !rEdit.IsNextControl()) || + (itTest != m_aControlList.end() && rEdit.IsNextControl())) + { + auto iterFocus = it; + rEdit.IsNextControl() ? ++iterFocus : --iterFocus; + + SwTOXWidget *pCtrlFocus = iterFocus->get(); + pCtrlFocus->GrabFocus(); + static_cast<SwTOXButton*>(pCtrlFocus)->Check(); + + AdjustScrolling(); + } +} + +IMPL_LINK(SwTokenWindow, TbxFocusHdl, SwTOXWidget&, rControl, void) +{ + SwTOXEdit* pEdit = static_cast<SwTOXEdit*>(&rControl); + for (const auto& aControl : m_aControlList) + { + SwTOXWidget* pCtrl = aControl.get(); + if (pCtrl && pCtrl->GetType() != WindowType::EDIT) + static_cast<SwTOXButton*>(pCtrl)->Check(false); + } + + SetActiveControl(pEdit); +} + +IMPL_LINK(SwTokenWindow, NextItemBtnHdl, SwTOXButton&, rBtn, void ) +{ + auto it = std::find_if(m_aControlList.begin(), m_aControlList.end(), + [&rBtn](const auto& rControl) + { + SwTOXWidget* pCtrl = rControl.get(); + return pCtrl == &rBtn; + }); + + if (it == m_aControlList.end()) + return; + + auto itTest = it; + ++itTest; + + if (!rBtn.IsNextControl() || (itTest != m_aControlList.end() && rBtn.IsNextControl())) + { + bool isNext = rBtn.IsNextControl(); + + auto iterFocus = it; + isNext ? ++iterFocus : --iterFocus; + + SwTOXWidget* pCtrlFocus = iterFocus->get(); + pCtrlFocus->GrabFocus(); + int nStartPos(0), nEndPos(0); + + if (!isNext) + { + const sal_Int32 nLen = static_cast<SwTOXEdit*>(pCtrlFocus)->GetText().getLength(); + + nStartPos = nLen; + nEndPos = nLen; + } + + static_cast<SwTOXEdit*>(pCtrlFocus)->select_region(nStartPos, nEndPos); + + rBtn.Check(false); + + AdjustScrolling(); + } +} + +IMPL_LINK(SwTokenWindow, TbxFocusBtnHdl, SwTOXWidget&, rControl, void) +{ + SwTOXButton* pBtn = static_cast<SwTOXButton*>(&rControl); + for (const auto& aControl : m_aControlList) + { + SwTOXWidget* pControl = aControl.get(); + + if (pControl && WindowType::EDIT != pControl->GetType()) + static_cast<SwTOXButton*>(pControl)->Check(pBtn == pControl); + } + + SetActiveControl(pBtn); +} + +void SwTokenWindow::SetFocus2theAllBtn() +{ + if (m_pParent) + { + m_pParent->SetFocus2theAllBtn(); + } +} + +sal_uInt32 SwTokenWindow::GetControlIndex(FormTokenType eType) const +{ + //there are only one entry-text button and only one page-number button, + //so we need not add index for these two buttons. + if ( eType == TOKEN_ENTRY_TEXT || eType == TOKEN_PAGE_NUMS ) + { + return 0; + } + + sal_uInt32 nIndex = 0; + for (const auto& elem : m_aControlList) + { + const SwTOXWidget* pControl = elem.get(); + + const SwFormToken& rNewToken = WindowType::EDIT == pControl->GetType() + ? const_cast<SwTOXEdit*>(static_cast<const SwTOXEdit*>(pControl))->GetFormToken() + : static_cast<const SwTOXButton*>(pControl)->GetFormToken(); + + if(eType == rNewToken.eTokenType) + { + ++nIndex; + } + } + + return nIndex; +} + +SwTOXStylesTabPage::SwTOXStylesTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/tocstylespage.ui", "TocStylesPage", &rAttrSet) + , m_xLevelLB(m_xBuilder->weld_tree_view("levels")) + , m_xAssignBT(m_xBuilder->weld_button("assign")) + , m_xParaLayLB(m_xBuilder->weld_tree_view("styles")) + , m_xStdBT(m_xBuilder->weld_button("default")) + , m_xEditStyleBT(m_xBuilder->weld_button("edit")) +{ + m_xParaLayLB->make_sorted(); + auto nHeight = m_xLevelLB->get_height_rows(16); + m_xLevelLB->set_size_request(-1, nHeight); + m_xParaLayLB->set_size_request(-1, nHeight); + + SetExchangeSupport(); + + m_xEditStyleBT->connect_clicked(LINK(this, SwTOXStylesTabPage, EditStyleHdl)); + m_xAssignBT->connect_clicked(LINK(this, SwTOXStylesTabPage, AssignHdl)); + m_xStdBT->connect_clicked(LINK(this, SwTOXStylesTabPage, StdHdl)); + m_xParaLayLB->connect_changed(LINK(this, SwTOXStylesTabPage, EnableSelectHdl)); + m_xLevelLB->connect_changed(LINK(this, SwTOXStylesTabPage, EnableSelectHdl)); + m_xParaLayLB->connect_row_activated(LINK(this, SwTOXStylesTabPage, DoubleClickHdl)); +} + +SwTOXStylesTabPage::~SwTOXStylesTabPage() +{ +} + +bool SwTOXStylesTabPage::FillItemSet( SfxItemSet* ) +{ + return true; +} + +void SwTOXStylesTabPage::Reset( const SfxItemSet* rSet ) +{ + ActivatePage(*rSet); +} + +void SwTOXStylesTabPage::ActivatePage( const SfxItemSet& ) +{ + m_pCurrentForm.reset(new SwForm(GetForm())); + + // not hyperlink for user directories + const sal_uInt16 nSize = m_pCurrentForm->GetFormMax(); + + // display form pattern without title + + m_xLevelLB->freeze(); + m_xLevelLB->clear(); + // display 1st TemplateEntry + OUString aStr( SwResId( STR_TITLE )); + if( !m_pCurrentForm->GetTemplate( 0 ).isEmpty() ) + { + aStr += " " + OUStringChar(aDeliStart) + + m_pCurrentForm->GetTemplate( 0 ) + + OUStringChar(aDeliEnd); + } + m_xLevelLB->append_text(aStr); + + for( sal_uInt16 i=1; i < nSize; ++i ) + { + if( TOX_INDEX == m_pCurrentForm->GetTOXType() && + FORM_ALPHA_DELIMITER == i ) + { + aStr = SwResId(STR_ALPHA); + } + else + { + aStr = SwResId(STR_LEVEL) + OUString::number( + TOX_INDEX == m_pCurrentForm->GetTOXType() ? i - 1 : i ); + } + if( !m_pCurrentForm->GetTemplate( i ).isEmpty() ) + { + aStr += " " + OUStringChar(aDeliStart) + + m_pCurrentForm->GetTemplate( i ) + + OUStringChar(aDeliEnd); + } + m_xLevelLB->append_text(aStr); + } + m_xLevelLB->thaw(); + + // initialise templates + SwWrtShell& rSh = static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(); + const sal_uInt16 nSz = rSh.GetTextFormatCollCount(); + + m_xParaLayLB->freeze(); + m_xParaLayLB->clear(); + for( sal_uInt16 i = 0; i < nSz; ++i ) + { + const SwTextFormatColl *pColl = &rSh.GetTextFormatColl( i ); + if( !pColl->IsDefault() ) + m_xParaLayLB->append_text( pColl->GetName() ); + } + + // query pool collections and set them for the directory + for( sal_uInt16 i = 0; i < m_pCurrentForm->GetFormMax(); ++i ) + { + aStr = m_pCurrentForm->GetTemplate( i ); + if (!aStr.isEmpty() && m_xParaLayLB->find_text(aStr) == -1) + m_xParaLayLB->append_text(aStr); + } + m_xParaLayLB->thaw(); + + EnableSelectHdl(*m_xParaLayLB); +} + +DeactivateRC SwTOXStylesTabPage::DeactivatePage( SfxItemSet* /*pSet*/ ) +{ + GetForm() = *m_pCurrentForm; + return DeactivateRC::LeavePage; +} + +std::unique_ptr<SfxTabPage> SwTOXStylesTabPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwTOXStylesTabPage>(pPage, pController, *rAttrSet); +} + +IMPL_LINK_NOARG(SwTOXStylesTabPage, EditStyleHdl, weld::Button&, void) +{ + if (m_xParaLayLB->get_selected_index() != -1) + { + SfxStringItem aStyle(SID_STYLE_EDIT, m_xParaLayLB->get_selected_text()); + SfxUInt16Item aFamily(SID_STYLE_FAMILY, sal_uInt16(SfxStyleFamily::Para)); + SwWrtShell& rSh = static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(); + rSh.GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_STYLE_EDIT, + SfxCallMode::SYNCHRON, + { &aStyle, &aFamily }); + } +} + +// allocate templates +IMPL_LINK_NOARG(SwTOXStylesTabPage, AssignHdl, weld::Button&, void) +{ + auto nLevPos = m_xLevelLB->get_selected_index(); + auto nTemplPos = m_xParaLayLB->get_selected_index(); + if (nLevPos != -1 && nTemplPos != -1) + { + const OUString aStr(m_xLevelLB->get_text(nLevPos).getToken(0, aDeliStart) + + OUStringChar(aDeliStart) + + m_xParaLayLB->get_selected_text() + + OUStringChar(aDeliEnd)); + + m_pCurrentForm->SetTemplate(nLevPos, m_xParaLayLB->get_selected_text()); + + m_xLevelLB->remove(nLevPos); + m_xLevelLB->insert_text(nLevPos, aStr); + m_xLevelLB->select_text(aStr); + Modify(); + } +} + +IMPL_LINK_NOARG(SwTOXStylesTabPage, StdHdl, weld::Button&, void) +{ + const auto nPos = m_xLevelLB->get_selected_index(); + if (nPos != -1) + { + const OUString aStr(m_xLevelLB->get_text(nPos).getToken(0, aDeliStart)); + m_xLevelLB->remove(nPos); + m_xLevelLB->insert_text(nPos, aStr); + m_xLevelLB->select_text(aStr); + m_pCurrentForm->SetTemplate(nPos, OUString()); + Modify(); + } +} + +IMPL_LINK_NOARG(SwTOXStylesTabPage, DoubleClickHdl, weld::TreeView&, bool) +{ + const OUString aTmpName(m_xParaLayLB->get_selected_text()); + SwWrtShell& rSh = static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(); + + if(m_xParaLayLB->get_selected_index() != -1 && + (m_xLevelLB->get_selected_index() == 0 || SwMultiTOXTabDialog::IsNoNum(rSh, aTmpName))) + AssignHdl(*m_xAssignBT); + + return true; +} + +// enable only when selected +IMPL_LINK_NOARG(SwTOXStylesTabPage, EnableSelectHdl, weld::TreeView&, void) +{ + m_xStdBT->set_sensitive(m_xLevelLB->get_selected_index() != -1); + + SwWrtShell& rSh = static_cast<SwMultiTOXTabDialog*>(GetDialogController())->GetWrtShell(); + const OUString aTmpName(m_xParaLayLB->get_selected_text()); + m_xAssignBT->set_sensitive(m_xParaLayLB->get_selected_index() != -1 && + m_xLevelLB->get_selected_index() != -1 && + (m_xLevelLB->get_selected_index() == 0 || SwMultiTOXTabDialog::IsNoNum(rSh, aTmpName))); + m_xEditStyleBT->set_sensitive(m_xParaLayLB->get_selected_index() != -1); +} + +void SwTOXStylesTabPage::Modify() +{ + SwMultiTOXTabDialog* pTOXDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + if (pTOXDlg) + { + GetForm() = *m_pCurrentForm; + pTOXDlg->CreateOrUpdateExample(pTOXDlg->GetCurrentTOXType().eType, TOX_PAGE_STYLES); + } +} + +#define ITEM_SEARCH 1 +#define ITEM_ALTERNATIVE 2 +#define ITEM_PRIM_KEY 3 +#define ITEM_SEC_KEY 4 +#define ITEM_COMMENT 5 +#define ITEM_CASE 6 +#define ITEM_WORDONLY 7 + +SwEntryBrowseBox::SwEntryBrowseBox(const css::uno::Reference<css::awt::XWindow> &rParent) + : SwEntryBrowseBox_Base(VCLUnoHelper::GetWindow(rParent), EditBrowseBoxFlags::NONE, WB_TABSTOP | WB_BORDER, + BrowserMode::KEEPHIGHLIGHT | + BrowserMode::COLUMNSELECTION | + BrowserMode::MULTISELECTION | + BrowserMode::TRACKING_TIPS | + BrowserMode::HLINES | + BrowserMode::VLINES | + BrowserMode::AUTO_VSCROLL| + BrowserMode::HIDECURSOR ) + , m_aCellEdit(VclPtr<Edit>::Create(&GetDataWindow(), 0)) + , m_aCellCheckBox(VclPtr< ::svt::CheckBoxControl>::Create(&GetDataWindow())) + , m_nCurrentRow(0) + , m_bModified(false) +{ + OUString sSearch = SwResId(STR_AUTOMARK_SEARCHTERM); + OUString sAlternative = SwResId(STR_AUTOMARK_ALTERNATIVE); + OUString sPrimKey = SwResId(STR_AUTOMARK_KEY1); + OUString sSecKey = SwResId(STR_AUTOMARK_KEY2); + OUString sComment = SwResId(STR_AUTOMARK_COMMENT); + OUString sCaseSensitive = SwResId(STR_AUTOMARK_CASESENSITIVE); + OUString sWordOnly = SwResId(STR_AUTOMARK_WORDONLY); + m_sYes = SwResId(STR_AUTOMARK_YES); + m_sNo = SwResId(STR_AUTOMARK_NO); + + m_aCellCheckBox->GetBox().EnableTriState(false); + m_xController = new ::svt::EditCellController(m_aCellEdit.get()); + m_xCheckController = new ::svt::CheckBoxCellController(m_aCellCheckBox.get()); + + // HACK: BrowseBox doesn't invalidate its children, how it should be. + // That's why WB_CLIPCHILDREN is reset in order to enforce the + // children' invalidation + WinBits aStyle = GetStyle(); + if( aStyle & WB_CLIPCHILDREN ) + { + aStyle &= ~WB_CLIPCHILDREN; + SetStyle( aStyle ); + } + + const OUString* aTitles[7] = + { + &sSearch, + &sAlternative, + &sPrimKey, + &sSecKey, + &sComment, + &sCaseSensitive, + &sWordOnly + }; + + long nWidth = GetSizePixel().Width(); + nWidth /=7; + --nWidth; + for(sal_uInt16 i = 1; i < 8; i++) + InsertDataColumn( i, *aTitles[i - 1], nWidth ); +} + +SwEntryBrowseBox::~SwEntryBrowseBox() +{ + disposeOnce(); +} + +void SwEntryBrowseBox::dispose() +{ + m_aCellEdit.disposeAndClear(); + m_aCellCheckBox.disposeAndClear(); + SwEntryBrowseBox_Base::dispose(); +} + +void SwEntryBrowseBox::Resize() +{ + SwEntryBrowseBox_Base::Resize(); + + long nWidth = GetSizePixel().Width(); + std::vector<long> aWidths = GetOptimalColWidths(); + long nNaturalWidth(std::accumulate(aWidths.begin(), aWidths.end(), 0)); + long nExcess = ((nWidth - nNaturalWidth) / aWidths.size()) - 1; + + for (size_t i = 0; i < aWidths.size(); ++i) + SetColumnWidth(i+1, aWidths[i] + nExcess); +} + +std::vector<long> SwEntryBrowseBox::GetOptimalColWidths() const +{ + std::vector<long> aWidths; + + long nStandardColMinWidth = approximate_digit_width() * 15; + long nYesNoWidth = approximate_digit_width() * 5; + nYesNoWidth = std::max(nYesNoWidth, GetTextWidth(m_sYes)); + nYesNoWidth = std::max(nYesNoWidth, GetTextWidth(m_sNo)); + for (sal_uInt16 i = 1; i < 6; i++) + { + long nColWidth = std::max(nStandardColMinWidth, + GetTextWidth(GetColumnTitle(i))); + nColWidth += 12; + aWidths.push_back(nColWidth); + } + + for (sal_uInt16 i = 6; i < 8; i++) + { + long nColWidth = std::max(nYesNoWidth, + GetTextWidth(GetColumnTitle(i))); + nColWidth += 12; + aWidths.push_back(nColWidth); + } + + return aWidths; +} + +Size SwEntryBrowseBox::GetOptimalSize() const +{ + Size aSize = LogicToPixel(Size(276 , 175), MapMode(MapUnit::MapAppFont)); + + std::vector<long> aWidths = GetOptimalColWidths(); + + long nWidth(std::accumulate(aWidths.begin(), aWidths.end(), 0)); + + aSize.setWidth( std::max(aSize.Width(), nWidth) ); + + return aSize; +} + +bool SwEntryBrowseBox::SeekRow( long nRow ) +{ + m_nCurrentRow = nRow; + return true; +} + +OUString SwEntryBrowseBox::GetCellText(long nRow, sal_uInt16 nColumn) const +{ + OUString pRet; + if (o3tl::make_unsigned(nRow) < m_Entries.size()) + { + const AutoMarkEntry* pEntry = m_Entries[ nRow ].get(); + switch(nColumn) + { + case ITEM_SEARCH : pRet = pEntry->sSearch; break; + case ITEM_ALTERNATIVE : pRet = pEntry->sAlternative; break; + case ITEM_PRIM_KEY : pRet = pEntry->sPrimKey; break; + case ITEM_SEC_KEY : pRet = pEntry->sSecKey; break; + case ITEM_COMMENT : pRet = pEntry->sComment; break; + case ITEM_CASE : pRet = pEntry->bCase ? m_sYes : m_sNo; break; + case ITEM_WORDONLY : pRet = pEntry->bWord ? m_sYes : m_sNo; break; + } + } + return pRet; +} + +void SwEntryBrowseBox::PaintCell(OutputDevice& rDev, + const tools::Rectangle& rRect, sal_uInt16 nColumnId) const +{ + const DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center; + rDev.DrawText( rRect, GetCellText( m_nCurrentRow, nColumnId ), nStyle ); +} + +::svt::CellController* SwEntryBrowseBox::GetController(long /*nRow*/, sal_uInt16 nCol) +{ + return nCol < ITEM_CASE ? m_xController.get() : m_xCheckController.get(); +} + +bool SwEntryBrowseBox::SaveModified() +{ + m_bModified = true; + const size_t nRow = GetCurRow(); + const sal_uInt16 nCol = GetCurColumnId(); + + OUString sNew; + bool bVal = false; + ::svt::CellController* pController = nullptr; + if(nCol < ITEM_CASE) + { + pController = m_xController.get(); + sNew = static_cast< ::svt::EditCellController*>(pController)->GetEditImplementation()->GetText( LINEEND_LF ); + } + else + { + pController = m_xCheckController.get(); + bVal = static_cast< ::svt::CheckBoxCellController*>(pController)->GetCheckBox().IsChecked(); + } + const bool bAddEntry = nRow >= m_Entries.size(); + std::unique_ptr<AutoMarkEntry> xNewEntry(bAddEntry ? new AutoMarkEntry : nullptr); + AutoMarkEntry* pEntry = bAddEntry ? xNewEntry.get() : m_Entries[nRow].get(); + switch(nCol) + { + case ITEM_SEARCH : pEntry->sSearch = sNew; break; + case ITEM_ALTERNATIVE : pEntry->sAlternative = sNew; break; + case ITEM_PRIM_KEY : pEntry->sPrimKey = sNew; break; + case ITEM_SEC_KEY : pEntry->sSecKey = sNew; break; + case ITEM_COMMENT : pEntry->sComment = sNew; break; + case ITEM_CASE : pEntry->bCase = bVal; break; + case ITEM_WORDONLY : pEntry->bWord = bVal; break; + } + if (bAddEntry) + { + m_Entries.push_back(std::move(xNewEntry)); + RowInserted(nRow, 1, true, true); + if(nCol < ITEM_WORDONLY) + { + pController->ClearModified(); + GoToRow( nRow ); + } + } + return true; +} + +void SwEntryBrowseBox::InitController( + ::svt::CellControllerRef& rController, long nRow, sal_uInt16 nCol) +{ + const OUString rText = GetCellText( nRow, nCol ); + if(nCol < ITEM_CASE) + { + rController = m_xController; + ::svt::CellController* pController = m_xController.get(); + static_cast< ::svt::EditCellController*>(pController)->GetEditImplementation()->SetText( rText ); + } + else + { + rController = m_xCheckController; + ::svt::CellController* pController = m_xCheckController.get(); + static_cast< ::svt::CheckBoxCellController*>(pController)->GetCheckBox().Check( + rText == m_sYes ); + } +} + +void SwEntryBrowseBox::ReadEntries(SvStream& rInStr) +{ + AutoMarkEntry* pToInsert = nullptr; + rtl_TextEncoding eTEnc = osl_getThreadTextEncoding(); + while (rInStr.good()) + { + OUString sLine; + rInStr.ReadByteStringLine( sLine, eTEnc ); + + // # -> comment + // ; -> delimiter between entries -> + // Format: TextToSearchFor;AlternativeString;PrimaryKey;SecondaryKey + // Leading and trailing blanks are ignored + if( !sLine.isEmpty() ) + { + //comments are contained in separate lines but are put into the struct of the following data + //line (if available) + if( '#' != sLine[0] ) + { + if( !pToInsert ) + pToInsert = new AutoMarkEntry; + + sal_Int32 nSttPos = 0; + pToInsert->sSearch = sLine.getToken(0, ';', nSttPos ); + pToInsert->sAlternative = sLine.getToken(0, ';', nSttPos ); + pToInsert->sPrimKey = sLine.getToken(0, ';', nSttPos ); + pToInsert->sSecKey = sLine.getToken(0, ';', nSttPos ); + + OUString sStr = sLine.getToken(0, ';', nSttPos ); + pToInsert->bCase = !sStr.isEmpty() && sStr != "0"; + + sStr = sLine.getToken(0, ';', nSttPos ); + pToInsert->bWord = !sStr.isEmpty() && sStr != "0"; + + m_Entries.push_back(std::unique_ptr<AutoMarkEntry>(pToInsert)); + pToInsert = nullptr; + } + else + { + if(pToInsert) + m_Entries.push_back(std::unique_ptr<AutoMarkEntry>(pToInsert)); + pToInsert = new AutoMarkEntry; + pToInsert->sComment = sLine.copy(1); + } + } + } + if( pToInsert ) + m_Entries.push_back(std::unique_ptr<AutoMarkEntry>(pToInsert)); + RowInserted(0, m_Entries.size() + 1); +} + +void SwEntryBrowseBox::WriteEntries(SvStream& rOutStr) +{ + //check if the current controller is modified + const sal_uInt16 nCol = GetCurColumnId(); + ::svt::CellController* pController; + if(nCol < ITEM_CASE) + pController = m_xController.get(); + else + pController = m_xCheckController.get(); + if(pController ->IsModified()) + GoToColumnId(nCol + (nCol < ITEM_CASE ? 1 : -1 )); + + rtl_TextEncoding eTEnc = osl_getThreadTextEncoding(); + for(const std::unique_ptr<AutoMarkEntry> & rpEntry : m_Entries) + { + AutoMarkEntry* pEntry = rpEntry.get(); + if(!pEntry->sComment.isEmpty()) + { + rOutStr.WriteByteStringLine( "#" + pEntry->sComment, eTEnc ); + } + + OUString sWrite( pEntry->sSearch + ";" + + pEntry->sAlternative + ";" + + pEntry->sPrimKey + ";" + + pEntry->sSecKey + ";" + + (pEntry->bCase ? OUStringLiteral("1") : OUStringLiteral("0")) + ";" + + (pEntry->bWord ? OUStringLiteral("1") : OUStringLiteral("0")) ); + + if( sWrite.getLength() > 5 ) + rOutStr.WriteByteStringLine( sWrite, eTEnc ); + } +} + +bool SwEntryBrowseBox::IsModified()const +{ + if(m_bModified) + return true; + + //check if the current controller is modified + const sal_uInt16 nCol = GetCurColumnId(); + ::svt::CellController* pController; + if(nCol < ITEM_CASE) + pController = m_xController.get(); + else + pController = m_xCheckController.get(); + return pController->IsModified(); +} + +SwAutoMarkDlg_Impl::SwAutoMarkDlg_Impl(weld::Window* pParent, const OUString& rAutoMarkURL, + bool bCreate) + : GenericDialogController(pParent, "modules/swriter/ui/createautomarkdialog.ui", "CreateAutomarkDialog") + , sAutoMarkURL(rAutoMarkURL) + , bCreateMode(bCreate) + , m_xOKPB(m_xBuilder->weld_button("ok")) + , m_xTable(m_xBuilder->weld_container("area")) + , m_xTableCtrlParent(m_xTable->CreateChildFrame()) + , m_xEntriesBB(VclPtr<SwEntryBrowseBox>::Create(m_xTableCtrlParent)) +{ + m_xEntriesBB->Show(); + m_xOKPB->connect_clicked(LINK(this, SwAutoMarkDlg_Impl, OkHdl)); + + m_xDialog->set_title(m_xDialog->get_title() + ": " + sAutoMarkURL); + bool bError = false; + if( bCreateMode ) + m_xEntriesBB->RowInserted(0); + else + { + SfxMedium aMed( sAutoMarkURL, StreamMode::STD_READ ); + if( aMed.GetInStream() && !aMed.GetInStream()->GetError() ) + m_xEntriesBB->ReadEntries( *aMed.GetInStream() ); + else + bError = true; + } + + Size aPrefSize = m_xEntriesBB->GetOptimalSize(); + m_xTable->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + + if (bError) + m_xDialog->response(RET_CANCEL); +} + +SwAutoMarkDlg_Impl::~SwAutoMarkDlg_Impl() +{ + m_xEntriesBB.disposeAndClear(); + m_xTableCtrlParent->dispose(); + m_xTableCtrlParent.clear(); +} + +IMPL_LINK_NOARG(SwAutoMarkDlg_Impl, OkHdl, weld::Button&, void) +{ + bool bError = false; + if (m_xEntriesBB->IsModified() || bCreateMode) + { + SfxMedium aMed( sAutoMarkURL, + bCreateMode ? StreamMode::WRITE + : StreamMode::WRITE| StreamMode::TRUNC ); + SvStream* pStrm = aMed.GetOutStream(); + pStrm->SetStreamCharSet( RTL_TEXTENCODING_MS_1253 ); + if( !pStrm->GetError() ) + { + m_xEntriesBB->WriteEntries( *pStrm ); + aMed.Commit(); + } + else + bError = true; + } + if (!bError) + m_xDialog->response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/index/multmrk.cxx b/sw/source/ui/index/multmrk.cxx new file mode 100644 index 000000000..6d021eb07 --- /dev/null +++ b/sw/source/ui/index/multmrk.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 <multmrk.hxx> +#include <toxmgr.hxx> +#include <wrtsh.hxx> + +SwMultiTOXMarkDlg::SwMultiTOXMarkDlg(weld::Window* pParent, SwTOXMgr& rTOXMgr) + : GenericDialogController(pParent, "modules/swriter/ui/selectindexdialog.ui", "SelectIndexDialog") + , m_rMgr(rTOXMgr) + , m_nPos(0) + , m_xTextFT(m_xBuilder->weld_label("type")) + , m_xTOXLB(m_xBuilder->weld_tree_view("treeview")) +{ + m_xTOXLB->set_size_request(m_xTOXLB->get_approximate_digit_width() * 32, + m_xTOXLB->get_height_rows(8)); + + m_xTOXLB->connect_changed(LINK(this, SwMultiTOXMarkDlg, SelectHdl)); + + sal_uInt16 nSize = m_rMgr.GetTOXMarkCount(); + for(sal_uInt16 i=0; i < nSize; ++i) + m_xTOXLB->append_text(m_rMgr.GetTOXMark(i)->GetText(m_rMgr.GetShell()->GetLayout())); + + m_xTOXLB->select(0); + m_xTextFT->set_label(m_rMgr.GetTOXMark(0)->GetTOXType()->GetTypeName()); +} + +IMPL_LINK( SwMultiTOXMarkDlg, SelectHdl, weld::TreeView&, rBox, void ) +{ + if (rBox.get_selected_index() != -1) + { + SwTOXMark* pMark = m_rMgr.GetTOXMark(rBox.get_selected_index()); + m_xTextFT->set_label(pMark->GetTOXType()->GetTypeName()); + m_nPos = rBox.get_selected_index(); + } +} + +short SwMultiTOXMarkDlg::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + m_rMgr.SetCurTOXMark(m_nPos); + return nRet; +} + +SwMultiTOXMarkDlg::~SwMultiTOXMarkDlg() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/index/swuiidxmrk.cxx b/sw/source/ui/index/swuiidxmrk.cxx new file mode 100644 index 000000000..150ca2492 --- /dev/null +++ b/sw/source/ui/index/swuiidxmrk.cxx @@ -0,0 +1,1716 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <swuiidxmrk.hxx> +#include <hintids.hxx> +#include <helpids.h> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/Bibliography.hpp> +#include <com/sun/star/i18n/IndexEntrySupplier.hpp> +#include <com/sun/star/util/SearchAlgorithms2.hpp> +#include <com/sun/star/util/SearchFlags.hpp> +#include <rtl/ustrbuf.hxx> +#include <i18nutil/searchopt.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/itemset.hxx> +#include <editeng/langitem.hxx> +#include <swtypes.hxx> +#include <toxmgr.hxx> +#include <txttxmrk.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swundo.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <fldmgr.hxx> +#include <fldbas.hxx> +#include <strings.hrc> +#include <svl/cjkoptions.hxx> +#include <ndtxt.hxx> +#include <SwRewriter.hxx> + +#define POS_CONTENT 0 +#define POS_INDEX 1 + +static sal_Int32 nTypePos = 1; // TOX_INDEX as standard +static sal_uInt16 nKey1Pos = USHRT_MAX; + +static sal_uInt16 nKey2Pos = USHRT_MAX; + +using namespace com::sun::star; +using namespace com::sun::star::i18n; +using namespace com::sun::star::lang; +using namespace com::sun::star::util; +using namespace ::comphelper; + +// dialog to insert a directory selection +SwIndexMarkPane::SwIndexMarkPane(const std::shared_ptr<weld::Dialog>& rDialog, weld::Builder& rBuilder, bool bNewDlg, + SwWrtShell& rWrtShell) + : m_xDialog(rDialog) + , m_bDel(false) + , m_bNewMark(bNewDlg) + , m_bSelected(false) + , m_bPhoneticED0_ChangedByUser(false) + , m_bPhoneticED1_ChangedByUser(false) + , m_bPhoneticED2_ChangedByUser(false) + , m_nLangForPhoneticReading(LANGUAGE_CHINESE_SIMPLIFIED) + , m_bIsPhoneticReadingEnabled(false) + , m_pSh(&rWrtShell) + , m_xTypeFT(rBuilder.weld_label("typeft")) + , m_xTypeDCB(rBuilder.weld_combo_box("typecb")) + , m_xNewBT(rBuilder.weld_button("new")) + , m_xEntryED(rBuilder.weld_entry("entryed")) + , m_xSyncED(rBuilder.weld_button("sync")) + , m_xPhoneticFT0(rBuilder.weld_label("phonetic0ft")) + , m_xPhoneticED0(rBuilder.weld_entry("phonetic0ed")) + , m_xKey1FT(rBuilder.weld_label("key1ft")) + , m_xKey1DCB(rBuilder.weld_combo_box("key1cb")) + , m_xPhoneticFT1(rBuilder.weld_label("phonetic1ft")) + , m_xPhoneticED1(rBuilder.weld_entry("phonetic1ed")) + , m_xKey2FT(rBuilder.weld_label("key2ft")) + , m_xKey2DCB(rBuilder.weld_combo_box("key2cb")) + , m_xPhoneticFT2(rBuilder.weld_label("phonetic2ft")) + , m_xPhoneticED2(rBuilder.weld_entry("phonetic2ed")) + , m_xLevelFT(rBuilder.weld_label("levelft")) + , m_xLevelNF(rBuilder.weld_spin_button("levelnf")) + , m_xMainEntryCB(rBuilder.weld_check_button("mainentrycb")) + , m_xApplyToAllCB(rBuilder.weld_check_button("applytoallcb")) + , m_xSearchCaseSensitiveCB(rBuilder.weld_check_button("searchcasesensitivecb")) + , m_xSearchCaseWordOnlyCB(rBuilder.weld_check_button("searchcasewordonlycb")) + , m_xOKBT(bNewDlg ? rBuilder.weld_button("insert") : rBuilder.weld_button("ok")) + , m_xCloseBT(rBuilder.weld_button("close")) + , m_xDelBT(rBuilder.weld_button("delete")) + , m_xPrevSameBT(rBuilder.weld_button("first")) + , m_xNextSameBT(rBuilder.weld_button("last")) + , m_xPrevBT(rBuilder.weld_button("previous")) + , m_xNextBT(rBuilder.weld_button("next")) +{ + m_xSyncED->show(); + + if (SvtCJKOptions().IsCJKFontEnabled()) + { + uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + + m_xExtendedIndexEntrySupplier = i18n::IndexEntrySupplier::create(xContext); + + m_xPhoneticFT0->show(); + m_xPhoneticED0->show(); + m_xPhoneticFT1->show(); + m_xPhoneticED1->show(); + m_xPhoneticFT2->show(); + m_xPhoneticED2->show(); + } + + // tdf#129726 there are two help pages for this dialog, one for each mode, + // where a widget/dialog appears in both, use -insert/-edit to disambiguate + if (m_bNewMark) + { + m_xDialog->set_title(SwResId(STR_IDXMRK_INSERT)); + m_xDialog->set_help_id(m_xDialog->get_help_id() + "-insert"); + m_xTypeDCB->set_help_id(m_xTypeDCB->get_help_id() + "-insert"); + } + else + { + m_xDialog->set_title(SwResId(STR_IDXMRK_EDIT)); + m_xDialog->set_help_id(m_xDialog->get_help_id() + "-edit"); + m_xTypeDCB->set_help_id(m_xTypeDCB->get_help_id() + "-edit"); + } + + m_xDelBT->connect_clicked(LINK(this,SwIndexMarkPane, DelHdl)); + m_xPrevBT->connect_clicked(LINK(this,SwIndexMarkPane, PrevHdl)); + m_xPrevSameBT->connect_clicked(LINK(this,SwIndexMarkPane, PrevSameHdl)); + m_xNextBT->connect_clicked(LINK(this,SwIndexMarkPane, NextHdl)); + m_xNextSameBT->connect_clicked(LINK(this,SwIndexMarkPane, NextSameHdl)); + m_xTypeDCB->connect_changed(LINK(this,SwIndexMarkPane, ModifyListBoxHdl)); + m_xKey1DCB->connect_changed(LINK(this,SwIndexMarkPane, KeyDCBModifyHdl)); + m_xKey2DCB->connect_changed(LINK(this,SwIndexMarkPane, KeyDCBModifyHdl)); + m_xCloseBT->connect_clicked(LINK(this,SwIndexMarkPane, CloseHdl)); + m_xEntryED->connect_changed(LINK(this,SwIndexMarkPane, ModifyEditHdl)); + m_xNewBT->connect_clicked(LINK(this, SwIndexMarkPane, NewUserIdxHdl)); + m_xApplyToAllCB->connect_toggled(LINK(this, SwIndexMarkPane, SearchTypeHdl)); + m_xPhoneticED0->connect_changed(LINK(this,SwIndexMarkPane, PhoneticEDModifyHdl)); + m_xPhoneticED1->connect_changed(LINK(this,SwIndexMarkPane, PhoneticEDModifyHdl)); + m_xPhoneticED2->connect_changed(LINK(this,SwIndexMarkPane, PhoneticEDModifyHdl)); + m_xSyncED->connect_clicked(LINK(this, SwIndexMarkPane, SyncSelectionHdl)); + + if (m_bNewMark) + m_xDelBT->hide(); + else + m_xNewBT->hide(); + m_xOKBT->show(); + m_xOKBT->connect_clicked(LINK(this, SwIndexMarkPane, InsertHdl)); + + m_xEntryED->grab_focus(); +} + +// Newly initialise controls with the new selection +void SwIndexMarkPane::InitControls() +{ + assert(m_pSh && m_pTOXMgr && "no shell?"); + // contents index + const SwTOXType* pType = m_pTOXMgr->GetTOXType(TOX_CONTENT); + assert(pType && "No directory type !!"); + OUString sTmpTypeSelection; + if (m_xTypeDCB->get_active() != -1) + sTmpTypeSelection = m_xTypeDCB->get_active_text(); + m_xTypeDCB->clear(); + m_xTypeDCB->append_text(pType->GetTypeName()); + + // keyword index + pType = m_pTOXMgr->GetTOXType(TOX_INDEX); + assert(pType && "No directory type !!"); + m_xTypeDCB->append_text(pType->GetTypeName()); + + // user index + sal_uInt16 nCount = m_pSh->GetTOXTypeCount(TOX_USER); + for (sal_uInt16 i = 0; i < nCount; ++i) + m_xTypeDCB->append_text(m_pSh->GetTOXType(TOX_USER, i)->GetTypeName()); + + // read keywords primary + { + std::vector<OUString> aArr; + m_pSh->GetTOIKeys(TOI_PRIMARY, aArr); + std::sort(aArr.begin(), aArr.end()); + auto last = std::unique(aArr.begin(), aArr.end()); + for (auto it = aArr.begin(); it != last; ++it) + m_xKey1DCB->append_text(*it); + } + + // read keywords secondary + { + std::vector<OUString> aArr; + m_pSh->GetTOIKeys( TOI_SECONDARY, aArr ); + std::sort(aArr.begin(), aArr.end()); + auto last = std::unique(aArr.begin(), aArr.end()); + for (auto it = aArr.begin(); it != last; ++it) + m_xKey2DCB->append_text(*it); + } + + UpdateLanguageDependenciesForPhoneticReading(); + + // current entry + const SwTOXMark* pMark = m_pTOXMgr->GetCurTOXMark(); + if( pMark && !m_bNewMark) + { + // Controls-Handling + + // only if there are more than one + // if equal it lands at the same entry + m_pSh->SttCursorMove(); + + const SwTOXMark* pMoveMark; + bool bShow = false; + + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_PRV ); + if( pMoveMark != pMark ) + { + m_pSh->GotoTOXMark( *pMoveMark, TOX_NXT ); + bShow = true; + } + m_xPrevBT->set_sensitive(pMoveMark != pMark); + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_NXT ); + if( pMoveMark != pMark ) + { + m_pSh->GotoTOXMark( *pMoveMark, TOX_PRV ); + bShow = true; + } + m_xNextBT->set_sensitive(pMoveMark != pMark); + if( bShow ) + { + m_xPrevBT->show(); + m_xNextBT->show(); + bShow = false; + } + + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_SAME_PRV ); + if( pMoveMark != pMark ) + { + m_pSh->GotoTOXMark( *pMoveMark, TOX_SAME_NXT ); + bShow = true; + } + m_xPrevSameBT->set_sensitive(pMoveMark != pMark); + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_SAME_NXT ); + if( pMoveMark != pMark ) + { + m_pSh->GotoTOXMark( *pMoveMark, TOX_SAME_PRV ); + bShow = true; + } + m_xNextSameBT->set_sensitive(pMoveMark != pMark); + if( bShow ) + { + m_xNextSameBT->show(); + m_xPrevSameBT->show(); + } + m_pSh->EndCursorMove(); + + m_xTypeFT->show(); + + m_xTypeDCB->set_sensitive(false); + m_xTypeFT->set_sensitive(false); + + UpdateDialog(); + } + else + { // display current selection (first element) ???? + if (m_pSh->GetCursorCnt() < 2) + { + m_bSelected = !m_pSh->HasSelection(); + m_aOrgStr = m_pSh->GetView().GetSelectionTextParam(true, false); + m_xEntryED->set_text(m_aOrgStr); + + //to include all equal entries may only be allowed in the body and even there + //only when a simple selection exists + const FrameTypeFlags nFrameType = m_pSh->GetFrameType(nullptr,true); + m_xApplyToAllCB->show(); + m_xSearchCaseSensitiveCB->show(); + m_xSearchCaseWordOnlyCB->show(); + m_xApplyToAllCB->set_sensitive(!m_aOrgStr.isEmpty() && + !(nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FLY_ANY ))); + SearchTypeHdl(*m_xApplyToAllCB); + } + + // index type is default + if (!sTmpTypeSelection.isEmpty() && m_xTypeDCB->find_text(sTmpTypeSelection) != -1) + m_xTypeDCB->set_active_text(sTmpTypeSelection); + else + m_xTypeDCB->set_active_text(m_xTypeDCB->get_text(nTypePos)); + ModifyHdl(*m_xTypeDCB); + } +} + +void SwIndexMarkPane::UpdateLanguageDependenciesForPhoneticReading() +{ + //no phonetic reading if no global cjk support + if( !m_xExtendedIndexEntrySupplier.is() ) + { + m_bIsPhoneticReadingEnabled = false; + return; + } + m_bIsPhoneticReadingEnabled = true; + + //get the current language + if(!m_bNewMark) //if dialog is opened to iterate existing marks + { + OSL_ENSURE(m_pTOXMgr, "need TOXMgr"); + if(!m_pTOXMgr) + return; + SwTOXMark* pMark = m_pTOXMgr->GetCurTOXMark(); + OSL_ENSURE(pMark, "need current SwTOXMark"); + if(!pMark) + return; + SwTextTOXMark* pTextTOXMark = pMark->GetTextTOXMark(); + OSL_ENSURE(pTextTOXMark, "need current SwTextTOXMark"); + if(!pTextTOXMark) + return; + const SwTextNode* pTextNode = pTextTOXMark->GetpTextNd(); + OSL_ENSURE(pTextNode, "need current SwTextNode"); + if(!pTextNode) + return; + sal_Int32 nTextIndex = pTextTOXMark->GetStart(); + m_nLangForPhoneticReading = pTextNode->GetLang( nTextIndex ); + } + else //if dialog is opened to create a new mark + { + sal_uInt16 nWhich; + switch(m_pSh->GetScriptType()) + { + case SvtScriptType::ASIAN: nWhich = RES_CHRATR_CJK_LANGUAGE; break; + case SvtScriptType::COMPLEX:nWhich = RES_CHRATR_CTL_LANGUAGE; break; + default:nWhich = RES_CHRATR_LANGUAGE; break; + } + SfxItemSet aLangSet(m_pSh->GetAttrPool(), {{nWhich, nWhich}}); + m_pSh->GetCurAttr(aLangSet); + m_nLangForPhoneticReading = static_cast<const SvxLanguageItem&>(aLangSet.Get(nWhich)).GetLanguage(); + } + +} + +OUString SwIndexMarkPane::GetDefaultPhoneticReading( const OUString& rText ) +{ + if( !m_bIsPhoneticReadingEnabled ) + return OUString(); + + return m_xExtendedIndexEntrySupplier->getPhoneticCandidate(rText, LanguageTag::convertToLocale( m_nLangForPhoneticReading )); +} + +void SwIndexMarkPane::Activate() +{ + // display current selection (first element) ???? + if (m_bNewMark) + { + m_xSyncED->set_sensitive(m_pSh->GetCursorCnt() < 2); + } +} + +IMPL_LINK_NOARG(SwIndexMarkPane, SyncSelectionHdl, weld::Button&, void) +{ + m_bSelected = !m_pSh->HasSelection(); + m_aOrgStr = m_pSh->GetView().GetSelectionTextParam(true, false); + m_xEntryED->set_text(m_aOrgStr); + + //to include all equal entries may only be allowed in the body and even there + //only when a simple selection exists + const FrameTypeFlags nFrameType = m_pSh->GetFrameType(nullptr,true); + m_xApplyToAllCB->show(); + m_xSearchCaseSensitiveCB->show(); + m_xSearchCaseWordOnlyCB->show(); + m_xApplyToAllCB->set_sensitive(!m_aOrgStr.isEmpty() && + !(nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FLY_ANY ))); + SearchTypeHdl(*m_xApplyToAllCB); + ModifyHdl(*m_xEntryED); +} + +// evaluate Ok-Button +void SwIndexMarkPane::Apply() +{ + InsertUpdate(); + if(m_bSelected) + m_pSh->ResetSelect(nullptr, false); +} + +// apply changes +void SwIndexMarkPane::InsertUpdate() +{ + m_pSh->StartUndo(m_bDel ? SwUndoId::INDEX_ENTRY_DELETE : SwUndoId::INDEX_ENTRY_INSERT); + m_pSh->StartAllAction(); + SwRewriter aRewriter; + + if( m_bNewMark ) + { + InsertMark(); + + if ( m_pTOXMgr->GetCurTOXMark()) + aRewriter.AddRule(UndoArg1, + m_pTOXMgr->GetCurTOXMark()->GetText(m_pSh->GetLayout())); + } + else if( !m_pSh->HasReadonlySel() ) + { + if ( m_pTOXMgr->GetCurTOXMark()) + aRewriter.AddRule(UndoArg1, + m_pTOXMgr->GetCurTOXMark()->GetText(m_pSh->GetLayout())); + + if( m_bDel ) + m_pTOXMgr->DeleteTOXMark(); + else if( m_pTOXMgr->GetCurTOXMark() ) + UpdateMark(); + } + + m_pSh->EndAllAction(); + m_pSh->EndUndo(m_bDel ? SwUndoId::INDEX_ENTRY_DELETE : SwUndoId::INDEX_ENTRY_INSERT); + + if((nTypePos = m_xTypeDCB->find_text(m_xTypeDCB->get_active_text())) == -1) + nTypePos = 0; + + nKey1Pos = m_xKey1DCB->find_text(m_xKey1DCB->get_active_text()); + nKey2Pos = m_xKey2DCB->find_text(m_xKey2DCB->get_active_text()); +} + +// insert mark +static void lcl_SelectSameStrings(SwWrtShell& rSh, bool bWordOnly, bool bCaseSensitive) +{ + rSh.Push(); + + i18nutil::SearchOptions2 aSearchOpt( + SearchAlgorithms_ABSOLUTE, + ( bWordOnly ? SearchFlags::NORM_WORD_ONLY : 0 ), + rSh.GetSelText(), OUString(), + GetAppLanguageTag().getLocale(), + 0, 0, 0, + (bCaseSensitive + ? TransliterationFlags::NONE + : TransliterationFlags::IGNORE_CASE), + SearchAlgorithms2::ABSOLUTE, + '\\' ); + + rSh.ClearMark(); + bool bCancel; + + //todo/mba: assuming that notes should not be searched + rSh.Find_Text(aSearchOpt, false/*bSearchInNotes*/, SwDocPositions::Start, SwDocPositions::End, bCancel, + FindRanges::InSelAll | FindRanges::InBodyOnly ); +} + +void SwIndexMarkPane::InsertMark() +{ + auto nPos = m_xTypeDCB->find_text(m_xTypeDCB->get_active_text()); + TOXTypes eType = nPos == POS_CONTENT ? TOX_CONTENT : + nPos == POS_INDEX ? TOX_INDEX : TOX_USER; + + SwTOXMarkDescription aDesc(eType); + + const int nLevel = m_xLevelNF->denormalize(m_xLevelNF->get_value()); + switch( nPos) + { + case POS_CONTENT : break; + case POS_INDEX: // keyword index mark + { + UpdateKeyBoxes(); + aDesc.SetPrimKey(m_xKey1DCB->get_active_text()); + aDesc.SetSecKey(m_xKey2DCB->get_active_text()); + aDesc.SetMainEntry(m_xMainEntryCB->get_active()); + aDesc.SetPhoneticReadingOfAltStr(m_xPhoneticED0->get_text()); + aDesc.SetPhoneticReadingOfPrimKey(m_xPhoneticED1->get_text()); + aDesc.SetPhoneticReadingOfSecKey(m_xPhoneticED2->get_text()); + } + break; + default: // Userdefined index mark + { + aDesc.SetTOUName(m_xTypeDCB->get_active_text()); + } + } + if (m_aOrgStr != m_xEntryED->get_text()) + aDesc.SetAltStr(m_xEntryED->get_text()); + bool bApplyAll = m_xApplyToAllCB->get_active(); + bool bWordOnly = m_xSearchCaseWordOnlyCB->get_active(); + bool bCaseSensitive = m_xSearchCaseSensitiveCB->get_active(); + + m_pSh->StartAllAction(); + // all equal strings have to be selected here so that the + // entry is applied to all equal strings + if(bApplyAll) + { + lcl_SelectSameStrings(*m_pSh, bWordOnly, bCaseSensitive); + } + aDesc.SetLevel(nLevel); + SwTOXMgr aMgr(m_pSh); + aMgr.InsertTOXMark(aDesc); + if(bApplyAll) + m_pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + + m_pSh->EndAllAction(); +} + +// update mark +void SwIndexMarkPane::UpdateMark() +{ + OUString aAltText(m_xEntryED->get_text()); + OUString* pAltText = m_aOrgStr != m_xEntryED->get_text() ? &aAltText : nullptr; + //empty alternative texts are not allowed + if(pAltText && pAltText->isEmpty()) + return; + + UpdateKeyBoxes(); + + auto nPos = m_xTypeDCB->find_text(m_xTypeDCB->get_active_text()); + TOXTypes eType = TOX_USER; + if(POS_CONTENT == nPos) + eType = TOX_CONTENT; + else if(POS_INDEX == nPos) + eType = TOX_INDEX; + + SwTOXMarkDescription aDesc(eType); + aDesc.SetLevel(m_xLevelNF->get_value()); + if(pAltText) + aDesc.SetAltStr(*pAltText); + + OUString aPrim(m_xKey1DCB->get_active_text()); + if(!aPrim.isEmpty()) + aDesc.SetPrimKey(aPrim); + OUString aSec(m_xKey2DCB->get_active_text()); + if(!aSec.isEmpty()) + aDesc.SetSecKey(aSec); + + if(eType == TOX_INDEX) + { + aDesc.SetPhoneticReadingOfAltStr(m_xPhoneticED0->get_text()); + aDesc.SetPhoneticReadingOfPrimKey(m_xPhoneticED1->get_text()); + aDesc.SetPhoneticReadingOfSecKey(m_xPhoneticED2->get_text()); + } + aDesc.SetMainEntry(m_xMainEntryCB->get_visible() && m_xMainEntryCB->get_active()); + m_pTOXMgr->UpdateTOXMark(aDesc); +} + +// insert new keys +void SwIndexMarkPane::UpdateKeyBoxes() +{ + OUString aKey(m_xKey1DCB->get_active_text()); + auto nPos = m_xKey1DCB->find_text(aKey); + if(nPos == -1 && !aKey.isEmpty()) + { // create new key + m_xKey1DCB->append_text(aKey); + } + + aKey = m_xKey2DCB->get_active_text(); + nPos = m_xKey2DCB->find_text(aKey); + + if(nPos == -1 && !aKey.isEmpty()) + { // create new key + m_xKey2DCB->append_text(aKey); + } +} + +namespace { + +class SwNewUserIdxDlg : public weld::GenericDialogController +{ + SwIndexMarkPane* m_pDlg; + + std::unique_ptr<weld::Button> m_xOKPB; + std::unique_ptr<weld::Entry> m_xNameED; + + DECL_LINK(ModifyHdl, weld::Entry&, void); + +public: + explicit SwNewUserIdxDlg(SwIndexMarkPane* pPane, weld::Window* pParent) + : GenericDialogController(pParent, "modules/swriter/ui/newuserindexdialog.ui", "NewUserIndexDialog") + , m_pDlg(pPane) + , m_xOKPB(m_xBuilder->weld_button("ok")) + , m_xNameED(m_xBuilder->weld_entry("entry")) + { + m_xNameED->connect_changed(LINK(this, SwNewUserIdxDlg, ModifyHdl)); + m_xOKPB->set_sensitive(false); + m_xNameED->grab_focus(); + } + OUString GetName() const { return m_xNameED->get_text(); } +}; + +} + +IMPL_LINK( SwNewUserIdxDlg, ModifyHdl, weld::Entry&, rEdit, void) +{ + m_xOKPB->set_sensitive(!rEdit.get_text().isEmpty() && !m_pDlg->IsTOXType(rEdit.get_text())); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, NewUserIdxHdl, weld::Button&, void) +{ + SwNewUserIdxDlg aDlg(this, m_xDialog.get()); + if (aDlg.run() == RET_OK) + { + OUString sNewName(aDlg.GetName()); + m_xTypeDCB->append_text(sNewName); + m_xTypeDCB->set_active_text(sNewName); + } +} + +IMPL_LINK( SwIndexMarkPane, SearchTypeHdl, weld::ToggleButton&, rBox, void) +{ + const bool bEnable = rBox.get_active() && rBox.get_sensitive(); + m_xSearchCaseWordOnlyCB->set_sensitive(bEnable); + m_xSearchCaseSensitiveCB->set_sensitive(bEnable); +} + +IMPL_LINK(SwIndexMarkPane, InsertHdl, weld::Button&, rButton, void) +{ + Apply(); + //close the dialog if only one entry is available + if(!m_bNewMark && !m_xPrevBT->get_visible() && !m_xNextBT->get_visible()) + CloseHdl(rButton); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, CloseHdl, weld::Button&, void) +{ + if (m_bNewMark) + { + SfxViewFrame::Current()->GetDispatcher()->Execute(FN_INSERT_IDX_ENTRY_DLG, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD); + } + else + { + m_xDialog->response(RET_CLOSE); + } +} + +// select index type only when inserting +IMPL_LINK(SwIndexMarkPane, ModifyListBoxHdl, weld::ComboBox&, rBox, void) +{ + ModifyHdl(rBox); +} + +IMPL_LINK(SwIndexMarkPane, ModifyEditHdl, weld::Entry&, rEdit, void) +{ + ModifyHdl(rEdit); +} + +void SwIndexMarkPane::ModifyHdl(const weld::Widget& rBox) +{ + if (m_xTypeDCB.get() == &rBox) + { + // set index type + auto nPos = m_xTypeDCB->find_text(m_xTypeDCB->get_active_text()); + bool bLevelEnable = false, + bKeyEnable = false, + bSetKey2 = false, + bKey2Enable = false, + bEntryHasText = false, + bKey1HasText = false, + bKey2HasText = false; + if(nPos == POS_INDEX) + { + if (!m_xEntryED->get_text().isEmpty()) + bEntryHasText = true; + m_xPhoneticED0->set_text(GetDefaultPhoneticReading(m_xEntryED->get_text())); + + bKeyEnable = true; + m_xKey1DCB->set_active_text(m_xKey1DCB->get_text(nKey1Pos)); + m_xPhoneticED1->set_text(GetDefaultPhoneticReading(m_xKey1DCB->get_active_text())); + if (!m_xKey1DCB->get_active_text().isEmpty()) + { + bKey1HasText = bSetKey2 = bKey2Enable = true; + m_xKey2DCB->set_active_text(m_xKey2DCB->get_text(nKey2Pos)); + m_xPhoneticED2->set_text(GetDefaultPhoneticReading(m_xKey2DCB->get_active_text())); + if(!m_xKey2DCB->get_active_text().isEmpty()) + bKey2HasText = true; + } + } + else + { + bLevelEnable = true; + m_xLevelNF->set_max(MAXLEVEL); + m_xLevelNF->set_value(m_xLevelNF->normalize(0)); + bSetKey2 = true; + } + m_xLevelFT->set_visible(bLevelEnable); + m_xLevelNF->set_visible(bLevelEnable); + m_xMainEntryCB->set_visible(nPos == POS_INDEX); + + m_xKey1FT->set_sensitive(bKeyEnable); + m_xKey1DCB->set_sensitive(bKeyEnable); + if ( bSetKey2 ) + { + m_xKey2DCB->set_sensitive(bKey2Enable); + m_xKey2FT->set_sensitive(bKey2Enable); + } + m_xPhoneticFT0->set_sensitive(bKeyEnable&&bEntryHasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED0->set_sensitive(bKeyEnable&&bEntryHasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticFT1->set_sensitive(bKeyEnable&&bKey1HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED1->set_sensitive(bKeyEnable&&bKey1HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticFT2->set_sensitive(bKeyEnable&&bKey2HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED2->set_sensitive(bKeyEnable&&bKey2HasText&&m_bIsPhoneticReadingEnabled); + } + else //m_xEntryED !!m_xEntryED is not a ListBox but an Edit + { + bool bHasText = !m_xEntryED->get_text().isEmpty(); + if(!bHasText) + { + m_xPhoneticED0->set_text(OUString()); + m_bPhoneticED0_ChangedByUser = false; + } + else if(!m_bPhoneticED0_ChangedByUser) + m_xPhoneticED0->set_text(GetDefaultPhoneticReading(m_xEntryED->get_text())); + + m_xPhoneticFT0->set_sensitive(bHasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED0->set_sensitive(bHasText&&m_bIsPhoneticReadingEnabled); + } + m_xOKBT->set_sensitive(!m_pSh->HasReadonlySel() && + (!m_xEntryED->get_text().isEmpty() || m_pSh->GetCursorCnt(false))); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, NextHdl, weld::Button&, void) +{ + InsertUpdate(); + m_pTOXMgr->NextTOXMark(); + UpdateDialog(); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, NextSameHdl, weld::Button&, void) +{ + InsertUpdate(); + m_pTOXMgr->NextTOXMark(true); + UpdateDialog(); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, PrevHdl, weld::Button&, void) +{ + InsertUpdate(); + m_pTOXMgr->PrevTOXMark(); + UpdateDialog(); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, PrevSameHdl, weld::Button&, void) +{ + InsertUpdate(); + m_pTOXMgr->PrevTOXMark(true); + UpdateDialog(); +} + +IMPL_LINK_NOARG(SwIndexMarkPane, DelHdl, weld::Button&, void) +{ + m_bDel = true; + InsertUpdate(); + m_bDel = false; + + if(m_pTOXMgr->GetCurTOXMark()) + UpdateDialog(); + else + { + CloseHdl(*m_xCloseBT); + SfxViewFrame::Current()->GetBindings().Invalidate(FN_EDIT_IDX_ENTRY_DLG); + } +} + +// renew dialog view +void SwIndexMarkPane::UpdateDialog() +{ + OSL_ENSURE(m_pSh && m_pTOXMgr, "no shell?"); + SwTOXMark* pMark = m_pTOXMgr->GetCurTOXMark(); + OSL_ENSURE(pMark, "no current marker"); + if(!pMark) + return; + + SwViewShell::SetCareDialog(m_xDialog); + + m_aOrgStr = pMark->GetText(m_pSh->GetLayout()); + m_xEntryED->set_text(m_aOrgStr); + + // set index type + bool bLevelEnable = true, + bKeyEnable = false, + bKey2Enable = false, + bEntryHasText = false, + bKey1HasText = false, + bKey2HasText = false; + + TOXTypes eCurType = pMark->GetTOXType()->GetType(); + if(TOX_INDEX == eCurType) + { + bLevelEnable = false; + bKeyEnable = true; + bKey1HasText = bKey2Enable = !pMark->GetPrimaryKey().isEmpty(); + bKey2HasText = !pMark->GetSecondaryKey().isEmpty(); + bEntryHasText = !pMark->GetText(m_pSh->GetLayout()).isEmpty(); + m_xKey1DCB->set_entry_text( pMark->GetPrimaryKey() ); + m_xKey2DCB->set_entry_text( pMark->GetSecondaryKey() ); + m_xPhoneticED0->set_text( pMark->GetTextReading() ); + m_xPhoneticED1->set_text( pMark->GetPrimaryKeyReading() ); + m_xPhoneticED2->set_text( pMark->GetSecondaryKeyReading() ); + m_xMainEntryCB->set_active(pMark->IsMainEntry()); + } + else if(TOX_CONTENT == eCurType || TOX_USER == eCurType) + { + m_xLevelNF->set_value(m_xLevelNF->normalize(pMark->GetLevel())); + } + m_xKey1FT->set_sensitive(bKeyEnable); + m_xKey1DCB->set_sensitive(bKeyEnable); + m_xLevelNF->set_max(MAXLEVEL); + m_xLevelFT->set_visible(bLevelEnable); + m_xLevelNF->set_visible(bLevelEnable); + m_xMainEntryCB->set_visible(!bLevelEnable); + m_xKey2FT->set_sensitive(bKey2Enable); + m_xKey2DCB->set_sensitive(bKey2Enable); + + UpdateLanguageDependenciesForPhoneticReading(); + m_xPhoneticFT0->set_sensitive(bKeyEnable&&bEntryHasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED0->set_sensitive(bKeyEnable&&bEntryHasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticFT1->set_sensitive(bKeyEnable&&bKey1HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED1->set_sensitive(bKeyEnable&&bKey1HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticFT2->set_sensitive(bKeyEnable&&bKey2HasText&&m_bIsPhoneticReadingEnabled); + m_xPhoneticED2->set_sensitive(bKeyEnable&&bKey2HasText&&m_bIsPhoneticReadingEnabled); + + // set index type + m_xTypeDCB->set_active_text(pMark->GetTOXType()->GetTypeName()); + + // set Next - Prev - Buttons + m_pSh->SttCursorMove(); + if( m_xPrevBT->get_visible() ) + { + const SwTOXMark* pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_PRV ); + if( pMoveMark != pMark ) + m_pSh->GotoTOXMark( *pMoveMark, TOX_NXT ); + m_xPrevBT->set_sensitive( pMoveMark != pMark ); + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_NXT ); + if( pMoveMark != pMark ) + m_pSh->GotoTOXMark( *pMoveMark, TOX_PRV ); + m_xNextBT->set_sensitive( pMoveMark != pMark ); + } + + if (m_xPrevSameBT->get_visible()) + { + const SwTOXMark* pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_SAME_PRV ); + if( pMoveMark != pMark ) + m_pSh->GotoTOXMark( *pMoveMark, TOX_SAME_NXT ); + m_xPrevSameBT->set_sensitive( pMoveMark != pMark ); + pMoveMark = &m_pSh->GotoTOXMark( *pMark, TOX_SAME_NXT ); + if( pMoveMark != pMark ) + m_pSh->GotoTOXMark( *pMoveMark, TOX_SAME_PRV ); + m_xNextSameBT->set_sensitive( pMoveMark != pMark ); + } + + const bool bEnable = !m_pSh->HasReadonlySel(); + m_xOKBT->set_sensitive(bEnable); + m_xDelBT->set_sensitive(bEnable); + m_xEntryED->set_sensitive(bEnable); + m_xLevelNF->set_sensitive(bEnable); + m_xKey1DCB->set_sensitive(bEnable); + m_xKey2DCB->set_sensitive(bEnable); + + m_pSh->SelectTextAttr( RES_TXTATR_TOXMARK, pMark->GetTextTOXMark() ); + // we need the point at the start of the attribute + m_pSh->SwapPam(); + + m_pSh->EndCursorMove(); +} + +// Remind whether the edit boxes for Phonetic reading are changed manually +IMPL_LINK(SwIndexMarkPane, PhoneticEDModifyHdl, weld::Entry&, rEdit, void) +{ + if (m_xPhoneticED0.get() == &rEdit) + { + m_bPhoneticED0_ChangedByUser = !rEdit.get_text().isEmpty(); + } + else if (m_xPhoneticED1.get() == &rEdit) + { + m_bPhoneticED1_ChangedByUser = !rEdit.get_text().isEmpty(); + } + else if (m_xPhoneticED2.get() == &rEdit) + { + m_bPhoneticED2_ChangedByUser = !rEdit.get_text().isEmpty(); + } +} + +// Enable Disable of the 2nd key +IMPL_LINK( SwIndexMarkPane, KeyDCBModifyHdl, weld::ComboBox&, rBox, void ) +{ + if (m_xKey1DCB.get() == &rBox) + { + bool bEnable = !rBox.get_active_text().isEmpty(); + if(!bEnable) + { + m_xKey2DCB->set_entry_text(OUString()); + m_xPhoneticED1->set_text(OUString()); + m_xPhoneticED2->set_text(OUString()); + m_bPhoneticED1_ChangedByUser = false; + m_bPhoneticED2_ChangedByUser = false; + } + else + { + if (rBox.get_popup_shown()) + { + //reset bPhoneticED1_ChangedByUser if a completely new string is selected + m_bPhoneticED1_ChangedByUser = false; + } + if (!m_bPhoneticED1_ChangedByUser) + m_xPhoneticED1->set_text(GetDefaultPhoneticReading(rBox.get_active_text())); + } + m_xKey2DCB->set_sensitive(bEnable); + m_xKey2FT->set_sensitive(bEnable); + } + else if (m_xKey2DCB.get() == &rBox) + { + if (rBox.get_active_text().isEmpty()) + { + m_xPhoneticED2->set_text(OUString()); + m_bPhoneticED2_ChangedByUser = false; + } + else + { + if (rBox.get_popup_shown()) + { + //reset bPhoneticED1_ChangedByUser if a completely new string is selected + m_bPhoneticED2_ChangedByUser = false; + } + if(!m_bPhoneticED2_ChangedByUser) + m_xPhoneticED2->set_text(GetDefaultPhoneticReading(rBox.get_active_text())); + } + } + + bool bKey1HasText = !m_xKey1DCB->get_active_text().isEmpty(); + bool bKey2HasText = !m_xKey2DCB->get_active_text().isEmpty(); + + m_xPhoneticFT1->set_sensitive(bKey1HasText && m_bIsPhoneticReadingEnabled); + m_xPhoneticED1->set_sensitive(bKey1HasText && m_bIsPhoneticReadingEnabled); + m_xPhoneticFT2->set_sensitive(bKey2HasText && m_bIsPhoneticReadingEnabled); + m_xPhoneticED2->set_sensitive(bKey2HasText && m_bIsPhoneticReadingEnabled); +} + +SwIndexMarkPane::~SwIndexMarkPane() +{ +} + +void SwIndexMarkPane::ReInitDlg(SwWrtShell& rWrtShell, SwTOXMark const * pCurTOXMark) +{ + m_pSh = &rWrtShell; + m_pTOXMgr.reset( new SwTOXMgr(m_pSh) ); + if(pCurTOXMark) + { + for(sal_uInt16 i = 0; i < m_pTOXMgr->GetTOXMarkCount(); i++) + if(m_pTOXMgr->GetTOXMark(i) == pCurTOXMark) + { + m_pTOXMgr->SetCurTOXMark(i); + break; + } + } + InitControls(); +} + +SwIndexMarkFloatDlg::SwIndexMarkFloatDlg(SfxBindings* _pBindings, + SfxChildWindow* pChild, weld::Window *pParent, + SfxChildWinInfo const * pInfo, bool bNew) + : SfxModelessDialogController(_pBindings, pChild, pParent, + "modules/swriter/ui/indexentry.ui", "IndexEntryDialog") + , m_aContent(m_xDialog, *m_xBuilder, bNew, *::GetActiveWrtShell()) +{ + if (SwWrtShell* pSh = ::GetActiveWrtShell()) + m_aContent.ReInitDlg(*pSh); + Initialize(pInfo); +} + +void SwIndexMarkFloatDlg::Activate() +{ + SfxModelessDialogController::Activate(); + m_aContent.Activate(); +} + +void SwIndexMarkFloatDlg::ReInitDlg(SwWrtShell& rWrtShell) +{ + m_aContent.ReInitDlg( rWrtShell ); +} + +SwIndexMarkModalDlg::SwIndexMarkModalDlg(weld::Window *pParent, SwWrtShell& rSh, SwTOXMark const * pCurTOXMark) + : SfxDialogController(pParent, "modules/swriter/ui/indexentry.ui", + "IndexEntryDialog") + , m_aContent(m_xDialog, *m_xBuilder, false, rSh) +{ + m_aContent.ReInitDlg(rSh, pCurTOXMark); +} + +SwIndexMarkModalDlg::~SwIndexMarkModalDlg() +{ + SwViewShell::SetCareDialog(nullptr); +} + +short SwIndexMarkModalDlg::run() +{ + short nRet = SfxDialogController::run(); + if (RET_OK == nRet) + m_aContent.Apply(); + return nRet; +} + +namespace { + +class SwCreateAuthEntryDlg_Impl : public weld::GenericDialogController +{ + std::vector<std::unique_ptr<weld::Builder>> m_aBuilders; + + Link<weld::Entry&,bool> aShortNameCheckLink; + + SwWrtShell& rWrtSh; + + bool m_bNewEntryMode; + bool m_bNameAllowed; + + std::vector<std::unique_ptr<weld::Container>> m_aOrigContainers; + std::vector<std::unique_ptr<weld::Label>> m_aFixedTexts; + std::unique_ptr<weld::Entry> pEdits[AUTH_FIELD_END]; + std::unique_ptr<weld::Button> m_xOKBT; + std::unique_ptr<weld::Container> m_xBox; + std::unique_ptr<weld::Container> m_xLeft; + std::unique_ptr<weld::Container> m_xRight; + std::unique_ptr<weld::ComboBox> m_xTypeListBox; + std::unique_ptr<weld::ComboBox> m_xIdentifierBox; + + DECL_LINK(IdentifierHdl, weld::ComboBox&, void); + DECL_LINK(ShortNameHdl, weld::Entry&, void); + DECL_LINK(EnableHdl, weld::ComboBox&, void); + +public: + SwCreateAuthEntryDlg_Impl(weld::Window* pParent, + const OUString pFields[], + SwWrtShell& rSh, + bool bNewEntry, + bool bCreate); + + OUString GetEntryText(ToxAuthorityField eField) const; + + void SetCheckNameHdl(const Link<weld::Entry&,bool>& rLink) {aShortNameCheckLink = rLink;} + +}; + +struct TextInfo +{ + ToxAuthorityField nToxField; + const char* pHelpId; +}; + +} + +static const TextInfo aTextInfoArr[] = +{ + {AUTH_FIELD_IDENTIFIER, HID_AUTH_FIELD_IDENTIFIER }, + {AUTH_FIELD_AUTHORITY_TYPE, HID_AUTH_FIELD_AUTHORITY_TYPE }, + {AUTH_FIELD_AUTHOR, HID_AUTH_FIELD_AUTHOR }, + {AUTH_FIELD_TITLE, HID_AUTH_FIELD_TITLE }, + {AUTH_FIELD_YEAR, HID_AUTH_FIELD_YEAR }, + {AUTH_FIELD_PUBLISHER, HID_AUTH_FIELD_PUBLISHER }, + {AUTH_FIELD_ADDRESS, HID_AUTH_FIELD_ADDRESS }, + {AUTH_FIELD_ISBN, HID_AUTH_FIELD_ISBN }, + {AUTH_FIELD_CHAPTER, HID_AUTH_FIELD_CHAPTER }, + {AUTH_FIELD_PAGES, HID_AUTH_FIELD_PAGES }, + {AUTH_FIELD_EDITOR, HID_AUTH_FIELD_EDITOR }, + {AUTH_FIELD_EDITION, HID_AUTH_FIELD_EDITION }, + {AUTH_FIELD_BOOKTITLE, HID_AUTH_FIELD_BOOKTITLE }, + {AUTH_FIELD_VOLUME, HID_AUTH_FIELD_VOLUME }, + {AUTH_FIELD_HOWPUBLISHED, HID_AUTH_FIELD_HOWPUBLISHED }, + {AUTH_FIELD_ORGANIZATIONS, HID_AUTH_FIELD_ORGANIZATIONS }, + {AUTH_FIELD_INSTITUTION, HID_AUTH_FIELD_INSTITUTION }, + {AUTH_FIELD_SCHOOL, HID_AUTH_FIELD_SCHOOL }, + {AUTH_FIELD_REPORT_TYPE, HID_AUTH_FIELD_REPORT_TYPE }, + {AUTH_FIELD_MONTH, HID_AUTH_FIELD_MONTH }, + {AUTH_FIELD_JOURNAL, HID_AUTH_FIELD_JOURNAL }, + {AUTH_FIELD_NUMBER, HID_AUTH_FIELD_NUMBER }, + {AUTH_FIELD_SERIES, HID_AUTH_FIELD_SERIES }, + {AUTH_FIELD_ANNOTE, HID_AUTH_FIELD_ANNOTE }, + {AUTH_FIELD_NOTE, HID_AUTH_FIELD_NOTE }, + {AUTH_FIELD_URL, HID_AUTH_FIELD_URL }, + {AUTH_FIELD_CUSTOM1, HID_AUTH_FIELD_CUSTOM1 }, + {AUTH_FIELD_CUSTOM2, HID_AUTH_FIELD_CUSTOM2 }, + {AUTH_FIELD_CUSTOM3, HID_AUTH_FIELD_CUSTOM3 }, + {AUTH_FIELD_CUSTOM4, HID_AUTH_FIELD_CUSTOM4 }, + {AUTH_FIELD_CUSTOM5, HID_AUTH_FIELD_CUSTOM5 } +}; + +static OUString lcl_FindColumnEntry(const uno::Sequence<beans::PropertyValue>& rFields, const OUString& rColumnTitle) +{ + for(const auto& rField : rFields) + { + OUString sRet; + if(rField.Name == rColumnTitle && + (rField.Value >>= sRet)) + { + return sRet; + } + } + return OUString(); +} + +bool SwAuthorMarkPane::bIsFromComponent = true; + +SwAuthorMarkPane::SwAuthorMarkPane(weld::DialogController &rDialog, weld::Builder& rBuilder, bool bNewDlg) + : m_rDialog(rDialog) + , bNewEntry(bNewDlg) + , bBibAccessInitialized(false) + , pSh(nullptr) + , m_xFromComponentRB(rBuilder.weld_radio_button("frombibliography")) + , m_xFromDocContentRB(rBuilder.weld_radio_button("fromdocument")) + , m_xAuthorFI(rBuilder.weld_label("author")) + , m_xTitleFI(rBuilder.weld_label("title")) + , m_xEntryED(rBuilder.weld_entry("entryed")) + , m_xEntryLB(rBuilder.weld_combo_box("entrylb")) + , m_xActionBT(rBuilder.weld_button(bNewEntry ? OString("insert") : OString("modify"))) + , m_xCloseBT(rBuilder.weld_button("close")) + , m_xCreateEntryPB(rBuilder.weld_button("new")) + , m_xEditEntryPB(rBuilder.weld_button("edit")) +{ + m_xActionBT->show(); + m_xFromComponentRB->set_visible(bNewEntry); + m_xFromDocContentRB->set_visible(bNewEntry); + m_xFromComponentRB->set_active(bIsFromComponent); + m_xFromDocContentRB->set_active(!bIsFromComponent); + + m_xActionBT->connect_clicked(LINK(this,SwAuthorMarkPane, InsertHdl)); + m_xCloseBT->connect_clicked(LINK(this,SwAuthorMarkPane, CloseHdl)); + m_xCreateEntryPB->connect_clicked(LINK(this,SwAuthorMarkPane, CreateEntryHdl)); + m_xEditEntryPB->connect_clicked(LINK(this,SwAuthorMarkPane, CreateEntryHdl)); + m_xFromComponentRB->connect_toggled(LINK(this,SwAuthorMarkPane, ChangeSourceHdl)); + m_xFromDocContentRB->connect_toggled(LINK(this,SwAuthorMarkPane, ChangeSourceHdl)); + m_xEntryED->connect_changed(LINK(this,SwAuthorMarkPane, EditModifyHdl)); + + m_rDialog.set_title(SwResId( + bNewEntry ? STR_AUTHMRK_INSERT : STR_AUTHMRK_EDIT)); + + m_xEntryED->set_visible(!bNewEntry); + m_xEntryLB->set_visible(bNewEntry); + if (bNewEntry) + { + m_xEntryLB->connect_changed(LINK(this, SwAuthorMarkPane, CompEntryHdl)); + } +} + +void SwAuthorMarkPane::ReInitDlg(SwWrtShell& rWrtShell) +{ + pSh = &rWrtShell; + InitControls(); +} + +IMPL_LINK_NOARG(SwAuthorMarkPane, CloseHdl, weld::Button&, void) +{ + if(bNewEntry) + { + SfxViewFrame::Current()->GetDispatcher()->Execute(FN_INSERT_AUTH_ENTRY_DLG, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD); + } + else + { + m_rDialog.response(RET_CANCEL); + } +} + +IMPL_LINK( SwAuthorMarkPane, CompEntryHdl, weld::ComboBox&, rBox, void) +{ + const OUString sEntry(rBox.get_active_text()); + if(bIsFromComponent) + { + if(xBibAccess.is() && !sEntry.isEmpty()) + { + if(xBibAccess->hasByName(sEntry)) + { + uno::Any aEntry(xBibAccess->getByName(sEntry)); + uno::Sequence<beans::PropertyValue> aFieldProps; + if(aEntry >>= aFieldProps) + { + auto nSize = std::min(static_cast<sal_Int32>(AUTH_FIELD_END), aFieldProps.getLength()); + for(sal_Int32 i = 0; i < nSize; i++) + { + m_sFields[i] = lcl_FindColumnEntry(aFieldProps, m_sColumnTitles[i]); + } + } + } + } + } + else + { + if(!sEntry.isEmpty()) + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + const SwAuthEntry* pEntry = pFType ? pFType->GetEntryByIdentifier(sEntry) : nullptr; + for(int i = 0; i < AUTH_FIELD_END; i++) + m_sFields[i] = pEntry ? + pEntry->GetAuthorField(static_cast<ToxAuthorityField>(i)) : OUString(); + } + } + if (rBox.get_active_text().isEmpty()) + { + for(OUString & s : m_sFields) + s.clear(); + } + m_xAuthorFI->set_label(m_sFields[AUTH_FIELD_AUTHOR]); + m_xTitleFI->set_label(m_sFields[AUTH_FIELD_TITLE]); +} + +IMPL_LINK_NOARG(SwAuthorMarkPane, InsertHdl, weld::Button&, void) +{ + //insert or update the SwAuthorityField... + if(pSh) + { + bool bDifferent = false; + OSL_ENSURE(!m_sFields[AUTH_FIELD_IDENTIFIER].isEmpty() , "No Id is set!"); + OSL_ENSURE(!m_sFields[AUTH_FIELD_AUTHORITY_TYPE].isEmpty() , "No authority type is set!"); + //check if the entry already exists with different content + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + const SwAuthEntry* pEntry = pFType ? + pFType->GetEntryByIdentifier( m_sFields[AUTH_FIELD_IDENTIFIER]) + : nullptr; + if(pEntry) + { + for(int i = 0; i < AUTH_FIELD_END && !bDifferent; i++) + bDifferent |= m_sFields[i] != pEntry->GetAuthorField(static_cast<ToxAuthorityField>(i)); + if(bDifferent) + { + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(m_rDialog.getDialog(), + VclMessageType::Question, VclButtonsType::YesNo, + SwResId(STR_QUERY_CHANGE_AUTH_ENTRY))); + if (RET_YES != xQuery->run()) + return; + } + } + + SwFieldMgr aMgr(pSh); + OUStringBuffer sFields; + for(OUString & s : m_sFields) + { + sFields.append(s).append(TOX_STYLE_DELIMITER); + } + if(bNewEntry) + { + if(bDifferent) + { + rtl::Reference<SwAuthEntry> xNewData(new SwAuthEntry); + for(int i = 0; i < AUTH_FIELD_END; i++) + xNewData->SetAuthorField(static_cast<ToxAuthorityField>(i), m_sFields[i]); + pSh->ChangeAuthorityData(xNewData.get()); + } + SwInsertField_Data aData(SwFieldTypesEnum::Authority, 0, sFields.makeStringAndClear(), OUString(), 0 ); + aMgr.InsertField( aData ); + } + else if(aMgr.GetCurField()) + { + aMgr.UpdateCurField(0, sFields.makeStringAndClear(), OUString()); + } + } + if(!bNewEntry) + CloseHdl(*m_xCloseBT); +} + +IMPL_LINK(SwAuthorMarkPane, CreateEntryHdl, weld::Button&, rButton, void) +{ + bool bCreate = &rButton == m_xCreateEntryPB.get(); + OUString sOldId = m_sCreatedEntry[0]; + for(int i = 0; i < AUTH_FIELD_END; i++) + m_sCreatedEntry[i] = bCreate ? OUString() : m_sFields[i]; + SwCreateAuthEntryDlg_Impl aDlg(m_rDialog.getDialog(), + bCreate ? m_sCreatedEntry : m_sFields, + *pSh, bNewEntry, bCreate); + if(bNewEntry) + { + aDlg.SetCheckNameHdl(LINK(this, SwAuthorMarkPane, IsEntryAllowedHdl)); + } + if(RET_OK == aDlg.run()) + { + if(bCreate && !sOldId.isEmpty()) + { + m_xEntryLB->remove_text(sOldId); + } + for(int i = 0; i < AUTH_FIELD_END; i++) + { + m_sFields[i] = aDlg.GetEntryText(static_cast<ToxAuthorityField>(i)); + m_sCreatedEntry[i] = m_sFields[i]; + } + if(bNewEntry && !m_xFromDocContentRB->get_active()) + { + m_xFromDocContentRB->set_active(true); + ChangeSourceHdl(*m_xFromDocContentRB); + } + if(bCreate) + { + OSL_ENSURE(m_xEntryLB->find_text(m_sFields[AUTH_FIELD_IDENTIFIER]) == -1, + "entry exists!"); + m_xEntryLB->append_text(m_sFields[AUTH_FIELD_IDENTIFIER]); + m_xEntryLB->set_active_text(m_sFields[AUTH_FIELD_IDENTIFIER]); + } + m_xEntryED->set_text(m_sFields[AUTH_FIELD_IDENTIFIER]); + m_xAuthorFI->set_label(m_sFields[AUTH_FIELD_AUTHOR]); + m_xTitleFI->set_label(m_sFields[AUTH_FIELD_TITLE]); + m_xActionBT->set_sensitive(true); + } +} + +IMPL_LINK_NOARG(SwAuthorMarkPane, ChangeSourceHdl, weld::ToggleButton&, void) +{ + bool bFromComp = m_xFromComponentRB->get_active(); + bIsFromComponent = bFromComp; + m_xCreateEntryPB->set_sensitive(!bIsFromComponent); + m_xEntryLB->clear(); + if(bIsFromComponent) + { + if(!bBibAccessInitialized) + { + uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + xBibAccess = frame::Bibliography::create( xContext ); + uno::Reference< beans::XPropertySet > xPropSet(xBibAccess, uno::UNO_QUERY); + OUString uPropName("BibliographyDataFieldNames"); + if(xPropSet.is() && xPropSet->getPropertySetInfo()->hasPropertyByName(uPropName)) + { + uno::Any aNames = xPropSet->getPropertyValue(uPropName); + uno::Sequence<beans::PropertyValue> aSeq; + if( aNames >>= aSeq) + { + for(const beans::PropertyValue& rProp : std::as_const(aSeq)) + { + sal_Int16 nField = 0; + rProp.Value >>= nField; + if(nField >= 0 && nField < AUTH_FIELD_END) + m_sColumnTitles[nField] = rProp.Name; + } + } + } + bBibAccessInitialized = true; + } + if(xBibAccess.is()) + { + const uno::Sequence<OUString> aIdentifiers = xBibAccess->getElementNames(); + for(const OUString& rName : aIdentifiers) + m_xEntryLB->append_text(rName); + } + } + else + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + std::vector<OUString> aIds; + pFType->GetAllEntryIdentifiers( aIds ); + for(const OUString & i : aIds) + m_xEntryLB->append_text(i); + } + if(!m_sCreatedEntry[AUTH_FIELD_IDENTIFIER].isEmpty()) + m_xEntryLB->append_text(m_sCreatedEntry[AUTH_FIELD_IDENTIFIER]); + } + m_xEntryLB->set_active(0); + CompEntryHdl(*m_xEntryLB); +} + +IMPL_LINK(SwAuthorMarkPane, EditModifyHdl, weld::Entry&, rEdit, void) +{ + Link<weld::Entry&,bool> aAllowed = LINK(this, SwAuthorMarkPane, IsEditAllowedHdl); + bool bResult = aAllowed.Call(rEdit); + m_xActionBT->set_sensitive(bResult); + if(bResult) + { + OUString sEntry(rEdit.get_text()); + m_sFields[AUTH_FIELD_IDENTIFIER] = sEntry; + m_sCreatedEntry[AUTH_FIELD_IDENTIFIER] = sEntry; + } +}; + +IMPL_LINK(SwAuthorMarkPane, IsEntryAllowedHdl, weld::Entry&, rEdit, bool) +{ + OUString sEntry = rEdit.get_text(); + bool bAllowed = false; + if(!sEntry.isEmpty()) + { + if (m_xEntryLB->find_text(sEntry) != -1) + return false; + else if(bIsFromComponent) + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + bAllowed = !pFType || !pFType->GetEntryByIdentifier(sEntry); + } + else + { + bAllowed = !xBibAccess.is() || !xBibAccess->hasByName(sEntry); + } + } + return bAllowed; +} + +IMPL_LINK(SwAuthorMarkPane, IsEditAllowedHdl, weld::Entry&, rEdit, bool) +{ + OUString sEntry = rEdit.get_text(); + bool bAllowed = false; + if(!sEntry.isEmpty()) + { + if (m_xEntryLB->find_text(sEntry) != -1) + return false; + else if(bIsFromComponent) + { + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + bAllowed = !pFType || !pFType->GetEntryByIdentifier(sEntry); + } + else + { + bAllowed = !xBibAccess.is() || !xBibAccess->hasByName(sEntry); + } + } + return bAllowed; +} + +void SwAuthorMarkPane::InitControls() +{ + OSL_ENSURE(pSh, "no shell?"); + SwField* pField = pSh->GetCurField(); + OSL_ENSURE(bNewEntry || pField, "no current marker"); + if(bNewEntry) + { + ChangeSourceHdl(m_xFromComponentRB->get_active() ? *m_xFromComponentRB : *m_xFromDocContentRB); + m_xCreateEntryPB->set_sensitive(!m_xFromComponentRB->get_active()); + if(!m_xFromComponentRB->get_active() && !m_sCreatedEntry[0].isEmpty()) + for(int i = 0; i < AUTH_FIELD_END; i++) + m_sFields[i] = m_sCreatedEntry[i]; + } + if(bNewEntry || !pField || pField->GetTyp()->Which() != SwFieldIds::TableOfAuthorities) + return; + + const SwAuthEntry* pEntry = static_cast<SwAuthorityField*>(pField)->GetAuthEntry(); + + OSL_ENSURE(pEntry, "No authority entry found"); + if(!pEntry) + return; + for(int i = 0; i < AUTH_FIELD_END; i++) + m_sFields[i] = pEntry->GetAuthorField(static_cast<ToxAuthorityField>(i)); + + m_xEntryED->set_text(pEntry->GetAuthorField(AUTH_FIELD_IDENTIFIER)); + m_xAuthorFI->set_label(pEntry->GetAuthorField(AUTH_FIELD_AUTHOR)); + m_xTitleFI->set_label(pEntry->GetAuthorField(AUTH_FIELD_TITLE)); +} + +void SwAuthorMarkPane::Activate() +{ + m_xActionBT->set_sensitive(!pSh->HasReadonlySel()); +} + +namespace +{ + const char* STR_AUTH_FIELD_ARY[] = + { + STR_AUTH_FIELD_IDENTIFIER, + STR_AUTH_FIELD_AUTHORITY_TYPE, + STR_AUTH_FIELD_ADDRESS, + STR_AUTH_FIELD_ANNOTE, + STR_AUTH_FIELD_AUTHOR, + STR_AUTH_FIELD_BOOKTITLE, + STR_AUTH_FIELD_CHAPTER, + STR_AUTH_FIELD_EDITION, + STR_AUTH_FIELD_EDITOR, + STR_AUTH_FIELD_HOWPUBLISHED, + STR_AUTH_FIELD_INSTITUTION, + STR_AUTH_FIELD_JOURNAL, + STR_AUTH_FIELD_MONTH, + STR_AUTH_FIELD_NOTE, + STR_AUTH_FIELD_NUMBER, + STR_AUTH_FIELD_ORGANIZATIONS, + STR_AUTH_FIELD_PAGES, + STR_AUTH_FIELD_PUBLISHER, + STR_AUTH_FIELD_SCHOOL, + STR_AUTH_FIELD_SERIES, + STR_AUTH_FIELD_TITLE, + STR_AUTH_FIELD_TYPE, + STR_AUTH_FIELD_VOLUME, + STR_AUTH_FIELD_YEAR, + STR_AUTH_FIELD_URL, + STR_AUTH_FIELD_CUSTOM1, + STR_AUTH_FIELD_CUSTOM2, + STR_AUTH_FIELD_CUSTOM3, + STR_AUTH_FIELD_CUSTOM4, + STR_AUTH_FIELD_CUSTOM5, + STR_AUTH_FIELD_ISBN + }; +} + +SwCreateAuthEntryDlg_Impl::SwCreateAuthEntryDlg_Impl(weld::Window* pParent, + const OUString pFields[], + SwWrtShell& rSh, + bool bNewEntry, + bool bCreate) + : GenericDialogController(pParent, "modules/swriter/ui/createauthorentry.ui", "CreateAuthorEntryDialog") + , rWrtSh(rSh) + , m_bNewEntryMode(bNewEntry) + , m_bNameAllowed(true) + , m_xOKBT(m_xBuilder->weld_button("ok")) + , m_xBox(m_xBuilder->weld_container("box")) + , m_xLeft(m_xBuilder->weld_container("leftgrid")) + , m_xRight(m_xBuilder->weld_container("rightgrid")) +{ + bool bLeft = true; + sal_Int32 nLeftRow(0), nRightRow(0); + for(int nIndex = 0; nIndex < AUTH_FIELD_END; nIndex++) + { + //m_xBox parent just to have some parent during setup, added contents are not directly visible under m_xBox + m_aBuilders.emplace_back(Application::CreateBuilder(m_xBox.get(), "modules/swriter/ui/bibliofragment.ui")); + const TextInfo aCurInfo = aTextInfoArr[nIndex]; + + m_aOrigContainers.emplace_back(m_aBuilders.back()->weld_container("biblioentry")); + m_aFixedTexts.emplace_back(m_aBuilders.back()->weld_label("label")); + if (bLeft) + m_aOrigContainers.back()->move(m_aFixedTexts.back().get(), m_xLeft.get()); + else + m_aOrigContainers.back()->move(m_aFixedTexts.back().get(), m_xRight.get()); + m_aFixedTexts.back()->set_grid_left_attach(0); + m_aFixedTexts.back()->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + m_aFixedTexts.back()->set_label(SwResId(STR_AUTH_FIELD_ARY[aCurInfo.nToxField])); + m_aFixedTexts.back()->show(); + if( AUTH_FIELD_AUTHORITY_TYPE == aCurInfo.nToxField ) + { + m_xTypeListBox = m_aBuilders.back()->weld_combo_box("listbox"); + if (bLeft) + m_aOrigContainers.back()->move(m_xTypeListBox.get(), m_xLeft.get()); + else + m_aOrigContainers.back()->move(m_xTypeListBox.get(), m_xRight.get()); + + for (int j = 0; j < AUTH_TYPE_END; j++) + m_xTypeListBox->append_text(SwAuthorityFieldType::GetAuthTypeName(static_cast<ToxAuthorityType>(j))); + if(!pFields[aCurInfo.nToxField].isEmpty()) + { + m_xTypeListBox->set_active(pFields[aCurInfo.nToxField].toInt32()); + } + m_xTypeListBox->set_grid_left_attach(1); + m_xTypeListBox->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + m_xTypeListBox->set_hexpand(true); + m_xTypeListBox->show(); + m_xTypeListBox->connect_changed(LINK(this, SwCreateAuthEntryDlg_Impl, EnableHdl)); + m_xTypeListBox->set_help_id(aCurInfo.pHelpId); + m_aFixedTexts.back()->set_mnemonic_widget(m_xTypeListBox.get()); + } + else if(AUTH_FIELD_IDENTIFIER == aCurInfo.nToxField && !m_bNewEntryMode) + { + m_xIdentifierBox = m_aBuilders.back()->weld_combo_box("combobox"); + if (bLeft) + m_aOrigContainers.back()->move(m_xIdentifierBox.get(), m_xLeft.get()); + else + m_aOrigContainers.back()->move(m_xIdentifierBox.get(), m_xRight.get()); + + m_xIdentifierBox->connect_changed(LINK(this, + SwCreateAuthEntryDlg_Impl, IdentifierHdl)); + + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + rSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + std::vector<OUString> aIds; + pFType->GetAllEntryIdentifiers( aIds ); + for (const OUString& a : aIds) + m_xIdentifierBox->append_text(a); + } + m_xIdentifierBox->set_entry_text(pFields[aCurInfo.nToxField]); + m_xIdentifierBox->set_grid_left_attach(1); + m_xIdentifierBox->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + m_xIdentifierBox->set_hexpand(true); + m_xIdentifierBox->show(); + m_xIdentifierBox->set_help_id(aCurInfo.pHelpId); + m_aFixedTexts.back()->set_mnemonic_widget(m_xIdentifierBox.get()); + } + else + { + pEdits[nIndex] = m_aBuilders.back()->weld_entry("entry"); + if (bLeft) + m_aOrigContainers.back()->move(pEdits[nIndex].get(), m_xLeft.get()); + else + m_aOrigContainers.back()->move(pEdits[nIndex].get(), m_xRight.get()); + + pEdits[nIndex]->set_grid_left_attach(1); + pEdits[nIndex]->set_grid_top_attach(bLeft ? nLeftRow : nRightRow); + pEdits[nIndex]->set_hexpand(true); + pEdits[nIndex]->set_text(pFields[aCurInfo.nToxField]); + pEdits[nIndex]->show(); + pEdits[nIndex]->set_help_id(aCurInfo.pHelpId); + if(AUTH_FIELD_IDENTIFIER == aCurInfo.nToxField) + { + pEdits[nIndex]->connect_changed(LINK(this, SwCreateAuthEntryDlg_Impl, ShortNameHdl)); + m_bNameAllowed = !pFields[nIndex].isEmpty(); + if(!bCreate) + { + m_aFixedTexts.back()->set_sensitive(false); + pEdits[nIndex]->set_sensitive(false); + } + } + m_aFixedTexts.back()->set_mnemonic_widget(pEdits[nIndex].get()); + } + if(bLeft) + ++nLeftRow; + else + ++nRightRow; + bLeft = !bLeft; + } + EnableHdl(*m_xTypeListBox); +} + +OUString SwCreateAuthEntryDlg_Impl::GetEntryText(ToxAuthorityField eField) const +{ + if( AUTH_FIELD_AUTHORITY_TYPE == eField ) + { + OSL_ENSURE(m_xTypeListBox, "No ListBox"); + return OUString::number(m_xTypeListBox->get_active()); + } + + if( AUTH_FIELD_IDENTIFIER == eField && !m_bNewEntryMode) + { + OSL_ENSURE(m_xIdentifierBox, "No ComboBox"); + return m_xIdentifierBox->get_active_text(); + } + + for(int nIndex = 0; nIndex < AUTH_FIELD_END; nIndex++) + { + const TextInfo aCurInfo = aTextInfoArr[nIndex]; + if(aCurInfo.nToxField == eField) + { + return pEdits[nIndex]->get_text(); + } + } + + return OUString(); +} + +IMPL_LINK(SwCreateAuthEntryDlg_Impl, IdentifierHdl, weld::ComboBox&, rBox, void) +{ + const SwAuthorityFieldType* pFType = static_cast<const SwAuthorityFieldType*>( + rWrtSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if(pFType) + { + const SwAuthEntry* pEntry = pFType->GetEntryByIdentifier( + rBox.get_active_text()); + if(pEntry) + { + for(int i = 0; i < AUTH_FIELD_END; i++) + { + const TextInfo aCurInfo = aTextInfoArr[i]; + if(AUTH_FIELD_IDENTIFIER == aCurInfo.nToxField) + continue; + if(AUTH_FIELD_AUTHORITY_TYPE == aCurInfo.nToxField) + m_xTypeListBox->set_active_text( + pEntry->GetAuthorField(aCurInfo.nToxField)); + else + pEdits[i]->set_text( + pEntry->GetAuthorField(aCurInfo.nToxField)); + } + } + } +} + +IMPL_LINK(SwCreateAuthEntryDlg_Impl, ShortNameHdl, weld::Entry&, rEdit, void) +{ + if (aShortNameCheckLink.IsSet()) + { + bool bEnable = aShortNameCheckLink.Call(rEdit); + m_bNameAllowed |= bEnable; + m_xOKBT->set_sensitive(m_xTypeListBox->get_active() != -1 && bEnable); + } +} + +IMPL_LINK(SwCreateAuthEntryDlg_Impl, EnableHdl, weld::ComboBox&, rBox, void) +{ + m_xOKBT->set_sensitive(m_bNameAllowed && rBox.get_active() != -1); +}; + +SwAuthMarkFloatDlg::SwAuthMarkFloatDlg(SfxBindings* _pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo const * pInfo, + bool bNew) + : SfxModelessDialogController(_pBindings, pChild, pParent, + "modules/swriter/ui/bibliographyentry.ui", "BibliographyEntryDialog") + , m_aContent(*this, *m_xBuilder, bNew) +{ + Initialize(pInfo); + SwWrtShell* pWrtShell = ::GetActiveWrtShell(); + if (pWrtShell) + m_aContent.ReInitDlg(*pWrtShell); +} + +void SwAuthMarkFloatDlg::Activate() +{ + SfxModelessDialogController::Activate(); + m_aContent.Activate(); +} + +void SwAuthMarkFloatDlg::ReInitDlg(SwWrtShell& rWrtShell) +{ + m_aContent.ReInitDlg( rWrtShell ); +} + +SwAuthMarkModalDlg::SwAuthMarkModalDlg(weld::Window *pParent, SwWrtShell& rSh) + : SfxDialogController(pParent, "modules/swriter/ui/bibliographyentry.ui", + "BibliographyEntryDialog") + , m_aContent(*this, *m_xBuilder, false) +{ + m_aContent.ReInitDlg(rSh); +} + +short SwAuthMarkModalDlg::run() +{ + short ret = SfxDialogController::run(); + if (ret == RET_OK) + Apply(); + return ret; +} + +void SwAuthMarkModalDlg::Apply() +{ + m_aContent.InsertHdl(*m_aContent.m_xActionBT); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/bookmark.cxx b/sw/source/ui/misc/bookmark.cxx new file mode 100644 index 000000000..731fe0651 --- /dev/null +++ b/sw/source/ui/misc/bookmark.cxx @@ -0,0 +1,507 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/ustrbuf.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> + +#include <swabstdlg.hxx> +#include <swuiexp.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <bookmark.hxx> +#include <docsh.hxx> +#include <ndtxt.hxx> +#include <strings.hrc> +#include <svtools/miscopt.hxx> +#include <IDocumentSettingAccess.hxx> + +using namespace ::com::sun::star; + +const OUString BookmarkTable::aForbiddenChars("/\\@*?\",#"); +const char BookmarkTable::cSeparator(';'); + +// callback to modify EditBox +IMPL_LINK_NOARG(SwInsertBookmarkDlg, ModifyHdl, weld::Entry&, void) +{ + ValidateBookmarks(); + m_xBookmarksBox->unselect_all(); + // if a string has been pasted from the clipboard then + // there may be illegal characters in the box + // sanitization + OUString sTmp = m_xEditBox->get_text(); + OUString sMsg; + const sal_Int32 nLen = sTmp.getLength(); + for (sal_Int32 i = 0; i < BookmarkTable::aForbiddenChars.getLength(); i++) + { + const sal_Int32 nTmpLen = sTmp.getLength(); + sTmp = sTmp.replaceAll(OUStringChar(BookmarkTable::aForbiddenChars[i]), ""); + if (sTmp.getLength() != nTmpLen) + sMsg += OUStringChar(BookmarkTable::aForbiddenChars[i]); + } + const bool bHasForbiddenChars = sTmp.getLength() != nLen; + m_xForbiddenChars->set_visible(bHasForbiddenChars); + if (bHasForbiddenChars) + m_xEditBox->set_message_type(weld::EntryMessageType::Error); + else + m_xEditBox->set_message_type(weld::EntryMessageType::Normal); + + sal_Int32 nSelectedEntries = 0; + sal_Int32 nEntries = 0; + sal_Int32 nTokenIndex = 0; + while (!sTmp.isEmpty() && nTokenIndex >= 0) + { + OUString aToken = sTmp.getToken(0, BookmarkTable::cSeparator, nTokenIndex); + if (m_xBookmarksBox->GetBookmarkByName(aToken)) + { + m_xBookmarksBox->SelectByName(aToken); + nSelectedEntries++; + } + nEntries++; + } + + // allow to add new bookmark only if one name provided and it's not taken + m_xInsertBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 0 && !bHasForbiddenChars && !m_bAreProtected); + + // allow to delete only if all bookmarks are recognized + m_xDeleteBtn->set_sensitive(nEntries > 0 && nSelectedEntries == nEntries && !m_bAreProtected); + m_xGotoBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1); + m_xRenameBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1 && !m_bAreProtected); +} + +// callback to delete a text mark +IMPL_LINK_NOARG(SwInsertBookmarkDlg, DeleteHdl, weld::Button&, void) +{ + if (!ValidateBookmarks()) + return; + + int nSelectedRows(0); + + m_xBookmarksBox->selected_foreach([this, &nSelectedRows](weld::TreeIter& rEntry){ + // remove from model + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(rEntry).toInt64()); + OUString sRemoved = pBookmark->GetName(); + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved)); + SfxRequest aReq(rSh.GetView().GetViewFrame(), FN_DELETE_BOOKMARK); + aReq.AppendItem(SfxStringItem(FN_DELETE_BOOKMARK, sRemoved)); + aReq.Done(); + aTableBookmarks.erase(std::remove(aTableBookmarks.begin(), aTableBookmarks.end(), + std::make_pair(pBookmark, sRemoved)), aTableBookmarks.end()); + + ++nSelectedRows; + + return false; + }); + + if (!nSelectedRows) + return; + + // remove from BookmarkTable + m_xBookmarksBox->remove_selection(); + + ValidateBookmarks(); + + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xInsertBtn->set_sensitive(false); +} + +// callback to a goto button +IMPL_LINK_NOARG(SwInsertBookmarkDlg, GotoHdl, weld::Button&, void) +{ + GotoSelectedBookmark(); +} + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + GotoSelectedBookmark(); + return true; +} + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void) +{ + if (!ValidateBookmarks()) + return; + // this event should fired only if we change selection by clicking on BookmarkTable entry + if (!m_xBookmarksBox->has_focus()) + return; + + OUStringBuffer sEditBoxText; + int nSelectedRows = 0; + m_xBookmarksBox->selected_foreach([this, &sEditBoxText, &nSelectedRows](weld::TreeIter& rEntry){ + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(rEntry).toInt64()); + const OUString& sEntryName = pBookmark->GetName(); + if (!sEditBoxText.isEmpty()) + sEditBoxText.append(";"); + sEditBoxText.append(sEntryName); + ++nSelectedRows; + return false; + }); + if (nSelectedRows) + { + m_xInsertBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(nSelectedRows == 1); + m_xRenameBtn->set_sensitive(nSelectedRows == 1 && !m_bAreProtected); + m_xDeleteBtn->set_sensitive(!m_bAreProtected); + m_xEditBox->set_text(sEditBoxText.makeStringAndClear()); + } + else + { + m_xInsertBtn->set_sensitive(!m_bAreProtected); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xDeleteBtn->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, RenameHdl, weld::Button&, void) +{ + if (!ValidateBookmarks()) + return; + auto xSelected = m_xBookmarksBox->get_selected(); + if (!xSelected) + return; + + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(*xSelected).toInt64()); + uno::Reference<frame::XModel> xModel = rSh.GetView().GetDocShell()->GetBaseModel(); + uno::Reference<text::XBookmarksSupplier> xBkms(xModel, uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xNameAccess = xBkms->getBookmarks(); + uno::Any aObj = xNameAccess->getByName(pBookmark->GetName()); + uno::Reference<uno::XInterface> xTmp; + aObj >>= xTmp; + uno::Reference<container::XNamed> xNamed(xTmp, uno::UNO_QUERY); + SwAbstractDialogFactory& rFact = swui::GetFactory(); + ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(rFact.CreateSwRenameXNamedDlg(m_xDialog.get(), xNamed, xNameAccess)); + pDlg->SetForbiddenChars(BookmarkTable::aForbiddenChars + OUStringChar(BookmarkTable::cSeparator)); + + if (pDlg->Execute()) + { + ValidateBookmarks(); + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xInsertBtn->set_sensitive(false); + } +} + +// callback to an insert button. Inserts a new text mark to the current position. +IMPL_LINK_NOARG(SwInsertBookmarkDlg, InsertHdl, weld::Button&, void) +{ + OUString sBookmark = m_xEditBox->get_text(); + rSh.SetBookmark2(vcl::KeyCode(), sBookmark, m_xHideCB->get_active(), m_xConditionED->get_text()); + rReq.AppendItem(SfxStringItem(FN_INSERT_BOOKMARK, sBookmark)); + rReq.Done(); + if (!rReq.IsDone()) + rReq.Ignore(); + + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwInsertBookmarkDlg, ChangeHideHdl, weld::ToggleButton&, rBox, void) +{ + bool bHide = rBox.get_active(); + m_xConditionED->set_sensitive(bHide); + m_xConditionFT->set_sensitive(bHide); +} + +void SwInsertBookmarkDlg::GotoSelectedBookmark() +{ + if (!ValidateBookmarks()) + return; + // if no entries selected we can't jump anywhere + // shouldn't be needed as we disable GoTo button when jump is not possible + auto xSelected = m_xBookmarksBox->get_selected(); + if (!xSelected) + return; + + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xBookmarksBox->get_id(*xSelected).toInt64()); + + rSh.EnterStdMode(); + rSh.GotoMark(pBookmark); +} + +bool SwInsertBookmarkDlg::ValidateBookmarks() +{ + if (HaveBookmarksChanged()) + { + PopulateTable(); + m_xEditBox->set_text(""); + return false; + } + return true; +} + +bool SwInsertBookmarkDlg::HaveBookmarksChanged() +{ + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + if (pMarkAccess->getBookmarksCount() != m_nLastBookmarksCount) + return true; + + std::vector<std::pair<sw::mark::IMark*, OUString>>::const_iterator aListIter = aTableBookmarks.begin(); + for (IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark) + { + if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + // more bookmarks then expected + if (aListIter == aTableBookmarks.end()) + return true; + if (aListIter->first != *ppBookmark || + aListIter->second != (*ppBookmark)->GetName()) + return true; + ++aListIter; + } + } + // less bookmarks then expected + return aListIter != aTableBookmarks.end(); +} + +void SwInsertBookmarkDlg::PopulateTable() +{ + aTableBookmarks.clear(); + m_xBookmarksBox->clear(); + + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + for (IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark) + { + if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + m_xBookmarksBox->InsertBookmark(*ppBookmark); + aTableBookmarks.emplace_back(*ppBookmark, (*ppBookmark)->GetName()); + } + } + m_nLastBookmarksCount = pMarkAccess->getBookmarksCount(); +} + +SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS, SfxRequest& rRequest) + : SfxDialogController(pParent, "modules/swriter/ui/insertbookmark.ui", "InsertBookmarkDialog") + , rSh(rS) + , rReq(rRequest) + , m_nLastBookmarksCount(0) + , m_bSorted(false) + , m_xEditBox(m_xBuilder->weld_entry("name")) + , m_xInsertBtn(m_xBuilder->weld_button("insert")) + , m_xDeleteBtn(m_xBuilder->weld_button("delete")) + , m_xGotoBtn(m_xBuilder->weld_button("goto")) + , m_xRenameBtn(m_xBuilder->weld_button("rename")) + , m_xHideCB(m_xBuilder->weld_check_button("hide")) + , m_xConditionFT(m_xBuilder->weld_label("condlabel")) + , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("withcond"))) + , m_xBookmarksBox(new BookmarkTable(m_xBuilder->weld_tree_view("bookmarks"))) + , m_xForbiddenChars(m_xBuilder->weld_label("lbForbiddenChars")) +{ + m_xBookmarksBox->connect_changed(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl)); + m_xBookmarksBox->connect_row_activated(LINK(this, SwInsertBookmarkDlg, DoubleClickHdl)); + m_xBookmarksBox->connect_column_clicked(LINK(this, SwInsertBookmarkDlg, HeaderBarClick)); + m_xEditBox->connect_changed(LINK(this, SwInsertBookmarkDlg, ModifyHdl)); + m_xInsertBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, InsertHdl)); + m_xDeleteBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, DeleteHdl)); + m_xGotoBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, GotoHdl)); + m_xRenameBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, RenameHdl)); + m_xHideCB->connect_toggled(LINK(this, SwInsertBookmarkDlg, ChangeHideHdl)); + + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + + PopulateTable(); + + m_xEditBox->set_text(m_xBookmarksBox->GetNameProposal()); + m_xEditBox->set_position(-1); + + m_xForbiddenChars->set_label(SwResId(STR_BOOKMARK_FORBIDDENCHARS) + " " + BookmarkTable::aForbiddenChars); + m_xForbiddenChars->set_visible(false); + + SvtMiscOptions aMiscOpt; + if ( !aMiscOpt.IsExperimentalMode() ) + { + m_xHideCB->set_visible( false ); + m_xConditionFT->set_visible( false ); + m_xConditionED->set_visible( false ); + } + + m_bAreProtected = rSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + + // disabled until "Hide" flag is not checked + m_xConditionED->set_sensitive(false); + m_xConditionFT->set_sensitive(false); +} + +IMPL_LINK(SwInsertBookmarkDlg, HeaderBarClick, int, nColumn, void) +{ + if (!m_bSorted) + { + m_xBookmarksBox->make_sorted(); + m_bSorted = true; + } + + bool bSortAtoZ = m_xBookmarksBox->get_sort_order(); + + //set new arrow positions in headerbar + if (nColumn == m_xBookmarksBox->get_sort_column()) + { + bSortAtoZ = !bSortAtoZ; + m_xBookmarksBox->set_sort_order(bSortAtoZ); + } + else + { + int nOldSortColumn = m_xBookmarksBox->get_sort_column(); + if (nOldSortColumn != -1) + m_xBookmarksBox->set_sort_indicator(TRISTATE_INDET, nOldSortColumn); + m_xBookmarksBox->set_sort_column(nColumn); + } + + if (nColumn != -1) + { + //sort lists + m_xBookmarksBox->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn); + } +} + +SwInsertBookmarkDlg::~SwInsertBookmarkDlg() +{ +} + +BookmarkTable::BookmarkTable(std::unique_ptr<weld::TreeView> xControl) + : m_xControl(std::move(xControl)) +{ + m_xControl->set_size_request(450, 250); + std::vector<int> aWidths; + aWidths.push_back(40); + aWidths.push_back(110); + aWidths.push_back(150); + aWidths.push_back(60); + m_xControl->set_column_fixed_widths(aWidths); + m_xControl->set_selection_mode(SelectionMode::Multiple); +} + +std::unique_ptr<weld::TreeIter> BookmarkTable::get_selected() const +{ + std::unique_ptr<weld::TreeIter> xIter(m_xControl->make_iterator()); + if (!m_xControl->get_selected(xIter.get())) + xIter.reset(); + return xIter; +} + +void BookmarkTable::InsertBookmark(sw::mark::IMark* pMark) +{ + sw::mark::IBookmark* pBookmark = dynamic_cast<sw::mark::IBookmark*>(pMark); + assert(pBookmark); + + OUString sBookmarkNodeText = pBookmark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText(); + sal_Int32 nBookmarkNodeTextPos = pBookmark->GetMarkStart().nContent.GetIndex(); + sal_Int32 nBookmarkTextLen = 0; + bool bPulledAll = false; + bool bPulling = false; + static const sal_Int32 nMaxTextLen = 50; + + if (pBookmark->IsExpanded()) + { + nBookmarkTextLen = pBookmark->GetMarkEnd().nContent.GetIndex() - nBookmarkNodeTextPos; + } + else + { + if (nBookmarkNodeTextPos == sBookmarkNodeText.getLength()) // no text after bookmark + { + nBookmarkNodeTextPos = std::max<sal_Int32>(0, nBookmarkNodeTextPos - nMaxTextLen); + bPulling = true; + if (nBookmarkNodeTextPos == 0) + bPulledAll = true; + } + nBookmarkTextLen = sBookmarkNodeText.getLength() - nBookmarkNodeTextPos; + } + bool bExceedsLength = nBookmarkTextLen > nMaxTextLen; + nBookmarkTextLen = std::min<sal_Int32>(nMaxTextLen, nBookmarkTextLen); + sBookmarkNodeText = sBookmarkNodeText.copy(nBookmarkNodeTextPos, nBookmarkTextLen).trim(); + if (bExceedsLength) + sBookmarkNodeText += "..."; + else if (bPulling && !bPulledAll) + sBookmarkNodeText = "..." + sBookmarkNodeText; + + OUString sHidden = SwResId(STR_BOOKMARK_NO); + if (pBookmark->IsHidden()) + sHidden = SwResId(STR_BOOKMARK_YES); + const OUString& sHideCondition = pBookmark->GetHideCondition(); + OUString sPageNum = OUString::number(SwPaM(pMark->GetMarkStart()).GetPageNum()); + int nRow = m_xControl->n_children(); + m_xControl->append(OUString::number(reinterpret_cast<sal_Int64>(pMark)), sPageNum); + m_xControl->set_text(nRow, pBookmark->GetName(), 1); + m_xControl->set_text(nRow, sBookmarkNodeText, 2); + m_xControl->set_text(nRow, sHidden, 3); + m_xControl->set_text(nRow, sHideCondition, 4); +} + +std::unique_ptr<weld::TreeIter> BookmarkTable::GetRowByBookmarkName(const OUString& sName) +{ + std::unique_ptr<weld::TreeIter> xRet; + m_xControl->all_foreach([this, &sName, &xRet](weld::TreeIter& rEntry){ + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xControl->get_id(rEntry).toInt64()); + if (pBookmark->GetName() == sName) + { + xRet = m_xControl->make_iterator(&rEntry); + return true; + } + return false; + }); + return xRet; +} + +sw::mark::IMark* BookmarkTable::GetBookmarkByName(const OUString& sName) +{ + auto xEntry = GetRowByBookmarkName(sName); + if (!xEntry) + return nullptr; + + return reinterpret_cast<sw::mark::IMark*>(m_xControl->get_id(*xEntry).toInt64()); +} + +void BookmarkTable::SelectByName(const OUString& sName) +{ + auto xEntry = GetRowByBookmarkName(sName); + if (!xEntry) + return; + select(*xEntry); +} + +OUString BookmarkTable::GetNameProposal() const +{ + OUString sDefaultBookmarkName = SwResId(STR_BOOKMARK_DEF_NAME); + sal_Int32 nHighestBookmarkId = 0; + for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i) + { + sw::mark::IMark* pBookmark = reinterpret_cast<sw::mark::IMark*>(m_xControl->get_id(i).toInt64()); + const OUString& sName = pBookmark->GetName(); + sal_Int32 nIndex = 0; + if (sName.getToken(0, ' ', nIndex) == sDefaultBookmarkName) + { + sal_Int32 nCurrBookmarkId = sName.getToken(0, ' ', nIndex).toInt32(); + nHighestBookmarkId = std::max<sal_Int32>(nHighestBookmarkId, nCurrBookmarkId); + } + } + return sDefaultBookmarkName + " " + OUString::number(nHighestBookmarkId + 1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/docfnote.cxx b/sw/source/ui/misc/docfnote.cxx new file mode 100644 index 000000000..1455565d6 --- /dev/null +++ b/sw/source/ui/misc/docfnote.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 <svl/style.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <docfnote.hxx> +#include "impfnote.hxx" +#include <ftninfo.hxx> +#include <fmtcol.hxx> +#include <pagedesc.hxx> +#include <charfmt.hxx> +#include <docstyle.hxx> +#include <wdocsh.hxx> +#include <uitool.hxx> +#include <poolfmt.hxx> +#include <SwStyleNameMapper.hxx> +#include <memory> + +SwFootNoteOptionDlg::SwFootNoteOptionDlg(weld::Window *pParent, SwWrtShell &rS) + : SfxTabDialogController(pParent, "modules/swriter/ui/footendnotedialog.ui", "FootEndnoteDialog") + , rSh( rS ) +{ + RemoveResetButton(); + + GetOKButton().connect_clicked(LINK(this, SwFootNoteOptionDlg, OkHdl)); + + AddTabPage("footnotes", SwFootNoteOptionPage::Create, nullptr); + AddTabPage("endnotes", SwEndNoteOptionPage::Create, nullptr); +} + +void SwFootNoteOptionDlg::PageCreated(const OString& /*rId*/, SfxTabPage &rPage) +{ + static_cast<SwEndNoteOptionPage&>(rPage).SetShell(rSh); +} + +IMPL_LINK(SwFootNoteOptionDlg, OkHdl, weld::Button&, rBtn, void) +{ + SfxItemSet aDummySet(rSh.GetAttrPool(), svl::Items<1, 1>{} ); + SfxTabPage *pPage = GetTabPage("footnotes"); + if ( pPage ) + pPage->FillItemSet( &aDummySet ); + pPage = GetTabPage("endnotes"); + if ( pPage ) + pPage->FillItemSet( &aDummySet ); + SfxTabDialogController::OkHdl(rBtn); +} + +SwEndNoteOptionPage::SwEndNoteOptionPage(weld::Container* pPage, weld::DialogController* pController, bool bEN, + const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, + bEN ? OUString("modules/swriter/ui/endnotepage.ui") : OUString("modules/swriter/ui/footnotepage.ui"), + bEN ? OString("EndnotePage") : OString("FootnotePage"), + &rSet) + , pSh(nullptr) + , bPosDoc(false) + , bEndNote(bEN) + , m_xNumViewBox(new SwNumberingTypeListBox(m_xBuilder->weld_combo_box("numberinglb"))) + , m_xOffsetLbl(m_xBuilder->weld_label("offset")) + , m_xOffsetField(m_xBuilder->weld_spin_button("offsetnf")) + , m_xNumCountBox(m_xBuilder->weld_combo_box("countinglb")) + , m_xPrefixED(m_xBuilder->weld_entry("prefix")) + , m_xSuffixED(m_xBuilder->weld_entry("suffix")) + , m_xPosFT(m_xBuilder->weld_label("pos")) + , m_xPosPageBox(m_xBuilder->weld_radio_button("pospagecb")) + , m_xPosChapterBox(m_xBuilder->weld_radio_button("posdoccb")) + , m_xStylesContainer(m_xBuilder->weld_widget("allstyles")) + , m_xParaTemplBox(m_xBuilder->weld_combo_box("parastylelb")) + , m_xPageTemplLbl(m_xBuilder->weld_label("pagestyleft")) + , m_xPageTemplBox(m_xBuilder->weld_combo_box("pagestylelb")) + , m_xFootnoteCharAnchorTemplBox(m_xBuilder->weld_combo_box("charanchorstylelb")) + , m_xFootnoteCharTextTemplBox(m_xBuilder->weld_combo_box("charstylelb")) + , m_xContEdit(m_xBuilder->weld_entry("conted")) + , m_xContFromEdit(m_xBuilder->weld_entry("contfromed")) +{ + m_xNumViewBox->Reload(SwInsertNumTypes::Extended); + if (!bEndNote) + { + m_xNumCountBox->connect_changed(LINK(this, SwEndNoteOptionPage, NumCountHdl)); + aNumDoc = m_xNumCountBox->get_text(FTNNUM_DOC); + aNumPage = m_xNumCountBox->get_text(FTNNUM_PAGE); + aNumChapter = m_xNumCountBox->get_text(FTNNUM_CHAPTER); + m_xPosPageBox->connect_clicked(LINK(this, SwEndNoteOptionPage, PosPageHdl)); + m_xPosChapterBox->connect_clicked(LINK(this, SwEndNoteOptionPage, PosChapterHdl)); + } + +} + +SwEndNoteOptionPage::~SwEndNoteOptionPage() +{ +} + +void SwEndNoteOptionPage::Reset( const SfxItemSet* ) +{ + std::unique_ptr<SwEndNoteInfo> pInf(bEndNote ? new SwEndNoteInfo( pSh->GetEndNoteInfo() ) + : new SwFootnoteInfo( pSh->GetFootnoteInfo() )); + SfxObjectShell * pDocSh = SfxObjectShell::Current(); + + if (dynamic_cast<SwWebDocShell*>( pDocSh) ) + m_xStylesContainer->hide(); + + if ( bEndNote ) + { + bPosDoc = true; + } + else + { + const SwFootnoteInfo &rInf = pSh->GetFootnoteInfo(); + // set position (page, chapter) + if ( rInf.m_ePos == FTNPOS_PAGE ) + { + m_xPosPageBox->set_active(true); + m_xPageTemplLbl->set_sensitive(false); + m_xPageTemplBox->set_sensitive(false); + } + else + { + m_xPosChapterBox->set_active(true); + m_xNumCountBox->remove_text(aNumPage); + m_xNumCountBox->remove_text(aNumChapter); + bPosDoc = true; + } + // reference tests + m_xContEdit->set_text(rInf.m_aQuoVadis); + m_xContFromEdit->set_text(rInf.m_aErgoSum); + + // collected + SelectNumbering(rInf.m_eNum); + } + + // numbering + // art + m_xNumViewBox->SelectNumberingType( pInf->m_aFormat.GetNumberingType()); + m_xOffsetField->set_value(pInf->m_nFootnoteOffset + 1); + m_xPrefixED->set_text(pInf->GetPrefix().replaceAll("\t", "\\t")); // fdo#65666 + m_xSuffixED->set_text(pInf->GetSuffix().replaceAll("\t", "\\t")); + + const SwCharFormat* pCharFormat = pInf->GetCharFormat( + *pSh->GetView().GetDocShell()->GetDoc()); + m_xFootnoteCharTextTemplBox->set_active_text(pCharFormat->GetName()); + m_xFootnoteCharTextTemplBox->save_value(); + + pCharFormat = pInf->GetAnchorCharFormat( *pSh->GetDoc() ); + m_xFootnoteCharAnchorTemplBox->set_active_text( pCharFormat->GetName() ); + m_xFootnoteCharAnchorTemplBox->save_value(); + + // styles special regions + // paragraph + SfxStyleSheetBasePool* pStyleSheetPool = pSh->GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase *pStyle = pStyleSheetPool->First(SfxStyleFamily::Para, SfxStyleSearchBits::SwExtra); + while(pStyle) + { + m_xParaTemplBox->append_text(pStyle->GetName()); + pStyle = pStyleSheetPool->Next(); + } + m_xParaTemplBox->make_sorted(); + + OUString sStr; + SwStyleNameMapper::FillUIName( static_cast< sal_uInt16 >(bEndNote ? RES_POOLCOLL_ENDNOTE + : RES_POOLCOLL_FOOTNOTE), sStr ); + if (m_xParaTemplBox->find_text(sStr) == -1) + m_xParaTemplBox->append_text(sStr); + + SwTextFormatColl* pColl = pInf->GetFootnoteTextColl(); + if( !pColl ) + m_xParaTemplBox->set_active_text(sStr); // Default + else + { + OSL_ENSURE(!pColl->IsDefault(), "default style for footnotes is wrong"); + const int nPos = m_xParaTemplBox->find_text(pColl->GetName()); + if (nPos != -1) + m_xParaTemplBox->set_active( nPos ); + else + { + m_xParaTemplBox->append_text(pColl->GetName()); + m_xParaTemplBox->set_active_text(pColl->GetName()); + } + } + + // page + for (sal_uInt16 i = RES_POOLPAGE_BEGIN; i < RES_POOLPAGE_END; ++i) + m_xPageTemplBox->append_text(SwStyleNameMapper::GetUIName(i, OUString())); + + const size_t nCount = pSh->GetPageDescCnt(); + for(size_t i = 0; i < nCount; ++i) + { + const SwPageDesc &rPageDesc = pSh->GetPageDesc(i); + if (m_xPageTemplBox->find_text(rPageDesc.GetName()) == -1) + m_xPageTemplBox->append_text(rPageDesc.GetName()); + } + m_xPageTemplBox->make_sorted(); + + m_xPageTemplBox->set_active_text(pInf->GetPageDesc(*pSh->GetDoc())->GetName()); +} + +std::unique_ptr<SfxTabPage> SwEndNoteOptionPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet ) +{ + return std::make_unique<SwEndNoteOptionPage>(pPage, pController, true, *rSet); +} + +// Different kinds of numbering; because the Listbox has varying numbers of +// entries, here are functions to set and query the intended kind of numbering. +void SwEndNoteOptionPage::SelectNumbering(SwFootnoteNum const eNum) +{ + OUString sSelect; + switch(eNum) + { + case FTNNUM_DOC: + sSelect = aNumDoc; + break; + case FTNNUM_PAGE: + sSelect = aNumPage; + break; + case FTNNUM_CHAPTER: + sSelect = aNumChapter; + break; + default: + assert(false); + } + m_xNumCountBox->set_active_text(sSelect); + NumCountHdl(*m_xNumCountBox); +} + +SwFootnoteNum SwEndNoteOptionPage::GetNumbering() const +{ + const int nPos = m_xNumCountBox->get_active(); + return static_cast<SwFootnoteNum>(bPosDoc ? nPos + 2 : nPos); +} + +void SwEndNoteOptionPage::SetShell( SwWrtShell &rShell ) +{ + pSh = &rShell; + // collect character templates + m_xFootnoteCharTextTemplBox->clear(); + m_xFootnoteCharAnchorTemplBox->clear(); + ::FillCharStyleListBox(*m_xFootnoteCharTextTemplBox, + pSh->GetView().GetDocShell(), true); + + ::FillCharStyleListBox(*m_xFootnoteCharAnchorTemplBox, + pSh->GetView().GetDocShell(), true); +} + +// Handler behind the button to collect the footnote at the page. In this case +// all kinds of numbering can be used. +IMPL_LINK_NOARG(SwEndNoteOptionPage, PosPageHdl, weld::Button&, void) +{ + const SwFootnoteNum eNum = GetNumbering(); + bPosDoc = false; + if (m_xNumCountBox->find_text(aNumPage) == -1) + { + m_xNumCountBox->insert_text(FTNNUM_PAGE, aNumPage); + m_xNumCountBox->insert_text(FTNNUM_CHAPTER, aNumChapter); + SelectNumbering(eNum); + } + m_xPageTemplLbl->set_sensitive(false); + m_xPageTemplBox->set_sensitive(false); +} + +IMPL_LINK_NOARG(SwEndNoteOptionPage, NumCountHdl, weld::ComboBox&, void) +{ + bool bEnable = true; + if (m_xNumCountBox->get_count() - 1 != m_xNumCountBox->get_active()) + { + bEnable = false; + m_xOffsetField->set_value(1); + } + m_xOffsetLbl->set_sensitive(bEnable); + m_xOffsetField->set_sensitive(bEnable); +} + +// Handler behind the button to collect the footnote at the chapter or end of +// the document. In this case no pagewise numbering can be used. +IMPL_LINK_NOARG(SwEndNoteOptionPage, PosChapterHdl, weld::Button&, void) +{ + if ( !bPosDoc ) + SelectNumbering(FTNNUM_DOC); + + bPosDoc = true; + m_xNumCountBox->remove_text(aNumPage); + m_xNumCountBox->remove_text(aNumChapter); + m_xPageTemplLbl->set_sensitive(true); + m_xPageTemplBox->set_sensitive(true); +} + +static SwCharFormat* lcl_GetCharFormat( SwWrtShell* pSh, const OUString& rCharFormatName ) +{ + SwCharFormat* pFormat = nullptr; + const sal_uInt16 nChCount = pSh->GetCharFormatCount(); + for(sal_uInt16 i = 0; i< nChCount; i++) + { + SwCharFormat& rChFormat = pSh->GetCharFormat(i); + if(rChFormat.GetName() == rCharFormatName ) + { + pFormat = &rChFormat; + break; + } + } + if(!pFormat) + { + SfxStyleSheetBasePool* pPool = pSh->GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase* pBase; + pBase = pPool->Find(rCharFormatName, SfxStyleFamily::Char); + if(!pBase) + pBase = &pPool->Make(rCharFormatName, SfxStyleFamily::Char); + pFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat(); + } + return pFormat; +} + +bool SwEndNoteOptionPage::FillItemSet( SfxItemSet * ) +{ + std::unique_ptr<SwEndNoteInfo> pInf(bEndNote ? new SwEndNoteInfo() : new SwFootnoteInfo()); + + pInf->m_nFootnoteOffset = m_xOffsetField->get_value() - 1; + pInf->m_aFormat.SetNumberingType(m_xNumViewBox->GetSelectedNumberingType() ); + pInf->SetPrefix(m_xPrefixED->get_text().replaceAll("\\t", "\t")); + pInf->SetSuffix(m_xSuffixED->get_text().replaceAll("\\t", "\t")); + + pInf->SetCharFormat( lcl_GetCharFormat( pSh, + m_xFootnoteCharTextTemplBox->get_active_text() ) ); + pInf->SetAnchorCharFormat( lcl_GetCharFormat( pSh, + m_xFootnoteCharAnchorTemplBox->get_active_text() ) ); + + // paragraph template + int nPos = m_xParaTemplBox->get_active(); + if (nPos != -1) + { + const OUString aFormatName( m_xParaTemplBox->get_active_text() ); + SwTextFormatColl *pColl = pSh->GetParaStyle(aFormatName, SwWrtShell::GETSTYLE_CREATEANY); + OSL_ENSURE(pColl, "paragraph style not found"); + pInf->SetFootnoteTextColl(*pColl); + } + + // page template + pInf->ChgPageDesc( pSh->FindPageDescByName( + m_xPageTemplBox->get_active_text(), true ) ); + + if ( bEndNote ) + { + if ( !(*pInf == pSh->GetEndNoteInfo()) ) + pSh->SetEndNoteInfo( *pInf ); + } + else + { + SwFootnoteInfo *pI = static_cast<SwFootnoteInfo*>(pInf.get()); + pI->m_ePos = m_xPosPageBox->get_active() ? FTNPOS_PAGE : FTNPOS_CHAPTER; + pI->m_eNum = GetNumbering(); + pI->m_aQuoVadis = m_xContEdit->get_text(); + pI->m_aErgoSum = m_xContFromEdit->get_text(); + if ( !(*pI == pSh->GetFootnoteInfo()) ) + pSh->SetFootnoteInfo( *pI ); + } + return true; +} + +SwFootNoteOptionPage::SwFootNoteOptionPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SwEndNoteOptionPage(pPage, pController, false, rSet) +{ +} + +SwFootNoteOptionPage::~SwFootNoteOptionPage() +{ +} + +std::unique_ptr<SfxTabPage> SwFootNoteOptionPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet ) +{ + return std::make_unique<SwFootNoteOptionPage>(pPage, pController, *rSet); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/glosbib.cxx b/sw/source/ui/misc/glosbib.cxx new file mode 100644 index 000000000..598fb0509 --- /dev/null +++ b/sw/source/ui/misc/glosbib.cxx @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/pathoptions.hxx> +#include <osl/diagnose.h> + +#include <swtypes.hxx> +#include <glosbib.hxx> +#include <gloshdl.hxx> +#include <glossary.hxx> +#include <glosdoc.hxx> +#include <swunohelper.hxx> + +#include <strings.hrc> + +#define PATH_CASE_SENSITIVE 0x01 +#define PATH_READONLY 0x02 + +#define RENAME_TOKEN_DELIM u'\x0001' + +SwGlossaryGroupDlg::SwGlossaryGroupDlg(weld::Window * pParent, + std::vector<OUString> const& rPathArr, SwGlossaryHdl *pHdl) + : SfxDialogController(pParent, "modules/swriter/ui/editcategories.ui", + "EditCategoriesDialog") + , m_pParent(pParent) + , pGlosHdl(pHdl) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xPathLB(m_xBuilder->weld_combo_box("pathlb")) + , m_xGroupTLB(m_xBuilder->weld_tree_view("group")) + , m_xNewPB(m_xBuilder->weld_button("new")) + , m_xDelPB(m_xBuilder->weld_button("delete")) + , m_xRenamePB(m_xBuilder->weld_button("rename")) +{ + int nWidth = m_xGroupTLB->get_approximate_digit_width() * 34; + m_xPathLB->set_size_request(nWidth, -1); + //just has to be something small, real size will be available space + m_xGroupTLB->set_size_request(nWidth, m_xGroupTLB->get_height_rows(10)); + + std::vector<int> aWidths; + aWidths.push_back(nWidth); + m_xGroupTLB->set_column_fixed_widths(aWidths); + m_xGroupTLB->connect_changed(LINK(this, SwGlossaryGroupDlg, SelectHdl)); + + m_xNewPB->connect_clicked(LINK(this, SwGlossaryGroupDlg, NewHdl)); + m_xDelPB->connect_clicked(LINK(this, SwGlossaryGroupDlg, DeleteHdl)); + m_xNameED->connect_changed(LINK(this, SwGlossaryGroupDlg, ModifyHdl)); + m_xNameED->connect_insert_text(LINK(this, SwGlossaryGroupDlg, EditInsertTextHdl)); + m_xPathLB->connect_changed(LINK(this, SwGlossaryGroupDlg, ModifyListBoxHdl)); + m_xRenamePB->connect_clicked(LINK(this, SwGlossaryGroupDlg, RenameHdl)); + + m_xNameED->connect_size_allocate(LINK(this, SwGlossaryGroupDlg, EntrySizeAllocHdl)); + m_xPathLB->connect_size_allocate(LINK(this, SwGlossaryGroupDlg, EntrySizeAllocHdl)); + + for (size_t i = 0; i < rPathArr.size(); ++i) + { + INetURLObject aTempURL(rPathArr[i]); + const OUString sPath = aTempURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset ); + sal_uInt32 nCaseReadonly = 0; + utl::TempFile aTempFile(&sPath); + aTempFile.EnableKillingFile(); + if(!aTempFile.IsValid()) + nCaseReadonly |= PATH_READONLY; + else if( SWUnoHelper::UCB_IsCaseSensitiveFileName( aTempFile.GetURL())) + nCaseReadonly |= PATH_CASE_SENSITIVE; + m_xPathLB->append(OUString::number(nCaseReadonly), sPath); + } + m_xPathLB->set_active(0); + m_xPathLB->set_sensitive(true); + + const size_t nCount = pHdl->GetGroupCnt(); + /* tdf#111870 "My AutoText" comes from mytexts.bau but should be translated + here as well, see also SwGlossaryDlg::Init */ + const OUString sMyAutoTextEnglish("My AutoText"); + for( size_t i = 0; i < nCount; ++i) + { + OUString sTitle; + OUString sGroup = pHdl->GetGroupName(i, &sTitle); + if(sGroup.isEmpty()) + continue; + GlosBibUserData* pData = new GlosBibUserData; + pData->sGroupName = sGroup; + if ( sTitle == sMyAutoTextEnglish ) + pData->sGroupTitle = SwResId(STR_MY_AUTOTEXT); + else + pData->sGroupTitle = sTitle; + pData->sPath = m_xPathLB->get_text(sGroup.getToken(1, GLOS_DELIM).toInt32()); + const OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pData))); + m_xGroupTLB->append(sId, pData->sGroupTitle); + int nEntry = m_xGroupTLB->find_id(sId); + m_xGroupTLB->set_text(nEntry, pData->sPath, 1); + + } + m_xGroupTLB->make_sorted(); +} + +SwGlossaryGroupDlg::~SwGlossaryGroupDlg() +{ + int nCount = m_xGroupTLB->n_children(); + for (int i = 0; i < nCount; ++i) + { + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(i).toInt64()); + delete pUserData; + } +} + +short SwGlossaryGroupDlg::run() +{ + short nRet = SfxDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +void SwGlossaryGroupDlg::Apply() +{ + if (m_xNewPB->get_sensitive()) + NewHdl(*m_xNewPB); + + const OUString aActGroup = SwGlossaryDlg::GetCurrGroup(); + + for (const auto& removedStr : m_RemovedArr) + { + sal_Int32 nIdx{ 0 }; + const OUString sDelGroup = removedStr.getToken(0, '\t', nIdx); + if( sDelGroup == aActGroup ) + { + //when the current group is deleted, the current group has to be relocated + if (m_xGroupTLB->n_children()) + { + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(0).toInt64()); + pGlosHdl->SetCurGroup(pUserData->sGroupName); + } + } + const OUString sMsg(SwResId(STR_QUERY_DELETE_GROUP1) + + removedStr.getToken(0, '\t', nIdx) + + SwResId(STR_QUERY_DELETE_GROUP2)); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_pParent, + VclMessageType::Question, VclButtonsType::YesNo, sMsg)); + xQueryBox->set_default_response(RET_NO); + if (RET_YES == xQueryBox->run()) + pGlosHdl->DelGroup( sDelGroup ); + } + + //don't rename before there was one + for (auto it(m_RenamedArr.cbegin()); it != m_RenamedArr.cend(); ++it) + { + sal_Int32 nIdx{ 0 }; + OUString const sOld(it->getToken(0, RENAME_TOKEN_DELIM, nIdx)); + OUString sNew(it->getToken(0, RENAME_TOKEN_DELIM, nIdx)); + OUString const sTitle(it->getToken(0, RENAME_TOKEN_DELIM, nIdx)); + pGlosHdl->RenameGroup(sOld, sNew, sTitle); + if (it == m_RenamedArr.begin()) + { + sCreatedGroup = sNew; + } + } + for (auto& sNewGroup : m_InsertedArr) + { + OUString sNewTitle = sNewGroup.getToken(0, GLOS_DELIM); + if( sNewGroup != aActGroup ) + { + pGlosHdl->NewGroup(sNewGroup, sNewTitle); + if(sCreatedGroup.isEmpty()) + sCreatedGroup = sNewGroup; + } + } +} + +IMPL_LINK_NOARG( SwGlossaryGroupDlg, SelectHdl, weld::TreeView&, void ) +{ + m_xNewPB->set_sensitive(false); + int nFirstEntry = m_xGroupTLB->get_selected_index(); + if (nFirstEntry != -1) + { + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(nFirstEntry).toInt64()); + const OUString sEntry(pUserData->sGroupName); + const OUString sName(m_xNameED->get_text()); + bool bExists = false; + int nPos = m_xGroupTLB->find_text(sName); + if (nPos != -1) + { + GlosBibUserData* pFoundData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(nPos).toInt64()); + fprintf(stderr, "comparing %s and %s\n", + OUStringToOString(pFoundData->sGroupName, RTL_TEXTENCODING_UTF8).getStr(), + OUStringToOString(sEntry, RTL_TEXTENCODING_UTF8).getStr()); + bExists = pFoundData->sGroupName == sEntry; + } + + m_xRenamePB->set_sensitive(!bExists && !sName.isEmpty()); + fprintf(stderr, "one rename %d\n", !bExists && !sName.isEmpty()); + m_xDelPB->set_sensitive(IsDeleteAllowed(sEntry)); + } +} + +IMPL_LINK_NOARG(SwGlossaryGroupDlg, NewHdl, weld::Button&, void) +{ + OUString sGroup = m_xNameED->get_text() + + OUStringChar(GLOS_DELIM) + + OUString::number(m_xPathLB->get_active()); + OSL_ENSURE(!pGlosHdl->FindGroupName(sGroup), "group already available!"); + m_InsertedArr.push_back(sGroup); + GlosBibUserData* pData = new GlosBibUserData; + pData->sPath = m_xPathLB->get_active_text(); + pData->sGroupName = sGroup; + pData->sGroupTitle = m_xNameED->get_text(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pData))); + m_xGroupTLB->append(sId, m_xNameED->get_text()); + int nEntry = m_xGroupTLB->find_id(sId); + m_xGroupTLB->set_text(nEntry, pData->sPath, 1); + m_xGroupTLB->select(nEntry); + SelectHdl(*m_xGroupTLB); + m_xGroupTLB->scroll_to_row(nEntry); +} + +IMPL_LINK_NOARG(SwGlossaryGroupDlg, EntrySizeAllocHdl, const Size&, void) +{ + std::vector<int> aWidths; + int x, y, width, height; + if (m_xPathLB->get_extents_relative_to(*m_xGroupTLB, x, y, width, height)) + { + aWidths.push_back(x); + m_xGroupTLB->set_column_fixed_widths(aWidths); + } +} + +IMPL_LINK( SwGlossaryGroupDlg, DeleteHdl, weld::Button&, rButton, void ) +{ + int nEntry = m_xGroupTLB->get_selected_index(); + if (nEntry == -1) + { + rButton.set_sensitive(false); + return; + } + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(nEntry).toInt64()); + OUString const sEntry(pUserData->sGroupName); + // if the name to be deleted is among the new ones - get rid of it + bool bDelete = true; + auto it = std::find(m_InsertedArr.begin(), m_InsertedArr.end(), sEntry); + if (it != m_InsertedArr.end()) + { + m_InsertedArr.erase(it); + bDelete = false; + } + // it should probably be renamed? + if(bDelete) + { + it = std::find_if(m_RenamedArr.begin(), m_RenamedArr.end(), + [&sEntry](OUString& s) { return s.getToken(0, RENAME_TOKEN_DELIM) == sEntry; }); + if (it != m_RenamedArr.end()) + { + m_RenamedArr.erase(it); + bDelete = false; + } + } + if(bDelete) + { + m_RemovedArr.emplace_back(pUserData->sGroupName + "\t" + pUserData->sGroupTitle); + } + delete pUserData; + m_xGroupTLB->remove(nEntry); + if (!m_xGroupTLB->n_children()) + rButton.set_sensitive(false); + //the content must be deleted - otherwise the new handler would be called in Apply() + m_xNameED->set_text(OUString()); + ModifyHdl(*m_xNameED); +} + +IMPL_LINK_NOARG(SwGlossaryGroupDlg, RenameHdl, weld::Button&, void) +{ + int nEntry = m_xGroupTLB->get_selected_index(); + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(nEntry).toInt64()); + OUString sEntry(pUserData->sGroupName); + + const OUString sNewTitle(m_xNameED->get_text()); + OUString sNewName = sNewTitle + + OUStringChar(GLOS_DELIM) + + OUString::number(m_xPathLB->get_active()); + OSL_ENSURE(!pGlosHdl->FindGroupName(sNewName), "group already available!"); + + // if the name to be renamed is among the new ones - replace + bool bDone = false; + auto it = std::find(m_InsertedArr.begin(), m_InsertedArr.end(), sEntry); + if (it != m_InsertedArr.end()) + { + m_InsertedArr.erase(it); + m_InsertedArr.push_back(sNewName); + bDone = true; + } + if(!bDone) + { + sEntry += OUStringChar(RENAME_TOKEN_DELIM) + sNewName + + OUStringChar(RENAME_TOKEN_DELIM) + sNewTitle; + m_RenamedArr.push_back(sEntry); + } + delete pUserData; + m_xGroupTLB->remove(nEntry); + + GlosBibUserData* pData = new GlosBibUserData; + pData->sPath = m_xPathLB->get_active_text(); + pData->sGroupName = sNewName; + pData->sGroupTitle = sNewTitle; + + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pData))); + m_xGroupTLB->append(sId, m_xNameED->get_text()); + nEntry = m_xGroupTLB->find_id(sId); + m_xGroupTLB->set_text(nEntry, m_xPathLB->get_active_text(), 1); + m_xGroupTLB->select(nEntry); + SelectHdl(*m_xGroupTLB); + m_xGroupTLB->scroll_to_row(nEntry); +} + +IMPL_LINK_NOARG(SwGlossaryGroupDlg, ModifyListBoxHdl, weld::ComboBox&, void) +{ + ModifyHdl(*m_xNameED); +} + +IMPL_LINK_NOARG(SwGlossaryGroupDlg, ModifyHdl, weld::Entry&, void) +{ + const OUString sEntry(m_xNameED->get_text()); + bool bEnableNew = true; + bool bEnableDel = false; + sal_uInt32 nCaseReadonly = m_xPathLB->get_active_id().toUInt32(); + bool bDirReadonly = 0 != (nCaseReadonly&PATH_READONLY); + + if (sEntry.isEmpty() || bDirReadonly) + bEnableNew = false; + else if(!sEntry.isEmpty()) + { + int nPos = m_xGroupTLB->find_text(sEntry); + //if it's not case sensitive you have to search for yourself + if (nPos == -1) + { + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + for (int i = 0, nEntryCount = m_xGroupTLB->n_children(); i < nEntryCount; ++i) + { + const OUString sTemp = m_xGroupTLB->get_text(i, 0); + nCaseReadonly = m_xPathLB->get_id(m_xPathLB->find_text(m_xGroupTLB->get_text(i,1))).toUInt32(); + bool bCase = 0 != (nCaseReadonly & PATH_CASE_SENSITIVE); + + if( !bCase && rSCmp.isEqual( sTemp, sEntry )) + { + nPos = i; + break; + } + } + } + if (nPos != -1) + { + bEnableNew = false; + m_xGroupTLB->select(nPos); + m_xGroupTLB->scroll_to_row(nPos); + SelectHdl(*m_xGroupTLB); + } + } + int nEntry = m_xGroupTLB->get_selected_index(); + if (nEntry != -1) + { + GlosBibUserData* pUserData = reinterpret_cast<GlosBibUserData*>(m_xGroupTLB->get_id(nEntry).toInt64()); + bEnableDel = IsDeleteAllowed(pUserData->sGroupName); + } + + m_xDelPB->set_sensitive(bEnableDel); + m_xNewPB->set_sensitive(bEnableNew); + m_xRenamePB->set_sensitive(bEnableNew && nEntry != -1); + fprintf(stderr, "two rename %d\n", bEnableNew && nEntry != -1); +} + +bool SwGlossaryGroupDlg::IsDeleteAllowed(const OUString &rGroup) +{ + bool bDel = !pGlosHdl->IsReadOnly(&rGroup); + + // OM: if the name is among the new region name, it is deletable + // as well! Because for non existing region names ReadOnly issues + // true. + + auto it = std::find(m_InsertedArr.cbegin(), m_InsertedArr.cend(), rGroup); + if (it != m_InsertedArr.cend()) + bDel = true; + + return bDel; +} + +IMPL_STATIC_LINK(SwGlossaryGroupDlg, EditInsertTextHdl, OUString&, rText, bool) +{ + rText = rText.replaceAll(OUStringChar(SVT_SEARCHPATH_DELIMITER), ""); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/glossary.cxx b/sw/source/ui/misc/glossary.cxx new file mode 100644 index 000000000..916ade14f --- /dev/null +++ b/sw/source/ui/misc/glossary.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 <hintids.hxx> + +#include <o3tl/any.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svl/stritem.hxx> +#include <svl/macitem.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/lingucfg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfilt.hxx> + +#include <svx/svxdlg.hxx> +#include <editeng/acorrcfg.hxx> +#include <sfx2/viewfrm.hxx> +#include <unotools.hxx> +#include <comphelper/processfactory.hxx> +#include <ucbhelper/content.hxx> +#include <com/sun/star/text/AutoTextContainer.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <svl/urihelper.hxx> +#include <unotools/charclass.hxx> +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <glossary.hxx> +#include <gloshdl.hxx> +#include <glosbib.hxx> +#include <initui.hxx> +#include <glosdoc.hxx> +#include <macassgn.hxx> +#include <docsh.hxx> +#include <shellio.hxx> + +#include <cmdid.h> +#include <sfx2/filedlghelper.hxx> + +#include <memory> + +#include <strings.hrc> +#include <iodetect.hxx> + +#include <officecfg/Office/Writer.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::ucbhelper; +using namespace ::sfx2; + +static OUString lcl_GetValidShortCut( const OUString& rName ) +{ + const sal_Int32 nSz = rName.getLength(); + + if ( 0 == nSz ) + return rName; + + sal_Int32 nStart = 1; + while( rName[nStart-1]==' ' && nStart < nSz ) + nStart++; + + OUStringBuffer aBuf; + aBuf.append(rName[nStart-1]); + + for( ; nStart < nSz; ++nStart ) + { + if( rName[nStart-1]==' ' && rName[nStart]!=' ') + aBuf.append(rName[nStart]); + } + return aBuf.makeStringAndClear(); +} + +struct GroupUserData +{ + OUString sGroupName; + sal_uInt16 nPathIdx; + bool bReadonly; + + GroupUserData() + : nPathIdx(0), + bReadonly(false) {} +}; + +// dialog for new block name +class SwNewGlosNameDlg : public weld::GenericDialogController +{ + TextFilter m_aNoSpaceFilter; + SwGlossaryDlg* m_pParent; + + std::unique_ptr<weld::Entry> m_xNewName; + std::unique_ptr<weld::Entry> m_xNewShort; + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::Entry> m_xOldName; + std::unique_ptr<weld::Entry> m_xOldShort; + +protected: + DECL_LINK(Modify, weld::Entry&, void); + DECL_LINK(Rename, weld::Button&, void); + DECL_LINK(TextFilterHdl, OUString&, bool); + +public: + SwNewGlosNameDlg(SwGlossaryDlg* pParent, + const OUString& rOldName, + const OUString& rOldShort); + + OUString GetNewName() const { return m_xNewName->get_text(); } + OUString GetNewShort() const { return m_xNewShort->get_text(); } +}; + +IMPL_LINK(SwNewGlosNameDlg, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aNoSpaceFilter.filter(rTest); + return true; +} + +SwNewGlosNameDlg::SwNewGlosNameDlg(SwGlossaryDlg* pParent, const OUString& rOldName, const OUString& rOldShort) + : GenericDialogController(pParent->getDialog(), "modules/swriter/ui/renameautotextdialog.ui", "RenameAutoTextDialog") + , m_pParent(pParent) + , m_xNewName(m_xBuilder->weld_entry("newname")) + , m_xNewShort(m_xBuilder->weld_entry("newsc")) + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xOldName(m_xBuilder->weld_entry("oldname")) + , m_xOldShort(m_xBuilder->weld_entry("oldsc")) +{ + m_xNewShort->connect_insert_text(LINK(this, SwNewGlosNameDlg, TextFilterHdl)); + + m_xOldName->set_text(rOldName); + m_xOldShort->set_text(rOldShort); + m_xNewName->connect_changed(LINK(this, SwNewGlosNameDlg, Modify )); + m_xNewShort->connect_changed(LINK(this, SwNewGlosNameDlg, Modify )); + m_xOk->connect_clicked(LINK(this, SwNewGlosNameDlg, Rename )); + m_xNewName->grab_focus(); +} + +// query / set currently set group +OUString SwGlossaryDlg::GetCurrGroup() +{ + if( !::GetCurrGlosGroup().isEmpty() ) + return ::GetCurrGlosGroup(); + return SwGlossaries::GetDefName(); +} + +void SwGlossaryDlg::SetActGroup(const OUString &rGrp) +{ + ::SetCurrGlosGroup(rGrp); +} + +IMPL_LINK(SwGlossaryDlg, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aNoSpaceFilter.filter(rTest); + return true; +} + +SwGlossaryDlg::SwGlossaryDlg(SfxViewFrame const * pViewFrame, + SwGlossaryHdl * pGlosHdl, SwWrtShell *pWrtShell) + : SfxDialogController(pViewFrame->GetWindow().GetFrameWeld(), "modules/swriter/ui/autotext.ui", "AutoTextDialog") + , m_sReadonlyPath(SwResId(STR_READONLY_PATH)) + , m_pGlossaryHdl(pGlosHdl) + , m_bResume(false) + , m_bSelection(pWrtShell->IsSelection()) + , m_bReadOnly(false) + , m_bIsOld(false) + , m_bIsDocReadOnly(false) + , m_pShell(pWrtShell) + , m_xInsertTipCB(m_xBuilder->weld_check_button("inserttip")) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xShortNameLbl(m_xBuilder->weld_label("shortnameft")) + , m_xShortNameEdit(m_xBuilder->weld_entry("shortname")) + , m_xCategoryBox(m_xBuilder->weld_tree_view("category")) + , m_xFileRelCB(m_xBuilder->weld_check_button("relfile")) + , m_xNetRelCB(m_xBuilder->weld_check_button("relnet")) + , m_xInsertBtn(m_xBuilder->weld_button("ok")) + , m_xEditBtn(m_xBuilder->weld_menu_button("autotext")) + , m_xBibBtn(m_xBuilder->weld_button("categories")) + , m_xPathBtn(m_xBuilder->weld_button("path")) +{ + m_xCategoryBox->set_size_request(m_xCategoryBox->get_approximate_digit_width() * 52, + m_xCategoryBox->get_height_rows(12)); + + Link<SwOneExampleFrame&,void> aLink(LINK(this, SwGlossaryDlg, PreviewLoadedHdl)); + m_xExampleFrame.reset(new SwOneExampleFrame(EX_SHOW_ONLINE_LAYOUT, &aLink)); + m_xExampleFrameWin.reset(new weld::CustomWeld(*m_xBuilder, "example", *m_xExampleFrame)); + Size aSize = m_xExampleFrame->GetDrawingArea()->get_ref_device().LogicToPixel( + Size(82, 124), MapMode(MapUnit::MapAppFont)); + m_xExampleFrame->set_size_request(aSize.Width(), aSize.Height()); + + m_xShortNameEdit->connect_insert_text(LINK(this, SwGlossaryDlg, TextFilterHdl)); + + m_xEditBtn->connect_toggled(LINK(this, SwGlossaryDlg, EnableHdl)); + m_xEditBtn->connect_selected(LINK(this, SwGlossaryDlg, MenuHdl)); + m_xPathBtn->connect_clicked(LINK(this, SwGlossaryDlg, PathHdl)); + + m_xNameED->connect_changed(LINK(this,SwGlossaryDlg,NameModify)); + m_xShortNameEdit->connect_changed(LINK(this,SwGlossaryDlg,NameModify)); + + m_xCategoryBox->connect_row_activated(LINK(this, SwGlossaryDlg, NameDoubleClick)); + m_xCategoryBox->connect_changed(LINK(this, SwGlossaryDlg, GrpSelect)); + m_xCategoryBox->connect_key_press(LINK(this, SwGlossaryDlg, KeyInputHdl)); + m_xBibBtn->connect_clicked(LINK(this,SwGlossaryDlg,BibHdl)); + + m_xInsertBtn->connect_clicked(LINK(this,SwGlossaryDlg,InsertHdl)); + + ShowPreview(); + + m_bIsDocReadOnly = m_pShell->GetView().GetDocShell()->IsReadOnly() || + m_pShell->HasReadonlySel(); + if( m_bIsDocReadOnly ) + m_xInsertBtn->set_sensitive(false); + m_xNameED->grab_focus(); + m_xCategoryBox->make_sorted(); + m_xCategoryBox->set_sort_order(true); + + Init(); +} + +SwGlossaryDlg::~SwGlossaryDlg() +{ +} + +namespace +{ + +OUString getCurrentGlossary() +{ + const OUString sTemp{ ::GetCurrGlosGroup() }; + + // the zeroth path is not being recorded! + if (sTemp.getToken(1, GLOS_DELIM).startsWith("0")) + return sTemp.getToken(0, GLOS_DELIM); + + return sTemp; +} + +} + +// select new group +IMPL_LINK(SwGlossaryDlg, GrpSelect, weld::TreeView&, rBox, void) +{ + std::unique_ptr<weld::TreeIter> xEntry = rBox.make_iterator(); + if (!rBox.get_selected(xEntry.get())) + return; + + std::unique_ptr<weld::TreeIter> xParent = rBox.make_iterator(xEntry.get()); + weld::TreeIter* pParent; + if (rBox.get_iter_depth(*xParent)) + { + rBox.iter_parent(*xParent); + pParent = xParent.get(); + } + else + pParent = xEntry.get(); + GroupUserData* pGroupData = reinterpret_cast<GroupUserData*>(rBox.get_id(*pParent).toInt64()); + ::SetCurrGlosGroup(pGroupData->sGroupName + + OUStringChar(GLOS_DELIM) + + OUString::number(pGroupData->nPathIdx)); + m_pGlossaryHdl->SetCurGroup(::GetCurrGlosGroup()); + // set current text block + m_bReadOnly = m_pGlossaryHdl->IsReadOnly(); + EnableShortName( !m_bReadOnly ); + m_xEditBtn->set_sensitive(!m_bReadOnly); + m_bIsOld = m_pGlossaryHdl->IsOld(); + if( pParent != xEntry.get()) + { + OUString aName(rBox.get_text(*xEntry)); + m_xNameED->set_text(aName); + m_xShortNameEdit->set_text(rBox.get_id(*xEntry)); + m_xInsertBtn->set_sensitive( !m_bIsDocReadOnly); + ShowAutoText(::GetCurrGlosGroup(), m_xShortNameEdit->get_text()); + } + else + { + m_xNameED->set_text(""); + m_xShortNameEdit->set_text(""); + m_xShortNameEdit->set_sensitive(false); + ShowAutoText("", ""); + } + // update controls + NameModify(*m_xShortNameEdit); + if( SfxRequest::HasMacroRecorder( m_pShell->GetView().GetViewFrame() ) ) + { + SfxRequest aReq( m_pShell->GetView().GetViewFrame(), FN_SET_ACT_GLOSSARY ); + aReq.AppendItem(SfxStringItem(FN_SET_ACT_GLOSSARY, getCurrentGlossary())); + aReq.Done(); + } +} + +short SwGlossaryDlg::run() +{ + short nRet = SfxDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +void SwGlossaryDlg::Apply() +{ + const OUString aGlosName(m_xShortNameEdit->get_text()); + if (!aGlosName.isEmpty()) + { + m_pGlossaryHdl->InsertGlossary(aGlosName); + } + if( SfxRequest::HasMacroRecorder( m_pShell->GetView().GetViewFrame() ) ) + { + SfxRequest aReq( m_pShell->GetView().GetViewFrame(), FN_INSERT_GLOSSARY ); + aReq.AppendItem(SfxStringItem(FN_INSERT_GLOSSARY, getCurrentGlossary())); + aReq.AppendItem(SfxStringItem(FN_PARAM_1, aGlosName)); + aReq.Done(); + } +} + +void SwGlossaryDlg::EnableShortName(bool bOn) +{ + m_xShortNameLbl->set_sensitive(bOn); + m_xShortNameEdit->set_sensitive(bOn); +} + +// does the title exist in the selected group? +std::unique_ptr<weld::TreeIter> SwGlossaryDlg::DoesBlockExist(const OUString& rBlock, + const OUString& rShort) +{ + // look for possible entry in TreeListBox + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + if (m_xCategoryBox->get_selected(xEntry.get())) + { + if (m_xCategoryBox->get_iter_depth(*xEntry)) + m_xCategoryBox->iter_parent(*xEntry); + if (!m_xCategoryBox->iter_children(*xEntry)) + return nullptr; + do + { + if (rBlock == m_xCategoryBox->get_text(*xEntry) && + (rShort.isEmpty() || + rShort == m_xCategoryBox->get_id(*xEntry)) + ) + { + return xEntry; + } + } + while (m_xCategoryBox->iter_next_sibling(*xEntry)); + } + return nullptr; +} + +IMPL_LINK(SwGlossaryDlg, NameModify, weld::Entry&, rEdit, void) +{ + const OUString aName(m_xNameED->get_text()); + bool bNameED = &rEdit == m_xNameED.get(); + if( aName.isEmpty() ) + { + if(bNameED) + m_xShortNameEdit->set_text(aName); + m_xInsertBtn->set_sensitive(false); + return; + } + const bool bNotFound = !DoesBlockExist(aName, bNameED ? OUString() : rEdit.get_text()); + if(bNameED) + { + // did the text get in to the Listbox in the Edit with a click? + if(bNotFound) + { + m_xShortNameEdit->set_text( lcl_GetValidShortCut( aName ) ); + EnableShortName(); + } + else + { + m_xShortNameEdit->set_text(m_pGlossaryHdl->GetGlossaryShortName(aName)); + EnableShortName(!m_bReadOnly); + } + m_xInsertBtn->set_sensitive(!bNotFound && !m_bIsDocReadOnly); + } + else + { + //ShortNameEdit + if(!bNotFound) + { + m_xInsertBtn->set_sensitive(!m_bIsDocReadOnly); + } + } +} + +IMPL_LINK( SwGlossaryDlg, NameDoubleClick, weld::TreeView&, rBox, bool ) +{ + std::unique_ptr<weld::TreeIter> xEntry = rBox.make_iterator(); + if (rBox.get_selected(xEntry.get()) && rBox.get_iter_depth(*xEntry) && !m_bIsDocReadOnly) + m_xDialog->response(RET_OK); + return true; +} + +IMPL_LINK_NOARG( SwGlossaryDlg, EnableHdl, weld::ToggleButton&, void ) +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + bool bEntry = m_xCategoryBox->get_selected(xEntry.get()); + + const OUString aEditText(m_xNameED->get_text()); + const bool bHasEntry = !aEditText.isEmpty() && !m_xShortNameEdit->get_text().isEmpty(); + const bool bExists = nullptr != DoesBlockExist(aEditText, m_xShortNameEdit->get_text()); + const bool bIsGroup = bEntry && !m_xCategoryBox->get_iter_depth(*xEntry); + m_xEditBtn->set_item_visible("new", m_bSelection && bHasEntry && !bExists); + m_xEditBtn->set_item_visible("newtext", m_bSelection && bHasEntry && !bExists); + m_xEditBtn->set_item_visible("copy", bExists && !bIsGroup); + m_xEditBtn->set_item_visible("replace", m_bSelection && bExists && !bIsGroup && !m_bIsOld ); + m_xEditBtn->set_item_visible("replacetext", m_bSelection && bExists && !bIsGroup && !m_bIsOld ); + m_xEditBtn->set_item_visible("edit", bExists && !bIsGroup ); + m_xEditBtn->set_item_visible("rename", bExists && !bIsGroup ); + m_xEditBtn->set_item_visible("delete", bExists && !bIsGroup ); + m_xEditBtn->set_item_visible("macro", bExists && !bIsGroup && !m_bIsOld && + !m_pGlossaryHdl->IsReadOnly() ); + m_xEditBtn->set_item_visible("import", bIsGroup && !m_bIsOld && !m_pGlossaryHdl->IsReadOnly() ); +} + +IMPL_LINK(SwGlossaryDlg, MenuHdl, const OString&, rItemIdent, void) +{ + if (rItemIdent == "edit") + { + std::unique_ptr<SwTextBlocks> pGroup = ::GetGlossaries()->GetGroupDoc ( GetCurrGrpName () ); + pGroup.reset(); + m_xDialog->response(RET_EDIT); + } + else if (rItemIdent == "replace") + { + m_pGlossaryHdl->NewGlossary(m_xNameED->get_text(), + m_xShortNameEdit->get_text()); + } + else if (rItemIdent == "replacetext") + { + m_pGlossaryHdl->NewGlossary(m_xNameED->get_text(), + m_xShortNameEdit->get_text(), + false, true); + } + else if (rItemIdent == "new" || rItemIdent == "newtext") + { + bool bNoAttr = rItemIdent == "newtext"; + + const OUString aStr(m_xNameED->get_text()); + const OUString aShortName(m_xShortNameEdit->get_text()); + if(m_pGlossaryHdl->HasShortName(aShortName)) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_DOUBLE_SHORTNAME))); + xInfoBox->run(); + m_xShortNameEdit->select_region(0, -1); + m_xShortNameEdit->grab_focus(); + } + if(m_pGlossaryHdl->NewGlossary(aStr, aShortName, false, bNoAttr )) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + if (!m_xCategoryBox->get_selected(xEntry.get())) + xEntry.reset(); + else if (m_xCategoryBox->get_iter_depth(*xEntry)) + m_xCategoryBox->iter_parent(*xEntry); + m_xCategoryBox->insert(xEntry.get(), -1, &aStr, &aShortName, + nullptr, nullptr, nullptr, false, nullptr); + + m_xNameED->set_text(aStr); + m_xShortNameEdit->set_text(aShortName); + NameModify(*m_xNameED); // for toggling the buttons + + if( SfxRequest::HasMacroRecorder( m_pShell->GetView().GetViewFrame() ) ) + { + SfxRequest aReq(m_pShell->GetView().GetViewFrame(), FN_NEW_GLOSSARY); + aReq.AppendItem(SfxStringItem(FN_NEW_GLOSSARY, getCurrentGlossary())); + aReq.AppendItem(SfxStringItem(FN_PARAM_1, aShortName)); + aReq.AppendItem(SfxStringItem(FN_PARAM_2, aStr)); + aReq.Done(); + } + } + } + else if (rItemIdent == "copy") + { + m_pGlossaryHdl->CopyToClipboard(*m_pShell, m_xShortNameEdit->get_text()); + } + else if (rItemIdent == "rename") + { + m_xShortNameEdit->set_text(m_pGlossaryHdl->GetGlossaryShortName(m_xNameED->get_text())); + SwNewGlosNameDlg aNewNameDlg(this, m_xNameED->get_text(), m_xShortNameEdit->get_text()); + if (aNewNameDlg.run() == RET_OK && m_pGlossaryHdl->Rename(m_xShortNameEdit->get_text(), + aNewNameDlg.GetNewShort(), + aNewNameDlg.GetNewName())) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + if (m_xCategoryBox->get_selected(xEntry.get())) + { + std::unique_ptr<weld::TreeIter> xOldEntry = m_xCategoryBox->make_iterator(xEntry.get()); + if (m_xCategoryBox->get_iter_depth(*xEntry)) + m_xCategoryBox->iter_parent(*xEntry); + + std::unique_ptr<weld::TreeIter> xNewEntry = m_xCategoryBox->make_iterator(); + OUString sId(aNewNameDlg.GetNewShort()); + OUString sName(aNewNameDlg.GetNewName()); + + m_xCategoryBox->insert(xEntry.get(), -1, &sName, &sId, + nullptr, nullptr, nullptr, false, xNewEntry.get()); + + m_xCategoryBox->remove(*xOldEntry); + m_xCategoryBox->select(*xNewEntry); + m_xCategoryBox->scroll_to_row(*xNewEntry); + } + } + GrpSelect(*m_xCategoryBox); + } + else if (rItemIdent == "delete") + { + DeleteEntry(); + } + else if (rItemIdent == "macro") + { + SfxItemSet aSet( m_pShell->GetAttrPool(), svl::Items<RES_FRMMACRO, RES_FRMMACRO, SID_EVENTCONFIG, SID_EVENTCONFIG>{} ); + + SvxMacro aStart(OUString(), OUString(), STARBASIC); + SvxMacro aEnd(OUString(), OUString(), STARBASIC); + m_pGlossaryHdl->GetMacros(m_xShortNameEdit->get_text(), aStart, aEnd ); + + SvxMacroItem aItem(RES_FRMMACRO); + if( aStart.HasMacro() ) + aItem.SetMacro( SvMacroItemId::SwStartInsGlossary, aStart ); + if( aEnd.HasMacro() ) + aItem.SetMacro( SvMacroItemId::SwEndInsGlossary, aEnd ); + + aSet.Put( aItem ); + aSet.Put( SwMacroAssignDlg::AddEvents( MACASSGN_AUTOTEXT ) ); + + const SfxPoolItem* pItem; + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pMacroDlg(pFact->CreateEventConfigDialog(m_xDialog.get(), aSet, + m_pShell->GetView().GetViewFrame()->GetFrame().GetFrameInterface() )); + if ( pMacroDlg && pMacroDlg->Execute() == RET_OK && + SfxItemState::SET == pMacroDlg->GetOutputItemSet()->GetItemState( RES_FRMMACRO, false, &pItem ) ) + { + const SvxMacroTableDtor& rTable = static_cast<const SvxMacroItem*>(pItem)->GetMacroTable(); + m_pGlossaryHdl->SetMacros( m_xShortNameEdit->get_text(), + rTable.Get( SvMacroItemId::SwStartInsGlossary ), + rTable.Get( SvMacroItemId::SwEndInsGlossary ) ); + } + } + else if (rItemIdent == "import") + { + // call the FileOpenDialog do find WinWord - Files with templates + FileDialogHelper aDlgHelper(TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, m_xDialog.get()); + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + SvtPathOptions aPathOpt; + xFP->setDisplayDirectory(aPathOpt.GetWorkPath() ); + + SfxFilterMatcher aMatcher( SwDocShell::Factory().GetFactoryName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFilter = aIter.First(); + while ( pFilter ) + { + if( pFilter->GetUserData() == FILTER_WW8 ) + { + xFP->appendFilter( pFilter->GetUIName(), + pFilter->GetWildcard().getGlob() ); + xFP->setCurrentFilter( pFilter->GetUIName() ) ; + } + else if( pFilter->GetUserData() == FILTER_DOCX ) + { + xFP->appendFilter( pFilter->GetUIName(), + pFilter->GetWildcard().getGlob() ); + xFP->setCurrentFilter( pFilter->GetUIName() ) ; + } + + pFilter = aIter.Next(); + } + + if( aDlgHelper.Execute() == ERRCODE_NONE ) + { + if( m_pGlossaryHdl->ImportGlossaries( xFP->getSelectedFiles().getConstArray()[0] )) + Init(); + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_NO_GLOSSARIES))); + xInfoBox->run(); + } + } + } +} + +// dialog manage regions +IMPL_LINK_NOARG(SwGlossaryDlg, BibHdl, weld::Button&, void) +{ + SwGlossaries* pGloss = ::GetGlossaries(); + if( pGloss->IsGlosPathErr() ) + pGloss->ShowError(); + else + { + //check if at least one glossary path is write enabled + SvtPathOptions aPathOpt; + const OUString& sGlosPath( aPathOpt.GetAutoTextPath() ); + bool bIsWritable = false; + sal_Int32 nIdx {sGlosPath.isEmpty() ? -1 : 0}; + while (nIdx>=0) + { + const OUString sPath = URIHelper::SmartRel2Abs( + INetURLObject(), sGlosPath.getToken(0, ';', nIdx), + URIHelper::GetMaybeFileHdl()); + try + { + Content aTestContent( sPath, + uno::Reference< XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + Any aAny = aTestContent.getPropertyValue( "IsReadOnly" ); + if(aAny.hasValue()) + { + bIsWritable = !*o3tl::doAccess<bool>(aAny); + } + } + catch (const Exception&) + { + } + if(bIsWritable) + break; + } + if(bIsWritable) + { + + SwGlossaryGroupDlg aDlg(m_xDialog.get(), pGloss->GetPathArray(), m_pGlossaryHdl); + if (aDlg.run() == RET_OK) + { + Init(); + //if new groups were created - select one of them + const OUString sNewGroup = aDlg.GetCreatedGroupName(); + + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + bool bEntry = m_xCategoryBox->get_iter_first(*xEntry); + + while (!sNewGroup.isEmpty() && bEntry) + { + if (!m_xCategoryBox->get_iter_depth(*xEntry)) + { + GroupUserData* pGroupData = reinterpret_cast<GroupUserData*>(m_xCategoryBox->get_id(*xEntry).toInt64()); + const OUString sGroup = pGroupData->sGroupName + + OUStringChar(GLOS_DELIM) + + OUString::number(pGroupData->nPathIdx); + if(sGroup == sNewGroup) + { + m_xCategoryBox->select(*xEntry); + m_xCategoryBox->scroll_to_row(*xEntry); + GrpSelect(*m_xCategoryBox); + break; + } + } + bEntry = m_xCategoryBox->iter_next(*xEntry); + } + + } + } + else + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + m_sReadonlyPath)); + if (RET_YES == xBox->run()) + PathHdl(*m_xPathBtn); + } + } +} + +// initialisation; from Ctor and after editing regions +void SwGlossaryDlg::Init() +{ + m_xCategoryBox->freeze(); + m_xCategoryBox->clear(); + m_xGroupData.clear(); + m_xCategoryBox->make_unsorted(); + + // display text block regions + const size_t nCnt = m_pGlossaryHdl->GetGroupCnt(); + std::unique_ptr<weld::TreeIter> xSelEntry; + const OUString sSelStr(::GetCurrGlosGroup().getToken(0, GLOS_DELIM)); + const sal_Int32 nSelPath = ::GetCurrGlosGroup().getToken(1, GLOS_DELIM).toInt32(); + // #i66304# - "My AutoText" comes from mytexts.bau, but should be translated + const OUString sMyAutoTextEnglish("My AutoText"); + const OUString sMyAutoTextTranslated(SwResId(STR_MY_AUTOTEXT)); + for(size_t nId = 0; nId < nCnt; ++nId ) + { + OUString sTitle; + OUString sGroupName(m_pGlossaryHdl->GetGroupName(nId, &sTitle)); + if(sGroupName.isEmpty()) + continue; + sal_Int32 nIdx{ 0 }; + const OUString sName{ sGroupName.getToken( 0, GLOS_DELIM, nIdx ) }; + if(sTitle.isEmpty()) + sTitle = sName; + if(sTitle == sMyAutoTextEnglish) + sTitle = sMyAutoTextTranslated; + + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + m_xCategoryBox->append(xEntry.get()); + m_xCategoryBox->set_text(*xEntry, sTitle, 0); + const sal_Int32 nPath = sGroupName.getToken( 0, GLOS_DELIM, nIdx ).toInt32(); + + GroupUserData* pData = new GroupUserData; + pData->sGroupName = sName; + pData->nPathIdx = static_cast< sal_uInt16 >(nPath); + pData->bReadonly = m_pGlossaryHdl->IsReadOnly(&sGroupName); + m_xGroupData.emplace_back(pData); + + m_xCategoryBox->set_id(*xEntry, OUString::number(reinterpret_cast<sal_Int64>(pData))); + if (sSelStr == pData->sGroupName && nSelPath == nPath) + xSelEntry = m_xCategoryBox->make_iterator(xEntry.get()); + + // fill entries for the groups + { + m_pGlossaryHdl->SetCurGroup(sGroupName, false, true); + const sal_uInt16 nCount = m_pGlossaryHdl->GetGlossaryCnt(); + for(sal_uInt16 i = 0; i < nCount; ++i) + { + OUString sEntryName = m_pGlossaryHdl->GetGlossaryName(i); + OUString sId = m_pGlossaryHdl->GetGlossaryShortName(i); + m_xCategoryBox->insert(xEntry.get(), -1, &sEntryName, &sId, + nullptr, nullptr, nullptr, false, nullptr); + } + } + } + // set current group and display text blocks + if (!xSelEntry) + { + //find a non-readonly group + std::unique_ptr<weld::TreeIter> xSearch = m_xCategoryBox->make_iterator(); + if (m_xCategoryBox->get_iter_first(*xSearch)) + { + do + { + if (!m_xCategoryBox->get_iter_depth(*xSearch)) + { + GroupUserData* pData = reinterpret_cast<GroupUserData*>(m_xCategoryBox->get_id(*xSearch).toInt64()); + if (!pData->bReadonly) + { + xSelEntry = std::move(xSearch); + break; + } + } + } + while (m_xCategoryBox->iter_next(*xSearch)); + } + if (!xSelEntry) + { + xSelEntry = std::move(xSearch); + if (!m_xCategoryBox->get_iter_first(*xSelEntry)) + xSelEntry.reset(); + } + } + + m_xCategoryBox->thaw(); + m_xCategoryBox->make_sorted(); + + if (xSelEntry) + { + m_xCategoryBox->expand_row(*xSelEntry); + m_xCategoryBox->select(*xSelEntry); + m_xCategoryBox->scroll_to_row(*xSelEntry); + GrpSelect(*m_xCategoryBox); + } + + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + m_xFileRelCB->set_active( rCfg.IsSaveRelFile() ); + m_xFileRelCB->connect_toggled(LINK(this, SwGlossaryDlg, CheckBoxHdl)); + m_xNetRelCB->set_active( rCfg.IsSaveRelNet() ); + m_xNetRelCB->connect_toggled(LINK(this, SwGlossaryDlg, CheckBoxHdl)); + m_xInsertTipCB->set_active( rCfg.IsAutoTextTip() ); + m_xInsertTipCB->set_sensitive(!officecfg::Office::Writer::AutoFunction::Text::ShowToolTip::isReadOnly()); + m_xInsertTipCB->connect_toggled(LINK(this, SwGlossaryDlg, CheckBoxHdl)); +} + +// KeyInput for ShortName - Edits without Spaces +IMPL_LINK( SwNewGlosNameDlg, Modify, weld::Entry&, rBox, void ) +{ + OUString aName(m_xNewName->get_text()); + SwGlossaryDlg* pDlg = m_pParent; + if (&rBox == m_xNewName.get()) + m_xNewShort->set_text(lcl_GetValidShortCut(aName)); + + bool bEnable = !aName.isEmpty() && !m_xNewShort->get_text().isEmpty() && + (!pDlg->DoesBlockExist(aName, m_xNewShort->get_text()) + || aName == m_xOldName->get_text()); + m_xOk->set_sensitive(bEnable); +} + +IMPL_LINK_NOARG(SwNewGlosNameDlg, Rename, weld::Button&, void) +{ + SwGlossaryDlg* pDlg = m_pParent; + OUString sNew = GetAppCharClass().uppercase(m_xNewShort->get_text()); + if (pDlg->m_pGlossaryHdl->HasShortName(m_xNewShort->get_text()) + && sNew != m_xOldShort->get_text()) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_DOUBLE_SHORTNAME))); + xBox->run(); + m_xNewShort->grab_focus(); + } + else + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwGlossaryDlg, CheckBoxHdl, weld::ToggleButton&, rBox, void) +{ + SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + bool bCheck = rBox.get_active(); + if (&rBox == m_xInsertTipCB.get()) + rCfg.SetAutoTextTip(bCheck); + else if (&rBox == m_xFileRelCB.get()) + rCfg.SetSaveRelFile(bCheck); + else + rCfg.SetSaveRelNet(bCheck); + rCfg.Commit(); +} + +IMPL_LINK(SwGlossaryDlg, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + if (rKEvt.GetKeyCode().GetCode() == KEY_DELETE) + { + DeleteEntry(); + return true; + } + return false; +} + +OUString SwGlossaryDlg::GetCurrGrpName() const +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xCategoryBox->make_iterator(); + if (m_xCategoryBox->get_selected(xEntry.get())) + { + if (m_xCategoryBox->get_iter_depth(*xEntry)) + m_xCategoryBox->iter_parent(*xEntry); + GroupUserData* pGroupData = reinterpret_cast<GroupUserData*>(m_xCategoryBox->get_id(*xEntry).toInt64()); + return pGroupData->sGroupName + OUStringChar(GLOS_DELIM) + OUString::number(pGroupData->nPathIdx); + } + return OUString(); +} + +IMPL_LINK_NOARG( SwGlossaryDlg, PathHdl, weld::Button&, void ) +{ + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxMultiPathDialog> pDlg(pFact->CreateSvxPathSelectDialog(m_xDialog.get())); + SvtPathOptions aPathOpt; + const OUString sGlosPath( aPathOpt.GetAutoTextPath() ); + pDlg->SetPath(sGlosPath); + if(RET_OK == pDlg->Execute()) + { + const OUString sTmp(pDlg->GetPath()); + if(sTmp != sGlosPath) + { + aPathOpt.SetAutoTextPath( sTmp ); + ::GetGlossaries()->UpdateGlosPath( true ); + Init(); + } + } +} + +IMPL_LINK_NOARG(SwGlossaryDlg, InsertHdl, weld::Button&, void) +{ + m_xDialog->response(RET_OK); +} + +void SwGlossaryDlg::ShowPreview() +{ + ShowAutoText(::GetCurrGlosGroup(), m_xShortNameEdit->get_text()); +}; + +IMPL_LINK_NOARG(SwGlossaryDlg, PreviewLoadedHdl, SwOneExampleFrame&, void) +{ + ResumeShowAutoText(); +} + +void SwGlossaryDlg::ShowAutoText(const OUString& rGroup, const OUString& rShortName) +{ + if (m_xExampleFrameWin->get_visible()) + { + SetResumeData(rGroup, rShortName); + //try to make an Undo() + m_xExampleFrame->ClearDocument(); + } +} + +void SwGlossaryDlg::ResumeShowAutoText() +{ + OUString sGroup; + OUString sShortName; + if(GetResumeData(sGroup, sShortName) && m_xExampleFrameWin->get_visible()) + { + if(!m_xAutoText.is()) + { + //now the AutoText ListBoxes have to be filled + m_xAutoText = text::AutoTextContainer::create( comphelper::getProcessComponentContext() ); + } + + uno::Reference< XTextCursor > & xCursor = m_xExampleFrame->GetTextCursor(); + if(xCursor.is()) + { + if (!sShortName.isEmpty()) + { + uno::Any aGroup = m_xAutoText->getByName(sGroup); + uno::Reference< XAutoTextGroup > xGroup; + if((aGroup >>= xGroup) && xGroup->hasByName(sShortName)) + { + uno::Any aEntry(xGroup->getByName(sShortName)); + uno::Reference< XAutoTextEntry > xEntry; + aEntry >>= xEntry; + xEntry->applyTo(xCursor); + } + } + } + } + m_bResume = false; +} + +void SwGlossaryDlg::DeleteEntry() +{ + bool bEntry = m_xCategoryBox->get_selected(nullptr); + + const OUString aTitle(m_xNameED->get_text()); + const OUString aShortName(m_xShortNameEdit->get_text()); + + std::unique_ptr<weld::TreeIter> xParent; + std::unique_ptr<weld::TreeIter> xChild = DoesBlockExist(aTitle, aShortName); + if (xChild && m_xCategoryBox->get_iter_depth(*xChild)) + { + xParent = m_xCategoryBox->make_iterator(xChild.get()); + m_xCategoryBox->iter_parent(*xParent); + } + + const bool bExists = nullptr != xChild; + const bool bIsGroup = bEntry && !xParent; + + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + SwResId(STR_QUERY_DELETE))); + if (bExists && !bIsGroup && RET_YES == xQuery->run()) + { + if (!aTitle.isEmpty() && m_pGlossaryHdl->DelGlossary(aShortName)) + { + OSL_ENSURE(xChild, "entry not found!"); + m_xCategoryBox->select(*xParent); + m_xCategoryBox->remove(*xChild); + m_xNameED->set_text(OUString()); + NameModify(*m_xNameED); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/impfnote.hxx b/sw/source/ui/misc/impfnote.hxx new file mode 100644 index 000000000..8984c566f --- /dev/null +++ b/sw/source/ui/misc/impfnote.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_MISC_IMPFNOTE_HXX +#define INCLUDED_SW_SOURCE_UI_MISC_IMPFNOTE_HXX + +#include <sfx2/tabdlg.hxx> +#include <vcl/weld.hxx> +#include <numberingtypelistbox.hxx> + +enum SwFootnoteNum : unsigned; +class SwWrtShell; + +class SwEndNoteOptionPage : public SfxTabPage +{ + OUString aNumDoc; + OUString aNumPage; + OUString aNumChapter; + SwWrtShell *pSh; + bool bPosDoc; + bool bEndNote; + + std::unique_ptr<SwNumberingTypeListBox> m_xNumViewBox; + std::unique_ptr<weld::Label> m_xOffsetLbl; + std::unique_ptr<weld::SpinButton> m_xOffsetField; + std::unique_ptr<weld::ComboBox> m_xNumCountBox; + std::unique_ptr<weld::Entry> m_xPrefixED; + std::unique_ptr<weld::Entry> m_xSuffixED; + std::unique_ptr<weld::Label> m_xPosFT; + std::unique_ptr<weld::RadioButton> m_xPosPageBox; + std::unique_ptr<weld::RadioButton> m_xPosChapterBox; + std::unique_ptr<weld::Widget> m_xStylesContainer; + std::unique_ptr<weld::ComboBox> m_xParaTemplBox; + std::unique_ptr<weld::Label> m_xPageTemplLbl; + std::unique_ptr<weld::ComboBox> m_xPageTemplBox; + std::unique_ptr<weld::ComboBox> m_xFootnoteCharAnchorTemplBox; + std::unique_ptr<weld::ComboBox> m_xFootnoteCharTextTemplBox; + std::unique_ptr<weld::Entry> m_xContEdit; + std::unique_ptr<weld::Entry> m_xContFromEdit; + + inline void SelectNumbering(SwFootnoteNum eNum); + SwFootnoteNum GetNumbering() const; + + DECL_LINK(PosPageHdl, weld::Button&, void); + DECL_LINK(PosChapterHdl, weld::Button&, void); + DECL_LINK(NumCountHdl, weld::ComboBox&, void); + +public: + SwEndNoteOptionPage(weld::Container* pPage, weld::DialogController* pController, bool bEndNote, const SfxItemSet &rSet); + virtual ~SwEndNoteOptionPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset( const SfxItemSet* ) override; + + void SetShell( SwWrtShell &rShell ); +}; + +class SwFootNoteOptionPage : public SwEndNoteOptionPage +{ +public: + SwFootNoteOptionPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet ); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + virtual ~SwFootNoteOptionPage() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/insfnote.cxx b/sw/source/ui/misc/insfnote.cxx new file mode 100644 index 000000000..23e5514a3 --- /dev/null +++ b/sw/source/ui/misc/insfnote.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/eitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/fontitem.hxx> +#include <fmtftn.hxx> +#include <swundo.hxx> +#include <cmdid.h> +#include <wrtsh.hxx> +#include <insfnote.hxx> +#include <svx/svxdlg.hxx> + +#include <memory> + +static bool bFootnote = true; + +// inserting a footnote with OK +void SwInsFootNoteDlg::Apply() +{ + OUString aStr; + if ( m_xNumberCharBtn->get_active() ) + aStr = m_xNumberCharEdit->get_text(); + + if (m_bEdit) + { + m_rSh.StartAction(); + m_rSh.Left(CRSR_SKIP_CHARS, false, 1, false ); + m_rSh.StartUndo( SwUndoId::START ); + SwFormatFootnote aNote( m_xEndNoteBtn->get_active() ); + aNote.SetNumStr( aStr ); + + if (m_rSh.SetCurFootnote( aNote ) && m_bExtCharAvailable) + { + m_rSh.Right(CRSR_SKIP_CHARS, true, 1, false ); + SfxItemSet aSet(m_rSh.GetAttrPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT>{}); + m_rSh.GetCurAttr(aSet); + const SvxFontItem &rFont = aSet.Get( RES_CHRATR_FONT ); + SvxFontItem aFont( rFont.GetFamily(), m_aFontName, + rFont.GetStyleName(), rFont.GetPitch(), + m_eCharSet, RES_CHRATR_FONT ); + aSet.Put( aFont ); + m_rSh.SetAttrSet( aSet, SetAttrMode::DONTEXPAND ); + m_rSh.ResetSelect(nullptr, false); + m_rSh.Left(CRSR_SKIP_CHARS, false, 1, false ); + } + m_rSh.EndUndo( SwUndoId::END ); + m_rSh.EndAction(); + } + + bFootnote = m_xFootnoteBtn->get_active(); +} + +IMPL_LINK_NOARG(SwInsFootNoteDlg, NumberCharHdl, weld::Button&, void) +{ + m_xNumberCharEdit->grab_focus(); + m_xOkBtn->set_sensitive( !m_xNumberCharEdit->get_text().isEmpty() || m_bExtCharAvailable ); +} + +IMPL_LINK_NOARG(SwInsFootNoteDlg, NumberEditHdl, weld::Entry&, void) +{ + m_xNumberCharBtn->set_active(true); + m_xOkBtn->set_sensitive( !m_xNumberCharEdit->get_text().isEmpty() ); +} + +IMPL_LINK_NOARG(SwInsFootNoteDlg, NumberAutoBtnHdl, weld::Button&, void) +{ + m_xOkBtn->set_sensitive(true); +} + +IMPL_LINK_NOARG(SwInsFootNoteDlg, NumberExtCharHdl, weld::Button&, void) +{ + m_xNumberCharBtn->set_active(true); + + SfxItemSet aSet(m_rSh.GetAttrPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT>{}); + m_rSh.GetCurAttr( aSet ); + const SvxFontItem &rFont = aSet.Get( RES_CHRATR_FONT ); + + SfxAllItemSet aAllSet(m_rSh.GetAttrPool()); + aAllSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + aAllSet.Put( rFont ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(m_xDialog.get(), aAllSet, nullptr)); + if (RET_OK == pDlg->Execute()) + { + const SfxStringItem* pItem = SfxItemSet::GetItem<SfxStringItem>(pDlg->GetOutputItemSet(), SID_CHARMAP, false); + const SvxFontItem* pFontItem = SfxItemSet::GetItem<SvxFontItem>(pDlg->GetOutputItemSet(), SID_ATTR_CHAR_FONT, false); + if ( pItem ) + { + m_xNumberCharEdit->set_text(pItem->GetValue()); + + if ( pFontItem ) + { + m_aFontName = pFontItem->GetFamilyName(); + m_eCharSet = pFontItem->GetCharSet(); + vcl::Font aFont(m_aFontName, pFontItem->GetStyleName(), m_xNumberCharEdit->get_font().GetFontSize()); + aFont.SetCharSet( pFontItem->GetCharSet() ); + aFont.SetPitch( pFontItem->GetPitch() ); + m_xNumberCharEdit->set_font(aFont); + } + + m_bExtCharAvailable = true; + m_xOkBtn->set_sensitive(!m_xNumberCharEdit->get_text().isEmpty()); + } + } +} + +IMPL_LINK( SwInsFootNoteDlg, NextPrevHdl, weld::Button&, rBtn, void ) +{ + Apply(); + + // go to the next foot/endnote here + m_rSh.ResetSelect(nullptr, false); + if (&rBtn == m_xNextBT.get()) + m_rSh.GotoNextFootnoteAnchor(); + else + m_rSh.GotoPrevFootnoteAnchor(); + + Init(); +} + +SwInsFootNoteDlg::SwInsFootNoteDlg(weld::Window *pParent, SwWrtShell &rShell, bool bEd) + : GenericDialogController(pParent, "modules/swriter/ui/insertfootnote.ui", "InsertFootnoteDialog") + , m_rSh(rShell) + , m_eCharSet(RTL_TEXTENCODING_DONTKNOW) + , m_bExtCharAvailable(false) + , m_bEdit(bEd) + , m_xNumberFrame(m_xBuilder->weld_widget("numberingframe")) + , m_xNumberAutoBtn(m_xBuilder->weld_radio_button("automatic")) + , m_xNumberCharBtn(m_xBuilder->weld_radio_button("character")) + , m_xNumberCharEdit(m_xBuilder->weld_entry("characterentry")) + , m_xNumberExtChar(m_xBuilder->weld_button("choosecharacter")) + , m_xFootnoteBtn(m_xBuilder->weld_radio_button("footnote")) + , m_xEndNoteBtn(m_xBuilder->weld_radio_button("endnote")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , m_xPrevBT(m_xBuilder->weld_button("prev")) + , m_xNextBT(m_xBuilder->weld_button("next")) +{ + m_xNumberAutoBtn->connect_clicked(LINK(this,SwInsFootNoteDlg,NumberAutoBtnHdl)); + m_xNumberExtChar->connect_clicked(LINK(this,SwInsFootNoteDlg,NumberExtCharHdl)); + m_xNumberCharBtn->connect_clicked(LINK(this,SwInsFootNoteDlg,NumberCharHdl)); + m_xNumberCharEdit->connect_changed(LINK(this,SwInsFootNoteDlg,NumberEditHdl)); + + m_xPrevBT->connect_clicked(LINK(this, SwInsFootNoteDlg, NextPrevHdl)); + m_xNextBT->connect_clicked(LINK(this, SwInsFootNoteDlg, NextPrevHdl)); + + SwViewShell::SetCareDialog(m_xDialog); + + if (m_bEdit) + { + Init(); + + m_xPrevBT->show(); + m_xNextBT->show(); + } +} + +SwInsFootNoteDlg::~SwInsFootNoteDlg() COVERITY_NOEXCEPT_FALSE +{ + SwViewShell::SetCareDialog(nullptr); + + if (m_bEdit) + m_rSh.ResetSelect(nullptr, false); +} + +void SwInsFootNoteDlg::Init() +{ + SwFormatFootnote aFootnoteNote; + OUString sNumStr; + vcl::Font aFont; + m_bExtCharAvailable = false; + + m_rSh.StartAction(); + + if (m_rSh.GetCurFootnote(&aFootnoteNote)) + { + if (!aFootnoteNote.GetNumStr().isEmpty()) + { + sNumStr = aFootnoteNote.GetNumStr(); + + m_rSh.Right(CRSR_SKIP_CHARS, true, 1, false ); + SfxItemSet aSet(m_rSh.GetAttrPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT>{}); + m_rSh.GetCurAttr(aSet); + const SvxFontItem &rFont = aSet.Get( RES_CHRATR_FONT ); + aFont = m_xNumberCharEdit->get_font(); + m_aFontName = rFont.GetFamilyName(); + m_eCharSet = rFont.GetCharSet(); + aFont.SetFamilyName(m_aFontName); + aFont.SetCharSet(m_eCharSet); + m_bExtCharAvailable = true; + m_rSh.Left( CRSR_SKIP_CHARS, false, 1, false ); + } + bFootnote = !aFootnoteNote.IsEndNote(); + } + m_xNumberCharEdit->set_font(aFont); + + const bool bNumChar = !sNumStr.isEmpty(); + + m_xNumberCharEdit->set_text(sNumStr); + m_xNumberCharBtn->set_active(bNumChar); + m_xNumberAutoBtn->set_active(!bNumChar); + if (bNumChar) + m_xNumberCharEdit->grab_focus(); + + if (bFootnote) + m_xFootnoteBtn->set_active(true); + else + m_xEndNoteBtn->set_active(true); + + bool bNext = m_rSh.GotoNextFootnoteAnchor(); + + if (bNext) + m_rSh.GotoPrevFootnoteAnchor(); + + bool bPrev = m_rSh.GotoPrevFootnoteAnchor(); + + if (bPrev) + m_rSh.GotoNextFootnoteAnchor(); + + m_xPrevBT->set_sensitive(bPrev); + m_xNextBT->set_sensitive(bNext); + + m_rSh.Right(CRSR_SKIP_CHARS, true, 1, false ); + + m_rSh.EndAction(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/linenum.cxx b/sw/source/ui/misc/linenum.cxx new file mode 100644 index 000000000..2d6cbd063 --- /dev/null +++ b/sw/source/ui/misc/linenum.cxx @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/viewfrm.hxx> +#include <svl/style.hxx> +#include <svtools/unitconv.hxx> +#include <sal/log.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <charfmt.hxx> + +#include <docstyle.hxx> + +#include <lineinfo.hxx> +#include <linenum.hxx> +#include <swmodule.hxx> +#include <uitool.hxx> +#include <usrpref.hxx> +#include <wdocsh.hxx> +#include <fmtline.hxx> +#include <strings.hrc> + +#include <IDocumentStylePoolAccess.hxx> + +static rtl::Reference<SwDocStyleSheet> lcl_getDocStyleSheet(const OUString& rName, SwWrtShell *pSh) +{ + SfxStyleSheetBasePool* pBase = pSh->GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase* pStyle = pBase->Find(rName, SfxStyleFamily::Para); + SAL_WARN_IF( !pStyle, "sw.ui", "Style not found" ); + if(!pStyle) + return nullptr; + return new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pStyle)); +} + +static void lcl_setLineNumbering(const OUString& rName, SwWrtShell* pSh, bool bLineNumber) +{ + rtl::Reference<SwDocStyleSheet> xStyleSheet = lcl_getDocStyleSheet(rName, pSh); + if(!xStyleSheet.is()) + return; + SfxItemSet& rSet = xStyleSheet->GetItemSet(); + SwFormatLineNumber aFormat; + aFormat.SetCountLines(bLineNumber); + rSet.Put(aFormat); + xStyleSheet->MergeIndentAttrsOfListStyle( rSet ); + xStyleSheet->SetItemSet(rSet); +} + +SwLineNumberingDlg::SwLineNumberingDlg(const SwView& rVw) + : SfxDialogController(rVw.GetViewFrame()->GetWindow().GetFrameWeld(), + "modules/swriter/ui/linenumbering.ui", "LineNumberingDialog") + , m_pSh(rVw.GetWrtShellPtr()) + , m_xBodyContent(m_xBuilder->weld_widget("content")) + , m_xDivIntervalFT(m_xBuilder->weld_widget("every")) + , m_xDivIntervalNF(m_xBuilder->weld_spin_button("linesspin")) + , m_xDivRowsFT(m_xBuilder->weld_widget("lines")) + , m_xNumIntervalNF(m_xBuilder->weld_spin_button("intervalspin")) + , m_xCharStyleLB(m_xBuilder->weld_combo_box("styledropdown")) + , m_xFormatLB(new SwNumberingTypeListBox(m_xBuilder->weld_combo_box("formatdropdown"))) + , m_xPosLB(m_xBuilder->weld_combo_box("positiondropdown")) + , m_xOffsetMF(m_xBuilder->weld_metric_spin_button("spacingspin", FieldUnit::CM)) + , m_xDivisorED(m_xBuilder->weld_entry("textentry")) + , m_xCountEmptyLinesCB(m_xBuilder->weld_check_button("blanklines")) + , m_xCountFrameLinesCB(m_xBuilder->weld_check_button("linesintextframes")) + , m_xRestartEachPageCB(m_xBuilder->weld_check_button("restarteverynewpage")) + , m_xNumberingOnCB(m_xBuilder->weld_check_button("shownumbering")) + , m_xNumberingOnFooterHeader(m_xBuilder->weld_check_button("showfooterheadernumbering")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_xNumIntervalFT(m_xBuilder->weld_widget("interval")) + , m_xNumRowsFT(m_xBuilder->weld_widget("intervallines")) +{ + m_xFormatLB->Reload(SwInsertNumTypes::Extended); + + OUString sIntervalName = m_xDivIntervalFT->get_accessible_name() + + "(" + + m_xDivRowsFT->get_accessible_name() + + ")"; + m_xDivIntervalNF->set_accessible_name(sIntervalName); + + sIntervalName = m_xNumIntervalFT->get_accessible_name() + + "(" + + m_xNumRowsFT->get_accessible_name() + + ")"; + m_xNumIntervalNF->set_accessible_name(sIntervalName); + + // char styles + ::FillCharStyleListBox(*m_xCharStyleLB, m_pSh->GetView().GetDocShell()); + + const SwLineNumberInfo &rInf = m_pSh->GetLineNumberInfo(); + IDocumentStylePoolAccess& rIDSPA = m_pSh->getIDocumentStylePoolAccess(); + + OUString sStyleName(rInf.GetCharFormat( rIDSPA )->GetName()); + const int nPos = m_xCharStyleLB->find_text(sStyleName); + + if (nPos != -1) + m_xCharStyleLB->set_active(nPos); + else + { + if (!sStyleName.isEmpty()) + { + m_xCharStyleLB->append_text(sStyleName); + m_xCharStyleLB->set_active_text(sStyleName); + } + } + + // format + SvxNumType nSelFormat = rInf.GetNumType().GetNumberingType(); + + m_xFormatLB->SelectNumberingType(nSelFormat); + + // position + m_xPosLB->set_active(rInf.GetPos()); + + // offset + sal_uInt16 nOffset = rInf.GetPosFromLeft(); + if (nOffset == USHRT_MAX) + nOffset = 0; + + FieldUnit eFieldUnit = SW_MOD()->GetUsrPref(dynamic_cast< const SwWebDocShell*>( + rVw.GetDocShell()) != nullptr)->GetMetric(); + ::SetFieldUnit(*m_xOffsetMF, eFieldUnit); + m_xOffsetMF->set_value(m_xOffsetMF->normalize(nOffset), FieldUnit::TWIP); + + // numbering offset + m_xNumIntervalNF->set_value(rInf.GetCountBy()); + + // divider + m_xDivisorED->set_text(rInf.GetDivider()); + + // divider offset + m_xDivIntervalNF->set_value(rInf.GetDividerCountBy()); + + // count + m_xCountEmptyLinesCB->set_active(rInf.IsCountBlankLines()); + m_xCountFrameLinesCB->set_active(rInf.IsCountInFlys()); + m_xRestartEachPageCB->set_active(rInf.IsRestartEachPage()); + + m_xNumberingOnCB->set_active(rInf.IsPaintLineNumbers()); + + // Header/Footer Line Numbering + rtl::Reference< SwDocStyleSheet > xStyleSheet = lcl_getDocStyleSheet(SwResId(STR_POOLCOLL_FOOTER), m_pSh); + if(xStyleSheet.is()) + { + SfxItemSet& rSet = xStyleSheet->GetItemSet(); + const SwFormatLineNumber &aFormat = rSet.Get(RES_LINENUMBER); + if (aFormat.IsCount()) + m_xNumberingOnFooterHeader->set_state(TRISTATE_TRUE); + else + m_xNumberingOnFooterHeader->set_state(TRISTATE_FALSE); + } + + // Line Numbering + m_xNumberingOnCB->connect_clicked(LINK(this, SwLineNumberingDlg, LineOnOffHdl)); + m_xDivisorED->connect_changed(LINK(this, SwLineNumberingDlg, ModifyHdl)); + ModifyHdl(*m_xDivisorED); + LineOnOffHdl(*m_xNumberingOnCB); + + m_xOKButton->connect_clicked(LINK(this, SwLineNumberingDlg, OKHdl)); +} + +SwLineNumberingDlg::~SwLineNumberingDlg() +{ +} + +IMPL_LINK_NOARG(SwLineNumberingDlg, OKHdl, weld::Button&, void) +{ + SwLineNumberInfo aInf(m_pSh->GetLineNumberInfo()); + + // char styles + OUString sCharFormatName(m_xCharStyleLB->get_active_text()); + SwCharFormat *pCharFormat = m_pSh->FindCharFormatByName(sCharFormatName); + + if (!pCharFormat) + { + SfxStyleSheetBasePool* pPool = m_pSh->GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase* pBase; + pBase = pPool->Find(sCharFormatName, SfxStyleFamily::Char); + if(!pBase) + pBase = &pPool->Make(sCharFormatName, SfxStyleFamily::Char); + pCharFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat(); + } + + if (pCharFormat) + aInf.SetCharFormat(pCharFormat); + + // format + SvxNumberType aType; + aType.SetNumberingType(m_xFormatLB->GetSelectedNumberingType()); + aInf.SetNumType(aType); + + // position + aInf.SetPos(static_cast<LineNumberPosition>(m_xPosLB->get_active())); + + // offset + aInf.SetPosFromLeft(static_cast<sal_uInt16>(m_xOffsetMF->denormalize(m_xOffsetMF->get_value(FieldUnit::TWIP)))); + + // numbering offset + aInf.SetCountBy(static_cast<sal_uInt16>(m_xNumIntervalNF->get_value())); + + // divider + aInf.SetDivider(m_xDivisorED->get_text()); + + // divider offset + aInf.SetDividerCountBy(static_cast<sal_uInt16>(m_xDivIntervalNF->get_value())); + + // count + aInf.SetCountBlankLines(m_xCountEmptyLinesCB->get_active()); + aInf.SetCountInFlys(m_xCountFrameLinesCB->get_active()); + aInf.SetRestartEachPage(m_xRestartEachPageCB->get_active()); + + aInf.SetPaintLineNumbers(m_xNumberingOnCB->get_active()); + + m_pSh->SetLineNumberInfo(aInf); + + // Set LineNumber explicitly for Header and Footer + lcl_setLineNumbering(SwResId(STR_POOLCOLL_FOOTER), m_pSh, m_xNumberingOnFooterHeader->get_active()); + lcl_setLineNumbering(SwResId(STR_POOLCOLL_HEADER), m_pSh, m_xNumberingOnFooterHeader->get_active()); + if( m_xNumberingOnFooterHeader->get_active()) + m_xNumberingOnFooterHeader->set_state(TRISTATE_TRUE); + else + m_xNumberingOnFooterHeader->set_state(TRISTATE_FALSE); + + m_xDialog->response(RET_OK); +} + +// modify +IMPL_LINK_NOARG(SwLineNumberingDlg, ModifyHdl, weld::Entry&, void) +{ + bool bEnable = m_xNumberingOnCB->get_active() && !m_xDivisorED->get_text().isEmpty(); + + m_xDivIntervalFT->set_sensitive(bEnable); + m_xDivIntervalNF->set_sensitive(bEnable); + m_xDivRowsFT->set_sensitive(bEnable); +} + +// On/Off +IMPL_LINK_NOARG(SwLineNumberingDlg, LineOnOffHdl, weld::Button&, void) +{ + bool bEnable = m_xNumberingOnCB->get_active(); + m_xBodyContent->set_sensitive(bEnable); + ModifyHdl(*m_xDivisorED); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/num.cxx b/sw/source/ui/misc/num.cxx new file mode 100644 index 000000000..1dce5e2bc --- /dev/null +++ b/sw/source/ui/misc/num.cxx @@ -0,0 +1,957 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/numitem.hxx> +#include <cmdid.h> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <wview.hxx> +#include <uitool.hxx> +#include <wdocsh.hxx> +#include <uiitems.hxx> +#include <poolfmt.hxx> +#include <shellres.hxx> +#include <outline.hxx> +#include <num.hxx> + +#include <SwStyleNameMapper.hxx> +#include <svx/dialogs.hrc> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <svl/slstitm.hxx> +#include <svl/intitem.hxx> +#include <comphelper/lok.hxx> + +static bool bLastRelative = false; + +//See cui/uiconfig/ui/numberingpositionpage.ui for effectively a duplicate +//dialog to this one, except with a different preview window impl. +//TODO, determine if SwNumPositionTabPage and SvxNumPositionTabPage can be +//merged +SwNumPositionTabPage::SwNumPositionTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/outlinepositionpage.ui", "OutlinePositionPage", &rSet) + , pSaveNum(nullptr) + , pWrtSh(nullptr) + , pOutlineDlg(nullptr) + , nActNumLvl(0) + , bModified(false) + , bPreset(false) + , bInInintControl(false) + , bLabelAlignmentPosAndSpaceModeActive(false) + , m_xLevelLB(m_xBuilder->weld_tree_view("levellb")) + , m_xPositionFrame(m_xBuilder->weld_widget("numberingframe")) + , m_xDistBorderFT(m_xBuilder->weld_label("indent")) + , m_xDistBorderMF(m_xBuilder->weld_metric_spin_button("indentmf", FieldUnit::CM)) + , m_xRelativeCB(m_xBuilder->weld_check_button("relative")) + , m_xIndentFT(m_xBuilder->weld_label("numberingwidth")) + , m_xIndentMF(m_xBuilder->weld_metric_spin_button("numberingwidthmf", FieldUnit::CM)) + , m_xDistNumFT(m_xBuilder->weld_label("numdist")) + , m_xDistNumMF(m_xBuilder->weld_metric_spin_button("numdistmf", FieldUnit::CM)) + , m_xAlignFT(m_xBuilder->weld_label("numalign")) + , m_xAlignLB(m_xBuilder->weld_combo_box("numalignlb")) + , m_xLabelFollowedByFT(m_xBuilder->weld_label("numfollowedby")) + , m_xLabelFollowedByLB(m_xBuilder->weld_combo_box("numfollowedbylb")) + , m_xListtabFT(m_xBuilder->weld_label("at")) + , m_xListtabMF(m_xBuilder->weld_metric_spin_button("atmf", FieldUnit::CM)) + , m_xAlign2FT(m_xBuilder->weld_label("num2align")) + , m_xAlign2LB(m_xBuilder->weld_combo_box("num2alignlb")) + , m_xAlignedAtFT(m_xBuilder->weld_label("alignedat")) + , m_xAlignedAtMF(m_xBuilder->weld_metric_spin_button("alignedatmf", FieldUnit::CM)) + , m_xIndentAtFT(m_xBuilder->weld_label("indentat")) + , m_xIndentAtMF(m_xBuilder->weld_metric_spin_button("indentatmf", FieldUnit::CM)) + , m_xStandardPB(m_xBuilder->weld_button("standard")) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWIN)) +{ + SetExchangeSupport(); + + m_xLevelLB->set_selection_mode(SelectionMode::Multiple); + + m_xRelativeCB->set_active(true); + m_xAlignLB->connect_changed(LINK(this, SwNumPositionTabPage, EditModifyHdl)); + m_xAlign2LB->connect_changed(LINK(this, SwNumPositionTabPage, EditModifyHdl)); + for (int i = 0; i < m_xAlignLB->get_count(); ++i) + { + m_xAlign2LB->append_text(m_xAlignLB->get_text(i)); + } + m_xAlign2FT->set_label(m_xAlignFT->get_label()); + + Link<weld::MetricSpinButton&, void> aLk = LINK(this, SwNumPositionTabPage, DistanceHdl); + m_xDistBorderMF->connect_value_changed(aLk); + m_xDistNumMF->connect_value_changed(aLk); + m_xIndentMF->connect_value_changed(aLk); + + m_xLabelFollowedByLB->connect_changed( LINK(this, SwNumPositionTabPage, LabelFollowedByHdl_Impl) ); + + aLk = LINK(this, SwNumPositionTabPage, ListtabPosHdl_Impl); + m_xListtabMF->connect_value_changed(aLk); + + aLk = LINK(this, SwNumPositionTabPage, AlignAtHdl_Impl); + m_xAlignedAtMF->connect_value_changed(aLk); + + aLk = LINK(this, SwNumPositionTabPage, IndentAtHdl_Impl); + m_xIndentAtMF->connect_value_changed(aLk); + + m_xLevelLB->connect_changed(LINK(this, SwNumPositionTabPage, LevelHdl)); + m_xRelativeCB->connect_toggled(LINK(this, SwNumPositionTabPage, RelativeHdl)); + m_xStandardPB->connect_clicked(LINK(this, SwNumPositionTabPage, StandardHdl)); + + // insert levels + for(sal_uInt16 i = 1; i <= MAXLEVEL; i++) + m_xLevelLB->append_text(OUString::number(i)); + OUString sEntry = "1 - " + OUString::number(MAXLEVEL); + m_xLevelLB->append_text(sEntry); + m_xLevelLB->select_text(sEntry); + + m_xRelativeCB->set_active(bLastRelative); + m_aPreviewWIN.SetPositionMode(); +} + +SwNumPositionTabPage::~SwNumPositionTabPage() +{ + pActNum.reset(); + pOutlineDlg = nullptr; +} + +void SwNumPositionTabPage::InitControls() +{ + bInInintControl = true; + const bool bRelative = !bLabelAlignmentPosAndSpaceModeActive && + m_xRelativeCB->get_sensitive() && m_xRelativeCB->get_active(); + const bool bSingleSelection = m_xLevelLB->count_selected_rows() == 1 && + USHRT_MAX != nActNumLvl; + + m_xDistBorderMF->set_sensitive( !bLabelAlignmentPosAndSpaceModeActive && + ( bSingleSelection || bRelative || pOutlineDlg != nullptr ) ); + m_xDistBorderFT->set_sensitive( !bLabelAlignmentPosAndSpaceModeActive && + ( bSingleSelection || bRelative || pOutlineDlg != nullptr ) ); + + bool bSetDistEmpty = false; + bool bSameDistBorderNum = !bLabelAlignmentPosAndSpaceModeActive; + bool bSameDist = !bLabelAlignmentPosAndSpaceModeActive; + bool bSameIndent = !bLabelAlignmentPosAndSpaceModeActive; + bool bSameAdjust = true; + + bool bSameLabelFollowedBy = bLabelAlignmentPosAndSpaceModeActive; + bool bSameListtab = bLabelAlignmentPosAndSpaceModeActive; + bool bSameAlignAt = bLabelAlignmentPosAndSpaceModeActive; + bool bSameIndentAt = bLabelAlignmentPosAndSpaceModeActive; + + const SwNumFormat* aNumFormatArr[MAXLEVEL]; + sal_uInt16 nMask = 1; + sal_uInt16 nLvl = USHRT_MAX; + long nFirstBorderTextRelative = -1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + aNumFormatArr[i] = &pActNum->Get(i); + if(nActNumLvl & nMask) + { + if(USHRT_MAX == nLvl) + { + nLvl = i; + } + + if( i > nLvl) + { + bSameAdjust &= aNumFormatArr[i]->GetNumAdjust() == aNumFormatArr[nLvl]->GetNumAdjust(); + if ( !bLabelAlignmentPosAndSpaceModeActive ) + { + if(bRelative) + { + const long nBorderTextRelative = + aNumFormatArr[i]->GetAbsLSpace() + aNumFormatArr[i]->GetFirstLineOffset() - + aNumFormatArr[i - 1]->GetAbsLSpace() + aNumFormatArr[i - 1]->GetFirstLineOffset(); + if (nFirstBorderTextRelative == -1) + nFirstBorderTextRelative = nBorderTextRelative; + else + bSameDistBorderNum &= nFirstBorderTextRelative == nBorderTextRelative; + } + else + { + bSameDistBorderNum &= + aNumFormatArr[i]->GetAbsLSpace() - aNumFormatArr[i]->GetFirstLineOffset() == + aNumFormatArr[i - 1]->GetAbsLSpace() - aNumFormatArr[i - 1]->GetFirstLineOffset(); + } + + bSameDist &= aNumFormatArr[i]->GetCharTextDistance() == aNumFormatArr[nLvl]->GetCharTextDistance(); + bSameIndent &= aNumFormatArr[i]->GetFirstLineOffset() == aNumFormatArr[nLvl]->GetFirstLineOffset(); + } + else + { + bSameLabelFollowedBy &= + aNumFormatArr[i]->GetLabelFollowedBy() == aNumFormatArr[nLvl]->GetLabelFollowedBy(); + bSameListtab &= + aNumFormatArr[i]->GetListtabPos() == aNumFormatArr[nLvl]->GetListtabPos(); + bSameAlignAt &= + ( ( aNumFormatArr[i]->GetIndentAt() + aNumFormatArr[i]->GetFirstLineIndent() ) + == ( aNumFormatArr[nLvl]->GetIndentAt() + aNumFormatArr[nLvl]->GetFirstLineIndent() ) ); + bSameIndentAt &= + aNumFormatArr[i]->GetIndentAt() == aNumFormatArr[nLvl]->GetIndentAt(); + } + } + } + nMask <<= 1; + + } + if (MAXLEVEL <= nLvl) + { + OSL_ENSURE(false, "cannot happen."); + return; + } + if(bSameDistBorderNum) + { + long nDistBorderNum; + if(bRelative) + { + nDistBorderNum = static_cast<long>(aNumFormatArr[nLvl]->GetAbsLSpace())+ aNumFormatArr[nLvl]->GetFirstLineOffset(); + if(nLvl) + nDistBorderNum -= static_cast<long>(aNumFormatArr[nLvl - 1]->GetAbsLSpace())+ aNumFormatArr[nLvl - 1]->GetFirstLineOffset(); + } + else + { + nDistBorderNum = static_cast<long>(aNumFormatArr[nLvl]->GetAbsLSpace())+ aNumFormatArr[nLvl]->GetFirstLineOffset(); + } + m_xDistBorderMF->set_value(m_xDistBorderMF->normalize(nDistBorderNum),FieldUnit::TWIP); + } + else + bSetDistEmpty = true; + + if(bSameDist) + m_xDistNumMF->set_value(m_xDistNumMF->normalize(aNumFormatArr[nLvl]->GetCharTextDistance()), FieldUnit::TWIP); + else + m_xDistNumMF->set_text(OUString()); + if(bSameIndent) + m_xIndentMF->set_value(m_xIndentMF->normalize(-aNumFormatArr[nLvl]->GetFirstLineOffset()), FieldUnit::TWIP); + else + m_xIndentMF->set_text(OUString()); + + if(bSameAdjust) + { + sal_Int32 nPos = 1; // centered + if(aNumFormatArr[nLvl]->GetNumAdjust() == SvxAdjust::Left) + nPos = 0; + else if(aNumFormatArr[nLvl]->GetNumAdjust() == SvxAdjust::Right) + nPos = 2; + m_xAlignLB->set_active(nPos); + m_xAlign2LB->set_active( nPos ); + } + else + { + m_xAlignLB->set_active(-1); + m_xAlign2LB->set_active(-1); + } + + if ( bSameLabelFollowedBy ) + { + sal_Int32 nPos = 0; // LISTTAB + if ( aNumFormatArr[nLvl]->GetLabelFollowedBy() == SvxNumberFormat::SPACE ) + { + nPos = 1; + } + else if ( aNumFormatArr[nLvl]->GetLabelFollowedBy() == SvxNumberFormat::NOTHING ) + { + nPos = 2; + } + else if ( aNumFormatArr[nLvl]->GetLabelFollowedBy() == SvxNumberFormat::NEWLINE ) + { + nPos = 3; + } + m_xLabelFollowedByLB->set_active(nPos); + } + else + { + m_xLabelFollowedByLB->set_active(-1); + } + + if ( aNumFormatArr[nLvl]->GetLabelFollowedBy() == SvxNumberFormat::LISTTAB ) + { + m_xListtabFT->set_sensitive(true); + m_xListtabMF->set_sensitive(true); + if ( bSameListtab ) + { + m_xListtabMF->set_value(m_xListtabMF->normalize(aNumFormatArr[nLvl]->GetListtabPos()),FieldUnit::TWIP); + } + else + { + m_xListtabMF->set_text(OUString()); + } + } + else + { + m_xListtabFT->set_sensitive( false ); + m_xListtabMF->set_sensitive( false ); + m_xListtabMF->set_text(OUString()); + } + + if ( bSameAlignAt ) + { + m_xAlignedAtMF->set_value( + m_xAlignedAtMF->normalize( aNumFormatArr[nLvl]->GetIndentAt() + + aNumFormatArr[nLvl]->GetFirstLineIndent()), + FieldUnit::TWIP ); + } + else + { + m_xAlignedAtMF->set_text(OUString()); + } + + if ( bSameIndentAt ) + { + m_xIndentAtMF->set_value( + m_xIndentAtMF->normalize( aNumFormatArr[nLvl]->GetIndentAt()), FieldUnit::TWIP ); + } + else + { + m_xIndentAtMF->set_text(OUString()); + } + + if (bSetDistEmpty) + m_xDistBorderMF->set_text(OUString()); + + bInInintControl = false; +} + +void SwNumPositionTabPage::ActivatePage(const SfxItemSet& ) +{ + const SfxPoolItem* pItem; + sal_uInt16 nTmpNumLvl = + pOutlineDlg ? SwOutlineTabDialog::GetActNumLevel() : 0; + const SfxItemSet* pExampleSet = GetDialogExampleSet(); + if(pExampleSet && pExampleSet->GetItemState(FN_PARAM_NUM_PRESET, false, &pItem) != SfxItemState::UNKNOWN) + { + bPreset = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + } + bModified = (!pActNum->GetNumFormat( 0 ) || bPreset); + if(*pActNum != *pSaveNum || + nActNumLvl != nTmpNumLvl ) + { + *pActNum = *pSaveNum; + nActNumLvl = nTmpNumLvl; + sal_uInt16 nMask = 1; + m_xLevelLB->unselect_all(); + if (nActNumLvl == USHRT_MAX) + m_xLevelLB->select(MAXLEVEL); + else + { + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1 ; + } + } + + InitPosAndSpaceMode(); + ShowControlsDependingOnPosAndSpaceMode(); + + InitControls(); + } + m_xRelativeCB->set_sensitive(1 != nActNumLvl); + m_aPreviewWIN.Invalidate(); +} + +DeactivateRC SwNumPositionTabPage::DeactivatePage(SfxItemSet *_pSet) +{ + SwOutlineTabDialog::SetActNumLevel(nActNumLvl); + if(_pSet) + FillItemSet(_pSet); + return DeactivateRC::LeavePage; + +} + +bool SwNumPositionTabPage::FillItemSet( SfxItemSet* rSet ) +{ + if(pOutlineDlg) + *pOutlineDlg->GetNumRule() = *pActNum; + else if(bModified && pActNum) + { + *pSaveNum = *pActNum; + rSet->Put(SwUINumRuleItem( *pSaveNum )); + rSet->Put(SfxBoolItem(FN_PARAM_NUM_PRESET, false)); + } + return bModified; +} + +void SwNumPositionTabPage::Reset( const SfxItemSet* rSet ) +{ + const SfxPoolItem* pItem; + if (pOutlineDlg) + { + pSaveNum = pOutlineDlg->GetNumRule(); + m_xLevelLB->set_selection_mode(SelectionMode::Single); + } + else if(SfxItemState::SET == rSet->GetItemState(FN_PARAM_ACT_NUMBER, false, &pItem)) + pSaveNum = const_cast<SwUINumRuleItem*>(static_cast<const SwUINumRuleItem*>(pItem))->GetNumRule(); + + nActNumLvl = SwOutlineTabDialog::GetActNumLevel(); + sal_uInt16 nMask = 1; + m_xLevelLB->unselect_all(); + if(nActNumLvl == USHRT_MAX) + { + m_xLevelLB->select(MAXLEVEL); + } + else + { + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1; + } + } + + if(!pActNum) + pActNum.reset(new SwNumRule(*pSaveNum)); + else if(*pSaveNum != *pActNum) + *pActNum = *pSaveNum; + m_aPreviewWIN.SetNumRule(pActNum.get()); + InitPosAndSpaceMode(); + ShowControlsDependingOnPosAndSpaceMode(); + InitControls(); + bModified = false; +} + +void SwNumPositionTabPage::InitPosAndSpaceMode() +{ + if ( pActNum == nullptr ) + { + OSL_FAIL( "<SwNumPositionTabPage::InitPosAndSpaceMode()> - misusage of method -> <pAktNum> has to be already set!" ); + return; + } + + SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode = + SvxNumberFormat::LABEL_ALIGNMENT; + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if(nActNumLvl & nMask) + { + SvxNumberFormat aNumFormat( pActNum->Get(i) ); + ePosAndSpaceMode = aNumFormat.GetPositionAndSpaceMode(); + if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) + { + break; + } + } + nMask <<= 1; + } + + bLabelAlignmentPosAndSpaceModeActive = + ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT; +} + +void SwNumPositionTabPage::ShowControlsDependingOnPosAndSpaceMode() +{ + m_xDistBorderFT->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xDistBorderMF->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xRelativeCB->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xIndentFT->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xIndentMF->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xDistNumFT->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xDistNumMF->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xAlignFT->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + m_xAlignLB->set_visible( !bLabelAlignmentPosAndSpaceModeActive ); + + m_xLabelFollowedByFT->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xLabelFollowedByLB->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xListtabFT->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xListtabMF->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xAlign2FT->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xAlign2LB->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xAlignedAtFT->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xAlignedAtMF->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xIndentAtFT->set_visible( bLabelAlignmentPosAndSpaceModeActive ); + m_xIndentAtMF->set_visible( bLabelAlignmentPosAndSpaceModeActive ); +} + +std::unique_ptr<SfxTabPage> SwNumPositionTabPage::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwNumPositionTabPage>(pPage, pController, *rAttrSet); +} + +void SwNumPositionTabPage::SetWrtShell(SwWrtShell* pSh) +{ + pWrtSh = pSh; + + const SwTwips nWidth = pWrtSh->GetAnyCurRect(CurRectType::Frame).Width(); + + m_xDistBorderMF->set_max(m_xDistBorderMF->normalize( nWidth ), FieldUnit::TWIP ); + m_xDistNumMF->set_max(m_xDistNumMF->normalize( nWidth ), FieldUnit::TWIP); + m_xIndentMF->set_max(m_xIndentMF->normalize( nWidth ), FieldUnit::TWIP ); + m_xListtabMF->set_max(m_xListtabMF->normalize( nWidth ), FieldUnit::TWIP ); + m_xAlignedAtMF->set_max(m_xAlignedAtMF->normalize( nWidth ), FieldUnit::TWIP ); + m_xIndentAtMF->set_max(m_xIndentAtMF->normalize( nWidth ), FieldUnit::TWIP ); + + const SwRect& rPrtRect = pWrtSh->GetAnyCurRect(CurRectType::Page); + m_aPreviewWIN.SetPageWidth(rPrtRect.Width()); + FieldUnit eMetric = ::GetDfltMetric( dynamic_cast<SwWebView*>( &pWrtSh->GetView()) != nullptr ); + if(eMetric == FieldUnit::MM) + { + m_xDistBorderMF->set_digits(1); + m_xDistNumMF->set_digits(1); + m_xIndentMF->set_digits(1); + m_xListtabMF->set_digits(1); + m_xAlignedAtMF->set_digits(1); + m_xIndentAtMF->set_digits(1); + } + m_xDistBorderMF->set_unit( eMetric ); + m_xDistNumMF->set_unit( eMetric ); + m_xIndentMF->set_unit( eMetric ); + m_xListtabMF->set_unit( eMetric ); + m_xAlignedAtMF->set_unit( eMetric ); + m_xIndentAtMF->set_unit( eMetric ); +} + +IMPL_LINK_NOARG(SwNumPositionTabPage, EditModifyHdl, weld::ComboBox&, void) +{ + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActNumLvl & nMask) + { + SwNumFormat aNumFormat(pActNum->Get(i)); + + const int nPos = m_xAlignLB->get_visible() + ? m_xAlignLB->get_active() + : m_xAlign2LB->get_active(); + SvxAdjust eAdjust = SvxAdjust::Center; + if(nPos == 0) + eAdjust = SvxAdjust::Left; + else if(nPos == 2) + eAdjust = SvxAdjust::Right; + aNumFormat.SetNumAdjust( eAdjust ); + pActNum->Set(i, aNumFormat); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK( SwNumPositionTabPage, LevelHdl, weld::TreeView&, rBox, void ) +{ + sal_uInt16 nSaveNumLvl = nActNumLvl; + nActNumLvl = 0; + auto aRows = rBox.get_selected_rows(); + if ((std::find(aRows.begin(), aRows.end(), MAXLEVEL) != aRows.end()) && + (aRows.size() == 1 || nSaveNumLvl != 0xffff)) + { + nActNumLvl = 0xFFFF; + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + rBox.unselect(i); + } + else if (!aRows.empty()) + { + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + if (std::find(aRows.begin(), aRows.end(), i) != aRows.end()) + nActNumLvl |= nMask; + nMask <<= 1; + } + rBox.unselect(MAXLEVEL); + } + else + { + nActNumLvl = nSaveNumLvl; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + if(nActNumLvl & nMask) + { + rBox.select(i); + break; + } + nMask <<=1; + } + } + m_xRelativeCB->set_sensitive(1 != nActNumLvl); + SetModified(); + InitPosAndSpaceMode(); + ShowControlsDependingOnPosAndSpaceMode(); + InitControls(); +} + +IMPL_LINK(SwNumPositionTabPage, DistanceHdl, weld::MetricSpinButton&, rField, void) +{ + if(bInInintControl) + return; + long nValue = static_cast< long >(rField.denormalize(rField.get_value(FieldUnit::TWIP))); + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActNumLvl & nMask) + { + SwNumFormat aNumFormat( pActNum->Get( i ) ); + if (&rField == m_xDistBorderMF.get()) + { + + if (m_xRelativeCB->get_active() && m_xRelativeCB->get_sensitive()) + { + if(0 == i) + { + auto const nTmp = aNumFormat.GetFirstLineOffset(); + aNumFormat.SetAbsLSpace( nValue - nTmp ); + } + else + { + long nTmp = pActNum->Get( i - 1 ).GetAbsLSpace() + + pActNum->Get( i - 1 ).GetFirstLineOffset() - + pActNum->Get( i ).GetFirstLineOffset(); + + aNumFormat.SetAbsLSpace( nValue + nTmp ); + } + } + else + { + aNumFormat.SetAbsLSpace( nValue - aNumFormat.GetFirstLineOffset()); + } + } + else if (&rField == m_xDistNumMF.get()) + { + aNumFormat.SetCharTextDistance( nValue ); + } + else if (&rField == m_xIndentMF.get()) + { + // now AbsLSpace also has to be modified by FirstLineOffset + long nDiff = nValue + aNumFormat.GetFirstLineOffset(); + auto const nAbsLSpace = aNumFormat.GetAbsLSpace(); + aNumFormat.SetAbsLSpace( nAbsLSpace + nDiff ); + aNumFormat.SetFirstLineOffset( -nValue ); + } + + pActNum->Set( i, aNumFormat ); + } + nMask <<= 1; + } + + SetModified(); + if(!m_xDistBorderMF->get_sensitive()) + m_xDistBorderMF->set_text(OUString()); +} + +IMPL_LINK( SwNumPositionTabPage, RelativeHdl, weld::ToggleButton&, rBox, void ) +{ + bool bOn = rBox.get_active(); + bool bSingleSelection = m_xLevelLB->n_children() == 1 && USHRT_MAX != nActNumLvl; + bool bSetValue = false; + long nValue = 0; + if(bOn || bSingleSelection) + { + sal_uInt16 nMask = 1; + bool bFirst = true; + bSetValue = true; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActNumLvl & nMask) + { + const SwNumFormat &rNumFormat = pActNum->Get(i); + if(bFirst) + { + nValue = rNumFormat.GetAbsLSpace(); + if(bOn && i) + nValue -= pActNum->Get(i - 1).GetAbsLSpace(); + } + else + bSetValue = nValue == rNumFormat.GetAbsLSpace() - pActNum->Get(i - 1).GetAbsLSpace(); + bFirst = false; + } + nMask <<= 1; + } + + } + if(bSetValue) + m_xDistBorderMF->set_value(m_xDistBorderMF->normalize(nValue), FieldUnit::TWIP); + else + m_xDistBorderMF->set_text(OUString()); + m_xDistBorderMF->set_sensitive(bOn || bSingleSelection || pOutlineDlg); + bLastRelative = bOn; +} + +IMPL_LINK_NOARG(SwNumPositionTabPage, LabelFollowedByHdl_Impl, weld::ComboBox&, void) +{ + // determine value to be set at the chosen list levels + SvxNumberFormat::LabelFollowedBy eLabelFollowedBy = SvxNumberFormat::LISTTAB; + { + const int nPos = m_xLabelFollowedByLB->get_active(); + if ( nPos == 1 ) + { + eLabelFollowedBy = SvxNumberFormat::SPACE; + } + else if ( nPos == 2 ) + { + eLabelFollowedBy = SvxNumberFormat::NOTHING; + } + else if ( nPos == 3 ) + { + eLabelFollowedBy = SvxNumberFormat::NEWLINE; + } + } + + // set value at the chosen list levels + bool bSameListtabPos = true; + sal_uInt16 nFirstLvl = USHRT_MAX; + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if ( nActNumLvl & nMask ) + { + SwNumFormat aNumFormat( pActNum->Get(i) ); + aNumFormat.SetLabelFollowedBy( eLabelFollowedBy ); + pActNum->Set( i, aNumFormat ); + + if ( nFirstLvl == USHRT_MAX ) + { + nFirstLvl = i; + } + else + { + bSameListtabPos &= aNumFormat.GetListtabPos() == + pActNum->Get( nFirstLvl ).GetListtabPos(); + } + } + nMask <<= 1; + } + + // enable/disable metric field for list tab stop position depending on + // selected item following the list label. + m_xListtabFT->set_sensitive( eLabelFollowedBy == SvxNumberFormat::LISTTAB ); + m_xListtabMF->set_sensitive( eLabelFollowedBy == SvxNumberFormat::LISTTAB ); + if ( bSameListtabPos && eLabelFollowedBy == SvxNumberFormat::LISTTAB ) + { + m_xListtabMF->set_value( + m_xListtabMF->normalize( pActNum->Get( nFirstLvl ).GetListtabPos() ), + FieldUnit::TWIP ); + } + else + { + m_xListtabMF->set_text(OUString()); + } + + SetModified(); +} + +IMPL_LINK( SwNumPositionTabPage, ListtabPosHdl_Impl, weld::MetricSpinButton&, rField, void ) +{ + // determine value to be set at the chosen list levels + const long nValue = static_cast< long >(rField.denormalize(rField.get_value(FieldUnit::TWIP))); + + // set value at the chosen list levels + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if ( nActNumLvl & nMask ) + { + SwNumFormat aNumFormat( pActNum->Get(i) ); + aNumFormat.SetListtabPos( nValue ); + pActNum->Set( i, aNumFormat ); + } + nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK( SwNumPositionTabPage, AlignAtHdl_Impl, weld::MetricSpinButton&, rField, void ) +{ + // determine value to be set at the chosen list levels + const long nValue = static_cast< long >(rField.denormalize(rField.get_value(FieldUnit::TWIP))); + + // set value at the chosen list levels + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if ( nActNumLvl & nMask ) + { + SwNumFormat aNumFormat( pActNum->Get(i) ); + const long nFirstLineIndent = nValue - aNumFormat.GetIndentAt(); + aNumFormat.SetFirstLineIndent( nFirstLineIndent ); + pActNum->Set( i, aNumFormat ); + } + nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK( SwNumPositionTabPage, IndentAtHdl_Impl, weld::MetricSpinButton&, rField, void ) +{ + // determine value to be set at the chosen list levels + const long nValue = static_cast< long >(rField.denormalize(rField.get_value(FieldUnit::TWIP))); + + // set value at the chosen list levels + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if ( nActNumLvl & nMask ) + { + SwNumFormat aNumFormat( pActNum->Get(i) ); + const long nAlignedAt = aNumFormat.GetIndentAt() + + aNumFormat.GetFirstLineIndent(); + aNumFormat.SetIndentAt( nValue ); + const long nNewFirstLineIndent = nAlignedAt - nValue; + aNumFormat.SetFirstLineIndent( nNewFirstLineIndent ); + pActNum->Set( i, aNumFormat ); + } + nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK_NOARG(SwNumPositionTabPage, StandardHdl, weld::Button&, void) +{ + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActNumLvl & nMask) + { + SwNumFormat aNumFormat( pActNum->Get( i ) ); + SwNumRule aTmpNumRule( pWrtSh->GetUniqueNumRuleName(), + aNumFormat.GetPositionAndSpaceMode(), + pOutlineDlg ? OUTLINE_RULE : NUM_RULE ); + const SwNumFormat& aTempFormat(aTmpNumRule.Get( i )); + aNumFormat.SetPositionAndSpaceMode( aTempFormat.GetPositionAndSpaceMode() ); + if ( aTempFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + aNumFormat.SetAbsLSpace( aTempFormat.GetAbsLSpace()); + aNumFormat.SetCharTextDistance( aTempFormat.GetCharTextDistance() ); + aNumFormat.SetFirstLineOffset( aTempFormat.GetFirstLineOffset() ); + } + else if ( aTempFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + aNumFormat.SetNumAdjust( aTempFormat.GetNumAdjust() ); + aNumFormat.SetLabelFollowedBy( aTempFormat.GetLabelFollowedBy() ); + aNumFormat.SetListtabPos( aTempFormat.GetListtabPos() ); + aNumFormat.SetFirstLineIndent( aTempFormat.GetFirstLineIndent() ); + aNumFormat.SetIndentAt( aTempFormat.GetIndentAt() ); + } + pActNum->Set( i, aNumFormat ); + } + nMask <<= 1; + } + + InitControls(); + SetModified(); +} + +#ifdef DBG_UTIL +void SwNumPositionTabPage::SetModified() +{ + bModified = true; + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); +} +#endif + +SwSvxNumBulletTabDialog::SwSvxNumBulletTabDialog(weld::Window* pParent, + const SfxItemSet* pSwItemSet, SwWrtShell & rSh) + : SfxTabDialogController(pParent, "modules/swriter/ui/bulletsandnumbering.ui", "BulletsAndNumberingDialog", + pSwItemSet) + , rWrtSh(rSh) + , m_xDummyCombo(m_xBuilder->weld_combo_box("dummycombo")) +{ + weld::Button* pButton = GetUserButton(); + pButton->connect_clicked(LINK(this, SwSvxNumBulletTabDialog, RemoveNumberingHdl)); + pButton->set_sensitive(rWrtSh.GetNumRuleAtCurrCursorPos() != nullptr); + AddTabPage("singlenum", RID_SVXPAGE_PICK_SINGLE_NUM ); + AddTabPage("bullets", RID_SVXPAGE_PICK_BULLET ); + AddTabPage("outlinenum", RID_SVXPAGE_PICK_NUM ); + AddTabPage("graphics", RID_SVXPAGE_PICK_BMP ); + + if (comphelper::LibreOfficeKit::isActive()) + { + RemoveTabPage("customize"); + } + else + { + AddTabPage("customize", RID_SVXPAGE_NUM_OPTIONS ); + } + + AddTabPage("position", RID_SVXPAGE_NUM_POSITION ); +} + +SwSvxNumBulletTabDialog::~SwSvxNumBulletTabDialog() +{ +} + +void SwSvxNumBulletTabDialog::PageCreated(const OString& rPageId, SfxTabPage& rPage) +{ + // set styles' names and metric + OUString sNumCharFormat, sBulletCharFormat; + SwStyleNameMapper::FillUIName( RES_POOLCHR_NUM_LEVEL, sNumCharFormat ); + SwStyleNameMapper::FillUIName( RES_POOLCHR_BULLET_LEVEL, sBulletCharFormat ); + + if (rPageId == "singlenum") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxStringItem(SID_NUM_CHAR_FMT,sNumCharFormat)); + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + rPage.PageCreated(aSet); + } + else if (rPageId == "bullets") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + rPage.PageCreated(aSet); + } + else if (rPageId == "customize") + { + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put (SfxStringItem(SID_NUM_CHAR_FMT,sNumCharFormat)); + aSet.Put (SfxStringItem(SID_BULLET_CHAR_FMT,sBulletCharFormat)); + // collect char styles + m_xDummyCombo->clear(); + m_xDummyCombo->append_text(SwViewShell::GetShellRes()->aStrNone); + SwDocShell* pDocShell = rWrtSh.GetView().GetDocShell(); + ::FillCharStyleListBox(*m_xDummyCombo, pDocShell); + + std::vector<OUString> aList; + aList.reserve(m_xDummyCombo->get_count()); + for (sal_Int32 j = 0; j < m_xDummyCombo->get_count(); j++) + aList.push_back(m_xDummyCombo->get_text(j)); + + aSet.Put( SfxStringListItem( SID_CHAR_FMT_LIST_BOX,&aList ) ) ; + + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast< const SwWebDocShell *>( pDocShell ) != nullptr); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM, static_cast< sal_uInt16 >(eMetric) ) ); + rPage.PageCreated(aSet); + } + else if (rPageId == "position") + { + SwDocShell* pDocShell = rWrtSh.GetView().GetDocShell(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast< const SwWebDocShell *>( pDocShell ) != nullptr); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM, static_cast< sal_uInt16 >(eMetric)) ); + rPage.PageCreated(aSet); + } +} + +short SwSvxNumBulletTabDialog::Ok() +{ + short nRet = SfxTabDialogController::Ok(); + m_xExampleSet->ClearItem(SID_PARAM_NUM_PRESET); + return nRet; +} + +IMPL_LINK_NOARG(SwSvxNumBulletTabDialog, RemoveNumberingHdl, weld::Button&, void) +{ + m_xDialog->response(RET_USER); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/outline.cxx b/sw/source/ui/misc/outline.cxx new file mode 100644 index 000000000..d95731321 --- /dev/null +++ b/sw/source/ui/misc/outline.cxx @@ -0,0 +1,1076 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/settings.hxx> +#include <vcl/virdev.hxx> +#include <sfx2/tabdlg.hxx> +#include <editeng/brushitem.hxx> +#include <unotools/configmgr.hxx> +#include <SwStyleNameMapper.hxx> +#include <num.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <uitool.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <fmtcol.hxx> +#include <outline.hxx> +#include <uinums.hxx> +#include <poolfmt.hxx> +#include <shellres.hxx> +#include <svl/style.hxx> +#include <charfmt.hxx> +#include <docstyle.hxx> +#include <viewopt.hxx> +#include <outline.hrc> +#include <strings.hrc> +#include <paratr.hxx> + +#include <IDocumentOutlineNodes.hxx> + +using namespace ::com::sun::star; + +namespace { + +class SwNumNamesDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::Entry> m_xFormEdit; + std::unique_ptr<weld::TreeView> m_xFormBox; + std::unique_ptr<weld::Button> m_xOKBtn; + + DECL_LINK( ModifyHdl, weld::Entry&, void ); + DECL_LINK( SelectHdl, weld::TreeView&, void ); + DECL_LINK( DoubleClickHdl, weld::TreeView&, bool ); + +public: + explicit SwNumNamesDlg(weld::Window *pParent); + void SetUserNames(const OUString *pList[]); + OUString GetName() const { return m_xFormEdit->get_text(); } + int GetCurEntryPos() const { return m_xFormBox->get_selected_index(); } +}; + +} + +// remember selected entry +IMPL_LINK( SwNumNamesDlg, SelectHdl, weld::TreeView&, rBox, void ) +{ + m_xFormEdit->set_text(rBox.get_selected_text()); + m_xFormEdit->select_region(0, -1); +} + +/** set user defined names + * + * @param pList list of user defined names; unknown positions for the user are 0. + */ +void SwNumNamesDlg::SetUserNames(const OUString *pList[]) +{ + sal_uInt16 nSelect = 0; + for (sal_uInt16 i = 0; i < SwChapterNumRules::nMaxRules; ++i) + { + if(pList[i]) + { + m_xFormBox->remove(i); + m_xFormBox->insert_text(i, *pList[i]); + if (i == nSelect) + nSelect++; + } + } + m_xFormBox->select(nSelect); + SelectHdl(*m_xFormBox); +} + +// unlock OK-Button when text is in Edit +IMPL_LINK( SwNumNamesDlg, ModifyHdl, weld::Entry&, rBox, void ) +{ + m_xOKBtn->set_sensitive(!rBox.get_text().isEmpty()); +} + +// DoubleClickHdl +IMPL_LINK_NOARG(SwNumNamesDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +SwNumNamesDlg::SwNumNamesDlg(weld::Window *pParent) + : GenericDialogController(pParent, + "modules/swriter/ui/numberingnamedialog.ui", + "NumberingNameDialog") + , m_xFormEdit(m_xBuilder->weld_entry("entry")) + , m_xFormBox(m_xBuilder->weld_tree_view("form")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) +{ + for (size_t i = 0; i < SAL_N_ELEMENTS(OUTLINE_STYLE); ++i) + m_xFormBox->append_text(SwResId(OUTLINE_STYLE[i])); + + m_xFormEdit->connect_changed(LINK(this, SwNumNamesDlg, ModifyHdl)); + m_xFormBox->connect_changed(LINK(this, SwNumNamesDlg, SelectHdl)); + m_xFormBox->connect_row_activated(LINK(this, SwNumNamesDlg, DoubleClickHdl)); + m_xFormBox->set_size_request(-1, m_xFormBox->get_height_rows(9)); +} + +static sal_uInt16 lcl_BitToLevel(sal_uInt16 nActLevel) +{ + sal_uInt16 nTmp = nActLevel; + sal_uInt16 nTmpLevel = 0; + while( 0 != (nTmp >>= 1) ) + nTmpLevel++; + return nTmpLevel; +} + +sal_uInt16 SwOutlineTabDialog::nNumLevel = 1; + +SwOutlineTabDialog::SwOutlineTabDialog(weld::Window* pParent, const SfxItemSet* pSwItemSet, + SwWrtShell &rSh) + : SfxTabDialogController(pParent, "modules/swriter/ui/outlinenumbering.ui", "OutlineNumberingDialog", pSwItemSet) + , rWrtSh(rSh) + , pChapterNumRules(SW_MOD()->GetChapterNumRules()) + , bModified(rWrtSh.IsModified()) + , m_xMenuButton(m_xBuilder->weld_menu_button("format")) +{ + m_xMenuButton->connect_toggled(LINK(this, SwOutlineTabDialog, FormHdl)); + m_xMenuButton->connect_selected(LINK(this, SwOutlineTabDialog, MenuSelectHdl)); + + xNumRule.reset(new SwNumRule(*rSh.GetOutlineNumRule())); + GetCancelButton().connect_clicked(LINK(this, SwOutlineTabDialog, CancelHdl)); + + AddTabPage("position", &SwNumPositionTabPage::Create, nullptr); + AddTabPage("numbering", &SwOutlineSettingsTabPage::Create, nullptr); + + OUString sHeadline; + sal_uInt16 i; + + for( i = 0; i < MAXLEVEL; ++i ) + { + // if the style wasn't created yet, it's still at this position + if( !rWrtSh.GetParaStyle( sHeadline = + SwStyleNameMapper::GetUIName( static_cast< sal_uInt16 >(RES_POOLCOLL_HEADLINE1 + i), + sHeadline )) ) + aCollNames[i] = sHeadline; + } + + // query the text templates' outlining levels + const sal_uInt16 nCount = rWrtSh.GetTextFormatCollCount(); + for(i = 0; i < nCount; ++i ) + { + SwTextFormatColl &rTextColl = rWrtSh.GetTextFormatColl(i); + if(!rTextColl.IsDefault()) + { + if(rTextColl.IsAssignedToListLevelOfOutlineStyle()) + { + int nOutLevel = rTextColl.GetAssignedOutlineStyleLevel(); + aCollNames[ nOutLevel ] = rTextColl.GetName(); + } + } + } +} + +SwOutlineTabDialog::~SwOutlineTabDialog() +{ +} + +void SwOutlineTabDialog::PageCreated(const OString& rPageId, SfxTabPage& rPage) +{ + if (rPageId == "position") + { + static_cast<SwNumPositionTabPage&>(rPage).SetWrtShell(&rWrtSh); + static_cast<SwNumPositionTabPage&>(rPage).SetOutlineTabDialog(this); + } + else if (rPageId == "numbering") + { + static_cast<SwOutlineSettingsTabPage&>(rPage).SetWrtShell(&rWrtSh); + } +} + +IMPL_LINK_NOARG(SwOutlineTabDialog, CancelHdl, weld::Button&, void) +{ + if (!bModified) + rWrtSh.ResetModified(); + m_xDialog->response(RET_CANCEL); +} + +IMPL_LINK_NOARG(SwOutlineTabDialog, FormHdl, weld::ToggleButton&, void) +{ + if (!m_xMenuButton->get_active()) + return; + + // fill PopupMenu + for(sal_uInt16 i = 0; i < SwChapterNumRules::nMaxRules; ++i) + { + const SwNumRulesWithName *pRules = pChapterNumRules->GetRules(i); + if (!pRules) + continue; + m_xMenuButton->set_item_label("form" + OString::number(i + 1), pRules->GetName()); + } + + OString sHelpId(m_xMenuButton->get_item_help_id("form1")); + for (sal_Int32 i = 2; i <= 9; ++i) + { + m_xMenuButton->set_item_help_id("form" + OString::number(i), sHelpId); + } +} + +IMPL_LINK(SwOutlineTabDialog, MenuSelectHdl, const OString&, rIdent, void) +{ + sal_uInt8 nLevelNo = 0; + + if (rIdent == "form1") + nLevelNo = 1; + else if (rIdent == "form2") + nLevelNo = 2; + else if (rIdent == "form3") + nLevelNo = 3; + else if (rIdent == "form4") + nLevelNo = 4; + else if (rIdent == "form5") + nLevelNo = 5; + else if (rIdent == "form6") + nLevelNo = 6; + else if (rIdent == "form7") + nLevelNo = 7; + else if (rIdent == "form8") + nLevelNo = 8; + else if (rIdent == "form9") + nLevelNo = 9; + else if (rIdent == "saveas") + { + SwNumNamesDlg aDlg(m_xDialog.get()); + const OUString *aStrArr[SwChapterNumRules::nMaxRules]; + for(sal_uInt16 i = 0; i < SwChapterNumRules::nMaxRules; ++i) + { + const SwNumRulesWithName *pRules = pChapterNumRules->GetRules(i); + if(pRules) + aStrArr[i] = &pRules->GetName(); + else + aStrArr[i] = nullptr; + } + aDlg.SetUserNames(aStrArr); + if (aDlg.run() == RET_OK) + { + const OUString aName(aDlg.GetName()); + pChapterNumRules->ApplyNumRules( SwNumRulesWithName( + *xNumRule, aName ), aDlg.GetCurEntryPos() ); + m_xMenuButton->set_item_label("form" + OString::number(aDlg.GetCurEntryPos() + 1), aName); + } + return; + } + + if( nLevelNo-- ) + { + const SwNumRulesWithName *pRules = pChapterNumRules->GetRules( nLevelNo ); + if( pRules ) + { + xNumRule = pRules->MakeNumRule(rWrtSh); + xNumRule->SetRuleType( OUTLINE_RULE ); + SfxTabPage* pOutlinePage = GetTabPage("numbering"); + assert(pOutlinePage); + static_cast<SwOutlineSettingsTabPage*>(pOutlinePage)->SetNumRule(xNumRule.get()); + } + else + *xNumRule = *rWrtSh.GetOutlineNumRule(); + } + + SfxTabPage* pPage = GetCurTabPage(); + pPage->Reset(GetOutputItemSet()); +} + +sal_uInt16 SwOutlineTabDialog::GetLevel(const OUString &rFormatName) const +{ + for(sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + if(aCollNames[i] == rFormatName) + return i; + } + return MAXLEVEL; +} + +short SwOutlineTabDialog::Ok() +{ + SfxTabDialogController::Ok(); + // set levels for all created templates; has to be done in order to + // delete possibly cancelled assignments again. + + // encapsulate changes into an action to avoid effects on the current cursor + // position during the changes. + rWrtSh.StartAction(); + + const SwNumRule * pOutlineRule = rWrtSh.GetOutlineNumRule(); + + sal_uInt16 i, nCount = rWrtSh.GetTextFormatCollCount(); + for( i = 0; i < nCount; ++i ) + { + SwTextFormatColl &rTextColl = rWrtSh.GetTextFormatColl(i); + if( !rTextColl.IsDefault() ) + { + const SfxPoolItem & rItem = + rTextColl.GetFormatAttr(RES_PARATR_NUMRULE, false); + + if (static_cast<sal_uInt8>(GetLevel(rTextColl.GetName())) == MAXLEVEL) + { + if(rTextColl.IsAssignedToListLevelOfOutlineStyle()) + { + rTextColl.DeleteAssignmentToListLevelOfOutlineStyle(); + } + if (static_cast<const SwNumRuleItem &>(rItem).GetValue() == + pOutlineRule->GetName()) + { + rTextColl.ResetFormatAttr(RES_PARATR_NUMRULE); + } + } + else + { + rTextColl.AssignToListLevelOfOutlineStyle(GetLevel(rTextColl.GetName())); + + if (static_cast<const SwNumRuleItem &>(rItem).GetValue() != + pOutlineRule->GetName()) + { + SwNumRuleItem aItem(pOutlineRule->GetName()); + rTextColl.SetFormatAttr(aItem); + } + } + } + } + + for(i = 0; i < MAXLEVEL; ++i ) + { + OUString sHeadline; + ::SwStyleNameMapper::FillUIName( static_cast< sal_uInt16 >(RES_POOLCOLL_HEADLINE1 + i), + sHeadline ); + SwTextFormatColl* pColl = rWrtSh.FindTextFormatCollByName( sHeadline ); + if( !pColl && aCollNames[i] != sHeadline) + { + SwTextFormatColl* pTextColl = rWrtSh.GetTextCollFromPool( + static_cast< sal_uInt16 >(RES_POOLCOLL_HEADLINE1 + i) ); + pTextColl->DeleteAssignmentToListLevelOfOutlineStyle(); + pTextColl->ResetFormatAttr(RES_PARATR_NUMRULE); + + if( !aCollNames[i].isEmpty() ) + { + pTextColl = rWrtSh.GetParaStyle( + aCollNames[i], SwWrtShell::GETSTYLE_CREATESOME); + if(pTextColl) + { + pTextColl->AssignToListLevelOfOutlineStyle(i); + SwNumRuleItem aItem(pOutlineRule->GetName()); + pTextColl->SetFormatAttr(aItem); + } + } + } + } + + rWrtSh.SetOutlineNumRule(*xNumRule); + + // #i30443# + rWrtSh.EndAction(); + + return RET_OK; +} + +SwOutlineSettingsTabPage::SwOutlineSettingsTabPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/outlinenumberingpage.ui", "OutlineNumberingPage", &rSet) + , aNoFormatName(SwResId(SW_STR_NONE)) + , pSh(nullptr) + , pNumRule(nullptr) + , pCollNames(nullptr) + , nActLevel(1) + , m_xLevelLB(m_xBuilder->weld_tree_view("level")) + , m_xCollBox(m_xBuilder->weld_combo_box("style")) + , m_xNumberBox(new SwNumberingTypeListBox(m_xBuilder->weld_combo_box("numbering"))) + , m_xCharFormatLB(m_xBuilder->weld_combo_box("charstyle")) + , m_xAllLevelFT(m_xBuilder->weld_label("sublevelsft")) + , m_xAllLevelNF(m_xBuilder->weld_spin_button("sublevelsnf")) + , m_xPrefixED(m_xBuilder->weld_entry("prefix")) + , m_xSuffixED(m_xBuilder->weld_entry("suffix")) + , m_xStartEdit(m_xBuilder->weld_spin_button("startat")) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWIN)) +{ + SetExchangeSupport(); + + m_xNumberBox->Reload(SwInsertNumTypes::NoNumbering | SwInsertNumTypes::Extended); + m_xCollBox->make_sorted(); + m_xCollBox->append_text(aNoFormatName); + m_xLevelLB->connect_changed(LINK(this, SwOutlineSettingsTabPage, LevelHdl)); + m_xAllLevelNF->connect_value_changed(LINK(this, SwOutlineSettingsTabPage, ToggleComplete)); + m_xCollBox->connect_changed(LINK(this, SwOutlineSettingsTabPage, CollSelect)); + m_xNumberBox->connect_changed(LINK(this, SwOutlineSettingsTabPage, NumberSelect)); + m_xPrefixED->connect_changed(LINK(this, SwOutlineSettingsTabPage, DelimModify)); + m_xSuffixED->connect_changed(LINK(this, SwOutlineSettingsTabPage, DelimModify)); + m_xStartEdit->connect_value_changed(LINK(this, SwOutlineSettingsTabPage, StartModified)); + m_xCharFormatLB->connect_changed(LINK(this, SwOutlineSettingsTabPage, CharFormatHdl)); +} + +void SwOutlineSettingsTabPage::Update() +{ + // if a template was already selected for this level, select it in the ListBox + m_xCollBox->set_sensitive(USHRT_MAX != nActLevel); + if(USHRT_MAX == nActLevel) + { + bool bSamePrefix = true; + bool bSameSuffix = true; + bool bSameType = true; + bool bSameComplete = true; + bool bSameStart = true; + bool bSameCharFormat = true; + + const SwNumFormat* aNumFormatArr[MAXLEVEL]; + const SwCharFormat* pFirstFormat = nullptr; + + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + + aNumFormatArr[ i ] = &pNumRule->Get(i); + if(i == 0) + pFirstFormat = aNumFormatArr[i]->GetCharFormat(); + else + { + bSameType &= aNumFormatArr[i]->GetNumberingType() == aNumFormatArr[0]->GetNumberingType(); + bSameStart &= aNumFormatArr[i]->GetStart() == aNumFormatArr[0]->GetStart(); + bSamePrefix &= aNumFormatArr[i]->GetPrefix() == aNumFormatArr[0]->GetPrefix(); + bSameSuffix &= aNumFormatArr[i]->GetSuffix() == aNumFormatArr[0]->GetSuffix(); + bSameComplete &= aNumFormatArr[i]->GetIncludeUpperLevels() == aNumFormatArr[0]->GetIncludeUpperLevels(); + const SwCharFormat* pFormat = aNumFormatArr[i]->GetCharFormat(); + bSameCharFormat &= (!pFirstFormat && !pFormat) + || (pFirstFormat && pFormat && pFormat->GetName() == pFirstFormat->GetName()); + } + } + CheckForStartValue_Impl(aNumFormatArr[0]->GetNumberingType()); + if (bSameType) + m_xNumberBox->SelectNumberingType( aNumFormatArr[0]->GetNumberingType() ); + else + m_xNumberBox->SetNoSelection(); + if(bSameStart) + m_xStartEdit->set_value(aNumFormatArr[0]->GetStart()); + else + m_xStartEdit->set_text(OUString()); + if(bSamePrefix) + m_xPrefixED->set_text(aNumFormatArr[0]->GetPrefix()); + else + m_xPrefixED->set_text(OUString()); + if(bSameSuffix) + m_xSuffixED->set_text(aNumFormatArr[0]->GetSuffix()); + else + m_xSuffixED->set_text(OUString()); + + if (bSameCharFormat) + { + if (pFirstFormat) + m_xCharFormatLB->set_active_text(pFirstFormat->GetName()); + else + m_xCharFormatLB->set_active_text(SwViewShell::GetShellRes()->aStrNone); + } + else + m_xCharFormatLB->set_active(-1); + + m_xAllLevelFT->set_sensitive(true); + m_xAllLevelNF->set_sensitive(true); + m_xAllLevelNF->set_max(MAXLEVEL); + if (bSameComplete) + { + m_xAllLevelNF->set_value(aNumFormatArr[0]->GetIncludeUpperLevels()); + } + else + { + m_xAllLevelNF->set_text(OUString()); + } + } + else + { + sal_uInt16 nTmpLevel = lcl_BitToLevel(nActLevel); + OUString aColl(pCollNames[nTmpLevel]); + if(!aColl.isEmpty()) + m_xCollBox->set_active_text(aColl); + else + m_xCollBox->set_active_text(aNoFormatName); + const SwNumFormat &rFormat = pNumRule->Get(nTmpLevel); + + m_xNumberBox->SelectNumberingType( rFormat.GetNumberingType() ); + m_xPrefixED->set_text(rFormat.GetPrefix()); + m_xSuffixED->set_text(rFormat.GetSuffix()); + const SwCharFormat* pFormat = rFormat.GetCharFormat(); + if(pFormat) + m_xCharFormatLB->set_active_text(pFormat->GetName()); + else + m_xCharFormatLB->set_active_text(SwViewShell::GetShellRes()->aStrNone); + + if(nTmpLevel) + { + m_xAllLevelFT->set_sensitive(true); + m_xAllLevelNF->set_sensitive(true); + m_xAllLevelNF->set_max(nTmpLevel + 1); + m_xAllLevelNF->set_value(rFormat.GetIncludeUpperLevels()); + } + else + { + m_xAllLevelNF->set_text(OUString()); + m_xAllLevelNF->set_sensitive(false); + m_xAllLevelFT->set_sensitive(false); + } + CheckForStartValue_Impl(rFormat.GetNumberingType()); + m_xStartEdit->set_value( rFormat.GetStart() ); + } + SetModified(); +} + +IMPL_LINK( SwOutlineSettingsTabPage, LevelHdl, weld::TreeView&, rBox, void ) +{ + nActLevel = 0; + auto aRows = rBox.get_selected_rows(); + if (std::find(aRows.begin(), aRows.end(), MAXLEVEL) != aRows.end()) + { + nActLevel = 0xFFFF; + } + else + { + sal_uInt16 nMask = 1; + for( sal_uInt16 i = 0; i < MAXLEVEL; i++ ) + { + if (std::find(aRows.begin(), aRows.end(), i) != aRows.end()) + nActLevel |= nMask; + nMask <<= 1; + } + } + Update(); +} + +IMPL_LINK(SwOutlineSettingsTabPage, ToggleComplete, weld::SpinButton&, rEdit, void) +{ + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActLevel & nMask) + { + SwNumFormat aNumFormat(pNumRule->Get(i)); + aNumFormat.SetIncludeUpperLevels( std::min( static_cast<sal_uInt8>(rEdit.get_value()), + static_cast<sal_uInt8>(i + 1)) ); + pNumRule->Set(i, aNumFormat); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK( SwOutlineSettingsTabPage, CollSelect, weld::ComboBox&, rBox, void ) +{ + sal_uInt8 i; + + const OUString aCollName(rBox.get_active_text()); + //0xFFFF not allowed here (disable) + sal_uInt16 nTmpLevel = lcl_BitToLevel(nActLevel); + OUString sOldName( pCollNames[nTmpLevel] ); + + for( i = 0; i < MAXLEVEL; ++i) + pCollNames[i] = aSaveCollNames[i]; + + if(aCollName == aNoFormatName) + pCollNames[nTmpLevel].clear(); + else + { + pCollNames[nTmpLevel] = aCollName; + // template already in use? + for( i = 0; i < MAXLEVEL; ++i) + if(i != nTmpLevel && pCollNames[i] == aCollName ) + pCollNames[i].clear(); + } + + // search the oldname and put it into the current entries + if( !sOldName.isEmpty() ) + for( i = 0; i < MAXLEVEL; ++i) + if( aSaveCollNames[ i ] == sOldName && i != nTmpLevel && + pCollNames[ i ].isEmpty() ) + { + sal_uInt8 n; + for( n = 0; n < MAXLEVEL; ++n ) + if( pCollNames[ n ] == sOldName ) + break; + + if( MAXLEVEL == n ) + // it was an outline level name and the current entries is zero. + pCollNames[ i ] = sOldName; + } + + SetModified(); + CollSave(); +} + +void SwOutlineSettingsTabPage::CollSave() +{ + for (sal_uInt8 i = 0; i < MAXLEVEL; ++i) + aSaveCollNames[i] = pCollNames[i]; +} + +IMPL_LINK_NOARG(SwOutlineSettingsTabPage, NumberSelect, weld::ComboBox&, void) +{ + sal_uInt16 nMask = 1; + SvxNumType nNumberType = m_xNumberBox->GetSelectedNumberingType(); + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActLevel & nMask) + { + SwNumFormat aNumFormat(pNumRule->Get(i)); + aNumFormat.SetNumberingType(nNumberType); + pNumRule->Set(i, aNumFormat); + CheckForStartValue_Impl(nNumberType); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK_NOARG(SwOutlineSettingsTabPage, DelimModify, weld::Entry&, void) +{ + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActLevel & nMask) + { + SwNumFormat aNumFormat(pNumRule->Get(i)); + aNumFormat.SetPrefix( m_xPrefixED->get_text() ); + aNumFormat.SetSuffix( m_xSuffixED->get_text() ); + pNumRule->Set(i, aNumFormat); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK( SwOutlineSettingsTabPage, StartModified, weld::SpinButton&, rEdit, void ) +{ + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActLevel & nMask) + { + SwNumFormat aNumFormat(pNumRule->Get(i)); + aNumFormat.SetStart(static_cast<sal_uInt16>(rEdit.get_value())); + pNumRule->Set(i, aNumFormat); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK_NOARG(SwOutlineSettingsTabPage, CharFormatHdl, weld::ComboBox&, void) +{ + OUString sEntry = m_xCharFormatLB->get_active_text(); + sal_uInt16 nMask = 1; + bool bFormatNone = sEntry == SwViewShell::GetShellRes()->aStrNone; + SwCharFormat* pFormat = nullptr; + if(!bFormatNone) + { + sal_uInt16 nChCount = pSh->GetCharFormatCount(); + for(sal_uInt16 i = 0; i < nChCount; i++) + { + SwCharFormat& rChFormat = pSh->GetCharFormat(i); + if(rChFormat.GetName() == sEntry) + { + pFormat = &rChFormat; + break; + } + } + if(!pFormat) + { + SfxStyleSheetBasePool* pPool = pSh->GetView().GetDocShell()->GetStyleSheetPool(); + SfxStyleSheetBase* pBase; + pBase = pPool->Find(sEntry, SfxStyleFamily::Char); + if(!pBase) + pBase = &pPool->Make(sEntry, SfxStyleFamily::Page); + pFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat(); + + } + } + + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + { + if(nActLevel & nMask) + { + SwNumFormat aNumFormat(pNumRule->Get(i)); + if(bFormatNone) + aNumFormat.SetCharFormat(nullptr); + else + aNumFormat.SetCharFormat(pFormat); + pNumRule->Set(i, aNumFormat); + } + nMask <<= 1; + } +} + +SwOutlineSettingsTabPage::~SwOutlineSettingsTabPage() +{ +} + +void SwOutlineSettingsTabPage::SetWrtShell(SwWrtShell* pShell) +{ + pSh = pShell; + // query this document's NumRules + pNumRule = static_cast<SwOutlineTabDialog*>(GetDialogController())->GetNumRule(); + pCollNames = static_cast<SwOutlineTabDialog*>(GetDialogController())->GetCollNames(); + + CollSave(); + + m_aPreviewWIN.SetNumRule(pNumRule); + m_aPreviewWIN.SetOutlineNames(pCollNames); + // set start value - nActLevel must be 1 here + sal_uInt16 nTmpLevel = lcl_BitToLevel(nActLevel); + const SwNumFormat& rNumFormat = pNumRule->Get( nTmpLevel ); + m_xStartEdit->set_value( rNumFormat.GetStart() ); + + // create pool formats for headlines + for (sal_uInt16 i = 0; i < MAXLEVEL; ++i) + { + m_xCollBox->append_text( SwStyleNameMapper::GetUIName( + static_cast< sal_uInt16 >(RES_POOLCOLL_HEADLINE1 + i), OUString())); + m_xLevelLB->append_text( OUString::number(i + 1) ); + } + OUString sStr = "1 - " + OUString::number(MAXLEVEL); + m_xLevelLB->append_text(sStr); + + // query the texttemplates' outlining levels + const sal_uInt16 nCount = pSh->GetTextFormatCollCount(); + for (sal_uInt16 i = 0; i < nCount; ++i) + { + SwTextFormatColl &rTextColl = pSh->GetTextFormatColl(i); + if(!rTextColl.IsDefault()) + { + sStr = rTextColl.GetName(); + if (m_xCollBox->find_text(sStr) == -1) + m_xCollBox->append_text(sStr); + } + } + + m_xNumberBox->SelectNumberingType(rNumFormat.GetNumberingType()); + SwOutlineNodes::size_type nOutlinePos = pSh->GetOutlinePos(MAXLEVEL); + int nTmp = 0; + if(nOutlinePos != SwOutlineNodes::npos) + { + nTmp = static_cast<sal_uInt16>(pSh->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos)); + } + m_xLevelLB->select(nTmp-1); + + // collect char styles + m_xCharFormatLB->clear(); + m_xCharFormatLB->append_text(SwViewShell::GetShellRes()->aStrNone); + + // char styles + ::FillCharStyleListBox(*m_xCharFormatLB, + pSh->GetView().GetDocShell()); + Update(); +} + +void SwOutlineSettingsTabPage::ActivatePage(const SfxItemSet& ) +{ + nActLevel = SwOutlineTabDialog::GetActNumLevel(); + if(nActLevel != USHRT_MAX) + m_xLevelLB->select(lcl_BitToLevel(nActLevel)); + else + m_xLevelLB->select(MAXLEVEL); + LevelHdl(*m_xLevelLB); +} + +DeactivateRC SwOutlineSettingsTabPage::DeactivatePage(SfxItemSet*) +{ + SwOutlineTabDialog::SetActNumLevel(nActLevel); + return DeactivateRC::LeavePage; +} + +bool SwOutlineSettingsTabPage::FillItemSet( SfxItemSet* ) +{ + return true; +} + +void SwOutlineSettingsTabPage::Reset( const SfxItemSet* rSet ) +{ + ActivatePage(*rSet); +} + +std::unique_ptr<SfxTabPage> SwOutlineSettingsTabPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwOutlineSettingsTabPage>(pPage, pController, *rAttrSet); +} + +void SwOutlineSettingsTabPage::CheckForStartValue_Impl(sal_uInt16 nNumberingType) +{ + bool bIsNull = m_xStartEdit->get_value() == 0; + bool bNoZeroAllowed = nNumberingType < SVX_NUM_ARABIC || + SVX_NUM_CHARS_UPPER_LETTER_N == nNumberingType || + SVX_NUM_CHARS_LOWER_LETTER_N == nNumberingType; + m_xStartEdit->set_min(bNoZeroAllowed ? 1 : 0); + if (bIsNull && bNoZeroAllowed) + StartModified(*m_xStartEdit); +} + +static long lcl_DrawBullet(vcl::RenderContext* pVDev, const SwNumFormat& rFormat, long nXStart, long nYStart, const Size& rSize) +{ + vcl::Font aTmpFont(pVDev->GetFont()); + + // via Uno it's possible that no font has been set! + vcl::Font aFont(rFormat.GetBulletFont() ? *rFormat.GetBulletFont() : aTmpFont); + Size aTmpSize(rSize); + aTmpSize.setWidth( aTmpSize.Width() * ( rFormat.GetBulletRelSize()) ); + aTmpSize.setWidth( aTmpSize.Width() / 100 ) ; + aTmpSize.setHeight( aTmpSize.Height() * ( rFormat.GetBulletRelSize()) ); + aTmpSize.setHeight( aTmpSize.Height() / 100 ) ; + // in case of a height of zero it is drawn in original height + if(!aTmpSize.Height()) + aTmpSize.setHeight( 1 ); + aFont.SetFontSize(aTmpSize); + aFont.SetTransparent(true); + Color aBulletColor = rFormat.GetBulletColor(); + if(aBulletColor == COL_AUTO) + aBulletColor = pVDev->GetFillColor().IsDark() ? COL_WHITE : COL_BLACK; + else if(aBulletColor == pVDev->GetFillColor()) + aBulletColor.Invert(); + aFont.SetColor(aBulletColor); + pVDev->SetFont( aFont ); + OUString aText(rFormat.GetBulletChar()); + long nY = nYStart; + nY -= ((aTmpSize.Height() - rSize.Height())/ 2); + pVDev->DrawText( Point(nXStart, nY), aText ); + long nRet = pVDev->GetTextWidth(aText); + + pVDev->SetFont(aTmpFont); + return nRet; +} + +static long lcl_DrawGraphic(vcl::RenderContext* pVDev, const SwNumFormat &rFormat, long nXStart, long nYStart, long nDivision) +{ + const SvxBrushItem* pBrushItem = rFormat.GetBrush(); + long nRet = 0; + if (pBrushItem) + { + const Graphic* pGraphic = pBrushItem->GetGraphic(); + if (pGraphic) + { + Size aGSize( rFormat.GetGraphicSize()); + aGSize.setWidth( aGSize.Width() / nDivision ); + nRet = aGSize.Width(); + aGSize.setHeight( aGSize.Height() / nDivision ); + pGraphic->Draw(pVDev, Point(nXStart, nYStart), pVDev->PixelToLogic(aGSize)); + } + } + return nRet; +} + +void NumberingPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + const Size aSize(rRenderContext.PixelToLogic(GetOutputSizePixel())); + + ScopedVclPtrInstance<VirtualDevice> pVDev(rRenderContext); + pVDev->SetMapMode(rRenderContext.GetMapMode()); + pVDev->SetOutputSize(aSize); + + // #101524# OJ + pVDev->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + pVDev->SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor()); + pVDev->DrawRect(tools::Rectangle(Point(0,0), aSize)); + + if (pActNum) + { + long nWidthRelation = 30; // chapter dialog + if(nPageWidth) + { + nWidthRelation = nPageWidth / aSize.Width(); + if(bPosition) + nWidthRelation = nWidthRelation * 2 / 3; + else + nWidthRelation = nWidthRelation / 4; + } + + // height per level + const long nXStep = aSize.Width() / (3 * MAXLEVEL * ((MAXLEVEL < 10) ? 2 : 1)); + const long nYStep = (aSize.Height() - 6)/ MAXLEVEL; + long nYStart = 4; + aStdFont = OutputDevice::GetDefaultFont(DefaultFontType::UI_SANS, GetAppLanguage(), + GetDefaultFontFlags::OnlyOne, &rRenderContext); + // #101524# OJ + aStdFont.SetColor(SwViewOption::GetFontColor()); + + const long nFontHeight = nYStep * ( bPosition ? 15 : 6 ) / 10; + aStdFont.SetFontSize(Size( 0, nFontHeight )); + + long nPreNum = pActNum->Get(0).GetStart(); + + if (bPosition) + { + const long nLineHeight = nFontHeight * 8 / 7; + sal_uInt8 nStart = 0; + while (!(nActLevel & (1 << nStart))) + { + nStart++; + } + if(nStart) // so that possible predecessors and successors are showed + nStart--; + + SwNumberTree::tNumberVector aNumVector; + sal_uInt8 nEnd = std::min(sal_uInt8(nStart + 3), MAXLEVEL); + for (sal_uInt8 nLevel = nStart; nLevel < nEnd; ++nLevel) + { + const SwNumFormat &rFormat = pActNum->Get(nLevel); + aNumVector.push_back(rFormat.GetStart()); + + long nXStart( 0 ); + long nTextOffset( 0 ); + long nNumberXPos( 0 ); + if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION) + { + nXStart = rFormat.GetAbsLSpace() / nWidthRelation; + nTextOffset = rFormat.GetCharTextDistance() / nWidthRelation; + nNumberXPos = nXStart; + const long nFirstLineOffset = (-rFormat.GetFirstLineOffset()) / nWidthRelation; + + if(nFirstLineOffset <= nNumberXPos) + nNumberXPos -= nFirstLineOffset; + else + nNumberXPos = 0; + } + else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + const long nTmpNumberXPos((rFormat.GetIndentAt() + rFormat.GetFirstLineIndent()) / nWidthRelation); + nNumberXPos = (nTmpNumberXPos < 0) ? 0 : nTmpNumberXPos; + } + + long nBulletWidth = 0; + if (SVX_NUM_BITMAP == rFormat.GetNumberingType()) + { + nBulletWidth = lcl_DrawGraphic(pVDev.get(), rFormat, nNumberXPos, + nYStart, nWidthRelation); + } + else if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType()) + { + nBulletWidth = lcl_DrawBullet(pVDev.get(), rFormat, nNumberXPos, + nYStart, aStdFont.GetFontSize()); + } + else + { + pVDev->SetFont(aStdFont); + if(pActNum->IsContinusNum()) + aNumVector[nLevel] = nPreNum; + OUString aText(pActNum->MakeNumString( aNumVector )); + pVDev->DrawText( Point(nNumberXPos, nYStart), aText ); + nBulletWidth = pVDev->GetTextWidth(aText); + nPreNum++; + } + if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT && + rFormat.GetLabelFollowedBy() == SvxNumberFormat::SPACE ) + { + pVDev->SetFont(aStdFont); + OUString aText(' '); + pVDev->DrawText( Point(nNumberXPos, nYStart), aText ); + nBulletWidth += pVDev->GetTextWidth(aText); + } + + long nTextXPos(0); + if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION) + { + nTextXPos = nXStart; + if (nTextOffset < 0) + nTextXPos = nTextXPos + nTextOffset; + if (nNumberXPos + nBulletWidth + nTextOffset > nTextXPos) + nTextXPos = nNumberXPos + nBulletWidth + nTextOffset; + } + else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + switch (rFormat.GetLabelFollowedBy()) + { + case SvxNumberFormat::LISTTAB: + { + nTextXPos = rFormat.GetListtabPos() / nWidthRelation; + if (nTextXPos < nNumberXPos + nBulletWidth) + { + nTextXPos = nNumberXPos + nBulletWidth; + } + } + break; + case SvxNumberFormat::SPACE: + case SvxNumberFormat::NOTHING: + case SvxNumberFormat::NEWLINE: + { + nTextXPos = nNumberXPos + nBulletWidth; + } + break; + } + + nXStart = rFormat.GetIndentAt() / nWidthRelation; + } + + tools::Rectangle aRect1(Point(nTextXPos, nYStart + nFontHeight / 2), Size(aSize.Width() / 2, 2)); + pVDev->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); // COL_BLACK ); + pVDev->DrawRect(aRect1); + + tools::Rectangle aRect2(Point(nXStart, nYStart + nLineHeight + nFontHeight / 2), Size(aSize.Width() / 2, 2)); + pVDev->DrawRect(aRect2); + nYStart += 2 * nLineHeight; + } + } + else + { + SwNumberTree::tNumberVector aNumVector; + const long nLineHeight = nFontHeight * 3 / 2; + for (sal_uInt8 nLevel = 0; nLevel < MAXLEVEL; ++nLevel, nYStart = nYStart + nYStep) + { + const SwNumFormat &rFormat = pActNum->Get(nLevel); + aNumVector.push_back(rFormat.GetStart()); + long nXStart(0); + if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION) + { + nXStart = rFormat.GetAbsLSpace() / nWidthRelation; + } + else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT) + { + const long nTmpXStart((rFormat.GetIndentAt() + rFormat.GetFirstLineIndent() ) / nWidthRelation); + nXStart = (nTmpXStart < 0) ? 0 : nTmpXStart; + } + nXStart /= 2; + nXStart += 2; + long nTextOffset; + if (SVX_NUM_BITMAP == rFormat.GetNumberingType()) + { + lcl_DrawGraphic(pVDev.get(), rFormat, nXStart, nYStart, nWidthRelation); + nTextOffset = nLineHeight + nXStep; + } + else if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType()) + { + nTextOffset = lcl_DrawBullet(pVDev.get(), rFormat, nXStart, nYStart, aStdFont.GetFontSize()); + nTextOffset += nXStep; + } + else + { + pVDev->SetFont(aStdFont); + if (pActNum->IsContinusNum()) + aNumVector[nLevel] = nPreNum; + OUString aText(pActNum->MakeNumString( aNumVector )); + pVDev->DrawText( Point(nXStart, nYStart), aText ); + nTextOffset = pVDev->GetTextWidth(aText) + nXStep; + nPreNum++; + } + pVDev->SetFont(aStdFont); + pVDev->DrawText( + Point(nXStart + nTextOffset, nYStart), + (pOutlineNames == nullptr + ? utl::ConfigManager::getProductName() + : pOutlineNames[nLevel])); + } + } + } + rRenderContext.DrawOutDev(Point(0,0), aSize, Point(0,0), aSize, *pVDev); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/pgfnote.cxx b/sw/source/ui/misc/pgfnote.cxx new file mode 100644 index 000000000..a90b15e2a --- /dev/null +++ b/sw/source/ui/misc/pgfnote.cxx @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <fmtfsize.hxx> +#include <hintids.hxx> +#include <svtools/unitconv.hxx> +#include <vcl/fieldvalues.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <editeng/borderline.hxx> +#include <editeng/sizeitem.hxx> +#include <svx/pageitem.hxx> +#include <svl/eitem.hxx> +#include <editeng/ulspitem.hxx> +#include <uitool.hxx> +#include <pagedesc.hxx> +#include <pgfnote.hxx> +#include <uiitems.hxx> + +#include <memory> + +using namespace ::com::sun::star; + +const sal_uInt16 SwFootNotePage::aPageRg[] = { + FN_PARAM_FTN_INFO, FN_PARAM_FTN_INFO, + 0 +}; + +// handler to switch between the different possibilities how the footnote +// region's height can be set. +IMPL_LINK_NOARG(SwFootNotePage, HeightPage, weld::ToggleButton&, void) +{ + if (m_xMaxHeightPageBtn->get_active()) + m_xMaxHeightEdit->set_sensitive(false); +} + +IMPL_LINK_NOARG(SwFootNotePage, HeightMetric, weld::ToggleButton&, void) +{ + if (m_xMaxHeightBtn->get_active()) + { + m_xMaxHeightEdit->set_sensitive(true); + m_xMaxHeightEdit->grab_focus(); + } +} + +// handler limit values +IMPL_LINK_NOARG(SwFootNotePage, HeightModify, weld::MetricSpinButton&, void) +{ + m_xMaxHeightEdit->set_max(m_xMaxHeightEdit->normalize(lMaxHeight - + (m_xDistEdit->denormalize(m_xDistEdit->get_value(FieldUnit::TWIP)) + + m_xLineDistEdit->denormalize(m_xLineDistEdit->get_value(FieldUnit::TWIP)))), + FieldUnit::TWIP); + if (m_xMaxHeightEdit->get_value(FieldUnit::NONE) < 0) + m_xMaxHeightEdit->set_value(0, FieldUnit::NONE); + m_xDistEdit->set_max(m_xDistEdit->normalize(lMaxHeight - + (m_xMaxHeightEdit->denormalize(m_xMaxHeightEdit->get_value(FieldUnit::TWIP)) + + m_xLineDistEdit->denormalize(m_xLineDistEdit->get_value(FieldUnit::TWIP)))), + FieldUnit::TWIP); + if (m_xDistEdit->get_value(FieldUnit::NONE) < 0) + m_xDistEdit->set_value(0, FieldUnit::NONE); + m_xLineDistEdit->set_max(m_xLineDistEdit->normalize(lMaxHeight - + (m_xMaxHeightEdit->denormalize(m_xMaxHeightEdit->get_value(FieldUnit::TWIP)) + + m_xDistEdit->denormalize(m_xDistEdit->get_value(FieldUnit::TWIP)))), + FieldUnit::TWIP); +} + +IMPL_LINK_NOARG(SwFootNotePage, LineWidthChanged_Impl, weld::MetricSpinButton&, void) +{ + sal_Int64 nVal = m_xLineWidthEdit->get_value(FieldUnit::NONE); + nVal = static_cast<sal_Int64>(vcl::ConvertDoubleValue( + nVal, + m_xLineWidthEdit->get_digits(), + m_xLineWidthEdit->get_unit(), MapUnit::MapTwip )); + m_xLineTypeBox->SetWidth(nVal); +} + +IMPL_LINK(SwFootNotePage, LineColorSelected_Impl, ColorListBox&, rColorBox, void) +{ + m_xLineTypeBox->SetColor(rColorBox.GetSelectEntryColor()); +} + +SwFootNotePage::SwFootNotePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/footnoteareapage.ui", "FootnoteAreaPage", &rSet) + , lMaxHeight(0) + , m_xMaxHeightPageBtn(m_xBuilder->weld_radio_button("maxheightpage")) + , m_xMaxHeightBtn(m_xBuilder->weld_radio_button("maxheight")) + , m_xMaxHeightEdit(m_xBuilder->weld_metric_spin_button("maxheightsb", FieldUnit::CM)) + , m_xDistEdit(m_xBuilder->weld_metric_spin_button("spacetotext", FieldUnit::CM)) + , m_xLinePosBox(m_xBuilder->weld_combo_box("position")) + , m_xLineTypeBox(new SvtLineListBox(m_xBuilder->weld_menu_button("style"))) + , m_xLineWidthEdit(m_xBuilder->weld_metric_spin_button("thickness", FieldUnit::POINT)) + , m_xLineColorBox(new ColorListBox(m_xBuilder->weld_menu_button("color"), pController->getDialog())) + , m_xLineLengthEdit(m_xBuilder->weld_metric_spin_button("length", FieldUnit::PERCENT)) + , m_xLineDistEdit(m_xBuilder->weld_metric_spin_button("spacingtocontents", FieldUnit::CM)) +{ + SetExchangeSupport(); + FieldUnit aMetric = ::GetDfltMetric(false); + ::SetFieldUnit(*m_xMaxHeightEdit, aMetric); + ::SetFieldUnit(*m_xDistEdit, aMetric); + ::SetFieldUnit(*m_xLineDistEdit, aMetric); + MeasurementSystem eSys = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum(); + long nHeightValue = MeasurementSystem::Metric != eSys ? 1440 : 1134; + m_xMaxHeightEdit->set_value(m_xMaxHeightEdit->normalize(nHeightValue),FieldUnit::TWIP); +} + +SwFootNotePage::~SwFootNotePage() +{ + m_xLineColorBox.reset(); + m_xLineTypeBox.reset(); +} + +std::unique_ptr<SfxTabPage> SwFootNotePage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwFootNotePage>(pPage, pController, *rSet); +} + +void SwFootNotePage::Reset(const SfxItemSet *rSet) +{ + // if no example exists, otherwise Init here in Activate + std::unique_ptr<SwPageFootnoteInfo> pDefFootnoteInfo; + const SwPageFootnoteInfo* pFootnoteInfo; + const SfxPoolItem* pItem = SfxTabPage::GetItem(*rSet, FN_PARAM_FTN_INFO); + if( pItem ) + { + pFootnoteInfo = &static_cast<const SwPageFootnoteInfoItem*>(pItem)->GetPageFootnoteInfo(); + } + else + { + // when "standard" is being activated the footnote item is deleted, + // that's why a footnote structure has to be created here + pDefFootnoteInfo.reset(new SwPageFootnoteInfo()); + pFootnoteInfo = pDefFootnoteInfo.get(); + } + // footnote area's height + SwTwips lHeight = pFootnoteInfo->GetHeight(); + if(lHeight) + { + m_xMaxHeightEdit->set_value(m_xMaxHeightEdit->normalize(lHeight),FieldUnit::TWIP); + m_xMaxHeightBtn->set_active(true); + } + else + { + m_xMaxHeightPageBtn->set_active(true); + m_xMaxHeightEdit->set_sensitive(false); + } + m_xMaxHeightPageBtn->connect_toggled(LINK(this,SwFootNotePage,HeightPage)); + m_xMaxHeightBtn->connect_toggled(LINK(this,SwFootNotePage,HeightMetric)); + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwFootNotePage, HeightModify); + m_xMaxHeightEdit->connect_value_changed(aLk); + m_xDistEdit->connect_value_changed(aLk); + m_xLineDistEdit->connect_value_changed(aLk); + + // Separator width + m_xLineWidthEdit->connect_value_changed(LINK(this, SwFootNotePage, LineWidthChanged_Impl)); + + sal_Int64 nWidthPt = static_cast<sal_Int64>(vcl::ConvertDoubleValue( + sal_Int64( pFootnoteInfo->GetLineWidth() ), m_xLineWidthEdit->get_digits(), + MapUnit::MapTwip, m_xLineWidthEdit->get_unit( ) )); + m_xLineWidthEdit->set_value(nWidthPt, FieldUnit::NONE); + + // Separator style + m_xLineTypeBox->SetSourceUnit( FieldUnit::TWIP ); + + m_xLineTypeBox->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::SOLID), + SvxBorderLineStyle::SOLID ); + m_xLineTypeBox->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DOTTED), + SvxBorderLineStyle::DOTTED ); + m_xLineTypeBox->InsertEntry( + ::editeng::SvxBorderLine::getWidthImpl(SvxBorderLineStyle::DASHED), + SvxBorderLineStyle::DASHED ); + m_xLineTypeBox->SetWidth( pFootnoteInfo->GetLineWidth( ) ); + m_xLineTypeBox->SelectEntry( pFootnoteInfo->GetLineStyle() ); + + // Separator Color + m_xLineColorBox->SelectEntry(pFootnoteInfo->GetLineColor()); + m_xLineColorBox->SetSelectHdl(LINK(this, SwFootNotePage, LineColorSelected_Impl)); + m_xLineTypeBox->SetColor(pFootnoteInfo->GetLineColor()); + + // position + m_xLinePosBox->set_active(static_cast<sal_Int32>(pFootnoteInfo->GetAdj())); + + // width + Fraction aTmp( 100, 1 ); + aTmp *= pFootnoteInfo->GetWidth(); + m_xLineLengthEdit->set_value(static_cast<long>(aTmp), FieldUnit::PERCENT); + + // gap footnote area + m_xDistEdit->set_value(m_xDistEdit->normalize(pFootnoteInfo->GetTopDist()), FieldUnit::TWIP); + m_xLineDistEdit->set_value( + m_xLineDistEdit->normalize(pFootnoteInfo->GetBottomDist()), FieldUnit::TWIP); + ActivatePage( *rSet ); +} + +// stuff attributes into the set, when OK +bool SwFootNotePage::FillItemSet(SfxItemSet *rSet) +{ + SwPageFootnoteInfoItem aItem(static_cast<const SwPageFootnoteInfoItem&>(GetItemSet().Get(FN_PARAM_FTN_INFO))); + + // that's the original + SwPageFootnoteInfo &rFootnoteInfo = aItem.GetPageFootnoteInfo(); + + // footnote area's height + if (m_xMaxHeightBtn->get_active()) + rFootnoteInfo.SetHeight( static_cast< SwTwips >( + m_xMaxHeightEdit->denormalize(m_xMaxHeightEdit->get_value(FieldUnit::TWIP)))); + else + rFootnoteInfo.SetHeight(0); + + // gap footnote area + rFootnoteInfo.SetTopDist( static_cast< SwTwips >( + m_xDistEdit->denormalize(m_xDistEdit->get_value(FieldUnit::TWIP)))); + rFootnoteInfo.SetBottomDist( static_cast< SwTwips >( + m_xLineDistEdit->denormalize(m_xLineDistEdit->get_value(FieldUnit::TWIP)))); + + // Separator style + rFootnoteInfo.SetLineStyle(m_xLineTypeBox->GetSelectEntryStyle()); + + // Separator width + sal_Int64 nWidth = m_xLineWidthEdit->get_value(FieldUnit::NONE); + nWidth = static_cast<long>(vcl::ConvertDoubleValue( + nWidth, + m_xLineWidthEdit->get_digits(), + m_xLineWidthEdit->get_unit(), MapUnit::MapTwip )); + rFootnoteInfo.SetLineWidth( nWidth ); + + // Separator color + rFootnoteInfo.SetLineColor(m_xLineColorBox->GetSelectEntryColor()); + + // Position + rFootnoteInfo.SetAdj(static_cast<css::text::HorizontalAdjust>(m_xLinePosBox->get_active())); + + // Width + rFootnoteInfo.SetWidth(Fraction(m_xLineLengthEdit->get_value(FieldUnit::PERCENT), 100)); + + const SfxPoolItem* pOldItem; + if(nullptr == (pOldItem = GetOldItem( *rSet, FN_PARAM_FTN_INFO )) || + aItem != *pOldItem ) + rSet->Put(aItem); + + return true; +} + +void SwFootNotePage::ActivatePage(const SfxItemSet& rSet) +{ + auto const & rSize = rSet.Get( RES_FRM_SIZE ); + lMaxHeight = rSize.GetHeight(); + + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( rSet.GetPool()->GetWhich( SID_ATTR_PAGE_HEADERSET), false, &pItem ) ) + { + const SfxItemSet& rHeaderSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rHeaderOn = + static_cast<const SfxBoolItem&>(rHeaderSet.Get( rSet.GetPool()->GetWhich( SID_ATTR_PAGE_ON ) )); + + if ( rHeaderOn.GetValue() ) + { + const SvxSizeItem& rSizeItem = + static_cast<const SvxSizeItem&>(rHeaderSet.Get(rSet.GetPool()->GetWhich(SID_ATTR_PAGE_SIZE))); + lMaxHeight -= rSizeItem.GetSize().Height(); + } + } + + if( SfxItemState::SET == rSet.GetItemState( rSet.GetPool()->GetWhich( SID_ATTR_PAGE_FOOTERSET), + false, &pItem ) ) + { + const SfxItemSet& rFooterSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rFooterOn = rFooterSet.Get( SID_ATTR_PAGE_ON ); + + if ( rFooterOn.GetValue() ) + { + const SvxSizeItem& rSizeItem = + static_cast<const SvxSizeItem&>(rFooterSet.Get( rSet.GetPool()->GetWhich( SID_ATTR_PAGE_SIZE ) )); + lMaxHeight -= rSizeItem.GetSize().Height(); + } + } + + if ( rSet.GetItemState( RES_UL_SPACE , false ) == SfxItemState::SET ) + { + const SvxULSpaceItem &rUL = rSet.Get( RES_UL_SPACE ); + lMaxHeight -= rUL.GetUpper() + rUL.GetLower(); + } + + lMaxHeight *= 8; + lMaxHeight /= 10; + + // set maximum values + HeightModify(*m_xMaxHeightEdit); +} + +DeactivateRC SwFootNotePage::DeactivatePage( SfxItemSet* _pSet) +{ + if(_pSet) + FillItemSet(_pSet); + + return DeactivateRC::LeavePage; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/pggrid.cxx b/sw/source/ui/misc/pggrid.cxx new file mode 100644 index 000000000..fcc9dc343 --- /dev/null +++ b/sw/source/ui/misc/pggrid.cxx @@ -0,0 +1,490 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <swtypes.hxx> +#include <svx/colorbox.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/ruler.hxx> +#include <pggrid.hxx> +#include <tgrditem.hxx> + +#include <wrtsh.hxx> +#include <doc.hxx> +#include <swmodule.hxx> +#include <view.hxx> + +SwTextGridPage::SwTextGridPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/textgridpage.ui", "TextGridPage", &rSet) + , m_nRubyUserValue(0) + , m_bRubyUserValue(false) + , m_aPageSize(MM50, MM50) + , m_bVertical(false) + , m_bSquaredMode(false) + , m_bHRulerChanged(false) + , m_bVRulerChanged(false) + , m_xNoGridRB(m_xBuilder->weld_radio_button("radioRB_NOGRID")) + , m_xLinesGridRB(m_xBuilder->weld_radio_button("radioRB_LINESGRID")) + , m_xCharsGridRB(m_xBuilder->weld_radio_button("radioRB_CHARSGRID")) + , m_xSnapToCharsCB(m_xBuilder->weld_check_button("checkCB_SNAPTOCHARS")) + , m_xExampleWN(new weld::CustomWeld(*m_xBuilder, "drawingareaWN_EXAMPLE", m_aExampleWN)) + , m_xLayoutFL(m_xBuilder->weld_widget("frameFL_LAYOUT")) + , m_xLinesPerPageNF(m_xBuilder->weld_spin_button("spinNF_LINESPERPAGE")) + , m_xLinesRangeFT(m_xBuilder->weld_label("labelFT_LINERANGE")) + , m_xTextSizeMF(m_xBuilder->weld_metric_spin_button("spinMF_TEXTSIZE", FieldUnit::POINT)) + , m_xCharsPerLineFT(m_xBuilder->weld_label("labelFT_CHARSPERLINE")) + , m_xCharsPerLineNF(m_xBuilder->weld_spin_button("spinNF_CHARSPERLINE")) + , m_xCharsRangeFT(m_xBuilder->weld_label("labelFT_CHARRANGE")) + , m_xCharWidthFT(m_xBuilder->weld_label("labelFT_CHARWIDTH")) + , m_xCharWidthMF(m_xBuilder->weld_metric_spin_button("spinMF_CHARWIDTH", FieldUnit::POINT)) + , m_xRubySizeFT(m_xBuilder->weld_label("labelFT_RUBYSIZE")) + , m_xRubySizeMF(m_xBuilder->weld_metric_spin_button("spinMF_RUBYSIZE", FieldUnit::POINT)) + , m_xRubyBelowCB(m_xBuilder->weld_check_button("checkCB_RUBYBELOW")) + , m_xDisplayFL(m_xBuilder->weld_widget("frameFL_DISPLAY")) + , m_xDisplayCB(m_xBuilder->weld_check_button("checkCB_DISPLAY")) + , m_xPrintCB(m_xBuilder->weld_check_button("checkCB_PRINT")) + , m_xColorLB(new ColorListBox(m_xBuilder->weld_menu_button("listLB_COLOR"), pController->getDialog())) +{ + Link<weld::SpinButton&,void> aLink = LINK(this, SwTextGridPage, CharorLineChangedHdl); + m_xCharsPerLineNF->connect_value_changed(aLink); + m_xLinesPerPageNF->connect_value_changed(aLink); + + Link<weld::MetricSpinButton&,void> aSizeLink = LINK(this, SwTextGridPage, TextSizeChangedHdl); + m_xTextSizeMF->connect_value_changed(aSizeLink); + m_xRubySizeMF->connect_value_changed(aSizeLink); + m_xCharWidthMF->connect_value_changed(aSizeLink); + + Link<weld::ToggleButton&,void> aGridTypeHdl = LINK(this, SwTextGridPage, GridTypeHdl); + m_xNoGridRB->connect_toggled(aGridTypeHdl); + m_xLinesGridRB->connect_toggled(aGridTypeHdl); + m_xCharsGridRB->connect_toggled(aGridTypeHdl); + + m_xColorLB->SetSelectHdl(LINK(this, SwTextGridPage, ColorModifyHdl)); + m_xPrintCB->connect_toggled(LINK(this, SwTextGridPage, GridModifyClickHdl)); + m_xRubyBelowCB->connect_toggled(LINK(this, SwTextGridPage, GridModifyClickHdl)); + + m_xDisplayCB->connect_toggled(LINK(this, SwTextGridPage, DisplayGridHdl)); + + //Get the default paper mode + SwView *pView = ::GetActiveView(); + if( pView ) + { + SwWrtShell* pSh = pView->GetWrtShellPtr(); + if( pSh ) + { + m_bSquaredMode = pSh->GetDoc()->IsSquaredPageMode(); + } + } + if( m_bSquaredMode ) + { + + m_xRubySizeFT->show(); + m_xRubySizeMF->show(); + m_xRubyBelowCB->show(); + m_xSnapToCharsCB->hide(); + m_xCharWidthFT->hide(); + m_xCharWidthMF->hide(); + } + else + { + m_xRubySizeFT->hide(); + m_xRubySizeMF->hide(); + m_xRubyBelowCB->hide(); + m_xSnapToCharsCB->show(); + m_xCharWidthFT->show(); + m_xCharWidthMF->show(); + } +} + +SwTextGridPage::~SwTextGridPage() +{ + m_xColorLB.reset(); +} + +std::unique_ptr<SfxTabPage> SwTextGridPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet) +{ + return std::make_unique<SwTextGridPage>(pPage, pController, *rSet); +} + +bool SwTextGridPage::FillItemSet(SfxItemSet *rSet) +{ + bool bRet = false; + if (m_xNoGridRB->get_state_changed_from_saved() || + m_xLinesGridRB->get_state_changed_from_saved() || + m_xLinesPerPageNF->get_value_changed_from_saved() || + m_xTextSizeMF->get_value_changed_from_saved() || + m_xCharsPerLineNF->get_value_changed_from_saved() || + m_xSnapToCharsCB->get_state_changed_from_saved() || + m_xRubySizeMF->get_value_changed_from_saved() || + m_xCharWidthMF->get_value_changed_from_saved() || + m_xRubyBelowCB->get_state_changed_from_saved() || + m_xDisplayCB->get_state_changed_from_saved() || + m_xPrintCB->get_state_changed_from_saved() || + m_xColorLB->IsValueChangedFromSaved()) + { + PutGridItem(*rSet); + bRet = true; + } + + // draw ticks of ruler + SwView * pView = ::GetActiveView(); + if ( m_bHRulerChanged ) + pView->GetHRuler().DrawTicks(); + if ( m_bVRulerChanged ) + pView->GetVRuler().DrawTicks(); + return bRet; +} + +void SwTextGridPage::Reset(const SfxItemSet *rSet) +{ + sal_Int32 nLinesPerPage = 0; + + if(SfxItemState::DEFAULT <= rSet->GetItemState(RES_TEXTGRID)) + { + const SwTextGridItem& rGridItem = rSet->Get(RES_TEXTGRID); + weld::RadioButton* pButton = nullptr; + switch(rGridItem.GetGridType()) + { + case GRID_NONE : pButton = m_xNoGridRB.get(); break; + case GRID_LINES_ONLY : pButton = m_xLinesGridRB.get(); break; + default: pButton = m_xCharsGridRB.get(); + } + pButton->set_active(true); + m_xDisplayCB->set_active(rGridItem.IsDisplayGrid()); + GridTypeHdl(*pButton); + m_xSnapToCharsCB->set_active(rGridItem.IsSnapToChars()); + nLinesPerPage = rGridItem.GetLines(); + + SetLinesOrCharsRanges(*m_xLinesRangeFT , m_xLinesPerPageNF->get_max()); + m_nRubyUserValue = rGridItem.GetBaseHeight(); + m_bRubyUserValue = true; + m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(m_nRubyUserValue), FieldUnit::TWIP); + m_xRubySizeMF->set_value(m_xRubySizeMF->normalize(rGridItem.GetRubyHeight()), FieldUnit::TWIP); + m_xCharWidthMF->set_value(m_xCharWidthMF->normalize(rGridItem.GetBaseWidth()), FieldUnit::TWIP); + m_xRubyBelowCB->set_active(rGridItem.IsRubyTextBelow()); + m_xPrintCB->set_active(rGridItem.IsPrintGrid()); + m_xColorLB->SelectEntry(rGridItem.GetColor()); + } + UpdatePageSize(*rSet); + + if (nLinesPerPage > 0) + m_xLinesPerPageNF->set_value(nLinesPerPage); + + m_xNoGridRB->save_state(); + m_xLinesGridRB->save_state(); + m_xSnapToCharsCB->save_state(); + m_xLinesPerPageNF->save_value(); + m_xTextSizeMF->save_value(); + m_xCharsPerLineNF->save_value(); + m_xRubySizeMF->save_value(); + m_xCharWidthMF->save_value(); + m_xRubyBelowCB->save_state(); + m_xDisplayCB->save_state(); + m_xPrintCB->save_state(); + m_xColorLB->SaveValue(); +} + +void SwTextGridPage::ActivatePage( const SfxItemSet& rSet ) +{ + m_aExampleWN.Hide(); + m_aExampleWN.UpdateExample(rSet); + UpdatePageSize(rSet); + m_aExampleWN.Show(); + m_aExampleWN.Invalidate(); +} + +DeactivateRC SwTextGridPage::DeactivatePage( SfxItemSet* ) +{ + return DeactivateRC::LeavePage; +} + +void SwTextGridPage::PutGridItem(SfxItemSet& rSet) +{ + SwTextGridItem aGridItem; + aGridItem.SetGridType(m_xNoGridRB->get_active() ? GRID_NONE : + m_xLinesGridRB->get_active() ? GRID_LINES_ONLY : GRID_LINES_CHARS ); + aGridItem.SetSnapToChars(m_xSnapToCharsCB->get_active()); + aGridItem.SetLines( static_cast< sal_uInt16 >(m_xLinesPerPageNF->get_value()) ); + aGridItem.SetBaseHeight( static_cast< sal_uInt16 >( + m_bRubyUserValue ? m_nRubyUserValue : + m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP))) ); + aGridItem.SetRubyHeight( static_cast< sal_uInt16 >(m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP))) ); + aGridItem.SetBaseWidth( static_cast< sal_uInt16 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP))) ); + aGridItem.SetRubyTextBelow(m_xRubyBelowCB->get_active()); + aGridItem.SetSquaredMode(m_bSquaredMode); + aGridItem.SetDisplayGrid(m_xDisplayCB->get_active()); + aGridItem.SetPrintGrid(m_xPrintCB->get_active()); + aGridItem.SetColor(m_xColorLB->GetSelectEntryColor()); + rSet.Put(aGridItem); + + SwView * pView = ::GetActiveView(); + if ( aGridItem.GetGridType() != GRID_NONE ) + { + if ( aGridItem.GetGridType() == GRID_LINES_CHARS ) + { + m_bHRulerChanged = true; + } + m_bVRulerChanged = true; + pView->GetHRuler().SetCharWidth(static_cast<long>(m_xCharWidthMF->get_value(FieldUnit::TWIP)/56.7)); + pView->GetVRuler().SetLineHeight(static_cast<long>(m_xTextSizeMF->get_value(FieldUnit::TWIP)/56.7)); + } +} + +void SwTextGridPage::UpdatePageSize(const SfxItemSet& rSet) +{ + if( SfxItemState::UNKNOWN != rSet.GetItemState( RES_FRAMEDIR )) + { + const SvxFrameDirectionItem& rDirItem = + rSet.Get(RES_FRAMEDIR); + m_bVertical = rDirItem.GetValue() == SvxFrameDirection::Vertical_RL_TB|| + rDirItem.GetValue() == SvxFrameDirection::Vertical_LR_TB; + } + + if( SfxItemState::SET != rSet.GetItemState( SID_ATTR_PAGE_SIZE )) + return; + + const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE); + const SvxLRSpaceItem& rLRSpace = rSet.Get( RES_LR_SPACE ); + const SvxULSpaceItem& rULSpace = rSet.Get( RES_UL_SPACE ); + const SvxBoxItem& rBox = rSet.Get(RES_BOX); + sal_Int32 nDistanceLR = rLRSpace.GetLeft() + rLRSpace.GetRight(); + sal_Int32 nDistanceUL = rULSpace.GetUpper() + rULSpace.GetLower(); + + sal_Int32 nValue1 = rSize.GetSize().Height() - nDistanceUL - + rBox.GetDistance(SvxBoxItemLine::TOP) - + rBox.GetDistance(SvxBoxItemLine::BOTTOM); + sal_Int32 nValue2 = rSize.GetSize().Width() - nDistanceLR - + rBox.GetDistance(SvxBoxItemLine::LEFT) - + rBox.GetDistance(SvxBoxItemLine::RIGHT); + if(m_bVertical) + { + m_aPageSize.setWidth( nValue1 ); + m_aPageSize.setHeight( nValue2 ); + } + else + { + m_aPageSize.setWidth( nValue2 ); + m_aPageSize.setHeight( nValue1 ); + } + + sal_Int32 nTextSize = static_cast< sal_Int32 >(m_bRubyUserValue ? + m_nRubyUserValue : + m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP))); + + if ( m_bSquaredMode ) + { + sal_Int32 nCharsPerLine = m_aPageSize.Width() / nTextSize; + m_xCharsPerLineNF->set_max(nCharsPerLine); + m_xCharsPerLineNF->set_value(nCharsPerLine); + m_xLinesPerPageNF->set_max(m_aPageSize.Height() / + ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) + + m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP)))); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + } + else + { + sal_Int32 nTextWidth = static_cast< sal_Int32 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP))); + m_xLinesPerPageNF->set_value(m_aPageSize.Height() / nTextSize); + if (nTextWidth) + m_xCharsPerLineNF->set_value(m_aPageSize.Width() / nTextWidth); + else + m_xCharsPerLineNF->set_value(45); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + } +} + +void SwTextGridPage::SetLinesOrCharsRanges(weld::Label& rField, const sal_Int32 nValue ) +{ + OUString aFieldStr = "( 1 -" + OUString::number(nValue) + " )"; + rField.set_label(aFieldStr); +} + +const sal_uInt16* SwTextGridPage::GetRanges() +{ + static const sal_uInt16 aPageRg[] = { + RES_TEXTGRID, RES_TEXTGRID, + 0}; + return aPageRg; +} + +IMPL_LINK(SwTextGridPage, CharorLineChangedHdl, weld::SpinButton&, rField, void) +{ + //if in squared mode + if ( m_bSquaredMode ) + { + if (m_xCharsPerLineNF.get() == &rField) + { + auto nValue = m_xCharsPerLineNF->get_value(); + assert(nValue && "div-by-zero"); + auto nWidth = m_aPageSize.Width() / nValue; + m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(nWidth), FieldUnit::TWIP); + //prevent rounding errors in the MetricField by saving the used value + m_nRubyUserValue = nWidth; + m_bRubyUserValue = true; + + } + //set maximum line per page + { + sal_Int32 nMaxLines = static_cast< sal_Int32 >(m_aPageSize.Height() / + ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) + + m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP)))); + m_xLinesPerPageNF->set_max(nMaxLines); + } + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + } + else//in normal mode + { + if (m_xLinesPerPageNF.get() == &rField) + { + auto nValue = m_xLinesPerPageNF->get_value(); + assert(nValue && "div-by-zero"); + auto nHeight = m_aPageSize.Height() / nValue; + m_xTextSizeMF->set_value(m_xTextSizeMF->normalize(nHeight), FieldUnit::TWIP); + m_xRubySizeMF->set_value(0, FieldUnit::TWIP); + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + + m_nRubyUserValue = nHeight; + m_bRubyUserValue = true; + } + else if (m_xCharsPerLineNF.get() == &rField) + { + auto nValue = m_xCharsPerLineNF->get_value(); + assert(nValue && "div-by-zero"); + auto nWidth = m_aPageSize.Width() / nValue; + m_xCharWidthMF->set_value(m_xCharWidthMF->normalize(nWidth), FieldUnit::TWIP); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + } + } + GridModifyHdl(); +} + +IMPL_LINK(SwTextGridPage, TextSizeChangedHdl, weld::MetricSpinButton&, rField, void) +{ + //if in squared mode + if( m_bSquaredMode ) + { + if (m_xTextSizeMF.get() == &rField) + { + m_bRubyUserValue = false; + + // fdo#50941: set maximum characters per line + sal_Int32 nTextSize = static_cast< sal_Int32 >(m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP))); + if (nTextSize > 0) + { + sal_Int32 nMaxChars = m_aPageSize.Width() / nTextSize; + m_xCharsPerLineNF->set_value(nMaxChars); + m_xCharsPerLineNF->set_max(nMaxChars); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + } + } + //set maximum line per page + { + sal_Int32 nMaxLines = static_cast< sal_Int32 >(m_aPageSize.Height() / + ( m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP)) + + m_xRubySizeMF->denormalize(m_xRubySizeMF->get_value(FieldUnit::TWIP)))); + m_xLinesPerPageNF->set_max(nMaxLines); + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + } + } + else + { + if (m_xTextSizeMF.get() == &rField) + { + sal_Int32 nTextSize = static_cast< sal_Int32 >(m_xTextSizeMF->denormalize(m_xTextSizeMF->get_value(FieldUnit::TWIP))); + m_xLinesPerPageNF->set_value(m_aPageSize.Height() / nTextSize); + m_bRubyUserValue = false; + SetLinesOrCharsRanges( *m_xLinesRangeFT , m_xLinesPerPageNF->get_max() ); + } + else if (m_xCharWidthMF.get() == &rField) + { + sal_Int32 nTextWidth = static_cast< sal_Int32 >(m_xCharWidthMF->denormalize(m_xCharWidthMF->get_value(FieldUnit::TWIP))); + sal_Int32 nMaxChar = 45 ; + if (nTextWidth) + nMaxChar = m_aPageSize.Width() / nTextWidth; + m_xCharsPerLineNF->set_value( nMaxChar ); + SetLinesOrCharsRanges( *m_xCharsRangeFT , m_xCharsPerLineNF->get_max() ); + } + //rubySize is disabled + } + GridModifyHdl(); +} + +IMPL_LINK(SwTextGridPage, GridTypeHdl, weld::ToggleButton&, rButton, void) +{ + bool bEnable = m_xNoGridRB.get() != &rButton; + m_xLayoutFL->set_sensitive(bEnable); + m_xDisplayFL->set_sensitive(bEnable); + + //one special case + if (bEnable) + DisplayGridHdl(*m_xDisplayCB); + + bEnable = m_xCharsGridRB.get() == &rButton; + m_xSnapToCharsCB->set_sensitive(bEnable); + + bEnable = m_xLinesGridRB.get() == &rButton; + if (bEnable && !m_bSquaredMode) + { + m_xCharsPerLineFT->set_sensitive(false); + m_xCharsPerLineNF->set_sensitive(false); + m_xCharsRangeFT->set_sensitive(false); + m_xCharWidthFT->set_sensitive(false); + m_xCharWidthMF->set_sensitive(false); + } + + GridModifyHdl(); +} + +IMPL_LINK_NOARG(SwTextGridPage, DisplayGridHdl, weld::ToggleButton&, void) +{ + bool bChecked = m_xDisplayCB->get_active(); + m_xPrintCB->set_sensitive(bChecked); + m_xPrintCB->set_active(bChecked); +} + +IMPL_LINK_NOARG(SwTextGridPage, GridModifyClickHdl, weld::ToggleButton&, void) +{ + GridModifyHdl(); +} + +IMPL_LINK_NOARG(SwTextGridPage, ColorModifyHdl, ColorListBox&, void) +{ + GridModifyHdl(); +} + +void SwTextGridPage::GridModifyHdl() +{ + const SfxItemSet& rOldSet = GetItemSet(); + SfxItemSet aSet(rOldSet); + const SfxItemSet* pExSet = GetDialogExampleSet(); + if(pExSet) + aSet.Put(*pExSet); + PutGridItem(aSet); + m_aExampleWN.UpdateExample(aSet); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/srtdlg.cxx b/sw/source/ui/misc/srtdlg.cxx new file mode 100644 index 000000000..3e46ea05a --- /dev/null +++ b/sw/source/ui/misc/srtdlg.cxx @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <srtdlg.hxx> + +#include <editeng/editids.hrc> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svl/intitem.hxx> +#include <svx/svxdlg.hxx> +#include <unotools/collatorwrapper.hxx> +#include <svtools/collatorres.hxx> +#include <swwait.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <strings.hrc> +#include <swtable.hxx> +#include <sortopt.hxx> +#include <node.hxx> +#include <tblsel.hxx> +#include <memory> + +static bool bCheck1 = true; +static bool bCheck2 = false; +static bool bCheck3 = false; + +static sal_uInt16 nCol1 = 1; +static sal_uInt16 nCol2 = 1; +static sal_uInt16 nCol3 = 1; + +static sal_uInt16 nType1 = 0; +static sal_uInt16 nType2 = 0; +static sal_uInt16 nType3 = 0; + +static LanguageType nLang = LANGUAGE_NONE; + +static bool bAsc1 = true; +static bool bAsc2 = true; +static bool bAsc3 = true; +static bool bCol = false; +static bool bCsSens= false; + +static sal_Unicode cDeli = '\t'; + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +// determine lines and columns for table selection +static bool lcl_GetSelTable( SwWrtShell const &rSh, sal_uInt16& rX, sal_uInt16& rY ) +{ + const SwTableNode* pTableNd = rSh.IsCursorInTable(); + if( !pTableNd ) + return false; + + FndBox_ aFndBox( nullptr, nullptr ); + + // look for all boxes / lines + { + SwSelBoxes aSelBoxes; + ::GetTableSel( rSh, aSelBoxes ); + FndPara aPara( aSelBoxes, &aFndBox ); + const SwTable& rTable = pTableNd->GetTable(); + ForEach_FndLineCopyCol( const_cast<SwTableLines&>(rTable.GetTabLines()), &aPara ); + } + rX = aFndBox.GetLines().size(); + if( !rX ) + return false; + + rY = aFndBox.GetLines().front()->GetBoxes().size(); + return true; +} + +// init list +SwSortDlg::SwSortDlg(weld::Window* pParent, SwWrtShell &rShell) + : GenericDialogController(pParent, "modules/swriter/ui/sortdialog.ui", "SortDialog") + , m_pParent(pParent) + , m_xColLbl(m_xBuilder->weld_label("column")) + , m_xKeyCB1(m_xBuilder->weld_check_button("key1")) + , m_xColEdt1(m_xBuilder->weld_spin_button("colsb1")) + , m_xTypDLB1(m_xBuilder->weld_combo_box("typelb1")) + , m_xSortUp1RB(m_xBuilder->weld_radio_button("up1")) + , m_xSortDn1RB(m_xBuilder->weld_radio_button("down1")) + , m_xKeyCB2(m_xBuilder->weld_check_button("key2")) + , m_xColEdt2(m_xBuilder->weld_spin_button("colsb2")) + , m_xTypDLB2(m_xBuilder->weld_combo_box("typelb2")) + , m_xSortUp2RB(m_xBuilder->weld_radio_button("up2")) + , m_xSortDn2RB(m_xBuilder->weld_radio_button("down2")) + , m_xKeyCB3(m_xBuilder->weld_check_button("key3")) + , m_xColEdt3(m_xBuilder->weld_spin_button("colsb3")) + , m_xTypDLB3(m_xBuilder->weld_combo_box("typelb3")) + , m_xSortUp3RB(m_xBuilder->weld_radio_button("up3")) + , m_xSortDn3RB(m_xBuilder->weld_radio_button("down3")) + , m_xColumnRB(m_xBuilder->weld_radio_button("columns")) + , m_xRowRB(m_xBuilder->weld_radio_button("rows")) + , m_xDelimTabRB(m_xBuilder->weld_radio_button("tabs")) + , m_xDelimFreeRB(m_xBuilder->weld_radio_button("character")) + , m_xDelimEdt(m_xBuilder->weld_entry("separator")) + , m_xDelimPB(m_xBuilder->weld_button("delimpb")) + , m_xLangLB(new SvxLanguageBox(m_xBuilder->weld_combo_box("langlb"))) + , m_xCaseCB(m_xBuilder->weld_check_button("matchcase")) + , aColText(SwResId(STR_COL)) + , aRowText(SwResId(STR_ROW)) + , aNumericText(SwResId(STR_NUMERIC)) + , rSh(rShell) + , nX(99) + , nY(99) +{ + if(rSh.GetSelectionType() & + (SelectionType::Table|SelectionType::TableCell) ) + { + m_xColumnRB->set_active(bCol); + m_xColLbl->set_label(bCol ? aRowText : aColText); + m_xRowRB->set_active(!bCol); + m_xDelimTabRB->set_sensitive(false); + m_xDelimFreeRB->set_sensitive(false); + m_xDelimEdt->set_sensitive(false); + } + else + { + m_xColumnRB->set_sensitive(false); + m_xRowRB->set_active(true); + m_xColLbl->set_label(aColText); + } + + // Set accessible names here because text of m_xColLbl may be changed + // by the if-else block above + m_xColEdt1->set_accessible_name(m_xColLbl->get_label()); + m_xColEdt2->set_accessible_name(m_xColLbl->get_label()); + m_xColEdt3->set_accessible_name(m_xColLbl->get_label()); + + // initialise + Link<weld::ToggleButton&,void> aLk = LINK(this, SwSortDlg, CheckHdl); + m_xKeyCB1->connect_toggled( aLk ); + m_xKeyCB2->connect_toggled( aLk ); + m_xKeyCB3->connect_toggled( aLk ); + m_xColumnRB->connect_toggled( aLk ); + m_xRowRB->connect_toggled( aLk ); + + aLk = LINK(this, SwSortDlg, DelimHdl); + m_xDelimFreeRB->connect_toggled(aLk); + m_xDelimTabRB->connect_toggled(aLk); + + m_xDelimPB->connect_clicked( LINK( this, SwSortDlg, DelimCharHdl )); + + m_xKeyCB1->set_active(bCheck1); + m_xKeyCB2->set_active(bCheck2); + m_xKeyCB3->set_active(bCheck3); + + m_xColEdt1->set_value(nCol1); + m_xColEdt2->set_value(nCol2); + m_xColEdt3->set_value(nCol3); + + // first initialise the language, then select the + if( LANGUAGE_NONE == nLang || LANGUAGE_DONTKNOW == nLang ) + nLang = GetAppLanguage(); + + m_xLangLB->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, true ); + m_xLangLB->set_active_id(nLang); + + LanguageHdl( nullptr ); + m_xLangLB->connect_changed( LINK( this, SwSortDlg, LanguageListBoxHdl )); + + m_xSortUp1RB->set_active(bAsc1); + m_xSortDn1RB->set_active(!bAsc1); + m_xSortUp2RB->set_active(bAsc2); + m_xSortDn2RB->set_active(!bAsc2); + m_xSortUp3RB->set_active(bAsc3); + m_xSortDn3RB->set_active(!bAsc3); + + m_xCaseCB->set_active( bCsSens ); + + m_xDelimTabRB->set_active(cDeli == '\t'); + if(!m_xDelimTabRB->get_active()) + { + m_xDelimEdt->set_text(OUString(cDeli)); + m_xDelimFreeRB->set_active(true); + DelimHdl(*m_xDelimFreeRB); + } + else + DelimHdl(*m_xDelimTabRB); + + if( ::lcl_GetSelTable( rSh, nX, nY) ) + { + sal_uInt16 nMax = m_xRowRB->get_active()? nY : nX; + m_xColEdt1->set_max(nMax); + m_xColEdt2->set_max(nMax); + m_xColEdt3->set_max(nMax); + } +} + +sal_Unicode SwSortDlg::GetDelimChar() const +{ + sal_Unicode cRet = '\t'; + if( !m_xDelimTabRB->get_active() ) + { + OUString aTmp(m_xDelimEdt->get_text()); + if( !aTmp.isEmpty() ) + cRet = aTmp[0]; + } + return cRet; +} + +short SwSortDlg::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +// pass on to the Core +void SwSortDlg::Apply() +{ + // save all settings + bCheck1 = m_xKeyCB1->get_active(); + bCheck2 = m_xKeyCB2->get_active(); + bCheck3 = m_xKeyCB3->get_active(); + + nCol1 = m_xColEdt1->get_value(); + nCol2 = m_xColEdt2->get_value(); + nCol3 = m_xColEdt3->get_value(); + + nType1 = m_xTypDLB1->get_active(); + nType2 = m_xTypDLB2->get_active(); + nType3 = m_xTypDLB3->get_active(); + + bAsc1 = m_xSortUp1RB->get_active(); + bAsc2 = m_xSortUp2RB->get_active(); + bAsc3 = m_xSortUp3RB->get_active(); + bCol = m_xColumnRB->get_active(); + nLang = m_xLangLB->get_active_id(); + cDeli = GetDelimChar(); + bCsSens = m_xCaseCB->get_active(); + + SwSortOptions aOptions; + if( bCheck1 ) + { + OUString sEntry( m_xTypDLB1->get_active_text() ); + if( sEntry == aNumericText ) + sEntry.clear(); + else if (!m_xTypDLB1->get_active_id().isEmpty()) + sEntry = m_xTypDLB1->get_active_id(); + + aOptions.aKeys.push_back( + std::make_unique<SwSortKey>( nCol1, sEntry, + bAsc1 ? SwSortOrder::Ascending : SwSortOrder::Descending )); + } + + if( bCheck2 ) + { + OUString sEntry( m_xTypDLB2->get_active_text() ); + if( sEntry == aNumericText ) + sEntry.clear(); + else if (!m_xTypDLB2->get_active_id().isEmpty()) + sEntry = m_xTypDLB2->get_active_id(); + + aOptions.aKeys.push_back( + std::make_unique<SwSortKey>( nCol2, sEntry, + bAsc2 ? SwSortOrder::Ascending : SwSortOrder::Descending )); + } + + if( bCheck3 ) + { + OUString sEntry( m_xTypDLB3->get_active_text() ); + if( sEntry == aNumericText ) + sEntry.clear(); + else if (!m_xTypDLB3->get_active_id().isEmpty()) + sEntry = m_xTypDLB3->get_active_id(); + + aOptions.aKeys.push_back( + std::make_unique<SwSortKey>( nCol3, sEntry, + bAsc3 ? SwSortOrder::Ascending : SwSortOrder::Descending )); + } + + aOptions.eDirection = bCol ? SwSortDirection::Columns : SwSortDirection::Rows; + aOptions.cDeli = cDeli; + aOptions.nLanguage = nLang; + aOptions.bTable = rSh.IsTableMode(); + aOptions.bIgnoreCase = !bCsSens; + + bool bRet; + { + SwWait aWait( *rSh.GetView().GetDocShell(), true ); + rSh.StartAllAction(); + bRet = rSh.Sort( aOptions ); + if( bRet ) + rSh.SetModified(); + rSh.EndAllAction(); + } + + if (!bRet) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_pParent, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_SRTERR))); + xInfoBox->run(); + } +} + +IMPL_LINK( SwSortDlg, DelimHdl, weld::ToggleButton&, rButton, void ) +{ + bool bEnable = &rButton == m_xDelimFreeRB.get() && m_xDelimFreeRB->get_sensitive(); + m_xDelimEdt->set_sensitive( bEnable ); + m_xDelimPB->set_sensitive( bEnable ); +} + +IMPL_LINK_NOARG(SwSortDlg, DelimCharHdl, weld::Button&, void) +{ + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + SfxAllItemSet aSet( rSh.GetAttrPool() ); + aSet.Put( SfxInt32Item( SID_ATTR_CHAR, GetDelimChar() ) ); + ScopedVclPtr<SfxAbstractDialog> pMap(pFact->CreateCharMapDialog(m_xDialog.get(), aSet, nullptr)); + if( RET_OK == pMap->Execute() ) + { + const SfxInt32Item* pItem = SfxItemSet::GetItem<SfxInt32Item>(pMap->GetOutputItemSet(), SID_ATTR_CHAR, false); + if ( pItem ) + m_xDelimEdt->set_text(OUString(sal_Unicode(pItem->GetValue()))); + } +} + +IMPL_LINK( SwSortDlg, CheckHdl, weld::ToggleButton&, rControl, void ) +{ + if (&rControl == m_xRowRB.get()) + { + m_xColLbl->set_label(aColText); + m_xColEdt1->set_max(nY); + m_xColEdt2->set_max(nY); + m_xColEdt3->set_max(nY); + + m_xColEdt1->set_accessible_name(aColText); + m_xColEdt2->set_accessible_name(aColText); + m_xColEdt3->set_accessible_name(aColText); + } + else if (&rControl == m_xColumnRB.get()) + { + m_xColLbl->set_label(aRowText); + m_xColEdt1->set_max(nX); + m_xColEdt2->set_max(nX); + m_xColEdt3->set_max(nX); + + m_xColEdt1->set_accessible_name(aRowText); + m_xColEdt2->set_accessible_name(aRowText); + m_xColEdt3->set_accessible_name(aRowText); + } + else if(!m_xKeyCB1->get_active() && + !m_xKeyCB2->get_active() && + !m_xKeyCB3->get_active()) + { + rControl.set_active(true); + } +} + +IMPL_LINK( SwSortDlg, LanguageListBoxHdl, weld::ComboBox&, rLBox, void ) +{ + LanguageHdl(&rLBox); +} + +void SwSortDlg::LanguageHdl(weld::ComboBox const* pLBox) +{ + Sequence < OUString > aSeq( GetAppCollator().listCollatorAlgorithms( + LanguageTag( m_xLangLB->get_active_id()).getLocale() )); + + if (!m_xColRes) + m_xColRes.reset(new CollatorResource); + + const int nLstBoxCnt = 3; + weld::ComboBox* aLstArr[ nLstBoxCnt ] = { m_xTypDLB1.get(), m_xTypDLB2.get(), m_xTypDLB3.get() }; + sal_uInt16* const aTypeArr[ nLstBoxCnt ] = { &nType1, &nType2, &nType3 }; + OUString aOldStrArr[ nLstBoxCnt ]; + + for( int n = 0; n < nLstBoxCnt; ++n ) + { + weld::ComboBox* pL = aLstArr[ n ]; + OUString sUserData = pL->get_active_id(); + if (!sUserData.isEmpty()) + aOldStrArr[ n ] = sUserData; + pL->clear(); + } + + OUString sAlg, sUINm; + const sal_Int32 nEnd = aSeq.getLength(); + for( sal_Int32 nCnt = 0; nCnt <= nEnd; ++nCnt ) + { + if( nCnt < nEnd ) + { + sAlg = aSeq[ nCnt ]; + sUINm = m_xColRes->GetTranslation( sAlg ); + } + else + sUINm = sAlg = aNumericText; + + for( int n = 0; n < nLstBoxCnt; ++n ) + { + weld::ComboBox* pL = aLstArr[ n ]; + pL->append(sAlg, sUINm); + if (pLBox && sAlg == aOldStrArr[n]) + pL->set_active_id(sAlg); + } + } + + for( int n = 0; n < nLstBoxCnt; ++n ) + { + weld::ComboBox* pL = aLstArr[ n ]; + if( !pLBox ) + pL->set_active(*aTypeArr[n]); + else if (pL->get_active() == -1) + pL->set_active(0); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/swmodalredlineacceptdlg.cxx b/sw/source/ui/misc/swmodalredlineacceptdlg.cxx new file mode 100644 index 000000000..0054b360e --- /dev/null +++ b/sw/source/ui/misc/swmodalredlineacceptdlg.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 <svx/ctredlin.hxx> +#include <unotools/viewoptions.hxx> + +#include <redlndlg.hxx> +#include <swmodalredlineacceptdlg.hxx> + +SwModalRedlineAcceptDlg::SwModalRedlineAcceptDlg(weld::Window *pParent) + : SfxDialogController(pParent, "svx/ui/acceptrejectchangesdialog.ui", + "AcceptRejectChangesDialog") + , m_xContentArea(m_xDialog->weld_content_area()) +{ + m_xDialog->set_modal(true); + + m_xImplDlg.reset(new SwRedlineAcceptDlg(m_xDialog, m_xBuilder.get(), m_xContentArea.get(), true)); + + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + if (aDlgOpt.Exists()) + { + css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem"); + OUString sExtraData; + aUserItem >>= sExtraData; + m_xImplDlg->Initialize(sExtraData); + } + m_xImplDlg->Activate(); // for data's initialisation +} + +SwModalRedlineAcceptDlg::~SwModalRedlineAcceptDlg() +{ + AcceptAll(false); // refuse everything remaining + + OUString sExtraData; + m_xImplDlg->FillInfo(sExtraData); + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + aDlgOpt.SetUserItem("UserItem", css::uno::makeAny(sExtraData)); + + m_xDialog->set_modal(false); +} + +void SwModalRedlineAcceptDlg::Activate() +{ +} + +void SwModalRedlineAcceptDlg::AcceptAll( bool bAccept ) +{ + SvxTPFilter* pFilterTP = m_xImplDlg->GetChgCtrl().GetFilterPage(); + + if (pFilterTP->IsDate() || pFilterTP->IsAuthor() || + pFilterTP->IsRange() || pFilterTP->IsAction()) + { + pFilterTP->CheckDate(false); // turn off all filters + pFilterTP->CheckAuthor(false); + pFilterTP->CheckRange(false); + pFilterTP->CheckAction(false); + m_xImplDlg->FilterChangedHdl(nullptr); + } + + m_xImplDlg->CallAcceptReject( false, bAccept ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/misc/titlepage.cxx b/sw/source/ui/misc/titlepage.cxx new file mode 100644 index 000000000..26fa4bdeb --- /dev/null +++ b/sw/source/ui/misc/titlepage.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/. + */ + +#include <view.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <poolfmt.hxx> +#include <docsh.hxx> + +#include <titlepage.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> + +namespace +{ + bool lcl_GetPageDesc(SwWrtShell *pSh, sal_uInt16 &rPageNo, std::unique_ptr<const SwFormatPageDesc>* ppPageFormatDesc) + { + bool bRet = false; + SfxItemSet aSet( pSh->GetAttrPool(), svl::Items<RES_PAGEDESC, RES_PAGEDESC>{} ); + if (pSh->GetCurAttr( aSet )) + { + const SfxPoolItem* pItem(nullptr); + if (SfxItemState::SET == aSet.GetItemState( RES_PAGEDESC, true, &pItem ) && pItem) + { + ::std::optional<sal_uInt16> oNumOffset = static_cast<const SwFormatPageDesc *>(pItem)->GetNumOffset(); + if (oNumOffset) + rPageNo = *oNumOffset; + if (ppPageFormatDesc) + ppPageFormatDesc->reset(static_cast<const SwFormatPageDesc *>(pItem->Clone())); + bRet = true; + } + } + return bRet; + } + + void lcl_ChangePage(SwWrtShell *pSh, sal_uInt16 nNewNumber, + const SwPageDesc *pNewDesc) + { + const size_t nCurIdx = pSh->GetCurPageDesc(); + const SwPageDesc &rCurrentDesc = pSh->GetPageDesc( nCurIdx ); + + std::unique_ptr<const SwFormatPageDesc> pPageFormatDesc; + sal_uInt16 nDontCare; + lcl_GetPageDesc(pSh, nDontCare, &pPageFormatDesc); + + // If we want a new number then set it, otherwise reuse the existing one + sal_uInt16 nPgNo; + if (nNewNumber) + { + nPgNo = nNewNumber; + } + else + { + if (pPageFormatDesc) + { + ::std::optional<sal_uInt16> oNumOffset = pPageFormatDesc->GetNumOffset(); + if (oNumOffset) + { + nPgNo = *oNumOffset; + } + else + { + nPgNo = 0; + } + } + else + { + nPgNo = 0; + } + } + + // If we want a new descriptor then set it, otherwise reuse the existing one + if (!pNewDesc) + { + SwFormatPageDesc aPageFormatDesc(pPageFormatDesc ? *pPageFormatDesc : &rCurrentDesc); + if (nPgNo) aPageFormatDesc.SetNumOffset(nPgNo); + pSh->SetAttrItem(aPageFormatDesc); + } + else + { + SwFormatPageDesc aPageFormatDesc(pNewDesc); + if (nPgNo) aPageFormatDesc.SetNumOffset(nPgNo); + pSh->SetAttrItem(aPageFormatDesc); + } + } + + void lcl_PushCursor(SwWrtShell *pSh) + { + pSh->LockView( true ); + pSh->StartAllAction(); + pSh->SwCursorShell::Push(); + } + + void lcl_PopCursor(SwWrtShell *pSh) + { + pSh->SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + pSh->EndAllAction(); + pSh->LockView( false ); + } + + sal_uInt16 lcl_GetCurrentPage(SwWrtShell const *pSh) + { + OUString sDummy; + sal_uInt16 nPhyNum=1, nVirtNum=1; + pSh->GetPageNumber(0, true, nPhyNum, nVirtNum, sDummy); + return nPhyNum; + } +} + +/* + * Only include the Index page in the list if the page count implies one + * to reduce confusing things + */ +void SwTitlePageDlg::FillList() +{ + sal_uInt16 nTitlePages = m_xPageCountNF->get_value(); + m_xPagePropertiesLB->clear(); + if (mpTitleDesc) + m_xPagePropertiesLB->append_text(mpTitleDesc->GetName()); + if (nTitlePages > 1 && mpIndexDesc) + m_xPagePropertiesLB->append_text(mpIndexDesc->GetName()); + if (mpNormalDesc) + m_xPagePropertiesLB->append_text(mpNormalDesc->GetName()); + m_xPagePropertiesLB->set_active(0); +} + +sal_uInt16 SwTitlePageDlg::GetInsertPosition() const +{ + sal_uInt16 nPage = 1; + if (m_xPageStartNF->get_sensitive()) + nPage = m_xPageStartNF->get_value(); + return nPage; +} + +SwTitlePageDlg::SwTitlePageDlg(weld::Window *pParent) + : SfxDialogController(pParent, "modules/swriter/ui/titlepage.ui", "DLG_TITLEPAGE") + , m_xUseExistingPagesRB(m_xBuilder->weld_radio_button("RB_USE_EXISTING_PAGES")) + , m_xPageCountNF(m_xBuilder->weld_spin_button("NF_PAGE_COUNT")) + , m_xDocumentStartRB(m_xBuilder->weld_radio_button("RB_DOCUMENT_START")) + , m_xPageStartRB(m_xBuilder->weld_radio_button("RB_PAGE_START")) + , m_xPageStartNF(m_xBuilder->weld_spin_button("NF_PAGE_START")) + , m_xRestartNumberingCB(m_xBuilder->weld_check_button("CB_RESTART_NUMBERING")) + , m_xRestartNumberingNF(m_xBuilder->weld_spin_button("NF_RESTART_NUMBERING")) + , m_xSetPageNumberCB(m_xBuilder->weld_check_button("CB_SET_PAGE_NUMBER")) + , m_xSetPageNumberNF(m_xBuilder->weld_spin_button("NF_SET_PAGE_NUMBER")) + , m_xPagePropertiesLB(m_xBuilder->weld_combo_box("LB_PAGE_PROPERTIES")) + , m_xPagePropertiesPB(m_xBuilder->weld_button("PB_PAGE_PROPERTIES")) + , m_xOkPB(m_xBuilder->weld_button("ok")) +{ + m_xOkPB->connect_clicked(LINK(this, SwTitlePageDlg, OKHdl)); + m_xRestartNumberingCB->connect_toggled(LINK(this, SwTitlePageDlg, RestartNumberingHdl)); + m_xSetPageNumberCB->connect_toggled(LINK(this, SwTitlePageDlg, SetPageNumberHdl)); + + sal_uInt16 nSetPage = 1; + sal_uInt16 nResetPage = 1; + sal_uInt16 nTitlePages = 1; + mpSh = ::GetActiveView()->GetWrtShellPtr(); + lcl_PushCursor(mpSh); + + SwView& rView = mpSh->GetView(); + rView.InvalidateRulerPos(); + + bool bMaybeResetNumbering = false; + + mpTitleDesc = mpSh->GetPageDescFromPool(RES_POOLPAGE_FIRST); + mpIndexDesc = mpSh->GetPageDescFromPool(RES_POOLPAGE_REGISTER); + mpNormalDesc = mpSh->GetPageDescFromPool(RES_POOLPAGE_STANDARD); + + mpSh->StartOfSection(); + if (lcl_GetPageDesc( mpSh, nSetPage, &mpPageFormatDesc )) + { + if (mpPageFormatDesc->GetPageDesc() == mpTitleDesc) + { + while (mpSh->SttNxtPg()) + { + const size_t nCurIdx = mpSh->GetCurPageDesc(); + const SwPageDesc &rPageDesc = mpSh->GetPageDesc( nCurIdx ); + + if (mpIndexDesc != &rPageDesc) + { + mpNormalDesc = &rPageDesc; + bMaybeResetNumbering = lcl_GetPageDesc(mpSh, nResetPage, nullptr); + break; + } + ++nTitlePages; + } + } + } + lcl_PopCursor(mpSh); + + m_xUseExistingPagesRB->set_active(true); + m_xPageCountNF->set_value(nTitlePages); + m_xPageCountNF->connect_value_changed(LINK(this, SwTitlePageDlg, ValueChangeHdl)); + + m_xDocumentStartRB->set_active(true); + m_xPageStartNF->set_sensitive(false); + m_xPageStartNF->set_value(lcl_GetCurrentPage(mpSh)); + Link<weld::ToggleButton&,void> aStartPageHdl = LINK(this, SwTitlePageDlg, StartPageHdl); + m_xDocumentStartRB->connect_toggled(aStartPageHdl); + m_xPageStartRB->connect_toggled(aStartPageHdl); + + if (bMaybeResetNumbering && nResetPage > 0) + { + m_xRestartNumberingCB->set_active(true); + m_xRestartNumberingNF->set_value(nResetPage); + } + m_xRestartNumberingNF->set_sensitive(m_xRestartNumberingCB->get_active()); + + m_xSetPageNumberNF->set_value(nSetPage); + if (nSetPage > 1) + m_xSetPageNumberCB->set_active(true); + m_xSetPageNumberNF->set_sensitive(m_xSetPageNumberCB->get_active()); + + FillList(); + m_xPagePropertiesPB->connect_clicked(LINK(this, SwTitlePageDlg, EditHdl)); +} + +IMPL_LINK_NOARG(SwTitlePageDlg, ValueChangeHdl, weld::SpinButton&, void) +{ + if (m_xPageCountNF->get_value() == 1 || m_xPageCountNF->get_value() == 2) + FillList(); +} + +IMPL_LINK_NOARG(SwTitlePageDlg, RestartNumberingHdl, weld::ToggleButton&, void) +{ + m_xRestartNumberingNF->set_sensitive(m_xRestartNumberingCB->get_active()); +} + +IMPL_LINK_NOARG(SwTitlePageDlg, SetPageNumberHdl, weld::ToggleButton&, void) +{ + m_xSetPageNumberNF->set_sensitive(m_xSetPageNumberCB->get_active()); +} + +IMPL_LINK_NOARG(SwTitlePageDlg, StartPageHdl, weld::ToggleButton&, void) +{ + m_xPageStartNF->set_sensitive(m_xPageStartRB->get_active()); +} + +SwTitlePageDlg::~SwTitlePageDlg() +{ +} + +IMPL_LINK_NOARG(SwTitlePageDlg, EditHdl, weld::Button&, void) +{ + SwView& rView = mpSh->GetView(); + rView.GetDocShell()->FormatPage(m_xPagePropertiesLB->get_active_text(), "page", *mpSh); + rView.InvalidateRulerPos(); +} + +IMPL_LINK_NOARG(SwTitlePageDlg, OKHdl, weld::Button&, void) +{ + lcl_PushCursor(mpSh); + + mpSh->StartUndo(); + + SwFormatPageDesc aTitleDesc(mpTitleDesc); + + if (m_xSetPageNumberCB->get_active()) + aTitleDesc.SetNumOffset(m_xSetPageNumberNF->get_value()); + else if (mpPageFormatDesc) + aTitleDesc.SetNumOffset(mpPageFormatDesc->GetNumOffset()); + + sal_uInt16 nNoPages = m_xPageCountNF->get_value(); + if (!m_xUseExistingPagesRB->get_active()) + { + mpSh->GotoPage(GetInsertPosition(), false); + for (sal_uInt16 nI=0; nI < nNoPages; ++nI) + mpSh->InsertPageBreak(); + } + + mpSh->GotoPage(GetInsertPosition(), false); + for (sal_uInt16 nI=1; nI < nNoPages; ++nI) + { + if (mpSh->SttNxtPg()) + lcl_ChangePage(mpSh, 0, mpIndexDesc); + } + + mpSh->GotoPage(GetInsertPosition(), false); + mpSh->SetAttrItem(aTitleDesc); + + if (nNoPages > 1 && mpSh->GotoPage(GetInsertPosition() + nNoPages, false)) + { + SwFormatPageDesc aPageFormatDesc(mpNormalDesc); + mpSh->SetAttrItem(aPageFormatDesc); + } + + if (m_xRestartNumberingCB->get_active() || nNoPages > 1) + { + sal_uInt16 nPgNo = m_xRestartNumberingCB->get_active() ? m_xRestartNumberingNF->get_value() : 0; + const SwPageDesc *pNewDesc = nNoPages > 1 ? mpNormalDesc : nullptr; + mpSh->GotoPage(GetInsertPosition() + nNoPages, false); + lcl_ChangePage(mpSh, nPgNo, pNewDesc); + } + + mpSh->EndUndo(); + lcl_PopCursor(mpSh); + if (!m_xUseExistingPagesRB->get_active()) + mpSh->GotoPage(GetInsertPosition(), false); + m_xDialog->response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/autoformatpreview.cxx b/sw/source/ui/table/autoformatpreview.cxx new file mode 100644 index 000000000..b524532d9 --- /dev/null +++ b/sw/source/ui/table/autoformatpreview.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 <editeng/adjustitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <vcl/settings.hxx> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <comphelper/processfactory.hxx> +#include <svtools/scriptedtext.hxx> +#include <svx/framelink.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <strings.hrc> + +#include <autoformatpreview.hxx> + +#define FRAME_OFFSET 4 + +AutoFormatPreview::AutoFormatPreview() + : maCurrentData(OUString()) + , mbFitWidth(false) + , mbRTL(false) + , maStringJan(SwResId(STR_JAN)) + , maStringFeb(SwResId(STR_FEB)) + , maStringMar(SwResId(STR_MAR)) + , maStringNorth(SwResId(STR_NORTH)) + , maStringMid(SwResId(STR_MID)) + , maStringSouth(SwResId(STR_SOUTH)) + , maStringSum(SwResId(STR_SUM)) +{ + uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext(); + m_xBreak = i18n::BreakIterator::create(xContext); + mxNumFormat.reset(new SvNumberFormatter(xContext, LANGUAGE_SYSTEM)); + + Init(); +} + +void AutoFormatPreview::Resize() +{ + Size aSize = GetOutputSizePixel(); + maPreviousSize = Size(aSize.Width() - 6, aSize.Height() - 30); + mnLabelColumnWidth = (maPreviousSize.Width() - 4) / 4 - 12; + mnDataColumnWidth1 = (maPreviousSize.Width() - 4 - 2 * mnLabelColumnWidth) / 3; + mnDataColumnWidth2 = (maPreviousSize.Width() - 4 - 2 * mnLabelColumnWidth) / 4; + mnRowHeight = (maPreviousSize.Height() - 4) / 5; + NotifyChange(maCurrentData); +} + +void AutoFormatPreview::DetectRTL(SwWrtShell const* pWrtShell) +{ + if (!pWrtShell->IsCursorInTable()) // We haven't created the table yet + mbRTL = AllSettings::GetLayoutRTL(); + else + mbRTL = pWrtShell->IsTableRightToLeft(); +} + +static void lcl_SetFontProperties(vcl::Font& rFont, const SvxFontItem& rFontItem, + const SvxWeightItem& rWeightItem, + const SvxPostureItem& rPostureItem) +{ + rFont.SetFamily(rFontItem.GetFamily()); + rFont.SetFamilyName(rFontItem.GetFamilyName()); + rFont.SetStyleName(rFontItem.GetStyleName()); + rFont.SetCharSet(rFontItem.GetCharSet()); + rFont.SetPitch(rFontItem.GetPitch()); + rFont.SetWeight(rWeightItem.GetValue()); + rFont.SetItalic(rPostureItem.GetValue()); +} + +#define SETONALLFONTS(MethodName, Value) \ + rFont.MethodName(Value); \ + rCJKFont.MethodName(Value); \ + rCTLFont.MethodName(Value); + +void AutoFormatPreview::MakeFonts(vcl::RenderContext const& rRenderContext, sal_uInt8 nIndex, + vcl::Font& rFont, vcl::Font& rCJKFont, vcl::Font& rCTLFont) +{ + const SwBoxAutoFormat& rBoxFormat = maCurrentData.GetBoxFormat(nIndex); + + rFont = rCJKFont = rCTLFont = rRenderContext.GetFont(); + Size aFontSize(rFont.GetFontSize().Width(), 10 * rRenderContext.GetDPIScaleFactor()); + + lcl_SetFontProperties(rFont, rBoxFormat.GetFont(), rBoxFormat.GetWeight(), + rBoxFormat.GetPosture()); + lcl_SetFontProperties(rCJKFont, rBoxFormat.GetCJKFont(), rBoxFormat.GetCJKWeight(), + rBoxFormat.GetCJKPosture()); + lcl_SetFontProperties(rCTLFont, rBoxFormat.GetCTLFont(), rBoxFormat.GetCTLWeight(), + rBoxFormat.GetCTLPosture()); + + SETONALLFONTS(SetUnderline, rBoxFormat.GetUnderline().GetValue()); + SETONALLFONTS(SetOverline, rBoxFormat.GetOverline().GetValue()); + SETONALLFONTS(SetStrikeout, rBoxFormat.GetCrossedOut().GetValue()); + SETONALLFONTS(SetOutline, rBoxFormat.GetContour().GetValue()); + SETONALLFONTS(SetShadow, rBoxFormat.GetShadowed().GetValue()); + SETONALLFONTS(SetColor, rBoxFormat.GetColor().GetValue()); + SETONALLFONTS(SetFontSize, aFontSize); + SETONALLFONTS(SetTransparent, true); +} + +sal_uInt8 AutoFormatPreview::GetFormatIndex(size_t nCol, size_t nRow) const +{ + static const sal_uInt8 pnFormatMap[] + = { 0, 1, 2, 1, 3, 4, 5, 6, 5, 7, 8, 9, 10, 9, 11, 4, 5, 6, 5, 7, 12, 13, 14, 13, 15 }; + return pnFormatMap[maArray.GetCellIndex(nCol, nRow, mbRTL)]; +} + +void AutoFormatPreview::DrawString(vcl::RenderContext& rRenderContext, size_t nCol, size_t nRow) +{ + // Output of the cell text: + sal_uLong nNum; + double nVal; + OUString cellString; + sal_uInt8 nIndex = static_cast<sal_uInt8>(maArray.GetCellIndex(nCol, nRow, mbRTL)); + + switch (nIndex) + { + case 1: + cellString = maStringJan; + break; + case 2: + cellString = maStringFeb; + break; + case 3: + cellString = maStringMar; + break; + case 5: + cellString = maStringNorth; + break; + case 10: + cellString = maStringMid; + break; + case 15: + cellString = maStringSouth; + break; + case 4: + case 20: + cellString = maStringSum; + break; + case 6: + case 8: + case 16: + case 18: + nVal = nIndex; + nNum = 5; + goto MAKENUMSTR; + case 17: + case 7: + nVal = nIndex; + nNum = 6; + goto MAKENUMSTR; + case 11: + case 12: + case 13: + nVal = nIndex; + nNum = 12 == nIndex ? 10 : 9; + goto MAKENUMSTR; + case 9: + nVal = 21; + nNum = 7; + goto MAKENUMSTR; + case 14: + nVal = 36; + nNum = 11; + goto MAKENUMSTR; + case 19: + nVal = 51; + nNum = 7; + goto MAKENUMSTR; + case 21: + nVal = 33; + nNum = 13; + goto MAKENUMSTR; + case 22: + nVal = 36; + nNum = 14; + goto MAKENUMSTR; + case 23: + nVal = 39; + nNum = 13; + goto MAKENUMSTR; + case 24: + nVal = 108; + nNum = 15; + goto MAKENUMSTR; + + MAKENUMSTR: + if (maCurrentData.IsValueFormat()) + { + OUString sFormat; + LanguageType eLng, eSys; + maCurrentData.GetBoxFormat(sal_uInt8(nNum)).GetValueFormat(sFormat, eLng, eSys); + + SvNumFormatType nType; + bool bNew; + sal_Int32 nCheckPos; + sal_uInt32 nKey = mxNumFormat->GetIndexPuttingAndConverting(sFormat, eLng, eSys, + nType, bNew, nCheckPos); + Color* pDummy; + mxNumFormat->GetOutputString(nVal, nKey, cellString, &pDummy); + } + else + cellString = OUString::number(sal_Int32(nVal)); + break; + } + + if (cellString.isEmpty()) + return; + + SvtScriptedTextHelper aScriptedText(rRenderContext); + Size aStrSize; + sal_uInt8 nFormatIndex = GetFormatIndex(nCol, nRow); + const basegfx::B2DRange aCellRange(maArray.GetCellRange(nCol, nRow, true)); + const tools::Rectangle cellRect( + basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()), + basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY())); + Point aPos = cellRect.TopLeft(); + long nRightX = 0; + + Size theMaxStrSize(cellRect.GetWidth() - FRAME_OFFSET, cellRect.GetHeight() - FRAME_OFFSET); + if (maCurrentData.IsFont()) + { + vcl::Font aFont, aCJKFont, aCTLFont; + MakeFonts(rRenderContext, nFormatIndex, aFont, aCJKFont, aCTLFont); + aScriptedText.SetFonts(&aFont, &aCJKFont, &aCTLFont); + } + else + aScriptedText.SetDefaultFont(); + + aScriptedText.SetText(cellString, m_xBreak); + aStrSize = aScriptedText.GetTextSize(); + + if (maCurrentData.IsFont() && theMaxStrSize.Height() < aStrSize.Height()) + { + // If the string in this font does not + // fit into the cell, the standard font + // is taken again: + aScriptedText.SetDefaultFont(); + aStrSize = aScriptedText.GetTextSize(); + } + + while (theMaxStrSize.Width() <= aStrSize.Width() && cellString.getLength() > 1) + { + cellString = cellString.copy(0, cellString.getLength() - 1); + aScriptedText.SetText(cellString, m_xBreak); + aStrSize = aScriptedText.GetTextSize(); + } + + nRightX = cellRect.GetWidth() - aStrSize.Width() - FRAME_OFFSET; + + // vertical (always centering): + aPos.AdjustY((mnRowHeight - aStrSize.Height()) / 2); + + // horizontal + if (mbRTL) + aPos.AdjustX(nRightX); + else if (maCurrentData.IsJustify()) + { + const SvxAdjustItem& rAdj = maCurrentData.GetBoxFormat(nFormatIndex).GetAdjust(); + switch (rAdj.GetAdjust()) + { + case SvxAdjust::Left: + aPos.AdjustX(FRAME_OFFSET); + break; + case SvxAdjust::Right: + aPos.AdjustX(nRightX); + break; + default: + aPos.AdjustX((cellRect.GetWidth() - aStrSize.Width()) / 2); + break; + } + } + else + { + // Standard align: + if (nCol == 0 || nIndex == 4) + { + // Text-Label left or sum left aligned + aPos.AdjustX(FRAME_OFFSET); + } + else + { + // numbers/dates right aligned + aPos.AdjustX(nRightX); + } + } + + aScriptedText.DrawText(aPos); +} + +void AutoFormatPreview::DrawBackground(vcl::RenderContext& rRenderContext) +{ + for (size_t nRow = 0; nRow < 5; ++nRow) + { + for (size_t nCol = 0; nCol < 5; ++nCol) + { + SvxBrushItem aBrushItem( + maCurrentData.GetBoxFormat(GetFormatIndex(nCol, nRow)).GetBackground()); + + rRenderContext.Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(aBrushItem.GetColor()); + const basegfx::B2DRange aCellRange(maArray.GetCellRange(nCol, nRow, true)); + rRenderContext.DrawRect(tools::Rectangle( + basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()), + basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY()))); + rRenderContext.Pop(); + } + } +} + +void AutoFormatPreview::PaintCells(vcl::RenderContext& rRenderContext) +{ + // 1) background + if (maCurrentData.IsBackground()) + DrawBackground(rRenderContext); + + // 2) values + for (size_t nRow = 0; nRow < 5; ++nRow) + for (size_t nCol = 0; nCol < 5; ++nCol) + DrawString(rRenderContext, nCol, nRow); + + // 3) border + if (maCurrentData.IsFrame()) + { + const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( + drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice( + rRenderContext, aNewViewInformation2D)); + + if (pProcessor2D) + { + pProcessor2D->process(maArray.CreateB2DPrimitiveArray()); + pProcessor2D.reset(); + } + } +} + +void AutoFormatPreview::Init() +{ + maArray.Initialize(5, 5); + mnLabelColumnWidth = 0; + mnDataColumnWidth1 = 0; + mnDataColumnWidth2 = 0; + mnRowHeight = 0; + CalcCellArray(false); + CalcLineMap(); +} + +void AutoFormatPreview::CalcCellArray(bool _bFitWidth) +{ + maArray.SetAllColWidths(_bFitWidth ? mnDataColumnWidth2 : mnDataColumnWidth1); + maArray.SetColWidth(0, mnLabelColumnWidth); + maArray.SetColWidth(4, mnLabelColumnWidth); + + maArray.SetAllRowHeights(mnRowHeight); + + maPreviousSize.setWidth(maArray.GetWidth() + 4); + maPreviousSize.setHeight(maArray.GetHeight() + 4); +} + +static void lclSetStyleFromBorder(svx::frame::Style& rStyle, + const ::editeng::SvxBorderLine* pBorder) +{ + rStyle.Set(pBorder, 0.05, 5); +} + +void AutoFormatPreview::CalcLineMap() +{ + for (size_t nRow = 0; nRow < 5; ++nRow) + { + for (size_t nCol = 0; nCol < 5; ++nCol) + { + svx::frame::Style aStyle; + + const SvxBoxItem& rItem + = maCurrentData.GetBoxFormat(GetFormatIndex(nCol, nRow)).GetBox(); + lclSetStyleFromBorder(aStyle, rItem.GetLeft()); + maArray.SetCellStyleLeft(nCol, nRow, aStyle); + lclSetStyleFromBorder(aStyle, rItem.GetRight()); + maArray.SetCellStyleRight(nCol, nRow, aStyle); + lclSetStyleFromBorder(aStyle, rItem.GetTop()); + maArray.SetCellStyleTop(nCol, nRow, aStyle); + lclSetStyleFromBorder(aStyle, rItem.GetBottom()); + maArray.SetCellStyleBottom(nCol, nRow, aStyle); + + // FIXME - uncomment to draw diagonal borders + // lclSetStyleFromBorder( aStyle, GetDiagItem( nCol, nRow, true ).GetLine() ); + // maArray.SetCellStyleTLBR( nCol, nRow, aStyle ); + // lclSetStyleFromBorder( aStyle, GetDiagItem( nCol, nRow, false ).GetLine() ); + // maArray.SetCellStyleBLTR( nCol, nRow, aStyle ); + } + } +} + +void AutoFormatPreview::NotifyChange(const SwTableAutoFormat& rNewData) +{ + maCurrentData = rNewData; + mbFitWidth = maCurrentData.IsJustify(); // true; //??? + CalcCellArray(mbFitWidth); + CalcLineMap(); + Invalidate(); +} + +void AutoFormatPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(PushFlags::ALL); + + DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode(); + if (rRenderContext.GetSettings().GetStyleSettings().GetHighContrastMode()) + rRenderContext.SetDrawMode(DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill + | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient); + + Size theWndSize = rRenderContext.GetOutputSizePixel(); + + vcl::Font aFont(rRenderContext.GetFont()); + aFont.SetTransparent(true); + + rRenderContext.SetFont(aFont); + rRenderContext.SetLineColor(); + const Color& rWinColor = rRenderContext.GetSettings().GetStyleSettings().GetWindowColor(); + rRenderContext.SetBackground(Wallpaper(rWinColor)); + rRenderContext.SetFillColor(rWinColor); + + // Draw the Frame + Color oldColor = rRenderContext.GetLineColor(); + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), theWndSize)); + rRenderContext.SetLineColor(oldColor); + + // Center the preview + maArray.SetXOffset(2 + (theWndSize.Width() - maPreviousSize.Width()) / 2); + maArray.SetYOffset(2 + (theWndSize.Height() - maPreviousSize.Height()) / 2); + // Draw cells on virtual device + PaintCells(rRenderContext); + + rRenderContext.SetDrawMode(nOldDrawMode); + rRenderContext.Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/colwd.cxx b/sw/source/ui/table/colwd.cxx new file mode 100644 index 000000000..435b4ee10 --- /dev/null +++ b/sw/source/ui/table/colwd.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 <svtools/unitconv.hxx> +#include <colwd.hxx> +#include <tablemgr.hxx> +#include <wrtsh.hxx> +#include <wdocsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <usrpref.hxx> + +IMPL_LINK_NOARG(SwTableWidthDlg, LoseFocusHdl, weld::SpinButton&, void) +{ + sal_uInt16 nId = static_cast<sal_uInt16>(m_xColNF->get_value()) - 1; + const SwTwips lWidth = m_rFnc.GetColWidth(nId); + m_xWidthMF->set_max(m_xWidthMF->normalize(m_rFnc.GetMaxColWidth(nId)), FieldUnit::TWIP); + m_xWidthMF->set_value(m_xWidthMF->normalize(lWidth), FieldUnit::TWIP); +} + +SwTableWidthDlg::SwTableWidthDlg(weld::Window *pParent, SwTableFUNC &rTableFnc) + : GenericDialogController(pParent, "modules/swriter/ui/columnwidth.ui", "ColumnWidthDialog") + , m_rFnc(rTableFnc) + , m_xColNF(m_xBuilder->weld_spin_button("column")) + , m_xWidthMF(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM)) +{ + bool bIsWeb = rTableFnc.GetShell() + && (dynamic_cast< const SwWebDocShell* >( + rTableFnc.GetShell()->GetView().GetDocShell()) != nullptr ); + FieldUnit eFieldUnit = SW_MOD()->GetUsrPref( bIsWeb )->GetMetric(); + ::SetFieldUnit(*m_xWidthMF, eFieldUnit); + + m_xColNF->set_max(m_rFnc.GetColCount() + 1); + m_xColNF->set_value(m_rFnc.GetCurColNum() + 1); + + if (m_rFnc.GetColCount() == 0) + m_xWidthMF->set_min(m_xWidthMF->normalize(m_rFnc.GetColWidth(0)), FieldUnit::TWIP); + else + m_xWidthMF->set_min(m_xWidthMF->normalize(MINLAY), FieldUnit::TWIP); + m_xColNF->connect_value_changed(LINK(this, SwTableWidthDlg, LoseFocusHdl)); + LoseFocusHdl(*m_xColNF); +} + +void SwTableWidthDlg::Apply() +{ + m_rFnc.InitTabCols(); + m_rFnc.SetColWidth(static_cast<sal_uInt16>(m_xColNF->get_value() - 1), + static_cast<sal_uInt16>(m_xWidthMF->denormalize(m_xWidthMF->get_value(FieldUnit::TWIP)))); +} + +short SwTableWidthDlg::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/convert.cxx b/sw/source/ui/table/convert.cxx new file mode 100644 index 000000000..fe49843b5 --- /dev/null +++ b/sw/source/ui/table/convert.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 <modcfg.hxx> +#include <sfx2/htmlmode.hxx> +#include <viewopt.hxx> +#include <swmodule.hxx> +#include <convert.hxx> +#include <tablemgr.hxx> +#include <view.hxx> +#include <tblafmt.hxx> + +#include <strings.hrc> +#include <swabstdlg.hxx> +#include <swuiexp.hxx> +#include <memory> + +//keep the state of the buttons on runtime +static int nSaveButtonState = -1; // 0: tab, 1: semicolon, 2: paragraph, 3: other, -1: not yet used +static bool bIsKeepColumn = true; +static sal_Unicode uOther = ','; + +void SwConvertTableDlg::GetValues( sal_Unicode& rDelim, + SwInsertTableOptions& rInsTableOpts, + SwTableAutoFormat const*& prTAFormat ) +{ + if (m_xTabBtn->get_active()) + { + //0x0b mustn't be set when re-converting table into text + bIsKeepColumn = !m_xKeepColumn->get_visible() || m_xKeepColumn->get_active(); + rDelim = bIsKeepColumn ? 0x09 : 0x0b; + nSaveButtonState = 0; + } + else if (m_xSemiBtn->get_active()) + { + rDelim = ';'; + nSaveButtonState = 1; + } + else if (m_xOtherBtn->get_active() && !m_xOtherEd->get_text().isEmpty()) + { + uOther = m_xOtherEd->get_text()[0]; + rDelim = uOther; + nSaveButtonState = 3; + } + else + { + nSaveButtonState = 2; + rDelim = cParaDelim; + if (m_xOtherBtn->get_active()) + { + nSaveButtonState = 3; + uOther = 0; + } + } + + SwInsertTableFlags nInsMode = SwInsertTableFlags::NONE; + if (m_xHeaderCB->get_active()) + nInsMode |= SwInsertTableFlags::Headline; + if (m_xRepeatHeaderCB->get_sensitive() && m_xRepeatHeaderCB->get_active()) + rInsTableOpts.mnRowsToRepeat = m_xRepeatHeaderNF->get_value(); + else + rInsTableOpts.mnRowsToRepeat = 0; + if (!m_xDontSplitCB->get_active()) + nInsMode |= SwInsertTableFlags::SplitLayout; + + if (mxTAutoFormat) + prTAFormat = new SwTableAutoFormat(*mxTAutoFormat); + + rInsTableOpts.mnInsMode = nInsMode; +} + +SwConvertTableDlg::SwConvertTableDlg(SwView& rView, bool bToTable) + : SfxDialogController(rView.GetFrameWeld(), + "modules/swriter/ui/converttexttable.ui", "ConvertTextTableDialog") + , m_xTabBtn(m_xBuilder->weld_radio_button("tabs")) + , m_xSemiBtn(m_xBuilder->weld_radio_button("semicolons")) + , m_xParaBtn(m_xBuilder->weld_radio_button("paragraph")) + , m_xOtherBtn(m_xBuilder->weld_radio_button("other")) + , m_xOtherEd(m_xBuilder->weld_entry("othered")) + , m_xKeepColumn(m_xBuilder->weld_check_button("keepcolumn")) + , m_xOptions(m_xBuilder->weld_container("options")) + , m_xHeaderCB(m_xBuilder->weld_check_button("headingcb")) + , m_xRepeatHeaderCB(m_xBuilder->weld_check_button("repeatheading")) + , m_xRepeatRows(m_xBuilder->weld_container("repeatrows")) + , m_xRepeatHeaderNF(m_xBuilder->weld_spin_button("repeatheadersb")) + , m_xDontSplitCB(m_xBuilder->weld_check_button("dontsplitcb")) + , m_xAutoFormatBtn(m_xBuilder->weld_button("autofmt")) + , pShell(&rView.GetWrtShell()) +{ + if (nSaveButtonState > -1) + { + switch (nSaveButtonState) + { + case 0: + m_xTabBtn->set_active(true); + m_xKeepColumn->set_active(bIsKeepColumn); + break; + case 1: + m_xSemiBtn->set_active(true); + break; + case 2: + m_xParaBtn->set_active(true); + break; + case 3: + m_xOtherBtn->set_active(true); + if (uOther) + m_xOtherEd->set_text(OUString(uOther)); + break; + } + + } + if( bToTable ) + { + m_xDialog->set_title(SwResId(STR_CONVERT_TEXT_TABLE)); + m_xAutoFormatBtn->connect_clicked(LINK(this, SwConvertTableDlg, AutoFormatHdl)); + m_xAutoFormatBtn->show(); + m_xKeepColumn->show(); + m_xKeepColumn->set_sensitive(m_xTabBtn->get_active()); + } + else + { + //hide insert options + m_xOptions->hide(); + } + m_xKeepColumn->save_state(); + + Link<weld::Button&,void> aLk( LINK(this, SwConvertTableDlg, BtnHdl) ); + m_xTabBtn->connect_clicked(aLk); + m_xSemiBtn->connect_clicked(aLk); + m_xParaBtn->connect_clicked(aLk); + m_xOtherBtn->connect_clicked(aLk); + m_xOtherEd->set_sensitive(m_xOtherBtn->get_active()); + + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + bool bHTMLMode = 0 != (::GetHtmlMode(rView.GetDocShell())&HTMLMODE_ON); + + SwInsertTableOptions aInsOpts = pModOpt->GetInsTableFlags(bHTMLMode); + SwInsertTableFlags nInsTableFlags = aInsOpts.mnInsMode; + + m_xHeaderCB->set_active(bool(nInsTableFlags & SwInsertTableFlags::Headline)); + m_xRepeatHeaderCB->set_active(aInsOpts.mnRowsToRepeat > 0); + m_xDontSplitCB->set_active(!(nInsTableFlags & SwInsertTableFlags::SplitLayout)); + + m_xHeaderCB->connect_clicked(LINK(this, SwConvertTableDlg, CheckBoxHdl)); + m_xRepeatHeaderCB->connect_clicked(LINK(this, SwConvertTableDlg, ReapeatHeaderCheckBoxHdl)); + ReapeatHeaderCheckBoxHdl(*m_xRepeatHeaderCB); + CheckBoxHdl(*m_xHeaderCB); +} + +IMPL_LINK_NOARG(SwConvertTableDlg, AutoFormatHdl, weld::Button&, void) +{ + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<AbstractSwAutoFormatDlg> pDlg(rFact.CreateSwAutoFormatDlg(m_xDialog.get(), pShell, false, mxTAutoFormat.get())); + if (RET_OK == pDlg->Execute()) + mxTAutoFormat = pDlg->FillAutoFormatOfIndex(); +} + +IMPL_LINK(SwConvertTableDlg, BtnHdl, weld::Button&, rButton, void) +{ + if (&rButton == m_xTabBtn.get()) + m_xKeepColumn->set_state(m_xKeepColumn->get_saved_state()); + else + { + if (m_xKeepColumn->get_sensitive()) + m_xKeepColumn->save_state(); + m_xKeepColumn->set_active(true); + } + m_xKeepColumn->set_sensitive(m_xTabBtn->get_active()); + m_xOtherEd->set_sensitive(m_xOtherBtn->get_active()); +} + +IMPL_LINK_NOARG(SwConvertTableDlg, CheckBoxHdl, weld::Button&, void) +{ + m_xRepeatHeaderCB->set_sensitive(m_xHeaderCB->get_active()); + ReapeatHeaderCheckBoxHdl(*m_xRepeatHeaderCB); +} + +IMPL_LINK_NOARG(SwConvertTableDlg, ReapeatHeaderCheckBoxHdl, weld::Button&, void) +{ + bool bEnable = m_xHeaderCB->get_active() && m_xRepeatHeaderCB->get_active(); + m_xRepeatRows->set_sensitive(bEnable); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/instable.cxx b/sw/source/ui/table/instable.cxx new file mode 100644 index 000000000..3ca34f37b --- /dev/null +++ b/sw/source/ui/table/instable.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 <instable.hxx> +#include <shellres.hxx> +#include <modcfg.hxx> +#include <swmodule.hxx> +#include <sfx2/htmlmode.hxx> +#include <viewopt.hxx> +#include <comphelper/lok.hxx> + +#define ROW_COL_PROD 16384 + +void SwInsTableDlg::GetValues( OUString& rName, sal_uInt16& rRow, sal_uInt16& rCol, + SwInsertTableOptions& rInsTableOpts, OUString& rAutoName, + std::unique_ptr<SwTableAutoFormat>& prTAFormat ) +{ + SwInsertTableFlags nInsMode = SwInsertTableFlags::NONE; + rName = m_xNameEdit->get_text(); + rRow = m_xRowNF->get_value(); + rCol = m_xColNF->get_value(); + + if (m_xHeaderCB->get_active()) + nInsMode |= SwInsertTableFlags::Headline; + if (m_xRepeatHeaderCB->get_sensitive() && m_xRepeatHeaderCB->get_active()) + rInsTableOpts.mnRowsToRepeat = m_xRepeatHeaderNF->get_value(); + else + rInsTableOpts.mnRowsToRepeat = 0; + if (!m_xDontSplitCB->get_active()) + nInsMode |= SwInsertTableFlags::SplitLayout; + if( pTAutoFormat ) + { + prTAFormat.reset(new SwTableAutoFormat( *pTAutoFormat )); + rAutoName = prTAFormat->GetName(); + } + + rInsTableOpts.mnInsMode = nInsMode; +} + +IMPL_LINK(SwInsTableDlg, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aTextFilter.filter(rTest); + return true; +} + +SwInsTableDlg::SwInsTableDlg(SwView& rView) + : SfxDialogController(rView.GetFrameWeld(), "modules/swriter/ui/inserttable.ui", "InsertTableDialog") + , m_aTextFilter(" .<>") + , pShell(&rView.GetWrtShell()) + , pTAutoFormat(nullptr) + , nEnteredValRepeatHeaderNF(-1) + , m_xNameEdit(m_xBuilder->weld_entry("nameedit")) + , m_xColNF(m_xBuilder->weld_spin_button("colspin")) + , m_xRowNF(m_xBuilder->weld_spin_button("rowspin")) + , m_xHeaderCB(m_xBuilder->weld_check_button("headercb")) + , m_xRepeatHeaderCB(m_xBuilder->weld_check_button("repeatcb")) + , m_xRepeatHeaderNF(m_xBuilder->weld_spin_button("repeatheaderspin")) + , m_xRepeatGroup(m_xBuilder->weld_widget("repeatgroup")) + , m_xDontSplitCB(m_xBuilder->weld_check_button("dontsplitcb")) + , m_xInsertBtn(m_xBuilder->weld_button("ok")) + , m_xLbFormat(m_xBuilder->weld_tree_view("formatlbinstable")) + , m_xWndPreview(new weld::CustomWeld(*m_xBuilder, "previewinstable", m_aWndPreview)) + , m_xStyleFrame(m_xBuilder->weld_frame("stylesframe")) +{ + if (comphelper::LibreOfficeKit::isActive()) + m_xStyleFrame->hide(); + + const int nWidth = m_xLbFormat->get_approximate_digit_width() * 32; + const int nHeight = m_xLbFormat->get_height_rows(8); + m_xLbFormat->set_size_request(nWidth, nHeight); + m_xWndPreview->set_size_request(nWidth, nHeight); + + m_xNameEdit->connect_insert_text(LINK(this, SwInsTableDlg, TextFilterHdl)); + m_xNameEdit->set_text(pShell->GetUniqueTableName()); + m_xNameEdit->connect_changed(LINK(this, SwInsTableDlg, ModifyName)); + m_xColNF->connect_value_changed(LINK(this, SwInsTableDlg, ModifyRowCol)); + m_xRowNF->connect_value_changed(LINK(this, SwInsTableDlg, ModifyRowCol)); + + m_xRowNF->set_max(ROW_COL_PROD/m_xColNF->get_value()); + m_xColNF->set_max(ROW_COL_PROD/m_xRowNF->get_value()); + + m_xInsertBtn->connect_clicked(LINK(this, SwInsTableDlg, OKHdl)); + + bool bHTMLMode = 0 != (::GetHtmlMode(rView.GetDocShell())&HTMLMODE_ON); + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + SwInsertTableOptions aInsOpts = pModOpt->GetInsTableFlags(bHTMLMode); + SwInsertTableFlags nInsTableFlags = aInsOpts.mnInsMode; + + m_xHeaderCB->set_active(bool(nInsTableFlags & SwInsertTableFlags::Headline)); + m_xRepeatHeaderCB->set_active(aInsOpts.mnRowsToRepeat > 0); + if (bHTMLMode) + m_xDontSplitCB->hide(); + else + m_xDontSplitCB->set_active(!(nInsTableFlags & SwInsertTableFlags::SplitLayout)); + + m_xRepeatHeaderNF->connect_value_changed( LINK( this, SwInsTableDlg, ModifyRepeatHeaderNF_Hdl ) ); + m_xHeaderCB->connect_toggled( LINK( this, SwInsTableDlg, CheckBoxHdl ) ); + m_xRepeatHeaderCB->connect_toggled( LINK( this, SwInsTableDlg, RepeatHeaderCheckBoxHdl ) ); + RepeatHeaderCheckBoxHdl(*m_xRepeatHeaderCB); + CheckBoxHdl(*m_xHeaderCB); + + sal_Int64 nMax = m_xRowNF->get_value(); + if( nMax <= 1 ) + nMax = 1; + else + --nMax; + m_xRepeatHeaderNF->set_max( nMax ); + + InitAutoTableFormat(); +} + +void SwInsTableDlg::InitAutoTableFormat() +{ + m_aWndPreview.DetectRTL(pShell); + + m_xLbFormat->connect_changed(LINK(this, SwInsTableDlg, SelFormatHdl)); + + pTableTable = new SwTableAutoFormatTable; + pTableTable->Load(); + + // Add "- none -" style autoformat table. + m_xLbFormat->append_text(SwViewShell::GetShellRes()->aStrNone); // Insert to listbox + + // Add other styles of autoformat tables. + for (sal_uInt8 i = 0, nCount = static_cast<sal_uInt8>(pTableTable->size()); + i < nCount; i++) + { + SwTableAutoFormat const& rFormat = (*pTableTable)[ i ]; + m_xLbFormat->append_text(rFormat.GetName()); + if (pTAutoFormat && rFormat.GetName() == pTAutoFormat->GetName()) + lbIndex = i; + } + + // Change this min variable if you add autotable manually. + minTableIndexInLb = 1; + maxTableIndexInLb = minTableIndexInLb + static_cast<sal_uInt8>(pTableTable->size()); + lbIndex = 0; + m_xLbFormat->select( lbIndex ); + tbIndex = lbIndexToTableIndex(lbIndex); + + SelFormatHdl( *m_xLbFormat ); +} + +sal_uInt8 SwInsTableDlg::lbIndexToTableIndex( const sal_uInt8 listboxIndex ) +{ + if( minTableIndexInLb != maxTableIndexInLb && + minTableIndexInLb <= listboxIndex && + listboxIndex < maxTableIndexInLb ) + { + return listboxIndex - minTableIndexInLb; + } + + return 255; +} + +static void lcl_SetProperties( SwTableAutoFormat* pTableAutoFormat, bool bVal ) +{ + pTableAutoFormat->SetFont( bVal ); + pTableAutoFormat->SetJustify( bVal ); + pTableAutoFormat->SetFrame( bVal ); + pTableAutoFormat->SetBackground( bVal ); + pTableAutoFormat->SetValueFormat( bVal ); + pTableAutoFormat->SetWidthHeight( bVal ); +} + +IMPL_LINK_NOARG(SwInsTableDlg, SelFormatHdl, weld::TreeView&, void) +{ + // Get index of selected item from the listbox + lbIndex = static_cast<sal_uInt8>(m_xLbFormat->get_selected_index()); + tbIndex = lbIndexToTableIndex( lbIndex ); + + // To understand this index mapping, look InitAutoTableFormat function to + // see how listbox item is implemented. + if( tbIndex < 255 ) + m_aWndPreview.NotifyChange( (*pTableTable)[tbIndex] ); + else + { + SwTableAutoFormat aTmp( SwViewShell::GetShellRes()->aStrNone ); + lcl_SetProperties( &aTmp, false ); + + m_aWndPreview.NotifyChange( aTmp ); + } +} + +IMPL_LINK_NOARG(SwInsTableDlg, OKHdl, weld::Button&, void) +{ + if( tbIndex < 255 ) + pShell->SetTableStyle((*pTableTable)[tbIndex]); + + if( tbIndex < 255 ) + { + if( pTAutoFormat ) + *pTAutoFormat = (*pTableTable)[ tbIndex ]; + else + pTAutoFormat = new SwTableAutoFormat( (*pTableTable)[ tbIndex ] ); + } + else + { + delete pTAutoFormat; + pTAutoFormat = new SwTableAutoFormat( SwViewShell::GetShellRes()->aStrNone ); + lcl_SetProperties( pTAutoFormat, false ); + } + + m_xDialog->response(RET_OK); +} + +IMPL_LINK( SwInsTableDlg, ModifyName, weld::Entry&, rEdit, void ) +{ + OUString sTableName = rEdit.get_text(); + m_xInsertBtn->set_sensitive(pShell->GetTableStyle(sTableName) == nullptr); +} + +IMPL_LINK( SwInsTableDlg, ModifyRowCol, weld::SpinButton&, rEdit, void ) +{ + if(&rEdit == m_xColNF.get()) + { + sal_Int64 nCol = m_xColNF->get_value(); + if(!nCol) + nCol = 1; + m_xRowNF->set_max(ROW_COL_PROD/nCol); + } + else + { + sal_Int64 nRow = m_xRowNF->get_value(); + if(!nRow) + nRow = 1; + m_xColNF->set_max(ROW_COL_PROD/nRow); + + // adjust depending NF for repeated rows + sal_Int64 nMax = ( nRow == 1 )? 1 : nRow - 1 ; + sal_Int64 nActVal = m_xRepeatHeaderNF->get_value(); + + m_xRepeatHeaderNF->set_max( nMax ); + + if( nActVal > nMax ) + m_xRepeatHeaderNF->set_value( nMax ); + else if( nActVal < nEnteredValRepeatHeaderNF ) + m_xRepeatHeaderNF->set_value(std::min(nEnteredValRepeatHeaderNF, nMax)); + } +} + +IMPL_LINK_NOARG(SwInsTableDlg, CheckBoxHdl, weld::ToggleButton&, void) +{ + m_xRepeatHeaderCB->set_sensitive(m_xHeaderCB->get_active()); + RepeatHeaderCheckBoxHdl(*m_xRepeatHeaderCB); +} + +IMPL_LINK_NOARG(SwInsTableDlg, RepeatHeaderCheckBoxHdl, weld::ToggleButton&, void) +{ + m_xRepeatGroup->set_sensitive(m_xHeaderCB->get_active() && m_xRepeatHeaderCB->get_active()); +} + +IMPL_LINK_NOARG(SwInsTableDlg, ModifyRepeatHeaderNF_Hdl, weld::SpinButton&, void) +{ + nEnteredValRepeatHeaderNF = m_xRepeatHeaderNF->get_value(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/mergetbl.cxx b/sw/source/ui/table/mergetbl.cxx new file mode 100644 index 000000000..571370914 --- /dev/null +++ b/sw/source/ui/table/mergetbl.cxx @@ -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 . + */ + +#include <mergetbl.hxx> + +SwMergeTableDlg::SwMergeTableDlg(weld::Window *pParent, bool& rWithPrev) + : GenericDialogController(pParent, "modules/swriter/ui/mergetabledialog.ui", "MergeTableDialog") + , m_rMergePrev(rWithPrev) + , m_xMergePrevRB(m_xBuilder->weld_radio_button("prev")) +{ + m_xMergePrevRB->set_active(true); +} + +void SwMergeTableDlg::Apply() +{ + m_rMergePrev = m_xMergePrevRB->get_active(); +} + +short SwMergeTableDlg::run() +{ + int nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/rowht.cxx b/sw/source/ui/table/rowht.cxx new file mode 100644 index 000000000..f46297bf7 --- /dev/null +++ b/sw/source/ui/table/rowht.cxx @@ -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 . + */ + +#include <svtools/unitconv.hxx> + +#include <fmtfsize.hxx> +#include <swtypes.hxx> +#include <rowht.hxx> +#include <wrtsh.hxx> +#include <wdocsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <usrpref.hxx> + +void SwTableHeightDlg::Apply() +{ + SwTwips nHeight = static_cast< SwTwips >(m_xHeightEdit->denormalize(m_xHeightEdit->get_value(FieldUnit::TWIP))); + SwFormatFrameSize aSz(SwFrameSize::Fixed, 0, nHeight); + + SwFrameSize eFrameSize = m_xAutoHeightCB->get_active() ? SwFrameSize::Minimum : SwFrameSize::Fixed; + if(eFrameSize != aSz.GetHeightSizeType()) + { + aSz.SetHeightSizeType(eFrameSize); + } + m_rSh.SetRowHeight(aSz); +} + +SwTableHeightDlg::SwTableHeightDlg(weld::Window *pParent, SwWrtShell &rS) + : GenericDialogController(pParent, "modules/swriter/ui/rowheight.ui", "RowHeightDialog") + , m_rSh(rS) + , m_xHeightEdit(m_xBuilder->weld_metric_spin_button("heightmf", FieldUnit::CM)) + , m_xAutoHeightCB(m_xBuilder->weld_check_button("fit")) +{ + FieldUnit eFieldUnit = SW_MOD()->GetUsrPref( dynamic_cast< const SwWebDocShell*>( + m_rSh.GetView().GetDocShell() ) != nullptr )->GetMetric(); + ::SetFieldUnit(*m_xHeightEdit, eFieldUnit); + + m_xHeightEdit->set_min(MINLAY, FieldUnit::TWIP); + std::unique_ptr<SwFormatFrameSize> pSz = m_rSh.GetRowHeight(); + if (pSz) + { + auto nHeight = pSz->GetHeight(); + m_xAutoHeightCB->set_active(pSz->GetHeightSizeType() != SwFrameSize::Fixed); + m_xHeightEdit->set_value(m_xHeightEdit->normalize(nHeight), FieldUnit::TWIP); + } +} + +short SwTableHeightDlg::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/splittbl.cxx b/sw/source/ui/table/splittbl.cxx new file mode 100644 index 000000000..e322bd846 --- /dev/null +++ b/sw/source/ui/table/splittbl.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 <wrtsh.hxx> +#include <splittbl.hxx> +#include <tblenum.hxx> + +SwSplitTableDlg::SwSplitTableDlg(weld::Window *pParent, SwWrtShell &rSh) + : GenericDialogController(pParent, "modules/swriter/ui/splittable.ui", "SplitTableDialog") + , m_xContentCopyRB(m_xBuilder->weld_radio_button("copyheading")) + , m_xBoxAttrCopyWithParaRB(m_xBuilder->weld_radio_button("customheadingapplystyle")) + , m_xBoxAttrCopyNoParaRB(m_xBuilder->weld_radio_button("customheading")) + , m_xBorderCopyRB(m_xBuilder->weld_radio_button("noheading")) + , rShell(rSh) + , m_nSplit(SplitTable_HeadlineOption::ContentCopy) +{ +} + +void SwSplitTableDlg::Apply() +{ + m_nSplit = SplitTable_HeadlineOption::ContentCopy; + if (m_xBoxAttrCopyWithParaRB->get_active()) + m_nSplit = SplitTable_HeadlineOption::BoxAttrAllCopy; + else if (m_xBoxAttrCopyNoParaRB->get_active()) + m_nSplit = SplitTable_HeadlineOption::BoxAttrCopy; + else if (m_xBorderCopyRB->get_active()) + m_nSplit = SplitTable_HeadlineOption::BorderCopy; + + rShell.SplitTable(m_nSplit); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/tabledlg.cxx b/sw/source/ui/table/tabledlg.cxx new file mode 100644 index 000000000..9844f96a2 --- /dev/null +++ b/sw/source/ui/table/tabledlg.cxx @@ -0,0 +1,1737 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/weld.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svl/ctloptions.hxx> +#include <swmodule.hxx> +#include <fmtpdsc.hxx> +#include <fmtlsplt.hxx> + +#include <svtools/htmlcfg.hxx> +#include <fmtrowsplt.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/sfxdlg.hxx> + +#include <strings.hrc> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <uitool.hxx> +#include <tabledlg.hxx> +#include "../../uibase/table/tablepg.hxx" +#include <tablemgr.hxx> +#include <pagedesc.hxx> +#include <uiitems.hxx> +#include <poolfmt.hxx> +#include <swtablerep.hxx> +#include <SwStyleNameMapper.hxx> + +#include <cmdid.h> +#include <svx/dialogs.hrc> +#include <svx/flagsdef.hxx> + +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> + +using namespace ::com::sun::star; + +SwFormatTablePage::SwFormatTablePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/formattablepage.ui", "FormatTablePage", &rSet) + , pTableData(nullptr) + , nSaveWidth(0) + , nMinTableWidth(MINLAY) + , bModified(false) + , bFull(false) + , bHtmlMode(false) + , m_xNameED(m_xBuilder->weld_entry("name")) + , m_xWidthFT(m_xBuilder->weld_label("widthft")) + , m_xWidthMF(new SwPercentField(m_xBuilder->weld_metric_spin_button("widthmf", FieldUnit::CM))) + , m_xRelWidthCB(m_xBuilder->weld_check_button("relwidth")) + , m_xFullBtn(m_xBuilder->weld_radio_button("full")) + , m_xLeftBtn(m_xBuilder->weld_radio_button("left")) + , m_xFromLeftBtn(m_xBuilder->weld_radio_button("fromleft")) + , m_xRightBtn(m_xBuilder->weld_radio_button("right")) + , m_xCenterBtn(m_xBuilder->weld_radio_button("center")) + , m_xFreeBtn(m_xBuilder->weld_radio_button("free")) + , m_xLeftFT(m_xBuilder->weld_label("leftft")) + , m_xLeftMF(new SwPercentField(m_xBuilder->weld_metric_spin_button("leftmf", FieldUnit::CM))) + , m_xRightFT(m_xBuilder->weld_label("rightft")) + , m_xRightMF(new SwPercentField(m_xBuilder->weld_metric_spin_button("rightmf", FieldUnit::CM))) + , m_xTopFT(m_xBuilder->weld_label("aboveft")) + , m_xTopMF(m_xBuilder->weld_metric_spin_button("abovemf", FieldUnit::CM)) + , m_xBottomFT(m_xBuilder->weld_label("belowft")) + , m_xBottomMF(m_xBuilder->weld_metric_spin_button("belowmf", FieldUnit::CM)) + , m_xTextDirectionLB(new svx::FrameDirectionListBox(m_xBuilder->weld_combo_box("textdirection"))) + , m_xProperties(m_xBuilder->weld_widget("properties")) +{ + //lock these to initial sizes so they don't change on percent to non percent change + Size aPrefSize(m_xLeftMF->get()->get_preferred_size()); + m_xLeftMF->get()->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + m_xRightMF->get()->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + m_xWidthMF->get()->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + + m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_LR_TB, SvxResId(RID_SVXSTR_FRAMEDIR_LTR)); + m_xTextDirectionLB->append(SvxFrameDirection::Horizontal_RL_TB, SvxResId(RID_SVXSTR_FRAMEDIR_RTL)); + m_xTextDirectionLB->append(SvxFrameDirection::Environment, SvxResId(RID_SVXSTR_FRAMEDIR_SUPER)); + + SetExchangeSupport(); + + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet.GetItemState(SID_HTML_MODE, false, &pItem)) + bHtmlMode = 0 != (static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON); + + bool bCTL = SW_MOD()->GetCTLOptions().IsCTLFontEnabled(); + m_xProperties->set_visible(!bHtmlMode && bCTL); + + Init(); +} + +SwFormatTablePage::~SwFormatTablePage() +{ +} + +void SwFormatTablePage::Init() +{ + m_xLeftMF->SetMetricFieldMin(-999999); + m_xRightMF->SetMetricFieldMin(-999999); + + //handler + Link<weld::ToggleButton&,void> aLk2 = LINK( this, SwFormatTablePage, AutoClickHdl ); + m_xFullBtn->connect_toggled( aLk2 ); + m_xFreeBtn->connect_toggled( aLk2 ); + m_xLeftBtn->connect_toggled( aLk2 ); + m_xFromLeftBtn->connect_toggled( aLk2 ); + m_xRightBtn->connect_toggled( aLk2 ); + m_xCenterBtn->connect_toggled( aLk2 ); + + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwFormatTablePage, ValueChangedHdl); + m_xTopMF->connect_value_changed(aLk); + m_xBottomMF->connect_value_changed(aLk); + m_xRightMF->connect_value_changed(aLk); + m_xLeftMF->connect_value_changed(aLk); + m_xWidthMF->connect_value_changed(aLk); + + m_xRelWidthCB->connect_toggled(LINK( this, SwFormatTablePage, RelWidthClickHdl )); +} + +IMPL_LINK( SwFormatTablePage, RelWidthClickHdl, weld::ToggleButton&, rBtn, void ) +{ + OSL_ENSURE(pTableData, "table data not available?"); + bool bIsChecked = rBtn.get_active(); + sal_Int64 nLeft = m_xLeftMF->DenormalizePercent(m_xLeftMF->get_value(FieldUnit::TWIP)); + sal_Int64 nRight = m_xRightMF->DenormalizePercent(m_xRightMF->get_value(FieldUnit::TWIP)); + m_xWidthMF->ShowPercent(bIsChecked); + m_xLeftMF->ShowPercent(bIsChecked); + m_xRightMF->ShowPercent(bIsChecked); + + if (bIsChecked) + { + m_xWidthMF->SetRefValue(pTableData->GetSpace()); + m_xLeftMF->SetRefValue(pTableData->GetSpace()); + m_xRightMF->SetRefValue(pTableData->GetSpace()); + m_xLeftMF->SetMetricFieldMin(0); //will be overwritten by the Percentfield + m_xRightMF->SetMetricFieldMin(0); //dito + m_xLeftMF->SetMetricFieldMax(99); + m_xRightMF->SetMetricFieldMax(99); + m_xLeftMF->set_value(m_xLeftMF->NormalizePercent(nLeft ), FieldUnit::TWIP ); + m_xRightMF->set_value(m_xRightMF->NormalizePercent(nRight ), FieldUnit::TWIP ); + } + else + ModifyHdl(*m_xLeftMF->get()); //correct values again + + if (m_xFreeBtn->get_active()) + { + bool bEnable = !rBtn.get_active(); + m_xRightMF->set_sensitive(bEnable); + m_xRightFT->set_sensitive(bEnable); + } + bModified = true; +} + +IMPL_LINK_NOARG(SwFormatTablePage, AutoClickHdl, weld::ToggleButton&, void) +{ + bool bRestore = true, + bLeftEnable = false, + bRightEnable= false, + bWidthEnable= false, + bOthers = true; + if (m_xFullBtn->get_active()) + { + m_xLeftMF->set_value(0); + m_xRightMF->set_value(0); + nSaveWidth = static_cast<SwTwips>(m_xWidthMF->DenormalizePercent(m_xWidthMF->get_value(FieldUnit::TWIP))); + m_xWidthMF->set_value(m_xWidthMF->NormalizePercent(pTableData->GetSpace()), FieldUnit::TWIP); + bFull = true; + bRestore = false; + } + else if (m_xLeftBtn->get_active()) + { + bRightEnable = bWidthEnable = true; + m_xLeftMF->set_value(0); + } + else if (m_xFromLeftBtn->get_active()) + { + bLeftEnable = bWidthEnable = true; + m_xRightMF->set_value(0); + } + else if (m_xRightBtn->get_active()) + { + bLeftEnable = bWidthEnable = true; + m_xRightMF->set_value(0); + } + else if (m_xCenterBtn->get_active()) + { + bLeftEnable = bWidthEnable = true; + } + else if (m_xFreeBtn->get_active()) + { + RightModify(); + bLeftEnable = true; + bWidthEnable = true; + bOthers = false; + } + m_xLeftMF->set_sensitive(bLeftEnable); + m_xLeftFT->set_sensitive(bLeftEnable); + m_xWidthMF->set_sensitive(bWidthEnable); + m_xWidthFT->set_sensitive(bWidthEnable); + if ( bOthers ) + { + m_xRightMF->set_sensitive(bRightEnable); + m_xRightFT->set_sensitive(bRightEnable); + m_xRelWidthCB->set_sensitive(bWidthEnable); + } + + if(bFull && bRestore) + { + //After being switched on automatic, the width was pinned + //in order to restore the width while switching back to. + bFull = false; + m_xWidthMF->set_value(m_xWidthMF->NormalizePercent(nSaveWidth ), FieldUnit::TWIP ); + } + ModifyHdl(*m_xWidthMF->get()); + bModified = true; +} + +void SwFormatTablePage::RightModify() +{ + if (m_xFreeBtn->get_active()) + { + bool bEnable = m_xRightMF->get_value() == 0; + m_xRelWidthCB->set_sensitive(bEnable); + if ( !bEnable ) + { + m_xRelWidthCB->set_active(false); + RelWidthClickHdl(*m_xRelWidthCB); + } + bEnable = m_xRelWidthCB->get_active(); + m_xRightMF->set_sensitive(!bEnable); + m_xRightFT->set_sensitive(!bEnable); + } +} + +IMPL_LINK( SwFormatTablePage, ValueChangedHdl, weld::MetricSpinButton&, rEdit, void ) +{ + if (m_xRightMF->get() == &rEdit) + RightModify(); + ModifyHdl(rEdit); +} + +void SwFormatTablePage::ModifyHdl(const weld::MetricSpinButton& rEdit) +{ + SwTwips nCurWidth = static_cast< SwTwips >(m_xWidthMF->DenormalizePercent(m_xWidthMF->get_value(FieldUnit::TWIP))); + SwTwips nPrevWidth = nCurWidth; + SwTwips nRight = static_cast< SwTwips >(m_xRightMF->DenormalizePercent(m_xRightMF->get_value(FieldUnit::TWIP))); + SwTwips nLeft = static_cast< SwTwips >(m_xLeftMF->DenormalizePercent(m_xLeftMF->get_value(FieldUnit::TWIP))); + SwTwips nDiff; + + if (&rEdit == m_xWidthMF->get()) + { + if( nCurWidth < MINLAY ) + nCurWidth = MINLAY; + nDiff = nRight + nLeft + nCurWidth - pTableData->GetSpace() ; + //right aligned: only change the left margin + if (m_xRightBtn->get_active()) + nLeft -= nDiff; + //left aligned: only change the right margin + else if(m_xLeftBtn->get_active()) + nRight -= nDiff; + //left margin and width allowed - first right - then left + else if (m_xFromLeftBtn->get_active()) + { + if( nRight >= nDiff ) + nRight -= nDiff; + else + { + nDiff -= nRight; + nRight = 0; + if(nLeft >= nDiff) + nLeft -= nDiff; + else + { + nRight += nLeft - nDiff; + nLeft = 0; + nCurWidth = pTableData->GetSpace(); + } + + } + } + //centered: change both sides equally + else if (m_xCenterBtn->get_active()) + { + if(nLeft != nRight) + { + nDiff += nLeft + nRight; + nLeft = nDiff/2; + nRight = nDiff/2; + } + else + { + nLeft -= nDiff/2; + nRight -= nDiff/2; + } + } + //free alignment: decrease both margins + else if (m_xFreeBtn->get_active()) + { + nLeft -= nDiff/2; + nRight -= nDiff/2; + } + } + if (&rEdit == m_xRightMF->get()) + { + + if( nRight + nLeft > pTableData->GetSpace() - MINLAY ) + nRight = pTableData->GetSpace() -nLeft - MINLAY; + + nCurWidth = pTableData->GetSpace() - nLeft - nRight; + } + if (&rEdit == m_xLeftMF->get()) + { + if(!m_xFromLeftBtn->get_active()) + { + bool bCenter = m_xCenterBtn->get_active(); + if( bCenter ) + nRight = nLeft; + if(nRight + nLeft > pTableData->GetSpace() - MINLAY ) + { + nLeft = bCenter ? (pTableData->GetSpace() - MINLAY) /2 : + (pTableData->GetSpace() - MINLAY) - nRight; + nRight = bCenter ? (pTableData->GetSpace() - MINLAY) /2 : nRight; + } + nCurWidth = pTableData->GetSpace() - nLeft - nRight; + } + else + { + //Upon changes on the left side the right margin will be changed at first, + //thereafter the width. + nDiff = nRight + nLeft + nCurWidth - pTableData->GetSpace() ; + + nRight -= nDiff; + nCurWidth = pTableData->GetSpace() - nLeft - nRight; + } + } + if (nCurWidth != nPrevWidth ) + m_xWidthMF->set_value( m_xWidthMF->NormalizePercent( nCurWidth ), FieldUnit::TWIP ); + m_xRightMF->set_value( m_xRightMF->NormalizePercent( nRight ), FieldUnit::TWIP ); + m_xLeftMF->set_value( m_xLeftMF->NormalizePercent( nLeft ), FieldUnit::TWIP ); + bModified = true; +} + +std::unique_ptr<SfxTabPage> SwFormatTablePage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwFormatTablePage>(pPage, pController, *rAttrSet); +} + +bool SwFormatTablePage::FillItemSet( SfxItemSet* rCoreSet ) +{ + //Test if one of the controls still has the focus + if (m_xWidthMF->has_focus()) + ModifyHdl(*m_xWidthMF->get()); + else if (m_xLeftMF->has_focus()) + ModifyHdl(*m_xLeftMF->get()); + else if (m_xRightMF->has_focus()) + ModifyHdl(*m_xRightMF->get()); + else if (m_xTopMF->has_focus()) + ModifyHdl(*m_xTopMF); + else if (m_xBottomMF->has_focus()) + ModifyHdl(*m_xBottomMF); + + if (bModified) + { + if (m_xBottomMF->get_value_changed_from_saved() || + m_xTopMF->get_value_changed_from_saved() ) + { + SvxULSpaceItem aULSpace(RES_UL_SPACE); + aULSpace.SetUpper(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP))); + aULSpace.SetLower(m_xBottomMF->denormalize(m_xBottomMF->get_value(FieldUnit::TWIP))); + rCoreSet->Put(aULSpace); + } + + } + if (m_xNameED->get_value_changed_from_saved()) + { + rCoreSet->Put(SfxStringItem(FN_PARAM_TABLE_NAME, m_xNameED->get_text())); + bModified = true; + } + + if (m_xTextDirectionLB->get_visible()) + { + if (m_xTextDirectionLB->get_value_changed_from_saved()) + { + SvxFrameDirection nDirection = m_xTextDirectionLB->get_active_id(); + rCoreSet->Put(SvxFrameDirectionItem(nDirection, RES_FRAMEDIR)); + bModified = true; + } + } + + return bModified; +} + +void SwFormatTablePage::Reset( const SfxItemSet* ) +{ + const SfxItemSet& rSet = GetItemSet(); + const SfxPoolItem* pItem; + + if(bHtmlMode) + { + m_xNameED->set_sensitive(false); + m_xTopFT->hide(); + m_xTopMF->hide(); + m_xBottomFT->hide(); + m_xBottomMF->hide(); + m_xFreeBtn->set_sensitive(false); + } + FieldUnit aMetric = ::GetDfltMetric(bHtmlMode); + m_xWidthMF->SetMetric(aMetric); + m_xRightMF->SetMetric(aMetric); + m_xLeftMF->SetMetric(aMetric); + SetFieldUnit(*m_xTopMF, aMetric); + SetFieldUnit(*m_xBottomMF, aMetric); + + //Name + if(SfxItemState::SET == rSet.GetItemState( FN_PARAM_TABLE_NAME, false, &pItem )) + { + m_xNameED->set_text(static_cast<const SfxStringItem*>(pItem)->GetValue()); + m_xNameED->save_value(); + } + + if(SfxItemState::SET == rSet.GetItemState( FN_TABLE_REP, false, &pItem )) + { + pTableData = static_cast<SwTableRep*>(static_cast<const SwPtrItem*>( pItem)->GetValue()); + nMinTableWidth = pTableData->GetColCount() * MINLAY; + + if(pTableData->GetWidthPercent()) + { + m_xRelWidthCB->set_active(true); + RelWidthClickHdl(*m_xRelWidthCB); + m_xWidthMF->set_value(pTableData->GetWidthPercent(), FieldUnit::PERCENT); + + m_xWidthMF->save_value(); + nSaveWidth = static_cast< SwTwips >(m_xWidthMF->get_value(FieldUnit::PERCENT)); + } + else + { + m_xWidthMF->set_value(m_xWidthMF->NormalizePercent( + pTableData->GetWidth()), FieldUnit::TWIP); + m_xWidthMF->save_value(); + nSaveWidth = pTableData->GetWidth(); + nMinTableWidth = std::min( nSaveWidth, nMinTableWidth ); + } + + m_xWidthMF->SetRefValue(pTableData->GetSpace()); + + m_xLeftMF->set_value(m_xLeftMF->NormalizePercent( + pTableData->GetLeftSpace()), FieldUnit::TWIP); + m_xRightMF->set_value(m_xRightMF->NormalizePercent( + pTableData->GetRightSpace()), FieldUnit::TWIP); + m_xLeftMF->save_value(); + m_xRightMF->save_value(); + + bool bSetRight = false, bSetLeft = false; + switch( pTableData->GetAlign() ) + { + case text::HoriOrientation::NONE: + m_xFreeBtn->set_active(true); + if (m_xRelWidthCB->get_active()) + bSetRight = true; + break; + case text::HoriOrientation::FULL: + { + bSetRight = bSetLeft = true; + m_xFullBtn->set_active(true); + m_xWidthMF->set_sensitive(false); + m_xRelWidthCB->set_sensitive(false); + m_xWidthFT->set_sensitive(false); + } + break; + case text::HoriOrientation::LEFT: + { + bSetLeft = true; + m_xLeftBtn->set_active(true); + } + break; + case text::HoriOrientation::LEFT_AND_WIDTH : + { + bSetRight = true; + m_xFromLeftBtn->set_active(true); + } + break; + case text::HoriOrientation::RIGHT: + { + bSetRight = true; + m_xRightBtn->set_active(true); + } + break; + case text::HoriOrientation::CENTER: + { + bSetRight = true; + m_xCenterBtn->set_active(true); + } + break; + } + if ( bSetRight ) + { + m_xRightMF->set_sensitive(false); + m_xRightFT->set_sensitive(false); + } + if ( bSetLeft ) + { + m_xLeftMF->set_sensitive(false); + m_xLeftFT->set_sensitive(false); + } + + } + + //Margins + if(SfxItemState::SET == rSet.GetItemState( RES_UL_SPACE, false,&pItem )) + { + m_xTopMF->set_value(m_xTopMF->normalize( + static_cast<const SvxULSpaceItem*>(pItem)->GetUpper()), FieldUnit::TWIP); + m_xBottomMF->set_value(m_xBottomMF->normalize( + static_cast<const SvxULSpaceItem*>(pItem)->GetLower()), FieldUnit::TWIP); + m_xTopMF->save_value(); + m_xBottomMF->save_value(); + } + + //Text direction + if( SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, true, &pItem ) ) + { + SvxFrameDirection nVal = static_cast<const SvxFrameDirectionItem*>(pItem)->GetValue(); + m_xTextDirectionLB->set_active_id(nVal); + m_xTextDirectionLB->save_value(); + } + + m_xWidthMF->set_max( 2*m_xWidthMF->NormalizePercent( pTableData->GetSpace() ), FieldUnit::TWIP ); + m_xRightMF->set_max( m_xRightMF->NormalizePercent( pTableData->GetSpace() ), FieldUnit::TWIP ); + m_xLeftMF->set_max( m_xLeftMF->NormalizePercent( pTableData->GetSpace() ), FieldUnit::TWIP ); + m_xWidthMF->set_min( m_xWidthMF->NormalizePercent( nMinTableWidth ), FieldUnit::TWIP ); +} + +void SwFormatTablePage::ActivatePage( const SfxItemSet& rSet ) +{ + OSL_ENSURE(pTableData, "table data not available?"); + if(SfxItemState::SET == rSet.GetItemState( FN_TABLE_REP )) + { + SwTwips nCurWidth = text::HoriOrientation::FULL != pTableData->GetAlign() ? + pTableData->GetWidth() : + pTableData->GetSpace(); + if(pTableData->GetWidthPercent() == 0 && + nCurWidth != m_xWidthMF->DenormalizePercent(m_xWidthMF->get_value(FieldUnit::TWIP))) + { + m_xWidthMF->set_value(m_xWidthMF->NormalizePercent( + nCurWidth), FieldUnit::TWIP); + m_xWidthMF->save_value(); + nSaveWidth = nCurWidth; + m_xLeftMF->set_value(m_xLeftMF->NormalizePercent( + pTableData->GetLeftSpace()), FieldUnit::TWIP); + m_xLeftMF->save_value(); + m_xRightMF->set_value(m_xRightMF->NormalizePercent( + pTableData->GetRightSpace()), FieldUnit::TWIP); + m_xRightMF->save_value(); + } + } +} + +DeactivateRC SwFormatTablePage::DeactivatePage( SfxItemSet* _pSet ) +{ + //test the table name for spaces + OUString sTableName = m_xNameED->get_text(); + if(sTableName.indexOf(' ') != -1) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_WRONG_TABLENAME))); + xInfoBox->run(); + m_xNameED->grab_focus(); + return DeactivateRC::KeepPage; + } + if(_pSet) + { + FillItemSet(_pSet); + if(bModified) + { + SwTwips lLeft = static_cast< SwTwips >(m_xLeftMF->DenormalizePercent(m_xLeftMF->get_value(FieldUnit::TWIP))); + SwTwips lRight = static_cast< SwTwips >(m_xRightMF->DenormalizePercent(m_xRightMF->get_value(FieldUnit::TWIP))); + + if( m_xLeftMF->get_value_changed_from_saved() || + m_xRightMF->get_value_changed_from_saved() ) + { + pTableData->SetWidthChanged(); + pTableData->SetLeftSpace( lLeft); + pTableData->SetRightSpace( lRight); + } + + SwTwips lWidth; + if (m_xRelWidthCB->get_active() && m_xRelWidthCB->get_sensitive()) + { + lWidth = pTableData->GetSpace() - lRight - lLeft; + const sal_uInt16 nPercentWidth = m_xWidthMF->get_value(FieldUnit::PERCENT); + if(pTableData->GetWidthPercent() != nPercentWidth) + { + pTableData->SetWidthPercent(nPercentWidth); + pTableData->SetWidthChanged(); + } + } + else + { + pTableData->SetWidthPercent(0); + lWidth = static_cast<SwTwips>(m_xWidthMF->DenormalizePercent(m_xWidthMF->get_value(FieldUnit::TWIP))); + } + pTableData->SetWidth(lWidth); + + SwTwips nColSum = 0; + + for( sal_uInt16 i = 0; i < pTableData->GetColCount(); i++) + { + nColSum += pTableData->GetColumns()[i].nWidth; + } + if(nColSum != pTableData->GetWidth()) + { + SwTwips nMinWidth = std::min( long(MINLAY), + static_cast<long>(pTableData->GetWidth() / + pTableData->GetColCount() - 1)); + SwTwips nDiff = nColSum - pTableData->GetWidth(); + while ( std::abs(nDiff) > pTableData->GetColCount() + 1 ) + { + SwTwips nSub = nDiff / pTableData->GetColCount(); + for( sal_uInt16 i = 0; i < pTableData->GetColCount(); i++) + { + if(pTableData->GetColumns()[i].nWidth - nMinWidth > nSub) + { + pTableData->GetColumns()[i].nWidth -= nSub; + nDiff -= nSub; + } + else + { + nDiff -= pTableData->GetColumns()[i].nWidth - nMinWidth; + pTableData->GetColumns()[i].nWidth = nMinWidth; + } + + } + } + } + + sal_Int16 nAlign = 0; + if (m_xRightBtn->get_active()) + nAlign = text::HoriOrientation::RIGHT; + else if(m_xLeftBtn->get_active()) + nAlign = text::HoriOrientation::LEFT; + else if(m_xFromLeftBtn->get_active()) + nAlign = text::HoriOrientation::LEFT_AND_WIDTH; + else if(m_xCenterBtn->get_active()) + nAlign = text::HoriOrientation::CENTER; + else if(m_xFreeBtn->get_active()) + nAlign = text::HoriOrientation::NONE; + else if(m_xFullBtn->get_active()) + { + nAlign = text::HoriOrientation::FULL; + lWidth = lAutoWidth; + } + if(nAlign != pTableData->GetAlign()) + { + pTableData->SetWidthChanged(); + pTableData->SetAlign(nAlign); + } + + if(pTableData->GetWidth() != lWidth ) + { + pTableData->SetWidthChanged(); + pTableData->SetWidth( + nAlign == text::HoriOrientation::FULL ? pTableData->GetSpace() : lWidth ); + } + if(pTableData->HasWidthChanged()) + _pSet->Put(SwPtrItem(FN_TABLE_REP, pTableData)); + } + } + return DeactivateRC::LeavePage; +} + +//Description: Page column configuration +SwTableColumnPage::SwTableColumnPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/tablecolumnpage.ui", "TableColumnPage", &rSet) + , m_pTableData(nullptr) + , m_pSizeHdlEvent(nullptr) + , m_nTableWidth(0) + , m_nMinWidth(MINLAY) + , m_nMetFields(MET_FIELDS) + , m_nNoOfCols(0) + , m_nNoOfVisibleCols(0) + , m_bModified(false) + , m_bModifyTable(false) + , m_bPercentMode(false) + , m_aFieldArr { m_xBuilder->weld_metric_spin_button("width1", FieldUnit::CM), + m_xBuilder->weld_metric_spin_button("width2", FieldUnit::CM), + m_xBuilder->weld_metric_spin_button("width3", FieldUnit::CM), + m_xBuilder->weld_metric_spin_button("width4", FieldUnit::CM), + m_xBuilder->weld_metric_spin_button("width5", FieldUnit::CM), + m_xBuilder->weld_metric_spin_button("width6", FieldUnit::CM) } + , m_aTextArr { m_xBuilder->weld_label("1"), + m_xBuilder->weld_label("2"), + m_xBuilder->weld_label("3"), + m_xBuilder->weld_label("4"), + m_xBuilder->weld_label("5"), + m_xBuilder->weld_label("6") } + , m_xModifyTableCB(m_xBuilder->weld_check_button("adaptwidth")) + , m_xProportionalCB(m_xBuilder->weld_check_button("adaptcolumns")) + , m_xSpaceFT(m_xBuilder->weld_label("spaceft")) + , m_xSpaceED(m_xBuilder->weld_metric_spin_button("space", FieldUnit::CM)) + , m_xUpBtn(m_xBuilder->weld_button("next")) + , m_xDownBtn(m_xBuilder->weld_button("back")) +{ + SetExchangeSupport(); + + // fire off this handler to happen on next event loop when all the rest of + // the pages are instantiated and the dialog preferred size is that of the + // all the pages that currently exist and the rest to come after this one + m_pSizeHdlEvent = Application::PostUserEvent(LINK(this, SwTableColumnPage, SizeHdl)); + + const SfxPoolItem* pItem; + Init(SfxItemState::SET == GetItemSet().GetItemState(SID_HTML_MODE, false, &pItem) + && static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON); +} + +IMPL_LINK_NOARG(SwTableColumnPage, SizeHdl, void*, void) +{ + m_pSizeHdlEvent = nullptr; + + //tdf#120420 keeping showing column width fields unless + //the dialog begins to grow, then stop adding them + weld::Window* pTopLevel = GetFrameWeld(); + Size aOrigSize = pTopLevel->get_preferred_size(); + for (sal_uInt16 i = 0; i < MET_FIELDS; ++i) + { + m_aFieldArr[i].show(); + m_aTextArr[i]->show(); + + if (pTopLevel->get_preferred_size().Width() > aOrigSize.Width()) + { + m_nMetFields = i + 1; + m_aTextArr[i]->set_grid_width(1); + m_xUpBtn->set_grid_left_attach(m_nMetFields * 2 - 1); + break; + } + } +} + +SwTableColumnPage::~SwTableColumnPage() +{ + if (m_pSizeHdlEvent) + { + Application::RemoveUserEvent(m_pSizeHdlEvent); + m_pSizeHdlEvent = nullptr; + } +} + +std::unique_ptr<SfxTabPage> SwTableColumnPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwTableColumnPage>(pPage, pController, *rAttrSet); +} + +void SwTableColumnPage::Reset( const SfxItemSet* ) +{ + const SfxItemSet& rSet = GetItemSet(); + + const SfxPoolItem* pItem; + if(SfxItemState::SET == rSet.GetItemState( FN_TABLE_REP, false, &pItem )) + { + m_pTableData = static_cast<SwTableRep*>(static_cast<const SwPtrItem*>( pItem)->GetValue()); + m_nNoOfVisibleCols = m_pTableData->GetColCount(); + m_nNoOfCols = m_pTableData->GetAllColCount(); + m_nTableWidth = m_pTableData->GetAlign() != text::HoriOrientation::FULL && + m_pTableData->GetAlign() != text::HoriOrientation::LEFT_AND_WIDTH? + m_pTableData->GetWidth() : m_pTableData->GetSpace(); + + for( sal_uInt16 i = 0; i < m_nNoOfCols; i++ ) + { + if( m_pTableData->GetColumns()[i].nWidth < m_nMinWidth ) + m_nMinWidth = m_pTableData->GetColumns()[i].nWidth; + } + sal_Int64 nMinTwips = m_aFieldArr[0].NormalizePercent( m_nMinWidth ); + sal_Int64 nMaxTwips = m_aFieldArr[0].NormalizePercent( m_nTableWidth ); + for( sal_uInt16 i = 0; (i < m_nMetFields) && (i < m_nNoOfVisibleCols); i++ ) + { + m_aFieldArr[i].set_value( m_aFieldArr[i].NormalizePercent( + GetVisibleWidth(i) ), FieldUnit::TWIP ); + m_aFieldArr[i].set_min(nMinTwips, FieldUnit::TWIP); + m_aFieldArr[i].set_max(nMaxTwips, FieldUnit::TWIP); + m_aFieldArr[i].set_sensitive(true); + m_aTextArr[i]->set_sensitive(true); + } + + if (m_nNoOfVisibleCols > m_nMetFields) + { + m_xUpBtn->set_sensitive(true); + } + + for( sal_uInt16 i = m_nNoOfVisibleCols; i < m_nMetFields; ++i ) + { + m_aFieldArr[i].set_text(OUString()); + m_aTextArr[i]->set_sensitive(false); + } + } + ActivatePage(rSet); + +} + +void SwTableColumnPage::Init(bool bWeb) +{ + FieldUnit aMetric = ::GetDfltMetric(bWeb); + Link<weld::MetricSpinButton&,void> aLk = LINK(this, SwTableColumnPage, ValueChangedHdl); + for (sal_uInt16 i = 0; i < MET_FIELDS; ++i) + { + m_aValueTable[i] = i; + m_aFieldArr[i].SetMetric(aMetric); + m_aFieldArr[i].connect_value_changed(aLk); + } + SetFieldUnit(*m_xSpaceED, aMetric); + + Link<weld::Button&,void> aClickLk = LINK(this, SwTableColumnPage, AutoClickHdl); + m_xUpBtn->connect_clicked(aClickLk); + m_xDownBtn->connect_clicked(aClickLk); + + Link<weld::ToggleButton&,void> aToggleLk = LINK(this, SwTableColumnPage, ModeHdl); + m_xModifyTableCB->connect_toggled(aToggleLk); + m_xProportionalCB->connect_toggled(aToggleLk); +} + +IMPL_LINK(SwTableColumnPage, AutoClickHdl, weld::Button&, rControl, void) +{ + //move display window + if (&rControl == m_xDownBtn.get()) + { + if(m_aValueTable[0] > 0) + { + for(sal_uInt16 & rn : m_aValueTable) + rn -= 1; + } + } + if (&rControl == m_xUpBtn.get()) + { + if( m_aValueTable[ m_nMetFields -1 ] < m_nNoOfVisibleCols -1 ) + { + for(sal_uInt16 & rn : m_aValueTable) + rn += 1; + } + } + for( sal_uInt16 i = 0; (i < m_nNoOfVisibleCols ) && ( i < m_nMetFields); i++ ) + { + OUString sEntry('~'); + OUString sIndex = OUString::number( m_aValueTable[i] + 1 ); + sEntry += sIndex; + m_aTextArr[i]->set_label(sEntry); + } + + m_xDownBtn->set_sensitive(m_aValueTable[0] > 0); + m_xUpBtn->set_sensitive(m_aValueTable[ m_nMetFields -1 ] < m_nNoOfVisibleCols -1 ); + UpdateCols(0); +} + +IMPL_LINK(SwTableColumnPage, ValueChangedHdl, weld::MetricSpinButton&, rEdit, void) +{ + m_bModified = true; + ModifyHdl(&rEdit); +} + +IMPL_LINK(SwTableColumnPage, ModeHdl, weld::ToggleButton&, rBox, void) +{ + const bool bCheck = rBox.get_active(); + if (&rBox == m_xProportionalCB.get()) + { + if (bCheck) + m_xModifyTableCB->set_active(true); + m_xModifyTableCB->set_sensitive(!bCheck && m_bModifyTable); + } +} + +bool SwTableColumnPage::FillItemSet( SfxItemSet* ) +{ + for (SwPercentField & i : m_aFieldArr) + { + if (i.has_focus()) + { + ModifyHdl(i.get()); + break; + } + } + + if (m_bModified) + { + m_pTableData->SetColsChanged(); + } + return m_bModified; +} + +void SwTableColumnPage::ModifyHdl(const weld::MetricSpinButton* pField) +{ + SwPercentField *pEdit = nullptr; + sal_uInt16 i; + + for( i = 0; i < m_nMetFields; i++) + { + if (pField == m_aFieldArr[i].get()) + { + pEdit = &m_aFieldArr[i]; + break; + } + } + + if (m_nMetFields <= i || !pEdit) + { + OSL_ENSURE(false, "cannot happen."); + return; + } + + SetVisibleWidth(m_aValueTable[i], pEdit->DenormalizePercent(pEdit->get_value(FieldUnit::TWIP))); + + UpdateCols( m_aValueTable[i] ); +} + +void SwTableColumnPage::UpdateCols( sal_uInt16 nCurrentPos ) +{ + SwTwips nSum = 0; + + for( sal_uInt16 i = 0; i < m_nNoOfCols; i++ ) + { + nSum += (m_pTableData->GetColumns())[i].nWidth; + } + SwTwips nDiff = nSum - m_nTableWidth; + + bool bModifyTableChecked = m_xModifyTableCB->get_active(); + bool bProp = m_xProportionalCB->get_active(); + + if (!bModifyTableChecked && !bProp) + { + //The table width is constant, the difference is balanced with the other columns + sal_uInt16 nLoopCount = 0; + while( nDiff ) + { + if( ++nCurrentPos == m_nNoOfVisibleCols) + { + nCurrentPos = 0; + ++nLoopCount; + //#i101353# in small tables it might not be possible to balance column width + if( nLoopCount > 1 ) + break; + } + if( nDiff < 0 ) + { + SetVisibleWidth(nCurrentPos, GetVisibleWidth(nCurrentPos) -nDiff); + nDiff = 0; + } + else if( GetVisibleWidth(nCurrentPos) >= nDiff + m_nMinWidth ) + { + SetVisibleWidth(nCurrentPos, GetVisibleWidth(nCurrentPos) -nDiff); + nDiff = 0; + } + if( nDiff > 0 && GetVisibleWidth(nCurrentPos) > m_nMinWidth ) + { + if( nDiff >= (GetVisibleWidth(nCurrentPos) - m_nMinWidth) ) + { + nDiff -= (GetVisibleWidth(nCurrentPos) - m_nMinWidth); + SetVisibleWidth(nCurrentPos, m_nMinWidth); + } + else + { + nDiff = 0; + SetVisibleWidth(nCurrentPos, GetVisibleWidth(nCurrentPos) -nDiff); + } + OSL_ENSURE(nDiff >= 0, "nDiff < 0 cannot be here!"); + } + } + } + else if (bModifyTableChecked && !bProp) + { + //Difference is balanced by the width of the table, + //other columns remain unchanged. + OSL_ENSURE(nDiff <= m_pTableData->GetSpace() - m_nTableWidth, "wrong maximum" ); + SwTwips nActSpace = m_pTableData->GetSpace() - m_nTableWidth; + if(nDiff > nActSpace) + { + m_nTableWidth = m_pTableData->GetSpace(); + SetVisibleWidth(nCurrentPos, GetVisibleWidth(nCurrentPos) - nDiff + nActSpace ); + } + else + { + m_nTableWidth += nDiff; + } + } + else if (bModifyTableChecked && bProp) + { + //All columns will be changed proportionally with, + //the table width is adjusted accordingly. + OSL_ENSURE(nDiff * m_nNoOfVisibleCols <= m_pTableData->GetSpace() - m_nTableWidth, "wrong maximum" ); + long nAdd = nDiff; + if(nDiff * m_nNoOfVisibleCols > m_pTableData->GetSpace() - m_nTableWidth) + { + nAdd = (m_pTableData->GetSpace() - m_nTableWidth) / m_nNoOfVisibleCols; + SetVisibleWidth(nCurrentPos, GetVisibleWidth(nCurrentPos) - nDiff + nAdd ); + nDiff = nAdd; + } + if(nAdd) + for( sal_uInt16 i = 0; i < m_nNoOfVisibleCols; i++ ) + { + if(i == nCurrentPos) + continue; + SwTwips nVisWidth; + if((nVisWidth = GetVisibleWidth(i)) + nDiff < MINLAY) + { + nAdd += nVisWidth - MINLAY; + SetVisibleWidth(i, MINLAY); + } + else + { + SetVisibleWidth(i, nVisWidth + nDiff); + nAdd += nDiff; + } + + } + m_nTableWidth += nAdd; + } + + if(!m_bPercentMode) + m_xSpaceED->set_value(m_xSpaceED->normalize(m_pTableData->GetSpace() - m_nTableWidth), FieldUnit::TWIP); + + for( sal_uInt16 i = 0; ( i < m_nNoOfVisibleCols ) && ( i < m_nMetFields ); i++) + { + m_aFieldArr[i].set_value(m_aFieldArr[i].NormalizePercent( + GetVisibleWidth(m_aValueTable[i]) ), FieldUnit::TWIP); + } +} + +void SwTableColumnPage::ActivatePage( const SfxItemSet& ) +{ + m_bPercentMode = m_pTableData->GetWidthPercent() != 0; + for( sal_uInt16 i = 0; (i < m_nMetFields) && (i < m_nNoOfVisibleCols); i++ ) + { + m_aFieldArr[i].SetRefValue(m_pTableData->GetWidth()); + m_aFieldArr[i].ShowPercent( m_bPercentMode ); + } + + const sal_uInt16 nTableAlign = m_pTableData->GetAlign(); + if((text::HoriOrientation::FULL != nTableAlign && m_nTableWidth != m_pTableData->GetWidth()) || + (text::HoriOrientation::FULL == nTableAlign && m_nTableWidth != m_pTableData->GetSpace())) + { + m_nTableWidth = text::HoriOrientation::FULL == nTableAlign ? + m_pTableData->GetSpace() : + m_pTableData->GetWidth(); + UpdateCols(0); + } + m_bModifyTable = true; + if (m_pTableData->GetWidthPercent() || + text::HoriOrientation::FULL == nTableAlign || + m_pTableData->IsLineSelected() ) + m_bModifyTable = false; + if (m_bPercentMode) + { + m_xModifyTableCB->set_active(false); + m_xProportionalCB->set_active(false); + } + else if (!m_bModifyTable) + { + m_xProportionalCB->set_active(false); + m_xModifyTableCB->set_active(false); + } + m_xSpaceFT->set_sensitive(!m_bPercentMode); + m_xSpaceED->set_sensitive(!m_bPercentMode); + m_xModifyTableCB->set_sensitive( !m_bPercentMode && m_bModifyTable ); + m_xProportionalCB->set_sensitive(!m_bPercentMode && m_bModifyTable ); + + m_xSpaceED->set_value(m_xSpaceED->normalize( + m_pTableData->GetSpace() - m_nTableWidth), FieldUnit::TWIP); + +} + +DeactivateRC SwTableColumnPage::DeactivatePage( SfxItemSet* _pSet ) +{ + if(_pSet) + { + FillItemSet(_pSet); + if(text::HoriOrientation::FULL != m_pTableData->GetAlign() && m_pTableData->GetWidth() != m_nTableWidth) + { + m_pTableData->SetWidth(m_nTableWidth); + SwTwips nDiff = m_pTableData->GetSpace() - m_pTableData->GetWidth() - + m_pTableData->GetLeftSpace() - m_pTableData->GetRightSpace(); + switch( m_pTableData->GetAlign() ) + { + case text::HoriOrientation::RIGHT: + m_pTableData->SetLeftSpace(m_pTableData->GetLeftSpace() + nDiff); + break; + case text::HoriOrientation::LEFT: + m_pTableData->SetRightSpace(m_pTableData->GetRightSpace() + nDiff); + break; + case text::HoriOrientation::NONE: + { + SwTwips nDiff2 = nDiff/2; + if( nDiff > 0 || + (-nDiff2 < m_pTableData->GetRightSpace() && - nDiff2 < m_pTableData->GetLeftSpace())) + { + m_pTableData->SetRightSpace(m_pTableData->GetRightSpace() + nDiff2); + m_pTableData->SetLeftSpace(m_pTableData->GetLeftSpace() + nDiff2); + } + else + { + if(m_pTableData->GetRightSpace() > m_pTableData->GetLeftSpace()) + { + m_pTableData->SetLeftSpace(0); + m_pTableData->SetRightSpace(m_pTableData->GetSpace() - m_pTableData->GetWidth()); + } + else + { + m_pTableData->SetRightSpace(0); + m_pTableData->SetLeftSpace(m_pTableData->GetSpace() - m_pTableData->GetWidth()); + } + } + } + break; + case text::HoriOrientation::CENTER: + m_pTableData->SetRightSpace(m_pTableData->GetRightSpace() + nDiff/2); + m_pTableData->SetLeftSpace(m_pTableData->GetLeftSpace() + nDiff/2); + break; + case text::HoriOrientation::LEFT_AND_WIDTH : + if(nDiff > m_pTableData->GetRightSpace()) + { + m_pTableData->SetLeftSpace(m_pTableData->GetSpace() - m_pTableData->GetWidth()); + } + m_pTableData->SetRightSpace( + m_pTableData->GetSpace() - m_pTableData->GetWidth() - m_pTableData->GetLeftSpace()); + break; + } + m_pTableData->SetWidthChanged(); + } + _pSet->Put(SwPtrItem( FN_TABLE_REP, m_pTableData )); + } + return DeactivateRC::LeavePage; +} + +SwTwips SwTableColumnPage::GetVisibleWidth(sal_uInt16 nPos) +{ + sal_uInt16 i=0; + + while( nPos ) + { + if(m_pTableData->GetColumns()[i].bVisible) + nPos--; + i++; + } + SwTwips nReturn = m_pTableData->GetColumns()[i].nWidth; + OSL_ENSURE(i < m_nNoOfCols, "Array index out of range"); + while(!m_pTableData->GetColumns()[i].bVisible && (i + 1) < m_nNoOfCols) + nReturn += m_pTableData->GetColumns()[++i].nWidth; + + return nReturn; +} + +void SwTableColumnPage::SetVisibleWidth(sal_uInt16 nPos, SwTwips nNewWidth) +{ + sal_uInt16 i=0; + while( nPos ) + { + if(m_pTableData->GetColumns()[i].bVisible) + nPos--; + i++; + } + OSL_ENSURE(i < m_nNoOfCols, "Array index out of range"); + m_pTableData->GetColumns()[i].nWidth = nNewWidth; + while(!m_pTableData->GetColumns()[i].bVisible && (i + 1) < m_nNoOfCols) + m_pTableData->GetColumns()[++i].nWidth = 0; + +} + +SwTableTabDlg::SwTableTabDlg(weld::Window* pParent, const SfxItemSet* pItemSet, SwWrtShell* pSh) + : SfxTabDialogController(pParent, "modules/swriter/ui/tableproperties.ui", "TablePropertiesDialog", pItemSet) + , pShell(pSh) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + AddTabPage("table", &SwFormatTablePage::Create, nullptr); + AddTabPage("textflow", &SwTextFlowPage::Create, nullptr); + AddTabPage("columns", &SwTableColumnPage::Create, nullptr); + AddTabPage("background", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); + AddTabPage("borders", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BORDER), nullptr); +} + +void SwTableTabDlg::PageCreated(const OString& rId, SfxTabPage& rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "background") + { + SvxBackgroundTabFlags const nFlagType = SvxBackgroundTabFlags::SHOW_TBLCTL; + aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(nFlagType))); + rPage.PageCreated(aSet); + } + else if (rId == "borders") + { + aSet.Put (SfxUInt16Item(SID_SWMODE_TYPE, static_cast<sal_uInt16>(SwBorderModes::TABLE))); + rPage.PageCreated(aSet); + } + else if (rId == "textflow") + { + static_cast<SwTextFlowPage&>(rPage).SetShell(pShell); + const FrameTypeFlags eType = pShell->GetFrameType(nullptr,true); + if( !(FrameTypeFlags::BODY & eType) ) + static_cast<SwTextFlowPage&>(rPage).DisablePageBreak(); + } +} + +SwTextFlowPage::SwTextFlowPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet) + : SfxTabPage(pPage, pController, "modules/swriter/ui/tabletextflowpage.ui", "TableTextFlowPage", &rSet) + , pShell(nullptr) + , bPageBreak(true) + , bHtmlMode(false) + , m_xPgBrkCB(m_xBuilder->weld_check_button("break")) + , m_xPgBrkRB(m_xBuilder->weld_radio_button("page")) + , m_xColBrkRB(m_xBuilder->weld_radio_button("column")) + , m_xPgBrkBeforeRB(m_xBuilder->weld_radio_button("before")) + , m_xPgBrkAfterRB(m_xBuilder->weld_radio_button("after")) + , m_xPageCollCB(m_xBuilder->weld_check_button("pagestyle")) + , m_xPageCollLB(m_xBuilder->weld_combo_box("pagestylelb")) + , m_xPageNoCB(m_xBuilder->weld_check_button("pagenoft")) + , m_xPageNoNF(m_xBuilder->weld_spin_button("pagenonf")) + , m_xSplitCB(m_xBuilder->weld_check_button("split")) + , m_xSplitRowCB(m_xBuilder->weld_check_button("splitrow")) + , m_xKeepCB(m_xBuilder->weld_check_button("keep")) + , m_xHeadLineCB(m_xBuilder->weld_check_button("headline")) + , m_xRepeatHeaderCombo(m_xBuilder->weld_widget("repeatheader")) + , m_xRepeatHeaderNF(m_xBuilder->weld_spin_button("repeatheadernf")) + , m_xTextDirectionLB(m_xBuilder->weld_combo_box("textorientation")) + , m_xVertOrientLB(m_xBuilder->weld_combo_box("vertorient")) +{ + m_xPgBrkCB->connect_toggled(LINK(this, SwTextFlowPage, PageBreakHdl_Impl)); + m_xPgBrkBeforeRB->connect_toggled( + LINK(this, SwTextFlowPage, PageBreakPosHdl_Impl)); + m_xPgBrkAfterRB->connect_toggled( + LINK(this, SwTextFlowPage, PageBreakPosHdl_Impl)); + m_xPageCollCB->connect_toggled( + LINK(this, SwTextFlowPage, ApplyCollClickHdl_Impl)); + m_xColBrkRB->connect_toggled( + LINK(this, SwTextFlowPage, PageBreakTypeHdl_Impl)); + m_xPgBrkRB->connect_toggled( + LINK(this, SwTextFlowPage, PageBreakTypeHdl_Impl)); + m_xPageNoCB->connect_toggled( + LINK(this, SwTextFlowPage, PageNoClickHdl_Impl)); + m_xSplitCB->connect_toggled( + LINK(this, SwTextFlowPage, SplitHdl_Impl)); + m_xHeadLineCB->connect_toggled(LINK(this, SwTextFlowPage, HeadLineCBClickHdl)); + + const SfxPoolItem *pItem; + if(SfxItemState::SET == rSet.GetItemState( SID_HTML_MODE, false,&pItem ) + && static_cast<const SfxUInt16Item*>(pItem)->GetValue() & HTMLMODE_ON) + { + m_xKeepCB->hide(); + m_xSplitCB->hide(); + m_xSplitRowCB->hide(); + } + + HeadLineCBClickHdl(*m_xHeadLineCB); +} + +SwTextFlowPage::~SwTextFlowPage() +{ +} + +std::unique_ptr<SfxTabPage> SwTextFlowPage::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet) +{ + return std::make_unique<SwTextFlowPage>(pPage, pController, *rAttrSet); +} + +bool SwTextFlowPage::FillItemSet( SfxItemSet* rSet ) +{ + bool bModified = false; + + //Repeat Heading + if (m_xHeadLineCB->get_state_changed_from_saved() || + m_xRepeatHeaderNF->get_value_changed_from_saved() ) + { + bModified |= nullptr != rSet->Put( + SfxUInt16Item(FN_PARAM_TABLE_HEADLINE, m_xHeadLineCB->get_active() ? sal_uInt16(m_xRepeatHeaderNF->get_value()) : 0)); + } + if (m_xKeepCB->get_state_changed_from_saved()) + bModified |= nullptr != rSet->Put( SvxFormatKeepItem( m_xKeepCB->get_active(), RES_KEEP)); + + if (m_xSplitCB->get_state_changed_from_saved()) + bModified |= nullptr != rSet->Put( SwFormatLayoutSplit( m_xSplitCB->get_active())); + + if (m_xSplitRowCB->get_state_changed_from_saved()) + bModified |= nullptr != rSet->Put( SwFormatRowSplit( m_xSplitRowCB->get_active())); + + const SvxFormatBreakItem* pBreak = GetOldItem( *rSet, RES_BREAK ); + const SwFormatPageDesc* pDesc = GetOldItem( *rSet, RES_PAGEDESC ); + + bool bState = m_xPageCollCB->get_active(); + + //If we have a page style, then there's no break + bool bPageItemPut = false; + if ( bState != (m_xPageCollCB->get_saved_state() == TRISTATE_TRUE) + || (bState && m_xPageCollLB->get_value_changed_from_saved()) + || (m_xPageNoCB->get_sensitive() && m_xPageNoCB->get_state_changed_from_saved()) + || (m_xPageNoNF->get_sensitive() && m_xPageNoNF->get_value_changed_from_saved())) + { + OUString sPage; + + if ( bState ) + { + sPage = m_xPageCollLB->get_active_text(); + } + sal_uInt16 nPgNum = static_cast<sal_uInt16>(m_xPageNoNF->get_value()); + bool const usePageNo(bState && m_xPageNoCB->get_active()); + std::optional<sal_uInt16> const oPageNum( + usePageNo ? nPgNum : std::optional<sal_Int16>()); + if (!pDesc || !pDesc->GetPageDesc() + || (pDesc->GetPageDesc()->GetName() != sPage) + || (pDesc->GetNumOffset() != oPageNum)) + { + SwFormatPageDesc aFormat( pShell->FindPageDescByName( sPage, true ) ); + aFormat.SetNumOffset(oPageNum); + bModified |= nullptr != rSet->Put( aFormat ); + bPageItemPut = bState; + } + } + bool bIsChecked = m_xPgBrkCB->get_active(); + if ( !bPageItemPut && + ( bState != (m_xPageCollCB->get_saved_state() == TRISTATE_TRUE) || + bIsChecked != (m_xPgBrkCB->get_saved_state() == TRISTATE_TRUE) || + m_xPgBrkBeforeRB->get_state_changed_from_saved() || + m_xPgBrkRB->get_state_changed_from_saved() )) + { + SvxFormatBreakItem aBreak( GetItemSet().Get( RES_BREAK ) ); + + if(bIsChecked) + { + bool bBefore = m_xPgBrkBeforeRB->get_active(); + + if (m_xPgBrkRB->get_active()) + { + if ( bBefore ) + aBreak.SetValue( SvxBreak::PageBefore ); + else + aBreak.SetValue( SvxBreak::PageAfter ); + } + else + { + if ( bBefore ) + aBreak.SetValue( SvxBreak::ColumnBefore ); + else + aBreak.SetValue( SvxBreak::ColumnAfter ); + } + } + else + { + aBreak.SetValue( SvxBreak::NONE ); + } + + if ( !pBreak || !( *pBreak == aBreak ) ) + { + bModified |= nullptr != rSet->Put( aBreak ); + } + } + + if (m_xTextDirectionLB->get_value_changed_from_saved()) + { + OUString sId = m_xTextDirectionLB->get_active_id(); + bModified |= nullptr != rSet->Put(SvxFrameDirectionItem(static_cast<SvxFrameDirection>(sId.toUInt32()), FN_TABLE_BOX_TEXTORIENTATION)); + } + + if (m_xVertOrientLB->get_value_changed_from_saved()) + { + sal_uInt16 nOrient = USHRT_MAX; + switch (m_xVertOrientLB->get_active()) + { + case 0 : nOrient = text::VertOrientation::NONE; break; + case 1 : nOrient = text::VertOrientation::CENTER; break; + case 2 : nOrient = text::VertOrientation::BOTTOM; break; + } + if (nOrient != USHRT_MAX) + bModified |= nullptr != rSet->Put(SfxUInt16Item(FN_TABLE_SET_VERT_ALIGN, nOrient)); + } + + return bModified; + +} + +void SwTextFlowPage::Reset( const SfxItemSet* rSet ) +{ + const SfxPoolItem* pItem; + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + bool bFlowAllowed = !bHtmlMode || rHtmlOpt.IsPrintLayoutExtension(); + if(bFlowAllowed) + { + //Inserting of the existing page templates in the list box + const size_t nCount = pShell->GetPageDescCnt(); + + for( size_t i = 0; i < nCount; ++i) + { + const SwPageDesc &rPageDesc = pShell->GetPageDesc(i); + m_xPageCollLB->append_text(rPageDesc.GetName()); + } + + OUString aFormatName; + for (sal_uInt16 i = RES_POOLPAGE_BEGIN; i < RES_POOLPAGE_END; ++i) + { + aFormatName = SwStyleNameMapper::GetUIName(i, aFormatName); + if (m_xPageCollLB->find_text(aFormatName) == -1) + m_xPageCollLB->append_text(aFormatName); + } + + if(SfxItemState::SET == rSet->GetItemState( RES_KEEP, false, &pItem )) + { + m_xKeepCB->set_active( static_cast<const SvxFormatKeepItem*>(pItem)->GetValue() ); + m_xKeepCB->save_state(); + } + if(SfxItemState::SET == rSet->GetItemState( RES_LAYOUT_SPLIT, false, &pItem )) + { + m_xSplitCB->set_active( static_cast<const SwFormatLayoutSplit*>(pItem)->GetValue() ); + } + else + m_xSplitCB->set_active(true); + + m_xSplitCB->save_state(); + SplitHdl_Impl(*m_xSplitCB); + + if(SfxItemState::SET == rSet->GetItemState( RES_ROW_SPLIT, false, &pItem )) + { + m_xSplitRowCB->set_active( static_cast<const SwFormatRowSplit*>(pItem)->GetValue() ); + } + else + m_xSplitRowCB->set_state(TRISTATE_INDET); + m_xSplitRowCB->save_state(); + + if(bPageBreak) + { + if(SfxItemState::SET == rSet->GetItemState( RES_PAGEDESC, false, &pItem )) + { + OUString sPageDesc; + const SwPageDesc* pDesc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc(); + + ::std::optional<sal_uInt16> oNumOffset = static_cast<const SwFormatPageDesc*>(pItem)->GetNumOffset(); + if (oNumOffset) + { + m_xPageNoCB->set_active(true); + m_xPageNoNF->set_sensitive(true); + m_xPageNoNF->set_value(*oNumOffset); + } + else + { + m_xPageNoCB->set_active(false); + m_xPageNoNF->set_sensitive(false); + } + + if(pDesc) + sPageDesc = pDesc->GetName(); + if (!sPageDesc.isEmpty() && m_xPageCollLB->find_text(sPageDesc) != -1) + { + m_xPageCollLB->set_active_text(sPageDesc); + m_xPageCollCB->set_active(true); + + m_xPgBrkCB->set_sensitive(true); + m_xPgBrkRB->set_sensitive(true); + m_xColBrkRB->set_sensitive(true); + m_xPgBrkBeforeRB->set_sensitive(true); + m_xPgBrkAfterRB->set_sensitive(true); + m_xPageCollCB->set_sensitive(true); + m_xPgBrkCB->set_active(true); + + m_xPgBrkCB->set_active(true); + m_xColBrkRB->set_active( false ); + m_xPgBrkBeforeRB->set_active(true); + m_xPgBrkAfterRB->set_active( false ); + } + else + { + m_xPageCollLB->set_active(-1); + m_xPageCollCB->set_active(false); + } + } + + if(SfxItemState::SET == rSet->GetItemState( RES_BREAK, false, &pItem )) + { + const SvxFormatBreakItem* pPageBreak = static_cast<const SvxFormatBreakItem*>(pItem); + SvxBreak eBreak = pPageBreak->GetBreak(); + + if ( eBreak != SvxBreak::NONE ) + { + m_xPgBrkCB->set_active(true); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + m_xPageNoNF->set_sensitive(false); + } + switch ( eBreak ) + { + case SvxBreak::PageBefore: + m_xPgBrkRB->set_active(true); + m_xColBrkRB->set_active( false ); + m_xPgBrkBeforeRB->set_active(true); + m_xPgBrkAfterRB->set_active( false ); + break; + case SvxBreak::PageAfter: + m_xPgBrkRB->set_active(true); + m_xColBrkRB->set_active( false ); + m_xPgBrkBeforeRB->set_active( false ); + m_xPgBrkAfterRB->set_active(true); + break; + case SvxBreak::ColumnBefore: + m_xPgBrkRB->set_active( false ); + m_xColBrkRB->set_active(true); + m_xPgBrkBeforeRB->set_active(true); + m_xPgBrkAfterRB->set_active( false ); + break; + case SvxBreak::ColumnAfter: + m_xPgBrkRB->set_active( false ); + m_xColBrkRB->set_active(true); + m_xPgBrkBeforeRB->set_active( false ); + m_xPgBrkAfterRB->set_active(true); + break; + default:; //prevent warning + } + + } + if (m_xPgBrkBeforeRB->get_active()) + PageBreakPosHdl_Impl(*m_xPgBrkBeforeRB); + else if (m_xPgBrkAfterRB->get_active()) + PageBreakPosHdl_Impl(*m_xPgBrkAfterRB); + PageBreakHdl_Impl(*m_xPgBrkCB); + } + } + else + { + m_xPgBrkRB->set_sensitive(false); + m_xColBrkRB->set_sensitive(false); + m_xPgBrkBeforeRB->set_sensitive(false); + m_xPgBrkAfterRB->set_sensitive(false); + m_xKeepCB->set_sensitive(false); + m_xSplitCB->set_sensitive(false); + m_xPgBrkCB->set_sensitive(false); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + } + + if(SfxItemState::SET == rSet->GetItemState( FN_PARAM_TABLE_HEADLINE, false, &pItem )) + { + sal_uInt16 nRep = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + m_xHeadLineCB->set_active(nRep > 0); + m_xHeadLineCB->save_state(); + m_xRepeatHeaderNF->set_value(nRep); + m_xRepeatHeaderNF->set_min(1); + m_xRepeatHeaderNF->save_value(); + } + if ( rSet->GetItemState(FN_TABLE_BOX_TEXTORIENTATION) > SfxItemState::DEFAULT ) + { + SvxFrameDirection nDirection = + static_cast<const SvxFrameDirectionItem&>(rSet->Get(FN_TABLE_BOX_TEXTORIENTATION)).GetValue(); + m_xTextDirectionLB->set_active_id(OUString::number(static_cast<sal_uInt32>(nDirection))); + } + + if ( rSet->GetItemState(FN_TABLE_SET_VERT_ALIGN) > SfxItemState::DEFAULT ) + { + sal_uInt16 nVert = static_cast<const SfxUInt16Item&>(rSet->Get(FN_TABLE_SET_VERT_ALIGN)).GetValue(); + sal_uInt16 nPos = 0; + switch(nVert) + { + case text::VertOrientation::NONE: nPos = 0; break; + case text::VertOrientation::CENTER: nPos = 1; break; + case text::VertOrientation::BOTTOM: nPos = 2; break; + } + m_xVertOrientLB->set_active(nPos); + } + + m_xPageCollCB->save_state(); + m_xPageCollLB->save_value(); + m_xPgBrkCB->save_state(); + m_xPgBrkRB->save_state(); + m_xColBrkRB->save_state(); + m_xPgBrkBeforeRB->save_state(); + m_xPgBrkAfterRB->save_state(); + m_xPageNoCB->save_state(); + m_xPageNoNF->save_value(); + m_xTextDirectionLB->save_value(); + m_xVertOrientLB->save_value(); + + HeadLineCBClickHdl(*m_xHeadLineCB); +} + +void SwTextFlowPage::SetShell(SwWrtShell* pSh) +{ + pShell = pSh; + bHtmlMode = 0 != (::GetHtmlMode(pShell->GetView().GetDocShell()) & HTMLMODE_ON); + if(bHtmlMode) + { + m_xPageNoNF->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SwTextFlowPage, PageBreakHdl_Impl, weld::ToggleButton&, void) +{ + if (m_xPgBrkCB->get_active()) + { + m_xPgBrkRB->set_sensitive(true); + m_xColBrkRB->set_sensitive(true); + m_xPgBrkBeforeRB->set_sensitive(true); + m_xPgBrkAfterRB->set_sensitive(true); + + if (m_xPgBrkRB->get_active() && m_xPgBrkBeforeRB->get_active()) + { + m_xPageCollCB->set_sensitive(true); + + bool bEnable = m_xPageCollCB->get_active() && m_xPageCollLB->get_count(); + m_xPageCollLB->set_sensitive(bEnable); + if (!bHtmlMode) + { + m_xPageNoCB->set_sensitive(bEnable); + m_xPageNoNF->set_sensitive(bEnable && m_xPageNoCB->get_active()); + } + } + } + else + { + m_xPageCollCB->set_active(false); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + m_xPageNoNF->set_sensitive(false); + m_xPgBrkRB->set_sensitive(false); + m_xColBrkRB->set_sensitive(false); + m_xPgBrkBeforeRB->set_sensitive(false); + m_xPgBrkAfterRB->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SwTextFlowPage, ApplyCollClickHdl_Impl, weld::ToggleButton&, void) +{ + bool bEnable = false; + if (m_xPageCollCB->get_active() && m_xPageCollLB->get_count()) + { + bEnable = true; + m_xPageCollLB->set_active(0); + } + else + { + m_xPageCollLB->set_active(-1); + } + m_xPageCollLB->set_sensitive(bEnable); + if (!bHtmlMode) + { + m_xPageNoCB->set_sensitive(bEnable); + m_xPageNoNF->set_sensitive(bEnable && m_xPageNoCB->get_active()); + } +} + +IMPL_LINK_NOARG(SwTextFlowPage, PageBreakPosHdl_Impl, weld::ToggleButton&, void) +{ + if (m_xPgBrkCB->get_active()) + { + if (m_xPgBrkBeforeRB->get_active() && m_xPgBrkRB->get_active()) + { + m_xPageCollCB->set_sensitive(true); + + bool bEnable = m_xPageCollCB->get_active() && m_xPageCollLB->get_count(); + + m_xPageCollLB->set_sensitive(bEnable); + if (!bHtmlMode) + { + m_xPageNoCB->set_sensitive(bEnable); + m_xPageNoNF->set_sensitive(bEnable && m_xPageNoCB->get_active()); + } + } + else if (m_xPgBrkAfterRB->get_active()) + { + m_xPageCollCB->set_active(false); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + m_xPageNoNF->set_sensitive(false); + } + } +} + +IMPL_LINK_NOARG(SwTextFlowPage, PageBreakTypeHdl_Impl, weld::ToggleButton&, void) +{ + if (m_xColBrkRB->get_active() || m_xPgBrkAfterRB->get_active()) + { + m_xPageCollCB->set_active(false); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + m_xPageNoNF->set_sensitive(false); + } + else if (m_xPgBrkBeforeRB->get_active()) + PageBreakPosHdl_Impl(*m_xPgBrkBeforeRB); +} + +IMPL_LINK_NOARG(SwTextFlowPage, PageNoClickHdl_Impl, weld::ToggleButton&, void) +{ + m_xPageNoNF->set_sensitive(m_xPageNoCB->get_active()); +} + +IMPL_LINK(SwTextFlowPage, SplitHdl_Impl, weld::ToggleButton&, rBox, void) +{ + m_xSplitRowCB->set_sensitive(rBox.get_active()); +} + +IMPL_LINK_NOARG(SwTextFlowPage, HeadLineCBClickHdl, weld::ToggleButton&, void) +{ + m_xRepeatHeaderCombo->set_sensitive(m_xHeadLineCB->get_active()); +} + +void SwTextFlowPage::DisablePageBreak() +{ + bPageBreak = false; + m_xPgBrkCB->set_sensitive(false); + m_xPgBrkRB->set_sensitive(false); + m_xColBrkRB->set_sensitive(false); + m_xPgBrkBeforeRB->set_sensitive(false); + m_xPgBrkAfterRB->set_sensitive(false); + m_xPageCollCB->set_sensitive(false); + m_xPageCollLB->set_sensitive(false); + m_xPageNoCB->set_sensitive(false); + m_xPageNoNF->set_sensitive(false); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/table/tautofmt.cxx b/sw/source/ui/table/tautofmt.cxx new file mode 100644 index 000000000..169d38f22 --- /dev/null +++ b/sw/source/ui/table/tautofmt.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 <memory> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sal/log.hxx> +#include <strings.hrc> +#include <shellres.hxx> +#include <tautofmt.hxx> + +namespace { + +class SwStringInputDlg : public SfxDialogController +{ +private: + std::unique_ptr<weld::Label> m_xLabel; + std::unique_ptr<weld::Entry> m_xEdInput; // Edit obtains the focus. + +public: + SwStringInputDlg(weld::Window* pParent, const OUString& rTitle, + const OUString& rEditTitle, const OUString& rDefault) + : SfxDialogController(pParent, "modules/swriter/ui/stringinput.ui", + "StringInputDialog") + , m_xLabel(m_xBuilder->weld_label("name")) + , m_xEdInput(m_xBuilder->weld_entry("edit")) + { + m_xLabel->set_label(rEditTitle); + m_xDialog->set_title(rTitle); + m_xEdInput->set_text(rDefault); + m_xEdInput->select_region(0, -1); + } + + OUString GetInputString() const + { + return m_xEdInput->get_text(); + } +}; + +} + +// AutoFormat-Dialogue: +SwAutoFormatDlg::SwAutoFormatDlg(weld::Window* pParent, SwWrtShell* pWrtShell, + bool bAutoFormat, const SwTableAutoFormat* pSelFormat) + : SfxDialogController(pParent, "modules/swriter/ui/autoformattable.ui", + "AutoFormatTableDialog") + , m_aStrTitle(SwResId(STR_ADD_AUTOFORMAT_TITLE)) + , m_aStrLabel(SwResId(STR_ADD_AUTOFORMAT_LABEL)) + , m_aStrClose(SwResId(STR_BTN_AUTOFORMAT_CLOSE)) + , m_aStrDelTitle(SwResId(STR_DEL_AUTOFORMAT_TITLE)) + , m_aStrDelMsg(SwResId(STR_DEL_AUTOFORMAT_MSG)) + , m_aStrRenameTitle(SwResId(STR_RENAME_AUTOFORMAT_TITLE)) + , m_aStrInvalidFormat(SwResId(STR_INVALID_AUTOFORMAT_NAME)) + , m_pShell(pWrtShell) + , m_nIndex(0) + , m_nDfltStylePos(0) + , m_bCoreDataChanged(false) + , m_bSetAutoFormat(bAutoFormat) + , m_xTableTable(new SwTableAutoFormatTable) + , m_xLbFormat(m_xBuilder->weld_tree_view("formatlb")) + , m_xBtnNumFormat(m_xBuilder->weld_check_button("numformatcb")) + , m_xBtnBorder(m_xBuilder->weld_check_button("bordercb")) + , m_xBtnFont(m_xBuilder->weld_check_button("fontcb")) + , m_xBtnPattern(m_xBuilder->weld_check_button("patterncb")) + , m_xBtnAlignment(m_xBuilder->weld_check_button("alignmentcb")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) + , m_xBtnAdd(m_xBuilder->weld_button("add")) + , m_xBtnRemove(m_xBuilder->weld_button("remove")) + , m_xBtnRename(m_xBuilder->weld_button("rename")) + , m_xWndPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aWndPreview)) +{ + m_aWndPreview.DetectRTL(pWrtShell); + m_xTableTable->Load(); + + const int nWidth = m_xLbFormat->get_approximate_digit_width() * 32; + const int nHeight = m_xLbFormat->get_height_rows(8); + m_xLbFormat->set_size_request(nWidth, nHeight); + m_xWndPreview->set_size_request(nWidth, nHeight); + + Init(pSelFormat); +} + +SwAutoFormatDlg::~SwAutoFormatDlg() +{ + try + { + if (m_bCoreDataChanged) + m_xTableTable->Save(); + } + catch (...) + { + } + m_xTableTable.reset(); +} + +void SwAutoFormatDlg::Init( const SwTableAutoFormat* pSelFormat ) +{ + Link<weld::ToggleButton&, void> aLk(LINK(this, SwAutoFormatDlg, CheckHdl)); + m_xBtnBorder->connect_toggled(aLk); + m_xBtnFont->connect_toggled(aLk); + m_xBtnPattern->connect_toggled(aLk); + m_xBtnAlignment->connect_toggled(aLk); + m_xBtnNumFormat->connect_toggled(aLk); + + m_xBtnAdd->connect_clicked(LINK(this, SwAutoFormatDlg, AddHdl)); + m_xBtnRemove->connect_clicked(LINK(this, SwAutoFormatDlg, RemoveHdl)); + m_xBtnRename->connect_clicked(LINK(this, SwAutoFormatDlg, RenameHdl)); + m_xLbFormat->connect_changed(LINK(this, SwAutoFormatDlg, SelFormatHdl)); + + m_xBtnAdd->set_sensitive(m_bSetAutoFormat); + + m_nIndex = 0; + if( !m_bSetAutoFormat ) + { + // Then the list to be expanded by the entry "- none -". + m_xLbFormat->append_text(SwViewShell::GetShellRes()->aStrNone); + m_nDfltStylePos = 1; + m_nIndex = 255; + } + + for (sal_uInt8 i = 0, nCount = static_cast<sal_uInt8>(m_xTableTable->size()); + i < nCount; i++) + { + SwTableAutoFormat const& rFormat = (*m_xTableTable)[ i ]; + m_xLbFormat->append_text(rFormat.GetName()); + if (pSelFormat && rFormat.GetName() == pSelFormat->GetName()) + m_nIndex = i; + } + + m_xLbFormat->select(255 != m_nIndex ? (m_nDfltStylePos + m_nIndex) : 0); + SelFormatHdl(*m_xLbFormat); +} + +void SwAutoFormatDlg::UpdateChecks( const SwTableAutoFormat& rFormat, bool bEnable ) +{ + m_xBtnNumFormat->set_sensitive(bEnable); + m_xBtnNumFormat->set_active(rFormat.IsValueFormat()); + + m_xBtnBorder->set_sensitive(bEnable); + m_xBtnBorder->set_active(rFormat.IsFrame()); + + m_xBtnFont->set_sensitive(bEnable); + m_xBtnFont->set_active(rFormat.IsFont()); + + m_xBtnPattern->set_sensitive(bEnable); + m_xBtnPattern->set_active(rFormat.IsBackground()); + + m_xBtnAlignment->set_sensitive(bEnable); + m_xBtnAlignment->set_active(rFormat.IsJustify()); +} + +std::unique_ptr<SwTableAutoFormat> SwAutoFormatDlg::FillAutoFormatOfIndex() const +{ + if( 255 != m_nIndex ) + { + return std::make_unique<SwTableAutoFormat>( (*m_xTableTable)[ m_nIndex ] ); + } + + return nullptr; +} + +// Handler: +IMPL_LINK(SwAutoFormatDlg, CheckHdl, weld::ToggleButton&, rBtn, void) +{ + if (m_nIndex == 255) + return; + + SwTableAutoFormat& rData = (*m_xTableTable)[m_nIndex]; + bool bCheck = rBtn.get_active(), bDataChgd = true; + + if (&rBtn == m_xBtnNumFormat.get()) + rData.SetValueFormat( bCheck ); + else if (&rBtn == m_xBtnBorder.get()) + rData.SetFrame( bCheck ); + else if (&rBtn == m_xBtnFont.get()) + rData.SetFont( bCheck ); + else if (&rBtn == m_xBtnPattern.get()) + rData.SetBackground( bCheck ); + else if (&rBtn == m_xBtnAlignment.get()) + rData.SetJustify( bCheck ); + else + bDataChgd = false; + + if( bDataChgd ) + { + if( !m_bCoreDataChanged ) + { + m_xBtnCancel->set_label(m_aStrClose); + m_bCoreDataChanged = true; + } + + m_aWndPreview.NotifyChange(rData); + } +} + +IMPL_LINK_NOARG(SwAutoFormatDlg, AddHdl, weld::Button&, void) +{ + bool bOk = false, bFormatInserted = false; + while( !bOk ) + { + SwStringInputDlg aDlg(m_xDialog.get(), m_aStrTitle, m_aStrLabel, OUString()); + if (RET_OK == aDlg.run()) + { + const OUString aFormatName(aDlg.GetInputString()); + + if ( !aFormatName.isEmpty() ) + { + size_t n; + for( n = 0; n < m_xTableTable->size(); ++n ) + if( (*m_xTableTable)[n].GetName() == aFormatName ) + break; + + if( n >= m_xTableTable->size() ) + { + // Format with the name does not already exist, so take up. + std::unique_ptr<SwTableAutoFormat> pNewData( + new SwTableAutoFormat(aFormatName)); + bool bGetOk = m_pShell->GetTableAutoFormat( *pNewData ); + SAL_WARN_IF(!bGetOk, "sw.ui", "GetTableAutoFormat failed for: " << aFormatName); + + // Insert sorted!! + for( n = 1; n < m_xTableTable->size(); ++n ) + if( (*m_xTableTable)[ n ].GetName() > aFormatName ) + break; + + m_xTableTable->InsertAutoFormat(n, std::move(pNewData)); + m_xLbFormat->insert_text(m_nDfltStylePos + n, aFormatName); + m_xLbFormat->select(m_nDfltStylePos + n); + bFormatInserted = true; + m_xBtnAdd->set_sensitive(false); + if ( !m_bCoreDataChanged ) + { + m_xBtnCancel->set_label(m_aStrClose); + m_bCoreDataChanged = true; + } + + SelFormatHdl(*m_xLbFormat); + bOk = true; + } + } + + if( !bFormatInserted ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Error, VclButtonsType::OkCancel, m_aStrInvalidFormat)); + bOk = RET_CANCEL == xBox->run(); + } + } + else + bOk = true; + } +} + +IMPL_LINK_NOARG(SwAutoFormatDlg, RemoveHdl, weld::Button&, void) +{ + OUString aMessage = m_aStrDelMsg + "\n\n" + + m_xLbFormat->get_selected_text() + "\n"; + + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Question, + VclButtonsType::OkCancel, m_aStrDelTitle)); + xBox->set_secondary_text(aMessage); + + if (xBox->run() == RET_OK) + { + sal_uInt8 nIndex = m_nIndex; + + m_xLbFormat->remove(m_nDfltStylePos + nIndex); + m_xLbFormat->select(m_nDfltStylePos + nIndex - 1); + + m_xTableTable->EraseAutoFormat(nIndex); + m_nIndex = nIndex - 1; + + if( !m_nIndex ) + { + m_xBtnRemove->set_sensitive(false); + m_xBtnRename->set_sensitive(false); + } + + if( !m_bCoreDataChanged ) + { + m_xBtnCancel->set_label(m_aStrClose); + m_bCoreDataChanged = true; + } + } + + SelFormatHdl(*m_xLbFormat); +} + +IMPL_LINK_NOARG(SwAutoFormatDlg, RenameHdl, weld::Button&, void) +{ + bool bOk = false; + while( !bOk ) + { + SwStringInputDlg aDlg(m_xDialog.get(), m_aStrRenameTitle, m_aStrLabel, m_xLbFormat->get_selected_text()); + if (aDlg.run() == RET_OK) + { + bool bFormatRenamed = false; + const OUString aFormatName(aDlg.GetInputString()); + + if ( !aFormatName.isEmpty() ) + { + size_t n; + for( n = 0; n < m_xTableTable->size(); ++n ) + if ((*m_xTableTable)[n].GetName() == aFormatName) + break; + + if( n >= m_xTableTable->size() ) + { + sal_uInt8 nIndex = m_nIndex; + + // no format with this name exists, so rename it + m_xLbFormat->remove(m_nDfltStylePos + nIndex); + std::unique_ptr<SwTableAutoFormat> p( + m_xTableTable->ReleaseAutoFormat(nIndex)); + + p->SetName( aFormatName ); + + // keep all arrays sorted! + for( n = 1; n < m_xTableTable->size(); ++n ) + if ((*m_xTableTable)[n].GetName() > aFormatName) + { + break; + } + + m_xTableTable->InsertAutoFormat( n, std::move(p) ); + m_xLbFormat->insert_text(m_nDfltStylePos + n, aFormatName); + m_xLbFormat->select(m_nDfltStylePos + n); + + if ( !m_bCoreDataChanged ) + { + m_xBtnCancel->set_label(m_aStrClose); + m_bCoreDataChanged = true; + } + + SelFormatHdl(*m_xLbFormat); + bOk = true; + bFormatRenamed = true; + } + } + + if( !bFormatRenamed ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Error, VclButtonsType::OkCancel, m_aStrInvalidFormat)); + bOk = RET_CANCEL == xBox->run(); + } + } + else + bOk = true; + } +} + +IMPL_LINK_NOARG(SwAutoFormatDlg, SelFormatHdl, weld::TreeView&, void) +{ + bool bBtnEnable = false; + sal_uInt8 nOldIdx = m_nIndex; + int nSelPos = m_xLbFormat->get_selected_index(); + if (nSelPos >= m_nDfltStylePos) + { + m_nIndex = nSelPos - m_nDfltStylePos; + m_aWndPreview.NotifyChange((*m_xTableTable)[m_nIndex]); + bBtnEnable = 0 != m_nIndex; + UpdateChecks( (*m_xTableTable)[m_nIndex], true ); + } + else + { + m_nIndex = 255; + + SwTableAutoFormat aTmp( SwViewShell::GetShellRes()->aStrNone ); + aTmp.SetFont( false ); + aTmp.SetJustify( false ); + aTmp.SetFrame( false ); + aTmp.SetBackground( false ); + aTmp.SetValueFormat( false ); + aTmp.SetWidthHeight( false ); + + if (nOldIdx != m_nIndex) + m_aWndPreview.NotifyChange(aTmp); + UpdateChecks( aTmp, false ); + } + + m_xBtnRemove->set_sensitive(bBtnEnable); + m_xBtnRename->set_sensitive(bBtnEnable); +} + +short SwAutoFormatDlg::run() +{ + short nRet = SfxDialogController::run(); + if (nRet == RET_OK && m_bSetAutoFormat) + m_pShell->SetTableStyle((*m_xTableTable)[m_nIndex]); + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/uno/swdetect.cxx b/sw/source/ui/uno/swdetect.cxx new file mode 100644 index 000000000..29badcd43 --- /dev/null +++ b/sw/source/ui/uno/swdetect.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 "swdetect.hxx" + +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <sfx2/docfile.hxx> +#include <sot/storage.hxx> +#include <unotools/mediadescriptor.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using utl::MediaDescriptor; + +SwFilterDetect::SwFilterDetect() +{ +} + +SwFilterDetect::~SwFilterDetect() +{ +} + +OUString SAL_CALL SwFilterDetect::detect( Sequence< PropertyValue >& lDescriptor ) +{ + MediaDescriptor aMediaDesc( lDescriptor ); + OUString aTypeName = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME(), OUString() ); + uno::Reference< io::XInputStream > xInStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM()], uno::UNO_QUERY ); + if ( !xInStream.is() ) + return OUString(); + + SfxMedium aMedium; + aMedium.UseInteractionHandler( false ); + aMedium.setStreamToLoadFrom( xInStream, true ); + + SvStream *pInStrm = aMedium.GetInStream(); + if ( !pInStrm || pInStrm->GetError() ) + return OUString(); + + bool bIsDetected = false; + + if ( aTypeName == "writer_Rich_Text_Format" ) + { + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + bIsDetected = ( read_uInt8s_ToOString( *pInStrm, 5 ) == "{\\rtf" ); + } + else if ( aTypeName == "writer_MS_WinWord_5" ) + { + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + const sal_uInt8 nBufSize = 3; + sal_uInt8 nBuffer[ nBufSize ]; + if (pInStrm->ReadBytes(nBuffer, nBufSize) < nBufSize) + return OUString(); + + bIsDetected = (nBuffer[0] == 0x9B && nBuffer[1] == 0xA5 && nBuffer[2] == 0x21) // WinWord 1 + || (nBuffer[0] == 0x9C && nBuffer[1] == 0xA5 && nBuffer[2] == 0x21) // PMWord 1 + || (nBuffer[0] == 0xDB && nBuffer[1] == 0xA5 && nBuffer[2] == 0x2D) // WinWord 2 + || (nBuffer[0] == 0xDC && nBuffer[1] == 0xA5 && nBuffer[2] == 0x65); // WinWord 6.0/95, as a single stream file + } + else + { + // Do not attempt to create an SotStorage on a + // 0-length stream as that would create the compound + // document header on the stream and effectively write to + // disk! + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + if ( pInStrm->remainingSize() == 0 ) + return OUString(); + + try + { + tools::SvRef<SotStorage> aStorage = new SotStorage ( pInStrm, false ); + if ( !aStorage->GetError() ) + { + bIsDetected = aStorage->IsContained( "WordDocument" ); + if ( bIsDetected && aTypeName.startsWith( "writer_MS_Word_97" ) ) + { + bIsDetected = ( aStorage->IsContained("0Table") || aStorage->IsContained("1Table") ); + + // If we are checking the template type, and the document is not a .dot, don't + // mis-detect it. + if ( bIsDetected && aTypeName == "writer_MS_Word_97_Vorlage" ) + { + // Super ugly hack, but we don't want to use the whole WW8Fib thing here in + // the swd library, apparently. We know (do we?) that the "aBits1" byte, as + // the variable is called in WW8Fib::WW8Fib(SvStream&,sal_uInt8,sal_uInt32), + // is at offset 10 in the WordDocument stream. The fDot bit is bit 0x01 of + // that byte. + tools::SvRef<SotStorageStream> xWordDocument = aStorage->OpenSotStream("WordDocument", StreamMode::STD_READ); + xWordDocument->Seek( 10 ); + if ( xWordDocument->Tell() == 10 ) + { + sal_uInt8 aBits1; + xWordDocument->ReadUChar( aBits1 ); + // Check fDot bit + bIsDetected = ((aBits1 & 0x01) == 0x01); + } + } + } + } + } + catch (...) + { + bIsDetected = false; + } + } + + if ( bIsDetected ) + return aTypeName; + + return OUString(); +} + +/* XServiceInfo */ +OUString SAL_CALL SwFilterDetect::getImplementationName() +{ + return "com.sun.star.comp.writer.FormatDetector"; +} + +/* XServiceInfo */ +sal_Bool SAL_CALL SwFilterDetect::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +/* XServiceInfo */ +Sequence< OUString > SAL_CALL SwFilterDetect::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ExtendedTypeDetection", "com.sun.star.text.FormatDetector", "com.sun.star.text.W4WFormatDetector" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_writer_FormatDetector_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwFilterDetect()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/uno/swdetect.hxx b/sw/source/ui/uno/swdetect.hxx new file mode 100644 index 000000000..ce410c97a --- /dev/null +++ b/sw/source/ui/uno/swdetect.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_UNO_SWDETECT_HXX +#define INCLUDED_SW_SOURCE_UI_UNO_SWDETECT_HXX + +#include <rtl/ustring.hxx> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +namespace com +{ + namespace sun + { + namespace star + { + namespace beans + { + struct PropertyValue; + } + } + } +} + +class SfxMedium; +class SfxFilter; +class SfxMedium; + +class SwFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo > +{ +public: + SwFilterDetect(); + virtual ~SwFilterDetect() 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; + + // XExtendedFilterDetect + virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/utlui/swrenamexnameddlg.cxx b/sw/source/ui/utlui/swrenamexnameddlg.cxx new file mode 100644 index 000000000..73993a68f --- /dev/null +++ b/sw/source/ui/utlui/swrenamexnameddlg.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 <osl/diagnose.h> + +#include <swrenamexnameddlg.hxx> + +using namespace ::com::sun::star; + +SwRenameXNamedDlg::SwRenameXNamedDlg(weld::Window* pWin, + uno::Reference< container::XNamed > & xN, + uno::Reference< container::XNameAccess > & xNA ) + : GenericDialogController(pWin, "modules/swriter/ui/renameobjectdialog.ui", "RenameObjectDialog") + , xNamed(xN) + , xNameAccess(xNA) + , m_xNewNameED(m_xBuilder->weld_entry("entry")) + , m_xOk(m_xBuilder->weld_button("ok")) +{ + m_xNewNameED->connect_insert_text(LINK(this, SwRenameXNamedDlg, TextFilterHdl)); + + OUString sTmp(m_xDialog->get_title()); + m_xNewNameED->set_text(xNamed->getName()); + m_xNewNameED->select_region(0, -1); + sTmp += xNamed->getName(); + m_xDialog->set_title(sTmp); + + m_xOk->connect_clicked(LINK(this, SwRenameXNamedDlg, OkHdl)); + m_xNewNameED->connect_changed(LINK(this, SwRenameXNamedDlg, ModifyHdl)); + m_xOk->set_sensitive(false); +} + +IMPL_LINK(SwRenameXNamedDlg, TextFilterHdl, OUString&, rTest, bool) +{ + rTest = m_aTextFilter.filter(rTest); + return true; +} + +IMPL_LINK_NOARG(SwRenameXNamedDlg, OkHdl, weld::Button&, void) +{ + try + { + xNamed->setName(m_xNewNameED->get_text()); + } + catch (const uno::RuntimeException&) + { + OSL_FAIL("name wasn't changed"); + } + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwRenameXNamedDlg, ModifyHdl, weld::Entry&, rEdit, void) +{ + OUString sTmp(rEdit.get_text()); + + m_xOk->set_sensitive(!sTmp.isEmpty() + && !xNameAccess->hasByName(sTmp) + && (!xSecondAccess.is() || !xSecondAccess->hasByName(sTmp)) + && (!xThirdAccess.is() || !xThirdAccess->hasByName(sTmp)) + ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/service.cxx b/sw/source/ui/vba/service.cxx new file mode 100644 index 000000000..43345e302 --- /dev/null +++ b/sw/source/ui/vba/service.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 <comphelper/servicedecl.hxx> +#include <sal/log.hxx> + +#include "service.hxx" + +// component exports + +namespace sdecl = comphelper::service_decl; + +extern "C" SAL_DLLPUBLIC_EXPORT void * vbaswobj_component_getFactory( + const char * pImplName, void *, void *) +{ + void* pRet = sdecl::component_getFactoryHelper(pImplName, + {&globals::serviceDecl, &::document::serviceDecl, + &wrapformat::serviceDecl, &vbaeventshelper::serviceDecl} ); + SAL_INFO("sw.vba", "Ret is " << pRet); + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/service.hxx b/sw/source/ui/vba/service.hxx new file mode 100644 index 000000000..3ed37ff0f --- /dev/null +++ b/sw/source/ui/vba/service.hxx @@ -0,0 +1,35 @@ +/* -*- 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_UI_VBA_SERVICE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_SERVICE_HXX + +#include <sal/config.h> + +namespace comphelper::service_decl { class ServiceDecl; } + +namespace document { +extern comphelper::service_decl::ServiceDecl const serviceDecl; +} + +namespace globals { +extern comphelper::service_decl::ServiceDecl const serviceDecl; +} + +namespace vbaeventshelper { +extern comphelper::service_decl::ServiceDecl const serviceDecl; +} + +namespace wrapformat { +extern comphelper::service_decl::ServiceDecl const serviceDecl; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/ui/vba/vbaaddin.cxx b/sw/source/ui/vba/vbaaddin.cxx new file mode 100644 index 000000000..b3a424d6a --- /dev/null +++ b/sw/source/ui/vba/vbaaddin.cxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaaddin.hxx" +#include <vbahelper/vbahelper.hxx> +#include <tools/urlobj.hxx> +#include <osl/file.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaAddin::SwVbaAddin( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const OUString& rFileURL ) : + SwVbaAddin_BASE( rParent, rContext ), msFileURL( rFileURL ), mbInstalled( true ) +{ +} + +SwVbaAddin::~SwVbaAddin() +{ +} + +OUString SAL_CALL SwVbaAddin::getName() +{ + OUString sName; + INetURLObject aURL( msFileURL ); + ::osl::File::getSystemPathFromFileURL( aURL.GetLastName(), sName ); + return sName; +} + +void SAL_CALL +SwVbaAddin::setName( const OUString& ) +{ + throw uno::RuntimeException(" Fail to set name" ); +} + +OUString SAL_CALL SwVbaAddin::getPath() +{ + INetURLObject aURL( msFileURL ); + aURL.CutLastName(); + return aURL.GetURLPath(); +} + +sal_Bool SAL_CALL SwVbaAddin::getAutoload() +{ + return true; +} + +sal_Bool SAL_CALL SwVbaAddin::getInstalled() +{ + return mbInstalled; +} + +void SAL_CALL SwVbaAddin::setInstalled( sal_Bool _installed ) +{ + if( bool(_installed) != mbInstalled ) + { + mbInstalled = _installed; + // TODO: should call AutoExec and AutoExit etc. + } +} + +OUString +SwVbaAddin::getServiceImplName() +{ + return "SwVbaAddin"; +} + +uno::Sequence< OUString > +SwVbaAddin::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Addin" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaaddin.hxx b/sw/source/ui/vba/vbaaddin.hxx new file mode 100644 index 000000000..7b66b6ba6 --- /dev/null +++ b/sw/source/ui/vba/vbaaddin.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_UI_VBA_VBAADDIN_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAADDIN_HXX + +#include <ooo/vba/word/XAddin.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XAddin > SwVbaAddin_BASE; + +class SwVbaAddin : public SwVbaAddin_BASE +{ +private: + OUString msFileURL; + bool mbInstalled; + +public: + /// @throws css::uno::RuntimeException + SwVbaAddin( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const OUString& rFileURL ); + virtual ~SwVbaAddin() override; + + // Attributes + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName( const OUString& _name ) override; + virtual OUString SAL_CALL getPath() override; + virtual sal_Bool SAL_CALL getAutoload() override; + virtual sal_Bool SAL_CALL getInstalled() override; + virtual void SAL_CALL setInstalled( sal_Bool _installed ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAADDIN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaaddins.cxx b/sw/source/ui/vba/vbaaddins.cxx new file mode 100644 index 000000000..40fc57744 --- /dev/null +++ b/sw/source/ui/vba/vbaaddins.cxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaaddins.hxx" +#include "vbaaddin.hxx" +#include <unotools/pathoptions.hxx> +#include <sal/log.hxx> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static uno::Reference< container::XIndexAccess > lcl_getAddinCollection( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext ) +{ + XNamedObjectCollectionHelper< word::XAddin >::XNamedVec aAddins; + + // first get the autoload addins in the directory STARTUP + uno::Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager(), uno::UNO_SET_THROW ); + uno::Reference<ucb::XSimpleFileAccess3> xSFA(ucb::SimpleFileAccess::create(xContext)); + SvtPathOptions aPathOpt; + // FIXME: temporary the STARTUP path is located in $OO/basic3.1/program/addin + const OUString& aAddinPath = aPathOpt.GetAddinPath(); + SAL_INFO("sw.vba", "lcl_getAddinCollection: " << aAddinPath ); + if( xSFA->isFolder( aAddinPath ) ) + { + const uno::Sequence< OUString > sEntries = xSFA->getFolderContents( aAddinPath, false ); + for( const OUString& sUrl : sEntries ) + { + if( !xSFA->isFolder( sUrl ) && sUrl.endsWithIgnoreAsciiCase( ".dot" ) ) + { + aAddins.push_back( uno::Reference< word::XAddin >( new SwVbaAddin( xParent, xContext, sUrl ) ) ); + } + } + } + + // TODO: second get the customize addins in the org.openoffice.Office.Writer/GlobalTemplateList + + uno::Reference< container::XIndexAccess > xAddinsAccess( new XNamedObjectCollectionHelper< word::XAddin >( aAddins ) ); + return xAddinsAccess; +} + +SwVbaAddins::SwVbaAddins( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext ): SwVbaAddins_BASE( xParent, xContext, lcl_getAddinCollection( xParent,xContext ) ) +{ +} +// XEnumerationAccess +uno::Type +SwVbaAddins::getElementType() +{ + return cppu::UnoType<word::XAddin>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaAddins::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumerationAccess->createEnumeration(); +} + +uno::Any +SwVbaAddins::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaAddins::getServiceImplName() +{ + return "SwVbaAddins"; +} + +css::uno::Sequence<OUString> +SwVbaAddins::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Addins" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaaddins.hxx b/sw/source/ui/vba/vbaaddins.hxx new file mode 100644 index 000000000..67a9edb3c --- /dev/null +++ b/sw/source/ui/vba/vbaaddins.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAADDINS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAADDINS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XAddins.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XAddins > SwVbaAddins_BASE; + +class SwVbaAddins : public SwVbaAddins_BASE +{ +public: + /// @throws css::uno::RuntimeException + SwVbaAddins( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaAddins_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAADDINS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaapplication.cxx b/sw/source/ui/vba/vbaapplication.cxx new file mode 100644 index 000000000..c2e01d75f --- /dev/null +++ b/sw/source/ui/vba/vbaapplication.cxx @@ -0,0 +1,776 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/task/XStatusIndicatorSupplier.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/util/thePathSettings.hpp> + +#include "vbaapplication.hxx" +#include "vbadocument.hxx" +#include "vbafilterpropsfromformat.hxx" +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <vcl/svapp.hxx> +#include <vbahelper/vbahelper.hxx> +#include "vbawindow.hxx" +#include "vbasystem.hxx" +#include "vbaoptions.hxx" +#include "vbaselection.hxx" +#include "vbadocuments.hxx" +#include "vbaaddins.hxx" +#include "vbamailmerge.hxx" +#include "vbadialogs.hxx" +#include <ooo/vba/XConnectionPoint.hpp> +#include <ooo/vba/word/WdEnableCancelKey.hpp> +#include <ooo/vba/word/WdWindowState.hpp> +#include <ooo/vba/word/XApplicationOutgoing.hpp> +#include <ooo/vba/word/XBookmarks.hpp> +#include <comphelper/processfactory.hxx> +#include <editeng/acorrcfg.hxx> +#include <swdll.hxx> +#include <swmodule.hxx> +#include "vbalistgalleries.hxx" +#include <tools/urlobj.hxx> + +using namespace ::ooo; +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class SwVbaApplicationOutgoingConnectionPoint : public cppu::WeakImplHelper<XConnectionPoint> +{ +private: + SwVbaApplication* mpApp; + +public: + SwVbaApplicationOutgoingConnectionPoint( SwVbaApplication* pApp ); + + // XConnectionPoint + sal_uInt32 SAL_CALL Advise(const uno::Reference< XSink >& Sink ) override; + void SAL_CALL Unadvise( sal_uInt32 Cookie ) override; +}; + +class SwWordBasic : public cppu::WeakImplHelper<word::XWordBasic> +{ +private: + SwVbaApplication* mpApp; + +public: + SwWordBasic( SwVbaApplication* pApp ); + + // XWordBasic + virtual sal_Int32 SAL_CALL getMailMergeMainDocumentType() override; + virtual void SAL_CALL setMailMergeMainDocumentType( sal_Int32 _mailmergemaindocumenttype ) override; + + virtual void SAL_CALL FileOpen( const OUString& Name, const uno::Any& ConfirmConversions, const uno::Any& ReadOnly, const uno::Any& AddToMru, const uno::Any& PasswordDoc, const uno::Any& PasswordDot, const uno::Any& Revert, const uno::Any& WritePasswordDoc, const uno::Any& WritePasswordDot ) override; + virtual void SAL_CALL FileSave() override; + virtual void SAL_CALL FileSaveAs( const css::uno::Any& Name, + const css::uno::Any& Format, + const css::uno::Any& LockAnnot, + const css::uno::Any& Password, + const css::uno::Any& AddToMru, + const css::uno::Any& WritePassword, + const css::uno::Any& RecommendReadOnly, + const css::uno::Any& EmbedFonts, + const css::uno::Any& NativePictureFormat, + const css::uno::Any& FormsData, + const css::uno::Any& SaveAsAOCELetter ) override; + virtual void SAL_CALL FileClose( const css::uno::Any& Save ) override; + virtual void SAL_CALL ToolsOptionsView( const css::uno::Any& DraftFont, + const css::uno::Any& WrapToWindow, + const css::uno::Any& PicturePlaceHolders, + const css::uno::Any& FieldCodes, + const css::uno::Any& BookMarks, + const css::uno::Any& FieldShading, + const css::uno::Any& StatusBar, + const css::uno::Any& HScroll, + const css::uno::Any& VScroll, + const css::uno::Any& StyleAreaWidth, + const css::uno::Any& Tabs, + const css::uno::Any& Spaces, + const css::uno::Any& Paras, + const css::uno::Any& Hyphens, + const css::uno::Any& Hidden, + const css::uno::Any& ShowAll, + const css::uno::Any& Drawings, + const css::uno::Any& Anchors, + const css::uno::Any& TextBoundaries, + const css::uno::Any& VRuler, + const css::uno::Any& Highlight ) override; + virtual css::uno::Any SAL_CALL WindowName( const css::uno::Any& Number ) override; + virtual css::uno::Any SAL_CALL ExistingBookmark( const OUString& Name ) override; + virtual void SAL_CALL MailMergeOpenDataSource(const OUString& Name, const css::uno::Any& Format, + const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, + const css::uno::Any& LinkToSource, const css::uno::Any& AddToRecentFiles, + const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, + const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, + const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Connection, + const css::uno::Any& SQLStatement, const css::uno::Any& SQLStatement1, + const css::uno::Any& OpenExclusive, const css::uno::Any& SubType) override; + virtual css::uno::Any SAL_CALL AppMaximize( const css::uno::Any& WindowName, const css::uno::Any& State ) override; + virtual css::uno::Any SAL_CALL DocMaximize( const css::uno::Any& State ) override; + virtual void SAL_CALL AppShow( const css::uno::Any& WindowName ) override; + virtual css::uno::Any SAL_CALL AppCount() override; +}; + +} + +SwVbaApplication::SwVbaApplication( uno::Reference<uno::XComponentContext >& xContext ): + SwVbaApplication_BASE( xContext ) +{ +} + +SwVbaApplication::~SwVbaApplication() +{ +} + +sal_uInt32 +SwVbaApplication::AddSink( const uno::Reference< XSink >& xSink ) +{ + { + SolarMutexGuard aGuard; + SwGlobals::ensure(); + } + // No harm in potentially calling this several times + SW_MOD()->RegisterAutomationApplicationEventsCaller( uno::Reference< XSinkCaller >(this) ); + mvSinks.push_back(xSink); + return mvSinks.size(); +} + +void +SwVbaApplication::RemoveSink( sal_uInt32 nNumber ) +{ + if (nNumber < 1 || nNumber > mvSinks.size()) + return; + + mvSinks[nNumber-1] = uno::Reference< XSink >(); +} + +OUString SAL_CALL +SwVbaApplication::getName() +{ + return "Microsoft Word"; +} + +uno::Reference< word::XDocument > SAL_CALL +SwVbaApplication::getActiveDocument() +{ + return new SwVbaDocument( this, mxContext, getCurrentDocument() ); +} + +SwVbaWindow * +SwVbaApplication::getActiveSwVbaWindow() +{ + // #FIXME so far can't determine Parent + uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW ); + uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW ); + return new SwVbaWindow( uno::Reference< XHelperInterface >(), mxContext, xModel, xController ); +} + +uno::Reference< css::uno::XComponentContext > const & +SwVbaApplication::getContext() const +{ + return mxContext; +} + +uno::Reference< word::XWindow > SAL_CALL +SwVbaApplication::getActiveWindow() +{ + return getActiveSwVbaWindow(); +} + +uno::Reference<word::XSystem > SAL_CALL +SwVbaApplication::getSystem() +{ + return uno::Reference< word::XSystem >( new SwVbaSystem( mxContext ) ); +} + +uno::Reference<word::XOptions > SAL_CALL +SwVbaApplication::getOptions() +{ + return uno::Reference< word::XOptions >( new SwVbaOptions( mxContext ) ); +} + +uno::Any SAL_CALL +SwVbaApplication::CommandBars( const uno::Any& aIndex ) +{ + try + { + return VbaApplicationBase::CommandBars( aIndex ); + } + catch (const uno::RuntimeException&) + { + return uno::Any(); + } +} + +uno::Reference< word::XSelection > SAL_CALL +SwVbaApplication::getSelection() +{ + return new SwVbaSelection( this, mxContext, getCurrentDocument() ); +} + +uno::Reference< word::XWordBasic > SAL_CALL +SwVbaApplication::getWordBasic() +{ + uno::Reference< word::XWordBasic > xWB( new SwWordBasic( this ) ); + return xWB; +} + +uno::Any SAL_CALL +SwVbaApplication::Documents( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaDocuments( this, mxContext ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaApplication::Addins( const uno::Any& index ) +{ + static uno::Reference< XCollection > xCol( new SwVbaAddins( this, mxContext ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaApplication::Dialogs( const uno::Any& index ) +{ + uno::Reference< word::XDialogs > xCol( new SwVbaDialogs( this, mxContext, getCurrentDocument() )); + if ( index.hasValue() ) + return xCol->Item( index ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaApplication::ListGalleries( const uno::Any& index ) +{ + uno::Reference< text::XTextDocument > xTextDoc( getCurrentDocument(), uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaListGalleries( this, mxContext, xTextDoc ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +sal_Bool SAL_CALL SwVbaApplication::getDisplayAutoCompleteTips() +{ + return SvxAutoCorrCfg::Get().IsAutoTextTip(); +} + +void SAL_CALL SwVbaApplication::setDisplayAutoCompleteTips( sal_Bool _displayAutoCompleteTips ) +{ + SvxAutoCorrCfg::Get().SetAutoTextTip( _displayAutoCompleteTips ); +} + +sal_Int32 SAL_CALL SwVbaApplication::getEnableCancelKey() +{ + // the default value is wdCancelInterrupt in Word + return word::WdEnableCancelKey::wdCancelInterrupt; +} + +void SAL_CALL SwVbaApplication::setEnableCancelKey( sal_Int32/* _enableCancelKey */) +{ + // seems not supported in Writer +} + +sal_Int32 SAL_CALL SwVbaApplication::getWindowState() +{ + auto xWindow = getActiveWindow(); + if (xWindow.is()) + { + uno::Any aState = xWindow->getWindowState(); + sal_Int32 nState; + if (aState >>= nState) + return nState; + } + + return word::WdWindowState::wdWindowStateNormal; // ? +} + +void SAL_CALL SwVbaApplication::setWindowState( sal_Int32 _windowstate ) +{ + try + { + auto xWindow = getActiveWindow(); + if (xWindow.is()) + { + uno::Any aState; + aState <<= _windowstate; + xWindow->setWindowState( aState ); + } + } + catch (const uno::RuntimeException&) + { + } +} + +sal_Int32 SAL_CALL SwVbaApplication::getWidth() +{ + auto pWindow = getActiveSwVbaWindow(); + return pWindow->getWidth(); +} + +void SAL_CALL SwVbaApplication::setWidth( sal_Int32 _width ) +{ + auto pWindow = getActiveSwVbaWindow(); + pWindow->setWidth( _width ); +} + +sal_Int32 SAL_CALL SwVbaApplication::getHeight() +{ + auto pWindow = getActiveSwVbaWindow(); + return pWindow->getHeight(); +} + +void SAL_CALL SwVbaApplication::setHeight( sal_Int32 _height ) +{ + auto pWindow = getActiveSwVbaWindow(); + pWindow->setHeight( _height ); +} + +sal_Int32 SAL_CALL SwVbaApplication::getLeft() +{ + auto pWindow = getActiveSwVbaWindow(); + return pWindow->getLeft(); +} + +void SAL_CALL SwVbaApplication::setLeft( sal_Int32 _left ) +{ + auto pWindow = getActiveSwVbaWindow(); + pWindow->setLeft( _left ); +} + +sal_Int32 SAL_CALL SwVbaApplication::getTop() +{ + auto pWindow = getActiveSwVbaWindow(); + return pWindow->getTop(); +} + +void SAL_CALL SwVbaApplication::setTop( sal_Int32 _top ) +{ + auto pWindow = getActiveSwVbaWindow(); + pWindow->setTop( _top ); +} + +OUString SAL_CALL SwVbaApplication::getStatusBar() +{ + return ""; +} + +uno::Any SAL_CALL SwVbaApplication::getCustomizationContext() +{ + return uno::Any(); // ??? +} + +void SAL_CALL SwVbaApplication::setCustomizationContext(const uno::Any& /*_customizationcontext*/) +{ + // ??? +} + +void SAL_CALL SwVbaApplication::setStatusBar( const OUString& _statusbar ) +{ + // ScVbaAppSettings::setStatusBar() also uses the XStatusIndicator to show this, so maybe that is OK? + uno::Reference< frame::XModel > xModel = getCurrentDocument(); + if (xModel.is()) + { + uno::Reference< task::XStatusIndicatorSupplier > xStatusIndicatorSupplier( xModel->getCurrentController(), uno::UNO_QUERY ); + if (xStatusIndicatorSupplier.is()) + { + uno::Reference< task::XStatusIndicator > xStatusIndicator = xStatusIndicatorSupplier->getStatusIndicator(); + if (xStatusIndicator.is()) + xStatusIndicator->start( _statusbar, 100 ); + } + } + + // Yes, we intentionally use the "extensions.olebridge" tag here even if this is sw. We + // interpret setting the StatusBar property as a request from an Automation client to display + // the string in LibreOffice's debug output, and all other generic Automation support debug + // output (in extensions/source/ole) uses that tag. If the check for "cross-module" or mixed log + // areas in compilerplugins/clang/sallogareas.cxx is re-activated, this will have to be added as + // a special case. + + SAL_INFO("extensions.olebridge", "Client debug output: " << _statusbar); +} + +float SAL_CALL SwVbaApplication::CentimetersToPoints( float Centimeters ) +{ + return VbaApplicationBase::CentimetersToPoints( Centimeters ); +} + +void SAL_CALL SwVbaApplication::ShowMe() +{ + // No idea what we should or could do +} + +void SAL_CALL SwVbaApplication::Resize( sal_Int32 Width, sal_Int32 Height ) +{ + // Have to do it like this as the Width and Height are hidden away in the ooo::vba::XWindowBase + // which ooo::vba::word::XApplication does not inherit from. SwVbaWindow, however, does inherit + // from XWindowBase. Ugh. + auto pWindow = getActiveSwVbaWindow(); + pWindow->setWidth( Width ); + pWindow->setHeight( Height ); +} + +void SAL_CALL SwVbaApplication::Move( sal_Int32 Left, sal_Int32 Top ) +{ + // See comment in Resize(). + auto pWindow = getActiveSwVbaWindow(); + pWindow->setLeft( Left ); + pWindow->setTop( Top ); +} + +// XInterfaceWithIID + +OUString SAL_CALL +SwVbaApplication::getIID() +{ + return "{82154421-0FBF-11d4-8313-005004526AB4}"; +} + +// XConnectable + +OUString SAL_CALL +SwVbaApplication::GetIIDForClassItselfNotCoclass() +{ + return "{82154423-0FBF-11D4-8313-005004526AB4}"; +} + +TypeAndIID SAL_CALL +SwVbaApplication::GetConnectionPoint() +{ + TypeAndIID aResult = + { word::XApplicationOutgoing::static_type(), + "{82154422-0FBF-11D4-8313-005004526AB4}" + }; + + return aResult; +} + +uno::Reference<XConnectionPoint> SAL_CALL +SwVbaApplication::FindConnectionPoint() +{ + uno::Reference<XConnectionPoint> xCP(new SwVbaApplicationOutgoingConnectionPoint(this)); + return xCP; +} + +OUString +SwVbaApplication::getServiceImplName() +{ + return "SwVbaApplication"; +} + +uno::Sequence< OUString > +SwVbaApplication::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Application" + }; + return aServiceNames; +} + +uno::Reference< frame::XModel > +SwVbaApplication::getCurrentDocument() +{ + return getCurrentWordDoc( mxContext ); +} + +// XSinkCaller + +void SAL_CALL +SwVbaApplication::CallSinks( const OUString& Method, uno::Sequence< uno::Any >& Arguments ) +{ + for (auto& i : mvSinks) + { + if (i.is()) + i->Call(Method, Arguments); + } +} + +// SwVbaApplicationOutgoingConnectionPoint + +SwVbaApplicationOutgoingConnectionPoint::SwVbaApplicationOutgoingConnectionPoint( SwVbaApplication* pApp ) : + mpApp(pApp) +{ +} + +// XConnectionPoint +sal_uInt32 SAL_CALL +SwVbaApplicationOutgoingConnectionPoint::Advise( const uno::Reference< XSink >& Sink ) +{ + return mpApp->AddSink(Sink); +} + +void SAL_CALL +SwVbaApplicationOutgoingConnectionPoint::Unadvise( sal_uInt32 Cookie ) +{ + mpApp->RemoveSink( Cookie ); +} + +// SwWordBasic + +SwWordBasic::SwWordBasic( SwVbaApplication* pApp ) : + mpApp(pApp) +{ +} + +// XWordBasic +sal_Int32 SAL_CALL +SwWordBasic::getMailMergeMainDocumentType() +{ + return SwVbaMailMerge::get( mpApp->getParent(), mpApp->getContext() )->getMainDocumentType(); +} + +// XWordBasic +void SAL_CALL +SwWordBasic::setMailMergeMainDocumentType( sal_Int32 _mailmergemaindocumenttype ) +{ + SwVbaMailMerge::get( mpApp->getParent(), mpApp->getContext() )->setMainDocumentType( _mailmergemaindocumenttype ); +} + +void SAL_CALL +SwWordBasic::FileOpen( const OUString& Name, const uno::Any& ConfirmConversions, const uno::Any& ReadOnly, const uno::Any& AddToMru, const uno::Any& PasswordDoc, const uno::Any& PasswordDot, const uno::Any& Revert, const uno::Any& WritePasswordDoc, const uno::Any& WritePasswordDot ) +{ + uno::Any aDocuments = mpApp->Documents( uno::Any() ); + + uno::Reference<word::XDocuments> rDocuments; + + if (aDocuments >>= rDocuments) + rDocuments->Open( Name, ConfirmConversions, ReadOnly, AddToMru, PasswordDoc, PasswordDot, Revert, WritePasswordDoc, WritePasswordDot, uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any() ); +} + +void SAL_CALL +SwWordBasic::FileSave() +{ + uno::Reference< frame::XModel > xModel( mpApp->getCurrentDocument(), uno::UNO_SET_THROW ); + dispatchRequests(xModel,".uno:Save"); +} + +void SAL_CALL +SwWordBasic::FileSaveAs( const css::uno::Any& Name, + const css::uno::Any& Format, + const css::uno::Any& /*LockAnnot*/, + const css::uno::Any& /*Password*/, + const css::uno::Any& /*AddToMru*/, + const css::uno::Any& /*WritePassword*/, + const css::uno::Any& /*RecommendReadOnly*/, + const css::uno::Any& /*EmbedFonts*/, + const css::uno::Any& /*NativePictureFormat*/, + const css::uno::Any& /*FormsData*/, + const css::uno::Any& /*SaveAsAOCELetter*/ ) +{ + SAL_INFO("sw.vba", "WordBasic.FileSaveAs(Name:=" << Name << ",Format:=" << Format << ")"); + + uno::Reference< frame::XModel > xModel( mpApp->getCurrentDocument(), uno::UNO_SET_THROW ); + + // Based on SwVbaDocument::SaveAs2000. + + OUString sFileName; + Name >>= sFileName; + + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( sFileName, sURL ); + + // Detect if there is no path then we need to use the current folder. + INetURLObject aURL( sURL ); + sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + if( sURL.isEmpty() ) + { + // Need to add cur dir ( of this document ) or else the 'Work' dir + sURL = xModel->getURL(); + + if ( sURL.isEmpty() ) + { + // Not path available from 'this' document. Need to add the 'document'/work directory then. + // Based on SwVbaOptions::getValueEvent() + uno::Reference< util::XPathSettings > xPathSettings = util::thePathSettings::get( comphelper::getProcessComponentContext() ); + OUString sPathUrl; + xPathSettings->getPropertyValue( "Work" ) >>= sPathUrl; + // Path could be a multipath, Microsoft doesn't support this feature in Word currently. + // Only the last path is from interest. + // No idea if this crack is relevant for WordBasic or not. + sal_Int32 nIndex = sPathUrl.lastIndexOf( ';' ); + if( nIndex != -1 ) + { + sPathUrl = sPathUrl.copy( nIndex + 1 ); + } + + aURL.SetURL( sPathUrl ); + } + else + { + aURL.SetURL( sURL ); + aURL.Append( sFileName ); + } + sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + + } + sal_Int32 nFileFormat = word::WdSaveFormat::wdFormatDocument; + Format >>= nFileFormat; + + uno::Sequence< beans::PropertyValue > aProps(2); + aProps[0].Name = "FilterName"; + + setFilterPropsFromFormat( nFileFormat, aProps ); + + aProps[1].Name = "FileName"; + aProps[1].Value <<= sURL; + + dispatchRequests(xModel,".uno:SaveAs",aProps); +} + +void SAL_CALL +SwWordBasic::FileClose( const css::uno::Any& Save ) +{ + uno::Reference< frame::XModel > xModel( mpApp->getCurrentDocument(), uno::UNO_SET_THROW ); + + sal_Int16 nSave = 0; + if (Save.hasValue() && (Save >>= nSave) && (nSave == 0 || nSave == 1)) + FileSave(); + + // FIXME: Here I would much prefer to call VbaDocumentBase::Close() but not sure how to get at + // the VbaDocumentBase of the current document. (Probably it is easy and I haven't looked hard + // enough.) + // + // FIXME: Error handling. If there is no current document, return some kind of error? But for + // now, just ignore errors. This code is written to work for a very specific customer use case + // anyway, not for an arbitrary sequence of COM calls to the "VBA" API. + dispatchRequests(xModel,".uno:CloseDoc"); +} + +void SAL_CALL +SwWordBasic::ToolsOptionsView( const css::uno::Any& DraftFont, + const css::uno::Any& WrapToWindow, + const css::uno::Any& PicturePlaceHolders, + const css::uno::Any& FieldCodes, + const css::uno::Any& BookMarks, + const css::uno::Any& FieldShading, + const css::uno::Any& StatusBar, + const css::uno::Any& HScroll, + const css::uno::Any& VScroll, + const css::uno::Any& StyleAreaWidth, + const css::uno::Any& Tabs, + const css::uno::Any& Spaces, + const css::uno::Any& Paras, + const css::uno::Any& Hyphens, + const css::uno::Any& Hidden, + const css::uno::Any& ShowAll, + const css::uno::Any& Drawings, + const css::uno::Any& Anchors, + const css::uno::Any& TextBoundaries, + const css::uno::Any& VRuler, + const css::uno::Any& Highlight ) +{ + SAL_INFO("sw.vba", "WordBasic.ToolsOptionsView(" + "DraftFont:=" << DraftFont + << ", WrapToWindow:=" << WrapToWindow + << ", PicturePlaceHolders:=" << PicturePlaceHolders + << ", FieldCodes:=" << FieldCodes + << ", BookMarks:=" << BookMarks + << ", FieldShading:=" << FieldShading + << ", StatusBar:=" << StatusBar + << ", HScroll:=" << HScroll + << ", VScroll:=" << VScroll + << ", StyleAreaWidth:=" << StyleAreaWidth + << ", Tabs:=" << Tabs + << ", Spaces:=" << Spaces + << ", Paras:=" << Paras + << ", Hyphens:=" << Hyphens + << ", Hidden:=" << Hidden + << ", ShowAll:=" << ShowAll + << ", Drawings:=" << Drawings + << ", Anchors:=" << Anchors + << ", TextBoundaries:=" << TextBoundaries + << ", VRuler:=" << VRuler + << ", Highlight:=" << Highlight + << ")"); +} + +css::uno::Any SAL_CALL +SwWordBasic::WindowName( const css::uno::Any& /*Number*/ ) +{ + return css::uno::makeAny( mpApp->getActiveSwVbaWindow()->getCaption() ); +} + +css::uno::Any SAL_CALL +SwWordBasic::ExistingBookmark( const OUString& Name ) +{ + uno::Reference< word::XBookmarks > xBookmarks( mpApp->getActiveDocument()->Bookmarks( uno::Any() ), uno::UNO_QUERY ); + return css::uno::makeAny( xBookmarks.is() && xBookmarks->Exists( Name ) ); +} + +void SAL_CALL +SwWordBasic::MailMergeOpenDataSource( const OUString& Name, const css::uno::Any& Format, + const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, + const css::uno::Any& LinkToSource, const css::uno::Any& AddToRecentFiles, + const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, + const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, + const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Connection, + const css::uno::Any& SQLStatement, const css::uno::Any& SQLStatement1, + const css::uno::Any& OpenExclusive, const css::uno::Any& SubType ) +{ + mpApp->getActiveDocument()->getMailMerge()->OpenDataSource( Name, Format, ConfirmConversions, ReadOnly, + LinkToSource, AddToRecentFiles, + PasswordDocument, PasswordTemplate, + Revert, WritePasswordDocument, + WritePasswordTemplate, Connection, + SQLStatement, SQLStatement1, + OpenExclusive, SubType ); +} + +css::uno::Any SAL_CALL +SwWordBasic::AppMaximize( const css::uno::Any& WindowName, const css::uno::Any& State ) +{ + SAL_INFO("sw.vba", "WordBasic.AppMaximize( WindowName:=" << WindowName << ", State:=" << State); + + // FIXME: Implement if necessary + return css::uno::makeAny( sal_Int32(0) ); +} + +css::uno::Any SAL_CALL +SwWordBasic::DocMaximize( const css::uno::Any& State ) +{ + SAL_INFO("sw.vba", "WordBasic.DocMaximize(State:=" << State << ")"); + + // FIXME: Implement if necessary + return css::uno::makeAny( sal_Int32(0) ); +} + +void SAL_CALL +SwWordBasic::AppShow( const css::uno::Any& WindowName ) +{ + SAL_INFO("sw.vba", "WordBasic.AppShow(WindowName:=" << WindowName << ")"); + + // FIXME: Implement if necessary +} + +css::uno::Any SAL_CALL +SwWordBasic::AppCount() +{ + SAL_INFO("sw.vba", "WordBasic.AppCount()"); + + // FIXME: Implement if necessary. Return a random number for now. + return css::uno::makeAny( sal_Int32(2) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaapplication.hxx b/sw/source/ui/vba/vbaapplication.hxx new file mode 100644 index 000000000..77c58e5d8 --- /dev/null +++ b/sw/source/ui/vba/vbaapplication.hxx @@ -0,0 +1,116 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_VBA_VBAAPPLICATION_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAAPPLICATION_HXX + +#include <vector> + +#include <ooo/vba/XSink.hpp> +#include <ooo/vba/XSinkCaller.hpp> +#include <ooo/vba/word/XApplication.hpp> +#include <ooo/vba/word/XDocument.hpp> +#include <ooo/vba/word/XWindow.hpp> +#include <ooo/vba/word/XSystem.hpp> +#include <ooo/vba/word/XOptions.hpp> +#include <ooo/vba/word/XSelection.hpp> +#include <vbahelper/vbaapplicationbase.hxx> +#include <cppuhelper/implbase.hxx> + +#include "vbawindow.hxx" + +typedef cppu::ImplInheritanceHelper< VbaApplicationBase, ooo::vba::word::XApplication, ooo::vba::XSinkCaller > SwVbaApplication_BASE; + +// This class is currently not a singleton. One instance is created per document with (potential?) +// StarBasic code in it, I think, and a shared one for all Automation clients connected to the +// ooo::vba::word::Application (Writer.Application) service. (Of course it probably is not common to +// have several Automation clients at once.) + +// Should it be a true singleton? Hard to say. Anyway, it is actually the SwVbaGlobals class that +// should be a singleton in that case, I think. + +class SwVbaApplication : public SwVbaApplication_BASE +{ + std::vector<css::uno::Reference< ooo::vba::XSink >> mvSinks; + +public: + explicit SwVbaApplication( css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~SwVbaApplication() override; + + sal_uInt32 AddSink( const css::uno::Reference< ooo::vba::XSink >& xSink ); + void RemoveSink( sal_uInt32 nNumber ); + + SwVbaWindow* getActiveSwVbaWindow(); + css::uno::Reference< css::uno::XComponentContext > const & getContext() const; + + // XApplication + virtual OUString SAL_CALL getName() override; + virtual css::uno::Reference< ooo::vba::word::XSystem > SAL_CALL getSystem() override; + virtual css::uno::Reference< ov::word::XDocument > SAL_CALL getActiveDocument() override; + virtual css::uno::Reference< ov::word::XWindow > SAL_CALL getActiveWindow() override; + virtual css::uno::Reference< ooo::vba::word::XOptions > SAL_CALL getOptions() override; + virtual css::uno::Reference< ooo::vba::word::XSelection > SAL_CALL getSelection() override; + virtual css::uno::Reference< ooo::vba::word::XWordBasic > SAL_CALL getWordBasic() override; + virtual css::uno::Any SAL_CALL CommandBars( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Documents( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Addins( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Dialogs( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL ListGalleries( const css::uno::Any& aIndex ) override; + virtual sal_Bool SAL_CALL getDisplayAutoCompleteTips() override; + virtual void SAL_CALL setDisplayAutoCompleteTips( sal_Bool _displayAutoCompleteTips ) override; + virtual sal_Int32 SAL_CALL getEnableCancelKey() override; + virtual void SAL_CALL setEnableCancelKey( sal_Int32 _enableCancelKey ) override; + virtual sal_Int32 SAL_CALL getWindowState() override; + virtual void SAL_CALL setWindowState( sal_Int32 _windowstate ) override; + virtual sal_Int32 SAL_CALL getWidth() override; + virtual void SAL_CALL setWidth( sal_Int32 _width ) override; + virtual sal_Int32 SAL_CALL getHeight() override; + virtual void SAL_CALL setHeight( sal_Int32 _height ) override; + virtual sal_Int32 SAL_CALL getLeft() override; + virtual void SAL_CALL setLeft( sal_Int32 _left ) override; + virtual sal_Int32 SAL_CALL getTop() override; + virtual void SAL_CALL setTop( sal_Int32 _top ) override; + virtual OUString SAL_CALL getStatusBar() override; + virtual void SAL_CALL setStatusBar( const OUString& _statusbar ) override; + virtual css::uno::Any SAL_CALL getCustomizationContext() override; + virtual void SAL_CALL setCustomizationContext( const css::uno::Any& _customizationcontext ) override; + virtual float SAL_CALL CentimetersToPoints( float Centimeters ) override; + virtual void SAL_CALL ShowMe() override; + virtual void SAL_CALL Resize( sal_Int32 Width, sal_Int32 Height ) override; + virtual void SAL_CALL Move( sal_Int32 Left, sal_Int32 Top ) override; + + // XInterfaceWithIID + virtual OUString SAL_CALL getIID() override; + + // XConnectable + virtual OUString SAL_CALL GetIIDForClassItselfNotCoclass() override; + virtual ov::TypeAndIID SAL_CALL GetConnectionPoint() override; + virtual css::uno::Reference<ov::XConnectionPoint> SAL_CALL FindConnectionPoint() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + + // XSinkCaller + virtual void SAL_CALL CallSinks( const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments ) override; + + virtual css::uno::Reference< css::frame::XModel > getCurrentDocument() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAAPPLICATION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaautotextentry.cxx b/sw/source/ui/vba/vbaautotextentry.cxx new file mode 100644 index 000000000..89165585c --- /dev/null +++ b/sw/source/ui/vba/vbaautotextentry.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 "vbaautotextentry.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/text/XParagraphCursor.hpp> +#include "wordvbahelper.hxx" +#include "vbarange.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaAutoTextEntry::SwVbaAutoTextEntry( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XAutoTextEntry >& xEntry ) : + SwVbaAutoTextEntry_BASE( rParent, rContext ), mxEntry( xEntry ) +{ +} + +SwVbaAutoTextEntry::~SwVbaAutoTextEntry() +{ +} + +uno::Reference< word::XRange > SAL_CALL SwVbaAutoTextEntry::Insert( const uno::Reference< word::XRange >& _where, const uno::Any& _richtext ) +{ + SwVbaRange* pWhere = dynamic_cast<SwVbaRange*>( _where.get() ); + if( pWhere ) + { + uno::Reference< text::XTextRange > xTextRange = pWhere->getXTextRange(); + xTextRange->setString( "x" ); // set marker + uno::Reference< text::XTextRange > xEndMarker = xTextRange->getEnd(); + xEndMarker->setString( "x" ); // set marker + uno::Reference< text::XText > xText = pWhere->getXText(); + mxEntry->applyTo( xEndMarker->getStart() ); + uno::Reference< text::XTextCursor > xTC = xText->createTextCursorByRange( xTextRange->getStart() ); + xTC->goRight( 1, true ); + xTC->setString( "" ); // remove marker + // remove the blank paragraph if it is a rich text + bool bRich = false; + _richtext >>= bRich; + if( bRich ) + { + // check if it is a blank paragraph + uno::Reference< text::XParagraphCursor > xParaCursor( xTC, uno::UNO_QUERY_THROW ); + if( xParaCursor->isStartOfParagraph() && xParaCursor->isEndOfParagraph() ) + { + //remove the blank paragraph + uno::Reference< frame::XModel > xModel( getCurrentWordDoc( mxContext ), uno::UNO_SET_THROW ); + uno::Reference< text::XTextViewCursor > xTVCursor = word::getXTextViewCursor( xModel ); + uno::Reference< text::XTextRange > xCurrentRange( xTC->getEnd(), uno::UNO_SET_THROW ); + xTVCursor->gotoRange( xCurrentRange, false ); + OUString url = ".uno:Delete"; + dispatchRequests( xModel,url ); + xTVCursor->gotoRange( xEndMarker->getEnd(), false ); + } + } + xEndMarker->setString( "" ); // remove marker + xTC = xText->createTextCursorByRange( xEndMarker->getEnd() ); + pWhere->setXTextCursor( xTC ); + } + return uno::Reference< word::XRange >( pWhere ); +} + +OUString +SwVbaAutoTextEntry::getServiceImplName() +{ + return "SwVbaAutoTextEntry"; +} + +uno::Sequence< OUString > +SwVbaAutoTextEntry::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.AutoTextEntry" + }; + return aServiceNames; +} + +SwVbaAutoTextEntries::SwVbaAutoTextEntries( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess ) : SwVbaAutoTextEntries_BASE( xParent, xContext, xIndexAccess ) +{ +} + +// XEnumerationAccess +uno::Type +SwVbaAutoTextEntries::getElementType() +{ + return cppu::UnoType<word::XAutoTextEntry>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaAutoTextEntries::createEnumeration() +{ + throw uno::RuntimeException("Not implemented" ); +} + +uno::Any +SwVbaAutoTextEntries::createCollectionObject( const css::uno::Any& aSource ) +{ + uno::Reference< text::XAutoTextEntry > xEntry( aSource, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XAutoTextEntry >( new SwVbaAutoTextEntry( this, mxContext, xEntry ) ) ); +} + +OUString +SwVbaAutoTextEntries::getServiceImplName() +{ + return "SwVbaAutoTextEntries"; +} + +css::uno::Sequence<OUString> +SwVbaAutoTextEntries::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.AutoTextEntries" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaautotextentry.hxx b/sw/source/ui/vba/vbaautotextentry.hxx new file mode 100644 index 000000000..08d82b5d9 --- /dev/null +++ b/sw/source/ui/vba/vbaautotextentry.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_UI_VBA_VBAAUTOTEXTENTRY_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAAUTOTEXTENTRY_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XAutoTextEntries.hpp> +#include <ooo/vba/word/XAutoTextEntry.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <ooo/vba/word/XRange.hpp> +#include <com/sun/star/text/XAutoTextEntry.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XAutoTextEntry > SwVbaAutoTextEntry_BASE; + +class SwVbaAutoTextEntry : public SwVbaAutoTextEntry_BASE +{ +private: + css::uno::Reference< css::text::XAutoTextEntry > mxEntry; + +public: + /// @throws css::uno::RuntimeException + SwVbaAutoTextEntry( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XAutoTextEntry >& xEntry ); + virtual ~SwVbaAutoTextEntry() override; + + // XAutoTextEntry + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL Insert( const css::uno::Reference< ooo::vba::word::XRange >& _where, const css::uno::Any& _richtext ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +typedef CollTestImplHelper< ooo::vba::word::XAutoTextEntries > SwVbaAutoTextEntries_BASE; + +class SwVbaAutoTextEntries : public SwVbaAutoTextEntries_BASE +{ +public: + /// @throws css::uno::RuntimeException + SwVbaAutoTextEntries( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XIndexAccess >& xIndexAccess ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaAutoTextEntries_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAAUTOTEXTENTRY_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbabookmark.cxx b/sw/source/ui/vba/vbabookmark.cxx new file mode 100644 index 000000000..6c1569730 --- /dev/null +++ b/sw/source/ui/vba/vbabookmark.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 "vbabookmark.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include "vbarange.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaBookmark::SwVbaBookmark( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, + const css::uno::Reference< frame::XModel >& rModel, const OUString& rBookmarkName ) : + SwVbaBookmark_BASE( rParent, rContext ), mxModel( rModel ), maBookmarkName( rBookmarkName ), mbValid( true ) +{ + uno::Reference< text::XBookmarksSupplier > xBookmarksSupplier( mxModel, uno::UNO_QUERY_THROW ); + mxBookmark.set( xBookmarksSupplier->getBookmarks()->getByName( maBookmarkName ), uno::UNO_QUERY_THROW ); +} + +SwVbaBookmark::~SwVbaBookmark() +{ +} + +void SwVbaBookmark::checkVality() +{ + if( !mbValid ) + throw uno::RuntimeException("The bookmark is not valid" ); +} + +void SAL_CALL SwVbaBookmark::Delete() +{ + checkVality(); + uno::Reference< text::XTextDocument > xTextDocument( mxModel, uno::UNO_QUERY_THROW ); + xTextDocument->getText()->removeTextContent( mxBookmark ); + mbValid = false; +} + +void SAL_CALL SwVbaBookmark::Select() +{ + checkVality(); + uno::Reference< view::XSelectionSupplier > xSelectSupp( mxModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xSelectSupp->select( uno::makeAny( mxBookmark ) ); +} + +OUString SAL_CALL SwVbaBookmark::getName() +{ + return maBookmarkName; +} + +void SAL_CALL SwVbaBookmark::setName( const OUString& _name ) +{ + uno::Reference< container::XNamed > xNamed( mxBookmark, uno::UNO_QUERY_THROW ); + xNamed->setName( _name ); +} + +uno::Any SAL_CALL SwVbaBookmark::Range() +{ + uno::Reference< text::XTextContent > xTextContent( mxBookmark, uno::UNO_SET_THROW ); + uno::Reference< text::XTextDocument > xTextDocument( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTextRange( xTextContent->getAnchor(), uno::UNO_SET_THROW ); + return uno::makeAny( uno::Reference< word::XRange>( new SwVbaRange( this, mxContext, xTextDocument, xTextRange->getStart(), xTextRange->getEnd(), xTextRange->getText() ) ) ); +} + +OUString +SwVbaBookmark::getServiceImplName() +{ + return "SwVbaBookmark"; +} + +uno::Sequence< OUString > +SwVbaBookmark::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Bookmark" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbabookmark.hxx b/sw/source/ui/vba/vbabookmark.hxx new file mode 100644 index 000000000..633335426 --- /dev/null +++ b/sw/source/ui/vba/vbabookmark.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_UI_VBA_VBABOOKMARK_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBABOOKMARK_HXX + +#include <ooo/vba/word/XBookmark.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextContent.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XBookmark > SwVbaBookmark_BASE; + +class SwVbaBookmark : public SwVbaBookmark_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextContent > mxBookmark; + OUString maBookmarkName; + bool mbValid; + +private: + /// @throws css::uno::RuntimeException + void checkVality(); + +public: + /// @throws css::uno::RuntimeException + SwVbaBookmark( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XModel >& rModel, const OUString& rName ); + virtual ~SwVbaBookmark() override; + + // Methods + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName( const OUString& ) override; + virtual void SAL_CALL Delete() override; + virtual void SAL_CALL Select() override; + virtual css::uno::Any SAL_CALL Range() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBABOOKMARK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbabookmarks.cxx b/sw/source/ui/vba/vbabookmarks.cxx new file mode 100644 index 000000000..4da9fc1e7 --- /dev/null +++ b/sw/source/ui/vba/vbabookmarks.cxx @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbabookmarks.hxx" +#include "vbabookmark.hxx" +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <ooo/vba/word/WdBookmarkSortBy.hpp> +#include "vbarange.hxx" +#include "wordvbahelper.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class BookmarksEnumeration : public EnumerationHelperImpl +{ + uno::Reference< frame::XModel > mxModel; +public: + /// @throws uno::RuntimeException + BookmarksEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, const uno::Reference< frame::XModel >& xModel ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), mxModel( xModel ) {} + + virtual uno::Any SAL_CALL nextElement( ) override + { + uno::Reference< container::XNamed > xNamed( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + OUString aName = xNamed->getName(); + return uno::makeAny( uno::Reference< word::XBookmark > ( new SwVbaBookmark( m_xParent, m_xContext, mxModel, aName ) ) ); + } + +}; + +// Bookmarks use case-insensitive name lookup in MS Word. +class BookmarkCollectionHelper : public ::cppu::WeakImplHelper< container::XNameAccess, + container::XIndexAccess > +{ +private: + uno::Reference< container::XNameAccess > mxNameAccess; + uno::Reference< container::XIndexAccess > mxIndexAccess; + uno::Any cachePos; +public: + /// @throws uno::RuntimeException + explicit BookmarkCollectionHelper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : mxIndexAccess( xIndexAccess ) + { + mxNameAccess.set( mxIndexAccess, uno::UNO_QUERY_THROW ); + } + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return mxIndexAccess->getElementType(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return mxIndexAccess->hasElements(); } + // XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + if ( !hasByName(aName) ) + throw container::NoSuchElementException(); + return cachePos; + } + virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + return mxNameAccess->getElementNames(); + } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + if( mxNameAccess->hasByName( aName ) ) + { + cachePos = mxNameAccess->getByName( aName ); + return true; + } + else + { + for( sal_Int32 nIndex = 0; nIndex < mxIndexAccess->getCount(); nIndex++ ) + { + uno::Reference< container::XNamed > xNamed( mxIndexAccess->getByIndex( nIndex ), uno::UNO_QUERY_THROW ); + OUString aBookmarkName = xNamed->getName(); + if( aName.equalsIgnoreAsciiCase( aBookmarkName ) ) + { + cachePos <<= xNamed; + return true; + } + } + } + return false; + } + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + return mxIndexAccess->getCount(); + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + return mxIndexAccess->getByIndex( Index ); + } +}; + +} + +SwVbaBookmarks::SwVbaBookmarks( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< container::XIndexAccess >& xBookmarks, const uno::Reference< frame::XModel >& xModel ): SwVbaBookmarks_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new BookmarkCollectionHelper( xBookmarks ) ) ), mxModel( xModel ) +{ + mxBookmarksSupplier.set( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextDocument > xDocument( mxModel, uno::UNO_QUERY_THROW ); +} +// XEnumerationAccess +uno::Type +SwVbaBookmarks::getElementType() +{ + return cppu::UnoType<word::XBookmark>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaBookmarks::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return new BookmarksEnumeration( getParent(), mxContext,xEnumAccess->createEnumeration(), mxModel ); +} + +uno::Any +SwVbaBookmarks::createCollectionObject( const css::uno::Any& aSource ) +{ + uno::Reference< container::XNamed > xNamed( aSource, uno::UNO_QUERY_THROW ); + OUString aName = xNamed->getName(); + return uno::makeAny( uno::Reference< word::XBookmark > ( new SwVbaBookmark( getParent(), mxContext, mxModel, aName ) ) ); +} + +void SwVbaBookmarks::removeBookmarkByName( const OUString& rName ) +{ + uno::Reference< text::XTextContent > xBookmark( m_xNameAccess->getByName( rName ), uno::UNO_QUERY_THROW ); + word::getXTextViewCursor( mxModel )->getText()->removeTextContent( xBookmark ); +} + +void SwVbaBookmarks::addBookmarkByName( const uno::Reference< frame::XModel >& xModel, const OUString& rName, const uno::Reference< text::XTextRange >& rTextRange ) +{ + uno::Reference< lang::XMultiServiceFactory > xDocMSF( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextContent > xBookmark( xDocMSF->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW ); + uno::Reference< container::XNamed > xNamed( xBookmark, uno::UNO_QUERY_THROW ); + xNamed->setName( rName ); + rTextRange->getText()->insertTextContent( rTextRange, xBookmark, false ); +} + +uno::Any SAL_CALL +SwVbaBookmarks::Add( const OUString& rName, const uno::Any& rRange ) +{ + uno::Reference< text::XTextRange > xTextRange; + uno::Reference< word::XRange > xRange; + if( rRange >>= xRange ) + { + SwVbaRange* pRange = dynamic_cast< SwVbaRange* >( xRange.get() ); + if( pRange ) + xTextRange = pRange->getXTextRange(); + } + else + { + // FIXME: insert the bookmark into current view cursor + xTextRange.set( word::getXTextViewCursor( mxModel ), uno::UNO_QUERY_THROW ); + } + + // remove the exist bookmark + if( m_xNameAccess->hasByName( rName ) ) + removeBookmarkByName( rName ); + + addBookmarkByName( mxModel, rName, xTextRange ); + + return uno::makeAny( uno::Reference< word::XBookmark >( new SwVbaBookmark( getParent(), mxContext, mxModel, rName ) ) ); +} + +sal_Int32 SAL_CALL +SwVbaBookmarks::getDefaultSorting() +{ + return word::WdBookmarkSortBy::wdSortByName; +} + +void SAL_CALL +SwVbaBookmarks::setDefaultSorting( sal_Int32/* _type*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL +SwVbaBookmarks::getShowHidden() +{ + return true; +} + +void SAL_CALL +SwVbaBookmarks::setShowHidden( sal_Bool /*_hidden*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL +SwVbaBookmarks::Exists( const OUString& rName ) +{ + bool bExist = m_xNameAccess->hasByName( rName ); + return bExist; +} + +OUString +SwVbaBookmarks::getServiceImplName() +{ + return "SwVbaBookmarks"; +} + +css::uno::Sequence<OUString> +SwVbaBookmarks::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Bookmarks" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbabookmarks.hxx b/sw/source/ui/vba/vbabookmarks.hxx new file mode 100644 index 000000000..546679bbf --- /dev/null +++ b/sw/source/ui/vba/vbabookmarks.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBABOOKMARKS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBABOOKMARKS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XBookmarks.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XBookmarks > SwVbaBookmarks_BASE; + +class SwVbaBookmarks : public SwVbaBookmarks_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XBookmarksSupplier > mxBookmarksSupplier; + +private: + /// @throws css::uno::RuntimeException + void removeBookmarkByName( const OUString& rName ); + +public: + SwVbaBookmarks( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XIndexAccess >& xBookmarks, const css::uno::Reference< css::frame::XModel >& xModel ); + + /// @throws css::uno::RuntimeException + static void addBookmarkByName( const css::uno::Reference< css::frame::XModel >& xModel, const OUString& rName, const css::uno::Reference< css::text::XTextRange >& rTextRange ); + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaBookmarks_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + + // XBookmarks + virtual sal_Int32 SAL_CALL getDefaultSorting() override; + virtual void SAL_CALL setDefaultSorting( sal_Int32 _type ) override; + virtual sal_Bool SAL_CALL getShowHidden() override; + virtual void SAL_CALL setShowHidden( sal_Bool _hidden ) override; + + virtual css::uno::Any SAL_CALL Add( const OUString& rName, const css::uno::Any& rRange ) override; + virtual sal_Bool SAL_CALL Exists( const OUString& rName ) override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBABOOKMARKS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaborders.cxx b/sw/source/ui/vba/vbaborders.cxx new file mode 100644 index 000000000..5561bbfcb --- /dev/null +++ b/sw/source/ui/vba/vbaborders.cxx @@ -0,0 +1,365 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaborders.hxx" +#include <ooo/vba/word/XBorder.hpp> +#include <ooo/vba/word/WdBorderType.hpp> +#include <ooo/vba/word/WdLineStyle.hpp> +#include <sal/macros.h> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/table/TableBorder.hpp> +#include "vbapalette.hxx" + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +typedef ::cppu::WeakImplHelper<container::XIndexAccess > RangeBorders_Base; +typedef InheritedHelperInterfaceWeakImpl<word::XBorder > SwVbaBorder_Base; + +// #TODO sort these indexes to match the order in which Word iterates over the +// borders, the enumeration will match the order in this list +static const sal_Int16 supportedIndexTable[] = { word::WdBorderType::wdBorderBottom, word::WdBorderType::wdBorderDiagonalDown, word::WdBorderType::wdBorderDiagonalUp, word::WdBorderType::wdBorderHorizontal, word::WdBorderType::wdBorderLeft, word::WdBorderType::wdBorderRight, word::WdBorderType::wdBorderTop, word::WdBorderType::wdBorderVertical }; + +// Equiv widths in 1/100 mm +const static sal_Int32 OOLineHairline = 2; + +namespace { + +class SwVbaBorder : public SwVbaBorder_Base +{ +private: + uno::Reference< beans::XPropertySet > m_xProps; + sal_Int32 m_LineType; + void setBorderLine( table::BorderLine const & rBorderLine ) + { + table::TableBorder aTableBorder; + m_xProps->getPropertyValue( "TableBorder" ) >>= aTableBorder; + + switch ( m_LineType ) + { + case word::WdBorderType::wdBorderLeft: + aTableBorder.IsLeftLineValid = true; + aTableBorder.LeftLine= rBorderLine; + break; + case word::WdBorderType::wdBorderTop: + aTableBorder.IsTopLineValid = true; + aTableBorder.TopLine = rBorderLine; + break; + + case word::WdBorderType::wdBorderBottom: + aTableBorder.IsBottomLineValid = true; + aTableBorder.BottomLine = rBorderLine; + break; + case word::WdBorderType::wdBorderRight: + aTableBorder.IsRightLineValid = true; + aTableBorder.RightLine = rBorderLine; + break; + case word::WdBorderType::wdBorderVertical: + aTableBorder.IsVerticalLineValid = true; + aTableBorder.VerticalLine = rBorderLine; + break; + case word::WdBorderType::wdBorderHorizontal: + aTableBorder.IsHorizontalLineValid = true; + aTableBorder.HorizontalLine = rBorderLine; + break; + case word::WdBorderType::wdBorderDiagonalDown: + case word::WdBorderType::wdBorderDiagonalUp: + // #TODO have to ignore at the moment, would be + // nice to investigate what we can do here + break; + default: + return; + } + m_xProps->setPropertyValue( "TableBorder", uno::makeAny(aTableBorder) ); + } + + bool getBorderLine( table::BorderLine& rBorderLine ) + { + table::TableBorder aTableBorder; + m_xProps->getPropertyValue( "TableBorder" ) >>= aTableBorder; + switch ( m_LineType ) + { + case word::WdBorderType::wdBorderLeft: + if ( aTableBorder.IsLeftLineValid ) + rBorderLine = aTableBorder.LeftLine; + break; + case word::WdBorderType::wdBorderTop: + if ( aTableBorder.IsTopLineValid ) + rBorderLine = aTableBorder.TopLine; + break; + case word::WdBorderType::wdBorderBottom: + if ( aTableBorder.IsBottomLineValid ) + rBorderLine = aTableBorder.BottomLine; + break; + case word::WdBorderType::wdBorderRight: + if ( aTableBorder.IsRightLineValid ) + rBorderLine = aTableBorder.RightLine; + break; + case word::WdBorderType::wdBorderVertical: + if ( aTableBorder.IsVerticalLineValid ) + rBorderLine = aTableBorder.VerticalLine; + break; + case word::WdBorderType::wdBorderHorizontal: + if ( aTableBorder.IsHorizontalLineValid ) + rBorderLine = aTableBorder.HorizontalLine; + break; + + case word::WdBorderType::wdBorderDiagonalDown: + case word::WdBorderType::wdBorderDiagonalUp: + // #TODO have to ignore at the moment, would be + // nice to investigate what we can do here + break; + default: + return false; + } + return true; + } + +protected: + virtual OUString getServiceImplName() override + { + return "SwVbaBorder"; + } + + virtual css::uno::Sequence<OUString> getServiceNames() override + { + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Border" + }; + return aServiceNames; + } +public: + SwVbaBorder( const uno::Reference< beans::XPropertySet > & xProps, const uno::Reference< uno::XComponentContext >& xContext, sal_Int32 lineType ) : SwVbaBorder_Base( uno::Reference< XHelperInterface >( xProps, uno::UNO_QUERY ), xContext ), m_xProps( xProps ), m_LineType( lineType ) {} + + uno::Any SAL_CALL getLineStyle() override + { + sal_Int32 nLineStyle = word::WdLineStyle::wdLineStyleNone; + table::BorderLine aBorderLine; + if ( getBorderLine( aBorderLine ) ) + { + if( aBorderLine.InnerLineWidth !=0 && aBorderLine.OuterLineWidth !=0 ) + { + nLineStyle = word::WdLineStyle::wdLineStyleDouble; + } + else if( aBorderLine.InnerLineWidth !=0 || aBorderLine.OuterLineWidth !=0 ) + { + nLineStyle = word::WdLineStyle::wdLineStyleSingle; + } + else + { + nLineStyle = word::WdLineStyle::wdLineStyleNone; + } + } + return uno::makeAny( nLineStyle ); + } + void SAL_CALL setLineStyle( const uno::Any& _linestyle ) override + { + // Urk no choice but to silently ignore we don't support this attribute + // #TODO would be nice to support the word line styles + sal_Int32 nLineStyle = 0; + _linestyle >>= nLineStyle; + table::BorderLine aBorderLine; + if ( !getBorderLine( aBorderLine ) ) + throw uno::RuntimeException("Method failed" ); + + switch ( nLineStyle ) + { + case word::WdLineStyle::wdLineStyleNone: + { + aBorderLine.InnerLineWidth = 0; + aBorderLine.OuterLineWidth = 0; + break; + } + case word::WdLineStyle::wdLineStyleDashDot: + case word::WdLineStyle::wdLineStyleDashDotDot: + case word::WdLineStyle::wdLineStyleDashDotStroked: + case word::WdLineStyle::wdLineStyleDashLargeGap: + case word::WdLineStyle::wdLineStyleDashSmallGap: + case word::WdLineStyle::wdLineStyleDot: + case word::WdLineStyle::wdLineStyleDouble: + case word::WdLineStyle::wdLineStyleDoubleWavy: + case word::WdLineStyle::wdLineStyleEmboss3D: + case word::WdLineStyle::wdLineStyleEngrave3D: + case word::WdLineStyle::wdLineStyleInset: + case word::WdLineStyle::wdLineStyleOutset: + case word::WdLineStyle::wdLineStyleSingle: + case word::WdLineStyle::wdLineStyleSingleWavy: + case word::WdLineStyle::wdLineStyleThickThinLargeGap: + case word::WdLineStyle::wdLineStyleThickThinMedGap: + case word::WdLineStyle::wdLineStyleThickThinSmallGap: + case word::WdLineStyle::wdLineStyleThinThickLargeGap: + case word::WdLineStyle::wdLineStyleThinThickMedGap: + case word::WdLineStyle::wdLineStyleThinThickSmallGap: + case word::WdLineStyle::wdLineStyleThinThickThinLargeGap: + case word::WdLineStyle::wdLineStyleThinThickThinMedGap: + case word::WdLineStyle::wdLineStyleThinThickThinSmallGap: + case word::WdLineStyle::wdLineStyleTriple: + { + aBorderLine.InnerLineWidth = 0; + aBorderLine.OuterLineWidth = OOLineHairline; + break; + } + default: + throw uno::RuntimeException("Bad param" ); + } + setBorderLine( aBorderLine ); + + } +}; + +class RangeBorders : public RangeBorders_Base +{ +private: + uno::Reference< table::XCellRange > m_xRange; + uno::Reference< uno::XComponentContext > m_xContext; + VbaPalette m_Palette; + sal_Int32 getTableIndex( sal_Int32 nConst ) + { + // okay return position of the index in the table + sal_Int32 nIndexes = getCount(); + sal_Int32 realIndex = 0; + const sal_Int16* pTableEntry = supportedIndexTable; + for ( ; realIndex < nIndexes; ++realIndex, ++pTableEntry ) + { + if ( *pTableEntry == nConst ) + return realIndex; + } + return getCount(); // error condition + } +public: + RangeBorders( const uno::Reference< table::XCellRange >& xRange, const uno::Reference< uno::XComponentContext > & xContext, VbaPalette const & rPalette ) : m_xRange( xRange ), m_xContext( xContext ), m_Palette( rPalette ) + { + } + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + return SAL_N_ELEMENTS( supportedIndexTable ); + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + + sal_Int32 nIndex = getTableIndex( Index ); + if ( nIndex >= 0 && nIndex < getCount() ) + { + uno::Reference< beans::XPropertySet > xProps( m_xRange, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XBorder >( new SwVbaBorder( xProps, m_xContext, supportedIndexTable[ nIndex ] )) ); + } + throw lang::IndexOutOfBoundsException(); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XBorder>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } +}; + +} + +static uno::Reference< container::XIndexAccess > +rangeToBorderIndexAccess( const uno::Reference< table::XCellRange >& xRange, const uno::Reference< uno::XComponentContext > & xContext, VbaPalette const & rPalette ) +{ + return new RangeBorders( xRange, xContext, rPalette ); +} + +namespace { + +class RangeBorderEnumWrapper : public EnumerationHelper_BASE +{ + uno::Reference<container::XIndexAccess > m_xIndexAccess; + sal_Int32 nIndex; +public: + explicit RangeBorderEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : m_xIndexAccess( xIndexAccess ), nIndex( 0 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < m_xIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex < m_xIndexAccess->getCount() ) + return m_xIndexAccess->getByIndex( nIndex++ ); + throw container::NoSuchElementException(); + } +}; + +} + +// for Table borders +SwVbaBorders::SwVbaBorders( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< table::XCellRange >& xRange, VbaPalette const & rPalette ): SwVbaBorders_BASE( xParent, xContext, rangeToBorderIndexAccess( xRange ,xContext, rPalette ) ) +{ + m_xProps.set( xRange, uno::UNO_QUERY_THROW ); +} + +uno::Reference< container::XEnumeration > +SwVbaBorders::createEnumeration() +{ + return new RangeBorderEnumWrapper( m_xIndexAccess ); +} + +uno::Any +SwVbaBorders::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; // it's already a Border object +} + +uno::Type +SwVbaBorders::getElementType() +{ + return cppu::UnoType<word::XBorders>::get(); +} + +uno::Any +SwVbaBorders::getItemByIntIndex( const sal_Int32 nIndex ) +{ + return createCollectionObject( m_xIndexAccess->getByIndex( nIndex ) ); +} + +sal_Bool SAL_CALL SwVbaBorders::getShadow() +{ + // always return False for table border in MS Word + return false; +} + +void SAL_CALL SwVbaBorders::setShadow( sal_Bool /*_shadow*/ ) +{ + // not support in Table border in Word + // TODO: +} + +OUString +SwVbaBorders::getServiceImplName() +{ + return "SwVbaBorders"; +} + +uno::Sequence< OUString > +SwVbaBorders::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Borders" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaborders.hxx b/sw/source/ui/vba/vbaborders.hxx new file mode 100644 index 000000000..a4ca819f6 --- /dev/null +++ b/sw/source/ui/vba/vbaborders.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_UI_VBA_VBABORDERS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBABORDERS_HXX + +#include <ooo/vba/word/XBorders.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <vbahelper/vbacollectionimpl.hxx> + +typedef CollTestImplHelper< ov::word::XBorders > SwVbaBorders_BASE; +class VbaPalette; +class SwVbaBorders : public SwVbaBorders_BASE +{ + // XEnumerationAccess + virtual css::uno::Any getItemByIntIndex( const sal_Int32 nIndex ) override; + css::uno::Reference< css::beans::XPropertySet > m_xProps; +public: + SwVbaBorders( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::table::XCellRange >& xRange, VbaPalette const & rPalette ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaCollectionBaseImpl + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + + virtual sal_Bool SAL_CALL getShadow() override; + virtual void SAL_CALL setShadow( sal_Bool _shadow ) override; + + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBABORDERS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacell.cxx b/sw/source/ui/vba/vbacell.cxx new file mode 100644 index 000000000..e4a573b2c --- /dev/null +++ b/sw/source/ui/vba/vbacell.cxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbacell.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbatablehelper.hxx" +#include "vbarow.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaCell::SwVbaCell( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nColumn, sal_Int32 nRow ) : + SwVbaCell_BASE( rParent, rContext ), mxTextTable( xTextTable ), mnColumn( nColumn ), mnRow( nRow ) +{ +} + +SwVbaCell::~SwVbaCell() +{ +} + +::sal_Int32 SAL_CALL SwVbaCell::getWidth() +{ + SwVbaTableHelper aTableHelper( mxTextTable ); + return aTableHelper.GetColWidth( mnColumn, mnRow ); +} + +void SAL_CALL SwVbaCell::setWidth( ::sal_Int32 _width ) +{ + SwVbaTableHelper aTableHelper( mxTextTable ); + aTableHelper.SetColWidth( _width, mnColumn, mnRow, true ); +} + +uno::Any SAL_CALL SwVbaCell::getHeight() +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnRow ) ); + return xRow->getHeight(); +} + +void SAL_CALL SwVbaCell::setHeight( const uno::Any& _height ) +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnRow ) ); + xRow->setHeight( _height ); +} + +::sal_Int32 SAL_CALL SwVbaCell::getHeightRule() +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnRow ) ); + return xRow->getHeightRule(); +} + +void SAL_CALL SwVbaCell::setHeightRule( ::sal_Int32 _heightrule ) +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnRow ) ); + xRow->setHeightRule( _heightrule ); +} + +void SAL_CALL SwVbaCell::SetWidth( float width, sal_Int32 /*rulestyle*/ ) +{ + // FIXME: handle the argument: rulestyle + setWidth( static_cast<sal_Int32>(width) ); +} + +void SAL_CALL SwVbaCell::SetHeight( float height, sal_Int32 heightrule ) +{ + // FIXME: handle the argument: heightrule + setHeightRule( heightrule ); + setHeight( uno::makeAny( height ) ); +} + +OUString +SwVbaCell::getServiceImplName() +{ + return "SwVbaCell"; +} + +uno::Sequence< OUString > +SwVbaCell::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Cell" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacell.hxx b/sw/source/ui/vba/vbacell.hxx new file mode 100644 index 000000000..9c9e7524e --- /dev/null +++ b/sw/source/ui/vba/vbacell.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_UI_VBA_VBACELL_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBACELL_HXX + +#include <ooo/vba/word/XCell.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextTable.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XCell > SwVbaCell_BASE; + +class SwVbaCell : public SwVbaCell_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + sal_Int32 mnColumn; + sal_Int32 mnRow; + +public: + /// @throws css::uno::RuntimeException + SwVbaCell( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nColumn, sal_Int32 nRow ); + virtual ~SwVbaCell() override; + + // Attributes + virtual ::sal_Int32 SAL_CALL getWidth() override; + virtual void SAL_CALL setWidth( ::sal_Int32 _width ) override; + virtual css::uno::Any SAL_CALL getHeight() override; + virtual void SAL_CALL setHeight( const css::uno::Any& _height ) override; + virtual ::sal_Int32 SAL_CALL getHeightRule() override; + virtual void SAL_CALL setHeightRule( ::sal_Int32 _heightrule ) override; + + // Methods + virtual void SAL_CALL SetWidth( float width, sal_Int32 rulestyle ) override; + virtual void SAL_CALL SetHeight( float height, sal_Int32 heightrule ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBACELL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacells.cxx b/sw/source/ui/vba/vbacells.cxx new file mode 100644 index 000000000..72daf252c --- /dev/null +++ b/sw/source/ui/vba/vbacells.cxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbacells.hxx" +#include "vbacell.hxx" +#include "vbarow.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class CellsEnumWrapper : public EnumerationHelper_BASE +{ + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 nIndex; + +public: + explicit CellsEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : mxIndexAccess( xIndexAccess ), nIndex( 0 ) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if( nIndex < mxIndexAccess->getCount() ) + { + return mxIndexAccess->getByIndex( nIndex++ ); + } + throw container::NoSuchElementException(); + } +}; + +class CellCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< css::text::XTextTable > mxTextTable; + sal_Int32 mnLeft; + sal_Int32 mnTop; + sal_Int32 mnRight; + sal_Int32 mnBottom; + +public: + /// @throws css::uno::RuntimeException + CellCollectionHelper( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ): mxParent( xParent ), mxContext( xContext ), mxTextTable( xTextTable ), mnLeft( nLeft ), mnTop( nTop ), mnRight( nRight ), mnBottom( nBottom ) + { + } + + virtual sal_Int32 SAL_CALL getCount( ) override + { + return ( mnRight - mnLeft + 1 ) * ( mnBottom - mnTop + 1 ); + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw css::lang::IndexOutOfBoundsException(); + + for( sal_Int32 row = mnTop; row <= mnBottom; row++ ) + { + for( sal_Int32 col = mnLeft; col <= mnRight; col++ ) + { + if( Index == ( ( row - mnTop ) * ( mnRight - mnLeft + 1 ) + ( col - mnLeft ) ) ) + return uno::makeAny( uno::Reference< word::XCell >( new SwVbaCell( mxParent, mxContext, mxTextTable, col, row ) ) ); + } + } + throw css::lang::IndexOutOfBoundsException(); + + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XCell>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new CellsEnumWrapper( this ); + } +}; + +} + +SwVbaCells::SwVbaCells( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) : SwVbaCells_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new CellCollectionHelper( xParent, xContext, xTextTable, nLeft, nTop, nRight, nBottom ) ) ), mxTextTable( xTextTable ), mnTop( nTop ), mnBottom( nBottom ) +{ +} + +::sal_Int32 SAL_CALL SwVbaCells::getWidth() +{ + uno::Reference< word::XCell > xCell( m_xIndexAccess->getByIndex( 0 ), uno::UNO_QUERY_THROW ); + return xCell->getWidth(); +} + +void SAL_CALL SwVbaCells::setWidth( ::sal_Int32 _width ) +{ + sal_Int32 nIndex = 0; + while( nIndex < m_xIndexAccess->getCount() ) + { + uno::Reference< word::XCell > xCell( m_xIndexAccess->getByIndex( nIndex++ ), uno::UNO_QUERY_THROW ); + xCell->setWidth( _width ); + } +} + +uno::Any SAL_CALL SwVbaCells::getHeight() +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnTop ) ); + return xRow->getHeight(); +} + +void SAL_CALL SwVbaCells::setHeight( const uno::Any& _height ) +{ + for( sal_Int32 row = mnTop; row <= mnBottom; row++ ) + { + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, row ) ); + xRow->setHeight( _height ); + } +} + +::sal_Int32 SAL_CALL SwVbaCells::getHeightRule() +{ + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, mnTop ) ); + return xRow->getHeightRule(); +} + +void SAL_CALL SwVbaCells::setHeightRule( ::sal_Int32 _heightrule ) +{ + for( sal_Int32 row = mnTop; row <= mnBottom; row++ ) + { + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, row ) ); + xRow->setHeightRule( _heightrule ); + } +} + +void SAL_CALL SwVbaCells::SetWidth( float width, sal_Int32 rulestyle ) +{ + sal_Int32 nIndex = 0; + while( nIndex < m_xIndexAccess->getCount() ) + { + uno::Reference< word::XCell > xCell( m_xIndexAccess->getByIndex( nIndex++ ), uno::UNO_QUERY_THROW ); + xCell->SetWidth( width, rulestyle ); + } +} + +void SAL_CALL SwVbaCells::SetHeight( float height, sal_Int32 heightrule ) +{ + for( sal_Int32 row = mnTop; row <= mnBottom; row++ ) + { + uno::Reference< word::XRow > xRow( new SwVbaRow( getParent(), mxContext, mxTextTable, row ) ); + xRow->SetHeight( height, heightrule ); + } +} + +// XEnumerationAccess +uno::Type +SwVbaCells::getElementType() +{ + return cppu::UnoType<word::XCell>::get(); +} + +uno::Reference< container::XEnumeration > +SwVbaCells::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumAccess->createEnumeration(); +} + +uno::Any +SwVbaCells::createCollectionObject( const uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaCells::getServiceImplName() +{ + return "SwVbaCells"; +} + +uno::Sequence<OUString> +SwVbaCells::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Cells" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacells.hxx b/sw/source/ui/vba/vbacells.hxx new file mode 100644 index 000000000..e9de9f532 --- /dev/null +++ b/sw/source/ui/vba/vbacells.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBACELLS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBACELLS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XCells.hpp> +#include <com/sun/star/text/XTextTable.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XCells > SwVbaCells_BASE; + +class SwVbaCells : public SwVbaCells_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + sal_Int32 mnTop; + sal_Int32 mnBottom; + +public: + /// @throws css::uno::RuntimeException + SwVbaCells( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ); + + // Attributes + virtual ::sal_Int32 SAL_CALL getWidth() override; + virtual void SAL_CALL setWidth( ::sal_Int32 _width ) override; + virtual css::uno::Any SAL_CALL getHeight() override; + virtual void SAL_CALL setHeight( const css::uno::Any& _height ) override; + virtual ::sal_Int32 SAL_CALL getHeightRule() override; + virtual void SAL_CALL setHeightRule( ::sal_Int32 _heightrule ) override; + + // Methods + virtual void SAL_CALL SetWidth( float width, sal_Int32 rulestyle ) override; + virtual void SAL_CALL SetHeight( float height, sal_Int32 heightrule ) override; + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaCells_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBACELLS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacolumn.cxx b/sw/source/ui/vba/vbacolumn.cxx new file mode 100644 index 000000000..3e2d380f6 --- /dev/null +++ b/sw/source/ui/vba/vbacolumn.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 "vbacolumn.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include "vbatablehelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaColumn::SwVbaColumn( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nIndex ) : + SwVbaColumn_BASE( rParent, rContext ), mxTextTable( xTextTable ), mnIndex( nIndex ) +{ +} + +SwVbaColumn::~SwVbaColumn() +{ +} + +sal_Int32 SAL_CALL +SwVbaColumn::getWidth( ) +{ + SwVbaTableHelper aTableHelper( mxTextTable ); + return aTableHelper.GetColWidth( mnIndex ); +} + +void SAL_CALL +SwVbaColumn::setWidth( sal_Int32 _width ) +{ + + SwVbaTableHelper aTableHelper( mxTextTable ); + aTableHelper.SetColWidth( _width, mnIndex ); +} + +void SAL_CALL +SwVbaColumn::Select( ) +{ + SelectColumn( getCurrentWordDoc(mxContext), mxTextTable, mnIndex, mnIndex ); +} + +void SwVbaColumn::SelectColumn( const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nStartColumn, sal_Int32 nEndColumn ) +{ + OUString sStartCol = SwVbaTableHelper::getColumnStr( nStartColumn ); + OUString aRangeName = sStartCol + OUString::number( 1 ); + OUString sEndCol = SwVbaTableHelper::getColumnStr( nEndColumn ); + sal_Int32 nRowCount = xTextTable->getRows()->getCount(); + aRangeName += ":" + sEndCol + OUString::number(nRowCount); + + uno::Reference< table::XCellRange > xCellRange( xTextTable, uno::UNO_QUERY_THROW ); + uno::Reference< table::XCellRange > xSelRange = xCellRange->getCellRangeByName( aRangeName ); + + uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xSelection->select( uno::makeAny( xSelRange ) ); +} + +OUString +SwVbaColumn::getServiceImplName() +{ + return "SwVbaColumn"; +} + +uno::Sequence< OUString > +SwVbaColumn::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Column" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacolumn.hxx b/sw/source/ui/vba/vbacolumn.hxx new file mode 100644 index 000000000..2c05f3b0e --- /dev/null +++ b/sw/source/ui/vba/vbacolumn.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_UI_VBA_VBACOLUMN_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBACOLUMN_HXX + +#include <ooo/vba/word/XColumn.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextTable.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XColumn > SwVbaColumn_BASE; + +class SwVbaColumn : public SwVbaColumn_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + sal_Int32 mnIndex; + +public: + /// @throws css::uno::RuntimeException + SwVbaColumn( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nIndex ); + virtual ~SwVbaColumn() override; + + // Methods + virtual sal_Int32 SAL_CALL getWidth() override; + virtual void SAL_CALL setWidth( sal_Int32 _width ) override; + virtual void SAL_CALL Select( ) override; + + /// @throws css::uno::RuntimeException + static void SelectColumn( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nStartColumn, sal_Int32 nEndColumn ); + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBACOLUMN_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacolumns.cxx b/sw/source/ui/vba/vbacolumns.cxx new file mode 100644 index 000000000..9d9c32db8 --- /dev/null +++ b/sw/source/ui/vba/vbacolumns.cxx @@ -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 . + */ +#include "vbacolumns.hxx" +#include "vbacolumn.hxx" +#include "vbatablehelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class ColumnsEnumWrapper : public EnumerationHelper_BASE +{ + uno::WeakReference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< text::XTextTable > mxTextTable; + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 nIndex; + +public: + ColumnsEnumWrapper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< text::XTextTable >& xTextTable ) : mxParent( xParent ), mxContext( xContext ), mxTextTable( xTextTable ), nIndex( 0 ) + { + mxIndexAccess = mxTextTable->getColumns(); + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if( nIndex < mxIndexAccess->getCount() ) + { + return uno::makeAny( uno::Reference< word::XColumn > ( new SwVbaColumn( mxParent, mxContext, mxTextTable, nIndex++ ) ) ); + } + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaColumns::SwVbaColumns( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextTable >& xTextTable, const uno::Reference< table::XTableColumns >& xTableColumns ) : SwVbaColumns_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( xTableColumns, uno::UNO_QUERY_THROW ) ), mxTextTable( xTextTable ) +{ + mnStartColumnIndex = 0; + SwVbaTableHelper aTableHelper( mxTextTable ); + mnEndColumnIndex = aTableHelper.getTabColumnsMaxCount( ) - 1; +} + +SwVbaColumns::SwVbaColumns( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextTable >& xTextTable, const uno::Reference< table::XTableColumns >& xTableColumns, sal_Int32 nStartCol, sal_Int32 nEndCol ) : SwVbaColumns_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( xTableColumns, uno::UNO_QUERY_THROW ) ), mxTextTable( xTextTable ), mnStartColumnIndex( nStartCol ), mnEndColumnIndex( nEndCol ) +{ + if( mnEndColumnIndex < mnStartColumnIndex ) + throw uno::RuntimeException(); +} + +uno::Reference< word::XColumn > SwVbaColumns::getColumnAtIndex( sal_Int32 index ) +{ + return uno::Reference< word::XColumn >( new SwVbaColumn( this, mxContext, mxTextTable, index ) ); +} + +::sal_Int32 SAL_CALL SwVbaColumns::getWidth() +{ + return getColumnAtIndex( mnStartColumnIndex )->getWidth(); +} + +void SAL_CALL SwVbaColumns::setWidth( ::sal_Int32 _width ) +{ + for( sal_Int32 index = mnStartColumnIndex; index <= mnEndColumnIndex; index++ ) + { + getColumnAtIndex( index )->setWidth( _width ); + } +} + +void SAL_CALL SwVbaColumns::Select( ) +{ + SwVbaColumn::SelectColumn( getCurrentWordDoc(mxContext), mxTextTable, mnStartColumnIndex, mnEndColumnIndex ); +} + +::sal_Int32 SAL_CALL SwVbaColumns::getCount() +{ + return ( mnEndColumnIndex - mnStartColumnIndex + 1 ); +} + +uno::Any SAL_CALL SwVbaColumns::Item( const uno::Any& Index1, const uno::Any& /*not processed in this base class*/ ) +{ + sal_Int32 nIndex = 0; + if( Index1 >>= nIndex ) + { + if( nIndex <= 0 || nIndex > getCount() ) + { + throw lang::IndexOutOfBoundsException("Index out of bounds" ); + } + return uno::makeAny( uno::Reference< word::XColumn >( new SwVbaColumn( this, mxContext, mxTextTable, nIndex - 1 ) ) ); + } + throw uno::RuntimeException("Index out of bounds" ); +} + +// XEnumerationAccess +uno::Type +SwVbaColumns::getElementType() +{ + return cppu::UnoType<word::XColumn>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaColumns::createEnumeration() +{ + return new ColumnsEnumWrapper( this, mxContext, mxTextTable ); +} + +uno::Any +SwVbaColumns::createCollectionObject( const uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaColumns::getServiceImplName() +{ + return "SwVbaColumns"; +} + +uno::Sequence<OUString> +SwVbaColumns::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Columns" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbacolumns.hxx b/sw/source/ui/vba/vbacolumns.hxx new file mode 100644 index 000000000..8cacef23f --- /dev/null +++ b/sw/source/ui/vba/vbacolumns.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBACOLUMNS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBACOLUMNS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XColumns.hpp> +#include <ooo/vba/word/XColumn.hpp> +#include <com/sun/star/table/XTableColumns.hpp> +#include <com/sun/star/text/XTextTable.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XColumns > SwVbaColumns_BASE; + +class SwVbaColumns : public SwVbaColumns_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + sal_Int32 mnStartColumnIndex; + sal_Int32 mnEndColumnIndex; + +private: + /// @throws css::uno::RuntimeException + css::uno::Reference< ooo::vba::word::XColumn > getColumnAtIndex( sal_Int32 index ); + +public: + /// @throws css::uno::RuntimeException + SwVbaColumns( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, const css::uno::Reference< css::table::XTableColumns >& xTableColumns ); + /// @throws css::uno::RuntimeException + SwVbaColumns( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, const css::uno::Reference< css::table::XTableColumns >& xTableColumns, sal_Int32 nStartCol, sal_Int32 nEndCol ); + + virtual sal_Int32 SAL_CALL getWidth( ) override; + virtual void SAL_CALL setWidth( sal_Int32 _width ) override; + virtual void SAL_CALL Select( ) override; + + //XCollection + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& /*not processed in this base class*/ ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaColumns_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBACOLUMNS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadialog.cxx b/sw/source/ui/vba/vbadialog.cxx new file mode 100644 index 000000000..c161b5d7c --- /dev/null +++ b/sw/source/ui/vba/vbadialog.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 "vbadialog.hxx" +#include <ooo/vba/word/WdWordDialog.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +struct WordDialogTable +{ + sal_Int32 wdDialog; + const char* ooDialog; +}; + +} + +static const WordDialogTable aWordDialogTable[] = +{ + { word::WdWordDialog::wdDialogFileNew, ".uno:NewDoc" }, + { word::WdWordDialog::wdDialogFileOpen, ".uno:Open" }, + { word::WdWordDialog::wdDialogFilePrint, ".uno:Print" }, + { word::WdWordDialog::wdDialogFileSaveAs, ".uno:SaveAs" }, + { 0, nullptr } +}; + +OUString +SwVbaDialog::mapIndexToName( sal_Int32 nIndex ) +{ + for (const WordDialogTable & rTable : aWordDialogTable) + { + if( nIndex == rTable.wdDialog ) + { + return OUString::createFromAscii( rTable.ooDialog ); + } + } + return OUString(); +} + +OUString +SwVbaDialog::getServiceImplName() +{ + return "SwVbaDialog"; +} + +uno::Sequence< OUString > +SwVbaDialog::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Dialog" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadialog.hxx b/sw/source/ui/vba/vbadialog.hxx new file mode 100644 index 000000000..df4443446 --- /dev/null +++ b/sw/source/ui/vba/vbadialog.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_UI_VBA_VBADIALOG_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBADIALOG_HXX + +#include <cppuhelper/implbase.hxx> +#include <ooo/vba/word/XDialog.hpp> +#include <vbahelper/vbadialogbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaDialogBase, ov::word::XDialog > SwVbaDialog_BASE; + +class SwVbaDialog : public SwVbaDialog_BASE +{ +public: + SwVbaDialog( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, sal_Int32 nIndex ):SwVbaDialog_BASE( xParent, xContext, xModel, nIndex ) {} + + // Methods + virtual OUString mapIndexToName( sal_Int32 nIndex ) override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBADIALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadialogs.cxx b/sw/source/ui/vba/vbadialogs.cxx new file mode 100644 index 000000000..2b26a4cfa --- /dev/null +++ b/sw/source/ui/vba/vbadialogs.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 <ooo/vba/word/XDialog.hpp> +#include "vbadialogs.hxx" +#include "vbadialog.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +uno::Any +SwVbaDialogs::Item( const uno::Any &aItem ) +{ + sal_Int32 nIndex = 0; + aItem >>= nIndex; + uno::Reference< word::XDialog > aDialog( new SwVbaDialog( uno::Reference< XHelperInterface >( Application(),uno::UNO_QUERY_THROW ), mxContext, m_xModel, nIndex ) ); + return uno::Any( aDialog ); +} + +OUString +SwVbaDialogs::getServiceImplName() +{ + return "SwVbaDialogs"; +} + +uno::Sequence< OUString > +SwVbaDialogs::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Dialogs" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadialogs.hxx b/sw/source/ui/vba/vbadialogs.hxx new file mode 100644 index 000000000..3703868f1 --- /dev/null +++ b/sw/source/ui/vba/vbadialogs.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_UI_VBA_VBADIALOGS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBADIALOGS_HXX + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <ooo/vba/word/XDialogs.hpp> +#include <vbahelper/vbadialogsbase.hxx> +#include <cppuhelper/implbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaDialogsBase, ov::word::XDialogs > SwVbaDialogs_BASE; + +class SwVbaDialogs : public SwVbaDialogs_BASE +{ +public: + SwVbaDialogs( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > &xContext, const css::uno::Reference< css::frame::XModel >& xModel ): SwVbaDialogs_BASE( xParent, xContext, xModel ) {} + + // XCollection + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBADIALOGS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocument.cxx b/sw/source/ui/vba/vbadocument.cxx new file mode 100644 index 000000000..3e001d70e --- /dev/null +++ b/sw/source/ui/vba/vbadocument.cxx @@ -0,0 +1,723 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "service.hxx" +#include "vbafilterpropsfromformat.hxx" +#include "vbadocument.hxx" +#include "vbarange.hxx" +#include "vbarangehelper.hxx" +#include "vbadocumentproperties.hxx" +#include "vbabookmarks.hxx" +#include "vbamailmerge.hxx" +#include "vbavariables.hxx" +#include <comphelper/processfactory.hxx> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/form/XFormsSupplier.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/document/XRedlinesSupplier.hpp> +#include <com/sun/star/util/thePathSettings.hpp> +#include <ooo/vba/XControlProvider.hpp> +#include <ooo/vba/word/WdProtectionType.hpp> +#include <ooo/vba/word/WdSaveFormat.hpp> +#include <ooo/vba/word/XDocumentOutgoing.hpp> + +#include <vbahelper/helperdecl.hxx> +#include "wordvbahelper.hxx" +#include <docsh.hxx> +#include "vbatemplate.hxx" +#include "vbaparagraph.hxx" +#include "vbastyles.hxx" +#include "vbatables.hxx" +#include "vbafield.hxx" +#include "vbapagesetup.hxx" +#include "vbasections.hxx" +#include "vbatablesofcontents.hxx" +#include <vbahelper/vbashapes.hxx> +#include <vbahelper/vbahelper.hxx> +#include "vbarevisions.hxx" +#include "vbaframes.hxx" +#include <basic/sberrors.hxx> +#include <osl/file.hxx> +#include <tools/urlobj.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class SwVbaDocumentOutgoingConnectionPoint : public cppu::WeakImplHelper<XConnectionPoint> +{ +private: + SwVbaDocument* mpDoc; + +public: + SwVbaDocumentOutgoingConnectionPoint( SwVbaDocument* pDoc ); + + // XConnectionPoint + sal_uInt32 SAL_CALL Advise(const uno::Reference< XSink >& Sink ) override; + void SAL_CALL Unadvise( sal_uInt32 Cookie ) override; +}; + +} + +SwVbaDocument::SwVbaDocument( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< frame::XModel > const & xModel ): SwVbaDocument_BASE( xParent, xContext, xModel ) +{ + Initialize(); +} +SwVbaDocument::SwVbaDocument( uno::Sequence< uno::Any > const& aArgs, uno::Reference< uno::XComponentContext >const& xContext ) : SwVbaDocument_BASE( aArgs, xContext ) +{ + Initialize(); +} + +SwVbaDocument::~SwVbaDocument() +{ +} + +void SwVbaDocument::Initialize() +{ + mxTextDocument.set( getModel(), uno::UNO_QUERY_THROW ); + word::getDocShell( mxModel )->RegisterAutomationDocumentObject( this ); +} + +sal_uInt32 +SwVbaDocument::AddSink( const uno::Reference< XSink >& xSink ) +{ + word::getDocShell( mxModel )->RegisterAutomationDocumentEventsCaller( uno::Reference< XSinkCaller >(this) ); + mvSinks.push_back(xSink); + return mvSinks.size(); +} + +void +SwVbaDocument::RemoveSink( sal_uInt32 nNumber ) +{ + if (nNumber < 1 || nNumber > mvSinks.size()) + return; + + mvSinks[nNumber-1] = uno::Reference< XSink >(); +} + +uno::Reference< word::XRange > SAL_CALL +SwVbaDocument::getContent() +{ + uno::Reference< text::XTextRange > xStart = mxTextDocument->getText()->getStart(); + uno::Reference< text::XTextRange > xEnd; + return uno::Reference< word::XRange >( new SwVbaRange( this, mxContext, mxTextDocument, xStart, xEnd ) ); +} + +uno::Reference< word::XRange > SAL_CALL +SwVbaDocument::Range( const uno::Any& rStart, const uno::Any& rEnd ) +{ + if( !rStart.hasValue() && !rEnd.hasValue() ) + return getContent(); + + sal_Int32 nStart = 0; + sal_Int32 nEnd = 0; + rStart >>= nStart; + rEnd >>= nEnd; + nStart--; + nEnd--; + + uno::Reference< text::XTextRange > xStart; + uno::Reference< text::XTextRange > xEnd; + if( nStart != -1 || nEnd != -1 ) + { + if( nStart == -1 ) + xStart = mxTextDocument->getText()->getStart(); + else + xStart = SwVbaRangeHelper::getRangeByPosition( mxTextDocument->getText(), nStart ); + + if( nEnd == -1 ) + xEnd = mxTextDocument->getText()->getEnd(); + else + xEnd = SwVbaRangeHelper::getRangeByPosition( mxTextDocument->getText(), nEnd ); + } + + if( !xStart.is() && !xEnd.is() ) + { + try + { + // FIXME + xStart = mxTextDocument->getText()->getStart(); + xEnd = mxTextDocument->getText()->getEnd(); + } + catch(const uno::Exception&) + { + DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, OUString()); + } + } + return uno::Reference< word::XRange >( new SwVbaRange( this, mxContext, mxTextDocument, xStart, xEnd ) ); +} + +uno::Any SAL_CALL +SwVbaDocument::BuiltInDocumentProperties( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaBuiltinDocumentProperties( mxParent, mxContext, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::CustomDocumentProperties( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaCustomDocumentProperties( mxParent, mxContext, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Bookmarks( const uno::Any& rIndex ) +{ + uno::Reference< text::XBookmarksSupplier > xBookmarksSupplier( getModel(),uno::UNO_QUERY_THROW ); + uno::Reference<container::XIndexAccess > xBookmarks( xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xBookmarksVba( new SwVbaBookmarks( this, mxContext, xBookmarks, getModel() ) ); + if ( rIndex.getValueTypeClass() == uno::TypeClass_VOID ) + return uno::makeAny( xBookmarksVba ); + + return xBookmarksVba->Item( rIndex, uno::Any() ); +} + +uno::Any SAL_CALL +SwVbaDocument::Variables( const uno::Any& rIndex ) +{ + uno::Reference< css::document::XDocumentPropertiesSupplier > xDocumentPropertiesSupplier( getModel(),uno::UNO_QUERY_THROW ); + uno::Reference< css::document::XDocumentProperties > xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); + uno::Reference< beans::XPropertyAccess > xUserDefined( xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY_THROW ); + + uno::Reference< XCollection > xVariables( new SwVbaVariables( this, mxContext, xUserDefined ) ); + if ( rIndex.getValueTypeClass() == uno::TypeClass_VOID ) + return uno::makeAny( xVariables ); + + return xVariables->Item( rIndex, uno::Any() ); +} + +uno::Any SAL_CALL +SwVbaDocument::Paragraphs( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaParagraphs( mxParent, mxContext, mxTextDocument ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Styles( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaStyles( mxParent, mxContext, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Fields( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaFields( mxParent, mxContext, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Shapes( const uno::Any& index ) +{ + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( getModel(), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new ScVbaShapes( this, mxContext, xIndexAccess, xModel ) ); + + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Sections( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaSections( mxParent, mxContext, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::TablesOfContents( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaTablesOfContents( this, mxContext, mxTextDocument ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::FormFields( const uno::Any& /*index*/ ) +{ + uno::Reference< XCollection > xCol; + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::PageSetup( ) +{ + uno::Reference< beans::XPropertySet > xPageProps( word::getCurrentPageStyle( mxModel ), uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XPageSetup >( new SwVbaPageSetup( this, mxContext, mxModel, xPageProps ) ) ); +} + +OUString +SwVbaDocument::getServiceImplName() +{ + return "SwVbaDocument"; +} + +uno::Any SAL_CALL +SwVbaDocument::getAttachedTemplate() +{ + uno::Reference< word::XTemplate > xTemplate; + uno::Reference<css::document::XDocumentPropertiesSupplier> const xDocPropSupp( + getModel(), uno::UNO_QUERY_THROW); + uno::Reference< css::document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW ); + OUString sTemplateUrl = xDocProps->getTemplateURL(); + + xTemplate = new SwVbaTemplate( this, mxContext, sTemplateUrl ); + return uno::makeAny( xTemplate ); +} + +void SAL_CALL +SwVbaDocument::setAttachedTemplate( const css::uno::Any& _attachedtemplate ) +{ + OUString sTemplate; + if( !( _attachedtemplate >>= sTemplate ) ) + { + throw uno::RuntimeException(); + } + OUString aURL; + INetURLObject aObj; + aObj.SetURL( sTemplate ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + if ( bIsURL ) + aURL = sTemplate; + else + osl::FileBase::getFileURLFromSystemPath( sTemplate, aURL ); + + uno::Reference<css::document::XDocumentPropertiesSupplier> const xDocPropSupp( + getModel(), uno::UNO_QUERY_THROW ); + uno::Reference< css::document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_SET_THROW ); + xDocProps->setTemplateURL( aURL ); +} + +uno::Any SAL_CALL +SwVbaDocument::Tables( const css::uno::Any& aIndex ) +{ + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xColl( new SwVbaTables( mxParent, mxContext, xModel ) ); + + if ( aIndex.hasValue() ) + return xColl->Item( aIndex, uno::Any() ); + return uno::makeAny( xColl ); +} + +void SAL_CALL SwVbaDocument::Activate() +{ + VbaDocumentBase::Activate(); +} + +::sal_Int32 SAL_CALL SwVbaDocument::getProtectionType() +{ + //TODO + return word::WdProtectionType::wdNoProtection; +} + +void SAL_CALL SwVbaDocument::setProtectionType( ::sal_Int32 /*_protectiontype*/ ) +{ + //TODO +} + +sal_Bool SAL_CALL SwVbaDocument::getUpdateStylesOnOpen() +{ + //TODO + return false; +} + +void SAL_CALL SwVbaDocument::setUpdateStylesOnOpen( sal_Bool /*_updatestylesonopen*/ ) +{ + //TODO +} + +sal_Bool SAL_CALL SwVbaDocument::getAutoHyphenation() +{ + // check this property only in default paragraph style + bool IsAutoHyphenation = false; + uno::Reference< beans::XPropertySet > xParaProps( word::getDefaultParagraphStyle( getModel() ), uno::UNO_QUERY_THROW ); + xParaProps->getPropertyValue("ParaIsHyphenation") >>= IsAutoHyphenation; + return IsAutoHyphenation; +} + +void SAL_CALL SwVbaDocument::setAutoHyphenation( sal_Bool _autohyphenation ) +{ + //TODO + uno::Reference< beans::XPropertySet > xParaProps( word::getDefaultParagraphStyle( getModel() ), uno::UNO_QUERY_THROW ); + xParaProps->setPropertyValue("ParaIsHyphenation", uno::makeAny( _autohyphenation ) ); +} + +::sal_Int32 SAL_CALL SwVbaDocument::getHyphenationZone() +{ + //TODO + return 0; +} + +void SAL_CALL SwVbaDocument::setHyphenationZone( ::sal_Int32 /*_hyphenationzone*/ ) +{ + //TODO +} + +::sal_Int32 SAL_CALL SwVbaDocument::getConsecutiveHyphensLimit() +{ + //TODO + sal_Int16 nHyphensLimit = 0; + uno::Reference< beans::XPropertySet > xParaProps( word::getDefaultParagraphStyle( getModel() ), uno::UNO_QUERY_THROW ); + xParaProps->getPropertyValue("ParaHyphenationMaxHyphens") >>= nHyphensLimit; + return nHyphensLimit; +} + +void SAL_CALL SwVbaDocument::setConsecutiveHyphensLimit( ::sal_Int32 _consecutivehyphenslimit ) +{ + sal_Int16 nHyphensLimit = static_cast< sal_Int16 >( _consecutivehyphenslimit ); + uno::Reference< beans::XPropertySet > xParaProps( word::getDefaultParagraphStyle( getModel() ), uno::UNO_QUERY_THROW ); + xParaProps->setPropertyValue("ParaHyphenationMaxHyphens", uno::makeAny( nHyphensLimit ) ); +} + +uno::Reference< ooo::vba::word::XMailMerge > SAL_CALL SwVbaDocument::getMailMerge() +{ + return uno::Reference< ooo::vba::word::XMailMerge >(SwVbaMailMerge::get(mxParent, mxContext).get()); +} + +void SAL_CALL SwVbaDocument::Protect( ::sal_Int32 /*Type*/, const uno::Any& /*NOReset*/, const uno::Any& /*Password*/, const uno::Any& /*UseIRM*/, const uno::Any& /*EnforceStyleLock*/ ) +{ + // Seems not support in Writer + // VbaDocumentBase::Protect( Password ); +} + +void SAL_CALL SwVbaDocument::PrintOut( const uno::Any& /*Background*/, const uno::Any& /*Append*/, const uno::Any& /*Range*/, const uno::Any& /*OutputFileName*/, const uno::Any& /*From*/, const uno::Any& /*To*/, const uno::Any& /*Item*/, const uno::Any& /*Copies*/, const uno::Any& /*Pages*/, const uno::Any& /*PageType*/, const uno::Any& /*PrintToFile*/, const uno::Any& /*Collate*/, const uno::Any& /*FileName*/, const uno::Any& /*ActivePrinterMacGX*/, const uno::Any& /*ManualDuplexPrint*/, const uno::Any& /*PrintZoomColumn*/, const uno::Any& /*PrintZoomRow*/, const uno::Any& /*PrintZoomPaperWidth*/, const uno::Any& /*PrintZoomPaperHeight*/ ) +{ + //TODO +} + +void SAL_CALL SwVbaDocument::PrintPreview( ) +{ + OUString url = ".uno:PrintPreview"; + dispatchRequests( mxModel,url ); +} + +void SAL_CALL SwVbaDocument::ClosePrintPreview( ) +{ + OUString url = ".uno:ClosePreview"; + dispatchRequests( mxModel,url ); +} + +uno::Any SAL_CALL +SwVbaDocument::Revisions( const uno::Any& index ) +{ + uno::Reference< css::document::XRedlinesSupplier > xRedlinesSupp( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xRedlines( xRedlinesSupp->getRedlines(), uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaRevisions( this, mxContext, getModel(), xRedlines ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaDocument::Frames( const uno::Any& index ) +{ + uno::Reference< text::XTextFramesSupplier > xTextFramesSupp( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xFrames( xTextFramesSupp->getTextFrames(), uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaFrames( this, mxContext, xFrames, getModel() ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +void SAL_CALL +SwVbaDocument::SaveAs2000( const uno::Any& FileName, const uno::Any& FileFormat, const uno::Any& /*LockComments*/, const uno::Any& /*Password*/, const uno::Any& /*AddToRecentFiles*/, const uno::Any& /*WritePassword*/, const uno::Any& /*ReadOnlyRecommended*/, const uno::Any& /*EmbedTrueTypeFonts*/, const uno::Any& /*SaveNativePictureFormat*/, const uno::Any& /*SaveFormsData*/, const uno::Any& /*SaveAsAOCELetter*/ ) +{ + SAL_INFO("sw.vba", "Document.SaveAs2000(FileName:=" << FileName << ",FileFormat:=" << FileFormat << ")"); + + // Based on ScVbaWorkbook::SaveAs. + OUString sFileName; + FileName >>= sFileName; + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( sFileName, sURL ); + + // Detect if there is no path then we need to use the current folder. + INetURLObject aURL( sURL ); + sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + if( sURL.isEmpty() ) + { + // Need to add cur dir ( of this document ) or else the 'Work' dir + sURL = getModel()->getURL(); + + if ( sURL.isEmpty() ) + { + // Not path available from 'this' document. Need to add the 'document'/work directory then. + // Based on SwVbaOptions::getValueEvent() + uno::Reference< util::XPathSettings > xPathSettings = util::thePathSettings::get( comphelper::getProcessComponentContext() ); + OUString sPathUrl; + xPathSettings->getPropertyValue( "Work" ) >>= sPathUrl; + // Path could be a multipath, Microsoft doesn't support this feature in Word currently. + // Only the last path is from interest. + sal_Int32 nIndex = sPathUrl.lastIndexOf( ';' ); + if( nIndex != -1 ) + { + sPathUrl = sPathUrl.copy( nIndex + 1 ); + } + + aURL.SetURL( sPathUrl ); + } + else + { + aURL.SetURL( sURL ); + aURL.Append( sFileName ); + } + sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + + } + + sal_Int32 nFileFormat = word::WdSaveFormat::wdFormatDocument; + FileFormat >>= nFileFormat; + + uno::Sequence< beans::PropertyValue > storeProps(1); + storeProps[0].Name = "FilterName" ; + + setFilterPropsFromFormat( nFileFormat, storeProps ); + + uno::Reference< frame::XStorable > xStor( getModel(), uno::UNO_QUERY_THROW ); + xStor->storeAsURL( sURL, storeProps ); +} + +void SAL_CALL +SwVbaDocument::SaveAs( const uno::Any& FileName, const uno::Any& FileFormat, const uno::Any& LockComments, const uno::Any& Password, const uno::Any& AddToRecentFiles, const uno::Any& WritePassword, const uno::Any& ReadOnlyRecommended, const uno::Any& EmbedTrueTypeFonts, const uno::Any& SaveNativePictureFormat, const uno::Any& SaveFormsData, const uno::Any& SaveAsAOCELetter, const uno::Any& /*Encoding*/, const uno::Any& /*InsertLineBreaks*/, const uno::Any& /*AllowSubstitutions*/, const uno::Any& /*LineEnding*/, const uno::Any& /*AddBiDiMarks*/ ) +{ + return SaveAs2000( FileName, FileFormat, LockComments, Password, AddToRecentFiles, WritePassword, ReadOnlyRecommended, EmbedTrueTypeFonts, SaveNativePictureFormat, SaveFormsData, SaveAsAOCELetter ); +} + +void SAL_CALL +SwVbaDocument::Close( const uno::Any& SaveChanges, const uno::Any& /*OriginalFormat*/, const uno::Any& /*RouteDocument*/ ) +{ + VbaDocumentBase::Close( SaveChanges, uno::Any(), uno::Any() ); +} + +void SAL_CALL +SwVbaDocument::SavePreviewPngAs( const uno::Any& FileName ) +{ + OUString sFileName; + FileName >>= sFileName; + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( sFileName, sURL ); + + uno::Sequence< beans::PropertyValue > storeProps(1); + storeProps[0].Name = "FilterName" ; + storeProps[0].Value <<= OUString("writer_png_Export"); + + uno::Reference< frame::XStorable > xStor( getModel(), uno::UNO_QUERY_THROW ); + xStor->storeToURL( sURL, storeProps ); +} + +uno::Any +SwVbaDocument::getControlShape( const OUString& sName ) +{ + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW ); + + sal_Int32 nCount = xIndexAccess->getCount(); + for( int index = 0; index < nCount; index++ ) + { + uno::Any aUnoObj = xIndexAccess->getByIndex( index ); + // It seems there are some drawing objects that can not query into Control shapes? + uno::Reference< drawing::XControlShape > xControlShape( aUnoObj, uno::UNO_QUERY ); + if( xControlShape.is() ) + { + uno::Reference< container::XNamed > xNamed( xControlShape->getControl(), uno::UNO_QUERY_THROW ); + if( sName == xNamed->getName() ) + { + return aUnoObj; + } + } + } + return uno::Any(); +} + +uno::Reference< beans::XIntrospectionAccess > SAL_CALL +SwVbaDocument::getIntrospection( ) +{ + return uno::Reference< beans::XIntrospectionAccess >(); +} + +uno::Any SAL_CALL +SwVbaDocument::invoke( const OUString& aFunctionName, const uno::Sequence< uno::Any >& /*aParams*/, uno::Sequence< ::sal_Int16 >& /*aOutParamIndex*/, uno::Sequence< uno::Any >& /*aOutParam*/ ) +{ + SAL_INFO("sw.vba", "** will barf " << aFunctionName ); + throw uno::RuntimeException(); // unsupported operation +} + +void SAL_CALL +SwVbaDocument::setValue( const OUString& /*aPropertyName*/, const uno::Any& /*aValue*/ ) +{ + throw uno::RuntimeException(); // unsupported operation +} +uno::Any SAL_CALL +SwVbaDocument::getValue( const OUString& aPropertyName ) +{ + uno::Reference< drawing::XControlShape > xControlShape( getControlShape( aPropertyName ), uno::UNO_QUERY_THROW ); + + uno::Reference<lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager(), uno::UNO_SET_THROW ); + uno::Reference< XControlProvider > xControlProvider( xServiceManager->createInstanceWithContext("ooo.vba.ControlProvider", mxContext ), uno::UNO_QUERY_THROW ); + uno::Reference< msforms::XControl > xControl( xControlProvider->createControl( xControlShape, getModel() ) ); + return uno::makeAny( xControl ); +} + +sal_Bool SAL_CALL +SwVbaDocument::hasMethod( const OUString& /*aName*/ ) +{ + return false; +} + +sal_Bool SAL_CALL +SwVbaDocument::hasProperty( const OUString& aName ) +{ + uno::Reference< container::XNameAccess > xFormControls( getFormControls() ); + if ( xFormControls.is() ) + return xFormControls->hasByName( aName ); + return false; +} + +uno::Reference< container::XNameAccess > +SwVbaDocument::getFormControls() const +{ + uno::Reference< container::XNameAccess > xFormControls; + try + { + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< form::XFormsSupplier > xFormSupplier( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xFormSupplier->getForms(), uno::UNO_QUERY_THROW ); + // get the www-standard container ( maybe we should access the + // 'www-standard' by name rather than index, this seems an + // implementation detail + xFormControls.set( xIndexAccess->getByIndex(0), uno::UNO_QUERY_THROW ); + } + catch(const uno::Exception&) + { + } + return xFormControls; +} + +// XInterfaceWithIID + +OUString SAL_CALL +SwVbaDocument::getIID() +{ + return "{82154424-0FBF-11d4-8313-005004526AB4}"; +} + +// XConnectable + +OUString SAL_CALL +SwVbaDocument::GetIIDForClassItselfNotCoclass() +{ + return "{82154428-0FBF-11D4-8313-005004526AB4}"; +} + +TypeAndIID SAL_CALL +SwVbaDocument::GetConnectionPoint() +{ + TypeAndIID aResult = + { word::XDocumentOutgoing::static_type(), + "{82154429-0FBF-11D4-8313-005004526AB4}" + }; + + return aResult; +} + +// XSinkCaller + +void SAL_CALL +SwVbaDocument::CallSinks( const OUString& Method, uno::Sequence< uno::Any >& Arguments ) +{ + for (auto& i : mvSinks) + { + if (i.is()) + i->Call(Method, Arguments); + } +} + +uno::Reference<XConnectionPoint> SAL_CALL +SwVbaDocument::FindConnectionPoint() +{ + uno::Reference<XConnectionPoint> xCP(new SwVbaDocumentOutgoingConnectionPoint(this)); + return xCP; +} + +// SwVbaApplicationOutgoingConnectionPoint + +SwVbaDocumentOutgoingConnectionPoint::SwVbaDocumentOutgoingConnectionPoint( SwVbaDocument* pDoc ) : + mpDoc(pDoc) +{ +} + +// XConnectionPoint + +sal_uInt32 SAL_CALL +SwVbaDocumentOutgoingConnectionPoint::Advise( const uno::Reference< XSink >& Sink ) +{ + return mpDoc->AddSink(Sink); +} + +void SAL_CALL +SwVbaDocumentOutgoingConnectionPoint::Unadvise( sal_uInt32 Cookie ) +{ + mpDoc->RemoveSink( Cookie ); +} + +uno::Sequence< OUString > +SwVbaDocument::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Document" + }; + return aServiceNames; +} + +namespace document +{ +namespace sdecl = comphelper::service_decl; +sdecl::vba_service_class_<SwVbaDocument, sdecl::with_args<true> > const serviceImpl; +sdecl::ServiceDecl const serviceDecl( + serviceImpl, + "SwVbaDocument", + "ooo.vba.word.Document" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocument.hxx b/sw/source/ui/vba/vbadocument.hxx new file mode 100644 index 000000000..a5e4dabf8 --- /dev/null +++ b/sw/source/ui/vba/vbadocument.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_UI_VBA_VBADOCUMENT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBADOCUMENT_HXX + +#include <ooo/vba/XSink.hpp> +#include <ooo/vba/XSinkCaller.hpp> +#include <ooo/vba/word/XDocument.hpp> +#include <vbahelper/vbadocumentbase.hxx> +#include <com/sun/star/text/XTextDocument.hpp> +#include <cppuhelper/implbase.hxx> + +#include <vector> + +typedef cppu::ImplInheritanceHelper< VbaDocumentBase, ooo::vba::word::XDocument, ooo::vba::XSinkCaller > SwVbaDocument_BASE; + +class SwVbaDocument : public SwVbaDocument_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + + std::vector<css::uno::Reference< ooo::vba::XSink >> mvSinks; + + void Initialize(); + css::uno::Any getControlShape( const OUString& sName ); + css::uno::Reference< css::container::XNameAccess > getFormControls() const; + +public: + SwVbaDocument( const css::uno::Reference< ooo::vba::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& m_xContext, css::uno::Reference< css::frame::XModel > const & xModel ); + SwVbaDocument( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& xContext ); + virtual ~SwVbaDocument() override; + + sal_uInt32 AddSink( const css::uno::Reference< ooo::vba::XSink >& xSink ); + void RemoveSink( sal_uInt32 nNumber ); + + // XDocument + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL getContent() override; + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL Range( const css::uno::Any& rStart, const css::uno::Any& rEnd ) override; + virtual css::uno::Any SAL_CALL BuiltInDocumentProperties( const css::uno::Any& index ) override; + virtual css::uno::Any SAL_CALL CustomDocumentProperties( const css::uno::Any& index ) override; + virtual css::uno::Any SAL_CALL Bookmarks( const css::uno::Any& rIndex ) override; + virtual css::uno::Any SAL_CALL Variables( const css::uno::Any& rIndex ) override; + virtual css::uno::Any SAL_CALL getAttachedTemplate() override; + virtual void SAL_CALL setAttachedTemplate( const css::uno::Any& _attachedtemplate ) override; + virtual css::uno::Any SAL_CALL Paragraphs( const css::uno::Any& rIndex ) override; + virtual css::uno::Any SAL_CALL Styles( const css::uno::Any& rIndex ) override; + virtual css::uno::Any SAL_CALL Tables( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Fields( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Shapes( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Sections( const css::uno::Any& aIndex ) override; + virtual void SAL_CALL Activate() override; + virtual css::uno::Any SAL_CALL PageSetup() override; + virtual css::uno::Any SAL_CALL TablesOfContents( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL FormFields( const css::uno::Any& aIndex ) override; + virtual ::sal_Int32 SAL_CALL getProtectionType() override; + virtual void SAL_CALL setProtectionType( ::sal_Int32 _protectiontype ) override; + virtual sal_Bool SAL_CALL getUpdateStylesOnOpen() override; + virtual void SAL_CALL setUpdateStylesOnOpen( sal_Bool _updatestylesonopen ) override; + virtual sal_Bool SAL_CALL getAutoHyphenation() override; + virtual void SAL_CALL setAutoHyphenation( sal_Bool _autohyphenation ) override; + virtual ::sal_Int32 SAL_CALL getHyphenationZone() override; + virtual void SAL_CALL setHyphenationZone( ::sal_Int32 _hyphenationzone ) override; + virtual ::sal_Int32 SAL_CALL getConsecutiveHyphensLimit() override; + virtual void SAL_CALL setConsecutiveHyphensLimit( ::sal_Int32 _consecutivehyphenslimit ) override; + virtual css::uno::Reference< ooo::vba::word::XMailMerge > SAL_CALL getMailMerge() override; + + using VbaDocumentBase::Protect; + virtual void SAL_CALL Protect( ::sal_Int32 Type, const css::uno::Any& NOReset, const css::uno::Any& Password, const css::uno::Any& UseIRM, const css::uno::Any& EnforceStyleLock ) override; + virtual void SAL_CALL PrintOut( const css::uno::Any& Background, const css::uno::Any& Append, const css::uno::Any& Range, const css::uno::Any& OutputFileName, const css::uno::Any& From, const css::uno::Any& To, const css::uno::Any& Item, const css::uno::Any& Copies, const css::uno::Any& Pages, const css::uno::Any& PageType, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& FileName, const css::uno::Any& ActivePrinterMacGX, const css::uno::Any& ManualDuplexPrint, const css::uno::Any& PrintZoomColumn, const css::uno::Any& PrintZoomRow, const css::uno::Any& PrintZoomPaperWidth, const css::uno::Any& PrintZoomPaperHeight ) override; + virtual void SAL_CALL PrintPreview( ) override; + virtual void SAL_CALL ClosePrintPreview( ) override; + virtual css::uno::Any SAL_CALL Revisions( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Frames( const css::uno::Any& aIndex ) override; + virtual void SAL_CALL SaveAs2000( const css::uno::Any& FileName, const css::uno::Any& FileFormat, const css::uno::Any& LockComments, const css::uno::Any& Password, const css::uno::Any& AddToRecentFiles, const css::uno::Any& WritePassword, const css::uno::Any& ReadOnlyRecommended, const css::uno::Any& EmbedTrueTypeFonts, const css::uno::Any& SaveNativePictureFormat, const css::uno::Any& SaveFormsData, const css::uno::Any& SaveAsAOCELetter ) override; + virtual void SAL_CALL SaveAs( const css::uno::Any& FileName, const css::uno::Any& FileFormat, const css::uno::Any& LockComments, const css::uno::Any& Password, const css::uno::Any& AddToRecentFiles, const css::uno::Any& WritePassword, const css::uno::Any& ReadOnlyRecommended, const css::uno::Any& EmbedTrueTypeFonts, const css::uno::Any& SaveNativePictureFormat, const css::uno::Any& SaveFormsData, const css::uno::Any& SaveAsAOCELetter, const css::uno::Any& Encoding, const css::uno::Any& InsertLineBreaks, const css::uno::Any& AllowSubstitutions, const css::uno::Any& LineEnding, const css::uno::Any& AddBiDiMarks ) override; + virtual void SAL_CALL Close( const css::uno::Any& SaveChanges, const css::uno::Any& OriginalFormat, const css::uno::Any& RouteDocument ) override; + virtual void SAL_CALL SavePreviewPngAs( const css::uno::Any& FileName ) override; + + // XInvocation + virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection( ) override; + virtual css::uno::Any SAL_CALL invoke( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override; + virtual void SAL_CALL setValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getValue( const OUString& aPropertyName ) override; + virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override; + + // XInterfaceWithIID + virtual OUString SAL_CALL getIID() override; + + // XConnectable + virtual OUString SAL_CALL GetIIDForClassItselfNotCoclass() override; + virtual ov::TypeAndIID SAL_CALL GetConnectionPoint() override; + virtual css::uno::Reference<ov::XConnectionPoint> SAL_CALL FindConnectionPoint() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + + // XSinkCaller + virtual void SAL_CALL CallSinks( const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments ) override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBADOCUMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocumentproperties.cxx b/sw/source/ui/vba/vbadocumentproperties.cxx new file mode 100644 index 000000000..af04be582 --- /dev/null +++ b/sw/source/ui/vba/vbadocumentproperties.cxx @@ -0,0 +1,922 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbadocumentproperties.hxx" +#include <cppuhelper/implbase.hxx> +#include <sal/log.hxx> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <ooo/vba/word/WdBuiltInProperty.hpp> +#include <ooo/vba/office/MsoDocProperties.hpp> +#include <tools/diagnose_ex.h> +#include <memory> +#include "wordvbahelper.hxx" +#include <fesh.hxx> +#include <docsh.hxx> +using namespace ::ooo::vba; +using namespace css; + +/// @throws lang::IllegalArgumentException +static sal_Int8 lcl_toMSOPropType( const uno::Type& aType ) +{ + sal_Int16 msoType = office::MsoDocProperties::msoPropertyTypeString; + + switch ( aType.getTypeClass() ) + { + case uno::TypeClass_BOOLEAN: + msoType = office::MsoDocProperties::msoPropertyTypeBoolean; + break; + case uno::TypeClass_FLOAT: + msoType = office::MsoDocProperties::msoPropertyTypeFloat; + break; + case uno::TypeClass_STRUCT: // Assume date + msoType = office::MsoDocProperties::msoPropertyTypeDate; + break; + case uno::TypeClass_BYTE: + case uno::TypeClass_SHORT: + case uno::TypeClass_LONG: + case uno::TypeClass_HYPER: + msoType = office::MsoDocProperties::msoPropertyTypeNumber; + break; + default: + throw lang::IllegalArgumentException(); + } + return msoType; +} + +namespace { + +class PropertGetSetHelper +{ +protected: + uno::Reference< frame::XModel > m_xModel; + uno::Reference<document::XDocumentProperties> m_xDocProps; +public: + explicit PropertGetSetHelper( const uno::Reference< frame::XModel >& xModel ):m_xModel( xModel ) + { + uno::Reference<document::XDocumentPropertiesSupplier> const + xDocPropSupp(m_xModel, uno::UNO_QUERY_THROW); + m_xDocProps.set(xDocPropSupp->getDocumentProperties(), + uno::UNO_SET_THROW); + } + virtual ~PropertGetSetHelper() {} + virtual uno::Any getPropertyValue( const OUString& rPropName ) = 0; + virtual void setPropertyValue( const OUString& rPropName, const uno::Any& aValue ) = 0; + uno::Reference< beans::XPropertySet > getUserDefinedProperties() { + return uno::Reference<beans::XPropertySet>( + m_xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW); + } + +}; + +class BuiltinPropertyGetSetHelper : public PropertGetSetHelper +{ +public: + explicit BuiltinPropertyGetSetHelper( const uno::Reference< frame::XModel >& xModel ) :PropertGetSetHelper( xModel ) + { + } + virtual uno::Any getPropertyValue( const OUString& rPropName ) override + { + if ( rPropName == "EditingDuration" ) + { + sal_Int32 const nSecs = m_xDocProps->getEditingDuration(); + return uno::makeAny( nSecs/60 ); // minutes + } + else if ("Title" == rPropName) + { + return uno::makeAny(m_xDocProps->getTitle()); + } + else if ("Subject" == rPropName) + { + return uno::makeAny(m_xDocProps->getSubject()); + } + else if ("Author" == rPropName) + { + return uno::makeAny(m_xDocProps->getAuthor()); + } + else if ("Keywords" == rPropName) + { + return uno::makeAny(m_xDocProps->getKeywords()); + } + else if ("Description" == rPropName) + { + return uno::makeAny(m_xDocProps->getDescription()); + } + else if ("Template" == rPropName) + { + return uno::makeAny(m_xDocProps->getTemplateName()); + } + else if ("ModifiedBy" == rPropName) + { + return uno::makeAny(m_xDocProps->getModifiedBy()); + } + else if ("Generator" == rPropName) + { + return uno::makeAny(m_xDocProps->getGenerator()); + } + else if ("PrintDate" == rPropName) + { + return uno::makeAny(m_xDocProps->getPrintDate()); + } + else if ("CreationDate" == rPropName) + { + return uno::makeAny(m_xDocProps->getCreationDate()); + } + else if ("ModifyDate" == rPropName) + { + return uno::makeAny(m_xDocProps->getModificationDate()); + } + else if ("AutoloadURL" == rPropName) + { + return uno::makeAny(m_xDocProps->getAutoloadURL()); + } + else + { + // fall back to user-defined properties + return getUserDefinedProperties()->getPropertyValue(rPropName); + } + } + virtual void setPropertyValue( const OUString& rPropName, const uno::Any& aValue ) override + { + if ("EditingDuration" == rPropName) + { + sal_Int32 nMins = 0; + if (aValue >>= nMins) + { + m_xDocProps->setEditingDuration(nMins * 60); // convert minutes + } + } + else if ("Title" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setTitle(str); + } + } + else if ("Subject" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setSubject(str); + } + } + else if ("Author" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setAuthor(str); + } + } + else if ("Keywords" == rPropName) + { + uno::Sequence<OUString> keywords; + if (aValue >>= keywords) + { + m_xDocProps->setKeywords(keywords); + } + } + else if ("Description" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setDescription(str); + } + } + else if ("Template" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setTemplateName(str); + } + } + else if ("ModifiedBy" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setModifiedBy(str); + } + } + else if ("Generator" == rPropName) + { + OUString str; + if (aValue >>= str) + { + return m_xDocProps->setGenerator(str); + } + } + else if ("PrintDate" == rPropName) + { + util::DateTime dt; + if (aValue >>= dt) + { + m_xDocProps->setPrintDate(dt); + } + } + else if ("CreationDate" == rPropName) + { + util::DateTime dt; + if (aValue >>= dt) + { + m_xDocProps->setCreationDate(dt); + } + } + else if ("ModifyDate" == rPropName) + { + util::DateTime dt; + if (aValue >>= dt) + { + m_xDocProps->setModificationDate(dt); + } + } + else if ("AutoloadURL" == rPropName) + { + OUString str; + if (aValue >>= str) + { + m_xDocProps->setAutoloadURL(str); + } + } + else + { + // fall back to user-defined properties + getUserDefinedProperties()->setPropertyValue(rPropName, aValue); + } + } +}; + +class CustomPropertyGetSetHelper : public BuiltinPropertyGetSetHelper +{ +public: + explicit CustomPropertyGetSetHelper( const uno::Reference< frame::XModel >& xModel ) :BuiltinPropertyGetSetHelper( xModel ) + { + } + virtual uno::Any getPropertyValue( const OUString& rPropName ) override + { + return getUserDefinedProperties()->getPropertyValue(rPropName); + } + virtual void setPropertyValue( + const OUString& rPropName, const uno::Any& rValue) override + { + return getUserDefinedProperties()->setPropertyValue(rPropName, rValue); + } +}; + +class StatisticPropertyGetSetHelper : public PropertGetSetHelper +{ + SwDocShell* mpDocShell; + uno::Reference< beans::XPropertySet > mxModelProps; +public: + explicit StatisticPropertyGetSetHelper( const uno::Reference< frame::XModel >& xModel ) :PropertGetSetHelper( xModel ) , mpDocShell( nullptr ) + { + mxModelProps.set( m_xModel, uno::UNO_QUERY_THROW ); + mpDocShell = word::getDocShell( xModel ); + } + virtual uno::Any getPropertyValue( const OUString& rPropName ) override + { + try + { + // Characters, ParagraphCount & WordCount are available from + // the model ( and additionally these also update the statics object ) + return mxModelProps->getPropertyValue( rPropName ); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.vba", ""); + } + uno::Any aReturn; + if ( rPropName == "LineCount" ) // special processing needed + { + if ( mpDocShell ) + { + SwFEShell* pFEShell = mpDocShell->GetFEShell(); + if(pFEShell) + { + aReturn <<= pFEShell->GetLineCount(); + } + } + } + else + { + uno::Sequence< beans::NamedValue > const stats( + m_xDocProps->getDocumentStatistics()); + + auto pStat = std::find_if(stats.begin(), stats.end(), + [&rPropName](const beans::NamedValue& rStat) { return rPropName == rStat.Name; }); + if (pStat == stats.end()) + throw uno::RuntimeException(); // bad Property + + aReturn = pStat->Value; + } + return aReturn; + } + + virtual void setPropertyValue( const OUString& rPropName, const uno::Any& aValue ) override + { + uno::Sequence< beans::NamedValue > stats( + m_xDocProps->getDocumentStatistics()); + + auto pStat = std::find_if(stats.begin(), stats.end(), + [&rPropName](const beans::NamedValue& rStat) { return rPropName == rStat.Name; }); + if (pStat != stats.end()) + { + pStat->Value = aValue; + m_xDocProps->setDocumentStatistics(stats); + } + } +}; + +class DocPropInfo +{ +public: + OUString msMSODesc; + OUString msOOOPropName; + std::shared_ptr< PropertGetSetHelper > mpPropGetSetHelper; + + static DocPropInfo createDocPropInfo( const OUString& sDesc, const OUString& sPropName, std::shared_ptr< PropertGetSetHelper > const & rHelper ) + { + DocPropInfo aItem; + aItem.msMSODesc = sDesc; + aItem.msOOOPropName = sPropName; + aItem.mpPropGetSetHelper = rHelper; + return aItem; + } + + static DocPropInfo createDocPropInfo( const char* sDesc, const char* sPropName, std::shared_ptr< PropertGetSetHelper > const & rHelper ) + { + return createDocPropInfo( OUString::createFromAscii( sDesc ), OUString::createFromAscii( sPropName ), rHelper ); + } + uno::Any getValue() + { + if ( mpPropGetSetHelper ) + return mpPropGetSetHelper->getPropertyValue( msOOOPropName ); + return uno::Any(); + } + void setValue( const uno::Any& rValue ) + { + if ( mpPropGetSetHelper ) + mpPropGetSetHelper->setPropertyValue( msOOOPropName, rValue ); + } + uno::Reference< beans::XPropertySet > getUserDefinedProperties() + { + uno::Reference< beans::XPropertySet > xProps; + if ( mpPropGetSetHelper ) + return mpPropGetSetHelper->getUserDefinedProperties(); + return xProps; + } +}; + +} + +typedef std::unordered_map< sal_Int32, DocPropInfo > MSOIndexToOODocPropInfo; + +namespace { + +class BuiltInIndexHelper +{ + MSOIndexToOODocPropInfo m_docPropInfoMap; + +public: + explicit BuiltInIndexHelper( const uno::Reference< frame::XModel >& xModel ) + { + auto aStandardHelper = std::make_shared<BuiltinPropertyGetSetHelper>( xModel ); + auto aUsingStatsHelper = std::make_shared<StatisticPropertyGetSetHelper>( xModel ); + + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyTitle ] = DocPropInfo::createDocPropInfo( "Title", "Title", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertySubject ] = DocPropInfo::createDocPropInfo( "Subject", "Subject", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyAuthor ] = DocPropInfo::createDocPropInfo( "Author", "Author", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyKeywords ] = DocPropInfo::createDocPropInfo( "Keywords", "Keywords", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyComments ] = DocPropInfo::createDocPropInfo( "Comments", "Description", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyTemplate ] = DocPropInfo::createDocPropInfo( "Template", "Template", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyLastAuthor ] = DocPropInfo::createDocPropInfo( "Last author", "ModifiedBy", aStandardHelper ); // doesn't seem to exist - throw or return nothing ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyRevision ] = DocPropInfo::createDocPropInfo( "Revision number", "EditingCycles", aStandardHelper ); // doesn't seem to exist - throw or return nothing ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyAppName ] = DocPropInfo::createDocPropInfo( "Application name", "Generator", aStandardHelper ); // doesn't seem to exist - throw or return nothing ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyTimeLastPrinted ] = DocPropInfo::createDocPropInfo( "Last print date", "PrintDate", aStandardHelper ); // doesn't seem to exist - throw or return nothing ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyTimeCreated ] = DocPropInfo::createDocPropInfo( "Creation date", "CreationDate", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyTimeLastSaved ] = DocPropInfo::createDocPropInfo( "Last save time", "ModifyDate", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyVBATotalEdit ] = DocPropInfo::createDocPropInfo( "Total editing time", "EditingDuration", aStandardHelper ); // Not sure if this is correct + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyPages ] = DocPropInfo::createDocPropInfo( "Number of pages", "PageCount", aUsingStatsHelper ); // special handling required ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyWords ] = DocPropInfo::createDocPropInfo( "Number of words", "WordCount", aUsingStatsHelper ); // special handling require ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyCharacters ] = DocPropInfo::createDocPropInfo( "Number of characters", "CharacterCount", aUsingStatsHelper ); // special handling required ? + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertySecurity ] = DocPropInfo::createDocPropInfo( "Security", "", aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyCategory ] = DocPropInfo::createDocPropInfo( "Category", "Category", aStandardHelper ); // hacked in + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyFormat ] = DocPropInfo::createDocPropInfo( "Format", "", aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyManager ] = DocPropInfo::createDocPropInfo( "Manager", "Manager", aStandardHelper ); // hacked in + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyCompany ] = DocPropInfo::createDocPropInfo( "Company", "Company", aStandardHelper ); // hacked in + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyBytes ] = DocPropInfo::createDocPropInfo( "Number of bytes", "", aStandardHelper ); // doesn't seem to exist - size on disk exists ( for an already saved document ) perhaps it will do ( or we need something else ) + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyLines ] = DocPropInfo::createDocPropInfo( "Number of lines", "LineCount", aUsingStatsHelper ); // special handling + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyParas ] = DocPropInfo::createDocPropInfo( "Number of paragraphs", "ParagraphCount", aUsingStatsHelper ); // special handling + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertySlides ] = DocPropInfo::createDocPropInfo( "Number of slides", "" , aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyNotes ] = DocPropInfo::createDocPropInfo( "Number of notes", "", aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyHiddenSlides ] = DocPropInfo::createDocPropInfo("Number of hidden Slides", "", aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyMMClips ] = DocPropInfo::createDocPropInfo( "Number of multimedia clips", "", aStandardHelper ); // doesn't seem to exist + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyHyperlinkBase ] = DocPropInfo::createDocPropInfo( "Hyperlink base", "AutoloadURL", aStandardHelper ); + m_docPropInfoMap[ word::WdBuiltInProperty::wdPropertyCharsWSpaces ] = DocPropInfo::createDocPropInfo( "Number of characters (with spaces)", "", aStandardHelper ); // doesn't seem to be supported + } + + MSOIndexToOODocPropInfo& getDocPropInfoMap() { return m_docPropInfoMap; } +}; + +} + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::XDocumentProperty > SwVbaDocumentProperty_BASE; + +namespace { + +class SwVbaBuiltInDocumentProperty : public SwVbaDocumentProperty_BASE +{ +protected: + DocPropInfo mPropInfo; +public: + SwVbaBuiltInDocumentProperty( const uno::Reference< ov::XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const DocPropInfo& rInfo ); + // XDocumentProperty + virtual void SAL_CALL Delete( ) override; + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& Name ) override; + virtual ::sal_Int8 SAL_CALL getType( ) override; + virtual void SAL_CALL setType( ::sal_Int8 Type ) override; + virtual sal_Bool SAL_CALL getLinkToContent( ) override; + virtual void SAL_CALL setLinkToContent( sal_Bool LinkToContent ) override; + virtual uno::Any SAL_CALL getValue( ) override; + virtual void SAL_CALL setValue( const uno::Any& Value ) override; + virtual OUString SAL_CALL getLinkSource( ) override; + virtual void SAL_CALL setLinkSource( const OUString& LinkSource ) override; + //XDefaultProperty + virtual OUString SAL_CALL getDefaultPropertyName( ) override { return "Value"; } + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual uno::Sequence<OUString> getServiceNames() override; +}; + +class SwVbaCustomDocumentProperty : public SwVbaBuiltInDocumentProperty +{ +public: + + SwVbaCustomDocumentProperty( const uno::Reference< ov::XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const DocPropInfo& rInfo ); + + virtual sal_Bool SAL_CALL getLinkToContent( ) override; + virtual void SAL_CALL setLinkToContent( sal_Bool LinkToContent ) override; + + virtual OUString SAL_CALL getLinkSource( ) override; + virtual void SAL_CALL setLinkSource( const OUString& LinkSource ) override; + virtual void SAL_CALL Delete( ) override; + virtual void SAL_CALL setName( const OUString& Name ) override; + virtual void SAL_CALL setType( ::sal_Int8 Type ) override; + +}; + +} + +SwVbaCustomDocumentProperty::SwVbaCustomDocumentProperty( const uno::Reference< ov::XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const DocPropInfo& rInfo ) : SwVbaBuiltInDocumentProperty( xParent, xContext, rInfo ) +{ +} + +sal_Bool +SwVbaCustomDocumentProperty::getLinkToContent( ) +{ + // #FIXME we need to store the link content somewhere + return false; +} + +void +SwVbaCustomDocumentProperty::setLinkToContent( sal_Bool /*bLinkContent*/ ) +{ +} + +OUString +SwVbaCustomDocumentProperty::getLinkSource( ) +{ + // #FIXME we need to store the link content somewhere + return OUString(); +} + +void +SwVbaCustomDocumentProperty::setLinkSource( const OUString& /*rsLinkContent*/ ) +{ + // #FIXME we need to store the link source somewhere +} + +void SAL_CALL +SwVbaCustomDocumentProperty::setName( const OUString& /*Name*/ ) +{ + // setName on existing property ? + // #FIXME + // do we need to delete existing property and create a new one? +} + +void SAL_CALL +SwVbaCustomDocumentProperty::setType( ::sal_Int8 /*Type*/ ) +{ + // setType, do we need to do a conversion? + // #FIXME the underlying value needs to be changed to the new type +} + +void SAL_CALL +SwVbaCustomDocumentProperty::Delete( ) +{ + uno::Reference< beans::XPropertyContainer > xContainer( + mPropInfo.getUserDefinedProperties(), uno::UNO_QUERY_THROW); + xContainer->removeProperty( getName() ); +} + +SwVbaBuiltInDocumentProperty::SwVbaBuiltInDocumentProperty( const uno::Reference< ov::XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const DocPropInfo& rInfo ) : SwVbaDocumentProperty_BASE( xParent, xContext ), mPropInfo( rInfo ) +{ +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::Delete( ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +OUString SAL_CALL +SwVbaBuiltInDocumentProperty::getName( ) +{ + return mPropInfo.msMSODesc; +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::setName( const OUString& ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +::sal_Int8 SAL_CALL +SwVbaBuiltInDocumentProperty::getType( ) +{ + return lcl_toMSOPropType( getValue().getValueType() ); +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::setType( ::sal_Int8 /*Type*/ ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +sal_Bool SAL_CALL +SwVbaBuiltInDocumentProperty::getLinkToContent( ) +{ + return false; // built-in always false +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::setLinkToContent( sal_Bool /*LinkToContent*/ ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +uno::Any SAL_CALL +SwVbaBuiltInDocumentProperty::getValue( ) +{ + uno::Any aRet = mPropInfo.getValue(); + if ( !aRet.hasValue() ) + throw uno::RuntimeException(); + return aRet; +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::setValue( const uno::Any& Value ) +{ + mPropInfo.setValue( Value ); +} + +OUString SAL_CALL +SwVbaBuiltInDocumentProperty::getLinkSource( ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +void SAL_CALL +SwVbaBuiltInDocumentProperty::setLinkSource( const OUString& /*LinkSource*/ ) +{ + // not valid for Builtin + throw uno::RuntimeException(); +} + +OUString +SwVbaBuiltInDocumentProperty::getServiceImplName() +{ + return "SwVbaBuiltinDocumentProperty"; +} + +uno::Sequence<OUString> +SwVbaBuiltInDocumentProperty::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.DocumentProperty" + }; + return aServiceNames; +} +typedef ::cppu::WeakImplHelper< css::container::XIndexAccess + ,css::container::XNameAccess + ,css::container::XEnumerationAccess + > PropertiesImpl_BASE; + +typedef std::unordered_map< sal_Int32, uno::Reference< XDocumentProperty > > DocProps; + +namespace { + +class DocPropEnumeration : public ::cppu::WeakImplHelper< css::container::XEnumeration > +{ + DocProps mDocProps; + DocProps::iterator mIt; +public: + + explicit DocPropEnumeration( const DocProps& rProps ) : mDocProps( rProps ), mIt( mDocProps.begin() ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return mIt != mDocProps.end(); + } + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( !hasMoreElements() ) + throw container::NoSuchElementException(); + return uno::makeAny( mIt++->second ); + } +}; + +} + +typedef std::unordered_map< OUString, uno::Reference< XDocumentProperty > > DocPropsByName; + +namespace { + +class BuiltInPropertiesImpl : public PropertiesImpl_BASE +{ +protected: + + uno::Reference< frame::XModel > m_xModel; + + DocProps mDocProps; + DocPropsByName mNamedDocProps; + + public: + BuiltInPropertiesImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : m_xModel( xModel ) + { + BuiltInIndexHelper builtIns( m_xModel ); + for ( sal_Int32 index = word::WdBuiltInProperty::wdPropertyTitle; index <= word::WdBuiltInProperty::wdPropertyCharsWSpaces; ++index ) + { + mDocProps[ index ] = new SwVbaBuiltInDocumentProperty( xParent, xContext, builtIns.getDocPropInfoMap()[ index ] ); + mNamedDocProps[ mDocProps[ index ]->getName() ] = mDocProps[ index ]; + } + } +// XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + return mDocProps.size(); + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + // correct the correct by the base class for 1 based indices + DocProps::iterator it = mDocProps.find( ++Index ); + if ( it == mDocProps.end() ) + throw lang::IndexOutOfBoundsException(); + return uno::makeAny( it->second ); + } + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + if ( !hasByName( aName ) ) + throw container::NoSuchElementException(); + DocPropsByName::iterator it = mNamedDocProps.find( aName ); + return uno::Any( it->second ); + + } + virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + uno::Sequence< OUString > aNames( getCount() ); + OUString* pName = aNames.getArray(); + for (const auto& rEntry : mNamedDocProps) + { + *pName = rEntry.first; + ++pName; + } + return aNames; + } + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + DocPropsByName::iterator it = mNamedDocProps.find( aName ); + if ( it == mNamedDocProps.end() ) + return false; + return true; + } +// XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<XDocumentProperty>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return !mDocProps.empty(); + } + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new DocPropEnumeration( mDocProps ); + } +}; + +} + +SwVbaBuiltinDocumentProperties::SwVbaBuiltinDocumentProperties( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : SwVbaDocumentproperties_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new BuiltInPropertiesImpl( xParent, xContext, xModel ) ) ) +{ +} + +uno::Reference< XDocumentProperty > SAL_CALL +SwVbaBuiltinDocumentProperties::Add( const OUString& /*Name*/, sal_Bool /*LinkToContent*/, ::sal_Int8 /*Type*/, const uno::Any& /*value*/, const uno::Any& /*LinkSource*/ ) +{ + throw uno::RuntimeException( "not supported for Builtin properties" ); +} + +// XEnumerationAccess +uno::Type SAL_CALL +SwVbaBuiltinDocumentProperties::getElementType() +{ + return cppu::UnoType<XDocumentProperty>::get(); +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwVbaBuiltinDocumentProperties::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumAccess->createEnumeration(); +} + +// ScVbaCollectionBaseImpl +uno::Any +SwVbaBuiltinDocumentProperties::createCollectionObject( const uno::Any& aSource ) +{ + // pass through + return aSource; +} + +// XHelperInterface +OUString +SwVbaBuiltinDocumentProperties::getServiceImplName() +{ + return "SwVbaBuiltinDocumentProperties"; +} + +uno::Sequence<OUString> +SwVbaBuiltinDocumentProperties::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.DocumentProperties" + }; + return aServiceNames; +} + +namespace { + +class CustomPropertiesImpl : public PropertiesImpl_BASE +{ + uno::Reference< XHelperInterface > m_xParent; + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< frame::XModel > m_xModel; + uno::Reference< beans::XPropertySet > mxUserDefinedProp; + std::shared_ptr< PropertGetSetHelper > mpPropGetSetHelper; +public: + CustomPropertiesImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : m_xParent( xParent ), m_xContext( xContext ), m_xModel( xModel ) + { + // suck in the document( custom ) properties + mpPropGetSetHelper = std::make_shared<CustomPropertyGetSetHelper>( m_xModel ); + mxUserDefinedProp.set(mpPropGetSetHelper->getUserDefinedProperties(), + uno::UNO_SET_THROW); + }; + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + return mxUserDefinedProp->getPropertySetInfo()->getProperties().getLength(); + } + + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + uno::Sequence< beans::Property > aProps = mxUserDefinedProp->getPropertySetInfo()->getProperties(); + if ( Index >= aProps.getLength() ) + throw lang::IndexOutOfBoundsException(); + // How to determine type e.g Date? ( com.sun.star.util.DateTime ) + DocPropInfo aPropInfo = DocPropInfo::createDocPropInfo( aProps[ Index ].Name, aProps[ Index ].Name, mpPropGetSetHelper ); + return uno::makeAny( uno::Reference< XDocumentProperty >( new SwVbaCustomDocumentProperty( m_xParent, m_xContext, aPropInfo ) ) ); + } + + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + if ( !hasByName( aName ) ) + throw container::NoSuchElementException(); + + DocPropInfo aPropInfo = DocPropInfo::createDocPropInfo( aName, aName, mpPropGetSetHelper ); + return uno::makeAny( uno::Reference< XDocumentProperty >( new SwVbaCustomDocumentProperty( m_xParent, m_xContext, aPropInfo ) ) ); + } + + virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + uno::Sequence< beans::Property > aProps = mxUserDefinedProp->getPropertySetInfo()->getProperties(); + uno::Sequence< OUString > aNames( aProps.getLength() ); + std::transform(aProps.begin(), aProps.end(), aNames.begin(), + [](const beans::Property& rProp) -> OUString { return rProp.Name; }); + return aNames; + } + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + SAL_INFO("sw.vba", "hasByName(" << aName << ") returns " << mxUserDefinedProp->getPropertySetInfo()->hasPropertyByName( aName ) ); + return mxUserDefinedProp->getPropertySetInfo()->hasPropertyByName( aName ); + } + + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<XDocumentProperty>::get(); + } + + virtual sal_Bool SAL_CALL hasElements( ) override + { + return getCount() > 0; + } + + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + // create a map of properties ( the key doesn't matter ) + SAL_INFO("sw.vba", "Creating an enumeration"); + sal_Int32 key = 0; + sal_Int32 nElem = getCount(); + DocProps simpleDocPropSnapShot; + for ( ; key < nElem; ++key ) + simpleDocPropSnapShot[ key ].set( getByIndex( key ), uno::UNO_QUERY_THROW ); + SAL_INFO("sw.vba", "After creating the enumeration"); + return new DocPropEnumeration( simpleDocPropSnapShot ); + } + + void addProp( const OUString& Name, const uno::Any& Value ) + { + uno::Reference< beans::XPropertyContainer > xContainer( mxUserDefinedProp, uno::UNO_QUERY_THROW ); + // TODO fixme, perform the necessary Type Value conversions + xContainer->addProperty( Name, sal_Int16(128), Value ); + } + +}; + +} + +SwVbaCustomDocumentProperties::SwVbaCustomDocumentProperties( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : SwVbaBuiltinDocumentProperties( xParent, xContext, xModel ) +{ + // replace the m_xIndexAccess implementation ( we need a virtual init ) + m_xIndexAccess.set( new CustomPropertiesImpl( xParent, xContext, xModel ) ); + m_xNameAccess.set( m_xIndexAccess, uno::UNO_QUERY_THROW ); +} + +uno::Reference< XDocumentProperty > SAL_CALL +SwVbaCustomDocumentProperties::Add( const OUString& Name, sal_Bool LinkToContent, ::sal_Int8 /*Type*/, const uno::Any& Value, const uno::Any& LinkSource ) +{ + CustomPropertiesImpl* pCustomProps = dynamic_cast< CustomPropertiesImpl* > ( m_xIndexAccess.get() ); + uno::Reference< XDocumentProperty > xDocProp; + if ( pCustomProps ) + { + OUString sLinkSource; + pCustomProps->addProp( Name, Value ); + + xDocProp.set( m_xNameAccess->getByName( Name ), uno::UNO_QUERY_THROW ); + xDocProp->setLinkToContent( LinkToContent ); + + if ( LinkSource >>= sLinkSource ) + xDocProp->setLinkSource( sLinkSource ); + } + return xDocProp; +} + +// XHelperInterface +OUString +SwVbaCustomDocumentProperties::getServiceImplName() +{ + return "SwVbaCustomDocumentProperties"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocumentproperties.hxx b/sw/source/ui/vba/vbadocumentproperties.hxx new file mode 100644 index 000000000..740353e82 --- /dev/null +++ b/sw/source/ui/vba/vbadocumentproperties.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_UI_VBA_VBADOCUMENTPROPERTIES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBADOCUMENTPROPERTIES_HXX + +#include <ooo/vba/XDocumentProperties.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <vbahelper/vbacollectionimpl.hxx> + +typedef CollTestImplHelper< ov::XDocumentProperties > SwVbaDocumentproperties_BASE; + +class SwVbaBuiltinDocumentProperties : public SwVbaDocumentproperties_BASE +{ +public: + SwVbaBuiltinDocumentProperties( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xDocument ); + + // XDocumentProperties + virtual css::uno::Reference< ::ooo::vba::XDocumentProperty > SAL_CALL Add( const OUString& Name, sal_Bool LinkToContent, ::sal_Int8 Type, const css::uno::Any& Value, const css::uno::Any& LinkSource ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + // ScVbaCollectionBaseImpl + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +class SwVbaCustomDocumentProperties : public SwVbaBuiltinDocumentProperties +{ +public: + SwVbaCustomDocumentProperties( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xDocument ); +// XDocumentProperties + virtual css::uno::Reference< ::ooo::vba::XDocumentProperty > SAL_CALL Add( const OUString& Name, sal_Bool LinkToContent, ::sal_Int8 Type, const css::uno::Any& Value, const css::uno::Any& LinkSource ) override; + // XHelperInterface + virtual OUString getServiceImplName() override; +}; + +#endif /* SW_VBA_DOCUMENTPROPERTY_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocuments.cxx b/sw/source/ui/vba/vbadocuments.cxx new file mode 100644 index 000000000..5c8dea110 --- /dev/null +++ b/sw/source/ui/vba/vbadocuments.cxx @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XTextDocument.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <tools/urlobj.hxx> + +#include "vbadocument.hxx" +#include "vbadocuments.hxx" + +#include <osl/file.hxx> +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static uno::Any +getDocument( uno::Reference< uno::XComponentContext > const & xContext, const uno::Reference< text::XTextDocument > &xDoc, const uno::Any& aApplication ) +{ + // FIXME: fine as long as SwVbaDocument is stateless ... + if( !xDoc.is() ) + return uno::Any(); + + SwVbaDocument *pWb = new SwVbaDocument( uno::Reference< XHelperInterface >( aApplication, uno::UNO_QUERY_THROW ), xContext, xDoc ); + return uno::Any( uno::Reference< word::XDocument > (pWb) ); +} + +namespace { + +class DocumentEnumImpl : public EnumerationHelperImpl +{ + uno::Any m_aApplication; +public: + /// @throws uno::RuntimeException + DocumentEnumImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, const uno::Any& aApplication ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), m_aApplication( aApplication ) {} + + virtual uno::Any SAL_CALL nextElement( ) override + { + uno::Reference< text::XTextDocument > xDoc( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + return getDocument( m_xContext, xDoc, m_aApplication ); + } +}; + +} + +SwVbaDocuments::SwVbaDocuments( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext ) : SwVbaDocuments_BASE( xParent, xContext, VbaDocumentsBase::WORD_DOCUMENT ) +{ +} +// XEnumerationAccess +uno::Type +SwVbaDocuments::getElementType() +{ + return cppu::UnoType<word::XDocument>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaDocuments::createEnumeration() +{ + // #FIXME its possible the DocumentEnumImpl here doesn't reflect + // the state of this object ( although it should ) would be + // safer to create an enumeration based on this objects state + // rather than one effectively based of the desktop component + uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return new DocumentEnumImpl( mxParent, mxContext, xEnumerationAccess->createEnumeration(), Application() ); +} + +uno::Any +SwVbaDocuments::createCollectionObject( const uno::Any& aSource ) +{ + uno::Reference< text::XTextDocument > xDoc( aSource, uno::UNO_QUERY_THROW ); + return getDocument( mxContext, xDoc, Application() ); +} + +uno::Any SAL_CALL +SwVbaDocuments::Add( const uno::Any& Template, const uno::Any& /*NewTemplate*/, const uno::Any& /*DocumentType*/, const uno::Any& /*Visible*/ ) +{ + OUString sFileName; + if( Template.hasValue() && ( Template >>= sFileName ) ) + { + return Open( sFileName, uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any()); + } + uno::Reference <text::XTextDocument> xTextDoc( createDocument() , uno::UNO_QUERY_THROW ); + return getDocument( mxContext, xTextDoc, Application() ); +} + +// #TODO# #FIXME# can any of the unused params below be used? +// #TODO# #FIXME# surely we should actually close the document here +void SAL_CALL +SwVbaDocuments::Close( const uno::Any& /*SaveChanges*/, const uno::Any& /*OriginalFormat*/, const uno::Any& /*RouteDocument*/ ) +{ +} + +// #TODO# #FIXME# can any of the unused params below be used? +uno::Any SAL_CALL +SwVbaDocuments::Open( const OUString& Filename, const uno::Any& /*ConfirmConversions*/, const uno::Any& ReadOnly, const uno::Any& /*AddToRecentFiles*/, const uno::Any& /*PasswordDocument*/, const uno::Any& /*PasswordTemplate*/, const uno::Any& /*Revert*/, const uno::Any& /*WritePasswordDocument*/, const uno::Any& /*WritePasswordTemplate*/, const uno::Any& /*Format*/, const uno::Any& /*Encoding*/, const uno::Any& /*Visible*/, const uno::Any& /*OpenAndRepair*/, const uno::Any& /*DocumentDirection*/, const uno::Any& /*NoEncodingDialog*/, const uno::Any& /*XMLTransform*/ ) +{ + SAL_INFO("sw.vba", "Documents.Open(Filename:=" << Filename << ",ReadOnly:=" << ReadOnly << ")"); + + // we need to detect if this is a URL, if not then assume it's a file path + OUString aURL; + INetURLObject aObj; + aObj.SetURL( Filename ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + if ( bIsURL ) + aURL = Filename; + else + osl::FileBase::getFileURLFromSystemPath( Filename, aURL ); + + uno::Sequence< beans::PropertyValue > sProps(0); + + uno::Reference <text::XTextDocument> xSpreadDoc( openDocument( Filename, ReadOnly, sProps ), uno::UNO_QUERY_THROW ); + uno::Any aRet = getDocument( mxContext, xSpreadDoc, Application() ); + uno::Reference< word::XDocument > xDocument( aRet, uno::UNO_QUERY ); + if ( xDocument.is() ) + xDocument->Activate(); + return aRet; +} + +uno::Any SAL_CALL +SwVbaDocuments::OpenNoRepairDialog( const OUString& Filename, const uno::Any& ConfirmConversions, const uno::Any& ReadOnly, const uno::Any& AddToRecentFiles, const uno::Any& PasswordDocument, const uno::Any& PasswordTemplate, const uno::Any& Revert, const uno::Any& WritePasswordDocument, const uno::Any& WritePasswordTemplate, const uno::Any& Format, const uno::Any& Encoding, const uno::Any& Visible, const uno::Any& OpenAndRepair, const uno::Any& DocumentDirection, const uno::Any& NoEncodingDialog, const uno::Any& XMLTransform ) +{ + return Open( Filename, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format, Encoding, Visible, OpenAndRepair, DocumentDirection, NoEncodingDialog, XMLTransform ); +} + +uno::Any SAL_CALL +SwVbaDocuments::OpenOld( const OUString& FileName, const uno::Any& ConfirmConversions, const uno::Any& ReadOnly, const uno::Any& AddToRecentFiles, const uno::Any& PasswordDocument, const uno::Any& PasswordTemplate, const uno::Any& Revert, const uno::Any& WritePasswordDocument, const uno::Any& WritePasswordTemplate, const uno::Any& Format ) +{ + return Open( FileName, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format, uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any(), uno::Any() ); +} + +OUString +SwVbaDocuments::getServiceImplName() +{ + return "SwVbaDocuments"; +} + +uno::Sequence<OUString> +SwVbaDocuments::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Documents" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbadocuments.hxx b/sw/source/ui/vba/vbadocuments.hxx new file mode 100644 index 000000000..2b6261adb --- /dev/null +++ b/sw/source/ui/vba/vbadocuments.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_UI_VBA_VBADOCUMENTS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBADOCUMENTS_HXX + +#include <ooo/vba/word/XDocuments.hpp> +#include <vbahelper/vbadocumentsbase.hxx> +#include <cppuhelper/implbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaDocumentsBase, ov::word::XDocuments > SwVbaDocuments_BASE; + +class SwVbaDocuments : public SwVbaDocuments_BASE +{ +public: + SwVbaDocuments( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaDocuments_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + + // Methods + virtual css::uno::Any SAL_CALL Add( const css::uno::Any& Template, const css::uno::Any& NewTemplate, const css::uno::Any& DocumentType, const css::uno::Any& Visible ) override; + virtual css::uno::Any SAL_CALL Open( const OUString& Filename, const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, const css::uno::Any& AddToRecentFiles, const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Format, const css::uno::Any& Encoding, const css::uno::Any& Visible, const css::uno::Any& OpenAndRepair, const css::uno::Any& DocumentDirection, const css::uno::Any& NoEncodingDialog, const css::uno::Any& XMLTransform ) override; + virtual css::uno::Any SAL_CALL OpenNoRepairDialog( const OUString& Filename, const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, const css::uno::Any& AddToRecentFiles, const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Format, const css::uno::Any& Encoding, const css::uno::Any& Visible, const css::uno::Any& OpenAndRepair, const css::uno::Any& DocumentDirection, const css::uno::Any& NoEncodingDialog, const css::uno::Any& XMLTransform ) override; + virtual css::uno::Any SAL_CALL OpenOld( const OUString& FileName, const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, const css::uno::Any& AddToRecentFiles, const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Format ) override; + virtual void SAL_CALL Close( const css::uno::Any& SaveChanges, const css::uno::Any& OriginalFormat, const css::uno::Any& RouteDocument ) override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBADOCUMENTS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaeventshelper.cxx b/sw/source/ui/vba/vbaeventshelper.cxx new file mode 100644 index 000000000..5b53c6fc9 --- /dev/null +++ b/sw/source/ui/vba/vbaeventshelper.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 <sal/config.h> + +#include "service.hxx" +#include "vbaeventshelper.hxx" +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/vba/VBAEventId.hpp> +#include <comphelper/servicedecl.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::script::vba::VBAEventId; + +SwVbaEventsHelper::SwVbaEventsHelper( uno::Sequence< css::uno::Any > const& aArgs, uno::Reference< uno::XComponentContext > const& /*xContext*/ ) : + VbaEventsHelperBase( aArgs ) +{ + using namespace ::com::sun::star::script::ModuleType; + registerEventHandler( DOCUMENT_NEW, DOCUMENT, "Document_New" ); + registerEventHandler( AUTO_NEW, NORMAL, "AutoNew" ); + registerEventHandler( DOCUMENT_OPEN, DOCUMENT, "Document_Open" ); + registerEventHandler( AUTO_OPEN, NORMAL, "AutoOpen" ); + registerEventHandler( DOCUMENT_CLOSE, DOCUMENT, "Document_Close" ); + registerEventHandler( AUTO_CLOSE, NORMAL, "AutoClose" ); +} + +SwVbaEventsHelper::~SwVbaEventsHelper() +{ +} + +bool SwVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue, + const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& /*rArgs*/ ) +{ + switch( rInfo.mnEventId ) + { + case DOCUMENT_NEW: + rEventQueue.emplace_back(AUTO_NEW ); + break; + case DOCUMENT_OPEN: + rEventQueue.emplace_back(AUTO_OPEN ); + break; + case DOCUMENT_CLOSE: + rEventQueue.emplace_back(AUTO_CLOSE ); + break; + } + return true; +} + +uno::Sequence< uno::Any > SwVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& /*rInfo*/, + const uno::Sequence< uno::Any >& /*rArgs*/ ) +{ + // no event handler expects any arguments + return uno::Sequence< uno::Any >(); +} + +void SwVbaEventsHelper::implPostProcessEvent( EventQueue& /*rEventQueue*/, + const EventHandlerInfo& /*rInfo*/, bool /*bCancel*/ ) +{ + // nothing to do after any event +} + +OUString SwVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& /*rInfo*/, + const uno::Sequence< uno::Any >& /*rArgs*/ ) const +{ + // TODO: get actual codename from document + return "ThisDocument"; +} + +namespace vbaeventshelper +{ +namespace sdecl = comphelper::service_decl; +sdecl::inheritingClass_<SwVbaEventsHelper, sdecl::with_args<true> > const serviceImpl; +sdecl::ServiceDecl const serviceDecl( + serviceImpl, + "SwVbaEventsHelper", + "com.sun.star.document.vba.VBATextEventProcessor" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaeventshelper.hxx b/sw/source/ui/vba/vbaeventshelper.hxx new file mode 100644 index 000000000..03a2f8a45 --- /dev/null +++ b/sw/source/ui/vba/vbaeventshelper.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_UI_VBA_VBAEVENTSHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAEVENTSHELPER_HXX + +#include <vbahelper/vbaeventshelperbase.hxx> + +class SwVbaEventsHelper : public VbaEventsHelperBase +{ +public: + SwVbaEventsHelper( + const css::uno::Sequence< css::uno::Any >& rArgs, + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~SwVbaEventsHelper() override; + +protected: + virtual bool implPrepareEvent( EventQueue& rEventQueue, const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) override; + virtual css::uno::Sequence< css::uno::Any > implBuildArgumentList( const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) override; + virtual void implPostProcessEvent( EventQueue& rEventQueue, const EventHandlerInfo& rInfo, bool bCancel ) override; + virtual OUString implGetDocumentModuleName( const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) const override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafield.cxx b/sw/source/ui/vba/vbafield.cxx new file mode 100644 index 000000000..6e3aef2f3 --- /dev/null +++ b/sw/source/ui/vba/vbafield.cxx @@ -0,0 +1,536 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbafield.hxx" +#include "vbarange.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XTextFieldsSupplier.hpp> +#include <com/sun/star/text/FilenameDisplayFormat.hpp> +#include <com/sun/star/util/XRefreshable.hpp> +#include <com/sun/star/util/XUpdatable.hpp> +#include <ooo/vba/word/WdFieldType.hpp> +#include <basic/sberrors.hxx> +#include <cppuhelper/implbase.hxx> +#include <sal/log.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaField::SwVbaField( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< css::text::XTextField >& xTextField) : SwVbaField_BASE( rParent, rContext ) +{ + mxTextField.set( xTextField, uno::UNO_SET_THROW ); +} + +sal_Bool SAL_CALL SwVbaField::Update() +{ + uno::Reference< util::XUpdatable > xUpdatable( mxTextField, uno::UNO_QUERY ); + if( xUpdatable.is() ) + { + xUpdatable->update(); + return true; + } + return false; +} + +// XHelperInterface +OUString +SwVbaField::getServiceImplName() +{ + return "SwVbaField"; +} + +uno::Sequence<OUString> +SwVbaField::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Field" + }; + return aServiceNames; +} + +namespace { + +// FIXME? copy and paste code +// the codes are copied from ww8par5.cxx +class SwVbaReadFieldParams +{ +private: + OUString aData; + sal_Int32 nLen, nFnd, nNext, nSavPtr; + OUString aFieldName; +public: + explicit SwVbaReadFieldParams( const OUString& rData ); + + long SkipToNextToken(); + + sal_Int32 FindNextStringPiece( sal_Int32 _nStart ); + + OUString GetResult() const; + const OUString& GetFieldName()const { return aFieldName; } +}; + +} + +SwVbaReadFieldParams::SwVbaReadFieldParams( const OUString& _rData ) + : aData( _rData ), nLen( _rData.getLength() ), nNext( 0 ) +{ + // First search for an opening parenthesis or a space or a quotation mark + // or a backslash, so that the field command + // (thus INCLUDEPICTURE or ...) is ignored. + while( (nLen > nNext) && (aData[ nNext ] == ' ') ) + ++nNext; + + sal_Unicode c; + while( nLen > nNext + && (c = aData[ nNext ]) != ' ' + && c != '"' + && c != '\\' + && c != 132 + && c != 0x201c ) + ++nNext; + + nFnd = nNext; + nSavPtr = nNext; + aFieldName = aData.copy( 0, nFnd ); +} + +OUString SwVbaReadFieldParams::GetResult() const +{ + return (-1 == nFnd) + ? OUString() + : aData.copy( nFnd, (nSavPtr - nFnd) ); +} + +// ret: -2: NOT a '\' parameter but normal Text +long SwVbaReadFieldParams::SkipToNextToken() +{ + long nRet = -1; // end + if ( + (-1 != nNext) && (nLen > nNext) && + -1 != (nFnd = FindNextStringPiece(nNext)) + ) + { + nSavPtr = nNext; + + if ('\\' == aData[nFnd] && '\\' != aData[nFnd + 1]) + { + nRet = aData[++nFnd]; + nNext = ++nFnd; // and set behind + } + else + { + nRet = -2; + if ( + (-1 != nSavPtr ) && + ( + ('"' == aData[nSavPtr - 1]) || + (0x201d == aData[nSavPtr - 1]) + ) + ) + { + --nSavPtr; + } + } + } + return nRet; +} + +// FindNextPara is searching for the next Backslash-Parameter or the next string +// until blank or the next "\" or until the closing quotation mark +// or until the string end of pStr. + +// Output ppNext (if ppNext != 0) beginning of the search for the next parameter or 0 + +// Return value: 0 if String-End reached, otherwise begin of the parameter or the string + +sal_Int32 SwVbaReadFieldParams::FindNextStringPiece(const sal_Int32 nStart) +{ + sal_Int32 n = ( -1 == nStart ) ? nFnd : nStart; // Start + sal_Int32 n2; // End + + nNext = -1; // Default for not found + + while( (nLen > n) && (aData[ n ] == ' ') ) + ++n; + + if( nLen == n ) + return -1; // String End reached! + + if( (aData[ n ] == '"') // quotation marks are in front of parenthesis? + || (aData[ n ] == 0x201c) + || (aData[ n ] == 132) ) + { + n++; // ignore quotation marks + n2 = n; // From here search for the end + while( (nLen > n2) + && (aData[ n2 ] != '"') + && (aData[ n2 ] != 0x201d) + && (aData[ n2 ] != 147) ) + n2++; // Search for the end of the parenthesis + } + else // no quotation marks + { + n2 = n; // from here search for the end + while( (nLen > n2) && (aData[ n2 ] != ' ') ) // Search for the end of the parenthesis + { + if( aData[ n2 ] == '\\' ) + { + if( aData[ n2+1 ] == '\\' ) + n2 += 2; // double-backslash -> OK + else + { + if( n2 > n ) + n2--; + break; // single-backslash -> End + } + } + else + n2++; // no backslash -> OK + } + } + if( nLen > n2 ) + { + if(aData[ n2 ] != ' ') n2++; + nNext = n2; + } + return n; +} + +// SwVbaFields + +static uno::Any lcl_createField( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel, const uno::Any& aSource ) +{ + uno::Reference< text::XTextField > xTextField( aSource, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextDocument > xTextDocument( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< word::XField > xField( new SwVbaField( xParent, xContext, xTextField ) ); + return uno::makeAny( xField ); +} + +namespace { + +class FieldEnumeration : public ::cppu::WeakImplHelper< css::container::XEnumeration > +{ + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + uno::Reference< container::XEnumeration > mxEnumeration; +public: + FieldEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< container::XEnumeration >& xEnumeration ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ), mxEnumeration( xEnumeration ) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return mxEnumeration->hasMoreElements(); + } + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( !hasMoreElements() ) + throw container::NoSuchElementException(); + return lcl_createField( mxParent, mxContext, mxModel, mxEnumeration->nextElement() ); + } +}; + +class FieldCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + uno::Reference< container::XEnumerationAccess > mxEnumerationAccess; +public: + /// @throws css::uno::RuntimeException + FieldCollectionHelper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ) + { + uno::Reference< text::XTextFieldsSupplier > xSupp( xModel, uno::UNO_QUERY_THROW ); + mxEnumerationAccess.set( xSupp->getTextFields(), uno::UNO_SET_THROW ); + } + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return mxEnumerationAccess->getElementType(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return mxEnumerationAccess->hasElements(); } + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration(); + sal_Int32 nCount = 0; + while( xEnumeration->hasMoreElements() ) + { + ++nCount; + xEnumeration->nextElement(); + } + return nCount; + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + if( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration(); + sal_Int32 nCount = 0; + while( xEnumeration->hasMoreElements() ) + { + if( nCount == Index ) + { + return xEnumeration->nextElement(); + } + ++nCount; + } + throw lang::IndexOutOfBoundsException(); + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + uno::Reference< container::XEnumeration > xEnumeration = mxEnumerationAccess->createEnumeration(); + return uno::Reference< container::XEnumeration >( new FieldEnumeration( mxParent, mxContext, mxModel, xEnumeration ) ); + } +}; + +} + +SwVbaFields::SwVbaFields( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel ) : SwVbaFields_BASE( xParent, xContext , uno::Reference< container::XIndexAccess >( new FieldCollectionHelper( xParent, xContext, xModel ) ) ), mxModel( xModel ) +{ + mxMSF.set( mxModel, uno::UNO_QUERY_THROW ); +} + +uno::Reference< word::XField > SAL_CALL +SwVbaFields::Add( const css::uno::Reference< ::ooo::vba::word::XRange >& Range, const css::uno::Any& Type, const css::uno::Any& Text, const css::uno::Any& /*PreserveFormatting*/ ) +{ + sal_Int32 nType = word::WdFieldType::wdFieldEmpty; + Type >>= nType; + OUString sText; + Text >>= sText; + + OUString sFieldName; + if( ( nType == word::WdFieldType::wdFieldEmpty ) && !sText.isEmpty() ) + { + SwVbaReadFieldParams aReadParam(sText); + sFieldName = aReadParam.GetFieldName(); + SAL_INFO("sw.vba", "the field name is " << sFieldName ); + } + + uno::Reference< text::XTextContent > xTextField; + if( nType == word::WdFieldType::wdFieldFileName || sFieldName.equalsIgnoreAsciiCase("FILENAME") ) + { + xTextField.set( Create_Field_FileName( sText ), uno::UNO_QUERY_THROW ); + } + else if( nType == word::WdFieldType::wdFieldDocProperty || sFieldName.equalsIgnoreAsciiCase("DOCPROPERTY") ) + { + xTextField.set( Create_Field_DocProperty( sText ), uno::UNO_QUERY_THROW ); + } + else + { + throw uno::RuntimeException("Not implemented" ); + } + + SwVbaRange& rVbaRange = dynamic_cast<SwVbaRange&>(*Range); + uno::Reference< text::XTextRange > xTextRange = rVbaRange.getXTextRange(); + uno::Reference< text::XText > xText = xTextRange->getText(); + xText->insertTextContent( xTextRange, xTextField, true ); + return uno::Reference< word::XField >( new SwVbaField( mxParent, mxContext, uno::Reference< text::XTextField >( xTextField, uno::UNO_QUERY_THROW ) ) ); +} + +uno::Reference< text::XTextField > SwVbaFields::Create_Field_FileName( const OUString& _text ) +{ + uno::Reference< text::XTextField > xTextField( mxMSF->createInstance("com.sun.star.text.TextField.FileName"), uno::UNO_QUERY_THROW ); + sal_Int16 nFileFormat = text::FilenameDisplayFormat::NAME_AND_EXT; + if( !_text.isEmpty() ) + { + long nRet; + SwVbaReadFieldParams aReadParam( _text ); + while (-1 != (nRet = aReadParam.SkipToNextToken())) + { + switch (nRet) + { + case 'p': + nFileFormat = text::FilenameDisplayFormat::FULL; + break; + case '*': + //Skip over MERGEFORMAT + aReadParam.SkipToNextToken(); + break; + default: + DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString()); + break; + } + } + } + + uno::Reference< beans::XPropertySet > xProps( xTextField, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue("FileFormat", uno::makeAny( nFileFormat ) ); + + return xTextField; +} + +namespace { + +struct DocPropertyTable +{ + const char* sDocPropertyName; + const char* sFieldService; +}; + +} + +static const DocPropertyTable aDocPropertyTables[] = +{ + { "Author", "com.sun.star.text.textfield.docinfo.CreateAuthor" }, + { "Bytes", nullptr }, + { "Category", nullptr }, + { "Characters",nullptr }, + { "CharactersWithSpaces", nullptr }, + { "Comments", "com.sun.star.text.textfield.docinfo.Description" }, + { "Company", nullptr }, + { "CreateTime", "com.sun.star.text.textfield.docinfo.CreateDateTime" }, + { "HyperlinkBase", nullptr }, + { "Keywords", "com.sun.star.text.textfield.docinfo.Keywords" }, + { "LastPrinted", "com.sun.star.text.textfield.docinfo.PrintDateTime" }, + { "LastSavedBy", "com.sun.star.text.textfield.docinfo.ChangeAuthor" }, + { "LastSavedTime", "com.sun.star.text.textfield.docinfo.ChangeDateTime" }, + { "Lines", nullptr }, + { "Manager", nullptr }, + { "NameofApplication", nullptr }, + { "ODMADocID", nullptr }, + { "Pages", "com.sun.star.text.textfield.PageCount" }, + { "Paragraphs", "com.sun.star.text.textfield.ParagraphCount" }, + { "RevisionNumber", "com.sun.star.text.textfield.docinfo.Revision" }, + { "Security", nullptr }, + { "Subject", "com.sun.star.text.textfield.docinfo.Subject" }, + { "Template", "com.sun.star.text.textfield.TemplateName" }, + { "Title", "com.sun.star.text.textfield.docinfo.Title" }, + { "TotalEditingTime", "com.sun.star.text.textfield.docinfo.EditTime" }, + { "Words", "com.sun.star.text.textfield.WordCount" }, + { nullptr, nullptr } +}; + +uno::Reference< text::XTextField > SwVbaFields::Create_Field_DocProperty( const OUString& _text ) +{ + OUString aDocProperty; + SwVbaReadFieldParams aReadParam( _text ); + long nRet; + while( -1 != ( nRet = aReadParam.SkipToNextToken() )) + { + switch( nRet ) + { + case -2: + if( aDocProperty.isEmpty() ) + aDocProperty = aReadParam.GetResult(); + break; + case '*': + //Skip over MERGEFORMAT + aReadParam.SkipToNextToken(); + break; + } + } + aDocProperty = aDocProperty.replaceAll("\"", ""); + SAL_INFO("sw.vba", "SwVbaFields::Create_Field_DocProperty, the document property name is " << aDocProperty ); + if( aDocProperty.isEmpty() ) + { + throw uno::RuntimeException(); + } + + bool bCustom = true; + OUString sFieldService; + // find the build in document properties + for( const DocPropertyTable* pTable = aDocPropertyTables; pTable->sDocPropertyName != nullptr; pTable++ ) + { + if( aDocProperty.equalsIgnoreAsciiCaseAscii( pTable->sDocPropertyName ) ) + { + if( pTable->sFieldService != nullptr ) + sFieldService = OUString::createFromAscii(pTable->sFieldService); + bCustom = false; + break; + } + } + + if( bCustom ) + { + sFieldService = "com.sun.star.text.textfield.docinfo.Custom"; + } + else if( sFieldService.isEmpty() ) + { + throw uno::RuntimeException("Not implemented" ); + } + + uno::Reference< text::XTextField > xTextField( mxMSF->createInstance( sFieldService ), uno::UNO_QUERY_THROW ); + + if( bCustom ) + { + uno::Reference< beans::XPropertySet > xProps( xTextField, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue("Name", uno::makeAny( aDocProperty ) ); + } + + return xTextField; +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwVbaFields::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumerationAccess->createEnumeration(); +} + +// ScVbaCollectionBaseImpl +uno::Any +SwVbaFields::createCollectionObject( const uno::Any& aSource ) +{ + return lcl_createField( mxParent, mxContext, mxModel, aSource ); +} + +sal_Int32 SAL_CALL SwVbaFields::Update() +{ + sal_Int32 nUpdate = 1; + try + { + uno::Reference< text::XTextFieldsSupplier > xSupp( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< util::XRefreshable > xRef( xSupp->getTextFields(), uno::UNO_QUERY_THROW ); + xRef->refresh(); + nUpdate = 0; + } + catch(const uno::Exception&) + { + nUpdate = 1; + } + return nUpdate; +} + +// XHelperInterface +OUString +SwVbaFields::getServiceImplName() +{ + return "SwVbaFields"; +} + +// XEnumerationAccess +uno::Type SAL_CALL +SwVbaFields::getElementType() +{ + return cppu::UnoType<word::XField>::get(); +} + +uno::Sequence<OUString> +SwVbaFields::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Fields" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafield.hxx b/sw/source/ui/vba/vbafield.hxx new file mode 100644 index 000000000..292be2edb --- /dev/null +++ b/sw/source/ui/vba/vbafield.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_UI_VBA_VBAFIELD_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFIELD_HXX +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <ooo/vba/word/XField.hpp> +#include <ooo/vba/word/XFields.hpp> +#include <vbahelper/vbacollectionimpl.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XField > SwVbaField_BASE; + +class SwVbaField : public SwVbaField_BASE +{ + css::uno::Reference< css::text::XTextField > mxTextField; +public: + /// @throws css::uno::RuntimeException + SwVbaField( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextField >& xTextField); + + virtual sal_Bool SAL_CALL Update() override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +typedef CollTestImplHelper< ov::word::XFields > SwVbaFields_BASE; + +class SwVbaFields : public SwVbaFields_BASE +{ + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::lang::XMultiServiceFactory > mxMSF; +private: + /// @throws css::uno::RuntimeException + /// @throws css::script::BasicErrorException + css::uno::Reference< css::text::XTextField > Create_Field_FileName(const OUString& rText); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextField > Create_Field_DocProperty( const OUString& _text ); + +public: + SwVbaFields( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel ); + // XFields + virtual css::uno::Reference< ::ooo::vba::word::XField > SAL_CALL Add( const css::uno::Reference< ::ooo::vba::word::XRange >& Range, const css::uno::Any& Type, const css::uno::Any& Text, const css::uno::Any& PreserveFormatting ) override; + virtual sal_Int32 SAL_CALL Update() override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + // ScVbaCollectionBaseImpl + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafilterpropsfromformat.hxx b/sw/source/ui/vba/vbafilterpropsfromformat.hxx new file mode 100644 index 000000000..01fd93be5 --- /dev/null +++ b/sw/source/ui/vba/vbafilterpropsfromformat.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_UI_VBA_VBAFILTERPROPSFROMFORMAT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFILTERPROPSFROMFORMAT_HXX + +#include <sal/config.h> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <rtl/ustring.hxx> +#include <ooo/vba/word/WdSaveFormat.hpp> + +namespace +{ +inline bool setFilterPropsFromFormat(sal_Int32 nFormat, + css::uno::Sequence<css::beans::PropertyValue>& rProps) +{ + auto pProp + = std::find_if(rProps.begin(), rProps.end(), [](const css::beans::PropertyValue& rProp) { + return rProp.Name == "FilterName"; + }); + if (pProp != rProps.end()) + { + switch (nFormat) + { + case ooo::vba::word::WdSaveFormat::wdFormatDocument: + pProp->Value <<= OUString("MS Word 97"); + break; + // Just save all the text formats as "Text" + case ooo::vba::word::WdSaveFormat::wdFormatDOSText: + case ooo::vba::word::WdSaveFormat::wdFormatDOSTextLineBreaks: + case ooo::vba::word::WdSaveFormat::wdFormatEncodedText: + case ooo::vba::word::WdSaveFormat::wdFormatText: + case ooo::vba::word::WdSaveFormat::wdFormatTextLineBreaks: + pProp->Value <<= OUString("Text"); + break; + case ooo::vba::word::WdSaveFormat::wdFormatFilteredHTML: + case ooo::vba::word::WdSaveFormat::wdFormatHTML: + pProp->Value <<= OUString("HTML"); + break; + case ooo::vba::word::WdSaveFormat::wdFormatRTF: + pProp->Value <<= OUString("Rich Text Format"); + break; + case ooo::vba::word::WdSaveFormat::wdFormatTemplate: + pProp->Value <<= OUString("MS Word 97 Vorlage"); + break; + + // Default to "MS Word 97" + default: + pProp->Value <<= OUString("MS Word 97"); + break; + } + return true; + } + return false; +} +} + +#endif diff --git a/sw/source/ui/vba/vbafind.cxx b/sw/source/ui/vba/vbafind.cxx new file mode 100644 index 000000000..bb8f457fd --- /dev/null +++ b/sw/source/ui/vba/vbafind.cxx @@ -0,0 +1,407 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbafind.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbareplacement.hxx" +#include <ooo/vba/word/WdFindWrap.hpp> +#include <ooo/vba/word/WdReplace.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> +#include "wordvbahelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaFind::SwVbaFind( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ) : + SwVbaFind_BASE( rParent, rContext ), mxModel( xModel ), mxTextRange( xTextRange ), mbReplace( false ), mnReplaceType( word::WdReplace::wdReplaceOne ), mnWrap( word::WdFindWrap::wdFindStop ) +{ + mxReplaceable.set( mxModel, uno::UNO_QUERY_THROW ); + mxPropertyReplace.set( mxReplaceable->createReplaceDescriptor(), uno::UNO_QUERY_THROW ); + mxTVC = word::getXTextViewCursor( mxModel ); + mxSelSupp.set( mxModel->getCurrentController(), uno::UNO_QUERY_THROW ); +} + +SwVbaFind::~SwVbaFind() +{ +} + +bool SwVbaFind::InRange( const uno::Reference< text::XTextRange >& xCurrentRange ) +{ + uno::Reference< text::XTextRangeCompare > xTRC( mxTextRange->getText(), uno::UNO_QUERY_THROW ); + return xTRC->compareRegionStarts( mxTextRange, xCurrentRange ) >= 0 && xTRC->compareRegionEnds( mxTextRange, xCurrentRange ) <= 0; +} + +bool SwVbaFind::InEqualRange( const uno::Reference< text::XTextRange >& xCurrentRange ) +{ + uno::Reference< text::XTextRangeCompare > xTRC( mxTextRange->getText(), uno::UNO_QUERY_THROW ); + return xTRC->compareRegionStarts( mxTextRange, xCurrentRange ) == 0 && xTRC->compareRegionEnds( mxTextRange, xCurrentRange ) == 0; +} + +void SwVbaFind::SetReplaceWith( const OUString& rText ) +{ + mxPropertyReplace->setReplaceString( rText ); + mbReplace = true; +} + +OUString SwVbaFind::GetReplaceWith() +{ + return mxPropertyReplace->getReplaceString(); +} +void SwVbaFind::SetReplace( sal_Int32 type ) +{ + mnReplaceType = type; + mbReplace = true; +} +uno::Reference< text::XTextRange > SwVbaFind::FindOneElement() +{ + uno::Reference< text::XTextRange > xFoundOne; + if( !mxTVC->getString().isEmpty() ) + { + if( getForward() ) + { + xFoundOne.set( mxReplaceable->findNext( mxTextRange->getStart(), uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + } + else + { + xFoundOne.set( mxReplaceable->findNext( mxTextRange->getEnd(), uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + } + + if( xFoundOne.is() && InEqualRange( xFoundOne ) ) + { + xFoundOne.set( mxReplaceable->findNext( xFoundOne, uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + } + else if( xFoundOne.is() && !InRange( xFoundOne ) ) + { + xFoundOne.clear(); + } + } + else + { + xFoundOne.set( mxReplaceable->findNext( mxTextRange, uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + } + + if( !xFoundOne.is() && ( getWrap() == word::WdFindWrap::wdFindContinue || getWrap() == word::WdFindWrap::wdFindAsk ) ) + { + if( getForward() ) + { + mxTVC->gotoStart(false); + xFoundOne.set( mxReplaceable->findNext( mxTextRange->getStart(), uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + } + else + { + mxTVC->gotoEnd( false ); + xFoundOne.set( mxReplaceable->findNext( mxTextRange->getEnd(), uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ), uno::UNO_QUERY ); + + } + } + return xFoundOne; +} + +bool SwVbaFind::SearchReplace() +{ + bool result = false; + + // TODO: map wildcards in area to OOo wildcards + + if( mbReplace ) + { + switch( mnReplaceType ) + { + case word::WdReplace::wdReplaceNone: + { + result = true; + break; + } + case word::WdReplace::wdReplaceOne: + { + uno::Reference< text::XTextRange > xFindOne = FindOneElement(); + if( xFindOne.is() ) + { + xFindOne->setString( GetReplaceWith() ); + result = mxSelSupp->select( uno::makeAny( xFindOne ) ); + } + break; + } + case word::WdReplace::wdReplaceAll: + { + uno::Reference< container::XIndexAccess > xIndexAccess = mxReplaceable->findAll( uno::Reference< util::XSearchDescriptor >( mxPropertyReplace, uno::UNO_QUERY_THROW ) ); + if( xIndexAccess->getCount() > 0 ) + { + for( sal_Int32 i = 0; i < xIndexAccess->getCount(); i++ ) + { + uno::Reference< text::XTextRange > xTextRange( xIndexAccess->getByIndex( i ), uno::UNO_QUERY_THROW ); + if( mnWrap == word::WdFindWrap::wdFindContinue || mnWrap == word::WdFindWrap::wdFindAsk || InRange( xTextRange ) ) + { + xTextRange->setString( GetReplaceWith() ); + result = true; + } + } + } + break; + } + default: + { + result = false; + } + } + } + else + { + uno::Reference< text::XTextRange > xFindOne = FindOneElement(); + if( xFindOne.is() ) + result = mxSelSupp->select( uno::makeAny( xFindOne ) ); + } + + return result; +} + +OUString SAL_CALL SwVbaFind::getText() +{ + return mxPropertyReplace->getSearchString(); +} + +void SAL_CALL SwVbaFind::setText( const OUString& _text ) +{ + mxPropertyReplace->setSearchString( _text ); +} + +uno::Any SAL_CALL SwVbaFind::getReplacement() +{ + return uno::makeAny( uno::Reference< word::XReplacement >( new SwVbaReplacement( this, mxContext, mxPropertyReplace ) ) ); +} + +void SAL_CALL SwVbaFind::setReplacement( const uno::Any& /*_replacement */ ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +sal_Bool SAL_CALL SwVbaFind::getForward() +{ + bool bBackward = false; + mxPropertyReplace->getPropertyValue("SearchBackwards") >>= bBackward; + return !bBackward; +} + +void SAL_CALL SwVbaFind::setForward( sal_Bool _forward ) +{ + bool bBackward = !_forward; + mxPropertyReplace->setPropertyValue("SearchBackwards", uno::makeAny( bBackward ) ); +} + +::sal_Int32 SAL_CALL SwVbaFind::getWrap() +{ + // seems not supported in Writer + return mnWrap; +} + +void SAL_CALL SwVbaFind::setWrap( ::sal_Int32 _wrap ) +{ + // seems not supported in Writer + mnWrap = _wrap; +} + +sal_Bool SAL_CALL SwVbaFind::getFormat() +{ + return mxPropertyReplace->getValueSearch(); +} + +void SAL_CALL SwVbaFind::setFormat( sal_Bool _format ) +{ + mxPropertyReplace->setValueSearch( _format ); +} + +sal_Bool SAL_CALL SwVbaFind::getMatchCase() +{ + bool value = false; + mxPropertyReplace->getPropertyValue("SearchCaseSensitive") >>= value; + return value; +} + +void SAL_CALL SwVbaFind::setMatchCase( sal_Bool _matchcase ) +{ + mxPropertyReplace->setPropertyValue("SearchCaseSensitive", uno::makeAny( _matchcase ) ); +} + +sal_Bool SAL_CALL SwVbaFind::getMatchWholeWord() +{ + bool value = false; + mxPropertyReplace->getPropertyValue("SearchWords") >>= value; + return value; +} + +void SAL_CALL SwVbaFind::setMatchWholeWord( sal_Bool _matchwholeword ) +{ + mxPropertyReplace->setPropertyValue("SearchWords", uno::makeAny( _matchwholeword ) ); +} + +sal_Bool SAL_CALL SwVbaFind::getMatchWildcards() +{ + bool value = false; + mxPropertyReplace->getPropertyValue("SearchRegularExpression") >>= value; + return value; +} + +void SAL_CALL SwVbaFind::setMatchWildcards( sal_Bool _matchwildcards ) +{ + mxPropertyReplace->setPropertyValue("SearchRegularExpression", uno::makeAny( _matchwildcards ) ); +} + +sal_Bool SAL_CALL SwVbaFind::getMatchSoundsLike() +{ + bool value = false; + mxPropertyReplace->getPropertyValue("SearchSimilarity") >>= value; + return value; +} + +void SAL_CALL SwVbaFind::setMatchSoundsLike( sal_Bool _matchsoundslike ) +{ + // seems not accurate + mxPropertyReplace->setPropertyValue("SearchSimilarity", uno::makeAny( _matchsoundslike ) ); +} + +sal_Bool SAL_CALL SwVbaFind::getMatchAllWordForms() +{ + bool value = false; + mxPropertyReplace->getPropertyValue("SearchSimilarity") >>= value; + if( value ) + mxPropertyReplace->getPropertyValue("SearchSimilarityRelax") >>= value; + return value; +} + +void SAL_CALL SwVbaFind::setMatchAllWordForms( sal_Bool _matchallwordforms ) +{ + // seems not accurate + mxPropertyReplace->setPropertyValue("SearchSimilarity", uno::makeAny( _matchallwordforms ) ); + mxPropertyReplace->setPropertyValue("SearchSimilarityRelax", uno::makeAny( _matchallwordforms ) ); +} + +uno::Any SAL_CALL SwVbaFind::getStyle() +{ + throw uno::RuntimeException("Not implemented" ); +} + +void SAL_CALL SwVbaFind::setStyle( const uno::Any& /*_style */ ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +sal_Bool SAL_CALL +SwVbaFind::Execute( const uno::Any& FindText, const uno::Any& MatchCase, const uno::Any& MatchWholeWord, const uno::Any& MatchWildcards, const uno::Any& MatchSoundsLike, const uno::Any& MatchAllWordForms, const uno::Any& Forward, const uno::Any& Wrap, const uno::Any& Format, const uno::Any& ReplaceWith, const uno::Any& Replace, const uno::Any& /*MatchKashida*/, const uno::Any& /*MatchDiacritics*/, const uno::Any& /*MatchAlefHamza*/, const uno::Any& /*MatchControl*/, const uno::Any& /*MatchPrefix*/, const uno::Any& /*MatchSuffix*/, const uno::Any& /*MatchPhrase*/, const uno::Any& /*IgnoreSpace*/, const uno::Any& /*IgnorePunct*/ ) +{ + bool result = false; + if( FindText.hasValue() ) + { + OUString sText; + FindText >>= sText; + setText( sText ); + } + + bool bValue = false; + if( MatchCase.hasValue() ) + { + MatchCase >>= bValue; + setMatchCase( bValue ); + } + + if( MatchWholeWord.hasValue() ) + { + MatchWholeWord >>= bValue; + setMatchWholeWord( bValue ); + } + + if( MatchWildcards.hasValue() ) + { + MatchWildcards >>= bValue; + setMatchWildcards( bValue ); + } + + if( MatchSoundsLike.hasValue() ) + { + MatchSoundsLike >>= bValue; + setMatchSoundsLike( bValue ); + } + + if( MatchAllWordForms.hasValue() ) + { + MatchAllWordForms >>= bValue; + setMatchAllWordForms( bValue ); + } + + if( Forward.hasValue() ) + { + Forward >>= bValue; + setForward( bValue ); + } + + if( Wrap.hasValue() ) + { + sal_Int32 nWrapType = 0; + Wrap >>= nWrapType; + setWrap( nWrapType ); + } + + if( Format.hasValue() ) + { + Format >>= bValue; + setFormat( bValue ); + } + + if( ReplaceWith.hasValue() ) + { + OUString sValue; + ReplaceWith >>= sValue; + SetReplaceWith( sValue ); + } + + if( Replace.hasValue() ) + { + sal_Int32 nValue(0); + Replace >>= nValue; + SetReplace( nValue ); + } + + result = SearchReplace(); + + return result; +} + +void SAL_CALL +SwVbaFind::ClearFormatting( ) +{ + uno::Sequence< beans::PropertyValue > aSearchAttribs; + mxPropertyReplace->setSearchAttributes( aSearchAttribs ); +} + +OUString +SwVbaFind::getServiceImplName() +{ + return "SwVbaFind"; +} + +uno::Sequence< OUString > +SwVbaFind::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Find" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafind.hxx b/sw/source/ui/vba/vbafind.hxx new file mode 100644 index 000000000..cdbcc9553 --- /dev/null +++ b/sw/source/ui/vba/vbafind.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAFIND_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFIND_HXX + +#include <ooo/vba/word/XFind.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/util/XReplaceable.hpp> +#include <com/sun/star/util/XPropertyReplace.hpp> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XFind > SwVbaFind_BASE; + +class SwVbaFind : public SwVbaFind_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextRange > mxTextRange; + css::uno::Reference< css::util::XReplaceable > mxReplaceable; + css::uno::Reference< css::util::XPropertyReplace> mxPropertyReplace; + css::uno::Reference< css::text::XTextViewCursor> mxTVC; + css::uno::Reference< css::view::XSelectionSupplier> mxSelSupp; + bool mbReplace; + sal_Int32 mnReplaceType; + sal_Int32 mnWrap; + +private: + /// @throws css::uno::RuntimeException + bool InRange( const css::uno::Reference< css::text::XTextRange >& xCurrentRange ); + /// @throws css::uno::RuntimeException + bool InEqualRange( const css::uno::Reference< css::text::XTextRange >& xCurrentRange ); + void SetReplace( sal_Int32 type ); + /// @throws css::uno::RuntimeException + void SetReplaceWith( const OUString& rText ); + /// @throws css::uno::RuntimeException + OUString GetReplaceWith(); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextRange > FindOneElement(); + /// @throws css::uno::RuntimeException + bool SearchReplace(); + +public: + /// @throws css::uno::RuntimeException + SwVbaFind( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + virtual ~SwVbaFind() override; + + // Attributes + virtual OUString SAL_CALL getText() override; + virtual void SAL_CALL setText( const OUString& _text ) override; + virtual css::uno::Any SAL_CALL getReplacement() override; + virtual void SAL_CALL setReplacement( const css::uno::Any& _replacement ) override; + virtual sal_Bool SAL_CALL getForward() override; + virtual void SAL_CALL setForward( sal_Bool _forward ) override; + virtual ::sal_Int32 SAL_CALL getWrap() override; + virtual void SAL_CALL setWrap( ::sal_Int32 _wrap ) override; + virtual sal_Bool SAL_CALL getFormat() override; + virtual void SAL_CALL setFormat( sal_Bool _format ) override; + virtual sal_Bool SAL_CALL getMatchCase() override; + virtual void SAL_CALL setMatchCase( sal_Bool _matchcase ) override; + virtual sal_Bool SAL_CALL getMatchWholeWord() override; + virtual void SAL_CALL setMatchWholeWord( sal_Bool _matchwholeword ) override; + virtual sal_Bool SAL_CALL getMatchWildcards() override; + virtual void SAL_CALL setMatchWildcards( sal_Bool _matchwildcards ) override; + virtual sal_Bool SAL_CALL getMatchSoundsLike() override; + virtual void SAL_CALL setMatchSoundsLike( sal_Bool _matchsoundslike ) override; + virtual sal_Bool SAL_CALL getMatchAllWordForms() override; + virtual void SAL_CALL setMatchAllWordForms( sal_Bool _matchallwordforms ) override; + virtual css::uno::Any SAL_CALL getStyle() override; + virtual void SAL_CALL setStyle( const css::uno::Any& _style ) override; + + // Methods + virtual sal_Bool SAL_CALL Execute( const css::uno::Any& FindText, const css::uno::Any& MatchCase, const css::uno::Any& MatchWholeWord, const css::uno::Any& MatchWildcards, const css::uno::Any& MatchSoundsLike, const css::uno::Any& MatchAllWordForms, const css::uno::Any& Forward, const css::uno::Any& Wrap, const css::uno::Any& Format, const css::uno::Any& ReplaceWith, const css::uno::Any& Replace, const css::uno::Any& MatchKashida, const css::uno::Any& MatchDiacritics, const css::uno::Any& MatchAlefHamza, const css::uno::Any& MatchControl, const css::uno::Any& MatchPrefix, const css::uno::Any& MatchSuffix, const css::uno::Any& MatchPhrase, const css::uno::Any& IgnoreSpace, const css::uno::Any& IgnorePunct ) override; + virtual void SAL_CALL ClearFormatting( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAFIND_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafont.cxx b/sw/source/ui/vba/vbafont.cxx new file mode 100644 index 000000000..01b15bd06 --- /dev/null +++ b/sw/source/ui/vba/vbafont.cxx @@ -0,0 +1,242 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "vbafont.hxx" +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <ooo/vba/word/WdUnderline.hpp> +#include <sal/macros.h> +#include <unordered_map> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +const uno::Any aLongAnyTrue( sal_Int16(-1) ); +const uno::Any aLongAnyFalse( sal_Int16( 0 ) ); + +namespace { + +struct MapPair +{ + sal_Int32 nMSOConst; + sal_Int32 nOOOConst; +}; + +} + +static MapPair const UnderLineTable[] = { + { word::WdUnderline::wdUnderlineNone, css::awt::FontUnderline::NONE }, + { word::WdUnderline::wdUnderlineSingle, css::awt::FontUnderline::SINGLE }, + { word::WdUnderline::wdUnderlineWords, css::awt::FontUnderline::SINGLE }, + { word::WdUnderline::wdUnderlineDouble, css::awt::FontUnderline::DOUBLE }, + { word::WdUnderline::wdUnderlineDotted, css::awt::FontUnderline::DOTTED }, + { word::WdUnderline::wdUnderlineThick, css::awt::FontUnderline::BOLDDASH }, + { word::WdUnderline::wdUnderlineDash, css::awt::FontUnderline::DASH }, + { word::WdUnderline::wdUnderlineDotDash, css::awt::FontUnderline::DASHDOT }, + { word::WdUnderline::wdUnderlineDotDotDash, css::awt::FontUnderline::DASHDOTDOT }, + { word::WdUnderline::wdUnderlineWavy, css::awt::FontUnderline::WAVE }, + { word::WdUnderline::wdUnderlineDottedHeavy, css::awt::FontUnderline::BOLDDOTTED }, + { word::WdUnderline::wdUnderlineDashHeavy, css::awt::FontUnderline::BOLDDASH }, + { word::WdUnderline::wdUnderlineDotDashHeavy, css::awt::FontUnderline::BOLDDASHDOT }, + { word::WdUnderline::wdUnderlineDotDotDashHeavy, css::awt::FontUnderline::BOLDDASHDOTDOT }, + { word::WdUnderline::wdUnderlineWavyHeavy, css::awt::FontUnderline::BOLDWAVE }, + { word::WdUnderline::wdUnderlineDashLong, css::awt::FontUnderline::LONGDASH }, + { word::WdUnderline::wdUnderlineWavyDouble, css::awt::FontUnderline::DOUBLEWAVE }, + { word::WdUnderline::wdUnderlineDashLongHeavy, css::awt::FontUnderline::BOLDLONGDASH }, +}; + +typedef std::unordered_map< sal_Int32, sal_Int32 > ConstToConst; + +namespace { + +class UnderLineMapper +{ + ConstToConst MSO2OOO; + ConstToConst OOO2MSO; +private: + UnderLineMapper() + { + for ( sal_Int32 index=0; index<sal_Int32(SAL_N_ELEMENTS( UnderLineTable )); ++index ) + { + MSO2OOO[ UnderLineTable[ index ].nMSOConst ] = UnderLineTable[ index ].nOOOConst; + OOO2MSO[ UnderLineTable[ index ].nOOOConst ] = UnderLineTable[ index ].nMSOConst; + } + } +public: + static OUString propName() + { + return "CharUnderline"; + } + + static UnderLineMapper& instance() + { + static UnderLineMapper theMapper; + return theMapper; + } + + /// @throws lang::IllegalArgumentException + sal_Int32 getOOOFromMSO( sal_Int32 nMSOConst ) + { + ConstToConst::iterator it = MSO2OOO.find( nMSOConst ); + if ( it == MSO2OOO.end() ) + throw lang::IllegalArgumentException(); + return it->second; + } + /// @throws lang::IllegalArgumentException + sal_Int32 getMSOFromOOO( sal_Int32 nOOOConst ) + { + ConstToConst::iterator it = OOO2MSO.find( nOOOConst ); + if ( it == OOO2MSO.end() ) + throw lang::IllegalArgumentException(); + return it->second; + } +}; + +} + +SwVbaFont::SwVbaFont( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XIndexAccess >& xPalette, uno::Reference< css::beans::XPropertySet > const & xPropertySet ) : SwVbaFont_BASE( xParent, xContext, xPalette, xPropertySet ) +{ +} + +uno::Any SAL_CALL +SwVbaFont::getUnderline() +{ + sal_Int32 nOOVal = 0; + mxFont->getPropertyValue( UnderLineMapper::propName() ) >>= nOOVal; + return uno::makeAny( UnderLineMapper::instance().getMSOFromOOO( nOOVal ) ); +} + +void SAL_CALL +SwVbaFont::setUnderline( const uno::Any& _underline ) +{ + sal_Int32 nMSOVal = 0; + + if ( _underline >>= nMSOVal ) + { + sal_Int32 nOOVal = UnderLineMapper::instance().getOOOFromMSO( nMSOVal ); + mxFont->setPropertyValue( UnderLineMapper::propName(), uno::makeAny( nOOVal ) ); + } +} + +OUString +SwVbaFont::getServiceImplName() +{ + return "SwVbaFont"; +} + +void SAL_CALL +SwVbaFont::setColorIndex( const uno::Any& _colorindex ) +{ + sal_Int32 nIndex = 0; + _colorindex >>= nIndex; + return setColor( OORGBToXLRGB(mxPalette->getByIndex( nIndex )) ); +} + +uno::Any SAL_CALL +SwVbaFont::getColorIndex() +{ + sal_Int32 nColor = 0; + + XLRGBToOORGB( getColor() ) >>= nColor; + sal_Int32 nElems = mxPalette->getCount(); + sal_Int32 nIndex = 0; + for ( sal_Int32 count=0; count<nElems; ++count ) + { + sal_Int32 nPaletteColor = 0; + mxPalette->getByIndex( count ) >>= nPaletteColor; + if ( nPaletteColor == nColor ) + { + nIndex = count; + break; + } + } + return uno::makeAny( nIndex ); +} +uno::Any SAL_CALL +SwVbaFont::getSubscript() +{ + bool bRes = false; + SwVbaFont_BASE::getSubscript() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Any SAL_CALL +SwVbaFont::getSuperscript() +{ + bool bRes = false; + SwVbaFont_BASE::getSuperscript() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Any SAL_CALL +SwVbaFont::getBold() +{ + bool bRes = false; + SwVbaFont_BASE::getBold() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Any SAL_CALL +SwVbaFont::getItalic() +{ + bool bRes = false; + SwVbaFont_BASE::getItalic() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Any SAL_CALL +SwVbaFont::getStrikethrough() +{ + bool bRes = false; + SwVbaFont_BASE::getStrikethrough() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Any SAL_CALL +SwVbaFont::getShadow() +{ + bool bRes = false; + SwVbaFont_BASE::getShadow() >>= bRes; + if ( bRes ) + return aLongAnyTrue; + return aLongAnyFalse; +} + +uno::Sequence< OUString > +SwVbaFont::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Font" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbafont.hxx b/sw/source/ui/vba/vbafont.hxx new file mode 100644 index 000000000..2902de5a2 --- /dev/null +++ b/sw/source/ui/vba/vbafont.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_UI_VBA_VBAFONT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFONT_HXX + +#include <vbahelper/vbafontbase.hxx> +#include <ooo/vba/word/XFont.hpp> +#include <cppuhelper/implbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaFontBase, ov::word::XFont > SwVbaFont_BASE; + +class SwVbaFont : public SwVbaFont_BASE +{ +public: + /// @throws css::uno::RuntimeException + SwVbaFont( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::container::XIndexAccess >& xPalette, css::uno::Reference< css::beans::XPropertySet > const & xPropertySet ); + + // Attributes + virtual void SAL_CALL setColorIndex( const css::uno::Any& _colorindex ) override; + virtual css::uno::Any SAL_CALL getColorIndex() override; + virtual css::uno::Any SAL_CALL getUnderline() override; + virtual void SAL_CALL setUnderline( const css::uno::Any& _underline ) override; + virtual css::uno::Any SAL_CALL getSubscript() override; + virtual css::uno::Any SAL_CALL getSuperscript() override; + + virtual css::uno::Any SAL_CALL getBold() override; + virtual css::uno::Any SAL_CALL getItalic() override; + virtual css::uno::Any SAL_CALL getStrikethrough() override; + virtual css::uno::Any SAL_CALL getShadow() override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaframe.cxx b/sw/source/ui/vba/vbaframe.cxx new file mode 100644 index 000000000..4aec19444 --- /dev/null +++ b/sw/source/ui/vba/vbaframe.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 "vbaframe.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaFrame::SwVbaFrame( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const css::uno::Reference< frame::XModel >& rModel, const css::uno::Reference< text::XTextFrame >& xTextFrame ) : + SwVbaFrame_BASE( rParent, rContext ), mxModel( rModel ), mxTextFrame( xTextFrame ) +{ +} + +SwVbaFrame::~SwVbaFrame() +{ +} + +void SAL_CALL SwVbaFrame::Select() +{ + uno::Reference< view::XSelectionSupplier > xSelectSupp( mxModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xSelectSupp->select( uno::makeAny( mxTextFrame ) ); +} + +OUString +SwVbaFrame::getServiceImplName() +{ + return "SwVbaFrame"; +} + +uno::Sequence< OUString > +SwVbaFrame::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Frame" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaframe.hxx b/sw/source/ui/vba/vbaframe.hxx new file mode 100644 index 000000000..2097e9ac1 --- /dev/null +++ b/sw/source/ui/vba/vbaframe.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_UI_VBA_VBAFRAME_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFRAME_HXX + +#include <ooo/vba/word/XFrame.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextFrame.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XFrame > SwVbaFrame_BASE; + +class SwVbaFrame : public SwVbaFrame_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextFrame > mxTextFrame; + +public: + /// @throws css::uno::RuntimeException + SwVbaFrame( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& rModel, const css::uno::Reference< css::text::XTextFrame >& xTextFrame ); + virtual ~SwVbaFrame() override; + + // Methods + virtual void SAL_CALL Select() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAFRAME_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaframes.cxx b/sw/source/ui/vba/vbaframes.cxx new file mode 100644 index 000000000..676438a19 --- /dev/null +++ b/sw/source/ui/vba/vbaframes.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 "vbaframes.hxx" +#include "vbaframe.hxx" +#include <com/sun/star/frame/XModel.hpp> +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class FramesEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< container::XIndexAccess> mxIndexAccess; + uno::Reference< frame::XModel > mxModel; + sal_Int32 nCurrentPos; +public: + /// @throws uno::RuntimeException + FramesEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess, const uno::Reference< frame::XModel >& xModel ) : mxParent( xParent ), mxContext( xContext), mxIndexAccess( xIndexAccess ), mxModel( xModel ), nCurrentPos(0) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nCurrentPos < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( !hasMoreElements() ) + throw container::NoSuchElementException(); + uno::Reference< text::XTextFrame > xTextFrame( mxIndexAccess->getByIndex( nCurrentPos++ ), uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XFrame > ( new SwVbaFrame( mxParent, mxContext, mxModel, xTextFrame ) ) ); + } + +}; + +} + +SwVbaFrames::SwVbaFrames( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< container::XIndexAccess >& xFrames, const uno::Reference< frame::XModel >& xModel ): SwVbaFrames_BASE( xParent, xContext, xFrames ), mxModel( xModel ) +{ + mxFramesSupplier.set( mxModel, uno::UNO_QUERY_THROW ); +} +// XEnumerationAccess +uno::Type +SwVbaFrames::getElementType() +{ + return cppu::UnoType<word::XFrame>::get(); +} + +uno::Reference< container::XEnumeration > +SwVbaFrames::createEnumeration() +{ + return new FramesEnumeration( this, mxContext,m_xIndexAccess, mxModel ); +} + +uno::Any +SwVbaFrames::createCollectionObject( const css::uno::Any& aSource ) +{ + uno::Reference< text::XTextFrame > xTextFrame( aSource, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XFrame > ( new SwVbaFrame( this, mxContext, mxModel, xTextFrame ) ) ); +} + +OUString +SwVbaFrames::getServiceImplName() +{ + return "SwVbaFrames"; +} + +css::uno::Sequence<OUString> +SwVbaFrames::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Frames" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaframes.hxx b/sw/source/ui/vba/vbaframes.hxx new file mode 100644 index 000000000..e83f411b1 --- /dev/null +++ b/sw/source/ui/vba/vbaframes.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_VBA_VBAFRAMES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAFRAMES_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XFrames.hpp> +#include <com/sun/star/text/XTextFramesSupplier.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XFrames > SwVbaFrames_BASE; + +class SwVbaFrames : public SwVbaFrames_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextFramesSupplier > mxFramesSupplier; + +public: + SwVbaFrames( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XIndexAccess >& xFrames, const css::uno::Reference< css::frame::XModel >& xModel ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaFrames_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAFRAMES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaglobals.cxx b/sw/source/ui/vba/vbaglobals.cxx new file mode 100644 index 000000000..527d6c61e --- /dev/null +++ b/sw/source/ui/vba/vbaglobals.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 <vbahelper/helperdecl.hxx> +#include "service.hxx" +#include "vbaglobals.hxx" +#include <sal/log.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include "vbaapplication.hxx" +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::ooo::vba; + +SwVbaGlobals::SwVbaGlobals( uno::Sequence< uno::Any > const& aArgs, uno::Reference< uno::XComponentContext >const& rxContext ) : SwVbaGlobals_BASE( uno::Reference< XHelperInterface >(), rxContext, "WordDocumentContext" ) +{ + SAL_INFO("sw.vba", "SwVbaGlobals::SwVbaGlobals()"); + uno::Sequence< beans::PropertyValue > aInitArgs( aArgs.getLength() + 1 ); + aInitArgs[ 0 ].Name = "Application"; + aInitArgs[ 0 ].Value <<= getApplication(); + if ( aArgs.hasElements() ) + { + aInitArgs[ 1 ].Name = "WordDocumentContext"; + aInitArgs[ 1 ].Value <<= getXSomethingFromArgs< frame::XModel >( aArgs, 0 ); + } + init( aInitArgs ); +} + +SwVbaGlobals::~SwVbaGlobals() +{ + SAL_INFO("sw.vba", "SwVbaGlobals::~SwVbaGlobals"); +} + +// XGlobals + +uno::Reference<word::XApplication > const & +SwVbaGlobals::getApplication() +{ + SAL_INFO("sw.vba", "In SwVbaGlobals::getApplication"); + if ( !mxApplication.is() ) + mxApplication.set( new SwVbaApplication( mxContext) ); + + return mxApplication; +} + +uno::Reference<word::XSystem > SAL_CALL +SwVbaGlobals::getSystem() +{ + return getApplication()->getSystem(); +} + +uno::Reference< word::XDocument > SAL_CALL +SwVbaGlobals::getActiveDocument() +{ + return getApplication()->getActiveDocument(); +} + +uno::Reference< word::XWindow > SAL_CALL +SwVbaGlobals::getActiveWindow() +{ + return getApplication()->getActiveWindow(); +} + +OUString SAL_CALL +SwVbaGlobals::getName() +{ + return getApplication()->getName(); +} + +uno::Reference<word::XOptions > SAL_CALL +SwVbaGlobals::getOptions() +{ + return getApplication()->getOptions(); +} + +uno::Any SAL_CALL +SwVbaGlobals::CommandBars( const uno::Any& aIndex ) +{ + return getApplication()->CommandBars( aIndex ); +} + +uno::Any SAL_CALL +SwVbaGlobals::Documents( const uno::Any& index ) +{ + return getApplication()->Documents( index ); +} + +uno::Any SAL_CALL +SwVbaGlobals::Addins( const uno::Any& index ) +{ + return getApplication()->Addins( index ); +} + +uno::Any SAL_CALL +SwVbaGlobals::Dialogs( const uno::Any& index ) +{ + return getApplication()->Dialogs( index ); +} + +uno::Any SAL_CALL +SwVbaGlobals::ListGalleries( const uno::Any& index ) +{ + return getApplication()->ListGalleries( index ); +} + +uno::Reference<word::XSelection > SAL_CALL +SwVbaGlobals::getSelection() +{ + return getApplication()->getSelection(); +} + +float SAL_CALL SwVbaGlobals::CentimetersToPoints( float Centimeters ) +{ + return getApplication()->CentimetersToPoints( Centimeters ); +} + +OUString +SwVbaGlobals::getServiceImplName() +{ + return "SwVbaGlobals"; +} + +uno::Sequence< OUString > +SwVbaGlobals::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Globals" + }; + return aServiceNames; +} + +uno::Sequence< OUString > +SwVbaGlobals::getAvailableServiceNames( ) +{ + static uno::Sequence< OUString > const serviceNames = [&]() + { + uno::Sequence< OUString > tmp = SwVbaGlobals_BASE::getAvailableServiceNames(); + tmp.realloc( tmp.getLength() + 1 ); + tmp[ tmp.getLength() - 1 ] = "ooo.vba.word.Document"; +// #FIXME #TODO make Application a proper service +// OUString( "ooo.vba.word.Application" ), + return tmp; + }(); + return serviceNames; +} + +namespace globals +{ +namespace sdecl = comphelper::service_decl; +sdecl::vba_service_class_<SwVbaGlobals, sdecl::with_args<true> > const serviceImpl; +sdecl::ServiceDecl const serviceDecl( + serviceImpl, + "SwVbaGlobals", + "ooo.vba.word.Globals" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaglobals.hxx b/sw/source/ui/vba/vbaglobals.hxx new file mode 100644 index 000000000..23e40c45d --- /dev/null +++ b/sw/source/ui/vba/vbaglobals.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_UI_VBA_VBAGLOBALS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAGLOBALS_HXX + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <ooo/vba/word/XGlobals.hpp> +#include <ooo/vba/word/XApplication.hpp> +#include <ooo/vba/word/XSystem.hpp> +#include <ooo/vba/word/XOptions.hpp> +#include <ooo/vba/word/XSelection.hpp> +#include <cppuhelper/implbase.hxx> +#include <vbahelper/vbaglobalbase.hxx> + +typedef ::cppu::ImplInheritanceHelper< VbaGlobalsBase, ov::word::XGlobals > SwVbaGlobals_BASE; + +class SwVbaGlobals : public SwVbaGlobals_BASE +{ +private: + css::uno::Reference< ooo::vba::word::XApplication > mxApplication; + + /// @throws css::uno::RuntimeException + css::uno::Reference< ooo::vba::word::XApplication > const & getApplication(); + +public: + + SwVbaGlobals( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& rxContext ); + virtual ~SwVbaGlobals() override; + + // XGlobals + virtual OUString SAL_CALL getName() override; + virtual css::uno::Reference< ooo::vba::word::XSystem > SAL_CALL getSystem() override; + virtual css::uno::Reference< ov::word::XDocument > SAL_CALL getActiveDocument() override; + virtual css::uno::Reference< ov::word::XWindow > SAL_CALL getActiveWindow() override; + virtual css::uno::Reference< ooo::vba::word::XOptions > SAL_CALL getOptions() override; + virtual css::uno::Reference< ooo::vba::word::XSelection > SAL_CALL getSelection() override; + virtual css::uno::Any SAL_CALL CommandBars( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Documents( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Addins( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Dialogs( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL ListGalleries( const css::uno::Any& aIndex ) override; + virtual float SAL_CALL CentimetersToPoints( float Centimeters ) override; + // XMultiServiceFactory + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAGLOBALS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheaderfooter.cxx b/sw/source/ui/vba/vbaheaderfooter.cxx new file mode 100644 index 000000000..9a9df62ac --- /dev/null +++ b/sw/source/ui/vba/vbaheaderfooter.cxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaheaderfooter.hxx" +#include <vbahelper/vbahelper.hxx> +#include <ooo/vba/word/WdHeaderFooterIndex.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include "vbarange.hxx" +#include <vbahelper/vbashapes.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaHeaderFooter::SwVbaHeaderFooter( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& rProps, bool isHeader, sal_Int32 index ) : SwVbaHeaderFooter_BASE( rParent, rContext ), mxModel( xModel ), mxPageStyleProps( rProps ), mbHeader( isHeader ), mnIndex( index ) +{ +} + +sal_Bool SAL_CALL SwVbaHeaderFooter::getIsHeader() +{ + return mbHeader; +} + +sal_Bool SAL_CALL SwVbaHeaderFooter::getLinkToPrevious() +{ + // seems always false + return false; +} + +void SAL_CALL SwVbaHeaderFooter::setLinkToPrevious( sal_Bool /*_linktoprevious*/ ) +{ + // not support in Writer +} + +uno::Reference< word::XRange > SAL_CALL SwVbaHeaderFooter::getRange() +{ + OUString sPropsNameText; + if( mbHeader ) + { + sPropsNameText = "HeaderText"; + } + else + { + sPropsNameText = "FooterText"; + } + if( mnIndex == word::WdHeaderFooterIndex::wdHeaderFooterEvenPages ) + { + sPropsNameText += "Left"; + } + + uno::Reference< text::XText > xText( mxPageStyleProps->getPropertyValue( sPropsNameText ), uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextDocument > xDocument( mxModel, uno::UNO_QUERY_THROW ); + return uno::Reference< word::XRange >( new SwVbaRange( this, mxContext, xDocument, xText->getStart(), xText->getEnd(), xText ) ); +} + +uno::Any SAL_CALL +SwVbaHeaderFooter::Shapes( const uno::Any& index ) +{ + // #FIXME: only get the shapes in the current header/footer + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( mxModel, uno::UNO_QUERY_THROW ); + //uno::Reference< drawing::XShapes > xShapes( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new ScVbaShapes( this, mxContext, xIndexAccess, mxModel ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +OUString +SwVbaHeaderFooter::getServiceImplName() +{ + return "SwVbaHeaderFooter"; +} + +uno::Sequence< OUString > +SwVbaHeaderFooter::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Pane" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheaderfooter.hxx b/sw/source/ui/vba/vbaheaderfooter.hxx new file mode 100644 index 000000000..fd3a3dea4 --- /dev/null +++ b/sw/source/ui/vba/vbaheaderfooter.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_UI_VBA_VBAHEADERFOOTER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAHEADERFOOTER_HXX + +#include <ooo/vba/word/XHeaderFooter.hpp> +#include <ooo/vba/word/XRange.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XHeaderFooter > SwVbaHeaderFooter_BASE; + +class SwVbaHeaderFooter : public SwVbaHeaderFooter_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::beans::XPropertySet > mxPageStyleProps; + bool mbHeader; + sal_Int32 mnIndex; + +public: + /// @throws css::uno::RuntimeException + SwVbaHeaderFooter( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::beans::XPropertySet >& xProps, bool isHeader, sal_Int32 index ); + + // Attributes + virtual sal_Bool SAL_CALL getIsHeader() override; + virtual sal_Bool SAL_CALL getLinkToPrevious() override; + virtual void SAL_CALL setLinkToPrevious( sal_Bool _linktoprevious ) override; + virtual css::uno::Reference< ::ooo::vba::word::XRange > SAL_CALL getRange() override; + virtual css::uno::Any SAL_CALL Shapes( const css::uno::Any& aIndex ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAHEADERFOOTER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheaderfooterhelper.cxx b/sw/source/ui/vba/vbaheaderfooterhelper.cxx new file mode 100644 index 000000000..3e9f937a1 --- /dev/null +++ b/sw/source/ui/vba/vbaheaderfooterhelper.cxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaheaderfooterhelper.hxx" +#include "wordvbahelper.hxx" +#include <com/sun/star/text/XTextRangeCompare.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +#define FIRST_PAGE 1 + +// Class HeaderFooterHelper +bool HeaderFooterHelper::isHeaderFooter( const uno::Reference< frame::XModel >& xModel ) +{ + return isHeaderFooter( word::getCurrentXText( xModel ) ); +} + +bool HeaderFooterHelper::isHeaderFooter( const uno::Reference< text::XText >& xText ) +{ + uno::Reference< lang::XServiceInfo > xServiceInfo( xText, uno::UNO_QUERY_THROW ); + OUString aImplName = xServiceInfo->getImplementationName(); + return aImplName == "SwXHeadFootText"; +} + +bool HeaderFooterHelper::isHeader( const uno::Reference< frame::XModel >& xModel ) +{ + const uno::Reference< text::XText > xCurrentText = word::getCurrentXText( xModel ); + if( !isHeaderFooter( xCurrentText ) ) + return false; + + OUString aPropIsShared = "HeaderIsShared"; + OUString aPropText = "HeaderText"; + uno::Reference< style::XStyle > xPageStyle = word::getCurrentPageStyle( xModel ); + uno::Reference< beans::XPropertySet > xPageProps( xPageStyle, uno::UNO_QUERY_THROW ); + bool isShared = true; + xPageProps->getPropertyValue( aPropIsShared ) >>= isShared; + if( !isShared ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + if( 0 == xPageCursor->getPage() % 2 ) + aPropText = "HeaderTextLeft"; + else + aPropText = "HeaderTextRight"; + } + + uno::Reference< text::XText > xHeaderText( xPageProps->getPropertyValue( aPropText ), uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRangeCompare > xTRC( xHeaderText, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTR1( xCurrentText, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTR2( xHeaderText, uno::UNO_QUERY_THROW ); + try + { + if( xTRC->compareRegionStarts( xTR1, xTR2 ) == 0 ) + return true; + } + catch (const lang::IllegalArgumentException&) + { + return false; + } + + return false; +} + +bool HeaderFooterHelper::isFirstPageHeader( const uno::Reference< frame::XModel >& xModel ) +{ + if( isHeader( xModel ) ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + // FIXME: getPage always returns 1 + sal_Int32 nPage = xPageCursor->getPage(); + return nPage == FIRST_PAGE; + } + return false; +} + +bool HeaderFooterHelper::isEvenPagesHeader( const uno::Reference< frame::XModel >& xModel ) +{ + if( isHeader( xModel ) ) + { + uno::Reference< beans::XPropertySet > xStyleProps( word::getCurrentPageStyle( xModel ), uno::UNO_QUERY_THROW ); + bool isShared = false; + xStyleProps->getPropertyValue("HeaderIsShared") >>= isShared; + if( !isShared ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + return ( 0 == xPageCursor->getPage() % 2 ); + } + } + return false; +} + +bool HeaderFooterHelper::isFooter( const uno::Reference< frame::XModel >& xModel ) +{ + const uno::Reference< text::XText > xCurrentText = word::getCurrentXText( xModel ); + if( !isHeaderFooter( xCurrentText ) ) + return false; + + OUString aPropIsShared = "FooterIsShared"; + OUString aPropText = "FooterText"; + uno::Reference< style::XStyle > xPageStyle = word::getCurrentPageStyle( xModel ); + uno::Reference< beans::XPropertySet > xPageProps( xPageStyle, uno::UNO_QUERY_THROW ); + bool isShared = true; + xPageProps->getPropertyValue( aPropIsShared ) >>= isShared; + if( !isShared ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + if( 0 == xPageCursor->getPage() % 2 ) + aPropText = "FooterTextLeft"; + else + aPropText = "FooterTextRight"; + } + + uno::Reference< text::XText > xFooterText( xPageProps->getPropertyValue( aPropText ), uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRangeCompare > xTRC( xFooterText, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTR1( xCurrentText, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTR2( xFooterText, uno::UNO_QUERY_THROW ); + try + { + if( xTRC->compareRegionStarts( xTR1, xTR2 ) == 0 ) + return true; + } + catch (const lang::IllegalArgumentException&) + { + return false; + } + + return false; +} + +bool HeaderFooterHelper::isFirstPageFooter( const uno::Reference< frame::XModel >& xModel ) +{ + if( isFooter( xModel ) ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + sal_Int32 nPage = xPageCursor->getPage(); + return nPage == FIRST_PAGE; + } + return false; +} + +bool HeaderFooterHelper::isEvenPagesFooter( const uno::Reference< frame::XModel >& xModel ) +{ + if( isFooter( xModel ) ) + { + uno::Reference< beans::XPropertySet > xStyleProps( word::getCurrentPageStyle( xModel ), uno::UNO_QUERY_THROW ); + bool isShared = false; + xStyleProps->getPropertyValue("FooterIsShared") >>= isShared; + if( !isShared ) + { + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + return ( 0 == xPageCursor->getPage() % 2 ); + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheaderfooterhelper.hxx b/sw/source/ui/vba/vbaheaderfooterhelper.hxx new file mode 100644 index 000000000..ed3c5d2d9 --- /dev/null +++ b/sw/source/ui/vba/vbaheaderfooterhelper.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_UI_VBA_VBAHEADERFOOTERHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAHEADERFOOTERHELPER_HXX + +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/text/XText.hpp> + +class HeaderFooterHelper +{ +public: + /// @throws css::uno::RuntimeException + static bool isHeaderFooter( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isHeaderFooter( const css::uno::Reference< css::text::XText >& xText ); + /// @throws css::uno::RuntimeException + static bool isHeader( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isFirstPageHeader( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isEvenPagesHeader( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isFooter( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isFirstPageFooter( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static bool isEvenPagesFooter( const css::uno::Reference< css::frame::XModel >& xModel ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheadersfooters.cxx b/sw/source/ui/vba/vbaheadersfooters.cxx new file mode 100644 index 000000000..03a997d3f --- /dev/null +++ b/sw/source/ui/vba/vbaheadersfooters.cxx @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaheadersfooters.hxx" +#include "vbaheaderfooter.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +// I assume there is only one headersfooters in Writer +class HeadersFootersIndexAccess : public ::cppu::WeakImplHelper<container::XIndexAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + uno::Reference< beans::XPropertySet > mxPageStyleProps; + bool mbHeader; + +public: + HeadersFootersIndexAccess( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& xPageStyleProps, bool bHeader ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ), mxPageStyleProps( xPageStyleProps ), mbHeader( bHeader ) {} + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override + { + // first page, even pages and primary page + return 3; + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if( Index < 1 || Index > 3 ) + throw lang::IndexOutOfBoundsException(); + return uno::makeAny( uno::Reference< word::XHeaderFooter >( new SwVbaHeaderFooter( mxParent, mxContext, mxModel, mxPageStyleProps, mbHeader, Index ) ) ); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XHeaderFooter>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } +}; + +class HeadersFootersEnumWrapper : public EnumerationHelper_BASE +{ + SwVbaHeadersFooters* pHeadersFooters; + sal_Int32 nIndex; +public: + explicit HeadersFootersEnumWrapper( SwVbaHeadersFooters* _pHeadersFooters ) : pHeadersFooters( _pHeadersFooters ), nIndex( 0 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < pHeadersFooters->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex < pHeadersFooters->getCount() ) + return pHeadersFooters->Item( uno::makeAny( ++nIndex ), uno::Any() ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaHeadersFooters::SwVbaHeadersFooters( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& xPageStyleProps, bool isHeader ): SwVbaHeadersFooters_BASE( xParent, xContext, new HeadersFootersIndexAccess( xParent, xContext, xModel, xPageStyleProps, isHeader ) ), mxModel( xModel ), mxPageStyleProps( xPageStyleProps ), mbHeader( isHeader ) +{ +} + +::sal_Int32 SAL_CALL SwVbaHeadersFooters::getCount() +{ + // wdHeaderFooterFirstPage, wdHeaderFooterPrimary and wdHeaderFooterEvenPages + return 3; +} + +uno::Any SAL_CALL SwVbaHeadersFooters::Item( const uno::Any& Index1, const uno::Any& ) +{ + sal_Int32 nIndex = 0; + Index1 >>= nIndex; + if( ( nIndex < 1 ) || ( nIndex > 3 ) ) + { + throw lang::IndexOutOfBoundsException(); + } + return uno::makeAny( uno::Reference< word::XHeaderFooter >( new SwVbaHeaderFooter( this, mxContext, mxModel, mxPageStyleProps, mbHeader, nIndex ) ) ); +} + +// XEnumerationAccess +uno::Type +SwVbaHeadersFooters::getElementType() +{ + return cppu::UnoType<word::XHeaderFooter>::get(); +} +uno::Reference< container::XEnumeration > + +SwVbaHeadersFooters::createEnumeration() +{ + return new HeadersFootersEnumWrapper( this ); +} + +uno::Any +SwVbaHeadersFooters::createCollectionObject( const uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaHeadersFooters::getServiceImplName() +{ + return "SwVbaHeadersFooters"; +} + +uno::Sequence<OUString> +SwVbaHeadersFooters::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.HeadersFooters" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaheadersfooters.hxx b/sw/source/ui/vba/vbaheadersfooters.hxx new file mode 100644 index 000000000..44aa77cfd --- /dev/null +++ b/sw/source/ui/vba/vbaheadersfooters.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_UI_VBA_VBAHEADERSFOOTERS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAHEADERSFOOTERS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XHeadersFooters.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XHeadersFooters > SwVbaHeadersFooters_BASE; + +class SwVbaHeadersFooters : public SwVbaHeadersFooters_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::beans::XPropertySet > mxPageStyleProps; + bool mbHeader; + +public: + SwVbaHeadersFooters( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::beans::XPropertySet >& xProps, bool isHeader ); + + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaHeadersFooters_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAHEADERSFOOTERS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbainformationhelper.cxx b/sw/source/ui/vba/vbainformationhelper.cxx new file mode 100644 index 000000000..f6aeacee7 --- /dev/null +++ b/sw/source/ui/vba/vbainformationhelper.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 "vbainformationhelper.hxx" +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include "wordvbahelper.hxx" +#include <docsh.hxx> +#include <doc.hxx> +#include <vbahelper/vbahelper.hxx> +#include <viewsh.hxx> +#include <IDocumentLayoutAccess.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static const sal_Int32 DEFAULT_PAGE_DISTANCE = 500; + +sal_Int32 SwVbaInformationHelper::handleWdActiveEndPageNumber( const css::uno::Reference< css::text::XTextViewCursor >& xTVCursor ) +{ + uno::Reference< text::XPageCursor > xPageCursor( xTVCursor, uno::UNO_QUERY_THROW ); + return xPageCursor->getPage(); +} + +sal_Int32 SwVbaInformationHelper::handleWdNumberOfPagesInDocument( const css::uno::Reference< css::frame::XModel >& xModel ) +{ + return word::getPageCount( xModel ); +} + +double SwVbaInformationHelper::handleWdVerticalPositionRelativeToPage( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextViewCursor >& xTVCursor ) +{ + xTVCursor->collapseToStart(); + uno::Reference< beans::XPropertySet > xStyleProps( word::getCurrentPageStyle( xModel ), uno::UNO_QUERY_THROW ); + sal_Int32 nTopMargin = 0; + xStyleProps->getPropertyValue( "TopMargin" ) >>= nTopMargin; + sal_Int32 nCurrentPos = xTVCursor->getPosition().Y; + + sal_Int32 nCurrentPage = handleWdActiveEndPageNumber( xTVCursor ); + SwDoc* pDoc = word::getDocShell( xModel )->GetDoc(); + SwViewShell* pViewSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + sal_Int32 nPageHeight = pViewSh ? pViewSh->GetPageSize( nCurrentPage, false ).Height() : 0; + // FIXME: handle multiple page style + // it is very strange that the cursor position is incorrect when open Word file. + // e.g. if current cursor in the top left of the text body of the first page without header, + // the top value of current position should be 0, but is 201 when open a Word file. + nCurrentPos = nCurrentPos + nTopMargin - ( DEFAULT_PAGE_DISTANCE + convertTwipToMm100( nPageHeight ) ) * ( nCurrentPage - 1 ); + return Millimeter::getInPoints( nCurrentPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbainformationhelper.hxx b/sw/source/ui/vba/vbainformationhelper.hxx new file mode 100644 index 000000000..01e5500ba --- /dev/null +++ b/sw/source/ui/vba/vbainformationhelper.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAINFORMATIONHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAINFORMATIONHELPER_HXX + +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/frame/XModel.hpp> + +class SwVbaInformationHelper +{ +public: + /// @throws css::uno::RuntimeException + static sal_Int32 handleWdActiveEndPageNumber( const css::uno::Reference< css::text::XTextViewCursor >& xTVCursor ); + /// @throws css::uno::RuntimeException + static sal_Int32 handleWdNumberOfPagesInDocument( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + static double handleWdVerticalPositionRelativeToPage( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextViewCursor >& xTVCursor ); + //static double verticalPositionRelativeToPageBoundary( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextViewCursor >& xTVCursor, const css::uno::Reference< css::beans::XPropertySet >& xStyleProps ) throw( css::uno::RuntimeException ); + +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAINFORMATIONHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistformat.cxx b/sw/source/ui/vba/vbalistformat.cxx new file mode 100644 index 000000000..9ad18ebfc --- /dev/null +++ b/sw/source/ui/vba/vbalistformat.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbalistformat.hxx" +#include <vbahelper/vbahelper.hxx> +#include <ooo/vba/word/WdListApplyTo.hpp> +#include <ooo/vba/word/WdDefaultListBehavior.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include "vbalisttemplate.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaListFormat::SwVbaListFormat( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextRange >& xTextRange ) : SwVbaListFormat_BASE( rParent, rContext ), mxTextRange( xTextRange ) +{ +} + +SwVbaListFormat::~SwVbaListFormat() +{ +} + +void SAL_CALL SwVbaListFormat::ApplyListTemplate( const css::uno::Reference< word::XListTemplate >& ListTemplate, const css::uno::Any& ContinuePreviousList, const css::uno::Any& ApplyTo, const css::uno::Any& DefaultListBehavior ) +{ + bool bContinuePreviousList = true; + if( ContinuePreviousList.hasValue() ) + ContinuePreviousList >>= bContinuePreviousList; + + // "applyto" must be current selection + sal_Int32 bApplyTo = word::WdListApplyTo::wdListApplyToSelection; + if( ApplyTo.hasValue() ) + ApplyTo >>= bApplyTo; + if( bApplyTo != word::WdListApplyTo::wdListApplyToSelection ) + throw uno::RuntimeException(); + + // default behaviour must be wdWord8ListBehavior + sal_Int32 nDefaultListBehavior = word::WdDefaultListBehavior::wdWord8ListBehavior; + if( DefaultListBehavior.hasValue() ) + DefaultListBehavior >>= nDefaultListBehavior; + if( nDefaultListBehavior != word::WdDefaultListBehavior::wdWord8ListBehavior ) + throw uno::RuntimeException(); + + uno::Reference< container::XEnumerationAccess > xEnumAccess( mxTextRange, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration(); + if (!xEnum->hasMoreElements()) + return; + + SwVbaListTemplate& rListTemplate = dynamic_cast<SwVbaListTemplate&>(*ListTemplate); + + bool isFirstElement = true; + do + { + uno::Reference< beans::XPropertySet > xProps( xEnum->nextElement(), uno::UNO_QUERY_THROW ); + if( isFirstElement ) + { + bool isNumberingRestart = !bContinuePreviousList; + xProps->setPropertyValue("ParaIsNumberingRestart", uno::makeAny( isNumberingRestart ) ); + if( isNumberingRestart ) + { + xProps->setPropertyValue("NumberingStartValue", uno::makeAny( sal_Int16(1) ) ); + } + isFirstElement = false; + } + else + { + xProps->setPropertyValue("ParaIsNumberingRestart", uno::makeAny( false ) ); + } + rListTemplate.applyListTemplate( xProps ); + } + while( xEnum->hasMoreElements() ); +} + +void SAL_CALL SwVbaListFormat::ConvertNumbersToText( ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +OUString +SwVbaListFormat::getServiceImplName() +{ + return "SwVbaListFormat"; +} + +uno::Sequence< OUString > +SwVbaListFormat::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.ListFormat" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistformat.hxx b/sw/source/ui/vba/vbalistformat.hxx new file mode 100644 index 000000000..273c67fe5 --- /dev/null +++ b/sw/source/ui/vba/vbalistformat.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_UI_VBA_VBALISTFORMAT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTFORMAT_HXX + +#include <ooo/vba/word/XListFormat.hpp> +#include <ooo/vba/word/XListTemplate.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextRange.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XListFormat > SwVbaListFormat_BASE; + +class SwVbaListFormat : public SwVbaListFormat_BASE +{ +private: + css::uno::Reference< css::text::XTextRange > mxTextRange; + +public: + /// @throws css::uno::RuntimeException + SwVbaListFormat( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + virtual ~SwVbaListFormat() override; + + // Methods + virtual void SAL_CALL ApplyListTemplate( const css::uno::Reference< ::ooo::vba::word::XListTemplate >& ListTemplate, const css::uno::Any& ContinuePreviousList, const css::uno::Any& ApplyTo, const css::uno::Any& DefaultListBehavior ) override; + virtual void SAL_CALL ConvertNumbersToText( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTFORMAT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistgalleries.cxx b/sw/source/ui/vba/vbalistgalleries.cxx new file mode 100644 index 000000000..0a4c8084b --- /dev/null +++ b/sw/source/ui/vba/vbalistgalleries.cxx @@ -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 . + */ +#include "vbalistgalleries.hxx" +#include "vbalistgallery.hxx" +#include <ooo/vba/word/WdListGalleryType.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class ListGalleriesEnumWrapper : public EnumerationHelper_BASE +{ + SwVbaListGalleries* pListGalleries; + sal_Int32 nIndex; +public: + explicit ListGalleriesEnumWrapper( SwVbaListGalleries* pGalleries ) : pListGalleries( pGalleries ), nIndex( 1 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex <= pListGalleries->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex <= pListGalleries->getCount() ) + return pListGalleries->Item( uno::makeAny( nIndex++ ), uno::Any() ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaListGalleries::SwVbaListGalleries( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextDocument >& xTextDoc ) : SwVbaListGalleries_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >() ), mxTextDocument( xTextDoc ) +{ +} + +::sal_Int32 SAL_CALL SwVbaListGalleries::getCount() +{ + // 3 types of list( bullet, numbered and outline ) + return 3; +} + +uno::Any SAL_CALL SwVbaListGalleries::Item( const uno::Any& Index1, const uno::Any& /*not processed in this base class*/ ) +{ + sal_Int32 nIndex = 0; + if( Index1 >>= nIndex ) + { + if( nIndex == word::WdListGalleryType::wdBulletGallery + || nIndex == word::WdListGalleryType::wdNumberGallery + || nIndex == word::WdListGalleryType::wdOutlineNumberGallery ) + return uno::makeAny( uno::Reference< word::XListGallery >( new SwVbaListGallery( this, mxContext, mxTextDocument, nIndex ) ) ); + } + throw uno::RuntimeException("Index out of bounds" ); +} + +// XEnumerationAccess +uno::Type +SwVbaListGalleries::getElementType() +{ + return cppu::UnoType<word::XListGallery>::get(); +} + +uno::Reference< container::XEnumeration > +SwVbaListGalleries::createEnumeration() +{ + return new ListGalleriesEnumWrapper( this ); +} + +uno::Any +SwVbaListGalleries::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaListGalleries::getServiceImplName() +{ + return "SwVbaListGalleries"; +} + +css::uno::Sequence<OUString> +SwVbaListGalleries::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.ListGalleries" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistgalleries.hxx b/sw/source/ui/vba/vbalistgalleries.hxx new file mode 100644 index 000000000..57cfc9d98 --- /dev/null +++ b/sw/source/ui/vba/vbalistgalleries.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_UI_VBA_VBALISTGALLERIES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTGALLERIES_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XListGalleries.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XListGalleries > SwVbaListGalleries_BASE; + +class SwVbaListGalleries : public SwVbaListGalleries_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + +public: + /// @throws css::uno::RuntimeException + SwVbaListGalleries( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextDocument >& xTextDoc ); + + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& /*not processed in this base class*/ ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaListGalleries_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTGALLERIES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistgallery.cxx b/sw/source/ui/vba/vbalistgallery.cxx new file mode 100644 index 000000000..d07e96c4e --- /dev/null +++ b/sw/source/ui/vba/vbalistgallery.cxx @@ -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 . + */ +#include "vbalistgallery.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbalisttemplates.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaListGallery::SwVbaListGallery( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& xTextDoc, sal_Int32 nType ) : SwVbaListGallery_BASE( rParent, rContext ), mxTextDocument( xTextDoc ), mnType( nType ) +{ +} + +SwVbaListGallery::~SwVbaListGallery() +{ +} + +uno::Any SAL_CALL +SwVbaListGallery::ListTemplates( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaListTemplates( mxParent, mxContext, mxTextDocument, mnType ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +OUString +SwVbaListGallery::getServiceImplName() +{ + return "SwVbaListGallery"; +} + +uno::Sequence< OUString > +SwVbaListGallery::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.ListGallery" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistgallery.hxx b/sw/source/ui/vba/vbalistgallery.hxx new file mode 100644 index 000000000..187cdd418 --- /dev/null +++ b/sw/source/ui/vba/vbalistgallery.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_UI_VBA_VBALISTGALLERY_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTGALLERY_HXX + +#include <ooo/vba/word/XListGallery.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextDocument.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XListGallery > SwVbaListGallery_BASE; + +class SwVbaListGallery : public SwVbaListGallery_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + sal_Int32 mnType; + +public: + /// @throws css::uno::RuntimeException + SwVbaListGallery( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& xTextDoc, sal_Int32 nType ); + virtual ~SwVbaListGallery() override; + + // Methods + virtual css::uno::Any SAL_CALL ListTemplates( const css::uno::Any& index ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTGALLERY_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisthelper.cxx b/sw/source/ui/vba/vbalisthelper.cxx new file mode 100644 index 000000000..ee92f31f9 --- /dev/null +++ b/sw/source/ui/vba/vbalisthelper.cxx @@ -0,0 +1,660 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbalisthelper.hxx" +#include <vbahelper/vbahelper.hxx> +#include <sal/log.hxx> +#include <ooo/vba/word/WdListGalleryType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/container/XIndexReplace.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static const sal_Int32 LIST_LEVEL_COUNT = 9; + +static const char UNO_NAME_PARENT_NUMBERING[] = "ParentNumbering"; +static const char UNO_NAME_PREFIX[] = "Prefix"; +static const char UNO_NAME_SUFFIX[] = "Suffix"; +static const char UNO_NAME_CHAR_STYLE_NAME[] = "CharStyleName"; +static const char UNO_NAME_NUMBERING_TYPE[] = "NumberingType"; +static const char UNO_NAME_BULLET_CHAR[] = "BulletChar"; + +static const sal_Unicode CHAR_CLOSED_DOT[] = u"\u2022"; +static const char CHAR_EMPTY_DOT[] = "o"; +static const sal_Unicode CHAR_SQUARE[] = u"\u2540"; +static const sal_Unicode CHAR_STAR_SYMBOL[] = u"\u272A"; +static const sal_Unicode CHAR_FOUR_DIAMONDS[] = u"\u2756"; +static const sal_Unicode CHAR_DIAMOND[] = u"\u2726"; +static const sal_Unicode CHAR_ARROW[] = u"\u27A2"; +static const sal_Unicode CHAR_CHECK_MARK[] = u"\u2713"; + +SwVbaListHelper::SwVbaListHelper( const css::uno::Reference< css::text::XTextDocument >& xTextDoc, sal_Int32 nGalleryType, sal_Int32 nTemplateType ) : mxTextDocument( xTextDoc ), mnGalleryType( nGalleryType ), mnTemplateType( nTemplateType ) +{ + Init(); +} + +void SwVbaListHelper::Init() +{ + // set the numbering style name + switch( mnGalleryType ) + { + case word::WdListGalleryType::wdBulletGallery: + { + msStyleName = "WdBullet"; + break; + } + case word::WdListGalleryType::wdNumberGallery: + { + msStyleName = "WdNumber"; + break; + } + case word::WdListGalleryType::wdOutlineNumberGallery: + { + msStyleName = "WdOutlineNumber"; + break; + } + default: + { + throw uno::RuntimeException(); + } + } + msStyleName += OUString::number( mnTemplateType ); + + // get the numbering style + uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( mxTextDocument, uno::UNO_QUERY_THROW ); + mxStyleFamily.set( xStyleSupplier->getStyleFamilies()->getByName("NumberingStyles"), uno::UNO_QUERY_THROW ); + SAL_INFO("sw.vba", "numbering style name: " << msStyleName ); + if( mxStyleFamily->hasByName( msStyleName ) ) + { + mxStyleProps.set( mxStyleFamily->getByName( msStyleName ), uno::UNO_QUERY_THROW ); + mxNumberingRules.set( mxStyleProps->getPropertyValue("NumberingRules"), uno::UNO_QUERY_THROW ); + } + else + { + // create new numbering style + uno::Reference< lang::XMultiServiceFactory > xDocMSF( mxTextDocument, uno::UNO_QUERY_THROW ); + mxStyleProps.set( xDocMSF->createInstance("com.sun.star.style.NumberingStyle"), uno::UNO_QUERY_THROW ); + // insert this style into style family, or the property NumberingRules doesn't exist. + mxStyleFamily->insertByName( msStyleName, uno::makeAny( mxStyleProps ) ); + mxStyleProps->getPropertyValue("NumberingRules") >>= mxNumberingRules; + + CreateListTemplate(); + + mxStyleProps->setPropertyValue("NumberingRules", uno::makeAny( mxNumberingRules ) ); + } +} + +void SwVbaListHelper::CreateListTemplate() +{ + switch( mnGalleryType ) + { + case word::WdListGalleryType::wdBulletGallery: + { + CreateBulletListTemplate(); + break; + } + case word::WdListGalleryType::wdNumberGallery: + { + CreateNumberListTemplate(); + break; + } + case word::WdListGalleryType::wdOutlineNumberGallery: + { + CreateOutlineNumberListTemplate(); + break; + } + default: + { + throw uno::RuntimeException(); + } + } +} + +void SwVbaListHelper::CreateBulletListTemplate() +{ + // there is only 1 level for each bullet list in MSWord + sal_Int32 nLevel = 0; + uno::Sequence< beans::PropertyValue > aPropertyValues; + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_CHAR_STYLE_NAME, uno::makeAny( OUString( "Bullet Symbols" ) ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( sal_Int16(style::NumberingType::CHAR_SPECIAL) ) ); + + OUString aBulletChar; + switch( mnTemplateType ) + { + case 1: + { + aBulletChar = CHAR_CLOSED_DOT; + break; + } + case 2: + { + aBulletChar = CHAR_EMPTY_DOT; + break; + } + case 3: + { + aBulletChar = CHAR_SQUARE; + break; + } + case 4: + { + aBulletChar = CHAR_STAR_SYMBOL; + break; + } + case 5: + { + aBulletChar = CHAR_FOUR_DIAMONDS; + break; + } + case 6: + { + aBulletChar = CHAR_ARROW; + break; + } + case 7: + { + aBulletChar = CHAR_CHECK_MARK; + break; + } + default: + { + // we only support 7 types template now + throw css::uno::RuntimeException(); + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_BULLET_CHAR, uno::makeAny( aBulletChar ) ); + + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); +} + +void SwVbaListHelper::CreateNumberListTemplate() +{ + // there is only 1 level for each bullet list in MSWord + sal_Int32 nLevel = 0; + uno::Sequence< beans::PropertyValue > aPropertyValues; + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + + sal_Int16 nNumberingType = 0; + OUString sSuffix; + switch( mnTemplateType ) + { + case 1: + { + nNumberingType = style::NumberingType::ARABIC; + sSuffix = "."; + break; + } + case 2: + { + nNumberingType = style::NumberingType::ARABIC; + sSuffix = ")"; + break; + } + case 3: + { + nNumberingType = style::NumberingType::ROMAN_UPPER; + sSuffix = "."; + break; + } + case 4: + { + nNumberingType = style::NumberingType::CHARS_UPPER_LETTER; + sSuffix = "."; + break; + } + case 5: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sSuffix = ")"; + break; + } + case 6: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sSuffix = "."; + break; + } + case 7: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sSuffix = "."; + break; + } + default: + { + // we only support 7 types template now + throw css::uno::RuntimeException(); + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( nNumberingType ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_SUFFIX, uno::makeAny( sSuffix ) ); + + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); +} + +void SwVbaListHelper::CreateOutlineNumberListTemplate() +{ + switch( mnTemplateType ) + { + case 1: + { + CreateOutlineNumberForType1(); + break; + } + case 2: + { + CreateOutlineNumberForType2(); + break; + } + case 3: + { + CreateOutlineNumberForType3(); + break; + } + case 4: + { + CreateOutlineNumberForType4(); + break; + } + case 5: + { + CreateOutlineNumberForType5(); + break; + } + case 6: + { + CreateOutlineNumberForType6(); + break; + } + case 7: + { + CreateOutlineNumberForType7(); + break; + } + default: + { + // we only support 7 types template now + throw css::uno::RuntimeException(); + } + } +} + +void SwVbaListHelper::CreateOutlineNumberForType1() +{ + sal_Int16 nNumberingType = 0; + OUString sPrefix; + OUString sSuffix; + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + switch( nLevel ) + { + case 0: + case 1: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 2: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 3: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 4: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 5: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 6: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 7: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 8: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix.clear(); + sSuffix = "."; + break; + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( nNumberingType ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PREFIX, uno::makeAny( sPrefix ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_SUFFIX, uno::makeAny( sSuffix ) ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType2() +{ + sal_Int16 nParentNumbering = 0; + OUString sSuffix( '.' ); + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( sal_Int16(style::NumberingType::ARABIC) ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_SUFFIX, uno::makeAny( sSuffix ) ); + if( nLevel != 0 ) + { + nParentNumbering = sal_Int16( nLevel - 1 ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PARENT_NUMBERING, uno::makeAny( nParentNumbering ) ); + } + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType3() +{ + OUString aBulletChar; + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( sal_Int16(style::NumberingType::CHAR_SPECIAL) ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_CHAR_STYLE_NAME, uno::makeAny( OUString("Bullet Symbols") ) ); + switch( nLevel ) + { + case 0: + { + aBulletChar = CHAR_FOUR_DIAMONDS; + break; + } + case 1: + case 5: + { + aBulletChar = CHAR_ARROW; + break; + } + case 2: + case 6: + { + aBulletChar = CHAR_SQUARE; + break; + } + case 3: + case 7: + { + aBulletChar = CHAR_CLOSED_DOT; + break; + } + case 4: + case 8: + { + aBulletChar = CHAR_DIAMOND; + break; + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_BULLET_CHAR, uno::makeAny( aBulletChar ) ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType4() +{ + sal_Int16 nNumberingType = 0; + OUString sPrefix; + OUString sSuffix; + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + switch( nLevel ) + { + case 0: + { + nNumberingType = style::NumberingType::ROMAN_UPPER; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 1: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix.clear(); + sSuffix = "."; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PARENT_NUMBERING, uno::makeAny( sal_Int16(0) ) ); + break; + } + case 2: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 3: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 4: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 5: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 6: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 7: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 8: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix.clear(); + sSuffix = "."; + break; + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( nNumberingType ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PREFIX, uno::makeAny( sPrefix ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_SUFFIX, uno::makeAny( sSuffix ) ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType5() +{ + sal_Int16 nParentNumbering = 0; + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( sal_Int16(style::NumberingType::ARABIC) ) ); + if( nLevel != 0 ) + { + nParentNumbering = sal_Int16( nLevel - 1 ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PARENT_NUMBERING, uno::makeAny( nParentNumbering ) ); + } + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType6() +{ + sal_Int16 nNumberingType = 0; + OUString sPrefix; + OUString sSuffix; + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + switch( nLevel ) + { + case 0: + { + nNumberingType = style::NumberingType::ROMAN_UPPER; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 1: + { + nNumberingType = style::NumberingType::CHARS_UPPER_LETTER; + sPrefix.clear(); + sSuffix = "."; + break; + } + case 2: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 3: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix.clear(); + sSuffix = ")"; + break; + } + case 4: + { + nNumberingType = style::NumberingType::ARABIC; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 5: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 6: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix = "("; + sSuffix = ")"; + break; + } + case 7: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER; + sPrefix = "("; + sSuffix = "."; + break; + } + case 8: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + sPrefix = "("; + sSuffix = "."; + break; + } + } + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( nNumberingType ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PREFIX, uno::makeAny( sPrefix ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_SUFFIX, uno::makeAny( sSuffix ) ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +void SwVbaListHelper::CreateOutlineNumberForType7() +{ + uno::Sequence< beans::PropertyValue > aPropertyValues; + + for( sal_Int32 nLevel = 0; nLevel < LIST_LEVEL_COUNT; nLevel++ ) + { + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_NUMBERING_TYPE, uno::makeAny( sal_Int16(style::NumberingType::ARABIC) ) ); + setOrAppendPropertyValue( aPropertyValues, UNO_NAME_PREFIX, uno::makeAny( OUString("Chapter ") ) ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + } +} + +uno::Any SwVbaListHelper::getPropertyValueWithNameAndLevel( sal_Int32 nLevel, const OUString& sName ) +{ + uno::Sequence< beans::PropertyValue > aPropertyValues; + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + return getPropertyValue( aPropertyValues, sName ); +} + +void SwVbaListHelper::setPropertyValueWithNameAndLevel( sal_Int32 nLevel, const OUString& sName, const css::uno::Any& aValue ) +{ + uno::Sequence< beans::PropertyValue > aPropertyValues; + mxNumberingRules->getByIndex( nLevel ) >>= aPropertyValues; + setOrAppendPropertyValue( aPropertyValues, sName, aValue ); + mxNumberingRules->replaceByIndex( nLevel, uno::makeAny( aPropertyValues ) ); + mxStyleProps->setPropertyValue("NumberingRules", uno::makeAny( mxNumberingRules ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisthelper.hxx b/sw/source/ui/vba/vbalisthelper.hxx new file mode 100644 index 000000000..16c6f9d42 --- /dev/null +++ b/sw/source/ui/vba/vbalisthelper.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/. + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBALISTHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTHELPER_HXX + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexReplace.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +#include <memory> + +class SwVbaListHelper; +typedef std::shared_ptr< SwVbaListHelper > SwVbaListHelperRef; + +class SwVbaListHelper +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + css::uno::Reference< css::container::XIndexReplace > mxNumberingRules; + css::uno::Reference< css::container::XNameContainer > mxStyleFamily; + css::uno::Reference< css::beans::XPropertySet > mxStyleProps; + sal_Int32 mnGalleryType; + sal_Int32 mnTemplateType; + OUString msStyleName; + + /// @throws css::uno::RuntimeException + void Init(); + /// @throws css::uno::RuntimeException + void CreateListTemplate(); + /// @throws css::uno::RuntimeException + void CreateBulletListTemplate(); + /// @throws css::uno::RuntimeException + void CreateNumberListTemplate(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberListTemplate(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType1(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType2(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType3(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType4(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType5(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType6(); + /// @throws css::uno::RuntimeException + void CreateOutlineNumberForType7(); + +public: + /// @throws css::uno::RuntimeException + SwVbaListHelper( const css::uno::Reference< css::text::XTextDocument >& xTextDoc, sal_Int32 nGalleryType, sal_Int32 nTemplateType ); + + sal_Int32 getGalleryType() const { return mnGalleryType; } + const css::uno::Reference< css::container::XIndexReplace >& getNumberingRules() const { return mxNumberingRules; } + /// @throws css::uno::RuntimeException + css::uno::Any getPropertyValueWithNameAndLevel( sal_Int32 nLevel, const OUString& sName ); + /// @throws css::uno::RuntimeException + void setPropertyValueWithNameAndLevel( sal_Int32 nLevel, const OUString& sName, const css::uno::Any& aValue ); + +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistlevel.cxx b/sw/source/ui/vba/vbalistlevel.cxx new file mode 100644 index 000000000..07374bd9f --- /dev/null +++ b/sw/source/ui/vba/vbalistlevel.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 "vbalistlevel.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <ooo/vba/word/WdListNumberStyle.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <ooo/vba/word/WdListLevelAlignment.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaListLevel::SwVbaListLevel( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, SwVbaListHelperRef const & pHelper, sal_Int32 nLevel ) : SwVbaListLevel_BASE( rParent, rContext ), pListHelper( pHelper ), mnLevel( nLevel ) +{ +} + +SwVbaListLevel::~SwVbaListLevel() +{ +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getAlignment() +{ + sal_Int16 nAlignment = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "Adjust" ) >>= nAlignment; + switch( nAlignment ) + { + case text::HoriOrientation::LEFT: + { + nAlignment = word::WdListLevelAlignment::wdListLevelAlignLeft; + break; + } + case text::HoriOrientation::RIGHT: + { + nAlignment = word::WdListLevelAlignment::wdListLevelAlignRight; + break; + } + case text::HoriOrientation::CENTER: + { + nAlignment = word::WdListLevelAlignment::wdListLevelAlignCenter; + break; + } + default: + { + throw uno::RuntimeException(); + } + } + return nAlignment; +} + +void SAL_CALL SwVbaListLevel::setAlignment( ::sal_Int32 _alignment ) +{ + sal_Int16 nAlignment = text::HoriOrientation::LEFT; + switch( _alignment ) + { + case word::WdListLevelAlignment::wdListLevelAlignLeft: + { + nAlignment = text::HoriOrientation::LEFT; + break; + } + case word::WdListLevelAlignment::wdListLevelAlignRight: + { + nAlignment = text::HoriOrientation::RIGHT; + break; + } + case word::WdListLevelAlignment::wdListLevelAlignCenter: + { + nAlignment = text::HoriOrientation::CENTER; + break; + } + default: + { + throw uno::RuntimeException(); + } + } + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "Adjust", uno::makeAny( nAlignment ) ); +} + +uno::Reference< ::ooo::vba::word::XFont > SAL_CALL SwVbaListLevel::getFont() +{ + throw uno::RuntimeException("Not implemented" ); +} + +void SAL_CALL SwVbaListLevel::setFont( const uno::Reference< ::ooo::vba::word::XFont >& /*_font*/ ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getIndex() +{ + return mnLevel + 1; +} + +OUString SAL_CALL SwVbaListLevel::getLinkedStyle() +{ + // TODO: + return OUString(); +} + +void SAL_CALL SwVbaListLevel::setLinkedStyle( const OUString& /*_linkedstyle*/ ) +{ + // TODO: +} + +OUString SAL_CALL SwVbaListLevel::getNumberFormat() +{ + // TODO:: + return OUString(); +} + +void SAL_CALL SwVbaListLevel::setNumberFormat( const OUString& /*_numberformat*/ ) +{ + // TODO:: +} + +float SAL_CALL SwVbaListLevel::getNumberPosition() +{ + // indentAt + firstlineindent + sal_Int32 nIndentAt = 0; + sal_Int32 nFirstLineIndent = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "IndentAt" ) >>= nIndentAt; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "FirstLineIndent" ) >>= nFirstLineIndent; + + sal_Int32 nResult = nIndentAt + nFirstLineIndent; + + return static_cast< float >( Millimeter::getInPoints( nResult ) ); +} + +void SAL_CALL SwVbaListLevel::setNumberPosition( float _numberposition ) +{ + sal_Int32 nNumberPosition = Millimeter::getInHundredthsOfOneMillimeter( _numberposition ); + + sal_Int32 nIndentAt = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "IndentAt" ) >>= nIndentAt; + + sal_Int32 nFirstLineIndent = nNumberPosition - nIndentAt; + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "FirstLineIndent", uno::makeAny( nFirstLineIndent ) ); +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getNumberStyle() +{ + sal_Int16 nNumberingType = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "NumberingType" ) >>= nNumberingType; + switch( nNumberingType ) + { + case style::NumberingType::CHAR_SPECIAL: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleBullet; + break; + } + case style::NumberingType::CHARS_UPPER_LETTER: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleUppercaseLetter; + break; + } + case style::NumberingType::CHARS_LOWER_LETTER: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleLowercaseLetter; + break; + } + case style::NumberingType::ROMAN_UPPER: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleUppercaseRoman; + break; + } + case style::NumberingType::ROMAN_LOWER: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleLowercaseRoman; + break; + } + case style::NumberingType::ARABIC: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleArabic; + break; + } + case style::NumberingType::NUMBER_NONE: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleNone; + break; + } + case style::NumberingType::FULLWIDTH_ARABIC: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleArabicFullWidth; + break; + } + case style::NumberingType::CIRCLE_NUMBER: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleNumberInCircle; + break; + } + case style::NumberingType::CHARS_ARABIC: + { + nNumberingType = word::WdListNumberStyle::wdListNumberStyleCardinalText; + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } + return nNumberingType; +} + +void SAL_CALL SwVbaListLevel::setNumberStyle( ::sal_Int32 _numberstyle ) +{ + sal_Int16 nNumberingType = 0; + switch( _numberstyle ) + { + case word::WdListNumberStyle::wdListNumberStyleBullet: + { + nNumberingType = style::NumberingType::CHAR_SPECIAL; + break; + } + case word::WdListNumberStyle::wdListNumberStyleUppercaseLetter: + { + nNumberingType = style::NumberingType::CHARS_UPPER_LETTER_N; + break; + } + case word::WdListNumberStyle::wdListNumberStyleLowercaseLetter: + { + nNumberingType = style::NumberingType::CHARS_LOWER_LETTER_N; + break; + } + case word::WdListNumberStyle::wdListNumberStyleUppercaseRoman: + { + nNumberingType = style::NumberingType::ROMAN_UPPER; + break; + } + case word::WdListNumberStyle::wdListNumberStyleLowercaseRoman: + { + nNumberingType = style::NumberingType::ROMAN_LOWER; + break; + } + case word::WdListNumberStyle::wdListNumberStyleArabic: + { + nNumberingType = style::NumberingType::ARABIC; + break; + } + case word::WdListNumberStyle::wdListNumberStyleNone: + { + nNumberingType = style::NumberingType::NUMBER_NONE; + break; + } + case word::WdListNumberStyle::wdListNumberStyleArabicFullWidth: + { + nNumberingType = style::NumberingType::FULLWIDTH_ARABIC; + break; + } + case word::WdListNumberStyle::wdListNumberStyleNumberInCircle: + { + nNumberingType = style::NumberingType::CIRCLE_NUMBER; + break; + } + case word::WdListNumberStyle::wdListNumberStyleCardinalText: + { + nNumberingType = style::NumberingType::CHARS_ARABIC; + break; + } + case word::WdListNumberStyle::wdListNumberStyleOrdinal: + case word::WdListNumberStyle::wdListNumberStyleOrdinalText: + case word::WdListNumberStyle::wdListNumberStyleKanji: + case word::WdListNumberStyle::wdListNumberStyleKanjiDigit: + case word::WdListNumberStyle::wdListNumberStyleAiueoHalfWidth: + case word::WdListNumberStyle::wdListNumberStyleIrohaHalfWidth: + { + nNumberingType = style::NumberingType::ARABIC; + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } + + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "NumberingType", uno::makeAny( nNumberingType ) ); +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getResetOnHigher() +{ + //seems not support? + return 0; +} + +void SAL_CALL SwVbaListLevel::setResetOnHigher( ::sal_Int32 /*_resetonhigher*/ ) +{ + //seems not support? +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getStartAt() +{ + sal_Int16 nStartWith = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "StartWith" ) >>= nStartWith; + return nStartWith; +} + +void SAL_CALL SwVbaListLevel::setStartAt( ::sal_Int32 _startat ) +{ + sal_Int16 nStartWith = static_cast<sal_Int16>(_startat); + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "StartWith", uno::makeAny( nStartWith ) ); +} + +float SAL_CALL SwVbaListLevel::getTabPosition() +{ + sal_Int32 nTabPosition = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "ListtabStopPosition" ) >>= nTabPosition; + + return static_cast< float >( Millimeter::getInPoints( nTabPosition ) ); +} + +void SAL_CALL SwVbaListLevel::setTabPosition( float _tabposition ) +{ + sal_Int32 nTabPosition = Millimeter::getInHundredthsOfOneMillimeter( _tabposition ); + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "ListtabStopPosition", uno::makeAny( nTabPosition ) ); +} + +float SAL_CALL SwVbaListLevel::getTextPosition() +{ + // indentAt + sal_Int32 nIndentAt = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "IndentAt" ) >>= nIndentAt; + + return static_cast< float >( Millimeter::getInPoints( nIndentAt ) ); +} + +void SAL_CALL SwVbaListLevel::setTextPosition( float _textposition ) +{ + sal_Int32 nIndentAt = 0; + sal_Int32 nFirstLineIndent = 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "IndentAt" ) >>= nIndentAt; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "FirstLineIndent" ) >>= nFirstLineIndent; + + sal_Int32 nAlignedAt = nIndentAt + nFirstLineIndent; + + nIndentAt = Millimeter::getInHundredthsOfOneMillimeter( _textposition ); + nFirstLineIndent = nAlignedAt - nIndentAt; + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "IndentAt", uno::makeAny( nIndentAt ) ); + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "FirstLineIndent", uno::makeAny( nFirstLineIndent ) ); +} + +::sal_Int32 SAL_CALL SwVbaListLevel::getTrailingCharacter() +{ + sal_Int16 nLabelFollowedBy= 0; + pListHelper->getPropertyValueWithNameAndLevel( mnLevel, "LabelFollowedBy" ) >>= nLabelFollowedBy; + + return nLabelFollowedBy; +} + +void SAL_CALL SwVbaListLevel::setTrailingCharacter( ::sal_Int32 _trailingcharacter ) +{ + sal_Int16 nLabelFollowedBy = static_cast<sal_Int16>(_trailingcharacter); + pListHelper->setPropertyValueWithNameAndLevel( mnLevel, "LabelFollowedBy", uno::makeAny( nLabelFollowedBy ) ); +} + +OUString +SwVbaListLevel::getServiceImplName() +{ + return "SwVbaListLevel"; +} + +uno::Sequence< OUString > +SwVbaListLevel::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.ListLevel" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistlevel.hxx b/sw/source/ui/vba/vbalistlevel.hxx new file mode 100644 index 000000000..f149a7109 --- /dev/null +++ b/sw/source/ui/vba/vbalistlevel.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_UI_VBA_VBALISTLEVEL_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTLEVEL_HXX + +#include <ooo/vba/word/XListLevel.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include "vbalisthelper.hxx" + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XListLevel > SwVbaListLevel_BASE; + +class SwVbaListLevel : public SwVbaListLevel_BASE +{ +private: + SwVbaListHelperRef pListHelper; + sal_Int32 mnLevel; + +public: + /// @throws css::uno::RuntimeException + SwVbaListLevel( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, SwVbaListHelperRef const & pHelper, sal_Int32 nLevel ); + virtual ~SwVbaListLevel() override; + + // Attributes + virtual ::sal_Int32 SAL_CALL getAlignment() override; + virtual void SAL_CALL setAlignment( ::sal_Int32 _alignment ) override; + virtual css::uno::Reference< ::ooo::vba::word::XFont > SAL_CALL getFont() override; + virtual void SAL_CALL setFont( const css::uno::Reference< ::ooo::vba::word::XFont >& _font ) override; + virtual ::sal_Int32 SAL_CALL getIndex() override; + virtual OUString SAL_CALL getLinkedStyle() override; + virtual void SAL_CALL setLinkedStyle( const OUString& _linkedstyle ) override; + virtual OUString SAL_CALL getNumberFormat() override; + virtual void SAL_CALL setNumberFormat( const OUString& _numberformat ) override; + virtual float SAL_CALL getNumberPosition() override; + virtual void SAL_CALL setNumberPosition( float _numberposition ) override; + virtual ::sal_Int32 SAL_CALL getNumberStyle() override; + virtual void SAL_CALL setNumberStyle( ::sal_Int32 _numberstyle ) override; + virtual ::sal_Int32 SAL_CALL getResetOnHigher() override; + virtual void SAL_CALL setResetOnHigher( ::sal_Int32 _resetonhigher ) override; + virtual ::sal_Int32 SAL_CALL getStartAt() override; + virtual void SAL_CALL setStartAt( ::sal_Int32 _startat ) override; + virtual float SAL_CALL getTabPosition() override; + virtual void SAL_CALL setTabPosition( float _tabposition ) override; + virtual float SAL_CALL getTextPosition() override; + virtual void SAL_CALL setTextPosition( float _textposition ) override; + virtual ::sal_Int32 SAL_CALL getTrailingCharacter() override; + virtual void SAL_CALL setTrailingCharacter( ::sal_Int32 _trailingcharacter ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTLEVEL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistlevels.cxx b/sw/source/ui/vba/vbalistlevels.cxx new file mode 100644 index 000000000..a83e6fe97 --- /dev/null +++ b/sw/source/ui/vba/vbalistlevels.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbalistlevels.hxx" +#include "vbalistlevel.hxx" +#include <ooo/vba/word/WdListGalleryType.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class ListLevelsEnumWrapper : public EnumerationHelper_BASE +{ + SwVbaListLevels* pListLevels; + sal_Int32 nIndex; +public: + explicit ListLevelsEnumWrapper( SwVbaListLevels* pLevels ) : pListLevels( pLevels ), nIndex( 1 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex <= pListLevels->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex <= pListLevels->getCount() ) + return pListLevels->Item( uno::makeAny( nIndex++ ), uno::Any() ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaListLevels::SwVbaListLevels( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, SwVbaListHelperRef const & pHelper ) : SwVbaListLevels_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >() ), pListHelper( pHelper ) +{ +} + +::sal_Int32 SAL_CALL SwVbaListLevels::getCount() +{ + sal_Int32 nGalleryType = pListHelper->getGalleryType(); + if( nGalleryType == word::WdListGalleryType::wdBulletGallery + || nGalleryType == word::WdListGalleryType::wdNumberGallery ) + return 1; + else if( nGalleryType == word::WdListGalleryType::wdOutlineNumberGallery ) + return 9; + return 0; +} + +uno::Any SAL_CALL SwVbaListLevels::Item( const uno::Any& Index1, const uno::Any& /*not processed in this base class*/ ) +{ + sal_Int32 nIndex = 0; + if( !( Index1 >>= nIndex ) ) + throw uno::RuntimeException(); + if( nIndex <=0 || nIndex > getCount() ) + throw uno::RuntimeException("Index out of bounds" ); + + return uno::makeAny( uno::Reference< word::XListLevel >( new SwVbaListLevel( this, mxContext, pListHelper, nIndex - 1 ) ) ); +} + +// XEnumerationAccess +uno::Type +SwVbaListLevels::getElementType() +{ + return cppu::UnoType<word::XListLevel>::get(); +} + +uno::Reference< container::XEnumeration > +SwVbaListLevels::createEnumeration() +{ + return new ListLevelsEnumWrapper( this ); +} + +uno::Any +SwVbaListLevels::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaListLevels::getServiceImplName() +{ + return "SwVbaListLevels"; +} + +css::uno::Sequence<OUString> +SwVbaListLevels::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.ListLevels" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalistlevels.hxx b/sw/source/ui/vba/vbalistlevels.hxx new file mode 100644 index 000000000..ce5674fc7 --- /dev/null +++ b/sw/source/ui/vba/vbalistlevels.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_UI_VBA_VBALISTLEVELS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTLEVELS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XListLevels.hpp> +#include "vbalisthelper.hxx" + +typedef CollTestImplHelper< ooo::vba::word::XListLevels > SwVbaListLevels_BASE; + +class SwVbaListLevels : public SwVbaListLevels_BASE +{ +private: + SwVbaListHelperRef pListHelper; + +public: + /// @throws css::uno::RuntimeException + SwVbaListLevels( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, SwVbaListHelperRef const & pHelper ); + + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& /*not processed in this base class*/ ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaListLevels_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTLEVELS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisttemplate.cxx b/sw/source/ui/vba/vbalisttemplate.cxx new file mode 100644 index 000000000..43f3ac129 --- /dev/null +++ b/sw/source/ui/vba/vbalisttemplate.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 "vbalisttemplate.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbalistlevels.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaListTemplate::SwVbaListTemplate( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& xTextDoc, sal_Int32 nGalleryType, sal_Int32 nTemplateType ) : SwVbaListTemplate_BASE( rParent, rContext ) +{ + pListHelper = std::make_shared<SwVbaListHelper>( xTextDoc, nGalleryType, nTemplateType ); +} + +SwVbaListTemplate::~SwVbaListTemplate() +{ +} + +uno::Any SAL_CALL +SwVbaListTemplate::ListLevels( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaListLevels( mxParent, mxContext, pListHelper ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +void SwVbaListTemplate::applyListTemplate( uno::Reference< beans::XPropertySet > const & xProps ) +{ + uno::Reference< container::XIndexReplace > xNumberingRules = pListHelper->getNumberingRules(); + xProps->setPropertyValue("NumberingRules", uno::makeAny( xNumberingRules ) ); +} + +OUString +SwVbaListTemplate::getServiceImplName() +{ + return "SwVbaListTemplate"; +} + +uno::Sequence< OUString > +SwVbaListTemplate::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.ListTemplate" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisttemplate.hxx b/sw/source/ui/vba/vbalisttemplate.hxx new file mode 100644 index 000000000..902695cb2 --- /dev/null +++ b/sw/source/ui/vba/vbalisttemplate.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_UI_VBA_VBALISTTEMPLATE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTTEMPLATE_HXX + +#include <ooo/vba/word/XListTemplate.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextDocument.hpp> +#include "vbalisthelper.hxx" + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XListTemplate > SwVbaListTemplate_BASE; + +class SwVbaListTemplate : public SwVbaListTemplate_BASE +{ +private: + SwVbaListHelperRef pListHelper; + +public: + /// @throws css::uno::RuntimeException + SwVbaListTemplate( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& xTextDoc, sal_Int32 nGalleryType, sal_Int32 nTemplateType ); + virtual ~SwVbaListTemplate() override; + + /// @throws css::uno::RuntimeException + void applyListTemplate( css::uno::Reference< css::beans::XPropertySet > const & xProps ); + + // Methods + virtual css::uno::Any SAL_CALL ListLevels( const css::uno::Any& index ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTTEMPLATE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisttemplates.cxx b/sw/source/ui/vba/vbalisttemplates.cxx new file mode 100644 index 000000000..ffcef424b --- /dev/null +++ b/sw/source/ui/vba/vbalisttemplates.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 "vbalisttemplates.hxx" +#include "vbalisttemplate.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class ListTemplatesEnumWrapper : public EnumerationHelper_BASE +{ + SwVbaListTemplates* pListTemplates; + sal_Int32 nIndex; +public: + explicit ListTemplatesEnumWrapper( SwVbaListTemplates* pTemplates ) : pListTemplates( pTemplates ), nIndex( 1 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex <= pListTemplates->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex <= pListTemplates->getCount() ) + return pListTemplates->Item( uno::makeAny( nIndex++ ), uno::Any() ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaListTemplates::SwVbaListTemplates( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextDocument >& xTextDoc, sal_Int32 nType ) : SwVbaListTemplates_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >() ), mxTextDocument( xTextDoc ), mnGalleryType( nType ) +{ +} + +::sal_Int32 SAL_CALL SwVbaListTemplates::getCount() +{ + // 3 types of list( bullet, numbered and outline ) + return 7; +} + +uno::Any SAL_CALL SwVbaListTemplates::Item( const uno::Any& Index1, const uno::Any& /*not processed in this base class*/ ) +{ + sal_Int32 nIndex = 0; + if( !( Index1 >>= nIndex ) ) + throw uno::RuntimeException(); + if( nIndex <=0 || nIndex > getCount() ) + throw uno::RuntimeException("Index out of bounds" ); + + return uno::makeAny( uno::Reference< word::XListTemplate >( new SwVbaListTemplate( this, mxContext, mxTextDocument, mnGalleryType, nIndex ) ) ); +} + +// XEnumerationAccess +uno::Type +SwVbaListTemplates::getElementType() +{ + return cppu::UnoType<word::XListTemplate>::get(); +} + +uno::Reference< container::XEnumeration > +SwVbaListTemplates::createEnumeration() +{ + return new ListTemplatesEnumWrapper( this ); +} + +uno::Any +SwVbaListTemplates::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaListTemplates::getServiceImplName() +{ + return "SwVbaListTemplates"; +} + +css::uno::Sequence<OUString> +SwVbaListTemplates::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.ListTemplates" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbalisttemplates.hxx b/sw/source/ui/vba/vbalisttemplates.hxx new file mode 100644 index 000000000..d9d4e9d7f --- /dev/null +++ b/sw/source/ui/vba/vbalisttemplates.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_UI_VBA_VBALISTTEMPLATES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBALISTTEMPLATES_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XListTemplates.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XListTemplates > SwVbaListTemplates_BASE; + +class SwVbaListTemplates : public SwVbaListTemplates_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + sal_Int32 mnGalleryType; + +public: + /// @throws css::uno::RuntimeException + SwVbaListTemplates( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextDocument >& xTextDoc, sal_Int32 nType ); + + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& /*not processed in this base class*/ ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaListTemplates_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBALISTTEMPLATES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbamailmerge.cxx b/sw/source/ui/vba/vbamailmerge.cxx new file mode 100644 index 000000000..479f5dfd7 --- /dev/null +++ b/sw/source/ui/vba/vbamailmerge.cxx @@ -0,0 +1,56 @@ +/* -*- 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 "vbamailmerge.hxx" + +#include <ooo/vba/word/WdMailMergeMainDocType.hpp> + +SwVbaMailMerge::SwVbaMailMerge(const css::uno::Reference<ooo::vba::XHelperInterface>& xParent, + const css::uno::Reference<css::uno::XComponentContext>& xContext) + : SwVbaMailMerge_BASE(xParent, xContext) + , m_nMainDocType(ooo::vba::word::WdMailMergeMainDocType::wdNotAMergeDocument) +{ +} + +SwVbaMailMerge::~SwVbaMailMerge() {} + +rtl::Reference<SwVbaMailMerge> const& +SwVbaMailMerge::get(const css::uno::Reference<ooo::vba::XHelperInterface>& xParent, + const css::uno::Reference<css::uno::XComponentContext>& xContext) +{ + static rtl::Reference<SwVbaMailMerge> xInstance(new SwVbaMailMerge(xParent, xContext)); + + return xInstance; +} + +sal_Int32 SAL_CALL SwVbaMailMerge::getMainDocumentType() { return m_nMainDocType; } + +void SAL_CALL SwVbaMailMerge::setMainDocumentType(sal_Int32 _maindocumenttype) +{ + m_nMainDocType = _maindocumenttype; +} + +// Completely dummy, no-op. +void SAL_CALL SwVbaMailMerge::OpenDataSource( + const OUString&, const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, + const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, + const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, + const css::uno::Any&, const css::uno::Any&, const css::uno::Any&, const css::uno::Any&) +{ +} + +OUString SwVbaMailMerge::getServiceImplName() { return "SwVbaMailMerge"; } + +css::uno::Sequence<OUString> SwVbaMailMerge::getServiceNames() +{ + static css::uno::Sequence<OUString> const aServiceNames{ "ooo.vba.word.MailMerge" }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbamailmerge.hxx b/sw/source/ui/vba/vbamailmerge.hxx new file mode 100644 index 000000000..bfc28667d --- /dev/null +++ b/sw/source/ui/vba/vbamailmerge.hxx @@ -0,0 +1,55 @@ +/* -*- 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_UI_VBA_VBAMAILMERGE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAMAILMERGE_HXX + +#include <ooo/vba/word/XMailMerge.hpp> +#include <rtl/ref.hxx> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl<ooo::vba::word::XMailMerge> SwVbaMailMerge_BASE; + +// Singleton class. Get the single instance using the get() method. + +class SwVbaMailMerge : public SwVbaMailMerge_BASE +{ + sal_Int32 m_nMainDocType; + + SwVbaMailMerge(const css::uno::Reference<ooo::vba::XHelperInterface>& xParent, + const css::uno::Reference<css::uno::XComponentContext>& xContext); + +public: + virtual ~SwVbaMailMerge() override; + + static rtl::Reference<SwVbaMailMerge> const& + get(const css::uno::Reference<ooo::vba::XHelperInterface>& xParent, + const css::uno::Reference<css::uno::XComponentContext>& xContext); + + // XMailMerge + virtual sal_Int32 SAL_CALL getMainDocumentType() override; + virtual void SAL_CALL setMainDocumentType(sal_Int32 _maindocumenttype) override; + + virtual void SAL_CALL + OpenDataSource(const OUString& Name, const css::uno::Any& Format, + const css::uno::Any& ConfirmConversions, const css::uno::Any& ReadOnly, + const css::uno::Any& LinkToSource, const css::uno::Any& AddToRecentFiles, + const css::uno::Any& PasswordDocument, const css::uno::Any& PasswordTemplate, + const css::uno::Any& Revert, const css::uno::Any& WritePasswordDocument, + const css::uno::Any& WritePasswordTemplate, const css::uno::Any& Connection, + const css::uno::Any& SQLStatement, const css::uno::Any& SQLStatement1, + const css::uno::Any& OpenExclusive, const css::uno::Any& SubType) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAMAILMERGE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaoptions.cxx b/sw/source/ui/vba/vbaoptions.cxx new file mode 100644 index 000000000..7fd79bd6c --- /dev/null +++ b/sw/source/ui/vba/vbaoptions.cxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaoptions.hxx" +#include <vbahelper/vbahelper.hxx> +#include <ooo/vba/word/WdDefaultFilePath.hpp> +#include <ooo/vba/word/WdLineStyle.hpp> +#include <ooo/vba/word/WdLineWidth.hpp> +#include <ooo/vba/word/WdColorIndex.hpp> +#include <com/sun/star/util/thePathSettings.hpp> +#include <comphelper/processfactory.hxx> +#include <basic/sberrors.hxx> +#include <osl/file.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaOptions::SwVbaOptions( uno::Reference<uno::XComponentContext > const & xContext ) : SwVbaOptions_BASE( uno::Reference< XHelperInterface >(), xContext ) +{ +} + +SwVbaOptions::~SwVbaOptions() +{ +} + +uno::Any SAL_CALL +SwVbaOptions::DefaultFilePath( sal_Int32 _path ) +{ + switch( _path ) + { + case word::WdDefaultFilePath::wdDocumentsPath: + { + msDefaultFilePath = "Work"; + break; + } + case word::WdDefaultFilePath::wdPicturesPath: + { + msDefaultFilePath = "Gallery"; + break; + } + case word::WdDefaultFilePath::wdUserTemplatesPath: + case word::WdDefaultFilePath::wdWorkgroupTemplatesPath: + { + msDefaultFilePath = "Template"; + break; + } + case word::WdDefaultFilePath::wdStartupPath: + { + msDefaultFilePath = "Addin"; + break; + } + case word::WdDefaultFilePath::wdUserOptionsPath: + { + msDefaultFilePath = "UserConfig"; + break; + } + case word::WdDefaultFilePath::wdToolsPath: + case word::WdDefaultFilePath::wdProgramPath: + { + msDefaultFilePath = "Module"; + break; + } + case word::WdDefaultFilePath::wdTempFilePath: + { + msDefaultFilePath = "Temp"; + break; + } + default: + { + DebugHelper::basicexception( ERRCODE_BASIC_NOT_IMPLEMENTED, OUString() ); + break; + } + } + return uno::makeAny( uno::Reference< XPropValue > ( new ScVbaPropValue( this ) ) ); +} + +void SwVbaOptions::setValueEvent( const uno::Any& value ) +{ + OUString sNewPath; + value >>= sNewPath; + OUString sNewPathUrl; + ::osl::File::getFileURLFromSystemPath( sNewPath, sNewPathUrl ); + uno::Reference< util::XPathSettings > xPathSettings = util::thePathSettings::get( comphelper::getProcessComponentContext() ); + OUString sOldPathUrl; + xPathSettings->getPropertyValue( msDefaultFilePath ) >>= sOldPathUrl; + // path could be a multipath, Microsoft doesn't support this feature in Word currently + // only the last path is from interest. + sal_Int32 nIndex = sOldPathUrl.lastIndexOf( ';' ); + if( nIndex != -1 ) + { + sNewPathUrl = sOldPathUrl.copy( 0, nIndex + 1 ).concat( sNewPathUrl ); + } + xPathSettings->setPropertyValue( msDefaultFilePath, uno::makeAny( sNewPathUrl ) ); +} + +uno::Any SwVbaOptions::getValueEvent() +{ + uno::Reference< util::XPathSettings > xPathSettings = util::thePathSettings::get( comphelper::getProcessComponentContext() ); + OUString sPathUrl; + xPathSettings->getPropertyValue( msDefaultFilePath ) >>= sPathUrl; + // path could be a multipath, Microsoft doesn't support this feature in Word currently + // only the last path is from interest. + sal_Int32 nIndex = sPathUrl.lastIndexOf( ';' ); + if( nIndex != -1 ) + { + sPathUrl = sPathUrl.copy( nIndex + 1 ); + } + OUString sPath; + ::osl::File::getSystemPathFromFileURL( sPathUrl, sPath ); + return uno::makeAny( sPath ); +} + +sal_Int32 SAL_CALL SwVbaOptions::getDefaultBorderLineStyle() +{ + return word::WdLineStyle::wdLineStyleSingle; +} + +void SAL_CALL SwVbaOptions::setDefaultBorderLineStyle( ::sal_Int32 /*_defaultborderlinestyle*/ ) +{ + // not support in Writer +} + +sal_Int32 SAL_CALL SwVbaOptions::getDefaultBorderLineWidth() +{ + return word::WdLineWidth::wdLineWidth050pt; +} + +void SAL_CALL SwVbaOptions::setDefaultBorderLineWidth( ::sal_Int32 /*_defaultborderlinewidth*/ ) +{ + // not support in Writer +} + +sal_Int32 SAL_CALL SwVbaOptions::getDefaultBorderColorIndex() +{ + return word::WdColorIndex::wdAuto; +} + +void SAL_CALL SwVbaOptions::setDefaultBorderColorIndex( ::sal_Int32 /*_defaultbordercolorindex*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getReplaceSelection() +{ + return true; +} + +void SAL_CALL SwVbaOptions::setReplaceSelection( sal_Bool /*_replaceselection*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getMapPaperSize() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setMapPaperSize( sal_Bool /*_mappapersize*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatAsYouTypeApplyHeadings() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatAsYouTypeApplyHeadings( sal_Bool /*_autoformatasyoutypeapplyheadings*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatAsYouTypeApplyBulletedLists() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatAsYouTypeApplyBulletedLists( sal_Bool /*_autoformatasyoutypeapplybulletedlists*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatAsYouTypeApplyNumberedLists() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatAsYouTypeApplyNumberedLists( sal_Bool /*_autoformatasyoutypeapplynumberedlists*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatAsYouTypeFormatListItemBeginning() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatAsYouTypeFormatListItemBeginning( sal_Bool /*_autoformatasyoutypeformatlistitembeginning*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatAsYouTypeDefineStyles() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatAsYouTypeDefineStyles( sal_Bool /*_autoformatasyoutypedefinestyles*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatApplyHeadings() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatApplyHeadings( sal_Bool /*_autoformatapplyheadings*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatApplyLists() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatApplyLists( sal_Bool /*_autoformatapplylists*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaOptions::getAutoFormatApplyBulletedLists() +{ + return false; +} + +void SAL_CALL SwVbaOptions::setAutoFormatApplyBulletedLists( sal_Bool /*_autoformatapplybulletedlists*/ ) +{ + // not support in Writer +} + +OUString +SwVbaOptions::getServiceImplName() +{ + return "SwVbaOptions"; +} + +uno::Sequence< OUString > +SwVbaOptions::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Options" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaoptions.hxx b/sw/source/ui/vba/vbaoptions.hxx new file mode 100644 index 000000000..92994d558 --- /dev/null +++ b/sw/source/ui/vba/vbaoptions.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_UI_VBA_VBAOPTIONS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAOPTIONS_HXX + +#include <ooo/vba/word/XOptions.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <vbahelper/vbapropvalue.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XOptions > SwVbaOptions_BASE; + +class SwVbaOptions : public SwVbaOptions_BASE, + public PropListener +{ +private: + OUString msDefaultFilePath; +public: + explicit SwVbaOptions( css::uno::Reference< css::uno::XComponentContext > const & m_xContext ); + virtual ~SwVbaOptions() override; + + // Attributes + virtual ::sal_Int32 SAL_CALL getDefaultBorderLineStyle() override; + virtual void SAL_CALL setDefaultBorderLineStyle( ::sal_Int32 _defaultborderlinestyle ) override; + virtual ::sal_Int32 SAL_CALL getDefaultBorderLineWidth() override; + virtual void SAL_CALL setDefaultBorderLineWidth( ::sal_Int32 _defaultborderlinewidth ) override; + virtual ::sal_Int32 SAL_CALL getDefaultBorderColorIndex() override; + virtual void SAL_CALL setDefaultBorderColorIndex( ::sal_Int32 _defaultbordercolorindex ) override; + virtual sal_Bool SAL_CALL getReplaceSelection() override; + virtual void SAL_CALL setReplaceSelection( sal_Bool _replaceselection ) override; + virtual sal_Bool SAL_CALL getMapPaperSize() override; + virtual void SAL_CALL setMapPaperSize( sal_Bool _mappapersize ) override; + virtual sal_Bool SAL_CALL getAutoFormatAsYouTypeApplyHeadings() override; + virtual void SAL_CALL setAutoFormatAsYouTypeApplyHeadings( sal_Bool _autoformatasyoutypeapplyheadings ) override; + virtual sal_Bool SAL_CALL getAutoFormatAsYouTypeApplyBulletedLists() override; + virtual void SAL_CALL setAutoFormatAsYouTypeApplyBulletedLists( sal_Bool _autoformatasyoutypeapplybulletedlists ) override; + virtual sal_Bool SAL_CALL getAutoFormatAsYouTypeApplyNumberedLists() override; + virtual void SAL_CALL setAutoFormatAsYouTypeApplyNumberedLists( sal_Bool _autoformatasyoutypeapplynumberedlists ) override; + virtual sal_Bool SAL_CALL getAutoFormatAsYouTypeFormatListItemBeginning() override; + virtual void SAL_CALL setAutoFormatAsYouTypeFormatListItemBeginning( sal_Bool _autoformatasyoutypeformatlistitembeginning ) override; + virtual sal_Bool SAL_CALL getAutoFormatAsYouTypeDefineStyles() override; + virtual void SAL_CALL setAutoFormatAsYouTypeDefineStyles( sal_Bool _autoformatasyoutypedefinestyles ) override; + virtual sal_Bool SAL_CALL getAutoFormatApplyHeadings() override; + virtual void SAL_CALL setAutoFormatApplyHeadings( sal_Bool _autoformatapplyheadings ) override; + virtual sal_Bool SAL_CALL getAutoFormatApplyLists() override; + virtual void SAL_CALL setAutoFormatApplyLists( sal_Bool _autoformatapplylists ) override; + virtual sal_Bool SAL_CALL getAutoFormatApplyBulletedLists() override; + virtual void SAL_CALL setAutoFormatApplyBulletedLists( sal_Bool _autoformatapplybulletedlists ) override; + + // Methods + virtual css::uno::Any SAL_CALL DefaultFilePath( sal_Int32 _path ) override; + + //PropListener + virtual void setValueEvent( const css::uno::Any& value ) override; + virtual css::uno::Any getValueEvent() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAOPTIONS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapagesetup.cxx b/sw/source/ui/vba/vbapagesetup.cxx new file mode 100644 index 000000000..45e4ca874 --- /dev/null +++ b/sw/source/ui/vba/vbapagesetup.cxx @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbapagesetup.hxx" +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <ooo/vba/word/WdSectionStart.hpp> +#include <ooo/vba/word/WdOrientation.hpp> +#include "wordvbahelper.hxx" + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +SwVbaPageSetup::SwVbaPageSetup(const uno::Reference< XHelperInterface >& xParent, + const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< frame::XModel >& xModel, + const uno::Reference< beans::XPropertySet >& xProps ): + SwVbaPageSetup_BASE( xParent, xContext ) +{ + mxModel.set( xModel, uno::UNO_SET_THROW ); + mxPageProps.set( xProps, uno::UNO_SET_THROW ); + mnOrientPortrait = word::WdOrientation::wdOrientPortrait; + mnOrientLandscape = word::WdOrientation::wdOrientLandscape; +} + +double SAL_CALL SwVbaPageSetup::getGutter() +{ + // not support in Writer + return 0; +} + +void SAL_CALL SwVbaPageSetup::setGutter( double _gutter ) +{ + // default add gutter into left margin + if( _gutter != 0 ) + { + double margin = VbaPageSetupBase::getLeftMargin() + _gutter; + VbaPageSetupBase::setLeftMargin( margin ); + } +} + +double SAL_CALL SwVbaPageSetup::getHeaderDistance() +{ + bool isHeaderOn = false; + mxPageProps->getPropertyValue("HeaderIsOn") >>= isHeaderOn; + if( !isHeaderOn ) + mxPageProps->setPropertyValue("HeaderIsOn", uno::makeAny( true ) ); + return VbaPageSetupBase::getHeaderMargin(); +} + + /** + * changes the value of TopMargin to the value of new MS-Word-HeaderDistance. Subtracts the difference + * between old TopMargin and the new headerDistance from the value of HeaderSpacing (which defines the + * space between the header and the body of the text). calculates the new HeaderHeight (= height of the + * header + headerBodyDistance). + * + * @param: headerDistance is the value that is set in MS Word for the distance from the top of the page + * to the header + */ +void SAL_CALL SwVbaPageSetup::setHeaderDistance( double _headerdistance ) +{ + sal_Int32 newHeaderDistance = Millimeter::getInHundredthsOfOneMillimeter( _headerdistance ); + bool isHeaderOn = false; + sal_Int32 currentTopMargin = 0; + sal_Int32 currentSpacing = 0; + sal_Int32 currentHeaderHeight = 0; + + mxPageProps->getPropertyValue("HeaderIsOn") >>= isHeaderOn; + if( !isHeaderOn ) + mxPageProps->setPropertyValue("HeaderIsOn", uno::makeAny( true ) ); + + mxPageProps->getPropertyValue("TopMargin") >>= currentTopMargin; + mxPageProps->getPropertyValue("HeaderBodyDistance") >>= currentSpacing; + mxPageProps->getPropertyValue("HeaderHeight") >>= currentHeaderHeight; + + sal_Int32 newSpacing = currentSpacing - ( newHeaderDistance - currentTopMargin ); + sal_Int32 height = currentHeaderHeight - currentSpacing; + sal_Int32 newHeaderHeight = newSpacing + height; + + mxPageProps->setPropertyValue("TopMargin", uno::makeAny( newHeaderDistance ) ); + mxPageProps->setPropertyValue("HeaderBodyDistance", uno::makeAny( newSpacing ) ); + mxPageProps->setPropertyValue("HeaderHeight", uno::makeAny( newHeaderHeight ) ); +} + +double SAL_CALL SwVbaPageSetup::getFooterDistance() +{ + bool isFooterOn = false; + mxPageProps->getPropertyValue("FooterIsOn") >>= isFooterOn; + if( !isFooterOn ) + mxPageProps->setPropertyValue("FooterIsOn", uno::makeAny( true ) ); + return VbaPageSetupBase::getFooterMargin(); +} + +void SAL_CALL SwVbaPageSetup::setFooterDistance( double _footerdistance ) +{ + sal_Int32 newFooterDistance = Millimeter::getInHundredthsOfOneMillimeter( _footerdistance ); + bool isFooterOn = false; + sal_Int32 currentBottomMargin = 0; + sal_Int32 currentSpacing = 0; + sal_Int32 currentFooterHeight = 0; + + mxPageProps->getPropertyValue("FooterIsOn") >>= isFooterOn; + if( !isFooterOn ) + mxPageProps->setPropertyValue("FooterIsOn", uno::makeAny( true ) ); + + mxPageProps->getPropertyValue("BottomMargin") >>= currentBottomMargin; + mxPageProps->getPropertyValue("FooterBodyDistance") >>= currentSpacing; + mxPageProps->getPropertyValue("FooterHeight") >>= currentFooterHeight; + + sal_Int32 newSpacing = currentSpacing - ( newFooterDistance - currentBottomMargin ); + sal_Int32 height = currentFooterHeight - currentSpacing; + sal_Int32 newFooterHeight = newSpacing + height; + + mxPageProps->setPropertyValue("BottomMargin", uno::makeAny( newFooterDistance ) ); + mxPageProps->setPropertyValue("FooterBodyDistance", uno::makeAny( newSpacing ) ); + mxPageProps->setPropertyValue("FooterHeight", uno::makeAny( newFooterHeight ) ); +} + +sal_Bool SAL_CALL SwVbaPageSetup::getDifferentFirstPageHeaderFooter() +{ + OUString pageStyle = getStyleOfFirstPage(); + if ( pageStyle == "First Page" ) + return true; + + return false; +} + +void SAL_CALL SwVbaPageSetup::setDifferentFirstPageHeaderFooter( sal_Bool status ) +{ + if( status == getDifferentFirstPageHeaderFooter() ) + return; + + OUString newStyle; + if( status ) + newStyle = "First Page"; + else + newStyle = "Standard"; + + uno::Reference< beans::XPropertySet > xStyleProps( word::getCurrentPageStyle( mxModel ), uno::UNO_QUERY_THROW ); + sal_Int32 nTopMargin = 0; + xStyleProps->getPropertyValue("TopMargin") >>= nTopMargin; + sal_Int32 nBottomMargin = 0; + xStyleProps->getPropertyValue("BottomMargin") >>= nBottomMargin; + sal_Int32 nLeftMargin = 0; + xStyleProps->getPropertyValue("LeftMargin") >>= nLeftMargin; + sal_Int32 nRightMargin = 0; + xStyleProps->getPropertyValue("RightMargin") >>= nRightMargin; + sal_Int32 nHeaderHeight = 0; + xStyleProps->getPropertyValue("HeaderHeight") >>= nHeaderHeight; + sal_Int32 nFooterHeight = 0; + xStyleProps->getPropertyValue("FooterHeight") >>= nFooterHeight; + + bool isHeaderOn = false; + xStyleProps->getPropertyValue("HeaderIsOn") >>= isHeaderOn; + if( isHeaderOn ) + { + nTopMargin += nHeaderHeight; + nBottomMargin += nFooterHeight; + xStyleProps->setPropertyValue("HeaderIsOn", uno::makeAny( false ) ); + xStyleProps->setPropertyValue("FooterIsOn", uno::makeAny( false ) ); + } + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( mxModel ), uno::UNO_QUERY_THROW ); + if( xPageCursor->getPage() != 1 ) + { + xPageCursor->jumpToFirstPage(); + } + + uno::Reference< beans::XPropertySet > xCursorProps( xPageCursor, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xTableProps( xCursorProps->getPropertyValue("TextTable"), uno::UNO_QUERY ); + if( xTableProps.is() ) + { + xTableProps->setPropertyValue("PageDescName", uno::makeAny( newStyle ) ); + } + else + { + xCursorProps->setPropertyValue("PageDescName", uno::makeAny( newStyle ) ); + } + + uno::Reference< beans::XPropertySet > xFirstPageProps( word::getCurrentPageStyle( mxModel ), uno::UNO_QUERY_THROW ); + xFirstPageProps->setPropertyValue("TopMargin", uno::makeAny( nTopMargin ) ); + xFirstPageProps->setPropertyValue("BottomMargin", uno::makeAny( nBottomMargin ) ); + xFirstPageProps->setPropertyValue("LeftMargin", uno::makeAny( nLeftMargin ) ); + xFirstPageProps->setPropertyValue("RightMargin", uno::makeAny( nRightMargin ) ); +} + +OUString SwVbaPageSetup::getStyleOfFirstPage() const +{ + OUString styleFirstPage; + uno::Reference< text::XPageCursor > xPageCursor( word::getXTextViewCursor( mxModel ), uno::UNO_QUERY_THROW ); + if( xPageCursor->getPage() != 1 ) + { + xPageCursor->jumpToFirstPage(); + } + + uno::Reference< beans::XPropertySet > xCursorProps( xPageCursor, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xTableProps( xCursorProps->getPropertyValue("TextTable"), uno::UNO_QUERY ); + if( xTableProps.is() ) + { + xTableProps->getPropertyValue("PageDescName") >>= styleFirstPage; + } + else + { + xCursorProps->getPropertyValue("PageDescName") >>= styleFirstPage; + } + return styleFirstPage; +} + +::sal_Int32 SAL_CALL SwVbaPageSetup::getSectionStart() +{ + // FIXME: + sal_Int32 wdSectionStart = word::WdSectionStart::wdSectionNewPage; + uno::Reference< container::XNamed > xNamed( mxPageProps, uno::UNO_QUERY_THROW ); + OUString sStyleName = xNamed->getName(); + if ( sStyleName == "Left Page" ) + wdSectionStart = word::WdSectionStart::wdSectionEvenPage; + else if ( sStyleName == "Right Page" ) + wdSectionStart = word::WdSectionStart::wdSectionOddPage; + else + wdSectionStart = word::WdSectionStart::wdSectionNewPage; + return wdSectionStart; +} + +void SAL_CALL SwVbaPageSetup::setSectionStart( ::sal_Int32 /*_sectionstart*/ ) +{ + // fail to find corresponding feature in Writer + // #FIXME: +} + +OUString +SwVbaPageSetup::getServiceImplName() +{ + return "SwVbaPageSetup"; +} + +uno::Sequence< OUString > +SwVbaPageSetup::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.PageSetup" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapagesetup.hxx b/sw/source/ui/vba/vbapagesetup.hxx new file mode 100644 index 000000000..b6b9c0155 --- /dev/null +++ b/sw/source/ui/vba/vbapagesetup.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAPAGESETUP_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPAGESETUP_HXX + +#include <cppuhelper/implbase.hxx> +#include <ooo/vba/word/XPageSetup.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <vbahelper/vbapagesetupbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaPageSetupBase, ooo::vba::word::XPageSetup > SwVbaPageSetup_BASE; + +class SwVbaPageSetup : public SwVbaPageSetup_BASE +{ +private: + /// @throws css::uno::RuntimeException + OUString getStyleOfFirstPage() const; + +public: + /// @throws css::uno::RuntimeException + SwVbaPageSetup( const css::uno::Reference< ooo::vba::XHelperInterface >& xParent, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::beans::XPropertySet >& xProps ); + + // Attributes + virtual double SAL_CALL getGutter() override; + virtual void SAL_CALL setGutter( double _gutter ) override; + virtual double SAL_CALL getHeaderDistance() override; + virtual void SAL_CALL setHeaderDistance( double _headerdistance ) override; + virtual double SAL_CALL getFooterDistance() override; + virtual void SAL_CALL setFooterDistance( double _footerdistance ) override; + virtual sal_Bool SAL_CALL getDifferentFirstPageHeaderFooter() override; + virtual void SAL_CALL setDifferentFirstPageHeaderFooter( sal_Bool status ) override; + virtual ::sal_Int32 SAL_CALL getSectionStart() override; + virtual void SAL_CALL setSectionStart( ::sal_Int32 _sectionstart ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapalette.cxx b/sw/source/ui/vba/vbapalette.cxx new file mode 100644 index 000000000..f5ed233bb --- /dev/null +++ b/sw/source/ui/vba/vbapalette.cxx @@ -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 . + */ + +#include "vbapalette.hxx" +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <ooo/vba/word/WdColor.hpp> +#include <sal/macros.h> + +using namespace ::ooo::vba; +using namespace ::ooo::vba::word; +using namespace ::com::sun::star; + +static const sal_Int32 ColorTable[] = +{ +WdColor::wdColorAutomatic, // 0 +WdColor::wdColorBlack, // 1 +WdColor::wdColorBlue, // 2 +WdColor::wdColorTurquoise, // 3 +WdColor::wdColorBrightGreen, // 4 +WdColor::wdColorPink, // 5 +WdColor::wdColorRed, // 6 +WdColor::wdColorYellow, // 7 +WdColor::wdColorWhite, // 8 +WdColor::wdColorDarkBlue, // 9 +WdColor::wdColorTeal, // 10 +WdColor::wdColorGreen, // 11 +WdColor::wdColorViolet, // 12 +WdColor::wdColorDarkRed, // 13 +WdColor::wdColorDarkYellow, // 14 +WdColor::wdColorGray50, // 15 +WdColor::wdColorGray25, // 16 +}; + +typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE; + +namespace { + +class DefaultPalette : public XIndexAccess_BASE +{ +public: + DefaultPalette(){} + + // Methods XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount() override + { + return SAL_N_ELEMENTS(ColorTable); + } + + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + return uno::makeAny( sal_Int32( ColorTable[ Index ] ) ); + } + + // Methods XElementAccess + virtual uno::Type SAL_CALL getElementType() override + { + return ::cppu::UnoType<sal_Int32>::get(); + } + virtual sal_Bool SAL_CALL hasElements() override + { + return true; + } + +}; + +} + +VbaPalette::VbaPalette() + : mxPalette(new DefaultPalette()) +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapalette.hxx b/sw/source/ui/vba/vbapalette.hxx new file mode 100644 index 000000000..cbf639301 --- /dev/null +++ b/sw/source/ui/vba/vbapalette.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_UI_VBA_VBAPALETTE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPALETTE_HXX + +#include <com/sun/star/container/XIndexAccess.hpp> + +class VbaPalette +{ + css::uno::Reference< css::container::XIndexAccess > mxPalette; +public: + VbaPalette(); + // if no palette available e.g. because the document doesn't have a + // palette defined then a default palette will be returned. + const css::uno::Reference< css::container::XIndexAccess >& getPalette() const { return mxPalette;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapane.cxx b/sw/source/ui/vba/vbapane.cxx new file mode 100644 index 000000000..3ced1a844 --- /dev/null +++ b/sw/source/ui/vba/vbapane.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 "vbapane.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbaview.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaPane::SwVbaPane( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, + const uno::Reference< frame::XModel >& xModel ) : + SwVbaPane_BASE( rParent, rContext ), mxModel( xModel ) +{ +} + +SwVbaPane::~SwVbaPane() +{ +} + +uno::Any SAL_CALL +SwVbaPane::View() +{ + return uno::makeAny( uno::Reference< word::XView >( new SwVbaView( this, mxContext, mxModel ) ) ); +} + +void SAL_CALL +SwVbaPane::Close( ) +{ + OUString url = ".uno:CloseWin"; + dispatchRequests( mxModel,url ); +} + +OUString +SwVbaPane::getServiceImplName() +{ + return "SwVbaPane"; +} + +uno::Sequence< OUString > +SwVbaPane::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Pane" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapane.hxx b/sw/source/ui/vba/vbapane.hxx new file mode 100644 index 000000000..e5c13bf1c --- /dev/null +++ b/sw/source/ui/vba/vbapane.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_UI_VBA_VBAPANE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPANE_HXX + +#include <ooo/vba/word/XPane.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XPane > SwVbaPane_BASE; + +class SwVbaPane : public SwVbaPane_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + +public: + /// @throws css::uno::RuntimeException + SwVbaPane( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XModel >& xModel ); + virtual ~SwVbaPane() override; + + // Methods + virtual css::uno::Any SAL_CALL View( ) override; + virtual void SAL_CALL Close( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAPANE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapanes.cxx b/sw/source/ui/vba/vbapanes.cxx new file mode 100644 index 000000000..a9e437380 --- /dev/null +++ b/sw/source/ui/vba/vbapanes.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 "vbapanes.hxx" +#include "vbapane.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +// I assume there is only one pane in Writer +class PanesIndexAccess : public ::cppu::WeakImplHelper<container::XIndexAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + +public: + PanesIndexAccess( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ) {} + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override + { + return 1; + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if( Index != 0 ) + throw lang::IndexOutOfBoundsException(); + return uno::makeAny( uno::Reference< word::XPane >( new SwVbaPane( mxParent, mxContext, mxModel ) ) ); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XPane>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } +}; + +class PanesEnumWrapper : public EnumerationHelper_BASE +{ + uno::Reference<container::XIndexAccess > m_xIndexAccess; + sal_Int32 nIndex; +public: + explicit PanesEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : m_xIndexAccess( xIndexAccess ), nIndex( 0 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < m_xIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex < m_xIndexAccess->getCount() ) + return m_xIndexAccess->getByIndex( nIndex++ ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaPanes::SwVbaPanes( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel ): SwVbaPanes_BASE( xParent, xContext, new PanesIndexAccess( xParent, xContext, xModel ) ) +{ +} +// XEnumerationAccess +uno::Type +SwVbaPanes::getElementType() +{ + return cppu::UnoType<word::XPane>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaPanes::createEnumeration() +{ + return new PanesEnumWrapper( m_xIndexAccess ); +} + +uno::Any +SwVbaPanes::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaPanes::getServiceImplName() +{ + return "SwVbaPanes"; +} + +css::uno::Sequence<OUString> +SwVbaPanes::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Panes" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbapanes.hxx b/sw/source/ui/vba/vbapanes.hxx new file mode 100644 index 000000000..da09790d7 --- /dev/null +++ b/sw/source/ui/vba/vbapanes.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_UI_VBA_VBAPANES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPANES_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XPanes.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XPanes > SwVbaPanes_BASE; + +class SwVbaPanes : public SwVbaPanes_BASE +{ +public: + SwVbaPanes( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaPanes_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAPANES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaparagraph.cxx b/sw/source/ui/vba/vbaparagraph.cxx new file mode 100644 index 000000000..30ad4fd51 --- /dev/null +++ b/sw/source/ui/vba/vbaparagraph.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaparagraph.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbarange.hxx" +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaParagraph::SwVbaParagraph( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& xDocument, const uno::Reference< text::XTextRange >& xTextRange ) : + SwVbaParagraph_BASE( rParent, rContext ), mxTextDocument( xDocument ), mxTextRange( xTextRange ) +{ +} + +SwVbaParagraph::~SwVbaParagraph() +{ +} + +uno::Reference< word::XRange > SAL_CALL +SwVbaParagraph::getRange( ) +{ + return uno::Reference< word::XRange >( new SwVbaRange( this, mxContext, mxTextDocument, mxTextRange->getStart(), mxTextRange->getEnd(), mxTextRange->getText() ) ); +} + +uno::Any SAL_CALL +SwVbaParagraph::getStyle( ) +{ + uno::Reference< word::XRange > xRange = getRange(); + return xRange->getStyle(); +} + +void SAL_CALL +SwVbaParagraph::setStyle( const uno::Any& style ) +{ + uno::Reference< word::XRange > xRange = getRange(); + xRange->setStyle( style ); +} + +OUString +SwVbaParagraph::getServiceImplName() +{ + return "SwVbaParagraph"; +} + +uno::Sequence< OUString > +SwVbaParagraph::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Paragraph" + }; + return aServiceNames; +} + +namespace { + +class ParagraphCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< text::XTextDocument > mxTextDocument; + + /// @throws uno::RuntimeException + uno::Reference< container::XEnumeration > getEnumeration() + { + uno::Reference< container::XEnumerationAccess > xParEnumAccess( mxTextDocument->getText(), uno::UNO_QUERY_THROW ); + return xParEnumAccess->createEnumeration(); + } + +public: + /// @throws uno::RuntimeException + explicit ParagraphCollectionHelper( const uno::Reference< text::XTextDocument >& xDocument ): mxTextDocument( xDocument ) + { + } + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<text::XTextRange>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return true; } + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + sal_Int32 nCount = 0; + uno::Reference< container::XEnumeration > xParEnum = getEnumeration(); + while( xParEnum->hasMoreElements() ) + { + uno::Reference< lang::XServiceInfo > xServiceInfo( xParEnum->nextElement(), uno::UNO_QUERY_THROW ); + if( xServiceInfo->supportsService("com.sun.star.text.Paragraph") ) + { + nCount++; + } + } + return nCount; + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + if( Index < getCount() ) + { + sal_Int32 nCount = 0; + uno::Reference< container::XEnumeration > xParEnum = getEnumeration(); + while( xParEnum->hasMoreElements() ) + { + uno::Reference< lang::XServiceInfo > xServiceInfo( xParEnum->nextElement(), uno::UNO_QUERY_THROW ); + if( xServiceInfo->supportsService("com.sun.star.text.Paragraph") ) + { + if( Index == nCount ) + return uno::makeAny( xServiceInfo ); + nCount++; + } + } + } + throw lang::IndexOutOfBoundsException(); + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return getEnumeration(); + } +}; + +} + +SwVbaParagraphs::SwVbaParagraphs( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< text::XTextDocument >& xDocument ) : SwVbaParagraphs_BASE( xParent, xContext, new ParagraphCollectionHelper( xDocument ) ), mxTextDocument( xDocument ) +{ +} + +// XEnumerationAccess +uno::Type +SwVbaParagraphs::getElementType() +{ + return cppu::UnoType<word::XParagraph>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaParagraphs::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumerationAccess->createEnumeration(); +} + +uno::Any +SwVbaParagraphs::createCollectionObject( const css::uno::Any& aSource ) +{ + uno::Reference< text::XTextRange > xTextRange( aSource, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XParagraph >( new SwVbaParagraph( this, mxContext, mxTextDocument, xTextRange ) ) ); +} + +OUString +SwVbaParagraphs::getServiceImplName() +{ + return "SwVbaParagraphs"; +} + +css::uno::Sequence<OUString> +SwVbaParagraphs::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Paragraphs" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaparagraph.hxx b/sw/source/ui/vba/vbaparagraph.hxx new file mode 100644 index 000000000..7ce0b0555 --- /dev/null +++ b/sw/source/ui/vba/vbaparagraph.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_UI_VBA_VBAPARAGRAPH_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPARAGRAPH_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XParagraphs.hpp> +#include <ooo/vba/word/XParagraph.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XParagraph > SwVbaParagraph_BASE; + +class SwVbaParagraph : public SwVbaParagraph_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + css::uno::Reference< css::text::XTextRange > mxTextRange; + +public: + /// @throws css::uno::RuntimeException + SwVbaParagraph( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& xDocument, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + virtual ~SwVbaParagraph() override; + + // XParagraph + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL getRange() override; + virtual css::uno::Any SAL_CALL getStyle() override; + virtual void SAL_CALL setStyle( const css::uno::Any& style ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +typedef CollTestImplHelper< ooo::vba::word::XParagraphs > SwVbaParagraphs_BASE; + +class SwVbaParagraphs : public SwVbaParagraphs_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; +public: + /// @throws css::uno::RuntimeException + SwVbaParagraphs( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextDocument >& xDocument ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaParagraphs_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAPARAGRAPH_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaparagraphformat.cxx b/sw/source/ui/vba/vbaparagraphformat.cxx new file mode 100644 index 000000000..0a8c85663 --- /dev/null +++ b/sw/source/ui/vba/vbaparagraphformat.cxx @@ -0,0 +1,565 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaparagraphformat.hxx" +#include <vbahelper/vbahelper.hxx> +#include <basic/sberrors.hxx> +#include <com/sun/star/style/LineSpacingMode.hpp> +#include <ooo/vba/word/WdLineSpacing.hpp> +#include <ooo/vba/word/WdParagraphAlignment.hpp> +#include <ooo/vba/word/WdOutlineLevel.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <com/sun/star/style/BreakType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include "vbatabstops.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static const sal_Int16 CHARACTER_INDENT_FACTOR = 12; +static const sal_Int16 PERCENT100 = 100; +static const sal_Int16 PERCENT150 = 150; +static const sal_Int16 PERCENT200 = 200; + +SwVbaParagraphFormat::SwVbaParagraphFormat( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< beans::XPropertySet >& rParaProps ) : SwVbaParagraphFormat_BASE( rParent, rContext ), mxParaProps( rParaProps ) +{ +} + +SwVbaParagraphFormat::~SwVbaParagraphFormat() +{ +} + +sal_Int32 SAL_CALL SwVbaParagraphFormat::getAlignment() +{ + style::ParagraphAdjust aParaAdjust = style::ParagraphAdjust_LEFT; + mxParaProps->getPropertyValue("ParaAdjust") >>= aParaAdjust; + return getMSWordAlignment( aParaAdjust ); +} + +void SAL_CALL SwVbaParagraphFormat::setAlignment( sal_Int32 _alignment ) +{ + style::ParagraphAdjust aParaAdjust = getOOoAlignment( _alignment ); + mxParaProps->setPropertyValue("ParaAdjust", uno::makeAny( aParaAdjust ) ); +} + +float SAL_CALL SwVbaParagraphFormat::getFirstLineIndent() +{ + sal_Int32 indent = 0; + mxParaProps->getPropertyValue("ParaFirstLineIndent") >>= indent; + return static_cast<float>( Millimeter::getInPoints( indent ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setFirstLineIndent( float _firstlineindent ) +{ + sal_Int32 indent = Millimeter::getInHundredthsOfOneMillimeter( _firstlineindent ); + mxParaProps->setPropertyValue("ParaFirstLineIndent", uno::makeAny( indent ) ); +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getKeepTogether() +{ + bool bKeep = false; + mxParaProps->getPropertyValue("ParaKeepTogether") >>= bKeep; + return uno::makeAny ( bKeep ); +} + +void SAL_CALL SwVbaParagraphFormat::setKeepTogether( const uno::Any& _keeptogether ) +{ + bool bKeep = false; + if( _keeptogether >>= bKeep ) + { + mxParaProps->setPropertyValue("ParaKeepTogether", uno::makeAny( bKeep ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getKeepWithNext() +{ + bool bKeep = false; + mxParaProps->getPropertyValue("ParaSplit") >>= bKeep; + return uno::makeAny ( bKeep ); +} + +void SAL_CALL SwVbaParagraphFormat::setKeepWithNext( const uno::Any& _keepwithnext ) +{ + bool bKeep = false; + if( _keepwithnext >>= bKeep ) + { + mxParaProps->setPropertyValue("ParaSplit", uno::makeAny( bKeep ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getHyphenation() +{ + bool bHypn = false; + mxParaProps->getPropertyValue("ParaIsHyphenation") >>= bHypn; + return uno::makeAny ( bHypn ); +} + +void SAL_CALL SwVbaParagraphFormat::setHyphenation( const uno::Any& _hyphenation ) +{ + bool bHypn = false; + if( _hyphenation >>= bHypn ) + { + mxParaProps->setPropertyValue("ParaIsHyphenation", uno::makeAny( bHypn ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +float SAL_CALL SwVbaParagraphFormat::getLineSpacing() +{ + style::LineSpacing aLineSpacing; + mxParaProps->getPropertyValue("ParaLineSpacing") >>= aLineSpacing; + return getMSWordLineSpacing( aLineSpacing ); +} + +void SAL_CALL SwVbaParagraphFormat::setLineSpacing( float _linespacing ) +{ + style::LineSpacing aLineSpacing; + mxParaProps->getPropertyValue("ParaLineSpacing") >>= aLineSpacing; + aLineSpacing = getOOoLineSpacing( _linespacing, aLineSpacing.Mode ); + mxParaProps->setPropertyValue("ParaLineSpacing", uno::makeAny( aLineSpacing ) ); +} + +sal_Int32 SAL_CALL SwVbaParagraphFormat::getLineSpacingRule() +{ + style::LineSpacing aLineSpacing; + mxParaProps->getPropertyValue("ParaLineSpacing") >>= aLineSpacing; + return getMSWordLineSpacingRule( aLineSpacing ); +} + +void SAL_CALL SwVbaParagraphFormat::setLineSpacingRule( sal_Int32 _linespacingrule ) +{ + style::LineSpacing aLineSpacing = getOOoLineSpacingFromRule( _linespacingrule ); + mxParaProps->setPropertyValue("ParaLineSpacing", uno::makeAny( aLineSpacing ) ); +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getNoLineNumber() +{ + bool noLineNum = false; + mxParaProps->getPropertyValue("ParaLineNumberCount") >>= noLineNum; + return uno::makeAny ( noLineNum ); +} + +void SAL_CALL SwVbaParagraphFormat::setNoLineNumber( const uno::Any& _nolinenumber ) +{ + bool noLineNum = false; + if( _nolinenumber >>= noLineNum ) + { + mxParaProps->setPropertyValue("ParaLineNumberCount", uno::makeAny( noLineNum ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +sal_Int32 SAL_CALL SwVbaParagraphFormat::getOutlineLevel() +{ + sal_Int32 nLevel = word::WdOutlineLevel::wdOutlineLevelBodyText; + OUString aHeading; + const OUString HEADING = "Heading"; + mxParaProps->getPropertyValue("ParaStyleName") >>= aHeading; + if( aHeading.startsWith( HEADING ) ) + { + // get the sub string after "Heading" + nLevel = aHeading.copy( HEADING.getLength() ).toInt32(); + } + return nLevel; +} + +void SAL_CALL SwVbaParagraphFormat::setOutlineLevel( sal_Int32 _outlinelevel ) +{ + if( _outlinelevel != getOutlineLevel() ) + { + // TODO: in my test in msword, there is no effect for this function. + } +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getPageBreakBefore() +{ + style::BreakType aBreakType; + mxParaProps->getPropertyValue("BreakType") >>= aBreakType; + bool bBreakBefore = ( aBreakType == style::BreakType_PAGE_BEFORE || aBreakType == style::BreakType_PAGE_BOTH ); + return uno::makeAny( bBreakBefore ); +} + +void SAL_CALL SwVbaParagraphFormat::setPageBreakBefore( const uno::Any& _breakbefore ) +{ + bool bBreakBefore = false; + if( _breakbefore >>= bBreakBefore ) + { + style::BreakType aBreakType; + mxParaProps->getPropertyValue("BreakType") >>= aBreakType; + if( bBreakBefore ) + { + if( aBreakType == style::BreakType_NONE ) + aBreakType = style::BreakType_PAGE_BEFORE; + else if ( aBreakType == style::BreakType_PAGE_AFTER ) + aBreakType = style::BreakType_PAGE_BOTH; + } + else + { + if( aBreakType == style::BreakType_PAGE_BOTH ) + aBreakType = style::BreakType_PAGE_AFTER; + else if ( aBreakType == style::BreakType_PAGE_BEFORE ) + aBreakType = style::BreakType_PAGE_AFTER; + } + mxParaProps->setPropertyValue("BreakType", uno::makeAny( aBreakType ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +float SAL_CALL SwVbaParagraphFormat::getSpaceBefore() +{ + sal_Int32 nSpace = 0; + mxParaProps->getPropertyValue("ParaTopMargin") >>= nSpace; + return static_cast<float>( Millimeter::getInPoints( nSpace ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setSpaceBefore( float _space ) +{ + sal_Int32 nSpace = Millimeter::getInHundredthsOfOneMillimeter( _space ); + mxParaProps->setPropertyValue("ParaTopMargin", uno::makeAny( nSpace ) ); +} + +float SAL_CALL SwVbaParagraphFormat::getSpaceAfter() +{ + sal_Int32 nSpace = 0; + mxParaProps->getPropertyValue("ParaBottomMargin") >>= nSpace; + return static_cast<float>( Millimeter::getInPoints( nSpace ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setSpaceAfter( float _space ) +{ + sal_Int32 nSpace = Millimeter::getInHundredthsOfOneMillimeter( _space ); + mxParaProps->setPropertyValue("ParaBottomMargin", uno::makeAny( nSpace ) ); +} + +float SAL_CALL SwVbaParagraphFormat::getLeftIndent() +{ + sal_Int32 nIndent = 0; + mxParaProps->getPropertyValue("ParaLeftMargin") >>= nIndent; + return static_cast<float>( Millimeter::getInPoints( nIndent ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setLeftIndent( float _leftindent ) +{ + sal_Int32 nIndent = Millimeter::getInHundredthsOfOneMillimeter( _leftindent ); + mxParaProps->setPropertyValue("ParaLeftMargin", uno::makeAny( nIndent ) ); +} + +float SAL_CALL SwVbaParagraphFormat::getRightIndent() +{ + sal_Int32 nIndent = 0; + mxParaProps->getPropertyValue("ParaRightMargin") >>= nIndent; + return static_cast<float>( Millimeter::getInPoints( nIndent ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setRightIndent( float _rightindent ) +{ + sal_Int32 nIndent = Millimeter::getInHundredthsOfOneMillimeter( _rightindent ); + mxParaProps->setPropertyValue("ParaRightMargin", uno::makeAny( nIndent ) ); +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getTabStops() +{ + return uno::makeAny( uno::Reference< word::XTabStops >( new SwVbaTabStops( this, mxContext, mxParaProps ) ) ); +} + +void SAL_CALL SwVbaParagraphFormat::setTabStops( const uno::Any& /*_tabstops*/ ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +uno::Any SAL_CALL SwVbaParagraphFormat::getWidowControl() +{ + sal_Int8 nWidow = 0; + mxParaProps->getPropertyValue("ParaWidows") >>= nWidow; + sal_Int8 nOrphan = 0; + mxParaProps->getPropertyValue("ParaOrphans") >>= nOrphan; + // if the amount of single lines on one page > 1 and the same of start and end of the paragraph, + // true is returned. + bool bWidow = ( nWidow > 1 && nOrphan == nWidow ); + return uno::makeAny( bWidow ); +} + +void SAL_CALL SwVbaParagraphFormat::setWidowControl( const uno::Any& _widowcontrol ) +{ + // if we get true, the part of the paragraph on one page has to be + // at least two lines + bool bWidow = false; + if( _widowcontrol >>= bWidow ) + { + sal_Int8 nControl = bWidow? 2:1; + mxParaProps->setPropertyValue("ParaWidows", uno::makeAny( nControl ) ); + mxParaProps->setPropertyValue("ParaOrphans", uno::makeAny( nControl ) ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } +} + +style::LineSpacing SwVbaParagraphFormat::getOOoLineSpacing( float _lineSpace, sal_Int16 mode ) +{ + style::LineSpacing aLineSpacing; + if( mode != style::LineSpacingMode::MINIMUM && mode != style::LineSpacingMode::FIX ) + { + // special behaviour of word: if the space is set to these values, the rule and + // the height are changed accordingly + if( _lineSpace == CHARACTER_INDENT_FACTOR ) + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = PERCENT100; + } + else if( _lineSpace == CHARACTER_INDENT_FACTOR * 1.5 ) // no rounding issues, == 18 + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = PERCENT150; + } + else if( _lineSpace == CHARACTER_INDENT_FACTOR * 2 ) + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = PERCENT200; + } + else + { + aLineSpacing.Mode = style::LineSpacingMode::FIX; + aLineSpacing.Height = static_cast<sal_Int16>( Millimeter::getInHundredthsOfOneMillimeter( _lineSpace ) ); + } + } + else + { + aLineSpacing.Mode = mode; + aLineSpacing.Height = static_cast<sal_Int16>( Millimeter::getInHundredthsOfOneMillimeter( _lineSpace ) ); + } + return aLineSpacing; +} + +style::LineSpacing SwVbaParagraphFormat::getOOoLineSpacingFromRule( sal_Int32 _linespacingrule ) +{ + style::LineSpacing aLineSpacing; + switch( _linespacingrule ) + { + case word::WdLineSpacing::wdLineSpace1pt5: + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = PERCENT150; + break; + } + case word::WdLineSpacing::wdLineSpaceAtLeast: + { + aLineSpacing.Mode = style::LineSpacingMode::MINIMUM; + aLineSpacing.Height = getCharHeight(); + break; + } + case word::WdLineSpacing::wdLineSpaceDouble: + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = getCharHeight(); + break; + } + case word::WdLineSpacing::wdLineSpaceExactly: + case word::WdLineSpacing::wdLineSpaceMultiple: + { + aLineSpacing.Mode = style::LineSpacingMode::FIX; + aLineSpacing.Height = getCharHeight(); + break; + } + case word::WdLineSpacing::wdLineSpaceSingle: + { + aLineSpacing.Mode = style::LineSpacingMode::PROP; + aLineSpacing.Height = PERCENT100; + break; + } + default: + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + break; + } + } + return aLineSpacing; +} + +float SwVbaParagraphFormat::getMSWordLineSpacing( style::LineSpacing const & rLineSpacing ) +{ + float wdLineSpacing = 0; + if( rLineSpacing.Mode != style::LineSpacingMode::PROP ) + { + wdLineSpacing = static_cast<float>( Millimeter::getInPoints( rLineSpacing.Height ) ); + } + else + { + wdLineSpacing = static_cast<float>( CHARACTER_INDENT_FACTOR * rLineSpacing.Height ) / PERCENT100; + } + return wdLineSpacing; +} + +sal_Int32 SwVbaParagraphFormat::getMSWordLineSpacingRule( style::LineSpacing const & rLineSpacing ) +{ + sal_Int32 wdLineSpacing = word::WdLineSpacing::wdLineSpaceSingle; + switch( rLineSpacing.Mode ) + { + case style::LineSpacingMode::PROP: + { + switch( rLineSpacing.Height ) + { + case PERCENT100: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpaceSingle; + break; + } + case PERCENT150: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpace1pt5; + break; + } + case PERCENT200: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpaceDouble; + break; + } + default: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpaceMultiple; + } + } + break; + } + case style::LineSpacingMode::MINIMUM: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpaceAtLeast; + break; + } + case style::LineSpacingMode::FIX: + case style::LineSpacingMode::LEADING: + { + wdLineSpacing = word::WdLineSpacing::wdLineSpaceExactly; + break; + } + default: + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } + } + return wdLineSpacing; +} + +sal_Int16 SwVbaParagraphFormat::getCharHeight() +{ + float fCharHeight = 0.0; + mxParaProps->getPropertyValue("CharHeight") >>= fCharHeight; + return static_cast<sal_Int16>( Millimeter::getInHundredthsOfOneMillimeter( fCharHeight ) ); +} + +style::ParagraphAdjust SwVbaParagraphFormat::getOOoAlignment( sal_Int32 _alignment ) +{ + style::ParagraphAdjust nParaAjust = style::ParagraphAdjust_LEFT; + switch( _alignment ) + { + case word::WdParagraphAlignment::wdAlignParagraphCenter: + { + nParaAjust = style::ParagraphAdjust_CENTER; + break; + } + case word::WdParagraphAlignment::wdAlignParagraphJustify: + { + nParaAjust = style::ParagraphAdjust_BLOCK; + break; + } + case word::WdParagraphAlignment::wdAlignParagraphLeft: + { + nParaAjust = style::ParagraphAdjust_LEFT; + break; + } + case word::WdParagraphAlignment::wdAlignParagraphRight: + { + nParaAjust = style::ParagraphAdjust_RIGHT; + break; + } + default: + { + DebugHelper::runtimeexception( ERRCODE_BASIC_BAD_PARAMETER ); + } + } + return nParaAjust; +} + +sal_Int32 SwVbaParagraphFormat::getMSWordAlignment( style::ParagraphAdjust _alignment ) +{ + sal_Int32 wdAlignment = word::WdParagraphAlignment::wdAlignParagraphLeft; + switch( _alignment ) + { + case style::ParagraphAdjust_CENTER: + { + wdAlignment = word::WdParagraphAlignment::wdAlignParagraphCenter; + break; + } + case style::ParagraphAdjust_LEFT: + { + wdAlignment = word::WdParagraphAlignment::wdAlignParagraphLeft; + break; + } + case style::ParagraphAdjust_BLOCK: + { + wdAlignment = word::WdParagraphAlignment::wdAlignParagraphJustify; + break; + } + case style::ParagraphAdjust_RIGHT: + { + wdAlignment = word::WdParagraphAlignment::wdAlignParagraphRight; + break; + } + default: + { + DebugHelper::basicexception( ERRCODE_BASIC_BAD_PARAMETER, OUString() ); + } + } + return wdAlignment; +} + +OUString +SwVbaParagraphFormat::getServiceImplName() +{ + return "SwVbaParagraphFormat"; +} + +uno::Sequence< OUString > +SwVbaParagraphFormat::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.ParagraphFormat" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaparagraphformat.hxx b/sw/source/ui/vba/vbaparagraphformat.hxx new file mode 100644 index 000000000..0b9b02b54 --- /dev/null +++ b/sw/source/ui/vba/vbaparagraphformat.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_UI_VBA_VBAPARAGRAPHFORMAT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAPARAGRAPHFORMAT_HXX + +#include <ooo/vba/word/XParagraphFormat.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/style/LineSpacing.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XParagraphFormat > SwVbaParagraphFormat_BASE; + +class SwVbaParagraphFormat : public SwVbaParagraphFormat_BASE +{ +private: + css::uno::Reference< css::beans::XPropertySet > mxParaProps; + +private: + static css::style::LineSpacing getOOoLineSpacing( float _lineSpace, sal_Int16 mode ); + css::style::LineSpacing getOOoLineSpacingFromRule( sal_Int32 _linespacingrule ); + static float getMSWordLineSpacing( css::style::LineSpacing const & rLineSpacing ); + static sal_Int32 getMSWordLineSpacingRule( css::style::LineSpacing const & rLineSpacing ); + /// @throws css::uno::RuntimeException + sal_Int16 getCharHeight(); + static css::style::ParagraphAdjust getOOoAlignment( sal_Int32 _alignment ); + static sal_Int32 getMSWordAlignment( css::style::ParagraphAdjust _alignment ); + +public: + SwVbaParagraphFormat( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::beans::XPropertySet >& rParaProps ); + virtual ~SwVbaParagraphFormat() override; + + // Attributes + virtual ::sal_Int32 SAL_CALL getAlignment() override; + virtual void SAL_CALL setAlignment( ::sal_Int32 _alignment ) override; + virtual float SAL_CALL getFirstLineIndent() override; + virtual void SAL_CALL setFirstLineIndent( float _firstlineindent ) override; + virtual css::uno::Any SAL_CALL getKeepTogether() override; + virtual void SAL_CALL setKeepTogether( const css::uno::Any& _keeptogether ) override; + virtual css::uno::Any SAL_CALL getKeepWithNext() override; + virtual void SAL_CALL setKeepWithNext( const css::uno::Any& _keepwithnext ) override; + virtual css::uno::Any SAL_CALL getHyphenation() override; + virtual void SAL_CALL setHyphenation( const css::uno::Any& _hyphenation ) override; + virtual float SAL_CALL getLineSpacing() override; + virtual void SAL_CALL setLineSpacing( float _linespacing ) override; + virtual ::sal_Int32 SAL_CALL getLineSpacingRule() override; + virtual void SAL_CALL setLineSpacingRule( ::sal_Int32 _linespacingrule ) override; + virtual css::uno::Any SAL_CALL getNoLineNumber() override; + virtual void SAL_CALL setNoLineNumber( const css::uno::Any& _nolinenumber ) override; + virtual ::sal_Int32 SAL_CALL getOutlineLevel() override; + virtual void SAL_CALL setOutlineLevel( ::sal_Int32 _outlinelevel ) override; + virtual css::uno::Any SAL_CALL getPageBreakBefore() override; + virtual void SAL_CALL setPageBreakBefore( const css::uno::Any& _pagebreakbefore ) override; + virtual float SAL_CALL getSpaceBefore() override; + virtual void SAL_CALL setSpaceBefore( float _spacebefore ) override; + virtual float SAL_CALL getSpaceAfter() override; + virtual void SAL_CALL setSpaceAfter( float _spaceafter ) override; + virtual float SAL_CALL getLeftIndent() override; + virtual void SAL_CALL setLeftIndent( float _leftindent ) override; + virtual float SAL_CALL getRightIndent() override; + virtual void SAL_CALL setRightIndent( float _rightindent ) override; + virtual css::uno::Any SAL_CALL getTabStops() override; + virtual void SAL_CALL setTabStops( const css::uno::Any& _tabstops ) override; + virtual css::uno::Any SAL_CALL getWidowControl() override; + virtual void SAL_CALL setWidowControl( const css::uno::Any& _widowcontrol ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAPARAGRAPHFORMAT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarange.cxx b/sw/source/ui/vba/vbarange.cxx new file mode 100644 index 000000000..42668fb59 --- /dev/null +++ b/sw/source/ui/vba/vbarange.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 "vbarange.hxx" +#include <vbahelper/vbahelper.hxx> +#include <basic/sberrors.hxx> +#include "vbarangehelper.hxx" +#include <ooo/vba/word/WdBreakType.hpp> +#include <com/sun/star/style/BreakType.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include "vbaparagraphformat.hxx" +#include "vbastyle.hxx" +#include "vbafont.hxx" +#include "vbapalette.hxx" +#include "vbapagesetup.hxx" +#include "vbalistformat.hxx" +#include "vbarevisions.hxx" +#include "vbabookmarks.hxx" +#include "vbasections.hxx" +#include "vbafield.hxx" +#include "wordvbahelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaRange::SwVbaRange( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& rTextDocument, const uno::Reference< text::XTextRange >& rStart ) : SwVbaRange_BASE( rParent, rContext ), mxTextDocument( rTextDocument ) +{ + uno::Reference< text::XTextRange > xEnd; + initialize( rStart, xEnd ); +} + +SwVbaRange::SwVbaRange( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& rTextDocument, const uno::Reference< text::XTextRange >& rStart, const uno::Reference< text::XTextRange >& rEnd ) : SwVbaRange_BASE( rParent, rContext ), mxTextDocument( rTextDocument ) +{ + initialize( rStart, rEnd ); +} + +SwVbaRange::SwVbaRange( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& rTextDocument, const uno::Reference< text::XTextRange >& rStart, const uno::Reference< text::XTextRange >& rEnd, const uno::Reference< text::XText >& rText ) : SwVbaRange_BASE( rParent, rContext ),mxTextDocument( rTextDocument ), mxText( rText ) +{ + initialize( rStart, rEnd ); +} + +SwVbaRange::~SwVbaRange() +{ +} + +void SwVbaRange::initialize( const uno::Reference< text::XTextRange >& rStart, const uno::Reference< text::XTextRange >& rEnd ) +{ + if( !mxText.is() ) + { + mxText = mxTextDocument->getText(); + } + + mxTextCursor = SwVbaRangeHelper::initCursor( rStart, mxText ); + if( !mxTextCursor.is() ) + throw uno::RuntimeException("Fails to create text cursor" ); + mxTextCursor->collapseToStart(); + + if( rEnd.is() ) + mxTextCursor->gotoRange( rEnd, true ); + else + mxTextCursor->gotoEnd( true ); +} + +uno::Reference< text::XTextRange > SAL_CALL +SwVbaRange::getXTextRange() +{ + uno::Reference< text::XTextRange > xTextRange( mxTextCursor, uno::UNO_QUERY_THROW ); + return xTextRange; +} + +/** +* The complexity in this method is because we need to workaround +* an issue that the last paragraph in a document does not have a trailing CRLF. +* @return +*/ +OUString SAL_CALL +SwVbaRange::getText() +{ + OUString aText = mxTextCursor->getString(); + sal_Int32 nLen = aText.getLength(); + + // FIXME: should add a line separator if the range includes the last paragraph + if( nLen == 0 ) + { + if( mxTextCursor->isCollapsed() ) + { + mxTextCursor->goRight( 1, true ); + aText = mxTextCursor->getString(); + mxTextCursor->collapseToStart(); + } + else + { + uno::Reference< text::XTextRange > xStart = mxTextCursor->getStart(); + uno::Reference< text::XTextRange > xEnd = mxTextCursor->getEnd(); + mxTextCursor->collapseToEnd(); + mxTextCursor->goRight( 1, true ); + mxTextCursor->gotoRange( xStart, false ); + mxTextCursor->gotoRange( xEnd, true ); + } + } + + return aText; +} + +void SAL_CALL +SwVbaRange::setText( const OUString& rText ) +{ + // Emulate the MSWord behavior, Don't delete the bookmark + // which contains no text string in current inserting position, + OUString sName; + uno::Reference< text::XTextRange > xRange( mxTextCursor, uno::UNO_QUERY_THROW ); + try + { + uno::Reference< text::XTextContent > xBookmark = SwVbaRangeHelper::findBookmarkByPosition( mxTextDocument, xRange->getStart() ); + if( xBookmark.is() ) + { + uno::Reference< container::XNamed > xNamed( xBookmark, uno::UNO_QUERY_THROW ); + sName = xNamed->getName(); + } + } + catch (const uno::Exception&) + { + // do nothing + } + + if( rText.indexOf( '\n' ) != -1 ) + { + mxTextCursor->setString( OUString() ); + // process CR in strings + SwVbaRangeHelper::insertString( xRange, mxText, rText, true ); + } + else + { + mxTextCursor->setString( rText ); + } + + // insert the bookmark if the bookmark is deleted during setting text string + if( !sName.isEmpty() ) + { + uno::Reference< text::XBookmarksSupplier > xBookmarksSupplier( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xNameAccess( xBookmarksSupplier->getBookmarks(), uno::UNO_SET_THROW ); + if( !xNameAccess->hasByName( sName ) ) + { + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + SwVbaBookmarks::addBookmarkByName( xModel, sName, xRange->getStart() ); + } + } +} + +// FIXME: test is not pass +void SAL_CALL SwVbaRange::InsertBreak(const uno::Any& _breakType) +{ + // default type is wdPageBreak; + sal_Int32 nBreakType = word::WdBreakType::wdPageBreak; + if( _breakType.hasValue() ) + _breakType >>= nBreakType; + + style::BreakType eBreakType = style::BreakType_NONE; + switch( nBreakType ) + { + case word::WdBreakType::wdPageBreak: + eBreakType = style::BreakType_PAGE_BEFORE; + break; + case word::WdBreakType::wdColumnBreak: + eBreakType = style::BreakType_COLUMN_AFTER; + break; + case word::WdBreakType::wdLineBreak: + case word::WdBreakType::wdLineBreakClearLeft: + case word::WdBreakType::wdLineBreakClearRight: + case word::WdBreakType::wdSectionBreakContinuous: + case word::WdBreakType::wdSectionBreakEvenPage: + case word::WdBreakType::wdSectionBreakNextPage: + case word::WdBreakType::wdSectionBreakOddPage: + case word::WdBreakType::wdTextWrappingBreak: + DebugHelper::basicexception( ERRCODE_BASIC_NOT_IMPLEMENTED, OUString() ); + break; + default: + DebugHelper::basicexception( ERRCODE_BASIC_BAD_PARAMETER, OUString() ); + } + + if( eBreakType != style::BreakType_NONE ) + { + if( !mxTextCursor->isCollapsed() ) + { + mxTextCursor->setString( OUString() ); + mxTextCursor->collapseToStart(); + } + + uno::Reference< beans::XPropertySet > xProp( mxTextCursor, uno::UNO_QUERY_THROW ); + xProp->setPropertyValue("BreakType", uno::makeAny( eBreakType ) ); + } +} + +void SAL_CALL +SwVbaRange::Select() +{ + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextViewCursor > xTextViewCursor = word::getXTextViewCursor( xModel ); + xTextViewCursor->gotoRange( mxTextCursor->getStart(), false ); + xTextViewCursor->gotoRange( mxTextCursor->getEnd(), true ); +} + +void SAL_CALL +SwVbaRange::InsertParagraph() +{ + mxTextCursor->setString( "" ); + InsertParagraphBefore(); +} + +void SAL_CALL +SwVbaRange::InsertParagraphBefore() +{ + uno::Reference< text::XTextRange > xTextRange = mxTextCursor->getStart(); + mxText->insertControlCharacter( xTextRange, text::ControlCharacter::PARAGRAPH_BREAK, true ); + mxTextCursor->gotoRange( xTextRange, true ); +} + +void SAL_CALL +SwVbaRange::InsertParagraphAfter() +{ + uno::Reference< text::XTextRange > xTextRange = mxTextCursor->getEnd(); + mxText->insertControlCharacter( xTextRange, text::ControlCharacter::PARAGRAPH_BREAK, true ); +} + +uno::Reference< word::XParagraphFormat > SAL_CALL +SwVbaRange::getParagraphFormat() +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextCursor, uno::UNO_QUERY_THROW ); + return uno::Reference< word::XParagraphFormat >( new SwVbaParagraphFormat( this, mxContext, xParaProps ) ); +} + +void SAL_CALL +SwVbaRange::setParagraphFormat( const uno::Reference< word::XParagraphFormat >& /*rParagraphFormat*/ ) +{ + throw uno::RuntimeException("Not implemented" ); +} + +void SwVbaRange::GetStyleInfo(OUString& aStyleName, OUString& aStyleType ) +{ + uno::Reference< beans::XPropertySet > xProp( mxTextCursor, uno::UNO_QUERY_THROW ); + if( ( xProp->getPropertyValue("CharStyleName") >>= aStyleName ) && !aStyleName.isEmpty() ) + { + aStyleType = "CharacterStyles"; + } + else if( ( xProp->getPropertyValue("ParaStyleName") >>= aStyleName ) && !aStyleName.isEmpty() ) + { + aStyleType = "ParagraphStyles"; + } + if( aStyleType.isEmpty() ) + { + DebugHelper::runtimeexception( ERRCODE_BASIC_INTERNAL_ERROR ); + } +} + +uno::Any SAL_CALL +SwVbaRange::getStyle() +{ + OUString aStyleName; + OUString aStyleType; + GetStyleInfo( aStyleName, aStyleType ); + uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( mxTextDocument, uno::UNO_QUERY_THROW); + uno::Reference< container::XNameAccess > xStylesAccess( xStyleSupplier->getStyleFamilies()->getByName( aStyleType ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xStyleProps( xStylesAccess->getByName( aStyleName ), uno::UNO_QUERY_THROW ); + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XStyle >( new SwVbaStyle( this, mxContext, xModel, xStyleProps ) ) ); +} + +void SAL_CALL +SwVbaRange::setStyle( const uno::Any& rStyle ) +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextCursor, uno::UNO_QUERY_THROW ); + SwVbaStyle::setStyle( xParaProps, rStyle ); +} + +uno::Reference< word::XFont > SAL_CALL +SwVbaRange::getFont() +{ + VbaPalette aColors; + return new SwVbaFont( mxParent, mxContext, aColors.getPalette(), uno::Reference< beans::XPropertySet >( getXTextRange(), uno::UNO_QUERY_THROW ) ); +} + +uno::Reference< word::XListFormat > SAL_CALL +SwVbaRange::getListFormat() +{ + return uno::Reference< word::XListFormat >( new SwVbaListFormat( this, mxContext, getXTextRange() ) ); +} + +::sal_Int32 SAL_CALL SwVbaRange::getLanguageID() +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextCursor, uno::UNO_QUERY_THROW ); + return static_cast<sal_uInt16>(SwVbaStyle::getLanguageID( xParaProps )); +} + +void SAL_CALL SwVbaRange::setLanguageID( ::sal_Int32 _languageid ) +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextCursor, uno::UNO_QUERY_THROW ); + SwVbaStyle::setLanguageID( xParaProps, LanguageType(_languageid) ); +} + +uno::Any SAL_CALL +SwVbaRange::PageSetup( ) +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextCursor, uno::UNO_QUERY_THROW ); + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + OUString aPageStyleName; + xParaProps->getPropertyValue("PageStyleName") >>= aPageStyleName; + uno::Reference< style::XStyleFamiliesSupplier > xSytleFamSupp( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xSytleFamNames( xSytleFamSupp->getStyleFamilies(), uno::UNO_SET_THROW ); + uno::Reference< container::XNameAccess > xPageStyles( xSytleFamNames->getByName("PageStyles"), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPageProps( xPageStyles->getByName( aPageStyleName ), uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XPageSetup >( new SwVbaPageSetup( this, mxContext, xModel, xPageProps ) ) ); +} + +::sal_Int32 SAL_CALL SwVbaRange::getStart() +{ + uno::Reference< text::XText > xText = mxTextDocument->getText(); + return SwVbaRangeHelper::getPosition( xText, mxTextCursor->getStart() ); +} + +void SAL_CALL SwVbaRange::setStart( ::sal_Int32 _start ) +{ + uno::Reference< text::XText > xText = mxTextDocument->getText(); + uno::Reference< text::XTextRange > xStart = SwVbaRangeHelper::getRangeByPosition( xText, _start ); + uno::Reference< text::XTextRange > xEnd = mxTextCursor->getEnd(); + + mxTextCursor->gotoRange( xStart, false ); + mxTextCursor->gotoRange( xEnd, true ); +} + +::sal_Int32 SAL_CALL SwVbaRange::getEnd() +{ + uno::Reference< text::XText > xText = mxTextDocument->getText(); + return SwVbaRangeHelper::getPosition( xText, mxTextCursor->getEnd() ); +} + +void SAL_CALL SwVbaRange::setEnd( ::sal_Int32 _end ) +{ + uno::Reference< text::XText > xText = mxTextDocument->getText(); + uno::Reference< text::XTextRange > xEnd = SwVbaRangeHelper::getRangeByPosition( xText, _end ); + + mxTextCursor->collapseToStart(); + mxTextCursor->gotoRange( xEnd, true ); +} + +sal_Bool SAL_CALL SwVbaRange::InRange( const uno::Reference< ::ooo::vba::word::XRange >& Range ) +{ + SwVbaRange* pRange = dynamic_cast< SwVbaRange* >( Range.get() ); + if( !pRange ) + throw uno::RuntimeException(); + uno::Reference< text::XTextRange > xTextRange = pRange->getXTextRange(); + uno::Reference< text::XTextRangeCompare > xTRC( mxTextCursor->getText(), uno::UNO_QUERY_THROW ); + if( xTRC->compareRegionStarts( xTextRange, getXTextRange() ) >= 0 && xTRC->compareRegionEnds( xTextRange, getXTextRange() ) <= 0 ) + return true; + return false; +} + +uno::Any SAL_CALL +SwVbaRange::Revisions( const uno::Any& index ) +{ + uno::Reference< text::XTextRange > xTextRange = getXTextRange(); + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaRevisions( mxParent, mxContext, xModel, xTextRange ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaRange::Sections( const uno::Any& index ) +{ + uno::Reference< text::XTextRange > xTextRange = getXTextRange(); + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaSections( mxParent, mxContext, xModel, xTextRange ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaRange::Fields( const uno::Any& index ) +{ + //FIXME: should be get the field in current range + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< XCollection > xCol( new SwVbaFields( mxParent, mxContext, xModel ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +OUString +SwVbaRange::getServiceImplName() +{ + return "SwVbaRange"; +} + +uno::Sequence< OUString > +SwVbaRange::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Range" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarange.hxx b/sw/source/ui/vba/vbarange.hxx new file mode 100644 index 000000000..d311c25a7 --- /dev/null +++ b/sw/source/ui/vba/vbarange.hxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_VBA_VBARANGE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBARANGE_HXX + +#include <ooo/vba/word/XRange.hpp> +#include <ooo/vba/word/XParagraphFormat.hpp> +#include <ooo/vba/word/XFont.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <ooo/vba/word/XListFormat.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XRange > SwVbaRange_BASE; + +class SwVbaRange : public SwVbaRange_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + css::uno::Reference< css::text::XTextCursor > mxTextCursor; + css::uno::Reference< css::text::XText > mxText; + +private: + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + void initialize( const css::uno::Reference< css::text::XTextRange >& rStart, const css::uno::Reference< css::text::XTextRange >& rEnd ); + /// @throws css::uno::RuntimeException + void GetStyleInfo(OUString& aStyleName, OUString& aStyleType ); +public: + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + SwVbaRange( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& rTextDocument, const css::uno::Reference< css::text::XTextRange >& rStart); + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + SwVbaRange( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& rTextDocument, const css::uno::Reference< css::text::XTextRange >& rStart, const css::uno::Reference< css::text::XTextRange >& rEnd ); + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + SwVbaRange( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& rTextDocument, const css::uno::Reference< css::text::XTextRange >& rStart, const css::uno::Reference< css::text::XTextRange >& rEnd, const css::uno::Reference< css::text::XText >& rText); + virtual ~SwVbaRange() override; + const css::uno::Reference< css::text::XTextDocument >& getDocument() const { return mxTextDocument; } + + virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getXTextRange() override; + const css::uno::Reference< css::text::XText >& getXText() const { return mxText; } + void setXTextCursor( const css::uno::Reference< css::text::XTextCursor >& xTextCursor ) { mxTextCursor = xTextCursor; } + + // Attribute + virtual OUString SAL_CALL getText() override; + virtual void SAL_CALL setText( const OUString& rText ) override; + virtual css::uno::Reference< ooo::vba::word::XParagraphFormat > SAL_CALL getParagraphFormat() override; + virtual void SAL_CALL setParagraphFormat( const css::uno::Reference< ooo::vba::word::XParagraphFormat >& rParagraphFormat ) override; + virtual css::uno::Any SAL_CALL getStyle() override; + virtual void SAL_CALL setStyle( const css::uno::Any& _xStyle ) override; + + virtual css::uno::Reference< ooo::vba::word::XFont > SAL_CALL getFont() override; + virtual css::uno::Reference< ooo::vba::word::XListFormat > SAL_CALL getListFormat() override; + // Methods + virtual void SAL_CALL InsertBreak(const css::uno::Any& _breakType) override; + virtual void SAL_CALL Select() override; + virtual void SAL_CALL InsertParagraph() override; + virtual void SAL_CALL InsertParagraphBefore() override; + virtual void SAL_CALL InsertParagraphAfter() override; + virtual ::sal_Int32 SAL_CALL getLanguageID() override; + virtual void SAL_CALL setLanguageID( ::sal_Int32 _languageid ) override; + virtual css::uno::Any SAL_CALL PageSetup() override; + virtual ::sal_Int32 SAL_CALL getStart() override; + virtual void SAL_CALL setStart( ::sal_Int32 _start ) override; + virtual ::sal_Int32 SAL_CALL getEnd() override; + virtual void SAL_CALL setEnd( ::sal_Int32 _end ) override; + virtual sal_Bool SAL_CALL InRange( const css::uno::Reference< ::ooo::vba::word::XRange >& Range ) override; + virtual css::uno::Any SAL_CALL Revisions( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Sections( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Fields( const css::uno::Any& aIndex ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBARANGE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarangehelper.cxx b/sw/source/ui/vba/vbarangehelper.cxx new file mode 100644 index 000000000..3812fbfa0 --- /dev/null +++ b/sw/source/ui/vba/vbarangehelper.cxx @@ -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 . + */ +#include "vbarangehelper.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +/** + * get a range in a xText by creating + * a cursor that iterates over the text. If the iterating cursor is + * equal to the desired position, the range equivalent is returned. + * Some special cases are tables that are inside of the text, because the + * position has to be adjusted. + * @param xText a text where a range position is searched + * @param position a position inside o the text + * @return a range for the position; null is returned if no range can be + * constructed. + */ +uno::Reference< text::XTextRange > SwVbaRangeHelper::getRangeByPosition( const uno::Reference< text::XText >& rText, sal_Int32 _position ) +{ + uno::Reference< text::XTextRange > xRange; + if( rText.is() ) + { + sal_Int32 nPos = 0; + uno::Reference< text::XTextCursor > xCursor = rText->createTextCursor(); + xCursor->collapseToStart(); + bool bCanGo = true; + while( !xRange.is() && bCanGo ) + { + if( _position == nPos ) + { + xRange = xCursor->getStart(); + } + else + { + bCanGo = xCursor->goRight( 1, false ); + nPos++; + } + } + } + return xRange; +} + +void SwVbaRangeHelper::insertString( uno::Reference< text::XTextRange > const & rTextRange, uno::Reference< text::XText > const & rText, const OUString& rStr, bool _bAbsorb ) +{ + sal_Int32 nlastIndex = 0; + sal_Int32 nIndex = 0; + uno::Reference< text::XTextRange > xRange = rTextRange; + + while(( nIndex = rStr.indexOf('\n', nlastIndex)) >= 0 ) + { + xRange = xRange->getEnd(); + if( nlastIndex < ( nIndex - 1 ) ) + { + rText->insertString( xRange, rStr.copy( nlastIndex, ( nIndex - 1 - nlastIndex ) ), _bAbsorb ); + xRange = xRange->getEnd(); + } + + rText->insertControlCharacter( xRange, text::ControlCharacter::PARAGRAPH_BREAK, _bAbsorb ); + nlastIndex = nIndex + 1; + } + + if( nlastIndex < rStr.getLength() ) + { + xRange = xRange->getEnd(); + + OUString aWatt = rStr.copy( nlastIndex ); + rText->insertString( xRange, aWatt, _bAbsorb ); + } +} + +uno::Reference< text::XTextCursor > SwVbaRangeHelper::initCursor( const uno::Reference< text::XTextRange >& rTextRange, + const uno::Reference< text::XText >& rText ) +{ + uno::Reference< text::XTextCursor > xTextCursor; + bool bGotTextCursor = false; + + try + { + xTextCursor = rText->createTextCursorByRange( rTextRange ); + bGotTextCursor = true; + } + catch (const uno::Exception& e) + { + DebugHelper::basicexception(e); + } + + if( !bGotTextCursor || !xTextCursor.is() ) + { + try + { + uno::Reference< text::XText > xText = rTextRange->getText(); + xTextCursor = xText->createTextCursor(); + bGotTextCursor = true; + } + catch (const uno::Exception& e) + { + DebugHelper::basicexception(e); + } + } + + if( !bGotTextCursor || !xTextCursor.is() ) + { + try + { + xTextCursor = rText->createTextCursor(); + } + catch (const uno::Exception& e) + { + DebugHelper::basicexception(e); + } + } + return xTextCursor; +} + +sal_Int32 SwVbaRangeHelper::getPosition( const uno::Reference< text::XText >& rText, const uno::Reference< text::XTextRange >& rTextRange ) +{ + sal_Int32 nPosition = -1; + if( rText.is() && rTextRange.is() ) + { + nPosition = 0; + uno::Reference< text::XTextCursor > xCursor = rText->createTextCursor(); + xCursor->collapseToStart(); + uno::Reference< text::XTextRangeCompare > xCompare( rText, uno::UNO_QUERY_THROW ); + // compareValue is 0 if the ranges are equal + sal_Int32 nCompareValue = xCompare->compareRegionStarts( xCursor->getStart(), rTextRange ); + bool canGo = true; + + while( nCompareValue !=0 && canGo ) + { + canGo = xCursor->goRight( 1, false ); + nCompareValue = xCompare->compareRegionStarts( xCursor->getStart(), rTextRange ); + nPosition++; + } + + // check fails: no correct position found + if( !canGo && nCompareValue != 0 ) + { + nPosition = -1; + } + } + + return nPosition; +} + +uno::Reference< text::XTextContent > SwVbaRangeHelper::findBookmarkByPosition( const uno::Reference< text::XTextDocument >& xTextDoc, const uno::Reference< text::XTextRange >& xTextRange ) +{ + uno::Reference< text::XBookmarksSupplier > xBookmarksSupplier( xTextDoc, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xIndexAccess( xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY_THROW ); + for( sal_Int32 index = 0; index < xIndexAccess->getCount(); index++ ) + { + uno::Reference< text::XTextContent > xBookmark( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xBkAnchor = xBookmark->getAnchor(); + uno::Reference< text::XTextRangeCompare > xCompare( xBkAnchor->getText(), uno::UNO_QUERY_THROW ); + if( xCompare->compareRegionStarts( xBkAnchor->getStart(), xBkAnchor->getEnd() ) == 0 ) + { + try + { + if( xCompare->compareRegionStarts( xTextRange, xBkAnchor->getStart() ) == 0 ) + return xBookmark; + } + catch (const uno::Exception&) + { + continue; + } + } + } + return uno::Reference< text::XTextContent >(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarangehelper.hxx b/sw/source/ui/vba/vbarangehelper.hxx new file mode 100644 index 000000000..7ea5ed579 --- /dev/null +++ b/sw/source/ui/vba/vbarangehelper.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_UI_VBA_VBARANGEHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBARANGEHELPER_HXX + +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextDocument.hpp> + +class SwVbaRangeHelper +{ +public: + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::text::XTextRange > getRangeByPosition( const css::uno::Reference< css::text::XText >& rText, sal_Int32 _position ); + /// @throws css::uno::RuntimeException + static void insertString( css::uno::Reference< css::text::XTextRange > const & rTextRange, css::uno::Reference< css::text::XText > const & rText, const OUString& rStr, bool _bAbsorb ); + /// @throws css::uno::RuntimeException + /// @throws css::script::BasicErrorException + static css::uno::Reference< css::text::XTextCursor > initCursor( const css::uno::Reference< css::text::XTextRange >& rTextRange, const css::uno::Reference< css::text::XText >& rText ); + /// @throws css::uno::RuntimeException + static sal_Int32 getPosition( const css::uno::Reference< css::text::XText >& rText, const css::uno::Reference< css::text::XTextRange >& rTextRange ); + /// @throws css::uno::RuntimeException + static css::uno::Reference< css::text::XTextContent > findBookmarkByPosition( const css::uno::Reference< css::text::XTextDocument >& xTextDoc, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBARANGEHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbareplacement.cxx b/sw/source/ui/vba/vbareplacement.cxx new file mode 100644 index 000000000..6aab38b59 --- /dev/null +++ b/sw/source/ui/vba/vbareplacement.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 "vbareplacement.hxx" +#include <vbahelper/vbahelper.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaReplacement::SwVbaReplacement( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< util::XPropertyReplace >& xPropertyReplace ) : + SwVbaReplacement_BASE( rParent, rContext ), mxPropertyReplace( xPropertyReplace ) +{ +} + +SwVbaReplacement::~SwVbaReplacement() +{ +} + +OUString SAL_CALL SwVbaReplacement::getText() +{ + return mxPropertyReplace->getReplaceString(); +} + +void SAL_CALL SwVbaReplacement::setText( const OUString& _text ) +{ + mxPropertyReplace->setReplaceString( _text ); +} + +void SAL_CALL SwVbaReplacement::ClearFormatting( ) +{ + uno::Sequence< beans::PropertyValue > aPropValues; + mxPropertyReplace->setReplaceAttributes( aPropValues ); +} + +OUString +SwVbaReplacement::getServiceImplName() +{ + return "SwVbaReplacement"; +} + +uno::Sequence< OUString > +SwVbaReplacement::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Replacement" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbareplacement.hxx b/sw/source/ui/vba/vbareplacement.hxx new file mode 100644 index 000000000..22f37910e --- /dev/null +++ b/sw/source/ui/vba/vbareplacement.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_UI_VBA_VBAREPLACEMENT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAREPLACEMENT_HXX + +#include <ooo/vba/word/XReplacement.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/util/XPropertyReplace.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XReplacement > SwVbaReplacement_BASE; + +class SwVbaReplacement : public SwVbaReplacement_BASE +{ +private: + css::uno::Reference< css::util::XPropertyReplace> mxPropertyReplace; + +public: + /// @throws css::uno::RuntimeException + SwVbaReplacement( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::util::XPropertyReplace >& xPropertyReplace ); + virtual ~SwVbaReplacement() override; + + // Attributes + virtual OUString SAL_CALL getText() override; + virtual void SAL_CALL setText( const OUString& _text ) override; + + //Methods + virtual void SAL_CALL ClearFormatting() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAREPLACEMENT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarevision.cxx b/sw/source/ui/vba/vbarevision.cxx new file mode 100644 index 000000000..d0439a8b3 --- /dev/null +++ b/sw/source/ui/vba/vbarevision.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 "vbarevision.hxx" +#include <vbahelper/vbahelper.hxx> +#include <sal/log.hxx> +#include <com/sun/star/document/XRedlinesSupplier.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include "wordvbahelper.hxx" +#include <docsh.hxx> +#include <doc.hxx> +#include <IDocumentRedlineAccess.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaRevision::SwVbaRevision( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& xRedlineProps ) : SwVbaRevision_BASE( rParent, rContext ), mxModel( xModel ), mxRedlineProps( xRedlineProps ) +{ +} + +SwVbaRevision::~SwVbaRevision() +{ +} + +sal_Int32 SwVbaRevision::GetPosition() +{ + sal_Int32 nPos = -1; + uno::Reference< document::XRedlinesSupplier > xRedlinesSupp( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xRedlines( xRedlinesSupp->getRedlines(), uno::UNO_QUERY_THROW ); + sal_Int32 nCount = xRedlines->getCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< beans::XPropertySet > xProps( xRedlines->getByIndex( i ), uno::UNO_QUERY_THROW ); + if( xProps == mxRedlineProps ) + { + nPos = i; + SAL_INFO("sw.ui", "the redline position is " << nPos); + break; + } + } + if( nPos == -1 ) + throw uno::RuntimeException(); + + return nPos; +} + +void SAL_CALL +SwVbaRevision::Accept() +{ + SwDoc* pDoc = word::getDocShell( mxModel )->GetDoc(); + if( pDoc ) + pDoc->getIDocumentRedlineAccess().AcceptRedline( GetPosition(), true ); +} + +void SAL_CALL +SwVbaRevision::Reject( ) +{ + SwDoc* pDoc = word::getDocShell( mxModel )->GetDoc(); + if( pDoc ) + pDoc->getIDocumentRedlineAccess().RejectRedline( GetPosition(), true ); +} + +OUString +SwVbaRevision::getServiceImplName() +{ + return "SwVbaRevision"; +} + +uno::Sequence< OUString > +SwVbaRevision::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Revision" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarevision.hxx b/sw/source/ui/vba/vbarevision.hxx new file mode 100644 index 000000000..77561a42d --- /dev/null +++ b/sw/source/ui/vba/vbarevision.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_UI_VBA_VBAREVISION_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAREVISION_HXX + +#include <ooo/vba/word/XRevision.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XRevision > SwVbaRevision_BASE; + +class SwVbaRevision : public SwVbaRevision_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::beans::XPropertySet > mxRedlineProps; + +private: + /// @throws css::uno::RuntimeException + sal_Int32 GetPosition(); + +public: + /// @throws css::uno::RuntimeException + SwVbaRevision( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::beans::XPropertySet >& xRedlineProps ); + virtual ~SwVbaRevision() override; + + // Methods + virtual void SAL_CALL Accept( ) override; + virtual void SAL_CALL Reject( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAREVISION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarevisions.cxx b/sw/source/ui/vba/vbarevisions.cxx new file mode 100644 index 000000000..37e078176 --- /dev/null +++ b/sw/source/ui/vba/vbarevisions.cxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbarevisions.hxx" +#include "vbarevision.hxx" +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XRedlinesSupplier.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XTextRangeCompare.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +typedef std::vector< uno::Reference< beans::XPropertySet > > RevisionMap; + +namespace { + +class RedlinesEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration > +{ + RevisionMap mRevisionMap; + RevisionMap::iterator mIt; +public: + explicit RedlinesEnumeration( const RevisionMap& sMap ) : mRevisionMap( sMap ), mIt( mRevisionMap.begin() ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( mIt != mRevisionMap.end() ); + } + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( !hasMoreElements() ) + throw container::NoSuchElementException(); + uno::Reference< beans::XPropertySet > xRevision( *mIt++ ); + return uno::makeAny( xRevision ) ; + } +}; + +class RevisionCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ + RevisionMap mRevisionMap; +public: +/// @throws css::uno::RuntimeException +RevisionCollectionHelper( const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ); + + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<beans::XPropertySet>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return ( !mRevisionMap.empty() ); } + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override { return mRevisionMap.size(); } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + return uno::makeAny( mRevisionMap[ Index ] ); + + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new RedlinesEnumeration( mRevisionMap ); + } +}; + +} + +RevisionCollectionHelper::RevisionCollectionHelper( const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ) + { + uno::Reference< text::XTextRangeCompare > xTRC( xTextRange->getText(), uno::UNO_QUERY_THROW ); + uno::Reference< document::XRedlinesSupplier > xRedlinesSupp( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xRedlines( xRedlinesSupp->getRedlines(), uno::UNO_QUERY_THROW ); + sal_Int32 nCount = xRedlines->getCount(); + for( sal_Int32 index = 0; index < nCount; index++ ) + { + uno::Reference< text::XTextRange > xRedlineRange( xRedlines->getByIndex( index ), uno::UNO_QUERY_THROW ); + if( xTRC->compareRegionStarts( xTextRange, xRedlineRange ) >= 0 && xTRC->compareRegionEnds( xTextRange, xRedlineRange ) <= 0 ) + { + uno::Reference< beans::XPropertySet > xRedlineProps( xRedlineRange, uno::UNO_QUERY_THROW ); + mRevisionMap.push_back( xRedlineProps ); + } + } + } + +namespace { + +class RevisionsEnumeration : public EnumerationHelperImpl +{ + uno::Reference< frame::XModel > m_xModel; +public: + /// @throws uno::RuntimeException + RevisionsEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, const uno::Reference< frame::XModel >& xModel ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), m_xModel( xModel ) {} + + virtual uno::Any SAL_CALL nextElement( ) override + { + uno::Reference< beans::XPropertySet > xRevision( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XRevision > ( new SwVbaRevision( m_xParent, m_xContext, m_xModel, xRevision ) ) ); + } + +}; + +} + +SwVbaRevisions::SwVbaRevisions( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ): SwVbaRevisions_BASE( xParent, xContext, new RevisionCollectionHelper( xModel, xTextRange ) ), mxModel( xModel ) +{ +} + +SwVbaRevisions::SwVbaRevisions( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< container::XIndexAccess >& xIndexAccess ): SwVbaRevisions_BASE( xParent, xContext, xIndexAccess ), mxModel( xModel ) +{ +} + +// XEnumerationAccess +uno::Type +SwVbaRevisions::getElementType() +{ + return cppu::UnoType<word::XRevision>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaRevisions::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return new RevisionsEnumeration( this, mxContext, xEnumAccess->createEnumeration(), mxModel ); +} + +uno::Any +SwVbaRevisions::createCollectionObject( const css::uno::Any& aSource ) +{ + uno::Reference< beans::XPropertySet > xRevision( aSource, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XRevision > ( new SwVbaRevision( this, mxContext, mxModel, xRevision ) ) ); +} + +void SAL_CALL SwVbaRevisions::AcceptAll( ) +{ + // First we need to put all the redline into a vector, because if the redline is accepted, + // it will auto delete in the document. + std::vector< uno::Reference< word::XRevision > > aRevisions; + uno::Reference< container::XEnumeration > xEnumeration = createEnumeration(); + while( xEnumeration->hasMoreElements() ) + { + uno::Reference< word::XRevision > xRevision( xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + aRevisions.push_back( xRevision ); + } + + for( const auto& xRevision : aRevisions ) + xRevision->Accept(); +} + +void SAL_CALL SwVbaRevisions::RejectAll( ) +{ + throw uno::RuntimeException(); +} + +OUString +SwVbaRevisions::getServiceImplName() +{ + return "SwVbaRevisions"; +} + +css::uno::Sequence<OUString> +SwVbaRevisions::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Revisions" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarevisions.hxx b/sw/source/ui/vba/vbarevisions.hxx new file mode 100644 index 000000000..7cf2f54a5 --- /dev/null +++ b/sw/source/ui/vba/vbarevisions.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_UI_VBA_VBAREVISIONS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAREVISIONS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XRevisions.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XRevisions > SwVbaRevisions_BASE; + +class SwVbaRevisions : public SwVbaRevisions_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + +public: + SwVbaRevisions( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + + SwVbaRevisions( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::container::XIndexAccess >& xIndexAccess ); + + // Methods + virtual void SAL_CALL AcceptAll( ) override; + virtual void SAL_CALL RejectAll( ) override; + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaRevisions_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAREVISIONS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarow.cxx b/sw/source/ui/vba/vbarow.cxx new file mode 100644 index 000000000..4ce00b716 --- /dev/null +++ b/sw/source/ui/vba/vbarow.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 "vbarow.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <ooo/vba/word/WdRowHeightRule.hpp> +#include <ooo/vba/word/WdConstants.hpp> +#include "vbatablehelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaRow::SwVbaRow( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext,const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nIndex ) : + SwVbaRow_BASE( rParent, rContext ), mxTextTable( xTextTable ), mnIndex( nIndex ) +{ + mxTableRows = mxTextTable->getRows(); + mxRowProps.set( mxTableRows->getByIndex( mnIndex ), uno::UNO_QUERY_THROW ); +} + +SwVbaRow::~SwVbaRow() +{ +} + +uno::Any SAL_CALL SwVbaRow::getHeight() +{ + if( getHeightRule() == word::WdRowHeightRule::wdRowHeightAuto ) + return uno::makeAny( sal_Int32( word::WdConstants::wdUndefined ) ); + + sal_Int32 nHeight = 0; + mxRowProps->getPropertyValue("Height") >>= nHeight; + return uno::makeAny( static_cast<float>(Millimeter::getInPoints( nHeight )) ); +} + +void SAL_CALL SwVbaRow::setHeight( const uno::Any& _height ) +{ + float height = 0; + _height >>= height; + + sal_Int32 nHeight = Millimeter::getInHundredthsOfOneMillimeter( height ); + mxRowProps->setPropertyValue("Height", uno::makeAny( nHeight ) ); +} + +::sal_Int32 SAL_CALL SwVbaRow::getHeightRule() +{ + bool isAutoHeight = false; + mxRowProps->getPropertyValue("IsAutoHeight") >>= isAutoHeight; + return isAutoHeight ? word::WdRowHeightRule::wdRowHeightAuto : word::WdRowHeightRule::wdRowHeightExactly; +} + +void SAL_CALL SwVbaRow::setHeightRule( ::sal_Int32 _heightrule ) +{ + bool isAutoHeight = ( _heightrule == word::WdRowHeightRule::wdRowHeightAuto ); + mxRowProps->setPropertyValue("IsAutoHeight", uno::makeAny( isAutoHeight ) ); +} + +void SAL_CALL +SwVbaRow::Select( ) +{ + SelectRow( getCurrentWordDoc(mxContext), mxTextTable, mnIndex, mnIndex ); +} + +void SwVbaRow::SelectRow( const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextTable >& xTextTable, sal_Int32 nStartRow, sal_Int32 nEndRow ) +{ + OUString sRangeName = "A" + OUString::number(nStartRow + 1); + SwVbaTableHelper aTableHelper( xTextTable ); + sal_Int32 nColCount = aTableHelper.getTabColumnsCount( nEndRow ); + // FIXME: the column count > 26 + //char cCol = 'A' + nColCount - 1; + OUString sCol = SwVbaTableHelper::getColumnStr( nColCount - 1); + sRangeName += ":" + sCol + OUString::number(nEndRow + 1); + + uno::Reference< table::XCellRange > xCellRange( xTextTable, uno::UNO_QUERY_THROW ); + uno::Reference< table::XCellRange > xSelRange = xCellRange->getCellRangeByName( sRangeName ); + + uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xSelection->select( uno::makeAny( xSelRange ) ); +} + +void SAL_CALL SwVbaRow::SetHeight( float height, sal_Int32 heightrule ) +{ + setHeightRule( heightrule ); + setHeight( uno::makeAny( height ) ); +} + +OUString +SwVbaRow::getServiceImplName() +{ + return "SwVbaRow"; +} + +uno::Sequence< OUString > +SwVbaRow::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Row" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarow.hxx b/sw/source/ui/vba/vbarow.hxx new file mode 100644 index 000000000..7c6e6f3cc --- /dev/null +++ b/sw/source/ui/vba/vbarow.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_UI_VBA_VBAROW_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAROW_HXX + +#include <ooo/vba/word/XRow.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/table/XTableRows.hpp> +#include <com/sun/star/text/XTextTable.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XRow > SwVbaRow_BASE; + +class SwVbaRow : public SwVbaRow_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + css::uno::Reference< css::table::XTableRows > mxTableRows; + css::uno::Reference< css::beans::XPropertySet > mxRowProps; + sal_Int32 mnIndex; + +public: + /// @throws css::uno::RuntimeException + SwVbaRow( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nIndex ); + virtual ~SwVbaRow() override; + + // Attributes + virtual css::uno::Any SAL_CALL getHeight() override; + virtual void SAL_CALL setHeight( const css::uno::Any& _height ) override; + virtual ::sal_Int32 SAL_CALL getHeightRule() override; + virtual void SAL_CALL setHeightRule( ::sal_Int32 _heightrule ) override; + + // Methods + virtual void SAL_CALL Select( ) override; + virtual void SAL_CALL SetHeight( float height, sal_Int32 heightrule ) override; + + /// @throws css::uno::RuntimeException + static void SelectRow( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextTable >& xTextTable, sal_Int32 nStartRow, sal_Int32 nEndRow ); + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAROW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarows.cxx b/sw/source/ui/vba/vbarows.cxx new file mode 100644 index 000000000..08afc401e --- /dev/null +++ b/sw/source/ui/vba/vbarows.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 "vbarows.hxx" +#include "vbarow.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <ooo/vba/word/WdRowAlignment.hpp> +#include <ooo/vba/word/WdConstants.hpp> +#include <ooo/vba/word/WdRulerStyle.hpp> +#include <basic/sberrors.hxx> +#include "vbacolumns.hxx" +#include "vbatablehelper.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class RowsEnumWrapper : public EnumerationHelper_BASE +{ + uno::WeakReference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< text::XTextTable > mxTextTable; + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 nIndex; + +public: + RowsEnumWrapper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< text::XTextTable >& xTextTable ) : mxParent( xParent ), mxContext( xContext ), mxTextTable( xTextTable ), nIndex( 0 ) + { + mxIndexAccess = mxTextTable->getRows(); + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if( nIndex < mxIndexAccess->getCount() ) + { + return uno::makeAny( uno::Reference< word::XRow > ( new SwVbaRow( mxParent, mxContext, mxTextTable, nIndex++ ) ) ); + } + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaRows::SwVbaRows( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextTable >& xTextTable, const uno::Reference< table::XTableRows >& xTableRows ) : SwVbaRows_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( xTableRows, uno::UNO_QUERY_THROW ) ), mxTextTable( xTextTable ), mxTableRows( xTableRows ) +{ + mnStartRowIndex = 0; + mnEndRowIndex = m_xIndexAccess->getCount() - 1; +} + +SwVbaRows::SwVbaRows( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextTable >& xTextTable, const uno::Reference< table::XTableRows >& xTableRows, sal_Int32 nStarIndex, sal_Int32 nEndIndex ) : SwVbaRows_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( xTableRows, uno::UNO_QUERY_THROW ) ), mxTextTable( xTextTable ), mxTableRows( xTableRows ), mnStartRowIndex( nStarIndex ), mnEndRowIndex( nEndIndex ) +{ + if( mnEndRowIndex < mnStartRowIndex ) + throw uno::RuntimeException(); +} + +/** + * get the alignment of the rows: SO format com.sun.star.text.HoriOrientation + * is mapped to WdRowAlignment in Word + * @return the alignment + */ +::sal_Int32 SAL_CALL SwVbaRows::getAlignment() +{ + sal_Int16 nAlignment = text::HoriOrientation::LEFT; + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + xTableProps->getPropertyValue("HoriOrient") >>= nAlignment; + sal_Int32 nRet = 0; + switch( nAlignment ) + { + case text::HoriOrientation::CENTER: + { + nRet = word::WdRowAlignment::wdAlignRowCenter; + break; + } + case text::HoriOrientation::RIGHT: + { + nRet = word::WdRowAlignment::wdAlignRowRight; + break; + } + default: + { + nRet = word::WdRowAlignment::wdAlignRowLeft; + } + } + return nRet; +} + +void SAL_CALL SwVbaRows::setAlignment( ::sal_Int32 _alignment ) +{ + sal_Int16 nAlignment = text::HoriOrientation::LEFT; + switch( _alignment ) + { + case word::WdRowAlignment::wdAlignRowCenter: + { + nAlignment = text::HoriOrientation::CENTER; + break; + } + case word::WdRowAlignment::wdAlignRowRight: + { + nAlignment = text::HoriOrientation::RIGHT; + break; + } + default: + { + nAlignment = text::HoriOrientation::LEFT; + } + } + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + xTableProps->setPropertyValue("HoriOrient", uno::makeAny( nAlignment ) ); +} + +uno::Any SAL_CALL SwVbaRows::getAllowBreakAcrossPages() +{ + bool bAllowBreak = false; + uno::Reference< container::XIndexAccess > xRowsAccess( mxTableRows, uno::UNO_QUERY_THROW ); + for( sal_Int32 index = mnStartRowIndex; index <= mnEndRowIndex; ++index ) + { + uno::Reference< beans::XPropertySet > xRowProps( xRowsAccess->getByIndex( index ), uno::UNO_QUERY_THROW ); + bool bSplit = false; + xRowProps->getPropertyValue("IsSplitAllowed") >>= bSplit; + if( index == 0 ) + { + bAllowBreak = bSplit; + } + if( bSplit != bAllowBreak ) + { + return uno::makeAny( sal_Int32(word::WdConstants::wdUndefined) ); + } + } + return uno::makeAny( bAllowBreak ); +} + +void SAL_CALL SwVbaRows::setAllowBreakAcrossPages( const uno::Any& _allowbreakacrosspages ) +{ + bool bAllowBreak = false; + _allowbreakacrosspages >>= bAllowBreak; + uno::Reference< container::XIndexAccess > xRowsAccess( mxTableRows, uno::UNO_QUERY_THROW ); + for( sal_Int32 index = mnStartRowIndex; index <= mnEndRowIndex; ++index ) + { + uno::Reference< beans::XPropertySet > xRowProps( xRowsAccess->getByIndex( index ), uno::UNO_QUERY_THROW ); + xRowProps->setPropertyValue("IsSplitAllowed", uno::makeAny( bAllowBreak ) ); + } +} + +float SAL_CALL SwVbaRows::getSpaceBetweenColumns() +{ + // just get the first spacing of the first cell + uno::Reference< table::XCellRange > xCellRange( mxTextTable, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xCellProps( xCellRange->getCellByPosition( 0, mnStartRowIndex ), uno::UNO_QUERY_THROW ); + sal_Int32 nLeftBorderDistance = 0; + sal_Int32 nRightBorderDistance = 0; + xCellProps->getPropertyValue("LeftBorderDistance") >>= nLeftBorderDistance; + xCellProps->getPropertyValue("RightBorderDistance") >>= nRightBorderDistance; + return static_cast< float >( Millimeter::getInPoints( nLeftBorderDistance + nRightBorderDistance ) ); +} + +void SAL_CALL SwVbaRows::setSpaceBetweenColumns( float _spacebetweencolumns ) +{ + sal_Int32 nSpace = Millimeter::getInHundredthsOfOneMillimeter( _spacebetweencolumns ) / 2; + uno::Reference< container::XIndexAccess > xColumnAccess( mxTextTable->getColumns(), uno::UNO_QUERY_THROW ); + uno::Reference< table::XCellRange > xCellRange( mxTextTable, uno::UNO_QUERY_THROW ); + SwVbaTableHelper aTableHelper( mxTextTable ); + for( sal_Int32 row = mnStartRowIndex; row <= mnEndRowIndex; ++row ) + { + sal_Int32 nColumns = aTableHelper.getTabColumnsCount( row ); + for( sal_Int32 column = 0; column < nColumns; ++column ) + { + uno::Reference< beans::XPropertySet > xCellProps( xCellRange->getCellByPosition( column, row ), uno::UNO_QUERY_THROW ); + xCellProps->setPropertyValue("LeftBorderDistance", uno::makeAny( nSpace ) ); + xCellProps->setPropertyValue("RightBorderDistance", uno::makeAny( nSpace ) ); + } + } +} + +void SAL_CALL SwVbaRows::Delete( ) +{ + mxTableRows->removeByIndex( mnStartRowIndex, getCount() ); +} + +void SAL_CALL SwVbaRows::SetLeftIndent( float LeftIndent, ::sal_Int32 RulerStyle ) +{ + uno::Reference< word::XColumns > xColumns( new SwVbaColumns( getParent(), mxContext, mxTextTable, mxTextTable->getColumns() ) ); + sal_Int32 nIndent = static_cast<sal_Int32>(LeftIndent); + switch( RulerStyle ) + { + case word::WdRulerStyle::wdAdjustFirstColumn: + { + setIndentWithAdjustFirstColumn( xColumns, nIndent ); + break; + } + case word::WdRulerStyle::wdAdjustNone: + { + setIndentWithAdjustNone( nIndent ); + break; + } + case word::WdRulerStyle::wdAdjustProportional: + { + setIndentWithAdjustProportional( xColumns, nIndent ); + break; + } + case word::WdRulerStyle::wdAdjustSameWidth: + { + setIndentWithAdjustSameWidth( xColumns, nIndent ); + break; + } + default: + { + DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_ARGUMENT); + } + } +} + +void SwVbaRows::setIndentWithAdjustNone( sal_Int32 indent ) +{ + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + sal_Int32 nMargin = 0; + xTableProps->getPropertyValue("LeftMargin") >>= nMargin; + nMargin += indent; + xTableProps->setPropertyValue("LeftMargin", uno::makeAny( nMargin ) ); +} + + void SwVbaRows::setIndentWithAdjustFirstColumn( const uno::Reference< word::XColumns >& xColumns, sal_Int32 indent ) + { + uno::Reference< XCollection > xCol( xColumns, uno::UNO_QUERY_THROW ); + uno::Reference< word::XColumn > xColumn( xCol->Item( uno::makeAny( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW ); + sal_Int32 nWidth = xColumn->getWidth(); + nWidth -= indent; + xColumn->setWidth( nWidth ); + setIndentWithAdjustNone( indent ); + } + + void SwVbaRows::setIndentWithAdjustProportional( + const uno::Reference< word::XColumns >& xColumns, + sal_Int32 indent +) + { + // calculate the new width and get the proportion between old and new + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + sal_Int32 nWidth = 0; + xTableProps->getPropertyValue("Width") >>= nWidth; + sal_Int32 nNewWidth = nWidth - indent; + if ((nNewWidth <= 0) || (nWidth <= 0)) + { + throw uno::RuntimeException( + "Pb with width, in SwVbaRows::setIndentWithAdjustProportional " + "(nNewWidth <= 0) || (nWidth <= 0)" + ); + } + double propFactor = static_cast<double>(nNewWidth)/static_cast<double>(nWidth); + + // get all columns, calculate and set the new width of the columns + uno::Reference< XCollection > xCol( xColumns, uno::UNO_QUERY_THROW ); + sal_Int32 nColCount = xCol->getCount(); + for( sal_Int32 i = 0; i < nColCount; i++ ) + { + uno::Reference< word::XColumn > xColumn( xCol->Item( uno::makeAny( i ), uno::Any() ), uno::UNO_QUERY_THROW ); + sal_Int32 nColWidth = xColumn->getWidth(); + sal_Int32 nNewColWidth = static_cast<sal_Int32>( propFactor * nColWidth ); + xColumn->setWidth( nNewColWidth ); + } + + // set the width and position of the table + setIndentWithAdjustNone( indent ); + xTableProps->setPropertyValue("Width", uno::makeAny( nNewWidth ) ); + } + + void SwVbaRows::setIndentWithAdjustSameWidth( const uno::Reference< word::XColumns >& xColumns, sal_Int32 indent ) + { + // calculate the new width and get the width of all columns + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + sal_Int32 nWidth = 0; + xTableProps->getPropertyValue("Width") >>= nWidth; + sal_Int32 nNewWidth = nWidth - indent; + + // get all columns, calculate and set the new width of the columns + uno::Reference< XCollection > xCol( xColumns, uno::UNO_QUERY_THROW ); + sal_Int32 nColCount = xCol->getCount(); + sal_Int32 nNewColWidth = static_cast<sal_Int32>( double( nNewWidth )/nColCount ); + for( sal_Int32 i = 0; i < nColCount; i++ ) + { + uno::Reference< word::XColumn > xColumn( xCol->Item( uno::makeAny( i ), uno::Any() ), uno::UNO_QUERY_THROW ); + xColumn->setWidth( nNewColWidth ); + } + + // set the width and position of the table + setIndentWithAdjustNone( indent ); + xTableProps->setPropertyValue("Width", uno::makeAny( nNewWidth ) ); + } + +void SAL_CALL SwVbaRows::Select( ) +{ + SwVbaRow::SelectRow( getCurrentWordDoc(mxContext), mxTextTable, mnStartRowIndex, mnEndRowIndex ); +} + +::sal_Int32 SAL_CALL SwVbaRows::getCount() +{ + return ( mnEndRowIndex - mnStartRowIndex + 1 ); +} + +uno::Any SAL_CALL SwVbaRows::Item( const uno::Any& Index1, const uno::Any& /*not processed in this base class*/ ) +{ + sal_Int32 nIndex = 0; + if( Index1 >>= nIndex ) + { + if( nIndex <= 0 || nIndex > getCount() ) + { + throw lang::IndexOutOfBoundsException("Index out of bounds" ); + } + return uno::makeAny( uno::Reference< word::XRow >( new SwVbaRow( this, mxContext, mxTextTable, nIndex - 1 ) ) ); + } + throw uno::RuntimeException("Index out of bounds" ); +} + +// XEnumerationAccess +uno::Type +SwVbaRows::getElementType() +{ + return cppu::UnoType<word::XRow>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaRows::createEnumeration() +{ + return new RowsEnumWrapper( this, mxContext, mxTextTable ); +} + +uno::Any +SwVbaRows::createCollectionObject( const uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaRows::getServiceImplName() +{ + return "SwVbaRows"; +} + +uno::Sequence<OUString> +SwVbaRows::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Rows" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbarows.hxx b/sw/source/ui/vba/vbarows.hxx new file mode 100644 index 000000000..54e76ec92 --- /dev/null +++ b/sw/source/ui/vba/vbarows.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAROWS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAROWS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XRows.hpp> +#include <ooo/vba/word/XColumns.hpp> +#include <com/sun/star/table/XTableRows.hpp> +#include <com/sun/star/text/XTextTable.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XRows > SwVbaRows_BASE; + +class SwVbaRows : public SwVbaRows_BASE +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + css::uno::Reference< css::table::XTableRows > mxTableRows; + sal_Int32 mnStartRowIndex; + sal_Int32 mnEndRowIndex; + +private: + /// @throws css::uno::RuntimeException + void setIndentWithAdjustNone( sal_Int32 indent ); + /// @throws css::uno::RuntimeException + void setIndentWithAdjustFirstColumn( const css::uno::Reference< ooo::vba::word::XColumns >& xColumns, sal_Int32 indent ); + /// @throws css::uno::RuntimeException + void setIndentWithAdjustProportional( const css::uno::Reference< ooo::vba::word::XColumns >& xColumns, sal_Int32 indent ); + /// @throws css::uno::RuntimeException + void setIndentWithAdjustSameWidth( const css::uno::Reference< ooo::vba::word::XColumns >& xColumns, sal_Int32 indent ); + +public: + /// @throws css::uno::RuntimeException + SwVbaRows( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, const css::uno::Reference< css::table::XTableRows >& xTableRows ); + /// @throws css::uno::RuntimeException + SwVbaRows( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextTable >& xTextTable, const css::uno::Reference< css::table::XTableRows >& xTableRows, sal_Int32 nStarIndex, sal_Int32 nEndIndex ); + + // Attributes + virtual ::sal_Int32 SAL_CALL getAlignment() override; + virtual void SAL_CALL setAlignment( ::sal_Int32 _alignment ) override; + virtual css::uno::Any SAL_CALL getAllowBreakAcrossPages() override; + virtual void SAL_CALL setAllowBreakAcrossPages( const css::uno::Any& _allowbreakacrosspages ) override; + virtual float SAL_CALL getSpaceBetweenColumns() override; + virtual void SAL_CALL setSpaceBetweenColumns( float _spacebetweencolumns ) override; + + // Methods + virtual void SAL_CALL Delete( ) override; + virtual void SAL_CALL SetLeftIndent( float LeftIndent, ::sal_Int32 RulerStyle ) override; + virtual void SAL_CALL Select( ) override; + + //XCollection + virtual ::sal_Int32 SAL_CALL getCount() override; + virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& /*not processed in this base class*/ ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaRows_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAROWS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasection.cxx b/sw/source/ui/vba/vbasection.cxx new file mode 100644 index 000000000..53be97c3b --- /dev/null +++ b/sw/source/ui/vba/vbasection.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 "vbasection.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbapagesetup.hxx" +#include "vbaheadersfooters.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaSection::SwVbaSection( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& xProps ) : + SwVbaSection_BASE( rParent, rContext ), mxModel( xModel ), mxPageProps( xProps ) +{ +} + +SwVbaSection::~SwVbaSection() +{ +} + +sal_Bool SAL_CALL SwVbaSection::getProtectedForForms() +{ + return false; +} + +void SAL_CALL SwVbaSection::setProtectedForForms( sal_Bool /*_protectedforforms*/ ) +{ +} + +uno::Any SAL_CALL SwVbaSection::Headers( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaHeadersFooters( this, mxContext, mxModel, mxPageProps, true ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL SwVbaSection::Footers( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaHeadersFooters( this, mxContext, mxModel, mxPageProps, false ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaSection::PageSetup( ) +{ + return uno::makeAny( uno::Reference< word::XPageSetup >( new SwVbaPageSetup( this, mxContext, mxModel, mxPageProps ) ) ); +} + +OUString +SwVbaSection::getServiceImplName() +{ + return "SwVbaSection"; +} + +uno::Sequence< OUString > +SwVbaSection::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Section" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasection.hxx b/sw/source/ui/vba/vbasection.hxx new file mode 100644 index 000000000..e06043ff0 --- /dev/null +++ b/sw/source/ui/vba/vbasection.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_UI_VBA_VBASECTION_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASECTION_HXX + +#include <ooo/vba/word/XSection.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XSection > SwVbaSection_BASE; + +class SwVbaSection : public SwVbaSection_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::beans::XPropertySet > mxPageProps; + +public: + /// @throws css::uno::RuntimeException + SwVbaSection( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::beans::XPropertySet >& xProps ); + virtual ~SwVbaSection() override; + + // Attributes + virtual sal_Bool SAL_CALL getProtectedForForms() override; + virtual void SAL_CALL setProtectedForForms( sal_Bool _protectedforforms ) override; + + // Methods + virtual css::uno::Any SAL_CALL Headers( const css::uno::Any& index ) override; + virtual css::uno::Any SAL_CALL Footers( const css::uno::Any& index ) override; + virtual css::uno::Any SAL_CALL PageSetup( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBASECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasections.cxx b/sw/source/ui/vba/vbasections.cxx new file mode 100644 index 000000000..04a6cd560 --- /dev/null +++ b/sw/source/ui/vba/vbasections.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbasections.hxx" +#include "vbasection.hxx" +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include "wordvbahelper.hxx" +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +typedef std::vector< uno::Reference< beans::XPropertySet > > XSectionVec; + +namespace { + +class SectionEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration > +{ + XSectionVec mxSections; + XSectionVec::iterator mIt; + +public: + explicit SectionEnumeration( const XSectionVec& rVec ) : mxSections( rVec ), mIt( mxSections.begin() ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( mIt != mxSections.end() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( hasMoreElements() ) + return uno::makeAny( *mIt++ ); + throw container::NoSuchElementException(); + } +}; + +// here I regard pagestyle as section +class SectionCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxModel; + XSectionVec mxSections; + +public: + /// @throws uno::RuntimeException + SectionCollectionHelper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ) + { + uno::Reference< style::XStyleFamiliesSupplier > xSytleFamSupp( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xSytleFamNames( xSytleFamSupp->getStyleFamilies(), uno::UNO_SET_THROW ); + uno::Reference< container::XIndexAccess > xPageStyles( xSytleFamNames->getByName("PageStyles"), uno::UNO_QUERY_THROW ); + sal_Int32 nCount = xPageStyles->getCount(); + for( sal_Int32 index = 0; index < nCount; ++index ) + { + uno::Reference< style::XStyle > xStyle( xPageStyles->getByIndex( index ), uno::UNO_QUERY_THROW ); + // only the pagestyles in using are considered + if( xStyle->isInUse( ) ) + { + uno::Reference< beans::XPropertySet > xPageProps( xStyle, uno::UNO_QUERY_THROW ); + mxSections.push_back( xPageProps ); + } + } + } + + /// @throws uno::RuntimeException + SectionCollectionHelper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ) : mxParent( xParent ), mxContext( xContext ), mxModel( xModel ) + { + // Hacky implementation of Range.Sections, only support 1 section + uno::Reference< beans::XPropertySet > xRangeProps( xTextRange, uno::UNO_QUERY_THROW ); + uno::Reference< style::XStyle > xStyle = word::getCurrentPageStyle( mxModel, xRangeProps ); + uno::Reference< beans::XPropertySet > xPageProps( xStyle, uno::UNO_QUERY_THROW ); + mxSections.push_back( xPageProps ); + } + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override + { + return mxSections.size(); + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw css::lang::IndexOutOfBoundsException(); + + uno::Reference< beans::XPropertySet > xPageProps( mxSections[ Index ], uno::UNO_SET_THROW ); + return uno::makeAny( uno::Reference< word::XSection >( new SwVbaSection( mxParent, mxContext, mxModel, xPageProps ) ) ); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XSection>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new SectionEnumeration( mxSections ); + } +}; + +class SectionsEnumWrapper : public EnumerationHelperImpl +{ + uno::Reference< frame::XModel > mxModel; +public: + /// @throws uno::RuntimeException + SectionsEnumWrapper( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, const uno::Reference< frame::XModel >& xModel ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), mxModel( xModel ){} + + virtual uno::Any SAL_CALL nextElement( ) override + { + uno::Reference< beans::XPropertySet > xPageProps( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XSection > ( new SwVbaSection( m_xParent, m_xContext, mxModel, xPageProps ) ) ); + } +}; + +} + +SwVbaSections::SwVbaSections( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel ): SwVbaSections_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new SectionCollectionHelper( xParent, xContext, xModel ) ) ), mxModel( xModel ) +{ +} + +SwVbaSections::SwVbaSections( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel, const uno::Reference< text::XTextRange >& xTextRange ): SwVbaSections_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new SectionCollectionHelper( xParent, xContext, xModel, xTextRange ) ) ), mxModel( xModel ) +{ +} + +uno::Any SAL_CALL +SwVbaSections::PageSetup( ) +{ + if( m_xIndexAccess->getCount() ) + { + // check if the first section is our want + uno::Reference< word::XSection > xSection( m_xIndexAccess->getByIndex( 0 ), uno::UNO_QUERY_THROW ); + return xSection->PageSetup(); + } + throw uno::RuntimeException("There is no section" ); +} + +// XEnumerationAccess +uno::Type SAL_CALL +SwVbaSections::getElementType() +{ + return cppu::UnoType<word::XSection>::get(); +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwVbaSections::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return new SectionsEnumWrapper( this, mxContext, xEnumAccess->createEnumeration(), mxModel ); +} + +uno::Any +SwVbaSections::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaSections::getServiceImplName() +{ + return "SwVbaSections"; +} + +css::uno::Sequence<OUString> +SwVbaSections::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Sections" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasections.hxx b/sw/source/ui/vba/vbasections.hxx new file mode 100644 index 000000000..3bf3ab4ca --- /dev/null +++ b/sw/source/ui/vba/vbasections.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_UI_VBA_VBASECTIONS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASECTIONS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XSections.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XSections > SwVbaSections_BASE; + +class SwVbaSections : public SwVbaSections_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + +public: + SwVbaSections( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel ); + SwVbaSections( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::text::XTextRange >& xTextRange ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + virtual css::uno::Any SAL_CALL PageSetup( ) override; + + // SwVbaSections_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBASECTIONS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaselection.cxx b/sw/source/ui/vba/vbaselection.cxx new file mode 100644 index 000000000..a0c5a02f5 --- /dev/null +++ b/sw/source/ui/vba/vbaselection.cxx @@ -0,0 +1,1166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaselection.hxx" +#include <vbahelper/vbahelper.hxx> +#include "vbarange.hxx" +#include "vbafind.hxx" +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/text/XTextTableCursor.hpp> +#include <com/sun/star/table/XCell.hpp> +#include <basic/sberrors.hxx> +#include <ooo/vba/word/WdUnits.hpp> +#include <ooo/vba/word/WdMovementType.hpp> +#include <ooo/vba/word/WdGoToItem.hpp> +#include <ooo/vba/word/WdGoToDirection.hpp> +#include <ooo/vba/word/XBookmark.hpp> +#include <ooo/vba/word/XApplication.hpp> +#include <ooo/vba/word/WdCollapseDirection.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <unotbl.hxx> +#include <unocoll.hxx> +#include "vbatable.hxx" +#include <com/sun/star/view/XViewCursor.hpp> +#include <com/sun/star/view/XLineCursor.hpp> +#include <com/sun/star/text/XWordCursor.hpp> +#include <com/sun/star/text/XParagraphCursor.hpp> +#include <ooo/vba/word/WdInformation.hpp> +#include <ooo/vba/word/WdHeaderFooterIndex.hpp> +#include <ooo/vba/word/WdSeekView.hpp> +#include "vbainformationhelper.hxx" +#include "vbafield.hxx" +#include "vbaheaderfooter.hxx" +#include "vbaheaderfooterhelper.hxx" +#include <vbahelper/vbashaperange.hxx> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include "vbarows.hxx" +#include "vbacolumns.hxx" +#include "vbatablehelper.hxx" +#include "vbacells.hxx" +#include "vbaview.hxx" +#include "vbaparagraph.hxx" +#include "vbastyle.hxx" +#include <docsh.hxx> +#include <tblenum.hxx> +#include <fesh.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaSelection::SwVbaSelection( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< frame::XModel >& rModel ) : SwVbaSelection_BASE( rParent, rContext ), mxModel( rModel ) +{ + mxTextViewCursor = word::getXTextViewCursor( mxModel ); +} + +SwVbaSelection::~SwVbaSelection() +{ +} + +uno::Reference< text::XTextRange > SwVbaSelection::GetSelectedRange() +{ + uno::Reference< text::XTextRange > xTextRange; + uno::Reference< lang::XServiceInfo > xServiceInfo( mxModel->getCurrentSelection(), uno::UNO_QUERY_THROW ); + if( !xServiceInfo->supportsService("com.sun.star.text.TextRanges") ) + { + throw uno::RuntimeException("Not implemented" ); + } + + uno::Reference< container::XIndexAccess > xTextRanges( xServiceInfo, uno::UNO_QUERY_THROW ); + if( xTextRanges->getCount() > 0 ) + { + // if there are multiple selection, just return the last selected Range. + xTextRange.set( xTextRanges->getByIndex( xTextRanges->getCount()-1 ), uno::UNO_QUERY_THROW ); + } + + return xTextRange; +} + +uno::Reference< word::XRange > SAL_CALL +SwVbaSelection::getRange() +{ + uno::Reference< text::XTextRange > xTextRange = GetSelectedRange(); + uno::Reference< text::XTextDocument > xDocument( mxModel, uno::UNO_QUERY_THROW ); + return uno::Reference< word::XRange >( new SwVbaRange( this, mxContext, xDocument, xTextRange->getStart(), xTextRange->getEnd(), mxTextViewCursor->getText() ) ); +} + +OUString SAL_CALL +SwVbaSelection::getText() +{ + return getRange()->getText(); +} + +void SAL_CALL +SwVbaSelection::setText( const OUString& rText ) +{ + getRange()->setText( rText ); +} + +void SAL_CALL +SwVbaSelection::TypeText( const OUString& rText ) +{ + // FIXME: handle the property Options.ReplaceSelection, the default value is true + setText( rText ); +} + +void SAL_CALL +SwVbaSelection::HomeKey( const uno::Any& _unit, const uno::Any& _extend ) +{ + sal_Int32 nUnit = word::WdUnits::wdLine; + sal_Int32 nExtend = word::WdMovementType::wdMove; + _unit >>= nUnit; + _extend >>= nExtend; + bool bExtend = nExtend == word::WdMovementType::wdExtend; + + switch( nUnit ) + { + case word::WdUnits::wdStory: + { + // go to the valid text first so that the current view cursor is valid to call gotoRange. + word::gotoSelectedObjectAnchor(mxModel); + // go to the begin of the document + uno::Reference< text::XText > xCurrentText = word::getCurrentXText( mxModel ); + uno::Reference< text::XTextRange > xFirstRange = word::getFirstObjectPosition( xCurrentText ); + mxTextViewCursor->gotoRange( xFirstRange, bExtend ); + break; + } + case word::WdUnits::wdLine: + { + // go to the begin of the Line + uno::Reference< view::XLineCursor > xLineCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + xLineCursor->gotoStartOfLine( bExtend ); + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } +} + +void SAL_CALL +SwVbaSelection::EndKey( const uno::Any& _unit, const uno::Any& _extend ) +{ + sal_Int32 nUnit = word::WdUnits::wdLine; + sal_Int32 nExtend = word::WdMovementType::wdMove; + _unit >>= nUnit; + _extend >>= nExtend; + bool bExtend = nExtend == word::WdMovementType::wdExtend; + + switch( nUnit ) + { + case word::WdUnits::wdStory: + { + // go to the valid text first so that the current view cursor is valid to call gotoRange. + word::gotoSelectedObjectAnchor(mxModel); + // go to the end of the document + uno::Reference< text::XText > xCurrentText = word::getCurrentXText( mxModel ); + uno::Reference< text::XTextRange > xEnd = xCurrentText->getEnd(); + mxTextViewCursor->gotoRange( xEnd, bExtend ); + break; + } + case word::WdUnits::wdLine: + { + // go to the end of the Line + uno::Reference< view::XLineCursor > xLineCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + xLineCursor->gotoEndOfLine( bExtend ); + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } +} + +void SAL_CALL +SwVbaSelection::Delete( const uno::Any& _unit, const uno::Any& _count ) +{ + sal_Int32 nUnit = word::WdUnits::wdLine; + sal_Int32 nCount = 0; + if( _count.hasValue() ) + _count >>= nCount; + if( _unit.hasValue() && ( nCount > 0 ) ) + { + _unit >>= nUnit; + switch( nUnit ) + { + case word::WdUnits::wdCharacter: + { + if( HasSelection() ) + nCount--; + mxTextViewCursor->goRight( nCount, true ); + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } + } + OUString url = ".uno:Delete"; + dispatchRequests( mxModel,url ); +} + +void +SwVbaSelection::Move( const uno::Any& _unit, const uno::Any& _count, const uno::Any& _extend, word::E_DIRECTION eDirection ) +{ + sal_Int32 nUnit = word::WdUnits::wdCharacter; + sal_Int32 nCount = 1; + sal_Int32 nExtend = word::WdMovementType::wdMove; + + if( _unit.hasValue() ) + _unit >>= nUnit; + if( _count.hasValue() ) + _count >>= nCount; + if( _extend.hasValue() ) + _extend >>= nExtend; + + if( nCount == 0 ) + return; + + bool bExpand = nExtend != word::WdMovementType::wdMove; + + switch( nUnit ) + { + case word::WdUnits::wdCell: + { + if( nExtend == word::WdMovementType::wdExtend ) + { + DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString()); + return; + } + NextCell( nCount, eDirection ); + break; + } + case word::WdUnits::wdLine: + { + if( eDirection == word::MOVE_LEFT || eDirection == word::MOVE_RIGHT ) + { + throw uno::RuntimeException("Not implemented" ); + } + uno::Reference< view::XViewCursor > xViewCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + if( eDirection == word::MOVE_UP ) + xViewCursor->goUp( nCount, bExpand ); + else if( eDirection == word::MOVE_DOWN ) + xViewCursor->goDown( nCount, bExpand ); + break; + } + case word::WdUnits::wdCharacter: + { + if( eDirection == word::MOVE_UP || eDirection == word::MOVE_DOWN ) + { + throw uno::RuntimeException("Not implemented" ); + } + if( word::gotoSelectedObjectAnchor( mxModel ) ) + { + nCount--; + } + uno::Reference< view::XViewCursor > xViewCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + if( eDirection == word::MOVE_LEFT ) + { + // if current select is a cellrange or table, + // the first count of move should move to the first selected cell. + uno::Reference< text::XTextTableCursor > xTextTableCursor( mxModel->getCurrentSelection(), uno::UNO_QUERY ); + if ( xTextTableCursor.is() ) + { + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + if( xTextTable.is() ) + { + uno::Reference< text::XTextRange > xRange( xTextTable->getCellByName( xTextTableCursor->getRangeName()), uno::UNO_QUERY_THROW ); + mxTextViewCursor->gotoRange( xRange->getStart(), bExpand ); + nCount--; + } + } + xViewCursor->goLeft( nCount, bExpand ); + } + else if( eDirection == word::MOVE_RIGHT ) + xViewCursor->goRight( nCount, bExpand ); + break; + } + case word::WdUnits::wdWord: + case word::WdUnits::wdParagraph: + { + uno::Reference< text::XTextRange > xRange = GetSelectedRange(); + uno::Reference< text::XText > xText = xRange->getText(); + uno::Reference< text::XTextCursor > xTextCursor = xText->createTextCursorByRange( xRange ); + if( nUnit == word::WdUnits::wdParagraph ) + { + if( eDirection == word::MOVE_LEFT || eDirection == word::MOVE_RIGHT ) + { + throw uno::RuntimeException("Not implemented" ); + } + uno::Reference< text::XParagraphCursor > xParagraphCursor( xTextCursor, uno::UNO_QUERY_THROW ); + for( sal_Int32 i=0; i<nCount; i++ ) + { + if( ( eDirection == word::MOVE_UP ) && !xParagraphCursor->gotoPreviousParagraph( bExpand ) ) + break; + else if( ( eDirection == word::MOVE_DOWN ) && !xParagraphCursor->gotoNextParagraph( bExpand ) ) + break; + } + } + else if( nUnit == word::WdUnits::wdWord ) + { + if( eDirection == word::MOVE_UP || eDirection == word::MOVE_DOWN ) + { + throw uno::RuntimeException("Not implemented" ); + } + uno::Reference< text::XWordCursor > xWordCursor( xTextCursor, uno::UNO_QUERY_THROW ); + for( sal_Int32 i=0; i<nCount; i++ ) + { + if( (eDirection == word::MOVE_LEFT ) && !xWordCursor->gotoPreviousWord( bExpand ) ) + break; + else if( ( eDirection == word::MOVE_RIGHT ) && !xWordCursor->gotoNextWord( bExpand ) ) + break; + } + } + mxTextViewCursor->gotoRange( xTextCursor->getStart(), false ); + mxTextViewCursor->gotoRange( xTextCursor->getEnd(), true ); + break; + } + default: + { + throw uno::RuntimeException("Not implemented" ); + } + } +} + +void SwVbaSelection::NextCell(sal_Int32 nCount, word::E_DIRECTION eDirection) +{ + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + uno::Reference< table::XCell > xCell; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + xCursorProps->getPropertyValue("Cell") >>= xCell; + if( !xTextTable.is() || !xCell.is() ) + { + DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString()); + return; + } + uno::Reference< beans::XPropertySet > xCellProps( xCell, uno::UNO_QUERY_THROW ); + OUString aCellName; + xCellProps->getPropertyValue("CellName") >>= aCellName; + uno::Reference< text::XTextTableCursor > xTextTableCursor = xTextTable->createCursorByCellName( aCellName ); + // move the table cursor + switch( eDirection ) + { + case word::MOVE_LEFT: + { + xTextTableCursor->goLeft( nCount, false ); + break; + } + case word::MOVE_RIGHT: + { + xTextTableCursor->goRight( nCount, false ); + break; + } + case word::MOVE_UP: + { + xTextTableCursor->goUp( nCount, false ); + break; + } + case word::MOVE_DOWN: + { + xTextTableCursor->goDown( nCount, false ); + break; + } + default: + { + DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString()); + return; + } + } + // move the view cursor + xCell = xTextTable->getCellByName( xTextTableCursor->getRangeName() ); + mxTextViewCursor->gotoRange( uno::Reference< text::XTextRange >( xCell, uno::UNO_QUERY_THROW ), false ); +} + +void SAL_CALL +SwVbaSelection::MoveRight(const uno::Any& _unit, const uno::Any& _count, const uno::Any& _extend) +{ + sal_Int32 nCount = 1; + + if( _count.hasValue() ) + _count >>= nCount; + + if( nCount == 0 ) + return; + + if( nCount < 0 ) + { + MoveLeft( _unit, uno::makeAny( -nCount ), _extend ); + return; + } + + Move( _unit, _count, _extend, word::MOVE_RIGHT ); +} + +void SAL_CALL +SwVbaSelection::MoveLeft(const uno::Any& _unit, const uno::Any& _count, const uno::Any& _extend) +{ + sal_Int32 nCount = 1; + if( _count.hasValue() ) + _count >>= nCount; + + if( nCount == 0 ) + return; + + if( nCount < 0 ) + { + MoveRight( _unit, uno::makeAny( -nCount ), _extend ); + return; + } + + Move( _unit, _count, _extend, word::MOVE_LEFT ); +} + +void SAL_CALL +SwVbaSelection::MoveDown(const uno::Any& _unit, const uno::Any& _count, const uno::Any& _extend) +{ + sal_Int32 nCount = 1; + + if( _count.hasValue() ) + _count >>= nCount; + + if( nCount == 0 ) + return; + + if( nCount < 0 ) + { + MoveUp( _unit, uno::makeAny( -nCount ), _extend ); + return; + } + + Move( _unit, _count, _extend, word::MOVE_DOWN ); +} + +void SAL_CALL +SwVbaSelection::MoveUp(const uno::Any& _unit, const uno::Any& _count, const uno::Any& _extend) +{ + sal_Int32 nCount = 1; + + if( _count.hasValue() ) + _count >>= nCount; + + if( nCount == 0 ) + return; + + if( nCount < 0 ) + { + MoveDown( _unit, uno::makeAny( -nCount ), _extend ); + return; + } + + Move( _unit, _count, _extend, word::MOVE_UP ); +} + +void SAL_CALL +SwVbaSelection::TypeParagraph() +{ + // #FIXME: if the selection is an entire paragraph, it's replaced + // by the new paragraph + bool isCollapsed = mxTextViewCursor->isCollapsed(); + InsertParagraph(); + if( isCollapsed ) + mxTextViewCursor->collapseToStart(); +} + +void SAL_CALL +SwVbaSelection::InsertParagraph() +{ + // #FIXME: the selection should include the new paragraph. + getRange()->InsertParagraph(); +} + +void SAL_CALL +SwVbaSelection::InsertParagraphBefore() +{ + getRange()->InsertParagraphBefore(); +} + +void SAL_CALL +SwVbaSelection::InsertParagraphAfter() +{ + getRange()->InsertParagraphAfter(); +} + +uno::Reference< word::XParagraphFormat > SAL_CALL +SwVbaSelection::getParagraphFormat() +{ + return getRange()->getParagraphFormat(); +} + +void SAL_CALL +SwVbaSelection::setParagraphFormat( const uno::Reference< word::XParagraphFormat >& rParagraphFormat ) +{ + return getRange()->setParagraphFormat( rParagraphFormat ); +} + +uno::Reference< word::XFind > SAL_CALL +SwVbaSelection::getFind() +{ + uno::Reference< text::XTextRange > xTextRange = GetSelectedRange(); + return uno::Reference< word::XFind >( new SwVbaFind( this, mxContext, mxModel, xTextRange ) ); +} + +uno::Any SAL_CALL +SwVbaSelection::getStyle() +{ + return getRange()->getStyle(); +} + +void SAL_CALL +SwVbaSelection::setStyle( const uno::Any& rStyle ) +{ + uno::Reference< beans::XPropertySet > xParaProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + return SwVbaStyle::setStyle( xParaProps, rStyle ); +} + +uno::Reference< word::XFont > SAL_CALL +SwVbaSelection::getFont() +{ + return getRange()->getFont(); +} + +void SAL_CALL +SwVbaSelection::TypeBackspace() +{ + OUString url = ".uno:SwBackspace"; + dispatchRequests( mxModel,url ); +} + +uno::Reference< word::XRange > SAL_CALL SwVbaSelection::GoTo( const uno::Any& _what, const uno::Any& _which, const uno::Any& _count, const uno::Any& _name ) +{ + sal_Int32 nWhat = 0; + if( !( _what >>= nWhat ) ) + DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, OUString()); + switch( nWhat ) + { + case word::WdGoToItem::wdGoToBookmark: + { + uno::Reference< word::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW ); + uno::Reference< word::XBookmark > xBookmark( xApplication->getActiveDocument()->Bookmarks(_name), uno::UNO_QUERY_THROW ); + xBookmark->Select(); + break; + } + case word::WdGoToItem::wdGoToPage: + { + uno::Reference< text::XPageCursor > xPageCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + sal_Int32 nCurrPage = xPageCursor->getPage(); + sal_Int32 nLastPage = word::getPageCount( mxModel ); + sal_Int32 nCount = 0; + if( _count.hasValue() ) + _count >>= nCount; + sal_Int32 nWhich = 0; + if( _which.hasValue() ) + _which >>= nWhich; + sal_Int32 nPage = 0; + switch( nWhich ) + { + case word::WdGoToDirection::wdGoToLast: + { + nPage = nLastPage; + break; + } + case word::WdGoToDirection::wdGoToNext: + { + if( nCount !=0 ) + nPage = nCurrPage + nCount; + else + nPage = nCurrPage + 1; + break; + } + case word::WdGoToDirection::wdGoToPrevious: + { + if( nCount !=0 ) + nPage = nCurrPage - nCount; + else + nPage = nCurrPage - 1; + break; + } + default: + { + nPage = nCount; + } + } + if( _name.hasValue() ) + { + OUString sName; + _name >>= sName; + sal_Int32 nName = sName.toInt32(); + if( nName !=0 ) + nPage = nName; + } + if( nPage <= 0 ) + nPage = 1; + if( nPage > nLastPage ) + nPage = nLastPage; + xPageCursor->jumpToPage( static_cast<sal_Int16>(nPage) ); + break; + } + case word::WdGoToItem::wdGoToSection: + { + uno::Reference< text::XPageCursor > xPageCursor( mxTextViewCursor, uno::UNO_QUERY_THROW ); + sal_Int32 nCount = 0; + if( _count.hasValue() ) + _count >>= nCount; + sal_Int32 nWhich = 0; + if( _which.hasValue() ) + _which >>= nWhich; + sal_Int32 nPage = 0; + switch( nWhich ) + { + case word::WdGoToDirection::wdGoToAbsolute: + { + // currently only support this type + if( nCount == 1 ) + nPage = 1; + break; + } + default: + { + nPage = 0; + } + } + if( nPage == 0 ) + throw uno::RuntimeException("Not implemented" ); + xPageCursor->jumpToPage( static_cast<sal_Int16>(nPage) ); + break; + } + default: + throw uno::RuntimeException("Not implemented" ); + } + return getRange(); +} + +::sal_Int32 SAL_CALL SwVbaSelection::getLanguageID() +{ + return getRange()->getLanguageID(); +} + +void SAL_CALL SwVbaSelection::setLanguageID( ::sal_Int32 _languageid ) +{ + getRange()->setLanguageID( _languageid ); +} + +uno::Any SAL_CALL SwVbaSelection::Information( sal_Int32 _type ) +{ + uno::Any result; + switch( _type ) + { + case word::WdInformation::wdActiveEndPageNumber: + { + result <<= SwVbaInformationHelper::handleWdActiveEndPageNumber( mxTextViewCursor ); + break; + } + case word::WdInformation::wdNumberOfPagesInDocument: + { + result <<= SwVbaInformationHelper::handleWdNumberOfPagesInDocument( mxModel ); + break; + } + case word::WdInformation::wdVerticalPositionRelativeToPage: + { + result <<= SwVbaInformationHelper::handleWdVerticalPositionRelativeToPage( mxModel, mxTextViewCursor ); + break; + } + case word::WdInformation::wdWithInTable: + { + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + result <<= xTextTable.is(); + break; + } + case word::WdInformation::wdHeaderFooterType: + { + uno::Reference< word::XView > xView( new SwVbaView( this, mxContext, mxModel ) ); + sal_Int32 nView = xView->getSeekView(); + sal_Int32 nHeaderFooterType = 0; + switch( nView ) + { + case word::WdSeekView::wdSeekMainDocument: + { + nHeaderFooterType = -1; // not in a header or footer + break; + } + case word::WdSeekView::wdSeekEvenPagesHeader: + { + nHeaderFooterType = 0; // even page header + break; + } + case word::WdSeekView::wdSeekPrimaryHeader: + { + nHeaderFooterType = 1; // odd page header + break; + } + case word::WdSeekView::wdSeekEvenPagesFooter: + { + nHeaderFooterType = 2; // even page footer + break; + } + case word::WdSeekView::wdSeekPrimaryFooter: + { + nHeaderFooterType = 3; // odd page footer + break; + } + case word::WdSeekView::wdSeekFirstPageHeader: + case word::WdSeekView::wdSeekFirstPageFooter: + { + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + OUString aPageStyleName; + xCursorProps->getPropertyValue("PageStyleName") >>= aPageStyleName; + bool bFirstPage = false; + if ( aPageStyleName == "First Page" ) + bFirstPage = true; + if( nView == word::WdSeekView::wdSeekFirstPageHeader ) + { + if( bFirstPage ) + nHeaderFooterType = 4; + else + nHeaderFooterType = 1; + } + else + { + if( bFirstPage ) + nHeaderFooterType = 5; + else + nHeaderFooterType = 3; + } + break; + } + default: + { + nHeaderFooterType = -1; + } + } + result <<= nHeaderFooterType; + break; + } + default: + throw uno::RuntimeException("Not implemented" ); + } + return result; +} + +void SAL_CALL SwVbaSelection::InsertBreak( const uno::Any& _breakType ) +{ + getRange()->InsertBreak( _breakType ); +} + +uno::Any SAL_CALL +SwVbaSelection::Tables( const uno::Any& aIndex ) +{ + // Hacky implementation due to missing api ( and lack of knowledge ) + // we can only support a selection that is a single table + if ( !aIndex.hasValue() ) // currently we can't support multiple tables in a selection + throw uno::RuntimeException(); + + sal_Int32 nIndex = 0; + aIndex >>= nIndex; + + uno::Any aRet; + + if ( nIndex != 1 ) + throw uno::RuntimeException(); + + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + if( xTextTable.is() ) + { + uno::Reference< css::text::XTextDocument > xTextDoc( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< word::XTable > xVBATable = new SwVbaTable( mxParent, mxContext, xTextDoc, xTextTable ); + aRet <<= xVBATable; + return aRet; + } + + // if the current selection is a XTextTableCursor and the index is 1 then we can service this request, otherwise we just have to throw + uno::Reference< text::XTextTableCursor > xTextTableCursor( mxModel->getCurrentSelection(), uno::UNO_QUERY_THROW ); + SwXTextTableCursor* pTTCursor = dynamic_cast< SwXTextTableCursor* >( xTextTableCursor.get() ); + if ( pTTCursor ) + { + SwFrameFormat* pFormat = pTTCursor->GetFrameFormat(); + if ( pFormat ) + { + uno::Reference< text::XTextTable > xTable = SwXTextTables::GetObject(*pFormat); + uno::Reference< css::text::XTextDocument > xTextDoc( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< word::XTable > xVBATable = new SwVbaTable( mxParent, mxContext, xTextDoc, xTable ); + aRet <<= xVBATable; + } + } + return aRet; + +} + +uno::Any SAL_CALL +SwVbaSelection::Fields( const uno::Any& index ) +{ + uno::Reference< XCollection > xCol( new SwVbaFields( mxParent, mxContext, mxModel ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Reference< word::XHeaderFooter > SAL_CALL +SwVbaSelection::getHeaderFooter() +{ + if( HeaderFooterHelper::isHeaderFooter( mxModel ) ) + { + uno::Reference< beans::XPropertySet > xPageStyleProps( word::getCurrentPageStyle( mxModel ), uno::UNO_QUERY_THROW ); + sal_Int32 nIndex = word::WdHeaderFooterIndex::wdHeaderFooterPrimary; + bool isHeader = HeaderFooterHelper::isHeader( mxModel ); + if( HeaderFooterHelper::isEvenPagesHeader( mxModel ) || HeaderFooterHelper::isEvenPagesFooter( mxModel ) ) + nIndex = word::WdHeaderFooterIndex::wdHeaderFooterEvenPages; + else if( HeaderFooterHelper::isFirstPageHeader( mxModel ) || HeaderFooterHelper::isFirstPageFooter( mxModel ) ) + nIndex = word::WdHeaderFooterIndex::wdHeaderFooterFirstPage; + + return uno::Reference< word::XHeaderFooter >( new SwVbaHeaderFooter( this, mxContext, mxModel, xPageStyleProps, isHeader, nIndex ) ); + + } + return uno::Reference< word::XHeaderFooter >(); +} + +uno::Any SAL_CALL +SwVbaSelection::ShapeRange( ) +{ + uno::Reference< drawing::XShapes > xShapes( mxModel->getCurrentSelection(), uno::UNO_QUERY ); + if ( !xShapes.is() ) + { + uno::Reference< drawing::XShape > xShape( mxModel->getCurrentSelection(), uno::UNO_QUERY_THROW ); + xShapes.set( drawing::ShapeCollection::create(mxContext) ); + xShapes->add( xShape ); + } + + uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< drawing::XDrawPage > xDrawPage = xDrawPageSupplier->getDrawPage(); + uno::Reference< container::XIndexAccess > xShapesAccess( xShapes, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< msforms::XShapeRange >( new ScVbaShapeRange( this, mxContext, xShapesAccess, xDrawPage, mxModel ) ) ); +} + +::sal_Int32 SAL_CALL SwVbaSelection::getStart() +{ + return getRange()->getStart(); +} + +void SAL_CALL SwVbaSelection::setStart( ::sal_Int32 _start ) +{ + getRange()->setStart( _start ); +} +::sal_Int32 SAL_CALL SwVbaSelection::getEnd() +{ + return getRange()->getEnd(); +} + +void SAL_CALL SwVbaSelection::setEnd( ::sal_Int32 _end ) +{ + getRange()->setEnd( _end ); +} + +void SAL_CALL SwVbaSelection::SelectRow() +{ + uno::Reference< word::XRows > xRows( Rows( uno::Any() ), uno::UNO_QUERY_THROW ); + xRows->Select(); +} + +void SAL_CALL SwVbaSelection::SelectColumn() +{ + uno::Reference< word::XColumns > xColumns( Columns( uno::Any() ), uno::UNO_QUERY_THROW ); + xColumns->Select(); +} + +uno::Any SAL_CALL SwVbaSelection::Rows( const uno::Any& index ) +{ + OUString sTLName; + OUString sBRName; + GetSelectedCellRange( sTLName, sBRName ); + + sal_Int32 nStartRow = 0; + sal_Int32 nEndRow = 0; + uno::Reference< text::XTextTable > xTextTable = GetXTextTable(); + SwVbaTableHelper aTableHelper( xTextTable ); + nStartRow = aTableHelper.getTabRowIndex( sTLName ); + if( !sBRName.isEmpty() ) + { + nEndRow = aTableHelper.getTabRowIndex( sBRName ); + } + else + { + nEndRow = nStartRow; + } + + uno::Reference< XCollection > xCol( new SwVbaRows( this, mxContext, xTextTable, xTextTable->getRows(), nStartRow, nEndRow ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL SwVbaSelection::Columns( const uno::Any& index ) +{ + OUString sTLName; + OUString sBRName; + GetSelectedCellRange( sTLName, sBRName ); + sal_Int32 nStartColumn = 0; + sal_Int32 nEndColumn = 0; + + uno::Reference< text::XTextTable > xTextTable = GetXTextTable(); + SwVbaTableHelper aTableHelper( xTextTable ); + nStartColumn = aTableHelper.getTabColIndex( sTLName ); + if( !sBRName.isEmpty() ) + { + nEndColumn = aTableHelper.getTabColIndex( sBRName ); + } + else + { + nEndColumn = nStartColumn; + } + + uno::Reference< XCollection > xCol( new SwVbaColumns( this, mxContext, xTextTable, xTextTable->getColumns(), nStartColumn, nEndColumn ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Reference< text::XTextTable > SwVbaSelection::GetXTextTable() const +{ + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + return xTextTable; +} + +bool SwVbaSelection::IsInTable() const +{ + uno::Reference< text::XTextTable > xTextTable = GetXTextTable(); + return xTextTable.is(); +} + +bool SwVbaSelection::HasSelection() +{ + uno::Reference< text::XTextRange > xStart = mxTextViewCursor->getStart(); + uno::Reference< text::XTextRange > xEnd = mxTextViewCursor->getEnd(); + uno::Reference< text::XTextRangeCompare > xTRC( mxTextViewCursor->getText(), uno::UNO_QUERY_THROW ); + return xTRC->compareRegionStarts( xStart, xEnd ) != 0 || xTRC->compareRegionEnds( xStart, xEnd ) != 0; +} + +void SwVbaSelection::GetSelectedCellRange( OUString& sTLName, OUString& sBRName ) +{ + uno::Reference< beans::XPropertySet > xCursorProps( mxTextViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextTable > xTextTable; + xCursorProps->getPropertyValue("TextTable") >>= xTextTable; + if( !xTextTable.is() ) + throw uno::RuntimeException( ); + + uno::Reference< text::XTextTableCursor > xTextTableCursor( mxModel->getCurrentSelection(), uno::UNO_QUERY ); + if( xTextTableCursor.is() ) + { + const OUString sRange( xTextTableCursor->getRangeName() ); + if (!sRange.isEmpty()) + { + sal_Int32 nIdx{0}; + sTLName = sRange.getToken(0, ':', nIdx); + sBRName = sRange.getToken(0, ':', nIdx); + } + } + if( sTLName.isEmpty() ) + { + uno::Reference< table::XCell > xCell; + xCursorProps->getPropertyValue("Cell") >>= xCell; + if( !xCell.is() ) + { + throw uno::RuntimeException( ); + } + uno::Reference< beans::XPropertySet > xCellProps( xCell, uno::UNO_QUERY_THROW ); + xCellProps->getPropertyValue("CellName") >>= sTLName; + } +} + +uno::Any SAL_CALL SwVbaSelection::Cells( const uno::Any& index ) +{ + OUString sTLName; + OUString sBRName; + GetSelectedCellRange( sTLName, sBRName ); + sal_Int32 nLeft = 0; + sal_Int32 nTop = 0; + sal_Int32 nRight = 0; + sal_Int32 nBottom = 0; + + uno::Reference< text::XTextTable > xTextTable = GetXTextTable(); + SwVbaTableHelper aTableHelper( xTextTable ); + nLeft = aTableHelper.getTabColIndex( sTLName ); + nTop = aTableHelper.getTabRowIndex( sTLName ); + if( !sBRName.isEmpty() ) + { + nRight = aTableHelper.getTabColIndex( sBRName ); + nBottom = aTableHelper.getTabRowIndex( sBRName ); + } + else + { + nRight = nLeft; + nBottom = nTop; + } + + uno::Reference< XCollection > xCol( new SwVbaCells( this, mxContext, xTextTable, nLeft, nTop, nRight, nBottom ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +void SAL_CALL SwVbaSelection::Copy( ) +{ + OUString url = ".uno:Copy"; + dispatchRequests( mxModel,url ); +} + +void SAL_CALL SwVbaSelection::CopyAsPicture( ) +{ + // seems not support in Writer + Copy(); +} + +void SAL_CALL SwVbaSelection::Paste( ) +{ + OUString url = ".uno:Paste"; + dispatchRequests( mxModel,url ); +} + +void SAL_CALL SwVbaSelection::Collapse( const uno::Any& Direction ) +{ + if( word::gotoSelectedObjectAnchor( mxModel ) ) + return; + + sal_Int32 nDirection = word::WdCollapseDirection::wdCollapseStart; + if( Direction.hasValue() ) + Direction >>= nDirection; + + uno::Reference< text::XTextViewCursor > xTextViewCursor = word::getXTextViewCursor( mxModel ); + if( nDirection == word::WdCollapseDirection::wdCollapseStart ) + { + // it is inaccurate if current selection is multiple cells, so it needs to go to start + uno::Reference< text::XTextRange > xTextRange = mxTextViewCursor->getStart(); + xTextViewCursor->gotoRange( xTextRange, false ); + xTextViewCursor->collapseToStart(); + } + else if( nDirection == word::WdCollapseDirection::wdCollapseEnd ) + { + uno::Reference< text::XTextRange > xTextRange = mxTextViewCursor->getEnd(); + xTextViewCursor->gotoRange( xTextRange, false ); + xTextViewCursor->collapseToEnd(); + } + else + { + throw uno::RuntimeException(); + } +} + +void SAL_CALL SwVbaSelection::WholeStory( ) +{ + uno::Reference< text::XText > xText = word::getCurrentXText( mxModel ); + // FIXME: for i#7747,if the first line is a table, it fails to select all the contents in the story. + // Temporary solution, insert an empty line before the table so that it could select all the contents. + uno::Reference< container::XEnumerationAccess > xParaAccess( xText, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration> xParaEnum = xParaAccess->createEnumeration(); + if( xParaEnum->hasMoreElements() ) + { + uno::Reference< text::XTextTable > xTextTable( xParaEnum->nextElement(), uno::UNO_QUERY ); + if( xTextTable.is() ) + { + // insert an empty line + uno::Reference< text::XTextRange > xFirstCellRange = word::getFirstObjectPosition( xText ); + mxTextViewCursor->gotoRange( xFirstCellRange, false ); + OUString url = ".uno:InsertPara"; + dispatchRequests( mxModel,url ); + } + } + uno::Reference< text::XTextRange > xStart = xText->getStart(); + uno::Reference< text::XTextRange > xEnd = xText->getEnd(); + mxTextViewCursor->gotoRange( xStart, false ); + mxTextViewCursor->gotoRange( xEnd, true ); +} + +sal_Bool SAL_CALL SwVbaSelection::InRange( const uno::Reference< ::ooo::vba::word::XRange >& Range ) +{ + return getRange()->InRange( Range ); +} + +void SAL_CALL SwVbaSelection::SplitTable() +{ + if( !IsInTable() ) + throw uno::RuntimeException(); + + SwDocShell* pDocShell = word::getDocShell( mxModel ); + if( pDocShell ) + { + SwFEShell* pFEShell = pDocShell->GetFEShell(); + if( pFEShell ) + { + pFEShell->SplitTable( SplitTable_HeadlineOption::ContentCopy ); + } + } +} + +uno::Any SAL_CALL +SwVbaSelection::Paragraphs( const uno::Any& aIndex ) +{ + // Hacky implementation due to missing api ( and lack of knowledge ) + // we can only support a selection that is a single paragraph + if ( !aIndex.hasValue() ) // currently we can't support multiple paragraphs in a selection + throw uno::RuntimeException(); + + sal_Int32 nIndex = 0; + aIndex >>= nIndex; + + uno::Any aRet; + + if ( nIndex != 1 ) + throw uno::RuntimeException(); + + uno::Reference< text::XTextRange > xTextRange = mxTextViewCursor->getStart(); + uno::Reference< text::XText > xText = xTextRange->getText(); + uno::Reference< text::XParagraphCursor > xParaCursor( xText->createTextCursor(), uno::UNO_QUERY_THROW ); + xParaCursor->gotoStartOfParagraph( false ); + xParaCursor->gotoStartOfParagraph( true ); + + uno::Reference< text::XTextDocument > xTextDoc( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xParaRange( xParaCursor, uno::UNO_QUERY_THROW ); + uno::Reference< word::XParagraph > xParagraph = new SwVbaParagraph( mxParent, mxContext, xTextDoc, xParaRange ); + + aRet <<= xParagraph; + return aRet; +} + +OUString +SwVbaSelection::getServiceImplName() +{ + return "SwVbaSelection"; +} + +uno::Sequence< OUString > +SwVbaSelection::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Selection" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaselection.hxx b/sw/source/ui/vba/vbaselection.hxx new file mode 100644 index 000000000..322804fe0 --- /dev/null +++ b/sw/source/ui/vba/vbaselection.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_UI_VBA_VBASELECTION_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASELECTION_HXX + +#include <ooo/vba/word/XSelection.hpp> +#include <ooo/vba/word/XRange.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <ooo/vba/word/XParagraphFormat.hpp> +#include <ooo/vba/word/XFind.hpp> +#include <ooo/vba/word/XFont.hpp> +#include <ooo/vba/word/XHeaderFooter.hpp> +#include "wordvbahelper.hxx" + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XSelection > SwVbaSelection_BASE; + +class SwVbaSelection : public SwVbaSelection_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextViewCursor > mxTextViewCursor; + +private: + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + void Move( const css::uno::Any& _unit, const css::uno::Any& _count, const css::uno::Any& _extend, ooo::vba::word::E_DIRECTION eDirection ); + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + void NextCell( sal_Int32 nCount, ooo::vba::word::E_DIRECTION eDirection ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextRange > GetSelectedRange(); + /// @throws css::uno::RuntimeException + void GetSelectedCellRange( OUString& sTLName, OUString& sBRName ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextTable > GetXTextTable() const; + /// @throws css::uno::RuntimeException + bool IsInTable() const; + /// @throws css::uno::RuntimeException + bool HasSelection(); + +public: + /// @throws css::uno::RuntimeException + SwVbaSelection( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::frame::XModel >& rModel ); + virtual ~SwVbaSelection() override; + + // Attribute + virtual OUString SAL_CALL getText() override; + virtual void SAL_CALL setText( const OUString& rText ) override; + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL getRange() override; + virtual void SAL_CALL HomeKey( const css::uno::Any& _unit, const css::uno::Any& _extend ) override; + virtual void SAL_CALL EndKey( const css::uno::Any& _unit, const css::uno::Any& _extend ) override; + virtual void SAL_CALL TypeText( const OUString& rText ) override; + virtual void SAL_CALL Delete( const css::uno::Any& _unit, const css::uno::Any& _count ) override; + virtual void SAL_CALL MoveRight( const css::uno::Any& _unit, const css::uno::Any& _count, const css::uno::Any& _extend ) override; + virtual void SAL_CALL MoveLeft( const css::uno::Any& _unit, const css::uno::Any& _count, const css::uno::Any& _extend ) override; + virtual void SAL_CALL MoveDown( const css::uno::Any& _unit, const css::uno::Any& _count, const css::uno::Any& _extend ) override; + virtual void SAL_CALL MoveUp( const css::uno::Any& _unit, const css::uno::Any& _count, const css::uno::Any& _extend ) override; + virtual void SAL_CALL TypeParagraph() override; + virtual void SAL_CALL InsertParagraph() override; + virtual void SAL_CALL InsertParagraphBefore() override; + virtual void SAL_CALL InsertParagraphAfter() override; + virtual css::uno::Reference< ooo::vba::word::XParagraphFormat > SAL_CALL getParagraphFormat() override; + virtual void SAL_CALL setParagraphFormat( const css::uno::Reference< ooo::vba::word::XParagraphFormat >& rParagraphFormat ) override; + virtual css::uno::Reference< ooo::vba::word::XFind > SAL_CALL getFind() override; + virtual css::uno::Any SAL_CALL getStyle() override; + virtual void SAL_CALL setStyle( const css::uno::Any& _xStyle ) override; + virtual css::uno::Reference< ooo::vba::word::XFont > SAL_CALL getFont() override; + virtual void SAL_CALL TypeBackspace() override; + virtual css::uno::Reference< ooo::vba::word::XRange > SAL_CALL GoTo( const css::uno::Any& _what, const css::uno::Any& _which, const css::uno::Any& _count, const css::uno::Any& _name ) override; + virtual ::sal_Int32 SAL_CALL getLanguageID( ) override; + virtual void SAL_CALL setLanguageID( ::sal_Int32 _languageid ) override; + virtual css::uno::Any SAL_CALL Information( sal_Int32 _type ) override; + virtual void SAL_CALL InsertBreak( const css::uno::Any& _breakType ) override; + virtual css::uno::Any SAL_CALL Tables( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Fields( const css::uno::Any& aIndex ) override; + virtual css::uno::Reference< ooo::vba::word::XHeaderFooter > SAL_CALL getHeaderFooter() override; + virtual css::uno::Any SAL_CALL ShapeRange( ) override; + virtual ::sal_Int32 SAL_CALL getStart() override; + virtual void SAL_CALL setStart( ::sal_Int32 _start ) override; + virtual ::sal_Int32 SAL_CALL getEnd() override; + virtual void SAL_CALL setEnd( ::sal_Int32 _end ) override; + virtual void SAL_CALL SelectRow() override; + virtual void SAL_CALL SelectColumn() override; + virtual css::uno::Any SAL_CALL Rows( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Columns( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Cells( const css::uno::Any& aIndex ) override; + virtual void SAL_CALL Copy( ) override; + virtual void SAL_CALL CopyAsPicture( ) override; + virtual void SAL_CALL Paste( ) override; + virtual void SAL_CALL Collapse( const css::uno::Any& Direction ) override; + virtual void SAL_CALL WholeStory( ) override; + virtual sal_Bool SAL_CALL InRange( const css::uno::Reference< ::ooo::vba::word::XRange >& Range ) override; + virtual void SAL_CALL SplitTable() override; + virtual css::uno::Any SAL_CALL Paragraphs( const css::uno::Any& aIndex ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBASELECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbastyle.cxx b/sw/source/ui/vba/vbastyle.cxx new file mode 100644 index 000000000..5565575f3 --- /dev/null +++ b/sw/source/ui/vba/vbastyle.cxx @@ -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 . + */ + +#include "vbastyle.hxx" +#include <ooo/vba/word/WdStyleType.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <i18nlangtag/languagetag.hxx> +#include "vbafont.hxx" +#include "vbapalette.hxx" +#include "vbaparagraphformat.hxx" +#include "vbastyles.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaStyle::SwVbaStyle( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel>& xModel, const uno::Reference< beans::XPropertySet >& _xPropertySet ) : SwVbaStyle_BASE( xParent, xContext ) , mxModel( xModel ), mxStyleProps( _xPropertySet ) +{ + mxStyle.set( _xPropertySet, uno::UNO_QUERY_THROW ); +} + +void SAL_CALL +SwVbaStyle::setName( const OUString& Name ) +{ + mxStyle->setName(Name); +} + +OUString SAL_CALL +SwVbaStyle::getName() +{ + return mxStyle->getName(); +} + +LanguageType SwVbaStyle::getLanguageID( const uno::Reference< beans::XPropertySet >& xTCProps ) +{ + lang::Locale aLocale; + xTCProps->getPropertyValue("CharLocale") >>= aLocale; + return LanguageTag::convertToLanguageType( aLocale, false); +} + +void SwVbaStyle::setLanguageID( const uno::Reference< beans::XPropertySet >& xTCProps, LanguageType _languageid ) +{ + lang::Locale aLocale = LanguageTag( _languageid ).getLocale(); + xTCProps->setPropertyValue("CharLocale", uno::makeAny( aLocale ) ) ; +} + +::sal_Int32 SAL_CALL SwVbaStyle::getLanguageID() +{ + return static_cast<sal_uInt16>(getLanguageID( mxStyleProps )); +} + +void SAL_CALL SwVbaStyle::setLanguageID( ::sal_Int32 _languageid ) +{ + setLanguageID( mxStyleProps, LanguageType(_languageid) ); +} + +::sal_Int32 SAL_CALL SwVbaStyle::getType() +{ + sal_Int32 nType = word::WdStyleType::wdStyleTypeParagraph; + uno::Reference< lang::XServiceInfo > xServiceInfo( mxStyle, uno::UNO_QUERY_THROW ); + if( xServiceInfo->supportsService("com.sun.star.style.ParagraphStyle") ) + nType = word::WdStyleType::wdStyleTypeParagraph; + else if( xServiceInfo->supportsService("com.sun.star.style.CharacterStyle") ) + nType = word::WdStyleType::wdStyleTypeCharacter; + else + nType = word::WdStyleType::wdStyleTypeList; + return nType; +} + +uno::Reference< word::XFont > SAL_CALL +SwVbaStyle::getFont() +{ + VbaPalette aColors; + return new SwVbaFont( mxParent, mxContext, aColors.getPalette(), mxStyleProps ); +} + +void SwVbaStyle::setStyle( const uno::Reference< beans::XPropertySet >& xParaProps, const uno::Any& rStyle ) +{ + OUString sStyle; + uno::Reference< word::XStyle > xStyle; + if( rStyle >>= xStyle ) + { + sStyle = xStyle->getName(); + } + else + { + rStyle >>= sStyle; + } + + if( !sStyle.isEmpty() ) + { + xParaProps->setPropertyValue("ParaStyleName", uno::makeAny( sStyle ) ); + return; + } + + throw uno::RuntimeException(); +} + +OUString SAL_CALL SwVbaStyle::getNameLocal() +{ + OUString sNameLocal; + mxStyleProps->getPropertyValue("DisplayName") >>= sNameLocal; + return sNameLocal; +} + +void SAL_CALL SwVbaStyle::setNameLocal( const OUString& _namelocal ) +{ + mxStyleProps->setPropertyValue("DisplayName", uno::makeAny( _namelocal ) ); +} + +uno::Reference< word::XParagraphFormat > SAL_CALL SwVbaStyle::getParagraphFormat() +{ + if( word::WdStyleType::wdStyleTypeParagraph != getType() ) + { + throw uno::RuntimeException(); + } + + uno::Reference< text::XTextDocument > xTextDocument( mxModel, uno::UNO_QUERY_THROW ); + return uno::Reference< word::XParagraphFormat >( new SwVbaParagraphFormat( this, mxContext, mxStyleProps ) ); +} + +sal_Bool SAL_CALL SwVbaStyle::getAutomaticallyUpdate() +{ + bool isAutoUpdate = false; + mxStyleProps->getPropertyValue("IsAutoUpdate") >>= isAutoUpdate; + return isAutoUpdate; +} + +void SAL_CALL SwVbaStyle::setAutomaticallyUpdate( sal_Bool _automaticallyupdate ) +{ + mxStyleProps->setPropertyValue("IsAutoUpdate", uno::makeAny( _automaticallyupdate ) ); +} + +uno::Any SAL_CALL SwVbaStyle::getBaseStyle() +{ + // ParentStyle + OUString sBaseStyle; + mxStyleProps->getPropertyValue("ParentStyle") >>= sBaseStyle; + if( sBaseStyle.isEmpty() ) + { + throw uno::RuntimeException(); + } + + uno::Reference< XCollection > xCol( new SwVbaStyles( this, mxContext, mxModel ) ); + return xCol->Item( uno::makeAny( sBaseStyle ), uno::Any() ); +} + +void SAL_CALL SwVbaStyle::setBaseStyle( const uno::Any& _basestyle ) +{ + uno::Reference< word::XStyle > xStyle; + _basestyle >>= xStyle; + if( !xStyle.is() ) + { + throw uno::RuntimeException(); + } + + OUString sBaseStyle = xStyle->getName(); + mxStyleProps->setPropertyValue("ParentStyle", uno::makeAny( sBaseStyle ) ); +} + +uno::Any SAL_CALL SwVbaStyle::getNextParagraphStyle() +{ + //FollowStyle + OUString sFollowStyle; + mxStyleProps->getPropertyValue("FollowStyle") >>= sFollowStyle; + if( sFollowStyle.isEmpty() ) + { + throw uno::RuntimeException(); + } + + uno::Reference< XCollection > xCol( new SwVbaStyles( this, mxContext, mxModel ) ); + return xCol->Item( uno::makeAny( sFollowStyle ), uno::Any() ); +} + +void SAL_CALL SwVbaStyle::setNextParagraphStyle( const uno::Any& _nextparagraphstyle ) +{ + uno::Reference< word::XStyle > xStyle; + _nextparagraphstyle >>= xStyle; + if( !xStyle.is() ) + { + throw uno::RuntimeException(); + } + + OUString sFollowStyle = xStyle->getName(); + mxStyleProps->setPropertyValue("FollowStyle", uno::makeAny( sFollowStyle ) ); +} + +::sal_Int32 SAL_CALL SwVbaStyle::getListLevelNumber() +{ + sal_Int16 nNumberingLevel = 0; + mxStyleProps->getPropertyValue("NumberingLevel") >>= nNumberingLevel; + return nNumberingLevel; +} + +OUString +SwVbaStyle::getServiceImplName() +{ + return "SwVbaStyle"; +} + +uno::Sequence< OUString > +SwVbaStyle::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.XStyle" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbastyle.hxx b/sw/source/ui/vba/vbastyle.hxx new file mode 100644 index 000000000..159b9ebf6 --- /dev/null +++ b/sw/source/ui/vba/vbastyle.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_UI_VBA_VBASTYLE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASTYLE_HXX + +#include <ooo/vba/word/XStyle.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <i18nlangtag/lang.h> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <ooo/vba/word/XFont.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XStyle > SwVbaStyle_BASE; + +class SwVbaStyle : public SwVbaStyle_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::beans::XPropertySet > mxStyleProps; + css::uno::Reference< css::style::XStyle > mxStyle; +public: + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + SwVbaStyle( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::beans::XPropertySet >& _xPropertySet ); + + /// @throws css::uno::RuntimeException + static void setStyle( const css::uno::Reference< css::beans::XPropertySet >& xParaProps, const css::uno::Any& xStyle ); + /// @throws css::uno::RuntimeException + static LanguageType getLanguageID( const css::uno::Reference< css::beans::XPropertySet >& xTCProps ); + /// @throws css::uno::RuntimeException + static void setLanguageID( const css::uno::Reference< css::beans::XPropertySet >& xTCProps, LanguageType _languageid ); + + // Attributes + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName( const OUString& Name ) override; + virtual ::sal_Int32 SAL_CALL getLanguageID( ) override; + virtual void SAL_CALL setLanguageID( ::sal_Int32 _languageid ) override; + virtual ::sal_Int32 SAL_CALL getType() override; + virtual css::uno::Reference< ooo::vba::word::XFont > SAL_CALL getFont() override; + virtual OUString SAL_CALL getNameLocal() override; + virtual void SAL_CALL setNameLocal( const OUString& _namelocal ) override; + virtual css::uno::Reference< ::ooo::vba::word::XParagraphFormat > SAL_CALL getParagraphFormat() override; + virtual sal_Bool SAL_CALL getAutomaticallyUpdate() override; + virtual void SAL_CALL setAutomaticallyUpdate( sal_Bool _automaticallyupdate ) override; + virtual css::uno::Any SAL_CALL getBaseStyle() override; + virtual void SAL_CALL setBaseStyle( const css::uno::Any& _basestyle ) override; + virtual css::uno::Any SAL_CALL getNextParagraphStyle() override; + virtual void SAL_CALL setNextParagraphStyle( const css::uno::Any& _nextparagraphstyle ) override; + virtual ::sal_Int32 SAL_CALL getListLevelNumber() override; + + //XDefaultProperty + virtual OUString SAL_CALL getDefaultPropertyName( ) override { return "Name"; } + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif //SW_VBA_AXIS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbastyles.cxx b/sw/source/ui/vba/vbastyles.cxx new file mode 100644 index 000000000..bfebbb8b4 --- /dev/null +++ b/sw/source/ui/vba/vbastyles.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 "vbastyles.hxx" +#include "vbastyle.hxx" +#include <basic/sberrors.hxx> +#include <cppuhelper/implbase.hxx> +#include <sal/log.hxx> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <ooo/vba/word/WdBuiltinStyle.hpp> +#include <ooo/vba/word/WdStyleType.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +struct BuiltinStyleTable +{ + sal_Int32 wdBuiltinStyle; + const sal_Char* pOOoStyleName; + sal_Int32 wdStyleType; +}; + +} + +static const BuiltinStyleTable aBuiltinStyleTable[] = +{ + { word::WdBuiltinStyle::wdStyleBlockQuotation, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyText, "Text body", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyText2, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyText3, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyTextFirstIndent, "First line indent", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyTextFirstIndent2, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyTextIndent, "Text body indent", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyTextIndent2, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleBodyTextIndent3, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleCaption, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleClosing, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleCommentReference, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleCommentText, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleDate, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleDefaultParagraphFont, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleEmphasis, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleEndnoteReference, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleEndnoteText, "Endnote", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleEnvelopeAddress, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleEnvelopeReturn, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleFooter, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleFootnoteReference, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleFootnoteText, "Footnote", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeader, "Header", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading1, "Heading 1", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading2, "Heading 2", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading3, "Heading 3", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading4, "Heading 4", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading5, "Heading 5", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading6, "Heading 6", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading7, "Heading 7", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading8, "Heading 8", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHeading9, "Heading 9", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlAcronym, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlAddress, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlCite, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlCode, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlDfn, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlKbd, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlNormal, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlPre, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlSamp, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlTt, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHtmlVar, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHyperlink, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleHyperlinkFollowed, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex1, "Index 1", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex2, "Index 2", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex3, "Index 3", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex4, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex5, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex6, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex7, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex8, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndex9, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleIndexHeading, "Index Heading", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleLineNumber, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleList, "List", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleList2, "List 2", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleList3, "List 3", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleList4, "List 4", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleList5, "List 5", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListBullet, "List 1", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListBullet2, "List 2", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListBullet3, "List 3", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListBullet4, "List 4", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListBullet5, "List 5", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListContinue, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListContinue2, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListContinue3, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListContinue4, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListContinue5, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleListNumber, "Numbering 123", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListNumber2, "Numbering ABC", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListNumber3, "Numbering abc", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListNumber4, "Numbering IVX", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleListNumber5, "Numbering ivx", word::WdStyleType::wdStyleTypeList }, + { word::WdBuiltinStyle::wdStyleMacroText, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleMessageHeader, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleNavPane, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleNormal, "Default", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleNormalIndent, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleNormalTable, "Table", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleNoteHeading, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStylePageNumber, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStylePlainText, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleSalutation, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleSignature, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleStrong, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleSubtitle, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTableOfAuthorities, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTableOfFigures, "", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTitle, "Title", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOAHeading, "Contents Heading", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC1, "Contents 1", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC2, "Contents 2", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC3, "Contents 3", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC4, "Contents 4", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC5, "Contents 5", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC6, "Contents 6", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC7, "Contents 7", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC8, "Contents 8", word::WdStyleType::wdStyleTypeParagraph }, + { word::WdBuiltinStyle::wdStyleTOC9, "Contents 9", word::WdStyleType::wdStyleTypeParagraph }, + { 0, nullptr, 0 } +}; + +namespace { + +struct MSOStyleNameTable +{ + const char* pMSOStyleName; + const char* pOOoStyleName; +}; + +} + +static const MSOStyleNameTable aMSOStyleNameTable[] = +{ + { "Normal", "Default" }, + { nullptr, nullptr } +}; + +namespace { + +class StyleCollectionHelper : public ::cppu::WeakImplHelper< container::XNameAccess, + container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< container::XNameAccess > mxParaStyles; + uno::Any cachePos; +public: + explicit StyleCollectionHelper( const uno::Reference< frame::XModel >& _xModel ) + { + // we only concern about the Paragraph styles + uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( _xModel, uno::UNO_QUERY_THROW); + uno::Reference< container::XNameAccess > xStyleFamilies = xStyleSupplier->getStyleFamilies(); + mxParaStyles.set( xStyleFamilies->getByName("ParagraphStyles"), uno::UNO_QUERY_THROW ); + } + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<style::XStyle>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return getCount() > 0; } + // XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + if ( !hasByName(aName) ) + throw container::NoSuchElementException(); + return cachePos; + } + virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + return mxParaStyles->getElementNames(); + } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + // search in the MSOStyleName table first + for( const MSOStyleNameTable* pTable = aMSOStyleNameTable; pTable->pMSOStyleName != nullptr; pTable++ ) + { + if( aName.equalsIgnoreAsciiCaseAscii( pTable->pMSOStyleName ) ) + { + //Found it + OUString sStyleName = OUString::createFromAscii( pTable->pOOoStyleName ); + if( mxParaStyles->hasByName( sStyleName ) ) + { + cachePos = mxParaStyles->getByName( sStyleName ); + return true; + } + return false; + } + } + + if( mxParaStyles->hasByName( aName ) ) + { + cachePos = mxParaStyles->getByName( aName ); + return true; + } + else + { + uno::Sequence< OUString > sElementNames = mxParaStyles->getElementNames(); + auto pStyleName = std::find_if(sElementNames.begin(), sElementNames.end(), + [&aName](const OUString& rStyleName) { return rStyleName.equalsIgnoreAsciiCase( aName ); }); + if (pStyleName != sElementNames.end()) + { + cachePos = mxParaStyles->getByName( *pStyleName ); + return true; + } + } + return false; + } + + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override + { + uno::Reference< container::XIndexAccess > xIndexAccess( mxParaStyles, uno::UNO_QUERY_THROW ); + return xIndexAccess->getCount(); + } + virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< container::XIndexAccess > xIndexAccess( mxParaStyles, uno::UNO_QUERY_THROW ); + return xIndexAccess->getByIndex( Index ); + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + throw uno::RuntimeException("Not implemented" ); + } +}; + +class StylesEnumWrapper : public EnumerationHelper_BASE +{ + SwVbaStyles* pStyles; + sal_Int32 nIndex; +public: + explicit StylesEnumWrapper( SwVbaStyles* _pStyles ) : pStyles( _pStyles ), nIndex( 1 ) {} + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex <= pStyles->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( nIndex <= pStyles->getCount() ) + return pStyles->Item( uno::makeAny( nIndex++ ), uno::Any() ); + throw container::NoSuchElementException(); + } +}; + +} + +SwVbaStyles::SwVbaStyles( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel ) + : SwVbaStyles_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new StyleCollectionHelper( xModel ) ) ), mxModel( xModel ) +{ + mxMSF.set( mxModel, uno::UNO_QUERY_THROW ); +} + +uno::Any +SwVbaStyles::createCollectionObject(const uno::Any& aObject) +{ + uno::Reference< beans::XPropertySet > xStyleProp( aObject, uno::UNO_QUERY_THROW ); + return uno::makeAny( uno::Reference< word::XStyle >( new SwVbaStyle( this, mxContext, mxModel, xStyleProp ) ) ); +} + +uno::Type SAL_CALL +SwVbaStyles::getElementType() +{ + return cppu::UnoType<word::XStyle>::get(); +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwVbaStyles::createEnumeration() +{ + return new StylesEnumWrapper( this ); +} + +uno::Any SAL_CALL +SwVbaStyles::Item( const uno::Any& Index1, const uno::Any& Index2 ) +{ + //handle WdBuiltinStyle + sal_Int32 nIndex = 0; + if( ( Index1 >>= nIndex ) && ( nIndex < 0 ) ) + { + for( const BuiltinStyleTable* pTable = aBuiltinStyleTable; pTable != nullptr; pTable++ ) + { + if( nIndex == pTable->wdBuiltinStyle ) + { + OUString aStyleName = OUString::createFromAscii( pTable->pOOoStyleName ); + if( !aStyleName.isEmpty() ) + { + OUString aStyleType; + switch( pTable->wdStyleType ) + { + case word::WdStyleType::wdStyleTypeParagraph: + case word::WdStyleType::wdStyleTypeTable: + { + aStyleType = "ParagraphStyles"; + break; + } + case word::WdStyleType::wdStyleTypeCharacter: + { + aStyleType = "CharacterStyles"; + break; + } + case word::WdStyleType::wdStyleTypeList: + { + // should use Paragraph style and set the property "NumberingStyleName" + aStyleType = "ParagraphStyles"; + break; + } + default: + DebugHelper::basicexception( ERRCODE_BASIC_INTERNAL_ERROR, OUString() ); + } + uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( mxModel, uno::UNO_QUERY_THROW); + uno::Reference< container::XNameAccess > xStylesAccess( xStyleSupplier->getStyleFamilies()->getByName( aStyleType ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xStyleProps( xStylesAccess->getByName( aStyleName ), uno::UNO_QUERY_THROW ); + // set the property "NumberingStyleName" if it is a listbullet + if( pTable->wdStyleType == word::WdStyleType::wdStyleTypeList ) + { + xStyleProps->setPropertyValue("NumberingStyleName", uno::makeAny( aStyleName ) ); + } + return uno::makeAny( uno::Reference< word::XStyle >( new SwVbaStyle( this, mxContext, mxModel, xStyleProps ) ) ); + } + else + { + SAL_WARN("sw.vba", "the builtin style type is not implemented"); + throw uno::RuntimeException("Not implemented" ); + } + } + } + } + return SwVbaStyles_BASE::Item( Index1, Index2 ); +} + +OUString +SwVbaStyles::getServiceImplName() +{ + return "SwVbaStyles"; +} + +uno::Sequence< OUString > +SwVbaStyles::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.XStyles" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbastyles.hxx b/sw/source/ui/vba/vbastyles.hxx new file mode 100644 index 000000000..390225cd0 --- /dev/null +++ b/sw/source/ui/vba/vbastyles.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_UI_VBA_VBASTYLES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASTYLES_HXX + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <ooo/vba/word/XStyles.hpp> +#include <vbahelper/vbacollectionimpl.hxx> + +typedef CollTestImplHelper< ooo::vba::word::XStyles > SwVbaStyles_BASE; +class SwVbaStyles: public SwVbaStyles_BASE +{ + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::lang::XMultiServiceFactory > mxMSF; +public: + /// @throws css::script::BasicErrorException + /// @throws css::uno::RuntimeException + SwVbaStyles( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel ); + + virtual css::uno::Any SAL_CALL Item(const css::uno::Any& Index1, const css::uno::Any& Index2) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + virtual css::uno::Any createCollectionObject(const css::uno::Any&) override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasystem.cxx b/sw/source/ui/vba/vbasystem.cxx new file mode 100644 index 000000000..f072e5c0d --- /dev/null +++ b/sw/source/ui/vba/vbasystem.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbasystem.hxx" + +#include <ooo/vba/word/WdCursorType.hpp> +#include <tools/config.hxx> +#include <osl/file.hxx> +#include <tools/urlobj.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +PrivateProfileStringListener::~PrivateProfileStringListener() +{ +} + +void PrivateProfileStringListener::Initialize( const OUString& rFileName, const OString& rGroupName, const OString& rKey ) +{ + maFileName = rFileName; + maGroupName = rGroupName; + maKey = rKey; +} +#ifdef _WIN32 +static void lcl_getRegKeyInfo( const OString& sKeyInfo, HKEY& hBaseKey, OString& sSubKey ) +{ + sal_Int32 nBaseKeyIndex = sKeyInfo.indexOf('\\'); + if( nBaseKeyIndex > 0 ) + { + OString sBaseKey = sKeyInfo.copy( 0, nBaseKeyIndex ); + sSubKey = sKeyInfo.copy( nBaseKeyIndex + 1 ); + if( sBaseKey == "HKEY_CURRENT_USER" ) + { + hBaseKey = HKEY_CURRENT_USER; + } + else if( sBaseKey == "HKEY_LOCAL_MACHINE" ) + { + hBaseKey = HKEY_LOCAL_MACHINE; + } + else if( sBaseKey == "HKEY_CLASSES_ROOT" ) + { + hBaseKey = HKEY_CLASSES_ROOT; + } + else if( sBaseKey == "HKEY_USERS" ) + { + hBaseKey = HKEY_USERS; + } + else if( sBaseKey == "HKEY_CURRENT_CONFIG" ) + { + hBaseKey = HKEY_CURRENT_CONFIG; + } + } +} +#endif + +uno::Any PrivateProfileStringListener::getValueEvent() +{ + // get the private profile string + OUString sValue; + if(maFileName.isEmpty()) + { + // get key/value from Windows registry +#ifdef _WIN32 + HKEY hBaseKey = nullptr; + OString sSubKey; + lcl_getRegKeyInfo( maGroupName, hBaseKey, sSubKey ); + if( hBaseKey != nullptr ) + { + HKEY hKey = nullptr; + LPCSTR lpSubKey = sSubKey.getStr(); + // We use RegOpenKeyExA here for convenience, because we already have subkey name as 8-bit string + LONG lResult = RegOpenKeyExA( hBaseKey, lpSubKey, 0, KEY_QUERY_VALUE, &hKey ); + if( ERROR_SUCCESS == lResult ) + { + OUString sUValName = OStringToOUString(maKey, RTL_TEXTENCODING_DONTKNOW); + LPCWSTR lpValueName = o3tl::toW(sUValName.getStr()); + WCHAR szBuffer[1024]; + DWORD cbData = sizeof(szBuffer); + lResult = RegQueryValueExW( hKey, lpValueName, nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &cbData ); + RegCloseKey( hKey ); + // https://msdn.microsoft.com/en-us/ms724911 mentions that + // "the string may not have been stored with the proper terminating null characters" + szBuffer[std::min(size_t(cbData / sizeof(szBuffer[0])), SAL_N_ELEMENTS(szBuffer)-1)] = 0; + sValue = o3tl::toU(szBuffer); + } + } +#else + throw uno::RuntimeException("Only support on Windows" ); +#endif + } + + // get key/value from a file + Config aCfg( maFileName ); + aCfg.SetGroup( maGroupName ); + sValue = OStringToOUString(aCfg.ReadKey(maKey), RTL_TEXTENCODING_DONTKNOW); + + + return uno::makeAny( sValue ); +} + +void PrivateProfileStringListener::setValueEvent( const css::uno::Any& value ) +{ + // set the private profile string + OUString aValue; + value >>= aValue; + if(maFileName.isEmpty()) + { + //set value into Windows registry +#ifdef _WIN32 + HKEY hBaseKey = nullptr; + OString sSubKey; + lcl_getRegKeyInfo( maGroupName, hBaseKey, sSubKey ); + if( hBaseKey != nullptr ) + { + HKEY hKey = nullptr; + LPCSTR lpSubKey = sSubKey.getStr(); + // We use RegCreateKeyExA here for convenience, because we already have subkey name as 8-bit string + LONG lResult = RegCreateKeyExA( hBaseKey, lpSubKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hKey, nullptr ); + if( ERROR_SUCCESS == lResult ) + { + DWORD cbData = sizeof(WCHAR) * (aValue.getLength() + 1); + OUString sUValName = OStringToOUString(maKey, RTL_TEXTENCODING_DONTKNOW); + LPCWSTR lpValueName = o3tl::toW(sUValName.getStr()); + lResult = RegSetValueExW( hKey, lpValueName, 0 /* Reserved */, REG_SZ, reinterpret_cast<BYTE const *>(aValue.getStr()), cbData ); + RegCloseKey( hKey ); + } + } + return; +#else + throw uno::RuntimeException("Not implemented" ); +#endif + } + + // set value into a file + Config aCfg( maFileName ); + aCfg.SetGroup( maGroupName ); + aCfg.WriteKey( maKey, OUStringToOString(aValue, RTL_TEXTENCODING_DONTKNOW) ); + + +} + +SwVbaSystem::SwVbaSystem( uno::Reference<uno::XComponentContext > const & xContext ): SwVbaSystem_BASE( uno::Reference< XHelperInterface >(), xContext ) +{ +} + +SwVbaSystem::~SwVbaSystem() +{ +} + +sal_Int32 SAL_CALL +SwVbaSystem::getCursor() +{ + PointerStyle nPointerStyle = getPointerStyle( getCurrentWordDoc(mxContext) ); + + switch( nPointerStyle ) + { + case PointerStyle::Arrow: + return word::WdCursorType::wdCursorNorthwestArrow; + case PointerStyle::Null: + return word::WdCursorType::wdCursorNormal; + case PointerStyle::Wait: + return word::WdCursorType::wdCursorWait; + case PointerStyle::Text: + return word::WdCursorType::wdCursorIBeam; + default: + return word::WdCursorType::wdCursorNormal; + } +} + +void SAL_CALL +SwVbaSystem::setCursor( sal_Int32 _cursor ) +{ + try + { + switch( _cursor ) + { + case word::WdCursorType::wdCursorNorthwestArrow: + { + setCursorHelper( getCurrentWordDoc(mxContext), PointerStyle::Arrow, false ); + break; + } + case word::WdCursorType::wdCursorWait: + { + //It will set the edit window, toobar and statusbar's mouse pointer. + setCursorHelper( getCurrentWordDoc(mxContext), PointerStyle::Wait, true ); + break; + } + case word::WdCursorType::wdCursorIBeam: + { + //It will set the edit window, toobar and statusbar's mouse pointer. + setCursorHelper( getCurrentWordDoc( mxContext ), PointerStyle::Text, true ); + break; + } + case word::WdCursorType::wdCursorNormal: + { + setCursorHelper( getCurrentWordDoc( mxContext ), PointerStyle::Null, false ); + break; + } + default: + throw uno::RuntimeException("Unknown value for Cursor pointer" ); + // TODO: isn't this a flaw in the API? It should be allowed to throw an + // IllegalArgumentException, or so + } + } + catch( const uno::Exception& ) + { + } +} + +uno::Any SAL_CALL +SwVbaSystem::PrivateProfileString( const OUString& rFilename, const OUString& rSection, const OUString& rKey ) +{ + // FIXME: need to detect whether it is a relative file path + // we need to detect if this is a URL, if not then assume it's a file path + OUString sFileUrl; + if( !rFilename.isEmpty() ) + { + INetURLObject aObj; + aObj.SetURL( rFilename ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + if ( bIsURL ) + sFileUrl = rFilename; + else + osl::FileBase::getFileURLFromSystemPath( rFilename, sFileUrl); + } + + OString aGroupName(OUStringToOString(rSection, RTL_TEXTENCODING_DONTKNOW)); + OString aKey(OUStringToOString(rKey, RTL_TEXTENCODING_DONTKNOW)); + maPrivateProfileStringListener.Initialize( sFileUrl, aGroupName, aKey ); + + return uno::makeAny( uno::Reference< XPropValue > ( new ScVbaPropValue( &maPrivateProfileStringListener ) ) ); +} + +OUString +SwVbaSystem::getServiceImplName() +{ + return "SwVbaSystem"; +} + +uno::Sequence< OUString > +SwVbaSystem::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.System" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbasystem.hxx b/sw/source/ui/vba/vbasystem.hxx new file mode 100644 index 000000000..c0acb2ad4 --- /dev/null +++ b/sw/source/ui/vba/vbasystem.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_UI_VBA_VBASYSTEM_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBASYSTEM_HXX + +#include <ooo/vba/word/XSystem.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <vbahelper/vbapropvalue.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XSystem > SwVbaSystem_BASE; + +class PrivateProfileStringListener : public PropListener +{ +private: + OUString maFileName; + OString maGroupName; + OString maKey; +public: + PrivateProfileStringListener(){}; + virtual ~PrivateProfileStringListener(); + void Initialize( const OUString& rFileName, const OString& rGroupName, const OString& rKey ); + + //PropListener + virtual void setValueEvent( const css::uno::Any& value ) override; + virtual css::uno::Any getValueEvent() override; +}; + +class SwVbaSystem : public SwVbaSystem_BASE +{ +private: + PrivateProfileStringListener maPrivateProfileStringListener; + +public: + explicit SwVbaSystem( css::uno::Reference< css::uno::XComponentContext > const & m_xContext ); + virtual ~SwVbaSystem() override; + + // XSystem + virtual sal_Int32 SAL_CALL getCursor() override; + virtual void SAL_CALL setCursor( sal_Int32 _cursor ) override; + virtual css::uno::Any SAL_CALL PrivateProfileString( const OUString& rFilename, const OUString& rSection, const OUString& rKey ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBASYSTEM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatable.cxx b/sw/source/ui/vba/vbatable.cxx new file mode 100644 index 000000000..501168abf --- /dev/null +++ b/sw/source/ui/vba/vbatable.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "vbatable.hxx" +#include "vbarange.hxx" +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/table/XTableRows.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include "vbaborders.hxx" +#include "vbapalette.hxx" +#include "vbarows.hxx" +#include "vbacolumns.hxx" + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaTable::SwVbaTable( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& rDocument, const uno::Reference< text::XTextTable >& xTextTable) : SwVbaTable_BASE( rParent, rContext ), mxTextDocument( rDocument ) +{ + mxTextTable.set( xTextTable, uno::UNO_SET_THROW ); +} + +uno::Reference< word::XRange > SAL_CALL +SwVbaTable::Range( ) +{ + return new SwVbaRange( mxParent, mxContext, mxTextDocument, mxTextTable->getAnchor() ); +} + +void SAL_CALL +SwVbaTable::Select( ) +{ + uno::Reference< frame::XModel > xModel( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< frame::XController > xController = xModel->getCurrentController(); + + uno::Reference< text::XTextViewCursorSupplier > xViewCursorSupplier( xController, uno::UNO_QUERY_THROW ); + uno::Reference< view::XSelectionSupplier > xSelectionSupplier( xController, uno::UNO_QUERY_THROW ); + + // set the view cursor to the start of the table. + xSelectionSupplier->select( uno::makeAny( mxTextTable ) ); + + // go to the end of the table and span the view + uno::Reference< text::XTextViewCursor > xCursor = xViewCursorSupplier->getViewCursor(); + xCursor->gotoEnd(true); + +} + +void SAL_CALL +SwVbaTable::Delete( ) +{ + uno::Reference< table::XTableRows > xRows( mxTextTable->getRows() ); + xRows->removeByIndex( 0, xRows->getCount() ); +} + +OUString SAL_CALL +SwVbaTable::getName() +{ + uno::Reference< container::XNamed > xNamed( mxTextTable, uno::UNO_QUERY_THROW ); + return xNamed->getName(); +} + +uno::Any SAL_CALL +SwVbaTable::Borders( const uno::Any& index ) +{ + uno::Reference< table::XCellRange > aCellRange( mxTextTable, uno::UNO_QUERY_THROW ); + VbaPalette aPalette; + uno::Reference< XCollection > xCol( new SwVbaBorders( this, mxContext, aCellRange, aPalette ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaTable::Rows( const uno::Any& index ) +{ + uno::Reference< table::XTableRows > xTableRows( mxTextTable->getRows(), uno::UNO_SET_THROW ); + uno::Reference< XCollection > xCol( new SwVbaRows( this, mxContext, mxTextTable, xTableRows ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +uno::Any SAL_CALL +SwVbaTable::Columns( const uno::Any& index ) +{ + uno::Reference< table::XTableColumns > xTableColumns( mxTextTable->getColumns(), uno::UNO_SET_THROW ); + uno::Reference< XCollection > xCol( new SwVbaColumns( this, mxContext, mxTextTable, xTableColumns ) ); + if ( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +// XHelperInterface +OUString +SwVbaTable::getServiceImplName() +{ + return "SwVbaTable"; +} + +uno::Sequence<OUString> +SwVbaTable::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Table" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatable.hxx b/sw/source/ui/vba/vbatable.hxx new file mode 100644 index 000000000..37bdd332f --- /dev/null +++ b/sw/source/ui/vba/vbatable.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_UI_VBA_VBATABLE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABLE_HXX +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextTable.hpp> +#include <ooo/vba/word/XRange.hpp> +#include <ooo/vba/word/XTable.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XTable > SwVbaTable_BASE; + +class SwVbaTable : public SwVbaTable_BASE +{ + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + css::uno::Reference< css::text::XTextTable > mxTextTable; +public: + /// @throws css::uno::RuntimeException + SwVbaTable( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& rDocument, const css::uno::Reference< css::text::XTextTable >& xTextTable); + virtual css::uno::Reference< ::ooo::vba::word::XRange > SAL_CALL Range( ) override; + virtual void SAL_CALL Select( ) override; + virtual void SAL_CALL Delete( ) override; + virtual OUString SAL_CALL getName( ) override; + virtual css::uno::Any SAL_CALL Borders( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Rows( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL Columns( const css::uno::Any& aIndex ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatablehelper.cxx b/sw/source/ui/vba/vbatablehelper.cxx new file mode 100644 index 000000000..b0b88ae2f --- /dev/null +++ b/sw/source/ui/vba/vbatablehelper.cxx @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vbahelper/vbahelper.hxx> +#include "vbatablehelper.hxx" +#include <swtable.hxx> +#include <unotbl.hxx> + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +#define UNO_TABLE_COLUMN_SUM 10000 + +SwVbaTableHelper::SwVbaTableHelper( const uno::Reference< text::XTextTable >& xTextTable ) : mxTextTable( xTextTable ) +{ + pTable = GetSwTable( mxTextTable ); +} + +SwTable* SwVbaTableHelper::GetSwTable( const uno::Reference< text::XTextTable >& xTextTable ) +{ + uno::Reference< lang::XUnoTunnel > xTunnel( xTextTable, uno::UNO_QUERY_THROW ); + SwXTextTable* pXTextTable = reinterpret_cast< SwXTextTable * >( sal::static_int_cast< sal_IntPtr >(xTunnel->getSomething(SwXTextTable::getUnoTunnelId()))); + if( !pXTextTable ) + throw uno::RuntimeException(); + + SwFrameFormat* pFrameFormat = pXTextTable->GetFrameFormat(); + if( !pFrameFormat ) + throw uno::RuntimeException(); + + SwTable* pTable = SwTable::FindTable( pFrameFormat ); + return pTable; +} + +sal_Int32 SwVbaTableHelper::getTabColumnsCount( sal_Int32 nRowIndex ) +{ + sal_Int32 nRet = 0; + if(!pTable->IsTableComplex()) + { + SwTableLines& rLines = pTable->GetTabLines(); + SwTableLine* pLine = rLines[ nRowIndex ]; + nRet = pLine->GetTabBoxes().size(); + } + return nRet; +} + +sal_Int32 SwVbaTableHelper::getTabColumnsMaxCount( ) +{ + sal_Int32 nRet = 0; + sal_Int32 nRowCount = pTable->GetTabLines().size(); + for( sal_Int32 index = 0; index < nRowCount; index++ ) + { + sal_Int32 nColCount = getTabColumnsCount( index ); + if( nRet < nColCount ) + nRet = nColCount; + } + return nRet; +} + +sal_Int32 SwVbaTableHelper::getTabRowIndex( const OUString& rCellName ) +{ + sal_Int32 nRet = 0; + SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox( rCellName )); + if( !pBox ) + throw uno::RuntimeException(); + + const SwTableLine* pLine = pBox->GetUpper(); + const SwTableLines* pLines = pLine->GetUpper() + ? &pLine->GetUpper()->GetTabLines() : &pTable->GetTabLines(); + nRet = pLines->GetPos( pLine ); + return nRet; +} + +sal_Int32 SwVbaTableHelper::getTabColIndex( const OUString& rCellName ) +{ + const SwTableBox* pBox = pTable->GetTableBox( rCellName ); + if( !pBox ) + throw uno::RuntimeException(); + return pBox->GetUpper()->GetBoxPos( pBox ); +} + +OUString SwVbaTableHelper::getColumnStr( sal_Int32 nCol ) +{ + const sal_Int32 coDiff = 52; // 'A'-'Z' 'a' - 'z' + sal_Int32 nCalc = 0; + + OUString sRet; + do{ + nCalc = nCol % coDiff; + if( nCalc >= 26 ) + sRet = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + sRet; + else + sRet = OUStringChar( sal_Unicode('A' + nCalc) ) + sRet; + + if( 0 == ( nCol = nCol - nCalc ) ) + break; + nCol /= coDiff; + --nCol; + }while(true); + return sRet; +} + +sal_Int32 SwVbaTableHelper::getTableWidth( ) const +{ + sal_Int32 nWidth = 0; + bool isWidthRelatvie = false; + uno::Reference< beans::XPropertySet > xTableProps( mxTextTable, uno::UNO_QUERY_THROW ); + xTableProps->getPropertyValue("IsWidthRelative") >>= isWidthRelatvie; + if( isWidthRelatvie ) + { + xTableProps->getPropertyValue("RelativeWidth") >>= nWidth; + } + else + { + xTableProps->getPropertyValue("Width") >>= nWidth; + } + return nWidth; +} + +SwTableBox* SwVbaTableHelper::GetTabBox( sal_Int32 nCol, sal_Int32 nRow ) +{ + SwTableLines& rLines = pTable->GetTabLines(); + sal_Int32 nRowCount = rLines.size(); + if (nRow < 0 || nRow >= nRowCount) + throw uno::RuntimeException(); + + SwTableLine* pLine = rLines[ nRow ]; + sal_Int32 nColCount = pLine->GetTabBoxes().size(); + if (nCol < 0 || nCol >= nColCount) + throw uno::RuntimeException(); + + SwTableBox* pStart = pLine->GetTabBoxes()[ nCol ]; + + if( !pStart ) + throw uno::RuntimeException(); + + return pStart; +} + +void SwVbaTableHelper::InitTabCols( SwTabCols& rCols, const SwTableBox *pStart ) +{ + rCols.SetLeftMin ( 0 ); + rCols.SetLeft ( 0 ); + rCols.SetRight ( UNO_TABLE_COLUMN_SUM ); + rCols.SetRightMax( UNO_TABLE_COLUMN_SUM ); + pTable->GetTabCols( rCols, pStart ); +} + +sal_Int32 SwVbaTableHelper::GetColCount( SwTabCols const & rCols ) +{ + sal_Int32 nCount = 0; + for( size_t i = 0; i < rCols.Count(); ++i ) + if(rCols.IsHidden(i)) + nCount ++; + return rCols.Count() - nCount; +} + +sal_Int32 SwVbaTableHelper::GetRightSeparator( SwTabCols const & rCols, sal_Int32 nNum) +{ + OSL_ENSURE( nNum < GetColCount( rCols ) ,"Index out of range"); + sal_Int32 i = 0; + while( nNum >= 0 ) + { + if( !rCols.IsHidden(i) ) + nNum--; + i++; + } + return i - 1; +} + +sal_Int32 SwVbaTableHelper::GetColWidth( sal_Int32 nCol, sal_Int32 nRow ) +{ + SwTableBox* pStart = GetTabBox( nCol, nRow ); + SwTabCols aCols; + InitTabCols( aCols, pStart ); + sal_Int32 nWidth = GetColWidth( aCols, nCol ); + + sal_Int32 nTableWidth = getTableWidth( ); + double dAbsWidth = ( static_cast<double>(nWidth) / UNO_TABLE_COLUMN_SUM ) * static_cast<double>(nTableWidth); + return static_cast<sal_Int32>(Millimeter::getInPoints( static_cast<int>(dAbsWidth) )); +} + +sal_Int32 SwVbaTableHelper::GetColWidth( SwTabCols& rCols, sal_Int32 nNum ) +{ + SwTwips nWidth = 0; + + if( rCols.Count() > 0 ) + { + if(rCols.Count() == static_cast<size_t>(GetColCount( rCols ))) + { + if(static_cast<size_t>(nNum) == rCols.Count()) + nWidth = rCols.GetRight() - rCols[nNum-1]; + else + { + nWidth = rCols[nNum]; + if(nNum == 0) + nWidth -= rCols.GetLeft(); + else + nWidth -= rCols[nNum-1]; + } + } + else + { + SwTwips nRValid = nNum < GetColCount( rCols ) ? + rCols[GetRightSeparator( rCols, nNum )]: + rCols.GetRight(); + SwTwips nLValid = nNum ? + rCols[GetRightSeparator( rCols, nNum - 1 )]: + rCols.GetLeft(); + nWidth = nRValid - nLValid; + } + } + else + nWidth = rCols.GetRight(); + + return nWidth; +} + +void SwVbaTableHelper::SetColWidth( sal_Int32 _width, sal_Int32 nCol, sal_Int32 nRow, bool bCurRowOnly ) +{ + double dAbsWidth = Millimeter::getInHundredthsOfOneMillimeter( _width ); + sal_Int32 nTableWidth = getTableWidth( ); + if (!nTableWidth) + throw uno::RuntimeException(); + sal_Int32 nNewWidth = dAbsWidth/nTableWidth * UNO_TABLE_COLUMN_SUM; + + SwTableBox* pStart = GetTabBox( nCol, nRow ); + SwTabCols aOldCols; + InitTabCols( aOldCols, pStart ); + + SwTabCols aCols( aOldCols ); + if ( aCols.Count() > 0 ) + { + SwTwips nWidth = GetColWidth( aCols, nCol); + + int nDiff = nNewWidth - nWidth; + if( !nCol ) + aCols[ GetRightSeparator(aCols, 0) ] += nDiff; + else if( nCol < GetColCount( aCols ) ) + { + if(nDiff < GetColWidth( aCols, nCol + 1) - MINLAY) + aCols[ GetRightSeparator( aCols, nCol ) ] += nDiff; + else + { + int nDiffLeft = nDiff - static_cast<int>(GetColWidth( aCols, nCol + 1)) + int(MINLAY); + aCols[ GetRightSeparator( aCols, nCol ) ] += (nDiff - nDiffLeft); + aCols[ GetRightSeparator( aCols, nCol - 1 ) ] -= nDiffLeft; + } + } + else + aCols[ GetRightSeparator( aCols, nCol-1 ) ] -= nDiff; + } + else + aCols.SetRight( std::min( static_cast<long>(nNewWidth), aCols.GetRightMax()) ); + + pTable->SetTabCols(aCols, aOldCols, pStart, bCurRowOnly ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatablehelper.hxx b/sw/source/ui/vba/vbatablehelper.hxx new file mode 100644 index 000000000..2b6fedbeb --- /dev/null +++ b/sw/source/ui/vba/vbatablehelper.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UI_VBA_VBATABLEHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABLEHELPER_HXX + +#include <com/sun/star/text/XTextTable.hpp> +#include <swtable.hxx> +#include <tabcol.hxx> + +class SwVbaTableHelper +{ +private: + css::uno::Reference< css::text::XTextTable > mxTextTable; + SwTable* pTable; + +private: + /// @throws css::uno::RuntimeException + SwTableBox* GetTabBox( sal_Int32 nCol, sal_Int32 nRow ); + void InitTabCols( SwTabCols& rCols, const SwTableBox *pStart ); + static sal_Int32 GetRightSeparator( SwTabCols const & rCols, sal_Int32 nNum); + static sal_Int32 GetColCount( SwTabCols const & rCols ); + /// @throws css::uno::RuntimeException + static sal_Int32 GetColWidth( SwTabCols& rCols, sal_Int32 nNum ); + +public: + /// @throws css::uno::RuntimeException + explicit SwVbaTableHelper( const css::uno::Reference< css::text::XTextTable >& xTextTable ); + /// @throws css::uno::RuntimeException + sal_Int32 getTabColumnsCount( sal_Int32 nRowIndex ); + /// @throws css::uno::RuntimeException + sal_Int32 getTabColumnsMaxCount( ); + /// @throws css::uno::RuntimeException + sal_Int32 getTabRowIndex( const OUString& sCellName ); + /// @throws css::uno::RuntimeException + sal_Int32 getTabColIndex( const OUString& sCellName ); + /// @throws css::uno::RuntimeException + sal_Int32 getTableWidth( ) const; + + /// @throws css::uno::RuntimeException + sal_Int32 GetColWidth( sal_Int32 nCol, sal_Int32 nRow = 0 ); + /// @throws css::uno::RuntimeException + void SetColWidth( sal_Int32 _width, sal_Int32 nCol, sal_Int32 nRow = 0, bool bCurRowOnly = false ); + + /// @throws css::uno::RuntimeException + static SwTable* GetSwTable( const css::uno::Reference< css::text::XTextTable >& xTextTable ); + static OUString getColumnStr( sal_Int32 nCol ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatableofcontents.cxx b/sw/source/ui/vba/vbatableofcontents.cxx new file mode 100644 index 000000000..2afceac20 --- /dev/null +++ b/sw/source/ui/vba/vbatableofcontents.cxx @@ -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 . + */ +#include "vbatableofcontents.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <ooo/vba/word/WdTabLeader.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaTableOfContents::SwVbaTableOfContents( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Reference< text::XTextDocument >& xDoc, const uno::Reference< text::XDocumentIndex >& xDocumentIndex ) : + SwVbaTableOfContents_BASE( rParent, rContext ), mxTextDocument( xDoc ), mxDocumentIndex( xDocumentIndex ) +{ + mxTocProps.set( mxDocumentIndex, uno::UNO_QUERY_THROW ); +} + +SwVbaTableOfContents::~SwVbaTableOfContents() +{ +} + +::sal_Int32 SAL_CALL SwVbaTableOfContents::getLowerHeadingLevel() +{ + sal_Int16 nLevel = 0; + mxTocProps->getPropertyValue("Level") >>= nLevel; + return nLevel; +} + +void SAL_CALL SwVbaTableOfContents::setLowerHeadingLevel( ::sal_Int32 _lowerheadinglevel ) +{ + mxTocProps->setPropertyValue("Level", uno::makeAny( sal_Int8( _lowerheadinglevel ) ) ); +} + +::sal_Int32 SAL_CALL SwVbaTableOfContents::getTabLeader() +{ + // not support in Writer + return word::WdTabLeader::wdTabLeaderDots; +} + +void SAL_CALL SwVbaTableOfContents::setTabLeader( ::sal_Int32 /*_tableader*/ ) +{ + // not support in Writer +} + +sal_Bool SAL_CALL SwVbaTableOfContents::getUseFields() +{ + bool bUseFields = false; + mxTocProps->getPropertyValue("CreateFromMarks") >>= bUseFields; + return bUseFields; +} + +void SAL_CALL SwVbaTableOfContents::setUseFields( sal_Bool _useFields ) +{ + mxTocProps->setPropertyValue("CreateFromMarks", uno::makeAny( _useFields ) ); +} + +sal_Bool SAL_CALL SwVbaTableOfContents::getUseOutlineLevels() +{ + bool bUseOutlineLevels = false; + mxTocProps->getPropertyValue("CreateFromOutline") >>= bUseOutlineLevels; + return bUseOutlineLevels; +} + +void SAL_CALL SwVbaTableOfContents::setUseOutlineLevels( sal_Bool _useOutlineLevels ) +{ + mxTocProps->setPropertyValue("CreateFromOutline", uno::makeAny( _useOutlineLevels ) ); +} + +void SAL_CALL SwVbaTableOfContents::Delete( ) +{ + uno::Reference< text::XTextContent > xTextContent( mxDocumentIndex, uno::UNO_QUERY_THROW ); + mxTextDocument->getText()->removeTextContent( xTextContent ); +} + +void SAL_CALL SwVbaTableOfContents::Update( ) +{ + mxDocumentIndex->update(); +} + +OUString +SwVbaTableOfContents::getServiceImplName() +{ + return "SwVbaTableOfContents"; +} + +uno::Sequence< OUString > +SwVbaTableOfContents::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.TableOfContents" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatableofcontents.hxx b/sw/source/ui/vba/vbatableofcontents.hxx new file mode 100644 index 000000000..effdd6c93 --- /dev/null +++ b/sw/source/ui/vba/vbatableofcontents.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_UI_VBA_VBATABLEOFCONTENTS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABLEOFCONTENTS_HXX + +#include <ooo/vba/word/XTableOfContents.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XTableOfContents > SwVbaTableOfContents_BASE; + +class SwVbaTableOfContents : public SwVbaTableOfContents_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + css::uno::Reference< css::text::XDocumentIndex > mxDocumentIndex; + css::uno::Reference< css::beans::XPropertySet > mxTocProps; + +public: + /// @throws css::uno::RuntimeException + SwVbaTableOfContents( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, const css::uno::Reference< css::text::XTextDocument >& xDoc, const css::uno::Reference< css::text::XDocumentIndex >& xDocumentIndex ); + virtual ~SwVbaTableOfContents() override; + + // Attributes + virtual ::sal_Int32 SAL_CALL getLowerHeadingLevel() override; + virtual void SAL_CALL setLowerHeadingLevel( ::sal_Int32 _lowerheadinglevel ) override; + virtual ::sal_Int32 SAL_CALL getTabLeader() override; + virtual void SAL_CALL setTabLeader( ::sal_Int32 _tableader ) override; + virtual sal_Bool SAL_CALL getUseFields() override; + virtual void SAL_CALL setUseFields( sal_Bool _useFields ) override; + virtual sal_Bool SAL_CALL getUseOutlineLevels() override; + virtual void SAL_CALL setUseOutlineLevels( sal_Bool _useOutlineLevels ) override; + + // Methods + virtual void SAL_CALL Delete( ) override; + virtual void SAL_CALL Update( ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBATABLEOFCONTENTS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatables.cxx b/sw/source/ui/vba/vbatables.cxx new file mode 100644 index 000000000..286bb25a8 --- /dev/null +++ b/sw/source/ui/vba/vbatables.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 "vbatables.hxx" +#include "vbatable.hxx" +#include "vbarange.hxx" +#include "wordvbahelper.hxx" +#include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace css; + +static uno::Reference< container::XIndexAccess > lcl_getTables( const uno::Reference< frame::XModel >& xDoc ) +{ + uno::Reference< container::XIndexAccess > xTables; + uno::Reference< text::XTextTablesSupplier > xSupp( xDoc, uno::UNO_QUERY ); + if ( xSupp.is() ) + xTables.set( xSupp->getTextTables(), uno::UNO_QUERY_THROW ); + return xTables; +} + +static uno::Any lcl_createTable( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xDocument, const uno::Any& aSource ) +{ + uno::Reference< text::XTextTable > xTextTable( aSource, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextDocument > xTextDocument( xDocument, uno::UNO_QUERY_THROW ); + uno::Reference< word::XTable > xTable( new SwVbaTable( xParent, xContext, xTextDocument, xTextTable ) ); + return uno::makeAny( xTable ); +} + +static bool lcl_isInHeaderFooter( const uno::Reference< text::XTextTable >& xTable ) +{ + uno::Reference< text::XTextContent > xTextContent( xTable, uno::UNO_QUERY_THROW ); + uno::Reference< text::XText > xText = xTextContent->getAnchor()->getText(); + uno::Reference< lang::XServiceInfo > xServiceInfo( xText, uno::UNO_QUERY_THROW ); + OUString aImplName = xServiceInfo->getImplementationName(); + return aImplName == "SwXHeadFootText"; +} + +typedef std::vector< uno::Reference< text::XTextTable > > XTextTableVec; + +namespace { + +class TableCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XNameAccess > +{ + XTextTableVec mxTables; + XTextTableVec::iterator cachePos; + +public: + explicit TableCollectionHelper( const uno::Reference< frame::XModel >& xDocument ) + { + // only count the tables in the body text, not in the header/footer + uno::Reference< container::XIndexAccess > xTables = lcl_getTables( xDocument ); + sal_Int32 nCount = xTables->getCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< text::XTextTable > xTable( xTables->getByIndex( i ) , uno::UNO_QUERY_THROW ); + if( !lcl_isInHeaderFooter( xTable ) ) + mxTables.push_back( xTable ); + } + cachePos = mxTables.begin(); + } + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override + { + return mxTables.size(); + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + uno::Reference< text::XTextTable > xTable( mxTables[ Index ], uno::UNO_SET_THROW ); + return uno::makeAny( xTable ); + } + // XElementAccess + virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<text::XTextTable>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override { return getCount() > 0 ; } + // XNameAccess + virtual uno::Any SAL_CALL getByName( const OUString& aName ) override + { + if ( !hasByName(aName) ) + throw container::NoSuchElementException(); + uno::Reference< text::XTextTable > xTable( *cachePos, uno::UNO_SET_THROW ); + return uno::makeAny( xTable ); + } + virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override + { + uno::Sequence< OUString > sNames( mxTables.size() ); + OUString* pString = sNames.getArray(); + for ( const auto& rxTable : mxTables ) + { + uno::Reference< container::XNamed > xName( rxTable, uno::UNO_QUERY_THROW ); + *pString = xName->getName(); + ++pString; + } + return sNames; + } + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override + { + cachePos = mxTables.begin(); + XTextTableVec::iterator it_end = mxTables.end(); + for ( ; cachePos != it_end; ++cachePos ) + { + uno::Reference< container::XNamed > xName( *cachePos, uno::UNO_QUERY_THROW ); + if ( aName.equalsIgnoreAsciiCase( xName->getName() ) ) + break; + } + return ( cachePos != it_end ); + } +}; + +class TableEnumerationImpl : public ::cppu::WeakImplHelper< css::container::XEnumeration > +{ + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< frame::XModel > mxDocument; + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 mnCurIndex; +public: + TableEnumerationImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xDocument, const uno::Reference< container::XIndexAccess >& xIndexAccess ) : mxParent( xParent ), mxContext( xContext ), mxDocument( xDocument ), mxIndexAccess( xIndexAccess ), mnCurIndex(0) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( mnCurIndex < mxIndexAccess->getCount() ); + } + virtual uno::Any SAL_CALL nextElement( ) override + { + if ( !hasMoreElements() ) + throw container::NoSuchElementException(); + return lcl_createTable( mxParent, mxContext, mxDocument, mxIndexAccess->getByIndex( mnCurIndex++ ) ); + } + +}; + +} + +SwVbaTables::SwVbaTables( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xDocument ) : SwVbaTables_BASE( xParent, xContext , uno::Reference< container::XIndexAccess >( new TableCollectionHelper( xDocument ) ) ), mxDocument( xDocument ) +{ +} + +uno::Reference< word::XTable > SAL_CALL +SwVbaTables::Add( const uno::Reference< word::XRange >& Range, const uno::Any& NumRows, const uno::Any& NumColumns, const uno::Any& /*DefaultTableBehavior*/, const uno::Any& /*AutoFitBehavior*/ ) +{ + sal_Int32 nCols = 0; + sal_Int32 nRows = 0; + SwVbaRange* pVbaRange = dynamic_cast< SwVbaRange* >( Range.get() ); + // Preconditions + if ( !( pVbaRange && ( NumRows >>= nRows ) && ( NumColumns >>= nCols ) ) ) + throw uno::RuntimeException(); // #FIXME better exception?? + if ( nCols <= 0 || nRows <= 0 ) + throw uno::RuntimeException(); // #FIXME better exception?? + + uno::Reference< frame::XModel > xModel( pVbaRange->getDocument(), uno::UNO_QUERY_THROW ); + uno::Reference< lang::XMultiServiceFactory > xMsf( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextRange > xTextRange = pVbaRange->getXTextRange(); + + uno::Reference< text::XTextTable > xTable; + xTable.set( xMsf->createInstance("com.sun.star.text.TextTable"), uno::UNO_QUERY_THROW ); + + xTable->initialize( nRows, nCols ); + uno::Reference< text::XText > xText = xTextRange->getText(); + uno::Reference< text::XTextContent > xContext( xTable, uno::UNO_QUERY_THROW ); + + xText->insertTextContent( xTextRange, xContext, true ); + + // move the current cursor to the first table cell + uno::Reference< table::XCellRange > xCellRange( xTable, uno::UNO_QUERY_THROW ); + uno::Reference< text::XText> xFirstCellText( xCellRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW ); + word::getXTextViewCursor( mxDocument )->gotoRange( xFirstCellText->getStart(), false ); + + uno::Reference< word::XTable > xVBATable( new SwVbaTable( mxParent, mxContext, pVbaRange->getDocument(), xTable ) ); + return xVBATable; +} + +uno::Reference< container::XEnumeration > SAL_CALL +SwVbaTables::createEnumeration() +{ + return new TableEnumerationImpl( mxParent, mxContext, mxDocument, m_xIndexAccess ); +} + +// ScVbaCollectionBaseImpl +uno::Any +SwVbaTables::createCollectionObject( const uno::Any& aSource ) +{ + return lcl_createTable( mxParent, mxContext, mxDocument, aSource ); +} + +// XHelperInterface +OUString +SwVbaTables::getServiceImplName() +{ + return "SwVbaTables"; +} + +// XEnumerationAccess +uno::Type SAL_CALL +SwVbaTables::getElementType() +{ + return cppu::UnoType<word::XTable>::get(); +} + +uno::Sequence<OUString> +SwVbaTables::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Tables" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatables.hxx b/sw/source/ui/vba/vbatables.hxx new file mode 100644 index 000000000..ae7de0b00 --- /dev/null +++ b/sw/source/ui/vba/vbatables.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_UI_VBA_VBATABLES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABLES_HXX + +#include <ooo/vba/word/XTables.hpp> +#include <vbahelper/vbacollectionimpl.hxx> + +typedef CollTestImplHelper< ov::word::XTables > SwVbaTables_BASE; + +class SwVbaTables : public SwVbaTables_BASE +{ + css::uno::Reference< css::frame::XModel > mxDocument; +public: + SwVbaTables( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xDocument ); + // XTables + virtual css::uno::Reference< ov::word::XTable > SAL_CALL Add( const css::uno::Reference< ::ooo::vba::word::XRange >& Range, const css::uno::Any& NumRows, const css::uno::Any& NumColumns, const css::uno::Any& DefaultTableBehavior, const css::uno::Any& AutoFitBehavior ) override; + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + // ScVbaCollectionBaseImpl + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatablesofcontents.cxx b/sw/source/ui/vba/vbatablesofcontents.cxx new file mode 100644 index 000000000..c6cc23e6e --- /dev/null +++ b/sw/source/ui/vba/vbatablesofcontents.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbatablesofcontents.hxx" +#include "vbatableofcontents.hxx" +#include "vbarange.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/XDocumentIndexesSupplier.hpp> +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +namespace { + +class TablesOfContentsEnumWrapper : public EnumerationHelper_BASE +{ + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 nIndex; + +public: + explicit TablesOfContentsEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : mxIndexAccess( xIndexAccess ), nIndex( 0 ) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if( nIndex < mxIndexAccess->getCount() ) + { + return mxIndexAccess->getByIndex( nIndex++ ); + } + throw container::NoSuchElementException(); + } +}; + +class TableOfContentsCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + uno::Reference< text::XTextDocument > mxTextDocument; + std::vector< uno::Reference< text::XDocumentIndex > > maToc; + +public: + /// @throws uno::RuntimeException + TableOfContentsCollectionHelper( const uno::Reference< ov::XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextDocument >& xDoc ): mxParent( xParent ), mxContext( xContext ), mxTextDocument( xDoc ) + { + uno::Reference< text::XDocumentIndexesSupplier > xDocIndexSupp( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xDocIndexes = xDocIndexSupp->getDocumentIndexes(); + sal_Int32 nCount = xDocIndexes->getCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< text::XDocumentIndex > xToc( xDocIndexes->getByIndex(i), uno::UNO_QUERY_THROW ); + if( xToc->getServiceName() == "com.sun.star.text.ContentIndex" ) + { + maToc.push_back( xToc ); + } + } + } + + virtual sal_Int32 SAL_CALL getCount( ) override + { + return maToc.size(); + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< text::XDocumentIndex > xToc( maToc[Index], uno::UNO_SET_THROW ); + return uno::makeAny( uno::Reference< word::XTableOfContents >( new SwVbaTableOfContents( mxParent, mxContext, mxTextDocument, xToc ) ) ); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XTableOfContents>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new TablesOfContentsEnumWrapper( this ); + } +}; + +} + +SwVbaTablesOfContents::SwVbaTablesOfContents( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< text::XTextDocument >& xDoc ) : SwVbaTablesOfContents_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new TableOfContentsCollectionHelper( xParent, xContext, xDoc ) ) ), mxTextDocument( xDoc ) +{ +} + +uno::Reference< word::XTableOfContents > SAL_CALL +SwVbaTablesOfContents::Add( const uno::Reference< word::XRange >& Range, const uno::Any& /*UseHeadingStyles*/, const uno::Any& /*UpperHeadingLevel*/, const uno::Any& LowerHeadingLevel, const uno::Any& UseFields, const uno::Any& /*TableID*/, const uno::Any& /*RightAlignPageNumbers*/, const uno::Any& /*IncludePageNumbers*/, const uno::Any& /*AddedStyles*/, const uno::Any& /*UseHyperlinks*/, const uno::Any& /*HidePageNumbersInWeb*/, const uno::Any& /*UseOutlineLevels*/ ) +{ + uno::Reference< lang::XMultiServiceFactory > xDocMSF( mxTextDocument, uno::UNO_QUERY_THROW ); + uno::Reference< text::XDocumentIndex > xDocumentIndex( xDocMSF->createInstance("com.sun.star.text.ContentIndex"), uno::UNO_QUERY_THROW ); + + uno::Reference< beans::XPropertySet > xTocProps( xDocumentIndex, uno::UNO_QUERY_THROW ); + xTocProps->setPropertyValue("IsProtected", uno::makeAny( false ) ); + + uno::Reference< word::XTableOfContents > xToc( new SwVbaTableOfContents( this, mxContext, mxTextDocument, xDocumentIndex ) ); + + sal_Int32 nLowerHeadingLevel = 9; + if( LowerHeadingLevel.hasValue() ) + LowerHeadingLevel >>= nLowerHeadingLevel; + xToc->setLowerHeadingLevel( nLowerHeadingLevel ); + + bool bUseFields = false; + if( UseFields.hasValue() ) + UseFields >>= bUseFields; + xToc->setUseFields( bUseFields ); + + xToc->setUseOutlineLevels( true ); + + SwVbaRange* pVbaRange = dynamic_cast<SwVbaRange*>( Range.get() ); + if( !pVbaRange ) + throw uno::RuntimeException(); + + uno::Reference< text::XTextRange > xTextRange = pVbaRange->getXTextRange(); + uno::Reference< text::XText > xText = pVbaRange->getXText(); + uno::Reference< text::XTextContent > xTextContent( xDocumentIndex, uno::UNO_QUERY_THROW ); + xText->insertTextContent( xTextRange, xTextContent, false ); + xToc->Update(); + + return xToc; +} + +// XEnumerationAccess +uno::Type +SwVbaTablesOfContents::getElementType() +{ + return cppu::UnoType<word::XTableOfContents>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaTablesOfContents::createEnumeration() +{ + return new TablesOfContentsEnumWrapper( m_xIndexAccess ); +} + +uno::Any +SwVbaTablesOfContents::createCollectionObject( const uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaTablesOfContents::getServiceImplName() +{ + return "SwVbaTablesOfContents"; +} + +uno::Sequence<OUString> +SwVbaTablesOfContents::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.TablesOfContents" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatablesofcontents.hxx b/sw/source/ui/vba/vbatablesofcontents.hxx new file mode 100644 index 000000000..37048ae1b --- /dev/null +++ b/sw/source/ui/vba/vbatablesofcontents.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_UI_VBA_VBATABLESOFCONTENTS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABLESOFCONTENTS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XTablesOfContents.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <ooo/vba/word/XTableOfContents.hpp> +#include <ooo/vba/word/XRange.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XTablesOfContents > SwVbaTablesOfContents_BASE; + +class SwVbaTablesOfContents : public SwVbaTablesOfContents_BASE +{ +private: + css::uno::Reference< css::text::XTextDocument > mxTextDocument; + +public: + /// @throws css::uno::RuntimeException + SwVbaTablesOfContents( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::text::XTextDocument >& xDoc ); + + // Methods + virtual css::uno::Reference< ::ooo::vba::word::XTableOfContents > SAL_CALL Add( const css::uno::Reference< ::ooo::vba::word::XRange >& Range, const css::uno::Any& UseHeadingStyles, const css::uno::Any& UpperHeadingLevel, const css::uno::Any& LowerHeadingLevel, const css::uno::Any& UseFields, const css::uno::Any& TableID, const css::uno::Any& RightAlignPageNumbers, const css::uno::Any& IncludePageNumbers, const css::uno::Any& AddedStyles, const css::uno::Any& UseHyperlinks, const css::uno::Any& HidePageNumbersInWeb, const css::uno::Any& UseOutlineLevels ) override; + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaTablesOfContents_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBATABLESOFCONTENTS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatabstop.cxx b/sw/source/ui/vba/vbatabstop.cxx new file mode 100644 index 000000000..44574bab0 --- /dev/null +++ b/sw/source/ui/vba/vbatabstop.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 "vbatabstop.hxx" +#include <vbahelper/vbahelper.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaTabStop::SwVbaTabStop( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext ) : SwVbaTabStop_BASE( rParent, rContext ) +{ +} + +SwVbaTabStop::~SwVbaTabStop() +{ +} + +OUString +SwVbaTabStop::getServiceImplName() +{ + return "SwVbaTabStop"; +} + +uno::Sequence< OUString > +SwVbaTabStop::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.TabStop" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatabstop.hxx b/sw/source/ui/vba/vbatabstop.hxx new file mode 100644 index 000000000..de956205d --- /dev/null +++ b/sw/source/ui/vba/vbatabstop.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_UI_VBA_VBATABSTOP_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABSTOP_HXX + +#include <ooo/vba/word/XTabStop.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XTabStop > SwVbaTabStop_BASE; + +class SwVbaTabStop : public SwVbaTabStop_BASE +{ +public: + /// @throws css::uno::RuntimeException + SwVbaTabStop( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext ); + virtual ~SwVbaTabStop() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBATABSTOP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatabstops.cxx b/sw/source/ui/vba/vbatabstops.cxx new file mode 100644 index 000000000..0c902a18d --- /dev/null +++ b/sw/source/ui/vba/vbatabstops.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbatabstops.hxx" +#include "vbatabstop.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/style/TabAlign.hpp> +#include <com/sun/star/style/TabStop.hpp> +#include <ooo/vba/word/WdTabLeader.hpp> +#include <ooo/vba/word/WdTabAlignment.hpp> +#include <basic/sberrors.hxx> +#include <cppuhelper/implbase.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +/// @throws uno::RuntimeException +static uno::Sequence< style::TabStop > lcl_getTabStops( const uno::Reference< beans::XPropertySet >& xParaProps ) +{ + uno::Sequence< style::TabStop > aSeq; + xParaProps->getPropertyValue("ParaTabStops") >>= aSeq; + return aSeq; +} + +/// @throws uno::RuntimeException +static void lcl_setTabStops( const uno::Reference< beans::XPropertySet >& xParaProps, const uno::Sequence< style::TabStop >& aSeq ) +{ + xParaProps->setPropertyValue("ParaTabStops", uno::makeAny( aSeq ) ); +} + +namespace { + +class TabStopsEnumWrapper : public EnumerationHelper_BASE +{ + uno::Reference< container::XIndexAccess > mxIndexAccess; + sal_Int32 nIndex; + +public: + explicit TabStopsEnumWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess ) : mxIndexAccess( xIndexAccess ), nIndex( 0 ) + { + } + virtual sal_Bool SAL_CALL hasMoreElements( ) override + { + return ( nIndex < mxIndexAccess->getCount() ); + } + + virtual uno::Any SAL_CALL nextElement( ) override + { + if( nIndex < mxIndexAccess->getCount() ) + { + return mxIndexAccess->getByIndex( nIndex++ ); + } + throw container::NoSuchElementException(); + } +}; + +class TabStopCollectionHelper : public ::cppu::WeakImplHelper< container::XIndexAccess, + container::XEnumerationAccess > +{ +private: + uno::Reference< XHelperInterface > mxParent; + uno::Reference< uno::XComponentContext > mxContext; + sal_Int32 mnTabStops; + +public: + /// @throws css::uno::RuntimeException + TabStopCollectionHelper( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::beans::XPropertySet >& xParaProps ): mxParent( xParent ), mxContext( xContext ), mnTabStops(lcl_getTabStops( xParaProps ).getLength()) + { + } + + virtual sal_Int32 SAL_CALL getCount( ) override + { + return mnTabStops; + } + virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override + { + if ( Index < 0 || Index >= getCount() ) + throw css::lang::IndexOutOfBoundsException(); + + return uno::makeAny( uno::Reference< word::XTabStop >( new SwVbaTabStop( mxParent, mxContext ) ) ); + } + virtual uno::Type SAL_CALL getElementType( ) override + { + return cppu::UnoType<word::XTabStop>::get(); + } + virtual sal_Bool SAL_CALL hasElements( ) override + { + return true; + } + // XEnumerationAccess + virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override + { + return new TabStopsEnumWrapper( this ); + } +}; + +} + +SwVbaTabStops::SwVbaTabStops( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< beans::XPropertySet >& xParaProps ) : SwVbaTabStops_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( new TabStopCollectionHelper( xParent, xContext, xParaProps ) ) ), mxParaProps( xParaProps ) +{ +} + +uno::Reference< word::XTabStop > SAL_CALL SwVbaTabStops::Add( float Position, const uno::Any& Alignment, const uno::Any& Leader ) +{ + sal_Int32 nPosition = Millimeter::getInHundredthsOfOneMillimeter( Position ); + + style::TabAlign nAlign = style::TabAlign_LEFT; + if( Alignment.hasValue() ) + { + sal_Int32 wdAlign = word::WdTabAlignment::wdAlignTabLeft; + Alignment >>= wdAlign; + switch( wdAlign ) + { + case word::WdTabAlignment::wdAlignTabLeft: + { + nAlign = style::TabAlign_LEFT; + break; + } + case word::WdTabAlignment::wdAlignTabRight: + { + nAlign = style::TabAlign_RIGHT; + break; + } + case word::WdTabAlignment::wdAlignTabCenter: + { + nAlign = style::TabAlign_CENTER; + break; + } + case word::WdTabAlignment::wdAlignTabDecimal: + { + nAlign = style::TabAlign_DECIMAL; + break; + } + case word::WdTabAlignment::wdAlignTabBar: + case word::WdTabAlignment::wdAlignTabList: + { + DebugHelper::basicexception( ERRCODE_BASIC_NOT_IMPLEMENTED, OUString() ); + break; + } + default: + { + //left + } + } + } + + sal_Unicode cLeader = ' '; // default is space + if( Leader.hasValue() ) + { + sal_Int32 wdLeader = word::WdTabLeader::wdTabLeaderSpaces; + Leader >>= wdLeader; + switch( wdLeader ) + { + case word::WdTabLeader::wdTabLeaderSpaces: + { + cLeader = ' '; + break; + } + case word::WdTabLeader::wdTabLeaderMiddleDot: + { + cLeader = 183; // U+00B7 MIDDLE DOT + break; + } + case word::WdTabLeader::wdTabLeaderDots: + { + cLeader = '.'; + break; + } + case word::WdTabLeader::wdTabLeaderDashes: + case word::WdTabLeader::wdTabLeaderHeavy: + case word::WdTabLeader::wdTabLeaderLines: + { + cLeader = '_'; + break; + } + default: + { + //left + } + } + } + + style::TabStop aTab; + aTab.Position = nPosition; + aTab.Alignment = nAlign; + aTab.DecimalChar = '.'; // default value + aTab.FillChar = cLeader; + + uno::Sequence< style::TabStop > aOldTabs = lcl_getTabStops( mxParaProps ); + + style::TabStop* pOldTab = std::find_if(aOldTabs.begin(), aOldTabs.end(), + [nPosition](const style::TabStop& rTab) { return rTab.Position == nPosition; }); + bool bOverWriter = pOldTab != aOldTabs.end(); + if( bOverWriter ) + { + *pOldTab = aTab; + lcl_setTabStops( mxParaProps, aOldTabs ); + } + else + { + sal_Int32 nTabs = aOldTabs.getLength(); + uno::Sequence< style::TabStop > aNewTabs( nTabs + 1 ); + + aNewTabs[0] = aTab; + std::copy(aOldTabs.begin(), aOldTabs.end(), std::next(aNewTabs.begin())); + lcl_setTabStops( mxParaProps, aNewTabs ); + } + + return uno::Reference< word::XTabStop >( new SwVbaTabStop( this, mxContext ) ); +} + +void SAL_CALL SwVbaTabStops::ClearAll() +{ + uno::Sequence< style::TabStop > aSeq; + lcl_setTabStops( mxParaProps, aSeq ); +} + +// XEnumerationAccess +uno::Type +SwVbaTabStops::getElementType() +{ + return cppu::UnoType<word::XTabStop>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaTabStops::createEnumeration() +{ + return new TabStopsEnumWrapper( m_xIndexAccess ); +} + +uno::Any +SwVbaTabStops::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +OUString +SwVbaTabStops::getServiceImplName() +{ + return "SwVbaTabStops"; +} + +css::uno::Sequence<OUString> +SwVbaTabStops::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.TabStops" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatabstops.hxx b/sw/source/ui/vba/vbatabstops.hxx new file mode 100644 index 000000000..66a163d49 --- /dev/null +++ b/sw/source/ui/vba/vbatabstops.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_UI_VBA_VBATABSTOPS_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATABSTOPS_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XTabStops.hpp> +#include <ooo/vba/word/XTabStop.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XTabStops > SwVbaTabStops_BASE; + +class SwVbaTabStops : public SwVbaTabStops_BASE +{ +private: + css::uno::Reference< css::beans::XPropertySet > mxParaProps; + +public: + /// @throws css::uno::RuntimeException + SwVbaTabStops( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::beans::XPropertySet >& xParaProps ); + + // Methods + virtual css::uno::Reference< ::ooo::vba::word::XTabStop > SAL_CALL Add( float Position, const css::uno::Any& Alignment, const css::uno::Any& Leader ) override; + virtual void SAL_CALL ClearAll( ) override; + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaTabStops_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBATABSTOPS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatemplate.cxx b/sw/source/ui/vba/vbatemplate.cxx new file mode 100644 index 000000000..58059da51 --- /dev/null +++ b/sw/source/ui/vba/vbatemplate.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbatemplate.hxx" +#include "vbaautotextentry.hxx" +#include <com/sun/star/text/AutoTextContainer.hpp> +#include <comphelper/processfactory.hxx> +#include <tools/urlobj.hxx> +#include <rtl/character.hxx> +#include <osl/file.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static OUString lcl_CheckGroupName( const OUString& rGroupName ) +{ + OUStringBuffer sRet; + //group name should contain only A-Z and a-z and spaces + for( sal_Int32 i = 0; i < rGroupName.getLength(); i++ ) + { + sal_Unicode cChar = rGroupName[i]; + if (rtl::isAsciiAlphanumeric(cChar) || + cChar == '_' || cChar == 0x20) + { + sRet.append(cChar); + } + } + sRet.strip(' '); + return sRet.makeStringAndClear(); +} + +SwVbaTemplate::SwVbaTemplate( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const OUString& rFullUrl ) + : SwVbaTemplate_BASE( rParent, rContext ), msFullUrl( rFullUrl ) +{ +} + +SwVbaTemplate::~SwVbaTemplate() +{ +} + +OUString +SwVbaTemplate::getName() +{ + OUString sName; + if( !msFullUrl.isEmpty() ) + { + INetURLObject aURL( msFullUrl ); + ::osl::File::getSystemPathFromFileURL( aURL.GetLastName(), sName ); + } + return sName; +} + +OUString +SwVbaTemplate::getPath() +{ + OUString sPath; + if( !msFullUrl.isEmpty() ) + { + INetURLObject aURL( msFullUrl ); + OUString sURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ) ); + sURL = sURL.copy( 0, sURL.getLength() - aURL.GetLastName().getLength() - 1 ); + ::osl::File::getSystemPathFromFileURL( sURL, sPath ); + } + return sPath; +} + +uno::Any SAL_CALL +SwVbaTemplate::AutoTextEntries( const uno::Any& index ) +{ + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + uno::Reference< text::XAutoTextContainer2 > xAutoTextContainer = text::AutoTextContainer::create( xContext ); + + // the default template is "Normal.dot" in Word. + OUString sGroup("Normal"); + OUString sName = getName(); + sal_Int32 nIndex = sName.lastIndexOf( '.' ); + if( nIndex > 0 ) + { + sGroup = sName.copy( 0, sName.lastIndexOf( '.' ) ); + } + OUString sNewGroup = lcl_CheckGroupName( sGroup ); + + uno::Reference< container::XIndexAccess > xGroup; + if( !xAutoTextContainer->hasByName( sNewGroup ) ) + { + throw uno::RuntimeException("Auto Text Entry doesn't exist" ); + } + + xGroup.set( xAutoTextContainer->getByName( sNewGroup ), uno::UNO_QUERY_THROW ); + + uno::Reference< XCollection > xCol( new SwVbaAutoTextEntries( this, mxContext, xGroup ) ); + if( index.hasValue() ) + return xCol->Item( index, uno::Any() ); + return uno::makeAny( xCol ); +} + +OUString +SwVbaTemplate::getServiceImplName() +{ + return "SwVbaTemplate"; +} + +uno::Sequence< OUString > +SwVbaTemplate::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Template" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbatemplate.hxx b/sw/source/ui/vba/vbatemplate.hxx new file mode 100644 index 000000000..5765c06a8 --- /dev/null +++ b/sw/source/ui/vba/vbatemplate.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_UI_VBA_VBATEMPLATE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBATEMPLATE_HXX + +#include <ooo/vba/word/XTemplate.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XTemplate > SwVbaTemplate_BASE; + +class SwVbaTemplate : public SwVbaTemplate_BASE +{ +private: + OUString msFullUrl; +public: + SwVbaTemplate( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, + const OUString& ); + virtual ~SwVbaTemplate() override; + + // XTemplate + virtual OUString SAL_CALL getName() override; + virtual OUString SAL_CALL getPath() override; + virtual css::uno::Any SAL_CALL AutoTextEntries( const css::uno::Any& index ) override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBATEMPLATE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbavariable.cxx b/sw/source/ui/vba/vbavariable.cxx new file mode 100644 index 000000000..c2dd26dbd --- /dev/null +++ b/sw/source/ui/vba/vbavariable.cxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbavariable.hxx" +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +SwVbaVariable::SwVbaVariable( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, + const uno::Reference< beans::XPropertyAccess >& rUserDefined, const OUString& rVariableName ) : + SwVbaVariable_BASE( rParent, rContext ), mxUserDefined( rUserDefined ), maVariableName( rVariableName ) +{ +} + +SwVbaVariable::~SwVbaVariable() +{ +} + +OUString SAL_CALL +SwVbaVariable::getName() +{ + return maVariableName; +} + +void SAL_CALL +SwVbaVariable::setName( const OUString& ) +{ + throw uno::RuntimeException(" Fail to set name" ); +} + +uno::Any SAL_CALL +SwVbaVariable::getValue() +{ + uno::Reference< beans::XPropertySet > xProp( mxUserDefined, uno::UNO_QUERY_THROW ); + return xProp->getPropertyValue( maVariableName ); +} + +void SAL_CALL +SwVbaVariable::setValue( const uno::Any& rValue ) +{ + // FIXME: fail to set the value if the new type of value is different from the original one. + uno::Reference< beans::XPropertySet > xProp( mxUserDefined, uno::UNO_QUERY_THROW ); + xProp->setPropertyValue( maVariableName, rValue ); +} + +sal_Int32 SAL_CALL +SwVbaVariable::getIndex() +{ + const uno::Sequence< beans::PropertyValue > props = mxUserDefined->getPropertyValues(); + auto pProp = std::find_if(props.begin(), props.end(), + [this](const beans::PropertyValue& rProp) { return rProp.Name == maVariableName; }); + if (pProp != props.end()) + return static_cast<sal_Int32>(std::distance(props.begin(), pProp)) + 1; + + return 0; +} + +OUString +SwVbaVariable::getServiceImplName() +{ + return "SwVbaVariable"; +} + +uno::Sequence< OUString > +SwVbaVariable::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Variable" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbavariable.hxx b/sw/source/ui/vba/vbavariable.hxx new file mode 100644 index 000000000..a278ed16d --- /dev/null +++ b/sw/source/ui/vba/vbavariable.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_UI_VBA_VBAVARIABLE_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAVARIABLE_HXX + +#include <ooo/vba/word/XVariable.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/beans/XPropertyAccess.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XVariable > SwVbaVariable_BASE; + +class SwVbaVariable : public SwVbaVariable_BASE +{ +private: + css::uno::Reference< css::beans::XPropertyAccess > mxUserDefined; + OUString maVariableName; + +public: + /// @throws css::uno::RuntimeException + SwVbaVariable( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::beans::XPropertyAccess >& rUserDefined, const OUString& rName ); + virtual ~SwVbaVariable() override; + + // XVariable + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName( const OUString& ) override; + virtual css::uno::Any SAL_CALL getValue() override; + virtual void SAL_CALL setValue( const css::uno::Any& rValue ) override; + virtual sal_Int32 SAL_CALL getIndex() override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAVARIABLE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbavariables.cxx b/sw/source/ui/vba/vbavariables.cxx new file mode 100644 index 000000000..9e786e905 --- /dev/null +++ b/sw/source/ui/vba/vbavariables.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 "vbavariables.hxx" +#include "vbavariable.hxx" +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +/// @throws uno::RuntimeException +static uno::Reference< container::XIndexAccess > createVariablesAccess( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< beans::XPropertyAccess >& xUserDefined ) +{ + // FIXME: the performance is poor? + XNamedObjectCollectionHelper< word::XVariable >::XNamedVec aVariables; + const uno::Sequence< beans::PropertyValue > props = xUserDefined->getPropertyValues(); + sal_Int32 nCount = props.getLength(); + aVariables.reserve( nCount ); + std::transform(props.begin(), props.end(), std::back_inserter(aVariables), + [&xParent, &xContext, &xUserDefined](const beans::PropertyValue& rProp) -> uno::Reference< word::XVariable > { + return uno::Reference< word::XVariable > ( new SwVbaVariable( xParent, xContext, xUserDefined, rProp.Name ) ); }); + + uno::Reference< container::XIndexAccess > xVariables( new XNamedObjectCollectionHelper< word::XVariable >( aVariables ) ); + return xVariables; +} + +SwVbaVariables::SwVbaVariables( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< beans::XPropertyAccess >& rUserDefined ): SwVbaVariables_BASE( xParent, xContext, createVariablesAccess( xParent, xContext, rUserDefined ) ), mxUserDefined( rUserDefined ) +{ +} +// XEnumerationAccess +uno::Type +SwVbaVariables::getElementType() +{ + return cppu::UnoType<word::XVariable>::get(); +} +uno::Reference< container::XEnumeration > +SwVbaVariables::createEnumeration() +{ + uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); + return xEnumerationAccess->createEnumeration(); +} + +uno::Any +SwVbaVariables::createCollectionObject( const css::uno::Any& aSource ) +{ + return aSource; +} + +uno::Any SAL_CALL +SwVbaVariables::Add( const OUString& rName, const uno::Any& rValue ) +{ + uno::Any aValue; + if( rValue.hasValue() ) + aValue = rValue; + else + aValue <<= OUString(); + uno::Reference< beans::XPropertyContainer > xPropertyContainer( mxUserDefined, uno::UNO_QUERY_THROW ); + xPropertyContainer->addProperty( rName, beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::REMOVABLE, aValue ); + + return uno::makeAny( uno::Reference< word::XVariable >( new SwVbaVariable( getParent(), mxContext, mxUserDefined, rName ) ) ); +} + +OUString +SwVbaVariables::getServiceImplName() +{ + return "SwVbaVariables"; +} + +css::uno::Sequence<OUString> +SwVbaVariables::getServiceNames() +{ + static uno::Sequence< OUString > const sNames + { + "ooo.vba.word.Variables" + }; + return sNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbavariables.hxx b/sw/source/ui/vba/vbavariables.hxx new file mode 100644 index 000000000..639c54a33 --- /dev/null +++ b/sw/source/ui/vba/vbavariables.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_UI_VBA_VBAVARIABLES_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAVARIABLES_HXX + +#include <vbahelper/vbacollectionimpl.hxx> +#include <ooo/vba/word/XVariables.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> + +typedef CollTestImplHelper< ooo::vba::word::XVariables > SwVbaVariables_BASE; + +class SwVbaVariables : public SwVbaVariables_BASE +{ +private: + css::uno::Reference< css::beans::XPropertyAccess > mxUserDefined; + +public: + SwVbaVariables( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::beans::XPropertyAccess >& rUserDefined ); + + // XEnumerationAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override; + + // SwVbaVariables_BASE + virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override; + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; + + // XVariables + virtual css::uno::Any SAL_CALL Add( const OUString& rName, const css::uno::Any& rValue ) override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAVARIABLES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaview.cxx b/sw/source/ui/vba/vbaview.cxx new file mode 100644 index 000000000..e7012f4b1 --- /dev/null +++ b/sw/source/ui/vba/vbaview.cxx @@ -0,0 +1,386 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include "vbaview.hxx" +#include <vbahelper/vbahelper.hxx> +#include <basic/sberrors.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XFootnotesSupplier.hpp> +#include <com/sun/star/text/XEndnotesSupplier.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <ooo/vba/word/WdSpecialPane.hpp> +#include <ooo/vba/word/WdViewType.hpp> +#include <ooo/vba/word/WdSeekView.hpp> + +#include "wordvbahelper.hxx" +#include "vbaheaderfooterhelper.hxx" +#include <view.hxx> + +using namespace ::ooo::vba; +using namespace ::com::sun::star; + +static const sal_Int32 DEFAULT_BODY_DISTANCE = 500; + +SwVbaView::SwVbaView( const uno::Reference< ooo::vba::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, + const uno::Reference< frame::XModel >& rModel ) : + SwVbaView_BASE( rParent, rContext ), mxModel( rModel ) +{ + uno::Reference< frame::XController > xController = mxModel->getCurrentController(); + + uno::Reference< text::XTextViewCursorSupplier > xTextViewCursorSupp( xController, uno::UNO_QUERY_THROW ); + mxViewCursor = xTextViewCursorSupp->getViewCursor(); + + uno::Reference< view::XViewSettingsSupplier > xViewSettingSupp( xController, uno::UNO_QUERY_THROW ); + mxViewSettings.set( xViewSettingSupp->getViewSettings(), uno::UNO_SET_THROW ); +} + +SwVbaView::~SwVbaView() +{ +} + +::sal_Int32 SAL_CALL +SwVbaView::getSeekView() +{ + // FIXME: if the view cursor is in table, field, section and frame + // handle if the cursor is in table + uno::Reference< text::XText > xCurrentText = mxViewCursor->getText(); + uno::Reference< beans::XPropertySet > xCursorProps( mxViewCursor, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextContent > xTextContent; + while( xCursorProps->getPropertyValue("TextTable") >>= xTextContent ) + { + xCurrentText = xTextContent->getAnchor()->getText(); + xCursorProps.set( xCurrentText->createTextCursor(), uno::UNO_QUERY_THROW ); + } + uno::Reference< lang::XServiceInfo > xServiceInfo( xCurrentText, uno::UNO_QUERY_THROW ); + OUString aImplName = xServiceInfo->getImplementationName(); + if ( aImplName == "SwXBodyText" ) + { + return word::WdSeekView::wdSeekMainDocument; + } + else if ( aImplName == "SwXHeadFootText" ) + { + if( HeaderFooterHelper::isHeader( mxModel ) ) + { + if( HeaderFooterHelper::isFirstPageHeader( mxModel ) ) + return word::WdSeekView::wdSeekFirstPageHeader; + else if( HeaderFooterHelper::isEvenPagesHeader( mxModel ) ) + return word::WdSeekView::wdSeekEvenPagesHeader; + else + return word::WdSeekView::wdSeekPrimaryHeader; + } + else + { + if( HeaderFooterHelper::isFirstPageFooter( mxModel ) ) + return word::WdSeekView::wdSeekFirstPageFooter; + else if( HeaderFooterHelper::isEvenPagesFooter( mxModel ) ) + return word::WdSeekView::wdSeekEvenPagesFooter; + else + return word::WdSeekView::wdSeekPrimaryFooter; + } + } + else if ( aImplName == "SwXFootnote" ) + { + if( xServiceInfo->supportsService("com.sun.star.text.Endnote") ) + return word::WdSeekView::wdSeekEndnotes; + else + return word::WdSeekView::wdSeekFootnotes; + } + + return word::WdSeekView::wdSeekMainDocument; +} + +void SAL_CALL +SwVbaView::setSeekView( ::sal_Int32 _seekview ) +{ + // FIXME: save the current cursor position, if the cursor is in the main + // document, so we can jump back to this position, if the macro sets + // the ViewMode back to wdSeekMainDocument + + word::gotoSelectedObjectAnchor( mxModel ); + switch( _seekview ) + { + case word::WdSeekView::wdSeekFirstPageFooter: + case word::WdSeekView::wdSeekFirstPageHeader: + case word::WdSeekView::wdSeekCurrentPageFooter: + case word::WdSeekView::wdSeekCurrentPageHeader: + case word::WdSeekView::wdSeekPrimaryFooter: + case word::WdSeekView::wdSeekPrimaryHeader: + case word::WdSeekView::wdSeekEvenPagesFooter: + case word::WdSeekView::wdSeekEvenPagesHeader: + { + // need to test + mxViewCursor->gotoRange( getHFTextRange( _seekview ), false ); + break; + } + case word::WdSeekView::wdSeekFootnotes: + { + uno::Reference< text::XFootnotesSupplier > xFootnotesSupp( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xFootnotes( xFootnotesSupp->getFootnotes(), uno::UNO_SET_THROW ); + if( xFootnotes->getCount() > 0 ) + { + uno::Reference< text::XText > xText( xFootnotes->getByIndex(0), uno::UNO_QUERY_THROW ); + mxViewCursor->gotoRange( xText->getStart(), false ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_NO_ACTIVE_OBJECT ); + } + break; + } + case word::WdSeekView::wdSeekEndnotes: + { + uno::Reference< text::XEndnotesSupplier > xEndnotesSupp( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xEndnotes( xEndnotesSupp->getEndnotes(), uno::UNO_SET_THROW ); + if( xEndnotes->getCount() > 0 ) + { + uno::Reference< text::XText > xText( xEndnotes->getByIndex(0), uno::UNO_QUERY_THROW ); + mxViewCursor->gotoRange( xText->getStart(), false ); + } + else + { + DebugHelper::runtimeexception( ERRCODE_BASIC_NO_ACTIVE_OBJECT ); + } + break; + } + case word::WdSeekView::wdSeekMainDocument: + { + uno::Reference< text::XTextDocument > xTextDocument( mxModel, uno::UNO_QUERY_THROW ); + uno::Reference< text::XText > xText = xTextDocument->getText(); + mxViewCursor->gotoRange( word::getFirstObjectPosition( xText ), false ); + break; + } + } +} + +::sal_Int32 SAL_CALL +SwVbaView::getSplitSpecial() +{ + return word::WdSpecialPane::wdPaneNone; +} + +void SAL_CALL +SwVbaView::setSplitSpecial( ::sal_Int32/* _splitspecial */) +{ + // not support in Writer +} + +sal_Bool SAL_CALL +SwVbaView::getTableGridLines() +{ + bool bShowTableGridLine = false; + mxViewSettings->getPropertyValue("ShowTableBoundaries") >>= bShowTableGridLine; + return bShowTableGridLine; +} + +void SAL_CALL +SwVbaView::setTableGridLines( sal_Bool _tablegridlines ) +{ + mxViewSettings->setPropertyValue("ShowTableBoundaries", uno::makeAny( _tablegridlines ) ); +} + +::sal_Int32 SAL_CALL +SwVbaView::getType() +{ + // FIXME: handle wdPrintPreview type + bool bOnlineLayout = false; + mxViewSettings->getPropertyValue("ShowOnlineLayout") >>= bOnlineLayout; + return bOnlineLayout ? word::WdViewType::wdWebView : word::WdViewType::wdPrintView; +} + +void SAL_CALL +SwVbaView::setType( ::sal_Int32 _type ) +{ + // FIXME: handle wdPrintPreview type + switch( _type ) + { + case word::WdViewType::wdPrintView: + case word::WdViewType::wdNormalView: + { + mxViewSettings->setPropertyValue("ShowOnlineLayout", uno::makeAny( false ) ); + break; + } + case word::WdViewType::wdWebView: + { + mxViewSettings->setPropertyValue("ShowOnlineLayout", uno::makeAny( true ) ); + break; + } + case word::WdViewType::wdPrintPreview: + { + PrintPreviewHelper( uno::Any(),word::getView( mxModel ) ); + break; + } + default: + DebugHelper::runtimeexception( ERRCODE_BASIC_NOT_IMPLEMENTED ); + + } +} + +uno::Reference< text::XTextRange > SwVbaView::getHFTextRange( sal_Int32 nType ) +{ + mxModel->lockControllers(); + + OUString aPropIsOn; + OUString aPropIsShared; + OUString aPropBodyDistance; + OUString aPropText; + + switch( nType ) + { + case word::WdSeekView::wdSeekCurrentPageFooter: + case word::WdSeekView::wdSeekFirstPageFooter: + case word::WdSeekView::wdSeekPrimaryFooter: + case word::WdSeekView::wdSeekEvenPagesFooter: + { + aPropIsOn = "FooterIsOn"; + aPropIsShared = "FooterIsShared"; + aPropBodyDistance = "FooterBodyDistance"; + aPropText = "FooterText"; + break; + } + case word::WdSeekView::wdSeekCurrentPageHeader: + case word::WdSeekView::wdSeekFirstPageHeader: + case word::WdSeekView::wdSeekPrimaryHeader: + case word::WdSeekView::wdSeekEvenPagesHeader: + { + aPropIsOn = "HeaderIsOn"; + aPropIsShared = "HeaderIsShared"; + aPropBodyDistance = "HeaderBodyDistance"; + aPropText = "HeaderText"; + break; + } + } + + uno::Reference< text::XPageCursor > xPageCursor( mxViewCursor, uno::UNO_QUERY_THROW ); + + if( nType == word::WdSeekView::wdSeekFirstPageFooter + || nType == word::WdSeekView::wdSeekFirstPageHeader ) + { + xPageCursor->jumpToFirstPage(); + } + + uno::Reference< style::XStyle > xStyle; + uno::Reference< text::XText > xText; + switch( nType ) + { + case word::WdSeekView::wdSeekPrimaryFooter: + case word::WdSeekView::wdSeekPrimaryHeader: + case word::WdSeekView::wdSeekEvenPagesFooter: + case word::WdSeekView::wdSeekEvenPagesHeader: + { + // The primary header is the first header of the section. + // If the header is not shared between odd and even pages + // the odd page's header is the primary header. If the + // first page's header is different from the rest of the + // document, it is NOT the primary header ( the next primary + // header would be on page 3 ) + // The even pages' header is only available if the header is + // not shared and the current style is applied to a page with + // an even page number + uno::Reference< beans::XPropertySet > xCursorProps( mxViewCursor, uno::UNO_QUERY_THROW ); + OUString aPageStyleName; + xCursorProps->getPropertyValue("PageStyleName") >>= aPageStyleName; + if ( aPageStyleName == "First Page" ) + { + // go to the beginning of where the next style is used + bool hasNextPage = false; + xStyle = word::getCurrentPageStyle( mxModel ); + do + { + hasNextPage = xPageCursor->jumpToNextPage(); + } + while( hasNextPage && ( xStyle == word::getCurrentPageStyle( mxModel ) ) ); + + if( !hasNextPage ) + DebugHelper::basicexception( ERRCODE_BASIC_BAD_ACTION, OUString() ); + } + break; + } + default: + { + break; + } + } + + xStyle = word::getCurrentPageStyle( mxModel ); + uno::Reference< beans::XPropertySet > xPageProps( xStyle, uno::UNO_QUERY_THROW ); + bool isOn = false; + xPageProps->getPropertyValue( aPropIsOn ) >>= isOn; + bool isShared = false; + xPageProps->getPropertyValue( aPropIsShared ) >>= isShared; + if( !isOn ) + { + xPageProps->setPropertyValue( aPropIsOn, uno::makeAny( true ) ); + xPageProps->setPropertyValue( aPropBodyDistance, uno::makeAny( DEFAULT_BODY_DISTANCE ) ); + } + if( !isShared ) + { + OUString aTempPropText = aPropText; + if( nType == word::WdSeekView::wdSeekEvenPagesFooter + || nType == word::WdSeekView::wdSeekEvenPagesHeader ) + { + aTempPropText += "Left"; + } + else + { + aTempPropText += "Right"; + } + xText.set( xPageProps->getPropertyValue( aTempPropText), uno::UNO_QUERY_THROW ); + } + else + { + if( nType == word::WdSeekView::wdSeekEvenPagesFooter + || nType == word::WdSeekView::wdSeekEvenPagesHeader ) + { + DebugHelper::basicexception( ERRCODE_BASIC_BAD_ACTION, OUString() ); + } + xText.set( xPageProps->getPropertyValue( aPropText ), uno::UNO_QUERY_THROW ); + } + + mxModel->unlockControllers(); + if( !xText.is() ) + { + DebugHelper::basicexception( ERRCODE_BASIC_INTERNAL_ERROR, OUString() ); + } + uno::Reference< text::XTextRange > xTextRange = word::getFirstObjectPosition( xText ); + return xTextRange; +} + +OUString +SwVbaView::getServiceImplName() +{ + return "SwVbaView"; +} + +uno::Sequence< OUString > +SwVbaView::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.View" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbaview.hxx b/sw/source/ui/vba/vbaview.hxx new file mode 100644 index 000000000..9921fb3c3 --- /dev/null +++ b/sw/source/ui/vba/vbaview.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAVIEW_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAVIEW_HXX + +#include <ooo/vba/word/XView.hpp> +#include <vbahelper/vbahelperinterface.hxx> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XView > SwVbaView_BASE; + +class SwVbaView : public SwVbaView_BASE +{ +private: + css::uno::Reference< css::frame::XModel > mxModel; + css::uno::Reference< css::text::XTextViewCursor > mxViewCursor; + css::uno::Reference< css::beans::XPropertySet > mxViewSettings; + + /// @throws css::uno::RuntimeException + /// @throws css::script::BasicErrorException + css::uno::Reference< css::text::XTextRange > getHFTextRange( sal_Int32 nType ); + +public: + /// @throws css::uno::RuntimeException + SwVbaView( const css::uno::Reference< ooo::vba::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, + const css::uno::Reference< css::frame::XModel >& rModel ); + virtual ~SwVbaView() override; + + // XView + virtual ::sal_Int32 SAL_CALL getSeekView() override; + virtual void SAL_CALL setSeekView( ::sal_Int32 _seekview ) override; + virtual ::sal_Int32 SAL_CALL getSplitSpecial() override; + virtual void SAL_CALL setSplitSpecial( ::sal_Int32 _splitspecial ) override; + virtual sal_Bool SAL_CALL getTableGridLines() override; + virtual void SAL_CALL setTableGridLines( sal_Bool _tablegridlines ) override; + virtual ::sal_Int32 SAL_CALL getType() override; + virtual void SAL_CALL setType( ::sal_Int32 _type ) override; + + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbawindow.cxx b/sw/source/ui/vba/vbawindow.cxx new file mode 100644 index 000000000..f4014ffba --- /dev/null +++ b/sw/source/ui/vba/vbawindow.cxx @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ooo/vba/word/WdWindowState.hpp> +#include <sfx2/viewfrm.hxx> +#include <vcl/wrkwin.hxx> + +#include "vbawindow.hxx" +#include "vbadocument.hxx" +#include "vbaview.hxx" +#include "vbapanes.hxx" +#include "vbapane.hxx" +#include "wordvbahelper.hxx" +#include <view.hxx> + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +SwVbaWindow::SwVbaWindow( + const uno::Reference< XHelperInterface >& xParent, + const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< frame::XModel >& xModel, + const uno::Reference< frame::XController >& xController ) : + WindowImpl_BASE( xParent, xContext, xModel, xController ) +{ +} + +void +SwVbaWindow::Activate() +{ + rtl::Reference<SwVbaDocument> document( new SwVbaDocument(uno::Reference< XHelperInterface >( Application(), uno::UNO_QUERY_THROW ), mxContext, m_xModel) ); + + document->Activate(); +} + +void +SwVbaWindow::Close( const uno::Any& SaveChanges, const uno::Any& RouteDocument ) +{ + // FIXME: it is incorrect when there are more than 1 windows + rtl::Reference<SwVbaDocument> document( new SwVbaDocument(uno::Reference< XHelperInterface >( Application(), uno::UNO_QUERY_THROW ), mxContext, m_xModel) ); + uno::Any FileName; + document->Close(SaveChanges, FileName, RouteDocument ); +} + +uno::Any SAL_CALL +SwVbaWindow::getView() +{ + return uno::makeAny( uno::Reference< word::XView >( new SwVbaView( this, mxContext, m_xModel ) ) ); +} + +void SAL_CALL SwVbaWindow::setView( const uno::Any& _view ) +{ + sal_Int32 nType = 0; + if( _view >>= nType ) + { + rtl::Reference<SwVbaView> view( new SwVbaView(this, mxContext, m_xModel) ); + view->setType( nType ); + } +} + +uno::Any SAL_CALL +SwVbaWindow::getWindowState() +{ + sal_Int32 nwindowState = word::WdWindowState::wdWindowStateNormal; + SwView* pView = word::getView( m_xModel ); + SfxViewFrame* pViewFrame = pView -> GetViewFrame(); + WorkWindow* pWork = static_cast<WorkWindow*>( pViewFrame->GetFrame().GetSystemWindow() ); + if ( pWork ) + { + if ( pWork -> IsMaximized()) + nwindowState = word::WdWindowState::wdWindowStateMaximize; + else if (pWork -> IsMinimized()) + nwindowState = word::WdWindowState::wdWindowStateMinimize; + } + return uno::makeAny( nwindowState ); +} + +void SAL_CALL +SwVbaWindow::setWindowState( const uno::Any& _windowstate ) +{ + sal_Int32 nwindowState = word::WdWindowState::wdWindowStateMaximize; + _windowstate >>= nwindowState; + SwView* pView = word::getView( m_xModel ); + SfxViewFrame* pViewFrame = pView -> GetViewFrame(); + WorkWindow* pWork = static_cast<WorkWindow*>( pViewFrame->GetFrame().GetSystemWindow() ); + if ( pWork ) + { + if ( nwindowState == word::WdWindowState::wdWindowStateMaximize ) + pWork -> Maximize(); + else if (nwindowState == word::WdWindowState::wdWindowStateMinimize) + pWork -> Minimize(); + else if (nwindowState == word::WdWindowState::wdWindowStateNormal) + pWork -> Restore(); + else + SAL_WARN("sw.vba", "Unhandled window state " << nwindowState); + } +} + +OUString SAL_CALL +SwVbaWindow::getCaption() +{ + SwView* pView = word::getView( m_xModel ); + if( !pView ) + return ""; + + uno::Reference< css::beans::XPropertySet > xFrameProps( pView->GetViewFrame()->GetFrame().GetFrameInterface()->getController()->getFrame(), uno::UNO_QUERY ); + if( !xFrameProps.is() ) + return ""; + + OUString sTitle; + xFrameProps->getPropertyValue( "Title" ) >>= sTitle; + + return sTitle; +} + +void SAL_CALL +SwVbaWindow::setCaption( const OUString& _caption ) +{ + SwView* pView = word::getView( m_xModel ); + if( !pView ) + return; + + uno::Reference< css::beans::XPropertySet > xFrameProps( pView->GetViewFrame()->GetFrame().GetFrameInterface()->getController()->getFrame(), uno::UNO_QUERY ); + if( !xFrameProps.is() ) + return; + + xFrameProps->setPropertyValue( "Title", uno::makeAny( _caption ) ); +} + +uno::Any SAL_CALL +SwVbaWindow::Panes( const uno::Any& aIndex ) +{ + uno::Reference< XCollection > xPanes( new SwVbaPanes( this, mxContext, m_xModel ) ); + if( aIndex.getValueTypeClass() == uno::TypeClass_VOID ) + return uno::makeAny( xPanes ); + + return xPanes->Item( aIndex, uno::Any() ); +} + +uno::Any SAL_CALL +SwVbaWindow::ActivePane() +{ + return uno::makeAny( uno::Reference< word::XPane >( new SwVbaPane( this, mxContext, m_xModel ) ) ); +} + +OUString +SwVbaWindow::getServiceImplName() +{ + return "SwVbaWindow"; +} + +uno::Sequence< OUString > +SwVbaWindow::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.Window" + }; + return aServiceNames; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbawindow.hxx b/sw/source/ui/vba/vbawindow.hxx new file mode 100644 index 000000000..ee435b150 --- /dev/null +++ b/sw/source/ui/vba/vbawindow.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_UI_VBA_VBAWINDOW_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAWINDOW_HXX +#include <cppuhelper/implbase.hxx> +#include <ooo/vba/word/XWindow.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <vbahelper/vbahelperinterface.hxx> +#include <vbahelper/vbawindowbase.hxx> + +typedef cppu::ImplInheritanceHelper< VbaWindowBase, ov::word::XWindow > WindowImpl_BASE; + +class SwVbaWindow : public WindowImpl_BASE +{ +public: + /// @throws css::uno::RuntimeException + SwVbaWindow( + const css::uno::Reference< ov::XHelperInterface >& xParent, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::frame::XController >& xController ); + + // Attributes + virtual css::uno::Any SAL_CALL getView() override; + virtual void SAL_CALL setView( const css::uno::Any& _view ) override; + virtual css::uno::Any SAL_CALL getWindowState() override; + virtual void SAL_CALL setWindowState( const css::uno::Any& _windowstate ) override; + virtual OUString SAL_CALL getCaption() override; + virtual void SAL_CALL setCaption( const OUString& _caption ) override; + // Methods + virtual void SAL_CALL Activate( ) override; + virtual void SAL_CALL Close( const css::uno::Any& SaveChanges, const css::uno::Any& RouteDocument ) override; + virtual css::uno::Any SAL_CALL Panes( const css::uno::Any& aIndex ) override; + virtual css::uno::Any SAL_CALL ActivePane() override; + // XHelperInterface + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbawrapformat.cxx b/sw/source/ui/vba/vbawrapformat.cxx new file mode 100644 index 000000000..b854027fe --- /dev/null +++ b/sw/source/ui/vba/vbawrapformat.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include "service.hxx" +#include "vbawrapformat.hxx" +#include <ooo/vba/word/WdWrapSideType.hpp> +#include <ooo/vba/word/WdWrapType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <basic/sberrors.hxx> +#include <vbahelper/vbahelper.hxx> +#include <vbahelper/helperdecl.hxx> + +using namespace ooo::vba; +using namespace com::sun::star; + +SwVbaWrapFormat::SwVbaWrapFormat( uno::Sequence< uno::Any > const& aArgs, uno::Reference< uno::XComponentContext >const& xContext ) : SwVbaWrapFormat_BASE( getXSomethingFromArgs< XHelperInterface >( aArgs, 0 ), xContext ), m_xShape( getXSomethingFromArgs< drawing::XShape >( aArgs, 1, false ) ), mnWrapFormatType( 0 ), mnSide( word::WdWrapSideType::wdWrapBoth ) +{ + m_xPropertySet.set( m_xShape, uno::UNO_QUERY_THROW ); +} + +void SwVbaWrapFormat::makeWrap() +{ + text::WrapTextMode eTextMode = text::WrapTextMode_NONE; + if( mnSide == word::WdWrapSideType::wdWrapLeft ) + { + eTextMode = text::WrapTextMode_LEFT; + } + else if( mnSide == word::WdWrapSideType::wdWrapRight ) + { + eTextMode = text::WrapTextMode_RIGHT; + } + else if( mnSide == word::WdWrapSideType::wdWrapBoth || + mnSide == word::WdWrapSideType::wdWrapLargest ) + { + switch( mnWrapFormatType ) + { + case word::WdWrapType::wdWrapNone: + case word::WdWrapType::wdWrapThrough: + { + eTextMode = text::WrapTextMode_THROUGH; + break; + } + case word::WdWrapType::wdWrapInline: + case word::WdWrapType::wdWrapTopBottom: + { + eTextMode = text::WrapTextMode_NONE; + break; + } + case word::WdWrapType::wdWrapSquare: + { + eTextMode = text::WrapTextMode_PARALLEL; + m_xPropertySet->setPropertyValue("SurroundContour", uno::makeAny( false ) ); + break; + } + case word::WdWrapType::wdWrapTight: + { + eTextMode = text::WrapTextMode_PARALLEL; + m_xPropertySet->setPropertyValue("SurroundContour", uno::makeAny( true ) ); + break; + } + default: + { + DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_ARGUMENT); + } + } + } + m_xPropertySet->setPropertyValue("TextWrap", uno::makeAny( eTextMode ) ); +} + +::sal_Int32 SAL_CALL SwVbaWrapFormat::getType() +{ + sal_Int32 nType = word::WdWrapType::wdWrapSquare; + text::WrapTextMode eTextMode; + m_xPropertySet->getPropertyValue("TextWrap") >>= eTextMode; + switch( eTextMode ) + { + case text::WrapTextMode_NONE: + { + nType = word::WdWrapType::wdWrapTopBottom; + break; + } + case text::WrapTextMode_THROUGH: + { + nType = word::WdWrapType::wdWrapNone; + break; + } + case text::WrapTextMode_PARALLEL: + { + bool bContour = false; + m_xPropertySet->getPropertyValue("SurroundContour") >>= bContour; + if( bContour ) + nType = word::WdWrapType::wdWrapTight; + else + nType = word::WdWrapType::wdWrapSquare; + break; + } + case text::WrapTextMode_DYNAMIC: + case text::WrapTextMode_LEFT: + case text::WrapTextMode_RIGHT: + { + nType = word::WdWrapType::wdWrapThrough; + break; + } + default: + { + nType = word::WdWrapType::wdWrapSquare; + } + } + return nType; +} + +void SAL_CALL SwVbaWrapFormat::setType( ::sal_Int32 _type ) +{ + mnWrapFormatType = _type; + makeWrap(); +} + +::sal_Int32 SAL_CALL SwVbaWrapFormat::getSide() +{ + sal_Int32 nSide = word::WdWrapSideType::wdWrapBoth; + text::WrapTextMode eTextMode; + m_xPropertySet->getPropertyValue("TextWrap") >>= eTextMode; + switch( eTextMode ) + { + case text::WrapTextMode_LEFT: + { + nSide = word::WdWrapSideType::wdWrapLeft; + break; + } + case text::WrapTextMode_RIGHT: + { + nSide = word::WdWrapSideType::wdWrapRight; + break; + } + default: + { + nSide = word::WdWrapSideType::wdWrapBoth; + } + } + return nSide; +} + +void SAL_CALL SwVbaWrapFormat::setSide( ::sal_Int32 _side ) +{ + mnSide = _side; + makeWrap(); +} + +float SwVbaWrapFormat::getDistance( const OUString& sName ) +{ + sal_Int32 nDistance = 0; + m_xPropertySet->getPropertyValue( sName ) >>= nDistance; + return static_cast< float >( Millimeter::getInPoints( nDistance ) ); +} + +void SwVbaWrapFormat::setDistance( const OUString& sName, float _distance ) +{ + sal_Int32 nDistance = Millimeter::getInHundredthsOfOneMillimeter( _distance ); + m_xPropertySet->setPropertyValue( sName, uno::makeAny( nDistance ) ); +} + +float SAL_CALL SwVbaWrapFormat::getDistanceTop() +{ + return getDistance( "TopMargin" ); +} + +void SAL_CALL SwVbaWrapFormat::setDistanceTop( float _distancetop ) +{ + setDistance( "TopMargin", _distancetop ); +} + +float SAL_CALL SwVbaWrapFormat::getDistanceBottom() +{ + return getDistance( "BottomMargin" ); +} + +void SAL_CALL SwVbaWrapFormat::setDistanceBottom( float _distancebottom ) +{ + setDistance( "BottomMargin", _distancebottom ); +} + +float SAL_CALL SwVbaWrapFormat::getDistanceLeft() +{ + return getDistance( "LeftMargin" ); +} + +void SAL_CALL SwVbaWrapFormat::setDistanceLeft( float _distanceleft ) +{ + setDistance( "LeftMargin", _distanceleft ); +} + +float SAL_CALL SwVbaWrapFormat::getDistanceRight() +{ + return getDistance( "RightMargin" ); +} + +void SAL_CALL SwVbaWrapFormat::setDistanceRight( float _distanceright ) +{ + setDistance( "RightMargin", _distanceright ); +} + +OUString +SwVbaWrapFormat::getServiceImplName() +{ + return "SwVbaWrapFormat"; +} + +uno::Sequence< OUString > +SwVbaWrapFormat::getServiceNames() +{ + static uno::Sequence< OUString > const aServiceNames + { + "ooo.vba.word.WrapFormat" + }; + return aServiceNames; +} + +namespace wrapformat +{ +namespace sdecl = comphelper::service_decl; +sdecl::vba_service_class_<SwVbaWrapFormat, sdecl::with_args<true> > const serviceImpl; +sdecl::ServiceDecl const serviceDecl( + serviceImpl, + "SwVbaWrapFormat", + "ooo.vba.word.WrapFormat" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/vbawrapformat.hxx b/sw/source/ui/vba/vbawrapformat.hxx new file mode 100644 index 000000000..ef28118ed --- /dev/null +++ b/sw/source/ui/vba/vbawrapformat.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UI_VBA_VBAWRAPFORMAT_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_VBAWRAPFORMAT_HXX + +#include <com/sun/star/drawing/XShape.hpp> +#include <ooo/vba/word/XWrapFormat.hpp> +#include <vbahelper/vbahelperinterface.hxx> + +typedef InheritedHelperInterfaceWeakImpl< ooo::vba::word::XWrapFormat > SwVbaWrapFormat_BASE; + +class SwVbaWrapFormat : public SwVbaWrapFormat_BASE +{ +private: + css::uno::Reference< css::drawing::XShape > m_xShape; + css::uno::Reference< css::beans::XPropertySet > m_xPropertySet; + sal_Int32 mnWrapFormatType; + sal_Int32 mnSide; + +private: + /// @throws css::uno::RuntimeException + void makeWrap(); + /// @throws css::uno::RuntimeException + float getDistance( const OUString& sName ); + /// @throws css::uno::RuntimeException + void setDistance( const OUString& sName, float _distance ); + +public: + SwVbaWrapFormat( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& xContext ); + + virtual ::sal_Int32 SAL_CALL getType() override; + virtual void SAL_CALL setType( ::sal_Int32 _type ) override; + virtual ::sal_Int32 SAL_CALL getSide() override; + virtual void SAL_CALL setSide( ::sal_Int32 _side ) override; + virtual float SAL_CALL getDistanceTop() override; + virtual void SAL_CALL setDistanceTop( float _distancetop ) override; + virtual float SAL_CALL getDistanceBottom() override; + virtual void SAL_CALL setDistanceBottom( float _distancebottom ) override; + virtual float SAL_CALL getDistanceLeft() override; + virtual void SAL_CALL setDistanceLeft( float _distanceleft ) override; + virtual float SAL_CALL getDistanceRight() override; + virtual void SAL_CALL setDistanceRight( float _distanceright ) override; + + virtual OUString getServiceImplName() override; + virtual css::uno::Sequence<OUString> getServiceNames() override; +}; + +#endif // INCLUDED_SW_SOURCE_UI_VBA_VBAWRAPFORMAT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/wordvbahelper.cxx b/sw/source/ui/vba/wordvbahelper.cxx new file mode 100644 index 000000000..658750207 --- /dev/null +++ b/sw/source/ui/vba/wordvbahelper.cxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <docsh.hxx> +#include "wordvbahelper.hxx" +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <unotxdoc.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <view.hxx> +#include <viewsh.hxx> + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +namespace ooo::vba::word +{ + +SwDocShell* getDocShell( const uno::Reference< frame::XModel>& xModel ) +{ + uno::Reference< lang::XUnoTunnel > xTunnel( xModel, uno::UNO_QUERY_THROW ); + SwXTextDocument* pXDoc = reinterpret_cast< SwXTextDocument * >( sal::static_int_cast< sal_IntPtr >(xTunnel->getSomething(SwXTextDocument::getUnoTunnelId()))); + return pXDoc ? pXDoc->GetDocShell() : nullptr; +} + +SwView* getView( const uno::Reference< frame::XModel>& xModel ) +{ + SwDocShell* pDocShell = getDocShell( xModel ); + return pDocShell? pDocShell->GetView() : nullptr; +} + +uno::Reference< text::XTextViewCursor > getXTextViewCursor( const uno::Reference< frame::XModel >& xModel ) +{ + uno::Reference< frame::XController > xController = xModel->getCurrentController(); + uno::Reference< text::XTextViewCursorSupplier > xTextViewCursorSupp( xController, uno::UNO_QUERY_THROW ); + uno::Reference< text::XTextViewCursor > xTextViewCursor = xTextViewCursorSupp->getViewCursor(); + return xTextViewCursor; +} + +uno::Reference< style::XStyle > getCurrentPageStyle( const uno::Reference< frame::XModel >& xModel ) +{ + uno::Reference< beans::XPropertySet > xCursorProps( getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + return getCurrentPageStyle( xModel, xCursorProps ); +} + +uno::Reference< style::XStyle > getCurrentPageStyle( const uno::Reference< frame::XModel >& xModel, const uno::Reference< beans::XPropertySet >& xProps ) +{ + OUString aPageStyleName; + xProps->getPropertyValue("PageStyleName") >>= aPageStyleName; + uno::Reference< style::XStyleFamiliesSupplier > xSytleFamSupp( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xSytleFamNames( xSytleFamSupp->getStyleFamilies(), uno::UNO_SET_THROW ); + uno::Reference< container::XNameAccess > xPageStyles( xSytleFamNames->getByName("PageStyles"), uno::UNO_QUERY_THROW ); + uno::Reference< style::XStyle > xStyle( xPageStyles->getByName( aPageStyleName ), uno::UNO_QUERY_THROW ); + + return xStyle; +} + +sal_Int32 getPageCount( const uno::Reference< frame::XModel>& xModel ) +{ + SwDocShell* pDocShell = getDocShell( xModel ); + SwViewShell* pViewSh = pDocShell ? pDocShell->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() : nullptr; + return pViewSh ? pViewSh->GetPageCount() : 0; +} + +uno::Reference< style::XStyle > getDefaultParagraphStyle( const uno::Reference< frame::XModel >& xModel ) +{ + uno::Reference< style::XStyleFamiliesSupplier > xSytleFamSupp( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xSytleFamNames( xSytleFamSupp->getStyleFamilies(), uno::UNO_SET_THROW ); + uno::Reference< container::XNameAccess > xParaStyles( xSytleFamNames->getByName("ParagraphStyles"), uno::UNO_QUERY_THROW ); + uno::Reference< style::XStyle > xStyle( xParaStyles->getByName("Standard"), uno::UNO_QUERY_THROW ); + + return xStyle; +} + +uno::Reference< text::XTextRange > getFirstObjectPosition( const uno::Reference< text::XText >& xText ) +{ + // if the first object is table, get the position of first cell + uno::Reference< text::XTextRange > xTextRange; + uno::Reference< container::XEnumerationAccess > xParaAccess( xText, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration> xParaEnum = xParaAccess->createEnumeration(); + if( xParaEnum->hasMoreElements() ) + { + uno::Reference< lang::XServiceInfo > xServiceInfo( xParaEnum->nextElement(), uno::UNO_QUERY_THROW ); + if( xServiceInfo->supportsService("com.sun.star.text.TextTable") ) + { + uno::Reference< table::XCellRange > xCellRange( xServiceInfo, uno::UNO_QUERY_THROW ); + uno::Reference< text::XText> xFirstCellText( xCellRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW ); + xTextRange = xFirstCellText->getStart(); + } + } + if( !xTextRange.is() ) + xTextRange = xText->getStart(); + return xTextRange; +} + +uno::Reference< text::XText > getCurrentXText( const uno::Reference< frame::XModel >& xModel ) +{ + uno::Reference< text::XTextRange > xTextRange; + uno::Reference< text::XTextContent > xTextContent( xModel->getCurrentSelection(), uno::UNO_QUERY ); + if( !xTextContent.is() ) + { + uno::Reference< container::XIndexAccess > xIndexAccess( xModel->getCurrentSelection(), uno::UNO_QUERY ); + if( xIndexAccess.is() ) + { + xTextContent.set( xIndexAccess->getByIndex(0), uno::UNO_QUERY ); + } + } + + if( xTextContent.is() ) + xTextRange = xTextContent->getAnchor(); + + if( !xTextRange.is() ) + xTextRange.set( getXTextViewCursor( xModel ), uno::UNO_QUERY_THROW ); + + uno::Reference< text::XText > xText; + try + { + xText = xTextRange->getText(); + } + catch (const uno::RuntimeException&) + { + //catch exception "no text selection" + } + uno::Reference< beans::XPropertySet > xVCProps( xTextRange, uno::UNO_QUERY_THROW ); + while( xVCProps->getPropertyValue("TextTable") >>= xTextContent ) + { + xText = xTextContent->getAnchor()->getText(); + xVCProps.set( xText->createTextCursor(), uno::UNO_QUERY_THROW ); + } + + if( !xText.is() ) + throw uno::RuntimeException("no text selection" ); + + return xText; +} + +bool gotoSelectedObjectAnchor( const uno::Reference< frame::XModel>& xModel ) +{ + bool isObjectSelected = false; + uno::Reference< text::XTextContent > xTextContent( xModel->getCurrentSelection(), uno::UNO_QUERY ); + if( xTextContent.is() ) + { + uno::Reference< text::XTextRange > xTextRange( xTextContent->getAnchor(), uno::UNO_SET_THROW ); + uno::Reference< view::XSelectionSupplier > xSelectSupp( xModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xSelectSupp->select( uno::makeAny( xTextRange ) ); + isObjectSelected = true; + } + return isObjectSelected; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/ui/vba/wordvbahelper.hxx b/sw/source/ui/vba/wordvbahelper.hxx new file mode 100644 index 000000000..d0ac9e43f --- /dev/null +++ b/sw/source/ui/vba/wordvbahelper.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_UI_VBA_WORDVBAHELPER_HXX +#define INCLUDED_SW_SOURCE_UI_VBA_WORDVBAHELPER_HXX + +#include <vbahelper/vbahelper.hxx> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +class SwDocShell; +class SwView; +namespace ooo +{ + namespace vba + { + namespace word + { + //css::uno::Reference< css::frame::XModel > getCurrentDocument() throw (css::uno::RuntimeException); + SwDocShell* getDocShell( const css::uno::Reference< css::frame::XModel>& xModel ); + SwView* getView( const css::uno::Reference< css::frame::XModel>& xModel ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextViewCursor > getXTextViewCursor( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::style::XStyle > getCurrentPageStyle( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::style::XStyle > getCurrentPageStyle( const css::uno::Reference< css::frame::XModel>& xModel, const css::uno::Reference< css::beans::XPropertySet >& xProps ); + /// @throws css::uno::RuntimeException + sal_Int32 getPageCount( const css::uno::Reference< css::frame::XModel>& xModel ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::style::XStyle > getDefaultParagraphStyle( const css::uno::Reference< css::frame::XModel >& xModel ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XTextRange > getFirstObjectPosition( const css::uno::Reference< css::text::XText >& xText ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::text::XText > getCurrentXText( const css::uno::Reference< css::frame::XModel>& xModel ); + /// @throws css::uno::RuntimeException + bool gotoSelectedObjectAnchor( const css::uno::Reference< css::frame::XModel>& xModel ); + + enum E_DIRECTION + { + MOVE_LEFT = 1, + MOVE_RIGHT, + MOVE_UP, + MOVE_DOWN + }; + +} // word +} // vba +} // ooo +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/appenv.cxx b/sw/source/uibase/app/appenv.cxx new file mode 100644 index 000000000..71b042b28 --- /dev/null +++ b/sw/source/uibase/app/appenv.cxx @@ -0,0 +1,493 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/request.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/pbinitem.hxx> +#include <editeng/paperinf.hxx> +#include <fmthdft.hxx> +#include <swwait.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <frmatr.hxx> +#include <fldbas.hxx> +#include <swundo.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <dialoghelp.hxx> +#include <fmtcol.hxx> +#include <frmmgr.hxx> +#include <fldmgr.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> +#include <expfld.hxx> +#include <SwStyleNameMapper.hxx> +#include <fmtpdsc.hxx> + +#include <cmdid.h> +#include <strings.hrc> +#include <swabstdlg.hxx> +#include <envimg.hxx> +#include "appenv.hxx" + +#define ENV_NEWDOC RET_OK +#define ENV_INSERT RET_USER + +// Function used for labels and envelopes in applab.cxx and appenv.cxx +OUString InsertLabEnvText( SwWrtShell& rSh, SwFieldMgr& rFieldMgr, const OUString& rText ) +{ + OUString sRet; + OUString aText = rText.replaceAll("\r", ""); + + sal_Int32 nTokenPos = 0; + while( -1 != nTokenPos ) + { + OUString aLine = aText.getToken( 0, '\n', nTokenPos ); + while ( !aLine.isEmpty() ) + { + OUString sTmpText; + bool bField = false; + + sal_Int32 nPos = aLine.indexOf( '<' ); + if (0 != nPos) + { + sal_Int32 const nCopy((nPos != -1) ? nPos : aLine.getLength()); + sTmpText = aLine.copy(0, nCopy); + aLine = aLine.copy(nCopy); + } + else + { + nPos = aLine.indexOf( '>' ); + if ( nPos == -1 ) + { + sTmpText = aLine; + aLine.clear(); + } + else + { + sTmpText = aLine.copy( 0, nPos + 1); + aLine = aLine.copy( nPos + 1); + + // Database fields must contain at least 3 points! + OUString sDBName( sTmpText.copy( 1, sTmpText.getLength() - 2)); + if (comphelper::string::getTokenCount(sDBName, '.') >= 3) + { + sDBName = ::ReplacePoint(sDBName, true); + SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, sDBName, OUString(), 0, &rSh); + rFieldMgr.InsertField( aData ); + sRet = sDBName; + bField = true; + } + } + } + if ( !bField ) + rSh.Insert( sTmpText ); + } + rSh.SplitNode(); + } + rSh.DelLeft(); // Again remove last linebreak + + return sRet; +} + +static void lcl_CopyCollAttr(SwWrtShell const * pOldSh, SwWrtShell* pNewSh, sal_uInt16 nCollId) +{ + sal_uInt16 nCollCnt = pOldSh->GetTextFormatCollCount(); + for( sal_uInt16 nCnt = 0; nCnt < nCollCnt; ++nCnt ) + { + SwTextFormatColl* pColl = &pOldSh->GetTextFormatColl(nCnt); + if(nCollId == pColl->GetPoolFormatId()) + pNewSh->GetTextCollFromPool(nCollId)->SetFormatAttr(pColl->GetAttrSet()); + } +} + +void SwModule::InsertEnv( SfxRequest& rReq ) +{ + static sal_uInt16 nTitleNo = 0; + + SwDocShell *pMyDocSh; + SfxViewFrame *pFrame; + SwView *pNewView; + SwWrtShell *pOldSh, + *pSh; + + // Get current shell + pMyDocSh = static_cast<SwDocShell*>( SfxObjectShell::Current()); + pOldSh = pMyDocSh ? pMyDocSh->GetWrtShell() : nullptr; + + // Create new document (don't show!) + SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::STANDARD ) ); + xDocSh->DoInitNew(); + pFrame = SfxViewFrame::LoadHiddenDocument( *xDocSh, SFX_INTERFACE_NONE ); + pNewView = static_cast<SwView*>( pFrame->GetViewShell()); + pNewView->AttrChangedNotify(nullptr); // so that SelectShell is being called + pSh = pNewView->GetWrtShellPtr(); + + OUString aTmp = SwResId(STR_ENV_TITLE) + OUString::number( ++nTitleNo ); + xDocSh->SetTitle( aTmp ); + + // if applicable, copy the old Collections "Sender" and "Receiver" to + // a new document + if ( pOldSh ) + { + ::lcl_CopyCollAttr(pOldSh, pSh, RES_POOLCOLL_JAKETADRESS); + ::lcl_CopyCollAttr(pOldSh, pSh, RES_POOLCOLL_SENDADRESS); + } + + // Read SwEnvItem from config + SwEnvCfgItem aEnvCfg; + + // Check if there's already an envelope. + bool bEnvChange = false; + + SfxItemSet aSet(GetPool(), svl::Items<FN_ENVELOP, FN_ENVELOP>{}); + aSet.Put(aEnvCfg.GetItem()); + + SfxPrinter* pTempPrinter = pSh->getIDocumentDeviceAccess().getPrinter( true ); + if(pOldSh ) + { + const SwPageDesc& rCurPageDesc = pOldSh->GetPageDesc(pOldSh->GetCurPageDesc()); + OUString sJacket; + SwStyleNameMapper::FillUIName( RES_POOLPAGE_JAKET, sJacket ); + bEnvChange = rCurPageDesc.GetName() == sJacket; + + IDocumentDeviceAccess& rIDDA_old = pOldSh->getIDocumentDeviceAccess(); + if( rIDDA_old.getPrinter( false ) ) + { + IDocumentDeviceAccess& rIDDA = pSh->getIDocumentDeviceAccess(); + rIDDA.setJobsetup( *rIDDA_old.getJobsetup() ); + //#69563# if it isn't the same printer then the pointer has been invalidated! + pTempPrinter = rIDDA.getPrinter( true ); + } + pTempPrinter->SetPaperBin(rCurPageDesc.GetMaster().GetPaperBin().GetValue()); + + } + + ScopedVclPtr<SfxAbstractTabDialog> pDlg; + short nMode = ENV_INSERT; + + const SwEnvItem* pItem = rReq.GetArg<SwEnvItem>(FN_ENVELOP); + if ( !pItem ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSwEnvDlg(GetFrameWeld(pMyDocSh), aSet, pOldSh, pTempPrinter, !bEnvChange)); + nMode = pDlg->Execute(); + } + else + { + const SfxBoolItem* pBoolItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + if ( pBoolItem && pBoolItem->GetValue() ) + nMode = ENV_NEWDOC; + } + + if (nMode == ENV_NEWDOC || nMode == ENV_INSERT) + { + SwWait aWait( static_cast<SwDocShell&>(*xDocSh), true ); + + // Read dialog and save item to config + const SwEnvItem& rItem = pItem ? *pItem : static_cast<const SwEnvItem&>( pDlg->GetOutputItemSet()->Get(FN_ENVELOP) ); + aEnvCfg.GetItem() = rItem; + aEnvCfg.Commit(); + + // When we print we take the Jobsetup that is set up in the dialog. + // Information has to be set here, before a possible destruction of + // the new shell because the shell's printer has been handed to the + // dialog. + if ( nMode != ENV_NEWDOC ) + { + OSL_ENSURE(pOldSh, "No document - wasn't 'Insert' disabled???"); + SvxPaperBinItem aItem( RES_PAPER_BIN ); + aItem.SetValue(static_cast<sal_uInt8>(pSh->getIDocumentDeviceAccess().getPrinter(true)->GetPaperBin())); + pOldSh->GetPageDescFromPool(RES_POOLPAGE_JAKET)->GetMaster().SetFormatAttr(aItem); + } + + SwWrtShell *pTmp = nMode == ENV_INSERT ? pOldSh : pSh; + const SwPageDesc* pFollow = nullptr; + SwTextFormatColl *pSend = pTmp->GetTextCollFromPool( RES_POOLCOLL_SENDADRESS ), + *pAddr = pTmp->GetTextCollFromPool( RES_POOLCOLL_JAKETADRESS); + const OUString sSendMark = pSend->GetName(); + const OUString sAddrMark = pAddr->GetName(); + + if (nMode == ENV_INSERT) + { + + SetView(&pOldSh->GetView()); // Set pointer to top view + + // Delete new document + xDocSh->DoClose(); + pSh = pOldSh; + //#i4251# selected text or objects in the document should + //not be deleted on inserting envelopes + pSh->EnterStdMode(); + // Here it goes (insert) + pSh->StartUndo(SwUndoId::UI_INSERT_ENVELOPE); + pSh->StartAllAction(); + pSh->SttEndDoc(true); + + if (bEnvChange) + { + // followup template: page 2 + pFollow = pSh->GetPageDesc(pSh->GetCurPageDesc()).GetFollow(); + + // Delete text from the first page + if ( !pSh->SttNxtPg(true) ) + pSh->EndPg(true); + pSh->DelRight(); + // Delete frame of the first page + if ( pSh->GotoFly(sSendMark) ) + { + pSh->EnterSelFrameMode(); + pSh->DelRight(); + } + if ( pSh->GotoFly(sAddrMark) ) + { + pSh->EnterSelFrameMode(); + pSh->DelRight(); + } + pSh->SttEndDoc(true); + } + else + // Followup template: page 1 + pFollow = &pSh->GetPageDesc(pSh->GetCurPageDesc()); + + // Insert page break + if ( pSh->IsCursorInTable() ) + { + pSh->SplitNode(); + pSh->Right( CRSR_SKIP_CHARS, false, 1, false ); + SfxItemSet aBreakSet( pSh->GetAttrPool(), svl::Items<RES_PAGEDESC, RES_PAGEDESC>{} ); + aBreakSet.Put( SwFormatPageDesc( pFollow ) ); + pSh->SetTableAttr( aBreakSet ); + } + else + { + OUString sFollowName(pFollow->GetName()); + pSh->InsertPageBreak(&sFollowName, std::nullopt); + } + pSh->SttEndDoc(true); + } + else + { + pFollow = &pSh->GetPageDesc(pSh->GetCurPageDesc()); + // Let's go (print) + pSh->StartAllAction(); + pSh->DoUndo(false); + + // Again, copy the new collections "Sender" and "Receiver" to + // a new document + if ( pOldSh ) + { + ::lcl_CopyCollAttr(pOldSh, pSh, RES_POOLCOLL_JAKETADRESS); + ::lcl_CopyCollAttr(pOldSh, pSh, RES_POOLCOLL_SENDADRESS); + } + } + + SET_CURR_SHELL(pSh); + pSh->SetNewDoc(); // Avoid performance problems + + // Remember Flys of this site + std::vector<SwFrameFormat*> aFlyArr; + if( ENV_NEWDOC != nMode && !bEnvChange ) + pSh->GetPageObjs( aFlyArr ); + + // Get page description + SwPageDesc* pDesc = pSh->GetPageDescFromPool(RES_POOLPAGE_JAKET); + SwFrameFormat& rFormat = pDesc->GetMaster(); + + Printer *pPrt = pSh->getIDocumentDeviceAccess().getPrinter( true ); + + // Borders (are put together by Shift-Offset and alignment) + Size aPaperSize = pPrt->PixelToLogic( pPrt->GetPaperSizePixel(), + MapMode(MapUnit::MapTwip)); + if ( !aPaperSize.Width() && !aPaperSize.Height() ) + aPaperSize = SvxPaperInfo::GetPaperSize(PAPER_A4); + if ( aPaperSize.Width() > aPaperSize.Height() ) + Swap( aPaperSize ); + + long lLeft = rItem.m_nShiftRight, + lUpper = rItem.m_nShiftDown; + + sal_uInt16 nPageW = static_cast<sal_uInt16>(std::max(rItem.m_nWidth, rItem.m_nHeight)), + nPageH = static_cast<sal_uInt16>(std::min(rItem.m_nWidth, rItem.m_nHeight)); + + switch (rItem.m_eAlign) + { + case ENV_HOR_LEFT: break; + case ENV_HOR_CNTR: lLeft += std::max(0L, long(aPaperSize.Width() - nPageW)) / 2; + break; + case ENV_HOR_RGHT: lLeft += std::max(0L, long(aPaperSize.Width() - nPageW)); + break; + case ENV_VER_LEFT: lUpper += std::max(0L, long(aPaperSize.Width() - nPageH)); + break; + case ENV_VER_CNTR: lUpper += std::max(0L, long(aPaperSize.Width() - nPageH)) / 2; + break; + case ENV_VER_RGHT: break; + } + SvxLRSpaceItem aLRMargin( RES_LR_SPACE ); + SvxULSpaceItem aULMargin( RES_UL_SPACE ); + aLRMargin.SetLeft (static_cast<sal_uInt16>(lLeft) ); + aULMargin.SetUpper(static_cast<sal_uInt16>(lUpper)); + aLRMargin.SetRight(0); + aULMargin.SetLower(0); + rFormat.SetFormatAttr(aLRMargin); + rFormat.SetFormatAttr(aULMargin); + + // Header and footer + rFormat.SetFormatAttr(SwFormatHeader(false)); + pDesc->ChgHeaderShare(false); + rFormat.SetFormatAttr(SwFormatFooter(false)); + pDesc->ChgFooterShare(false); + + // Page numbering + pDesc->SetUseOn(UseOnPage::All); + + // Page size + rFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Fixed, + nPageW + lLeft, nPageH + lUpper)); + + // Set type of page numbering + SvxNumberType aType; + aType.SetNumberingType(SVX_NUM_NUMBER_NONE); + pDesc->SetNumType(aType); + + // Followup template + if (pFollow) + pDesc->SetFollow(pFollow); + + // Landscape + pDesc->SetLandscape( rItem.m_eAlign >= ENV_VER_LEFT && + rItem.m_eAlign <= ENV_VER_RGHT); + + // Apply page description + + size_t nPos; + pSh->FindPageDescByName( pDesc->GetName(), + false, + &nPos ); + + pSh->ChgPageDesc( nPos, *pDesc); + pSh->ChgCurPageDesc(*pDesc); + + // Insert Frame + SwFlyFrameAttrMgr aMgr(false, pSh, Frmmgr_Type::ENVELP, nullptr); + SwFieldMgr aFieldMgr; + aMgr.SetHeightSizeType(SwFrameSize::Variable); + + // Overwrite defaults! + aMgr.GetAttrSet().Put( SvxBoxItem(RES_BOX) ); + aMgr.SetULSpace( 0, 0 ); + aMgr.SetLRSpace( 0, 0 ); + + // Sender + if (rItem.m_bSend) + { + pSh->SttEndDoc(true); + aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PAGE, + Point(rItem.m_nSendFromLeft + lLeft, rItem.m_nSendFromTop + lUpper), + Size (rItem.m_nAddrFromLeft - rItem.m_nSendFromLeft, 0)); + + pSh->EnterSelFrameMode(); + pSh->SetFlyName(sSendMark); + pSh->UnSelectFrame(); + pSh->LeaveSelFrameMode(); + pSh->SetTextFormatColl( pSend ); + InsertLabEnvText( *pSh, aFieldMgr, rItem.m_aSendText ); + aMgr.UpdateAttrMgr(); + } + + // Addressee + pSh->SttEndDoc(true); + + aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PAGE, + Point(rItem.m_nAddrFromLeft + lLeft, rItem.m_nAddrFromTop + lUpper), + Size (nPageW - rItem.m_nAddrFromLeft - 566, 0)); + pSh->EnterSelFrameMode(); + pSh->SetFlyName(sAddrMark); + pSh->UnSelectFrame(); + pSh->LeaveSelFrameMode(); + pSh->SetTextFormatColl( pAddr ); + InsertLabEnvText(*pSh, aFieldMgr, rItem.m_aAddrText); + + // Move Flys to the "old" pages + if (!aFlyArr.empty()) + pSh->SetPageObjsNewPage(aFlyArr); + + // Finished + pSh->SttEndDoc(true); + + pSh->EndAllAction(); + + if (nMode == ENV_NEWDOC) + pSh->DoUndo(); + else + pSh->EndUndo(SwUndoId::UI_INSERT_ENVELOPE); + + if (nMode == ENV_NEWDOC) + { + pFrame->GetFrame().Appear(); + + if ( rItem.m_aAddrText.indexOf('<') >= 0 ) + { + static sal_uInt16 const aInva[] = + { + SID_SBA_BRW_UPDATE, + SID_SBA_BRW_INSERT, + SID_SBA_BRW_MERGE, + 0 + }; + pFrame->GetBindings().Invalidate( aInva ); + + // Open database beamer + ShowDBObj(*pNewView, pSh->GetDBData()); + } + } + + if ( !pItem ) + { + rReq.AppendItem( rItem ); + if ( nMode == ENV_NEWDOC ) + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, true ) ); + } + + rReq.Done(); + } + else // Abort + { + rReq.Ignore(); + + xDocSh->DoClose(); + --nTitleNo; + + // Set pointer to top view + if (pOldSh) + SetView(&pOldSh->GetView()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/appenv.hxx b/sw/source/uibase/app/appenv.hxx new file mode 100644 index 000000000..64fcd4a81 --- /dev/null +++ b/sw/source/uibase/app/appenv.hxx @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_UIBASE_APP_APPENV_HXX +#define INCLUDED_SW_SOURCE_UIBASE_APP_APPENV_HXX + +#include <rtl/ustring.hxx> + +class SwWrtShell; +class SwFieldMgr; + +OUString InsertLabEnvText( SwWrtShell& , SwFieldMgr& , const OUString& ); + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/apphdl.cxx b/sw/source/uibase/app/apphdl.cxx new file mode 100644 index 000000000..d92879eb3 --- /dev/null +++ b/sw/source/uibase/app/apphdl.cxx @@ -0,0 +1,1077 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/propertysequence.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/event.hxx> +#include <sfx2/objitem.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <svtools/restartdialog.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <svl/isethint.hxx> +#include <svl/stritem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/fcontnr.hxx> +#include <svl/ctloptions.hxx> +#include <svtools/colorcfg.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/useroptions.hxx> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <sfx2/docfile.hxx> +#include <sfx2/objface.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <view.hxx> +#include <pview.hxx> +#include <srcview.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <cmdid.h> +#include <initui.hxx> +#include <uitool.hxx> +#include <swmodule.hxx> +#include <wview.hxx> +#include <usrpref.hxx> +#include <gloslst.hxx> +#include <glosdoc.hxx> +#include <doc.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <prtopt.hxx> +#include <modcfg.hxx> +#include <fontcfg.hxx> +#include <barcfg.hxx> +#include <navicfg.hxx> +#include <uinums.hxx> +#include <dbconfig.hxx> +#include <mmconfigitem.hxx> +#include <strings.hrc> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdb/TextConnectionSettings.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/task/OfficeRestartManager.hpp> +#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp> +#include <swabstdlg.hxx> +#include <comphelper/dispatchcommand.hxx> +#include <comphelper/processfactory.hxx> + +#include <salhelper/simplereferenceobject.hxx> +#include <rtl/ref.hxx> + +#include <officecfg/Office/Common.hxx> + +using namespace ::com::sun::star; + +// Slotmaps for the application's methods + +// here are the SlotID's being included +// see Idl-file +#define ShellClass_SwModule +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwModule, SfxModule) + +void SwModule::InitInterface_Impl() +{ + GetStaticInterface()->RegisterStatusBar(StatusBarId::WriterStatusBar); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, + SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer, + ToolbarId::Module_Toolbox); +} + +// other states +void SwModule::StateOther(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + SwView* pActView = ::GetActiveView(); + bool bWebView = dynamic_cast<SwWebView*>( pActView ) != nullptr; + + while(nWhich) + { + switch(nWhich) + { + case FN_BUSINESS_CARD: + case FN_LABEL: + case FN_ENVELOP: + { + bool bDisable = false; + SfxViewShell* pCurrView = SfxViewShell::Current(); + if( !pCurrView || dynamic_cast< const SwView *>( pCurrView ) == nullptr ) + bDisable = true; + SwDocShell *pDocSh = static_cast<SwDocShell*>( SfxObjectShell::Current()); + if ( bDisable || + (pDocSh && (pDocSh->IsReadOnly() || + pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)) ) + rSet.DisableItem( nWhich ); + + } + break; + case FN_XFORMS_INIT: + // slot is always active! + break; + case FN_EDIT_FORMULA: + { + SwWrtShell* pSh = nullptr; + SelectionType nSelection = SelectionType::NONE; + if( pActView ) + pSh = &pActView->GetWrtShell(); + if( pSh ) + nSelection = pSh->GetSelectionType(); + + if( (pSh && pSh->HasSelection()) || + !(nSelection & (SelectionType::Text | SelectionType::Table))) + rSet.DisableItem(nWhich); + } + break; + case SID_ATTR_METRIC: + rSet.Put( SfxUInt16Item( SID_ATTR_METRIC, static_cast< sal_uInt16 >(::GetDfltMetric(bWebView)))); + break; + case FN_SET_MODOPT_TBLNUMFMT: + rSet.Put( SfxBoolItem( nWhich, m_pModuleConfig-> + IsInsTableFormatNum( bWebView ))); + break; + case FN_MAILMERGE_WIZARD: + { + SfxObjectShell* pObjectShell = GetObjectShell(); + if (pObjectShell && pObjectShell->isExportLocked()) + rSet.DisableItem(nWhich); + break; + } + case FN_MAILMERGE_FIRST_ENTRY: + case FN_MAILMERGE_PREV_ENTRY: + case FN_MAILMERGE_NEXT_ENTRY: + case FN_MAILMERGE_LAST_ENTRY: + { + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->GetMailMergeConfigItem(); + if (!xConfigItem) + rSet.DisableItem(nWhich); + else if (xConfigItem->GetConnection().is() + && !xConfigItem->GetConnection()->isClosed()) + { + bool bFirst, bLast; + bool bValid = xConfigItem->IsResultSetFirstLast(bFirst, bLast); + + if (!bValid || + (bFirst && (nWhich == FN_MAILMERGE_FIRST_ENTRY || nWhich == FN_MAILMERGE_PREV_ENTRY)) || + (bLast && (nWhich == FN_MAILMERGE_LAST_ENTRY || nWhich == FN_MAILMERGE_NEXT_ENTRY))) + { + rSet.DisableItem(nWhich); + } + } + } + break; + case FN_MAILMERGE_CURRENT_ENTRY: + case FN_MAILMERGE_EXCLUDE_ENTRY: + { + // just trigger calling statusChanged() of MMExcludeEntryController + // resp. MMCurrentEntryController + rSet.InvalidateItem(nWhich); + } + break; + case FN_MAILMERGE_CREATE_DOCUMENTS: + case FN_MAILMERGE_SAVE_DOCUMENTS: + case FN_MAILMERGE_PRINT_DOCUMENTS: + case FN_MAILMERGE_EMAIL_DOCUMENTS: + { + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->EnsureMailMergeConfigItem(); + + // #i51949# hide e-Mail option if e-Mail is not supported + // #i63267# printing might be disabled + if (!xConfigItem || + !xConfigItem->GetConnection().is() || + xConfigItem->GetConnection()->isClosed() || + !xConfigItem->GetResultSet().is() || + xConfigItem->GetCurrentDBData().sDataSource.isEmpty() || + xConfigItem->GetCurrentDBData().sCommand.isEmpty() || + (nWhich == FN_MAILMERGE_PRINT_DOCUMENTS && Application::GetSettings().GetMiscSettings().GetDisablePrinting()) || + (nWhich == FN_MAILMERGE_EMAIL_DOCUMENTS && !xConfigItem->IsMailAvailable())) + { + rSet.DisableItem(nWhich); + } + } + break; + default: + OSL_FAIL("::StateOther: default"); + } + nWhich = aIter.NextWhich(); + } +} + +// start field dialog +static void NewXForms( SfxRequest& rReq ); // implementation: below + +std::shared_ptr<SwMailMergeConfigItem> SwView::EnsureMailMergeConfigItem(const SfxItemSet* pArgs) +{ + // create if it does not exist yet + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = GetMailMergeConfigItem(); + if (!xMMConfig) + { + xMMConfig = std::make_shared<SwMailMergeConfigItem>(); + xMMConfig->SetSourceView(this); + + //set the first used database as default source on the config item + const SfxPoolItem* pItem = nullptr; + if (pArgs && SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_DATABASE_PROPERTIES, false, &pItem)) + { + //mailmerge has been called from the database beamer + uno::Sequence< beans::PropertyValue> aDBValues; + if (static_cast<const SfxUnoAnyItem*>(pItem)->GetValue() >>= aDBValues) + { + SwDBData aDBData; + svx::ODataAccessDescriptor aDescriptor(aDBValues); + aDescriptor[svx::DataAccessDescriptorProperty::DataSource] >>= aDBData.sDataSource; + aDescriptor[svx::DataAccessDescriptorProperty::Command] >>= aDBData.sCommand; + aDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= aDBData.nCommandType; + + uno::Reference< sdbc::XConnection> xConnection; + uno::Reference< sdbc::XDataSource> xSource; + uno::Reference< sdbcx::XColumnsSupplier> xColumnsSupplier; + if (aDescriptor.has(svx::DataAccessDescriptorProperty::Connection)) + aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection; + uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY); + if (xChild.is()) + xSource.set(xChild->getParent(), uno::UNO_QUERY); + xMMConfig->SetCurrentConnection( + xSource, SharedConnection(xConnection, SharedConnection::NoTakeOwnership), + xColumnsSupplier, aDBData); + } + } + else + { + std::vector<OUString> aDBNameList; + std::vector<OUString> aAllDBNames; + GetWrtShell().GetAllUsedDB(aDBNameList, &aAllDBNames); + if (!aDBNameList.empty()) + { + OUString sDBName(aDBNameList[0]); + SwDBData aDBData; + sal_Int32 nIdx{ 0 }; + aDBData.sDataSource = sDBName.getToken(0, DB_DELIM, nIdx); + aDBData.sCommand = sDBName.getToken(0, DB_DELIM, nIdx); + aDBData.nCommandType = sDBName.getToken(0, DB_DELIM, nIdx).toInt32(); + //set the currently used database for the wizard + xMMConfig->SetCurrentDBData(aDBData); + } + } + + SetMailMergeConfigItem(xMMConfig); + } + return xMMConfig; +} + +#if HAVE_FEATURE_DBCONNECTIVITY + +namespace +{ + +SwView* lcl_LoadDoc(SwView* pView, const OUString& rURL) +{ + SwView* pNewView = nullptr; + if(!rURL.isEmpty()) + { + SfxStringItem aURL(SID_FILE_NAME, rURL); + SfxStringItem aTargetFrameName( SID_TARGETNAME, "_blank" ); + SfxBoolItem aHidden( SID_HIDDEN, true ); + SfxStringItem aReferer(SID_REFERER, pView->GetDocShell()->GetTitle()); + const SfxObjectItem* pItem = static_cast<const SfxObjectItem*>( + pView->GetViewFrame()->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::SYNCHRON, + { &aURL, &aHidden, &aReferer, &aTargetFrameName })); + SfxShell* pShell = pItem ? pItem->GetShell() : nullptr; + + if(pShell) + { + SfxViewShell* pViewShell = pShell->GetViewShell(); + if(pViewShell) + { + if ((pNewView = dynamic_cast<SwView*>(pViewShell))) + { + pNewView->GetViewFrame()->GetFrame().Appear(); + } + else + { + pViewShell->GetViewFrame()->DoClose(); + } + } + } + } + else + { + SfxStringItem aFactory(SID_NEWDOCDIRECT, SwDocShell::Factory().GetFilterContainer()->GetName()); + const SfxFrameItem* pItem = static_cast<const SfxFrameItem*>( + pView->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NEWDOCDIRECT, SfxCallMode::SYNCHRON, { &aFactory })); + SfxFrame* pFrame = pItem ? pItem->GetFrame() : nullptr; + SfxViewFrame* pViewFrame = pFrame ? pFrame->GetCurrentViewFrame() : nullptr; + pNewView = pViewFrame ? dynamic_cast<SwView*>( pViewFrame->GetViewShell() ) : nullptr; + } + + return pNewView; +} + +class SwMailMergeWizardExecutor : public salhelper::SimpleReferenceObject +{ + SwView* m_pView; // never owner + SwView* m_pView2Close; // never owner + VclPtr<AbstractMailMergeWizard> m_pWizard; // always owner + VclPtr<AbstractMailMergeWizard> m_pWizardToDestroyInCallback; + + void EndDialogHdl(sal_Int32 nResponse); + DECL_LINK( DestroyDialogHdl, void*, void ); + DECL_LINK( DestroyWizardHdl, void*, void ); + DECL_LINK( CancelHdl, void*, void ); + DECL_LINK( CloseFrameHdl, void*, void ); + + void ExecutionFinished(); + void ExecuteWizard(); + +public: + SwMailMergeWizardExecutor(); + virtual ~SwMailMergeWizardExecutor() override; + + void ExecuteMailMergeWizard( const SfxItemSet * pArgs ); +}; + +SwMailMergeWizardExecutor::SwMailMergeWizardExecutor() + : m_pView( nullptr ), + m_pView2Close( nullptr ), + m_pWizard( nullptr ) +{ +} + +SwMailMergeWizardExecutor::~SwMailMergeWizardExecutor() +{ + OSL_ENSURE( m_pWizard == nullptr, "SwMailMergeWizardExecutor: m_pWizard must be Null!" ); +} + +bool lcl_hasAllComponentsAvailable() +{ + try + { + return css::sdb::TextConnectionSettings::create(comphelper::getProcessComponentContext()).is(); + } + catch (const css::uno::Exception &) + { + TOOLS_INFO_EXCEPTION( + "sw.core", "assuming Base to be missing; caught "); + return false; + } +} + +void SwMailMergeWizardExecutor::ExecuteMailMergeWizard( const SfxItemSet * pArgs ) +{ + if(!lcl_hasAllComponentsAvailable()) + { + if (officecfg::Office::Common::PackageKit::EnableBaseInstallation::get()) + { + try + { + using namespace org::freedesktop::PackageKit; + using namespace svtools; + css::uno::Reference< XSyncDbusSessionHelper > xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext())); + const css::uno::Sequence< OUString > vPackages{ "libreoffice-base" }; + xSyncDbusSessionHelper->InstallPackageNames(vPackages, OUString()); + SolarMutexGuard aGuard; + executeRestartDialog(comphelper::getProcessComponentContext(), nullptr, RESTART_REASON_MAILMERGE_INSTALL); + } + catch (const css::uno::Exception &) + { + TOOLS_INFO_EXCEPTION( + "sw.core", + "trying to install LibreOffice Base, caught"); + auto xRestartManager + = css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext()); + if (!xRestartManager->isRestartRequested(false)) + { + // Base is absent, and could not initiate its install - ask user to do that manually + // Only show the dialog if restart is not initiated yet + std::unique_ptr<weld::MessageDialog> xWarnBox(Application::CreateMessageDialog( + nullptr, VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_NO_BASE_FOR_MERGE))); + xWarnBox->run(); + } + } + } else { + auto xRestartManager + = css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext()); + if (!xRestartManager->isRestartRequested(false)) + { + // Base is absent, and could not initiate its install - ask user to do that manually + // Only show the dialog if restart is not initiated yet + std::unique_ptr<weld::MessageDialog> xWarnBox(Application::CreateMessageDialog( + nullptr, VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_NO_BASE_FOR_MERGE))); + xWarnBox->run(); + } + } + return; + } + if ( m_pView ) + { + OSL_FAIL("SwMailMergeWizardExecutor::ExecuteMailMergeWizard: Already executing the wizard!" ); + return; + } + + m_pView = ::GetActiveView(); + if (!m_pView) + return; + + // keep self alive until done. + acquire(); + + // create if it does not exist yet + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->EnsureMailMergeConfigItem(pArgs); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + m_pWizard = pFact->CreateMailMergeWizard(*m_pView, xMMConfig); + + ExecuteWizard(); +} + +void SwMailMergeWizardExecutor::ExecutionFinished() +{ + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + if (xMMConfig) + xMMConfig->Commit(); + + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + if (pDoc) + { + SwDBManager* pDbManager = pDoc->GetDBManager(); + if (pDbManager) + pDbManager->CommitLastRegistrations(); + + // Show the toolbar + m_pView->ShowUIElement("private:resource/toolbar/mailmerge"); + + // Update Mail Merge controls + const sal_uInt16 slotIds[] = { FN_MAILMERGE_FIRST_ENTRY, + FN_MAILMERGE_PREV_ENTRY, + FN_MAILMERGE_NEXT_ENTRY, + FN_MAILMERGE_LAST_ENTRY, + FN_MAILMERGE_CURRENT_ENTRY, + FN_MAILMERGE_EXCLUDE_ENTRY, + FN_MAILMERGE_CREATE_DOCUMENTS, + FN_MAILMERGE_SAVE_DOCUMENTS, + FN_MAILMERGE_PRINT_DOCUMENTS, + FN_MAILMERGE_EMAIL_DOCUMENTS, + 0 }; + m_pView->GetViewFrame()->GetBindings().Invalidate(slotIds); + } + + // release/destroy asynchronously + Application::PostUserEvent( LINK( this, SwMailMergeWizardExecutor, DestroyDialogHdl ) ); +} + +void SwMailMergeWizardExecutor::ExecuteWizard() +{ + m_pWizard->StartExecuteAsync([this](sal_Int32 nResult){ + EndDialogHdl(nResult); + }); +} + +void SwMailMergeWizardExecutor::EndDialogHdl(sal_Int32 nRet) +{ + sal_uInt16 nRestartPage = m_pWizard->GetRestartPage(); + + switch ( nRet ) + { + case RET_LOAD_DOC: + { + SwView* pNewView = lcl_LoadDoc(m_pView, m_pWizard->GetReloadDocument()); + + // Destroy wizard asynchronously, since we are deep inside the wizard and dialog + // machinery code here + m_pWizardToDestroyInCallback = m_pWizard; + Application::PostUserEvent( + LINK( this, SwMailMergeWizardExecutor, DestroyWizardHdl ), nullptr ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + if (pNewView) + { + pNewView->SetMailMergeConfigItem(xMMConfig); + m_pView = pNewView; + xMMConfig->DocumentReloaded(); + //new source view! + xMMConfig->SetSourceView( m_pView ); + m_pWizard = pFact->CreateMailMergeWizard(*m_pView, xMMConfig); + m_pWizard->ShowPage( nRestartPage ); + } + else + { + m_pWizard = pFact->CreateMailMergeWizard(*m_pView, xMMConfig); + } + + // execute the wizard again + ExecuteWizard(); + break; + } + case RET_TARGET_CREATED: + { + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + SwView* pTargetView = xMMConfig->GetTargetView(); + OSL_ENSURE(pTargetView, "No target view has been created"); + if(pTargetView) + { + // destroy wizard asynchronously + m_pWizardToDestroyInCallback = m_pWizard; + Application::PostUserEvent( + LINK( this, SwMailMergeWizardExecutor, DestroyWizardHdl ), nullptr ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + m_pWizard = pFact->CreateMailMergeWizard(*pTargetView, xMMConfig); + m_pWizard->ShowPage( nRestartPage ); + + // execute the wizard again + ExecuteWizard(); + } + else + { + // should not happen - just in case no target view has been created + ExecutionFinished(); + } + break; + } + case RET_REMOVE_TARGET: + { + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + SwView* pTargetView = xMMConfig->GetTargetView(); + SwView* pSourceView = xMMConfig->GetSourceView(); + OSL_ENSURE(pTargetView && pSourceView, "source or target view not available" ); + if(pTargetView && pSourceView) + { + m_pView2Close = pTargetView; + pTargetView->GetViewFrame()->GetTopViewFrame()->GetWindow().Hide(); + pSourceView->GetViewFrame()->GetFrame().AppearWithUpdate(); + // the current view has be set when the target is destroyed + m_pView = pSourceView; + xMMConfig->SetTargetView(nullptr); + + // destroy wizard asynchronously + m_pWizardToDestroyInCallback = m_pWizard; + Application::PostUserEvent( + LINK( this, SwMailMergeWizardExecutor, CloseFrameHdl ), m_pWizard ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + m_pWizard = pFact->CreateMailMergeWizard(*pSourceView, xMMConfig); + m_pWizard->ShowPage( nRestartPage ); + + // execute the wizard again + ExecuteWizard(); + } + else + { + // should not happen - just in case no target view has been created + ExecutionFinished(); + } + break; + } + case RET_CANCEL: + { + // close frame and destroy wizard asynchronously + Application::PostUserEvent( + LINK( this, SwMailMergeWizardExecutor, CancelHdl ), m_pWizard ); + break; + } + default: // finish + { + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + SwView* pSourceView = xMMConfig ? xMMConfig->GetSourceView() : nullptr; + if(pSourceView) + { + xMMConfig->GetSourceView()->GetViewFrame()->GetFrame().Appear(); + } + ExecutionFinished(); + break; + } + + } // switch +} + +IMPL_LINK_NOARG(SwMailMergeWizardExecutor, DestroyDialogHdl, void*, void) +{ + m_pWizard.disposeAndClear(); + + release(); +} + +IMPL_LINK_NOARG(SwMailMergeWizardExecutor, DestroyWizardHdl, void*, void) +{ + m_pWizardToDestroyInCallback.disposeAndClear(); +} + +IMPL_LINK_NOARG(SwMailMergeWizardExecutor, CancelHdl, void*, void) +{ + std::shared_ptr<SwMailMergeConfigItem> xMMConfig = m_pView->GetMailMergeConfigItem(); + if (xMMConfig) + { + if (xMMConfig->GetTargetView()) + { + xMMConfig->GetTargetView()->GetViewFrame()->DoClose(); + xMMConfig->SetTargetView(nullptr); + } + if (xMMConfig->GetSourceView()) + { + auto pViewFrame(xMMConfig->GetSourceView()->GetViewFrame()); + pViewFrame->GetFrame().AppearWithUpdate(); + } + xMMConfig->Commit(); + } + + // Revoke created connections + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + SwDBManager* pDbManager = pDoc->GetDBManager(); + if (pDbManager) + pDbManager->RevokeLastRegistrations(); + + m_pWizard.disposeAndClear(); + release(); +} + +IMPL_LINK_NOARG(SwMailMergeWizardExecutor, CloseFrameHdl, void*, void) +{ + if ( m_pView2Close ) + { + m_pView2Close->GetViewFrame()->DoClose(); + m_pView2Close = nullptr; + } + m_pWizardToDestroyInCallback.disposeAndClear(); +} +} // namespace + +#endif // HAVE_FEATURE_DBCONNECTIVITY + +void SwModule::ExecOther(SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + + sal_uInt16 nWhich = rReq.GetSlot(); + switch (nWhich) + { + case FN_ENVELOP: + InsertEnv( rReq ); + break; + + case FN_BUSINESS_CARD: + case FN_LABEL: + InsertLab(rReq, nWhich == FN_LABEL); + break; + + case FN_XFORMS_INIT: + NewXForms( rReq ); + break; + + case SID_ATTR_METRIC: + if(pArgs && SfxItemState::SET == pArgs->GetItemState(nWhich, false, &pItem)) + { + FieldUnit eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + switch( eUnit ) + { + case FieldUnit::MM: + case FieldUnit::CM: + case FieldUnit::INCH: + case FieldUnit::PICA: + case FieldUnit::POINT: + { + SwView* pActView = ::GetActiveView(); + bool bWebView = dynamic_cast<SwWebView*>( pActView ) != nullptr; + ::SetDfltMetric(eUnit, bWebView); + } + break; + default:;//prevent warning + } + } + break; + + case FN_SET_MODOPT_TBLNUMFMT: + { + bool bWebView = dynamic_cast<SwWebView*>( ::GetActiveView() )!= nullptr , + bSet; + + if( pArgs && SfxItemState::SET == pArgs->GetItemState( + nWhich, false, &pItem )) + bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + else + bSet = !m_pModuleConfig->IsInsTableFormatNum( bWebView ); + + m_pModuleConfig->SetInsTableFormatNum( bWebView, bSet ); + } + break; +#if HAVE_FEATURE_DBCONNECTIVITY + case FN_MAILMERGE_WIZARD: + { + // show the mailmerge wizard + rtl::Reference< SwMailMergeWizardExecutor > xEx( new SwMailMergeWizardExecutor ); + xEx->ExecuteMailMergeWizard( pArgs ); + } + break; + case FN_MAILMERGE_FIRST_ENTRY: + case FN_MAILMERGE_PREV_ENTRY: + case FN_MAILMERGE_NEXT_ENTRY: + case FN_MAILMERGE_LAST_ENTRY: + case FN_MAILMERGE_CURRENT_ENTRY: + { + SwView* pView = ::GetActiveView(); + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + if (!xConfigItem) + return; + + const bool bHadConnection + = xConfigItem->GetConnection().is() && !xConfigItem->GetConnection()->isClosed(); + + sal_Int32 nPos = xConfigItem->GetResultSetPosition(); + switch (nWhich) + { + case FN_MAILMERGE_FIRST_ENTRY: xConfigItem->MoveResultSet(1); break; + case FN_MAILMERGE_PREV_ENTRY: xConfigItem->MoveResultSet(nPos - 1); break; + case FN_MAILMERGE_NEXT_ENTRY: xConfigItem->MoveResultSet(nPos + 1); break; + case FN_MAILMERGE_LAST_ENTRY: xConfigItem->MoveResultSet(-1); break; + case FN_MAILMERGE_CURRENT_ENTRY: /* don't move the result set, just update the document */ break; + default: break; + } + + // now the record has to be merged into the source document + // TODO can we re-use PerformMailMerge() here somehow? + const SwDBData& rDBData = xConfigItem->GetCurrentDBData(); + uno::Sequence<uno::Any> vSelection({ uno::makeAny(xConfigItem->GetResultSetPosition()) }); + svx::ODataAccessDescriptor aDescriptor(::comphelper::InitPropertySequence({ + {"Selection", uno::makeAny(vSelection)}, + {"DataSourceName", uno::makeAny(rDBData.sDataSource)}, + {"Command", uno::makeAny(rDBData.sCommand)}, + {"CommandType", uno::makeAny(rDBData.nCommandType)}, + {"ActiveConnection", uno::makeAny(xConfigItem->GetConnection().getTyped())}, + {"Filter", uno::makeAny(xConfigItem->GetFilter())}, + {"Cursor", uno::makeAny(xConfigItem->GetResultSet())} + })); + + SwWrtShell& rSh = pView->GetWrtShell(); + SwMergeDescriptor aMergeDesc(DBMGR_MERGE, rSh, aDescriptor); + rSh.GetDBManager()->Merge(aMergeDesc); + + // update enabled / disabled status of the buttons in the toolbar + SfxBindings& rBindings = rSh.GetView().GetViewFrame()->GetBindings(); + rBindings.Invalidate(FN_MAILMERGE_FIRST_ENTRY); + rBindings.Invalidate(FN_MAILMERGE_PREV_ENTRY); + rBindings.Invalidate(FN_MAILMERGE_NEXT_ENTRY); + rBindings.Invalidate(FN_MAILMERGE_LAST_ENTRY); + rBindings.Invalidate(FN_MAILMERGE_CURRENT_ENTRY); + rBindings.Invalidate(FN_MAILMERGE_EXCLUDE_ENTRY); + if (!bHadConnection && xConfigItem->GetConnection().is() + && !xConfigItem->GetConnection()->isClosed()) + { + // The connection has been activated. Update controls that were disabled + rBindings.Invalidate(FN_MAILMERGE_CREATE_DOCUMENTS); + rBindings.Invalidate(FN_MAILMERGE_SAVE_DOCUMENTS); + rBindings.Invalidate(FN_MAILMERGE_PRINT_DOCUMENTS); + rBindings.Invalidate(FN_MAILMERGE_EMAIL_DOCUMENTS); + } + rBindings.Update(); + } + break; + case FN_MAILMERGE_CREATE_DOCUMENTS: + { + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = SwDBManager::PerformMailMerge(GetActiveView()); + + if (xConfigItem && xConfigItem->GetTargetView()) + xConfigItem->GetTargetView()->GetViewFrame()->GetFrame().Appear(); + } + break; + case FN_MAILMERGE_SAVE_DOCUMENTS: + case FN_MAILMERGE_PRINT_DOCUMENTS: + case FN_MAILMERGE_EMAIL_DOCUMENTS: + { + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = GetActiveView()->GetMailMergeConfigItem(); + if(!xConfigItem) + return; + xConfigItem->SetTargetView(nullptr); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + switch (nWhich) + { + case FN_MAILMERGE_SAVE_DOCUMENTS: pFact->ExecuteMMResultSaveDialog(rReq.GetFrameWeld()); break; + case FN_MAILMERGE_PRINT_DOCUMENTS: pFact->ExecuteMMResultPrintDialog(rReq.GetFrameWeld()); break; + case FN_MAILMERGE_EMAIL_DOCUMENTS: pFact->ExecuteMMResultEmailDialog(rReq.GetFrameWeld()); break; + } + } + break; +#endif + } +} + +// Catch notifications + +// Catch hint for DocInfo +void SwModule::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if( const SfxEventHint* pEvHint = dynamic_cast<const SfxEventHint*>( &rHint) ) + { + SwDocShell* pDocSh = dynamic_cast<SwDocShell*>( pEvHint->GetObjShell() ); + if( pDocSh ) + { + SwWrtShell* pWrtSh = pDocSh->GetWrtShell(); + switch( pEvHint->GetEventId() ) + { + case SfxEventHintId::LoadFinished: + // if it is a new document created from a template, + // update fixed fields + if (pDocSh->GetMedium()) + { + const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(pDocSh->GetMedium()->GetItemSet(), SID_TEMPLATE, false); + if (pTemplateItem && pTemplateItem->GetValue()) + { + // assume that not calling via SwEditShell::SetFixFields + // is allowed, because the shell hasn't been created yet + assert(!pWrtSh); + pDocSh->GetDoc()->getIDocumentFieldsAccess().SetFixFields(nullptr); + } + } + break; + case SfxEventHintId::CreateDoc: + // Update all FIX-Date/Time fields + if( pWrtSh ) + { + const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(pDocSh->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE, false); + bool bUpdateFields = true; + if( pUpdateDocItem && pUpdateDocItem->GetValue() == document::UpdateDocMode::NO_UPDATE) + bUpdateFields = false; + if(bUpdateFields) + { + comphelper::dispatchCommand(".uno:UpdateInputFields", {}); + + // Are database fields contained? + // Get all used databases for the first time + SwDoc *pDoc = pDocSh->GetDoc(); + std::vector<OUString> aDBNameList; + pDoc->GetAllUsedDB( aDBNameList ); + if(!aDBNameList.empty()) + { // Open database beamer + ShowDBObj(pWrtSh->GetView(), pDoc->GetDBData()); + } + } + } + break; + default: break; + } + } + } + else if(const SfxItemSetHint* pSfxItemSetHint = dynamic_cast<const SfxItemSetHint*>(&rHint)) + { + if( SfxItemState::SET == pSfxItemSetHint->GetItemSet().GetItemState(SID_ATTR_PATHNAME)) + { + ::GetGlossaries()->UpdateGlosPath( false ); + SwGlossaryList* pList = ::GetGlossaryList(); + if(pList->IsActive()) + pList->Update(); + } + } + else + { + if (rHint.GetId() == SfxHintId::Deinitializing) + { + m_pWebUsrPref.reset(); + m_pUsrPref.reset(); + m_pModuleConfig.reset(); + m_pPrintOptions.reset(); + m_pWebPrintOptions.reset(); + m_pChapterNumRules.reset(); + m_pStdFontConfig.reset(); + m_pNavigationConfig.reset(); + m_pToolbarConfig.reset(); + m_pWebToolbarConfig.reset(); + m_pDBConfig.reset(); + if( m_pColorConfig ) + { + m_pColorConfig->RemoveListener(this); + m_pColorConfig.reset(); + } + if( m_pAccessibilityOptions ) + { + m_pAccessibilityOptions->RemoveListener(this); + m_pAccessibilityOptions.reset(); + } + if( m_pCTLOptions ) + { + m_pCTLOptions->RemoveListener(this); + m_pCTLOptions.reset(); + } + if( m_pUserOptions ) + { + m_pUserOptions->RemoveListener(this); + m_pUserOptions.reset(); + } + } + } +} + +void SwModule::ConfigurationChanged( utl::ConfigurationBroadcaster* pBrdCst, ConfigurationHints ) +{ + if( pBrdCst == m_pUserOptions.get() ) + { + m_bAuthorInitialised = false; + } + else if ( pBrdCst == m_pColorConfig.get() || pBrdCst == m_pAccessibilityOptions.get() ) + { + bool bAccessibility = false; + if( pBrdCst == m_pColorConfig.get() ) + SwViewOption::ApplyColorConfigValues(*m_pColorConfig); + else + bAccessibility = true; + + //invalidate all edit windows + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while(pViewShell) + { + if(pViewShell->GetWindow()) + { + if(dynamic_cast< const SwView *>( pViewShell ) != nullptr || + dynamic_cast< const SwPagePreview *>( pViewShell ) != nullptr || + dynamic_cast< const SwSrcView *>( pViewShell ) != nullptr) + { + if(bAccessibility) + { + if(dynamic_cast< const SwView *>( pViewShell ) != nullptr) + static_cast<SwView*>(pViewShell)->ApplyAccessibilityOptions(*m_pAccessibilityOptions); + else if(dynamic_cast< const SwPagePreview *>( pViewShell ) != nullptr) + static_cast<SwPagePreview*>(pViewShell)->ApplyAccessibilityOptions(*m_pAccessibilityOptions); + } + pViewShell->GetWindow()->Invalidate(); + } + } + pViewShell = SfxViewShell::GetNext( *pViewShell ); + } + } + else if( pBrdCst == m_pCTLOptions.get() ) + { + const SfxObjectShell* pObjSh = SfxObjectShell::GetFirst(); + while( pObjSh ) + { + if( auto pDocShell = dynamic_cast<const SwDocShell*>(pObjSh) ) + { + SwDoc* pDoc = const_cast<SwDocShell*>(pDocShell)->GetDoc(); + SwViewShell* pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(); + if ( pVSh ) + pVSh->ChgNumberDigits(); + } + pObjSh = SfxObjectShell::GetNext(*pObjSh); + } + } + +} + +SwDBConfig* SwModule::GetDBConfig() +{ + if(!m_pDBConfig) + m_pDBConfig.reset(new SwDBConfig); + return m_pDBConfig.get(); +} + +svtools::ColorConfig& SwModule::GetColorConfig() +{ + if(!m_pColorConfig) + { + m_pColorConfig.reset(new svtools::ColorConfig); + SwViewOption::ApplyColorConfigValues(*m_pColorConfig); + m_pColorConfig->AddListener(this); + } + return *m_pColorConfig; +} + +SvtAccessibilityOptions& SwModule::GetAccessibilityOptions() +{ + if(!m_pAccessibilityOptions) + { + m_pAccessibilityOptions.reset(new SvtAccessibilityOptions); + m_pAccessibilityOptions->AddListener(this); + } + return *m_pAccessibilityOptions; +} + +SvtCTLOptions& SwModule::GetCTLOptions() +{ + if(!m_pCTLOptions) + { + m_pCTLOptions.reset(new SvtCTLOptions); + m_pCTLOptions->AddListener(this); + } + return *m_pCTLOptions; +} + +SvtUserOptions& SwModule::GetUserOptions() +{ + if(!m_pUserOptions) + { + m_pUserOptions.reset(new SvtUserOptions); + m_pUserOptions->AddListener(this); + } + return *m_pUserOptions; +} + +const SwMasterUsrPref *SwModule::GetUsrPref(bool bWeb) const +{ + SwModule* pNonConstModule = const_cast<SwModule*>(this); + if(bWeb && !m_pWebUsrPref) + { + // The SpellChecker is needed in SwMasterUsrPref's Load, but it must not + // be created there #58256# + pNonConstModule->m_pWebUsrPref.reset(new SwMasterUsrPref(true)); + } + else if(!bWeb && !m_pUsrPref) + { + pNonConstModule->m_pUsrPref.reset(new SwMasterUsrPref(false)); + } + return bWeb ? m_pWebUsrPref.get() : m_pUsrPref.get(); +} + +void NewXForms( SfxRequest& rReq ) +{ + // copied & excerpted from SwModule::InsertLab(..) + + // create new document + SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::STANDARD) ); + xDocSh->DoInitNew(); + + // initialize XForms + static_cast<SwDocShell*>( &xDocSh )->GetDoc()->initXForms( true ); + + // load document into frame + SfxViewFrame::DisplayNewDocument( *xDocSh, rReq ); + + // set return value + rReq.SetReturnValue( SfxVoidItem( rReq.GetSlot() ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/applab.cxx b/sw/source/uibase/app/applab.cxx new file mode 100644 index 000000000..c562f3450 --- /dev/null +++ b/sw/source/uibase/app/applab.cxx @@ -0,0 +1,393 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/string.hxx> +#include <o3tl/deleter.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/request.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/pbinitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <fmthdft.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <swwait.hxx> +#include <gloshdl.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <fldbas.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <dbmgr.hxx> +#include <fldmgr.hxx> +#include <labimg.hxx> +#include <section.hxx> +#include <pagedesc.hxx> +#include <poolfmt.hxx> + +#include <strings.hrc> +#include <swabstdlg.hxx> + +#include <IDocumentDeviceAccess.hxx> + +#include "appenv.hxx" +#include <memory> + +using namespace ::com::sun::star; + +const char MASTER_LABEL[] = "MasterLabel"; + +static const SwFrameFormat *lcl_InsertBCText( SwWrtShell& rSh, const SwLabItem& rItem, + SwFrameFormat &rFormat, + sal_uInt16 nCol, sal_uInt16 nRow ) +{ + SfxItemSet aSet( + rSh.GetAttrPool(), svl::Items<RES_VERT_ORIENT, RES_ANCHOR>{}); + sal_uInt16 nPhyPageNum, nVirtPageNum; + rSh.GetPageNum( nPhyPageNum, nVirtPageNum ); + + //anchor frame to page + aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, nPhyPageNum ) ); + aSet.Put( SwFormatHoriOrient( rItem.m_lLeft + static_cast<SwTwips>(nCol) * rItem.m_lHDist, + text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ) ); + aSet.Put( SwFormatVertOrient( rItem.m_lUpper + static_cast<SwTwips>(nRow) * rItem.m_lVDist, + text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ) ); + const SwFrameFormat *pFormat = rSh.NewFlyFrame(aSet, true, &rFormat ); // Insert Fly + OSL_ENSURE( pFormat, "Fly not inserted" ); + + rSh.UnSelectFrame(); //Frame was selected automatically + + rSh.SetTextFormatColl( rSh.GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + + if(!rItem.m_bSynchron || !(nCol|nRow)) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossarySetActGroup fnSetActGroup = pFact->SetGlossaryActGroupFunc(); + if ( fnSetActGroup ) + (*fnSetActGroup)( rItem.m_sGlossaryGroup ); + SwGlossaryHdl* pGlosHdl = rSh.GetView().GetGlosHdl(); + pGlosHdl->SetCurGroup(rItem.m_sGlossaryGroup, true); + pGlosHdl->InsertGlossary( rItem.m_sGlossaryBlockName ); + } + + return pFormat; +} + +static const SwFrameFormat *lcl_InsertLabText( SwWrtShell& rSh, const SwLabItem& rItem, + SwFrameFormat &rFormat, SwFieldMgr& rFieldMgr, + sal_uInt16 nCol, sal_uInt16 nRow, bool bLast ) +{ + SfxItemSet aSet( + rSh.GetAttrPool(), svl::Items<RES_VERT_ORIENT, RES_ANCHOR>{}); + sal_uInt16 nPhyPageNum, nVirtPageNum; + rSh.GetPageNum( nPhyPageNum, nVirtPageNum ); + + //anchor frame to page + aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, nPhyPageNum ) ); + aSet.Put( SwFormatHoriOrient( rItem.m_lLeft + static_cast<SwTwips>(nCol) * rItem.m_lHDist, + text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ) ); + aSet.Put( SwFormatVertOrient( rItem.m_lUpper + static_cast<SwTwips>(nRow) * rItem.m_lVDist, + text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ) ); + const SwFrameFormat *pFormat = rSh.NewFlyFrame(aSet, true, &rFormat ); // Insert Fly + OSL_ENSURE( pFormat, "Fly not inserted" ); + + rSh.UnSelectFrame(); //Frame was selected automatically + + rSh.SetTextFormatColl( rSh.GetTextCollFromPool( RES_POOLCOLL_STANDARD ) ); + + // If applicable "next dataset" + OUString sDBName; + if( (!rItem.m_bSynchron || !(nCol|nRow)) && !(sDBName = InsertLabEnvText( rSh, rFieldMgr, rItem.m_aWriting )).isEmpty() && !bLast ) + { + sDBName = comphelper::string::setToken(sDBName, 3, DB_DELIM, "True"); + SwInsertField_Data aData(SwFieldTypesEnum::DatabaseNextSet, 0, sDBName, OUString(), 0, &rSh); + rFieldMgr.InsertField( aData ); + } + + return pFormat; +} + +void SwModule::InsertLab(SfxRequest& rReq, bool bLabel) +{ + static sal_uInt16 nLabelTitleNo = 0; + static sal_uInt16 nBCTitleNo = 0; + +#if HAVE_FEATURE_DBCONNECTIVITY + // Create DB-Manager + std::unique_ptr<SwDBManager, o3tl::default_delete<SwDBManager>> pDBManager(new SwDBManager(nullptr)); +#endif + + // Read SwLabItem from Config + SwLabCfgItem aLabCfg(bLabel); + + // Move up Dialog + SfxItemSet aSet( GetPool(), svl::Items<FN_LABEL, FN_LABEL>{} ); + aSet.Put( aLabCfg.GetItem() ); + + SwAbstractDialogFactory* pDialogFactory = SwAbstractDialogFactory::Create(); + + ScopedVclPtr<AbstractSwLabDlg> pDlg(pDialogFactory->CreateSwLabDlg(rReq.GetFrameWeld(), aSet, +#if HAVE_FEATURE_DBCONNECTIVITY + pDBManager.get(), +#else + NULL, +#endif + bLabel)); + + if ( RET_OK != pDlg->Execute() ) + return; + + // Read dialog, store item in config + const SwLabItem& rItem = static_cast<const SwLabItem&>( pDlg-> + GetOutputItemSet()->Get(FN_LABEL)); + aLabCfg.GetItem() = rItem; + aLabCfg.Commit(); + + // Create new document + SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::STANDARD)); + xDocSh->DoInitNew(); + + // Printer + Printer *pPrt = pDlg->GetPrt(); + if (pPrt) + { + SwDocShell *pDocSh = static_cast<SwDocShell*>(&*xDocSh); + pDocSh->getIDocumentDeviceAccess().setJobsetup(pPrt->GetJobSetup()); + } + + SfxViewFrame* pViewFrame = SfxViewFrame::DisplayNewDocument( *xDocSh, rReq ); + + SwView *pNewView = static_cast<SwView*>( pViewFrame->GetViewShell()); + pNewView->AttrChangedNotify(nullptr);// So that SelectShell is being called. + + // Set document title + OUString aTmp; + if(bLabel) + { + aTmp = SwResId( STR_LAB_TITLE) + + OUString::number(++nLabelTitleNo ); + } + else + { + aTmp = pDlg->GetBusinessCardStr() + + OUString::number( ++nBCTitleNo ); + } + xDocSh->SetTitle( aTmp ); + + pViewFrame->GetFrame().Appear(); + + // Determine Shell + SwWrtShell *pSh = pNewView->GetWrtShellPtr(); + OSL_ENSURE( pSh, "missing WrtShell" ); + + { // block for locks the dispatcher!! + + SwWait aWait( static_cast<SwDocShell&>(*xDocSh), true ); + + SET_CURR_SHELL(pSh); + pSh->SetLabelDoc(rItem.m_bSynchron); + pSh->DoUndo( false ); + pSh->StartAllAction(); + + pSh->SetNewDoc(); // Avoid performance problems + + SwPageDesc aDesc = pSh->GetPageDesc( 0 ); + SwFrameFormat& rFormat = aDesc.GetMaster(); + + // Borders + SvxLRSpaceItem aLRMargin( RES_LR_SPACE ); + SvxULSpaceItem aULMargin( RES_UL_SPACE ); + aLRMargin.SetLeft (static_cast<sal_uInt16>(rItem.m_lLeft) ); + aULMargin.SetUpper(static_cast<sal_uInt16>(rItem.m_lUpper)); + aLRMargin.SetRight( 0 ); + aULMargin.SetLower( 0 ); + rFormat.SetFormatAttr(aLRMargin); + rFormat.SetFormatAttr(aULMargin); + + // Header and footer + rFormat.SetFormatAttr(SwFormatHeader(false)); + aDesc.ChgHeaderShare(false); + rFormat.SetFormatAttr(SwFormatFooter(false)); + aDesc.ChgFooterShare(false); + + aDesc.SetUseOn(UseOnPage::All); // Site numbering + + // Set page size + long lPgWidth, lPgHeight; + lPgWidth = std::max<sal_Int32>(rItem.m_lPWidth, MINLAY); + lPgHeight = std::max<sal_Int32>(rItem.m_lPHeight, MINLAY); + rFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed, lPgWidth, lPgHeight )); + // Numbering type + SvxNumberType aType; + aType.SetNumberingType(SVX_NUM_NUMBER_NONE); + aDesc.SetNumType( aType ); + + // Followup template + const SwPageDesc &rFollow = pSh->GetPageDesc( pSh->GetCurPageDesc() ); + aDesc.SetFollow( &rFollow ); + + pPrt = pSh->getIDocumentDeviceAccess().getPrinter( true ); + SvxPaperBinItem aItem( RES_PAPER_BIN ); + aItem.SetValue(static_cast<sal_Int8>(pPrt->GetPaperBin())); + rFormat.SetFormatAttr(aItem); + + // Determine orientation of the resulting page + aDesc.SetLandscape(rItem.m_lPWidth > rItem.m_lPHeight); + + pSh->ChgPageDesc( 0, aDesc ); + + // Insert frame + std::unique_ptr<SwFieldMgr> pFieldMgr(new SwFieldMgr); + pFieldMgr->SetEvalExpFields(false); + + // Prepare border template + SwFrameFormat* pFormat = pSh->GetFrameFormatFromPool( RES_POOLFRM_LABEL ); + sal_Int32 iResultWidth = rItem.m_lLeft + (rItem.m_nCols - 1) * rItem.m_lHDist + rItem.m_lWidth - rItem.m_lPWidth; + sal_Int32 iResultHeight = rItem.m_lUpper + (rItem.m_nRows - 1) * rItem.m_lVDist + rItem.m_lHeight - rItem.m_lPHeight; + sal_Int32 iWidth = (iResultWidth > 0 ? rItem.m_lWidth - (iResultWidth / rItem.m_nCols) - 1 : rItem.m_lWidth); + sal_Int32 iHeight = (iResultHeight > 0 ? rItem.m_lHeight - (iResultHeight / rItem.m_nRows) - 1 : rItem.m_lHeight); + SwFormatFrameSize aFrameSize( SwFrameSize::Fixed, iWidth, iHeight ); + pFormat->SetFormatAttr( aFrameSize ); + + //frame represents label itself, no border space + SvxULSpaceItem aFrameNoULSpace( 0, 0, RES_UL_SPACE ); + SvxLRSpaceItem aFrameNoLRSpace( 0, 0, 0, 0, RES_LR_SPACE ); + pFormat->SetFormatAttr( aFrameNoULSpace ); + pFormat->SetFormatAttr( aFrameNoLRSpace ); + + const SwFrameFormat *pFirstFlyFormat = nullptr; + if ( rItem.m_bPage ) + { + SwFormatVertOrient aFrameVertOrient( pFormat->GetVertOrient() ); + aFrameVertOrient.SetVertOrient( text::VertOrientation::TOP ); + pFormat->SetFormatAttr(aFrameVertOrient); + + for ( sal_Int32 i = 0; i < rItem.m_nRows; ++i ) + { + for ( sal_Int32 j = 0; j < rItem.m_nCols; ++j ) + { + pSh->Push(); + const SwFrameFormat *pTmp = ( bLabel ? + lcl_InsertLabText( *pSh, rItem, *pFormat, *pFieldMgr, j, i, + i == rItem.m_nRows - 1 && j == rItem.m_nCols - 1 ) : + lcl_InsertBCText( *pSh, rItem, *pFormat, j, i ) ); + if (!(i|j)) + { + pFirstFlyFormat = pTmp; + + if (rItem.m_bSynchron) + { + // if there is no content in the fly then + // don't leave the fly!!! + pSh->Push(); + pSh->StartOfSection(); + bool bInFly = nullptr != pSh->WizardGetFly(); + pSh->Pop(bInFly ? SwCursorShell::PopMode::DeleteStack : SwCursorShell::PopMode::DeleteCurrent); + + if( bInFly ) + pSh->EndOfSection(true); // select all content + // in the fly + else + pSh->SetMark(); // set only the mark + + SwSectionData aSect(SectionType::Content, MASTER_LABEL); + pSh->InsertSection(aSect); + } + } + else if (rItem.m_bSynchron) + { + SwSectionData aSect(SectionType::FileLink, + pSh->GetUniqueSectionName()); + OUString sLinkName = + OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + + MASTER_LABEL; + aSect.SetLinkFileName(sLinkName); + aSect.SetProtectFlag(true); + pSh->Insert("."); // Dummytext to allocate the Section + pSh->StartOfSection(); + pSh->EndOfSection(true); // Select everything in the frame + pSh->InsertSection(aSect); + } + pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + } + else + { + pFirstFlyFormat = bLabel ? + lcl_InsertLabText( *pSh, rItem, *pFormat, *pFieldMgr, + static_cast< sal_uInt16 >(rItem.m_nCol - 1), + static_cast< sal_uInt16 >(rItem.m_nRow - 1), true ) : + lcl_InsertBCText(*pSh, rItem, *pFormat, + static_cast< sal_uInt16 >(rItem.m_nCol - 1), + static_cast< sal_uInt16 >(rItem.m_nRow - 1)); + } + + //fill the user fields + if(!bLabel) + { + uno::Reference< frame::XModel > xModel = pSh->GetView().GetDocShell()->GetBaseModel(); + OSL_ENSURE(pDialogFactory, "SwAbstractDialogFactory fail!"); + SwLabDlgMethod SwLabDlgUpdateFieldInformation = pDialogFactory->GetSwLabDlgStaticMethod (); + SwLabDlgUpdateFieldInformation(xModel, rItem); + } + + pFieldMgr->SetEvalExpFields(true); + pFieldMgr->EvalExpFields(pSh); + + pFieldMgr.reset(); + + if (pFirstFlyFormat) + pSh->GotoFly(pFirstFlyFormat->GetName(), FLYCNTTYPE_ALL, false); + + if (pSh->IsAnyDatabaseFieldInDoc()) + pSh->GetView().ShowUIElement("private:resource/toolbar/mailmerge"); + + pSh->EndAllAction(); + pSh->DoUndo(); + } + + if( rItem.m_aWriting.indexOf( '<' ) >= 0 ) + { + // Open database browser on recently used database + ShowDBObj( *pNewView, pSh->GetDBData() ); + } + + if( rItem.m_bSynchron ) + { + SfxDispatcher* pDisp = pViewFrame->GetDispatcher(); + assert(pDisp && "No dispatcher in frame?"); + pDisp->Execute(FN_SYNC_LABELS, SfxCallMode::ASYNCHRON); + } + rReq.SetReturnValue(SfxVoidItem(bLabel ? FN_LABEL : FN_BUSINESS_CARD)); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/appopt.cxx b/sw/source/uibase/app/appopt.cxx new file mode 100644 index 000000000..dcc59d8d0 --- /dev/null +++ b/sw/source/uibase/app/appopt.cxx @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmdid.h> + +#include <com/sun/star/i18n/ScriptType.hpp> + +#include <sal/log.hxx> +#include <hintids.hxx> +#include <svl/eitem.hxx> +#include <sfx2/app.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/htmlmode.hxx> +#include <sfx2/bindings.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/tstpitem.hxx> +#include <svx/optgrid.hxx> +#include <svx/dialogs.hrc> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <fontcfg.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <doc.hxx> +#include <wrtsh.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <uitool.hxx> +#include <wview.hxx> +#include <cfgitems.hxx> +#include <prtopt.hxx> +#include <pview.hxx> +#include <usrpref.hxx> +#include <uiitems.hxx> +#include <editeng/langitem.hxx> +#include <unotools/lingucfg.hxx> +#include <globals.hrc> +#include <swabstdlg.hxx> +#include <swwrtshitem.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +std::unique_ptr<SfxItemSet> SwModule::CreateItemSet( sal_uInt16 nId ) +{ + bool bTextDialog = (nId == SID_SW_EDITOPTIONS); + + // the options for the Web- and Textdialog are put together here + SwViewOption aViewOpt = *GetUsrPref(!bTextDialog); + SwMasterUsrPref* pPref = bTextDialog ? m_pUsrPref.get() : m_pWebUsrPref.get(); + // no MakeUsrPref, because only options from textdoks can be used here + SwView* pAppView = GetView(); + if(pAppView && pAppView->GetViewFrame() != SfxViewFrame::Current()) + pAppView = nullptr; + if(pAppView) + { + bool bWebView = dynamic_cast<SwWebView*>( pAppView ) != nullptr; + // if Text then no WebView and vice versa + if (bWebView != bTextDialog) + { + aViewOpt = *pAppView->GetWrtShell().GetViewOptions(); + } + else + pAppView = nullptr; // with View, there's nothing to win here + } + + // Options/Edit + auto pRet = std::make_unique<SfxItemSet>( + GetPool(), + svl::Items< + RES_BACKGROUND, RES_BACKGROUND, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_PRINTPREVIEW, SID_PRINTPREVIEW, + SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS, + SID_HTML_MODE, SID_HTML_MODE, + SID_ATTR_CHAR_CJK_LANGUAGE, SID_ATTR_CHAR_CJK_LANGUAGE, + SID_ATTR_CHAR_CTL_LANGUAGE, SID_ATTR_CHAR_CTL_LANGUAGE, + SID_ATTR_LANGUAGE, SID_ATTR_METRIC, + SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP, + SID_ATTR_APPLYCHARUNIT, SID_ATTR_APPLYCHARUNIT, + FN_HSCROLL_METRIC, FN_VSCROLL_METRIC, + FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER, + FN_PARAM_DOCDISP, FN_PARAM_ELEM, + FN_PARAM_PRINTER, FN_PARAM_STDFONTS, + FN_PARAM_WRTSHELL, FN_PARAM_WRTSHELL, + FN_PARAM_SHADOWCURSOR, FN_PARAM_SHADOWCURSOR, + FN_PARAM_CRSR_IN_PROTECTED, FN_PARAM_CRSR_IN_PROTECTED>{}); + + pRet->Put( SwDocDisplayItem( aViewOpt ) ); + pRet->Put( SwElemItem( aViewOpt ) ); + if( bTextDialog ) + { + pRet->Put( SwShadowCursorItem( aViewOpt )); + pRet->Put( SfxBoolItem(FN_PARAM_CRSR_IN_PROTECTED, aViewOpt.IsCursorInProtectedArea())); + } + + if( pAppView ) + { + SwWrtShell& rWrtShell = pAppView->GetWrtShell(); + + SfxPrinter* pPrt = rWrtShell.getIDocumentDeviceAccess().getPrinter( false ); + if( pPrt ) + pRet->Put(SwPtrItem(FN_PARAM_PRINTER, pPrt)); + pRet->Put(SwPtrItem(FN_PARAM_WRTSHELL, &rWrtShell)); + + pRet->Put(rWrtShell.GetDefault(RES_CHRATR_LANGUAGE).CloneSetWhich(SID_ATTR_LANGUAGE)); + pRet->Put(rWrtShell.GetDefault(RES_CHRATR_CJK_LANGUAGE).CloneSetWhich(SID_ATTR_CHAR_CJK_LANGUAGE)); + pRet->Put(rWrtShell.GetDefault(RES_CHRATR_CTL_LANGUAGE).CloneSetWhich(SID_ATTR_CHAR_CTL_LANGUAGE)); + } + else + { + SvtLinguConfig aLinguCfg; + css::lang::Locale aLocale; + LanguageType nLang; + + using namespace ::com::sun::star::i18n::ScriptType; + + Any aLang = aLinguCfg.GetProperty("DefaultLocale"); + aLang >>= aLocale; + nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aLocale, false), LATIN); + pRet->Put(SvxLanguageItem(nLang, SID_ATTR_LANGUAGE)); + + aLang = aLinguCfg.GetProperty("DefaultLocale_CJK"); + aLang >>= aLocale; + nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aLocale, false), ASIAN); + pRet->Put(SvxLanguageItem(nLang, SID_ATTR_CHAR_CJK_LANGUAGE)); + + aLang = aLinguCfg.GetProperty("DefaultLocale_CTL"); + aLang >>= aLocale; + nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType( aLocale, false), COMPLEX); + pRet->Put(SvxLanguageItem(nLang, SID_ATTR_CHAR_CTL_LANGUAGE)); + } + if(bTextDialog) + pRet->Put(SwPtrItem(FN_PARAM_STDFONTS, GetStdFontConfig())); + if( dynamic_cast<SwPagePreview*>( SfxViewShell::Current())!=nullptr ) + { + SfxBoolItem aBool(SfxBoolItem(SID_PRINTPREVIEW, true)); + pRet->Put(aBool); + } + + FieldUnit eUnit = pPref->GetHScrollMetric(); + if(pAppView) + pAppView->GetHRulerMetric(eUnit); + pRet->Put(SfxUInt16Item( FN_HSCROLL_METRIC, static_cast< sal_uInt16 >(eUnit))); + + eUnit = pPref->GetVScrollMetric(); + if(pAppView) + pAppView->GetVRulerMetric(eUnit); + pRet->Put(SfxUInt16Item( FN_VSCROLL_METRIC, static_cast< sal_uInt16 >(eUnit) )); + pRet->Put(SfxUInt16Item( SID_ATTR_METRIC, static_cast< sal_uInt16 >(pPref->GetMetric()) )); + pRet->Put(SfxBoolItem(SID_ATTR_APPLYCHARUNIT, pPref->IsApplyCharUnit())); + if(bTextDialog) + { + if(pAppView) + { + const SvxTabStopItem& rDefTabs = + pAppView->GetWrtShell().GetDefault(RES_PARATR_TABSTOP); + pRet->Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP, static_cast<sal_uInt16>(::GetTabDist(rDefTabs)))); + } + else + pRet->Put(SfxUInt16Item( SID_ATTR_DEFTABSTOP, static_cast<sal_uInt16>(convertMm100ToTwip(pPref->GetDefTabInMm100())))); + } + + // Options for GridTabPage + SvxGridItem aGridItem( SID_ATTR_GRID_OPTIONS); + + aGridItem.SetUseGridSnap( aViewOpt.IsSnap()); + aGridItem.SetSynchronize( aViewOpt.IsSynchronize()); + aGridItem.SetGridVisible( aViewOpt.IsGridVisible()); + + const Size& rSnapSize = aViewOpt.GetSnapSize(); + aGridItem.SetFieldDrawX( static_cast<sal_uInt16>(rSnapSize.Width() )); + aGridItem.SetFieldDrawY( static_cast<sal_uInt16>(rSnapSize.Height())); + + aGridItem.SetFieldDivisionX( aViewOpt.GetDivisionX()); + aGridItem.SetFieldDivisionY( aViewOpt.GetDivisionY()); + + pRet->Put(aGridItem); + + // Options for PrintTabPage + const SwPrintData* pOpt = GetPrtOptions(!bTextDialog); + SwAddPrinterItem aAddPrinterItem(*pOpt ); + pRet->Put(aAddPrinterItem); + + // Options for Web + if(!bTextDialog) + { + pRet->Put(SvxBrushItem(aViewOpt.GetRetoucheColor(), RES_BACKGROUND)); + pRet->Put(SfxUInt16Item(SID_HTML_MODE, HTMLMODE_ON)); + } + + return pRet; +} + +void SwModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet ) +{ + bool bTextDialog = nId == SID_SW_EDITOPTIONS; + SwView* pAppView = GetView(); + if(pAppView && pAppView->GetViewFrame() != SfxViewFrame::Current()) + pAppView = nullptr; + if(pAppView) + { + // the text dialog mustn't apply data to the web view and vice versa + bool bWebView = dynamic_cast<SwWebView*>( pAppView ) != nullptr; + if(bWebView == bTextDialog) + pAppView = nullptr; + } + + SwViewOption aViewOpt = *GetUsrPref(!bTextDialog); + SwMasterUsrPref* pPref = bTextDialog ? m_pUsrPref.get() : m_pWebUsrPref.get(); + + const SfxPoolItem* pItem; + SfxBindings *pBindings = pAppView ? &pAppView->GetViewFrame()->GetBindings() + : nullptr; + + // Interpret the page Documentview + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_DOCDISP, false, &pItem )) + { + const SwDocDisplayItem* pDocDispItem = static_cast<const SwDocDisplayItem*>(pItem); + + if(!aViewOpt.IsViewMetaChars()) + { + if( (!aViewOpt.IsTab( true ) && pDocDispItem->bTab) || + (!aViewOpt.IsBlank( true ) && pDocDispItem->bSpace) || + (!aViewOpt.IsShowBookmarks(true) && pDocDispItem->bBookmarks) || + (!aViewOpt.IsParagraph( true ) && pDocDispItem->bParagraphEnd) || + (!aViewOpt.IsLineBreak( true ) && pDocDispItem->bManualBreak) ) + { + aViewOpt.SetViewMetaChars(true); + if(pBindings) + pBindings->Invalidate(FN_VIEW_META_CHARS); + } + + } + pDocDispItem->FillViewOptions( aViewOpt ); + if(pBindings) + { + pBindings->Invalidate(FN_VIEW_GRAPHIC); + pBindings->Invalidate(FN_VIEW_HIDDEN_PARA); + } + } + + // Elements - interpret Item + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_ELEM, false, &pItem ) ) + { + const SwElemItem* pElemItem = static_cast<const SwElemItem*>(pItem); + pElemItem->FillViewOptions( aViewOpt ); + + } + + if( SfxItemState::SET == rSet.GetItemState(SID_ATTR_METRIC, false, &pItem ) ) + { + SfxGetpApp()->SetOptions(rSet); + PutItem(*pItem); + const SfxUInt16Item* pMetricItem = static_cast<const SfxUInt16Item*>(pItem); + ::SetDfltMetric(static_cast<FieldUnit>(pMetricItem->GetValue()), !bTextDialog); + } + if( SfxItemState::SET == rSet.GetItemState(SID_ATTR_APPLYCHARUNIT, + false, &pItem ) ) + { + SfxGetpApp()->SetOptions(rSet); + const SfxBoolItem* pCharItem = static_cast<const SfxBoolItem*>(pItem); + ::SetApplyCharUnit(pCharItem->GetValue(), !bTextDialog); + } + + if( SfxItemState::SET == rSet.GetItemState(FN_HSCROLL_METRIC, false, &pItem ) ) + { + const SfxUInt16Item* pMetricItem = static_cast<const SfxUInt16Item*>(pItem); + FieldUnit eUnit = static_cast<FieldUnit>(pMetricItem->GetValue()); + pPref->SetHScrollMetric(eUnit); + if(pAppView) + pAppView->ChangeTabMetric(eUnit); + } + + if( SfxItemState::SET == rSet.GetItemState(FN_VSCROLL_METRIC, false, &pItem ) ) + { + const SfxUInt16Item* pMetricItem = static_cast<const SfxUInt16Item*>(pItem); + FieldUnit eUnit = static_cast<FieldUnit>(pMetricItem->GetValue()); + pPref->SetVScrollMetric(eUnit); + if(pAppView) + pAppView->ChangeVRulerMetric(eUnit); + } + + if( SfxItemState::SET == rSet.GetItemState(SID_ATTR_DEFTABSTOP, false, &pItem ) ) + { + sal_uInt16 nTabDist = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + pPref->SetDefTabInMm100(convertTwipToMm100(nTabDist)); + if(pAppView) + { + SvxTabStopItem aDefTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + MakeDefTabs( nTabDist, aDefTabs ); + pAppView->GetWrtShell().SetDefault( aDefTabs ); + } + } + + // Background only in WebDialog + if(SfxItemState::SET == rSet.GetItemState(RES_BACKGROUND)) + { + const SvxBrushItem& rBrushItem = rSet.Get(RES_BACKGROUND); + aViewOpt.SetRetoucheColor( rBrushItem.GetColor() ); + } + + // Interpret page Grid Settings + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_GRID_OPTIONS, false, &pItem )) + { + const SvxGridItem* pGridItem = static_cast<const SvxGridItem*>(pItem); + + aViewOpt.SetSnap( pGridItem->GetUseGridSnap() ); + aViewOpt.SetSynchronize(pGridItem->GetSynchronize()); + if( aViewOpt.IsGridVisible() != pGridItem->GetGridVisible() ) + aViewOpt.SetGridVisible( pGridItem->GetGridVisible()); + Size aSize( pGridItem->GetFieldDrawX(), pGridItem->GetFieldDrawY() ); + if( aViewOpt.GetSnapSize() != aSize ) + aViewOpt.SetSnapSize( aSize ); + short nDiv = static_cast<short>(pGridItem->GetFieldDivisionX()) ; + if( aViewOpt.GetDivisionX() != nDiv ) + aViewOpt.SetDivisionX( nDiv ); + nDiv = static_cast<short>(pGridItem->GetFieldDivisionY()); + if( aViewOpt.GetDivisionY() != nDiv ) + aViewOpt.SetDivisionY( nDiv ); + + if(pBindings) + { + pBindings->Invalidate(SID_GRID_VISIBLE); + pBindings->Invalidate(SID_GRID_USE); + } + } + + // Interpret Writer Printer Options + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_ADDPRINTER, false, &pItem )) + { + SwPrintOptions* pOpt = GetPrtOptions(!bTextDialog); + if (pOpt) + { + const SwAddPrinterItem* pAddPrinterAttr = static_cast<const SwAddPrinterItem*>(pItem); + *pOpt = *pAddPrinterAttr; + } + + } + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_SHADOWCURSOR, false, &pItem )) + { + static_cast<const SwShadowCursorItem*>(pItem)->FillViewOptions( aViewOpt ); + if(pBindings) + pBindings->Invalidate(FN_SHADOWCURSOR); + } + + if( pAppView ) + { + SwWrtShell &rWrtSh = pAppView->GetWrtShell(); + const bool bAlignFormulas = rWrtSh.GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ); + pPref->SetAlignMathObjectsToBaseline( bAlignFormulas ); + + // don't align formulas in documents that are currently loading + if (bAlignFormulas && !rWrtSh.GetDoc()->IsInReading()) + rWrtSh.AlignAllFormulasToBaseline(); + } + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_CRSR_IN_PROTECTED, false, &pItem )) + { + aViewOpt.SetCursorInProtectedArea(static_cast<const SfxBoolItem*>(pItem)->GetValue()); + } + + // set elements for the current view and shell + ApplyUsrPref( aViewOpt, pAppView, bTextDialog? SvViewOpt::DestText : SvViewOpt::DestWeb); +} + +std::unique_ptr<SfxTabPage> SwModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) +{ + std::unique_ptr<SfxTabPage> xRet; + SfxAllItemSet aSet(*(rSet.GetPool())); + switch( nId ) + { + case RID_SW_TP_CONTENT_OPT: + case RID_SW_TP_HTML_CONTENT_OPT: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + break; + } + case RID_SW_TP_HTML_OPTGRID_PAGE: + case RID_SVXPAGE_GRID: + xRet = SvxGridTabPage::Create(pPage, pController, rSet); + break; + + case RID_SW_TP_STD_FONT: + case RID_SW_TP_STD_FONT_CJK: + case RID_SW_TP_STD_FONT_CTL: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if(RID_SW_TP_STD_FONT != nId) + { + aSet.Put (SfxUInt16Item(SID_FONTMODE_TYPE, RID_SW_TP_STD_FONT_CJK == nId ? FONT_GROUP_CJK : FONT_GROUP_CTL)); + xRet->PageCreated(aSet); + } + } + break; + case RID_SW_TP_HTML_OPTPRINT_PAGE: + case RID_SW_TP_OPTPRINT_PAGE: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + aSet.Put (SfxBoolItem(SID_FAX_LIST, true)); + xRet->PageCreated(aSet); + } + break; + case RID_SW_TP_HTML_OPTTABLE_PAGE: + case RID_SW_TP_OPTTABLE_PAGE: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + SwView* pCurrView = GetView(); + if(pCurrView) + { + // if text then not WebView and vice versa + bool bWebView = dynamic_cast<SwWebView*>( pCurrView ) != nullptr; + if( (bWebView && RID_SW_TP_HTML_OPTTABLE_PAGE == nId) || + (!bWebView && RID_SW_TP_HTML_OPTTABLE_PAGE != nId) ) + { + aSet.Put (SwWrtShellItem(pCurrView->GetWrtShellPtr())); + xRet->PageCreated(aSet); + } + } + } + break; + case RID_SW_TP_OPTSHDWCRSR: + case RID_SW_TP_HTML_OPTSHDWCRSR: + case RID_SW_TP_REDLINE_OPT: + case RID_SW_TP_COMPARISON_OPT: + case RID_SW_TP_OPTLOAD_PAGE: + case RID_SW_TP_OPTCOMPATIBILITY_PAGE: + case RID_SW_TP_MAILCONFIG: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if (nId == RID_SW_TP_OPTSHDWCRSR || nId == RID_SW_TP_HTML_OPTSHDWCRSR) + { + SwView* pCurrView = GetView(); + if(pCurrView) + { + aSet.Put( SwWrtShellItem( pCurrView->GetWrtShellPtr() ) ); + xRet->PageCreated(aSet); + } + } + } + break; + case RID_SW_TP_OPTTEST_PAGE: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( nId ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + break; + } + case RID_SW_TP_BACKGROUND: + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + xRet->PageCreated( rSet ); + break; + } + case RID_SW_TP_OPTCAPTION_PAGE: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SW_TP_OPTCAPTION_PAGE ); + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + } + + if(!xRet) + SAL_WARN( "sw", "SwModule::CreateTabPage(): Unknown tabpage id " << nId ); + return xRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docsh.cxx b/sw/source/uibase/app/docsh.cxx new file mode 100644 index 000000000..aeb522be5 --- /dev/null +++ b/sw/source/uibase/app/docsh.cxx @@ -0,0 +1,1412 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <vcl/weld.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syswin.hxx> +#include <vcl/jobset.hxx> +#include <svl/whiter.hxx> +#include <svl/zforlist.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <svl/PasswordHelper.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/misccfg.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/linkmgr.hxx> +#include <editeng/flstitem.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/classids.hxx> +#include <basic/sbmod.hxx> +#include <fmtfld.hxx> +#include <node.hxx> +#include <swwait.hxx> +#include <printdata.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <PostItMgr.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <viewopt.hxx> +#include <wdocsh.hxx> +#include <swmodule.hxx> +#include <globdoc.hxx> +#include <usrpref.hxx> +#include <shellio.hxx> +#include <docstyle.hxx> +#include <doc.hxx> +#include <docfunc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentState.hxx> +#include <pview.hxx> +#include <srcview.hxx> +#include <ndindex.hxx> +#include <ndole.hxx> +#include <txtftn.hxx> +#include <ftnidx.hxx> +#include <fldbas.hxx> +#include <docary.hxx> +#include <swerror.h> +#include <cmdid.h> +#include <strings.hrc> + +#include <unotools/fltrcfg.hxx> +#include <svtools/htmlcfg.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objface.hxx> + +#define ShellClass_SwDocShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <com/sun/star/document/UpdateDocMode.hpp> + +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <ooo/vba/XSinkCaller.hpp> + +#include <unotextrange.hxx> + +#include <dbmgr.hxx> +#include <iodetect.hxx> + +#include <comphelper/processfactory.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::container; + +SFX_IMPL_SUPERCLASS_INTERFACE(SwDocShell, SfxObjectShell) + +void SwDocShell::InitInterface_Impl() +{ +} + + +SFX_IMPL_OBJECTFACTORY(SwDocShell, SvGlobalName(SO3_SW_CLASSID), "swriter" ) + +bool SwDocShell::InsertGeneratedStream(SfxMedium & rMedium, + uno::Reference<text::XTextRange> const& xInsertPosition) +{ + SwUnoInternalPaM aPam(*GetDoc()); // must have doc since called from SwView + if (!::sw::XTextRangeToSwPaM(aPam, xInsertPosition)) + return false; + // similar to SwView::InsertMedium + SwReaderPtr pReader; + Reader *const pRead = StartConvertFrom(rMedium, pReader, nullptr, &aPam); + if (!pRead) + return false; + ErrCode const nError = pReader->Read(*pRead); + return ERRCODE_NONE == nError; +} + +// Prepare loading +Reader* SwDocShell::StartConvertFrom(SfxMedium& rMedium, SwReaderPtr& rpRdr, + SwCursorShell const *pCursorShell, + SwPaM* pPaM ) +{ + bool bAPICall = false; + const SfxPoolItem* pApiItem; + const SfxItemSet* pMedSet; + if( nullptr != ( pMedSet = rMedium.GetItemSet() ) && SfxItemState::SET == + pMedSet->GetItemState( FN_API_CALL, true, &pApiItem ) ) + bAPICall = static_cast<const SfxBoolItem*>(pApiItem)->GetValue(); + + std::shared_ptr<const SfxFilter> pFlt = rMedium.GetFilter(); + if( !pFlt ) + { + if(!bAPICall) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_CANTOPEN))); + xInfoBox->run(); + } + return nullptr; + } + OUString aFileName( rMedium.GetName() ); + Reader* pRead = SwReaderWriter::GetReader( pFlt->GetUserData() ); + if( !pRead ) + return nullptr; + + if( rMedium.IsStorage() + ? SwReaderType::Storage & pRead->GetReaderType() + : SwReaderType::Stream & pRead->GetReaderType() ) + { + if (pPaM) + rpRdr.reset(new SwReader( rMedium, aFileName, *pPaM )); + else if (pCursorShell) + rpRdr.reset(new SwReader( rMedium, aFileName, *pCursorShell->GetCursor() )); + else + rpRdr.reset(new SwReader( rMedium, aFileName, m_xDoc.get() )); + } + else + return nullptr; + + // #i30171# set the UpdateDocMode at the SwDocShell + const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(rMedium.GetItemSet(), SID_UPDATEDOCMODE, false); + m_nUpdateDocMode = pUpdateDocItem ? pUpdateDocItem->GetValue() : document::UpdateDocMode::NO_UPDATE; + + if (!pFlt->GetDefaultTemplate().isEmpty()) + pRead->SetTemplateName( pFlt->GetDefaultTemplate() ); + + if( pRead == ReadAscii && nullptr != rMedium.GetInStream() && + pFlt->GetUserData() == FILTER_TEXT_DLG ) + { + SwAsciiOptions aOpt; + const SfxItemSet* pSet; + const SfxPoolItem* pItem; + if( nullptr != ( pSet = rMedium.GetItemSet() ) && SfxItemState::SET == + pSet->GetItemState( SID_FILE_FILTEROPTIONS, true, &pItem ) ) + aOpt.ReadUserData( static_cast<const SfxStringItem*>(pItem)->GetValue() ); + + pRead->GetReaderOpt().SetASCIIOpts( aOpt ); + } + + return pRead; +} + +// Loading +bool SwDocShell::ConvertFrom( SfxMedium& rMedium ) +{ + SwReaderPtr pRdr; + Reader* pRead = StartConvertFrom(rMedium, pRdr); + if (!pRead) + return false; // #129881# return if no reader is found + tools::SvRef<SotStorage> pStg=pRead->getSotStorageRef(); // #i45333# save sot storage ref in case of recursive calls + + m_xDoc->setDocAccTitle(OUString()); + if (const auto pFrame1 = SfxViewFrame::GetFirst(this)) + { + if (auto pSysWin = pFrame1->GetWindow().GetSystemWindow()) + { + pSysWin->SetAccessibleName(OUString()); + } + } + SwWait aWait( *this, true ); + + // Suppress SfxProgress, when we are Embedded + SW_MOD()->SetEmbeddedLoadSave( + SfxObjectCreateMode::EMBEDDED == GetCreateMode() ); + + pRdr->GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, dynamic_cast< const SwWebDocShell *>( this ) != nullptr); + + // Restore the pool default if reading a saved document. + m_xDoc->RemoveAllFormatLanguageDependencies(); + + ErrCode nErr = pRdr->Read( *pRead ); + + // Maybe put away one old Doc + if (m_xDoc.get() != pRdr->GetDoc()) + { + RemoveLink(); + m_xDoc = pRdr->GetDoc(); + + AddLink(); + + if (!m_xBasePool.is()) + m_xBasePool = new SwDocStyleSheetPool( *m_xDoc, SfxObjectCreateMode::ORGANIZER == GetCreateMode() ); + } + + UpdateFontList(); + InitDrawModelAndDocShell(this, m_xDoc ? m_xDoc->getIDocumentDrawModelAccess().GetDrawModel() : nullptr); + + pRdr.reset(); + + SW_MOD()->SetEmbeddedLoadSave( false ); + + SetError(nErr); + bool bOk = !nErr.IsError(); + + if (bOk && !m_xDoc->IsInLoadAsynchron()) + { + LoadingFinished(); + } + + pRead->setSotStorageRef(pStg); // #i45333# save sot storage ref in case of recursive calls + + return bOk; +} + +// Saving the Default-Format, Stg present +bool SwDocShell::Save() +{ + //#i3370# remove quick help to prevent saving of autocorrection suggestions + if (m_pView) + m_pView->GetEditWin().StopQuickHelp(); + SwWait aWait( *this, true ); + + CalcLayoutForOLEObjects(); // format for OLE objects + // #i62875# + // reset compatibility flag <DoNotCaptureDrawObjsOnPage>, if possible + if (m_pWrtShell && m_xDoc && + m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && + docfunc::AllDrawObjsOnPage(*m_xDoc)) + { + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false); + } + + ErrCode nErr = ERR_SWG_WRITE_ERROR, nVBWarning = ERRCODE_NONE; + if( SfxObjectShell::Save() ) + { + switch( GetCreateMode() ) + { + case SfxObjectCreateMode::INTERNAL: + nErr = ERRCODE_NONE; + break; + + case SfxObjectCreateMode::ORGANIZER: + { + WriterRef xWrt; + ::GetXMLWriter(OUString(), GetMedium()->GetBaseURL(true), xWrt); + xWrt->SetOrganizerMode( true ); + SwWriter aWrt( *GetMedium(), *m_xDoc ); + nErr = aWrt.Write( xWrt ); + xWrt->SetOrganizerMode( false ); + } + break; + + case SfxObjectCreateMode::EMBEDDED: + // Suppress SfxProgress, if we are Embedded + SW_MOD()->SetEmbeddedLoadSave( true ); + [[fallthrough]]; + + case SfxObjectCreateMode::STANDARD: + default: + { + if (m_xDoc->ContainsMSVBasic()) + { + if( SvtFilterOptions::Get().IsLoadWordBasicStorage() ) + nVBWarning = GetSaveWarningOfMSVBAStorage( static_cast<SfxObjectShell&>(*this) ); + m_xDoc->SetContainsMSVBasic( false ); + } + + // End TableBox Edit! + if (m_pWrtShell) + m_pWrtShell->EndAllTableBoxEdit(); + + WriterRef xWrt; + ::GetXMLWriter(OUString(), GetMedium()->GetBaseURL(true), xWrt); + + bool bLockedView(false); + if (m_pWrtShell) + { + bLockedView = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); //lock visible section + } + + SwWriter aWrt( *GetMedium(), *m_xDoc ); + nErr = aWrt.Write( xWrt ); + + if (m_pWrtShell) + m_pWrtShell->LockView( bLockedView ); + } + break; + } + SW_MOD()->SetEmbeddedLoadSave( false ); + } + SetError(nErr ? nErr : nVBWarning); + + SfxViewFrame *const pFrame = + m_pWrtShell ? m_pWrtShell->GetView().GetViewFrame() : nullptr; + if( pFrame ) + { + pFrame->GetBindings().SetState(SfxBoolItem(SID_DOC_MODIFIED, false)); + } + return !nErr.IsError(); +} + +SwDocShell::LockAllViewsGuard::LockAllViewsGuard(SwViewShell* pViewShell) +{ + if (!pViewShell) + return; + for (SwViewShell& rShell : pViewShell->GetRingContainer()) + { + if (!rShell.IsViewLocked()) + { + m_aViewWasUnLocked.push_back(&rShell); + rShell.LockView(true); + } + } +} + +SwDocShell::LockAllViewsGuard::~LockAllViewsGuard() +{ + for (SwViewShell* pShell : m_aViewWasUnLocked) + pShell->LockView(false); +} + +std::unique_ptr<SwDocShell::LockAllViewsGuard> SwDocShell::LockAllViews() +{ + return std::make_unique<LockAllViewsGuard>(GetEditShell()); +} + +// Save using the Defaultformat +bool SwDocShell::SaveAs( SfxMedium& rMedium ) +{ + SwWait aWait( *this, true ); + // tdf#41063: prevent jumping to cursor at any temporary modification + auto aViewGuard(LockAllViews()); + //#i3370# remove quick help to prevent saving of autocorrection suggestions + if (m_pView) + m_pView->GetEditWin().StopQuickHelp(); + + //#i91811# mod if we have an active margin window, write back the text + if (m_pView && + m_pView->GetPostItMgr() && + m_pView->GetPostItMgr()->HasActiveSidebarWin()) + { + m_pView->GetPostItMgr()->UpdateDataOnActiveSidebarWin(); + } + + if (m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) && + !m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS)) + RemoveOLEObjects(); + + if (GetMedium()) + { + // Task 75666 - is the Document imported by our Microsoft-Filters? + std::shared_ptr<const SfxFilter> pOldFilter = GetMedium()->GetFilter(); + if( pOldFilter && + ( pOldFilter->GetUserData() == FILTER_WW8 || + pOldFilter->GetUserData() == "CWW6" || + pOldFilter->GetUserData() == "WW6" ) ) + { + // when saving it in our own fileformat, then remove the template + // name from the docinfo. + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + xDocProps->setTemplateName(OUString()); + xDocProps->setTemplateURL(OUString()); + xDocProps->setTemplateDate(::util::DateTime()); + } + } + + CalcLayoutForOLEObjects(); // format for OLE objects + + const bool bURLChanged = GetMedium() && GetMedium()->GetURLObject() != rMedium.GetURLObject(); + const SwDBManager* const pMgr = m_xDoc->GetDBManager(); + const bool bHasEmbedded = pMgr && !pMgr->getEmbeddedName().isEmpty(); + bool bSaveDS = bHasEmbedded && bURLChanged; + if (bSaveDS) + { + // Don't save data source in case a temporary is being saved for preview in MM wizard + if (const SfxBoolItem* pNoEmbDS + = SfxItemSet::GetItem(rMedium.GetItemSet(), SID_NO_EMBEDDED_DS, false)) + bSaveDS = !pNoEmbDS->GetValue(); + } + if (bSaveDS) + { + // We have an embedded data source definition, need to re-store it, + // otherwise relative references will break when the new file is in a + // different directory. + + OUString aURL(GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE)); + if (aURL.isEmpty()) + { + // No old URL - is this a new document created from a template with embedded DS? + // Try to get the template URL to reconstruct the embedded data source URL + const css::beans::PropertyValues& rArgs = GetMedium()->GetArgs(); + const auto aURLIter = std::find_if(rArgs.begin(), rArgs.end(), + [](const auto& v) { return v.Name == "URL"; }); + if (aURLIter != rArgs.end()) + aURLIter->Value >>= aURL; + } + + if (!aURL.isEmpty()) + { + auto xContext(comphelper::getProcessComponentContext()); + auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(aURL); + assert(xUri.is()); + xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext) + ->createVndSunStarPkgUrlReference(xUri); + assert(xUri.is()); + aURL = xUri->getUriReference() + "/" + + INetURLObject::encode(pMgr->getEmbeddedName(), INetURLObject::PART_FPATH, + INetURLObject::EncodeMechanism::All); + + bool bCopyTo = GetCreateMode() == SfxObjectCreateMode::EMBEDDED; + if (!bCopyTo) + { + if (const SfxBoolItem* pSaveToItem + = SfxItemSet::GetItem(rMedium.GetItemSet(), SID_SAVETO, false)) + bCopyTo = pSaveToItem->GetValue(); + } + + auto xDatabaseContext = sdb::DatabaseContext::create(xContext); + uno::Reference<sdb::XDocumentDataSource> xDataSource(xDatabaseContext->getByName(aURL), + uno::UNO_QUERY); + if (xDataSource) + { + uno::Reference<frame::XStorable> xStorable(xDataSource->getDatabaseDocument(), + uno::UNO_QUERY); + SwDBManager::StoreEmbeddedDataSource(xStorable, rMedium.GetOutputStorage(), + pMgr->getEmbeddedName(), rMedium.GetName(), + bCopyTo); + } + } + } + + // #i62875# + // reset compatibility flag <DoNotCaptureDrawObjsOnPage>, if possible + if (m_pWrtShell && + m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && + docfunc::AllDrawObjsOnPage(*m_xDoc)) + { + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false); + } + + ErrCode nErr = ERR_SWG_WRITE_ERROR, nVBWarning = ERRCODE_NONE; + uno::Reference < embed::XStorage > xStor = rMedium.GetOutputStorage(); + if( SfxObjectShell::SaveAs( rMedium ) ) + { + if( GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) && dynamic_cast< const SwGlobalDocShell *>( this ) == nullptr ) + { + // The document is closed explicitly, but using SfxObjectShellLock is still more correct here + SfxObjectShellLock xDocSh = + new SwGlobalDocShell( SfxObjectCreateMode::INTERNAL ); + // the global document can not be a template + xDocSh->SetupStorage( xStor, SotStorage::GetVersion( xStor ), false ); + xDocSh->DoClose(); + } + + if (m_xDoc->ContainsMSVBasic()) + { + if( SvtFilterOptions::Get().IsLoadWordBasicStorage() ) + nVBWarning = GetSaveWarningOfMSVBAStorage( static_cast<SfxObjectShell&>(*this) ); + m_xDoc->SetContainsMSVBasic( false ); + } + + if (m_pWrtShell) + { + // End TableBox Edit! + m_pWrtShell->EndAllTableBoxEdit(); + + // Remove invalid signatures. + m_pWrtShell->ValidateAllParagraphSignatures(false); + + m_pWrtShell->ClassifyDocPerHighestParagraphClass(); + } + + // Remember and preserve Modified-Flag without calling the Link + // (for OLE; after Statement from MM) + const bool bIsModified = m_xDoc->getIDocumentState().IsModified(); + m_xDoc->GetIDocumentUndoRedo().LockUndoNoModifiedPosition(); + Link<bool,void> aOldOLELnk( m_xDoc->GetOle2Link() ); + m_xDoc->SetOle2Link( Link<bool,void>() ); + + // Suppress SfxProgress when we are Embedded + SW_MOD()->SetEmbeddedLoadSave( + SfxObjectCreateMode::EMBEDDED == GetCreateMode() ); + + WriterRef xWrt; + ::GetXMLWriter(OUString(), rMedium.GetBaseURL(true), xWrt); + + bool bLockedView(false); + if (m_pWrtShell) + { + bLockedView = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); //lock visible section + } + + SwWriter aWrt( rMedium, *m_xDoc ); + nErr = aWrt.Write( xWrt ); + + if (m_pWrtShell) + m_pWrtShell->LockView( bLockedView ); + + if( bIsModified ) + { + m_xDoc->getIDocumentState().SetModified(); + m_xDoc->GetIDocumentUndoRedo().UnLockUndoNoModifiedPosition(); + } + m_xDoc->SetOle2Link( aOldOLELnk ); + + SW_MOD()->SetEmbeddedLoadSave( false ); + + // Increase RSID + m_xDoc->setRsid( m_xDoc->getRsid() ); + + m_xDoc->cleanupUnoCursorTable(); + } + SetError(nErr ? nErr : nVBWarning); + + return !nErr.IsError(); +} + +// Save all Formats +static SwSrcView* lcl_GetSourceView( SwDocShell const * pSh ) +{ + // are we in SourceView? + SfxViewFrame* pVFrame = SfxViewFrame::GetFirst( pSh ); + SfxViewShell* pViewShell = pVFrame ? pVFrame->GetViewShell() : nullptr; + return dynamic_cast<SwSrcView*>( pViewShell ); +} + +bool SwDocShell::ConvertTo( SfxMedium& rMedium ) +{ + std::shared_ptr<const SfxFilter> pFlt = rMedium.GetFilter(); + if( !pFlt ) + return false; + + WriterRef xWriter; + SwReaderWriter::GetWriter( pFlt->GetUserData(), rMedium.GetBaseURL( true ), xWriter ); + if( !xWriter.is() ) + { // Filter not available + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_DLLNOTFOUND))); + xInfoBox->run(); + return false; + } + + //#i3370# remove quick help to prevent saving of autocorrection suggestions + if (m_pView) + m_pView->GetEditWin().StopQuickHelp(); + + //#i91811# mod if we have an active margin window, write back the text + if (m_pView && + m_pView->GetPostItMgr() && + m_pView->GetPostItMgr()->HasActiveSidebarWin()) + { + m_pView->GetPostItMgr()->UpdateDataOnActiveSidebarWin(); + } + + ErrCode nVBWarning = ERRCODE_NONE; + + if (m_xDoc->ContainsMSVBasic()) + { + bool bSave = pFlt->GetUserData() == "CWW8" + && SvtFilterOptions::Get().IsLoadWordBasicStorage(); + + if ( bSave ) + { + tools::SvRef<SotStorage> xStg = new SotStorage( rMedium.GetOutStream(), false ); + OSL_ENSURE( !xStg->GetError(), "No storage available for storing VBA macros!" ); + if ( !xStg->GetError() ) + { + nVBWarning = SaveOrDelMSVBAStorage( static_cast<SfxObjectShell&>(*this), *xStg, bSave, "Macros" ); + xStg->Commit(); + m_xDoc->SetContainsMSVBasic( true ); + } + } + } + + // End TableBox Edit! + if (m_pWrtShell) + m_pWrtShell->EndAllTableBoxEdit(); + + if( pFlt->GetUserData() == "HTML" ) + { +#if HAVE_FEATURE_SCRIPTING + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + if( !rHtmlOpt.IsStarBasic() && rHtmlOpt.IsStarBasicWarning() && HasBasic() ) + { + uno::Reference< XLibraryContainer > xLibCont = GetBasicContainer(); + uno::Reference< XNameAccess > xLib; + const Sequence<OUString> aNames = xLibCont->getElementNames(); + for(const OUString& rName : aNames) + { + Any aLib = xLibCont->getByName(rName); + aLib >>= xLib; + if(xLib.is()) + { + Sequence<OUString> aModNames = xLib->getElementNames(); + if(aModNames.hasElements()) + { + SetError(WARN_SWG_HTML_NO_MACROS); + break; + } + } + } + } +#endif + } + + // #i76360# Update document statistics + if ( !rMedium.IsSkipImages() ) + m_xDoc->getIDocumentStatistics().UpdateDocStat( false, true ); + + CalcLayoutForOLEObjects(); // format for OLE objects + // #i62875# + // reset compatibility flag <DoNotCaptureDrawObjsOnPage>, if possible + if (m_pWrtShell && + m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) && + docfunc::AllDrawObjsOnPage(*m_xDoc)) + { + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, false); + } + + if( xWriter->IsStgWriter() && + ( pFlt->GetUserData() == FILTER_XML || + pFlt->GetUserData() == FILTER_XMLV || + pFlt->GetUserData() == FILTER_XMLVW ) ) + { + // determine the own Type + sal_uInt8 nMyType = 0; + if( dynamic_cast< const SwWebDocShell *>( this ) != nullptr ) + nMyType = 1; + else if( dynamic_cast< const SwGlobalDocShell *>( this ) != nullptr ) + nMyType = 2; + + // determine the desired Type + sal_uInt8 nSaveType = 0; + SotClipboardFormatId nSaveClipId = pFlt->GetFormat(); + if( SotClipboardFormatId::STARWRITERWEB_8 == nSaveClipId || + SotClipboardFormatId::STARWRITERWEB_60 == nSaveClipId || + SotClipboardFormatId::STARWRITERWEB_50 == nSaveClipId || + SotClipboardFormatId::STARWRITERWEB_40 == nSaveClipId ) + nSaveType = 1; + else if( SotClipboardFormatId::STARWRITERGLOB_8 == nSaveClipId || + SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE == nSaveClipId || + SotClipboardFormatId::STARWRITERGLOB_60 == nSaveClipId || + SotClipboardFormatId::STARWRITERGLOB_50 == nSaveClipId || + SotClipboardFormatId::STARWRITERGLOB_40 == nSaveClipId ) + nSaveType = 2; + + // Change Flags of the Document accordingly + bool bIsHTMLModeSave = GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE); + bool bIsGlobalDocSave = GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT); + bool bIsGlblDocSaveLinksSave = GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS); + if( nMyType != nSaveType ) + { + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, 1 == nSaveType); + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT, 2 == nSaveType); + if( 2 != nSaveType ) + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, false); + } + + // if the target format is storage based, then the output storage must be already created + if ( rMedium.IsStorage() ) + { + // set MediaType on target storage + // (MediaType will be queried during SaveAs) + try + { + // TODO/MBA: testing + uno::Reference < beans::XPropertySet > xSet( rMedium.GetStorage(), uno::UNO_QUERY ); + if ( xSet.is() ) + xSet->setPropertyValue("MediaType", uno::makeAny( SotExchange::GetFormatMimeType( nSaveClipId ) ) ); + } + catch (const uno::Exception&) + { + } + } + + // Now normally save the Document + bool bRet = SaveAs( rMedium ); + + if( nMyType != nSaveType ) + { + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, bIsHTMLModeSave ); + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT, bIsGlobalDocSave); + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, bIsGlblDocSaveLinksSave); + } + + return bRet; + } + + if( pFlt->GetUserData() == FILTER_TEXT_DLG && + (m_pWrtShell || !::lcl_GetSourceView(this))) + { + SwAsciiOptions aOpt; + OUString sItemOpt; + const SfxItemSet* pSet; + const SfxPoolItem* pItem; + if( nullptr != ( pSet = rMedium.GetItemSet() ) ) + { + if( SfxItemState::SET == pSet->GetItemState( SID_FILE_FILTEROPTIONS, + true, &pItem ) ) + sItemOpt = static_cast<const SfxStringItem*>(pItem)->GetValue(); + } + if(!sItemOpt.isEmpty()) + aOpt.ReadUserData( sItemOpt ); + + xWriter->SetAsciiOptions( aOpt ); + } + + // Suppress SfxProgress when we are Embedded + SW_MOD()->SetEmbeddedLoadSave( + SfxObjectCreateMode::EMBEDDED == GetCreateMode()); + + // Span Context in order to suppress the Selection's View + ErrCode nErrno; + const OUString aFileName( rMedium.GetName() ); + + // No View, so the whole Document! + if (m_pWrtShell && !Application::IsHeadlessModeEnabled()) + { + SwWait aWait( *this, true ); + // #i106906# + const bool bFormerLockView = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->StartAllAction(); + m_pWrtShell->Push(); + SwWriter aWrt( rMedium, *m_pWrtShell, true ); + nErrno = aWrt.Write( xWriter, &aFileName ); + //JP 16.05.97: In case the SFX revokes the View while saving + if (m_pWrtShell) + { + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + m_pWrtShell->EndAllAction(); + // #i106906# + m_pWrtShell->LockView( bFormerLockView ); + } + } + else + { + // are we in SourceView? + SwSrcView* pSrcView = ::lcl_GetSourceView( this ); + if( pSrcView ) + { + pSrcView->SaveContentTo(rMedium); + nErrno = ERRCODE_NONE; + } + else + { + SwWriter aWrt( rMedium, *m_xDoc ); + nErrno = aWrt.Write( xWriter, &aFileName ); + } + } + + SW_MOD()->SetEmbeddedLoadSave( false ); + SetError(nErrno ? nErrno : nVBWarning); + if( !rMedium.IsStorage() ) + rMedium.CloseOutStream(); + + return ! nErrno.IsError(); +} + +// Hands off +// do not yet activate, must deliver TRUE +bool SwDocShell::SaveCompleted( const uno::Reference < embed::XStorage >& xStor ) +{ + bool bRet = SfxObjectShell::SaveCompleted( xStor ); + if( bRet ) + { + // Do not decide until here, whether Saving was successful or not + if( IsModified() ) + m_xDoc->getIDocumentState().SetModified(); + else + m_xDoc->getIDocumentState().ResetModified(); + } + + if (m_pOLEChildList) + { + bool bResetModified = IsEnableSetModified(); + if( bResetModified ) + EnableSetModified( false ); + + uno::Sequence < OUString > aNames = m_pOLEChildList->GetObjectNames(); + for( sal_Int32 n = aNames.getLength(); n; n-- ) + { + if (!m_pOLEChildList->MoveEmbeddedObject(aNames[n-1], GetEmbeddedObjectContainer())) + { + OSL_FAIL("Copying of objects didn't work!" ); + } + } + + m_pOLEChildList.reset(); + if( bResetModified ) + EnableSetModified(); + } + return bRet; +} + +// Draw()-Override for OLE2 (Sfx) +void SwDocShell::Draw( OutputDevice* pDev, const JobSetup& rSetup, + sal_uInt16 nAspect ) +{ + //fix #25341# Draw should not affect the Modified + bool bResetModified = IsEnableSetModified(); + if ( bResetModified ) + EnableSetModified( false ); + + // When there is a JobSetup connected to the Document, we copy it to + // reconnect it after PrtOle2. We don't use an empty JobSetup because + // that would only lead to questionable results after expensive + // reformatting (Preview!) + std::unique_ptr<JobSetup> pOrig; + if ( !rSetup.GetPrinterName().isEmpty() && ASPECT_THUMBNAIL != nAspect ) + { + const JobSetup* pCurrentJobSetup = m_xDoc->getIDocumentDeviceAccess().getJobsetup(); + if( pCurrentJobSetup ) // then we copy that + pOrig.reset(new JobSetup( *pCurrentJobSetup )); + m_xDoc->getIDocumentDeviceAccess().setJobsetup( rSetup ); + } + + tools::Rectangle aRect( nAspect == ASPECT_THUMBNAIL ? + GetVisArea( nAspect ) : GetVisArea( ASPECT_CONTENT ) ); + + pDev->Push(); + pDev->SetFillColor(); + pDev->SetLineColor(); + pDev->SetBackground(); + const bool bWeb = dynamic_cast< const SwWebDocShell *>( this ) != nullptr; + SwPrintData aOpts; + SwViewShell::PrtOle2(m_xDoc.get(), SW_MOD()->GetUsrPref(bWeb), aOpts, *pDev, aRect); + pDev->Pop(); + + if( pOrig ) + { + m_xDoc->getIDocumentDeviceAccess().setJobsetup( *pOrig ); + } + if ( bResetModified ) + EnableSetModified(); +} + +void SwDocShell::SetVisArea( const tools::Rectangle &rRect ) +{ + tools::Rectangle aRect( rRect ); + if (m_pView) + { + Size aSz( m_pView->GetDocSz() ); + aSz.AdjustWidth(DOCUMENTBORDER ); aSz.AdjustHeight(DOCUMENTBORDER ); + long nMoveX = 0, nMoveY = 0; + if ( aRect.Right() > aSz.Width() ) + nMoveX = aSz.Width() - aRect.Right(); + if ( aRect.Bottom() > aSz.Height() ) + nMoveY = aSz.Height() - aRect.Bottom(); + aRect.Move( nMoveX, nMoveY ); + nMoveX = aRect.Left() < 0 ? -aRect.Left() : 0; + nMoveY = aRect.Top() < 0 ? -aRect.Top() : 0; + aRect.Move( nMoveX, nMoveY ); + + // Calls SfxInPlaceObject::SetVisArea()! + m_pView->SetVisArea( aRect ); + } + else + SfxObjectShell::SetVisArea( aRect ); +} + +tools::Rectangle SwDocShell::GetVisArea( sal_uInt16 nAspect ) const +{ + if ( nAspect == ASPECT_THUMBNAIL ) + { + // Preview: set VisArea to the first page. + SwNodeIndex aIdx( m_xDoc->GetNodes().GetEndOfExtras(), 1 ); + SwContentNode* pNd = m_xDoc->GetNodes().GoNext( &aIdx ); + + const SwRect aPageRect = pNd->FindPageFrameRect(); + if (aPageRect.IsEmpty()) + return tools::Rectangle(); + tools::Rectangle aRect(aPageRect.SVRect()); + + // tdf#81219 sanitize - nobody is interested in a thumbnail where's + // nothing visible + if (aRect.GetHeight() > 2*aRect.GetWidth()) + aRect.SetSize(Size(aRect.GetWidth(), 2*aRect.GetWidth())); + else if (aRect.GetWidth() > 2*aRect.GetHeight()) + aRect.SetSize(Size(2*aRect.GetHeight(), aRect.GetHeight())); + + return aRect; + } + return SfxObjectShell::GetVisArea( nAspect ); +} + +Printer *SwDocShell::GetDocumentPrinter() +{ + return m_xDoc->getIDocumentDeviceAccess().getPrinter( false ); +} + +OutputDevice* SwDocShell::GetDocumentRefDev() +{ + return m_xDoc->getIDocumentDeviceAccess().getReferenceDevice( false ); +} + +void SwDocShell::OnDocumentPrinterChanged( Printer * pNewPrinter ) +{ + if ( pNewPrinter ) + GetDoc()->getIDocumentDeviceAccess().setJobsetup( pNewPrinter->GetJobSetup() ); + else + GetDoc()->getIDocumentDeviceAccess().setPrinter( nullptr, true, true ); +} + +// #i20883# Digital Signatures and Encryption +HiddenInformation SwDocShell::GetHiddenInformationState( HiddenInformation nStates ) +{ + // get global state like HiddenInformation::DOCUMENTVERSIONS + HiddenInformation nState = SfxObjectShell::GetHiddenInformationState( nStates ); + + if ( nStates & HiddenInformation::RECORDEDCHANGES ) + { + if ( !GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty() ) + nState |= HiddenInformation::RECORDEDCHANGES; + } + if ( nStates & HiddenInformation::NOTES ) + { + OSL_ENSURE( GetWrtShell(), "No SwWrtShell, no information" ); + if(GetWrtShell() && GetWrtShell()->GetFieldType(SwFieldIds::Postit, OUString())->HasHiddenInformationNotes()) + nState |= HiddenInformation::NOTES; + } + + return nState; +} + +void SwDocShell::GetState(SfxItemSet& rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while (nWhich) + { + switch (nWhich) + { + case SID_PRINTPREVIEW: + { + bool bDisable = IsInPlaceActive(); + // Disable "multiple layout" + if ( !bDisable ) + { + SfxViewFrame *pTmpFrame = SfxViewFrame::GetFirst(this); + while (pTmpFrame) // Look for Preview + { + if ( dynamic_cast<SwView*>( pTmpFrame->GetViewShell() ) && + static_cast<SwView*>(pTmpFrame->GetViewShell())->GetWrtShell().GetViewOptions()->getBrowseMode() ) + { + bDisable = true; + break; + } + pTmpFrame = SfxViewFrame::GetNext(*pTmpFrame, this); + } + } + // End of disabled "multiple layout" + if ( bDisable ) + rSet.DisableItem( SID_PRINTPREVIEW ); + else + { + SfxBoolItem aBool( SID_PRINTPREVIEW, false ); + if( dynamic_cast<SwPagePreview*>( SfxViewShell::Current()) ) + aBool.SetValue( true ); + rSet.Put( aBool ); + } + } + break; + case SID_AUTO_CORRECT_DLG: + if ( comphelper::LibreOfficeKit::isActive() ) + rSet.DisableItem( SID_AUTO_CORRECT_DLG ); + break; + case SID_SOURCEVIEW: + { + SfxViewShell* pCurrView = GetView() ? static_cast<SfxViewShell*>(GetView()) + : SfxViewShell::Current(); + bool bSourceView = dynamic_cast<SwSrcView*>( pCurrView ) != nullptr; + rSet.Put(SfxBoolItem(SID_SOURCEVIEW, bSourceView)); + } + break; + case SID_HTML_MODE: + rSet.Put(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(this))); + break; + + case FN_ABSTRACT_STARIMPRESS: + case FN_OUTLINE_TO_IMPRESS: + { + SvtModuleOptions aMOpt; + if (!aMOpt.IsImpress() || GetObjectShell()->isExportLocked()) + rSet.DisableItem( nWhich ); + } + [[fallthrough]]; + case FN_ABSTRACT_NEWDOC: + case FN_OUTLINE_TO_CLIPBOARD: + { + if ( GetDoc()->GetNodes().GetOutLineNds().empty() ) + rSet.DisableItem( nWhich ); + } + break; + case SID_BROWSER_MODE: + case FN_PRINT_LAYOUT: + { + bool bState = GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE); + if(FN_PRINT_LAYOUT == nWhich) + bState = !bState; + rSet.Put( SfxBoolItem( nWhich, bState)); + } + break; + + case FN_NEW_GLOBAL_DOC: + if (dynamic_cast<const SwGlobalDocShell*>(this) != nullptr + || GetObjectShell()->isExportLocked()) + rSet.DisableItem( nWhich ); + break; + + case FN_NEW_HTML_DOC: + if (dynamic_cast<const SwWebDocShell*>(this) != nullptr + || GetObjectShell()->isExportLocked()) + rSet.DisableItem( nWhich ); + break; + + case FN_OPEN_FILE: + if( dynamic_cast< const SwWebDocShell *>( this ) != nullptr ) + rSet.DisableItem( nWhich ); + break; + + case SID_ATTR_YEAR2000: + { + const SvNumberFormatter* pFormatr = m_xDoc->GetNumberFormatter(false); + rSet.Put( SfxUInt16Item( nWhich, + static_cast< sal_uInt16 >( + pFormatr ? pFormatr->GetYear2000() + : ::utl::MiscCfg().GetYear2000() ))); + } + break; + case SID_ATTR_CHAR_FONTLIST: + { + rSet.Put( SvxFontListItem(m_pFontList.get(), SID_ATTR_CHAR_FONTLIST) ); + } + break; + case SID_MAIL_PREPAREEXPORT: + { + //check if linked content or possibly hidden content is available + //m_xDoc->UpdateFields( NULL, false ); + sfx2::LinkManager& rLnkMgr = m_xDoc->getIDocumentLinksAdministration().GetLinkManager(); + const ::sfx2::SvBaseLinks& rLnks = rLnkMgr.GetLinks(); + bool bRet = false; + if( !rLnks.empty() ) + bRet = true; + else + { + //sections with hidden flag, hidden character attribute, hidden paragraph/text or conditional text fields + bRet = m_xDoc->HasInvisibleContent(); + } + rSet.Put( SfxBoolItem( nWhich, bRet ) ); + } + break; + case SID_NOTEBOOKBAR: + { + SfxViewShell* pViewShell = GetView()? GetView(): SfxViewShell::Current(); + bool bVisible = sfx2::SfxNotebookBar::StateMethod(pViewShell->GetViewFrame()->GetBindings(), + "modules/swriter/ui/"); + rSet.Put( SfxBoolItem( SID_NOTEBOOKBAR, bVisible ) ); + } + break; + case FN_REDLINE_ACCEPT_ALL: + case FN_REDLINE_REJECT_ALL: + { + if (GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty() || + HasChangeRecordProtection()) // tdf#128229 Disable Accept / Reject all if redlines are password protected + rSet.DisableItem(nWhich); + } + break; + + default: OSL_ENSURE(false,"You cannot get here!"); + + } + nWhich = aIter.NextWhich(); + } +} + +// OLE-Hdls +IMPL_LINK( SwDocShell, Ole2ModifiedHdl, bool, bNewStatus, void ) +{ + if( IsEnableSetModified() ) + SetModified( bNewStatus ); +} + +// return Pool here, because virtual +SfxStyleSheetBasePool* SwDocShell::GetStyleSheetPool() +{ + return m_xBasePool.get(); +} + +sfx2::StyleManager* SwDocShell::GetStyleManager() +{ + return m_pStyleManager.get(); +} + +void SwDocShell::SetView(SwView* pVw) +{ + SetViewShell_Impl(pVw); + m_pView = pVw; + if (m_pView) + { + m_pWrtShell = &m_pView->GetWrtShell(); + + // Set view-specific redline author. + const OUString& rRedlineAuthor = m_pView->GetRedlineAuthor(); + if (!rRedlineAuthor.isEmpty()) + SW_MOD()->SetRedlineAuthor(m_pView->GetRedlineAuthor()); + } + else + m_pWrtShell = nullptr; +} + +// #i59688# +// linked graphics are now loaded on demand. +// Thus, loading of linked graphics no longer needed and necessary for +// the load of document being finished. +void SwDocShell::LoadingFinished() +{ + // #i38810# + // Original fix fails after integration of cws xmlsec11: + // interface <SfxObjectShell::EnableSetModified(..)> no longer works, because + // <SfxObjectShell::FinishedLoading(..)> doesn't care about its status and + // enables the document modification again. + // Thus, manual modify the document, if it's modified and its links are updated + // before <FinishedLoading(..)> is called. + const bool bHasDocToStayModified( m_xDoc->getIDocumentState().IsModified() && m_xDoc->getIDocumentLinksAdministration().LinksUpdated() ); + + FinishedLoading(); + SfxViewFrame* pVFrame = SfxViewFrame::GetFirst(this); + if(pVFrame) + { + SfxViewShell* pShell = pVFrame->GetViewShell(); + if(auto pSrcView = dynamic_cast<SwSrcView*>( pShell) ) + pSrcView->Load(this); + } + + // #i38810# + if ( bHasDocToStayModified && !m_xDoc->getIDocumentState().IsModified() ) + { + m_xDoc->getIDocumentState().SetModified(); + } +} + +// a Transfer is cancelled (is called from SFX) +void SwDocShell::CancelTransfers() +{ + // Cancel all links from LinkManager + m_xDoc->getIDocumentLinksAdministration().GetLinkManager().CancelTransfers(); + SfxObjectShell::CancelTransfers(); +} + +SwEditShell * SwDocShell::GetEditShell() +{ + return m_pWrtShell; +} + +SwFEShell* SwDocShell::GetFEShell() +{ + return m_pWrtShell; +} + +void SwDocShell::RemoveOLEObjects() +{ + SwIterator<SwContentNode,SwFormatColl> aIter( *m_xDoc->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + { + SwOLENode* pOLENd = pNd->GetOLENode(); + if( pOLENd && ( pOLENd->IsOLEObjectDeleted() || + pOLENd->IsInGlobalDocSection() ) ) + { + if (!m_pOLEChildList) + m_pOLEChildList.reset( new comphelper::EmbeddedObjectContainer ); + + OUString aObjName = pOLENd->GetOLEObj().GetCurrentPersistName(); + GetEmbeddedObjectContainer().MoveEmbeddedObject( aObjName, *m_pOLEChildList ); + } + } +} + +// When a document is loaded, SwDoc::PrtOLENotify is called to update +// the sizes of math objects. However, for objects that do not have a +// SwFrame at this time, only a flag is set (bIsOLESizeInvalid) and the +// size change takes place later, while calculating the layout in the +// idle handler. If this document is saved now, it is saved with invalid +// sizes. For this reason, the layout has to be calculated before a document is +// saved, but of course only id there are OLE objects with bOLESizeInvalid set. +void SwDocShell::CalcLayoutForOLEObjects() +{ + if (!m_pWrtShell) + return; + + if (m_pView && m_pView->GetIPClient()) + { + // We have an active OLE edit: allow link updates, so an up to date replacement graphic can + // be created. + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + } + + SwIterator<SwContentNode,SwFormatColl> aIter( *m_xDoc->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + { + SwOLENode* pOLENd = pNd->GetOLENode(); + if( pOLENd && pOLENd->IsOLESizeInvalid() ) + { + m_pWrtShell->CalcLayout(); + break; + } + } +} + +// #i42634# Overwrites SfxObjectShell::UpdateLinks +// This new function is necessary to trigger update of links in docs +// read by the binary filter: +void SwDocShell::UpdateLinks() +{ + GetDoc()->getIDocumentLinksAdministration().UpdateLinks(); + // #i50703# Update footnote numbers + SwTextFootnote::SetUniqueSeqRefNo( *GetDoc() ); + SwNodeIndex aTmp( GetDoc()->GetNodes() ); + GetDoc()->GetFootnoteIdxs().UpdateFootnote( aTmp ); +} + +uno::Reference< frame::XController > + SwDocShell::GetController() +{ + css::uno::Reference< css::frame::XController > aRet; + // #i82346# No view in page preview + if ( GetView() ) + aRet = GetView()->GetController(); + return aRet; +} + +static const char* s_EventNames[] = +{ + "OnPageCountChange", + "OnMailMerge", + "OnMailMergeFinished", + "OnFieldMerge", + "OnFieldMergeFinished", + "OnLayoutFinished" +}; +static sal_Int32 const s_nEvents(SAL_N_ELEMENTS(s_EventNames)); + +Sequence< OUString > SwDocShell::GetEventNames() +{ + Sequence< OUString > aRet = SfxObjectShell::GetEventNames(); + sal_Int32 nLen = aRet.getLength(); + aRet.realloc(nLen + 6); + OUString* pNames = aRet.getArray(); + pNames[nLen++] = GetEventName(0); + pNames[nLen++] = GetEventName(1); + pNames[nLen++] = GetEventName(2); + pNames[nLen++] = GetEventName(3); + pNames[nLen++] = GetEventName(4); + pNames[nLen] = GetEventName(5); + + return aRet; +} + +OUString SwDocShell::GetEventName( sal_Int32 nIndex ) +{ + if (nIndex < s_nEvents) + { + return OUString::createFromAscii(s_EventNames[nIndex]); + } + return OUString(); +} + +const ::sfx2::IXmlIdRegistry* SwDocShell::GetXmlIdRegistry() const +{ + return m_xDoc ? &m_xDoc->GetXmlIdRegistry() : nullptr; +} + +bool SwDocShell::IsChangeRecording() const +{ + if (!m_pWrtShell) + return false; + return bool(m_pWrtShell->GetRedlineFlags() & RedlineFlags::On); +} + +bool SwDocShell::HasChangeRecordProtection() const +{ + if (!m_pWrtShell) + return false; + return m_pWrtShell->getIDocumentRedlineAccess().GetRedlinePassword().hasElements(); +} + +void SwDocShell::SetChangeRecording( bool bActivate ) +{ + RedlineFlags nOn = bActivate ? RedlineFlags::On : RedlineFlags::NONE; + RedlineFlags nMode = m_pWrtShell->GetRedlineFlags(); + m_pWrtShell->SetRedlineFlagsAndCheckInsMode( (nMode & ~RedlineFlags::On) | nOn ); +} + +void SwDocShell::SetProtectionPassword( const OUString &rNewPassword ) +{ + const SfxAllItemSet aSet( GetPool() ); + const SfxPoolItem* pItem = nullptr; + + IDocumentRedlineAccess& rIDRA = m_pWrtShell->getIDocumentRedlineAccess(); + Sequence< sal_Int8 > aPasswd = rIDRA.GetRedlinePassword(); + if (SfxItemState::SET == aSet.GetItemState(FN_REDLINE_PROTECT, false, &pItem) + && static_cast<const SfxBoolItem*>(pItem)->GetValue() == aPasswd.hasElements()) + return; + + if (!rNewPassword.isEmpty()) + { + // when password protection is applied change tracking must always be active + SetChangeRecording( true ); + + Sequence< sal_Int8 > aNewPasswd; + SvPasswordHelper::GetHashPassword( aNewPasswd, rNewPassword ); + rIDRA.SetRedlinePassword( aNewPasswd ); + } + else + { + rIDRA.SetRedlinePassword( Sequence< sal_Int8 >() ); + } +} + +bool SwDocShell::GetProtectionHash( /*out*/ css::uno::Sequence< sal_Int8 > &rPasswordHash ) +{ + bool bRes = false; + + const SfxAllItemSet aSet( GetPool() ); + const SfxPoolItem* pItem = nullptr; + + IDocumentRedlineAccess& rIDRA = m_pWrtShell->getIDocumentRedlineAccess(); + const Sequence< sal_Int8 >& aPasswdHash( rIDRA.GetRedlinePassword() ); + if (SfxItemState::SET == aSet.GetItemState(FN_REDLINE_PROTECT, false, &pItem) + && static_cast<const SfxBoolItem*>(pItem)->GetValue() == aPasswdHash.hasElements()) + return false; + rPasswordHash = aPasswdHash; + bRes = true; + + return bRes; +} + +void SwDocShell::RegisterAutomationDocumentEventsCaller(css::uno::Reference< ooo::vba::XSinkCaller > const& xCaller) +{ + mxAutomationDocumentEventsCaller = xCaller; +} + +void SwDocShell::CallAutomationDocumentEventSinks(const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments) +{ + if (mxAutomationDocumentEventsCaller.is()) + mxAutomationDocumentEventsCaller->CallSinks(Method, Arguments); +} + +void SwDocShell::RegisterAutomationDocumentObject(css::uno::Reference< ooo::vba::word::XDocument > const& xDocument) +{ + mxAutomationDocumentObject = xDocument; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docsh2.cxx b/sw/source/uibase/app/docsh2.cxx new file mode 100644 index 000000000..e70129d99 --- /dev/null +++ b/sw/source/uibase/app/docsh2.cxx @@ -0,0 +1,1745 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/drawing/ModuleDispatcher.hpp> +#include <com/sun/star/frame/DispatchHelper.hpp> +#include <ooo/vba/word/XDocument.hpp> +#include <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> + +#include <sal/log.hxx> +#include <edtwin.hxx> +#include <tools/urlobj.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/errinf.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svl/eitem.hxx> +#include <svl/macitem.hxx> +#include <svl/zforlist.hxx> +#include <unotools/pathoptions.hxx> +#include <vcl/transfer.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/new.hxx> +#include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/evntconf.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <svx/dialogs.hrc> +#include <svx/drawitem.hxx> +#include <editeng/svxacorr.hxx> +#include <svx/fmshell.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/classificationhelper.hxx> +#include <sfx2/watermarkitem.hxx> + +#include <svtools/htmlcfg.hxx> +#include <svx/ofaitem.hxx> +#include <SwSmartTagMgr.hxx> +#include <sfx2/app.hxx> +#include <basic/sbstar.hxx> +#include <basic/basmgr.hxx> +#include <comphelper/classids.hxx> +#include <fmtcol.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <docary.hxx> +#include <wrtsh.hxx> +#include <fldbas.hxx> +#include <viewopt.hxx> +#include <globdoc.hxx> +#include <fldwrap.hxx> +#include <redlndlg.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentState.hxx> +#include <shellio.hxx> +#include <pview.hxx> +#include <srcview.hxx> +#include <wdocsh.hxx> +#include <unotxdoc.hxx> +#include <acmplwrd.hxx> +#include <swmodule.hxx> +#include <unobaseclass.hxx> +#include <swwait.hxx> +#include <swcli.hxx> + +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ListboxControlActions.hpp> +#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/script/vba/XVBAEventProcessor.hpp> +#include <com/sun/star/script/vba/VBAEventId.hpp> +#include <editeng/acorrcfg.hxx> +#include <officecfg/Office/Security.hxx> + +#include <sfx2/fcontnr.hxx> +#include <svx/ClassificationDialog.hxx> +#include <svtools/embedhlp.hxx> + +#include <swabstdlg.hxx> +#include <watermarkdialog.hxx> + +#include <ndtxt.hxx> +#include <iodetect.hxx> + +#include <memory> + +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace ::sfx2; + +// create DocInfo (virtual) +std::shared_ptr<SfxDocumentInfoDialog> SwDocShell::CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet) +{ + std::shared_ptr<SfxDocumentInfoDialog> xDlg = std::make_shared<SfxDocumentInfoDialog>(pParent, rSet); + //only with statistics, when this document is being shown, not + //from within the Doc-Manager + SwDocShell* pDocSh = static_cast<SwDocShell*>( SfxObjectShell::Current()); + if( pDocSh == this ) + { + //Not for SourceView. + SfxViewShell *pVSh = SfxViewShell::Current(); + if ( pVSh && dynamic_cast< const SwSrcView *>( pVSh ) == nullptr ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + xDlg->AddFontTabPage(); + xDlg->AddTabPage("writerstats", SwResId(STR_DOC_STAT), pFact->GetTabPageCreatorFunc(RID_SW_TP_DOC_STAT)); + } + } + return xDlg; +} + +void SwDocShell::ToggleLayoutMode(SwView* pView) +{ + OSL_ENSURE( pView, "SwDocShell::ToggleLayoutMode, pView is null." ); + + const SwViewOption& rViewOptions = *pView->GetWrtShell().GetViewOptions(); + + //TODO: Should HideWhitespace flag be saved in the document settings? + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, rViewOptions.getBrowseMode()); + UpdateFontList(); // Why is this necessary here? + + pView->GetViewFrame()->GetBindings().Invalidate(FN_SHADOWCURSOR); + if( !GetDoc()->getIDocumentDeviceAccess().getPrinter( false ) ) + pView->SetPrinter( GetDoc()->getIDocumentDeviceAccess().getPrinter( false ), SfxPrinterChangeFlags::PRINTER | SfxPrinterChangeFlags::JOBSETUP ); + GetDoc()->CheckDefaultPageFormat(); + SfxViewFrame *pTmpFrame = SfxViewFrame::GetFirst(this, false); + while (pTmpFrame) + { + if( pTmpFrame != pView->GetViewFrame() ) + { + pTmpFrame->DoClose(); + pTmpFrame = SfxViewFrame::GetFirst(this, false); + } + else + pTmpFrame = SfxViewFrame::GetNext(*pTmpFrame, this, false); + } + + pView->GetWrtShell().InvalidateLayout(true); + + pView->RecheckBrowseMode(); + + pView->SetNewWindowAllowed(!rViewOptions.getBrowseMode()); +} + +// update text fields on document properties changes +void SwDocShell::DoFlushDocInfo() +{ + if (!m_xDoc) + return; + + bool bUnlockView(true); + if (m_pWrtShell) + { + bUnlockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); // lock visible section + m_pWrtShell->StartAllAction(); + } + + m_xDoc->getIDocumentStatistics().DocInfoChgd(IsEnableSetModified()); + + if (m_pWrtShell) + { + m_pWrtShell->EndAllAction(); + if (bUnlockView) + { + m_pWrtShell->LockView( false ); + } + } +} + +static void lcl_processCompatibleSfxHint( const uno::Reference< script::vba::XVBAEventProcessor >& xVbaEvents, const SfxHint& rHint ) +{ + using namespace com::sun::star::script::vba::VBAEventId; + if ( const SfxEventHint* pSfxEventHint = dynamic_cast<const SfxEventHint*>(&rHint) ) + { + uno::Sequence< uno::Any > aArgs; + switch( pSfxEventHint->GetEventId() ) + { + case SfxEventHintId::CreateDoc: + xVbaEvents->processVbaEvent( DOCUMENT_NEW, aArgs ); + break; + case SfxEventHintId::OpenDoc: + xVbaEvents->processVbaEvent( DOCUMENT_OPEN, aArgs ); + break; + default: break; + } + } +} + +// Notification on DocInfo changes +void SwDocShell::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if (!m_xDoc) + { + return ; + } + + uno::Reference< script::vba::XVBAEventProcessor > const xVbaEvents = + m_xDoc->GetVbaEventProcessor(); + if( xVbaEvents.is() ) + lcl_processCompatibleSfxHint( xVbaEvents, rHint ); + + if ( const SfxEventHint* pSfxEventHint = dynamic_cast<const SfxEventHint*>(&rHint) ) + { + switch( pSfxEventHint->GetEventId() ) + { + case SfxEventHintId::ActivateDoc: + case SfxEventHintId::CreateDoc: + case SfxEventHintId::OpenDoc: + { + uno::Sequence< css::uno::Any > aArgs; + SW_MOD()->CallAutomationApplicationEventSinks( "DocumentChange", aArgs ); + break; + } + default: + break; + } + + switch( pSfxEventHint->GetEventId() ) + { + case SfxEventHintId::CreateDoc: + { + uno::Any aDocument; + aDocument <<= mxAutomationDocumentObject; + uno::Sequence< uno::Any > aArgs(1); + aArgs[0] = aDocument; + SW_MOD()->CallAutomationApplicationEventSinks( "NewDocument", aArgs ); + } + break; + case SfxEventHintId::OpenDoc: + { + uno::Any aDocument; + aDocument <<= mxAutomationDocumentObject; + uno::Sequence< uno::Any > aArgs(1); + aArgs[0] = aDocument; + SW_MOD()->CallAutomationApplicationEventSinks( "DocumentOpen", aArgs ); + } + break; + default: + break; + } + } + + sal_uInt16 nAction = 0; + auto pEventHint = dynamic_cast<const SfxEventHint*>(&rHint); + if( pEventHint && pEventHint->GetEventId() == SfxEventHintId::LoadFinished ) + { + // #i38126# - own action id + nAction = 3; + } + else + { + // switch for more actions + if( rHint.GetId() == SfxHintId::TitleChanged) + { + if( GetMedium() ) + nAction = 2; + } + } + + if( nAction ) + { + bool bUnlockView = true; //initializing prevents warning + if (m_pWrtShell) + { + bUnlockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); //lock visible section + m_pWrtShell->StartAllAction(); + } + switch( nAction ) + { + case 2: + m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Filename )->UpdateFields(); + break; + // #i38126# - own action for event LOADFINISHED + // in order to avoid a modified document. + // #i41679# - Also for the instance of <SwDoc> + // it has to be assured, that it's not modified. + // Perform the same as for action id 1, but disable <SetModified>. + case 3: + { + const bool bResetModified = IsEnableSetModified(); + if ( bResetModified ) + EnableSetModified( false ); + // #i41679# + const bool bIsDocModified = m_xDoc->getIDocumentState().IsModified(); + // TODO: is the ResetModified() below because of only the direct call from DocInfoChgd, or does UpdateFields() set it too? + + m_xDoc->getIDocumentStatistics().DocInfoChgd(false); + + // #i41679# + if ( !bIsDocModified ) + m_xDoc->getIDocumentState().ResetModified(); + if ( bResetModified ) + EnableSetModified(); + } + break; + } + + if (m_pWrtShell) + { + m_pWrtShell->EndAllAction(); + if( bUnlockView ) + m_pWrtShell->LockView( false ); + } + } +} + +// Notification Close Doc +bool SwDocShell::PrepareClose( bool bUI ) +{ + bool bRet = SfxObjectShell::PrepareClose( bUI ); + + // If we are going to close it at this point, let potential DocumentBeforeClose event handlers + // in Automation clients veto it. + if (bRet && m_xDoc && IsInPrepareClose()) + { + uno::Any aDocument; + aDocument <<= mxAutomationDocumentObject; + + uno::Sequence< uno::Any > aArgs(2); + // Arg 0: Document + aArgs[0] = aDocument; + // Arg 1: Cancel + aArgs[1] <<= false; + + SW_MOD()->CallAutomationApplicationEventSinks( "DocumentBeforeClose", aArgs ); + + // If the Cancel argument was set to True by an event handler, return false. + bool bCancel(false); + aArgs[1] >>= bCancel; + if (bCancel) + bRet = false; + } + + if( bRet ) + EndListening( *this ); + + if (m_xDoc && IsInPrepareClose()) + { + uno::Reference< script::vba::XVBAEventProcessor > const xVbaEvents = + m_xDoc->GetVbaEventProcessor(); + if( xVbaEvents.is() ) + { + using namespace com::sun::star::script::vba::VBAEventId; + uno::Sequence< uno::Any > aNoArgs; + xVbaEvents->processVbaEvent( DOCUMENT_CLOSE, aNoArgs ); + } + } + return bRet; +} + +void SwDocShell::Execute(SfxRequest& rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + sal_uInt16 nWhich = rReq.GetSlot(); + bool bDone = false; + switch ( nWhich ) + { + case SID_AUTO_CORRECT_DLG: + { + SvxSwAutoFormatFlags* pAFlags = &SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags(); + SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); + + bool bOldLocked = rACW.IsLockWordLstLocked(), + bOldAutoCmpltCollectWords = pAFlags->bAutoCmpltCollectWords; + + rACW.SetLockWordLstLocked( true ); + + editeng::SortedAutoCompleteStrings aTmpLst( rACW.GetWordList().createNonOwningCopy() ); + pAFlags->m_pAutoCompleteList = &aTmpLst; + + SfxApplication* pApp = SfxGetpApp(); + SfxRequest aAppReq(SID_AUTO_CORRECT_DLG, SfxCallMode::SYNCHRON, pApp->GetPool()); + SfxBoolItem aSwOptions( SID_AUTO_CORRECT_DLG, true ); + aAppReq.AppendItem(aSwOptions); + + pAFlags->pSmartTagMgr = &SwSmartTagMgr::Get(); + + SfxItemSet aSet( pApp->GetPool(), svl::Items<SID_AUTO_CORRECT_DLG, SID_AUTO_CORRECT_DLG, SID_OPEN_SMARTTAGOPTIONS, SID_OPEN_SMARTTAGOPTIONS>{} ); + aSet.Put( aSwOptions ); + + const SfxPoolItem* pOpenSmartTagOptionsItem = nullptr; + if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_OPEN_SMARTTAGOPTIONS, false, &pOpenSmartTagOptionsItem ) ) + aSet.Put( *static_cast<const SfxBoolItem*>(pOpenSmartTagOptionsItem) ); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + VclPtr<SfxAbstractTabDialog> pDlg = pFact->CreateAutoCorrTabDialog(GetView()->GetFrameWeld(), &aSet); + pDlg->Execute(); + pDlg.disposeAndClear(); + + + rACW.SetLockWordLstLocked( bOldLocked ); + + SwEditShell::SetAutoFormatFlags( pAFlags ); + rACW.SetMinWordLen( pAFlags->nAutoCmpltWordLen ); + rACW.SetMaxCount( pAFlags->nAutoCmpltListLen ); + if (pAFlags->m_pAutoCompleteList) // any changes? + { + rACW.CheckChangedList( aTmpLst ); + // clear the temp WordList pointer + pAFlags->m_pAutoCompleteList = nullptr; + } + + if( !bOldAutoCmpltCollectWords && bOldAutoCmpltCollectWords != + pAFlags->bAutoCmpltCollectWords ) + { + // call on all Docs the idle formatter to start + // the collection of Words + for( SwDocShell *pDocSh = static_cast<SwDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<SwDocShell>)); + pDocSh; + pDocSh = static_cast<SwDocShell*>(SfxObjectShell::GetNext( *pDocSh, checkSfxObjectShell<SwDocShell> )) ) + { + SwDoc* pTmp = pDocSh->GetDoc(); + if ( pTmp->getIDocumentLayoutAccess().GetCurrentViewShell() ) + pTmp->InvalidateAutoCompleteFlag(); + } + } + } + break; + + case SID_PRINTPREVIEW: + { + bool bSet = false; + bool bFound = false, bOnly = true; + SfxViewFrame *pTmpFrame = SfxViewFrame::GetFirst(this); + SfxViewShell* pViewShell = SfxViewShell::Current(); + SwView* pCurrView = dynamic_cast< SwView *> ( pViewShell ); + bool bCurrent = typeid(SwPagePreview) == typeid( pViewShell ); + + while( pTmpFrame ) // search Preview + { + if( typeid(SwView) == typeid( pTmpFrame->GetViewShell()) ) + bOnly = false; + else if( typeid(SwPagePreview) == typeid( pTmpFrame->GetViewShell())) + { + pTmpFrame->GetFrame().Appear(); + bFound = true; + } + if( bFound && !bOnly ) + break; + pTmpFrame = SfxViewFrame::GetNext(*pTmpFrame, this); + } + + if( pArgs && SfxItemState::SET == + pArgs->GetItemState( SID_PRINTPREVIEW, false, &pItem )) + bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + else + bSet = !bCurrent; + + sal_uInt16 nSlotId = 0; + if( bSet && !bFound ) // Nothing found, so create new Preview + nSlotId = SID_VIEWSHELL1; + else if( bFound && !bSet ) + nSlotId = bOnly ? SID_VIEWSHELL0 : SID_VIEWSHELL1; + + if( nSlotId ) + { + // PagePreview in the WebDocShell + // is found under Id VIEWSHELL2. + if( dynamic_cast< const SwWebDocShell *>( this ) != nullptr && SID_VIEWSHELL1 == nSlotId ) + nSlotId = SID_VIEWSHELL2; + + if( pCurrView && pCurrView->GetDocShell() == this ) + pTmpFrame = pCurrView->GetViewFrame(); + else + pTmpFrame = SfxViewFrame::GetFirst( this ); + + if (pTmpFrame) + pTmpFrame->GetDispatcher()->Execute( nSlotId, SfxCallMode::ASYNCHRON ); + } + + rReq.SetReturnValue(SfxBoolItem(SID_PRINTPREVIEW, bSet )); + } + break; + case SID_TEMPLATE_LOAD: + { + OUString aFileName; + static bool bText = true; + static bool bFrame = false; + static bool bPage = false; + static bool bNum = false; + static bool bMerge = false; + sal_uInt16 nRet = USHRT_MAX; + + SfxTemplateFlags nFlags = bFrame ? SfxTemplateFlags::LOAD_FRAME_STYLES : SfxTemplateFlags::NONE; + if(bPage) + nFlags |= SfxTemplateFlags::LOAD_PAGE_STYLES; + if(bNum) + nFlags |= SfxTemplateFlags::LOAD_NUM_STYLES; + if(nFlags == SfxTemplateFlags::NONE || bText) + nFlags |= SfxTemplateFlags::LOAD_TEXT_STYLES; + if(bMerge) + nFlags |= SfxTemplateFlags::MERGE_STYLES; + + if ( pArgs ) + { + const SfxStringItem* pTemplateItem = rReq.GetArg<SfxStringItem>(SID_TEMPLATE_NAME); + if ( pTemplateItem ) + { + aFileName = pTemplateItem->GetValue(); + const SfxInt32Item* pFlagsItem = rReq.GetArg<SfxInt32Item>(SID_TEMPLATE_LOAD); + if ( pFlagsItem ) + nFlags = static_cast<SfxTemplateFlags>(static_cast<sal_uInt16>(pFlagsItem->GetValue())); + } + } + + if ( aFileName.isEmpty() ) + { + SvtPathOptions aPathOpt; + SfxNewFileDialog aNewFileDlg(GetView()->GetFrameWeld(), SfxNewFileDialogMode::LoadTemplate); + aNewFileDlg.SetTemplateFlags(nFlags); + + nRet = aNewFileDlg.run(); + if(RET_TEMPLATE_LOAD == nRet) + { + FileDialogHelper aDlgHelper(TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, GetView()->GetFrameWeld()); + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + xFP->setDisplayDirectory( aPathOpt.GetWorkPath() ); + + SfxObjectFactory &rFact = GetFactory(); + SfxFilterMatcher aMatcher( rFact.GetFactoryName() ); + SfxFilterMatcherIter aIter( aMatcher ); + std::shared_ptr<const SfxFilter> pFlt = aIter.First(); + while( pFlt ) + { + // --> OD #i117339# + if( pFlt && pFlt->IsAllowedAsTemplate() && + ( pFlt->GetUserData() == "CXML" || + pFlt->GetUserData() == "CXMLV" ) ) + { + const OUString sWild = pFlt->GetWildcard().getGlob(); + xFP->appendFilter( pFlt->GetUIName(), sWild ); + } + pFlt = aIter.Next(); + } + bool bWeb = dynamic_cast< SwWebDocShell *>( this ) != nullptr; + std::shared_ptr<const SfxFilter> pOwnFlt = + SwDocShell::Factory().GetFilterContainer()-> + GetFilter4FilterName("writer8"); + + // make sure the default file format is also available + if(bWeb) + { + const OUString sWild = pOwnFlt->GetWildcard().getGlob(); + xFP->appendFilter( pOwnFlt->GetUIName(), sWild ); + } + + bool bError = false; + // catch exception if wrong filter is selected - should not happen anymore + try + { + xFP->setCurrentFilter( pOwnFlt->GetUIName() ); + } + catch (const uno::Exception&) + { + bError = true; + } + + if( !bError && ERRCODE_NONE == aDlgHelper.Execute() ) + { + aFileName = xFP->getSelectedFiles().getConstArray()[0]; + } + } + else if( RET_OK == nRet) + { + aFileName = aNewFileDlg.GetTemplateFileName(); + } + + nFlags = aNewFileDlg.GetTemplateFlags(); + rReq.AppendItem( SfxStringItem( SID_TEMPLATE_NAME, aFileName ) ); + rReq.AppendItem( SfxInt32Item( SID_TEMPLATE_LOAD, static_cast<long>(nFlags) ) ); + } + + if( !aFileName.isEmpty() ) + { + SwgReaderOption aOpt; + bText = bool(nFlags & SfxTemplateFlags::LOAD_TEXT_STYLES ); + aOpt.SetTextFormats(bText); + bFrame = bool(nFlags & SfxTemplateFlags::LOAD_FRAME_STYLES); + aOpt.SetFrameFormats(bFrame); + bPage = bool(nFlags & SfxTemplateFlags::LOAD_PAGE_STYLES ); + aOpt.SetPageDescs(bPage); + bNum = bool(nFlags & SfxTemplateFlags::LOAD_NUM_STYLES ); + aOpt.SetNumRules(bNum); + //different meaning between SFX_MERGE_STYLES and aOpt.SetMerge! + bMerge = bool(nFlags & SfxTemplateFlags::MERGE_STYLES); + aOpt.SetMerge( !bMerge ); + + SetError(LoadStylesFromFile(aFileName, aOpt, false)); + if ( !GetError() ) + rReq.Done(); + } + } + break; + case SID_SOURCEVIEW: + { + SfxViewShell* pViewShell = GetView() + ? static_cast<SfxViewShell*>(GetView()) + : SfxViewShell::Current(); + SfxViewFrame* pViewFrame = pViewShell->GetViewFrame(); + SwSrcView* pSrcView = dynamic_cast< SwSrcView *>( pViewShell ); + if(!pSrcView) + { + // 3 possible state: + // 1 - file unsaved -> save as HTML + // 2 - file modified and HTML filter active -> save + // 3 - file saved in non-HTML -> QueryBox to save as HTML + std::shared_ptr<const SfxFilter> pHtmlFlt = + SwIoSystem::GetFilterOfFormat( + "HTML", + SwWebDocShell::Factory().GetFilterContainer() ); + bool bLocalHasName = HasName(); + if(bLocalHasName) + { + //check for filter type + std::shared_ptr<const SfxFilter> pFlt = GetMedium()->GetFilter(); + if(!pFlt || pFlt->GetUserData() != pHtmlFlt->GetUserData()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pViewFrame->GetWindow().GetFrameWeld(), "modules/swriter/ui/saveashtmldialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("SaveAsHTMLDialog")); + if (RET_YES == xQuery->run()) + bLocalHasName = false; + else + break; + } + } + if(!bLocalHasName) + { + FileDialogHelper aDlgHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, + GetView()->GetFrameWeld()); + aDlgHelper.AddFilter( pHtmlFlt->GetFilterName(), pHtmlFlt->GetDefaultExtension() ); + aDlgHelper.SetCurrentFilter( pHtmlFlt->GetFilterName() ); + if( ERRCODE_NONE != aDlgHelper.Execute()) + { + break; + } + OUString sPath = aDlgHelper.GetPath(); + SfxStringItem aName(SID_FILE_NAME, sPath); + SfxStringItem aFilter(SID_FILTER_NAME, pHtmlFlt->GetName()); + const SfxBoolItem* pBool = static_cast<const SfxBoolItem*>( + pViewFrame->GetDispatcher()->ExecuteList( + SID_SAVEASDOC, SfxCallMode::SYNCHRON, + { &aName, &aFilter })); + if(!pBool || !pBool->GetValue()) + break; + } + } + + OSL_ENSURE(dynamic_cast<SwWebDocShell*>(this), + "SourceView only in WebDocShell"); + + // the SourceView is not the 1 for SwWebDocShell + sal_uInt16 nSlot = SID_VIEWSHELL1; + bool bSetModified = false; + VclPtr<SfxPrinter> pSavePrinter; + if( nullptr != pSrcView) + { + SfxPrinter* pTemp = GetDoc()->getIDocumentDeviceAccess().getPrinter( false ); + if(pTemp) + pSavePrinter = VclPtr<SfxPrinter>::Create(*pTemp); + bSetModified = IsModified() || pSrcView->IsModified(); + if(pSrcView->IsModified()||pSrcView->HasSourceSaved()) + { + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + pSrcView->SaveContent(aTempFile.GetURL()); + bDone = true; + SvxMacro aMac(OUString(), OUString(), STARBASIC); + SfxEventConfiguration::ConfigureEvent(GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC ), aMac, this); + SfxEventConfiguration::ConfigureEvent(GlobalEventConfig::GetEventName( GlobalEventId::PREPARECLOSEDOC ), aMac, this); + SfxEventConfiguration::ConfigureEvent(GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ), aMac, this); + SfxEventConfiguration::ConfigureEvent(GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ), aMac, this); + ReloadFromHtml(aTempFile.GetURL(), pSrcView); + nSlot = 0; + } + else + { + nSlot = SID_VIEWSHELL0; + } + } + if(nSlot) + pViewFrame->GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON); + if(bSetModified) + GetDoc()->getIDocumentState().SetModified(); + if(pSavePrinter) + { + GetDoc()->getIDocumentDeviceAccess().setPrinter( pSavePrinter, true, true); + //pSavePrinter must not be deleted again + } + pViewFrame->GetBindings().SetState(SfxBoolItem(SID_SOURCEVIEW, false)); // not SID_VIEWSHELL2 + pViewFrame->GetBindings().Invalidate( SID_NEWWINDOW ); + pViewFrame->GetBindings().Invalidate( SID_BROWSER_MODE ); + pViewFrame->GetBindings().Invalidate( FN_PRINT_LAYOUT ); + } + break; + case SID_GET_COLORLIST: + { + const SvxColorListItem* pColItem = GetItem(SID_COLOR_TABLE); + const XColorListRef& pList = pColItem->GetColorList(); + rReq.SetReturnValue(OfaRefItem<XColorList>(SID_GET_COLORLIST, pList)); + } + break; + case FN_ABSTRACT_STARIMPRESS: + case FN_ABSTRACT_NEWDOC: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwInsertAbstractDlg> pDlg(pFact->CreateSwInsertAbstractDlg(GetView()->GetFrameWeld())); + if(RET_OK == pDlg->Execute()) + { + sal_uInt8 nLevel = pDlg->GetLevel(); + sal_uInt8 nPara = pDlg->GetPara(); + SwDoc* pSmryDoc = new SwDoc(); + SfxObjectShellLock xDocSh( new SwDocShell( pSmryDoc, SfxObjectCreateMode::STANDARD)); + xDocSh->DoInitNew(); + + bool bImpress = FN_ABSTRACT_STARIMPRESS == nWhich; + m_xDoc->Summary( pSmryDoc, nLevel, nPara, bImpress ); + if( bImpress ) + { + WriterRef xWrt; + // mba: looks as if relative URLs don't make sense here + ::GetRTFWriter(OUString(), OUString(), xWrt); + SvMemoryStream *pStrm = new SvMemoryStream(); + pStrm->SetBufferSize( 16348 ); + SwWriter aWrt( *pStrm, *pSmryDoc ); + ErrCode eErr = aWrt.Write( xWrt ); + if( !eErr.IgnoreWarning() ) + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< frame::XDispatchProvider > xProv = drawing::ModuleDispatcher::create( xContext ); + + uno::Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) ); + pStrm->Seek( STREAM_SEEK_TO_END ); + pStrm->WriteChar( '\0' ); + pStrm->Seek( STREAM_SEEK_TO_BEGIN ); + + // Transfer ownership of stream to a lockbytes object + SvLockBytes aLockBytes( pStrm, true ); + SvLockBytesStat aStat; + if ( aLockBytes.Stat( &aStat ) == ERRCODE_NONE ) + { + sal_uInt32 nLen = aStat.nSize; + std::size_t nRead = 0; + uno::Sequence< sal_Int8 > aSeq( nLen ); + aLockBytes.ReadAt( 0, aSeq.getArray(), nLen, &nRead ); + + uno::Sequence< beans::PropertyValue > aArgs(1); + aArgs[0].Name = "RtfOutline"; + aArgs[0].Value <<= aSeq; + xHelper->executeDispatch( xProv, "SendOutlineToImpress", OUString(), 0, aArgs ); + } + } + else + ErrorHandler::HandleError( eErr ); + } + else + { + // Create new document + SfxViewFrame *pFrame = SfxViewFrame::LoadDocument( *xDocSh, SFX_INTERFACE_NONE ); + SwView *pCurrView = static_cast<SwView*>( pFrame->GetViewShell()); + + // Set document's title + OUString aTmp = SwResId(STR_ABSTRACT_TITLE) + GetTitle(); + xDocSh->SetTitle( aTmp ); + pCurrView->GetWrtShell().SetNewDoc(); + pFrame->Show(); + pSmryDoc->getIDocumentState().SetModified(); + } + + } + } + break; + case FN_OUTLINE_TO_CLIPBOARD: + case FN_OUTLINE_TO_IMPRESS: + { + bool bEnable = IsEnableSetModified(); + EnableSetModified( false ); + WriterRef xWrt; + // mba: looks as if relative URLs don't make sense here + ::GetRTFWriter( OUString('O'), OUString(), xWrt ); + std::unique_ptr<SvMemoryStream> pStrm (new SvMemoryStream()); + pStrm->SetBufferSize( 16348 ); + SwWriter aWrt( *pStrm, *GetDoc() ); + ErrCode eErr = aWrt.Write( xWrt ); + EnableSetModified( bEnable ); + if( !eErr.IgnoreWarning() ) + { + pStrm->Seek( STREAM_SEEK_TO_END ); + pStrm->WriteChar( '\0' ); + pStrm->Seek( STREAM_SEEK_TO_BEGIN ); + if ( nWhich == FN_OUTLINE_TO_IMPRESS ) + { + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< frame::XDispatchProvider > xProv = drawing::ModuleDispatcher::create( xContext ); + + uno::Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create(xContext) ); + pStrm->Seek( STREAM_SEEK_TO_END ); + pStrm->WriteChar( '\0' ); + pStrm->Seek( STREAM_SEEK_TO_BEGIN ); + + // Transfer ownership of stream to a lockbytes object + SvLockBytes aLockBytes( pStrm.release(), true ); + SvLockBytesStat aStat; + if ( aLockBytes.Stat( &aStat ) == ERRCODE_NONE ) + { + sal_uInt32 nLen = aStat.nSize; + std::size_t nRead = 0; + uno::Sequence< sal_Int8 > aSeq( nLen ); + aLockBytes.ReadAt( 0, aSeq.getArray(), nLen, &nRead ); + + uno::Sequence< beans::PropertyValue > aArgs(1); + aArgs[0].Name = "RtfOutline"; + aArgs[0].Value <<= aSeq; + xHelper->executeDispatch( xProv, "SendOutlineToImpress", OUString(), 0, aArgs ); + } + } + else + { + rtl::Reference<TransferDataContainer> pClipCntnr = new TransferDataContainer; + + pClipCntnr->CopyAnyData( SotClipboardFormatId::RTF, static_cast<char const *>( + pStrm->GetData()), pStrm->GetEndOfData() ); + pClipCntnr->CopyToClipboard( + GetView()? &GetView()->GetEditWin() : nullptr ); + } + } + else + ErrorHandler::HandleError( eErr ); + } + break; + case SID_SPELLCHECKER_CHANGED: + //! false, true, true is on the save side but a probably overdone + SwModule::CheckSpellChanges(false, true, true, false ); + break; + + case SID_MAIL_PREPAREEXPORT: + { + //pWrtShell is not set in page preview + if (m_pWrtShell) + m_pWrtShell->StartAllAction(); + m_xDoc->getIDocumentFieldsAccess().UpdateFields( false ); + m_xDoc->getIDocumentLinksAdministration().EmbedAllLinks(); + m_IsRemovedInvisibleContent + = officecfg::Office::Security::HiddenContent::RemoveHiddenContent::get(); + if (m_IsRemovedInvisibleContent) + m_xDoc->RemoveInvisibleContent(); + if (m_pWrtShell) + m_pWrtShell->EndAllAction(); + } + break; + + case SID_MAIL_EXPORT_FINISHED: + { + if (m_pWrtShell) + m_pWrtShell->StartAllAction(); + //try to undo the removal of invisible content + if (m_IsRemovedInvisibleContent) + m_xDoc->RestoreInvisibleContent(); + if (m_pWrtShell) + m_pWrtShell->EndAllAction(); + } + break; + case FN_NEW_HTML_DOC: + case FN_NEW_GLOBAL_DOC: + { + bDone = false; + bool bCreateHtml = FN_NEW_HTML_DOC == nWhich; + + bool bCreateByOutlineLevel = false; + sal_Int32 nTemplateOutlineLevel = 0; + + OUString aFileName, aTemplateName; + if( pArgs && SfxItemState::SET == pArgs->GetItemState( nWhich, false, &pItem ) ) + { + aFileName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SfxStringItem* pTemplItem = SfxItemSet::GetItem<SfxStringItem>(pArgs, SID_TEMPLATE_NAME, false); + if ( pTemplItem ) + aTemplateName = pTemplItem->GetValue(); + } + if ( aFileName.isEmpty() ) + { + bool bError = false; + + FileDialogHelper aDlgHelper(TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE, FileDialogFlags::NONE, + GetView()->GetFrameWeld()); + + const sal_Int16 nControlIds[] = { + CommonFilePickerElementIds::PUSHBUTTON_OK, + CommonFilePickerElementIds::PUSHBUTTON_CANCEL, + CommonFilePickerElementIds::LISTBOX_FILTER, + CommonFilePickerElementIds::CONTROL_FILEVIEW, + CommonFilePickerElementIds::EDIT_FILEURL, + ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, + ExtendedFilePickerElementIds::LISTBOX_TEMPLATE, + 0 + }; + + if (bCreateHtml) + { + const char* aHTMLHelpIds[] = + { + HID_SEND_HTML_CTRL_PUSHBUTTON_OK, + HID_SEND_HTML_CTRL_PUSHBUTTON_CANCEL, + HID_SEND_HTML_CTRL_LISTBOX_FILTER, + HID_SEND_HTML_CTRL_CONTROL_FILEVIEW, + HID_SEND_HTML_CTRL_EDIT_FILEURL, + HID_SEND_HTML_CTRL_CHECKBOX_AUTOEXTENSION, + HID_SEND_HTML_CTRL_LISTBOX_TEMPLATE, + "" + }; + aDlgHelper.SetControlHelpIds( nControlIds, aHTMLHelpIds ); + } + else + { + const char* aMasterHelpIds[] = + { + HID_SEND_MASTER_CTRL_PUSHBUTTON_OK, + HID_SEND_MASTER_CTRL_PUSHBUTTON_CANCEL, + HID_SEND_MASTER_CTRL_LISTBOX_FILTER, + HID_SEND_MASTER_CTRL_CONTROL_FILEVIEW, + HID_SEND_MASTER_CTRL_EDIT_FILEURL, + HID_SEND_MASTER_CTRL_CHECKBOX_AUTOEXTENSION, + HID_SEND_MASTER_CTRL_LISTBOX_TEMPLATE, + "" + }; + aDlgHelper.SetControlHelpIds( nControlIds, aMasterHelpIds ); + } + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + std::shared_ptr<const SfxFilter> pFlt; + const char* pStrId; + + if( bCreateHtml ) + { + // for HTML there is only one filter!! + pFlt = SwIoSystem::GetFilterOfFormat( + "HTML", + SwWebDocShell::Factory().GetFilterContainer() ); + pStrId = STR_LOAD_HTML_DOC; + } + else + { + // for Global-documents we now only offer the current one. + pFlt = SwGlobalDocShell::Factory().GetFilterContainer()-> + GetFilter4Extension( "odm" ); + pStrId = STR_LOAD_GLOBAL_DOC; + } + + if( pFlt ) + { + const OUString sWild = pFlt->GetWildcard().getGlob(); + xFP->appendFilter( pFlt->GetUIName(), sWild ); + try + { + xFP->setCurrentFilter( pFlt->GetUIName() ) ; + } + catch (const uno::Exception&) + { + bError = true; + } + } + if(!bError) + { + uno::Reference<XFilePickerControlAccess> xCtrlAcc(xFP, UNO_QUERY); + + bool bOutline[MAXLEVEL] = {false}; + const SwOutlineNodes& rOutlNds = m_xDoc->GetNodes().GetOutLineNds(); + for( size_t n = 0; n < rOutlNds.size(); ++n ) + { + const int nLevel = rOutlNds[n]->GetTextNode()->GetAttrOutlineLevel(); + if( nLevel > 0 && ! bOutline[nLevel-1] ) + { + bOutline[nLevel-1] = true; + } + } + + const sal_uInt16 nStyleCount = m_xDoc->GetTextFormatColls()->size(); + Sequence<OUString> aListBoxEntries( MAXLEVEL + nStyleCount); + OUString* pEntries = aListBoxEntries.getArray(); + sal_Int32 nIdx = 0 ; + + OUString sOutline( SwResId(STR_FDLG_OUTLINE_LEVEL) ); + for( sal_uInt16 i = 0; i < MAXLEVEL; ++i ) + { + if( bOutline[i] ) + pEntries[nIdx++] = sOutline + OUString::number( i+1 ); + } + + OUString sStyle( SwResId(STR_FDLG_STYLE) ); + for(sal_uInt16 i = 0; i < nStyleCount; ++i) + { + SwTextFormatColl &rTextColl = *(*m_xDoc->GetTextFormatColls())[ i ]; + if( !rTextColl.IsDefault() && rTextColl.IsAtDocNodeSet() ) + { + pEntries[nIdx++] = sStyle + rTextColl.GetName(); + } + } + + aListBoxEntries.realloc(nIdx); + sal_Int16 nSelect = 0; + + try + { + Any aTemplates(&aListBoxEntries, cppu::UnoType<decltype(aListBoxEntries)>::get()); + + xCtrlAcc->setValue( ExtendedFilePickerElementIds::LISTBOX_TEMPLATE, + ListboxControlActions::ADD_ITEMS , aTemplates ); + Any aSelectPos(&nSelect, cppu::UnoType<decltype(nSelect)>::get()); + xCtrlAcc->setValue( ExtendedFilePickerElementIds::LISTBOX_TEMPLATE, + ListboxControlActions::SET_SELECT_ITEM, aSelectPos ); + xCtrlAcc->setLabel( ExtendedFilePickerElementIds::LISTBOX_TEMPLATE, + SwResId( STR_FDLG_TEMPLATE_NAME )); + } + catch (const Exception&) + { + OSL_FAIL("control access failed"); + } + + xFP->setTitle(SwResId(pStrId)); + SvtPathOptions aPathOpt; + xFP->setDisplayDirectory( aPathOpt.GetWorkPath() ); + if( ERRCODE_NONE == aDlgHelper.Execute()) + { + aFileName = xFP->getSelectedFiles().getConstArray()[0]; + Any aTemplateValue = xCtrlAcc->getValue( + ExtendedFilePickerElementIds::LISTBOX_TEMPLATE, + ListboxControlActions::GET_SELECTED_ITEM ); + OUString sTmpl; + aTemplateValue >>= sTmpl; + + OUString aStyle(SwResId(STR_FDLG_STYLE)); + OUString aOutline(SwResId(STR_FDLG_OUTLINE_LEVEL)); + + if ( sTmpl.startsWith(aStyle) ) + { + aTemplateName = sTmpl.copy( aStyle.getLength() ); //get string behind "Style: " + } + else if ( sTmpl.startsWith(aOutline) ) + { + nTemplateOutlineLevel = sTmpl.copy(aOutline.getLength()).toInt32(); //get string behind "Outline: Level "; + bCreateByOutlineLevel = true; + } + + if ( !aFileName.isEmpty() ) + { + rReq.AppendItem( SfxStringItem( nWhich, aFileName ) ); + if( !aTemplateName.isEmpty() ) + rReq.AppendItem( SfxStringItem( SID_TEMPLATE_NAME, aTemplateName ) ); + } + } + } + } + + if( !aFileName.isEmpty() ) + { + if( PrepareClose( false ) ) + { + SwWait aWait( *this, true ); + + if ( bCreateByOutlineLevel ) + { + bDone = bCreateHtml + ? m_xDoc->GenerateHTMLDoc( aFileName, nTemplateOutlineLevel ) + : m_xDoc->GenerateGlobalDoc( aFileName, nTemplateOutlineLevel ); + } + else + { + const SwTextFormatColl* pSplitColl = nullptr; + if ( !aTemplateName.isEmpty() ) + pSplitColl = m_xDoc->FindTextFormatCollByName(aTemplateName); + bDone = bCreateHtml + ? m_xDoc->GenerateHTMLDoc( aFileName, pSplitColl ) + : m_xDoc->GenerateGlobalDoc( aFileName, pSplitColl ); + } + if( bDone ) + { + SfxStringItem aName( SID_FILE_NAME, aFileName ); + SfxStringItem aReferer(SID_REFERER, OUString()); + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while(pViewShell) + { + //search for the view that created the call + if(pViewShell->GetObjectShell() == this && pViewShell->GetDispatcher()) + { + std::unique_ptr<SfxFrameItem> pFrameItem(new SfxFrameItem( SID_DOCFRAME, + pViewShell->GetViewFrame() )); + SfxDispatcher* pDispatch = pViewShell->GetDispatcher(); + pDispatch->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON, + { &aName, &aReferer, pFrameItem.get() }); + break; + } + pViewShell = SfxViewShell::GetNext(*pViewShell); + } + } + } + if( !bDone && !rReq.IsAPI() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_CANTCREATE))); + xInfoBox->run(); + } + } + } + rReq.SetReturnValue(SfxBoolItem( nWhich, bDone )); + if (bDone) + rReq.Done(); + else + rReq.Ignore(); + break; + + case SID_ATTR_YEAR2000: + if ( pArgs && SfxItemState::SET == pArgs->GetItemState( nWhich , false, &pItem )) + { + OSL_ENSURE(dynamic_cast< const SfxUInt16Item *>( pItem ) != nullptr, "wrong Item"); + sal_uInt16 nYear2K = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + // iterate over Views and put the State to FormShells + + SfxViewFrame* pVFrame = SfxViewFrame::GetFirst( this ); + SfxViewShell* pViewShell = pVFrame ? pVFrame->GetViewShell() : nullptr; + SwView* pCurrView = dynamic_cast< SwView* >( pViewShell ); + while(pCurrView) + { + FmFormShell* pFormShell = pCurrView->GetFormShell(); + if(pFormShell) + pFormShell->SetY2KState(nYear2K); + pVFrame = SfxViewFrame::GetNext( *pVFrame, this ); + pViewShell = pVFrame ? pVFrame->GetViewShell() : nullptr; + pCurrView = dynamic_cast<SwView*>( pViewShell ); + } + m_xDoc->GetNumberFormatter()->SetYear2000(nYear2K); + } + break; + case FN_OPEN_FILE: + { + SfxViewShell* pViewShell = GetView(); + if (!pViewShell) + pViewShell = SfxViewShell::Current(); + + if (!pViewShell) + // Ok. I did my best. + break; + + SfxStringItem aApp(SID_DOC_SERVICE, "com.sun.star.text.TextDocument"); + SfxStringItem aTarget(SID_TARGETNAME, "_blank"); + pViewShell->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::API|SfxCallMode::SYNCHRON, + { &aApp, &aTarget }); + } + break; + case SID_CLASSIFICATION_APPLY: + { + if (pArgs && pArgs->GetItemState(nWhich, false, &pItem) == SfxItemState::SET) + { + SwWrtShell* pSh = GetWrtShell(); + const OUString& rValue = static_cast<const SfxStringItem*>(pItem)->GetValue(); + auto eType = SfxClassificationPolicyType::IntellectualProperty; + if (pArgs->GetItemState(SID_TYPE_NAME, false, &pItem) == SfxItemState::SET) + { + const OUString& rType = static_cast<const SfxStringItem*>(pItem)->GetValue(); + eType = SfxClassificationHelper::stringToPolicyType(rType); + } + pSh->SetClassification(rValue, eType); + } + else + SAL_WARN("sw.ui", "missing parameter for SID_CLASSIFICATION_APPLY"); + } + break; + case SID_CLASSIFICATION_DIALOG: + { + auto xDialog = std::make_shared<svx::ClassificationDialog>(GetView()->GetFrameWeld(), false); + + SwWrtShell* pShell = GetWrtShell(); + std::vector<svx::ClassificationResult> aInput = pShell->CollectAdvancedClassification(); + xDialog->setupValues(aInput); + + weld::DialogController::runAsync(xDialog, [xDialog, pShell](sal_Int32 nResult){ + if (RET_OK == nResult) + pShell->ApplyAdvancedClassification(xDialog->getResult()); + }); + } + break; + case SID_PARAGRAPH_SIGN_CLASSIFY_DLG: + { + SwWrtShell* pShell = GetWrtShell(); + auto xDialog = std::make_shared<svx::ClassificationDialog>(GetView()->GetFrameWeld(), true, [pShell]() + { + pShell->SignParagraph(); + }); + + std::vector<svx::ClassificationResult> aInput = pShell->CollectParagraphClassification(); + xDialog->setupValues(aInput); + + weld::DialogController::runAsync(xDialog, [xDialog, pShell](sal_Int32 nResult){ + if (RET_OK == nResult) + pShell->ApplyParagraphClassification(xDialog->getResult()); + }); + } + break; + case SID_WATERMARK: + { + SwWrtShell* pSh = GetWrtShell(); + if ( pSh ) + { + if (pArgs && pArgs->GetItemState( SID_WATERMARK, false, &pItem ) == SfxItemState::SET) + { + SfxWatermarkItem aItem; + aItem.SetText( static_cast<const SfxStringItem*>( pItem )->GetValue() ); + + if ( pArgs->GetItemState( SID_WATERMARK_FONT, false, &pItem ) == SfxItemState::SET ) + aItem.SetFont( static_cast<const SfxStringItem*>( pItem )->GetValue() ); + if ( pArgs->GetItemState( SID_WATERMARK_ANGLE, false, &pItem ) == SfxItemState::SET ) + aItem.SetAngle( static_cast<const SfxInt16Item*>( pItem )->GetValue() ); + if ( pArgs->GetItemState( SID_WATERMARK_TRANSPARENCY, false, &pItem ) == SfxItemState::SET ) + aItem.SetTransparency( static_cast<const SfxInt16Item*>( pItem )->GetValue() ); + if ( pArgs->GetItemState( SID_WATERMARK_COLOR, false, &pItem ) == SfxItemState::SET ) + aItem.SetColor( Color(static_cast<const SfxUInt32Item*>( pItem )->GetValue()) ); + + pSh->SetWatermark( aItem ); + } + else + { + SfxViewShell* pViewShell = GetView() ? GetView() : SfxViewShell::Current(); + SfxBindings& rBindings( pViewShell->GetViewFrame()->GetBindings() ); + auto xDlg = std::make_shared<SwWatermarkDialog>(pViewShell->GetViewFrame()->GetWindow().GetFrameWeld(), + rBindings); + weld::DialogController::runAsync(xDlg, [](sal_Int32 /*nResult*/){}); + } + } + } + break; + case SID_NOTEBOOKBAR: + { + const SfxStringItem* pFile = rReq.GetArg<SfxStringItem>( SID_NOTEBOOKBAR ); + SfxViewShell* pViewShell = GetView()? GetView(): SfxViewShell::Current(); + SfxBindings& rBindings( pViewShell->GetViewFrame()->GetBindings() ); + + if ( SfxNotebookBar::IsActive() ) + sfx2::SfxNotebookBar::ExecMethod( rBindings, pFile ? pFile->GetValue() : "" ); + else + { + sfx2::SfxNotebookBar::CloseMethod( rBindings ); + } + } + break; + case FN_REDLINE_ACCEPT_ALL: + case FN_REDLINE_REJECT_ALL: + { + IDocumentRedlineAccess& rRedlineAccess = GetDoc()->getIDocumentRedlineAccess(); + SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>(GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()); + + if (rRedlineAccess.GetRedlineTable().empty()) + { + break; + } + + if (pWrtShell) + { + pWrtShell->StartAllAction(); + } + + rRedlineAccess.AcceptAllRedline(nWhich == FN_REDLINE_ACCEPT_ALL); + + if (pWrtShell) + { + pWrtShell->EndAllAction(); + } + + Broadcast(SfxHint(SfxHintId::RedlineChanged)); + rReq.Done(); + } + break; + + default: OSL_FAIL("wrong Dispatcher"); + } +} + +#if defined(_WIN32) +bool SwDocShell::DdeGetData( const OUString& rItem, const OUString& rMimeType, + uno::Any & rValue ) +{ + return m_xDoc->getIDocumentLinksAdministration().GetData( rItem, rMimeType, rValue ); +} + +bool SwDocShell::DdeSetData( const OUString& rItem, const OUString& /*rMimeType*/, + const uno::Any & /*rValue*/ ) +{ + m_xDoc->getIDocumentLinksAdministration().SetData( rItem ); + return false; +} + +#endif + +::sfx2::SvLinkSource* SwDocShell::DdeCreateLinkSource( const OUString& rItem ) +{ + return m_xDoc->getIDocumentLinksAdministration().CreateLinkSource( rItem ); +} + +void SwDocShell::ReconnectDdeLink(SfxObjectShell& rServer) +{ + if (m_xDoc) + { + ::sfx2::LinkManager& rLinkManager = m_xDoc->getIDocumentLinksAdministration().GetLinkManager(); + rLinkManager.ReconnectDdeLink(rServer); + } +} + +void SwDocShell::FillClass( SvGlobalName * pClassName, + SotClipboardFormatId * pClipFormat, + OUString * pLongUserName, + sal_Int32 nVersion, + bool bTemplate /* = false */) const +{ + if (nVersion == SOFFICE_FILEFORMAT_60) + { + *pClassName = SvGlobalName( SO3_SW_CLASSID_60 ); + *pClipFormat = SotClipboardFormatId::STARWRITER_60; + *pLongUserName = SwResId(STR_WRITER_DOCUMENT_FULLTYPE); + } + else if (nVersion == SOFFICE_FILEFORMAT_8) + { + *pClassName = SvGlobalName( SO3_SW_CLASSID_60 ); + *pClipFormat = bTemplate ? SotClipboardFormatId::STARWRITER_8_TEMPLATE : SotClipboardFormatId::STARWRITER_8; + *pLongUserName = SwResId(STR_WRITER_DOCUMENT_FULLTYPE); + } +// #FIXME check with new Event handling +#if 0 + uno::Reference< document::XVbaEventsHelper > xVbaEventsHelper = m_xDoc->GetVbaEventsHelper(); + if( xVbaEventsHelper.is() ) + lcl_processCompatibleSfxHint( xVbaEventsHelper, rHint ); +#endif +} + +void SwDocShell::SetModified( bool bSet ) +{ + if (utl::ConfigManager::IsFuzzing()) + return; + SfxObjectShell::SetModified( bSet ); + if( IsEnableSetModified()) + { + if (!m_xDoc->getIDocumentState().IsInCallModified()) + { + EnableSetModified( false ); + if( bSet ) + { + bool const bOld = m_xDoc->getIDocumentState().IsModified(); + m_xDoc->getIDocumentState().SetModified(); + if( !bOld ) + { + m_xDoc->GetIDocumentUndoRedo().SetUndoNoResetModified(); + } + } + else + m_xDoc->getIDocumentState().ResetModified(); + + EnableSetModified(); + } + + UpdateChildWindows(); + Broadcast(SfxHint(SfxHintId::DocChanged)); + } +} + +void SwDocShell::UpdateChildWindows() +{ + // if necessary newly initialize Fielddlg (i.e. for TYP_SETVAR) + if(!GetView()) + return; + SfxViewFrame* pVFrame = GetView()->GetViewFrame(); + SwFieldDlgWrapper *pWrp = static_cast<SwFieldDlgWrapper*>(pVFrame-> + GetChildWindow( SwFieldDlgWrapper::GetChildWindowId() )); + if( pWrp ) + pWrp->ReInitDlg( this ); + + // if necessary newly initialize RedlineDlg + SwRedlineAcceptChild *pRed = static_cast<SwRedlineAcceptChild*>(pVFrame-> + GetChildWindow( SwRedlineAcceptChild::GetChildWindowId() )); + if( pRed ) + pRed->ReInitDlg( this ); +} + +namespace { + +// #i48748# +class SwReloadFromHtmlReader : public SwReader +{ + public: + SwReloadFromHtmlReader( SfxMedium& _rTmpMedium, + const OUString& _rFilename, + SwDoc* _pDoc ) + : SwReader( _rTmpMedium, _rFilename, _pDoc ) + { + SetBaseURL( _rFilename ); + } +}; + +} + +void SwDocShell::ReloadFromHtml( const OUString& rStreamName, SwSrcView* pSrcView ) +{ + bool bModified = IsModified(); + + // The HTTP-Header fields have to be removed, otherwise + // there are some from Meta-Tags duplicated or triplicated afterwards. + ClearHeaderAttributesForSourceViewHack(); + +#if HAVE_FEATURE_SCRIPTING + // The Document-Basic also bites the dust ... + // A EnterBasicCall is not needed here, because nothing is called and + // there can't be any Dok-Basic, that has not yet been loaded inside + // of an HTML document. + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + //#59620# HasBasic() shows, that there already is a BasicManager at the DocShell. + // That was always generated in HTML-Import, when there are + // Macros in the source code. + if( rHtmlOptions.IsStarBasic() && HasBasic()) + { + BasicManager *pBasicMan = GetBasicManager(); + if( pBasicMan && (pBasicMan != SfxApplication::GetBasicManager()) ) + { + sal_uInt16 nLibCount = pBasicMan->GetLibCount(); + while( nLibCount ) + { + StarBASIC *pBasic = pBasicMan->GetLib( --nLibCount ); + if( pBasic ) + { + // Notify the IDE + SfxUnoAnyItem aShellItem( SID_BASICIDE_ARG_DOCUMENT_MODEL, makeAny( GetModel() ) ); + OUString aLibName( pBasic->GetName() ); + SfxStringItem aLibNameItem( SID_BASICIDE_ARG_LIBNAME, aLibName ); + pSrcView->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_BASICIDE_LIBREMOVED, + SfxCallMode::SYNCHRON, + { &aShellItem, &aLibNameItem }); + + // Only the modules are deleted from the standard-lib + if( nLibCount ) + pBasicMan->RemoveLib( nLibCount, true ); + else + pBasic->Clear(); + } + } + + OSL_ENSURE( pBasicMan->GetLibCount() <= 1, + "Deleting Basics didn't work" ); + } + } +#endif + bool bWasBrowseMode = m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE); + RemoveLink(); + + // now also the UNO-Model has to be informed about the new Doc #51535# + uno::Reference<text::XTextDocument> xDoc(GetBaseModel(), uno::UNO_QUERY); + text::XTextDocument* pxDoc = xDoc.get(); + static_cast<SwXTextDocument*>(pxDoc)->InitNewDoc(); + + AddLink(); + //#116402# update font list when new document is created + UpdateFontList(); + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::BROWSE_MODE, bWasBrowseMode); + pSrcView->SetPool(&GetPool()); + + const OUString& rMedname = GetMedium()->GetName(); + + // The HTML template still has to be set + SetHTMLTemplate( *GetDoc() ); //Styles from HTML.vor + + SfxViewShell* pViewShell = GetView() ? static_cast<SfxViewShell*>(GetView()) + : SfxViewShell::Current(); + SfxViewFrame* pViewFrame = pViewShell->GetViewFrame(); + pViewFrame->GetDispatcher()->Execute( SID_VIEWSHELL0, SfxCallMode::SYNCHRON ); + + SubInitNew(); + + SfxMedium aMed( rStreamName, StreamMode::READ ); + // #i48748# - use class <SwReloadFromHtmlReader>, because + // the base URL has to be set to the filename of the document <rMedname> + // and not to the base URL of the temporary file <aMed> in order to get + // the URLs of the linked graphics correctly resolved. + SwReloadFromHtmlReader aReader( aMed, rMedname, m_xDoc.get() ); + + aReader.Read( *ReadHTML ); + + const SwView* pCurrView = GetView(); + //in print layout the first page(s) may have been formatted as a mix of browse + //and print layout + if(!bWasBrowseMode && pCurrView) + { + SwWrtShell& rWrtSh = pCurrView->GetWrtShell(); + if( rWrtSh.GetLayout()) + rWrtSh.InvalidateLayout( true ); + } + + // Take HTTP-Header-Attributes over into the DocInfo again. + // The Base-URL doesn't matter here because TLX uses the one from the document + // for absolutization. + SetHeaderAttributesForSourceViewHack(); + + if(bModified && !IsReadOnly()) + SetModified(); + else + m_xDoc->getIDocumentState().ResetModified(); +} + +ErrCode SwDocShell::LoadStylesFromFile(const OUString& rURL, SwgReaderOption& rOpt, bool bUnoCall) +{ + ErrCode nErr = ERRCODE_NONE; + + // Set filter: + SfxFilterMatcher aMatcher( SwDocShell::Factory().GetFactoryName() ); + + // search for filter in WebDocShell, too + SfxMedium aMed( rURL, StreamMode::STD_READ ); + if (rURL == "private:stream") + aMed.setStreamToLoadFrom(rOpt.GetInputStream(), true); + std::shared_ptr<const SfxFilter> pFlt; + aMatcher.DetectFilter( aMed, pFlt ); + if(!pFlt) + { + SfxFilterMatcher aWebMatcher( SwWebDocShell::Factory().GetFactoryName() ); + aWebMatcher.DetectFilter( aMed, pFlt ); + } + // --> OD #i117339# - trigger import only for own formats + bool bImport( false ); + if ( aMed.IsStorage() ) + { + // As <SfxMedium.GetFilter().IsOwnFormat() resp. IsOwnTemplateFormat() + // does not work correct (e.g., MS Word 2007 XML Template), + // use workaround provided by MAV. + uno::Reference< embed::XStorage > xStorage = aMed.GetStorage(); + if ( xStorage.is() ) + { + // use <try-catch> on retrieving <MediaType> in order to check, + // if the storage is one of our own ones. + try + { + uno::Reference< beans::XPropertySet > xProps( xStorage, uno::UNO_QUERY_THROW ); + const OUString aMediaTypePropName( "MediaType" ); + xProps->getPropertyValue( aMediaTypePropName ); + bImport = true; + } + catch (const uno::Exception&) + { + bImport = false; + } + } + } + if ( bImport ) + { + Reader* pRead = ReadXML; + SwReaderPtr pReader; + std::unique_ptr<SwPaM> pPam; + // the SW3IO - Reader need the pam/wrtshell, because only then he + // insert the styles! + if( bUnoCall ) + { + SwNodeIndex aIdx( m_xDoc->GetNodes().GetEndOfContent(), -1 ); + pPam.reset(new SwPaM( aIdx )); + pReader.reset(new SwReader( aMed, rURL, *pPam )); + } + else + { + pReader.reset(new SwReader( aMed, rURL, *m_pWrtShell->GetCursor() )); + } + + pRead->GetReaderOpt().SetTextFormats( rOpt.IsTextFormats() ); + pRead->GetReaderOpt().SetFrameFormats( rOpt.IsFrameFormats() ); + pRead->GetReaderOpt().SetPageDescs( rOpt.IsPageDescs() ); + pRead->GetReaderOpt().SetNumRules( rOpt.IsNumRules() ); + pRead->GetReaderOpt().SetMerge( rOpt.IsMerge() ); + + if( bUnoCall ) + { + UnoActionContext aAction( m_xDoc.get() ); + nErr = pReader->Read( *pRead ); + } + else + { + m_pWrtShell->StartAllAction(); + nErr = pReader->Read( *pRead ); + m_pWrtShell->EndAllAction(); + } + } + + return nErr; +} + +// Get a client for an embedded object if possible. +SfxInPlaceClient* SwDocShell::GetIPClient( const ::svt::EmbeddedObjectRef& xObjRef ) +{ + SfxInPlaceClient* pResult = nullptr; + + SwWrtShell* pShell = GetWrtShell(); + if ( pShell ) + { + pResult = pShell->GetView().FindIPClient( xObjRef.GetObject(), &pShell->GetView().GetEditWin() ); + if ( !pResult ) + pResult = new SwOleClient( &pShell->GetView(), &pShell->GetView().GetEditWin(), xObjRef ); + } + + return pResult; +} + +int SwFindDocShell( SfxObjectShellRef& xDocSh, + SfxObjectShellLock& xLockRef, + const OUString& rFileName, + const OUString& rPasswd, + const OUString& rFilter, + sal_Int16 nVersion, + SwDocShell* pDestSh ) +{ + if ( rFileName.isEmpty() ) + return 0; + + // 1. Does the file already exist in the list of all Documents? + INetURLObject aTmpObj( rFileName ); + aTmpObj.SetMark( OUString() ); + + // Iterate over the DocShell and get the ones with the name + + SfxObjectShell* pShell = pDestSh; + bool bFirst = nullptr != pShell; + + if( !bFirst ) + // No DocShell passed, starting with the first from the DocShell list + pShell = SfxObjectShell::GetFirst( checkSfxObjectShell<SwDocShell> ); + + while( pShell ) + { + // We want this one + SfxMedium* pMed = pShell->GetMedium(); + if( pMed && pMed->GetURLObject() == aTmpObj ) + { + const SfxPoolItem* pItem; + if( ( SfxItemState::SET == pMed->GetItemSet()->GetItemState( + SID_VERSION, false, &pItem ) ) + ? (nVersion == static_cast<const SfxInt16Item*>(pItem)->GetValue()) + : !nVersion ) + { + // Found, thus return + xDocSh = pShell; + return 1; + } + } + + if( bFirst ) + { + bFirst = false; + pShell = SfxObjectShell::GetFirst( checkSfxObjectShell<SwDocShell> ); + } + else + pShell = SfxObjectShell::GetNext( *pShell, checkSfxObjectShell<SwDocShell> ); + } + + // 2. Open the file ourselves + std::unique_ptr<SfxMedium> xMed(new SfxMedium( aTmpObj.GetMainURL( + INetURLObject::DecodeMechanism::NONE ), StreamMode::READ )); + if( INetProtocol::File == aTmpObj.GetProtocol() ) + xMed->Download(); // Touch the medium (download it) + + std::shared_ptr<const SfxFilter> pSfxFlt; + if (!xMed->GetError()) + { + SfxFilterMatcher aMatcher( rFilter == "writerglobal8" + ? SwGlobalDocShell::Factory().GetFactoryName() + : SwDocShell::Factory().GetFactoryName() ); + + // No Filter, so search for it. Else test if the one passed is a valid one + if( !rFilter.isEmpty() ) + { + pSfxFlt = aMatcher.GetFilter4FilterName( rFilter ); + } + + if( nVersion ) + xMed->GetItemSet()->Put( SfxInt16Item( SID_VERSION, nVersion )); + + if( !rPasswd.isEmpty() ) + xMed->GetItemSet()->Put( SfxStringItem( SID_PASSWORD, rPasswd )); + + if( !pSfxFlt ) + aMatcher.DetectFilter( *xMed, pSfxFlt ); + + if( pSfxFlt ) + { + // We cannot do anything without a Filter + xMed->SetFilter( pSfxFlt ); + + // If the new shell is created, SfxObjectShellLock should be used to let it be closed later for sure + SwDocShell *const pNew(new SwDocShell(SfxObjectCreateMode::INTERNAL)); + xLockRef = pNew; + xDocSh = static_cast<SfxObjectShell*>(xLockRef); + if (xDocSh->DoLoad(xMed.release())) + { + return 2; + } + } + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docshdrw.cxx b/sw/source/uibase/app/docshdrw.cxx new file mode 100644 index 000000000..e9a8b2e31 --- /dev/null +++ b/sw/source/uibase/app/docshdrw.cxx @@ -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 . + */ + +#include <svx/svxids.hrc> +#include <svx/drawitem.hxx> +#include <svx/svdoutl.hxx> +#include <svx/xtable.hxx> +#include <unotools/configmgr.hxx> +#include <docsh.hxx> +#include <drawdoc.hxx> +#include <swtypes.hxx> + +using namespace ::com::sun::star; + +// Load Document +void InitDrawModelAndDocShell(SwDocShell* pSwDocShell, SwDrawModel* pSwDrawDocument) +{ + if(pSwDrawDocument) + { + if(pSwDocShell == pSwDrawDocument->GetObjectShell()) + { + // association already done, nothing to do + } + else + { + // set object shell (mainly for FormControl stuff), maybe zero + pSwDrawDocument->SetObjectShell(pSwDocShell); + + // set persist, maybe zero + pSwDrawDocument->SetPersist(pSwDocShell); + + // get and decide on the color table to use + if(pSwDocShell) + { + const SvxColorListItem* pColItemFromDocShell = pSwDocShell->GetItem(SID_COLOR_TABLE); + + if(pColItemFromDocShell) + { + // the DocShell has a ColorTable, use it also in DrawingLayer + const XColorListRef& xCol(pColItemFromDocShell->GetColorList()); + pSwDrawDocument->SetPropertyList(static_cast<XPropertyList*>(xCol.get())); + } + else + { + // Use the ColorTable which is used at the DrawingLayer's SdrModel + XColorListRef xColorList = pSwDrawDocument->GetColorList(); + if (xColorList.is()) + { + pSwDocShell->PutItem(SvxColorListItem(xColorList, SID_COLOR_TABLE)); + } + else if (!utl::ConfigManager::IsFuzzing()) + { + // there wasn't one, get the standard and set to the + // docshell and then to the drawdocument + xColorList = XColorList::GetStdColorList(); + pSwDocShell->PutItem(SvxColorListItem(xColorList, SID_COLOR_TABLE)); + pSwDrawDocument->SetPropertyList(xColorList.get()); + } + } + + // add other tables in SfxItemSet of the DocShell + pSwDocShell->PutItem(SvxGradientListItem(pSwDrawDocument->GetGradientList(), SID_GRADIENT_LIST)); + pSwDocShell->PutItem(SvxHatchListItem(pSwDrawDocument->GetHatchList(), SID_HATCH_LIST)); + pSwDocShell->PutItem(SvxBitmapListItem(pSwDrawDocument->GetBitmapList(), SID_BITMAP_LIST)); + pSwDocShell->PutItem(SvxPatternListItem(pSwDrawDocument->GetPatternList(), SID_PATTERN_LIST)); + pSwDocShell->PutItem(SvxDashListItem(pSwDrawDocument->GetDashList(), SID_DASH_LIST)); + pSwDocShell->PutItem(SvxLineEndListItem(pSwDrawDocument->GetLineEndList(), SID_LINEEND_LIST)); + } + + // init hyphenator for DrawingLayer outliner + uno::Reference<linguistic2::XHyphenator> xHyphenator(::GetHyphenator()); + Outliner& rOutliner = pSwDrawDocument->GetDrawOutliner(); + + rOutliner.SetHyphenator(xHyphenator); + } + } + else if(pSwDocShell) + { + // fallback: add the default color list to have one when someone requests it from the DocShell + pSwDocShell->PutItem(SvxColorListItem(XColorList::GetStdColorList(), SID_COLOR_TABLE)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docshini.cxx b/sw/source/uibase/app/docshini.cxx new file mode 100644 index 000000000..eb2452150 --- /dev/null +++ b/sw/source/uibase/app/docshini.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 <osl/diagnose.h> +#include <sal/log.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <svtools/ctrltool.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/lingucfg.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <sfx2/printer.hxx> +#include <svl/asiancfg.hxx> +#include <svl/intitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/autokernitem.hxx> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <svx/svxids.hrc> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <prtopt.hxx> +#include <fmtcol.hxx> +#include <docsh.hxx> +#include <wdocsh.hxx> +#include <swmodule.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentChartDataProviderAccess.hxx> +#include <IDocumentState.hxx> +#include <docfac.hxx> +#include <docstyle.hxx> +#include <shellio.hxx> +#include <swdtflvr.hxx> +#include <usrpref.hxx> +#include <fontcfg.hxx> +#include <poolfmt.hxx> +#include <globdoc.hxx> +#include <unotxdoc.hxx> +#include <linkenum.hxx> +#include <swwait.hxx> +#include <swerror.h> +#include <unochart.hxx> +#include <drawdoc.hxx> +#include <DocumentSettingManager.hxx> + +#include <svx/CommonStyleManager.hxx> + +#include <memory> + +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +// Load Document +bool SwDocShell::InitNew( const uno::Reference < embed::XStorage >& xStor ) +{ + bool bRet = SfxObjectShell::InitNew( xStor ); + OSL_ENSURE( GetMapUnit() == MapUnit::MapTwip, "map unit is not twip!" ); + bool bHTMLTemplSet = false; + if( bRet ) + { + AddLink(); // create m_xDoc / pIo if applicable + + bool bWeb = dynamic_cast< const SwWebDocShell *>( this ) != nullptr; + if ( bWeb ) + bHTMLTemplSet = SetHTMLTemplate( *GetDoc() );// Styles from HTML.vor + else if( dynamic_cast< const SwGlobalDocShell *>( this ) != nullptr ) + GetDoc()->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT, true); // Globaldokument + + if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + SwTransferable::InitOle( this ); + + // set forbidden characters if necessary + if (!utl::ConfigManager::IsFuzzing()) + { + SvxAsianConfig aAsian; + const Sequence<lang::Locale> aLocales = aAsian.GetStartEndCharLocales(); + for(const lang::Locale& rLocale : aLocales) + { + ForbiddenCharacters aForbidden; + aAsian.GetStartEndChars( rLocale, aForbidden.beginLine, aForbidden.endLine); + LanguageType eLang = LanguageTag::convertToLanguageType(rLocale); + m_xDoc->getIDocumentSettingAccess().setForbiddenCharacters( eLang, aForbidden); + } + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, + !aAsian.IsKerningWesternTextOnly()); + m_xDoc->getIDocumentSettingAccess().setCharacterCompressionType(aAsian.GetCharDistanceCompression()); + m_xDoc->getIDocumentDeviceAccess().setPrintData(*SW_MOD()->GetPrtOptions(bWeb)); + } + + SubInitNew(); + + // for all + + SwStdFontConfig* pStdFont = SW_MOD()->GetStdFontConfig(); + SfxPrinter* pPrt = m_xDoc->getIDocumentDeviceAccess().getPrinter( false ); + + OUString sEntry; + static const sal_uInt16 aFontWhich[] = + { RES_CHRATR_FONT, + RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT + }; + static const sal_uInt16 aFontHeightWhich[] = + { + RES_CHRATR_FONTSIZE, + RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CTL_FONTSIZE + }; + static const sal_uInt16 aFontIds[] = + { + FONT_STANDARD, + FONT_STANDARD_CJK, + FONT_STANDARD_CTL + }; + static const DefaultFontType nFontTypes[] = + { + DefaultFontType::LATIN_TEXT, + DefaultFontType::CJK_TEXT, + DefaultFontType::CTL_TEXT + }; + static const sal_uInt16 aLangTypes[] = + { + RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE + }; + + for(sal_uInt8 i = 0; i < 3; i++) + { + sal_uInt16 nFontWhich = aFontWhich[i]; + sal_uInt16 nFontId = aFontIds[i]; + std::unique_ptr<SvxFontItem> pFontItem; + const SvxLanguageItem& rLang = static_cast<const SvxLanguageItem&>(m_xDoc->GetDefault( aLangTypes[i] )); + LanguageType eLanguage = rLang.GetLanguage(); + if(!pStdFont->IsFontDefault(nFontId)) + { + sEntry = pStdFont->GetFontFor(nFontId); + + vcl::Font aFont( sEntry, Size( 0, 10 ) ); + if( pPrt ) + { + aFont = pPrt->GetFontMetric( aFont ); + } + + pFontItem.reset(new SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(), + OUString(), aFont.GetPitch(), aFont.GetCharSet(), nFontWhich)); + } + else + { + // #107782# OJ use korean language if latin was used + if ( i == 0 ) + { + LanguageType eUiLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType(); + if (MsLangId::isKorean(eUiLanguage)) + eLanguage = eUiLanguage; + } + + vcl::Font aLangDefFont = OutputDevice::GetDefaultFont( + nFontTypes[i], + eLanguage, + GetDefaultFontFlags::OnlyOne ); + pFontItem.reset(new SvxFontItem(aLangDefFont.GetFamilyType(), aLangDefFont.GetFamilyName(), + OUString(), aLangDefFont.GetPitch(), aLangDefFont.GetCharSet(), nFontWhich)); + } + m_xDoc->SetDefault(*pFontItem); + if( !bHTMLTemplSet ) + { + SwTextFormatColl *pColl = m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD); + pColl->ResetFormatAttr(nFontWhich); + } + pFontItem.reset(); + sal_Int32 nFontHeight = pStdFont->GetFontHeight( FONT_STANDARD, i, eLanguage ); + if(nFontHeight <= 0) + nFontHeight = SwStdFontConfig::GetDefaultHeightFor( nFontId, eLanguage ); + m_xDoc->SetDefault(SvxFontHeightItem( nFontHeight, 100, aFontHeightWhich[i] )); + if( !bHTMLTemplSet ) + { + SwTextFormatColl *pColl = m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD); + pColl->ResetFormatAttr(aFontHeightWhich[i]); + } + + } + sal_uInt16 aFontIdPoolId[] = + { + FONT_OUTLINE, RES_POOLCOLL_HEADLINE_BASE, + FONT_LIST, RES_POOLCOLL_NUMBER_BULLET_BASE, + FONT_CAPTION, RES_POOLCOLL_LABEL, + FONT_INDEX, RES_POOLCOLL_REGISTER_BASE, + FONT_OUTLINE_CJK, RES_POOLCOLL_HEADLINE_BASE, + FONT_LIST_CJK, RES_POOLCOLL_NUMBER_BULLET_BASE, + FONT_CAPTION_CJK, RES_POOLCOLL_LABEL, + FONT_INDEX_CJK, RES_POOLCOLL_REGISTER_BASE, + FONT_OUTLINE_CTL, RES_POOLCOLL_HEADLINE_BASE, + FONT_LIST_CTL, RES_POOLCOLL_NUMBER_BULLET_BASE, + FONT_CAPTION_CTL, RES_POOLCOLL_LABEL, + FONT_INDEX_CTL, RES_POOLCOLL_REGISTER_BASE + }; + + sal_uInt16 nFontWhich = RES_CHRATR_FONT; + sal_uInt16 nFontHeightWhich = RES_CHRATR_FONTSIZE; + LanguageType eLanguage = m_xDoc->GetDefault( RES_CHRATR_LANGUAGE ).GetLanguage(); + for(sal_uInt8 nIdx = 0; nIdx < 24; nIdx += 2) + { + if(nIdx == 8) + { + nFontWhich = RES_CHRATR_CJK_FONT; + nFontHeightWhich = RES_CHRATR_CJK_FONTSIZE; + eLanguage = m_xDoc->GetDefault( RES_CHRATR_CJK_LANGUAGE ).GetLanguage(); + } + else if(nIdx == 16) + { + nFontWhich = RES_CHRATR_CTL_FONT; + nFontHeightWhich = RES_CHRATR_CTL_FONTSIZE; + eLanguage = m_xDoc->GetDefault( RES_CHRATR_CTL_LANGUAGE ).GetLanguage(); + } + SwTextFormatColl *pColl = nullptr; + if(!pStdFont->IsFontDefault(aFontIdPoolId[nIdx])) + { + sEntry = pStdFont->GetFontFor(aFontIdPoolId[nIdx]); + + vcl::Font aFont( sEntry, Size( 0, 10 ) ); + if( pPrt ) + aFont = pPrt->GetFontMetric( aFont ); + + pColl = m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(aFontIdPoolId[nIdx + 1]); + if( !bHTMLTemplSet || + SfxItemState::SET != pColl->GetAttrSet().GetItemState( + nFontWhich, false ) ) + { + pColl->SetFormatAttr(SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(), + OUString(), aFont.GetPitch(), aFont.GetCharSet(), nFontWhich)); + } + } + sal_Int32 nFontHeight = pStdFont->GetFontHeight( static_cast< sal_Int8 >(aFontIdPoolId[nIdx]), 0, eLanguage ); + if(nFontHeight <= 0) + nFontHeight = SwStdFontConfig::GetDefaultHeightFor( aFontIdPoolId[nIdx], eLanguage ); + if(!pColl) + pColl = m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(aFontIdPoolId[nIdx + 1]); + SvxFontHeightItem aFontHeight( static_cast<const SvxFontHeightItem&>(pColl->GetFormatAttr( nFontHeightWhich ))); + if(aFontHeight.GetHeight() != sal::static_int_cast<sal_uInt32, sal_Int32>(nFontHeight)) + { + aFontHeight.SetHeight(nFontHeight); + pColl->SetFormatAttr( aFontHeight ); + } + } + + // the default for documents created via 'File/New' should be 'on' + // (old documents, where this property was not yet implemented, will get the + // value 'false' in the SwDoc c-tor) + m_xDoc->getIDocumentSettingAccess().set( DocumentSettingId::MATH_BASELINE_ALIGNMENT, + SW_MOD()->GetUsrPref( bWeb )->IsAlignMathObjectsToBaseline() ); + } + + /* #106748# If the default frame direction of a document is RTL + the default adjustment is to the right. */ + if( !bHTMLTemplSet && + SvxFrameDirection::Horizontal_RL_TB == GetDefaultFrameDirection(GetAppLanguage()) ) + { + m_xDoc->SetDefault( SvxAdjustItem(SvxAdjust::Right, RES_PARATR_ADJUST ) ); + } + +// #i29550# + m_xDoc->SetDefault( SfxBoolItem( RES_COLLAPSING_BORDERS, true ) ); +// <-- collapsing + + //#i16874# AutoKerning as default for new documents + m_xDoc->SetDefault( SvxAutoKernItem( true, RES_CHRATR_AUTOKERN ) ); + + // #i42080# - Due to the several calls of method <SetDefault(..)> + // at the document instance, the document is modified. Thus, reset this + // status here. Note: In method <SubInitNew()> this is also done. + m_xDoc->getIDocumentState().ResetModified(); + + return bRet; +} + +// Ctor with SfxCreateMode ????? +SwDocShell::SwDocShell( SfxObjectCreateMode const eMode ) + : SfxObjectShell(eMode) + , m_IsInUpdateFontList(false) + , m_pStyleManager(new svx::CommonStyleManager(*this)) + , m_pView(nullptr) + , m_pWrtShell(nullptr) + , m_nUpdateDocMode(document::UpdateDocMode::ACCORDING_TO_CONFIG) + , m_IsATemplate(false) + , m_IsRemovedInvisibleContent(false) +{ + Init_Impl(); +} + +// Ctor / Dtor +SwDocShell::SwDocShell( const SfxModelFlags i_nSfxCreationFlags ) + : SfxObjectShell ( i_nSfxCreationFlags ) + , m_IsInUpdateFontList(false) + , m_pStyleManager(new svx::CommonStyleManager(*this)) + , m_pView(nullptr) + , m_pWrtShell(nullptr) + , m_nUpdateDocMode(document::UpdateDocMode::ACCORDING_TO_CONFIG) + , m_IsATemplate(false) + , m_IsRemovedInvisibleContent(false) +{ + Init_Impl(); +} + +// Ctor / Dtor +SwDocShell::SwDocShell( SwDoc *const pD, SfxObjectCreateMode const eMode ) + : SfxObjectShell(eMode) + , m_xDoc(pD) + , m_IsInUpdateFontList(false) + , m_pStyleManager(new svx::CommonStyleManager(*this)) + , m_pView(nullptr) + , m_pWrtShell(nullptr) + , m_nUpdateDocMode(document::UpdateDocMode::ACCORDING_TO_CONFIG) + , m_IsATemplate(false) + , m_IsRemovedInvisibleContent(false) +{ + Init_Impl(); +} + +// Dtor +SwDocShell::~SwDocShell() +{ + // disable chart related objects now because in ~SwDoc it may be too late for this + if (m_xDoc) + { + m_xDoc->getIDocumentChartDataProviderAccess().GetChartControllerHelper().Disconnect(); + SwChartDataProvider *pPCD = m_xDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD) + pPCD->dispose(); + } + + RemoveLink(); + m_pFontList.reset(); + + // we, as BroadCaster also become our own Listener + // (for DocInfo/FileNames/...) + EndListening( *this ); + + m_pOLEChildList.reset(); +} + +void SwDocShell::Init_Impl() +{ + SetPool(&SW_MOD()->GetPool()); + SetBaseModel(new SwXTextDocument(this)); + // we, as BroadCaster also become our own Listener + // (for DocInfo/FileNames/...) + StartListening( *this ); + //position of the "Automatic" style filter for the stylist (app.src) + SetAutoStyleFilterIndex(3); + + // set map unit to twip + SetMapUnit( MapUnit::MapTwip ); +} + +void SwDocShell::AddLink() +{ + if (!m_xDoc) + { + SwDocFac aFactory; + m_xDoc = aFactory.GetDoc(); + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, dynamic_cast< const SwWebDocShell *>( this ) != nullptr ); + } + m_xDoc->SetDocShell( this ); // set the DocShell-Pointer for Doc + uno::Reference< text::XTextDocument > xDoc(GetBaseModel(), uno::UNO_QUERY); + static_cast<SwXTextDocument*>(xDoc.get())->Reactivate(this); + + SetPool(&m_xDoc->GetAttrPool()); + + // most suitably not until a sdbcx::View is created!!! + m_xDoc->SetOle2Link(LINK(this, SwDocShell, Ole2ModifiedHdl)); +} + +// create new FontList Change Printer +void SwDocShell::UpdateFontList() +{ + if (!m_IsInUpdateFontList) + { + m_IsInUpdateFontList = true; + OSL_ENSURE(m_xDoc, "No Doc no FontList"); + if (m_xDoc) + { + m_pFontList.reset( new FontList( m_xDoc->getIDocumentDeviceAccess().getReferenceDevice(true) ) ); + PutItem( SvxFontListItem( m_pFontList.get(), SID_ATTR_CHAR_FONTLIST ) ); + } + m_IsInUpdateFontList = false; + } +} + +void SwDocShell::RemoveLink() +{ + // disconnect Uno-Object + uno::Reference< text::XTextDocument > xDoc(GetBaseModel(), uno::UNO_QUERY); + static_cast<SwXTextDocument*>(xDoc.get())->Invalidate(); + if (m_xDoc) + { + if (m_xBasePool.is()) + { + static_cast<SwDocStyleSheetPool*>(m_xBasePool.get())->dispose(); + m_xBasePool.clear(); + } + m_xDoc->SetOle2Link(Link<bool,void>()); + m_xDoc->SetDocShell( nullptr ); + m_xDoc.clear(); // we don't have the Doc anymore!! + } +} +void SwDocShell::InvalidateModel() +{ + // disconnect Uno-Object + uno::Reference< text::XTextDocument > xDoc(GetBaseModel(), uno::UNO_QUERY); + static_cast<SwXTextDocument*>(xDoc.get())->Invalidate(); +} +void SwDocShell::ReactivateModel() +{ + // disconnect Uno-Object + uno::Reference< text::XTextDocument > xDoc(GetBaseModel(), uno::UNO_QUERY); + static_cast<SwXTextDocument*>(xDoc.get())->Reactivate(this); +} + +// Load, Default-Format +bool SwDocShell::Load( SfxMedium& rMedium ) +{ + bool bRet = false; + + if (SfxObjectShell::Load(rMedium)) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); + + SAL_INFO( "sw.ui", "after SfxInPlaceObject::Load" ); + if (m_xDoc) // for last version!! + RemoveLink(); // release the existing + + AddLink(); // set Link and update Data!! + + // Define some settings for legacy ODF files that have different default values now + // (if required, they will be overridden later when settings will be read) + if (IsOwnStorageFormat(rMedium)) + { + // legacy processing for tdf#99729 + if (m_xDoc->getIDocumentDrawModelAccess().GetDrawModel()) + m_xDoc->getIDocumentDrawModelAccess().GetDrawModel()->SetAnchoredTextOverflowLegacy( + true); + // legacy behaviour (not hiding paragraph) for Database (MailMerge) fields + m_xDoc->GetDocumentSettingManager().set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, + false); + } + + // Loading + // for MD + OSL_ENSURE( !m_xBasePool.is(), "who hasn't destroyed their Pool?" ); + m_xBasePool = new SwDocStyleSheetPool( *m_xDoc, SfxObjectCreateMode::ORGANIZER == GetCreateMode() ); + if(GetCreateMode() != SfxObjectCreateMode::ORGANIZER) + { + const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(rMedium.GetItemSet(), SID_UPDATEDOCMODE, false); + m_nUpdateDocMode = pUpdateDocItem ? pUpdateDocItem->GetValue() : document::UpdateDocMode::NO_UPDATE; + } + + SwWait aWait( *this, true ); + ErrCode nErr = ERR_SWG_READ_ERROR; + switch( GetCreateMode() ) + { + case SfxObjectCreateMode::ORGANIZER: + { + if( ReadXML ) + { + ReadXML->SetOrganizerMode( true ); + SwReader aRdr(rMedium, OUString(), m_xDoc.get()); + nErr = aRdr.Read( *ReadXML ); + ReadXML->SetOrganizerMode( false ); + } + } + break; + + case SfxObjectCreateMode::INTERNAL: + case SfxObjectCreateMode::EMBEDDED: + { + SwTransferable::InitOle( this ); + } + // suppress SfxProgress, when we are Embedded + SW_MOD()->SetEmbeddedLoadSave( true ); + [[fallthrough]]; + + case SfxObjectCreateMode::STANDARD: + { + Reader *pReader = ReadXML; + if( pReader ) + { + // set Doc's DocInfo at DocShell-Medium + SAL_INFO( "sw.ui", "before ReadDocInfo" ); + SwReader aRdr(rMedium, OUString(), m_xDoc.get()); + SAL_INFO( "sw.ui", "before Read" ); + nErr = aRdr.Read( *pReader ); + SAL_INFO( "sw.ui", "after Read" ); + // If a XML document is loaded, the global doc/web doc + // flags have to be set, because they aren't loaded + // by this formats. + if( dynamic_cast< const SwWebDocShell *>( this ) != nullptr ) + { + if (!m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true); + } + if( dynamic_cast< const SwGlobalDocShell *>( this ) != nullptr ) + { + if (!m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT)) + m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT, true); + } + } + } + break; + + default: + OSL_ENSURE( false, "Load: new CreateMode?" ); + } + + UpdateFontList(); + InitDrawModelAndDocShell(this, m_xDoc ? m_xDoc->getIDocumentDrawModelAccess().GetDrawModel() + : nullptr); + + SetError(nErr); + bRet = !nErr.IsError(); + + if (bRet && !m_xDoc->IsInLoadAsynchron() && + GetCreateMode() == SfxObjectCreateMode::STANDARD) + { + LoadingFinished(); + } + + // suppress SfxProgress, when we are Embedded + SW_MOD()->SetEmbeddedLoadSave( false ); + } + + return bRet; +} + +bool SwDocShell::LoadFrom( SfxMedium& rMedium ) +{ + bool bRet = false; + if (m_xDoc) + RemoveLink(); + + AddLink(); // set Link and update Data!! + + do { // middle check loop + ErrCode nErr = ERR_SWG_READ_ERROR; + OUString aStreamName = "styles.xml"; + uno::Reference < container::XNameAccess > xAccess = rMedium.GetStorage(); + if ( xAccess->hasByName( aStreamName ) && rMedium.GetStorage()->isStreamElement( aStreamName ) ) + { + // Loading + SwWait aWait( *this, true ); + { + OSL_ENSURE( !m_xBasePool.is(), "who hasn't destroyed their Pool?" ); + m_xBasePool = new SwDocStyleSheetPool( *m_xDoc, SfxObjectCreateMode::ORGANIZER == GetCreateMode() ); + if( ReadXML ) + { + ReadXML->SetOrganizerMode( true ); + SwReader aRdr(rMedium, OUString(), m_xDoc.get()); + nErr = aRdr.Read( *ReadXML ); + ReadXML->SetOrganizerMode( false ); + } + } + } + else + { + OSL_FAIL("Code removed!"); + } + + SetError(nErr); + bRet = !nErr.IsError(); + + } while( false ); + + SfxObjectShell::LoadFrom( rMedium ); + m_xDoc->getIDocumentState().ResetModified(); + return bRet; +} + +void SwDocShell::SubInitNew() +{ + OSL_ENSURE( !m_xBasePool.is(), "who hasn't destroyed their Pool?" ); + m_xBasePool = new SwDocStyleSheetPool( *m_xDoc, SfxObjectCreateMode::ORGANIZER == GetCreateMode() ); + UpdateFontList(); + InitDrawModelAndDocShell(this, m_xDoc ? m_xDoc->getIDocumentDrawModelAccess().GetDrawModel() : nullptr); + + m_xDoc->getIDocumentSettingAccess().setLinkUpdateMode( GLOBALSETTING ); + m_xDoc->getIDocumentSettingAccess().setFieldUpdateFlags( AUTOUPD_GLOBALSETTING ); + + bool bWeb = dynamic_cast< const SwWebDocShell *>( this ) != nullptr; + + sal_uInt16 nRange[] = { + RES_PARATR_ADJUST, RES_PARATR_ADJUST, + RES_CHRATR_COLOR, RES_CHRATR_COLOR, + RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, + 0, 0, 0 }; + if(!bWeb) + { + nRange[ SAL_N_ELEMENTS(nRange) - 3 ] = RES_PARATR_TABSTOP; + nRange[ SAL_N_ELEMENTS(nRange) - 2 ] = RES_PARATR_HYPHENZONE; + } + SfxItemSet aDfltSet( m_xDoc->GetAttrPool(), nRange ); + + //! get lingu options without loading lingu DLL + SvtLinguOptions aLinguOpt; + + if (!utl::ConfigManager::IsFuzzing()) + SvtLinguConfig().GetOptions(aLinguOpt); + + LanguageType nVal = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN), + eCJK = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN), + eCTL = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + aDfltSet.Put( SvxLanguageItem( nVal, RES_CHRATR_LANGUAGE ) ); + aDfltSet.Put( SvxLanguageItem( eCJK, RES_CHRATR_CJK_LANGUAGE ) ); + aDfltSet.Put( SvxLanguageItem( eCTL, RES_CHRATR_CTL_LANGUAGE ) ); + + if(!bWeb) + { + SvxHyphenZoneItem aHyp( m_xDoc->GetDefault(RES_PARATR_HYPHENZONE) ); + aHyp.GetMinLead() = static_cast< sal_uInt8 >(aLinguOpt.nHyphMinLeading); + aHyp.GetMinTrail() = static_cast< sal_uInt8 >(aLinguOpt.nHyphMinTrailing); + + aDfltSet.Put( aHyp ); + + sal_uInt16 nNewPos = static_cast< sal_uInt16 >(convertMm100ToTwip(SW_MOD()->GetUsrPref(false)->GetDefTabInMm100())); + if( nNewPos ) + aDfltSet.Put( SvxTabStopItem( 1, nNewPos, + SvxTabAdjust::Default, RES_PARATR_TABSTOP ) ); + } + aDfltSet.Put( SvxColorItem( COL_AUTO, RES_CHRATR_COLOR ) ); + + m_xDoc->SetDefault( aDfltSet ); + + //default page mode for text grid + if(!bWeb) + { + bool bSquaredPageMode = SW_MOD()->GetUsrPref(false)->IsSquaredPageMode(); + m_xDoc->SetDefaultPageMode( bSquaredPageMode ); + + // only set Widow/Orphan defaults on a new, non-web document - not an opened one + if( GetMedium() && GetMedium()->GetOrigURL().isEmpty() ) + { + m_xDoc->SetDefault( SvxWidowsItem( sal_uInt8(2), RES_PARATR_WIDOWS) ); + m_xDoc->SetDefault( SvxOrphansItem( sal_uInt8(2), RES_PARATR_ORPHANS) ); + } + } + + m_xDoc->getIDocumentState().ResetModified(); +} + +/* + * Document Interface Access + */ +IDocumentDeviceAccess& SwDocShell::getIDocumentDeviceAccess() +{ + return m_xDoc->getIDocumentDeviceAccess(); +} + +IDocumentChartDataProviderAccess& SwDocShell::getIDocumentChartDataProviderAccess() +{ + return m_xDoc->getIDocumentChartDataProviderAccess(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docst.cxx b/sw/source/uibase/app/docst.cxx new file mode 100644 index 000000000..6886b548b --- /dev/null +++ b/sw/source/uibase/app/docst.cxx @@ -0,0 +1,1547 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <comphelper/flagguard.hxx> +#include <o3tl/any.hxx> +#include <sal/log.hxx> +#include <hintids.hxx> +#include <sfx2/styledlg.hxx> +#include <svl/whiter.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/newstyle.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <svl/languageoptions.hxx> +#include <sfx2/htmlmode.hxx> +#include <swmodule.hxx> +#include <fchrfmt.hxx> +#include <svtools/htmlcfg.hxx> +#include <svx/xdef.hxx> +#include <SwStyleNameMapper.hxx> +#include <SwRewriter.hxx> +#include <numrule.hxx> +#include <swundo.hxx> +#include <svx/drawitem.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <viewopt.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <frmfmt.hxx> +#include <charfmt.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <docstyle.hxx> +#include <uiitems.hxx> +#include <fmtcol.hxx> +#include <edtwin.hxx> +#include <unochart.hxx> +#include <swabstdlg.hxx> +#include <paratr.hxx> +#include <tblafmt.hxx> +#include <sfx2/watermarkitem.hxx> +#include <SwUndoFmt.hxx> +#include <strings.hrc> +#include <AccessibilityCheck.hxx> + +using namespace ::com::sun::star; + +void SwDocShell::StateStyleSheet(SfxItemSet& rSet, SwWrtShell* pSh) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxStyleFamily nActualFamily = SfxStyleFamily(USHRT_MAX); + + SwWrtShell* pShell = pSh ? pSh : GetWrtShell(); + if(!pShell) + { + while (nWhich) + { + rSet.DisableItem(nWhich); + nWhich = aIter.NextWhich(); + } + return; + } + else + { + SfxViewFrame* pFrame = pShell->GetView().GetViewFrame(); + std::unique_ptr<SfxPoolItem> pItem; + pFrame->GetBindings().QueryState(SID_STYLE_FAMILY, pItem); + SfxUInt16Item* pFamilyItem = dynamic_cast<SfxUInt16Item*>(pItem.get()); + if (pFamilyItem) + { + nActualFamily = static_cast<SfxStyleFamily>(pFamilyItem->GetValue()); + } + } + + while (nWhich) + { + // determine current template to every family + OUString aName; + SwTableAutoFormat aTableAutoFormat("dummy"); // needed to check if can take a table auto format at current cursor position + switch (nWhich) + { + case SID_STYLE_APPLY: + {// here the template and its family are passed to the StyleBox + // so that this family is being showed + if(pShell->IsFrameSelected()) + { + SwFrameFormat* pFormat = pShell->GetSelectedFrameFormat(); + if( pFormat ) + aName = pFormat->GetName(); + } + else + { + SwTextFormatColl* pColl = pShell->GetCurTextFormatColl(); + if(pColl) + aName = pColl->GetName(); + } + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + break; + case SID_STYLE_FAMILY1: + if( !pShell->IsFrameSelected() ) + { + SwCharFormat* pFormat = pShell->GetCurCharFormat(); + if(pFormat) + aName = pFormat->GetName(); + else + aName = SwResId(STR_POOLCHR_STANDARD); + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + break; + + case SID_STYLE_FAMILY2: + if(!pShell->IsFrameSelected()) + { + SwTextFormatColl* pColl = pShell->GetCurTextFormatColl(); + if(pColl) + aName = pColl->GetName(); + + SfxTemplateItem aItem(nWhich, aName); + + SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto; + if (m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) + nMask = SfxStyleSearchBits::SwHtml; + else + { + const FrameTypeFlags nSelection = pShell->GetFrameType(nullptr,true); + if(pShell->GetCurTOX()) + nMask = SfxStyleSearchBits::SwIndex ; + else if(nSelection & FrameTypeFlags::HEADER || + nSelection & FrameTypeFlags::FOOTER || + nSelection & FrameTypeFlags::TABLE || + nSelection & FrameTypeFlags::FLY_ANY || + nSelection & FrameTypeFlags::FOOTNOTE || + nSelection & FrameTypeFlags::FTNPAGE) + nMask = SfxStyleSearchBits::SwExtra; + else + nMask = SfxStyleSearchBits::SwText; + } + + aItem.SetValue(nMask); + rSet.Put(aItem); + } + + break; + + case SID_STYLE_FAMILY3: + + if (m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) + rSet.DisableItem( nWhich ); + else + { + SwFrameFormat* pFormat = pShell->GetSelectedFrameFormat(); + if(pFormat && pShell->IsFrameSelected()) + { + aName = pFormat->GetName(); + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + } + break; + + case SID_STYLE_FAMILY4: + { + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + if (m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !rHtmlOpt.IsPrintLayoutExtension()) + rSet.DisableItem( nWhich ); + else + { + size_t n = pShell->GetCurPageDesc( false ); + if( n < pShell->GetPageDescCnt() ) + aName = pShell->GetPageDesc( n ).GetName(); + + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + } + break; + case SID_STYLE_FAMILY5: + { + const SwNumRule* pRule = pShell->GetNumRuleAtCurrCursorPos(); + if( pRule ) + aName = pRule->GetName(); + + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + break; + case SID_STYLE_FAMILY6: + { + const SwTableNode *pTableNd = pShell->IsCursorInTable(); + if( pTableNd ) + aName = pTableNd->GetTable().GetTableStyleName(); + + rSet.Put(SfxTemplateItem(nWhich, aName)); + } + break; + + case SID_STYLE_WATERCAN: + { + SwEditWin& rEdtWin = pShell->GetView().GetEditWin(); + SwApplyTemplate* pApply = rEdtWin.GetApplyTemplate(); + rSet.Put(SfxBoolItem(nWhich, pApply && pApply->eType != SfxStyleFamily(0))); + } + break; + case SID_STYLE_UPDATE_BY_EXAMPLE: + if( pShell->IsFrameSelected() + ? SfxStyleFamily::Frame != nActualFamily + : ( SfxStyleFamily::Frame == nActualFamily || + SfxStyleFamily::Page == nActualFamily || + (SfxStyleFamily::Pseudo == nActualFamily && !pShell->GetNumRuleAtCurrCursorPos()) || + (SfxStyleFamily::Table == nActualFamily && !pShell->GetTableAutoFormat(aTableAutoFormat))) ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + if( (pShell->IsFrameSelected() + ? SfxStyleFamily::Frame != nActualFamily + : SfxStyleFamily::Frame == nActualFamily) || + (SfxStyleFamily::Pseudo == nActualFamily && !pShell->GetNumRuleAtCurrCursorPos()) || + (SfxStyleFamily::Table == nActualFamily && !pShell->GetTableAutoFormat(aTableAutoFormat)) ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_CLASSIFICATION_APPLY: + // Just trigger ClassificationCategoriesController::statusChanged(). + rSet.InvalidateItem(nWhich); + break; + case SID_CLASSIFICATION_DIALOG: + rSet.InvalidateItem(nWhich); + break; + case SID_STYLE_EDIT: + break; + case SID_WATERMARK: + if (pSh) + { + SfxWatermarkItem aItem = pSh->GetWatermark(); + rSet.Put(aItem); + } + break; + default: + OSL_FAIL("Invalid SlotId"); + } + nWhich = aIter.NextWhich(); + } +} + +// evaluate StyleSheet-Requests +void SwDocShell::ExecStyleSheet( SfxRequest& rReq ) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + switch (nSlot) + { + case SID_STYLE_NEW: + if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_STYLE_FAMILY, + false, &pItem )) + { + const SfxStyleFamily nFamily = static_cast<SfxStyleFamily>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + + OUString sName; + SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto; + if( SfxItemState::SET == pArgs->GetItemState( SID_STYLE_NEW, + false, &pItem )) + sName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + if( SfxItemState::SET == pArgs->GetItemState( SID_STYLE_MASK, + false, &pItem )) + nMask = static_cast<SfxStyleSearchBits>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + OUString sParent; + if( SfxItemState::SET == pArgs->GetItemState( SID_STYLE_REFERENCE, + false, &pItem )) + sParent = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if (sName.isEmpty() && m_xBasePool) + sName = SfxStyleDialogController::GenerateUnusedName(*m_xBasePool, nFamily); + + Edit(sName, sParent, nFamily, nMask, true, OString(), nullptr, &rReq, nSlot); + } + break; + + case SID_STYLE_APPLY: + if( !pArgs ) + { + GetView()->GetViewFrame()->GetDispatcher()->Execute(SID_STYLE_DESIGNER); + break; + } + else + { + // convert internal StyleName to DisplayName (slot implementation uses the latter) + const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_APPLY_STYLE); + const SfxStringItem* pFamilyItem = rReq.GetArg<SfxStringItem>(SID_STYLE_FAMILYNAME); + if ( pFamilyItem && pNameItem ) + { + uno::Reference< style::XStyleFamiliesSupplier > xModel(GetModel(), uno::UNO_QUERY); + try + { + uno::Reference< container::XNameAccess > xStyles; + uno::Reference< container::XNameAccess > xCont = xModel->getStyleFamilies(); + xCont->getByName(pFamilyItem->GetValue()) >>= xStyles; + uno::Reference< beans::XPropertySet > xInfo; + xStyles->getByName( pNameItem->GetValue() ) >>= xInfo; + OUString aUIName; + xInfo->getPropertyValue("DisplayName") >>= aUIName; + if ( !aUIName.isEmpty() ) + rReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aUIName ) ); + } + catch (const uno::Exception&) + { + } + } + } + + [[fallthrough]]; + + case SID_STYLE_EDIT: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_WATERCAN: + case SID_STYLE_FAMILY: + case SID_STYLE_UPDATE_BY_EXAMPLE: + case SID_STYLE_NEW_BY_EXAMPLE: + { + OUString aParam; + SfxStyleFamily nFamily = SfxStyleFamily::Para; + SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto; + SwWrtShell* pActShell = nullptr; + + if( !pArgs ) + { + switch (nSlot) + { + case SID_STYLE_NEW_BY_EXAMPLE: + { + SfxStyleSheetBasePool& rPool = *GetStyleSheetPool(); + SfxNewStyleDlg aDlg(GetView()->GetFrameWeld(), rPool, nFamily); + if (aDlg.run() == RET_OK) + { + aParam = aDlg.GetName(); + rReq.AppendItem(SfxStringItem(nSlot, aParam)); + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + case SID_STYLE_EDIT: + { + SwTextFormatColl* pColl = GetWrtShell()->GetCurTextFormatColl(); + if(pColl) + { + aParam = pColl->GetName(); + rReq.AppendItem(SfxStringItem(nSlot, aParam)); + } + } + break; + } + } + else + { + SAL_WARN_IF( !pArgs->Count(), "sw.ui", "SfxBug ItemSet is empty" ); + + SwWrtShell* pShell = GetWrtShell(); + if( SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem )) + aParam = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if( SfxItemState::SET == pArgs->GetItemState(SID_STYLE_FAMILY, + false, &pItem )) + nFamily = static_cast<SfxStyleFamily>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + + if( SfxItemState::SET == pArgs->GetItemState(SID_STYLE_FAMILYNAME, false, &pItem )) + { + OUString aFamily = static_cast<const SfxStringItem*>(pItem)->GetValue(); + if(aFamily == "CharacterStyles") + nFamily = SfxStyleFamily::Char; + else + if(aFamily == "ParagraphStyles") + nFamily = SfxStyleFamily::Para; + else + if(aFamily == "PageStyles") + nFamily = SfxStyleFamily::Page; + else + if(aFamily == "FrameStyles") + nFamily = SfxStyleFamily::Frame; + else + if(aFamily == "NumberingStyles") + nFamily = SfxStyleFamily::Pseudo; + else + if(aFamily == "TableStyles") + nFamily = SfxStyleFamily::Table; + } + + if( SfxItemState::SET == pArgs->GetItemState(SID_STYLE_MASK, + false, &pItem )) + nMask = static_cast<SfxStyleSearchBits>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + if( SfxItemState::SET == pArgs->GetItemState(FN_PARAM_WRTSHELL, + false, &pItem )) + pActShell = pShell = static_cast<SwWrtShell*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + + if( nSlot == SID_STYLE_UPDATE_BY_EXAMPLE && aParam.isEmpty() ) + { + switch( nFamily ) + { + case SfxStyleFamily::Para: + { + SwTextFormatColl* pColl = pShell->GetCurTextFormatColl(); + if(pColl) + aParam = pColl->GetName(); + } + break; + case SfxStyleFamily::Frame: + { + SwFrameFormat* pFrame = m_pWrtShell->GetSelectedFrameFormat(); + if( pFrame ) + aParam = pFrame->GetName(); + } + break; + case SfxStyleFamily::Char: + { + SwCharFormat* pChar = m_pWrtShell->GetCurCharFormat(); + if( pChar ) + aParam = pChar->GetName(); + } + break; + case SfxStyleFamily::Pseudo: + if(SfxItemState::SET == pArgs->GetItemState(SID_STYLE_UPD_BY_EX_NAME, false, &pItem)) + { + aParam = static_cast<const SfxStringItem*>(pItem)->GetValue(); + } + break; + case SfxStyleFamily::Table: + if(SfxItemState::SET == pArgs->GetItemState(SID_STYLE_UPD_BY_EX_NAME, false, &pItem)) + { + aParam = static_cast<const SfxStringItem*>(pItem)->GetValue(); + } + break; + default: break; + } + rReq.AppendItem(SfxStringItem(nSlot, aParam)); + } + } + if (!aParam.isEmpty() || nSlot == SID_STYLE_WATERCAN ) + { + sal_uInt16 nRet = 0xffff; + bool bReturns = false; + + switch(nSlot) + { + case SID_STYLE_EDIT: + Edit(aParam, OUString(), nFamily, nMask, false, OString(), pActShell); + break; + case SID_STYLE_DELETE: + Delete(aParam, nFamily); + break; + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + Hide(aParam, nFamily, nSlot == SID_STYLE_HIDE); + break; + case SID_STYLE_APPLY: + // Shell-switch in ApplyStyles + nRet = static_cast<sal_uInt16>(ApplyStyles(aParam, nFamily, pActShell, rReq.GetModifier() )); + bReturns = true; + break; + case SID_STYLE_WATERCAN: + nRet = static_cast<sal_uInt16>(DoWaterCan(aParam, nFamily)); + bReturns = true; + break; + case SID_STYLE_UPDATE_BY_EXAMPLE: + UpdateStyle(aParam, nFamily, pActShell); + break; + case SID_STYLE_NEW_BY_EXAMPLE: + MakeByExample(aParam, nFamily, nMask, pActShell); + break; + + default: + OSL_FAIL("Invalid SlotId"); + } + + if (bReturns) + { + if(rReq.IsAPI()) // Basic only gets TRUE or FALSE + rReq.SetReturnValue(SfxUInt16Item(nSlot, sal_uInt16(nRet !=0))); + else + rReq.SetReturnValue(SfxUInt16Item(nSlot, nRet)); + } + + rReq.Done(); + } + + break; + } + } +} + +namespace { + +class ApplyStyle +{ +public: + ApplyStyle(SwDocShell &rDocSh, bool bNew, + rtl::Reference< SwDocStyleSheet > const & xTmp, + SfxStyleFamily nFamily, SfxAbstractApplyTabDialog *pDlg, + rtl::Reference< SfxStyleSheetBasePool > const & xBasePool, + bool bModified) + : m_pDlg(pDlg) + , m_rDocSh(rDocSh) + , m_bNew(bNew) + , m_xTmp(xTmp) + , m_nFamily(nFamily) + , m_xBasePool(xBasePool) + , m_bModified(bModified) + { + } + DECL_LINK( ApplyHdl, LinkParamNone*, void ); + void apply() + { + ApplyHdl(nullptr); + } + VclPtr<SfxAbstractApplyTabDialog> m_pDlg; +private: + SwDocShell &m_rDocSh; + bool m_bNew; + rtl::Reference< SwDocStyleSheet > m_xTmp; + SfxStyleFamily m_nFamily; + rtl::Reference< SfxStyleSheetBasePool > m_xBasePool; + bool m_bModified; +}; + +} + +IMPL_LINK_NOARG(ApplyStyle, ApplyHdl, LinkParamNone*, void) +{ + SwWrtShell* pWrtShell = m_rDocSh.GetWrtShell(); + SwDoc* pDoc = m_rDocSh.GetDoc(); + SwView* pView = m_rDocSh.GetView(); + + pWrtShell->StartAllAction(); + + if( SfxStyleFamily::Para == m_nFamily ) + { + SfxItemSet aSet( *m_pDlg->GetOutputItemSet() ); + ::SfxToSwPageDescAttr( *pWrtShell, aSet ); + // reset indent attributes at paragraph style, if a list style + // will be applied and no indent attributes will be applied. + m_xTmp->SetItemSet( aSet, true ); + } + else + { + if(SfxStyleFamily::Page == m_nFamily) + { + static const sal_uInt16 aInval[] = { + SID_IMAGE_ORIENTATION, + SID_ATTR_CHAR_FONT, + FN_INSERT_CTRL, FN_INSERT_OBJ_CTRL, 0}; + pView->GetViewFrame()->GetBindings().Invalidate(aInval); + } + SfxItemSet aTmpSet( *m_pDlg->GetOutputItemSet() ); + if( SfxStyleFamily::Char == m_nFamily ) + { + ::ConvertAttrGenToChar(aTmpSet, m_xTmp->GetItemSet()); + } + + m_xTmp->SetItemSet( aTmpSet ); + + if( SfxStyleFamily::Page == m_nFamily && SvtLanguageOptions().IsCTLFontEnabled() ) + { + const SfxPoolItem *pItem = nullptr; + if( aTmpSet.GetItemState( m_rDocSh.GetPool().GetTrueWhich( SID_ATTR_FRAMEDIRECTION, false ) , true, &pItem ) == SfxItemState::SET ) + SwChartHelper::DoUpdateAllCharts( pDoc ); + } + } + + if(m_bNew) + { + if(SfxStyleFamily::Frame == m_nFamily || SfxStyleFamily::Para == m_nFamily) + { + // clear FillStyle so that it works as a derived attribute + SfxItemSet aTmpSet(*m_pDlg->GetOutputItemSet()); + + aTmpSet.ClearItem(XATTR_FILLSTYLE); + m_xTmp->SetItemSet(aTmpSet); + } + } + + if(SfxStyleFamily::Page == m_nFamily) + pView->InvalidateRulerPos(); + + if( m_bNew ) + m_xBasePool->Broadcast(SfxStyleSheetHint(SfxHintId::StyleSheetCreated, *m_xTmp)); + else + m_xBasePool->Broadcast(SfxStyleSheetHint(SfxHintId::StyleSheetModified, *m_xTmp)); + + pDoc->getIDocumentState().SetModified(); + if( !m_bModified ) + { + pDoc->GetIDocumentUndoRedo().SetUndoNoResetModified(); + } + + pWrtShell->EndAllAction(); +} + +namespace +{ +/// Checks if there is an Endnote page style in use, and makes sure it has the same orientation +/// with the Default (Standard) page style. +void syncEndnoteOrientation(const uno::Reference< style::XStyleFamiliesSupplier >& xStyleFamSupp) +{ + if (!xStyleFamSupp.is()) + { + SAL_WARN("sw.ui", "Ref to XStyleFamiliesSupplier is null."); + return; + } + uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamSupp->getStyleFamilies(); + + if (!xStyleFamilies.is()) + return; + + uno::Reference<container::XNameAccess> xPageStyles(xStyleFamilies->getByName("PageStyles"), + uno::UNO_QUERY); + + if (!xPageStyles.is()) + return; + + uno::Reference<css::style::XStyle> xEndnotePageStyle(xPageStyles->getByName("Endnote"), + uno::UNO_QUERY); + + if (!xEndnotePageStyle.is()) + return; + + // Language-independent name of the "Default Style" is "Standard" + uno::Reference<css::style::XStyle> xDefaultPageStyle(xPageStyles->getByName("Standard"), + uno::UNO_QUERY); + if (!xDefaultPageStyle.is()) + return; + + if (xEndnotePageStyle->isUserDefined() || !xEndnotePageStyle->isInUse()) + return; + + uno::Reference<beans::XPropertySet> xEndnotePagePropSet(xPageStyles->getByName("Endnote"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xDefaultPagePropSet(xPageStyles->getByName("Standard"), uno::UNO_QUERY); + + if (!xEndnotePagePropSet.is() || !xDefaultPagePropSet.is()) + { + SAL_WARN("sw.ui", "xEndnotePagePropSet or xDefaultPagePropSet is null."); + return; + } + + auto const bIsDefLandScape = *o3tl::doAccess<bool>( + xDefaultPagePropSet->getPropertyValue("IsLandscape")); + auto const bIsEndLandScape = *o3tl::doAccess<bool>( + xEndnotePagePropSet->getPropertyValue("IsLandscape")); + + if (bIsDefLandScape == bIsEndLandScape) + return; + + auto const nWidth = xEndnotePagePropSet->getPropertyValue("Width"); + auto const nHeight = xEndnotePagePropSet->getPropertyValue("Height"); + + xEndnotePagePropSet->setPropertyValue("IsLandscape", css::uno::toAny(bIsDefLandScape)); + xEndnotePagePropSet->setPropertyValue("Width", nHeight); + xEndnotePagePropSet->setPropertyValue("Height", nWidth); +} +} + +void SwDocShell::Edit( + const OUString &rName, + const OUString &rParent, + const SfxStyleFamily nFamily, + SfxStyleSearchBits nMask, + const bool bNew, + const OString& sPage, + SwWrtShell* pActShell, + SfxRequest* pReq, + sal_uInt16 nSlot) +{ + assert( GetWrtShell() ); + const bool bBasic = pReq && pReq->IsAPI(); + SfxStyleSheetBase *pStyle = nullptr; + + bool bModified = m_xDoc->getIDocumentState().IsModified(); + + SwUndoId nNewStyleUndoId(SwUndoId::EMPTY); + + if( bNew ) + { + if (!bBasic) + { + // start undo action in order to get only one undo action for the + // UI new style + change style operations + m_pWrtShell->StartUndo(); + } + + if( SfxStyleSearchBits::All != nMask && SfxStyleSearchBits::AllVisible != nMask && SfxStyleSearchBits::Used != nMask ) + nMask |= SfxStyleSearchBits::UserDefined; + else + nMask = SfxStyleSearchBits::UserDefined; + + if ( nFamily == SfxStyleFamily::Para || nFamily == SfxStyleFamily::Char || nFamily == SfxStyleFamily::Frame ) + { + // Prevent undo append from being done during paragraph, character, and frame style Make + // Do it after ok return from style dialog when derived from style is known + ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo()); + pStyle = &m_xBasePool->Make( rName, nFamily, nMask ); + } + else + { + pStyle = &m_xBasePool->Make( rName, nFamily, nMask ); + } + + // set the current one as Parent + SwDocStyleSheet* pDStyle = static_cast<SwDocStyleSheet*>(pStyle); + switch( nFamily ) + { + case SfxStyleFamily::Para: + { + if(!rParent.isEmpty()) + { + SwTextFormatColl* pColl = m_pWrtShell->FindTextFormatCollByName( rParent ); + if(!pColl) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rParent, SwGetPoolIdFromName::TxtColl); + if(USHRT_MAX != nId) + pColl = m_pWrtShell->GetTextCollFromPool( nId ); + } + pDStyle->GetCollection()->SetDerivedFrom( pColl ); + pDStyle->PresetParent( rParent ); + + /*When a new paragraph style is created based on a "to outline style + assigned" paragraph style, the outline level attribute and the list + style attribute of the new paragraph style have to be set to 0 + respectively "".*/ + if (pColl && pColl->IsAssignedToListLevelOfOutlineStyle()) + { + SwNumRuleItem aItem; + pDStyle->GetCollection()->SetFormatAttr( aItem ); + pDStyle->GetCollection()->SetAttrOutlineLevel( 0 ); + } + } + else + { + SwTextFormatColl* pColl = m_pWrtShell->GetCurTextFormatColl(); + pDStyle->GetCollection()->SetDerivedFrom( pColl ); + if( pColl ) + pDStyle->PresetParent( pColl->GetName() ); + } + } + break; + case SfxStyleFamily::Char: + { + if(!rParent.isEmpty()) + { + SwCharFormat* pCFormat = m_pWrtShell->FindCharFormatByName(rParent); + if(!pCFormat) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rParent, SwGetPoolIdFromName::ChrFmt); + if(USHRT_MAX != nId) + pCFormat = m_pWrtShell->GetCharFormatFromPool( nId ); + } + + pDStyle->GetCharFormat()->SetDerivedFrom( pCFormat ); + pDStyle->PresetParent( rParent ); + } + else + { + SwCharFormat* pCFormat = m_pWrtShell->GetCurCharFormat(); + pDStyle->GetCharFormat()->SetDerivedFrom( pCFormat ); + if( pCFormat ) + pDStyle->PresetParent( pCFormat->GetName() ); + } + } + break; + case SfxStyleFamily::Frame : + { + if(!rParent.isEmpty()) + { + SwFrameFormat* pFFormat = m_pWrtShell->GetDoc()->FindFrameFormatByName( rParent ); + if(!pFFormat) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rParent, SwGetPoolIdFromName::FrmFmt); + if(USHRT_MAX != nId) + pFFormat = m_pWrtShell->GetFrameFormatFromPool( nId ); + } + pDStyle->GetFrameFormat()->SetDerivedFrom( pFFormat ); + pDStyle->PresetParent( rParent ); + } + } + break; + default: break; + } + + if (!bBasic) + { + //Get the undo id for the type of style that was created in order to re-use that comment for the grouped + //create style + change style operations + m_pWrtShell->GetLastUndoInfo(nullptr, &nNewStyleUndoId); + } + } + else + { + pStyle = m_xBasePool->Find( rName, nFamily ); + SAL_WARN_IF( !pStyle, "sw.ui", "Style not found" ); + } + + if(!pStyle) + return; + + // put dialogues together + rtl::Reference< SwDocStyleSheet > xTmp( new SwDocStyleSheet( *static_cast<SwDocStyleSheet*>(pStyle) ) ); + if( SfxStyleFamily::Para == nFamily ) + { + SfxItemSet& rSet = xTmp->GetItemSet(); + ::SwToSfxPageDescAttr( rSet ); + // merge list level indent attributes into the item set if needed + xTmp->MergeIndentAttrsOfListStyle( rSet ); + } + else if( SfxStyleFamily::Char == nFamily ) + { + ::ConvertAttrCharToGen(xTmp->GetItemSet()); + } + + if(SfxStyleFamily::Page == nFamily || SfxStyleFamily::Para == nFamily) + { + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + SfxItemSet& rSet = xTmp->GetItemSet(); + const SwDrawModel* pDrawModel = GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + + rSet.Put(SvxColorListItem(pDrawModel->GetColorList(), SID_COLOR_TABLE)); + rSet.Put(SvxGradientListItem(pDrawModel->GetGradientList(), SID_GRADIENT_LIST)); + rSet.Put(SvxHatchListItem(pDrawModel->GetHatchList(), SID_HATCH_LIST)); + rSet.Put(SvxBitmapListItem(pDrawModel->GetBitmapList(), SID_BITMAP_LIST)); + rSet.Put(SvxPatternListItem(pDrawModel->GetPatternList(), SID_PATTERN_LIST)); + } + + if (!bBasic) + { + // prior to the dialog the HtmlMode at the DocShell is being sunk + sal_uInt16 nHtmlMode = ::GetHtmlMode(this); + + // In HTML mode, we do not always have a printer. In order to show + // the correct page size in the Format - Page dialog, we have to + // get one here. + SwWrtShell* pCurrShell = pActShell ? pActShell : m_pWrtShell; + if( ( HTMLMODE_ON & nHtmlMode ) && + !pCurrShell->getIDocumentDeviceAccess().getPrinter( false ) ) + pCurrShell->InitPrt( pCurrShell->getIDocumentDeviceAccess().getPrinter( true ) ); + + PutItem(SfxUInt16Item(SID_HTML_MODE, nHtmlMode)); + FieldUnit eMetric = ::GetDfltMetric(0 != (HTMLMODE_ON&nHtmlMode)); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<SfxAbstractApplyTabDialog> pDlg(pFact->CreateTemplateDialog(GetView()->GetFrameWeld(), + *xTmp, nFamily, sPage, pCurrShell, bNew)); + auto pApplyStyleHelper = std::make_shared<ApplyStyle>(*this, bNew, xTmp, nFamily, pDlg.get(), m_xBasePool, bModified); + pDlg->SetApplyHdl(LINK(pApplyStyleHelper.get(), ApplyStyle, ApplyHdl)); + + std::shared_ptr<SfxRequest> pRequest; + if (pReq) + { + pRequest = std::make_shared<SfxRequest>(*pReq); + pReq->Ignore(); // the 'old' request is not relevant any more + } + + bool bIsDefaultPage = nFamily == SfxStyleFamily::Page + && rName == SwResId(STR_POOLPAGE_STANDARD) + && pStyle->IsUsed() + && !pStyle->IsUserDefined(); + + pDlg->StartExecuteAsync([bIsDefaultPage, bModified, bNew, nFamily, nSlot, nNewStyleUndoId, pApplyStyleHelper, pRequest, xTmp, this](sal_Int32 nResult){ + if (RET_OK == nResult) + pApplyStyleHelper->apply(); + + if (bNew) + { + switch( nFamily ) + { + case SfxStyleFamily::Para: + { + if(!xTmp->GetParent().isEmpty()) + { + SwTextFormatColl* pColl = m_pWrtShell->FindTextFormatCollByName(xTmp->GetParent()); + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoTextFormatCollCreate>(xTmp->GetCollection(), pColl, GetDoc())); + } + } + } + break; + case SfxStyleFamily::Char: + { + if(!xTmp->GetParent().isEmpty()) + { + SwCharFormat* pCFormat = m_pWrtShell->FindCharFormatByName(xTmp->GetParent()); + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoCharFormatCreate>(xTmp->GetCharFormat(), pCFormat, GetDoc())); + } + } + } + break; + case SfxStyleFamily::Frame: + { + if(!xTmp->GetParent().isEmpty()) + { + SwFrameFormat* pFFormat = m_pWrtShell->GetDoc()->FindFrameFormatByName(xTmp->GetParent()); + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoFrameFormatCreate>(xTmp->GetFrameFormat(), pFFormat, GetDoc())); + } + } + } + break; + default: break; + } + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, xTmp->GetName()); + //Group the create style and change style operations together under the + //one "create style" comment + m_pWrtShell->EndUndo(nNewStyleUndoId, &aRewriter); + } + + if (RET_OK != nResult) + { + if (bNew) + { + GetWrtShell()->Undo(); + m_xDoc->GetIDocumentUndoRedo().ClearRedo(); + } + + if (!bModified) + m_xDoc->getIDocumentState().ResetModified(); + } + + // Update Watermark if new page style was created + if (nSlot == SID_STYLE_NEW && nFamily == SfxStyleFamily::Page) + { + SwWrtShell* pShell = GetWrtShell(); + const SfxWatermarkItem aWatermark = pShell->GetWatermark(); + pShell->SetWatermark(aWatermark); + } + + pApplyStyleHelper->m_pDlg.disposeAndClear(); + if (pRequest) + pRequest->Done(); + + if (bIsDefaultPage && bModified) + { + uno::Reference< style::XStyleFamiliesSupplier > xStyleFamSupp(GetModel(), uno::UNO_QUERY); + + if (!xStyleFamSupp.is()) + { + SAL_WARN("sw.ui", "Ref to XStyleFamiliesSupplier is null."); + return; + } + + syncEndnoteOrientation(xStyleFamSupp); + } + }); + } + else + { + // prior to the dialog the HtmlMode at the DocShell is being sunk + PutItem(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(this))); + + GetWrtShell()->StartAllAction(); + + if( SfxStyleFamily::Para == nFamily ) + ::SfxToSwPageDescAttr( *GetWrtShell(), xTmp->GetItemSet() ); + else + { + ::ConvertAttrGenToChar(xTmp->GetItemSet(), xTmp->GetItemSet()); + } + if(SfxStyleFamily::Page == nFamily) + m_pView->InvalidateRulerPos(); + + if( bNew ) + m_xBasePool->Broadcast(SfxStyleSheetHint(SfxHintId::StyleSheetCreated, *xTmp)); + + m_xDoc->getIDocumentState().SetModified(); + if( !bModified ) // Bug 57028 + { + m_xDoc->GetIDocumentUndoRedo().SetUndoNoResetModified(); + } + GetWrtShell()->EndAllAction(); + } +} + +void SwDocShell::Delete(const OUString &rName, SfxStyleFamily nFamily) +{ + SfxStyleSheetBase *pStyle = m_xBasePool->Find(rName, nFamily); + + if(pStyle) + { + assert( GetWrtShell() ); + + GetWrtShell()->StartAllAction(); + m_xBasePool->Remove(pStyle); + GetWrtShell()->EndAllAction(); + } +} + +void SwDocShell::Hide(const OUString &rName, SfxStyleFamily nFamily, bool bHidden) +{ + SfxStyleSheetBase *pStyle = m_xBasePool->Find(rName, nFamily); + + if(pStyle) + { + assert( GetWrtShell() ); + + GetWrtShell()->StartAllAction(); + rtl::Reference< SwDocStyleSheet > xTmp( new SwDocStyleSheet( *static_cast<SwDocStyleSheet*>(pStyle) ) ); + xTmp->SetHidden( bHidden ); + GetWrtShell()->EndAllAction(); + } +} + +// apply template +SfxStyleFamily SwDocShell::ApplyStyles(const OUString &rName, SfxStyleFamily nFamily, + SwWrtShell* pShell, const sal_uInt16 nMode ) +{ + SwDocStyleSheet* pStyle = static_cast<SwDocStyleSheet*>( m_xBasePool->Find( rName, nFamily ) ); + + SAL_WARN_IF( !pStyle, "sw.ui", "Style not found" ); + + if(!pStyle) + return SfxStyleFamily::None; + + SwWrtShell *pSh = pShell ? pShell : GetWrtShell(); + + assert( pSh ); + + pSh->StartAllAction(); + + switch (nFamily) + { + case SfxStyleFamily::Char: + { + SwFormatCharFormat aFormat(pStyle->GetCharFormat()); + pSh->SetAttrItem( aFormat, (nMode & KEY_SHIFT) ? + SetAttrMode::DONTREPLACE : SetAttrMode::DEFAULT ); + break; + } + case SfxStyleFamily::Para: + { + // #i62675# + // clear also list attributes at affected text nodes, if paragraph + // style has the list style attribute set. + pSh->SetTextFormatColl( pStyle->GetCollection(), true ); + break; + } + case SfxStyleFamily::Frame: + { + if ( pSh->IsFrameSelected() ) + pSh->SetFrameFormat( pStyle->GetFrameFormat() ); + break; + } + case SfxStyleFamily::Page: + { + pSh->SetPageStyle(pStyle->GetPageDesc()->GetName()); + break; + } + case SfxStyleFamily::Pseudo: + { + // reset indent attribute on applying list style + // continue list of list style + const SwNumRule* pNumRule = pStyle->GetNumRule(); + const OUString sListIdForStyle =pNumRule->GetDefaultListId(); + pSh->SetCurNumRule( *pNumRule, false, sListIdForStyle, true ); + break; + } + case SfxStyleFamily::Table: + { + pSh->SetTableStyle(pStyle->GetName()); + break; + } + default: + OSL_FAIL("Unknown family"); + } + pSh->EndAllAction(); + + return nFamily; +} + +// start watering-can +SfxStyleFamily SwDocShell::DoWaterCan(const OUString &rName, SfxStyleFamily nFamily) +{ + assert( GetWrtShell() ); + + SwEditWin& rEdtWin = m_pView->GetEditWin(); + SwApplyTemplate* pApply = rEdtWin.GetApplyTemplate(); + bool bWaterCan = !(pApply && pApply->eType != SfxStyleFamily(0)); + + if( rName.isEmpty() ) + bWaterCan = false; + + SwApplyTemplate aTemplate; + aTemplate.eType = nFamily; + + if(bWaterCan) + { + SwDocStyleSheet* pStyle = + static_cast<SwDocStyleSheet*>( m_xBasePool->Find(rName, nFamily) ); + + SAL_WARN_IF( !pStyle, "sw.ui", "Where's the StyleSheet" ); + + if(!pStyle) return nFamily; + + switch(nFamily) + { + case SfxStyleFamily::Char: + aTemplate.aColl.pCharFormat = pStyle->GetCharFormat(); + break; + case SfxStyleFamily::Para: + aTemplate.aColl.pTextColl = pStyle->GetCollection(); + break; + case SfxStyleFamily::Frame: + aTemplate.aColl.pFrameFormat = pStyle->GetFrameFormat(); + break; + case SfxStyleFamily::Page: + aTemplate.aColl.pPageDesc = const_cast<SwPageDesc*>(pStyle->GetPageDesc()); + break; + case SfxStyleFamily::Pseudo: + aTemplate.aColl.pNumRule = const_cast<SwNumRule*>(pStyle->GetNumRule()); + break; + + default: + OSL_FAIL("Unknown family"); + } + } + else + aTemplate.eType = SfxStyleFamily(0); + + m_pView->GetEditWin().SetApplyTemplate(aTemplate); + + return nFamily; +} + +// update template +void SwDocShell::UpdateStyle(const OUString &rName, SfxStyleFamily nFamily, SwWrtShell* pShell) +{ + SwWrtShell* pCurrWrtShell = pShell ? pShell : GetWrtShell(); + assert( pCurrWrtShell ); + + SwDocStyleSheet* pStyle = + static_cast<SwDocStyleSheet*>( m_xBasePool->Find(rName, nFamily) ); + + if (!pStyle) + return; + + switch(nFamily) + { + case SfxStyleFamily::Para: + { + SwTextFormatColl* pColl = pStyle->GetCollection(); + if(pColl && !pColl->IsDefault()) + { + GetWrtShell()->StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, pColl->GetName()); + + GetWrtShell()->StartUndo(SwUndoId::INSFMTATTR, &aRewriter); + GetWrtShell()->FillByEx(pColl); + // also apply template to remove hard set attributes + GetWrtShell()->SetTextFormatColl( pColl ); + GetWrtShell()->EndUndo(); + GetWrtShell()->EndAllAction(); + } + break; + } + case SfxStyleFamily::Frame: + { + SwFrameFormat* pFrame = pStyle->GetFrameFormat(); + if( pCurrWrtShell->IsFrameSelected() && pFrame && !pFrame->IsDefault() ) + { + SfxItemSet aSet( GetPool(), aFrameFormatSetRange ); + pCurrWrtShell->StartAllAction(); + pCurrWrtShell->GetFlyFrameAttr( aSet ); + + // #i105535# + // no update of anchor attribute + aSet.ClearItem( RES_ANCHOR ); + + pFrame->SetFormatAttr( aSet ); + + // also apply template to remove hard set attributes + pCurrWrtShell->SetFrameFormat( pFrame, true ); + pCurrWrtShell->EndAllAction(); + } + } + break; + case SfxStyleFamily::Char: + { + SwCharFormat* pChar = pStyle->GetCharFormat(); + if( pChar && !pChar->IsDefault() ) + { + pCurrWrtShell->StartAllAction(); + pCurrWrtShell->FillByEx(pChar); + // also apply template to remove hard set attributes + pCurrWrtShell->EndAllAction(); + } + + } + break; + case SfxStyleFamily::Pseudo: + { + const SwNumRule* pCurRule; + if( pStyle->GetNumRule() && + nullptr != ( pCurRule = pCurrWrtShell->GetNumRuleAtCurrCursorPos() )) + { + SwNumRule aRule( *pCurRule ); + // #i91400# + aRule.SetName( pStyle->GetNumRule()->GetName(), + pCurrWrtShell->GetDoc()->getIDocumentListsAccess() ); + pCurrWrtShell->ChgNumRuleFormats( aRule ); + } + } + break; + case SfxStyleFamily::Table: + { + + SwTableAutoFormat aFormat(rName); + if (pCurrWrtShell->GetTableAutoFormat(aFormat)) + { + pCurrWrtShell->StartAllAction(); + pCurrWrtShell->GetDoc()->ChgTableStyle(rName, aFormat); + pCurrWrtShell->EndAllAction(); + } + + } + break; + default: break; + } +} + +// NewByExample +void SwDocShell::MakeByExample( const OUString &rName, SfxStyleFamily nFamily, + SfxStyleSearchBits nMask, SwWrtShell* pShell ) +{ + SwWrtShell* pCurrWrtShell = pShell ? pShell : GetWrtShell(); + SwDocStyleSheet* pStyle = static_cast<SwDocStyleSheet*>( m_xBasePool->Find( + rName, nFamily ) ); + if(!pStyle) + { + // preserve the current mask of PI, then the new one is + // immediately merged with the viewable area + if( SfxStyleSearchBits::All == nMask || SfxStyleSearchBits::Used == nMask ) + nMask = SfxStyleSearchBits::UserDefined; + else + nMask |= SfxStyleSearchBits::UserDefined; + + if (nFamily == SfxStyleFamily::Para || nFamily == SfxStyleFamily::Char || nFamily == SfxStyleFamily::Frame) + { + // Prevent undo append from being done during paragraph, character, and frame style Make. Do it later + ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo()); + pStyle = static_cast<SwDocStyleSheet*>(&m_xBasePool->Make(rName, nFamily, nMask)); + } + else + { + pStyle = static_cast<SwDocStyleSheet*>(&m_xBasePool->Make(rName, nFamily, nMask)); + } + } + + switch(nFamily) + { + case SfxStyleFamily::Para: + { + SwTextFormatColl* pColl = pStyle->GetCollection(); + if(pColl && !pColl->IsDefault()) + { + pCurrWrtShell->StartAllAction(); + pCurrWrtShell->FillByEx(pColl); + // also apply template to remove hard set attributes + SwTextFormatColl * pDerivedFrom = pCurrWrtShell->GetCurTextFormatColl(); + pColl->SetDerivedFrom(pDerivedFrom); + + // set the mask at the Collection: + sal_uInt16 nId = pColl->GetPoolFormatId() & 0x87ff; + switch( nMask & static_cast<SfxStyleSearchBits>(0x0fff) ) + { + case SfxStyleSearchBits::SwText: + nId |= COLL_TEXT_BITS; + break; + case SfxStyleSearchBits::SwChapter: + nId |= COLL_DOC_BITS; + break; + case SfxStyleSearchBits::SwList: + nId |= COLL_LISTS_BITS; + break; + case SfxStyleSearchBits::SwIndex: + nId |= COLL_REGISTER_BITS; + break; + case SfxStyleSearchBits::SwExtra: + nId |= COLL_EXTRA_BITS; + break; + case SfxStyleSearchBits::SwHtml: + nId |= COLL_HTML_BITS; + break; + default: break; + } + pColl->SetPoolFormatId(nId); + + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoTextFormatCollCreate>(pColl, pDerivedFrom, GetDoc())); + } + pCurrWrtShell->SetTextFormatColl(pColl); + pCurrWrtShell->EndAllAction(); + } + } + break; + case SfxStyleFamily::Frame: + { + SwFrameFormat* pFrame = pStyle->GetFrameFormat(); + if(pCurrWrtShell->IsFrameSelected() && pFrame && !pFrame->IsDefault()) + { + pCurrWrtShell->StartAllAction(); + + SfxItemSet aSet(GetPool(), aFrameFormatSetRange ); + pCurrWrtShell->GetFlyFrameAttr( aSet ); + aSet.ClearItem(RES_ANCHOR); // tdf#112574 no anchor in styles + + SwFrameFormat* pFFormat = pCurrWrtShell->GetSelectedFrameFormat(); + pFrame->SetDerivedFrom( pFFormat ); + pFrame->SetFormatAttr( aSet ); + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoFrameFormatCreate>(pFrame, pFFormat, GetDoc())); + } + // also apply template to remove hard set attributes + pCurrWrtShell->SetFrameFormat(pFrame); + pCurrWrtShell->EndAllAction(); + } + } + break; + case SfxStyleFamily::Char: + { + SwCharFormat* pChar = pStyle->GetCharFormat(); + if(pChar && !pChar->IsDefault()) + { + pCurrWrtShell->StartAllAction(); + pCurrWrtShell->FillByEx( pChar ); + SwCharFormat * pDerivedFrom = pCurrWrtShell->GetCurCharFormat(); + pChar->SetDerivedFrom( pDerivedFrom ); + SwFormatCharFormat aFormat( pChar ); + + if (GetDoc()->GetIDocumentUndoRedo().DoesUndo()) + { + // Looks like sometimes pDerivedFrom can be null and this is not supported by redo code + // So use default format as a derived from in such situations + GetDoc()->GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoCharFormatCreate>( + pChar, pDerivedFrom ? pDerivedFrom : GetDoc()->GetDfltCharFormat(), + GetDoc())); + } + pCurrWrtShell->SetAttrItem(aFormat); + pCurrWrtShell->EndAllAction(); + } + } + break; + + case SfxStyleFamily::Page: + { + pCurrWrtShell->StartAllAction(); + size_t nPgDsc = pCurrWrtShell->GetCurPageDesc(); + SwPageDesc& rSrc = const_cast<SwPageDesc&>(pCurrWrtShell->GetPageDesc( nPgDsc )); + SwPageDesc& rDest = *const_cast<SwPageDesc*>(pStyle->GetPageDesc()); + + sal_uInt16 nPoolId = rDest.GetPoolFormatId(); + sal_uInt16 nHId = rDest.GetPoolHelpId(); + sal_uInt8 nHFId = rDest.GetPoolHlpFileId(); + + pCurrWrtShell->GetDoc()->CopyPageDesc( rSrc, rDest ); + + // PoolId must NEVER be copied! + rDest.SetPoolFormatId( nPoolId ); + rDest.SetPoolHelpId( nHId ); + rDest.SetPoolHlpFileId( nHFId ); + + // when Headers/Footers are created, there is no Undo anymore! + pCurrWrtShell->GetDoc()->GetIDocumentUndoRedo().DelAllUndoObj(); + + pCurrWrtShell->EndAllAction(); + } + break; + + case SfxStyleFamily::Pseudo: + { + const SwNumRule* pCurRule = pCurrWrtShell->GetNumRuleAtCurrCursorPos(); + + if (pCurRule) + { + pCurrWrtShell->StartAllAction(); + + SwNumRule aRule( *pCurRule ); + OUString sOrigRule( aRule.GetName() ); + // #i91400# + aRule.SetName( pStyle->GetNumRule()->GetName(), + pCurrWrtShell->GetDoc()->getIDocumentListsAccess() ); + pCurrWrtShell->ChgNumRuleFormats( aRule ); + + pCurrWrtShell->ReplaceNumRule( sOrigRule, aRule.GetName() ); + + pCurrWrtShell->EndAllAction(); + } + } + break; + + case SfxStyleFamily::Table: + { + SwTableAutoFormat* pFormat = pStyle->GetTableFormat(); + if (pCurrWrtShell->GetTableAutoFormat(*pFormat)) + { + pCurrWrtShell->StartAllAction(); + + pCurrWrtShell->SetTableStyle(rName); + + pCurrWrtShell->EndAllAction(); + } + } + break; + + default: break; + } +} + +sfx::AccessibilityIssueCollection SwDocShell::runAccessibilityCheck() +{ + sw::AccessibilityCheck aCheck(m_xDoc.get()); + aCheck.check(); + return aCheck.getIssueCollecton(); +} + +std::set<Color> SwDocShell::GetDocColors() +{ + return m_xDoc->GetDocColors(); +} + +void SwDocShell::LoadStyles( SfxObjectShell& rSource ) +{ + LoadStyles_(rSource, false); +} + +// bPreserveCurrentDocument determines whether SetFixFields() is called +// This call modifies the source document. This mustn't happen when the source +// is a document the user is working on. +// Calls of ::LoadStyles() normally use files especially loaded for the purpose +// of importing styles. +void SwDocShell::LoadStyles_( SfxObjectShell& rSource, bool bPreserveCurrentDocument ) +{ +/* [Description] + + This method is called by SFx if Styles have to be reloaded from a + document-template. Existing Styles should be overwritten by that. + That's why the document has to be reformatted. Therefore applications + will usually override this method and call the baseclass' implementation + in their implementation. +*/ + // When the source is our document, we do the checking ourselves + // (much quicker and doesn't use the crutch StxStylePool). + if( dynamic_cast<const SwDocShell*>( &rSource) != nullptr) + { + // in order for the Headers/Footers not to get the fixed content + // of the template, update all the Source's + // FixFields once. + if(!bPreserveCurrentDocument) + static_cast<SwDocShell&>(rSource).m_xDoc->getIDocumentFieldsAccess().SetFixFields(nullptr); + if (m_pWrtShell) + { + // rhbz#818557, fdo#58893: EndAllAction will call SelectShell(), + // which pushes a bunch of SfxShells that are not cleared + // (for unknown reasons) when closing the document, causing crash; + // setting g_bNoInterrupt appears to avoid the problem. + ::comphelper::FlagRestorationGuard g(g_bNoInterrupt, true); + m_pWrtShell->StartAllAction(); + m_xDoc->ReplaceStyles( *static_cast<SwDocShell&>(rSource).m_xDoc ); + m_pWrtShell->EndAllAction(); + } + else + { + bool bModified = m_xDoc->getIDocumentState().IsModified(); + m_xDoc->ReplaceStyles( *static_cast<SwDocShell&>(rSource).m_xDoc ); + if (!bModified && m_xDoc->getIDocumentState().IsModified() && !m_pView) + { + // the View is created later, but overwrites the Modify-Flag. + // Undo doesn't work anymore anyways. + m_xDoc->GetIDocumentUndoRedo().SetUndoNoResetModified(); + } + } + } + else + SfxObjectShell::LoadStyles( rSource ); +} + +void SwDocShell::FormatPage( + const OUString& rPage, + const OString& rPageId, + SwWrtShell& rActShell, + SfxRequest* pRequest) +{ + Edit(rPage, OUString(), SfxStyleFamily::Page, SfxStyleSearchBits::Auto, false, rPageId, &rActShell, pRequest); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/docstyle.cxx b/sw/source/uibase/app/docstyle.cxx new file mode 100644 index 000000000..693ae7f20 --- /dev/null +++ b/sw/source/uibase/app/docstyle.cxx @@ -0,0 +1,3220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cstdlib> + +#include <hintids.hxx> +#include <rtl/ustrbuf.hxx> +#include <svl/itemiter.hxx> +#include <svl/eitem.hxx> +#include <unotools/syslocale.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/numitem.hxx> +#include <editeng/lrspitem.hxx> +#include <drawdoc.hxx> +#include <fmtcol.hxx> +#include <uitool.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <frmfmt.hxx> +#include <charfmt.hxx> +#include <tblafmt.hxx> +#include <poolfmt.hxx> +#include <pagedesc.hxx> +#include <docstyle.hxx> +#include <docary.hxx> +#include <ccoll.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentState.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <paratr.hxx> +#include <SwStyleNameMapper.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <unotools/intlwrapper.hxx> +#include <numrule.hxx> +#include <svx/xdef.hxx> +#include <SwRewriter.hxx> +#include <hints.hxx> +#include <frameformats.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflftrit.hxx> +#include <svx/drawitem.hxx> + +// The Format names in the list of all names have the +// following family as their first character: + +#define cCHAR u'c' +#define cPARA u'p' +#define cFRAME u'f' +#define cPAGE u'g' +#define cNUMRULE u'n' +#define cTABSTYLE u't' +#define cCELLSTYLE u'b' + +using namespace com::sun::star; + +// At the names' publication, this character is removed again and the +// family is newly generated. + +// In addition now there is the Bit bPhysical. In case this Bit is +// TRUE, the Pool-Formatnames are not being submitted. + +namespace { + +class SwImplShellAction +{ + SwWrtShell* pSh; + std::unique_ptr<CurrShell> pCurrSh; +public: + explicit SwImplShellAction( SwDoc& rDoc ); + ~SwImplShellAction() COVERITY_NOEXCEPT_FALSE; + SwImplShellAction(const SwImplShellAction&) = delete; + SwImplShellAction& operator=(const SwImplShellAction&) = delete; +}; + +} + +SwImplShellAction::SwImplShellAction( SwDoc& rDoc ) +{ + if( rDoc.GetDocShell() ) + pSh = rDoc.GetDocShell()->GetWrtShell(); + else + pSh = nullptr; + + if( pSh ) + { + pCurrSh.reset( new CurrShell( pSh ) ); + pSh->StartAllAction(); + } +} + +SwImplShellAction::~SwImplShellAction() COVERITY_NOEXCEPT_FALSE +{ + if( pCurrSh ) + { + pSh->EndAllAction(); + pCurrSh.reset(); + } +} + +// find/create SwCharFormate +// possibly fill Style +static SwCharFormat* lcl_FindCharFormat( SwDoc& rDoc, + const OUString& rName, + SwDocStyleSheet* pStyle = nullptr, + bool bCreate = true ) +{ + SwCharFormat* pFormat = nullptr; + if (!rName.isEmpty()) + { + pFormat = rDoc.FindCharFormatByName( rName ); + if( !pFormat && rName == SwResId(STR_POOLCHR_STANDARD)) + { + // Standard-Character template + pFormat = rDoc.GetDfltCharFormat(); + } + + if( !pFormat && bCreate ) + { // explore Pool + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::ChrFmt); + if(nId != USHRT_MAX) + pFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(nId); + } + } + if(pStyle) + { + if(pFormat) + { + pStyle->SetPhysical(true); + SwFormat* p = pFormat->DerivedFrom(); + if( p && !p->IsDefault() ) + pStyle->PresetParent( p->GetName() ); + else + pStyle->PresetParent( OUString() ); + } + else + pStyle->SetPhysical(false); + } + return pFormat; +} + +// find/create ParaFormats +// fill Style +static SwTextFormatColl* lcl_FindParaFormat( SwDoc& rDoc, + const OUString& rName, + SwDocStyleSheet* pStyle = nullptr, + bool bCreate = true ) +{ + SwTextFormatColl* pColl = nullptr; + + if (!rName.isEmpty()) + { + pColl = rDoc.FindTextFormatCollByName( rName ); + if( !pColl && bCreate ) + { // explore Pool + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::TxtColl); + if(nId != USHRT_MAX) + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(nId); + } + } + + if(pStyle) + { + if(pColl) + { + pStyle->SetPhysical(true); + if( pColl->DerivedFrom() && !pColl->DerivedFrom()->IsDefault() ) + pStyle->PresetParent( pColl->DerivedFrom()->GetName() ); + else + pStyle->PresetParent( OUString() ); + + SwTextFormatColl& rNext = pColl->GetNextTextFormatColl(); + pStyle->PresetFollow(rNext.GetName()); + } + else + pStyle->SetPhysical(false); + } + return pColl; +} + +// Border formats +static SwFrameFormat* lcl_FindFrameFormat( SwDoc& rDoc, + const OUString& rName, + SwDocStyleSheet* pStyle = nullptr, + bool bCreate = true ) +{ + SwFrameFormat* pFormat = nullptr; + if( !rName.isEmpty() ) + { + pFormat = rDoc.FindFrameFormatByName( rName ); + if( !pFormat && bCreate ) + { // explore Pool + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::FrmFmt); + if(nId != USHRT_MAX) + pFormat = rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool(nId); + } + } + + if(pStyle) + { + if(pFormat) + { + pStyle->SetPhysical(true); + if( pFormat->DerivedFrom() && !pFormat->DerivedFrom()->IsDefault() ) + pStyle->PresetParent( pFormat->DerivedFrom()->GetName() ); + else + pStyle->PresetParent( OUString() ); + } + else + pStyle->SetPhysical(false); + } + return pFormat; +} + +// Page descriptors +static const SwPageDesc* lcl_FindPageDesc( SwDoc& rDoc, + const OUString& rName, + SwDocStyleSheet* pStyle = nullptr, + bool bCreate = true ) +{ + const SwPageDesc* pDesc = nullptr; + + if (!rName.isEmpty()) + { + pDesc = rDoc.FindPageDesc(rName); + if( !pDesc && bCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::PageDesc); + if(nId != USHRT_MAX) + pDesc = rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(nId); + } + } + + if(pStyle) + { + if(pDesc) + { + pStyle->SetPhysical(true); + if(pDesc->GetFollow()) + pStyle->PresetFollow(pDesc->GetFollow()->GetName()); + else + pStyle->PresetParent( OUString() ); + } + else + pStyle->SetPhysical(false); + } + return pDesc; +} + +static const SwNumRule* lcl_FindNumRule( SwDoc& rDoc, + const OUString& rName, + SwDocStyleSheet* pStyle = nullptr, + bool bCreate = true ) +{ + const SwNumRule* pRule = nullptr; + + if (!rName.isEmpty()) + { + pRule = rDoc.FindNumRulePtr( rName ); + if( !pRule && bCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::NumRule); + if(nId != USHRT_MAX) + pRule = rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool(nId); + } + } + + if(pStyle) + { + if(pRule) + { + pStyle->SetPhysical(true); + pStyle->PresetParent( OUString() ); + } + else + pStyle->SetPhysical(false); + } + return pRule; +} + +static SwTableAutoFormat* lcl_FindTableStyle(SwDoc& rDoc, const OUString& rName, SwDocStyleSheet *pStyle = nullptr, bool bCreate = true) +{ + SwTableAutoFormat* pFormat = nullptr; + + if (!rName.isEmpty()) + { + pFormat = rDoc.GetTableStyles().FindAutoFormat(rName); + if (!pFormat && bCreate) + { + SwTableAutoFormat aNew(rName); + rDoc.GetTableStyles().AddAutoFormat(aNew); + } + } + + if(pStyle) + { + if(pFormat) + { + pStyle->SetPhysical(true); + pStyle->PresetParent(OUString()); + } + else + pStyle->SetPhysical(false); + } + return pFormat; +} + +static const SwBoxAutoFormat* lcl_FindCellStyle(SwDoc& rDoc, const OUString& rName, SwDocStyleSheet *pStyle) +{ + const SwBoxAutoFormat* pFormat = rDoc.GetCellStyles().GetBoxFormat(rName); + + if (!pFormat) + { + const auto& aTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap(); + SwTableAutoFormatTable& rTableStyles = rDoc.GetTableStyles(); + for (size_t i=0; i < rTableStyles.size() && !pFormat; ++i) + { + const SwTableAutoFormat& rTableStyle = rTableStyles[i]; + for (size_t nBoxFormat=0; nBoxFormat < aTableTemplateMap.size() && !pFormat; ++nBoxFormat) + { + const sal_uInt32 nBoxIndex = aTableTemplateMap[nBoxFormat]; + const SwBoxAutoFormat& rBoxFormat = rTableStyle.GetBoxFormat(nBoxIndex); + OUString sBoxFormatName; + SwStyleNameMapper::FillProgName(rTableStyle.GetName(), sBoxFormatName, SwGetPoolIdFromName::TabStyle); + sBoxFormatName += rTableStyle.GetTableTemplateCellSubName(rBoxFormat); + if (rName == sBoxFormatName) + pFormat = &rBoxFormat; + } + } + } + + if(pStyle) + { + if(pFormat) + { + pStyle->SetPhysical(true); + pStyle->PresetParent(OUString()); + } + else + pStyle->SetPhysical(false); + } + return pFormat; +} + +sal_uInt32 SwStyleSheetIterator::SwPoolFormatList::FindName(SfxStyleFamily eFam, + const OUString &rName) +{ + if(!maImpl.empty()) + { + sal_Unicode cStyle(0); + switch( eFam ) + { + case SfxStyleFamily::Char: + cStyle = cCHAR; + break; + case SfxStyleFamily::Para: + cStyle = cPARA; + break; + case SfxStyleFamily::Frame: + cStyle = cFRAME; + break; + case SfxStyleFamily::Page: + cStyle = cPAGE; + break; + case SfxStyleFamily::Pseudo: + cStyle = cNUMRULE; + break; + case SfxStyleFamily::Table: + cStyle = cTABSTYLE; + break; + case SfxStyleFamily::Cell: + cStyle = cCELLSTYLE; + break; + default: + cStyle = ' '; + break; + } + const OUString sSrch = OUStringChar(cStyle) + rName; + + UniqueHash::const_iterator it = maUnique.find(sSrch); + if (it != maUnique.end()) + { + sal_uInt32 nIdx = it->second; + assert (nIdx < maImpl.size()); + assert (maImpl.size() == maUnique.size()); + return nIdx; + } + } + return SAL_MAX_UINT32; +} + +void SwStyleSheetIterator::SwPoolFormatList::rehash() +{ + maUnique.clear(); + for (size_t i = 0; i < maImpl.size(); i++) + maUnique[maImpl[i]] = i; + assert (maImpl.size() == maUnique.size()); +} + +void SwStyleSheetIterator::SwPoolFormatList::RemoveName(SfxStyleFamily eFam, + const OUString &rName) +{ + sal_uInt32 nTmpPos = FindName( eFam, rName ); + if( nTmpPos < maImpl.size() ) + maImpl.erase(maImpl.begin() + nTmpPos); + + // assumption: this seldom occurs, the iterator is built, then emptied. + rehash(); + assert (maImpl.size() == maUnique.size()); +} + +// Add Strings to the list of templates +void SwStyleSheetIterator::SwPoolFormatList::Append( char cChar, const OUString& rStr ) +{ + const OUString aStr = OUStringChar(cChar) + rStr; + + UniqueHash::const_iterator it = maUnique.find(aStr); + if (it != maUnique.end()) + return; + + maUnique[aStr] = static_cast<sal_uInt32>(maImpl.size()); + maImpl.push_back(aStr); +} + +// UI-sided implementation of StyleSheets +// uses the Core-Engine +SwDocStyleSheet::SwDocStyleSheet( SwDoc& rDocument, + SwDocStyleSheetPool& rPool) : + + SfxStyleSheetBase( OUString(), &rPool, SfxStyleFamily::Char, SfxStyleSearchBits::Auto ), + pCharFormat(nullptr), + pColl(nullptr), + pFrameFormat(nullptr), + pDesc(nullptr), + pNumRule(nullptr), + pTableFormat(nullptr), + pBoxFormat(nullptr), + rDoc(rDocument), + aCoreSet( + rPool.GetPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_PARATR_BEGIN, RES_FRMATR_END - 1, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1, + // FillAttribute support: + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_PAGE, SID_ATTR_PAGE_EXT1, + SID_ATTR_PAGE_HEADERSET, SID_ATTR_PAGE_FOOTERSET, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL, + // Items to hand over XPropertyList things like XColorList, + // XHatchList, XGradientList, and XBitmapList to the Area TabPage: + SID_COLOR_TABLE, SID_PATTERN_LIST, + SID_SWREGISTER_COLLECTION, SID_SWREGISTER_COLLECTION, + SID_ATTR_PARA_PAGENUM, SID_ATTR_PARA_PAGENUM, + SID_SWREGISTER_MODE, SID_SWREGISTER_MODE, + SID_ATTR_BRUSH_CHAR, SID_ATTR_BRUSH_CHAR, + SID_ATTR_NUMBERING_RULE, SID_ATTR_NUMBERING_RULE, + SID_ATTR_AUTO_STYLE_UPDATE, SID_ATTR_AUTO_STYLE_UPDATE, + FN_PARAM_FTN_INFO, FN_PARAM_FTN_INFO, + FN_COND_COLL, FN_COND_COLL>{}), + bPhysical(false) +{ + nHelpId = UCHAR_MAX; +} + +SwDocStyleSheet::SwDocStyleSheet( const SwDocStyleSheet& ) = default; + +SwDocStyleSheet::~SwDocStyleSheet() = default; + +void SwDocStyleSheet::Reset() +{ + aName.clear(); + aFollow.clear(); + aParent.clear(); + SetPhysical(false); +} + +void SwDocStyleSheet::SetGrabBagItem(const uno::Any& rVal) +{ + bool bChg = false; + if (!bPhysical) + FillStyleSheet(FillPhysical); + + SwFormat* pFormat = nullptr; + switch (nFamily) + { + case SfxStyleFamily::Char: + pFormat = rDoc.FindCharFormatByName(aName); + if (pFormat) + { + pFormat->SetGrabBagItem(rVal); + bChg = true; + } + break; + case SfxStyleFamily::Para: + pFormat = rDoc.FindTextFormatCollByName(aName); + if (pFormat) + { + pFormat->SetGrabBagItem(rVal); + bChg = true; + } + break; + case SfxStyleFamily::Pseudo: + { + SwNumRule* pRule = rDoc.FindNumRulePtr(aName); + if (pRule) + { + pRule->SetGrabBagItem(rVal); + bChg = true; + } + } + break; + default: + break; + } + + if (bChg) + { + dynamic_cast<SwDocStyleSheetPool&>(*m_pPool).InvalidateIterator(); + m_pPool->Broadcast(SfxStyleSheetHint(SfxHintId::StyleSheetModified, *this)); + SwEditShell* pSh = rDoc.GetEditShell(); + if (pSh) + pSh->CallChgLnk(); + } +} + +void SwDocStyleSheet::GetGrabBagItem(uno::Any& rVal) const +{ + SwFormat* pFormat = nullptr; + switch (nFamily) + { + case SfxStyleFamily::Char: + pFormat = rDoc.FindCharFormatByName(aName); + if (pFormat) + pFormat->GetGrabBagItem(rVal); + break; + case SfxStyleFamily::Para: + pFormat = rDoc.FindTextFormatCollByName(aName); + if (pFormat) + pFormat->GetGrabBagItem(rVal); + break; + case SfxStyleFamily::Pseudo: + { + SwNumRule* pRule = rDoc.FindNumRulePtr(aName); + if (pRule) + pRule->GetGrabBagItem(rVal); + } + break; + default: + break; + } +} +// virtual methods +void SwDocStyleSheet::SetHidden( bool bValue ) +{ + bool bChg = false; + if(!bPhysical) + FillStyleSheet( FillPhysical ); + + SwFormat* pFormat = nullptr; + switch(nFamily) + { + case SfxStyleFamily::Char: + pFormat = rDoc.FindCharFormatByName( aName ); + if ( pFormat ) + { + pFormat->SetHidden( bValue ); + bChg = true; + } + break; + + case SfxStyleFamily::Para: + pFormat = rDoc.FindTextFormatCollByName( aName ); + if ( pFormat ) + { + pFormat->SetHidden( bValue ); + bChg = true; + } + break; + + case SfxStyleFamily::Frame: + pFormat = rDoc.FindFrameFormatByName( aName ); + if ( pFormat ) + { + pFormat->SetHidden( bValue ); + bChg = true; + } + break; + + case SfxStyleFamily::Page: + { + SwPageDesc* pPgDesc = rDoc.FindPageDesc(aName); + if ( pPgDesc ) + { + pPgDesc->SetHidden( bValue ); + bChg = true; + } + } + break; + + case SfxStyleFamily::Pseudo: + { + SwNumRule* pRule = rDoc.FindNumRulePtr( aName ); + if ( pRule ) + { + pRule->SetHidden( bValue ); + bChg = true; + } + } + break; + + case SfxStyleFamily::Table: + { + SwTableAutoFormat* pTableAutoFormat = rDoc.GetTableStyles().FindAutoFormat( aName ); + if ( pTableAutoFormat ) + { + pTableAutoFormat->SetHidden( bValue ); + bChg = true; + } + } + break; + + default: + break; + } + + if( bChg ) + { + // calling pPool->First() here would be quite slow... + dynamic_cast<SwDocStyleSheetPool&>(*m_pPool).InvalidateIterator(); // internal list has to be updated + m_pPool->Broadcast( SfxStyleSheetHint( SfxHintId::StyleSheetModified, *this ) ); + SwEditShell* pSh = rDoc.GetEditShell(); + if( pSh ) + pSh->CallChgLnk(); + } +} + +bool SwDocStyleSheet::IsHidden( ) const +{ + bool bRet = false; + + SwFormat* pFormat = nullptr; + switch(nFamily) + { + case SfxStyleFamily::Char: + pFormat = rDoc.FindCharFormatByName( aName ); + bRet = pFormat && pFormat->IsHidden( ); + break; + + case SfxStyleFamily::Para: + pFormat = rDoc.FindTextFormatCollByName( aName ); + bRet = pFormat && pFormat->IsHidden( ); + break; + + case SfxStyleFamily::Frame: + pFormat = rDoc.FindFrameFormatByName( aName ); + bRet = pFormat && pFormat->IsHidden( ); + break; + + case SfxStyleFamily::Page: + { + SwPageDesc* pPgDesc = rDoc.FindPageDesc(aName); + bRet = pPgDesc && pPgDesc->IsHidden( ); + } + break; + case SfxStyleFamily::Pseudo: + { + SwNumRule* pRule = rDoc.FindNumRulePtr( aName ); + bRet = pRule && pRule->IsHidden( ); + } + break; + case SfxStyleFamily::Table: + { + SwTableAutoFormat* pTableAutoFormat = rDoc.GetTableStyles().FindAutoFormat( aName ); + bRet = pTableAutoFormat && pTableAutoFormat->IsHidden( ); + } + break; + default: + break; + } + + return bRet; +} + +const OUString& SwDocStyleSheet::GetParent() const +{ + if( !bPhysical ) + { + // check if it's already in document + SwFormat* pFormat = nullptr; + SwGetPoolIdFromName eGetType; + switch(nFamily) + { + case SfxStyleFamily::Char: + pFormat = rDoc.FindCharFormatByName( aName ); + eGetType = SwGetPoolIdFromName::ChrFmt; + break; + + case SfxStyleFamily::Para: + pFormat = rDoc.FindTextFormatCollByName( aName ); + eGetType = SwGetPoolIdFromName::TxtColl; + break; + + case SfxStyleFamily::Frame: + pFormat = rDoc.FindFrameFormatByName( aName ); + eGetType = SwGetPoolIdFromName::FrmFmt; + break; + + case SfxStyleFamily::Page: + case SfxStyleFamily::Pseudo: + default: + { + static const OUString sEmpty; + return sEmpty; // there's no parent + } + } + + OUString sTmp; + if( !pFormat ) // not yet there, so default Parent + { + sal_uInt16 i = SwStyleNameMapper::GetPoolIdFromUIName( aName, eGetType ); + i = ::GetPoolParent( i ); + if( i && USHRT_MAX != i ) + SwStyleNameMapper::FillUIName( i, sTmp ); + } + else + { + SwFormat* p = pFormat->DerivedFrom(); + if( p && !p->IsDefault() ) + sTmp = p->GetName(); + } + SwDocStyleSheet* pThis = const_cast<SwDocStyleSheet*>(this); + pThis->aParent = sTmp; + } + return aParent; +} + +// Follower +const OUString& SwDocStyleSheet::GetFollow() const +{ + if( !bPhysical ) + { + SwDocStyleSheet* pThis = const_cast<SwDocStyleSheet*>(this); + pThis->FillStyleSheet( FillAllInfo ); + } + return aFollow; +} + +// What Linkage is possible +bool SwDocStyleSheet::HasFollowSupport() const +{ + switch(nFamily) + { + case SfxStyleFamily::Para : + case SfxStyleFamily::Page : return true; + case SfxStyleFamily::Frame: + case SfxStyleFamily::Char : + case SfxStyleFamily::Pseudo: return false; + default: + OSL_ENSURE(false, "unknown style family"); + } + return false; +} + +// Parent ? +bool SwDocStyleSheet::HasParentSupport() const +{ + bool bRet = false; + switch(nFamily) + { + case SfxStyleFamily::Char : + case SfxStyleFamily::Para : + case SfxStyleFamily::Frame: bRet = true; + break; + default:; //prevent warning + } + return bRet; +} + +bool SwDocStyleSheet::HasClearParentSupport() const +{ + bool bRet = false; + switch(nFamily) + { + case SfxStyleFamily::Para : + case SfxStyleFamily::Char : + case SfxStyleFamily::Frame: bRet = true; + break; + default:; //prevent warning + } + return bRet; +} + +// determine textual description +OUString SwDocStyleSheet::GetDescription(MapUnit eUnit) +{ + IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag()); + + const OUString sPlus(" + "); + if ( SfxStyleFamily::Page == nFamily ) + { + if( !pSet ) + GetItemSet(); + + SfxItemIter aIter( *pSet ); + OUStringBuffer aDesc; + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if(!IsInvalidItem(pItem)) + { + switch ( pItem->Which() ) + { + case RES_LR_SPACE: + case SID_ATTR_PAGE_SIZE: + case SID_ATTR_PAGE_MAXSIZE: + case SID_ATTR_PAGE_PAPERBIN: + case SID_ATTR_BORDER_INNER: + break; + default: + { + OUString aItemPresentation; + if ( !IsInvalidItem( pItem ) && + m_pPool->GetPool().GetPresentation( + *pItem, eUnit, aItemPresentation, aIntlWrapper ) ) + { + if ( !aDesc.isEmpty() && !aItemPresentation.isEmpty() ) + aDesc.append(sPlus); + aDesc.append(aItemPresentation); + } + } + } + } + } + return aDesc.makeStringAndClear(); + } + + if ( SfxStyleFamily::Frame == nFamily || SfxStyleFamily::Para == nFamily || SfxStyleFamily::Char == nFamily ) + { + if( !pSet ) + GetItemSet(); + + SfxItemIter aIter( *pSet ); + OUStringBuffer aDesc; + OUString sPageNum; + OUString sModel; + OUString sBreak; + bool bHasWesternFontPrefix = false; + bool bHasCJKFontPrefix = false; + bool bHasCTLFontPrefix = false; + SvtCJKOptions aCJKOptions; + SvtCTLOptions aCTLOptions; + + // Get currently used FillStyle and remember, also need the XFillFloatTransparenceItem + // to decide if gradient transparence is used + const drawing::FillStyle eFillStyle(pSet->Get(XATTR_FILLSTYLE).GetValue()); + const bool bUseFloatTransparence(pSet->Get(XATTR_FILLFLOATTRANSPARENCE).IsEnabled()); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if(!IsInvalidItem(pItem)) + { + switch ( pItem->Which() ) + { + case SID_ATTR_AUTO_STYLE_UPDATE: + case RES_PAGEDESC: + break; + default: + { + OUString aItemPresentation; + if ( !IsInvalidItem( pItem ) && + m_pPool->GetPool().GetPresentation( + *pItem, eUnit, aItemPresentation, aIntlWrapper ) ) + { + bool bIsDefault = false; + switch ( pItem->Which() ) + { + case XATTR_FILLCOLOR: + { + // only use active FillStyle information + bIsDefault = (drawing::FillStyle_SOLID == eFillStyle); + break; + } + case XATTR_FILLGRADIENT: + { + // only use active FillStyle information + bIsDefault = (drawing::FillStyle_GRADIENT == eFillStyle); + break; + } + case XATTR_FILLHATCH: + { + // only use active FillStyle information + bIsDefault = (drawing::FillStyle_HATCH == eFillStyle); + break; + } + case XATTR_FILLBITMAP: + { + // only use active FillStyle information + bIsDefault = (drawing::FillStyle_BITMAP == eFillStyle); + break; + } + case XATTR_FILLTRANSPARENCE: + { + // only active when not FloatTransparence + bIsDefault = !bUseFloatTransparence; + break; + } + case XATTR_FILLFLOATTRANSPARENCE: + { + // only active when FloatTransparence + bIsDefault = bUseFloatTransparence; + break; + } + + case SID_ATTR_PARA_PAGENUM: + sPageNum = aItemPresentation; + break; + case SID_ATTR_PARA_MODEL: + sModel = aItemPresentation; + break; + case RES_BREAK: + sBreak = aItemPresentation; + break; + case RES_CHRATR_CJK_FONT: + case RES_CHRATR_CJK_FONTSIZE: + case RES_CHRATR_CJK_LANGUAGE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CJK_WEIGHT: + if(aCJKOptions.IsCJKFontEnabled()) + bIsDefault = true; + if(!bHasCJKFontPrefix) + { + aItemPresentation = SwResId(STR_CJK_FONT) + aItemPresentation; + bHasCJKFontPrefix = true; + } + break; + case RES_CHRATR_CTL_FONT: + case RES_CHRATR_CTL_FONTSIZE: + case RES_CHRATR_CTL_LANGUAGE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_CTL_WEIGHT: + if(aCTLOptions.IsCTLFontEnabled()) + bIsDefault = true; + if(!bHasCTLFontPrefix) + { + aItemPresentation = SwResId(STR_CTL_FONT) + aItemPresentation; + bHasCTLFontPrefix = true; + } + break; + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_LANGUAGE: + case RES_CHRATR_POSTURE: + case RES_CHRATR_WEIGHT: + if(!bHasWesternFontPrefix) + { + aItemPresentation = SwResId(STR_WESTERN_FONT) + aItemPresentation; + bHasWesternFontPrefix = true; + } + [[fallthrough]]; + default: + bIsDefault = true; + } + if(bIsDefault) + { + if ( !aDesc.isEmpty() && !aItemPresentation.isEmpty() ) + aDesc.append(sPlus); + aDesc.append(aItemPresentation); + } + } + } + } + } + } + // Special treatment for Break, Page template and Site offset + if (!sModel.isEmpty()) + { + if (!aDesc.isEmpty()) + aDesc.append(sPlus); + aDesc.append(SwResId(STR_PAGEBREAK)).append(sPlus).append(sModel); + if (sPageNum != "0") + { + aDesc.append(sPlus).append(SwResId(STR_PAGEOFFSET)).append(sPageNum); + } + } + else if (!sBreak.isEmpty()) // Break can be valid only when NO Model + { + if (!aDesc.isEmpty()) + aDesc.append(sPlus); + aDesc.append(sBreak); + } + return aDesc.makeStringAndClear(); + } + + if( SfxStyleFamily::Pseudo == nFamily ) + { + return OUString(); + } + + return SfxStyleSheetBase::GetDescription(eUnit); +} + +// Set names +bool SwDocStyleSheet::SetName(const OUString& rStr, bool bReindexNow) +{ + if( rStr.isEmpty() ) + return false; + + if( aName != rStr ) + { + if( !SfxStyleSheetBase::SetName(rStr, bReindexNow)) + return false; + } + else if(!bPhysical) + FillStyleSheet( FillPhysical ); + + bool bChg = false; + switch(nFamily) + { + case SfxStyleFamily::Char : + { + OSL_ENSURE(pCharFormat, "SwCharFormat missing!"); + if( pCharFormat && pCharFormat->GetName() != rStr ) + { + if (!pCharFormat->GetName().isEmpty()) + rDoc.RenameFormat(*pCharFormat, rStr); + else + pCharFormat->SetName(rStr); + + bChg = true; + } + break; + } + case SfxStyleFamily::Para : + { + OSL_ENSURE(pColl, "Collection missing!"); + if( pColl && pColl->GetName() != rStr ) + { + if (!pColl->GetName().isEmpty()) + rDoc.RenameFormat(*pColl, rStr); + else + pColl->SetName(rStr); + + bChg = true; + } + break; + } + case SfxStyleFamily::Frame: + { + OSL_ENSURE(pFrameFormat, "FrameFormat missing!"); + if( pFrameFormat && pFrameFormat->GetName() != rStr ) + { + if (!pFrameFormat->GetName().isEmpty()) + rDoc.RenameFormat(*pFrameFormat, rStr); + else + pFrameFormat->SetName( rStr ); + + bChg = true; + } + break; + } + case SfxStyleFamily::Page : + OSL_ENSURE(pDesc, "PageDesc missing!"); + if( pDesc && pDesc->GetName() != rStr ) + { + // Set PageDesc - copy with earlier one - probably not + // necessary for setting the name. So here we allow a + // cast. + SwPageDesc aPageDesc(*const_cast<SwPageDesc*>(pDesc)); + const OUString aOldName(aPageDesc.GetName()); + + aPageDesc.SetName( rStr ); + bool const bDoesUndo = rDoc.GetIDocumentUndoRedo().DoesUndo(); + + rDoc.GetIDocumentUndoRedo().DoUndo(!aOldName.isEmpty()); + rDoc.ChgPageDesc(aOldName, aPageDesc); + rDoc.GetIDocumentUndoRedo().DoUndo(bDoesUndo); + + rDoc.getIDocumentState().SetModified(); + bChg = true; + } + break; + case SfxStyleFamily::Pseudo: + OSL_ENSURE(pNumRule, "NumRule missing!"); + + if (pNumRule) + { + OUString aOldName = pNumRule->GetName(); + + if (!aOldName.isEmpty()) + { + if ( aOldName != rStr && + rDoc.RenameNumRule(aOldName, rStr)) + { + pNumRule = rDoc.FindNumRulePtr(rStr); + rDoc.getIDocumentState().SetModified(); + + bChg = true; + } + } + else + { + // #i91400# + const_cast<SwNumRule*>(pNumRule)->SetName( rStr, rDoc.getIDocumentListsAccess() ); + rDoc.getIDocumentState().SetModified(); + + bChg = true; + } + } + + break; + + default: + OSL_ENSURE(false, "unknown style family"); + } + + if( bChg ) + { + m_pPool->First(nFamily); // internal list has to be updated + m_pPool->Broadcast( SfxStyleSheetHint( SfxHintId::StyleSheetModified, *this ) ); + SwEditShell* pSh = rDoc.GetEditShell(); + if( pSh ) + pSh->CallChgLnk(); + } + return true; +} + +// hierarchy of deduction +bool SwDocStyleSheet::SetParent( const OUString& rStr) +{ + SwFormat* pFormat = nullptr, *pParent = nullptr; + switch(nFamily) + { + case SfxStyleFamily::Char : + OSL_ENSURE( pCharFormat, "SwCharFormat missing!" ); + if( nullptr != ( pFormat = pCharFormat ) && !rStr.isEmpty() ) + pParent = lcl_FindCharFormat(rDoc, rStr); + break; + + case SfxStyleFamily::Para : + OSL_ENSURE( pColl, "Collection missing!"); + if( nullptr != ( pFormat = pColl ) && !rStr.isEmpty() ) + pParent = lcl_FindParaFormat( rDoc, rStr ); + break; + + case SfxStyleFamily::Frame: + OSL_ENSURE(pFrameFormat, "FrameFormat missing!"); + if( nullptr != ( pFormat = pFrameFormat ) && !rStr.isEmpty() ) + pParent = lcl_FindFrameFormat( rDoc, rStr ); + break; + + case SfxStyleFamily::Page: + case SfxStyleFamily::Pseudo: + break; + default: + OSL_ENSURE(false, "unknown style family"); + } + + bool bRet = false; + if( pFormat && pFormat->DerivedFrom() && + pFormat->DerivedFrom()->GetName() != rStr ) + { + { + SwImplShellAction aTmp( rDoc ); + bRet = pFormat->SetDerivedFrom( pParent ); + } + + if( bRet ) + { + aParent = rStr; + m_pPool->Broadcast( SfxStyleSheetHint( SfxHintId::StyleSheetModified, + *this ) ); + } + } + + return bRet; +} + +// Set Follower +bool SwDocStyleSheet::SetFollow( const OUString& rStr) +{ + if( !rStr.isEmpty() && !SfxStyleSheetBase::SetFollow( rStr )) + return false; + + SwImplShellAction aTmpSh( rDoc ); + switch(nFamily) + { + case SfxStyleFamily::Para : + { + OSL_ENSURE(pColl, "Collection missing!"); + if( pColl ) + { + SwTextFormatColl* pFollow = pColl; + if( !rStr.isEmpty() && nullptr == (pFollow = lcl_FindParaFormat(rDoc, rStr) )) + pFollow = pColl; + + pColl->SetNextTextFormatColl(*pFollow); + } + break; + } + case SfxStyleFamily::Page : + { + OSL_ENSURE(pDesc, "PageDesc missing!"); + if( pDesc ) + { + const SwPageDesc* pFollowDesc = !rStr.isEmpty() + ? lcl_FindPageDesc(rDoc, rStr) + : nullptr; + size_t nId = 0; + if (pFollowDesc != pDesc->GetFollow() && rDoc.FindPageDesc(pDesc->GetName(), &nId)) + { + SwPageDesc aDesc( *pDesc ); + aDesc.SetFollow( pFollowDesc ); + rDoc.ChgPageDesc( nId, aDesc ); + pDesc = &rDoc.GetPageDesc( nId ); + } + } + break; + } + case SfxStyleFamily::Char: + case SfxStyleFamily::Frame: + case SfxStyleFamily::Pseudo: + break; + default: + OSL_ENSURE(false, "unknown style family"); + } + + return true; +} + +static +std::unique_ptr<SfxItemSet> lcl_SwFormatToFlatItemSet(SwFormat const *const pFormat) +{ + // note: we don't add the odd items that GetItemSet() would add + // because they don't seem relevant for preview + std::vector<SfxItemSet const*> sets; + sets.push_back(&pFormat->GetAttrSet()); + while (SfxItemSet const*const pParent = sets.back()->GetParent()) + { + sets.push_back(pParent); + } + // start by copying top-level parent set + std::unique_ptr<SfxItemSet> pRet(new SfxItemSet(*sets.back())); + sets.pop_back(); + for (auto iter = sets.rbegin(); iter != sets.rend(); ++iter) + { // in reverse so child overrides parent + pRet->Put(**iter); + } + return pRet; +} + +std::unique_ptr<SfxItemSet> SwDocStyleSheet::GetItemSetForPreview() +{ + if (SfxStyleFamily::Page == nFamily || SfxStyleFamily::Pseudo == nFamily || SfxStyleFamily::Table == nFamily) + { + SAL_WARN("sw.ui", "GetItemSetForPreview not implemented for page or number or table style"); + return std::unique_ptr<SfxItemSet>(); + } + if (!bPhysical) + { + // because not only this style, but also any number of its parents + // (or follow style) may not actually exist in the document at this + // time, return one "flattened" item set that contains all items from + // all parents. + std::unique_ptr<SfxItemSet> pRet; + FillStyleSheet(FillPreview, &pRet); + assert(pRet); + return pRet; + } + else + { + switch (nFamily) + { + case SfxStyleFamily::Char: + return lcl_SwFormatToFlatItemSet(pCharFormat); + case SfxStyleFamily::Para: + return lcl_SwFormatToFlatItemSet(pColl); + case SfxStyleFamily::Frame: + return lcl_SwFormatToFlatItemSet(pFrameFormat); + default: + std::abort(); + } + } +} + +// extract ItemSet to Name and Family, Mask + +SfxItemSet& SwDocStyleSheet::GetItemSet() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + + switch(nFamily) + { + case SfxStyleFamily::Char: + case SfxStyleFamily::Para: + case SfxStyleFamily::Frame: + { + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + aBoxInfo.SetTable( false ); + aBoxInfo.SetDist( true ); // always show gap field + aBoxInfo.SetMinDist( true );// set minimum size in tables and paragraphs + aBoxInfo.SetDefDist( MIN_BORDER_DIST );// always set Default-Gap + // Single lines can only have DontCare-Status in tables + aBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISABLE ); + + if( nFamily == SfxStyleFamily::Char ) + { + SAL_WARN_IF(!pCharFormat, "sw.ui", "Where's SwCharFormat"); + aCoreSet.Put(pCharFormat->GetAttrSet()); + aCoreSet.Put( aBoxInfo ); + + if(pCharFormat->DerivedFrom()) + aCoreSet.SetParent(&pCharFormat->DerivedFrom()->GetAttrSet()); + } + else if ( nFamily == SfxStyleFamily::Para ) + { + OSL_ENSURE(pColl, "Where's Collection"); + aCoreSet.Put(pColl->GetAttrSet()); + aCoreSet.Put( aBoxInfo ); + aCoreSet.Put(SfxBoolItem(SID_ATTR_AUTO_STYLE_UPDATE, pColl->IsAutoUpdateFormat())); + + if(pColl->DerivedFrom()) + aCoreSet.SetParent(&pColl->DerivedFrom()->GetAttrSet()); + } + else + { + OSL_ENSURE(pFrameFormat, "Where's FrameFormat"); + aCoreSet.Put(pFrameFormat->GetAttrSet()); + aCoreSet.Put( aBoxInfo ); + aCoreSet.Put(SfxBoolItem(SID_ATTR_AUTO_STYLE_UPDATE, pFrameFormat->IsAutoUpdateFormat())); + + if(pFrameFormat->DerivedFrom()) + aCoreSet.SetParent(&pFrameFormat->DerivedFrom()->GetAttrSet()); + + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + const SwDrawModel* pDrawModel = rDoc.getIDocumentDrawModelAccess().GetDrawModel(); + + aCoreSet.Put(SvxColorListItem(pDrawModel->GetColorList(), SID_COLOR_TABLE)); + aCoreSet.Put(SvxGradientListItem(pDrawModel->GetGradientList(), SID_GRADIENT_LIST)); + aCoreSet.Put(SvxHatchListItem(pDrawModel->GetHatchList(), SID_HATCH_LIST)); + aCoreSet.Put(SvxBitmapListItem(pDrawModel->GetBitmapList(), SID_BITMAP_LIST)); + aCoreSet.Put(SvxPatternListItem(pDrawModel->GetPatternList(), SID_PATTERN_LIST)); + } + } + break; + + case SfxStyleFamily::Page : + { + // set correct parent to get the drawing::FillStyle_NONE FillStyle as needed + if(!aCoreSet.GetParent()) + { + aCoreSet.SetParent(&rDoc.GetDfltFrameFormat()->GetAttrSet()); + } + + OSL_ENSURE(pDesc, "No PageDescriptor"); + ::PageDescToItemSet(*const_cast<SwPageDesc*>(pDesc), aCoreSet); + } + break; + + case SfxStyleFamily::Pseudo: + { + OSL_ENSURE(pNumRule, "No NumRule"); + SvxNumRule aRule = pNumRule->MakeSvxNumRule(); + aCoreSet.Put(SvxNumBulletItem(aRule)); + } + break; + + default: + OSL_ENSURE(false, "unknown style family"); + } + // Member of Baseclass + pSet = &aCoreSet; + + return aCoreSet; +} + +void SwDocStyleSheet::MergeIndentAttrsOfListStyle( SfxItemSet& rSet ) +{ + if ( nFamily != SfxStyleFamily::Para ) + { + return; + } + + OSL_ENSURE( pColl, "<SwDocStyleSheet::MergeIndentAttrsOfListStyle(..)> - missing paragraph style"); + if ( pColl->AreListLevelIndentsApplicable() ) + { + OSL_ENSURE( pColl->GetItemState( RES_PARATR_NUMRULE ) == SfxItemState::SET, + "<SwDocStyleSheet::MergeIndentAttrsOfListStyle(..)> - list level indents are applicable at paragraph style, but no list style found. Serious defect." ); + const OUString sNumRule = pColl->GetNumRule().GetValue(); + if (!sNumRule.isEmpty()) + { + const SwNumRule* pRule = rDoc.FindNumRulePtr( sNumRule ); + if( pRule ) + { + const SwNumFormat& rFormat = pRule->Get( 0 ); + 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 ); + } + } + } + } +} + +// handling of parameter <bResetIndentAttrsAtParagraphStyle> +void SwDocStyleSheet::SetItemSet( const SfxItemSet& rSet, + const bool bResetIndentAttrsAtParagraphStyle ) +{ + // if applicable determine format first + if(!bPhysical) + FillStyleSheet( FillPhysical ); + + SwImplShellAction aTmpSh( rDoc ); + + OSL_ENSURE( &rSet != &aCoreSet, "SetItemSet with own Set is not allowed" ); + + if (rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + SwRewriter aRewriter; + aRewriter.AddRule( UndoArg1, GetName() ); + rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::INSFMTATTR, &aRewriter ); + } + + SwFormat* pFormat = nullptr; + std::unique_ptr<SwPageDesc> pNewDsc; + size_t nPgDscPos = 0; + + switch(nFamily) + { + case SfxStyleFamily::Char : + { + OSL_ENSURE(pCharFormat, "Where's CharFormat"); + pFormat = pCharFormat; + } + break; + + case SfxStyleFamily::Para : + { + OSL_ENSURE(pColl, "Where's Collection"); + const SfxPoolItem* pAutoUpdate; + if(SfxItemState::SET == rSet.GetItemState(SID_ATTR_AUTO_STYLE_UPDATE,false, &pAutoUpdate )) + { + pColl->SetAutoUpdateFormat(static_cast<const SfxBoolItem*>(pAutoUpdate)->GetValue()); + } + + const SwCondCollItem* pCondItem; + if( SfxItemState::SET != rSet.GetItemState( FN_COND_COLL, false, + reinterpret_cast<const SfxPoolItem**>(&pCondItem) )) + pCondItem = nullptr; + + if( RES_CONDTXTFMTCOLL == pColl->Which() && pCondItem ) + { + const CommandStruct* pCmds = SwCondCollItem::GetCmds(); + for(sal_uInt16 i = 0; i < COND_COMMAND_COUNT; i++) + { + SwCollCondition aCond( nullptr, pCmds[ i ].nCnd, pCmds[ i ].nSubCond ); + static_cast<SwConditionTextFormatColl*>(pColl)->RemoveCondition( aCond ); + const OUString sStyle = pCondItem->GetStyle( i ); + if (sStyle.isEmpty()) + continue; + SwFormat *const pFindFormat = lcl_FindParaFormat( rDoc, sStyle ); + if (pFindFormat) + { + aCond.RegisterToFormat( *pFindFormat ); + static_cast<SwConditionTextFormatColl*>(pColl)->InsertCondition( aCond ); + } + } + + // Update document to new conditions + SwCondCollCondChg aMsg( pColl ); + pColl->ModifyNotification( &aMsg, &aMsg ); + } + else if( pCondItem && !pColl->HasWriterListeners() ) + { + // no conditional template, then first create and adopt + // all important values + SwConditionTextFormatColl* pCColl = rDoc.MakeCondTextFormatColl( + pColl->GetName(), static_cast<SwTextFormatColl*>(pColl->DerivedFrom()) ); + if( pColl != &pColl->GetNextTextFormatColl() ) + pCColl->SetNextTextFormatColl( pColl->GetNextTextFormatColl() ); + + if( pColl->IsAssignedToListLevelOfOutlineStyle()) + pCColl->AssignToListLevelOfOutlineStyle(pColl->GetAssignedOutlineStyleLevel()); + else + pCColl->DeleteAssignmentToListLevelOfOutlineStyle(); + + const CommandStruct* pCmds = SwCondCollItem::GetCmds(); + for( sal_uInt16 i = 0; i < COND_COMMAND_COUNT; ++i ) + { + const OUString sStyle = pCondItem->GetStyle( i ); + if (sStyle.isEmpty()) + continue; + SwTextFormatColl *const pFindFormat = lcl_FindParaFormat( rDoc, sStyle ); + if (pFindFormat) + { + pCColl->InsertCondition( SwCollCondition( pFindFormat, + pCmds[ i ].nCnd, pCmds[ i ].nSubCond ) ); + } + } + + rDoc.DelTextFormatColl( pColl ); + pColl = pCColl; + } + if ( bResetIndentAttrsAtParagraphStyle && + rSet.GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET && + rSet.GetItemState( RES_LR_SPACE, false ) != SfxItemState::SET && + pColl->GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET ) + { + rDoc.ResetAttrAtFormat( RES_LR_SPACE, *pColl ); + } + + // #i56252: If a standard numbering style is assigned to a standard paragraph style + // we have to create a physical instance of the numbering style. If we do not and + // neither the paragraph style nor the numbering style is used in the document + // the numbering style will not be saved with the document and the assignment got lost. + const SfxPoolItem* pNumRuleItem = nullptr; + if( SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE, false, &pNumRuleItem ) ) + { // Setting a numbering rule? + const OUString sNumRule = static_cast<const SwNumRuleItem*>(pNumRuleItem)->GetValue(); + if (!sNumRule.isEmpty()) + { + SwNumRule* pRule = rDoc.FindNumRulePtr( sNumRule ); + if( !pRule ) + { // Numbering rule not in use yet. + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sNumRule, SwGetPoolIdFromName::NumRule ); + if( USHRT_MAX != nPoolId ) // It's a standard numbering rule + { + rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId ); // Create numbering rule (physical) + } + } + } + } + + pFormat = pColl; + + sal_uInt16 nId = pColl->GetPoolFormatId() & + ~ ( COLL_GET_RANGE_BITS | POOLGRP_NOCOLLID ); + switch( GetMask() & ( static_cast<SfxStyleSearchBits>(0x0fff) & ~SfxStyleSearchBits::SwCondColl ) ) + { + case SfxStyleSearchBits::SwText: + nId |= COLL_TEXT_BITS; + break; + case SfxStyleSearchBits::SwChapter: + nId |= COLL_DOC_BITS; + break; + case SfxStyleSearchBits::SwList: + nId |= COLL_LISTS_BITS; + break; + case SfxStyleSearchBits::SwIndex: + nId |= COLL_REGISTER_BITS; + break; + case SfxStyleSearchBits::SwExtra: + nId |= COLL_EXTRA_BITS; + break; + case SfxStyleSearchBits::SwHtml: + nId |= COLL_HTML_BITS; + break; + default: break; + } + pColl->SetPoolFormatId( nId ); + break; + } + case SfxStyleFamily::Frame: + { + OSL_ENSURE(pFrameFormat, "Where's FrameFormat"); + const SfxPoolItem* pAutoUpdate; + if(SfxItemState::SET == rSet.GetItemState(SID_ATTR_AUTO_STYLE_UPDATE,false, &pAutoUpdate )) + { + pFrameFormat->SetAutoUpdateFormat(static_cast<const SfxBoolItem*>(pAutoUpdate)->GetValue()); + } + pFormat = pFrameFormat; + } + break; + + case SfxStyleFamily::Page : + { + OSL_ENSURE(pDesc, "Where's PageDescriptor"); + + if (rDoc.FindPageDesc(pDesc->GetName(), &nPgDscPos)) + { + pNewDsc.reset( new SwPageDesc( *pDesc ) ); + // #i48949# - no undo actions for the + // copy of the page style + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + rDoc.CopyPageDesc(*pDesc, *pNewDsc); // #i7983# + + pFormat = &pNewDsc->GetMaster(); + } + } + break; + + case SfxStyleFamily::Pseudo: + { + OSL_ENSURE(pNumRule, "Where's NumRule"); + + if (!pNumRule) + break; + + const SfxPoolItem* pItem; + switch( rSet.GetItemState( SID_ATTR_NUMBERING_RULE, false, &pItem )) + { + case SfxItemState::SET: + { + SvxNumRule* pSetRule = static_cast<const SvxNumBulletItem*>(pItem)->GetNumRule(); + pSetRule->UnLinkGraphics(); + SwNumRule aSetRule(*pNumRule); + aSetRule.SetSvxRule(*pSetRule, &rDoc); + rDoc.ChgNumRuleFormats( aSetRule ); + } + break; + case SfxItemState::DONTCARE: + // set NumRule to default values + // what are the default values? + { + SwNumRule aRule( pNumRule->GetName(), + // #i89178# + numfunc::GetDefaultPositionAndSpaceMode() ); + rDoc.ChgNumRuleFormats( aRule ); + } + break; + default: break; + } + } + break; + + default: + OSL_ENSURE(false, "unknown style family"); + } + + if( pFormat && rSet.Count()) + { + SfxItemIter aIter( rSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + do + { + if( IsInvalidItem( pItem ) ) // Clear + { + // use method <SwDoc::ResetAttrAtFormat(..)> in order to + // create an Undo object for the attribute reset. + rDoc.ResetAttrAtFormat( rSet.GetWhichByPos(aIter.GetCurPos()), + *pFormat ); + } + + pItem = aIter.NextItem(); + } while (pItem); + SfxItemSet aSet(rSet); + aSet.ClearInvalidItems(); + + if(SfxStyleFamily::Frame == nFamily) + { + // 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 + rDoc.CheckForUniqueItemForLineFillNameOrIndex(aSet); + } + + aCoreSet.ClearItem(); + + if( pNewDsc ) + { + ::ItemSetToPageDesc( aSet, *pNewDsc ); + rDoc.ChgPageDesc( nPgDscPos, *pNewDsc ); + pDesc = &rDoc.GetPageDesc( nPgDscPos ); + rDoc.PreDelPageDesc(pNewDsc.get()); // #i7983# + pNewDsc.reset(); + } + else + rDoc.ChgFormat(*pFormat, aSet); // put all that is set + } + else + { + aCoreSet.ClearItem(); + if( pNewDsc ) // we still need to delete it + { + rDoc.PreDelPageDesc(pNewDsc.get()); // #i7983# + pNewDsc.reset(); + } + } + + if (rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr); + } +} + +static void lcl_SaveStyles( SfxStyleFamily nFamily, std::vector<void*>& rArr, SwDoc& rDoc ) +{ + switch( nFamily ) + { + case SfxStyleFamily::Char: + { + const SwCharFormats& rTable = *rDoc.GetCharFormats(); + for( size_t n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + rArr.push_back( rTable[ n ] ); + } + } + break; + case SfxStyleFamily::Para: + { + const SwTextFormatColls& rTable = *rDoc.GetTextFormatColls(); + for( size_t n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + rArr.push_back( rTable[ n ] ); + } + } + break; + case SfxStyleFamily::Frame: + { + const SwFrameFormats& rTable = *rDoc.GetFrameFormats(); + for( size_t n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + rArr.push_back( rTable[ n ] ); + } + } + break; + + case SfxStyleFamily::Page: + { + for( size_t n = 0, nCnt = rDoc.GetPageDescCnt(); n < nCnt; ++n ) + { + rArr.push_back( &rDoc.GetPageDesc( n ) ); + } + } + break; + + case SfxStyleFamily::Pseudo: + { + const SwNumRuleTable& rTable = rDoc.GetNumRuleTable(); + for( size_t n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + rArr.push_back( rTable[ n ] ); + } + } + break; + default: break; + } +} + +static bool lcl_Contains(const std::vector<void*>& rArr, const void* p) +{ + return std::find( rArr.begin(), rArr.end(), p ) != rArr.end(); +} + +static void lcl_DeleteInfoStyles( SfxStyleFamily nFamily, std::vector<void*> const & rArr, SwDoc& rDoc ) +{ + size_t n, nCnt; + switch( nFamily ) + { + case SfxStyleFamily::Char: + { + std::deque<sal_uInt16> aDelArr; + const SwCharFormats& rTable = *rDoc.GetCharFormats(); + for( n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + if( !lcl_Contains( rArr, rTable[ n ] )) + aDelArr.push_front( n ); + } + for( n = 0, nCnt = aDelArr.size(); n < nCnt; ++n ) + rDoc.DelCharFormat( aDelArr[ n ] ); + } + break; + + case SfxStyleFamily::Para : + { + std::deque<sal_uInt16> aDelArr; + const SwTextFormatColls& rTable = *rDoc.GetTextFormatColls(); + for( n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + if( !lcl_Contains( rArr, rTable[ n ] )) + aDelArr.push_front( n ); + } + for( n = 0, nCnt = aDelArr.size(); n < nCnt; ++n ) + rDoc.DelTextFormatColl( aDelArr[ n ] ); + } + break; + + case SfxStyleFamily::Frame: + { + std::deque<SwFrameFormat*> aDelArr; + const SwFrameFormats& rTable = *rDoc.GetFrameFormats(); + for( n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + if( !lcl_Contains( rArr, rTable[ n ] )) + aDelArr.push_front( rTable[ n ] ); + } + for( n = 0, nCnt = aDelArr.size(); n < nCnt; ++n ) + rDoc.DelFrameFormat( aDelArr[ n ] ); + } + break; + + case SfxStyleFamily::Page: + { + std::deque<size_t> aDelArr; + for( n = 0, nCnt = rDoc.GetPageDescCnt(); n < nCnt; ++n ) + { + if( !lcl_Contains( rArr, &rDoc.GetPageDesc( n ) )) + aDelArr.push_front( n ); + } + for( n = 0, nCnt = aDelArr.size(); n < nCnt; ++n ) + rDoc.DelPageDesc( aDelArr[ n ] ); + } + break; + + case SfxStyleFamily::Pseudo: + { + std::deque<SwNumRule*> aDelArr; + const SwNumRuleTable& rTable = rDoc.GetNumRuleTable(); + for( n = 0, nCnt = rTable.size(); n < nCnt; ++n ) + { + if( !lcl_Contains( rArr, rTable[ n ] )) + aDelArr.push_front( rTable[ n ] ); + } + for( n = 0, nCnt = aDelArr.size(); n < nCnt; ++n ) + rDoc.DelNumRule( aDelArr[ n ]->GetName() ); + } + break; + default: break; + } +} + +// determine the format +bool SwDocStyleSheet::FillStyleSheet( + FillStyleType const eFType, std::unique_ptr<SfxItemSet> *const o_ppFlatSet) +{ + bool bRet = false; + sal_uInt16 nPoolId = USHRT_MAX; + SwFormat* pFormat = nullptr; + + bool bCreate = FillPhysical == eFType; + bool bDeleteInfo = false; + bool bFillOnlyInfo = FillAllInfo == eFType || FillPreview == eFType; + std::vector<void*> aDelArr; + bool const isModified(rDoc.getIDocumentState().IsModified()); + + switch(nFamily) + { + case SfxStyleFamily::Char: + pCharFormat = lcl_FindCharFormat(rDoc, aName, this, bCreate ); + bPhysical = nullptr != pCharFormat; + if( bFillOnlyInfo && !bPhysical ) + { + // create style (plus all needed parents) and clean it up + // later - without affecting the undo/redo stack + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + bDeleteInfo = true; + ::lcl_SaveStyles( nFamily, aDelArr, rDoc ); + pCharFormat = lcl_FindCharFormat(rDoc, aName, this ); + } + + pFormat = pCharFormat; + if( !bCreate && !pFormat ) + { + if( aName == SwResId(STR_POOLCHR_STANDARD)) + nPoolId = 0; + else + nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::ChrFmt ); + } + + bRet = nullptr != pCharFormat || USHRT_MAX != nPoolId; + + if( bDeleteInfo ) + pCharFormat = nullptr; + break; + + case SfxStyleFamily::Para: + { + pColl = lcl_FindParaFormat(rDoc, aName, this, bCreate); + bPhysical = nullptr != pColl; + if( bFillOnlyInfo && !bPhysical ) + { + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + bDeleteInfo = true; + ::lcl_SaveStyles( nFamily, aDelArr, rDoc ); + pColl = lcl_FindParaFormat(rDoc, aName, this ); + } + + pFormat = pColl; + if( pColl ) + PresetFollow( pColl->GetNextTextFormatColl().GetName() ); + else if( !bCreate ) + nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::TxtColl ); + + bRet = nullptr != pColl || USHRT_MAX != nPoolId; + + if( bDeleteInfo ) + pColl = nullptr; + } + break; + + case SfxStyleFamily::Frame: + pFrameFormat = lcl_FindFrameFormat(rDoc, aName, this, bCreate); + bPhysical = nullptr != pFrameFormat; + if (bFillOnlyInfo && !bPhysical) + { + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + bDeleteInfo = true; + ::lcl_SaveStyles( nFamily, aDelArr, rDoc ); + pFrameFormat = lcl_FindFrameFormat(rDoc, aName, this ); + } + pFormat = pFrameFormat; + if( !bCreate && !pFormat ) + nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::FrmFmt ); + + bRet = nullptr != pFrameFormat || USHRT_MAX != nPoolId; + + if( bDeleteInfo ) + pFrameFormat = nullptr; + break; + + case SfxStyleFamily::Page: + pDesc = lcl_FindPageDesc(rDoc, aName, this, bCreate); + bPhysical = nullptr != pDesc; + if( bFillOnlyInfo && !pDesc ) + { + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + bDeleteInfo = true; + ::lcl_SaveStyles( nFamily, aDelArr, rDoc ); + pDesc = lcl_FindPageDesc( rDoc, aName, this ); + } + + if( pDesc ) + { + nPoolId = pDesc->GetPoolFormatId(); + nHelpId = pDesc->GetPoolHelpId(); + if( pDesc->GetPoolHlpFileId() != UCHAR_MAX ) + aHelpFile = *rDoc.GetDocPattern( pDesc->GetPoolHlpFileId() ); + else + aHelpFile.clear(); + } + else if( !bCreate ) + nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::PageDesc ); + SetMask( (USER_FMT & nPoolId) ? SfxStyleSearchBits::UserDefined : SfxStyleSearchBits::Auto ); + + bRet = nullptr != pDesc || USHRT_MAX != nPoolId; + if( bDeleteInfo ) + pDesc = nullptr; + break; + + case SfxStyleFamily::Pseudo: + pNumRule = lcl_FindNumRule(rDoc, aName, this, bCreate); + bPhysical = nullptr != pNumRule; + if( bFillOnlyInfo && !pNumRule ) + { + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + bDeleteInfo = true; + ::lcl_SaveStyles( nFamily, aDelArr, rDoc ); + pNumRule = lcl_FindNumRule( rDoc, aName, this ); + } + + if( pNumRule ) + { + nPoolId = pNumRule->GetPoolFormatId(); + nHelpId = pNumRule->GetPoolHelpId(); + if( pNumRule->GetPoolHlpFileId() != UCHAR_MAX ) + aHelpFile = *rDoc.GetDocPattern( pNumRule->GetPoolHlpFileId() ); + else + aHelpFile.clear(); + } + else if( !bCreate ) + nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::NumRule ); + SetMask( (USER_FMT & nPoolId) ? SfxStyleSearchBits::UserDefined : SfxStyleSearchBits::Auto ); + + bRet = nullptr != pNumRule || USHRT_MAX != nPoolId; + + if( bDeleteInfo ) + pNumRule = nullptr; + break; + + case SfxStyleFamily::Table: + pTableFormat = lcl_FindTableStyle(rDoc, aName, this, bCreate); + SetMask((pTableFormat && pTableFormat->IsUserDefined()) ? SfxStyleSearchBits::UserDefined : SfxStyleSearchBits::Auto); + bRet = bPhysical = (nullptr != pTableFormat); + break; + + case SfxStyleFamily::Cell: + pBoxFormat = lcl_FindCellStyle(rDoc, aName, this); + bRet = bPhysical = (nullptr != pBoxFormat); + break; + default:; //prevent warning + } + + if( SfxStyleFamily::Char == nFamily || + SfxStyleFamily::Para == nFamily || + SfxStyleFamily::Frame == nFamily ) + { + if( pFormat ) + nPoolId = pFormat->GetPoolFormatId(); + + SfxStyleSearchBits _nMask = SfxStyleSearchBits::Auto; + if( pFormat == rDoc.GetDfltCharFormat() ) + _nMask |= SfxStyleSearchBits::ReadOnly; + else if( USER_FMT & nPoolId ) + _nMask |= SfxStyleSearchBits::UserDefined; + + switch ( COLL_GET_RANGE_BITS & nPoolId ) + { + case COLL_TEXT_BITS: _nMask |= SfxStyleSearchBits::SwText; break; + case COLL_DOC_BITS : _nMask |= SfxStyleSearchBits::SwChapter; break; + case COLL_LISTS_BITS: _nMask |= SfxStyleSearchBits::SwList; break; + case COLL_REGISTER_BITS: _nMask |= SfxStyleSearchBits::SwIndex; break; + case COLL_EXTRA_BITS: _nMask |= SfxStyleSearchBits::SwExtra; break; + case COLL_HTML_BITS: _nMask |= SfxStyleSearchBits::SwHtml; break; + } + + if( pFormat ) + { + OSL_ENSURE( bPhysical, "Format not found" ); + + nHelpId = pFormat->GetPoolHelpId(); + if( pFormat->GetPoolHlpFileId() != UCHAR_MAX ) + aHelpFile = *rDoc.GetDocPattern( pFormat->GetPoolHlpFileId() ); + else + aHelpFile.clear(); + + if( RES_CONDTXTFMTCOLL == pFormat->Which() ) + _nMask |= SfxStyleSearchBits::SwCondColl; + + if (FillPreview == eFType) + { + assert(o_ppFlatSet); + *o_ppFlatSet = lcl_SwFormatToFlatItemSet(pFormat); + } + } + + SetMask( _nMask ); + } + if( bDeleteInfo && bFillOnlyInfo ) + { + ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); + ::lcl_DeleteInfoStyles( nFamily, aDelArr, rDoc ); + if (!isModified) + { + rDoc.getIDocumentState().ResetModified(); + } + } + return bRet; +} + +// Create new format in Core +void SwDocStyleSheet::Create() +{ + switch(nFamily) + { + case SfxStyleFamily::Char : + pCharFormat = lcl_FindCharFormat( rDoc, aName ); + if( !pCharFormat ) + pCharFormat = rDoc.MakeCharFormat(aName, + rDoc.GetDfltCharFormat()); + pCharFormat->SetAuto(false); + break; + + case SfxStyleFamily::Para : + pColl = lcl_FindParaFormat( rDoc, aName ); + if( !pColl ) + { + SwTextFormatColl *pPar = (*rDoc.GetTextFormatColls())[0]; + if( nMask & SfxStyleSearchBits::SwCondColl ) + pColl = rDoc.MakeCondTextFormatColl( aName, pPar ); + else + pColl = rDoc.MakeTextFormatColl( aName, pPar ); + } + break; + + case SfxStyleFamily::Frame: + pFrameFormat = lcl_FindFrameFormat( rDoc, aName ); + if( !pFrameFormat ) + pFrameFormat = rDoc.MakeFrameFormat(aName, rDoc.GetDfltFrameFormat(), false, false); + + break; + + case SfxStyleFamily::Page : + pDesc = lcl_FindPageDesc( rDoc, aName ); + if( !pDesc ) + { + pDesc = rDoc.MakePageDesc(aName); + } + break; + + case SfxStyleFamily::Pseudo: + pNumRule = lcl_FindNumRule( rDoc, aName ); + if( !pNumRule ) + { + const OUString sTmpNm( aName.isEmpty() ? rDoc.GetUniqueNumRuleName() : aName ); + SwNumRule* pRule = rDoc.GetNumRuleTable()[ + rDoc.MakeNumRule( sTmpNm, nullptr, false, + // #i89178# + numfunc::GetDefaultPositionAndSpaceMode() ) ]; + pRule->SetAutoRule( false ); + if( aName.isEmpty() ) + { + // #i91400# + pRule->SetName( aName, rDoc.getIDocumentListsAccess() ); + } + pNumRule = pRule; + } + break; + + case SfxStyleFamily::Table: + if (aName.isEmpty()) + return; + pTableFormat = lcl_FindTableStyle(rDoc, aName); + if (!pTableFormat) + { + rDoc.MakeTableStyle(aName); + pTableFormat = rDoc.GetTableStyles().FindAutoFormat(aName); + SAL_WARN_IF(!pTableFormat, "sw.ui", "Recently added auto format not found"); + } + break; + default:; //prevent warning + } + bPhysical = true; + aCoreSet.ClearItem(); +} + +SwCharFormat* SwDocStyleSheet::GetCharFormat() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + return pCharFormat; +} + +SwTextFormatColl* SwDocStyleSheet::GetCollection() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + return pColl; +} + +const SwPageDesc* SwDocStyleSheet::GetPageDesc() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + return pDesc; +} + +const SwNumRule * SwDocStyleSheet::GetNumRule() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + return pNumRule; +} + + +void SwDocStyleSheet::SetNumRule(const SwNumRule& rRule) +{ + OSL_ENSURE(pNumRule, "Where is the NumRule"); + rDoc.ChgNumRuleFormats( rRule ); +} + +SwTableAutoFormat* SwDocStyleSheet::GetTableFormat() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + assert(pTableFormat && "SwDocStyleSheet table style, SwTableAutoFormat not found"); + return pTableFormat; +} + +// re-generate Name AND Family from String +// First() and Next() (see below) insert an identification letter at Pos.1 + +void SwDocStyleSheet::PresetNameAndFamily(const OUString& rName) +{ + switch( rName[0] ) + { + case cPARA: nFamily = SfxStyleFamily::Para; break; + case cFRAME: nFamily = SfxStyleFamily::Frame; break; + case cPAGE: nFamily = SfxStyleFamily::Page; break; + case cNUMRULE: nFamily = SfxStyleFamily::Pseudo; break; + case cTABSTYLE: nFamily = SfxStyleFamily::Table; break; + default: nFamily = SfxStyleFamily::Char; break; + } + aName = rName.copy(1); +} + +// Is the format physically present yet +void SwDocStyleSheet::SetPhysical(bool bPhys) +{ + bPhysical = bPhys; + + if(!bPhys) + { + pCharFormat = nullptr; + pColl = nullptr; + pFrameFormat = nullptr; + pDesc = nullptr; + } +} + +SwFrameFormat* SwDocStyleSheet::GetFrameFormat() +{ + if(!bPhysical) + FillStyleSheet( FillPhysical ); + return pFrameFormat; +} + +bool SwDocStyleSheet::IsUsed() const +{ + if( !bPhysical ) + { + SwDocStyleSheet* pThis = const_cast<SwDocStyleSheet*>(this); + pThis->FillStyleSheet( FillOnlyName ); + } + + if( !bPhysical ) + return false; + + const SwModify* pMod; + switch( nFamily ) + { + case SfxStyleFamily::Char : pMod = pCharFormat; break; + case SfxStyleFamily::Para : pMod = pColl; break; + case SfxStyleFamily::Frame: pMod = pFrameFormat; break; + case SfxStyleFamily::Page : pMod = pDesc; break; + + case SfxStyleFamily::Pseudo: + return pNumRule && SwDoc::IsUsed( *pNumRule ); + + case SfxStyleFamily::Table: + return pTableFormat && rDoc.IsUsed( *pTableFormat ); + + default: + OSL_ENSURE(false, "unknown style family"); + return false; + } + return rDoc.IsUsed( *pMod ); +} + +OUString SwDocStyleSheet::GetUsedBy() +{ + return pNumRule ? pNumRule->MakeParagraphStyleListString() : OUString(); +} + +sal_uLong SwDocStyleSheet::GetHelpId( OUString& rFile ) +{ + sal_uInt16 nId = 0; + sal_uInt16 nPoolId = 0; + unsigned char nFileId = UCHAR_MAX; + + rFile = "swrhlppi.hlp"; + + const SwFormat* pTmpFormat = nullptr; + switch( nFamily ) + { + case SfxStyleFamily::Char : + if( !pCharFormat && + nullptr == (pCharFormat = lcl_FindCharFormat( rDoc, aName, nullptr, false )) ) + { + nId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::ChrFmt ); + return USHRT_MAX == nId ? 0 : nId; + } + pTmpFormat = pCharFormat; + break; + + case SfxStyleFamily::Para: + if( !pColl && + nullptr == ( pColl = lcl_FindParaFormat( rDoc, aName, nullptr, false )) ) + { + nId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::TxtColl ); + return USHRT_MAX == nId ? 0 : nId; + } + pTmpFormat = pColl; + break; + + case SfxStyleFamily::Frame: + if( !pFrameFormat && + nullptr == ( pFrameFormat = lcl_FindFrameFormat( rDoc, aName, nullptr, false ) ) ) + { + nId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::FrmFmt ); + return USHRT_MAX == nId ? 0 : nId; + } + pTmpFormat = pFrameFormat; + break; + + case SfxStyleFamily::Page: + if( !pDesc && + nullptr == ( pDesc = lcl_FindPageDesc( rDoc, aName, nullptr, false ) ) ) + { + nId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::PageDesc ); + return USHRT_MAX == nId ? 0 : nId; + } + + nId = pDesc->GetPoolHelpId(); + nFileId = pDesc->GetPoolHlpFileId(); + nPoolId = pDesc->GetPoolFormatId(); + break; + + case SfxStyleFamily::Pseudo: + if( !pNumRule && + nullptr == ( pNumRule = lcl_FindNumRule( rDoc, aName, nullptr, false ) ) ) + { + nId = SwStyleNameMapper::GetPoolIdFromUIName( aName, SwGetPoolIdFromName::NumRule ); + return USHRT_MAX == nId ? 0 : nId; + } + + nId = pNumRule->GetPoolHelpId(); + nFileId = pNumRule->GetPoolHlpFileId(); + nPoolId = pNumRule->GetPoolFormatId(); + break; + + default: + OSL_ENSURE(false, "unknown style family"); + return 0; + } + + if( pTmpFormat ) + { + nId = pTmpFormat->GetPoolHelpId(); + nFileId = pTmpFormat->GetPoolHlpFileId(); + nPoolId = pTmpFormat->GetPoolFormatId(); + } + + if( UCHAR_MAX != nFileId ) + { + const OUString *pTemplate = rDoc.GetDocPattern( nFileId ); + if( pTemplate ) + { + rFile = *pTemplate; + } + } + else if( !IsPoolUserFormat( nPoolId ) ) + { + nId = nPoolId; + } + + // because SFX acts like that, with HelpId: + if( USHRT_MAX == nId ) + nId = 0; // don't show Help accordingly + + return nId; +} + +void SwDocStyleSheet::SetHelpId( const OUString& r, sal_uLong nId ) +{ + sal_uInt8 nFileId = static_cast< sal_uInt8 >(rDoc.SetDocPattern( r )); + sal_uInt16 nHId = static_cast< sal_uInt16 >(nId); //!! SFX changed over to ULONG arbitrarily! + + SwFormat* pTmpFormat = nullptr; + switch( nFamily ) + { + case SfxStyleFamily::Char : pTmpFormat = pCharFormat; break; + case SfxStyleFamily::Para : pTmpFormat = pColl; break; + case SfxStyleFamily::Frame: pTmpFormat = pFrameFormat; break; + case SfxStyleFamily::Page : + const_cast<SwPageDesc*>(pDesc)->SetPoolHelpId( nHId ); + const_cast<SwPageDesc*>(pDesc)->SetPoolHlpFileId( nFileId ); + break; + + case SfxStyleFamily::Pseudo: + const_cast<SwNumRule*>(pNumRule)->SetPoolHelpId( nHId ); + const_cast<SwNumRule*>(pNumRule)->SetPoolHlpFileId( nFileId ); + break; + + default: + OSL_ENSURE(false, "unknown style family"); + return ; + } + if( pTmpFormat ) + { + pTmpFormat->SetPoolHelpId( nHId ); + pTmpFormat->SetPoolHlpFileId( nFileId ); + } +} + +// methods for DocStyleSheetPool +SwDocStyleSheetPool::SwDocStyleSheetPool( SwDoc& rDocument, bool bOrg ) + : SfxStyleSheetBasePool(rDocument.GetAttrPool()) + , mxStyleSheet(new SwDocStyleSheet(rDocument, *this)) + , rDoc(rDocument) +{ + bOrganizer = bOrg; +} + +SwDocStyleSheetPool::~SwDocStyleSheetPool() +{ +} + +SfxStyleSheetBase& SwDocStyleSheetPool::Make( const OUString& rName, + SfxStyleFamily eFam, + SfxStyleSearchBits _nMask) +{ + mxStyleSheet->PresetName(rName); + mxStyleSheet->PresetParent(OUString()); + mxStyleSheet->PresetFollow(OUString()); + mxStyleSheet->SetMask(_nMask) ; + mxStyleSheet->SetFamily(eFam); + mxStyleSheet->SetPhysical(true); + mxStyleSheet->Create(); + + return *mxStyleSheet; +} + +SfxStyleSheetBase* SwDocStyleSheetPool::Create( const SfxStyleSheetBase& /*rOrg*/) +{ + OSL_ENSURE(false , "Create in SW-Stylesheet-Pool not possible" ); + return nullptr; +} + +SfxStyleSheetBase* SwDocStyleSheetPool::Create( const OUString &, + SfxStyleFamily, SfxStyleSearchBits ) +{ + OSL_ENSURE( false, "Create in SW-Stylesheet-Pool not possible" ); + return nullptr; +} + +std::unique_ptr<SfxStyleSheetIterator> SwDocStyleSheetPool::CreateIterator( SfxStyleFamily eFam, SfxStyleSearchBits _nMask ) +{ + return std::make_unique<SwStyleSheetIterator>(*this, eFam, _nMask); +} + +void SwDocStyleSheetPool::dispose() +{ + mxStyleSheet.clear(); +} + +void SwDocStyleSheetPool::Remove( SfxStyleSheetBase* pStyle) +{ + if( !pStyle ) + return; + + bool bBroadcast = true; + SwImplShellAction aTmpSh( rDoc ); + const OUString sName = pStyle->GetName(); + switch( pStyle->GetFamily() ) + { + case SfxStyleFamily::Char: + { + SwCharFormat* pFormat = lcl_FindCharFormat(rDoc, sName, nullptr, false ); + if(pFormat) + rDoc.DelCharFormat(pFormat); + } + break; + case SfxStyleFamily::Para: + { + SwTextFormatColl* pColl = lcl_FindParaFormat(rDoc, sName, nullptr, false ); + if(pColl) + rDoc.DelTextFormatColl(pColl); + } + break; + case SfxStyleFamily::Frame: + { + SwFrameFormat* pFormat = lcl_FindFrameFormat(rDoc, sName, nullptr, false ); + if(pFormat) + rDoc.DelFrameFormat(pFormat); + } + break; + case SfxStyleFamily::Page : + { + rDoc.DelPageDesc(sName); + } + break; + + case SfxStyleFamily::Pseudo: + { + if( !rDoc.DelNumRule( sName ) ) + // Only send Broadcast, when something was deleted + bBroadcast = false; + } + break; + + case SfxStyleFamily::Table: + { + rDoc.DelTableStyle(sName); + } + break; + + default: + OSL_ENSURE(false, "unknown style family"); + bBroadcast = false; + } + + if( bBroadcast ) + Broadcast( SfxStyleSheetHint( SfxHintId::StyleSheetErased, *pStyle ) ); +} + +bool SwDocStyleSheetPool::SetParent( SfxStyleFamily eFam, + const OUString &rStyle, const OUString &rParent ) +{ + SwFormat* pFormat = nullptr, *pParent = nullptr; + switch( eFam ) + { + case SfxStyleFamily::Char : + if( nullptr != ( pFormat = lcl_FindCharFormat( rDoc, rStyle ) ) && !rParent.isEmpty() ) + pParent = lcl_FindCharFormat(rDoc, rParent ); + break; + + case SfxStyleFamily::Para : + if( nullptr != ( pFormat = lcl_FindParaFormat( rDoc, rStyle ) ) && !rParent.isEmpty() ) + pParent = lcl_FindParaFormat( rDoc, rParent ); + break; + + case SfxStyleFamily::Frame: + if( nullptr != ( pFormat = lcl_FindFrameFormat( rDoc, rStyle ) ) && !rParent.isEmpty() ) + pParent = lcl_FindFrameFormat( rDoc, rParent ); + break; + + case SfxStyleFamily::Page: + case SfxStyleFamily::Pseudo: + break; + + default: + OSL_ENSURE(false, "unknown style family"); + } + + bool bRet = false; + if( pFormat && pFormat->DerivedFrom() && + pFormat->DerivedFrom()->GetName() != rParent ) + { + { + SwImplShellAction aTmpSh( rDoc ); + bRet = pFormat->SetDerivedFrom( pParent ); + } + + if( bRet ) + { + // only for Broadcasting + mxStyleSheet->PresetName( rStyle ); + mxStyleSheet->PresetParent( rParent ); + if( SfxStyleFamily::Para == eFam ) + mxStyleSheet->PresetFollow( static_cast<SwTextFormatColl*>(pFormat)-> + GetNextTextFormatColl().GetName() ); + else + mxStyleSheet->PresetFollow( OUString() ); + + Broadcast( SfxStyleSheetHint( SfxHintId::StyleSheetModified, *mxStyleSheet ) ); + } + } + + return bRet; +} + +SfxStyleSheetBase* SwDocStyleSheetPool::Find( const OUString& rName, + SfxStyleFamily eFam, SfxStyleSearchBits n ) +{ + SfxStyleSearchBits nSMask = n; + if( SfxStyleFamily::Para == eFam && rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ) + { + // then only HTML-Templates are of interest + if( SfxStyleSearchBits::All == nSMask ) + nSMask = SfxStyleSearchBits::SwHtml | SfxStyleSearchBits::UserDefined | SfxStyleSearchBits::Used; + else + nSMask &= SfxStyleSearchBits::Used | SfxStyleSearchBits::UserDefined | + SfxStyleSearchBits::SwCondColl | SfxStyleSearchBits::SwHtml; + if( nSMask == SfxStyleSearchBits::Auto ) + nSMask = SfxStyleSearchBits::SwHtml; + } + + const bool bSearchUsed = ( n != SfxStyleSearchBits::All && n & SfxStyleSearchBits::Used ); + const SwModify* pMod = nullptr; + + mxStyleSheet->SetPhysical( false ); + mxStyleSheet->PresetName( rName ); + mxStyleSheet->SetFamily( eFam ); + bool bFnd = mxStyleSheet->FillStyleSheet( SwDocStyleSheet::FillOnlyName ); + + if( mxStyleSheet->IsPhysical() ) + { + switch( eFam ) + { + case SfxStyleFamily::Char: + pMod = mxStyleSheet->GetCharFormat(); + break; + + case SfxStyleFamily::Para: + pMod = mxStyleSheet->GetCollection(); + break; + + case SfxStyleFamily::Frame: + pMod = mxStyleSheet->GetFrameFormat(); + break; + + case SfxStyleFamily::Page: + pMod = mxStyleSheet->GetPageDesc(); + break; + + case SfxStyleFamily::Pseudo: + { + const SwNumRule* pRule = mxStyleSheet->GetNumRule(); + if( pRule && + !bSearchUsed && + (( nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(pRule->GetPoolFormatId() & USER_FMT) + // searched for used and found none + : bSearchUsed )) + bFnd = false; + } + break; + + case SfxStyleFamily::Table: + case SfxStyleFamily::Cell: + break; + default: + OSL_ENSURE(false, "unknown style family"); + } + } + + // then evaluate the mask: + if( pMod && !bSearchUsed ) + { + const sal_uInt16 nId = SfxStyleFamily::Page == eFam + ? static_cast<const SwPageDesc*>(pMod)->GetPoolFormatId() + : static_cast<const SwFormat*>(pMod)->GetPoolFormatId(); + + if( ( nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(nId & USER_FMT) + // searched for used and found none + : bSearchUsed ) + bFnd = false; + } + return bFnd ? mxStyleSheet.get() : nullptr; +} + +SwStyleSheetIterator::SwStyleSheetIterator(SwDocStyleSheetPool& rBase, + SfxStyleFamily eFam, SfxStyleSearchBits n ) + : SfxStyleSheetIterator(&rBase, eFam, n) + , mxIterSheet(new SwDocStyleSheet(rBase.GetDoc(), rBase)) + , mxStyleSheet(new SwDocStyleSheet(rBase.GetDoc(), rBase)) +{ + bFirstCalled = false; + nLastPos = 0; + StartListening(rBase); +} + +SwStyleSheetIterator::~SwStyleSheetIterator() +{ + EndListening( *mxIterSheet->GetPool() ); +} + +sal_uInt16 SwStyleSheetIterator::Count() +{ + // let the list fill correctly!! + if( !bFirstCalled ) + First(); + return aLst.size(); +} + +SfxStyleSheetBase* SwStyleSheetIterator::operator[]( sal_uInt16 nIdx ) +{ + // found + if( !bFirstCalled ) + First(); + mxStyleSheet->PresetNameAndFamily( aLst[ nIdx ] ); + mxStyleSheet->SetPhysical( false ); + mxStyleSheet->FillStyleSheet( SwDocStyleSheet::FillOnlyName ); + + return mxStyleSheet.get(); +} + +SfxStyleSheetBase* SwStyleSheetIterator::First() +{ + // Delete old list + bFirstCalled = true; + nLastPos = 0; + aLst.clear(); + + // Delete current + mxIterSheet->Reset(); + + SwDoc& rDoc = static_cast<SwDocStyleSheetPool*>(pBasePool)->GetDoc(); + const SfxStyleSearchBits nSrchMask = nMask; + const bool bIsSearchUsed = SearchUsed(); + + bool bSearchHidden( nMask & SfxStyleSearchBits::Hidden ); + bool bOnlyHidden = nMask == SfxStyleSearchBits::Hidden; + + const bool bOrganizer = static_cast<SwDocStyleSheetPool*>(pBasePool)->IsOrganizerMode(); + bool bAll = ( nSrchMask & SfxStyleSearchBits::AllVisible ) == SfxStyleSearchBits::AllVisible; + + if( nSearchFamily == SfxStyleFamily::Char + || nSearchFamily == SfxStyleFamily::All ) + { + const size_t nArrLen = rDoc.GetCharFormats()->size(); + for( size_t i = 0; i < nArrLen; i++ ) + { + SwCharFormat* pFormat = (*rDoc.GetCharFormats())[ i ]; + + const bool bUsed = bIsSearchUsed && (bOrganizer || rDoc.IsUsed(*pFormat)); + if( ( !bSearchHidden && pFormat->IsHidden() && !bUsed ) || ( pFormat->IsDefault() && pFormat != rDoc.GetDfltCharFormat() ) ) + continue; + + if ( nSrchMask == SfxStyleSearchBits::Hidden && !pFormat->IsHidden( ) ) + continue; + + if( !bUsed ) + { + // Standard is no User template + const sal_uInt16 nId = rDoc.GetDfltCharFormat() == pFormat ? + sal_uInt16( RES_POOLCHR_INET_NORMAL ): + pFormat->GetPoolFormatId(); + if( (nSrchMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(nId & USER_FMT) + // searched for used and found none + : bIsSearchUsed ) + { + continue; + } + + if( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) && !(nId & USER_FMT) && + !( RES_POOLCHR_HTML_BEGIN <= nId && + nId < RES_POOLCHR_HTML_END ) && + RES_POOLCHR_INET_NORMAL != nId && + RES_POOLCHR_INET_VISIT != nId && + RES_POOLCHR_FOOTNOTE != nId && + RES_POOLCHR_ENDNOTE != nId ) + continue; + } + + aLst.Append( cCHAR, pFormat == rDoc.GetDfltCharFormat() + ? SwResId(STR_POOLCHR_STANDARD) + : pFormat->GetName() ); + } + + // PoolFormat + if( bAll ) + { + if( ! rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ) + AppendStyleList(SwStyleNameMapper::GetChrFormatUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, + SwGetPoolIdFromName::ChrFmt, cCHAR); + else + { + aLst.Append( cCHAR, SwStyleNameMapper::GetChrFormatUINameArray()[ + RES_POOLCHR_INET_NORMAL - RES_POOLCHR_BEGIN ] ); + aLst.Append( cCHAR, SwStyleNameMapper::GetChrFormatUINameArray()[ + RES_POOLCHR_INET_VISIT - RES_POOLCHR_BEGIN ] ); + aLst.Append( cCHAR, SwStyleNameMapper::GetChrFormatUINameArray()[ + RES_POOLCHR_ENDNOTE - RES_POOLCHR_BEGIN ] ); + aLst.Append( cCHAR, SwStyleNameMapper::GetChrFormatUINameArray()[ + RES_POOLCHR_FOOTNOTE - RES_POOLCHR_BEGIN ] ); + } + AppendStyleList(SwStyleNameMapper::GetHTMLChrFormatUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, + SwGetPoolIdFromName::ChrFmt, cCHAR); + } + } + + if( nSearchFamily == SfxStyleFamily::Para || + nSearchFamily == SfxStyleFamily::All ) + { + SfxStyleSearchBits nSMask = nSrchMask; + if( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ) + { + // then only HTML-Template are of interest + if( SfxStyleSearchBits::AllVisible == ( nSMask & SfxStyleSearchBits::AllVisible ) ) + nSMask = SfxStyleSearchBits::SwHtml | SfxStyleSearchBits::UserDefined | + SfxStyleSearchBits::Used; + else + nSMask &= SfxStyleSearchBits::Used | SfxStyleSearchBits::UserDefined | + SfxStyleSearchBits::SwCondColl | SfxStyleSearchBits::SwHtml; + if( nSMask == SfxStyleSearchBits::Auto ) + nSMask = SfxStyleSearchBits::SwHtml; + } + + const size_t nArrLen = rDoc.GetTextFormatColls()->size(); + for( size_t i = 0; i < nArrLen; i++ ) + { + SwTextFormatColl* pColl = (*rDoc.GetTextFormatColls())[ i ]; + + const bool bUsed = bOrganizer || rDoc.IsUsed(*pColl); + if ( ( !bSearchHidden && pColl->IsHidden( ) && !bUsed ) || pColl->IsDefault() ) + continue; + + if ( nSMask == SfxStyleSearchBits::Hidden && !pColl->IsHidden( ) ) + continue; + + if( !(bIsSearchUsed && bUsed )) + { + const sal_uInt16 nId = pColl->GetPoolFormatId(); + auto tmpMask = nSMask & ~SfxStyleSearchBits::Used; + if (tmpMask == SfxStyleSearchBits::UserDefined) + { + if(!IsPoolUserFormat(nId)) continue; + } + else if (tmpMask == SfxStyleSearchBits::SwText) + { + if((nId & COLL_GET_RANGE_BITS) != COLL_TEXT_BITS) continue; + } + else if (tmpMask == SfxStyleSearchBits::SwChapter) + { + if((nId & COLL_GET_RANGE_BITS) != COLL_DOC_BITS) continue; + } + else if (tmpMask == SfxStyleSearchBits::SwList) + { + if((nId & COLL_GET_RANGE_BITS) != COLL_LISTS_BITS) continue; + } + else if (tmpMask == SfxStyleSearchBits::SwIndex) + { + if((nId & COLL_GET_RANGE_BITS) != COLL_REGISTER_BITS) continue; + } + else if (tmpMask == SfxStyleSearchBits::SwExtra) + { + if((nId & COLL_GET_RANGE_BITS) != COLL_EXTRA_BITS) continue; + } + else if (tmpMask == (SfxStyleSearchBits::SwHtml | SfxStyleSearchBits::UserDefined) + || tmpMask == SfxStyleSearchBits::SwHtml) + { + if((tmpMask & SfxStyleSearchBits::UserDefined) && IsPoolUserFormat(nId)) + ; // do nothing + else if( (nId & COLL_GET_RANGE_BITS) != COLL_HTML_BITS) + { + // but some we also want to see in this section + bool bContinue = true; + switch( nId ) + { + case RES_POOLCOLL_SENDADRESS: // --> ADDRESS + case RES_POOLCOLL_TABLE_HDLN: // --> TH + case RES_POOLCOLL_TABLE: // --> TD + case RES_POOLCOLL_TEXT: // --> P + case RES_POOLCOLL_HEADLINE_BASE:// --> H + case RES_POOLCOLL_HEADLINE1: // --> H1 + case RES_POOLCOLL_HEADLINE2: // --> H2 + case RES_POOLCOLL_HEADLINE3: // --> H3 + case RES_POOLCOLL_HEADLINE4: // --> H4 + case RES_POOLCOLL_HEADLINE5: // --> H5 + case RES_POOLCOLL_HEADLINE6: // --> H6 + case RES_POOLCOLL_STANDARD: // --> P + case RES_POOLCOLL_FOOTNOTE: + case RES_POOLCOLL_ENDNOTE: + bContinue = false; + break; + } + if( bContinue ) + continue; + } + } + else if (tmpMask == SfxStyleSearchBits::SwCondColl) + { + if( RES_CONDTXTFMTCOLL != pColl->Which() ) continue; + } + else + { + // searched for used and found none + if( bIsSearchUsed ) + continue; + } + } + aLst.Append( cPARA, pColl->GetName() ); + } + + bAll = ( nSMask & SfxStyleSearchBits::AllVisible ) == SfxStyleSearchBits::AllVisible; + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwText ) + AppendStyleList(SwStyleNameMapper::GetTextUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ); + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwChapter ) + AppendStyleList(SwStyleNameMapper::GetDocUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ) ; + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwList ) + AppendStyleList(SwStyleNameMapper::GetListsUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ) ; + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwIndex ) + AppendStyleList(SwStyleNameMapper::GetRegisterUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ) ; + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwExtra ) + AppendStyleList(SwStyleNameMapper::GetExtraUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ) ; + if ( bAll || (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwCondColl ) + { + if( !bIsSearchUsed || + rDoc.getIDocumentStylePoolAccess().IsPoolTextCollUsed( RES_POOLCOLL_TEXT )) + aLst.Append( cPARA, SwStyleNameMapper::GetTextUINameArray()[ + RES_POOLCOLL_TEXT - RES_POOLCOLL_TEXT_BEGIN ] ); + } + if ( bAll || + (nSMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::SwHtml || + (nSMask & ~SfxStyleSearchBits::Used) == + (SfxStyleSearchBits::SwHtml | SfxStyleSearchBits::UserDefined) ) + { + AppendStyleList(SwStyleNameMapper::GetHTMLUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::TxtColl, cPARA ) ; + if( !bAll ) + { + // then also the ones, that we are mapping: + static sal_uInt16 aPoolIds[] = { + RES_POOLCOLL_SENDADRESS, // --> ADDRESS + RES_POOLCOLL_TABLE_HDLN, // --> TH + RES_POOLCOLL_TABLE, // --> TD + RES_POOLCOLL_STANDARD, // --> P + RES_POOLCOLL_TEXT, // --> P + RES_POOLCOLL_HEADLINE_BASE, // --> H + RES_POOLCOLL_HEADLINE1, // --> H1 + RES_POOLCOLL_HEADLINE2, // --> H2 + RES_POOLCOLL_HEADLINE3, // --> H3 + RES_POOLCOLL_HEADLINE4, // --> H4 + RES_POOLCOLL_HEADLINE5, // --> H5 + RES_POOLCOLL_HEADLINE6, // --> H6 + RES_POOLCOLL_FOOTNOTE, + RES_POOLCOLL_ENDNOTE, + 0 + }; + + sal_uInt16* pPoolIds = aPoolIds; + OUString s; + while( *pPoolIds ) + { + if( !bIsSearchUsed || rDoc.getIDocumentStylePoolAccess().IsPoolTextCollUsed( *pPoolIds ) ) + { + s = SwStyleNameMapper::GetUIName( *pPoolIds, s ); + aLst.Append( cPARA, s); + } + ++pPoolIds; + } + } + } + } + + if( nSearchFamily == SfxStyleFamily::Frame || + nSearchFamily == SfxStyleFamily::All ) + { + const size_t nArrLen = rDoc.GetFrameFormats()->size(); + for( size_t i = 0; i < nArrLen; i++ ) + { + const SwFrameFormat* pFormat = (*rDoc.GetFrameFormats())[ i ]; + + bool bUsed = bIsSearchUsed && ( bOrganizer || rDoc.IsUsed(*pFormat)); + if( ( !bSearchHidden && pFormat->IsHidden( ) && !bUsed ) || pFormat->IsDefault() || pFormat->IsAuto() ) + continue; + + if ( nSrchMask == SfxStyleSearchBits::Hidden && !pFormat->IsHidden( ) ) + continue; + + const sal_uInt16 nId = pFormat->GetPoolFormatId(); + if( !bUsed ) + { + if( (nSrchMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(nId & USER_FMT) + // searched for used and found none + : bIsSearchUsed ) + { + continue; + } + } + + aLst.Append( cFRAME, pFormat->GetName() ); + } + + // PoolFormat + if ( bAll ) + AppendStyleList(SwStyleNameMapper::GetFrameFormatUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::FrmFmt, cFRAME); + } + + if( nSearchFamily == SfxStyleFamily::Page || + nSearchFamily == SfxStyleFamily::All ) + { + const size_t nCount = rDoc.GetPageDescCnt(); + for(size_t i = 0; i < nCount; ++i) + { + const SwPageDesc& rDesc = rDoc.GetPageDesc(i); + const sal_uInt16 nId = rDesc.GetPoolFormatId(); + bool bUsed = bIsSearchUsed && ( bOrganizer || rDoc.IsUsed(rDesc)); + if( !bUsed ) + { + if ( ( !bSearchHidden && rDesc.IsHidden() ) || + ( (nSrchMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(nId & USER_FMT) + // searched for used and found none + : bIsSearchUsed ) ) + continue; + } + + if ( nSrchMask == SfxStyleSearchBits::Hidden && !rDesc.IsHidden( ) ) + continue; + + aLst.Append( cPAGE, rDesc.GetName() ); + } + if ( bAll ) + AppendStyleList(SwStyleNameMapper::GetPageDescUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::PageDesc, cPAGE); + } + + if( nSearchFamily == SfxStyleFamily::Pseudo || + nSearchFamily == SfxStyleFamily::All ) + { + const SwNumRuleTable& rNumTable = rDoc.GetNumRuleTable(); + for(size_t i = 0; i < rNumTable.size(); ++i) + { + const SwNumRule& rRule = *rNumTable[ i ]; + if( !rRule.IsAutoRule() ) + { + if ( nSrchMask == SfxStyleSearchBits::Hidden && !rRule.IsHidden( ) ) + continue; + + bool bUsed = bIsSearchUsed && ( bOrganizer || SwDoc::IsUsed(rRule) ); + if( !bUsed ) + { + if( ( !bSearchHidden && rRule.IsHidden() ) || + ( (nSrchMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !(rRule.GetPoolFormatId() & USER_FMT) + // searched for used and found none + : bIsSearchUsed ) ) + continue; + } + + aLst.Append( cNUMRULE, rRule.GetName() ); + } + } + if ( bAll ) + AppendStyleList(SwStyleNameMapper::GetNumRuleUINameArray(), + bIsSearchUsed, bSearchHidden, bOnlyHidden, SwGetPoolIdFromName::NumRule, cNUMRULE); + } + + if( nSearchFamily == SfxStyleFamily::Table || + nSearchFamily == SfxStyleFamily::All ) + { + const SwTableAutoFormatTable& rTableStyles = rDoc.GetTableStyles(); + for(size_t i = 0; i < rTableStyles.size(); ++i) + { + const SwTableAutoFormat& rTableStyle = rTableStyles[i]; + + bool bUsed = bIsSearchUsed && (bOrganizer || rDoc.IsUsed(rTableStyle)); + if(!bUsed) + { + if(nSrchMask == SfxStyleSearchBits::Hidden && !rTableStyle.IsHidden()) + continue; + + if( (!bSearchHidden && rTableStyle.IsHidden() ) || + ( (nSrchMask & ~SfxStyleSearchBits::Used) == SfxStyleSearchBits::UserDefined + ? !rTableStyle.IsUserDefined() + // searched for used and found none + : bIsSearchUsed ) ) + continue; + } + + aLst.Append( cTABSTYLE, rTableStyle.GetName() ); + } + } + + if( nSearchFamily == SfxStyleFamily::Cell || + nSearchFamily == SfxStyleFamily::All ) + { + const auto& aTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap(); + if (rDoc.HasTableStyles()) + { + const SwTableAutoFormatTable& rTableStyles = rDoc.GetTableStyles(); + for(size_t i = 0; i < rTableStyles.size(); ++i) + { + const SwTableAutoFormat& rTableStyle = rTableStyles[i]; + for(size_t nBoxFormat = 0; nBoxFormat < aTableTemplateMap.size(); ++nBoxFormat) + { + const sal_uInt32 nBoxIndex = aTableTemplateMap[nBoxFormat]; + const SwBoxAutoFormat& rBoxFormat = rTableStyle.GetBoxFormat(nBoxIndex); + OUString sBoxFormatName; + SwStyleNameMapper::FillProgName(rTableStyle.GetName(), sBoxFormatName, SwGetPoolIdFromName::TabStyle); + sBoxFormatName += rTableStyle.GetTableTemplateCellSubName(rBoxFormat); + aLst.Append( cCELLSTYLE, sBoxFormatName ); + } + } + } + const SwCellStyleTable& rCellStyles = rDoc.GetCellStyles(); + for(size_t i = 0; i < rCellStyles.size(); ++i) + aLst.Append( cCELLSTYLE, rCellStyles[i].GetName() ); + } + + if(!aLst.empty()) + { + nLastPos = SAL_MAX_UINT32; + return Next(); + } + return nullptr; +} + +SfxStyleSheetBase* SwStyleSheetIterator::Next() +{ + assert(bFirstCalled); + ++nLastPos; + if(nLastPos < aLst.size()) + { + mxIterSheet->PresetNameAndFamily(aLst[nLastPos]); + mxIterSheet->SetPhysical( false ); + mxIterSheet->SetMask( nMask ); + if(mxIterSheet->pSet) + { + mxIterSheet->pSet->ClearItem(); + mxIterSheet->pSet= nullptr; + } + return mxIterSheet.get(); + } + return nullptr; +} + +SfxStyleSheetBase* SwStyleSheetIterator::Find(const OUString& rName) +{ + // searching + if( !bFirstCalled ) + First(); + + nLastPos = aLst.FindName( nSearchFamily, rName ); + if( SAL_MAX_UINT32 != nLastPos ) + { + // found + mxStyleSheet->PresetNameAndFamily(aLst[nLastPos]); + // new name is set, so determine its Data + mxStyleSheet->FillStyleSheet( SwDocStyleSheet::FillOnlyName ); + if( !mxStyleSheet->IsPhysical() ) + mxStyleSheet->SetPhysical( false ); + + return mxStyleSheet.get(); + } + return nullptr; +} + +void SwStyleSheetIterator::AppendStyleList(const std::vector<OUString>& rList, + bool bTestUsed, bool bTestHidden, bool bOnlyHidden, + SwGetPoolIdFromName nSection, char cType ) +{ + SwDoc& rDoc = static_cast<SwDocStyleSheetPool*>(pBasePool)->GetDoc(); + bool bUsed = false; + for (const auto & i : rList) + { + bool bHidden = false; + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(i, nSection); + switch ( nSection ) + { + case SwGetPoolIdFromName::TxtColl: + { + bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolTextCollUsed( nId ); + SwFormat* pFormat = rDoc.FindTextFormatCollByName( i ); + bHidden = pFormat && pFormat->IsHidden( ); + } + break; + case SwGetPoolIdFromName::ChrFmt: + { + bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolFormatUsed( nId ); + SwFormat* pFormat = rDoc.FindCharFormatByName( i ); + bHidden = pFormat && pFormat->IsHidden( ); + } + break; + case SwGetPoolIdFromName::FrmFmt: + { + bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolFormatUsed( nId ); + SwFormat* pFormat = rDoc.FindFrameFormatByName( i ); + bHidden = pFormat && pFormat->IsHidden( ); + } + break; + case SwGetPoolIdFromName::PageDesc: + { + bUsed = rDoc.getIDocumentStylePoolAccess().IsPoolPageDescUsed( nId ); + SwPageDesc* pPgDesc = rDoc.FindPageDesc(i); + bHidden = pPgDesc && pPgDesc->IsHidden( ); + } + break; + case SwGetPoolIdFromName::NumRule: + { + SwNumRule* pRule = rDoc.FindNumRulePtr( i ); + bUsed = pRule && SwDoc::IsUsed( *pRule ); + bHidden = pRule && pRule->IsHidden( ); + } + break; + default: + OSL_ENSURE( false, "unknown PoolFormat-Id" ); + } + + bool bMatchHidden = ( bTestHidden && ( bHidden || !bOnlyHidden ) ) || ( !bTestHidden && ( !bHidden || bUsed ) ); + if ( ( !bTestUsed && bMatchHidden ) || ( bTestUsed && bUsed ) ) + aLst.Append( cType, i ); + } +} + +void SwDocStyleSheetPool::InvalidateIterator() +{ + if (SfxStyleSheetIterator* pIter = GetCachedIterator()) + dynamic_cast<SwStyleSheetIterator&>(*pIter).InvalidateIterator(); +} + +void SwStyleSheetIterator::InvalidateIterator() +{ + // potentially we could send an SfxHint to Notify but currently it's + // iterating over the vector anyway so would still be slow - why does + // this iterator not use a map? + bFirstCalled = false; + nLastPos = 0; + aLst.clear(); +} + +void SwStyleSheetIterator::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + // search and remove from View-List!! + const SfxStyleSheetHint* pStyleSheetHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint); + if( pStyleSheetHint && + SfxHintId::StyleSheetErased == pStyleSheetHint->GetId() ) + { + SfxStyleSheetBase* pStyle = pStyleSheetHint->GetStyleSheet(); + + if (pStyle) + aLst.RemoveName(pStyle->GetFamily(), pStyle->GetName()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/mainwn.cxx b/sw/source/uibase/app/mainwn.cxx new file mode 100644 index 000000000..9a01a8437 --- /dev/null +++ b/sw/source/uibase/app/mainwn.cxx @@ -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 . + */ + +#include <mdiexp.hxx> +#include <sfx2/progress.hxx> +#include <docsh.hxx> +#include <swmodule.hxx> +#include <swtypes.hxx> + +class SwDocShell; + +namespace { + +struct SwProgress +{ + long nStartValue, + nStartCount; + SwDocShell *pDocShell; + std::unique_ptr<SfxProgress> pProgress; +}; + +} + +static std::vector<std::unique_ptr<SwProgress>> *pProgressContainer = nullptr; + +static SwProgress *lcl_SwFindProgress( SwDocShell const *pDocShell ) +{ + for (const auto& pTmp : *pProgressContainer) + { + if ( pTmp->pDocShell == pDocShell ) + return pTmp.get(); + } + return nullptr; +} + +void StartProgress( const char* pMessResId, long nStartValue, long nEndValue, + SwDocShell *pDocShell ) +{ + if( SW_MOD()->IsEmbeddedLoadSave() ) + return; + + SwProgress *pProgress = nullptr; + + if ( !pProgressContainer ) + pProgressContainer = new std::vector<std::unique_ptr<SwProgress>>; + else + { + pProgress = lcl_SwFindProgress( pDocShell ); + if ( pProgress ) + ++pProgress->nStartCount; + } + + if ( !pProgress ) + { + pProgress = new SwProgress; + pProgress->pProgress.reset( new SfxProgress( pDocShell, + SwResId(pMessResId), + nEndValue - nStartValue ) ); + pProgress->nStartCount = 1; + pProgress->pDocShell = pDocShell; + pProgressContainer->insert( pProgressContainer->begin(), std::unique_ptr<SwProgress>(pProgress) ); + } + pProgress->nStartValue = nStartValue; +} + +void SetProgressState( long nPosition, SwDocShell const *pDocShell ) +{ + if( pProgressContainer && !SW_MOD()->IsEmbeddedLoadSave() ) + { + SwProgress *pProgress = lcl_SwFindProgress( pDocShell ); + if ( pProgress ) + pProgress->pProgress->SetState(nPosition - pProgress->nStartValue); + } +} + +void EndProgress( SwDocShell const *pDocShell ) +{ + if( pProgressContainer && !SW_MOD()->IsEmbeddedLoadSave() ) + { + SwProgress *pProgress = nullptr; + std::vector<SwProgress *>::size_type i; + for ( i = 0; i < pProgressContainer->size(); ++i ) + { + SwProgress *pTmp = (*pProgressContainer)[i].get(); + if ( pTmp->pDocShell == pDocShell ) + { + pProgress = pTmp; + break; + } + } + + if ( pProgress && 0 == --pProgress->nStartCount ) + { + pProgress->pProgress->Stop(); + pProgressContainer->erase( pProgressContainer->begin() + i ); + //#112337# it may happen that the container has been removed + //while rescheduling + if ( pProgressContainer && pProgressContainer->empty() ) + { + delete pProgressContainer; + pProgressContainer = nullptr; + } + } + } +} + +void RescheduleProgress( SwDocShell const *pDocShell ) +{ + if( pProgressContainer && !SW_MOD()->IsEmbeddedLoadSave() ) + { + SwProgress *pProgress = lcl_SwFindProgress( pDocShell ); + if ( pProgress ) + SfxProgress::Reschedule(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/swdll.cxx b/sw/source/uibase/app/swdll.cxx new file mode 100644 index 000000000..090480bc5 --- /dev/null +++ b/sw/source/uibase/app/swdll.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 <memory> + +#include <svx/svdobj.hxx> + +#include <swdll.hxx> +#include <wdocsh.hxx> +#include <globdoc.hxx> +#include <initui.hxx> +#include <swmodule.hxx> +#include <init.hxx> +#include <dobjfac.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/unique_disposing_ptr.hxx> +#include <comphelper/processfactory.hxx> + +#include <sal/log.hxx> +#include <svx/fmobjfac.hxx> +#include <svx/objfac3d.hxx> +#include <editeng/acorrcfg.hxx> + +#include <swacorr.hxx> +#include <swabstdlg.hxx> + +#include "swdllimpl.hxx" + +using namespace com::sun::star; + +namespace +{ + //Holds a SwDLL and release it on exit, or dispose of the + //default XComponent, whichever comes first + class SwDLLInstance : public comphelper::unique_disposing_solar_mutex_reset_ptr<SwDLL> + { + public: + SwDLLInstance() : comphelper::unique_disposing_solar_mutex_reset_ptr<SwDLL>(uno::Reference<lang::XComponent>( frame::Desktop::create(comphelper::getProcessComponentContext()), uno::UNO_QUERY_THROW), new SwDLL, true) + { + } + }; + + struct theSwDLLInstance : public rtl::Static<SwDLLInstance, theSwDLLInstance> {}; +} + +namespace SwGlobals +{ + void ensure() + { + // coverity[side_effect_free : FALSE] - not actually side-effect-free + theSwDLLInstance::get(); + } + + sw::Filters & getFilters() + { + return theSwDLLInstance::get()->getFilters(); + } +} + +SwDLL::SwDLL() + : m_pAutoCorrCfg(nullptr) +{ + if ( SfxApplication::GetModule(SfxToolsModule::Writer) ) // Module already active + return; + + std::unique_ptr<SvtModuleOptions> xOpt; + if (!utl::ConfigManager::IsFuzzing()) + xOpt.reset(new SvtModuleOptions); + SfxObjectFactory* pDocFact = nullptr; + SfxObjectFactory* pGlobDocFact = nullptr; + if (!xOpt || xOpt->IsWriter()) + { + pDocFact = &SwDocShell::Factory(); + pGlobDocFact = &SwGlobalDocShell::Factory(); + } + + SfxObjectFactory* pWDocFact = &SwWebDocShell::Factory(); + + auto pUniqueModule = std::make_unique<SwModule>(pWDocFact, pDocFact, pGlobDocFact); + SwModule* pModule = pUniqueModule.get(); + SfxApplication::SetModule(SfxToolsModule::Writer, std::move(pUniqueModule)); + + pWDocFact->SetDocumentServiceName("com.sun.star.text.WebDocument"); + + if (!xOpt || xOpt->IsWriter()) + { + pGlobDocFact->SetDocumentServiceName("com.sun.star.text.GlobalDocument"); + pDocFact->SetDocumentServiceName("com.sun.star.text.TextDocument"); + } + + // register 3D-object-Factory + E3dObjFactory(); + + // register form::component::Form-object-Factory + FmFormObjFactory(); + + SdrObjFactory::InsertMakeObjectHdl( LINK( &aSwObjectFactory, SwObjectFactory, MakeObject ) ); + + SAL_INFO( "sw.ui", "Init Core/UI/Filter" ); + // Initialisation of Statics + ::InitCore(); + filters_.reset(new sw::Filters); + ::InitUI(); + + pModule->InitAttrPool(); + // now SWModule can create its Pool + + // register your view-factories here + RegisterFactories(); + + // register your shell-interfaces here + RegisterInterfaces(); + + // register your controllers here + RegisterControls(); + + if (!utl::ConfigManager::IsFuzzing()) + { + // replace SvxAutocorrect with SwAutocorrect + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + const SvxAutoCorrect* pOld = rACfg.GetAutoCorrect(); + rACfg.SetAutoCorrect(new SwAutoCorrect( *pOld )); + m_pAutoCorrCfg = &rACfg; + } +} + +SwDLL::~SwDLL() COVERITY_NOEXCEPT_FALSE +{ + if (m_pAutoCorrCfg) + { + // fdo#86494 SwAutoCorrect must be deleted before FinitCore + m_pAutoCorrCfg->SetAutoCorrect(nullptr); // delete SwAutoCorrect before exit handlers + } + + // Pool has to be deleted before statics are + SW_MOD()->RemoveAttrPool(); + + ::FinitUI(); + filters_.reset(); + ::FinitCore(); + // sign out object-Factory + SdrObjFactory::RemoveMakeObjectHdl(LINK(&aSwObjectFactory, SwObjectFactory, MakeObject )); +} + +sw::Filters & SwDLL::getFilters() +{ + assert(filters_); + return *filters_; +} + +#ifndef DISABLE_DYNLOADING + +extern "C" SAL_DLLPUBLIC_EXPORT +void lok_preload_hook() +{ + SwAbstractDialogFactory::Create(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/swdllimpl.hxx b/sw/source/uibase/app/swdllimpl.hxx new file mode 100644 index 000000000..bcbcaf005 --- /dev/null +++ b/sw/source/uibase/app/swdllimpl.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_APP_SWDLLIMPL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_APP_SWDLLIMPL_HXX + +#include <sal/config.h> + +#include <sal/types.h> + +#include <memory> + +namespace sw { class Filters; } + +class SvxAutoCorrCfg; + +class SwDLL +{ +public: + static void RegisterFactories(); + static void RegisterInterfaces(); + static void RegisterControls(); + + SwDLL(); + ~SwDLL() COVERITY_NOEXCEPT_FALSE; + + sw::Filters & getFilters(); + +private: + SwDLL(SwDLL const&) = delete; + SwDLL& operator=(SwDLL const&) = delete; + + std::unique_ptr< sw::Filters > filters_; + SvxAutoCorrCfg *m_pAutoCorrCfg; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/swmodul1.cxx b/sw/source/uibase/app/swmodul1.cxx new file mode 100644 index 000000000..b6cfe847a --- /dev/null +++ b/sw/source/uibase/app/swmodul1.cxx @@ -0,0 +1,692 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <memory> +#include <boost/property_tree/json_parser.hpp> + +#include <hintids.hxx> +#include <sfx2/request.hxx> +#include <unotools/useroptions.hxx> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <svx/colorwindow.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <editeng/editids.hrc> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/brushitem.hxx> +#include <svl/cjkoptions.hxx> +#include <swmodule.hxx> +#include <swtypes.hxx> +#include <usrpref.hxx> +#include <modcfg.hxx> +#include <view.hxx> +#include <pview.hxx> +#include <wview.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <uinums.hxx> +#include <prtopt.hxx> +#include <navicfg.hxx> +#include <doc.hxx> +#include <strings.hrc> +#include <IDocumentLayoutAccess.hxx> + +#include <tools/color.hxx> +#include <PostItMgr.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::frame; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::lang; + +static void lcl_SetUIPrefs(const SwViewOption &rPref, SwView* pView, SwViewShell* pSh ) +{ + // in FrameSets the actual visibility can differ from the ViewOption's setting + bool bVScrollChanged = rPref.IsViewVScrollBar() != pSh->GetViewOptions()->IsViewVScrollBar(); + bool bHScrollChanged = rPref.IsViewHScrollBar() != pSh->GetViewOptions()->IsViewHScrollBar(); + bool bVAlignChanged = rPref.IsVRulerRight() != pSh->GetViewOptions()->IsVRulerRight(); + + pSh->SetUIOptions(rPref); + const SwViewOption* pNewPref = pSh->GetViewOptions(); + + // Scrollbars on / off + if(bVScrollChanged) + { + pView->EnableVScrollbar(pNewPref->IsViewVScrollBar()); + } + if(bHScrollChanged) + { + pView->EnableHScrollbar( pNewPref->IsViewHScrollBar() || pNewPref->getBrowseMode() ); + } + //if only the position of the vertical ruler has been changed initiate an update + if(bVAlignChanged && !bHScrollChanged && !bVScrollChanged) + pView->InvalidateBorder(); + + // Rulers on / off + if(pNewPref->IsViewVRuler()) + pView->CreateVRuler(); + else + pView->KillVRuler(); + + // TabWindow on / off + if(pNewPref->IsViewHRuler()) + pView->CreateTab(); + else + pView->KillTab(); + + pView->GetPostItMgr()->PrepareView(true); +} + +SwWrtShell* GetActiveWrtShell() +{ + SwView *pActive = ::GetActiveView(); + if( pActive ) + return &pActive->GetWrtShell(); + return nullptr; +} + +SwView* GetActiveView() +{ + SfxViewShell* pView = SfxViewShell::Current(); + return dynamic_cast<SwView*>( pView ); +} + +SwView* SwModule::GetFirstView() +{ + // returns only visible SwView + SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>)); + return pView; +} + +SwView* SwModule::GetNextView(SwView const * pView) +{ + OSL_ENSURE( pView,"return no SwView" ); + SwView* pNView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>)); + return pNView; +} + +// New Master for the settings is set; this affects the current view and all following. +void SwModule::ApplyUsrPref(const SwViewOption &rUsrPref, SwView* pActView, + SvViewOpt nDest ) +{ + SwView* pCurrView = pActView; + SwViewShell* pSh = pCurrView ? &pCurrView->GetWrtShell() : nullptr; + + SwMasterUsrPref* pPref = const_cast<SwMasterUsrPref*>(GetUsrPref( + nDest == SvViewOpt::DestWeb + || (nDest != SvViewOpt::DestText + && dynamic_cast< const SwWebView *>( pCurrView )) )); + + // with Uno, only sdbcx::View, but not the Module should be changed + bool bViewOnly = SvViewOpt::DestViewOnly == nDest; + // fob Preview off + SwPagePreview* pPPView; + if( !pCurrView && nullptr != (pPPView = dynamic_cast<SwPagePreview*>( SfxViewShell::Current())) ) + { + if(!bViewOnly) + pPref->SetUIOptions( rUsrPref ); + pPPView->EnableVScrollbar(pPref->IsViewVScrollBar()); + pPPView->EnableHScrollbar(pPref->IsViewHScrollBar()); + if(!bViewOnly) + { + pPref->SetPagePrevRow(rUsrPref.GetPagePrevRow()); + pPref->SetPagePrevCol(rUsrPref.GetPagePrevCol()); + } + return; + } + + if(!bViewOnly) + { + pPref->SetUsrPref( rUsrPref ); + pPref->SetModified(); + } + + if( !pCurrView ) + return; + + // Passing on to CORE + bool bReadonly; + const SwDocShell* pDocSh = pCurrView->GetDocShell(); + if (pDocSh) + bReadonly = pDocSh->IsReadOnly(); + else //Use existing option if DocShell missing + bReadonly = pSh->GetViewOptions()->IsReadonly(); + std::unique_ptr<SwViewOption> xViewOpt; + if (!bViewOnly) + xViewOpt.reset(new SwViewOption(*pPref)); + else + xViewOpt.reset(new SwViewOption(rUsrPref)); + xViewOpt->SetReadonly( bReadonly ); + if( !(*pSh->GetViewOptions() == *xViewOpt) ) + { + //is maybe only a SwViewShell + pSh->StartAction(); + pSh->ApplyViewOptions( *xViewOpt ); + static_cast<SwWrtShell*>(pSh)->SetReadOnlyAvailable(xViewOpt->IsCursorInProtectedArea()); + pSh->EndAction(); + } + if ( pSh->GetViewOptions()->IsReadonly() != bReadonly ) + pSh->SetReadonlyOption(bReadonly); + + lcl_SetUIPrefs(*xViewOpt, pCurrView, pSh); + + // in the end the Idle-Flag is set again + pPref->SetIdle(true); +} + +void SwModule::ApplyUserMetric( FieldUnit eMetric, bool bWeb ) +{ + SwMasterUsrPref* pPref; + if(bWeb) + { + if(!m_pWebUsrPref) + GetUsrPref(true); + pPref = m_pWebUsrPref.get(); + } + else + { + if(!m_pUsrPref) + GetUsrPref(false); + pPref = m_pUsrPref.get(); + } + FieldUnit eOldMetric = pPref->GetMetric(); + if(eOldMetric != eMetric) + pPref->SetMetric(eMetric); + + FieldUnit eHScrollMetric = pPref->IsHScrollMetric() ? pPref->GetHScrollMetric() : eMetric; + FieldUnit eVScrollMetric = pPref->IsVScrollMetric() ? pPref->GetVScrollMetric() : eMetric; + + SwView* pTmpView = SwModule::GetFirstView(); + // switch the ruler for all MDI-Windows + while(pTmpView) + { + if(bWeb == (dynamic_cast<SwWebView*>( pTmpView) != nullptr) ) + { + pTmpView->ChangeVRulerMetric(eVScrollMetric); + pTmpView->ChangeTabMetric(eHScrollMetric); + } + + pTmpView = SwModule::GetNextView(pTmpView); + } +} + +void SwModule::ApplyRulerMetric( FieldUnit eMetric, bool bHorizontal, bool bWeb ) +{ + SwMasterUsrPref* pPref; + if(bWeb) + { + if(!m_pWebUsrPref) + GetUsrPref(true); + pPref = m_pWebUsrPref.get(); + } + else + { + if(!m_pUsrPref) + GetUsrPref(false); + pPref = m_pUsrPref.get(); + } + if( bHorizontal ) + pPref->SetHScrollMetric(eMetric); + else + pPref->SetVScrollMetric(eMetric); + + SwView* pTmpView = SwModule::GetFirstView(); + // switch metric at the appropriate rulers + while(pTmpView) + { + if(bWeb == (dynamic_cast<SwWebView *>( pTmpView ) != nullptr)) + { + if( bHorizontal ) + pTmpView->ChangeTabMetric(eMetric); + else + pTmpView->ChangeVRulerMetric(eMetric); + } + pTmpView = SwModule::GetNextView(pTmpView); + } +} + +//set the usrpref 's char unit attribute and set rulers unit as char if the "apply char unit" is checked +void SwModule::ApplyUserCharUnit(bool bApplyChar, bool bWeb) +{ + SwMasterUsrPref* pPref; + if(bWeb) + { + if(!m_pWebUsrPref) + GetUsrPref(true); + pPref = m_pWebUsrPref.get(); + } + else + { + if(!m_pUsrPref) + GetUsrPref(false); + pPref = m_pUsrPref.get(); + } + bool bOldApplyCharUnit = pPref->IsApplyCharUnit(); + bool bHasChanged = false; + if(bOldApplyCharUnit != bApplyChar) + { + pPref->SetApplyCharUnit(bApplyChar); + bHasChanged = true; + } + + if( !bHasChanged ) + return; + + FieldUnit eHScrollMetric = pPref->IsHScrollMetric() ? pPref->GetHScrollMetric() : pPref->GetMetric(); + FieldUnit eVScrollMetric = pPref->IsVScrollMetric() ? pPref->GetVScrollMetric() : pPref->GetMetric(); + if(bApplyChar) + { + eHScrollMetric = FieldUnit::CHAR; + eVScrollMetric = FieldUnit::LINE; + } + else + { + SvtCJKOptions aCJKOptions; + if ( !aCJKOptions.IsAsianTypographyEnabled() && ( eHScrollMetric == FieldUnit::CHAR )) + eHScrollMetric = FieldUnit::INCH; + else if ( eHScrollMetric == FieldUnit::CHAR ) + eHScrollMetric = FieldUnit::CM; + if ( !aCJKOptions.IsAsianTypographyEnabled() && ( eVScrollMetric == FieldUnit::LINE )) + eVScrollMetric = FieldUnit::INCH; + else if ( eVScrollMetric == FieldUnit::LINE ) + eVScrollMetric = FieldUnit::CM; + } + SwView* pTmpView = SwModule::GetFirstView(); + // switch rulers for all MDI-Windows + while(pTmpView) + { + if(bWeb == (dynamic_cast<SwWebView*>( pTmpView) != nullptr) ) + { + pTmpView->ChangeVRulerMetric(eVScrollMetric); + pTmpView->ChangeTabMetric(eHScrollMetric); + } + + pTmpView = SwModule::GetNextView(pTmpView); + } +} + +SwNavigationConfig* SwModule::GetNavigationConfig() +{ + if(!m_pNavigationConfig) + { + m_pNavigationConfig.reset( new SwNavigationConfig ); + } + return m_pNavigationConfig.get(); +} + +SwPrintOptions* SwModule::GetPrtOptions(bool bWeb) +{ + if(bWeb && !m_pWebPrintOptions) + { + m_pWebPrintOptions.reset(new SwPrintOptions(true)); + } + else if(!bWeb && !m_pPrintOptions) + { + m_pPrintOptions.reset(new SwPrintOptions(false)); + } + + return bWeb ? m_pWebPrintOptions.get() : m_pPrintOptions.get(); +} + +SwChapterNumRules* SwModule::GetChapterNumRules() +{ + if(!m_pChapterNumRules) + m_pChapterNumRules.reset(new SwChapterNumRules); + return m_pChapterNumRules.get(); +} + +void SwModule::ShowDBObj(SwView const & rView, const SwDBData& rData) +{ + Reference<XFrame> xFrame = rView.GetViewFrame()->GetFrame().GetFrameInterface(); + + uno::Reference<XFrame> xBeamerFrame = xFrame->findFrame("_beamer", FrameSearchFlag::CHILDREN); + if (xBeamerFrame.is()) + { // the beamer has been opened by the SfxViewFrame + Reference<XController> xController = xBeamerFrame->getController(); + Reference<XSelectionSupplier> xControllerSelection(xController, UNO_QUERY); + if (xControllerSelection.is()) + { + + ODataAccessDescriptor aSelection; + aSelection.setDataSource(rData.sDataSource); + aSelection[DataAccessDescriptorProperty::Command] <<= rData.sCommand; + aSelection[DataAccessDescriptorProperty::CommandType] <<= rData.nCommandType; + xControllerSelection->select(makeAny(aSelection.createPropertyValueSequence())); + } + else { + OSL_FAIL("no selection supplier in the beamer!"); + } + } +} + +std::size_t SwModule::GetRedlineAuthor() +{ + if (!m_bAuthorInitialised) + { + const SvtUserOptions& rOpt = GetUserOptions(); + m_sActAuthor = rOpt.GetFullName(); + if (m_sActAuthor.isEmpty()) + { + m_sActAuthor = rOpt.GetID(); + if (m_sActAuthor.isEmpty()) + m_sActAuthor = SwResId( STR_REDLINE_UNKNOWN_AUTHOR ); + } + m_bAuthorInitialised = true; + } + return InsertRedlineAuthor( m_sActAuthor ); +} + +void SwModule::SetRedlineAuthor(const OUString &rAuthor) +{ + m_bAuthorInitialised = true; + m_sActAuthor = rAuthor; + InsertRedlineAuthor( m_sActAuthor ); +} + +OUString const & SwModule::GetRedlineAuthor(std::size_t nPos) +{ + OSL_ENSURE(nPos < m_pAuthorNames.size(), "author not found!"); //#i45342# RTF doc with no author table caused reader to crash + while(nPos >= m_pAuthorNames.size()) + { + InsertRedlineAuthor("nn"); + } + return m_pAuthorNames[nPos]; +} + +static Color lcl_GetAuthorColor(std::size_t nPos) +{ + static const Color aColArr[] = + { + COL_AUTHOR1_DARK, COL_AUTHOR2_DARK, COL_AUTHOR3_DARK, + COL_AUTHOR4_DARK, COL_AUTHOR5_DARK, COL_AUTHOR6_DARK, + COL_AUTHOR7_DARK, COL_AUTHOR8_DARK, COL_AUTHOR9_DARK + }; + + return aColArr[nPos % SAL_N_ELEMENTS(aColArr)]; +} + +/// Returns a JSON representation of a redline author. +static boost::property_tree::ptree lcl_AuthorToJson(const OUString& rAuthor, std::size_t nIndex) +{ + boost::property_tree::ptree aRet; + aRet.put("index", nIndex); + aRet.put("name", rAuthor.toUtf8().getStr()); + aRet.put("color", sal_uInt32(lcl_GetAuthorColor(nIndex))); + return aRet; +} + +OUString SwModule::GetRedlineAuthorInfo() +{ + boost::property_tree::ptree aTable; + for (std::size_t nAuthor = 0; nAuthor < m_pAuthorNames.size(); ++nAuthor) + { + boost::property_tree::ptree aAuthor = lcl_AuthorToJson(m_pAuthorNames[nAuthor], nAuthor); + aTable.push_back(std::make_pair("", aAuthor)); + } + + boost::property_tree::ptree aTree; + aTree.add_child("authors", aTable); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + return OUString::fromUtf8(aStream.str().c_str()); +} + +std::size_t SwModule::InsertRedlineAuthor(const OUString& rAuthor) +{ + std::size_t nPos = 0; + + while(nPos < m_pAuthorNames.size() && m_pAuthorNames[nPos] != rAuthor) + ++nPos; + + if (nPos == m_pAuthorNames.size()) + m_pAuthorNames.push_back(rAuthor); + + return nPos; +} + +static void lcl_FillAuthorAttr( std::size_t nAuthor, SfxItemSet &rSet, + const AuthorCharAttr &rAttr ) +{ + Color aCol( rAttr.m_nColor ); + + if( rAttr.m_nColor == COL_TRANSPARENT ) + aCol = lcl_GetAuthorColor(nAuthor); + + bool bBackGr = rAttr.m_nColor == COL_NONE_COLOR; + + switch (rAttr.m_nItemId) + { + case SID_ATTR_CHAR_WEIGHT: + { + SvxWeightItem aW( static_cast<FontWeight>(rAttr.m_nAttr), RES_CHRATR_WEIGHT ); + rSet.Put( aW ); + aW.SetWhich( RES_CHRATR_CJK_WEIGHT ); + rSet.Put( aW ); + aW.SetWhich( RES_CHRATR_CTL_WEIGHT ); + rSet.Put( aW ); + } + break; + + case SID_ATTR_CHAR_POSTURE: + { + SvxPostureItem aP( static_cast<FontItalic>(rAttr.m_nAttr), RES_CHRATR_POSTURE ); + rSet.Put( aP ); + aP.SetWhich( RES_CHRATR_CJK_POSTURE ); + rSet.Put( aP ); + aP.SetWhich( RES_CHRATR_CTL_POSTURE ); + rSet.Put( aP ); + } + break; + + case SID_ATTR_CHAR_UNDERLINE: + rSet.Put( SvxUnderlineItem( static_cast<FontLineStyle>(rAttr.m_nAttr), + RES_CHRATR_UNDERLINE)); + break; + + case SID_ATTR_CHAR_STRIKEOUT: + rSet.Put(SvxCrossedOutItem( static_cast<FontStrikeout>(rAttr.m_nAttr), + RES_CHRATR_CROSSEDOUT)); + break; + + case SID_ATTR_CHAR_CASEMAP: + rSet.Put( SvxCaseMapItem( static_cast<SvxCaseMap>(rAttr.m_nAttr), + RES_CHRATR_CASEMAP)); + break; + + case SID_ATTR_BRUSH: + rSet.Put( SvxBrushItem( aCol, RES_CHRATR_BACKGROUND )); + bBackGr = true; + break; + } + + if( !bBackGr ) + rSet.Put( SvxColorItem( aCol, RES_CHRATR_COLOR ) ); +} + +void SwModule::GetInsertAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet) +{ + lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetInsertAuthorAttr()); +} + +void SwModule::GetDeletedAuthorAttr(std::size_t nAuthor, SfxItemSet &rSet) +{ + lcl_FillAuthorAttr(nAuthor, rSet, m_pModuleConfig->GetDeletedAuthorAttr()); +} + +// For future extension: +void SwModule::GetFormatAuthorAttr( std::size_t nAuthor, SfxItemSet &rSet ) +{ + lcl_FillAuthorAttr( nAuthor, rSet, m_pModuleConfig->GetFormatAuthorAttr() ); +} + +sal_uInt16 SwModule::GetRedlineMarkPos() const +{ + return m_pModuleConfig->GetMarkAlignMode(); +} + +bool SwModule::IsInsTableFormatNum(bool bHTML) const +{ + return m_pModuleConfig->IsInsTableFormatNum(bHTML); +} + +bool SwModule::IsInsTableChangeNumFormat(bool bHTML) const +{ + return m_pModuleConfig->IsInsTableChangeNumFormat(bHTML); +} + +bool SwModule::IsInsTableAlignNum(bool bHTML) const +{ + return m_pModuleConfig->IsInsTableAlignNum(bHTML); +} + +bool SwModule::IsSplitVerticalByDefault(bool bHTML) const +{ + return m_pModuleConfig->IsSplitVerticalByDefault(bHTML); +} + +void SwModule::SetSplitVerticalByDefault(bool bHTML, bool value) +{ + m_pModuleConfig->SetSplitVerticalByDefault(bHTML, value); +} + +const Color &SwModule::GetRedlineMarkColor() const +{ + return m_pModuleConfig->GetMarkAlignColor(); +} + +const SwViewOption* SwModule::GetViewOption(bool bWeb) +{ + return GetUsrPref( bWeb ); +} + +OUString const & SwModule::GetDocStatWordDelim() const +{ + return m_pModuleConfig->GetWordDelimiter(); +} + +// Passing-through of the ModuleConfig's Metric (for HTML-Export) +FieldUnit SwModule::GetMetric( bool bWeb ) const +{ + SwMasterUsrPref* pPref; + if(bWeb) + { + if(!m_pWebUsrPref) + GetUsrPref(true); + pPref = m_pWebUsrPref.get(); + } + else + { + if(!m_pUsrPref) + GetUsrPref(false); + pPref = m_pUsrPref.get(); + } + return pPref->GetMetric(); +} + +// Pass-through Update-Status +sal_uInt16 SwModule::GetLinkUpdMode() const +{ + if(!m_pUsrPref) + GetUsrPref(false); + return static_cast<sal_uInt16>(m_pUsrPref->GetUpdateLinkMode()); +} + +SwFieldUpdateFlags SwModule::GetFieldUpdateFlags() const +{ + if(!m_pUsrPref) + GetUsrPref(false); + return m_pUsrPref->GetFieldUpdateFlags(); +} + +void SwModule::ApplyFieldUpdateFlags(SwFieldUpdateFlags eFieldFlags) +{ + if(!m_pUsrPref) + GetUsrPref(false); + m_pUsrPref->SetFieldUpdateFlags(eFieldFlags); +} + +void SwModule::ApplyLinkMode(sal_Int32 nNewLinkMode) +{ + if(!m_pUsrPref) + GetUsrPref(false); + m_pUsrPref->SetUpdateLinkMode(nNewLinkMode); +} + +void SwModule::CheckSpellChanges( bool bOnlineSpelling, + bool bIsSpellWrongAgain, bool bIsSpellAllAgain, bool bSmartTags ) +{ + bool bOnlyWrong = bIsSpellWrongAgain && !bIsSpellAllAgain; + bool bInvalid = bOnlyWrong || bIsSpellAllAgain; + if( bOnlineSpelling || bInvalid ) + { + for( SwDocShell *pDocSh = static_cast<SwDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<SwDocShell>)); + pDocSh; + pDocSh = static_cast<SwDocShell*>(SfxObjectShell::GetNext( *pDocSh, checkSfxObjectShell<SwDocShell> ) ) ) + { + SwDoc* pTmp = pDocSh->GetDoc(); + if ( pTmp->getIDocumentLayoutAccess().GetCurrentViewShell() ) + { + pTmp->SpellItAgainSam( bInvalid, bOnlyWrong, bSmartTags ); + SwViewShell* pViewShell = pTmp->getIDocumentLayoutAccess().GetCurrentViewShell(); + if ( bSmartTags && pViewShell && pViewShell->GetWin() ) + pViewShell->GetWin()->Invalidate(); + } + } + } +} + +void SwModule::ApplyDefaultPageMode(bool bIsSquaredPageMode) +{ + if(!m_pUsrPref) + GetUsrPref(false); + m_pUsrPref->SetDefaultPageMode(bIsSquaredPageMode); +} + +SwCompareMode SwModule::GetCompareMode() const +{ + return m_pModuleConfig->GetCompareMode(); +} + +bool SwModule::IsUseRsid() const +{ + return m_pModuleConfig->IsUseRsid(); +} + +bool SwModule::IsIgnorePieces() const +{ + return m_pModuleConfig->IsIgnorePieces(); +} + +sal_uInt16 SwModule::GetPieceLen() const +{ + return m_pModuleConfig->GetPieceLen(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/swmodule.cxx b/sw/source/uibase/app/swmodule.cxx new file mode 100644 index 000000000..b1e51296f --- /dev/null +++ b/sw/source/uibase/app/swmodule.cxx @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svtools/ehdl.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <unotools/resmgr.hxx> +#include <unotools/useroptions.hxx> +#include <svl/ctloptions.hxx> +#include <svx/ParaLineSpacingPopup.hxx> +#include <svx/TextCharacterSpacingPopup.hxx> +#include <svx/TextUnderlinePopup.hxx> +#include <svx/ParaSpacingControl.hxx> +#include <svx/pszctrl.hxx> +#include <svx/insctrl.hxx> +#include <svx/selctrl.hxx> +#include <svx/linectrl.hxx> +#include <svx/tbxctl.hxx> +#include <svx/fillctrl.hxx> +#include <svx/tbcontrl.hxx> +#include <svx/verttexttbxctrl.hxx> +#include <svx/formatpaintbrushctrl.hxx> +#include <svx/contdlg.hxx> +#include <svx/layctrl.hxx> +#include <svx/fontwork.hxx> +#include <SwSpellDialogChildWindow.hxx> +#include <svx/grafctrl.hxx> +#include <svx/clipboardctl.hxx> +#include <svx/lboxctrl.hxx> +#include <svx/imapdlg.hxx> +#include <svx/srchdlg.hxx> +#include <svx/hyperdlg.hxx> +#include <svx/modctrl.hxx> +#include <sfx2/emojipopup.hxx> +#include <sfx2/charmappopup.hxx> +#include <com/sun/star/scanner/ScannerManager.hpp> +#include <com/sun/star/linguistic2/LanguageGuessing.hpp> +#include <ooo/vba/XSinkCaller.hpp> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <docsh.hxx> +#include <swmodule.hxx> +#include <cmdid.h> +#include <pview.hxx> +#include <wview.hxx> +#include <wdocsh.hxx> +#include <srcview.hxx> +#include <glshell.hxx> +#include <tabsh.hxx> +#include <listsh.hxx> +#include <grfsh.hxx> +#include <mediash.hxx> +#include <olesh.hxx> +#include <drawsh.hxx> +#include <wformsh.hxx> +#include <drwtxtsh.hxx> +#include <beziersh.hxx> +#include <wtextsh.hxx> +#include <wfrmsh.hxx> +#include <drformsh.hxx> +#include <wgrfsh.hxx> +#include <wolesh.hxx> +#include <wlistsh.hxx> +#include <wtabsh.hxx> +#include <navipi.hxx> +#include <inputwin.hxx> +#include <usrpref.hxx> +#include <uinums.hxx> +#include <prtopt.hxx> +#include <bookctrl.hxx> +#include <tmplctrl.hxx> +#include <viewlayoutctrl.hxx> +#include <svx/zoomsliderctrl.hxx> +#include <zoomctrl.hxx> +#include <wordcountctrl.hxx> +#include <workctrl.hxx> +#include <fldwrap.hxx> +#include <redlndlg.hxx> +#include <syncbtn.hxx> +#include <modcfg.hxx> +#include <fontcfg.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <swatrset.hxx> +#include <idxmrk.hxx> +#include <wordcountdialog.hxx> +#include <dlelstnr.hxx> +#include <barcfg.hxx> +#include <svx/rubydialog.hxx> +#include <svtools/colorcfg.hxx> +#include <PageSizePopup.hxx> +#include <PageMarginPopup.hxx> +#include <PageOrientationPopup.hxx> +#include <PageColumnPopup.hxx> + +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> + +#include <avmedia/mediaplayer.hxx> +#include <avmedia/mediatoolbox.hxx> + +#include <annotsh.hxx> +#include <navsh.hxx> + +#include <app.hrc> +#include <error.hrc> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <svx/xmlsecctrl.hxx> +bool g_bNoInterrupt = false; + +#include <sfx2/app.hxx> + +#include <svx/svxerr.hxx> + +#include "swdllimpl.hxx" +#include <dbconfig.hxx> +#include <navicfg.hxx> + +using namespace com::sun::star; +using namespace ::com::sun::star::uno; + +SwModule::SwModule( SfxObjectFactory* pWebFact, + SfxObjectFactory* pFact, + SfxObjectFactory* pGlobalFact ) + : SfxModule("sw", {pWebFact, pFact, pGlobalFact}), + m_pAttrPool(nullptr), + m_pView(nullptr), + m_bAuthorInitialised(false), + m_bEmbeddedLoadSave( false ), + m_pDragDrop( nullptr ), + m_pXSelection( nullptr ) +{ + SetName( "StarWriter" ); + SvxErrorHandler::ensure(); + m_pErrorHandler.reset( new SfxErrorHandler( RID_SW_ERRHDL, + ErrCodeArea::Sw, + ErrCodeArea::Sw, + GetResLocale() ) ); + + m_pModuleConfig.reset(new SwModuleOptions); + + // We need them anyways + m_pToolbarConfig.reset(new SwToolbarConfigItem( false )); + m_pWebToolbarConfig.reset(new SwToolbarConfigItem( true )); + + m_pStdFontConfig.reset(new SwStdFontConfig); + + StartListening( *SfxGetpApp() ); + + if (!utl::ConfigManager::IsFuzzing()) + { + // init color configuration + // member <pColorConfig> is created and the color configuration is applied + // at the view options. + GetColorConfig(); + m_xLinguServiceEventListener = new SwLinguServiceEventListener; + } +} + +OUString SwResId(const char* pId) +{ + return Translate::get(pId, SW_MOD()->GetResLocale()); +} + +OUString SwResId(const char* pId, int nCardinality) +{ + return Translate::nget(pId, nCardinality, SW_MOD()->GetResLocale()); +} + +uno::Reference< scanner::XScannerManager2 > const & +SwModule::GetScannerManager() +{ + if (!m_xScannerManager.is()) + { + m_xScannerManager = scanner::ScannerManager::create( comphelper::getProcessComponentContext() ); + } + return m_xScannerManager; +} + +uno::Reference< linguistic2::XLanguageGuessing > const & SwModule::GetLanguageGuesser() +{ + if (!m_xLanguageGuesser.is()) + { + m_xLanguageGuesser = linguistic2::LanguageGuessing::create( comphelper::getProcessComponentContext() ); + } + return m_xLanguageGuesser; +} + +SwModule::~SwModule() +{ + css::uno::Sequence< css::uno::Any > aArgs; + CallAutomationApplicationEventSinks( "Quit", aArgs ); + m_pErrorHandler.reset(); + EndListening( *SfxGetpApp() ); +} + +void SwDLL::RegisterFactories() +{ + // These Id's must not be changed. Through these Id's the View (resume Documentview) + // is created by Sfx. + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsWriter()) + SwView::RegisterFactory ( SFX_INTERFACE_SFXDOCSH ); + +#if HAVE_FEATURE_DESKTOP + SwWebView::RegisterFactory ( SFX_INTERFACE_SFXMODULE ); + + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsWriter()) + { + SwSrcView::RegisterFactory ( SfxInterfaceId(6) ); + SwPagePreview::RegisterFactory ( SfxInterfaceId(7) ); + } +#endif +} + +void SwDLL::RegisterInterfaces() +{ + SwModule* pMod = SW_MOD(); + SwModule::RegisterInterface( pMod ); + SwDocShell::RegisterInterface( pMod ); + SwWebDocShell::RegisterInterface( pMod ); + SwGlosDocShell::RegisterInterface( pMod ); + SwWebGlosDocShell::RegisterInterface( pMod ); + SwView::RegisterInterface( pMod ); + SwWebView::RegisterInterface( pMod ); + SwPagePreview::RegisterInterface( pMod ); + SwSrcView::RegisterInterface( pMod ); + + SwBaseShell::RegisterInterface(pMod); + SwTextShell::RegisterInterface(pMod); + SwTableShell::RegisterInterface(pMod); + SwListShell::RegisterInterface(pMod); + SwFrameShell::RegisterInterface(pMod); + SwDrawBaseShell::RegisterInterface(pMod); + SwDrawShell::RegisterInterface(pMod); + SwDrawFormShell::RegisterInterface(pMod); + SwDrawTextShell::RegisterInterface(pMod); + SwBezierShell::RegisterInterface(pMod); + SwGrfShell::RegisterInterface(pMod); + SwOleShell::RegisterInterface(pMod); + SwNavigationShell::RegisterInterface(pMod); + SwWebTextShell::RegisterInterface(pMod); + SwWebFrameShell::RegisterInterface(pMod); + SwWebGrfShell::RegisterInterface(pMod); + SwWebListShell::RegisterInterface(pMod); + SwWebTableShell::RegisterInterface(pMod); + SwWebDrawFormShell::RegisterInterface(pMod); + SwWebOleShell::RegisterInterface(pMod); + SwMediaShell::RegisterInterface(pMod); + SwAnnotationShell::RegisterInterface(pMod); +} + +void SwDLL::RegisterControls() +{ + SwModule* pMod = SW_MOD(); + + SvxTbxCtlDraw::RegisterControl(SID_INSERT_DRAW, pMod ); + SvxTbxCtlDraw::RegisterControl(SID_TRACK_CHANGES_BAR, pMod ); + SwTbxAutoTextCtrl::RegisterControl(FN_GLOSSARY_DLG, pMod ); + svx::ParaAboveSpacingControl::RegisterControl(SID_ATTR_PARA_ABOVESPACE, pMod); + svx::ParaBelowSpacingControl::RegisterControl(SID_ATTR_PARA_BELOWSPACE, pMod); + svx::ParaLeftSpacingControl::RegisterControl(SID_ATTR_PARA_LEFTSPACE, pMod); + svx::ParaRightSpacingControl::RegisterControl(SID_ATTR_PARA_RIGHTSPACE, pMod); + svx::ParaFirstLineSpacingControl::RegisterControl(SID_ATTR_PARA_FIRSTLINESPACE, pMod); + + SvxClipBoardControl::RegisterControl(SID_PASTE, pMod ); + svx::FormatPaintBrushToolBoxControl::RegisterControl(SID_FORMATPAINTBRUSH, pMod ); + + SvxFillToolBoxControl::RegisterControl(SID_ATTR_FILL_STYLE, pMod ); + SvxLineWidthToolBoxControl::RegisterControl(SID_ATTR_LINE_WIDTH, pMod ); + + SwZoomControl::RegisterControl(SID_ATTR_ZOOM, pMod ); + SwPreviewZoomControl::RegisterControl(FN_PREVIEW_ZOOM, pMod); + SvxPosSizeStatusBarControl::RegisterControl(0, pMod ); + SvxInsertStatusBarControl::RegisterControl(SID_ATTR_INSERT, pMod ); + SvxSelectionModeControl::RegisterControl(FN_STAT_SELMODE, pMod ); + XmlSecStatusBarControl::RegisterControl( SID_SIGNATURE, pMod ); + SwWordCountStatusBarControl::RegisterControl(FN_STAT_WORDCOUNT, pMod); + + SwBookmarkControl::RegisterControl(FN_STAT_PAGE, pMod ); + SwTemplateControl::RegisterControl(FN_STAT_TEMPLATE, pMod ); + SwViewLayoutControl::RegisterControl( SID_ATTR_VIEWLAYOUT, pMod ); + SvxModifyControl::RegisterControl( SID_DOC_MODIFIED, pMod ); + SvxZoomSliderControl::RegisterControl( SID_ATTR_ZOOMSLIDER, pMod ); + + SvxIMapDlgChildWindow::RegisterChildWindow( false, pMod ); + SvxSearchDialogWrapper::RegisterChildWindow( false, pMod ); + SvxHlinkDlgWrapper::RegisterChildWindow( false, pMod ); + SvxFontWorkChildWindow::RegisterChildWindow( false, pMod ); + SwFieldDlgWrapper::RegisterChildWindow( false, pMod ); + SwFieldDataOnlyDlgWrapper::RegisterChildWindow( false, pMod ); + SvxContourDlgChildWindow::RegisterChildWindow( false, pMod ); + SwNavigationChild::RegisterChildWindowContext( pMod ); + SwInputChild::RegisterChildWindow( false, pMod, SfxChildWindowFlags::FORCEDOCK ); + SwRedlineAcceptChild::RegisterChildWindow( false, pMod ); + SwSyncChildWin::RegisterChildWindow( true, pMod ); + SwInsertIdxMarkWrapper::RegisterChildWindow( false, pMod ); + SwInsertAuthMarkWrapper::RegisterChildWindow( false, pMod ); + SwWordCountWrapper::RegisterChildWindow( false, pMod ); + SvxRubyChildWindow::RegisterChildWindow( false, pMod); + SwSpellDialogChildWindow::RegisterChildWindow( + false, pMod, comphelper::LibreOfficeKit::isActive() ? SfxChildWindowFlags::NEVERCLONE + : SfxChildWindowFlags::NONE); + + SvxGrafRedToolBoxControl::RegisterControl( SID_ATTR_GRAF_RED, pMod ); + SvxGrafGreenToolBoxControl::RegisterControl( SID_ATTR_GRAF_GREEN, pMod ); + SvxGrafBlueToolBoxControl::RegisterControl( SID_ATTR_GRAF_BLUE, pMod ); + SvxGrafLuminanceToolBoxControl::RegisterControl( SID_ATTR_GRAF_LUMINANCE, pMod ); + SvxGrafContrastToolBoxControl::RegisterControl( SID_ATTR_GRAF_CONTRAST, pMod ); + SvxGrafGammaToolBoxControl::RegisterControl( SID_ATTR_GRAF_GAMMA, pMod ); + SvxGrafTransparenceToolBoxControl::RegisterControl( SID_ATTR_GRAF_TRANSPARENCE, pMod ); + SvxGrafModeToolBoxControl::RegisterControl( SID_ATTR_GRAF_MODE, pMod ); + +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaToolBoxControl::RegisterControl(SID_AVMEDIA_TOOLBOX, pMod); + ::avmedia::MediaPlayer::RegisterChildWindow(false, pMod); +#endif + + ::sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod); + + SwJumpToSpecificPageControl::RegisterControl(SID_JUMP_TO_SPECIFIC_PAGE, pMod); +} + +// Load Module (only dummy for linking of the DLL) +void SwModule::InitAttrPool() +{ + OSL_ENSURE(!m_pAttrPool, "Pool already exists!"); + m_pAttrPool = new SwAttrPool(nullptr); + SetPool(m_pAttrPool); +} + +void SwModule::RemoveAttrPool() +{ + SetPool(nullptr); + SfxItemPool::Free(m_pAttrPool); +} + +std::unique_ptr<SfxStyleFamilies> SwModule::CreateStyleFamilies() +{ + std::unique_ptr<SfxStyleFamilies> pStyleFamilies(new SfxStyleFamilies); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Para, + SwResId(STR_PARAGRAPHSTYLEFAMILY), + BMP_STYLES_FAMILY_PARA, + RID_PARAGRAPHSTYLEFAMILY, GetResLocale())); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Char, + SwResId(STR_CHARACTERSTYLEFAMILY), + BMP_STYLES_FAMILY_CHAR, + RID_CHARACTERSTYLEFAMILY, GetResLocale())); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Frame, + SwResId(STR_FRAMESTYLEFAMILY), + BMP_STYLES_FAMILY_FRAME, + RID_FRAMESTYLEFAMILY, GetResLocale())); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Page, + SwResId(STR_PAGESTYLEFAMILY), + BMP_STYLES_FAMILY_PAGE, + RID_PAGESTYLEFAMILY, GetResLocale())); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Pseudo, + SwResId(STR_LISTSTYLEFAMILY), + BMP_STYLES_FAMILY_LIST, + RID_LISTSTYLEFAMILY, GetResLocale())); + + pStyleFamilies->emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Table, + SwResId(STR_TABLESTYLEFAMILY), + BMP_STYLES_FAMILY_TABLE, + RID_TABLESTYLEFAMILY, GetResLocale())); + + return pStyleFamilies; +} + +void SwModule::RegisterAutomationApplicationEventsCaller(css::uno::Reference< ooo::vba::XSinkCaller > const& xCaller) +{ + mxAutomationApplicationEventsCaller = xCaller; +} + +void SwModule::CallAutomationApplicationEventSinks(const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments) +{ + if (mxAutomationApplicationEventsCaller.is()) + mxAutomationApplicationEventsCaller->CallSinks(Method, Arguments); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/app/swwait.cxx b/sw/source/uibase/app/swwait.cxx new file mode 100644 index 000000000..f76b79d0b --- /dev/null +++ b/sw/source/uibase/app/swwait.cxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <swwait.hxx> +#include <docsh.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> + +#include <vcl/window.hxx> + +SwWait::SwWait( + SwDocShell &rDocShell, + const bool bLockUnlockDispatcher ) + : mrDoc ( rDocShell ) + , mbLockUnlockDispatcher( bLockUnlockDispatcher ) + , mpLockedDispatchers() +{ + EnterWaitAndLockDispatcher(); +} + +SwWait::~SwWait() +{ + LeaveWaitAndUnlockDispatcher(); +} + +void SwWait::EnterWaitAndLockDispatcher() +{ + SfxViewFrame *pFrame = SfxViewFrame::GetFirst( &mrDoc, false ); + while ( pFrame ) + { + pFrame->GetWindow().EnterWait(); + if ( mbLockUnlockDispatcher ) + { + // do not look already locked dispatchers + SfxDispatcher* pDispatcher = pFrame->GetDispatcher(); + if ( !pDispatcher->IsLocked() ) + { + pDispatcher->Lock( true ); + mpLockedDispatchers.insert( pDispatcher ); + } + } + + pFrame = SfxViewFrame::GetNext( *pFrame, &mrDoc, false ); + } +} + +void SwWait::LeaveWaitAndUnlockDispatcher() +{ + SfxViewFrame *pFrame = SfxViewFrame::GetFirst( &mrDoc, false ); + while ( pFrame ) + { + pFrame->GetWindow().LeaveWait(); + if ( mbLockUnlockDispatcher ) + { + // only unlock dispatchers which had been locked + SfxDispatcher* pDispatcher = pFrame->GetDispatcher(); + if ( mpLockedDispatchers.erase( pDispatcher ) ) + { + pDispatcher->Lock( false ); + } + } + + pFrame = SfxViewFrame::GetNext( *pFrame, &mrDoc, false ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/chrdlg/ccoll.cxx b/sw/source/uibase/chrdlg/ccoll.cxx new file mode 100644 index 000000000..1183c4542 --- /dev/null +++ b/sw/source/uibase/chrdlg/ccoll.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 <cmdid.h> + +#include <ccoll.hxx> +#include <fmtcol.hxx> + +//!! order of entries has to be the same as in +//!! CommandStruct SwCondCollItem::aCmds[] + +// note: also keep this in sync with the list of conditions in xmloff/source/style/prstylecond.cxx + +const char * const aCommandContext[COND_COMMAND_COUNT] = +{ + "TableHeader", + "Table", + "Frame", + "Section", + "Footnote", + "Endnote", + "Header", + "Footer", + "OutlineLevel1", + "OutlineLevel2", + "OutlineLevel3", + "OutlineLevel4", + "OutlineLevel5", + "OutlineLevel6", + "OutlineLevel7", + "OutlineLevel8", + "OutlineLevel9", + "OutlineLevel10", + "NumberingLevel1", + "NumberingLevel2", + "NumberingLevel3", + "NumberingLevel4", + "NumberingLevel5", + "NumberingLevel6", + "NumberingLevel7", + "NumberingLevel8", + "NumberingLevel9", + "NumberingLevel10" +}; + +sal_Int16 GetCommandContextIndex( const OUString &rContextName ) +{ + sal_Int16 nRes = -1; + for (sal_Int16 i = 0; nRes == -1 && i < COND_COMMAND_COUNT; ++i) + { + if (rContextName.equalsAscii( aCommandContext[i] )) + nRes = i; + } + return nRes; +} + +OUString GetCommandContextByIndex( sal_Int16 nIndex ) +{ + OUString aRes; + if (0 <= nIndex && nIndex < COND_COMMAND_COUNT) + { + aRes = OUString::createFromAscii( aCommandContext[ nIndex ] ); + } + return aRes; +} + +// Globals + +const CommandStruct SwCondCollItem::aCmds[] = +{ + { Master_CollCondition::PARA_IN_TABLEHEAD, 0 }, + { Master_CollCondition::PARA_IN_TABLEBODY, 0 }, + { Master_CollCondition::PARA_IN_FRAME, 0 }, + { Master_CollCondition::PARA_IN_SECTION, 0 }, + { Master_CollCondition::PARA_IN_FOOTNOTE, 0 }, + { Master_CollCondition::PARA_IN_ENDNOTE, 0 }, + { Master_CollCondition::PARA_IN_HEADER, 0 }, + { Master_CollCondition::PARA_IN_FOOTER, 0 }, + { Master_CollCondition::PARA_IN_OUTLINE, 0 }, + { Master_CollCondition::PARA_IN_OUTLINE, 1 }, + { Master_CollCondition::PARA_IN_OUTLINE, 2 }, + { Master_CollCondition::PARA_IN_OUTLINE, 3 }, + { Master_CollCondition::PARA_IN_OUTLINE, 4 }, + { Master_CollCondition::PARA_IN_OUTLINE, 5 }, + { Master_CollCondition::PARA_IN_OUTLINE, 6 }, + { Master_CollCondition::PARA_IN_OUTLINE, 7 }, + { Master_CollCondition::PARA_IN_OUTLINE, 8 }, + { Master_CollCondition::PARA_IN_OUTLINE, 9 }, + { Master_CollCondition::PARA_IN_LIST, 0 }, + { Master_CollCondition::PARA_IN_LIST, 1 }, + { Master_CollCondition::PARA_IN_LIST, 2 }, + { Master_CollCondition::PARA_IN_LIST, 3 }, + { Master_CollCondition::PARA_IN_LIST, 4 }, + { Master_CollCondition::PARA_IN_LIST, 5 }, + { Master_CollCondition::PARA_IN_LIST, 6 }, + { Master_CollCondition::PARA_IN_LIST, 7 }, + { Master_CollCondition::PARA_IN_LIST, 8 }, + { Master_CollCondition::PARA_IN_LIST, 9 } +}; + + +// Item for the transport of the condition table +SwCondCollItem::SwCondCollItem() : + SfxPoolItem(FN_COND_COLL) +{ +} + +SwCondCollItem::~SwCondCollItem() +{ +} + +SwCondCollItem* SwCondCollItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwCondCollItem(*this); +} + +bool SwCondCollItem::operator==( const SfxPoolItem& rItem) const +{ + assert(SfxPoolItem::operator==(rItem)); + bool bReturn = true; + for(sal_uInt16 i = 0; i < COND_COMMAND_COUNT; i++) + if (m_sStyles[i] != + static_cast<SwCondCollItem const&>(rItem).m_sStyles[i]) + { + bReturn = false; + break; + } + + return bReturn; +} + +OUString SwCondCollItem::GetStyle(sal_uInt16 const nPos) const +{ + return (nPos < COND_COMMAND_COUNT) ? m_sStyles[nPos] : OUString(); +} + +void +SwCondCollItem::SetStyle(OUString const*const pStyle, sal_uInt16 const nPos) +{ + if( nPos < COND_COMMAND_COUNT ) + m_sStyles[nPos] = pStyle ? *pStyle : OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/StoredChapterNumbering.cxx b/sw/source/uibase/config/StoredChapterNumbering.cxx new file mode 100644 index 000000000..89b72040a --- /dev/null +++ b/sw/source/uibase/config/StoredChapterNumbering.cxx @@ -0,0 +1,476 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <uinums.hxx> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/container/XIndexReplace.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/xml/sax/Writer.hpp> + +#include <comphelper/processfactory.hxx> + +#include <unotools/streamwrap.hxx> + +#include <xmloff/xmlnmspe.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/nmspmap.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlnume.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmlnumi.hxx> + +#include <vcl/svapp.hxx> +#include <tools/diagnose_ex.h> + +#include <unosett.hxx> + + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +namespace sw { + +class StoredChapterNumberingRules + : public ::cppu::WeakImplHelper<container::XNamed,container::XIndexReplace> +{ +private: + // TODO in case this ever becomes accessible via API need an invalidate + SwChapterNumRules & m_rNumRules; + sal_uInt16 const m_nIndex; + + SwNumRulesWithName * GetOrCreateRules() + { + SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); + if (!pRules) + { + m_rNumRules.CreateEmptyNumRule(m_nIndex); + pRules = m_rNumRules.GetRules(m_nIndex); + assert(pRules); + } + return const_cast<SwNumRulesWithName*>(pRules); + } + +public: + StoredChapterNumberingRules( + SwChapterNumRules & rNumRules, sal_uInt16 const nIndex) + : m_rNumRules(rNumRules) + , m_nIndex(nIndex) + { + assert(m_nIndex < SwChapterNumRules::nMaxRules); + } + + // XNamed + virtual OUString SAL_CALL getName() override + { + SolarMutexGuard g; + SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); + if (!pRules) + { + return OUString(); + } + return pRules->GetName(); + } + + virtual void SAL_CALL setName(OUString const& rName) override + { + SolarMutexGuard g; + SwNumRulesWithName *const pRules(GetOrCreateRules()); + pRules->SetName(rName); + } + + // XElementAccess + virtual uno::Type SAL_CALL getElementType() override + { + return ::cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(); + } + + virtual ::sal_Bool SAL_CALL hasElements() override + { + return true; + } + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override + { + return MAXLEVEL; + } + + virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override + { + if (nIndex < 0 || MAXLEVEL <= nIndex) + throw lang::IndexOutOfBoundsException(); + + SolarMutexGuard g; + SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); + if (!pRules) + { + return uno::Any(); + } + SwNumFormat const* pNumFormat(nullptr); + OUString const* pCharStyleName(nullptr); + pRules->GetNumFormat(nIndex, pNumFormat, pCharStyleName); + if (!pNumFormat) + { // the dialog only fills in those levels that are non-default + return uno::Any(); // the export will ignore this level, yay + } + assert(pCharStyleName); + OUString dummy; // pass in empty HeadingStyleName - can't import anyway + uno::Sequence<beans::PropertyValue> const ret( + SwXNumberingRules::GetPropertiesForNumFormat( + *pNumFormat, *pCharStyleName, &dummy, "")); + return uno::makeAny(ret); + } + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( + sal_Int32 nIndex, uno::Any const& rElement) override + { + if (nIndex < 0 || MAXLEVEL <= nIndex) + throw lang::IndexOutOfBoundsException(); + uno::Sequence<beans::PropertyValue> props; + if (!(rElement >>= props)) + throw lang::IllegalArgumentException("invalid type", + static_cast< ::cppu::OWeakObject*>(this), 1); + + SolarMutexGuard g; + SwNumFormat aNumberFormat; + OUString charStyleName; + SwXNumberingRules::SetPropertiesToNumFormat( + aNumberFormat, + charStyleName, + nullptr, nullptr, nullptr, nullptr, + props); + SwNumRulesWithName *const pRules(GetOrCreateRules()); + pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName); + } +}; + +namespace { + +class StoredChapterNumberingExport + : public SvXMLExport +{ +public: + StoredChapterNumberingExport( + uno::Reference<uno::XComponentContext> const& xContext, + OUString const& rFileName, + uno::Reference<xml::sax::XDocumentHandler> const& xHandler) + : SvXMLExport(xContext, "sw::StoredChapterNumberingExport", rFileName, + util::MeasureUnit::CM, xHandler) + { + GetNamespaceMap_().Add(GetXMLToken(XML_NP_OFFICE), + GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE); + GetNamespaceMap_().Add(GetXMLToken(XML_NP_TEXT), + GetXMLToken(XML_N_TEXT), XML_NAMESPACE_TEXT); + GetNamespaceMap_().Add(GetXMLToken(XML_NP_STYLE), + GetXMLToken(XML_N_STYLE), XML_NAMESPACE_STYLE); + GetNamespaceMap_().Add(GetXMLToken(XML_NP_FO), + GetXMLToken(XML_N_FO), XML_NAMESPACE_FO); + GetNamespaceMap_().Add(GetXMLToken(XML_NP_SVG), + GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG); + } + + virtual void ExportAutoStyles_() override {} + virtual void ExportMasterStyles_() override {} + virtual void ExportContent_() override {} + + void ExportRule(SvxXMLNumRuleExport & rExport, + uno::Reference<container::XIndexReplace> const& xRule) + { + uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY); + OUString const name(xNamed->getName()); + bool bEncoded(false); + AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, + EncodeStyleName(name, &bEncoded) ); + if (bEncoded) + { + AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name); + } + + SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT, + XML_OUTLINE_STYLE, true, true ); + rExport.exportLevelStyles(xRule, true); + } + + void ExportRules( + std::set<OUString> const& rCharStyles, + std::vector<uno::Reference<container::XIndexReplace>> const& rRules) + { + GetDocHandler()->startDocument(); + + AddAttribute(XML_NAMESPACE_NONE, + GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_OFFICE), + GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_OFFICE)); + AddAttribute(XML_NAMESPACE_NONE, + GetNamespaceMap_().GetAttrNameByKey (XML_NAMESPACE_TEXT), + GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_TEXT)); + AddAttribute(XML_NAMESPACE_NONE, + GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_STYLE), + GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_STYLE)); + AddAttribute(XML_NAMESPACE_NONE, + GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_FO), + GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_FO)); + AddAttribute(XML_NAMESPACE_NONE, + GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_SVG), + GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_SVG)); + + { + // let's just have an office:styles as a dummy root + SvXMLElementExport styles(*this, + XML_NAMESPACE_OFFICE, XML_STYLES, true, true); + + // horrible hack for char styles to get display-name mapping + for (const auto& rCharStyle : rCharStyles) + { + AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TEXT ); + bool bEncoded(false); + AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, + EncodeStyleName(rCharStyle, &bEncoded) ); + if (bEncoded) + { + AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCharStyle); + } + + SvXMLElementExport style(*this, + XML_NAMESPACE_STYLE, XML_STYLE, true, true); + } + + SvxXMLNumRuleExport numRuleExport(*this); + + for (const auto& rRule : rRules) + { + ExportRule(numRuleExport, rRule); + } + } + + GetDocHandler()->endDocument(); + } +}; + +/** Dummy import context for style:style element that can just read the + attributes needed to map name to display-name. + Unfortunately the "real" context for this depends on some other things. + The mapping is necessary to import the text:style-name attribute + of the text:outline-level-style element. + */ +class StoredChapterNumberingDummyStyleContext + : public SvXMLImportContext +{ +public: + StoredChapterNumberingDummyStyleContext( + SvXMLImport & rImport, + uno::Reference<xml::sax::XFastAttributeList> const& xAttrList) + : SvXMLImportContext(rImport) + { + OUString name; + OUString displayName; + XmlStyleFamily nFamily(XmlStyleFamily::DATA_STYLE); + + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + if (aIter.getToken() == (XML_NAMESPACE_STYLE | XML_FAMILY)) + { + if (IsXMLToken(aIter, XML_TEXT)) + nFamily = XmlStyleFamily::TEXT_TEXT; + else if (IsXMLToken(aIter, XML_NAME)) + name = aIter.toString(); + else if (IsXMLToken(aIter, XML_DISPLAY_NAME)) + displayName = aIter.toString(); + else + SAL_WARN("xmloff", "unknown value for style:family=" << aIter.toString()); + } + else + SAL_WARN("xmloff", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString()); + + if (nFamily != XmlStyleFamily::DATA_STYLE && !name.isEmpty() && !displayName.isEmpty()) + { + rImport.AddStyleDisplayName(nFamily, name, displayName); + } + } +}; + +class StoredChapterNumberingImport; + +class StoredChapterNumberingRootContext + : public SvXMLImportContext +{ +private: + SwChapterNumRules & m_rNumRules; + size_t m_nCounter; + std::vector<rtl::Reference<SvxXMLListStyleContext>> m_Contexts; + +public: + StoredChapterNumberingRootContext( + SwChapterNumRules & rNumRules, SvXMLImport & rImport) + : SvXMLImportContext(rImport) + , m_rNumRules(rNumRules) + , m_nCounter(0) + { + } + + virtual void SAL_CALL startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) override {} + + virtual void SAL_CALL endFastElement(sal_Int32 /*Element*/) override + { + assert(m_Contexts.size() < SwChapterNumRules::nMaxRules); + for (auto iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) + { + uno::Reference<container::XIndexReplace> const xRule( + new sw::StoredChapterNumberingRules(m_rNumRules, + iter - m_Contexts.begin())); + (*iter)->FillUnoNumRule(xRule); + // TODO: xmloff's outline-style import seems to ignore this??? + uno::Reference<container::XNamed> const xNamed(xRule, uno::UNO_QUERY); + xNamed->setName((*iter)->GetDisplayName()); + } + } + + virtual SvXMLImportContextRef CreateChildContext( + sal_uInt16 nPrefix, + const OUString& rLocalName, + const css::uno::Reference< css::xml::sax::XAttributeList >& xAttrList ) override + { + if (XML_NAMESPACE_TEXT == nPrefix && IsXMLToken(rLocalName, XML_OUTLINE_STYLE)) + { + ++m_nCounter; + if (m_nCounter <= SwChapterNumRules::nMaxRules) + { + SvxXMLListStyleContext *const pContext( + new SvxXMLListStyleContext(GetImport(), + nPrefix, rLocalName, xAttrList, true)); + m_Contexts.emplace_back(pContext); + return pContext; + } + } + return nullptr; + } + + 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(TEXT, XML_OUTLINE_STYLE)) + { + // handled in CreateChildContext + } + else if (Element == XML_ELEMENT(STYLE, XML_STYLE)) + { + return new StoredChapterNumberingDummyStyleContext(GetImport(), xAttrList); + } + + return nullptr; + } +}; + +class StoredChapterNumberingImport + : public SvXMLImport +{ +private: + SwChapterNumRules & m_rNumRules; + +public: + StoredChapterNumberingImport( + uno::Reference<uno::XComponentContext> const& xContext, + SwChapterNumRules & rNumRules) + : SvXMLImport(xContext, "sw::StoredChapterNumberingImport", SvXMLImportFlags::ALL) + , m_rNumRules(rNumRules) + { + } + + virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ ) override + { + if (Element == XML_ELEMENT(OFFICE, XML_STYLES)) + return new StoredChapterNumberingRootContext(m_rNumRules, *this); + return nullptr; + } +}; + +} + +void ExportStoredChapterNumberingRules(SwChapterNumRules & rRules, + SvStream & rStream, OUString const& rFileName) +{ + uno::Reference<uno::XComponentContext> const xContext( + ::comphelper::getProcessComponentContext()); + + uno::Reference<io::XOutputStream> const xOutStream( + new ::utl::OOutputStreamWrapper(rStream)); + + uno::Reference<xml::sax::XWriter> const xWriter( + xml::sax::Writer::create(xContext)); + + xWriter->setOutputStream(xOutStream); + + rtl::Reference<StoredChapterNumberingExport> exp(new StoredChapterNumberingExport(xContext, rFileName, xWriter)); + + // if style name contains a space then name != display-name + // ... and the import needs to map from name to display-name then! + std::set<OUString> charStyles; + std::vector<uno::Reference<container::XIndexReplace>> numRules; + for (size_t i = 0; i < SwChapterNumRules::nMaxRules; ++i) + { + if (SwNumRulesWithName const* pRule = rRules.GetRules(i)) + { + for (size_t j = 0; j < MAXLEVEL; ++j) + { + SwNumFormat const* pDummy(nullptr); + OUString const* pCharStyleName(nullptr); + pRule->GetNumFormat(j, pDummy, pCharStyleName); + if (pCharStyleName && !pCharStyleName->isEmpty()) + { + charStyles.insert(*pCharStyleName); + } + } + numRules.push_back(new StoredChapterNumberingRules(rRules, i)); + } + } + + try + { + exp->ExportRules(charStyles, numRules); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.ui", "ExportStoredChapterNumberingRules"); + } +} + +void ImportStoredChapterNumberingRules(SwChapterNumRules & rRules, + SvStream & rStream, OUString const& rFileName) +{ + uno::Reference<uno::XComponentContext> const xContext( + ::comphelper::getProcessComponentContext()); + + uno::Reference<io::XInputStream> const xInStream( + new ::utl::OInputStreamWrapper(rStream)); + + rtl::Reference<StoredChapterNumberingImport> const xImport(new StoredChapterNumberingImport(xContext, rRules)); + + xml::sax::InputSource const source(xInStream, "", "", rFileName); + + try + { + xImport->parseStream(source); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.ui", "ImportStoredChapterNumberingRules"); + } +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/barcfg.cxx b/sw/source/uibase/config/barcfg.cxx new file mode 100644 index 000000000..f57a3b4de --- /dev/null +++ b/sw/source/uibase/config/barcfg.cxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/uno/Sequence.hxx> +#include <wrtsh.hxx> +#include <barcfg.hxx> + +using namespace utl; +using namespace com::sun::star::uno; + +#define SEL_TYPE_TABLE_TEXT 0 +#define SEL_TYPE_LIST_TEXT 1 +#define SEL_TYPE_TABLE_LIST 2 +#define SEL_TYPE_BEZIER 3 +#define SEL_TYPE_GRAPHIC 4 + +SwToolbarConfigItem::SwToolbarConfigItem( bool bWeb ) : + ConfigItem(bWeb ? OUString("Office.WriterWeb/ObjectBar") : OUString("Office.Writer/ObjectBar"), + ConfigItemMode::ReleaseTree) +{ + for(int i = 0; i <= SEL_TYPE_GRAPHIC; ++i) + aTbxIdArray[i] = -1; + + 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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + sal_Int32 nVal = 0; + pValues[nProp] >>= nVal; + aTbxIdArray[nProp] = nVal; + } + } + } +} + +SwToolbarConfigItem::~SwToolbarConfigItem() +{ +} + +static sal_Int32 lcl_getArrayIndex(SelectionType nSelType) +{ + sal_Int32 nRet = -1; + if(nSelType & SelectionType::NumberList) + { + if(nSelType & SelectionType::Table) + nRet = SEL_TYPE_TABLE_LIST; + else + nRet = SEL_TYPE_LIST_TEXT; + } + else if(nSelType & SelectionType::Table) + nRet = SEL_TYPE_TABLE_TEXT; + else if(nSelType & SelectionType::Ornament) + nRet = SEL_TYPE_BEZIER; + else if(nSelType & SelectionType::Graphic) + nRet = SEL_TYPE_GRAPHIC; + return nRet; +} + +void SwToolbarConfigItem::SetTopToolbar(SelectionType nSelType, ToolbarId eBarId) +{ + sal_Int32 nProp = lcl_getArrayIndex(nSelType); + if(nProp >= 0) + { + aTbxIdArray[nProp] = static_cast<sal_Int32>(eBarId); + SetModified(); + } +} + +Sequence<OUString> SwToolbarConfigItem::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "Selection/Table", // SEL_TYPE_TABLE_TEXT + "Selection/NumberedList", // SEL_TYPE_LIST_TEXT + "Selection/NumberedList_InTable", // SEL_TYPE_TABLE_LIST + "Selection/BezierObject", // SEL_TYPE_BEZIER + "Selection/Graphic" //SEL_TYPE_GRAPHIC + }; + const int nCount = 5; + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + return aNames; +} + +void SwToolbarConfigItem::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + pValues[nProp] <<= aTbxIdArray[nProp]; + PutProperties(aNames, aValues); +} + +void SwToolbarConfigItem::Notify( const css::uno::Sequence< OUString >& ) {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/caption.cxx b/sw/source/uibase/config/caption.cxx new file mode 100644 index 000000000..d023bd836 --- /dev/null +++ b/sw/source/uibase/config/caption.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 <editeng/svxenum.hxx> +#include <caption.hxx> + +InsCaptionOpt::InsCaptionOpt(const SwCapObjType eType, const SvGlobalName* pOleId) : + m_bUseCaption(false), + m_eObjType(eType), + m_nNumType(SVX_NUM_ARABIC), + m_sNumberSeparator(". "), + m_nPos(1), + m_nLevel(0), + m_sSeparator( OUString(": ") ), + m_bIgnoreSeqOpts(false), + m_bCopyAttributes(false) +{ + if (pOleId) + m_aOleId = *pOleId; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/cfgitems.cxx b/sw/source/uibase/config/cfgitems.cxx new file mode 100644 index 000000000..41ce12460 --- /dev/null +++ b/sw/source/uibase/config/cfgitems.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 <viewopt.hxx> +#include <cmdid.h> +#include <cfgitems.hxx> +#include <crstate.hxx> + + +SwDocDisplayItem::SwDocDisplayItem() : + SfxPoolItem(FN_PARAM_DOCDISP) +{ + bParagraphEnd = + bTab = + bSpace = + bNonbreakingSpace = + bSoftHyphen = + bCharHiddenText = + bBookmarks = + bManualBreak = true; +}; + +// Item for the Settings dialog, page document view +SwDocDisplayItem::SwDocDisplayItem(const SwViewOption& rVOpt ) : + SfxPoolItem( FN_PARAM_DOCDISP ) +{ + bParagraphEnd = rVOpt.IsParagraph(true); + bTab = rVOpt.IsTab(true); + bSpace = rVOpt.IsBlank(true); + bNonbreakingSpace = rVOpt.IsHardBlank(); + bSoftHyphen = rVOpt.IsSoftHyph(); + bCharHiddenText = rVOpt.IsShowHiddenChar(true); + bBookmarks = rVOpt.IsShowBookmarks(true); + bManualBreak = rVOpt.IsLineBreak(true); +} + +SwDocDisplayItem* SwDocDisplayItem::Clone( SfxItemPool* ) const +{ + return new SwDocDisplayItem( *this ); +} + +bool SwDocDisplayItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + + const SwDocDisplayItem& rItem = static_cast<const SwDocDisplayItem&>(rAttr); + + return ( bParagraphEnd == rItem.bParagraphEnd && + bTab == rItem.bTab && + bSpace == rItem.bSpace && + bNonbreakingSpace == rItem.bNonbreakingSpace && + bSoftHyphen == rItem.bSoftHyphen && + bCharHiddenText == rItem.bCharHiddenText && + bBookmarks == rItem.bBookmarks && + bManualBreak == rItem.bManualBreak ); +} + +void SwDocDisplayItem::FillViewOptions( SwViewOption& rVOpt) const +{ + rVOpt.SetParagraph (bParagraphEnd ); + rVOpt.SetTab (bTab ); + rVOpt.SetBlank (bSpace ); + rVOpt.SetHardBlank (bNonbreakingSpace ); + rVOpt.SetSoftHyph (bSoftHyphen ); + rVOpt.SetShowHiddenChar(bCharHiddenText ); + rVOpt.SetShowBookmarks(bBookmarks ); + rVOpt.SetLineBreak (bManualBreak ); +} + +SwElemItem::SwElemItem() : + SfxPoolItem(FN_PARAM_ELEM) +{ + m_bVertRuler = + m_bVertRulerRight= + m_bCrosshair = + m_bSmoothScroll = + m_bTable = + m_bGraphic = + m_bDrawing = + m_bNotes = false; + m_bShowInlineTooltips = true; + m_bFieldHiddenText = + m_bShowHiddenPara = false; +} + +SwElemItem::SwElemItem(const SwViewOption& rVOpt) : + SfxPoolItem( FN_PARAM_ELEM ) +{ + m_bVertRuler = rVOpt.IsViewVRuler(true); + m_bVertRulerRight = rVOpt.IsVRulerRight(); + m_bCrosshair = rVOpt.IsCrossHair(); + m_bSmoothScroll = rVOpt.IsSmoothScroll(); + m_bTable = rVOpt.IsTable(); + m_bGraphic = rVOpt.IsGraphic(); + m_bDrawing = rVOpt.IsDraw() && rVOpt.IsControl(); + m_bNotes = rVOpt.IsPostIts(); + m_bShowInlineTooltips = rVOpt.IsShowInlineTooltips(); + m_bFieldHiddenText = rVOpt.IsShowHiddenField(); + m_bShowHiddenPara = rVOpt.IsShowHiddenPara(); +} + +SwElemItem* SwElemItem::Clone( SfxItemPool* ) const +{ + return new SwElemItem( *this ); +} + +bool SwElemItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + + const SwElemItem& rItem = static_cast<const SwElemItem&>(rAttr); + + return ( m_bVertRuler == rItem.m_bVertRuler && + m_bVertRulerRight == rItem.m_bVertRulerRight&& + m_bCrosshair == rItem.m_bCrosshair && + m_bSmoothScroll == rItem.m_bSmoothScroll && + m_bTable == rItem.m_bTable && + m_bGraphic == rItem.m_bGraphic && + m_bDrawing == rItem.m_bDrawing && + m_bNotes == rItem.m_bNotes && + m_bShowInlineTooltips == rItem.m_bShowInlineTooltips && + m_bFieldHiddenText == rItem.m_bFieldHiddenText && + m_bShowHiddenPara == rItem.m_bShowHiddenPara); +} + +void SwElemItem::FillViewOptions( SwViewOption& rVOpt) const +{ + rVOpt.SetViewVRuler(m_bVertRuler ); + rVOpt.SetVRulerRight(m_bVertRulerRight ); + rVOpt.SetCrossHair(m_bCrosshair ); + rVOpt.SetSmoothScroll(m_bSmoothScroll); + rVOpt.SetTable (m_bTable ); + rVOpt.SetGraphic (m_bGraphic ); + rVOpt.SetDraw (m_bDrawing ); + rVOpt.SetControl (m_bDrawing ); + rVOpt.SetPostIts (m_bNotes ); + rVOpt.SetShowInlineTooltips( m_bShowInlineTooltips ); + rVOpt.SetShowHiddenField(m_bFieldHiddenText ); + rVOpt.SetShowHiddenPara(m_bShowHiddenPara ); +} + +// CTOR for empty Item +SwAddPrinterItem::SwAddPrinterItem(): + SfxPoolItem(FN_PARAM_ADDPRINTER) +{ +} + +// CTOR from SwPrintOptions +SwAddPrinterItem::SwAddPrinterItem( const SwPrintData& rPrtData ) : + SfxPoolItem(FN_PARAM_ADDPRINTER) +{ + SwPrintData::operator=(rPrtData); +} + +SwAddPrinterItem* SwAddPrinterItem::Clone( SfxItemPool* ) const +{ + return new SwAddPrinterItem( *this ); +} + +bool SwAddPrinterItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + + const SwAddPrinterItem& rItem = static_cast<const SwAddPrinterItem&>(rAttr); + + return SwPrintData::operator==(rItem); +} + +// Item for Settings dialog, ShadowCursorPage +SwShadowCursorItem::SwShadowCursorItem() + : SfxPoolItem( FN_PARAM_SHADOWCURSOR ), + eMode( SwFillMode::Tab ) + ,bOn( false ) +{ +} + +SwShadowCursorItem::SwShadowCursorItem( const SwViewOption& rVOpt ) + : SfxPoolItem( FN_PARAM_SHADOWCURSOR ), + eMode( rVOpt.GetShdwCursorFillMode() ) + ,bOn( rVOpt.IsShadowCursor() ) + +{ +} + +SwShadowCursorItem* SwShadowCursorItem::Clone( SfxItemPool* ) const +{ + return new SwShadowCursorItem( *this ); +} + +bool SwShadowCursorItem::operator==( const SfxPoolItem& rCmp ) const +{ + return SfxPoolItem::operator==(rCmp) && + IsOn() == static_cast<const SwShadowCursorItem&>(rCmp).IsOn() && + GetMode() == static_cast<const SwShadowCursorItem&>(rCmp).GetMode(); +} + +void SwShadowCursorItem::FillViewOptions( SwViewOption& rVOpt ) const +{ + rVOpt.SetShadowCursor( bOn ); + rVOpt.SetShdwCursorFillMode( eMode ); +} + +#ifdef DBG_UTIL +SwTestItem* SwTestItem::Clone( SfxItemPool* ) const +{ + return new SwTestItem( *this ); +} + +bool SwTestItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + + const SwTestItem& rItem = static_cast<const SwTestItem&>( rAttr); + + return ( m_bTest1==rItem.m_bTest1&& + m_bTest2==rItem.m_bTest2&& + m_bTest3==rItem.m_bTest3&& + m_bTest4==rItem.m_bTest4&& + m_bTest5==rItem.m_bTest5&& + m_bTest6==rItem.m_bTest6&& + m_bTest7==rItem.m_bTest7&& + m_bTest8==rItem.m_bTest8&& + m_bTest9==rItem.m_bTest9&& + m_bTest10==rItem.m_bTest10); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/dbconfig.cxx b/sw/source/uibase/config/dbconfig.cxx new file mode 100644 index 000000000..8df0a7d49 --- /dev/null +++ b/sw/source/uibase/config/dbconfig.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 <dbconfig.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/uno/Sequence.hxx> +#include <swdbdata.hxx> + +using namespace utl; +using namespace com::sun::star::uno; + +const Sequence<OUString>& SwDBConfig::GetPropertyNames() +{ + static Sequence<OUString> aNames { + "AddressBook/DataSourceName", // 0 + "AddressBook/Command", // 1 + "AddressBook/CommandType", // 2 + "Bibliography/CurrentDataSource/DataSourceName", // 4 + "Bibliography/CurrentDataSource/Command", // 5 + "Bibliography/CurrentDataSource/CommandType" // 6 + }; + return aNames; +} + +SwDBConfig::SwDBConfig() : + ConfigItem("Office.DataAccess", ConfigItemMode::ReleaseTree) +{ +}; + +SwDBConfig::~SwDBConfig() +{ + pAdrImpl.reset(); + pBibImpl.reset(); +} + +void SwDBConfig::Load() +{ + const Sequence<OUString>& rNames = GetPropertyNames(); + if(!pAdrImpl) + { + pAdrImpl.reset(new SwDBData); + pAdrImpl->nCommandType = 0; + pBibImpl.reset(new SwDBData); + pBibImpl->nCommandType = 0; + } + Sequence<Any> aValues = GetProperties(rNames); + const Any* pValues = aValues.getConstArray(); + OSL_ENSURE(aValues.getLength() == rNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == rNames.getLength()) + { + for(int nProp = 0; nProp < rNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] >>= pAdrImpl->sDataSource; break; + case 1: pValues[nProp] >>= pAdrImpl->sCommand; break; + case 2: pValues[nProp] >>= pAdrImpl->nCommandType; break; + case 3: pValues[nProp] >>= pBibImpl->sDataSource; break; + case 4: pValues[nProp] >>= pBibImpl->sCommand; break; + case 5: pValues[nProp] >>= pBibImpl->nCommandType; break; + } + } + } +} + +const SwDBData& SwDBConfig::GetAddressSource() +{ + if(!pAdrImpl) + Load(); + return *pAdrImpl; +} + +const SwDBData& SwDBConfig::GetBibliographySource() +{ + if(!pBibImpl) + Load(); + return *pBibImpl; +} + +void SwDBConfig::ImplCommit() {} +void SwDBConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/fontcfg.cxx b/sw/source/uibase/config/fontcfg.cxx new file mode 100644 index 000000000..e58a42bb8 --- /dev/null +++ b/sw/source/uibase/config/fontcfg.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 <fontcfg.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <osl/diagnose.h> +#include <vcl/outdev.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/lingucfg.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> + +using namespace utl; +using namespace com::sun::star::uno; + +static LanguageType lcl_LanguageOfType(sal_Int16 nType, LanguageType eWestern, LanguageType eCJK, LanguageType eCTL) +{ + return nType < FONT_STANDARD_CJK + ? eWestern + : nType >= FONT_STANDARD_CTL ? eCTL : eCJK; +} + +Sequence<OUString> const & SwStdFontConfig::GetPropertyNames() +{ + static Sequence<OUString> aNames { + "DefaultFont/Standard", // 0 + "DefaultFont/Heading", // 1 + "DefaultFont/List", // 2 + "DefaultFont/Caption", // 3 + "DefaultFont/Index", // 4 + "DefaultFontCJK/Standard", // 5 + "DefaultFontCJK/Heading", // 6 + "DefaultFontCJK/List", // 7 + "DefaultFontCJK/Caption", // 8 + "DefaultFontCJK/Index", // 9 + "DefaultFontCTL/Standard", // 10 + "DefaultFontCTL/Heading", // 11 + "DefaultFontCTL/List", // 12 + "DefaultFontCTL/Caption", // 13 + "DefaultFontCTL/Index", // 14 + "DefaultFont/StandardHeight", // 15 + "DefaultFont/HeadingHeight", // 16 + "DefaultFont/ListHeight", // 17 + "DefaultFont/CaptionHeight", // 18 + "DefaultFont/IndexHeight", // 19 + "DefaultFontCJK/StandardHeight", // 20 + "DefaultFontCJK/HeadingHeight", // 21 + "DefaultFontCJK/ListHeight", // 22 + "DefaultFontCJK/CaptionHeight", // 23 + "DefaultFontCJK/IndexHeight", // 24 + "DefaultFontCTL/StandardHeight", // 25 + "DefaultFontCTL/HeadingHeight", // 26 + "DefaultFontCTL/ListHeight", // 27 + "DefaultFontCTL/CaptionHeight", // 28 + "DefaultFontCTL/IndexHeight" // 29 + }; + return aNames; +} + +SwStdFontConfig::SwStdFontConfig() : + utl::ConfigItem("Office.Writer") +{ + SvtLinguOptions aLinguOpt; + + if (!utl::ConfigManager::IsFuzzing()) + SvtLinguConfig().GetOptions( aLinguOpt ); + + LanguageType eWestern = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN), + eCJK = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN), + eCTL = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + for(sal_Int16 i = 0; i < DEF_FONT_COUNT; i++) + { + sDefaultFonts[i] = GetDefaultFor(i, + lcl_LanguageOfType(i, eWestern, eCJK, eCTL)); + nDefaultFontHeight[i] = -1; + } + + 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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + if( nProp < DEF_FONT_COUNT) + { + OUString sVal; + pValues[nProp] >>= sVal; + sDefaultFonts[nProp] = sVal; + } + else + { + pValues[nProp] >>= nDefaultFontHeight[nProp - DEF_FONT_COUNT]; + nDefaultFontHeight[nProp - DEF_FONT_COUNT] = convertMm100ToTwip(nDefaultFontHeight[nProp - DEF_FONT_COUNT]); + } + } + } + } +} + +void SwStdFontConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + SvtLinguOptions aLinguOpt; + + SvtLinguConfig().GetOptions( aLinguOpt ); + + LanguageType eWestern = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN), + eCJK = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN), + eCTL = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + for(sal_uInt16 nProp = 0; + nProp < sal::static_int_cast< sal_uInt16, sal_Int32 >( aNames.getLength() ); + nProp++) + { + if( nProp < DEF_FONT_COUNT ) + { + if(GetDefaultFor(nProp, lcl_LanguageOfType(nProp, eWestern, eCJK, eCTL)) != sDefaultFonts[nProp]) + pValues[nProp] <<= sDefaultFonts[nProp]; + } + else + { + if(nDefaultFontHeight[nProp - DEF_FONT_COUNT] > 0) + pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(nDefaultFontHeight[nProp - DEF_FONT_COUNT])); + } + } + PutProperties(aNames, aValues); +} + +SwStdFontConfig::~SwStdFontConfig() +{ +} + +bool SwStdFontConfig::IsFontDefault(sal_uInt16 nFontType) const +{ + bool bSame = false; + SvtLinguOptions aLinguOpt; + + if (!utl::ConfigManager::IsFuzzing()) + SvtLinguConfig().GetOptions(aLinguOpt); + + LanguageType eWestern = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN), + eCJK = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN), + eCTL = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + OUString sDefFont(GetDefaultFor(FONT_STANDARD, eWestern)); + OUString sDefFontCJK(GetDefaultFor(FONT_STANDARD_CJK, eCJK)); + OUString sDefFontCTL(GetDefaultFor(FONT_STANDARD_CTL, eCTL)); + LanguageType eLang = lcl_LanguageOfType(nFontType, eWestern, eCJK, eCTL); + switch( nFontType ) + { + case FONT_STANDARD: + bSame = sDefaultFonts[nFontType] == sDefFont; + break; + case FONT_STANDARD_CJK: + bSame = sDefaultFonts[nFontType] == sDefFontCJK; + break; + case FONT_STANDARD_CTL: + bSame = sDefaultFonts[nFontType] == sDefFontCTL; + break; + case FONT_OUTLINE : + case FONT_OUTLINE_CJK : + case FONT_OUTLINE_CTL : + bSame = sDefaultFonts[nFontType] == + GetDefaultFor(nFontType, eLang); + break; + case FONT_LIST : + case FONT_CAPTION : + case FONT_INDEX : + bSame = sDefaultFonts[nFontType] == sDefFont && + sDefaultFonts[FONT_STANDARD] == sDefFont; + break; + case FONT_LIST_CJK : + case FONT_CAPTION_CJK : + case FONT_INDEX_CJK : + { + bool b1 = sDefaultFonts[FONT_STANDARD_CJK] == sDefFontCJK; + bSame = b1 && sDefaultFonts[nFontType] == sDefFontCJK; + } + break; + case FONT_LIST_CTL : + case FONT_CAPTION_CTL : + case FONT_INDEX_CTL : + { + bool b1 = sDefaultFonts[FONT_STANDARD_CJK] == sDefFontCTL; + bSame = b1 && sDefaultFonts[nFontType] == sDefFontCTL; + } + break; + } + return bSame; +} + +OUString SwStdFontConfig::GetDefaultFor(sal_uInt16 nFontType, LanguageType eLang) +{ + DefaultFontType nFontId; + switch( nFontType ) + { + case FONT_OUTLINE : + nFontId = DefaultFontType::LATIN_HEADING; + break; + case FONT_OUTLINE_CJK : + nFontId = DefaultFontType::CJK_HEADING; + break; + case FONT_OUTLINE_CTL : + nFontId = DefaultFontType::CTL_HEADING; + break; + case FONT_STANDARD_CJK: + case FONT_LIST_CJK : + case FONT_CAPTION_CJK : + case FONT_INDEX_CJK : + nFontId = DefaultFontType::CJK_TEXT; + break; + case FONT_STANDARD_CTL: + case FONT_LIST_CTL : + case FONT_CAPTION_CTL : + case FONT_INDEX_CTL : + nFontId = DefaultFontType::CTL_TEXT; + break; + default: + nFontId = DefaultFontType::LATIN_TEXT; + } + vcl::Font aFont = OutputDevice::GetDefaultFont(nFontId, eLang, GetDefaultFontFlags::OnlyOne); + return aFont.GetFamilyName(); +} + +sal_Int32 SwStdFontConfig::GetDefaultHeightFor(sal_uInt16 nFontType, LanguageType eLang) +{ + sal_Int32 nRet = FONTSIZE_DEFAULT; + switch( nFontType ) + { + case FONT_OUTLINE: + case FONT_OUTLINE_CJK: + case FONT_OUTLINE_CTL: + nRet = FONTSIZE_OUTLINE; + break; + case FONT_STANDARD_CJK: + nRet = FONTSIZE_CJK_DEFAULT; + break; + } + if( eLang == LANGUAGE_THAI && nFontType >= FONT_STANDARD_CTL ) + { + nRet = nRet * 4 / 3; + } + return nRet; +} + +void SwStdFontConfig::ChangeInt( sal_uInt16 nFontType, sal_Int32 nHeight ) +{ + OSL_ENSURE( nFontType < DEF_FONT_COUNT, "invalid index in SwStdFontConfig::ChangeInt()"); + if( nFontType < DEF_FONT_COUNT && nDefaultFontHeight[nFontType] != nHeight) + { + SvtLinguOptions aLinguOpt; + if (!utl::ConfigManager::IsFuzzing()) + SvtLinguConfig().GetOptions( aLinguOpt ); + + LanguageType eWestern = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN), + eCJK = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN), + eCTL = MsLangId::resolveSystemLanguageByScriptType(aLinguOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + // #i92090# default height value sets back to -1 + const sal_Int32 nDefaultHeight = GetDefaultHeightFor(nFontType, lcl_LanguageOfType(nFontType, eWestern, eCJK, eCTL)); + const bool bIsDefaultHeight = nHeight == nDefaultHeight; + if( bIsDefaultHeight && nDefaultFontHeight[nFontType] > 0 ) + { + SetModified(); + nDefaultFontHeight[nFontType] = -1; + } + else if( !bIsDefaultHeight && nHeight != nDefaultFontHeight[nFontType] ) + { + SetModified(); + nDefaultFontHeight[nFontType] = nHeight; + } + } +} + +sal_Int32 SwStdFontConfig::GetFontHeight( sal_uInt8 nFont, sal_uInt8 nScriptType, LanguageType eLang ) +{ + OSL_ENSURE(nFont + FONT_PER_GROUP * nScriptType < DEF_FONT_COUNT, "wrong index in SwStdFontConfig::GetFontHeight()"); + sal_Int32 nRet = nDefaultFontHeight[nFont + FONT_PER_GROUP * nScriptType]; + if(nRet <= 0) + return GetDefaultHeightFor(nFont + FONT_PER_GROUP * nScriptType, eLang); + return nRet; +} + +void SwStdFontConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/modcfg.cxx b/sw/source/uibase/config/modcfg.cxx new file mode 100644 index 000000000..949aeff1e --- /dev/null +++ b/sw/source/uibase/config/modcfg.cxx @@ -0,0 +1,1334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/classids.hxx> +#include <o3tl/any.hxx> +#include <tools/fontenum.hxx> +#include <editeng/svxenum.hxx> +#include <editeng/editids.hrc> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> + +#include <tools/mapunit.hxx> +#include <tools/globname.hxx> +#include <itabenum.hxx> +#include <modcfg.hxx> +#include <caption.hxx> + +using namespace com::sun::star::uno; + +#define GLOB_NAME_CALC 0 +#define GLOB_NAME_IMPRESS 1 +#define GLOB_NAME_DRAW 2 +#define GLOB_NAME_MATH 3 +#define GLOB_NAME_CHART 4 + +InsCaptionOpt* InsCaptionOptArr::Find(const SwCapObjType eType, const SvGlobalName *pOleId) +{ + for (auto const& it : m_InsCapOptArr) + { + InsCaptionOpt &rObj = *it; + if (rObj.GetObjType() == eType && (eType != OLE_CAP || (pOleId && rObj.GetOleId() == *pOleId))) + return &rObj; + } + + return nullptr; +} + +void InsCaptionOptArr::Insert(InsCaptionOpt* pObj) +{ + m_InsCapOptArr.push_back(std::unique_ptr<InsCaptionOpt>(pObj)); //takes ownership +} + +const InsCaptionOpt* SwModuleOptions::GetCapOption( + bool bHTML, const SwCapObjType eType, const SvGlobalName *pOleId) +{ + if(bHTML) + { + OSL_FAIL("no caption option in sw/web!"); + return nullptr; + } + else + { + if(eType == OLE_CAP && pOleId) + { + bool bFound = false; + for( sal_uInt16 nId = 0; nId <= GLOB_NAME_CHART && !bFound; nId++) + bFound = *pOleId == m_aInsertConfig.m_aGlobalNames[nId ]; + if(!bFound) + return m_aInsertConfig.m_pOLEMiscOpt.get(); + } + return m_aInsertConfig.m_pCapOptions->Find(eType, pOleId); + } +} + +bool SwModuleOptions::SetCapOption(bool bHTML, const InsCaptionOpt* pOpt) +{ + bool bRet = false; + + if(bHTML) + { + OSL_FAIL("no caption option in sw/web!"); + } + else if (pOpt) + { + if(pOpt->GetObjType() == OLE_CAP) + { + bool bFound = false; + for( sal_uInt16 nId = 0; nId <= GLOB_NAME_CHART; nId++) + bFound = pOpt->GetOleId() == m_aInsertConfig.m_aGlobalNames[nId ]; + if(!bFound) + { + if(m_aInsertConfig.m_pOLEMiscOpt) + *m_aInsertConfig.m_pOLEMiscOpt = *pOpt; + else + m_aInsertConfig.m_pOLEMiscOpt.reset(new InsCaptionOpt(*pOpt)); + } + } + + InsCaptionOptArr& rArr = *m_aInsertConfig.m_pCapOptions; + InsCaptionOpt *pObj = rArr.Find(pOpt->GetObjType(), &pOpt->GetOleId()); + + if (pObj) + { + *pObj = *pOpt; + } + else + rArr.Insert(new InsCaptionOpt(*pOpt)); + + m_aInsertConfig.SetModified(); + bRet = true; + } + + return bRet; +} + +SwModuleOptions::SwModuleOptions() : + m_aInsertConfig(false), + m_aWebInsertConfig(true), + m_aTableConfig(false), + m_aWebTableConfig(true), + m_bHideFieldTips(false) +{ +} + +OUString SwModuleOptions::ConvertWordDelimiter(const OUString& rDelim, bool bFromUI) +{ + OUStringBuffer sReturn; + const sal_Int32 nDelimLen = rDelim.getLength(); + if(bFromUI) + { + for (sal_Int32 i = 0; i < nDelimLen; ) + { + const sal_Unicode c = rDelim[i++]; + + if (c == '\\' && i < nDelimLen ) + { + switch (rDelim[i++]) + { + case 'n': sReturn.append("\n"); break; + case 't': sReturn.append("\t"); break; + case '\\': sReturn.append("\\"); break; + + case 'x': + { + sal_Unicode nChar = 0; + bool bValidData = true; + for( sal_Int32 n = 0; n < 2 && i < nDelimLen; ++n, ++i ) + { + sal_Unicode nVal = rDelim[i]; + if( (nVal >= '0') && ( nVal <= '9') ) + nVal -= '0'; + else if( (nVal >= 'A') && (nVal <= 'F') ) + nVal -= 'A' - 10; + else if( (nVal >= 'a') && (nVal <= 'f') ) + nVal -= 'a' - 10; + else + { + OSL_FAIL("wrong hex value" ); + bValidData = false; + break; + } + + nChar <<= 4; + nChar += nVal; + } + if( bValidData ) + sReturn.append(nChar); + break; + } + + default: // Unknown, so insert backslash + sReturn.append("\\"); + i--; + break; + } + } + else + sReturn.append(c); + } + } + else + { + for (sal_Int32 i = 0; i < nDelimLen; ++i) + { + const sal_Unicode c = rDelim[i]; + + switch (c) + { + case '\n': sReturn.append("\\n"); break; + case '\t': sReturn.append("\\t"); break; + case '\\': sReturn.append("\\\\"); break; + + default: + if( c <= 0x1f || c >= 0x7f ) + { + sReturn.append("\\x").append(OUString::number( c, 16 )); + } + else + { + sReturn.append(c); + } + } + } + } + return sReturn.makeStringAndClear(); +} + +const Sequence<OUString>& SwRevisionConfig::GetPropertyNames() +{ + static Sequence<OUString> const aNames + { + "TextDisplay/Insert/Attribute", // 0 + "TextDisplay/Insert/Color", // 1 + "TextDisplay/Delete/Attribute", // 2 + "TextDisplay/Delete/Color", // 3 + "TextDisplay/ChangedAttribute/Attribute", // 4 + "TextDisplay/ChangedAttribute/Color", // 5 + "LinesChanged/Mark", // 6 + "LinesChanged/Color" // 7 + }; + return aNames; +} + +SwRevisionConfig::SwRevisionConfig() : + ConfigItem("Office.Writer/Revision", ConfigItemMode::ReleaseTree) +{ + m_aInsertAttr.m_nItemId = SID_ATTR_CHAR_UNDERLINE; + m_aInsertAttr.m_nAttr = LINESTYLE_SINGLE; + m_aInsertAttr.m_nColor = COL_TRANSPARENT; + m_aDeletedAttr.m_nItemId = SID_ATTR_CHAR_STRIKEOUT; + // coverity[mixed_enums : FALSE] - unhelpfully warns different enum than LINESTYLE_SINGLE above + m_aDeletedAttr.m_nAttr = STRIKEOUT_SINGLE; + m_aDeletedAttr.m_nColor = COL_TRANSPARENT; + m_aFormatAttr.m_nItemId = SID_ATTR_CHAR_WEIGHT; + // coverity[mixed_enums : FALSE] - unhelpfully warns different enum than STRIKEOUT_SINGLE above + m_aFormatAttr.m_nAttr = WEIGHT_BOLD; + m_aFormatAttr.m_nColor = COL_BLACK; + Load(); +} + +SwRevisionConfig::~SwRevisionConfig() +{ +} + +static sal_Int32 lcl_ConvertAttrToCfg(const AuthorCharAttr& rAttr) +{ + sal_Int32 nRet = 0; + switch(rAttr.m_nItemId) + { + case SID_ATTR_CHAR_WEIGHT: nRet = 1; break; + case SID_ATTR_CHAR_POSTURE: nRet = 2; break; + case SID_ATTR_CHAR_UNDERLINE: nRet = LINESTYLE_SINGLE == rAttr.m_nAttr ? 3 : 4; break; + case SID_ATTR_CHAR_STRIKEOUT: nRet = 3; break; + case SID_ATTR_CHAR_CASEMAP: + { + switch(static_cast<SvxCaseMap>(rAttr.m_nAttr)) + { + case SvxCaseMap::Uppercase : nRet = 5;break; + case SvxCaseMap::Lowercase : nRet = 6;break; + case SvxCaseMap::SmallCaps : nRet = 7;break; + case SvxCaseMap::Capitalize: nRet = 8;break; + default: break; + } + } + break; + case SID_ATTR_BRUSH : nRet = 9; break; + } + return nRet; +} + +void SwRevisionConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwRevisionConfig::ImplCommit() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0 : pValues[nProp] <<= lcl_ConvertAttrToCfg(m_aInsertAttr); break; + case 1 : pValues[nProp] <<= m_aInsertAttr.m_nColor; break; + case 2 : pValues[nProp] <<= lcl_ConvertAttrToCfg(m_aDeletedAttr); break; + case 3 : pValues[nProp] <<= m_aDeletedAttr.m_nColor; break; + case 4 : pValues[nProp] <<= lcl_ConvertAttrToCfg(m_aFormatAttr); break; + case 5 : pValues[nProp] <<= m_aFormatAttr.m_nColor; break; + case 6 : pValues[nProp] <<= m_nMarkAlign; break; + case 7 : pValues[nProp] <<= m_aMarkColor; break; + } + } + PutProperties(aNames, aValues); +} + +static void lcl_ConvertCfgToAttr(sal_Int32 nVal, AuthorCharAttr& rAttr, bool bDelete = false) +{ + rAttr.m_nItemId = rAttr.m_nAttr = 0; + switch(nVal) + { + case 1: rAttr.m_nItemId = SID_ATTR_CHAR_WEIGHT; rAttr.m_nAttr = WEIGHT_BOLD ; break; + case 2: rAttr.m_nItemId = SID_ATTR_CHAR_POSTURE; rAttr.m_nAttr = ITALIC_NORMAL ; break; + case 3: if(bDelete) + { + rAttr.m_nItemId = SID_ATTR_CHAR_STRIKEOUT; + rAttr.m_nAttr = STRIKEOUT_SINGLE; + } + else + { + rAttr.m_nItemId = SID_ATTR_CHAR_UNDERLINE; + rAttr.m_nAttr = LINESTYLE_SINGLE; + } + break; + case 4: rAttr.m_nItemId = SID_ATTR_CHAR_UNDERLINE;rAttr.m_nAttr = LINESTYLE_DOUBLE ; break; + case 5: rAttr.m_nItemId = SID_ATTR_CHAR_CASEMAP; rAttr.m_nAttr = sal_uInt16(SvxCaseMap::Uppercase); break; + case 6: rAttr.m_nItemId = SID_ATTR_CHAR_CASEMAP; rAttr.m_nAttr = sal_uInt16(SvxCaseMap::Lowercase); break; + case 7: rAttr.m_nItemId = SID_ATTR_CHAR_CASEMAP; rAttr.m_nAttr = sal_uInt16(SvxCaseMap::SmallCaps); break; + case 8: rAttr.m_nItemId = SID_ATTR_CHAR_CASEMAP; rAttr.m_nAttr = sal_uInt16(SvxCaseMap::Capitalize); break; + case 9: rAttr.m_nItemId = SID_ATTR_BRUSH; break; + } +} +void SwRevisionConfig::Load() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == aNames.getLength()); + for (sal_Int32 nProp = 0; nProp < aNames.getLength(); ++nProp) + { + if (pValues[nProp].hasValue()) + { + sal_Int32 nVal = 0; + pValues[nProp] >>= nVal; + switch (nProp) + { + case 0 : lcl_ConvertCfgToAttr(nVal, m_aInsertAttr); break; + case 1 : m_aInsertAttr.m_nColor = Color(nVal); break; + case 2 : lcl_ConvertCfgToAttr(nVal, m_aDeletedAttr, true); break; + case 3 : m_aDeletedAttr.m_nColor = Color(nVal); break; + case 4 : lcl_ConvertCfgToAttr(nVal, m_aFormatAttr); break; + case 5 : m_aFormatAttr.m_nColor = Color(nVal); break; + case 6 : m_nMarkAlign = sal::static_int_cast< sal_uInt16, sal_Int32>(nVal); break; + case 7 : m_aMarkColor = Color(nVal); break; + } + } + } +} + +namespace { + +enum InsertConfigProp +{ + INS_PROP_TABLE_HEADER = 0, + INS_PROP_TABLE_REPEATHEADER, // 1 + INS_PROP_TABLE_BORDER, // 2 + INS_PROP_TABLE_SPLIT, // 3 from here not in writer/web + INS_PROP_CAP_AUTOMATIC, // 4 + INS_PROP_CAP_CAPTIONORDERNUMBERINGFIRST, // 5 + INS_PROP_CAP_OBJECT_TABLE_ENABLE, // 6 + INS_PROP_CAP_OBJECT_TABLE_CATEGORY, // 7 + INS_PROP_CAP_OBJECT_TABLE_NUMBERING, // 8 + INS_PROP_CAP_OBJECT_TABLE_NUMBERINGSEPARATOR, // 9 + INS_PROP_CAP_OBJECT_TABLE_CAPTIONTEXT, //10 + INS_PROP_CAP_OBJECT_TABLE_DELIMITER, //11 + INS_PROP_CAP_OBJECT_TABLE_LEVEL, //12 + INS_PROP_CAP_OBJECT_TABLE_POSITION, //13 + INS_PROP_CAP_OBJECT_TABLE_CHARACTERSTYLE, //14 + INS_PROP_CAP_OBJECT_FRAME_ENABLE, //15 + INS_PROP_CAP_OBJECT_FRAME_CATEGORY, //16 + INS_PROP_CAP_OBJECT_FRAME_NUMBERING, //17 + INS_PROP_CAP_OBJECT_FRAME_NUMBERINGSEPARATOR, //18 + INS_PROP_CAP_OBJECT_FRAME_CAPTIONTEXT, //19 + INS_PROP_CAP_OBJECT_FRAME_DELIMITER, //20 + INS_PROP_CAP_OBJECT_FRAME_LEVEL, //21 + INS_PROP_CAP_OBJECT_FRAME_POSITION, //22 + INS_PROP_CAP_OBJECT_FRAME_CHARACTERSTYLE, //23 + INS_PROP_CAP_OBJECT_GRAPHIC_ENABLE, //24 + INS_PROP_CAP_OBJECT_GRAPHIC_CATEGORY, //25 + INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERING, //26 + INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERINGSEPARATOR, //27 + INS_PROP_CAP_OBJECT_GRAPHIC_CAPTIONTEXT, //28 + INS_PROP_CAP_OBJECT_GRAPHIC_DELIMITER, //29 + INS_PROP_CAP_OBJECT_GRAPHIC_LEVEL, //30 + INS_PROP_CAP_OBJECT_GRAPHIC_POSITION, //31 + INS_PROP_CAP_OBJECT_GRAPHIC_CHARACTERSTYLE, //32 + INS_PROP_CAP_OBJECT_GRAPHIC_APPLYATTRIBUTES, //33 + INS_PROP_CAP_OBJECT_CALC_ENABLE, //34 + INS_PROP_CAP_OBJECT_CALC_CATEGORY, //35 + INS_PROP_CAP_OBJECT_CALC_NUMBERING, //36 + INS_PROP_CAP_OBJECT_CALC_NUMBERINGSEPARATOR, //37 + INS_PROP_CAP_OBJECT_CALC_CAPTIONTEXT, //38 + INS_PROP_CAP_OBJECT_CALC_DELIMITER, //39 + INS_PROP_CAP_OBJECT_CALC_LEVEL, //40 + INS_PROP_CAP_OBJECT_CALC_POSITION, //41 + INS_PROP_CAP_OBJECT_CALC_CHARACTERSTYLE, //42 + INS_PROP_CAP_OBJECT_CALC_APPLYATTRIBUTES, //43 + INS_PROP_CAP_OBJECT_IMPRESS_ENABLE, //44 + INS_PROP_CAP_OBJECT_IMPRESS_CATEGORY, //45 + INS_PROP_CAP_OBJECT_IMPRESS_NUMBERING, //46 + INS_PROP_CAP_OBJECT_IMPRESS_NUMBERINGSEPARATOR, //47 + INS_PROP_CAP_OBJECT_IMPRESS_CAPTIONTEXT, //48 + INS_PROP_CAP_OBJECT_IMPRESS_DELIMITER, //49 + INS_PROP_CAP_OBJECT_IMPRESS_LEVEL, //50 + INS_PROP_CAP_OBJECT_IMPRESS_POSITION, //51 + INS_PROP_CAP_OBJECT_IMPRESS_CHARACTERSTYLE, //52 + INS_PROP_CAP_OBJECT_IMPRESS_APPLYATTRIBUTES, //53 + INS_PROP_CAP_OBJECT_CHART_ENABLE, //54 + INS_PROP_CAP_OBJECT_CHART_CATEGORY, //55 + INS_PROP_CAP_OBJECT_CHART_NUMBERING, //56 + INS_PROP_CAP_OBJECT_CHART_NUMBERINGSEPARATOR, //57 + INS_PROP_CAP_OBJECT_CHART_CAPTIONTEXT, //58 + INS_PROP_CAP_OBJECT_CHART_DELIMITER, //59 + INS_PROP_CAP_OBJECT_CHART_LEVEL, //60 + INS_PROP_CAP_OBJECT_CHART_POSITION, //61 + INS_PROP_CAP_OBJECT_CHART_CHARACTERSTYLE, //62 + INS_PROP_CAP_OBJECT_CHART_APPLYATTRIBUTES, //63 + INS_PROP_CAP_OBJECT_FORMULA_ENABLE, //64 + INS_PROP_CAP_OBJECT_FORMULA_CATEGORY, //65 + INS_PROP_CAP_OBJECT_FORMULA_NUMBERING, //66 + INS_PROP_CAP_OBJECT_FORMULA_NUMBERINGSEPARATOR, //67 + INS_PROP_CAP_OBJECT_FORMULA_CAPTIONTEXT, //68 + INS_PROP_CAP_OBJECT_FORMULA_DELIMITER, //69 + INS_PROP_CAP_OBJECT_FORMULA_LEVEL, //70 + INS_PROP_CAP_OBJECT_FORMULA_POSITION, //71 + INS_PROP_CAP_OBJECT_FORMULA_CHARACTERSTYLE, //72 + INS_PROP_CAP_OBJECT_FORMULA_APPLYATTRIBUTES, //73 + INS_PROP_CAP_OBJECT_DRAW_ENABLE, //74 + INS_PROP_CAP_OBJECT_DRAW_CATEGORY, //75 + INS_PROP_CAP_OBJECT_DRAW_NUMBERING, //76 + INS_PROP_CAP_OBJECT_DRAW_NUMBERINGSEPARATOR, //77 + INS_PROP_CAP_OBJECT_DRAW_CAPTIONTEXT, //78 + INS_PROP_CAP_OBJECT_DRAW_DELIMITER, //79 + INS_PROP_CAP_OBJECT_DRAW_LEVEL, //80 + INS_PROP_CAP_OBJECT_DRAW_POSITION, //81 + INS_PROP_CAP_OBJECT_DRAW_CHARACTERSTYLE, //82 + INS_PROP_CAP_OBJECT_DRAW_APPLYATTRIBUTES, //83 + INS_PROP_CAP_OBJECT_OLEMISC_ENABLE, //84 + INS_PROP_CAP_OBJECT_OLEMISC_CATEGORY, //85 + INS_PROP_CAP_OBJECT_OLEMISC_NUMBERING, //86 + INS_PROP_CAP_OBJECT_OLEMISC_NUMBERINGSEPARATOR, //87 + INS_PROP_CAP_OBJECT_OLEMISC_CAPTIONTEXT, //88 + INS_PROP_CAP_OBJECT_OLEMISC_DELIMITER, //89 + INS_PROP_CAP_OBJECT_OLEMISC_LEVEL, //90 + INS_PROP_CAP_OBJECT_OLEMISC_POSITION, //91 + INS_PROP_CAP_OBJECT_OLEMISC_CHARACTERSTYLE, //92 + INS_PROP_CAP_OBJECT_OLEMISC_APPLYATTRIBUTES //93 +}; + +} + +const Sequence<OUString>& SwInsertConfig::GetPropertyNames() const +{ + static Sequence<OUString> aNames + { + "Table/Header", // 0 + "Table/RepeatHeader", // 1 + "Table/Border", // 2 + "Table/Split", // 3 from here not in writer/web + "Caption/Automatic", // 4 + "Caption/CaptionOrderNumberingFirst", // 5 + "Caption/WriterObject/Table/Enable", // 6 + "Caption/WriterObject/Table/Settings/Category", // 7 + "Caption/WriterObject/Table/Settings/Numbering", // 8 + "Caption/WriterObject/Table/Settings/NumberingSeparator", // 9 + "Caption/WriterObject/Table/Settings/CaptionText", //10 + "Caption/WriterObject/Table/Settings/Delimiter", //11 + "Caption/WriterObject/Table/Settings/Level", //12 + "Caption/WriterObject/Table/Settings/Position", //13 + "Caption/WriterObject/Table/Settings/CharacterStyle", //14 + "Caption/WriterObject/Frame/Enable", //15 + "Caption/WriterObject/Frame/Settings/Category", //16 + "Caption/WriterObject/Frame/Settings/Numbering", //17 + "Caption/WriterObject/Frame/Settings/NumberingSeparator", //18 + "Caption/WriterObject/Frame/Settings/CaptionText", //19 + "Caption/WriterObject/Frame/Settings/Delimiter", //20 + "Caption/WriterObject/Frame/Settings/Level", //21 + "Caption/WriterObject/Frame/Settings/Position", //22 + "Caption/WriterObject/Frame/Settings/CharacterStyle", //23 + "Caption/WriterObject/Graphic/Enable", //24 + "Caption/WriterObject/Graphic/Settings/Category", //25 + "Caption/WriterObject/Graphic/Settings/Numbering", //26 + "Caption/WriterObject/Graphic/Settings/NumberingSeparator", //27 + "Caption/WriterObject/Graphic/Settings/CaptionText", //28 + "Caption/WriterObject/Graphic/Settings/Delimiter", //29 + "Caption/WriterObject/Graphic/Settings/Level", //30 + "Caption/WriterObject/Graphic/Settings/Position", //31 + "Caption/WriterObject/Graphic/Settings/CharacterStyle", //32 + "Caption/WriterObject/Graphic/Settings/ApplyAttributes", //33 + "Caption/OfficeObject/Calc/Enable", //34 + "Caption/OfficeObject/Calc/Settings/Category", //35 + "Caption/OfficeObject/Calc/Settings/Numbering", //36 + "Caption/OfficeObject/Calc/Settings/NumberingSeparator", //37 + "Caption/OfficeObject/Calc/Settings/CaptionText", //38 + "Caption/OfficeObject/Calc/Settings/Delimiter", //39 + "Caption/OfficeObject/Calc/Settings/Level", //40 + "Caption/OfficeObject/Calc/Settings/Position", //41 + "Caption/OfficeObject/Calc/Settings/CharacterStyle", //42 + "Caption/OfficeObject/Calc/Settings/ApplyAttributes", //43 + "Caption/OfficeObject/Impress/Enable", //44 + "Caption/OfficeObject/Impress/Settings/Category", //45 + "Caption/OfficeObject/Impress/Settings/Numbering", //46 + "Caption/OfficeObject/Impress/Settings/NumberingSeparator", //47 + "Caption/OfficeObject/Impress/Settings/CaptionText", //48 + "Caption/OfficeObject/Impress/Settings/Delimiter", //49 + "Caption/OfficeObject/Impress/Settings/Level", //50 + "Caption/OfficeObject/Impress/Settings/Position", //51 + "Caption/OfficeObject/Impress/Settings/CharacterStyle", //52 + "Caption/OfficeObject/Impress/Settings/ApplyAttributes", //53 + "Caption/OfficeObject/Chart/Enable", //54 + "Caption/OfficeObject/Chart/Settings/Category", //55 + "Caption/OfficeObject/Chart/Settings/Numbering", //56 + "Caption/OfficeObject/Chart/Settings/NumberingSeparator", //57 + "Caption/OfficeObject/Chart/Settings/CaptionText", //58 + "Caption/OfficeObject/Chart/Settings/Delimiter", //59 + "Caption/OfficeObject/Chart/Settings/Level", //60 + "Caption/OfficeObject/Chart/Settings/Position", //61 + "Caption/OfficeObject/Chart/Settings/CharacterStyle", //62 + "Caption/OfficeObject/Chart/Settings/ApplyAttributes", //63 + "Caption/OfficeObject/Formula/Enable", //64 + "Caption/OfficeObject/Formula/Settings/Category", //65 + "Caption/OfficeObject/Formula/Settings/Numbering", //66 + "Caption/OfficeObject/Formula/Settings/NumberingSeparator", //67 + "Caption/OfficeObject/Formula/Settings/CaptionText", //68 + "Caption/OfficeObject/Formula/Settings/Delimiter", //69 + "Caption/OfficeObject/Formula/Settings/Level", //70 + "Caption/OfficeObject/Formula/Settings/Position", //71 + "Caption/OfficeObject/Formula/Settings/CharacterStyle", //72 + "Caption/OfficeObject/Formula/Settings/ApplyAttributes", //73 + "Caption/OfficeObject/Draw/Enable", //74 + "Caption/OfficeObject/Draw/Settings/Category", //75 + "Caption/OfficeObject/Draw/Settings/Numbering", //76 + "Caption/OfficeObject/Draw/Settings/NumberingSeparator", //77 + "Caption/OfficeObject/Draw/Settings/CaptionText", //78 + "Caption/OfficeObject/Draw/Settings/Delimiter", //79 + "Caption/OfficeObject/Draw/Settings/Level", //80 + "Caption/OfficeObject/Draw/Settings/Position", //81 + "Caption/OfficeObject/Draw/Settings/CharacterStyle", //82 + "Caption/OfficeObject/Draw/Settings/ApplyAttributes", //83 + "Caption/OfficeObject/OLEMisc/Enable", //84 + "Caption/OfficeObject/OLEMisc/Settings/Category", //85 + "Caption/OfficeObject/OLEMisc/Settings/Numbering", //86 + "Caption/OfficeObject/OLEMisc/Settings/NumberingSeparator", //87 + "Caption/OfficeObject/OLEMisc/Settings/CaptionText", //88 + "Caption/OfficeObject/OLEMisc/Settings/Delimiter", //89 + "Caption/OfficeObject/OLEMisc/Settings/Level", //90 + "Caption/OfficeObject/OLEMisc/Settings/Position", //91 + "Caption/OfficeObject/OLEMisc/Settings/CharacterStyle", //92 + "Caption/OfficeObject/OLEMisc/Settings/ApplyAttributes" //93 + }; + static Sequence<OUString> const aWebNames(aNames.getArray(), INS_PROP_TABLE_BORDER + 1); + return m_bIsWeb ? aWebNames : aNames; +} + +SwInsertConfig::SwInsertConfig(bool bWeb) : + ConfigItem(bWeb ? OUString("Office.WriterWeb/Insert") : OUString("Office.Writer/Insert"), + ConfigItemMode::ReleaseTree), + m_bInsWithCaption( false ), + m_bCaptionOrderNumberingFirst( false ), + m_aInsTableOpts(SwInsertTableFlags::NONE,0), + m_bIsWeb(bWeb) +{ + m_aGlobalNames[GLOB_NAME_CALC ] = SvGlobalName(SO3_SC_CLASSID); + m_aGlobalNames[GLOB_NAME_IMPRESS] = SvGlobalName(SO3_SIMPRESS_CLASSID); + m_aGlobalNames[GLOB_NAME_DRAW ] = SvGlobalName(SO3_SDRAW_CLASSID); + m_aGlobalNames[GLOB_NAME_MATH ] = SvGlobalName(SO3_SM_CLASSID); + m_aGlobalNames[GLOB_NAME_CHART ] = SvGlobalName(SO3_SCH_CLASSID); + if(!m_bIsWeb) + m_pCapOptions.reset(new InsCaptionOptArr); + + Load(); +} + +SwInsertConfig::~SwInsertConfig() +{ + m_pCapOptions.reset(); + m_pOLEMiscOpt.reset(); +} + +static void lcl_WriteOpt(const InsCaptionOpt& rOpt, Any* pValues, sal_Int32 nProp, sal_Int32 nOffset) +{ + switch(nOffset) + { + case 0: pValues[nProp] <<= rOpt.UseCaption(); break;//Enable + case 1: pValues[nProp] <<= rOpt.GetCategory(); break;//Category + case 2: pValues[nProp] <<= static_cast<sal_Int32>(rOpt.GetNumType()); break;//Numbering", + case 3: pValues[nProp] <<= rOpt.GetNumSeparator(); break;//NumberingSeparator", + case 4: pValues[nProp] <<= rOpt.GetCaption(); break;//CaptionText", + case 5: pValues[nProp] <<= rOpt.GetSeparator();break;//Delimiter", + case 6: pValues[nProp] <<= static_cast<sal_Int32>(rOpt.GetLevel()); break;//Level", + case 7: pValues[nProp] <<= static_cast<sal_Int32>(rOpt.GetPos()); break;//Position", + case 8: pValues[nProp] <<= rOpt.GetCharacterStyle(); break; //CharacterStyle + case 9: pValues[nProp] <<= rOpt.CopyAttributes(); break; //ApplyAttributes + } +} + +void SwInsertConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwInsertConfig::ImplCommit() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + const InsCaptionOpt* pWriterTableOpt = nullptr; + const InsCaptionOpt* pWriterFrameOpt = nullptr; + const InsCaptionOpt* pWriterGraphicOpt = nullptr; + const InsCaptionOpt* pOLECalcOpt = nullptr; + const InsCaptionOpt* pOLEImpressOpt = nullptr; + const InsCaptionOpt* pOLEChartOpt = nullptr; + const InsCaptionOpt* pOLEFormulaOpt = nullptr; + const InsCaptionOpt* pOLEDrawOpt = nullptr; + if(m_pCapOptions) + { + pWriterTableOpt = m_pCapOptions->Find(TABLE_CAP); + pWriterFrameOpt = m_pCapOptions->Find(FRAME_CAP); + pWriterGraphicOpt = m_pCapOptions->Find(GRAPHIC_CAP); + pOLECalcOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CALC]); + pOLEImpressOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_IMPRESS]); + pOLEDrawOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_DRAW ]); + pOLEFormulaOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_MATH ]); + pOLEChartOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CHART ]); + } + switch(nProp) + { + case INS_PROP_TABLE_HEADER: + pValues[nProp] <<= bool(m_aInsTableOpts.mnInsMode & SwInsertTableFlags::Headline); + break;//"Table/Header", + case INS_PROP_TABLE_REPEATHEADER: + pValues[nProp] <<= m_aInsTableOpts.mnRowsToRepeat > 0; + break;//"Table/RepeatHeader", + case INS_PROP_TABLE_BORDER: + pValues[nProp] <<= bool(m_aInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder); + break;//"Table/Border", + case INS_PROP_TABLE_SPLIT: + pValues[nProp] <<= bool(m_aInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout); + break;//"Table/Split", + case INS_PROP_CAP_AUTOMATIC: + pValues[nProp] <<= m_bInsWithCaption; + break;//"Caption/Automatic", + case INS_PROP_CAP_CAPTIONORDERNUMBERINGFIRST: + pValues[nProp] <<= m_bCaptionOrderNumberingFirst; + break;//"Caption/CaptionOrderNumberingFirst" + + case INS_PROP_CAP_OBJECT_TABLE_ENABLE: + case INS_PROP_CAP_OBJECT_TABLE_CATEGORY: + case INS_PROP_CAP_OBJECT_TABLE_NUMBERING: + case INS_PROP_CAP_OBJECT_TABLE_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_TABLE_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_TABLE_DELIMITER: + case INS_PROP_CAP_OBJECT_TABLE_LEVEL: + case INS_PROP_CAP_OBJECT_TABLE_POSITION: + case INS_PROP_CAP_OBJECT_TABLE_CHARACTERSTYLE: + if(pWriterTableOpt) + lcl_WriteOpt(*pWriterTableOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_TABLE_ENABLE); + break; + case INS_PROP_CAP_OBJECT_FRAME_ENABLE: + case INS_PROP_CAP_OBJECT_FRAME_CATEGORY: + case INS_PROP_CAP_OBJECT_FRAME_NUMBERING: + case INS_PROP_CAP_OBJECT_FRAME_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_FRAME_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_FRAME_DELIMITER: + case INS_PROP_CAP_OBJECT_FRAME_LEVEL: + case INS_PROP_CAP_OBJECT_FRAME_POSITION: + case INS_PROP_CAP_OBJECT_FRAME_CHARACTERSTYLE: + if(pWriterFrameOpt) + lcl_WriteOpt(*pWriterFrameOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_FRAME_ENABLE); + break; + case INS_PROP_CAP_OBJECT_GRAPHIC_ENABLE: + case INS_PROP_CAP_OBJECT_GRAPHIC_CATEGORY: + case INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERING: + case INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_GRAPHIC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_GRAPHIC_DELIMITER: + case INS_PROP_CAP_OBJECT_GRAPHIC_LEVEL: + case INS_PROP_CAP_OBJECT_GRAPHIC_POSITION: + case INS_PROP_CAP_OBJECT_GRAPHIC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_GRAPHIC_APPLYATTRIBUTES: + if(pWriterGraphicOpt) + lcl_WriteOpt(*pWriterGraphicOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_GRAPHIC_ENABLE); + break; + case INS_PROP_CAP_OBJECT_CALC_ENABLE: + case INS_PROP_CAP_OBJECT_CALC_CATEGORY: + case INS_PROP_CAP_OBJECT_CALC_NUMBERING: + case INS_PROP_CAP_OBJECT_CALC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_CALC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_CALC_DELIMITER: + case INS_PROP_CAP_OBJECT_CALC_LEVEL: + case INS_PROP_CAP_OBJECT_CALC_POSITION: + case INS_PROP_CAP_OBJECT_CALC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_CALC_APPLYATTRIBUTES: + if(pOLECalcOpt) + lcl_WriteOpt(*pOLECalcOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_CALC_ENABLE); + break; + case INS_PROP_CAP_OBJECT_IMPRESS_ENABLE: + case INS_PROP_CAP_OBJECT_IMPRESS_CATEGORY: + case INS_PROP_CAP_OBJECT_IMPRESS_NUMBERING: + case INS_PROP_CAP_OBJECT_IMPRESS_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_IMPRESS_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_IMPRESS_DELIMITER: + case INS_PROP_CAP_OBJECT_IMPRESS_LEVEL: + case INS_PROP_CAP_OBJECT_IMPRESS_POSITION: + case INS_PROP_CAP_OBJECT_IMPRESS_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_IMPRESS_APPLYATTRIBUTES: + if(pOLEImpressOpt) + lcl_WriteOpt(*pOLEImpressOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_IMPRESS_ENABLE); + break; + case INS_PROP_CAP_OBJECT_CHART_ENABLE: + case INS_PROP_CAP_OBJECT_CHART_CATEGORY: + case INS_PROP_CAP_OBJECT_CHART_NUMBERING: + case INS_PROP_CAP_OBJECT_CHART_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_CHART_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_CHART_DELIMITER: + case INS_PROP_CAP_OBJECT_CHART_LEVEL: + case INS_PROP_CAP_OBJECT_CHART_POSITION: + case INS_PROP_CAP_OBJECT_CHART_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_CHART_APPLYATTRIBUTES: + if(pOLEChartOpt) + lcl_WriteOpt(*pOLEChartOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_CHART_ENABLE); + break; + case INS_PROP_CAP_OBJECT_FORMULA_ENABLE: + case INS_PROP_CAP_OBJECT_FORMULA_CATEGORY: + case INS_PROP_CAP_OBJECT_FORMULA_NUMBERING: + case INS_PROP_CAP_OBJECT_FORMULA_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_FORMULA_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_FORMULA_DELIMITER: + case INS_PROP_CAP_OBJECT_FORMULA_LEVEL: + case INS_PROP_CAP_OBJECT_FORMULA_POSITION: + case INS_PROP_CAP_OBJECT_FORMULA_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_FORMULA_APPLYATTRIBUTES: + if(pOLEFormulaOpt) + lcl_WriteOpt(*pOLEFormulaOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_FORMULA_ENABLE); + break; + case INS_PROP_CAP_OBJECT_DRAW_ENABLE: + case INS_PROP_CAP_OBJECT_DRAW_CATEGORY: + case INS_PROP_CAP_OBJECT_DRAW_NUMBERING: + case INS_PROP_CAP_OBJECT_DRAW_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_DRAW_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_DRAW_DELIMITER: + case INS_PROP_CAP_OBJECT_DRAW_LEVEL: + case INS_PROP_CAP_OBJECT_DRAW_POSITION: + case INS_PROP_CAP_OBJECT_DRAW_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_DRAW_APPLYATTRIBUTES: + if(pOLEDrawOpt) + lcl_WriteOpt(*pOLEDrawOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_DRAW_ENABLE); + break; + case INS_PROP_CAP_OBJECT_OLEMISC_ENABLE: + case INS_PROP_CAP_OBJECT_OLEMISC_CATEGORY: + case INS_PROP_CAP_OBJECT_OLEMISC_NUMBERING: + case INS_PROP_CAP_OBJECT_OLEMISC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_OLEMISC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_OLEMISC_DELIMITER: + case INS_PROP_CAP_OBJECT_OLEMISC_LEVEL: + case INS_PROP_CAP_OBJECT_OLEMISC_POSITION: + case INS_PROP_CAP_OBJECT_OLEMISC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_OLEMISC_APPLYATTRIBUTES: + if(m_pOLEMiscOpt) + lcl_WriteOpt(*m_pOLEMiscOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_OLEMISC_ENABLE); + break; + + } + } + PutProperties(aNames, aValues); +} + +static void lcl_ReadOpt(InsCaptionOpt& rOpt, const Any* pValues, sal_Int32 nProp, sal_Int32 nOffset) +{ + switch(nOffset) + { + case 0: + rOpt.UseCaption() = *o3tl::doAccess<bool>(pValues[nProp]); + break;//Enable + case 1: + { + OUString sTemp; pValues[nProp] >>= sTemp; + rOpt.SetCategory(sTemp); + } + break;//Category + case 2: + { + sal_Int32 nTemp = 0; + pValues[nProp] >>= nTemp; + rOpt.SetNumType(sal::static_int_cast< sal_uInt16, sal_Int32>(nTemp)); + } + break;//Numbering", + case 3: + { + OUString sTemp; pValues[nProp] >>= sTemp; + rOpt.SetNumSeparator(sTemp); + } + break;//NumberingSeparator", + case 4: + { + OUString sTemp; pValues[nProp] >>= sTemp; + rOpt.SetCaption(sTemp); + } + break;//CaptionText", + case 5: + { + OUString sTemp; + if(pValues[nProp] >>= sTemp) + rOpt.SetSeparator(sTemp); + } + break;//Delimiter", + case 6: + { + sal_Int32 nTemp = 0; + pValues[nProp] >>= nTemp; + rOpt.SetLevel(sal::static_int_cast< sal_uInt16, sal_Int32>(nTemp)); + } + break;//Level", + case 7: + { + sal_Int32 nTemp = 0; + pValues[nProp] >>= nTemp; + rOpt.SetPos(sal::static_int_cast< sal_uInt16, sal_Int32>(nTemp)); + } + break;//Position", + case 8 : //CharacterStyle + { + OUString sTemp; pValues[nProp] >>= sTemp; + rOpt.SetCharacterStyle( sTemp ); + } + break; + case 9 : //ApplyAttributes + { + pValues[nProp] >>= rOpt.CopyAttributes(); + } + break; + } +} + +void SwInsertConfig::Load() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == aNames.getLength()); + InsCaptionOpt* pWriterTableOpt = nullptr; + InsCaptionOpt* pWriterFrameOpt = nullptr; + InsCaptionOpt* pWriterGraphicOpt = nullptr; + InsCaptionOpt* pOLECalcOpt = nullptr; + InsCaptionOpt* pOLEImpressOpt = nullptr; + InsCaptionOpt* pOLEChartOpt = nullptr; + InsCaptionOpt* pOLEFormulaOpt = nullptr; + InsCaptionOpt* pOLEDrawOpt = nullptr; + if (m_pCapOptions) + { + pWriterTableOpt = m_pCapOptions->Find(TABLE_CAP); + pWriterFrameOpt = m_pCapOptions->Find(FRAME_CAP); + pWriterGraphicOpt = m_pCapOptions->Find(GRAPHIC_CAP); + pOLECalcOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CALC]); + pOLEImpressOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_IMPRESS]); + pOLEDrawOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_DRAW ]); + pOLEFormulaOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_MATH ]); + pOLEChartOpt = m_pCapOptions->Find(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CHART ]); + } + else if (!m_bIsWeb) + return; + + SwInsertTableFlags nInsTableFlags = SwInsertTableFlags::NONE; + for (sal_Int32 nProp = 0; nProp < aNames.getLength(); ++nProp) + { + if (pValues[nProp].hasValue()) + { + bool bBool = nProp < INS_PROP_CAP_OBJECT_TABLE_ENABLE && *o3tl::doAccess<bool>(pValues[nProp]); + switch (nProp) + { + case INS_PROP_TABLE_HEADER: + { + if(bBool) + nInsTableFlags |= SwInsertTableFlags::Headline; + } + break;//"Table/Header", + case INS_PROP_TABLE_REPEATHEADER: + { + m_aInsTableOpts.mnRowsToRepeat = bBool? 1 : 0; + + } + break;//"Table/RepeatHeader", + case INS_PROP_TABLE_BORDER: + { + if(bBool) + nInsTableFlags |= SwInsertTableFlags::DefaultBorder; + } + break;//"Table/Border", + case INS_PROP_TABLE_SPLIT: + { + if(bBool) + nInsTableFlags |= SwInsertTableFlags::SplitLayout; + } + break;//"Table/Split", + case INS_PROP_CAP_AUTOMATIC: + m_bInsWithCaption = bBool; + break; + case INS_PROP_CAP_CAPTIONORDERNUMBERINGFIRST: m_bCaptionOrderNumberingFirst = bBool; break; + case INS_PROP_CAP_OBJECT_TABLE_ENABLE: + case INS_PROP_CAP_OBJECT_TABLE_CATEGORY: + case INS_PROP_CAP_OBJECT_TABLE_NUMBERING: + case INS_PROP_CAP_OBJECT_TABLE_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_TABLE_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_TABLE_DELIMITER: + case INS_PROP_CAP_OBJECT_TABLE_LEVEL: + case INS_PROP_CAP_OBJECT_TABLE_POSITION: + case INS_PROP_CAP_OBJECT_TABLE_CHARACTERSTYLE: + if(!pWriterTableOpt) + { + pWriterTableOpt = new InsCaptionOpt(TABLE_CAP); + m_pCapOptions->Insert(pWriterTableOpt); + } + lcl_ReadOpt(*pWriterTableOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_TABLE_ENABLE); + break; + case INS_PROP_CAP_OBJECT_FRAME_ENABLE: + case INS_PROP_CAP_OBJECT_FRAME_CATEGORY: + case INS_PROP_CAP_OBJECT_FRAME_NUMBERING: + case INS_PROP_CAP_OBJECT_FRAME_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_FRAME_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_FRAME_DELIMITER: + case INS_PROP_CAP_OBJECT_FRAME_LEVEL: + case INS_PROP_CAP_OBJECT_FRAME_POSITION: + case INS_PROP_CAP_OBJECT_FRAME_CHARACTERSTYLE: + if(!pWriterFrameOpt) + { + pWriterFrameOpt = new InsCaptionOpt(FRAME_CAP); + m_pCapOptions->Insert(pWriterFrameOpt); + } + lcl_ReadOpt(*pWriterFrameOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_FRAME_ENABLE); + break; + case INS_PROP_CAP_OBJECT_GRAPHIC_ENABLE: + case INS_PROP_CAP_OBJECT_GRAPHIC_CATEGORY: + case INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERING: + case INS_PROP_CAP_OBJECT_GRAPHIC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_GRAPHIC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_GRAPHIC_DELIMITER: + case INS_PROP_CAP_OBJECT_GRAPHIC_LEVEL: + case INS_PROP_CAP_OBJECT_GRAPHIC_POSITION: + case INS_PROP_CAP_OBJECT_GRAPHIC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_GRAPHIC_APPLYATTRIBUTES: + if(!pWriterGraphicOpt) + { + pWriterGraphicOpt = new InsCaptionOpt(GRAPHIC_CAP); + m_pCapOptions->Insert(pWriterGraphicOpt); + } + lcl_ReadOpt(*pWriterGraphicOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_GRAPHIC_ENABLE); + break; + case INS_PROP_CAP_OBJECT_CALC_ENABLE: + case INS_PROP_CAP_OBJECT_CALC_CATEGORY: + case INS_PROP_CAP_OBJECT_CALC_NUMBERING: + case INS_PROP_CAP_OBJECT_CALC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_CALC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_CALC_DELIMITER: + case INS_PROP_CAP_OBJECT_CALC_LEVEL: + case INS_PROP_CAP_OBJECT_CALC_POSITION: + case INS_PROP_CAP_OBJECT_CALC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_CALC_APPLYATTRIBUTES: + if(!pOLECalcOpt) + { + pOLECalcOpt = new InsCaptionOpt(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CALC]); + m_pCapOptions->Insert(pOLECalcOpt); + } + lcl_ReadOpt(*pOLECalcOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_CALC_ENABLE); + break; + case INS_PROP_CAP_OBJECT_IMPRESS_ENABLE: + case INS_PROP_CAP_OBJECT_IMPRESS_CATEGORY: + case INS_PROP_CAP_OBJECT_IMPRESS_NUMBERING: + case INS_PROP_CAP_OBJECT_IMPRESS_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_IMPRESS_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_IMPRESS_DELIMITER: + case INS_PROP_CAP_OBJECT_IMPRESS_LEVEL: + case INS_PROP_CAP_OBJECT_IMPRESS_POSITION: + case INS_PROP_CAP_OBJECT_IMPRESS_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_IMPRESS_APPLYATTRIBUTES: + if(!pOLEImpressOpt) + { + pOLEImpressOpt = new InsCaptionOpt(OLE_CAP, &m_aGlobalNames[GLOB_NAME_IMPRESS]); + m_pCapOptions->Insert(pOLEImpressOpt); + } + lcl_ReadOpt(*pOLEImpressOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_IMPRESS_ENABLE); + break; + case INS_PROP_CAP_OBJECT_CHART_ENABLE: + case INS_PROP_CAP_OBJECT_CHART_CATEGORY: + case INS_PROP_CAP_OBJECT_CHART_NUMBERING: + case INS_PROP_CAP_OBJECT_CHART_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_CHART_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_CHART_DELIMITER: + case INS_PROP_CAP_OBJECT_CHART_LEVEL: + case INS_PROP_CAP_OBJECT_CHART_POSITION: + case INS_PROP_CAP_OBJECT_CHART_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_CHART_APPLYATTRIBUTES: + if(!pOLEChartOpt) + { + pOLEChartOpt = new InsCaptionOpt(OLE_CAP, &m_aGlobalNames[GLOB_NAME_CHART]); + m_pCapOptions->Insert(pOLEChartOpt); + } + lcl_ReadOpt(*pOLEChartOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_CHART_ENABLE); + break; + case INS_PROP_CAP_OBJECT_FORMULA_ENABLE: + case INS_PROP_CAP_OBJECT_FORMULA_CATEGORY: + case INS_PROP_CAP_OBJECT_FORMULA_NUMBERING: + case INS_PROP_CAP_OBJECT_FORMULA_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_FORMULA_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_FORMULA_DELIMITER: + case INS_PROP_CAP_OBJECT_FORMULA_LEVEL: + case INS_PROP_CAP_OBJECT_FORMULA_POSITION: + case INS_PROP_CAP_OBJECT_FORMULA_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_FORMULA_APPLYATTRIBUTES: + if(!pOLEFormulaOpt) + { + pOLEFormulaOpt = new InsCaptionOpt(OLE_CAP, &m_aGlobalNames[GLOB_NAME_MATH]); + m_pCapOptions->Insert(pOLEFormulaOpt); + } + lcl_ReadOpt(*pOLEFormulaOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_FORMULA_ENABLE); + break; + case INS_PROP_CAP_OBJECT_DRAW_ENABLE: + case INS_PROP_CAP_OBJECT_DRAW_CATEGORY: + case INS_PROP_CAP_OBJECT_DRAW_NUMBERING: + case INS_PROP_CAP_OBJECT_DRAW_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_DRAW_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_DRAW_DELIMITER: + case INS_PROP_CAP_OBJECT_DRAW_LEVEL: + case INS_PROP_CAP_OBJECT_DRAW_POSITION: + case INS_PROP_CAP_OBJECT_DRAW_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_DRAW_APPLYATTRIBUTES: + if(!pOLEDrawOpt) + { + pOLEDrawOpt = new InsCaptionOpt(OLE_CAP, &m_aGlobalNames[GLOB_NAME_DRAW]); + m_pCapOptions->Insert(pOLEDrawOpt); + } + lcl_ReadOpt(*pOLEDrawOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_DRAW_ENABLE); + break; + case INS_PROP_CAP_OBJECT_OLEMISC_ENABLE: + case INS_PROP_CAP_OBJECT_OLEMISC_CATEGORY: + case INS_PROP_CAP_OBJECT_OLEMISC_NUMBERING: + case INS_PROP_CAP_OBJECT_OLEMISC_NUMBERINGSEPARATOR: + case INS_PROP_CAP_OBJECT_OLEMISC_CAPTIONTEXT: + case INS_PROP_CAP_OBJECT_OLEMISC_DELIMITER: + case INS_PROP_CAP_OBJECT_OLEMISC_LEVEL: + case INS_PROP_CAP_OBJECT_OLEMISC_POSITION: + case INS_PROP_CAP_OBJECT_OLEMISC_CHARACTERSTYLE: + case INS_PROP_CAP_OBJECT_OLEMISC_APPLYATTRIBUTES: + if(!m_pOLEMiscOpt) + { + m_pOLEMiscOpt.reset(new InsCaptionOpt(OLE_CAP)); + } + lcl_ReadOpt(*m_pOLEMiscOpt, pValues, nProp, nProp - INS_PROP_CAP_OBJECT_OLEMISC_ENABLE); + break; + } + } + else if (nProp == INS_PROP_CAP_CAPTIONORDERNUMBERINGFIRST) + { + m_bCaptionOrderNumberingFirst = false; + } + + } + m_aInsTableOpts.mnInsMode = nInsTableFlags; +} + +const Sequence<OUString>& SwTableConfig::GetPropertyNames() +{ + static Sequence<OUString> const aNames + { + "Shift/Row", // 0 + "Shift/Column", // 1 + "Insert/Row", // 2 + "Insert/Column", // 3 + "Change/Effect", // 4 + "Input/NumberRecognition", // 5 + "Input/NumberFormatRecognition",// 6 + "Input/Alignment", // 7 + "Input/SplitVerticalByDefault" // 8 + }; + return aNames; +} + +SwTableConfig::SwTableConfig(bool bWeb) + : ConfigItem(bWeb ? OUString("Office.WriterWeb/Table") : OUString("Office.Writer/Table"), + ConfigItemMode::ReleaseTree) + , m_nTableHMove(0) + , m_nTableVMove(0) + , m_nTableHInsert(0) + , m_nTableVInsert(0) + , m_eTableChgMode(TableChgMode::FixedWidthChangeAbs) + , m_bInsTableFormatNum(false) + , m_bInsTableChangeNumFormat(false) + , m_bInsTableAlignNum(false) + , m_bSplitVerticalByDefault(false) +{ + Load(); +} + +SwTableConfig::~SwTableConfig() +{ +} + +void SwTableConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwTableConfig::ImplCommit() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0 : pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(m_nTableHMove)); break; //"Shift/Row", + case 1 : pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(m_nTableVMove)); break; //"Shift/Column", + case 2 : pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(m_nTableHInsert)); break; //"Insert/Row", + case 3 : pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(m_nTableVInsert)); break; //"Insert/Column", + case 4 : pValues[nProp] <<= static_cast<sal_Int32>(m_eTableChgMode); break; //"Change/Effect", + case 5 : pValues[nProp] <<= m_bInsTableFormatNum; break; //"Input/NumberRecognition", + case 6 : pValues[nProp] <<= m_bInsTableChangeNumFormat; break; //"Input/NumberFormatRecognition", + case 7 : pValues[nProp] <<= m_bInsTableAlignNum; break; //"Input/Alignment" + case 8 : pValues[nProp] <<= m_bSplitVerticalByDefault; break; //"Input/SplitVerticalByDefault" + } + } + PutProperties(aNames, aValues); +} + +void SwTableConfig::Load() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == aNames.getLength()); + for (sal_Int32 nProp = 0; nProp < aNames.getLength(); ++nProp) + { + if (pValues[nProp].hasValue()) + { + sal_Int32 nTemp = 0; + switch (nProp) + { + case 0 : pValues[nProp] >>= nTemp; m_nTableHMove = static_cast<sal_uInt16>(convertMm100ToTwip(nTemp)); break; //"Shift/Row", + case 1 : pValues[nProp] >>= nTemp; m_nTableVMove = static_cast<sal_uInt16>(convertMm100ToTwip(nTemp)); break; //"Shift/Column", + case 2 : pValues[nProp] >>= nTemp; m_nTableHInsert = static_cast<sal_uInt16>(convertMm100ToTwip(nTemp)); break; //"Insert/Row", + case 3 : pValues[nProp] >>= nTemp; m_nTableVInsert = static_cast<sal_uInt16>(convertMm100ToTwip(nTemp)); break; //"Insert/Column", + case 4 : pValues[nProp] >>= nTemp; m_eTableChgMode = static_cast<TableChgMode>(nTemp); break; //"Change/Effect", + case 5 : m_bInsTableFormatNum = *o3tl::doAccess<bool>(pValues[nProp]); break; //"Input/NumberRecognition", + case 6 : m_bInsTableChangeNumFormat = *o3tl::doAccess<bool>(pValues[nProp]); break; //"Input/NumberFormatRecognition", + case 7 : m_bInsTableAlignNum = *o3tl::doAccess<bool>(pValues[nProp]); break; //"Input/Alignment" + case 8 : m_bSplitVerticalByDefault = *o3tl::doAccess<bool>(pValues[nProp]); break; //"Input/SplitVerticalByDefault" + } + } + } +} + +SwMiscConfig::SwMiscConfig() : + ConfigItem("Office.Writer", ConfigItemMode::ReleaseTree), + m_bDefaultFontsInCurrDocOnly(false), + m_bShowIndexPreview(false), + m_bGrfToGalleryAsLnk(true), + m_bNumAlignSize(true), + m_bSinglePrintJob(false), + m_bIsNameFromColumn(true), + m_bIsPasswordFromColumn(false), + m_bAskForMailMergeInPrint(true), + m_nMailingFormats(MailTextFormats::NONE) +{ + Load(); +} + +SwMiscConfig::~SwMiscConfig() +{ +} + +const Sequence<OUString>& SwMiscConfig::GetPropertyNames() +{ + static Sequence<OUString> const aNames + { + "Statistics/WordNumber/Delimiter", // 0 + "DefaultFont/Document", // 1 + "Index/ShowPreview", // 2 + "Misc/GraphicToGalleryAsLink", // 3 + "Numbering/Graphic/KeepRatio", // 4 + "FormLetter/PrintOutput/SinglePrintJobs", // 5 + "FormLetter/MailingOutput/Format", // 6 + "FormLetter/FileOutput/FileName/FromDatabaseField", // 7 + "FormLetter/FileOutput/Path", // 8 + "FormLetter/FileOutput/FileName/FromManualSetting", // 9 + "FormLetter/FileOutput/FileName/Generation",//10 + "FormLetter/PrintOutput/AskForMerge", //11 + "FormLetter/FileOutput/FilePassword/FromDatabaseField", // 12 + "FormLetter/FileOutput/FilePassword/Generation" //13 + }; + return aNames; +} + +void SwMiscConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwMiscConfig::ImplCommit() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0 : + pValues[nProp] <<= + SwModuleOptions::ConvertWordDelimiter(m_sWordDelimiter, false); + break; + case 1 : pValues[nProp] <<= m_bDefaultFontsInCurrDocOnly; break; + case 2 : pValues[nProp] <<= m_bShowIndexPreview; break; + case 3 : pValues[nProp] <<= m_bGrfToGalleryAsLnk; break; + case 4 : pValues[nProp] <<= m_bNumAlignSize; break; + case 5 : pValues[nProp] <<= m_bSinglePrintJob; break; + case 6 : pValues[nProp] <<= static_cast<sal_Int32>(m_nMailingFormats); break; + case 7 : pValues[nProp] <<= m_sNameFromColumn; break; + case 8 : pValues[nProp] <<= m_sMailingPath; break; + case 9 : pValues[nProp] <<= m_sMailName; break; + case 10: pValues[nProp] <<= m_bIsNameFromColumn; break; + case 11: pValues[nProp] <<= m_bAskForMailMergeInPrint; break; + case 12: pValues[nProp] <<= m_sPasswordFromColumn; break; + case 13: pValues[nProp] <<= m_bIsPasswordFromColumn; break; + } + } + PutProperties(aNames, aValues); +} + +void SwMiscConfig::Load() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == aNames.getLength()); + OUString sTmp; + for (sal_Int32 nProp = 0; nProp < aNames.getLength(); ++nProp) + { + if (pValues[nProp].hasValue()) + { + switch (nProp) + { + case 0 : pValues[nProp] >>= sTmp; + m_sWordDelimiter = SwModuleOptions::ConvertWordDelimiter(sTmp, true); + break; + case 1 : m_bDefaultFontsInCurrDocOnly = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 2 : m_bShowIndexPreview = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 3 : m_bGrfToGalleryAsLnk = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 4 : m_bNumAlignSize = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 5 : m_bSinglePrintJob = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 6 : m_nMailingFormats = static_cast<MailTextFormats>(*o3tl::doAccess<sal_Int32>(pValues[nProp])); break; + case 7 : pValues[nProp] >>= sTmp; m_sNameFromColumn = sTmp; break; + case 8 : pValues[nProp] >>= sTmp; m_sMailingPath = sTmp; break; + case 9 : pValues[nProp] >>= sTmp; m_sMailName = sTmp; break; + case 10: m_bIsNameFromColumn = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 11: pValues[nProp] >>= m_bAskForMailMergeInPrint; break; + case 12: pValues[nProp] >>= sTmp; m_sPasswordFromColumn = sTmp; break; + case 13: m_bIsPasswordFromColumn = *o3tl::doAccess<bool>(pValues[nProp]); break; + } + } + } +} + +const Sequence<OUString>& SwCompareConfig::GetPropertyNames() +{ + static Sequence<OUString> const aNames + { + "Mode", // 0 + "UseRSID", // 1 + "IgnorePieces", // 2 + "IgnoreLength", // 3 + "StoreRSID" // 4 + }; + return aNames; +} + +SwCompareConfig::SwCompareConfig() : + ConfigItem("Office.Writer/Comparison", ConfigItemMode::ReleaseTree) + ,m_bStoreRsid(true) +{ + m_eCmpMode = SwCompareMode::Auto; + m_bUseRsid = false; + m_bIgnorePieces = false; + m_nPieceLen = 1; + + Load(); +} + +SwCompareConfig::~SwCompareConfig() +{ +} + +void SwCompareConfig::ImplCommit() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + pValues[0] <<= static_cast<sal_Int16>(m_eCmpMode); + pValues[1] <<= m_bUseRsid; + pValues[2] <<= m_bIgnorePieces; + pValues[3] <<= static_cast<sal_Int16>(m_nPieceLen); + pValues[4] <<= m_bStoreRsid; + + PutProperties(aNames, aValues); +} + +void SwCompareConfig::Load() +{ + const Sequence<OUString>& aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == aNames.getLength()); + for (sal_Int32 nProp = 0; nProp < aNames.getLength(); nProp++) + { + if (pValues[nProp].hasValue()) + { + sal_Int32 nVal = 0; + pValues[nProp] >>= nVal; + + switch(nProp) + { + case 0 : m_eCmpMode = static_cast<SwCompareMode>(nVal); break; + case 1 : m_bUseRsid = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 2 : m_bIgnorePieces = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 3 : m_nPieceLen = nVal; break; + case 4 : m_bStoreRsid = *o3tl::doAccess<bool>(pValues[nProp]); break; + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/prtopt.cxx b/sw/source/uibase/config/prtopt.cxx new file mode 100644 index 000000000..13831df97 --- /dev/null +++ b/sw/source/uibase/config/prtopt.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unotools/configmgr.hxx> +#include <prtopt.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/uno/Sequence.hxx> + +using namespace utl; +using namespace com::sun::star::uno; + +// Ctor +Sequence<OUString> SwPrintOptions::GetPropertyNames() const +{ + static const char* aPropNames[] = + { + "Content/Graphic", // 0 + "Content/Table", // 1 + "Content/Control", // 2 + "Content/Background", // 3 + "Content/PrintBlack", // 4 + "Content/Note", // 5 + "Page/Reversed", // 6 + "Page/Brochure", // 7 + "Page/BrochureRightToLeft", // 8 + "Output/SinglePrintJob", // 9 + "Output/Fax", // 10 + "Papertray/FromPrinterSetup", // 11 + "Content/Drawing", // 12 not in SW/Web + "Page/LeftPage", // 13 not in SW/Web + "Page/RightPage", // 14 not in SW/Web + "EmptyPages", // 15 not in SW/Web + "Content/PrintPlaceholders", // 16 not in Sw/Web + "Content/PrintHiddenText" // 17 not in Sw/Web + }; + const int nCount = m_bIsWeb ? 12 : 18; + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + { + pNames[i] = OUString::createFromAscii(aPropNames[i]); + } + return aNames; +} + +SwPrintOptions::SwPrintOptions(bool bWeb) : + ConfigItem(bWeb ? OUString("Office.WriterWeb/Print") : OUString("Office.Writer/Print"), + ConfigItemMode::ReleaseTree), + m_bIsWeb(bWeb) +{ + m_bPrintPageBackground = !bWeb; + m_bPrintBlackFont = bWeb; + m_bPrintTextPlaceholder = m_bPrintHiddenText = false; + if (bWeb) + m_bPrintEmptyPages = false; + + 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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case 0: m_bPrintGraphic = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 1: m_bPrintTable = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 2: m_bPrintControl = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 3: m_bPrintPageBackground= *o3tl::doAccess<bool>(pValues[nProp]); break; + case 4: m_bPrintBlackFont = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 5: + { + sal_Int32 nTmp = 0; + pValues[nProp] >>= nTmp; + m_nPrintPostIts = static_cast<SwPostItMode>(nTmp); + } + break; + case 6: m_bPrintReverse = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 7: m_bPrintProspect = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 8: m_bPrintProspectRTL = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 9: m_bPrintSingleJobs = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 10: pValues[nProp] >>= m_sFaxName; break; + case 11: m_bPaperFromSetup = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 12: m_bPrintDraw = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 13: m_bPrintLeftPages = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 14: m_bPrintRightPages = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 15: m_bPrintEmptyPages = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 16: m_bPrintTextPlaceholder = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 17: m_bPrintHiddenText = *o3tl::doAccess<bool>(pValues[nProp]); break; + } + } + } + } + + // currently there is just one checkbox for print drawings and print graphics + // In the UI. (File/Print dialog and Tools/Options/.../Print) + // And since print graphics is the only available in Writer and WrtierWeb ... + + m_bPrintDraw = m_bPrintGraphic; +} + +SwPrintOptions::~SwPrintOptions() +{ +} + +void SwPrintOptions::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwPrintOptions::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= m_bPrintGraphic; break; + case 1: pValues[nProp] <<= m_bPrintTable; break; + case 2: pValues[nProp] <<= m_bPrintControl; break; + case 3: pValues[nProp] <<= m_bPrintPageBackground; break; + case 4: pValues[nProp] <<= m_bPrintBlackFont; break; + case 5: pValues[nProp] <<= static_cast<sal_Int32>(m_nPrintPostIts) ; break; + case 6: pValues[nProp] <<= m_bPrintReverse; break; + case 7: pValues[nProp] <<= m_bPrintProspect; break; + case 8: pValues[nProp] <<= m_bPrintProspectRTL; break; + case 9: pValues[nProp] <<= m_bPrintSingleJobs; break; + case 10: pValues[nProp] <<= m_sFaxName; break; + case 11: pValues[nProp] <<= m_bPaperFromSetup; break; + case 12: pValues[nProp] <<= m_bPrintDraw; break; + case 13: pValues[nProp] <<= m_bPrintLeftPages; break; + case 14: pValues[nProp] <<= m_bPrintRightPages; break; + case 15: pValues[nProp] <<= m_bPrintEmptyPages; break; + case 16: pValues[nProp] <<= m_bPrintTextPlaceholder; break; + case 17: pValues[nProp] <<= m_bPrintHiddenText; break; + } + } + + // currently there is just one checkbox for print drawings and print graphics + // In the UI. (File/Print dialog and Tools/Options/.../Print) + // And since print graphics is the only available in Writer and WrtierWeb ... + m_bPrintDraw = m_bPrintGraphic; + + PutProperties(aNames, aValues); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/uinums.cxx b/sw/source/uibase/config/uinums.cxx new file mode 100644 index 000000000..0ffee4437 --- /dev/null +++ b/sw/source/uibase/config/uinums.cxx @@ -0,0 +1,260 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <uinums.hxx> + +#include <unotools/pathoptions.hxx> +#include <tools/stream.hxx> +#include <sfx2/docfile.hxx> +#include <svl/itemiter.hxx> + +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <poolfmt.hxx> +#include <charfmt.hxx> + +using namespace ::com::sun::star; + +#define CHAPTER_FILENAME "chapter.cfg" + +/* + Description: Saving a rule + Parameter: rCopy -- the rule to save + nIdx -- position, where the rule is to be saved. + An old rule at that position will be overwritten. +*/ + +SwChapterNumRules::SwChapterNumRules() +{ + Init(); +} + +void SwChapterNumRules::Save() +{ + INetURLObject aURL; + SvtPathOptions aPathOpt; + aURL.SetSmartURL( aPathOpt.GetUserConfigPath() ); + aURL.setFinalSlash(); + aURL.Append(CHAPTER_FILENAME); + + SfxMedium aMedium( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE ); + SvStream* pStream = aMedium.GetOutStream(); + bool bRet = (pStream && pStream->GetError() == ERRCODE_NONE); + if (bRet) + { + sw::ExportStoredChapterNumberingRules(*this, *pStream,CHAPTER_FILENAME); + + pStream->Flush(); + + aMedium.Commit(); + } +} + +SwChapterNumRules::~SwChapterNumRules() +{ +} + +void SwChapterNumRules::Init() +{ + for(auto & rpNumRule : pNumRules) + rpNumRule.reset(); + + OUString sNm(CHAPTER_FILENAME); + SvtPathOptions aOpt; + if( aOpt.SearchFile( sNm )) + { + SfxMedium aStrm( sNm, StreamMode::STD_READ ); + sw::ImportStoredChapterNumberingRules(*this, *aStrm.GetInStream(), + CHAPTER_FILENAME); + } +} + +void SwChapterNumRules::CreateEmptyNumRule(sal_uInt16 const nIndex) +{ + assert(nIndex < nMaxRules); + assert(!pNumRules[nIndex]); + pNumRules[nIndex].reset(new SwNumRulesWithName); +} + +void SwChapterNumRules::ApplyNumRules(const SwNumRulesWithName &rCopy, sal_uInt16 nIdx) +{ + assert(nIdx < nMaxRules); + if( !pNumRules[nIdx] ) + pNumRules[nIdx].reset(new SwNumRulesWithName( rCopy )); + else + *pNumRules[nIdx] = rCopy; + Save(); // store it immediately +} + +SwNumRulesWithName::SwNumRulesWithName( const SwNumRule &rCopy, + const OUString &rName ) + : maName(rName) +{ + for( sal_uInt16 n = 0; n < MAXLEVEL; ++n ) + { + const SwNumFormat* pFormat = rCopy.GetNumFormat( n ); + if( pFormat ) + aFormats[ n ].reset(new SwNumFormatGlobal( *pFormat )); + else + aFormats[ n ].reset(); + } +} + +SwNumRulesWithName::SwNumRulesWithName( const SwNumRulesWithName& rCopy ) +{ + *this = rCopy; +} + +SwNumRulesWithName::~SwNumRulesWithName() +{ +} + +SwNumRulesWithName& SwNumRulesWithName::operator=(const SwNumRulesWithName &rCopy) +{ + if( this != &rCopy ) + { + maName = rCopy.maName; + for( int n = 0; n < MAXLEVEL; ++n ) + { + SwNumFormatGlobal* pFormat = rCopy.aFormats[ n ].get(); + if( pFormat ) + aFormats[ n ].reset(new SwNumFormatGlobal( *pFormat )); + else + aFormats[ n ].reset(); + } + } + return *this; +} + +std::unique_ptr<SwNumRule> SwNumRulesWithName::MakeNumRule(SwWrtShell& rSh) const +{ + // #i89178# + std::unique_ptr<SwNumRule> pChg(new SwNumRule(maName, numfunc::GetDefaultPositionAndSpaceMode())); + pChg->SetAutoRule( false ); + for (sal_uInt16 n = 0; n < MAXLEVEL; ++n) + { + SwNumFormatGlobal* pFormat = aFormats[ n ].get(); + if (!pFormat) + continue; + pChg->Set(n, pFormat->MakeNumFormat(rSh)); + } + return pChg; +} + +void SwNumRulesWithName::GetNumFormat( + size_t const nIndex, SwNumFormat const*& rpNumFormat, OUString const*& rpName) const +{ + rpNumFormat = (aFormats[nIndex]) ? &aFormats[nIndex]->aFormat : nullptr; + rpName = (aFormats[nIndex]) ? &aFormats[nIndex]->sCharFormatName : nullptr; +} + +void SwNumRulesWithName::SetNumFormat( + size_t const nIndex, SwNumFormat const& rNumFormat, OUString const& rName) +{ + aFormats[nIndex].reset( new SwNumFormatGlobal(rNumFormat) ); + aFormats[nIndex]->sCharFormatName = rName; + aFormats[nIndex]->nCharPoolId = USHRT_MAX; + aFormats[nIndex]->m_Items.clear(); +} + +SwNumRulesWithName::SwNumFormatGlobal::SwNumFormatGlobal( const SwNumFormat& rFormat ) + : aFormat( rFormat ), nCharPoolId( USHRT_MAX ) +{ + // relative gaps????? + + SwCharFormat* pFormat = rFormat.GetCharFormat(); + if( pFormat ) + { + sCharFormatName = pFormat->GetName(); + nCharPoolId = pFormat->GetPoolFormatId(); + if( pFormat->GetAttrSet().Count() ) + { + SfxItemIter aIter( pFormat->GetAttrSet() ); + const SfxPoolItem *pCurr = aIter.GetCurItem(); + do + { + m_Items.push_back(std::unique_ptr<SfxPoolItem>(pCurr->Clone())); + pCurr = aIter.NextItem(); + } while (pCurr); + } + + aFormat.SetCharFormat( nullptr ); + } +} + +SwNumRulesWithName::SwNumFormatGlobal::SwNumFormatGlobal( const SwNumFormatGlobal& rFormat ) + : + aFormat( rFormat.aFormat ), + sCharFormatName( rFormat.sCharFormatName ), + nCharPoolId( rFormat.nCharPoolId ) +{ + for (size_t n = rFormat.m_Items.size(); n; ) + { + m_Items.push_back(std::unique_ptr<SfxPoolItem>(rFormat.m_Items[ --n ]->Clone())); + } +} + +SwNumRulesWithName::SwNumFormatGlobal::~SwNumFormatGlobal() +{ +} + +SwNumFormat SwNumRulesWithName::SwNumFormatGlobal::MakeNumFormat(SwWrtShell& rSh) const +{ + SwCharFormat* pFormat = nullptr; + if( !sCharFormatName.isEmpty() ) + { + // at first, look for the name + sal_uInt16 nArrLen = rSh.GetCharFormatCount(); + for( sal_uInt16 i = 1; i < nArrLen; ++i ) + { + pFormat = &rSh.GetCharFormat( i ); + if (pFormat->GetName()==sCharFormatName) + // exists, so leave attributes as they are! + break; + pFormat = nullptr; + } + + if( !pFormat ) + { + if( IsPoolUserFormat( nCharPoolId ) ) + { + pFormat = rSh.MakeCharFormat( sCharFormatName ); + pFormat->SetAuto(false); + } + else + pFormat = rSh.GetCharFormatFromPool( nCharPoolId ); + + if( !pFormat->HasWriterListeners() ) // set attributes + { + for (size_t n = m_Items.size(); n; ) + { + pFormat->SetFormatAttr( *m_Items[ --n ] ); + } + } + } + } + const_cast<SwNumFormat&>(aFormat).SetCharFormat(pFormat); + SwNumFormat aNew = aFormat; + if (pFormat) + const_cast<SwNumFormat&>(aFormat).SetCharFormat(nullptr); + return aNew; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/usrpref.cxx b/sw/source/uibase/config/usrpref.cxx new file mode 100644 index 000000000..77790b82c --- /dev/null +++ b/sw/source/uibase/config/usrpref.cxx @@ -0,0 +1,599 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cstring> +#include <osl/diagnose.h> +#include <o3tl/any.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/syslocale.hxx> + +#include <usrpref.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <unotools/localedatawrapper.hxx> + +using namespace utl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +void SwMasterUsrPref::SetUsrPref(const SwViewOption &rCopy) +{ + *static_cast<SwViewOption*>(this) = rCopy; +} + +SwMasterUsrPref::SwMasterUsrPref(bool bWeb) : + m_eFieldUpdateFlags(AUTOUPD_OFF), + m_nLinkUpdateMode(0), + m_bIsHScrollMetricSet(false), + m_bIsVScrollMetricSet(false), + m_nDefTabInMm100( 2000 ), // 2 cm + m_bIsSquaredPageMode(false), + m_bIsAlignMathObjectsToBaseline(false), + m_aContentConfig(bWeb, *this), + m_aLayoutConfig(bWeb, *this), + m_aGridConfig(bWeb, *this), + m_aCursorConfig(*this), + m_pWebColorConfig(bWeb ? new SwWebColorConfig(*this) : nullptr), + m_bApplyCharUnit(false) +{ + if (utl::ConfigManager::IsFuzzing()) + { + m_eHScrollMetric = m_eVScrollMetric = m_eUserMetric = FieldUnit::CM; + return; + } + MeasurementSystem eSystem = SvtSysLocale().GetLocaleData().getMeasurementSystemEnum(); + m_eUserMetric = MeasurementSystem::Metric == eSystem ? FieldUnit::CM : FieldUnit::INCH; + m_eHScrollMetric = m_eVScrollMetric = m_eUserMetric; + + m_aContentConfig.Load(); + m_aLayoutConfig.Load(); + m_aGridConfig.Load(); + m_aCursorConfig.Load(); + if(m_pWebColorConfig) + m_pWebColorConfig->Load(); +} + +SwMasterUsrPref::~SwMasterUsrPref() +{ +} + +static const auto g_UpdateLinkIndex = 17; + +Sequence<OUString> SwContentViewConfig::GetPropertyNames() const +{ + static constexpr const char*const aPropNames[] = + { + "Display/GraphicObject", // 0 + "Display/Table", // 1 + "Display/DrawingControl", // 2 + "Display/FieldCode", // 3 + "Display/Note", // 4 + "Display/ShowContentTips", // 5 + "NonprintingCharacter/MetaCharacters", // 6 + "NonprintingCharacter/ParagraphEnd", // 7 + "NonprintingCharacter/OptionalHyphen", // 8 + "NonprintingCharacter/Space", // 9 + "NonprintingCharacter/Break", // 10 + "NonprintingCharacter/ProtectedSpace", // 11 + "NonprintingCharacter/Tab", // 12 //not in Writer/Web + "NonprintingCharacter/HiddenText", // 13 + "NonprintingCharacter/HiddenParagraph", // 14 + "NonprintingCharacter/HiddenCharacter", // 15 + "NonprintingCharacter/Bookmarks", // 16 + "Update/Link", // 17 + "Update/Field", // 18 + "Update/Chart", // 19 + "Display/ShowInlineTooltips", // 20 + "Display/UseHeaderFooterMenu" // 21 + }; +#if defined(__GNUC__) && !defined(__clang__) + // clang 8.0.0 says strcmp isn't constexpr + static_assert(std::strcmp("Update/Link", aPropNames[g_UpdateLinkIndex]) == 0); +#endif + const int nCount = bWeb ? 12 : SAL_N_ELEMENTS(aPropNames); + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + { + pNames[i] = OUString::createFromAscii(aPropNames[i]); + } + return aNames; +} + +SwContentViewConfig::SwContentViewConfig(bool bIsWeb, SwMasterUsrPref& rPar) : + ConfigItem(bIsWeb ? OUString("Office.WriterWeb/Content") : OUString("Office.Writer/Content")), + rParent(rPar), + bWeb(bIsWeb) +{ + Load(); + EnableNotification( GetPropertyNames() ); +} + +SwContentViewConfig::~SwContentViewConfig() +{ +} + +void SwContentViewConfig::Notify( const Sequence< OUString > & /*rPropertyNames*/ ) +{ + Load(); +} + +void SwContentViewConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + bool bVal = false; + switch(nProp) + { + case 0: bVal = rParent.IsGraphic(); break;// "Display/GraphicObject", + case 1: bVal = rParent.IsTable(); break;// "Display/Table", + case 2: bVal = rParent.IsDraw(); break;// "Display/DrawingControl", + case 3: bVal = rParent.IsFieldName(); break;// "Display/FieldCode", + case 4: bVal = rParent.IsPostIts(); break;// "Display/Note", + case 5: bVal = rParent.IsShowContentTips(); break; // "Display/ShowContentTips" + case 6: bVal = rParent.IsViewMetaChars(); break; //"NonprintingCharacter/MetaCharacters" + case 7: bVal = rParent.IsParagraph(true); break;// "NonprintingCharacter/ParagraphEnd", + case 8: bVal = rParent.IsSoftHyph(); break;// "NonprintingCharacter/OptionalHyphen", + case 9: bVal = rParent.IsBlank(true); break;// "NonprintingCharacter/Space", + case 10: bVal = rParent.IsLineBreak(true);break;// "NonprintingCharacter/Break", + case 11: bVal = rParent.IsHardBlank(); break;// "NonprintingCharacter/ProtectedSpace", + case 12: bVal = rParent.IsTab(true); break;// "NonprintingCharacter/Tab", + case 13: bVal = rParent.IsShowHiddenField(); break;// "NonprintingCharacter/Fields: HiddenText", + case 14: bVal = rParent.IsShowHiddenPara(); break;// "NonprintingCharacter/Fields: HiddenParagraph", + case 15: bVal = rParent.IsShowHiddenChar(true); break;// "NonprintingCharacter/HiddenCharacter", + case 16: bVal = rParent.IsShowBookmarks(true); break;// "NonprintingCharacter/Bookmarks", + case 17: pValues[nProp] <<= rParent.GetUpdateLinkMode(); break;// "Update/Link", + case 18: bVal = rParent.IsUpdateFields(); break;// "Update/Field", + case 19: bVal = rParent.IsUpdateCharts(); break;// "Update/Chart" + case 20: bVal = rParent.IsShowInlineTooltips(); break;// "Display/ShowInlineTooltips" + case 21: bVal = rParent.IsUseHeaderFooterMenu(); break;// "Display/UseHeaderFooterMenu" + } + if (nProp != g_UpdateLinkIndex) + pValues[nProp] <<= bVal; + } + PutProperties(aNames, aValues); +} + +void SwContentViewConfig::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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + bool bSet = nProp != g_UpdateLinkIndex && *o3tl::doAccess<bool>(pValues[nProp]); + switch(nProp) + { + case 0: rParent.SetGraphic(bSet); break;// "Display/GraphicObject", + case 1: rParent.SetTable(bSet); break;// "Display/Table", + case 2: rParent.SetDraw(bSet); break;// "Display/DrawingControl", + case 3: rParent.SetFieldName(bSet); break;// "Display/FieldCode", + case 4: rParent.SetPostIts(bSet); break;// "Display/Note", + case 5: rParent.SetShowContentTips(bSet); break;// "Display/ShowContentTips", + case 6: rParent.SetViewMetaChars(bSet); break; //"NonprintingCharacter/MetaCharacters" + case 7: rParent.SetParagraph(bSet); break;// "NonprintingCharacter/ParagraphEnd", + case 8: rParent.SetSoftHyph(bSet); break;// "NonprintingCharacter/OptionalHyphen", + case 9: rParent.SetBlank(bSet); break;// "NonprintingCharacter/Space", + case 10: rParent.SetLineBreak(bSet);break;// "NonprintingCharacter/Break", + case 11: rParent.SetHardBlank(bSet); break;// "NonprintingCharacter/ProtectedSpace", + case 12: rParent.SetTab(bSet); break;// "NonprintingCharacter/Tab", + case 13: rParent.SetShowHiddenField(bSet); break;// "NonprintingCharacter/Fields: HiddenText", + case 14: rParent.SetShowHiddenPara(bSet); break;// "NonprintingCharacter/Fields: HiddenParagraph", + case 15: rParent.SetShowHiddenChar(bSet); break;// "NonprintingCharacter/HiddenCharacter", + case 16: rParent.SetShowBookmarks(bSet); break;// "NonprintingCharacter/Bookmarks", + case 17: + { + sal_Int32 nSet = 0; + pValues[nProp] >>= nSet; + rParent.SetUpdateLinkMode(nSet, true); + } + break;// "Update/Link", + case 18: rParent.SetUpdateFields(bSet); break;// "Update/Field", + case 19: rParent.SetUpdateCharts(bSet); break;// "Update/Chart" + case 20: rParent.SetShowInlineTooltips(bSet); break;// "Display/ShowInlineTooltips" + case 21: rParent.SetUseHeaderFooterMenu(bSet); break;// "Display/UseHeaderFooterMenu" + } + } + } + } +} + +Sequence<OUString> SwLayoutViewConfig::GetPropertyNames() const +{ + static const char* aPropNames[] = + { + "Line/Guide", // 0 + "Window/HorizontalScroll", // 1 + "Window/VerticalScroll", // 2 + "Window/ShowRulers", // 3 + "Window/HorizontalRuler", // 4 + "Window/VerticalRuler", // 5 + "Window/HorizontalRulerUnit", // 6 + "Window/VerticalRulerUnit", // 7 + "Window/SmoothScroll", // 8 + "Zoom/Value", // 9 + "Zoom/Type", //10 + "Other/IsAlignMathObjectsToBaseline", //11 + "Other/MeasureUnit", //12 + // below properties are not available in WriterWeb + "Other/TabStop", //13 + "Window/IsVerticalRulerRight", //14 + "ViewLayout/Columns", //15 + "ViewLayout/BookMode", //16 + "Other/IsSquaredPageMode", //17 + "Other/ApplyCharUnit", //18 + "Window/ShowScrollBarTips" //19 + }; + const int nCount = bWeb ? 13 : 20; + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + { + pNames[i] = OUString::createFromAscii(aPropNames[i]); + } + return aNames; +} + +SwLayoutViewConfig::SwLayoutViewConfig(bool bIsWeb, SwMasterUsrPref& rPar) : + ConfigItem(bIsWeb ? OUString("Office.WriterWeb/Layout") : OUString("Office.Writer/Layout"), + ConfigItemMode::ReleaseTree), + rParent(rPar), + bWeb(bIsWeb) +{ +} + +SwLayoutViewConfig::~SwLayoutViewConfig() +{ +} + +void SwLayoutViewConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + Any &rVal = pValues[nProp]; + switch(nProp) + { + case 0: rVal <<= rParent.IsCrossHair(); break; // "Line/Guide", + case 1: rVal <<= rParent.IsViewHScrollBar(); break; // "Window/HorizontalScroll", + case 2: rVal <<= rParent.IsViewVScrollBar(); break; // "Window/VerticalScroll", + case 3: rVal <<= rParent.IsViewAnyRuler(); break; // "Window/ShowRulers" + // #i14593# use IsView*Ruler(true) instead of IsView*Ruler() + // this preserves the single ruler states even if "Window/ShowRulers" is off + case 4: rVal <<= rParent.IsViewHRuler(true); break; // "Window/HorizontalRuler", + case 5: rVal <<= rParent.IsViewVRuler(true); break; // "Window/VerticalRuler", + case 6: + if(rParent.m_bIsHScrollMetricSet) + rVal <<= static_cast<sal_Int32>(rParent.m_eHScrollMetric); // "Window/HorizontalRulerUnit" + break; + case 7: + if(rParent.m_bIsVScrollMetricSet) + rVal <<= static_cast<sal_Int32>(rParent.m_eVScrollMetric); // "Window/VerticalRulerUnit" + break; + case 8: rVal <<= rParent.IsSmoothScroll(); break; // "Window/SmoothScroll", + case 9: rVal <<= static_cast<sal_Int32>(rParent.GetZoom()); break; // "Zoom/Value", + case 10: rVal <<= static_cast<sal_Int32>(rParent.GetZoomType()); break; // "Zoom/Type", + case 11: rVal <<= rParent.IsAlignMathObjectsToBaseline(); break; // "Other/IsAlignMathObjectsToBaseline" + case 12: rVal <<= static_cast<sal_Int32>(rParent.GetMetric()); break; // "Other/MeasureUnit", + case 13: rVal <<= rParent.GetDefTabInMm100(); break;// "Other/TabStop", + case 14: rVal <<= rParent.IsVRulerRight(); break; // "Window/IsVerticalRulerRight", + case 15: rVal <<= static_cast<sal_Int32>(rParent.GetViewLayoutColumns()); break; // "ViewLayout/Columns", + case 16: rVal <<= rParent.IsViewLayoutBookMode(); break; // "ViewLayout/BookMode", + case 17: rVal <<= rParent.IsSquaredPageMode(); break; // "Other/IsSquaredPageMode", + case 18: rVal <<= rParent.IsApplyCharUnit(); break; // "Other/ApplyCharUnit", + case 19: rVal <<= rParent.IsShowScrollBarTips(); break; // "Window/ShowScrollBarTips", + } + } + PutProperties(aNames, aValues); +} + +void SwLayoutViewConfig::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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + sal_Int32 nInt32Val = 0; + bool bSet = false; + pValues[nProp] >>= nInt32Val; + pValues[nProp] >>= bSet; + + switch(nProp) + { + case 0: rParent.SetCrossHair(bSet); break;// "Line/Guide", + case 1: rParent.SetViewHScrollBar(bSet); break;// "Window/HorizontalScroll", + case 2: rParent.SetViewVScrollBar(bSet); break;// "Window/VerticalScroll", + case 3: rParent.SetViewAnyRuler(bSet);break; // "Window/ShowRulers" + case 4: rParent.SetViewHRuler(bSet); break;// "Window/HorizontalRuler", + case 5: rParent.SetViewVRuler(bSet); break;// "Window/VerticalRuler", + case 6: + { + rParent.m_bIsHScrollMetricSet = true; + rParent.m_eHScrollMetric = static_cast<FieldUnit>(nInt32Val); // "Window/HorizontalRulerUnit" + } + break; + case 7: + { + rParent.m_bIsVScrollMetricSet = true; + rParent.m_eVScrollMetric = static_cast<FieldUnit>(nInt32Val); // "Window/VerticalRulerUnit" + } + break; + case 8: rParent.SetSmoothScroll(bSet); break;// "Window/SmoothScroll", + case 9: rParent.SetZoom( static_cast< sal_uInt16 >(nInt32Val) ); break;// "Zoom/Value", + case 10: rParent.SetZoomType( static_cast< SvxZoomType >(nInt32Val) ); break;// "Zoom/Type", + case 11: rParent.SetAlignMathObjectsToBaseline(bSet, true); break;// "Other/IsAlignMathObjectsToBaseline" + case 12: rParent.SetMetric(static_cast<FieldUnit>(nInt32Val), true); break;// "Other/MeasureUnit", + case 13: rParent.SetDefTabInMm100(nInt32Val, true); break;// "Other/TabStop", + case 14: rParent.SetVRulerRight(bSet); break;// "Window/IsVerticalRulerRight", + case 15: rParent.SetViewLayoutColumns( static_cast<sal_uInt16>(nInt32Val) ); break;// "ViewLayout/Columns", + case 16: rParent.SetViewLayoutBookMode(bSet); break;// "ViewLayout/BookMode", + case 17: rParent.SetDefaultPageMode(bSet,true); break;// "Other/IsSquaredPageMode", + case 18: rParent.SetApplyCharUnit(bSet, true); break;// "Other/ApplyUserChar" + case 19: rParent.SetShowScrollBarTips(bSet); break;// "Window/ShowScrollBarTips", + } + } + } + } +} + +void SwLayoutViewConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +Sequence<OUString> SwGridConfig::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "Option/SnapToGrid", // 0 + "Option/VisibleGrid", // 1 + "Option/Synchronize", // 2 + "Resolution/XAxis", // 3 + "Resolution/YAxis", // 4 + "Subdivision/XAxis", // 5 + "Subdivision/YAxis" // 6 + }; + const int nCount = 7; + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + { + pNames[i] = OUString::createFromAscii(aPropNames[i]); + } + return aNames; +} + +SwGridConfig::SwGridConfig(bool bIsWeb, SwMasterUsrPref& rPar) : + ConfigItem(bIsWeb ? OUString("Office.WriterWeb/Grid") : OUString("Office.Writer/Grid"), + ConfigItemMode::ReleaseTree), + rParent(rPar) +{ +} + +SwGridConfig::~SwGridConfig() +{ +} + +void SwGridConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= rParent.IsSnap(); break;// "Option/SnapToGrid", + case 1: pValues[nProp] <<= rParent.IsGridVisible(); break;//"Option/VisibleGrid", + case 2: pValues[nProp] <<= rParent.IsSynchronize(); break;// "Option/Synchronize", + case 3: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(rParent.GetSnapSize().Width())); break;// "Resolution/XAxis", + case 4: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(rParent.GetSnapSize().Height())); break;// "Resolution/YAxis", + case 5: pValues[nProp] <<= static_cast<sal_Int16>(rParent.GetDivisionX()); break;// "Subdivision/XAxis", + case 6: pValues[nProp] <<= static_cast<sal_Int16>(rParent.GetDivisionY()); break;// "Subdivision/YAxis" + } + } + PutProperties(aNames, aValues); +} + +void SwGridConfig::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()) + { + Size aSnap(rParent.GetSnapSize()); + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + bool bSet = nProp < 3 && *o3tl::doAccess<bool>(pValues[nProp]); + sal_Int32 nSet = 0; + if(nProp >= 3) + pValues[nProp] >>= nSet; + switch(nProp) + { + case 0: rParent.SetSnap(bSet); break;// "Option/SnapToGrid", + case 1: rParent.SetGridVisible(bSet); break;//"Option/VisibleGrid", + case 2: rParent.SetSynchronize(bSet); break;// "Option/Synchronize", + case 3: aSnap.setWidth( convertMm100ToTwip(nSet) ); break;// "Resolution/XAxis", + case 4: aSnap.setHeight( convertMm100ToTwip(nSet) ); break;// "Resolution/YAxis", + case 5: rParent.SetDivisionX(static_cast<short>(nSet)); break;// "Subdivision/XAxis", + case 6: rParent.SetDivisionY(static_cast<short>(nSet)); break;// "Subdivision/YAxis" + } + } + } + rParent.SetSnapSize(aSnap); + } +} + +void SwGridConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +Sequence<OUString> SwCursorConfig::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "DirectCursor/UseDirectCursor", // 0 + "DirectCursor/Insert", // 1 + "Option/ProtectedArea", // 2 + }; + const int nCount = SAL_N_ELEMENTS(aPropNames); + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + for(int i = 0; i < nCount; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + return aNames; +} + +SwCursorConfig::SwCursorConfig(SwMasterUsrPref& rPar) : + ConfigItem("Office.Writer/Cursor", ConfigItemMode::ReleaseTree), + rParent(rPar) +{ +} + +SwCursorConfig::~SwCursorConfig() +{ +} + +void SwCursorConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= rParent.IsShadowCursor(); break; // "DirectCursor/UseDirectCursor", + case 1: pValues[nProp] <<= static_cast<sal_Int32>(rParent.GetShdwCursorFillMode()); break; // "DirectCursor/Insert", + case 2: pValues[nProp] <<= rParent.IsCursorInProtectedArea(); break; // "Option/ProtectedArea" + } + } + PutProperties(aNames, aValues); +} + +void SwCursorConfig::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()) + { + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + bool bSet = false; + sal_Int32 nSet = 0; + if(nProp != 1 ) + bSet = *o3tl::doAccess<bool>(pValues[nProp]); + else + pValues[nProp] >>= nSet; + switch(nProp) + { + case 0: rParent.SetShadowCursor(bSet); break; // "DirectCursor/UseDirectCursor", + case 1: rParent.SetShdwCursorFillMode(static_cast<SwFillMode>(nSet)); break; // "DirectCursor/Insert", + case 2: rParent.SetCursorInProtectedArea(bSet); break; // "Option/ProtectedArea" + } + } + } + + } +} + +void SwCursorConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +SwWebColorConfig::SwWebColorConfig(SwMasterUsrPref& rPar) : + ConfigItem("Office.WriterWeb/Background", ConfigItemMode::ReleaseTree), + rParent(rPar), + aPropNames(1) +{ + aPropNames.getArray()[0] = "Color"; +} + +SwWebColorConfig::~SwWebColorConfig() +{ +} + +void SwWebColorConfig::ImplCommit() +{ + Sequence<Any> aValues(aPropNames.getLength()); + Any* pValues = aValues.getArray(); + for(int nProp = 0; nProp < aPropNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= rParent.GetRetoucheColor(); break;// "Color", + } + } + PutProperties(aPropNames, aValues); +} + +void SwWebColorConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwWebColorConfig::Load() +{ + Sequence<Any> aValues = GetProperties(aPropNames); + const Any* pValues = aValues.getConstArray(); + OSL_ENSURE(aValues.getLength() == aPropNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aPropNames.getLength()) + { + for(int nProp = 0; nProp < aPropNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case 0: + Color nSet; + pValues[nProp] >>= nSet; rParent.SetRetoucheColor(nSet); + break;// "Color", + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/config/viewopt.cxx b/sw/source/uibase/config/viewopt.cxx new file mode 100644 index 000000000..194c89bba --- /dev/null +++ b/sw/source/uibase/config/viewopt.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 <sfx2/htmlmode.hxx> +#include <svtools/htmlcfg.hxx> + +#include <editeng/editids.hrc> +#include <editeng/svxacorr.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <swmodule.hxx> +#include <viewopt.hxx> +#include <wdocsh.hxx> +#include <swrect.hxx> +#include <crstate.hxx> +#include <authratr.hxx> +#include <svtools/colorcfg.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/syslocale.hxx> + +#include <editeng/acorrcfg.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/configurationlistener.hxx> + +Color SwViewOption::s_aDocBoundColor(COL_LIGHTGRAY); +Color SwViewOption::s_aObjectBoundColor(COL_LIGHTGRAY); +Color SwViewOption::s_aDocColor(COL_LIGHTGRAY); +Color SwViewOption::s_aAppBackgroundColor(COL_LIGHTGRAY); +Color SwViewOption::s_aTableBoundColor(COL_LIGHTGRAY); +Color SwViewOption::s_aIndexShadingsColor(COL_LIGHTGRAY); +Color SwViewOption::s_aLinksColor(COL_BLUE); +Color SwViewOption::s_aVisitedLinksColor(COL_RED); +Color SwViewOption::s_aDirectCursorColor(COL_BLUE); +Color SwViewOption::s_aTextGridColor(COL_LIGHTGRAY); +Color SwViewOption::s_aSpellColor(COL_LIGHTRED); +Color SwViewOption::s_aSmarttagColor(COL_LIGHTMAGENTA); +Color SwViewOption::s_aFontColor(COL_BLACK); +Color SwViewOption::s_aFieldShadingsColor(COL_LIGHTGRAY); +Color SwViewOption::s_aSectionBoundColor(COL_LIGHTGRAY); +Color SwViewOption::s_aPageBreakColor(COL_BLUE); +Color SwViewOption::s_aScriptIndicatorColor(COL_GREEN); +Color SwViewOption::s_aShadowColor(COL_GRAY); +Color SwViewOption::s_aHeaderFooterMarkColor(COL_BLUE); + +ViewOptFlags SwViewOption::s_nAppearanceFlags = ViewOptFlags::DocBoundaries|ViewOptFlags::ObjectBoundaries; +sal_uInt16 SwViewOption::s_nPixelTwips = 0; // one pixel on the screen + +bool SwViewOption::IsEqualFlags( const SwViewOption &rOpt ) const +{ + return m_nCoreOptions == rOpt.m_nCoreOptions + && m_nCore2Options == rOpt.m_nCore2Options + && m_aSnapSize == rOpt.m_aSnapSize + && mnViewLayoutColumns == rOpt.mnViewLayoutColumns + && m_nDivisionX == rOpt.GetDivisionX() + && m_nDivisionY == rOpt.GetDivisionY() + && m_nPagePreviewRow == rOpt.GetPagePrevRow() + && m_nPagePreviewCol == rOpt.GetPagePrevCol() + && m_aRetouchColor == rOpt.GetRetoucheColor() + && mbFormView == rOpt.IsFormView() + && mbBrowseMode == rOpt.getBrowseMode() + && mbViewLayoutBookMode == rOpt.mbViewLayoutBookMode + && mbHideWhitespaceMode == rOpt.mbHideWhitespaceMode + && m_bShowPlaceHolderFields == rOpt.m_bShowPlaceHolderFields + && m_bIdle == rOpt.m_bIdle +#ifdef DBG_UTIL + // correspond to the statements in ui/config/cfgvw.src + && m_bTest1 == rOpt.IsTest1() + && m_bTest2 == rOpt.IsTest2() + && m_bTest3 == rOpt.IsTest3() + && m_bTest4 == rOpt.IsTest4() + && m_bTest5 == rOpt.IsTest5() + && m_bTest6 == rOpt.IsTest6() + && m_bTest7 == rOpt.IsTest7() + && m_bTest8 == rOpt.IsTest8() + && m_bTest10 == rOpt.IsTest10() +#endif + ; +} + +void SwViewOption::DrawRect( OutputDevice *pOut, + const SwRect &rRect, ::Color nCol ) +{ + if ( pOut->GetOutDevType() != OUTDEV_PRINTER ) + { + const Color aCol( nCol ); + const Color aOldColor( pOut->GetFillColor() ); + pOut->SetFillColor( aCol ); + pOut->DrawRect( rRect.SVRect() ); + pOut->SetFillColor( aOldColor ); + } + else + DrawRectPrinter( pOut, rRect ); +} + +void SwViewOption::DrawRectPrinter( OutputDevice *pOut, + const SwRect &rRect ) +{ + Color aOldColor(pOut->GetLineColor()); + Color aOldFillColor( pOut->GetFillColor() ); + pOut->SetLineColor( COL_BLACK ); + pOut->SetFillColor( COL_TRANSPARENT); + pOut->DrawRect( rRect.SVRect() ); + pOut->SetFillColor( aOldFillColor ); + pOut->SetLineColor( aOldColor ); +} + +sal_uInt16 SwViewOption::GetPostItsWidth( const OutputDevice *pOut ) +{ + assert(pOut && "no Outdev"); + return sal_uInt16(pOut->GetTextWidth(" ")); +} + +void SwViewOption::PaintPostIts( OutputDevice *pOut, const SwRect &rRect, bool bIsScript ) +{ + if( pOut && bIsScript ) + { + Color aOldLineColor( pOut->GetLineColor() ); + pOut->SetLineColor( COL_GRAY ); + // to make it look nice, we subtract two pixels everywhere + sal_uInt16 nPix = s_nPixelTwips * 2; + if( rRect.Width() <= 2 * nPix || rRect.Height() <= 2 * nPix ) + nPix = 0; + const Point aTopLeft( rRect.Left() + nPix, rRect.Top() + nPix ); + const Point aBotRight( rRect.Right() - nPix, rRect.Bottom() - nPix ); + const SwRect aRect( aTopLeft, aBotRight ); + DrawRect( pOut, aRect, s_aScriptIndicatorColor ); + pOut->SetLineColor( aOldLineColor ); + } +} + +SwViewOption::SwViewOption() : + m_sSymbolFont( "symbol" ), + m_aRetouchColor( COL_TRANSPARENT ), + mnViewLayoutColumns( 0 ), + m_nPagePreviewRow( 1 ), + m_nPagePreviewCol( 2 ), + m_nShadowCursorFillMode( SwFillMode::Tab ), + m_bReadonly(false), + m_bStarOneSetting(false), + m_bIsPagePreview(false), + m_bSelectionInReadonly(false), + mbFormView(false), + mbBrowseMode(false), + mbBookView(false), + mbViewLayoutBookMode(false), + mbHideWhitespaceMode(false), + m_bShowPlaceHolderFields( true ), + m_nZoom( 100 ), + m_eZoom( SvxZoomType::PERCENT ), + m_nTableDestination(TBL_DEST_CELL) +{ + // Initialisation is a little simpler now + // all Bits to 0 + m_nCoreOptions = + ViewOptFlags1::HardBlank | + ViewOptFlags1::SoftHyph | + ViewOptFlags1::Ref | + ViewOptFlags1::Graphic | + ViewOptFlags1::Table | + ViewOptFlags1::Draw | + ViewOptFlags1::Control | + ViewOptFlags1::Pageback | + ViewOptFlags1::Postits; + + m_nCore2Options = + ViewOptCoreFlags2::BlackFont | + ViewOptCoreFlags2::HiddenPara; + + m_nUIOptions = + ViewOptFlags2::Modified | + ViewOptFlags2::GrfKeepZoom | + ViewOptFlags2::ResolvedPostits | + ViewOptFlags2::AnyRuler; + + if (!utl::ConfigManager::IsFuzzing() && MeasurementSystem::Metric != SvtSysLocale().GetLocaleData().getMeasurementSystemEnum()) + { + m_aSnapSize.setWidth(720); // 1/2" + m_aSnapSize.setHeight(720); // 1/2" + + } + else + { + m_aSnapSize.setWidth(567); // 1 cm + m_aSnapSize.setHeight(567); // 1 cm + } + m_nDivisionX = m_nDivisionY = 1; + + m_bSelectionInReadonly = !utl::ConfigManager::IsFuzzing() && SW_MOD()->GetAccessibilityOptions().IsSelectionInReadonly(); + + m_bIdle = true; + +#ifdef DBG_UTIL + // correspond to the statements in ui/config/cfgvw.src + m_bTest1 = m_bTest2 = m_bTest3 = m_bTest4 = + m_bTest5 = m_bTest6 = m_bTest7 = m_bTest8 = m_bTest10 = false; +#endif + if (comphelper::LibreOfficeKit::isActive()) + s_aAppBackgroundColor = COL_TRANSPARENT; +} + +SwViewOption::SwViewOption(const SwViewOption& rVOpt) +{ + m_bReadonly = false; + m_bSelectionInReadonly = false; + // #114856# Form view + mbFormView = rVOpt.mbFormView; + m_nZoom = rVOpt.m_nZoom ; + m_aSnapSize = rVOpt.m_aSnapSize ; + mnViewLayoutColumns = rVOpt.mnViewLayoutColumns ; + m_nDivisionX = rVOpt.m_nDivisionX ; + m_nDivisionY = rVOpt.m_nDivisionY ; + m_nPagePreviewRow = rVOpt.m_nPagePreviewRow; + m_nPagePreviewCol = rVOpt.m_nPagePreviewCol; + m_bIsPagePreview = rVOpt.m_bIsPagePreview; + m_eZoom = rVOpt.m_eZoom ; + m_nTableDestination = rVOpt.m_nTableDestination ; + m_nUIOptions = rVOpt.m_nUIOptions ; + m_nCoreOptions = rVOpt.m_nCoreOptions ; + m_nCore2Options = rVOpt.m_nCore2Options ; + m_aRetouchColor = rVOpt.GetRetoucheColor(); + m_sSymbolFont = rVOpt.m_sSymbolFont; + m_nShadowCursorFillMode = rVOpt.m_nShadowCursorFillMode; + m_bStarOneSetting = rVOpt.m_bStarOneSetting; + mbBookView = rVOpt.mbBookView; + mbBrowseMode = rVOpt.mbBrowseMode; + mbViewLayoutBookMode = rVOpt.mbViewLayoutBookMode; + mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode; + m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields; + m_bIdle = rVOpt.m_bIdle; + +#ifdef DBG_UTIL + m_bTest1 = rVOpt.m_bTest1; + m_bTest2 = rVOpt.m_bTest2; + m_bTest3 = rVOpt.m_bTest3; + m_bTest4 = rVOpt.m_bTest4; + m_bTest5 = rVOpt.m_bTest5; + m_bTest6 = rVOpt.m_bTest6; + m_bTest7 = rVOpt.m_bTest7; + m_bTest8 = rVOpt.m_bTest8; + m_bTest10 = rVOpt.m_bTest10; +#endif +} + +SwViewOption& SwViewOption::operator=( const SwViewOption &rVOpt ) +{ + // #114856# Form view + mbFormView = rVOpt.mbFormView ; + m_nZoom = rVOpt.m_nZoom ; + m_aSnapSize = rVOpt.m_aSnapSize ; + mnViewLayoutColumns = rVOpt.mnViewLayoutColumns ; + m_nDivisionX = rVOpt.m_nDivisionX ; + m_nDivisionY = rVOpt.m_nDivisionY ; + m_nPagePreviewRow = rVOpt.m_nPagePreviewRow; + m_nPagePreviewCol = rVOpt.m_nPagePreviewCol; + m_bIsPagePreview = rVOpt.m_bIsPagePreview; + m_eZoom = rVOpt.m_eZoom ; + m_nTableDestination = rVOpt.m_nTableDestination ; + m_nUIOptions = rVOpt.m_nUIOptions ; + m_nCoreOptions = rVOpt.m_nCoreOptions; + m_nCore2Options = rVOpt.m_nCore2Options; + m_aRetouchColor = rVOpt.GetRetoucheColor(); + m_sSymbolFont = rVOpt.m_sSymbolFont; + m_nShadowCursorFillMode = rVOpt.m_nShadowCursorFillMode; + m_bStarOneSetting = rVOpt.m_bStarOneSetting; + mbBookView = rVOpt.mbBookView; + mbBrowseMode = rVOpt.mbBrowseMode; + mbViewLayoutBookMode = rVOpt.mbViewLayoutBookMode; + mbHideWhitespaceMode = rVOpt.mbHideWhitespaceMode; + m_bShowPlaceHolderFields = rVOpt.m_bShowPlaceHolderFields; + m_bIdle = rVOpt.m_bIdle; + +#ifdef DBG_UTIL + m_bTest1 = rVOpt.m_bTest1; + m_bTest2 = rVOpt.m_bTest2; + m_bTest3 = rVOpt.m_bTest3; + m_bTest4 = rVOpt.m_bTest4; + m_bTest5 = rVOpt.m_bTest5; + m_bTest6 = rVOpt.m_bTest6; + m_bTest7 = rVOpt.m_bTest7; + m_bTest8 = rVOpt.m_bTest8; + m_bTest10 = rVOpt.m_bTest10; +#endif + return *this; +} + +SwViewOption::~SwViewOption() +{ +} + +void SwViewOption::Init( vcl::Window const *pWin ) +{ + if( !s_nPixelTwips && pWin ) + { + s_nPixelTwips = static_cast<sal_uInt16>(pWin->PixelToLogic( Size(1,1) ).Height()); + } +} + +bool SwViewOption::IsAutoCompleteWords() +{ + const SvxSwAutoFormatFlags& rFlags = SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags(); + return rFlags.bAutoCmpltCollectWords; +} + +void SwViewOption::SetOnlineSpell(bool b) +{ + if (b) + m_nCoreOptions |= ViewOptFlags1::OnlineSpell; + else + m_nCoreOptions &= ~ViewOptFlags1::OnlineSpell; +} + +AuthorCharAttr::AuthorCharAttr() : + m_nItemId (SID_ATTR_CHAR_UNDERLINE), + m_nAttr (LINESTYLE_SINGLE), + m_nColor (COL_TRANSPARENT) +{ +} + +sal_uInt16 GetHtmlMode(const SwDocShell* pShell) +{ + sal_uInt16 nRet = 0; + if(!pShell || dynamic_cast<const SwWebDocShell*>( pShell) ) + { + nRet = HTMLMODE_ON | HTMLMODE_SOME_STYLES; + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + switch ( rHtmlOpt.GetExportMode() ) + { + case HTML_CFG_MSIE: + nRet |= HTMLMODE_FULL_STYLES; + break; + case HTML_CFG_NS40: + // no special features for this browser + break; + case HTML_CFG_WRITER: + nRet |= HTMLMODE_FULL_STYLES; + break; + } + } + return nRet; +} + +Color& SwViewOption::GetDocColor() +{ + return s_aDocColor; +} + +Color& SwViewOption::GetDocBoundariesColor() +{ + return s_aDocBoundColor; +} + +Color& SwViewOption::GetObjectBoundariesColor() +{ + return s_aObjectBoundColor; +} + +Color& SwViewOption::GetAppBackgroundColor() +{ + return s_aAppBackgroundColor; +} + +Color& SwViewOption::GetTableBoundariesColor() +{ + return s_aTableBoundColor; +} + +Color& SwViewOption::GetIndexShadingsColor() +{ + return s_aIndexShadingsColor; +} + +Color& SwViewOption::GetLinksColor() +{ + return s_aLinksColor; +} + +Color& SwViewOption::GetVisitedLinksColor() +{ + return s_aVisitedLinksColor; +} + +Color& SwViewOption::GetDirectCursorColor() +{ + return s_aDirectCursorColor; +} + +Color& SwViewOption::GetTextGridColor() +{ + return s_aTextGridColor; +} + +Color& SwViewOption::GetSpellColor() +{ + return s_aSpellColor; +} + +Color& SwViewOption::GetSmarttagColor() +{ + return s_aSmarttagColor; +} + +Color& SwViewOption::GetShadowColor() +{ + return s_aShadowColor; +} + +Color& SwViewOption::GetFontColor() +{ + return s_aFontColor; +} + +Color& SwViewOption::GetFieldShadingsColor() +{ + return s_aFieldShadingsColor; +} + +Color& SwViewOption::GetSectionBoundColor() +{ + return s_aSectionBoundColor; +} + +Color& SwViewOption::GetPageBreakColor() +{ + return s_aPageBreakColor; +} + +Color& SwViewOption::GetHeaderFooterMarkColor() +{ + return s_aHeaderFooterMarkColor; +} + +void SwViewOption::ApplyColorConfigValues(const svtools::ColorConfig& rConfig ) +{ + s_aDocColor = rConfig.GetColorValue(svtools::DOCCOLOR).nColor; + + svtools::ColorConfigValue aValue = rConfig.GetColorValue(svtools::DOCBOUNDARIES); + s_aDocBoundColor = aValue.nColor; + s_nAppearanceFlags = ViewOptFlags::NONE; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::DocBoundaries; + + s_aAppBackgroundColor = rConfig.GetColorValue(svtools::APPBACKGROUND).nColor; + + aValue = rConfig.GetColorValue(svtools::OBJECTBOUNDARIES); + s_aObjectBoundColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::ObjectBoundaries; + + aValue = rConfig.GetColorValue(svtools::TABLEBOUNDARIES); + s_aTableBoundColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::TableBoundaries; + + aValue = rConfig.GetColorValue(svtools::WRITERIDXSHADINGS); + s_aIndexShadingsColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::IndexShadings; + + aValue = rConfig.GetColorValue(svtools::LINKS); + s_aLinksColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::Links; + + aValue = rConfig.GetColorValue(svtools::LINKSVISITED); + s_aVisitedLinksColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::VisitedLinks; + + aValue = rConfig.GetColorValue(svtools::SHADOWCOLOR); + s_aShadowColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::Shadow; + + s_aDirectCursorColor = rConfig.GetColorValue(svtools::WRITERDIRECTCURSOR).nColor; + + s_aTextGridColor = rConfig.GetColorValue(svtools::WRITERTEXTGRID).nColor; + + s_aSpellColor = rConfig.GetColorValue(svtools::SPELL).nColor; + + s_aSmarttagColor = rConfig.GetColorValue(svtools::SMARTTAGS).nColor; + + s_aFontColor = rConfig.GetColorValue(svtools::FONTCOLOR).nColor; + + aValue = rConfig.GetColorValue(svtools::WRITERFIELDSHADINGS); + s_aFieldShadingsColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::FieldShadings; + + aValue = rConfig.GetColorValue(svtools::WRITERSECTIONBOUNDARIES); + s_aSectionBoundColor = aValue.nColor; + if(aValue.bIsVisible) + s_nAppearanceFlags |= ViewOptFlags::SectionBoundaries; + + aValue = rConfig.GetColorValue(svtools::WRITERPAGEBREAKS); + s_aPageBreakColor = aValue.nColor; + + aValue = rConfig.GetColorValue(svtools::WRITERHEADERFOOTERMARK); + s_aHeaderFooterMarkColor = aValue.nColor; + + s_aScriptIndicatorColor = rConfig.GetColorValue(svtools::WRITERSCRIPTINDICATOR).nColor; +} + +void SwViewOption::SetAppearanceFlag(ViewOptFlags nFlag, bool bSet, bool bSaveInConfig ) +{ + if(bSet) + s_nAppearanceFlags |= nFlag; + else + s_nAppearanceFlags &= ~nFlag; + if(bSaveInConfig) + { + //create an editable svtools::ColorConfig and store the change + svtools::EditableColorConfig aEditableConfig; + struct FlagToConfig_Impl + { + ViewOptFlags nFlag; + svtools::ColorConfigEntry eEntry; + }; + static const FlagToConfig_Impl aFlags[] = + { + { ViewOptFlags::DocBoundaries , svtools::DOCBOUNDARIES }, + { ViewOptFlags::ObjectBoundaries , svtools::OBJECTBOUNDARIES }, + { ViewOptFlags::TableBoundaries , svtools::TABLEBOUNDARIES }, + { ViewOptFlags::IndexShadings , svtools::WRITERIDXSHADINGS }, + { ViewOptFlags::Links , svtools::LINKS }, + { ViewOptFlags::VisitedLinks , svtools::LINKSVISITED }, + { ViewOptFlags::FieldShadings , svtools::WRITERFIELDSHADINGS }, + { ViewOptFlags::SectionBoundaries , svtools::WRITERSECTIONBOUNDARIES }, + { ViewOptFlags::Shadow , svtools::SHADOWCOLOR }, + { ViewOptFlags::NONE , svtools::ColorConfigEntryCount } + }; + sal_uInt16 nPos = 0; + while(aFlags[nPos].nFlag != ViewOptFlags::NONE) + { + if(nFlag & aFlags[nPos].nFlag) + { + svtools::ColorConfigValue aValue = aEditableConfig.GetColorValue(aFlags[nPos].eEntry); + aValue.bIsVisible = bSet; + aEditableConfig.SetColorValue(aFlags[nPos].eEntry, aValue); + } + nPos++; + } + } +} + +bool SwViewOption::IsAppearanceFlag(ViewOptFlags nFlag) +{ + return bool(s_nAppearanceFlags & nFlag); +} + +namespace{ +rtl::Reference<comphelper::ConfigurationListener> const & getWCOptionListener() +{ + static rtl::Reference<comphelper::ConfigurationListener> xListener(new comphelper::ConfigurationListener("/org.openoffice.Office.Writer/Cursor/Option")); + return xListener; +} +} + +bool SwViewOption::IsIgnoreProtectedArea() +{ + static comphelper::ConfigurationListenerProperty<bool> gIgnoreProtectedArea(getWCOptionListener(), "IgnoreProtectedArea"); + return gIgnoreProtectedArea.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/README b/sw/source/uibase/dbui/README new file mode 100644 index 000000000..7af8c114f --- /dev/null +++ b/sw/source/uibase/dbui/README @@ -0,0 +1,48 @@ +Mail merge (MM) has *four modes*. The modes 1-3 are directly exposed to the +user via different GUIs. The modes are: + +1. FILE = saves the result as documents +2. PRINTER = directly prints resulting documents +3. EMAIL = sends results as inidividual emails +4. SHELL = returns an internal document shell for further programming + +There is one property, which changes the overall behaviour of these modes: +*bCreateSingleFile*. This is the primary controller of the mail merge source +code workflow, as it affects all modes! + +This also has timing constraints: for individual files the result can be +defined in advance, while further processing of the single result can just +be done after the merging. It mainly affects printing and the generation +of huge (1000+ datasets) combined non-PRINT merge results. A valid +combined document has to follow constraints, which are already met by +individual documents, as these are just modified copies. + +LO currently has five working combinations of mode and *bCreateSingleFile* +The others, ''PRINTER+false'', ''EMAIL+true'' and ''SHELL+false'', are currently not +implemented. But the list contains implementation proposals for them. + +* Mode: FILE +** false: Saves each merged document as an individual file. The file name can + be selected from a database column! +** true: Saves a combined file of concatenated documents. Each document starts + with a new / reset page style. Depending on the page style this inserts + hidden blank pages +* Mode: PRINTER +** false: *not implemented*. This could generate individual print jobs, which could be grouped on some + platforms for easier abortion (needs verification!). All print options + affect only the individual document! Printing could start after the first + document is generated. +** true: Generates a single print job. All print options affect the combined + document! This especially effects the results of reverse and N-UP printing. + Printing can just start after all documents are generated. +* Mode: EMAIL +** false: Each document can either be used as the email body (html or txt) or + be attached to a general email text. The emails are extracted from a DB + column, which must be provided! +** true: *not implemented*. Could send the combined document to a single + email address. +* Mode: SHELL +** false: *not implemented*. Instead of a single shell this could return a + sequence of shells for the individual documents. +** true: Returns the shell of the generated document. This is mainly + interesting for programmers. The document is just generated internally. diff --git a/sw/source/uibase/dbui/dbmgr.cxx b/sw/source/uibase/dbui/dbmgr.cxx new file mode 100644 index 000000000..309d74ae8 --- /dev/null +++ b/sw/source/uibase/dbui/dbmgr.cxx @@ -0,0 +1,3366 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cassert> + +#include <unotxdoc.hxx> +#include <sfx2/app.hxx> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp> +#include <com/sun/star/util/NumberFormatter.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/TextConnectionSettings.hpp> +#include <com/sun/star/sdb/XCompletedConnection.hpp> +#include <com/sun/star/sdb/XCompletedExecution.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/text/MailMergeEvent.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <vcl/errinf.hxx> +#include <vcl/print.hxx> +#include <vcl/scheduler.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/viewfrm.hxx> +#include <dbconfig.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/zforlist.hxx> +#include <svl/stritem.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/dispatch.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <fldbas.hxx> +#include <dbui.hxx> +#include <dbmgr.hxx> +#include <doc.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swwait.hxx> +#include <swunohelper.hxx> +#include <strings.hrc> +#include <mmconfigitem.hxx> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/ResultSetType.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/mail/MailAttachment.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/property.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/string.hxx> +#include <comphelper/types.hxx> +#include <mailmergehelper.hxx> +#include <maildispatcher.hxx> +#include <svtools/htmlcfg.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <svl/numuno.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbconversion.hxx> +#include <unotools/charclass.hxx> +#include <tools/diagnose_ex.h> + +#include <unomailmerge.hxx> +#include <sfx2/event.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <osl/mutex.hxx> +#include <rtl/textenc.h> +#include <rtl/tencinfo.h> +#include <cppuhelper/implbase.hxx> +#include <ndindex.hxx> +#include <swevent.hxx> +#include <sal/log.hxx> +#include <swabstdlg.hxx> +#include <vector> +#include <section.hxx> +#include <rootfrm.hxx> +#include <calc.hxx> +#include <dbfld.hxx> +#include <IDocumentState.hxx> +#include <imaildsplistener.hxx> +#include <iodetect.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <memory> +#include <comphelper/propertysequence.hxx> + +using namespace ::com::sun::star; +using namespace sw; + +#define DB_SEP_SPACE 0 +#define DB_SEP_TAB 1 +#define DB_SEP_RETURN 2 +#define DB_SEP_NEWLINE 3 + +namespace { + +void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell) +{ + SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId, + SwDocShell::GetEventName(nStrId), + pDocShell)); +} + +} + +std::vector<std::pair<SwDocShell*, OUString>> SwDBManager::m_aUncommittedRegistrations; + +namespace { + +enum class SwDBNextRecord { NEXT, FIRST }; + +} + +static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action = SwDBNextRecord::NEXT ); + +namespace { + +enum class WorkingDocType { SOURCE, TARGET, COPY }; + +} + +static SfxObjectShell* lcl_CreateWorkingDocument( + const WorkingDocType aType, const SwWrtShell &rSourceWrtShell, + const vcl::Window *pSourceWindow, + SwDBManager** const ppDBManager, + SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc ); + +static bool lcl_getCountFromResultSet( sal_Int32& rCount, const SwDSParam* pParam ) +{ + rCount = pParam->aSelection.getLength(); + if ( rCount > 0 ) + return true; + + uno::Reference<beans::XPropertySet> xPrSet(pParam->xResultSet, uno::UNO_QUERY); + if ( xPrSet.is() ) + { + try + { + bool bFinal = false; + uno::Any aFinal = xPrSet->getPropertyValue("IsRowCountFinal"); + aFinal >>= bFinal; + if(!bFinal) + { + pParam->xResultSet->last(); + pParam->xResultSet->first(); + } + uno::Any aCount = xPrSet->getPropertyValue("RowCount"); + if( aCount >>= rCount ) + return true; + } + catch(const uno::Exception&) + { + } + } + return false; +} + +class SwDBManager::ConnectionDisposedListener_Impl + : public cppu::WeakImplHelper< lang::XEventListener > +{ +private: + SwDBManager * m_pDBManager; + + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + +public: + explicit ConnectionDisposedListener_Impl(SwDBManager& rMgr); + + void Dispose() { m_pDBManager = nullptr; } + +}; + +namespace { + +/// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal. +class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener> +{ + uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext; + SwDBManager* m_pDBManager; + +public: + explicit SwDataSourceRemovedListener(SwDBManager& rDBManager); + virtual ~SwDataSourceRemovedListener() override; + virtual void SAL_CALL registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override; + virtual void SAL_CALL revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override; + virtual void SAL_CALL changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override; + virtual void SAL_CALL disposing(const lang::EventObject& rObject) override; + void Dispose(); +}; + +} + +SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager& rDBManager) + : m_pDBManager(&rDBManager) +{ + uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext()); + m_xDatabaseContext = sdb::DatabaseContext::create(xComponentContext); + m_xDatabaseContext->addDatabaseRegistrationsListener(this); +} + +SwDataSourceRemovedListener::~SwDataSourceRemovedListener() +{ + if (m_xDatabaseContext.is()) + m_xDatabaseContext->removeDatabaseRegistrationsListener(this); +} + +void SAL_CALL SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& /*rEvent*/) +{ +} + +void SAL_CALL SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) +{ + if (!m_pDBManager || m_pDBManager->getEmbeddedName().isEmpty()) + return; + + SwDoc* pDoc = m_pDBManager->getDoc(); + if (!pDoc) + return; + + SwDocShell* pDocShell = pDoc->GetDocShell(); + if (!pDocShell) + return; + + OUString aOwnURL = pDocShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset); + OUString sTmpName = "vnd.sun.star.pkg://" + + INetURLObject::encode(aOwnURL, INetURLObject::PART_AUTHORITY, INetURLObject::EncodeMechanism::All); + sTmpName += "/" + m_pDBManager->getEmbeddedName(); + + if (sTmpName != rEvent.OldLocation) + return; + + // The revoked database location is inside this document, then remove the + // embedding, as otherwise it would be back on the next reload of the + // document. + pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName()); + m_pDBManager->setEmbeddedName(OUString(), *pDocShell); +} + +void SAL_CALL SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) +{ + if (rEvent.OldLocation != rEvent.NewLocation) + revokedDatabaseLocation(rEvent); +} + +void SwDataSourceRemovedListener::disposing(const lang::EventObject& /*rObject*/) +{ + m_xDatabaseContext.clear(); +} + +void SwDataSourceRemovedListener::Dispose() +{ + m_pDBManager = nullptr; +} + +struct SwDBManager::SwDBManager_Impl +{ + std::unique_ptr<SwDSParam> pMergeData; + VclPtr<AbstractMailMergeDlg> pMergeDialog; + rtl::Reference<SwDBManager::ConnectionDisposedListener_Impl> m_xDisposeListener; + rtl::Reference<SwDataSourceRemovedListener> m_xDataSourceRemovedListener; + osl::Mutex m_aAllEmailSendMutex; + uno::Reference< mail::XMailMessage> m_xLastMessage; + + explicit SwDBManager_Impl(SwDBManager& rDBManager) + : m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager)) + {} + + ~SwDBManager_Impl() + { + m_xDisposeListener->Dispose(); + if (m_xDataSourceRemovedListener.is()) + m_xDataSourceRemovedListener->Dispose(); + } +}; + +static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<sdbc::XDataSource> const & xSource) +{ + uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + rParam.xFormatter = util::NumberFormatter::create(xContext); + uno::Reference<beans::XPropertySet> xSourceProps( + (xSource.is() + ? xSource + : SwDBManager::getDataSourceAsParent( + rParam.xConnection, rParam.sDataSource)), + uno::UNO_QUERY); + if(xSourceProps.is()) + { + uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier"); + if(aFormats.hasValue()) + { + uno::Reference<util::XNumberFormatsSupplier> xSuppl; + aFormats >>= xSuppl; + if(xSuppl.is()) + { + uno::Reference< beans::XPropertySet > xSettings = xSuppl->getNumberFormatSettings(); + uno::Any aNull = xSettings->getPropertyValue("NullDate"); + aNull >>= rParam.aNullDate; + if(rParam.xFormatter.is()) + rParam.xFormatter->attachNumberFormatsSupplier(xSuppl); + } + } + } +} + +static bool lcl_MoveAbsolute(SwDSParam* pParam, long nAbsPos) +{ + bool bRet = false; + try + { + if(pParam->aSelection.hasElements()) + { + if(pParam->aSelection.getLength() <= nAbsPos) + { + pParam->bEndOfDB = true; + bRet = false; + } + else + { + pParam->nSelectionIndex = nAbsPos; + sal_Int32 nPos = 0; + pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos; + pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos ); + bRet = !pParam->bEndOfDB; + } + } + else if(pParam->bScrollable) + { + bRet = pParam->xResultSet->absolute( nAbsPos ); + } + else + { + OSL_FAIL("no absolute positioning available"); + } + } + catch(const uno::Exception&) + { + } + return bRet; +} + +static void lcl_GetColumnCnt(SwDSParam *pParam, + const uno::Reference< beans::XPropertySet > &rColumnProps, + LanguageType nLanguage, OUString &rResult, double* pNumber) +{ + SwDBFormatData aFormatData; + if(!pParam->xFormatter.is()) + { + uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent( + pParam->xConnection,pParam->sDataSource); + lcl_InitNumberFormatter(*pParam, xSource ); + } + aFormatData.aNullDate = pParam->aNullDate; + aFormatData.xFormatter = pParam->xFormatter; + + aFormatData.aLocale = LanguageTag( nLanguage ).getLocale(); + + rResult = SwDBManager::GetDBField( rColumnProps, aFormatData, pNumber); +} + +static bool lcl_GetColumnCnt(SwDSParam* pParam, const OUString& rColumnName, + LanguageType nLanguage, OUString& rResult, double* pNumber) +{ + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pParam->xResultSet, uno::UNO_QUERY ); + uno::Reference<container::XNameAccess> xCols; + try + { + xCols = xColsSupp->getColumns(); + } + catch(const lang::DisposedException&) + { + } + if(!xCols.is() || !xCols->hasByName(rColumnName)) + return false; + uno::Any aCol = xCols->getByName(rColumnName); + uno::Reference< beans::XPropertySet > xColumnProps; + aCol >>= xColumnProps; + lcl_GetColumnCnt( pParam, xColumnProps, nLanguage, rResult, pNumber ); + return true; +}; + +// import data +bool SwDBManager::Merge( const SwMergeDescriptor& rMergeDesc ) +{ + assert( !m_bInMerge && !m_pImpl->pMergeData && "merge already activated!" ); + + SfxObjectShellLock xWorkObjSh; + SwWrtShell *pWorkShell = nullptr; + SwDoc *pWorkDoc = nullptr; + SwDBManager *pWorkDocOrigDBManager = nullptr; + + switch( rMergeDesc.nMergeType ) + { + case DBMGR_MERGE_PRINTER: + case DBMGR_MERGE_EMAIL: + case DBMGR_MERGE_FILE: + case DBMGR_MERGE_SHELL: + { + SwDocShell *pSourceDocSh = rMergeDesc.rSh.GetView().GetDocShell(); + if( pSourceDocSh->IsModified() ) + { + pWorkDocOrigDBManager = this; + xWorkObjSh = lcl_CreateWorkingDocument( + WorkingDocType::SOURCE, rMergeDesc.rSh, nullptr, + &pWorkDocOrigDBManager, nullptr, &pWorkShell, &pWorkDoc ); + } + [[fallthrough]]; + } + + default: + if( !xWorkObjSh.Is() ) + pWorkShell = &rMergeDesc.rSh; + break; + } + + SwDBData aData; + aData.nCommandType = sdb::CommandType::TABLE; + uno::Reference<sdbc::XResultSet> xResSet; + uno::Sequence<uno::Any> aSelection; + uno::Reference< sdbc::XConnection> xConnection; + + aData.sDataSource = rMergeDesc.rDescriptor.getDataSource(); + rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Command] >>= aData.sCommand; + rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= aData.nCommandType; + + if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Cursor) ) + rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Cursor] >>= xResSet; + if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Selection) ) + rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection; + if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Connection) ) + rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection; + + if((aData.sDataSource.isEmpty() || aData.sCommand.isEmpty()) && !xResSet.is()) + { + return false; + } + + m_pImpl->pMergeData.reset(new SwDSParam(aData, xResSet, aSelection)); + SwDSParam* pTemp = FindDSData(aData, false); + if(pTemp) + *pTemp = *m_pImpl->pMergeData; + else + { + // calls from the calculator may have added a connection with an invalid commandtype + //"real" data base connections added here have to re-use the already available + //DSData and set the correct CommandType + aData.nCommandType = -1; + pTemp = FindDSData(aData, false); + if(pTemp) + *pTemp = *m_pImpl->pMergeData; + else + { + m_DataSourceParams.push_back(std::make_unique<SwDSParam>(*m_pImpl->pMergeData)); + try + { + uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY); + if(xComponent.is()) + xComponent->addEventListener(m_pImpl->m_xDisposeListener.get()); + } + catch(const uno::Exception&) + { + } + } + } + if(!m_pImpl->pMergeData->xConnection.is()) + m_pImpl->pMergeData->xConnection = xConnection; + // add an XEventListener + + lcl_ToNextRecord(m_pImpl->pMergeData.get(), SwDBNextRecord::FIRST); + + uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,aData.sDataSource); + + lcl_InitNumberFormatter(*m_pImpl->pMergeData, xSource); + + pWorkShell->ChgDBData(aData); + m_bInMerge = true; + + if (IsInitDBFields()) + { + // with database fields without DB-Name, use DB-Name from Doc + std::vector<OUString> aDBNames; + aDBNames.emplace_back(); + SwDBData aInsertData = pWorkShell->GetDBData(); + OUString sDBName = aInsertData.sDataSource + + OUStringChar(DB_DELIM) + aInsertData.sCommand + + OUStringChar(DB_DELIM) + + OUString::number(aInsertData.nCommandType); + pWorkShell->ChangeDBFields( aDBNames, sDBName); + SetInitDBFields(false); + } + + bool bRet = true; + switch(rMergeDesc.nMergeType) + { + case DBMGR_MERGE: + pWorkShell->StartAllAction(); + pWorkShell->SwViewShell::UpdateFields( true ); + pWorkShell->SetModified(); + pWorkShell->EndAllAction(); + break; + + case DBMGR_MERGE_PRINTER: + case DBMGR_MERGE_EMAIL: + case DBMGR_MERGE_FILE: + case DBMGR_MERGE_SHELL: + // save files and send them as e-Mail if required + bRet = MergeMailFiles(pWorkShell, rMergeDesc); + break; + + default: + // insert selected entries + // (was: InsertRecord) + ImportFromConnection(pWorkShell); + break; + } + + m_pImpl->pMergeData.reset(); + + if( xWorkObjSh.Is() ) + { + pWorkDoc->SetDBManager( pWorkDocOrigDBManager ); + xWorkObjSh->DoClose(); + } + + m_bInMerge = false; + + return bRet; +} + +void SwDBManager::ImportFromConnection( SwWrtShell* pSh ) +{ + if(m_pImpl->pMergeData && !m_pImpl->pMergeData->bEndOfDB) + { + pSh->StartAllAction(); + pSh->StartUndo(); + bool bGroupUndo(pSh->DoesGroupUndo()); + pSh->DoGroupUndo(false); + + if( pSh->HasSelection() ) + pSh->DelRight(); + + std::unique_ptr<SwWait> pWait; + + { + sal_uLong i = 0; + do { + + ImportDBEntry(pSh); + if( 10 == ++i ) + pWait.reset(new SwWait( *pSh->GetView().GetDocShell(), true)); + + } while(ToNextMergeRecord()); + } + + pSh->DoGroupUndo(bGroupUndo); + pSh->EndUndo(); + pSh->EndAllAction(); + } +} + +static OUString lcl_FindColumn(const OUString& sFormatStr,sal_uInt16 &nUsedPos, sal_uInt8 &nSeparator) +{ + OUStringBuffer sReturn; + sal_uInt16 nLen = sFormatStr.getLength(); + nSeparator = 0xff; + while(nUsedPos < nLen && nSeparator == 0xff) + { + sal_Unicode cCurrent = sFormatStr[nUsedPos]; + switch(cCurrent) + { + case ',': + nSeparator = DB_SEP_SPACE; + break; + case ';': + nSeparator = DB_SEP_RETURN; + break; + case ':': + nSeparator = DB_SEP_TAB; + break; + case '#': + nSeparator = DB_SEP_NEWLINE; + break; + default: + sReturn.append(cCurrent); + } + nUsedPos++; + + } + return sReturn.makeStringAndClear(); +} + +void SwDBManager::ImportDBEntry(SwWrtShell* pSh) +{ + if(m_pImpl->pMergeData && !m_pImpl->pMergeData->bEndOfDB) + { + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY ); + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + OUString sFormatStr; + sal_uInt16 nFormatLen = sFormatStr.getLength(); + if( nFormatLen ) + { + const char cSpace = ' '; + const char cTab = '\t'; + sal_uInt16 nUsedPos = 0; + sal_uInt8 nSeparator; + OUString sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator); + while( !sColumn.isEmpty() ) + { + if(!xCols->hasByName(sColumn)) + return; + uno::Any aCol = xCols->getByName(sColumn); + uno::Reference< beans::XPropertySet > xColumnProp; + aCol >>= xColumnProp; + if(xColumnProp.is()) + { + SwDBFormatData aDBFormat; + OUString sInsert = GetDBField( xColumnProp, aDBFormat); + if( DB_SEP_SPACE == nSeparator ) + sInsert += OUStringChar(cSpace); + else if( DB_SEP_TAB == nSeparator) + sInsert += OUStringChar(cTab); + pSh->Insert(sInsert); + if( DB_SEP_RETURN == nSeparator) + pSh->SplitNode(); + else if(DB_SEP_NEWLINE == nSeparator) + pSh->InsertLineBreak(); + } + else + { + // column not found -> show error + OUString sInsert = "?" + sColumn + "?"; + pSh->Insert(sInsert); + } + sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator); + } + pSh->SplitNode(); + } + else + { + OUStringBuffer sStr; + uno::Sequence<OUString> aColNames = xCols->getElementNames(); + const OUString* pColNames = aColNames.getConstArray(); + long nLength = aColNames.getLength(); + for(long i = 0; i < nLength; i++) + { + uno::Any aCol = xCols->getByName(pColNames[i]); + uno::Reference< beans::XPropertySet > xColumnProp; + aCol >>= xColumnProp; + SwDBFormatData aDBFormat; + sStr.append(GetDBField( xColumnProp, aDBFormat)); + if (i < nLength - 1) + sStr.append("\t"); + } + pSh->SwEditShell::Insert2(sStr.makeStringAndClear()); + pSh->SwFEShell::SplitNode(); // line feed + } + } +} + +bool SwDBManager::GetTableNames(weld::ComboBox& rBox, const OUString& rDBName) +{ + bool bRet = false; + OUString sOldTableName(rBox.get_active_text()); + rBox.clear(); + SwDSParam* pParam = FindDSConnection(rDBName, false); + uno::Reference< sdbc::XConnection> xConnection; + if (pParam && pParam->xConnection.is()) + xConnection = pParam->xConnection; + else + { + if ( !rDBName.isEmpty() ) + xConnection = RegisterConnection( rDBName ); + } + if (xConnection.is()) + { + uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY); + if(xTSupplier.is()) + { + uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables(); + const uno::Sequence<OUString> aTables = xTables->getElementNames(); + for (const OUString& rTable : aTables) + rBox.append("0", rTable); + } + uno::Reference<sdb::XQueriesSupplier> xQSupplier(xConnection, uno::UNO_QUERY); + if(xQSupplier.is()) + { + uno::Reference<container::XNameAccess> xQueries = xQSupplier->getQueries(); + const uno::Sequence<OUString> aQueries = xQueries->getElementNames(); + for (const OUString& rQuery : aQueries) + rBox.append("1", rQuery); + } + if (!sOldTableName.isEmpty()) + rBox.set_active_text(sOldTableName); + bRet = true; + } + return bRet; +} + +// fill Listbox with column names of a database +void SwDBManager::GetColumnNames(weld::ComboBox& rBox, + const OUString& rDBName, const OUString& rTableName) +{ + SwDBData aData; + aData.sDataSource = rDBName; + aData.sCommand = rTableName; + aData.nCommandType = -1; + SwDSParam* pParam = FindDSData(aData, false); + uno::Reference< sdbc::XConnection> xConnection; + if(pParam && pParam->xConnection.is()) + xConnection = pParam->xConnection; + else + { + xConnection = RegisterConnection( rDBName ); + } + GetColumnNames(rBox, xConnection, rTableName); +} + +void SwDBManager::GetColumnNames(weld::ComboBox& rBox, + uno::Reference< sdbc::XConnection> const & xConnection, + const OUString& rTableName) +{ + rBox.clear(); + uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName); + if(xColsSupp.is()) + { + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + const uno::Sequence<OUString> aColNames = xCols->getElementNames(); + for (const OUString& rColName : aColNames) + { + rBox.append_text(rColName); + } + ::comphelper::disposeComponent( xColsSupp ); + } +} + +SwDBManager::SwDBManager(SwDoc* pDoc) + : m_aMergeStatus( MergeStatus::Ok ) + , m_bInitDBFields(false) + , m_bInMerge(false) + , m_bMergeSilent(false) + , m_pImpl(new SwDBManager_Impl(*this)) + , m_pMergeEvtSrc(nullptr) + , m_pDoc(pDoc) +{ +} + +SwDBManager::~SwDBManager() COVERITY_NOEXCEPT_FALSE +{ + RevokeLastRegistrations(); + + // copy required, m_DataSourceParams can be modified while disposing components + std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections; + for (const auto & pParam : m_DataSourceParams) + { + if(pParam->xConnection.is()) + { + aCopiedConnections.push_back(pParam->xConnection); + } + } + for (const auto & xConnection : aCopiedConnections) + { + try + { + uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY); + if(xComp.is()) + xComp->dispose(); + } + catch(const uno::RuntimeException&) + { + //may be disposed already since multiple entries may have used the same connection + } + } +} + +static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell ) +{ + //reset all links of the sections of synchronized labels + size_t nSections = rWorkShell.GetSectionFormatCount(); + for (size_t nSection = 0; nSection < nSections; ++nSection) + { + SwSectionData aSectionData( *rWorkShell.GetSectionFormat( nSection ).GetSection() ); + if( aSectionData.GetType() == SectionType::FileLink ) + { + aSectionData.SetType( SectionType::Content ); + aSectionData.SetLinkFileName( OUString() ); + rWorkShell.UpdateSection( nSection, aSectionData ); + } + } + rWorkShell.SetLabelDoc( false ); +} + +static void lcl_SaveDebugDoc( SfxObjectShell *xTargetDocShell, + const char *name, int no = 0 ) +{ + static OUString sTempDirURL; + if( sTempDirURL.isEmpty() ) + { + SvtPathOptions aPathOpt; + utl::TempFile aTempDir( &aPathOpt.GetTempPath(), true ); + if( aTempDir.IsValid() ) + { + INetURLObject aTempDirURL( aTempDir.GetURL() ); + sTempDirURL = aTempDirURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL ); + } + } + if( sTempDirURL.isEmpty() ) + return; + + const OUString sExt( ".odt" ); + OUString basename = OUString::createFromAscii( name ); + if (no > 0) + basename += OUString::number(no) + "-"; + // aTempFile is not deleted, but that seems to be intentional + utl::TempFile aTempFile( basename, true, &sExt, &sTempDirURL ); + INetURLObject aTempFileURL( aTempFile.GetURL() ); + auto pDstMed = std::make_unique<SfxMedium>( + aTempFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + StreamMode::STD_READWRITE ); + bool bAnyError = !xTargetDocShell->DoSaveAs( *pDstMed ); + // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it + bAnyError |= (ERRCODE_NONE != xTargetDocShell->GetError()); + if( bAnyError ) + SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile.GetURL() ); + else + SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile.GetURL() ); +} + +static bool lcl_SaveDoc( + const INetURLObject* pFileURL, + const std::shared_ptr<const SfxFilter>& pStoreToFilter, + const OUString* pStoreToFilterOptions, + const uno::Sequence< beans::PropertyValue >* pSaveToFilterData, + const bool bIsPDFexport, + SfxObjectShell* xObjectShell, + SwWrtShell& rWorkShell, + OUString * const decodedURL = nullptr ) +{ + OUString url = pFileURL->GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( decodedURL ) + (*decodedURL) = url; + + SfxMedium* pDstMed = new SfxMedium( url, StreamMode::STD_READWRITE ); + pDstMed->SetFilter( pStoreToFilter ); + if( pDstMed->GetItemSet() ) + { + if( pStoreToFilterOptions ) + pDstMed->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS, + *pStoreToFilterOptions)); + if( pSaveToFilterData->hasElements() ) + pDstMed->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA, + uno::makeAny(*pSaveToFilterData))); + } + + // convert fields to text if we are exporting to PDF. + // this prevents a second merge while updating the fields + // in SwXTextDocument::getRendererCount() + if( bIsPDFexport ) + rWorkShell.ConvertFieldsToText(); + + bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed); + // Actually this should be a bool... so in case of email and individual + // files, where this is set, we skip the recently used handling + bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL ); + bAnyError |= (ERRCODE_NONE != xObjectShell->GetError()); + if( bAnyError ) + { + // error message ?? + ErrorHandler::HandleError( xObjectShell->GetError() ); + } + return !bAnyError; +} + +static void lcl_PreparePrinterOptions( + const uno::Sequence< beans::PropertyValue >& rInPrintOptions, + uno::Sequence< beans::PropertyValue >& rOutPrintOptions) +{ + // printing should be done synchronously otherwise the document + // might already become invalid during the process + + const sal_Int32 nOffset = 1; + rOutPrintOptions.realloc( nOffset ); + rOutPrintOptions[ 0 ].Name = "Wait"; + rOutPrintOptions[ 0 ].Value <<= true; + + // copy print options + sal_Int32 nIndex = nOffset; + for( const beans::PropertyValue& rOption : rInPrintOptions) + { + if( rOption.Name == "CopyCount" || rOption.Name == "FileName" + || rOption.Name == "Collate" || rOption.Name == "Pages" + || rOption.Name == "Wait" || rOption.Name == "PrinterName" ) + { + // add an option + rOutPrintOptions.realloc( nIndex + 1 ); + rOutPrintOptions[ nIndex ].Name = rOption.Name; + rOutPrintOptions[ nIndex++ ].Value = rOption.Value ; + } + } +} + +static void lcl_PrepareSaveFilterDataOptions( + const uno::Sequence< beans::PropertyValue >& rInSaveFilterDataptions, + uno::Sequence< beans::PropertyValue >& rOutSaveFilterDataOptions, + const OUString& sPassword) +{ + const sal_Int32 nOffset = 2; + rOutSaveFilterDataOptions.realloc( nOffset ); + rOutSaveFilterDataOptions[ 0 ].Name = "EncryptFile"; + rOutSaveFilterDataOptions[ 0 ].Value <<= true; + rOutSaveFilterDataOptions[ 1 ].Name = "DocumentOpenPassword"; + rOutSaveFilterDataOptions[ 1 ].Value <<= sPassword; + + // copy other options + sal_Int32 nIndex = nOffset; + for( const beans::PropertyValue& rOption : rInSaveFilterDataptions) + { + rOutSaveFilterDataOptions.realloc( nIndex + 1 ); + rOutSaveFilterDataOptions[ nIndex ].Name = rOption.Name; + rOutSaveFilterDataOptions[ nIndex++ ].Value = rOption.Value ; + } + +} + + +static SfxObjectShell* lcl_CreateWorkingDocument( + // input + const WorkingDocType aType, const SwWrtShell &rSourceWrtShell, + // optional input + const vcl::Window *pSourceWindow, + // optional in and output to swap the DB manager + SwDBManager** const ppDBManager, + // optional output + SwView** const pView, SwWrtShell** const pWrtShell, SwDoc** const pDoc ) +{ + const SwDoc *pSourceDoc = rSourceWrtShell.GetDoc(); + SfxObjectShellRef xWorkObjectShell = pSourceDoc->CreateCopy( true, (aType == WorkingDocType::TARGET) ); + SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell, SFX_INTERFACE_NONE ); + + if( pSourceWindow ) + { + // the created window has to be located at the same position as the source window + vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow(); + rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() ); + } + + SwView* pWorkView = static_cast< SwView* >( pWorkFrame->GetViewShell() ); + SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr(); + pWorkWrtShell->GetViewOptions()->SetIdle( false ); + pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called + SwDoc* pWorkDoc = pWorkWrtShell->GetDoc(); + pWorkDoc->GetIDocumentUndoRedo().DoUndo( false ); + pWorkDoc->ReplaceDocumentProperties( *pSourceDoc ); + + // import print settings + const SwPrintData &rPrintData = pSourceDoc->getIDocumentDeviceAccess().getPrintData(); + pWorkDoc->getIDocumentDeviceAccess().setPrintData(rPrintData); + const JobSetup *pJobSetup = pSourceDoc->getIDocumentDeviceAccess().getJobsetup(); + if (pJobSetup) + pWorkDoc->getIDocumentDeviceAccess().setJobsetup(*pJobSetup); + + if( aType == WorkingDocType::TARGET ) + { + assert( !ppDBManager ); + pWorkDoc->SetInMailMerge( true ); + pWorkWrtShell->SetLabelDoc( false ); + } + else + { + // We have to swap the DBmanager of the new doc, so we also need input + assert(ppDBManager && *ppDBManager); + SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager(); + pWorkDoc->SetDBManager( *ppDBManager ); + *ppDBManager = pWorkDBManager; + + if( aType == WorkingDocType::SOURCE ) + { + // the GetDBData call constructs the data, if it's missing - kind of const... + pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() ); + // some DocumentSettings are currently not copied by SwDoc::CreateCopy + pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() ); + pWorkDoc->getIDocumentState().ResetModified(); + } + else + pWorkDoc->getIDocumentLinksAdministration().EmbedAllLinks(); + } + + if( pView ) *pView = pWorkView; + if( pWrtShell ) *pWrtShell = pWorkWrtShell; + if( pDoc ) *pDoc = pWorkDoc; + + return xWorkObjectShell.get(); +} + +static SwMailMessage* lcl_CreateMailFromDoc( + const SwMergeDescriptor &rMergeDescriptor, + const OUString &sFileURL, const OUString &sMailRecipient, + const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding, + const OUString &sAttachmentMimeType ) +{ + SwMailMessage* pMessage = new SwMailMessage; + if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() ) + pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo()); + pMessage->addRecipient( sMailRecipient ); + pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() ); + + OUStringBuffer sBody; + if( rMergeDescriptor.bSendAsAttachment ) + { + sBody = rMergeDescriptor.sMailBody; + mail::MailAttachment aAttach; + aAttach.Data = new SwMailTransferable( sFileURL, + rMergeDescriptor.sAttachmentName, sAttachmentMimeType ); + aAttach.ReadableName = rMergeDescriptor.sAttachmentName; + pMessage->addAttachment( aAttach ); + } + else + { + //read in the temporary file and use it as mail body + SfxMedium aMedium( sFileURL, StreamMode::READ ); + SvStream* pInStream = aMedium.GetInStream(); + assert( pInStream && "no output file created?" ); + if( !pInStream ) + return pMessage; + + pInStream->SetStreamCharSet( sMailEncoding ); + OString sLine; + while ( pInStream->ReadLine( sLine ) ) + { + sBody.append(OStringToOUString( sLine, sMailEncoding )); + sBody.append("\n"); + } + } + pMessage->setSubject( rMergeDescriptor.sSubject ); + uno::Reference< datatransfer::XTransferable> xBody = + new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType ); + pMessage->setBody( xBody ); + + for( const OUString& sCcRecipient : rMergeDescriptor.aCopiesTo ) + pMessage->addCcRecipient( sCcRecipient ); + for( const OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo ) + pMessage->addBccRecipient( sBccRecipient ); + + return pMessage; +} + +class SwDBManager::MailDispatcherListener_Impl : public IMailDispatcherListener +{ + SwDBManager &m_rDBManager; + +public: + explicit MailDispatcherListener_Impl( SwDBManager &rDBManager ) + : m_rDBManager( rDBManager ) {} + + virtual void idle() override {} + + virtual void mailDelivered( uno::Reference< mail::XMailMessage> xMessage ) override + { + osl::MutexGuard aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex ); + if ( m_rDBManager.m_pImpl->m_xLastMessage == xMessage ) + m_rDBManager.m_pImpl->m_xLastMessage.clear(); + } + + virtual void mailDeliveryError( ::rtl::Reference<MailDispatcher> xMailDispatcher, + uno::Reference< mail::XMailMessage>, const OUString& ) override + { + osl::MutexGuard aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex ); + m_rDBManager.m_aMergeStatus = MergeStatus::Error; + m_rDBManager.m_pImpl->m_xLastMessage.clear(); + xMailDispatcher->stop(); + } +}; + +/** + * Please have a look at the README in the same directory, before you make + * larger changes in this function! + */ +bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell, + const SwMergeDescriptor& rMergeDescriptor) +{ + // deconstruct mail merge type for better readability. + // uppercase naming is intentional! + const bool bMT_EMAIL = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL; + const bool bMT_SHELL = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL; + const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER; + const bool bMT_FILE = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE; + + //check if the doc is synchronized and contains at least one linked section + const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1; + const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE ); + const bool bIsMergeSilent = IsMergeSilent(); + + bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile; + OUString sPrefix_ = rMergeDescriptor.sPrefix; + if( bMT_EMAIL ) + { + assert( !rMergeDescriptor.bPrefixIsFilename ); + assert(!bCheckSingleFile_); + bCheckSingleFile_ = false; + } + else if( bMT_SHELL || bMT_PRINTER ) + { + assert(bCheckSingleFile_); + bCheckSingleFile_ = true; + assert(sPrefix_.isEmpty()); + sPrefix_.clear(); + } + const bool bCreateSingleFile = bCheckSingleFile_; + const OUString sDescriptorPrefix = sPrefix_; + + // Setup for dumping debugging documents + static const sal_Int32 nMaxDumpDocs = []() { + if (const char* sEnv = getenv("SW_DEBUG_MAILMERGE_DOCS")) + return OUString(sEnv, strlen(sEnv), osl_getThreadTextEncoding()).toInt32(); + else + return sal_Int32(0); + }(); + + ::rtl::Reference< MailDispatcher > xMailDispatcher; + ::rtl::Reference< IMailDispatcherListener > xMailListener; + OUString sMailBodyMimeType; + rtl_TextEncoding sMailEncoding = ::osl_getThreadTextEncoding(); + + uno::Reference< beans::XPropertySet > xColumnProp; + uno::Reference< beans::XPropertySet > xPasswordColumnProp; + + // Check for (mandatory) email or (optional) filename column + SwDBFormatData aColumnDBFormat; + bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty(); + bool bPasswordColumnName = !rMergeDescriptor.sDBPasswordColumn.isEmpty(); + + if( ! bColumnName ) + { + if( bMT_EMAIL ) + return false; + } + else + { + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY ); + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) ) + return false; + uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn ); + aCol >>= xColumnProp; + + if(bPasswordColumnName) + { + aCol = xCols->getByName( rMergeDescriptor.sDBPasswordColumn ); + aCol >>= xPasswordColumnProp; + } + + aColumnDBFormat.xFormatter = m_pImpl->pMergeData->xFormatter; + aColumnDBFormat.aNullDate = m_pImpl->pMergeData->aNullDate; + + if( bMT_EMAIL ) + { + // Reset internal mail accounting data + m_pImpl->m_xLastMessage.clear(); + + xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) ); + xMailListener = new MailDispatcherListener_Impl( *this ); + xMailDispatcher->addListener( xMailListener ); + if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML) + { + sMailBodyMimeType = "text/html; charset=" + OUString::createFromAscii( + rtl_getBestMimeCharsetFromTextEncoding( sMailEncoding )); + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + sMailEncoding = rHtmlOptions.GetTextEncoding(); + } + else + sMailBodyMimeType = "text/plain; charset=UTF-8; format=flowed"; + } + } + + SwDocShell *pSourceDocSh = pSourceShell->GetView().GetDocShell(); + + // setup the output format + std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter( + pSourceDocSh->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE)); + SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer(); + const OUString* pStoreToFilterOptions = nullptr; + + // if a save_to filter is set then use it - otherwise use the default + if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment ) + { + OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt"); + pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT); + } + else if( !rMergeDescriptor.sSaveToFilter.isEmpty()) + { + std::shared_ptr<const SfxFilter> pFilter = + pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter ); + if(pFilter) + { + pStoreToFilter = pFilter; + if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty()) + pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions; + } + } + const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export"; + + m_aMergeStatus = MergeStatus::Ok; + + // in case of creating a single resulting file this has to be created here + SwView* pTargetView = rMergeDescriptor.pMailMergeConfigItem ? + rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr; + SwWrtShell* pTargetShell = nullptr; + SwDoc* pTargetDoc = nullptr; + SfxObjectShellRef xTargetDocShell; + + std::unique_ptr< utl::TempFile > aTempFile; + sal_uInt16 nStartingPageNo = 0; + + vcl::Window *pSourceWindow = nullptr; + std::shared_ptr<weld::GenericDialogController> xProgressDlg; + + try + { + if( !bIsMergeSilent ) + { + // construct the process dialog + pSourceWindow = &pSourceShell->GetView().GetEditWin(); + if (!bMT_PRINTER) + xProgressDlg = std::make_shared<CreateMonitor>(pSourceWindow->GetFrameWeld()); + else + { + xProgressDlg = std::make_shared<PrintMonitor>(pSourceWindow->GetFrameWeld()); + static_cast<PrintMonitor*>(xProgressDlg.get())->set_title( + pSourceDocSh->GetTitle(22)); + } + weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){ + if (nResult == RET_CANCEL) + MergeCancel(); + xProgressDlg.reset(); + }); + + Application::Reschedule( true ); + } + + if( bCreateSingleFile && !pTargetView ) + { + // create a target docshell to put the merged document into + xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET, + *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr, + nullptr, &pTargetView, &pTargetShell, &pTargetDoc ); + + if (nMaxDumpDocs) + lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" ); + } + else if( pTargetView ) + { + pTargetShell = pTargetView->GetWrtShellPtr(); + pTargetDoc = pTargetShell->GetDoc(); + xTargetDocShell = pTargetView->GetDocShell(); + } + + if( bCreateSingleFile ) + { + // determine the page style and number used at the start of the source document + pSourceShell->SttEndDoc(true); + nStartingPageNo = pSourceShell->GetVirtPageNum(); + } + + // Progress, to prohibit KeyInputs + SfxProgress aProgress(pSourceDocSh, OUString(), 1); + + // lock all dispatchers + SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh); + while (pViewFrame) + { + pViewFrame->GetDispatcher()->Lock(true); + pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh); + } + + sal_Int32 nDocNo = 1; + + // For single file mode, the number of pages in the target document so far, which is used + // by AppendDoc() to adjust position of page-bound objects. Getting this information directly + // from the target doc would require repeated layouts of the doc, which is expensive, but + // it can be manually computed from the source documents (for which we do layouts, so the page + // count is known, and there is a blank page between each of them in the target document). + int targetDocPageCount = 0; + + if( !bIsMergeSilent && !bMT_PRINTER ) + { + sal_Int32 nRecordCount = 1; + lcl_getCountFromResultSet( nRecordCount, m_pImpl->pMergeData.get() ); + + // Synchronized docs don't auto-advance the record set, but there is a + // "security" check, which will always advance the record set, if there + // is no "next record" field in a synchronized doc => nRecordPerDoc > 0 + sal_Int32 nRecordPerDoc = pSourceShell->GetDoc() + ->getIDocumentFieldsAccess().GetRecordsPerDocument(); + if ( bSynchronizedDoc && (nRecordPerDoc > 1) ) + --nRecordPerDoc; + assert( nRecordPerDoc > 0 ); + + sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc; + if ( 0 != nRecordCount % nRecordPerDoc ) + nMaxDocs += 1; + static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs); + } + + long nStartRow, nEndRow; + bool bFreezedLayouts = false; + // to collect temporary email files + std::vector< OUString> aFilesToRemove; + + // The SfxObjectShell will be closed explicitly later but + // it is more safe to use SfxObjectShellLock here + SfxObjectShellLock xWorkDocSh; + SwView* pWorkView = nullptr; + SwDoc* pWorkDoc = nullptr; + SwDBManager* pWorkDocOrigDBManager = nullptr; + SwWrtShell* pWorkShell = nullptr; + bool bWorkDocInitialized = false; + + do + { + nStartRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0; + + OUString sColumnData; + + // Read the indicated data column, which should contain a valid mail + // address or an optional file name + if( bMT_EMAIL || bColumnName ) + { + sColumnData = GetDBField( xColumnProp, aColumnDBFormat ); + } + + // create a new temporary file name - only done once in case of bCreateSingleFile + if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile )) + { + OUString sPrefix = sDescriptorPrefix; + OUString sLeading; + + //#i97667# if the name is from a database field then it will be used _as is_ + if( bColumnName && !bMT_EMAIL ) + { + if (!sColumnData.isEmpty()) + sLeading = sColumnData; + else + sLeading = "_"; + } + else + { + INetURLObject aEntry( sPrefix ); + sLeading = aEntry.GetBase(); + aEntry.removeSegment(); + sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*')); + aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPrefix, true) ); + if( !aTempFile->IsValid() ) + { + ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED ); + m_aMergeStatus = MergeStatus::Error; + } + } + + OUString sPasswordColumnData; + uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions( rMergeDescriptor.aSaveToFilterData ); + + if( bMT_EMAIL || bPasswordColumnName ) + { + sPasswordColumnData = GetDBField( xPasswordColumnProp, aColumnDBFormat ); + lcl_PrepareSaveFilterDataOptions( rMergeDescriptor.aSaveToFilterData, aSaveToFilterDataOptions, sPasswordColumnData ); + } + + if( IsMergeOk() ) + { + std::unique_ptr< INetURLObject > aTempFileURL; + if( bNeedsTempFiles ) + aTempFileURL.reset( new INetURLObject(aTempFile->GetURL())); + if( !bIsMergeSilent ) { + if( !bMT_PRINTER ) + static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo); + else { + PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get()); + pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles + ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2)); + OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number( nDocNo ); + pPrintMonDlg->m_xPrintInfo->set_label(sStat); + } + //TODO xProgressDlg->queue_draw(); + } + + Scheduler::ProcessEventsToIdle(); + + // Create a copy of the source document and work with that one instead of the source. + // If we're not in the single file mode (which requires modifying the document for the merging), + // it is enough to do this just once. Currently PDF also has to be treated special. + if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport ) + { + assert( !xWorkDocSh.Is()); + pWorkDocOrigDBManager = this; + xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY, + *pSourceShell, nullptr, &pWorkDocOrigDBManager, + &pWorkView, &pWorkShell, &pWorkDoc ); + if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) ) + lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo ); + + // #i69458# lock fields to prevent access to the result set while calculating layout + // tdf#92324: and do not unlock: keep document locked during printing to avoid + // ExpFields update during printing, generation of preview, etc. + pWorkShell->LockExpFields(); + pWorkShell->CalcLayout(); + // tdf#121168: Now force correct page descriptions applied to page frames. Without + // this, e.g., page frames starting with sections could have page descriptions set + // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below. + pWorkShell->GetViewOptions()->SetIdle(true); + for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts()) + { + aLayout->FreezeLayout(false); + aLayout->AllCheckPageDescs(); + } + } + + lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh); + + // tdf#92324: Allow ExpFields update only by explicit instruction to avoid + // database cursor movement on any other fields update, for example during + // print preview and other operations + if ( pWorkShell->IsExpFieldsLocked() ) + pWorkShell->UnlockExpFields(); + pWorkShell->SwViewShell::UpdateFields(); + pWorkShell->LockExpFields(); + + lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh); + + // also emit MailMergeEvent on XInterface if possible + const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc(); + if(pEvtSrc) + { + uno::Reference< uno::XInterface > xRef( + static_cast<text::XMailMergeBroadcaster*>(const_cast<SwXMailMerge*>(pEvtSrc)) ); + text::MailMergeEvent aEvt( xRef, xWorkDocSh->GetModel() ); + pEvtSrc->LaunchMailMergeEvent( aEvt ); + } + + // working copy is merged - prepare final steps depending on merge options + + if( bCreateSingleFile ) + { + assert( pTargetShell && "no target shell available!" ); + + // prepare working copy and target to append + + pWorkDoc->RemoveInvisibleContent(); + // remove of invisible content has influence on page count and so on fields for page count, + // therefore layout has to be updated before fields are converted to text + pWorkShell->CalcLayout(); + pWorkShell->ConvertFieldsToText(); + pWorkShell->SetNumberingRestart(); + if( bSynchronizedDoc ) + { + lcl_RemoveSectionLinks( *pWorkShell ); + } + + if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) ) + lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo ); + + // append the working document to the target document + if( targetDocPageCount % 2 == 1 ) + ++targetDocPageCount; // Docs always start on odd pages (so offset must be even). + SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc, + nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo); + targetDocPageCount += pWorkShell->GetPageCnt(); + + if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) ) + lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" ); + + if (bMT_SHELL) + { + SwDocMergeInfo aMergeInfo; + // Name of the mark is actually irrelevant, UNO bookmarks have internals names. + aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark( + appendedDocStart, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK, + ::sw::mark::InsertMode::New); + aMergeInfo.nDBRow = nStartRow; + rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo ); + } + } + else + { + assert( bNeedsTempFiles ); + assert( pWorkShell->IsExpFieldsLocked() ); + + // fields are locked, so it's fine to + // restore the old / empty DB manager for save + pWorkDoc->SetDBManager( pWorkDocOrigDBManager ); + + // save merged document + OUString sFileURL; + if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions, + &aSaveToFilterDataOptions, bIsPDFexport, + xWorkDocSh, *pWorkShell, &sFileURL ) ) + { + m_aMergeStatus = MergeStatus::Error; + } + + // back to the MM DB manager + pWorkDoc->SetDBManager( this ); + + if( bMT_EMAIL && !IsMergeError() ) + { + // schedule file for later removal + aFilesToRemove.push_back( sFileURL ); + + if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) ) + { + OSL_FAIL("invalid e-Mail address in database column"); + } + else + { + uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc( + rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType, + sMailEncoding, pStoreToFilter->GetMimeType() ); + if( xMessage.is() ) + { + osl::MutexGuard aGuard( m_pImpl->m_aAllEmailSendMutex ); + m_pImpl->m_xLastMessage.set( xMessage ); + xMailDispatcher->enqueueMailMessage( xMessage ); + if( !xMailDispatcher->isStarted() ) + xMailDispatcher->start(); + } + } + } + } + if( bCreateSingleFile || bIsPDFexport ) + { + pWorkDoc->SetDBManager( pWorkDocOrigDBManager ); + xWorkDocSh->DoClose(); + xWorkDocSh = nullptr; + } + } + + bWorkDocInitialized = true; + nDocNo++; + nEndRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0; + + // Freeze the layouts of the target document after the first inserted + // sub-document, to get the correct PageDesc. + if(!bFreezedLayouts && bCreateSingleFile) + { + for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() ) + aLayout->FreezeLayout(true); + bFreezedLayouts = true; + } + } while( IsMergeOk() && + ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord())); + + if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() ) + { + // Unlock document fields after merge complete + pWorkView->GetWrtShell().UnlockExpFields(); + } + + if( !bCreateSingleFile ) + { + if( bMT_PRINTER ) + Printer::FinishPrintJob( pWorkView->GetPrinterController()); + if( !bIsPDFexport ) + { + pWorkDoc->SetDBManager( pWorkDocOrigDBManager ); + xWorkDocSh->DoClose(); + } + } + else if( IsMergeOk() ) // && bCreateSingleFile + { + Application::Reschedule( true ); + + // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate + // unique fly names, do it here once. + pTargetDoc->SetInMailMerge(false); + pTargetDoc->SetAllUniqueFlyNames(); + + // Unfreeze target document layouts and correct all PageDescs. + SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" ); + pTargetShell->CalcLayout(); + SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" ); + pTargetShell->GetViewOptions()->SetIdle( true ); + pTargetDoc->GetIDocumentUndoRedo().DoUndo( true ); + for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() ) + { + aLayout->FreezeLayout(false); + aLayout->AllCheckPageDescs(); + } + + Application::Reschedule( true ); + + if( IsMergeOk() && bMT_FILE ) + { + // save merged document + assert( aTempFile ); + INetURLObject aTempFileURL; + if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename) + aTempFileURL.SetURL( aTempFile->GetURL() ); + else + { + aTempFileURL.SetURL(sDescriptorPrefix); + // remove the unneeded temporary file + aTempFile->EnableKillingFile(); + } + if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter, + pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData, + bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) ) + { + m_aMergeStatus = MergeStatus::Error; + } + } + else if( IsMergeOk() && bMT_PRINTER ) + { + // print the target document + uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions ); + lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions ); + pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ ); + } + } + + // we also show canceled documents, as long as there was no error + if( !IsMergeError() && bMT_SHELL ) + // leave docshell available for caller (e.g. MM wizard) + rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView ); + else if( xTargetDocShell.is() ) + xTargetDocShell->DoClose(); + + Application::Reschedule( true ); + + if (xProgressDlg) + { + xProgressDlg->response(RET_OK); + } + + // unlock all dispatchers + pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh); + while (pViewFrame) + { + pViewFrame->GetDispatcher()->Lock(false); + pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh); + } + + SW_MOD()->SetView(&pSourceShell->GetView()); + + if( xMailDispatcher.is() ) + { + if( IsMergeOk() ) + { + // TODO: Instead of polling via an AutoTimer, post an Idle event, + // if the main loop has been made thread-safe. + AutoTimer aEmailDispatcherPollTimer; + aEmailDispatcherPollTimer.SetDebugName( + "sw::SwDBManager aEmailDispatcherPollTimer" ); + aEmailDispatcherPollTimer.SetTimeout( 500 ); + aEmailDispatcherPollTimer.Start(); + while( IsMergeOk() && m_pImpl->m_xLastMessage.is() ) + Application::Yield(); + aEmailDispatcherPollTimer.Stop(); + } + xMailDispatcher->stop(); + xMailDispatcher->shutdown(); + } + + // remove the temporary files + // has to be done after xMailDispatcher is finished, as mails may be + // delivered as message attachments! + for( const OUString &sFileURL : aFilesToRemove ) + SWUnoHelper::UCB_DeleteFile( sFileURL ); + } + catch (const uno::Exception&) + { + if (xProgressDlg) + { + xProgressDlg->response(RET_CANCEL); + } + } + + return !IsMergeError(); +} + +void SwDBManager::MergeCancel() +{ + if (m_aMergeStatus < MergeStatus::Cancel) + m_aMergeStatus = MergeStatus::Cancel; +} + +// determine the column's Numberformat and transfer to the forwarded Formatter, +// if applicable. +sal_uLong SwDBManager::GetColumnFormat( const OUString& rDBName, + const OUString& rTableName, + const OUString& rColNm, + SvNumberFormatter* pNFormatr, + LanguageType nLanguage ) +{ + sal_uLong nRet = 0; + if(pNFormatr) + { + uno::Reference< sdbc::XDataSource> xSource; + uno::Reference< sdbc::XConnection> xConnection; + bool bUseMergeData = false; + uno::Reference< sdbcx::XColumnsSupplier> xColsSupp; + bool bDisposeConnection = false; + if(m_pImpl->pMergeData && + ((m_pImpl->pMergeData->sDataSource == rDBName && m_pImpl->pMergeData->sCommand == rTableName) || + (rDBName.isEmpty() && rTableName.isEmpty()))) + { + xConnection = m_pImpl->pMergeData->xConnection; + xSource = SwDBManager::getDataSourceAsParent(xConnection,rDBName); + bUseMergeData = true; + xColsSupp.set(m_pImpl->pMergeData->xResultSet, css::uno::UNO_QUERY); + } + if(!xConnection.is()) + { + SwDBData aData; + aData.sDataSource = rDBName; + aData.sCommand = rTableName; + aData.nCommandType = -1; + SwDSParam* pParam = FindDSData(aData, false); + if(pParam && pParam->xConnection.is()) + { + xConnection = pParam->xConnection; + xColsSupp.set(pParam->xResultSet, css::uno::UNO_QUERY); + } + else + { + xConnection = RegisterConnection( rDBName ); + bDisposeConnection = true; + } + if(bUseMergeData) + m_pImpl->pMergeData->xConnection = xConnection; + } + bool bDispose = !xColsSupp.is(); + if(bDispose) + { + xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName); + } + if(xColsSupp.is()) + { + uno::Reference<container::XNameAccess> xCols; + try + { + xCols = xColsSupp->getColumns(); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()"); + } + if(!xCols.is() || !xCols->hasByName(rColNm)) + return nRet; + uno::Any aCol = xCols->getByName(rColNm); + uno::Reference< beans::XPropertySet > xColumn; + aCol >>= xColumn; + nRet = GetColumnFormat(xSource, xConnection, xColumn, pNFormatr, nLanguage); + if(bDispose) + { + ::comphelper::disposeComponent( xColsSupp ); + } + if(bDisposeConnection) + { + ::comphelper::disposeComponent( xConnection ); + } + } + else + nRet = pNFormatr->GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_SYSTEM ); + } + return nRet; +} + +sal_uLong SwDBManager::GetColumnFormat( uno::Reference< sdbc::XDataSource> const & xSource_in, + uno::Reference< sdbc::XConnection> const & xConnection, + uno::Reference< beans::XPropertySet> const & xColumn, + SvNumberFormatter* pNFormatr, + LanguageType nLanguage ) +{ + auto xSource = xSource_in; + + // set the NumberFormat in the doc if applicable + sal_uLong nRet = 0; + + if(!xSource.is()) + { + uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY); + if ( xChild.is() ) + xSource.set(xChild->getParent(), uno::UNO_QUERY); + } + if(xSource.is() && xConnection.is() && xColumn.is() && pNFormatr) + { + SvNumberFormatsSupplierObj* pNumFormat = new SvNumberFormatsSupplierObj( pNFormatr ); + uno::Reference< util::XNumberFormatsSupplier > xDocNumFormatsSupplier = pNumFormat; + uno::Reference< util::XNumberFormats > xDocNumberFormats = xDocNumFormatsSupplier->getNumberFormats(); + uno::Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, uno::UNO_QUERY); + + css::lang::Locale aLocale( LanguageTag( nLanguage ).getLocale()); + + //get the number formatter of the data source + uno::Reference<beans::XPropertySet> xSourceProps(xSource, uno::UNO_QUERY); + uno::Reference< util::XNumberFormats > xNumberFormats; + if(xSourceProps.is()) + { + uno::Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier"); + if(aFormats.hasValue()) + { + uno::Reference<util::XNumberFormatsSupplier> xSuppl; + aFormats >>= xSuppl; + if(xSuppl.is()) + { + xNumberFormats = xSuppl->getNumberFormats(); + } + } + } + bool bUseDefault = true; + try + { + uno::Any aFormatKey = xColumn->getPropertyValue("FormatKey"); + if(aFormatKey.hasValue()) + { + sal_Int32 nFormat = 0; + aFormatKey >>= nFormat; + if(xNumberFormats.is()) + { + try + { + uno::Reference<beans::XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat ); + uno::Any aFormatString = xNumProps->getPropertyValue("FormatString"); + uno::Any aLocaleVal = xNumProps->getPropertyValue("Locale"); + OUString sFormat; + aFormatString >>= sFormat; + lang::Locale aLoc; + aLocaleVal >>= aLoc; + nFormat = xDocNumberFormats->queryKey( sFormat, aLoc, false ); + if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFormat)) + nFormat = xDocNumberFormats->addNew( sFormat, aLoc ); + nRet = nFormat; + bUseDefault = false; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key"); + } + } + } + } + catch(const uno::Exception&) + { + SAL_WARN("sw.mailmerge", "no FormatKey property found"); + } + if(bUseDefault) + nRet = dbtools::getDefaultNumberFormat(xColumn, xDocNumberFormatTypes, aLocale); + } + return nRet; +} + +sal_Int32 SwDBManager::GetColumnType( const OUString& rDBName, + const OUString& rTableName, + const OUString& rColNm ) +{ + sal_Int32 nRet = sdbc::DataType::SQLNULL; + SwDBData aData; + aData.sDataSource = rDBName; + aData.sCommand = rTableName; + aData.nCommandType = -1; + SwDSParam* pParam = FindDSData(aData, false); + uno::Reference< sdbc::XConnection> xConnection; + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp; + bool bDispose = false; + if(pParam && pParam->xConnection.is()) + { + xConnection = pParam->xConnection; + xColsSupp.set( pParam->xResultSet, uno::UNO_QUERY ); + } + else + { + xConnection = RegisterConnection( rDBName ); + } + if( !xColsSupp.is() ) + { + xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName); + bDispose = true; + } + if(xColsSupp.is()) + { + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + if(xCols->hasByName(rColNm)) + { + uno::Any aCol = xCols->getByName(rColNm); + uno::Reference<beans::XPropertySet> xCol; + aCol >>= xCol; + uno::Any aType = xCol->getPropertyValue("Type"); + aType >>= nRet; + } + if(bDispose) + ::comphelper::disposeComponent( xColsSupp ); + } + return nRet; +} + +uno::Reference< sdbc::XConnection> SwDBManager::GetConnection(const OUString& rDataSource, + uno::Reference<sdbc::XDataSource>& rxSource, const SwView *pView) +{ + uno::Reference< sdbc::XConnection> xConnection; + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + try + { + uno::Reference<sdb::XCompletedConnection> xComplConnection(dbtools::getDataSource(rDataSource, xContext), uno::UNO_QUERY); + if ( xComplConnection.is() ) + { + rxSource.set(xComplConnection, uno::UNO_QUERY); + weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr; + uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(xContext, pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW ); + xConnection = xComplConnection->connectWithCompletion( xHandler ); + } + } + catch(const uno::Exception&) + { + } + + return xConnection; +} + +uno::Reference< sdbcx::XColumnsSupplier> SwDBManager::GetColumnSupplier(uno::Reference<sdbc::XConnection> const & xConnection, + const OUString& rTableOrQuery, + SwDBSelect eTableOrQuery) +{ + uno::Reference< sdbcx::XColumnsSupplier> xRet; + try + { + if(eTableOrQuery == SwDBSelect::UNKNOWN) + { + //search for a table with the given command name + uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY); + if(xTSupplier.is()) + { + uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables(); + eTableOrQuery = xTables->hasByName(rTableOrQuery) ? + SwDBSelect::TABLE : SwDBSelect::QUERY; + } + } + sal_Int32 nCommandType = SwDBSelect::TABLE == eTableOrQuery ? + sdb::CommandType::TABLE : sdb::CommandType::QUERY; + uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); + uno::Reference<sdbc::XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY); + + OUString sDataSource; + uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection, sDataSource); + uno::Reference<beans::XPropertySet> xSourceProperties(xSource, uno::UNO_QUERY); + if(xSourceProperties.is()) + { + xSourceProperties->getPropertyValue("Name") >>= sDataSource; + } + + uno::Reference<beans::XPropertySet> xRowProperties(xRowSet, uno::UNO_QUERY); + xRowProperties->setPropertyValue("DataSourceName", uno::makeAny(sDataSource)); + xRowProperties->setPropertyValue("Command", uno::makeAny(rTableOrQuery)); + xRowProperties->setPropertyValue("CommandType", uno::makeAny(nCommandType)); + xRowProperties->setPropertyValue("FetchSize", uno::makeAny(sal_Int32(10))); + xRowProperties->setPropertyValue("ActiveConnection", uno::makeAny(xConnection)); + xRowSet->execute(); + xRet.set( xRowSet, uno::UNO_QUERY ); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier"); + } + + return xRet; +} + +OUString SwDBManager::GetDBField(uno::Reference<beans::XPropertySet> const & xColumnProps, + const SwDBFormatData& rDBFormatData, + double* pNumber) +{ + uno::Reference< sdb::XColumn > xColumn(xColumnProps, uno::UNO_QUERY); + OUString sRet; + assert( xColumn.is() && "SwDBManager::::ImportDBField: illegal arguments" ); + if(!xColumn.is()) + return sRet; + + uno::Any aType = xColumnProps->getPropertyValue("Type"); + sal_Int32 eDataType = sdbc::DataType::SQLNULL; + aType >>= eDataType; + switch(eDataType) + { + case sdbc::DataType::CHAR: + case sdbc::DataType::VARCHAR: + case sdbc::DataType::LONGVARCHAR: + try + { + sRet = xColumn->getString(); + sRet = sRet.replace( '\xb', '\n' ); // MSWord uses \xb as a newline + } + catch(const sdbc::SQLException&) + { + } + break; + case sdbc::DataType::BIT: + case sdbc::DataType::BOOLEAN: + case sdbc::DataType::TINYINT: + case sdbc::DataType::SMALLINT: + case sdbc::DataType::INTEGER: + case sdbc::DataType::BIGINT: + case sdbc::DataType::FLOAT: + case sdbc::DataType::REAL: + case sdbc::DataType::DOUBLE: + case sdbc::DataType::NUMERIC: + case sdbc::DataType::DECIMAL: + case sdbc::DataType::DATE: + case sdbc::DataType::TIME: + case sdbc::DataType::TIMESTAMP: + { + + try + { + sRet = dbtools::DBTypeConversion::getFormattedValue( + xColumnProps, + rDBFormatData.xFormatter, + rDBFormatData.aLocale, + rDBFormatData.aNullDate); + if (pNumber) + { + double fVal = xColumn->getDouble(); + if(!xColumn->wasNull()) + { + *pNumber = fVal; + } + } + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", ""); + } + + } + break; + } + + return sRet; +} + +// checks if a desired data source table or query is open +bool SwDBManager::IsDataSourceOpen(const OUString& rDataSource, + const OUString& rTableOrQuery, bool bMergeShell) +{ + if(m_pImpl->pMergeData) + { + return ((rDataSource == m_pImpl->pMergeData->sDataSource + && rTableOrQuery == m_pImpl->pMergeData->sCommand) + || (rDataSource.isEmpty() && rTableOrQuery.isEmpty())) + && m_pImpl->pMergeData->xResultSet.is(); + } + else if(!bMergeShell) + { + SwDBData aData; + aData.sDataSource = rDataSource; + aData.sCommand = rTableOrQuery; + aData.nCommandType = -1; + SwDSParam* pFound = FindDSData(aData, false); + return (pFound && pFound->xResultSet.is()); + } + return false; +} + +// read column data at a specified position +bool SwDBManager::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName, + const OUString& rColumnName, sal_uInt32 nAbsRecordId, + LanguageType nLanguage, + OUString& rResult, double* pNumber) +{ + bool bRet = false; + SwDSParam* pFound = nullptr; + //check if it's the merge data source + if(m_pImpl->pMergeData && + rSourceName == m_pImpl->pMergeData->sDataSource && + rTableName == m_pImpl->pMergeData->sCommand) + { + pFound = m_pImpl->pMergeData.get(); + } + else + { + SwDBData aData; + aData.sDataSource = rSourceName; + aData.sCommand = rTableName; + aData.nCommandType = -1; + pFound = FindDSData(aData, false); + } + if (!pFound) + return false; + //check validity of supplied record Id + if(pFound->aSelection.hasElements()) + { + //the destination has to be an element of the selection + bool bFound = std::any_of(pFound->aSelection.begin(), pFound->aSelection.end(), + [nAbsRecordId](const uno::Any& rSelection) { + sal_Int32 nSelection = 0; + rSelection >>= nSelection; + return nSelection == static_cast<sal_Int32>(nAbsRecordId); + }); + if(!bFound) + return false; + } + if( pFound->HasValidRecord() ) + { + sal_Int32 nOldRow = 0; + try + { + nOldRow = pFound->xResultSet->getRow(); + } + catch(const uno::Exception&) + { + return false; + } + //position to the desired index + bool bMove = true; + if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) ) + bMove = lcl_MoveAbsolute(pFound, nAbsRecordId); + if(bMove) + bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber); + if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) ) + lcl_MoveAbsolute(pFound, nOldRow); + } + return bRet; +} + +// reads the column data at the current position +bool SwDBManager::GetMergeColumnCnt(const OUString& rColumnName, LanguageType nLanguage, + OUString &rResult, double *pNumber) +{ + if( !IsValidMergeRecord() ) + { + rResult.clear(); + return false; + } + + bool bRet = lcl_GetColumnCnt(m_pImpl->pMergeData.get(), rColumnName, nLanguage, rResult, pNumber); + return bRet; +} + +bool SwDBManager::ToNextMergeRecord() +{ + assert( m_pImpl->pMergeData && m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" ); + return lcl_ToNextRecord( m_pImpl->pMergeData.get() ); +} + +bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter *pDocFormatter, + LanguageType nLanguage, SwCalc &rCalc ) +{ + if( !IsValidMergeRecord() ) + return false; + + uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY ); + if( !xColsSupp.is() ) + return false; + + { + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + const uno::Sequence<OUString> aColNames = xCols->getElementNames(); + OUString aString; + + // add the "record number" variable, as SwCalc::VarLook would. + rCalc.VarChange( GetAppCharClass().lowercase( + SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber) ), GetSelectedRecordId() ); + + for( const OUString& rColName : aColNames ) + { + // get the column type + sal_Int32 nColumnType = sdbc::DataType::SQLNULL; + uno::Any aCol = xCols->getByName( rColName ); + uno::Reference<beans::XPropertySet> xColumnProps; + aCol >>= xColumnProps; + uno::Any aType = xColumnProps->getPropertyValue( "Type" ); + aType >>= nColumnType; + double aNumber = DBL_MAX; + + lcl_GetColumnCnt( m_pImpl->pMergeData.get(), xColumnProps, nLanguage, aString, &aNumber ); + + sal_uInt32 nFormat = GetColumnFormat( m_pImpl->pMergeData->sDataSource, + m_pImpl->pMergeData->sCommand, + rColName, pDocFormatter, nLanguage ); + // aNumber is overwritten by SwDBField::FormatValue, so store initial status + bool colIsNumber = aNumber != DBL_MAX; + bool bValidValue = SwDBField::FormatValue( pDocFormatter, aString, nFormat, + aNumber, nColumnType ); + if( colIsNumber ) + { + if( bValidValue ) + { + SwSbxValue aValue; + aValue.PutDouble( aNumber ); + aValue.SetDBvalue( true ); + SAL_INFO( "sw.ui", "'" << rColName << "': " << aNumber << " / " << aString ); + rCalc.VarChange( rColName, aValue ); + } + } + else + { + SwSbxValue aValue; + aValue.PutString( aString ); + aValue.SetDBvalue( true ); + SAL_INFO( "sw.ui", "'" << rColName << "': " << aString ); + rCalc.VarChange( rColName, aValue ); + } + } + } + + return true; +} + +void SwDBManager::ToNextRecord( + const OUString& rDataSource, const OUString& rCommand) +{ + SwDSParam* pFound = nullptr; + if(m_pImpl->pMergeData && + rDataSource == m_pImpl->pMergeData->sDataSource && + rCommand == m_pImpl->pMergeData->sCommand) + { + pFound = m_pImpl->pMergeData.get(); + } + else + { + SwDBData aData; + aData.sDataSource = rDataSource; + aData.sCommand = rCommand; + aData.nCommandType = -1; + pFound = FindDSData(aData, false); + } + lcl_ToNextRecord( pFound ); +} + +static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action ) +{ + bool bRet = true; + + assert( SwDBNextRecord::NEXT == action || + (SwDBNextRecord::FIRST == action && pParam) ); + if( nullptr == pParam ) + return false; + + if( action == SwDBNextRecord::FIRST ) + { + pParam->nSelectionIndex = 0; + pParam->bEndOfDB = false; + } + + if( !pParam->HasValidRecord() ) + return false; + + try + { + if( pParam->aSelection.hasElements() ) + { + if( pParam->nSelectionIndex >= pParam->aSelection.getLength() ) + pParam->bEndOfDB = true; + else + { + sal_Int32 nPos = 0; + pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos; + pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos ); + } + } + else if( action == SwDBNextRecord::FIRST ) + { + pParam->bEndOfDB = !pParam->xResultSet->first(); + } + else + { + sal_Int32 nBefore = pParam->xResultSet->getRow(); + pParam->bEndOfDB = !pParam->xResultSet->next(); + if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow() ) + { + // next returned true but it didn't move + ::dbtools::throwFunctionSequenceException( pParam->xResultSet ); + } + } + + ++pParam->nSelectionIndex; + bRet = !pParam->bEndOfDB; + } + catch( const uno::Exception & ) + { + // we allow merging with empty databases, so don't warn on init + TOOLS_WARN_EXCEPTION_IF(action == SwDBNextRecord::NEXT, + "sw.mailmerge", "exception in ToNextRecord()"); + pParam->bEndOfDB = true; + bRet = false; + } + return bRet; +} + +// synchronized labels contain a next record field at their end +// to assure that the next page can be created in mail merge +// the cursor position must be validated +bool SwDBManager::IsValidMergeRecord() const +{ + return( m_pImpl->pMergeData && m_pImpl->pMergeData->HasValidRecord() ); +} + +sal_uInt32 SwDBManager::GetSelectedRecordId() +{ + sal_uInt32 nRet = 0; + assert( m_pImpl->pMergeData && + m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" ); + if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is()) + return 0; + try + { + nRet = m_pImpl->pMergeData->xResultSet->getRow(); + } + catch(const uno::Exception&) + { + } + return nRet; +} + +bool SwDBManager::ToRecordId(sal_Int32 nSet) +{ + assert( m_pImpl->pMergeData && + m_pImpl->pMergeData->xResultSet.is() && "no data source in merge" ); + if(!m_pImpl->pMergeData || !m_pImpl->pMergeData->xResultSet.is()|| nSet < 0) + return false; + bool bRet = false; + sal_Int32 nAbsPos = nSet; + assert(nAbsPos >= 0); + bRet = lcl_MoveAbsolute(m_pImpl->pMergeData.get(), nAbsPos); + m_pImpl->pMergeData->bEndOfDB = !bRet; + return bRet; +} + +bool SwDBManager::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery) +{ + SwDBData aData; + aData.sDataSource = rDataSource; + aData.sCommand = rTableOrQuery; + aData.nCommandType = -1; + + SwDSParam* pFound = FindDSData(aData, true); + if(pFound->xResultSet.is()) + return true; + SwDSParam* pParam = FindDSConnection(rDataSource, false); + if(pParam && pParam->xConnection.is()) + pFound->xConnection = pParam->xConnection; + if(pFound->xConnection.is()) + { + try + { + uno::Reference< sdbc::XDatabaseMetaData > xMetaData = pFound->xConnection->getMetaData(); + try + { + pFound->bScrollable = xMetaData + ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE)); + } + catch(const uno::Exception&) + { + // DB driver may not be ODBC 3.0 compliant + pFound->bScrollable = true; + } + pFound->xStatement = pFound->xConnection->createStatement(); + OUString aQuoteChar = xMetaData->getIdentifierQuoteString(); + OUString sStatement = "SELECT * FROM " + aQuoteChar + rTableOrQuery + aQuoteChar; + pFound->xResultSet = pFound->xStatement->executeQuery( sStatement ); + + //after executeQuery the cursor must be positioned + pFound->bEndOfDB = !pFound->xResultSet->next(); + ++pFound->nSelectionIndex; + } + catch (const uno::Exception&) + { + pFound->xResultSet = nullptr; + pFound->xStatement = nullptr; + pFound->xConnection = nullptr; + } + } + return pFound->xResultSet.is(); +} + +uno::Reference< sdbc::XConnection> const & SwDBManager::RegisterConnection(OUString const& rDataSource) +{ + SwDSParam* pFound = SwDBManager::FindDSConnection(rDataSource, true); + uno::Reference< sdbc::XDataSource> xSource; + if(!pFound->xConnection.is()) + { + SwView* pView = (m_pDoc && m_pDoc->GetDocShell()) ? m_pDoc->GetDocShell()->GetView() : nullptr; + pFound->xConnection = SwDBManager::GetConnection(rDataSource, xSource, pView); + try + { + uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY); + if(xComponent.is()) + xComponent->addEventListener(m_pImpl->m_xDisposeListener.get()); + } + catch(const uno::Exception&) + { + } + } + return pFound->xConnection; +} + +sal_uInt32 SwDBManager::GetSelectedRecordId( + const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType) +{ + sal_uInt32 nRet = 0xffffffff; + //check for merge data source first + if(m_pImpl->pMergeData && + ((rDataSource == m_pImpl->pMergeData->sDataSource && + rTableOrQuery == m_pImpl->pMergeData->sCommand) || + (rDataSource.isEmpty() && rTableOrQuery.isEmpty())) && + (nCommandType == -1 || nCommandType == m_pImpl->pMergeData->nCommandType) && + m_pImpl->pMergeData->xResultSet.is()) + { + nRet = GetSelectedRecordId(); + } + else + { + SwDBData aData; + aData.sDataSource = rDataSource; + aData.sCommand = rTableOrQuery; + aData.nCommandType = nCommandType; + SwDSParam* pFound = FindDSData(aData, false); + if(pFound && pFound->xResultSet.is()) + { + try + { //if a selection array is set the current row at the result set may not be set yet + if(pFound->aSelection.hasElements()) + { + sal_Int32 nSelIndex = pFound->nSelectionIndex; + if(nSelIndex >= pFound->aSelection.getLength()) + nSelIndex = pFound->aSelection.getLength() -1; + pFound->aSelection.getConstArray()[nSelIndex] >>= nRet; + + } + else + nRet = pFound->xResultSet->getRow(); + } + catch(const uno::Exception&) + { + } + } + } + return nRet; +} + +// close all data sources - after fields were updated +void SwDBManager::CloseAll(bool bIncludingMerge) +{ + //the only thing done here is to reset the selection index + //all connections stay open + for (auto & pParam : m_DataSourceParams) + { + if (bIncludingMerge || pParam.get() != m_pImpl->pMergeData.get()) + { + pParam->nSelectionIndex = 0; + pParam->bEndOfDB = false; + try + { + if(!m_bInMerge && pParam->xResultSet.is()) + pParam->xResultSet->first(); + } + catch(const uno::Exception&) + {} + } + } +} + +SwDSParam* SwDBManager::FindDSData(const SwDBData& rData, bool bCreate) +{ + //prefer merge data if available + if(m_pImpl->pMergeData && + ((rData.sDataSource == m_pImpl->pMergeData->sDataSource && + rData.sCommand == m_pImpl->pMergeData->sCommand) || + (rData.sDataSource.isEmpty() && rData.sCommand.isEmpty())) && + (rData.nCommandType == -1 || rData.nCommandType == m_pImpl->pMergeData->nCommandType || + (bCreate && m_pImpl->pMergeData->nCommandType == -1))) + { + return m_pImpl->pMergeData.get(); + } + + SwDSParam* pFound = nullptr; + for (size_t nPos = m_DataSourceParams.size(); nPos; nPos--) + { + SwDSParam* pParam = m_DataSourceParams[nPos - 1].get(); + if(rData.sDataSource == pParam->sDataSource && + rData.sCommand == pParam->sCommand && + (rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType || + (bCreate && pParam->nCommandType == -1))) + { + // calls from the calculator may add a connection with an invalid commandtype + //later added "real" data base connections have to re-use the already available + //DSData and set the correct CommandType + if(bCreate && pParam->nCommandType == -1) + pParam->nCommandType = rData.nCommandType; + pFound = pParam; + break; + } + } + if(bCreate && !pFound) + { + pFound = new SwDSParam(rData); + m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound)); + try + { + uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY); + if(xComponent.is()) + xComponent->addEventListener(m_pImpl->m_xDisposeListener.get()); + } + catch(const uno::Exception&) + { + } + } + return pFound; +} + +SwDSParam* SwDBManager::FindDSConnection(const OUString& rDataSource, bool bCreate) +{ + //prefer merge data if available + if(m_pImpl->pMergeData && rDataSource == m_pImpl->pMergeData->sDataSource ) + { + SetAsUsed(rDataSource); + return m_pImpl->pMergeData.get(); + } + SwDSParam* pFound = nullptr; + for (const auto & pParam : m_DataSourceParams) + { + if(rDataSource == pParam->sDataSource) + { + SetAsUsed(rDataSource); + pFound = pParam.get(); + break; + } + } + if(bCreate && !pFound) + { + SwDBData aData; + aData.sDataSource = rDataSource; + pFound = new SwDSParam(aData); + SetAsUsed(rDataSource); + m_DataSourceParams.push_back(std::unique_ptr<SwDSParam>(pFound)); + try + { + uno::Reference<lang::XComponent> xComponent(pFound->xConnection, uno::UNO_QUERY); + if(xComponent.is()) + xComponent->addEventListener(m_pImpl->m_xDisposeListener.get()); + } + catch(const uno::Exception&) + { + } + } + return pFound; +} + +const SwDBData& SwDBManager::GetAddressDBName() +{ + return SW_MOD()->GetDBConfig()->GetAddressSource(); +} + +uno::Sequence<OUString> SwDBManager::GetExistingDatabaseNames() +{ + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext); + return xDBContext->getElementNames(); +} + +namespace sw +{ +DBConnURIType GetDBunoType(const INetURLObject &rURL) +{ + OUString sExt(rURL.GetFileExtension()); + DBConnURIType type = DBConnURIType::UNKNOWN; + + if (sExt == "odb") + { + type = DBConnURIType::ODB; + } + else if (sExt.equalsIgnoreAsciiCase("sxc") + || sExt.equalsIgnoreAsciiCase("ods") + || sExt.equalsIgnoreAsciiCase("xls") + || sExt.equalsIgnoreAsciiCase("xlsx")) + { + type = DBConnURIType::CALC; + } + else if (sExt.equalsIgnoreAsciiCase("sxw") || sExt.equalsIgnoreAsciiCase("odt") || sExt.equalsIgnoreAsciiCase("doc") || sExt.equalsIgnoreAsciiCase("docx")) + { + type = DBConnURIType::WRITER; + } + else if (sExt.equalsIgnoreAsciiCase("dbf")) + { + type = DBConnURIType::DBASE; + } + else if (sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt")) + { + type = DBConnURIType::FLAT; + } +#ifdef _WIN32 + else if (sExt.equalsIgnoreAsciiCase("mdb") || sExt.equalsIgnoreAsciiCase("mde")) + { + type = DBConnURIType::MSJET; + } + else if (sExt.equalsIgnoreAsciiCase("accdb") || sExt.equalsIgnoreAsciiCase("accde")) + { + type = DBConnURIType::MSACE; + } +#endif + return type; +} +} + +namespace +{ +uno::Any GetDBunoURI(const INetURLObject &rURL, DBConnURIType& rType) +{ + uno::Any aURLAny; + + if (rType == DBConnURIType::UNKNOWN) + rType = GetDBunoType(rURL); + + switch (rType) { + case DBConnURIType::UNKNOWN: + case DBConnURIType::ODB: + break; + case DBConnURIType::CALC: + { + OUString sDBURL = "sdbc:calc:" + + rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + aURLAny <<= sDBURL; + } + break; + case DBConnURIType::WRITER: + { + OUString sDBURL = "sdbc:writer:" + + rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE); + aURLAny <<= sDBURL; + } + break; + case DBConnURIType::DBASE: + { + INetURLObject aUrlTmp(rURL); + aUrlTmp.removeSegment(); + aUrlTmp.removeFinalSlash(); + OUString sDBURL = "sdbc:dbase:" + + aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE); + aURLAny <<= sDBURL; + } + break; + case DBConnURIType::FLAT: + { + INetURLObject aUrlTmp(rURL); + aUrlTmp.removeSegment(); + aUrlTmp.removeFinalSlash(); + OUString sDBURL = "sdbc:flat:" + + //only the 'path' has to be added + aUrlTmp.GetMainURL(INetURLObject::DecodeMechanism::NONE); + aURLAny <<= sDBURL; + } + break; + case DBConnURIType::MSJET: +#ifdef _WIN32 + { + OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL.PathToFileName()); + aURLAny <<= sDBURL; + } +#endif + break; + case DBConnURIType::MSACE: +#ifdef _WIN32 + { + OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL.PathToFileName()); + aURLAny <<= sDBURL; + } +#endif + break; + } + return aURLAny; +} + +/// Returns the URL of this SwDoc. +OUString getOwnURL(SfxObjectShell const * pDocShell) +{ + OUString aRet; + + if (!pDocShell) + return aRet; + + const INetURLObject& rURLObject = pDocShell->GetMedium()->GetURLObject(); + aRet = rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE); + return aRet; +} + +/** +Loads a data source from file and registers it. + +In case of success it returns the registered name, otherwise an empty string. +Optionally add a prefix to the registered DB name. +*/ +OUString LoadAndRegisterDataSource_Impl(DBConnURIType type, const uno::Reference< beans::XPropertySet > *pSettings, + const INetURLObject &rURL, const OUString *pDestDir, SfxObjectShell* pDocShell) +{ + OUString sExt(rURL.GetFileExtension()); + uno::Any aTableFilterAny; + uno::Any aSuppressVersionsAny; + uno::Any aInfoAny; + bool bStore = true; + OUString sFind; + uno::Sequence<OUString> aFilters(1); + + uno::Any aURLAny = GetDBunoURI(rURL, type); + switch (type) { + case DBConnURIType::UNKNOWN: + case DBConnURIType::CALC: + case DBConnURIType::WRITER: + break; + case DBConnURIType::ODB: + bStore = false; + break; + case DBConnURIType::FLAT: + case DBConnURIType::DBASE: + //set the filter to the file name without extension + aFilters[0] = rURL.getBase(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset); + aTableFilterAny <<= aFilters; + break; + case DBConnURIType::MSJET: + case DBConnURIType::MSACE: + aSuppressVersionsAny <<= true; + break; + } + + try + { + uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext); + + OUString sNewName = rURL.getName( + INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous); + sal_Int32 nExtLen = sExt.getLength(); + sNewName = sNewName.replaceAt(sNewName.getLength() - nExtLen - 1, nExtLen + 1, ""); + + //find a unique name if sNewName already exists + sFind = sNewName; + sal_Int32 nIndex = 0; + while (xDBContext->hasByName(sFind)) + sFind = sNewName + OUString::number(++nIndex); + + uno::Reference<uno::XInterface> xNewInstance; + if (!bStore) + { + //odb-file + uno::Any aDataSource = xDBContext->getByName(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + aDataSource >>= xNewInstance; + } + else + { + xNewInstance = xDBContext->createInstance(); + uno::Reference<beans::XPropertySet> xDataProperties(xNewInstance, uno::UNO_QUERY); + + if (aURLAny.hasValue()) + xDataProperties->setPropertyValue("URL", aURLAny); + if (aTableFilterAny.hasValue()) + xDataProperties->setPropertyValue("TableFilter", aTableFilterAny); + if (aSuppressVersionsAny.hasValue()) + xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny); + if (aInfoAny.hasValue()) + xDataProperties->setPropertyValue("Info", aInfoAny); + + if (DBConnURIType::FLAT == type && pSettings) + { + uno::Any aSettings = xDataProperties->getPropertyValue("Settings"); + uno::Reference < beans::XPropertySet > xDSSettings; + aSettings >>= xDSSettings; + ::comphelper::copyProperties(*pSettings, xDSSettings); + xDSSettings->setPropertyValue("Extension", uno::makeAny(sExt)); + } + + uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, uno::UNO_QUERY_THROW); + uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), uno::UNO_QUERY_THROW); + OUString aOwnURL = getOwnURL(pDocShell); + if (aOwnURL.isEmpty()) + { + // Cannot embed, as embedded data source would need the URL of the parent document. + OUString const sOutputExt = ".odb"; + OUString sHomePath(SvtPathOptions().GetWorkPath()); + utl::TempFile aTempFile(sNewName, true, &sOutputExt, pDestDir ? pDestDir : &sHomePath); + const OUString& sTmpName = aTempFile.GetURL(); + xStore->storeAsURL(sTmpName, uno::Sequence<beans::PropertyValue>()); + } + else + { + // Embed. + OUString aStreamRelPath = "EmbeddedDatabase"; + uno::Reference<embed::XStorage> xStorage = pDocShell->GetStorage(); + + // Refer to the sub-storage name in the document settings, so + // we can load it again next time the file is imported. + uno::Reference<lang::XMultiServiceFactory> xFactory(pDocShell->GetModel(), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY); + xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::makeAny(aStreamRelPath)); + + // Store it only after setting the above property, so that only one data source gets registered. + SwDBManager::StoreEmbeddedDataSource(xStore, xStorage, aStreamRelPath, aOwnURL); + } + } + xDBContext->registerObject(sFind, xNewInstance); + } + catch (const uno::Exception&) + { + sFind.clear(); + } + return sFind; +} + +// Construct vnd.sun.star.pkg:// URL +OUString ConstructVndSunStarPkgUrl(const OUString& rMainURL, const OUString& rStreamRelPath) +{ + auto xContext(comphelper::getProcessComponentContext()); + auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(rMainURL); + assert(xUri.is()); + xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext) + ->createVndSunStarPkgUrlReference(xUri); + assert(xUri.is()); + return xUri->getUriReference() + "/" + + INetURLObject::encode( + rStreamRelPath, INetURLObject::PART_FPATH, + INetURLObject::EncodeMechanism::All); +} +} + +OUString SwDBManager::LoadAndRegisterDataSource(weld::Window* pParent, SwDocShell* pDocShell) +{ + sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, pParent); + uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + OUString sHomePath(SvtPathOptions().GetWorkPath()); + aDlgHelper.SetDisplayDirectory( sHomePath ); + + OUString sFilterAll(SwResId(STR_FILTER_ALL)); + OUString sFilterAllData(SwResId(STR_FILTER_ALL_DATA)); + OUString sFilterSXB(SwResId(STR_FILTER_SXB)); + OUString sFilterSXC(SwResId(STR_FILTER_SXC)); + OUString sFilterSXW(SwResId(STR_FILTER_SXW)); + OUString sFilterDBF(SwResId(STR_FILTER_DBF)); + OUString sFilterXLS(SwResId(STR_FILTER_XLS)); + OUString sFilterDOC(SwResId(STR_FILTER_DOC)); + OUString sFilterTXT(SwResId(STR_FILTER_TXT)); + OUString sFilterCSV(SwResId(STR_FILTER_CSV)); +#ifdef _WIN32 + OUString sFilterMDB(SwResId(STR_FILTER_MDB)); + OUString sFilterACCDB(SwResId(STR_FILTER_ACCDB)); +#endif + xFP->appendFilter( sFilterAll, "*" ); + xFP->appendFilter( sFilterAllData, "*.ods;*.sxc;*.odt;*.sxw;*.dbf;*.xls;*.xlsx;*.doc;*.docx;*.txt;*.csv"); + + xFP->appendFilter( sFilterSXB, "*.odb" ); + xFP->appendFilter( sFilterSXC, "*.ods;*.sxc" ); + xFP->appendFilter( sFilterSXW, "*.odt;*.sxw" ); + xFP->appendFilter( sFilterDBF, "*.dbf" ); + xFP->appendFilter( sFilterXLS, "*.xls;*.xlsx" ); + xFP->appendFilter( sFilterDOC, "*.doc;*.docx" ); + xFP->appendFilter( sFilterTXT, "*.txt" ); + xFP->appendFilter( sFilterCSV, "*.csv" ); +#ifdef _WIN32 + xFP->appendFilter(sFilterMDB, "*.mdb;*.mde"); + xFP->appendFilter(sFilterACCDB, "*.accdb;*.accde"); +#endif + + xFP->setCurrentFilter( sFilterAll ) ; + OUString sFind; + if( ERRCODE_NONE == aDlgHelper.Execute() ) + { + uno::Reference< beans::XPropertySet > aSettings; + const INetURLObject aURL( xFP->getSelectedFiles().getConstArray()[0] ); + const DBConnURIType type = GetDBunoType( aURL ); + + if( DBConnURIType::FLAT == type ) + { + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext); + if( xSettingsDlg->execute() ) + aSettings.set( uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ) ); + } + sFind = LoadAndRegisterDataSource_Impl( type, DBConnURIType::FLAT == type ? &aSettings : nullptr, aURL, nullptr, pDocShell ); + + m_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(pDocShell, sFind)); + } + return sFind; +} + +void SwDBManager::StoreEmbeddedDataSource(const uno::Reference<frame::XStorable>& xStorable, + const uno::Reference<embed::XStorage>& xStorage, + const OUString& rStreamRelPath, + const OUString& rOwnURL, bool bCopyTo) +{ + // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing. + OUString const sTmpName = ConstructVndSunStarPkgUrl(rOwnURL, rStreamRelPath); + + uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence( + { + {"TargetStorage", uno::makeAny(xStorage)}, + {"StreamRelPath", uno::makeAny(rStreamRelPath)}, + {"BaseURI", uno::makeAny(rOwnURL)} + }); + if (bCopyTo) + xStorable->storeToURL(sTmpName, aSequence); + else + xStorable->storeAsURL(sTmpName, aSequence); +} + +OUString SwDBManager::LoadAndRegisterDataSource(const OUString &rURI, const OUString *pDestDir) +{ + return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN, nullptr, INetURLObject(rURI), pDestDir, nullptr ); +} + +namespace +{ + // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage + // which allows the original storage to be deleted + void switchEmbeddedDatabaseStorage(const uno::Reference<sdb::XDatabaseContext>& rDatabaseContext, const OUString& rName) + { + uno::Reference<sdb::XDocumentDataSource> xDS(rDatabaseContext->getByName(rName), uno::UNO_QUERY); + if (!xDS) + return; + uno::Reference<document::XStorageBasedDocument> xStorageDoc(xDS->getDatabaseDocument(), uno::UNO_QUERY); + if (!xStorageDoc) + return; + xStorageDoc->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage()); + } +} + +void SwDBManager::RevokeDataSource(const OUString& rName) +{ + uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext()); + if (xDatabaseContext->hasByName(rName)) + { + switchEmbeddedDatabaseStorage(xDatabaseContext, rName); + xDatabaseContext->revokeObject(rName); + } +} + +void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData& rData, const SwDocShell& rDocShell) +{ + uno::Reference<sdb::XDatabaseContext> xDatabaseContext = sdb::DatabaseContext::create(comphelper::getProcessComponentContext()); + + OUString sDataSource = rData.sDataSource; + + // Fallback, just in case the document would contain an embedded data source, but no DB fields. + if (sDataSource.isEmpty()) + sDataSource = "EmbeddedDatabase"; + + SwDBManager::RevokeDataSource( sDataSource ); + + // Encode the stream name and the real path into a single URL. + const INetURLObject& rURLObject = rDocShell.GetMedium()->GetURLObject(); + OUString const aURL = ConstructVndSunStarPkgUrl( + rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE), + m_sEmbeddedName); + + uno::Reference<uno::XInterface> xDataSource(xDatabaseContext->getByName(aURL), uno::UNO_QUERY); + xDatabaseContext->registerObject( sDataSource, xDataSource ); + + // temp file - don't remember connection + if (rData.sDataSource.isEmpty()) + m_aUncommittedRegistrations.push_back(std::pair<SwDocShell*, OUString>(nullptr, sDataSource)); +} + +void SwDBManager::ExecuteFormLetter( SwWrtShell& rSh, + const uno::Sequence<beans::PropertyValue>& rProperties) +{ + //prevent second call + if(m_pImpl->pMergeDialog) + return ; + OUString sDataSource, sDataTableOrQuery; + uno::Sequence<uno::Any> aSelection; + + sal_Int32 nCmdType = sdb::CommandType::TABLE; + uno::Reference< sdbc::XConnection> xConnection; + + svx::ODataAccessDescriptor aDescriptor(rProperties); + sDataSource = aDescriptor.getDataSource(); + OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDataTableOrQuery); + OSL_VERIFY(aDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCmdType); + + if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Selection) ) + aDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection; + if ( aDescriptor.has(svx::DataAccessDescriptorProperty::Connection) ) + aDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection; + + if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty()) + { + OSL_FAIL("PropertyValues missing or unset"); + return; + } + + //always create a connection for the dialog and dispose it after the dialog has been closed + SwDSParam* pFound = nullptr; + if(!xConnection.is()) + { + xConnection = SwDBManager::RegisterConnection(sDataSource); + pFound = FindDSConnection(sDataSource, true); + } + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + m_pImpl->pMergeDialog = pFact->CreateMailMergeDlg(rSh.GetView().GetViewFrame()->GetWindow().GetFrameWeld(), rSh, + sDataSource, + sDataTableOrQuery, + nCmdType, + xConnection); + if(m_pImpl->pMergeDialog->Execute() == RET_OK) + { + aDescriptor[svx::DataAccessDescriptorProperty::Selection] <<= m_pImpl->pMergeDialog->GetSelection(); + + uno::Reference<sdbc::XResultSet> xResSet = m_pImpl->pMergeDialog->GetResultSet(); + if(xResSet.is()) + aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet; + + // SfxObjectShellRef is ok, since there should be no control over the document lifetime here + SfxObjectShellRef xDocShell = rSh.GetView().GetViewFrame()->GetObjectShell(); + + lcl_emitEvent(SfxEventHintId::SwMailMerge, STR_SW_EVENT_MAIL_MERGE, xDocShell.get()); + + // prepare mail merge descriptor + SwMergeDescriptor aMergeDesc( m_pImpl->pMergeDialog->GetMergeType(), rSh, aDescriptor ); + aMergeDesc.sSaveToFilter = m_pImpl->pMergeDialog->GetSaveFilter(); + aMergeDesc.bCreateSingleFile = m_pImpl->pMergeDialog->IsSaveSingleDoc(); + aMergeDesc.bPrefixIsFilename = aMergeDesc.bCreateSingleFile; + aMergeDesc.sPrefix = m_pImpl->pMergeDialog->GetTargetURL(); + + if(!aMergeDesc.bCreateSingleFile) + { + if(m_pImpl->pMergeDialog->IsGenerateFromDataBase()) + aMergeDesc.sDBcolumn = m_pImpl->pMergeDialog->GetColumnName(); + + if(m_pImpl->pMergeDialog->IsFileEncryptedFromDataBase()) + aMergeDesc.sDBPasswordColumn = m_pImpl->pMergeDialog->GetPasswordColumnName(); + } + + Merge( aMergeDesc ); + + lcl_emitEvent(SfxEventHintId::SwMailMergeEnd, STR_SW_EVENT_MAIL_MERGE_END, xDocShell.get()); + + // reset the cursor inside + xResSet = nullptr; + aDescriptor[svx::DataAccessDescriptorProperty::Cursor] <<= xResSet; + } + if(pFound) + { + for (const auto & pParam : m_DataSourceParams) + { + if (pParam.get() == pFound) + { + try + { + uno::Reference<lang::XComponent> xComp(pParam->xConnection, uno::UNO_QUERY); + if(xComp.is()) + xComp->dispose(); + } + catch(const uno::RuntimeException&) + { + //may be disposed already since multiple entries may have used the same connection + } + break; + } + //pFound doesn't need to be removed/deleted - + //this has been done by the SwConnectionDisposedListener_Impl already + } + } + m_pImpl->pMergeDialog.disposeAndClear(); +} + +void SwDBManager::InsertText(SwWrtShell& rSh, + const uno::Sequence< beans::PropertyValue>& rProperties) +{ + OUString sDataSource, sDataTableOrQuery; + uno::Reference<sdbc::XResultSet> xResSet; + uno::Sequence<uno::Any> aSelection; + sal_Int16 nCmdType = sdb::CommandType::TABLE; + uno::Reference< sdbc::XConnection> xConnection; + for(const beans::PropertyValue& rValue : rProperties) + { + if ( rValue.Name == "DataSourceName" ) + rValue.Value >>= sDataSource; + else if ( rValue.Name == "Command" ) + rValue.Value >>= sDataTableOrQuery; + else if ( rValue.Name == "Cursor" ) + rValue.Value >>= xResSet; + else if ( rValue.Name == "Selection" ) + rValue.Value >>= aSelection; + else if ( rValue.Name == "CommandType" ) + rValue.Value >>= nCmdType; + else if ( rValue.Name == "ActiveConnection" ) + rValue.Value >>= xConnection; + } + if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is()) + { + OSL_FAIL("PropertyValues missing or unset"); + return; + } + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<sdbc::XDataSource> xSource; + uno::Reference<container::XChild> xChild(xConnection, uno::UNO_QUERY); + if(xChild.is()) + xSource.set(xChild->getParent(), uno::UNO_QUERY); + if(!xSource.is()) + xSource = dbtools::getDataSource(sDataSource, xContext); + uno::Reference< sdbcx::XColumnsSupplier > xColSupp( xResSet, uno::UNO_QUERY ); + SwDBData aDBData; + aDBData.sDataSource = sDataSource; + aDBData.sCommand = sDataTableOrQuery; + aDBData.nCommandType = nCmdType; + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwInsertDBColAutoPilot> pDlg(pFact->CreateSwInsertDBColAutoPilot( rSh.GetView(), + xSource, + xColSupp, + aDBData )); + if( RET_OK == pDlg->Execute() ) + { + OUString sDummy; + if(!xConnection.is()) + xConnection = xSource->getConnection(sDummy, sDummy); + try + { + pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", ""); + } + } +} + +uno::Reference<sdbc::XDataSource> SwDBManager::getDataSourceAsParent(const uno::Reference< sdbc::XConnection>& _xConnection,const OUString& _sDataSourceName) +{ + uno::Reference<sdbc::XDataSource> xSource; + try + { + uno::Reference<container::XChild> xChild(_xConnection, uno::UNO_QUERY); + if ( xChild.is() ) + xSource.set(xChild->getParent(), uno::UNO_QUERY); + if ( !xSource.is() ) + xSource = dbtools::getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext()); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()"); + } + return xSource; +} + +uno::Reference<sdbc::XResultSet> SwDBManager::createCursor(const OUString& _sDataSourceName, + const OUString& _sCommand, + sal_Int32 _nCommandType, + const uno::Reference<sdbc::XConnection>& _xConnection, + const SwView* pView) +{ + uno::Reference<sdbc::XResultSet> xResultSet; + try + { + uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); + if( xMgr.is() ) + { + uno::Reference<uno::XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet"); + uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, uno::UNO_QUERY); + if(xRowSetPropSet.is()) + { + xRowSetPropSet->setPropertyValue("DataSourceName", uno::makeAny(_sDataSourceName)); + xRowSetPropSet->setPropertyValue("ActiveConnection", uno::makeAny(_xConnection)); + xRowSetPropSet->setPropertyValue("Command", uno::makeAny(_sCommand)); + xRowSetPropSet->setPropertyValue("CommandType", uno::makeAny(_nCommandType)); + + uno::Reference< sdb::XCompletedExecution > xRowSet(xInstance, uno::UNO_QUERY); + + if ( xRowSet.is() ) + { + weld::Window* pWindow = pView ? pView->GetFrameWeld() : nullptr; + uno::Reference< task::XInteractionHandler > xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), pWindow ? pWindow->GetXWindow() : nullptr), uno::UNO_QUERY_THROW ); + xRowSet->executeWithCompletion(xHandler); + } + xResultSet.set(xRowSet, uno::UNO_QUERY); + } + } + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet"); + } + return xResultSet; +} + +void SwDBManager::setEmbeddedName(const OUString& rEmbeddedName, SwDocShell& rDocShell) +{ + bool bLoad = m_sEmbeddedName != rEmbeddedName && !rEmbeddedName.isEmpty(); + bool bRegisterListener = m_sEmbeddedName.isEmpty() && !rEmbeddedName.isEmpty(); + + m_sEmbeddedName = rEmbeddedName; + + if (bLoad) + { + uno::Reference<embed::XStorage> xStorage = rDocShell.GetStorage(); + // It's OK that we don't have the named sub-storage yet, in case + // we're in the process of creating it. + if (xStorage->hasByName(rEmbeddedName)) + LoadAndRegisterEmbeddedDataSource(rDocShell.GetDoc()->GetDBData(), rDocShell); + } + + if (bRegisterListener) + // Register a remove listener, so we know when the embedded data source is removed. + m_pImpl->m_xDataSourceRemovedListener = new SwDataSourceRemovedListener(*this); +} + +const OUString& SwDBManager::getEmbeddedName() const +{ + return m_sEmbeddedName; +} + +SwDoc* SwDBManager::getDoc() const +{ + return m_pDoc; +} + +void SwDBManager::releaseRevokeListener() +{ + if (m_pImpl->m_xDataSourceRemovedListener.is()) + { + m_pImpl->m_xDataSourceRemovedListener->Dispose(); + m_pImpl->m_xDataSourceRemovedListener.clear(); + } +} + +SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager& rManager) + : m_pDBManager(&rManager) +{ +} + +void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject& rSource ) +{ + ::SolarMutexGuard aGuard; + + if (!m_pDBManager) return; // we're disposed too! + + uno::Reference<sdbc::XConnection> xSource(rSource.Source, uno::UNO_QUERY); + for (size_t nPos = m_pDBManager->m_DataSourceParams.size(); nPos; nPos--) + { + SwDSParam* pParam = m_pDBManager->m_DataSourceParams[nPos - 1].get(); + if(pParam->xConnection.is() && + (xSource == pParam->xConnection)) + { + m_pDBManager->m_DataSourceParams.erase( + m_pDBManager->m_DataSourceParams.begin() + nPos - 1); + } + } +} + +std::shared_ptr<SwMailMergeConfigItem> SwDBManager::PerformMailMerge(SwView const * pView) +{ + std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem(); + if (!xConfigItem) + return xConfigItem; + + svx::ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(xConfigItem->GetCurrentDBData().sDataSource); + aDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= xConfigItem->GetConnection().getTyped(); + aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ] <<= xConfigItem->GetResultSet(); + aDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= xConfigItem->GetCurrentDBData().sCommand; + aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= xConfigItem->GetCurrentDBData().nCommandType; + aDescriptor[ svx::DataAccessDescriptorProperty::Selection ] <<= xConfigItem->GetSelection(); + + SwWrtShell& rSh = pView->GetWrtShell(); + xConfigItem->SetTargetView(nullptr); + + SwMergeDescriptor aMergeDesc(DBMGR_MERGE_SHELL, rSh, aDescriptor); + aMergeDesc.pMailMergeConfigItem = xConfigItem.get(); + aMergeDesc.bCreateSingleFile = true; + rSh.GetDBManager()->Merge(aMergeDesc); + + return xConfigItem; +} + +void SwDBManager::RevokeLastRegistrations() +{ + if (!m_aUncommittedRegistrations.empty()) + { + SwView* pView = ( m_pDoc && m_pDoc->GetDocShell() ) ? m_pDoc->GetDocShell()->GetView() : nullptr; + if (pView) + { + const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem(); + if (xConfigItem) + { + xConfigItem->DisposeResultSet(); + xConfigItem->DocumentReloaded(); + } + } + + for (auto it = m_aUncommittedRegistrations.begin(); it != m_aUncommittedRegistrations.end();) + { + if ((m_pDoc && it->first == m_pDoc->GetDocShell()) || it->first == nullptr) + { + RevokeDataSource(it->second); + it = m_aUncommittedRegistrations.erase(it); + } + else + ++it; + } + } +} + +void SwDBManager::CommitLastRegistrations() +{ + for (auto aIt = m_aUncommittedRegistrations.begin(); aIt != m_aUncommittedRegistrations.end();) + { + if (aIt->first == m_pDoc->GetDocShell() || aIt->first == nullptr) + { + m_aNotUsedConnections.push_back(aIt->second); + aIt = m_aUncommittedRegistrations.erase(aIt); + } + else + aIt++; + } +} + +void SwDBManager::SetAsUsed(const OUString& rName) +{ + auto aFound = std::find(m_aNotUsedConnections.begin(), m_aNotUsedConnections.end(), rName); + if (aFound != m_aNotUsedConnections.end()) + m_aNotUsedConnections.erase(aFound); +} + +void SwDBManager::RevokeNotUsedConnections() +{ + for (auto aIt = m_aNotUsedConnections.begin(); aIt != m_aNotUsedConnections.end();) + { + RevokeDataSource(*aIt); + aIt = m_aNotUsedConnections.erase(aIt); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/dbtree.cxx b/sw/source/uibase/dbui/dbtree.cxx new file mode 100644 index 000000000..cf31941c3 --- /dev/null +++ b/sw/source/uibase/dbui/dbtree.cxx @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XNameAccess.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/container/XContainerListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <svx/dbaexchange.hxx> + +#include <dbmgr.hxx> +#include <wrtsh.hxx> +#include <dbtree.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <bitmaps.hlst> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; + +class SwDBTreeList_Impl : public cppu::WeakImplHelper < XContainerListener > +{ + Reference< XDatabaseContext > m_xDatabaseContext; + SwWrtShell* m_pWrtShell; + + public: + explicit SwDBTreeList_Impl() + : m_pWrtShell(nullptr) + { + } + virtual ~SwDBTreeList_Impl() override; + + virtual void SAL_CALL elementInserted( const ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const ContainerEvent& Event ) override; + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + bool HasContext(); + SwWrtShell* GetWrtShell() { return m_pWrtShell;} + void SetWrtShell(SwWrtShell& rSh) { m_pWrtShell = &rSh;} + const Reference<XDatabaseContext>& GetContext() const {return m_xDatabaseContext;} + Reference<XConnection> GetConnection(const OUString& rSourceName); +}; + +SwDBTreeList_Impl::~SwDBTreeList_Impl() +{ + if(m_xDatabaseContext.is()) + { + osl_atomic_increment(&m_refCount); + //block necessary due to solaris' compiler behaviour to + //remove temporaries at the block's end + { + m_xDatabaseContext->removeContainerListener( this ); + } + osl_atomic_decrement(&m_refCount); + } +} + +void SwDBTreeList_Impl::elementInserted( const ContainerEvent& ) +{ + // information not needed +} + +void SwDBTreeList_Impl::elementRemoved( const ContainerEvent& ) +{ +} + +void SwDBTreeList_Impl::disposing( const EventObject& ) +{ + m_xDatabaseContext = nullptr; +} + +void SwDBTreeList_Impl::elementReplaced( const ContainerEvent& rEvent ) +{ + elementRemoved(rEvent); +} + +bool SwDBTreeList_Impl::HasContext() +{ + if(!m_xDatabaseContext.is()) + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + m_xDatabaseContext = DatabaseContext::create(xContext); + m_xDatabaseContext->addContainerListener( this ); + } + return m_xDatabaseContext.is(); +} + +Reference<XConnection> SwDBTreeList_Impl::GetConnection(const OUString& rSourceName) +{ + Reference<XConnection> xRet; + if (m_xDatabaseContext.is() && m_pWrtShell) + { + xRet = m_pWrtShell->GetDBManager()->RegisterConnection(rSourceName); + } + return xRet; +} + +SwDBTreeList::SwDBTreeList(std::unique_ptr<weld::TreeView> xTreeView) + : bInitialized(false) + , bShowColumns(false) + , pImpl(new SwDBTreeList_Impl) + , m_xTreeView(std::move(xTreeView)) +{ + m_xTreeView->connect_expanding(LINK(this, SwDBTreeList, RequestingChildrenHdl)); +} + +SwDBTreeList::~SwDBTreeList() +{ +} + +void SwDBTreeList::InitTreeList() +{ + if (!pImpl->HasContext() && pImpl->GetWrtShell()) + return; + + Sequence< OUString > aDBNames = pImpl->GetContext()->getElementNames(); + auto const sort = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + std::sort( + aDBNames.begin(), aDBNames.end(), + [&sort](OUString const & x, OUString const & y) + { return sort.compare(x, y) < 0; }); + + OUString aImg(RID_BMP_DB); + for (const OUString& rDBName : std::as_const(aDBNames)) + { + Reference<XConnection> xConnection = pImpl->GetConnection(rDBName); + if (xConnection.is()) + { + m_xTreeView->insert(nullptr, -1, &rDBName, nullptr, nullptr, nullptr, &aImg, true, nullptr); + } + } + Select(OUString(), OUString(), OUString()); + + bInitialized = true; +} + +void SwDBTreeList::AddDataSource(const OUString& rSource) +{ + OUString aImg(RID_BMP_DB); + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + m_xTreeView->insert(nullptr, -1, &rSource, nullptr, nullptr, nullptr, &aImg, true, xIter.get()); + m_xTreeView->select(*xIter); +} + +IMPL_LINK(SwDBTreeList, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool) +{ + if (!m_xTreeView->iter_has_child(rParent)) + { + if (m_xTreeView->get_iter_depth(rParent)) // column names + { + try + { + std::unique_ptr<weld::TreeIter> xGrandParent(m_xTreeView->make_iterator(&rParent)); + m_xTreeView->iter_parent(*xGrandParent); + OUString sSourceName = m_xTreeView->get_text(*xGrandParent); + OUString sTableName = m_xTreeView->get_text(rParent); + + if(!pImpl->GetContext()->hasByName(sSourceName)) + return true; + Reference<XConnection> xConnection = pImpl->GetConnection(sSourceName); + bool bTable = m_xTreeView->get_id(rParent).isEmpty(); + Reference<XColumnsSupplier> xColsSupplier; + if(bTable) + { + Reference<XTablesSupplier> xTSupplier(xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + OSL_ENSURE(xTables->hasByName(sTableName), "table not available anymore?"); + try + { + Any aTable = xTables->getByName(sTableName); + Reference<XPropertySet> xPropSet; + aTable >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + else + { + Reference<XQueriesSupplier> xQSupplier(xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + OSL_ENSURE(xQueries->hasByName(sTableName), "table not available anymore?"); + try + { + Any aQuery = xQueries->getByName(sTableName); + Reference<XPropertySet> xPropSet; + aQuery >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + + if(xColsSupplier.is()) + { + Reference <XNameAccess> xCols = xColsSupplier->getColumns(); + const Sequence< OUString> aColNames = xCols->getElementNames(); + for (const OUString& rColName : aColNames) + { + m_xTreeView->append(&rParent, rColName); + } + } + } + catch (const Exception&) + { + } + } + else // table names + { + try + { + OUString sSourceName = m_xTreeView->get_text(rParent); + if (!pImpl->GetContext()->hasByName(sSourceName)) + return true; + Reference<XConnection> xConnection = pImpl->GetConnection(sSourceName); + if (xConnection.is()) + { + Reference<XTablesSupplier> xTSupplier(xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + const Sequence< OUString> aTableNames = xTables->getElementNames(); + OUString aImg(RID_BMP_DBTABLE); + for (const OUString& rTableName : aTableNames) + { + m_xTreeView->insert(&rParent, -1, &rTableName, nullptr, + nullptr, nullptr, &aImg, bShowColumns, nullptr); + } + } + + Reference<XQueriesSupplier> xQSupplier(xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + const Sequence< OUString> aQueryNames = xQueries->getElementNames(); + OUString aImg(RID_BMP_DBQUERY); + for (const OUString& rQueryName : aQueryNames) + { + //to discriminate between queries and tables the user data of query entries is set + OUString sId(OUString::number(1)); + m_xTreeView->insert(&rParent, -1, &rQueryName, &sId, + nullptr, nullptr, &aImg, bShowColumns, nullptr); + } + } + } + } + catch (const Exception&) + { + } + } + } + return true; +} + +OUString SwDBTreeList::GetDBName(OUString& rTableName, OUString& rColumnName, sal_Bool* pbIsTable) +{ + OUString sDBName; + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xIter.get())) + { + if (m_xTreeView->get_iter_depth(*xIter) == 2) + { + rColumnName = m_xTreeView->get_text(*xIter); + m_xTreeView->iter_parent(*xIter); // column name was selected + } + if (m_xTreeView->get_iter_depth(*xIter) == 1) + { + if (pbIsTable) + *pbIsTable = m_xTreeView->get_id(*xIter).isEmpty(); + rTableName = m_xTreeView->get_text(*xIter); + m_xTreeView->iter_parent(*xIter); + } + sDBName = m_xTreeView->get_text(*xIter); + } + return sDBName; +} + +// Format: database.table +void SwDBTreeList::Select(const OUString& rDBName, const OUString& rTableName, const OUString& rColumnName) +{ + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xParent)) + return; + + do + { + if (rDBName == m_xTreeView->get_text(*xParent)) + { + if (!m_xTreeView->iter_has_child(*xParent)) + { + RequestingChildrenHdl(*xParent); + m_xTreeView->expand_row(*xParent); + } + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xParent.get())); + if (!m_xTreeView->iter_children(*xChild)) + continue; + do + { + if (rTableName == m_xTreeView->get_text(*xChild)) + { + m_xTreeView->copy_iterator(*xChild, *xParent); + + bool bNoChild = false; + if (bShowColumns && !rColumnName.isEmpty()) + { + if (!m_xTreeView->iter_has_child(*xParent)) + { + RequestingChildrenHdl(*xParent); + m_xTreeView->expand_row(*xParent); + } + + bNoChild = true; + if (m_xTreeView->iter_children(*xChild)) + { + do + { + if (rColumnName == m_xTreeView->get_text(*xChild)) + { + bNoChild = false; + break; + } + } + while (m_xTreeView->iter_next_sibling(*xChild)); + } + } + + if (bNoChild) + m_xTreeView->copy_iterator(*xParent, *xChild); + + m_xTreeView->scroll_to_row(*xChild); + m_xTreeView->select(*xChild); + return; + } + } + while (m_xTreeView->iter_next_sibling(*xChild)); + } + } while (m_xTreeView->iter_next_sibling(*xParent)); +} + +void SwDBTreeList::SetWrtShell(SwWrtShell& rSh) +{ + pImpl->SetWrtShell(rSh); + if (m_xTreeView->get_visible() && !bInitialized) + InitTreeList(); +} + +namespace +{ + void GotoRootLevelParent(const weld::TreeView& rTreeView, weld::TreeIter& rEntry) + { + while (rTreeView.get_iter_depth(rEntry)) + rTreeView.iter_parent(rEntry); + } +} + +void SwDBTreeList::ShowColumns(bool bShowCol) +{ + if (bShowCol != bShowColumns) + { + bShowColumns = bShowCol; + OUString sTableName; + OUString sColumnName; + const OUString sDBName(GetDBName(sTableName, sColumnName)); + + m_xTreeView->freeze(); + + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator()); + if (m_xTreeView->get_iter_first(*xIter)) + { + do + { + GotoRootLevelParent(*m_xTreeView, *xIter); + m_xTreeView->collapse_row(*xIter); + while (m_xTreeView->iter_has_child(*xIter)) + { + m_xTreeView->copy_iterator(*xIter, *xChild); + (void)m_xTreeView->iter_children(*xChild); + m_xTreeView->remove(*xChild); + } + } while (m_xTreeView->iter_next(*xIter)); + } + + m_xTreeView->thaw(); + + if (!sDBName.isEmpty()) + { + Select(sDBName, sTableName, sColumnName); // force RequestingChildren + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/dbui.cxx b/sw/source/uibase/dbui/dbui.cxx new file mode 100644 index 000000000..ad3b1c69a --- /dev/null +++ b/sw/source/uibase/dbui/dbui.cxx @@ -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 . + */ + +#include <dbui.hxx> + +SaveMonitor::SaveMonitor(weld::Window *pParent) + : GenericDialogController(pParent, "modules/swriter/ui/savemonitordialog.ui", + "SaveMonitorDialog") + , m_xDocName(m_xBuilder->weld_label("docname")) + , m_xPrinter(m_xBuilder->weld_label("printer")) + , m_xPrintInfo(m_xBuilder->weld_label("printinfo")) +{ +} + +SaveMonitor::~SaveMonitor() +{ +} + +PrintMonitor::PrintMonitor(weld::Window *pParent) + : GenericDialogController(pParent, "modules/swriter/ui/printmonitordialog.ui", + "PrintMonitorDialog") + , m_xDocName(m_xBuilder->weld_label("docname")) + , m_xPrinter(m_xBuilder->weld_label("printer")) + , m_xPrintInfo(m_xBuilder->weld_label("printinfo")) +{ +} + +PrintMonitor::~PrintMonitor() +{ +} + +// Progress Indicator for Creation of personalized Mail Merge documents: +CreateMonitor::CreateMonitor(weld::Window *pParent) + : GenericDialogController(pParent, "modules/swriter/ui/mmcreatingdialog.ui", + "MMCreatingDialog") + , m_sCountingPattern() + , m_nTotalCount(0) + , m_nCurrentPosition(0) + , m_xCounting(m_xBuilder->weld_label("progress")) +{ + m_sCountingPattern = m_xCounting->get_label(); + m_xCounting->set_label("..."); +} + +CreateMonitor::~CreateMonitor() +{ +} + +void CreateMonitor::UpdateCountingText() +{ + constexpr OUStringLiteral sVariable_Total("%Y"); + constexpr OUStringLiteral sVariable_Position("%X"); + + OUString sText(m_sCountingPattern); + sText = sText.replaceAll( sVariable_Total, OUString::number( m_nTotalCount ) ); + sText = sText.replaceAll( sVariable_Position, OUString::number( m_nCurrentPosition ) ); + m_xCounting->set_label(sText); +} + +void CreateMonitor::SetTotalCount( sal_Int32 nTotal ) +{ + m_nTotalCount = nTotal; + UpdateCountingText(); +} + +void CreateMonitor::SetCurrentPosition( sal_Int32 nCurrent ) +{ + m_nCurrentPosition = nCurrent; + UpdateCountingText(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/maildispatcher.cxx b/sw/source/uibase/dbui/maildispatcher.cxx new file mode 100644 index 000000000..10d8ea748 --- /dev/null +++ b/sw/source/uibase/dbui/maildispatcher.cxx @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <maildispatcher.hxx> +#include <imaildsplistener.hxx> + +#include <algorithm> + +#include <com/sun/star/mail/MailException.hpp> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; + +typedef std::vector< ::rtl::Reference<IMailDispatcherListener> > MailDispatcherListenerContainer_t; + +namespace /* private */ +{ + class MailDeliveryNotifier + { + public: + MailDeliveryNotifier(uno::Reference<mail::XMailMessage> const & message) : + message_(message) + {} + + void operator() (::rtl::Reference<IMailDispatcherListener> const & listener) const + { listener->mailDelivered(message_); } + + private: + uno::Reference<mail::XMailMessage> message_; + }; + + class MailDeliveryErrorNotifier + { + public: + MailDeliveryErrorNotifier( + ::rtl::Reference<MailDispatcher> const & xMailDispatcher, + uno::Reference<mail::XMailMessage> const & message, + const OUString& error_message) : + mail_dispatcher_(xMailDispatcher), + message_(message), + error_message_(error_message) + {} + + void operator() (::rtl::Reference<IMailDispatcherListener> const & listener) const + { listener->mailDeliveryError(mail_dispatcher_, message_, error_message_); } + + private: + ::rtl::Reference<MailDispatcher> mail_dispatcher_; + uno::Reference<mail::XMailMessage> message_; + OUString error_message_; + }; + +} // namespace private + +MailDispatcher::MailDispatcher(uno::Reference<mail::XSmtpService> const & mailserver) : + m_xMailserver( mailserver ), + m_bActive( false ), + m_bShutdownRequested( false ) +{ + m_aWakeupCondition.reset(); + m_aRunCondition.reset(); + + if (!create()) + throw uno::RuntimeException(); + + // wait until the mail dispatcher thread is really alive + // and has acquired a reference to this instance of the + // class + m_aRunCondition.wait(); +} + +MailDispatcher::~MailDispatcher() +{ +} + +void MailDispatcher::enqueueMailMessage(uno::Reference<mail::XMailMessage> const & message) +{ + ::osl::MutexGuard thread_status_guard( m_aThreadStatusMutex ); + ::osl::MutexGuard message_container_guard( m_aMessageContainerMutex ); + + OSL_PRECOND( !m_bShutdownRequested, "MailDispatcher thread is shutting down already" ); + + m_aXMessageList.push_back( message ); + if ( m_bActive ) + m_aWakeupCondition.set(); +} + +uno::Reference<mail::XMailMessage> MailDispatcher::dequeueMailMessage() +{ + ::osl::MutexGuard guard( m_aMessageContainerMutex ); + uno::Reference<mail::XMailMessage> message; + if ( !m_aXMessageList.empty() ) + { + message = m_aXMessageList.front(); + m_aXMessageList.pop_front(); + } + return message; +} + +void MailDispatcher::start() +{ + OSL_PRECOND(!isStarted(), "MailDispatcher is already started!"); + + ::osl::ClearableMutexGuard thread_status_guard( m_aThreadStatusMutex ); + + OSL_PRECOND(!m_bShutdownRequested, "MailDispatcher thread is shutting down already"); + + if ( !m_bShutdownRequested ) + { + m_bActive = true; + m_aWakeupCondition.set(); + thread_status_guard.clear(); + } +} + +void MailDispatcher::stop() +{ + OSL_PRECOND(isStarted(), "MailDispatcher not started!"); + + ::osl::ClearableMutexGuard thread_status_guard( m_aThreadStatusMutex ); + + OSL_PRECOND(!m_bShutdownRequested, "MailDispatcher thread is shutting down already"); + + if (!m_bShutdownRequested) + { + m_bActive = false; + m_aWakeupCondition.reset(); + thread_status_guard.clear(); + } +} + +void MailDispatcher::shutdown() +{ + ::osl::MutexGuard thread_status_guard( m_aThreadStatusMutex ); + + OSL_PRECOND(!m_bShutdownRequested, "MailDispatcher thread is shutting down already"); + + m_bShutdownRequested = true; + m_aWakeupCondition.set(); +} + + +void MailDispatcher::addListener(::rtl::Reference<IMailDispatcherListener> const & listener) +{ + OSL_PRECOND(!m_bShutdownRequested, "MailDispatcher thread is shutting down already"); + + ::osl::MutexGuard guard( m_aListenerContainerMutex ); + m_aListenerVector.push_back( listener ); +} + +std::vector< ::rtl::Reference<IMailDispatcherListener> > MailDispatcher::cloneListener() +{ + ::osl::MutexGuard guard( m_aListenerContainerMutex ); + return m_aListenerVector; +} + +void MailDispatcher::sendMailMessageNotifyListener(uno::Reference<mail::XMailMessage> const & message) +{ + try + { + m_xMailserver->sendMailMessage( message ); + MailDispatcherListenerContainer_t aClonedListenerVector(cloneListener()); + std::for_each( aClonedListenerVector.begin(), aClonedListenerVector.end(), + MailDeliveryNotifier(message) ); + } + catch (const mail::MailException& ex) + { + MailDispatcherListenerContainer_t aClonedListenerVector(cloneListener()); + std::for_each( aClonedListenerVector.begin(), aClonedListenerVector.end(), + MailDeliveryErrorNotifier(this, message, ex.Message) ); + } + catch (const uno::RuntimeException& ex) + { + MailDispatcherListenerContainer_t aClonedListenerVector(cloneListener()); + std::for_each( aClonedListenerVector.begin(), aClonedListenerVector.end(), + MailDeliveryErrorNotifier(this, message, ex.Message) ); + } +} + +void MailDispatcher::run() +{ + osl_setThreadName("MailDispatcher"); + + // acquire a self reference in order to avoid race + // conditions. The last client of this class must + // call shutdown before releasing his last reference + // to this class in order to shutdown this thread + // which will release his (the very last reference + // to the class and so force their destruction + m_xSelfReference = this; + + // signal that the mail dispatcher thread is now alive + m_aRunCondition.set(); + + for(;;) + { + m_aWakeupCondition.wait(); + + ::osl::ClearableMutexGuard thread_status_guard( m_aThreadStatusMutex ); + if ( m_bShutdownRequested ) + break; + + ::osl::ClearableMutexGuard message_container_guard( m_aMessageContainerMutex ); + + if ( !m_aXMessageList.empty() ) + { + thread_status_guard.clear(); + uno::Reference<mail::XMailMessage> message = m_aXMessageList.front(); + m_aXMessageList.pop_front(); + message_container_guard.clear(); + sendMailMessageNotifyListener( message ); + } + else // idle - put ourself to sleep + { + m_aWakeupCondition.reset(); + message_container_guard.clear(); + thread_status_guard.clear(); + MailDispatcherListenerContainer_t aListenerListcloned( cloneListener() ); + for( const auto & l : aListenerListcloned) + l->idle(); + } + } +} + +void MailDispatcher::onTerminated() +{ + //keep the reference until the end of onTerminated() because of the call order in the + //_threadFunc() from osl/thread.hxx + m_xSelfReference = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/mailmergehelper.cxx b/sw/source/uibase/dbui/mailmergehelper.cxx new file mode 100644 index 000000000..0981bce7f --- /dev/null +++ b/sw/source/uibase/dbui/mailmergehelper.cxx @@ -0,0 +1,832 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <mailmergehelper.hxx> +#include <mmconfigitem.hxx> +#include <docsh.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/docfile.hxx> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XColumn.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/mail/MailServiceProvider.hpp> +#include <com/sun/star/mail/XSmtpService.hpp> +#include <comphelper/processfactory.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <sfx2/passwd.hxx> + +#include <dbui.hrc> +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; + +namespace SwMailMergeHelper +{ + +OUString CallSaveAsDialog(weld::Window* pParent, OUString& rFilter) +{ + ::sfx2::FileDialogHelper aDialog( ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, + SwDocShell::Factory().GetFactoryName(), SfxFilterFlags::NONE, SfxFilterFlags::NONE, pParent); + + if (aDialog.Execute()!=ERRCODE_NONE) + { + return OUString(); + } + + rFilter = aDialog.GetRealFilter(); + uno::Reference < ui::dialogs::XFilePicker3 > xFP = aDialog.GetFilePicker(); + return xFP->getSelectedFiles().getConstArray()[0]; +} + +/* + simple address check: check for '@' + for at least one '.' after the '@' + and for at least two characters before and after the dot +*/ +bool CheckMailAddress( const OUString& rMailAddress ) +{ + const sal_Int32 nPosAt = rMailAddress.indexOf('@'); + if (nPosAt<0 || rMailAddress.lastIndexOf('@')!=nPosAt) + return false; + const sal_Int32 nPosDot = rMailAddress.indexOf('.', nPosAt); + return !(nPosDot<0 || nPosDot-nPosAt<3 || rMailAddress.getLength()-nPosDot<3); +} + +uno::Reference< mail::XSmtpService > ConnectToSmtpServer( + SwMailMergeConfigItem const & rConfigItem, + uno::Reference< mail::XMailService >& rxInMailService, + const OUString& rInMailServerPassword, + const OUString& rOutMailServerPassword, + weld::Window* pDialogParentWindow ) +{ + uno::Reference< mail::XSmtpService > xSmtpServer; + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + try + { + uno::Reference< mail::XMailServiceProvider > xMailServiceProvider( + mail::MailServiceProvider::create( xContext ) ); + xSmtpServer.set(xMailServiceProvider->create(mail::MailServiceType_SMTP), uno::UNO_QUERY); + + uno::Reference< mail::XConnectionListener> xConnectionListener(new SwConnectionListener()); + + if(rConfigItem.IsAuthentication() && rConfigItem.IsSMTPAfterPOP()) + { + uno::Reference< mail::XMailService > xInMailService = + xMailServiceProvider->create( + rConfigItem.IsInServerPOP() ? + mail::MailServiceType_POP3 : mail::MailServiceType_IMAP); + //authenticate at the POP or IMAP server first + OUString sPasswd = rConfigItem.GetInServerPassword(); + if(!rInMailServerPassword.isEmpty()) + sPasswd = rInMailServerPassword; + uno::Reference<mail::XAuthenticator> xAuthenticator = + new SwAuthenticator( + rConfigItem.GetInServerUserName(), + sPasswd, + pDialogParentWindow); + + xInMailService->addConnectionListener(xConnectionListener); + //check connection + uno::Reference< uno::XCurrentContext> xConnectionContext = + new SwConnectionContext( + rConfigItem.GetInServerName(), + rConfigItem.GetInServerPort(), + "Insecure"); + xInMailService->connect(xConnectionContext, xAuthenticator); + rxInMailService = xInMailService; + } + uno::Reference< mail::XAuthenticator> xAuthenticator; + if(rConfigItem.IsAuthentication() && + !rConfigItem.IsSMTPAfterPOP() && + !rConfigItem.GetMailUserName().isEmpty()) + { + OUString sPasswd = rConfigItem.GetMailPassword(); + if(!rOutMailServerPassword.isEmpty()) + sPasswd = rOutMailServerPassword; + xAuthenticator = + new SwAuthenticator(rConfigItem.GetMailUserName(), + sPasswd, + pDialogParentWindow); + } + else + xAuthenticator = new SwAuthenticator(); + //just to check if the server exists + xSmtpServer->getSupportedConnectionTypes(); + //check connection + + uno::Reference< uno::XCurrentContext> xConnectionContext = + new SwConnectionContext( + rConfigItem.GetMailServer(), + rConfigItem.GetMailPort(), + rConfigItem.IsSecureConnection() ? OUString("Ssl") : OUString("Insecure") ); + xSmtpServer->connect(xConnectionContext, xAuthenticator); + rxInMailService = xSmtpServer; + } + catch (const uno::Exception&) + { + OSL_FAIL("exception caught"); + } + return xSmtpServer; +} + +} //namespace + +struct SwAddressPreview_Impl +{ + std::vector< OUString > aAddresses; + sal_uInt16 nRows; + sal_uInt16 nColumns; + sal_uInt16 nSelectedAddress; + bool bEnableScrollBar; + + SwAddressPreview_Impl() : + nRows(1), + nColumns(1), + nSelectedAddress(0), + bEnableScrollBar(false) + { + } +}; + +OUString SwAddressPreview::FillData( + const OUString& rAddress, + SwMailMergeConfigItem const & rConfigItem, + const Sequence< OUString>* pAssignments) +{ + //find the column names in the address string (with name assignment!) and + //exchange the placeholder (like <Firstname>) with the database content + //unassigned columns are expanded to <not assigned> + Reference< XColumnsSupplier > xColsSupp( rConfigItem.GetResultSet(), UNO_QUERY); + Reference <XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr; + Sequence< OUString> aAssignment = pAssignments ? + *pAssignments : + rConfigItem.GetColumnAssignment( + rConfigItem.GetCurrentDBData() ); + const OUString* pAssignment = aAssignment.getConstArray(); + const std::vector<std::pair<OUString, int>>& rDefHeaders = rConfigItem.GetDefaultAddressHeaders(); + OUString sNotAssigned = "<" + SwResId(STR_NOTASSIGNED) + ">"; + + bool bIncludeCountry = rConfigItem.IsIncludeCountry(); + const OUString rExcludeCountry = rConfigItem.GetExcludeCountry(); + bool bSpecialReplacementForCountry = (!bIncludeCountry || !rExcludeCountry.isEmpty()); + OUString sCountryColumn; + if( bSpecialReplacementForCountry ) + { + sCountryColumn = rDefHeaders[MM_PART_COUNTRY].first; + Sequence< OUString> aSpecialAssignment = + rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() ); + if(aSpecialAssignment.getLength() > MM_PART_COUNTRY && aSpecialAssignment[MM_PART_COUNTRY].getLength()) + sCountryColumn = aSpecialAssignment[MM_PART_COUNTRY]; + } + + SwAddressIterator aIter(rAddress); + OUStringBuffer sAddress; + while(aIter.HasMore()) + { + SwMergeAddressItem aItem = aIter.Next(); + if(aItem.bIsColumn) + { + //get the default column name + + //find the appropriate assignment + OUString sConvertedColumn = aItem.sText; + auto nSize = std::min(sal_uInt32(rDefHeaders.size()), sal_uInt32(aAssignment.getLength())); + for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn) + { + if (rDefHeaders[nColumn].first == aItem.sText && + !pAssignment[nColumn].isEmpty()) + { + sConvertedColumn = pAssignment[nColumn]; + break; + } + } + if(!sConvertedColumn.isEmpty() && + xColAccess.is() && + xColAccess->hasByName(sConvertedColumn)) + { + //get the content and exchange it in the address string + Any aCol = xColAccess->getByName(sConvertedColumn); + Reference< XColumn > xColumn; + aCol >>= xColumn; + if(xColumn.is()) + { + try + { + OUString sReplace = xColumn->getString(); + + if( bSpecialReplacementForCountry && sCountryColumn == sConvertedColumn ) + { + if( !rExcludeCountry.isEmpty() && sReplace != rExcludeCountry ) + aItem.sText = sReplace; + else + aItem.sText.clear(); + } + else + { + aItem.sText = sReplace; + } + } + catch (const sdbc::SQLException&) + { + OSL_FAIL("SQLException caught"); + } + } + } + else + { + aItem.sText = sNotAssigned; + } + + } + sAddress.append(aItem.sText); + } + return sAddress.makeStringAndClear(); +} + +SwAddressPreview::SwAddressPreview(std::unique_ptr<weld::ScrolledWindow> xWindow) + : pImpl(new SwAddressPreview_Impl()) + , m_xVScrollBar(std::move(xWindow)) +{ + m_xVScrollBar->set_user_managed_scrolling(); + m_xVScrollBar->connect_vadjustment_changed(LINK(this, SwAddressPreview, ScrollHdl)); +} + +SwAddressPreview::~SwAddressPreview() +{ +} + +IMPL_LINK_NOARG(SwAddressPreview, ScrollHdl, weld::ScrolledWindow&, void) +{ + Invalidate(); +} + +void SwAddressPreview::AddAddress(const OUString& rAddress) +{ + pImpl->aAddresses.push_back(rAddress); + UpdateScrollBar(); +} + +void SwAddressPreview::SetAddress(const OUString& rAddress) +{ + pImpl->aAddresses.clear(); + pImpl->aAddresses.push_back(rAddress); + m_xVScrollBar->set_vpolicy(VclPolicyType::NEVER); + Invalidate(); +} + +sal_uInt16 SwAddressPreview::GetSelectedAddress()const +{ + OSL_ENSURE(pImpl->nSelectedAddress < pImpl->aAddresses.size(), "selection invalid"); + return pImpl->nSelectedAddress; +} + +void SwAddressPreview::SelectAddress(sal_uInt16 nSelect) +{ + OSL_ENSURE(pImpl->nSelectedAddress < pImpl->aAddresses.size(), "selection invalid"); + pImpl->nSelectedAddress = nSelect; + // now make it visible... + sal_uInt16 nSelectRow = nSelect / pImpl->nColumns; + sal_uInt16 nStartRow = m_xVScrollBar->vadjustment_get_value(); + if( (nSelectRow < nStartRow) || (nSelectRow >= (nStartRow + pImpl->nRows) )) + m_xVScrollBar->vadjustment_set_value(nSelectRow); +} + +void SwAddressPreview::Clear() +{ + pImpl->aAddresses.clear(); + pImpl->nSelectedAddress = 0; + UpdateScrollBar(); +} + +void SwAddressPreview::ReplaceSelectedAddress(const OUString& rNew) +{ + pImpl->aAddresses[pImpl->nSelectedAddress] = rNew; + Invalidate(); +} + +void SwAddressPreview::RemoveSelectedAddress() +{ + pImpl->aAddresses.erase(pImpl->aAddresses.begin() + pImpl->nSelectedAddress); + if(pImpl->nSelectedAddress) + --pImpl->nSelectedAddress; + UpdateScrollBar(); + Invalidate(); +} + +void SwAddressPreview::SetLayout(sal_uInt16 nRows, sal_uInt16 nColumns) +{ + pImpl->nRows = nRows; + pImpl->nColumns = nColumns; + UpdateScrollBar(); +} + +void SwAddressPreview::EnableScrollBar() +{ + pImpl->bEnableScrollBar = true; +} + +void SwAddressPreview::UpdateScrollBar() +{ + if (pImpl->nColumns) + { + sal_uInt16 nResultingRows = static_cast<sal_uInt16>(pImpl->aAddresses.size() + pImpl->nColumns - 1) / pImpl->nColumns; + ++nResultingRows; + auto nValue = m_xVScrollBar->vadjustment_get_value(); + if (nValue > nResultingRows) + nValue = nResultingRows; + m_xVScrollBar->set_vpolicy(pImpl->bEnableScrollBar && nResultingRows > pImpl->nRows ? VclPolicyType::ALWAYS : VclPolicyType::NEVER); + m_xVScrollBar->vadjustment_configure(nValue, 0, nResultingRows, 1, 10, pImpl->nRows); + } +} + +void SwAddressPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); + rRenderContext.SetFillColor(rSettings.GetWindowColor()); + rRenderContext.SetLineColor(COL_TRANSPARENT); + rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), GetOutputSizePixel())); + Color aPaintColor(IsEnabled() ? rSettings.GetWindowTextColor() : rSettings.GetDisableColor()); + rRenderContext.SetLineColor(aPaintColor); + + if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice())) + pDefaultDevice->SetPointFont(rRenderContext, GetDrawingArea()->get_font()); + vcl::Font aFont(rRenderContext.GetFont()); + aFont.SetColor(aPaintColor); + rRenderContext.SetFont(aFont); + + Size aSize(GetOutputSizePixel()); + sal_uInt16 nStartRow = 0; + if (m_xVScrollBar->get_vpolicy() != VclPolicyType::NEVER) + { + aSize.AdjustWidth(-m_xVScrollBar->get_vscroll_width()); + nStartRow = m_xVScrollBar->vadjustment_get_value(); + } + Size aPartSize(aSize.Width() / pImpl->nColumns, + aSize.Height() / pImpl->nRows); + aPartSize.AdjustWidth( -2 ); + aPartSize.AdjustHeight( -2 ); + + sal_uInt16 nAddress = nStartRow * pImpl->nColumns; + const sal_uInt16 nNumAddresses = static_cast<sal_uInt16>(pImpl->aAddresses.size()); + for (sal_uInt16 nRow = 0; nRow < pImpl->nRows ; ++nRow) + { + for (sal_uInt16 nCol = 0; nCol < pImpl->nColumns; ++nCol) + { + if (nAddress >= nNumAddresses) + break; + Point aPos(nCol * aPartSize.Width(), + nRow * aPartSize.Height()); + aPos.Move(1, 1); + bool bIsSelected = nAddress == pImpl->nSelectedAddress; + if ((pImpl->nColumns * pImpl->nRows) == 1) + bIsSelected = false; + OUString adr(pImpl->aAddresses[nAddress]); + DrawText_Impl(rRenderContext, adr, aPos, aPartSize, bIsSelected); + ++nAddress; + } + } + rRenderContext.SetClipRegion(); +} + +bool SwAddressPreview::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (rMEvt.IsLeft() && pImpl->nRows && pImpl->nColumns) + { + //determine the selected address + const Point& rMousePos = rMEvt.GetPosPixel(); + Size aSize(GetOutputSizePixel()); + Size aPartSize( aSize.Width()/pImpl->nColumns, aSize.Height()/pImpl->nRows ); + sal_uInt32 nRow = rMousePos.Y() / aPartSize.Height() ; + if (m_xVScrollBar->get_vpolicy() != VclPolicyType::NEVER) + { + nRow += m_xVScrollBar->vadjustment_get_value(); + } + sal_uInt32 nCol = rMousePos.X() / aPartSize.Width(); + sal_uInt32 nSelect = nRow * pImpl->nColumns + nCol; + + if( nSelect < pImpl->aAddresses.size() && + pImpl->nSelectedAddress != static_cast<sal_uInt16>(nSelect)) + { + pImpl->nSelectedAddress = static_cast<sal_uInt16>(nSelect); + m_aSelectHdl.Call(nullptr); + } + Invalidate(); + } + return true; +} + +bool SwAddressPreview::KeyInput( const KeyEvent& rKEvt ) +{ + sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); + bool bHandled = false; + if (pImpl->nRows && pImpl->nColumns) + { + sal_uInt32 nSelectedRow = pImpl->nSelectedAddress / pImpl->nColumns; + sal_uInt32 nSelectedColumn = pImpl->nSelectedAddress - (nSelectedRow * pImpl->nColumns); + switch(nKey) + { + case KEY_UP: + if(nSelectedRow) + --nSelectedRow; + bHandled = true; + break; + case KEY_DOWN: + if(pImpl->aAddresses.size() > o3tl::make_unsigned(pImpl->nSelectedAddress + pImpl->nColumns)) + ++nSelectedRow; + bHandled = true; + break; + case KEY_LEFT: + if(nSelectedColumn) + --nSelectedColumn; + bHandled = true; + break; + case KEY_RIGHT: + if(nSelectedColumn < o3tl::make_unsigned(pImpl->nColumns - 1) && + pImpl->aAddresses.size() - 1 > pImpl->nSelectedAddress ) + ++nSelectedColumn; + bHandled = true; + break; + } + sal_uInt32 nSelect = nSelectedRow * pImpl->nColumns + nSelectedColumn; + if( nSelect < pImpl->aAddresses.size() && + pImpl->nSelectedAddress != static_cast<sal_uInt16>(nSelect)) + { + pImpl->nSelectedAddress = static_cast<sal_uInt16>(nSelect); + m_aSelectHdl.Call(nullptr); + Invalidate(); + } + } + return bHandled; +} + +void SwAddressPreview::DrawText_Impl(vcl::RenderContext& rRenderContext, const OUString& rAddress, + const Point& rTopLeft, const Size& rSize, bool bIsSelected) +{ + rRenderContext.SetClipRegion(vcl::Region(tools::Rectangle(rTopLeft, rSize))); + if (bIsSelected) + { + //selection rectangle + rRenderContext.SetFillColor(COL_TRANSPARENT); + rRenderContext.DrawRect(tools::Rectangle(rTopLeft, rSize)); + } + sal_Int32 nHeight = GetTextHeight(); + Point aStart = rTopLeft; + //put it away from the border + aStart.Move(2, 2); + sal_Int32 nPos = 0; + do + { + rRenderContext.DrawText(aStart, rAddress.getToken(0, '\n', nPos)); + aStart.AdjustY(nHeight ); + } + while (nPos >= 0); +} + +SwMergeAddressItem SwAddressIterator::Next() +{ + //currently the string may either start with a '<' then it's a column + //otherwise it's simple text maybe containing a return + SwMergeAddressItem aRet; + if(!sAddress.isEmpty()) + { + if(sAddress[0] == '<') + { + aRet.bIsColumn = true; + sal_Int32 nClose = sAddress.indexOf('>'); + OSL_ENSURE(nClose != -1, "closing '>' not found"); + if( nClose != -1 ) + { + aRet.sText = sAddress.copy(1, nClose - 1); + sAddress = sAddress.copy(nClose + 1); + } + else + { + aRet.sText = sAddress.copy(1, 1); + sAddress = sAddress.copy(1); + } + } + else + { + sal_Int32 nOpen = sAddress.indexOf('<'); + sal_Int32 nReturn = sAddress.indexOf('\n'); + if(nReturn == 0) + { + aRet.bIsReturn = true; + aRet.sText = "\n"; + sAddress = sAddress.copy(1); + } + else if(-1 == nOpen && -1 == nReturn) + { + aRet.sText = sAddress; + sAddress.clear(); + } + else + { + if (nOpen == -1) + nOpen = sAddress.getLength(); + if (nReturn == -1) + nReturn = sAddress.getLength(); + sal_Int32 nTarget = std::min(nOpen, nReturn); + aRet.sText = sAddress.copy(0, nTarget); + sAddress = sAddress.copy(nTarget); + } + } + } + return aRet; + +} + +SwAuthenticator::~SwAuthenticator() +{ +} + +OUString SwAuthenticator::getUserName( ) +{ + return m_aUserName; +} + +OUString SwAuthenticator::getPassword( ) +{ + if(!m_aUserName.isEmpty() && m_aPassword.isEmpty() && m_pParentWindow) + { + SfxPasswordDialog aPasswdDlg(m_pParentWindow); + aPasswdDlg.SetMinLen(0); + if (RET_OK == aPasswdDlg.run()) + m_aPassword = aPasswdDlg.GetPassword(); + } + return m_aPassword; +} + +SwConnectionContext::SwConnectionContext( + const OUString& rMailServer, sal_Int16 nPort, + const OUString& rConnectionType) : + m_sMailServer(rMailServer), + m_nPort(nPort), + m_sConnectionType(rConnectionType) +{ +} + +SwConnectionContext::~SwConnectionContext() +{ +} + +uno::Any SwConnectionContext::getValueByName( const OUString& rName ) +{ + uno::Any aRet; + if( rName == "ServerName" ) + aRet <<= m_sMailServer; + else if( rName == "Port" ) + aRet <<= static_cast<sal_Int32>(m_nPort); + else if( rName == "ConnectionType" ) + aRet <<= m_sConnectionType; + return aRet; +} + +SwConnectionListener::~SwConnectionListener() +{ +} + +void SwConnectionListener::connected(const lang::EventObject& /*aEvent*/) +{ +} + +void SwConnectionListener::disconnected(const lang::EventObject& /*aEvent*/) +{ +} + +void SwConnectionListener::disposing(const lang::EventObject& /*aEvent*/) +{ +} + +SwMailTransferable::SwMailTransferable(const OUString& rBody, const OUString& rMimeType) : + cppu::WeakComponentImplHelper< datatransfer::XTransferable, beans::XPropertySet >(m_aMutex), + m_aMimeType( rMimeType ), + m_sBody( rBody ), + m_bIsBody( true ) +{ +} + +SwMailTransferable::SwMailTransferable(const OUString& rURL, + const OUString& rName, const OUString& rMimeType) : + cppu::WeakComponentImplHelper< datatransfer::XTransferable, beans::XPropertySet >(m_aMutex), + m_aMimeType( rMimeType ), + m_aURL(rURL), + m_aName( rName ), + m_bIsBody( false ) +{ +} + +SwMailTransferable::~SwMailTransferable() +{ +} + +uno::Any SwMailTransferable::getTransferData( const datatransfer::DataFlavor& /*aFlavor*/ ) +{ + uno::Any aRet; + if( m_bIsBody ) + aRet <<= m_sBody; + else + { + Sequence<sal_Int8> aData; + SfxMedium aMedium( m_aURL, StreamMode::STD_READ ); + SvStream* pStream = aMedium.GetInStream(); + if ( aMedium.GetErrorCode() == ERRCODE_NONE && pStream) + { + aData.realloc(pStream->TellEnd()); + pStream->Seek(0); + sal_Int8 * pData = aData.getArray(); + pStream->ReadBytes( pData, aData.getLength() ); + } + aRet <<= aData; + } + return aRet; +} + +uno::Sequence< datatransfer::DataFlavor > SwMailTransferable::getTransferDataFlavors( ) +{ + uno::Sequence< datatransfer::DataFlavor > aRet(1); + aRet[0].MimeType = m_aMimeType; + if( m_bIsBody ) + { + aRet[0].DataType = cppu::UnoType<OUString>::get(); + } + else + { + aRet[0].HumanPresentableName = m_aName; + aRet[0].DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get(); + } + return aRet; +} + +sal_Bool SwMailTransferable::isDataFlavorSupported( + const datatransfer::DataFlavor& aFlavor ) +{ + return (aFlavor.MimeType == m_aMimeType); +} + +uno::Reference< beans::XPropertySetInfo > SwMailTransferable::getPropertySetInfo( ) +{ + return uno::Reference< beans::XPropertySetInfo >(); +} + +void SwMailTransferable::setPropertyValue( const OUString& , const uno::Any& ) +{ +} + +uno::Any SwMailTransferable::getPropertyValue( const OUString& rPropertyName ) +{ + uno::Any aRet; + if ( rPropertyName == "URL" ) + aRet <<= m_aURL; + return aRet; +} + +void SwMailTransferable::addPropertyChangeListener( + const OUString&, const uno::Reference< beans::XPropertyChangeListener >& ) +{ +} + +void SwMailTransferable::removePropertyChangeListener( + const OUString&, + const uno::Reference< beans::XPropertyChangeListener >& ) +{ +} + +void SwMailTransferable::addVetoableChangeListener( + const OUString&, + const uno::Reference< beans::XVetoableChangeListener >& ) +{ +} + +void SwMailTransferable::removeVetoableChangeListener( + const OUString& , + const uno::Reference< beans::XVetoableChangeListener >& ) +{ +} + +SwMailMessage::SwMailMessage() : + cppu::WeakComponentImplHelper< mail::XMailMessage>(m_aMutex) +{ +} + +SwMailMessage::~SwMailMessage() +{ +} + +OUString SwMailMessage::getSenderName() +{ + return m_sSenderName; +} + +OUString SwMailMessage::getSenderAddress() +{ + return m_sSenderAddress; +} + +OUString SwMailMessage::getReplyToAddress() +{ + return m_sReplyToAddress; +} + +void SwMailMessage::setReplyToAddress( const OUString& _replytoaddress ) +{ + m_sReplyToAddress = _replytoaddress; +} + +OUString SwMailMessage::getSubject() +{ + return m_sSubject; +} + +void SwMailMessage::setSubject( const OUString& _subject ) +{ + m_sSubject = _subject; +} + +uno::Reference< datatransfer::XTransferable > SwMailMessage::getBody() +{ + return m_xBody; +} + +void SwMailMessage::setBody( + const uno::Reference< datatransfer::XTransferable >& rBody ) +{ + m_xBody = rBody; +} + +void SwMailMessage::addRecipient( const OUString& rRecipientAddress ) +{ + m_aRecipients.realloc(m_aRecipients.getLength() + 1); + m_aRecipients[m_aRecipients.getLength() - 1] = rRecipientAddress; +} + +void SwMailMessage::addCcRecipient( const OUString& rRecipientAddress ) +{ + m_aCcRecipients.realloc(m_aCcRecipients.getLength() + 1); + m_aCcRecipients[m_aCcRecipients.getLength() - 1] = rRecipientAddress; + +} + +void SwMailMessage::addBccRecipient( const OUString& rRecipientAddress ) +{ + m_aBccRecipients.realloc(m_aBccRecipients.getLength() + 1); + m_aBccRecipients[m_aBccRecipients.getLength() - 1] = rRecipientAddress; +} + +uno::Sequence< OUString > SwMailMessage::getRecipients( ) +{ + return m_aRecipients; +} + +uno::Sequence< OUString > SwMailMessage::getCcRecipients( ) +{ + return m_aCcRecipients; +} + +uno::Sequence< OUString > SwMailMessage::getBccRecipients( ) +{ + return m_aBccRecipients; +} + +void SwMailMessage::addAttachment( const mail::MailAttachment& rMailAttachment ) +{ + m_aAttachments.realloc(m_aAttachments.getLength() + 1); + m_aAttachments[m_aAttachments.getLength() - 1] = rMailAttachment; +} + +uno::Sequence< mail::MailAttachment > SwMailMessage::getAttachments( ) +{ + return m_aAttachments; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/mailmergetoolbarcontrols.cxx b/sw/source/uibase/dbui/mailmergetoolbarcontrols.cxx new file mode 100644 index 000000000..e2cff7390 --- /dev/null +++ b/sw/source/uibase/dbui/mailmergetoolbarcontrols.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 <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <strings.hrc> +#include <mmconfigitem.hxx> +#include <swmodule.hxx> +#include <view.hxx> + +using namespace css; + +namespace { + +class CurrentEdit final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::Entry> m_xWidget; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); +public: + CurrentEdit(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/swriter/ui/editbox.ui", "EditBox") + , m_xWidget(m_xBuilder->weld_entry("entry")) + { + m_xWidget->connect_key_press(LINK(this, CurrentEdit, KeyInputHdl)); + SetSizePixel(m_xWidget->get_preferred_size()); + } + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void set_sensitive(bool bSensitive) + { + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); + } + + bool get_sensitive() const + { + return m_xWidget->get_sensitive(); + } + + void set_text(const OUString& rText) + { + m_xWidget->set_text(rText); + } + + OUString get_text() const + { + return m_xWidget->get_text(); + } + + void connect_activate(const Link<weld::Entry&, bool>& rLink) + { + m_xWidget->connect_activate(rLink); + } + + virtual ~CurrentEdit() override + { + disposeOnce(); + } +}; + +IMPL_LINK(CurrentEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +/// Controller for .uno:MailMergeCurrentEntry toolbar checkbox: creates the checkbox & handles the value. +class MMCurrentEntryController : public svt::ToolboxController, public lang::XServiceInfo +{ + VclPtr<CurrentEdit> m_xCurrentEdit; + + DECL_LINK(CurrentEditUpdatedHdl, weld::Entry&, bool); + +public: + explicit MMCurrentEntryController(const uno::Reference<uno::XComponentContext>& rContext) + : svt::ToolboxController(rContext, uno::Reference<frame::XFrame>(), ".uno:MailMergeCurrentEntry") + , m_xCurrentEdit(nullptr) + { + } + + // XInterface + virtual uno::Any SAL_CALL queryInterface(const uno::Type& aType) override + { + uno::Any a(ToolboxController::queryInterface(aType)); + if (a.hasValue()) + return a; + + return ::cppu::queryInterface(aType, static_cast<lang::XServiceInfo*>(this)); + } + + void SAL_CALL acquire() throw () override + { + ToolboxController::acquire(); + } + + void SAL_CALL release() throw () override + { + ToolboxController::release(); + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "lo.writer.MMCurrentEntryController"; + } + + 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.frame.ToolbarController" }; + } + + // XComponent + virtual void SAL_CALL dispose() override; + + // XToolbarController + virtual uno::Reference<awt::XWindow> SAL_CALL createItemWindow(const uno::Reference<awt::XWindow>& rParent) override; + + // XStatusListener + virtual void SAL_CALL statusChanged(const frame::FeatureStateEvent& rEvent) override; +}; + +class ExcludeCheckBox final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::CheckButton> m_xWidget; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); +public: + ExcludeCheckBox(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/swriter/ui/checkbox.ui", "CheckBox") + , m_xWidget(m_xBuilder->weld_check_button("checkbutton")) + { + m_xWidget->set_label(SwResId(ST_EXCLUDE)); + m_xWidget->connect_key_press(LINK(this, ExcludeCheckBox, KeyInputHdl)); + SetSizePixel(m_xWidget->get_preferred_size()); + } + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void set_sensitive(bool bSensitive) + { + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); + } + + void set_active(bool bActive) + { + m_xWidget->set_active(bActive); + } + + void connect_toggled(const Link<weld::ToggleButton&, void>& rLink) + { + m_xWidget->connect_toggled(rLink); + } + + virtual ~ExcludeCheckBox() override + { + disposeOnce(); + } +}; + +IMPL_LINK(ExcludeCheckBox, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +/// Controller for .uno:MailMergeExcludeEntry toolbar checkbox: creates the checkbox & handles the value. +class MMExcludeEntryController : public svt::ToolboxController, public lang::XServiceInfo +{ + VclPtr<ExcludeCheckBox> m_xExcludeCheckbox; + + DECL_STATIC_LINK(MMExcludeEntryController, ExcludeHdl, weld::ToggleButton&, void); + +public: + explicit MMExcludeEntryController(const uno::Reference<uno::XComponentContext>& rContext) + : svt::ToolboxController(rContext, uno::Reference<frame::XFrame>(), ".uno:MailMergeExcludeEntry") + , m_xExcludeCheckbox(nullptr) + { + } + + // XInterface + virtual uno::Any SAL_CALL queryInterface(const uno::Type& aType) override + { + uno::Any a(ToolboxController::queryInterface(aType)); + if (a.hasValue()) + return a; + + return ::cppu::queryInterface(aType, static_cast<lang::XServiceInfo*>(this)); + } + + void SAL_CALL acquire() throw () override + { + ToolboxController::acquire(); + } + + void SAL_CALL release() throw () override + { + ToolboxController::release(); + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "lo.writer.MMExcludeEntryController"; + } + + 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.frame.ToolbarController" }; + } + + // XComponent + virtual void SAL_CALL dispose() override; + + // XToolbarController + virtual uno::Reference<awt::XWindow> SAL_CALL createItemWindow(const uno::Reference<awt::XWindow>& rParent) override; + + // XStatusListener + virtual void SAL_CALL statusChanged(const frame::FeatureStateEvent& rEvent) override; +}; + +void MMCurrentEntryController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + svt::ToolboxController::dispose(); + m_xCurrentEdit.disposeAndClear(); +} + +uno::Reference<awt::XWindow> MMCurrentEntryController::createItemWindow(const uno::Reference<awt::XWindow>& rParent) +{ + VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent); + ToolBox* pToolbar = dynamic_cast<ToolBox*>(pParent.get()); + if (pToolbar) + { + // make it visible + m_xCurrentEdit = VclPtr<CurrentEdit>::Create(pToolbar); + m_xCurrentEdit->connect_activate(LINK(this, MMCurrentEntryController, CurrentEditUpdatedHdl)); + } + + return VCLUnoHelper::GetInterface(m_xCurrentEdit); +} + +IMPL_LINK(MMCurrentEntryController, CurrentEditUpdatedHdl, weld::Entry&, rEdit, bool) +{ + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->GetMailMergeConfigItem(); + + if (!xConfigItem) + return true; + + OUString aText(rEdit.get_text()); + sal_Int32 nEntry = aText.toInt32(); + if (!aText.isEmpty() && nEntry != xConfigItem->GetResultSetPosition()) + { + xConfigItem->MoveResultSet(nEntry); + // notify about the change + dispatchCommand(".uno:MailMergeCurrentEntry", uno::Sequence<beans::PropertyValue>()); + } + return true; +}; + +void MMCurrentEntryController::statusChanged(const frame::FeatureStateEvent& rEvent) +{ + if (!m_xCurrentEdit) + return; + + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->GetMailMergeConfigItem(); + + if (!xConfigItem || !rEvent.IsEnabled) + { + m_xCurrentEdit->set_sensitive(false); + m_xCurrentEdit->set_text(""); + } + else + { + sal_Int32 nEntry = m_xCurrentEdit->get_text().toInt32(); + if (!m_xCurrentEdit->get_sensitive() || nEntry != xConfigItem->GetResultSetPosition()) + { + m_xCurrentEdit->set_sensitive(true); + m_xCurrentEdit->set_text(OUString::number(xConfigItem->GetResultSetPosition())); + } + } +} + +void MMExcludeEntryController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + svt::ToolboxController::dispose(); + m_xExcludeCheckbox.disposeAndClear(); +} + +uno::Reference<awt::XWindow> MMExcludeEntryController::createItemWindow(const uno::Reference<awt::XWindow>& rParent) +{ + VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent); + ToolBox* pToolbar = dynamic_cast<ToolBox*>(pParent.get()); + if (pToolbar) + { + // make it visible + m_xExcludeCheckbox = VclPtr<ExcludeCheckBox>::Create(pToolbar); + m_xExcludeCheckbox->connect_toggled(LINK(this, MMExcludeEntryController, ExcludeHdl)); + } + + return VCLUnoHelper::GetInterface(m_xExcludeCheckbox); +} + +IMPL_STATIC_LINK(MMExcludeEntryController, ExcludeHdl, weld::ToggleButton&, rCheckbox, void) +{ + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->GetMailMergeConfigItem(); + + if (xConfigItem) + xConfigItem->ExcludeRecord(xConfigItem->GetResultSetPosition(), rCheckbox.get_active()); +}; + +void MMExcludeEntryController::statusChanged(const frame::FeatureStateEvent& rEvent) +{ + if (!m_xExcludeCheckbox) + return; + + SwView* pView = ::GetActiveView(); + std::shared_ptr<SwMailMergeConfigItem> xConfigItem; + if (pView) + xConfigItem = pView->GetMailMergeConfigItem(); + + if (!xConfigItem || !rEvent.IsEnabled) + { + m_xExcludeCheckbox->set_sensitive(false); + m_xExcludeCheckbox->set_active(false); + } + else + { + m_xExcludeCheckbox->set_sensitive(false); + m_xExcludeCheckbox->set_active(xConfigItem->IsRecordExcluded(xConfigItem->GetResultSetPosition())); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface * +lo_writer_MMCurrentEntryController_get_implementation( + uno::XComponentContext *context, + uno::Sequence<uno::Any> const &) +{ + return cppu::acquire(new MMCurrentEntryController(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface * +lo_writer_MMExcludeEntryController_get_implementation( + uno::XComponentContext *context, + uno::Sequence<uno::Any> const &) +{ + return cppu::acquire(new MMExcludeEntryController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dbui/mmconfigitem.cxx b/sw/source/uibase/dbui/mmconfigitem.cxx new file mode 100644 index 000000000..84cb67742 --- /dev/null +++ b/sw/source/uibase/dbui/mmconfigitem.cxx @@ -0,0 +1,1711 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <mmconfigitem.hxx> +#include <vector> +#include <swtypes.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> +#include <com/sun/star/sdb/CommandType.hpp> +#include <comphelper/sequence.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <unotools/configitem.hxx> +#include <tools/diagnose_ex.h> +#include <mailmergehelper.hxx> +#include <swunohelper.hxx> +#include <dbmgr.hxx> +#include <view.hxx> +#include <unodispatch.hxx> +#include <wrtsh.hxx> +#include <dbui.hrc> + +using namespace utl; +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::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; + +const char cAddressDataAssignments[] = "AddressDataAssignments"; +const char cDBColumnAssignments[] = "DBColumnAssignments"; +const char cDataSourceName[] = "DataSource/DataSourceName"; +const char cDataTableName[] = "DataSource/DataTableName" ; +const char cDataCommandType[] = "DataSource/DataCommandType"; + +#define SECURE_PORT 587 +#define DEFAULT_PORT 25 +#define POP_PORT 110 +#define POP_SECURE_PORT 995 +#define IMAP_PORT 143 +#define IMAP_SECURE_PORT 993 + +namespace { + +struct DBAddressDataAssignment +{ + SwDBData aDBData; + Sequence< OUString> aDBColumnAssignments; + //if loaded the name of the node has to be saved + OUString sConfigNodeName; + //all created or changed assignments need to be stored + bool bColumnAssignmentsChanged; + + DBAddressDataAssignment() : + bColumnAssignmentsChanged(false) + {} +}; + +} + +class SwMailMergeConfigItem_Impl : public utl::ConfigItem +{ + friend class SwMailMergeConfigItem; + Reference< XDataSource> m_xSource; + SharedConnection m_xConnection; + Reference< XColumnsSupplier> m_xColumnsSupplier; + Reference< XResultSet> m_xResultSet; + SwDBData m_aDBData; + OUString m_sFilter; + sal_Int32 m_nResultSetCursorPos; + + std::vector<DBAddressDataAssignment> m_aAddressDataAssignments; + std::vector< OUString> m_aAddressBlocks; + sal_Int32 m_nCurrentAddressBlock; + bool m_bIsAddressBlock; + bool m_bIsHideEmptyParagraphs; + + bool m_bIsOutputToLetter; + bool m_bIncludeCountry; + OUString m_sExcludeCountry; + + bool m_bIsGreetingLine; + bool m_bIsIndividualGreetingLine; + std::vector< OUString> m_aFemaleGreetingLines; + sal_Int32 m_nCurrentFemaleGreeting; + std::vector< OUString> m_aMaleGreetingLines; + sal_Int32 m_nCurrentMaleGreeting; + std::vector< OUString> m_aNeutralGreetingLines; + sal_Int32 m_nCurrentNeutralGreeting; + OUString m_sFemaleGenderValue; + uno::Sequence< OUString> m_aSavedDocuments; + + bool m_bIsGreetingLineInMail; + bool m_bIsIndividualGreetingLineInMail; + + //mail settings + OUString m_sMailDisplayName; + OUString m_sMailAddress; + OUString m_sMailReplyTo; + OUString m_sMailServer; + OUString m_sMailUserName; + OUString m_sMailPassword; + + bool m_bIsSMPTAfterPOP; + OUString m_sInServerName; + sal_Int16 m_nInServerPort; + bool m_bInServerPOP; + OUString m_sInServerUserName; + OUString m_sInServerPassword; + + sal_Int16 m_nMailPort; + bool m_bIsMailReplyTo; + bool m_bIsSecureConnection; + bool m_bIsAuthentication; + + bool m_bIsEMailSupported; + + std::vector<std::pair<OUString, int>> m_AddressHeaderSA; + + //these addresses are not stored in the configuration + std::vector< SwDocMergeInfo > m_aMergeInfos; + + //we do overwrite the usersettings in a special case + //then we do remind the usersettings here + bool m_bUserSettingWereOverwritten; + bool m_bIsAddressBlock_LastUserSetting; + bool m_bIsGreetingLineInMail_LastUserSetting; + bool m_bIsGreetingLine_LastUserSetting; + + static const Sequence< OUString>& GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwMailMergeConfigItem_Impl(); + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + Sequence< OUString> GetAddressBlocks(bool bConvertToConfig = false) const; + void SetAddressBlocks( + const Sequence< OUString>& rBlocks, + bool bConvertFromConfig = false); + uno::Sequence< OUString> + GetGreetings(SwMailMergeConfigItem::Gender eType, + bool bConvertToConfig = false) const; + void SetGreetings(SwMailMergeConfigItem::Gender eType, + const uno::Sequence< OUString>& rBlocks, + bool bConvertFromConfig = false); + + void SetCurrentAddressBlockIndex( sal_Int32 nSet ); + sal_Int32 GetCurrentAddressBlockIndex() const + { return m_nCurrentAddressBlock; } + sal_Int32 GetCurrentGreeting(SwMailMergeConfigItem::Gender eType) const; + void SetCurrentGreeting(SwMailMergeConfigItem::Gender eType, sal_Int32 nIndex); + +}; + +SwMailMergeConfigItem_Impl::SwMailMergeConfigItem_Impl() : + ConfigItem("Office.Writer/MailMergeWizard", ConfigItemMode::NONE), + m_nResultSetCursorPos(-1), + m_nCurrentAddressBlock(0), + m_bIsAddressBlock(true), + m_bIsHideEmptyParagraphs(false), + m_bIsOutputToLetter(true), + m_bIncludeCountry(false), + m_bIsGreetingLine(true), + m_bIsIndividualGreetingLine(false), + m_nCurrentFemaleGreeting(0), + m_nCurrentMaleGreeting(0), + m_nCurrentNeutralGreeting(0), + m_bIsGreetingLineInMail(false), + m_bIsIndividualGreetingLineInMail(false), + m_bIsSMPTAfterPOP(false), + m_nInServerPort( POP_SECURE_PORT ), + m_bInServerPOP( true ), + m_nMailPort(SECURE_PORT), + m_bIsMailReplyTo(false), + m_bIsSecureConnection(true), + m_bIsAuthentication(false), + + m_bIsEMailSupported(false), + m_bUserSettingWereOverwritten(false), + m_bIsAddressBlock_LastUserSetting(false), + m_bIsGreetingLineInMail_LastUserSetting(false), + m_bIsGreetingLine_LastUserSetting(false) +{ + for (size_t i = 0; i < SAL_N_ELEMENTS(SA_ADDRESS_HEADER); ++i) + { + m_AddressHeaderSA.emplace_back(SwResId(SA_ADDRESS_HEADER[i].first), SA_ADDRESS_HEADER[i].second); + } + + const Sequence<OUString>& rNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(rNames); + const Any* pValues = aValues.getConstArray(); + assert(aValues.getLength() == rNames.getLength()); + if(aValues.getLength() == rNames.getLength()) + { + for(int nProp = 0; nProp < rNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] >>= m_bIsOutputToLetter; break; + case 1: pValues[nProp] >>= m_bIncludeCountry; break; + case 2: pValues[nProp] >>= m_sExcludeCountry; break; + case 3: + { + Sequence< OUString> aBlocks; + pValues[nProp] >>= aBlocks; + SetAddressBlocks(aBlocks, true); + } + break; + case 4: pValues[nProp] >>= m_bIsAddressBlock; break; + case 5: pValues[nProp] >>= m_bIsGreetingLine; break; + case 6: pValues[nProp] >>= m_bIsIndividualGreetingLine; break; + case 7 : + case 8 : + case 9 : + { + Sequence< OUString> aGreetings; + pValues[nProp] >>= aGreetings; + SetGreetings(SwMailMergeConfigItem::Gender( + SwMailMergeConfigItem::FEMALE + nProp - 7), aGreetings, true); + } + break; + + case 10: pValues[nProp] >>= m_nCurrentFemaleGreeting; break; + case 11: pValues[nProp] >>= m_nCurrentMaleGreeting; break; + case 12: pValues[nProp] >>= m_nCurrentNeutralGreeting; break; + case 13: pValues[nProp] >>= m_sFemaleGenderValue; break; + case 14: pValues[nProp] >>= m_sMailDisplayName; break; + case 15: pValues[nProp] >>= m_sMailAddress; break; + case 16: pValues[nProp] >>= m_bIsMailReplyTo; break; + case 17: pValues[nProp] >>= m_sMailReplyTo; break; + case 18: pValues[nProp] >>= m_sMailServer; break; + case 19: pValues[nProp] >>= m_nMailPort; break; + case 20: pValues[nProp] >>= m_bIsSecureConnection; break; + case 21: pValues[nProp] >>= m_bIsAuthentication; break; + case 22: pValues[nProp] >>= m_sMailUserName; break; + case 23: pValues[nProp] >>= m_sMailPassword; break; + case 24 :pValues[nProp] >>= m_aDBData.sDataSource; break; + case 25 :pValues[nProp] >>= m_aDBData.sCommand; break; + case 26 : + { + short nTemp = 0; + if(pValues[nProp] >>= nTemp) + m_aDBData.nCommandType = nTemp; + } + break; + case 27: pValues[nProp] >>= m_sFilter; break; + case 28: pValues[nProp] >>= m_aSavedDocuments; break; + case 29: + pValues[nProp] >>= m_bIsEMailSupported; + break; + case 30: pValues[nProp] >>= m_bIsGreetingLineInMail; break; + case 31: pValues[nProp] >>= m_bIsIndividualGreetingLineInMail; break; + case 32: pValues[nProp] >>= m_bIsSMPTAfterPOP; break; + case 33: pValues[nProp] >>= m_sInServerName; break; + case 34: pValues[nProp] >>= m_nInServerPort; break; + case 35: pValues[nProp] >>= m_bInServerPOP; break; + case 36: pValues[nProp] >>= m_sInServerUserName; break; + case 37: pValues[nProp] >>= m_sInServerPassword; break; + case 38: pValues[nProp] >>= m_bIsHideEmptyParagraphs; break; + case 39: pValues[nProp] >>= m_nCurrentAddressBlock; break; + } + } + } + //read the list of data base assignments + Sequence<OUString> aAssignments = GetNodeNames(cAddressDataAssignments); + if(aAssignments.hasElements()) + { + //create a list of property names to load the URLs of all data bases + const OUString* pAssignments = aAssignments.getConstArray(); + Sequence< OUString > aAssignProperties(4 * aAssignments.getLength()); + OUString* pAssignProperties = aAssignProperties.getArray(); + sal_Int32 nAssign; + for(nAssign = 0; nAssign < aAssignProperties.getLength(); nAssign += 4) + { + OUString sAssignPath = OUStringLiteral(cAddressDataAssignments) + + "/" + + pAssignments[nAssign / 4] + + "/"; + pAssignProperties[nAssign] = sAssignPath; + pAssignProperties[nAssign] += cDataSourceName; + pAssignProperties[nAssign + 1] = sAssignPath; + pAssignProperties[nAssign + 1] += cDataTableName; + pAssignProperties[nAssign + 2] = sAssignPath; + pAssignProperties[nAssign + 2] += cDataCommandType; + pAssignProperties[nAssign + 3] = sAssignPath; + pAssignProperties[nAssign + 3] += cDBColumnAssignments; + } + Sequence<Any> aAssignValues = GetProperties(aAssignProperties); + const Any* pAssignValues = aAssignValues.getConstArray(); + for(nAssign = 0; nAssign < aAssignValues.getLength(); nAssign += 4 ) + { + DBAddressDataAssignment aAssignment; + pAssignValues[nAssign] >>= aAssignment.aDBData.sDataSource; + pAssignValues[nAssign + 1] >>= aAssignment.aDBData.sCommand; + pAssignValues[nAssign + 2] >>= aAssignment.aDBData.nCommandType; + pAssignValues[nAssign + 3] >>= aAssignment.aDBColumnAssignments; + aAssignment.sConfigNodeName = pAssignments[nAssign / 4]; + m_aAddressDataAssignments.push_back(aAssignment); + } + } + //check if the saved documents still exist + if(m_aSavedDocuments.hasElements()) + { + uno::Sequence< OUString > aTempDocuments(m_aSavedDocuments.getLength()); + OUString* pTempDocuments = std::copy_if(m_aSavedDocuments.begin(), m_aSavedDocuments.end(), aTempDocuments.begin(), + [](const OUString& rDoc) { return SWUnoHelper::UCB_IsFile( rDoc ); }); + sal_Int32 nIndex = static_cast<sal_Int32>(std::distance(aTempDocuments.begin(), pTempDocuments)); + if(nIndex < m_aSavedDocuments.getLength()) + { + m_aSavedDocuments = aTempDocuments; + m_aSavedDocuments.realloc(nIndex); + } + } +} + +void SwMailMergeConfigItem_Impl::SetCurrentAddressBlockIndex( sal_Int32 nSet ) +{ + if(m_aAddressBlocks.size() >= sal::static_int_cast<sal_uInt32, sal_Int32>(nSet)) + { + m_nCurrentAddressBlock = nSet; + SetModified(); + } +} + +static OUString lcl_CreateNodeName(Sequence<OUString>& rAssignments ) +{ + sal_Int32 nStart = rAssignments.getLength(); + OUString sNewName; + //search if the name exists + while(true) + { + sNewName = "_" + OUString::number(nStart++); + if(comphelper::findValue(rAssignments, sNewName) == -1) + break; + } + // add the new name to the array of existing names + rAssignments.realloc(rAssignments.getLength() + 1); + rAssignments.getArray()[rAssignments.getLength() - 1] = sNewName; + return sNewName; +} + +static void lcl_ConvertToNumbers(OUString& rBlock, const std::vector<std::pair<OUString, int>>& rHeaders ) +{ + //convert the strings used for UI to numbers used for the configuration + OUString sBlock(rBlock.replaceAll("\n", "\\n")); + for (size_t i = 0; i < rHeaders.size(); ++i) + { + OUString sHeader = "<" + rHeaders[i].first + ">"; + OUString sReplace = "<" + OUStringChar(sal_Unicode('0' + i)) + ">"; + sBlock = sBlock.replaceAll(sHeader, sReplace); + } + rBlock = sBlock; +} + +static void lcl_ConvertFromNumbers(OUString& rBlock, const std::vector<std::pair<OUString, int>>& rHeaders) +{ + //convert the numbers used for the configuration to strings used for UI to numbers + //doesn't use ReplaceAll to prevent expansion of numbers inside of the headers + SwAddressIterator aGreetingIter(rBlock.replaceAll("\\n", "\n")); + OUStringBuffer sBlock; + while(aGreetingIter.HasMore()) + { + SwMergeAddressItem aNext = aGreetingIter.Next(); + if(aNext.bIsColumn) + { + //the text should be 1 characters long + sal_Unicode cChar = aNext.sText[0]; + if(cChar >= '0' && cChar <= 'c') + { + sBlock.append("<"); + sal_uInt16 nHeader = cChar - '0'; + if(nHeader < rHeaders.size()) + sBlock.append(rHeaders[nHeader].first); + sBlock.append(">"); + } + else + { + SAL_WARN("sw.ui", "parse error in address block or greeting line"); + } + } + else + sBlock.append(aNext.sText); + } + rBlock = sBlock.makeStringAndClear(); +} + +const Sequence<OUString>& SwMailMergeConfigItem_Impl::GetPropertyNames() +{ + static Sequence<OUString> aNames { + "OutputToLetter", // 0 + "IncludeCountry", // 1 + "ExcludeCountry", // 2 + "AddressBlockSettings", // 3 + "IsAddressBlock", // 4 + "IsGreetingLine", // 5 + "IsIndividualGreetingLine", // 6 + "FemaleGreetingLines", // 7 + "MaleGreetingLines", // 8 + "NeutralGreetingLines", // 9 + "CurrentFemaleGreeting", // 10 + "CurrentMaleGreeting", // 11 + "CurrentNeutralGreeting", // 12 + "FemaleGenderValue", // 13 + "MailDisplayName", // 14 + "MailAddress", // 15 + "IsMailReplyTo", // 16 + "MailReplyTo", // 17 + "MailServer", // 18 + "MailPort", // 19 + "IsSecureConnection", // 20 + "IsAuthentication", // 21 + "MailUserName", // 22 + "MailPassword", // 23 + "DataSource/DataSourceName", // 24 + "DataSource/DataTableName", // 25 + "DataSource/DataCommandType",// 26 + "Filter", // 27 + "SavedDocuments", // 28 + "EMailSupported", // 29 + "IsEMailGreetingLine", //30 + "IsEMailIndividualGreetingLine", //31 + "IsSMPTAfterPOP", //32 + "InServerName", //33 + "InServerPort", //34 + "InServerIsPOP", //35 + "InServerUserName", //36 + "InServerPassword", //37 + "IsHideEmptyParagraphs", //38 + "CurrentAddressBlock" //39 + }; + return aNames; +} + +void SwMailMergeConfigItem_Impl::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwMailMergeConfigItem_Impl::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= m_bIsOutputToLetter; break; + case 1: pValues[nProp] <<= m_bIncludeCountry; break; + case 2: pValues[nProp] <<= m_sExcludeCountry; break; + case 3: pValues[nProp] <<= GetAddressBlocks(true); break; + case 4: + { + if( m_bUserSettingWereOverwritten) + pValues[nProp] <<= m_bIsAddressBlock_LastUserSetting; + else + pValues[nProp] <<= m_bIsAddressBlock; + break; + } + case 5: + { + if( m_bUserSettingWereOverwritten) + pValues[nProp] <<= m_bIsGreetingLine_LastUserSetting; + else + pValues[nProp] <<= m_bIsGreetingLine; + break; + } + case 6: pValues[nProp] <<= m_bIsIndividualGreetingLine; break; + case 7: + case 8: + case 9: + pValues[nProp] <<= GetGreetings( + SwMailMergeConfigItem::Gender( + SwMailMergeConfigItem::FEMALE + nProp - 7), true); + break; + case 10: pValues[nProp] <<= m_nCurrentFemaleGreeting; break; + case 11: pValues[nProp] <<= m_nCurrentMaleGreeting; break; + case 12: pValues[nProp] <<= m_nCurrentNeutralGreeting; break; + case 13: pValues[nProp] <<= m_sFemaleGenderValue; break; + case 14: pValues[nProp] <<= m_sMailDisplayName; break; + case 15: pValues[nProp] <<= m_sMailAddress; break; + case 16: pValues[nProp] <<= m_bIsMailReplyTo; break; + case 17: pValues[nProp] <<= m_sMailReplyTo; break; + case 18: pValues[nProp] <<= m_sMailServer; break; + case 19: pValues[nProp] <<= m_nMailPort; break; + case 20: pValues[nProp] <<= m_bIsSecureConnection; break; + case 21: pValues[nProp] <<= m_bIsAuthentication; break; + case 22: pValues[nProp] <<= m_sMailUserName; break; + case 23: pValues[nProp] <<= m_sMailPassword; break; + case 24 :pValues[nProp] <<= m_aDBData.sDataSource; break; + case 25 :pValues[nProp] <<= m_aDBData.sCommand; break; + case 26 :pValues[nProp] <<= static_cast<short>(m_aDBData.nCommandType); break; + case 27 :pValues[nProp] <<= m_sFilter; break; + case 28 :pValues[nProp] <<= m_aSavedDocuments; break; + case 29: pValues[nProp] <<= m_bIsEMailSupported; break; + case 30: + { + if( m_bUserSettingWereOverwritten) + pValues[nProp] <<= m_bIsGreetingLineInMail_LastUserSetting; + else + pValues[nProp] <<= m_bIsGreetingLineInMail; + break; + } + case 31: pValues[nProp] <<= m_bIsIndividualGreetingLineInMail; break; + case 32: pValues[nProp] <<= m_bIsSMPTAfterPOP; break; + case 33: pValues[nProp] <<= m_sInServerName; break; + case 34: pValues[nProp] <<= m_nInServerPort; break; + case 35: pValues[nProp] <<= m_bInServerPOP; break; + case 36: pValues[nProp] <<= m_sInServerUserName; break; + case 37: pValues[nProp] <<= m_sInServerPassword; break; + case 38: pValues[nProp] <<= m_bIsHideEmptyParagraphs; break; + case 39: pValues[nProp] <<= m_nCurrentAddressBlock; break; + } + } + PutProperties(aNames, aValues); + //store the changed / new assignments + + //load the existing node names to find new names + Sequence<OUString> aAssignments = GetNodeNames(cAddressDataAssignments); + + for(const auto& rAssignment : m_aAddressDataAssignments) + { + if(rAssignment.bColumnAssignmentsChanged) + { + //create a new node name + OUString sNewNode = !rAssignment.sConfigNodeName.isEmpty() ? + rAssignment.sConfigNodeName : + lcl_CreateNodeName(aAssignments); + OUString sSlash = "/"; + OUString sNodePath = cAddressDataAssignments + sSlash + sNewNode + sSlash; + //only one new entry is written + Sequence< PropertyValue > aNewValues(4); + PropertyValue* pNewValues = aNewValues.getArray(); + pNewValues[0].Name = sNodePath; + pNewValues[0].Name += cDataSourceName; + pNewValues[0].Value <<= rAssignment.aDBData.sDataSource; + pNewValues[1].Name = sNodePath; + pNewValues[1].Name += cDataTableName; + pNewValues[1].Value <<= rAssignment.aDBData.sCommand; + pNewValues[2].Name = sNodePath; + pNewValues[2].Name += cDataCommandType; + pNewValues[2].Value <<= rAssignment.aDBData.nCommandType; + pNewValues[3].Name = sNodePath; + pNewValues[3].Name += cDBColumnAssignments; + pNewValues[3].Value <<= rAssignment.aDBColumnAssignments; + + SetSetProperties(cAddressDataAssignments, aNewValues); + } + } + + m_bUserSettingWereOverwritten = false; +} + +Sequence< OUString> SwMailMergeConfigItem_Impl::GetAddressBlocks( + bool bConvertToConfig) const +{ + Sequence< OUString> aRet(m_aAddressBlocks.size()); + std::transform(m_aAddressBlocks.begin(), m_aAddressBlocks.end(), aRet.begin(), + [this, bConvertToConfig](const OUString& rBlock) -> OUString { + OUString sBlock = rBlock; + if(bConvertToConfig) + lcl_ConvertToNumbers(sBlock, m_AddressHeaderSA); + return sBlock; + }); + return aRet; +} + +void SwMailMergeConfigItem_Impl::SetAddressBlocks( + const Sequence< OUString>& rBlocks, + bool bConvertFromConfig) +{ + m_aAddressBlocks.clear(); + std::transform(rBlocks.begin(), rBlocks.end(), std::back_inserter(m_aAddressBlocks), + [this, bConvertFromConfig](const OUString& rBlock) -> OUString { + OUString sBlock = rBlock; + if(bConvertFromConfig) + lcl_ConvertFromNumbers(sBlock, m_AddressHeaderSA); + return sBlock; + }); + m_nCurrentAddressBlock = 0; + SetModified(); +} + +Sequence< OUString> SwMailMergeConfigItem_Impl::GetGreetings( + SwMailMergeConfigItem::Gender eType, bool bConvertToConfig) const +{ + const std::vector< OUString>& rGreetings = + eType == SwMailMergeConfigItem::FEMALE ? m_aFemaleGreetingLines : + eType == SwMailMergeConfigItem::MALE ? m_aMaleGreetingLines : + m_aNeutralGreetingLines; + Sequence< OUString> aRet(rGreetings.size()); + std::transform(rGreetings.begin(), rGreetings.end(), aRet.begin(), + [this, bConvertToConfig](const OUString& rGreeting) -> OUString { + OUString sGreeting = rGreeting; + if(bConvertToConfig) + lcl_ConvertToNumbers(sGreeting, m_AddressHeaderSA); + return sGreeting; + }); + return aRet; +} + +void SwMailMergeConfigItem_Impl::SetGreetings( + SwMailMergeConfigItem::Gender eType, + const Sequence< OUString>& rSetGreetings, + bool bConvertFromConfig) +{ + std::vector< OUString>& rGreetings = + eType == SwMailMergeConfigItem::FEMALE ? m_aFemaleGreetingLines : + eType == SwMailMergeConfigItem::MALE ? m_aMaleGreetingLines : + m_aNeutralGreetingLines; + + rGreetings.clear(); + std::transform(rSetGreetings.begin(), rSetGreetings.end(), std::back_inserter(rGreetings), + [this, bConvertFromConfig](const OUString& rGreeting) -> OUString { + OUString sGreeting = rGreeting; + if(bConvertFromConfig) + lcl_ConvertFromNumbers(sGreeting, m_AddressHeaderSA); + return sGreeting; + }); + SetModified(); +} + +sal_Int32 SwMailMergeConfigItem_Impl::GetCurrentGreeting( + SwMailMergeConfigItem::Gender eType) const +{ + sal_Int32 nRet; + switch(eType) + { + case SwMailMergeConfigItem::FEMALE: nRet = m_nCurrentFemaleGreeting ; break; + case SwMailMergeConfigItem::MALE: nRet = m_nCurrentMaleGreeting ; break; + default: nRet = m_nCurrentNeutralGreeting; break; + } + return nRet; +} + +void SwMailMergeConfigItem_Impl::SetCurrentGreeting( + SwMailMergeConfigItem::Gender eType, sal_Int32 nIndex) +{ + bool bChanged = false; + switch(eType) + { + case SwMailMergeConfigItem::FEMALE: + bChanged = m_nCurrentFemaleGreeting != nIndex; + m_nCurrentFemaleGreeting = nIndex; + break; + case SwMailMergeConfigItem::MALE: + bChanged = m_nCurrentMaleGreeting != nIndex; + m_nCurrentMaleGreeting = nIndex; + break; + default: + bChanged = m_nCurrentNeutralGreeting != nIndex; + m_nCurrentNeutralGreeting = nIndex; + } + if(bChanged) + SetModified(); +} + +SwMailMergeConfigItem::SwMailMergeConfigItem() : + m_pImpl(new SwMailMergeConfigItem_Impl), + m_bAddressInserted(false), + m_bGreetingInserted(false), + m_nGreetingMoves(0), + m_pSourceView(nullptr), + m_pTargetView(nullptr) +{ +} + +void SwMailMergeConfigItem::stopDBChangeListening() +{ + if (m_xDBChangedListener.is()) + { + uno::Reference<view::XSelectionSupplier> xSupplier = m_pSourceView->GetUNOObject(); + xSupplier->removeSelectionChangeListener(m_xDBChangedListener); + m_xDBChangedListener.clear(); + } +} + +void SwMailMergeConfigItem::updateCurrentDBDataFromDocument() +{ + const SwDBData& rDBData = m_pSourceView->GetWrtShell().GetDBDesc(); + SetCurrentDBData(rDBData); +} + +SwMailMergeConfigItem::~SwMailMergeConfigItem() +{ + stopDBChangeListening(); +} + +void SwMailMergeConfigItem::Commit() +{ + if(m_pImpl->IsModified()) + m_pImpl->Commit(); +} + +const std::vector<std::pair<OUString, int>>& SwMailMergeConfigItem::GetDefaultAddressHeaders() const +{ + return m_pImpl->m_AddressHeaderSA; +} + +void SwMailMergeConfigItem::SetAddressBlocks( + const Sequence< OUString>& rBlocks) +{ + m_pImpl->SetAddressBlocks(rBlocks); +} + +Sequence< OUString> SwMailMergeConfigItem::GetAddressBlocks() const +{ + return m_pImpl->GetAddressBlocks(); +} + +bool SwMailMergeConfigItem::IsAddressBlock()const +{ + return m_pImpl->m_bIsAddressBlock && IsOutputToLetter(); +} + +void SwMailMergeConfigItem::SetAddressBlock(bool bSet) +{ + m_pImpl->m_bUserSettingWereOverwritten = false; + if(m_pImpl->m_bIsAddressBlock != bSet) + { + m_pImpl->m_bIsAddressBlock = bSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsHideEmptyParagraphs() const +{ + return m_pImpl->m_bIsHideEmptyParagraphs; +} + +void SwMailMergeConfigItem::SetHideEmptyParagraphs(bool bSet) +{ + if(m_pImpl->m_bIsHideEmptyParagraphs != bSet) + { + m_pImpl->m_bIsHideEmptyParagraphs = bSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsIncludeCountry() const +{ + return m_pImpl->m_bIncludeCountry; +} + +OUString& SwMailMergeConfigItem::GetExcludeCountry() const +{ + return m_pImpl->m_sExcludeCountry; +} + +void SwMailMergeConfigItem::SetCountrySettings(bool bSet, const OUString& rCountry) +{ + if(m_pImpl->m_sExcludeCountry != rCountry || + m_pImpl->m_bIncludeCountry != bSet) + { + m_pImpl->m_bIncludeCountry = bSet; + m_pImpl->m_sExcludeCountry = bSet ? rCountry : OUString(); + m_pImpl->SetModified(); + } +} + +void SwMailMergeConfigItem::SetCurrentConnection( + Reference< XDataSource> const & xSource, + const SharedConnection& rConnection, + Reference< XColumnsSupplier> const & xColumnsSupplier, + const SwDBData& rDBData) +{ + m_pImpl->m_xSource = xSource ; + m_pImpl->m_xConnection = rConnection ; + m_pImpl->m_xColumnsSupplier = xColumnsSupplier; + m_pImpl->m_aDBData = rDBData; + m_pImpl->m_xResultSet = nullptr; + m_pImpl->m_nResultSetCursorPos = 0; + m_pImpl->SetModified(); +} + +Reference< XDataSource> const & SwMailMergeConfigItem::GetSource() const +{ + return m_pImpl->m_xSource; +} + +SharedConnection const & SwMailMergeConfigItem::GetConnection() const +{ + return m_pImpl->m_xConnection; +} + +Reference< XColumnsSupplier> const & SwMailMergeConfigItem::GetColumnsSupplier() +{ + if(!m_pImpl->m_xColumnsSupplier.is() && m_pImpl->m_xConnection.is()) + { + m_pImpl->m_xColumnsSupplier = SwDBManager::GetColumnSupplier(m_pImpl->m_xConnection, + m_pImpl->m_aDBData.sCommand, + m_pImpl->m_aDBData.nCommandType == CommandType::TABLE ? + SwDBSelect::TABLE : SwDBSelect::QUERY ); + } + return m_pImpl->m_xColumnsSupplier; +} + +const SwDBData& SwMailMergeConfigItem::GetCurrentDBData() const +{ + return m_pImpl->m_aDBData; +} + +void SwMailMergeConfigItem::SetCurrentDBData( const SwDBData& rDBData) +{ + if(m_pImpl->m_aDBData != rDBData) + { + m_pImpl->m_aDBData = rDBData; + m_pImpl->m_xConnection.clear(); + m_pImpl->m_xSource = nullptr; + m_pImpl->m_xResultSet = nullptr; + m_pImpl->m_xColumnsSupplier = nullptr; + m_pImpl->SetModified(); + } +} + +Reference< XResultSet> const & SwMailMergeConfigItem::GetResultSet() const +{ + if(!m_pImpl->m_xConnection.is() && !m_pImpl->m_aDBData.sDataSource.isEmpty()) + { + m_pImpl->m_xConnection.reset( + SwDBManager::GetConnection(m_pImpl->m_aDBData.sDataSource, m_pImpl->m_xSource, m_pSourceView), + SharedConnection::TakeOwnership + ); + } + if(!m_pImpl->m_xResultSet.is() && m_pImpl->m_xConnection.is()) + { + try + { + Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); + + Reference<XRowSet> xRowSet( xMgr->createInstance("com.sun.star.sdb.RowSet"), UNO_QUERY ); + Reference<XPropertySet> xRowProperties(xRowSet, UNO_QUERY); + xRowProperties->setPropertyValue("DataSourceName", makeAny(m_pImpl->m_aDBData.sDataSource)); + xRowProperties->setPropertyValue("Command", makeAny(m_pImpl->m_aDBData.sCommand)); + xRowProperties->setPropertyValue("CommandType", makeAny(m_pImpl->m_aDBData.nCommandType)); + xRowProperties->setPropertyValue("FetchSize", makeAny(sal_Int32(10))); + xRowProperties->setPropertyValue("ActiveConnection", makeAny(m_pImpl->m_xConnection.getTyped())); + try + { + xRowProperties->setPropertyValue("ApplyFilter", makeAny(!m_pImpl->m_sFilter.isEmpty())); + xRowProperties->setPropertyValue("Filter", makeAny(m_pImpl->m_sFilter)); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sw.ui", ""); + } + xRowSet->execute(); + m_pImpl->m_xResultSet = xRowSet.get(); + m_pImpl->m_xResultSet->first(); + m_pImpl->m_nResultSetCursorPos = 1; + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sw.ui", "SwMailMergeConfigItem::GetResultSet()"); + } + } + return m_pImpl->m_xResultSet; +} + +void SwMailMergeConfigItem::DisposeResultSet() +{ + m_pImpl->m_xConnection.clear(); + if(m_pImpl->m_xResultSet.is()) + { + ::comphelper::disposeComponent( m_pImpl->m_xResultSet ); + } +} + +OUString& SwMailMergeConfigItem::GetFilter() const +{ + return m_pImpl->m_sFilter; +} + +void SwMailMergeConfigItem::SetFilter(OUString const & rFilter) +{ + if(m_pImpl->m_sFilter != rFilter) + { + m_pImpl->m_sFilter = rFilter; + m_pImpl->SetModified(); + Reference<XPropertySet> xRowProperties(m_pImpl->m_xResultSet, UNO_QUERY); + if(xRowProperties.is()) + { + try + { + xRowProperties->setPropertyValue("ApplyFilter", makeAny(!m_pImpl->m_sFilter.isEmpty())); + xRowProperties->setPropertyValue("Filter", makeAny(m_pImpl->m_sFilter)); + uno::Reference<XRowSet> xRowSet( m_pImpl->m_xResultSet, UNO_QUERY_THROW ); + xRowSet->execute(); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sw.ui", "SwMailMergeConfigItem::SetFilter()"); + } + } + } +} + +sal_Int32 SwMailMergeConfigItem::MoveResultSet(sal_Int32 nTarget) +{ + if(!m_pImpl->m_xResultSet.is()) + GetResultSet(); + if(m_pImpl->m_xResultSet.is()) + { + try + { + //no action if the resultset is already at the right position + if(m_pImpl->m_xResultSet->getRow() != nTarget) + { + if(nTarget > 0) + { + bool bMoved = m_pImpl->m_xResultSet->absolute(nTarget); + if(!bMoved) + { + if(nTarget > 1) + m_pImpl->m_xResultSet->last(); + else if(nTarget == 1) + m_pImpl->m_xResultSet->first(); + } + } + else if(nTarget == -1) + m_pImpl->m_xResultSet->last(); + m_pImpl->m_nResultSetCursorPos = m_pImpl->m_xResultSet->getRow(); + } + } + catch (const Exception&) + { + } + } + return m_pImpl->m_nResultSetCursorPos; +} + +bool SwMailMergeConfigItem::IsResultSetFirstLast(bool& bIsFirst, bool& bIsLast) +{ + bool bRet = false; + if(!m_pImpl->m_xResultSet.is()) + GetResultSet(); + if(m_pImpl->m_xResultSet.is()) + { + try + { + bIsFirst = m_pImpl->m_xResultSet->isFirst(); + bIsLast = m_pImpl->m_xResultSet->isLast(); + bRet = true; + } + catch (const Exception&) + { + } + } + return bRet; +} + +sal_Int32 SwMailMergeConfigItem::GetResultSetPosition() const +{ + return m_pImpl->m_nResultSetCursorPos; +} + +bool SwMailMergeConfigItem::IsRecordExcluded(sal_Int32 nRecord) const + { return m_aExcludedRecords.find(nRecord) != m_aExcludedRecords.end(); } + +void SwMailMergeConfigItem::ExcludeRecord(sal_Int32 nRecord, bool bExclude) +{ + if(bExclude) + m_aExcludedRecords.insert(nRecord); + else + m_aExcludedRecords.erase(nRecord); +} + +uno::Sequence<uno::Any> SwMailMergeConfigItem::GetSelection() const +{ + if(!m_pImpl->m_xResultSet.is()) + GetResultSet(); + if(!m_pImpl->m_xResultSet.is()) + return {}; + m_pImpl->m_xResultSet->last(); + sal_Int32 nResultSetSize = m_pImpl->m_xResultSet->getRow()+1; + std::vector<uno::Any> vResult; + vResult.reserve(nResultSetSize); + for(sal_Int32 nIdx=1; nIdx<nResultSetSize;++nIdx) + if(!IsRecordExcluded(nIdx)) + vResult.push_back(uno::makeAny<sal_Int32>(nIdx)); + return comphelper::containerToSequence(vResult); +} + + +const uno::Sequence< OUString>& + SwMailMergeConfigItem::GetSavedDocuments() const +{ + return m_pImpl->m_aSavedDocuments; +} + +bool SwMailMergeConfigItem::IsOutputToLetter()const +{ + return m_pImpl->m_bIsOutputToLetter || !IsMailAvailable(); +} + +void SwMailMergeConfigItem::SetOutputToLetter(bool bSet) +{ + if(m_pImpl->m_bIsOutputToLetter != bSet) + { + m_pImpl->m_bIsOutputToLetter = bSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsIndividualGreeting(bool bInEMail) const +{ + return bInEMail ? + m_pImpl->m_bIsIndividualGreetingLineInMail : + m_pImpl->m_bIsIndividualGreetingLine; +} + +void SwMailMergeConfigItem::SetIndividualGreeting( + bool bSet, bool bInEMail) +{ + if(bInEMail) + { + if(m_pImpl->m_bIsIndividualGreetingLineInMail != bSet) + { + m_pImpl->m_bIsIndividualGreetingLineInMail = bSet; + m_pImpl->SetModified(); + } + } + else + { + if(m_pImpl->m_bIsIndividualGreetingLine != bSet) + { + m_pImpl->m_bIsIndividualGreetingLine = bSet; + m_pImpl->SetModified(); + } + } +} + +bool SwMailMergeConfigItem::IsGreetingLine(bool bInEMail) const +{ + return bInEMail ? m_pImpl->m_bIsGreetingLineInMail : m_pImpl->m_bIsGreetingLine; +} + +void SwMailMergeConfigItem::SetGreetingLine(bool bSet, bool bInEMail) +{ + m_pImpl->m_bUserSettingWereOverwritten = false; + if(bInEMail) + { + if(m_pImpl->m_bIsGreetingLineInMail != bSet) + { + m_pImpl->m_bIsGreetingLineInMail = bSet; + m_pImpl->SetModified(); + } + } + else + { + if(m_pImpl->m_bIsGreetingLine != bSet) + { + m_pImpl->m_bIsGreetingLine = bSet; + m_pImpl->SetModified(); + } + } +} + +Sequence< OUString> SwMailMergeConfigItem::GetGreetings( + Gender eType ) const +{ + return m_pImpl->GetGreetings(eType); +} + +void SwMailMergeConfigItem::SetGreetings( + Gender eType, const Sequence< OUString>& rSetGreetings) +{ + m_pImpl->SetGreetings( eType, rSetGreetings); +} + +sal_Int32 SwMailMergeConfigItem::GetCurrentGreeting( + SwMailMergeConfigItem::Gender eType) const +{ + return m_pImpl->GetCurrentGreeting(eType); +} + +void SwMailMergeConfigItem::SetCurrentGreeting(Gender eType, sal_Int32 nIndex) +{ + m_pImpl->SetCurrentGreeting(eType, nIndex); +} + +const OUString& SwMailMergeConfigItem::GetFemaleGenderValue() const +{ + return m_pImpl->m_sFemaleGenderValue; +} + +void SwMailMergeConfigItem::SetFemaleGenderValue(const OUString& rValue) +{ + if( m_pImpl->m_sFemaleGenderValue != rValue ) + { + m_pImpl->m_sFemaleGenderValue = rValue; + m_pImpl->SetModified(); + } +} + +Sequence< OUString> SwMailMergeConfigItem::GetColumnAssignment( + const SwDBData& rDBData ) const +{ + Sequence< OUString> aRet; + auto aAssignIter = std::find_if(m_pImpl->m_aAddressDataAssignments.begin(), m_pImpl->m_aAddressDataAssignments.end(), + [&rDBData](const DBAddressDataAssignment& rAssignment) { return rAssignment.aDBData == rDBData; }); + if (aAssignIter != m_pImpl->m_aAddressDataAssignments.end()) + { + aRet = aAssignIter->aDBColumnAssignments; + } + return aRet; +} + +// returns the name that is assigned as e-mail column of the current data base +OUString SwMailMergeConfigItem::GetAssignedColumn(sal_uInt32 nColumn) const +{ + OUString sRet; + Sequence< OUString> aAssignment = GetColumnAssignment( m_pImpl->m_aDBData ); + if(aAssignment.getLength() > sal::static_int_cast< sal_Int32, sal_uInt32>(nColumn) && !aAssignment[nColumn].isEmpty()) + sRet = aAssignment[nColumn]; + else if(nColumn < m_pImpl->m_AddressHeaderSA.size()) + sRet = m_pImpl->m_AddressHeaderSA[nColumn].first; + return sRet; +} + +void SwMailMergeConfigItem::SetColumnAssignment( const SwDBData& rDBData, + const Sequence< OUString>& rList) +{ + auto aAssignIter = std::find_if(m_pImpl->m_aAddressDataAssignments.begin(), m_pImpl->m_aAddressDataAssignments.end(), + [&rDBData](const DBAddressDataAssignment& rAssignment) { return rAssignment.aDBData == rDBData; }); + if (aAssignIter != m_pImpl->m_aAddressDataAssignments.end()) + { + if(aAssignIter->aDBColumnAssignments != rList) + { + aAssignIter->aDBColumnAssignments = rList; + aAssignIter->bColumnAssignmentsChanged = true; + } + } + else + { + DBAddressDataAssignment aAssignment; + aAssignment.aDBData = rDBData; + aAssignment.aDBColumnAssignments = rList; + aAssignment.bColumnAssignmentsChanged = true; + m_pImpl->m_aAddressDataAssignments.push_back(aAssignment); + } + m_pImpl->SetModified(); +} + +bool SwMailMergeConfigItem::IsAddressFieldsAssigned() const +{ + bool bResult = true; + Reference< XResultSet> xResultSet = GetResultSet(); + uno::Reference< XColumnsSupplier > xColsSupp( xResultSet, UNO_QUERY ); + if(!xColsSupp.is()) + return false; + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + + const std::vector<std::pair<OUString, int>>& rHeaders = GetDefaultAddressHeaders(); + Sequence< OUString> aAssignment = + GetColumnAssignment( GetCurrentDBData() ); + const OUString* pAssignment = aAssignment.getConstArray(); + const Sequence< OUString> aBlocks = GetAddressBlocks(); + + if(aBlocks.getLength() <= m_pImpl->GetCurrentAddressBlockIndex()) + return false; + SwAddressIterator aIter(aBlocks[m_pImpl->GetCurrentAddressBlockIndex()]); + while(aIter.HasMore()) + { + SwMergeAddressItem aItem = aIter.Next(); + if(aItem.bIsColumn) + { + OUString sConvertedColumn = aItem.sText; + auto nSize = std::min(sal_uInt32(rHeaders.size()), sal_uInt32(aAssignment.getLength())); + for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn) + { + if (rHeaders[nColumn].first == aItem.sText && + !pAssignment[nColumn].isEmpty()) + { + sConvertedColumn = pAssignment[nColumn]; + break; + } + } + //find out if the column exists in the data base + if(!xCols->hasByName(sConvertedColumn)) + { + bResult = false; + break; + } + } + } + return bResult; +} + +bool SwMailMergeConfigItem::IsGreetingFieldsAssigned() const +{ + bool bResult = true; + + if(!IsIndividualGreeting(false)) + return true; + + Reference< XResultSet> xResultSet = GetResultSet(); + uno::Reference< XColumnsSupplier > xColsSupp( xResultSet, UNO_QUERY ); + if(!xColsSupp.is()) + return false; + const std::vector<std::pair<OUString, int>>& rHeaders = GetDefaultAddressHeaders(); + uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns(); + + Sequence< OUString> aAssignment = + GetColumnAssignment( GetCurrentDBData() ); + const OUString* pAssignment = aAssignment.getConstArray(); + + const Sequence< OUString> rFemaleEntries = GetGreetings(SwMailMergeConfigItem::FEMALE); + sal_Int32 nCurrentFemale = GetCurrentGreeting(SwMailMergeConfigItem::FEMALE); + const Sequence< OUString> rMaleEntries = GetGreetings(SwMailMergeConfigItem::MALE); + sal_Int32 nCurrentMale = GetCurrentGreeting(SwMailMergeConfigItem::MALE); + OUString sMale, sFemale; + if(rFemaleEntries.getLength() > nCurrentFemale) + sFemale = rFemaleEntries[nCurrentFemale]; + if(rMaleEntries.getLength() > nCurrentMale) + sMale = rMaleEntries[nCurrentMale]; + + OUString sAddress = sFemale + sMale; + SwAddressIterator aIter(sAddress); + while(aIter.HasMore()) + { + SwMergeAddressItem aItem = aIter.Next(); + if(aItem.bIsColumn) + { + OUString sConvertedColumn = aItem.sText; + auto nSize = std::min(sal_uInt32(rHeaders.size()), sal_uInt32(aAssignment.getLength())); + for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn) + { + if (rHeaders[nColumn].first == aItem.sText && + !pAssignment[nColumn].isEmpty()) + { + sConvertedColumn = pAssignment[nColumn]; + break; + } + } + //find out if the column exists in the data base + if(!xCols->hasByName(sConvertedColumn)) + { + bResult = false; + break; + } + } + } + return bResult; +} + +OUString const & SwMailMergeConfigItem::GetMailDisplayName() const +{ + return m_pImpl->m_sMailDisplayName; +} + +void SwMailMergeConfigItem::SetMailDisplayName(const OUString& rName) +{ + if(m_pImpl->m_sMailDisplayName != rName) + { + m_pImpl->m_sMailDisplayName = rName; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetMailAddress() const +{ + return m_pImpl->m_sMailAddress; +} + +void SwMailMergeConfigItem::SetMailAddress(const OUString& rAddress) +{ + if(m_pImpl->m_sMailAddress != rAddress ) + { + m_pImpl->m_sMailAddress = rAddress; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsMailReplyTo() const +{ + return m_pImpl->m_bIsMailReplyTo; +} + +void SwMailMergeConfigItem::SetMailReplyTo(bool bSet) +{ + if(m_pImpl->m_bIsMailReplyTo != bSet) + { + m_pImpl->m_bIsMailReplyTo = bSet; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetMailReplyTo() const +{ + return m_pImpl->m_sMailReplyTo; +} + +void SwMailMergeConfigItem::SetMailReplyTo(const OUString& rReplyTo) +{ + if(m_pImpl->m_sMailReplyTo != rReplyTo) + { + m_pImpl->m_sMailReplyTo = rReplyTo; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetMailServer() const +{ + return m_pImpl->m_sMailServer; +} + +void SwMailMergeConfigItem::SetMailServer(const OUString& rAddress) +{ + if(m_pImpl->m_sMailServer != rAddress) + { + m_pImpl->m_sMailServer = rAddress; + m_pImpl->SetModified(); + } +} + +sal_Int16 SwMailMergeConfigItem::GetMailPort() const +{ + // provide appropriate TCP port, based on SSL/STARTTLS status, if current port is one of the defaults + switch (m_pImpl->m_nMailPort) + { + case SECURE_PORT: + case DEFAULT_PORT: + return m_pImpl->m_bIsSecureConnection ? SECURE_PORT : DEFAULT_PORT; + break; + default: + return m_pImpl->m_nMailPort; + } +} + +void SwMailMergeConfigItem::SetMailPort(sal_Int16 nSet) +{ + if(m_pImpl->m_nMailPort != nSet) + { + m_pImpl->m_nMailPort = nSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsSecureConnection() const +{ + return m_pImpl->m_bIsSecureConnection; +} + +void SwMailMergeConfigItem::SetSecureConnection(bool bSet) +{ + if(m_pImpl->m_bIsSecureConnection != bSet) + { + m_pImpl->m_bIsSecureConnection = bSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsAuthentication() const +{ + return m_pImpl->m_bIsAuthentication; +} + +void SwMailMergeConfigItem::SetAuthentication(bool bSet) +{ + if(m_pImpl->m_bIsAuthentication != bSet) + { + m_pImpl->m_bIsAuthentication = bSet; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetMailUserName() const +{ + return m_pImpl->m_sMailUserName; +} + +void SwMailMergeConfigItem::SetMailUserName(const OUString& rName) +{ + if(m_pImpl->m_sMailUserName != rName) + { + m_pImpl->m_sMailUserName = rName; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetMailPassword() const +{ + return m_pImpl->m_sMailPassword; +} + +void SwMailMergeConfigItem::SetMailPassword(const OUString& rPassword) +{ + if(m_pImpl->m_sMailPassword != rPassword) + { + m_pImpl->m_sMailPassword = rPassword; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsSMTPAfterPOP() const +{ + return m_pImpl->m_bIsSMPTAfterPOP; +} + +void SwMailMergeConfigItem::SetSMTPAfterPOP(bool bSet) +{ + if( m_pImpl->m_bIsSMPTAfterPOP != bSet) + { + m_pImpl->m_bIsSMPTAfterPOP = bSet; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetInServerName() const +{ + return m_pImpl->m_sInServerName; +} + +void SwMailMergeConfigItem::SetInServerName(const OUString& rServer) +{ + if(m_pImpl->m_sInServerName != rServer) + { + m_pImpl->m_sInServerName = rServer; + m_pImpl->SetModified(); + } +} + +sal_Int16 SwMailMergeConfigItem::GetInServerPort() const +{ + // provide appropriate TCP port as user toggles between POP/IMAP if current port is one of the defaults + switch (m_pImpl->m_nInServerPort) + { + case POP_SECURE_PORT: + case POP_PORT: + case IMAP_SECURE_PORT: + case IMAP_PORT: + if ( m_pImpl->m_bInServerPOP ) + return m_pImpl->m_bIsSecureConnection ? POP_SECURE_PORT : POP_PORT; + else + return m_pImpl->m_bIsSecureConnection ? IMAP_SECURE_PORT : IMAP_PORT; + break; + default: + return m_pImpl->m_nInServerPort; + } +} + +void SwMailMergeConfigItem::SetInServerPort(sal_Int16 nSet) +{ + if( m_pImpl->m_nInServerPort != nSet) + { + m_pImpl->m_nInServerPort = nSet; + m_pImpl->SetModified(); + } +} + +bool SwMailMergeConfigItem::IsInServerPOP() const +{ + return m_pImpl->m_bInServerPOP; +} + +void SwMailMergeConfigItem::SetInServerPOP(bool bSet) +{ + if( m_pImpl->m_bInServerPOP != bSet) + { + m_pImpl->m_bInServerPOP = bSet; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetInServerUserName() const +{ + return m_pImpl->m_sInServerUserName; +} + +void SwMailMergeConfigItem::SetInServerUserName(const OUString& rName) +{ + if( m_pImpl->m_sInServerUserName != rName) + { + m_pImpl->m_sInServerUserName = rName; + m_pImpl->SetModified(); + } +} + +OUString const & SwMailMergeConfigItem::GetInServerPassword() const +{ + return m_pImpl->m_sInServerPassword; +} + +void SwMailMergeConfigItem::SetInServerPassword(const OUString& rPassword) +{ + if(m_pImpl->m_sInServerPassword != rPassword) + { + m_pImpl->m_sInServerPassword = rPassword; + m_pImpl->SetModified(); + } +} + +void SwMailMergeConfigItem::DocumentReloaded() +{ + m_bGreetingInserted = false; + m_bAddressInserted = false; +} + +bool SwMailMergeConfigItem::IsMailAvailable() const +{ + return m_pImpl->m_bIsEMailSupported; +} + +void SwMailMergeConfigItem::AddMergedDocument(SwDocMergeInfo const & rInfo) +{ + m_pImpl->m_aMergeInfos.push_back(rInfo); +} + +SwDocMergeInfo& SwMailMergeConfigItem::GetDocumentMergeInfo(sal_uInt32 nDocument) +{ + assert(nDocument < m_pImpl->m_aMergeInfos.size()); + return m_pImpl->m_aMergeInfos[nDocument]; +} + + +sal_uInt32 SwMailMergeConfigItem::GetMergedDocumentCount() +{ + if(m_pTargetView) + return m_pImpl->m_aMergeInfos.size(); + else + { + sal_Int32 nRestore = GetResultSetPosition(); + MoveResultSet(-1); + sal_Int32 nRet = GetResultSetPosition(); + MoveResultSet( nRestore ); + nRet -= m_aExcludedRecords.size(); + return nRet >= 0 ? nRet : 0; + } +} + +static SwView* lcl_ExistsView(SwView* pView) +{ + SfxViewShell* pViewShell = SfxViewShell::GetFirst( false, checkSfxViewShell<SwView> ); + while(pViewShell) + { + if(pViewShell == pView) + return pView; + + pViewShell = SfxViewShell::GetNext( *pViewShell, false, checkSfxViewShell<SwView> ); + } + return nullptr; +} + +SwView* SwMailMergeConfigItem::GetTargetView() +{ + //make sure that the pointer is really valid - the document may have been closed manually + if(m_pTargetView) + { + m_pTargetView = lcl_ExistsView(m_pTargetView); + } + return m_pTargetView; +} + +void SwMailMergeConfigItem::SetTargetView(SwView* pView) +{ + m_pTargetView = pView; + //reset the document merge counter + if(!m_pTargetView) + { + m_pImpl->m_aMergeInfos.clear(); + } +} + +SwView* SwMailMergeConfigItem::GetSourceView() +{ + m_pSourceView = lcl_ExistsView(m_pSourceView); + return m_pSourceView; +} + +namespace { + +//This implements XSelectionChangeListener and XDispatch because the +//broadcaster uses this combo to determine if to send the database-changed +//update. Its probably that listening to statusChanged at some other level is +//equivalent to this. See the other call to SwXDispatch::GetDBChangeURL for +//the broadcaster of the event. +class DBChangeListener : public cppu::WeakImplHelper<css::view::XSelectionChangeListener, css::frame::XDispatch> +{ + SwMailMergeConfigItem& m_rParent; +public: + explicit DBChangeListener(SwMailMergeConfigItem& rParent) + : m_rParent(rParent) + { + } + + virtual void SAL_CALL selectionChanged(const EventObject& /*rEvent*/) override + { + } + + virtual void SAL_CALL disposing(const EventObject&) override + { + m_rParent.stopDBChangeListening(); + } + + virtual void SAL_CALL dispatch(const css::util::URL& rURL, const css::uno::Sequence< css::beans::PropertyValue >& /*rArgs*/) override + { + if (rURL.Complete.equalsAscii(SwXDispatch::GetDBChangeURL())) + m_rParent.updateCurrentDBDataFromDocument(); + } + + virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener >&, const css::util::URL&) override + { + } + + virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >&, const css::util::URL&) override + { + } +}; + +} + +void SwMailMergeConfigItem::SetSourceView(SwView* pView) +{ + if (m_xDBChangedListener.is()) + { + uno::Reference<view::XSelectionSupplier> xSupplier = m_pSourceView->GetUNOObject(); + xSupplier->removeSelectionChangeListener(m_xDBChangedListener); + m_xDBChangedListener.clear(); + } + + m_pSourceView = pView; + + if (!m_pSourceView) + return; + + std::vector<OUString> aDBNameList; + std::vector<OUString> aAllDBNames; + m_pSourceView->GetWrtShell().GetAllUsedDB( aDBNameList, &aAllDBNames ); + if(!aDBNameList.empty()) + { + // if fields are available there is usually no need of an addressblock and greeting + if(!m_pImpl->m_bUserSettingWereOverwritten) + { + if( m_pImpl->m_bIsAddressBlock + || m_pImpl->m_bIsGreetingLineInMail + || m_pImpl->m_bIsGreetingLine ) + { + //store user settings + m_pImpl->m_bUserSettingWereOverwritten = true; + m_pImpl->m_bIsAddressBlock_LastUserSetting = m_pImpl->m_bIsAddressBlock; + m_pImpl->m_bIsGreetingLineInMail_LastUserSetting = m_pImpl->m_bIsGreetingLineInMail; + m_pImpl->m_bIsGreetingLine_LastUserSetting = m_pImpl->m_bIsGreetingLine; + + //set all to false + m_pImpl->m_bIsAddressBlock = false; + m_pImpl->m_bIsGreetingLineInMail = false; + m_pImpl->m_bIsGreetingLine = false; + + m_pImpl->SetModified(); + } + } + } + else if( m_pImpl->m_bUserSettingWereOverwritten ) + { + //restore last user settings: + m_pImpl->m_bIsAddressBlock = m_pImpl->m_bIsAddressBlock_LastUserSetting; + m_pImpl->m_bIsGreetingLineInMail = m_pImpl->m_bIsGreetingLineInMail_LastUserSetting; + m_pImpl->m_bIsGreetingLine = m_pImpl->m_bIsGreetingLine_LastUserSetting; + + m_pImpl->m_bUserSettingWereOverwritten = false; + } + + if (!m_xDBChangedListener.is()) + { + m_xDBChangedListener.set(new DBChangeListener(*this)); + } + + uno::Reference<view::XSelectionSupplier> xSupplier = m_pSourceView->GetUNOObject(); + xSupplier->addSelectionChangeListener(m_xDBChangedListener); +} + +void SwMailMergeConfigItem::SetCurrentAddressBlockIndex( sal_Int32 nSet ) +{ + m_pImpl->SetCurrentAddressBlockIndex( nSet ); +} + +sal_Int32 SwMailMergeConfigItem::GetCurrentAddressBlockIndex() const +{ + return m_pImpl->GetCurrentAddressBlockIndex(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dialog/SwSpellDialogChildWindow.cxx b/sw/source/uibase/dialog/SwSpellDialogChildWindow.cxx new file mode 100644 index 000000000..cd19c9a39 --- /dev/null +++ b/sw/source/uibase/dialog/SwSpellDialogChildWindow.cxx @@ -0,0 +1,827 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <SwSpellDialogChildWindow.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/acorrcfg.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <wrtsh.hxx> +#include <sfx2/printer.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdview.hxx> +#include <unotools/linguprops.hxx> +#include <unotools/lingucfg.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <docsh.hxx> +#include <drawdoc.hxx> +#include <dcontact.hxx> +#include <edtwin.hxx> +#include <pam.hxx> +#include <drawbase.hxx> +#include <unotextrange.hxx> +#include <strings.hrc> +#include <cmdid.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::beans; + +SFX_IMPL_CHILDWINDOW_WITHID(SwSpellDialogChildWindow, FN_SPELL_GRAMMAR_DIALOG) + +struct SpellState +{ + bool m_bInitialCall; + bool m_bLockFocus; // lock the focus notification while a modal dialog is active + bool m_bLostFocus; + + // restart and progress information + bool m_bBodySpelled; // body already spelled + bool m_bOtherSpelled; // frames, footnotes, headers and footers spelled + bool m_bStartedInOther; // started the spelling inside of the _other_ area + bool m_bStartedInSelection; // there was an initial text selection + std::unique_ptr<SwPaM> + pOtherCursor; // position where the spelling inside the _other_ area started + bool m_bDrawingsSpelled; // all drawings spelled + Reference<XTextRange> m_xStartRange; // text range that marks the start of spelling + const SdrObject* m_pStartDrawing; // draw text object spelling started in + ESelection m_aStartDrawingSelection; // draw text start selection + bool m_bRestartDrawing; // the first selected drawing object is found again + + // lose/get focus information to decide if spelling can be continued + ShellMode m_eSelMode; + const SwNode* m_pPointNode; + const SwNode* m_pMarkNode; + sal_Int32 m_nPointPos; + sal_Int32 m_nMarkPos; + const SdrOutliner* m_pOutliner; + ESelection m_aESelection; + + // iterating over draw text objects + std::list<SdrTextObj*> m_aTextObjects; + bool m_bTextObjectsCollected; + + SpellState() : + m_bInitialCall(true), + m_bLockFocus(false), + m_bLostFocus(false), + m_bBodySpelled(false), + m_bOtherSpelled(false), + m_bStartedInOther(false), + m_bStartedInSelection(false), + m_bDrawingsSpelled(false), + m_pStartDrawing(nullptr), + m_bRestartDrawing(false), + + m_eSelMode(ShellMode::Object), // initially invalid + m_pPointNode(nullptr), + m_pMarkNode(nullptr), + m_nPointPos(0), + m_nMarkPos(0), + m_pOutliner(nullptr), + m_bTextObjectsCollected(false) + {} + + // reset state in ::InvalidateSpellDialog + void Reset() + { m_bInitialCall = true; + m_bBodySpelled = m_bOtherSpelled = m_bDrawingsSpelled = false; + m_xStartRange = nullptr; + m_pStartDrawing = nullptr; + m_bRestartDrawing = false; + m_bTextObjectsCollected = false; + m_aTextObjects.clear(); + m_bStartedInOther = false; + pOtherCursor.reset(); + } +}; + +static void lcl_LeaveDrawText(SwWrtShell& rSh) +{ + if(rSh.GetDrawView()) + { + rSh.GetDrawView()->SdrEndTextEdit( true ); + Point aPt(LONG_MIN, LONG_MIN); + // go out of the frame + rSh.SelectObj(aPt, SW_LEAVE_FRAME); + rSh.EnterStdMode(); + rSh.GetView().AttrChangedNotify(nullptr); + } +} + +SwSpellDialogChildWindow::SwSpellDialogChildWindow ( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* /*pInfo*/) + : svx::SpellDialogChildWindow ( + _pParent, nId, pBindings) + , m_bIsGrammarCheckingOn(false) + , m_pSpellState(new SpellState) +{ + SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_INTERACTIVE ) >>= m_bIsGrammarCheckingOn; +} + +SwSpellDialogChildWindow::~SwSpellDialogChildWindow () +{ + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(!m_pSpellState->m_bInitialCall && pWrtShell) + pWrtShell->SpellEnd(); + m_pSpellState.reset(); +} + +SfxChildWinInfo SwSpellDialogChildWindow::GetInfo() const +{ + SfxChildWinInfo aInfo = svx::SpellDialogChildWindow::GetInfo(); + aInfo.bVisible = false; + return aInfo; +} + +svx::SpellPortions SwSpellDialogChildWindow::GetNextWrongSentence(bool bRecheck) +{ + svx::SpellPortions aRet; + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(pWrtShell) + { + bool bNoDictionaryAvailable = pWrtShell->GetDoc()->IsDictionaryMissing(); + + if (!bRecheck) + { + // first set continuation point for spell/grammar check to the + // end of the current sentence + SwEditShell::MoveContinuationPosToEndOfCheckedSentence(); + } + + ShellMode eSelMode = pWrtShell->GetView().GetShellMode(); + bool bDrawText = ShellMode::DrawText == eSelMode; + bool bNormalText = + ShellMode::TableText == eSelMode || + ShellMode::ListText == eSelMode || + ShellMode::TableListText == eSelMode || + ShellMode::Text == eSelMode; + // Writer text outside of the body + bool bOtherText = false; + + if( m_pSpellState->m_bInitialCall ) + { + // if no text selection exists the cursor has to be set into the text + if(!bDrawText && !bNormalText) + { + MakeTextSelection_Impl(*pWrtShell, eSelMode); + // the selection type has to be checked again - both text types are possible + if(pWrtShell->GetSelectionType() & SelectionType::DrawObjectEditMode) + bDrawText = true; + bNormalText = !bDrawText; + } + if(bNormalText) + { + // set cursor to the start of the sentence + if(!pWrtShell->HasSelection()) + pWrtShell->GoStartSentence(); + else + { + pWrtShell->ExpandToSentenceBorders(); + m_pSpellState->m_bStartedInSelection = true; + } + // determine if the selection is outside of the body text + bOtherText = !(pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY); + if(bOtherText) + { + m_pSpellState->pOtherCursor.reset( new SwPaM(*pWrtShell->GetCursor()->GetPoint()) ); + m_pSpellState->m_bStartedInOther = true; + pWrtShell->SpellStart( SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::Curr ); + } + else + { + // mark the start position only if not at start of doc + if(!pWrtShell->IsStartOfDoc()) + { + // Record the position *before* the current cursor, as + // the word at the current cursor can possibly be + // replaced by a spellcheck correction which invalidates + // an XTextRange at this position. + SwDoc *pDoc = pWrtShell->GetDoc(); + auto pStart = pWrtShell->GetCursor()->Start(); + auto pUnoCursor = pDoc->CreateUnoCursor(*pStart); + pUnoCursor->Left( 1 ); + pStart = pUnoCursor->Start(); + m_pSpellState->m_xStartRange + = SwXTextRange::CreateXTextRange(*pDoc, *pStart, nullptr); + } + pWrtShell->SpellStart( SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Curr ); + } + } + else + { + SdrView* pSdrView = pWrtShell->GetDrawView(); + m_pSpellState->m_pStartDrawing = pSdrView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + // start checking at the top of the drawing object + pOLV->SetSelection( ESelection() ); + m_pSpellState->m_aStartDrawingSelection = ESelection(); +/* +Note: spelling in a selection only, or starting in a mid of a drawing object requires +further changes elsewhere. (Especially if it should work in sc and sd as well.) +The code below would only be part of the solution. +(Keeping it as a comment for the time being) + ESelection aCurSel( pOLV->GetSelection() ); + ESelection aSentenceSel( pOLV->GetEditView().GetEditEngine()->SelectSentence( aCurSel ) ); + if (!aCurSel.HasRange()) + { + aSentenceSel.nEndPara = aSentenceSel.nStartPara; + aSentenceSel.nEndPos = aSentenceSel.nStartPos; + } + pOLV->SetSelection( aSentenceSel ); + m_pSpellState->m_aStartDrawingSelection = aSentenceSel; +*/ + } + + m_pSpellState->m_bInitialCall = false; + } + if( bDrawText ) + { + // spell inside of the current draw text + if(!SpellDrawText_Impl(*pWrtShell, aRet)) + { + if(!FindNextDrawTextError_Impl(*pWrtShell) || !SpellDrawText_Impl(*pWrtShell, aRet)) + { + lcl_LeaveDrawText(*pWrtShell); + // now the drawings have been spelled + m_pSpellState->m_bDrawingsSpelled = true; + // the spelling continues at the other content + // if there's any that has not been spelled yet + if(!m_pSpellState->m_bOtherSpelled && pWrtShell->HasOtherCnt()) + { + pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::OtherStart ); + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + { + pWrtShell->SpellEnd(); + m_pSpellState->m_bOtherSpelled = true; + } + } + else + m_pSpellState->m_bOtherSpelled = true; + // if no result has been found try at the body text - completely + if(!m_pSpellState->m_bBodySpelled && aRet.empty()) + { + pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Start ); + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + { + m_pSpellState->m_bBodySpelled = true; + pWrtShell->SpellEnd(); + } + } + + } + } + } + else + { + // spell inside of the Writer text + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + { + // if there is a selection (within body or header/footer text) + // then spell/grammar checking should not move outside of it. + if (!m_pSpellState->m_bStartedInSelection) + { + // find out which text has been spelled body or other + bOtherText = !(pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY); + if(bOtherText && m_pSpellState->m_bStartedInOther && m_pSpellState->pOtherCursor) + { + m_pSpellState->m_bStartedInOther = false; + pWrtShell->SetSelection(*m_pSpellState->pOtherCursor); + pWrtShell->SpellEnd(); + m_pSpellState->pOtherCursor.reset(); + pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::Curr, SwDocPositions::OtherStart ); + (void)pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn); + } + if(aRet.empty()) + { + // end spelling + pWrtShell->SpellEnd(); + if(bOtherText) + { + m_pSpellState->m_bOtherSpelled = true; + // has the body been spelled? + if(!m_pSpellState->m_bBodySpelled) + { + pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Start ); + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + { + m_pSpellState->m_bBodySpelled = true; + pWrtShell->SpellEnd(); + } + } + } + else + { + m_pSpellState->m_bBodySpelled = true; + if(!m_pSpellState->m_bOtherSpelled && pWrtShell->HasOtherCnt()) + { + pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::OtherStart ); + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + { + pWrtShell->SpellEnd(); + m_pSpellState->m_bOtherSpelled = true; + } + } + else + m_pSpellState->m_bOtherSpelled = true; + } + } + + // search for a draw text object that contains error and spell it + if(aRet.empty() && + (m_pSpellState->m_bDrawingsSpelled || + !FindNextDrawTextError_Impl(*pWrtShell) || !SpellDrawText_Impl(*pWrtShell, aRet))) + { + lcl_LeaveDrawText(*pWrtShell); + m_pSpellState->m_bDrawingsSpelled = true; + } + } + } + } + // now only the rest of the body text can be spelled - + // if the spelling started inside of the body + bool bCloseMessage = true; + if(aRet.empty() && !m_pSpellState->m_bStartedInSelection) + { + OSL_ENSURE(m_pSpellState->m_bDrawingsSpelled && + m_pSpellState->m_bOtherSpelled && m_pSpellState->m_bBodySpelled, + "not all parts of the document are already spelled"); + if( m_pSpellState->m_xStartRange.is() && !bNoDictionaryAvailable ) + { + LockFocusNotification( true ); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetController()->getDialog(), + VclMessageType::Question, VclButtonsType::YesNo, SwResId(STR_QUERY_SPELL_CONTINUE))); + sal_uInt16 nRet = xBox->run(); + if (RET_YES == nRet) + { + SwUnoInternalPaM aPam(*pWrtShell->GetDoc()); + if (::sw::XTextRangeToSwPaM(aPam, + m_pSpellState->m_xStartRange)) + { + pWrtShell->SetSelection(aPam); + pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::Curr, SwDocPositions::Start); + if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn)) + pWrtShell->SpellEnd(); + } + m_pSpellState->m_xStartRange = nullptr; + LockFocusNotification( false ); + // take care that the now valid selection is stored + LoseFocus(); + } + else + bCloseMessage = false; // no closing message if a wrap around has been denied + } + } + if( aRet.empty() && bCloseMessage && !bNoDictionaryAvailable ) + { + LockFocusNotification( true ); + OUString sInfo( SwResId( bNoDictionaryAvailable ? STR_DICTIONARY_UNAVAILABLE : STR_SPELLING_COMPLETED ) ); + auto xSpellController = GetController(); + // #i84610# + std::unique_ptr<weld::MessageDialog> xBox( + Application::CreateMessageDialog( xSpellController->getDialog(), + VclMessageType::Info, + VclButtonsType::Ok, + sInfo ) ); + xBox->run(); + LockFocusNotification( false ); + // take care that the now valid selection is stored + LoseFocus(); + xSpellController->getDialog()->grab_focus(); + } + } + return aRet; +} + +void SwSpellDialogChildWindow::ApplyChangedSentence(const svx::SpellPortions& rChanged, bool bRecheck) +{ + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + OSL_ENSURE(!m_pSpellState->m_bInitialCall, "ApplyChangedSentence in initial call or after resume"); + if(pWrtShell && !m_pSpellState->m_bInitialCall) + { + ShellMode eSelMode = pWrtShell->GetView().GetShellMode(); + bool bDrawText = ShellMode::DrawText == eSelMode; + bool bNormalText = + ShellMode::TableText == eSelMode || + ShellMode::ListText == eSelMode || + ShellMode::TableListText == eSelMode || + ShellMode::Text == eSelMode; + + // evaluate if the same sentence should be rechecked or not. + // Sentences that got grammar checked should always be rechecked in order + // to detect possible errors that get introduced with the changes + bRecheck |= SwEditShell::HasLastSentenceGotGrammarChecked(); + + if(bNormalText) + pWrtShell->ApplyChangedSentence(rChanged, bRecheck); + else if(bDrawText ) + { + SdrView* pDrView = pWrtShell->GetDrawView(); + SdrOutliner *pOutliner = pDrView->GetTextEditOutliner(); + pOutliner->ApplyChangedSentence(pDrView->GetTextEditOutlinerView()->GetEditView(), rChanged, bRecheck); + } + } +} + +void SwSpellDialogChildWindow::AddAutoCorrection( + const OUString& rOld, const OUString& rNew, LanguageType eLanguage) +{ + SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect(); + pACorr->PutText( rOld, rNew, eLanguage ); +} + +bool SwSpellDialogChildWindow::HasAutoCorrection() +{ + return true; +} + +bool SwSpellDialogChildWindow::HasGrammarChecking() +{ + return SvtLinguConfig().HasGrammarChecker(); +} + +bool SwSpellDialogChildWindow::IsGrammarChecking() +{ + return m_bIsGrammarCheckingOn; +} + +void SwSpellDialogChildWindow::SetGrammarChecking(bool bOn) +{ + uno::Any aVal; + aVal <<= bOn; + m_bIsGrammarCheckingOn = bOn; + SvtLinguConfig().SetProperty( UPN_IS_GRAMMAR_INTERACTIVE, aVal ); + // set current spell position to the start of the current sentence to + // continue with this sentence after grammar checking state has been changed + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(pWrtShell) + { + ShellMode eSelMode = pWrtShell->GetView().GetShellMode(); + bool bDrawText = ShellMode::DrawText == eSelMode; + bool bNormalText = + ShellMode::TableText == eSelMode || + ShellMode::ListText == eSelMode || + ShellMode::TableListText == eSelMode || + ShellMode::Text == eSelMode; + if( bNormalText ) + SwEditShell::PutSpellingToSentenceStart(); + else if( bDrawText ) + { + SdrView* pSdrView = pWrtShell->GetDrawView(); + SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr; + OSL_ENSURE(pOutliner, "No Outliner in SwSpellDialogChildWindow::SetGrammarChecking"); + if(pOutliner) + { + pOutliner->PutSpellingToSentenceStart( pSdrView->GetTextEditOutlinerView()->GetEditView() ); + } + } + } +} + +void SwSpellDialogChildWindow::GetFocus() +{ + if(m_pSpellState->m_bLockFocus) + return; + bool bInvalidate = false; + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(pWrtShell && !m_pSpellState->m_bInitialCall) + { + ShellMode eSelMode = pWrtShell->GetView().GetShellMode(); + if(eSelMode != m_pSpellState->m_eSelMode) + { + // prevent initial invalidation + if(m_pSpellState->m_bLostFocus) + bInvalidate = true; + } + else + { + switch(m_pSpellState->m_eSelMode) + { + case ShellMode::Text: + case ShellMode::ListText: + case ShellMode::TableText: + case ShellMode::TableListText: + { + SwPaM* pCursor = pWrtShell->GetCursor(); + if(m_pSpellState->m_pPointNode != &pCursor->GetNode() || + m_pSpellState->m_pMarkNode != &pCursor->GetNode(false)|| + m_pSpellState->m_nPointPos != pCursor->GetPoint()->nContent.GetIndex()|| + m_pSpellState->m_nMarkPos != pCursor->GetMark()->nContent.GetIndex()) + bInvalidate = true; + } + break; + case ShellMode::DrawText: + { + SdrView* pSdrView = pWrtShell->GetDrawView(); + SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr; + if(!pOutliner || m_pSpellState->m_pOutliner != pOutliner) + bInvalidate = true; + else + { + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + OSL_ENSURE(pOLV, "no OutlinerView in SwSpellDialogChildWindow::GetFocus()"); + if(!pOLV || m_pSpellState->m_aESelection != pOLV->GetSelection()) + bInvalidate = true; + } + } + break; + default: bInvalidate = true; + } + } + } + else + { + bInvalidate = true; + } + if(bInvalidate) + InvalidateSpellDialog(); +} + +void SwSpellDialogChildWindow::LoseFocus() +{ + // prevent initial invalidation + m_pSpellState->m_bLostFocus = true; + if(m_pSpellState->m_bLockFocus) + return; + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(pWrtShell) + { + m_pSpellState->m_eSelMode = pWrtShell->GetView().GetShellMode(); + m_pSpellState->m_pPointNode = m_pSpellState->m_pMarkNode = nullptr; + m_pSpellState->m_nPointPos = m_pSpellState->m_nMarkPos = 0; + m_pSpellState->m_pOutliner = nullptr; + + switch(m_pSpellState->m_eSelMode) + { + case ShellMode::Text: + case ShellMode::ListText: + case ShellMode::TableText: + case ShellMode::TableListText: + { + // store a node pointer and a pam-position to be able to check on next GetFocus(); + SwPaM* pCursor = pWrtShell->GetCursor(); + m_pSpellState->m_pPointNode = &pCursor->GetNode(); + m_pSpellState->m_pMarkNode = &pCursor->GetNode(false); + m_pSpellState->m_nPointPos = pCursor->GetPoint()->nContent.GetIndex(); + m_pSpellState->m_nMarkPos = pCursor->GetMark()->nContent.GetIndex(); + + } + break; + case ShellMode::DrawText: + { + SdrView* pSdrView = pWrtShell->GetDrawView(); + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + m_pSpellState->m_pOutliner = pOutliner; + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + OSL_ENSURE(pOutliner && pOLV, "no Outliner/OutlinerView in SwSpellDialogChildWindow::LoseFocus()"); + if(pOLV) + { + m_pSpellState->m_aESelection = pOLV->GetSelection(); + } + } + break; + default:;// prevent warning + } + } + else + m_pSpellState->m_eSelMode = ShellMode::Object; +} + +void SwSpellDialogChildWindow::InvalidateSpellDialog() +{ + SwWrtShell* pWrtShell = GetWrtShell_Impl(); + if(!m_pSpellState->m_bInitialCall && pWrtShell) + pWrtShell->SpellEnd(nullptr, false); + m_pSpellState->Reset(); + svx::SpellDialogChildWindow::InvalidateSpellDialog(); +} + +SwWrtShell* SwSpellDialogChildWindow::GetWrtShell_Impl() +{ + SfxDispatcher* pDispatch = GetBindings().GetDispatcher(); + SwView* pView = nullptr; + if(pDispatch) + { + sal_uInt16 nShellIdx = 0; + SfxShell* pShell; + while(nullptr != (pShell = pDispatch->GetShell(nShellIdx++))) + if(dynamic_cast< const SwView *>( pShell ) != nullptr) + { + pView = static_cast<SwView* >(pShell); + break; + } + } + return pView ? pView->GetWrtShellPtr(): nullptr; +} + +// set the cursor into the body text - necessary if any object is selected +// on start of the spelling dialog +void SwSpellDialogChildWindow::MakeTextSelection_Impl(SwWrtShell& rShell, ShellMode eSelMode) +{ + SwView& rView = rShell.GetView(); + switch(eSelMode) + { + case ShellMode::Text: + case ShellMode::ListText: + case ShellMode::TableText: + case ShellMode::TableListText: + case ShellMode::DrawText: + OSL_FAIL("text already active in SwSpellDialogChildWindow::MakeTextSelection_Impl()"); + break; + + case ShellMode::Frame: + { + rShell.UnSelectFrame(); + rShell.LeaveSelFrameMode(); + rView.AttrChangedNotify(nullptr); + } + break; + + case ShellMode::Draw: + case ShellMode::DrawForm: + case ShellMode::Bezier: + if(FindNextDrawTextError_Impl(rShell)) + { + rView.AttrChangedNotify(nullptr); + break; + } + [[fallthrough]]; // to deselect the object + case ShellMode::Graphic: + case ShellMode::Object: + { + if ( rShell.IsDrawCreate() ) + { + rView.GetDrawFuncPtr()->BreakCreate(); + rView.AttrChangedNotify(nullptr); + } + else if ( rShell.HasSelection() || rView.IsDrawMode() ) + { + SdrView *pSdrView = rShell.GetDrawView(); + if(pSdrView && pSdrView->AreObjectsMarked() && + pSdrView->GetHdlList().GetFocusHdl()) + { + const_cast<SdrHdlList&>(pSdrView->GetHdlList()).ResetFocusHdl(); + } + else + { + rView.LeaveDrawCreate(); + Point aPt(LONG_MIN, LONG_MIN); + // go out of the frame + rShell.SelectObj(aPt, SW_LEAVE_FRAME); + SfxBindings& rBind = rView.GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + rShell.EnterStdMode(); + rView.AttrChangedNotify(nullptr); + } + } + } + break; + default:; // prevent warning + } +} + +// select the next draw text object that has a spelling error +bool SwSpellDialogChildWindow::FindNextDrawTextError_Impl(SwWrtShell& rSh) +{ + bool bNextDoc = false; + SdrView* pDrView = rSh.GetDrawView(); + if(!pDrView) + return bNextDoc; + SwView& rView = rSh.GetView(); + SwDoc* pDoc = rView.GetDocShell()->GetDoc(); + const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList(); + // start at the current draw object - if there is any selected + SdrTextObj* pCurrentTextObj = nullptr; + if ( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( auto pSdrTextObj = dynamic_cast<SdrTextObj *>( pObj ) ) + pCurrentTextObj = pSdrTextObj; + } + // at first fill the list of drawing objects + if(!m_pSpellState->m_bTextObjectsCollected ) + { + m_pSpellState->m_bTextObjectsCollected = true; + SwDrawContact::GetTextObjectsFromFormat( m_pSpellState->m_aTextObjects, pDoc ); + if(pCurrentTextObj) + { + m_pSpellState->m_aTextObjects.remove(pCurrentTextObj); + m_pSpellState->m_aTextObjects.push_back(pCurrentTextObj); + } + } + if(!m_pSpellState->m_aTextObjects.empty()) + { + Reference< XSpellChecker1 > xSpell( GetSpellChecker() ); + while(!bNextDoc && !m_pSpellState->m_aTextObjects.empty()) + { + std::list<SdrTextObj*>::iterator aStart = m_pSpellState->m_aTextObjects.begin(); + SdrTextObj* pTextObj = *aStart; + if(m_pSpellState->m_pStartDrawing == pTextObj) + m_pSpellState->m_bRestartDrawing = true; + m_pSpellState->m_aTextObjects.erase(aStart); + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if ( pParaObj ) + { + bool bHasSpellError = false; + { + SdrOutliner aTmpOutliner(pDoc->getIDocumentDrawModelAccess().GetDrawModel()-> + GetDrawOutliner().GetEmptyItemSet().GetPool(), + OutlinerMode::TextObject ); + aTmpOutliner.SetRefDevice( pDoc->getIDocumentDeviceAccess().getPrinter( false ) ); + MapMode aMapMode (MapUnit::MapTwip); + aTmpOutliner.SetRefMapMode(aMapMode); + aTmpOutliner.SetPaperSize( pTextObj->GetLogicRect().GetSize() ); + aTmpOutliner.SetSpeller( xSpell ); + + std::unique_ptr<OutlinerView> pOutlView( new OutlinerView( &aTmpOutliner, &(rView.GetEditWin()) ) ); + pOutlView->GetOutliner()->SetRefDevice( rSh.getIDocumentDeviceAccess().getPrinter( false ) ); + aTmpOutliner.InsertView( pOutlView.get() ); + Size aSize(1,1); + tools::Rectangle aRect( Point(), aSize ); + pOutlView->SetOutputArea( aRect ); + aTmpOutliner.SetText( *pParaObj ); + aTmpOutliner.ClearModifyFlag(); + bHasSpellError = EESpellState::Ok != aTmpOutliner.HasSpellErrors(); + aTmpOutliner.RemoveView( pOutlView.get() ); + } + if(bHasSpellError) + { + // now the current one has to be deselected + if(pCurrentTextObj) + pDrView->SdrEndTextEdit( true ); + // and the found one should be activated + rSh.MakeVisible(pTextObj->GetLogicRect()); + Point aTmp( 0,0 ); + rSh.SelectObj( aTmp, 0, pTextObj ); + SdrPageView* pPV = pDrView->GetSdrPageView(); + rView.BeginTextEdit( pTextObj, pPV, &rView.GetEditWin(), false, true ); + rView.AttrChangedNotify(nullptr); + bNextDoc = true; + } + } + } + } + return bNextDoc; +} + +bool SwSpellDialogChildWindow::SpellDrawText_Impl(SwWrtShell& rSh, svx::SpellPortions& rPortions) +{ + bool bRet = false; + SdrView* pSdrView = rSh.GetDrawView(); + SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr; + OSL_ENSURE(pOutliner, "No Outliner in SwSpellDialogChildWindow::SpellDrawText_Impl"); + if(pOutliner) + { + bRet = pOutliner->SpellSentence(pSdrView->GetTextEditOutlinerView()->GetEditView(), rPortions); + // find out if the current selection is in the first spelled drawing object + // and behind the initial selection + if(bRet && m_pSpellState->m_bRestartDrawing) + { + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + ESelection aCurrentSelection = pOLV->GetSelection(); + if(m_pSpellState->m_aStartDrawingSelection.nEndPara < aCurrentSelection.nEndPara || + (m_pSpellState->m_aStartDrawingSelection.nEndPara == aCurrentSelection.nEndPara && + m_pSpellState->m_aStartDrawingSelection.nEndPos < aCurrentSelection.nEndPos)) + { + bRet = false; + rPortions.clear(); + } + } + } + return bRet; +} + +void SwSpellDialogChildWindow::LockFocusNotification(bool bLock) +{ + OSL_ENSURE(m_pSpellState->m_bLockFocus != bLock, "invalid locking - no change of state"); + m_pSpellState->m_bLockFocus = bLock; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dialog/regionsw.cxx b/sw/source/uibase/dialog/regionsw.cxx new file mode 100644 index 000000000..45cc86b4f --- /dev/null +++ b/sw/source/uibase/dialog/regionsw.cxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/linkmgr.hxx> +#include <editeng/sizeitem.hxx> +#include <section.hxx> +#include <basesh.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <fmtclds.hxx> +#include <fmtfsize.hxx> +#include <cmdid.h> +#include <swabstdlg.hxx> + +void SwBaseShell::InsertRegionDialog(SfxRequest& rReq) +{ + SwWrtShell& rSh = GetShell(); + const SfxItemSet *pSet = rReq.GetArgs(); + + SfxItemSet aSet( + GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + RES_FTN_AT_TXTEND, RES_FRAMEDIR, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE>{}); + + if (!pSet || pSet->Count()==0) + { + SwRect aRect; + rSh.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + + long nWidth = aRect.Width(); + aSet.Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth)); + + // height=width for more consistent preview (analog to edit region) + aSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(nWidth, nWidth))); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<AbstractInsertSectionTabDialog> aTabDlg(pFact->CreateInsertSectionTabDialog( + GetView().GetFrameWeld(), aSet , rSh)); + aTabDlg->StartExecuteAsync([aTabDlg](sal_Int32 /*nResult*/){ + aTabDlg->disposeOnce(); + }); + rReq.Ignore(); + } + else + { + const SfxPoolItem *pItem = nullptr; + OUString aTmpStr; + if ( SfxItemState::SET == + pSet->GetItemState(FN_PARAM_REGION_NAME, true, &pItem) ) + { + const OUString sRemoveWhenUniStringIsGone = static_cast<const SfxStringItem *>(pItem)->GetValue(); + aTmpStr = rSh.GetUniqueSectionName(&sRemoveWhenUniStringIsGone); + } + else + aTmpStr = rSh.GetUniqueSectionName(); + + SwSectionData aSection(SectionType::Content, aTmpStr); + rReq.SetReturnValue(SfxStringItem(FN_INSERT_REGION, aTmpStr)); + + aSet.Put( *pSet ); + if(SfxItemState::SET == pSet->GetItemState(SID_ATTR_COLUMNS, false, &pItem)|| + SfxItemState::SET == pSet->GetItemState(FN_INSERT_REGION, false, &pItem)) + { + SwFormatCol aCol; + SwRect aRect; + rSh.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + long nWidth = aRect.Width(); + + sal_uInt16 nCol = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + if(nCol) + { + aCol.Init( nCol, 0, static_cast< sal_uInt16 >(nWidth) ); + aSet.Put(aCol); + } + } + else if(SfxItemState::SET == pSet->GetItemState(RES_COL, false, &pItem)) + { + aSet.Put(*pItem); + } + + const bool bHidden = SfxItemState::SET == pSet->GetItemState(FN_PARAM_REGION_HIDDEN, true, &pItem) && + static_cast<const SfxBoolItem *>(pItem)->GetValue(); + const bool bProtect = SfxItemState::SET == pSet->GetItemState(FN_PARAM_REGION_PROTECT, true, &pItem) && + static_cast<const SfxBoolItem *>(pItem)->GetValue(); + // #114856# edit in readonly sections + const bool bEditInReadonly = SfxItemState::SET == pSet->GetItemState(FN_PARAM_REGION_EDIT_IN_READONLY, true, &pItem) && + static_cast<const SfxBoolItem *>(pItem)->GetValue(); + + aSection.SetProtectFlag(bProtect); + aSection.SetHidden(bHidden); + // #114856# edit in readonly sections + aSection.SetEditInReadonlyFlag(bEditInReadonly); + + if(SfxItemState::SET == + pSet->GetItemState(FN_PARAM_REGION_CONDITION, true, &pItem)) + aSection.SetCondition(static_cast<const SfxStringItem *>(pItem)->GetValue()); + + OUString aFile, aSub; + if(SfxItemState::SET == + pSet->GetItemState(FN_PARAM_1, true, &pItem)) + aFile = static_cast<const SfxStringItem *>(pItem)->GetValue(); + + if(SfxItemState::SET == + pSet->GetItemState(FN_PARAM_3, true, &pItem)) + aSub = static_cast<const SfxStringItem *>(pItem)->GetValue(); + + if(!aFile.isEmpty() || !aSub.isEmpty()) + { + OUString sLinkFileName = OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator); + sLinkFileName = comphelper::string::setToken(sLinkFileName, 0, sfx2::cTokenSeparator, aFile); + + if(SfxItemState::SET == + pSet->GetItemState(FN_PARAM_2, true, &pItem)) + { + sLinkFileName = comphelper::string::setToken(sLinkFileName, 1, sfx2::cTokenSeparator, + static_cast<const SfxStringItem *>(pItem)->GetValue()); + } + + sLinkFileName += aSub; + aSection.SetType( SectionType::FileLink ); + aSection.SetLinkFileName(sLinkFileName); + } + rSh.InsertSection(aSection, aSet.Count() ? &aSet : nullptr); + rReq.Done(); + } +} + +void SwWrtShell::StartInsertRegionDialog(const SwSectionData& rSectionData) +{ + SfxItemSet aSet( + GetView().GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE>{}); + SwRect aRect; + CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + long nWidth = aRect.Width(); + aSet.Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth)); + // height=width for more consistent preview (analog to edit region) + aSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(nWidth, nWidth))); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<AbstractInsertSectionTabDialog> aTabDlg(pFact->CreateInsertSectionTabDialog( + GetView().GetFrameWeld(), aSet, *this)); + aTabDlg->SetSectionData(rSectionData); + aTabDlg->StartExecuteAsync([aTabDlg](sal_Int32 /*nResult*/){ + aTabDlg->disposeOnce(); + }); +} + +void SwBaseShell::EditRegionDialog(SfxRequest const & rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxPoolItem* pItem = nullptr; + if(pArgs) + pArgs->GetItemState(nSlot, false, &pItem); + SwWrtShell& rWrtShell = GetShell(); + + switch ( nSlot ) + { + case FN_EDIT_REGION: + case FN_EDIT_CURRENT_REGION: + { + weld::Window* pParentWin = GetView().GetFrameWeld(); + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractEditRegionDlg> pEditRegionDlg(pFact->CreateEditRegionDlg(pParentWin, rWrtShell)); + if(auto pStringItem = dynamic_cast< const SfxStringItem *>( pItem )) + { + pEditRegionDlg->SelectSection(pStringItem->GetValue()); + } + pEditRegionDlg->Execute(); + } + } + break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dialog/swabstdlg.cxx b/sw/source/uibase/dialog/swabstdlg.cxx new file mode 100644 index 000000000..f726a8274 --- /dev/null +++ b/sw/source/uibase/dialog/swabstdlg.cxx @@ -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 . + */ + +#include <swabstdlg.hxx> + +#include <osl/module.hxx> + +typedef SwAbstractDialogFactory* (*SwFuncPtrCreateDialogFactory)(); + +#ifndef DISABLE_DYNLOADING + +extern "C" { static void thisModule() {} } + +#else + +extern "C" SwAbstractDialogFactory* SwCreateDialogFactory(); + +#endif + +SwAbstractDialogFactory* SwAbstractDialogFactory::Create() +{ + SwFuncPtrCreateDialogFactory fp = nullptr; +#ifndef DISABLE_DYNLOADING + static ::osl::Module aDialogLibrary; + static const OUString sLibName(SWUI_DLL_NAME); + if ( aDialogLibrary.is() || aDialogLibrary.loadRelative( &thisModule, sLibName, + SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY ) ) + fp = reinterpret_cast<SwAbstractDialogFactory* (SAL_CALL*)()>( + aDialogLibrary.getFunctionSymbol( "SwCreateDialogFactory" )); +#else + fp = SwCreateDialogFactory; +#endif + + if ( fp ) + return fp(); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dialog/swwrtshitem.cxx b/sw/source/uibase/dialog/swwrtshitem.cxx new file mode 100644 index 000000000..ad88e205e --- /dev/null +++ b/sw/source/uibase/dialog/swwrtshitem.cxx @@ -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 . + */ + +#include <swwrtshitem.hxx> +#include <globals.hrc> + +SwWrtShellItem::SwWrtShellItem( SwWrtShell* pSh ) + : SfxPoolItem( SID_WRT_SHELL ), pWrtSh( pSh ) +{ + +} + +bool SwWrtShellItem::operator==( const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) + && pWrtSh == static_cast<const SwWrtShellItem&>(rItem).pWrtSh; +} + +SwWrtShellItem* SwWrtShellItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwWrtShellItem( *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dialog/watermarkdialog.cxx b/sw/source/uibase/dialog/watermarkdialog.cxx new file mode 100644 index 000000000..d558d0c6d --- /dev/null +++ b/sw/source/uibase/dialog/watermarkdialog.cxx @@ -0,0 +1,106 @@ +/* -*- 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 <watermarkdialog.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/dispatchcommand.hxx> +#include <editeng/editids.hrc> +#include <editeng/flstitem.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/watermarkitem.hxx> +#include <svtools/ctrltool.hxx> + +SwWatermarkDialog::SwWatermarkDialog(weld::Window* pParent, SfxBindings& rBindings) + : SfxDialogController(pParent, "modules/swriter/ui/watermarkdialog.ui", "WatermarkDialog") + , m_rBindings(rBindings) + , m_xTextInput(m_xBuilder->weld_entry("TextInput")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_xFont(m_xBuilder->weld_combo_box("FontBox")) + , m_xAngle(m_xBuilder->weld_metric_spin_button("Angle", FieldUnit::DEGREE)) + , m_xTransparency(m_xBuilder->weld_metric_spin_button("Transparency", FieldUnit::PERCENT)) + , m_xColor(new ColorListBox(m_xBuilder->weld_menu_button("Color"), m_xDialog.get())) +{ + InitFields(); +} + +SwWatermarkDialog::~SwWatermarkDialog() +{ +} + +void SwWatermarkDialog::InitFields() +{ + // Update font list + SfxObjectShell* pDocSh = SfxObjectShell::Current(); + const SfxPoolItem* pFontItem; + const FontList* pFontList = nullptr; + std::unique_ptr<FontList> xFontList; + + if ( pDocSh && ( ( pFontItem = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ) ) != nullptr ) ) + pFontList = static_cast<const SvxFontListItem*>( pFontItem )->GetFontList(); + + if (!pFontList) + { + xFontList.reset(new FontList(Application::GetDefaultDevice(), nullptr)); + pFontList = xFontList.get(); + } + + m_xFont->freeze(); + sal_uInt16 nFontCount = pFontList->GetFontNameCount(); + for (sal_uInt16 i = 0; i < nFontCount; ++i) + { + const FontMetric& rFontMetric = pFontList->GetFontName(i); + m_xFont->append_text(rFontMetric.GetFamilyName()); + } + m_xFont->thaw(); + + m_xOKButton->connect_clicked(LINK(this, SwWatermarkDialog, OKButtonHdl)); + + // Get watermark properties + const SfxPoolItem* pItem; + SfxItemState eState = m_rBindings.GetDispatcher()->QueryState( SID_WATERMARK, pItem ); + + if( eState >= SfxItemState::DEFAULT && pItem && pItem->Which() == SID_WATERMARK) + { + const SfxWatermarkItem* pWatermark = static_cast<const SfxWatermarkItem*>( pItem ); + const OUString& sText = pWatermark->GetText(); + m_xTextInput->set_text(sText); + OUString sFontName = pWatermark->GetFont(); + int nFontIndex = m_xFont->find_text(sFontName); + if (nFontIndex != -1) + m_xFont->set_active(nFontIndex); + else + m_xFont->set_entry_text(sFontName); + m_xAngle->set_value(pWatermark->GetAngle(), FieldUnit::DEGREE); + m_xColor->SelectEntry( pWatermark->GetColor() ); + m_xTransparency->set_value(pWatermark->GetTransparency(), FieldUnit::PERCENT); + } +} + +IMPL_LINK_NOARG(SwWatermarkDialog, OKButtonHdl, weld::Button&, void) +{ + OUString sText = m_xTextInput->get_text(); + + css::uno::Sequence<css::beans::PropertyValue> aPropertyValues( comphelper::InitPropertySequence( + { + { "Text", css::uno::makeAny( sText ) }, + { "Font", css::uno::makeAny( m_xFont->get_active_text() ) }, + { "Angle", css::uno::makeAny( static_cast<sal_Int16>( m_xAngle->get_value(FieldUnit::DEGREE) ) ) }, + { "Transparency", css::uno::makeAny( static_cast<sal_Int16>( m_xTransparency->get_value(FieldUnit::PERCENT) ) ) }, + { "Color", css::uno::makeAny( static_cast<sal_uInt32>( m_xColor->GetSelectEntryColor().GetRGBColor() ) ) } + } ) ); + comphelper::dispatchCommand( ".uno:Watermark", aPropertyValues ); + + m_xDialog->response(RET_OK); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/dialog/wordcountwrapper.cxx b/sw/source/uibase/dialog/wordcountwrapper.cxx new file mode 100644 index 000000000..e1a6c99a9 --- /dev/null +++ b/sw/source/uibase/dialog/wordcountwrapper.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/. + */ + +#include <wordcountdialog.hxx> +#include <docstat.hxx> +#include <cmdid.h> + +SFX_IMPL_CHILDWINDOW_WITHID(SwWordCountWrapper, FN_WORDCOUNT_DIALOG) + +SwWordCountWrapper::SwWordCountWrapper(vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) + : SfxChildWindow(pParentWindow, nId) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + xAbstDlg.reset(pFact->CreateSwWordCountDialog(pBindings, this, pParentWindow->GetFrameWeld(), pInfo)); + SetController(xAbstDlg->GetController()); +} + +SwWordCountWrapper::~SwWordCountWrapper() +{ + xAbstDlg.disposeAndClear(); +} + +SfxChildWinInfo SwWordCountWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + +void SwWordCountWrapper::UpdateCounts() +{ + xAbstDlg->UpdateCounts(); +} + +void SwWordCountWrapper::SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat) +{ + xAbstDlg->SetCounts(rCurrCnt, rDocStat); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dochdl/gloshdl.cxx b/sw/source/uibase/dochdl/gloshdl.cxx new file mode 100644 index 000000000..860a926a5 --- /dev/null +++ b/sw/source/uibase/dochdl/gloshdl.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 <vcl/errinf.hxx> +#include <vcl/weld.hxx> +#include <svl/macitem.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <gloshdl.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <swundo.hxx> +#include <expfld.hxx> +#include <initui.hxx> +#include <gloslst.hxx> +#include <swdtflvr.hxx> + +#include <strings.hrc> +#include <vcl/svapp.hxx> + +#include <editeng/acorrcfg.hxx> +#include <sfx2/event.hxx> +#include <swabstdlg.hxx> + +#include <memory> + +using namespace ::com::sun::star; + +const short RET_EDIT = 100; + +namespace { + +struct TextBlockInfo_Impl +{ + OUString sTitle; + OUString sLongName; + OUString sGroupName; + TextBlockInfo_Impl(OUString const& rTitle, OUString const& rLongName, OUString const& rGroupName) + : sTitle(rTitle), sLongName(rLongName), sGroupName(rGroupName) {} +}; + +} + +// Dialog for edit templates +void SwGlossaryHdl::GlossaryDlg() +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGlossaryDlg> pDlg(pFact->CreateGlossaryDlg(pViewFrame, this, pWrtShell)); + OUString sName; + OUString sShortName; + + if( RET_EDIT == pDlg->Execute() ) + { + sName = pDlg->GetCurrGrpName(); + sShortName = pDlg->GetCurrShortName(); + } + + pDlg.disposeAndClear(); + pCurGrp.reset(); + if(HasGlossaryList()) + { + GetGlossaryList()->ClearGroups(); + } + + if( !sName.isEmpty() || !sShortName.isEmpty() ) + rStatGlossaries.EditGroupDoc( sName, sShortName ); +} + +// set the default group; if called from the dialog +// the group is created temporarily for faster access +void SwGlossaryHdl::SetCurGroup(const OUString &rGrp, bool bApi, bool bAlwaysCreateNew ) +{ + OUString sGroup(rGrp); + if (sGroup.indexOf(GLOS_DELIM)<0 && !FindGroupName(sGroup)) + { + sGroup += OUStringChar(GLOS_DELIM) + "0"; + } + if(pCurGrp) + { + bool bPathEqual = false; + if(!bAlwaysCreateNew) + { + INetURLObject aTemp( pCurGrp->GetFileName() ); + const OUString sCurBase = aTemp.getBase(); + aTemp.removeSegment(); + const OUString sCurEntryPath = aTemp.GetMainURL(INetURLObject::DecodeMechanism::NONE); + const std::vector<OUString> & rPathArr = rStatGlossaries.GetPathArray(); + sal_uInt16 nCurrentPath = USHRT_MAX; + for (size_t nPath = 0; nPath < rPathArr.size(); ++nPath) + { + if (sCurEntryPath == rPathArr[nPath]) + { + nCurrentPath = static_cast<sal_uInt16>(nPath); + break; + } + } + const OUString sPath = sGroup.getToken(1, GLOS_DELIM); + sal_uInt16 nComparePath = static_cast<sal_uInt16>(sPath.toInt32()); + if(nCurrentPath == nComparePath && + sGroup.getToken(0, GLOS_DELIM) == sCurBase) + bPathEqual = true; + } + + // When path changed, the name is not reliable + if(!bAlwaysCreateNew && bPathEqual) + return; + } + aCurGrp = sGroup; + if(!bApi) + { + pCurGrp = rStatGlossaries.GetGroupDoc(aCurGrp, true); + } +} + +size_t SwGlossaryHdl::GetGroupCnt() const +{ + return rStatGlossaries.GetGroupCnt(); +} + +OUString SwGlossaryHdl::GetGroupName( size_t nId, OUString* pTitle ) +{ + OUString sRet = rStatGlossaries.GetGroupName(nId); + if(pTitle) + { + std::unique_ptr<SwTextBlocks> pGroup = rStatGlossaries.GetGroupDoc(sRet); + if (pGroup && !pGroup->GetError()) + { + *pTitle = pGroup->GetName(); + if (pTitle->isEmpty()) + { + *pTitle = sRet.getToken(0, GLOS_DELIM); + pGroup->SetName(*pTitle); + } + } + else + { + sRet.clear(); + } + } + return sRet; +} + +void SwGlossaryHdl::NewGroup(OUString &rGrpName, const OUString& rTitle) +{ + if (rGrpName.indexOf(GLOS_DELIM)<0) + FindGroupName(rGrpName); + rStatGlossaries.NewGroupDoc(rGrpName, rTitle); +} + +void SwGlossaryHdl::RenameGroup(const OUString& rOld, OUString& rNew, const OUString& rNewTitle) +{ + OUString sOldGroup(rOld); + if (rOld.indexOf(GLOS_DELIM)<0) + FindGroupName(sOldGroup); + if(rOld == rNew) + { + std::unique_ptr<SwTextBlocks> pGroup = rStatGlossaries.GetGroupDoc(sOldGroup); + if(pGroup) + { + pGroup->SetName(rNewTitle); + } + } + else + { + OUString sNewGroup(rNew); + if (sNewGroup.indexOf(GLOS_DELIM)<0) + { + sNewGroup += OUStringChar(GLOS_DELIM) + "0"; + } + rStatGlossaries.RenameGroupDoc(sOldGroup, sNewGroup, rNewTitle); + rNew = sNewGroup; + } +} + +// delete an autotext-file-group +bool SwGlossaryHdl::DelGroup(const OUString &rGrpName) +{ + OUString sGroup(rGrpName); + if (sGroup.indexOf(GLOS_DELIM)<0) + FindGroupName(sGroup); + if( rStatGlossaries.DelGroupDoc(sGroup) ) + { + if(pCurGrp) + { + if (pCurGrp->GetName() == sGroup) + pCurGrp.reset(); + } + return true; + } + return false; +} + +// ask for number of autotexts +sal_uInt16 SwGlossaryHdl::GetGlossaryCnt() const +{ + return pCurGrp ? pCurGrp->GetCount() : 0; +} + +OUString SwGlossaryHdl::GetGlossaryName( sal_uInt16 nId ) +{ + OSL_ENSURE(nId < GetGlossaryCnt(), "Text building block array over-indexed."); + return pCurGrp->GetLongName( nId ); +} + +OUString SwGlossaryHdl::GetGlossaryShortName(sal_uInt16 nId) +{ + OSL_ENSURE(nId < GetGlossaryCnt(), "Text building block array over-indexed."); + return pCurGrp->GetShortName( nId ); +} + +// ask for short name +OUString SwGlossaryHdl::GetGlossaryShortName(const OUString &rName) +{ + OUString sReturn; + SwTextBlocks *pTmp = + pCurGrp ? pCurGrp.get() : rStatGlossaries.GetGroupDoc( aCurGrp ).release(); + if(pTmp) + { + sal_uInt16 nIdx = pTmp->GetLongIndex( rName ); + if( nIdx != sal_uInt16(-1) ) + sReturn = pTmp->GetShortName( nIdx ); + if( !pCurGrp ) + delete pTmp; + } + return sReturn; +} + +// short name for autotext already used? +bool SwGlossaryHdl::HasShortName(const OUString& rShortName) const +{ + SwTextBlocks *pBlock = pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc( aCurGrp ).release(); + bool bRet = pBlock->GetIndex( rShortName ) != sal_uInt16(-1); + if( !pCurGrp ) + delete pBlock; + return bRet; +} + +// Create autotext +bool SwGlossaryHdl::NewGlossary(const OUString& rName, const OUString& rShortName, + bool bCreateGroup, bool bNoAttr) +{ + SwTextBlocks *pTmp = + pCurGrp ? pCurGrp.get() : rStatGlossaries.GetGroupDoc( aCurGrp, bCreateGroup ).release(); + //pTmp == 0 if the AutoText path setting is wrong + if(!pTmp) + { + if (!pCurGrp) + delete pTmp; + return false; + } + + OUString sOnlyText; + OUString* pOnlyText = nullptr; + if( bNoAttr ) + { + pWrtShell->GetSelectedText( sOnlyText, ParaBreakType::ToOnlyCR ); + pOnlyText = &sOnlyText; + } + + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + + const sal_uInt16 nSuccess = pWrtShell->MakeGlossary( *pTmp, rName, rShortName, + rCfg.IsSaveRelFile(), pOnlyText ); + if(nSuccess == sal_uInt16(-1) ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWrtShell->GetView().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_ERR_INSERT_GLOS))); + xBox->run(); + } + if( !pCurGrp ) + delete pTmp; + return nSuccess != sal_uInt16(-1); +} + +// Delete an autotext +bool SwGlossaryHdl::DelGlossary(const OUString &rShortName) +{ + SwTextBlocks *pGlossary = pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc(aCurGrp).release(); + //pTmp == 0 if the AutoText path setting is wrong + if(!pGlossary) + { + if( !pCurGrp ) + delete pGlossary; + return false; + } + + sal_uInt16 nIdx = pGlossary->GetIndex( rShortName ); + if( nIdx != sal_uInt16(-1) ) + pGlossary->Delete( nIdx ); + if( !pCurGrp ) + delete pGlossary; + return true; +} + +// expand short name +bool SwGlossaryHdl::ExpandGlossary(weld::Window* pParent) +{ + OSL_ENSURE(pWrtShell->CanInsert(), "illegal"); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossaryGetCurrGroup fnGetCurrGroup = pFact->GetGlossaryCurrGroupFunc(); + OUString sGroupName( (*fnGetCurrGroup)() ); + if (sGroupName.indexOf(GLOS_DELIM)<0) + FindGroupName(sGroupName); + std::unique_ptr<SwTextBlocks> pGlossary = rStatGlossaries.GetGroupDoc(sGroupName); + + OUString aShortName; + + // use this at text selection + if(pWrtShell->SwCursorShell::HasSelection() && !pWrtShell->IsBlockMode()) + { + aShortName = pWrtShell->GetSelText(); + } + else + { + if(pWrtShell->IsAddMode()) + pWrtShell->LeaveAddMode(); + else if(pWrtShell->IsBlockMode()) + pWrtShell->LeaveBlockMode(); + else if(pWrtShell->IsExtMode()) + pWrtShell->LeaveExtMode(); + // select word (tdf#126589: part to the left of cursor) + if (pWrtShell->IsInWord() || pWrtShell->IsEndWrd()) + pWrtShell->PrvWrd(true); + // ask for word + if(pWrtShell->IsSelection()) + aShortName = pWrtShell->GetSelText(); + } + return pGlossary && Expand(pParent, aShortName, &rStatGlossaries, std::move(pGlossary)); +} + +bool SwGlossaryHdl::Expand(weld::Window* pParent, const OUString& rShortName, + SwGlossaries *pGlossaries, + std::unique_ptr<SwTextBlocks> pGlossary) +{ + std::vector<TextBlockInfo_Impl> aFoundArr; + OUString aShortName( rShortName ); + bool bCancel = false; + // search for text block + // - don't prefer current group depending on configuration setting + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + sal_uInt16 nFound = !rCfg.IsSearchInAllCategories() ? pGlossary->GetIndex( aShortName ) : -1; + // if not found then search in all groups + if( nFound == sal_uInt16(-1) ) + { + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + SwGlossaryList* pGlossaryList = ::GetGlossaryList(); + const size_t nGroupCount = pGlossaryList->GetGroupCount(); + for(size_t i = 0; i < nGroupCount; ++i) + { + // get group name with path-extension + const OUString sGroupName = pGlossaryList->GetGroupName(i); + if(sGroupName == pGlossary->GetName()) + continue; + const sal_uInt16 nBlockCount = pGlossaryList->GetBlockCount(i); + if(nBlockCount) + { + const OUString sTitle = pGlossaryList->GetGroupTitle(i); + for(sal_uInt16 j = 0; j < nBlockCount; j++) + { + const OUString sLongName(pGlossaryList->GetBlockLongName(i, j)); + const OUString sShortName(pGlossaryList->GetBlockShortName(i, j)); + if( rSCmp.isEqual( rShortName, sShortName )) + { + aFoundArr.emplace_back(sTitle, sLongName, sGroupName); + } + } + } + } + if( !aFoundArr.empty() ) // one was found + { + pGlossary.reset(); + if (1 == aFoundArr.size()) + { + TextBlockInfo_Impl& rData = aFoundArr.front(); + pGlossary = pGlossaries->GetGroupDoc(rData.sGroupName); + nFound = pGlossary->GetIndex( aShortName ); + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwSelGlossaryDlg> pDlg(pFact->CreateSwSelGlossaryDlg(pParent, aShortName)); + for(const TextBlockInfo_Impl & i : aFoundArr) + { + pDlg->InsertGlos(i.sTitle, i.sLongName); + } + pDlg->SelectEntryPos(0); + const sal_Int32 nRet = RET_OK == pDlg->Execute() ? + pDlg->GetSelectedIdx() : + -1; + pDlg.disposeAndClear(); + if (nRet != -1) + { + TextBlockInfo_Impl& rData = aFoundArr[nRet]; + pGlossary = pGlossaries->GetGroupDoc(rData.sGroupName); + nFound = pGlossary->GetIndex( aShortName ); + } + else + { + nFound = sal_uInt16(-1); + bCancel = true; + } + } + } + } + + // not found + if( nFound == sal_uInt16(-1) ) + { + if( !bCancel ) + { + pGlossary.reset(); + + const sal_Int32 nMaxLen = 50; + if(pWrtShell->IsSelection() && aShortName.getLength() > nMaxLen) + { + aShortName = aShortName.copy(0, nMaxLen) + " ..."; + } + OUString aTmp( SwResId(STR_NOGLOS)); + aTmp = aTmp.replaceFirst("%1", aShortName); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWrtShell->GetView().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + aTmp)); + xInfoBox->run(); + } + + return false; + } + else + { + SvxMacro aStartMacro(OUString(), OUString(), STARBASIC); + SvxMacro aEndMacro(OUString(), OUString(), STARBASIC); + GetMacros( aShortName, aStartMacro, aEndMacro, pGlossary.get() ); + + // StartAction must not be before HasSelection and DelRight, + // otherwise the possible Shell change gets delayed and + // API-programs would hang. + // Moreover the event macro must also not be called in an action + pWrtShell->StartUndo(SwUndoId::INSGLOSSARY); + if( aStartMacro.HasMacro() ) + pWrtShell->ExecMacro( aStartMacro ); + if(pWrtShell->HasSelection()) + pWrtShell->DelLeft(); + pWrtShell->StartAllAction(); + + // cache all InputFields + SwInputFieldList aFieldLst( pWrtShell, true ); + + pWrtShell->InsertGlossary(*pGlossary, aShortName); + pWrtShell->EndAllAction(); + if( aEndMacro.HasMacro() ) + { + pWrtShell->ExecMacro( aEndMacro ); + } + pWrtShell->EndUndo(SwUndoId::INSGLOSSARY); + + // demand input for all new InputFields + if( aFieldLst.BuildSortLst() ) + pWrtShell->UpdateInputFields( &aFieldLst ); + } + return true; +} + +// add autotext +bool SwGlossaryHdl::InsertGlossary(const OUString &rName) +{ + OSL_ENSURE(pWrtShell->CanInsert(), "illegal"); + + SwTextBlocks *pGlos = + pCurGrp ? pCurGrp.get() : rStatGlossaries.GetGroupDoc(aCurGrp).release(); + + if (!pGlos) + { + if (!pCurGrp) + delete pGlos; + return false; + } + + SvxMacro aStartMacro(OUString(), OUString(), STARBASIC); + SvxMacro aEndMacro(OUString(), OUString(), STARBASIC); + GetMacros( rName, aStartMacro, aEndMacro, pGlos ); + + // StartAction must not be before HasSelection and DelRight, + // otherwise the possible Shell change gets delayed and + // API-programs would hang. + // Moreover the event macro must also not be called in an action + if( aStartMacro.HasMacro() ) + pWrtShell->ExecMacro( aStartMacro ); + if( pWrtShell->HasSelection() ) + pWrtShell->DelRight(); + pWrtShell->StartAllAction(); + + // cache all InputFields + SwInputFieldList aFieldLst( pWrtShell, true ); + + pWrtShell->InsertGlossary(*pGlos, rName); + pWrtShell->EndAllAction(); + if( aEndMacro.HasMacro() ) + { + pWrtShell->ExecMacro( aEndMacro ); + } + + // demand input for all new InputFields + if( aFieldLst.BuildSortLst() ) + pWrtShell->UpdateInputFields( &aFieldLst ); + + if(!pCurGrp) + delete pGlos; + return true; +} + +// set / ask for macro +void SwGlossaryHdl::SetMacros(const OUString& rShortName, + const SvxMacro* pStart, + const SvxMacro* pEnd, + SwTextBlocks *pGlossary ) +{ + SwTextBlocks *pGlos = pGlossary ? pGlossary : + pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc( aCurGrp ).release(); + SvxMacroTableDtor aMacroTable; + if( pStart ) + aMacroTable.Insert( SvMacroItemId::SwStartInsGlossary, *pStart); + if( pEnd ) + aMacroTable.Insert( SvMacroItemId::SwEndInsGlossary, *pEnd); + sal_uInt16 nIdx = pGlos->GetIndex( rShortName ); + if( !pGlos->SetMacroTable( nIdx, aMacroTable ) && pGlos->GetError() ) + ErrorHandler::HandleError( pGlos->GetError() ); + + if(!pCurGrp && !pGlossary) + delete pGlos; +} + +void SwGlossaryHdl::GetMacros( const OUString &rShortName, + SvxMacro& rStart, + SvxMacro& rEnd, + SwTextBlocks *pGlossary ) +{ + SwTextBlocks *pGlos = pGlossary ? pGlossary + : pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc(aCurGrp).release(); + sal_uInt16 nIndex = pGlos->GetIndex( rShortName ); + if( nIndex != USHRT_MAX ) + { + SvxMacroTableDtor aMacroTable; + if( pGlos->GetMacroTable( nIndex, aMacroTable ) ) + { + SvxMacro *pMacro = aMacroTable.Get( SvMacroItemId::SwStartInsGlossary ); + if( pMacro ) + rStart = *pMacro; + + pMacro = aMacroTable.Get( SvMacroItemId::SwEndInsGlossary ); + if( pMacro ) + rEnd = *pMacro; + } + } + + if( !pCurGrp && !pGlossary ) + delete pGlos; +} + +// ctor, dtor +SwGlossaryHdl::SwGlossaryHdl(SfxViewFrame* pVwFrame, SwWrtShell *pSh) + : rStatGlossaries( *::GetGlossaries() ), + aCurGrp( SwGlossaries::GetDefName() ), + pViewFrame( pVwFrame ), + pWrtShell( pSh ) +{ +} + +SwGlossaryHdl::~SwGlossaryHdl() +{ +} + +// rename an autotext +bool SwGlossaryHdl::Rename(const OUString& rOldShort, const OUString& rNewShortName, + const OUString& rNewName ) +{ + bool bRet = false; + SwTextBlocks *pGlossary = pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc(aCurGrp).release(); + if(pGlossary) + { + sal_uInt16 nIdx = pGlossary->GetIndex( rOldShort ); + sal_uInt16 nOldLongIdx = pGlossary->GetLongIndex( rNewName ); + sal_uInt16 nOldIdx = pGlossary->GetIndex( rNewShortName ); + + if( nIdx != USHRT_MAX && + (nOldLongIdx == USHRT_MAX || nOldLongIdx == nIdx )&& + (nOldIdx == USHRT_MAX || nOldIdx == nIdx )) + { + pGlossary->Rename( nIdx, &rNewShortName, &rNewName ); + bRet = pGlossary->GetError() == ERRCODE_NONE; + } + if( !pCurGrp ) + delete pGlossary; + } + return bRet; +} + +bool SwGlossaryHdl::IsReadOnly( const OUString* pGrpNm ) const +{ + SwTextBlocks *pGlossary = nullptr; + + if (pGrpNm) + pGlossary = rStatGlossaries.GetGroupDoc( *pGrpNm ).release(); + else if (pCurGrp) + pGlossary = pCurGrp.get(); + else + pGlossary = rStatGlossaries.GetGroupDoc(aCurGrp).release(); + + const bool bRet = !pGlossary || pGlossary->IsReadOnly(); + if( pGrpNm || !pCurGrp ) + delete pGlossary; + return bRet; +} + +bool SwGlossaryHdl::IsOld() const +{ + if( !pCurGrp ) + rStatGlossaries.GetGroupDoc(aCurGrp).reset(); + return false; +} + +// find group without path index +bool SwGlossaryHdl::FindGroupName(OUString& rGroup) +{ + return rStatGlossaries.FindGroupName(rGroup); +} + +bool SwGlossaryHdl::CopyToClipboard(SwWrtShell& rSh, const OUString& rShortName) +{ + SwTextBlocks *pGlossary = pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc(aCurGrp).release(); + + rtl::Reference<SwTransferable> pTransfer = new SwTransferable( rSh ); + + bool bRet = pTransfer->CopyGlossary( *pGlossary, rShortName ); + if( !pCurGrp ) + delete pGlossary; + return bRet; +} + +bool SwGlossaryHdl::ImportGlossaries( const OUString& rName ) +{ + bool bRet = false; + if( !rName.isEmpty() ) + { + std::shared_ptr<const SfxFilter> pFilter; + std::unique_ptr<SfxMedium> pMed(new SfxMedium( rName, StreamMode::READ, nullptr, nullptr )); + SfxFilterMatcher aMatcher( "swriter" ); + pMed->UseInteractionHandler( true ); + if (!aMatcher.GuessFilter(*pMed, pFilter, SfxFilterFlags::NONE)) + { + SwTextBlocks *pGlossary = nullptr; + pMed->SetFilter( pFilter ); + Reader* pR = SwReaderWriter::GetReader( pFilter->GetUserData() ); + if( pR && nullptr != ( pGlossary = pCurGrp ? pCurGrp.get() + : rStatGlossaries.GetGroupDoc(aCurGrp).release()) ) + { + SwReader aReader( *pMed, rName ); + if( aReader.HasGlossaries( *pR ) ) + { + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + bRet = aReader.ReadGlossaries( *pR, *pGlossary, + rCfg.IsSaveRelFile() ); + } + + if (!pCurGrp) + delete pGlossary; + } + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx new file mode 100644 index 000000000..b4e91dcec --- /dev/null +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -0,0 +1,4349 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/MSOLEObjectSystemCreator.hpp> +#include <com/sun/star/text/XPasteListener.hpp> + +#include <svtools/embedtransfer.hxx> +#include <svtools/insdlg.hxx> +#include <unotools/tempfile.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/servicehelper.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/string.hxx> +#include <o3tl/deleter.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <sot/filelist.hxx> +#include <svx/svxdlg.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <osl/endian.h> +#include <sfx2/linkmgr.hxx> +#include <tools/urlobj.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <vcl/imap.hxx> +#include <sot/storage.hxx> +#include <vcl/graph.hxx> +#include <svl/urihelper.hxx> +#include <svx/svdmodel.hxx> +#include <svx/xmlexchg.hxx> +#include <svx/dbaexchange.hxx> +#include <svx/clipfmtitem.hxx> +#include <sfx2/mieclip.hxx> +#include <svl/urlbmk.hxx> +#include <vcl/inetimg.hxx> +#include <svx/fmview.hxx> +#include <sfx2/docfilt.hxx> +#include <vcl/imapobj.hxx> +#include <sfx2/docfile.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/graphicfilter.hxx> + +#ifdef _WIN32 +#include <prewin.h> +#include <postwin.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <osl/file.hxx> +#endif + +#include <svx/unomodel.hxx> +#include <fmturl.hxx> +#include <fmtinfmt.hxx> +#include <swdtflvr.hxx> +#include <shellio.hxx> +#include <ddefld.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <IMark.hxx> +#include <section.hxx> +#include <ndtxt.hxx> +#include <edtdd.hxx> +#include <edtwin.hxx> +#include <navicont.hxx> +#include <swcont.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <wdocsh.hxx> +#include <fldbas.hxx> +#include <swundo.hxx> +#include <pam.hxx> +#include <ndole.hxx> +#include <swwait.hxx> +#include <viewopt.hxx> +#include <SwCapObjType.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <svx/svditer.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <svx/svdpage.hxx> +#include <avmedia/mediawindow.hxx> +#include <swcrsr.hxx> +#include <SwRewriter.hxx> +#include <vcl/svapp.hxx> +#include <swserv.hxx> +#include <fmtmeta.hxx> +#include <itabenum.hxx> +#include <iodetect.hxx> +#include <unotextrange.hxx> +#include <unoframe.hxx> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +#include <vcl/GraphicNativeTransform.hxx> +#include <vcl/GraphicNativeMetadata.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/classificationhelper.hxx> +#include <sfx2/sfxdlg.hxx> +#include <comphelper/classids.hxx> + +#include <memory> + +#define OLESIZE 11905 - 2 * lMinBorder, 6 * MM50 + +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DRAWMODEL = 0x00000001; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_HTML = 0x00000002; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_RTF = 0x00000004; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_STRING = 0x00000008; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_SWOLE = 0x00000010; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DDE = 0x00000020; +constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_RICHTEXT = 0x00000040; + +using namespace ::svx; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::datatransfer; +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); +} + +} + +#define DDE_TXT_ENCODING osl_getThreadTextEncoding() + +namespace { + +class SwTransferDdeLink : public ::sfx2::SvBaseLink +{ + OUString sName; + ::sfx2::SvLinkSourceRef refObj; + SwTransferable& rTrnsfr; + SwDocShell* pDocShell; + sal_uLong nOldTimeOut; + bool bDelBookmrk : 1; + bool bInDisconnect : 1; + + bool FindDocShell(); + + using sfx2::SvBaseLink::Disconnect; + +protected: + virtual ~SwTransferDdeLink() override; + +public: + SwTransferDdeLink( SwTransferable& rTrans, SwWrtShell& rSh ); + + virtual ::sfx2::SvBaseLink::UpdateResult DataChanged( + const OUString& rMimeType, const css::uno::Any & rValue ) override; + virtual void Closed() override; + + bool WriteData( SvStream& rStrm ); + + void Disconnect( bool bRemoveDataAdvise ); +}; + +} + +/// Tracks the boundaries of pasted content and notifies listeners. +class SwPasteContext +{ +public: + SwPasteContext(SwWrtShell& rWrtShell); + ~SwPasteContext(); + + void remember(); + void forget(); + +private: + SwWrtShell& m_rWrtShell; + std::unique_ptr<SwPaM> m_pPaM; + sal_Int32 m_nStartContent = 0; +}; + +namespace { + +// helper class for Action and Undo enclosing +class SwTrnsfrActionAndUndo +{ + SwWrtShell *pSh; +public: + SwTrnsfrActionAndUndo( SwWrtShell *pS, bool bDelSel = false, SwPasteContext* pContext = nullptr) + : pSh( pS ) + { + pSh->StartUndo( SwUndoId::PASTE_CLIPBOARD ); + if( bDelSel ) + { + if (pContext) + pContext->forget(); + pSh->DelRight(); + if (pContext) + pContext->remember(); + } + pSh->StartAllAction(); + } + ~SwTrnsfrActionAndUndo() COVERITY_NOEXCEPT_FALSE + { + pSh->EndUndo(); + pSh->EndAllAction(); + } +}; + +} + +SwTransferable::SwTransferable( SwWrtShell& rSh ) + : m_pWrtShell( &rSh ), + m_pCreatorView( nullptr ), + m_pOrigGraphic( nullptr ), + m_eBufferType( TransferBufferType::NONE ) +{ + rSh.GetView().AddTransferable(*this); + SwDocShell* pDShell = rSh.GetDoc()->GetDocShell(); + if( pDShell ) + { + pDShell->FillTransferableObjectDescriptor( m_aObjDesc ); + if( pDShell->GetMedium() ) + { + const INetURLObject& rURLObj = pDShell->GetMedium()->GetURLObject(); + m_aObjDesc.maDisplayName = URIHelper::removePassword( + rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ); + } + + PrepareOLE( m_aObjDesc ); + } +} + +SwTransferable::~SwTransferable() +{ + SolarMutexGuard aSolarGuard; + + // the DDELink still needs the WrtShell! + if( m_xDdeLink.is() ) + { + static_cast<SwTransferDdeLink*>( m_xDdeLink.get() )->Disconnect( true ); + m_xDdeLink.clear(); + } + + m_pWrtShell = nullptr; + + // release reference to the document so that aDocShellRef will delete + // it (if aDocShellRef is set). Otherwise, the OLE nodes keep references + // to their sub-storage when the storage is already dead. + m_pClpDocFac.reset(); + + // first close, then the Ref. can be cleared as well, so that + // the DocShell really gets deleted! + if( m_aDocShellRef.Is() ) + { + SfxObjectShell * pObj = m_aDocShellRef; + SwDocShell* pDocSh = static_cast<SwDocShell*>(pObj); + pDocSh->DoClose(); + } + m_aDocShellRef.Clear(); + + SwModule* pMod = SW_MOD(); + if(pMod) + { + if ( pMod->m_pDragDrop == this ) + pMod->m_pDragDrop = nullptr; + else if ( pMod->m_pXSelection == this ) + pMod->m_pXSelection = nullptr; + } + + m_eBufferType = TransferBufferType::NONE; +} + +static SwDoc * lcl_GetDoc(SwDocFac & rDocFac) +{ + SwDoc *const pDoc = rDocFac.GetDoc(); + OSL_ENSURE( pDoc, "Document not found" ); + if (pDoc) + { + pDoc->SetClipBoard( true ); + } + return pDoc; +} + +void SwTransferable::ObjectReleased() +{ + SwModule *pMod = SW_MOD(); + if( this == pMod->m_pDragDrop ) + pMod->m_pDragDrop = nullptr; + else if( this == pMod->m_pXSelection ) + pMod->m_pXSelection = nullptr; +} + +void SwTransferable::AddSupportedFormats() +{ + // only need if we are the current XSelection Object + SwModule *pMod = SW_MOD(); + if( this == pMod->m_pXSelection || comphelper::LibreOfficeKit::isActive()) + { + SetDataForDragAndDrop( Point( 0,0) ); + } +} + +void SwTransferable::InitOle( SfxObjectShell* pDoc ) +{ + //set OleVisArea. Upper left corner of the page and size of + //RealSize in Twips. + const Size aSz( OLESIZE ); + SwRect aVis( Point( DOCUMENTBORDER, DOCUMENTBORDER ), aSz ); + pDoc->SetVisArea( aVis.SVRect() ); +} + +uno::Reference < embed::XEmbeddedObject > SwTransferable::FindOLEObj( sal_Int64& nAspect ) const +{ + uno::Reference < embed::XEmbeddedObject > xObj; + if( m_pClpDocFac ) + { + SwIterator<SwContentNode,SwFormatColl> aIter( *m_pClpDocFac->GetDoc()->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + if( SwNodeType::Ole == pNd->GetNodeType() ) + { + xObj = static_cast<SwOLENode*>(pNd)->GetOLEObj().GetOleRef(); + nAspect = static_cast<SwOLENode*>(pNd)->GetAspect(); + break; + } + } + return xObj; +} + +const Graphic* SwTransferable::FindOLEReplacementGraphic() const +{ + if( m_pClpDocFac ) + { + SwIterator<SwContentNode,SwFormatColl> aIter( *m_pClpDocFac->GetDoc()->GetDfltGrfFormatColl() ); + for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) + if( SwNodeType::Ole == pNd->GetNodeType() ) + { + return static_cast<SwOLENode*>(pNd)->GetGraphic(); + } + } + + return nullptr; +} + +void SwTransferable::RemoveDDELinkFormat( const vcl::Window& rWin ) +{ + RemoveFormat( SotClipboardFormatId::LINK ); + CopyToClipboard( const_cast<vcl::Window*>(&rWin) ); +} + +namespace +{ + //Resolves: fdo#40717 surely when we create a clipboard document we should + //overwrite the clipboard documents styles and settings with that of the + //source, so that we can WYSIWYG paste. If we want that the destinations + //styles are used over the source styles, that's a matter of the + //destination paste code to handle, not the source paste code. + void lclOverWriteDoc(SwWrtShell &rSrcWrtShell, SwDoc &rDest) + { + const SwDoc &rSrc = *rSrcWrtShell.GetDoc(); + + rDest.ReplaceCompatibilityOptions(rSrc); + rDest.ReplaceDefaults(rSrc); + + //It would probably make most sense here to only insert the styles used + //by the selection, e.g. apply SwDoc::IsUsed on styles ? + rDest.ReplaceStyles(rSrc, false); + + rSrcWrtShell.Copy(&rDest); + + rDest.GetMetaFieldManager().copyDocumentProperties(rSrc); + } + + void lclCheckAndPerformRotation(Graphic& aGraphic) + { + GraphicNativeMetadata aMetadata; + if ( aMetadata.read(aGraphic) ) + { + sal_uInt16 aRotation = aMetadata.getRotation(); + if (aRotation != 0) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/queryrotateintostandarddialog.ui")); + std::unique_ptr<weld::MessageDialog> xQueryBox(xBuilder->weld_message_dialog("QueryRotateIntoStandardOrientationDialog")); + if (xQueryBox->run() == RET_YES) + { + GraphicNativeTransform aTransform( aGraphic ); + aTransform.rotate( aRotation ); + } + } + } + } +} + +sal_Bool SAL_CALL SwTransferable::isComplex() +{ + // Copy into a new Doc so we don't mess with the existing one. + //FIXME: We *should* be able to avoid this and improve the performance. + m_pClpDocFac.reset(new SwDocFac); + SwDoc* const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); + + pTmpDoc->getIDocumentFieldsAccess() + .LockExpFields(); // never update fields - leave text as it is + lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); + + sal_Int32 nTextLength = 0; + const SwNode* pEndOfContent = &m_pWrtShell->GetDoc()->GetNodes().GetEndOfContent(); + SwNodes& aNodes = pTmpDoc->GetNodes(); + for( sal_uLong nIndex = 0; nIndex < aNodes.Count(); ++nIndex) + { + SwNode& rNd = *aNodes[nIndex]; + if (&rNd == pEndOfContent) + break; + + if (rNd.IsOLENode() || rNd.IsGrfNode()) + return true; // Complex + + SwTextNode* pTextNode = rNd.GetTextNode(); + if (pTextNode) + { + nTextLength += pTextNode->GetText().getLength(); + if (nTextLength >= 1024 * 512) + return true; // Complex + } + } + + if (m_pWrtShell->GetSelectionType() == SelectionType::DrawObject) + return true; // Complex + + // Simple + return false; +} + +bool SwTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + + // we can only fulfil the request if + // 1) we have data for this format + // 2) we have either a clipboard document (pClpDocFac), or + // we have a SwWrtShell (so we can generate a new clipboard document) + if( !HasFormat( nFormat ) || ( m_pClpDocFac == nullptr && m_pWrtShell == nullptr ) ) + return false; + + if( !m_pClpDocFac ) + { + SelectionType nSelectionType = m_pWrtShell->GetSelectionType(); + + // when pending we will not get the correct type, but SelectionType::Text + // as fallback. This *happens* during D&D, so we need to check if we are in + // the fallback and just try to get a graphic + const bool bPending(m_pWrtShell->ActionPend()); + + // SEL_GRF is from ContentType of editsh + if(bPending || ((SelectionType::Graphic | SelectionType::DbForm) & nSelectionType)) + { + m_pClpGraphic.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_pClpGraphic )) + m_pOrigGraphic = m_pClpGraphic.get(); + m_pClpBitmap.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_pClpBitmap )) + m_pOrigGraphic = m_pClpBitmap.get(); + + // is it a URL-Button ? + OUString sURL; + OUString sDesc; + if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) + { + m_pBookmark.reset(new INetBookmark( sURL, sDesc )); + m_eBufferType = TransferBufferType::InetField; + } + } + + m_pClpDocFac.reset(new SwDocFac); + SwDoc *const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); + + pTmpDoc->getIDocumentFieldsAccess().LockExpFields(); // never update fields - leave text as it is + lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); + + // in CORE a new one was created (OLE-objects copied!) + m_aDocShellRef = pTmpDoc->GetTmpDocShell(); + if( m_aDocShellRef.Is() ) + SwTransferable::InitOle( m_aDocShellRef ); + pTmpDoc->SetTmpDocShell( nullptr ); + + if( nSelectionType & SelectionType::Text && !m_pWrtShell->HasMark() ) + { + SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); + + Point aPos( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY()); + + bool bSelect = g_bExecuteDrag && + m_pWrtShell->GetView().GetDocShell() && + !m_pWrtShell->GetView().GetDocShell()->IsReadOnly(); + if( m_pWrtShell->GetContentAtPos( aPos, aContentAtPos, bSelect ) ) + { + m_pBookmark.reset(new INetBookmark( + static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)->GetValue(), + aContentAtPos.sStr )); + m_eBufferType = TransferBufferType::InetField; + if( bSelect ) + m_pWrtShell->SelectTextAttr( RES_TXTATR_INETFMT ); + } + } + if( m_pWrtShell->IsFrameSelected() ) + { + SfxItemSet aSet( m_pWrtShell->GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + m_pWrtShell->GetFlyFrameAttr( aSet ); + const SwFormatURL& rURL = aSet.Get( RES_URL ); + if( rURL.GetMap() ) + m_pImageMap.reset(new ImageMap( *rURL.GetMap() )); + else if( !rURL.GetURL().isEmpty() ) + m_pTargetURL.reset(new INetImage(OUString(), rURL.GetURL(), + rURL.GetTargetFrameName() )); + } + } + + bool bOK = false; + if( TransferBufferType::Ole == m_eBufferType ) + { + //TODO/MBA: testing - is this the "single OLE object" case?! + // get OLE-Object from ClipDoc and get the data from that. + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; // will be set in the next statement + uno::Reference < embed::XEmbeddedObject > xObj = FindOLEObj( nAspect ); + const Graphic* pOLEGraph = FindOLEReplacementGraphic(); + if( xObj.is() ) + { + TransferableDataHelper aD( new SvEmbedTransferHelper( xObj, pOLEGraph, nAspect ) ); + uno::Any aAny = aD.GetAny(rFlavor, rDestDoc); + if( aAny.hasValue() ) + bOK = SetAny( aAny ); + } + + // the following solution will be used in the case when the object can not generate the image + // TODO/LATER: in future the transferhelper must probably be created based on object and the replacement stream + // TODO: Block not required now, SvEmbedTransferHelper should be able to handle GDIMetaFile format + if ( nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + pOLEGraph = FindOLEReplacementGraphic(); + if ( pOLEGraph ) + bOK = SetGDIMetaFile( pOLEGraph->GetGDIMetaFile() ); + } + } + else + { + switch( nFormat ) + { + case SotClipboardFormatId::LINK: + if( m_xDdeLink.is() ) + bOK = SetObject( m_xDdeLink.get(), SWTRANSFER_OBJECTTYPE_DDE, rFlavor ); + break; + + case SotClipboardFormatId::OBJECTDESCRIPTOR: + case SotClipboardFormatId::LINKSRCDESCRIPTOR: + bOK = SetTransferableObjectDescriptor( m_aObjDesc ); + break; + + case SotClipboardFormatId::DRAWING: + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + bOK = SetObject( pDoc->getIDocumentDrawModelAccess().GetDrawModel(), + SWTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor ); + } + break; + + case SotClipboardFormatId::STRING: + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + bOK = SetObject( pDoc, SWTRANSFER_OBJECTTYPE_STRING, rFlavor ); + } + break; + case SotClipboardFormatId::RTF: + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + bOK = SetObject( pDoc, SWTRANSFER_OBJECTTYPE_RTF, rFlavor ); + } + break; + case SotClipboardFormatId::RICHTEXT: + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + bOK = SetObject( pDoc, SWTRANSFER_OBJECTTYPE_RICHTEXT, rFlavor ); + } + break; + + case SotClipboardFormatId::HTML: + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + bOK = SetObject( pDoc, SWTRANSFER_OBJECTTYPE_HTML, rFlavor ); + } + break; + + case SotClipboardFormatId::SVXB: + if( m_eBufferType & TransferBufferType::Graphic && m_pOrigGraphic ) + bOK = SetGraphic( *m_pOrigGraphic ); + break; + + case SotClipboardFormatId::GDIMETAFILE: + if( m_eBufferType & TransferBufferType::Graphic ) + bOK = SetGDIMetaFile( m_pClpGraphic->GetGDIMetaFile() ); + break; + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::PNG: + // Neither pClpBitmap nor pClpGraphic are necessarily set + if( (m_eBufferType & TransferBufferType::Graphic) && (m_pClpBitmap != nullptr || m_pClpGraphic != nullptr)) + bOK = SetBitmapEx( (m_pClpBitmap ? m_pClpBitmap : m_pClpGraphic)->GetBitmapEx(), rFlavor ); + break; + + case SotClipboardFormatId::SVIM: + if( m_pImageMap ) + bOK = SetImageMap( *m_pImageMap ); + break; + + case SotClipboardFormatId::INET_IMAGE: + if( m_pTargetURL ) + bOK = SetINetImage( *m_pTargetURL, rFlavor ); + break; + + case SotClipboardFormatId::SOLK: + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + case SotClipboardFormatId::FILEGRPDESCRIPTOR: + case SotClipboardFormatId::FILECONTENT: + case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: + case SotClipboardFormatId::SIMPLE_FILE: + if( (TransferBufferType::InetField & m_eBufferType) && m_pBookmark ) + bOK = SetINetBookmark( *m_pBookmark, rFlavor ); + break; + + case SotClipboardFormatId::EMBED_SOURCE: + if( !m_aDocShellRef.Is() ) + { + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + SwDocShell* pNewDocSh = new SwDocShell( pDoc, + SfxObjectCreateMode::EMBEDDED ); + m_aDocShellRef = pNewDocSh; + m_aDocShellRef->DoInitNew(); + SwTransferable::InitOle( m_aDocShellRef ); + } + bOK = SetObject( &m_aDocShellRef, SWTRANSFER_OBJECTTYPE_SWOLE, + rFlavor ); + break; + default: break; + } + } + return bOK; +} + +bool SwTransferable::WriteObject( tools::SvRef<SotStorageStream>& xStream, + void* pObject, sal_uInt32 nObjectType, + const DataFlavor& /*rFlavor*/ ) +{ + bool bRet = false; + WriterRef xWrt; + + switch( nObjectType ) + { + case SWTRANSFER_OBJECTTYPE_DRAWMODEL: + { + // don't change the sequence of commands + SdrModel *pModel = static_cast<SdrModel*>(pObject); + xStream->SetBufferSize( 16348 ); + + // for the changed pool defaults from drawing layer pool set those + // attributes as hard attributes to preserve them for saving + const SfxItemPool& rItemPool = pModel->GetItemPool(); + const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem(EE_CHAR_FONTHEIGHT); + + // SW should have no MasterPages + OSL_ENSURE(0 == pModel->GetMasterPageCount(), "SW with MasterPages (!)"); + + for(sal_uInt16 a(0); a < pModel->GetPageCount(); a++) + { + const SdrPage* pPage = pModel->GetPage(a); + SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + const SvxFontHeightItem& rItem = pObj->GetMergedItem(EE_CHAR_FONTHEIGHT); + + if(rItem.GetHeight() == rDefaultFontHeight.GetHeight()) + { + pObj->SetMergedItem(rDefaultFontHeight); + } + } + } + + { + uno::Reference<io::XOutputStream> xDocOut( new utl::OOutputStreamWrapper( *xStream ) ); + if( SvxDrawingLayerExport( pModel, xDocOut ) ) + xStream->Commit(); + } + + bRet = ERRCODE_NONE == xStream->GetError(); + } + break; + + case SWTRANSFER_OBJECTTYPE_SWOLE: + { + SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pObject); + try + { + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + // mba: no BaseURL for clipboard + SfxMedium aMedium( xWorkStore, OUString() ); + pEmbObj->DoSaveObjectAs( aMedium, false ); + pEmbObj->DoSaveCompleted(); + + uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + std::unique_ptr<SvStream> pSrcStm(::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ )); + if( pSrcStm ) + { + xStream->SetBufferSize( 0xff00 ); + xStream->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + xWorkStore->dispose(); + xWorkStore.clear(); + xStream->Commit(); + } + catch (const uno::Exception&) + { + } + + bRet = ( xStream->GetError() == ERRCODE_NONE ); + } + break; + + case SWTRANSFER_OBJECTTYPE_DDE: + { + xStream->SetBufferSize( 1024 ); + SwTransferDdeLink* pDdeLnk = static_cast<SwTransferDdeLink*>(pObject); + if( pDdeLnk->WriteData( *xStream ) ) + { + xStream->Commit(); + bRet = ERRCODE_NONE == xStream->GetError(); + } + } + break; + + case SWTRANSFER_OBJECTTYPE_HTML: + { + // LOK is interested in getting images embedded for copy/paste support. + const OUString aFilterOptions("EmbedImages"); + GetHTMLWriter( comphelper::LibreOfficeKit::isActive() ? aFilterOptions : OUString(), OUString(), xWrt ); + break; + } + + case SWTRANSFER_OBJECTTYPE_RTF: + case SWTRANSFER_OBJECTTYPE_RICHTEXT: + GetRTFWriter(OUString(), OUString(), xWrt); + break; + + case SWTRANSFER_OBJECTTYPE_STRING: + GetASCWriter(OUString(), OUString(), xWrt); + if( xWrt.is() ) + { + SwAsciiOptions aAOpt; + aAOpt.SetCharSet( RTL_TEXTENCODING_UTF8 ); + xWrt->SetAsciiOptions( aAOpt ); + + // no start char for clipboard + xWrt->m_bUCS2_WithStartChar = false; + } + break; + default: break; + } + + if( xWrt.is() ) + { + SwDoc* pDoc = static_cast<SwDoc*>(pObject); + xWrt->m_bWriteClipboardDoc = true; + xWrt->m_bWriteOnlyFirstTable = bool(TransferBufferType::Table & m_eBufferType); + xWrt->SetShowProgress(false); + +#if defined(DEBUGPASTE) + SvFileStream aPasteDebug(OUString( + "PASTEBUFFER.debug"), StreamMode::WRITE|StreamMode::TRUNC); + SwWriter aDbgWrt( aPasteDebug, *pDoc ); + aDbgWrt.Write( xWrt ); +#endif + + SwWriter aWrt( *xStream, *pDoc ); + if( ! aWrt.Write( xWrt ).IsError() ) + { + xStream->WriteChar( '\0' ); // terminate with a zero + xStream->Commit(); + bRet = true; + } + } + + return bRet; +} + +int SwTransferable::Cut() +{ + int nRet = Copy( true ); + if( nRet ) + DeleteSelection(); + collectUIInformation("CUT", "parameter"); + return nRet; +} + +void SwTransferable::DeleteSelection() +{ + if(!m_pWrtShell) + return; + // ask for type of selection before action-bracketing + const SelectionType nSelection = m_pWrtShell->GetSelectionType(); + // cut rows or columns selected by enhanced table selection and wholly selected tables + bool bCutMode = ( SelectionType::TableCell & nSelection ) && ( (SelectionType::TableRow | SelectionType::TableCol) & nSelection || + m_pWrtShell->HasWholeTabSelection() ); + + m_pWrtShell->StartUndo( SwUndoId::START ); + if( bCutMode ) + { + if( !(SelectionType::TableCol & nSelection) ) + m_pWrtShell->DeleteTable(); + else + { + SfxDispatcher* pDispatch = m_pWrtShell->GetView().GetViewFrame()->GetDispatcher(); + pDispatch->Execute(FN_TABLE_DELETE_COL, SfxCallMode::SYNCHRON); + } + } + else + { + if( ( SelectionType::Text | SelectionType::Table ) & nSelection ) + m_pWrtShell->IntelligentCut( nSelection ); + m_pWrtShell->DelRight(); + } + m_pWrtShell->EndUndo( SwUndoId::END ); +} + +int SwTransferable::PrepareForCopy( bool bIsCut ) +{ + int nRet = 1; + if(!m_pWrtShell) + return 0; + + if ( m_pWrtShell->GetTableInsertMode() != SwTable::SEARCH_NONE ) + m_pWrtShell->SetTableInsertMode( SwTable::SEARCH_NONE ); + + if ( m_pWrtShell->GetTableCopied() ) + m_pWrtShell->SetTableCopied( false ); + + OUString sGrfNm; + const SelectionType nSelection = m_pWrtShell->GetSelectionType(); + if( nSelection == SelectionType::Graphic ) + { + m_pClpGraphic.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_pClpGraphic )) + m_pOrigGraphic = m_pClpGraphic.get(); + m_pClpBitmap.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_pClpBitmap )) + m_pOrigGraphic = m_pClpBitmap.get(); + + m_pClpDocFac.reset(new SwDocFac); + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + m_pWrtShell->Copy( pDoc ); + +#if HAVE_FEATURE_DESKTOP + if (m_pOrigGraphic && !m_pOrigGraphic->GetBitmapEx().IsEmpty()) + AddFormat( SotClipboardFormatId::SVXB ); +#endif + + PrepareOLE( m_aObjDesc ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + + const Graphic* pGrf = m_pWrtShell->GetGraphic(); + if( pGrf && pGrf->IsSupportedGraphic() ) + { + AddFormat( SotClipboardFormatId::PNG ); +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::BITMAP ); +#endif + } + m_eBufferType = TransferBufferType::Graphic; + m_pWrtShell->GetGrfNms( &sGrfNm, nullptr ); + } + else if ( nSelection == SelectionType::Ole ) + { + m_pClpDocFac.reset(new SwDocFac); + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + m_aDocShellRef = new SwDocShell( pDoc, SfxObjectCreateMode::EMBEDDED); + m_aDocShellRef->DoInitNew(); + m_pWrtShell->Copy( pDoc ); + + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + + // --> OD #i98753# + // set size of embedded object at the object description structure + m_aObjDesc.maSize = OutputDevice::LogicToLogic(m_pWrtShell->GetObjSize(), MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM)); + // <-- + PrepareOLE( m_aObjDesc ); + +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + + // Fetch the formats supported via embedtransferhelper as well + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + uno::Reference < embed::XEmbeddedObject > xObj = FindOLEObj( nAspect ); + const Graphic* pOLEGraph = FindOLEReplacementGraphic(); + if( xObj.is() ) + { + TransferableDataHelper aD( new SvEmbedTransferHelper( xObj, pOLEGraph, nAspect ) ); + if ( aD.GetTransferable().is() ) + { + DataFlavorExVector aVector( aD.GetDataFlavorExVector() ); + + for( const auto& rItem : aVector ) + AddFormat( rItem ); + } + } +#endif + m_eBufferType = TransferBufferType::Ole; + } + // Is there anything to provide anyway? + else if ( m_pWrtShell->IsSelection() || m_pWrtShell->IsFrameSelected() || + m_pWrtShell->IsObjSelected() ) + { + std::unique_ptr<SwWait> pWait; + if( m_pWrtShell->ShouldWait() ) + pWait.reset(new SwWait( *m_pWrtShell->GetView().GetDocShell(), true )); + + m_pClpDocFac.reset(new SwDocFac); + + // create additional cursor so that equal treatment of keyboard + // and mouse selection is possible. + // In AddMode with keyboard selection, the new cursor is not created + // before the cursor is moved after end of selection. + if( m_pWrtShell->IsAddMode() && m_pWrtShell->SwCursorShell::HasSelection() ) + m_pWrtShell->CreateCursor(); + + SwDoc *const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); + + pTmpDoc->getIDocumentFieldsAccess().LockExpFields(); // Never update fields - leave text as is + lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); + + { + IDocumentMarkAccess* const pMarkAccess = pTmpDoc->getIDocumentMarkAccess(); + std::vector< ::sw::mark::IMark* > vDdeMarks; + // find all DDE-Bookmarks + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); + ppMark != pMarkAccess->getAllMarksEnd(); + ++ppMark) + { + if(IDocumentMarkAccess::MarkType::DDE_BOOKMARK == IDocumentMarkAccess::GetType(**ppMark)) + vDdeMarks.push_back(*ppMark); + } + // remove all DDE-Bookmarks, they are invalid inside the clipdoc! + for(const auto& rpMark : vDdeMarks) + pMarkAccess->deleteMark(rpMark); + } + + // a new one was created in CORE (OLE objects copied!) + m_aDocShellRef = pTmpDoc->GetTmpDocShell(); + if( m_aDocShellRef.Is() ) + SwTransferable::InitOle( m_aDocShellRef ); + pTmpDoc->SetTmpDocShell( nullptr ); + + if( m_pWrtShell->IsObjSelected() ) + m_eBufferType = TransferBufferType::Drawing; + else + { + m_eBufferType = TransferBufferType::Document; + if (m_pWrtShell->IntelligentCut(nSelection, false) != SwWrtShell::NO_WORD) + m_eBufferType = TransferBufferType::DocumentWord | m_eBufferType; + } + + bool bDDELink = m_pWrtShell->IsSelection(); + if( nSelection & SelectionType::TableCell ) + { + m_eBufferType = TransferBufferType::Table | m_eBufferType; + bDDELink = m_pWrtShell->HasWholeTabSelection(); + + m_pWrtShell->SetTableCopied(true); + + if ( bIsCut && (SelectionType::TableRow | SelectionType::TableCol) & nSelection ) + m_pWrtShell->SetTableInsertMode( (SelectionType::TableRow & nSelection) ? SwTable::SEARCH_ROW : SwTable::SEARCH_COL ); + } + +#if HAVE_FEATURE_DESKTOP + //When someone needs it, we 'OLE' him something + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); +#endif + + //put RTF ahead of the OLE's Metafile to have less loss + if( !m_pWrtShell->IsObjSelected() ) + { + AddFormat( SotClipboardFormatId::RTF ); +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::RICHTEXT ); + AddFormat( SotClipboardFormatId::HTML ); +#endif + } + if( m_pWrtShell->IsSelection() ) + AddFormat( SotClipboardFormatId::STRING ); + + if( nSelection & ( SelectionType::DrawObject | SelectionType::DbForm )) + { + AddFormat( SotClipboardFormatId::DRAWING ); + if ( nSelection & SelectionType::DrawObject ) + { +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::BITMAP ); +#endif + AddFormat( SotClipboardFormatId::PNG ); + } + m_eBufferType = static_cast<TransferBufferType>( TransferBufferType::Graphic | m_eBufferType ); + + m_pClpGraphic.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_pClpGraphic )) + m_pOrigGraphic = m_pClpGraphic.get(); + m_pClpBitmap.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_pClpBitmap )) + m_pOrigGraphic = m_pClpBitmap.get(); + + // is it a URL-Button ? + OUString sURL; + OUString sDesc; + if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) + { + AddFormat( SotClipboardFormatId::STRING ); +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::FILECONTENT ); + AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); +#endif + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + m_eBufferType = TransferBufferType::InetField | m_eBufferType; + nRet = 1; + } + } + + // at Cut, DDE-Link doesn't make sense!! + SwDocShell* pDShell; + if( !bIsCut && bDDELink && + nullptr != ( pDShell = m_pWrtShell->GetDoc()->GetDocShell()) && + SfxObjectCreateMode::STANDARD == pDShell->GetCreateMode() ) + { +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::LINK ); +#endif + m_xDdeLink = new SwTransferDdeLink( *this, *m_pWrtShell ); + } + + //ObjectDescriptor was already filly from the old DocShell. + //Now adjust it. Thus in GetData the first query can still + //be answered with delayed rendering. + Size aSz( OLESIZE ); + m_aObjDesc.maSize = OutputDevice::LogicToLogic(aSz, MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM)); + + PrepareOLE( m_aObjDesc ); +#if HAVE_FEATURE_DESKTOP + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); +#endif + } + else + nRet = 0; + + if( m_pWrtShell->IsFrameSelected() ) + { + SfxItemSet aSet( m_pWrtShell->GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + m_pWrtShell->GetFlyFrameAttr( aSet ); + const SwFormatURL& rURL = aSet.Get( RES_URL ); + if( rURL.GetMap() ) + { + m_pImageMap.reset( new ImageMap( *rURL.GetMap() ) ); + AddFormat( SotClipboardFormatId::SVIM ); + } + else if( !rURL.GetURL().isEmpty() ) + { + m_pTargetURL.reset(new INetImage( sGrfNm, rURL.GetURL(), + rURL.GetTargetFrameName() )); + AddFormat( SotClipboardFormatId::INET_IMAGE ); + } + } + + return nRet; +} + +int SwTransferable::Copy( bool bIsCut ) +{ + if (m_pWrtShell->GetView().GetObjectShell()->isContentExtractionLocked()) + return 0; + + int nRet = PrepareForCopy( bIsCut ); + if ( nRet ) + { + CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); + } + + if( !bIsCut ){ + collectUIInformation("COPY", "parameter"); + } + + return nRet; +} + +void SwTransferable::CalculateAndCopy() +{ + if(!m_pWrtShell) + return; + SwWait aWait( *m_pWrtShell->GetView().GetDocShell(), true ); + + OUString aStr( m_pWrtShell->Calculate() ); + + m_pClpDocFac.reset(new SwDocFac); + SwDoc *const pDoc = lcl_GetDoc(*m_pClpDocFac); + m_pWrtShell->Copy(pDoc, & aStr); + m_eBufferType = TransferBufferType::Document; + AddFormat( SotClipboardFormatId::STRING ); + + CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); +} + +bool SwTransferable::CopyGlossary( SwTextBlocks& rGlossary, const OUString& rStr ) +{ + if(!m_pWrtShell) + return false; + SwWait aWait( *m_pWrtShell->GetView().GetDocShell(), true ); + + m_pClpDocFac.reset(new SwDocFac); + SwDoc *const pCDoc = lcl_GetDoc(*m_pClpDocFac); + + SwNodes& rNds = pCDoc->GetNodes(); + SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() ); + SwContentNode* pCNd = rNds.GoNext( &aNodeIdx ); // go to 1st ContentNode + SwPaM aPam( *pCNd ); + + pCDoc->getIDocumentFieldsAccess().LockExpFields(); // never update fields - leave text as it is + + pCDoc->InsertGlossary( rGlossary, rStr, aPam ); + + // a new one was created in CORE (OLE-Objects copied!) + m_aDocShellRef = pCDoc->GetTmpDocShell(); + if( m_aDocShellRef.Is() ) + SwTransferable::InitOle( m_aDocShellRef ); + pCDoc->SetTmpDocShell( nullptr ); + + m_eBufferType = TransferBufferType::Document; + + //When someone needs it, we 'OLE' her something. + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + AddFormat( SotClipboardFormatId::HTML ); + AddFormat( SotClipboardFormatId::STRING ); + + //ObjectDescriptor was already filled from the old DocShell. + //Now adjust it. Thus in GetData the first query can still + //be answered with delayed rendering. + Size aSz( OLESIZE ); + m_aObjDesc.maSize = OutputDevice::LogicToLogic(aSz, MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM)); + + PrepareOLE( m_aObjDesc ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + + CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); + + return true; +} + +static uno::Reference < XTransferable > * lcl_getTransferPointer ( uno::Reference < XTransferable > &xRef ) +{ + return &xRef; +} + +SwPasteContext::SwPasteContext(SwWrtShell& rWrtShell) + : m_rWrtShell(rWrtShell) +{ + remember(); +} + +void SwPasteContext::remember() +{ + if (m_rWrtShell.GetPasteListeners().getLength() == 0) + return; + + SwPaM* pCursor = m_rWrtShell.GetCursor(); + if (!pCursor) + return; + + // Set point to the previous node, so it is not moved. + const SwNodeIndex& rNodeIndex = pCursor->GetPoint()->nNode; + m_pPaM.reset(new SwPaM(rNodeIndex, rNodeIndex, 0, -1)); + m_nStartContent = pCursor->GetPoint()->nContent.GetIndex(); +} + +void SwPasteContext::forget() { m_pPaM.reset(); } + +SwPasteContext::~SwPasteContext() +{ + try + { + if (m_rWrtShell.GetPasteListeners().getLength() == 0) + return; + + beans::PropertyValue aPropertyValue; + + switch (m_rWrtShell.GetView().GetShellMode()) + { + case ShellMode::Graphic: + { + SwFrameFormat* pFormat = m_rWrtShell.GetFlyFrameFormat(); + if (!pFormat) + return; + + aPropertyValue.Name = "TextGraphicObject"; + aPropertyValue.Value + <<= SwXTextGraphicObject::CreateXTextGraphicObject(*pFormat->GetDoc(), pFormat); + break; + } + + default: + { + if (!m_pPaM) + return; + + SwPaM* pCursor = m_rWrtShell.GetCursor(); + if (!pCursor) + return; + + if (!pCursor->GetPoint()->nNode.GetNode().IsTextNode()) + // Non-text was pasted. + return; + + // Update mark after paste. + *m_pPaM->GetMark() = *pCursor->GetPoint(); + + // Restore point. + ++m_pPaM->GetPoint()->nNode; + SwNode& rNode = m_pPaM->GetNode(); + if (!rNode.IsTextNode()) + // Starting point is no longer text. + return; + + m_pPaM->GetPoint()->nContent.Assign(static_cast<SwContentNode*>(&rNode), + m_nStartContent); + + aPropertyValue.Name = "TextRange"; + const uno::Reference<text::XTextRange> xTextRange = SwXTextRange::CreateXTextRange( + *m_pPaM->GetDoc(), *m_pPaM->GetPoint(), m_pPaM->GetMark()); + aPropertyValue.Value <<= xTextRange; + break; + } + } + + if (aPropertyValue.Name.isEmpty()) + return; + + // Invoke the listeners. + uno::Sequence<beans::PropertyValue> aEvent{ aPropertyValue }; + + comphelper::OInterfaceIteratorHelper2 it(m_rWrtShell.GetPasteListeners()); + while (it.hasMoreElements()) + { + uno::Reference<text::XPasteListener> xListener(it.next(), UNO_QUERY); + if (xListener.is()) + xListener->notifyPasteEvent(aEvent); + } + } + catch (const uno::Exception& rException) + { + SAL_WARN("sw", + "SwPasteContext::~SwPasteContext: uncaught exception: " << rException.Message); + } +} + +bool SwTransferable::IsPaste( const SwWrtShell& rSh, + const TransferableDataHelper& rData ) +{ + // Check the common case first: We can always paste our own data! + // If _only_ the internal format can be pasted, this check will + // yield 'true', while the one below would give a (wrong) result 'false'. + + bool bIsPaste = ( GetSwTransferable( rData ) != nullptr ); + + // if it's not our own data, we need to have a closer look: + if( ! bIsPaste ) + { + // determine the proper paste action, and return true if we find one + uno::Reference<XTransferable> xTransferable( rData.GetXTransferable() ); + + SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); + sal_uInt16 nSourceOptions = + (( SotExchangeDest::DOC_TEXTFRAME == nDestination || + SotExchangeDest::SWDOC_FREE_AREA == nDestination || + SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || + SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) + ? EXCHG_IN_ACTION_COPY + : EXCHG_IN_ACTION_MOVE); + + SotClipboardFormatId nFormat; // output param for GetExchangeAction + sal_uInt8 nEventAction; // output param for GetExchangeAction + sal_uInt8 nAction = SotExchange::GetExchangeAction( + rData.GetDataFlavorExVector(), + nDestination, + nSourceOptions, /* ?? */ + EXCHG_IN_ACTION_DEFAULT, /* ?? */ + nFormat, nEventAction, SotClipboardFormatId::NONE, + lcl_getTransferPointer ( xTransferable ) ); + + // if we find a suitable action, we can paste! + bIsPaste = (EXCHG_INOUT_ACTION_NONE != nAction); + } + + return bIsPaste; +} + +void SwTransferable::SelectPasteFormat(TransferableDataHelper& rData, sal_uInt8& nAction, + SotClipboardFormatId& nFormat) +{ + if (nFormat != SotClipboardFormatId::RICHTEXT) + { + return; + } + + if (!rData.HasFormat(SotClipboardFormatId::EMBED_SOURCE)) + { + return; + } + + if (!rData.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + return; + } + + TransferableObjectDescriptor aObjDesc; + if (!rData.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc)) + { + return; + } + + if (aObjDesc.maClassName != SvGlobalName(SO3_SW_CLASSID)) + { + return; + } + + // At this point we know that we paste from Writer to Writer and the clipboard has the content + // in both RTF and ODF formats. Prefer ODF in this case. + nAction = EXCHG_OUT_ACTION_INSERT_OLE; + nFormat = SotClipboardFormatId::EMBED_SOURCE; +} + +bool SwTransferable::Paste(SwWrtShell& rSh, TransferableDataHelper& rData, RndStdIds nAnchorType, bool bIgnoreComments, PasteTableType ePasteTable) +{ + SwPasteContext aPasteContext(rSh); + + sal_uInt8 nEventAction, nAction=0; + SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + SotExchangeActionFlags nActionFlags = SotExchangeActionFlags::NONE; + bool bSingleCellTable = false; + + if( GetSwTransferable( rData ) ) + { + nAction = EXCHG_OUT_ACTION_INSERT_PRIVATE; + } + else + { + sal_uInt16 nSourceOptions = + (( SotExchangeDest::DOC_TEXTFRAME == nDestination || + SotExchangeDest::SWDOC_FREE_AREA == nDestination || + SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || + SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) + ? EXCHG_IN_ACTION_COPY + : EXCHG_IN_ACTION_MOVE); + uno::Reference<XTransferable> xTransferable( rData.GetXTransferable() ); + nAction = SotExchange::GetExchangeAction( + rData.GetDataFlavorExVector(), + nDestination, + nSourceOptions, /* ?? */ + EXCHG_IN_ACTION_DEFAULT, /* ?? */ + nFormat, nEventAction, SotClipboardFormatId::NONE, + lcl_getTransferPointer ( xTransferable ), + &nActionFlags ); + } + + // tdf#37223 avoid non-native insertion of Calc worksheets in the following cases: + // content of 1-cell worksheets are inserted as simple text using RTF format, + // bigger worksheets within native (Writer) table cells are inserted as native tables, + // ie. cell by cell instead of embedding the worksheet in a single cell of the Writer table + if ( EXCHG_IN_ACTION_COPY == nAction && ( rData.HasFormat( SotClipboardFormatId::SYLK ) || + rData.HasFormat( SotClipboardFormatId::SYLK_BIGCAPS ) ) ) + { + // is it a 1-cell worksheet? + OUString aExpand; + if( rData.GetString( SotClipboardFormatId::STRING, aExpand )) + { + const sal_Int32 nNewlines{comphelper::string::getTokenCount(aExpand, '\n')}; + const sal_Int32 nRows = nNewlines ? nNewlines-1 : 0; + if ( nRows == 1 ) + { + const sal_Int32 nCols = comphelper::string::getTokenCount(aExpand.getToken(0, '\n'), '\t'); + if (nCols == 1) + bSingleCellTable = true; + } + } + + // convert the worksheet to a temporary native table using HTML format, and copy that into the original native table + if (!bSingleCellTable && rData.HasFormat( SotClipboardFormatId::HTML ) && + rSh.GetDoc()->IsIdxInTable(rSh.GetCursor()->GetNode()) != nullptr && rSh.DoesUndo()) + { + SfxDispatcher* pDispatch = rSh.GetView().GetViewFrame()->GetDispatcher(); + sal_uInt32 nLevel = 0; + // within Writer table cells, inserting worksheets using HTML format results only plain text, not a native table, + // so remove all outer nested tables temporary to get a working insertion point + // (RTF format has no such problem, but that inserts the hidden rows of the original Calc worksheet, too) + do + { + // insert a random character to redo the place of the insertion at the end + pDispatch->Execute(FN_INSERT_NNBSP, SfxCallMode::SYNCHRON); + pDispatch->Execute(FN_TABLE_DELETE_TABLE, SfxCallMode::SYNCHRON); + nLevel++; + } while (rSh.GetDoc()->IsIdxInTable(rSh.GetCursor()->GetNode()) != nullptr); + if ( SwTransferable::PasteData( rData, rSh, EXCHG_OUT_ACTION_INSERT_STRING, nActionFlags, SotClipboardFormatId::HTML, + nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext, ePasteTable) ) + { + pDispatch->Execute(FN_CHAR_LEFT, SfxCallMode::SYNCHRON); + pDispatch->Execute(FN_TABLE_SELECT_ALL, SfxCallMode::SYNCHRON); + pDispatch->Execute(SID_COPY, SfxCallMode::SYNCHRON); + for(sal_uInt32 a = 0; a < 1 + (nLevel * 2); a++) + pDispatch->Execute(SID_UNDO, SfxCallMode::SYNCHRON); + if (ePasteTable == PasteTableType::PASTE_TABLE) + pDispatch->Execute(FN_PASTE_NESTED_TABLE, SfxCallMode::SYNCHRON); + else if (ePasteTable == PasteTableType::PASTE_ROW) + pDispatch->Execute(FN_TABLE_PASTE_ROW_BEFORE, SfxCallMode::SYNCHRON); + else if (ePasteTable == PasteTableType::PASTE_COLUMN) + pDispatch->Execute(FN_TABLE_PASTE_COL_BEFORE, SfxCallMode::SYNCHRON); + else + pDispatch->Execute(SID_PASTE, SfxCallMode::SYNCHRON); + return true; + } else { + for(sal_uInt32 a = 0; a < (nLevel * 2); a++) + pDispatch->Execute(SID_UNDO, SfxCallMode::SYNCHRON); + } + } + } + // insert clipboard content as new table rows/columns before the actual row/column instead of overwriting it + else if ( (rSh.GetTableInsertMode() != SwTable::SEARCH_NONE || ePasteTable == PasteTableType::PASTE_ROW || ePasteTable == PasteTableType::PASTE_COLUMN) && + rData.HasFormat( SotClipboardFormatId::HTML ) && + rSh.GetDoc()->IsIdxInTable(rSh.GetCursor()->GetNode()) != nullptr ) + { + OUString aExpand; + sal_Int32 nIdx; + bool bRowMode = rSh.GetTableInsertMode() == SwTable::SEARCH_ROW || ePasteTable == PasteTableType::PASTE_ROW; + if( rData.GetString( SotClipboardFormatId::HTML, aExpand ) && (nIdx = aExpand.indexOf("<table")) > -1 ) + { + // table rows with span use also tbody + bool bShifted = aExpand.indexOf("<tbody>") > -1; + // calculate count of selected rows or columns + sal_Int32 nSelectedRowsOrCols = 0; + const OUString sSearchRowOrCol = bRowMode ? OUString("</tr>") : OUString("<col "); + while((nIdx = aExpand.indexOf(sSearchRowOrCol, nIdx)) > -1) + { + // skip rows/columns of nested tables, based on HTML indentation + if (nIdx > 2 && (aExpand[nIdx-1] != '\t' || aExpand[nIdx-2] != '\t' || (bShifted && aExpand[nIdx-3] != '\t'))) + ++nSelectedRowsOrCols; + ++nIdx; + } + // are we at the beginning of the cell? + bool bStartTableBoxNode = + // first paragraph of the cell? + rSh.GetCursor()->GetNode().GetIndex() == rSh.GetCursor()->GetNode().FindTableBoxStartNode()->GetIndex()+1 && + // beginning of the paragraph? + !rSh.GetCursor()->GetPoint()->nContent.GetIndex(); + SfxDispatcher* pDispatch = rSh.GetView().GetViewFrame()->GetDispatcher(); + + // go start of the cell + if (!bStartTableBoxNode) + pDispatch->Execute(FN_START_OF_DOCUMENT, SfxCallMode::SYNCHRON); + + // store cursor position in row mode + ::sw::mark::IMark* pMark = (!bRowMode || nSelectedRowsOrCols == 0) ? nullptr : rSh.SetBookmark( + vcl::KeyCode(), + OUString(), + IDocumentMarkAccess::MarkType::UNO_BOOKMARK ); + + // add a new empty row/column before the actual table row/column and go there + const sal_uInt16 nDispatchSlot = bRowMode ? FN_TABLE_INSERT_ROW_BEFORE : FN_TABLE_INSERT_COL_BEFORE; + pDispatch->Execute(nDispatchSlot, SfxCallMode::SYNCHRON); + pDispatch->Execute(bRowMode ? FN_LINE_UP : FN_CHAR_LEFT, SfxCallMode::SYNCHRON); + + // add the other new empty rows/columns after the actual table row/column + if ( nSelectedRowsOrCols > 1 ) + { + SfxUInt16Item aCountItem( nDispatchSlot, nSelectedRowsOrCols-1 ); + SfxBoolItem aAfter( FN_PARAM_INSERT_AFTER, true ); + pDispatch->ExecuteList(nDispatchSlot, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aCountItem, &aAfter }); + } + + // paste rows + bool bResult = SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, + nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext ); + + // restore cursor position + if (pMark != nullptr) + { + rSh.GotoMark( pMark ); + rSh.getIDocumentMarkAccess()->deleteMark( pMark ); + } + + return bResult; + } + } + + // special case for tables from draw application or 1-cell tables + if( EXCHG_OUT_ACTION_INSERT_DRAWOBJ == nAction || bSingleCellTable ) + { + if( rData.HasFormat( SotClipboardFormatId::RTF ) ) + { + nAction = EXCHG_OUT_ACTION_INSERT_STRING; + nFormat = SotClipboardFormatId::RTF; + } + else if( rData.HasFormat( SotClipboardFormatId::RICHTEXT ) ) + { + nAction = EXCHG_OUT_ACTION_INSERT_STRING; + nFormat = SotClipboardFormatId::RICHTEXT; + } + } + + // Tweak the format if necessary: the source application can be considered in this context, + // while not in sot/ code. + SwTransferable::SelectPasteFormat(rData, nAction, nFormat); + + collectUIInformation("PASTE", "parameter"); + + return EXCHG_INOUT_ACTION_NONE != nAction && + SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, + nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext, ePasteTable); +} + +bool SwTransferable::PasteData( TransferableDataHelper& rData, + SwWrtShell& rSh, sal_uInt8 nAction, SotExchangeActionFlags nActionFlags, + SotClipboardFormatId nFormat, + SotExchangeDest nDestination, bool bIsPasteFormat, + bool bIsDefault, + const Point* pPt, sal_Int8 nDropAction, + bool bPasteSelection, RndStdIds nAnchorType, + bool bIgnoreComments, + SwPasteContext* pContext, + PasteTableType ePasteTable ) +{ + SwWait aWait( *rSh.GetView().GetDocShell(), false ); + std::unique_ptr<SwTrnsfrActionAndUndo, o3tl::default_delete<SwTrnsfrActionAndUndo>> pAction; + SwModule* pMod = SW_MOD(); + + bool bRet = false; + bool bCallAutoCaption = false; + + if( pPt ) + { + // external Drop + if( bPasteSelection ? !pMod->m_pXSelection : !pMod->m_pDragDrop ) + { + switch( nDestination ) + { + case SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP: + case SotExchangeDest::DOC_LNKD_GRAPHOBJ: + case SotExchangeDest::DOC_GRAPH_W_IMAP: + case SotExchangeDest::DOC_GRAPHOBJ: + case SotExchangeDest::DOC_OLEOBJ: + case SotExchangeDest::DOC_DRAWOBJ: + case SotExchangeDest::DOC_URLBUTTON: + case SotExchangeDest::DOC_GROUPOBJ: + // select frames/objects + SwTransferable::SetSelInShell( rSh, true, pPt ); + break; + + default: + SwTransferable::SetSelInShell( rSh, false, pPt ); + break; + } + } + } + else if( ( !GetSwTransferable( rData ) || bIsPasteFormat ) && + !rSh.IsTableMode() && rSh.HasSelection() ) + { + // then delete the selections + + //don't delete selected content + // - at table-selection + // - at ReRead of a graphic/DDEData + // - at D&D, for the right selection was taken care of + // in Drop-Handler + bool bDelSel = false; + switch( nDestination ) + { + case SotExchangeDest::DOC_TEXTFRAME: + case SotExchangeDest::SWDOC_FREE_AREA: + case SotExchangeDest::DOC_TEXTFRAME_WEB: + case SotExchangeDest::SWDOC_FREE_AREA_WEB: + bDelSel = true; + break; + default: + break; + } + + if( bDelSel ) + // #i34830# + pAction.reset(new SwTrnsfrActionAndUndo(&rSh, true, pContext)); + } + + SwTransferable *pTrans=nullptr, *pTunneledTrans=GetSwTransferable( rData ); + + // check for private drop + bool bPrivateDrop(pPt); + if (bPrivateDrop) + { + if (bPasteSelection) + pTrans = pMod->m_pXSelection; + else + pTrans = pMod->m_pDragDrop; + bPrivateDrop = nullptr != pTrans; + } + bool bNeedToSelectBeforePaste(false); + + if(bPrivateDrop && DND_ACTION_LINK == nDropAction) + { + // internal drop on object, suppress bPrivateDrop to change internal fill + bPrivateDrop = false; + bNeedToSelectBeforePaste = true; + } + + if(bPrivateDrop && pPt && DND_ACTION_MOVE == nDropAction) + { + // check if dragged over a useful target. If yes, use as content exchange + // drop as if from external + const SwFrameFormat* pSwFrameFormat = rSh.GetFormatFromObj(*pPt); + + if(dynamic_cast< const SwDrawFrameFormat* >(pSwFrameFormat)) + { + bPrivateDrop = false; + bNeedToSelectBeforePaste = true; + } + } + + if(bPrivateDrop) + { + // then internal Drag & Drop or XSelection + bRet = pTrans->PrivateDrop( rSh, *pPt, DND_ACTION_MOVE == nDropAction, + bPasteSelection ); + } + else if( !pPt && pTunneledTrans && + EXCHG_OUT_ACTION_INSERT_PRIVATE == nAction ) + { + // then internal paste + bRet = pTunneledTrans->PrivatePaste(rSh, pContext, ePasteTable); + } + else if( EXCHG_INOUT_ACTION_NONE != nAction ) + { + if( !pAction ) + { + pAction.reset(new SwTrnsfrActionAndUndo( &rSh )); + } + + // in Drag&Drop MessageBoxes must not be showed + bool bMsg = nullptr == pPt; + + // delete selections + + switch( nAction ) + { + case EXCHG_OUT_ACTION_INSERT_PRIVATE: + OSL_ENSURE( pPt, "EXCHG_OUT_ACTION_INSERT_PRIVATE: what should happen here?" ); + break; + + case EXCHG_OUT_ACTION_MOVE_PRIVATE: + OSL_ENSURE( pPt, "EXCHG_OUT_ACTION_MOVE_PRIVATE: what should happen here?" ); + break; + + case EXCHG_IN_ACTION_MOVE: + case EXCHG_IN_ACTION_COPY: + case EXCHG_IN_ACTION_LINK: + case EXCHG_OUT_ACTION_INSERT_HTML: + case EXCHG_OUT_ACTION_INSERT_STRING: + case EXCHG_OUT_ACTION_INSERT_IMAGEMAP: + case EXCHG_OUT_ACTION_REPLACE_IMAGEMAP: + + // then we have to use the format + switch( nFormat ) + { + case SotClipboardFormatId::DRAWING: + bRet = SwTransferable::PasteSdrFormat( rData, rSh, + SwPasteSdr::Insert, pPt, + nActionFlags, bNeedToSelectBeforePaste); + break; + + case SotClipboardFormatId::HTML: + case SotClipboardFormatId::HTML_SIMPLE: + case SotClipboardFormatId::HTML_NO_COMMENT: + case SotClipboardFormatId::RTF: + case SotClipboardFormatId::RICHTEXT: + case SotClipboardFormatId::STRING: + bRet = SwTransferable::PasteFileContent( rData, rSh, + nFormat, bMsg, bIgnoreComments ); + break; + + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + { + INetBookmark aBkmk; + if( rData.GetINetBookmark( nFormat, aBkmk ) ) + { + SwFormatINetFormat aFormat( aBkmk.GetURL(), OUString() ); + rSh.InsertURL( aFormat, aBkmk.GetDescription() ); + bRet = true; + } + } + break; + + case SotClipboardFormatId::SD_OLE: + bRet = SwTransferable::PasteOLE( rData, rSh, nFormat, + nActionFlags, bMsg ); + break; + + case SotClipboardFormatId::SVIM: + bRet = SwTransferable::PasteImageMap( rData, rSh ); + break; + + case SotClipboardFormatId::SVXB: + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::PNG: + case SotClipboardFormatId::GDIMETAFILE: + bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, + SwPasteSdr::Insert,pPt, + nActionFlags, nDropAction, bNeedToSelectBeforePaste); + break; + + case SotClipboardFormatId::XFORMS: + case SotClipboardFormatId::SBA_FIELDDATAEXCHANGE: + case SotClipboardFormatId::SBA_DATAEXCHANGE: + case SotClipboardFormatId::SBA_CTRLDATAEXCHANGE: + bRet = SwTransferable::PasteDBData( rData, rSh, nFormat, + EXCHG_IN_ACTION_LINK == nAction, + pPt, bMsg ); + break; + + case SotClipboardFormatId::SIMPLE_FILE: + bRet = SwTransferable::PasteFileName( rData, rSh, nFormat, + ( EXCHG_IN_ACTION_MOVE == nAction + ? SwPasteSdr::Replace + : EXCHG_IN_ACTION_LINK == nAction + ? SwPasteSdr::SetAttr + : SwPasteSdr::Insert), + pPt, nActionFlags, nullptr ); + break; + + case SotClipboardFormatId::FILE_LIST: + // then insert as graphics only + bRet = SwTransferable::PasteFileList( rData, rSh, + EXCHG_IN_ACTION_LINK == nAction, + pPt, bMsg ); + break; + + case SotClipboardFormatId::SONLK: + if( pPt ) + { + NaviContentBookmark aBkmk; + if( aBkmk.Paste( rData ) ) + { + if(bIsDefault) + { + switch(aBkmk.GetDefaultDragType()) + { + case RegionMode::NONE: nAction = EXCHG_IN_ACTION_COPY; break; + case RegionMode::EMBEDDED: nAction = EXCHG_IN_ACTION_MOVE; break; + case RegionMode::LINK: nAction = EXCHG_IN_ACTION_LINK; break; + } + } + rSh.NavigatorPaste( aBkmk, nAction ); + bRet = true; + } + } + break; + + case SotClipboardFormatId::INET_IMAGE: + case SotClipboardFormatId::NETSCAPE_IMAGE: + bRet = SwTransferable::PasteTargetURL( rData, rSh, + SwPasteSdr::Insert, + pPt, true ); + break; + + default: + OSL_ENSURE( pPt, "unknown format" ); + } + break; + + case EXCHG_OUT_ACTION_INSERT_FILE: + { + bool graphicInserted; + bRet = SwTransferable::PasteFileName( rData, rSh, nFormat, + SwPasteSdr::Insert, pPt, + nActionFlags, + &graphicInserted ); + if( graphicInserted ) + bCallAutoCaption = true; + } + break; + + case EXCHG_OUT_ACTION_INSERT_OLE: + bRet = SwTransferable::PasteOLE( rData, rSh, nFormat, + nActionFlags,bMsg ); + break; + + case EXCHG_OUT_ACTION_INSERT_DDE: + { + bool bReRead = 0 != CNT_HasGrf( rSh.GetCntType() ); + bRet = SwTransferable::PasteDDE( rData, rSh, bReRead, bMsg ); + } + break; + + case EXCHG_OUT_ACTION_INSERT_HYPERLINK: + { + OUString sURL, sDesc; + if( SotClipboardFormatId::SIMPLE_FILE == nFormat ) + { + if( rData.GetString( nFormat, sURL ) && !sURL.isEmpty() ) + { + SwTransferable::CheckForURLOrLNKFile( rData, sURL, &sDesc ); + if( sDesc.isEmpty() ) + sDesc = sURL; + bRet = true; + } + } + else + { + INetBookmark aBkmk; + if( rData.GetINetBookmark( nFormat, aBkmk ) ) + { + sURL = aBkmk.GetURL(); + sDesc = aBkmk.GetDescription(); + bRet = true; + } + } + + if( bRet ) + { + SwFormatINetFormat aFormat( sURL, OUString() ); + rSh.InsertURL( aFormat, sDesc ); + } + } + break; + + case EXCHG_OUT_ACTION_GET_ATTRIBUTES: + switch( nFormat ) + { + case SotClipboardFormatId::DRAWING: + bRet = SwTransferable::PasteSdrFormat( rData, rSh, + SwPasteSdr::SetAttr, pPt, + nActionFlags, bNeedToSelectBeforePaste); + break; + case SotClipboardFormatId::SVXB: + case SotClipboardFormatId::GDIMETAFILE: + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::PNG: + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + case SotClipboardFormatId::SIMPLE_FILE: + case SotClipboardFormatId::FILEGRPDESCRIPTOR: + case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: + bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, + SwPasteSdr::SetAttr, pPt, + nActionFlags, nDropAction, bNeedToSelectBeforePaste); + break; + default: + OSL_FAIL( "unknown format" ); + } + + break; + + case EXCHG_OUT_ACTION_INSERT_DRAWOBJ: + bRet = SwTransferable::PasteSdrFormat( rData, rSh, + SwPasteSdr::Insert, pPt, + nActionFlags, bNeedToSelectBeforePaste); + break; + case EXCHG_OUT_ACTION_INSERT_SVXB: + case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE: + case EXCHG_OUT_ACTION_INSERT_BITMAP: + case EXCHG_OUT_ACTION_INSERT_GRAPH: + bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, + SwPasteSdr::Insert, pPt, + nActionFlags, nDropAction, bNeedToSelectBeforePaste, nAnchorType ); + break; + + case EXCHG_OUT_ACTION_REPLACE_DRAWOBJ: + bRet = SwTransferable::PasteSdrFormat( rData, rSh, + SwPasteSdr::Replace, pPt, + nActionFlags, bNeedToSelectBeforePaste); + break; + + case EXCHG_OUT_ACTION_REPLACE_SVXB: + case EXCHG_OUT_ACTION_REPLACE_GDIMETAFILE: + case EXCHG_OUT_ACTION_REPLACE_BITMAP: + case EXCHG_OUT_ACTION_REPLACE_GRAPH: + bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, + SwPasteSdr::Replace,pPt, + nActionFlags, nDropAction, bNeedToSelectBeforePaste); + break; + + case EXCHG_OUT_ACTION_INSERT_INTERACTIVE: + bRet = SwTransferable::PasteAsHyperlink( rData, rSh, nFormat ); + break; + + default: + OSL_FAIL("unknown action" ); + } + } + + if( !bPasteSelection && rSh.IsFrameSelected() ) + { + rSh.EnterSelFrameMode(); + //force ::SelectShell + rSh.GetView().StopShellTimer(); + } + + pAction.reset(); + if( bCallAutoCaption ) + rSh.GetView().AutoCaption( GRAPHIC_CAP ); + + return bRet; +} + +SotExchangeDest SwTransferable::GetSotDestination( const SwWrtShell& rSh ) +{ + SotExchangeDest nRet = SotExchangeDest::NONE; + + ObjCntType eOType = rSh.GetObjCntTypeOfSelection(); + + switch( eOType ) + { + case OBJCNT_GRF: + { + bool bIMap, bLink; + bIMap = nullptr != rSh.GetFlyFrameFormat()->GetURL().GetMap(); + OUString aDummy; + rSh.GetGrfNms( &aDummy, nullptr ); + bLink = !aDummy.isEmpty(); + + if( bLink && bIMap ) + nRet = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP; + else if( bLink ) + nRet = SotExchangeDest::DOC_LNKD_GRAPHOBJ; + else if( bIMap ) + nRet = SotExchangeDest::DOC_GRAPH_W_IMAP; + else + nRet = SotExchangeDest::DOC_GRAPHOBJ; + } + break; + + case OBJCNT_FLY: + if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) + nRet = SotExchangeDest::DOC_TEXTFRAME_WEB; + else + nRet = SotExchangeDest::DOC_TEXTFRAME; + break; + case OBJCNT_OLE: nRet = SotExchangeDest::DOC_OLEOBJ; break; + + case OBJCNT_CONTROL: /* no Action avail */ + case OBJCNT_SIMPLE: nRet = SotExchangeDest::DOC_DRAWOBJ; break; + case OBJCNT_URLBUTTON: nRet = SotExchangeDest::DOC_URLBUTTON; break; + case OBJCNT_GROUPOBJ: nRet = SotExchangeDest::DOC_GROUPOBJ; break; + + // what do we do at multiple selections??? + default: + { + if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) + nRet = SotExchangeDest::SWDOC_FREE_AREA_WEB; + else + nRet = SotExchangeDest::SWDOC_FREE_AREA; + } + } + + return nRet; +} + +bool SwTransferable::PasteFileContent( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat, bool bMsg, bool bIgnoreComments ) +{ + const char* pResId = STR_CLPBRD_FORMAT_ERROR; + bool bRet = false; + + MSE40HTMLClipFormatObj aMSE40ClpObj; + + tools::SvRef<SotStorageStream> xStrm; + SvStream* pStream = nullptr; + Reader* pRead = nullptr; + OUString sData; + switch( nFormat ) + { + case SotClipboardFormatId::STRING: + { + pRead = ReadAscii; + if( rData.GetString( nFormat, sData ) ) + { + pStream = new SvMemoryStream( const_cast<sal_Unicode *>(sData.getStr()), + sData.getLength() * sizeof( sal_Unicode ), + StreamMode::READ ); +#ifdef OSL_BIGENDIAN + pStream->SetEndian( SvStreamEndian::BIG ); +#else + pStream->SetEndian( SvStreamEndian::LITTLE ); +#endif + + SwAsciiOptions aAOpt; + aAOpt.SetCharSet( RTL_TEXTENCODING_UCS2 ); + pRead->GetReaderOpt().SetASCIIOpts( aAOpt ); + break; + } + } + [[fallthrough]]; // because then test if we get a stream + + default: + if( rData.GetSotStorageStream( nFormat, xStrm ) ) + { + if( ( SotClipboardFormatId::HTML_SIMPLE == nFormat ) || + ( SotClipboardFormatId::HTML_NO_COMMENT == nFormat ) ) + { + pStream = aMSE40ClpObj.IsValid( *xStrm ); + pRead = ReadHTML; + pRead->SetReadUTF8( true ); + + bool bNoComments = + ( nFormat == SotClipboardFormatId::HTML_NO_COMMENT ); + pRead->SetIgnoreHTMLComments( bNoComments ); + } + else + { + pStream = xStrm.get(); + if( SotClipboardFormatId::RTF == nFormat || SotClipboardFormatId::RICHTEXT == nFormat) + pRead = SwReaderWriter::GetRtfReader(); + else if( !pRead ) + { + pRead = ReadHTML; + pRead->SetReadUTF8( true ); + } + } + } + break; + } + + if( pStream && pRead ) + { + Link<LinkParamNone*,void> aOldLink( rSh.GetChgLnk() ); + rSh.SetChgLnk( Link<LinkParamNone*,void>() ); + + const SwPosition& rInsPos = *rSh.GetCursor()->Start(); + SwReader aReader(*pStream, OUString(), OUString(), *rSh.GetCursor()); + rSh.SaveTableBoxContent( &rInsPos ); + + if (bIgnoreComments) + pRead->SetIgnoreHTMLComments(true); + + if( aReader.Read( *pRead ).IsError() ) + pResId = STR_ERROR_CLPBRD_READ; + else + { + pResId = nullptr; + bRet = true; + } + + rSh.SetChgLnk( aOldLink ); + if( bRet ) + rSh.CallChgLnk(); + } + else + pResId = STR_CLPBRD_FORMAT_ERROR; + + // Exist a SvMemoryStream? (data in the OUString and xStrm is empty) + if( pStream && !xStrm.is() ) + delete pStream; + + if (bMsg && pResId) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(pResId))); + xBox->run(); + } + return bRet; +} + +bool SwTransferable::PasteOLE( TransferableDataHelper& rData, SwWrtShell& rSh, + SotClipboardFormatId nFormat, SotExchangeActionFlags nActionFlags, bool bMsg ) +{ + bool bRet = false; + TransferableObjectDescriptor aObjDesc; + uno::Reference < io::XInputStream > xStrm; + uno::Reference < embed::XStorage > xStore; + Reader* pRead = nullptr; + + // Get the preferred format + SotClipboardFormatId nId; + if( rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ) + nId = SotClipboardFormatId::EMBEDDED_OBJ; + else if( rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) && + rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )) + nId = SotClipboardFormatId::EMBED_SOURCE; + else + nId = SotClipboardFormatId::NONE; + + if (nId != SotClipboardFormatId::NONE) + { + SwDocShell* pDocSh = rSh.GetDoc()->GetDocShell(); + xStrm = rData.GetInputStream(nId, SfxObjectShell::CreateShellID(pDocSh)); + } + + if (xStrm.is()) + { + // if there is an embedded object, first try if it's a writer object + // this will be inserted into the document by using a Reader + try + { + xStore = comphelper::OStorageHelper::GetStorageFromInputStream( xStrm ); + switch( SotStorage::GetFormatID( xStore ) ) + { + case SotClipboardFormatId::STARWRITER_60: + case SotClipboardFormatId::STARWRITERWEB_60: + case SotClipboardFormatId::STARWRITERGLOB_60: + case SotClipboardFormatId::STARWRITER_8: + case SotClipboardFormatId::STARWRITERWEB_8: + case SotClipboardFormatId::STARWRITERGLOB_8: + pRead = ReadXML; + break; + default: + try + { + xStore->dispose(); + xStore = nullptr; + } + catch (const uno::Exception&) + { + } + + break; + } + } + catch (const uno::Exception&) + { + // it wasn't a storage, but maybe it's a useful stream + } + } + + if( pRead ) + { + SwPaM &rPAM = *rSh.GetCursor(); + SwReader aReader(xStore, OUString(), rPAM); + if( ! aReader.Read( *pRead ).IsError() ) + bRet = true; + else if( bMsg ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_ERROR_CLPBRD_READ))); + xBox->run(); + } + } + else + { + // temporary storage until the object is inserted + uno::Reference< embed::XStorage > xTmpStor; + uno::Reference < embed::XEmbeddedObject > xObj; + OUString aName; + comphelper::EmbeddedObjectContainer aCnt; + + if ( xStrm.is() ) + { + if ( !rData.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) + { + OSL_ENSURE( !xStrm.is(), "An object without descriptor in clipboard!"); + } + } + else + { + if( rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE ) && rData.GetTransferableObjectDescriptor( nFormat, aObjDesc ) ) + { + xStrm = rData.GetInputStream(SotClipboardFormatId::EMBED_SOURCE_OLE, OUString()); + if (!xStrm.is()) + xStrm = rData.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString()); + + if ( !xStrm.is() ) + { + // This is MSOLE object that should be created by direct using of system clipboard + try + { + xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage(); + uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator = + embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() ); + + embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard( + xTmpStor, + "DummyName", + uno::Sequence< beans::PropertyValue >() ); + + // TODO/LATER: in future InsertedObjectInfo will be used to get container related information + // for example whether the object should be an iconified one + xObj = aInfo.Object; + } + catch (const uno::Exception&) + { + } + } + } + } + + if ( xStrm.is() && !xObj.is() ) + xObj = aCnt.InsertEmbeddedObject( xStrm, aName ); + + if( xObj.is() ) + { + svt::EmbeddedObjectRef xObjRef( xObj, aObjDesc.mnViewAspect ); + + // try to get the replacement image from the clipboard + Graphic aGraphic; + SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE; + + // limit the size of the preview metafile to 100000 actions + GDIMetaFile aMetafile; + if (rData.GetGDIMetaFile(SotClipboardFormatId::GDIMETAFILE, aMetafile, 100000)) + { + nGrFormat = SotClipboardFormatId::GDIMETAFILE; + aGraphic = aMetafile; + } + + // insert replacement image ( if there is one ) into the object helper + if ( nGrFormat != SotClipboardFormatId::NONE ) + { + DataFlavor aDataFlavor; + SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); + xObjRef.SetGraphic( aGraphic, aDataFlavor.MimeType ); + } + else if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + // it is important to have an icon, let an empty graphic be used + // if no other graphic is provided + // TODO/LATER: in future a default bitmap could be used + MapMode aMapMode( MapUnit::Map100thMM ); + aGraphic.SetPrefSize( Size( 2500, 2500 ) ); + aGraphic.SetPrefMapMode( aMapMode ); + xObjRef.SetGraphic( aGraphic, OUString() ); + } + + //set size. This is a hack because of handing over, size should be + //passed to the InsertOle!!!!!!!!!! + Size aSize; + if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + aSize = aObjDesc.maSize; + else + { + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = xObjRef.GetSize( &aMapMode ); + } + } + else if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + { + aSize = aObjDesc.maSize; //always 100TH_MM + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); + aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aUnit)); + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch (const embed::NoVisualAreaSizeException&) + { + // in this case the provided size is used + } + + if ( aSz.Width != aSize.Width() || aSz.Height != aSize.Height() ) + { + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + } + else + { + // the descriptor contains the wrong object size + // the following call will let the MSOLE objects cache the size if it is possible + // it should be done while the object is running + try + { + xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch (const uno::Exception&) + { + } + } + //End of Hack! + + rSh.InsertOleObject( xObjRef ); + bRet = true; + + if( bRet && ( nActionFlags & SotExchangeActionFlags::InsertTargetUrl) ) + SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); + + // let the object be unloaded if possible + SwOLEObj::UnloadObject( xObj, rSh.GetDoc(), embed::Aspects::MSOLE_CONTENT ); + } + } + return bRet; +} + +bool SwTransferable::PasteTargetURL( TransferableDataHelper& rData, + SwWrtShell& rSh, SwPasteSdr nAction, + const Point* pPt, bool bInsertGRF ) +{ + bool bRet = false; + INetImage aINetImg; + if( ( rData.HasFormat( SotClipboardFormatId::INET_IMAGE ) && + rData.GetINetImage( SotClipboardFormatId::INET_IMAGE, aINetImg )) || + ( rData.HasFormat( SotClipboardFormatId::NETSCAPE_IMAGE ) && + rData.GetINetImage( SotClipboardFormatId::NETSCAPE_IMAGE, aINetImg )) ) + { + if( !aINetImg.GetImageURL().isEmpty() && bInsertGRF ) + { + OUString sURL( aINetImg.GetImageURL() ); + SwTransferable::CheckForURLOrLNKFile( rData, sURL ); + + //!!! check at FileSystem - only then it makes sense to test graphics !!! + Graphic aGraphic; + GraphicFilter &rFlt = GraphicFilter::GetGraphicFilter(); + bRet = ERRCODE_NONE == GraphicFilter::LoadGraphic(sURL, OUString(), aGraphic, &rFlt); + + if( bRet ) + { + //Check and Perform rotation if needed + lclCheckAndPerformRotation(aGraphic); + + switch( nAction ) + { + case SwPasteSdr::Insert: + SwTransferable::SetSelInShell( rSh, false, pPt ); + rSh.Insert(sURL, OUString(), aGraphic); + break; + + case SwPasteSdr::Replace: + if( rSh.IsObjSelected() ) + { + rSh.ReplaceSdrObj( sURL, &aGraphic ); + Point aPt( pPt ? *pPt : rSh.GetCursorDocPos() ); + SwTransferable::SetSelInShell( rSh, true, &aPt ); + } + else + rSh.ReRead(sURL, OUString(), &aGraphic); + break; + + case SwPasteSdr::SetAttr: + if( rSh.IsObjSelected() ) + rSh.Paste( aGraphic, OUString() ); + else if( OBJCNT_GRF == rSh.GetObjCntTypeOfSelection() ) + rSh.ReRead(sURL, OUString(), &aGraphic); + else + { + SwTransferable::SetSelInShell( rSh, false, pPt ); + rSh.Insert(sURL, OUString(), aGraphic); + } + break; + default: + bRet = false; + } + } + } + else + bRet = true; + } + + if( bRet ) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + + if( aURL.GetURL() != aINetImg.GetTargetURL() || + aURL.GetTargetFrameName() != aINetImg.GetTargetFrame() ) + { + aURL.SetURL( aINetImg.GetTargetURL(), false ); + aURL.SetTargetFrameName( aINetImg.GetTargetFrame() ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + } + } + return bRet; +} + +void SwTransferable::SetSelInShell( SwWrtShell& rSh, bool bSelectFrame, + const Point* pPt ) +{ + if( bSelectFrame ) + { + // select frames/objects + if( pPt && !rSh.GetView().GetViewFrame()->GetDispatcher()->IsLocked() ) + { + rSh.GetView().NoRotate(); + if( rSh.SelectObj( *pPt )) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( pPt ); + g_bFrameDrag = true; + } + } + } + else + { + if( rSh.IsFrameSelected() || rSh.IsObjSelected() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + rSh.GetView().GetEditWin().StopInsFrame(); + g_bFrameDrag = false; + } + else if( rSh.GetView().GetDrawFuncPtr() ) + rSh.GetView().GetEditWin().StopInsFrame(); + + rSh.EnterStdMode(); + if( pPt ) + rSh.SwCursorShell::SetCursor( *pPt, true ); + } +} + +bool SwTransferable::PasteDDE( TransferableDataHelper& rData, + SwWrtShell& rWrtShell, bool bReReadGrf, + bool bMsg ) +{ + // data from Clipboardformat + OUString aApp, aTopic, aItem; + + { + tools::SvRef<SotStorageStream> xStrm; + if( !rData.GetSotStorageStream( SotClipboardFormatId::LINK, xStrm )) + { + OSL_ENSURE( false, "DDE Data not found." ); + return false; + } // report useful error!! + + rtl_TextEncoding eEncoding = DDE_TXT_ENCODING; + aApp = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); + aTopic = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); + aItem = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); + } + + OUString aCmd; + sfx2::MakeLnkName( aCmd, &aApp, aTopic, aItem ); + + // do we want to read in a graphic now? + SotClipboardFormatId nFormat; + if( !rData.HasFormat( SotClipboardFormatId::RTF ) && + !rData.HasFormat( SotClipboardFormatId::RICHTEXT ) && + !rData.HasFormat( SotClipboardFormatId::HTML ) && + !rData.HasFormat( SotClipboardFormatId::STRING ) && + (rData.HasFormat( nFormat = SotClipboardFormatId::GDIMETAFILE ) || + rData.HasFormat( nFormat = SotClipboardFormatId::BITMAP )) ) + { + Graphic aGrf; + bool bRet = rData.GetGraphic( nFormat, aGrf ); + if( bRet ) + { + OUString sLnkTyp("DDE"); + if ( bReReadGrf ) + rWrtShell.ReRead( aCmd, sLnkTyp, &aGrf ); + else + rWrtShell.Insert( aCmd, sLnkTyp, aGrf ); + } + return bRet; + } + + SwFieldType* pTyp = nullptr; + size_t i = 1; + size_t j; + OUString aName; + bool bDoublePaste = false; + const size_t nSize = rWrtShell.GetFieldTypeCount(); + const ::utl::TransliterationWrapper& rColl = ::GetAppCmpStrIgnore(); + + do { + aName = aApp + OUString::number( i ); + for( j = INIT_FLDTYPES; j < nSize; j++ ) + { + pTyp = rWrtShell.GetFieldType( j ); + if( SwFieldIds::Dde == pTyp->Which() ) + { + if( rColl.isEqual( static_cast<SwDDEFieldType*>(pTyp)->GetCmd(), aCmd ) && + SfxLinkUpdateMode::ALWAYS == static_cast<SwDDEFieldType*>(pTyp)->GetType() ) + { + aName = pTyp->GetName(); + bDoublePaste = true; + break; + } + else if( rColl.isEqual( aName, pTyp->GetName() ) ) + break; + } + } + if( j == nSize ) + break; + ++i; + } + while( !bDoublePaste ); + + if( !bDoublePaste ) + { + SwDDEFieldType aType( aName, aCmd, SfxLinkUpdateMode::ALWAYS ); + pTyp = rWrtShell.InsertFieldType( aType ); + } + + SwDDEFieldType* pDDETyp = static_cast<SwDDEFieldType*>(pTyp); + + OUString aExpand; + if( rData.GetString( SotClipboardFormatId::STRING, aExpand )) + { + do { // middle checked loop + + const sal_Int32 nNewlines{comphelper::string::getTokenCount(aExpand, '\n')}; + // When data comes from a spreadsheet, we add a DDE-table + if( !aExpand.isEmpty() && + ( rData.HasFormat( SotClipboardFormatId::SYLK ) || + rData.HasFormat( SotClipboardFormatId::SYLK_BIGCAPS ) ) ) + { + const sal_Int32 nRows = nNewlines ? nNewlines-1 : 0; + const sal_Int32 nCols = comphelper::string::getTokenCount(aExpand.getToken(0, '\n'), '\t'); + + // don't try to insert tables that are too large for writer + if (nRows > SAL_MAX_UINT16 || nCols > SAL_MAX_UINT16) + { + if( bMsg ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_TABLE_TOO_LARGE))); + xBox->run(); + } + pDDETyp = nullptr; + break; + } + + // at least one column & row must be there + if( !nRows || !nCols ) + { + if( bMsg ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_NO_TABLE))); + xBox->run(); + } + pDDETyp = nullptr; + break; + } + + rWrtShell.InsertDDETable( + SwInsertTableOptions( SwInsertTableFlags::SplitLayout, 1 ), // TODO MULTIHEADER + pDDETyp, nRows, nCols ); + } + else if( nNewlines > 1 ) + { + // multiple paragraphs -> insert a protected section + if( rWrtShell.HasSelection() ) + rWrtShell.DelRight(); + + SwSectionData aSect( SectionType::DdeLink, aName ); + aSect.SetLinkFileName( aCmd ); + aSect.SetProtectFlag(true); + rWrtShell.InsertSection( aSect ); + + pDDETyp = nullptr; // remove FieldTypes again + } + else + { + // insert + SwDDEField aSwDDEField( pDDETyp ); + rWrtShell.Insert( aSwDDEField ); + } + + } while( false ); + } + else + pDDETyp = nullptr; // remove FieldTypes again + + if( !pDDETyp && !bDoublePaste ) + { + // remove FieldType again - error occurred! + for( j = nSize; j >= INIT_FLDTYPES; --j ) + if( pTyp == rWrtShell.GetFieldType( j ) ) + { + rWrtShell.RemoveFieldType( j ); + break; + } + } + + return true; +} + +bool SwTransferable::PasteSdrFormat( TransferableDataHelper& rData, + SwWrtShell& rSh, SwPasteSdr nAction, + const Point* pPt, SotExchangeActionFlags nActionFlags, bool bNeedToSelectBeforePaste) +{ + bool bRet = false; + tools::SvRef<SotStorageStream> xStrm; + if( rData.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStrm )) + { + xStrm->SetVersion( SOFFICE_FILEFORMAT_50 ); + + if(bNeedToSelectBeforePaste && pPt) + { + // if this is an internal drag, need to set the target right (select it), else + // still the source will be selected + SwTransferable::SetSelInShell( rSh, true, pPt ); + } + + rSh.Paste( *xStrm, nAction, pPt ); + bRet = true; + + if( bRet && ( nActionFlags & SotExchangeActionFlags::InsertTargetUrl )) + SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); + } + return bRet; +} + +bool SwTransferable::PasteGrf( TransferableDataHelper& rData, SwWrtShell& rSh, + SotClipboardFormatId nFormat, SwPasteSdr nAction, const Point* pPt, + SotExchangeActionFlags nActionFlags, sal_Int8 nDropAction, bool bNeedToSelectBeforePaste, RndStdIds nAnchorType ) +{ + bool bRet = false; + + Graphic aGraphic; + INetBookmark aBkmk; + bool bCheckForGrf = false, bCheckForImageMap = false; + + switch( nFormat ) + { + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::PNG: + case SotClipboardFormatId::GDIMETAFILE: + bRet = rData.GetGraphic( nFormat, aGraphic ); + break; + + case SotClipboardFormatId::SVXB: + { + tools::SvRef<SotStorageStream> xStm; + + if(rData.GetSotStorageStream(SotClipboardFormatId::SVXB, xStm)) + { + ReadGraphic( *xStm, aGraphic ); + bRet = (GraphicType::NONE != aGraphic.GetType() && GraphicType::Default != aGraphic.GetType()); + } + + break; + } + + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + case SotClipboardFormatId::FILEGRPDESCRIPTOR: + case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: + bRet = rData.GetINetBookmark( nFormat, aBkmk ); + if( bRet ) + { + if( SwPasteSdr::SetAttr == nAction ) + nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK; + else + bCheckForGrf = true; + } + break; + + case SotClipboardFormatId::SIMPLE_FILE: + { + OUString sText; + bRet = rData.GetString( nFormat, sText ); + if( bRet ) + { + OUString sDesc; + SwTransferable::CheckForURLOrLNKFile( rData, sText, &sDesc ); + + sText = URIHelper::SmartRel2Abs(INetURLObject(), sText, Link<OUString*, bool>(), + false); + +#ifdef _WIN32 + // Now that the path could be modified after SwTransferable::CheckForURLOrLNKFile, + // where it could have been converted to URL, and made sure it's actually converted + // to URL in URIHelper::SmartRel2Abs, we can finally convert file: URL back to + // system path to make sure we don't use short path. + // It looks not optimal, when we could apply GetLongPathNameW right to the original + // pasted filename. But I don't know if (1) all arriving strings are system paths; + // and (2) if SwTransferable::CheckForURLOrLNKFile could result in a different short + // path, so taking a safe route. + if (sText.startsWithIgnoreAsciiCase("file:")) + { + // tdf#124500: Convert short path to long path which should be used in links + OUString sSysPath; + osl::FileBase::getSystemPathFromFileURL(sText, sSysPath); + std::unique_ptr<sal_Unicode[]> aBuf(new sal_Unicode[32767]); + DWORD nCopied = GetLongPathNameW(o3tl::toW(sSysPath.getStr()), + o3tl::toW(aBuf.get()), 32767); + if (nCopied && nCopied < 32767) + sText = URIHelper::SmartRel2Abs(INetURLObject(), aBuf.get(), + Link<OUString*, bool>(), false); + } +#endif + + aBkmk = INetBookmark(sText, sDesc); + bCheckForGrf = true; + bCheckForImageMap = SwPasteSdr::Replace == nAction; + } + } + break; + + default: + bRet = rData.GetGraphic( nFormat, aGraphic ); + break; + } + + if( bCheckForGrf ) + { + //!!! check at FileSystem - only then it makes sense to test the graphics !!! + GraphicFilter &rFlt = GraphicFilter::GetGraphicFilter(); + bRet = ERRCODE_NONE == GraphicFilter::LoadGraphic(aBkmk.GetURL(), OUString(), + aGraphic, &rFlt ); + + if( !bRet && SwPasteSdr::SetAttr == nAction && + SotClipboardFormatId::SIMPLE_FILE == nFormat && + // only at frame selection + rSh.IsFrameSelected() ) + { + // then set as hyperlink after the graphic + nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK; + bRet = true; + } + } + + if(pPt && bNeedToSelectBeforePaste) + { + // when using internal D&Ds, still the source object is selected and + // this is necessary to get the correct source data which is also + // dependent from selection. After receiving the drag data it is + // now time to select the correct target object + SwTransferable::SetSelInShell( rSh, true, pPt ); + } + + if( bRet ) + { + //Check and Perform rotation if needed + lclCheckAndPerformRotation(aGraphic); + + OUString sURL; + if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr + // #i123922# if link action is noted, also take URL + || DND_ACTION_LINK == nDropAction) + { + sURL = aBkmk.GetURL(); + } + + switch( nAction ) + { + case SwPasteSdr::Insert: + { + SwTransferable::SetSelInShell( rSh, false, pPt ); + rSh.Insert(sURL, OUString(), aGraphic, nullptr, nAnchorType); + break; + } + + case SwPasteSdr::Replace: + { + if( rSh.IsObjSelected() ) + { + // #i123922# for D&D on draw objects, do for now the same for + // SwPasteSdr::Replace (D&D) as for SwPasteSdr::SetAttr (D&D and + // CTRL+SHIFT). The code below replaces the draw object with + // a writer graphic; maybe this is an option later again if wanted + rSh.Paste( aGraphic, sURL ); + + // rSh.ReplaceSdrObj(sURL, OUString(), &aGraphic); + // Point aPt( pPt ? *pPt : rSh.GetCursorDocPos() ); + // SwTransferable::SetSelInShell( rSh, true, &aPt ); + } + else + { + // set graphic at writer graphic without link + rSh.ReRead(sURL, OUString(), &aGraphic); + } + + break; + } + + case SwPasteSdr::SetAttr: + { + if( SotClipboardFormatId::NETSCAPE_BOOKMARK == nFormat ) + { + if( rSh.IsFrameSelected() ) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + aURL.SetURL( aBkmk.GetURL(), false ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + } + } + else if( rSh.IsObjSelected() ) + { + // set as attribute at DrawObject + rSh.Paste( aGraphic, sURL ); + } + else if( OBJCNT_GRF == rSh.GetObjCntTypeOfSelection() ) + { + // set as linked graphic at writer graphic frame + rSh.ReRead(sURL, OUString(), &aGraphic); + } + else + { + SwTransferable::SetSelInShell( rSh, false, pPt ); + rSh.Insert(aBkmk.GetURL(), OUString(), aGraphic); + } + break; + } + default: + { + bRet = false; + break; + } + } + } + + if( bRet ) + { + + if( nActionFlags & + ( SotExchangeActionFlags::InsertImageMap | SotExchangeActionFlags::ReplaceImageMap ) ) + SwTransferable::PasteImageMap( rData, rSh ); + + if( nActionFlags & SotExchangeActionFlags::InsertTargetUrl ) + SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); + } + else if( bCheckForImageMap ) + { + // or should the file be an ImageMap-File? + ImageMap aMap; + SfxMedium aMed( INetURLObject(aBkmk.GetURL()).GetFull(), + StreamMode::STD_READ ); + SvStream* pStream = aMed.GetInStream(); + if( pStream != nullptr && + !pStream->GetError() && + // mba: no BaseURL for clipboard functionality + aMap.Read( *pStream, IMAP_FORMAT_DETECT ) == IMAP_ERR_OK && + aMap.GetIMapObjectCount() ) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + aURL.SetMap( &aMap ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + bRet = true; + } + } + + return bRet; +} + +bool SwTransferable::PasteImageMap( TransferableDataHelper& rData, + SwWrtShell& rSh ) +{ + bool bRet = false; + if( rData.HasFormat( SotClipboardFormatId::SVIM )) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + const ImageMap* pOld = aURL.GetMap(); + + // set or replace, that is the question + ImageMap aImageMap; + if( rData.GetImageMap( SotClipboardFormatId::SVIM, aImageMap ) && + ( !pOld || aImageMap != *pOld )) + { + aURL.SetMap( &aImageMap ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + } + bRet = true; + } + return bRet; +} + +bool SwTransferable::PasteAsHyperlink( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat ) +{ + bool bRet = false; + OUString sFile; + if( rData.GetString( nFormat, sFile ) && !sFile.isEmpty() ) + { + OUString sDesc; + SwTransferable::CheckForURLOrLNKFile( rData, sFile, &sDesc ); + + // first, make the URL absolute + INetURLObject aURL; + aURL.SetSmartProtocol( INetProtocol::File ); + aURL.SetSmartURL( sFile ); + sFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + switch( rSh.GetObjCntTypeOfSelection() ) + { + case OBJCNT_FLY: + case OBJCNT_GRF: + case OBJCNT_OLE: + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL2( aSet.Get( RES_URL ) ); + aURL2.SetURL( sFile, false ); + if( aURL2.GetName().isEmpty() ) + aURL2.SetName( sFile ); + aSet.Put( aURL2 ); + rSh.SetFlyFrameAttr( aSet ); + } + break; + + default: + { + rSh.InsertURL( SwFormatINetFormat( sFile, OUString() ), + sDesc.isEmpty() ? sFile : sDesc); + } + } + bRet = true; + } + return bRet; +} + +bool SwTransferable::PasteFileName( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat, + SwPasteSdr nAction, const Point* pPt, + SotExchangeActionFlags nActionFlags, + bool * graphicInserted) +{ + bool bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, nAction, + pPt, nActionFlags, 0, false); + if (graphicInserted != nullptr) { + *graphicInserted = bRet; + } + if( !bRet ) + { + OUString sFile, sDesc; + if( rData.GetString( nFormat, sFile ) && !sFile.isEmpty() ) + { +#if HAVE_FEATURE_AVMEDIA + INetURLObject aMediaURL; + + aMediaURL.SetSmartURL( sFile ); + const OUString aMediaURLStr( aMediaURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + if( ::avmedia::MediaWindow::isMediaURL( aMediaURLStr, ""/*TODO?*/ ) ) + { + const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, aMediaURLStr ); + rSh.GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + SID_INSERT_AVMEDIA, SfxCallMode::SYNCHRON, + { &aMediaURLItem }); + } +#else + if (false) + { + } +#endif + else + { + bool bIsURLFile = SwTransferable::CheckForURLOrLNKFile( rData, sFile, &sDesc ); + + //Own FileFormat? --> insert, not for StarWriter/Web + OUString sFileURL = URIHelper::SmartRel2Abs(INetURLObject(), sFile, Link<OUString *, bool>(), false ); + std::shared_ptr<const SfxFilter> pFlt = SwPasteSdr::SetAttr == nAction + ? nullptr : SwIoSystem::GetFileFilter(sFileURL); + if( pFlt && dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) == nullptr ) + { + // and then pull up the insert-region-dialog + SwSectionData aSect( + SectionType::FileLink, + rSh.GetDoc()->GetUniqueSectionName() ); + aSect.SetLinkFileName( sFileURL ); + aSect.SetProtectFlag( true ); + + rSh.StartInsertRegionDialog( aSect ); // starts dialog asynchronously + bRet = true; + } + else if( SwPasteSdr::SetAttr == nAction || + ( bIsURLFile && SwPasteSdr::Insert == nAction )) + { + //we can insert foreign files as links after all + + // first, make the URL absolute + INetURLObject aURL; + aURL.SetSmartProtocol( INetProtocol::File ); + aURL.SetSmartURL( sFile ); + sFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + switch( rSh.GetObjCntTypeOfSelection() ) + { + case OBJCNT_FLY: + case OBJCNT_GRF: + case OBJCNT_OLE: + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL2( aSet.Get( RES_URL ) ); + aURL2.SetURL( sFile, false ); + if( aURL2.GetName().isEmpty() ) + aURL2.SetName( sFile ); + aSet.Put( aURL2 ); + rSh.SetFlyFrameAttr( aSet ); + } + break; + + default: + { + rSh.InsertURL( SwFormatINetFormat( sFile, OUString() ), + sDesc.isEmpty() ? sFile : sDesc ); + } + } + bRet = true; + } + } + } + } + return bRet; +} + +bool SwTransferable::PasteDBData( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat, bool bLink, + const Point* pDragPt, bool bMsg ) +{ + bool bRet = false; + OUString sText; + if( rData.GetString( nFormat, sText ) && !sText.isEmpty() ) + { + sal_uInt16 nWh = SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == nFormat + ? 0 + : SotClipboardFormatId::SBA_DATAEXCHANGE == nFormat + ? (bLink + ? FN_QRY_MERGE_FIELD + : FN_QRY_INSERT) + : (bLink + ? 0 + : FN_QRY_INSERT_FIELD ); + const DataFlavorExVector& rVector = rData.GetDataFlavorExVector(); + bool bHaveColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(rVector, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE); + if ( SotClipboardFormatId::XFORMS == nFormat ) + { + rSh.MakeDrawView(); + FmFormView* pFmView = dynamic_cast<FmFormView*>( rSh.GetDrawView() ); + if (pFmView && pDragPt) + { + const OXFormsDescriptor &rDesc = OXFormsTransferable::extractDescriptor(rData); + SdrObjectUniquePtr pObj = pFmView->CreateXFormsControl(rDesc); + if(pObj) + { + rSh.SwFEShell::InsertDrawObj( *(pObj.release()), *pDragPt ); + } + } + } + else if( nWh ) + { + std::unique_ptr<SfxUnoAnyItem> pConnectionItem; + std::unique_ptr<SfxUnoAnyItem> pCursorItem; + std::unique_ptr<SfxUnoAnyItem> pColumnItem; + std::unique_ptr<SfxUnoAnyItem> pSourceItem; + std::unique_ptr<SfxUnoAnyItem> pCommandItem; + std::unique_ptr<SfxUnoAnyItem> pCommandTypeItem; + std::unique_ptr<SfxUnoAnyItem> pColumnNameItem; + std::unique_ptr<SfxUnoAnyItem> pSelectionItem; + + bool bDataAvailable = true; + ODataAccessDescriptor aDesc; + if(bHaveColumnDescriptor) + aDesc = OColumnTransferable::extractColumnDescriptor(rData); + else if(ODataAccessObjectTransferable::canExtractObjectDescriptor(rVector) ) + aDesc = ODataAccessObjectTransferable::extractObjectDescriptor(rData); + else + bDataAvailable = false; + + if ( bDataAvailable ) + { + pConnectionItem.reset(new SfxUnoAnyItem(FN_DB_CONNECTION_ANY, aDesc[DataAccessDescriptorProperty::Connection])); + pColumnItem.reset(new SfxUnoAnyItem(FN_DB_COLUMN_ANY, aDesc[DataAccessDescriptorProperty::ColumnObject])); + pSourceItem.reset(new SfxUnoAnyItem(FN_DB_DATA_SOURCE_ANY, makeAny(aDesc.getDataSource()))); + pCommandItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COMMAND_ANY, aDesc[DataAccessDescriptorProperty::Command])); + pCommandTypeItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COMMAND_TYPE_ANY, aDesc[DataAccessDescriptorProperty::CommandType])); + pColumnNameItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COLUMN_NAME_ANY, aDesc[DataAccessDescriptorProperty::ColumnName])); + pSelectionItem.reset(new SfxUnoAnyItem(FN_DB_DATA_SELECTION_ANY, aDesc[DataAccessDescriptorProperty::Selection])); + pCursorItem.reset(new SfxUnoAnyItem(FN_DB_DATA_CURSOR_ANY, aDesc[DataAccessDescriptorProperty::Cursor])); + } + + SwView& rView = rSh.GetView(); + //force ::SelectShell + rView.StopShellTimer(); + + SfxStringItem aDataDesc( nWh, sText ); + rView.GetViewFrame()->GetDispatcher()->ExecuteList( + nWh, SfxCallMode::ASYNCHRON, + { &aDataDesc, pConnectionItem.get(), pColumnItem.get(), + pSourceItem.get(), pCommandItem.get(), pCommandTypeItem.get(), + pColumnNameItem.get(), pSelectionItem.get(), + pCursorItem.get() }); + } + else + { + rSh.MakeDrawView(); + FmFormView* pFmView = dynamic_cast<FmFormView*>( rSh.GetDrawView() ); + if (pFmView && bHaveColumnDescriptor && pDragPt) + { + SdrObjectUniquePtr pObj = pFmView->CreateFieldControl( OColumnTransferable::extractColumnDescriptor(rData) ); + if (pObj) + rSh.SwFEShell::InsertDrawObj( *(pObj.release()), *pDragPt ); + } + } + bRet = true; + } + else if( bMsg ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_CLPBRD_FORMAT_ERROR))); + xBox->run(); + } + return bRet; +} + +bool SwTransferable::PasteFileList( TransferableDataHelper& rData, + SwWrtShell& rSh, bool bLink, + const Point* pPt, bool bMsg ) +{ + bool bRet = false; + FileList aFileList; + if( rData.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ) && + aFileList.Count() ) + { + SwPasteSdr nAct = bLink ? SwPasteSdr::SetAttr : SwPasteSdr::Insert; + OUString sFlyNm; + // iterate over the filelist + for( sal_uLong n = 0, nEnd = aFileList.Count(); n < nEnd; ++n ) + { + TransferDataContainer* pHlp = new TransferDataContainer; + pHlp->CopyString( SotClipboardFormatId::SIMPLE_FILE, aFileList.GetFile( n )); + TransferableDataHelper aData( pHlp ); + + if( SwTransferable::PasteFileName( aData, rSh, SotClipboardFormatId::SIMPLE_FILE, nAct, + pPt, SotExchangeActionFlags::NONE, nullptr )) + { + if( bLink ) + { + sFlyNm = rSh.GetFlyName(); + SwTransferable::SetSelInShell( rSh, false, pPt ); + } + bRet = true; + } + } + if( !sFlyNm.isEmpty() ) + rSh.GotoFly( sFlyNm ); + } + else if( bMsg ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_CLPBRD_FORMAT_ERROR))); + xBox->run(); + } + return bRet; +} + +bool SwTransferable::CheckForURLOrLNKFile( TransferableDataHelper& rData, + OUString& rFileName, OUString* pTitle ) +{ + bool bIsURLFile = false; + INetBookmark aBkmk; + if( rData.GetINetBookmark( SotClipboardFormatId::SOLK, aBkmk ) ) + { + rFileName = aBkmk.GetURL(); + if( pTitle ) + *pTitle = aBkmk.GetDescription(); + bIsURLFile = true; + } + else + { + if( rFileName.getLength()>4 && rFileName.endsWithIgnoreAsciiCase(".url") ) + { + OSL_ENSURE( false, "how do we read today .URL - Files?" ); + } + } + return bIsURLFile; +} + +bool SwTransferable::IsPasteSpecial( const SwWrtShell& rWrtShell, + const TransferableDataHelper& rData ) +{ + // we can paste-special if there's an entry in the paste-special-format list + SvxClipboardFormatItem aClipboardFormatItem(0); + FillClipFormatItem( rWrtShell, rData, aClipboardFormatItem); + return aClipboardFormatItem.Count() > 0; +} + +bool SwTransferable::IsPasteOwnFormat( const TransferableDataHelper& rData ) +{ + return ( GetSwTransferable( rData ) != nullptr ); +} + +bool SwTransferable::PasteFormat( SwWrtShell& rSh, + TransferableDataHelper& rData, + SotClipboardFormatId nFormat ) +{ + SwWait aWait( *rSh.GetView().GetDocShell(), false ); + bool bRet = false; + + SotClipboardFormatId nPrivateFormat = SotClipboardFormatId::PRIVATE; + SwTransferable *pClipboard = GetSwTransferable( rData ); + if( pClipboard && + ((TransferBufferType::Document|TransferBufferType::Graphic|TransferBufferType::Ole) & pClipboard->m_eBufferType )) + nPrivateFormat = SotClipboardFormatId::EMBED_SOURCE; + + if( pClipboard && nPrivateFormat == nFormat ) + bRet = pClipboard->PrivatePaste( rSh ); + else if( rData.HasFormat( nFormat ) ) + { + uno::Reference<XTransferable> xTransferable( rData.GetXTransferable() ); + sal_uInt8 nEventAction; + SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); + sal_uInt16 nSourceOptions = + (( SotExchangeDest::DOC_TEXTFRAME == nDestination || + SotExchangeDest::SWDOC_FREE_AREA == nDestination || + SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || + SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) + ? EXCHG_IN_ACTION_COPY + : EXCHG_IN_ACTION_MOVE); + SotExchangeActionFlags nActionFlags; + sal_uInt8 nAction = SotExchange::GetExchangeAction( + rData.GetDataFlavorExVector(), + nDestination, + nSourceOptions, /* ?? */ + EXCHG_IN_ACTION_DEFAULT, /* ?? */ + nFormat, nEventAction, nFormat, + lcl_getTransferPointer ( xTransferable ), + &nActionFlags ); + + if( EXCHG_INOUT_ACTION_NONE != nAction ) + bRet = SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, + nDestination, true, false ); + } + return bRet; +} + +bool SwTransferable::TestAllowedFormat( const TransferableDataHelper& rData, + SotClipboardFormatId nFormat, SotExchangeDest nDestination ) +{ + sal_uInt8 nAction = EXCHG_INOUT_ACTION_NONE, nEventAction; + if( rData.HasFormat( nFormat )) { + uno::Reference<XTransferable> xTransferable( rData.GetXTransferable() ); + nAction = SotExchange::GetExchangeAction( + rData.GetDataFlavorExVector(), + nDestination, EXCHG_IN_ACTION_COPY, + EXCHG_IN_ACTION_COPY, nFormat, + nEventAction, nFormat, + lcl_getTransferPointer ( xTransferable ) ); + } + return EXCHG_INOUT_ACTION_NONE != nAction; +} + +/** + * the list of formats which will be offered to the user in the 'Paste + * Special...' dialog and the paste button menu + */ +static SotClipboardFormatId aPasteSpecialIds[] = +{ + SotClipboardFormatId::HTML, + SotClipboardFormatId::HTML_SIMPLE, + SotClipboardFormatId::HTML_NO_COMMENT, + SotClipboardFormatId::RTF, + SotClipboardFormatId::RICHTEXT, + SotClipboardFormatId::STRING, + SotClipboardFormatId::SONLK, + SotClipboardFormatId::NETSCAPE_BOOKMARK, + SotClipboardFormatId::DRAWING, + SotClipboardFormatId::SVXB, + SotClipboardFormatId::GDIMETAFILE, + SotClipboardFormatId::BITMAP, + SotClipboardFormatId::SVIM, + SotClipboardFormatId::FILEGRPDESCRIPTOR, + SotClipboardFormatId::NONE +}; + +bool SwTransferable::PasteUnformatted( SwWrtShell& rSh, TransferableDataHelper& rData ) +{ + // Plain text == unformatted + return SwTransferable::PasteFormat( rSh, rData, SotClipboardFormatId::STRING ); +} + +void SwTransferable::PrePasteSpecial( const SwWrtShell& rSh, TransferableDataHelper& rData, const VclPtr<SfxAbstractPasteDialog>& pDlg ) +{ + DataFlavorExVector aFormats( rData.GetDataFlavorExVector() ); + TransferableObjectDescriptor aDesc; + + SotExchangeDest nDest = SwTransferable::GetSotDestination( rSh ); + + SwTransferable *pClipboard = GetSwTransferable( rData ); + if( pClipboard ) + { + aDesc = pClipboard->m_aObjDesc; + const char* pResId; + if( pClipboard->m_eBufferType & TransferBufferType::Document ) + pResId = STR_PRIVATETEXT; + else if( pClipboard->m_eBufferType & TransferBufferType::Graphic ) + pResId = STR_PRIVATEGRAPHIC; + else if( pClipboard->m_eBufferType == TransferBufferType::Ole ) + pResId = STR_PRIVATEOLE; + else + pResId = nullptr; + + if (pResId) + { + if (strcmp(STR_PRIVATEOLE, pResId) == 0 || strcmp(STR_PRIVATEGRAPHIC, pResId) == 0) + { + // add SotClipboardFormatId::EMBED_SOURCE to the formats. This + // format display then the private format name. + DataFlavorEx aFlavorEx; + aFlavorEx.mnSotId = SotClipboardFormatId::EMBED_SOURCE; + aFormats.insert( aFormats.begin(), aFlavorEx ); + } + pDlg->SetObjName( pClipboard->m_aObjDesc.maClassName, + SwResId(pResId) ); + pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); + } + } + else + { + if( rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) ) + { + (void)rData.GetTransferableObjectDescriptor( + SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc ); + } + + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::EMBED_SOURCE, nDest )) + pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK_SOURCE, nDest )) + pDlg->Insert( SotClipboardFormatId::LINK_SOURCE, OUString() ); + } + + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK, nDest )) + pDlg->Insert( SotClipboardFormatId::LINK, SwResId(STR_DDEFORMAT) ); + + for( SotClipboardFormatId* pIds = aPasteSpecialIds; *pIds != SotClipboardFormatId::NONE; ++pIds ) + if( SwTransferable::TestAllowedFormat( rData, *pIds, nDest )) + pDlg->Insert( *pIds, OUString() ); +} + +void SwTransferable::FillClipFormatItem( const SwWrtShell& rSh, + const TransferableDataHelper& rData, + SvxClipboardFormatItem & rToFill ) +{ + SotExchangeDest nDest = SwTransferable::GetSotDestination( rSh ); + + SwTransferable *pClipboard = GetSwTransferable( rData ); + if( pClipboard ) + { + const char* pResId; + if( pClipboard->m_eBufferType & TransferBufferType::Document ) + pResId = STR_PRIVATETEXT; + else if( pClipboard->m_eBufferType & TransferBufferType::Graphic ) + pResId = STR_PRIVATEGRAPHIC; + else if( pClipboard->m_eBufferType == TransferBufferType::Ole ) + pResId = STR_PRIVATEOLE; + else + pResId = nullptr; + + if (pResId) + rToFill.AddClipbrdFormat(SotClipboardFormatId::EMBED_SOURCE, + SwResId(pResId)); + } + else + { + TransferableObjectDescriptor aDesc; + if (rData.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + (void)const_cast<TransferableDataHelper&>(rData).GetTransferableObjectDescriptor( + SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc); + } + + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::EMBED_SOURCE, nDest )) + rToFill.AddClipbrdFormat( SotClipboardFormatId::EMBED_SOURCE, + aDesc.maTypeName ); + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK_SOURCE, nDest )) + rToFill.AddClipbrdFormat( SotClipboardFormatId::LINK_SOURCE ); + + SotClipboardFormatId nFormat; + if ( rData.HasFormat(nFormat = SotClipboardFormatId::EMBED_SOURCE_OLE) || rData.HasFormat(nFormat = SotClipboardFormatId::EMBEDDED_OBJ_OLE) ) + { + OUString sName,sSource; + if ( SvPasteObjectHelper::GetEmbeddedName(rData,sName,sSource,nFormat) ) + rToFill.AddClipbrdFormat( nFormat, sName ); + } + } + + if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK, nDest )) + rToFill.AddClipbrdFormat( SotClipboardFormatId::LINK, SwResId(STR_DDEFORMAT) ); + + for( SotClipboardFormatId* pIds = aPasteSpecialIds; *pIds != SotClipboardFormatId::NONE; ++pIds ) + if( SwTransferable::TestAllowedFormat( rData, *pIds, nDest )) + rToFill.AddClipbrdFormat(*pIds, OUString()); +} + +void SwTransferable::SetDataForDragAndDrop( const Point& rSttPos ) +{ + if(!m_pWrtShell) + return; + OUString sGrfNm; + const SelectionType nSelection = m_pWrtShell->GetSelectionType(); + if( SelectionType::Graphic == nSelection) + { + AddFormat( SotClipboardFormatId::SVXB ); + const Graphic* pGrf = m_pWrtShell->GetGraphic(); + if ( pGrf && pGrf->IsSupportedGraphic() ) + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + m_eBufferType = TransferBufferType::Graphic; + m_pWrtShell->GetGrfNms( &sGrfNm, nullptr ); + } + else if( SelectionType::Ole == nSelection ) + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + PrepareOLE( m_aObjDesc ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + m_eBufferType = TransferBufferType::Ole; + } + //Is there anything to provide anyway? + else if ( m_pWrtShell->IsSelection() || m_pWrtShell->IsFrameSelected() || + m_pWrtShell->IsObjSelected() ) + { + if( m_pWrtShell->IsObjSelected() ) + m_eBufferType = TransferBufferType::Drawing; + else + { + m_eBufferType = TransferBufferType::Document; + if( SwWrtShell::NO_WORD != + m_pWrtShell->IntelligentCut( nSelection, false )) + m_eBufferType = TransferBufferType::DocumentWord | m_eBufferType; + } + + if( nSelection & SelectionType::TableCell ) + m_eBufferType = TransferBufferType::Table | m_eBufferType; + + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + + //put RTF ahead of the OLE's Metafile for less loss + if( !m_pWrtShell->IsObjSelected() ) + { + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + AddFormat( SotClipboardFormatId::HTML ); + } + if( m_pWrtShell->IsSelection() ) + AddFormat( SotClipboardFormatId::STRING ); + + if( nSelection & ( SelectionType::DrawObject | SelectionType::DbForm )) + { + AddFormat( SotClipboardFormatId::DRAWING ); + if ( nSelection & SelectionType::DrawObject ) + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + m_eBufferType = TransferBufferType::Graphic | m_eBufferType; + + m_pClpGraphic.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_pClpGraphic )) + m_pOrigGraphic = m_pClpGraphic.get(); + m_pClpBitmap.reset(new Graphic); + if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_pClpBitmap )) + m_pOrigGraphic = m_pClpBitmap.get(); + + // is it a URL-Button ? + OUString sURL; + OUString sDesc; + if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) + { + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::FILECONTENT ); + AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + m_eBufferType = TransferBufferType::InetField | m_eBufferType; + } + } + + //ObjectDescriptor was already filled from the old DocShell. + //Now adjust it. Thus in GetData the first query can still + //be answered with delayed rendering. + m_aObjDesc.maDragStartPos = rSttPos; + m_aObjDesc.maSize = OutputDevice::LogicToLogic( Size( OLESIZE ), + MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM)); + PrepareOLE( m_aObjDesc ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + } + else if( nSelection & SelectionType::Text && !m_pWrtShell->HasMark() ) + { + // is only one field - selected? + SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); + Point aPos( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY()); + + if( m_pWrtShell->GetContentAtPos( aPos, aContentAtPos ) ) + { + AddFormat( SotClipboardFormatId::STRING ); + AddFormat( SotClipboardFormatId::SOLK ); + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::FILECONTENT ); + AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); + AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); + m_eBufferType = TransferBufferType::InetField; + } + } + + if( m_pWrtShell->IsFrameSelected() ) + { + SfxItemSet aSet( m_pWrtShell->GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + m_pWrtShell->GetFlyFrameAttr( aSet ); + const SwFormatURL& rURL = aSet.Get( RES_URL ); + if( rURL.GetMap() ) + { + m_pImageMap.reset( new ImageMap( *rURL.GetMap() ) ); + AddFormat( SotClipboardFormatId::SVIM ); + } + else if( !rURL.GetURL().isEmpty() ) + { + m_pTargetURL.reset(new INetImage( sGrfNm, rURL.GetURL(), + rURL.GetTargetFrameName() )); + AddFormat( SotClipboardFormatId::INET_IMAGE ); + } + } +} + +void SwTransferable::StartDrag( vcl::Window* pWin, const Point& rPos ) +{ + if(!m_pWrtShell) + return; + m_bOldIdle = m_pWrtShell->GetViewOptions()->IsIdle(); + m_bCleanUp = true; + + m_pWrtShell->GetViewOptions()->SetIdle( false ); + + if( m_pWrtShell->IsSelFrameMode() ) + m_pWrtShell->ShowCursor(); + + SW_MOD()->m_pDragDrop = this; + + SetDataForDragAndDrop( rPos ); + + sal_Int8 nDragOptions = DND_ACTION_COPYMOVE | DND_ACTION_LINK; + SwDocShell* pDShell = m_pWrtShell->GetView().GetDocShell(); + if( ( pDShell && pDShell->IsReadOnly() ) || m_pWrtShell->HasReadonlySel() ) + nDragOptions &= ~DND_ACTION_MOVE; + + TransferableHelper::StartDrag( pWin, nDragOptions ); +} + +void SwTransferable::DragFinished( sal_Int8 nAction ) +{ + //And the last finishing work so that all statuses are right + if( DND_ACTION_MOVE == nAction ) + { + if( m_bCleanUp ) + { + //It was dropped outside of Writer. We still have to + //delete. + + m_pWrtShell->StartAllAction(); + m_pWrtShell->StartUndo( SwUndoId::UI_DRAG_AND_MOVE ); + if ( m_pWrtShell->IsTableMode() ) + m_pWrtShell->DeleteTableSel(); + else + { + if ( !(m_pWrtShell->IsSelFrameMode() || m_pWrtShell->IsObjSelected()) ) + //SmartCut, take one of the blanks along + m_pWrtShell->IntelligentCut( m_pWrtShell->GetSelectionType() ); + m_pWrtShell->DelRight(); + } + m_pWrtShell->EndUndo( SwUndoId::UI_DRAG_AND_MOVE ); + m_pWrtShell->EndAllAction(); + } + else + { + const SelectionType nSelection = m_pWrtShell->GetSelectionType(); + if( ( SelectionType::Frame | SelectionType::Graphic | + SelectionType::Ole | SelectionType::DrawObject ) & nSelection ) + { + m_pWrtShell->EnterSelFrameMode(); + } + } + } + m_pWrtShell->GetView().GetEditWin().DragFinished(); + + if( m_pWrtShell->IsSelFrameMode() ) + m_pWrtShell->HideCursor(); + else + m_pWrtShell->ShowCursor(); + + m_pWrtShell->GetViewOptions()->SetIdle( m_bOldIdle ); +} + +namespace +{ + +bool lcl_checkClassification(SwDoc* pSourceDoc, SwDoc* pDestinationDoc) +{ + if (!pSourceDoc || !pDestinationDoc) + return true; + + SwDocShell* pSourceShell = pSourceDoc->GetDocShell(); + SwDocShell* pDestinationShell = pDestinationDoc->GetDocShell(); + if (!pSourceShell || !pDestinationShell) + return true; + + SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSourceShell->getDocProperties(), pDestinationShell->getDocProperties()); + return SfxClassificationHelper::ShowPasteInfo(eResult); +} + +} + +bool SwTransferable::PrivatePaste(SwWrtShell& rShell, SwPasteContext* pContext, PasteTableType ePasteTable) +{ + // first, ask for the SelectionType, then action-bracketing !!!! + // (otherwise it's not pasted into a TableSelection!!!) + OSL_ENSURE( !rShell.ActionPend(), "Paste must never have an ActionPend" ); + if ( !m_pClpDocFac ) + return false; // the return value of the SwFEShell::Paste also is bool! + + const SelectionType nSelection = rShell.GetSelectionType(); + + SwTrnsfrActionAndUndo aAction( &rShell ); + + bool bKillPaMs = false; + + //Delete selected content, not at table-selection and table in Clipboard, and don't delete hovering graphics. + if( rShell.HasSelection() && !( nSelection & SelectionType::TableCell) && !( nSelection & SelectionType::DrawObject)) + { + if (!(nSelection & SelectionType::NumberList)) + { + bKillPaMs = true; + rShell.SetRetainSelection( true ); + } + if (pContext) + pContext->forget(); + rShell.DelRight(); + if (pContext) + pContext->remember(); + // when a Fly was selected, a valid cursor position has to be found now + // (parked Cursor!) + if( ( SelectionType::Frame | SelectionType::Graphic | + SelectionType::Ole | SelectionType::DrawObject | + SelectionType::DbForm ) & nSelection ) + { + // position the cursor again + Point aPt( rShell.GetCharRect().Pos() ); + rShell.SwCursorShell::SetCursor( aPt, true ); + } + if (!(nSelection & SelectionType::NumberList)) + { + rShell.SetRetainSelection( false ); + } + } + if ( nSelection & SelectionType::DrawObject) //unselect hovering graphics + { + rShell.ResetSelect(nullptr,false); + } + + bool bInWrd = false, bEndWrd = false, bSttWrd = false, + bSmart(TransferBufferType::DocumentWord & m_eBufferType); + if( bSmart ) + { + // Why not for other Scripts? If TransferBufferType::DocumentWord is set, we have a word + // in the buffer, word in this context means 'something with spaces at beginning + // and end'. In this case we definitely want these spaces to be inserted here. + bInWrd = rShell.IsInWord(); + bEndWrd = rShell.IsEndWrd(); + bSmart = bInWrd || bEndWrd; + if( bSmart ) + { + bSttWrd = rShell.IsStartWord(); + if (!bSttWrd && (bInWrd || bEndWrd)) + rShell.SwEditShell::Insert(' '); + } + } + + bool bRet = true; + // m_pWrtShell is nullptr when the source document is closed already. + if (!m_pWrtShell || lcl_checkClassification(m_pWrtShell->GetDoc(), rShell.GetDoc())) + bRet = rShell.Paste(m_pClpDocFac->GetDoc(), ePasteTable == PasteTableType::PASTE_TABLE); + + if( bKillPaMs ) + rShell.KillPams(); + + // If Smart Paste then insert blank + if( bRet && bSmart && ((bInWrd && !bEndWrd )|| bSttWrd) ) + rShell.SwEditShell::Insert(' '); + + return bRet; +} + +bool SwTransferable::PrivateDrop( SwWrtShell& rSh, const Point& rDragPt, + bool bMove, bool bIsXSelection ) +{ + int cWord = 0; + bool bInWrd = false; + bool bEndWrd = false; + bool bSttWrd = false; + bool bSttPara = false; + bool bTableSel = false; + bool bTableMove = false; + bool bFrameSel = false; + + SwWrtShell& rSrcSh = *GetShell(); + + rSh.UnSetVisibleCursor(); + + if( TransferBufferType::InetField == m_eBufferType ) + { + if( rSh.GetFormatFromObj( rDragPt ) ) + { + INetBookmark aTmp; + if( (TransferBufferType::InetField & m_eBufferType) && m_pBookmark ) + aTmp = *m_pBookmark; + + // select target graphic + if( rSh.SelectObj( rDragPt ) ) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( &rDragPt ); + g_bFrameDrag = true; + } + + const SelectionType nSelection = rSh.GetSelectionType(); + + // not yet consider Draw objects + if( SelectionType::Graphic & nSelection ) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + aURL.SetURL( aTmp.GetURL(), false ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + return true; + } + + if( SelectionType::DrawObject & nSelection ) + { + rSh.LeaveSelFrameMode(); + rSh.UnSelectFrame(); + rSh.ShowCursor(); + g_bFrameDrag = false; + } + } + } + + if( &rSh != &rSrcSh && (SelectionType::Graphic & rSh.GetSelectionType()) && + TransferBufferType::Graphic == m_eBufferType ) + { + // ReRead the graphic + OUString sGrfNm; + OUString sFltNm; + rSrcSh.GetGrfNms( &sGrfNm, &sFltNm ); + rSh.ReRead( sGrfNm, sFltNm, rSrcSh.GetGraphic() ); + return true; + } + + //not in selections or selected frames + if( rSh.TestCurrPam( rDragPt ) || + ( rSh.IsSelFrameMode() && rSh.IsInsideSelectedObj( rDragPt )) ) + return false; + + if( rSrcSh.IsTableMode() ) + { + bTableSel = true; + const SelectionType nSelection = rSrcSh.GetSelectionType(); + // at enhanced table row/column selection or wholly selected tables, + // paste rows above or columns before, and in the case of moving, remove the selection + // (limit only to the single document case temporarily) + if( rSrcSh.GetDoc() == rSh.GetDoc() && + ( (( SelectionType::TableRow | SelectionType::TableCol) & nSelection ) || rSrcSh.HasWholeTabSelection() ) ) + { + bool bTableCol(SelectionType::TableCol & nSelection); + + SwUndoId eUndoId = bMove ? SwUndoId::UI_DRAG_AND_MOVE : SwUndoId::UI_DRAG_AND_COPY; + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, rSrcSh.GetSelDescr()); + + if(rSrcSh.GetDoc() != rSh.GetDoc()) + rSrcSh.StartUndo( eUndoId, &aRewriter ); + rSh.StartUndo( eUndoId, &aRewriter ); + + rSh.StartAction(); + rSrcSh.StartAction(); + + SfxDispatcher* pDispatch = rSrcSh.GetView().GetViewFrame()->GetDispatcher(); + pDispatch->Execute(SID_COPY, SfxCallMode::SYNCHRON); + + rSrcSh.Push(); // save selection for later restoration + rSh.EnterStdMode(); + rSh.SwCursorShell::SetCursor(rDragPt, false); + + // store cursor + ::sw::mark::IMark* pMark = rSh.SetBookmark( + vcl::KeyCode(), + OUString(), + IDocumentMarkAccess::MarkType::UNO_BOOKMARK ); + + rSrcSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // restore selection... + + // delete source rows/columns + if (bMove) + pDispatch->Execute(bTableCol ? FN_TABLE_DELETE_COL : FN_TABLE_DELETE_ROW, SfxCallMode::SYNCHRON); + + // restore cursor position + if (pMark != nullptr) + { + rSh.GotoMark( pMark ); + rSh.getIDocumentMarkAccess()->deleteMark( pMark ); + } + + // paste rows above/columns before + pDispatch->Execute(bTableCol ? FN_TABLE_PASTE_COL_BEFORE : FN_TABLE_PASTE_ROW_BEFORE, SfxCallMode::SYNCHRON); + + if( rSrcSh.GetDoc() != rSh.GetDoc() ) + rSrcSh.EndUndo(); + + rSh.DestroyCursor(); + rSh.EndUndo(); + rSh.EndAction(); + rSh.EndAction(); + return true; + } + + if ( bMove && rSrcSh.HasWholeTabSelection() ) + bTableMove = true; + } + else if( rSrcSh.IsSelFrameMode() || rSrcSh.IsObjSelected() ) + { + // don't move position-protected objects! + if( bMove && rSrcSh.IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE ) + return false; + + bFrameSel = true; + } + + const SelectionType nSel = rSrcSh.GetSelectionType(); + + SwUndoId eUndoId = bMove ? SwUndoId::UI_DRAG_AND_MOVE : SwUndoId::UI_DRAG_AND_COPY; + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, rSrcSh.GetSelDescr()); + + if(rSrcSh.GetDoc() != rSh.GetDoc()) + rSrcSh.StartUndo( eUndoId, &aRewriter ); + rSh.StartUndo( eUndoId, &aRewriter ); + + rSh.StartAction(); + rSrcSh.StartAction(); + + if( &rSrcSh != &rSh ) + { + rSh.EnterStdMode(); + rSh.SwCursorShell::SetCursor( rDragPt, true ); + cWord = rSrcSh.IntelligentCut( nSel, false ); + } + else if( !bTableSel && !bFrameSel ) + { + if( !rSh.IsAddMode() ) + { + // #i87233# + if ( rSh.IsBlockMode() ) + { + // preserve order of cursors for block mode + rSh.GoPrevCursor(); + } + + rSh.SwCursorShell::CreateCursor(); + } + rSh.SwCursorShell::SetCursor( rDragPt, true, false ); + rSh.GoPrevCursor(); + cWord = rSh.IntelligentCut( rSh.GetSelectionType(), false ); + rSh.GoNextCursor(); + } + + bInWrd = rSh.IsInWord(); + bEndWrd = rSh.IsEndWrd(); + bSttWrd = !bEndWrd && rSh.IsStartWord(); + bSttPara= rSh.IsSttPara(); + + Point aSttPt( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY() ); + + // at first, select InetFields! + if( TransferBufferType::InetField == m_eBufferType ) + { + if( &rSrcSh == &rSh ) + { + rSh.GoPrevCursor(); + rSh.SwCursorShell::SetCursor( aSttPt, true ); + rSh.SelectTextAttr( RES_TXTATR_INETFMT ); + if( rSh.TestCurrPam( rDragPt ) ) + { + // don't copy/move inside of yourself + rSh.DestroyCursor(); + rSh.EndUndo(); + rSh.EndAction(); + rSh.EndAction(); + return false; + } + rSh.GoNextCursor(); + } + else + { + rSrcSh.SwCursorShell::SetCursor( aSttPt, true ); + rSrcSh.SelectTextAttr( RES_TXTATR_INETFMT ); + } + + // is there a URL attribute at the insert point? Then replace that, + // so simply put up a selection? + rSh.DelINetAttrWithText(); + g_bDDINetAttr = true; + } + + if ( rSrcSh.IsSelFrameMode() ) + { + //Hack: fool the special treatment + aSttPt = rSrcSh.GetObjRect().Pos(); + } + + bool bRet = rSrcSh.SwFEShell::Copy( &rSh, aSttPt, rDragPt, bMove, + !bIsXSelection ); + + if( !bIsXSelection ) + { + rSrcSh.Push(); + if ( bRet && bMove && !bFrameSel ) + { + if ( bTableSel ) + { + /* delete table contents not cells */ + rSrcSh.Delete(); + } + else + { + //SmartCut, take one of the blanks along. + rSh.SwCursorShell::DestroyCursor(); + if ( cWord == SwWrtShell::WORD_SPACE_BEFORE ) + rSh.ExtendSelection( false ); + else if ( cWord == SwWrtShell::WORD_SPACE_AFTER ) + rSh.ExtendSelection(); + rSrcSh.DelRight(); + } + } + rSrcSh.KillPams(); + rSrcSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + + /* after dragging a table selection inside one shell + set cursor to the drop position. */ + if( &rSh == &rSrcSh && ( bTableSel || rSh.IsBlockMode() ) ) + { + rSrcSh.CalcLayout(); + rSrcSh.SwCursorShell::SetCursor(rDragPt); + rSrcSh.GetSwCursor()->SetMark(); + } + } + + if( bRet && !bTableSel && !bFrameSel ) + { + if( (bInWrd || bEndWrd) && + (cWord == SwWrtShell::WORD_SPACE_AFTER || + cWord == SwWrtShell::WORD_SPACE_BEFORE) ) + { + if ( bSttWrd || (bInWrd && !bEndWrd)) + rSh.SwEditShell::Insert(' ', bIsXSelection); + if ( !bSttWrd || (bInWrd && !bSttPara) ) + { + rSh.SwapPam(); + if ( !bSttWrd ) + rSh.SwEditShell::Insert(' ', bIsXSelection); + rSh.SwapPam(); + } + } + + if( bIsXSelection ) + { + if( &rSrcSh == &rSh && !rSh.IsAddMode() ) + { + rSh.SwCursorShell::DestroyCursor(); + rSh.GoPrevCursor(); + } + else + { + rSh.SwapPam(); + rSh.SwCursorShell::ClearMark(); + } + } + else + { + if( rSh.IsAddMode() ) + rSh.SwCursorShell::CreateCursor(); + else + { + // turn on selection mode + rSh.SttSelect(); + rSh.EndSelect(); + } + } + } + else if ( bRet && bTableMove ) + { + SfxDispatcher* pDispatch = rSrcSh.GetView().GetViewFrame()->GetDispatcher(); + pDispatch->Execute(FN_TABLE_DELETE_TABLE, SfxCallMode::SYNCHRON); + } + + if( bRet && bMove && bFrameSel ) + rSrcSh.LeaveSelFrameMode(); + + if( rSrcSh.GetDoc() != rSh.GetDoc() ) + rSrcSh.EndUndo(); + rSh.EndUndo(); + + // put the shell in the right state + if( &rSrcSh != &rSh && ( rSh.IsFrameSelected() || rSh.IsObjSelected() )) + rSh.EnterSelFrameMode(); + + rSrcSh.EndAction(); + rSh.EndAction(); + return true; +} + +// Interfaces for Selection +void SwTransferable::CreateSelection( SwWrtShell& rSh, + const SwFrameShell * _pCreatorView ) +{ + SwModule *pMod = SW_MOD(); + rtl::Reference<SwTransferable> pNew = new SwTransferable( rSh ); + + pNew->m_pCreatorView = _pCreatorView; + + pMod->m_pXSelection = pNew.get(); + pNew->CopyToSelection( rSh.GetWin() ); +} + +void SwTransferable::ClearSelection( SwWrtShell& rSh, + const SwFrameShell * _pCreatorView) +{ + SwModule *pMod = SW_MOD(); + if( pMod->m_pXSelection && + ((!pMod->m_pXSelection->m_pWrtShell) || (pMod->m_pXSelection->m_pWrtShell == &rSh)) && + (!_pCreatorView || (pMod->m_pXSelection->m_pCreatorView == _pCreatorView)) ) + { + TransferableHelper::ClearSelection( rSh.GetWin() ); + } +} + +namespace +{ + class theSwTransferableUnoTunnelId : public rtl::Static< UnoTunnelIdInit, SwTransferable > {}; +} + +const Sequence< sal_Int8 >& SwTransferable::getUnoTunnelId() +{ + return theSwTransferableUnoTunnelId::get().getSeq(); +} + +sal_Int64 SwTransferable::getSomething( const Sequence< sal_Int8 >& rId ) +{ + sal_Int64 nRet; + if( isUnoTunnelId<SwTransferable>(rId) ) + { + nRet = sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ) ); + } + else + nRet = TransferableHelper::getSomething(rId); + return nRet; +} + +SwTransferable* SwTransferable::GetSwTransferable( const TransferableDataHelper& rData ) +{ + return comphelper::getUnoTunnelImplementation<SwTransferable>(rData.GetTransferable()); +} + +SwTransferDdeLink::SwTransferDdeLink( SwTransferable& rTrans, SwWrtShell& rSh ) + : rTrnsfr(rTrans) + , pDocShell(nullptr) + , nOldTimeOut(0) + , bDelBookmrk(false) + , bInDisconnect(false) +{ + // we only end up here with table- or text selection + if( SelectionType::TableCell & rSh.GetSelectionType() ) + { + SwFrameFormat* pFormat = rSh.GetTableFormat(); + if( pFormat ) + sName = pFormat->GetName(); + } + else + { + // creating a temp. bookmark without undo + bool bUndo = rSh.DoesUndo(); + rSh.DoUndo( false ); + bool bIsModified = rSh.IsModified(); + + ::sw::mark::IMark* pMark = rSh.SetBookmark( + vcl::KeyCode(), + OUString(), + IDocumentMarkAccess::MarkType::DDE_BOOKMARK); + if(pMark) + { + sName = pMark->GetName(); + bDelBookmrk = true; + if( !bIsModified ) + rSh.ResetModified(); + } + else + sName.clear(); + rSh.DoUndo( bUndo ); + } + + if( !sName.isEmpty() && + nullptr != ( pDocShell = rSh.GetDoc()->GetDocShell() ) ) + { + // then we create our "server" and connect to it + refObj = pDocShell->DdeCreateLinkSource( sName ); + if( refObj.is() ) + { + refObj->AddConnectAdvise( this ); + refObj->AddDataAdvise( this, + OUString(), + ADVISEMODE_NODATA | ADVISEMODE_ONLYONCE ); + nOldTimeOut = refObj->GetUpdateTimeout(); + refObj->SetUpdateTimeout( 0 ); + } + } +} + +SwTransferDdeLink::~SwTransferDdeLink() +{ + if( refObj.is() ) + Disconnect( true ); +} + +::sfx2::SvBaseLink::UpdateResult SwTransferDdeLink::DataChanged( const OUString& , + const uno::Any& ) +{ + // well, that's it with the link + if( !bInDisconnect ) + { + if( FindDocShell() && pDocShell->GetView() ) + rTrnsfr.RemoveDDELinkFormat( pDocShell->GetView()->GetEditWin() ); + Disconnect( false ); + } + return SUCCESS; +} + +bool SwTransferDdeLink::WriteData( SvStream& rStrm ) +{ + if( !refObj.is() || !FindDocShell() ) + return false; + + rtl_TextEncoding eEncoding = DDE_TXT_ENCODING; + const OString aAppNm(OUStringToOString( + Application::GetAppName(), eEncoding)); + const OString aTopic(OUStringToOString( + pDocShell->GetTitle(SFX_TITLE_FULLNAME), eEncoding)); + const OString aName(OUStringToOString(sName, eEncoding)); + + std::unique_ptr<char[]> pMem(new char[ aAppNm.getLength() + aTopic.getLength() + aName.getLength() + 4 ]); + + sal_Int32 nLen = aAppNm.getLength(); + memcpy( pMem.get(), aAppNm.getStr(), nLen ); + pMem[ nLen++ ] = 0; + memcpy( pMem.get() + nLen, aTopic.getStr(), aTopic.getLength() ); + nLen = nLen + aTopic.getLength(); + pMem[ nLen++ ] = 0; + memcpy( pMem.get() + nLen, aName.getStr(), aName.getLength() ); + nLen = nLen + aName.getLength(); + pMem[ nLen++ ] = 0; + pMem[ nLen++ ] = 0; + + rStrm.WriteBytes( pMem.get(), nLen ); + pMem.reset(); + + IDocumentMarkAccess* const pMarkAccess = pDocShell->GetDoc()->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->findMark(sName); + if(ppMark != pMarkAccess->getAllMarksEnd() + && IDocumentMarkAccess::GetType(**ppMark) != IDocumentMarkAccess::MarkType::BOOKMARK) + { + // the mark is still a DdeBookmark + // we replace it with a Bookmark, so it will get saved etc. + ::sw::mark::IMark* const pMark = *ppMark; + ::sfx2::SvLinkSource* p = refObj.get(); + SwServerObject& rServerObject = dynamic_cast<SwServerObject&>(*p); + + // collecting state of old mark + SwPaM aPaM(pMark->GetMarkStart()); + *aPaM.GetPoint() = pMark->GetMarkStart(); + if(pMark->IsExpanded()) + { + aPaM.SetMark(); + *aPaM.GetMark() = pMark->GetMarkEnd(); + } + OUString sMarkName = pMark->GetName(); + + // remove mark + rServerObject.SetNoServer(); // this removes the connection between SwServerObject and mark + // N.B. ppMark was not loaded from file and cannot have xml:id + pMarkAccess->deleteMark(ppMark); + + // recreate as Bookmark + ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark( + aPaM, + sMarkName, + IDocumentMarkAccess::MarkType::BOOKMARK, + ::sw::mark::InsertMode::New); + rServerObject.SetDdeBookmark(*pNewMark); + } + + bDelBookmrk = false; + return true; +} + +void SwTransferDdeLink::Disconnect( bool bRemoveDataAdvise ) +{ + // don't accept DataChanged anymore, when already in Disconnect! + // (DTOR from Bookmark sends a DataChanged!) + bool bOldDisconnect = bInDisconnect; + bInDisconnect = true; + + // destroy the unused bookmark again (without Undo!)? + if( bDelBookmrk && refObj.is() && FindDocShell() ) + { + SwDoc* pDoc = pDocShell->GetDoc(); + ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); + + // #i58448# + Link<bool,void> aSavedOle2Link( pDoc->GetOle2Link() ); + pDoc->SetOle2Link( Link<bool,void>() ); + + bool bIsModified = pDoc->getIDocumentState().IsModified(); + + IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(sName)); + + if( !bIsModified ) + pDoc->getIDocumentState().ResetModified(); + // #i58448# + pDoc->SetOle2Link( aSavedOle2Link ); + + bDelBookmrk = false; + } + + if( refObj.is() ) + { + refObj->SetUpdateTimeout( nOldTimeOut ); + refObj->RemoveConnectAdvise( this ); + if( bRemoveDataAdvise ) + // in a DataChanged the SelectionObject must NEVER be deleted + // is already handled by the base class + // (ADVISEMODE_ONLYONCE!!!!) + // but always in normal Disconnect! + refObj->RemoveAllDataAdvise( this ); + refObj.clear(); + } + bInDisconnect = bOldDisconnect; +} + +bool SwTransferDdeLink::FindDocShell() +{ + SfxObjectShell* pTmpSh = SfxObjectShell::GetFirst( checkSfxObjectShell<SwDocShell> ); + while( pTmpSh ) + { + if( pTmpSh == pDocShell ) // that's what we want to have + { + if( pDocShell->GetDoc() ) + return true; + break; // the Doc is not there anymore, so leave! + } + pTmpSh = SfxObjectShell::GetNext( *pTmpSh, checkSfxObjectShell<SwDocShell> ); + } + + pDocShell = nullptr; + return false; +} + +void SwTransferDdeLink::Closed() +{ + if( !bInDisconnect && refObj.is() ) + { + refObj->RemoveAllDataAdvise( this ); + refObj->RemoveConnectAdvise( this ); + refObj.clear(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnchorOverlayObject.cxx b/sw/source/uibase/docvw/AnchorOverlayObject.cxx new file mode 100644 index 000000000..af5a3de41 --- /dev/null +++ b/sw/source/uibase/docvw/AnchorOverlayObject.cxx @@ -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 . + */ + +#include "AnchorOverlayObject.hxx" +#include <SidebarWindowsConsts.hxx> + +#include <swrect.hxx> +#include <view.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdview.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> + +#include <sw_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/primitivetools2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> + +namespace sw::sidebarwindows { + +namespace { + +// helper class: Primitive for discrete visualisation +class AnchorPrimitive : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D +{ +private: + basegfx::B2DPolygon maTriangle; + basegfx::B2DPolygon maLine; + basegfx::B2DPolygon maLineTop; + const AnchorState maAnchorState; + basegfx::BColor maColor; + + // discrete line width + double mfDiscreteLineWidth; + + bool mbLineSolid : 1; + +protected: + virtual void create2DDecomposition( + drawinglayer::primitive2d::Primitive2DContainer& rContainer, + const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override; + +public: + AnchorPrimitive( const basegfx::B2DPolygon& rTriangle, + const basegfx::B2DPolygon& rLine, + const basegfx::B2DPolygon& rLineTop, + AnchorState aAnchorState, + const basegfx::BColor& rColor, + double fDiscreteLineWidth, + bool bLineSolid ) + : drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D(), + maTriangle(rTriangle), + maLine(rLine), + maLineTop(rLineTop), + maAnchorState(aAnchorState), + maColor(rColor), + mfDiscreteLineWidth(fDiscreteLineWidth), + mbLineSolid(bLineSolid) + {} + + // data access + const basegfx::B2DPolygon& getLine() const { return maLine; } + const basegfx::BColor& getColor() const { return maColor; } + bool getLineSolid() const { return mbLineSolid; } + + virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const override; + + virtual sal_uInt32 getPrimitive2DID() const override; +}; + +} + +void AnchorPrimitive::create2DDecomposition( + drawinglayer::primitive2d::Primitive2DContainer& rContainer, + const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if ( AnchorState::Tri == maAnchorState || + AnchorState::All == maAnchorState ) + { + // create triangle + const drawinglayer::primitive2d::Primitive2DReference aTriangle( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(maTriangle), + getColor())); + + rContainer.push_back(aTriangle); + } + + // prepare view-independent LineWidth and color + const drawinglayer::attribute::LineAttribute aLineAttribute( + getColor(), + mfDiscreteLineWidth * getDiscreteUnit()); + + if ( AnchorState::All == maAnchorState ) + { + // create line start + if(getLineSolid()) + { + const drawinglayer::primitive2d::Primitive2DReference aSolidLine( + new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + getLine(), + aLineAttribute)); + + rContainer.push_back(aSolidLine); + } + else + { + std::vector< double > aDotDashArray; + const double fDistance(3.0 * 15.0); + const double fDashLen(5.0 * 15.0); + + aDotDashArray.push_back(fDashLen); + aDotDashArray.push_back(fDistance); + + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute( + aDotDashArray, + fDistance + fDashLen); + + const drawinglayer::primitive2d::Primitive2DReference aStrokedLine( + new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + getLine(), + aLineAttribute, + aStrokeAttribute)); + + rContainer.push_back(aStrokedLine); + } + } + + if ( AnchorState::All == maAnchorState || + AnchorState::End == maAnchorState ) + { + // LineTop has to be created, too, but uses no shadow, so add after + // the other parts are created + const drawinglayer::primitive2d::Primitive2DReference aLineTop( + new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + maLineTop, + aLineAttribute)); + + rContainer.push_back(aLineTop); + } +} + +bool AnchorPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const +{ + if(drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + { + const AnchorPrimitive& rCompare = static_cast< const AnchorPrimitive& >(rPrimitive); + + return (maTriangle == rCompare.maTriangle + && getLine() == rCompare.getLine() + && maLineTop == rCompare.maLineTop + && maAnchorState == rCompare.maAnchorState + && getColor() == rCompare.getColor() + && mfDiscreteLineWidth == rCompare.mfDiscreteLineWidth + && getLineSolid() == rCompare.getLineSolid()); + } + + return false; +} + +ImplPrimitive2DIDBlock(AnchorPrimitive, PRIMITIVE2D_ID_SWSIDEBARANCHORPRIMITIVE) + +/*static*/ std::unique_ptr<AnchorOverlayObject> AnchorOverlayObject::CreateAnchorOverlayObject( + SwView const & rDocView, + const SwRect& aAnchorRect, + long aPageBorder, + const Point& aLineStart, + const Point& aLineEnd, + const Color& aColorAnchor ) +{ + std::unique_ptr<AnchorOverlayObject> pAnchorOverlayObject; + if ( rDocView.GetDrawView() ) + { + SdrPaintWindow* pPaintWindow = rDocView.GetDrawView()->GetPaintWindow(0); + if( pPaintWindow ) + { + const rtl::Reference< sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager(); + + if ( xOverlayManager.is() ) + { + pAnchorOverlayObject.reset(new AnchorOverlayObject( + basegfx::B2DPoint( aAnchorRect.Left() , aAnchorRect.Bottom()-5*15), + basegfx::B2DPoint( aAnchorRect.Left()-5*15 , aAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( aAnchorRect.Left()+5*15 , aAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( aAnchorRect.Left(), aAnchorRect.Bottom()+2*15), + basegfx::B2DPoint( aPageBorder ,aAnchorRect.Bottom()+2*15), + basegfx::B2DPoint( aLineStart.X(),aLineStart.Y()), + basegfx::B2DPoint( aLineEnd.X(),aLineEnd.Y()) , + aColorAnchor)); + xOverlayManager->add(*pAnchorOverlayObject); + } + } + } + + return pAnchorOverlayObject; +} + +AnchorOverlayObject::AnchorOverlayObject( const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPos, + const basegfx::B2DPoint& rThirdPos, + const basegfx::B2DPoint& rFourthPos, + const basegfx::B2DPoint& rFifthPos, + const basegfx::B2DPoint& rSixthPos, + const basegfx::B2DPoint& rSeventhPos, + const Color& rBaseColor) + : OverlayObjectWithBasePosition(rBasePos, rBaseColor) + , maSecondPosition(rSecondPos) + , maThirdPosition(rThirdPos) + , maFourthPosition(rFourthPos) + , maFifthPosition(rFifthPos) + , maSixthPosition(rSixthPos) + , maSeventhPosition(rSeventhPos) + , maTriangle() + , maLine() + , maLineTop() + , mAnchorState(AnchorState::All) + , mbLineSolid(false) +{ +} + +AnchorOverlayObject::~AnchorOverlayObject() +{ + if ( getOverlayManager() ) + { + // remove this object from the chain + getOverlayManager()->remove(*this); + } +} + +void AnchorOverlayObject::implEnsureGeometry() +{ + if(!maTriangle.count()) + { + maTriangle.append(getBasePosition()); + maTriangle.append(GetSecondPosition()); + maTriangle.append(GetThirdPosition()); + maTriangle.setClosed(true); + } + + if(!maLine.count()) + { + maLine.append(GetFourthPosition()); + maLine.append(GetFifthPosition()); + maLine.append(GetSixthPosition()); + } + + if(!maLineTop.count()) + { + maLineTop.append(GetSixthPosition()); + maLineTop.append(GetSeventhPosition()); + } +} + +void AnchorOverlayObject::implResetGeometry() +{ + maTriangle.clear(); + maLine.clear(); + maLineTop.clear(); +} + +drawinglayer::primitive2d::Primitive2DContainer AnchorOverlayObject::createOverlayObjectPrimitive2DSequence() +{ + implEnsureGeometry(); + + static const double aDiscreteLineWidth(1.6); + const drawinglayer::primitive2d::Primitive2DReference aReference( + new AnchorPrimitive( maTriangle, + maLine, + maLineTop, + GetAnchorState(), + getBaseColor().getBColor(), + ANCHORLINE_WIDTH * aDiscreteLineWidth, + getLineSolid()) ); + + return drawinglayer::primitive2d::Primitive2DContainer { aReference }; +} + +void AnchorOverlayObject::SetAllPosition( const basegfx::B2DPoint& rPoint1, + const basegfx::B2DPoint& rPoint2, + const basegfx::B2DPoint& rPoint3, + const basegfx::B2DPoint& rPoint4, + const basegfx::B2DPoint& rPoint5, + const basegfx::B2DPoint& rPoint6, + const basegfx::B2DPoint& rPoint7) +{ + if ( rPoint1 != getBasePosition() || + rPoint2 != GetSecondPosition() || + rPoint3 != GetThirdPosition() || + rPoint4 != GetFourthPosition() || + rPoint5 != GetFifthPosition() || + rPoint6 != GetSixthPosition() || + rPoint7 != GetSeventhPosition() ) + { + maBasePosition = rPoint1; + maSecondPosition = rPoint2; + maThirdPosition = rPoint3; + maFourthPosition = rPoint4; + maFifthPosition = rPoint5; + maSixthPosition = rPoint6; + maSeventhPosition = rPoint7; + + implResetGeometry(); + objectChange(); + } +} + +void AnchorOverlayObject::SetSixthPosition(const basegfx::B2DPoint& rNew) +{ + if(rNew != maSixthPosition) + { + maSixthPosition = rNew; + implResetGeometry(); + objectChange(); + } +} + +void AnchorOverlayObject::SetSeventhPosition(const basegfx::B2DPoint& rNew) +{ + if(rNew != maSeventhPosition) + { + maSeventhPosition = rNew; + implResetGeometry(); + objectChange(); + } +} + +void AnchorOverlayObject::SetTriPosition(const basegfx::B2DPoint& rPoint1,const basegfx::B2DPoint& rPoint2,const basegfx::B2DPoint& rPoint3, + const basegfx::B2DPoint& rPoint4,const basegfx::B2DPoint& rPoint5) +{ + if(rPoint1 != getBasePosition() + || rPoint2 != GetSecondPosition() + || rPoint3 != GetThirdPosition() + || rPoint4 != GetFourthPosition() + || rPoint5 != GetFifthPosition()) + { + maBasePosition = rPoint1; + maSecondPosition = rPoint2; + maThirdPosition = rPoint3; + maFourthPosition = rPoint4; + maFifthPosition = rPoint5; + + implResetGeometry(); + objectChange(); + } +} + +void AnchorOverlayObject::setLineSolid( const bool bNew ) +{ + if ( bNew != getLineSolid() ) + { + mbLineSolid = bNew; + objectChange(); + } +} + +void AnchorOverlayObject::SetAnchorState( const AnchorState aState) +{ + if ( mAnchorState != aState) + { + mAnchorState = aState; + objectChange(); + } +} + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnchorOverlayObject.hxx b/sw/source/uibase/docvw/AnchorOverlayObject.hxx new file mode 100644 index 000000000..180d01757 --- /dev/null +++ b/sw/source/uibase/docvw/AnchorOverlayObject.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_DOCVW_ANCHOROVERLAYOBJECT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_ANCHOROVERLAYOBJECT_HXX + +#include <svx/sdr/overlay/overlayobject.hxx> + +#include <basegfx/polygon/b2dpolygon.hxx> + +class SwView; +class SwRect; +class Point; + +namespace sw::sidebarwindows { + +enum class AnchorState +{ + All, + End, + Tri +}; + +class AnchorOverlayObject final : public sdr::overlay::OverlayObjectWithBasePosition +{ + public: + static std::unique_ptr<AnchorOverlayObject> CreateAnchorOverlayObject( SwView const & rDocView, + const SwRect& aAnchorRect, + long aPageBorder, + const Point& aLineStart, + const Point& aLineEnd, + const Color& aColorAnchor ); + + const basegfx::B2DPoint& GetSecondPosition() const { return maSecondPosition; } + const basegfx::B2DPoint& GetThirdPosition() const { return maThirdPosition; } + const basegfx::B2DPoint& GetFourthPosition() const { return maFourthPosition; } + const basegfx::B2DPoint& GetFifthPosition() const { return maFifthPosition; } + const basegfx::B2DPoint& GetSixthPosition() const { return maSixthPosition; } + const basegfx::B2DPoint& GetSeventhPosition() const { return maSeventhPosition; } + + void SetAllPosition( const basegfx::B2DPoint& rPoint1, + const basegfx::B2DPoint& rPoint2, + const basegfx::B2DPoint& rPoint3, + const basegfx::B2DPoint& rPoint4, + const basegfx::B2DPoint& rPoint5, + const basegfx::B2DPoint& rPoint6, + const basegfx::B2DPoint& rPoint7 ); + void SetTriPosition( const basegfx::B2DPoint& rPoint1, + const basegfx::B2DPoint& rPoint2, + const basegfx::B2DPoint& rPoint3, + const basegfx::B2DPoint& rPoint4, + const basegfx::B2DPoint& rPoint5 ); + void SetSixthPosition( const basegfx::B2DPoint& rNew ); + void SetSeventhPosition( const basegfx::B2DPoint& rNew ); + + void setLineSolid( const bool bNew ); + bool getLineSolid() const { return mbLineSolid; } + + void SetAnchorState( const AnchorState aState ); + AnchorState GetAnchorState() const { return mAnchorState; } + + private: + /* 6------------7 + 1 / + /4\ ---------------5 + 2 - 3 + */ + + basegfx::B2DPoint maSecondPosition; + basegfx::B2DPoint maThirdPosition; + basegfx::B2DPoint maFourthPosition; + basegfx::B2DPoint maFifthPosition; + basegfx::B2DPoint maSixthPosition; + basegfx::B2DPoint maSeventhPosition; + + // helpers to fill and reset geometry + void implEnsureGeometry(); + void implResetGeometry(); + + // geometry creation for OverlayObject + virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override; + + // object's geometry + basegfx::B2DPolygon maTriangle; + basegfx::B2DPolygon maLine; + basegfx::B2DPolygon maLineTop; + AnchorState mAnchorState; + + bool mbLineSolid : 1; + + AnchorOverlayObject( const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPos, + const basegfx::B2DPoint& rThirdPos, + const basegfx::B2DPoint& rFourthPos, + const basegfx::B2DPoint& rFifthPos, + const basegfx::B2DPoint& rSixthPos, + const basegfx::B2DPoint& rSeventhPos, + const Color& rBaseColor ); + public: + virtual ~AnchorOverlayObject() override; +}; + +} // end of namespace sw::annotation + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnnotationMenuButton.cxx b/sw/source/uibase/docvw/AnnotationMenuButton.cxx new file mode 100644 index 000000000..847905385 --- /dev/null +++ b/sw/source/uibase/docvw/AnnotationMenuButton.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 "AnnotationMenuButton.hxx" + +#include <strings.hrc> + +#include <unotools/useroptions.hxx> + +#include <vcl/menu.hxx> +#include <vcl/decoview.hxx> +#include <vcl/gradient.hxx> +#include <vcl/settings.hxx> +#include <vcl/event.hxx> + +#include <cmdid.h> +#include <AnnotationWin.hxx> +#include <swtypes.hxx> + +namespace sw::annotation { + +static Color ColorFromAlphaColor(const sal_uInt8 aTransparency, const Color& aFront, const Color& aBack) +{ + return Color(sal_uInt8(aFront.GetRed() * aTransparency / 255.0 + aBack.GetRed() * (1 - aTransparency / 255.0)), + sal_uInt8(aFront.GetGreen() * aTransparency / 255.0 + aBack.GetGreen() * (1 - aTransparency / 255.0)), + sal_uInt8(aFront.GetBlue() * aTransparency / 255.0 + aBack.GetBlue() * (1 - aTransparency / 255.0))); +} + +AnnotationMenuButton::AnnotationMenuButton(sw::annotation::SwAnnotationWin& rSidebarWin) + : MenuButton(&rSidebarWin) + , mrSidebarWin(rSidebarWin) +{ + AddEventListener(LINK(&mrSidebarWin, sw::annotation::SwAnnotationWin, WindowEventListener)); + + SetAccessibleName(SwResId(STR_ACCESS_ANNOTATION_BUTTON_NAME)); + SetAccessibleDescription(SwResId(STR_ACCESS_ANNOTATION_BUTTON_DESC)); + SetQuickHelpText(GetAccessibleDescription()); +} + +AnnotationMenuButton::~AnnotationMenuButton() +{ + disposeOnce(); +} + +void AnnotationMenuButton::dispose() +{ + RemoveEventListener(LINK(&mrSidebarWin, sw::annotation::SwAnnotationWin, WindowEventListener)); + MenuButton::dispose(); +} + +void AnnotationMenuButton::Select() +{ + OString sIdent = GetCurItemIdent(); + if (sIdent.isEmpty()) + return; + + // tdf#136682 ensure this is the currently active sidebar win so the command + // operates in an active sidebar context + bool bSwitchedFocus = mrSidebarWin.SetActiveSidebarWin(); + + if (sIdent == "reply") + mrSidebarWin.ExecuteCommand(FN_REPLY); + if (sIdent == "resolve" || sIdent == "unresolve") + mrSidebarWin.ExecuteCommand(FN_RESOLVE_NOTE); + else if (sIdent == "delete") + mrSidebarWin.ExecuteCommand(FN_DELETE_COMMENT); + else if (sIdent == "deleteby") + mrSidebarWin.ExecuteCommand(FN_DELETE_NOTE_AUTHOR); + else if (sIdent == "deleteall") + mrSidebarWin.ExecuteCommand(FN_DELETE_ALL_NOTES); + else if (sIdent == "formatall") + mrSidebarWin.ExecuteCommand(FN_FORMAT_ALL_NOTES); + + if (bSwitchedFocus) + mrSidebarWin.UnsetActiveSidebarWin(); + mrSidebarWin.GrabFocusToDocument(); +} + +void AnnotationMenuButton::MouseButtonDown( const MouseEvent& rMEvt ) +{ + PopupMenu* pButtonPopup(GetPopupMenu()); + if (mrSidebarWin.IsReadOnly()) + { + pButtonPopup->EnableItem(pButtonPopup->GetItemId("reply"), false); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("resolve"), false); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("unresolve"), false); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("delete"), false ); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("deleteby"), false ); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("deleteall"), false ); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("formatall"), false ); + } + else + { + pButtonPopup->EnableItem(pButtonPopup->GetItemId("resolve"), !mrSidebarWin.IsResolved()); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("unresolve"), mrSidebarWin.IsResolved()); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("delete"), !mrSidebarWin.IsProtected()); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("deleteby")); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("deleteall")); + pButtonPopup->EnableItem(pButtonPopup->GetItemId("formatall")); + } + + if (mrSidebarWin.IsProtected()) + { + pButtonPopup->EnableItem(pButtonPopup->GetItemId("reply"), false); + } + else + { + SvtUserOptions aUserOpt; + OUString sAuthor; + if ((sAuthor = aUserOpt.GetFullName()).isEmpty()) + { + if ((sAuthor = aUserOpt.GetID()).isEmpty()) + { + sAuthor = SwResId(STR_REDLINE_UNKNOWN_AUTHOR); + } + } + // do not allow to reply to ourself and no answer possible if this note is in a protected section + if (sAuthor == mrSidebarWin.GetAuthor()) + { + pButtonPopup->EnableItem(pButtonPopup->GetItemId("reply"), false); + } + else + { + pButtonPopup->EnableItem(pButtonPopup->GetItemId("reply")); + } + } + + MenuButton::MouseButtonDown(rMEvt); +} + +void AnnotationMenuButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) +{ + bool bHighContrast = rRenderContext.GetSettings().GetStyleSettings().GetHighContrastMode(); + + if (bHighContrast) + rRenderContext.SetFillColor(COL_BLACK); + else + rRenderContext.SetFillColor(mrSidebarWin.ColorDark()); + rRenderContext.SetLineColor(); + const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + rRenderContext.DrawRect(aRect); + + if (bHighContrast) + { + //draw rect around button + rRenderContext.SetFillColor(COL_BLACK); + rRenderContext.SetLineColor(COL_WHITE); + } + else + { + //draw button + Gradient aGradient; + if (IsMouseOver()) + aGradient = Gradient(GradientStyle::Linear, + ColorFromAlphaColor(80, mrSidebarWin.ColorAnchor(), mrSidebarWin.ColorDark()), + ColorFromAlphaColor(15, mrSidebarWin.ColorAnchor(), mrSidebarWin.ColorDark())); + else + aGradient = Gradient(GradientStyle::Linear, + ColorFromAlphaColor(15, mrSidebarWin.ColorAnchor(), mrSidebarWin.ColorDark()), + ColorFromAlphaColor(80, mrSidebarWin.ColorAnchor(), mrSidebarWin.ColorDark())); + rRenderContext.DrawGradient(aRect, aGradient); + + //draw rect around button + rRenderContext.SetFillColor(); + rRenderContext.SetLineColor(ColorFromAlphaColor(90, mrSidebarWin.ColorAnchor(), mrSidebarWin.ColorDark())); + } + rRenderContext.DrawRect(aRect); + + tools::Rectangle aSymbolRect(aRect); + // 25% distance to the left and right button border + const long nBorderDistanceLeftAndRight = ((aSymbolRect.GetWidth() * 250) + 500) / 1000; + aSymbolRect.AdjustLeft(nBorderDistanceLeftAndRight ); + aSymbolRect.AdjustRight( -nBorderDistanceLeftAndRight ); + // 40% distance to the top button border + const long nBorderDistanceTop = ((aSymbolRect.GetHeight() * 400) + 500) / 1000; + aSymbolRect.AdjustTop(nBorderDistanceTop ); + // 15% distance to the bottom button border + const long nBorderDistanceBottom = ((aSymbolRect.GetHeight() * 150) + 500) / 1000; + aSymbolRect.AdjustBottom( -nBorderDistanceBottom ); + DecorationView aDecoView(&rRenderContext); + aDecoView.DrawSymbol(aSymbolRect, SymbolType::SPIN_DOWN, (bHighContrast ? COL_WHITE : COL_BLACK)); +} + +void AnnotationMenuButton::KeyInput(const KeyEvent& rKeyEvt) +{ + const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); + if (rKeyCode.GetCode() == KEY_TAB) + { + mrSidebarWin.ActivatePostIt(); + mrSidebarWin.GrabFocus(); + } + else + { + MenuButton::KeyInput(rKeyEvt); + } +} + +} // end of namespace sw::annotation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnnotationMenuButton.hxx b/sw/source/uibase/docvw/AnnotationMenuButton.hxx new file mode 100644 index 000000000..2e8ce9221 --- /dev/null +++ b/sw/source/uibase/docvw/AnnotationMenuButton.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_UIBASE_DOCVW_ANNOTATIONMENUBUTTON_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_ANNOTATIONMENUBUTTON_HXX + +#include <vcl/menubtn.hxx> + +namespace sw::annotation { + class SwAnnotationWin; +} + +namespace sw::annotation { + +class AnnotationMenuButton : public MenuButton +{ + public: + AnnotationMenuButton( sw::annotation::SwAnnotationWin& rSidebarWin ); + virtual ~AnnotationMenuButton() override; + virtual void dispose() override; + + // override MenuButton methods + virtual void Select() override; + + // override vcl::Window methods + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + virtual void KeyInput( const KeyEvent& rKeyEvt ) override; + + private: + sw::annotation::SwAnnotationWin& mrSidebarWin; +}; + +} // end of namespace sw::annotation + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnnotationWin.cxx b/sw/source/uibase/docvw/AnnotationWin.cxx new file mode 100644 index 000000000..da900ec54 --- /dev/null +++ b/sw/source/uibase/docvw/AnnotationWin.cxx @@ -0,0 +1,507 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <AnnotationWin.hxx> + +#include "AnnotationMenuButton.hxx" +#include <PostItMgr.hxx> + +#include <strings.hrc> + +#include <vcl/edit.hxx> +#include <vcl/menu.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/svapp.hxx> + +#include <svl/undo.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <svl/languageoptions.hxx> + +#include <editeng/eeitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/langitem.hxx> + +#include <editeng/editview.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editobj.hxx> +#include <editeng/outlobj.hxx> + +#include <comphelper/lok.hxx> +#include <docufld.hxx> +#include <txtfld.hxx> +#include <ndtxt.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <SwUndoField.hxx> +#include <edtwin.hxx> +#include "ShadowOverlayObject.hxx" +#include "AnchorOverlayObject.hxx" +#include "OverlayRanges.hxx" +#include "SidebarTxtControl.hxx" + +#include <memory> + +namespace sw::annotation { + +SwAnnotationWin::SwAnnotationWin( SwEditWin& rEditWin, + SwPostItMgr& aMgr, + SwSidebarItem& rSidebarItem, + SwFormatField* aField ) + : Window(&rEditWin) + , maBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/annotationmenu.ui", "") + , mrMgr(aMgr) + , mrView(rEditWin.GetView()) + , mnEventId(nullptr) + , mpSidebarTextControl(nullptr) + , mpVScrollbar(nullptr) + , mpMetadataAuthor(nullptr) + , mpMetadataDate(nullptr) + , mpMenuButton(nullptr) + , mColorAnchor() + , mColorDark() + , mColorLight() + , mChangeColor() + , meSidebarPosition(sw::sidebarwindows::SidebarPosition::NONE) + , mPosSize() + , mAnchorRect() + , mPageBorder(0) + , mbAnchorRectChanged(false) + , mbResolvedStateUpdated(false) + , mbMouseOver(false) + , mLayoutStatus(SwPostItHelper::INVISIBLE) + , mbReadonly(false) + , mbIsFollow(false) + , mrSidebarItem(rSidebarItem) + , mpAnchorFrame(rSidebarItem.maLayoutInfo.mpAnchorFrame) + , mpFormatField(aField) + , mpField( static_cast<SwPostItField*>(aField->GetField())) + , mpButtonPopup(nullptr) +{ + mpShadow = sidebarwindows::ShadowOverlayObject::CreateShadowOverlayObject( mrView ); + if ( mpShadow ) + { + mpShadow->setVisible(false); + } + + mrMgr.ConnectSidebarWinToFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame), + mrSidebarItem.GetFormatField(), + *this ); + + if (SupportsDoubleBuffering()) + // When double-buffering, allow parents to paint on our area. That's + // necessary when parents paint the complete buffer. + SetParentClipMode(ParentClipMode::NoClip); +} + +SwAnnotationWin::~SwAnnotationWin() +{ + disposeOnce(); +} + +void SwAnnotationWin::dispose() +{ + mpButtonPopup.clear(); + maBuilder.disposeBuilder(); + + if (IsDisposed()) + return; + + mrMgr.DisconnectSidebarWinFromFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame), + *this ); + + Disable(); + + if ( mpSidebarTextControl ) + { + if ( mpOutlinerView ) + { + mpOutlinerView->SetWindow( nullptr ); + } + } + mpSidebarTextControl.disposeAndClear(); + + mpOutlinerView.reset(); + mpOutliner.reset(); + + if (mpMetadataAuthor) + { + mpMetadataAuthor->RemoveEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + } + mpMetadataAuthor.disposeAndClear(); + + if (mpMetadataResolved) + { + mpMetadataResolved->RemoveEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + } + mpMetadataResolved.disposeAndClear(); + + if (mpMetadataDate) + { + mpMetadataDate->RemoveEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + } + mpMetadataDate.disposeAndClear(); + + if (mpVScrollbar) + { + mpVScrollbar->RemoveEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + } + mpVScrollbar.disposeAndClear(); + + RemoveEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + + mpAnchor.reset(); + mpShadow.reset(); + + mpTextRangeOverlay.reset(); + + mpMenuButton.disposeAndClear(); + + if (mnEventId) + Application::RemoveUserEvent( mnEventId ); + + vcl::Window::dispose(); +} + +void SwAnnotationWin::SetPostItText() +{ + //If the cursor was visible, then make it visible again after + //changing text, e.g. fdo#33599 + vcl::Cursor *pCursor = GetOutlinerView()->GetEditView().GetCursor(); + bool bCursorVisible = pCursor && pCursor->IsVisible(); + + //If the new text is the same as the old text, keep the same insertion + //point .e.g. fdo#33599 + mpField = static_cast<SwPostItField*>(mpFormatField->GetField()); + OUString sNewText = mpField->GetPar2(); + bool bTextUnchanged = sNewText == mpOutliner->GetEditEngine().GetText(); + ESelection aOrigSelection(GetOutlinerView()->GetEditView().GetSelection()); + + // get text from SwPostItField and insert into our textview + mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); + mpOutliner->EnableUndo( false ); + if( mpField->GetTextObject() ) + mpOutliner->SetText( *mpField->GetTextObject() ); + else + { + mpOutliner->Clear(); + GetOutlinerView()->SetAttribs(DefaultItem()); + GetOutlinerView()->InsertText(sNewText); + } + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + mpOutliner->EnableUndo( true ); + mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) ); + if (bTextUnchanged) + GetOutlinerView()->GetEditView().SetSelection(aOrigSelection); + if (bCursorVisible) + GetOutlinerView()->ShowCursor(); + Invalidate(); +} + +void SwAnnotationWin::SetResolved(bool resolved) +{ + bool oldState = IsResolved(); + static_cast<SwPostItField*>(mpFormatField->GetField())->SetResolved(resolved); + const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions(); + mrSidebarItem.bShow = !IsResolved() || (pVOpt->IsResolvedPostIts()); + + mpTextRangeOverlay.reset(); + + if(IsResolved()) + mpMetadataResolved->Show(); + else + mpMetadataResolved->Hide(); + + if(IsResolved() != oldState) + mbResolvedStateUpdated = true; + UpdateData(); + Invalidate(); +} + +void SwAnnotationWin::ToggleResolved() +{ + SetResolved(!IsResolved()); +} + +void SwAnnotationWin::ToggleResolvedForThread() +{ + GetTopReplyNote()->ToggleResolved(); + mrMgr.UpdateResolvedStatus(GetTopReplyNote()); + mrMgr.LayoutPostIts(); +} + +bool SwAnnotationWin::IsResolved() const +{ + return static_cast<SwPostItField*>(mpFormatField->GetField())->GetResolved(); +} + +bool SwAnnotationWin::IsThreadResolved() +{ + // Not const because GetTopReplyNote isn't. + return GetTopReplyNote()->IsResolved(); +} + +void SwAnnotationWin::UpdateData() +{ + if ( mpOutliner->IsModified() || mbResolvedStateUpdated ) + { + IDocumentUndoRedo & rUndoRedo( + mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo()); + std::unique_ptr<SwField> pOldField; + if (rUndoRedo.DoesUndo()) + { + pOldField = mpField->Copy(); + } + mpField->SetPar2(mpOutliner->GetEditEngine().GetText()); + mpField->SetTextObject(mpOutliner->CreateParaObject()); + if (rUndoRedo.DoesUndo()) + { + SwTextField *const pTextField = mpFormatField->GetTextField(); + SwPosition aPosition( pTextField->GetTextNode() ); + aPosition.nContent = pTextField->GetStart(); + rUndoRedo.AppendUndo( + std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, nullptr, true)); + } + // so we get a new layout of notes (anchor position is still the same and we would otherwise not get one) + mrMgr.SetLayout(); + // #i98686# if we have several views, all notes should update their text + if(mbResolvedStateUpdated) + mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::RESOLVED)); + else + mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::CHANGED)); + mrView.GetDocShell()->SetModified(); + } + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + mbResolvedStateUpdated = false; +} + +void SwAnnotationWin::Delete() +{ + if (mrView.GetWrtShellPtr()->GotoField(*mpFormatField)) + { + if ( mrMgr.GetActiveSidebarWin() == this) + { + mrMgr.SetActiveSidebarWin(nullptr); + // if the note is empty, the previous line will send a delete event, but we are already there + if (mnEventId) + { + Application::RemoveUserEvent( mnEventId ); + mnEventId = nullptr; + } + } + // we delete the field directly, the Mgr cleans up the PostIt by listening + GrabFocusToDocument(); + mrView.GetWrtShellPtr()->ClearMark(); + mrView.GetWrtShellPtr()->DelRight(); + } +} + +void SwAnnotationWin::GotoPos() +{ + mrView.GetDocShell()->GetWrtShell()->GotoField(*mpFormatField); +} + +sal_uInt32 SwAnnotationWin::MoveCaret() +{ + // if this is an answer, do not skip over all following ones, but insert directly behind the current one + // but when just leaving a note, skip all following ones as well to continue typing + return mrMgr.IsAnswer() + ? 1 + : 1 + CountFollowing(); +} + +// returns a non-zero postit parent id, if exists, otherwise 0 for root comments +sal_uInt32 SwAnnotationWin::CalcParent() +{ + SwTextField* pTextField = mpFormatField->GetTextField(); + SwPosition aPosition( pTextField->GetTextNode() ); + aPosition.nContent = pTextField->GetStart(); + SwTextAttr * const pTextAttr = + pTextField->GetTextNode().GetTextAttrForCharAt( + aPosition.nContent.GetIndex() - 1, + RES_TXTATR_ANNOTATION ); + const SwField* pField = pTextAttr ? pTextAttr->GetFormatField().GetField() : nullptr; + sal_uInt32 nParentId = 0; + if (pField && pField->Which() == SwFieldIds::Postit) + { + const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField); + nParentId = pPostItField->GetPostItId(); + } + return nParentId; +} + +// counts how many SwPostItField we have right after the current one +sal_uInt32 SwAnnotationWin::CountFollowing() +{ + sal_uInt32 aCount = 1; // we start with 1, so we have to subtract one at the end again + SwTextField* pTextField = mpFormatField->GetTextField(); + SwPosition aPosition( pTextField->GetTextNode() ); + aPosition.nContent = pTextField->GetStart(); + + SwTextAttr * pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt( + aPosition.nContent.GetIndex() + 1, + RES_TXTATR_ANNOTATION ); + SwField* pField = pTextAttr + ? const_cast<SwField*>(pTextAttr->GetFormatField().GetField()) + : nullptr; + while ( pField && ( pField->Which()== SwFieldIds::Postit ) ) + { + aCount++; + pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt( + aPosition.nContent.GetIndex() + aCount, + RES_TXTATR_ANNOTATION ); + pField = pTextAttr + ? const_cast<SwField*>(pTextAttr->GetFormatField().GetField()) + : nullptr; + } + return aCount - 1; +} + +VclPtr<MenuButton> SwAnnotationWin::CreateMenuButton() +{ + mpButtonPopup = maBuilder.get_menu("menu"); + sal_uInt16 nByAuthorId = mpButtonPopup->GetItemId("deleteby"); + OUString aText = mpButtonPopup->GetItemText(nByAuthorId); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1,GetAuthor()); + aText = aRewriter.Apply(aText); + mpButtonPopup->SetItemText(nByAuthorId, aText); + VclPtrInstance<AnnotationMenuButton> pMenuButton( *this ); + pMenuButton->SetPopupMenu( mpButtonPopup ); + pMenuButton->Show(); + return pMenuButton; +} + +void SwAnnotationWin::InitAnswer(OutlinerParaObject const * pText) +{ + // If tiled annotations is off in lok case, skip adding additional reply text. + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + return; + + //collect our old meta data + SwAnnotationWin* pWin = mrMgr.GetNextPostIt(KEY_PAGEUP, this); + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, pWin->GetAuthor()); + const OUString aText = aRewriter.Apply(SwResId(STR_REPLY)) + + " (" + rLocalData.getDate( pWin->GetDate()) + + ", " + rLocalData.getTime( pWin->GetTime(), false) + + "): \""; + GetOutlinerView()->InsertText(aText); + + // insert old, selected text or "..." + // TODO: iterate over all paragraphs, not only first one to find out if it is empty + if (!pText->GetTextObject().GetText(0).isEmpty()) + GetOutlinerView()->GetEditView().InsertText(pText->GetTextObject()); + else + GetOutlinerView()->InsertText("..."); + GetOutlinerView()->InsertText("\"\n"); + + GetOutlinerView()->SetSelection(ESelection(0,0,EE_PARA_ALL,EE_TEXTPOS_ALL)); + SfxItemSet aAnswerSet( mrView.GetDocShell()->GetPool() ); + aAnswerSet.Put(SvxFontHeightItem(200,80,EE_CHAR_FONTHEIGHT)); + aAnswerSet.Put(SvxPostureItem(ITALIC_NORMAL,EE_CHAR_ITALIC)); + GetOutlinerView()->SetAttribs(aAnswerSet); + GetOutlinerView()->SetSelection(ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT)); + + //remove all attributes and reset our standard ones + GetOutlinerView()->GetEditView().RemoveAttribsKeepLanguages(true); + GetOutlinerView()->SetAttribs(DefaultItem()); + // lets insert an undo step so the initial text can be easily deleted + // but do not use UpdateData() directly, would set modified state again and reentrance into Mgr + mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); + IDocumentUndoRedo & rUndoRedo( + mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo()); + std::unique_ptr<SwField> pOldField; + if (rUndoRedo.DoesUndo()) + { + pOldField = mpField->Copy(); + } + mpField->SetPar2(mpOutliner->GetEditEngine().GetText()); + mpField->SetTextObject(mpOutliner->CreateParaObject()); + if (rUndoRedo.DoesUndo()) + { + SwTextField *const pTextField = mpFormatField->GetTextField(); + SwPosition aPosition( pTextField->GetTextNode() ); + aPosition.nContent = pTextField->GetStart(); + rUndoRedo.AppendUndo( + std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, nullptr, true)); + } + mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) ); + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); +} + +void SwAnnotationWin::UpdateText(const OUString& aText) +{ + mpOutliner->Clear(); + GetOutlinerView()->InsertText(aText); + UpdateData(); +} + +SvxLanguageItem SwAnnotationWin::GetLanguage() const +{ + // set initial language for outliner + SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( mpField->GetLanguage() ); + sal_uInt16 nLangWhichId = 0; + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE ; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: OSL_FAIL("GetLanguage: wrong script type"); + } + return SvxLanguageItem(mpField->GetLanguage(),nLangWhichId); +} + +bool SwAnnotationWin::IsProtected() const +{ + return mbReadonly || + GetLayoutStatus() == SwPostItHelper::DELETED || + ( mpFormatField && mpFormatField->IsProtect() ); +} + +OUString SwAnnotationWin::GetAuthor() const +{ + return mpField->GetPar1(); +} + +Date SwAnnotationWin::GetDate() const +{ + return mpField->GetDate(); +} + +tools::Time SwAnnotationWin::GetTime() const +{ + return mpField->GetTime(); +} + +} // end of namespace sw::annotation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/AnnotationWin2.cxx b/sw/source/uibase/docvw/AnnotationWin2.cxx new file mode 100644 index 000000000..9c3539f2e --- /dev/null +++ b/sw/source/uibase/docvw/AnnotationWin2.cxx @@ -0,0 +1,1624 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cstddef> + +#include "SidebarWinAcc.hxx" +#include <PostItMgr.hxx> +#include <AnnotationWin.hxx> +#include <IDocumentUndoRedo.hxx> +#include <basegfx/range/b2drange.hxx> +#include "SidebarTxtControl.hxx" +#include "SidebarScrollBar.hxx" +#include "AnchorOverlayObject.hxx" +#include "ShadowOverlayObject.hxx" +#include "OverlayRanges.hxx" + +#include <strings.hrc> + +#include <viewopt.hxx> +#include <cmdid.h> + +#include <editeng/fhgtitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editeng.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> + +#include <svl/undo.hxx> +#include <svl/stritem.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> + +#include <vcl/edit.hxx> +#include <vcl/event.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/svapp.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> + +#include <edtwin.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <swmodule.hxx> + +#include <txtannotationfld.hxx> +#include <ndtxt.hxx> + +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <unotools/localedatawrapper.hxx> +#include <memory> +#include <comphelper/lok.hxx> + +using namespace sw::sidebarwindows; + +namespace +{ + +/// Translate absolute <-> relative twips: LOK wants absolute coordinates as output and gives absolute coordinates as input. +void lcl_translateTwips(vcl::Window const & rParent, vcl::Window& rChild, MouseEvent* pMouseEvent) +{ + // Set map mode, so that callback payloads will contain absolute coordinates instead of relative ones. + Point aOffset(rChild.GetOutOffXPixel() - rParent.GetOutOffXPixel(), rChild.GetOutOffYPixel() - rParent.GetOutOffYPixel()); + if (!rChild.IsMapModeEnabled()) + { + MapMode aMapMode(rChild.GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapTwip); + aMapMode.SetScaleX(rParent.GetMapMode().GetScaleX()); + aMapMode.SetScaleY(rParent.GetMapMode().GetScaleY()); + rChild.SetMapMode(aMapMode); + rChild.EnableMapMode(); + } + aOffset = rChild.PixelToLogic(aOffset); + MapMode aMapMode(rChild.GetMapMode()); + aMapMode.SetOrigin(aOffset); + aMapMode.SetMapUnit(rParent.GetMapMode().GetMapUnit()); + rChild.SetMapMode(aMapMode); + rChild.EnableMapMode(false); + + if (pMouseEvent) + { + // Set event coordinates, so they contain relative coordinates instead of absolute ones. + Point aPos = pMouseEvent->GetPosPixel(); + aPos.Move(-aOffset.getX(), -aOffset.getY()); + MouseEvent aMouseEvent(aPos, pMouseEvent->GetClicks(), pMouseEvent->GetMode(), pMouseEvent->GetButtons(), pMouseEvent->GetModifier()); + *pMouseEvent = aMouseEvent; + } +} + +/// Decide which one from the children of rParent should get rMouseEvent. +vcl::Window* lcl_getHitWindow(sw::annotation::SwAnnotationWin& rParent, const MouseEvent& rMouseEvent) +{ + vcl::Window* pRet = nullptr; + + rParent.EditWin().Push(PushFlags::MAPMODE); + rParent.EditWin().EnableMapMode(); + for (sal_Int16 i = rParent.GetChildCount() - 1; i >= 0; --i) + { + vcl::Window* pChild = rParent.GetChild(i); + + Point aPosition(rParent.GetPosPixel()); + aPosition.Move(pChild->GetPosPixel().getX(), pChild->GetPosPixel().getY()); + Size aSize(rParent.GetSizePixel()); + tools::Rectangle aRectangleLogic(rParent.EditWin().PixelToLogic(aPosition), rParent.EditWin().PixelToLogic(aSize)); + if (aRectangleLogic.IsInside(rMouseEvent.GetPosPixel())) + { + pRet = pChild; + break; + } + } + rParent.EditWin().Pop(); + return pRet; +} + +} + +namespace sw::annotation { + +#define METABUTTON_WIDTH 16 +#define METABUTTON_HEIGHT 18 +#define METABUTTON_AREA_WIDTH 30 +#define POSTIT_META_FIELD_HEIGHT sal_Int32(15) +#define POSTIT_MINIMUMSIZE_WITHOUT_META 50 + + +void SwAnnotationWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + Window::Paint(rRenderContext, rRect); + + if (mpMetadataAuthor->IsVisible()) + { + //draw left over space + if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + rRenderContext.SetFillColor(COL_BLACK); + } + else + { + rRenderContext.SetFillColor(mColorDark); + } + + sal_uInt32 boxHeight = mpMetadataAuthor->GetSizePixel().Height() + mpMetadataDate->GetSizePixel().Height(); + boxHeight += IsThreadResolved() ? mpMetadataResolved->GetSizePixel().Height() : 0; + + rRenderContext.SetLineColor(); + tools::Rectangle aRectangle(Point(mpMetadataAuthor->GetPosPixel().X() + mpMetadataAuthor->GetSizePixel().Width(), + mpMetadataAuthor->GetPosPixel().Y()), + Size(GetMetaButtonAreaWidth(), boxHeight)); + + if (comphelper::LibreOfficeKit::isActive()) + aRectangle = rRect; + else + aRectangle = PixelToLogic(aRectangle); + rRenderContext.DrawRect(aRectangle); + } +} + +void SwAnnotationWin::PaintTile(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + Paint(rRenderContext, rRect); + + for (sal_uInt16 i = 0; i < GetChildCount(); ++i) + { + vcl::Window* pChild = GetChild(i); + + // No point in showing this button till click on it are not handled. + if (pChild == mpMenuButton.get()) + continue; + + if (!pChild->IsVisible()) + continue; + + rRenderContext.Push(PushFlags::MAPMODE); + Point aOffset(PixelToLogic(pChild->GetPosPixel())); + MapMode aMapMode(rRenderContext.GetMapMode()); + aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset); + rRenderContext.SetMapMode(aMapMode); + + bool bPopChild = false; + if (pChild->GetMapMode().GetMapUnit() != rRenderContext.GetMapMode().GetMapUnit()) + { + // This is needed for the scrollbar that has its map unit in pixels. + pChild->Push(PushFlags::MAPMODE); + bPopChild = true; + pChild->EnableMapMode(); + aMapMode = pChild->GetMapMode(); + aMapMode.SetMapUnit(rRenderContext.GetMapMode().GetMapUnit()); + aMapMode.SetScaleX(rRenderContext.GetMapMode().GetScaleX()); + aMapMode.SetScaleY(rRenderContext.GetMapMode().GetScaleY()); + pChild->SetMapMode(aMapMode); + } + + pChild->Paint(rRenderContext, rRect); + + if (bPopChild) + pChild->Pop(); + rRenderContext.Pop(); + } + + const drawinglayer::geometry::ViewInformation2D aViewInformation; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aViewInformation)); + + // drawinglayer sets the map mode to pixels, not needed here. + rRenderContext.Pop(); + // Work in document-global twips. + rRenderContext.Pop(); + if (mpAnchor) + pProcessor->process(mpAnchor->getOverlayObjectPrimitive2DSequence()); + if (mpTextRangeOverlay) + pProcessor->process(mpTextRangeOverlay->getOverlayObjectPrimitive2DSequence()); + + rRenderContext.Push(PushFlags::NONE); + pProcessor.reset(); + rRenderContext.Push(PushFlags::NONE); +} + +bool SwAnnotationWin::IsHitWindow(const Point& rPointLogic) +{ + tools::Rectangle aRectangleLogic(EditWin().PixelToLogic(GetPosPixel()), EditWin().PixelToLogic(GetSizePixel())); + return aRectangleLogic.IsInside(rPointLogic); +} + +void SwAnnotationWin::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark) +{ + mpSidebarTextControl->Push(PushFlags::MAPMODE); + MouseEvent aMouseEvent(rPosition); + lcl_translateTwips(EditWin(), *mpSidebarTextControl, &aMouseEvent); + Point aPosition(aMouseEvent.GetPosPixel()); + + EditView& rEditView = GetOutlinerView()->GetEditView(); + rEditView.SetCursorLogicPosition(aPosition, bPoint, bClearMark); + + mpSidebarTextControl->Pop(); +} + +void SwAnnotationWin::Draw(OutputDevice* pDev, const Point& rPt, DrawFlags nInFlags) +{ + Size aSz = pDev->PixelToLogic(GetSizePixel()); + + if (mpMetadataAuthor->IsVisible() ) + { + pDev->SetFillColor(mColorDark); + pDev->SetLineColor(); + pDev->DrawRect( tools::Rectangle( rPt, aSz ) ); + } + + if (mpMetadataAuthor->IsVisible()) + { + vcl::Font aOrigFont(mpMetadataAuthor->GetControlFont()); + Point aPos(PixelToLogic(mpMetadataAuthor->GetPosPixel())); + aPos += rPt; + vcl::Font aFont( mpMetadataAuthor->GetSettings().GetStyleSettings().GetFieldFont() ); + mpMetadataAuthor->SetControlFont( aFont ); + mpMetadataAuthor->Draw(pDev, aPos, nInFlags); + mpMetadataAuthor->SetControlFont( aOrigFont ); + } + + if (mpMetadataDate->IsVisible()) + { + vcl::Font aOrigFont(mpMetadataDate->GetControlFont()); + Point aPos(PixelToLogic(mpMetadataDate->GetPosPixel())); + aPos += rPt; + vcl::Font aFont( mpMetadataDate->GetSettings().GetStyleSettings().GetFieldFont() ); + mpMetadataDate->SetControlFont( aFont ); + mpMetadataDate->SetControlFont( aOrigFont ); + } + + if (mpMetadataResolved->IsVisible()) + { + vcl::Font aOrigFont(mpMetadataResolved->GetControlFont()); + Point aPos(PixelToLogic(mpMetadataResolved->GetPosPixel())); + aPos += rPt; + vcl::Font aFont( mpMetadataResolved->GetSettings().GetStyleSettings().GetFieldFont() ); + mpMetadataResolved->SetControlFont( aFont ); + mpMetadataResolved->SetControlFont( aOrigFont ); + } + + Size aOrigSize(mpSidebarTextControl->GetSizePixel()); + mpSidebarTextControl->SetSizePixel(aSz); + mpSidebarTextControl->Draw(pDev, rPt, nInFlags); + mpSidebarTextControl->SetSizePixel(aOrigSize); + + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice( + *pDev, aNewViewInfos )); + + if (mpAnchor) + pProcessor->process(mpAnchor->getOverlayObjectPrimitive2DSequence()); + if (mpTextRangeOverlay) + pProcessor->process(mpTextRangeOverlay->getOverlayObjectPrimitive2DSequence()); + pProcessor.reset(); + + if (!mpVScrollbar->IsVisible()) + return; + + vcl::Font aOrigFont(mpMetadataDate->GetControlFont()); + Color aOrigBg( mpMetadataDate->GetControlBackground() ); + OUString sOrigText(mpMetadataDate->GetText()); + + Point aPos(PixelToLogic(mpMenuButton->GetPosPixel())); + aPos += rPt; + + vcl::Font aFont( mpMetadataDate->GetSettings().GetStyleSettings().GetFieldFont() ); + mpMetadataDate->SetControlFont( aFont ); + mpMetadataDate->SetControlBackground( Color(0xFFFFFF) ); + mpMetadataDate->SetText("..."); + aOrigSize = mpMetadataDate->GetSizePixel(); + mpMetadataDate->SetSizePixel(mpMenuButton->GetSizePixel()); + mpMetadataDate->Draw(pDev, aPos, nInFlags); + mpMetadataDate->SetSizePixel(aOrigSize); + + mpMetadataDate->SetText(sOrigText); + mpMetadataDate->SetControlFont( aOrigFont ); + mpMetadataDate->SetControlBackground( aOrigBg ); +} + +void SwAnnotationWin::KeyInput(const KeyEvent& rKeyEvent) +{ + if (mpSidebarTextControl) + { + mpSidebarTextControl->Push(PushFlags::MAPMODE); + lcl_translateTwips(EditWin(), *mpSidebarTextControl, nullptr); + + mpSidebarTextControl->KeyInput(rKeyEvent); + + mpSidebarTextControl->Pop(); + } +} + +void SwAnnotationWin::MouseMove(const MouseEvent& rMouseEvent) +{ + if (vcl::Window* pHit = lcl_getHitWindow(*this, rMouseEvent)) + { + pHit->Push(PushFlags::MAPMODE); + MouseEvent aMouseEvent(rMouseEvent); + lcl_translateTwips(EditWin(), *pHit, &aMouseEvent); + + pHit->MouseMove(aMouseEvent); + + pHit->Pop(); + } +} + +void SwAnnotationWin::MouseButtonDown(const MouseEvent& rMouseEvent) +{ + if (vcl::Window* pHit = lcl_getHitWindow(*this, rMouseEvent)) + { + pHit->Push(PushFlags::MAPMODE); + MouseEvent aMouseEvent(rMouseEvent); + lcl_translateTwips(EditWin(), *pHit, &aMouseEvent); + + pHit->MouseButtonDown(aMouseEvent); + + pHit->Pop(); + } +} + +void SwAnnotationWin::MouseButtonUp(const MouseEvent& rMouseEvent) +{ + if (vcl::Window* pHit = lcl_getHitWindow(*this, rMouseEvent)) + { + pHit->Push(PushFlags::MAPMODE); + MouseEvent aMouseEvent(rMouseEvent); + lcl_translateTwips(EditWin(), *pHit, &aMouseEvent); + + pHit->MouseButtonUp(aMouseEvent); + + pHit->Pop(); + } +} + +void SwAnnotationWin::SetPosSizePixelRect(long nX, long nY, long nWidth, long nHeight, + const SwRect& aAnchorRect, const long aPageBorder) +{ + mPosSize = tools::Rectangle(Point(nX,nY),Size(nWidth,nHeight)); + if (!mAnchorRect.IsEmpty() && mAnchorRect != aAnchorRect) + mbAnchorRectChanged = true; + mAnchorRect = aAnchorRect; + mPageBorder = aPageBorder; +} + +void SwAnnotationWin::SetSize( const Size& rNewSize ) +{ + mPosSize.SetSize(rNewSize); +} + +void SwAnnotationWin::SetVirtualPosSize( const Point& aPoint, const Size& aSize) +{ + mPosSize = tools::Rectangle(aPoint,aSize); +} + +void SwAnnotationWin::TranslateTopPosition(const long aAmount) +{ + mPosSize.Move(0,aAmount); +} + +void SwAnnotationWin::ShowAnchorOnly(const Point &aPoint) +{ + HideNote(); + SetPosAndSize(); + if (mpAnchor) + { + mpAnchor->SetSixthPosition(basegfx::B2DPoint(aPoint.X(),aPoint.Y())); + mpAnchor->SetSeventhPosition(basegfx::B2DPoint(aPoint.X(),aPoint.Y())); + mpAnchor->SetAnchorState(AnchorState::All); + mpAnchor->setVisible(true); + } + if (mpShadow) + mpShadow->setVisible(false); +} + +SfxItemSet SwAnnotationWin::DefaultItem() +{ + SfxItemSet aItem( mrView.GetDocShell()->GetPool() ); + aItem.Put(SvxFontHeightItem(200,100,EE_CHAR_FONTHEIGHT)); + return aItem; +} + +void SwAnnotationWin::InitControls() +{ + AddEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + + // actual window which holds the user text + mpSidebarTextControl = VclPtr<SidebarTextControl>::Create( *this, + WB_NODIALOGCONTROL, + mrView, mrMgr ); + mpSidebarTextControl->SetPointer(PointerStyle::Text); + + // window controls for author and date + mpMetadataAuthor = VclPtr<Edit>::Create( this, 0 ); + mpMetadataAuthor->SetAccessibleName( SwResId( STR_ACCESS_ANNOTATION_AUTHOR_NAME ) ); + mpMetadataAuthor->EnableRTL(AllSettings::GetLayoutRTL()); + mpMetadataAuthor->SetReadOnly(); + mpMetadataAuthor->AlwaysDisableInput(true); + mpMetadataAuthor->SetCallHandlersOnInputDisabled(true); + mpMetadataAuthor->AddEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + // we should leave this setting alone, but for this we need a better layout algo + // with variable meta size height + { + AllSettings aSettings = mpMetadataAuthor->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + vcl::Font aFont = aStyleSettings.GetFieldFont(); + aFont.SetFontHeight(8); + aStyleSettings.SetFieldFont(aFont); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataAuthor->SetSettings(aSettings); + } + + mpMetadataDate = VclPtr<Edit>::Create( this, 0 ); + mpMetadataDate->SetAccessibleName( SwResId( STR_ACCESS_ANNOTATION_DATE_NAME ) ); + mpMetadataDate->EnableRTL(AllSettings::GetLayoutRTL()); + mpMetadataDate->SetReadOnly(); + mpMetadataDate->AlwaysDisableInput(true); + mpMetadataDate->SetCallHandlersOnInputDisabled(true); + mpMetadataDate->AddEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + // we should leave this setting alone, but for this we need a better layout algo + // with variable meta size height + { + AllSettings aSettings = mpMetadataDate->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + vcl::Font aFont = aStyleSettings.GetFieldFont(); + aFont.SetFontHeight(8); + aStyleSettings.SetFieldFont(aFont); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataDate->SetSettings(aSettings); + } + + mpMetadataResolved = VclPtr<Edit>::Create( this, 0 ); + mpMetadataResolved->SetAccessibleName( SwResId( STR_ACCESS_ANNOTATION_RESOLVED_NAME ) ); + mpMetadataResolved->EnableRTL(AllSettings::GetLayoutRTL()); + mpMetadataResolved->SetReadOnly(); + mpMetadataResolved->AlwaysDisableInput(true); + mpMetadataResolved->SetCallHandlersOnInputDisabled(true); + mpMetadataResolved->AddEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + // we should leave this setting alone, but for this we need a better layout algo + // with variable meta size height + { + AllSettings aSettings = mpMetadataResolved->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + vcl::Font aFont = aStyleSettings.GetFieldFont(); + aFont.SetFontHeight(8); + aStyleSettings.SetFieldFont(aFont); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataResolved->SetSettings(aSettings); + mpMetadataResolved->SetText( SwResId( STR_ACCESS_ANNOTATION_RESOLVED_NAME ) ); + } + + SwDocShell* aShell = mrView.GetDocShell(); + mpOutliner.reset(new Outliner(&aShell->GetPool(),OutlinerMode::TextObject)); + aShell->GetDoc()->SetCalcFieldValueHdl( mpOutliner.get() ); + mpOutliner->SetUpdateMode( true ); + Rescale(); + + mpSidebarTextControl->EnableRTL( false ); + mpOutlinerView.reset(new OutlinerView ( mpOutliner.get(), mpSidebarTextControl )); + mpOutlinerView->SetBackgroundColor(COL_TRANSPARENT); + mpOutliner->InsertView(mpOutlinerView.get() ); + mpOutlinerView->SetOutputArea( PixelToLogic( tools::Rectangle(0,0,1,1) ) ); + + mpOutlinerView->SetAttribs(DefaultItem()); + + if (comphelper::LibreOfficeKit::isActive()) + { + // If there is a callback already registered, inform the new outliner view about it. + mpOutlinerView->RegisterViewShell(&mrView); + } + + //create Scrollbars + mpVScrollbar = VclPtr<SidebarScrollBar>::Create(*this, WB_3DLOOK |WB_VSCROLL|WB_DRAG, mrView); + mpVScrollbar->EnableNativeWidget(false); + mpVScrollbar->EnableRTL( false ); + mpVScrollbar->SetScrollHdl(LINK(this, SwAnnotationWin, ScrollHdl)); + mpVScrollbar->EnableDrag(); + mpVScrollbar->AddEventListener( LINK( this, SwAnnotationWin, WindowEventListener ) ); + + const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions(); + EEControlBits nCntrl = mpOutliner->GetControlWord(); + // TODO: crash when AUTOCOMPLETE enabled + nCntrl |= EEControlBits::MARKFIELDS | EEControlBits::PASTESPECIAL | EEControlBits::AUTOCORRECT | EEControlBits::USECHARATTRIBS; // | EEControlBits::AUTOCOMPLETE; + if (SwViewOption::IsFieldShadings()) + nCntrl |= EEControlBits::MARKFIELDS; + else + nCntrl &= ~EEControlBits::MARKFIELDS; + if (pVOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + mpOutliner->SetControlWord(nCntrl); + + std::size_t aIndex = SW_MOD()->InsertRedlineAuthor(GetAuthor()); + SetColor( SwPostItMgr::GetColorDark(aIndex), + SwPostItMgr::GetColorLight(aIndex), + SwPostItMgr::GetColorAnchor(aIndex)); + + CheckMetaText(); + + mpMenuButton = CreateMenuButton(); + + SetLanguage(GetLanguage()); + GetOutlinerView()->StartSpeller(); + SetPostItText(); + mpOutliner->CompleteOnlineSpelling(); + + mpSidebarTextControl->Show(); + mpMetadataAuthor->Show(); + mpMetadataDate->Show(); + if(IsThreadResolved()) { mpMetadataResolved->Show(); } + mpVScrollbar->Show(); +} + +void SwAnnotationWin::CheckMetaText() +{ + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData(); + OUString sMeta = GetAuthor(); + if (sMeta.isEmpty()) + { + sMeta = SwResId(STR_NOAUTHOR); + } + else if (sMeta.getLength() > 23) + { + sMeta = sMeta.copy(0, 20) + "..."; + } + if ( mpMetadataAuthor->GetText() != sMeta ) + { + mpMetadataAuthor->SetText(sMeta); + } + + Date aDate = GetDate(); + if (aDate.IsValidAndGregorian() ) + { + sMeta = rLocalData.getDate(aDate); + } + else + { + sMeta = SwResId(STR_NODATE); + } + if (GetTime().GetTime()!=0) + { + sMeta += " " + rLocalData.getTime( GetTime(),false ); + } + if ( mpMetadataDate->GetText() != sMeta ) + { + mpMetadataDate->SetText(sMeta); + } +} + +void SwAnnotationWin::Rescale() +{ + // On Android, this method leads to invoke ImpEditEngine::UpdateViews + // which hides the text cursor. Moreover it causes sudden document scroll + // when modifying a commented text. Not clear the root cause, + // anyway skipping this method fixes the problem, and there should be + // no side effect, since the client has disabled annotations rendering. + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + return; + + MapMode aMode = GetParent()->GetMapMode(); + aMode.SetOrigin( Point() ); + mpOutliner->SetRefMapMode( aMode ); + SetMapMode( aMode ); + mpSidebarTextControl->SetMapMode( aMode ); + const Fraction& rFraction = mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY(); + if ( mpMetadataAuthor ) + { + vcl::Font aFont( mpMetadataAuthor->GetSettings().GetStyleSettings().GetFieldFont() ); + sal_Int32 nHeight = long(aFont.GetFontHeight() * rFraction); + aFont.SetFontHeight( nHeight ); + mpMetadataAuthor->SetControlFont( aFont ); + } + if ( mpMetadataDate ) + { + vcl::Font aFont( mpMetadataDate->GetSettings().GetStyleSettings().GetFieldFont() ); + sal_Int32 nHeight = long(aFont.GetFontHeight() * rFraction); + aFont.SetFontHeight( nHeight ); + mpMetadataDate->SetControlFont( aFont ); + } + if ( mpMetadataResolved ) + { + vcl::Font aFont( mpMetadataResolved->GetSettings().GetStyleSettings().GetFieldFont() ); + sal_Int32 nHeight = long(aFont.GetFontHeight() * rFraction); + aFont.SetFontHeight( nHeight ); + mpMetadataResolved->SetControlFont( aFont ); + } +} + +void SwAnnotationWin::SetPosAndSize() +{ + bool bChange = false; + + if (GetSizePixel() != mPosSize.GetSize()) + { + bChange = true; + SetSizePixel(mPosSize.GetSize()); + + if (comphelper::LibreOfficeKit::isActive()) + { + // Position is not yet set at VCL level, but the map mode should + // contain the right origin to emit the correct cursor position. + mpSidebarTextControl->Push(PushFlags::MAPMODE); + Point aOffset(mPosSize.Left(), mPosSize.Top()); + aOffset = PixelToLogic(aOffset); + MapMode aMapMode(mpSidebarTextControl->GetMapMode()); + aMapMode.SetOrigin(aOffset); + mpSidebarTextControl->SetMapMode(aMapMode); + mpSidebarTextControl->EnableMapMode(false); + } + + DoResize(); + + if (comphelper::LibreOfficeKit::isActive()) + mpSidebarTextControl->Pop(); + } + + if (GetPosPixel().X() != mPosSize.TopLeft().X() || (std::abs(GetPosPixel().Y() - mPosSize.TopLeft().Y()) > 5) ) + { + bChange = true; + SetPosPixel(mPosSize.TopLeft()); + + Point aLineStart; + Point aLineEnd ; + switch ( meSidebarPosition ) + { + case sw::sidebarwindows::SidebarPosition::LEFT: + { + aLineStart = EditWin().PixelToLogic( Point(GetPosPixel().X()+GetSizePixel().Width(),GetPosPixel().Y()-1) ); + aLineEnd = EditWin().PixelToLogic( Point(GetPosPixel().X(),GetPosPixel().Y()-1) ); + } + break; + case sw::sidebarwindows::SidebarPosition::RIGHT: + { + aLineStart = EditWin().PixelToLogic( Point(GetPosPixel().X(),GetPosPixel().Y()-1) ); + aLineEnd = EditWin().PixelToLogic( Point(GetPosPixel().X()+GetSizePixel().Width(),GetPosPixel().Y()-1) ); + } + break; + default: + OSL_FAIL( "<SwAnnotationWin::SetPosAndSize()> - unexpected position of sidebar" ); + break; + } + + // LOK has map mode disabled, and we still want to perform pixel -> + // twips conversion for the size of the line above the note. + if (comphelper::LibreOfficeKit::isActive() && !EditWin().IsMapModeEnabled()) + { + EditWin().EnableMapMode(); + Size aSize(aLineEnd.getX() - aLineStart.getX(), aLineEnd.getY() - aLineStart.getY()); + aSize = EditWin().PixelToLogic(aSize); + aLineEnd = aLineStart; + aLineEnd.Move(aSize.getWidth(), aSize.getHeight()); + EditWin().EnableMapMode(false); + } + + if (mpAnchor) + { + mpAnchor->SetAllPosition( basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom() - 5* 15), + basegfx::B2DPoint( mAnchorRect.Left()-5*15 , mAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( mAnchorRect.Left()+5*15 , mAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( mAnchorRect.Left(), mAnchorRect.Bottom()+2*15), + basegfx::B2DPoint( mPageBorder ,mAnchorRect.Bottom()+2*15), + basegfx::B2DPoint( aLineStart.X(),aLineStart.Y()), + basegfx::B2DPoint( aLineEnd.X(),aLineEnd.Y())); + } + else + { + mpAnchor = AnchorOverlayObject::CreateAnchorOverlayObject( mrView, + mAnchorRect, + mPageBorder, + aLineStart, + aLineEnd, + mColorAnchor ); + if ( mpAnchor ) + { + mpAnchor->setVisible(true); + mpAnchor->SetAnchorState(AnchorState::Tri); + if (HasChildPathFocus()) + { + mpAnchor->setLineSolid(true); + } + } + } + } + else + { + if ( mpAnchor && + ( mpAnchor->getBasePosition() != basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom()-5*15) ) ) + { + mpAnchor->SetTriPosition( basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom() - 5* 15), + basegfx::B2DPoint( mAnchorRect.Left()-5*15 , mAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( mAnchorRect.Left()+5*15 , mAnchorRect.Bottom()+5*15), + basegfx::B2DPoint( mAnchorRect.Left(), mAnchorRect.Bottom()+2*15), + basegfx::B2DPoint( mPageBorder , mAnchorRect.Bottom()+2*15)); + } + } + + if (mpShadow && bChange) + { + Point aStart = EditWin().PixelToLogic(GetPosPixel()+Point(0,GetSizePixel().Height())); + Point aEnd = EditWin().PixelToLogic(GetPosPixel()+Point(GetSizePixel().Width()-1,GetSizePixel().Height())); + mpShadow->SetPosition(basegfx::B2DPoint(aStart.X(),aStart.Y()), basegfx::B2DPoint(aEnd.X(),aEnd.Y())); + } + + if (mrMgr.ShowNotes()) + { + if (IsFollow() && !HasChildPathFocus()) + { + // #i111964# + if ( mpAnchor ) + { + mpAnchor->SetAnchorState(AnchorState::End); + } + } + else + { + // #i111964# + if ( mpAnchor ) + { + mpAnchor->SetAnchorState(AnchorState::All); + } + SwAnnotationWin* pWin = GetTopReplyNote(); + // #i111964# + if ( pWin != this && pWin->Anchor() ) + { + pWin->Anchor()->SetAnchorState(AnchorState::End); + } + } + } + + + // text range overlay + maAnnotationTextRanges.clear(); + if ( mrSidebarItem.maLayoutInfo.mnStartNodeIdx != 0 + && mrSidebarItem.maLayoutInfo.mnStartContent != -1 ) + { + const SwTextAnnotationField* pTextAnnotationField = + dynamic_cast< const SwTextAnnotationField* >( mrSidebarItem.GetFormatField().GetTextField() ); + SwTextNode* pTextNode = pTextAnnotationField ? pTextAnnotationField->GetpTextNode() : nullptr; + SwContentNode* pContentNd = nullptr; + if (pTextNode) + { + SwNodes& rNds = pTextNode->GetDoc()->GetNodes(); + pContentNd = rNds[mrSidebarItem.maLayoutInfo.mnStartNodeIdx]->GetContentNode(); + } + if (pContentNd) + { + SwPosition aStartPos( *pContentNd, mrSidebarItem.maLayoutInfo.mnStartContent ); + SwShellCursor* pTmpCursor = nullptr; + const bool bTableCursorNeeded = pTextNode->FindTableBoxStartNode() != pContentNd->FindTableBoxStartNode(); + if ( bTableCursorNeeded ) + { + SwShellTableCursor* pTableCursor = new SwShellTableCursor( mrView.GetWrtShell(), aStartPos ); + pTableCursor->SetMark(); + pTableCursor->GetMark()->nNode = *pTextNode; + pTableCursor->GetMark()->nContent.Assign( pTextNode, pTextAnnotationField->GetStart()+1 ); + pTableCursor->NewTableSelection(); + pTmpCursor = pTableCursor; + } + else + { + SwShellCursor* pCursor = new SwShellCursor( mrView.GetWrtShell(), aStartPos ); + pCursor->SetMark(); + pCursor->GetMark()->nNode = *pTextNode; + pCursor->GetMark()->nContent.Assign( pTextNode, pTextAnnotationField->GetStart()+1 ); + pTmpCursor = pCursor; + } + std::unique_ptr<SwShellCursor> pTmpCursorForAnnotationTextRange( pTmpCursor ); + + // For annotation text range rectangles to be calculated correctly, + // we need the map mode disabled + bool bDisableMapMode = comphelper::LibreOfficeKit::isActive() && EditWin().IsMapModeEnabled(); + if (bDisableMapMode) + EditWin().EnableMapMode(false); + + if (mrSidebarItem.maLayoutInfo.mPositionFromCommentAnchor) + pTmpCursorForAnnotationTextRange->FillRects(); + + if (bDisableMapMode) + EditWin().EnableMapMode(); + + SwRects* pRects(pTmpCursorForAnnotationTextRange.get()); + for(const SwRect & rNextRect : *pRects) + { + const tools::Rectangle aPntRect(rNextRect.SVRect()); + maAnnotationTextRanges.emplace_back( + aPntRect.Left(), aPntRect.Top(), + aPntRect.Right() + 1, aPntRect.Bottom() + 1); + } + } + } + + if (mrMgr.ShowNotes() && !maAnnotationTextRanges.empty()) + { + if ( mpTextRangeOverlay != nullptr ) + { + mpTextRangeOverlay->setRanges( maAnnotationTextRanges ); + if ( mpAnchor != nullptr && mpAnchor->getLineSolid() ) + { + mpTextRangeOverlay->ShowSolidBorder(); + } + else + { + mpTextRangeOverlay->HideSolidBorder(); + } + } + else if (!IsFollow()) + { + // This window is not a reply, then draw its range overlay. + mpTextRangeOverlay = + sw::overlay::OverlayRanges::CreateOverlayRange( + mrView, + mColorAnchor, + maAnnotationTextRanges, + mpAnchor && mpAnchor->getLineSolid() ); + } + } + else + { + mpTextRangeOverlay.reset(); + } +} + +void SwAnnotationWin::DoResize() +{ + long aTextHeight = LogicToPixel( mpOutliner->CalcTextSize()).Height(); + long aHeight = GetSizePixel().Height(); + unsigned long aWidth = GetSizePixel().Width(); + + aHeight -= GetMetaHeight(); + mpMetadataAuthor->Show(); + if(IsThreadResolved()) { mpMetadataResolved->Show(); } + mpMetadataDate->Show(); + mpSidebarTextControl->SetQuickHelpText(OUString()); + unsigned int numFields = GetNumFields(); + if (aTextHeight > aHeight) + { // we need vertical scrollbars and have to reduce the width + aWidth -= GetScrollbarWidth(); + mpVScrollbar->Show(); + } + else + { + mpVScrollbar->Hide(); + } + + { + const Size aSizeOfMetadataControls( GetSizePixel().Width() - GetMetaButtonAreaWidth(), + GetMetaHeight()/numFields ); + mpMetadataAuthor->setPosSizePixel( 0, + aHeight, + aSizeOfMetadataControls.Width(), + aSizeOfMetadataControls.Height() ); + mpMetadataDate->setPosSizePixel( 0, + aHeight + aSizeOfMetadataControls.Height(), + aSizeOfMetadataControls.Width(), + aSizeOfMetadataControls.Height() ); + if(IsThreadResolved()) { + mpMetadataResolved->setPosSizePixel( 0, + aHeight + aSizeOfMetadataControls.Height()*2, + aSizeOfMetadataControls.Width(), + aSizeOfMetadataControls.Height() ); + } + } + + mpOutliner->SetPaperSize( PixelToLogic( Size(aWidth,aHeight) ) ) ; + if (!mpVScrollbar->IsVisible()) + { // if we do not have a scrollbar anymore, we want to see the complete text + mpOutlinerView->SetVisArea( PixelToLogic( tools::Rectangle(0,0,aWidth,aHeight) ) ); + } + mpOutlinerView->SetOutputArea( PixelToLogic( tools::Rectangle(0,0,aWidth,aHeight) ) ); + + if (!AllSettings::GetLayoutRTL()) + { + mpSidebarTextControl->setPosSizePixel(0, 0, aWidth, aHeight); + mpVScrollbar->setPosSizePixel( aWidth, 0, GetScrollbarWidth(), aHeight); + } + else + { + mpSidebarTextControl->setPosSizePixel( ( aTextHeight > aHeight ? GetScrollbarWidth() : 0 ), 0, + aWidth, aHeight); + mpVScrollbar->setPosSizePixel( 0, 0, GetScrollbarWidth(), aHeight); + } + + mpVScrollbar->SetVisibleSize( PixelToLogic(Size(0,aHeight)).Height() ); + mpVScrollbar->SetPageSize( PixelToLogic(Size(0,aHeight)).Height() * 8 / 10 ); + mpVScrollbar->SetLineSize( mpOutliner->GetTextHeight() / 10 ); + SetScrollbar(); + mpVScrollbar->SetRange( Range(0, mpOutliner->GetTextHeight())); + + //calculate rects for meta- button + const Fraction& fx( GetMapMode().GetScaleX() ); + const Fraction& fy( GetMapMode().GetScaleY() ); + + const Point aPos( mpMetadataAuthor->GetPosPixel()); + mpMenuButton->setPosSizePixel( long(aPos.X()+GetSizePixel().Width()-(METABUTTON_WIDTH+10)*fx), + long(aPos.Y()+5*fy), + long(METABUTTON_WIDTH*fx), + long(METABUTTON_HEIGHT*fy) ); +} + +void SwAnnotationWin::SetSizePixel( const Size& rNewSize ) +{ + Window::SetSizePixel(rNewSize); + + if (mpShadow) + { + Point aStart = EditWin().PixelToLogic(GetPosPixel()+Point(0,GetSizePixel().Height())); + Point aEnd = EditWin().PixelToLogic(GetPosPixel()+Point(GetSizePixel().Width()-1,GetSizePixel().Height())); + mpShadow->SetPosition(basegfx::B2DPoint(aStart.X(),aStart.Y()), basegfx::B2DPoint(aEnd.X(),aEnd.Y())); + } +} + +void SwAnnotationWin::SetScrollbar() +{ + mpVScrollbar->SetThumbPos(mpOutlinerView->GetVisArea().Top()); +} + +void SwAnnotationWin::ResizeIfNecessary(long aOldHeight, long aNewHeight) +{ + if (aOldHeight != aNewHeight) + { + //check for lower border or next note + long aBorder = mrMgr.GetNextBorder(); + if (aBorder != -1) + { + if (aNewHeight > GetMinimumSizeWithoutMeta()) + { + long aNewLowerValue = GetPosPixel().Y() + aNewHeight + GetMetaHeight(); + if (aNewLowerValue < aBorder) + SetSizePixel(Size(GetSizePixel().Width(),aNewHeight+GetMetaHeight())); + else + SetSizePixel(Size(GetSizePixel().Width(),aBorder - GetPosPixel().Y())); + DoResize(); + Invalidate(); + } + else + { + if (GetSizePixel().Height() != GetMinimumSizeWithoutMeta() + GetMetaHeight()) + SetSizePixel(Size(GetSizePixel().Width(),GetMinimumSizeWithoutMeta() + GetMetaHeight())); + DoResize(); + Invalidate(); + } + } + else + { + DoResize(); + Invalidate(); + } + } + else + { + SetScrollbar(); + } +} + +void SwAnnotationWin::SetColor(Color aColorDark,Color aColorLight, Color aColorAnchor) +{ + mColorDark = aColorDark; + mColorLight = aColorLight; + mColorAnchor = aColorAnchor; + + if ( !Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + { + mpMetadataAuthor->SetControlBackground(mColorDark); + AllSettings aSettings = mpMetadataAuthor->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + aStyleSettings.SetFieldTextColor(aColorAnchor); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataAuthor->SetSettings(aSettings); + } + + { + mpMetadataDate->SetControlBackground(mColorDark); + AllSettings aSettings = mpMetadataDate->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + aStyleSettings.SetFieldTextColor(aColorAnchor); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataDate->SetSettings(aSettings); + } + + { + mpMetadataResolved->SetControlBackground(mColorDark); + AllSettings aSettings = mpMetadataResolved->GetSettings(); + StyleSettings aStyleSettings = aSettings.GetStyleSettings(); + aStyleSettings.SetFieldTextColor(aColorAnchor); + aSettings.SetStyleSettings(aStyleSettings); + mpMetadataResolved->SetSettings(aSettings); + } + + AllSettings aSettings2 = mpVScrollbar->GetSettings(); + StyleSettings aStyleSettings2 = aSettings2.GetStyleSettings(); + aStyleSettings2.SetButtonTextColor(Color(0,0,0)); + aStyleSettings2.SetCheckedColor(mColorLight); // background + aStyleSettings2.SetShadowColor(mColorAnchor); + aStyleSettings2.SetFaceColor(mColorDark); + aSettings2.SetStyleSettings(aStyleSettings2); + mpVScrollbar->SetSettings(aSettings2); + } +} + +void SwAnnotationWin::SetSidebarPosition(sw::sidebarwindows::SidebarPosition eSidebarPosition) +{ + meSidebarPosition = eSidebarPosition; +} + +void SwAnnotationWin::SetReadonly(bool bSet) +{ + mbReadonly = bSet; + GetOutlinerView()->SetReadOnly(bSet); +} + +void SwAnnotationWin::SetLanguage(const SvxLanguageItem& rNewItem) +{ + IDocumentUndoRedo& rUndoRedo( + mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo()); + const bool bDocUndoEnabled = rUndoRedo.DoesUndo(); + const bool bOutlinerUndoEnabled = mpOutliner->IsUndoEnabled(); + const bool bOutlinerModified = mpOutliner->IsModified(); + const bool bDisableAndRestoreUndoMode = !bDocUndoEnabled && bOutlinerUndoEnabled; + + if (bDisableAndRestoreUndoMode) + { + // doc undo is disabled, but outliner was enabled, turn outliner undo off + // for the duration of this function + mpOutliner->EnableUndo(false); + } + + Link<LinkParamNone*,void> aLink = mpOutliner->GetModifyHdl(); + mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); + ESelection aOld = GetOutlinerView()->GetSelection(); + + ESelection aNewSelection( 0, 0, mpOutliner->GetParagraphCount()-1, EE_TEXTPOS_ALL ); + GetOutlinerView()->SetSelection( aNewSelection ); + SfxItemSet aEditAttr(GetOutlinerView()->GetAttribs()); + aEditAttr.Put(rNewItem); + GetOutlinerView()->SetAttribs( aEditAttr ); + + if (!mpOutliner->IsUndoEnabled() && !bOutlinerModified) + { + // if undo was disabled (e.g. this is a redo action) and we were + // originally 'unmodified' keep it that way + mpOutliner->ClearModifyFlag(); + } + + GetOutlinerView()->SetSelection(aOld); + mpOutliner->SetModifyHdl( aLink ); + + const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions(); + EEControlBits nCntrl = mpOutliner->GetControlWord(); + // turn off + nCntrl &= ~EEControlBits::ONLINESPELLING; + mpOutliner->SetControlWord(nCntrl); + + //turn back on + if (pVOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + mpOutliner->SetControlWord(nCntrl); + + mpOutliner->CompleteOnlineSpelling(); + + // restore original mode + if (bDisableAndRestoreUndoMode) + mpOutliner->EnableUndo(true); + + Invalidate(); +} + +void SwAnnotationWin::GetFocus() +{ + if (mpSidebarTextControl) + mpSidebarTextControl->GrabFocus(); +} + +void SwAnnotationWin::LoseFocus() +{ +} + +void SwAnnotationWin::ShowNote() +{ + SetPosAndSize(); + if (!IsVisible()) + Window::Show(); + if (mpShadow && !mpShadow->isVisible()) + mpShadow->setVisible(true); + if (mpAnchor && !mpAnchor->isVisible()) + mpAnchor->setVisible(true); + + // Invalidate. + InvalidateControl(); +} + +void SwAnnotationWin::HideNote() +{ + if (IsVisible()) + Window::Hide(); + if (mpAnchor) + { + if (mrMgr.IsShowAnchor()) + mpAnchor->SetAnchorState(AnchorState::Tri); + else + mpAnchor->setVisible(false); + } + if (mpShadow && mpShadow->isVisible()) + mpShadow->setVisible(false); +} + +void SwAnnotationWin::InvalidateControl() +{ + // Invalidate. + mpSidebarTextControl->Push(PushFlags::MAPMODE); + lcl_translateTwips(EditWin(), *mpSidebarTextControl, nullptr); + mpSidebarTextControl->Invalidate(); + mpSidebarTextControl->Pop(); +} + +void SwAnnotationWin::ActivatePostIt() +{ + mrMgr.AssureStdModeAtShell(); + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + + CheckMetaText(); + SetViewState(ViewState::EDIT); + GetOutlinerView()->ShowCursor(); + + mpOutlinerView->GetEditView().SetInsertMode(mrView.GetWrtShellPtr()->IsInsMode()); + + if ( !Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + GetOutlinerView()->SetBackgroundColor(mColorDark); + + //tdf#119130 only have the active postit as a dialog control in which pressing + //ctrl+tab cycles between text and button so we don't waste time searching + //thousands of SwAnnotationWins + SetStyle(GetStyle() | WB_DIALOGCONTROL); +} + +void SwAnnotationWin::DeactivatePostIt() +{ + //tdf#119130 only have the active postit as a dialog control in which pressing + //ctrl+tab cycles between text and button so we don't waste time searching + //thousands of SwAnnotationWins + SetStyle(GetStyle() & ~WB_DIALOGCONTROL); + + // remove selection, #i87073# + if (GetOutlinerView()->GetEditView().HasSelection()) + { + ESelection aSelection = GetOutlinerView()->GetEditView().GetSelection(); + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos; + GetOutlinerView()->GetEditView().SetSelection(aSelection); + } + + mpOutliner->CompleteOnlineSpelling(); + + SetViewState(ViewState::NORMAL); + // Make sure this view doesn't emit LOK callbacks during the update, as the + // sidebar window's SidebarTextControl doesn't have a valid twip offset + // (map mode origin) during that operation. + bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting(); + comphelper::LibreOfficeKit::setTiledPainting(true); + // write the visible text back into the SwField + UpdateData(); + comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting); + + if ( !Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + GetOutlinerView()->SetBackgroundColor(COL_TRANSPARENT); + + if ( !IsProtected() && mpOutliner->GetEditEngine().GetText().isEmpty() ) + { + mnEventId = Application::PostUserEvent( LINK( this, SwAnnotationWin, DeleteHdl), nullptr, true ); + } +} + +void SwAnnotationWin::ToggleInsMode() +{ + if (!mrView.GetWrtShell().IsRedlineOn()) + { + //change outliner + mpOutlinerView->GetEditView().SetInsertMode(!mpOutlinerView->GetEditView().IsInsertMode()); + //change document + mrView.GetWrtShell().ToggleInsMode(); + //update statusbar + SfxBindings &rBnd = mrView.GetViewFrame()->GetBindings(); + rBnd.Invalidate(SID_ATTR_INSERT); + rBnd.Update(SID_ATTR_INSERT); + } +} + +void SwAnnotationWin::ExecuteCommand(sal_uInt16 nSlot) +{ + mrMgr.AssureStdModeAtShell(); + + switch (nSlot) + { + case FN_POSTIT: + case FN_REPLY: + { + // if this note is empty, it will be deleted once losing the focus, so no reply, but only a new note + // will be created + if (!mpOutliner->GetEditEngine().GetText().isEmpty()) + { + OutlinerParaObject* pPara = new OutlinerParaObject(*GetOutlinerView()->GetEditView().CreateTextObject()); + mrMgr.RegisterAnswer(pPara); + } + if (mrMgr.HasActiveSidebarWin()) + mrMgr.SetActiveSidebarWin(nullptr); + SwitchToFieldPos(); + mrView.GetViewFrame()->GetDispatcher()->Execute(FN_POSTIT); + break; + } + case FN_DELETE_COMMENT: + //Delete(); // do not kill the parent of our open popup menu + mnEventId = Application::PostUserEvent( LINK( this, SwAnnotationWin, DeleteHdl), nullptr, true ); + break; + case FN_RESOLVE_NOTE: + GetTopReplyNote()->ToggleResolved(); + mrMgr.UpdateResolvedStatus(GetTopReplyNote()); + DoResize(); + Invalidate(); + mrMgr.LayoutPostIts(); + break; + case FN_FORMAT_ALL_NOTES: + case FN_DELETE_ALL_NOTES: + case FN_HIDE_ALL_NOTES: + // not possible as slot as this would require that "this" is the active postit + mrView.GetViewFrame()->GetBindings().Execute( nSlot, nullptr, SfxCallMode::ASYNCHRON ); + break; + case FN_DELETE_NOTE_AUTHOR: + case FN_HIDE_NOTE_AUTHOR: + { + // not possible as slot as this would require that "this" is the active postit + SfxStringItem aItem( nSlot, GetAuthor() ); + const SfxPoolItem* aItems[2]; + aItems[0] = &aItem; + aItems[1] = nullptr; + mrView.GetViewFrame()->GetBindings().Execute( nSlot, aItems, SfxCallMode::ASYNCHRON ); + } + break; + default: + mrView.GetViewFrame()->GetBindings().Execute( nSlot ); + break; + } +} + +SwEditWin& SwAnnotationWin::EditWin() +{ + return mrView.GetEditWin(); +} + +long SwAnnotationWin::GetPostItTextHeight() +{ + return mpOutliner ? LogicToPixel(mpOutliner->CalcTextSize()).Height() : 0; +} + +void SwAnnotationWin::SwitchToPostIt(sal_uInt16 aDirection) +{ + SwAnnotationWin* pPostIt = mrMgr.GetNextPostIt(aDirection, this); + if (pPostIt) + pPostIt->GrabFocus(); +} + +IMPL_LINK( SwAnnotationWin, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + if ( rEvent.GetId() == VclEventId::WindowMouseMove ) + { + MouseEvent* pMouseEvt = static_cast<MouseEvent*>(rEvent.GetData()); + if ( pMouseEvt->IsEnterWindow() ) + { + mbMouseOver = true; + if ( !HasFocus() ) + { + SetViewState(ViewState::VIEW); + Invalidate(); + } + } + else if ( pMouseEvt->IsLeaveWindow()) + { + mbMouseOver = false; + if ( !HasFocus() ) + { + SetViewState(ViewState::NORMAL); + Invalidate(); + } + } + } + else if ( rEvent.GetId() == VclEventId::WindowActivate && + rEvent.GetWindow() == mpSidebarTextControl ) + { + SetActiveSidebarWin(); + mrMgr.MakeVisible( this ); + } +} + +bool SwAnnotationWin::SetActiveSidebarWin() +{ + if (mrMgr.GetActiveSidebarWin() == this) + return false; + const bool bLockView = mrView.GetWrtShell().IsViewLocked(); + mrView.GetWrtShell().LockView( true ); + mrMgr.SetActiveSidebarWin(this); + mrView.GetWrtShell().LockView( bLockView ); + return true; +} + +void SwAnnotationWin::UnsetActiveSidebarWin() +{ + if (mrMgr.GetActiveSidebarWin() != this) + return; + const bool bLockView = mrView.GetWrtShell().IsViewLocked(); + mrView.GetWrtShell().LockView( true ); + mrMgr.SetActiveSidebarWin(nullptr); + mrView.GetWrtShell().LockView( bLockView ); +} + +IMPL_LINK(SwAnnotationWin, ScrollHdl, ScrollBar*, pScroll, void) +{ + long nDiff = GetOutlinerView()->GetEditView().GetVisArea().Top() - pScroll->GetThumbPos(); + GetOutlinerView()->Scroll( 0, nDiff ); +} + +IMPL_LINK_NOARG(SwAnnotationWin, ModifyHdl, LinkParamNone*, void) +{ + mrView.GetDocShell()->SetModified(); +} + +IMPL_LINK_NOARG(SwAnnotationWin, DeleteHdl, void*, void) +{ + mnEventId = nullptr; + Delete(); +} + +void SwAnnotationWin::ResetAttributes() +{ + mpOutlinerView->RemoveAttribsKeepLanguages(true); + mpOutliner->RemoveFields(); + mpOutlinerView->SetAttribs(DefaultItem()); +} + +sal_Int32 SwAnnotationWin::GetScrollbarWidth() const +{ + return mrView.GetWrtShell().GetViewOptions()->GetZoom() / 10; +} + +sal_Int32 SwAnnotationWin::GetMetaButtonAreaWidth() const +{ + const Fraction& f( GetMapMode().GetScaleX() ); + return long(METABUTTON_AREA_WIDTH * f); +} + +sal_Int32 SwAnnotationWin::GetMetaHeight() +{ + const Fraction& f(mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY()); + const int fields = GetNumFields(); + return long(fields*POSTIT_META_FIELD_HEIGHT*f); +} + +sal_Int32 SwAnnotationWin::GetNumFields() +{ + return IsThreadResolved() ? 3 : 2; +} + +sal_Int32 SwAnnotationWin::GetMinimumSizeWithMeta() const +{ + return mrMgr.GetMinimumSizeWithMeta(); +} + +sal_Int32 SwAnnotationWin::GetMinimumSizeWithoutMeta() const +{ + const Fraction& f(mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY()); + return long(POSTIT_MINIMUMSIZE_WITHOUT_META * f); +} + +void SwAnnotationWin::SetSpellChecking() +{ + const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions(); + EEControlBits nCntrl = mpOutliner->GetControlWord(); + if (pVOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + mpOutliner->SetControlWord(nCntrl); + + mpOutliner->CompleteOnlineSpelling(); + Invalidate(); +} + +void SwAnnotationWin::SetViewState(ViewState bViewState) +{ + switch (bViewState) + { + case ViewState::EDIT: + { + if (mpAnchor) + { + mpAnchor->SetAnchorState(AnchorState::All); + SwAnnotationWin* pWin = GetTopReplyNote(); + // #i111964# + if ( pWin != this && pWin->Anchor() ) + { + pWin->Anchor()->SetAnchorState(AnchorState::End); + } + mpAnchor->setLineSolid(true); + if ( mpTextRangeOverlay != nullptr ) + { + mpTextRangeOverlay->ShowSolidBorder(); + } + } + if (mpShadow) + mpShadow->SetShadowState(SS_EDIT); + break; + } + case ViewState::VIEW: + { + if (mpAnchor) + { + mpAnchor->setLineSolid(true); + if ( mpTextRangeOverlay != nullptr ) + { + mpTextRangeOverlay->ShowSolidBorder(); + } + } + if (mpShadow) + mpShadow->SetShadowState(SS_VIEW); + break; + } + case ViewState::NORMAL: + { + if (mpAnchor) + { + if (IsFollow()) + { + // if there is no visible parent note, we want to see the complete anchor ?? + //if (IsAnyStackParentVisible()) + mpAnchor->SetAnchorState(AnchorState::End); + SwAnnotationWin* pTopWinSelf = GetTopReplyNote(); + SwAnnotationWin* pTopWinActive = mrMgr.HasActiveSidebarWin() + ? mrMgr.GetActiveSidebarWin()->GetTopReplyNote() + : nullptr; + // #i111964# + if ( ( pTopWinSelf != this ) && + ( pTopWinSelf != pTopWinActive ) && + pTopWinSelf->Anchor() ) + { + if ( pTopWinSelf != mrMgr.GetActiveSidebarWin() ) + { + pTopWinSelf->Anchor()->setLineSolid(false); + if ( pTopWinSelf->TextRange() != nullptr ) + { + pTopWinSelf->TextRange()->HideSolidBorder(); + } + } + pTopWinSelf->Anchor()->SetAnchorState(AnchorState::All); + } + } + mpAnchor->setLineSolid(false); + if ( mpTextRangeOverlay != nullptr ) + { + mpTextRangeOverlay->HideSolidBorder(); + } + } + if ( mpShadow ) + { + mpShadow->SetShadowState(SS_NORMAL); + } + break; + } + } +} + +SwAnnotationWin* SwAnnotationWin::GetTopReplyNote() +{ + SwAnnotationWin* pTopNote = this; + SwAnnotationWin* pSidebarWin = IsFollow() ? mrMgr.GetNextPostIt(KEY_PAGEUP, this) : nullptr; + while (pSidebarWin) + { + pTopNote = pSidebarWin; + pSidebarWin = pSidebarWin->IsFollow() ? mrMgr.GetNextPostIt(KEY_PAGEUP, pSidebarWin) : nullptr; + } + return pTopNote; +} + +void SwAnnotationWin::SwitchToFieldPos() +{ + if ( mrMgr.GetActiveSidebarWin() == this ) + mrMgr.SetActiveSidebarWin(nullptr); + GotoPos(); + sal_uInt32 aCount = MoveCaret(); + if (aCount) + mrView.GetDocShell()->GetWrtShell()->SwCursorShell::Right(aCount, 0); + GrabFocusToDocument(); +} + +void SwAnnotationWin::SetChangeTracking( const SwPostItHelper::SwLayoutStatus aLayoutStatus, + const Color& aChangeColor ) +{ + if ( (mLayoutStatus != aLayoutStatus) || + (mChangeColor != aChangeColor) ) + { + mLayoutStatus = aLayoutStatus; + mChangeColor = aChangeColor; + Invalidate(); + } +} + +bool SwAnnotationWin::HasScrollbar() const +{ + return mpVScrollbar != nullptr; +} + +bool SwAnnotationWin::IsScrollbarVisible() const +{ + return HasScrollbar() && mpVScrollbar->IsVisible(); +} + +void SwAnnotationWin::ChangeSidebarItem( SwSidebarItem const & rSidebarItem ) +{ + const bool bAnchorChanged = mpAnchorFrame != rSidebarItem.maLayoutInfo.mpAnchorFrame; + if ( bAnchorChanged ) + { + mrMgr.DisconnectSidebarWinFromFrame( *mpAnchorFrame, *this ); + } + + mrSidebarItem = rSidebarItem; + mpAnchorFrame = mrSidebarItem.maLayoutInfo.mpAnchorFrame; + + if ( GetWindowPeer() ) + { + SidebarWinAccessible* pAcc = + static_cast<SidebarWinAccessible*>( GetWindowPeer() ); + OSL_ENSURE( dynamic_cast<SidebarWinAccessible*>( GetWindowPeer() ), + "<SwAnnotationWin::ChangeSidebarItem(..)> - unexpected type of window peer -> crash possible!" ); + pAcc->ChangeSidebarItem( mrSidebarItem ); + } + + if ( bAnchorChanged ) + { + mrMgr.ConnectSidebarWinToFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame), + mrSidebarItem.GetFormatField(), + *this ); + } +} + +css::uno::Reference< css::accessibility::XAccessible > SwAnnotationWin::CreateAccessible() +{ + SidebarWinAccessible* pAcc( new SidebarWinAccessible( *this, + mrView.GetWrtShell(), + mrSidebarItem ) ); + css::uno::Reference< css::awt::XWindowPeer > xWinPeer( pAcc ); + SetWindowPeer( xWinPeer, pAcc ); + + css::uno::Reference< css::accessibility::XAccessible > xAcc( xWinPeer, css::uno::UNO_QUERY ); + return xAcc; +} + +} // eof of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/DashedLine.cxx b/sw/source/uibase/docvw/DashedLine.cxx new file mode 100644 index 000000000..de46be7cd --- /dev/null +++ b/sw/source/uibase/docvw/DashedLine.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/. + */ + +#include <DashedLine.hxx> + +#include <basegfx/color/bcolortools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <memory> + +SwDashedLine::SwDashedLine( vcl::Window* pParent, Color& ( *pColorFn )() ) : + FixedLine( pParent, WB_DIALOGCONTROL | WB_HORZ ), + m_pColorFn( pColorFn ) +{ +} + +SwDashedLine::~SwDashedLine( ) +{ +} + +void SwDashedLine::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos)); + + // Compute the start and end points + const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + double nHalfWidth = double(aRect.Top() + aRect.Bottom()) / 2.0; + + basegfx::B2DPoint aStart(double(aRect.Left()), nHalfWidth); + basegfx::B2DPoint aEnd(double(aRect.Right()), nHalfWidth); + + basegfx::B2DPolygon aPolygon; + aPolygon.append(aStart); + aPolygon.append(aEnd); + + drawinglayer::primitive2d::Primitive2DContainer aSeq(1); + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + + std::vector<double> aStrokePattern; + basegfx::BColor aColor = m_pColorFn().getBColor(); + 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(); + nLuminance += (1.0 - nLuminance) * 0.75; + if (aHslLine.getZ() > 0.7) + nLuminance = aHslLine.getZ() * 0.7; + aHslLine.setZ(nLuminance); + const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb(aHslLine); + + // Compute the plain line + drawinglayer::primitive2d::PolygonHairlinePrimitive2D * pPlainLine = + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon, aOtherColor); + + aSeq[0] = drawinglayer::primitive2d::Primitive2DReference(pPlainLine); + // Dashed line in twips + aStrokePattern.push_back(3); + aStrokePattern.push_back(3); + + aSeq.resize(2); + } + + // Compute the dashed line primitive + drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D * pLine = + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + drawinglayer::attribute::LineAttribute(m_pColorFn().getBColor()), + drawinglayer::attribute::StrokeAttribute(aStrokePattern)); + + aSeq[aSeq.size() - 1] = drawinglayer::primitive2d::Primitive2DReference(pLine); + + pProcessor->process(aSeq); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/FrameControlsManager.cxx b/sw/source/uibase/docvw/FrameControlsManager.cxx new file mode 100644 index 000000000..9d68bc950 --- /dev/null +++ b/sw/source/uibase/docvw/FrameControlsManager.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/. + */ + +#include <edtwin.hxx> +#include <FrameControlsManager.hxx> +#include <HeaderFooterWin.hxx> +#include <PageBreakWin.hxx> +#include <UnfloatTableButton.hxx> +#include <pagefrm.hxx> +#include <flyfrm.hxx> +#include <viewopt.hxx> +#include <view.hxx> +#include <wrtsh.hxx> + +using namespace std; + +SwFrameControlsManager::SwFrameControlsManager( SwEditWin* pEditWin ) : + m_pEditWin( pEditWin ), + m_aControls( ) +{ +} + +SwFrameControlsManager::~SwFrameControlsManager() +{ +} + +void SwFrameControlsManager::dispose() +{ + m_aControls.clear(); +} + +SwFrameControlPtr SwFrameControlsManager::GetControl( FrameControlType eType, const SwFrame* pFrame ) +{ + SwFrameControlPtrMap& rControls = m_aControls[eType]; + + SwFrameControlPtrMap::iterator aIt = rControls.find(pFrame); + + if (aIt != rControls.end()) + return aIt->second; + + return SwFrameControlPtr(); +} + +void SwFrameControlsManager::RemoveControls( const SwFrame* pFrame ) +{ + for ( auto& rEntry : m_aControls ) + { + SwFrameControlPtrMap& rMap = rEntry.second; + rMap.erase(pFrame); + } +} + +void SwFrameControlsManager::RemoveControlsByType( FrameControlType eType, const SwFrame* pFrame ) +{ + SwFrameControlPtrMap& rMap = m_aControls[eType]; + rMap.erase(pFrame); +} + +void SwFrameControlsManager::HideControls( FrameControlType eType ) +{ + for ( const auto& rCtrl : m_aControls[eType] ) + rCtrl.second->ShowAll( false ); +} + +void SwFrameControlsManager::SetReadonlyControls( bool bReadonly ) +{ + for ( auto& rEntry : m_aControls ) + for ( auto& rCtrl : rEntry.second ) + rCtrl.second->SetReadonly( bReadonly ); +} + +void SwFrameControlsManager::SetHeaderFooterControl( const SwPageFrame* pPageFrame, FrameControlType eType, Point aOffset ) +{ + assert( eType == FrameControlType::Header || eType == FrameControlType::Footer ); + + // Check if we already have the control + SwFrameControlPtr pControl; + const bool bHeader = ( eType == FrameControlType::Header ); + + SwFrameControlPtrMap& rControls = m_aControls[eType]; + + SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pPageFrame); + if (lb != rControls.end() && !(rControls.key_comp()(pPageFrame, lb->first))) + pControl = lb->second; + else + { + SwFrameControlPtr pNewControl = + std::make_shared<SwFrameControl>( VclPtr<SwHeaderFooterWin>::Create( + m_pEditWin, pPageFrame, bHeader ).get() ); + const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions(); + pNewControl->SetReadonly( pViewOpt->IsReadonly() ); + rControls.insert(lb, make_pair(pPageFrame, pNewControl)); + pControl.swap( pNewControl ); + } + + tools::Rectangle aPageRect = m_pEditWin->LogicToPixel( pPageFrame->getFrameArea().SVRect() ); + + SwHeaderFooterWin* pWin = dynamic_cast<SwHeaderFooterWin *>(pControl->GetWindow()); + assert( pWin != nullptr) ; + assert( pWin->IsHeader() == bHeader ); + pWin->SetOffset( aOffset, aPageRect.Left(), aPageRect.Right() ); + + if (!pWin->IsVisible()) + pControl->ShowAll( true ); +} + +void SwFrameControlsManager::SetPageBreakControl( const SwPageFrame* pPageFrame ) +{ + // Check if we already have the control + SwFrameControlPtr pControl; + + SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::PageBreak]; + + SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pPageFrame); + if (lb != rControls.end() && !(rControls.key_comp()(pPageFrame, lb->first))) + pControl = lb->second; + else + { + SwFrameControlPtr pNewControl = std::make_shared<SwFrameControl>( + VclPtr<SwPageBreakWin>::Create( m_pEditWin, pPageFrame ).get() ); + const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions(); + pNewControl->SetReadonly( pViewOpt->IsReadonly() ); + + rControls.insert(lb, make_pair(pPageFrame, pNewControl)); + + pControl.swap( pNewControl ); + } + + SwPageBreakWin* pWin = dynamic_cast<SwPageBreakWin *>(pControl->GetWindow()); + assert (pWin != nullptr); + pWin->UpdatePosition(); + if (!pWin->IsVisible()) + pControl->ShowAll( true ); +} + +void SwFrameControlsManager::SetUnfloatTableButton( const SwFlyFrame* pFlyFrame, bool bShow, Point aTopRightPixel ) +{ + if(pFlyFrame == nullptr) + return; + + // Check if we already have the control + SwFrameControlPtr pControl; + + SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::FloatingTable]; + + SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pFlyFrame); + if (lb != rControls.end() && !(rControls.key_comp()(pFlyFrame, lb->first))) + pControl = lb->second; + else if (!bShow) // Do not create the control when it's not shown + return; + else + { + SwFrameControlPtr pNewControl = std::make_shared<SwFrameControl>( + VclPtr<UnfloatTableButton>::Create( m_pEditWin, pFlyFrame ).get() ); + const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions(); + pNewControl->SetReadonly( pViewOpt->IsReadonly() ); + + rControls.insert(lb, make_pair(pFlyFrame, pNewControl)); + + pControl.swap( pNewControl ); + } + + UnfloatTableButton* pButton = dynamic_cast<UnfloatTableButton*>(pControl->GetWindow()); + assert(pButton != nullptr); + pButton->SetOffset(aTopRightPixel); + pControl->ShowAll( bShow ); +} + +SwFrameMenuButtonBase::SwFrameMenuButtonBase( SwEditWin* pEditWin, const SwFrame* pFrame ) : + MenuButton( pEditWin, WB_DIALOGCONTROL ), + m_pEditWin( pEditWin ), + m_pFrame( pFrame ) +{ +} + +const SwPageFrame* SwFrameMenuButtonBase::GetPageFrame() const +{ + if (m_pFrame->IsPageFrame()) + return static_cast<const SwPageFrame*>( m_pFrame ); + + if (m_pFrame->IsFlyFrame()) + return static_cast<const SwFlyFrame*>(m_pFrame)->GetAnchorFrame()->FindPageFrame(); + + return m_pFrame->FindPageFrame(); +} + +void SwFrameMenuButtonBase::dispose() +{ + m_pEditWin.clear(); + m_pFrame = nullptr; + MenuButton::dispose(); +} + +SwFrameControl::SwFrameControl( const VclPtr<vcl::Window> &pWindow ) +{ + assert(static_cast<bool>(pWindow)); + mxWindow.reset( pWindow ); + mpIFace = dynamic_cast<ISwFrameControl *>( pWindow.get() ); +} + +SwFrameControl::~SwFrameControl() +{ + mpIFace = nullptr; + mxWindow.disposeAndClear(); +} + +ISwFrameControl::~ISwFrameControl() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx b/sw/source/uibase/docvw/HeaderFooterWin.cxx new file mode 100644 index 000000000..bce5cfcde --- /dev/null +++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx @@ -0,0 +1,521 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <strings.hrc> + +#include <doc.hxx> +#include <drawdoc.hxx> +#include <cmdid.h> +#include <DashedLine.hxx> +#include <docsh.hxx> +#include <edtwin.hxx> +#include <fmthdft.hxx> +#include <HeaderFooterWin.hxx> +#include <pagedesc.hxx> +#include <pagefrm.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <IDocumentDrawModelAccess.hxx> + +#include <basegfx/color/bcolortools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <drawinglayer/attribute/fontattribute.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <editeng/boxitem.hxx> +#include <svx/hdft.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/metric.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <memory> + +#define TEXT_PADDING 5 +#define BOX_DISTANCE 10 +#define BUTTON_WIDTH 18 + +using namespace basegfx; +using namespace basegfx::utils; +using namespace drawinglayer::attribute; + +namespace +{ + basegfx::BColor lcl_GetFillColor(const basegfx::BColor& rLineColor) + { + basegfx::BColor aHslLine = basegfx::utils::rgb2hsl(rLineColor); + 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 ); + return basegfx::utils::hsl2rgb( aHslLine ); + } + + basegfx::BColor lcl_GetLighterGradientColor(const basegfx::BColor& rDarkColor) + { + basegfx::BColor aHslDark = basegfx::utils::rgb2hsl(rDarkColor); + double nLuminance = aHslDark.getZ() * 255 + 20; + aHslDark.setZ( nLuminance / 255.0 ); + return basegfx::utils::hsl2rgb( aHslDark ); + } + + B2DPolygon lcl_GetPolygon( const ::tools::Rectangle& rRect, bool bOnTop ) + { + const double nRadius = 3; + const double nKappa((M_SQRT2 - 1.0) * 4.0 / 3.0); + + B2DPolygon aPolygon; + aPolygon.append( B2DPoint( rRect.Left(), rRect.Top() ) ); + + { + B2DPoint aCorner( rRect.Left(), rRect.Bottom() ); + B2DPoint aStart( rRect.Left(), rRect.Bottom() - nRadius ); + B2DPoint aEnd( rRect.Left() + nRadius, rRect.Bottom() ); + aPolygon.append( aStart ); + aPolygon.appendBezierSegment( + interpolate( aStart, aCorner, nKappa ), + interpolate( aEnd, aCorner, nKappa ), + aEnd ); + } + + { + B2DPoint aCorner( rRect.Right(), rRect.Bottom() ); + B2DPoint aStart( rRect.Right() - nRadius, rRect.Bottom() ); + B2DPoint aEnd( rRect.Right(), rRect.Bottom() - nRadius ); + aPolygon.append( aStart ); + aPolygon.appendBezierSegment( + interpolate( aStart, aCorner, nKappa ), + interpolate( aEnd, aCorner, nKappa ), + aEnd ); + } + + aPolygon.append( B2DPoint( rRect.Right(), rRect.Top() ) ); + + if ( !bOnTop ) + { + B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(rRect); + B2DHomMatrix aRotation = createRotateAroundPoint( + aBRect.getCenterX(), aBRect.getCenterY(), M_PI ); + aPolygon.transform( aRotation ); + } + + return aPolygon; + } +} + +void SwFrameButtonPainter::PaintButton(drawinglayer::primitive2d::Primitive2DContainer& rSeq, + const tools::Rectangle& rRect, bool bOnTop) +{ + rSeq.clear(); + B2DPolygon aPolygon = lcl_GetPolygon(rRect, bOnTop); + + // Colors + basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); + basegfx::BColor aFillColor = lcl_GetFillColor(aLineColor); + basegfx::BColor aLighterColor = lcl_GetLighterGradientColor(aFillColor); + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + if (rSettings.GetHighContrastMode()) + { + aFillColor = rSettings.GetDialogColor().getBColor(); + aLineColor = rSettings.GetDialogTextColor().getBColor(); + + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), aFillColor))); + } + else + { + B2DRectangle aGradientRect = vcl::unotools::b2DRectangleFromRectangle(rRect); + double nAngle = M_PI; + if (bOnTop) + nAngle = 0; + FillGradientAttribute aFillAttrs(drawinglayer::attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, nAngle, aLighterColor, aFillColor, 10); + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::FillGradientPrimitive2D(aGradientRect, aFillAttrs))); + } + + // Create the border lines primitive + rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon, aLineColor))); +} + +SwHeaderFooterWin::SwHeaderFooterWin( SwEditWin* pEditWin, const SwFrame *pFrame, bool bHeader ) : + SwFrameMenuButtonBase( pEditWin, pFrame ), + m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/headerfootermenu.ui", ""), + m_bIsHeader( bHeader ), + m_pPopupMenu(m_aBuilder.get_menu("menu")), + m_pLine( nullptr ), + m_bIsAppearing( false ), + m_nFadeRate( 100 ), + m_aFadeTimer( ) +{ + //FIXME RenderContext + + // Get the font and configure it + vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetToolFont(); + SetZoomedPointFont(*this, aFont); + + // Create the line control + m_pLine = VclPtr<SwDashedLine>::Create(GetEditWin(), &SwViewOption::GetHeaderFooterMarkColor); + m_pLine->SetZOrder(this, ZOrderFlags::Before); + + // set the PopupMenu + // Rewrite the menu entries' text + if (m_bIsHeader) + { + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("edit"), SwResId(STR_FORMAT_HEADER)); + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("delete"), SwResId(STR_DELETE_HEADER)); + } + else + { + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("edit"), SwResId(STR_FORMAT_FOOTER)); + m_pPopupMenu->SetItemText(m_pPopupMenu->GetItemId("delete"), SwResId(STR_DELETE_FOOTER)); + } + + SetPopupMenu(m_pPopupMenu); + + m_aFadeTimer.SetTimeout(50); + m_aFadeTimer.SetInvokeHandler(LINK(this, SwHeaderFooterWin, FadeHandler)); +} + +SwHeaderFooterWin::~SwHeaderFooterWin( ) +{ + disposeOnce(); +} + +void SwHeaderFooterWin::dispose() +{ + m_pPopupMenu.clear(); + m_aBuilder.disposeBuilder(); + m_pLine.disposeAndClear(); + SwFrameMenuButtonBase::dispose(); +} + +void SwHeaderFooterWin::SetOffset(Point aOffset, long nXLineStart, long nXLineEnd) +{ + // Compute the text to show + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + bool bIsFirst = !pDesc->IsFirstShared() && GetPageFrame()->OnFirstPage(); + bool bIsLeft = !pDesc->IsHeaderShared() && !GetPageFrame()->OnRightPage(); + bool bIsRight = !pDesc->IsHeaderShared() && GetPageFrame()->OnRightPage(); + m_sLabel = SwResId(STR_HEADER_TITLE); + if (!m_bIsHeader) + m_sLabel = bIsFirst ? SwResId(STR_FIRST_FOOTER_TITLE) + : bIsLeft ? SwResId(STR_LEFT_FOOTER_TITLE) + : bIsRight ? SwResId(STR_RIGHT_FOOTER_TITLE) + : SwResId(STR_FOOTER_TITLE ); + else + m_sLabel = bIsFirst ? SwResId(STR_FIRST_HEADER_TITLE) + : bIsLeft ? SwResId(STR_LEFT_HEADER_TITLE) + : bIsRight ? SwResId(STR_RIGHT_HEADER_TITLE) + : SwResId(STR_HEADER_TITLE); + + sal_Int32 nPos = m_sLabel.lastIndexOf("%1"); + m_sLabel = m_sLabel.replaceAt(nPos, 2, pDesc->GetName()); + + // Compute the text size and get the box position & size from it + ::tools::Rectangle aTextRect; + GetTextBoundRect(aTextRect, m_sLabel); + ::tools::Rectangle aTextPxRect = LogicToPixel(aTextRect); + FontMetric aFontMetric = GetFontMetric(GetFont()); + Size aBoxSize (aTextPxRect.GetWidth() + BUTTON_WIDTH + TEXT_PADDING * 2, + aFontMetric.GetLineHeight() + TEXT_PADDING * 2 ); + + long nYFooterOff = 0; + if (!m_bIsHeader) + nYFooterOff = aBoxSize.Height(); + + Point aBoxPos(aOffset.X() - aBoxSize.Width() - BOX_DISTANCE, + aOffset.Y() - nYFooterOff); + + if (AllSettings::GetLayoutRTL()) + { + aBoxPos.setX( aOffset.X() + BOX_DISTANCE ); + } + + // Set the position & Size of the window + SetPosSizePixel(aBoxPos, aBoxSize); + + double nYLinePos = aBoxPos.Y(); + if (!m_bIsHeader) + nYLinePos += aBoxSize.Height(); + Point aLinePos(nXLineStart, nYLinePos); + Size aLineSize(nXLineEnd - nXLineStart, 1); + m_pLine->SetPosSizePixel(aLinePos, aLineSize); +} + +void SwHeaderFooterWin::ShowAll(bool bShow) +{ + if (!PopupMenu::IsInExecute()) + { + m_bIsAppearing = bShow; + + if (m_aFadeTimer.IsActive()) + m_aFadeTimer.Stop(); + m_aFadeTimer.Start(); + } +} + +bool SwHeaderFooterWin::Contains( const Point &rDocPt ) const +{ + ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel()); + if (aRect.IsInside(rDocPt)) + return true; + + ::tools::Rectangle aLineRect(m_pLine->GetPosPixel(), m_pLine->GetSizePixel()); + return aLineRect.IsInside(rDocPt); +} + +void SwHeaderFooterWin::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + // Use pixels for the rest of the drawing + SetMapMode(MapMode(MapUnit::MapPixel)); + drawinglayer::primitive2d::Primitive2DContainer aSeq; + const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + + SwFrameButtonPainter::PaintButton(aSeq, aRect, m_bIsHeader); + + // Create the text primitive + basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); + B2DVector aFontSize; + FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, rRenderContext.GetFont(), false, false); + + FontMetric aFontMetric = rRenderContext.GetFontMetric(rRenderContext.GetFont()); + double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING; + Point aTextPos(TEXT_PADDING, nTextOffsetY); + + basegfx::B2DHomMatrix aTextMatrix(createScaleTranslateB2DHomMatrix( + aFontSize.getX(), aFontSize.getY(), + double(aTextPos.X()), double(aTextPos.Y()))); + + aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), + std::vector<double>(), aFontAttr, css::lang::Locale(), aLineColor))); + + // Create the 'plus' or 'arrow' primitive + B2DRectangle aSignArea(B2DPoint(aRect.Right() - BUTTON_WIDTH, 0.0), + B2DSize(aRect.Right(), aRect.getHeight())); + + B2DPolygon aSign; + if (IsEmptyHeaderFooter()) + { + // Create the + polygon + double nLeft = aSignArea.getMinX() + TEXT_PADDING; + double nRight = aSignArea.getMaxX() - TEXT_PADDING; + double nHalfW = ( nRight - nLeft ) / 2.0; + + double nTop = aSignArea.getCenterY() - nHalfW; + double nBottom = aSignArea.getCenterY() + nHalfW; + + aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nTop)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nTop)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(nRight, aSignArea.getCenterY() - 1.0)); + aSign.append(B2DPoint(nRight, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nBottom)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nBottom)); + aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() + 1.0)); + aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() + 1.0)); + aSign.setClosed(true); + } + else + { + // Create the v polygon + B2DPoint aLeft(aSignArea.getMinX() + TEXT_PADDING, aSignArea.getCenterY()); + B2DPoint aRight(aSignArea.getMaxX() - TEXT_PADDING, aSignArea.getCenterY()); + B2DPoint aBottom((aLeft.getX() + aRight.getX()) / 2.0, aLeft.getY() + 4.0); + aSign.append(aLeft); + aSign.append(aRight); + aSign.append(aBottom); + aSign.setClosed(true); + } + + BColor aSignColor = COL_BLACK.getBColor(); + if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + aSignColor = COL_WHITE.getBColor(); + + aSeq.push_back( drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + B2DPolyPolygon(aSign), aSignColor)) ); + + // Create the processor and process the primitives + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos)); + + // TODO Ghost it all if needed + drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq(1); + double nFadeRate = double(m_nFadeRate) / 100.0; + + const basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(), + 1.0 - nFadeRate); + + aGhostedSeq[0] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::ModifiedColorPrimitive2D(aSeq, aBColorModifier)); + + pProcessor->process(aGhostedSeq); +} + +bool SwHeaderFooterWin::IsEmptyHeaderFooter( ) const +{ + bool bResult = true; + + // Actually check it + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + + bool const bFirst(GetPageFrame()->OnFirstPage()); + const SwFrameFormat *const pFormat = (GetPageFrame()->OnRightPage()) + ? pDesc->GetRightFormat(bFirst) + : pDesc->GetLeftFormat(bFirst); + + if ( pFormat ) + { + if ( m_bIsHeader ) + bResult = !pFormat->GetHeader().IsActive(); + else + bResult = !pFormat->GetFooter().IsActive(); + } + + return bResult; +} + +void SwHeaderFooterWin::ExecuteCommand(const OString& rIdent) +{ + SwView& rView = GetEditWin()->GetView(); + SwWrtShell& rSh = rView.GetWrtShell(); + + const OUString& rStyleName = GetPageFrame()->GetPageDesc()->GetName(); + if (rIdent == "edit") + { + OString sPageId = m_bIsHeader ? OString("header") : OString("footer"); + rView.GetDocShell()->FormatPage(rStyleName, sPageId, rSh); + } + else if (rIdent == "borderback") + { + const SwPageDesc* pDesc = GetPageFrame()->GetPageDesc(); + const SwFrameFormat& rMaster = pDesc->GetMaster(); + SwFrameFormat* pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetFooter().GetFooterFormat() ); + if ( m_bIsHeader ) + pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetHeader().GetHeaderFormat() ); + SfxItemSet aSet( pHFFormat->GetAttrSet() ); + + // Items to hand over XPropertyList things like XColorList, + // XHatchList, XGradientList, and XBitmapList to the Area TabPage: + aSet.MergeRange( SID_COLOR_TABLE, SID_PATTERN_LIST ); + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + rSh.GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->PutAreaListItems( aSet ); + + aSet.MergeRange(SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER); + // Create a box info item... needed by the dialog + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + const SfxPoolItem *pBoxInfo; + if (SfxItemState::SET == pHFFormat->GetAttrSet().GetItemState(SID_ATTR_BORDER_INNER, true, &pBoxInfo)) + aBoxInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxInfo->Clone())); + + aBoxInfo->SetTable(false); + aBoxInfo->SetDist(true); + aBoxInfo->SetMinDist(false); + aBoxInfo->SetDefDist(MIN_BORDER_DIST); + aBoxInfo->SetValid(SvxBoxInfoItemValidFlags::DISABLE); + aSet.Put(*aBoxInfo); + + if (svx::ShowBorderBackgroundDlg( GetFrameWeld(), &aSet ) ) + { + pHFFormat->SetFormatAttr( aSet ); + rView.GetDocShell()->SetModified(); + } + } + else if (rIdent == "delete") + { + rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, false, true ); + // warning: "this" may be disposed now + rSh.GetWin()->GrabFocusToDocument(); + } + else if (rIdent == "insert_pagenumber") + { + SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame(); + pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGNUMBER); + } + else if (rIdent == "insert_pagecount") + { + SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame(); + pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGCOUNT); + } +} + +void SwHeaderFooterWin::SetReadonly( bool bReadonly ) +{ + ShowAll( !bReadonly ); +} + +void SwHeaderFooterWin::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (IsEmptyHeaderFooter()) + { + SwView& rView = GetEditWin()->GetView(); + SwWrtShell& rSh = rView.GetWrtShell(); + + const OUString& rStyleName = GetPageFrame()->GetPageDesc()->GetName(); + rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, true, false ); + } + else + MenuButton::MouseButtonDown( rMEvt ); +} + +void SwHeaderFooterWin::Select() +{ + ExecuteCommand(GetCurItemIdent()); +} + +IMPL_LINK_NOARG(SwHeaderFooterWin, FadeHandler, Timer *, void) +{ + if (m_bIsAppearing && m_nFadeRate > 0) + m_nFadeRate -= 25; + else if (!m_bIsAppearing && m_nFadeRate < 100) + m_nFadeRate += 25; + + if (m_nFadeRate != 100 && !IsVisible()) + { + Show(); + m_pLine->Show(); + } + else if (m_nFadeRate == 100 && IsVisible()) + { + Show(false); + m_pLine->Show(false); + } + else + Invalidate(); + + if (IsVisible() && m_nFadeRate > 0 && m_nFadeRate < 100) + m_aFadeTimer.Start(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/OverlayRanges.cxx b/sw/source/uibase/docvw/OverlayRanges.cxx new file mode 100644 index 000000000..e00aaa30c --- /dev/null +++ b/sw/source/uibase/docvw/OverlayRanges.cxx @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "OverlayRanges.hxx" +#include <view.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdview.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/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.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 OverlayRanges::createOverlayObjectPrimitive2DSequence() + { + const sal_uInt32 nCount(getRanges().size()); + drawinglayer::primitive2d::Primitive2DContainer aRetval; + aRetval.resize(nCount); + for ( sal_uInt32 a = 0; a < nCount; ++a ) + { + const basegfx::BColor aRGBColor(getBaseColor().getBColor()); + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(maRanges[a])); + aRetval[a] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + aRGBColor)); + } + // embed all rectangles in transparent paint + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + const sal_uInt16 nTransparence( aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() ); + const double fTransparence( nTransparence / 100.0 ); + const drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + aRetval, + fTransparence)); + + if ( mbShowSolidBorder ) + { + 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(2); + aRetval[0] = aUnifiedTransparence; + aRetval[1] = aOutline; + } + else + { + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence }; + } + + return aRetval; + } + + /*static*/ std::unique_ptr<OverlayRanges> OverlayRanges::CreateOverlayRange( + SwView const & rDocView, + const Color& rColor, + const std::vector< basegfx::B2DRange >& rRanges, + const bool bShowSolidBorder ) + { + std::unique_ptr<OverlayRanges> pOverlayRanges; + + SdrView* pView = rDocView.GetDrawView(); + if ( pView != nullptr ) + { + SdrPaintWindow* pCandidate = pView->GetPaintWindow(0); + const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager(); + + if ( xTargetOverlay.is() ) + { + pOverlayRanges.reset(new sw::overlay::OverlayRanges( rColor, rRanges, bShowSolidBorder )); + xTargetOverlay->add( *pOverlayRanges ); + } + } + + return pOverlayRanges; + } + + OverlayRanges::OverlayRanges( + const Color& rColor, + const std::vector< basegfx::B2DRange >& rRanges, + const bool bShowSolidBorder ) + : sdr::overlay::OverlayObject( rColor ) + , maRanges( rRanges ) + , mbShowSolidBorder( bShowSolidBorder ) + { + // no AA for highlight overlays + allowAntiAliase(false); + } + + OverlayRanges::~OverlayRanges() + { + if( getOverlayManager() ) + { + getOverlayManager()->remove(*this); + } + } + + void OverlayRanges::setRanges(const std::vector< basegfx::B2DRange >& rNew) + { + if(rNew != maRanges) + { + maRanges = rNew; + objectChange(); + } + } + + void OverlayRanges::ShowSolidBorder() + { + if ( !mbShowSolidBorder ) + { + mbShowSolidBorder = true; + objectChange(); + } + } + + void OverlayRanges::HideSolidBorder() + { + if ( mbShowSolidBorder ) + { + mbShowSolidBorder = false; + objectChange(); + } + } + +} // end of namespace sw::overlay + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/OverlayRanges.hxx b/sw/source/uibase/docvw/OverlayRanges.hxx new file mode 100644 index 000000000..7482deef8 --- /dev/null +++ b/sw/source/uibase/docvw/OverlayRanges.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_UIBASE_DOCVW_OVERLAYRANGES_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_OVERLAYRANGES_HXX + +#include <svx/sdr/overlay/overlayobject.hxx> +#include <basegfx/range/b2drange.hxx> + +#include <vector> + +class SwView; + +namespace sw +{ + namespace overlay + { + class OverlayRanges final : public sdr::overlay::OverlayObject + { + public: + static std::unique_ptr<OverlayRanges> CreateOverlayRange( + SwView const & rDocView, + const Color& rColor, + const std::vector< basegfx::B2DRange >& rRanges, + const bool bShowSolidBorder ); + + virtual ~OverlayRanges() override; + + // data read access + const std::vector< basegfx::B2DRange >& getRanges() const + { + return maRanges; + } + + // data write access + void setRanges(const std::vector< basegfx::B2DRange >& rNew); + + void ShowSolidBorder(); + void HideSolidBorder(); + + private: + OverlayRanges( + const Color& rColor, + const std::vector< basegfx::B2DRange >& rRanges, + const bool bShowSolidBorder ); + + // geometry creation for OverlayObject + virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override; + + // geometry of overlay + std::vector< basegfx::B2DRange > maRanges; + bool mbShowSolidBorder; + }; + } // end of namespace overlay +} // end of namespace sw + +#endif // INCLUDED_SW_SOURCE_UIBASE_DOCVW_OVERLAYRANGES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/PageBreakWin.cxx b/sw/source/uibase/docvw/PageBreakWin.cxx new file mode 100644 index 000000000..84ebedb0e --- /dev/null +++ b/sw/source/uibase/docvw/PageBreakWin.cxx @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <bitmaps.hlst> + +#include <cmdid.h> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <ndtxt.hxx> +#include <DashedLine.hxx> +#include <doc.hxx> +#include <edtwin.hxx> +#include <fmtpdsc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentContentOperations.hxx> +#include <PageBreakWin.hxx> +#include <pagefrm.hxx> +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <uiitems.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> + +#include <basegfx/color/bcolortools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <editeng/formatbreakitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <memory> + +#define BUTTON_WIDTH 30 +#define BUTTON_HEIGHT 19 +#define ARROW_WIDTH 9 + +using namespace basegfx; +using namespace basegfx::utils; + +namespace +{ + class SwBreakDashedLine : public SwDashedLine + { + private: + VclPtr<SwPageBreakWin> m_pWin; + + public: + SwBreakDashedLine( vcl::Window* pParent, Color& ( *pColorFn )(), SwPageBreakWin* pWin ) : + SwDashedLine( pParent, pColorFn ), + m_pWin( pWin ) {}; + virtual ~SwBreakDashedLine() override { disposeOnce(); } + virtual void dispose() override { m_pWin.clear(); SwDashedLine::dispose(); } + + virtual void MouseMove( const MouseEvent& rMEvt ) override; + }; + + void SwBreakDashedLine::MouseMove( const MouseEvent& rMEvt ) + { + if ( rMEvt.IsLeaveWindow() ) + { + // don't fade if we just move to the 'button' + Point aEventPos( GetPosPixel() + rMEvt.GetPosPixel() ); + if ( !m_pWin->Contains( aEventPos ) || !m_pWin->IsVisible() ) + m_pWin->Fade( false ); + } + else if ( !m_pWin->IsVisible() ) + { + m_pWin->Fade( true ); + } + + if ( !rMEvt.IsSynthetic() && !m_pWin->IsVisible() ) + { + m_pWin->UpdatePosition( rMEvt.GetPosPixel() ); + } + } +} + +SwPageBreakWin::SwPageBreakWin( SwEditWin* pEditWin, const SwFrame *pFrame ) : + SwFrameMenuButtonBase( pEditWin, pFrame ), + m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/pagebreakmenu.ui", ""), + m_pPopupMenu(m_aBuilder.get_menu("menu")), + m_pLine( nullptr ), + m_bIsAppearing( false ), + m_nFadeRate( 100 ), + m_nDelayAppearing( 0 ), + m_bDestroyed( false ) +{ + // Use pixels for the rest of the drawing + SetMapMode( MapMode ( MapUnit::MapPixel ) ); + + // Create the line control + m_pLine = VclPtr<SwBreakDashedLine>::Create( GetEditWin(), &SwViewOption::GetPageBreakColor, this ); + + // Set the popup menu + m_pPopupMenu->SetDeactivateHdl( LINK( this, SwPageBreakWin, HideHandler ) ); + SetPopupMenu(m_pPopupMenu); + + m_aFadeTimer.SetTimeout( 50 ); + m_aFadeTimer.SetInvokeHandler( LINK( this, SwPageBreakWin, FadeHandler ) ); +} + +SwPageBreakWin::~SwPageBreakWin( ) +{ + disposeOnce(); +} + +void SwPageBreakWin::dispose() +{ + m_bDestroyed = true; + m_aFadeTimer.Stop(); + + m_pLine.disposeAndClear(); + m_pPopupMenu.clear(); + m_aBuilder.disposeBuilder(); + + SwFrameMenuButtonBase::dispose(); +} + +void SwPageBreakWin::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + + // Properly paint the control + BColor aColor = SwViewOption::GetPageBreakColor().getBColor(); + + BColor aHslLine = rgb2hsl(aColor); + double nLuminance = aHslLine.getZ(); + nLuminance += (1.0 - nLuminance) * 0.75; + if ( aHslLine.getZ() > 0.7 ) + nLuminance = aHslLine.getZ() * 0.7; + aHslLine.setZ(nLuminance); + BColor aOtherColor = hsl2rgb(aHslLine); + + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + if (rSettings.GetHighContrastMode()) + { + aColor = rSettings.GetDialogTextColor().getBColor(); + aOtherColor = rSettings.GetDialogColor().getBColor(); + } + + bool bRtl = AllSettings::GetLayoutRTL(); + + drawinglayer::primitive2d::Primitive2DContainer aSeq(3); + B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(aRect); + B2DPolygon aPolygon = createPolygonFromRect(aBRect, 3.0 / BUTTON_WIDTH, 3.0 / BUTTON_HEIGHT); + + // Create the polygon primitives + aSeq[0].set(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + B2DPolyPolygon(aPolygon), aOtherColor)); + aSeq[1].set(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + aPolygon, aColor)); + + // Create the primitive for the image + BitmapEx aBmpEx(RID_BMP_PAGE_BREAK); + double nImgOfstX = 3.0; + if (bRtl) + nImgOfstX = aRect.Right() - aBmpEx.GetSizePixel().Width() - 3.0; + aSeq[2].set(new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D( + aBmpEx, B2DPoint(nImgOfstX, 1.0))); + + double nTop = double(aRect.getHeight()) / 2.0; + double nBottom = nTop + 4.0; + double nLeft = aRect.getWidth() - ARROW_WIDTH - 6.0; + if (bRtl) + nLeft = ARROW_WIDTH - 2.0; + double nRight = nLeft + 8.0; + + B2DPolygon aTriangle; + aTriangle.append(B2DPoint(nLeft, nTop)); + aTriangle.append(B2DPoint(nRight, nTop)); + aTriangle.append(B2DPoint((nLeft + nRight) / 2.0, nBottom)); + aTriangle.setClosed(true); + + BColor aTriangleColor = COL_BLACK.getBColor(); + if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + aTriangleColor = COL_WHITE.getBColor(); + + aSeq.emplace_back(); + aSeq.back().set( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + B2DPolyPolygon(aTriangle), aTriangleColor)); + + drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq(1); + double nFadeRate = double(m_nFadeRate) / 100.0; + const basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(), + 1.0 - nFadeRate); + aGhostedSeq[0].set( new drawinglayer::primitive2d::ModifiedColorPrimitive2D( + aSeq, aBColorModifier)); + + // Create the processor and process the primitives + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos)); + + pProcessor->process(aGhostedSeq); +} + +void SwPageBreakWin::Select() +{ + SwFrameControlPtr pThis = GetEditWin()->GetFrameControlsManager( ).GetControl( FrameControlType::PageBreak, GetFrame() ); + + OString sIdent = GetCurItemIdent(); + if (sIdent == "edit") + { + const SwLayoutFrame* pBodyFrame = static_cast< const SwLayoutFrame* >( GetPageFrame()->Lower() ); + while ( pBodyFrame && !pBodyFrame->IsBodyFrame() ) + pBodyFrame = static_cast< const SwLayoutFrame* >( pBodyFrame->GetNext() ); + + SwEditWin* pEditWin = GetEditWin(); + + if ( pBodyFrame ) + { + SwWrtShell& rSh = pEditWin->GetView().GetWrtShell(); + bool bOldLock = rSh.IsViewLocked(); + rSh.LockView( true ); + + if ( pBodyFrame->Lower()->IsTabFrame() ) + { + rSh.Push( ); + rSh.ClearMark(); + + SwContentFrame *pCnt = const_cast< SwContentFrame* >( pBodyFrame->ContainsContent() ); + SwContentNode* pNd = pCnt->IsTextFrame() + ? static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst() + : static_cast<SwNoTextFrame*>(pCnt)->GetNode(); + rSh.SetSelection( *pNd ); + + SfxStringItem aItem(pEditWin->GetView().GetPool().GetWhich(FN_FORMAT_TABLE_DLG), "textflow"); + pEditWin->GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + FN_FORMAT_TABLE_DLG, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + SwContentFrame *pCnt = const_cast< SwContentFrame* >( pBodyFrame->ContainsContent() ); + SwContentNode* pNd = pCnt->IsTextFrame() + ? static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst() + : static_cast<SwNoTextFrame*>(pCnt)->GetNode(); + + SwPaM aPaM( *pNd ); + SwPaMItem aPaMItem( pEditWin->GetView().GetPool( ).GetWhich( FN_PARAM_PAM ), &aPaM ); + SfxStringItem aItem( pEditWin->GetView().GetPool( ).GetWhich( SID_PARA_DLG ), "textflow" ); + pEditWin->GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + SID_PARA_DLG, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem, &aPaMItem }); + } + rSh.LockView( bOldLock ); + pEditWin->GrabFocus( ); + } + } + else if (sIdent == "delete") + { + const SwLayoutFrame* pBodyFrame = static_cast< const SwLayoutFrame* >( GetPageFrame()->Lower() ); + while ( pBodyFrame && !pBodyFrame->IsBodyFrame() ) + pBodyFrame = static_cast< const SwLayoutFrame* >( pBodyFrame->GetNext() ); + + if ( pBodyFrame ) + { + + SwContentFrame *pCnt = const_cast< SwContentFrame* >( pBodyFrame->ContainsContent() ); + SwContentNode* pNd = pCnt->IsTextFrame() + ? static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst() + : static_cast<SwNoTextFrame*>(pCnt)->GetNode(); + + pNd->GetDoc()->GetIDocumentUndoRedo( ).StartUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr ); + + SfxItemSet aSet( + GetEditWin()->GetView().GetWrtShell().GetAttrPool(), + svl::Items<RES_PAGEDESC, RES_BREAK>{}); + aSet.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) ); + aSet.Put( SwFormatPageDesc( nullptr ) ); + + SwPaM aPaM( *pNd ); + pNd->GetDoc()->getIDocumentContentOperations().InsertItemSet( + aPaM, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame()); + + pNd->GetDoc()->GetIDocumentUndoRedo( ).EndUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr ); + } + } + + // Only fade if there is more than this temporary shared pointer: + // The main reference has been deleted due to a page break removal + if ( pThis.use_count() > 1 ) + Fade( false ); +} + +void SwPageBreakWin::MouseMove( const MouseEvent& rMEvt ) +{ + if ( rMEvt.IsLeaveWindow() ) + { + // don't fade if we just move to the 'line', or the popup menu is open + Point aEventPos( rMEvt.GetPosPixel() + rMEvt.GetPosPixel() ); + if ( !Contains( aEventPos ) && !PopupMenu::IsInExecute() ) + Fade( false ); + } + else if ( !IsVisible() ) + Fade( true ); +} + +void SwPageBreakWin::Activate( ) +{ + Fade( true ); + MenuButton::Activate(); +} + +void SwPageBreakWin::UpdatePosition(const std::optional<Point>& xEvtPt) +{ + if ( xEvtPt ) + { + if ( xEvtPt == m_xMousePt ) + return; + m_xMousePt = xEvtPt; + } + + const SwPageFrame* pPageFrame = GetPageFrame(); + const SwFrame* pPrevPage = pPageFrame; + do + { + pPrevPage = pPrevPage->GetPrev(); + } + while ( pPrevPage && ( ( pPrevPage->getFrameArea().Top( ) == pPageFrame->getFrameArea().Top( ) ) + || static_cast< const SwPageFrame* >( pPrevPage )->IsEmptyPage( ) ) ); + + ::tools::Rectangle aBoundRect = GetEditWin()->LogicToPixel( pPageFrame->GetBoundRect(GetEditWin()).SVRect() ); + ::tools::Rectangle aFrameRect = GetEditWin()->LogicToPixel( pPageFrame->getFrameArea().SVRect() ); + + long nYLineOffset = ( aBoundRect.Top() + aFrameRect.Top() ) / 2; + if ( pPrevPage ) + { + ::tools::Rectangle aPrevFrameRect = GetEditWin()->LogicToPixel( pPrevPage->getFrameArea().SVRect() ); + nYLineOffset = ( aPrevFrameRect.Bottom() + aFrameRect.Top() ) / 2; + } + + // Get the page + sidebar coords + long nPgLeft = aFrameRect.Left(); + long nPgRight = aFrameRect.Right(); + + unsigned long nSidebarWidth = 0; + const SwPostItMgr* pPostItMngr = GetEditWin()->GetView().GetWrtShell().GetPostItMgr(); + if ( pPostItMngr && pPostItMngr->HasNotes() && pPostItMngr->ShowNotes() ) + nSidebarWidth = pPostItMngr->GetSidebarBorderWidth( true ) + pPostItMngr->GetSidebarWidth( true ); + + if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::LEFT ) + nPgLeft -= nSidebarWidth; + else if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::RIGHT ) + nPgRight += nSidebarWidth; + + Size aBtnSize( BUTTON_WIDTH + ARROW_WIDTH, BUTTON_HEIGHT ); + + // Place the button on the left or right? + ::tools::Rectangle aVisArea = GetEditWin()->LogicToPixel( GetEditWin()->GetView().GetVisArea() ); + + long nLineLeft = std::max( nPgLeft, aVisArea.Left() ); + long nLineRight = std::min( nPgRight, aVisArea.Right() ); + long nBtnLeft = nLineLeft; + + if ( m_xMousePt ) + { + nBtnLeft = nLineLeft + m_xMousePt->X() - aBtnSize.getWidth() / 2; + + if ( nBtnLeft < nLineLeft ) + nBtnLeft = nLineLeft; + else if ( ( nBtnLeft + aBtnSize.getWidth() ) > nLineRight ) + nBtnLeft = nLineRight - aBtnSize.getWidth(); + } + + // Set the button position + Point aBtnPos( nBtnLeft, nYLineOffset - BUTTON_HEIGHT / 2 ); + SetPosSizePixel( aBtnPos, aBtnSize ); + + // Set the line position + Point aLinePos( nLineLeft, nYLineOffset - 5 ); + Size aLineSize( nLineRight - nLineLeft, 10 ); + m_pLine->SetPosSizePixel( aLinePos, aLineSize ); +} + +void SwPageBreakWin::ShowAll( bool bShow ) +{ + m_pLine->Show( bShow ); +} + +bool SwPageBreakWin::Contains( const Point &rDocPt ) const +{ + ::tools::Rectangle aRect( GetPosPixel(), GetSizePixel() ); + if ( aRect.IsInside( rDocPt ) ) + return true; + + ::tools::Rectangle aLineRect( m_pLine->GetPosPixel(), m_pLine->GetSizePixel() ); + return aLineRect.IsInside( rDocPt ); +} + +void SwPageBreakWin::SetReadonly( bool bReadonly ) +{ + ShowAll( !bReadonly ); +} + +void SwPageBreakWin::Fade( bool bFadeIn ) +{ + m_bIsAppearing = bFadeIn; + if ( bFadeIn ) + m_nDelayAppearing = 0; + + if ( !m_bDestroyed && m_aFadeTimer.IsActive( ) ) + m_aFadeTimer.Stop(); + if ( !m_bDestroyed ) + m_aFadeTimer.Start( ); +} + +IMPL_LINK_NOARG(SwPageBreakWin, HideHandler, Menu *, bool) +{ + Fade( false ); + + return false; +} + +IMPL_LINK_NOARG(SwPageBreakWin, FadeHandler, Timer *, void) +{ + const int TICKS_BEFORE_WE_APPEAR = 10; + if ( m_bIsAppearing && m_nDelayAppearing < TICKS_BEFORE_WE_APPEAR ) + { + ++m_nDelayAppearing; + m_aFadeTimer.Start(); + return; + } + + if ( m_bIsAppearing && m_nFadeRate > 0 ) + m_nFadeRate -= 25; + else if ( !m_bIsAppearing && m_nFadeRate < 100 ) + m_nFadeRate += 25; + + if ( m_nFadeRate != 100 && !IsVisible() ) + Show(); + else if ( m_nFadeRate == 100 && IsVisible( ) ) + Hide(); + else + { + UpdatePosition(); + Invalidate(); + } + + if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100) + m_aFadeTimer.Start(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/PostItMgr.cxx b/sw/source/uibase/docvw/PostItMgr.cxx new file mode 100644 index 000000000..b67252a13 --- /dev/null +++ b/sw/source/uibase/docvw/PostItMgr.cxx @@ -0,0 +1,2456 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <boost/property_tree/json_parser.hpp> + +#include <PostItMgr.hxx> +#include <postithelper.hxx> + +#include <AnnotationWin.hxx> +#include "frmsidebarwincontainer.hxx" +#include <accmap.hxx> + +#include <SidebarWindowsConsts.hxx> +#include "AnchorOverlayObject.hxx" +#include "ShadowOverlayObject.hxx" + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> + +#include <chrdlgmodes.hxx> +#include <viewopt.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <fldbas.hxx> +#include <fmtfld.hxx> +#include <docufld.hxx> +#include <edtwin.hxx> +#include <txtfld.hxx> +#include <txtannotationfld.hxx> +#include <rootfrm.hxx> +#include <SwRewriter.hxx> +#include <tools/color.hxx> +#include <unotools/datetime.hxx> + +#include <swmodule.hxx> +#include <strings.hrc> +#include <cmdid.h> + +#include <sfx2/request.hxx> +#include <sfx2/event.hxx> +#include <svl/srchitem.hxx> + +#include <svl/languageoptions.hxx> +#include <svl/hint.hxx> + +#include <svx/svdview.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/outliner.hxx> +#include <editeng/outlobj.hxx> + +#include <comphelper/lok.hxx> +#include <comphelper/string.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <annotsh.hxx> +#include <swabstdlg.hxx> +#include <memory> + +// distance between Anchor Y and initial note position +#define POSTIT_INITIAL_ANCHOR_DISTANCE 20 +//distance between two postits +#define POSTIT_SPACE_BETWEEN 8 +#define POSTIT_MINIMUMSIZE_WITH_META 60 +#define POSTIT_SCROLL_SIDEBAR_HEIGHT 20 + +// if we layout more often we stop, this should never happen +#define MAX_LOOP_COUNT 50 + +using namespace sw::sidebarwindows; +using namespace sw::annotation; + +namespace { + + enum class CommentNotificationType { Add, Remove, Modify, Resolve }; + + bool comp_pos(const std::unique_ptr<SwSidebarItem>& a, const std::unique_ptr<SwSidebarItem>& b) + { + // sort by anchor position + SwPosition aPosAnchorA = a->GetAnchorPosition(); + SwPosition aPosAnchorB = b->GetAnchorPosition(); + + bool aAnchorAInFooter = false; + bool aAnchorBInFooter = false; + + // is the anchor placed in Footnote or the Footer? + if( aPosAnchorA.nNode.GetNode().FindFootnoteStartNode() || aPosAnchorA.nNode.GetNode().FindFooterStartNode() ) + aAnchorAInFooter = true; + if( aPosAnchorB.nNode.GetNode().FindFootnoteStartNode() || aPosAnchorB.nNode.GetNode().FindFooterStartNode() ) + aAnchorBInFooter = true; + + // fdo#34800 + // if AnchorA is in footnote, and AnchorB isn't + // we do not want to change over the position + if( aAnchorAInFooter && !aAnchorBInFooter ) + return false; + // if aAnchorA is not placed in a footnote, and aAnchorB is + // force a change over + else if( !aAnchorAInFooter && aAnchorBInFooter ) + return true; + // If neither or both are in the footer, compare the positions. + // Since footnotes are in Inserts section of nodes array and footers + // in Autotext section, all footnotes precede any footers so no need + // to check that. + else + return aPosAnchorA < aPosAnchorB; + } + + /// Emits LOK notification about one addition/removal/change of a comment + void lcl_CommentNotification(const SwView* pView, const CommentNotificationType nType, const SwSidebarItem* pItem, const sal_uInt32 nPostItId) + { + if (!comphelper::LibreOfficeKit::isActive()) + return; + + boost::property_tree::ptree aAnnotation; + aAnnotation.put("action", (nType == CommentNotificationType::Add ? "Add" : + (nType == CommentNotificationType::Remove ? "Remove" : + (nType == CommentNotificationType::Modify ? "Modify" : + (nType == CommentNotificationType::Resolve ? "Resolve" : "???"))))); + aAnnotation.put("id", nPostItId); + if (nType != CommentNotificationType::Remove && pItem != nullptr) + { + sw::annotation::SwAnnotationWin* pWin = pItem->pPostIt.get(); + + const SwPostItField* pField = pWin->GetPostItField(); + const SwRect& aRect = pWin->GetAnchorRect(); + tools::Rectangle aSVRect(aRect.Pos().getX(), + aRect.Pos().getY(), + aRect.Pos().getX() + aRect.SSize().Width(), + aRect.Pos().getY() + aRect.SSize().Height()); + + if (!pItem->maLayoutInfo.mPositionFromCommentAnchor) + { + // Comments on frames: anchor position is the corner position, not the whole frame. + aSVRect.SetSize(Size(0, 0)); + } + + std::vector<OString> aRects; + for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges()) + { + const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight()); + aRects.push_back(rect.SVRect().toString()); + } + const OString sRects = comphelper::string::join("; ", aRects); + + aAnnotation.put("id", pField->GetPostItId()); + aAnnotation.put("parent", pWin->CalcParent()); + aAnnotation.put("author", pField->GetPar1().toUtf8().getStr()); + aAnnotation.put("text", pField->GetPar2().toUtf8().getStr()); + aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false"); + aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime())); + aAnnotation.put("anchorPos", aSVRect.toString()); + aAnnotation.put("textRange", sRects.getStr()); + } + + boost::property_tree::ptree aTree; + aTree.add_child("comment", aAnnotation); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + std::string aPayload = aStream.str(); + + if (pView) + { + pView->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str()); + } + } + +} // anonymous namespace + +SwPostItMgr::SwPostItMgr(SwView* pView) + : mpView(pView) + , mpWrtShell(mpView->GetDocShell()->GetWrtShell()) + , mpEditWin(&mpView->GetEditWin()) + , mnEventId(nullptr) + , mbWaitingForCalcRects(false) + , mpActivePostIt(nullptr) + , mbLayout(false) + , mbLayoutHeight(0) + , mbLayouting(false) + , mbReadOnly(mpView->GetDocShell()->IsReadOnly()) + , mbDeleteNote(true) + , mpAnswer(nullptr) + , mbIsShowAnchor( false ) +{ + if(!mpView->GetDrawView() ) + mpView->GetWrtShell().MakeDrawView(); + + SwNoteProps aProps; + mbIsShowAnchor = aProps.IsShowAnchor(); + + //make sure we get the colour yellow always, even if not the first one of comments or redlining + SW_MOD()->GetRedlineAuthor(); + + // collect all PostIts and redline comments that exist after loading the document + // don't check for existence for any of them, don't focus them + AddPostIts(false,false); + /* this code can be used once we want redline comments in the Sidebar + AddRedlineComments(false,false); + */ + // we want to receive stuff like SfxHintId::DocChanged + StartListening(*mpView->GetDocShell()); + if (!mvPostItFields.empty()) + { + mbWaitingForCalcRects = true; + mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) ); + } +} + +SwPostItMgr::~SwPostItMgr() +{ + if ( mnEventId ) + Application::RemoveUserEvent( mnEventId ); + // forget about all our Sidebar windows + RemoveSidebarWin(); + EndListening( *mpView->GetDocShell() ); + + mPages.clear(); +} + +void SwPostItMgr::CheckForRemovedPostIts() +{ + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + bool bRemoved = false; + auto it = mvPostItFields.begin(); + while(it != mvPostItFields.end()) + { + if (!(*it)->UseElement(*mpWrtShell->GetLayout(), rIDRA)) + { + EndListening(const_cast<SfxBroadcaster&>(*(*it)->GetBroadcaster())); + std::unique_ptr<SwSidebarItem> p = std::move(*it); + it = mvPostItFields.erase(it); + if (GetActiveSidebarWin() == p->pPostIt) + SetActiveSidebarWin(nullptr); + p->pPostIt.disposeAndClear(); + bRemoved = true; + } + else + ++it; + } + + if ( bRemoved ) + { + // make sure that no deleted items remain in page lists + // todo: only remove deleted ones?! + if ( mvPostItFields.empty() ) + { + PreparePageContainer(); + PrepareView(); + } + else + // if postits are their make sure that page lists are not empty + // otherwise sudden paints can cause pain (in BorderOverPageBorder) + CalcRects(); + } +} + +SwSidebarItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus) +{ + if (bCheckExistence) + { + for (auto const& postItField : mvPostItFields) + { + if ( postItField->GetBroadcaster() == pItem ) + return nullptr; + } + } + mbLayout = bFocus; + + SwSidebarItem* pAnnotationItem = nullptr; + if (dynamic_cast< const SwFormatField *>( pItem ) != nullptr) + { + mvPostItFields.push_back(std::make_unique<SwAnnotationItem>(static_cast<SwFormatField&>(*pItem), bFocus)); + pAnnotationItem = mvPostItFields.back().get(); + } + OSL_ENSURE(dynamic_cast< const SwFormatField *>( pItem ) != nullptr,"Mgr::InsertItem: seems like new stuff was added"); + StartListening(*pItem); + return pAnnotationItem; +} + +void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast ) +{ + EndListening(*pBroadcast); + auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(), + [&pBroadcast](const std::unique_ptr<SwSidebarItem>& pField) { return pField->GetBroadcaster() == pBroadcast; }); + if (i != mvPostItFields.end()) + { + std::unique_ptr<SwSidebarItem> p = std::move(*i); + // tdf#120487 remove from list before dispose, so comment window + // won't be recreated due to the entry still in the list if focus + // transferring from the pPostIt triggers relayout of postits + // tdf#133348 remove from list before calling SetActiveSidebarWin + // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr + mvPostItFields.erase(i); + if (GetActiveSidebarWin() == p->pPostIt) + SetActiveSidebarWin(nullptr); + p->pPostIt.disposeAndClear(); + } + mbLayout = true; + PrepareView(); +} + +void SwPostItMgr::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if ( const SfxEventHint* pSfxEventHint = dynamic_cast<const SfxEventHint*>(&rHint) ) + { + if ( pSfxEventHint->GetEventId() == SfxEventHintId::SwEventLayoutFinished ) + { + if ( !mbWaitingForCalcRects && !mvPostItFields.empty()) + { + mbWaitingForCalcRects = true; + mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) ); + } + } + } + else if ( const SwFormatFieldHint * pFormatHint = dynamic_cast<const SwFormatFieldHint*>(&rHint) ) + { + SwFormatField* pField = const_cast <SwFormatField*>( pFormatHint->GetField() ); + switch ( pFormatHint->Which() ) + { + case SwFormatFieldHintWhich::INSERTED : + { + if (!pField) + { + AddPostIts(); + break; + } + // get field to be inserted from hint + if ( pField->IsFieldInDoc() ) + { + bool bEmpty = !HasNotes(); + SwSidebarItem* pItem = InsertItem( pField, true, false ); + + if (bEmpty && !mvPostItFields.empty()) + PrepareView(true); + + // True until the layout of this post it finishes + if (pItem) + pItem->bPendingLayout = true; + } + else + { + OSL_FAIL("Inserted field not in document!" ); + } + break; + } + case SwFormatFieldHintWhich::REMOVED: + { + if (mbDeleteNote) + { + if (!pField) + { + CheckForRemovedPostIts(); + break; + } + RemoveItem(pField); + + // If LOK has disabled tiled annotations, emit annotation callbacks + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + { + SwPostItField* pPostItField = static_cast<SwPostItField*>(pField->GetField()); + lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, pPostItField->GetPostItId()); + } + } + break; + } + case SwFormatFieldHintWhich::FOCUS: + { + if (pFormatHint->GetView()== mpView) + Focus(rBC); + break; + } + case SwFormatFieldHintWhich::CHANGED: + case SwFormatFieldHintWhich::RESOLVED: + { + SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC); + for (auto const& postItField : mvPostItFields) + { + if ( pFormatField == postItField->GetBroadcaster() ) + { + if (postItField->pPostIt) + { + postItField->pPostIt->SetPostItText(); + mbLayout = true; + } + + // If LOK has disabled tiled annotations, emit annotation callbacks + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + { + if(SwFormatFieldHintWhich::CHANGED == pFormatHint->Which()) + lcl_CommentNotification(mpView, CommentNotificationType::Modify, postItField.get(), 0); + else + lcl_CommentNotification(mpView, CommentNotificationType::Resolve, postItField.get(), 0); + } + break; + } + } + break; + } + + case SwFormatFieldHintWhich::LANGUAGE: + { + SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC); + for (auto const& postItField : mvPostItFields) + { + if ( pFormatField == postItField->GetBroadcaster() ) + { + if (postItField->pPostIt) + { + const SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( postItField->GetFormatField().GetField()->GetLanguage() ); + sal_uInt16 nLangWhichId = 0; + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE ; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: break; + } + postItField->pPostIt->SetLanguage( + SvxLanguageItem( + postItField->GetFormatField().GetField()->GetLanguage(), + nLangWhichId) ); + } + break; + } + } + break; + } + } + } + else + { + SfxHintId nId = rHint.GetId(); + switch ( nId ) + { + case SfxHintId::ModeChanged: + { + if ( mbReadOnly != mpView->GetDocShell()->IsReadOnly() ) + { + mbReadOnly = !mbReadOnly; + SetReadOnlyState(); + mbLayout = true; + } + break; + } + case SfxHintId::DocChanged: + { + if ( mpView->GetDocShell() == &rBC ) + { + if ( !mbWaitingForCalcRects && !mvPostItFields.empty()) + { + mbWaitingForCalcRects = true; + mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) ); + } + } + break; + } + case SfxHintId::SwSplitNodeOperation: + { + // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker + mbDeleteNote = !mbDeleteNote; + break; + } + case SfxHintId::Dying: + { + if ( mpView->GetDocShell() != &rBC ) + { + // field to be removed is the broadcaster + OSL_FAIL("Notification for removed SwFormatField was not sent!"); + RemoveItem(&rBC); + } + break; + } + default: break; + } + } +} + +void SwPostItMgr::Focus(SfxBroadcaster& rBC) +{ + if (!mpWrtShell->GetViewOptions()->IsPostIts()) + { + SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_NOTES); + mpView->ExecViewOptions(aRequest); + } + + for (auto const& postItField : mvPostItFields) + { + // field to get the focus is the broadcaster + if ( &rBC == postItField->GetBroadcaster() ) + { + if (postItField->pPostIt) + { + postItField->pPostIt->GrabFocus(); + MakeVisible(postItField->pPostIt); + } + else + { + // when the layout algorithm starts, this postit is created and receives focus + postItField->bFocus = true; + } + } + } +} + +bool SwPostItMgr::CalcRects() +{ + if ( mnEventId ) + { + // if CalcRects() was forced and an event is still pending: remove it + // it is superfluous and also may cause reentrance problems if triggered while layouting + Application::RemoveUserEvent( mnEventId ); + mnEventId = nullptr; + } + + bool bChange = false; + bool bRepair = false; + PreparePageContainer(); + if ( !mvPostItFields.empty() ) + { + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + for (auto const& pItem : mvPostItFields) + { + if (!pItem->UseElement(*mpWrtShell->GetLayout(), rIDRA)) + { + OSL_FAIL("PostIt is not in doc or other wrong use"); + bRepair = true; + continue; + } + const SwRect aOldAnchorRect( pItem->maLayoutInfo.mPosition ); + const SwPostItHelper::SwLayoutStatus eOldLayoutStatus = pItem->mLayoutStatus; + const sal_uLong nOldStartNodeIdx( pItem->maLayoutInfo.mnStartNodeIdx ); + const sal_Int32 nOldStartContent( pItem->maLayoutInfo.mnStartContent ); + { + // update layout information + const SwTextAnnotationField* pTextAnnotationField = + dynamic_cast< const SwTextAnnotationField* >( pItem->GetFormatField().GetTextField() ); + const ::sw::mark::IMark* pAnnotationMark = + pTextAnnotationField != nullptr ? pTextAnnotationField->GetAnnotationMark() : nullptr; + if ( pAnnotationMark != nullptr ) + { + pItem->mLayoutStatus = + SwPostItHelper::getLayoutInfos( + pItem->maLayoutInfo, + pItem->GetAnchorPosition(), + pAnnotationMark ); + } + else + { + pItem->mLayoutStatus = + SwPostItHelper::getLayoutInfos( pItem->maLayoutInfo, pItem->GetAnchorPosition() ); + } + } + bChange = bChange + || pItem->maLayoutInfo.mPosition != aOldAnchorRect + || pItem->mLayoutStatus != eOldLayoutStatus + || pItem->maLayoutInfo.mnStartNodeIdx != nOldStartNodeIdx + || pItem->maLayoutInfo.mnStartContent != nOldStartContent; + } + + // show notes in right order in navigator + //prevent Anchors during layout to overlap, e.g. when moving a frame + if (mvPostItFields.size()>1 ) + std::stable_sort(mvPostItFields.begin(), mvPostItFields.end(), comp_pos); + + // sort the items into the right page vector, so layout can be done by page + for (auto const& pItem : mvPostItFields) + { + if( SwPostItHelper::INVISIBLE == pItem->mLayoutStatus ) + { + if (pItem->pPostIt) + pItem->pPostIt->HideNote(); + continue; + } + + if( SwPostItHelper::HIDDEN == pItem->mLayoutStatus ) + { + if (!mpWrtShell->GetViewOptions()->IsShowHiddenChar()) + { + if (pItem->pPostIt) + pItem->pPostIt->HideNote(); + continue; + } + } + + const unsigned long aPageNum = pItem->maLayoutInfo.mnPageNumber; + if (aPageNum > mPages.size()) + { + const unsigned long nNumberOfPages = mPages.size(); + mPages.reserve(aPageNum); + for (unsigned long j=0; j<aPageNum - nNumberOfPages; ++j) + mPages.emplace_back( new SwPostItPageItem()); + } + mPages[aPageNum-1]->mvSidebarItems.push_back(pItem.get()); + mPages[aPageNum-1]->mPageRect = pItem->maLayoutInfo.mPageFrame; + mPages[aPageNum-1]->eSidebarPosition = pItem->maLayoutInfo.meSidebarPosition; + } + + if (!bChange && mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)) + { + long nLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() ); + if( nLayoutHeight > mbLayoutHeight ) + { + if (mPages[0]->bScrollbar || HasScrollbars()) + bChange = true; + } + else if( nLayoutHeight < mbLayoutHeight ) + { + if (mPages[0]->bScrollbar || !BorderOverPageBorder(1)) + bChange = true; + } + } + } + + if ( bRepair ) + CheckForRemovedPostIts(); + + mbLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() ); + mbWaitingForCalcRects = false; + return bChange; +} + +bool SwPostItMgr::HasScrollbars() const +{ + for (auto const& postItField : mvPostItFields) + { + if (postItField->bShow && postItField->pPostIt && postItField->pPostIt->HasScrollbar()) + return true; + } + return false; +} + +void SwPostItMgr::PreparePageContainer() +{ + // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost + long lPageSize = mpWrtShell->GetNumPages(); + long lContainerSize = mPages.size(); + + if (lContainerSize < lPageSize) + { + mPages.reserve(lPageSize); + for (long i=0; i<lPageSize - lContainerSize;i++) + mPages.emplace_back( new SwPostItPageItem()); + } + else if (lContainerSize > lPageSize) + { + for (int i=mPages.size()-1; i >= lPageSize;--i) + { + mPages.pop_back(); + } + } + // only clear the list, DO NOT delete the objects itself + for (auto const& page : mPages) + { + page->mvSidebarItems.clear(); + if (mvPostItFields.empty()) + page->bScrollbar = false; + } +} + +void SwPostItMgr::LayoutPostIts() +{ + bool bEnableMapMode = comphelper::LibreOfficeKit::isActive() && !mpEditWin->IsMapModeEnabled(); + if (bEnableMapMode) + mpEditWin->EnableMapMode(); + + if ( !mvPostItFields.empty() && !mbWaitingForCalcRects ) + { + mbLayouting = true; + + //loop over all pages and do the layout + // - create SwPostIt if necessary + // - place SwPostIts on their initial position + // - calculate necessary height for all PostIts together + bool bUpdate = false; + for (std::unique_ptr<SwPostItPageItem>& pPage : mPages) + { + // only layout if there are notes on this page + if (!pPage->mvSidebarItems.empty()) + { + std::vector<SwAnnotationWin*> aVisiblePostItList; + unsigned long lNeededHeight = 0; + long mlPageBorder = 0; + long mlPageEnd = 0; + + for (auto const& pItem : pPage->mvSidebarItems) + { + VclPtr<SwAnnotationWin> pPostIt = pItem->pPostIt; + + if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT ) + { + // x value for notes positioning + mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true); + //bending point + mlPageEnd = + mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) + ? pItem->maLayoutInfo.mPagePrtArea.Left() + : pPage->mPageRect.Left() + 350; + } + else if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT ) + { + // x value for notes positioning + mlPageBorder = mpEditWin->LogicToPixel( Point(pPage->mPageRect.Right(), 0)).X() + GetSidebarBorderWidth(true); + //bending point + mlPageEnd = + mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) + ? pItem->maLayoutInfo.mPagePrtArea.Right() : + pPage->mPageRect.Right() - 350; + } + + if (pItem->bShow) + { + long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y(); + long aPostItHeight = 0; + if (!pPostIt) + { + pPostIt = pItem->GetSidebarWindow( mpView->GetEditWin(), + *this ); + pPostIt->InitControls(); + pPostIt->SetReadonly(mbReadOnly); + pItem->pPostIt = pPostIt; + if (mpAnswer) + { + if (static_cast<bool>(pPostIt->CalcParent())) //do we really have another note in front of this one + pPostIt->InitAnswer(mpAnswer); + delete mpAnswer; + mpAnswer = nullptr; + } + } + + pPostIt->SetChangeTracking( + pItem->mLayoutStatus, + GetColorAnchor(pItem->maLayoutInfo.mRedlineAuthor)); + pPostIt->SetSidebarPosition(pPage->eSidebarPosition); + pPostIt->SetFollow(static_cast<bool>(pPostIt->CalcParent())); + aPostItHeight = ( pPostIt->GetPostItTextHeight() < pPostIt->GetMinimumSizeWithoutMeta() + ? pPostIt->GetMinimumSizeWithoutMeta() + : pPostIt->GetPostItTextHeight() ) + + pPostIt->GetMetaHeight(); + pPostIt->SetPosSizePixelRect( mlPageBorder , + Y - GetInitialAnchorDistance(), + GetSidebarWidth(true), + aPostItHeight, + pItem->maLayoutInfo.mPosition, + mlPageEnd ); + pPostIt->ChangeSidebarItem( *pItem ); + + if (pItem->bFocus) + { + mbLayout = true; + pPostIt->GrabFocus(); + pItem->bFocus = false; + } + // only the visible postits are used for the final layout + aVisiblePostItList.push_back(pPostIt); + lNeededHeight += pPostIt->IsFollow() ? aPostItHeight : aPostItHeight+GetSpaceBetween(); + } + else // we don't want to see it + { + if (pPostIt) + pPostIt->HideNote(); + } + } + + if ((!aVisiblePostItList.empty()) && ShowNotes()) + { + bool bOldScrollbar = pPage->bScrollbar; + if (ShowNotes()) + pPage->bScrollbar = LayoutByPage(aVisiblePostItList, pPage->mPageRect.SVRect(), lNeededHeight); + else + pPage->bScrollbar = false; + if (!pPage->bScrollbar) + { + pPage->lOffset = 0; + } + else if (sal_Int32 nScrollSize = GetScrollSize()) + { + //when we changed our zoom level, the offset value can be too big, so lets check for the largest possible zoom value + long aAvailableHeight = mpEditWin->LogicToPixel(Size(0,pPage->mPageRect.Height())).Height() - 2 * GetSidebarScrollerHeight(); + long lOffset = -1 * nScrollSize * (aVisiblePostItList.size() - aAvailableHeight / nScrollSize); + if (pPage->lOffset < lOffset) + pPage->lOffset = lOffset; + } + bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate; + const long aSidebarheight = pPage->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0; + /* + TODO + - enlarge all notes till GetNextBorder(), as we resized to average value before + */ + //lets hide the ones which overlap the page + for (auto const& visiblePostIt : aVisiblePostItList) + { + if (pPage->lOffset != 0) + visiblePostIt->TranslateTopPosition(pPage->lOffset); + + bool bBottom = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y()+visiblePostIt->VirtualSize().Height())).Y() <= (pPage->mPageRect.Bottom()-aSidebarheight); + bool bTop = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() >= (pPage->mPageRect.Top()+aSidebarheight); + if ( bBottom && bTop ) + { + // When tiled rendering, make sure that only the + // view that has the comment focus emits callbacks, + // so the editing view jumps to the comment, but + // not the others. + bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting(); + if (!bTiledPainting) + // No focus -> disable callbacks. + comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt->HasChildPathFocus()); + visiblePostIt->ShowNote(); + if (!bTiledPainting) + { + comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting); + visiblePostIt->InvalidateControl(); + } + } + else + { + if (mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() < (pPage->mPageRect.Top()+aSidebarheight)) + { + if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT ) + visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Left(), + pPage->mPageRect.Top())); + else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT ) + visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Right(), + pPage->mPageRect.Top())); + } + else + { + if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT ) + visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Left(), + pPage->mPageRect.Bottom())); + else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT ) + visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Right(), + pPage->mPageRect.Bottom())); + } + OSL_ENSURE(pPage->bScrollbar,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true"); + } + } + } + else + { + for (auto const& visiblePostIt : aVisiblePostItList) + { + visiblePostIt->SetPosAndSize(); + } + + bool bOldScrollbar = pPage->bScrollbar; + pPage->bScrollbar = false; + bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate; + } + + for (auto const& visiblePostIt : aVisiblePostItList) + { + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + { + if (visiblePostIt->GetSidebarItem().bPendingLayout) + lcl_CommentNotification(mpView, CommentNotificationType::Add, &visiblePostIt->GetSidebarItem(), 0); + else if (visiblePostIt->IsAnchorRectChanged()) + { + lcl_CommentNotification(mpView, CommentNotificationType::Modify, &visiblePostIt->GetSidebarItem(), 0); + visiblePostIt->ResetAnchorRectChanged(); + } + } + + // Layout for this post it finished now + visiblePostIt->GetSidebarItem().bPendingLayout = false; + } + } + else + { + if (pPage->bScrollbar) + bUpdate = true; + pPage->bScrollbar = false; + } + } + + if (!ShowNotes()) + { // we do not want to see the notes anymore -> Options-Writer-View-Notes + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + bool bRepair = false; + for (auto const& postItField : mvPostItFields) + { + if (!postItField->UseElement(*mpWrtShell->GetLayout(), rIDRA)) + { + OSL_FAIL("PostIt is not in doc!"); + bRepair = true; + continue; + } + + if (postItField->pPostIt) + { + postItField->pPostIt->HideNote(); + if (postItField->pPostIt->HasChildPathFocus()) + { + SetActiveSidebarWin(nullptr); + postItField->pPostIt->GrabFocusToDocument(); + } + } + } + + if ( bRepair ) + CheckForRemovedPostIts(); + } + + // notes scrollbar is otherwise not drawn correctly for some cases + // scrollbar area is enough + if (bUpdate) + mpEditWin->Invalidate(); /*This is a super expensive relayout and render of the entire page*/ + + mbLayouting = false; + } + + if (bEnableMapMode) + mpEditWin->EnableMapMode(false); +} + +bool SwPostItMgr::BorderOverPageBorder(unsigned long aPage) const +{ + if ( mPages[aPage-1]->mvSidebarItems.empty() ) + { + OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!"); + return false; + } + + auto aItem = mPages[aPage-1]->mvSidebarItems.end(); + --aItem; + OSL_ENSURE ((*aItem)->pPostIt,"BorderOverPageBorder: NULL postIt, should never happen"); + if ((*aItem)->pPostIt) + { + const long aSidebarheight = mPages[aPage-1]->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0; + const long aEndValue = mpEditWin->PixelToLogic(Point(0,(*aItem)->pPostIt->GetPosPixel().Y()+(*aItem)->pPostIt->GetSizePixel().Height())).Y(); + return aEndValue <= mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight; + } + else + return false; +} + +void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage) +{ + assert(nPage < mPages.size()); + if (nPage >= mPages.size()) + return; + for (auto const& pItem : mPages[nPage]->mvSidebarItems) + { + SwAnnotationWin* pPostIt = pItem->pPostIt; + if (!pPostIt) + continue; + Point aPoint(mpEditWin->PixelToLogic(pPostIt->GetPosPixel())); + pPostIt->Draw(pOutDev, aPoint, DrawFlags::NONE); + } +} + +void SwPostItMgr::PaintTile(OutputDevice& rRenderContext) +{ + for (const std::unique_ptr<SwSidebarItem>& pItem : mvPostItFields) + { + SwAnnotationWin* pPostIt = pItem->pPostIt; + if (!pPostIt) + continue; + + bool bEnableMapMode = !mpEditWin->IsMapModeEnabled(); + mpEditWin->EnableMapMode(); + rRenderContext.Push(PushFlags::MAPMODE); + Point aOffset(mpEditWin->PixelToLogic(pPostIt->GetPosPixel())); + MapMode aMapMode(rRenderContext.GetMapMode()); + aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset); + rRenderContext.SetMapMode(aMapMode); + Size aSize(rRenderContext.PixelToLogic(pPostIt->GetSizePixel())); + tools::Rectangle aRectangle(Point(0, 0), aSize); + + pPostIt->PaintTile(rRenderContext, aRectangle); + + rRenderContext.Pop(); + if (bEnableMapMode) + mpEditWin->EnableMapMode(false); + } +} + +void SwPostItMgr::Scroll(const long lScroll,const unsigned long aPage) +{ + OSL_ENSURE((lScroll % GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value"); + // do not scroll more than necessary up or down + if ( ((mPages[aPage-1]->lOffset == 0) && (lScroll>0)) || ( BorderOverPageBorder(aPage) && (lScroll<0)) ) + return; + + const bool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage); + const bool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage); + const long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height(); + for (auto const& item : mPages[aPage-1]->mvSidebarItems) + { + SwAnnotationWin* pPostIt = item->pPostIt; + // if this is an answer, we should take the normal position and not the real, slightly moved position + pPostIt->SetVirtualPosSize(pPostIt->GetPosPixel(),pPostIt->GetSizePixel()); + pPostIt->TranslateTopPosition(lScroll); + + if (item->bShow) + { + bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y()+pPostIt->VirtualSize().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight); + bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight); + if ( bBottom && bTop) + { + pPostIt->ShowNote(); + } + else + { + if ( mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() < (mPages[aPage-1]->mPageRect.Top()+aSidebarheight)) + { + if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT) + pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Top())); + else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT) + pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Top())); + } + else + { + if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT) + pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Bottom())); + else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT) + pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Bottom())); + } + } + } + } + mPages[aPage-1]->lOffset += lScroll; + if ( (bOldUp != ArrowEnabled(KEY_PAGEUP,aPage)) ||(bOldDown != ArrowEnabled(KEY_PAGEDOWN,aPage)) ) + { + mpEditWin->Invalidate(GetBottomScrollRect(aPage)); + mpEditWin->Invalidate(GetTopScrollRect(aPage)); + } +} + +void SwPostItMgr::AutoScroll(const SwAnnotationWin* pPostIt,const unsigned long aPage ) +{ + // otherwise all notes are visible + if (mPages[aPage-1]->bScrollbar) + { + const long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height(); + const bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight); + const bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight); + if ( !(bBottom && bTop)) + { + const long aDiff = bBottom ? mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Top() + aSidebarheight)).Y() - pPostIt->GetPosPixel().Y() : + mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Bottom() - aSidebarheight)).Y() - (pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height()); + // this just adds the missing value to get the next a* GetScrollSize() after aDiff + // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100 + const auto nScrollSize = GetScrollSize(); + assert(nScrollSize); + const long lScroll = bBottom ? (aDiff + ( nScrollSize - (aDiff % nScrollSize))) : (aDiff - (nScrollSize + (aDiff % nScrollSize))); + Scroll(lScroll, aPage); + } + } +} + +void SwPostItMgr::MakeVisible(const SwAnnotationWin* pPostIt ) +{ + long aPage = -1; + // we don't know the page yet, lets find it ourselves + std::vector<SwPostItPageItem*>::size_type n=0; + for (auto const& page : mPages) + { + for (auto const& item : page->mvSidebarItems) + { + if (item->pPostIt==pPostIt) + { + aPage = n+1; + break; + } + } + ++n; + } + if (aPage!=-1) + AutoScroll(pPostIt,aPage); + tools::Rectangle aNoteRect (Point(pPostIt->GetPosPixel().X(),pPostIt->GetPosPixel().Y()-5),pPostIt->GetSizePixel()); + if (!aNoteRect.IsEmpty()) + mpWrtShell->MakeVisible(SwRect(mpEditWin->PixelToLogic(aNoteRect))); +} + +bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection,unsigned long aPage) const +{ + switch (aDirection) + { + case KEY_PAGEUP: + { + return (mPages[aPage-1]->lOffset != 0); + } + case KEY_PAGEDOWN: + { + return (!BorderOverPageBorder(aPage)); + } + default: return false; + } +} + +Color SwPostItMgr::GetArrowColor(sal_uInt16 aDirection,unsigned long aPage) const +{ + if (ArrowEnabled(aDirection,aPage)) + { + if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + return COL_WHITE; + else + return COL_NOTES_SIDEPANE_ARROW_ENABLED; + } + else + { + return COL_NOTES_SIDEPANE_ARROW_DISABLED; + } +} + +bool SwPostItMgr::LayoutByPage(std::vector<SwAnnotationWin*> &aVisiblePostItList, const tools::Rectangle& rBorder, long lNeededHeight) +{ + /*** General layout idea:***/ + // - if we have space left, we always move the current one up, + // otherwise the next one down + // - first all notes are resized + // - then the real layout starts + + //rBorder is the page rect + const tools::Rectangle aBorder = mpEditWin->LogicToPixel(rBorder); + long lTopBorder = aBorder.Top() + 5; + long lBottomBorder = aBorder.Bottom() - 5; + const long lVisibleHeight = lBottomBorder - lTopBorder; //aBorder.GetHeight() ; + const size_t nPostItListSize = aVisiblePostItList.size(); + long lTranslatePos = 0; + bool bScrollbars = false; + + // do all necessary resizings + if (nPostItListSize > 0 && lVisibleHeight < lNeededHeight) + { + // ok, now we have to really resize and adding scrollbars + const long lAverageHeight = (lVisibleHeight - nPostItListSize*GetSpaceBetween()) / nPostItListSize; + if (lAverageHeight<GetMinimumSizeWithMeta()) + { + bScrollbars = true; + lTopBorder += GetSidebarScrollerHeight() + 10; + lBottomBorder -= (GetSidebarScrollerHeight() + 10); + for (auto const& visiblePostIt : aVisiblePostItList) + visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta())); + } + else + { + for (auto const& visiblePostIt : aVisiblePostItList) + { + if ( visiblePostIt->VirtualSize().getHeight() > lAverageHeight) + visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),lAverageHeight)); + } + } + } + + //start the real layout so nothing overlaps anymore + if (aVisiblePostItList.size()>1) + { + int loop = 0; + bool bDone = false; + // if no window is moved anymore we are finished + while (!bDone) + { + loop++; + bDone = true; + long lSpaceUsed = lTopBorder + GetSpaceBetween(); + for(auto i = aVisiblePostItList.begin(); i != aVisiblePostItList.end() ; ++i) + { + auto aNextPostIt = i; + ++aNextPostIt; + + if (aNextPostIt != aVisiblePostItList.end()) + { + lTranslatePos = ( (*i)->VirtualPos().Y() + (*i)->VirtualSize().Height()) - (*aNextPostIt)->VirtualPos().Y(); + if (lTranslatePos > 0) // note windows overlaps the next one + { + // we are not done yet, loop at least once more + bDone = false; + // if there is space left, move the current note up + // it could also happen that there is no space left for the first note due to a scrollbar + // then we also jump into, so we move the current one up and the next one down + if ( (lSpaceUsed <= (*i)->VirtualPos().Y()) || (i==aVisiblePostItList.begin())) + { + // we have space left, so let's move the current one up + if ( ((*i)->VirtualPos().Y()- lTranslatePos - GetSpaceBetween()) > lTopBorder) + { + if ((*aNextPostIt)->IsFollow()) + (*i)->TranslateTopPosition(-1*(lTranslatePos+ANCHORLINE_WIDTH)); + else + (*i)->TranslateTopPosition(-1*(lTranslatePos+GetSpaceBetween())); + } + else + { + long lMoveUp = (*i)->VirtualPos().Y() - lTopBorder; + (*i)->TranslateTopPosition(-1* lMoveUp); + if ((*aNextPostIt)->IsFollow()) + (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+ANCHORLINE_WIDTH) - lMoveUp); + else + (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+GetSpaceBetween()) - lMoveUp); + } + } + else + { + // no space left, left move the next one down + if ((*aNextPostIt)->IsFollow()) + (*aNextPostIt)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH); + else + (*aNextPostIt)->TranslateTopPosition(lTranslatePos+GetSpaceBetween()); + } + } + else + { + // the first one could overlap the topborder instead of a second note + if (i==aVisiblePostItList.begin()) + { + long lMoveDown = lTopBorder - (*i)->VirtualPos().Y(); + if (lMoveDown>0) + { + bDone = false; + (*i)->TranslateTopPosition( lMoveDown); + } + } + } + if ( (*aNextPostIt)->IsFollow() ) + lSpaceUsed += (*i)->VirtualSize().Height() + ANCHORLINE_WIDTH; + else + lSpaceUsed += (*i)->VirtualSize().Height() + GetSpaceBetween(); + } + else + { + //(*i) is the last visible item + auto aPrevPostIt = i; + --aPrevPostIt; + lTranslatePos = ( (*aPrevPostIt)->VirtualPos().Y() + (*aPrevPostIt)->VirtualSize().Height() ) - (*i)->VirtualPos().Y(); + if (lTranslatePos > 0) + { + bDone = false; + if ( ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()+lTranslatePos) < lBottomBorder) + { + if ( (*i)->IsFollow() ) + (*i)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH); + else + (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween()); + } + else + { + (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()) ); + } + } + else + { + // note does not overlap, but we might be over the lower border + // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border + if (!bScrollbars && ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height() > lBottomBorder) ) + { + bDone = false; + (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height())); + } + } + } + } + // security check so we don't loop forever + if (loop>MAX_LOOP_COUNT) + { + OSL_FAIL("PostItMgr::Layout(): We are looping forever"); + break; + } + } + } + else + { + // only one left, make sure it is not hidden at the top or bottom + auto i = aVisiblePostItList.begin(); + lTranslatePos = lTopBorder - (*i)->VirtualPos().Y(); + if (lTranslatePos>0) + { + (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween()); + } + lTranslatePos = lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()); + if (lTranslatePos<0) + { + (*i)->TranslateTopPosition(lTranslatePos); + } + } + return bScrollbars; + } + +void SwPostItMgr::AddPostIts(const bool bCheckExistence, const bool bFocus) +{ + const bool bEmpty = mvPostItFields.empty(); + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + SwFieldType* pType = mpView->GetDocShell()->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(),false); + std::vector<SwFormatField*> vFormatFields; + pType->CollectPostIts(vFormatFields, rIDRA, mpWrtShell->GetLayout()->IsHideRedlines()); + for(auto pFormatField : vFormatFields) + InsertItem(pFormatField, bCheckExistence, bFocus); + // if we just added the first one we have to update the view for centering + if (bEmpty && !mvPostItFields.empty()) + PrepareView(true); +} + +void SwPostItMgr::RemoveSidebarWin() +{ + for (auto& postItField : mvPostItFields) + { + EndListening( *const_cast<SfxBroadcaster*>(postItField->GetBroadcaster()) ); + postItField->pPostIt.disposeAndClear(); + postItField.reset(); + } + mvPostItFields.clear(); + + // all postits removed, no items should be left in pages + PreparePageContainer(); +} + +namespace { + +class FilterFunctor +{ +public: + virtual bool operator()(const SwFormatField* pField) const = 0; + virtual ~FilterFunctor() {} +}; + +class IsPostitField : public FilterFunctor +{ +public: + bool operator()(const SwFormatField* pField) const override + { + return pField->GetField()->GetTyp()->Which() == SwFieldIds::Postit; + } +}; + +class IsPostitFieldWithAuthorOf : public FilterFunctor +{ + OUString m_sAuthor; +public: + explicit IsPostitFieldWithAuthorOf(const OUString &rAuthor) + : m_sAuthor(rAuthor) + { + } + bool operator()(const SwFormatField* pField) const override + { + if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit) + return false; + return static_cast<const SwPostItField*>(pField->GetField())->GetPar1() == m_sAuthor; + } +}; + +class IsPostitFieldWithPostitId : public FilterFunctor +{ + sal_uInt32 m_nPostItId; +public: + explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId) + : m_nPostItId(nPostItId) + {} + + bool operator()(const SwFormatField* pField) const override + { + if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit) + return false; + return static_cast<const SwPostItField*>(pField->GetField())->GetPostItId() == m_nPostItId; + } +}; + +class IsFieldNotDeleted : public FilterFunctor +{ +private: + IDocumentRedlineAccess const& m_rIDRA; + FilterFunctor const& m_rNext; + +public: + IsFieldNotDeleted(IDocumentRedlineAccess const& rIDRA, + const FilterFunctor & rNext) + : m_rIDRA(rIDRA) + , m_rNext(rNext) + { + } + bool operator()(const SwFormatField* pField) const override + { + if (!m_rNext(pField)) + return false; + if (!pField->GetTextField()) + return false; + return !sw::IsFieldDeletedInModel(m_rIDRA, *pField->GetTextField()); + } +}; + +//Manages the passed in vector by automatically removing entries if they are deleted +//and automatically adding entries if they appear in the document and match the +//functor. +// +//This will completely refill in the case of a "anonymous" NULL pField stating +//rather unhelpfully that "something changed" so you may process the same +//Fields more than once. +class FieldDocWatchingStack : public SfxListener +{ + std::vector<std::unique_ptr<SwSidebarItem>>& sidebarItemVector; + std::vector<const SwFormatField*> v; + SwDocShell& m_rDocShell; + FilterFunctor& m_rFilter; + + virtual void Notify(SfxBroadcaster&, const SfxHint& rHint) override + { + const SwFormatFieldHint* pHint = dynamic_cast<const SwFormatFieldHint*>(&rHint); + if (pHint) + { + bool bAllInvalidated = false; + if (pHint->Which() == SwFormatFieldHintWhich::REMOVED) + { + const SwFormatField* pField = pHint->GetField(); + bAllInvalidated = pField == nullptr; + if (!bAllInvalidated && m_rFilter(pField)) + { + EndListening(const_cast<SwFormatField&>(*pField)); + v.erase(std::remove(v.begin(), v.end(), pField), v.end()); + } + } + else if (pHint->Which() == SwFormatFieldHintWhich::INSERTED) + { + const SwFormatField* pField = pHint->GetField(); + bAllInvalidated = pField == nullptr; + if (!bAllInvalidated && m_rFilter(pField)) + { + StartListening(const_cast<SwFormatField&>(*pField)); + v.push_back(pField); + } + } + + if (bAllInvalidated) + FillVector(); + + return; + } + } + +public: + FieldDocWatchingStack(std::vector<std::unique_ptr<SwSidebarItem>>& in, SwDocShell &rDocShell, FilterFunctor& rFilter) + : sidebarItemVector(in) + , m_rDocShell(rDocShell) + , m_rFilter(rFilter) + { + FillVector(); + StartListening(m_rDocShell); + } + void FillVector() + { + EndListeningToAllFields(); + v.clear(); + v.reserve(sidebarItemVector.size()); + for (auto const& p : sidebarItemVector) + { + const SwFormatField& rField = p->GetFormatField(); + if (!m_rFilter(&rField)) + continue; + StartListening(const_cast<SwFormatField&>(rField)); + v.push_back(&rField); + } + } + void EndListeningToAllFields() + { + for (auto const& pField : v) + { + EndListening(const_cast<SwFormatField&>(*pField)); + } + } + virtual ~FieldDocWatchingStack() override + { + EndListeningToAllFields(); + EndListening(m_rDocShell); + } + const SwFormatField* pop() + { + if (v.empty()) + return nullptr; + const SwFormatField* p = v.back(); + EndListening(const_cast<SwFormatField&>(*p)); + v.pop_back(); + return p; + } +}; + +} + +// copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well +// RemoveItem will clean up the core field and visible postit if necessary +// we cannot just delete everything as before, as postits could move into change tracking +void SwPostItMgr::Delete(const OUString& rAuthor) +{ + mpWrtShell->StartAllAction(); + if (HasActiveSidebarWin() && (GetActiveSidebarWin()->GetAuthor() == rAuthor)) + { + SetActiveSidebarWin(nullptr); + } + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_AUTHOR_NOTES) + rAuthor); + mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter ); + + IsPostitFieldWithAuthorOf aFilter(rAuthor); + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + IsFieldNotDeleted aFilter2(rIDRA, aFilter); + FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2); + while (const SwFormatField* pField = aStack.pop()) + { + if (mpWrtShell->GotoField(*pField)) + mpWrtShell->DelRight(); + } + mpWrtShell->EndUndo(); + PrepareView(); + mpWrtShell->EndAllAction(); + mbLayout = true; + CalcRects(); + LayoutPostIts(); +} + +void SwPostItMgr::Delete(sal_uInt32 nPostItId) +{ + mpWrtShell->StartAllAction(); + if (HasActiveSidebarWin() && + mpActivePostIt->GetPostItField()->GetPostItId() == nPostItId) + { + SetActiveSidebarWin(nullptr); + } + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT)); + mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter ); + + IsPostitFieldWithPostitId aFilter(nPostItId); + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + IsFieldNotDeleted aFilter2(rIDRA, aFilter); + FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2); + const SwFormatField* pField = aStack.pop(); + if (pField && mpWrtShell->GotoField(*pField)) + mpWrtShell->DelRight(); + mpWrtShell->EndUndo(); + PrepareView(); + mpWrtShell->EndAllAction(); + mbLayout = true; + CalcRects(); + LayoutPostIts(); +} + +void SwPostItMgr::ToggleResolvedForThread(sal_uInt32 nPostItId) +{ + mpWrtShell->StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT)); + + // We have no undo ID at the moment. + + IsPostitFieldWithPostitId aFilter(nPostItId); + FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter); + const SwFormatField* pField = aStack.pop(); + // pField now contains our AnnotationWin object + if (pField) { + SwAnnotationWin* pWin = GetSidebarWin(pField); + pWin->ToggleResolvedForThread(); + } + + PrepareView(); + mpWrtShell->EndAllAction(); + mbLayout = true; + CalcRects(); + LayoutPostIts(); +} + + +void SwPostItMgr::Delete() +{ + mpWrtShell->StartAllAction(); + SetActiveSidebarWin(nullptr); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_ALL_NOTES) ); + mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter ); + + IsPostitField aFilter; + IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); + IsFieldNotDeleted aFilter2(rIDRA, aFilter); + FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), + aFilter2); + while (const SwFormatField* pField = aStack.pop()) + { + if (mpWrtShell->GotoField(*pField)) + mpWrtShell->DelRight(); + } + + mpWrtShell->EndUndo(); + PrepareView(); + mpWrtShell->EndAllAction(); + mbLayout = true; + CalcRects(); + LayoutPostIts(); +} + +void SwPostItMgr::ExecuteFormatAllDialog(SwView& rView) +{ + if (mvPostItFields.empty()) + return; + sw::annotation::SwAnnotationWin *pOrigActiveWin = GetActiveSidebarWin(); + sw::annotation::SwAnnotationWin *pWin = pOrigActiveWin; + if (!pWin) + { + for (auto const& postItField : mvPostItFields) + { + pWin = postItField->pPostIt; + if (pWin) + break; + } + } + if (!pWin) + return; + SetActiveSidebarWin(pWin); + OutlinerView* pOLV = pWin->GetOutlinerView(); + SfxItemSet aEditAttr(pOLV->GetAttribs()); + SfxItemPool* pPool(SwAnnotationShell::GetAnnotationPool(rView)); + SfxItemSet aDlgAttr(*pPool, svl::Items<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END>{}); + aDlgAttr.Put(aEditAttr); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(rView.GetFrameWeld(), rView, aDlgAttr, SwCharDlgMode::Ann)); + sal_uInt16 nRet = pDlg->Execute(); + if (RET_OK == nRet) + { + aDlgAttr.Put(*pDlg->GetOutputItemSet()); + FormatAll(aDlgAttr); + } + pDlg.disposeAndClear(); + SetActiveSidebarWin(pOrigActiveWin); +} + +void SwPostItMgr::FormatAll(const SfxItemSet &rNewAttr) +{ + mpWrtShell->StartAllAction(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_FORMAT_ALL_NOTES) ); + mpWrtShell->StartUndo( SwUndoId::INSATTR, &aRewriter ); + + for (auto const& postItField : mvPostItFields) + { + if (!postItField->pPostIt) + continue; + OutlinerView* pOLV = postItField->pPostIt->GetOutlinerView(); + //save old selection + ESelection aOrigSel(pOLV->GetSelection()); + //select all + Outliner *pOutliner = pOLV->GetOutliner(); + if (pOutliner) + { + sal_Int32 nParaCount = pOutliner->GetParagraphCount(); + if (nParaCount > 0) + pOLV->SelectRange(0, nParaCount); + } + //set new char properties + pOLV->SetAttribs(rNewAttr); + //restore old selection + pOLV->SetSelection(aOrigSel); + // tdf#91596 store updated formatting in SwField + postItField->pPostIt->UpdateData(); + } + + mpWrtShell->EndUndo(); + PrepareView(); + mpWrtShell->EndAllAction(); + mbLayout = true; + CalcRects(); + LayoutPostIts(); +} + +void SwPostItMgr::Hide( const OUString& rAuthor ) +{ + for (auto const& postItField : mvPostItFields) + { + if ( postItField->pPostIt && (postItField->pPostIt->GetAuthor() == rAuthor) ) + { + postItField->bShow = false; + postItField->pPostIt->HideNote(); + } + } + + LayoutPostIts(); +} + +void SwPostItMgr::Hide() +{ + for (auto const& postItField : mvPostItFields) + { + postItField->bShow = false; + postItField->pPostIt->HideNote(); + } +} + +void SwPostItMgr::Show() +{ + for (auto const& postItField : mvPostItFields) + { + postItField->bShow = true; + } + LayoutPostIts(); +} + +SwAnnotationWin* SwPostItMgr::GetSidebarWin( const SfxBroadcaster* pBroadcaster) const +{ + for (auto const& postItField : mvPostItFields) + { + if ( postItField->GetBroadcaster() == pBroadcaster) + return postItField->pPostIt; + } + return nullptr; +} + +sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const SwPostItField* pField) const +{ + for (auto const& postItField : mvPostItFields) + { + if ( postItField->GetFormatField().GetField() == pField ) + return postItField->pPostIt.get(); + } + return nullptr; +} + +sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const sal_uInt32 nPostItId) const +{ + for (auto const& postItField : mvPostItFields) + { + if ( static_cast<const SwPostItField*>(postItField->GetFormatField().GetField())->GetPostItId() == nPostItId ) + return postItField->pPostIt.get(); + } + return nullptr; +} + +SwAnnotationWin* SwPostItMgr::GetNextPostIt( sal_uInt16 aDirection, + SwAnnotationWin* aPostIt ) +{ + if (mvPostItFields.size()>1) + { + auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(), + [&aPostIt](const std::unique_ptr<SwSidebarItem>& pField) { return pField->pPostIt == aPostIt; }); + if (i == mvPostItFields.end()) + return nullptr; + + auto iNextPostIt = i; + if (aDirection == KEY_PAGEUP) + { + if ( iNextPostIt == mvPostItFields.begin() ) + { + return nullptr; + } + --iNextPostIt; + } + else + { + ++iNextPostIt; + if ( iNextPostIt == mvPostItFields.end() ) + { + return nullptr; + } + } + // lets quit, we are back at the beginning + if ( (*iNextPostIt)->pPostIt == aPostIt) + return nullptr; + return (*iNextPostIt)->pPostIt; + } + else + return nullptr; +} + +long SwPostItMgr::GetNextBorder() +{ + for (auto const& pPage : mPages) + { + for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b) + { + if ((*b)->pPostIt == mpActivePostIt) + { + auto aNext = b; + ++aNext; + bool bFollow = (aNext != pPage->mvSidebarItems.end()) && (*aNext)->pPostIt->IsFollow(); + if ( pPage->bScrollbar || bFollow ) + { + return -1; + } + else + { + //if this is the last item, return the bottom border otherwise the next item + if (aNext == pPage->mvSidebarItems.end()) + return mpEditWin->LogicToPixel(Point(0,pPage->mPageRect.Bottom())).Y() - GetSpaceBetween(); + else + return (*aNext)->pPostIt->GetPosPixel().Y() - GetSpaceBetween(); + } + } + } + } + + OSL_FAIL("SwPostItMgr::GetNextBorder(): We have to find a next border here"); + return -1; +} + +void SwPostItMgr::SetShadowState(const SwPostItField* pField,bool bCursor) +{ + if (pField) + { + if (pField !=mShadowState.mpShadowField) + { + if (mShadowState.mpShadowField) + { + // reset old one if still alive + // TODO: does not work properly if mouse and cursor was set + sw::annotation::SwAnnotationWin* pOldPostIt = + GetAnnotationWin(mShadowState.mpShadowField); + if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT)) + pOldPostIt->SetViewState(ViewState::NORMAL); + } + //set new one, if it is not currently edited + sw::annotation::SwAnnotationWin* pNewPostIt = GetAnnotationWin(pField); + if (pNewPostIt && pNewPostIt->Shadow() && (pNewPostIt->Shadow()->GetShadowState() != SS_EDIT)) + { + pNewPostIt->SetViewState(ViewState::VIEW); + //remember our new field + mShadowState.mpShadowField = pField; + mShadowState.bCursor = false; + mShadowState.bMouse = false; + } + } + if (bCursor) + mShadowState.bCursor = true; + else + mShadowState.bMouse = true; + } + else + { + if (mShadowState.mpShadowField) + { + if (bCursor) + mShadowState.bCursor = false; + else + mShadowState.bMouse = false; + if (!mShadowState.bCursor && !mShadowState.bMouse) + { + // reset old one if still alive + sw::annotation::SwAnnotationWin* pOldPostIt = GetAnnotationWin(mShadowState.mpShadowField); + if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT)) + { + pOldPostIt->SetViewState(ViewState::NORMAL); + mShadowState.mpShadowField = nullptr; + } + } + } + } +} + +void SwPostItMgr::PrepareView(bool bIgnoreCount) +{ + if (!HasNotes() || bIgnoreCount) + { + mpWrtShell->StartAllAction(); + SwRootFrame* pLayout = mpWrtShell->GetLayout(); + if ( pLayout ) + SwPostItHelper::setSidebarChanged( pLayout, + mpWrtShell->getIDocumentSettingAccess().get( DocumentSettingId::BROWSE_MODE ) ); + mpWrtShell->EndAllAction(); + } +} + +bool SwPostItMgr::ShowScrollbar(const unsigned long aPage) const +{ + if (mPages.size() > aPage-1) + return (mPages[aPage-1]->bScrollbar && !mbWaitingForCalcRects); + else + return false; +} + +bool SwPostItMgr::IsHit(const Point &aPointPixel) +{ + if (HasNotes() && ShowNotes()) + { + const Point aPoint = mpEditWin->PixelToLogic(aPointPixel); + const SwRootFrame* pLayout = mpWrtShell->GetLayout(); + SwRect aPageFrame; + const unsigned long nPageNum = SwPostItHelper::getPageInfo( aPageFrame, pLayout, aPoint ); + if( nPageNum ) + { + tools::Rectangle aRect; + OSL_ENSURE(mPages.size()>nPageNum-1,"SwPostitMgr:: page container size wrong"); + aRect = mPages[nPageNum-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? tools::Rectangle(Point(aPageFrame.Left()-GetSidebarWidth()-GetSidebarBorderWidth(),aPageFrame.Top()),Size(GetSidebarWidth(),aPageFrame.Height())) + : tools::Rectangle( Point(aPageFrame.Right()+GetSidebarBorderWidth(),aPageFrame.Top()) , Size(GetSidebarWidth(),aPageFrame.Height())); + if (aRect.IsInside(aPoint)) + { + // we hit the note's sidebar + // lets now test for the arrow area + if (mPages[nPageNum-1]->bScrollbar) + return ScrollbarHit(nPageNum,aPoint); + else + return false; + } + } + } + return false; +} + +vcl::Window* SwPostItMgr::IsHitSidebarWindow(const Point& rPointLogic) +{ + vcl::Window* pRet = nullptr; + + if (HasNotes() && ShowNotes()) + { + bool bEnableMapMode = !mpEditWin->IsMapModeEnabled(); + if (bEnableMapMode) + mpEditWin->EnableMapMode(); + + for (const std::unique_ptr<SwSidebarItem>& pItem : mvPostItFields) + { + SwAnnotationWin* pPostIt = pItem->pPostIt; + if (!pPostIt) + continue; + + if (pPostIt->IsHitWindow(rPointLogic)) + { + pRet = pPostIt; + break; + } + } + + if (bEnableMapMode) + mpEditWin->EnableMapMode(false); + } + + return pRet; +} + +tools::Rectangle SwPostItMgr::GetBottomScrollRect(const unsigned long aPage) const +{ + SwRect aPageRect = mPages[aPage-1]->mPageRect; + Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? Point(aPageRect.Left() - GetSidebarWidth() - GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height()) + : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height()); + Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ; + return tools::Rectangle(aPointBottom,aSize); +} + +tools::Rectangle SwPostItMgr::GetTopScrollRect(const unsigned long aPage) const +{ + SwRect aPageRect = mPages[aPage-1]->mPageRect; + Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? Point(aPageRect.Left() - GetSidebarWidth() -GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height()) + : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height()); + Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ; + return tools::Rectangle(aPointTop,aSize); +} + +//IMPORTANT: if you change the rects here, also change SwPageFrame::PaintNotesSidebar() +bool SwPostItMgr::ScrollbarHit(const unsigned long aPage,const Point &aPoint) +{ + SwRect aPageRect = mPages[aPage-1]->mPageRect; + Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height()) + : Point(aPageRect.Right() + GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height()); + + Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height()) + : Point(aPageRect.Right()+GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height()); + + tools::Rectangle aRectBottom(GetBottomScrollRect(aPage)); + tools::Rectangle aRectTop(GetTopScrollRect(aPage)); + + if (aRectBottom.IsInside(aPoint)) + { + if (aPoint.X() < long((aPointBottom.X() + GetSidebarWidth()/3))) + Scroll( GetScrollSize(),aPage); + else + Scroll( -1*GetScrollSize(), aPage); + return true; + } + else if (aRectTop.IsInside(aPoint)) + { + if (aPoint.X() < long((aPointTop.X() + GetSidebarWidth()/3*2))) + Scroll(GetScrollSize(), aPage); + else + Scroll(-1*GetScrollSize(), aPage); + return true; + } + return false; +} + +void SwPostItMgr::CorrectPositions() +{ + if ( mbWaitingForCalcRects || mbLayouting || mvPostItFields.empty() ) + return; + + // find first valid note + SwAnnotationWin *pFirstPostIt = nullptr; + for (auto const& postItField : mvPostItFields) + { + pFirstPostIt = postItField->pPostIt; + if (pFirstPostIt) + break; + } + + //if we have not found a valid note, forget about it and leave + if (!pFirstPostIt) + return; + + // yeah, I know, if this is a left page it could be wrong, but finding the page and the note is probably not even faster than just doing it + // check, if anchor overlay object exists. + const long aAnchorX = pFirstPostIt->Anchor() + ? mpEditWin->LogicToPixel( Point(static_cast<long>(pFirstPostIt->Anchor()->GetSixthPosition().getX()),0)).X() + : 0; + const long aAnchorY = pFirstPostIt->Anchor() + ? mpEditWin->LogicToPixel( Point(0,static_cast<long>(pFirstPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1 + : 0; + if (Point(aAnchorX,aAnchorY) != pFirstPostIt->GetPosPixel()) + { + long aAnchorPosX = 0; + long aAnchorPosY = 0; + for (const std::unique_ptr<SwPostItPageItem>& pPage : mPages) + { + for (auto const& item : pPage->mvSidebarItems) + { + // check, if anchor overlay object exists. + if ( item->bShow && item->pPostIt && item->pPostIt->Anchor() ) + { + aAnchorPosX = pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT + ? mpEditWin->LogicToPixel( Point(static_cast<long>(item->pPostIt->Anchor()->GetSeventhPosition().getX()),0)).X() + : mpEditWin->LogicToPixel( Point(static_cast<long>(item->pPostIt->Anchor()->GetSixthPosition().getX()),0)).X(); + aAnchorPosY = mpEditWin->LogicToPixel( Point(0,static_cast<long>(item->pPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1; + item->pPostIt->SetPosPixel(Point(aAnchorPosX,aAnchorPosY)); + } + } + } + } +} + +bool SwPostItMgr::ShowNotes() const +{ + // we only want to see notes if Options - Writer - View - Notes is ticked + return mpWrtShell->GetViewOptions()->IsPostIts(); +} + +bool SwPostItMgr::HasNotes() const +{ + return !mvPostItFields.empty(); +} + +unsigned long SwPostItMgr::GetSidebarWidth(bool bPx) const +{ + bool bEnableMapMode = !mpWrtShell->GetOut()->IsMapModeEnabled(); + sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom(); + if (comphelper::LibreOfficeKit::isActive() && !bEnableMapMode) + { + // The output device is the tile and contains the real wanted scale factor. + double fScaleX = double(mpWrtShell->GetOut()->GetMapMode().GetScaleX()); + nZoom = fScaleX * 100; + } + unsigned long aWidth = static_cast<unsigned long>(nZoom * 1.8); + + if (bPx) + return aWidth; + else + { + if (bEnableMapMode) + // The output device is the window. + mpWrtShell->GetOut()->EnableMapMode(); + long nRet = mpWrtShell->GetOut()->PixelToLogic(Size(aWidth, 0)).Width(); + if (bEnableMapMode) + mpWrtShell->GetOut()->EnableMapMode(false); + return nRet; + } +} + +unsigned long SwPostItMgr::GetSidebarBorderWidth(bool bPx) const +{ + if (bPx) + return 2; + else + return mpWrtShell->GetOut()->PixelToLogic(Size(2,0)).Width(); +} + +Color SwPostItMgr::GetColorDark(std::size_t aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayNormal[] = { + COL_AUTHOR1_NORMAL, COL_AUTHOR2_NORMAL, COL_AUTHOR3_NORMAL, + COL_AUTHOR4_NORMAL, COL_AUTHOR5_NORMAL, COL_AUTHOR6_NORMAL, + COL_AUTHOR7_NORMAL, COL_AUTHOR8_NORMAL, COL_AUTHOR9_NORMAL }; + + return aArrayNormal[ aAuthorIndex % SAL_N_ELEMENTS( aArrayNormal )]; + } + else + return COL_WHITE; +} + +Color SwPostItMgr::GetColorLight(std::size_t aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayLight[] = { + COL_AUTHOR1_LIGHT, COL_AUTHOR2_LIGHT, COL_AUTHOR3_LIGHT, + COL_AUTHOR4_LIGHT, COL_AUTHOR5_LIGHT, COL_AUTHOR6_LIGHT, + COL_AUTHOR7_LIGHT, COL_AUTHOR8_LIGHT, COL_AUTHOR9_LIGHT }; + + return aArrayLight[ aAuthorIndex % SAL_N_ELEMENTS( aArrayLight )]; + } + else + return COL_WHITE; +} + +Color SwPostItMgr::GetColorAnchor(std::size_t aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayAnchor[] = { + COL_AUTHOR1_DARK, COL_AUTHOR2_DARK, COL_AUTHOR3_DARK, + COL_AUTHOR4_DARK, COL_AUTHOR5_DARK, COL_AUTHOR6_DARK, + COL_AUTHOR7_DARK, COL_AUTHOR8_DARK, COL_AUTHOR9_DARK }; + + return aArrayAnchor[ aAuthorIndex % SAL_N_ELEMENTS( aArrayAnchor )]; + } + else + return COL_WHITE; +} + +void SwPostItMgr::SetActiveSidebarWin( SwAnnotationWin* p) +{ + if ( p != mpActivePostIt ) + { + // we need the temp variable so we can set mpActivePostIt before we call DeactivatePostIt + // therefore we get a new layout in DOCCHANGED when switching from postit to document, + // otherwise, GetActivePostIt() would still hold our old postit + SwAnnotationWin* pActive = mpActivePostIt; + mpActivePostIt = p; + if (pActive) + { + pActive->DeactivatePostIt(); + mShadowState.mpShadowField = nullptr; + } + if (mpActivePostIt) + { + mpActivePostIt->GotoPos(); + mpView->AttrChangedNotify(nullptr); + mpActivePostIt->ActivatePostIt(); + } + } +} + +IMPL_LINK_NOARG( SwPostItMgr, CalcHdl, void*, void ) +{ + mnEventId = nullptr; + if ( mbLayouting ) + { + OSL_FAIL("Reentrance problem in Layout Manager!"); + mbWaitingForCalcRects = false; + return; + } + + // do not change order, even if it would seem so in the first place, we need the calcrects always + if (CalcRects() || mbLayout) + { + mbLayout = false; + LayoutPostIts(); + } +} + +void SwPostItMgr::Rescale() +{ + for (auto const& postItField : mvPostItFields) + if ( postItField->pPostIt ) + postItField->pPostIt->Rescale(); +} + +sal_Int32 SwPostItMgr::GetInitialAnchorDistance() const +{ + const Fraction& f( mpEditWin->GetMapMode().GetScaleY() ); + return sal_Int32(POSTIT_INITIAL_ANCHOR_DISTANCE * f); +} + +sal_Int32 SwPostItMgr::GetSpaceBetween() const +{ + const Fraction& f( mpEditWin->GetMapMode().GetScaleY() ); + return sal_Int32(POSTIT_SPACE_BETWEEN * f); +} + +sal_Int32 SwPostItMgr::GetScrollSize() const +{ + const Fraction& f( mpEditWin->GetMapMode().GetScaleY() ); + return sal_Int32((POSTIT_SPACE_BETWEEN + POSTIT_MINIMUMSIZE_WITH_META) * f); +} + +sal_Int32 SwPostItMgr::GetMinimumSizeWithMeta() const +{ + const Fraction& f( mpEditWin->GetMapMode().GetScaleY() ); + return sal_Int32(POSTIT_MINIMUMSIZE_WITH_META * f); +} + +sal_Int32 SwPostItMgr::GetSidebarScrollerHeight() const +{ + const Fraction& f( mpEditWin->GetMapMode().GetScaleY() ); + return sal_Int32(POSTIT_SCROLL_SIDEBAR_HEIGHT * f); +} + +void SwPostItMgr::SetSpellChecking() +{ + for (auto const& postItField : mvPostItFields) + if ( postItField->pPostIt ) + postItField->pPostIt->SetSpellChecking(); +} + +void SwPostItMgr::SetReadOnlyState() +{ + for (auto const& postItField : mvPostItFields) + if ( postItField->pPostIt ) + postItField->pPostIt->SetReadonly( mbReadOnly ); +} + +void SwPostItMgr::CheckMetaText() +{ + for (auto const& postItField : mvPostItFields) + if ( postItField->pPostIt ) + postItField->pPostIt->CheckMetaText(); + +} + +sal_uInt16 SwPostItMgr::Replace(SvxSearchItem const * pItem) +{ + SwAnnotationWin* pWin = GetActiveSidebarWin(); + sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( *pItem ); + if (!aResult) + SetActiveSidebarWin(nullptr); + return aResult; +} + +sal_uInt16 SwPostItMgr::FinishSearchReplace(const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward) +{ + SwAnnotationWin* pWin = GetActiveSidebarWin(); + SvxSearchItem aItem(SID_SEARCH_ITEM ); + aItem.SetSearchOptions(rSearchOptions); + aItem.SetBackward(!bSrchForward); + sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem ); + if (!aResult) + SetActiveSidebarWin(nullptr); + return aResult; +} + +sal_uInt16 SwPostItMgr::SearchReplace(const SwFormatField &pField, const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward) +{ + sal_uInt16 aResult = 0; + SwAnnotationWin* pWin = GetSidebarWin(&pField); + if (pWin) + { + ESelection aOldSelection = pWin->GetOutlinerView()->GetSelection(); + if (bSrchForward) + pWin->GetOutlinerView()->SetSelection(ESelection(0,0,0,0)); + else + pWin->GetOutlinerView()->SetSelection( + ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT)); + SvxSearchItem aItem(SID_SEARCH_ITEM ); + aItem.SetSearchOptions(rSearchOptions); + aItem.SetBackward(!bSrchForward); + aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem ); + if (!aResult) + pWin->GetOutlinerView()->SetSelection(aOldSelection); + else + { + SetActiveSidebarWin(pWin); + MakeVisible(pWin); + } + } + return aResult; +} + +void SwPostItMgr::AssureStdModeAtShell() +{ + // deselect any drawing or frame and leave editing mode + SdrView* pSdrView = mpWrtShell->GetDrawView(); + if ( pSdrView && pSdrView->IsTextEdit() ) + { + bool bLockView = mpWrtShell->IsViewLocked(); + mpWrtShell->LockView( true ); + mpWrtShell->EndTextEdit(); + mpWrtShell->LockView( bLockView ); + } + + if( mpWrtShell->IsSelFrameMode() || mpWrtShell->IsObjSelected()) + { + mpWrtShell->UnSelectFrame(); + mpWrtShell->LeaveSelFrameMode(); + mpWrtShell->GetView().LeaveDrawCreate(); + mpWrtShell->EnterStdMode(); + + mpWrtShell->DrawSelChanged(); + mpView->StopShellTimer(); + } +} + +bool SwPostItMgr::HasActiveSidebarWin() const +{ + return mpActivePostIt != nullptr; +} + +bool SwPostItMgr::HasActiveAnnotationWin() const +{ + return HasActiveSidebarWin() && + mpActivePostIt != nullptr; +} + +void SwPostItMgr::GrabFocusOnActiveSidebarWin() +{ + if ( HasActiveSidebarWin() ) + { + mpActivePostIt->GrabFocus(); + } +} + +void SwPostItMgr::UpdateDataOnActiveSidebarWin() +{ + if ( HasActiveSidebarWin() ) + { + mpActivePostIt->UpdateData(); + } +} + +void SwPostItMgr::DeleteActiveSidebarWin() +{ + if ( HasActiveSidebarWin() ) + { + mpActivePostIt->Delete(); + } +} + +void SwPostItMgr::HideActiveSidebarWin() +{ + if ( HasActiveSidebarWin() ) + { + mpActivePostIt->Hide(); + } +} + +void SwPostItMgr::ToggleInsModeOnActiveSidebarWin() +{ + if ( HasActiveSidebarWin() ) + { + mpActivePostIt->ToggleInsMode(); + } +} + +void SwPostItMgr::ConnectSidebarWinToFrame( const SwFrame& rFrame, + const SwFormatField& rFormatField, + SwAnnotationWin& rSidebarWin ) +{ + if ( mpFrameSidebarWinContainer == nullptr ) + { + mpFrameSidebarWinContainer.reset(new SwFrameSidebarWinContainer()); + } + + const bool bInserted = mpFrameSidebarWinContainer->insert( rFrame, rFormatField, rSidebarWin ); + if ( bInserted && + mpWrtShell->GetAccessibleMap() ) + { + mpWrtShell->GetAccessibleMap()->InvalidatePosOrSize( nullptr, nullptr, &rSidebarWin, SwRect() ); + } +} + +void SwPostItMgr::DisconnectSidebarWinFromFrame( const SwFrame& rFrame, + SwAnnotationWin& rSidebarWin ) +{ + if ( mpFrameSidebarWinContainer != nullptr ) + { + const bool bRemoved = mpFrameSidebarWinContainer->remove( rFrame, rSidebarWin ); + if ( bRemoved && + mpWrtShell->GetAccessibleMap() ) + { + mpWrtShell->GetAccessibleMap()->A11yDispose( nullptr, nullptr, &rSidebarWin ); + } + } +} + +bool SwPostItMgr::HasFrameConnectedSidebarWins( const SwFrame& rFrame ) +{ + bool bRet( false ); + + if ( mpFrameSidebarWinContainer != nullptr ) + { + bRet = !mpFrameSidebarWinContainer->empty( rFrame ); + } + + return bRet; +} + +vcl::Window* SwPostItMgr::GetSidebarWinForFrameByIndex( const SwFrame& rFrame, + const sal_Int32 nIndex ) +{ + vcl::Window* pSidebarWin( nullptr ); + + if ( mpFrameSidebarWinContainer != nullptr ) + { + pSidebarWin = mpFrameSidebarWinContainer->get( rFrame, nIndex ); + } + + return pSidebarWin; +} + +void SwPostItMgr::GetAllSidebarWinForFrame( const SwFrame& rFrame, + std::vector< vcl::Window* >* pChildren ) +{ + if ( mpFrameSidebarWinContainer != nullptr ) + { + mpFrameSidebarWinContainer->getAll( rFrame, pChildren ); + } +} + +void SwPostItMgr::ShowHideResolvedNotes(bool visible) { + for (auto const& pPage : mPages) + { + for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b) + { + if ((*b)->pPostIt->IsThreadResolved()) + { + (*b)->pPostIt->SetResolved(true); + (*b)->pPostIt->GetSidebarItem().bShow = visible; + } + } + } + LayoutPostIts(); +} + +void SwPostItMgr::UpdateResolvedStatus(const sw::annotation::SwAnnotationWin* topNote) { + // Given the topmost note as an argument, scans over all notes and sets the + // 'resolved' state of each descendant of the top notes to the resolved state + // of the top note. + bool resolved = topNote->IsResolved(); + for (auto const& pPage : mPages) + { + for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b) + { + if((*b)->pPostIt->GetTopReplyNote() == topNote) { + (*b)->pPostIt->SetResolved(resolved); + } + } + } +} + +void SwNoteProps::ImplCommit() {} +void SwNoteProps::Notify( const css::uno::Sequence< OUString >& ) {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.cxx b/sw/source/uibase/docvw/ShadowOverlayObject.cxx new file mode 100644 index 000000000..65ccda759 --- /dev/null +++ b/sw/source/uibase/docvw/ShadowOverlayObject.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ShadowOverlayObject.hxx" + +#include <view.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdview.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> + +#include <sw_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/primitivetools2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> + +namespace sw::sidebarwindows { + +// helper SwPostItShadowPrimitive + +namespace { + +// Used to allow view-dependent primitive definition. For that purpose, the +// initially created primitive (this one) always has to be view-independent, +// but the decomposition is made view-dependent. Very simple primitive which +// just remembers the discrete data and applies it at decomposition time. +class ShadowPrimitive : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D +{ +private: + basegfx::B2DPoint maBasePosition; + basegfx::B2DPoint maSecondPosition; + ShadowState maShadowState; + +protected: + virtual void create2DDecomposition( + drawinglayer::primitive2d::Primitive2DContainer& rContainer, + const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override; + +public: + ShadowPrimitive( + const basegfx::B2DPoint& rBasePosition, + const basegfx::B2DPoint& rSecondPosition, + ShadowState aShadowState) + : drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D(), + maBasePosition(rBasePosition), + maSecondPosition(rSecondPosition), + maShadowState(aShadowState) + {} + + // data access + const basegfx::B2DPoint& getSecondPosition() const { return maSecondPosition; } + + virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const override; + + virtual sal_uInt32 getPrimitive2DID() const override; +}; + +} + +void ShadowPrimitive::create2DDecomposition( + drawinglayer::primitive2d::Primitive2DContainer& rContainer, + const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // get logic sizes in object coordinate system + basegfx::B2DRange aRange(maBasePosition); + + switch(maShadowState) + { + case SS_NORMAL: + { + aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (2.0 * getDiscreteUnit()))); + const ::drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute( + drawinglayer::attribute::GradientStyle::Linear, + 0.0, + 0.5, + 0.5, + F_PI, + basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0), + basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0), + 2); + + rContainer.push_back( + new drawinglayer::primitive2d::FillGradientPrimitive2D( + aRange, + aFillGradientAttribute)); + break; + } + case SS_VIEW: + { + aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (4.0 * getDiscreteUnit()))); + const drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute( + drawinglayer::attribute::GradientStyle::Linear, + 0.0, + 0.5, + 0.5, + F_PI, + basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0), + basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0), + 4); + + rContainer.push_back( + new drawinglayer::primitive2d::FillGradientPrimitive2D( + aRange, + aFillGradientAttribute)); + break; + } + case SS_EDIT: + { + aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (4.0 * getDiscreteUnit()))); + const drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute( + drawinglayer::attribute::GradientStyle::Linear, + 0.0, + 0.5, + 0.5, + F_PI, + basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0), + basegfx::BColor(83.0/255.0,83.0/255.0,83.0/255.0), + 4); + + rContainer.push_back( + new drawinglayer::primitive2d::FillGradientPrimitive2D( + aRange, + aFillGradientAttribute)); + break; + } + default: + { + break; + } + } +} + +bool ShadowPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const +{ + if(drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + { + const ShadowPrimitive& rCompare = static_cast< const ShadowPrimitive& >(rPrimitive); + + return (maBasePosition == rCompare.maBasePosition + && getSecondPosition() == rCompare.getSecondPosition() + && maShadowState == rCompare.maShadowState); + } + + return false; +} + +ImplPrimitive2DIDBlock(ShadowPrimitive, PRIMITIVE2D_ID_SWSIDEBARSHADOWPRIMITIVE) + +/* static */ std::unique_ptr<ShadowOverlayObject> ShadowOverlayObject::CreateShadowOverlayObject( SwView const & rDocView ) +{ + std::unique_ptr<ShadowOverlayObject> pShadowOverlayObject; + + if ( rDocView.GetDrawView() ) + { + SdrPaintWindow* pPaintWindow = rDocView.GetDrawView()->GetPaintWindow(0); + if( pPaintWindow ) + { + const rtl::Reference< sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager(); + + if ( xOverlayManager.is() ) + { + pShadowOverlayObject.reset( new ShadowOverlayObject( basegfx::B2DPoint(0,0), + basegfx::B2DPoint(0,0), + Color(0,0,0) ) ); + xOverlayManager->add(*pShadowOverlayObject); + } + } + } + + return pShadowOverlayObject; +} + +ShadowOverlayObject::ShadowOverlayObject( const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPosition, + Color aBaseColor ) + : OverlayObjectWithBasePosition(rBasePos, aBaseColor) + , maSecondPosition(rSecondPosition) + , mShadowState(SS_NORMAL) +{ +} + +ShadowOverlayObject::~ShadowOverlayObject() +{ + if ( getOverlayManager() ) + { + getOverlayManager()->remove(*this); + } +} + +drawinglayer::primitive2d::Primitive2DContainer ShadowOverlayObject::createOverlayObjectPrimitive2DSequence() +{ + const drawinglayer::primitive2d::Primitive2DReference aReference( + new ShadowPrimitive( getBasePosition(), + maSecondPosition, + GetShadowState() ) ); + return drawinglayer::primitive2d::Primitive2DContainer { aReference }; +} + +void ShadowOverlayObject::SetShadowState(ShadowState aState) +{ + if (mShadowState != aState) + { + mShadowState = aState; + + objectChange(); + } +} + +void ShadowOverlayObject::SetPosition( const basegfx::B2DPoint& rPoint1, + const basegfx::B2DPoint& rPoint2) +{ + if(!rPoint1.equal(getBasePosition()) || !rPoint2.equal(maSecondPosition)) + { + maBasePosition = rPoint1; + maSecondPosition = rPoint2; + + objectChange(); + } +} + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.hxx b/sw/source/uibase/docvw/ShadowOverlayObject.hxx new file mode 100644 index 000000000..b9af12c58 --- /dev/null +++ b/sw/source/uibase/docvw/ShadowOverlayObject.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_DOCVW_SHADOWOVERLAYOBJECT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SHADOWOVERLAYOBJECT_HXX + +#include <svx/sdr/overlay/overlayobject.hxx> + +class SwView; + +namespace sw::sidebarwindows { + +enum ShadowState +{ + SS_NORMAL, + SS_VIEW, + SS_EDIT +}; + +class ShadowOverlayObject: public sdr::overlay::OverlayObjectWithBasePosition +{ +protected: + // geometry creation for OverlayObject + virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override; + +private: + basegfx::B2DPoint maSecondPosition; + ShadowState mShadowState; + + ShadowOverlayObject( const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPosition, + Color aBaseColor ); + +public: + virtual ~ShadowOverlayObject() override; + + void SetShadowState(ShadowState aState); + ShadowState GetShadowState() const {return mShadowState;} + + void SetPosition( const basegfx::B2DPoint& rPoint1, + const basegfx::B2DPoint& rPoint2 ); + + static std::unique_ptr<ShadowOverlayObject> CreateShadowOverlayObject( SwView const & rDocView ); +}; + +} // end of namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarScrollBar.cxx b/sw/source/uibase/docvw/SidebarScrollBar.cxx new file mode 100644 index 000000000..c7e2978af --- /dev/null +++ b/sw/source/uibase/docvw/SidebarScrollBar.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/. + */ + +#include "SidebarScrollBar.hxx" + +#include <sfx2/lokhelper.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <AnnotationWin.hxx> + +namespace sw::sidebarwindows +{ +SidebarScrollBar::SidebarScrollBar(sw::annotation::SwAnnotationWin& rSidebarWin, WinBits nStyle, + SwView& rView) + : ScrollBar(&rSidebarWin, nStyle) + , m_rSidebarWin(rSidebarWin) + , m_rView(rView) +{ +} + +void SidebarScrollBar::LogicInvalidate(const tools::Rectangle* pRectangle) +{ + tools::Rectangle aRectangle; + + if (!pRectangle) + { + Push(PushFlags::MAPMODE); + EnableMapMode(); + MapMode aMapMode = GetMapMode(); + aMapMode.SetMapUnit(MapUnit::MapTwip); + SetMapMode(aMapMode); + aRectangle = tools::Rectangle(Point(0, 0), PixelToLogic(GetSizePixel())); + Pop(); + } + else + aRectangle = *pRectangle; + + // Convert from relative twips to absolute ones. + vcl::Window& rParent = m_rSidebarWin.EditWin(); + Point aOffset(GetOutOffXPixel() - rParent.GetOutOffXPixel(), + GetOutOffYPixel() - rParent.GetOutOffYPixel()); + rParent.Push(PushFlags::MAPMODE); + rParent.EnableMapMode(); + aOffset = rParent.PixelToLogic(aOffset); + rParent.Pop(); + aRectangle.Move(aOffset.getX(), aOffset.getY()); + + OString sRectangle = aRectangle.toString(); + SwWrtShell& rWrtShell = m_rView.GetWrtShell(); + SfxLokHelper::notifyInvalidation(rWrtShell.GetSfxViewShell(), sRectangle); +} + +void SidebarScrollBar::MouseButtonUp(const MouseEvent& /*rMouseEvent*/) { EndTracking(); } + +void SidebarScrollBar::MouseMove(const MouseEvent& rMouseEvent) +{ + TrackingEvent aEvent(rMouseEvent); + Tracking(aEvent); +} + +SidebarScrollBar::~SidebarScrollBar() { disposeOnce(); } + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarScrollBar.hxx b/sw/source/uibase/docvw/SidebarScrollBar.hxx new file mode 100644 index 000000000..2fb1a90de --- /dev/null +++ b/sw/source/uibase/docvw/SidebarScrollBar.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_UIBASE_DOCVW_SIDEBARSCROLLBAR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SIDEBARSCROLLBAR_HXX + +#include <vcl/scrbar.hxx> + +class SwView; + +namespace sw +{ +namespace annotation +{ +class SwAnnotationWin; +} +} + +namespace sw +{ +namespace sidebarwindows +{ +/// Similar to the VCL scrollbar, but instrumented with Writer-specific details for LOK. +class SidebarScrollBar : public ScrollBar +{ + sw::annotation::SwAnnotationWin& m_rSidebarWin; + SwView& m_rView; + +protected: + /// @see Window::LogicInvalidate(). + void LogicInvalidate(const tools::Rectangle* pRectangle) override; + void MouseMove(const MouseEvent& rMouseEvent) override; + void MouseButtonUp(const MouseEvent& rMouseEvent) override; + +public: + SidebarScrollBar(sw::annotation::SwAnnotationWin& rSidebarWin, WinBits nStyle, SwView& rView); + ~SidebarScrollBar() override; +}; +} +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarTxtControl.cxx b/sw/source/uibase/docvw/SidebarTxtControl.cxx new file mode 100644 index 000000000..65c67d098 --- /dev/null +++ b/sw/source/uibase/docvw/SidebarTxtControl.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 "SidebarTxtControl.hxx" + +#include "SidebarTxtControlAcc.hxx" +#include <docsh.hxx> +#include <doc.hxx> + +#include <PostItMgr.hxx> +#include <edtwin.hxx> + +#include <cmdid.h> +#include <strings.hrc> + +#include <unotools/securityoptions.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxhelp.hxx> + +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <vcl/weld.hxx> +#include <vcl/gradient.hxx> +#include <vcl/settings.hxx> + +#include <editeng/outliner.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/flditem.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#include <uitool.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <AnnotationWin.hxx> +#include <redline.hxx> +#include <memory> + +namespace sw::sidebarwindows { + +SidebarTextControl::SidebarTextControl( sw::annotation::SwAnnotationWin& rSidebarWin, + WinBits nBits, + SwView& rDocView, + SwPostItMgr& rPostItMgr ) + : Control( &rSidebarWin, nBits ) + , mrSidebarWin( rSidebarWin ) + , mrDocView( rDocView ) + , mrPostItMgr( rPostItMgr ) +{ + AddEventListener( LINK( &mrSidebarWin, sw::annotation::SwAnnotationWin, WindowEventListener ) ); +} + +SidebarTextControl::~SidebarTextControl() +{ + disposeOnce(); +} + +void SidebarTextControl::dispose() +{ + RemoveEventListener( LINK( &mrSidebarWin, sw::annotation::SwAnnotationWin, WindowEventListener ) ); + Control::dispose(); +} + +OutlinerView* SidebarTextControl::GetTextView() const +{ + return mrSidebarWin.GetOutlinerView(); +} + +void SidebarTextControl::GetFocus() +{ + Window::GetFocus(); + if ( !mrSidebarWin.IsMouseOver() ) + { + Invalidate(); + } +} + +void SidebarTextControl::LoseFocus() +{ + // write the visible text back into the SwField + mrSidebarWin.UpdateData(); + + Window::LoseFocus(); + if ( !mrSidebarWin.IsMouseOver() ) + { + Invalidate(); + } +} + +void SidebarTextControl::RequestHelp(const HelpEvent &rEvt) +{ + const char* pResId = nullptr; + switch( mrSidebarWin.GetLayoutStatus() ) + { + case SwPostItHelper::INSERTED: pResId = STR_REDLINE_INSERT; break; + case SwPostItHelper::DELETED: pResId = STR_REDLINE_DELETE; break; + default: pResId = nullptr; + } + + SwContentAtPos aContentAtPos( IsAttrAtPos::Redline ); + if ( pResId && + mrDocView.GetWrtShell().GetContentAtPos( mrSidebarWin.GetAnchorPos(), aContentAtPos ) ) + { + OUString sText = SwResId(pResId) + ": " + + aContentAtPos.aFnd.pRedl->GetAuthorString() + " - " + + GetAppLangDateTimeString( aContentAtPos.aFnd.pRedl->GetTimeStamp() ); + Help::ShowQuickHelp( this,PixelToLogic(tools::Rectangle(rEvt.GetMousePosPixel(),Size(50,10))),sText); + } +} + +void SidebarTextControl::Draw(OutputDevice* pDev, const Point& rPt, DrawFlags) +{ + //Take the control's height, but overwrite the scrollbar area if there was one + Size aSize(PixelToLogic(GetSizePixel())); + + if ( GetTextView() ) + { + GetTextView()->GetOutliner()->Draw(pDev, tools::Rectangle(rPt, aSize)); + } + + if ( mrSidebarWin.GetLayoutStatus()==SwPostItHelper::DELETED ) + { + SetLineColor(mrSidebarWin.GetChangeColor()); + pDev->DrawLine( PixelToLogic( GetPosPixel(), pDev->GetMapMode() ), + PixelToLogic( GetPosPixel() + + Point( GetSizePixel().Width(), + GetSizePixel().Height() ), pDev->GetMapMode() ) ); + pDev->DrawLine( PixelToLogic( GetPosPixel() + + Point( GetSizePixel().Width(),0), pDev->GetMapMode() ), + PixelToLogic( GetPosPixel() + + Point( 0, GetSizePixel().Height() ), pDev->GetMapMode() ) ); + } +} + +void SidebarTextControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (!rRenderContext.GetSettings().GetStyleSettings().GetHighContrastMode()) + { + if (mrSidebarWin.IsMouseOverSidebarWin() || HasFocus()) + { + rRenderContext.DrawGradient(tools::Rectangle(Point(0,0), rRenderContext.PixelToLogic(GetSizePixel())), + Gradient(GradientStyle::Linear, mrSidebarWin.ColorDark(), mrSidebarWin.ColorDark())); + } + else + { + rRenderContext.DrawGradient(tools::Rectangle(Point(0,0), rRenderContext.PixelToLogic(GetSizePixel())), + Gradient(GradientStyle::Linear, mrSidebarWin.ColorLight(), mrSidebarWin.ColorDark())); + } + } + + if (GetTextView()) + { + GetTextView()->Paint(rRect, &rRenderContext); + } + + if (mrSidebarWin.GetLayoutStatus() == SwPostItHelper::DELETED) + { + rRenderContext.SetLineColor(mrSidebarWin.GetChangeColor()); + rRenderContext.DrawLine(rRenderContext.PixelToLogic(GetPosPixel()), + rRenderContext.PixelToLogic(GetPosPixel() + Point(GetSizePixel().Width(), + GetSizePixel().Height()))); + rRenderContext.DrawLine(rRenderContext.PixelToLogic(GetPosPixel() + Point(GetSizePixel().Width(), + 0)), + rRenderContext.PixelToLogic(GetPosPixel() + Point(0, + GetSizePixel().Height()))); + } +} + +void SidebarTextControl::LogicInvalidate(const tools::Rectangle* pRectangle) +{ + tools::Rectangle aRectangle; + + if (!pRectangle) + { + Push(PushFlags::MAPMODE); + EnableMapMode(); + aRectangle = tools::Rectangle(Point(0, 0), PixelToLogic(GetSizePixel())); + Pop(); + } + else + aRectangle = *pRectangle; + + // Convert from relative twips to absolute ones. + vcl::Window& rParent = mrSidebarWin.EditWin(); + Point aOffset(GetOutOffXPixel() - rParent.GetOutOffXPixel(), GetOutOffYPixel() - rParent.GetOutOffYPixel()); + rParent.Push(PushFlags::MAPMODE); + rParent.EnableMapMode(); + aOffset = rParent.PixelToLogic(aOffset); + rParent.Pop(); + aRectangle.Move(aOffset.getX(), aOffset.getY()); + + OString sRectangle = aRectangle.toString(); + SwWrtShell& rWrtShell = mrDocView.GetWrtShell(); + SfxLokHelper::notifyInvalidation(rWrtShell.GetSfxViewShell(), sRectangle); +} + +void SidebarTextControl::KeyInput( const KeyEvent& rKeyEvt ) +{ + if (getenv("SW_DEBUG") && rKeyEvt.GetKeyCode().GetCode() == KEY_F12) + { + if (rKeyEvt.GetKeyCode().IsShift()) + { + mrDocView.GetDocShell()->GetDoc()->dumpAsXml(); + return; + } + } + + const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); + sal_uInt16 nKey = rKeyCode.GetCode(); + if ( ( rKeyCode.IsMod1() && rKeyCode.IsMod2() ) && + ( (nKey == KEY_PAGEUP) || (nKey == KEY_PAGEDOWN) ) ) + { + mrSidebarWin.SwitchToPostIt(nKey); + } + else if ( nKey == KEY_ESCAPE || + ( rKeyCode.IsMod1() && + ( nKey == KEY_PAGEUP || + nKey == KEY_PAGEDOWN ) ) ) + { + mrSidebarWin.SwitchToFieldPos(); + } + else if ( rKeyCode.GetFullCode() == KEY_INSERT ) + { + mrSidebarWin.ToggleInsMode(); + } + else + { + // MakeVisible can lose our MapMode, save it. + auto oldMapMode = GetMapMode(); + //let's make sure we see our note + mrPostItMgr.MakeVisible(&mrSidebarWin); + if (comphelper::LibreOfficeKit::isActive()) + SetMapMode(oldMapMode); + + long aOldHeight = mrSidebarWin.GetPostItTextHeight(); + bool bDone = false; + + /// HACK: need to switch off processing of Undo/Redo in Outliner + if ( !( (nKey == KEY_Z || nKey == KEY_Y) && rKeyCode.IsMod1()) ) + { + bool bIsProtected = mrSidebarWin.IsProtected(); + if ( !bIsProtected || !EditEngine::DoesKeyChangeText(rKeyEvt) ) + { + bDone = GetTextView() && GetTextView()->PostKeyEvent( rKeyEvt ); + } + else + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xQuery->run(); + } + } + if (bDone) + mrSidebarWin.ResizeIfNecessary( aOldHeight, mrSidebarWin.GetPostItTextHeight() ); + else + { + // write back data first when showing navigator + if ( nKey==KEY_F5 ) + mrSidebarWin.UpdateData(); + if (!mrDocView.KeyInput(rKeyEvt)) + Window::KeyInput(rKeyEvt); + } + } + + mrDocView.GetViewFrame()->GetBindings().InvalidateAll(false); +} + +void SidebarTextControl::MouseMove( const MouseEvent& rMEvt ) +{ + if ( GetTextView() ) + { + OutlinerView* pOutlinerView( GetTextView() ); + pOutlinerView->MouseMove( rMEvt ); + // mba: why does OutlinerView not handle the modifier setting?! + // this forces the postit to handle *all* pointer types + SetPointer( pOutlinerView->GetPointer( rMEvt.GetPosPixel() ) ); + + const EditView& aEV = pOutlinerView->GetEditView(); + const SvxFieldItem* pItem = aEV.GetFieldUnderMousePointer(); + if ( pItem ) + { + const SvxFieldData* pField = pItem->GetField(); + const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField ); + if ( pURL ) + { + OUString sText(SfxHelp::GetURLHelpText(pURL->GetURL())); + Help::ShowQuickHelp( + this, PixelToLogic(tools::Rectangle(GetPosPixel(), Size(50, 10))), sText); + } + } + } +} + +void SidebarTextControl::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( GetTextView() ) + { + SvtSecurityOptions aSecOpts; + bool bExecuteMod = aSecOpts.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink); + + if ( !bExecuteMod || (rMEvt.GetModifier() == KEY_MOD1)) + { + const EditView& aEV = GetTextView()->GetEditView(); + const SvxFieldItem* pItem = aEV.GetFieldUnderMousePointer(); + if ( pItem ) + { + const SvxFieldData* pField = pItem->GetField(); + const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField ); + if ( pURL ) + { + GetTextView()->MouseButtonDown( rMEvt ); + SwWrtShell &rSh = mrDocView.GetWrtShell(); + const OUString& sURL( pURL->GetURL() ); + const OUString& sTarget( pURL->GetTargetFrame() ); + ::LoadURL(rSh, sURL, LoadUrlFlags::NONE, sTarget); + return; + } + } + } + } + + GrabFocus(); + if ( GetTextView() ) + { + GetTextView()->MouseButtonDown( rMEvt ); + } + mrDocView.GetViewFrame()->GetBindings().InvalidateAll(false); +} + +void SidebarTextControl::MouseButtonUp( const MouseEvent& rMEvt ) +{ + if ( GetTextView() ) + GetTextView()->MouseButtonUp( rMEvt ); +} + +IMPL_LINK( SidebarTextControl, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void ) +{ + if ( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG ) + { + mrDocView.GetViewFrame()->GetDispatcher()->Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON); + } +} + +void SidebarTextControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + if ( !mrSidebarWin.IsProtected() && + GetTextView() && + GetTextView()->IsWrongSpelledWordAtPos( rCEvt.GetMousePosPixel(), true )) + { + Link<SpellCallbackInfo&,void> aLink = LINK(this, SidebarTextControl, OnlineSpellCallback); + GetTextView()->ExecuteSpellPopup(rCEvt.GetMousePosPixel(),&aLink); + } + else + { + Point aPos; + if (rCEvt.IsMouseEvent()) + aPos = rCEvt.GetMousePosPixel(); + else + { + const Size aSize = GetSizePixel(); + aPos = Point( aSize.getWidth()/2, aSize.getHeight()/2 ); + } + SfxDispatcher::ExecutePopup(this, &aPos); + } + } + else + if (rCEvt.GetCommand() == CommandEventId::Wheel) + { + if (mrSidebarWin.IsScrollbarVisible()) + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + if (pData->IsShift() || pData->IsMod1() || pData->IsMod2()) + { + mrDocView.HandleWheelCommands(rCEvt); + } + else + { + HandleScrollCommand( rCEvt, nullptr , mrSidebarWin.Scrollbar()); + } + } + else + { + mrDocView.HandleWheelCommands(rCEvt); + } + } + else + { + if ( GetTextView() ) + GetTextView()->Command( rCEvt ); + else + Window::Command(rCEvt); + } +} + +OUString SidebarTextControl::GetSurroundingText() const +{ + if (GetTextView()) + return GetTextView()->GetSurroundingText(); + return OUString(); +} + +Selection SidebarTextControl::GetSurroundingTextSelection() const +{ + if( GetTextView() ) + return GetTextView()->GetSurroundingTextSelection(); + else + return Selection( 0, 0 ); +} + +css::uno::Reference< css::accessibility::XAccessible > SidebarTextControl::CreateAccessible() +{ + + SidebarTextControlAccessible* pAcc( new SidebarTextControlAccessible( *this ) ); + css::uno::Reference< css::awt::XWindowPeer > xWinPeer( pAcc ); + SetWindowPeer( xWinPeer, pAcc ); + + css::uno::Reference< css::accessibility::XAccessible > xAcc( xWinPeer, css::uno::UNO_QUERY ); + return xAcc; +} + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarTxtControl.hxx b/sw/source/uibase/docvw/SidebarTxtControl.hxx new file mode 100644 index 000000000..ab9c7604e --- /dev/null +++ b/sw/source/uibase/docvw/SidebarTxtControl.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_UIBASE_DOCVW_SIDEBARTXTCONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SIDEBARTXTCONTROL_HXX + +#include <vcl/ctrl.hxx> + +class OutlinerView; +class SwView; +class SwPostItMgr; +struct SpellCallbackInfo; +namespace sw::annotation { class SwAnnotationWin; } + +namespace sw::sidebarwindows { + +class SidebarTextControl : public Control +{ + private: + sw::annotation::SwAnnotationWin& mrSidebarWin; + SwView& mrDocView; + SwPostItMgr& mrPostItMgr; + + protected: + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + /// @see Window::LogicInvalidate(). + void LogicInvalidate(const tools::Rectangle* pRectangle) override; + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void LoseFocus() override; + virtual void RequestHelp(const HelpEvent &rEvt) override; + virtual OUString GetSurroundingText() const override; + virtual Selection GetSurroundingTextSelection() const override; + + public: + SidebarTextControl( sw::annotation::SwAnnotationWin& rSidebarWin, + WinBits nBits, + SwView& rDocView, + SwPostItMgr& rPostItMgr ); + virtual ~SidebarTextControl() override; + virtual void dispose() override; + + virtual void GetFocus() override; + virtual void KeyInput( const KeyEvent& rKeyEvt ) override; + virtual void MouseButtonDown(const MouseEvent& rMouseEvent) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + + OutlinerView* GetTextView() const; + + DECL_LINK( OnlineSpellCallback, SpellCallbackInfo&, void ); + + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override; + + virtual void Draw(OutputDevice* pDev, const Point&, DrawFlags) override; +}; + +} // end of namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarTxtControlAcc.cxx b/sw/source/uibase/docvw/SidebarTxtControlAcc.cxx new file mode 100644 index 000000000..d3bec6eca --- /dev/null +++ b/sw/source/uibase/docvw/SidebarTxtControlAcc.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <memory> + +#include "SidebarTxtControlAcc.hxx" + +#include "SidebarTxtControl.hxx" + +#include <svl/SfxBroadcaster.hxx> +#include <toolkit/awt/vclxaccessiblecomponent.hxx> +#include <editeng/unoedsrc.hxx> +#include <editeng/unoforou.hxx> +#include <editeng/unoviwou.hxx> +#include <editeng/unoedhlp.hxx> +#include <svx/AccessibleTextHelper.hxx> +#include <editeng/outliner.hxx> +#include <vcl/vclevent.hxx> + +namespace sw::sidebarwindows { + +namespace { + +// declaration and implementation of <SvxEditSource> +// for <::accessibility::AccessibleTextHelper> instance +class SidebarTextEditSource : public SvxEditSource, + public SfxBroadcaster +{ + public: + explicit SidebarTextEditSource(SidebarTextControl& rSidebarTextControl); + virtual ~SidebarTextEditSource() override; + + virtual std::unique_ptr<SvxEditSource> Clone() const override; + + virtual SvxTextForwarder* GetTextForwarder() override; + virtual SvxViewForwarder* GetViewForwarder() override; + virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override; + + virtual void UpdateData() override; + + virtual SfxBroadcaster& GetBroadcaster() const override; + DECL_LINK( NotifyHdl, EENotify&, void ); + + private: + SidebarTextControl& mrSidebarTextControl; + SvxOutlinerForwarder mTextForwarder; + SvxDrawOutlinerViewForwarder mViewForwarder; +}; + +} + +SidebarTextEditSource::SidebarTextEditSource( SidebarTextControl& rSidebarTextControl ) + : SvxEditSource() + , mrSidebarTextControl( rSidebarTextControl ) + , mTextForwarder( *(rSidebarTextControl.GetTextView()->GetOutliner()), false ) + , mViewForwarder( *(rSidebarTextControl.GetTextView()) ) +{ + if ( mrSidebarTextControl.GetTextView() ) + { + mrSidebarTextControl.GetTextView()->GetOutliner()->SetNotifyHdl( LINK(this, SidebarTextEditSource, NotifyHdl) ); + } +} + +SidebarTextEditSource::~SidebarTextEditSource() +{ + if ( mrSidebarTextControl.GetTextView() ) + { + mrSidebarTextControl.GetTextView()->GetOutliner()->SetNotifyHdl( Link<EENotify&,void>() ); + } +} + +std::unique_ptr<SvxEditSource> SidebarTextEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new SidebarTextEditSource( mrSidebarTextControl )); +} + +SvxTextForwarder* SidebarTextEditSource::GetTextForwarder() +{ + return &mTextForwarder; +} + +SvxViewForwarder* SidebarTextEditSource::GetViewForwarder() +{ + return &mViewForwarder; +} + +SvxEditViewForwarder* SidebarTextEditSource::GetEditViewForwarder( bool /*bCreate*/ ) +{ + return &mViewForwarder; +} + +void SidebarTextEditSource::UpdateData() +{ + // nothing to do +} + +SfxBroadcaster& SidebarTextEditSource::GetBroadcaster() const +{ + return * const_cast< SidebarTextEditSource* > (this); +} + +IMPL_LINK(SidebarTextEditSource, NotifyHdl, EENotify&, rNotify, void) +{ + std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify ) ); + + if (aHint) + { + Broadcast(*aHint); + } +} + +namespace { + +// declaration and implementation of accessible context for <SidebarTextControl> instance +class SidebarTextControlAccessibleContext : public VCLXAccessibleComponent +{ + public: + explicit SidebarTextControlAccessibleContext( SidebarTextControl& rSidebarTextControl ); + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleChild( sal_Int32 i ) override; + + 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; + + protected: + virtual void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) override; + + private: + std::unique_ptr<::accessibility::AccessibleTextHelper> mpAccessibleTextHelper; + + ::osl::Mutex maMutex; +}; + +} + +SidebarTextControlAccessibleContext::SidebarTextControlAccessibleContext( SidebarTextControl& rSidebarTextControl ) + : VCLXAccessibleComponent( rSidebarTextControl.GetWindowPeer() ) + , maMutex() +{ + mpAccessibleTextHelper.reset(new ::accessibility::AccessibleTextHelper( std::make_unique<SidebarTextEditSource>(rSidebarTextControl) )); + mpAccessibleTextHelper->SetEventSource( rSidebarTextControl.GetWindowPeer() ); +} + +sal_Int32 SAL_CALL SidebarTextControlAccessibleContext::getAccessibleChildCount() +{ + osl::MutexGuard aGuard( maMutex ); + + sal_Int32 nChildCount( 0 ); + + if ( mpAccessibleTextHelper ) + { + nChildCount = mpAccessibleTextHelper->GetChildCount(); + } + + return nChildCount; +} + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL SidebarTextControlAccessibleContext::getAccessibleChild( sal_Int32 i ) +{ + osl::MutexGuard aGuard( maMutex ); + + css::uno::Reference< css::accessibility::XAccessible > xChild; + + if ( mpAccessibleTextHelper ) + { + xChild = mpAccessibleTextHelper->GetChild( i ); + } + + return xChild; +} + +void SAL_CALL SidebarTextControlAccessibleContext::addAccessibleEventListener ( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener) +{ + osl::MutexGuard aGuard( maMutex ); + + if ( mpAccessibleTextHelper ) + { + mpAccessibleTextHelper->AddEventListener(xListener); + } +} + +void SAL_CALL SidebarTextControlAccessibleContext::removeAccessibleEventListener ( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener) +{ + osl::MutexGuard aGuard( maMutex ); + + if ( mpAccessibleTextHelper ) + { + mpAccessibleTextHelper->RemoveEventListener(xListener); + } +} + +void SidebarTextControlAccessibleContext::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + if ( mpAccessibleTextHelper ) + { + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ObjectDying: + { + mpAccessibleTextHelper.reset(); + } + break; + case VclEventId::WindowGetFocus: + case VclEventId::ControlGetFocus: + { + mpAccessibleTextHelper->SetFocus(); + } + break; + case VclEventId::WindowLoseFocus: + case VclEventId::ControlLoseFocus: + { + mpAccessibleTextHelper->SetFocus( false ); + } + break; + default: break; + } + } + + VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); +} + +// implementation of accessible for <SidebarTextControl> instance +SidebarTextControlAccessible::SidebarTextControlAccessible( SidebarTextControl& rSidebarTextControl ) + : VCLXWindow() + , mrSidebarTextControl( rSidebarTextControl ) +{ + SetWindow( &mrSidebarTextControl ); +} + +SidebarTextControlAccessible::~SidebarTextControlAccessible() +{ +} + +css::uno::Reference< css::accessibility::XAccessibleContext > SidebarTextControlAccessible::CreateAccessibleContext() +{ + SidebarTextControlAccessibleContext* pAccContext( + new SidebarTextControlAccessibleContext( mrSidebarTextControl ) ); + css::uno::Reference< css::accessibility::XAccessibleContext > xAcc( pAccContext ); + return xAcc; +} + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarTxtControlAcc.hxx b/sw/source/uibase/docvw/SidebarTxtControlAcc.hxx new file mode 100644 index 000000000..b05693484 --- /dev/null +++ b/sw/source/uibase/docvw/SidebarTxtControlAcc.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_UIBASE_DOCVW_SIDEBARTXTCONTROLACC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SIDEBARTXTCONTROLACC_HXX + +#include <toolkit/awt/vclxwindow.hxx> + +namespace sw::sidebarwindows { + +class SidebarTextControl; + +class SidebarTextControlAccessible : public VCLXWindow +{ + public: + explicit SidebarTextControlAccessible( SidebarTextControl& rSidebarTextControl ); + virtual ~SidebarTextControlAccessible() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleContext > + CreateAccessibleContext() override; + + private: + SidebarTextControl& mrSidebarTextControl; +}; + +} // end of namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarWinAcc.cxx b/sw/source/uibase/docvw/SidebarWinAcc.cxx new file mode 100644 index 000000000..010394dad --- /dev/null +++ b/sw/source/uibase/docvw/SidebarWinAcc.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SidebarWinAcc.hxx" +#include <AnnotationWin.hxx> + +#include <viewsh.hxx> +#include <accmap.hxx> +#include <toolkit/awt/vclxaccessiblecomponent.hxx> + +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +namespace sw::sidebarwindows { + +namespace { + +// declaration and implementation of accessible context for <SidebarWinAccessible> instance +class SidebarWinAccessibleContext : public VCLXAccessibleComponent +{ + public: + explicit SidebarWinAccessibleContext( sw::annotation::SwAnnotationWin& rSidebarWin, + SwViewShell& rViewShell, + const SwFrame* pAnchorFrame ) + : VCLXAccessibleComponent( rSidebarWin.GetWindowPeer() ) + , mrViewShell( rViewShell ) + , mpAnchorFrame( pAnchorFrame ) + , maMutex() + { + rSidebarWin.SetAccessibleRole( css::accessibility::AccessibleRole::COMMENT ); + } + + void ChangeAnchor( const SwFrame* pAnchorFrame ) + { + osl::MutexGuard aGuard(maMutex); + + mpAnchorFrame = pAnchorFrame; + } + + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleParent() override + { + osl::MutexGuard aGuard(maMutex); + + css::uno::Reference< css::accessibility::XAccessible > xAccParent; + + if ( mpAnchorFrame && + mrViewShell.GetAccessibleMap() ) + { + xAccParent = mrViewShell.GetAccessibleMap()->GetContext( mpAnchorFrame, false ); + } + + return xAccParent; + } + + virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override + { + osl::MutexGuard aGuard(maMutex); + + sal_Int32 nIndex( -1 ); + + if ( mpAnchorFrame && GetWindow() && + mrViewShell.GetAccessibleMap() ) + { + nIndex = mrViewShell.GetAccessibleMap()->GetChildIndex( *mpAnchorFrame, + *GetWindow() ); + } + + return nIndex; + } + + private: + SwViewShell& mrViewShell; + const SwFrame* mpAnchorFrame; + + ::osl::Mutex maMutex; +}; + +} + +// implementation of accessible for <SwAnnotationWin> instance +SidebarWinAccessible::SidebarWinAccessible( sw::annotation::SwAnnotationWin& rSidebarWin, + SwViewShell& rViewShell, + const SwSidebarItem& rSidebarItem ) + : VCLXWindow() + , mrSidebarWin( rSidebarWin ) + , mrViewShell( rViewShell ) + , mpAnchorFrame( rSidebarItem.maLayoutInfo.mpAnchorFrame ) + , bAccContextCreated( false ) +{ + SetWindow( &mrSidebarWin ); +} + +SidebarWinAccessible::~SidebarWinAccessible() +{ +} + +void SidebarWinAccessible::ChangeSidebarItem( const SwSidebarItem& rSidebarItem ) +{ + if ( bAccContextCreated ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > xAcc + = getAccessibleContext(); + if ( xAcc.is() ) + { + SidebarWinAccessibleContext* pAccContext = + dynamic_cast<SidebarWinAccessibleContext*>(xAcc.get()); + if ( pAccContext ) + { + pAccContext->ChangeAnchor( rSidebarItem.maLayoutInfo.mpAnchorFrame ); + } + } + } +} + +css::uno::Reference< css::accessibility::XAccessibleContext > SidebarWinAccessible::CreateAccessibleContext() +{ + SidebarWinAccessibleContext* pAccContext = + new SidebarWinAccessibleContext( mrSidebarWin, + mrViewShell, + mpAnchorFrame ); + css::uno::Reference< css::accessibility::XAccessibleContext > xAcc( pAccContext ); + bAccContextCreated = true; + return xAcc; +} + +} // end of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/SidebarWinAcc.hxx b/sw/source/uibase/docvw/SidebarWinAcc.hxx new file mode 100644 index 000000000..f22dfac3e --- /dev/null +++ b/sw/source/uibase/docvw/SidebarWinAcc.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_DOCVW_SIDEBARWINACC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_SIDEBARWINACC_HXX + +#include <toolkit/awt/vclxwindow.hxx> + +class SwViewShell; +class SwSidebarItem; +class SwFrame; +namespace sw::annotation { class SwAnnotationWin; } + +namespace sw::sidebarwindows { + +class SidebarWinAccessible : public VCLXWindow +{ + public: + explicit SidebarWinAccessible( sw::annotation::SwAnnotationWin& rSidebarWin, + SwViewShell& rViewShell, + const SwSidebarItem& rSidebarItem ); + virtual ~SidebarWinAccessible() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleContext > + CreateAccessibleContext() override; + + void ChangeSidebarItem( const SwSidebarItem& rSidebarItem ); + + private: + sw::annotation::SwAnnotationWin& mrSidebarWin; + SwViewShell& mrViewShell; + const SwFrame* mpAnchorFrame; + bool bAccContextCreated; +}; + +} // end of namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/UnfloatTableButton.cxx b/sw/source/uibase/docvw/UnfloatTableButton.cxx new file mode 100644 index 000000000..4f7bda656 --- /dev/null +++ b/sw/source/uibase/docvw/UnfloatTableButton.cxx @@ -0,0 +1,233 @@ +/* -*- 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 <UnfloatTableButton.hxx> +#include <HeaderFooterWin.hxx> + +#include <edtwin.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <strings.hrc> +#include <fmtpdsc.hxx> +#include <vcl/metric.hxx> +#include <vcl/settings.hxx> +#include <viewopt.hxx> +#include <frame.hxx> +#include <flyfrm.hxx> +#include <tabfrm.hxx> +#include <txtfrm.hxx> +#include <pagefrm.hxx> +#include <ndindex.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <unoprnms.hxx> +#include <unotbl.hxx> +#include <IDocumentState.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/attribute/fontattribute.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <svl/grabbagitem.hxx> +#include <doc.hxx> + +#define TEXT_PADDING 3 +#define BOX_DISTANCE 3 +#define BUTTON_WIDTH 12 + +UnfloatTableButton::UnfloatTableButton(SwEditWin* pEditWin, const SwFrame* pFrame) + : SwFrameMenuButtonBase(pEditWin, pFrame) + , m_sLabel(SwResId(STR_UNFLOAT_TABLE)) +{ +} + +UnfloatTableButton::~UnfloatTableButton() { disposeOnce(); } + +void UnfloatTableButton::SetOffset(Point aTopRightPixel) +{ + // Compute the text size and get the box position & size from it + tools::Rectangle aTextRect; + GetTextBoundRect(aTextRect, m_sLabel); + tools::Rectangle aTextPxRect = LogicToPixel(aTextRect); + FontMetric aFontMetric = GetFontMetric(GetFont()); + Size aBoxSize(aTextPxRect.GetWidth() + BUTTON_WIDTH + TEXT_PADDING * 2, + aFontMetric.GetLineHeight() + TEXT_PADDING * 2); + + Point aBoxPos(aTopRightPixel.X() - aBoxSize.Width() - BOX_DISTANCE, aTopRightPixel.Y()); + + if (AllSettings::GetLayoutRTL()) + { + aBoxPos.setX(aTopRightPixel.X() + BOX_DISTANCE); + } + + // Set the position & Size of the window + SetPosSizePixel(aBoxPos, aBoxSize); +} + +void UnfloatTableButton::MouseButtonDown(const MouseEvent& /*rMEvt*/) +{ + assert(GetFrame()->IsFlyFrame()); + // const_cast is needed because of bad design of ISwFrameControl and derived classes + SwFlyFrame* pFlyFrame = const_cast<SwFlyFrame*>(static_cast<const SwFlyFrame*>(GetFrame())); + + // Find the table inside the text frame + SwTabFrame* pTableFrame = nullptr; + SwFrame* pLower = pFlyFrame->GetLower(); + while (pLower) + { + if (pLower->IsTabFrame()) + { + pTableFrame = static_cast<SwTabFrame*>(pLower); + break; + } + pLower = pLower->GetNext(); + } + + if (pTableFrame == nullptr) + return; + + // Insert the table at the position of the text node which has the frame anchored to + SwFrame* pAnchoreFrame = pFlyFrame->AnchorFrame(); + if (pAnchoreFrame == nullptr || !pAnchoreFrame->IsTextFrame()) + return; + + SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pAnchoreFrame); + if (pTextFrame->GetTextNodeFirst() == nullptr) + return; + + SwNodeIndex aInsertPos((*pTextFrame->GetTextNodeFirst())); + + SwTableNode* pTableNode = pTableFrame->GetTable()->GetTableNode(); + if (pTableNode == nullptr) + return; + + SwDoc& rDoc = pTextFrame->GetDoc(); + + // tdf#129176: clear "TablePosition" grab bag, since we explicitly change the position here + // See DomainMapperTableHandler::endTableGetTableStyle, where the grab bag is filled, and + // DocxAttributeOutput::TableDefinition that uses it on export + SwFrameFormat* pTableFormat = pTableFrame->GetTable()->GetFrameFormat(); + assert(pTableFormat); + if (const SfxGrabBagItem* pGrabBagItem = pTableFormat->GetAttrSet().GetItem(RES_FRMATR_GRABBAG)) + { + SfxGrabBagItem aGrabBagItem(*pGrabBagItem); // Editable copy + if (aGrabBagItem.GetGrabBag().erase("TablePosition")) + { + css::uno::Any aVal; + aGrabBagItem.QueryValue(aVal); + const auto xTable = SwXTextTable::CreateXTextTable(pTableFormat); + const css::uno::Reference<css::beans::XPropertySet> xSet(xTable, css::uno::UNO_QUERY); + assert(xSet); + xSet->setPropertyValue(UNO_NAME_TABLE_INTEROP_GRAB_BAG, aVal); + } + } + + // When we move the table before the first text node, we need to clear RES_PAGEDESC attribute + // of the text node otherwise LO will create a page break after the table + if (pTextFrame->GetTextNodeFirst()) + { + const SwPageDesc* pPageDesc + = pTextFrame->GetPageDescItem().GetPageDesc(); // First text node of the page has this + if (pPageDesc) + { + // First set the existing page desc for the table node + SfxItemSet aSet(GetEditWin()->GetView().GetWrtShell().GetAttrPool(), + svl::Items<RES_PAGEDESC, RES_PAGEDESC>{}); + aSet.Put(SwFormatPageDesc(pPageDesc)); + SwPaM aPaMTable(*pTableNode); + rDoc.getIDocumentContentOperations().InsertItemSet( + aPaMTable, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame()); + + // Then remove pagedesc from the attributes of the text node + aSet.Put(SwFormatPageDesc(nullptr)); + SwPaM aPaMTextNode(*pTextFrame->GetTextNodeFirst()); + rDoc.getIDocumentContentOperations().InsertItemSet( + aPaMTextNode, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame()); + } + } + + // Move the table outside of the text frame + SwNodeRange aRange(*pTableNode, 0, *pTableNode->EndOfSectionNode(), 1); + rDoc.getIDocumentContentOperations().MoveNodeRange(aRange, aInsertPos, SwMoveFlags::DEFAULT); + + // Remove the floating table's frame + SwFlyFrameFormat* pFrameFormat = pFlyFrame->GetFormat(); + if (pFrameFormat) + { + rDoc.getIDocumentLayoutAccess().DelLayoutFormat(pFrameFormat); + } + + rDoc.getIDocumentState().SetModified(); + + // Undoing MoveNodeRange() is not working correctly in case of tables, it crashes sometimes + // So don't allow to undo after unfloating (similar to MakeFlyAndMove() method) + if (rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + rDoc.GetIDocumentUndoRedo().DelAllUndoObj(); + } +} + +void UnfloatTableButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + SetMapMode(MapMode(MapUnit::MapPixel)); + drawinglayer::primitive2d::Primitive2DContainer aSeq; + const ::tools::Rectangle aRect( + ::tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel()))); + + // Create button + SwFrameButtonPainter::PaintButton(aSeq, aRect, true); + + // Create the text primitive + basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor(); + basegfx::B2DVector aFontSize; + drawinglayer::attribute::FontAttribute aFontAttr + = drawinglayer::primitive2d::getFontAttributeFromVclFont( + aFontSize, rRenderContext.GetFont(), false, false); + + FontMetric aFontMetric = rRenderContext.GetFontMetric(rRenderContext.GetFont()); + double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING; + double nTextOffsetX = std::abs(aRect.GetWidth() - rRenderContext.GetTextWidth(m_sLabel)) / 2.0; + Point aTextPos(nTextOffsetX, nTextOffsetY); + + basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aFontSize.getX(), aFontSize.getY(), static_cast<double>(aTextPos.X()), + static_cast<double>(aTextPos.Y()))); + + aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), std::vector<double>(), aFontAttr, + css::lang::Locale(), aLineColor))); + + // Create the processor and process the primitives + const drawinglayer::geometry::ViewInformation2D aNewViewInfos; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, + aNewViewInfos)); + + pProcessor->process(aSeq); +} + +void UnfloatTableButton::ShowAll(bool bShow) { Show(bShow); } + +bool UnfloatTableButton::Contains(const Point& rDocPt) const +{ + ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel()); + if (aRect.IsInside(rDocPt)) + return true; + + return false; +} + +void UnfloatTableButton::SetReadonly(bool bReadonly) { ShowAll(!bReadonly); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/docvw/edtdd.cxx b/sw/source/uibase/docvw/edtdd.cxx new file mode 100644 index 000000000..44728b9ba --- /dev/null +++ b/sw/source/uibase/docvw/edtdd.cxx @@ -0,0 +1,495 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svdview.hxx> +#include <editeng/outliner.hxx> +#include <svx/svdobj.hxx> +#include <sot/exchange.hxx> +#include <sot/formats.hxx> +#include <sfx2/bindings.hxx> +#include <vcl/commandevent.hxx> + +#include <sfx2/viewfrm.hxx> +#include <fmturl.hxx> +#include <frmfmt.hxx> +#include <wrtsh.hxx> +#include <edtdd.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <swdtflvr.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <wdocsh.hxx> + +using namespace ::com::sun::star; + +// no include "dbgoutsw.hxx" here!!!!!! + +bool g_bExecuteDrag = false; + +void SwEditWin::StartDDTimer() +{ + m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, DDHandler)); + m_aTimer.SetTimeout(480); + m_aTimer.Start(); + g_bDDTimerStarted = true; +} + +void SwEditWin::StopDDTimer(SwWrtShell *pSh, const Point &rPt) +{ + m_aTimer.Stop(); + g_bDDTimerStarted = false; + if(!pSh->IsSelFrameMode()) + pSh->CallSetCursor(&rPt, false); + m_aTimer.SetInvokeHandler(LINK(this,SwEditWin, TimerHandler)); +} + +void SwEditWin::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel ) +{ + if (m_rView.GetObjectShell()->isContentExtractionLocked()) + return; + + SwWrtShell &rSh = m_rView.GetWrtShell(); + if( rSh.GetDrawView() ) + { + CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true ); + if( rSh.GetDrawView()->Command( aDragEvent, this ) ) + { + m_rView.GetViewFrame()->GetBindings().InvalidateAll(false); + return; // Event evaluated by SdrView + } + } + + if ( !m_pApplyTempl && !rSh.IsDrawCreate() && !IsDrawAction()) + { + bool bStart = false, bDelSelect = false; + SdrObject *pObj = nullptr; + Point aDocPos( PixelToLogic( rPosPixel ) ); + if ( !rSh.IsInSelect() && rSh.TestCurrPam( aDocPos, true)) + //We are not selecting and aren't at a selection + bStart = true; + else if ( !g_bFrameDrag && rSh.IsSelFrameMode() && + rSh.IsInsideSelectedObj( aDocPos ) && + nullptr == m_pAnchorMarker) + { + //We are not dragging internally and are not at an + //object (frame, draw object) + + // #i106131# *and* AnchorDrag is *not* active: When active, + // entering global drag mode will destroy the AnchorHdl but + // keep the now invalid ptr in place, next access will crash. + // It is indeed wrong to enter drag mode when AnchorDrag is + // already active + bStart = true; + } + else if( !g_bFrameDrag && m_rView.GetDocShell()->IsReadOnly() && + OBJCNT_NONE != rSh.GetObjCntType( aDocPos, pObj )) + { + rSh.LockPaint(); + if( rSh.SelectObj( aDocPos, 0, pObj )) + bStart = bDelSelect = true; + else + rSh.UnlockPaint(); + } + else + { + SwContentAtPos aSwContentAtPos( IsAttrAtPos::InetAttr ); + bStart = rSh.GetContentAtPos( aDocPos, + aSwContentAtPos ); + } + + if ( bStart && !m_bIsInDrag ) + { + m_bMBPressed = false; + ReleaseMouse(); + g_bFrameDrag = false; + g_bExecuteDrag = true; + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + m_aMovePos = aDocPos; + StartExecuteDrag(); + if( bDelSelect ) + { + rSh.UnSelectFrame(); + rSh.UnlockPaint(); + } + } + } +} + +void SwEditWin::StartExecuteDrag() +{ + if( !g_bExecuteDrag || m_bIsInDrag ) + return; + + m_bIsInDrag = true; + + rtl::Reference<SwTransferable> pTransfer = new SwTransferable( m_rView.GetWrtShell() ); + + pTransfer->StartDrag( this, m_aMovePos ); +} + +void SwEditWin::DragFinished() +{ + DropCleanup(); + m_aTimer.SetInvokeHandler( LINK(this,SwEditWin, TimerHandler) ); + m_bIsInDrag = false; +} + +void SwEditWin::DropCleanup() +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + + // reset statuses + g_bNoInterrupt = false; + if ( m_bOldIdleSet ) + { + rSh.GetViewOptions()->SetIdle( m_bOldIdle ); + m_bOldIdleSet = false; + } + if ( m_pUserMarker ) + CleanupDropUserMarker(); + else + rSh.UnSetVisibleCursor(); + +} + +void SwEditWin::CleanupDropUserMarker() +{ + if ( m_pUserMarker ) + { + m_pUserMarker.reset(); + m_pUserMarkerObj = nullptr; + } +} + +//exhibition hack (MA,MBA) +void SwView::SelectShellForDrop() +{ + if ( !GetCurShell() ) + SelectShell(); +} + +sal_Int8 SwEditWin::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + GetView().SelectShellForDrop(); + DropCleanup(); + sal_Int8 nRet = DND_ACTION_NONE; + + //A Drop to an open OutlinerView doesn't concern us (also see QueryDrop) + SwWrtShell &rSh = m_rView.GetWrtShell(); + const Point aDocPt( PixelToLogic( rEvt.maPosPixel )); + SdrObject *pObj = nullptr; + OutlinerView* pOLV; + rSh.GetObjCntType( aDocPt, pObj ); + + if( pObj && nullptr != ( pOLV = rSh.GetDrawView()->GetTextEditOutlinerView() )) + { + tools::Rectangle aRect( pOLV->GetOutputArea() ); + aRect.Union( pObj->GetLogicRect() ); + const Point aPos = pOLV->GetWindow()->PixelToLogic(rEvt.maPosPixel); + if ( aRect.IsInside(aPos) ) + { + rSh.StartAllAction(); + rSh.EndAllAction(); + return nRet; + } + } + + // There's a special treatment for file lists with a single + // element, that depends on the actual content of the + // Transferable to be accessible. Since the transferable + // may only be accessed after the drop has been accepted + // (according to KA due to Java D&D), we'll have to + // reevaluate the drop action once more _with_ the + // Transferable. + sal_uInt8 nEventAction; + sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT + : rEvt.mnAction; + SotExchangeActionFlags nActionFlags; + m_nDropAction = SotExchange::GetExchangeAction( + GetDataFlavorExVector(), + m_nDropDestination, + rEvt.mnAction, + nUserOpt, m_nDropFormat, nEventAction, SotClipboardFormatId::NONE, + &rEvt.maDropEvent.Transferable, + &nActionFlags ); + + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + nRet = rEvt.mnAction; + if( !SwTransferable::PasteData( aData, rSh, m_nDropAction, nActionFlags, m_nDropFormat, + m_nDropDestination, false, rEvt.mbDefault, &aDocPt, nRet)) + nRet = DND_ACTION_NONE; + else if ( SW_MOD()->m_pDragDrop ) + //Don't clean up anymore at internal D&D! + SW_MOD()->m_pDragDrop->SetCleanUp( false ); + + return nRet; +} + +SotExchangeDest SwEditWin::GetDropDestination( const Point& rPixPnt, SdrObject ** ppObj ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + const Point aDocPt( PixelToLogic( rPixPnt ) ); + if( rSh.TestCurrPam( aDocPt ) + || rSh.IsOverReadOnlyPos( aDocPt ) + || rSh.DocPtInsideInputField( aDocPt ) ) + return SotExchangeDest::NONE; + + SdrObject *pObj = nullptr; + const ObjCntType eType = rSh.GetObjCntType( aDocPt, pObj ); + + //Drop to OutlinerView (TextEdit in Drawing) should decide it on its own! + if( pObj ) + { + OutlinerView* pOLV = rSh.GetDrawView()->GetTextEditOutlinerView(); + if ( pOLV ) + { + tools::Rectangle aRect( pOLV->GetOutputArea() ); + aRect.Union( pObj->GetLogicRect() ); + const Point aPos = pOLV->GetWindow()->PixelToLogic( rPixPnt ); + if( aRect.IsInside( aPos ) ) + return SotExchangeDest::NONE; + } + } + + //What do we want to drop on now? + SotExchangeDest nDropDestination = SotExchangeDest::NONE; + + //Did anything else arrive from the DrawingEngine? + if( OBJCNT_NONE != eType ) + { + switch ( eType ) + { + case OBJCNT_GRF: + { + bool bLink, + bIMap = nullptr != rSh.GetFormatFromObj( aDocPt )->GetURL().GetMap(); + OUString aDummy; + rSh.GetGrfAtPos( aDocPt, aDummy, bLink ); + if ( bLink && bIMap ) + nDropDestination = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP; + else if ( bLink ) + nDropDestination = SotExchangeDest::DOC_LNKD_GRAPHOBJ; + else if ( bIMap ) + nDropDestination = SotExchangeDest::DOC_GRAPH_W_IMAP; + else + nDropDestination = SotExchangeDest::DOC_GRAPHOBJ; + } + break; + case OBJCNT_FLY: + if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) + nDropDestination = SotExchangeDest::DOC_TEXTFRAME_WEB; + else + nDropDestination = SotExchangeDest::DOC_TEXTFRAME; + break; + case OBJCNT_OLE: nDropDestination = SotExchangeDest::DOC_OLEOBJ; break; + case OBJCNT_CONTROL: /* no Action avail */ + case OBJCNT_SIMPLE: nDropDestination = SotExchangeDest::DOC_DRAWOBJ; break; + case OBJCNT_URLBUTTON: nDropDestination = SotExchangeDest::DOC_URLBUTTON; break; + case OBJCNT_GROUPOBJ: nDropDestination = SotExchangeDest::DOC_GROUPOBJ; break; + + default: OSL_ENSURE( false, "new ObjectType?" ); + } + } + if ( !bool(nDropDestination) ) + { + if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) + nDropDestination = SotExchangeDest::SWDOC_FREE_AREA_WEB; + else + nDropDestination = SotExchangeDest::SWDOC_FREE_AREA; + } + if( ppObj ) + *ppObj = pObj; + return nDropDestination; +} + +sal_Int8 SwEditWin::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + if( rEvt.mbLeaving ) + { + DropCleanup(); + return rEvt.mnAction; + } + + if( m_rView.GetDocShell()->IsReadOnly() ) + return DND_ACTION_NONE; + + SwWrtShell &rSh = m_rView.GetWrtShell(); + + Point aPixPt( rEvt.maPosPixel ); + + // If the cursor is near the inner boundary + // we attempt to scroll towards the desired direction. + tools::Rectangle aWin(Point(), GetOutputSizePixel()); + const int nMargin = 10; + aWin.AdjustLeft(nMargin ); + aWin.AdjustTop(nMargin ); + aWin.AdjustRight( -nMargin ); + aWin.AdjustBottom( -nMargin ); + if(!aWin.IsInside(aPixPt)) { + static sal_uInt64 last_tick = 0; + sal_uInt64 current_tick = tools::Time::GetSystemTicks(); + if((current_tick-last_tick) > 500) { + last_tick = current_tick; + if(!m_bOldIdleSet) { + m_bOldIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle(false); + m_bOldIdleSet = true; + } + CleanupDropUserMarker(); + if(aPixPt.X() > aWin.Right()) aPixPt.AdjustX(nMargin ); + if(aPixPt.X() < aWin.Left()) aPixPt.AdjustX( -nMargin ); + if(aPixPt.Y() > aWin.Bottom()) aPixPt.AdjustY(nMargin ); + if(aPixPt.Y() < aWin.Top()) aPixPt.AdjustY( -nMargin ); + Point aDocPt(PixelToLogic(aPixPt)); + SwRect rect(aDocPt,Size(1,1)); + rSh.MakeVisible(rect); + } + } + + if(m_bOldIdleSet) { + rSh.GetViewOptions()->SetIdle( m_bOldIdle ); + m_bOldIdleSet = false; + } + + SdrObject *pObj = nullptr; + m_nDropDestination = GetDropDestination( aPixPt, &pObj ); + if( !bool(m_nDropDestination) ) + return DND_ACTION_NONE; + + sal_uInt8 nEventAction; + sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT + : rEvt.mnAction; + + m_nDropAction = SotExchange::GetExchangeAction( + GetDataFlavorExVector(), + m_nDropDestination, + rEvt.mnAction, + nUserOpt, m_nDropFormat, nEventAction ); + + if( EXCHG_INOUT_ACTION_NONE != m_nDropAction ) + { + const Point aDocPt( PixelToLogic( aPixPt ) ); + + //With the default action we still want to have a say. + SwModule *pMod = SW_MOD(); + if( pMod->m_pDragDrop ) + { + bool bCleanup = false; + //Drawing objects in Headers/Footers are not allowed + + SwWrtShell *pSrcSh = pMod->m_pDragDrop->GetShell(); + if( (pSrcSh->GetSelFrameType() == FrameTypeFlags::DRAWOBJ) && + pSrcSh->IsSelContainsControl() && + (rSh.GetFrameType( &aDocPt, false ) & (FrameTypeFlags::HEADER|FrameTypeFlags::FOOTER)) ) + { + bCleanup = true; + } + // don't more position protected objects! + else if( DND_ACTION_MOVE == rEvt.mnAction && + pSrcSh->IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE ) + { + bCleanup = true; + } + else if( rEvt.mbDefault ) + { + // internal Drag&Drop: within same Doc a Move + // otherwise a Copy - Task 54974 + nEventAction = pSrcSh->GetDoc() == rSh.GetDoc() + ? DND_ACTION_MOVE + : DND_ACTION_COPY; + } + if ( bCleanup ) + { + CleanupDropUserMarker(); + rSh.UnSetVisibleCursor(); + return DND_ACTION_NONE; + } + } + else + { + //D&D from outside of SW should be a Copy per default. + if( EXCHG_IN_ACTION_DEFAULT == nEventAction && + DND_ACTION_MOVE == rEvt.mnAction ) + nEventAction = DND_ACTION_COPY; + + if( (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == m_nDropFormat && + EXCHG_IN_ACTION_LINK == m_nDropAction) || + SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == m_nDropFormat ) + { + SdrMarkView* pMView = rSh.GetDrawView(); + if( pMView && !pMView->IsDesignMode() ) + return DND_ACTION_NONE; + } + } + + if ( EXCHG_IN_ACTION_DEFAULT != nEventAction ) + nUserOpt = static_cast<sal_Int8>(nEventAction); + + // show DropCursor or UserMarker ? + if( SotExchangeDest::SWDOC_FREE_AREA_WEB == m_nDropDestination || + SotExchangeDest::SWDOC_FREE_AREA == m_nDropDestination ) + { + CleanupDropUserMarker(); + SwContentAtPos aCont( IsAttrAtPos::ContentCheck ); + if(rSh.GetContentAtPos(aDocPt, aCont)) + rSh.SwCursorShell::SetVisibleCursor( aDocPt ); + } + else + { + rSh.UnSetVisibleCursor(); + + if ( m_pUserMarkerObj != pObj ) + { + CleanupDropUserMarker(); + m_pUserMarkerObj = pObj; + + if(m_pUserMarkerObj) + { + m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), *m_pUserMarkerObj )); + } + } + } + return nUserOpt; + } + + CleanupDropUserMarker(); + rSh.UnSetVisibleCursor(); + return DND_ACTION_NONE; +} + +IMPL_LINK_NOARG(SwEditWin, DDHandler, Timer *, void) +{ + g_bDDTimerStarted = false; + m_aTimer.Stop(); + m_aTimer.SetTimeout(240); + m_bMBPressed = false; + ReleaseMouse(); + g_bFrameDrag = false; + + if ( m_rView.GetViewFrame() ) + { + g_bExecuteDrag = true; + StartExecuteDrag(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx new file mode 100644 index 000000000..507919f60 --- /dev/null +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -0,0 +1,6429 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <comphelper/string.hxx> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/i18n/InputSequenceCheckMode.hpp> +#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp> + +#include <com/sun/star/i18n/UnicodeScript.hpp> +#include <com/sun/star/i18n/CalendarFieldIndex.hpp> +#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp> + +#include <vcl/inputctx.hxx> +#include <vcl/help.hxx> +#include <vcl/weld.hxx> +#include <vcl/ptrstyle.hxx> +#include <svl/macitem.hxx> +#include <unotools/securityoptions.hxx> +#include <basic/sbxvar.hxx> +#include <svl/ctloptions.hxx> +#include <basic/sbx.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/ptitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/langitem.hxx> +#include <svx/svdview.hxx> +#include <svx/svdhdl.hxx> +#include <svx/svdoutl.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/flditem.hxx> +#include <editeng/colritem.hxx> +#include <unotools/charclass.hxx> +#include <unotools/datetime.hxx> + +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#include <editeng/acorrcfg.hxx> +#include <SwSmartTagMgr.hxx> +#include <edtdd.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <textboxhelper.hxx> +#include <dcontact.hxx> +#include <fldbas.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <viewopt.hxx> +#include <drawbase.hxx> +#include <dselect.hxx> +#include <textsh.hxx> +#include <shdwcrsr.hxx> +#include <txatbase.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmthdft.hxx> +#include <frmfmt.hxx> +#include <modcfg.hxx> +#include <fmtcol.hxx> +#include <wview.hxx> +#include <gloslst.hxx> +#include <inputwin.hxx> +#include <gloshdl.hxx> +#include <swundo.hxx> +#include <drwtxtsh.hxx> +#include <fchrfmt.hxx> +#include "romenu.hxx" +#include <initui.hxx> +#include <frmatr.hxx> +#include <extinput.hxx> +#include <acmplwrd.hxx> +#include <swcalwrp.hxx> +#include <swdtflvr.hxx> +#include <breakit.hxx> +#include <checkit.hxx> +#include <pagefrm.hxx> + +#include <helpids.h> +#include <cmdid.h> +#include <uitool.hxx> +#include <fmtfollowtextflow.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <charfmt.hxx> +#include <numrule.hxx> +#include <pagedesc.hxx> +#include <svtools/ruler.hxx> +#include <formatclipboard.hxx> +#include <vcl/svapp.hxx> +#include <wordcountdialog.hxx> +#include <fmtfld.hxx> + +#include <IMark.hxx> +#include <doc.hxx> +#include <xmloff/odffields.hxx> + +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <AnnotationWin.hxx> + +#include <algorithm> +#include <vector> + +#include <rootfrm.hxx> + +#include <unotools/syslocaleoptions.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <salhelper/singletonref.hxx> +#include <sfx2/event.hxx> +#include <memory> + +using namespace sw::mark; +using namespace ::com::sun::star; + +/** + * Globals + */ +static bool g_bInputLanguageSwitched = false; + +// Usually in MouseButtonUp a selection is revoked when the selection is +// not currently being pulled open. Unfortunately in MouseButtonDown there +// is being selected at double/triple click. That selection is completely +// finished in the Handler and thus can't be distinguished in the Up. +// To resolve this g_bHoldSelection is set in Down and evaluated in Up. +static bool g_bHoldSelection = false; + +bool g_bFrameDrag = false; +static bool g_bValidCursorPos = false; +static bool g_bModePushed = false; +bool g_bDDTimerStarted = false; +bool g_bFlushCharBuffer = false; +bool g_bDDINetAttr = false; +static SdrHdlKind g_eSdrMoveHdl = SdrHdlKind::User; + +QuickHelpData* SwEditWin::m_pQuickHlpData = nullptr; + +long SwEditWin::m_nDDStartPosY = 0; +long SwEditWin::m_nDDStartPosX = 0; + +static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView ); + +/// Check if the selected shape has a TextBox: if so, go into that instead. +static bool lcl_goIntoTextBox(SwEditWin& rEditWin, SwWrtShell& rSh) +{ + SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0); + if (!pMark) + return false; + + SdrObject* pSdrObject = pMark->GetMarkedSdrObj(); + SwFrameFormat* pObjectFormat = ::FindFrameFormat(pSdrObject); + if (SwFrameFormat* pTextBoxFormat = SwTextBoxHelper::getOtherTextBoxFormat(pObjectFormat, RES_DRAWFRMFMT)) + { + SdrObject* pTextBox = pTextBoxFormat->FindRealSdrObject(); + SdrView* pSdrView = rSh.GetDrawView(); + // Unmark the shape. + pSdrView->UnmarkAllObj(); + // Mark the textbox. + rSh.SelectObj(Point(), SW_ALLOW_TEXTBOX, pTextBox); + // Clear the DrawFuncPtr. + rEditWin.StopInsFrame(); + return true; + } + return false; +} + +class SwAnchorMarker +{ + SdrHdl* pHdl; + Point aHdlPos; + Point aLastPos; + bool bTopRightHandle; +public: + explicit SwAnchorMarker( SdrHdl* pH ) + : pHdl( pH ) + , aHdlPos( pH->GetPos() ) + , aLastPos( pH->GetPos() ) + , bTopRightHandle( pH->GetKind() == SdrHdlKind::Anchor_TR ) + {} + const Point& GetLastPos() const { return aLastPos; } + void SetLastPos( const Point& rNew ) { aLastPos = rNew; } + void SetPos( const Point& rNew ) { pHdl->SetPos( rNew ); } + const Point& GetHdlPos() const { return aHdlPos; } + SdrHdl* GetHdl() const { return pHdl; } + void ChgHdl( SdrHdl* pNew ) + { + pHdl = pNew; + if ( pHdl ) + { + bTopRightHandle = (pHdl->GetKind() == SdrHdlKind::Anchor_TR); + } + } + Point GetPosForHitTest( const OutputDevice& rOut ) + { + Point aHitTestPos( pHdl->GetPos() ); + aHitTestPos = rOut.LogicToPixel( aHitTestPos ); + if ( bTopRightHandle ) + { + aHitTestPos += Point( -1, 1 ); + } + else + { + aHitTestPos += Point( 1, 1 ); + } + aHitTestPos = rOut.PixelToLogic( aHitTestPos ); + + return aHitTestPos; + } +}; + +/// Assists with auto-completion of AutoComplete words and AutoText names. +struct QuickHelpData +{ + /// Strings that at least partially match an input word, and match length. + std::vector<std::pair<OUString, sal_uInt16>> m_aHelpStrings; + /// Index of the current help string. + sal_uInt16 nCurArrPos; + static constexpr sal_uInt16 nNoPos = std::numeric_limits<sal_uInt16>::max(); + + /// Help data stores AutoText names rather than AutoComplete words. + bool m_bIsAutoText; + /// Display help string as a tip rather than inline. + bool m_bIsTip; + /// Tip ID when a help string is displayed as a tip. + void* nTipId; + /// Append a space character to the displayed help string (if appropriate). + bool m_bAppendSpace; + + /// Help string is currently displayed. + bool m_bIsDisplayed; + + QuickHelpData() { ClearContent(); } + + void Move( QuickHelpData& rCpy ); + void ClearContent(); + void Start(SwWrtShell& rSh, bool bRestart); + void Stop( SwWrtShell& rSh ); + + bool HasContent() const { return !m_aHelpStrings.empty() && nCurArrPos != nNoPos; } + const OUString& CurStr() const { return m_aHelpStrings[nCurArrPos].first; } + sal_uInt16 CurLen() const { return m_aHelpStrings[nCurArrPos].second; } + + /// Next help string. + void Next( bool bEndLess ) + { + if( ++nCurArrPos >= m_aHelpStrings.size() ) + nCurArrPos = (bEndLess && !m_bIsAutoText ) ? 0 : nCurArrPos-1; + } + /// Previous help string. + void Previous( bool bEndLess ) + { + if( 0 == nCurArrPos-- ) + nCurArrPos = (bEndLess && !m_bIsAutoText ) ? m_aHelpStrings.size()-1 : 0; + } + + // Fills internal structures with hopefully helpful information. + void FillStrArr( SwWrtShell const & rSh, const OUString& rWord ); + void SortAndFilter(const OUString &rOrigWord); +}; + +/** + * Avoid minimal movement shiver + */ +#define HIT_PIX 2 /* hit tolerance in pixel */ +#define MIN_MOVE 4 + +static bool IsMinMove(const Point &rStartPos, const Point &rLPt) +{ + return std::abs(rStartPos.X() - rLPt.X()) > MIN_MOVE || + std::abs(rStartPos.Y() - rLPt.Y()) > MIN_MOVE; +} + +/** + * For MouseButtonDown - determine whether a DrawObject + * a NO SwgFrame was hit! Shift/Ctrl should only result + * in selecting, with DrawObjects; at SwgFlys to trigger + * hyperlinks if applicable (Download/NewWindow!) + */ +static bool IsDrawObjSelectable( const SwWrtShell& rSh, const Point& rPt ) +{ + bool bRet = true; + SdrObject* pObj; + switch( rSh.GetObjCntType( rPt, pObj )) + { + case OBJCNT_NONE: + case OBJCNT_FLY: + case OBJCNT_GRF: + case OBJCNT_OLE: + bRet = false; + break; + default:; //prevent warning + } + return bRet; +} + +/* + * Switch pointer + */ +void SwEditWin::UpdatePointer(const Point &rLPt, sal_uInt16 nModifier ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + if( m_pApplyTempl ) + { + PointerStyle eStyle = PointerStyle::Fill; + if ( rSh.IsOverReadOnlyPos( rLPt ) ) + { + m_pUserMarker.reset(); + + eStyle = PointerStyle::NotAllowed; + } + else + { + SwRect aRect; + SwRect* pRect = &aRect; + const SwFrameFormat* pFormat = nullptr; + + bool bFrameIsValidTarget = false; + if( m_pApplyTempl->m_pFormatClipboard ) + bFrameIsValidTarget = m_pApplyTempl->m_pFormatClipboard->HasContentForThisType( SelectionType::Frame ); + else if( !m_pApplyTempl->nColor ) + bFrameIsValidTarget = ( m_pApplyTempl->eType == SfxStyleFamily::Frame ); + + if( bFrameIsValidTarget && + nullptr !=(pFormat = rSh.GetFormatFromObj( rLPt, &pRect )) && + dynamic_cast<const SwFlyFrameFormat*>( pFormat) ) + { + //turn on highlight for frame + tools::Rectangle aTmp( pRect->SVRect() ); + + if ( !m_pUserMarker ) + { + m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp )); + } + } + else + { + m_pUserMarker.reset(); + } + + rSh.SwCursorShell::SetVisibleCursor( rLPt ); + } + SetPointer( eStyle ); + return; + } + + if( !rSh.VisArea().Width() ) + return; + + SET_CURR_SHELL(&rSh); + + if ( IsChainMode() ) + { + SwRect aRect; + SwChainRet nChainable = rSh.Chainable( aRect, *rSh.GetFlyFrameFormat(), rLPt ); + PointerStyle eStyle = nChainable != SwChainRet::OK + ? PointerStyle::ChainNotAllowed : PointerStyle::Chain; + if ( nChainable == SwChainRet::OK ) + { + tools::Rectangle aTmp( aRect.SVRect() ); + + if ( !m_pUserMarker ) + { + m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp )); + } + } + else + { + m_pUserMarker.reset(); + } + + SetPointer( eStyle ); + return; + } + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + SvtSecurityOptions aSecOpts; + const bool bSecureOption = aSecOpts.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ( ( bSecureOption && nModifier == KEY_MOD1 ) || + ( !bSecureOption && nModifier != KEY_MOD1 ) ) + bExecHyperlinks = true; + } + + const bool bExecSmarttags = nModifier == KEY_MOD1; + + SdrView *pSdrView = rSh.GetDrawView(); + bool bPrefSdrPointer = false; + bool bHitHandle = false; + bool bCntAtPos = false; + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + m_aActHitType = SdrHitKind::NONE; + PointerStyle eStyle = PointerStyle::Text; + if ( !pSdrView ) + bCntAtPos = true; + else if ( (bHitHandle = (pSdrView->PickHandle(rLPt) != nullptr)) ) + { + m_aActHitType = SdrHitKind::Object; + bPrefSdrPointer = true; + } + else + { + const bool bNotInSelObj = !rSh.IsInsideSelectedObj( rLPt ); + if ( m_rView.GetDrawFuncPtr() && !m_bInsDraw && bNotInSelObj ) + { + m_aActHitType = SdrHitKind::Object; + if (IsObjectSelect()) + eStyle = PointerStyle::Arrow; + else + bPrefSdrPointer = true; + } + else + { + SdrPageView* pPV = nullptr; + pSdrView->SetHitTolerancePixel( HIT_PIX ); + SdrObject* pObj = (bNotInSelObj && bExecHyperlinks) ? + pSdrView->PickObj(rLPt, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : + nullptr; + if (pObj) + { + SdrObjMacroHitRec aTmp; + aTmp.aPos = rLPt; + aTmp.pPageView = pPV; + SetPointer( pObj->GetMacroPointer( aTmp ) ); + return; + } + else + { + // dvo: IsObjSelectable() eventually calls SdrView::PickObj, so + // apparently this is used to determine whether this is a + // drawling layer object or not. + if ( rSh.IsObjSelectable( rLPt ) ) + { + if (pSdrView->IsTextEdit()) + { + m_aActHitType = SdrHitKind::NONE; + bPrefSdrPointer = true; + } + else + { + SdrViewEvent aVEvt; + SdrHitKind eHit = pSdrView->PickAnything(rLPt, aVEvt); + + if (eHit == SdrHitKind::UrlField && bExecHyperlinks) + { + m_aActHitType = SdrHitKind::Object; + bPrefSdrPointer = true; + } + else + { + // if we're over a selected object, we show an + // ARROW by default. We only show a MOVE if 1) the + // object is selected, and 2) it may be moved + // (i.e., position is not protected). + bool bMovable = + (!bNotInSelObj) && + (rSh.IsObjSelected() || rSh.IsFrameSelected()) && + (rSh.IsSelObjProtected(FlyProtectFlags::Pos) == FlyProtectFlags::NONE); + + SdrObject* pSelectableObj = rSh.GetObjAt(rLPt); + // Don't update pointer if this is a background image only. + if (pSelectableObj->GetLayer() != rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId()) + eStyle = bMovable ? PointerStyle::Move : PointerStyle::Arrow; + m_aActHitType = SdrHitKind::Object; + } + } + } + else + { + if ( rSh.IsFrameSelected() && !bNotInSelObj ) + { + // dvo: this branch appears to be dead and should be + // removed in a future version. Reason: The condition + // !bNotInSelObj means that this branch will only be + // executed in the cursor points inside a selected + // object. However, if this is the case, the previous + // if( rSh.IsObjSelectable(rLPt) ) must always be true: + // rLPt is inside a selected object, then obviously + // rLPt is over a selectable object. + if (rSh.IsSelObjProtected(FlyProtectFlags::Size) != FlyProtectFlags::NONE) + eStyle = PointerStyle::NotAllowed; + else + eStyle = PointerStyle::Move; + m_aActHitType = SdrHitKind::Object; + } + else + { + if ( m_rView.GetDrawFuncPtr() ) + bPrefSdrPointer = true; + else + bCntAtPos = true; + } + } + } + } + } + if ( bPrefSdrPointer ) + { + if (bIsDocReadOnly || (rSh.IsObjSelected() && rSh.IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE)) + SetPointer( PointerStyle::NotAllowed ); + else + { + if (m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->IsInsertForm() && !bHitHandle) + SetPointer( PointerStyle::DrawRect ); + else + SetPointer( pSdrView->GetPreferredPointer( rLPt, rSh.GetOut() ) ); + } + } + else + { + if( !rSh.IsPageAtPos( rLPt ) || m_pAnchorMarker ) + eStyle = PointerStyle::Arrow; + else + { + // Even if we already have something, prefer URLs if possible. + SwContentAtPos aUrlPos(IsAttrAtPos::InetAttr); + if (bCntAtPos || rSh.GetContentAtPos(rLPt, aUrlPos)) + { + SwContentAtPos aSwContentAtPos( + IsAttrAtPos::Field | + IsAttrAtPos::ClickField | + IsAttrAtPos::InetAttr | + IsAttrAtPos::Ftn | + IsAttrAtPos::SmartTag ); + if( rSh.GetContentAtPos( rLPt, aSwContentAtPos) ) + { + // Is edit inline input field + if (IsAttrAtPos::Field == aSwContentAtPos.eContentAtPos + && aSwContentAtPos.pFndTextAttr != nullptr + && aSwContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD) + { + const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr; + if (!(pCursorField && pCursorField == aSwContentAtPos.pFndTextAttr->GetFormatField().GetField())) + eStyle = PointerStyle::RefHand; + } + else + { + const bool bClickToFollow = IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos || + IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos; + if( !bClickToFollow || + (IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos && bExecHyperlinks) || + (IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos && bExecSmarttags) ) + eStyle = PointerStyle::RefHand; + } + } + } + } + + // which kind of text pointer have we to show - horz / vert - ? + if( PointerStyle::Text == eStyle && rSh.IsInVerticalText( &rLPt )) + eStyle = PointerStyle::TextVertical; + else if (rSh.GetViewOptions()->CanHideWhitespace() && + rSh.GetLayout()->IsBetweenPages(rLPt)) + { + if (rSh.GetViewOptions()->IsHideWhitespaceMode()) + eStyle = PointerStyle::ShowWhitespace; + else + eStyle = PointerStyle::HideWhitespace; + } + + SetPointer( eStyle ); + } +} + +/** + * Increase timer for selection + */ +IMPL_LINK_NOARG(SwEditWin, TimerHandler, Timer *, void) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + Point aModPt( m_aMovePos ); + const SwRect aOldVis( rSh.VisArea() ); + bool bDone = false; + + if ( !rSh.VisArea().IsInside( aModPt ) ) + { + if ( m_bInsDraw ) + { + const int nMaxScroll = 40; + m_rView.Scroll( tools::Rectangle(aModPt,Size(1,1)), nMaxScroll, nMaxScroll); + bDone = true; + } + else if ( g_bFrameDrag ) + { + rSh.Drag(&aModPt, false); + bDone = true; + } + if ( !bDone ) + aModPt = rSh.GetContentPos( aModPt,aModPt.Y() > rSh.VisArea().Bottom() ); + } + if ( !bDone && !(g_bFrameDrag || m_bInsDraw) ) + { + if ( m_xRowColumnSelectionStart ) + { + Point aPos( aModPt ); + rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag ); + } + else + rSh.CallSetCursor( &aModPt, false ); + + // It can be that a "jump" over a table cannot be accomplished like + // that. So we jump over the table by Up/Down here. + const SwRect& rVisArea = rSh.VisArea(); + if( aOldVis == rVisArea && !rSh.IsStartOfDoc() && !rSh.IsEndOfDoc() ) + { + // take the center point of VisArea to + // decide in which direction the user want. + if( aModPt.Y() < ( rVisArea.Top() + rVisArea.Height() / 2 ) ) + rSh.Up( true ); + else + rSh.Down( true ); + } + } + + m_aMovePos += rSh.VisArea().Pos() - aOldVis.Pos(); + JustifyAreaTimer(); +} + +void SwEditWin::JustifyAreaTimer() +{ + const tools::Rectangle &rVisArea = GetView().GetVisArea(); +#ifdef UNX + const long coMinLen = 100; +#else + const long coMinLen = 50; +#endif + long const nTimeout = 800, + nDiff = std::max( + std::max( m_aMovePos.Y() - rVisArea.Bottom(), rVisArea.Top() - m_aMovePos.Y() ), + std::max( m_aMovePos.X() - rVisArea.Right(), rVisArea.Left() - m_aMovePos.X())); + m_aTimer.SetTimeout( std::max( coMinLen, nTimeout - nDiff*2L) ); +} + +void SwEditWin::LeaveArea(const Point &rPos) +{ + m_aMovePos = rPos; + JustifyAreaTimer(); + if( !m_aTimer.IsActive() ) + m_aTimer.Start(); + m_pShadCursor.reset(); +} + +inline void SwEditWin::EnterArea() +{ + m_aTimer.Stop(); +} + +/** + * Insert mode for frames + */ +void SwEditWin::InsFrame(sal_uInt16 nCols) +{ + StdDrawMode( OBJ_NONE, false ); + m_bInsFrame = true; + m_nInsFrameColCount = nCols; +} + +void SwEditWin::StdDrawMode( SdrObjKind eSdrObjectKind, bool bObjSelect ) +{ + SetSdrDrawMode( eSdrObjectKind ); + + if (bObjSelect) + m_rView.SetDrawFuncPtr(std::make_unique<DrawSelection>( &m_rView.GetWrtShell(), this, &m_rView )); + else + m_rView.SetDrawFuncPtr(std::make_unique<SwDrawBase>( &m_rView.GetWrtShell(), this, &m_rView )); + + m_rView.SetSelDrawSlot(); + SetSdrDrawMode( eSdrObjectKind ); + if (bObjSelect) + m_rView.GetDrawFuncPtr()->Activate( SID_OBJECT_SELECT ); + else + m_rView.GetDrawFuncPtr()->Activate( sal::static_int_cast< sal_uInt16 >(eSdrObjectKind) ); + m_bInsFrame = false; + m_nInsFrameColCount = 1; +} + +void SwEditWin::StopInsFrame() +{ + if (m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + } + m_rView.LeaveDrawCreate(); // leave construction mode + m_bInsFrame = false; + m_nInsFrameColCount = 1; +} + +bool SwEditWin::IsInputSequenceCheckingRequired( const OUString &rText, const SwPaM& rCursor ) +{ + const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions(); + if ( !rCTLOptions.IsCTLFontEnabled() || + !rCTLOptions.IsCTLSequenceChecking() ) + return false; + + if ( 0 == rCursor.Start()->nContent.GetIndex() ) /* first char needs not to be checked */ + return false; + + SwBreakIt *pBreakIter = SwBreakIt::Get(); + uno::Reference < i18n::XBreakIterator > xBI = pBreakIter->GetBreakIter(); + assert(xBI.is()); + long nCTLScriptPos = -1; + + if (xBI->getScriptType( rText, 0 ) == i18n::ScriptType::COMPLEX) + nCTLScriptPos = 0; + else + nCTLScriptPos = xBI->nextScript( rText, 0, i18n::ScriptType::COMPLEX ); + + return (0 <= nCTLScriptPos && nCTLScriptPos <= rText.getLength()); +} + +//return INVALID_HINT if language should not be explicitly overridden, the correct +//HintId to use for the eBufferLanguage otherwise +static sal_uInt16 lcl_isNonDefaultLanguage(LanguageType eBufferLanguage, SwView const & rView, + const OUString &rInBuffer) +{ + sal_uInt16 nWhich = INVALID_HINT; + + //If the option to IgnoreLanguageChange is set, short-circuit this method + //which results in the document/paragraph language remaining the same + //despite a change to the keyboard/input language + SvtSysLocaleOptions aSysLocaleOptions; + if(aSysLocaleOptions.IsIgnoreLanguageChange()) + { + return INVALID_HINT; + } + + bool bLang = true; + if(eBufferLanguage != LANGUAGE_DONTKNOW) + { + switch( SvtLanguageOptions::GetI18NScriptTypeOfLanguage( eBufferLanguage )) + { + case i18n::ScriptType::ASIAN: nWhich = RES_CHRATR_CJK_LANGUAGE; break; + case i18n::ScriptType::COMPLEX: nWhich = RES_CHRATR_CTL_LANGUAGE; break; + case i18n::ScriptType::LATIN: nWhich = RES_CHRATR_LANGUAGE; break; + default: bLang = false; + } + if(bLang) + { + SfxItemSet aLangSet(rView.GetPool(), {{nWhich, nWhich}}); + SwWrtShell& rSh = rView.GetWrtShell(); + rSh.GetCurAttr(aLangSet); + if(SfxItemState::DEFAULT <= aLangSet.GetItemState(nWhich)) + { + LanguageType eLang = static_cast<const SvxLanguageItem&>(aLangSet.Get(nWhich)).GetLanguage(); + if ( eLang == eBufferLanguage ) + { + // current language attribute equal to language reported from system + bLang = false; + } + else if ( !g_bInputLanguageSwitched && RES_CHRATR_LANGUAGE == nWhich ) + { + // special case: switching between two "LATIN" languages + // In case the current keyboard setting might be suitable + // for both languages we can't safely assume that the user + // wants to use the language reported from the system, + // except if we knew that it was explicitly switched (thus + // the check for "bInputLangeSwitched"). + + // The language reported by the system could be just the + // system default language that the user is not even aware + // of, because no language selection tool is installed at + // all. In this case the OOo language should get preference + // as it might have been selected by the user explicitly. + + // Usually this case happens if the OOo language is + // different to the system language but the system keyboard + // is still suitable for the OOo language (e.g. writing + // English texts with a German keyboard). + + // For non-latin keyboards overwriting the attribute is + // still valid. We do this for cyrillic and greek ATM. In + // future versions of OOo this should be replaced by a + // configuration switch that allows to give the preference + // to the OOo setting or the system setting explicitly + // and/or a better handling of the script type. + i18n::UnicodeScript eType = !rInBuffer.isEmpty() ? + GetAppCharClass().getScript( rInBuffer, 0 ) : + i18n::UnicodeScript_kScriptCount; + + bool bSystemIsNonLatin = false; + switch ( eType ) + { + case i18n::UnicodeScript_kGreek: + case i18n::UnicodeScript_kCyrillic: + // in case other UnicodeScripts require special + // keyboards they can be added here + bSystemIsNonLatin = true; + break; + default: + break; + } + + bool bOOoLangIsNonLatin = MsLangId::isNonLatinWestern( eLang); + + bLang = (bSystemIsNonLatin != bOOoLangIsNonLatin); + } + } + } + } + return bLang ? nWhich : INVALID_HINT; +} + +/** + * Character buffer is inserted into the document + */ +void SwEditWin::FlushInBuffer() +{ + if ( m_aInBuffer.isEmpty() ) + return; + + SwWrtShell& rSh = m_rView.GetWrtShell(); + + // generate new sequence input checker if not already done + if ( !pCheckIt ) + pCheckIt = new SwCheckIt; + + uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = pCheckIt->xCheck; + if ( xISC.is() && IsInputSequenceCheckingRequired( m_aInBuffer, *rSh.GetCursor() ) ) + { + + // apply (Thai) input sequence checking/correction + + rSh.Push(); // push current cursor to stack + + // get text from the beginning (i.e left side) of current selection + // to the start of the paragraph + rSh.NormalizePam(); // make point be the first (left) one + if (!rSh.GetCursor()->HasMark()) + rSh.GetCursor()->SetMark(); + rSh.GetCursor()->GetMark()->nContent = 0; + + const OUString aOldText( rSh.GetCursor()->GetText() ); + const sal_Int32 nOldLen = aOldText.getLength(); + + SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions(); + + sal_Int32 nExpandSelection = 0; + if (nOldLen > 0) + { + sal_Int32 nTmpPos = nOldLen; + sal_Int16 nCheckMode = rCTLOptions.IsCTLSequenceCheckingRestricted() ? + i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; + + OUString aNewText( aOldText ); + if (rCTLOptions.IsCTLSequenceCheckingTypeAndReplace()) + { + for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k) + { + const sal_Unicode cChar = m_aInBuffer[k]; + const sal_Int32 nPrevPos =xISC->correctInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode ); + + // valid sequence or sequence could be corrected: + if (nPrevPos != aNewText.getLength()) + nTmpPos = nPrevPos + 1; + } + + // find position of first character that has changed + sal_Int32 nNewLen = aNewText.getLength(); + const sal_Unicode *pOldText = aOldText.getStr(); + const sal_Unicode *pNewText = aNewText.getStr(); + sal_Int32 nChgPos = 0; + while ( nChgPos < nOldLen && nChgPos < nNewLen && + pOldText[nChgPos] == pNewText[nChgPos] ) + ++nChgPos; + + const sal_Int32 nChgLen = nNewLen - nChgPos; + if (nChgLen) + { + m_aInBuffer = aNewText.copy( nChgPos, nChgLen ); + nExpandSelection = nOldLen - nChgPos; + } + else + m_aInBuffer.clear(); + } + else + { + for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k ) + { + const sal_Unicode cChar = m_aInBuffer[k]; + if (xISC->checkInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode )) + { + // character can be inserted: + aNewText += OUStringChar( cChar ); + ++nTmpPos; + } + } + m_aInBuffer = aNewText.copy( aOldText.getLength() ); // copy new text to be inserted to buffer + } + } + + // at this point now we will insert the buffer text 'normally' some lines below... + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + + if (m_aInBuffer.isEmpty()) + return; + + // if text prior to the original selection needs to be changed + // as well, we now expand the selection accordingly. + SwPaM &rCursor = *rSh.GetCursor(); + const sal_Int32 nCursorStartPos = rCursor.Start()->nContent.GetIndex(); + OSL_ENSURE( nCursorStartPos >= nExpandSelection, "cannot expand selection as specified!!" ); + if (nExpandSelection && nCursorStartPos >= nExpandSelection) + { + if (!rCursor.HasMark()) + rCursor.SetMark(); + rCursor.Start()->nContent -= nExpandSelection; + } + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + // determine shell + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + // generate request and record + if (pSfxShell) + { + SfxRequest aReq( m_rView.GetViewFrame(), FN_INSERT_STRING ); + aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, m_aInBuffer ) ); + aReq.Done(); + } + } + + sal_uInt16 nWhich = lcl_isNonDefaultLanguage(m_eBufferLanguage, m_rView, m_aInBuffer); + if (nWhich != INVALID_HINT ) + { + SvxLanguageItem aLangItem( m_eBufferLanguage, nWhich ); + rSh.SetAttrItem( aLangItem ); + } + + rSh.Insert( m_aInBuffer ); + m_eBufferLanguage = LANGUAGE_DONTKNOW; + m_aInBuffer.clear(); + g_bFlushCharBuffer = false; + +} + +#define MOVE_LEFT_SMALL 0 +#define MOVE_UP_SMALL 1 +#define MOVE_RIGHT_BIG 2 +#define MOVE_DOWN_BIG 3 +#define MOVE_LEFT_BIG 4 +#define MOVE_UP_BIG 5 +#define MOVE_RIGHT_SMALL 6 +#define MOVE_DOWN_SMALL 7 + +// #i121236# Support for shift key in writer +#define MOVE_LEFT_HUGE 8 +#define MOVE_UP_HUGE 9 +#define MOVE_RIGHT_HUGE 10 +#define MOVE_DOWN_HUGE 11 + +void SwEditWin::ChangeFly( sal_uInt8 nDir, bool bWeb ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SwRect aTmp = rSh.GetFlyRect(); + if( !aTmp.HasArea() || + rSh.IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE ) + return; + + SfxItemSet aSet( + rSh.GetAttrPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_PROTECT, RES_PROTECT, + RES_VERT_ORIENT, RES_ANCHOR, + RES_COL, RES_COL, + RES_FOLLOW_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW>{}); + rSh.GetFlyFrameAttr( aSet ); + RndStdIds eAnchorId = aSet.Get(RES_ANCHOR).GetAnchorId(); + Size aSnap; + bool bHuge(MOVE_LEFT_HUGE == nDir || + MOVE_UP_HUGE == nDir || + MOVE_RIGHT_HUGE == nDir || + MOVE_DOWN_HUGE == nDir); + + if(MOVE_LEFT_SMALL == nDir || + MOVE_UP_SMALL == nDir || + MOVE_RIGHT_SMALL == nDir || + MOVE_DOWN_SMALL == nDir ) + { + aSnap = PixelToLogic(Size(1,1)); + } + else + { + aSnap = rSh.GetViewOptions()->GetSnapSize(); + short nDiv = rSh.GetViewOptions()->GetDivisionX(); + if ( nDiv > 0 ) + aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) ); + nDiv = rSh.GetViewOptions()->GetDivisionY(); + if ( nDiv > 0 ) + aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) ); + } + + if(bHuge) + { + // #i121236# 567twips == 1cm, but just take three times the normal snap + aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3); + } + + SwRect aBoundRect; + Point aRefPoint; + // adjustment for allowing vertical position + // aligned to page for fly frame anchored to paragraph or to character. + { + const SwFormatVertOrient& aVert( aSet.Get(RES_VERT_ORIENT) ); + const bool bFollowTextFlow = + aSet.Get(RES_FOLLOW_TEXT_FLOW).GetValue(); + const SwPosition* pToCharContentPos = aSet.Get(RES_ANCHOR).GetContentAnchor(); + rSh.CalcBoundRect( aBoundRect, eAnchorId, + text::RelOrientation::FRAME, aVert.GetRelationOrient(), + pToCharContentPos, bFollowTextFlow, + false, &aRefPoint ); + } + long nLeft = std::min( aTmp.Left() - aBoundRect.Left(), aSnap.Width() ); + long nRight = std::min( aBoundRect.Right() - aTmp.Right(), aSnap.Width() ); + long nUp = std::min( aTmp.Top() - aBoundRect.Top(), aSnap.Height() ); + long nDown = std::min( aBoundRect.Bottom() - aTmp.Bottom(), aSnap.Height() ); + + switch ( nDir ) + { + case MOVE_LEFT_BIG: + case MOVE_LEFT_HUGE: + case MOVE_LEFT_SMALL: aTmp.Left( aTmp.Left() - nLeft ); + break; + + case MOVE_UP_BIG: + case MOVE_UP_HUGE: + case MOVE_UP_SMALL: aTmp.Top( aTmp.Top() - nUp ); + break; + + case MOVE_RIGHT_SMALL: + if( aTmp.Width() < aSnap.Width() + MINFLY ) + break; + nRight = aSnap.Width(); + [[fallthrough]]; + case MOVE_RIGHT_HUGE: + case MOVE_RIGHT_BIG: aTmp.Left( aTmp.Left() + nRight ); + break; + + case MOVE_DOWN_SMALL: + if( aTmp.Height() < aSnap.Height() + MINFLY ) + break; + nDown = aSnap.Height(); + [[fallthrough]]; + case MOVE_DOWN_HUGE: + case MOVE_DOWN_BIG: aTmp.Top( aTmp.Top() + nDown ); + break; + + default: OSL_ENSURE(true, "ChangeFly: Unknown direction." ); + } + bool bSet = false; + if ((RndStdIds::FLY_AS_CHAR == eAnchorId) && ( nDir % 2 )) + { + long aDiff = aTmp.Top() - aRefPoint.Y(); + if( aDiff > 0 ) + aDiff = 0; + else if ( aDiff < -aTmp.Height() ) + aDiff = -aTmp.Height(); + SwFormatVertOrient aVert( aSet.Get(RES_VERT_ORIENT) ); + sal_Int16 eNew; + if( bWeb ) + { + eNew = aVert.GetVertOrient(); + bool bDown = 0 != ( nDir & 0x02 ); + switch( eNew ) + { + case text::VertOrientation::CHAR_TOP: + if( bDown ) eNew = text::VertOrientation::CENTER; + break; + case text::VertOrientation::CENTER: + eNew = bDown ? text::VertOrientation::TOP : text::VertOrientation::CHAR_TOP; + break; + case text::VertOrientation::TOP: + if( !bDown ) eNew = text::VertOrientation::CENTER; + break; + case text::VertOrientation::LINE_TOP: + if( bDown ) eNew = text::VertOrientation::LINE_CENTER; + break; + case text::VertOrientation::LINE_CENTER: + eNew = bDown ? text::VertOrientation::LINE_BOTTOM : text::VertOrientation::LINE_TOP; + break; + case text::VertOrientation::LINE_BOTTOM: + if( !bDown ) eNew = text::VertOrientation::LINE_CENTER; + break; + default:; //prevent warning + } + } + else + { + aVert.SetPos( aDiff ); + eNew = text::VertOrientation::NONE; + } + aVert.SetVertOrient( eNew ); + aSet.Put( aVert ); + bSet = true; + } + if (bWeb && (RndStdIds::FLY_AT_PARA == eAnchorId) + && ( nDir==MOVE_LEFT_SMALL || nDir==MOVE_RIGHT_BIG )) + { + SwFormatHoriOrient aHori( aSet.Get(RES_HORI_ORIENT) ); + sal_Int16 eNew; + eNew = aHori.GetHoriOrient(); + switch( eNew ) + { + case text::HoriOrientation::RIGHT: + if( nDir==MOVE_LEFT_SMALL ) + eNew = text::HoriOrientation::LEFT; + break; + case text::HoriOrientation::LEFT: + if( nDir==MOVE_RIGHT_BIG ) + eNew = text::HoriOrientation::RIGHT; + break; + default:; //prevent warning + } + if( eNew != aHori.GetHoriOrient() ) + { + aHori.SetHoriOrient( eNew ); + aSet.Put( aHori ); + bSet = true; + } + } + rSh.StartAllAction(); + if( bSet ) + rSh.SetFlyFrameAttr( aSet ); + bool bSetPos = (RndStdIds::FLY_AS_CHAR != eAnchorId); + if(bSetPos && bWeb) + { + bSetPos = RndStdIds::FLY_AT_PAGE == eAnchorId; + } + if( bSetPos ) + rSh.SetFlyPos( aTmp.Pos() ); + rSh.EndAllAction(); + +} + +void SwEditWin::ChangeDrawing( sal_uInt8 nDir ) +{ + // start undo action in order to get only one + // undo action for this change. + SwWrtShell &rSh = m_rView.GetWrtShell(); + rSh.StartUndo(); + + long nX = 0; + long nY = 0; + const bool bOnePixel( + MOVE_LEFT_SMALL == nDir || + MOVE_UP_SMALL == nDir || + MOVE_RIGHT_SMALL == nDir || + MOVE_DOWN_SMALL == nDir); + const bool bHuge( + MOVE_LEFT_HUGE == nDir || + MOVE_UP_HUGE == nDir || + MOVE_RIGHT_HUGE == nDir || + MOVE_DOWN_HUGE == nDir); + SwMove nAnchorDir = SwMove::UP; + switch(nDir) + { + case MOVE_LEFT_SMALL: + case MOVE_LEFT_HUGE: + case MOVE_LEFT_BIG: + nX = -1; + nAnchorDir = SwMove::LEFT; + break; + case MOVE_UP_SMALL: + case MOVE_UP_HUGE: + case MOVE_UP_BIG: + nY = -1; + break; + case MOVE_RIGHT_SMALL: + case MOVE_RIGHT_HUGE: + case MOVE_RIGHT_BIG: + nX = +1; + nAnchorDir = SwMove::RIGHT; + break; + case MOVE_DOWN_SMALL: + case MOVE_DOWN_HUGE: + case MOVE_DOWN_BIG: + nY = +1; + nAnchorDir = SwMove::DOWN; + break; + } + + if(0 != nX || 0 != nY) + { + FlyProtectFlags nProtect = rSh.IsSelObjProtected( FlyProtectFlags::Pos|FlyProtectFlags::Size ); + Size aSnap( rSh.GetViewOptions()->GetSnapSize() ); + short nDiv = rSh.GetViewOptions()->GetDivisionX(); + if ( nDiv > 0 ) + aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) ); + nDiv = rSh.GetViewOptions()->GetDivisionY(); + if ( nDiv > 0 ) + aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) ); + + if(bOnePixel) + { + aSnap = PixelToLogic(Size(1,1)); + } + else if(bHuge) + { + // #i121236# 567twips == 1cm, but just take three times the normal snap + aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3); + } + + nX *= aSnap.Width(); + nY *= aSnap.Height(); + + SdrView *pSdrView = rSh.GetDrawView(); + const SdrHdlList& rHdlList = pSdrView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + rSh.StartAllAction(); + if(nullptr == pHdl) + { + // now move the selected draw objects + // if the object's position is not protected + if(!(nProtect&FlyProtectFlags::Pos)) + { + // Check if object is anchored as character and move direction + bool bDummy1, bDummy2; + const bool bVertAnchor = rSh.IsFrameVertical( true, bDummy1, bDummy2 ); + bool bHoriMove = !bVertAnchor == !( nDir % 2 ); + bool bMoveAllowed = + !bHoriMove || (rSh.GetAnchorId() != RndStdIds::FLY_AS_CHAR); + if ( bMoveAllowed ) + { + pSdrView->MoveAllMarked(Size(nX, nY)); + rSh.SetModified(); + } + } + } + else + { + // move handle with index nHandleIndex + if (nX || nY) + { + if( SdrHdlKind::Anchor == pHdl->GetKind() || + SdrHdlKind::Anchor_TR == pHdl->GetKind() ) + { + // anchor move cannot be allowed when position is protected + if(!(nProtect&FlyProtectFlags::Pos)) + rSh.MoveAnchor( nAnchorDir ); + } + //now resize if size is protected + else if(!(nProtect&FlyProtectFlags::Size)) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = pSdrView->GetDragStat(); + + // start dragging + pSdrView->BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if(pSdrView->IsDragObj()) + { + bool bWasNoSnap = rDragStat.IsNoSnap(); + bool bWasSnapEnabled = pSdrView->IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(); + if(bWasSnapEnabled) + pSdrView->SetSnapEnabled(false); + + pSdrView->MovAction(aEndPoint); + pSdrView->EndDragObj(); + rSh.SetModified(); + + // restore snap + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + pSdrView->SetSnapEnabled(bWasSnapEnabled); + } + } + } + } + rSh.EndAllAction(); + } + + rSh.EndUndo(); +} + +/** + * KeyEvents + */ +void SwEditWin::KeyInput(const KeyEvent &rKEvt) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + + if (comphelper::LibreOfficeKit::isActive() && m_rView.GetPostItMgr()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->GetActiveSidebarWin()) + { + pWindow->KeyInput(rKEvt); + return; + } + } + + if( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE && + m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard ) + { + m_pApplyTempl->m_pFormatClipboard->Erase(); + SetApplyTemplate(SwApplyTemplate()); + m_rView.GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + } + else if ( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE && + rSh.IsHeaderFooterEdit( ) ) + { + bool bHeader = bool(FrameTypeFlags::HEADER & rSh.GetFrameType(nullptr,false)); + if ( bHeader ) + rSh.SttPg(); + else + rSh.EndPg(); + rSh.ToggleHeaderFooterEdit(); + } + + SfxObjectShell *pObjSh = m_rView.GetViewFrame()->GetObjectShell(); + if ( m_bLockInput || (pObjSh && pObjSh->GetProgress()) ) + // When the progress bar is active or a progress is + // running on a document, no order is being taken + return; + + m_pShadCursor.reset(); + m_aKeyInputFlushTimer.Stop(); + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + + //if the language changes the buffer must be flushed + LanguageType eNewLanguage = GetInputLanguage(); + if(!bIsDocReadOnly && m_eBufferLanguage != eNewLanguage && !m_aInBuffer.isEmpty()) + { + FlushInBuffer(); + } + m_eBufferLanguage = eNewLanguage; + + QuickHelpData aTmpQHD; + if( m_pQuickHlpData->m_bIsDisplayed ) + { + aTmpQHD.Move( *m_pQuickHlpData ); + m_pQuickHlpData->Stop( rSh ); + } + + // OS:the DrawView also needs a readonly-Flag as well + if ( !bIsDocReadOnly && rSh.GetDrawView() && rSh.GetDrawView()->KeyInput( rKEvt, this ) ) + { + rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll( false ); + rSh.SetModified(); + return; // Event evaluated by SdrView + } + + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + bool bFlushBuffer = false; + bool bNormalChar = false; + bool bAppendSpace = m_pQuickHlpData->m_bAppendSpace; + m_pQuickHlpData->m_bAppendSpace = false; + + if ( getenv("SW_DEBUG") && rKEvt.GetKeyCode().GetCode() == KEY_F12 ) + { + if( rKEvt.GetKeyCode().IsShift()) + { + GetView().GetDocShell()->GetDoc()->dumpAsXml(); + return; + } + else + { + SwRootFrame* pLayout = GetView().GetDocShell()->GetWrtShell()->GetLayout(); + pLayout->dumpAsXml( ); + return; + } + } + + KeyEvent aKeyEvent( rKEvt ); + // look for vertical mappings + if( !bIsDocReadOnly && !rSh.IsSelFrameMode() && !rSh.IsObjSelected() ) + { + // must changed from switch to if, because the Linux + // compiler has problem with the code. Has to remove if the new general + // handler exist. + sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); + + if( KEY_UP == nKey || KEY_DOWN == nKey || + KEY_LEFT == nKey || KEY_RIGHT == nKey ) + { + // In general, we want to map the direction keys if we are inside + // some vertical formatted text. + // 1. Exception: For a table cursor in a horizontal table, the + // directions should never be mapped. + // 2. Exception: For a table cursor in a vertical table, the + // directions should always be mapped. + const bool bVertText = rSh.IsInVerticalText(); + const bool bTableCursor = rSh.GetTableCursor(); + const bool bVertTable = rSh.IsTableVertical(); + if( ( bVertText && ( !bTableCursor || bVertTable ) ) || + ( bTableCursor && bVertTable ) ) + { + SvxFrameDirection eDirection = rSh.GetTextDirection(); + if (eDirection == SvxFrameDirection::Vertical_LR_BT) + { + // Map from physical to logical, so rotate clockwise. + if (KEY_UP == nKey) + nKey = KEY_RIGHT; + else if (KEY_DOWN == nKey) + nKey = KEY_LEFT; + else if (KEY_LEFT == nKey) + nKey = KEY_UP; + else /* KEY_RIGHT == nKey */ + nKey = KEY_DOWN; + } + else + { + // Attempt to integrate cursor travelling for mongolian layout does not work. + // Thus, back to previous mapping of cursor keys to direction keys. + if( KEY_UP == nKey ) nKey = KEY_LEFT; + else if( KEY_DOWN == nKey ) nKey = KEY_RIGHT; + else if( KEY_LEFT == nKey ) nKey = KEY_DOWN; + else /* KEY_RIGHT == nKey */ nKey = KEY_UP; + } + } + + if ( rSh.IsInRightToLeftText() ) + { + if( KEY_LEFT == nKey ) nKey = KEY_RIGHT; + else if( KEY_RIGHT == nKey ) nKey = KEY_LEFT; + } + + aKeyEvent = KeyEvent( rKEvt.GetCharCode(), + vcl::KeyCode( nKey, rKEvt.GetKeyCode().GetModifier() ), + rKEvt.GetRepeat() ); + } + } + + const vcl::KeyCode& rKeyCode = aKeyEvent.GetKeyCode(); + sal_Unicode aCh = aKeyEvent.GetCharCode(); + + // enable switching to notes anchor with Ctrl - Alt - Page Up/Down + // pressing this inside a note will switch to next/previous note + if ((rKeyCode.IsMod1() && rKeyCode.IsMod2()) && ((rKeyCode.GetCode() == KEY_PAGEUP) || (rKeyCode.GetCode() == KEY_PAGEDOWN))) + { + const bool bNext = rKeyCode.GetCode()==KEY_PAGEDOWN; + const SwFieldType* pFieldType = rSh.GetFieldType( 0, SwFieldIds::Postit ); + rSh.MoveFieldType( pFieldType, bNext ); + return; + } + + const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat(); + if( pFlyFormat ) + { + SvMacroItemId nEvent; + + if( 32 <= aCh && + 0 == (( KEY_MOD1 | KEY_MOD2 ) & rKeyCode.GetModifier() )) + nEvent = SvMacroItemId::SwFrmKeyInputAlpha; + else + nEvent = SvMacroItemId::SwFrmKeyInputNoAlpha; + + const SvxMacro* pMacro = pFlyFormat->GetMacro().GetMacroTable().Get( nEvent ); + if( pMacro ) + { + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put32( xVar.get(), 1 ); + + xVar = new SbxVariable; + if( SvMacroItemId::SwFrmKeyInputAlpha == nEvent ) + xVar->PutChar( aCh ); + else + xVar->PutUShort( rKeyCode.GetModifier() | rKeyCode.GetCode() ); + xArgs->Put32( xVar.get(), 2 ); + + OUString sRet; + rSh.ExecMacro( *pMacro, &sRet, xArgs.get() ); + if( !sRet.isEmpty() && sRet.toInt32()!=0 ) + return ; + } + } + SelectionType nLclSelectionType; + //A is converted to 1 + if( rKeyCode.GetFullCode() == (KEY_A | KEY_MOD1 |KEY_SHIFT) + && rSh.HasDrawView() && + (bool(nLclSelectionType = rSh.GetSelectionType()) && + ((nLclSelectionType & (SelectionType::Frame|SelectionType::Graphic)) || + ((nLclSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1)))) + { + SdrHdlList& rHdlList = const_cast<SdrHdlList&>(rSh.GetDrawView()->GetHdlList()); + SdrHdl* pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor); + if ( ! pAnchor ) + pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor_TR); + if(pAnchor) + rHdlList.SetFocusHdl(pAnchor); + return; + } + + SvxAutoCorrCfg* pACfg = nullptr; + SvxAutoCorrect* pACorr = nullptr; + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame()->GetBindings().GetRecorder(); + if ( !xRecorder.is() ) + { + pACfg = &SvxAutoCorrCfg::Get(); + pACorr = pACfg->GetAutoCorrect(); + } + + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + OUString sFormulaEntry; + + enum class SwKeyState { CheckKey, InsChar, InsTab, + NoNum, NumOff, NumOrNoNum, NumDown, NumUp, + NumIndentInc, NumIndentDec, + + OutlineLvOff, + NextCell, PrevCell, OutlineUp, OutlineDown, + GlossaryExpand, NextPrevGlossary, + AutoFormatByInput, + NextObject, PrevObject, + KeyToView, + LaunchOLEObject, GoIntoFly, GoIntoDrawing, + EnterDrawHandleMode, + CheckDocReadOnlyKeys, + CheckAutoCorrect, EditFormula, + ColLeftBig, ColRightBig, + ColLeftSmall, ColRightSmall, + ColBottomBig, + ColBottomSmall, + CellLeftBig, CellRightBig, + CellLeftSmall, CellRightSmall, + CellTopBig, CellBottomBig, + CellTopSmall, CellBottomSmall, + + Fly_Change, Draw_Change, + SpecialInsert, + EnterCharCell, + GotoNextFieldMark, + GotoPrevFieldMark, + End }; + + SwKeyState eKeyState = bIsDocReadOnly ? SwKeyState::CheckDocReadOnlyKeys : SwKeyState::CheckKey; + SwKeyState eNextKeyState = SwKeyState::End; + sal_uInt8 nDir = 0; + + if (m_nKS_NUMDOWN_Count > 0) + m_nKS_NUMDOWN_Count--; + + if (m_nKS_NUMINDENTINC_Count > 0) + m_nKS_NUMINDENTINC_Count--; + + while( SwKeyState::End != eKeyState ) + { + SwKeyState eFlyState = SwKeyState::KeyToView; + + switch( eKeyState ) + { + case SwKeyState::CheckKey: + eKeyState = SwKeyState::KeyToView; // default forward to View + +#if OSL_DEBUG_LEVEL > 1 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // for switching cursor behaviour in ReadOnly regions + if( 0x7210 == rKeyCode.GetFullCode() ) + rSh.SetReadOnlyAvailable( !rSh.IsReadOnlyAvailable() ); + else +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#endif + + if (!comphelper::LibreOfficeKit::isActive() && + !rKeyCode.IsMod2() && '=' == aCh && + !rSh.IsTableMode() && rSh.GetTableFormat() && + rSh.IsSttPara() && + !rSh.HasReadonlySel()) + { + // at the beginning of the table's cell a '=' -> + // call EditRow (F2-functionality) + // [Avoid this for LibreOfficeKit, as the separate input window + // steals the focus & things go wrong - the user never gets + // the focus back.] + rSh.Push(); + if( !rSh.MoveSection( GoCurrSection, fnSectionStart) && + !rSh.IsTableBoxTextFormat() ) + { + // is at the beginning of the box + eKeyState = SwKeyState::EditFormula; + if( rSh.HasMark() ) + rSh.SwapPam(); + else + rSh.SttSelect(); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + rSh.Pop(); + rSh.EndSelect(); + sFormulaEntry = "="; + } + else + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + if( pACorr && aTmpQHD.HasContent() && !rSh.HasSelection() && + !rSh.HasReadonlySel() && !aTmpQHD.m_bIsAutoText && + pACorr->GetSwFlags().nAutoCmpltExpandKey == + (rKeyCode.GetModifier() | rKeyCode.GetCode()) ) + { + eKeyState = SwKeyState::GlossaryExpand; + break; + } + + switch( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_RIGHT | KEY_MOD2: + eKeyState = SwKeyState::ColRightBig; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_RIGHT_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2: + eKeyState = SwKeyState::ColRightSmall; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_LEFT_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT: + eKeyState = SwKeyState::ColLeftSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_SHIFT: + eKeyState = SwKeyState::ColLeftBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellRightBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellRightSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellLeftSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_LEFT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellLeftBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2: + eKeyState = SwKeyState::ColBottomSmall; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_UP_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2: + eKeyState = SwKeyState::ColBottomBig; + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_DOWN_SMALL; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellBottomSmall; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2 | KEY_MOD1: + eKeyState = SwKeyState::CellBottomBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_UP | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellTopBig; + goto KEYINPUT_CHECKTABLE; + + case KEY_DOWN | KEY_MOD2 | KEY_SHIFT | KEY_MOD1: + eKeyState = SwKeyState::CellTopSmall; + goto KEYINPUT_CHECKTABLE; + +KEYINPUT_CHECKTABLE: + if( rSh.IsTableMode() || !rSh.GetTableFormat() ) + { + if(!pFlyFormat && SwKeyState::KeyToView != eFlyState && + (rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::Draw_Change; + + if( pFlyFormat ) + eKeyState = eFlyState; + else if( SwKeyState::Draw_Change != eKeyState) + eKeyState = SwKeyState::EnterCharCell; + } + break; + + // huge object move + case KEY_RIGHT | KEY_SHIFT: + case KEY_LEFT | KEY_SHIFT: + case KEY_UP | KEY_SHIFT: + case KEY_DOWN | KEY_SHIFT: + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if ( ( pFlyFormat + && ( nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic) ) ) + || ( ( nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm) ) + && rSh.GetDrawView()->AreObjectsMarked() ) ) + { + eKeyState = pFlyFormat ? SwKeyState::Fly_Change : SwKeyState::Draw_Change; + switch ( rKeyCode.GetCode() ) + { + case KEY_RIGHT: nDir = MOVE_RIGHT_HUGE; break; + case KEY_LEFT: nDir = MOVE_LEFT_HUGE; break; + case KEY_UP: nDir = MOVE_UP_HUGE; break; + case KEY_DOWN: nDir = MOVE_DOWN_HUGE; break; + } + } + break; + } + + case KEY_LEFT: + case KEY_LEFT | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_LEFT_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_RIGHT | KEY_MOD1: + { + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_UP: + case KEY_UP | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_UP_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_DOWN: + case KEY_DOWN | KEY_MOD1: + { + bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1); + if(!bMod1) + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_DOWN_BIG; + } + goto KEYINPUT_CHECKTABLE_INSDEL; + } + +KEYINPUT_CHECKTABLE_INSDEL: + if( rSh.IsTableMode() || !rSh.GetTableFormat() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + + eKeyState = SwKeyState::KeyToView; + if(SwKeyState::KeyToView != eFlyState) + { + if((nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::Draw_Change; + else if(nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) + eKeyState = SwKeyState::Fly_Change; + } + } + break; + + + case KEY_DELETE: + if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField()) + { + if (rSh.IsInFrontOfLabel() && rSh.NumOrNoNum()) + eKeyState = SwKeyState::NumOrNoNum; + } + else if (!rSh.IsCursorInParagraphMetadataField()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xInfo->run(); + eKeyState = SwKeyState::End; + } + break; + + case KEY_RETURN: + { + if ( !rSh.HasReadonlySel() + && !rSh.CursorInsideInputField() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Ole) + eKeyState = SwKeyState::LaunchOLEObject; + else if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else if((nSelectionType & SelectionType::DrawObject) && + !(nSelectionType & SelectionType::DrawObjectEditMode) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1) + { + eKeyState = SwKeyState::GoIntoDrawing; + if (lcl_goIntoTextBox(*this, rSh)) + eKeyState = SwKeyState::GoIntoFly; + } + else if( aTmpQHD.HasContent() && !rSh.HasSelection() && + aTmpQHD.m_bIsAutoText ) + eKeyState = SwKeyState::GlossaryExpand; + + //RETURN and empty paragraph in numbering -> end numbering + else if( m_aInBuffer.isEmpty() && + rSh.GetNumRuleAtCurrCursorPos() && + !rSh.GetNumRuleAtCurrCursorPos()->IsOutlineRule() && + !rSh.HasSelection() && + rSh.IsSttPara() && rSh.IsEndPara() ) + { + eKeyState = SwKeyState::NumOff; + eNextKeyState = SwKeyState::OutlineLvOff; + } + //RETURN for new paragraph with AutoFormatting + else if( pACfg && pACfg->IsAutoFormatByInput() && + !(nSelectionType & (SelectionType::Graphic | + SelectionType::Ole | SelectionType::Frame | + SelectionType::TableCell | SelectionType::DrawObject | + SelectionType::DrawObjectEditMode)) ) + { + eKeyState = SwKeyState::AutoFormatByInput; + } + else + { + eNextKeyState = eKeyState; + eKeyState = SwKeyState::CheckAutoCorrect; + } + } + } + break; + case KEY_RETURN | KEY_MOD2: + { + if ( !rSh.HasReadonlySel() + && !rSh.IsSttPara() + && rSh.GetNumRuleAtCurrCursorPos() + && !rSh.CursorInsideInputField() ) + { + eKeyState = SwKeyState::NoNum; + } + else if( rSh.CanSpecialInsert() ) + eKeyState = SwKeyState::SpecialInsert; + } + break; + case KEY_BACKSPACE: + case KEY_BACKSPACE | KEY_SHIFT: + if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField()) + { + bool bDone = false; + // try to add comment for code snip: + // Remove the paragraph indent, if the cursor is at the + // beginning of a paragraph, there is no selection + // and no numbering rule found at the current paragraph + // Also try to remove indent, if current paragraph + // has numbering rule, but isn't counted and only + // key <backspace> is hit. + const bool bOnlyBackspaceKey( KEY_BACKSPACE == rKeyCode.GetFullCode() ); + if ( rSh.IsSttPara() + && !rSh.HasSelection() + && ( rSh.GetNumRuleAtCurrCursorPos() == nullptr + || ( rSh.IsNoNum() && bOnlyBackspaceKey ) ) ) + { + bDone = rSh.TryRemoveIndent(); + } + + if (bDone) + eKeyState = SwKeyState::End; + else + { + if ( rSh.IsSttPara() && !rSh.IsNoNum() ) + { + if (m_nKS_NUMDOWN_Count > 0 && + 0 < rSh.GetNumLevel()) + { + eKeyState = SwKeyState::NumUp; + m_nKS_NUMDOWN_Count = 2; + bDone = true; + } + else if (m_nKS_NUMINDENTINC_Count > 0) + { + eKeyState = SwKeyState::NumIndentDec; + m_nKS_NUMINDENTINC_Count = 2; + bDone = true; + } + } + + // If the cursor is in an empty paragraph, which has + // a numbering, but not the outline numbering, and + // there is no selection, the numbering has to be + // deleted on key <Backspace>. + // Otherwise method <SwEditShell::NumOrNoNum(..)> + // should only change the <IsCounted()> state of + // the current paragraph depending of the key. + // On <backspace> it is set to <false>, + // on <shift-backspace> it is set to <true>. + // Thus, assure that method <SwEditShell::NumOrNum(..)> + // is only called for the intended purpose. + if ( !bDone && rSh.IsSttPara() ) + { + bool bCallNumOrNoNum( false ); + if ( bOnlyBackspaceKey && !rSh.IsNoNum() ) + { + bCallNumOrNoNum = true; + } + else if ( !bOnlyBackspaceKey && rSh.IsNoNum() ) + { + bCallNumOrNoNum = true; + } + else if ( bOnlyBackspaceKey + && rSh.IsSttPara() + && rSh.IsEndPara() + && !rSh.HasSelection() ) + { + const SwNumRule* pCurrNumRule( rSh.GetNumRuleAtCurrCursorPos() ); + if ( pCurrNumRule != nullptr + && pCurrNumRule != rSh.GetOutlineNumRule() ) + { + bCallNumOrNoNum = true; + } + } + if ( bCallNumOrNoNum + && rSh.NumOrNoNum( !bOnlyBackspaceKey ) ) + { + eKeyState = SwKeyState::NumOrNoNum; + } + } + } + } + else if (!rSh.IsCursorInParagraphMetadataField()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xInfo->run(); + eKeyState = SwKeyState::End; + } + break; + + case KEY_RIGHT: + { + eFlyState = SwKeyState::Fly_Change; + nDir = MOVE_RIGHT_BIG; + goto KEYINPUT_CHECKTABLE_INSDEL; + } + case KEY_TAB: + { + + if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark() || rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + eKeyState = SwKeyState::GotoNextFieldMark; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame()->GetDispatcher()->Execute( FN_GOTO_NEXT_INPUTFLD ); + eKeyState = SwKeyState::End; + } + else if( rSh.GetNumRuleAtCurrCursorPos() + && rSh.IsSttOfPara() + && !rSh.HasReadonlySel() ) + { + if ( !rSh.IsMultiSelection() + && rSh.IsFirstOfNumRuleAtCursorPos() + && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) + eKeyState = SwKeyState::NumIndentInc; + else + eKeyState = SwKeyState::NumDown; + } + else if ( rSh.GetTableFormat() ) + { + if( rSh.HasSelection() || rSh.HasReadonlySel() ) + eKeyState = SwKeyState::NextCell; + else + { + eKeyState = SwKeyState::CheckAutoCorrect; + eNextKeyState = SwKeyState::NextCell; + } + } + else if ( rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + + eKeyState = SwKeyState::NextObject; + else + { + eKeyState = SwKeyState::InsTab; + if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() ) + { + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && + + pColl->IsAssignedToListLevelOfOutlineStyle() + && MAXLEVEL-1 > pColl->GetAssignedOutlineStyleLevel() ) + eKeyState = SwKeyState::OutlineDown; + } + } + } + break; + case KEY_TAB | KEY_SHIFT: + { + if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark()|| rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + eKeyState = SwKeyState::GotoPrevFieldMark; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame()->GetDispatcher()->Execute( FN_GOTO_PREV_INPUTFLD ); + eKeyState = SwKeyState::End; + } + else if( rSh.GetNumRuleAtCurrCursorPos() + && rSh.IsSttOfPara() + && !rSh.HasReadonlySel() ) + { + if ( !rSh.IsMultiSelection() + && rSh.IsFirstOfNumRuleAtCursorPos() + && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() ) + eKeyState = SwKeyState::NumIndentDec; + else + eKeyState = SwKeyState::NumUp; + } + else if ( rSh.GetTableFormat() ) + { + if( rSh.HasSelection() || rSh.HasReadonlySel() ) + eKeyState = SwKeyState::PrevCell; + else + { + eKeyState = SwKeyState::CheckAutoCorrect; + eNextKeyState = SwKeyState::PrevCell; + } + } + else if ( rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + + eKeyState = SwKeyState::PrevObject; + else + { + eKeyState = SwKeyState::End; + if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() ) + { + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && + pColl->IsAssignedToListLevelOfOutlineStyle() && + 0 < pColl->GetAssignedOutlineStyleLevel()) + eKeyState = SwKeyState::OutlineUp; + } + } + } + break; + case KEY_TAB | KEY_MOD1: + case KEY_TAB | KEY_MOD2: + if( !rSh.HasReadonlySel() ) + { + if( aTmpQHD.HasContent() && !rSh.HasSelection() ) + { + // Next auto-complete suggestion + aTmpQHD.Next( pACorr && + pACorr->GetSwFlags().bAutoCmpltEndless ); + eKeyState = SwKeyState::NextPrevGlossary; + } + else if( rSh.GetTableFormat() ) + eKeyState = SwKeyState::InsTab; + else if((rSh.GetSelectionType() & + (SelectionType::DrawObject|SelectionType::DbForm| + SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) && + rSh.GetDrawView()->AreObjectsMarked()) + eKeyState = SwKeyState::EnterDrawHandleMode; + else + { + eKeyState = SwKeyState::InsTab; + } + } + break; + + case KEY_TAB | KEY_MOD1 | KEY_SHIFT: + { + if( aTmpQHD.HasContent() && !rSh.HasSelection() && + !rSh.HasReadonlySel() ) + { + // Previous auto-complete suggestion. + aTmpQHD.Previous( pACorr && + pACorr->GetSwFlags().bAutoCmpltEndless ); + eKeyState = SwKeyState::NextPrevGlossary; + } + else if((rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm| + SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) && + rSh.GetDrawView()->AreObjectsMarked()) + { + eKeyState = SwKeyState::EnterDrawHandleMode; + } + } + break; + case KEY_F2 : + if( !rSh.HasReadonlySel() ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else if(nSelectionType & SelectionType::DrawObject) + { + eKeyState = SwKeyState::GoIntoDrawing; + if (lcl_goIntoTextBox(*this, rSh)) + eKeyState = SwKeyState::GoIntoFly; + } + } + break; + } + } + break; + case SwKeyState::CheckDocReadOnlyKeys: + { + eKeyState = SwKeyState::KeyToView; + switch( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_TAB: + case KEY_TAB | KEY_SHIFT: + bNormalChar = false; + eKeyState = SwKeyState::End; + if ( rSh.GetSelectionType() & + (SelectionType::Graphic | + SelectionType::Frame | + SelectionType::Ole | + SelectionType::DrawObject | + SelectionType::DbForm)) + + { + eKeyState = (rKeyCode.GetModifier() & KEY_SHIFT) ? + SwKeyState::PrevObject : SwKeyState::NextObject; + } + else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame()->GetDispatcher()->Execute( + KEY_SHIFT != rKeyCode.GetModifier() ? FN_GOTO_NEXT_INPUTFLD : FN_GOTO_PREV_INPUTFLD ); + } + else + { + rSh.SelectNextPrevHyperlink( KEY_SHIFT != rKeyCode.GetModifier() ); + } + break; + case KEY_RETURN: + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + if(nSelectionType & SelectionType::Frame) + eKeyState = SwKeyState::GoIntoFly; + else + { + SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr(aSet); + if(SfxItemState::SET == aSet.GetItemState(RES_TXTATR_INETFMT, false)) + { + const SfxPoolItem& rItem = aSet.Get(RES_TXTATR_INETFMT); + bNormalChar = false; + eKeyState = SwKeyState::End; + rSh.ClickToINetAttr(static_cast<const SwFormatINetFormat&>(rItem)); + } + } + } + break; + } + } + break; + + case SwKeyState::EnterCharCell: + { + eKeyState = SwKeyState::KeyToView; + switch ( rKeyCode.GetModifier() | rKeyCode.GetCode() ) + { + case KEY_RIGHT | KEY_MOD2: + rSh.Right( CRSR_SKIP_CHARS, false, 1, false ); + eKeyState = SwKeyState::End; + FlushInBuffer(); + break; + case KEY_LEFT | KEY_MOD2: + rSh.Left( CRSR_SKIP_CHARS, false, 1, false ); + eKeyState = SwKeyState::End; + FlushInBuffer(); + break; + } + } + break; + + case SwKeyState::KeyToView: + { + eKeyState = SwKeyState::End; + bNormalChar = + !rKeyCode.IsMod2() && + rKeyCode.GetModifier() != KEY_MOD1 && + rKeyCode.GetModifier() != (KEY_MOD1|KEY_SHIFT) && + SW_ISPRINTABLE( aCh ); + + if( bNormalChar && rSh.IsInFrontOfLabel() ) + { + rSh.NumOrNoNum(); + } + + if( !m_aInBuffer.isEmpty() && ( !bNormalChar || bIsDocReadOnly )) + FlushInBuffer(); + + if (rSh.HasReadonlySel() + && ( rKeyCode.GetFunction() == KeyFuncType::PASTE + || rKeyCode.GetFunction() == KeyFuncType::CUT)) + { + auto xInfo(std::make_shared<weld::GenericDialogController>(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog")); + weld::DialogController::runAsync(xInfo, [](int) {}); + eKeyState = SwKeyState::End; + } + else if( m_rView.KeyInput( aKeyEvent ) ) + { + bFlushBuffer = true; + bNormalChar = false; + } + else + { + // Because Sfx accelerators are only called when they were + // enabled at the last status update, copy has to called + // 'forcefully' by us if necessary. + if( rKeyCode.GetFunction() == KeyFuncType::COPY ) + GetView().GetViewFrame()->GetBindings().Execute(SID_COPY); + + if( !bIsDocReadOnly && bNormalChar ) + { + const SelectionType nSelectionType = rSh.GetSelectionType(); + const bool bDrawObject = (nSelectionType & SelectionType::DrawObject) && + !(nSelectionType & SelectionType::DrawObjectEditMode) && + rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1; + + bool bTextBox = false; + if (bDrawObject && lcl_goIntoTextBox(*this, rSh)) + // A draw shape was selected, but it has a TextBox, + // start editing that instead when the normal + // character is pressed. + bTextBox = true; + + if (bDrawObject && !bTextBox) + { + SdrObject* pObj = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if(pObj) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) + static_cast<SwDrawTextShell*>(m_rView.GetCurShell())->Init(); + rSh.GetDrawView()->KeyInput( rKEvt, this ); + } + } + else if (nSelectionType & SelectionType::Frame || bTextBox) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + } + eKeyState = SwKeyState::InsChar; + } + else + { + bNormalChar = false; + Window::KeyInput( aKeyEvent ); + } + } + } + break; + case SwKeyState::LaunchOLEObject: + { + rSh.LaunchOLEObj(); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::GoIntoFly: + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + rSh.MoveSection( GoCurrSection, fnSectionEnd ); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::GoIntoDrawing: + { + if (SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0)) + { + SdrObject* pObj = pMark->GetMarkedSdrObj(); + if(pObj) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if (dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) + static_cast<SwDrawTextShell*>(m_rView.GetCurShell())->Init(); + } + } + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::EnterDrawHandleMode: + { + const SdrHdlList& rHdlList = rSh.GetDrawView()->GetHdlList(); + bool bForward(!aKeyEvent.GetKeyCode().IsShift()); + + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward); + eKeyState = SwKeyState::End; + } + break; + case SwKeyState::InsTab: + if( dynamic_cast<const SwWebView*>( &m_rView) != nullptr) // no Tab for WebView + { + // then it should be passed along + Window::KeyInput( aKeyEvent ); + eKeyState = SwKeyState::End; + break; + } + aCh = '\t'; + [[fallthrough]]; + case SwKeyState::InsChar: + if (rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + { + ::sw::mark::ICheckboxFieldmark* pFieldmark = + dynamic_cast< ::sw::mark::ICheckboxFieldmark* > + (rSh.GetCurrentFieldmark()); + OSL_ENSURE(pFieldmark, + "Where is my FieldMark??"); + if(pFieldmark) + { + pFieldmark->SetChecked(!pFieldmark->IsChecked()); + OSL_ENSURE(pFieldmark->IsExpanded(), + "where is the otherpos?"); + if (pFieldmark->IsExpanded()) + { + rSh.CalcLayout(); + } + } + eKeyState = SwKeyState::End; + } + else if ( !rSh.HasReadonlySel() + || rSh.CursorInsideInputField() ) + { + const bool bIsNormalChar = + GetAppCharClass().isLetterNumeric( OUString( aCh ), 0 ); + if( bAppendSpace && bIsNormalChar && + (!m_aInBuffer.isEmpty() || !rSh.IsSttPara() || !rSh.IsEndPara() )) + { + // insert a blank ahead of the character. this ends up + // between the expanded text and the new "non-word-separator". + m_aInBuffer += " "; + } + + const bool bIsAutoCorrectChar = SvxAutoCorrect::IsAutoCorrectChar( aCh ); + if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) && + pACfg->IsAutoFormatByInput() && + (( pACorr->IsAutoCorrFlag( ACFlags::ChgWeightUnderl ) && + ( '*' == aCh || '_' == aCh ) ) || + ( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))|| + ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh)))) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, aCh ); + if( '\"' != aCh && '\'' != aCh ) // only call when "*_"! + rSh.UpdateAttr(); + } + else if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) && + pACfg->IsAutoFormatByInput() && + pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::ChgOrdinalNumber | ACFlags::AddNonBrkSpace | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | + ACFlags::Autocorrect | ACFlags::TransliterateRTL ) && + '\"' != aCh && '\'' != aCh && '*' != aCh && '_' != aCh + ) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, aCh ); + } + else + { + OUStringBuffer aBuf(m_aInBuffer); + comphelper::string::padToLength(aBuf, + m_aInBuffer.getLength() + aKeyEvent.GetRepeat() + 1, aCh); + m_aInBuffer = aBuf.makeStringAndClear(); + g_bFlushCharBuffer = Application::AnyInput( VclInputFlags::KEYBOARD ); + bFlushBuffer = !g_bFlushCharBuffer; + if( g_bFlushCharBuffer ) + m_aKeyInputFlushTimer.Start(); + } + eKeyState = SwKeyState::End; + } + else + { + auto xInfo(std::make_shared<weld::GenericDialogController>(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog")); + weld::DialogController::runAsync(xInfo, [](int) {}); + eKeyState = SwKeyState::End; + } + break; + + case SwKeyState::CheckAutoCorrect: + { + if( pACorr && pACfg->IsAutoFormatByInput() && + pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | + ACFlags::Autocorrect ) && + !rSh.HasReadonlySel() ) + { + FlushInBuffer(); + rSh.AutoCorrect( *pACorr, u'\0' ); + } + eKeyState = eNextKeyState; + } + break; + + default: + { + sal_uInt16 nSlotId = 0; + FlushInBuffer(); + switch( eKeyState ) + { + case SwKeyState::SpecialInsert: + rSh.DoSpecialInsert(); + break; + + case SwKeyState::NoNum: + rSh.NoNum(); + break; + + case SwKeyState::NumOff: + // shell change - so record in advance + rSh.DelNumRules(); + break; + case SwKeyState::OutlineLvOff: // delete autofmt outlinelevel later + break; + + case SwKeyState::NumDown: + rSh.NumUpDown(); + m_nKS_NUMDOWN_Count = 2; + break; + case SwKeyState::NumUp: + rSh.NumUpDown( false ); + break; + + case SwKeyState::NumIndentInc: + rSh.ChangeIndentOfAllListLevels(360); + m_nKS_NUMINDENTINC_Count = 2; + break; + + case SwKeyState::GotoNextFieldMark: + { + ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkAfter(); + if(pFieldmark) rSh.GotoFieldmark(pFieldmark); + } + break; + + case SwKeyState::GotoPrevFieldMark: + { + ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkBefore(); + if( pFieldmark ) + rSh.GotoFieldmark(pFieldmark); + } + break; + + case SwKeyState::NumIndentDec: + rSh.ChangeIndentOfAllListLevels(-360); + break; + + case SwKeyState::OutlineDown: + rSh.OutlineUpDown(); + break; + case SwKeyState::OutlineUp: + rSh.OutlineUpDown( -1 ); + break; + + case SwKeyState::NextCell: + // always 'flush' in tables + rSh.GoNextCell(!rSh.HasReadonlySel()); + nSlotId = FN_GOTO_NEXT_CELL; + break; + case SwKeyState::PrevCell: + rSh.GoPrevCell(); + nSlotId = FN_GOTO_PREV_CELL; + break; + case SwKeyState::AutoFormatByInput: + rSh.SplitNode( true ); + break; + + case SwKeyState::NextObject: + case SwKeyState::PrevObject: + if(rSh.GotoObj( SwKeyState::NextObject == eKeyState, GotoObjFlags::Any)) + { + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + break; + case SwKeyState::GlossaryExpand: + { + // replace the word or abbreviation with the auto text + rSh.StartUndo( SwUndoId::START ); + + OUString sFnd(aTmpQHD.CurStr()); + if( aTmpQHD.m_bIsAutoText ) + { + SwGlossaryList* pList = ::GetGlossaryList(); + OUString sShrtNm; + OUString sGroup; + if(pList->GetShortName( sFnd, sShrtNm, sGroup)) + { + rSh.SttSelect(); + rSh.ExtendSelection(false, aTmpQHD.CurLen()); + SwGlossaryHdl* pGlosHdl = GetView().GetGlosHdl(); + pGlosHdl->SetCurGroup(sGroup, true); + pGlosHdl->InsertGlossary( sShrtNm); + m_pQuickHlpData->m_bAppendSpace = true; + } + } + else + { + sFnd = sFnd.copy(aTmpQHD.CurLen()); + rSh.Insert( sFnd ); + m_pQuickHlpData->m_bAppendSpace = !pACorr || + pACorr->GetSwFlags().bAutoCmpltAppendBlanc; + } + rSh.EndUndo( SwUndoId::END ); + } + break; + + case SwKeyState::NextPrevGlossary: + m_pQuickHlpData->Move( aTmpQHD ); + m_pQuickHlpData->Start(rSh, false); + break; + + case SwKeyState::EditFormula: + { + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + pVFrame->ToggleChildWindow( nId ); + SwInputChild* pChildWin = static_cast<SwInputChild*>(pVFrame-> + GetChildWindow( nId )); + if( pChildWin ) + pChildWin->SetFormula( sFormulaEntry ); + } + break; + + case SwKeyState::ColLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight, pModOpt->GetTableHMove() ); break; + case SwKeyState::ColBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::ColBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight, pModOpt->GetTableHMove() ); break; + case SwKeyState::CellTopBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellTopSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop, pModOpt->GetTableVMove() ); break; + case SwKeyState::CellBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom, pModOpt->GetTableVMove() ); break; + + case SwKeyState::Fly_Change: + { + SdrView *pSdrView = rSh.GetDrawView(); + const SdrHdlList& rHdlList = pSdrView->GetHdlList(); + if(rHdlList.GetFocusHdl()) + ChangeDrawing( nDir ); + else + ChangeFly( nDir, dynamic_cast<const SwWebView*>( &m_rView) != nullptr ); + } + break; + case SwKeyState::Draw_Change : + ChangeDrawing( nDir ); + break; + default: + break; + } + if( nSlotId && m_rView.GetViewFrame()->GetBindings().GetRecorder().is() ) + { + SfxRequest aReq(m_rView.GetViewFrame(), nSlotId ); + aReq.Done(); + } + eKeyState = SwKeyState::End; + } + } + } + + // update the page number in the statusbar + sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); + if( KEY_UP == nKey || KEY_DOWN == nKey || KEY_PAGEUP == nKey || KEY_PAGEDOWN == nKey ) + GetView().GetViewFrame()->GetBindings().Update( FN_STAT_PAGE ); + + // in case the buffered characters are inserted + if( bFlushBuffer && !m_aInBuffer.isEmpty() ) + { + // bFlushCharBuffer was not reset here + // why not? + bool bSave = g_bFlushCharBuffer; + FlushInBuffer(); + g_bFlushCharBuffer = bSave; + + // maybe show Tip-Help + if (bNormalChar) + { + const bool bAutoTextShown + = pACfg && pACfg->IsAutoTextTip() && ShowAutoText(rSh.GetChunkForAutoText()); + if (!bAutoTextShown && pACorr && pACorr->GetSwFlags().bAutoCompleteWords) + ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr); + } + } + + // get the word count dialog to update itself + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if( pWrdCnt ) + pWrdCnt->UpdateCounts(); + +} + +/** + * MouseEvents + */ +void SwEditWin::RstMBDownFlags() +{ + // Not on all systems a MouseButtonUp is used ahead + // of the modal dialog (like on WINDOWS). + // So reset the statuses here and release the mouse + // for the dialog. + m_bMBPressed = false; + g_bNoInterrupt = false; + EnterArea(); + ReleaseMouse(); +} + +/** + * Determines if the current position has a clickable url over a background + * frame. In that case, ctrl-click should select the url, not the frame. + */ +static bool lcl_urlOverBackground(SwWrtShell& rSh, const Point& rDocPos) +{ + SwContentAtPos aSwContentAtPos(IsAttrAtPos::InetAttr); + SdrObject* pSelectableObj = rSh.GetObjAt(rDocPos); + + return rSh.GetContentAtPos(rDocPos, aSwContentAtPos) && pSelectableObj->GetLayer() == rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId(); +} + +void SwEditWin::MoveCursor( SwWrtShell &rSh, const Point& rDocPos, + const bool bOnlyText, bool bLockView ) +{ + const bool bTmpNoInterrupt = g_bNoInterrupt; + g_bNoInterrupt = false; + + int nTmpSetCursor = 0; + + if( !rSh.IsViewLocked() && bLockView ) + rSh.LockView( true ); + else + bLockView = false; + + { + // only temporary generate move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + nTmpSetCursor = rSh.CallSetCursor(&rDocPos, bOnlyText); + g_bValidCursorPos = !(CRSR_POSCHG & nTmpSetCursor); + } + + // notify the edit window that from now on we do not use the input language + if ( !(CRSR_POSOLD & nTmpSetCursor) ) + SetUseInputLanguage( false ); + + if( bLockView ) + rSh.LockView( false ); + + g_bNoInterrupt = bTmpNoInterrupt; +} + +void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr; + + // We have to check if a context menu is shown and we have an UI + // active inplace client. In that case we have to ignore the mouse + // button down event. Otherwise we would crash (context menu has been + // opened by inplace client and we would deactivate the inplace client, + // the context menu is closed by VCL asynchronously which in the end + // would work on deleted objects or the context menu has no parent anymore) + SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + + if ( bIsOleActive && PopupMenu::IsInExecute() ) + return; + + MouseEvent rMEvt(_rMEvt); + + if (m_rView.GetPostItMgr()->IsHit(rMEvt.GetPosPixel())) + return; + + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) + { + pWindow->MouseButtonDown(rMEvt); + return; + } + } + + m_rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); + + GrabFocus(); + + //ignore key modifiers for format paintbrush + { + bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard + && m_pApplyTempl->m_pFormatClipboard->HasContent(); + if( bExecFormatPaintbrush ) + rMEvt = MouseEvent( _rMEvt.GetPosPixel(), _rMEvt.GetClicks(), + _rMEvt.GetMode(), _rMEvt.GetButtons() ); + } + + m_bWasShdwCursor = nullptr != m_pShadCursor; + m_pShadCursor.reset(); + + const Point aDocPos( PixelToLogic( rMEvt.GetPosPixel() ) ); + + FrameControlType eControl; + bool bOverFly = false; + bool bPageAnchored = false; + bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored ); + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly(); + if (bOverHeaderFooterFly && (!bIsDocReadOnly && rSh.GetCurField())) + // We have a field here, that should have priority over header/footer fly. + bOverHeaderFooterFly = false; + + // Are we clicking on a blank header/footer area? + if ( IsInHeaderFooter( aDocPos, eControl ) || bOverHeaderFooterFly ) + { + const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( aDocPos ); + + // Is it active? + bool bActive = true; + const SwPageDesc* pDesc = pPageFrame->GetPageDesc(); + + const SwFrameFormat* pFormat = pDesc->GetLeftFormat(); + if ( pPageFrame->OnRightPage() ) + pFormat = pDesc->GetRightFormat(); + + if ( pFormat ) + { + if ( eControl == FrameControlType::Header ) + bActive = pFormat->GetHeader().IsActive(); + else + bActive = pFormat->GetFooter().IsActive(); + } + + if ( !bActive ) + { + // When in Hide-Whitespace mode, we don't want header + // and footer controls. + if (!rSh.GetViewOptions()->IsHideWhitespaceMode()) + { + SwPaM aPam(*rSh.GetCurrentShellCursor().GetPoint()); + const bool bWasInHeader = aPam.GetPoint()->nNode.GetNode().FindHeaderStartNode() != nullptr; + const bool bWasInFooter = aPam.GetPoint()->nNode.GetNode().FindFooterStartNode() != nullptr; + + // Is the cursor in a part like similar to the one we clicked on? For example, + // if the cursor is in a header and we click on an empty header... don't change anything to + // keep consistent behaviour due to header edit mode (and the same for the footer as well). + + // Otherwise, we hide the header/footer control if a separator is shown, and vice versa. + if (!(bWasInHeader && eControl == FrameControlType::Header) && + !(bWasInFooter && eControl == FrameControlType::Footer)) + { + const bool bSeparatorWasVisible = rSh.IsShowHeaderFooterSeparator(eControl); + rSh.SetShowHeaderFooterSeparator(eControl, !bSeparatorWasVisible); + + // Repaint everything + Invalidate(); + + // tdf#84929. If the footer control had not been showing, do not change the cursor position, + // because the user may have scrolled to turn on the separator control and + // if the cursor cannot be positioned on-screen, then the user would need to scroll back again to use the control. + // This should only be done for the footer. The cursor can always be re-positioned near the header. tdf#134023. + if ( eControl == FrameControlType::Footer && !bSeparatorWasVisible + && rSh.GetViewOptions()->IsUseHeaderFooterMenu() && !Application::IsHeadlessModeEnabled() ) + return; + } + } + } + else + { + // Make sure we have the proper Header/Footer separators shown + // as these may be changed if clicking on an empty Header/Footer + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, eControl == FrameControlType::Header ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, eControl == FrameControlType::Footer ); + + if ( !rSh.IsHeaderFooterEdit() ) + { + rSh.ToggleHeaderFooterEdit(); + + // Repaint everything + rSh.GetWin()->Invalidate(); + } + } + } + else + { + if ( rSh.IsHeaderFooterEdit( ) ) + rSh.ToggleHeaderFooterEdit( ); + else + { + // Make sure that the separators are hidden + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, false ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, false ); + + // Repaint everything + // FIXME fdo#67358 for unknown reasons this causes painting + // problems when resizing table columns, so disable it +// rSh.GetWin()->Invalidate(); + } + + // Toggle Hide-Whitespace if between pages. + if (rSh.GetViewOptions()->CanHideWhitespace() && + rSh.GetLayout()->IsBetweenPages(aDocPos)) + { + if (_rMEvt.GetClicks() >= 2) + { + SwViewOption aOpt(*rSh.GetViewOptions()); + aOpt.SetHideWhitespaceMode(!aOpt.IsHideWhitespaceMode()); + rSh.ApplyViewOptions(aOpt); + } + + return; + } + } + + if ( IsChainMode() ) + { + SetChainMode( false ); + SwRect aDummy; + SwFlyFrameFormat *pFormat = static_cast<SwFlyFrameFormat*>(rSh.GetFlyFrameFormat()); + if ( rSh.Chainable( aDummy, *pFormat, aDocPos ) == SwChainRet::OK ) + rSh.Chain( *pFormat, aDocPos ); + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + return; + } + + // After GrabFocus a shell should be pushed. That should actually + // work but in practice ... + m_rView.SelectShellForDrop(); + + bool bCallBase = true; + + if( m_pQuickHlpData->m_bIsDisplayed ) + m_pQuickHlpData->Stop( rSh ); + m_pQuickHlpData->m_bAppendSpace = false; + + if( rSh.FinishOLEObj() ) + return; // end InPlace and the click doesn't count anymore + + SET_CURR_SHELL( &rSh ); + + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + if (pSdrView->MouseButtonDown( rMEvt, this ) ) + { + rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + return; // SdrView's event evaluated + } + } + + m_bIsInMove = false; + m_aStartPos = rMEvt.GetPosPixel(); + m_aRszMvHdlPt.setX( 0 ); + m_aRszMvHdlPt.setY( 0 ); + + SwTab nMouseTabCol = SwTab::COL_NONE; + const bool bTmp = !rSh.IsDrawCreate() && !m_pApplyTempl && !rSh.IsInSelect() && + rMEvt.GetClicks() == 1 && MOUSE_LEFT == rMEvt.GetButtons(); + if ( bTmp && + SwTab::COL_NONE != (nMouseTabCol = rSh.WhichMouseTabCol( aDocPos ) ) && + !rSh.IsObjSelectable( aDocPos ) ) + { + // Enhanced table selection + if ( SwTab::SEL_HORI <= nMouseTabCol && SwTab::COLSEL_VERT >= nMouseTabCol ) + { + rSh.EnterStdMode(); + rSh.SelectTableRowCol( aDocPos ); + if( SwTab::SEL_HORI != nMouseTabCol && SwTab::SEL_HORI_RTL != nMouseTabCol) + { + m_xRowColumnSelectionStart = aDocPos; + m_bIsRowDrag = SwTab::ROWSEL_HORI == nMouseTabCol|| + SwTab::ROWSEL_HORI_RTL == nMouseTabCol || + SwTab::COLSEL_VERT == nMouseTabCol; + m_bMBPressed = true; + CaptureMouse(); + } + return; + } + + if ( !rSh.IsTableMode() ) + { + // comes from table columns out of the document. + if(SwTab::COL_VERT == nMouseTabCol || SwTab::COL_HORI == nMouseTabCol) + m_rView.SetTabColFromDoc( true ); + else + m_rView.SetTabRowFromDoc( true ); + + m_rView.SetTabColFromDocPos( aDocPos ); + m_rView.InvalidateRulerPos(); + SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings(); + rBind.Update(); + if ( RulerColumnDrag( rMEvt, + (SwTab::COL_VERT == nMouseTabCol || SwTab::ROW_HORI == nMouseTabCol)) ) + { + m_rView.SetTabColFromDoc( false ); + m_rView.SetTabRowFromDoc( false ); + m_rView.InvalidateRulerPos(); + rBind.Update(); + bCallBase = false; + } + else + { + return; + } + } + } + else if (bTmp && + rSh.IsNumLabel(aDocPos)) + { + SwTextNode* pNodeAtPos = rSh.GetNumRuleNodeAtPos( aDocPos ); + m_rView.SetNumRuleNodeFromDoc( pNodeAtPos ); + m_rView.InvalidateRulerPos(); + SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings(); + rBind.Update(); + + if ( RulerMarginDrag( rMEvt, + SwFEShell::IsVerticalModeAtNdAndPos( *pNodeAtPos, aDocPos ) ) ) + { + m_rView.SetNumRuleNodeFromDoc( nullptr ); + m_rView.InvalidateRulerPos(); + rBind.Update(); + bCallBase = false; + } + else + { + // Make sure the pointer is set to 0, otherwise it may point to + // nowhere after deleting the corresponding text node. + m_rView.SetNumRuleNodeFromDoc( nullptr ); + return; + } + } + + if ( rSh.IsInSelect() ) + rSh.EndSelect(); + + // query against LEFT because otherwise for example also a right + // click releases the selection. + if ( MOUSE_LEFT == rMEvt.GetButtons() ) + { + bool bOnlyText = false; + m_bMBPressed = true; + g_bNoInterrupt = true; + m_nKS_NUMDOWN_Count = 0; + + CaptureMouse(); + + // reset cursor position if applicable + rSh.ResetCursorStack(); + + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_MOD2: + if( rSh.IsObjSelected() ) + { + SdrHdl* pHdl; + if( !bIsDocReadOnly && + !m_pAnchorMarker && + pSdrView && + nullptr != ( pHdl = pSdrView->PickHandle(aDocPos) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + // #i121463# Set selected during drag + pHdl->SetSelected(); + m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) ); + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + return; + } + } + if ( EnterDrawMode( rMEvt, aDocPos ) ) + { + g_bNoInterrupt = false; + return; + } + else if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + // Without SHIFT because otherwise Toggle doesn't work at selection + if (rMEvt.GetClicks() == 1) + { + if ( rSh.IsSelFrameMode()) + { + SdrHdl* pHdl = rSh.GetDrawView()->PickHandle(aDocPos); + bool bHitHandle = pHdl && pHdl->GetKind() != SdrHdlKind::Anchor && + pHdl->GetKind() != SdrHdlKind::Anchor_TR; + + if ((rSh.IsInsideSelectedObj(aDocPos) || bHitHandle) && + !(rMEvt.GetModifier() == KEY_SHIFT && !bHitHandle)) + { + rSh.EnterSelFrameMode( &aDocPos ); + if ( !m_pApplyTempl ) + { + // only if no position to size was hit. + if (!bHitHandle) + { + StartDDTimer(); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + } + g_bFrameDrag = true; + } + g_bNoInterrupt = false; + return; + } + } + } + } + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + SvtSecurityOptions aSecOpts; + const bool bSecureOption = aSecOpts.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ( ( bSecureOption && rMEvt.GetModifier() == KEY_MOD1 ) || + ( !bSecureOption && rMEvt.GetModifier() != KEY_MOD1 ) ) + bExecHyperlinks = true; + } + + // Enhanced selection + sal_uInt8 nNumberOfClicks = static_cast< sal_uInt8 >(rMEvt.GetClicks() % 4); + if ( 0 == nNumberOfClicks && 0 < rMEvt.GetClicks() ) + nNumberOfClicks = 4; + + bool bExecDrawTextLink = false; + + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + case MOUSE_LEFT + KEY_MOD1: + case MOUSE_LEFT + KEY_MOD2: + { + + // fdo#79604: first, check if a link has been clicked - do not + // select fly in this case! + if (1 == nNumberOfClicks) + { + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + + // hit a URL in DrawText object? + if (bExecHyperlinks && pSdrView) + { + SdrViewEvent aVEvt; + pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (aVEvt.eEvent == SdrEventKind::ExecuteUrl) + bExecDrawTextLink = true; + } + } + + if (1 == nNumberOfClicks && !bExecDrawTextLink) + { + // only try to select frame, if pointer already was + // switched accordingly + if ( m_aActHitType != SdrHitKind::NONE && !rSh.IsSelFrameMode() && + !GetView().GetViewFrame()->GetDispatcher()->IsLocked()) + { + // Test if there is a draw object at that position and if it should be selected. + bool bShould = rSh.ShouldObjectBeSelected(aDocPos); + + if(bShould) + { + m_rView.NoRotate(); + rSh.HideCursor(); + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + bool bSelObj = rSh.SelectObj( aDocPos, + rMEvt.IsMod1() ? SW_ENTER_GROUP : 0); + if( bUnLockView ) + rSh.LockView( false ); + + if( bSelObj ) + { + // if the frame was deselected in the macro + // the cursor just has to be displayed again + if( FrameTypeFlags::NONE == rSh.GetSelFrameType() ) + rSh.ShowCursor(); + else + { + if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + + rSh.EnterSelFrameMode( &aDocPos ); + g_bFrameDrag = true; + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + } + return; + } + else + bOnlyText = rSh.IsObjSelectable( aDocPos ); + + if (!m_rView.GetDrawFuncPtr()) + rSh.ShowCursor(); + } + else + bOnlyText = KEY_MOD1 != rMEvt.GetModifier(); + } + else if ( rSh.IsSelFrameMode() && + (m_aActHitType == SdrHitKind::NONE || + !rSh.IsInsideSelectedObj( aDocPos ))) + { + m_rView.NoRotate(); + SdrHdl *pHdl; + if( !bIsDocReadOnly && !m_pAnchorMarker && nullptr != + ( pHdl = pSdrView->PickHandle(aDocPos) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) ); + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + return; + } + else + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + sal_uInt8 nFlag = rMEvt.IsShift() ? SW_ADD_SELECT :0; + if( rMEvt.IsMod1() ) + nFlag = nFlag | SW_ENTER_GROUP; + + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + } + + bool bSelObj = rSh.SelectObj( aDocPos, nFlag ); + if( bUnLockView ) + rSh.LockView( false ); + + if( !bSelObj ) + { + // move cursor here so that it is not drawn in the + // frame first; ShowCursor() happens in LeaveSelFrameMode() + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + bCallBase = false; + } + else + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( &aDocPos ); + rSh.SelFlyGrabCursor(); + rSh.MakeSelVisible(); + g_bFrameDrag = true; + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + UpdatePointer( aDocPos, rMEvt.GetModifier() ); + return; + } + } + } + } + + switch ( nNumberOfClicks ) + { + case 1: + break; + case 2: + { + g_bFrameDrag = false; + if ( !bIsDocReadOnly && rSh.IsInsideSelectedObj(aDocPos) && + FlyProtectFlags::NONE == rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) ) + { + /* This is no good: on the one hand GetSelectionType is used as flag field + * (take a look into the GetSelectionType method) and on the other hand the + * return value is used in a switch without proper masking (very nice), this must lead to trouble + */ + switch ( rSh.GetSelectionType() & ~SelectionType( SelectionType::FontWork | SelectionType::ExtrudedCustomShape ) ) + { + case SelectionType::Graphic: + RstMBDownFlags(); + if (!comphelper::LibreOfficeKit::isActive()) + { + GetView().GetViewFrame()->GetBindings().Execute( + FN_FORMAT_GRAFIC_DLG, nullptr, + SfxCallMode::RECORD|SfxCallMode::SLOT); + } + return; + + // double click on OLE object --> OLE-InPlace + case SelectionType::Ole: + if (rSh.IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE) + { + RstMBDownFlags(); + rSh.LaunchOLEObj(); + } + return; + + case SelectionType::Frame: + RstMBDownFlags(); + if (!comphelper::LibreOfficeKit::isActive()) + { + GetView().GetViewFrame()->GetBindings().Execute( + FN_FORMAT_FRAME_DLG, nullptr, + SfxCallMode::RECORD|SfxCallMode::SLOT); + } + return; + + case SelectionType::DrawObject: + RstMBDownFlags(); + EnterDrawTextMode(aDocPos); + if ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) + static_cast<SwDrawTextShell*>(m_rView.GetCurShell())->Init(); + return; + + default: break; + } + } + + // if the cursor position was corrected or if a Fly + // was selected in ReadOnlyMode, no word selection, except when tiled rendering. + if ((!g_bValidCursorPos || rSh.IsFrameSelected()) && !comphelper::LibreOfficeKit::isActive()) + return; + + SwField *pField; + bool bFootnote = false; + + if( !bIsDocReadOnly && + ( nullptr != ( pField = rSh.GetCurField() ) || + ( bFootnote = rSh.GetCurFootnote() ) ) ) + { + RstMBDownFlags(); + if( bFootnote ) + GetView().GetViewFrame()->GetBindings().Execute( FN_EDIT_FOOTNOTE ); + else + { + SwFieldTypesEnum nTypeId = pField->GetTypeId(); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + switch( nTypeId ) + { + case SwFieldTypesEnum::Postit: + case SwFieldTypesEnum::Script: + { + // if it's a Readonly region, status has to be enabled + sal_uInt16 nSlot = SwFieldTypesEnum::Postit == nTypeId ? FN_POSTIT : FN_JAVAEDIT; + SfxBoolItem aItem(nSlot, true); + pVFrame->GetBindings().SetState(aItem); + pVFrame->GetBindings().Execute(nSlot); + break; + } + case SwFieldTypesEnum::Authority : + pVFrame->GetBindings().Execute(FN_EDIT_AUTH_ENTRY_DLG); + break; + case SwFieldTypesEnum::Input: + case SwFieldTypesEnum::Dropdown: + case SwFieldTypesEnum::SetInput: + pVFrame->GetBindings().Execute(FN_UPDATE_INPUTFIELDS); + break; + default: + pVFrame->GetBindings().Execute(FN_EDIT_FIELD); + } + } + return; + } + // in extended mode double and triple + // click has no effect. + if ( rSh.IsExtMode() || rSh.IsBlockMode() ) + return; + + // select word, AdditionalMode if applicable + if ( KEY_MOD1 == rMEvt.GetModifier() && !rSh.IsAddMode() ) + { + rSh.EnterAddMode(); + rSh.SelWrd( &aDocPos ); + rSh.LeaveAddMode(); + } + else + { + if (!rSh.SelWrd(&aDocPos) && comphelper::LibreOfficeKit::isActive()) + // Double click did not select any word: try to + // select the current cell in case we are in a + // table. + rSh.SelTableBox(); + } + + SwContentAtPos aContentAtPos(IsAttrAtPos::FormControl); + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) && + aContentAtPos.aFnd.pFieldmark != nullptr) + { + IFieldmark *pFieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark ); + if ( pFieldBM->GetFieldname( ) == ODF_FORMDROPDOWN || pFieldBM->GetFieldname( ) == ODF_FORMDATE ) + { + RstMBDownFlags(); + rSh.getIDocumentMarkAccess()->ClearFieldActivation(); + GetView().GetViewFrame()->GetBindings().Execute(SID_FM_CTL_PROPERTIES); + return; + } + } + + g_bHoldSelection = true; + return; + } + case 3: + case 4: + { + g_bFrameDrag = false; + // in extended mode double and triple + // click has no effect. + if ( rSh.IsExtMode() ) + return; + + // if the cursor position was corrected or if a Fly + // was selected in ReadOnlyMode, no word selection. + if ( !g_bValidCursorPos || rSh.IsFrameSelected() ) + return; + + // select line, AdditionalMode if applicable + const bool bMod = KEY_MOD1 == rMEvt.GetModifier() && + !rSh.IsAddMode(); + + if ( bMod ) + rSh.EnterAddMode(); + + // Enhanced selection + if ( 3 == nNumberOfClicks ) + rSh.SelSentence( &aDocPos ); + else + rSh.SelPara( &aDocPos ); + + if ( bMod ) + rSh.LeaveAddMode(); + + g_bHoldSelection = true; + return; + } + + default: + return; + } + + [[fallthrough]]; + } + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + { + bool bLockView = m_bWasShdwCursor; + + switch ( rMEvt.GetModifier() ) + { + case KEY_MOD1 + KEY_SHIFT: + { + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + rSh.SelectObj(aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP); + else + { if ( rSh.SelectObj( aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else if( rSh.IsSelFrameMode() && + rSh.GetDrawView()->PickHandle( aDocPos )) + { + g_bFrameDrag = true; + g_bNoInterrupt = false; + return; + } + } + break; + case KEY_MOD1: + if ( !bExecDrawTextLink ) + { + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) && !lcl_urlOverBackground( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + rSh.SelectObj(aDocPos, SW_ENTER_GROUP); + else + { if ( rSh.SelectObj( aDocPos, SW_ENTER_GROUP ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else if( rSh.IsSelFrameMode() && + rSh.GetDrawView()->PickHandle( aDocPos )) + { + g_bFrameDrag = true; + g_bNoInterrupt = false; + return; + } + else + { + if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() ) + { + rSh.PushMode(); + g_bModePushed = true; + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + rSh.EnterAddMode(); + if( bUnLockView ) + rSh.LockView( false ); + } + bCallBase = false; + } + } + break; + case KEY_MOD2: + { + if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() ) + { + rSh.PushMode(); + g_bModePushed = true; + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + rSh.EnterBlockMode(); + if( bUnLockView ) + rSh.LockView( false ); + } + bCallBase = false; + } + break; + case KEY_SHIFT: + { + if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) ) + { + m_rView.NoRotate(); + rSh.HideCursor(); + if ( rSh.IsSelFrameMode() ) + { + rSh.SelectObj(aDocPos, SW_ADD_SELECT); + + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (rMarkList.GetMark(0) == nullptr) + { + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + g_bFrameDrag = false; + } + } + else + { if ( rSh.SelectObj( aDocPos ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + } + } + else + { + if ( rSh.IsSelFrameMode() && + rSh.IsInsideSelectedObj( aDocPos ) ) + { + rSh.EnterSelFrameMode( &aDocPos ); + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + return; + } + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + g_bFrameDrag = false; + } + if ( !rSh.IsExtMode() ) + { + // don't start a selection when an + // URL field or a graphic is clicked + bool bSttSelect = rSh.HasSelection() || + PointerStyle::RefHand != GetPointer(); + + if( !bSttSelect ) + { + bSttSelect = true; + if( bExecHyperlinks ) + { + SwContentAtPos aContentAtPos( + IsAttrAtPos::Ftn | + IsAttrAtPos::InetAttr ); + + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) ) + { + if( !rSh.IsViewLocked() && + !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + bLockView = true; + + bSttSelect = false; + } + else if( rSh.IsURLGrfAtPos( aDocPos )) + bSttSelect = false; + } + } + + if( bSttSelect ) + rSh.SttSelect(); + } + } + bCallBase = false; + break; + } + default: + if( !rSh.IsViewLocked() ) + { + SwContentAtPos aContentAtPos( IsAttrAtPos::ClickField | + IsAttrAtPos::InetAttr ); + if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) && + !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + bLockView = true; + } + } + + if ( rSh.IsGCAttr() ) + { + rSh.GCAttr(); + rSh.ClearGCAttr(); + } + + SwContentAtPos aFieldAtPos(IsAttrAtPos::Field); + bool bEditableFieldClicked = false; + + // Are we clicking on a field? + if (rSh.GetContentAtPos(aDocPos, aFieldAtPos)) + { + bool bEditableField = (aFieldAtPos.pFndTextAttr != nullptr + && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD); + + if (!bEditableField) + { + rSh.CallSetCursor(&aDocPos, bOnlyText); + // Unfortunately the cursor may be on field + // position or on position after field depending on which + // half of the field was clicked on. + SwTextAttr const*const pTextField(aFieldAtPos.pFndTextAttr); + if (pTextField && rSh.GetCurrentShellCursor().GetPoint()->nContent + .GetIndex() != pTextField->GetStart()) + { + assert(rSh.GetCurrentShellCursor().GetPoint()->nContent + .GetIndex() == (pTextField->GetStart() + 1)); + rSh.Left( CRSR_SKIP_CHARS, false, 1, false ); + } + // don't go into the !bOverSelect block below - it moves + // the cursor + break; + } + else + { + bEditableFieldClicked = true; + } + } + + bool bOverSelect = rSh.TestCurrPam( aDocPos ); + bool bOverURLGrf = false; + if( !bOverSelect ) + bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos ); + + if ( !bOverSelect ) + { + MoveCursor( rSh, aDocPos, bOnlyText, bLockView ); + bCallBase = false; + } + if (!bOverURLGrf && !bExecDrawTextLink && !bOnlyText) + { + const SelectionType nSelType = rSh.GetSelectionType(); + // Check in general, if an object is selectable at given position. + // Thus, also text fly frames in background become selectable via Ctrl-Click. + if ( ( nSelType & SelectionType::Ole || + nSelType & SelectionType::Graphic || + rSh.IsObjSelectable( aDocPos ) ) && !lcl_urlOverBackground( rSh, aDocPos ) ) + { + SwMvContext aMvContext( &rSh ); + rSh.EnterSelFrameMode(); + bCallBase = false; + } + } + if ( !bOverSelect && bEditableFieldClicked && (!pCursorField || + pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField())) + { + // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART + // and CH_TXT_ATR_INPUTFIELDEND + rSh.SttSelect(); + rSh.SelectText( aFieldAtPos.pFndTextAttr->GetStart() + 1, + *(aFieldAtPos.pFndTextAttr->End()) - 1 ); + } + // don't reset here any longer so that, in case through MouseMove + // with pressed Ctrl key a multiple-selection should happen, + // the previous selection is not released in Drag. + break; + } + } + } + else if ( MOUSE_RIGHT == rMEvt.GetButtons() && !rMEvt.GetModifier() + && static_cast< sal_uInt8 >(rMEvt.GetClicks() % 4) == 1 + && !rSh.TestCurrPam( aDocPos ) ) + { + SwContentAtPos aFieldAtPos(IsAttrAtPos::Field); + + // Are we clicking on a field? + if (g_bValidCursorPos + && rSh.GetContentAtPos(aDocPos, aFieldAtPos) + && aFieldAtPos.pFndTextAttr != nullptr + && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD + && (!pCursorField || pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField())) + { + // Move the cursor + MoveCursor( rSh, aDocPos, rSh.IsObjSelectable( aDocPos ), m_bWasShdwCursor ); + bCallBase = false; + + // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART + // and CH_TXT_ATR_INPUTFIELDEND + rSh.SttSelect(); + rSh.SelectText( aFieldAtPos.pFndTextAttr->GetStart() + 1, + *(aFieldAtPos.pFndTextAttr->End()) - 1 ); + } + } + + if (bCallBase) + Window::MouseButtonDown(rMEvt); +} + +bool SwEditWin::changeMousePointer(Point const & rDocPoint) +{ + SwWrtShell & rShell = m_rView.GetWrtShell(); + + SwTab nMouseTabCol; + if ( SwTab::COL_NONE != (nMouseTabCol = rShell.WhichMouseTabCol( rDocPoint ) ) && + !rShell.IsObjSelectable( rDocPoint ) ) + { + PointerStyle nPointer = PointerStyle::Null; + bool bChkTableSel = false; + + switch ( nMouseTabCol ) + { + case SwTab::COL_VERT : + case SwTab::ROW_HORI : + nPointer = PointerStyle::VSizeBar; + bChkTableSel = true; + break; + case SwTab::ROW_VERT : + case SwTab::COL_HORI : + nPointer = PointerStyle::HSizeBar; + bChkTableSel = true; + break; + // Enhanced table selection + case SwTab::SEL_HORI : + nPointer = PointerStyle::TabSelectSE; + break; + case SwTab::SEL_HORI_RTL : + case SwTab::SEL_VERT : + nPointer = PointerStyle::TabSelectSW; + break; + case SwTab::COLSEL_HORI : + case SwTab::ROWSEL_VERT : + nPointer = PointerStyle::TabSelectS; + break; + case SwTab::ROWSEL_HORI : + nPointer = PointerStyle::TabSelectE; + break; + case SwTab::ROWSEL_HORI_RTL : + case SwTab::COLSEL_VERT : + nPointer = PointerStyle::TabSelectW; + break; + default: break; // prevent compiler warning + } + + if ( PointerStyle::Null != nPointer && + // i#35543 - Enhanced table selection is explicitly allowed in table mode + ( !bChkTableSel || !rShell.IsTableMode() ) && + !comphelper::LibreOfficeKit::isActive() ) + { + SetPointer( nPointer ); + } + + return true; + } + else if (rShell.IsNumLabel(rDocPoint, RULER_MOUSE_MARGINWIDTH)) + { + // i#42921 - consider vertical mode + SwTextNode* pNodeAtPos = rShell.GetNumRuleNodeAtPos( rDocPoint ); + const PointerStyle nPointer = + SwFEShell::IsVerticalModeAtNdAndPos( *pNodeAtPos, rDocPoint ) + ? PointerStyle::VSizeBar + : PointerStyle::HSizeBar; + SetPointer( nPointer ); + + return true; + } + return false; +} + +void SwEditWin::MouseMove(const MouseEvent& _rMEvt) +{ + MouseEvent rMEvt(_rMEvt); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) + { + pWindow->MouseMove(rMEvt); + return; + } + } + + //ignore key modifiers for format paintbrush + { + bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard + && m_pApplyTempl->m_pFormatClipboard->HasContent(); + if( bExecFormatPaintbrush ) + rMEvt = MouseEvent( _rMEvt.GetPosPixel(), _rMEvt.GetClicks(), + _rMEvt.GetMode(), _rMEvt.GetButtons() ); + } + + // as long as an action is running the MouseMove should be disconnected + // otherwise bug 40102 occurs + SwWrtShell &rSh = m_rView.GetWrtShell(); + if( rSh.ActionPend() ) + return ; + + if( m_pShadCursor && 0 != (rMEvt.GetModifier() + rMEvt.GetButtons() ) ) + { + m_pShadCursor.reset(); + } + + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly(); + + SET_CURR_SHELL( &rSh ); + + //aPixPt == Point in Pixel, relative to ChildWin + //aDocPt == Point in Twips, document coordinates + const Point aPixPt( rMEvt.GetPosPixel() ); + const Point aDocPt( PixelToLogic( aPixPt ) ); + + if ( IsChainMode() ) + { + UpdatePointer( aDocPt, rMEvt.GetModifier() ); + return; + } + + SdrView *pSdrView = rSh.GetDrawView(); + + const SwCallMouseEvent aLastCallEvent( m_aSaveCallEvent ); + m_aSaveCallEvent.Clear(); + + if ( !bIsDocReadOnly && pSdrView && pSdrView->MouseMove(rMEvt,this) ) + { + SetPointer( PointerStyle::Text ); + return; // evaluate SdrView's event + } + + const Point aOldPt( rSh.VisArea().Pos() ); + const bool bInsWin = rSh.VisArea().IsInside( aDocPt ) || comphelper::LibreOfficeKit::isActive(); + + if( m_pShadCursor && !bInsWin ) + { + m_pShadCursor.reset(); + } + + if( bInsWin && m_xRowColumnSelectionStart ) + { + EnterArea(); + Point aPos( aDocPt ); + if( rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag )) + return; + } + + // position is necessary for OS/2 because obviously after a MB-Down + // a MB-Move is called immediately. + if( g_bDDTimerStarted ) + { + Point aDD( SwEditWin::m_nDDStartPosX, SwEditWin::m_nDDStartPosY ); + aDD = LogicToPixel( aDD ); + tools::Rectangle aRect( aDD.X()-3, aDD.Y()-3, aDD.X()+3, aDD.Y()+3 ); + if ( !aRect.IsInside( aPixPt ) ) + StopDDTimer( &rSh, aDocPt ); + } + + if(m_rView.GetDrawFuncPtr()) + { + if( m_bInsDraw ) + { + m_rView.GetDrawFuncPtr()->MouseMove( rMEvt ); + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else + EnterArea(); + return; + } + else if(!rSh.IsFrameSelected() && !rSh.IsObjSelected()) + { + SfxBindings &rBnd = rSh.GetView().GetViewFrame()->GetBindings(); + Point aRelPos = rSh.GetRelativePagePosition(aDocPt); + if(aRelPos.X() >= 0) + { + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + const SfxPointItem aTmp1( SID_ATTR_POSITION, aRelPos ); + rBnd.SetState( aTmp1 ); + } + else + { + rBnd.Invalidate(SID_ATTR_POSITION); + } + rBnd.Invalidate(SID_ATTR_SIZE); + const SfxStringItem aCell( SID_TABLE_CELL, OUString() ); + rBnd.SetState( aCell ); + } + } + + // determine if we only change the mouse pointer and return + if (!bIsDocReadOnly && bInsWin && !m_pApplyTempl && !rSh.IsInSelect() && changeMousePointer(aDocPt)) + { + return; + } + + bool bDelShadCursor = true; + + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + if( m_pAnchorMarker ) + { + // Now we need to refresh the SdrHdl pointer of m_pAnchorMarker. + // This looks a little bit tricky, but it solves the following + // problem: the m_pAnchorMarker contains a pointer to an SdrHdl, + // if the FindAnchorPos-call cause a scrolling of the visible + // area, it's possible that the SdrHdl will be destroyed and a + // new one will initialized at the original position(GetHdlPos). + // So the m_pAnchorMarker has to find the right SdrHdl, if it's + // the old one, it will find it with position aOld, if this one + // is destroyed, it will find a new one at position GetHdlPos(). + + const Point aOld = m_pAnchorMarker->GetPosForHitTest( *(rSh.GetOut()) ); + Point aNew = rSh.FindAnchorPos( aDocPt ); + SdrHdl* pHdl; + if( pSdrView && (nullptr!=( pHdl = pSdrView->PickHandle( aOld ) )|| + nullptr !=(pHdl = pSdrView->PickHandle( m_pAnchorMarker->GetHdlPos()) ) ) && + ( pHdl->GetKind() == SdrHdlKind::Anchor || + pHdl->GetKind() == SdrHdlKind::Anchor_TR ) ) + { + m_pAnchorMarker->ChgHdl( pHdl ); + if( aNew.X() || aNew.Y() ) + { + m_pAnchorMarker->SetPos( aNew ); + m_pAnchorMarker->SetLastPos( aDocPt ); + } + } + else + { + m_pAnchorMarker.reset(); + } + } + if ( m_bInsDraw ) + { + if ( !m_bMBPressed ) + break; + if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) ) + { + if ( !bInsWin ) + LeaveArea( aDocPt ); + else + EnterArea(); + if ( m_rView.GetDrawFuncPtr() ) + { + pSdrView->SetOrtho(false); + m_rView.GetDrawFuncPtr()->MouseMove( rMEvt ); + } + m_bIsInMove = true; + } + return; + } + + { + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); + } + [[fallthrough]]; + + case MOUSE_LEFT + KEY_SHIFT: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + if ( !m_bMBPressed ) + break; + [[fallthrough]]; + case MOUSE_LEFT + KEY_MOD1: + if ( g_bFrameDrag && rSh.IsSelFrameMode() ) + { + if( !m_bMBPressed ) + break; + + if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) ) + { + // event processing for resizing + if (pSdrView && pSdrView->AreObjectsMarked()) + { + const Point aSttPt( PixelToLogic( m_aStartPos ) ); + + // can we start? + if( SdrHdlKind::User == g_eSdrMoveHdl ) + { + SdrHdl* pHdl = pSdrView->PickHandle( aSttPt ); + g_eSdrMoveHdl = pHdl ? pHdl->GetKind() : SdrHdlKind::Move; + } + + const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat()); + const SvxMacro* pMacro = nullptr; + + SvMacroItemId nEvent = SdrHdlKind::Move == g_eSdrMoveHdl + ? SvMacroItemId::SwFrmMove + : SvMacroItemId::SwFrmResize; + + if (nullptr != pFlyFormat) + pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent); + if (nullptr != pMacro && + // or notify only e.g. every 20 Twip? + m_aRszMvHdlPt != aDocPt ) + { + m_aRszMvHdlPt = aDocPt; + sal_uInt32 nPos = 0; + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put32( xVar.get(), ++nPos ); + + if( SvMacroItemId::SwFrmResize == nEvent ) + { + xVar = new SbxVariable; + xVar->PutUShort( static_cast< sal_uInt16 >(g_eSdrMoveHdl) ); + xArgs->Put32( xVar.get(), ++nPos ); + } + + xVar = new SbxVariable; + xVar->PutLong( aDocPt.X() - aSttPt.X() ); + xArgs->Put32( xVar.get(), ++nPos ); + xVar = new SbxVariable; + xVar->PutLong( aDocPt.Y() - aSttPt.Y() ); + xArgs->Put32( xVar.get(), ++nPos ); + + OUString sRet; + + ReleaseMouse(); + + rSh.ExecMacro( *pMacro, &sRet, xArgs.get() ); + + CaptureMouse(); + + if( !sRet.isEmpty() && sRet.toInt32()!=0 ) + return ; + } + } + // event processing for resizing + + if( bIsDocReadOnly ) + break; + + bool bResizeKeepRatio = rSh.GetSelectionType() & SelectionType::Graphic || + rSh.GetSelectionType() & SelectionType::Media || + rSh.GetSelectionType() & SelectionType::Ole; + bool bisResize = g_eSdrMoveHdl != SdrHdlKind::Move; + + if (pSdrView) + { + // Resize proportionally when media is selected and the user drags on a corner + const Point aSttPt(PixelToLogic(m_aStartPos)); + SdrHdl* pHdl = pSdrView->PickHandle(aSttPt); + if (pHdl) + bResizeKeepRatio = bResizeKeepRatio && pHdl->IsCornerHdl(); + + if (pSdrView->GetDragMode() == SdrDragMode::Crop) + bisResize = false; + if (rMEvt.IsShift()) + { + pSdrView->SetAngleSnapEnabled(!bResizeKeepRatio); + if (bisResize) + pSdrView->SetOrtho(!bResizeKeepRatio); + else + pSdrView->SetOrtho(true); + } + else + { + pSdrView->SetAngleSnapEnabled(bResizeKeepRatio); + if (bisResize) + pSdrView->SetOrtho(bResizeKeepRatio); + else + pSdrView->SetOrtho(false); + } + } + + rSh.Drag( &aDocPt, rMEvt.IsShift() ); + m_bIsInMove = true; + } + else if( bIsDocReadOnly ) + break; + + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else if(m_bIsInMove) + EnterArea(); + return; + } + if ( !rSh.IsSelFrameMode() && !g_bDDINetAttr && + (IsMinMove( m_aStartPos,aPixPt ) || m_bIsInMove) && + (rSh.IsInSelect() || !rSh.TestCurrPam( aDocPt )) ) + { + if ( pSdrView ) + { + if ( rMEvt.IsShift() ) + pSdrView->SetOrtho(true); + else + pSdrView->SetOrtho(false); + } + if ( !bInsWin ) + { + Point aTmp( aDocPt ); + aTmp += rSh.VisArea().Pos() - aOldPt; + LeaveArea( aTmp ); + } + else + { + if( !rMEvt.IsSynthetic() && + !(( MOUSE_LEFT + KEY_MOD1 == + rMEvt.GetModifier() + rMEvt.GetButtons() ) && + rSh.Is_FnDragEQBeginDrag() && !rSh.IsAddMode() )) + { + rSh.Drag( &aDocPt, false ); + + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false)); + EnterArea(); + } + } + } + g_bDDINetAttr = false; + break; + case 0: + { + if ( m_pApplyTempl ) + { + UpdatePointer(aDocPt); // maybe a frame has to be marked here + break; + } + // change ui if mouse is over SwPostItField + // TODO: do the same thing for redlines IsAttrAtPos::Redline + SwContentAtPos aContentAtPos( IsAttrAtPos::Field); + if (rSh.GetContentAtPos(aDocPt, aContentAtPos, false)) + { + const SwField* pField = aContentAtPos.aFnd.pField; + if (pField->Which()== SwFieldIds::Postit) + { + m_rView.GetPostItMgr()->SetShadowState(reinterpret_cast<const SwPostItField*>(pField),false); + } + else + m_rView.GetPostItMgr()->SetShadowState(nullptr,false); + } + else + m_rView.GetPostItMgr()->SetShadowState(nullptr,false); + [[fallthrough]]; + } + case KEY_SHIFT: + case KEY_MOD2: + case KEY_MOD1: + if ( !m_bInsDraw ) + { + bool bTstShdwCursor = true; + + UpdatePointer( aDocPt, rMEvt.GetModifier() ); + + const SwFrameFormat* pFormat = nullptr; + const SwFormatINetFormat* pINet = nullptr; + SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); + if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) ) + pINet = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr); + + const void* pTmp = pINet; + + if( pINet || + nullptr != ( pTmp = pFormat = rSh.GetFormatFromAnyObj( aDocPt ))) + { + bTstShdwCursor = false; + if( pTmp == pINet ) + m_aSaveCallEvent.Set( pINet ); + else + { + IMapObject* pIMapObj = pFormat->GetIMapObject( aDocPt ); + if( pIMapObj ) + m_aSaveCallEvent.Set( pFormat, pIMapObj ); + else + m_aSaveCallEvent.Set( EVENT_OBJECT_URLITEM, pFormat ); + } + + // should be over an InternetField with an + // embedded macro? + if( m_aSaveCallEvent != aLastCallEvent ) + { + if( aLastCallEvent.HasEvent() ) + rSh.CallEvent( SvMacroItemId::OnMouseOut, + aLastCallEvent, true ); + // 0 says that the object doesn't have any table + if( !rSh.CallEvent( SvMacroItemId::OnMouseOver, + m_aSaveCallEvent )) + m_aSaveCallEvent.Clear(); + } + } + else if( aLastCallEvent.HasEvent() ) + { + // cursor was on an object + rSh.CallEvent( SvMacroItemId::OnMouseOut, + aLastCallEvent, true ); + } + + if( bTstShdwCursor && bInsWin && !bIsDocReadOnly && + !m_bInsFrame && + !rSh.GetViewOptions()->getBrowseMode() && + rSh.GetViewOptions()->IsShadowCursor() && + !(rMEvt.GetModifier() + rMEvt.GetButtons()) && + !rSh.HasSelection() && !GetConnectMetaFile() ) + { + SwRect aRect; + sal_Int16 eOrient; + SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode(); + if( rSh.GetShadowCursorPos( aDocPt, eMode, aRect, eOrient )) + { + if( !m_pShadCursor ) + m_pShadCursor.reset( new SwShadowCursor( *this, + SwViewOption::GetDirectCursorColor() ) ); + if( text::HoriOrientation::RIGHT != eOrient && text::HoriOrientation::CENTER != eOrient ) + eOrient = text::HoriOrientation::LEFT; + m_pShadCursor->SetPos( aRect.Pos(), aRect.Height(), static_cast< sal_uInt16 >(eOrient) ); + bDelShadCursor = false; + } + } + } + break; + case MOUSE_LEFT + KEY_MOD2: + if( rSh.IsBlockMode() && !rMEvt.IsSynthetic() ) + { + rSh.Drag( &aDocPt, false ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false)); + EnterArea(); + } + break; + } + + if( bDelShadCursor && m_pShadCursor ) + { + m_pShadCursor.reset(); + } + m_bWasShdwCursor = false; +} + +/** + * Button Up + */ +void SwEditWin::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel())) + { + pWindow->MouseButtonUp(rMEvt); + return; + } + } + + bool bCallBase = true; + + bool bCallShadowCursor = m_bWasShdwCursor; + m_bWasShdwCursor = false; + if( m_pShadCursor ) + { + m_pShadCursor.reset(); + } + + m_xRowColumnSelectionStart.reset(); + + SdrHdlKind eOldSdrMoveHdl = g_eSdrMoveHdl; + g_eSdrMoveHdl = SdrHdlKind::User; // for MoveEvents - reset again + + // preventively reset + m_rView.SetTabColFromDoc( false ); + m_rView.SetNumRuleNodeFromDoc(nullptr); + + SwWrtShell &rSh = m_rView.GetWrtShell(); + SET_CURR_SHELL( &rSh ); + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + // tdf34555: ortho was always reset before being used in EndSdrDrag + // Now, it is reset only if not in Crop mode. + if (pSdrView->GetDragMode() != SdrDragMode::Crop && !rMEvt.IsShift()) + pSdrView->SetOrtho(false); + + if ( pSdrView->MouseButtonUp( rMEvt,this ) ) + { + rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + return; // SdrView's event evaluated + } + } + // only process MouseButtonUp when the Down went to that windows as well. + if ( !m_bMBPressed ) + { + // Undo for the watering can is already in CommandHdl + // that's the way it should be! + + return; + } + + Point aDocPt( PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( g_bDDTimerStarted ) + { + StopDDTimer( &rSh, aDocPt ); + m_bMBPressed = false; + if ( rSh.IsSelFrameMode() ) + { + rSh.EndDrag( &aDocPt, false ); + g_bFrameDrag = false; + } + g_bNoInterrupt = false; + const Point aDocPos( PixelToLogic( rMEvt.GetPosPixel() ) ); + if ((PixelToLogic(m_aStartPos).Y() == (aDocPos.Y())) && (PixelToLogic(m_aStartPos).X() == (aDocPos.X())))//To make sure it was not moved + { + SdrPageView* pPV = nullptr; + SdrObject* pObj = pSdrView ? pSdrView->PickObj(aDocPos, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER) : nullptr; + if (pObj) + { + SwFrameFormat* pFormat = GetUserCall(pObj)->GetFormat(); + SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT); + if (!pShapeFormat) + { + pSdrView->UnmarkAllObj(); + pSdrView->MarkObj(pObj,pPV); + } + else + { + // If the fly frame is a textbox of a shape, then select the shape instead. + SdrObject* pShape = pShapeFormat->FindSdrObject(); + pSdrView->UnmarkAllObj(); + pSdrView->MarkObj(pShape, pPV); + } + } + } + ReleaseMouse(); + return; + } + + if( m_pAnchorMarker ) + { + if(m_pAnchorMarker->GetHdl()) + { + // #i121463# delete selected after drag + m_pAnchorMarker->GetHdl()->SetSelected(false); + } + + Point aPnt( m_pAnchorMarker->GetLastPos() ); + m_pAnchorMarker.reset(); + if( aPnt.X() || aPnt.Y() ) + rSh.FindAnchorPos( aPnt, true ); + } + if ( m_bInsDraw && m_rView.GetDrawFuncPtr() ) + { + if ( m_rView.GetDrawFuncPtr()->MouseButtonUp( rMEvt ) ) + { + if (m_rView.GetDrawFuncPtr()) // could have been destroyed in MouseButtonUp + { + m_rView.GetDrawFuncPtr()->Deactivate(); + + if (!m_rView.IsDrawMode()) + { + m_rView.SetDrawFuncPtr(nullptr); + SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + rBind.Invalidate( SID_TABLE_CELL ); + } + } + + if ( rSh.IsObjSelected() ) + { + rSh.EnterSelFrameMode(); + if (!m_rView.GetDrawFuncPtr()) + StdDrawMode( OBJ_NONE, true ); + } + else if ( rSh.IsFrameSelected() ) + { + rSh.EnterSelFrameMode(); + StopInsFrame(); + } + else + { + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.Edit(); + } + + m_rView.AttrChangedNotify(nullptr); + } + else if (rMEvt.GetButtons() == MOUSE_RIGHT && rSh.IsDrawCreate()) + m_rView.GetDrawFuncPtr()->BreakCreate(); // abort drawing + + g_bNoInterrupt = false; + if (IsMouseCaptured()) + ReleaseMouse(); + return; + } + bool bPopMode = false; + switch ( rMEvt.GetModifier() + rMEvt.GetButtons() ) + { + case MOUSE_LEFT: + if ( m_bInsDraw && rSh.IsDrawCreate() ) + { + if ( m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->MouseButtonUp(rMEvt) ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.AttrChangedNotify(nullptr); + if ( rSh.IsObjSelected() ) + rSh.EnterSelFrameMode(); + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + StopInsFrame(); + } + bCallBase = false; + break; + } + [[fallthrough]]; + case MOUSE_LEFT + KEY_MOD1: + case MOUSE_LEFT + KEY_MOD2: + case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1: + if ( g_bFrameDrag && rSh.IsSelFrameMode() ) + { + if ( rMEvt.IsMod1() ) // copy and don't move. + { + // abort drag, use internal Copy instead + tools::Rectangle aRect; + rSh.GetDrawView()->TakeActionRect( aRect ); + if (!aRect.IsEmpty()) + { + rSh.BreakDrag(); + Point aEndPt, aSttPt; + if ( rSh.GetSelFrameType() & FrameTypeFlags::FLY_ATCNT ) + { + aEndPt = aRect.TopLeft(); + aSttPt = rSh.GetDrawView()->GetAllMarkedRect().TopLeft(); + } + else + { + aEndPt = aRect.Center(); + aSttPt = rSh.GetDrawView()->GetAllMarkedRect().Center(); + } + if ( aSttPt != aEndPt ) + { + rSh.StartUndo( SwUndoId::UI_DRAG_AND_COPY ); + rSh.Copy(&rSh, aSttPt, aEndPt); + rSh.EndUndo( SwUndoId::UI_DRAG_AND_COPY ); + } + } + else { + rSh.EndDrag( &aDocPt, false ); + } + } + else + { + { + const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat()); + const SvxMacro* pMacro = nullptr; + + SvMacroItemId nEvent = SdrHdlKind::Move == eOldSdrMoveHdl + ? SvMacroItemId::SwFrmMove + : SvMacroItemId::SwFrmResize; + + if (nullptr != pFlyFormat) + pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent); + if (nullptr != pMacro) + { + const Point aSttPt( PixelToLogic( m_aStartPos ) ); + m_aRszMvHdlPt = aDocPt; + sal_uInt32 nPos = 0; + SbxArrayRef xArgs = new SbxArray; + SbxVariableRef xVar = new SbxVariable; + xVar->PutString( pFlyFormat->GetName() ); + xArgs->Put32( xVar.get(), ++nPos ); + + if( SvMacroItemId::SwFrmResize == nEvent ) + { + xVar = new SbxVariable; + xVar->PutUShort( static_cast< sal_uInt16 >(eOldSdrMoveHdl) ); + xArgs->Put32( xVar.get(), ++nPos ); + } + + xVar = new SbxVariable; + xVar->PutLong( aDocPt.X() - aSttPt.X() ); + xArgs->Put32( xVar.get(), ++nPos ); + xVar = new SbxVariable; + xVar->PutLong( aDocPt.Y() - aSttPt.Y() ); + xArgs->Put32( xVar.get(), ++nPos ); + + xVar = new SbxVariable; + xVar->PutUShort( 1 ); + xArgs->Put32( xVar.get(), ++nPos ); + + ReleaseMouse(); + + rSh.ExecMacro( *pMacro, nullptr, xArgs.get() ); + + CaptureMouse(); + } + } + rSh.EndDrag( &aDocPt, false ); + } + g_bFrameDrag = false; + bCallBase = false; + break; + } + bPopMode = true; + [[fallthrough]]; + case MOUSE_LEFT + KEY_SHIFT: + if (rSh.IsSelFrameMode()) + { + + rSh.EndDrag( &aDocPt, false ); + g_bFrameDrag = false; + bCallBase = false; + break; + } + + if( g_bHoldSelection ) + { + // the EndDrag should be called in any case + g_bHoldSelection = false; + rSh.EndDrag( &aDocPt, false ); + } + else + { + SwContentAtPos aFieldAtPos ( IsAttrAtPos::Field ); + if ( !rSh.IsInSelect() && rSh.TestCurrPam( aDocPt ) && + !rSh.GetContentAtPos( aDocPt, aFieldAtPos ) ) + { + const bool bTmpNoInterrupt = g_bNoInterrupt; + g_bNoInterrupt = false; + { // create only temporary move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + } + g_bNoInterrupt = bTmpNoInterrupt; + + } + else + { + bool bInSel = rSh.IsInSelect(); + rSh.EndDrag( &aDocPt, false ); + + // Internetfield? --> call link (load doc!!) + if( !bInSel ) + { + LoadUrlFlags nFilter = LoadUrlFlags::NONE; + if( KEY_MOD1 == rMEvt.GetModifier() ) + nFilter |= LoadUrlFlags::NewView; + + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + SvtSecurityOptions aSecOpts; + const bool bSecureOption = aSecOpts.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink ); + if ( ( bSecureOption && rMEvt.GetModifier() == KEY_MOD1 ) || + ( !bSecureOption && rMEvt.GetModifier() != KEY_MOD1 ) ) + bExecHyperlinks = true; + } + + const bool bExecSmarttags = rMEvt.GetModifier() == KEY_MOD1; + + if(m_pApplyTempl) + bExecHyperlinks = false; + + SwContentAtPos aContentAtPos( IsAttrAtPos::Field | + IsAttrAtPos::InetAttr | + IsAttrAtPos::SmartTag | IsAttrAtPos::FormControl); + + if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) ) + { + // Do it again if we're not on a field/hyperlink to update the cursor accordingly + if ( IsAttrAtPos::Field != aContentAtPos.eContentAtPos + && IsAttrAtPos::InetAttr != aContentAtPos.eContentAtPos ) + rSh.GetContentAtPos( aDocPt, aContentAtPos, true ); + + bool bViewLocked = rSh.IsViewLocked(); + if( !bViewLocked && !rSh.IsReadOnlyAvailable() && + aContentAtPos.IsInProtectSect() ) + rSh.LockView( true ); + + ReleaseMouse(); + + if( IsAttrAtPos::Field == aContentAtPos.eContentAtPos ) + { + bool bAddMode(false); + // AdditionalMode if applicable + if (KEY_MOD1 == rMEvt.GetModifier() + && !rSh.IsAddMode()) + { + bAddMode = true; + rSh.EnterAddMode(); + } + if ( aContentAtPos.pFndTextAttr != nullptr + && aContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD ) + { + if (!rSh.IsInSelect()) + { + // create only temporary move context because otherwise + // the query to the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + const Point aDocPos( PixelToLogic( m_aStartPos ) ); + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + } + else + { + g_bValidCursorPos = true; + } + } + else + { + rSh.ClickToField( *aContentAtPos.aFnd.pField ); + // a bit of a mystery what this is good for? + // in this case we assume it's valid since we + // just selected a field + g_bValidCursorPos = true; + } + if (bAddMode) + { + rSh.LeaveAddMode(); + } + } + else if ( IsAttrAtPos::SmartTag == aContentAtPos.eContentAtPos ) + { + // execute smarttag menu + if ( bExecSmarttags && SwSmartTagMgr::Get().IsSmartTagsEnabled() ) + m_rView.ExecSmartTagPopup( aDocPt ); + } + else if ( IsAttrAtPos::FormControl == aContentAtPos.eContentAtPos ) + { + OSL_ENSURE( aContentAtPos.aFnd.pFieldmark != nullptr, "where is my field ptr???"); + if ( aContentAtPos.aFnd.pFieldmark != nullptr) + { + IFieldmark *fieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark ); + if ( fieldBM->GetFieldname( ) == ODF_FORMCHECKBOX ) + { + ICheckboxFieldmark& rCheckboxFm = dynamic_cast<ICheckboxFieldmark&>(*fieldBM); + rCheckboxFm.SetChecked(!rCheckboxFm.IsChecked()); + rCheckboxFm.Invalidate(); + rSh.InvalidateWindows( m_rView.GetVisArea() ); + } + } + } + else if ( IsAttrAtPos::InetAttr == aContentAtPos.eContentAtPos ) + { + if (comphelper::LibreOfficeKit::isActive()) + { + OUString val((*static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)).GetValue()); + if (val.startsWith("#")) + bExecHyperlinks = true; + } + if ( bExecHyperlinks && aContentAtPos.aFnd.pAttr ) + rSh.ClickToINetAttr( *static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr), nFilter ); + } + + rSh.LockView( bViewLocked ); + bCallShadowCursor = false; + } + else + { + aContentAtPos = SwContentAtPos( IsAttrAtPos::Ftn ); + if( !rSh.GetContentAtPos( aDocPt, aContentAtPos, true ) && bExecHyperlinks ) + { + SdrViewEvent aVEvt; + + if (pSdrView) + pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (pSdrView && aVEvt.eEvent == SdrEventKind::ExecuteUrl) + { + // hit URL field + const SvxURLField *pField = aVEvt.pURLField; + if (pField) + { + const OUString& sURL(pField->GetURL()); + const OUString& sTarget(pField->GetTargetFrame()); + ::LoadURL(rSh, sURL, nFilter, sTarget); + } + bCallShadowCursor = false; + } + else + { + // hit graphic + ReleaseMouse(); + if( rSh.ClickToINetGrf( aDocPt, nFilter )) + bCallShadowCursor = false; + } + } + } + + if( bCallShadowCursor && + rSh.GetViewOptions()->IsShadowCursor() && + MOUSE_LEFT == (rMEvt.GetModifier() + rMEvt.GetButtons()) && + !rSh.HasSelection() && + !GetConnectMetaFile() && + rSh.VisArea().IsInside( aDocPt )) + { + SwUndoId nLastUndoId(SwUndoId::EMPTY); + if (rSh.GetLastUndoInfo(nullptr, & nLastUndoId)) + { + if (SwUndoId::INS_FROM_SHADOWCRSR == nLastUndoId) + { + rSh.Undo(); + } + } + SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode(); + rSh.SetShadowCursorPos( aDocPt, eMode ); + } + } + } + bCallBase = false; + + } + + // reset pushed mode in Down again if applicable + if ( bPopMode && g_bModePushed ) + { + rSh.PopMode(); + g_bModePushed = false; + bCallBase = false; + } + break; + + default: + ReleaseMouse(); + return; + } + + if( m_pApplyTempl ) + { + SelectionType eSelection = rSh.GetSelectionType(); + SwFormatClipboard* pFormatClipboard = m_pApplyTempl->m_pFormatClipboard; + if( pFormatClipboard )//apply format paintbrush + { + //get some parameters + SwWrtShell& rWrtShell = m_rView.GetWrtShell(); + SfxStyleSheetBasePool* pPool=nullptr; + bool bNoCharacterFormats = false; + bool bNoParagraphFormats = true; + { + SwDocShell* pDocSh = m_rView.GetDocShell(); + if(pDocSh) + pPool = pDocSh->GetStyleSheetPool(); + if( (rMEvt.GetModifier()&KEY_MOD1) && (rMEvt.GetModifier()&KEY_SHIFT) ) + { + bNoCharacterFormats = true; + bNoParagraphFormats = false; + } + else if( rMEvt.GetModifier() & KEY_MOD1 ) + bNoParagraphFormats = false; + } + //execute paste + pFormatClipboard->Paste( rWrtShell, pPool, bNoCharacterFormats, bNoParagraphFormats ); + + //if the clipboard is empty after paste remove the ApplyTemplate + if(!pFormatClipboard->HasContent()) + SetApplyTemplate(SwApplyTemplate()); + + //tdf#38101 remove temporary highlighting + m_pUserMarker.reset(); + } + else if( m_pApplyTempl->nColor ) + { + sal_uInt16 nId = 0; + switch( m_pApplyTempl->nColor ) + { + case SID_ATTR_CHAR_COLOR_EXT: + nId = RES_CHRATR_COLOR; + break; + case SID_ATTR_CHAR_COLOR_BACKGROUND_EXT: + nId = RES_CHRATR_BACKGROUND; + break; + } + if( nId && (SelectionType::Text|SelectionType::Table) & eSelection) + { + if( rSh.IsSelection() && !rSh.HasReadonlySel() ) + { + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + if (nId == RES_CHRATR_BACKGROUND) + ApplyCharBackground(m_aWaterCanTextBackColor, rSh); + else + rSh.SetAttrItem( SvxColorItem( m_aWaterCanTextColor, nId ) ); + rSh.UnSetVisibleCursor(); + rSh.EnterStdMode(); + rSh.SetVisibleCursor(aDocPt); + bCallBase = false; + m_aTemplateTimer.Stop(); + } + else if(rMEvt.GetClicks() == 1) + { + // no selection -> so turn off watering can + m_aTemplateTimer.Start(); + } + } + } + else + { + OUString aStyleName; + switch ( m_pApplyTempl->eType ) + { + case SfxStyleFamily::Para: + if( (( SelectionType::Text | SelectionType::Table ) + & eSelection ) && !rSh.HasReadonlySel() ) + { + rSh.SetTextFormatColl( m_pApplyTempl->aColl.pTextColl ); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if ( m_pApplyTempl->aColl.pTextColl ) + aStyleName = m_pApplyTempl->aColl.pTextColl->GetName(); + } + break; + case SfxStyleFamily::Char: + if( (( SelectionType::Text | SelectionType::Table ) + & eSelection ) && !rSh.HasReadonlySel() ) + { + rSh.SetAttrItem( SwFormatCharFormat(m_pApplyTempl->aColl.pCharFormat) ); + rSh.UnSetVisibleCursor(); + rSh.EnterStdMode(); + rSh.SetVisibleCursor(aDocPt); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if ( m_pApplyTempl->aColl.pCharFormat ) + aStyleName = m_pApplyTempl->aColl.pCharFormat->GetName(); + } + break; + case SfxStyleFamily::Frame : + { + const SwFrameFormat* pFormat = rSh.GetFormatFromObj( aDocPt ); + if(dynamic_cast<const SwFlyFrameFormat*>( pFormat) ) + { + rSh.SetFrameFormat( m_pApplyTempl->aColl.pFrameFormat, false, &aDocPt ); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + if( m_pApplyTempl->aColl.pFrameFormat ) + aStyleName = m_pApplyTempl->aColl.pFrameFormat->GetName(); + } + break; + } + case SfxStyleFamily::Page: + // no Undo with page templates + rSh.ChgCurPageDesc( *m_pApplyTempl->aColl.pPageDesc ); + if ( m_pApplyTempl->aColl.pPageDesc ) + aStyleName = m_pApplyTempl->aColl.pPageDesc->GetName(); + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + bCallBase = false; + break; + case SfxStyleFamily::Pseudo: + if( !rSh.HasReadonlySel() ) + { + rSh.SetCurNumRule( *m_pApplyTempl->aColl.pNumRule, + false, + m_pApplyTempl->aColl.pNumRule->GetDefaultListId() ); + bCallBase = false; + m_pApplyTempl->nUndo = + std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()); + if( m_pApplyTempl->aColl.pNumRule ) + aStyleName = m_pApplyTempl->aColl.pNumRule->GetName(); + } + break; + default: break; + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame()->GetBindings().GetRecorder(); + if ( !aStyleName.isEmpty() && xRecorder.is() ) + { + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + if ( pSfxShell ) + { + SfxRequest aReq( m_rView.GetViewFrame(), SID_STYLE_APPLY ); + aReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aStyleName ) ); + aReq.AppendItem( SfxUInt16Item( SID_STYLE_FAMILY, static_cast<sal_uInt16>(m_pApplyTempl->eType) ) ); + aReq.Done(); + } + } + } + + } + ReleaseMouse(); + // Only processed MouseEvents arrive here; only at these this mode can + // be reset. + m_bMBPressed = false; + + // Make this call just to be sure. Selecting has finished surely by now. + // Otherwise the timeout's timer could give problems. + EnterArea(); + g_bNoInterrupt = false; + + if (bCallBase) + Window::MouseButtonUp(rMEvt); + + if (pSdrView && rMEvt.GetClicks() == 1 && comphelper::LibreOfficeKit::isActive()) + { + // When tiled rendering, single click on a shape text starts editing already. + SdrViewEvent aViewEvent; + SdrHitKind eHit = pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aViewEvent); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (eHit == SdrHitKind::TextEditObj && rMarkList.GetMarkCount() == 1) + { + if (SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj()) + { + EnterDrawTextMode(pObj->GetLogicRect().Center()); + if ( dynamic_cast< const SwDrawTextShell *>( m_rView.GetCurShell() ) != nullptr ) + static_cast<SwDrawTextShell*>(m_rView.GetCurShell())->Init(); + } + } + } +} + +/** + * Apply template + */ +void SwEditWin::SetApplyTemplate(const SwApplyTemplate &rTempl) +{ + static bool bIdle = false; + m_pApplyTempl.reset(); + SwWrtShell &rSh = m_rView.GetWrtShell(); + + if(rTempl.m_pFormatClipboard) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill );//@todo #i20119# maybe better a new brush pointer here in future + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else if(rTempl.nColor) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill ); + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else if( rTempl.eType != SfxStyleFamily::None ) + { + m_pApplyTempl.reset(new SwApplyTemplate( rTempl )); + m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount(); + SetPointer( PointerStyle::Fill ); + rSh.NoEdit( false ); + bIdle = rSh.GetViewOptions()->IsIdle(); + rSh.GetViewOptions()->SetIdle( false ); + } + else + { + SetPointer( PointerStyle::Text ); + rSh.UnSetVisibleCursor(); + + rSh.GetViewOptions()->SetIdle( bIdle ); + if ( !rSh.IsSelFrameMode() ) + rSh.Edit(); + } + + static sal_uInt16 aInva[] = + { + SID_STYLE_WATERCAN, + SID_ATTR_CHAR_COLOR_EXT, + SID_ATTR_CHAR_COLOR_BACKGROUND_EXT, + 0 + }; + m_rView.GetViewFrame()->GetBindings().Invalidate(aInva); +} + +/** + * Ctor + */ +SwEditWin::SwEditWin(vcl::Window *pParent, SwView &rMyView): + Window(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + DropTargetHelper( this ), + DragSourceHelper( this ), + + m_eBufferLanguage(LANGUAGE_DONTKNOW), + m_pUserMarkerObj( nullptr ), + + m_rView( rMyView ), + + m_aActHitType(SdrHitKind::NONE), + m_nDropFormat( SotClipboardFormatId::NONE ), + m_nDropAction( 0 ), + m_nDropDestination( SotExchangeDest::NONE ), + + m_eBezierMode(SID_BEZIER_INSERT), + m_nInsFrameColCount( 1 ), + m_eDrawMode(OBJ_NONE), + + m_bMBPressed(false), + m_bInsDraw(false), + m_bInsFrame(false), + m_bIsInMove(false), + m_bIsInDrag(false), + m_bOldIdle(false), + m_bOldIdleSet(false), + m_bChainMode(false), + m_bWasShdwCursor(false), + m_bLockInput(false), + m_bIsRowDrag(false), + m_bUseInputLanguage(false), + m_bObjectSelect(false), + m_nKS_NUMDOWN_Count(0), + m_nKS_NUMINDENTINC_Count(0), + m_pFrameControlsManager(new SwFrameControlsManager(this)) +{ + set_id("writer_edit"); + SetHelpId(HID_EDIT_WIN); + EnableChildTransparentMode(); + SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus ); + + m_bMBPressed = m_bInsDraw = m_bInsFrame = + m_bIsInDrag = m_bOldIdle = m_bOldIdleSet = m_bChainMode = m_bWasShdwCursor = false; + // initially use the input language + m_bUseInputLanguage = true; + + SetMapMode(MapMode(MapUnit::MapTwip)); + + SetPointer( PointerStyle::Text ); + m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, TimerHandler)); + + m_aKeyInputFlushTimer.SetTimeout( 200 ); + m_aKeyInputFlushTimer.SetInvokeHandler(LINK(this, SwEditWin, KeyInputFlushHandler)); + + // TemplatePointer for colors should be reset without + // selection after single click, but not after double-click (tdf#122442) + m_aTemplateTimer.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime()); + m_aTemplateTimer.SetInvokeHandler(LINK(this, SwEditWin, TemplateTimerHdl)); + + // temporary solution!!! Should set the font of the current + // insert position at every cursor movement! + if( !rMyView.GetDocShell()->IsReadOnly() ) + { + vcl::Font aFont; + SetInputContext( InputContext( aFont, InputContextFlags::Text | + InputContextFlags::ExtText ) ); + } +} + +SwEditWin::~SwEditWin() +{ + disposeOnce(); +} + +void SwEditWin::dispose() +{ + m_pShadCursor.reset(); + + if( m_pQuickHlpData->m_bIsDisplayed && m_rView.GetWrtShellPtr() ) + m_pQuickHlpData->Stop( m_rView.GetWrtShell() ); + g_bExecuteDrag = false; + m_pApplyTempl.reset(); + + m_rView.SetDrawFuncPtr(nullptr); + + m_pUserMarker.reset(); + + m_pAnchorMarker.reset(); + + m_pFrameControlsManager->dispose(); + m_pFrameControlsManager.reset(); + + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + vcl::Window::dispose(); +} + +/** + * Turn on DrawTextEditMode + */ +void SwEditWin::EnterDrawTextMode( const Point& aDocPos ) +{ + if ( m_rView.EnterDrawTextMode(aDocPos) ) + { + if (m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + } + m_rView.NoRotate(); + m_rView.AttrChangedNotify(nullptr); + } +} + +/** + * Turn on DrawMode + */ +bool SwEditWin::EnterDrawMode(const MouseEvent& rMEvt, const Point& aDocPos) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SdrView *pSdrView = rSh.GetDrawView(); + + if ( m_rView.GetDrawFuncPtr() ) + { + if (rSh.IsDrawCreate()) + return true; + + bool bRet = m_rView.GetDrawFuncPtr()->MouseButtonDown( rMEvt ); + m_rView.AttrChangedNotify(nullptr); + return bRet; + } + + if ( pSdrView && pSdrView->IsTextEdit() ) + { + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + + rSh.EndTextEdit(); // clicked aside, end Edit + rSh.SelectObj( aDocPos ); + if ( !rSh.IsObjSelected() && !rSh.IsFrameSelected() ) + rSh.LeaveSelFrameMode(); + else + { + SwEditWin::m_nDDStartPosY = aDocPos.Y(); + SwEditWin::m_nDDStartPosX = aDocPos.X(); + g_bFrameDrag = true; + } + if( bUnLockView ) + rSh.LockView( false ); + m_rView.AttrChangedNotify(nullptr); + return true; + } + return false; +} + +bool SwEditWin::IsDrawSelMode() const +{ + return IsObjectSelect(); +} + +void SwEditWin::GetFocus() +{ + if ( m_rView.GetPostItMgr()->HasActiveSidebarWin() ) + { + m_rView.GetPostItMgr()->GrabFocusOnActiveSidebarWin(); + } + else + { + m_rView.GotFocus(); + Window::GetFocus(); + m_rView.GetWrtShell().InvalidateAccessibleFocus(); + } +} + +void SwEditWin::LoseFocus() +{ + if (m_rView.GetWrtShellPtr()) + m_rView.GetWrtShell().InvalidateAccessibleFocus(); + Window::LoseFocus(); + if( m_pQuickHlpData && m_pQuickHlpData->m_bIsDisplayed ) + m_pQuickHlpData->Stop( m_rView.GetWrtShell() ); +} + +void SwEditWin::Command( const CommandEvent& rCEvt ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + + if ( !m_rView.GetViewFrame() ) + { + // If ViewFrame dies shortly, no popup anymore! + Window::Command(rCEvt); + return; + } + + // The command event is send to the window after a possible context + // menu from an inplace client has been closed. Now we have the chance + // to deactivate the inplace client without any problem regarding parent + // windows and code on the stack. + SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + if ( bIsOleActive && ( rCEvt.GetCommand() == CommandEventId::ContextMenu )) + { + rSh.FinishOLEObj(); + return; + } + + bool bCallBase = true; + + switch ( rCEvt.GetCommand() ) + { + case CommandEventId::ContextMenu: + { + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + SwInputChild* pChildWin = static_cast<SwInputChild*>(GetView().GetViewFrame()-> + GetChildWindow( nId )); + + if (m_rView.GetPostItMgr()->IsHit(rCEvt.GetMousePosPixel())) + return; + + Point aDocPos( PixelToLogic( rCEvt.GetMousePosPixel() ) ); + if ( !rCEvt.IsMouseEvent() ) + aDocPos = rSh.GetCharRect().Center(); + + // Don't trigger the command on a frame anchored to header/footer is not editing it + FrameControlType eControl; + bool bOverFly = false; + bool bPageAnchored = false; + bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored ); + // !bOverHeaderFooterFly doesn't mean we have a frame to select + if ( !bPageAnchored && rCEvt.IsMouseEvent( ) && + ( ( rSh.IsHeaderFooterEdit( ) && !bOverHeaderFooterFly && bOverFly ) || + ( !rSh.IsHeaderFooterEdit( ) && bOverHeaderFooterFly ) ) ) + { + return; + } + + if((!pChildWin || pChildWin->GetView() != &m_rView) && + !rSh.IsDrawCreate() && !IsDrawAction()) + { + SET_CURR_SHELL( &rSh ); + if (!m_pApplyTempl) + { + if (g_bNoInterrupt) + { + ReleaseMouse(); + g_bNoInterrupt = false; + m_bMBPressed = false; + } + if ( rCEvt.IsMouseEvent() ) + { + SelectMenuPosition(rSh, rCEvt.GetMousePosPixel()); + m_rView.StopShellTimer(); + } + const Point aPixPos = LogicToPixel( aDocPos ); + + if ( m_rView.GetDocShell()->IsReadOnly() ) + { + SwReadOnlyPopup aROPopup(aDocPos, m_rView); + + ui::ContextMenuExecuteEvent aEvent; + aEvent.SourceWindow = VCLUnoHelper::GetInterface( this ); + aEvent.ExecutePosition.X = aPixPos.X(); + aEvent.ExecutePosition.Y = aPixPos.Y(); + ScopedVclPtr<Menu> pMenu; + if (GetView().TryContextMenuInterception(aROPopup.GetMenu(), "private:resource/ReadonlyContextMenu", pMenu, aEvent)) + { + if ( pMenu ) + { + sal_uInt16 nExecId = static_cast<PopupMenu*>(pMenu.get())->Execute(this, aPixPos); + if( !::ExecuteMenuCommand( *static_cast<PopupMenu*>(pMenu.get()), *m_rView.GetViewFrame(), nExecId )) + aROPopup.Execute(this, nExecId); + } + else + aROPopup.Execute(this, aPixPos); + } + } + else if ( !m_rView.ExecSpellPopup( aDocPos ) ) + SfxDispatcher::ExecutePopup(this, &aPixPos); + } + else if (m_pApplyTempl->nUndo < rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()) + { + // Undo until we reach the point when we entered this context. + rSh.Do(SwWrtShell::UNDO); + } + bCallBase = false; + } + } + break; + + case CommandEventId::Wheel: + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + m_pShadCursor.reset(); + bCallBase = !m_rView.HandleWheelCommands( rCEvt ); + break; + + case CommandEventId::LongPress: + case CommandEventId::Swipe: //nothing yet + break; + + case CommandEventId::StartExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if(!bIsDocReadOnly) + { + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + if( rSh.HasSelection() ) + rSh.DelRight(); + + bCallBase = false; + LanguageType eInputLanguage = GetInputLanguage(); + rSh.CreateExtTextInput(eInputLanguage); + } + } + break; + } + case CommandEventId::EndExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if(!bIsDocReadOnly) + { + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + bCallBase = false; + OUString sRecord = rSh.DeleteExtTextInput(); + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame()->GetBindings().GetRecorder(); + + if ( !sRecord.isEmpty() ) + { + // convert quotes in IME text + // works on the last input character, this is especially in Korean text often done + // quotes that are inside of the string are not replaced! + const sal_Unicode aCh = sRecord[sRecord.getLength() - 1]; + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect(); + if(pACorr && + (( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))|| + ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh)))) + { + rSh.DelLeft(); + rSh.AutoCorrect( *pACorr, aCh ); + } + + if ( xRecorder.is() ) + { + // determine Shell + SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView ); + // generate request and record + if (pSfxShell) + { + SfxRequest aReq( m_rView.GetViewFrame(), FN_INSERT_STRING ); + aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, sRecord ) ); + aReq.Done(); + } + } + } + } + } + } + break; + case CommandEventId::ExtTextInput: + { + bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() && + rSh.IsCursorReadonly(); + if(!bIsDocReadOnly) + { + if( m_pQuickHlpData->m_bIsDisplayed ) + m_pQuickHlpData->Stop( rSh ); + + if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() ) + { + bCallBase = false; + rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt ); + } + else + { + const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); + if( pData ) + { + bCallBase = false; + rSh.SetExtTextInputData( *pData ); + } + } + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_rView.GetViewFrame()->GetBindings().GetRecorder(); + if(!xRecorder.is()) + { + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + if (!rACfg.IsAutoTextTip() || !ShowAutoText(rSh.GetChunkForAutoText())) + { + SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect(); + if (pACorr && pACorr->GetSwFlags().bAutoCompleteWords) + ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr); + } + } + } + } + break; + case CommandEventId::CursorPos: + // will be handled by the base class + break; + + case CommandEventId::PasteSelection: + if( !m_rView.GetDocShell()->IsReadOnly() ) + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSelection( this )); + if( !aDataHelper.GetXTransferable().is() ) + break; + + SotExchangeDest nDropDestination = GetDropDestination( rCEvt.GetMousePosPixel() ); + if( !bool(nDropDestination) ) + break; + SotClipboardFormatId nDropFormat; + sal_uInt8 nEventAction, nDropAction; + SotExchangeActionFlags nActionFlags; + nDropAction = SotExchange::GetExchangeAction( + aDataHelper.GetDataFlavorExVector(), + nDropDestination, EXCHG_IN_ACTION_COPY, + EXCHG_IN_ACTION_COPY, nDropFormat, + nEventAction, + SotClipboardFormatId::NONE, nullptr, + &nActionFlags ); + if( EXCHG_INOUT_ACTION_NONE != nDropAction ) + { + const Point aDocPt( PixelToLogic( rCEvt.GetMousePosPixel() ) ); + SwTransferable::PasteData( aDataHelper, rSh, nDropAction, nActionFlags, + nDropFormat, nDropDestination, false, + false, &aDocPt, EXCHG_IN_ACTION_COPY, + true ); + } + } + break; + case CommandEventId::ModKeyChange : + { + const CommandModKeyData* pCommandData = rCEvt.GetModKeyData(); + if (!pCommandData->IsDown() && pCommandData->IsMod1() && !pCommandData->IsMod2()) + { + sal_uInt16 nSlot = 0; + if(pCommandData->IsLeftShift() && !pCommandData->IsRightShift()) + nSlot = SID_ATTR_PARA_LEFT_TO_RIGHT; + else if(!pCommandData->IsLeftShift() && pCommandData->IsRightShift()) + nSlot = SID_ATTR_PARA_RIGHT_TO_LEFT; + if(nSlot && SW_MOD()->GetCTLOptions().IsCTLFontEnabled()) + GetView().GetViewFrame()->GetDispatcher()->Execute(nSlot); + } + } + break; + case CommandEventId::InputLanguageChange : + // i#42732 - update state of fontname if input language changes + g_bInputLanguageSwitched = true; + SetUseInputLanguage( true ); + break; + case CommandEventId::SelectionChange: + { + const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData(); + rSh.SttCursorMove(); + rSh.GoStartSentence(); + rSh.GetCursor()->GetPoint()->nContent += sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetStart()); + rSh.SetMark(); + rSh.GetCursor()->GetMark()->nContent += sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetEnd() - pData->GetStart()); + rSh.EndCursorMove( true ); + } + break; + case CommandEventId::PrepareReconversion: + if( rSh.HasSelection() ) + { + SwPaM *pCursor = rSh.GetCursor(); + + if( rSh.IsMultiSelection() ) + { + if (pCursor && !pCursor->HasMark() && + pCursor->GetPoint() == pCursor->GetMark()) + { + rSh.GoPrevCursor(); + pCursor = rSh.GetCursor(); + } + + // Cancel all selections other than the last selected one. + while( rSh.GetCursor()->GetNext() != rSh.GetCursor() ) + delete rSh.GetCursor()->GetNext(); + } + + if( pCursor ) + { + sal_uLong nPosNodeIdx = pCursor->GetPoint()->nNode.GetIndex(); + const sal_Int32 nPosIdx = pCursor->GetPoint()->nContent.GetIndex(); + sal_uLong nMarkNodeIdx = pCursor->GetMark()->nNode.GetIndex(); + const sal_Int32 nMarkIdx = pCursor->GetMark()->nContent.GetIndex(); + + if( !rSh.GetCursor()->HasMark() ) + rSh.GetCursor()->SetMark(); + + rSh.SttCursorMove(); + + if( nPosNodeIdx < nMarkNodeIdx ) + { + rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx; + rSh.GetCursor()->GetPoint()->nContent = nPosIdx; + rSh.GetCursor()->GetMark()->nNode = nPosNodeIdx; + rSh.GetCursor()->GetMark()->nContent = + rSh.GetCursor()->GetContentNode()->Len(); + } + else if( nPosNodeIdx == nMarkNodeIdx ) + { + rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx; + rSh.GetCursor()->GetPoint()->nContent = nPosIdx; + rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx; + rSh.GetCursor()->GetMark()->nContent = nMarkIdx; + } + else + { + rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx; + rSh.GetCursor()->GetMark()->nContent = nMarkIdx; + rSh.GetCursor()->GetPoint()->nNode = nMarkNodeIdx; + rSh.GetCursor()->GetPoint()->nContent = + rSh.GetCursor()->GetContentNode( false )->Len(); + } + + rSh.EndCursorMove( true ); + } + } + break; + case CommandEventId::QueryCharPosition: + { + bool bVertical = rSh.IsInVerticalText(); + const SwPosition& rPos = *rSh.GetCursor()->GetPoint(); + SwDocShell* pDocSh = m_rView.GetDocShell(); + SwDoc *pDoc = pDocSh->GetDoc(); + SwExtTextInput* pInput = pDoc->GetExtTextInput( rPos.nNode.GetNode(), rPos.nContent.GetIndex() ); + if ( pInput ) + { + const SwPosition& rStart = *pInput->Start(); + const SwPosition& rEnd = *pInput->End(); + int nSize = 0; + for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex ) + { + ++nSize; + } + vcl::Window& rWin = rSh.GetView().GetEditWin(); + if ( nSize == 0 ) + { + // When the composition does not exist, use Caret rect instead. + const SwRect& aCaretRect ( rSh.GetCharRect() ); + tools::Rectangle aRect( aCaretRect.Left(), aCaretRect.Top(), aCaretRect.Right(), aCaretRect.Bottom() ); + rWin.SetCompositionCharRect( &aRect, 1, bVertical ); + } + else + { + std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ nSize ]); + int nRectIndex = 0; + for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex ) + { + const SwPosition aPos( rStart.nNode, nIndex ); + SwRect aRect ( rSh.GetCharRect() ); + rSh.GetCharRectAt( aRect, &aPos ); + aRects[ nRectIndex ] = tools::Rectangle( aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom() ); + ++nRectIndex; + } + rWin.SetCompositionCharRect( aRects.get(), nSize, bVertical ); + } + } + bCallBase = false; + } + break; + default: + SAL_WARN("sw.ui", "unknown command."); + break; + } + if (bCallBase) + Window::Command(rCEvt); +} + +/* i#18686 select the object/cursor at the mouse + position of the context menu request */ +void SwEditWin::SelectMenuPosition(SwWrtShell& rSh, const Point& rMousePos ) +{ + const Point aDocPos( PixelToLogic( rMousePos ) ); + const bool bIsInsideSelectedObj( rSh.IsInsideSelectedObj( aDocPos ) ); + //create a synthetic mouse event out of the coordinates + MouseEvent aMEvt(rMousePos); + SdrView *pSdrView = rSh.GetDrawView(); + if ( pSdrView ) + { + // no close of insert_draw and reset of + // draw mode, if context menu position is inside a selected object. + if ( !bIsInsideSelectedObj && m_rView.GetDrawFuncPtr() ) + { + + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + rBind.Invalidate( SID_TABLE_CELL ); + } + + // if draw text is active and there's a text selection + // at the mouse position then do nothing + if(rSh.GetSelectionType() & SelectionType::DrawObjectEditMode) + { + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + ESelection aSelection = pOLV->GetSelection(); + if(!aSelection.IsZero()) + { + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + bool bVertical = pOutliner->IsVertical(); + const EditEngine& rEditEng = pOutliner->GetEditEngine(); + Point aEEPos(aDocPos); + const tools::Rectangle& rOutputArea = pOLV->GetOutputArea(); + // regard vertical mode + if(bVertical) + { + aEEPos -= rOutputArea.TopRight(); + //invert the horizontal direction and exchange X and Y + long nTemp = -aEEPos.X(); + aEEPos.setX( aEEPos.Y() ); + aEEPos.setY( nTemp ); + } + else + aEEPos -= rOutputArea.TopLeft(); + + EPosition aDocPosition = rEditEng.FindDocPosition(aEEPos); + ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex); + // make it a forward selection - otherwise the IsLess/IsGreater do not work :-( + aSelection.Adjust(); + if(!(aCompare < aSelection) && !(aCompare > aSelection)) + { + return; + } + } + + } + + if (pSdrView->MouseButtonDown( aMEvt, this ) ) + { + pSdrView->MouseButtonUp( aMEvt, this ); + rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + return; + } + } + rSh.ResetCursorStack(); + + if ( EnterDrawMode( aMEvt, aDocPos ) ) + { + return; + } + if ( m_rView.GetDrawFuncPtr() && m_bInsFrame ) + { + StopInsFrame(); + rSh.Edit(); + } + + UpdatePointer( aDocPos ); + + if( !rSh.IsSelFrameMode() && + !GetView().GetViewFrame()->GetDispatcher()->IsLocked() ) + { + // Test if there is a draw object at that position and if it should be selected. + bool bShould = rSh.ShouldObjectBeSelected(aDocPos); + + if(bShould) + { + m_rView.NoRotate(); + rSh.HideCursor(); + + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + bool bSelObj = rSh.SelectObj( aDocPos ); + if( bUnLockView ) + rSh.LockView( false ); + + if( bSelObj ) + { + // in case the frame was deselected in the macro + // just the cursor has to be displayed again. + if( FrameTypeFlags::NONE == rSh.GetSelFrameType() ) + rSh.ShowCursor(); + else + { + if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr()) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + + rSh.EnterSelFrameMode( &aDocPos ); + g_bFrameDrag = true; + UpdatePointer( aDocPos ); + return; + } + } + + if (!m_rView.GetDrawFuncPtr()) + rSh.ShowCursor(); + } + } + else if ( rSh.IsSelFrameMode() && + (m_aActHitType == SdrHitKind::NONE || + !bIsInsideSelectedObj)) + { + m_rView.NoRotate(); + bool bUnLockView = !rSh.IsViewLocked(); + rSh.LockView( true ); + + if ( rSh.IsSelFrameMode() ) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + m_rView.AttrChangedNotify(nullptr); + } + + bool bSelObj = rSh.SelectObj( aDocPos, 0/*nFlag*/ ); + if( bUnLockView ) + rSh.LockView( false ); + + if( !bSelObj ) + { + // move cursor here so that it is not drawn in the + // frame at first; ShowCursor() happens in LeaveSelFrameMode() + g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false)); + rSh.LeaveSelFrameMode(); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + else + { + rSh.HideCursor(); + rSh.EnterSelFrameMode( &aDocPos ); + rSh.SelFlyGrabCursor(); + rSh.MakeSelVisible(); + g_bFrameDrag = true; + if( rSh.IsFrameSelected() && + m_rView.GetDrawFuncPtr() ) + { + m_rView.GetDrawFuncPtr()->Deactivate(); + m_rView.SetDrawFuncPtr(nullptr); + m_rView.LeaveDrawCreate(); + m_rView.AttrChangedNotify(nullptr); + } + UpdatePointer( aDocPos ); + } + } + else if ( rSh.IsSelFrameMode() && bIsInsideSelectedObj ) + { + // Object at the mouse cursor is already selected - do nothing + return; + } + + if ( rSh.IsGCAttr() ) + { + rSh.GCAttr(); + rSh.ClearGCAttr(); + } + + bool bOverSelect = rSh.TestCurrPam( aDocPos ); + bool bOverURLGrf = false; + if( !bOverSelect ) + bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos ); + + if ( !bOverSelect ) + { + // create only temporary move context because otherwise + // the query against the content form doesn't work!!! + SwMvContext aMvContext( &rSh ); + rSh.CallSetCursor(&aDocPos, false); + } + if( !bOverURLGrf ) + { + const SelectionType nSelType = rSh.GetSelectionType(); + if( nSelType == SelectionType::Ole || + nSelType == SelectionType::Graphic ) + { + SwMvContext aMvContext( &rSh ); + if( !rSh.IsFrameSelected() ) + rSh.GotoNextFly(); + rSh.EnterSelFrameMode(); + } + } +} + +static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView ) +{ + // determine Shell + SfxShell* pShell; + SfxDispatcher* pDispatcher = rView.GetViewFrame()->GetDispatcher(); + for(sal_uInt16 i = 0; true; ++i ) + { + pShell = pDispatcher->GetShell( i ); + if( !pShell || dynamic_cast< const SwTextShell *>( pShell ) != nullptr ) + break; + } + return pShell; +} + +IMPL_LINK_NOARG(SwEditWin, KeyInputFlushHandler, Timer *, void) +{ + FlushInBuffer(); +} + +void SwEditWin::InitStaticData() +{ + m_pQuickHlpData = new QuickHelpData(); +} + +void SwEditWin::FinitStaticData() +{ + delete m_pQuickHlpData; +} +/* i#3370 - remove quick help to prevent saving + * of autocorrection suggestions */ +void SwEditWin::StopQuickHelp() +{ + if( HasFocus() && m_pQuickHlpData && m_pQuickHlpData->m_bIsDisplayed ) + m_pQuickHlpData->Stop( m_rView.GetWrtShell() ); +} + +IMPL_LINK_NOARG(SwEditWin, TemplateTimerHdl, Timer *, void) +{ + SetApplyTemplate(SwApplyTemplate()); +} + +void SwEditWin::SetChainMode( bool bOn ) +{ + if ( !m_bChainMode ) + StopInsFrame(); + + m_pUserMarker.reset(); + + m_bChainMode = bOn; + + static sal_uInt16 aInva[] = + { + FN_FRAME_CHAIN, FN_FRAME_UNCHAIN, 0 + }; + m_rView.GetViewFrame()->GetBindings().Invalidate(aInva); +} + +uno::Reference< css::accessibility::XAccessible > SwEditWin::CreateAccessible() +{ + SolarMutexGuard aGuard; // this should have happened already!!! + SwWrtShell *pSh = m_rView.GetWrtShellPtr(); + OSL_ENSURE( pSh, "no writer shell, no accessible object" ); + uno::Reference< + css::accessibility::XAccessible > xAcc; + if( pSh ) + xAcc = pSh->CreateAccessible(); + + return xAcc; +} + +void QuickHelpData::Move( QuickHelpData& rCpy ) +{ + m_aHelpStrings.clear(); + m_aHelpStrings.swap( rCpy.m_aHelpStrings ); + + m_bIsDisplayed = rCpy.m_bIsDisplayed; + nCurArrPos = rCpy.nCurArrPos; + m_bAppendSpace = rCpy.m_bAppendSpace; + m_bIsTip = rCpy.m_bIsTip; + m_bIsAutoText = rCpy.m_bIsAutoText; +} + +void QuickHelpData::ClearContent() +{ + nCurArrPos = nNoPos; + m_bIsDisplayed = m_bAppendSpace = false; + nTipId = nullptr; + m_aHelpStrings.clear(); + m_bIsTip = true; + m_bIsAutoText = true; +} + +void QuickHelpData::Start(SwWrtShell& rSh, const bool bRestart) +{ + if (bRestart) + { + nCurArrPos = 0; + } + m_bIsDisplayed = true; + + vcl::Window& rWin = rSh.GetView().GetEditWin(); + if( m_bIsTip ) + { + Point aPt( rWin.OutputToScreenPixel( rWin.LogicToPixel( + rSh.GetCharRect().Pos() ))); + aPt.AdjustY( -3 ); + nTipId = Help::ShowPopover(&rWin, tools::Rectangle( aPt, Size( 1, 1 )), + CurStr(), + QuickHelpFlags::Left | QuickHelpFlags::Bottom); + } + else + { + OUString sStr(CurStr()); + sStr = sStr.copy(CurLen()); + sal_uInt16 nL = sStr.getLength(); + const ExtTextInputAttr nVal = ExtTextInputAttr::DottedUnderline | + ExtTextInputAttr::Highlight; + const std::vector<ExtTextInputAttr> aAttrs( nL, nVal ); + CommandExtTextInputData aCETID( sStr, aAttrs.data(), nL, + 0, false ); + + //fdo#33092. If the current input language is the default + //language that text would appear in if typed, then don't + //force a language on for the ExtTextInput. + LanguageType eInputLanguage = rWin.GetInputLanguage(); + if (lcl_isNonDefaultLanguage(eInputLanguage, + rSh.GetView(), sStr) == INVALID_HINT) + { + eInputLanguage = LANGUAGE_DONTKNOW; + } + + rSh.CreateExtTextInput(eInputLanguage); + rSh.SetExtTextInputData( aCETID ); + } +} + +void QuickHelpData::Stop( SwWrtShell& rSh ) +{ + if( !m_bIsTip ) + rSh.DeleteExtTextInput( false ); + else if( nTipId ) + { + vcl::Window& rWin = rSh.GetView().GetEditWin(); + Help::HidePopover(&rWin, nTipId); + } + ClearContent(); +} + +void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord ) +{ + enum Capitalization { CASE_LOWER, CASE_UPPER, CASE_SENTENCE, CASE_OTHER }; + + // Determine word capitalization + const CharClass& rCC = GetAppCharClass(); + const OUString sWordLower = rCC.lowercase( rWord ); + Capitalization aWordCase = CASE_OTHER; + if ( !rWord.isEmpty() ) + { + if ( rWord[0] == sWordLower[0] ) + { + if ( rWord == sWordLower ) + aWordCase = CASE_LOWER; + } + else + { + // First character is not lower case i.e. assume upper or title case + OUString sWordSentence = sWordLower.replaceAt( 0, 1, OUString(rWord[0]) ); + if ( rWord == sWordSentence ) + aWordCase = CASE_SENTENCE; + else + { + if ( rWord == rCC.uppercase( rWord ) ) + aWordCase = CASE_UPPER; + } + } + } + + salhelper::SingletonRef<SwCalendarWrapper>* pCalendar = s_getCalendarWrapper(); + (*pCalendar)->LoadDefaultCalendar( rSh.GetCurLang() ); + + // Add matching calendar month and day names + for ( const auto& aNames : { (*pCalendar)->getMonths(), (*pCalendar)->getDays() } ) + { + for ( const auto& rName : aNames ) + { + const OUString& rStr( rName.FullName ); + // Check string longer than word and case insensitive match + if( rStr.getLength() > rWord.getLength() && + rCC.lowercase( rStr, 0, rWord.getLength() ) == sWordLower ) + { + OUString sStr; + + //fdo#61251 if it's an exact match, ensure unchanged replacement + //exists as a candidate + if (rStr.startsWith(rWord)) + m_aHelpStrings.emplace_back(rStr, rWord.getLength()); + else + sStr = rStr; // to be added if no case conversion is performed below + + if ( aWordCase == CASE_LOWER ) + sStr = rCC.lowercase(rStr); + else if ( aWordCase == CASE_SENTENCE ) + sStr = rCC.lowercase(rStr).replaceAt(0, 1, OUString(rStr[0])); + else if ( aWordCase == CASE_UPPER ) + sStr = rCC.uppercase(rStr); + + if (!sStr.isEmpty()) + m_aHelpStrings.emplace_back(sStr, rWord.getLength()); + } + } + } + + // Add matching current date in ISO 8601 format, for example 2016-01-30 + OUString rStrToday; + + // do not suggest for single years, for example for "2016", + // only for "201" or "2016-..." (to avoid unintentional text + // insertion at line ending, for example typing "30 January 2016") + if (!rWord.isEmpty() && rWord.getLength() != 4 && rWord[0] == '2') + { + rStrToday = utl::toISO8601(DateTime(Date(Date::SYSTEM)).GetUNODateTime()); + if (rStrToday.startsWith(rWord)) + m_aHelpStrings.emplace_back(rStrToday, rWord.getLength()); + } + + // Add matching words from AutoCompleteWord list + const SwAutoCompleteWord& rACList = SwEditShell::GetAutoCompleteWords(); + std::vector<OUString> strings; + + if ( rACList.GetWordsMatching( rWord, strings ) ) + { + for (const OUString & aCompletedString : strings) + { + // when we have a matching current date, avoid to suggest + // other words with the same matching starting characters, + // for example 2016-01-3 instead of 2016-01-30 + if (!rStrToday.isEmpty() && aCompletedString.startsWith(rWord)) + continue; + + OUString sStr; + + //fdo#61251 if it's an exact match, ensure unchanged replacement + //exists as a candidate + if (aCompletedString.startsWith(rWord)) + m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength()); + else + sStr = aCompletedString; // to be added if no case conversion is performed below + + if (aWordCase == CASE_LOWER) + sStr = rCC.lowercase(aCompletedString); + else if (aWordCase == CASE_SENTENCE) + sStr = rCC.lowercase(aCompletedString) + .replaceAt(0, 1, OUString(aCompletedString[0])); + else if (aWordCase == CASE_UPPER) + sStr = rCC.uppercase(aCompletedString); + + if (!sStr.isEmpty()) + m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength()); + } + } +} + +namespace { + +class CompareIgnoreCaseAsciiFavorExact +{ + const OUString &m_rOrigWord; +public: + explicit CompareIgnoreCaseAsciiFavorExact(const OUString& rOrigWord) + : m_rOrigWord(rOrigWord) + { + } + + bool operator()(const std::pair<OUString, sal_uInt16>& s1, + const std::pair<OUString, sal_uInt16>& s2) const + { + int nRet = s1.first.compareToIgnoreAsciiCase(s2.first); + if (nRet == 0) + { + //fdo#61251 sort stuff that starts with the exact rOrigWord before + //another ignore-case candidate + int n1StartsWithOrig = s1.first.startsWith(m_rOrigWord) ? 0 : 1; + int n2StartsWithOrig = s2.first.startsWith(m_rOrigWord) ? 0 : 1; + return n1StartsWithOrig < n2StartsWithOrig; + } + return nRet < 0; + } +}; + +struct EqualIgnoreCaseAscii +{ + bool operator()(const std::pair<OUString, sal_uInt16>& s1, + const std::pair<OUString, sal_uInt16>& s2) const + { + return s1.first.equalsIgnoreAsciiCase(s2.first); + } +}; + +} // anonymous namespace + +// TODO Implement an i18n aware sort +void QuickHelpData::SortAndFilter(const OUString &rOrigWord) +{ + std::sort( m_aHelpStrings.begin(), + m_aHelpStrings.end(), + CompareIgnoreCaseAsciiFavorExact(rOrigWord) ); + + const auto& it + = std::unique(m_aHelpStrings.begin(), m_aHelpStrings.end(), EqualIgnoreCaseAscii()); + m_aHelpStrings.erase( it, m_aHelpStrings.end() ); + + nCurArrPos = 0; +} + +// For a given chunk of typed text between 3 and 9 characters long that may start at a word boundary +// or in a whitespace and may include whitespaces, SwEditShell::GetChunkForAutoTextcreates a list of +// possible candidates for long AutoText names. Let's say, we have typed text "lorem ipsum dr f"; +// and the cursor is right after the "f". SwEditShell::GetChunkForAutoText would take " dr f", +// since it's the longest chunk to the left of the cursor no longer than 9 characters, not starting +// in the middle of a word. Then it would create this list from it (in this order, longest first): +// " dr f" +// " dr f" +// "dr f" +// It cannot add "r f", because it starts in the middle of the word "dr"; also it cannot give " f", +// because it's only 2 characters long. +// Now the result of SwEditShell::GetChunkForAutoText is passed here to SwEditWin::ShowAutoText, and +// then to SwGlossaryList::HasLongName, where all existing autotext entries' long names are tested +// if they start with one of the list elements. The matches are sorted according the position of the +// candidate that matched first, then alphabetically inside the group of suggestions for a given +// candidate. Say, if we have these AutoText entry long names: +// "Dr Frodo" +// "Dr Credo" +// "Or Bilbo" +// "dr foo" +// " Dr Fuzz" +// " dr Faust" +// the resulting list would be: +// " Dr Fuzz" -> matches the first (longest) item in the candidates list +// " dr Faust" -> matches the second candidate item +// "Dr Foo" -> first item of the two matching the third candidate; alphabetically sorted +// "Dr Frodo" -> second item of the two matching the third candidate; alphabetically sorted +// Each of the resulting suggestions knows the length of the candidate it replaces, so accepting the +// first suggestion would replace 6 characters before cursor, while tabbing to and accepting the +// last suggestion would replace only 4 characters to the left of cursor. +bool SwEditWin::ShowAutoText(const std::vector<OUString>& rChunkCandidates) +{ + m_pQuickHlpData->ClearContent(); + if (!rChunkCandidates.empty()) + { + SwGlossaryList* pList = ::GetGlossaryList(); + pList->HasLongName(rChunkCandidates, m_pQuickHlpData->m_aHelpStrings); + } + + if (!m_pQuickHlpData->m_aHelpStrings.empty()) + { + m_pQuickHlpData->Start(m_rView.GetWrtShell(), true); + } + return !m_pQuickHlpData->m_aHelpStrings.empty(); +} + +void SwEditWin::ShowAutoCorrectQuickHelp( + const OUString& rWord, SvxAutoCorrect& rACorr ) +{ + if (rWord.isEmpty()) + return; + SwWrtShell& rSh = m_rView.GetWrtShell(); + m_pQuickHlpData->ClearContent(); + + if( m_pQuickHlpData->m_aHelpStrings.empty() && + rACorr.GetSwFlags().bAutoCompleteWords ) + { + m_pQuickHlpData->m_bIsAutoText = false; + m_pQuickHlpData->m_bIsTip = rACorr.GetSwFlags().bAutoCmpltShowAsTip; + + // Get the necessary data to show help text. + m_pQuickHlpData->FillStrArr( rSh, rWord ); + } + + if( !m_pQuickHlpData->m_aHelpStrings.empty() ) + { + m_pQuickHlpData->SortAndFilter(rWord); + m_pQuickHlpData->Start(rSh, true); + } +} + +bool SwEditWin::IsInHeaderFooter( const Point &rDocPt, FrameControlType &rControl ) const +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( rDocPt ); + + if ( pPageFrame && pPageFrame->IsOverHeaderFooterArea( rDocPt, rControl ) ) + return true; + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) || rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) + { + SwFrameControlsManager &rMgr = rSh.GetView().GetEditWin().GetFrameControlsManager(); + Point aPoint( LogicToPixel( rDocPt ) ); + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) ) + { + SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Header, pPageFrame ); + if ( pControl && pControl->Contains( aPoint ) ) + { + rControl = FrameControlType::Header; + return true; + } + } + + if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) + { + SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Footer, pPageFrame ); + if ( pControl && pControl->Contains( aPoint ) ) + { + rControl = FrameControlType::Footer; + return true; + } + } + } + + return false; +} + +bool SwEditWin::IsOverHeaderFooterFly( const Point& rDocPos, FrameControlType& rControl, bool& bOverFly, bool& bPageAnchored ) const +{ + bool bRet = false; + Point aPt( rDocPos ); + SwWrtShell &rSh = m_rView.GetWrtShell(); + SwPaM aPam( *rSh.GetCurrentShellCursor().GetPoint() ); + rSh.GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aPt, nullptr, true ); + + const SwStartNode* pStartFly = aPam.GetPoint()->nNode.GetNode().FindFlyStartNode(); + if ( pStartFly ) + { + bOverFly = true; + SwFrameFormat* pFlyFormat = pStartFly->GetFlyFormat( ); + if ( pFlyFormat ) + { + const SwPosition* pAnchor = pFlyFormat->GetAnchor( ).GetContentAnchor( ); + if ( pAnchor ) + { + bool bInHeader = pAnchor->nNode.GetNode( ).FindHeaderStartNode( ) != nullptr; + bool bInFooter = pAnchor->nNode.GetNode( ).FindFooterStartNode( ) != nullptr; + + bRet = bInHeader || bInFooter; + if ( bInHeader ) + rControl = FrameControlType::Header; + else if ( bInFooter ) + rControl = FrameControlType::Footer; + } + else + bPageAnchored = pFlyFormat->GetAnchor( ).GetAnchorId( ) == RndStdIds::FLY_AT_PAGE; + } + } + else + bOverFly = false; + return bRet; +} + +void SwEditWin::SetUseInputLanguage( bool bNew ) +{ + if ( bNew || m_bUseInputLanguage ) + { + SfxBindings& rBind = GetView().GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_ATTR_CHAR_FONT ); + rBind.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + m_bUseInputLanguage = bNew; +} + +OUString SwEditWin::GetSurroundingText() const +{ + OUString sReturn; + SwWrtShell& rSh = m_rView.GetWrtShell(); + if( rSh.HasSelection() && !rSh.IsMultiSelection() && rSh.IsSelOnePara() ) + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + else if( !rSh.HasSelection() ) + { + SwPosition *pPos = rSh.GetCursor()->GetPoint(); + const sal_Int32 nPos = pPos->nContent.GetIndex(); + + // get the sentence around the cursor + rSh.HideCursor(); + rSh.GoStartSentence(); + rSh.SetMark(); + rSh.GoEndSentence(); + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + + pPos->nContent = nPos; + rSh.ClearMark(); + rSh.HideCursor(); + } + + return sReturn; +} + +Selection SwEditWin::GetSurroundingTextSelection() const +{ + SwWrtShell& rSh = m_rView.GetWrtShell(); + if( rSh.HasSelection() ) + { + OUString sReturn; + rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); + return Selection( 0, sReturn.getLength() ); + } + else + { + // Return the position of the visible cursor in the sentence + // around the visible cursor. + SwPosition *pPos = rSh.GetCursor()->GetPoint(); + const sal_Int32 nPos = pPos->nContent.GetIndex(); + + rSh.HideCursor(); + rSh.GoStartSentence(); + const sal_Int32 nStartPos = rSh.GetCursor()->GetPoint()->nContent.GetIndex(); + + pPos->nContent = nPos; + rSh.ClearMark(); + rSh.ShowCursor(); + + return Selection( nPos - nStartPos, nPos - nStartPos ); + } +} + +void SwEditWin::LogicInvalidate(const tools::Rectangle* pRectangle) +{ + OString sRectangle; + if (!pRectangle) + sRectangle = "EMPTY"; + else + sRectangle = pRectangle->toString(); + + SfxLokHelper::notifyInvalidation(&m_rView, sRectangle); +} + +void SwEditWin::LogicMouseButtonDown(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + MouseButtonDown(rMouseEvent); + + SetPointerPosPixel(aPoint); +} + +void SwEditWin::LogicMouseButtonUp(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + MouseButtonUp(rMouseEvent); + + SetPointerPosPixel(aPoint); +} + +void SwEditWin::LogicMouseMove(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + MouseMove(rMouseEvent); + + SetPointerPosPixel(aPoint); +} + +void SwEditWin::SetCursorTwipPosition(const Point& rPosition, bool bPoint, bool bClearMark) +{ + if (SdrView* pSdrView = m_rView.GetWrtShell().GetDrawView()) + { + // Editing shape text, then route the call to editeng. + if (pSdrView->GetTextEditObject()) + { + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + rEditView.SetCursorLogicPosition(rPosition, bPoint, bClearMark); + return; + } + } + + if (m_rView.GetPostItMgr()) + { + if (sw::annotation::SwAnnotationWin* pWin = m_rView.GetPostItMgr()->GetActiveSidebarWin()) + { + // Editing postit text. + pWin->SetCursorLogicPosition(rPosition, bPoint, bClearMark); + return; + } + } + + // Not an SwWrtShell, as that would make SwCursorShell::GetCursor() inaccessible. + SwEditShell& rShell = m_rView.GetWrtShell(); + + bool bCreateSelection = false; + { + SwMvContext aMvContext(&rShell); + if (bClearMark) + rShell.ClearMark(); + else + bCreateSelection = !rShell.HasMark(); + + if (bCreateSelection) + m_rView.GetWrtShell().SttSelect(); + + // If the mark is to be updated, then exchange the point and mark before + // and after, as we can't easily set the mark. + if (!bPoint) + rShell.getShellCursor(/*bBlock=*/false)->Exchange(); + rShell.SetCursor(rPosition); + if (!bPoint) + rShell.getShellCursor(/*bBlock=*/false)->Exchange(); + } + + if (bCreateSelection) + m_rView.GetWrtShell().EndSelect(); +} + +void SwEditWin::SetGraphicTwipPosition(bool bStart, const Point& rPosition) +{ + if (bStart) + { + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonDown(aClickEvent); + MouseEvent aMoveEvent(Point(rPosition.getX() + MIN_MOVE + 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent); + } + else + { + MouseEvent aMoveEvent(Point(rPosition.getX() - MIN_MOVE - 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent); + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonUp(aClickEvent); + } +} + +SwFrameControlsManager& SwEditWin::GetFrameControlsManager() +{ + return *m_pFrameControlsManager; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx new file mode 100644 index 000000000..b48b18ec4 --- /dev/null +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <osl/thread.h> +#include <vcl/help.hxx> +#include <tools/urlobj.hxx> +#include <fmtrfmrk.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/sfxhelp.hxx> +#include <svx/svdview.hxx> +#include <svx/svdpagv.hxx> +#include <swmodule.hxx> +#include <modcfg.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <edtwin.hxx> +#include <dpage.hxx> +#include <docufld.hxx> +#include <reffld.hxx> +#include <cellatr.hxx> +#include <shdwcrsr.hxx> +#include <fmtinfmt.hxx> +#include <fmtftn.hxx> +#include <redline.hxx> +#include <tox.hxx> +#include <txatbase.hxx> +#include <uitool.hxx> +#include <viewopt.hxx> +#include <strings.hrc> + +#include <IDocumentMarkAccess.hxx> +#include <txtfrm.hxx> +#include <ndtxt.hxx> + +static OUString lcl_GetRedlineHelp( const SwRangeRedline& rRedl, bool bBalloon ) +{ + const char* pResId = nullptr; + switch( rRedl.GetType() ) + { + case RedlineType::Insert: pResId = STR_REDLINE_INSERT; break; + case RedlineType::Delete: pResId = STR_REDLINE_DELETE; break; + case RedlineType::Format: pResId = STR_REDLINE_FORMAT; break; + case RedlineType::Table: pResId = STR_REDLINE_TABLE; break; + case RedlineType::FmtColl: pResId = STR_REDLINE_FMTCOLL; break; + case RedlineType::ParagraphFormat: pResId = STR_REDLINE_PARAGRAPH_FORMAT; break; + case RedlineType::TableRowInsert: pResId = STR_REDLINE_TABLE_ROW_INSERT; break; + case RedlineType::TableRowDelete: pResId = STR_REDLINE_TABLE_ROW_DELETE; break; + case RedlineType::TableCellInsert: pResId = STR_REDLINE_TABLE_CELL_INSERT; break; + case RedlineType::TableCellDelete: pResId = STR_REDLINE_TABLE_CELL_DELETE; break; + default: break; + } + + OUStringBuffer sBuf; + if (pResId) + { + sBuf.append(SwResId(pResId)); + sBuf.append(": "); + sBuf.append(rRedl.GetAuthorString()); + sBuf.append(" - "); + sBuf.append(GetAppLangDateTimeString(rRedl.GetTimeStamp())); + if( bBalloon && !rRedl.GetComment().isEmpty() ) + sBuf.append('\n').append(rRedl.GetComment()); + } + return sBuf.makeStringAndClear(); +} + +OUString SwEditWin::ClipLongToolTip(const OUString& rText) +{ + OUString sDisplayText(rText); + long nTextWidth = GetTextWidth(sDisplayText); + long nMaxWidth = GetDesktopRectPixel().GetWidth() * 2 / 3; + nMaxWidth = PixelToLogic(Size(nMaxWidth, 0)).Width(); + if (nTextWidth > nMaxWidth) + sDisplayText = GetEllipsisString(sDisplayText, nMaxWidth, DrawTextFlags::CenterEllipsis); + return sDisplayText; +} + +void SwEditWin::RequestHelp(const HelpEvent &rEvt) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + bool bQuickBalloon = bool(rEvt.GetMode() & ( HelpEventMode::QUICK | HelpEventMode::BALLOON )); + if(bQuickBalloon && !rSh.GetViewOptions()->IsShowContentTips()) + return; + bool bContinue = true; + SET_CURR_SHELL(&rSh); + OUString sText; + Point aPos( PixelToLogic( ScreenToOutputPixel( rEvt.GetMousePosPixel() ) )); + bool bBalloon = bool(rEvt.GetMode() & HelpEventMode::BALLOON); + + SdrView *pSdrView = rSh.GetDrawView(); + + if( bQuickBalloon && pSdrView ) + { + SdrPageView* pPV = pSdrView->GetSdrPageView(); + SwDPage* pPage = pPV ? static_cast<SwDPage*>(pPV->GetPage()) : nullptr; + bContinue = pPage && pPage->RequestHelp(this, pSdrView, rEvt); + } + + if( bContinue && bQuickBalloon) + { + SwRect aFieldRect; + SwContentAtPos aContentAtPos( IsAttrAtPos::Field | + IsAttrAtPos::InetAttr | + IsAttrAtPos::Ftn | + IsAttrAtPos::Redline | + IsAttrAtPos::ToxMark | + IsAttrAtPos::RefMark | + IsAttrAtPos::SmartTag | +#ifdef DBG_UTIL + IsAttrAtPos::TableBoxValue | + ( bBalloon ? IsAttrAtPos::CurrAttrs : IsAttrAtPos::NONE) | +#endif + IsAttrAtPos::TableBoxFml ); + + if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) ) + { + QuickHelpFlags nStyle = QuickHelpFlags::NONE; // style of quick help + switch( aContentAtPos.eContentAtPos ) + { + case IsAttrAtPos::TableBoxFml: + sText = "= " + static_cast<const SwTableBoxFormula*>(aContentAtPos.aFnd.pAttr)->GetFormula(); + break; +#ifdef DBG_UTIL + case IsAttrAtPos::TableBoxValue: + { + sText = OStringToOUString(OString::number( + static_cast<const SwTableBoxValue*>(aContentAtPos.aFnd.pAttr)->GetValue()), + osl_getThreadTextEncoding()); + break; + } + case IsAttrAtPos::CurrAttrs: + sText = aContentAtPos.sStr; + break; +#endif + + case IsAttrAtPos::InetAttr: + { + sText = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)->GetValue(); + sText = URIHelper::removePassword( sText, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous); + //#i63832# remove the link target type + sal_Int32 nFound = sText.indexOf(cMarkSeparator); + if( nFound != -1 && (++nFound) < sText.getLength() ) + { + OUString sSuffix( sText.copy(nFound) ); + if( sSuffix == "table" || + sSuffix == "frame" || + sSuffix == "region" || + sSuffix == "outline" || + sSuffix == "text" || + sSuffix == "graphic" || + sSuffix == "ole" ) + sText = sText.copy( 0, nFound - 1); + } + // #i104300# + // special handling if target is a cross-reference bookmark + { + OUString sTmpSearchStr = sText.copy( 1 ); + IDocumentMarkAccess* pMarkAccess = rSh.getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBkmk = + pMarkAccess->findBookmark( sTmpSearchStr ); + if ( ppBkmk != pMarkAccess->getBookmarksEnd() && + IDocumentMarkAccess::GetType(**ppBkmk) + == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK ) + { + SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().nNode.GetNode().GetTextNode(); + if ( pTextNode ) + { + sText = sw::GetExpandTextMerged(rSh.GetLayout(), *pTextNode, true, false, ExpandMode(0)); + + if( !sText.isEmpty() ) + { + OUStringBuffer sTmp(sText.replaceAll(u"\u00ad", "")); + for (sal_Int32 i = 0; i < sTmp.getLength(); ++i) + { + if (sTmp[i] < 0x20) + sTmp[i] = 0x20; + else if (sTmp[i] == 0x2011) + sTmp[i] = '-'; + } + sText = sTmp.makeStringAndClear(); + } + } + } + } + // #i80029# + bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly(); + if ( !bExecHyperlinks ) + { + sText = SfxHelp::GetURLHelpText(sText); + } + break; + } + case IsAttrAtPos::SmartTag: + { + vcl::KeyCode aCode( KEY_SPACE ); + vcl::KeyCode aModifiedCode( KEY_SPACE, KEY_MOD1 ); + OUString aModStr( aModifiedCode.GetName() ); + aModStr = aModStr.replaceFirst(aCode.GetName(), ""); + aModStr = aModStr.replaceAll("+", ""); + sText = SwResId(STR_SMARTTAG_CLICK).replaceAll("%s", aModStr); + break; + } + + case IsAttrAtPos::Ftn: + if( aContentAtPos.pFndTextAttr && aContentAtPos.aFnd.pAttr ) + { + const SwFormatFootnote* pFootnote = static_cast<const SwFormatFootnote*>(aContentAtPos.aFnd.pAttr); + OUString sTmp(pFootnote->GetFootnoteText(*rSh.GetLayout())); + sText = SwResId( pFootnote->IsEndNote() + ? STR_ENDNOTE : STR_FTNNOTE ) + sTmp; + bBalloon = true; + if( aContentAtPos.IsInRTLText() ) + nStyle |= QuickHelpFlags::BiDiRtl; + } + break; + + case IsAttrAtPos::Redline: + { + const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() ); + const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips(); + if ( bShowTrackChanges && bShowInlineTooltips ) + sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon); + break; + } + + case IsAttrAtPos::ToxMark: + sText = aContentAtPos.sStr; + if( !sText.isEmpty() && aContentAtPos.pFndTextAttr ) + { + const SwTOXType* pTType = aContentAtPos.pFndTextAttr-> + GetTOXMark().GetTOXType(); + if( pTType && !pTType->GetTypeName().isEmpty() ) + { + sText = ": " + sText; + sText = pTType->GetTypeName() + sText; + } + } + break; + + case IsAttrAtPos::RefMark: + if(aContentAtPos.aFnd.pAttr) + { + sText = SwResId(STR_CONTENT_TYPE_SINGLE_REFERENCE) + ": "; + sText += static_cast<const SwFormatRefMark*>(aContentAtPos.aFnd.pAttr)->GetRefName(); + } + break; + + default: + { + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + if(!pModOpt->IsHideFieldTips()) + { + const SwField* pField = aContentAtPos.aFnd.pField; + switch( pField->Which() ) + { + case SwFieldIds::SetExp: + case SwFieldIds::Table: + case SwFieldIds::GetExp: + { + sal_uInt16 nOldSubType = pField->GetSubType(); + const_cast<SwField*>(pField)->SetSubType(nsSwExtendedSubType::SUB_CMD); + sText = pField->ExpandField(true, rSh.GetLayout()); + const_cast<SwField*>(pField)->SetSubType(nOldSubType); + } + break; + + case SwFieldIds::Postit: + { + break; + } + case SwFieldIds::Input: // BubbleHelp, because the suggestion could be quite long + bBalloon = true; + [[fallthrough]]; + case SwFieldIds::Dropdown: + case SwFieldIds::JumpEdit: + sText = pField->GetPar2(); + break; + + case SwFieldIds::Database: + sText = pField->GetFieldName(); + break; + + case SwFieldIds::User: + case SwFieldIds::HiddenText: + sText = pField->GetPar1(); + break; + + case SwFieldIds::DocStat: + break; + + case SwFieldIds::Macro: + sText = static_cast<const SwMacroField*>(pField)->GetMacro(); + break; + + case SwFieldIds::GetRef: + { + // #i85090# + const SwGetRefField* pRefField( dynamic_cast<const SwGetRefField*>(pField) ); + OSL_ENSURE( pRefField, + "<SwEditWin::RequestHelp(..)> - unexpected type of <pField>" ); + if ( pRefField ) + { + if ( pRefField->IsRefToHeadingCrossRefBookmark() || + pRefField->IsRefToNumItemCrossRefBookmark() ) + { + sText = pRefField->GetExpandedTextOfReferencedTextNode(*rSh.GetLayout()); + if ( sText.getLength() > 80 ) + { + sText = sText.copy(0, 80) + "..."; + } + } + else + { + sText = static_cast<const SwGetRefField*>(pField)->GetSetRefName(); + } + } + break; + } + + default: break; + } + } + + if( sText.isEmpty() ) + { + const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() ); + const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips(); + if ( bShowTrackChanges && bShowInlineTooltips ) + { + aContentAtPos.eContentAtPos = IsAttrAtPos::Redline; + if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) ) + sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon); + } + } + } + } + if (!sText.isEmpty()) + { + tools::Rectangle aRect( aFieldRect.SVRect() ); + Point aPt( OutputToScreenPixel( LogicToPixel( aRect.TopLeft() ))); + aRect.SetLeft( aPt.X() ); + aRect.SetTop( aPt.Y() ); + aPt = OutputToScreenPixel( LogicToPixel( aRect.BottomRight() )); + aRect.SetRight( aPt.X() ); + aRect.SetBottom( aPt.Y() ); + + // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin + aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1))); + aRect.AdjustLeft(-1); + aRect.AdjustRight(1); + aRect.AdjustTop(-1); + aRect.AdjustBottom(1); + + if( bBalloon ) + Help::ShowBalloon( this, rEvt.GetMousePosPixel(), aRect, sText ); + else + { + // the show the help + OUString sDisplayText(ClipLongToolTip(sText)); + Help::ShowQuickHelp(this, aRect, sDisplayText, nStyle); + } + } + + bContinue = false; + } + + } + + if( bContinue ) + Window::RequestHelp( rEvt ); +} + +void SwEditWin::PrePaint(vcl::RenderContext& /*rRenderContext*/) +{ + SwWrtShell* pWrtShell = GetView().GetWrtShellPtr(); + + if(pWrtShell) + { + pWrtShell->PrePaint(); + } +} + +void SwEditWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + SwWrtShell* pWrtShell = GetView().GetWrtShellPtr(); + if(!pWrtShell) + return; + bool bPaintShadowCursor = false; + if( m_pShadCursor ) + { + tools::Rectangle aRect( m_pShadCursor->GetRect()); + // fully resides inside? + if( rRect.IsInside( aRect ) ) + { + // then cancel + m_pShadCursor.reset(); + } + else if( rRect.IsOver( aRect )) + { + // resides somewhat above, then everything is clipped outside + // and we have to make the "inner part" at the end of the + // Paint visible again. Otherwise Paint errors occur! + bPaintShadowCursor = true; + } + } + + if ( GetView().GetVisArea().GetWidth() <= 0 || + GetView().GetVisArea().GetHeight() <= 0 ) + Invalidate( rRect ); + else + { + pWrtShell->setOutputToWindow(true); + pWrtShell->Paint(rRenderContext, rRect); + pWrtShell->setOutputToWindow(false); + } + + if( bPaintShadowCursor ) + m_pShadCursor->Paint(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/edtwin3.cxx b/sw/source/uibase/docvw/edtwin3.cxx new file mode 100644 index 000000000..583f485f3 --- /dev/null +++ b/sw/source/uibase/docvw/edtwin3.cxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/event.hxx> +#include <vcl/settings.hxx> +#include <svx/ruler.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <basesh.hxx> +#include <pview.hxx> +#include <mdiexp.hxx> +#include <edtwin.hxx> +#include <swmodule.hxx> +#include <modcfg.hxx> +#include <docsh.hxx> +#include <uiobject.hxx> + +// Core-Notify +void ScrollMDI( SwViewShell const * pVwSh, const SwRect &rRect, + sal_uInt16 nRangeX, sal_uInt16 nRangeY) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell)) + pSwView->Scroll(rRect.SVRect(), nRangeX, nRangeY); +} + +// Docmdi - movable +bool IsScrollMDI( SwViewShell const * pVwSh, const SwRect &rRect ) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell)) + return pSwView->IsScroll(rRect.SVRect()); + + return false; +} + +// Notify for size change +void SizeNotify(SwViewShell const * pVwSh, const Size &rSize) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell)) + pSwView->DocSzChgd(rSize); + else if (SwPagePreview* pSwPageView = dynamic_cast<SwPagePreview *>(pSfxViewShell)) + pSwPageView->DocSzChgd(rSize); +} + +// Notify for page number update +void PageNumNotify( SwViewShell const * pVwSh, sal_uInt16 nPhyNum, sal_uInt16 nVirtNum, + const OUString& rPgStr) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell)) + { + if (pSwView->GetCurShell()) + pSwView->UpdatePageNums(nPhyNum, nVirtNum, rPgStr); + } +} + +void FrameNotify( SwViewShell* pVwSh, FlyMode eMode ) +{ + if (SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell *>(pVwSh)) + SwBaseShell::SetFrameMode(eMode, pWrtShell); +} + +// Notify for page number update +bool SwEditWin::RulerColumnDrag( const MouseEvent& rMEvt, bool bVerticalMode) +{ + SvxRuler& rRuler = bVerticalMode ? m_rView.GetVRuler() : m_rView.GetHRuler(); + return (!rRuler.StartDocDrag( rMEvt, RulerType::Border ) && + !rRuler.StartDocDrag( rMEvt, RulerType::Margin1) && + !rRuler.StartDocDrag( rMEvt, RulerType::Margin2)); +} + +// #i23726# +// #i42921# - add 3rd parameter <bVerticalMode> in order +// to consider vertical layout +bool SwEditWin::RulerMarginDrag( const MouseEvent& rMEvt, + const bool bVerticalMode ) +{ + SvxRuler& rRuler = bVerticalMode ? m_rView.GetVRuler() : m_rView.GetHRuler(); + return !rRuler.StartDocDrag( rMEvt, RulerType::Indent); +} + +TableChgMode GetTableChgDefaultMode() +{ + SwModuleOptions* pOpt = SW_MOD()->GetModuleConfig(); + return pOpt ? pOpt->GetTableMode() : TableChgMode::VarWidthChangeAbs; +} + +void RepaintPagePreview( SwViewShell const * pVwSh, const SwRect& rRect ) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwPagePreview* pSwPagePreview = dynamic_cast<SwPagePreview *>(pSfxViewShell)) + pSwPagePreview->RepaintCoreRect(rRect); +} + +bool JumpToSwMark( SwViewShell const * pVwSh, const OUString& rMark ) +{ + SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell(); + + if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell)) + return pSwView->JumpToSwMark(rMark); + + return false; +} + +void SwEditWin::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + SwWrtShell* pSh = GetView().GetWrtShellPtr(); + // DataChanged() is sometimes called prior to creating + // the SwWrtShell + if(!pSh) + return; + bool bViewWasLocked = pSh->IsViewLocked(), bUnlockPaint = false; + pSh->LockView( true ); + switch( rDCEvt.GetType() ) + { + case DataChangedEventType::SETTINGS: + // rearrange ScrollBars, respectively trigger resize, because + // the ScrollBar size can have change. For that, in the reset + // handler, the size of the ScrollBars also has to be queried + // from the settings. + if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) + { + pSh->LockPaint(); + bUnlockPaint = true; + pSh->DeleteReplacementBitmaps(); + GetView().InvalidateBorder(); //Scrollbar work + } + break; + + case DataChangedEventType::PRINTER: + case DataChangedEventType::DISPLAY: + case DataChangedEventType::FONTS: + case DataChangedEventType::FONTSUBSTITUTION: + pSh->LockPaint(); + bUnlockPaint = true; + GetView().GetDocShell()->UpdateFontList(); //e.g. printer change + pSh->InvalidateLayout(true); + break; + default: break; + } + pSh->LockView( bViewWasLocked ); + if( bUnlockPaint ) + pSh->UnlockPaint(); +} + +FactoryFunction SwEditWin::GetUITestFactory() const +{ + return SwEditWinUIObject::create; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/frmsidebarwincontainer.cxx b/sw/source/uibase/docvw/frmsidebarwincontainer.cxx new file mode 100644 index 000000000..17da0beee --- /dev/null +++ b/sw/source/uibase/docvw/frmsidebarwincontainer.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "frmsidebarwincontainer.hxx" + +#include <map> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <AnnotationWin.hxx> + +namespace { + struct SidebarWinKey + { + const sal_Int32 mnIndex; + + explicit SidebarWinKey( const sal_Int32 nIndex ) + : mnIndex( nIndex ) + {} + + bool operator < ( const SidebarWinKey& rSidebarWinKey ) const + { + return mnIndex < rSidebarWinKey.mnIndex; + } + }; + + typedef std::map < SidebarWinKey, VclPtr<sw::annotation::SwAnnotationWin> > SidebarWinContainer; + + struct FrameKey + { + const SwFrame* mpFrame; + + explicit FrameKey( const SwFrame* pFrame ) + : mpFrame( pFrame ) + {} + + bool operator < ( const FrameKey& rFrameKey ) const + { + return mpFrame < rFrameKey.mpFrame; + } + }; + + typedef std::map < FrameKey, SidebarWinContainer > FrameSidebarWinContainer_; +} + +namespace sw::sidebarwindows { + +class FrameSidebarWinContainer : public FrameSidebarWinContainer_ +{ +}; + +SwFrameSidebarWinContainer::SwFrameSidebarWinContainer() + : mpFrameSidebarWinContainer( new FrameSidebarWinContainer ) +{} + +SwFrameSidebarWinContainer::~SwFrameSidebarWinContainer() +{ + mpFrameSidebarWinContainer->clear(); + mpFrameSidebarWinContainer.reset(); +} + +bool SwFrameSidebarWinContainer::insert( const SwFrame& rFrame, + const SwFormatField& rFormatField, + sw::annotation::SwAnnotationWin& rSidebarWin ) +{ + bool bInserted( false ); + + FrameKey aFrameKey( &rFrame ); + SidebarWinContainer& rSidebarWinContainer = (*mpFrameSidebarWinContainer)[ aFrameKey ]; + + SidebarWinKey aSidebarWinKey( rFormatField.GetTextField()->GetStart() ); + if ( rSidebarWinContainer.empty() || + rSidebarWinContainer.find( aSidebarWinKey) == rSidebarWinContainer.end() ) + { + rSidebarWinContainer[ aSidebarWinKey ] = &rSidebarWin; + bInserted = true; + } + + return bInserted; +} + +bool SwFrameSidebarWinContainer::remove( const SwFrame& rFrame, + const sw::annotation::SwAnnotationWin & rSidebarWin ) +{ + bool bRemoved( false ); + + FrameKey aFrameKey( &rFrame ); + FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey ); + if ( aFrameIter != mpFrameSidebarWinContainer->end() ) + { + SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second; + auto aIter = std::find_if(rSidebarWinContainer.begin(), rSidebarWinContainer.end(), + [&rSidebarWin](const SidebarWinContainer::value_type& rEntry) { return rEntry.second == &rSidebarWin; }); + if ( aIter != rSidebarWinContainer.end() ) + { + rSidebarWinContainer.erase( aIter ); + bRemoved = true; + } + } + + return bRemoved; +} + +bool SwFrameSidebarWinContainer::empty( const SwFrame& rFrame ) +{ + bool bEmpty( true ); + + FrameKey aFrameKey( &rFrame ); + FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey ); + if ( aFrameIter != mpFrameSidebarWinContainer->end() ) + { + bEmpty = (*aFrameIter).second.empty(); + } + + return bEmpty; +} + +sw::annotation::SwAnnotationWin* SwFrameSidebarWinContainer::get( const SwFrame& rFrame, + const sal_Int32 nIndex ) +{ + sw::annotation::SwAnnotationWin* pRet( nullptr ); + + FrameKey aFrameKey( &rFrame ); + FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey ); + if ( aFrameIter != mpFrameSidebarWinContainer->end() && nIndex >= 0 ) + { + SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second; + if (nIndex < sal_Int32(rSidebarWinContainer.size())) + { + auto aIter = rSidebarWinContainer.begin(); + std::advance(aIter, nIndex); + pRet = (*aIter).second; + } + } + return pRet; +} + +void SwFrameSidebarWinContainer::getAll( const SwFrame& rFrame, + std::vector< vcl::Window* >* pSidebarWins ) +{ + pSidebarWins->clear(); + + FrameKey aFrameKey( &rFrame ); + FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey ); + if ( aFrameIter != mpFrameSidebarWinContainer->end() ) + { + SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second; + for ( const auto& rEntry : rSidebarWinContainer ) + { + pSidebarWins->push_back( rEntry.second ); + } + } +} + +} // eof of namespace sw::sidebarwindows + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/frmsidebarwincontainer.hxx b/sw/source/uibase/docvw/frmsidebarwincontainer.hxx new file mode 100644 index 000000000..4e8d7b41a --- /dev/null +++ b/sw/source/uibase/docvw/frmsidebarwincontainer.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_UIBASE_DOCVW_FRMSIDEBARWINCONTAINER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_FRMSIDEBARWINCONTAINER_HXX + +#include <sal/types.h> +#include <memory> +#include <vector> + +class SwFrame; +class SwFormatField; +namespace vcl { class Window; } +namespace sw::annotation { class SwAnnotationWin; } + +namespace sw::sidebarwindows { + +class FrameSidebarWinContainer; + +class SwFrameSidebarWinContainer +{ + public: + SwFrameSidebarWinContainer(); + ~SwFrameSidebarWinContainer(); + + bool insert( const SwFrame& rFrame, + const SwFormatField& rFormatField, + sw::annotation::SwAnnotationWin& rSidebarWin ); + + bool remove( const SwFrame& rFrame, + const sw::annotation::SwAnnotationWin& rSidebarWin ); + + bool empty( const SwFrame& rFrame ); + + sw::annotation::SwAnnotationWin* get( const SwFrame& rFrame, + const sal_Int32 nIndex ); + + void getAll( const SwFrame& rFrame, + std::vector< vcl::Window* >* pSidebarWins ); + + private: + std::unique_ptr<FrameSidebarWinContainer> mpFrameSidebarWinContainer; +}; + +} // namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/romenu.cxx b/sw/source/uibase/docvw/romenu.cxx new file mode 100644 index 000000000..75aa8847d --- /dev/null +++ b/sw/source/uibase/docvw/romenu.cxx @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/eitem.hxx> +#include <vcl/transfer.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/gallery.hxx> +#include <svx/graphichelper.hxx> +#include <editeng/brushitem.hxx> + +#include <fmtinfmt.hxx> +#include <docsh.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <swmodule.hxx> +#include "romenu.hxx" +#include <pagedesc.hxx> +#include <modcfg.hxx> + +#include <cmdid.h> + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace ::sfx2; + +SwReadOnlyPopup::~SwReadOnlyPopup() +{ + m_xMenu.disposeAndClear(); +} + +void SwReadOnlyPopup::Check( sal_uInt16 nMID, sal_uInt16 nSID, SfxDispatcher const &rDis ) +{ + std::unique_ptr<SfxPoolItem> _pItem; + SfxItemState eState = rDis.GetBindings()->QueryState( nSID, _pItem ); + if (eState >= SfxItemState::DEFAULT) + { + m_xMenu->EnableItem(nMID); + if (_pItem) + { + m_xMenu->CheckItem(nMID, !_pItem->IsVoidItem() && + dynamic_cast< const SfxBoolItem *>( _pItem.get() ) != nullptr && + static_cast<SfxBoolItem*>(_pItem.get())->GetValue()); + //remove full screen entry when not in full screen mode + if (SID_WIN_FULLSCREEN == nSID && !m_xMenu->IsItemChecked(m_nReadonlyFullscreen)) + m_xMenu->EnableItem(nMID, false); + } + } + else + m_xMenu->EnableItem(nMID, false); +} + +#define MN_READONLY_GRAPHICTOGALLERY 1000 +#define MN_READONLY_BACKGROUNDTOGALLERY 2000 + +SwReadOnlyPopup::SwReadOnlyPopup(const Point &rDPos, SwView &rV) + : m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/readonlymenu.ui", "") + , m_xMenu(m_aBuilder.get_menu("menu")) + , m_nReadonlyOpenurl(m_xMenu->GetItemId("openurl")) + , m_nReadonlyOpendoc(m_xMenu->GetItemId("opendoc")) + , m_nReadonlyEditdoc(m_xMenu->GetItemId("edit")) + , m_nReadonlySelectionMode(m_xMenu->GetItemId("selection")) + , m_nReadonlyReload(m_xMenu->GetItemId("reload")) + , m_nReadonlyReloadFrame(m_xMenu->GetItemId("reloadframe")) + , m_nReadonlySourceview(m_xMenu->GetItemId("html")) + , m_nReadonlyBrowseBackward(m_xMenu->GetItemId("backward")) + , m_nReadonlyBrowseForward(m_xMenu->GetItemId("forward")) + , m_nReadonlySaveGraphic(m_xMenu->GetItemId("savegraphic")) + , m_nReadonlyGraphictogallery(m_xMenu->GetItemId("graphictogallery")) + , m_nReadonlyTogallerylink(m_xMenu->GetItemId("graphicaslink")) + , m_nReadonlyTogallerycopy(m_xMenu->GetItemId("graphicascopy")) + , m_nReadonlySaveBackground(m_xMenu->GetItemId("savebackground")) + , m_nReadonlyBackgroundtogallery(m_xMenu->GetItemId("backgroundtogallery")) + , m_nReadonlyBackgroundTogallerylink(m_xMenu->GetItemId("backaslink")) + , m_nReadonlyBackgroundTogallerycopy(m_xMenu->GetItemId("backascopy")) + , m_nReadonlyCopylink(m_xMenu->GetItemId("copylink")) + , m_nReadonlyLoadGraphic(m_xMenu->GetItemId("loadgraphic")) + , m_nReadonlyGraphicoff(m_xMenu->GetItemId("imagesoff")) + , m_nReadonlyFullscreen(m_xMenu->GetItemId("fullscreen")) + , m_nReadonlyCopy(m_xMenu->GetItemId("copy")) + , m_rView(rV) + , m_xBrushItem(std::make_unique<SvxBrushItem>(RES_BACKGROUND)) +{ + m_bGrfToGalleryAsLnk = SW_MOD()->GetModuleConfig()->IsGrfToGalleryAsLnk(); + SwWrtShell &rSh = m_rView.GetWrtShell(); + OUString sDescription; + rSh.IsURLGrfAtPos( rDPos, &m_sURL, &m_sTargetFrameName, &sDescription ); + if ( m_sURL.isEmpty() ) + { + SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); + if( rSh.GetContentAtPos( rDPos, aContentAtPos)) + { + const SwFormatINetFormat &rIItem = *static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr); + m_sURL = rIItem.GetValue(); + m_sTargetFrameName = rIItem.GetTargetFrame(); + } + } + + bool bLink = false; + const Graphic *pGrf; + if ( nullptr == (pGrf = rSh.GetGrfAtPos( rDPos, m_sGrfName, bLink )) ) + { + m_xMenu->EnableItem(m_nReadonlySaveGraphic, false); + } + else + { + m_aGraphic = *pGrf; + } + + bool bEnableGraphicToGallery = bLink; + if ( bEnableGraphicToGallery ) + { + if (GalleryExplorer::FillThemeList( m_aThemeList )) + { + PopupMenu *pMenu = m_xMenu->GetPopupMenu(m_nReadonlyGraphictogallery); + pMenu->CheckItem(m_nReadonlyTogallerylink, m_bGrfToGalleryAsLnk); + pMenu->CheckItem(m_nReadonlyTogallerycopy, !m_bGrfToGalleryAsLnk); + + for ( size_t i=0; i < m_aThemeList.size(); ++i ) + pMenu->InsertItem(MN_READONLY_GRAPHICTOGALLERY + i, m_aThemeList[i]); + } + else + bEnableGraphicToGallery = false; + } + + m_xMenu->EnableItem(m_nReadonlyGraphictogallery, bEnableGraphicToGallery); + + SfxViewFrame * pVFrame = rV.GetViewFrame(); + SfxDispatcher &rDis = *pVFrame->GetDispatcher(); + const SwPageDesc &rDesc = rSh.GetPageDesc( rSh.GetCurPageDesc() ); + m_xBrushItem = rDesc.GetMaster().makeBackgroundBrushItem(); + bool bEnableBackGallery = false, + bEnableBack = false; + + if ( m_xBrushItem && GPOS_NONE != m_xBrushItem->GetGraphicPos() ) + { + bEnableBack = true; + if ( !m_xBrushItem->GetGraphicLink().isEmpty() ) + { + if ( m_aThemeList.empty() ) + GalleryExplorer::FillThemeList( m_aThemeList ); + + if ( !m_aThemeList.empty() ) + { + PopupMenu *pMenu = m_xMenu->GetPopupMenu(m_nReadonlyBackgroundtogallery); + pMenu->CheckItem(m_nReadonlyBackgroundTogallerylink, m_bGrfToGalleryAsLnk); + pMenu->CheckItem(m_nReadonlyBackgroundTogallerycopy, !m_bGrfToGalleryAsLnk); + bEnableBackGallery = true; + + for ( size_t i=0; i < m_aThemeList.size(); ++i ) + pMenu->InsertItem(MN_READONLY_BACKGROUNDTOGALLERY + i, m_aThemeList[i]); + } + } + } + m_xMenu->EnableItem(m_nReadonlySaveBackground, bEnableBack); + m_xMenu->EnableItem(m_nReadonlyBackgroundtogallery, bEnableBackGallery); + + if ( !rSh.GetViewOptions()->IsGraphic() ) + m_xMenu->CheckItem(m_nReadonlyGraphicoff); + else + m_xMenu->EnableItem(m_nReadonlyLoadGraphic, false); + + m_xMenu->EnableItem(m_nReadonlyReloadFrame, false); + m_xMenu->EnableItem(m_nReadonlyReload); + + Check(m_nReadonlyEditdoc, SID_EDITDOC, rDis); + Check(m_nReadonlySelectionMode, FN_READONLY_SELECTION_MODE, rDis); + Check(m_nReadonlySourceview, SID_SOURCEVIEW, rDis); + Check(m_nReadonlyBrowseBackward, SID_BROWSE_BACKWARD, rDis); + Check(m_nReadonlyBrowseForward,SID_BROWSE_FORWARD, rDis); + Check(m_nReadonlyOpenurl, SID_OPENDOC, rDis); + Check(m_nReadonlyOpendoc, SID_OPENDOC, rDis); + + std::unique_ptr<SfxPoolItem> pState; + + SfxItemState eState = pVFrame->GetBindings().QueryState( SID_COPY, pState ); + Check(m_nReadonlyCopy, SID_COPY, rDis); + if (eState < SfxItemState::DEFAULT) + m_xMenu->EnableItem(m_nReadonlyCopy, false); + + eState = pVFrame->GetBindings().QueryState( SID_EDITDOC, pState ); + if ( + eState < SfxItemState::DEFAULT || + (rSh.IsGlobalDoc() && m_rView.GetDocShell()->IsReadOnlyUI()) + ) + { + m_xMenu->EnableItem(m_nReadonlyEditdoc, false); + } + + if ( m_sURL.isEmpty() ) + { + m_xMenu->EnableItem(m_nReadonlyOpenurl, false); + m_xMenu->EnableItem(m_nReadonlyOpendoc, false); + m_xMenu->EnableItem(m_nReadonlyCopylink, false); + } + Check(m_nReadonlyFullscreen, SID_WIN_FULLSCREEN, rDis); + + m_xMenu->RemoveDisabledEntries( true, true ); +} + +void SwReadOnlyPopup::Execute( vcl::Window* pWin, const Point &rPixPos ) +{ + sal_uInt16 nId = m_xMenu->Execute(pWin, rPixPos); + Execute(pWin, nId); +} + +// execute the resulting ID only - necessary to support XContextMenuInterception +void SwReadOnlyPopup::Execute( vcl::Window* pWin, sal_uInt16 nId ) +{ + SwWrtShell &rSh = m_rView.GetWrtShell(); + SfxDispatcher &rDis = *m_rView.GetViewFrame()->GetDispatcher(); + if (nId >= MN_READONLY_GRAPHICTOGALLERY) + { + OUString sTmp; + sal_uInt16 nSaveId; + if (m_xBrushItem && nId >= MN_READONLY_BACKGROUNDTOGALLERY) + { + nId -= MN_READONLY_BACKGROUNDTOGALLERY; + nSaveId = m_nReadonlySaveBackground; + sTmp = m_xBrushItem->GetGraphicLink(); + } + else + { + nId -= MN_READONLY_GRAPHICTOGALLERY; + nSaveId = m_nReadonlySaveGraphic; + sTmp = m_sGrfName; + } + if ( !m_bGrfToGalleryAsLnk ) + sTmp = SaveGraphic(nSaveId); + + if ( !sTmp.isEmpty() ) + GalleryExplorer::InsertURL( m_aThemeList[nId], sTmp ); + + return; + } + + rtl::Reference<TransferDataContainer> pClipCntnr; + + sal_uInt16 nExecId = USHRT_MAX; + bool bFilterSet = false; + LoadUrlFlags nFilter = LoadUrlFlags::NONE; + if (nId == m_nReadonlyFullscreen) + nExecId = SID_WIN_FULLSCREEN; + else if (nId == m_nReadonlyOpenurl) + { + nFilter = LoadUrlFlags::NONE; + bFilterSet = true; + } + else if (nId == m_nReadonlyOpendoc) + { + nFilter = LoadUrlFlags::NewView; + bFilterSet = true; + } + else if (nId == m_nReadonlyCopy) + nExecId = SID_COPY; + else if (nId == m_nReadonlyEditdoc) + nExecId = SID_EDITDOC; + else if (nId == m_nReadonlySelectionMode) + nExecId = FN_READONLY_SELECTION_MODE; + else if (nId == m_nReadonlyReload || nId == m_nReadonlyReloadFrame) + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(SID_RELOAD); + else if (nId == m_nReadonlyBrowseBackward) + nExecId = SID_BROWSE_BACKWARD; + else if (nId == m_nReadonlyBrowseForward) + nExecId = SID_BROWSE_FORWARD; + else if (nId == m_nReadonlySourceview) + nExecId = SID_SOURCEVIEW; + else if (nId == m_nReadonlySaveGraphic || nId == m_nReadonlySaveBackground) + SaveGraphic(nId); + else if (nId == m_nReadonlyCopylink) + { + pClipCntnr = new TransferDataContainer; + pClipCntnr->CopyString( m_sURL ); + } + else if (nId == m_nReadonlyLoadGraphic) + { + bool bModified = rSh.IsModified(); + SwViewOption aOpt( *rSh.GetViewOptions() ); + aOpt.SetGraphic( true ); + rSh.ApplyViewOptions( aOpt ); + if(!bModified) + rSh.ResetModified(); + } + else if (nId == m_nReadonlyGraphicoff) + nExecId = FN_VIEW_GRAPHIC; + else if (nId == m_nReadonlyTogallerylink || nId == m_nReadonlyBackgroundTogallerylink) + SW_MOD()->GetModuleConfig()->SetGrfToGalleryAsLnk(true); + else if (nId == m_nReadonlyTogallerycopy || nId == m_nReadonlyBackgroundTogallerycopy) + SW_MOD()->GetModuleConfig()->SetGrfToGalleryAsLnk(false); + + if( USHRT_MAX != nExecId ) + rDis.GetBindings()->Execute( nExecId ); + if( bFilterSet ) + ::LoadURL(rSh, m_sURL, nFilter, m_sTargetFrameName); + + if( pClipCntnr && pClipCntnr->HasAnyData() ) + { + pClipCntnr->CopyToClipboard( pWin ); + } +} + +OUString SwReadOnlyPopup::SaveGraphic(sal_uInt16 nId) +{ + // fish out the graphic's name + if (nId == m_nReadonlySaveBackground) + { + if ( m_xBrushItem && !m_xBrushItem->GetGraphicLink().isEmpty() ) + m_sGrfName = m_xBrushItem->GetGraphicLink(); + const Graphic *pGrf = m_xBrushItem ? m_xBrushItem->GetGraphic() : nullptr; + if ( pGrf ) + { + m_aGraphic = *pGrf; + if ( !m_xBrushItem->GetGraphicLink().isEmpty() ) + m_sGrfName = m_xBrushItem->GetGraphicLink(); + } + else + return OUString(); + } + return GraphicHelper::ExportGraphic(m_rView.GetFrameWeld(), m_aGraphic, m_sGrfName); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/romenu.hxx b/sw/source/uibase/docvw/romenu.hxx new file mode 100644 index 000000000..7d2e82994 --- /dev/null +++ b/sw/source/uibase/docvw/romenu.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_DOCVW_ROMENU_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_ROMENU_HXX + +#include <editeng/brushitem.hxx> +#include <vcl/builder.hxx> +#include <vcl/graph.hxx> +#include <vcl/menu.hxx> + +class SwView; +class SfxDispatcher; +class ImageMap; +class INetImage; + +class SwReadOnlyPopup +{ + VclBuilder m_aBuilder; + ScopedVclPtr<PopupMenu> m_xMenu; + sal_uInt16 m_nReadonlyOpenurl; + sal_uInt16 m_nReadonlyOpendoc; + sal_uInt16 m_nReadonlyEditdoc; + sal_uInt16 m_nReadonlySelectionMode; + sal_uInt16 m_nReadonlyReload; + sal_uInt16 m_nReadonlyReloadFrame; + sal_uInt16 m_nReadonlySourceview; + sal_uInt16 m_nReadonlyBrowseBackward; + sal_uInt16 m_nReadonlyBrowseForward; + sal_uInt16 m_nReadonlySaveGraphic; + sal_uInt16 m_nReadonlyGraphictogallery; + sal_uInt16 m_nReadonlyTogallerylink; + sal_uInt16 m_nReadonlyTogallerycopy; + sal_uInt16 m_nReadonlySaveBackground; + sal_uInt16 m_nReadonlyBackgroundtogallery; + sal_uInt16 m_nReadonlyBackgroundTogallerylink; + sal_uInt16 m_nReadonlyBackgroundTogallerycopy; + sal_uInt16 m_nReadonlyCopylink; + sal_uInt16 m_nReadonlyLoadGraphic; + sal_uInt16 m_nReadonlyGraphicoff; + sal_uInt16 m_nReadonlyFullscreen; + sal_uInt16 m_nReadonlyCopy; + + SwView & m_rView; + std::unique_ptr<SvxBrushItem> m_xBrushItem; + Graphic m_aGraphic; + OUString m_sURL, + m_sTargetFrameName; + OUString m_sGrfName; + std::vector<OUString> m_aThemeList; + bool m_bGrfToGalleryAsLnk; + + void Check( sal_uInt16 nMID, sal_uInt16 nSID, SfxDispatcher const &rDis ); + OUString SaveGraphic( sal_uInt16 nId ); + +public: + SwReadOnlyPopup(const Point &rDPos, SwView &rV); + PopupMenu& GetMenu() const { return *m_xMenu; } + ~SwReadOnlyPopup(); + + void Execute( vcl::Window* pWin, const Point &rPPos ); + void Execute( vcl::Window* pWin, sal_uInt16 nId ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/docvw/srcedtw.cxx b/sw/source/uibase/docvw/srcedtw.cxx new file mode 100644 index 000000000..59d4e2555 --- /dev/null +++ b/sw/source/uibase/docvw/srcedtw.cxx @@ -0,0 +1,995 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <cmdid.h> + +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/beans/XPropertiesChangeListener.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <officecfg/Office/Common.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/textview.hxx> +#include <vcl/scrbar.hxx> +#include <vcl/ptrstyle.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svtools/htmltokn.h> +#include <vcl/txtattr.hxx> +#include <vcl/settings.hxx> +#include <svtools/colorcfg.hxx> +#include <editeng/flstitem.hxx> +#include <vcl/metric.hxx> +#include <svtools/ctrltool.hxx> +#include <tools/time.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <srcview.hxx> +#include <helpids.h> +#include <vector> + +namespace +{ + +struct TextPortion +{ + sal_uInt16 nStart, nEnd; + svtools::ColorConfigEntry eType; +}; + +} + +#define MAX_SYNTAX_HIGHLIGHT 20 +#define MAX_HIGHLIGHTTIME 200 + +typedef std::vector<TextPortion> TextPortions; + +static void lcl_Highlight(const OUString& rSource, TextPortions& aPortionList) +{ + const sal_Unicode cOpenBracket = '<'; + const sal_Unicode cCloseBracket= '>'; + const sal_Unicode cSlash = '/'; + const sal_Unicode cExclamation = '!'; + const sal_Unicode cMinus = '-'; + const sal_Unicode cSpace = ' '; + const sal_Unicode cTab = 0x09; + const sal_Unicode cLF = 0x0a; + const sal_Unicode cCR = 0x0d; + + const sal_uInt16 nStrLen = rSource.getLength(); + sal_uInt16 nInsert = 0; // number of inserted portions + sal_uInt16 nActPos = 0; // position, where '<' was found + sal_uInt16 nPortStart = USHRT_MAX; // for the TextPortion + sal_uInt16 nPortEnd = 0; + TextPortion aText; + while(nActPos < nStrLen) + { + svtools::ColorConfigEntry eFoundType = svtools::HTMLUNKNOWN; + if((nActPos < nStrLen - 2) && (rSource[nActPos] == cOpenBracket)) + { + // insert 'empty' portion + if(nPortEnd < nActPos - 1 ) + { + // don't move at the beginning + aText.nStart = nPortEnd; + if(nInsert) + aText.nStart += 1; + aText.nEnd = nActPos - 1; + aText.eType = svtools::HTMLUNKNOWN; + aPortionList.push_back( aText ); + nInsert++; + } + sal_Unicode cFollowFirst = rSource[nActPos + 1]; + sal_Unicode cFollowNext = rSource[nActPos + 2]; + if(cExclamation == cFollowFirst) + { + // "<!" SGML or comment + if(cMinus == cFollowNext && + nActPos < nStrLen - 3 && cMinus == rSource[nActPos + 3]) + { + eFoundType = svtools::HTMLCOMMENT; + } + else + eFoundType = svtools::HTMLSGML; + nPortStart = nActPos; + nPortEnd = nActPos + 1; + } + else if(cSlash == cFollowFirst) + { + // "</" ignore slash + nPortStart = nActPos; + nActPos++; + } + if(svtools::HTMLUNKNOWN == eFoundType) + { + // now here a keyword could follow + sal_uInt16 nSrchPos = nActPos; + while(++nSrchPos < nStrLen - 1) + { + sal_Unicode cNext = rSource[nSrchPos]; + if( cNext == cSpace || + cNext == cTab || + cNext == cLF || + cNext == cCR) + break; + else if(cNext == cCloseBracket) + { + break; + } + } + if(nSrchPos > nActPos + 1) + { + // some string was found + OUString sToken = rSource.copy(nActPos + 1, nSrchPos - nActPos - 1 ); + sToken = sToken.toAsciiUpperCase(); + HtmlTokenId nToken = ::GetHTMLToken(sToken); + if(nToken != HtmlTokenId::NONE) + { + // Token was found + eFoundType = svtools::HTMLKEYWORD; + nPortEnd = nSrchPos; + nPortStart = nActPos; + } + else + { + // what was that? + SAL_WARN( + "sw.level2", + "Token " << sToken + << " not recognised!"); + } + + } + } + // now we still have to look for '>' + if(svtools::HTMLUNKNOWN != eFoundType) + { + bool bFound = false; + for(sal_uInt16 i = nPortEnd; i < nStrLen; i++) + if(cCloseBracket == rSource[i]) + { + bFound = true; + nPortEnd = i; + break; + } + if(!bFound && (eFoundType == svtools::HTMLCOMMENT)) + { + // comment without ending in this line + bFound = true; + nPortEnd = nStrLen - 1; + } + + if(bFound ||(eFoundType == svtools::HTMLCOMMENT)) + { + TextPortion aTextPortion; + aTextPortion.nStart = nPortStart + 1; + aTextPortion.nEnd = nPortEnd; + aTextPortion.eType = eFoundType; + aPortionList.push_back( aTextPortion ); + nInsert++; + } + + } + } + nActPos++; + } + if(nInsert && nPortEnd < nActPos - 1) + { + aText.nStart = nPortEnd + 1; + aText.nEnd = nActPos - 1; + aText.eType = svtools::HTMLUNKNOWN; + aPortionList.push_back( aText ); + nInsert++; + } +} + +class SwSrcEditWindow::ChangesListener: + public cppu::WeakImplHelper< css::beans::XPropertiesChangeListener > +{ +public: + explicit ChangesListener(SwSrcEditWindow & editor): editor_(editor) {} + +private: + virtual ~ChangesListener() override {} + + virtual void SAL_CALL disposing(css::lang::EventObject const &) override + { + osl::MutexGuard g(editor_.mutex_); + editor_.m_xNotifier.clear(); + } + + virtual void SAL_CALL propertiesChange( + css::uno::Sequence< css::beans::PropertyChangeEvent > const &) override + { + SolarMutexGuard g; + editor_.SetFont(); + } + + SwSrcEditWindow & editor_; +}; + +SwSrcEditWindow::SwSrcEditWindow( vcl::Window* pParent, SwSrcView* pParentView ) : + Window( pParent, WB_BORDER|WB_CLIPCHILDREN ), + + m_pOutWin(nullptr), + m_pHScrollbar(nullptr), + m_pVScrollbar(nullptr), + + m_pSrcView(pParentView), + + m_nCurTextWidth(0), + m_nStartLine(USHRT_MAX), + m_eSourceEncoding(osl_getThreadTextEncoding()), + m_bReadonly(false), + m_bHighlighting(false), + m_aSyntaxIdle("sw uibase SwSrcEditWindow Syntax") +{ + SetHelpId(HID_SOURCE_EDITWIN); + CreateTextEngine(); + + // Using "this" in ctor is a little fishy, but should work here at least as + // long as there are no derivations: + m_xListener = new ChangesListener(*this); + css::uno::Reference< css::beans::XMultiPropertySet > n( + officecfg::Office::Common::Font::SourceViewFont::get(), + css::uno::UNO_QUERY_THROW); + { + osl::MutexGuard g(mutex_); + m_xNotifier = n; + } + css::uno::Sequence< OUString > s(2); + s[0] = "FontHeight"; + s[1] = "FontName"; + n->addPropertiesChangeListener(s, m_xListener.get()); +} + +SwSrcEditWindow::~SwSrcEditWindow() +{ + disposeOnce(); +} + +void SwSrcEditWindow::dispose() +{ + css::uno::Reference< css::beans::XMultiPropertySet > n; + { + osl::MutexGuard g(mutex_); + n = m_xNotifier; + } + if (n.is()) { + n->removePropertiesChangeListener(m_xListener.get()); + } + m_aSyntaxIdle.Stop(); + if ( m_pOutWin ) + m_pOutWin->SetTextView( nullptr ); + + if ( m_pTextEngine ) + { + EndListening( *m_pTextEngine ); + m_pTextEngine->RemoveView( m_pTextView.get() ); + + m_pTextView.reset(); + m_pTextEngine.reset(); + } + m_pHScrollbar.disposeAndClear(); + m_pVScrollbar.disposeAndClear(); + m_pOutWin.disposeAndClear(); + vcl::Window::dispose(); +} + +void SwSrcEditWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + switch ( rDCEvt.GetType() ) + { + case DataChangedEventType::SETTINGS: + // newly rearrange ScrollBars or trigger Resize, because + // ScrollBar size could have changed. For this, in the + // Resize handler the size of ScrollBars has to be queried + // from the settings as well. + if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) + Resize(); + break; + default: break; + } +} + +void SwSrcEditWindow::Resize() +{ + // ScrollBars, etc. happens in Adjust... + if ( !m_pTextView ) + return; + + long nVisY = m_pTextView->GetStartDocPos().Y(); + m_pTextView->ShowCursor(); + Size aOutSz( GetOutputSizePixel() ); + long nMaxVisAreaStart = m_pTextView->GetTextEngine()->GetTextHeight() - aOutSz.Height(); + if ( nMaxVisAreaStart < 0 ) + nMaxVisAreaStart = 0; + if ( m_pTextView->GetStartDocPos().Y() > nMaxVisAreaStart ) + { + Point aStartDocPos( m_pTextView->GetStartDocPos() ); + aStartDocPos.setY( nMaxVisAreaStart ); + m_pTextView->SetStartDocPos( aStartDocPos ); + m_pTextView->ShowCursor(); + } + long nScrollStd = GetSettings().GetStyleSettings().GetScrollBarSize(); + Size aScrollSz(aOutSz.Width() - nScrollStd, nScrollStd ); + Point aScrollPos(0, aOutSz.Height() - nScrollStd); + + m_pHScrollbar->SetPosSizePixel( aScrollPos, aScrollSz); + + aScrollSz.setWidth( aScrollSz.Height() ); + aScrollSz.setHeight( aOutSz.Height() ); + aScrollPos = Point(aOutSz.Width() - nScrollStd, 0); + + m_pVScrollbar->SetPosSizePixel( aScrollPos, aScrollSz); + aOutSz.AdjustWidth( -nScrollStd ); + aOutSz.AdjustHeight( -nScrollStd ); + m_pOutWin->SetOutputSizePixel(aOutSz); + InitScrollBars(); + + // set line in first Resize + if(USHRT_MAX != m_nStartLine) + { + if(m_nStartLine < m_pTextEngine->GetParagraphCount()) + { + TextSelection aSel(TextPaM( m_nStartLine, 0 ), TextPaM( m_nStartLine, 0x0 )); + m_pTextView->SetSelection(aSel); + m_pTextView->ShowCursor(); + } + m_nStartLine = USHRT_MAX; + } + + if ( nVisY != m_pTextView->GetStartDocPos().Y() ) + Invalidate(); + + +} + +void TextViewOutWin::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + switch( rDCEvt.GetType() ) + { + case DataChangedEventType::SETTINGS: + // query settings + if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) + { + const Color &rCol = GetSettings().GetStyleSettings().GetWindowColor(); + SetBackground( rCol ); + vcl::Font aFont( pTextView->GetTextEngine()->GetFont() ); + aFont.SetFillColor( rCol ); + pTextView->GetTextEngine()->SetFont( aFont ); + } + break; + default: break; + } +} + +void TextViewOutWin::MouseMove( const MouseEvent &rEvt ) +{ + if ( pTextView ) + pTextView->MouseMove( rEvt ); +} + +void TextViewOutWin::MouseButtonUp( const MouseEvent &rEvt ) +{ + if ( pTextView ) + { + pTextView->MouseButtonUp( rEvt ); + SfxViewFrame *pFrame = static_cast<SwSrcEditWindow*>(GetParent())->GetSrcView()->GetViewFrame(); + if ( pFrame ) + { + SfxBindings& rBindings = pFrame->GetBindings(); + rBindings.Invalidate( SID_TABLE_CELL ); + rBindings.Invalidate( SID_CUT ); + rBindings.Invalidate( SID_COPY ); + } + } +} + +void TextViewOutWin::MouseButtonDown( const MouseEvent &rEvt ) +{ + GrabFocus(); + if ( pTextView ) + pTextView->MouseButtonDown( rEvt ); +} + +void TextViewOutWin::Command( const CommandEvent& rCEvt ) +{ + switch(rCEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + SfxDispatcher::ExecutePopup(); + break; + case CommandEventId::Wheel: + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + { + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if( !pWData || CommandWheelMode::ZOOM != pWData->GetMode() ) + { + static_cast<SwSrcEditWindow*>(GetParent())->HandleWheelCommand( rCEvt ); + } + } + break; + + default: + if ( pTextView ) + pTextView->Command( rCEvt ); + else + Window::Command(rCEvt); + } +} + +void TextViewOutWin::KeyInput( const KeyEvent& rKEvt ) +{ + bool bDone = false; + SwSrcEditWindow* pSrcEditWin = static_cast<SwSrcEditWindow*>(GetParent()); + bool bChange = !pSrcEditWin->IsReadonly() || !TextEngine::DoesKeyChangeText( rKEvt ); + if(bChange) + bDone = pTextView->KeyInput( rKEvt ); + + SfxBindings& rBindings = static_cast<SwSrcEditWindow*>(GetParent())->GetSrcView()->GetViewFrame()->GetBindings(); + if ( !bDone ) + { + if ( !SfxViewShell::Current()->KeyInput( rKEvt ) ) + Window::KeyInput( rKEvt ); + } + else + { + rBindings.Invalidate( SID_TABLE_CELL ); + if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR ) + rBindings.Update( SID_BASICIDE_STAT_POS ); + if (pSrcEditWin->GetTextEngine()->IsModified() ) + { + rBindings.Invalidate( SID_SAVEDOC ); + rBindings.Invalidate( SID_DOC_MODIFIED ); + } + if( rKEvt.GetKeyCode().GetCode() == KEY_INSERT ) + rBindings.Invalidate( SID_ATTR_INSERT ); + } + + rBindings.Invalidate( SID_CUT ); + rBindings.Invalidate( SID_COPY ); + + SwDocShell* pDocShell = pSrcEditWin->GetSrcView()->GetDocShell(); + if(pSrcEditWin->GetTextEngine()->IsModified()) + { + pDocShell->SetModified(); + } +} + +void TextViewOutWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + pTextView->Paint(rRenderContext, rRect); +} + +void SwSrcEditWindow::CreateTextEngine() +{ + // FIXME RenderContext + + const Color &rCol = GetSettings().GetStyleSettings().GetWindowColor(); + m_pOutWin = VclPtr<TextViewOutWin>::Create(this, 0); + m_pOutWin->SetBackground(Wallpaper(rCol)); + m_pOutWin->SetPointer(PointerStyle::Text); + m_pOutWin->Show(); + + // create Scrollbars + m_pHScrollbar = VclPtr<ScrollBar>::Create(this, WB_3DLOOK |WB_HSCROLL|WB_DRAG); + m_pHScrollbar->EnableRTL( false ); + m_pHScrollbar->SetScrollHdl(LINK(this, SwSrcEditWindow, ScrollHdl)); + m_pHScrollbar->Show(); + + m_pVScrollbar = VclPtr<ScrollBar>::Create(this, WB_3DLOOK |WB_VSCROLL|WB_DRAG); + m_pVScrollbar->EnableRTL( false ); + m_pVScrollbar->SetScrollHdl(LINK(this, SwSrcEditWindow, ScrollHdl)); + m_pHScrollbar->EnableDrag(); + m_pVScrollbar->Show(); + + m_pTextEngine.reset(new ExtTextEngine); + m_pTextView.reset(new TextView( m_pTextEngine.get(), m_pOutWin )); + m_pTextView->SetAutoIndentMode(true); + m_pOutWin->SetTextView(m_pTextView.get()); + + m_pTextEngine->SetUpdateMode( false ); + m_pTextEngine->InsertView( m_pTextView.get() ); + + vcl::Font aFont; + aFont.SetTransparent( false ); + aFont.SetFillColor( rCol ); + SetPointFont(*this, aFont); + aFont = GetFont(); + aFont.SetFillColor( rCol ); + m_pOutWin->SetFont( aFont ); + m_pTextEngine->SetFont( aFont ); + + m_aSyntaxIdle.SetInvokeHandler( LINK( this, SwSrcEditWindow, SyntaxTimerHdl ) ); + + m_pTextEngine->EnableUndo( true ); + m_pTextEngine->SetUpdateMode( true ); + + m_pTextView->ShowCursor(); + InitScrollBars(); + StartListening( *m_pTextEngine ); + + SfxBindings& rBind = GetSrcView()->GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_TABLE_CELL ); +} + +void SwSrcEditWindow::SetScrollBarRanges() +{ + // Extra method, not InitScrollBars, because also for TextEngine events. + + m_pHScrollbar->SetRange( Range( 0, m_nCurTextWidth-1 ) ); + m_pVScrollbar->SetRange( Range(0, m_pTextEngine->GetTextHeight()-1) ); +} + +void SwSrcEditWindow::InitScrollBars() +{ + SetScrollBarRanges(); + + Size aOutSz( m_pOutWin->GetOutputSizePixel() ); + m_pVScrollbar->SetVisibleSize( aOutSz.Height() ); + m_pVScrollbar->SetPageSize( aOutSz.Height() * 8 / 10 ); + m_pVScrollbar->SetLineSize( m_pOutWin->GetTextHeight() ); + m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() ); + m_pHScrollbar->SetVisibleSize( aOutSz.Width() ); + m_pHScrollbar->SetPageSize( aOutSz.Width() * 8 / 10 ); + m_pHScrollbar->SetLineSize( m_pOutWin->GetTextWidth(OUString('x')) ); + m_pHScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().X() ); + +} + +IMPL_LINK(SwSrcEditWindow, ScrollHdl, ScrollBar*, pScroll, void) +{ + if(pScroll == m_pVScrollbar) + { + long nDiff = m_pTextView->GetStartDocPos().Y() - pScroll->GetThumbPos(); + GetTextView()->Scroll( 0, nDiff ); + m_pTextView->ShowCursor( false ); + pScroll->SetThumbPos( m_pTextView->GetStartDocPos().Y() ); + } + else + { + long nDiff = m_pTextView->GetStartDocPos().X() - pScroll->GetThumbPos(); + GetTextView()->Scroll( nDiff, 0 ); + m_pTextView->ShowCursor( false ); + pScroll->SetThumbPos( m_pTextView->GetStartDocPos().X() ); + } + GetSrcView()->GetViewFrame()->GetBindings().Invalidate( SID_TABLE_CELL ); +} + +IMPL_LINK( SwSrcEditWindow, SyntaxTimerHdl, Timer*, pIdle, void ) +{ + tools::Time aSyntaxCheckStart( tools::Time::SYSTEM ); + SAL_WARN_IF(m_pTextView == nullptr, "sw", "No View yet, but syntax highlighting?!"); + + m_bHighlighting = true; + sal_uInt16 nCount = 0; + // at first the region around the cursor is processed + TextSelection aSel = m_pTextView->GetSelection(); + sal_uInt16 nCur = static_cast<sal_uInt16>(aSel.GetStart().GetPara()); + if(nCur > 40) + nCur -= 40; + else + nCur = 0; + if(!m_aSyntaxLineTable.empty()) + for(sal_uInt16 i = 0; i < 80 && nCount < 40; i++, nCur++) + { + if(m_aSyntaxLineTable.find(nCur) != m_aSyntaxLineTable.end()) + { + DoSyntaxHighlight( nCur ); + m_aSyntaxLineTable.erase( nCur ); + nCount++; + if(m_aSyntaxLineTable.empty()) + break; + if((tools::Time( tools::Time::SYSTEM ).GetTime() - aSyntaxCheckStart.GetTime()) > MAX_HIGHLIGHTTIME ) + { + break; + } + } + } + + // when there is still anything left by then, go on from the beginning + while ( !m_aSyntaxLineTable.empty() && nCount < MAX_SYNTAX_HIGHLIGHT) + { + sal_uInt16 nLine = *m_aSyntaxLineTable.begin(); + DoSyntaxHighlight( nLine ); + m_aSyntaxLineTable.erase(nLine); + nCount ++; + if(tools::Time( tools::Time::SYSTEM ).GetTime() - aSyntaxCheckStart.GetTime() > MAX_HIGHLIGHTTIME) + { + break; + } + } + + if(!m_aSyntaxLineTable.empty() && !pIdle->IsActive()) + pIdle->Start(); + // SyntaxTimerHdl is called when text changed + // => good opportunity to determine text width! + long nPrevTextWidth = m_nCurTextWidth; + m_nCurTextWidth = m_pTextEngine->CalcTextWidth() + 25; // small tolerance + if ( m_nCurTextWidth != nPrevTextWidth ) + SetScrollBarRanges(); + m_bHighlighting = false; +} + +void SwSrcEditWindow::DoSyntaxHighlight( sal_uInt16 nPara ) +{ + // Because of DelayedSyntaxHighlight it could happen, + // that the line doesn't exist anymore! + if ( nPara >= m_pTextEngine->GetParagraphCount() ) + return; + + bool bTempModified = IsModified(); + m_pTextEngine->RemoveAttribs( nPara ); + OUString aSource( m_pTextEngine->GetText( nPara ) ); + m_pTextEngine->SetUpdateMode( false ); + ImpDoHighlight( aSource, nPara ); + TextView* pTmp = m_pTextEngine->GetActiveView(); + pTmp->SetAutoScroll(false); + m_pTextEngine->SetActiveView(nullptr); + m_pTextEngine->SetUpdateMode( true ); + m_pTextEngine->SetActiveView(pTmp); + pTmp->SetAutoScroll(true); + pTmp->ShowCursor( false/*pTmp->IsAutoScroll()*/ ); + + if(!bTempModified) + ClearModifyFlag(); + +} + +void SwSrcEditWindow::ImpDoHighlight( const OUString& rSource, sal_uInt16 nLineOff ) +{ + TextPortions aPortionList; + lcl_Highlight(rSource, aPortionList); + + size_t nCount = aPortionList.size(); + if ( !nCount ) + return; + + TextPortion& rLast = aPortionList[nCount-1]; + if ( rLast.nStart > rLast.nEnd ) // Only until Bug from MD is resolved + { + nCount--; + aPortionList.pop_back(); + if ( !nCount ) + return; + } + + { + // Only blanks and tabs have to be attributed along. + // When two identical attributes are placed consecutively, + // it optimises the TextEngine. + sal_uInt16 nLastEnd = 0; + + for ( size_t i = 0; i < nCount; i++ ) + { + TextPortion& r = aPortionList[i]; + if ( r.nStart > r.nEnd ) // only until Bug from MD is resolved + continue; + + if ( r.nStart > nLastEnd ) + { + // Can I rely on the fact that all except blank and tab + // are being highlighted?! + r.nStart = nLastEnd; + } + nLastEnd = r.nEnd+1; + if ( ( i == (nCount-1) ) && ( r.nEnd < rSource.getLength() ) ) + r.nEnd = rSource.getLength(); + } + } + + for (TextPortion & r : aPortionList) + { + if ( r.nStart > r.nEnd ) // only until Bug from MD is resolved + continue; + if(r.eType != svtools::HTMLSGML && + r.eType != svtools::HTMLCOMMENT && + r.eType != svtools::HTMLKEYWORD && + r.eType != svtools::HTMLUNKNOWN) + r.eType = svtools::HTMLUNKNOWN; + Color aColor(SW_MOD()->GetColorConfig().GetColorValue(r.eType).nColor); + m_pTextEngine->SetAttrib( TextAttribFontColor( aColor ), nLineOff, r.nStart, r.nEnd+1 ); + } +} + +void SwSrcEditWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + const TextHint* pTextHint = dynamic_cast<const TextHint*>(&rHint); + if (!pTextHint) + return; + + switch (pTextHint->GetId()) + { + case SfxHintId::TextViewScrolled: + m_pHScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().X() ); + m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() ); + break; + + case SfxHintId::TextHeightChanged: + if ( m_pTextEngine->GetTextHeight() < m_pOutWin->GetOutputSizePixel().Height() ) + m_pTextView->Scroll( 0, m_pTextView->GetStartDocPos().Y() ); + m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() ); + SetScrollBarRanges(); + break; + + case SfxHintId::TextParaInserted: + case SfxHintId::TextParaContentChanged: + if ( !m_bHighlighting ) + { + m_aSyntaxLineTable.insert( static_cast<sal_uInt16>(pTextHint->GetValue()) ); + m_aSyntaxIdle.Start(); + } + break; + default: break; + } +} + +void SwSrcEditWindow::Invalidate(InvalidateFlags ) +{ + m_pOutWin->Invalidate(); + Window::Invalidate(); +} + +void SwSrcEditWindow::Command( const CommandEvent& rCEvt ) +{ + switch(rCEvt.GetCommand()) + { + case CommandEventId::Wheel: + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + { + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if( !pWData || CommandWheelMode::ZOOM != pWData->GetMode() ) + HandleScrollCommand( rCEvt, m_pHScrollbar, m_pVScrollbar ); + } + break; + default: + Window::Command(rCEvt); + } +} + +void SwSrcEditWindow::HandleWheelCommand( const CommandEvent& rCEvt ) +{ + m_pTextView->Command(rCEvt); + HandleScrollCommand( rCEvt, m_pHScrollbar, m_pVScrollbar ); +} + +void SwSrcEditWindow::GetFocus() +{ + if (m_pOutWin) + m_pOutWin->GrabFocus(); +} + +static bool lcl_GetLanguagesForEncoding(rtl_TextEncoding eEnc, LanguageType aLanguages[]) +{ + switch(eEnc) + { + case RTL_TEXTENCODING_UTF7 : + case RTL_TEXTENCODING_UTF8 : + // don#t fill - all LANGUAGE_SYSTEM means unicode font has to be used + break; + + case RTL_TEXTENCODING_ISO_8859_3: + case RTL_TEXTENCODING_ISO_8859_1 : + case RTL_TEXTENCODING_MS_1252 : + case RTL_TEXTENCODING_APPLE_ROMAN : + case RTL_TEXTENCODING_IBM_850 : + case RTL_TEXTENCODING_ISO_8859_14 : + case RTL_TEXTENCODING_ISO_8859_15 : + //fill with western languages + aLanguages[0] = LANGUAGE_GERMAN; + aLanguages[1] = LANGUAGE_FRENCH; + aLanguages[2] = LANGUAGE_ITALIAN; + aLanguages[3] = LANGUAGE_SPANISH; + break; + + case RTL_TEXTENCODING_IBM_865 : + //scandinavian + aLanguages[0] = LANGUAGE_FINNISH; + aLanguages[1] = LANGUAGE_NORWEGIAN; + aLanguages[2] = LANGUAGE_SWEDISH; + aLanguages[3] = LANGUAGE_DANISH; + break; + + case RTL_TEXTENCODING_ISO_8859_10 : + case RTL_TEXTENCODING_ISO_8859_13 : + case RTL_TEXTENCODING_ISO_8859_2 : + case RTL_TEXTENCODING_IBM_852 : + case RTL_TEXTENCODING_MS_1250 : + case RTL_TEXTENCODING_APPLE_CENTEURO : + aLanguages[0] = LANGUAGE_POLISH; + aLanguages[1] = LANGUAGE_CZECH; + aLanguages[2] = LANGUAGE_HUNGARIAN; + aLanguages[3] = LANGUAGE_SLOVAK; + break; + + case RTL_TEXTENCODING_ISO_8859_4 : + case RTL_TEXTENCODING_IBM_775 : + case RTL_TEXTENCODING_MS_1257 : + aLanguages[0] = LANGUAGE_LATVIAN ; + aLanguages[1] = LANGUAGE_LITHUANIAN; + aLanguages[2] = LANGUAGE_ESTONIAN ; + break; + + case RTL_TEXTENCODING_IBM_863 : aLanguages[0] = LANGUAGE_FRENCH_CANADIAN; break; + case RTL_TEXTENCODING_APPLE_FARSI : aLanguages[0] = LANGUAGE_FARSI; break; + case RTL_TEXTENCODING_APPLE_ROMANIAN:aLanguages[0] = LANGUAGE_ROMANIAN; break; + + case RTL_TEXTENCODING_IBM_861 : + case RTL_TEXTENCODING_APPLE_ICELAND : + aLanguages[0] = LANGUAGE_ICELANDIC; + break; + + case RTL_TEXTENCODING_APPLE_CROATIAN:aLanguages[0] = LANGUAGE_CROATIAN; break; + + case RTL_TEXTENCODING_IBM_437 : + case RTL_TEXTENCODING_ASCII_US : aLanguages[0] = LANGUAGE_ENGLISH; break; + + case RTL_TEXTENCODING_IBM_862 : + case RTL_TEXTENCODING_MS_1255 : + case RTL_TEXTENCODING_APPLE_HEBREW : + case RTL_TEXTENCODING_ISO_8859_8 : + aLanguages[0] = LANGUAGE_HEBREW; + break; + + case RTL_TEXTENCODING_IBM_857 : + case RTL_TEXTENCODING_MS_1254 : + case RTL_TEXTENCODING_APPLE_TURKISH: + case RTL_TEXTENCODING_ISO_8859_9 : + aLanguages[0] = LANGUAGE_TURKISH; + break; + + case RTL_TEXTENCODING_IBM_860 : + aLanguages[0] = LANGUAGE_PORTUGUESE; + break; + + case RTL_TEXTENCODING_IBM_869 : + case RTL_TEXTENCODING_MS_1253 : + case RTL_TEXTENCODING_APPLE_GREEK : + case RTL_TEXTENCODING_ISO_8859_7 : + case RTL_TEXTENCODING_IBM_737 : + aLanguages[0] = LANGUAGE_GREEK; + break; + + case RTL_TEXTENCODING_KOI8_R : + case RTL_TEXTENCODING_ISO_8859_5 : + case RTL_TEXTENCODING_IBM_855 : + case RTL_TEXTENCODING_MS_1251 : + case RTL_TEXTENCODING_IBM_866 : + case RTL_TEXTENCODING_APPLE_CYRILLIC : + aLanguages[0] = LANGUAGE_RUSSIAN; + break; + + case RTL_TEXTENCODING_APPLE_UKRAINIAN: + case RTL_TEXTENCODING_KOI8_U: + aLanguages[0] = LANGUAGE_UKRAINIAN; + break; + + case RTL_TEXTENCODING_IBM_864 : + case RTL_TEXTENCODING_MS_1256 : + case RTL_TEXTENCODING_ISO_8859_6 : + case RTL_TEXTENCODING_APPLE_ARABIC : + aLanguages[0] = LANGUAGE_ARABIC_SAUDI_ARABIA; + break; + + case RTL_TEXTENCODING_APPLE_CHINTRAD : + case RTL_TEXTENCODING_MS_950 : + case RTL_TEXTENCODING_GBT_12345 : + case RTL_TEXTENCODING_BIG5 : + case RTL_TEXTENCODING_EUC_TW : + case RTL_TEXTENCODING_BIG5_HKSCS : + aLanguages[0] = LANGUAGE_CHINESE_TRADITIONAL; + break; + + case RTL_TEXTENCODING_EUC_JP : + case RTL_TEXTENCODING_ISO_2022_JP : + case RTL_TEXTENCODING_JIS_X_0201 : + case RTL_TEXTENCODING_JIS_X_0208 : + case RTL_TEXTENCODING_JIS_X_0212 : + case RTL_TEXTENCODING_APPLE_JAPANESE : + case RTL_TEXTENCODING_MS_932 : + case RTL_TEXTENCODING_SHIFT_JIS : + aLanguages[0] = LANGUAGE_JAPANESE; + break; + + case RTL_TEXTENCODING_GB_2312 : + case RTL_TEXTENCODING_MS_936 : + case RTL_TEXTENCODING_GBK : + case RTL_TEXTENCODING_GB_18030 : + case RTL_TEXTENCODING_APPLE_CHINSIMP : + case RTL_TEXTENCODING_EUC_CN : + case RTL_TEXTENCODING_ISO_2022_CN : + aLanguages[0] = LANGUAGE_CHINESE_SIMPLIFIED; + break; + + case RTL_TEXTENCODING_APPLE_KOREAN : + case RTL_TEXTENCODING_MS_949 : + case RTL_TEXTENCODING_EUC_KR : + case RTL_TEXTENCODING_ISO_2022_KR : + case RTL_TEXTENCODING_MS_1361 : + aLanguages[0] = LANGUAGE_KOREAN; + break; + + case RTL_TEXTENCODING_APPLE_THAI : + case RTL_TEXTENCODING_MS_874 : + case RTL_TEXTENCODING_TIS_620 : + aLanguages[0] = LANGUAGE_THAI; + break; + default: aLanguages[0] = Application::GetSettings().GetUILanguageTag().getLanguageType(); + } + return aLanguages[0] != LANGUAGE_SYSTEM; +} +void SwSrcEditWindow::SetFont() +{ + OUString sFontName( + officecfg::Office::Common::Font::SourceViewFont::FontName::get(). + value_or(OUString())); + if(sFontName.isEmpty()) + { + LanguageType aLanguages[5] = + { + LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM + }; + vcl::Font aFont; + if(lcl_GetLanguagesForEncoding(m_eSourceEncoding, aLanguages)) + { + //TODO: check for multiple languages + aFont = OutputDevice::GetDefaultFont(DefaultFontType::FIXED, aLanguages[0], GetDefaultFontFlags::NONE, this); + } + else + aFont = OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, + Application::GetSettings().GetLanguageTag().getLanguageType(), GetDefaultFontFlags::NONE, this); + sFontName = aFont.GetFamilyName(); + } + const SvxFontListItem* pFontListItem = + static_cast<const SvxFontListItem* >(m_pSrcView->GetDocShell()->GetItem( SID_ATTR_CHAR_FONTLIST )); + const FontList* pList = pFontListItem->GetFontList(); + FontMetric aFontMetric = pList->Get(sFontName,WEIGHT_NORMAL, ITALIC_NONE); + + const vcl::Font& rFont = GetTextEngine()->GetFont(); + vcl::Font aFont(aFontMetric); + Size aSize(rFont.GetFontSize()); + //font height is stored in point and set in twip + aSize.setHeight( + officecfg::Office::Common::Font::SourceViewFont::FontHeight::get() * 20 ); + aFont.SetFontSize(m_pOutWin->LogicToPixel(aSize, MapMode(MapUnit::MapTwip))); + GetTextEngine()->SetFont( aFont ); + m_pOutWin->SetFont(aFont); +} + +void SwSrcEditWindow::SetTextEncoding(rtl_TextEncoding eEncoding) +{ + m_eSourceEncoding = eEncoding; + SetFont(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/envelp/envimg.cxx b/sw/source/uibase/envelp/envimg.cxx new file mode 100644 index 000000000..6f8494601 --- /dev/null +++ b/sw/source/uibase/envelp/envimg.cxx @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <editeng/paperinf.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <rtl/ustrbuf.hxx> +#include <unotools/useroptions.hxx> +#include <swmodule.hxx> +#include <swtypes.hxx> +#include <envimg.hxx> +#include <strings.hrc> + +#include <cmdid.h> + +#include <unomid.h> + +#ifdef _WIN32 +#define NEXTLINE "\r\n" +#else +#define NEXTLINE "\n" +#endif + +using namespace utl; +using namespace ::com::sun::star::uno; + + +SfxPoolItem* SwEnvItem::CreateDefault() { return new SwEnvItem; } + +OUString MakeSender() +{ + SvtUserOptions& rUserOpt = SW_MOD()->GetUserOptions(); + + const OUString sSenderToken(SwResId(STR_SENDER_TOKENS)); + if (sSenderToken.isEmpty()) + return OUString(); + + OUStringBuffer sRet; + sal_Int32 nSttPos = 0; + bool bLastLength = true; + do { + OUString sToken = sSenderToken.getToken( 0, ';', nSttPos ); + if (sToken == "COMPANY") + { + sal_Int32 nOldLen = sRet.getLength(); + sRet.append(rUserOpt.GetCompany()); + bLastLength = sRet.getLength() != nOldLen; + } + else if (sToken == "CR") + { + if(bLastLength) + sRet.append(NEXTLINE); + bLastLength = true; + } + else if (sToken == "FIRSTNAME") + sRet.append(rUserOpt.GetFirstName()); + else if (sToken == "LASTNAME") + sRet.append(rUserOpt.GetLastName()); + else if (sToken == "ADDRESS") + sRet.append(rUserOpt.GetStreet()); + else if (sToken == "COUNTRY") + sRet.append(rUserOpt.GetCountry()); + else if (sToken == "POSTALCODE") + sRet.append(rUserOpt.GetZip()); + else if (sToken == "CITY") + sRet.append(rUserOpt.GetCity()); + else if (sToken == "STATEPROV") + sRet.append(rUserOpt.GetState()); + else if (!sToken.isEmpty()) //spaces + sRet.append(sToken); + } while (nSttPos>=0); + return sRet.makeStringAndClear(); +} + +SwEnvItem::SwEnvItem() : + SfxPoolItem(FN_ENVELOP) +{ + m_bSend = true; + m_aSendText = MakeSender(); + m_nSendFromLeft = 566; // 1 cm + m_nSendFromTop = 566; // 1 cm + Size aEnvSz = SvxPaperInfo::GetPaperSize(PAPER_ENV_C65); + m_nWidth = aEnvSz.Width(); + m_nHeight = aEnvSz.Height(); + m_eAlign = ENV_HOR_LEFT; + m_bPrintFromAbove = true; + m_nShiftRight = 0; + m_nShiftDown = 0; + + m_nAddrFromLeft = std::max(m_nWidth, m_nHeight) / 2; + m_nAddrFromTop = std::min(m_nWidth, m_nHeight) / 2; +} + +SwEnvItem& SwEnvItem::operator =(const SwEnvItem& rItem) +{ + m_aAddrText = rItem.m_aAddrText; + m_bSend = rItem.m_bSend; + m_aSendText = rItem.m_aSendText; + m_nSendFromLeft = rItem.m_nSendFromLeft; + m_nSendFromTop = rItem.m_nSendFromTop; + m_nAddrFromLeft = rItem.m_nAddrFromLeft; + m_nAddrFromTop = rItem.m_nAddrFromTop; + m_nWidth = rItem.m_nWidth; + m_nHeight = rItem.m_nHeight; + m_eAlign = rItem.m_eAlign; + m_bPrintFromAbove = rItem.m_bPrintFromAbove; + m_nShiftRight = rItem.m_nShiftRight; + m_nShiftDown = rItem.m_nShiftDown; + return *this; +} + +bool SwEnvItem::operator ==(const SfxPoolItem& rItem) const +{ + const SwEnvItem& rEnv = static_cast<const SwEnvItem&>( rItem); + + return m_aAddrText == rEnv.m_aAddrText && + m_bSend == rEnv.m_bSend && + m_aSendText == rEnv.m_aSendText && + m_nSendFromLeft == rEnv.m_nSendFromLeft && + m_nSendFromTop == rEnv.m_nSendFromTop && + m_nAddrFromLeft == rEnv.m_nAddrFromLeft && + m_nAddrFromTop == rEnv.m_nAddrFromTop && + m_nWidth == rEnv.m_nWidth && + m_nHeight == rEnv.m_nHeight && + m_eAlign == rEnv.m_eAlign && + m_bPrintFromAbove == rEnv.m_bPrintFromAbove && + m_nShiftRight == rEnv.m_nShiftRight && + m_nShiftDown == rEnv.m_nShiftDown; +} + +SwEnvItem* SwEnvItem::Clone(SfxItemPool*) const +{ + return new SwEnvItem(*this); +} + +SwEnvCfgItem::SwEnvCfgItem() : + ConfigItem("Office.Writer/Envelope") +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case 0: pValues[nProp] >>= m_aEnvItem.m_aAddrText; break;// "Inscription/Addressee", + case 1: pValues[nProp] >>= m_aEnvItem.m_aSendText; break;// "Inscription/Sender", + case 2: m_aEnvItem.m_bSend = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Inscription/UseSender", + case 3: + pValues[nProp] >>= m_aEnvItem.m_nAddrFromLeft;// "Format/AddresseeFromLeft", + m_aEnvItem.m_nAddrFromLeft = convertMm100ToTwip(m_aEnvItem.m_nAddrFromLeft); + break; + case 4: + pValues[nProp] >>= m_aEnvItem.m_nAddrFromTop; // "Format/AddresseeFromTop", + m_aEnvItem.m_nAddrFromTop = convertMm100ToTwip(m_aEnvItem.m_nAddrFromTop); + break; + case 5: + pValues[nProp] >>= m_aEnvItem.m_nSendFromLeft; // "Format/SenderFromLeft", + m_aEnvItem.m_nSendFromLeft = convertMm100ToTwip(m_aEnvItem.m_nSendFromLeft); + break; + case 6: + pValues[nProp] >>= m_aEnvItem.m_nSendFromTop;// "Format/SenderFromTop", + m_aEnvItem.m_nSendFromTop = convertMm100ToTwip(m_aEnvItem.m_nSendFromTop); + break; + case 7: + pValues[nProp] >>= m_aEnvItem.m_nWidth; // "Format/Width", + m_aEnvItem.m_nWidth = convertMm100ToTwip(m_aEnvItem.m_nWidth); + break; + case 8: + pValues[nProp] >>= m_aEnvItem.m_nHeight; // "Format/Height", + m_aEnvItem.m_nHeight = convertMm100ToTwip(m_aEnvItem.m_nHeight); + break; + case 9: + { + sal_Int32 nTemp = 0; + pValues[nProp] >>= nTemp; m_aEnvItem.m_eAlign = static_cast<SwEnvAlign>(nTemp); break;// "Print/Alignment", + } + case 10: m_aEnvItem.m_bPrintFromAbove = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Print/FromAbove", + case 11: + pValues[nProp] >>= m_aEnvItem.m_nShiftRight; + m_aEnvItem.m_nShiftRight = convertMm100ToTwip(m_aEnvItem.m_nShiftRight);// "Print/Right", + break; + case 12: + pValues[nProp] >>= m_aEnvItem.m_nShiftDown; + m_aEnvItem.m_nShiftDown = convertMm100ToTwip(m_aEnvItem.m_nShiftDown); + break;// "Print/Down" + } + } + } + } +} + +SwEnvCfgItem::~SwEnvCfgItem() +{ +} + +void SwEnvCfgItem::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= m_aEnvItem.m_aAddrText; break;// "Inscription/Addressee", + case 1: pValues[nProp] <<= m_aEnvItem.m_aSendText; break;// "Inscription/Sender", + case 2: pValues[nProp] <<= m_aEnvItem.m_bSend; break;// "Inscription/UseSender", + case 3: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nAddrFromLeft)) ; break;// "Format/AddresseeFromLeft", + case 4: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nAddrFromTop)) ; break;// "Format/AddresseeFromTop", + case 5: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nSendFromLeft)) ; break;// "Format/SenderFromLeft", + case 6: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nSendFromTop)) ; break;// "Format/SenderFromTop", + case 7: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nWidth)) ; break;// "Format/Width", + case 8: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nHeight)) ; break;// "Format/Height", + case 9: pValues[nProp] <<= sal_Int32(m_aEnvItem.m_eAlign); break;// "Print/Alignment", + case 10: pValues[nProp] <<= m_aEnvItem.m_bPrintFromAbove; break;// "Print/FromAbove", + case 11: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nShiftRight));break; // "Print/Right", + case 12: pValues[nProp] <<= static_cast <sal_Int32>(convertTwipToMm100(m_aEnvItem.m_nShiftDown)); break;// "Print/Down" + } + } + PutProperties(aNames, aValues); +} + +void SwEnvCfgItem::Notify( const css::uno::Sequence< OUString >& ) {} + +Sequence<OUString> SwEnvCfgItem::GetPropertyNames() +{ + static const char* aPropNames[] = + { + "Inscription/Addressee", // 0 + "Inscription/Sender", // 1 + "Inscription/UseSender", // 2 + "Format/AddresseeFromLeft", // 3 + "Format/AddresseeFromTop", // 4 + "Format/SenderFromLeft", // 5 + "Format/SenderFromTop", // 6 + "Format/Width", // 7 + "Format/Height", // 8 + "Print/Alignment", // 9 + "Print/FromAbove", // 10 + "Print/Right", // 11 + "Print/Down" // 12 + }; + const int nCount = 13; + Sequence<OUString> aNames(nCount); + OUString* pNames = aNames.getArray(); + + for(int i = 0; i < nCount; i++) + pNames[i] = OUString::createFromAscii(aPropNames[i]); + + return aNames; +} + +bool SwEnvItem::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const +{ + bool bRet = true; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_ENV_ADDR_TEXT : rVal <<= m_aAddrText; break; + case MID_ENV_SEND : rVal <<= m_bSend; break; + case MID_SEND_TEXT : rVal <<= m_aSendText; break; + case MID_ENV_ADDR_FROM_LEFT : rVal <<= m_nAddrFromLeft; break; + case MID_ENV_ADDR_FROM_TOP : rVal <<= m_nAddrFromTop; break; + case MID_ENV_SEND_FROM_LEFT : rVal <<= m_nSendFromLeft; break; + case MID_ENV_SEND_FROM_TOP : rVal <<= m_nSendFromTop; break; + case MID_ENV_WIDTH : rVal <<= m_nWidth; break; + case MID_ENV_HEIGHT : rVal <<= m_nHeight; break; + case MID_ENV_ALIGN : rVal <<= static_cast<sal_Int16>(m_eAlign); break; + case MID_ENV_PRINT_FROM_ABOVE : rVal <<= m_bPrintFromAbove; break; + case MID_ENV_SHIFT_RIGHT : rVal <<= m_nShiftRight; break; + case MID_ENV_SHIFT_DOWN : rVal <<= m_nShiftDown; break; + default: + OSL_FAIL("Wrong memberId"); + bRet = false; + } + return bRet; +} + +bool SwEnvItem::PutValue(const Any& rVal, sal_uInt8 nMemberId) +{ + bool bRet = false; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_ENV_ADDR_TEXT : bRet = (rVal >>= m_aAddrText); break; + case MID_ENV_SEND : bRet = (rVal >>= m_bSend); break; + case MID_SEND_TEXT : bRet = (rVal >>= m_aSendText); break; + case MID_ENV_ADDR_FROM_LEFT : bRet = (rVal >>= m_nAddrFromLeft); break; + case MID_ENV_ADDR_FROM_TOP : bRet = (rVal >>= m_nAddrFromTop); break; + case MID_ENV_SEND_FROM_LEFT : bRet = (rVal >>= m_nSendFromLeft); break; + case MID_ENV_SEND_FROM_TOP : bRet = (rVal >>= m_nSendFromTop); break; + case MID_ENV_WIDTH : bRet = (rVal >>= m_nWidth); break; + case MID_ENV_HEIGHT : bRet = (rVal >>= m_nHeight); break; + case MID_ENV_ALIGN : + { + sal_Int16 nTemp = 0; + bRet = (rVal >>= nTemp); + if (bRet) + m_eAlign = SwEnvAlign(nTemp); + break; + } + case MID_ENV_PRINT_FROM_ABOVE : bRet = (rVal >>= m_bPrintFromAbove); break; + case MID_ENV_SHIFT_RIGHT : bRet = (rVal >>= m_nShiftRight); break; + case MID_ENV_SHIFT_DOWN : bRet = (rVal >>= m_nShiftDown); break; + default: + OSL_FAIL("Wrong memberId"); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/envelp/labelcfg.cxx b/sw/source/uibase/envelp/labelcfg.cxx new file mode 100644 index 000000000..59c252be0 --- /dev/null +++ b/sw/source/uibase/envelp/labelcfg.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <config_folders.h> + +#include <swtypes.hxx> +#include <labelcfg.hxx> +#include <rtl/bootstrap.hxx> +#include <unotools/configpaths.hxx> +#include <xmlreader/xmlreader.hxx> +#include <comphelper/sequence.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/beans/PropertyValue.hpp> + +using namespace utl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +static void lcl_assertEndingItem(xmlreader::XmlReader& reader) +{ + int nsId; + xmlreader::Span name; + xmlreader::XmlReader::Result res; + res = reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId); + assert(res == xmlreader::XmlReader::Result::End); + (void) res; +} + +static OUString lcl_getValue(xmlreader::XmlReader& reader, + const xmlreader::Span& span) +{ + int nsId; + xmlreader::Span name; + xmlreader::XmlReader::Result res; + res = reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId); + assert(res == xmlreader::XmlReader::Result::Begin && name == span); + res = reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId); + assert(res == xmlreader::XmlReader::Result::Text); + (void) res; (void) span; + OUString sTmp = name.convertFromUtf8(); + lcl_assertEndingItem(reader); + return sTmp; +} + +static Sequence<OUString> lcl_CreatePropertyNames(const OUString& rPrefix) +{ + Sequence<OUString> aProperties(2); + OUString* pProperties = aProperties.getArray(); + std::fill(aProperties.begin(), aProperties.end(), rPrefix); + + pProperties[ 0] += "Name"; + pProperties[ 1] += "Measure"; + return aProperties; +} + +SwLabelConfig::SwLabelConfig() : + ConfigItem("Office.Labels/Manufacturer") +{ + OUString uri("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/labels/labels.xml"); + rtl::Bootstrap::expandMacros(uri); + xmlreader::XmlReader reader(uri); + int nsId; + xmlreader::Span name; + xmlreader::XmlReader::Result res; + OUString sManufacturer; + OUString sName; + OUString sMeasure; + + // fill m_aLabels and m_aManufacturers with the predefined labels + res = reader.nextItem( + xmlreader::XmlReader::Text::NONE, &name, &nsId); + assert( + res == xmlreader::XmlReader::Result::Begin + && name == "manufacturers"); + res = reader.nextItem( + xmlreader::XmlReader::Text::NONE, &name, &nsId); + while (res != xmlreader::XmlReader::Result::End) + { + // Opening manufacturer + assert( + res == xmlreader::XmlReader::Result::Begin + && name == "manufacturer"); + // Get the name + (void)reader.nextAttribute(&nsId, &name); + assert( + nsId == xmlreader::XmlReader::NAMESPACE_NONE + && name == "name"); + sManufacturer = reader.getAttributeValue(false).convertFromUtf8(); + + for(;;) { + // Opening label or ending manufacturer + res = reader.nextItem( + xmlreader::XmlReader::Text::NONE, &name, &nsId); + if (res == xmlreader::XmlReader::Result::End) + break; + assert( + res == xmlreader::XmlReader::Result::Begin + && name == "label"); + // Get name value + sName = lcl_getValue(reader, xmlreader::Span("name")); + // Get measure value + sMeasure = lcl_getValue(reader, xmlreader::Span("measure")); + // Ending label mark + lcl_assertEndingItem(reader); + if ( m_aLabels.find( sManufacturer ) == m_aLabels.end() ) + m_aManufacturers.push_back( sManufacturer ); + m_aLabels[sManufacturer][sName].m_aMeasure = sMeasure; + m_aLabels[sManufacturer][sName].m_bPredefined = true; + } + // Get next manufacturer or end + res = reader.nextItem( + xmlreader::XmlReader::Text::NONE, &name, &nsId); + }; + res = reader.nextItem( + xmlreader::XmlReader::Text::NONE, &name, &nsId); + assert(res == xmlreader::XmlReader::Result::Done); + + // add to m_aLabels and m_aManufacturers the custom labels + const Sequence<OUString>& rMan = GetNodeNames( OUString() ); + for ( const OUString& rManufacturer : rMan ) + { + const Sequence<OUString> aLabels = GetNodeNames( rManufacturer ); + for( const OUString& rLabel : aLabels ) + { + OUString sPrefix = rManufacturer + "/" + rLabel + "/"; + Sequence<OUString> aPropNames = lcl_CreatePropertyNames( sPrefix ); + Sequence<Any> aValues = GetProperties( aPropNames ); + const Any* pValues = aValues.getConstArray(); + if (aValues.getLength() >= 1) + if(pValues[0].hasValue()) + pValues[0] >>= sName; + if (aValues.getLength() >= 2) + if(pValues[1].hasValue()) + pValues[1] >>= sMeasure; + if ( m_aLabels.find( rManufacturer ) == m_aLabels.end() ) + m_aManufacturers.push_back( rManufacturer ); + m_aLabels[rManufacturer][sName].m_aMeasure = sMeasure; + m_aLabels[rManufacturer][sName].m_bPredefined = false; + } + } +} + +SwLabelConfig::~SwLabelConfig() +{ +} + +// the config item is not writable ?: +void SwLabelConfig::ImplCommit() {} + +void SwLabelConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +static std::unique_ptr<SwLabRec> lcl_CreateSwLabRec(const OUString& rType, const OUString& rMeasure, const OUString& rManufacturer) +{ + std::unique_ptr<SwLabRec> pNewRec(new SwLabRec); + pNewRec->m_aMake = rManufacturer; + pNewRec->m_nPWidth = 0; + pNewRec->m_nPHeight = 0; + pNewRec->m_aType = rType; + //all values are contained as colon-separated 1/100 mm values + //except for the continuous flag ('C'/'S') and nCols, nRows (sal_Int32) + sal_Int32 nTok{0}; + sal_Int32 nIdx{rMeasure.isEmpty() ? -1 : 0}; + while (nIdx>=0) + { + const OUString sToken(rMeasure.getToken(0, ';', nIdx)); + int nVal = sToken.toInt32(); + switch(nTok++) + { + case 0 : pNewRec->m_bCont = sToken[0] == 'C'; break; + case 1 : pNewRec->m_nHDist = convertMm100ToTwip(nVal); break; + case 2 : pNewRec->m_nVDist = convertMm100ToTwip(nVal); break; + case 3 : pNewRec->m_nWidth = convertMm100ToTwip(nVal); break; + case 4 : pNewRec->m_nHeight = convertMm100ToTwip(nVal); break; + case 5 : pNewRec->m_nLeft = convertMm100ToTwip(nVal); break; + case 6 : pNewRec->m_nUpper = convertMm100ToTwip(nVal); break; + case 7 : pNewRec->m_nCols = nVal; break; + case 8 : pNewRec->m_nRows = nVal; break; + case 9 : pNewRec->m_nPWidth = convertMm100ToTwip(nVal); break; + case 10 : pNewRec->m_nPHeight = convertMm100ToTwip(nVal); break; + } + } + // lines added for compatibility with custom label definitions saved before patch fdo#44516 + if (pNewRec->m_nPWidth == 0 || pNewRec->m_nPHeight == 0) + { + // old style definition (no paper dimensions), calculate probable values + pNewRec->m_nPWidth = 2 * pNewRec->m_nLeft + (pNewRec->m_nCols - 1) * pNewRec->m_nHDist + pNewRec->m_nWidth; + pNewRec->m_nPHeight = ( pNewRec->m_bCont ? pNewRec->m_nRows * pNewRec->m_nVDist : 2 * pNewRec->m_nUpper + (pNewRec->m_nRows - 1) * pNewRec->m_nVDist + pNewRec->m_nHeight ); + } + return pNewRec; +} + +static Sequence<PropertyValue> lcl_CreateProperties( + Sequence<OUString> const & rPropNames, OUString& rMeasure, const SwLabRec& rRec) +{ + const OUString* pNames = rPropNames.getConstArray(); + Sequence<PropertyValue> aRet(rPropNames.getLength()); + PropertyValue* pValues = aRet.getArray(); + OUString sColon(";"); + + for(sal_Int32 nProp = 0; nProp < rPropNames.getLength(); nProp++) + { + pValues[nProp].Name = pNames[nProp]; + switch(nProp) + { + case 0: pValues[nProp].Value <<= rRec.m_aType; break; + case 1: + { + rMeasure.clear(); + rMeasure += rRec.m_bCont ? OUStringLiteral( "C" ) : OUStringLiteral( "S" ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nHDist ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nVDist ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nWidth ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nHeight ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nLeft ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nUpper ) ); rMeasure += sColon; + rMeasure += OUString::number( rRec.m_nCols ); rMeasure += sColon; + rMeasure += OUString::number( rRec.m_nRows ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nPWidth ) ); rMeasure += sColon; + rMeasure += OUString::number( convertTwipToMm100( rRec.m_nPHeight ) ); + pValues[nProp].Value <<= rMeasure; + } + break; + } + } + return aRet; +} + +// function fills SwLabDlg with label definitions for manufacturer rManufacturer +void SwLabelConfig::FillLabels(const OUString& rManufacturer, SwLabRecs& rLabArr) +{ + if (m_aLabels.find(rManufacturer) == m_aLabels.end()) + return; + for (const auto& rEntry : m_aLabels[rManufacturer]) + rLabArr.push_back( lcl_CreateSwLabRec(rEntry.first, rEntry.second.m_aMeasure, rManufacturer) ); +} + +bool SwLabelConfig::HasLabel(const OUString& rManufacturer, const OUString& rType) +{ + return ( ( m_aLabels.find(rManufacturer) != m_aLabels.end() ) && + ( m_aLabels[rManufacturer].find(rType) != m_aLabels[rManufacturer].end() ) ); +} + +// label is always saved as a custom label +// predefined labels can NOT be overwritten by custom labels with same manufacturer/name +void SwLabelConfig::SaveLabel( const OUString& rManufacturer, + const OUString& rType, const SwLabRec& rRec ) +{ + OUString sFoundNode; + bool bManufacturerNodeFound; + if ( m_aLabels.find( rManufacturer ) == m_aLabels.end() || + !GetNodeNames( rManufacturer ).hasElements() ) + { + bManufacturerNodeFound = false; + // manufacturer node does not exist, add (and also to m_aManufacturers) + if ( !AddNode( OUString(), rManufacturer ) ) + { + OSL_FAIL("New configuration node could not be created"); + return ; + } + m_aManufacturers.push_back( rManufacturer ); + } + else + bManufacturerNodeFound = true; + + if ( !bManufacturerNodeFound || + m_aLabels[rManufacturer].find( rType ) == m_aLabels[rManufacturer].end() ) + { + // type does not yet exist, add to config + const Sequence<OUString> aLabels = GetNodeNames( rManufacturer ); + sal_Int32 nIndex = aLabels.getLength(); + OUString sPrefix( "Label" ); + sFoundNode = sPrefix + OUString::number( nIndex ); + while ( comphelper::findValue(aLabels, sFoundNode) != -1 ) + { + sFoundNode = sPrefix + OUString::number(nIndex++); + } + } + else + { + // get the appropriate node + OUString sManufacturer( wrapConfigurationElementName( rManufacturer ) ); + const Sequence<OUString> aLabels = GetNodeNames( sManufacturer ); + for (const OUString& rLabel : aLabels) + { + OUString sPrefix = sManufacturer + "/" + rLabel + "/"; + Sequence<OUString> aProperties { sPrefix }; + aProperties.getArray()[0] += "Name"; + Sequence<Any> aValues = GetProperties( aProperties ); + const Any* pValues = aValues.getConstArray(); + if ( pValues[0].hasValue() ) + { + OUString sTmp; + pValues[0] >>= sTmp; + if ( rType == sTmp ) + { + sFoundNode = rLabel; + break; + } + } + } + } + + OUString sPrefix = wrapConfigurationElementName( rManufacturer ) + + "/" + sFoundNode + "/"; + Sequence<OUString> aPropNames = lcl_CreatePropertyNames( sPrefix ); + OUString sMeasure; + Sequence<PropertyValue> aPropValues = lcl_CreateProperties( aPropNames, sMeasure, rRec ); + SetSetProperties( wrapConfigurationElementName( rManufacturer ), aPropValues ); + + //update m_aLabels + m_aLabels[rManufacturer][rType].m_aMeasure = sMeasure; + m_aLabels[rManufacturer][rType].m_bPredefined = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/envelp/labimg.cxx b/sw/source/uibase/envelp/labimg.cxx new file mode 100644 index 000000000..03a38a853 --- /dev/null +++ b/sw/source/uibase/envelp/labimg.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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/uno/Sequence.hxx> +#include <unotools/useroptions.hxx> +#include <swmodule.hxx> +#include <labimg.hxx> +#include <cmdid.h> + +using namespace utl; +using namespace ::com::sun::star::uno; + +SwLabItem::SwLabItem() : + + SfxPoolItem(FN_LABEL), + m_lLeft (0), + m_lUpper(0), + m_nCols (1), + m_nRows (1), + m_nCol (1), + m_nRow (1) +{ + m_bAddr = + m_bCont = + m_bSynchron = false; + m_bPage = true; + m_lHDist = + m_lVDist = + m_lWidth = + m_lHeight = + m_lPWidth = + m_lPHeight = 5669; // 10 cm +} + +SwLabItem& SwLabItem::operator =(const SwLabItem& rItem) +{ + m_bAddr = rItem.m_bAddr; + m_aWriting = rItem.m_aWriting; + m_bCont = rItem.m_bCont; + m_sDBName = rItem.m_sDBName; + m_aLstMake = rItem.m_aLstMake; + m_aLstType = rItem.m_aLstType; + m_aMake = rItem.m_aMake; + m_aType = rItem.m_aType; + m_bPage = rItem.m_bPage; + m_bSynchron = rItem.m_bSynchron; + m_nCol = rItem.m_nCol; + m_nRow = rItem.m_nRow; + m_lHDist = rItem.m_lHDist; + m_lVDist = rItem.m_lVDist; + m_lWidth = rItem.m_lWidth; + m_lHeight = rItem.m_lHeight; + m_lLeft = rItem.m_lLeft; + m_lUpper = rItem.m_lUpper; + m_nCols = rItem.m_nCols; + m_nRows = rItem.m_nRows; + m_lPWidth = rItem.m_lPWidth; + m_lPHeight = rItem.m_lPHeight; + m_aPrivFirstName = rItem.m_aPrivFirstName; + m_aPrivName = rItem.m_aPrivName; + m_aPrivShortCut = rItem.m_aPrivShortCut; + m_aPrivFirstName2 = rItem.m_aPrivFirstName2; + m_aPrivName2 = rItem.m_aPrivName2; + m_aPrivShortCut2 = rItem.m_aPrivShortCut2; + m_aPrivStreet = rItem.m_aPrivStreet; + m_aPrivZip = rItem.m_aPrivZip; + m_aPrivCity = rItem.m_aPrivCity; + m_aPrivCountry = rItem.m_aPrivCountry; + m_aPrivState = rItem.m_aPrivState; + m_aPrivTitle = rItem.m_aPrivTitle; + m_aPrivProfession = rItem.m_aPrivProfession; + m_aPrivPhone = rItem.m_aPrivPhone; + m_aPrivMobile = rItem.m_aPrivMobile; + m_aPrivFax = rItem.m_aPrivFax; + m_aPrivWWW = rItem.m_aPrivWWW; + m_aPrivMail = rItem.m_aPrivMail; + m_aCompCompany = rItem.m_aCompCompany; + m_aCompCompanyExt = rItem.m_aCompCompanyExt; + m_aCompSlogan = rItem.m_aCompSlogan; + m_aCompStreet = rItem.m_aCompStreet; + m_aCompZip = rItem.m_aCompZip; + m_aCompCity = rItem.m_aCompCity; + m_aCompCountry = rItem.m_aCompCountry; + m_aCompState = rItem.m_aCompState; + m_aCompPosition = rItem.m_aCompPosition; + m_aCompPhone = rItem.m_aCompPhone; + m_aCompMobile = rItem.m_aCompMobile; + m_aCompFax = rItem.m_aCompFax; + m_aCompWWW = rItem.m_aCompWWW; + m_aCompMail = rItem.m_aCompMail; + m_sGlossaryGroup = rItem.m_sGlossaryGroup; + m_sGlossaryBlockName = rItem.m_sGlossaryBlockName; + return *this; +} + +bool SwLabItem::operator ==(const SfxPoolItem& rItem) const +{ + const SwLabItem& rLab = static_cast<const SwLabItem&>( rItem); + + return m_bAddr == rLab.m_bAddr && + m_bCont == rLab.m_bCont && + m_bPage == rLab.m_bPage && + m_bSynchron == rLab.m_bSynchron && + m_nCol == rLab.m_nCol && + m_nRow == rLab.m_nRow && + m_lHDist == rLab.m_lHDist && + m_lVDist == rLab.m_lVDist && + m_lWidth == rLab.m_lWidth && + m_lHeight == rLab.m_lHeight && + m_lLeft == rLab.m_lLeft && + m_lUpper == rLab.m_lUpper && + m_nCols == rLab.m_nCols && + m_nRows == rLab.m_nRows && + m_lPWidth == rLab.m_lPWidth && + m_lPHeight == rLab.m_lPHeight&& + m_aWriting == rLab.m_aWriting&& + m_aMake == rLab.m_aMake && + m_aType == rLab.m_aType && + m_aLstMake == rLab.m_aLstMake&& + m_aLstType == rLab.m_aLstType&& + m_sDBName == rLab.m_sDBName && + m_aPrivFirstName == rLab.m_aPrivFirstName&& + m_aPrivName == rLab.m_aPrivName&& + m_aPrivShortCut == rLab.m_aPrivShortCut&& + m_aPrivFirstName2 == rLab.m_aPrivFirstName2&& + m_aPrivName2 == rLab.m_aPrivName2&& + m_aPrivShortCut2 == rLab.m_aPrivShortCut2&& + m_aPrivStreet == rLab.m_aPrivStreet&& + m_aPrivZip == rLab.m_aPrivZip&& + m_aPrivCity == rLab.m_aPrivCity&& + m_aPrivCountry == rLab.m_aPrivCountry&& + m_aPrivState == rLab.m_aPrivState&& + m_aPrivTitle == rLab.m_aPrivTitle&& + m_aPrivProfession == rLab.m_aPrivProfession&& + m_aPrivPhone == rLab.m_aPrivPhone&& + m_aPrivMobile == rLab.m_aPrivMobile&& + m_aPrivFax == rLab.m_aPrivFax&& + m_aPrivWWW == rLab.m_aPrivWWW&& + m_aPrivMail == rLab.m_aPrivMail&& + m_aCompCompany == rLab.m_aCompCompany&& + m_aCompCompanyExt == rLab.m_aCompCompanyExt&& + m_aCompSlogan == rLab.m_aCompSlogan&& + m_aCompStreet == rLab.m_aCompStreet&& + m_aCompZip == rLab.m_aCompZip&& + m_aCompCity == rLab.m_aCompCity&& + m_aCompCountry == rLab.m_aCompCountry&& + m_aCompState == rLab.m_aCompState&& + m_aCompPosition == rLab.m_aCompPosition&& + m_aCompPhone == rLab.m_aCompPhone&& + m_aCompMobile == rLab.m_aCompMobile&& + m_aCompFax == rLab.m_aCompFax&& + m_aCompWWW == rLab.m_aCompWWW&& + m_aCompMail == rLab.m_aCompMail && + m_sGlossaryGroup == rLab.m_sGlossaryGroup && + m_sGlossaryBlockName == rLab.m_sGlossaryBlockName; +} + +SwLabItem* SwLabItem::Clone(SfxItemPool*) const +{ + return new SwLabItem(*this); +} + +Sequence<OUString> SwLabCfgItem::GetPropertyNames() const +{ + static const char* aLabelPropNames[] = + { + "Medium/Continuous", // 0 + "Medium/Brand", // 1 + "Medium/Type", // 2 + "Format/Column", // 3 + "Format/Row", // 4 + "Format/HorizontalDistance",// 5 + "Format/VerticalDistance", // 6 + "Format/Width", // 7 + "Format/Height", // 8 + "Format/LeftMargin", // 9 + "Format/TopMargin", //10 + "Format/PageWidth", //11 + "Format/PageHeight", //12 + "Option/Synchronize", //13 + "Option/Page", //14 + "Option/Column", //15 + "Option/Row", //16 + "Inscription/UseAddress", //17 + "Inscription/Address", //18 + "Inscription/Database" //19 + }; + static const char* aBusinessPropNames[] = + { + "PrivateAddress/FirstName", // 0 + "PrivateAddress/Name", // 1 + "PrivateAddress/ShortCut", // 2 + "PrivateAddress/SecondFirstName", // 3 + "PrivateAddress/SecondName", // 4 + "PrivateAddress/SecondShortCut", // 5 + "PrivateAddress/Street", // 6 + "PrivateAddress/Zip", // 7 + "PrivateAddress/City", // 8 + "PrivateAddress/Country", // 9 + "PrivateAddress/State", // 10 + "PrivateAddress/Title", // 11 + "PrivateAddress/Profession", // 12 + "PrivateAddress/Phone", // 13 + "PrivateAddress/Mobile", // 14 + "PrivateAddress/Fax", // 15 + "PrivateAddress/WebAddress", // 16 + "PrivateAddress/Email", // 17 + "BusinessAddress/Company", // 18 + "BusinessAddress/CompanyExt", // 19 + "BusinessAddress/Slogan", // 20 + "BusinessAddress/Street", // 21 + "BusinessAddress/Zip", // 22 + "BusinessAddress/City", // 23 + "BusinessAddress/Country", // 24 + "BusinessAddress/State", // 25 + "BusinessAddress/Position", // 26 + "BusinessAddress/Phone", // 27 + "BusinessAddress/Mobile", // 28 + "BusinessAddress/Fax", // 29 + "BusinessAddress/WebAddress", // 30 + "BusinessAddress/Email", // 31 + "AutoText/Group", // 32 + "AutoText/Block" // 33 + }; + const int nBusinessCount = bIsLabel ? 0 : 34; + const int nLabelCount = bIsLabel ? 20 : 17; + Sequence<OUString> aNames(nBusinessCount + nLabelCount); + OUString* pNames = aNames.getArray(); + int nIndex = 0; + for(int nLabel = 0; nLabel < nLabelCount; nLabel++) + pNames[nIndex++] = OUString::createFromAscii(aLabelPropNames[nLabel]); + for(int nBusiness = 0; nBusiness < nBusinessCount; nBusiness++) + pNames[nIndex++] = OUString::createFromAscii(aBusinessPropNames[nBusiness]); + return aNames; +} + +SwLabCfgItem::SwLabCfgItem(bool bLabel) : + ConfigItem(bLabel ? OUString("Office.Writer/Label") : OUString("Office.Writer/BusinessCard")), + bIsLabel(bLabel) +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues = GetProperties(aNames); + EnableNotification(aNames); + const Any* pValues = aValues.getConstArray(); + OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed"); + bool bNoConfigValues = true; + if(aValues.getLength() == aNames.getLength()) + { + for(int nProp = 0, nProperty = 0; nProp < aNames.getLength(); nProp++, nProperty++) + { + + if(pValues[nProp].hasValue()) + { + //to have a contiuous switch an offset is added + if(nProp == 17 && !bIsLabel) + nProperty += 3; + if(nProperty >= 20) + bNoConfigValues = false; + switch(nProperty) + { + case 0: aItem.m_bCont = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Medium/Continuous", + case 1: pValues[nProp] >>= aItem.m_aMake; break;// "Medium/Brand", + case 2: pValues[nProp] >>= aItem.m_aType; break;// "Medium/Type", + case 3: pValues[nProp] >>= aItem.m_nCols; break;// "Format/Column", + case 4: pValues[nProp] >>= aItem.m_nRows; break;// "Format/Row", + case 5: + pValues[nProp] >>= aItem.m_lHDist; + aItem.m_lHDist = convertMm100ToTwip(aItem.m_lHDist); + break;// "Format/HorizontalDistance", + case 6: + pValues[nProp] >>= aItem.m_lVDist; + aItem.m_lVDist = convertMm100ToTwip(aItem.m_lVDist); + break;// "Format/VerticalDistance", + case 7: + pValues[nProp] >>= aItem.m_lWidth; + aItem.m_lWidth = convertMm100ToTwip(aItem.m_lWidth); + break;// "Format/Width", + case 8: + pValues[nProp] >>= aItem.m_lHeight; + aItem.m_lHeight = convertMm100ToTwip(aItem.m_lHeight); + break;// "Format/Height", + case 9: + pValues[nProp] >>= aItem.m_lLeft; + aItem.m_lLeft = convertMm100ToTwip(aItem.m_lLeft); + break;// "Format/LeftMargin", + case 10: + pValues[nProp] >>= aItem.m_lUpper; + aItem.m_lUpper = convertMm100ToTwip(aItem.m_lUpper); + break;// "Format/TopMargin", + case 11: + pValues[nProp] >>= aItem.m_lPWidth; + aItem.m_lPWidth = convertMm100ToTwip(aItem.m_lPWidth); + break;// "Format/PageWidth", + case 12: + pValues[nProp] >>= aItem.m_lPHeight; + aItem.m_lPHeight = convertMm100ToTwip(aItem.m_lPHeight); + break;// "Format/PageHeight", + case 13: aItem.m_bSynchron = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Option/Synchronize", + case 14: aItem.m_bPage = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Option/Page", + case 15: pValues[nProp] >>= aItem.m_nCol; break;// "Option/Column", + case 16: pValues[nProp] >>= aItem.m_nRow; break;// "Option/Row" + case 17: aItem.m_bAddr = *o3tl::doAccess<bool>(pValues[nProp]); break;// "Inscription/UseAddress", + case 18: pValues[nProp] >>= aItem.m_aWriting; break;// "Inscription/Address", + case 19: pValues[nProp] >>= aItem.m_sDBName; break;// "Inscription/Database" + case 20: pValues[nProp] >>= aItem.m_aPrivFirstName; break;// "PrivateAddress/FirstName", + case 21: pValues[nProp] >>= aItem.m_aPrivName; break;// "PrivateAddress/Name", + case 22: pValues[nProp] >>= aItem.m_aPrivShortCut; break;// "PrivateAddress/ShortCut", + case 23: pValues[nProp] >>= aItem.m_aPrivFirstName2; break;// "PrivateAddress/SecondFirstName", + case 24: pValues[nProp] >>= aItem.m_aPrivName2; break;// "PrivateAddress/SecondName", + case 25: pValues[nProp] >>= aItem.m_aPrivShortCut2; break;// "PrivateAddress/SecondShortCut", + case 26: pValues[nProp] >>= aItem.m_aPrivStreet; break;// "PrivateAddress/Street", + case 27: pValues[nProp] >>= aItem.m_aPrivZip; break;// "PrivateAddress/Zip", + case 28: pValues[nProp] >>= aItem.m_aPrivCity; break;// "PrivateAddress/City", + case 29: pValues[nProp] >>= aItem.m_aPrivCountry; break;// "PrivateAddress/Country", + case 30: pValues[nProp] >>= aItem.m_aPrivState; break;// "PrivateAddress/State", + case 31: pValues[nProp] >>= aItem.m_aPrivTitle; break;// "PrivateAddress/Title", + case 32: pValues[nProp] >>= aItem.m_aPrivProfession; break;// "PrivateAddress/Profession", + case 33: pValues[nProp] >>= aItem.m_aPrivPhone; break;// "PrivateAddress/Phone", + case 34: pValues[nProp] >>= aItem.m_aPrivMobile; break;// "PrivateAddress/Mobile", + case 35: pValues[nProp] >>= aItem.m_aPrivFax; break;// "PrivateAddress/Fax", + case 36: pValues[nProp] >>= aItem.m_aPrivWWW; break;// "PrivateAddress/WebAddress", + case 37: pValues[nProp] >>= aItem.m_aPrivMail; break;// "PrivateAddress/Email", + case 38: pValues[nProp] >>= aItem.m_aCompCompany; break;// "BusinessAddress/Company", + case 39: pValues[nProp] >>= aItem.m_aCompCompanyExt; break;// "BusinessAddress/CompanyExt", + case 40: pValues[nProp] >>= aItem.m_aCompSlogan; break;// "BusinessAddress/Slogan", + case 41: pValues[nProp] >>= aItem.m_aCompStreet; break;// "BusinessAddress/Street", + case 42: pValues[nProp] >>= aItem.m_aCompZip; break;// "BusinessAddress/Zip", + case 43: pValues[nProp] >>= aItem.m_aCompCity; break;// "BusinessAddress/City", + case 44: pValues[nProp] >>= aItem.m_aCompCountry; break;// "BusinessAddress/Country", + case 45: pValues[nProp] >>= aItem.m_aCompState; break;// "BusinessAddress/State", + case 46: pValues[nProp] >>= aItem.m_aCompPosition; break;// "BusinessAddress/Position", + case 47: pValues[nProp] >>= aItem.m_aCompPhone; break;// "BusinessAddress/Phone", + case 48: pValues[nProp] >>= aItem.m_aCompMobile; break;// "BusinessAddress/Mobile", + case 49: pValues[nProp] >>= aItem.m_aCompFax; break;// "BusinessAddress/Fax", + case 50: pValues[nProp] >>= aItem.m_aCompWWW; break;// "BusinessAddress/WebAddress", + case 51: pValues[nProp] >>= aItem.m_aCompMail; break;// "BusinessAddress/Email", + case 52: pValues[nProp] >>= aItem.m_sGlossaryGroup; break;// "AutoText/Group" + case 53: pValues[nProp] >>= aItem.m_sGlossaryBlockName; break;// "AutoText/Block" + } + } + } + } + + if(bIsLabel || !bNoConfigValues) + return; + + SvtUserOptions& rUserOpt = SW_MOD()->GetUserOptions(); + aItem.m_aPrivFirstName = rUserOpt.GetFirstName(); + aItem.m_aPrivName = rUserOpt.GetLastName(); + aItem.m_aPrivShortCut = rUserOpt.GetID(); + aItem.m_aCompCompany = rUserOpt.GetCompany(); + aItem.m_aCompStreet = aItem.m_aPrivStreet = rUserOpt.GetStreet(); + + aItem.m_aCompCountry = aItem.m_aPrivCountry = rUserOpt.GetCountry(); + aItem.m_aCompZip = aItem.m_aPrivZip= rUserOpt.GetZip(); + aItem.m_aCompCity = aItem.m_aPrivCity = rUserOpt.GetCity(); + aItem.m_aPrivTitle = rUserOpt.GetTitle(); + aItem.m_aCompPosition = rUserOpt.GetPosition(); + aItem.m_aPrivPhone = rUserOpt.GetTelephoneHome(); + aItem.m_aCompPhone = rUserOpt.GetTelephoneWork(); + aItem.m_aCompFax = aItem.m_aPrivFax = rUserOpt.GetFax(); + aItem.m_aCompMail = aItem.m_aPrivMail = rUserOpt.GetEmail(); + aItem.m_aCompState = aItem.m_aPrivState = rUserOpt.GetState(); + aItem.m_bSynchron = true; + SetModified(); + +} + +void SwLabCfgItem::Notify( const css::uno::Sequence< OUString >& ) {} + +void SwLabCfgItem::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0, nProperty = 0; nProp < aNames.getLength(); nProp++, nProperty++) + { + //to have a contiuous switch an offset is added + if(nProp == 17 && !bIsLabel) + nProperty += 3; + switch(nProperty) + { + case 0: pValues[nProp] <<= aItem.m_bCont; break;// "Medium/Continuous", + case 1: pValues[nProp] <<= aItem.m_aMake; break;// "Medium/Brand", + case 2: pValues[nProp] <<= aItem.m_aType; break;// "Medium/Type", + case 3: pValues[nProp] <<= aItem.m_nCols; break;// "Format/Column", + case 4: pValues[nProp] <<= aItem.m_nRows; break;// "Format/Row", + case 5: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lHDist));break;// "Format/HorizontalDistance", + case 6: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lVDist));break;// "Format/VerticalDistance", + case 7: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lWidth)); break;// "Format/Width", + case 8: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lHeight)); break;// "Format/Height", + case 9: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lLeft)); break;// "Format/LeftMargin", + case 10: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lUpper)); break;// "Format/TopMargin", + case 11: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lPWidth)); break;// "Format/Page Width", + case 12: pValues[nProp] <<= static_cast<sal_Int32>(convertTwipToMm100(aItem.m_lPHeight)); break;// "Format/PageHeight", + case 13: pValues[nProp] <<= aItem.m_bSynchron; break;// "Option/Synchronize", + case 14: pValues[nProp] <<= aItem.m_bPage; break;// "Option/Page", + case 15: pValues[nProp] <<= aItem.m_nCol; break;// "Option/Column", + case 16: pValues[nProp] <<= aItem.m_nRow; break;// "Option/Row" + case 17: pValues[nProp] <<= aItem.m_bAddr; break;// "Inscription/UseAddress", + case 18: pValues[nProp] <<= aItem.m_aWriting; break;// "Inscription/Address", + case 19: pValues[nProp] <<= aItem.m_sDBName; break;// "Inscription/Database" + case 20: pValues[nProp] <<= aItem.m_aPrivFirstName; break;// "PrivateAddress/FirstName", + case 21: pValues[nProp] <<= aItem.m_aPrivName; break;// "PrivateAddress/Name", + case 22: pValues[nProp] <<= aItem.m_aPrivShortCut; break;// "PrivateAddress/ShortCut", + case 23: pValues[nProp] <<= aItem.m_aPrivFirstName2; break;// "PrivateAddress/SecondFirstName", + case 24: pValues[nProp] <<= aItem.m_aPrivName2; break;// "PrivateAddress/SecondName", + case 25: pValues[nProp] <<= aItem.m_aPrivShortCut2; break;// "PrivateAddress/SecondShortCut", + case 26: pValues[nProp] <<= aItem.m_aPrivStreet; break;// "PrivateAddress/Street", + case 27: pValues[nProp] <<= aItem.m_aPrivZip; break;// "PrivateAddress/Zip", + case 28: pValues[nProp] <<= aItem.m_aPrivCity; break;// "PrivateAddress/City", + case 29: pValues[nProp] <<= aItem.m_aPrivCountry; break;// "PrivateAddress/Country", + case 30: pValues[nProp] <<= aItem.m_aPrivState; break;// "PrivateAddress/State", + case 31: pValues[nProp] <<= aItem.m_aPrivTitle; break;// "PrivateAddress/Title", + case 32: pValues[nProp] <<= aItem.m_aPrivProfession; break;// "PrivateAddress/Profession", + case 33: pValues[nProp] <<= aItem.m_aPrivPhone; break;// "PrivateAddress/Phone", + case 34: pValues[nProp] <<= aItem.m_aPrivMobile; break;// "PrivateAddress/Mobile", + case 35: pValues[nProp] <<= aItem.m_aPrivFax; break;// "PrivateAddress/Fax", + case 36: pValues[nProp] <<= aItem.m_aPrivWWW; break;// "PrivateAddress/WebAddress", + case 37: pValues[nProp] <<= aItem.m_aPrivMail; break;// "PrivateAddress/Email", + case 38: pValues[nProp] <<= aItem.m_aCompCompany; break;// "BusinessAddress/Company", + case 39: pValues[nProp] <<= aItem.m_aCompCompanyExt; break;// "BusinessAddress/CompanyExt", + case 40: pValues[nProp] <<= aItem.m_aCompSlogan; break;// "BusinessAddress/Slogan", + case 41: pValues[nProp] <<= aItem.m_aCompStreet; break;// "BusinessAddress/Street", + case 42: pValues[nProp] <<= aItem.m_aCompZip; break;// "BusinessAddress/Zip", + case 43: pValues[nProp] <<= aItem.m_aCompCity; break;// "BusinessAddress/City", + case 44: pValues[nProp] <<= aItem.m_aCompCountry; break;// "BusinessAddress/Country", + case 45: pValues[nProp] <<= aItem.m_aCompState; break;// "BusinessAddress/State", + case 46: pValues[nProp] <<= aItem.m_aCompPosition; break;// "BusinessAddress/Position", + case 47: pValues[nProp] <<= aItem.m_aCompPhone; break;// "BusinessAddress/Phone", + case 48: pValues[nProp] <<= aItem.m_aCompMobile; break;// "BusinessAddress/Mobile", + case 49: pValues[nProp] <<= aItem.m_aCompFax; break;// "BusinessAddress/Fax", + case 50: pValues[nProp] <<= aItem.m_aCompWWW; break;// "BusinessAddress/WebAddress", + case 51: pValues[nProp] <<= aItem.m_aCompMail; break;// "BusinessAddress/Email", + case 52: pValues[nProp] <<= aItem.m_sGlossaryGroup; break;// "AutoText/Group" + case 53: pValues[nProp] <<= aItem.m_sGlossaryBlockName; break;// "AutoText/Block" + } + } + PutProperties(aNames, aValues); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/envelp/syncbtn.cxx b/sw/source/uibase/envelp/syncbtn.cxx new file mode 100644 index 000000000..009525d02 --- /dev/null +++ b/sw/source/uibase/envelp/syncbtn.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 <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/windowstate.hxx> + +#include <cmdid.h> +#include <swmodule.hxx> +#include <view.hxx> +#include <edtwin.hxx> + +#include <syncbtn.hxx> + +SFX_IMPL_MODELESSDIALOGCONTOLLER(SwSyncChildWin, FN_SYNC_LABELS) + +SwSyncChildWin::SwSyncChildWin(vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : SfxChildWindow(_pParent, nId) +{ + SetController(std::make_shared<SwSyncBtnDlg>(pBindings, this, _pParent->GetFrameWeld())); + SwSyncBtnDlg* pBtnDlg = static_cast<SwSyncBtnDlg*>(GetController().get()); + + if (!pInfo->aSize.Width() || !pInfo->aSize.Height()) + { + weld::Dialog* pDlg = pBtnDlg->getDialog(); + Point aPos; + + SwView* pActiveView = ::GetActiveView(); + if (pActiveView) + { + const SwEditWin &rEditWin = pActiveView->GetEditWin(); + aPos = rEditWin.OutputToScreenPixel(Point(0, 0)); + } + else + aPos = _pParent->OutputToScreenPixel(Point(0, 0)); + + WindowStateData aState; + aState.SetMask(WindowStateMask::Pos); + aState.SetX(aPos.X()); + aState.SetY(aPos.Y()); + pDlg->set_window_state(aState.ToStr()); + + pInfo->aPos = pDlg->get_position(); + pInfo->aSize = pDlg->get_size(); + } + + pBtnDlg->Initialize(pInfo); +} + +SwSyncBtnDlg::SwSyncBtnDlg(SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent) + : SfxModelessDialogController(pBindings, pChild, pParent, "modules/swriter/ui/floatingsync.ui", "FloatingSync") + , m_xSyncBtn(m_xBuilder->weld_button("sync")) +{ + m_xSyncBtn->connect_clicked(LINK(this, SwSyncBtnDlg, BtnHdl)); +} + +SwSyncBtnDlg::~SwSyncBtnDlg() +{ +} + +IMPL_STATIC_LINK_NOARG(SwSyncBtnDlg, BtnHdl, weld::Button&, void) +{ + SfxViewFrame::Current()->GetDispatcher()->Execute(FN_UPDATE_ALL_LINKS, SfxCallMode::ASYNCHRON); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/fldui/fldmgr.cxx b/sw/source/uibase/fldui/fldmgr.cxx new file mode 100644 index 000000000..ff6b78296 --- /dev/null +++ b/sw/source/uibase/fldui/fldmgr.cxx @@ -0,0 +1,1850 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmdid.h> +#include <hintids.hxx> +#include <svl/stritem.hxx> +#include <com/sun/star/text/DefaultNumberingProvider.hpp> +#include <com/sun/star/text/XDefaultNumberingProvider.hpp> +#include <com/sun/star/text/XNumberingTypeInfo.hpp> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <tools/resary.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/app.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/strarray.hxx> +#include <fmtrfmrk.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <vcl/mnemonic.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <swmodule.hxx> +#include <fmtinfmt.hxx> +#include <cellatr.hxx> +#include <dbmgr.hxx> +#include <shellres.hxx> +#include <fldbas.hxx> +#include <docufld.hxx> +#include <chpfld.hxx> +#include <ddefld.hxx> +#include <expfld.hxx> +#include <reffld.hxx> +#include <usrfld.hxx> +#include <dbfld.hxx> +#include <authfld.hxx> +#include <flddat.hxx> +#include <fldmgr.hxx> +#include <flddropdown.hxx> +#include <strings.hrc> +#include <tox.hxx> +#include <unotools/useroptions.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::text; +using namespace com::sun::star::style; +using namespace com::sun::star::sdbc; +using namespace ::com::sun::star; +using namespace nsSwDocInfoSubType; + +// groups of fields +enum +{ + GRP_DOC_BEGIN = 0, + GRP_DOC_END = GRP_DOC_BEGIN + 12, + + GRP_FKT_BEGIN = GRP_DOC_END, + GRP_FKT_END = GRP_FKT_BEGIN + 8, + + GRP_REF_BEGIN = GRP_FKT_END, + GRP_REF_END = GRP_REF_BEGIN + 2, + + GRP_REG_BEGIN = GRP_REF_END, + GRP_REG_END = GRP_REG_BEGIN + 1, + + GRP_DB_BEGIN = GRP_REG_END, + GRP_DB_END = GRP_DB_BEGIN + 5, + + GRP_VAR_BEGIN = GRP_DB_END, + GRP_VAR_END = GRP_VAR_BEGIN + 9 +}; + +enum +{ + GRP_WEB_DOC_BEGIN = 0, + GRP_WEB_DOC_END = GRP_WEB_DOC_BEGIN + 9, + + GRP_WEB_FKT_BEGIN = GRP_WEB_DOC_END + 2, + GRP_WEB_FKT_END = GRP_WEB_FKT_BEGIN + 0, // the group is empty! + + GRP_WEB_REF_BEGIN = GRP_WEB_FKT_END + 6, // the group is empty! + GRP_WEB_REF_END = GRP_WEB_REF_BEGIN + 0, + + GRP_WEB_REG_BEGIN = GRP_WEB_REF_END + 2, + GRP_WEB_REG_END = GRP_WEB_REG_BEGIN + 1, + + GRP_WEB_DB_BEGIN = GRP_WEB_REG_END, // the group is empty! + GRP_WEB_DB_END = GRP_WEB_DB_BEGIN + 0, + + GRP_WEB_VAR_BEGIN = GRP_WEB_DB_END + 5, + GRP_WEB_VAR_END = GRP_WEB_VAR_BEGIN + 1 +}; + +static const sal_uInt16 VF_COUNT = 1; // { 0 } +static const sal_uInt16 VF_USR_COUNT = 2; // { 0, nsSwExtendedSubType::SUB_CMD } +static const sal_uInt16 VF_DB_COUNT = 1; // { nsSwExtendedSubType::SUB_OWN_FMT } + +static const char* FLD_EU_ARY[] = +{ + FLD_EU_COMPANY, + FLD_EU_GIVENNAME, + FLD_EU_SURNAME, + FLD_EU_INITIALS, + FLD_EU_STREET, + FLD_EU_COUNTRY, + FLD_EU_POSTCODE, + FLD_EU_TOWN, + FLD_EU_TITLE, + FLD_EU_POS, + FLD_EU_TELPERSONAL, + FLD_EU_TELWORK, + FLD_EU_FAX, + FLD_EU_EMAIL, + FLD_EU_REGION +}; + +static const char* FMT_AUTHOR_ARY[] = +{ + FMT_AUTHOR_NAME, + FMT_AUTHOR_SCUT +}; + +static const char* FLD_DATE_ARY[] = +{ + FLD_DATE_FIX, + FLD_DATE_STD, +}; + +static const char* FLD_TIME_ARY[] = +{ + FLD_TIME_FIX, + FLD_TIME_STD +}; + +static const char* FMT_NUM_ARY[] = +{ + FMT_NUM_ABC, + FMT_NUM_SABC, + FMT_NUM_ABC_N, + FMT_NUM_SABC_N, + FMT_NUM_ROMAN, + FMT_NUM_SROMAN, + FMT_NUM_ARABIC, + FMT_NUM_PAGEDESC, + FMT_NUM_PAGESPECIAL +}; + +static const char* FMT_FF_ARY[] = +{ + FMT_FF_NAME, + FMT_FF_PATHNAME, + FMT_FF_PATH, + FMT_FF_NAME_NOEXT, + FMT_FF_UI_NAME, + FMT_FF_UI_RANGE +}; + +static const char* FLD_STAT_ARY[] = +{ + FLD_STAT_PAGE, + FLD_STAT_PARA, + FLD_STAT_WORD, + FLD_STAT_CHAR, + FLD_STAT_TABLE, + FLD_STAT_GRF, + FLD_STAT_OBJ +}; + +static const char* FMT_CHAPTER_ARY[] = +{ + FMT_CHAPTER_NO, + FMT_CHAPTER_NAME, + FMT_CHAPTER_NAMENO, + FMT_CHAPTER_NO_NOSEPARATOR +}; + +static const char* FLD_INPUT_ARY[] = +{ + FLD_INPUT_TEXT +}; + +static const char* FMT_MARK_ARY[] = +{ + FMT_MARK_TEXT, + FMT_MARK_TABLE, + FMT_MARK_FRAME, + FMT_MARK_GRAFIC, + FMT_MARK_OLE +}; + +static const char* FMT_REF_ARY[] = +{ + FMT_REF_PAGE, + FMT_REF_CHAPTER, + FMT_REF_TEXT, + FMT_REF_UPDOWN, + FMT_REF_PAGE_PGDSC, + FMT_REF_ONLYNUMBER, + FMT_REF_ONLYCAPTION, + FMT_REF_ONLYSEQNO, + FMT_REF_NUMBER, + FMT_REF_NUMBER_NO_CONTEXT, + FMT_REF_NUMBER_FULL_CONTEXT +}; + +static const char* FMT_REG_ARY[] = +{ + FMT_REG_AUTHOR, + FMT_REG_TIME, + FMT_REG_DATE +}; + +static const char* FMT_DBFLD_ARY[] = +{ + FMT_DBFLD_DB, + FMT_DBFLD_SYS +}; + +static const char* FMT_SETVAR_ARY[] = +{ + FMT_SETVAR_SYS, + FMT_SETVAR_TEXT +}; + +static const char* FMT_GETVAR_ARY[] = +{ + FMT_GETVAR_TEXT, + FMT_GETVAR_NAME +}; + +static const char* FMT_DDE_ARY[] = +{ + FMT_DDE_NORMAL, + FMT_DDE_HOT +}; + +static const char* FLD_PAGEREF_ARY[] = +{ + FLD_PAGEREF_OFF, + FLD_PAGEREF_ON +}; + +static const char* FMT_USERVAR_ARY[] = +{ + FMT_USERVAR_TEXT, + FMT_USERVAR_CMD +}; + +namespace { + +// field types and subtypes +struct SwFieldPack +{ + SwFieldTypesEnum nTypeId; + + const char** pSubTypeResIds; + size_t nSubTypeLength; + + const char** pFormatResIds; + size_t nFormatLength; +}; + +} + +// strings and formats +static const SwFieldPack aSwFields[] = +{ + // Document + { SwFieldTypesEnum::ExtendedUser, FLD_EU_ARY, SAL_N_ELEMENTS(FLD_EU_ARY), nullptr, 0 }, + { SwFieldTypesEnum::Author, nullptr, 0, FMT_AUTHOR_ARY, SAL_N_ELEMENTS(FMT_AUTHOR_ARY) }, + { SwFieldTypesEnum::Date, FLD_DATE_ARY, SAL_N_ELEMENTS(FLD_DATE_ARY), nullptr, 0 }, + { SwFieldTypesEnum::Time, FLD_TIME_ARY, SAL_N_ELEMENTS(FLD_TIME_ARY), nullptr, 0 }, + { SwFieldTypesEnum::PageNumber, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) -1 }, + { SwFieldTypesEnum::NextPage, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) }, + { SwFieldTypesEnum::PreviousPage, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) }, + { SwFieldTypesEnum::Filename, nullptr, 0, FMT_FF_ARY, SAL_N_ELEMENTS(FMT_FF_ARY) }, + { SwFieldTypesEnum::DocumentStatistics, FLD_STAT_ARY, SAL_N_ELEMENTS(FLD_STAT_ARY), FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) -1 }, + + { SwFieldTypesEnum::Chapter, nullptr, 0, FMT_CHAPTER_ARY, SAL_N_ELEMENTS(FMT_CHAPTER_ARY) }, + { SwFieldTypesEnum::TemplateName, nullptr, 0, FMT_FF_ARY, SAL_N_ELEMENTS(FMT_FF_ARY) }, + { SwFieldTypesEnum::ParagraphSignature, nullptr, 0, nullptr, 0 }, + + // Functions + { SwFieldTypesEnum::ConditionalText, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::Dropdown, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::Input, FLD_INPUT_ARY, SAL_N_ELEMENTS(FLD_INPUT_ARY), nullptr, 0 }, + { SwFieldTypesEnum::Macro, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::JumpEdit, nullptr, 0, FMT_MARK_ARY, SAL_N_ELEMENTS(FMT_MARK_ARY) }, + { SwFieldTypesEnum::CombinedChars, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::HiddenText, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::HiddenParagraph, nullptr, 0, nullptr, 0 }, + + // Cross-References + { SwFieldTypesEnum::SetRef, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::GetRef, nullptr, 0, FMT_REF_ARY, SAL_N_ELEMENTS(FMT_REF_ARY) }, + + // DocInformation + { SwFieldTypesEnum::DocumentInfo, nullptr, 0, FMT_REG_ARY, SAL_N_ELEMENTS(FMT_REG_ARY) }, + + // Database + { SwFieldTypesEnum::Database, nullptr, 0, FMT_DBFLD_ARY, SAL_N_ELEMENTS(FMT_DBFLD_ARY) }, + { SwFieldTypesEnum::DatabaseNextSet, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::DatabaseNumberSet, nullptr, 0, nullptr, 0 }, + { SwFieldTypesEnum::DatabaseSetNumber, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) - 2 }, + { SwFieldTypesEnum::DatabaseName, nullptr, 0, nullptr, 0 }, + + // Variables + { SwFieldTypesEnum::Set, nullptr, 0, FMT_SETVAR_ARY, SAL_N_ELEMENTS(FMT_SETVAR_ARY) }, + + { SwFieldTypesEnum::Get, nullptr, 0, FMT_GETVAR_ARY, SAL_N_ELEMENTS(FMT_GETVAR_ARY) }, + { SwFieldTypesEnum::DDE, nullptr, 0, FMT_DDE_ARY, SAL_N_ELEMENTS(FMT_DDE_ARY) }, + { SwFieldTypesEnum::Formel, nullptr, 0, FMT_GETVAR_ARY, SAL_N_ELEMENTS(FMT_GETVAR_ARY) }, + { SwFieldTypesEnum::Input, FLD_INPUT_ARY, SAL_N_ELEMENTS(FLD_INPUT_ARY), nullptr, 0 }, + { SwFieldTypesEnum::Sequence, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) - 2 }, + { SwFieldTypesEnum::SetRefPage, FLD_PAGEREF_ARY, SAL_N_ELEMENTS(FLD_PAGEREF_ARY),nullptr, 0 }, + { SwFieldTypesEnum::GetRefPage, nullptr, 0, FMT_NUM_ARY, SAL_N_ELEMENTS(FMT_NUM_ARY) - 1 }, + { SwFieldTypesEnum::User, nullptr, 0, FMT_USERVAR_ARY, SAL_N_ELEMENTS(FMT_USERVAR_ARY) } +}; + +// access to the shell +static SwWrtShell* lcl_GetShell() +{ + SwView* pView; + if ( nullptr != (pView = ::GetActiveView()) ) + return pView->GetWrtShellPtr(); + OSL_FAIL("no current shell found!"); + return nullptr; +} + +static sal_uInt16 GetPackCount() { return SAL_N_ELEMENTS(aSwFields); } + +// FieldManager controls inserting and updating of fields +SwFieldMgr::SwFieldMgr(SwWrtShell* pSh ) : + m_pWrtShell(pSh), + m_bEvalExp(true) +{ + // determine current field if existing + GetCurField(); +} + +SwFieldMgr::~SwFieldMgr() +{ +} + +// organise RefMark by names +bool SwFieldMgr::CanInsertRefMark( const OUString& rStr ) +{ + bool bRet = false; + SwWrtShell *pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + if(pSh) + { + sal_uInt16 nCnt = pSh->GetCursorCnt(); + + // the last Cursor doesn't have to be a spanned selection + if( 1 < nCnt && !pSh->SwCursorShell::HasSelection() ) + --nCnt; + + bRet = 2 > nCnt && nullptr == pSh->GetRefMark( rStr ); + } + return bRet; +} + +// access over ResIds +void SwFieldMgr::RemoveFieldType(SwFieldIds nResId, const OUString& rName ) +{ + SwWrtShell * pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + if( pSh ) + pSh->RemoveFieldType(nResId, rName); +} + +size_t SwFieldMgr::GetFieldTypeCount() const +{ + SwWrtShell * pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + return pSh ? pSh->GetFieldTypeCount() : 0; +} + +SwFieldType* SwFieldMgr::GetFieldType(SwFieldIds nResId, size_t nField) const +{ + SwWrtShell * pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + return pSh ? pSh->GetFieldType(nField, nResId) : nullptr; +} + +SwFieldType* SwFieldMgr::GetFieldType(SwFieldIds nResId, const OUString& rName) const +{ + SwWrtShell * pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + return pSh ? pSh->GetFieldType(nResId, rName) : nullptr; +} + +// determine current field +SwField* SwFieldMgr::GetCurField() +{ + SwWrtShell *pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + if ( pSh ) + m_pCurField = pSh->GetCurField( true ); + else + m_pCurField = nullptr; + + // initialise strings and format + m_aCurPar1.clear(); + m_aCurPar2.clear(); + m_sCurFrame.clear(); + m_nCurFormat = 0; + + if(!m_pCurField) + return nullptr; + + // preprocess current values; determine parameter 1 and parameter 2 + // as well as the format + const SwFieldTypesEnum nTypeId = m_pCurField->GetTypeId(); + + m_nCurFormat = m_pCurField->GetFormat(); + m_aCurPar1 = m_pCurField->GetPar1(); + m_aCurPar2 = m_pCurField->GetPar2(); + + switch( nTypeId ) + { + case SwFieldTypesEnum::PageNumber: + case SwFieldTypesEnum::NextPage: + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::GetRefPage: + if( m_nCurFormat == SVX_NUM_PAGEDESC ) + m_nCurFormat -= 2; + break; + default: break; + } + return m_pCurField; +} + +// provide group range +const SwFieldGroupRgn& SwFieldMgr::GetGroupRange(bool bHtmlMode, sal_uInt16 nGrpId) +{ + static SwFieldGroupRgn const aRanges[] = + { + { /* Document */ GRP_DOC_BEGIN, GRP_DOC_END }, + { /* Functions */ GRP_FKT_BEGIN, GRP_FKT_END }, + { /* Cross-Refs */ GRP_REF_BEGIN, GRP_REF_END }, + { /* DocInfos */ GRP_REG_BEGIN, GRP_REG_END }, + { /* Database */ GRP_DB_BEGIN, GRP_DB_END }, + { /* User */ GRP_VAR_BEGIN, GRP_VAR_END } + }; + static SwFieldGroupRgn const aWebRanges[] = + { + { /* Document */ GRP_WEB_DOC_BEGIN, GRP_WEB_DOC_END }, + { /* Functions */ GRP_WEB_FKT_BEGIN, GRP_WEB_FKT_END }, + { /* Cross-Refs */ GRP_WEB_REF_BEGIN, GRP_WEB_REF_END }, + { /* DocInfos */ GRP_WEB_REG_BEGIN, GRP_WEB_REG_END }, + { /* Database */ GRP_WEB_DB_BEGIN, GRP_WEB_DB_END }, + { /* User */ GRP_WEB_VAR_BEGIN, GRP_WEB_VAR_END } + }; + + if (bHtmlMode) + return aWebRanges[nGrpId]; + else + return aRanges[nGrpId]; +} + +// determine GroupId +sal_uInt16 SwFieldMgr::GetGroup(SwFieldTypesEnum nTypeId, sal_uInt16 nSubType) +{ + if (nTypeId == SwFieldTypesEnum::SetInput) + nTypeId = SwFieldTypesEnum::Set; + + if (nTypeId == SwFieldTypesEnum::Input && (nSubType & INP_USR)) + nTypeId = SwFieldTypesEnum::User; + + if (nTypeId == SwFieldTypesEnum::FixedDate) + nTypeId = SwFieldTypesEnum::Date; + + if (nTypeId == SwFieldTypesEnum::FixedTime) + nTypeId = SwFieldTypesEnum::Time; + + for (sal_uInt16 i = GRP_DOC; i <= GRP_VAR; i++) + { + const SwFieldGroupRgn& rRange = GetGroupRange(false/*bHtmlMode*/, i); + for (sal_uInt16 nPos = rRange.nStart; nPos < rRange.nEnd; nPos++) + { + if (aSwFields[nPos].nTypeId == nTypeId) + return i; + } + } + return USHRT_MAX; +} + +// determine names to TypeId +// ACCESS over TYP_... +SwFieldTypesEnum SwFieldMgr::GetTypeId(sal_uInt16 nPos) +{ + OSL_ENSURE(nPos < ::GetPackCount(), "forbidden Pos"); + return aSwFields[ nPos ].nTypeId; +} + +OUString SwFieldMgr::GetTypeStr(sal_uInt16 nPos) +{ + OSL_ENSURE(nPos < ::GetPackCount(), "forbidden TypeId"); + + SwFieldTypesEnum nFieldWh = aSwFields[ nPos ].nTypeId; + + // special treatment for date/time fields (without var/fix) + if( SwFieldTypesEnum::Date == nFieldWh ) + { + static OUString g_aDate( SwResId( STR_DATEFLD ) ); + return g_aDate; + } + if( SwFieldTypesEnum::Time == nFieldWh ) + { + static OUString g_aTime( SwResId( STR_TIMEFLD ) ); + return g_aTime; + } + + return SwFieldType::GetTypeStr( nFieldWh ); +} + +// determine Pos in the list +sal_uInt16 SwFieldMgr::GetPos(SwFieldTypesEnum nTypeId) +{ + switch( nTypeId ) + { + case SwFieldTypesEnum::FixedDate: nTypeId = SwFieldTypesEnum::Date; break; + case SwFieldTypesEnum::FixedTime: nTypeId = SwFieldTypesEnum::Time; break; + case SwFieldTypesEnum::SetInput: nTypeId = SwFieldTypesEnum::Set; break; + case SwFieldTypesEnum::UserInput: nTypeId = SwFieldTypesEnum::User; break; + default: break; + } + + for(sal_uInt16 i = 0; i < GetPackCount(); i++) + if(aSwFields[i].nTypeId == nTypeId) + return i; + + return USHRT_MAX; +} + +// localise subtypes of a field +void SwFieldMgr::GetSubTypes(SwFieldTypesEnum nTypeId, std::vector<OUString>& rToFill) +{ + SwWrtShell *pSh = m_pWrtShell ? m_pWrtShell : lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + if(pSh) + { + const sal_uInt16 nPos = GetPos(nTypeId); + + switch(nTypeId) + { + case SwFieldTypesEnum::SetRef: + case SwFieldTypesEnum::GetRef: + { + // references are no fields + pSh->GetRefMarks( &rToFill ); + break; + } + case SwFieldTypesEnum::Macro: + { + break; + } + case SwFieldTypesEnum::Input: + { + rToFill.push_back(SwResId(aSwFields[nPos].pSubTypeResIds[0])); + [[fallthrough]]; // move on at generic types + } + case SwFieldTypesEnum::DDE: + case SwFieldTypesEnum::Sequence: + case SwFieldTypesEnum::Formel: + case SwFieldTypesEnum::Get: + case SwFieldTypesEnum::Set: + case SwFieldTypesEnum::User: + { + + const size_t nCount = pSh->GetFieldTypeCount(); + for(size_t i = 0; i < nCount; ++i) + { + SwFieldType* pFieldType = pSh->GetFieldType( i ); + const SwFieldIds nWhich = pFieldType->Which(); + + if((nTypeId == SwFieldTypesEnum::DDE && pFieldType->Which() == SwFieldIds::Dde) || + + (nTypeId == SwFieldTypesEnum::User && nWhich == SwFieldIds::User) || + + (nTypeId == SwFieldTypesEnum::Get && nWhich == SwFieldIds::SetExp && + !(static_cast<SwSetExpFieldType*>(pFieldType)->GetType() & nsSwGetSetExpType::GSE_SEQ)) || + + (nTypeId == SwFieldTypesEnum::Set && nWhich == SwFieldIds::SetExp && + !(static_cast<SwSetExpFieldType*>(pFieldType)->GetType() & nsSwGetSetExpType::GSE_SEQ)) || + + (nTypeId == SwFieldTypesEnum::Sequence && nWhich == SwFieldIds::SetExp && + (static_cast<SwSetExpFieldType*>(pFieldType)->GetType() & nsSwGetSetExpType::GSE_SEQ)) || + + ((nTypeId == SwFieldTypesEnum::Input || nTypeId == SwFieldTypesEnum::Formel) && + (nWhich == SwFieldIds::User || + (nWhich == SwFieldIds::SetExp && + !(static_cast<SwSetExpFieldType*>(pFieldType)->GetType() & nsSwGetSetExpType::GSE_SEQ))) ) ) + { + rToFill.push_back(pFieldType->GetName()); + } + } + break; + } + case SwFieldTypesEnum::DatabaseNextSet: + case SwFieldTypesEnum::DatabaseNumberSet: + case SwFieldTypesEnum::DatabaseName: + case SwFieldTypesEnum::DatabaseSetNumber: + break; + + default: + { + // static SubTypes + if(nPos != USHRT_MAX) + { + sal_uInt16 nCount; + if (nTypeId == SwFieldTypesEnum::DocumentInfo) + nCount = DI_SUBTYPE_END - DI_SUBTYPE_BEGIN; + else + nCount = aSwFields[nPos].nSubTypeLength; + + for(sal_uInt16 i = 0; i < nCount; ++i) + { + OUString sNew; + if (nTypeId == SwFieldTypesEnum::DocumentInfo) + { + if ( i == DI_CUSTOM ) + sNew = SwResId(STR_CUSTOM_FIELD); + else + sNew = SwViewShell::GetShellRes()->aDocInfoLst[i]; + } + else + sNew = SwResId(aSwFields[nPos].pSubTypeResIds[i]); + + rToFill.push_back(sNew); + } + } + } + } + } +} + +// determine format +// ACCESS over TYP_... +sal_uInt16 SwFieldMgr::GetFormatCount(SwFieldTypesEnum nTypeId, bool bHtmlMode) const +{ + assert(nTypeId < SwFieldTypesEnum::LAST && "forbidden TypeId"); + { + const sal_uInt16 nPos = GetPos(nTypeId); + + if (nPos == USHRT_MAX || (bHtmlMode && nTypeId == SwFieldTypesEnum::Set)) + return 0; + + sal_uInt16 nCount = aSwFields[nPos].nFormatLength; + + if (nTypeId == SwFieldTypesEnum::Filename) + nCount -= 2; // no range or template + + const char** pStart = aSwFields[nPos].pFormatResIds; + if (!pStart) + return nCount; + + if (strcmp(*pStart, FMT_GETVAR_ARY[0]) == 0 || strcmp(*pStart, FMT_SETVAR_ARY[0]) == 0) + return VF_COUNT; + else if (strcmp(*pStart, FMT_USERVAR_ARY[0]) == 0) + return VF_USR_COUNT; + else if (strcmp(*pStart, FMT_DBFLD_ARY[0]) == 0) + return VF_DB_COUNT; + else if (strcmp(*pStart, FMT_NUM_ARY[0]) == 0) + { + GetNumberingInfo(); + if(m_xNumberingInfo.is()) + { + Sequence<sal_Int16> aTypes = m_xNumberingInfo->getSupportedNumberingTypes(); + // #i28073# it's not necessarily a sorted sequence + //skip all values below or equal to CHARS_LOWER_LETTER_N + nCount += std::count_if(aTypes.begin(), aTypes.end(), + [](sal_Int16 nCurrent) { return nCurrent > NumberingType::CHARS_LOWER_LETTER_N; }); + } + return nCount; + } + + return nCount; + } +} + +// determine FormatString to a type +OUString SwFieldMgr::GetFormatStr(SwFieldTypesEnum nTypeId, sal_uInt32 nFormatId) const +{ + assert(nTypeId < SwFieldTypesEnum::LAST && "forbidden TypeId"); + const sal_uInt16 nPos = GetPos(nTypeId); + + if (nPos == USHRT_MAX) + return OUString(); + + const char** pStart = aSwFields[nPos].pFormatResIds; + if (!pStart) + return OUString(); + + if (SwFieldTypesEnum::Author == nTypeId || SwFieldTypesEnum::Filename == nTypeId) + nFormatId &= ~static_cast<sal_uInt32>(FF_FIXED); // mask out Fixed-Flag + + if (nFormatId < aSwFields[nPos].nFormatLength) + return SwResId(pStart[nFormatId]); + + OUString aRet; + if (*pStart == FMT_NUM_ARY[0]) + { + if (m_xNumberingInfo.is()) + { + Sequence<sal_Int16> aTypes = m_xNumberingInfo->getSupportedNumberingTypes(); + sal_Int32 nOffset = aSwFields[nPos].nFormatLength; + sal_uInt32 nValidEntry = 0; + for (const sal_Int16 nCurrent : aTypes) + { + if(nCurrent > NumberingType::CHARS_LOWER_LETTER_N && + (nCurrent != (NumberingType::BITMAP | LINK_TOKEN))) + { + if (nValidEntry == nFormatId - nOffset) + { + sal_uInt32 n = SvxNumberingTypeTable::FindIndex(nCurrent); + if (n != RESARRAY_INDEX_NOTFOUND) + { + aRet = SvxNumberingTypeTable::GetString(n); + } + else + { + aRet = m_xNumberingInfo->getNumberingIdentifier( nCurrent ); + } + break; + } + ++nValidEntry; + } + } + } + } + + return aRet; +} + +// determine FormatId from Pseudo-ID +sal_uInt16 SwFieldMgr::GetFormatId(SwFieldTypesEnum nTypeId, sal_uInt32 nFormatId) const +{ + sal_uInt16 nId = static_cast<sal_uInt16>(nFormatId); + switch( nTypeId ) + { + case SwFieldTypesEnum::DocumentInfo: + { + const OString sId(aSwFields[GetPos(nTypeId)].pFormatResIds[nFormatId]); + if (sId == FMT_REG_AUTHOR) + nId = DI_SUB_AUTHOR; + else if (sId == FMT_REG_TIME) + nId = DI_SUB_TIME; + else if (sId == FMT_REG_DATE) + nId = DI_SUB_DATE; + break; + } + case SwFieldTypesEnum::PageNumber: + case SwFieldTypesEnum::NextPage: + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::DocumentStatistics: + case SwFieldTypesEnum::DatabaseSetNumber: + case SwFieldTypesEnum::Sequence: + case SwFieldTypesEnum::GetRefPage: + { + sal_uInt16 nPos = GetPos(nTypeId); + if (nFormatId < aSwFields[nPos].nFormatLength) + { + const OString sId(aSwFields[nPos].pFormatResIds[nFormatId]); + if (sId == FMT_NUM_ABC) + nId = SVX_NUM_CHARS_UPPER_LETTER; + else if (sId == FMT_NUM_SABC) + nId = SVX_NUM_CHARS_LOWER_LETTER; + else if (sId == FMT_NUM_ROMAN) + nId = SVX_NUM_ROMAN_UPPER; + else if (sId == FMT_NUM_SROMAN) + nId = SVX_NUM_ROMAN_LOWER; + else if (sId == FMT_NUM_ARABIC) + nId = SVX_NUM_ARABIC; + else if (sId == FMT_NUM_PAGEDESC) + nId = SVX_NUM_PAGEDESC; + else if (sId == FMT_NUM_PAGESPECIAL) + nId = SVX_NUM_CHAR_SPECIAL; + else if (sId == FMT_NUM_ABC_N) + nId = SVX_NUM_CHARS_UPPER_LETTER_N; + else if (sId == FMT_NUM_SABC_N) + nId = SVX_NUM_CHARS_LOWER_LETTER_N; + } + else if (m_xNumberingInfo.is()) + { + Sequence<sal_Int16> aTypes = m_xNumberingInfo->getSupportedNumberingTypes(); + sal_Int32 nOffset = aSwFields[nPos].nFormatLength; + sal_Int32 nValidEntry = 0; + for (const sal_Int16 nCurrent : aTypes) + { + if (nCurrent > NumberingType::CHARS_LOWER_LETTER_N) + { + if (nValidEntry == static_cast<sal_Int32>(nFormatId) - nOffset) + { + nId = nCurrent; + break; + } + ++nValidEntry; + } + } + } + break; + } + case SwFieldTypesEnum::DDE: + { + const OString sId(aSwFields[GetPos(nTypeId)].pFormatResIds[nFormatId]); + if (sId == FMT_DDE_NORMAL) + nId = static_cast<sal_uInt16>(SfxLinkUpdateMode::ONCALL); + else if (sId == FMT_DDE_HOT) + nId = static_cast<sal_uInt16>(SfxLinkUpdateMode::ALWAYS); + break; + } + default: break; + } + return nId; +} + +// Traveling +bool SwFieldMgr::GoNextPrev( bool bNext, SwFieldType* pTyp ) +{ + SwWrtShell* pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + if(!pSh) + return false; + + if( !pTyp && m_pCurField ) + { + const SwFieldTypesEnum nTypeId = m_pCurField->GetTypeId(); + if( SwFieldTypesEnum::SetInput == nTypeId || SwFieldTypesEnum::UserInput == nTypeId ) + pTyp = pSh->GetFieldType( 0, SwFieldIds::Input ); + else + pTyp = m_pCurField->GetTyp(); + } + + if (pTyp && pTyp->Which() == SwFieldIds::Database) + { + // for fieldcommand-edit (hop to all DB fields) + return pSh->MoveFieldType( nullptr, bNext, SwFieldIds::Database ); + } + + return pTyp && pSh->MoveFieldType(pTyp, bNext); +} + +// insert field types +void SwFieldMgr::InsertFieldType(SwFieldType const & rType) +{ + SwWrtShell* pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + if(pSh) + pSh->InsertFieldType(rType); +} + +// determine current TypeId +SwFieldTypesEnum SwFieldMgr::GetCurTypeId() const +{ + return m_pCurField ? m_pCurField->GetTypeId() : SwFieldTypesEnum::Unknown; +} + +// Over string insert field or update +bool SwFieldMgr::InsertField( + const SwInsertField_Data& rData) +{ + std::unique_ptr<SwField> pField; + bool bExp = false; + bool bTable = false; + bool bPageVar = false; + sal_uInt32 nFormatId = rData.m_nFormatId; + sal_uInt16 nSubType = rData.m_nSubType; + sal_Unicode cSeparator = rData.m_cSeparator; + SwWrtShell* pCurShell = rData.m_pSh; + if(!pCurShell) + pCurShell = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + OSL_ENSURE(pCurShell, "no SwWrtShell found"); + if(!pCurShell) + return false; + + switch (rData.m_nTypeId) + { // ATTENTION this field is inserted by a separate dialog + case SwFieldTypesEnum::Postit: + { + SvtUserOptions aUserOpt; + SwPostItFieldType* pType = static_cast<SwPostItFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::Postit)); + pField.reset( + new SwPostItField( + pType, + rData.m_sPar1, // author + rData.m_sPar2, // content + aUserOpt.GetID(), // author's initials + OUString(), // name + DateTime(DateTime::SYSTEM) )); + } + break; + case SwFieldTypesEnum::Script: + { + SwScriptFieldType* pType = + static_cast<SwScriptFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::Script)); + pField.reset(new SwScriptField(pType, rData.m_sPar1, rData.m_sPar2, static_cast<bool>(nFormatId))); + break; + } + + case SwFieldTypesEnum::CombinedChars: + { + SwCombinedCharFieldType* pType = static_cast<SwCombinedCharFieldType*>( + pCurShell->GetFieldType( 0, SwFieldIds::CombinedChars )); + pField.reset(new SwCombinedCharField( pType, rData.m_sPar1 )); + } + break; + + case SwFieldTypesEnum::Authority: + { + SwAuthorityFieldType* pType = + static_cast<SwAuthorityFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::TableOfAuthorities)); + if (!pType) + { + SwAuthorityFieldType const type(pCurShell->GetDoc()); + pType = static_cast<SwAuthorityFieldType*>( + pCurShell->InsertFieldType(type)); + } + pField.reset(new SwAuthorityField(pType, rData.m_sPar1)); + } + break; + + case SwFieldTypesEnum::Date: + case SwFieldTypesEnum::Time: + { + sal_uInt16 nSub = static_cast< sal_uInt16 >(rData.m_nTypeId == SwFieldTypesEnum::Date ? DATEFLD : TIMEFLD); + nSub |= nSubType == DATE_VAR ? 0 : FIXEDFLD; + + SwDateTimeFieldType* pTyp = + static_cast<SwDateTimeFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::DateTime) ); + pField.reset(new SwDateTimeField(pTyp, nSub, nFormatId)); + pField->SetPar2(rData.m_sPar2); + break; + } + + case SwFieldTypesEnum::Filename: + { + SwFileNameFieldType* pTyp = + static_cast<SwFileNameFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::Filename) ); + pField.reset(new SwFileNameField(pTyp, nFormatId)); + break; + } + + case SwFieldTypesEnum::TemplateName: + { + SwTemplNameFieldType* pTyp = + static_cast<SwTemplNameFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::TemplateName) ); + pField.reset(new SwTemplNameField(pTyp, nFormatId)); + break; + } + + case SwFieldTypesEnum::Chapter: + { + sal_uInt16 nByte = static_cast<sal_uInt16>(rData.m_sPar2.toInt32()); + SwChapterFieldType* pTyp = + static_cast<SwChapterFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::Chapter) ); + pField.reset(new SwChapterField(pTyp, nFormatId)); + nByte = std::max(sal_uInt16(1), nByte); + nByte = std::min(nByte, sal_uInt16(MAXLEVEL)); + nByte -= 1; + static_cast<SwChapterField*>(pField.get())->SetLevel(static_cast<sal_uInt8>(nByte)); + break; + } + + case SwFieldTypesEnum::NextPage: + case SwFieldTypesEnum::PreviousPage: + case SwFieldTypesEnum::PageNumber: + { + short nOff = static_cast<short>(rData.m_sPar2.toInt32()); + + if(rData.m_nTypeId == SwFieldTypesEnum::NextPage) + { + if( SVX_NUM_CHAR_SPECIAL == nFormatId ) + nOff = 1; + else + nOff += 1; + nSubType = PG_NEXT; + } + else if(rData.m_nTypeId == SwFieldTypesEnum::PreviousPage) + { + if( SVX_NUM_CHAR_SPECIAL == nFormatId ) + nOff = -1; + else + nOff -= 1; + nSubType = PG_PREV; + } + else + nSubType = PG_RANDOM; + + SwPageNumberFieldType* pTyp = + static_cast<SwPageNumberFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::PageNumber) ); + pField.reset(new SwPageNumberField(pTyp, nSubType, nFormatId, nOff)); + + if( SVX_NUM_CHAR_SPECIAL == nFormatId && + ( PG_PREV == nSubType || PG_NEXT == nSubType ) ) + static_cast<SwPageNumberField*>(pField.get())->SetUserString( rData.m_sPar2 ); + break; + } + + case SwFieldTypesEnum::DocumentStatistics: + { + SwDocStatFieldType* pTyp = + static_cast<SwDocStatFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::DocStat) ); + pField.reset(new SwDocStatField(pTyp, nSubType, nFormatId)); + break; + } + + case SwFieldTypesEnum::Author: + { + SwAuthorFieldType* pTyp = + static_cast<SwAuthorFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::Author) ); + pField.reset(new SwAuthorField(pTyp, nFormatId)); + break; + } + + case SwFieldTypesEnum::ConditionalText: + case SwFieldTypesEnum::HiddenText: + { + SwHiddenTextFieldType* pTyp = + static_cast<SwHiddenTextFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::HiddenText) ); + pField.reset(new SwHiddenTextField(pTyp, true, rData.m_sPar1, rData.m_sPar2, false, rData.m_nTypeId)); + bExp = true; + break; + } + + case SwFieldTypesEnum::HiddenParagraph: + { + SwHiddenParaFieldType* pTyp = + static_cast<SwHiddenParaFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::HiddenPara) ); + pField.reset(new SwHiddenParaField(pTyp, rData.m_sPar1)); + bExp = true; + break; + } + + case SwFieldTypesEnum::SetRef: + { + if( !rData.m_sPar1.isEmpty() && CanInsertRefMark( rData.m_sPar1 ) ) + { + pCurShell->SetAttrItem( SwFormatRefMark( rData.m_sPar1 ) ); + return true; + } + return false; + } + + case SwFieldTypesEnum::GetRef: + { + SwGetRefFieldType* pTyp = + static_cast<SwGetRefFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::GetRef) ); + sal_uInt16 nSeqNo = static_cast<sal_uInt16>(rData.m_sPar2.toInt32()); + OUString sReferenceLanguage; + // handle language-variant formats + if (nFormatId >= SAL_N_ELEMENTS(FMT_REF_ARY)) + { + LanguageType nLang = GetCurrLanguage(); + if (nLang == LANGUAGE_HUNGARIAN) + { + if (nFormatId >= SAL_N_ELEMENTS(FMT_REF_ARY) * 2) + sReferenceLanguage = "Hu"; + else + sReferenceLanguage = "hu"; + } + nFormatId %= SAL_N_ELEMENTS(FMT_REF_ARY); + } + pField.reset(new SwGetRefField(pTyp, rData.m_sPar1, sReferenceLanguage, nSubType, nSeqNo, nFormatId)); + bExp = true; + break; + } + + case SwFieldTypesEnum::DDE: + { + //JP 28.08.95: DDE-Topics/-Items can have blanks in their names! + // That's not yet considered here. + sal_Int32 nIndex = 0; + OUString sCmd = rData.m_sPar2.replaceFirst(" ", OUStringChar(sfx2::cTokenSeparator), &nIndex); + if (nIndex>=0 && ++nIndex<sCmd.getLength()) + { + sCmd = sCmd.replaceFirst(" ", OUStringChar(sfx2::cTokenSeparator), &nIndex); + } + + SwDDEFieldType aType( rData.m_sPar1, sCmd, static_cast<SfxLinkUpdateMode>(nFormatId) ); + SwDDEFieldType* pTyp = static_cast<SwDDEFieldType*>( pCurShell->InsertFieldType( aType ) ); + pField.reset(new SwDDEField( pTyp )); + break; + } + + case SwFieldTypesEnum::Macro: + { + SwMacroFieldType* pTyp = + static_cast<SwMacroFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::Macro)); + + pField.reset(new SwMacroField(pTyp, rData.m_sPar1, rData.m_sPar2)); + + break; + } + + case SwFieldTypesEnum::Internet: + { + SwFormatINetFormat aFormat( rData.m_sPar1, m_sCurFrame ); + return pCurShell->InsertURL( aFormat, rData.m_sPar2 ); + } + + case SwFieldTypesEnum::JumpEdit: + { + SwJumpEditFieldType* pTyp = + static_cast<SwJumpEditFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::JumpEdit)); + + pField.reset(new SwJumpEditField(pTyp, nFormatId, rData.m_sPar1, rData.m_sPar2)); + break; + } + + case SwFieldTypesEnum::DocumentInfo: + { + SwDocInfoFieldType* pTyp = static_cast<SwDocInfoFieldType*>( pCurShell->GetFieldType( + 0, SwFieldIds::DocInfo ) ); + pField.reset(new SwDocInfoField(pTyp, nSubType, rData.m_sPar1, nFormatId)); + break; + } + + case SwFieldTypesEnum::ExtendedUser: + { + SwExtUserFieldType* pTyp = static_cast<SwExtUserFieldType*>( pCurShell->GetFieldType( + 0, SwFieldIds::ExtUser) ); + pField.reset(new SwExtUserField(pTyp, nSubType, nFormatId)); + break; + } + + case SwFieldTypesEnum::Database: + { +#if HAVE_FEATURE_DBCONNECTIVITY + SwDBData aDBData; + OUString sPar1; + + if (rData.m_sPar1.indexOf(DB_DELIM)<0) + { + aDBData = pCurShell->GetDBData(); + sPar1 = rData.m_sPar1; + } + else + { + sal_Int32 nIdx{ 0 }; + aDBData.sDataSource = rData.m_sPar1.getToken(0, DB_DELIM, nIdx); + aDBData.sCommand = rData.m_sPar1.getToken(0, DB_DELIM, nIdx); + aDBData.nCommandType = rData.m_sPar1.getToken(0, DB_DELIM, nIdx).toInt32(); + sPar1 = rData.m_sPar1.getToken(0, DB_DELIM, nIdx); + } + + if(!aDBData.sDataSource.isEmpty() && pCurShell->GetDBData() != aDBData) + pCurShell->ChgDBData(aDBData); + + SwDBFieldType* pTyp = static_cast<SwDBFieldType*>(pCurShell->InsertFieldType( + SwDBFieldType(pCurShell->GetDoc(), sPar1, aDBData) ) ); + pField.reset(new SwDBField(pTyp)); + pField->SetSubType(nSubType); + + if( !(nSubType & nsSwExtendedSubType::SUB_OWN_FMT) ) // determine database format + { + Reference< XDataSource> xSource; + rData.m_aDBDataSource >>= xSource; + Reference<XConnection> xConnection; + rData.m_aDBConnection >>= xConnection; + Reference<XPropertySet> xColumn; + rData.m_aDBColumn >>= xColumn; + if(xColumn.is()) + { + nFormatId = SwDBManager::GetColumnFormat(xSource, xConnection, xColumn, + pCurShell->GetNumberFormatter(), GetCurrLanguage() ); + } + else + nFormatId = pCurShell->GetDBManager()->GetColumnFormat( + aDBData.sDataSource, aDBData.sCommand, sPar1, + pCurShell->GetNumberFormatter(), GetCurrLanguage() ); + } + pField->ChangeFormat( nFormatId ); + + bExp = true; +#endif + break; + } + + case SwFieldTypesEnum::DatabaseSetNumber: + case SwFieldTypesEnum::DatabaseNumberSet: + case SwFieldTypesEnum::DatabaseNextSet: + case SwFieldTypesEnum::DatabaseName: + { +#if HAVE_FEATURE_DBCONNECTIVITY + SwDBData aDBData; + + // extract DBName from rData.m_sPar1. Format: DBName.TableName.CommandType.ExpStrg + sal_Int32 nTablePos = rData.m_sPar1.indexOf(DB_DELIM); + sal_Int32 nCmdTypePos = -1; + sal_Int32 nExpPos = -1; + + if (nTablePos>=0) + { + aDBData.sDataSource = rData.m_sPar1.copy(0, nTablePos++); + nCmdTypePos = rData.m_sPar1.indexOf(DB_DELIM, nTablePos); + if (nCmdTypePos>=0) + { + aDBData.sCommand = rData.m_sPar1.copy(nTablePos, nCmdTypePos++ - nTablePos); + nExpPos = rData.m_sPar1.indexOf(DB_DELIM, nCmdTypePos); + if (nExpPos>=0) + { + aDBData.nCommandType = rData.m_sPar1.copy(nCmdTypePos, nExpPos++ - nCmdTypePos).toInt32(); + } + } + } + + sal_Int32 nPos = 0; + if (nExpPos>=0) + nPos = nExpPos; + else if (nTablePos>=0) + nPos = nTablePos; + + OUString sPar1 = rData.m_sPar1.copy(nPos); + + if (!aDBData.sDataSource.isEmpty() && pCurShell->GetDBData() != aDBData) + pCurShell->ChgDBData(aDBData); + + switch(rData.m_nTypeId) + { + case SwFieldTypesEnum::DatabaseName: + { + SwDBNameFieldType* pTyp = + static_cast<SwDBNameFieldType*>(pCurShell->GetFieldType(0, SwFieldIds::DatabaseName)); + pField.reset(new SwDBNameField(pTyp, aDBData)); + + break; + } + case SwFieldTypesEnum::DatabaseNextSet: + { + SwDBNextSetFieldType* pTyp = static_cast<SwDBNextSetFieldType*>(pCurShell->GetFieldType( + 0, SwFieldIds::DbNextSet) ); + pField.reset(new SwDBNextSetField(pTyp, sPar1, aDBData)); + bExp = true; + break; + } + case SwFieldTypesEnum::DatabaseNumberSet: + { + SwDBNumSetFieldType* pTyp = static_cast<SwDBNumSetFieldType*>( pCurShell->GetFieldType( + 0, SwFieldIds::DbNumSet) ); + pField.reset(new SwDBNumSetField( pTyp, sPar1, rData.m_sPar2, aDBData)); + bExp = true; + break; + } + case SwFieldTypesEnum::DatabaseSetNumber: + { + SwDBSetNumberFieldType* pTyp = static_cast<SwDBSetNumberFieldType*>( + pCurShell->GetFieldType(0, SwFieldIds::DbSetNumber) ); + pField.reset(new SwDBSetNumberField( pTyp, aDBData, nFormatId)); + bExp = true; + break; + } + default: break; + } +#endif + break; + } + + case SwFieldTypesEnum::User: + { + SwUserFieldType* pTyp = + static_cast<SwUserFieldType*>( pCurShell->GetFieldType(SwFieldIds::User, rData.m_sPar1) ); + + // only if existing + if(!pTyp) + { + pTyp = static_cast<SwUserFieldType*>( pCurShell->InsertFieldType( + SwUserFieldType(pCurShell->GetDoc(), rData.m_sPar1)) ); + } + if (pTyp->GetContent(nFormatId) != rData.m_sPar2) + pTyp->SetContent(rData.m_sPar2, nFormatId); + pField.reset(new SwUserField(pTyp, 0, nFormatId)); + if (pField->GetSubType() != nSubType) + pField->SetSubType(nSubType); + bTable = true; + break; + } + + case SwFieldTypesEnum::Input: + { + if ((nSubType & 0x00ff) == INP_VAR) + { + SwSetExpFieldType* pTyp = static_cast<SwSetExpFieldType*>( + pCurShell->GetFieldType(SwFieldIds::SetExp, rData.m_sPar1) ); + + // no Expression Type with this name existing -> create + if(pTyp) + { + std::unique_ptr<SwSetExpField> pExpField( + new SwSetExpField(pTyp, OUString(), nFormatId)); + + // Don't change type of SwSetExpFieldType: + sal_uInt16 nOldSubType = pExpField->GetSubType(); + pExpField->SetSubType(nOldSubType | (nSubType & 0xff00)); + + pExpField->SetPromptText(rData.m_sPar2); + pExpField->SetInputFlag(true) ; + bExp = true; + pField = std::move(pExpField); + } + else + return false; + } + else + { + SwInputFieldType* pTyp = + static_cast<SwInputFieldType*>( pCurShell->GetFieldType(0, SwFieldIds::Input) ); + + pField.reset( + new SwInputField( pTyp, rData.m_sPar1, rData.m_sPar2, nSubType|nsSwExtendedSubType::SUB_INVISIBLE, nFormatId)); + } + break; + } + + case SwFieldTypesEnum::Set: + { + if (rData.m_sPar2.isEmpty()) // empty variables are not allowed + return false; + + SwSetExpFieldType* pTyp = static_cast<SwSetExpFieldType*>( pCurShell->InsertFieldType( + SwSetExpFieldType(pCurShell->GetDoc(), rData.m_sPar1) ) ); + + std::unique_ptr<SwSetExpField> pExpField(new SwSetExpField( pTyp, rData.m_sPar2, nFormatId)); + pExpField->SetSubType(nSubType); + pExpField->SetPar2(rData.m_sPar2); + bExp = true; + pField = std::move(pExpField); + break; + } + + case SwFieldTypesEnum::Sequence: + { + SwSetExpFieldType* pTyp = static_cast<SwSetExpFieldType*>( pCurShell->InsertFieldType( + SwSetExpFieldType(pCurShell->GetDoc(), rData.m_sPar1, nsSwGetSetExpType::GSE_SEQ))); + + sal_uInt8 nLevel = static_cast< sal_uInt8 >(nSubType & 0xff); + + pTyp->SetOutlineLvl(nLevel); + if (nLevel != 0x7f && cSeparator == 0) + cSeparator = '.'; + + pTyp->SetDelimiter(OUString(cSeparator)); + pField.reset(new SwSetExpField(pTyp, rData.m_sPar2, nFormatId)); + bExp = true; + break; + } + + case SwFieldTypesEnum::Get: + { + // is there a corresponding SetField + SwSetExpFieldType* pSetTyp = static_cast<SwSetExpFieldType*>( + pCurShell->GetFieldType(SwFieldIds::SetExp, rData.m_sPar1)); + + if(pSetTyp) + { + SwGetExpFieldType* pTyp = static_cast<SwGetExpFieldType*>( pCurShell->GetFieldType( + 0, SwFieldIds::GetExp) ); + pField.reset( new SwGetExpField(pTyp, rData.m_sPar1, pSetTyp->GetType(), nFormatId) ); + pField->SetSubType(nSubType | pSetTyp->GetType()); + bExp = true; + } + else + return false; + break; + } + + case SwFieldTypesEnum::Formel: + { + if(pCurShell->GetFrameType(nullptr,false) & FrameTypeFlags::TABLE) + { + pCurShell->StartAllAction(); + + SvNumberFormatter* pFormatter = pCurShell->GetDoc()->GetNumberFormatter(); + const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatId); + + if (pEntry) + { + SfxStringItem aFormat(FN_NUMBER_FORMAT, pEntry->GetFormatstring()); + pCurShell->GetView().GetViewFrame()->GetDispatcher()-> + ExecuteList(FN_NUMBER_FORMAT, SfxCallMode::SYNCHRON, + { &aFormat }); + } + + SfxItemSet aBoxSet( pCurShell->GetAttrPool(), + svl::Items<RES_BOXATR_FORMULA, RES_BOXATR_FORMULA>{} ); + + OUString sFormula(comphelper::string::stripStart(rData.m_sPar2, ' ')); + if ( sFormula.startsWith("=") ) + { + sFormula = sFormula.copy(1); + } + + aBoxSet.Put( SwTableBoxFormula( sFormula )); + pCurShell->SetTableBoxFormulaAttrs( aBoxSet ); + pCurShell->UpdateTable(); + + pCurShell->EndAllAction(); + return true; + + } + else + { + SwGetExpFieldType* pTyp = static_cast<SwGetExpFieldType*>( + pCurShell->GetFieldType(0, SwFieldIds::GetExp) ); + pField.reset( new SwGetExpField(pTyp, rData.m_sPar2, nsSwGetSetExpType::GSE_FORMULA, nFormatId) ); + pField->SetSubType(nSubType); + bExp = true; + } + break; + } + case SwFieldTypesEnum::SetRefPage: + pField.reset( new SwRefPageSetField( static_cast<SwRefPageSetFieldType*>( + pCurShell->GetFieldType( 0, SwFieldIds::RefPageSet ) ), + static_cast<short>(rData.m_sPar2.toInt32()), 0 != nSubType ) ); + bPageVar = true; + break; + + case SwFieldTypesEnum::GetRefPage: + pField.reset( new SwRefPageGetField( static_cast<SwRefPageGetFieldType*>( + pCurShell->GetFieldType( 0, SwFieldIds::RefPageGet ) ), nFormatId ) ); + bPageVar = true; + break; + case SwFieldTypesEnum::Dropdown : + { + pField.reset( new SwDropDownField(pCurShell->GetFieldType( 0, SwFieldIds::Dropdown )) ); + const sal_Int32 nTokenCount = comphelper::string::getTokenCount(rData.m_sPar2, DB_DELIM); + Sequence<OUString> aEntries(nTokenCount); + OUString* pArray = aEntries.getArray(); + for(sal_Int32 nToken = 0, nIdx = 0; nToken < nTokenCount; nToken++) + pArray[nToken] = rData.m_sPar2.getToken(0, DB_DELIM, nIdx); + static_cast<SwDropDownField*>(pField.get())->SetItems(aEntries); + static_cast<SwDropDownField*>(pField.get())->SetName(rData.m_sPar1); + } + break; + + // Insert Paragraph Signature field by signing the paragraph. + // The resulting field is really a metadata field, created and added via signing. + case SwFieldTypesEnum::ParagraphSignature: + pCurShell->SignParagraph(); + return true; + break; + + default: + { OSL_ENSURE(false, "wrong field type"); + return false; + } + } + OSL_ENSURE(pField, "field not available"); + + //the auto language flag has to be set prior to the language! + pField->SetAutomaticLanguage(rData.m_bIsAutomaticLanguage); + LanguageType nLang = GetCurrLanguage(); + pField->SetLanguage(nLang); + + // insert + pCurShell->StartAllAction(); + + pCurShell->Insert(*pField, rData.m_pAnnotationRange.get()); + + if (SwFieldTypesEnum::Input == rData.m_nTypeId) + { + pCurShell->Push(); + + // start dialog, not before the field is inserted tdf#99529 + pCurShell->Left(CRSR_SKIP_CHARS, + false, (INP_VAR == (nSubType & 0xff)) ? 1 : 2, false ); + pCurShell->StartInputFieldDlg(pField.get(), false, true, rData.m_pParent); + + pCurShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + } + + if(bExp && m_bEvalExp) + pCurShell->UpdateExpFields(true); + + if(bTable) + { + pCurShell->Left(CRSR_SKIP_CHARS, false, 1, false ); + pCurShell->UpdateOneField(*pField); + pCurShell->Right(CRSR_SKIP_CHARS, false, 1, false ); + } + else if( bPageVar ) + static_cast<SwRefPageGetFieldType*>(pCurShell->GetFieldType( 0, SwFieldIds::RefPageGet ))->UpdateFields(); + else if( SwFieldTypesEnum::GetRef == rData.m_nTypeId ) + pField->GetTyp()->ModifyNotification( nullptr, nullptr ); + + // delete temporary field + pField.reset(); + + pCurShell->EndAllAction(); + return true; +} + +// fields update +void SwFieldMgr::UpdateCurField(sal_uInt32 nFormat, + const OUString& rPar1, + const OUString& rPar2, + std::unique_ptr<SwField> pTmpField) +{ + // change format + OSL_ENSURE(m_pCurField, "no field at CursorPos"); + + if (!pTmpField) + pTmpField = m_pCurField->CopyField(); + + SwFieldType* pType = pTmpField->GetTyp(); + const SwFieldTypesEnum nTypeId = pTmpField->GetTypeId(); + + SwWrtShell* pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + OSL_ENSURE(pSh, "no SwWrtShell found"); + if(!pSh) + return; + pSh->StartAllAction(); + + bool bSetPar2 = true; + bool bSetPar1 = true; + OUString sPar2( rPar2 ); + + // Order to Format + switch( nTypeId ) + { + case SwFieldTypesEnum::DDE: + { + // DDE-Topics/-Items can have blanks in their names! + // That's not yet considered here! + sal_Int32 nIndex = 0; + sPar2 = sPar2.replaceFirst(" ", OUStringChar(sfx2::cTokenSeparator), &nIndex ); + if (nIndex>=0 && ++nIndex<sPar2.getLength()) + { + sPar2 = sPar2.replaceFirst(" ", OUStringChar(sfx2::cTokenSeparator), &nIndex); + } + break; + } + + case SwFieldTypesEnum::Chapter: + { + sal_uInt16 nByte = static_cast<sal_uInt16>(rPar2.toInt32()); + nByte = std::max(sal_uInt16(1), nByte); + nByte = std::min(nByte, sal_uInt16(MAXLEVEL)); + nByte -= 1; + static_cast<SwChapterField*>(pTmpField.get())->SetLevel(static_cast<sal_uInt8>(nByte)); + bSetPar2 = false; + break; + } + + case SwFieldTypesEnum::Script: + static_cast<SwScriptField*>(pTmpField.get())->SetCodeURL(static_cast<bool>(nFormat)); + break; + + case SwFieldTypesEnum::NextPage: + if( SVX_NUM_CHAR_SPECIAL == nFormat ) + { + static_cast<SwPageNumberField*>(m_pCurField)->SetUserString( sPar2 ); + sPar2 = "1"; + } + else + { + if( nFormat + 2 == SVX_NUM_PAGEDESC ) + nFormat = SVX_NUM_PAGEDESC; + short nOff = static_cast<short>(sPar2.toInt32()); + nOff += 1; + sPar2 = OUString::number(nOff); + } + break; + + case SwFieldTypesEnum::PreviousPage: + if( SVX_NUM_CHAR_SPECIAL == nFormat ) + { + static_cast<SwPageNumberField*>(m_pCurField)->SetUserString( sPar2 ); + sPar2 = "-1"; + } + else + { + if( nFormat + 2 == SVX_NUM_PAGEDESC ) + nFormat = SVX_NUM_PAGEDESC; + short nOff = static_cast<short>(sPar2.toInt32()); + nOff -= 1; + sPar2 = OUString::number(nOff); + } + break; + + case SwFieldTypesEnum::PageNumber: + case SwFieldTypesEnum::GetRefPage: + if( nFormat + 2 == SVX_NUM_PAGEDESC ) + nFormat = SVX_NUM_PAGEDESC; + break; + + case SwFieldTypesEnum::GetRef: + { + bSetPar2 = false; + static_cast<SwGetRefField*>(pTmpField.get())->SetSubType( static_cast<sal_uInt16>(rPar2.toInt32()) ); + const sal_Int32 nPos = rPar2.indexOf( '|' ); + if( nPos>=0 ) + static_cast<SwGetRefField*>(pTmpField.get())->SetSeqNo( static_cast<sal_uInt16>(rPar2.copy( nPos + 1 ).toInt32())); + } + break; + case SwFieldTypesEnum::Dropdown: + { + sal_Int32 nTokenCount = comphelper::string::getTokenCount(sPar2, DB_DELIM); + Sequence<OUString> aEntries(nTokenCount); + OUString* pArray = aEntries.getArray(); + for(sal_Int32 nToken = 0, nIdx = 0; nToken < nTokenCount; nToken++) + pArray[nToken] = sPar2.getToken(0, DB_DELIM, nIdx); + static_cast<SwDropDownField*>(pTmpField.get())->SetItems(aEntries); + static_cast<SwDropDownField*>(pTmpField.get())->SetName(rPar1); + bSetPar1 = bSetPar2 = false; + } + break; + case SwFieldTypesEnum::Authority : + { + //#i99069# changes to a bibliography field should change the field type + SwAuthorityField* pAuthorityField = static_cast<SwAuthorityField*>(pTmpField.get()); + SwAuthorityFieldType* pAuthorityType = static_cast<SwAuthorityFieldType*>(pType); + rtl::Reference<SwAuthEntry> xTempEntry(new SwAuthEntry); + for( sal_Int32 i = 0, nIdx = 0; i < AUTH_FIELD_END; ++i ) + xTempEntry->SetAuthorField( static_cast<ToxAuthorityField>(i), + rPar1.getToken( 0, TOX_STYLE_DELIMITER, nIdx )); + if( pAuthorityType->ChangeEntryContent( xTempEntry.get() ) ) + { + pType->UpdateFields(); + pSh->SetModified(); + } + + if( xTempEntry->GetAuthorField( AUTH_FIELD_IDENTIFIER ) == + pAuthorityField->GetFieldText( AUTH_FIELD_IDENTIFIER ) ) + bSetPar1 = false; //otherwise it's a new or changed entry, the field needs to be updated + bSetPar2 = false; + } + break; + default: break; + } + + // set format + // setup format before SetPar2 because of NumberFormatter! + pTmpField->ChangeFormat(nFormat); + + if( bSetPar1 ) + pTmpField->SetPar1( rPar1 ); + if( bSetPar2 ) + pTmpField->SetPar2( sPar2 ); + + // kick off update + if(nTypeId == SwFieldTypesEnum::DDE || + nTypeId == SwFieldTypesEnum::User || + nTypeId == SwFieldTypesEnum::UserInput) + { + pType->UpdateFields(); + pSh->SetModified(); + } + else { + // mb: #32157 + pSh->SwEditShell::UpdateOneField(*pTmpField); + GetCurField(); + } + + pTmpField.reset(); + + pSh->EndAllAction(); +} + +// explicitly evaluate ExpressionFields +void SwFieldMgr::EvalExpFields(SwWrtShell* pSh) +{ + if (pSh == nullptr) + pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + + if(pSh) + { + pSh->StartAllAction(); + pSh->UpdateExpFields(true); + pSh->EndAllAction(); + } +} +LanguageType SwFieldMgr::GetCurrLanguage() const +{ + SwWrtShell* pSh = m_pWrtShell ? m_pWrtShell : ::lcl_GetShell(); + if( pSh ) + return pSh->GetCurLang(); + return SvtSysLocale().GetLanguageTag().getLanguageType(); +} + +void SwFieldType::GetFieldName_() +{ + static const char* coFieldNms[] = + { + FLD_DATE_STD, + FLD_TIME_STD, + STR_FILENAMEFLD, + STR_DBNAMEFLD, + STR_CHAPTERFLD, + STR_PAGENUMBERFLD, + STR_DOCSTATFLD, + STR_AUTHORFLD, + STR_SETFLD, + STR_GETFLD, + STR_FORMELFLD, + STR_HIDDENTXTFLD, + STR_SETREFFLD, + STR_GETREFFLD, + STR_DDEFLD, + STR_MACROFLD, + STR_INPUTFLD, + STR_HIDDENPARAFLD, + STR_DOCINFOFLD, + STR_DBFLD, + STR_USERFLD, + STR_POSTITFLD, + STR_TEMPLNAMEFLD, + STR_SEQFLD, + STR_DBNEXTSETFLD, + STR_DBNUMSETFLD, + STR_DBSETNUMBERFLD, + STR_CONDTXTFLD, + STR_NEXTPAGEFLD, + STR_PREVPAGEFLD, + STR_EXTUSERFLD, + FLD_DATE_FIX, + FLD_TIME_FIX, + STR_SETINPUTFLD, + STR_USRINPUTFLD, + STR_SETREFPAGEFLD, + STR_GETREFPAGEFLD, + STR_INTERNETFLD, + STR_JUMPEDITFLD, + STR_SCRIPTFLD, + STR_AUTHORITY, + STR_COMBINED_CHARS, + STR_DROPDOWN, + STR_CUSTOM_FIELD, + STR_PARAGRAPH_SIGNATURE + }; + + // insert infos for fields + SwFieldType::s_pFieldNames = new std::vector<OUString>; + SwFieldType::s_pFieldNames->reserve(SAL_N_ELEMENTS(coFieldNms)); + for (const char* id : coFieldNms) + { + const OUString aTmp(SwResId(id)); + SwFieldType::s_pFieldNames->push_back(MnemonicGenerator::EraseAllMnemonicChars( aTmp )); + } +} + +bool SwFieldMgr::ChooseMacro(weld::Window* pDialogParent) +{ + bool bRet = false; + + // choose script dialog + OUString aScriptURL = SfxApplication::ChooseScript(pDialogParent); + + // the script selector dialog returns a valid script URL + if ( !aScriptURL.isEmpty() ) + { + SetMacroPath( aScriptURL ); + bRet = true; + } + + return bRet; +} + +void SwFieldMgr::SetMacroPath(const OUString& rPath) +{ + m_sMacroPath = rPath; + m_sMacroName = rPath; + + // try to set sMacroName member variable by parsing the macro path + // using the new URI parsing services + + Reference< XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + Reference< uri::XUriReferenceFactory > + xFactory = uri::UriReferenceFactory::create( xContext ); + + Reference< uri::XVndSunStarScriptUrl > + xUrl( xFactory->parse( m_sMacroPath ), UNO_QUERY ); + + if ( xUrl.is() ) + { + m_sMacroName = xUrl->getName(); + } +} + +sal_uInt32 SwFieldMgr::GetDefaultFormat(SwFieldTypesEnum nTypeId, bool bIsText, SvNumberFormatter* pFormatter) +{ + SvNumFormatType nDefFormat; + + switch (nTypeId) + { + case SwFieldTypesEnum::Time: + case SwFieldTypesEnum::Date: + { + nDefFormat = (nTypeId == SwFieldTypesEnum::Date) ? SvNumFormatType::DATE : SvNumFormatType::TIME; + } + break; + + default: + if (bIsText) + { + nDefFormat = SvNumFormatType::TEXT; + } + else + { + nDefFormat = SvNumFormatType::ALL; + } + break; + } + + return pFormatter->GetStandardFormat(nDefFormat, GetCurrLanguage()); +} + +Reference<XNumberingTypeInfo> const & SwFieldMgr::GetNumberingInfo() const +{ + if(!m_xNumberingInfo.is()) + { + Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + Reference<XDefaultNumberingProvider> xDefNum = text::DefaultNumberingProvider::create(xContext); + const_cast<SwFieldMgr*>(this)->m_xNumberingInfo.set(xDefNum, UNO_QUERY); + } + return m_xNumberingInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/fldui/fldwrap.cxx b/sw/source/uibase/fldui/fldwrap.cxx new file mode 100644 index 000000000..a2a41fa34 --- /dev/null +++ b/sw/source/uibase/fldui/fldwrap.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 <cmdid.h> +#include <sfx2/basedlgs.hxx> +#include <docsh.hxx> +#include <fldwrap.hxx> + +#include <swabstdlg.hxx> + +SFX_IMPL_CHILDWINDOW_WITHID(SwFieldDlgWrapper, FN_INSERT_FIELD) + +SwChildWinWrapper::SwChildWinWrapper(vcl::Window *pParentWindow, sal_uInt16 nId) : + SfxChildWindow(pParentWindow, nId), + m_pDocSh(nullptr) +{ + // avoid flickering of buttons: + m_aUpdateTimer.SetTimeout(200); + m_aUpdateTimer.SetInvokeHandler(LINK(this, SwChildWinWrapper, UpdateHdl)); +} + +IMPL_LINK_NOARG(SwChildWinWrapper, UpdateHdl, Timer *, void) +{ + if (GetController()) + GetController()->Activate(); // update dialog +} + +// newly initialise dialog after Doc switch +bool SwChildWinWrapper::ReInitDlg(SwDocShell *) +{ + bool bRet = false; + + if (m_pDocSh != GetOldDocShell()) + { + m_aUpdateTimer.Stop(); + bRet = true; // immediate Update + } + else + m_aUpdateTimer.Start(); + + return bRet; +} + +SfxChildWinInfo SwFieldDlgWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + +SwFieldDlgWrapper::SwFieldDlgWrapper( vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pB, + SfxChildWinInfo* ) + : SwChildWinWrapper( _pParent, nId ) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlgInterface = pFact->CreateSwFieldDlg(pB, this, _pParent->GetFrameWeld()); + SetController(pDlgInterface->GetController()); + pDlgInterface->StartExecuteAsync(nullptr); +} + +// newly initialise dialog after Doc switch +bool SwFieldDlgWrapper::ReInitDlg(SwDocShell *pDocSh) +{ + bool bRet = SwChildWinWrapper::ReInitDlg(pDocSh); + if (bRet) // update immediately, Doc switch + { + pDlgInterface->ReInitDlg(); + } + + return bRet; +} + +void SwFieldDlgWrapper::ShowReferencePage() +{ + pDlgInterface->ShowReferencePage(); +} + +SFX_IMPL_CHILDWINDOW(SwFieldDataOnlyDlgWrapper, FN_INSERT_FIELD_DATA_ONLY) + +SfxChildWinInfo SwFieldDataOnlyDlgWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); +// prevent instantiation of dialog other than by calling +// the mail merge dialog + aInfo.bVisible = false; + return aInfo; +} + +SwFieldDataOnlyDlgWrapper::SwFieldDataOnlyDlgWrapper( vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pB, + SfxChildWinInfo* pInfo ) + : SwChildWinWrapper( _pParent, nId ) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlgInterface = pFact->CreateSwFieldDlg(pB, this, _pParent->GetFrameWeld()); + + SetController(pDlgInterface->GetController()); + pDlgInterface->ActivateDatabasePage(); + pDlgInterface->StartExecuteAsync(nullptr); + pDlgInterface->Initialize( pInfo ); +} + +// re-init after doc activation +bool SwFieldDataOnlyDlgWrapper::ReInitDlg(SwDocShell *pDocSh) +{ + bool bRet = SwChildWinWrapper::ReInitDlg(pDocSh); + if (bRet) // update immediately, Doc switch + { + pDlgInterface->ReInitDlg(); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/fldui/xfldui.cxx b/sw/source/uibase/fldui/xfldui.cxx new file mode 100644 index 000000000..96321fab7 --- /dev/null +++ b/sw/source/uibase/fldui/xfldui.cxx @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <fldmgr.hxx> +#include <dbmgr.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; + +// This file contains all routines of the fldui directory, which must compile +// with exceptions. So we can reduce the code of the other files, which don't +// need any exception handling. + +// Is the database field numeric? +// remark: in case of error true is returned +bool SwFieldMgr::IsDBNumeric( const OUString& rDBName, const OUString& rTableQryName, + bool bIsTable, const OUString& rFieldName) +{ + bool bNumeric = true; + + SwDBManager* pDBManager = m_pWrtShell ? m_pWrtShell->GetDBManager() : + ::GetActiveView()->GetWrtShell().GetDBManager(); + + Reference< XConnection> xConnection = + pDBManager->RegisterConnection(rDBName); + + if( !xConnection.is() ) + return bNumeric; + + Reference<XColumnsSupplier> xColsSupplier; + if(bIsTable) + { + Reference<XTablesSupplier> xTSupplier(xConnection, UNO_QUERY); + if(xTSupplier.is()) + { + Reference<XNameAccess> xTables = xTSupplier->getTables(); + OSL_ENSURE(xTables->hasByName(rTableQryName), "table not available anymore?"); + try + { + Any aTable = xTables->getByName(rTableQryName); + Reference<XPropertySet> xPropSet; + aTable >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + else + { + Reference<XQueriesSupplier> xQSupplier(xConnection, UNO_QUERY); + if(xQSupplier.is()) + { + Reference<XNameAccess> xQueries = xQSupplier->getQueries(); + OSL_ENSURE(xQueries->hasByName(rTableQryName), "table not available anymore?"); + try + { + Any aQuery = xQueries->getByName(rTableQryName); + Reference<XPropertySet> xPropSet; + aQuery >>= xPropSet; + xColsSupplier.set(xPropSet, UNO_QUERY); + } + catch (const Exception&) + { + } + } + } + + if(xColsSupplier.is()) + { + Reference <XNameAccess> xCols; + try + { + xCols = xColsSupplier->getColumns(); + } + catch (const Exception&) + { + OSL_FAIL("Exception in getColumns()"); + } + if(xCols.is() && xCols->hasByName(rFieldName)) + { + Any aCol = xCols->getByName(rFieldName); + Reference <XPropertySet> xCol; + aCol >>= xCol; + Any aType = xCol->getPropertyValue("Type"); + sal_Int32 eDataType = 0; + aType >>= eDataType; + switch(eDataType) + { + case DataType::BIT: + case DataType::BOOLEAN: + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + case DataType::BIGINT: + case DataType::FLOAT: + case DataType::REAL: + case DataType::DOUBLE: + case DataType::NUMERIC: + case DataType::DECIMAL: + case DataType::DATE: + case DataType::TIME: + case DataType::TIMESTAMP: + break; + + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + case DataType::SQLNULL: + case DataType::OTHER: + case DataType::OBJECT: + case DataType::DISTINCT: + case DataType::STRUCT: + case DataType::ARRAY: + case DataType::BLOB: + case DataType::CLOB: + case DataType::REF: + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + default: + bNumeric = false; + } + } + } + return bNumeric; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/frmdlg/colex.cxx b/sw/source/uibase/frmdlg/colex.cxx new file mode 100644 index 000000000..8d458f016 --- /dev/null +++ b/sw/source/uibase/frmdlg/colex.cxx @@ -0,0 +1,607 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <svl/eitem.hxx> +#include <tools/fract.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/sizeitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <tgrditem.hxx> +#include <viewopt.hxx> +#include <colex.hxx> +#include <colmgr.hxx> +#include <svx/unobrushitemhelper.hxx> + +void SwPageExample::UpdateExample( const SfxItemSet& rSet ) +{ + if (SfxItemState::DEFAULT <= rSet.GetItemState(RES_FRAMEDIR)) + { + const SvxFrameDirectionItem& rDirItem = rSet.Get(RES_FRAMEDIR); + m_bVertical = rDirItem.GetValue() == SvxFrameDirection::Vertical_RL_TB|| + rDirItem.GetValue() == SvxFrameDirection::Vertical_LR_TB; + } + + SfxItemPool* pPool = rSet.GetPool(); + sal_uInt16 nWhich = pPool->GetWhich( SID_ATTR_PAGE ); + if ( rSet.GetItemState( nWhich, false ) == SfxItemState::SET ) + { + // alignment + const SvxPageItem& rPage = static_cast<const SvxPageItem&>(rSet.Get(nWhich)); + SetUsage(rPage.GetPageUsage()); + } + + nWhich = pPool->GetWhich( SID_ATTR_PAGE_SIZE ); + + if ( rSet.GetItemState( nWhich, false ) == SfxItemState::SET ) + { + // orientation and size from PageItem + const SvxSizeItem& rSize = static_cast<const SvxSizeItem&>(rSet.Get( nWhich )); + SetSize( rSize.GetSize() ); + } + nWhich = RES_LR_SPACE; + if ( rSet.GetItemState( nWhich, false ) == SfxItemState::SET ) + { + // set left and right border + const SvxLRSpaceItem& rLRSpace = static_cast<const SvxLRSpaceItem&>(rSet.Get( nWhich )); + + SetLeft( rLRSpace.GetLeft() ); + SetRight( rLRSpace.GetRight() ); + } + else + { + SetLeft( 0 ); + SetRight( 0 ); + } + + nWhich = RES_UL_SPACE; + + if ( rSet.GetItemState( nWhich, false ) == SfxItemState::SET ) + { + // set upper and lower border + const SvxULSpaceItem& rULSpace = static_cast<const SvxULSpaceItem&>(rSet.Get( nWhich )); + + SetTop( rULSpace.GetUpper() ); + SetBottom( rULSpace.GetLower() ); + } + else + { + SetTop( 0 ); + SetBottom( 0 ); + } + + // evaluate header-attributes + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( pPool->GetWhich( SID_ATTR_PAGE_HEADERSET), + false, &pItem ) ) + { + const SfxItemSet& rHeaderSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rHeaderOn = + static_cast<const SfxBoolItem&>(rHeaderSet.Get( pPool->GetWhich( SID_ATTR_PAGE_ON ) ) ); + + if ( rHeaderOn.GetValue() ) + { + const SvxSizeItem& rSize = + static_cast<const SvxSizeItem&>(rHeaderSet.Get(pPool->GetWhich(SID_ATTR_PAGE_SIZE))); + + const SvxULSpaceItem& rUL = static_cast<const SvxULSpaceItem&>(rHeaderSet.Get( + pPool->GetWhich(SID_ATTR_ULSPACE))); + const SvxLRSpaceItem& rLR = static_cast<const SvxLRSpaceItem&>(rHeaderSet.Get( + pPool->GetWhich(SID_ATTR_LRSPACE))); + + SetHdHeight( rSize.GetSize().Height() - rUL.GetLower()); + SetHdDist( rUL.GetLower() ); + SetHdLeft( rLR.GetLeft() ); + SetHdRight( rLR.GetRight() ); + SetHeader( true ); + + if(SfxItemState::SET == rHeaderSet.GetItemState(RES_BACKGROUND)) + { + // create FillAttributes from SvxBrushItem //SetHdColor(rItem.GetColor()); + const SvxBrushItem& rItem = rHeaderSet.Get(RES_BACKGROUND); + SfxItemSet aTempSet(*rHeaderSet.GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + setHeaderFillAttributes( + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>( + aTempSet)); + } + } + else + SetHeader( false ); + } + + if( SfxItemState::SET == rSet.GetItemState( pPool->GetWhich( SID_ATTR_PAGE_FOOTERSET), + false, &pItem ) ) + { + const SfxItemSet& rFooterSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rFooterOn = rFooterSet.Get( SID_ATTR_PAGE_ON ); + + if ( rFooterOn.GetValue() ) + { + const SvxSizeItem& rSize = + static_cast<const SvxSizeItem&>(rFooterSet.Get( pPool->GetWhich( SID_ATTR_PAGE_SIZE ) )); + + const SvxULSpaceItem& rUL = static_cast<const SvxULSpaceItem&>(rFooterSet.Get( + pPool->GetWhich( SID_ATTR_ULSPACE ) )); + const SvxLRSpaceItem& rLR = static_cast<const SvxLRSpaceItem&>(rFooterSet.Get( + pPool->GetWhich( SID_ATTR_LRSPACE ) )); + + SetFtHeight( rSize.GetSize().Height() - rUL.GetUpper()); + SetFtDist( rUL.GetUpper() ); + SetFtLeft( rLR.GetLeft() ); + SetFtRight( rLR.GetRight() ); + SetFooter( true ); + + if( rFooterSet.GetItemState( RES_BACKGROUND ) == SfxItemState::SET ) + { + // create FillAttributes from SvxBrushItem //SetFtColor(rItem.GetColor()); + const SvxBrushItem& rItem = rFooterSet.Get(RES_BACKGROUND); + SfxItemSet aTempSet(*rFooterSet.GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + setFooterFillAttributes( + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>( + aTempSet)); + } + } + else + SetFooter( false ); + } + + if(SfxItemState::SET == rSet.GetItemState(RES_BACKGROUND, false, &pItem)) + { + // create FillAttributes from SvxBrushItem + const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(*pItem); + SfxItemSet aTempSet(*rSet.GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + setPageFillAttributes( + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>( + aTempSet)); + } + + Invalidate(); +} + +void SwColExample::DrawPage(vcl::RenderContext& rRenderContext, const Point& rOrg, + const bool bSecond, const bool bEnabled) +{ + SwPageExample::DrawPage(rRenderContext, rOrg, bSecond, bEnabled); + if (!pColMgr) + return; + sal_uInt16 nColumnCount = pColMgr->GetCount(); + if (!nColumnCount) + return; + + long nL = GetLeft(); + long nR = GetRight(); + + if (GetUsage() == SvxPageUsage::Mirror && !bSecond) + { + // swap for mirrored + nL = GetRight(); + nR = GetLeft(); + } + + rRenderContext.SetFillColor(COL_LIGHTGRAY); + tools::Rectangle aRect; + aRect.SetRight( rOrg.X() + GetSize().Width() - nR ); + aRect.SetLeft( rOrg.X() + nL ); + aRect.SetTop( rOrg.Y() + GetTop() + GetHdHeight() + GetHdDist() ); + aRect.SetBottom( rOrg.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist() ); + rRenderContext.DrawRect(aRect); + + const tools::Rectangle aDefineRect(aRect); + const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes = getPageFillAttributes(); + + if (!rFillAttributes || !rFillAttributes->isUsed()) + { + // If there is no fill, use fallback color + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const Color& rFieldColor = rStyleSettings.GetFieldColor(); + + setPageFillAttributes( + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>( + rFieldColor)); + } + + // #97495# make sure that the automatic column width's are always equal + bool bAutoWidth = pColMgr->IsAutoWidth(); + sal_Int32 nAutoColWidth = 0; + if (bAutoWidth) + { + sal_Int32 nColumnWidthSum = 0; + for (sal_uInt16 i = 0; i < nColumnCount; ++i) + nColumnWidthSum += pColMgr->GetColWidth( i ); + nAutoColWidth = nColumnWidthSum / nColumnCount; + } + + for (sal_uInt16 i = 0; i < nColumnCount; ++i) + { + if (!bAutoWidth) + nAutoColWidth = pColMgr->GetColWidth(i); + + if (!m_bVertical) + aRect.SetRight( aRect.Left() + nAutoColWidth ); + else + aRect.SetBottom( aRect.Top() + nAutoColWidth ); + + // use primitive draw command + drawFillAttributes(rRenderContext, getPageFillAttributes(), aRect, aDefineRect); + + if (i < nColumnCount - 1) + { + if (!m_bVertical) + aRect.SetLeft( aRect.Right() + pColMgr->GetGutterWidth(i) ); + else + aRect.SetTop( aRect.Bottom() + pColMgr->GetGutterWidth(i) ); + } + } + if (pColMgr->HasLine()) + { + Point aUp(rOrg.X() + nL, rOrg.Y() + GetTop()); + Point aDown(rOrg.X() + nL, + rOrg.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist()); + + if (pColMgr->GetLineHeightPercent() != 100) + { + long nLength = !m_bVertical ? aDown.Y() - aUp.Y() : aDown.X() - aUp.X(); + nLength -= nLength * pColMgr->GetLineHeightPercent() / 100; + switch (pColMgr->GetAdjust()) + { + case COLADJ_BOTTOM: + if (!m_bVertical) + aUp.AdjustY(nLength ); + else + aUp.AdjustX(nLength ); + break; + case COLADJ_TOP: + if (!m_bVertical) + aDown.AdjustY( -nLength ); + else + aDown.AdjustX( -nLength ); + break; + case COLADJ_CENTER: + if (!m_bVertical) + { + aUp.AdjustY(nLength / 2 ); + aDown.AdjustY( -(nLength / 2) ); + } + else + { + aUp.AdjustX(nLength / 2 ); + aDown.AdjustX( -(nLength / 2) ); + } + break; + default: + break; // prevent warning + } + } + + for (sal_uInt16 i = 0; i < nColumnCount - 1; ++i) + { + int nGutter = pColMgr->GetGutterWidth(i); + int nDist = pColMgr->GetColWidth( i ) + nGutter; + nDist -= (i == 0) ? nGutter / 2 : 0; + if (!m_bVertical) + { + aUp.AdjustX(nDist ); + aDown.AdjustX(nDist ); + } + else + { + aUp.AdjustY(nDist ); + aDown.AdjustY(nDist ); + } + + rRenderContext.DrawLine(aUp, aDown); + } + } +} + +SwColumnOnlyExample::SwColumnOnlyExample() + : m_aFrameSize(SvxPaperInfo::GetPaperSize(PAPER_A4)) // DIN A4 +{ + ::FitToActualSize(m_aCols, static_cast<sal_uInt16>(m_aFrameSize.Width())); +} + +void SwColumnOnlyExample::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) +{ + rRenderContext.Push(PushFlags::MAPMODE); + + Fraction aScale(m_aWinSize.Height(), m_aFrameSize.Height()); + MapMode aMapMode(MapUnit::MapTwip); + aMapMode.SetScaleX(aScale); + aMapMode.SetScaleY(aScale); + rRenderContext.SetMapMode(aMapMode); + + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const Color& rFieldColor = rStyleSettings.GetFieldColor(); + const Color& rDlgColor = rStyleSettings.GetDialogColor(); + const Color& rFieldTextColor = SwViewOption::GetFontColor(); + Color aGrayColor(COL_LIGHTGRAY); + if (rFieldColor == aGrayColor) + aGrayColor.Invert(); + + Size aLogSize(rRenderContext.PixelToLogic(GetOutputSizePixel())); + tools::Rectangle aCompleteRect(Point(0,0), aLogSize); + rRenderContext.SetLineColor(rDlgColor); + rRenderContext.SetFillColor(rDlgColor); + rRenderContext.DrawRect(aCompleteRect); + + rRenderContext.SetLineColor(rFieldTextColor); + Point aTL((aLogSize.Width() - m_aFrameSize.Width()) / 2, + (aLogSize.Height() - m_aFrameSize.Height()) / 2); + tools::Rectangle aRect(aTL, m_aFrameSize); + + //draw a shadow rectangle + rRenderContext.SetFillColor(COL_GRAY); + tools::Rectangle aShadowRect(aRect); + aShadowRect.Move(aTL.Y(), aTL.Y()); + rRenderContext.DrawRect(aShadowRect); + + rRenderContext.SetFillColor(rFieldColor); + rRenderContext.DrawRect(aRect); + + rRenderContext.SetFillColor(aGrayColor); + + //column separator? + long nLength = aLogSize.Height() - 2 * aTL.Y(); + Point aUp(aTL); + Point aDown(aTL.X(), nLength); + bool bLines = false; + if (m_aCols.GetLineAdj() != COLADJ_NONE) + { + bLines = true; + + sal_uInt16 nPercent = m_aCols.GetLineHeight(); + if (nPercent != 100) + { + nLength -= nLength * nPercent / 100; + switch(m_aCols.GetLineAdj()) + { + case COLADJ_BOTTOM: aUp.AdjustY(nLength ); break; + case COLADJ_TOP: aDown.AdjustY( -nLength ); break; + case COLADJ_CENTER: + aUp.AdjustY(nLength / 2 ); + aDown.AdjustY( -(nLength / 2) ); + break; + default: + break; //prevent warning + } + } + + } + const SwColumns& rCols = m_aCols.GetColumns(); + sal_uInt16 nColCount = rCols.size(); + if (nColCount) + { + rRenderContext.DrawRect(aRect); + rRenderContext.SetFillColor(rFieldColor); + tools::Rectangle aFrameRect(aTL, m_aFrameSize); + long nSum = aTL.X(); + for (sal_uInt16 i = 0; i < nColCount; i++) + { + const SwColumn* pCol = &rCols[i]; + aFrameRect.SetLeft( nSum + pCol->GetLeft() ); //nSum + pCol->GetLeft() + aTL.X(); + nSum += pCol->GetWishWidth(); + aFrameRect.SetRight( nSum - pCol->GetRight() ); + rRenderContext.DrawRect(aFrameRect); + } + if (bLines) + { + nSum = aTL.X(); + for (sal_uInt16 i = 0; i < nColCount - 1; i++) + { + nSum += rCols[i].GetWishWidth(); + aUp.setX( nSum ); + aDown.setX( nSum ); + rRenderContext.DrawLine(aUp, aDown); + } + } + } + rRenderContext.Pop(); +} + +void SwColumnOnlyExample::SetColumns(const SwFormatCol& rCol) +{ + m_aCols = rCol; + sal_uInt16 nWishSum = m_aCols.GetWishWidth(); + long nFrameWidth = m_aFrameSize.Width(); + SwColumns& rCols = m_aCols.GetColumns(); + sal_uInt16 nColCount = rCols.size(); + + for(sal_uInt16 i = 0; i < nColCount; i++) + { + SwColumn* pCol = &rCols[i]; + long nWish = pCol->GetWishWidth(); + nWish *= nFrameWidth; + nWish /= nWishSum; + pCol->SetWishWidth(static_cast<sal_uInt16>(nWish)); + long nLeft = pCol->GetLeft(); + nLeft *= nFrameWidth; + nLeft /= nWishSum; + pCol->SetLeft(static_cast<sal_uInt16>(nLeft)); + long nRight = pCol->GetRight(); + nRight *= nFrameWidth; + nRight /= nWishSum; + pCol->SetRight(static_cast<sal_uInt16>(nRight)); + } + // #97495# make sure that the automatic column width's are always equal + if(nColCount && m_aCols.IsOrtho()) + { + sal_Int32 nColumnWidthSum = 0; + sal_uInt16 i; + for(i = 0; i < nColCount; ++i) + { + SwColumn* pCol = &rCols[i]; + nColumnWidthSum += pCol->GetWishWidth(); + nColumnWidthSum -= (pCol->GetRight() + pCol->GetLeft()); + } + nColumnWidthSum /= nColCount; + for(i = 0; i < nColCount; ++i) + { + SwColumn* pCol = &rCols[i]; + pCol->SetWishWidth( static_cast< sal_uInt16 >(nColumnWidthSum + pCol->GetRight() + pCol->GetLeft())); + } + } +} + +void SwColumnOnlyExample::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + OutputDevice& rRefDevice = pDrawingArea->get_ref_device(); + Size aPrefSize(rRefDevice.LogicToPixel(Size(75, 46), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height()); +} + +void SwColumnOnlyExample::Resize() +{ + OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device(); + rRefDevice.Push(PushFlags::MAPMODE); + rRefDevice.SetMapMode(MapMode(MapUnit::MapTwip)); + m_aWinSize = GetOutputSizePixel(); + m_aWinSize.AdjustHeight( -4 ); + m_aWinSize.AdjustWidth( -4 ); + m_aWinSize = rRefDevice.PixelToLogic(m_aWinSize); + rRefDevice.Pop(); + Invalidate(); +} + +SwPageGridExample::SwPageGridExample() +{ +} + +void SwPageGridExample::DrawPage(vcl::RenderContext& rRenderContext, const Point& rOrg, + const bool bSecond, const bool bEnabled) +{ + SwPageExample::DrawPage(rRenderContext, rOrg, bSecond, bEnabled); + + if (!pGridItem || !pGridItem->GetGridType()) + return; + + //paint the grid now + Color aLineColor = pGridItem->GetColor(); + if (aLineColor == COL_AUTO) + { + aLineColor = rRenderContext.GetFillColor(); + aLineColor.Invert(); + } + rRenderContext.SetLineColor(aLineColor); + long nL = GetLeft(); + long nR = GetRight(); + + if (GetUsage() == SvxPageUsage::Mirror && !bSecond) + { + // rotate for mirrored + nL = GetRight(); + nR = GetLeft(); + } + + tools::Rectangle aRect; + aRect.SetRight( rOrg.X() + GetSize().Width() - nR ); + aRect.SetLeft( rOrg.X() + nL ); + aRect.SetTop( rOrg.Y() + GetTop() + GetHdHeight() + GetHdDist() ); + aRect.SetBottom( rOrg.Y() + GetSize().Height() - GetBottom() - GetFtHeight() - GetFtDist() ); + + //increase the values to get a 'viewable' preview + sal_Int32 nBaseHeight = pGridItem->GetBaseHeight() * 3; + sal_Int32 nRubyHeight = pGridItem->GetRubyHeight() * 3; + + //detect height of rectangles + tools::Rectangle aRubyRect(aRect.TopLeft(), + m_bVertical ? + Size(nRubyHeight, aRect.GetHeight()) : + Size(aRect.GetWidth(), nRubyHeight)); + tools::Rectangle aCharRect(aRect.TopLeft(), + m_bVertical ? + Size(nBaseHeight, aRect.GetHeight()) : + Size(aRect.GetWidth(), nBaseHeight)); + + sal_Int32 nLineHeight = nBaseHeight + nRubyHeight; + + //detect count of rectangles + sal_Int32 nLines = (m_bVertical ? aRect.GetWidth(): aRect.GetHeight()) / nLineHeight; + if (nLines > pGridItem->GetLines()) + nLines = pGridItem->GetLines(); + + // determine start position + if (m_bVertical) + { + sal_Int16 nXStart = static_cast<sal_Int16>(aRect.GetWidth() / 2 - nLineHeight * nLines /2); + aRubyRect.Move(nXStart, 0); + aCharRect.Move(nXStart, 0); + } + else + { + sal_Int16 nYStart = static_cast<sal_Int16>(aRect.GetHeight() / 2 - nLineHeight * nLines /2); + aRubyRect.Move(0, nYStart); + aCharRect.Move(0, nYStart); + } + + if (pGridItem->IsRubyTextBelow()) + m_bVertical ? aRubyRect.Move(nBaseHeight, 0) : aRubyRect.Move(0, nBaseHeight); + else + m_bVertical ? aCharRect.Move(nRubyHeight, 0) : aCharRect.Move(0, nRubyHeight); + + //vertical lines + bool bBothLines = pGridItem->GetGridType() == GRID_LINES_CHARS; + rRenderContext.SetFillColor(COL_TRANSPARENT); + sal_Int32 nXMove = m_bVertical ? nLineHeight : 0; + sal_Int32 nYMove = m_bVertical ? 0 : nLineHeight; + for (sal_Int32 nLine = 0; nLine < nLines; nLine++) + { + rRenderContext.DrawRect(aRubyRect); + rRenderContext.DrawRect(aCharRect); + if (bBothLines) + { + Point aStart = aCharRect.TopLeft(); + Point aEnd = m_bVertical ? aCharRect.TopRight() : aCharRect.BottomLeft(); + while (m_bVertical ? aStart.Y() < aRect.Bottom(): aStart.X() < aRect.Right()) + { + rRenderContext.DrawLine(aStart, aEnd); + if (m_bVertical) + aStart.setY( aEnd.AdjustY(nBaseHeight ) ); + else + aStart.setX( aEnd.AdjustX(nBaseHeight ) ); + } + } + aRubyRect.Move(nXMove, nYMove); + aCharRect.Move(nXMove, nYMove); + } + +} + +void SwPageGridExample::UpdateExample( const SfxItemSet& rSet ) +{ + pGridItem.reset(); + //get the grid information + if (SfxItemState::DEFAULT <= rSet.GetItemState(RES_TEXTGRID)) + pGridItem.reset(rSet.Get(RES_TEXTGRID).Clone()); + SwPageExample::UpdateExample(rSet); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/frmdlg/colmgr.cxx b/sw/source/uibase/frmdlg/colmgr.cxx new file mode 100644 index 000000000..8dda357ee --- /dev/null +++ b/sw/source/uibase/frmdlg/colmgr.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 <sal/config.h> + +#include <algorithm> + +#include <hintids.hxx> +#include <editeng/lrspitem.hxx> +#include <osl/diagnose.h> + +#include <colmgr.hxx> +#include <fmtfsize.hxx> +#include <swtypes.hxx> + +// private methods + +// set column width to current width +void FitToActualSize(SwFormatCol& rCol, sal_uInt16 nWidth) +{ + const sal_uInt16 nCount = rCol.GetColumns().size(); + for(sal_uInt16 i = 0; i < nCount; ++i) + { + const sal_uInt16 nTmp = rCol.CalcColWidth(i, nWidth); + auto & col = rCol.GetColumns()[i]; + col.SetWishWidth(nTmp); + // If necessary, shrink borders (as equally as possible) to keep up the invariant that + // GetWishWidth() >= GetLeft() + GetRight(): + sal_uInt32 const borders = col.GetLeft() + col.GetRight(); + if (borders > nTmp) + { + auto const shrink = borders - nTmp; + auto const half = shrink / 2; // rounds down + if (col.GetLeft() < col.GetRight()) + { + auto const shrinkLeft = std::min(sal_uInt32(col.GetLeft()), half); + col.SetLeft(col.GetLeft() - shrinkLeft); + col.SetRight(col.GetRight() - (shrink - shrinkLeft)); + } + else + { + auto const shrinkRight = std::min(sal_uInt32(col.GetRight()), half); + col.SetLeft(col.GetLeft() - (shrink - shrinkRight)); + col.SetRight(col.GetRight() - shrinkRight); + } + } + } + rCol.SetWishWidth(nWidth); +} + +// public methods + +// set column quantity and Gutterwidth +void SwColMgr::SetCount(sal_uInt16 nCount, sal_uInt16 nGutterWidth) +{ + aFormatCol.Init(nCount, nGutterWidth, nWidth); + aFormatCol.SetWishWidth(nWidth); + aFormatCol.SetGutterWidth(nGutterWidth, nWidth); +} + +sal_uInt16 SwColMgr::GetGutterWidth( sal_uInt16 nPos ) const +{ + sal_uInt16 nRet; + if(nPos == USHRT_MAX ) + nRet = GetCount() > 1 ? aFormatCol.GetGutterWidth() : DEF_GUTTER_WIDTH; + else + { + OSL_ENSURE(nPos < GetCount() - 1, "column overindexed" ); + const SwColumns& rCols = aFormatCol.GetColumns(); + nRet = rCols[nPos].GetRight() + rCols[nPos + 1].GetLeft(); + } + return nRet; +} + +void SwColMgr::SetGutterWidth(sal_uInt16 nGutterWidth, sal_uInt16 nPos ) +{ + if(nPos == USHRT_MAX) + aFormatCol.SetGutterWidth(nGutterWidth, nWidth); + else + { + OSL_ENSURE(nPos < GetCount() - 1, "column overindexed" ); + SwColumns& rCols = aFormatCol.GetColumns(); + sal_uInt16 nGutterWidth2 = nGutterWidth / 2; + rCols[nPos].SetRight(nGutterWidth2); + rCols[nPos + 1].SetLeft(nGutterWidth2); + } +} + +// height separation line +short SwColMgr::GetLineHeightPercent() const +{ + return static_cast<short>(aFormatCol.GetLineHeight()); +} +void SwColMgr::SetLineHeightPercent(short nPercent) +{ + OSL_ENSURE(nPercent <= 100, "line height may be at most 100%"); + aFormatCol.SetLineHeight(static_cast<sal_uInt8>(nPercent)); +} + +// column width +sal_uInt16 SwColMgr::GetColWidth(sal_uInt16 nIdx) const +{ + OSL_ENSURE(nIdx < GetCount(), "Column array overindexed."); + return aFormatCol.CalcPrtColWidth(nIdx, nWidth); +} + +void SwColMgr::SetColWidth(sal_uInt16 nIdx, sal_uInt16 nWd) +{ + OSL_ENSURE(nIdx < GetCount(), "Column array overindexed."); + aFormatCol.GetColumns()[nIdx].SetWishWidth(nWd); + +} + +// newly set size +void SwColMgr::SetActualWidth(sal_uInt16 nW) +{ + nWidth = nW; + ::FitToActualSize(aFormatCol, nW); +} + +// ctor +SwColMgr::SwColMgr(const SfxItemSet& rSet) : + aFormatCol(rSet.Get(RES_COL)) +{ + nWidth = static_cast<sal_uInt16>(rSet.Get(RES_FRM_SIZE).GetWidth()); + if (nWidth < MINLAY) + nWidth = USHRT_MAX; + const SvxLRSpaceItem &rLR = rSet.Get(RES_LR_SPACE); + nWidth = nWidth - static_cast<sal_uInt16>(rLR.GetLeft()); + nWidth = nWidth - static_cast<sal_uInt16>(rLR.GetRight()); + ::FitToActualSize(aFormatCol, nWidth); +} + +SwColMgr::~SwColMgr() +{ +} + +void SwColMgr::SetLineWidthAndColor(SvxBorderLineStyle eStyle, sal_uLong nLWidth, const Color& rCol) +{ + aFormatCol.SetLineStyle(eStyle); + aFormatCol.SetLineWidth(nLWidth); + aFormatCol.SetLineColor(rCol); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/frmdlg/frmmgr.cxx b/sw/source/uibase/frmdlg/frmmgr.cxx new file mode 100644 index 000000000..926a949e2 --- /dev/null +++ b/sw/source/uibase/frmdlg/frmmgr.cxx @@ -0,0 +1,613 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> + +#include <svl/stritem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/shaditem.hxx> +#include <svx/swframevalidation.hxx> +#include <svx/xdef.hxx> +#include <tools/globname.hxx> +#include <comphelper/classids.hxx> +#include <fmtclds.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <uitool.hxx> +#include <frmmgr.hxx> +#include <format.hxx> +#include <mdiexp.hxx> +#include <poolfmt.hxx> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <grfatr.hxx> + +using namespace ::com::sun::star; + +static sal_uInt16 aFrameMgrRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, // 87-129 + + // RotGrfFlyFrame: Support here, but seems not to be + // added in range of m_pOwnSh->GetFlyFrameAttr result + // (see below). Tried to find, but could not identify + RES_GRFATR_ROTATION, RES_GRFATR_ROTATION, // 132 + + // FillAttribute support + XATTR_FILL_FIRST, XATTR_FILL_LAST, // 1014-1033 + + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + FN_SET_FRM_NAME, FN_SET_FRM_NAME, + 0}; + +// determine frame attributes via Shell +SwFlyFrameAttrMgr::SwFlyFrameAttrMgr( bool bNew, SwWrtShell* pSh, Frmmgr_Type nType, const SvGlobalName* pName ) : + m_aSet( static_cast<SwAttrPool&>(pSh->GetAttrPool()), aFrameMgrRange ), + m_pOwnSh( pSh ), + m_bAbsPos( false ), + m_bNewFrame( bNew ), + m_bIsInVertical( false ), + m_bIsInVerticalL2R( false ) +{ + if ( m_bNewFrame ) + { + sal_uInt16 nId; + switch ( nType ) + { + case Frmmgr_Type::TEXT: nId = RES_POOLFRM_FRAME; break; + case Frmmgr_Type::OLE: nId = RES_POOLFRM_OLE; break; + case Frmmgr_Type::GRF: nId = RES_POOLFRM_GRAPHIC; break; + // set defaults: + default: nId=0; break; + } + m_aSet.SetParent( &m_pOwnSh->GetFormatFromPool( nId )->GetAttrSet()); + m_aSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, DFLT_WIDTH, DFLT_HEIGHT )); + if ( 0 != ::GetHtmlMode(pSh->GetView().GetDocShell()) ) + m_aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT, text::RelOrientation::PRINT_AREA ) ); + + if (nType == Frmmgr_Type::GRF || nType == Frmmgr_Type::OLE) + { + if (!pName || *pName != SvGlobalName( SO3_SM_CLASSID )) + { + // Default anchor for new graphics and objects is at-char, except for Math objects. + m_aSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_CHAR)); + } + } + } + else if ( nType == Frmmgr_Type::NONE ) + { + m_pOwnSh->GetFlyFrameAttr( m_aSet ); + bool bRightToLeft; + m_bIsInVertical = m_pOwnSh->IsFrameVertical(true, bRightToLeft, m_bIsInVerticalL2R); + } + ::PrepareBoxInfo( m_aSet, *m_pOwnSh ); +} + +SwFlyFrameAttrMgr::SwFlyFrameAttrMgr( bool bNew, SwWrtShell* pSh, const SfxItemSet &rSet ) : + m_aSet( rSet ), + m_pOwnSh( pSh ), + m_bAbsPos( false ), + m_bNewFrame( bNew ), + m_bIsInVertical(false), + m_bIsInVerticalL2R(false) +{ + if(!bNew) + { + bool bRightToLeft; + m_bIsInVertical = pSh->IsFrameVertical(true, bRightToLeft, m_bIsInVerticalL2R); + } +} + +// Initialise +void SwFlyFrameAttrMgr::UpdateAttrMgr() +{ + if ( !m_bNewFrame && m_pOwnSh->IsFrameSelected() ) + m_pOwnSh->GetFlyFrameAttr( m_aSet ); + ::PrepareBoxInfo( m_aSet, *m_pOwnSh ); +} + +void SwFlyFrameAttrMgr::UpdateFlyFrame_() +{ + const SfxPoolItem* pItem = nullptr; + + if (m_aSet.GetItemState(FN_SET_FRM_NAME, false, &pItem) == SfxItemState::SET) + m_pOwnSh->SetFlyName(static_cast<const SfxStringItem *>(pItem)->GetValue()); + + m_pOwnSh->SetModified(); + + if ( m_bAbsPos ) + { + m_pOwnSh->SetFlyPos( m_aAbsPos ); + m_bAbsPos = false; + } +} + +// change existing Fly-Frame +void SwFlyFrameAttrMgr::UpdateFlyFrame() +{ + OSL_ENSURE( m_pOwnSh->IsFrameSelected(), + "no frame selected or no shell, update not possible"); + + if( m_pOwnSh->IsFrameSelected() ) + { + //JP 6.8.2001: set never an invalid anchor into the core. + const SfxPoolItem *pGItem, *pItem; + if( SfxItemState::SET == m_aSet.GetItemState( RES_ANCHOR, false, &pItem )) + { + SfxItemSet aGetSet( *m_aSet.GetPool(), svl::Items<RES_ANCHOR, RES_ANCHOR>{} ); + if( m_pOwnSh->GetFlyFrameAttr( aGetSet ) && 1 == aGetSet.Count() && + SfxItemState::SET == aGetSet.GetItemState( RES_ANCHOR, false, &pGItem ) + && static_cast<const SwFormatAnchor*>(pGItem)->GetAnchorId() == + static_cast<const SwFormatAnchor*>(pItem)->GetAnchorId() ) + m_aSet.ClearItem( RES_ANCHOR ); + } + + // return wg. BASIC + if( m_aSet.Count() ) + { + m_pOwnSh->StartAllAction(); + m_pOwnSh->SetFlyFrameAttr( m_aSet ); + UpdateFlyFrame_(); + m_pOwnSh->EndAllAction(); + } + } +} + +// insert frame +void SwFlyFrameAttrMgr::InsertFlyFrame() +{ + m_pOwnSh->StartAllAction(); + + bool bRet = nullptr != m_pOwnSh->NewFlyFrame( m_aSet ); + + // turn on the right mode at the shell, frame got selected automatically. + if ( bRet ) + { + UpdateFlyFrame_(); + m_pOwnSh->EnterSelFrameMode(); + FrameNotify(m_pOwnSh, FLY_DRAG_START); + } + m_pOwnSh->EndAllAction(); +} + +// Insert frames of type eAnchorType. Position and size are being set explicitly. +// Not-allowed values of the enumeration type get corrected. +void SwFlyFrameAttrMgr::InsertFlyFrame(RndStdIds eAnchorType, + const Point &rPos, + const Size &rSize ) +{ + OSL_ENSURE( eAnchorType == RndStdIds::FLY_AT_PAGE || + eAnchorType == RndStdIds::FLY_AT_PARA || + eAnchorType == RndStdIds::FLY_AT_CHAR || + eAnchorType == RndStdIds::FLY_AT_FLY || + eAnchorType == RndStdIds::FLY_AS_CHAR, "invalid frame type" ); + + SetPos( rPos ); + + SetSize( rSize ); + SetAnchor( eAnchorType ); + InsertFlyFrame(); +} + +// set anchor +void SwFlyFrameAttrMgr::SetAnchor( RndStdIds eId ) +{ + sal_uInt16 nPhyPageNum, nVirtPageNum; + m_pOwnSh->GetPageNum( nPhyPageNum, nVirtPageNum ); + + m_aSet.Put( SwFormatAnchor( eId, nPhyPageNum ) ); + if ((RndStdIds::FLY_AT_PAGE == eId) || (RndStdIds::FLY_AT_PARA == eId) || (RndStdIds::FLY_AT_CHAR == eId) + || (RndStdIds::FLY_AT_FLY == eId)) + { + SwFormatVertOrient aVertOrient( GetVertOrient() ); + SwFormatHoriOrient aHoriOrient( GetHoriOrient() ); + aHoriOrient.SetRelationOrient( text::RelOrientation::FRAME ); + aVertOrient.SetRelationOrient( text::RelOrientation::FRAME ); + m_aSet.Put( aVertOrient ); + m_aSet.Put( aHoriOrient ); + } +} + +// set the attribute for columns +void SwFlyFrameAttrMgr::SetCol( const SwFormatCol &rCol ) +{ + m_aSet.Put( rCol ); +} + +// set absolute position +void SwFlyFrameAttrMgr::SetAbsPos( const Point& rPoint ) +{ + m_bAbsPos = true; + m_aAbsPos = rPoint; + SwFormatVertOrient aVertOrient( GetVertOrient() ); + SwFormatHoriOrient aHoriOrient( GetHoriOrient() ); + aHoriOrient.SetHoriOrient( text::HoriOrientation::NONE ); + aVertOrient.SetVertOrient( text::VertOrientation::NONE ); + m_aSet.Put( aVertOrient ); + m_aSet.Put( aHoriOrient ); +} + +// check metrics for correctness +void SwFlyFrameAttrMgr::ValidateMetrics( SvxSwFrameValidation& rVal, + const SwPosition* pToCharContentPos, + bool bOnlyPercentRefValue ) +{ + if (!bOnlyPercentRefValue) + { + rVal.nMinHeight = MINFLY + CalcTopSpace() + CalcBottomSpace(); + rVal.nMinWidth = MINFLY + CalcLeftSpace()+ CalcRightSpace(); + } + + SwRect aBoundRect; + + // OD 18.09.2003 #i18732# - adjustment for allowing vertical position + // aligned to page for fly frame anchored to paragraph or to character. + const RndStdIds eAnchorType = rVal.nAnchorType; + const SwFormatFrameSize& rSize = m_aSet.Get(RES_FRM_SIZE); + m_pOwnSh->CalcBoundRect( aBoundRect, eAnchorType, + rVal.nHRelOrient, + rVal.nVRelOrient, + pToCharContentPos, + rVal.bFollowTextFlow, + rVal.bMirror, nullptr, &rVal.aPercentSize, + &rSize); + + if (bOnlyPercentRefValue) + return; + + // #mongolianlayout# + if ( m_bIsInVertical || m_bIsInVerticalL2R ) + { + Point aPos(aBoundRect.Pos()); + long nTmp = aPos.X(); + aPos.setX( aPos.Y() ); + aPos.setY( nTmp ); + Size aSize(aBoundRect.SSize()); + nTmp = aSize.Width(); + aSize.setWidth( aSize.Height() ); + aSize.setHeight( nTmp ); + aBoundRect.Chg( aPos, aSize ); + //exchange width/height to enable correct values + nTmp = rVal.nWidth; + rVal.nWidth = rVal.nHeight; + rVal.nHeight = nTmp; + } + if ((eAnchorType == RndStdIds::FLY_AT_PAGE) || (eAnchorType == RndStdIds::FLY_AT_FLY)) + { + // MinimalPosition + rVal.nMinHPos = aBoundRect.Left(); + rVal.nMinVPos = aBoundRect.Top(); + SwTwips nH = rVal.nHPos; + SwTwips nV = rVal.nVPos; + + if (rVal.nHPos + rVal.nWidth > aBoundRect.Right()) + { + if (rVal.nHoriOrient == text::HoriOrientation::NONE) + { + rVal.nHPos -= ((rVal.nHPos + rVal.nWidth) - aBoundRect.Right()); + nH = rVal.nHPos; + } + else + rVal.nWidth = aBoundRect.Right() - rVal.nHPos; + } + + if (rVal.nHPos + rVal.nWidth > aBoundRect.Right()) + rVal.nWidth = aBoundRect.Right() - rVal.nHPos; + + if (rVal.nVPos + rVal.nHeight > aBoundRect.Bottom()) + { + if (rVal.nVertOrient == text::VertOrientation::NONE) + { + rVal.nVPos -= ((rVal.nVPos + rVal.nHeight) - aBoundRect.Bottom()); + nV = rVal.nVPos; + } + else + rVal.nHeight = aBoundRect.Bottom() - rVal.nVPos; + } + + if (rVal.nVPos + rVal.nHeight > aBoundRect.Bottom()) + rVal.nHeight = aBoundRect.Bottom() - rVal.nVPos; + + if ( rVal.nVertOrient != text::VertOrientation::NONE ) + nV = aBoundRect.Top(); + + if ( rVal.nHoriOrient != text::HoriOrientation::NONE ) + nH = aBoundRect.Left(); + + rVal.nMaxHPos = aBoundRect.Right() - rVal.nWidth; + rVal.nMaxHeight = aBoundRect.Bottom() - nV; + + rVal.nMaxVPos = aBoundRect.Bottom() - rVal.nHeight; + rVal.nMaxWidth = aBoundRect.Right() - nH; + } + // OD 12.11.2003 #i22341# - handle to character anchored objects vertical + // aligned at character or top of line in a special case + else if ((eAnchorType == RndStdIds::FLY_AT_PARA) || + ((eAnchorType == RndStdIds::FLY_AT_CHAR) && + (rVal.nVRelOrient != text::RelOrientation::CHAR) && + (rVal.nVRelOrient != text::RelOrientation::TEXT_LINE) ) ) + { + if (rVal.nHPos + rVal.nWidth > aBoundRect.Right()) + { + if (rVal.nHoriOrient == text::HoriOrientation::NONE) + { + rVal.nHPos -= ((rVal.nHPos + rVal.nWidth) - aBoundRect.Right()); + } + else + rVal.nWidth = aBoundRect.Right() - rVal.nHPos; + } + + // OD 29.09.2003 #i17567#, #i18732# - consider following the text flow + // and alignment at page areas. + const bool bMaxVPosAtBottom = !rVal.bFollowTextFlow || + rVal.nVRelOrient == text::RelOrientation::PAGE_FRAME || + rVal.nVRelOrient == text::RelOrientation::PAGE_PRINT_AREA; + { + SwTwips nTmpMaxVPos = ( bMaxVPosAtBottom + ? aBoundRect.Bottom() + : aBoundRect.Height() ) - + rVal.nHeight; + if ( rVal.nVPos > nTmpMaxVPos ) + { + if (rVal.nVertOrient == text::VertOrientation::NONE) + { + rVal.nVPos = nTmpMaxVPos; + } + else + { + rVal.nHeight = ( bMaxVPosAtBottom + ? aBoundRect.Bottom() + : aBoundRect.Height() ) - rVal.nVPos; + } + } + } + + rVal.nMinHPos = aBoundRect.Left(); + rVal.nMaxHPos = aBoundRect.Right() - rVal.nWidth; + + rVal.nMinVPos = aBoundRect.Top(); + // OD 26.09.2003 #i17567#, #i18732# - determine maximum vertical position + if ( bMaxVPosAtBottom ) + { + rVal.nMaxVPos = aBoundRect.Bottom() - rVal.nHeight; + } + else + { + rVal.nMaxVPos = aBoundRect.Height() - rVal.nHeight; + } + + // maximum width height + const SwTwips nH = ( rVal.nHoriOrient != text::HoriOrientation::NONE ) + ? aBoundRect.Left() + : rVal.nHPos; + const SwTwips nV = ( rVal.nVertOrient != text::VertOrientation::NONE ) + ? aBoundRect.Top() + : rVal.nVPos; + rVal.nMaxHeight = rVal.nMaxVPos + rVal.nHeight - nV; + rVal.nMaxWidth = rVal.nMaxHPos + rVal.nWidth - nH; + } + // OD 12.11.2003 #i22341# - special case for to character anchored objects + // vertical aligned at character or top of line. + // Note: (1) positive vertical values are positions above the top of line + // (2) negative vertical values are positions below the top of line + else if ( (eAnchorType == RndStdIds::FLY_AT_CHAR) && + ( rVal.nVRelOrient == text::RelOrientation::CHAR || + rVal.nVRelOrient == text::RelOrientation::TEXT_LINE ) ) + { + // determine horizontal values + rVal.nMinHPos = aBoundRect.Left(); + + rVal.nMaxHPos = aBoundRect.Right() - rVal.nWidth; + if (rVal.nHPos + rVal.nWidth > aBoundRect.Right()) + { + if (rVal.nHoriOrient == text::HoriOrientation::NONE) + { + rVal.nHPos -= ((rVal.nHPos + rVal.nWidth) - aBoundRect.Right()); + } + else + rVal.nWidth = aBoundRect.Right() - rVal.nHPos; + } + + const SwTwips nH = ( rVal.nHoriOrient != text::HoriOrientation::NONE ) + ? aBoundRect.Left() + : rVal.nHPos; + rVal.nMaxWidth = rVal.nMaxHPos + rVal.nWidth - nH; + + // determine vertical values + rVal.nMinVPos = -( aBoundRect.Bottom() - rVal.nHeight ); + if ( rVal.nVPos < rVal.nMinVPos && + rVal.nVertOrient == text::VertOrientation::NONE ) + { + rVal.nVPos = rVal.nMinVPos; + } + + rVal.nMaxVPos = -aBoundRect.Top(); + if ( rVal.nVPos > rVal.nMaxVPos && + rVal.nVertOrient == text::VertOrientation::NONE ) + { + rVal.nVPos = rVal.nMaxVPos; + } + + if ( rVal.nVertOrient == text::VertOrientation::NONE ) + { + rVal.nMaxHeight = aBoundRect.Bottom() + rVal.nVPos; + } + else + { + rVal.nMaxHeight = aBoundRect.Height(); + } + } + else if ( eAnchorType == RndStdIds::FLY_AS_CHAR ) + { + rVal.nMinHPos = 0; + rVal.nMaxHPos = 0; + + rVal.nMaxHeight = aBoundRect.Height(); + rVal.nMaxWidth = aBoundRect.Width(); + + rVal.nMaxVPos = aBoundRect.Height(); + rVal.nMinVPos = -aBoundRect.Height() + rVal.nHeight; + if (rVal.nMaxVPos < rVal.nMinVPos) + { + rVal.nMinVPos = rVal.nMaxVPos; + rVal.nMaxVPos = -aBoundRect.Height(); + } + } + // #mongolianlayout# + if ( m_bIsInVertical || m_bIsInVerticalL2R ) + { + //restore width/height exchange + long nTmp = rVal.nWidth; + rVal.nWidth = rVal.nHeight; + rVal.nHeight = nTmp; + } + + if (rVal.nMaxWidth < rVal.nWidth) + rVal.nWidth = rVal.nMaxWidth; + if (rVal.nMaxHeight < rVal.nHeight) + rVal.nHeight = rVal.nMaxHeight; +} + +// correction for border +SwTwips SwFlyFrameAttrMgr::CalcTopSpace() +{ + const SvxShadowItem& rShadow = GetShadow(); + const SvxBoxItem& rBox = GetBox(); + return rShadow.CalcShadowSpace(SvxShadowItemSide::TOP ) + rBox.CalcLineSpace(SvxBoxItemLine::TOP); +} + +SwTwips SwFlyFrameAttrMgr::CalcBottomSpace() +{ + const SvxShadowItem& rShadow = GetShadow(); + const SvxBoxItem& rBox = GetBox(); + return rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM); +} + +SwTwips SwFlyFrameAttrMgr::CalcLeftSpace() +{ + const SvxShadowItem& rShadow = GetShadow(); + const SvxBoxItem& rBox = GetBox(); + return rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::LEFT); +} + +SwTwips SwFlyFrameAttrMgr::CalcRightSpace() +{ + const SvxShadowItem& rShadow = GetShadow(); + const SvxBoxItem& rBox = GetBox(); + return rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT); +} + +// erase attribute from the set +void SwFlyFrameAttrMgr::DelAttr( sal_uInt16 nId ) +{ + m_aSet.ClearItem( nId ); +} + +void SwFlyFrameAttrMgr::SetLRSpace( long nLeft, long nRight ) +{ + OSL_ENSURE( LONG_MAX != nLeft && LONG_MAX != nRight, "Which border to set?" ); + + SvxLRSpaceItem aTmp( m_aSet.Get( RES_LR_SPACE ) ); + if( LONG_MAX != nLeft ) + aTmp.SetLeft( sal_uInt16(nLeft) ); + if( LONG_MAX != nRight ) + aTmp.SetRight( sal_uInt16(nRight) ); + m_aSet.Put( aTmp ); +} + +void SwFlyFrameAttrMgr::SetULSpace( long nTop, long nBottom ) +{ + OSL_ENSURE(LONG_MAX != nTop && LONG_MAX != nBottom, "Which border to set?" ); + + SvxULSpaceItem aTmp( m_aSet.Get( RES_UL_SPACE ) ); + if( LONG_MAX != nTop ) + aTmp.SetUpper( sal_uInt16(nTop) ); + if( LONG_MAX != nBottom ) + aTmp.SetLower( sal_uInt16(nBottom) ); + m_aSet.Put( aTmp ); +} + +void SwFlyFrameAttrMgr::SetPos( const Point& rPoint ) +{ + SwFormatVertOrient aVertOrient( GetVertOrient() ); + SwFormatHoriOrient aHoriOrient( GetHoriOrient() ); + + aHoriOrient.SetPos ( rPoint.X() ); + aHoriOrient.SetHoriOrient( text::HoriOrientation::NONE ); + + aVertOrient.SetPos ( rPoint.Y() ); + aVertOrient.SetVertOrient( text::VertOrientation::NONE ); + + m_aSet.Put( aVertOrient ); + m_aSet.Put( aHoriOrient ); +} + +void SwFlyFrameAttrMgr::SetHorzOrientation( sal_Int16 eOrient ) +{ + SwFormatHoriOrient aHoriOrient( GetHoriOrient() ); + aHoriOrient.SetHoriOrient( eOrient ); + m_aSet.Put( aHoriOrient ); +} + +void SwFlyFrameAttrMgr::SetVertOrientation( sal_Int16 eOrient ) +{ + SwFormatVertOrient aVertOrient( GetVertOrient() ); + aVertOrient.SetVertOrient( eOrient ); + m_aSet.Put( aVertOrient ); +} + +void SwFlyFrameAttrMgr::SetHeightSizeType( SwFrameSize eType ) +{ + SwFormatFrameSize aSize( GetFrameSize() ); + aSize.SetHeightSizeType( eType ); + m_aSet.Put( aSize ); +} + +void SwFlyFrameAttrMgr::SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize) +{ + // RotGrfFlyFrame: Central handling of real change of rotation here, all adaptations use this. + // Adaptation of pos/size may be wanted in the future. Already tried to keep last Size in + // UnrotatedSize in the SwRotationGrf Item, but this will lead to various problems. Also tried + // to use m_aSet.Put(...) as in other methods (also tried read methods for Rotation/UnrotatedSize) but + // somehow the needed ID (RES_GRFATR_ROTATION) is *not* in the SfxItemSet of the Frame, so for + // now set directly. Undo/Redo is preserved by AttributeChange + if(nOld != nNew) + { + m_pOwnSh->SetAttrItem(SwRotationGrf(nNew, rUnrotatedSize)); + } +} + +void SwFlyFrameAttrMgr::SetSize( const Size& rSize ) +{ + SwFormatFrameSize aSize( GetFrameSize() ); + aSize.SetSize(Size(std::max(rSize.Width(), long(MINFLY)), std::max(rSize.Height(), long(MINFLY)))); + m_aSet.Put( aSize ); +} + +void SwFlyFrameAttrMgr::SetAttrSet(const SfxItemSet& rSet) +{ + m_aSet.ClearItem(); + m_aSet.Put( rSet ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/globdoc/globdoc.cxx b/sw/source/uibase/globdoc/globdoc.cxx new file mode 100644 index 000000000..283b5a016 --- /dev/null +++ b/sw/source/uibase/globdoc/globdoc.cxx @@ -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 . + */ + +#include <comphelper/fileformat.h> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> +#include <tools/globname.hxx> + +#include <swtypes.hxx> +#include <globdoc.hxx> +#include <strings.hrc> + +// Description: Register all filters + + +SFX_IMPL_OBJECTFACTORY( SwGlobalDocShell, SvGlobalName(SO3_SWGLOB_CLASSID), "swriter/GlobalDocument" ) + +SwGlobalDocShell::SwGlobalDocShell(SfxObjectCreateMode eMode ) : + SwDocShell(eMode) +{ +} + +SwGlobalDocShell::~SwGlobalDocShell() +{ +} + +void SwGlobalDocShell::FillClass( SvGlobalName * pClassName, + SotClipboardFormatId * pClipFormat, + OUString * pLongUserName, + sal_Int32 nVersion, + bool bTemplate /* = false */) const +{ + if (nVersion == SOFFICE_FILEFORMAT_60) + { + *pClassName = SvGlobalName( SO3_SWGLOB_CLASSID_60 ); + *pClipFormat = SotClipboardFormatId::STARWRITERGLOB_60; + *pLongUserName = SwResId(STR_WRITER_GLOBALDOC_FULLTYPE); + OSL_ENSURE( !bTemplate, "No template for Writer Global" ); + } + else if (nVersion == SOFFICE_FILEFORMAT_8) + { + *pClassName = SvGlobalName( SO3_SWGLOB_CLASSID_60 ); + *pClipFormat = bTemplate ? SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE : SotClipboardFormatId::STARWRITERGLOB_8; + *pLongUserName = SwResId(STR_WRITER_GLOBALDOC_FULLTYPE); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/DashedLine.hxx b/sw/source/uibase/inc/DashedLine.hxx new file mode 100644 index 000000000..0492be67e --- /dev/null +++ b/sw/source/uibase/inc/DashedLine.hxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_UIBASE_INC_DASHEDLINE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DASHEDLINE_HXX + +#include <vcl/fixed.hxx> + +/** Class for displaying a dashed line in the Writer GUI. + */ +class SwDashedLine : public FixedLine +{ + Color& (*m_pColorFn)(); + +public: + SwDashedLine( vcl::Window* pParent, Color& ( *pColorFn )() ); + virtual ~SwDashedLine( ) override; + + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/DateFormFieldDialog.hxx b/sw/source/uibase/inc/DateFormFieldDialog.hxx new file mode 100644 index 000000000..11ae445fe --- /dev/null +++ b/sw/source/uibase/inc/DateFormFieldDialog.hxx @@ -0,0 +1,58 @@ +/* -*- 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_UIBASE_INC_DATEFORMFIELDDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DATEFORMFIELDDIALOG_HXX + +#include <vcl/weld.hxx> +#include "numfmtlb.hxx" + +class SvNumberFormatter; +class SwDoc; + +namespace sw +{ +namespace mark +{ +class IDateFieldmark; +} +} // namespace sw + +/// Dialog to specify the properties of date form field +namespace sw +{ +class DateFormFieldDialog : public weld::GenericDialogController +{ +private: + sw::mark::IDateFieldmark* m_pDateField; + SvNumberFormatter* m_pNumberFormatter; + + std::unique_ptr<SwNumFormatTreeView> m_xFormatLB; + + void Apply(); + void InitControls(); + +public: + DateFormFieldDialog(weld::Widget* pParent, sw::mark::IDateFieldmark* pDateField, SwDoc* pDoc); + virtual ~DateFormFieldDialog() override; + + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } +}; + +} // namespace sw + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/inc/DropDownFieldDialog.hxx b/sw/source/uibase/inc/DropDownFieldDialog.hxx new file mode 100644 index 000000000..59510d37b --- /dev/null +++ b/sw/source/uibase/inc/DropDownFieldDialog.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_DROPDOWNFIELDDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DROPDOWNFIELDDIALOG_HXX + +#include <vcl/weld.hxx> + +class SwDropDownField; +class SwField; +class SwWrtShell; + +// Dialog to edit drop down field selection +namespace sw +{ +class DropDownFieldDialog : public weld::GenericDialogController +{ + SwWrtShell &m_rSh; + SwDropDownField* m_pDropField; + + weld::Button* m_pPressedButton; + std::unique_ptr<weld::TreeView> m_xListItemsLB; + std::unique_ptr<weld::Button> m_xOKPB; + std::unique_ptr<weld::Button> m_xPrevPB; + std::unique_ptr<weld::Button> m_xNextPB; + std::unique_ptr<weld::Button> m_xEditPB; + + DECL_LINK(EditHdl, weld::Button&, void); + DECL_LINK(PrevHdl, weld::Button&, void); + DECL_LINK(NextHdl, weld::Button&, void); + void Apply(); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); +public: + DropDownFieldDialog(weld::Widget *pParent, SwWrtShell &rSh, + SwField* pField, bool bPrevButton, bool bNextButton); + virtual ~DropDownFieldDialog() override; + bool PrevButtonPressed() const; + bool NextButtonPressed() const; + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } +}; +} //namespace sw + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/DropDownFormFieldDialog.hxx b/sw/source/uibase/inc/DropDownFormFieldDialog.hxx new file mode 100644 index 000000000..ae1e1b51d --- /dev/null +++ b/sw/source/uibase/inc/DropDownFormFieldDialog.hxx @@ -0,0 +1,68 @@ +/* -*- 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_UIBASE_INC_DROPDOWNFORMFIELDDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DROPDOWNFORMFIELDDIALOG_HXX + +#include <vcl/weld.hxx> + +namespace sw +{ +namespace mark +{ +class IFieldmark; +} +} // namespace sw + +/// Dialog to specify the properties of drop-down form field +namespace sw +{ +class DropDownFormFieldDialog : public weld::GenericDialogController +{ +private: + mark::IFieldmark* m_pDropDownField; + bool m_bListHasChanged; + + std::unique_ptr<weld::Entry> m_xListItemEntry; + std::unique_ptr<weld::Button> m_xListAddButton; + + std::unique_ptr<weld::TreeView> m_xListItemsTreeView; + + std::unique_ptr<weld::Button> m_xListRemoveButton; + std::unique_ptr<weld::Button> m_xListUpButton; + std::unique_ptr<weld::Button> m_xListDownButton; + + DECL_LINK(ListChangedHdl, weld::TreeView&, void); + DECL_LINK(KeyPressedHdl, const KeyEvent&, bool); + DECL_LINK(EntryChangedHdl, weld::Entry&, void); + DECL_LINK(ButtonPushedHdl, weld::Button&, void); + + void InitControls(); + void AppendItemToList(); + void UpdateButtons(); + void Apply(); + +public: + DropDownFormFieldDialog(weld::Widget* pParent, mark::IFieldmark* pDropDownField); + virtual ~DropDownFormFieldDialog() override; + + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } +}; + +} // namespace sw + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/inc/FrameControl.hxx b/sw/source/uibase/inc/FrameControl.hxx new file mode 100644 index 000000000..bf2ff6239 --- /dev/null +++ b/sw/source/uibase/inc/FrameControl.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/. + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_FRAMECONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRAMECONTROL_HXX + +#include <vcl/menubtn.hxx> + +#include "edtwin.hxx" + +class SwEditWin; +class SwPageFrame; +class SwFrame; +class Point; + +/// Abstract interface to be implemented by writer FrameControls +class ISwFrameControl +{ +public: + virtual ~ISwFrameControl(); + virtual void SetReadonly( bool bReadonly ) = 0; + virtual void ShowAll( bool bShow ) = 0; + + /// Returns true if the point is inside the control. + virtual bool Contains( const Point &rDocPt ) const = 0; + + virtual const SwFrame* GetFrame() = 0; + virtual SwEditWin* GetEditWin() = 0; +}; + +class SwFrameControl final +{ + VclPtr<vcl::Window> mxWindow; + ISwFrameControl *mpIFace; +public: + SwFrameControl( const VclPtr<vcl::Window> &pWindow ); + ~SwFrameControl(); + + vcl::Window* GetWindow() { return mxWindow.get(); } + + void SetReadonly( bool bReadonly ) { mpIFace->SetReadonly( bReadonly ); } + void ShowAll( bool bShow ) { mpIFace->ShowAll( bShow ); } + bool Contains( const Point &rDocPt ) const { return mpIFace->Contains( rDocPt ); } +}; + +/** Class sharing some MenuButton code + */ +class SwFrameMenuButtonBase : public MenuButton, public ISwFrameControl +{ + VclPtr<SwEditWin> m_pEditWin; + const SwFrame* m_pFrame; + +protected: + virtual ~SwFrameMenuButtonBase() override { disposeOnce(); } + virtual void dispose() override; + +public: + SwFrameMenuButtonBase( SwEditWin* pEditWin, const SwFrame* pFrame ); + + virtual const SwFrame* GetFrame() override { return m_pFrame; } + virtual SwEditWin* GetEditWin() override { return m_pEditWin; } + const SwPageFrame* GetPageFrame() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/FrameControlsManager.hxx b/sw/source/uibase/inc/FrameControlsManager.hxx new file mode 100644 index 000000000..66685bd8b --- /dev/null +++ b/sw/source/uibase/inc/FrameControlsManager.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/. + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_FRAMECONTROLSMANAGER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRAMECONTROLSMANAGER_HXX + +#include "FrameControl.hxx" + +#include <tools/gen.hxx> + +#include <map> +#include <memory> + +class SwPageFrame; +class SwEditWin; + +typedef std::shared_ptr< SwFrameControl > SwFrameControlPtr; + +typedef std::map<const SwFrame*, SwFrameControlPtr> SwFrameControlPtrMap; + +/** A container for the Header/Footer, or PageBreak controls. +*/ +class SwFrameControlsManager +{ + private: + VclPtr<SwEditWin> m_pEditWin; + std::map< FrameControlType, SwFrameControlPtrMap > m_aControls; + + public: + SwFrameControlsManager( SwEditWin* pEditWin ); + ~SwFrameControlsManager(); + void dispose(); + + SwFrameControlPtr GetControl( FrameControlType eType, const SwFrame* pFrame ); + void RemoveControls( const SwFrame* pFrame ); + void RemoveControlsByType( FrameControlType eType, const SwFrame* pFrame ); + void HideControls( FrameControlType eType ); + void SetReadonlyControls( bool bReadonly ); + + // Helper methods + void SetHeaderFooterControl( const SwPageFrame* pPageFrame, FrameControlType eType, Point aOffset ); + void SetPageBreakControl( const SwPageFrame* pPageFrame ); + void SetUnfloatTableButton( const SwFlyFrame* pFlyFrame, bool bShow, Point aTopRightPixel = Point() ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/HeaderFooterWin.hxx b/sw/source/uibase/inc/HeaderFooterWin.hxx new file mode 100644 index 000000000..dc1cee8cc --- /dev/null +++ b/sw/source/uibase/inc/HeaderFooterWin.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/. + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_HEADERFOOTERWIN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_HEADERFOOTERWIN_HXX + +#include "edtwin.hxx" +#include "FrameControl.hxx" +#include <vcl/builder.hxx> +#include <vcl/timer.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> + +/** + * Button painter helper class used to paint a runtime button positioned to a writer frame. + * See header/footer button. + */ +class SwFrameButtonPainter +{ +public: + + static void PaintButton(drawinglayer::primitive2d::Primitive2DContainer& rSeq, + const tools::Rectangle& rRect, bool bOnTop); +}; + +/** Class for the header and footer separator control window. + + This control is showing the header / footer style name and provides + a few useful actions to the user. + */ +class SwHeaderFooterWin : public SwFrameMenuButtonBase +{ + VclBuilder m_aBuilder; + OUString m_sLabel; + bool m_bIsHeader; + VclPtr<PopupMenu> m_pPopupMenu; + VclPtr<vcl::Window> m_pLine; + bool m_bIsAppearing; + int m_nFadeRate; + Timer m_aFadeTimer; + +public: + SwHeaderFooterWin( SwEditWin *pEditWin, const SwFrame *pFrame, bool bHeader ); + virtual ~SwHeaderFooterWin( ) override; + virtual void dispose() override; + + void SetOffset( Point aOffset, long nXLineStart, long nXLineEnd ); + + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void Select( ) override; + + virtual void ShowAll( bool bShow ) override; + virtual bool Contains( const Point &rDocPt ) const override; + + bool IsHeader() const { return m_bIsHeader; }; + bool IsEmptyHeaderFooter( ) const; + + void ExecuteCommand(const OString &rIdent); + + void SetReadonly( bool bReadonly ) override; + +private: + DECL_LINK( FadeHandler, Timer *, void ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/PageBreakWin.hxx b/sw/source/uibase/inc/PageBreakWin.hxx new file mode 100644 index 000000000..982c81e15 --- /dev/null +++ b/sw/source/uibase/inc/PageBreakWin.hxx @@ -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/. + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_PAGEBREAKWIN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PAGEBREAKWIN_HXX + +#include "edtwin.hxx" +#include "FrameControl.hxx" +#include <vcl/builder.hxx> +#include <vcl/timer.hxx> +#include <optional> + +class Menu; +class SwPageFrame; + +/** Class for the page break control window. + + This control shows a line indicating a manual page break and a + button providing a few actions on that page break. + */ +class SwPageBreakWin : public SwFrameMenuButtonBase +{ + VclBuilder m_aBuilder; + VclPtr<PopupMenu> m_pPopupMenu; + VclPtr<vcl::Window> m_pLine; + bool m_bIsAppearing; + int m_nFadeRate; + int m_nDelayAppearing; ///< Before we show the control, let it transparent for a few timer ticks to avoid appearing with every mouse over. + Timer m_aFadeTimer; + bool m_bDestroyed; + + std::optional<Point> m_xMousePt; + +public: + SwPageBreakWin( SwEditWin* pEditWin, const SwFrame *pFrame ); + virtual ~SwPageBreakWin() override; + virtual void dispose() override; + + virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) override; + virtual void Select( ) override; + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void Activate( ) override; + + void UpdatePosition(const std::optional<Point>& xEvtPt = std::optional<Point>()); + + virtual void ShowAll( bool bShow ) override; + virtual bool Contains( const Point &rDocPt ) const override; + + void SetReadonly( bool bReadonly ) override; + + void Fade( bool bFadeIn ); + +private: + /// Hide the button (used when the popup menu is closed by clicking outside) + DECL_LINK( HideHandler, Menu *, bool ); + DECL_LINK( FadeHandler, Timer *, void ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/SidebarWindowsConsts.hxx b/sw/source/uibase/inc/SidebarWindowsConsts.hxx new file mode 100644 index 000000000..a6becb92b --- /dev/null +++ b/sw/source/uibase/inc/SidebarWindowsConsts.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SIDEBARWINDOWSCONSTS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SIDEBARWINDOWSCONSTS_HXX + +#include <sal/types.h> + +namespace sw::sidebarwindows { + +const sal_Int8 ANCHORLINE_WIDTH = 1; + +} // end of namespace sw::sidebarwindows + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/SwSpellDialogChildWindow.hxx b/sw/source/uibase/inc/SwSpellDialogChildWindow.hxx new file mode 100644 index 000000000..82cd29204 --- /dev/null +++ b/sw/source/uibase/inc/SwSpellDialogChildWindow.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_SWSPELLDIALOGCHILDWINDOW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWSPELLDIALOGCHILDWINDOW_HXX + +#include <svx/SpellDialogChildWindow.hxx> +#include <view.hxx> + +class SwWrtShell; +struct SpellState; +class SwSpellDialogChildWindow + : public svx::SpellDialogChildWindow +{ + bool m_bIsGrammarCheckingOn; + std::unique_ptr<SpellState> m_pSpellState; + + SwWrtShell* GetWrtShell_Impl(); + void MakeTextSelection_Impl(SwWrtShell& rSh, ShellMode eSelMode); + bool FindNextDrawTextError_Impl(SwWrtShell& rSh); + bool SpellDrawText_Impl(SwWrtShell& rSh, svx::SpellPortions& rPortions); + void LockFocusNotification(bool bLock); + +protected: + virtual svx::SpellPortions GetNextWrongSentence(bool bRecheck) override; + virtual void ApplyChangedSentence(const svx::SpellPortions& rChanged, bool bRecheck) override; + virtual void AddAutoCorrection(const OUString& rOld, const OUString& rNew, LanguageType eLanguage) override; + virtual bool HasAutoCorrection() override; + virtual bool HasGrammarChecking() override; + virtual bool IsGrammarChecking() override; + virtual void SetGrammarChecking(bool bOn) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + +public: + SwSpellDialogChildWindow ( + vcl::Window*pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + virtual ~SwSpellDialogChildWindow() override; + + SFX_DECL_CHILDWINDOW_WITHID(SwSpellDialogChildWindow); + + void InvalidateSpellDialog(); + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/SwXFilterOptions.hxx b/sw/source/uibase/inc/SwXFilterOptions.hxx new file mode 100644 index 000000000..e9e036b5f --- /dev/null +++ b/sw/source/uibase/inc/SwXFilterOptions.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_UIBASE_INC_SWXFILTEROPTIONS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWXFILTEROPTIONS_HXX + +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/document/XImporter.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 com::sun::star::io { class XInputStream; } + +class SwXFilterOptions : public ::cppu::WeakImplHelper< + css::beans::XPropertyAccess, + css::ui::dialogs::XExecutableDialog, + css::document::XImporter, + css::document::XExporter, + css::lang::XInitialization, + css::lang::XServiceInfo > +{ + OUString sFilterOptions; + + css::uno::Reference< css::io::XInputStream > xInputStream; + css::uno::Reference< css::lang::XComponent > xModel; + css::uno::Reference< css::awt::XWindow > xDialogParent; + +public: + SwXFilterOptions(); + virtual ~SwXFilterOptions() override; + + // XPropertyAccess + virtual css::uno::Sequence< css::beans::PropertyValue > + SAL_CALL getPropertyValues() override; + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< + css::beans::PropertyValue >& aProps ) override; + + // XExecutableDialog + virtual void SAL_CALL setTitle( const OUString& aTitle ) override; + virtual sal_Int16 SAL_CALL execute() override; + + // XImporter + virtual void SAL_CALL setTargetDocument( const css::uno::Reference< + css::lang::XComponent >& xDoc ) 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>& rArguments) 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/uibase/inc/UnfloatTableButton.hxx b/sw/source/uibase/inc/UnfloatTableButton.hxx new file mode 100644 index 000000000..701c824b8 --- /dev/null +++ b/sw/source/uibase/inc/UnfloatTableButton.hxx @@ -0,0 +1,46 @@ +/* -*- 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_UIBASE_DOCVW_UNFLOATTABLEBUTTON_HXX +#define INCLUDED_SW_SOURCE_UIBASE_DOCVW_UNFLOATTABLEBUTTON_HXX + +#include "edtwin.hxx" +#include "FrameControl.hxx" + +/** Class for unfloat table button + * + * This unfloat button is used to convert a floating table into a simple writer table embedded to the text body. + * This unfloat operation is useful typically for documents imported from MSO file formats containing + * multi-page floating tables. In case of a multi-page table the text frame cuts off the table because + * the frame can't span across multiple pages. With unfloating we can get a multi-page table without + * floating properties. + * + */ +class UnfloatTableButton : public SwFrameMenuButtonBase +{ + OUString m_sLabel; + +public: + UnfloatTableButton(SwEditWin* pEditWin, const SwFrame* pFrame); + virtual ~UnfloatTableButton() override; + + void SetOffset(Point aTopRightPixel); + + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + + virtual void ShowAll(bool bShow) override; + virtual bool Contains(const Point& rDocPt) const override; + + virtual void SetReadonly(bool bReadonly) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/inc/abstract.hxx b/sw/source/uibase/inc/abstract.hxx new file mode 100644 index 000000000..6d6f6ba60 --- /dev/null +++ b/sw/source/uibase/inc/abstract.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_ABSTRACT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ABSTRACT_HXX + +#include <sfx2/basedlgs.hxx> + +class SwInsertAbstractDlg : public SfxDialogController +{ + std::unique_ptr<weld::SpinButton> m_xLevelNF; + std::unique_ptr<weld::SpinButton> m_xParaNF; + +public: + SwInsertAbstractDlg(weld::Window* pParent); + virtual ~SwInsertAbstractDlg() override; + + sal_uInt8 GetLevel() const; + sal_uInt8 GetPara() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/addrdlg.hxx b/sw/source/uibase/inc/addrdlg.hxx new file mode 100644 index 000000000..feb839afd --- /dev/null +++ b/sw/source/uibase/inc/addrdlg.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_UIBASE_INC_ADDRDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ADDRDLG_HXX + +#include <sfx2/basedlgs.hxx> + +class SwAddrDlg : public SfxSingleTabDialogController +{ +public: + SwAddrDlg(weld::Window* pParent, const SfxItemSet& rSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/annotsh.hxx b/sw/source/uibase/inc/annotsh.hxx new file mode 100644 index 000000000..1dbf4208e --- /dev/null +++ b/sw/source/uibase/inc/annotsh.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_ANNOTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ANNOTSH_HXX + +#include <sfx2/shell.hxx> +#include <shellid.hxx> +#include <swmodule.hxx> +#include <unotools/caserotate.hxx> + +class SwView; +class SwAnnotationShell: public SfxShell +{ + SwView& rView; + RotateTransliteration m_aRotateCase; + +public: + SFX_DECL_INTERFACE(SW_ANNOTATIONSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwAnnotationShell(SwView&); + virtual ~SwAnnotationShell() override; + + static void StateDisableItems(SfxItemSet &); + void Exec(SfxRequest &); + + void GetState(SfxItemSet &); + void StateInsert(SfxItemSet &rSet); + + void NoteExec(SfxRequest const &); + void GetNoteState(SfxItemSet &); + + void ExecLingu(SfxRequest &rReq); + void GetLinguState(SfxItemSet &); + + void ExecClpbrd(SfxRequest const &rReq); + void StateClpbrd(SfxItemSet &rSet); + + void ExecTransliteration(SfxRequest const &); + void ExecRotateTransliteration(SfxRequest const &); + + void ExecUndo(SfxRequest &rReq); + void StateUndo(SfxItemSet &rSet); + + static void StateStatusLine(SfxItemSet &rSet); + + void InsertSymbol(SfxRequest& rReq); + + void ExecSearch(SfxRequest&); + void StateSearch(SfxItemSet &); + + virtual SfxUndoManager* + GetUndoManager() override; + + static SfxItemPool* GetAnnotationPool(SwView const & rV); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/ascfldlg.hxx b/sw/source/uibase/inc/ascfldlg.hxx new file mode 100644 index 000000000..330159551 --- /dev/null +++ b/sw/source/uibase/inc/ascfldlg.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_UIBASE_INC_ASCFLDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ASCFLDLG_HXX + +#include <sfx2/basedlgs.hxx> +#include <svx/txencbox.hxx> +#include <svx/langbox.hxx> +#include <tools/lineend.hxx> + +class SwAsciiOptions; +class SvStream; +class SwDocShell; + +class SwAsciiFilterDlg : public SfxDialogController +{ + bool m_bSaveLineStatus; + OUString m_sExtraData; + + std::unique_ptr<SvxTextEncodingBox> m_xCharSetLB; + std::unique_ptr<weld::Label> m_xFontFT; + std::unique_ptr<weld::ComboBox> m_xFontLB; + std::unique_ptr<weld::Label> m_xLanguageFT; + std::unique_ptr<SvxLanguageBox> m_xLanguageLB; + std::unique_ptr<weld::RadioButton> m_xCRLF_RB; + std::unique_ptr<weld::RadioButton> m_xCR_RB; + std::unique_ptr<weld::RadioButton> m_xLF_RB; + std::unique_ptr<weld::CheckButton> m_xIncludeBOM_CB; + + DECL_LINK(CharSetSelHdl, weld::ComboBox&, void); + DECL_LINK(LineEndHdl, weld::ToggleButton&, void); + void SetCRLF( LineEnd eEnd ); + LineEnd GetCRLF() const; + void SetIncludeBOM( bool bIncludeBOM ); + bool GetIncludeBOM() const; + void UpdateIncludeBOMSensitiveState(); + +public: + // CTOR: for import - pStream is the inputstream + // for export - pStream must be 0 + SwAsciiFilterDlg(weld::Window* pParent, SwDocShell& rDocSh, SvStream* pStream); + virtual ~SwAsciiFilterDlg() override; + + void FillOptions( SwAsciiOptions& rOptions ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/autoformatpreview.hxx b/sw/source/uibase/inc/autoformatpreview.hxx new file mode 100644 index 000000000..a5f41e4d9 --- /dev/null +++ b/sw/source/uibase/inc/autoformatpreview.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_AUTOFORMATPREVIEW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_AUTOFORMATPREVIEW_HXX + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <sal/types.h> +#include <svx/framelinkarray.hxx> +#include <svl/zforlist.hxx> +#include <tools/gen.hxx> +#include <rtl/ustring.hxx> +#include <vcl/customweld.hxx> +#include <vcl/font.hxx> + +#include "wrtsh.hxx" +#include <tblafmt.hxx> + +class AutoFormatPreview : public weld::CustomWidgetController +{ +public: + AutoFormatPreview(); + + void NotifyChange(const SwTableAutoFormat& rNewData); + + void DetectRTL(SwWrtShell const* pWrtShell); + +private: + SwTableAutoFormat maCurrentData; + svx::frame::Array maArray; /// Implementation to draw the frame borders. + bool mbFitWidth; + bool mbRTL; + Size maPreviousSize; + long mnLabelColumnWidth; + long mnDataColumnWidth1; + long mnDataColumnWidth2; + long mnRowHeight; + const OUString maStringJan; + const OUString maStringFeb; + const OUString maStringMar; + const OUString maStringNorth; + const OUString maStringMid; + const OUString maStringSouth; + const OUString maStringSum; + std::unique_ptr<SvNumberFormatter> mxNumFormat; + + uno::Reference<i18n::XBreakIterator> m_xBreak; + + void Init(); + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void Resize() override; + void CalcCellArray(bool bFitWidth); + void CalcLineMap(); + void PaintCells(vcl::RenderContext& rRenderContext); + + sal_uInt8 GetFormatIndex(size_t nCol, size_t nRow) const; + + void DrawString(vcl::RenderContext& rRenderContext, size_t nCol, size_t nRow); + void DrawBackground(vcl::RenderContext& rRenderContext); + + void MakeFonts(vcl::RenderContext const& rRenderContext, sal_uInt8 nIndex, vcl::Font& rFont, + vcl::Font& rCJKFont, vcl::Font& rCTLFont); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/barcfg.hxx b/sw/source/uibase/inc/barcfg.hxx new file mode 100644 index 000000000..f40f2c7d1 --- /dev/null +++ b/sw/source/uibase/inc/barcfg.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_UIBASE_INC_BARCFG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BARCFG_HXX + +#include <sfx2/toolbarids.hxx> +#include <unotools/configitem.hxx> + +enum class SelectionType : sal_Int32; + +class SwToolbarConfigItem : public utl::ConfigItem +{ + sal_Int32 aTbxIdArray[5]; + + static css::uno::Sequence<OUString> GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwToolbarConfigItem( bool bWeb ); + virtual ~SwToolbarConfigItem() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + + void SetTopToolbar(SelectionType nSelType, ToolbarId eBarId); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/basesh.hxx b/sw/source/uibase/inc/basesh.hxx new file mode 100644 index 000000000..b4426137f --- /dev/null +++ b/sw/source/uibase/inc/basesh.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_BASESH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BASESH_HXX + +#include <shellid.hxx> + +#include <tools/link.hxx> +#include <sfx2/module.hxx> +#include <sfx2/shell.hxx> + +#include <mdiexp.hxx> +#include <set> + +class SwWrtShell; +class SwView; +class SfxItemSet; +class SwCursorShell; + +struct DBTextStruct_Impl; +class SW_DLLPUBLIC SwBaseShell: public SfxShell +{ + SwView &rView; + + // DragMode + static FlyMode eFrameMode; + + // Bug 75078 - if in GetState the async call of GetGraphic returns + // synch, the set the state directly into the itemset + SfxItemSet* pGetStateSet; + + // Update-Timer for graphic + std::set<sal_uInt16> aGrfUpdateSlots; + + DECL_LINK( GraphicArrivedHdl, SwCursorShell&, void ); + +protected: + SwWrtShell& GetShell(); + SwWrtShell* GetShellPtr(); + + SwView& GetView() { return rView; } + void SetGetStateSet( SfxItemSet* p ) { pGetStateSet = p; } + bool AddGrfUpdateSlot( sal_uInt16 nSlot ){ return aGrfUpdateSlots.insert( nSlot ).second; } + + DECL_LINK( InsertDBTextHdl, void*, void ); + + void InsertURLButton( const OUString& rURL, const OUString& rTarget, const OUString& rText ); + void InsertTable( SfxRequest& _rRequest ); + +public: + SwBaseShell(SwView &rShell); + virtual ~SwBaseShell() override; + + SFX_DECL_INTERFACE(SW_BASESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void ExecDelete(SfxRequest &); + + void ExecClpbrd(SfxRequest &); + void StateClpbrd(SfxItemSet &); + + void ExecUndo(SfxRequest &); + void StateUndo(SfxItemSet &); + + void Execute(SfxRequest &); + void GetState(SfxItemSet &); + void StateStyle(SfxItemSet &); + + void ExecuteGallery(SfxRequest&); + void GetGalleryState(SfxItemSet&); + + void ExecDlg(SfxRequest &); + + void ExecTextCtrl(SfxRequest& rReq); + void GetTextFontCtrlState(SfxItemSet& rSet); + void GetTextCtrlState(SfxItemSet& rSet); + void GetBorderState(SfxItemSet &rSet); + void GetBckColState(SfxItemSet &rSet); + + void ExecBckCol(SfxRequest& rReq); + void SetWrapMode( sal_uInt16 nSlot ); + + static void StateDisableItems(SfxItemSet &); + + void EditRegionDialog(SfxRequest const & rReq); + void InsertRegionDialog(SfxRequest& rReq); + + void ExecField(SfxRequest const & rReq); + + static void SetFrameMode( FlyMode eMode, SwWrtShell *pShell ); // with update! + static void SetFrameMode_( FlyMode eMode ) { eFrameMode = eMode; } + static FlyMode GetFrameMode() { return eFrameMode; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/beziersh.hxx b/sw/source/uibase/inc/beziersh.hxx new file mode 100644 index 000000000..abf64aa65 --- /dev/null +++ b/sw/source/uibase/inc/beziersh.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_UIBASE_INC_BEZIERSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BEZIERSH_HXX + +#include "basesh.hxx" + +class SwBezierShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_BEZIERSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwBezierShell(SwView &rView); + + void GetState(SfxItemSet &); + void Execute(SfxRequest const &); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/bmpwin.hxx b/sw/source/uibase/inc/bmpwin.hxx new file mode 100644 index 000000000..46d9544b7 --- /dev/null +++ b/sw/source/uibase/inc/bmpwin.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_UIBASE_INC_BMPWIN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BMPWIN_HXX + +#include <vcl/customweld.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/graph.hxx> + +// extended page for graphics +class BmpWindow : public weld::CustomWidgetController +{ +private: + Graphic aGraphic; + BitmapEx aBmp; + + bool bHorz : 1; + bool bVert : 1; + bool bGraphic : 1; + + virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect) override; + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + +public: + BmpWindow(); + virtual ~BmpWindow() override; + void MirrorVert(bool bMirror) { bVert = bMirror; Invalidate(); } + void MirrorHorz(bool bMirror) { bHorz = bMirror; Invalidate(); } + void SetGraphic(const Graphic& rGrf); + void SetBitmapEx(const BitmapEx& rGrf); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/bookctrl.hxx b/sw/source/uibase/inc/bookctrl.hxx new file mode 100644 index 000000000..a69f020d6 --- /dev/null +++ b/sw/source/uibase/inc/bookctrl.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_BOOKCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BOOKCTRL_HXX + +#include <rtl/ustring.hxx> +#include <sfx2/stbitem.hxx> + +class SwBookmarkControl : public SfxStatusBarControl +{ + virtual void Command( const CommandEvent& rCEvt ) override; + +public: + virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + + SFX_DECL_STATUSBAR_CONTROL(); + + SwBookmarkControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SwBookmarkControl() override; + +private: + OUString sPageNumber; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/bookmark.hxx b/sw/source/uibase/inc/bookmark.hxx new file mode 100644 index 000000000..3a8ee1506 --- /dev/null +++ b/sw/source/uibase/inc/bookmark.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_BOOKMARK_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BOOKMARK_HXX + +#include <sfx2/basedlgs.hxx> +#include <vcl/weld.hxx> +#include <IMark.hxx> +#include "condedit.hxx" + +class SwWrtShell; +class SfxRequest; + +class BookmarkTable +{ + std::unique_ptr<weld::TreeView> m_xControl; + std::unique_ptr<weld::TreeIter> GetRowByBookmarkName(const OUString& sName); +public: + BookmarkTable(std::unique_ptr<weld::TreeView> xControl); + void InsertBookmark(sw::mark::IMark* pMark); + void SelectByName(const OUString& sName); + sw::mark::IMark* GetBookmarkByName(const OUString& sName); + OUString GetNameProposal() const; + + void unselect_all() { m_xControl->unselect_all(); } + bool has_focus() const { return m_xControl->has_focus(); } + std::unique_ptr<weld::TreeIter> get_selected() const; + void clear() { m_xControl->clear(); } + void remove(const weld::TreeIter& rIter) { m_xControl->remove(rIter); } + void select(const weld::TreeIter& rIter) { m_xControl->select(rIter); } + void remove_selection() { m_xControl->remove_selection(); } + OUString get_id(const weld::TreeIter& rIter) const { return m_xControl->get_id(rIter); } + void set_sort_indicator(TriState eState, int nColumn = -1) { m_xControl->set_sort_indicator(eState, nColumn); } + void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) { m_xControl->selected_foreach(func); } + + void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xControl->connect_changed(rLink); } + void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { m_xControl->connect_row_activated(rLink); } + void connect_column_clicked(const Link<int, void>& rLink) { m_xControl->connect_column_clicked(rLink); } + void make_sorted() { m_xControl->make_sorted(); } + bool get_sort_order() const { return m_xControl->get_sort_order(); } + void set_sort_order(bool bAscending) { m_xControl->set_sort_order(bAscending); } + int get_sort_column() const { return m_xControl->get_sort_column(); } + void set_sort_column(int nColumn) { m_xControl->set_sort_column(nColumn); } + + static const OUString aForbiddenChars; + static const char cSeparator; +}; + +class SwInsertBookmarkDlg : public SfxDialogController +{ + SwWrtShell& rSh; + SfxRequest& rReq; + std::vector<std::pair<sw::mark::IMark*, OUString>> aTableBookmarks; + sal_Int32 m_nLastBookmarksCount; + bool m_bSorted; + bool m_bAreProtected; + + std::unique_ptr<weld::Entry> m_xEditBox; + std::unique_ptr<weld::Button> m_xInsertBtn; + std::unique_ptr<weld::Button> m_xDeleteBtn; + std::unique_ptr<weld::Button> m_xGotoBtn; + std::unique_ptr<weld::Button> m_xRenameBtn; + std::unique_ptr<weld::CheckButton> m_xHideCB; + std::unique_ptr<weld::Label> m_xConditionFT; + std::unique_ptr<ConditionEdit> m_xConditionED; + std::unique_ptr<BookmarkTable> m_xBookmarksBox; + std::unique_ptr<weld::Label> m_xForbiddenChars; + + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(InsertHdl, weld::Button&, void); + DECL_LINK(DeleteHdl, weld::Button&, void); + DECL_LINK(RenameHdl, weld::Button&, void); + DECL_LINK(GotoHdl, weld::Button&, void); + DECL_LINK(SelectionChangedHdl, weld::TreeView&, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(HeaderBarClick, int, void); + DECL_LINK(ChangeHideHdl, weld::ToggleButton&, void); + + // Fill table with bookmarks + void PopulateTable(); + /** + * Check if displayed bookmarks are up-to date, if not update them. + * @return True if no update was needed. + */ + bool ValidateBookmarks(); + bool HaveBookmarksChanged(); + void GotoSelectedBookmark(); + +public: + SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rSh, SfxRequest& rReq); + virtual ~SwInsertBookmarkDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/break.hxx b/sw/source/uibase/inc/break.hxx new file mode 100644 index 000000000..9c07ce2b5 --- /dev/null +++ b/sw/source/uibase/inc/break.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_UIBASE_INC_BREAK_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_BREAK_HXX + +#include <vcl/weld.hxx> +#include <optional> + +class SwWrtShell; + +class SwBreakDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::RadioButton> m_xLineBtn; + std::unique_ptr<weld::RadioButton> m_xColumnBtn; + std::unique_ptr<weld::RadioButton> m_xPageBtn; + std::unique_ptr<weld::Label> m_xPageCollText; + std::unique_ptr<weld::ComboBox> m_xPageCollBox; + std::unique_ptr<weld::CheckButton> m_xPageNumBox; + std::unique_ptr<weld::SpinButton> m_xPageNumEdit; + std::unique_ptr<weld::Button> m_xOkBtn; + + SwWrtShell &rSh; + OUString m_aTemplate; + sal_uInt16 nKind; + ::std::optional<sal_uInt16> oPgNum; + + bool bHtmlMode; + + DECL_LINK(ToggleHdl, weld::ToggleButton&, void); + DECL_LINK(ChangeHdl, weld::ComboBox&, void); + DECL_LINK(PageNumHdl, weld::ToggleButton&, void); + DECL_LINK(PageNumModifyHdl, weld::SpinButton&, void); + DECL_LINK(OkHdl, weld::Button&, void); + + void CheckEnable(); + +public: + SwBreakDlg(weld::Window *pParent, SwWrtShell &rSh); + virtual short run() override; + const OUString& GetTemplateName() const { return m_aTemplate; } + sal_uInt16 GetKind() const { return nKind; } + const ::std::optional<sal_uInt16>& GetPageNumber() const { return oPgNum; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/caption.hxx b/sw/source/uibase/inc/caption.hxx new file mode 100644 index 000000000..d762091b1 --- /dev/null +++ b/sw/source/uibase/inc/caption.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_UIBASE_INC_CAPTION_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CAPTION_HXX + +#include <rtl/ustring.hxx> +#include <tools/globname.hxx> +#include <SwCapObjType.hxx> +#include <swdllapi.h> + +class SW_DLLPUBLIC InsCaptionOpt +{ +private: + bool m_bUseCaption; + SwCapObjType m_eObjType; + SvGlobalName m_aOleId; + OUString m_sCategory; + sal_uInt16 m_nNumType; + OUString m_sNumberSeparator; + OUString m_sCaption; + sal_uInt16 m_nPos; + sal_uInt16 m_nLevel; + OUString m_sSeparator; + OUString m_sCharacterStyle; + + bool m_bIgnoreSeqOpts; // is not being saved + bool m_bCopyAttributes; // -""- + +public: + InsCaptionOpt(const SwCapObjType eType = FRAME_CAP, const SvGlobalName* pOleId = nullptr); + + bool& UseCaption() { return m_bUseCaption; } + bool UseCaption() const { return m_bUseCaption; } + + SwCapObjType GetObjType() const { return m_eObjType; } + + const SvGlobalName& GetOleId() const { return m_aOleId; } + + const OUString& GetCategory() const { return m_sCategory; } + void SetCategory(const OUString& rCat) { m_sCategory = rCat; } + + sal_uInt16 GetNumType() const { return m_nNumType; } + void SetNumType(const sal_uInt16 nNT) { m_nNumType = nNT; } + + const OUString& GetNumSeparator() const { return m_sNumberSeparator; } + void SetNumSeparator(const OUString& rSet) {m_sNumberSeparator = rSet;} + + const OUString& GetCaption() const { return m_sCaption; } + void SetCaption(const OUString& rCap) { m_sCaption = rCap; } + + sal_uInt16 GetPos() const { return m_nPos; } + void SetPos(const sal_uInt16 nP) { m_nPos = nP; } + + sal_uInt16 GetLevel() const { return m_nLevel; } + void SetLevel(const sal_uInt16 nLvl) { m_nLevel = nLvl; } + + const OUString& GetSeparator() const { return m_sSeparator; } + void SetSeparator(const OUString& rSep) { m_sSeparator = rSep; } + + const OUString& GetCharacterStyle() const { return m_sCharacterStyle; } + void SetCharacterStyle(const OUString& rStyle) + { m_sCharacterStyle = rStyle; } + + bool& IgnoreSeqOpts() { return m_bIgnoreSeqOpts; } + bool IgnoreSeqOpts() const { return m_bIgnoreSeqOpts; } + + bool& CopyAttributes() { return m_bCopyAttributes; } + bool CopyAttributes() const { return m_bCopyAttributes; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/cfgitems.hxx b/sw/source/uibase/inc/cfgitems.hxx new file mode 100644 index 000000000..bf50b8f10 --- /dev/null +++ b/sw/source/uibase/inc/cfgitems.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_UIBASE_INC_CFGITEMS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CFGITEMS_HXX + +#include <svl/poolitem.hxx> +#include <swdllapi.h> +#include <printdata.hxx> + +#include <cmdid.h> + +class SwModule; +#ifdef DBG_UTIL +class SwTestTabPage; +#endif +class SwAddPrinterTabPage; +class SfxPrinter; +class SwViewShell; +class SwViewOption; +class SwContentOptPage; +class SwShdwCursorOptionsTabPage; +enum class SwFillMode; + +// OS 12.01.95 +// Item for settings dialog - document view +class SW_DLLPUBLIC SwDocDisplayItem : public SfxPoolItem +{ + friend class SwShdwCursorOptionsTabPage; + friend class SwModule; + + bool bParagraphEnd :1; + bool bTab :1; + bool bSpace :1; + bool bNonbreakingSpace :1; + bool bSoftHyphen :1; + bool bCharHiddenText :1; + bool bBookmarks :1; + bool bManualBreak :1; + +public: + SwDocDisplayItem(); + SwDocDisplayItem( const SwViewOption& rVOpt ); + + virtual SwDocDisplayItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + void FillViewOptions( SwViewOption& rVOpt) const; +}; + +// OS 12.01.95 +// Item for settings dialog, element page +class SW_DLLPUBLIC SwElemItem : public SfxPoolItem +{ + //view + bool m_bVertRuler :1; + bool m_bVertRulerRight:1; + bool m_bSmoothScroll :1; + //visual aids + bool m_bCrosshair :1; + //display + bool m_bTable :1; + bool m_bGraphic :1; + bool m_bDrawing :1; + bool m_bNotes :1; + bool m_bShowInlineTooltips :1; + bool m_bFieldHiddenText :1; + bool m_bShowHiddenPara :1; + + friend class SwContentOptPage; + +public: + SwElemItem(); + SwElemItem(const SwViewOption& rVOpt); + + virtual SwElemItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void FillViewOptions( SwViewOption& rVOpt) const; + +}; + +// OS 12.01.95 +// Item for settings dialog - printer/add-ons +class SW_DLLPUBLIC SwAddPrinterItem : public SfxPoolItem, public SwPrintData +{ + using SwPrintData::operator ==; + +public: + SwAddPrinterItem(); + SwAddPrinterItem( const SwPrintData& rPrtData ); + + virtual SwAddPrinterItem* Clone( SfxItemPool *pPool = nullptr ) const override; + + virtual bool operator==( const SfxPoolItem& ) const override; +}; + +// Item for settings dialog, ShadowCursorPage +class SW_DLLPUBLIC SwShadowCursorItem : public SfxPoolItem +{ + SwFillMode eMode; + bool bOn; +public: + SwShadowCursorItem(); + SwShadowCursorItem( const SwViewOption& rVOpt ); + + virtual SwShadowCursorItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void FillViewOptions( SwViewOption& rVOpt) const; + + SwFillMode GetMode() const { return eMode; } + bool IsOn() const { return bOn; } + + void SetMode( SwFillMode eM ) { eMode = eM; } + void SetOn( bool bFlag ) { bOn = bFlag; } +}; + +#ifdef DBG_UTIL + +// Item for settings dialog - test settings +class SW_DLLPUBLIC SwTestItem : public SfxPoolItem +{ + friend class SwModule; + friend class SwTestTabPage; + + bool m_bTest1:1; + bool m_bTest2:1; + bool m_bTest3:1; + bool m_bTest4:1; + bool m_bTest5:1; + bool m_bTest6:1; + bool m_bTest7:1; + bool m_bTest8:1; + bool m_bTest9:1; + bool m_bTest10:1; + +public: + SwTestItem() : SfxPoolItem(FN_PARAM_SWTEST) {}; + + virtual SwTestItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + +}; +#endif + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/changedb.hxx b/sw/source/uibase/inc/changedb.hxx new file mode 100644 index 000000000..a56144bd9 --- /dev/null +++ b/sw/source/uibase/inc/changedb.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_CHANGEDB_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CHANGEDB_HXX + +#include <sfx2/basedlgs.hxx> +#include "dbtree.hxx" + +class SwFieldMgr; +class SwView; +class SwWrtShell; +struct SwDBData; + +// exchange database at fields +class SwChangeDBDlg : public SfxDialogController +{ + SwWrtShell *pSh; + + std::unique_ptr<weld::TreeView> m_xUsedDBTLB; + std::unique_ptr<SwDBTreeList> m_xAvailDBTLB; + std::unique_ptr<weld::Button> m_xAddDBPB; + std::unique_ptr<weld::Label> m_xDocDBNameFT; + std::unique_ptr<weld::Button> m_xDefineBT; + + void TreeSelect(); + + DECL_LINK(TreeSelectHdl, weld::TreeView&, void); + DECL_LINK(ButtonHdl, weld::Button&, void); + DECL_LINK(AddDBHdl, weld::Button&, void); + + void UpdateFields(); + void FillDBPopup(); + std::unique_ptr<weld::TreeIter> Insert(const OUString& rDBName); + void ShowDBName(const SwDBData& rDBData); + +public: + SwChangeDBDlg(SwView const & rVw); + virtual short run() override; + virtual ~SwChangeDBDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/chartins.hxx b/sw/source/uibase/inc/chartins.hxx new file mode 100644 index 000000000..d258053ae --- /dev/null +++ b/sw/source/uibase/inc/chartins.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_UIBASE_INC_CHARTINS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CHARTINS_HXX + +#include <tools/gen.hxx> +#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp> + +namespace vcl { class Window; } + +Point SwGetChartDialogPos( const vcl::Window *pParentWin, const Size& rDialogSize, const tools::Rectangle& rLogicChart ); + +class SwInsertChart +{ +public: + SwInsertChart( const Link<css::ui::dialogs::DialogClosedEvent*,void>& rLink ); +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CHARTINS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/chldwrap.hxx b/sw/source/uibase/inc/chldwrap.hxx new file mode 100644 index 000000000..b6f5b41ab --- /dev/null +++ b/sw/source/uibase/inc/chldwrap.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_UIBASE_INC_CHLDWRAP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CHLDWRAP_HXX + +#include <sfx2/childwin.hxx> +#include <vcl/timer.hxx> + +class SwDocShell; + +class SAL_DLLPUBLIC_RTTI SwChildWinWrapper : public SfxChildWindow +{ + Timer m_aUpdateTimer; + SwDocShell* m_pDocSh; + + DECL_LINK( UpdateHdl, Timer*, void ); + + // Implementation in fldtdlg.cxx +protected: + SwChildWinWrapper(vcl::Window *pParentWindow, sal_uInt16 nId); + +public: + virtual bool ReInitDlg(SwDocShell *pDocSh); + + SwDocShell* GetOldDocShell() { return m_pDocSh; } + void SetOldDocShell(SwDocShell *pDcSh) { m_pDocSh = pDcSh; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/chrdlg.hxx b/sw/source/uibase/inc/chrdlg.hxx new file mode 100644 index 000000000..09138b5cc --- /dev/null +++ b/sw/source/uibase/inc/chrdlg.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_UIBASE_INC_CHRDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CHRDLG_HXX + +#include <sfx2/tabdlg.hxx> +#include "chrdlgmodes.hxx" + +class SwView; +class SvxMacroItem; + +class SwCharDlg : public SfxTabDialogController +{ + SwView& m_rView; + SwCharDlgMode m_nDialogMode; + +public: + SwCharDlg(weld::Window* pParent, SwView& pVw, const SfxItemSet& rCoreSet, + SwCharDlgMode nDialogMode, const OUString* pFormatStr); + + virtual ~SwCharDlg() override; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; +}; + +class SwCharURLPage : public SfxTabPage +{ + std::unique_ptr<SvxMacroItem> pINetItem; + bool bModified; + + std::unique_ptr<weld::Entry> m_xURLED; + std::unique_ptr<weld::Label> m_xTextFT; + std::unique_ptr<weld::Entry> m_xTextED; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::ComboBox> m_xTargetFrameLB; + std::unique_ptr<weld::Button> m_xURLPB; + std::unique_ptr<weld::Button> m_xEventPB; + std::unique_ptr<weld::ComboBox> m_xVisitedLB; + std::unique_ptr<weld::ComboBox> m_xNotVisitedLB; + std::unique_ptr<weld::Widget> m_xCharStyleContainer; + + DECL_LINK(InsertFileHdl, weld::Button&, void); + DECL_LINK(EventHdl, weld::Button&, void); + +public: + SwCharURLPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + + virtual ~SwCharURLPage() override; + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/chrdlgmodes.hxx b/sw/source/uibase/inc/chrdlgmodes.hxx new file mode 100644 index 000000000..cddb6a9c2 --- /dev/null +++ b/sw/source/uibase/inc/chrdlgmodes.hxx @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_UIBASE_INC_CHRDLGMODES_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CHRDLGMODES_HXX + +// DialogModes for SwCharDlg +enum class SwCharDlgMode { + Std, Draw, Env, Ann +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CHRDLGMODES_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/cnttab.hxx b/sw/source/uibase/inc/cnttab.hxx new file mode 100644 index 000000000..e95cc5c62 --- /dev/null +++ b/sw/source/uibase/inc/cnttab.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_UIBASE_INC_CNTTAB_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CNTTAB_HXX + +#include <sal/types.h> + +#include <toxe.hxx> + +#define TOX_PAGE_SELECT 1 +#define TOX_PAGE_ENTRY 2 +#define TOX_PAGE_STYLES 3 + +#define TO_CONTENT 1 +#define TO_INDEX 2 +#define TO_ILLUSTRATION 4 +#define TO_TABLE 8 +#define TO_USER 16 +#define TO_OBJECT 32 +#define TO_AUTHORITIES 64 +#define TO_BIBLIOGRAPHY 128 + +struct CurTOXType +{ + TOXTypes eType; + sal_uInt16 nIndex; //for TOX_USER only + + bool operator==(const CurTOXType& rCmp) const + { + return eType == rCmp.eType && nIndex == rCmp.nIndex; + } + sal_uInt16 GetFlatIndex() const; + + CurTOXType () : eType (TOX_INDEX), nIndex (0) {}; + + CurTOXType (TOXTypes t) : eType (t), nIndex (0) {}; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/colex.hxx b/sw/source/uibase/inc/colex.hxx new file mode 100644 index 000000000..41f7e4143 --- /dev/null +++ b/sw/source/uibase/inc/colex.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_COLEX_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_COLEX_HXX + +#include <svx/pagectrl.hxx> +#include <editeng/paperinf.hxx> +#include <swdllapi.h> +#include <tgrditem.hxx> +#include <fmtclds.hxx> + +class SwColMgr; +class SfxItemSet; + +class SW_DLLPUBLIC SwPageExample : public SvxPageWindow +{ +protected: + bool m_bVertical; +public: + SwPageExample() + : m_bVertical(false) + { + SetSize(SvxPaperInfo::GetPaperSize(PAPER_A4)); + } + + void UpdateExample( const SfxItemSet& rSet ); +}; + +class SW_DLLPUBLIC SwPageGridExample : public SwPageExample +{ + std::unique_ptr<SwTextGridItem> pGridItem; +protected: + virtual void DrawPage(vcl::RenderContext& rRenderContext, + const Point& rPoint, + const bool bSecond, + const bool bEnabled) override; +public: + SwPageGridExample(); + + void UpdateExample( const SfxItemSet& rSet ); +}; + + +class SW_DLLPUBLIC SwColExample : public SwPageExample +{ + SwColMgr* pColMgr; + + using SwPageExample::UpdateExample; + +protected: + virtual void DrawPage(vcl::RenderContext& rRenderContext, + const Point& rPoint, + const bool bSecond, + const bool bEnabled) override; + +public: + SwColExample() + : pColMgr(nullptr) + { + } + + void UpdateExample( const SfxItemSet& rSet, SwColMgr* pMgr ) + { + pColMgr = pMgr; + SwPageExample::UpdateExample(rSet); + } +}; + +class SW_DLLPUBLIC SwColumnOnlyExample : public weld::CustomWidgetController +{ +private: + Size m_aWinSize; + + Size m_aFrameSize; + SwFormatCol m_aCols; + +protected: + virtual void Resize() override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + +public: + SwColumnOnlyExample(); + + void SetColumns(const SwFormatCol& rCol); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_COLEX_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/colmgr.hxx b/sw/source/uibase/inc/colmgr.hxx new file mode 100644 index 000000000..928e1dcfa --- /dev/null +++ b/sw/source/uibase/inc/colmgr.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_UIBASE_INC_COLMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_COLMGR_HXX + +#include <swdllapi.h> +#include <fmtclds.hxx> + +#include <climits> + +SW_DLLPUBLIC void FitToActualSize(SwFormatCol& rCol, sal_uInt16 nWidth); + +class SW_DLLPUBLIC SwColMgr +{ +public: + // lActWidth is passed directly from the page dialogs edits + SwColMgr(const SfxItemSet &rSet); + ~SwColMgr(); + + inline sal_uInt16 GetCount() const; + void SetCount(sal_uInt16 nCount, sal_uInt16 nGutterWidth); + sal_uInt16 GetGutterWidth(sal_uInt16 nPos = USHRT_MAX) const; + void SetGutterWidth(sal_uInt16 nWidth, sal_uInt16 nPos = USHRT_MAX); + + sal_uInt16 GetColWidth(sal_uInt16 nIdx) const; + void SetColWidth(sal_uInt16 nIdx, sal_uInt16 nWidth); + + inline bool IsAutoWidth() const; + void SetAutoWidth(bool bOn, sal_uInt16 lGutterWidth = 0); + + inline bool HasLine() const; + inline void SetNoLine(); + + void SetLineWidthAndColor(SvxBorderLineStyle eStyle, sal_uLong nWidth, const Color& rCol); + inline SvxBorderLineStyle GetLineStyle() const; + inline sal_uLong GetLineWidth() const; + inline const Color& GetLineColor() const; + + inline SwColLineAdj GetAdjust() const; + inline void SetAdjust(SwColLineAdj); + + short GetLineHeightPercent() const; + void SetLineHeightPercent(short nPercent); + + inline void NoCols(); + + const SwFormatCol& GetColumns() const { return aFormatCol; } + + void SetActualWidth(sal_uInt16 nW); + sal_uInt16 GetActualSize() const { return nWidth; } + +private: + SwFormatCol aFormatCol; + sal_uInt16 nWidth; +}; + +inline sal_uInt16 SwColMgr::GetCount() const +{ + return aFormatCol.GetNumCols(); +} + +inline SvxBorderLineStyle SwColMgr::GetLineStyle() const +{ + return aFormatCol.GetLineStyle(); +} +inline sal_uLong SwColMgr::GetLineWidth() const +{ + return aFormatCol.GetLineWidth(); +} + +inline const Color& SwColMgr::GetLineColor() const +{ + return aFormatCol.GetLineColor(); +} + +inline SwColLineAdj SwColMgr::GetAdjust() const +{ + return aFormatCol.GetLineAdj(); +} + +inline void SwColMgr::SetAdjust(SwColLineAdj eAdj) +{ + aFormatCol.SetLineAdj(eAdj); +} +inline bool SwColMgr::IsAutoWidth() const +{ + return aFormatCol.IsOrtho(); +} +inline void SwColMgr::SetAutoWidth(bool bOn, sal_uInt16 nGutterWidth) +{ + aFormatCol.SetOrtho(bOn, nGutterWidth, nWidth); +} + +inline void SwColMgr::NoCols() +{ + aFormatCol.GetColumns().clear(); +} +inline bool SwColMgr::HasLine() const +{ + return GetAdjust() != COLADJ_NONE; +} + +inline void SwColMgr::SetNoLine() +{ + SetAdjust(COLADJ_NONE); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/column.hxx b/sw/source/uibase/inc/column.hxx new file mode 100644 index 000000000..1b799c1c2 --- /dev/null +++ b/sw/source/uibase/inc/column.hxx @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_COLUMN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_COLUMN_HXX + +#include <svtools/ctrlbox.hxx> +#include <svtools/valueset.hxx> +#include <sfx2/basedlgs.hxx> +#include <sfx2/tabdlg.hxx> +#include <svx/colorbox.hxx> +#include <svx/frmdirlbox.hxx> +#include "colex.hxx" +#include "prcntfld.hxx" + +const int nMaxCols = 99; +class SwColMgr; +class SwWrtShell; +class SwColumnPage; + +class SwColumnDlg : public SfxDialogController +{ + SwWrtShell& m_rWrtShell; + std::unique_ptr<SwColumnPage> m_xTabPage; + std::unique_ptr<SfxItemSet> m_pPageSet; + std::unique_ptr<SfxItemSet> m_pSectionSet; + std::unique_ptr<SfxItemSet> m_pSelectionSet; + SfxItemSet* m_pFrameSet; + + long m_nOldSelection; + long m_nSelectionWidth; + long m_nPageWidth; + + bool m_bPageChanged : 1; + bool m_bSectionChanged : 1; + bool m_bSelSectionChanged : 1; + bool m_bFrameChanged : 1; + + std::unique_ptr<weld::Container> m_xContentArea; + std::unique_ptr<weld::Button> m_xOkButton; + + DECL_LINK(ObjectListBoxHdl, weld::ComboBox&, void); + DECL_LINK(OkHdl, weld::Button&, void); + void ObjectHdl(const weld::ComboBox*); + SfxItemSet* EvalCurrentSelection(void); + +public: + SwColumnDlg(weld::Window* pParent, SwWrtShell& rSh); + virtual ~SwColumnDlg() override; +}; + +class ColumnValueSet : public ValueSet +{ +public: + ColumnValueSet() + : ValueSet(nullptr) + { + } + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + ValueSet::SetDrawingArea(pDrawingArea); + SetStyle(WB_TABSTOP | WB_ITEMBORDER | WB_DOUBLEBORDER); + } + virtual void UserDraw(const UserDrawEvent& rUDEvt) override; + virtual void StyleUpdated() override; +}; + +// column dialog now as TabPage +class SwColumnPage : public SfxTabPage +{ + std::unique_ptr<SwColMgr> m_xColMgr; + + sal_uInt16 m_nFirstVis; + sal_uInt16 m_nCols; + long m_nColWidth[nMaxCols]; + long m_nColDist[nMaxCols]; + SwPercentField* m_pModifiedField; + + std::map<weld::MetricSpinButton*, SwPercentField*> m_aPercentFieldsMap; + + bool m_bFormat; + bool m_bFrame; + bool m_bHtmlMode; + bool m_bLockUpdate; + + ColumnValueSet m_aDefaultVS; + SwColExample m_aPgeExampleWN; + SwColumnOnlyExample m_aFrameExampleWN; + + std::unique_ptr<weld::SpinButton> m_xCLNrEdt; + std::unique_ptr<weld::CheckButton> m_xBalanceColsCB; + std::unique_ptr<weld::Button> m_xBtnBack; + std::unique_ptr<weld::Label> m_xLbl1; + std::unique_ptr<weld::Label> m_xLbl2; + std::unique_ptr<weld::Label> m_xLbl3; + std::unique_ptr<weld::Button> m_xBtnNext; + std::unique_ptr<weld::CheckButton> m_xAutoWidthBox; + std::unique_ptr<weld::Label> m_xLineTypeLbl; + std::unique_ptr<weld::Label> m_xLineWidthLbl; + std::unique_ptr<weld::MetricSpinButton> m_xLineWidthEdit; + std::unique_ptr<weld::Label> m_xLineColorLbl; + std::unique_ptr<weld::Label> m_xLineHeightLbl; + std::unique_ptr<weld::MetricSpinButton> m_xLineHeightEdit; + std::unique_ptr<weld::Label> m_xLinePosLbl; + std::unique_ptr<weld::ComboBox> m_xLinePosDLB; + std::unique_ptr<weld::Label> m_xTextDirectionFT; + std::unique_ptr<svx::FrameDirectionListBox> m_xTextDirectionLB; + std::unique_ptr<ColorListBox> m_xLineColorDLB; + std::unique_ptr<SvtLineListBox> m_xLineTypeDLB; + std::unique_ptr<SwPercentField> m_xEd1; + std::unique_ptr<SwPercentField> m_xEd2; + std::unique_ptr<SwPercentField> m_xEd3; + std::unique_ptr<SwPercentField> m_xDistEd1; + std::unique_ptr<SwPercentField> m_xDistEd2; + std::unique_ptr<weld::CustomWeld> m_xDefaultVS; + // Example + std::unique_ptr<weld::CustomWeld> m_xPgeExampleWN; + std::unique_ptr<weld::CustomWeld> m_xFrameExampleWN; + + std::unique_ptr<weld::Label> m_xApplyToFT; + std::unique_ptr<weld::ComboBox> m_xApplyToLB; + + // Handler + DECL_LINK(ColModify, weld::SpinButton&, void); + void ColModify(const weld::SpinButton*); + DECL_LINK(GapModify, weld::MetricSpinButton&, void); + DECL_LINK(EdModify, weld::MetricSpinButton&, void); + DECL_LINK(AutoWidthHdl, weld::ToggleButton&, void ); + DECL_LINK(SetDefaultsHdl, ValueSet *, void); + + DECL_LINK(Up, weld::Button&, void); + DECL_LINK(Down, weld::Button&, void); + DECL_LINK(UpdateColMgr, weld::MetricSpinButton&, void); + DECL_LINK(UpdateColMgrListBox, weld::ComboBox&, void); + DECL_LINK(UpdateColMgrLineBox, SvtLineListBox&, void); + DECL_LINK(UpdateColMgrColorBox, ColorListBox&, void); + void Timeout(); + + void Update(const weld::MetricSpinButton* pInteractiveField); + void UpdateCols(); + void Init(); + void ResetColWidth(); + void SetLabels( sal_uInt16 nVis ); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + + void connectPercentField(SwPercentField &rWrap); + + bool isLineNotNone() const; + + static const sal_uInt16 aPageRg[]; + +public: + SwColumnPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwColumnPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges() { return aPageRg; } + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + + void SetFrameMode(bool bMod); + void SetPageWidth(long nPageWidth); + + void SetFormatUsed(bool bFormatUsed) + { + m_bFormat = bFormatUsed; + } + + void ShowBalance(bool bShow) + { + m_xBalanceColsCB->set_visible(bShow); + } + + void SetInSection(bool bSet); + + void ActivateColumnControl() + { + m_xCLNrEdt->grab_focus(); + } + + weld::Label* GetApplyLabel() { return m_xApplyToFT.get(); } + weld::ComboBox* GetApplyComboBox() { return m_xApplyToLB.get(); } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/conarc.hxx b/sw/source/uibase/inc/conarc.hxx new file mode 100644 index 000000000..b1b31ec03 --- /dev/null +++ b/sw/source/uibase/inc/conarc.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_UIBASE_INC_CONARC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONARC_HXX + +#include "drawbase.hxx" + +// draw rectangle +class ConstArc : public SwDrawBase +{ + Point m_aStartPoint; + sal_uInt16 m_nButtonUpCount; + +public: + ConstArc(SwWrtShell* pSh, SwEditWin* pWin, SwView* pView); + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function + virtual void Deactivate() override; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CONARC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/concustomshape.hxx b/sw/source/uibase/inc/concustomshape.hxx new file mode 100644 index 000000000..4d67a00dd --- /dev/null +++ b/sw/source/uibase/inc/concustomshape.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_UIBASE_INC_CONCUSTOMSHAPE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONCUSTOMSHAPE_HXX + +#include <rtl/ustring.hxx> +#include "drawbase.hxx" + +class SdrObject; +class SfxRequest; + +// draw rectangle +class ConstCustomShape : public SwDrawBase +{ + + OUString aCustomShape; + + void SetAttributes( SdrObject* pObj ); + + public: + + ConstCustomShape( SwWrtShell* pSh, SwEditWin* pWin, SwView* pView, SfxRequest const & rReq ); + + // Mouse- & Key-Events + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function + + const OUString& GetShapeType() const; + static OUString GetShapeTypeFromRequest( SfxRequest const & rReq ); + + virtual void CreateDefaultObject() override; + + // #i33136# + virtual bool doConstructOrthogonal() const override; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CONCUSTOMSHAPE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/condedit.hxx b/sw/source/uibase/inc/condedit.hxx new file mode 100644 index 000000000..974fecf95 --- /dev/null +++ b/sw/source/uibase/inc/condedit.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_UIBASE_INC_CONDEDIT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONDEDIT_HXX + +#include <vcl/transfer.hxx> +#include <vcl/weld.hxx> +#include <swdllapi.h> + +class ConditionEdit; + +class SW_DLLPUBLIC ConditionEditDropTarget : public DropTargetHelper +{ +private: + ConditionEdit& m_rEdit; + + SAL_DLLPRIVATE virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + SAL_DLLPRIVATE virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + ConditionEditDropTarget(ConditionEdit& rEdit); +}; + +class SW_DLLPUBLIC ConditionEdit +{ + std::unique_ptr<weld::Entry> m_xControl; + ConditionEditDropTarget m_aDropTargetHelper; + bool bBrackets, bEnableDrop; + +public: + ConditionEdit(std::unique_ptr<weld::Entry> xControl); + + OUString get_text() const { return m_xControl->get_text(); } + void set_text(const OUString& rText) { m_xControl->set_text(rText); } + void set_visible(bool bVisible) { m_xControl->set_visible(bVisible); } + void set_accessible_name(const OUString& rName) { m_xControl->set_accessible_name(rName); } + bool get_sensitive() const { return m_xControl->get_sensitive(); } + void save_value() { m_xControl->save_value(); } + bool get_value_changed_from_saved() const { return m_xControl->get_value_changed_from_saved(); } + void set_sensitive(bool bSensitive) { m_xControl->set_sensitive(bSensitive); } + void connect_changed(const Link<weld::Entry&, void>& rLink) { m_xControl->connect_changed(rLink); } + void replace_selection(const OUString& rText) { m_xControl->replace_selection(rText); } + void hide() { m_xControl->hide(); } + weld::Entry& get_widget() { return *m_xControl; } + + void ShowBrackets(bool bShow) { bBrackets = bShow; } + bool GetBrackets() const { return bBrackets; } + void SetDropEnable(bool bFlag) { bEnableDrop = bFlag; } + bool GetDropEnable() const { return bEnableDrop; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/conform.hxx b/sw/source/uibase/inc/conform.hxx new file mode 100644 index 000000000..65e01caf9 --- /dev/null +++ b/sw/source/uibase/inc/conform.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_CONFORM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONFORM_HXX + +#include "drawbase.hxx" + +class ConstFormControl : public SwDrawBase +{ +public: + ConstFormControl(SwWrtShell* pSh, SwEditWin* pWin, SwView* pView); + + // Mouse- & Key-Events + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function + virtual void CreateDefaultObject() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/conpoly.hxx b/sw/source/uibase/inc/conpoly.hxx new file mode 100644 index 000000000..d1fad7db3 --- /dev/null +++ b/sw/source/uibase/inc/conpoly.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_CONPOLY_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONPOLY_HXX + +#include "drawbase.hxx" + +// base class for all functions +class ConstPolygon : public SwDrawBase +{ +public: + ConstPolygon(SwWrtShell* pSh, SwEditWin* pWin, SwView* pView); + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CONPOLY_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/conrect.hxx b/sw/source/uibase/inc/conrect.hxx new file mode 100644 index 000000000..878d99c80 --- /dev/null +++ b/sw/source/uibase/inc/conrect.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_CONRECT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONRECT_HXX + +#include "drawbase.hxx" + +// draw rectangle +class ConstRectangle : public SwDrawBase +{ + bool bMarquee; + bool bCapVertical; + + bool mbVertical; + + public: + ConstRectangle(SwWrtShell* pSh, SwEditWin* pWin, SwView* pView); + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_CONRECT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/content.hxx b/sw/source/uibase/inc/content.hxx new file mode 100644 index 000000000..414db7068 --- /dev/null +++ b/sw/source/uibase/inc/content.hxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_CONTENT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONTENT_HXX +#include <memory> +#include "swcont.hxx" + +#include <ndarr.hxx> + +class SwWrtShell; +class SwContentArr; +class SwContentType; +class SwFormatField; +class SwTextINetFormat; +class SwTOXBase; +class SwRangeRedline; + +// helper classes + +class SwOutlineContent : public SwContent +{ + SwOutlineNodes::size_type nOutlinePos; + sal_uInt8 nOutlineLevel; + bool bIsMoveable; + public: + SwOutlineContent( const SwContentType* pCnt, + const OUString& rName, + SwOutlineNodes::size_type nArrPos, + sal_uInt8 nLevel, + bool bMove, + long nYPos) : + SwContent(pCnt, rName, nYPos), + nOutlinePos(nArrPos), nOutlineLevel(nLevel), bIsMoveable(bMove) {} + + SwOutlineNodes::size_type GetOutlinePos() const {return nOutlinePos;} + sal_uInt8 GetOutlineLevel() const {return nOutlineLevel;} + bool IsMoveable() const {return bIsMoveable;}; +}; + +class SwRegionContent : public SwContent +{ + + sal_uInt8 nRegionLevel; + + public: + SwRegionContent( const SwContentType* pCnt, + const OUString& rName, + sal_uInt8 nLevel, + long nYPos) : + SwContent(pCnt, rName, nYPos), + nRegionLevel(nLevel){} + sal_uInt8 GetRegionLevel() const {return nRegionLevel;} +}; + +class SwURLFieldContent : public SwContent +{ + OUString sURL; + const SwTextINetFormat* pINetAttr; + +public: + SwURLFieldContent( const SwContentType* pCnt, + const OUString& rName, + const OUString& rURL, + const SwTextINetFormat* pAttr, + long nYPos ) + : SwContent( pCnt, rName, nYPos ), sURL( rURL ), pINetAttr( pAttr ) + {} + + virtual bool IsProtect() const override; + const OUString& GetURL() const { return sURL; } + const SwTextINetFormat* GetINetAttr() const { return pINetAttr; } +}; + +class SwPostItContent : public SwContent +{ + const SwFormatField* pField; +public: + SwPostItContent( const SwContentType* pCnt, + const OUString& rName, + const SwFormatField* pFormatField, + long nYPos ) + : SwContent(pCnt, rName, nYPos) + , pField(pFormatField) + {} + + const SwFormatField* GetPostIt() const { return pField; } + virtual bool IsProtect() const override; +}; + +class SwGraphicContent : public SwContent +{ + OUString sLink; +public: + SwGraphicContent(const SwContentType* pCnt, const OUString& rName, const OUString& rLink, long nYPos) + : SwContent( pCnt, rName, nYPos ), sLink( rLink ) + {} + virtual ~SwGraphicContent() override; + + const OUString& GetLink() const {return sLink;} +}; + +class SwTOXBaseContent : public SwContent +{ + const SwTOXBase* pBase; +public: + SwTOXBaseContent(const SwContentType* pCnt, const OUString& rName, long nYPos, const SwTOXBase& rBase) + : SwContent( pCnt, rName, nYPos ), pBase(&rBase) + {} + virtual ~SwTOXBaseContent() override; + + const SwTOXBase* GetTOXBase() const {return pBase;} +}; + +/** + * Content type, knows it's contents and the WrtShell. + * + * The class ContentType contains information to one type of content. + * MemberArray is only populated if the content is requested by + * GetMember. It is reloaded after Invalidate() only if the content + * should be read again. +*/ +class SwContentType : public SwTypeNumber +{ + SwWrtShell* m_pWrtShell; + std::unique_ptr<SwContentArr> + m_pMember; // array for content + OUString m_sContentTypeName; // name of content type + OUString m_sSingleContentTypeName; // name of content type, singular + OUString m_sTypeToken; // attachment for URL + size_t m_nMemberCount; // content count + ContentTypeId m_nContentType; // content type's Id + sal_uInt8 m_nOutlineLevel; + bool m_bDataValid : 1; + bool m_bEdit: 1; // can this type be edited? + bool m_bDelete: 1; // can this type be deleted? +protected: + static OUString RemoveNewline(const OUString&); +public: + SwContentType(SwWrtShell* pParent, ContentTypeId nType, sal_uInt8 nLevel ); + virtual ~SwContentType() override; + + void Init(bool* pbInvalidateWindow = nullptr); + + /** Fill the List of contents */ + void FillMemberList(bool* pbLevelChanged = nullptr); + size_t GetMemberCount() const + {return m_nMemberCount;}; + ContentTypeId GetType() const {return m_nContentType;} + + /** Deliver content, for that if necessary fill the list */ + const SwContent* GetMember(size_t nIndex); + const OUString& GetName() const {return m_sContentTypeName;} + const OUString& GetSingleName() const {return m_sSingleContentTypeName;} + const OUString& GetTypeToken() const{return m_sTypeToken;} + + void SetOutlineLevel(sal_uInt8 nNew) + { + m_nOutlineLevel = nNew; + Invalidate(); + } + + void Invalidate(); // only nMemberCount is read again + + bool IsEditable() const {return m_bEdit;} + bool IsDeletable() const {return m_bDelete;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/conttree.hxx b/sw/source/uibase/inc/conttree.hxx new file mode 100644 index 000000000..59df0d51c --- /dev/null +++ b/sw/source/uibase/inc/conttree.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_CONTTREE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONTTREE_HXX + +#include <svl/lstner.hxx> +#include <ndarr.hxx> +#include "swcont.hxx" + +#include <map> +#include <memory> + +#include <o3tl/enumarray.hxx> +#include <o3tl/typed_flags_set.hxx> + +class SwWrtShell; +class SwContentType; +class SwNavigationPI; +class SwNavigationConfig; +class Menu; +class ToolBox; +class SwGlblDocContents; +class SwGlblDocContent; +class SfxObjectShell; +class SdrObject; + +enum class EditEntryMode +{ + EDIT = 0, + UPD_IDX = 1, + RMV_IDX = 2, + UNPROTECT_TABLE = 3, + DELETE = 4, + RENAME = 5, +}; + +// Flags for PopupMenu-enable/disable +enum class MenuEnableFlags { + NONE = 0x0000, + InsertIdx = 0x0001, + InsertFile = 0x0002, + InsertText = 0x0004, + Edit = 0x0008, + Delete = 0x0010, + Update = 0x0020, + UpdateSel = 0x0040, + EditLink = 0x0080 +}; +namespace o3tl { + template<> struct typed_flags<MenuEnableFlags> : is_typed_flags<MenuEnableFlags, 0x00ff> {}; +} + +class SwContentTree; + +class SwContentTreeDropTarget : public DropTargetHelper +{ +private: + SwContentTree& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + SwContentTreeDropTarget(SwContentTree& rTreeView); +}; + +/** TreeListBox for content indicator */ +class SwContentTree final : public SfxListener +{ + std::unique_ptr<weld::TreeView> m_xTreeView; + SwContentTreeDropTarget m_aDropTargetHelper; + VclPtr<SwNavigationPI> m_xDialog; + OUString m_sSpace; + AutoTimer m_aUpdTimer; + + o3tl::enumarray<ContentTypeId,std::unique_ptr<SwContentType>> m_aActiveContentArr; + o3tl::enumarray<ContentTypeId,std::unique_ptr<SwContentType>> m_aHiddenContentArr; + OUString m_aContextStrings[CONTEXT_COUNT + 1]; + OUString m_sInvisible; + + SwWrtShell* m_pHiddenShell; // dropped Doc + SwWrtShell* m_pActiveShell; // the active or a const. open view + SwNavigationConfig* m_pConfig; + + std::map< void*, bool > mOutLineNodeMap; + + sal_Int32 m_nActiveBlock; + sal_Int32 m_nHiddenBlock; + size_t m_nEntryCount; + ContentTypeId m_nRootType; + ContentTypeId m_nLastSelType; + sal_uInt8 m_nOutlineLevel; + + sal_uInt32 m_nOutlineTracking = 1; + + enum class State { ACTIVE, CONSTANT, HIDDEN } m_eState; + + bool m_bIsRoot :1; + bool m_bIsIdleClear :1; + bool m_bIsLastReadOnly :1; + bool m_bIsOutlineMoveable :1; + bool m_bViewHasChanged :1; + + // outline root mode drag & drop + std::vector<std::unique_ptr<weld::TreeIter>> m_aDndOutlinesSelected; + + bool m_bIsInPromoteDemote = false; + + /** + * Before any data will be deleted, the last active entry has to be found. + * After this the UserData will be deleted + */ + void FindActiveTypeAndRemoveUserData(); + + void insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId, + const OUString* pExpanderName, bool bChildrenOnDemand, weld::TreeIter* pRet); + + void remove(const weld::TreeIter& rIter); + + SwNavigationPI* GetParentWindow(); + + bool FillTransferData( TransferDataContainer& rTransfer, + sal_Int8& rDragMode ); + + /** Check if the displayed content is valid. */ + bool HasContentChanged(); + + size_t GetAbsPos(const weld::TreeIter& rIter); + + void EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode); + + void GotoContent(const SwContent* pCnt); + + void ExecuteContextMenuAction(const OString& rSelectedPopupEntry); + + void DeleteOutlineSelections(); + + size_t GetEntryCount() const; + + size_t GetChildCount(const weld::TreeIter& rParent) const; + + std::unique_ptr<weld::TreeIter> GetEntryAtAbsPos(size_t nAbsPos) const; + + void Expand(const weld::TreeIter& rParent, std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand); + + void MoveOutline(SwOutlineNodes::size_type nTargetPos); + + void UpdateLastSelType(); + + /** Expand - Remember the state for content types */ + DECL_LINK(ExpandHdl, const weld::TreeIter&, bool); + /** Collapse - Remember the state for content types. */ + DECL_LINK(CollapseHdl, const weld::TreeIter&, bool); + DECL_LINK(ContentDoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(FocusHdl, weld::Widget&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); + DECL_LINK(DragBeginHdl, bool&, bool); + DECL_LINK(TimerUpdate, Timer *, void); + +public: + SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog); + ~SwContentTree(); + SdrObject* GetDrawingObjectsByContent(const SwContent *pCnt); + + /** Switch the display to Root */ + void ToggleToRoot(); + void SetRootType(ContentTypeId nType); + + /** Show the file */ + void Display( bool bActiveView ); + /** In the clear the content types have to be deleted, also. */ + void clear(); + + /** After a file is dropped on the Navigator, the new shell will be set */ + void SetHiddenShell(SwWrtShell* pSh); + void ShowHiddenShell(); + void ShowActualView(); + + /** Document change - set new Shell */ + void SetActiveShell(SwWrtShell* pSh); + + /** Set an open view as active. */ + void SetConstantShell(SwWrtShell* pSh); + + SwWrtShell* GetWrtShell() + { return State::HIDDEN == m_eState ? m_pHiddenShell : m_pActiveShell; } + + bool IsInDrag() const; + + sal_uInt8 GetOutlineLevel()const {return m_nOutlineLevel;} + void SetOutlineLevel(sal_uInt8 nSet); + + /** Execute commands of the Navigator */ + void ExecCommand(const OString& rCmd, bool bModifier); + + void ShowTree(); + void HideTree(); + + bool IsConstantView() const { return State::CONSTANT == m_eState; } + bool IsActiveView() const { return State::ACTIVE == m_eState; } + bool IsHiddenView() const { return State::HIDDEN == m_eState; } + + const SwWrtShell* GetActiveWrtShell() const {return m_pActiveShell;} + SwWrtShell* GetHiddenWrtShell() {return m_pHiddenShell;} + + void Select(); + + // return true if it has any children + bool RequestingChildren(const weld::TreeIter& rParent); + + virtual void Notify(SfxBroadcaster& rBC, SfxHint const& rHint) override; + + sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt); + sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt); + + bool IsDropFormatSupported(SotClipboardFormatId nFormat) + { + return m_aDropTargetHelper.IsDropFormatSupported(nFormat); + } + + void set_accessible_name(const OUString& rName) + { + m_xTreeView->set_accessible_name(rName); + } + + void grab_focus() + { + m_xTreeView->grab_focus(); + } + + OUString get_selected_text() const + { + return m_xTreeView->get_selected_text(); + } + + int count_selected_rows() const + { + return m_xTreeView->count_selected_rows(); + } + + void set_selection_mode(SelectionMode eMode) + { + m_xTreeView->set_selection_mode(eMode); + } + + weld::TreeView& get_widget() + { + return *m_xTreeView; + } +}; + +namespace sfx2 { class DocumentInserter; } +namespace sfx2 { class FileDialogHelper; } + +class SwGlobalTree; + +class SwGlobalTreeDropTarget : public DropTargetHelper +{ +private: + SwGlobalTree& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + SwGlobalTreeDropTarget(SwGlobalTree& rTreeView); +}; + +class SwGlobalTree final +{ +private: + std::unique_ptr<weld::TreeView> m_xTreeView; + SwGlobalTreeDropTarget m_aDropTargetHelper; + VclPtr<SwNavigationPI> m_xDialog; + AutoTimer m_aUpdateTimer; + OUString m_aContextStrings[GLOBAL_CONTEXT_COUNT]; + + SwWrtShell* m_pActiveShell; + std::unique_ptr<SwGlblDocContents> m_pSwGlblDocContents; // array with sorted content + + std::unique_ptr<SwGlblDocContent> m_pDocContent; + std::unique_ptr<sfx2::DocumentInserter> m_pDocInserter; + + static const SfxObjectShell* pShowShell; + + void InsertRegion( const SwGlblDocContent* _pContent, + const css::uno::Sequence< OUString >& _rFiles ); + + DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void ); + + void Select(); + + DECL_LINK(Timeout, Timer*, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); + + SwNavigationPI* GetParentWindow(); + + void OpenDoc(const SwGlblDocContent*); + void GotoContent(const SwGlblDocContent*); + MenuEnableFlags GetEnableFlags() const; + + static void SetShowShell(const SfxObjectShell*pSet) {pShowShell = pSet;} + DECL_STATIC_LINK(SwGlobalTree, ShowFrameHdl, void*, void); + +public: + SwGlobalTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog); + ~SwGlobalTree(); + + bool get_visible() const { return m_xTreeView->get_visible(); } + + void set_accessible_name(const OUString& rName) + { + m_xTreeView->set_accessible_name(rName); + } + + void grab_focus() + { + m_xTreeView->grab_focus(); + } + + int count_selected_rows() const + { + return m_xTreeView->count_selected_rows(); + } + + void set_selection_mode(SelectionMode eMode) + { + m_xTreeView->set_selection_mode(eMode); + } + + weld::TreeView& get_widget() + { + return *m_xTreeView; + } + + void MoveSelectionTo(weld::TreeIter* pDropTarget); + + void TbxMenuHdl(const OString& rCommand, weld::Menu& rMenu); + void InsertRegion( const SwGlblDocContent* pCont, + const OUString* pFileName = nullptr ); + void EditContent(const SwGlblDocContent* pCont ); + + void ShowTree(); + void HideTree(); + + void ExecCommand(const OString& rCmd); + + void Display(bool bOnlyUpdateUserData = false); + + bool Update(bool bHard); + + void ExecuteContextMenuAction(const OString& rSelectedPopupEntry); + + const SwWrtShell* GetActiveWrtShell() const {return m_pActiveShell;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/convert.hxx b/sw/source/uibase/inc/convert.hxx new file mode 100644 index 000000000..08a831b0b --- /dev/null +++ b/sw/source/uibase/inc/convert.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_UIBASE_INC_CONVERT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CONVERT_HXX + +#include <sfx2/basedlgs.hxx> +#include <tblafmt.hxx> + +class VclContainer; +class SwTableAutoFormat; +class SwView; +class SwWrtShell; +struct SwInsertTableOptions; + +class SwConvertTableDlg : public SfxDialogController +{ + std::unique_ptr<weld::RadioButton> m_xTabBtn; + std::unique_ptr<weld::RadioButton> m_xSemiBtn; + std::unique_ptr<weld::RadioButton> m_xParaBtn; + std::unique_ptr<weld::RadioButton> m_xOtherBtn; + std::unique_ptr<weld::Entry> m_xOtherEd; + std::unique_ptr<weld::CheckButton> m_xKeepColumn; + + std::unique_ptr<weld::Container> m_xOptions; + + std::unique_ptr<weld::CheckButton> m_xHeaderCB; + std::unique_ptr<weld::CheckButton> m_xRepeatHeaderCB; + + std::unique_ptr<weld::Container> m_xRepeatRows; + std::unique_ptr<weld::SpinButton> m_xRepeatHeaderNF; + + std::unique_ptr<weld::CheckButton> m_xDontSplitCB; + std::unique_ptr<weld::Button> m_xAutoFormatBtn; + + std::unique_ptr<SwTableAutoFormat> mxTAutoFormat; + SwWrtShell* pShell; + + DECL_LINK(AutoFormatHdl, weld::Button&, void); + DECL_LINK(BtnHdl, weld::Button&, void); + DECL_LINK(CheckBoxHdl, weld::Button&, void); + DECL_LINK(ReapeatHeaderCheckBoxHdl, weld::Button&, void); + +public: + SwConvertTableDlg(SwView& rView, bool bToTable); + + void GetValues( sal_Unicode& rDelim, + SwInsertTableOptions& rInsTableOpts, + SwTableAutoFormat const*& prTAFormat ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/cption.hxx b/sw/source/uibase/inc/cption.hxx new file mode 100644 index 000000000..16fa1f063 --- /dev/null +++ b/sw/source/uibase/inc/cption.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_CPTION_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_CPTION_HXX + +#include <sfx2/basedlgs.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> + +#include "wrtsh.hxx" +#include "optload.hxx" + +class SwFieldMgr; +class SwView; + +class SwCaptionDialog : public SfxDialogController +{ + OUString m_sNone; + TextFilterAutoConvert m_aTextFilter; + SwView &rView; // search per active, avoid View + std::unique_ptr<SwFieldMgr> pMgr; // pointer to save the include + + OUString sCharacterStyle; + bool bCopyAttributes; + bool bOrderNumberingFirst; //#i61007# order of captions + + css::uno::Reference< css::container::XNameAccess > xNameAccess; + + SwCaptionPreview m_aPreview; + std::unique_ptr<weld::Entry> m_xTextEdit; + std::unique_ptr<weld::ComboBox> m_xCategoryBox; + std::unique_ptr<weld::Label> m_xFormatText; + std::unique_ptr<weld::ComboBox> m_xFormatBox; + //#i61007# order of captions + std::unique_ptr<weld::Label> m_xNumberingSeparatorFT; + std::unique_ptr<weld::Entry> m_xNumberingSeparatorED; + std::unique_ptr<weld::Label> m_xSepText; + std::unique_ptr<weld::Entry> m_xSepEdit; + std::unique_ptr<weld::Label> m_xPosText; + std::unique_ptr<weld::ComboBox> m_xPosBox; + std::unique_ptr<weld::Button> m_xOKButton; + std::unique_ptr<weld::Button> m_xAutoCaptionButton; + std::unique_ptr<weld::Button> m_xOptionButton; + std::unique_ptr<weld::CustomWeld> m_xPreview; + + DECL_LINK(SelectListBoxHdl, weld::ComboBox&, void); + DECL_LINK(ModifyEntryHdl, weld::Entry&, void); + DECL_LINK(ModifyComboHdl, weld::ComboBox&, void); + DECL_LINK(OptionHdl, weld::Button&, void); + DECL_LINK(CaptionHdl, weld::Button&, void); + + void Apply(); + + void ModifyHdl(); + void DrawSample(); + void ApplyCaptionOrder(); //#i61007# order of captions + + static OUString our_aSepTextSave; // Save caption separator text +public: + SwCaptionDialog(weld::Window *pParent, SwView &rV); + virtual short run() override; + virtual ~SwCaptionDialog() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/dbconfig.hxx b/sw/source/uibase/inc/dbconfig.hxx new file mode 100644 index 000000000..5a611f5d8 --- /dev/null +++ b/sw/source/uibase/inc/dbconfig.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_UIBASE_INC_DBCONFIG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DBCONFIG_HXX + +#include <unotools/configitem.hxx> +#include <swdllapi.h> + +struct SwDBData; + +class SW_DLLPUBLIC SwDBConfig : public utl::ConfigItem +{ +private: + SAL_DLLPRIVATE static const css::uno::Sequence<OUString>& GetPropertyNames(); + virtual void ImplCommit() override; + + std::unique_ptr<SwDBData> pAdrImpl; + std::unique_ptr<SwDBData> pBibImpl; + +public: + SwDBConfig(); + virtual ~SwDBConfig() override; + + void Load(); + const SwDBData& GetAddressSource(); + const SwDBData& GetBibliographySource(); + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/dbinsdlg.hxx b/sw/source/uibase/inc/dbinsdlg.hxx new file mode 100644 index 000000000..0dc715d1c --- /dev/null +++ b/sw/source/uibase/inc/dbinsdlg.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_UIBASE_INC_DBINSDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DBINSDLG_HXX + +#include <vcl/weld.hxx> +#include <sfx2/basedlgs.hxx> +#include <unotools/configitem.hxx> +#include "numfmtlb.hxx" +#include <swdbdata.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> +#include <o3tl/sorted_vector.hxx> + +#include <memory> +#include <vector> + +namespace com::sun::star{ + namespace sdbcx{ + class XColumnsSupplier; + } + namespace sdbc{ + class XDataSource; + class XConnection; + class XResultSet; + } +} + +class SwTableAutoFormat; +class SwView; +class SfxItemSet; +class SwTableRep; +struct DB_Column; + +typedef std::vector<std::unique_ptr<DB_Column>> DB_Columns; + +struct SwInsDBColumn +{ + OUString sColumn, sUsrNumFormat; + sal_Int32 nDBNumFormat; + sal_uInt32 nUsrNumFormat; + LanguageType eUsrNumFormatLng; + bool bHasFormat : 1; + bool bIsDBFormat : 1; + + SwInsDBColumn( const OUString& rStr ) + : sColumn( rStr ), + nDBNumFormat( 0 ), + nUsrNumFormat( 0 ), + eUsrNumFormatLng( LANGUAGE_SYSTEM ), + bHasFormat(false), + bIsDBFormat(true) + {} + + bool operator<( const SwInsDBColumn& rCmp ) const; +}; + +class SwInsDBColumns : public o3tl::sorted_vector<std::unique_ptr<SwInsDBColumn>, o3tl::less_uniqueptr_to<SwInsDBColumn> > +{ +}; + +class SwInsertDBColAutoPilot : public SfxDialogController, public utl::ConfigItem +{ + SwInsDBColumns aDBColumns; + const SwDBData aDBData; + + OUString sNoTmpl; + + SwView* pView; + std::unique_ptr<SwTableAutoFormat> m_xTAutoFormat; + + std::unique_ptr<SfxItemSet> pTableSet; + std::unique_ptr<SwTableRep> pRep; + sal_Int32 nGBFormatLen; + + std::unique_ptr<weld::RadioButton> m_xRbAsTable; + std::unique_ptr<weld::RadioButton> m_xRbAsField; + std::unique_ptr<weld::RadioButton> m_xRbAsText; + + std::unique_ptr<weld::Frame> m_xHeadFrame; + + std::unique_ptr<weld::TreeView> m_xLbTableDbColumn; + std::unique_ptr<weld::TreeView> m_xLbTextDbColumn; + + std::unique_ptr<weld::Frame> m_xFormatFrame; + std::unique_ptr<weld::RadioButton> m_xRbDbFormatFromDb; + std::unique_ptr<weld::RadioButton> m_xRbDbFormatFromUsr; + std::unique_ptr<NumFormatListBox> m_xLbDbFormatFromUsr; + + // Page Text/Field + std::unique_ptr<weld::Button> m_xIbDbcolToEdit; + std::unique_ptr<weld::TextView> m_xEdDbText; + std::unique_ptr<weld::Label> m_xFtDbParaColl; + std::unique_ptr<weld::ComboBox> m_xLbDbParaColl; + + // Page Table + std::unique_ptr<weld::Button> m_xIbDbcolAllTo; + std::unique_ptr<weld::Button> m_xIbDbcolOneTo; + std::unique_ptr<weld::Button> m_xIbDbcolOneFrom; + std::unique_ptr<weld::Button> m_xIbDbcolAllFrom; + std::unique_ptr<weld::Label> m_xFtTableCol; + std::unique_ptr<weld::TreeView> m_xLbTableCol; + std::unique_ptr<weld::CheckButton> m_xCbTableHeadon; + std::unique_ptr<weld::RadioButton> m_xRbHeadlColnms; + std::unique_ptr<weld::RadioButton> m_xRbHeadlEmpty; + std::unique_ptr<weld::Button> m_xPbTableFormat; + std::unique_ptr<weld::Button> m_xPbTableAutofmt; + + DECL_LINK( PageHdl, weld::Button&, void ); + DECL_LINK( AutoFormatHdl, weld::Button&, void ); + DECL_LINK( TableFormatHdl, weld::Button&, void ); + DECL_LINK( DBFormatHdl, weld::Button&, void ); + DECL_LINK( TableToFromHdl, weld::Button&, void ); + DECL_LINK( TVSelectHdl, weld::TreeView&, void ); + DECL_LINK( CBSelectHdl, weld::ComboBox&, void ); + DECL_LINK( DblClickHdl, weld::TreeView&, bool ); + DECL_LINK( HeaderHdl, weld::Button&, void ); + + bool SplitTextToColArr( const OUString& rText, DB_Columns& rColArr, bool bInsField ); + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void ImplCommit() override; + void Load(); + + // set the tables - properties + void SetTabSet(); + +public: + SwInsertDBColAutoPilot( SwView& rView, + css::uno::Reference< css::sdbc::XDataSource> const & rxSource, + css::uno::Reference<css::sdbcx::XColumnsSupplier> const & xColSupp, + const SwDBData& rData ); + + virtual ~SwInsertDBColAutoPilot() override; + + void DataToDoc( const css::uno::Sequence< css::uno::Any >& rSelection, + css::uno::Reference< css::sdbc::XDataSource> const & rxSource, + css::uno::Reference< css::sdbc::XConnection> const & xConnection, + css::uno::Reference< css::sdbc::XResultSet > const & xResultSet); + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/dbtree.hxx b/sw/source/uibase/inc/dbtree.hxx new file mode 100644 index 000000000..e3c994d44 --- /dev/null +++ b/sw/source/uibase/inc/dbtree.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_DBTREE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DBTREE_HXX + +#include <memory> +#include <vcl/weld.hxx> + +#include <swdllapi.h> + +class SwDBTreeList_Impl; +class SwWrtShell; + +class SW_DLLPUBLIC SwDBTreeList +{ + bool bInitialized; + bool bShowColumns; + + rtl::Reference<SwDBTreeList_Impl> pImpl; + std::unique_ptr<weld::TreeView> m_xTreeView; + + DECL_DLLPRIVATE_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool); + SAL_DLLPRIVATE void InitTreeList(); + +public: + SwDBTreeList(std::unique_ptr<weld::TreeView> xTreeView); + ~SwDBTreeList(); + + OUString GetDBName(OUString& rTableName, OUString& rColumnName, sal_Bool* pbIsTable = nullptr); + + void Select( const OUString& rDBName, const OUString& rTableName, + const OUString& rColumnName ); + + void ShowColumns(bool bShowCol); + + void SetWrtShell(SwWrtShell& rSh); + + void AddDataSource(const OUString& rSource); + + void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xTreeView->connect_changed(rLink); } + void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { m_xTreeView->connect_row_activated(rLink); } + std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pOrig = nullptr) const { return m_xTreeView->make_iterator(pOrig); } + bool get_selected(weld::TreeIter* pIter) const { return m_xTreeView->get_selected(pIter); } + bool iter_parent(weld::TreeIter& rIter) const { return m_xTreeView->iter_parent(rIter); } + int get_iter_depth(const weld::TreeIter& rIter) const { return m_xTreeView->get_iter_depth(rIter); } + void set_size_request(int nWidth, int nHeight) { m_xTreeView->set_size_request(nWidth, nHeight); } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/dbui.hxx b/sw/source/uibase/inc/dbui.hxx new file mode 100644 index 000000000..38ace352a --- /dev/null +++ b/sw/source/uibase/inc/dbui.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_UIBASE_INC_DBUI_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DBUI_HXX + +#include <vcl/weld.hxx> + +#include <swdllapi.h> + +class PrintMonitor: public weld::GenericDialogController +{ +public: + std::unique_ptr<weld::Label> m_xDocName; + std::unique_ptr<weld::Label> m_xPrinter; + std::unique_ptr<weld::Label> m_xPrintInfo; + + PrintMonitor(weld::Window *pParent); + virtual ~PrintMonitor() override; +}; + +class SW_DLLPUBLIC SaveMonitor : public weld::GenericDialogController +{ +public: + std::unique_ptr<weld::Label> m_xDocName; + std::unique_ptr<weld::Label> m_xPrinter; + std::unique_ptr<weld::Label> m_xPrintInfo; + + SaveMonitor(weld::Window *pParent); + virtual ~SaveMonitor() override; +}; + +class CreateMonitor : public weld::GenericDialogController +{ +public: + CreateMonitor(weld::Window *pParent); + virtual ~CreateMonitor() override; + + void SetTotalCount( sal_Int32 nTotal ); + void SetCurrentPosition( sal_Int32 nCurrent ); + +private: + void UpdateCountingText(); + +private: + OUString m_sCountingPattern; + sal_Int32 m_nTotalCount; + sal_Int32 m_nCurrentPosition; + + std::unique_ptr<weld::Label> m_xCounting; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/docfnote.hxx b/sw/source/uibase/inc/docfnote.hxx new file mode 100644 index 000000000..521e2114a --- /dev/null +++ b/sw/source/uibase/inc/docfnote.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_UIBASE_INC_DOCFNOTE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DOCFNOTE_HXX + +#include <sfx2/tabdlg.hxx> + +class SwWrtShell; + +class SwFootNoteOptionDlg : public SfxTabDialogController +{ + SwWrtShell &rSh; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + + DECL_LINK(OkHdl, weld::Button&, void); + +public: + SwFootNoteOptionDlg(weld::Window *pParent, SwWrtShell &rSh); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/docstdlg.hxx b/sw/source/uibase/inc/docstdlg.hxx new file mode 100644 index 000000000..56119d057 --- /dev/null +++ b/sw/source/uibase/inc/docstdlg.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_UIBASE_INC_DOCSTDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DOCSTDLG_HXX + +#include <sfx2/tabdlg.hxx> +#include <docstat.hxx> + +// DocInfo now as page +class SwDocStatPage final : public SfxTabPage +{ +public: + SwDocStatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwDocStatPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + +private: + virtual bool FillItemSet( SfxItemSet *rSet) override; + virtual void Reset (const SfxItemSet *rSet) override; + + DECL_LINK(UpdateHdl, weld::Button&, void); + + std::unique_ptr<weld::Label> m_xPageNo; + std::unique_ptr<weld::Label> m_xTableNo; + std::unique_ptr<weld::Label> m_xGrfNo; + std::unique_ptr<weld::Label> m_xOLENo; + std::unique_ptr<weld::Label> m_xParaNo; + std::unique_ptr<weld::Label> m_xWordNo; + std::unique_ptr<weld::Label> m_xCharNo; + std::unique_ptr<weld::Label> m_xCharExclSpacesNo; + std::unique_ptr<weld::Label> m_xLineLbl; + std::unique_ptr<weld::Label> m_xLineNo; + std::unique_ptr<weld::Button> m_xUpdatePB; + + SwDocStat aDocStat; + + void Update(); + + void SetData(const SwDocStat &rStat); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drawbase.hxx b/sw/source/uibase/inc/drawbase.hxx new file mode 100644 index 000000000..6aeb4455a --- /dev/null +++ b/sw/source/uibase/inc/drawbase.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_UIBASE_INC_DRAWBASE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRAWBASE_HXX + +#include <tools/gen.hxx> +#include <vcl/vclptr.hxx> + +class SwView; +class SwWrtShell; +class SwEditWin; +class KeyEvent; +class MouseEvent; + +// base class for all functions +class SwDrawBase +{ +protected: + SwView* m_pView; + SwWrtShell* m_pSh; + VclPtr<SwEditWin> m_pWin; + Point m_aStartPos; // position of BeginCreate + sal_uInt16 m_nSlotId; + bool m_bCreateObj :1; + bool m_bInsForm :1; + + Point GetDefaultCenterPos() const; +public: + SwDrawBase(SwWrtShell *pSh, SwEditWin* pWin, SwView* pView); + virtual ~SwDrawBase(); + + void SetDrawPointer(); + void EnterSelectMode(const MouseEvent& rMEvt); + bool IsInsertForm() const { return m_bInsForm; } + bool IsCreateObj() const { return m_bCreateObj; } + + // mouse- & key events; return value=true: event was edited + bool MouseMove(const MouseEvent& rMEvt); + virtual bool MouseButtonUp(const MouseEvent& rMEvt); + virtual bool MouseButtonDown(const MouseEvent& rMEvt); + + void BreakCreate(); + void SetSlotId(sal_uInt16 nSlot) {m_nSlotId = nSlot;} + sal_uInt16 GetSlotId() const { return m_nSlotId;} + + virtual void Activate(const sal_uInt16 nSlotId); // activate function + virtual void Deactivate(); // deactivate function + + virtual void CreateDefaultObject(); + + // #i33136# + virtual bool doConstructOrthogonal() const; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_DRAWBASE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drawsh.hxx b/sw/source/uibase/inc/drawsh.hxx new file mode 100644 index 000000000..b204a39d9 --- /dev/null +++ b/sw/source/uibase/inc/drawsh.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_DRAWSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRAWSH_HXX + +#include "drwbassh.hxx" + +#include <svx/svdobj.hxx> + +class SwDrawBaseShell; + +class SwDrawShell: public SwDrawBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_DRAWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwDrawShell(SwView &rView); + + void Execute(SfxRequest &); + void GetState(SfxItemSet &); + void ExecDrawDlg(SfxRequest& rReq); + void ExecDrawAttrArgs(SfxRequest const & rReq); + void GetDrawAttrState(SfxItemSet &rSet); + + void ExecFormText(SfxRequest const & rReq); + void GetFormTextState(SfxItemSet& rSet); + + // #i123922# added helper methods to handle applying graphic data to draw objects + SdrObject* IsSingleFillableNonOLESelected(); + void InsertPictureFromFile(SdrObject& rObject); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drformsh.hxx b/sw/source/uibase/inc/drformsh.hxx new file mode 100644 index 000000000..9c8602d14 --- /dev/null +++ b/sw/source/uibase/inc/drformsh.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_DRFORMSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRFORMSH_HXX + +#include "drwbassh.hxx" + +class SwDrawBaseShell; + +class SwDrawFormShell: public SwDrawBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_DRAWFORMSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwDrawFormShell(SwView &rView); + virtual ~SwDrawFormShell() override; + + void Execute(SfxRequest const &); + void GetState(SfxItemSet &); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drpcps.hxx b/sw/source/uibase/inc/drpcps.hxx new file mode 100644 index 000000000..2ff081427 --- /dev/null +++ b/sw/source/uibase/inc/drpcps.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_UIBASE_INC_DRPCPS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRPCPS_HXX + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <editeng/svxfont.hxx> +#include <sfx2/basedlgs.hxx> +#include <sfx2/tabdlg.hxx> +#include <vcl/customweld.hxx> +#include <vcl/print.hxx> + +class SwWrtShell; + +class SwDropCapsDlg : public SfxSingleTabDialogController +{ +public: + SwDropCapsDlg(weld::Window *pParent, const SfxItemSet &rSet); +}; + +class SwDropCapsPage; + +class SwDropCapsPict : public weld::CustomWidgetController +{ + SwDropCapsPage* mpPage; + OUString maText; + OUString maScriptText; + Color maBackColor; + Color maTextLineColor; + sal_uInt8 mnLines; + long mnTotLineH; + long mnLineH; + long mnTextH; + sal_uInt16 mnDistance; + VclPtr<Printer> mpPrinter; + bool mbDelPrinter; + /// The ScriptInfo structure holds information on where we change from one + /// script to another. + struct ScriptInfo + { + sal_uLong textWidth; ///< Physical width of this segment. + sal_uInt16 scriptType; ///< Script type (e.g. Latin, Asian, Complex) + sal_Int32 changePos; ///< Character position where the script changes. + ScriptInfo(sal_uInt16 scrptType, sal_Int32 position) + : textWidth(0), scriptType(scrptType), changePos(position) {} + }; + std::vector<ScriptInfo> maScriptChanges; + SvxFont maFont; + SvxFont maCJKFont; + SvxFont maCTLFont; + Size maTextSize; + css::uno::Reference< css::i18n::XBreakIterator > xBreak; + + virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle &rRect) override; + void CheckScript(); + Size CalcTextSize(); + inline void InitPrinter(); + void InitPrinter_(); + static void GetFontSettings( const SwDropCapsPage& _rPage, vcl::Font& _rFont, sal_uInt16 _nWhich ); + void GetFirstScriptSegment(sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType); + bool GetNextScriptSegment(size_t &nIdx, sal_Int32 &start, sal_Int32 &end, sal_uInt16 &scriptType); + +public: + + SwDropCapsPict() + : mpPage(nullptr) + , mnLines(0) + , mnTotLineH(0) + , mnLineH(0) + , mnTextH(0) + , mnDistance(0) + , mpPrinter(nullptr) + , mbDelPrinter(false) + {} + + void SetDropCapsPage(SwDropCapsPage* pPage) { mpPage = pPage; } + + virtual ~SwDropCapsPict() override; + + void UpdatePaintSettings(); // also invalidates control! + + virtual void Resize() override; + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + void SetText( const OUString& rT ); + void SetLines( sal_uInt8 nL ); + void SetDistance( sal_uInt16 nD ); + void SetValues( const OUString& rText, sal_uInt8 nLines, sal_uInt16 nDistance ); + + void DrawPrev(vcl::RenderContext& rRenderContext, const Point& rPt); +}; + +class SwDropCapsPage : public SfxTabPage +{ +friend class SwDropCapsPict; + SwDropCapsPict m_aPict; + + bool bModified; + bool bFormat; + bool bHtmlMode; + + SwWrtShell &rSh; + + std::unique_ptr<weld::CheckButton> m_xDropCapsBox; + std::unique_ptr<weld::CheckButton> m_xWholeWordCB; + std::unique_ptr<weld::Label> m_xSwitchText; + std::unique_ptr<weld::SpinButton> m_xDropCapsField; + std::unique_ptr<weld::Label> m_xLinesText; + std::unique_ptr<weld::SpinButton> m_xLinesField; + std::unique_ptr<weld::Label> m_xDistanceText; + std::unique_ptr<weld::MetricSpinButton> m_xDistanceField; + std::unique_ptr<weld::Label> m_xTextText; + std::unique_ptr<weld::Entry> m_xTextEdit; + std::unique_ptr<weld::Label> m_xTemplateText; + std::unique_ptr<weld::ComboBox> m_xTemplateBox; + std::unique_ptr<weld::CustomWeld> m_xPict; + + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + void FillSet( SfxItemSet &rSet ); + + void ModifyEntry(weld::Entry& rEdit); + + DECL_LINK(ClickHdl, weld::ToggleButton&, void); + DECL_LINK(MetricValueChangedHdl, weld::MetricSpinButton&, void); + DECL_LINK(ValueChangedHdl, weld::SpinButton&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(WholeWordHdl, weld::ToggleButton&, void); + + static const sal_uInt16 aPageRg[]; + +public: + SwDropCapsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwDropCapsPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges() { return aPageRg; } + + + virtual bool FillItemSet( SfxItemSet *rSet) override; + virtual void Reset (const SfxItemSet *rSet) override; + + void SetFormat(bool bSet){bFormat = bSet;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drwbassh.hxx b/sw/source/uibase/inc/drwbassh.hxx new file mode 100644 index 000000000..e75576dec --- /dev/null +++ b/sw/source/uibase/inc/drwbassh.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_UIBASE_INC_DRWBASSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRWBASSH_HXX + +#include "basesh.hxx" + +class SwView; +class SfxItemSet; +class SwDrawBase; +class AbstractSvxObjectNameDialog; +struct SvxSwFrameValidation; + +class SwDrawBaseShell: public SwBaseShell +{ + DECL_LINK( CheckGroupShapeNameHdl, AbstractSvxObjectNameDialog&, bool ); + DECL_LINK(ValidatePosition, SvxSwFrameValidation&, void ); +public: + SwDrawBaseShell(SwView &rShell); + virtual ~SwDrawBaseShell() override; + + SFX_DECL_INTERFACE(SW_DRAWBASESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void Execute(SfxRequest const &); + void GetState(SfxItemSet &); + void GetDrawAttrStateForIFBX( SfxItemSet& rSet ); + void DisableState(SfxItemSet &rSet); + bool Disable(SfxItemSet& rSet, sal_uInt16 nWhich = 0); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/drwtxtsh.hxx b/sw/source/uibase/inc/drwtxtsh.hxx new file mode 100644 index 000000000..47a9fbd44 --- /dev/null +++ b/sw/source/uibase/inc/drwtxtsh.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_DRWTXTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DRWTXTSH_HXX + +#include <sfx2/shell.hxx> +#include <shellid.hxx> +#include <unotools/caserotate.hxx> + +class SdrView; +class SwView; +class SwWrtShell; + +class SwDrawTextShell: public SfxShell +{ + SwView &rView; + RotateTransliteration m_aRotateCase; + + SdrView *pSdrView; + + void SetAttrToMarked(const SfxItemSet& rAttr); + void InsertSymbol(SfxRequest& rReq); + bool IsTextEdit() const; + +public: + SFX_DECL_INTERFACE(SW_DRWTXTSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwView &GetView() { return rView; } + SwWrtShell &GetShell(); + + SwDrawTextShell(SwView &rView); + virtual ~SwDrawTextShell() override; + + virtual SfxUndoManager* + GetUndoManager() override; + + static void StateDisableItems(SfxItemSet &); + + void Execute(SfxRequest &); + void ExecDraw(SfxRequest &); + void GetStatePropPanelAttr(SfxItemSet &); + void GetState(SfxItemSet &); + void GetDrawTextCtrlState(SfxItemSet&); + + void ExecFontWork(SfxRequest const & rReq); + void StateFontWork(SfxItemSet& rSet); + void ExecFormText(SfxRequest const & rReq); + void GetFormTextState(SfxItemSet& rSet); + void ExecDrawLingu(SfxRequest const &rReq); + void ExecUndo(SfxRequest &rReq); + void StateUndo(SfxItemSet &rSet); + void ExecClpbrd(SfxRequest const &rReq); + void StateClpbrd(SfxItemSet &rSet); + void StateInsert(SfxItemSet &rSet); + void ExecTransliteration(SfxRequest const &); + void ExecRotateTransliteration(SfxRequest const &); + + void Init(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/dselect.hxx b/sw/source/uibase/inc/dselect.hxx new file mode 100644 index 000000000..cac7e5ce4 --- /dev/null +++ b/sw/source/uibase/inc/dselect.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_UIBASE_INC_DSELECT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_DSELECT_HXX + +#include "drawbase.hxx" + +// draw rectangle +class DrawSelection : public SwDrawBase +{ + public: + DrawSelection(SwWrtShell* pSh, SwEditWin* pWin, SwView* pView); + + virtual void Activate(const sal_uInt16 nSlotId) override; // activate function +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_DSELECT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/edtdd.hxx b/sw/source/uibase/inc/edtdd.hxx new file mode 100644 index 000000000..4416bc73c --- /dev/null +++ b/sw/source/uibase/inc/edtdd.hxx @@ -0,0 +1,19 @@ +/* -*- 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_UIBASE_INC_EDTDD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_EDTDD_HXX + +#include <sal/config.h> + +extern bool g_bExecuteDrag; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/inc/edtwin.hxx b/sw/source/uibase/inc/edtwin.hxx new file mode 100644 index 000000000..3059ee609 --- /dev/null +++ b/sw/source/uibase/inc/edtwin.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_EDTWIN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_EDTWIN_HXX + +#include <o3tl/deleter.hxx> +#include <sot/exchange.hxx> +#include <svx/svdobj.hxx> +#include <tools/link.hxx> +#include <vcl/timer.hxx> +#include <vcl/window.hxx> +#include <vcl/transfer.hxx> +#include <swevent.hxx> +#include <swtypes.hxx> + +class SwWrtShell; +class SwView; +class SwRect; +class SwViewShell; +class SwAnchorMarker; +class SdrObject; +class SwShadowCursor; +class DataChangedEvent; +class SvxAutoCorrCfg; +class SvxAutoCorrect; +class SwPaM; +struct SwApplyTemplate; +struct QuickHelpData; +class SdrDropMarkerOverlay; +class SwFrameControlsManager; +enum class SdrHitKind; + +// input window + +/** Window class for the Writer edit area, this is the one handling mouse + and keyboard events and doing the final painting of the document from + the buffered layout. + + To translate the pixel positions from the buffer OutputDevice to the real + pixel positions, use the PixelToLogic methods of this class. + */ +class SW_DLLPUBLIC SwEditWin final : public vcl::Window, + public DropTargetHelper, public DragSourceHelper +{ + static QuickHelpData* m_pQuickHlpData; + + static long m_nDDStartPosX; + static long m_nDDStartPosY; + + Color m_aWaterCanTextColor; // text color; for the watering can + Color m_aWaterCanTextBackColor; // text background; for the watering can + + /* + * timer and handler for scrolling on when the mousepointer + * stops outside of EditWin during a drag-operation. + * The selection is increased towards the mouse position + * regularly. + */ + AutoTimer m_aTimer; + // timer for ANY-KeyInput question without a following KeyInputEvent + Timer m_aKeyInputFlushTimer; + + OUString m_aInBuffer; + LanguageType m_eBufferLanguage; + Point m_aStartPos; + Point m_aMovePos; + Point m_aRszMvHdlPt; + Timer m_aTemplateTimer; + + // type/object where the mouse pointer is + SwCallMouseEvent m_aSaveCallEvent; + + std::unique_ptr<SwApplyTemplate> m_pApplyTempl; + std::unique_ptr<SwAnchorMarker> m_pAnchorMarker; // for relocating the anchor + + std::unique_ptr<SdrDropMarkerOverlay> m_pUserMarker; + SdrObject *m_pUserMarkerObj; + std::unique_ptr<SwShadowCursor, o3tl::default_delete<SwShadowCursor>> m_pShadCursor; + std::optional<Point> m_xRowColumnSelectionStart; // save position where table row/column selection has been started + + SwView &m_rView; + + SdrHitKind m_aActHitType; // current mouse pointer + + SotClipboardFormatId m_nDropFormat; // format from the last QueryDrop + sal_uInt8 m_nDropAction; // action from the last QueryDrop + SotExchangeDest m_nDropDestination; // destination from the last QueryDrop + + sal_uInt16 m_eBezierMode; + sal_uInt16 m_nInsFrameColCount; // column number for interactive frame + SdrObjKind m_eDrawMode; + bool m_bMBPressed : 1, + m_bInsDraw : 1, + m_bInsFrame : 1, + m_bIsInMove : 1, + m_bIsInDrag : 1, // don't execute StartExecuteDrag twice + m_bOldIdle : 1, // to stop to idle + m_bOldIdleSet : 1, // during QeueryDrop + m_bChainMode : 1, // connect frames + m_bWasShdwCursor : 1, // ShadowCursor was on in MouseButtonDown + m_bLockInput : 1, // lock while calc panel is active + m_bIsRowDrag : 1, //selection of rows is used, in combination with m_pRowColumnSelectionStart + /** #i42732# display status of font size/name depending on either the input language or the + selection position depending on what has changed lately + */ + m_bUseInputLanguage: 1, + m_bObjectSelect : 1; + + sal_uInt16 m_nKS_NUMDOWN_Count; // #i23725# + sal_uInt16 m_nKS_NUMINDENTINC_Count; + + std::unique_ptr<SwFrameControlsManager> m_pFrameControlsManager; + + void LeaveArea(const Point &); + void JustifyAreaTimer(); + inline void EnterArea(); + + void RstMBDownFlags(); + + void ChangeFly( sal_uInt8 nDir, bool bWeb ); + void ChangeDrawing( sal_uInt8 nDir ); + + bool EnterDrawMode(const MouseEvent& rMEvt, const Point& aDocPos); + bool RulerColumnDrag( const MouseEvent& rMEvt, bool bVerticalMode); + + // helper function for D&D + void DropCleanup(); + void CleanupDropUserMarker(); + SotExchangeDest GetDropDestination( const Point& rPixPnt, + SdrObject ** ppObj = nullptr ); + //select the object/cursor at the mouse position of the context menu request + void SelectMenuPosition(SwWrtShell& rSh, const Point& rMousePos ); + + /* + * handler for scrolling on when the mousepointer + * stops outside of EditWin during a drag-operation. + * The selection is regularly increased towards the mouse + * position. + */ + DECL_LINK( TimerHandler, Timer *, void ); + void StartDDTimer(); + void StopDDTimer(SwWrtShell *, const Point &); + DECL_LINK( DDHandler, Timer *, void ); + + // timer for ANY-KeyInut question without a following KeyInputEvent + DECL_LINK( KeyInputFlushHandler, Timer *, void ); + + // timer for ApplyTemplates via mouse (in disguise Drag&Drop) + DECL_LINK( TemplateTimerHdl, Timer *, void ); + + void MoveCursor( SwWrtShell &rSh, const Point& rDocPos, + const bool bOnlyText, bool bLockView ); + + virtual void DataChanged( const DataChangedEvent& ) override; + virtual void PrePaint(vcl::RenderContext& rRenderContext) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + + virtual void GetFocus() override; + virtual void LoseFocus() override; + + bool changeMousePointer(Point const & rDocPoint); + + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void RequestHelp(const HelpEvent& rEvt) override; + + // Drag & Drop Interface + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override; + + virtual OUString GetSurroundingText() const override; + virtual Selection GetSurroundingTextSelection() const override; + + void ShowAutoCorrectQuickHelp(const OUString& rWord, SvxAutoCorrect& rACorr); + bool ShowAutoText(const std::vector<OUString>& rChunkCandidates); + + /// Returns true if in header/footer area, or in the header/footer control. + bool IsInHeaderFooter( const Point &rDocPt, FrameControlType &rControl ) const; + + bool IsOverHeaderFooterFly( const Point& rDocPos, FrameControlType& rControl, bool& bOverFly, bool& bPageAnchored ) const; + +public: + virtual void KeyInput(const KeyEvent &rKEvt) override; + void UpdatePointer(const Point &, sal_uInt16 nButtons = 0); + + bool IsDrawSelMode() const; + bool IsDrawAction() const { return m_bInsDraw; } + void SetDrawAction(bool bFlag) { m_bInsDraw = bFlag; } + + void SetObjectSelect( bool bVal ) { m_bObjectSelect = bVal; } + bool IsObjectSelect() const { return m_bObjectSelect; } + + SdrObjKind GetSdrDrawMode() const { return m_eDrawMode; } + void SetSdrDrawMode( SdrObjKind eSdrObjectKind ) { m_eDrawMode = eSdrObjectKind; SetObjectSelect( false ); } + void StdDrawMode( SdrObjKind eSdrObjectKind, bool bObjSelect ); + + bool IsFrameAction() const { return m_bInsFrame; } + sal_uInt16 GetBezierMode() const { return m_eBezierMode; } + void SetBezierMode(sal_uInt16 eBezMode) { m_eBezierMode = eBezMode; } + void EnterDrawTextMode(const Point& aDocPos); // turn on DrawTextEditMode + void InsFrame(sal_uInt16 nCols); + void StopInsFrame(); + sal_uInt16 GetFrameColCount() const {return m_nInsFrameColCount;} // column number for interactive frame + + void SetChainMode( bool bOn ); + bool IsChainMode() const { return m_bChainMode; } + + void FlushInBuffer(); + static bool IsInputSequenceCheckingRequired( const OUString &rText, const SwPaM& rCursor ); + + void SetApplyTemplate(const SwApplyTemplate &); + SwApplyTemplate* GetApplyTemplate() const { return m_pApplyTempl.get(); } + + void StartExecuteDrag(); + void DragFinished(); + + void SetWaterCanTextColor(const Color& rCol ) { m_aWaterCanTextColor = rCol; } + + void SetWaterCanTextBackColor(const Color& rCol ) { m_aWaterCanTextBackColor = rCol; } + + void LockKeyInput(bool bSet){m_bLockInput = bSet;} + + const SwView &GetView() const { return m_rView; } + SwView &GetView() { return m_rView; } + + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override; + + static long GetDDStartPosX() { return m_nDDStartPosX; } + static long GetDDStartPosY() { return m_nDDStartPosY; } + + static void InitStaticData(); + static void FinitStaticData(); + + //#i3370# remove quick help to prevent saving of autocorrection suggestions + void StopQuickHelp(); + + // #i42921# - add parameter <bVerticalMode> + bool RulerMarginDrag( const MouseEvent& rMEvt, + const bool bVerticalMode ); + + /** #i42732# display status of font size/name depending on either the input + language or the selection position depending on what has changed lately + */ + void SetUseInputLanguage( bool bNew ); + bool IsUseInputLanguage() const { return m_bUseInputLanguage; } + + /** fdo#55546 cut very long tooltips to 2/3 of the width of the screen + via center ellipsis + */ + OUString ClipLongToolTip(const OUString& rText); + + SwFrameControlsManager& GetFrameControlsManager(); + + SwEditWin(vcl::Window *pParent, SwView &); + virtual ~SwEditWin() override; + virtual void dispose() override; + + virtual void Command( const CommandEvent& rCEvt ) override; + + /// @see Window::LogicInvalidate(). + void LogicInvalidate(const tools::Rectangle* pRectangle) override; + /// Same as MouseButtonDown(), but coordinates are in logic unit. + virtual void LogicMouseButtonDown(const MouseEvent& rMouseEvent) override; + /// Same as MouseButtonUp(), but coordinates are in logic unit. + virtual void LogicMouseButtonUp(const MouseEvent& rMouseEvent) override; + /// Same as MouseMove(), but coordinates are in logic unit. + virtual void LogicMouseMove(const MouseEvent& rMouseEvent) override; + /// Allows adjusting the point or mark of the selection to a document coordinate. + void SetCursorTwipPosition(const Point& rPosition, bool bPoint, bool bClearMark); + /// Allows starting or ending a graphic move or resize action. + void SetGraphicTwipPosition(bool bStart, const Point& rPosition); + + virtual FactoryFunction GetUITestFactory() const override; +}; + +extern bool g_bFrameDrag; +extern bool g_bDDTimerStarted; +extern bool g_bFlushCharBuffer; +extern bool g_bDDINetAttr; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/envimg.hxx b/sw/source/uibase/inc/envimg.hxx new file mode 100644 index 000000000..b63d6b0a5 --- /dev/null +++ b/sw/source/uibase/inc/envimg.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_UIBASE_INC_ENVIMG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ENVIMG_HXX + +#include <svl/poolitem.hxx> +#include <unotools/configitem.hxx> +#include <swdllapi.h> + +SW_DLLPUBLIC OUString MakeSender(); + +enum SwEnvAlign +{ + ENV_HOR_LEFT = 0, + ENV_HOR_CNTR, + ENV_HOR_RGHT, + ENV_VER_LEFT, + ENV_VER_CNTR, + ENV_VER_RGHT +}; + +class SW_DLLPUBLIC SwEnvItem : public SfxPoolItem +{ +public: + + OUString m_aAddrText; // text for receiver + bool m_bSend; // sender? + OUString m_aSendText; // text for sender + sal_Int32 m_nAddrFromLeft; // left gap for receiver (twips) + sal_Int32 m_nAddrFromTop; // upper gap for receiver (twips) + sal_Int32 m_nSendFromLeft; // left gap for sender (twips) + sal_Int32 m_nSendFromTop; // upper gap for sender (twips) + sal_Int32 m_nWidth; // envelope's width (twips) + sal_Int32 m_nHeight; // envelope's height (twips) + SwEnvAlign m_eAlign; // alignment at indent + bool m_bPrintFromAbove; // print from above? + sal_Int32 m_nShiftRight; // shift to right (twips) + sal_Int32 m_nShiftDown; // shift down (twips) + + SwEnvItem(); + + static SfxPoolItem* CreateDefault(); + SwEnvItem& operator =(const SwEnvItem& rItem); + SwEnvItem(SwEnvItem const &) = default; // SfxPoolItem copy function dichotomy + + virtual bool operator ==(const SfxPoolItem& rItem) const override; + + virtual SwEnvItem* Clone(SfxItemPool* = nullptr) const override; + virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override; + virtual bool PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) override; +}; + +class SwEnvCfgItem : public utl::ConfigItem +{ +private: + SwEnvItem m_aEnvItem; + + static css::uno::Sequence<OUString> GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwEnvCfgItem(); + virtual ~SwEnvCfgItem() override; + + SwEnvItem& GetItem() {return m_aEnvItem;} + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/envlop.hxx b/sw/source/uibase/inc/envlop.hxx new file mode 100644 index 000000000..7455d3579 --- /dev/null +++ b/sw/source/uibase/inc/envlop.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_ENVLOP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ENVLOP_HXX + +#include <sfx2/tabdlg.hxx> +#include <vcl/weld.hxx> +#include <vcl/customweld.hxx> +#include "envimg.hxx" + +#define GetFieldVal(rField) (rField).Denormalize((rField).GetValue(FieldUnit::TWIP)) +#define SetFieldVal(rField, lValue) (rField).SetValue((rField).Normalize(lValue), FieldUnit::TWIP) + +inline int getfieldval(const weld::MetricSpinButton& rField) +{ + return rField.denormalize(rField.get_value(FieldUnit::TWIP)); +} + +inline void setfieldval(weld::MetricSpinButton& rField, int lValue) +{ + rField.set_value(rField.normalize(lValue), FieldUnit::TWIP); +} + +class SwEnvDlg; +class SwEnvPage; +class SwEnvFormatPage; +class SwWrtShell; +class Printer; + +class SwEnvPreview : public weld::CustomWidgetController +{ +private: + SwEnvDlg* m_pDialog; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + +public: + SwEnvPreview(); + void SetDialog(SwEnvDlg* pDialog) { m_pDialog = pDialog; } +}; + +class SwEnvDlg : public SfxTabDialogController +{ +friend class SwEnvPage; +friend class SwEnvFormatPage; +friend class SwEnvPrtPage; +friend class SwEnvPreview; + + SwEnvItem aEnvItem; + SwWrtShell *pSh; + VclPtr<Printer> pPrinter; + std::unique_ptr<SfxItemSet> pAddresseeSet; + std::unique_ptr<SfxItemSet> pSenderSet; + + std::unique_ptr<weld::Button> m_xModify; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + virtual short Ok() override; + +public: + SwEnvDlg(weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell* pWrtSh, Printer* pPrt, bool bInsert); + virtual ~SwEnvDlg() override; +}; + +class SwEnvPage : public SfxTabPage +{ + SwEnvDlg* m_pDialog; + SwWrtShell* m_pSh; + OUString m_sActDBName; + + SwEnvPreview m_aPreview; + std::unique_ptr<weld::TextView> m_xAddrEdit; + std::unique_ptr<weld::ComboBox> m_xDatabaseLB; + std::unique_ptr<weld::ComboBox> m_xTableLB; + std::unique_ptr<weld::ComboBox> m_xDBFieldLB; + std::unique_ptr<weld::Button> m_xInsertBT; + std::unique_ptr<weld::CheckButton> m_xSenderBox; + std::unique_ptr<weld::TextView> m_xSenderEdit; + std::unique_ptr<weld::CustomWeld> m_xPreview; + + DECL_LINK(DatabaseHdl, weld::ComboBox&, void); + DECL_LINK(FieldHdl, weld::Button&, void); + DECL_LINK(SenderHdl, weld::Button&, void); + + void InitDatabaseBox(); + + SwEnvDlg* GetParentSwEnvDlg() { return m_pDialog; } + +public: + SwEnvPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + void Init(SwEnvDlg* pDialog); + virtual ~SwEnvPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override; + void FillItem(SwEnvItem& rItem); + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/fldedt.hxx b/sw/source/uibase/inc/fldedt.hxx new file mode 100644 index 000000000..9f512ea3f --- /dev/null +++ b/sw/source/uibase/inc/fldedt.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_UIBASE_INC_FLDEDT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FLDEDT_HXX + +#include <fldbas.hxx> + +#include <sfx2/basedlgs.hxx> + +class SwView; +class SwWrtShell; +class SwFieldMgr; + +class SwFieldEditDlg : public SfxSingleTabDialogController +{ + SwWrtShell* pSh; + std::unique_ptr<weld::Button> m_xPrevBT; + std::unique_ptr<weld::Button> m_xNextBT; + std::unique_ptr<weld::Button> m_xAddressBT; + + DECL_LINK(AddressHdl, weld::Button&, void); + DECL_LINK(NextPrevHdl, weld::Button&, void); + + void Init(); + SfxTabPage* CreatePage(sal_uInt16 nGroup); + + void EnsureSelection(SwField *pCurField, SwFieldMgr &rMgr); +public: + + SwFieldEditDlg(SwView const & rVw); + virtual ~SwFieldEditDlg() override; + + DECL_LINK(OKHdl, weld::Button&, void); + + virtual short run() override; + + void EnableInsert(bool bEnable); + void InsertHdl(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/fldmgr.hxx b/sw/source/uibase/inc/fldmgr.hxx new file mode 100644 index 000000000..9a701e2a4 --- /dev/null +++ b/sw/source/uibase/inc/fldmgr.hxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_FLDMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FLDMGR_HXX + +#include <fldbas.hxx> +#include <pam.hxx> +#include <swdllapi.h> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Any.h> +#include <memory> +#include <vector> + +namespace com::sun::star{ + namespace container{ + class XNameAccess; + } + namespace text{ + class XNumberingTypeInfo; + } +} + +class SwWrtShell; +class SwField; +class SwFieldType; +class SwPaM; +class SbModule; +class SvxMacroItem; +class SvNumberFormatter; +namespace vcl { class Window; } +namespace weld { class Widget; class Window; } +enum class SwFieldIds : sal_uInt16; + +// the groups of fields +enum SwFieldGroups +{ + GRP_DOC, + GRP_FKT, + GRP_REF, + GRP_REG, + GRP_DB, + GRP_VAR +}; + +struct SwFieldGroupRgn +{ + sal_uInt16 nStart; + sal_uInt16 nEnd; +}; + +// the field manager handles the insertation of fields +// with command strings +struct SwInsertField_Data +{ + SwFieldTypesEnum m_nTypeId; + sal_uInt16 m_nSubType; + const OUString m_sPar1; + const OUString m_sPar2; + sal_uInt32 m_nFormatId; + SwWrtShell* m_pSh; + sal_Unicode m_cSeparator; + bool m_bIsAutomaticLanguage; + css::uno::Any m_aDBDataSource; + css::uno::Any m_aDBConnection; + css::uno::Any m_aDBColumn; + weld::Widget* m_pParent; // parent widget used for SwWrtShell::StartInputFieldDlg() + /// Marks the PostIt field's annotation start/end if it differs from the cursor selection. + std::unique_ptr<SwPaM> m_pAnnotationRange; + + SwInsertField_Data(SwFieldTypesEnum nType, sal_uInt16 nSub, const OUString& rPar1, const OUString& rPar2, + sal_uInt32 nFormatId, SwWrtShell* pShell = nullptr, sal_Unicode cSep = ' ', bool bIsAutoLanguage = true) : + m_nTypeId(nType), + m_nSubType(nSub), + m_sPar1(rPar1), + m_sPar2(rPar2), + m_nFormatId(nFormatId), + m_pSh(pShell), + m_cSeparator(cSep), + m_bIsAutomaticLanguage(bIsAutoLanguage), + m_pParent(nullptr) {} +}; + +class SW_DLLPUBLIC SwFieldMgr +{ +private: + SwField* m_pCurField; + SwWrtShell* m_pWrtShell; // can be ZERO too! + OUString m_aCurPar1; + OUString m_aCurPar2; + OUString m_sCurFrame; + + OUString m_sMacroPath; + OUString m_sMacroName; + + sal_uInt32 m_nCurFormat; + bool m_bEvalExp; + + SAL_DLLPRIVATE LanguageType GetCurrLanguage() const; + + css::uno::Reference<css::text::XNumberingTypeInfo> m_xNumberingInfo; + SAL_DLLPRIVATE css::uno::Reference<css::text::XNumberingTypeInfo> const & GetNumberingInfo()const; + +public: + explicit SwFieldMgr(SwWrtShell* pSh = nullptr); + ~SwFieldMgr(); + + void SetWrtShell( SwWrtShell* pShell ) + { m_pWrtShell = pShell; } + + // insert field using TypeID (TYP_ ...) + bool InsertField( const SwInsertField_Data& rData ); + + // change the current field directly + void UpdateCurField(sal_uInt32 nFormat, + const OUString& rPar1, + const OUString& rPar2, + std::unique_ptr<SwField> _pField = nullptr); + + const OUString& GetCurFieldPar1() const { return m_aCurPar1; } + const OUString& GetCurFieldPar2() const { return m_aCurPar2; } + + // determine a field + SwField* GetCurField(); + + void InsertFieldType(SwFieldType const & rType); + + bool ChooseMacro(weld::Window* pDialogParent); + void SetMacroPath(const OUString& rPath); + const OUString& GetMacroPath() const { return m_sMacroPath; } + const OUString& GetMacroName() const { return m_sMacroName; } + + // previous and next of the same type + bool GoNextPrev( bool bNext = true, SwFieldType* pTyp = nullptr ); + bool GoNext() { return GoNextPrev(); } + bool GoPrev() { return GoNextPrev( false ); } + + bool IsDBNumeric(const OUString& rDBName, const OUString& rTableQryName, + bool bIsTable, const OUString& rFieldName); + + // organise RefMark with names + bool CanInsertRefMark( const OUString& rStr ); + + // access to field types via ResId + size_t GetFieldTypeCount() const; + SwFieldType* GetFieldType(SwFieldIds nResId, size_t nField = 0) const; + SwFieldType* GetFieldType(SwFieldIds nResId, const OUString& rName) const; + + void RemoveFieldType(SwFieldIds nResId, const OUString& rName); + + // access via TypeId from the dialog + // Ids for a range of fields + static const SwFieldGroupRgn& GetGroupRange(bool bHtmlMode, sal_uInt16 nGrpId); + static sal_uInt16 GetGroup(SwFieldTypesEnum nTypeId, sal_uInt16 nSubType); + + // the current field's TypeId + SwFieldTypesEnum GetCurTypeId() const; + + // TypeId for a concrete position in the list + static SwFieldTypesEnum GetTypeId(sal_uInt16 nPos); + // name of the type in the list of fields + static OUString GetTypeStr(sal_uInt16 nPos); + + // Pos in the list of fields + static sal_uInt16 GetPos(SwFieldTypesEnum nTypeId); + + // subtypes to a type + void GetSubTypes(SwFieldTypesEnum nId, std::vector<OUString>& rToFill); + + // format to a type + sal_uInt16 GetFormatCount(SwFieldTypesEnum nTypeId, bool bHtmlMode) const; + OUString GetFormatStr(SwFieldTypesEnum nTypeId, sal_uInt32 nFormatId) const; + sal_uInt16 GetFormatId(SwFieldTypesEnum nTypeId, sal_uInt32 nFormatId) const; + sal_uInt32 GetDefaultFormat(SwFieldTypesEnum nTypeId, bool bIsText, SvNumberFormatter* pFormatter); + + // turn off evaluation of expression fields for insertation + // of many expression fields (see labels) + + inline void SetEvalExpFields(bool bEval); + void EvalExpFields(SwWrtShell* pSh); +}; + +inline void SwFieldMgr::SetEvalExpFields(bool bEval) + { m_bEvalExp = bEval; } + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/fldtdlg.hxx b/sw/source/uibase/inc/fldtdlg.hxx new file mode 100644 index 000000000..159881ed1 --- /dev/null +++ b/sw/source/uibase/inc/fldtdlg.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_FLDTDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FLDTDLG_HXX +#include <sfx2/tabdlg.hxx> + +class SfxBindings; +class SfxTabPage; +class SwChildWinWrapper; +struct SfxChildWinInfo; + +class SwFieldDlg : public SfxTabDialogController +{ + SwChildWinWrapper* m_pChildWin; + SfxBindings* m_pBindings; + bool m_bHtmlMode; + bool m_bDataBaseMode; + bool m_bClosing; + + virtual SfxItemSet* CreateInputItemSet(const OString& rId) override; + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + + void ReInitTabPage(const OString& rPageId, + bool bOnlyActivate = false); + +public: + SwFieldDlg(SfxBindings* pB, SwChildWinWrapper* pCW, weld::Window *pParent); + virtual ~SwFieldDlg() override; + + DECL_LINK(OKHdl, weld::Button&, void); + DECL_LINK(CancelHdl, weld::Button&, void); + + void Initialize(SfxChildWinInfo const *pInfo); + void ReInitDlg(); + void EnableInsert(bool bEnable); + void InsertHdl(); + void ActivateDatabasePage(); + void ShowReferencePage(); + virtual void Close() override; + virtual void EndDialog() override; + virtual void Activate() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/fldwrap.hxx b/sw/source/uibase/inc/fldwrap.hxx new file mode 100644 index 000000000..6532ff7db --- /dev/null +++ b/sw/source/uibase/inc/fldwrap.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_UIBASE_INC_FLDWRAP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FLDWRAP_HXX + +#include <vcl/vclptr.hxx> + +#include <swabstdlg.hxx> + +#include "chldwrap.hxx" +class AbstractSwFieldDlg; + +class SwFieldDlgWrapper : public SwChildWinWrapper +{ + ScopedVclPtr<AbstractSwFieldDlg> pDlgInterface; +public: + SwFieldDlgWrapper(vcl::Window* pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(SwFieldDlgWrapper); + + virtual bool ReInitDlg(SwDocShell *pDocSh) override; + void ShowReferencePage(); +}; + +// field dialog only showing database page to support mail merge +class SwFieldDataOnlyDlgWrapper : public SwChildWinWrapper +{ + ScopedVclPtr<AbstractSwFieldDlg> pDlgInterface; +public: + SwFieldDataOnlyDlgWrapper(vcl::Window* pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW(SwFieldDataOnlyDlgWrapper); + + virtual bool ReInitDlg(SwDocShell *pDocSh) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/fontcfg.hxx b/sw/source/uibase/inc/fontcfg.hxx new file mode 100644 index 000000000..388f16d81 --- /dev/null +++ b/sw/source/uibase/inc/fontcfg.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_FONTCFG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FONTCFG_HXX + +#include <rtl/ustring.hxx> +#include <unotools/configitem.hxx> +#include <i18nlangtag/lang.h> +#include <swdllapi.h> + +#define FONT_STANDARD 0 +#define FONT_OUTLINE 1 +#define FONT_LIST 2 +#define FONT_CAPTION 3 +#define FONT_INDEX 4 +#define FONT_STANDARD_CJK 5 +#define FONT_OUTLINE_CJK 6 +#define FONT_LIST_CJK 7 +#define FONT_CAPTION_CJK 8 +#define FONT_INDEX_CJK 9 +#define FONT_STANDARD_CTL 10 +#define FONT_OUTLINE_CTL 11 +#define FONT_LIST_CTL 12 +#define FONT_CAPTION_CTL 13 +#define FONT_INDEX_CTL 14 +#define DEF_FONT_COUNT 15 + +#define FONT_PER_GROUP 5 + +#define FONT_GROUP_DEFAULT 0 +#define FONT_GROUP_CJK 1 +#define FONT_GROUP_CTL 2 + +//pt-size of fonts +#define FONTSIZE_DEFAULT 240 +#define FONTSIZE_CJK_DEFAULT 210 +#define FONTSIZE_OUTLINE 280 + +class SW_DLLPUBLIC SwStdFontConfig : public utl::ConfigItem +{ + OUString sDefaultFonts[DEF_FONT_COUNT]; + sal_Int32 nDefaultFontHeight[DEF_FONT_COUNT]; + + SAL_DLLPRIVATE static css::uno::Sequence<OUString> const & GetPropertyNames(); + + void ChangeString(sal_uInt16 nFontType, const OUString& rSet) + { + if(sDefaultFonts[nFontType] != rSet) + { + SetModified(); + sDefaultFonts[nFontType] = rSet; + } + } + + void ChangeInt( sal_uInt16 nFontType, sal_Int32 nHeight ); + + virtual void ImplCommit() override; + +public: + SwStdFontConfig(); + virtual ~SwStdFontConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + + const OUString& GetFontStandard(sal_uInt8 nFontGroup) const {return sDefaultFonts[FONT_STANDARD + FONT_PER_GROUP * nFontGroup];} + const OUString& GetFontOutline(sal_uInt8 nFontGroup) const {return sDefaultFonts[FONT_OUTLINE + FONT_PER_GROUP * nFontGroup];} + const OUString& GetFontList (sal_uInt8 nFontGroup) const {return sDefaultFonts[FONT_LIST + FONT_PER_GROUP * nFontGroup];} + const OUString& GetFontCaption(sal_uInt8 nFontGroup) const {return sDefaultFonts[FONT_CAPTION + FONT_PER_GROUP * nFontGroup];} + const OUString& GetFontIndex (sal_uInt8 nFontGroup) const {return sDefaultFonts[FONT_INDEX + FONT_PER_GROUP * nFontGroup];} + + const OUString& GetFontFor(sal_uInt16 nFontType) const {return sDefaultFonts[nFontType];} + bool IsFontDefault(sal_uInt16 nFontType) const; + + void SetFontStandard(const OUString& rSet, sal_uInt8 nFontGroup) + {ChangeString(FONT_STANDARD + FONT_PER_GROUP * nFontGroup, rSet);} + + void SetFontOutline(const OUString& rSet, sal_uInt8 nFontGroup) + { ChangeString(FONT_OUTLINE + FONT_PER_GROUP * nFontGroup, rSet);} + void SetFontList (const OUString& rSet, sal_uInt8 nFontGroup) + { ChangeString(FONT_LIST + FONT_PER_GROUP * nFontGroup, rSet);} + void SetFontCaption(const OUString& rSet, sal_uInt8 nFontGroup) + { ChangeString(FONT_CAPTION + FONT_PER_GROUP * nFontGroup, rSet);} + void SetFontIndex (const OUString& rSet, sal_uInt8 nFontGroup) + { ChangeString(FONT_INDEX + FONT_PER_GROUP * nFontGroup, rSet);} + + void SetFontHeight( sal_Int32 nHeight, sal_uInt8 nFont, sal_uInt8 nScriptType ) + { ChangeInt(nFont + FONT_PER_GROUP * nScriptType, nHeight);} + + sal_Int32 GetFontHeight( sal_uInt8 nFont, sal_uInt8 nScriptType, LanguageType eLang ); + + static OUString GetDefaultFor(sal_uInt16 nFontType, LanguageType eLang); + static sal_Int32 GetDefaultHeightFor(sal_uInt16 nFontType, LanguageType eLang); +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/formatclipboard.hxx b/sw/source/uibase/inc/formatclipboard.hxx new file mode 100644 index 000000000..262ecfc68 --- /dev/null +++ b/sw/source/uibase/inc/formatclipboard.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_UIBASE_INC_FORMATCLIPBOARD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FORMATCLIPBOARD_HXX + +#include <sal/config.h> + +#include <memory> + +#include "wrtsh.hxx" +#include <svl/itemset.hxx> +#include <svl/style.hxx> + +/** This class acts as data container and execution class for the format paintbrush feature in writer. +*/ + +class SwFormatClipboard +{ +public: + SwFormatClipboard(); + + /** + * Test if the object contains text or paragraph attribute + */ + bool HasContent() const; + bool HasContentForThisType( SelectionType nSelectionType ) const; + static bool CanCopyThisType( SelectionType nSelectionType ); + + /** + * Store/Backup the text and paragraph attribute of the current selection. + * + * @param bPersistentCopy + * input parameter - specify if the Paste function will erase the current object. + */ + void Copy( SwWrtShell& rWrtShell, SfxItemPool& rPool, bool bPersistentCopy ); + + /** + * Paste the stored text and paragraph attributes on the current selection and current paragraph. + * + * @param bNoCharacterFormats + * Do not paste the character formats. + * + * @param bNoParagraphFormats + * Do not paste the paragraph formats. + */ + void Paste( SwWrtShell& rWrtShell, SfxStyleSheetBasePool* pPool + , bool bNoCharacterFormats, bool bNoParagraphFormats ); + + /** + * Clear the currently stored text and paragraph attributes. + */ + void Erase(); + +private: + SelectionType m_nSelectionType; + + /** automatic/named character attribute set */ + std::unique_ptr<SfxItemSet> m_pItemSet_TextAttr; + /** automatic/named paragraph attribute set + * (it can be character attribute applied to the paragraph) */ + std::unique_ptr<SfxItemSet> m_pItemSet_ParAttr; + + /** table attribute set */ + std::unique_ptr<SfxItemSet> m_pTableItemSet; + + /** name of the character format (if it exist) */ + OUString m_aCharStyle; + /** name of the paragraph format (if it exist) */ + OUString m_aParaStyle; + //no frame style because it contains position information + + /** specify if the Paste function have to clear the current object */ + bool m_bPersistentCopy; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/frmdlg.hxx b/sw/source/uibase/inc/frmdlg.hxx new file mode 100644 index 000000000..e3c079a22 --- /dev/null +++ b/sw/source/uibase/inc/frmdlg.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_UIBASE_INC_FRMDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRMDLG_HXX + +#include <sfx2/tabdlg.hxx> +#include <sfx2/viewfrm.hxx> + +class SwWrtShell; + +// frame dialog +class SwFrameDlg : public SfxTabDialogController +{ + bool m_bFormat; + bool m_bNew; + const SfxItemSet& m_rSet; + OUString m_sDlgType; + SwWrtShell* m_pWrtShell; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + +public: + SwFrameDlg(SfxViewFrame const *pFrame, weld::Window* pParent, + const SfxItemSet& rCoreSet, + bool bNewFrame, + const OUString& sResType, + bool bFormat, + const OString& sDefPage = OString(), + const OUString* pFormatStr = nullptr); + + virtual ~SwFrameDlg() override; + + SwWrtShell* GetWrtShell() { return m_pWrtShell; } +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_FRMDLG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/frmmgr.hxx b/sw/source/uibase/inc/frmmgr.hxx new file mode 100644 index 000000000..8cd1ee303 --- /dev/null +++ b/sw/source/uibase/inc/frmmgr.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_UIBASE_INC_FRMMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRMMGR_HXX + +#include <swtypes.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <tools/gen.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtanchr.hxx> +#include <swdllapi.h> + +class SwWrtShell; +struct SvxSwFrameValidation; +struct SwPosition; + +class SwFormatCol; +class SvGlobalName; + +const SwTwips DFLT_WIDTH = MM50 * 4; +const SwTwips DFLT_HEIGHT = MM50; + +enum class Frmmgr_Type +{ + NONE = 0x00, + TEXT = 0x01, + GRF = 0x02, + OLE = 0x04, + ENVELP = 0x10 +}; + +class SW_DLLPUBLIC SwFlyFrameAttrMgr +{ + SfxItemSet m_aSet; + Point m_aAbsPos; + SwWrtShell* m_pOwnSh; + + bool m_bAbsPos, + m_bNewFrame; + bool m_bIsInVertical; + // #mongolianlayout# + bool m_bIsInVerticalL2R; + + // internal calculation for borders + SAL_DLLPRIVATE SwTwips CalcTopSpace(); + SAL_DLLPRIVATE SwTwips CalcBottomSpace(); + SAL_DLLPRIVATE SwTwips CalcLeftSpace(); + SAL_DLLPRIVATE SwTwips CalcRightSpace(); + + SAL_DLLPRIVATE void UpdateFlyFrame_(); // post-treatment after insert or update + +public: + SwFlyFrameAttrMgr( bool bNew, SwWrtShell* pSh, Frmmgr_Type nType, const SvGlobalName* pName ); + + //CopyCtor for dialogs to check the metrics + SwFlyFrameAttrMgr( bool bNew, SwWrtShell *pSh, const SfxItemSet &rSet ); + + void SetAnchor(RndStdIds eId); + inline RndStdIds GetAnchor() const; + + void SetHorzOrientation(sal_Int16 eOrient); + void SetVertOrientation(sal_Int16 eOrient); + + // absolute position + void SetAbsPos(const Point& rLPoint); + + // anchor's relative position + void SetPos(const Point& rLPoint); + inline Point GetPos() const; + + // size + void SetSize(const Size& rLSize); + inline const Size& GetSize() const; + + void SetHeightSizeType(SwFrameSize eType); + + // rotation + void SetRotation(sal_uInt16 nOld, sal_uInt16 nNew, const Size& rUnrotatedSize); + + // space to content + void SetLRSpace( long nLeft, + long nRight ); + void SetULSpace( long nTop, + long nBottom ); + + void SetCol( const SwFormatCol &rCol); + + // change and query attributes + void UpdateAttrMgr(); + void UpdateFlyFrame(); + + // create new frame + void InsertFlyFrame(); + void InsertFlyFrame(RndStdIds eAnchorType, + const Point &rPos, + const Size &rSize); + + // check and change metrics + void ValidateMetrics(SvxSwFrameValidation& rVal, + const SwPosition* pToCharContentPos, + bool bOnlyPercentRefValue = false); + + void DelAttr(sal_uInt16 nId); + + // reach out the set + const SfxItemSet &GetAttrSet() const { return m_aSet; } + SfxItemSet &GetAttrSet() { return m_aSet; } + void SetAttrSet(const SfxItemSet& rSet); + + inline const SwFormatVertOrient &GetVertOrient() const; + inline const SwFormatHoriOrient &GetHoriOrient() const; + inline const SvxShadowItem &GetShadow() const; + inline const SvxBoxItem &GetBox() const; + inline const SwFormatFrameSize &GetFrameSize() const; + + long CalcWidthBorder() { return CalcLeftSpace()+CalcRightSpace(); } + long CalcHeightBorder() { return CalcTopSpace()+CalcBottomSpace(); } +}; + +inline const Size& SwFlyFrameAttrMgr::GetSize() const +{ + return m_aSet.Get(RES_FRM_SIZE).GetSize(); +} + +inline const SwFormatVertOrient &SwFlyFrameAttrMgr::GetVertOrient() const +{ + return m_aSet.Get(RES_VERT_ORIENT); +} +inline const SwFormatHoriOrient &SwFlyFrameAttrMgr::GetHoriOrient() const +{ + return m_aSet.Get(RES_HORI_ORIENT); +} +inline const SwFormatFrameSize& SwFlyFrameAttrMgr::GetFrameSize() const +{ + return m_aSet.Get(RES_FRM_SIZE); +} +inline const SvxShadowItem &SwFlyFrameAttrMgr::GetShadow() const +{ + return m_aSet.Get(RES_SHADOW); +} +inline const SvxBoxItem &SwFlyFrameAttrMgr::GetBox() const +{ + return m_aSet.Get(RES_BOX); +} +inline Point SwFlyFrameAttrMgr::GetPos() const +{ + return Point( GetHoriOrient().GetPos(), GetVertOrient().GetPos() ); +} +inline RndStdIds SwFlyFrameAttrMgr::GetAnchor() const +{ + return m_aSet.Get(RES_ANCHOR).GetAnchorId(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/frmpage.hxx b/sw/source/uibase/inc/frmpage.hxx new file mode 100644 index 000000000..6aba31a44 --- /dev/null +++ b/sw/source/uibase/inc/frmpage.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_UIBASE_INC_FRMPAGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRMPAGE_HXX + +#include <sfx2/tabdlg.hxx> +#include <svx/dialcontrol.hxx> +#include <svx/frmdirlbox.hxx> +#include <svx/swframeexample.hxx> +#include <swtypes.hxx> +#include "bmpwin.hxx" +#include "prcntfld.hxx" + +namespace sfx2{class FileDialogHelper;} +class SwWrtShell; +struct FrameMap; +// OD 12.11.2003 #i22341# +struct SwPosition; + +// frame dialog +class SwFramePage: public SfxTabPage +{ + bool m_bAtHorzPosModified; + bool m_bAtVertPosModified; + + bool m_bFormat; + bool m_bNew; + bool m_bNoModifyHdl; + bool m_bIsVerticalFrame; //current frame is in vertical environment - strings are exchanged + // #mongolianlayout# + bool m_bIsVerticalL2R; + bool m_bIsInRightToLeft; // current frame is in right-to-left environment - strings are exchanged + bool m_bHtmlMode; + sal_uInt16 m_nHtmlMode; + OUString m_sDlgType; + Size m_aGrfSize; + SwTwips m_nUpperBorder; + SwTwips m_nLowerBorder; + double m_fWidthHeightRatio; //width-to-height ratio to support the KeepRatio button + + // OD 12.11.2003 #i22341# - keep content position of character for + // to character anchored objects. + const SwPosition* mpToCharContentPos; + + // old alignment + sal_Int16 m_nOldH; + sal_Int16 m_nOldHRel; + sal_Int16 m_nOldV; + sal_Int16 m_nOldVRel; + + FrameMap const * m_pVMap; + FrameMap const * m_pHMap; + + bool m_bAllowVertPositioning; + bool m_bIsMathOLE; + bool m_bIsMathBaselineAlignment; + + SwFrameExample m_aExampleWN; + + // size + std::unique_ptr<weld::Label> m_xWidthFT; + std::unique_ptr<weld::Label> m_xWidthAutoFT; + std::unique_ptr<weld::CheckButton> m_xRelWidthCB; + std::unique_ptr<weld::ComboBox> m_xRelWidthRelationLB; + std::unique_ptr<weld::CheckButton> m_xAutoWidthCB; + + std::unique_ptr<weld::Label> m_xHeightFT; + std::unique_ptr<weld::Label> m_xHeightAutoFT; + std::unique_ptr<weld::CheckButton> m_xRelHeightCB; + std::unique_ptr<weld::ComboBox> m_xRelHeightRelationLB; + std::unique_ptr<weld::CheckButton> m_xAutoHeightCB; + + std::unique_ptr<weld::CheckButton> m_xFixedRatioCB; + std::unique_ptr<weld::Button> m_xRealSizeBT; + + // anchor + std::unique_ptr<weld::Widget> m_xAnchorFrame; + std::unique_ptr<weld::RadioButton> m_xAnchorAtPageRB; + std::unique_ptr<weld::RadioButton> m_xAnchorAtParaRB; + std::unique_ptr<weld::RadioButton> m_xAnchorAtCharRB; + std::unique_ptr<weld::RadioButton> m_xAnchorAsCharRB; + std::unique_ptr<weld::RadioButton> m_xAnchorAtFrameRB; + + // position + std::unique_ptr<weld::Label> m_xHorizontalFT; + std::unique_ptr<weld::ComboBox> m_xHorizontalDLB; + std::unique_ptr<weld::Label> m_xAtHorzPosFT; + std::unique_ptr<weld::MetricSpinButton> m_xAtHorzPosED; + std::unique_ptr<weld::Label> m_xHoriRelationFT; + std::unique_ptr<weld::ComboBox> m_xHoriRelationLB; + + std::unique_ptr<weld::CheckButton> m_xMirrorPagesCB; + + std::unique_ptr<weld::Label> m_xVerticalFT; + std::unique_ptr<weld::ComboBox> m_xVerticalDLB; + std::unique_ptr<weld::Label> m_xAtVertPosFT; + std::unique_ptr<weld::MetricSpinButton> m_xAtVertPosED; + std::unique_ptr<weld::Label> m_xVertRelationFT; + std::unique_ptr<weld::ComboBox> m_xVertRelationLB; + // #i18732# - check box for new option 'FollowTextFlow' + std::unique_ptr<weld::CheckButton> m_xFollowTextFlowCB; + + // example + std::unique_ptr<weld::CustomWeld> m_xExampleWN; + + std::unique_ptr<SwPercentField> m_xWidthED; + std::unique_ptr<SwPercentField> m_xHeightED; + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + + DECL_LINK(RangeModifyClickHdl, weld::ToggleButton&, void); + void RangeModifyHdl(); + DECL_LINK(AnchorTypeHdl, weld::ToggleButton&, void); + DECL_LINK(PosHdl, weld::ComboBox&, void); + DECL_LINK(RelHdl, weld::ComboBox&, void); + void InitPos(RndStdIds eId, sal_Int16 nH, sal_Int16 nHRel, + sal_Int16 nV, sal_Int16 nVRel, + long nX, long nY); + + DECL_LINK(RealSizeHdl, weld::Button&, void); + DECL_LINK(RelSizeClickHdl, weld::ToggleButton&, void); + DECL_LINK(MirrorHdl, weld::ToggleButton&, void); + + DECL_LINK(AutoWidthClickHdl, weld::ToggleButton&, void); + DECL_LINK(AutoHeightClickHdl, weld::ToggleButton&, void); + + // update example + void UpdateExample(); + DECL_LINK(ModifyHdl, weld::MetricSpinButton&, void); + + void Init(const SfxItemSet& rSet); + // OD 12.11.2003 #i22341# - adjustment to handle maps, that are ambiguous + // in the alignment. + sal_Int32 FillPosLB( const FrameMap* _pMap, + const sal_Int16 _nAlign, + const sal_Int16 _nRel, + weld::ComboBox& _rLB ); + // OD 14.11.2003 #i22341# - adjustment to handle maps, that are ambiguous + // in their string entries. + void FillRelLB( const FrameMap* _pMap, + const sal_uInt16 _nLBSelPos, + const sal_Int16 _nAlign, + const sal_Int16 _nRel, + weld::ComboBox& _rLB, + weld::Label& _rFT ); + static sal_Int32 GetMapPos(const FrameMap *pMap, const weld::ComboBox& rAlignLB); + static sal_Int16 GetAlignment(FrameMap const *pMap, sal_Int32 nMapPos, const weld::ComboBox& rRelationLB); + static sal_Int16 GetRelation(const weld::ComboBox& rRelationLB); + RndStdIds GetAnchor() const; + + void setOptimalFrameWidth(); + void setOptimalRelWidth(); + + void EnableGraficMode(); // hides auto check boxes and re-org controls for "Real Size" button + + SwWrtShell *getFrameDlgParentShell(); + + static const sal_uInt16 aPageRg[]; + +public: + SwFramePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwFramePage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges() { return aPageRg; } + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + + void SetNewFrame(bool bNewFrame) { m_bNew = bNewFrame; } + void SetFormatUsed(bool bFormat); + void SetFrameType(const OUString &rType) { m_sDlgType = rType; } + bool IsInGraficMode() const { return m_sDlgType == "PictureDialog" || m_sDlgType == "ObjectDialog"; } + void EnableVerticalPositioning( bool bEnable ); +}; + +class SwGrfExtPage : public SfxTabPage +{ + OUString aFilterName; + OUString aGrfName, aNewGrfName; + + std::unique_ptr<::sfx2::FileDialogHelper> m_xGrfDlg; + + bool m_bHtmlMode; + + // mirror + BmpWindow m_aBmpWin; + std::unique_ptr<weld::Widget> m_xMirror; + std::unique_ptr<weld::CheckButton> m_xMirrorVertBox; + std::unique_ptr<weld::CheckButton> m_xMirrorHorzBox; + std::unique_ptr<weld::RadioButton> m_xAllPagesRB; + std::unique_ptr<weld::RadioButton> m_xLeftPagesRB; + std::unique_ptr<weld::RadioButton> m_xRightPagesRB; + + std::unique_ptr<weld::Entry> m_xConnectED; + std::unique_ptr<weld::Button> m_xBrowseBT; + std::unique_ptr<weld::Frame> m_xLinkFrame; + + // RotGrfFlyFrame: Need Angle and RotateControls now + std::unique_ptr<weld::Frame> m_xFlAngle; + std::unique_ptr<weld::MetricSpinButton> m_xNfAngle; + std::unique_ptr<svx::DialControl> m_xCtlAngle; + std::unique_ptr<weld::CustomWeld> m_xCtlAngleWin; + std::unique_ptr<weld::CustomWeld> m_xBmpWin; + + + // handler for mirroring + DECL_LINK(MirrorHdl, weld::ToggleButton&, void); + DECL_LINK(BrowseHdl, weld::Button&, void); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + +public: + SwGrfExtPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + virtual ~SwGrfExtPage() override; + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; +}; + +class SwFrameURLPage : public SfxTabPage +{ + // hyperlink + std::unique_ptr<weld::Entry> m_xURLED; + std::unique_ptr<weld::Button> m_xSearchPB; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::ComboBox> m_xFrameCB; + + // image map + std::unique_ptr<weld::CheckButton> m_xServerCB; + std::unique_ptr<weld::CheckButton> m_xClientCB; + + DECL_LINK(InsertFileHdl, weld::Button&, void); + +public: + SwFrameURLPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwFrameURLPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; +}; + +class SwFrameAddPage : public SfxTabPage +{ + SwWrtShell* m_pWrtSh; + + OUString m_sDlgType; + bool m_bHtmlMode; + bool m_bFormat; + bool m_bNew; + + std::unique_ptr<weld::Widget> m_xNameFrame; + std::unique_ptr<weld::Label> m_xNameFT; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Label> m_xAltNameFT; + std::unique_ptr<weld::Entry> m_xAltNameED; + std::unique_ptr<weld::Label> m_xDescriptionFT; + std::unique_ptr<weld::TextView> m_xDescriptionED; + std::unique_ptr<weld::Label> m_xPrevFT; + std::unique_ptr<weld::ComboBox> m_xPrevLB; + std::unique_ptr<weld::Label> m_xNextFT; + std::unique_ptr<weld::ComboBox> m_xNextLB; + + std::unique_ptr<weld::Widget> m_xProtectFrame; + std::unique_ptr<weld::CheckButton> m_xProtectContentCB; + std::unique_ptr<weld::CheckButton> m_xProtectFrameCB; + std::unique_ptr<weld::CheckButton> m_xProtectSizeCB; + + std::unique_ptr<weld::Widget> m_xContentAlignFrame; + std::unique_ptr<weld::ComboBox> m_xVertAlignLB; + + std::unique_ptr<weld::Widget> m_xPropertiesFrame; + std::unique_ptr<weld::CheckButton> m_xEditInReadonlyCB; + std::unique_ptr<weld::CheckButton> m_xPrintFrameCB; + std::unique_ptr<weld::Label> m_xTextFlowFT; + std::unique_ptr<svx::FrameDirectionListBox> m_xTextFlowLB; + + DECL_LINK(EditModifyHdl, weld::Entry&, void); + DECL_LINK(ChainModifyHdl, weld::ComboBox&, void); + + static const sal_uInt16 aAddPgRg[]; + +public: + SwFrameAddPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwFrameAddPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges() { return aAddPgRg; } + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + + void SetFormatUsed(bool bFormat); + void SetFrameType(const OUString &rType) { m_sDlgType = rType; } + void SetNewFrame(bool bNewFrame) { m_bNew = bNewFrame; } + void SetShell(SwWrtShell* pSh) { m_pWrtSh = pSh; } + +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_FRMPAGE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/frmsh.hxx b/sw/source/uibase/inc/frmsh.hxx new file mode 100644 index 000000000..3bb36497b --- /dev/null +++ b/sw/source/uibase/inc/frmsh.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_UIBASE_INC_FRMSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_FRMSH_HXX + +#include "basesh.hxx" + +class SwFrameShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_FRAMESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwFrameShell(SwView &rView); + virtual ~SwFrameShell() override; + + void Execute(SfxRequest &); + void ExecMove(SfxRequest& rReq); + void ExecField(const SfxRequest& rReq); + void GetState(SfxItemSet &); + void ExecFrameStyle(SfxRequest const & rReq); + void GetLineStyleState(SfxItemSet &rSet); + void StateInsert(SfxItemSet &rSet); + + void GetDrawAttrStateTextFrame(SfxItemSet &rSet); + void ExecDrawAttrArgsTextFrame(SfxRequest const & rReq); + + void ExecDrawDlgTextFrame(SfxRequest const & rReq); + void DisableStateTextFrame(SfxItemSet &rSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/globals.h b/sw/source/uibase/inc/globals.h new file mode 100644 index 000000000..8617d791c --- /dev/null +++ b/sw/source/uibase/inc/globals.h @@ -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_UIBASE_INC_GLOBALS_H +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOBALS_H + +enum SwChangeState +{ + STATE_OFF = 0, + STATE_ON = 1, + STATE_TOGGLE = 2 +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/glosbib.hxx b/sw/source/uibase/inc/glosbib.hxx new file mode 100644 index 000000000..b5ba48a97 --- /dev/null +++ b/sw/source/uibase/inc/glosbib.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_UIBASE_INC_GLOSBIB_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOSBIB_HXX + +#include <sfx2/basedlgs.hxx> +#include <rtl/ustring.hxx> +#include <vcl/weld.hxx> + +#include <vector> + +class SwGlossaryHdl; + +struct GlosBibUserData +{ + OUString sPath; + OUString sGroupName; + OUString sGroupTitle; +}; + +class SwGlossaryGroupDlg final : public SfxDialogController +{ + std::vector<OUString> m_RemovedArr; + std::vector<OUString> m_InsertedArr; + std::vector<OUString> m_RenamedArr; + + weld::Window* m_pParent; + SwGlossaryHdl* pGlosHdl; + + OUString sCreatedGroup; + + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::ComboBox> m_xPathLB; + std::unique_ptr<weld::TreeView> m_xGroupTLB; + + std::unique_ptr<weld::Button> m_xNewPB; + std::unique_ptr<weld::Button> m_xDelPB; + std::unique_ptr<weld::Button> m_xRenamePB; + + bool IsDeleteAllowed(const OUString &rGroup); + + void Apply(); + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(NewHdl, weld::Button&, void); + DECL_LINK(DeleteHdl, weld::Button&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(ModifyListBoxHdl, weld::ComboBox&, void); + DECL_LINK(RenameHdl, weld::Button&, void); + DECL_STATIC_LINK(SwGlossaryGroupDlg, EditInsertTextHdl, OUString&, bool); + DECL_LINK(EntrySizeAllocHdl, const Size&, void); + +public: + SwGlossaryGroupDlg(weld::Window* pParent, + std::vector<OUString> const& rPathArr, + SwGlossaryHdl *pGlosHdl); + virtual short run() override; + virtual ~SwGlossaryGroupDlg() override; + + const OUString& GetCreatedGroupName() const {return sCreatedGroup;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/glosdoc.hxx b/sw/source/uibase/inc/glosdoc.hxx new file mode 100644 index 000000000..14b4be59e --- /dev/null +++ b/sw/source/uibase/inc/glosdoc.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_UIBASE_INC_GLOSDOC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOSDOC_HXX + +#include <rtl/ustring.hxx> +#include <com/sun/star/text/XAutoTextGroup.hpp> + +class SwTextBlocks; +class SwDocShell; + +#ifndef SW_DECL_SWDOCSHELL_DEFINED +#define SW_DECL_SWDOCSHELL_DEFINED +#include <tools/ref.hxx> +typedef tools::SvRef<SwDocShell> SwDocShellRef; +#endif +#include <cppuhelper/weakref.hxx> + +#include <vector> +#include <swdllapi.h> + +typedef std::vector< css::uno::WeakReference< css::text::XAutoTextGroup > > UnoAutoTextGroups; +typedef std::vector< css::uno::WeakReference< css::text::XAutoTextEntry > > UnoAutoTextEntries; + +#define GLOS_DELIM u'*' + +class SW_DLLPUBLIC SwGlossaries +{ + UnoAutoTextGroups m_aGlossaryGroups; + UnoAutoTextEntries m_aGlossaryEntries; + + OUString m_aPath; + std::vector<OUString> m_aInvalidPaths; + std::vector<OUString> m_PathArr; + std::vector<OUString> m_GlosArr; + bool m_bError; + + SAL_DLLPRIVATE std::unique_ptr<SwTextBlocks> GetGlosDoc(const OUString &rName, bool bCreate = true) const; + SAL_DLLPRIVATE std::vector<OUString> & GetNameList(); + + // implementation in unoatxt.cxx + SAL_DLLPRIVATE void RemoveFileFromList( const OUString& rGroup ); + SAL_DLLPRIVATE void InvalidateUNOOjects(); + +public: + SwGlossaries(); + ~SwGlossaries(); + + /** returns the cached AutoTextGroup (if any) for the given group name + The group is created if it does not yet exist + + @precond + If <arg>_bCreate</arg> is <TRUE/>, the SolarMutex must be locked when calling into this method. + + @param _rGroupName + the name of the glossaries group + */ + css::uno::Reference< css::text::XAutoTextGroup > + GetAutoTextGroup( + const OUString& _rGroupName + ); + + /** returns the cached AutoTextEntry (if any) for the given group/with the given name + The entry is created if it does not yet exist + + @precond + If <arg>_bCreate</arg> is <TRUE/>, the SolarMutex must be locked when calling into this method. + + @param _rGroupAccessName + the name to access the group + @param _rGroupName + the name of the glossaries group, as to be passed to the entry + @param _rEntryName + the name of the auto text entry + */ + css::uno::Reference< css::text::XAutoTextEntry > + GetAutoTextEntry( + const OUString& _rCompleteGroupName, + const OUString& _rGroupName, + const OUString& _rEntryName + ); + + size_t GetGroupCnt(); + OUString const & GetGroupName(size_t); + OUString GetGroupTitle( const OUString& rGroupName ); + + bool FindGroupName(OUString& rGroup); + + std::unique_ptr<SwTextBlocks> + GetGroupDoc(const OUString &rName, + bool bCreate = false); + static OUString GetDefName(); + static OUString GetExtension(); + + OUString GetCompleteGroupName( const OUString& GroupName ); + + bool NewGroupDoc(OUString &rGroupName, const OUString& rTitle); + bool RenameGroupDoc(const OUString& sOldGroup, OUString& sNewGroup, const OUString& rNewTitle); + bool DelGroupDoc(const OUString &); + SwDocShellRef EditGroupDoc(const OUString &rGrpName, const OUString& rShortName, bool bShow = true ); + void UpdateGlosPath(bool bFull); + void ShowError(); + bool IsGlosPathErr() const { return m_bError; } + std::vector<OUString> const& GetPathArray() const { return m_PathArr; } +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_GLOSDOC_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/gloshdl.hxx b/sw/source/uibase/inc/gloshdl.hxx new file mode 100644 index 000000000..dd876c3a6 --- /dev/null +++ b/sw/source/uibase/inc/gloshdl.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_GLOSHDL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOSHDL_HXX + +#include <memory> +#include <rtl/ustring.hxx> +#include <vcl/weld.hxx> + +#include <swdllapi.h> + +class SwWrtShell; +class SwTextBlocks; +class SvxMacro; +class SwGlossaries; +class SfxViewFrame; + +class SW_DLLPUBLIC SwGlossaryHdl +{ + + SwGlossaries& rStatGlossaries; + OUString aCurGrp; + SfxViewFrame* pViewFrame; + SwWrtShell* pWrtShell; + std::unique_ptr<SwTextBlocks> + pCurGrp; + + SAL_DLLPRIVATE bool Expand(weld::Window* pParent, const OUString& rShortName, + SwGlossaries* pGlossaries, + std::unique_ptr<SwTextBlocks> pGlossary ); + +public: + void GlossaryDlg(); + + size_t GetGroupCnt() const; + OUString GetGroupName( size_t, OUString* pTitle ); + void NewGroup(OUString & rGroupName, const OUString& rTitle); + bool DelGroup(const OUString &); + void RenameGroup(const OUString& rOld, OUString& rNew, const OUString& rNewTitle); + void SetCurGroup(const OUString &aGrp, bool bApi = false, bool bAlwaysCreateNew = false); + + sal_uInt16 GetGlossaryCnt() const; + OUString GetGlossaryName(sal_uInt16); + OUString GetGlossaryShortName(const OUString &rName); + OUString GetGlossaryShortName(sal_uInt16); + + bool Rename( const OUString& rOldShortName, const OUString& rNewShortName, + const OUString& rNewName); + bool HasShortName(const OUString &rShortName) const; + // when NewGlossary is called from Basic then the previously set group should + // be newly created if applicable. + bool NewGlossary(const OUString &rName, const OUString &rShortName, + bool bApiCall = false, bool bNoAttr = false ); + bool DelGlossary(const OUString&); + bool CopyToClipboard(SwWrtShell& rSh, const OUString& rShortName); + + bool ExpandGlossary(weld::Window* pParent); + bool InsertGlossary(const OUString &rName); + + void SetMacros(const OUString& rName, + const SvxMacro* pStart, + const SvxMacro* pEnd, + SwTextBlocks *pGlossary = nullptr ); + void GetMacros(const OUString& rShortName, + SvxMacro& rStart, + SvxMacro& rEnd, + SwTextBlocks* pGlossary = nullptr ); + + bool IsReadOnly( const OUString* = nullptr ) const; + bool IsOld() const; + + bool FindGroupName(OUString& rGroup); // find group without path index + + bool ImportGlossaries( const OUString& rName ); + + SwGlossaryHdl(SfxViewFrame* pViewFrame, SwWrtShell *); + ~SwGlossaryHdl(); +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_GLOSHDL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/gloslst.hxx b/sw/source/uibase/inc/gloslst.hxx new file mode 100644 index 000000000..3da5d8861 --- /dev/null +++ b/sw/source/uibase/inc/gloslst.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_GLOSLST_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOSLST_HXX + +#include <rtl/ustring.hxx> +#include <tools/datetime.hxx> +#include <vcl/timer.hxx> + +#include <vector> + +class SwGlossaries; + +struct AutoTextGroup +{ + sal_uInt16 nCount; + OUString sName; + OUString sTitle; + OUString sLongNames; // by 0x0A separated long names + OUString sShortNames; // by 0x0A separated short names + DateTime aDateModified; + + AutoTextGroup() + : nCount(0) + , aDateModified(DateTime::EMPTY) + { + } +}; + +class SwGlossaryList : public AutoTimer +{ + std::vector<std::unique_ptr<AutoTextGroup>> aGroupArr; + OUString sPath; + bool bFilled; + + AutoTextGroup* FindGroup(const OUString& rGroupName); + static void FillGroup(AutoTextGroup* pGroup, SwGlossaries* pGloss); + +public: + SwGlossaryList(); + virtual ~SwGlossaryList() override; + + void HasLongName(const std::vector<OUString>& rBeginCandidates, + std::vector<std::pair<OUString, sal_uInt16>>& rLongNames); + bool GetShortName(const OUString& rLongName, + OUString& rShortName, OUString& rGroupName ); + + size_t GetGroupCount(); + OUString GetGroupName(size_t nPos); + OUString GetGroupTitle(size_t nPos); + + sal_uInt16 GetBlockCount(size_t nGroup); + OUString GetBlockLongName(size_t nGroup, sal_uInt16 nBlock); + OUString GetBlockShortName(size_t nGroup, sal_uInt16 nBlock); + + void Update(); + + virtual void Invoke() override; + + void ClearGroups(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/glossary.hxx b/sw/source/uibase/inc/glossary.hxx new file mode 100644 index 000000000..7f0dab689 --- /dev/null +++ b/sw/source/uibase/inc/glossary.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_GLOSSARY_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLOSSARY_HXX + +#include <vcl/customweld.hxx> +#include <vcl/textfilter.hxx> +#include <vcl/weld.hxx> + +#include <com/sun/star/text/XAutoTextContainer2.hpp> + +#include <rtl/ustring.hxx> +#include <sfx2/basedlgs.hxx> + +struct GroupUserData; +class SwGlossaryHdl; +class SwNewGlosNameDlg; +class SwWrtShell; +class SfxViewFrame; +class PopupMenu; +class Menu; +class SwOneExampleFrame; + +const short RET_EDIT = 100; + +class SwGlossaryDlg : public SfxDialogController +{ + friend class SwNewGlosNameDlg; + + OUString const m_sReadonlyPath; + + css::uno::Reference< css::text::XAutoTextContainer2 > m_xAutoText; + + SwGlossaryHdl* m_pGlossaryHdl; + + OUString m_sResumeGroup; + OUString m_sResumeShortName; + bool m_bResume; + + const bool m_bSelection : 1; + bool m_bReadOnly : 1; + bool m_bIsOld : 1; + bool m_bIsDocReadOnly:1; + + SwWrtShell* m_pShell; + + std::vector<std::unique_ptr<GroupUserData>> m_xGroupData; + + std::unique_ptr<weld::CheckButton> m_xInsertTipCB; + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Label> m_xShortNameLbl; + TextFilter m_aNoSpaceFilter; + std::unique_ptr<weld::Entry> m_xShortNameEdit; + std::unique_ptr<weld::TreeView> m_xCategoryBox; + std::unique_ptr<weld::CheckButton> m_xFileRelCB; + std::unique_ptr<weld::CheckButton> m_xNetRelCB; + std::unique_ptr<weld::Button> m_xInsertBtn; + std::unique_ptr<weld::MenuButton> m_xEditBtn; + std::unique_ptr<weld::Button> m_xBibBtn; + std::unique_ptr<weld::Button> m_xPathBtn; + std::unique_ptr<SwOneExampleFrame> m_xExampleFrame; + std::unique_ptr<weld::CustomWeld> m_xExampleFrameWin; + + void EnableShortName(bool bOn = true); + void ShowPreview(); + + DECL_LINK( NameModify, weld::Entry&, void ); + DECL_LINK( NameDoubleClick, weld::TreeView&, bool ); + DECL_LINK( GrpSelect, weld::TreeView&, void ); + DECL_LINK( MenuHdl, const OString&, void ); + DECL_LINK( EnableHdl, weld::ToggleButton&, void ); + DECL_LINK( BibHdl, weld::Button&, void ); + DECL_LINK( InsertHdl, weld::Button&, void ); + DECL_LINK( PathHdl, weld::Button&, void ); + DECL_LINK( CheckBoxHdl, weld::ToggleButton&, void ); + DECL_LINK( PreviewLoadedHdl, SwOneExampleFrame&, void ); + DECL_LINK( KeyInputHdl, const KeyEvent&, bool ); + DECL_LINK( TextFilterHdl, OUString&, bool ); + + void Apply(); + void Init(); + std::unique_ptr<weld::TreeIter> DoesBlockExist(const OUString& sBlock, const OUString& rShort); + void ShowAutoText(const OUString& rGroup, const OUString& rShortName); + void ResumeShowAutoText(); + + bool GetResumeData(OUString& rGroup, OUString& rShortName) + {rGroup = m_sResumeGroup; rShortName = m_sResumeShortName; return m_bResume;} + void SetResumeData(const OUString& rGroup, const OUString& rShortName) + {m_sResumeGroup = rGroup; m_sResumeShortName = rShortName; m_bResume = true;} + + void DeleteEntry(); +public: + SwGlossaryDlg(SfxViewFrame const * pViewFrame, SwGlossaryHdl* pGlosHdl, SwWrtShell *pWrtShell); + virtual short run() override; + virtual ~SwGlossaryDlg() override; + OUString GetCurrGrpName() const; + OUString GetCurrShortName() const + { + return m_xShortNameEdit->get_text(); + } + static OUString GetCurrGroup(); + static void SetActGroup(const OUString& rNewGroup); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/glshell.hxx b/sw/source/uibase/inc/glshell.hxx new file mode 100644 index 000000000..04fb73302 --- /dev/null +++ b/sw/source/uibase/inc/glshell.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_UIBASE_INC_GLSHELL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GLSHELL_HXX +#include <wdocsh.hxx> + +class SwGlosDocShell : public SwDocShell +{ + OUString aLongName; + OUString aShortName; + OUString aGroupName; + +protected: + virtual bool Save() override; + +public: + SFX_DECL_INTERFACE(SW_GLOSDOCSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwGlosDocShell( bool bNewShow); + virtual ~SwGlosDocShell() override; + + void Execute( SfxRequest& ); + void GetState( SfxItemSet& ); + void SetLongName( const OUString& rLongName ) + { aLongName = rLongName; } + void SetShortName( const OUString& rShortName ) + { aShortName = rShortName; } + void SetGroupName( const OUString& rGroupName ) + { aGroupName = rGroupName; } +}; + +class SwWebGlosDocShell : public SwWebDocShell +{ + OUString aLongName; + OUString aShortName; + OUString aGroupName; + +protected: + virtual bool Save() override; + +public: + SFX_DECL_INTERFACE(SW_WEBGLOSDOCSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebGlosDocShell(); + virtual ~SwWebGlosDocShell() override; + + void Execute( SfxRequest& ); + void GetState( SfxItemSet& ); + void SetLongName( const OUString& rLongName ) + { aLongName = rLongName; } + void SetShortName( const OUString& rShortName ) + { aShortName = rShortName; } + void SetGroupName( const OUString& rGroupName ) + { aGroupName = rGroupName; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/grfsh.hxx b/sw/source/uibase/inc/grfsh.hxx new file mode 100644 index 000000000..357fabf42 --- /dev/null +++ b/sw/source/uibase/inc/grfsh.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_GRFSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_GRFSH_HXX + +#include <memory> +#include <vector> + +#include "basesh.hxx" +#include <shellid.hxx> + +#include <sfx2/shell.hxx> + +class SwGrfShell: public SwBaseShell +{ + class SwExternalToolEdit; + std::vector<std::unique_ptr<SwExternalToolEdit>> m_ExternalEdits; + +public: + SFX_DECL_INTERFACE(SW_GRFSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void Execute(SfxRequest& rRequest); + void ExecAttr(SfxRequest const & rRequest); + void GetAttrState(SfxItemSet& rRequest); + + void ExecuteRotation(SfxRequest const &rRequest); + void GetAttrStateForRotation(SfxItemSet& rRequest); + + SwGrfShell(SwView &rView); + virtual ~SwGrfShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/hyp.hxx b/sw/source/uibase/inc/hyp.hxx new file mode 100644 index 000000000..9b2d26d12 --- /dev/null +++ b/sw/source/uibase/inc/hyp.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_UIBASE_INC_HYP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_HYP_HXX + +#include <editeng/splwrap.hxx> +#include <com/sun/star/linguistic2/XHyphenator.hpp> +#include <swdllapi.h> + +class SwView; + +class SW_DLLPUBLIC SwHyphWrapper : public SvxSpellWrapper { +private: + SwView* pView; + sal_uInt16 nPageCount; // page count for progress view + sal_uInt16 nPageStart; // 1st checked page + bool bInSelection : 1; // separating selected text + bool bAutomatic : 1; // insert separators without further inquiry + bool bInfoBox : 1; // display info-box when ending + +protected: + virtual void SpellStart( SvxSpellArea eSpell ) override; + virtual void SpellContinue() override; + virtual void SpellEnd( ) override; + virtual bool SpellMore() override; + virtual void InsertHyphen( const sal_Int32 nPos ) override; // insert hyphen + +public: + SwHyphWrapper( SwView* pVw, + css::uno::Reference< css::linguistic2::XHyphenator > const &rxHyph, + bool bStart, bool bOther, bool bSelect ); + virtual ~SwHyphWrapper() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/idxmrk.hxx b/sw/source/uibase/inc/idxmrk.hxx new file mode 100644 index 000000000..e180ebed2 --- /dev/null +++ b/sw/source/uibase/inc/idxmrk.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_IDXMRK_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_IDXMRK_HXX + +#include <sfx2/childwin.hxx> + +#include <swabstdlg.hxx> + +class SwWrtShell; + +class SwInsertIdxMarkWrapper final : public SfxChildWindow +{ + ScopedVclPtr<AbstractMarkFloatDlg> xAbstDlg; + +public: + SwInsertIdxMarkWrapper(vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(SwInsertIdxMarkWrapper); + + void ReInitDlg(SwWrtShell& rWrtShell); +}; + +class SwInsertAuthMarkWrapper final : public SfxChildWindow +{ + ScopedVclPtr<AbstractMarkFloatDlg> xAbstDlg; + +public: + SwInsertAuthMarkWrapper(vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(SwInsertAuthMarkWrapper); + + void ReInitDlg(SwWrtShell& rWrtShell); +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_IDXMRK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/imaildsplistener.hxx b/sw/source/uibase/inc/imaildsplistener.hxx new file mode 100644 index 000000000..8852a8800 --- /dev/null +++ b/sw/source/uibase/inc/imaildsplistener.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_UIBASE_INC_IMAILDSPLISTENER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_IMAILDSPLISTENER_HXX + +#include <com/sun/star/mail/XMailMessage.hpp> +#include <salhelper/simplereferenceobject.hxx> +#include <rtl/ref.hxx> + +class MailDispatcher; + +/** + MailDispatcher listener interface. + Clients may implement and register instances of the + mail dispatcher interface in order to get notifications + about the MailDispatcher status. + + @see MailDispatcher +*/ +class IMailDispatcherListener : public salhelper::SimpleReferenceObject +{ +public: + /** + Called when there are no more mail messages + to deliver. + */ + virtual void idle() = 0; + + /** + Called for every mail message that has been + successfully delivered. + */ + virtual void mailDelivered(css::uno::Reference< css::mail::XMailMessage> xMailMessage) = 0; + + /** + Called for every mail message whose delivery + failed. + */ + virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher, css::uno::Reference< css::mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) = 0; +}; + +#endif // INCLUDED_IMAILDISPATCHERLISTENER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/initui.hxx b/sw/source/uibase/inc/initui.hxx new file mode 100644 index 000000000..5f2b145ea --- /dev/null +++ b/sw/source/uibase/inc/initui.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_UIBASE_INC_INITUI_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_INITUI_HXX + +#include <swdllapi.h> +#include <rtl/ustring.hxx> + +/* + * Forward Declarations + */ +class SwThesaurus; + +/* + * Extern Definitions + */ +extern SwThesaurus* pThes; + +SW_DLLPUBLIC const OUString& GetCurrGlosGroup(); +SW_DLLPUBLIC void SetCurrGlosGroup(const OUString& sStr); + +// provides textblock management +class SwGlossaries; +SW_DLLPUBLIC SwGlossaries* GetGlossaries(); + +class SwGlossaryList; + +bool HasGlossaryList(); +SwGlossaryList* GetGlossaryList(); + +extern void InitUI(); +extern void FinitUI(); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/inpdlg.hxx b/sw/source/uibase/inc/inpdlg.hxx new file mode 100644 index 000000000..f1dfc725d --- /dev/null +++ b/sw/source/uibase/inc/inpdlg.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_UIBASE_INC_INPDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_INPDLG_HXX + +#include <vcl/weld.hxx> + +class SwInputField; +class SwSetExpField; +class SwUserFieldType; +class SwField; +class SwWrtShell; +class SwFieldMgr; + +// insert fields +class SwFieldInputDlg : public weld::GenericDialogController +{ + void Apply(); + + SwWrtShell& rSh; + SwInputField* pInpField; + SwSetExpField* pSetField; + SwUserFieldType* pUsrType; + + weld::Button* m_pPressedButton; + std::unique_ptr<weld::Entry> m_xLabelED; + std::unique_ptr<weld::TextView> m_xEditED; + std::unique_ptr<weld::Button> m_xPrevBT; + std::unique_ptr<weld::Button> m_xNextBT; + std::unique_ptr<weld::Button> m_xOKBT; + + DECL_LINK(NextHdl, weld::Button&, void); + DECL_LINK(PrevHdl, weld::Button&, void); + +public: + SwFieldInputDlg(weld::Widget *pParent, SwWrtShell &rSh, + SwField* pField, bool bPrevButton, bool bNextButton); + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } + virtual ~SwFieldInputDlg() override; + bool PrevButtonPressed() const; + bool NextButtonPressed() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/inputwin.hxx b/sw/source/uibase/inc/inputwin.hxx new file mode 100644 index 000000000..6698e1924 --- /dev/null +++ b/sw/source/uibase/inc/inputwin.hxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_INPUTWIN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_INPUTWIN_HXX + +#include <vcl/InterimItemWindow.hxx> +#include <vcl/menu.hxx> +#include <vcl/toolbox.hxx> + +#include <sfx2/childwin.hxx> + +class SwFieldMgr; +class SwWrtShell; +class SwView; +class SfxDispatcher; + +class InputEdit final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::Entry> m_xWidget; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ActivateHdl, weld::Entry&, bool); +public: + InputEdit(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/swriter/ui/inputeditbox.ui", "InputEditBox") + , m_xWidget(m_xBuilder->weld_entry("entry")) + { + m_xWidget->connect_key_press(LINK(this, InputEdit, KeyInputHdl)); + m_xWidget->connect_activate(LINK(this, InputEdit, ActivateHdl)); + SetSizePixel(m_xWidget->get_preferred_size()); + } + + void UpdateRange(const OUString& rSel, const OUString& rTableName); + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void set_text(const OUString& rText) + { + m_xWidget->set_text(rText); + } + + OUString get_text() const + { + return m_xWidget->get_text(); + } + + void set_accessible_name(const OUString& rName) + { + m_xWidget->set_accessible_name(rName); + } + + void replace_selection(const OUString& rText) + { + m_xWidget->replace_selection(rText); + } + + void select_region(int nStartPos, int nEndPos) + { + m_xWidget->select_region(nStartPos, nEndPos); + } + + void connect_changed(const Link<weld::Entry&, void>& rLink) + { + m_xWidget->connect_changed(rLink); + } + + virtual ~InputEdit() override + { + disposeOnce(); + } +}; + + +class PosEdit final : public InterimItemWindow +{ +private: + std::unique_ptr<weld::Entry> m_xWidget; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); +public: + PosEdit(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/swriter/ui/poseditbox.ui", "PosEditBox") + , m_xWidget(m_xBuilder->weld_entry("entry")) + { + m_xWidget->connect_key_press(LINK(this, PosEdit, KeyInputHdl)); + SetSizePixel(m_xWidget->get_preferred_size()); + } + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void set_text(const OUString& rText) + { + m_xWidget->set_text(rText); + } + + void set_accessible_name(const OUString& rName) + { + m_xWidget->set_accessible_name(rName); + } + + virtual ~PosEdit() override + { + disposeOnce(); + } +}; + +class SwInputWindow final : public ToolBox +{ +friend class InputEdit; + + VclPtr<PosEdit> mxPos; + VclPtr<InputEdit> mxEdit; + std::unique_ptr<SwFieldMgr> pMgr; + SwWrtShell* pWrtShell; + SwView* pView; + OUString aCurrentTableName, sOldFormula; + + bool bFirst : 1; // initialisations at first call + bool bIsTable : 1; + bool bDelSel : 1; + bool m_bDoesUndo : 1; + bool m_bResetUndo : 1; + bool m_bCallUndo : 1; + + void CleanupUglyHackWithUndo(); + + void DelBoxContent(); + DECL_LINK(ModifyHdl, weld::Entry&, void); + + using Window::IsActive; + + virtual void Resize() override; + virtual void Click() override; + DECL_LINK( MenuHdl, Menu *, bool ); + DECL_LINK( DropdownClickHdl, ToolBox*, void ); + void ApplyFormula(); + void CancelFormula(); + +public: + SwInputWindow(vcl::Window* pParent, SfxDispatcher const * pDispatcher); + virtual ~SwInputWindow() override; + virtual void dispose() override; + + void ShowWin(); + + DECL_LINK( SelTableCellsNotify, SwWrtShell&, void ); + + void SetFormula( const OUString& rFormula ); + const SwView* GetView() const{return pView;} +}; + +class SwInputChild : public SfxChildWindow +{ + SfxDispatcher* pDispatch; +public: + SwInputChild( vcl::Window* , + sal_uInt16 nId, + SfxBindings const *, + SfxChildWinInfo* ); + virtual ~SwInputChild() override; + SFX_DECL_CHILDWINDOW_WITHID( SwInputChild ); + void SetFormula( const OUString& rFormula ) + { static_cast<SwInputWindow*>(GetWindow())->SetFormula( rFormula ); } + const SwView* GetView() const + { return static_cast<SwInputWindow*>(GetWindow())->GetView();} + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/insfnote.hxx b/sw/source/uibase/inc/insfnote.hxx new file mode 100644 index 000000000..6ea4cc339 --- /dev/null +++ b/sw/source/uibase/inc/insfnote.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_UIBASE_INC_INSFNOTE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_INSFNOTE_HXX + +#include <vcl/weld.hxx> + +class SwWrtShell; + +class VclFrame; + +class SwInsFootNoteDlg: public weld::GenericDialogController +{ + SwWrtShell &m_rSh; + + // everything for the character(s) + OUString m_aFontName; + rtl_TextEncoding m_eCharSet; + bool m_bExtCharAvailable; + bool m_bEdit; + + std::unique_ptr<weld::Widget> m_xNumberFrame; + std::unique_ptr<weld::RadioButton> m_xNumberAutoBtn; + std::unique_ptr<weld::RadioButton> m_xNumberCharBtn; + std::unique_ptr<weld::Entry> m_xNumberCharEdit; + std::unique_ptr<weld::Button> m_xNumberExtChar; + + // everything for the selection footnote/endnote + std::unique_ptr<weld::RadioButton> m_xFootnoteBtn; + std::unique_ptr<weld::RadioButton> m_xEndNoteBtn; + + std::unique_ptr<weld::Button> m_xOkBtn; + std::unique_ptr<weld::Button> m_xPrevBT; + std::unique_ptr<weld::Button> m_xNextBT; + + DECL_LINK(NumberCharHdl, weld::Button&, void); + DECL_LINK(NumberEditHdl, weld::Entry&, void); + DECL_LINK(NumberAutoBtnHdl, weld::Button&, void); + DECL_LINK(NumberExtCharHdl, weld::Button&, void); + DECL_LINK(NextPrevHdl, weld::Button&, void); + + void Apply(); + + void Init(); + +public: + SwInsFootNoteDlg(weld::Window * pParent, SwWrtShell &rSh, bool bEd); + virtual ~SwInsFootNoteDlg() COVERITY_NOEXCEPT_FALSE override; + + const OUString& GetFontName() const { return m_aFontName; } + bool IsEndNote() const { return m_xEndNoteBtn->get_active(); } + OUString GetStr() const + { + if (m_xNumberCharBtn->get_active()) + return m_xNumberCharEdit->get_text(); + return OUString(); + } + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/instable.hxx b/sw/source/uibase/inc/instable.hxx new file mode 100644 index 000000000..aa36dc655 --- /dev/null +++ b/sw/source/uibase/inc/instable.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_UIBASE_INC_INSTABLE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_INSTABLE_HXX + +#include <sfx2/basedlgs.hxx> +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <tools/link.hxx> +#include <vcl/textfilter.hxx> + +#include "wrtsh.hxx" +#include "autoformatpreview.hxx" +#include <view.hxx> +#include <tblafmt.hxx> +#include <itabenum.hxx> + +class SwInsTableDlg : public SfxDialogController +{ + TextFilter m_aTextFilter; + + SwWrtShell* pShell; + SwTableAutoFormatTable* pTableTable; + SwTableAutoFormat* pTAutoFormat; + + sal_uInt8 lbIndex; + sal_uInt8 tbIndex; + sal_uInt8 minTableIndexInLb; + sal_uInt8 maxTableIndexInLb; + sal_Int64 nEnteredValRepeatHeaderNF; + + AutoFormatPreview m_aWndPreview; + + std::unique_ptr<weld::Entry> m_xNameEdit; + std::unique_ptr<weld::SpinButton> m_xColNF; + std::unique_ptr<weld::SpinButton> m_xRowNF; + std::unique_ptr<weld::CheckButton> m_xHeaderCB; + std::unique_ptr<weld::CheckButton> m_xRepeatHeaderCB; + std::unique_ptr<weld::SpinButton> m_xRepeatHeaderNF; + std::unique_ptr<weld::Widget> m_xRepeatGroup; + std::unique_ptr<weld::CheckButton> m_xDontSplitCB; + std::unique_ptr<weld::Button> m_xInsertBtn; + std::unique_ptr<weld::TreeView> m_xLbFormat; + std::unique_ptr<weld::CustomWeld> m_xWndPreview; + std::unique_ptr<weld::Frame> m_xStyleFrame; + + // Returns 255 if mapping is not possible. + // This means there cannot be more than 255 autotable style. + sal_uInt8 lbIndexToTableIndex( const sal_uInt8 listboxIndex ); + void InitAutoTableFormat(); + + DECL_LINK(TextFilterHdl, OUString&, bool); + DECL_LINK(SelFormatHdl, weld::TreeView&, void); + DECL_LINK(ModifyName, weld::Entry&, void); + DECL_LINK(ModifyRowCol, weld::SpinButton&, void); + DECL_LINK(OKHdl, weld::Button&, void); + DECL_LINK(CheckBoxHdl, weld::ToggleButton&, void); + DECL_LINK(RepeatHeaderCheckBoxHdl, weld::ToggleButton&, void); + DECL_LINK(ModifyRepeatHeaderNF_Hdl, weld::SpinButton&, void); + +public: + SwInsTableDlg(SwView& rView); + + void GetValues( OUString& rName, sal_uInt16& rRow, sal_uInt16& rCol, + SwInsertTableOptions& rInsTableOpts, OUString& rTableAutoFormatName, + std::unique_ptr<SwTableAutoFormat>& prTAFormat ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/javaedit.hxx b/sw/source/uibase/inc/javaedit.hxx new file mode 100644 index 000000000..2a801ee1f --- /dev/null +++ b/sw/source/uibase/inc/javaedit.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_JAVAEDIT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_JAVAEDIT_HXX + +#include <vcl/weld.hxx> + +class SwWrtShell; +class SwFieldMgr; +class SwScriptField; + +namespace sfx2 { class FileDialogHelper; } + +class SwJavaEditDialog : public weld::GenericDialogController +{ +private: + OUString m_aText; + OUString m_aType; + + bool m_bNew; + bool m_bIsUrl; + + SwScriptField* m_pField; + std::unique_ptr<SwFieldMgr> m_pMgr; + SwWrtShell* m_pSh; + std::unique_ptr<sfx2::FileDialogHelper> m_pFileDlg; + + std::unique_ptr<weld::Entry> m_xTypeED; + std::unique_ptr<weld::RadioButton> m_xUrlRB; + std::unique_ptr<weld::RadioButton> m_xEditRB; + std::unique_ptr<weld::Button> m_xUrlPB; + std::unique_ptr<weld::Entry> m_xUrlED; + std::unique_ptr<weld::TextView> m_xEditED; + std::unique_ptr<weld::Button> m_xOKBtn; + std::unique_ptr<weld::Button> m_xPrevBtn; + std::unique_ptr<weld::Button> m_xNextBtn; + + DECL_LINK(OKHdl, weld::Button&, void); + DECL_LINK(PrevHdl, weld::Button&, void); + DECL_LINK(NextHdl, weld::Button&, void); + DECL_LINK(RadioButtonHdl, weld::Button&, void); + DECL_LINK(InsertFileHdl, weld::Button&, void); + DECL_LINK(DlgClosedHdl, sfx2::FileDialogHelper *, void); + + void CheckTravel(); + void SetField(); + +public: + SwJavaEditDialog(weld::Window* pParent, SwWrtShell* pWrtSh); + virtual ~SwJavaEditDialog() override; + + const OUString& GetScriptText() const { return m_aText; } + + const OUString& GetScriptType() const { return m_aType; } + + bool IsUrl() const { return m_bIsUrl; } + bool IsNew() const { return m_bNew; } + bool IsUpdate() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/label.hxx b/sw/source/uibase/inc/label.hxx new file mode 100644 index 000000000..86d08a257 --- /dev/null +++ b/sw/source/uibase/inc/label.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_LABEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LABEL_HXX + +#include <sfx2/tabdlg.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include "labelcfg.hxx" +#include <vector> + +class SwLabItem; +class SwLabPrtPage; +class SwDBManager; +class Printer; + +class SwLabDlg : public SfxTabDialogController +{ + SwLabelConfig aLabelsCfg; + SwDBManager* pDBManager; + SwLabPrtPage* m_pPrtPage; + + std::vector<sal_uInt16> aTypeIds; + std::vector<OUString> aMakes; + + std::unique_ptr<SwLabRecs> m_pRecs; + OUString aLstGroup; + OUString m_sBusinessCardDlg; + bool m_bLabel; + void ReplaceGroup_( const OUString &rMake ); + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; +public: + + SwLabDlg(weld::Window* pParent, const SfxItemSet& rSet, + SwDBManager* pDBManager, bool bLabel); + virtual ~SwLabDlg() override; + + SwLabRec* GetRecord(const OUString &rRecName, bool bCont); + void GetLabItem(SwLabItem &rItem); + + SwLabRecs &Recs() { return *m_pRecs; } + const SwLabRecs &Recs() const { return *m_pRecs; } + + std::vector<sal_uInt16> &TypeIds() { return aTypeIds; } + const std::vector<sal_uInt16> &TypeIds() const { return aTypeIds; } + + std::vector<OUString> &Makes() { return aMakes; } + const std::vector<OUString> &Makes() const { return aMakes; } + + Printer *GetPrt(); + void ReplaceGroup( const OUString &rMake ) + { + if ( rMake != aLstGroup ) + ReplaceGroup_( rMake ); + } + + void UpdateGroup( const OUString &rMake ) {ReplaceGroup_( rMake );} + static void UpdateFieldInformation(css::uno::Reference< css::frame::XModel> const & xModel, + const SwLabItem& rItem); + const OUString& GetBusinessCardStr() const {return m_sBusinessCardDlg;} + + SwLabelConfig& GetLabelsConfig() {return aLabelsCfg;} + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/labelcfg.hxx b/sw/source/uibase/inc/labelcfg.hxx new file mode 100644 index 000000000..c206f67de --- /dev/null +++ b/sw/source/uibase/inc/labelcfg.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_UIBASE_INC_LABELCFG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LABELCFG_HXX + +#include <unotools/configitem.hxx> +#include <swdllapi.h> +#include "labrec.hxx" + +#include <map> +#include <vector> + +struct SwLabelMeasure +{ + OUString m_aMeasure; // string contains the label dimensions + bool m_bPredefined; // used to distinguish predefined from user-defined labels +}; + +class SW_DLLPUBLIC SwLabelConfig : public utl::ConfigItem +{ +private: + std::vector<OUString> m_aManufacturers; + std::map< OUString, std::map<OUString, SwLabelMeasure> > m_aLabels; + + virtual void ImplCommit() override; + +public: + SwLabelConfig(); + virtual ~SwLabelConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + + void FillLabels(const OUString& rManufacturer, SwLabRecs& rLabArr); + const std::vector<OUString>& GetManufacturers() const {return m_aManufacturers;} + + bool HasLabel(const OUString& rManufacturer, const OUString& rType); + bool IsPredefinedLabel(const OUString& rManufacturer, const OUString& rType) + { return m_aLabels[rManufacturer][rType].m_bPredefined; }; + void SaveLabel(const OUString& rManufacturer, const OUString& rType, + const SwLabRec& rRec); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/labimg.hxx b/sw/source/uibase/inc/labimg.hxx new file mode 100644 index 000000000..ec01bf38f --- /dev/null +++ b/sw/source/uibase/inc/labimg.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_UIBASE_INC_LABIMG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LABIMG_HXX + +#include <svl/poolitem.hxx> +#include <unotools/configitem.hxx> +#include <swdllapi.h> + +class SwLabCfgItem; + +class SW_DLLPUBLIC SwLabItem : public SfxPoolItem +{ + +public: + + SwLabItem(); + + SwLabItem& operator =(const SwLabItem& rItem); + SwLabItem(SwLabItem const &) = default; // SfxPoolItem copy function dichotomy + + virtual bool operator ==(const SfxPoolItem& rItem) const override; + + virtual SwLabItem* Clone(SfxItemPool* = nullptr) const override; + + OUString m_aLstMake; // remember last selection + OUString m_aLstType; + OUString m_sDBName; // used database + + OUString m_aWriting; // label + OUString m_aMake; // label mark + OUString m_aType; // label type + sal_Int32 m_lHDist; // horizontal distance (user) + sal_Int32 m_lVDist; // vertical distance (user) + sal_Int32 m_lWidth; // width (user) + sal_Int32 m_lHeight; // height (user) + sal_Int32 m_lLeft; // left border (user) + sal_Int32 m_lUpper; // upper border (user) + sal_Int32 m_nCols; // number of columns (user) + sal_Int32 m_nRows; // number of rows (user) + sal_Int32 m_nCol; // column for single print + sal_Int32 m_nRow; // row for single print + sal_Int32 m_lPHeight; // paper height + sal_Int32 m_lPWidth; // paper width + bool m_bAddr;// address as label? + bool m_bCont;// continuous paper? + bool m_bPage;// whole page or single labels? + bool m_bSynchron;// synchronise all labels + + //parts of the business card + OUString m_aPrivFirstName; + OUString m_aPrivName; + OUString m_aPrivShortCut; + OUString m_aPrivFirstName2; + OUString m_aPrivName2; + OUString m_aPrivShortCut2; + OUString m_aPrivStreet; + OUString m_aPrivZip; + OUString m_aPrivCity; + OUString m_aPrivCountry; + OUString m_aPrivState; + OUString m_aPrivTitle; + OUString m_aPrivProfession; + OUString m_aPrivPhone; + OUString m_aPrivMobile; + OUString m_aPrivFax; + OUString m_aPrivWWW; + OUString m_aPrivMail; + OUString m_aCompCompany; + OUString m_aCompCompanyExt; + OUString m_aCompSlogan; + OUString m_aCompStreet; + OUString m_aCompZip; + OUString m_aCompCity; + OUString m_aCompCountry; + OUString m_aCompState; + OUString m_aCompPosition; + OUString m_aCompPhone; + OUString m_aCompMobile; + OUString m_aCompFax; + OUString m_aCompWWW; + OUString m_aCompMail; + + OUString m_sGlossaryGroup; + OUString m_sGlossaryBlockName; +}; + +class SwLabCfgItem : public utl::ConfigItem +{ +private: + SwLabItem aItem; + bool bIsLabel; + + css::uno::Sequence<OUString> GetPropertyNames() const; + + virtual void ImplCommit() override; + +public: + SwLabCfgItem(bool bLabel); + + SwLabItem& GetItem() {return aItem;} + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/labimp.hxx b/sw/source/uibase/inc/labimp.hxx new file mode 100644 index 000000000..e8db8aab1 --- /dev/null +++ b/sw/source/uibase/inc/labimp.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_LABIMP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LABIMP_HXX + +#include <vcl/weld.hxx> + +inline int getfldval(const weld::MetricSpinButton& rField) +{ + return rField.denormalize(rField.get_value(FieldUnit::TWIP)); +} + +inline void setfldval(weld::MetricSpinButton& rField, int lValue) +{ + rField.set_value(rField.normalize(lValue), FieldUnit::TWIP); +} + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_LABIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/labrec.hxx b/sw/source/uibase/inc/labrec.hxx new file mode 100644 index 000000000..1810ff476 --- /dev/null +++ b/sw/source/uibase/inc/labrec.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_UIBASE_INC_LABREC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LABREC_HXX + +#include <rtl/ustring.hxx> + +#include <memory> +#include <vector> + + +class SwLabItem; + +class SwLabRec +{ +public: + SwLabRec(): m_nHDist(0), m_nVDist(0), m_nWidth(0), m_nHeight(0), m_nLeft(0), m_nUpper(0), m_nPWidth(0), m_nPHeight(0), m_nCols(0), m_nRows(0), m_bCont(false) {} + + void SetFromItem( const SwLabItem& rItem ); + void FillItem( SwLabItem& rItem ) const; + + OUString m_aMake; + OUString m_aType; + long m_nHDist; + long m_nVDist; + long m_nWidth; + long m_nHeight; + long m_nLeft; + long m_nUpper; + long m_nPWidth; + long m_nPHeight; + sal_Int32 m_nCols; + sal_Int32 m_nRows; + bool m_bCont; +}; + +typedef std::vector<std::unique_ptr<SwLabRec>> SwLabRecs; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/langhelper.hxx b/sw/source/uibase/inc/langhelper.hxx new file mode 100644 index 000000000..8c93d29d3 --- /dev/null +++ b/sw/source/uibase/inc/langhelper.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_UIBASE_INC_LANGHELPER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LANGHELPER_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <i18nlangtag/lang.h> + +class SwWrtShell; +class SwView; +class EditEngine; +class EditView; +class OutlinerView; +class SfxItemSet; +class SfxRequest; +struct ESelection; +enum class SvtScriptType; + +namespace SwLangHelper +{ + extern void GetLanguageStatus( OutlinerView* pOLV, SfxItemSet& rSet ); + extern bool SetLanguageStatus( OutlinerView* pOLV, SfxRequest &rReq, SwView const &rView, SwWrtShell &rSh ); + + extern void SetLanguage( SwWrtShell &rWrtSh, const OUString &rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ); + extern void SetLanguage( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, const OUString &rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ); + extern void SetLanguage_None( SwWrtShell &rWrtSh, bool bIsForSelection, SfxItemSet &rCoreSet ); + extern void SetLanguage_None( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, bool bIsForSelection, SfxItemSet &rCoreSet ); + extern void ResetLanguages( SwWrtShell &rWrtSh, OutlinerView const * pOLV = nullptr ); + + // document + extern void SelectCurrentPara( SwWrtShell &rWrtSh ); + // EditView + extern void SelectPara( EditView &rEditView, const ESelection &rCurSel ); + + extern OUString GetTextForLanguageGuessing(EditEngine const * rEditEngine, const ESelection& rDocSelection); + extern OUString GetTextForLanguageGuessing(SwWrtShell const &rSh); + + extern LanguageType GetLanguage( SfxItemSet const & aSet, sal_uInt16 nLangWhichId ); + extern LanguageType GetLanguage( SwWrtShell &rSh, sal_uInt16 nLangWhichId ); + + extern LanguageType GetCurrentLanguage( SfxItemSet const & aSet, SvtScriptType nScriptType ); + extern LanguageType GetCurrentLanguage( SwWrtShell &rSh ); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/linenum.hxx b/sw/source/uibase/inc/linenum.hxx new file mode 100644 index 000000000..21f4d7886 --- /dev/null +++ b/sw/source/uibase/inc/linenum.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_UIBASE_INC_LINENUM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LINENUM_HXX + +#include <sfx2/basedlgs.hxx> +#include "numberingtypelistbox.hxx" + +class SwView; +class SwWrtShell; + +class SwLineNumberingDlg : public SfxDialogController +{ +private: + SwWrtShell* m_pSh; + std::unique_ptr<weld::Widget> m_xBodyContent; + std::unique_ptr<weld::Widget> m_xDivIntervalFT; + std::unique_ptr<weld::SpinButton> m_xDivIntervalNF; + std::unique_ptr<weld::Widget> m_xDivRowsFT; + std::unique_ptr<weld::SpinButton> m_xNumIntervalNF; + std::unique_ptr<weld::ComboBox> m_xCharStyleLB; + std::unique_ptr<SwNumberingTypeListBox> m_xFormatLB; + std::unique_ptr<weld::ComboBox> m_xPosLB; + std::unique_ptr<weld::MetricSpinButton> m_xOffsetMF; + std::unique_ptr<weld::Entry> m_xDivisorED; + std::unique_ptr<weld::CheckButton> m_xCountEmptyLinesCB; + std::unique_ptr<weld::CheckButton> m_xCountFrameLinesCB; + std::unique_ptr<weld::CheckButton> m_xRestartEachPageCB; + std::unique_ptr<weld::CheckButton> m_xNumberingOnCB; + std::unique_ptr<weld::CheckButton> m_xNumberingOnFooterHeader; + std::unique_ptr<weld::Button> m_xOKButton; + std::unique_ptr<weld::Widget> m_xNumIntervalFT; + std::unique_ptr<weld::Widget> m_xNumRowsFT; + DECL_LINK(OKHdl, weld::Button&, void); + DECL_LINK(LineOnOffHdl, weld::Button&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + +public: + SwLineNumberingDlg(const SwView& rVw); + virtual ~SwLineNumberingDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/listsh.hxx b/sw/source/uibase/inc/listsh.hxx new file mode 100644 index 000000000..0caecf642 --- /dev/null +++ b/sw/source/uibase/inc/listsh.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_UIBASE_INC_LISTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_LISTSH_HXX + +#include "basesh.hxx" + +class SwListShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_LISTSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwListShell(SwView &rView); + + void Execute(SfxRequest &); + void GetState(SfxItemSet &); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/macassgn.hxx b/sw/source/uibase/inc/macassgn.hxx new file mode 100644 index 000000000..6c1ce2c4d --- /dev/null +++ b/sw/source/uibase/inc/macassgn.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_UIBASE_INC_MACASSGN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MACASSGN_HXX + +#include <sfx2/evntconf.hxx> +#include <vcl/weld.hxx> + +class SwWrtShell; +class SvxMacroItem; + +enum DlgEventType +{ + MACASSGN_AUTOTEXT, + MACASSGN_GRAPHIC, + MACASSGN_OLE, + MACASSGN_FRMURL, + MACASSGN_INETFMT, + MACASSGN_ALLFRM +}; + +class SwMacroAssignDlg +{ +public: + static SfxEventNamesItem AddEvents( DlgEventType eType ); + static bool INetFormatDlg(weld::Window* pParent, SwWrtShell& rSh, + std::unique_ptr<SvxMacroItem>& rpINetItem ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mailconfigpage.hxx b/sw/source/uibase/inc/mailconfigpage.hxx new file mode 100644 index 000000000..be6dfe137 --- /dev/null +++ b/sw/source/uibase/inc/mailconfigpage.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_UIBASE_INC_MAILCONFIGPAGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MAILCONFIGPAGE_HXX + +#include <sfx2/tabdlg.hxx> +#include <sfx2/basedlgs.hxx> + +class SwTestAccountSettingsDialog; +class SwMailMergeConfigItem; + +class SwMailConfigPage : public SfxTabPage +{ + friend class SwTestAccountSettingsDialog; + + std::unique_ptr<SwMailMergeConfigItem> m_pConfigItem; + + std::unique_ptr<weld::Entry> m_xDisplayNameED; + std::unique_ptr<weld::Entry> m_xAddressED; + std::unique_ptr<weld::CheckButton> m_xReplyToCB; + std::unique_ptr<weld::Label> m_xReplyToFT; + std::unique_ptr<weld::Entry> m_xReplyToED; + std::unique_ptr<weld::Entry> m_xServerED; + std::unique_ptr<weld::SpinButton> m_xPortNF; + std::unique_ptr<weld::CheckButton> m_xSecureCB; + std::unique_ptr<weld::Button> m_xServerAuthenticationPB; + std::unique_ptr<weld::Button> m_xTestPB; + + DECL_LINK(ReplyToHdl, weld::ToggleButton&, void); + DECL_LINK(AuthenticationHdl, weld::Button&, void); + DECL_LINK(TestHdl, weld::Button&, void); + DECL_LINK(SecureHdl, weld::ToggleButton&, void); + +public: + SwMailConfigPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwMailConfigPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +class SwMailConfigDlg : public SfxSingleTabDialogController +{ +public: + SwMailConfigDlg(weld::Window* pParent, SfxItemSet& rSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/maildispatcher.hxx b/sw/source/uibase/inc/maildispatcher.hxx new file mode 100644 index 000000000..c4cdf253c --- /dev/null +++ b/sw/source/uibase/inc/maildispatcher.hxx @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_MAILDISPATCHER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MAILDISPATCHER_HXX + +#include <com/sun/star/mail/XSmtpService.hpp> +#include <com/sun/star/mail/XMailMessage.hpp> +#include <osl/thread.hxx> +#include <osl/conditn.hxx> +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> + +#include <list> +#include <vector> + +#include <swdllapi.h> + +class IMailDispatcherListener; + +/** + A MailDispatcher should be used for sending a bunch a mail messages + asynchronously. Usually a client enqueues a number of mail messages + and then calls start to begin sending them. An instance of this class + must not be shared among different client threads. Instead each client + thread should create an own instance of this class. +*/ +class SW_DLLPUBLIC MailDispatcher + : public salhelper::SimpleReferenceObject + , private ::osl::Thread +{ +public: + // bringing operator new/delete into scope + using osl::Thread::operator new; + using osl::Thread::operator delete; + using osl::Thread::join; + + /** + @param xSmtpService + [in] a reference to a mail server. A user must be + connected to the mail server otherwise errors occur + during the delivery of mail messages. + + @throws css::uno::RuntimeException + on errors during construction of an instance of this class. + */ + MailDispatcher(css::uno::Reference< css::mail::XSmtpService> const & xMailService); + + /** + Shutdown the mail dispatcher. Every mail messages + not yet sent will be discarded. + */ + virtual ~MailDispatcher() override; + + /** + Enqueue a mail message for delivery. A client must + start the mail dispatcher in order to send the + enqueued mail messages. + + @param xMailMessage + [in] a mail message that should be send. + */ + void enqueueMailMessage(css::uno::Reference< css::mail::XMailMessage> const & xMailMessage); + /** + Dequeues a mail message. + This enables the caller to remove attachments when sending mails is to be cancelled. + */ + css::uno::Reference< css::mail::XMailMessage> dequeueMailMessage(); + + /** + Start sending mail messages asynchronously. A client may register + a listener for mail dispatcher events. For every mail message sent + the notification will be sent. While handling such notification a + client may enqueue new mail messages. If there are no more mail + messages to send a respective notification is sent and the mail + dispatcher waits for more mail messages. + + @precond not isStarted() + */ + void start(); + + /** + Stop sending mail messages. + + @precond isStarted() + */ + void stop(); + + /** + Request shutdown of the mail dispatcher thread. + NOTE: You must call this method before you release + your last reference to this class otherwise the + mail dispatcher thread will never end. + */ + void shutdown(); + + /** + Check whether the mail dispatcher is started or not. + + @return + <TRUE/> if the sending thread is running. + */ + bool isStarted() const { return m_bActive; } + + /** returns if the thread is still running + */ + using osl::Thread::isRunning; + + /** + * returns if shutdown has already been called + */ + bool isShutdownRequested() const { return m_bShutdownRequested; } + + /** + * Register a listener for mail dispatcher events + */ + void addListener(::rtl::Reference<IMailDispatcherListener> const & listener); + +protected: + virtual void SAL_CALL run() override; + virtual void SAL_CALL onTerminated() override; + +private: + std::vector< ::rtl::Reference<IMailDispatcherListener> > cloneListener(); + void sendMailMessageNotifyListener(css::uno::Reference< css::mail::XMailMessage> const & message); + +private: + css::uno::Reference< css::mail::XSmtpService> m_xMailserver; + std::list< css::uno::Reference< css::mail::XMailMessage > > m_aXMessageList; + std::vector< ::rtl::Reference<IMailDispatcherListener> > m_aListenerVector; + ::osl::Mutex m_aMessageContainerMutex; + ::osl::Mutex m_aListenerContainerMutex; + ::osl::Mutex m_aThreadStatusMutex; + ::osl::Condition m_aRunCondition; + ::osl::Condition m_aWakeupCondition; + ::rtl::Reference<MailDispatcher> m_xSelfReference; + bool m_bActive; + bool m_bShutdownRequested; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_MAILDISPATCHER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mailmergehelper.hxx b/sw/source/uibase/inc/mailmergehelper.hxx new file mode 100644 index 000000000..3b476a524 --- /dev/null +++ b/sw/source/uibase/inc/mailmergehelper.hxx @@ -0,0 +1,284 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_MAILMERGEHELPER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MAILMERGEHELPER_HXX + +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/mail/XAuthenticator.hpp> +#include <com/sun/star/mail/XConnectionListener.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/mail/XMailMessage.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> +#include <rtl/ustring.hxx> +#include <swdllapi.h> + +class SwMailMergeConfigItem; + +namespace com::sun::star::mail { + class XMailService; + class XSmtpService; +} + +namespace SwMailMergeHelper +{ + SW_DLLPUBLIC OUString CallSaveAsDialog(weld::Window* pParent, OUString& rFilter); + SW_DLLPUBLIC bool CheckMailAddress(const OUString& rMailAddress); + SW_DLLPUBLIC css::uno::Reference<css::mail::XSmtpService> ConnectToSmtpServer( + SwMailMergeConfigItem const & rConfigItem, + css::uno::Reference<css::mail::XMailService>& xInMailService, + const OUString& rInMailServerPassword, + const OUString& rOutMailServerPassword, + weld::Window* pDialogParentWindow = nullptr); +} + +struct SwAddressPreview_Impl; + +// Preview window used to show the possible selection of address blocks +// and also the resulting address filled with database data +class SW_DLLPUBLIC SwAddressPreview : public weld::CustomWidgetController +{ + std::unique_ptr<SwAddressPreview_Impl> pImpl; + std::unique_ptr<weld::ScrolledWindow> m_xVScrollBar; + Link<LinkParamNone*,void> m_aSelectHdl; + + void DrawText_Impl(vcl::RenderContext& rRenderContext, const OUString& rAddress, + const Point& rTopLeft, const Size& rSize, bool bIsSelected); + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + void UpdateScrollBar(); + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&,void); + +public: + SwAddressPreview(std::unique_ptr<weld::ScrolledWindow> xParent); + virtual ~SwAddressPreview() override; + + /** The address string is a list of address elements separated by spaces + and breaks. The addresses fit into the given layout. If more addresses then + rows/columns should be used a scrollbar will be added. + + AddAddress appends the new address to the already added ones. + Initially the first added address will be selected + */ + void AddAddress(const OUString& rAddress); + // for preview mode - replaces the currently used address by the given one + void SetAddress(const OUString& rAddress); + // removes all addresses + void Clear(); + + // returns the selected address + sal_uInt16 GetSelectedAddress() const; + void SelectAddress(sal_uInt16 nSelect); + void ReplaceSelectedAddress(const OUString&); + void RemoveSelectedAddress(); + + // set the number of rows and columns of addresses + void SetLayout(sal_uInt16 nRows, sal_uInt16 nColumns); + void EnableScrollBar(); + + // fill the actual data into a string (address block or greeting) + static OUString FillData(const OUString& rAddress, SwMailMergeConfigItem const & rConfigItem, + const css::uno::Sequence<OUString>* pAssignments = nullptr); + + void SetSelectHdl (const Link<LinkParamNone*,void>& rLink) { m_aSelectHdl = rLink; } +}; + + +// iterate over an address block or a greeting line the iterator returns the +// parts either as pure string or as column +struct SwMergeAddressItem +{ + OUString sText; + bool bIsColumn; + bool bIsReturn; + + SwMergeAddressItem() + : bIsColumn(false) + , bIsReturn(false) + {} +}; + +class SW_DLLPUBLIC SwAddressIterator +{ + OUString sAddress; +public: + SwAddressIterator(const OUString& rAddress) : + sAddress(rAddress) + {} + + SwMergeAddressItem Next(); + bool HasMore() const { return !sAddress.isEmpty(); } +}; + +class SW_DLLPUBLIC SwAuthenticator : + public cppu::WeakImplHelper<css::mail::XAuthenticator> +{ + OUString m_aUserName; + OUString m_aPassword; + weld::Window* m_pParentWindow; +public: + SwAuthenticator() + : m_pParentWindow(nullptr) + {} + SwAuthenticator(const OUString& username, const OUString& password, weld::Window* pParent) + : m_aUserName(username) + , m_aPassword(password) + , m_pParentWindow(pParent) + {} + virtual ~SwAuthenticator() override; + + virtual OUString SAL_CALL getUserName() override; + virtual OUString SAL_CALL getPassword() override; + +}; + +class SW_DLLPUBLIC SwConnectionContext : public cppu::WeakImplHelper<css::uno::XCurrentContext> +{ + OUString m_sMailServer; + sal_Int16 m_nPort; + OUString m_sConnectionType; + +public: + SwConnectionContext(const OUString& rMailServer, sal_Int16 nPort, const OUString& rConnectionType); + virtual ~SwConnectionContext() override; + + virtual css::uno::Any SAL_CALL getValueByName(const OUString& Name) override; +}; + +class SwMutexBase +{ +public: + osl::Mutex m_aMutex; +}; + +class SW_DLLPUBLIC SwConnectionListener : + public SwMutexBase, + public cppu::WeakComponentImplHelper<css::mail::XConnectionListener> +{ + using cppu::WeakComponentImplHelperBase::disposing; + +public: + SwConnectionListener() : + cppu::WeakComponentImplHelper<css::mail::XConnectionListener>(m_aMutex) + {} + virtual ~SwConnectionListener() override; + + virtual void SAL_CALL connected(const css::lang::EventObject& aEvent) override; + + virtual void SAL_CALL disconnected(const css::lang::EventObject& aEvent) override; + + virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override; +}; + +class SW_DLLPUBLIC SwMailTransferable : + public SwMutexBase, + public cppu::WeakComponentImplHelper<css::datatransfer::XTransferable, css::beans::XPropertySet> +{ + OUString m_aMimeType; + OUString m_sBody; + OUString m_aURL; + OUString m_aName; + bool m_bIsBody; + + public: + SwMailTransferable(const OUString& rURL, const OUString& rName, const OUString& rMimeType); + SwMailTransferable(const OUString& rBody, const OUString& rMimeType); + virtual ~SwMailTransferable() override; + virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& aFlavor) override; + + virtual css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() override; + virtual sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& aFlavor) 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; + +}; + +class SW_DLLPUBLIC SwMailMessage : + public SwMutexBase, + public cppu::WeakComponentImplHelper<css::mail::XMailMessage> +{ + OUString m_sSenderName; + OUString m_sSenderAddress; + OUString m_sReplyToAddress; + OUString m_sSubject; + + css::uno::Reference<css::datatransfer::XTransferable> m_xBody; +// css::mail::MailMessageBody m_aBody; + + css::uno::Sequence<OUString> m_aRecipients; + css::uno::Sequence<OUString> m_aCcRecipients; + css::uno::Sequence<OUString> m_aBccRecipients; +// css::uno::Sequence<css::mail::MailAttachmentDescriptor> m_aAttachments; + css::uno::Sequence<css::mail::MailAttachment> m_aAttachments; +public: + SwMailMessage(); + virtual ~SwMailMessage() override; + + // attributes + virtual OUString SAL_CALL getSenderName() override; + virtual OUString SAL_CALL getSenderAddress() override; + virtual OUString SAL_CALL getReplyToAddress() override; + virtual void SAL_CALL setReplyToAddress( const OUString& _replytoaddress ) override; + virtual OUString SAL_CALL getSubject() override; + virtual void SAL_CALL setSubject(const OUString& _subject) override; + + virtual css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getBody() override; + virtual void SAL_CALL setBody(const css::uno::Reference<css::datatransfer::XTransferable>& _body) override; + + // methods + virtual void SAL_CALL addRecipient( const OUString& sRecipientAddress ) override; + virtual void SAL_CALL addCcRecipient( const OUString& sRecipientAddress ) override; + virtual void SAL_CALL addBccRecipient( const OUString& sRecipientAddress ) override; + virtual css::uno::Sequence<OUString> SAL_CALL getRecipients() override; + virtual css::uno::Sequence<OUString> SAL_CALL getCcRecipients() override; + virtual css::uno::Sequence<OUString> SAL_CALL getBccRecipients() override; + virtual void SAL_CALL addAttachment(const css::mail::MailAttachment& aMailAttachment) override; + virtual css::uno::Sequence<css::mail::MailAttachment> SAL_CALL getAttachments() override; + void SetSenderName(const OUString& rSenderName) + { + m_sSenderName = rSenderName; + } + void SetSenderAddress(const OUString& rSenderAddress) + { + m_sSenderAddress = rSenderAddress; + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mailmergewizard.hxx b/sw/source/uibase/inc/mailmergewizard.hxx new file mode 100644 index 000000000..0ab3c8dce --- /dev/null +++ b/sw/source/uibase/inc/mailmergewizard.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_UIBASE_INC_MAILMERGEWIZARD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MAILMERGEWIZARD_HXX + +#include <vcl/roadmapwizard.hxx> +#include <rtl/ustring.hxx> + +class SwView; +class SwMailMergeConfigItem; + +using vcl::WizardTypes::WizardState; +using vcl::WizardTypes::CommitPageReason; + +#define MM_DOCUMENTSELECTPAGE 0 +#define MM_OUTPUTTYPETPAGE 1 +#define MM_ADDRESSBLOCKPAGE 2 +#define MM_GREETINGSPAGE 3 +#define MM_LAYOUTPAGE 4 + +class SwMailMergeWizard : public ::vcl::RoadmapWizardMachine +{ + SwView* m_pSwView; + OUString sDocumentURL; + bool m_bDocumentLoad; + + std::shared_ptr<SwMailMergeConfigItem> m_xConfigItem; + + OUString m_sStarting; + OUString m_sDocumentType; + OUString m_sAddressBlock; + OUString m_sAddressList; + OUString m_sGreetingsLine; + OUString m_sLayout; + + sal_uInt16 m_nRestartPage; + + using vcl::WizardMachine::skipUntil; + +protected: + virtual std::unique_ptr<BuilderPage> createPage( WizardState _nState ) override; + virtual void enterState( WizardState _nState ) override; + + virtual OUString getStateDisplayName( WizardState _nState ) const override; + +public: + SwMailMergeWizard(SwView& rView, std::shared_ptr<SwMailMergeConfigItem> const & rConfigItem); + virtual ~SwMailMergeWizard() override; + + SwView* GetSwView() {return m_pSwView;} + SwMailMergeConfigItem& GetConfigItem() { return *m_xConfigItem;} + + void SetReloadDocument(const OUString& rURL) {sDocumentURL = rURL;} + const OUString& GetReloadDocument() const {return sDocumentURL;} + + //next step requires loading of document + void SetDocumentLoad(bool bSet) {m_bDocumentLoad = bSet;} + + void UpdateRoadmap(); + + sal_uInt16 GetRestartPage() const {return m_nRestartPage;} + void SetRestartPage(sal_uInt16 nPage) { m_nRestartPage = nPage;} + + bool skipUntil( sal_uInt16 nPage) + {return ::vcl::RoadmapWizardMachine::skipUntil(WizardState(nPage));} + + virtual short run() override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mailmrge.hxx b/sw/source/uibase/inc/mailmrge.hxx new file mode 100644 index 000000000..f409c664f --- /dev/null +++ b/sw/source/uibase/inc/mailmrge.hxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_MAILMRGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MAILMRGE_HXX + +#include <memory> +#include <sfx2/basedlgs.hxx> + +#include <com/sun/star/uno/Sequence.h> +#include <com/sun/star/uno/Reference.h> + +#include <dbmgr.hxx> + +class SwWrtShell; +class SwModuleOptions; +class SwXSelChgLstnr_Impl; +struct SwMailMergeDlg_Impl; +namespace com::sun::star{ + namespace frame{ + class XFrame2; + } + namespace sdbc{ + class XResultSet; + class XConnection; + } +} + +class SwMailMergeDlg : public SfxDialogController +{ + friend class SwXSelChgLstnr_Impl; + + std::unique_ptr<SwMailMergeDlg_Impl> pImpl; + + SwWrtShell& rSh; + SwModuleOptions* pModOpt; + + DBManagerOptions nMergeType; + css::uno::Sequence< css::uno::Any > m_aSelection; + css::uno::Reference< css::frame::XFrame2 > m_xFrame; + + OUString m_sSaveFilter; + OUString m_sFilename; + + std::unique_ptr<weld::Container> m_xBeamerWin; + + std::unique_ptr<weld::RadioButton> m_xAllRB; + std::unique_ptr<weld::RadioButton> m_xMarkedRB; + std::unique_ptr<weld::RadioButton> m_xFromRB; + std::unique_ptr<weld::SpinButton> m_xFromNF; + std::unique_ptr<weld::SpinButton> m_xToNF; + + std::unique_ptr<weld::RadioButton> m_xPrinterRB; + std::unique_ptr<weld::RadioButton> m_xMailingRB; + std::unique_ptr<weld::RadioButton> m_xFileRB; + + std::unique_ptr<weld::CheckButton> m_xSingleJobsCB; + std::unique_ptr<weld::CheckButton> m_xPasswordCB; + + std::unique_ptr<weld::Label> m_xSaveMergedDocumentFT; + std::unique_ptr<weld::RadioButton> m_xSaveSingleDocRB; + std::unique_ptr<weld::RadioButton> m_xSaveIndividualRB; + + std::unique_ptr<weld::CheckButton> m_xGenerateFromDataBaseCB; + + std::unique_ptr<weld::Label> m_xColumnFT; + std::unique_ptr<weld::ComboBox> m_xColumnLB; + std::unique_ptr<weld::Label> m_xPasswordFT; + std::unique_ptr<weld::ComboBox> m_xPasswordLB; + std::unique_ptr<weld::Label> m_xPathFT; + std::unique_ptr<weld::Entry> m_xPathED; + std::unique_ptr<weld::Button> m_xPathPB; + std::unique_ptr<weld::Label> m_xFilterFT; + std::unique_ptr<weld::ComboBox> m_xFilterLB; + + std::unique_ptr<weld::ComboBox> m_xAddressFieldLB; + std::unique_ptr<weld::Label> m_xSubjectFT; + std::unique_ptr<weld::Entry> m_xSubjectED; + std::unique_ptr<weld::Label> m_xFormatFT; + std::unique_ptr<weld::Label> m_xAttachFT; + std::unique_ptr<weld::Entry> m_xAttachED; + std::unique_ptr<weld::Button> m_xAttachPB; + std::unique_ptr<weld::CheckButton> m_xFormatHtmlCB; + std::unique_ptr<weld::CheckButton> m_xFormatRtfCB; + std::unique_ptr<weld::CheckButton> m_xFormatSwCB; + + std::unique_ptr<weld::Button> m_xOkBTN; + + DECL_LINK( ButtonHdl, weld::Button&, void ); + DECL_LINK( InsertPathHdl, weld::Button&, void ); + DECL_LINK( OutputTypeHdl, weld::ToggleButton&, void ); + DECL_LINK( FilenameHdl, weld::ToggleButton&, void ); + DECL_LINK( ModifyHdl, weld::SpinButton&, void ); + DECL_LINK( SaveTypeHdl, weld::ToggleButton&, void ); + DECL_LINK( FileFormatHdl, weld::ComboBox&, void ); + + bool ExecQryShell(); + bool AskUserFilename() const; + OUString GetURLfromPath() const; + +public: + SwMailMergeDlg(weld::Window* pParent, SwWrtShell& rSh, + const OUString& rSourceName, + const OUString& rTableName, + sal_Int32 nCommandType, + const css::uno::Reference< css::sdbc::XConnection>& xConnection, + css::uno::Sequence< css::uno::Any > const * pSelection); + virtual ~SwMailMergeDlg() override; + + DBManagerOptions GetMergeType() const { return nMergeType; } + + bool IsSaveSingleDoc() const { return m_xSaveSingleDocRB->get_active(); } + bool IsGenerateFromDataBase() const { return m_xGenerateFromDataBaseCB->get_active(); } + bool IsFileEncryptedFromDataBase() const { return m_xPasswordCB->get_active(); } + OUString GetColumnName() const { return m_xColumnLB->get_active_text(); } + OUString GetPasswordColumnName() const { return m_xPasswordLB->get_active_text(); } + OUString GetTargetURL() const; + + const OUString& GetSaveFilter() const {return m_sSaveFilter;} + const css::uno::Sequence< css::uno::Any >& GetSelection() const { return m_aSelection; } + css::uno::Reference< css::sdbc::XResultSet> GetResultSet() const; + +}; + +class SwMailMergeCreateFromDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::RadioButton> m_xThisDocRB; +public: + SwMailMergeCreateFromDlg(weld::Window* pParent); + virtual ~SwMailMergeCreateFromDlg() override; + bool IsThisDocument() const + { + return m_xThisDocRB->get_active(); + } +}; + +class SwMailMergeFieldConnectionsDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::RadioButton> m_xUseExistingRB; +public: + SwMailMergeFieldConnectionsDlg(weld::Window* pParent); + virtual ~SwMailMergeFieldConnectionsDlg() override; + + bool IsUseExistingConnections() const + { + return m_xUseExistingRB->get_active(); + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mediash.hxx b/sw/source/uibase/inc/mediash.hxx new file mode 100644 index 000000000..b67f903f0 --- /dev/null +++ b/sw/source/uibase/inc/mediash.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_UIBASE_INC_MEDIASH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MEDIASH_HXX + +#include <sfx2/module.hxx> +#include <sfx2/shell.hxx> + +#include "basesh.hxx" +#include <shellid.hxx> + +class SwMediaShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_MEDIASHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void ExecMedia(SfxRequest const &); + void GetMediaState(SfxItemSet &); + + SwMediaShell(SwView &rView); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mergetbl.hxx b/sw/source/uibase/inc/mergetbl.hxx new file mode 100644 index 000000000..11ca8c726 --- /dev/null +++ b/sw/source/uibase/inc/mergetbl.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_MERGETBL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MERGETBL_HXX + +#include <vcl/weld.hxx> + +class SwMergeTableDlg : public weld::GenericDialogController +{ + bool& m_rMergePrev; + + std::unique_ptr<weld::RadioButton> m_xMergePrevRB; +private: + void Apply(); + +public: + SwMergeTableDlg(weld::Window *pParent, bool& rWithPrev); + virtual short run() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/mmconfigitem.hxx b/sw/source/uibase/inc/mmconfigitem.hxx new file mode 100644 index 000000000..bae35cfdc --- /dev/null +++ b/sw/source/uibase/inc/mmconfigitem.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_MMCONFIGITEM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MMCONFIGITEM_HXX + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <memory> +#include <set> +#include <vector> +#include <swdbdata.hxx> +#include <swdllapi.h> +#include "sharedconnection.hxx" + +namespace com::sun::star{ + namespace sdbc{ + class XDataSource; + class XResultSet; + } + namespace sdbcx{ + class XColumnsSupplier; + } +} + +class SwMailMergeConfigItem_Impl; +class SwView; +namespace sw::mark { class IMark; } + +struct SwDocMergeInfo +{ + sw::mark::IMark* startPageInTarget; + long nDBRow; +}; + +class SW_DLLPUBLIC SwMailMergeConfigItem +{ + std::unique_ptr<SwMailMergeConfigItem_Impl> m_pImpl; + //session information - not stored in configuration + bool m_bAddressInserted; + bool m_bGreetingInserted; + sal_Int32 m_nGreetingMoves; + std::set<sal_Int32> m_aExcludedRecords; + css::uno::Reference<css::view::XSelectionChangeListener> m_xDBChangedListener; + + OUString m_sSelectedPrinter; + + SwView* m_pSourceView; + SwView* m_pTargetView; +public: + SwMailMergeConfigItem(); + ~SwMailMergeConfigItem(); + + enum Gender + { + FEMALE, + MALE, + NEUTRAL + }; + + void Commit(); + + const std::vector<std::pair<OUString, int>>& GetDefaultAddressHeaders() const; + + void SetCurrentConnection( + css::uno::Reference< css::sdbc::XDataSource> const & xSource, + const SharedConnection& rConnection, + css::uno::Reference< css::sdbcx::XColumnsSupplier> const & xColumnsSupplier, + const SwDBData& rDBData); + + css::uno::Reference< css::sdbc::XDataSource> const & GetSource() const; + + SharedConnection const & GetConnection() const; + + css::uno::Reference< css::sdbcx::XColumnsSupplier> const & GetColumnsSupplier(); + + css::uno::Reference< css::sdbc::XResultSet> const & GetResultSet() const; + + void DisposeResultSet(); + + OUString& GetFilter() const; + void SetFilter(OUString const &); + + void SetCurrentDBData( const SwDBData& rDBData); + const SwDBData& GetCurrentDBData() const; + + // move absolute, nTarget == -1 -> goto last record + sal_Int32 MoveResultSet(sal_Int32 nTarget); + sal_Int32 GetResultSetPosition()const; + bool IsResultSetFirstLast(bool& bIsFirst, bool& bIsLast); + + bool IsRecordExcluded(sal_Int32 nRecord) const; + void ExcludeRecord(sal_Int32 nRecord, bool bExclude); + css::uno::Sequence< css::uno::Any> GetSelection() const; + + const css::uno::Sequence<OUString>& GetSavedDocuments() const; + + bool IsOutputToLetter()const; + void SetOutputToLetter(bool bSet); + + bool IsAddressBlock()const; + void SetAddressBlock(bool bSet); + + bool IsHideEmptyParagraphs() const; + void SetHideEmptyParagraphs(bool bSet); + + css::uno::Sequence<OUString> GetAddressBlocks() const; + void SetAddressBlocks(const css::uno::Sequence< OUString>& rBlocks); + + void SetCurrentAddressBlockIndex( sal_Int32 nSet ); + sal_Int32 GetCurrentAddressBlockIndex() const; + + bool IsIncludeCountry() const; + OUString& GetExcludeCountry() const; + void SetCountrySettings(bool bSet, const OUString& sCountry); + + bool IsIndividualGreeting(bool bInEMail) const; + void SetIndividualGreeting(bool bSet, bool bInEMail); + + bool IsGreetingLine(bool bInEMail) const; + void SetGreetingLine(bool bSet, bool bInEMail); + + css::uno::Sequence<OUString> GetGreetings(Gender eType) const; + void SetGreetings(Gender eType, const css::uno::Sequence< OUString>& rBlocks); + + sal_Int32 GetCurrentGreeting(Gender eType) const; + void SetCurrentGreeting(Gender eType, sal_Int32 nIndex); + + //the content of the gender column that marks it as female + const OUString& GetFemaleGenderValue() const; + void SetFemaleGenderValue(const OUString& rValue); + + //returns the assignment in the order of the default headers (GetDefaultAddressHeaders()) + css::uno::Sequence<OUString> GetColumnAssignment( const SwDBData& rDBData ) const; + void SetColumnAssignment( + const SwDBData& rDBData, + const css::uno::Sequence< OUString>& ); + + bool IsAddressFieldsAssigned() const; + bool IsGreetingFieldsAssigned() const; + + //e-Mail settings: + OUString const & GetMailDisplayName() const; + void SetMailDisplayName(const OUString& rName); + + OUString const & GetMailAddress() const; + void SetMailAddress(const OUString& rAddress); + + bool IsMailReplyTo() const; + void SetMailReplyTo(bool bSet); + + OUString const & GetMailReplyTo() const; + void SetMailReplyTo(const OUString& rReplyTo); + + OUString const & GetMailServer() const; + void SetMailServer(const OUString& rAddress); + + sal_Int16 GetMailPort() const; + void SetMailPort(sal_Int16 nSet); + + bool IsSecureConnection() const; + void SetSecureConnection(bool bSet); + + bool IsAuthentication() const; + void SetAuthentication(bool bSet); + + OUString const & GetMailUserName() const; + void SetMailUserName(const OUString& rName); + + OUString const & GetMailPassword() const; + void SetMailPassword(const OUString& rPassword); + + bool IsSMTPAfterPOP() const; + void SetSMTPAfterPOP(bool bSet); + + OUString const & GetInServerName() const; + void SetInServerName(const OUString& rServer); + + sal_Int16 GetInServerPort() const; + void SetInServerPort(sal_Int16 nSet); + + bool IsInServerPOP() const; + void SetInServerPOP(bool bSet); + + OUString const & GetInServerUserName() const; + void SetInServerUserName(const OUString& rName); + + OUString const & GetInServerPassword() const; + void SetInServerPassword(const OUString& rPassword); + + //session information + bool IsAddressInserted() const { return m_bAddressInserted; } + void SetAddressInserted() + { + m_bAddressInserted = true; + } + + bool IsGreetingInserted() const + { return m_bGreetingInserted; } + void SetGreetingInserted() + { m_bGreetingInserted = true; } + + // counts the moves in the layout page + void MoveGreeting( sal_Int32 nMove) { m_nGreetingMoves += nMove;} + sal_Int32 GetGreetingMoves() const { return m_nGreetingMoves;} + + // new source document - reset some flags + void DocumentReloaded(); + + bool IsMailAvailable() const; + + // notify a completed merge, provide the appropriate e-Mail address if available + void AddMergedDocument(SwDocMergeInfo const & rInfo); + //returns the page and database cursor information of each merged document + SwDocMergeInfo& GetDocumentMergeInfo(sal_uInt32 nDocument); + sal_uInt32 GetMergedDocumentCount(); + + const OUString& GetSelectedPrinter() const + { return m_sSelectedPrinter; } + void SetSelectedPrinter(const OUString& rSet) + { m_sSelectedPrinter = rSet; } + + SwView* GetTargetView(); + void SetTargetView(SwView* pView); + + SwView* GetSourceView(); + void SetSourceView(SwView* pView); + + //helper methods + OUString GetAssignedColumn(sal_uInt32 nColumn) const; + void stopDBChangeListening(); + void updateCurrentDBDataFromDocument(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/multmrk.hxx b/sw/source/uibase/inc/multmrk.hxx new file mode 100644 index 000000000..6edca05ce --- /dev/null +++ b/sw/source/uibase/inc/multmrk.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_MULTMRK_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_MULTMRK_HXX + +#include <vcl/weld.hxx> + +class SwTOXMgr; + +// insert mark for index entry +class SwMultiTOXMarkDlg : public weld::GenericDialogController +{ + DECL_LINK(SelectHdl, weld::TreeView&, void); + SwTOXMgr& m_rMgr; + sal_uInt16 m_nPos; + + std::unique_ptr<weld::Label> m_xTextFT; + std::unique_ptr<weld::TreeView> m_xTOXLB; + +public: + SwMultiTOXMarkDlg(weld::Window* pParent, SwTOXMgr &rTOXMgr); + virtual ~SwMultiTOXMarkDlg() override; + virtual short run() override; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_MULTMRK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/navicfg.hxx b/sw/source/uibase/inc/navicfg.hxx new file mode 100644 index 000000000..a9b562493 --- /dev/null +++ b/sw/source/uibase/inc/navicfg.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_NAVICFG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NAVICFG_HXX + +#include <unotools/configitem.hxx> + +enum class RegionMode; +enum class ContentTypeId; + +class SwNavigationConfig : public utl::ConfigItem +{ + ContentTypeId nRootType; //RootType + sal_Int32 nSelectedPos; //SelectedPosition + sal_Int32 nOutlineLevel; //OutlineLevel + RegionMode nRegionMode; //InsertMode + sal_Int32 nActiveBlock; //ActiveBlock//Expand/CollapsState + bool bIsSmall; //ShowListBox + bool bIsGlobalActive; //GlobalDocMode// global view for GlobalDoc valid? + + static css::uno::Sequence<OUString> GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwNavigationConfig(); + virtual ~SwNavigationConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + + ContentTypeId GetRootType()const {return nRootType;} + void SetRootType(ContentTypeId nSet){ + if(nRootType != nSet) + { + SetModified(); + nRootType = nSet; + } + } + + sal_Int32 GetOutlineLevel()const {return nOutlineLevel;} + void SetOutlineLevel(sal_Int32 nSet){ + if(nOutlineLevel != nSet) + { + SetModified(); + nOutlineLevel = nSet; + } + } + + RegionMode GetRegionMode()const {return nRegionMode;} + void SetRegionMode(RegionMode nSet){ + if(nRegionMode != nSet) + { + SetModified(); + nRegionMode = nSet; + } + } + + sal_Int32 GetActiveBlock()const {return nActiveBlock;} + void SetActiveBlock(sal_Int32 nSet){ + if(nActiveBlock != nSet) + { + SetModified(); + nActiveBlock = nSet; + } + } + + bool IsSmall() const {return bIsSmall;} + void SetSmall(bool bSet){ + if(bIsSmall != bSet) + { + SetModified(); + bIsSmall = bSet; + } + } + + bool IsGlobalActive() const {return bIsGlobalActive;} + void SetGlobalActive(bool bSet){ + if(bIsGlobalActive != bSet) + { + SetModified(); + bIsGlobalActive = bSet; + } + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/navicont.hxx b/sw/source/uibase/inc/navicont.hxx new file mode 100644 index 000000000..b6842ad91 --- /dev/null +++ b/sw/source/uibase/inc/navicont.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_UIBASE_INC_NAVICONT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NAVICONT_HXX + +#include <rtl/ustring.hxx> + +class SwDocShell; +class TransferDataContainer; +class TransferableDataHelper; +enum class RegionMode; + +/* + navigator bookmark for distinct identification in Sw +*/ + +class NaviContentBookmark +{ + OUString aUrl; // URL including jump mark + OUString aDescr; // description + long nDocSh; // address of DocShell + RegionMode nDefDrag; // description contains defaultDragType + +public: + NaviContentBookmark(); + NaviContentBookmark( const OUString &rUrl, const OUString& rDesc, + RegionMode nDragType, const SwDocShell* ); + + const OUString& GetURL() const { return aUrl; } + const OUString& GetDescription() const { return aDescr; } + RegionMode GetDefaultDragType() const { return nDefDrag; } + void Copy( TransferDataContainer& rData ) const; + bool Paste( TransferableDataHelper& rData ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/navipi.hxx b/sw/source/uibase/inc/navipi.hxx new file mode 100644 index 000000000..fee94525d --- /dev/null +++ b/sw/source/uibase/inc/navipi.hxx @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_NAVIPI_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NAVIPI_HXX + +#include <vcl/idle.hxx> +#include <svl/lstner.hxx> +#include <vcl/transfer.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/tbxctrl.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <sfx2/sidebar/SidebarToolBox.hxx> +#include <sfx2/weldutils.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include "conttree.hxx" +#include <ndarr.hxx> +#include <memory> + +class SwWrtShell; +class SwNavigationPI; +class SwNavigationChild; +class SfxBindings; +class SwNavigationConfig; +class SwView; +class SfxObjectShellLock; +class SfxChildWindowContext; +enum class RegionMode; +class SpinField; + +class SwNavigationPI : public PanelLayout + , public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface + , public SfxListener +{ + friend class SwNavigationChild; + friend class SwContentTree; + friend class SwGlobalTree; + friend class SwNavigationPIUIObject; + + ::sfx2::sidebar::ControllerItem m_aDocFullName; + ::sfx2::sidebar::ControllerItem m_aPageStats; + + std::unique_ptr<weld::Toolbar> m_xContent1ToolBox; + std::unique_ptr<weld::Toolbar> m_xContent2ToolBox; + std::unique_ptr<weld::Toolbar> m_xContent3ToolBox; + std::unique_ptr<ToolbarUnoDispatcher> m_xContent1Dispatch; + std::unique_ptr<weld::Menu> m_xHeadingsMenu; + std::unique_ptr<weld::Menu> m_xDragModeMenu; + std::unique_ptr<weld::Menu> m_xUpdateMenu; + std::unique_ptr<weld::Menu> m_xInsertMenu; + std::unique_ptr<weld::Toolbar> m_xGlobalToolBox; + std::unique_ptr<weld::SpinButton> m_xEdit; + std::unique_ptr<weld::Widget> m_xContentBox; + std::unique_ptr<SwContentTree> m_xContentTree; + std::unique_ptr<weld::Widget> m_xGlobalBox; + std::unique_ptr<SwGlobalTree> m_xGlobalTree; + std::unique_ptr<weld::ComboBox> m_xDocListBox; + Idle m_aPageChgIdle; + OUString m_sContentFileName; + OUString m_aStatusArr[4]; + + std::unique_ptr<SfxObjectShellLock> m_pxObjectShell; + SwView *m_pContentView; + SwWrtShell *m_pContentWrtShell; + SwView *m_pActContView; + SwView *m_pCreateView; + + SwNavigationConfig *m_pConfig; + SfxBindings &m_rBindings; + + RegionMode m_nRegionMode; // 0 - URL, 1 - region with link 2 - region without link + Size m_aExpandedSize; + + bool m_bIsZoomedIn : 1; + bool m_bGlobalMode : 1; + + bool IsZoomedIn() const {return m_bIsZoomedIn;} + void ZoomOut(); + void ZoomIn(); + + void FillBox(); + + DECL_LINK( DocListBoxSelectHdl, weld::ComboBox&, void ); + DECL_LINK( ToolBoxSelectHdl, const OString&, void ); + DECL_LINK( ToolBoxClickHdl, const OString&, void ); + DECL_LINK( ToolBox2DropdownClickHdl, const OString&, void ); + DECL_LINK( ToolBox3DropdownClickHdl, const OString&, void ); + DECL_LINK( DoneLink, SfxPoolItem const *, void ); + DECL_LINK( DropModeMenuSelectHdl, const OString&, void ); + DECL_LINK( HeadingsMenuSelectHdl, const OString&, void ); + DECL_LINK( GlobalMenuSelectHdl, const OString&, void ); + DECL_LINK( ChangePageHdl, Timer*, void ); + DECL_LINK( PageEditModifyHdl, weld::SpinButton&, void ); + DECL_LINK( EditActionHdl, weld::Entry&, bool ); + bool EditAction(); + void UsePage(); + +protected: + + // release ObjectShellLock early enough for app end + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + void ToggleTree(); + void SetGlobalMode(bool bSet) {m_bGlobalMode = bSet;} + +public: + + static VclPtr<vcl::Window> Create(vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + SwNavigationPI(vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* _pBindings); + virtual ~SwNavigationPI() override; + virtual void dispose() override; + + void UpdateListBox(); + void MoveOutline(SwOutlineNodes::size_type nSource, SwOutlineNodes::size_type nTarget); + + virtual void NotifyItemUpdate(const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState(const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + virtual void StateChanged(StateChangedType nStateChange) override; + + static OUString CreateDropFileName( TransferableDataHelper& rData ); + static OUString CleanEntry(const OUString& rEntry); + + RegionMode GetRegionDropMode() const {return m_nRegionMode;} + void SetRegionDropMode(RegionMode nNewMode); + + sal_Int8 AcceptDrop(); + sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ); + + bool IsGlobalDoc() const; + bool IsGlobalMode() const {return m_bGlobalMode;} + + SwView* GetCreateView() const; + + FactoryFunction GetUITestFactory() const override; +}; + +class SwNavigationChild : public SfxChildWindowContext +{ +public: + SwNavigationChild( vcl::Window* , + sal_uInt16 nId, + SfxBindings* ); + + //! soon obsolete ! + static std::unique_ptr<SfxChildWindowContext> CreateImpl(vcl::Window *pParent, + SfxBindings *pBindings, SfxChildWinInfo* pInfo ); + static void RegisterChildWindowContext(SfxModule *pMod); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/navmgr.hxx b/sw/source/uibase/inc/navmgr.hxx new file mode 100644 index 000000000..858b990fd --- /dev/null +++ b/sw/source/uibase/inc/navmgr.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/. + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_NAVMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NAVMGR_HXX + +#include <vector> + +#include <unocrsr.hxx> + +class SwWrtShell; +struct SwPosition; +class SwUnoCursor; + +class SwNavigationMgr final : public SfxListener +{ +private: + /* + * List of entries in the navigation history + * Entries are SwUnoCursor because these gets corrected automatically + * when nodes are deleted. + * + * The navigation history behaves as a stack, to which items are added when we jump to a new position + * (e.g. click a link, or double click an entry from the navigator). + * Every use of the back/forward buttons results in moving the stack pointer within the navigation history + */ + typedef std::vector< sw::UnoCursorPointer > Stack_t; + Stack_t m_entries; + Stack_t::size_type m_nCurrent; /* Current position within the navigation history */ + SwWrtShell & m_rMyShell; /* The active shell within which the navigation occurs */ + + void GotoSwPosition(const SwPosition &rPos); + +public: + /* Constructor that initializes the shell to the current shell */ + SwNavigationMgr( SwWrtShell & rShell ); + ~SwNavigationMgr() override; + /* Can we go back in the history ? */ + bool backEnabled() ; + /* Can we go forward in the history ? */ + bool forwardEnabled(); + /* The method that is called when we click the back button */ + void goBack() ; + /* The method that is called when we click the forward button */ + void goForward() ; + /* The method that adds the position pPos to the navigation history */ + bool addEntry(const SwPosition& rPos); + /* to get notified if our cursors self-destruct */ + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/navsh.hxx b/sw/source/uibase/inc/navsh.hxx new file mode 100644 index 000000000..4bc26ecb1 --- /dev/null +++ b/sw/source/uibase/inc/navsh.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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_UIBASE_INC_NAVSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NAVSH_HXX + +#include <sfx2/module.hxx> +#include <sfx2/shell.hxx> + +#include "basesh.hxx" +#include <shellid.hxx> + +class SwNavigationShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_NAVIGATIONSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwNavigationShell(SwView &rView); + + void GetState(SfxItemSet &); + void Execute(SfxRequest const &); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/num.hxx b/sw/source/uibase/inc/num.hxx new file mode 100644 index 000000000..9d5360669 --- /dev/null +++ b/sw/source/uibase/inc/num.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_UIBASE_INC_NUM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NUM_HXX + +#include <sfx2/tabdlg.hxx> +#include "numprevw.hxx" +#include <numrule.hxx> + +class SwWrtShell; +class SvxBrushItem; +class SwOutlineTabDialog; + +class SwNumPositionTabPage : public SfxTabPage +{ + std::unique_ptr<SwNumRule> pActNum; + SwNumRule* pSaveNum; + SwWrtShell* pWrtSh; + + SwOutlineTabDialog* pOutlineDlg; + sal_uInt16 nActNumLvl; + + bool bModified : 1; + bool bPreset : 1; + bool bInInintControl : 1; // work around modify-error; should be resolved from 391 on + bool bLabelAlignmentPosAndSpaceModeActive; + + NumberingPreview m_aPreviewWIN; + + std::unique_ptr<weld::TreeView> m_xLevelLB; + std::unique_ptr<weld::Widget> m_xPositionFrame; + + // former set of controls shown for numbering rules containing list level + // attributes in SvxNumberFormat::SvxNumPositionAndSpaceMode == LABEL_WIDTH_AND_POSITION + std::unique_ptr<weld::Label> m_xDistBorderFT; + std::unique_ptr<weld::MetricSpinButton> m_xDistBorderMF; + std::unique_ptr<weld::CheckButton> m_xRelativeCB; + std::unique_ptr<weld::Label> m_xIndentFT; + std::unique_ptr<weld::MetricSpinButton> m_xIndentMF; + std::unique_ptr<weld::Label> m_xDistNumFT; + std::unique_ptr<weld::MetricSpinButton> m_xDistNumMF; + std::unique_ptr<weld::Label> m_xAlignFT; + std::unique_ptr<weld::ComboBox> m_xAlignLB; + + // new set of controls shown for numbering rules containing list level + // attributes in SvxNumberFormat::SvxNumPositionAndSpaceMode == LABEL_ALIGNMENT + std::unique_ptr<weld::Label> m_xLabelFollowedByFT; + std::unique_ptr<weld::ComboBox> m_xLabelFollowedByLB; + std::unique_ptr<weld::Label> m_xListtabFT; + std::unique_ptr<weld::MetricSpinButton> m_xListtabMF; + std::unique_ptr<weld::Label> m_xAlign2FT; + std::unique_ptr<weld::ComboBox> m_xAlign2LB; + std::unique_ptr<weld::Label> m_xAlignedAtFT; + std::unique_ptr<weld::MetricSpinButton> m_xAlignedAtMF; + std::unique_ptr<weld::Label> m_xIndentAtFT; + std::unique_ptr<weld::MetricSpinButton> m_xIndentAtMF; + std::unique_ptr<weld::Button> m_xStandardPB; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + + + void InitControls(); + + DECL_LINK(LevelHdl, weld::TreeView&, void); + DECL_LINK(EditModifyHdl, weld::ComboBox&, void); + DECL_LINK(DistanceHdl, weld::MetricSpinButton&, void); + DECL_LINK(RelativeHdl, weld::ToggleButton&, void); + DECL_LINK(StandardHdl, weld::Button&, void); + + void InitPosAndSpaceMode(); + void ShowControlsDependingOnPosAndSpaceMode(); + + DECL_LINK(LabelFollowedByHdl_Impl, weld::ComboBox&, void); + DECL_LINK(ListtabPosHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(AlignAtHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(IndentAtHdl_Impl, weld::MetricSpinButton&, void); + +public: + + SwNumPositionTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwNumPositionTabPage() override; + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + void SetOutlineTabDialog(SwOutlineTabDialog* pDlg){pOutlineDlg = pDlg;} + void SetWrtShell(SwWrtShell* pSh); +#ifdef DBG_UTIL + void SetModified(); +#else + void SetModified() + { + bModified = true; + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + } +#endif +}; + +class SwSvxNumBulletTabDialog final : public SfxTabDialogController +{ + SwWrtShell& rWrtSh; + + virtual short Ok() override; + virtual void PageCreated(const OString& rPageId, SfxTabPage& rPage) override; + DECL_LINK(RemoveNumberingHdl, weld::Button&, void); + + std::unique_ptr<weld::ComboBox> m_xDummyCombo; + +public: + SwSvxNumBulletTabDialog(weld::Window* pParent, + const SfxItemSet* pSwItemSet, + SwWrtShell &); + virtual ~SwSvxNumBulletTabDialog() override; +}; +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_NUM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/numberingtypelistbox.hxx b/sw/source/uibase/inc/numberingtypelistbox.hxx new file mode 100644 index 000000000..b264dcf8c --- /dev/null +++ b/sw/source/uibase/inc/numberingtypelistbox.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_NUMBERINGTYPELISTBOX_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NUMBERINGTYPELISTBOX_HXX + +#include <memory> +#include <vcl/weld.hxx> +#include <swdllapi.h> +#include <o3tl/typed_flags_set.hxx> +#include <editeng/svxenum.hxx> + +enum class SwInsertNumTypes +{ + NoNumbering = 0x01, + Extended = 0x02 +}; + +namespace o3tl { + template<> struct typed_flags<SwInsertNumTypes> : is_typed_flags<SwInsertNumTypes, 0x03> {}; +}; + +struct SwNumberingTypeListBox_Impl; + +class SW_DLLPUBLIC SwNumberingTypeListBox +{ + std::unique_ptr<weld::ComboBox> m_xWidget; + std::unique_ptr<SwNumberingTypeListBox_Impl> m_xImpl; + +public: + SwNumberingTypeListBox(std::unique_ptr<weld::ComboBox> pWidget); + ~SwNumberingTypeListBox(); + + void connect_changed(const Link<weld::ComboBox&, void>& rLink) { m_xWidget->connect_changed(rLink); } + + void Reload(SwInsertNumTypes nTypeFlags); + SvxNumType GetSelectedNumberingType() const; + bool SelectNumberingType(SvxNumType nType); + void SetNoSelection() { m_xWidget->set_active(-1); } + void set_sensitive(bool bEnable) { m_xWidget->set_sensitive(bEnable); } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/numfmtlb.hxx b/sw/source/uibase/inc/numfmtlb.hxx new file mode 100644 index 000000000..f4f75e32d --- /dev/null +++ b/sw/source/uibase/inc/numfmtlb.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_NUMFMTLB_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NUMFMTLB_HXX + +#include <vcl/weld.hxx> +#include <svl/zforlist.hxx> +#include <swdllapi.h> + +class SwView; + +class SW_DLLPUBLIC SwNumFormatBase +{ +protected: + SvNumFormatType nCurrFormatType; + bool mbCurrFormatTypeNeedsInit; + sal_Int32 nStdEntry; + bool bOneArea; + sal_uInt32 nDefFormat; + LanguageType eCurLanguage; + bool bShowLanguageControl; //determine whether the language control has + //to be shown in the number format dialog + bool bUseAutomaticLanguage;//determine whether language is automatically assigned +public: + SwNumFormatBase(); + + void SetAutomaticLanguage(bool bSet) { bUseAutomaticLanguage = bSet; } + bool IsAutomaticLanguage()const { return bUseAutomaticLanguage; } + SvNumFormatType GetFormatType() const { return nCurrFormatType; } + LanguageType GetCurLanguage() const { return eCurLanguage;} + void SetLanguage(LanguageType eSet) { eCurLanguage = eSet; } + void SetShowLanguageControl(bool bSet) { bShowLanguageControl = bSet; } + SAL_DLLPRIVATE static double GetDefValue(const SvNumFormatType nFormatType); + void SetOneArea(bool bOnlyOne) { bOneArea = bOnlyOne; } + + void SetFormatType(const SvNumFormatType nFormatType); + void SetDefFormat(const sal_uInt32 nDefFormat); + virtual sal_uInt32 GetFormat() const = 0; + + virtual void Init(); + void CallSelectHdl(); + + virtual void clear(); + virtual int get_count() const = 0; + virtual int get_active() const = 0; + virtual OUString get_id(int nPos) const = 0; + virtual OUString get_text(int nPos) const = 0; + virtual weld::Widget& get_widget() const = 0; + virtual void append(const OUString& rId, const OUString& rText) = 0; + virtual void append_text(const OUString& rText) = 0; + virtual void insert_text(int nPos, const OUString& rText) = 0; + virtual void set_active(int nPos) = 0; + virtual void set_id(int nPos, const OUString& rId) = 0; + virtual ~SwNumFormatBase() {} +}; + +class SW_DLLPUBLIC NumFormatListBox : public SwNumFormatBase +{ + std::unique_ptr<weld::ComboBox> mxControl; + + DECL_DLLPRIVATE_LINK( SelectHdl, weld::ComboBox&, void ); + + virtual void Init() override; + +public: + NumFormatListBox(std::unique_ptr<weld::ComboBox> xControl); + + virtual sal_uInt32 GetFormat() const override; + + virtual void clear() override; + virtual int get_count() const override { return mxControl->get_count(); } + virtual int get_active() const override { return mxControl->get_active(); } + virtual OUString get_id(int nPos) const override { return mxControl->get_id(nPos); } + virtual OUString get_text(int nPos) const override { return mxControl->get_text(nPos); } + virtual weld::Widget& get_widget() const override { return *mxControl; } + virtual void append(const OUString& rId, const OUString& rText) override { mxControl->append(rId, rText); } + virtual void append_text(const OUString& rText) override { mxControl->append_text(rText); } + virtual void insert_text(int nPos, const OUString& rText) override { mxControl->insert_text(nPos, rText); } + virtual void set_active(int nPos) override { mxControl->set_active(nPos); } + virtual void set_id(int nPos, const OUString& rId) override { mxControl->set_id(nPos, rId); } + void show() { mxControl->show(); } + void hide() { mxControl->hide(); } + + void set_sensitive(bool bSensitive) { mxControl->set_sensitive(bSensitive); } + void connect_changed(const Link<weld::ComboBox&, void>& rLink) { mxControl->connect_changed(rLink); } +}; + +class SW_DLLPUBLIC SwNumFormatTreeView : public SwNumFormatBase +{ + std::unique_ptr<weld::TreeView> mxControl; + + DECL_DLLPRIVATE_LINK( SelectHdl, weld::TreeView&, void ); + + virtual void Init() override; + +public: + SwNumFormatTreeView(std::unique_ptr<weld::TreeView> xControl); + + virtual sal_uInt32 GetFormat() const override; + + virtual void clear() override; + virtual int get_count() const override { return mxControl->n_children(); } + virtual int get_active() const override { return mxControl->get_selected_index(); } + virtual OUString get_id(int nPos) const override { return mxControl->get_id(nPos); } + virtual OUString get_text(int nPos) const override { return mxControl->get_text(nPos); } + virtual weld::Widget& get_widget() const override { return *mxControl; } + virtual void append(const OUString& rId, const OUString& rText) override { mxControl->append(rId, rText); } + virtual void append_text(const OUString& rText) override { mxControl->append_text(rText); } + virtual void insert_text(int nPos, const OUString& rText) override { mxControl->insert_text(nPos, rText); } + virtual void set_active(int nPos) override { mxControl->select(nPos); } + virtual void set_id(int nPos, const OUString& rId) override { mxControl->set_id(nPos, rId); } + OUString get_selected_text() const { return mxControl->get_selected_text(); } + bool get_visible() const { return mxControl->get_visible(); } + bool get_value_changed_from_saved() const { return mxControl->get_value_changed_from_saved(); } + void save_value() { return mxControl->save_value(); } + void show() { mxControl->show(); } + void hide() { mxControl->hide(); } + int get_selected_index() const { return mxControl->get_selected_index(); } + void set_visible(bool bVisible) { mxControl->set_visible(bVisible); } + void select(int nPos) { mxControl->select(nPos); } + void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { mxControl->connect_row_activated(rLink); } + + void set_sensitive(bool bSensitive) { mxControl->set_sensitive(bSensitive); } + void connect_changed(const Link<weld::TreeView&, void>& rLink) { mxControl->connect_changed(rLink); } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/numpara.hxx b/sw/source/uibase/inc/numpara.hxx new file mode 100644 index 000000000..1589220e6 --- /dev/null +++ b/sw/source/uibase/inc/numpara.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_UIBASE_INC_NUMPARA_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NUMPARA_HXX + +#include <sfx2/tabdlg.hxx> +#include <svl/style.hxx> + +// with this TabPage numbering settings at the paragraph / paragraph style +// are performed. +class SwParagraphNumTabPage final : public SfxTabPage +{ + // #outlinelevel# + const OUString msOutlineNumbering; + + bool bModified : 1; + bool bCurNumrule : 1; + + std::unique_ptr<weld::Widget> m_xOutlineStartBX; + std::unique_ptr<weld::ComboBox> m_xOutlineLvLB; + std::unique_ptr<weld::Widget> m_xNumberStyleBX; + std::unique_ptr<weld::ComboBox> m_xNumberStyleLB; + std::unique_ptr<weld::Button> m_xEditNumStyleBtn; + + std::unique_ptr<weld::CheckButton> m_xNewStartCB; + std::unique_ptr<weld::Widget> m_xNewStartBX; + std::unique_ptr<weld::CheckButton> m_xNewStartNumberCB; + std::unique_ptr<weld::SpinButton> m_xNewStartNF; + + std::unique_ptr<weld::Widget> m_xCountParaFram; + std::unique_ptr<weld::CheckButton> m_xCountParaCB; + std::unique_ptr<weld::CheckButton> m_xRestartParaCountCB; + + std::unique_ptr<weld::Widget> m_xRestartBX; + std::unique_ptr<weld::SpinButton> m_xRestartNF; + + DECL_LINK(NewStartHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(StyleHdl_Impl, weld::ComboBox&,void); + DECL_LINK(LineCountHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(EditNumStyleHdl_Impl, weld::Button&, void); + DECL_LINK(EditNumStyleSelectHdl_Impl, weld::ComboBox&, void); + + static const sal_uInt16 aPageRg[]; + + static bool ExecuteEditNumStyle_Impl( sal_uInt16 nId, const OUString& rStr, + SfxStyleFamily nFamily ); + +public: + SwParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ); + virtual ~SwParagraphNumTabPage() override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rSet ); + static const sal_uInt16* GetRanges() { return aPageRg; } + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + virtual void ChangesApplied() override; + + void EnableNewStart(); + void DisableOutline(); + void DisableNumbering(); + + weld::ComboBox& GetStyleBox() {return *m_xNumberStyleLB;}; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/numprevw.hxx b/sw/source/uibase/inc/numprevw.hxx new file mode 100644 index 000000000..a5510a6d4 --- /dev/null +++ b/sw/source/uibase/inc/numprevw.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_NUMPREVW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_NUMPREVW_HXX + +#include <vcl/customweld.hxx> + +class SwNumRule; +namespace rtl { class OUString; } + +class NumberingPreview : public weld::CustomWidgetController +{ + const SwNumRule* pActNum; + vcl::Font aStdFont; + long nPageWidth; + const OUString* pOutlineNames; + bool bPosition; + sal_uInt16 nActLevel; + +private: + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + +public: + NumberingPreview() + : pActNum(nullptr) + , nPageWidth(0) + , pOutlineNames(nullptr) + , bPosition(false) + , nActLevel(USHRT_MAX) + { + } + + void SetNumRule(const SwNumRule* pNum) + { + pActNum = pNum; + Invalidate(); + } + + void SetPageWidth(long nPgWidth) + {nPageWidth = nPgWidth;} + void SetOutlineNames(const OUString* pNames) + {pOutlineNames = pNames;} + void SetPositionMode() + { bPosition = true;} + void SetLevel(sal_uInt16 nSet) {nActLevel = nSet;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/olesh.hxx b/sw/source/uibase/inc/olesh.hxx new file mode 100644 index 000000000..30979029e --- /dev/null +++ b/sw/source/uibase/inc/olesh.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_OLESH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OLESH_HXX + +#include "frmsh.hxx" + +class SwOleShell: public SwFrameShell +{ +public: + SFX_DECL_INTERFACE(SW_OLESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwOleShell(SwView &rView); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/olmenu.hxx b/sw/source/uibase/inc/olmenu.hxx new file mode 100644 index 000000000..047c21083 --- /dev/null +++ b/sw/source/uibase/inc/olmenu.hxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_OLMENU_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OLMENU_HXX + +#include <com/sun/star/linguistic2/XDictionary.hpp> +#include <com/sun/star/linguistic2/XSpellAlternatives.hpp> +#include <com/sun/star/linguistic2/ProofreadingResult.hpp> +#include <com/sun/star/uno/Sequence.h> + +#include <rtl/ustring.hxx> +#include <vcl/builder.hxx> +#include <vcl/menu.hxx> + +#include <map> +#include <vector> + +#include <swdllapi.h> + +//! Don't change these values. You may break context menu modifying extensions! +#define MN_IGNORE_SELECTION 201 +#define MN_SHORT_COMMENT 208 +#define MN_EXPLANATION_LINK 209 + +// id range for dictionaries sub menu +#define MN_DICTIONARIES_START 300 +#define MN_DICTIONARIES_END (MN_DICTIONARIES_START + 99) + +// id range for suggestions from spell and grammar checker +#define MN_SUGGESTION_START 500 +#define MN_SUGGESTION_END (MN_SUGGESTION_START + MN_MAX_NUM_LANG) + +// id range for auto correction sub menu entries +#define MN_AUTOCORR_START 700 +#define MN_AUTOCORR_END (MN_AUTOCORR_START + MN_MAX_NUM_LANG) + +// max number of language entries sub menus +#define MN_MAX_NUM_LANG 99 + +#define MN_NONE_OFFSET (MN_MAX_NUM_LANG + 1) +#define MN_RESET_OFFSET (MN_MAX_NUM_LANG + 2) +#define MN_MORE_OFFSET (MN_MAX_NUM_LANG + 3) + +// id range for 'set language for selection' sub menu entries +#define MN_SET_LANGUAGE_SELECTION_START 900 +#define MN_SET_LANGUAGE_SELECTION_END (MN_SET_LANGUAGE_SELECTION_START + MN_MAX_NUM_LANG) +#define MN_SET_SELECTION_NONE (MN_SET_LANGUAGE_SELECTION_START + MN_NONE_OFFSET) +#define MN_SET_SELECTION_RESET (MN_SET_LANGUAGE_SELECTION_START + MN_RESET_OFFSET) +#define MN_SET_SELECTION_MORE (MN_SET_LANGUAGE_SELECTION_START + MN_MORE_OFFSET) + +// id range for 'set language for paragraph' sub menu entries +#define MN_SET_LANGUAGE_PARAGRAPH_START 1100 +#define MN_SET_LANGUAGE_PARAGRAPH_END (MN_SET_LANGUAGE_PARAGRAPH_START + MN_MAX_NUM_LANG) +#define MN_SET_PARA_NONE (MN_SET_LANGUAGE_PARAGRAPH_START + MN_NONE_OFFSET) +#define MN_SET_PARA_RESET (MN_SET_LANGUAGE_PARAGRAPH_START + MN_RESET_OFFSET) +#define MN_SET_PARA_MORE (MN_SET_LANGUAGE_PARAGRAPH_START + MN_MORE_OFFSET) + +class SwWrtShell; + +class SW_DLLPUBLIC SwSpellPopup +{ + VclBuilder m_aBuilder; + VclPtr<PopupMenu> m_xPopupMenu; + sal_uInt16 m_nIgnoreWordId; + sal_uInt16 m_nAddMenuId; + sal_uInt16 m_nAddId; + sal_uInt16 m_nSpellDialogId; + sal_uInt16 m_nCorrectMenuId; + sal_uInt16 m_nCorrectDialogId; + sal_uInt16 m_nLangSelectionMenuId; + sal_uInt16 m_nLangParaMenuId; + sal_uInt16 m_nRedlineAcceptId; + sal_uInt16 m_nRedlineRejectId; + sal_uInt16 m_nRedlineNextId; + sal_uInt16 m_nRedlinePrevId; + SwWrtShell* m_pSh; + css::uno::Sequence< css::uno::Reference< css::linguistic2::XDictionary > > m_aDics; + css::uno::Reference< css::linguistic2::XSpellAlternatives > m_xSpellAlt; + + OUString m_sExplanationLink; + + LanguageType m_nCheckedLanguage; + + std::map< sal_Int16, OUString > m_aLangTable_Text; + std::map< sal_Int16, OUString > m_aLangTable_Paragraph; + + OUString m_aDicNameSingle; + bool m_bGrammarResults; // show grammar results? Or show spellcheck results? + + static void fillLangPopupMenu( PopupMenu *pPopupMenu, sal_uInt16 nLangStart, + const css::uno::Sequence< OUString >& aSeq, SwWrtShell* pWrtSh, + std::map< sal_Int16, OUString > &rLangTable ); + + /// Checks if any of the redline menu items should be hidden. + void checkRedline(); + +public: + SwSpellPopup( SwWrtShell *pWrtSh, + const css::uno::Reference< css::linguistic2::XSpellAlternatives > &xAlt, + const OUString & rParaText ); + + SwSpellPopup( SwWrtShell *pWrtSh, + const css::linguistic2::ProofreadingResult &rResult, + sal_Int32 nErrorInResult, + const css::uno::Sequence< OUString > &rSuggestions, + const OUString & rParaText ); + + ~SwSpellPopup(); + + void InitItemCommands(const css::uno::Sequence< OUString >& aSuggestions); + + PopupMenu& GetMenu() + { + return *m_xPopupMenu; + } + + void Execute( const tools::Rectangle& rPopupPos, vcl::Window* pWin ); + void Execute( sal_uInt16 nId ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/optcomp.hxx b/sw/source/uibase/inc/optcomp.hxx new file mode 100644 index 000000000..e087d3852 --- /dev/null +++ b/sw/source/uibase/inc/optcomp.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_OPTCOMP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OPTCOMP_HXX + +#include <memory> +#include <sfx2/tabdlg.hxx> +#include <unotools/compatibility.hxx> +#include <unotools/compatibilityviewoptions.hxx> +#include <rtl/ustring.hxx> + +class SwWrtShell; +struct SwCompatibilityOptPage_Impl; + +class SwCompatibilityOptPage : public SfxTabPage +{ +private: + // config item + SvtCompatibilityOptions m_aConfigItem; + // config item + SvtCompatibilityViewOptions m_aViewConfigItem; + // text of the user entry + OUString m_sUserEntry; + // shell of the current document + SwWrtShell* m_pWrtShell; + // impl object + std::unique_ptr<SwCompatibilityOptPage_Impl> m_pImpl; + // saved options after "Reset"; used in "FillItemSet" for comparison + sal_uInt32 m_nSavedOptions; + bool m_bSavedMSFormsMenuOption; + + // controls + std::unique_ptr<weld::Frame> m_xMain; + std::unique_ptr<weld::Frame> m_xGlobalOptionsFrame; + std::unique_ptr<weld::ComboBox> m_xFormattingLB; + std::unique_ptr<weld::ComboBox> m_xGlobalOptionsLB; + std::unique_ptr<weld::TreeView> m_xOptionsLB; + std::unique_ptr<weld::TreeView> m_xGlobalOptionsCLB; + std::unique_ptr<weld::Button> m_xDefaultPB; + + // handler + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(UseAsDefaultHdl, weld::Button&, void); + + // private methods + void InitControls( const SfxItemSet& rSet ); + void SetCurrentOptions( sal_uInt32 nOptions ); + sal_uInt32 GetDocumentOptions() const; + void WriteOptions(); + +public: + SwCompatibilityOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwCompatibilityOptPage() override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet ); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/optload.hxx b/sw/source/uibase/inc/optload.hxx new file mode 100644 index 000000000..1db4fcbf5 --- /dev/null +++ b/sw/source/uibase/inc/optload.hxx @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_OPTLOAD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OPTLOAD_HXX + +#include <tools/globname.hxx> +#include <sfx2/tabdlg.hxx> + +#include <vcl/customweld.hxx> +#include <vcl/textfilter.hxx> +#include <vcl/weld.hxx> +#include <sfx2/basedlgs.hxx> + +#include <SwCapObjType.hxx> + +class SwFieldMgr; +class SvTreeListEntry; +class SwWrtShell; + +class SwFieldUnitTable +{ +public: + static OUString GetString(sal_uInt32 i); + static sal_uInt32 Count(); + static FieldUnit GetValue(sal_uInt32 i); +}; + +class TextFilterAutoConvert : public TextFilter +{ +private: + OUString m_sLastGoodText; + OUString m_sNone; +public: + TextFilterAutoConvert(const OUString &rNone) + : m_sNone(rNone) + { + } + virtual OUString filter(const OUString &rText) override; +}; + +class SwLoadOptPage : public SfxTabPage +{ +private: + SwWrtShell* m_pWrtShell; + sal_uInt16 m_nLastTab; + sal_Int32 m_nOldLinkMode; + + std::unique_ptr<weld::RadioButton> m_xAlwaysRB; + std::unique_ptr<weld::RadioButton> m_xRequestRB; + std::unique_ptr<weld::RadioButton> m_xNeverRB; + + std::unique_ptr<weld::CheckButton> m_xAutoUpdateFields; + std::unique_ptr<weld::CheckButton> m_xAutoUpdateCharts; + + std::unique_ptr<weld::ComboBox> m_xMetricLB; + std::unique_ptr<weld::Label> m_xTabFT; + std::unique_ptr<weld::MetricSpinButton> m_xTabMF; + std::unique_ptr<weld::CheckButton> m_xUseSquaredPageMode; + std::unique_ptr<weld::CheckButton> m_xUseCharUnit; + std::unique_ptr<weld::Entry> m_xWordCountED; + std::unique_ptr<weld::CheckButton> m_xShowStandardizedPageCount; + std::unique_ptr<weld::SpinButton> m_xStandardizedPageSizeNF; + + DECL_LINK(MetricHdl, weld::ComboBox&, void); + DECL_LINK(StandardizedPageCountCheckHdl, weld::Button&, void); + +public: + SwLoadOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwLoadOptPage() override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +class SwCaptionOptDlg : public SfxSingleTabDialogController +{ +public: + SwCaptionOptDlg(weld::Window* pParent, const SfxItemSet& rSet); +}; + +class SwCaptionPreview : public weld::CustomWidgetController +{ +private: + OUString maText; + bool mbFontInitialized; + vcl::Font maFont; + + void ApplySettings(vcl::RenderContext& rRenderContext); + +public: + SwCaptionPreview(); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + void SetPreviewText(const OUString& rText); + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; +}; + +class SwCaptionOptPage : public SfxTabPage +{ +private: + OUString m_sSWTable; + OUString m_sSWFrame; + OUString m_sSWGraphic; + OUString m_sOLE; + + OUString m_sIllustration; + OUString m_sTable; + OUString m_sText; + OUString m_sDrawing; + + OUString m_sBegin; + OUString m_sEnd; + OUString m_sAbove; + OUString m_sBelow; + + OUString m_sNone; + + int m_nPrevSelectedEntry; + + std::unique_ptr<SwFieldMgr> pMgr; + bool bHTMLMode; + + TextFilterAutoConvert m_aTextFilter; + + SwCaptionPreview m_aPreview; + std::unique_ptr<weld::TreeView> m_xCheckLB; + std::unique_ptr<weld::ComboBox> m_xLbCaptionOrder; + + std::unique_ptr<weld::Widget> m_xSettingsGroup; + std::unique_ptr<weld::ComboBox> m_xCategoryBox; + std::unique_ptr<weld::Label> m_xFormatText; + std::unique_ptr<weld::ComboBox> m_xFormatBox; + //#i61007# order of captions + std::unique_ptr<weld::Label> m_xNumberingSeparatorFT; + std::unique_ptr<weld::Entry> m_xNumberingSeparatorED; + std::unique_ptr<weld::Label> m_xTextText; + std::unique_ptr<weld::Entry> m_xTextEdit; + std::unique_ptr<weld::ComboBox> m_xPosBox; + + std::unique_ptr<weld::Widget> m_xNumCapt; + std::unique_ptr<weld::ComboBox> m_xLbLevel; + std::unique_ptr<weld::Entry> m_xEdDelim; + + std::unique_ptr<weld::Widget> m_xCategory; + std::unique_ptr<weld::ComboBox> m_xCharStyleLB; + std::unique_ptr<weld::CheckButton> m_xApplyBorderCB; + std::unique_ptr<weld::CustomWeld> m_xPreview; + + typedef std::pair<int, int> row_col; + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(SelectListBoxHdl, weld::ComboBox&, void); + DECL_LINK(ModifyEntryHdl, weld::Entry&, void); + DECL_LINK(ModifyComboHdl, weld::ComboBox&, void); + DECL_LINK(OrderHdl, weld::ComboBox&, void ); + DECL_LINK(ShowEntryHdl, weld::TreeView&, void); + DECL_LINK(ToggleEntryHdl, const row_col&, void); + DECL_LINK(TextFilterHdl, OUString&, bool); + + void ModifyHdl(); + void UpdateEntry(int nRow); + void DelUserData(); + void SetOptions(const sal_uLong nPos, const SwCapObjType eType, const SvGlobalName *pOleId = nullptr); + void SaveEntry(int nEntry); + void InvalidatePreview(); + +public: + SwCaptionOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwCaptionOptPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/optpage.hxx b/sw/source/uibase/inc/optpage.hxx new file mode 100644 index 000000000..73062bb3b --- /dev/null +++ b/sw/source/uibase/inc/optpage.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_OPTPAGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OPTPAGE_HXX +#include <sfx2/tabdlg.hxx> + +#include <vcl/weld.hxx> +#include <svtools/ctrlbox.hxx> +#include <svx/colorbox.hxx> +#include <svx/fntctrl.hxx> +#include "fontcfg.hxx" +class SfxPrinter; +class SwStdFontConfig; +class SwWrtShell; +class FontList; + +// Tools->Options->Writer->View +// Tools->Options->Writer/Web->View +class SwContentOptPage : public SfxTabPage +{ + std::unique_ptr<weld::CheckButton> m_xCrossCB; + + std::unique_ptr<weld::ComboBox> m_xHMetric; + std::unique_ptr<weld::CheckButton> m_xVRulerCBox; + std::unique_ptr<weld::CheckButton> m_xVRulerRightCBox; + std::unique_ptr<weld::ComboBox> m_xVMetric; + std::unique_ptr<weld::CheckButton> m_xSmoothCBox; + + std::unique_ptr<weld::CheckButton> m_xGrfCB; + std::unique_ptr<weld::CheckButton> m_xTableCB; + std::unique_ptr<weld::CheckButton> m_xDrwCB; + std::unique_ptr<weld::CheckButton> m_xPostItCB; + + std::unique_ptr<weld::Frame> m_xSettingsFrame; + std::unique_ptr<weld::Label> m_xSettingsLabel; + std::unique_ptr<weld::Label> m_xMetricLabel; + std::unique_ptr<weld::ComboBox> m_xMetricLB; + + std::unique_ptr<weld::CheckButton> m_xShowInlineTooltips; + std::unique_ptr<weld::CheckButton> m_xFieldHiddenCB; + std::unique_ptr<weld::CheckButton> m_xFieldHiddenParaCB; + + DECL_LINK(VertRulerHdl, weld::ToggleButton&, void); +public: + SwContentOptPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwContentOptPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; +}; + +// TabPage printer settings additions +class SwAddPrinterTabPage : public SfxTabPage +{ + OUString sNone; + bool bAttrModified; + bool bPreview; + + std::unique_ptr<weld::CheckButton> m_xGrfCB; + std::unique_ptr<weld::CheckButton> m_xCtrlFieldCB; + std::unique_ptr<weld::CheckButton> m_xBackgroundCB; + std::unique_ptr<weld::CheckButton> m_xBlackFontCB; + std::unique_ptr<weld::CheckButton> m_xPrintHiddenTextCB; + std::unique_ptr<weld::CheckButton> m_xPrintTextPlaceholderCB; + + std::unique_ptr<weld::Widget> m_xPagesFrame; + std::unique_ptr<weld::CheckButton> m_xLeftPageCB; + std::unique_ptr<weld::CheckButton> m_xRightPageCB; + std::unique_ptr<weld::CheckButton> m_xProspectCB; + std::unique_ptr<weld::CheckButton> m_xProspectCB_RTL; + + std::unique_ptr<weld::Widget> m_xCommentsFrame; + std::unique_ptr<weld::RadioButton> m_xNoRB; + std::unique_ptr<weld::RadioButton> m_xOnlyRB; + std::unique_ptr<weld::RadioButton> m_xEndRB; + std::unique_ptr<weld::RadioButton> m_xEndPageRB; + std::unique_ptr<weld::RadioButton> m_xInMarginsRB; + + std::unique_ptr<weld::CheckButton> m_xPrintEmptyPagesCB; + std::unique_ptr<weld::CheckButton> m_xPaperFromSetupCB; + std::unique_ptr<weld::ComboBox> m_xFaxLB; + + DECL_LINK(AutoClickHdl, weld::ToggleButton&, void); + DECL_LINK(SelectHdl, weld::ComboBox&, void); + +public: + SwAddPrinterTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwAddPrinterTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + void SetFax(const std::vector<OUString>& ); + void SetPreview(bool bPrev); + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +class SwStdFontTabPage : public SfxTabPage +{ + OUString m_sShellStd; + OUString m_sShellTitle; + OUString m_sShellList; + OUString m_sShellLabel; + OUString m_sShellIndex; + + VclPtr<SfxPrinter> m_pPrt; + std::unique_ptr<FontList> m_pFontList; + SwStdFontConfig* m_pFontConfig; + SwWrtShell* m_pWrtShell; + LanguageType m_eLanguage; + + // only defaults were there? they were signed with the boxes + bool m_bListDefault :1; + bool m_bSetListDefault :1; + bool m_bLabelDefault :1; + bool m_bSetLabelDefault :1; + bool m_bIdxDefault :1; + bool m_bSetIdxDefault :1; + bool m_bDisposePrinter :1; + + bool m_bListHeightDefault :1; + bool m_bLabelHeightDefault :1; + bool m_bIndexHeightDefault :1; + + sal_uInt8 m_nFontGroup; //fontcfg.hxx: FONT_GROUP_[STANDARD|CJK|CTL] + + OUString m_sScriptWestern; + OUString m_sScriptAsian; + OUString m_sScriptComplex; + + std::unique_ptr<weld::Label> m_xLabelFT; + std::unique_ptr<weld::ComboBox> m_xStandardBox; + std::unique_ptr<FontSizeBox> m_xStandardHeightLB; + std::unique_ptr<weld::ComboBox> m_xTitleBox; + std::unique_ptr<FontSizeBox> m_xTitleHeightLB; + std::unique_ptr<weld::ComboBox> m_xListBox; + std::unique_ptr<FontSizeBox> m_xListHeightLB; + std::unique_ptr<weld::ComboBox> m_xLabelBox; + std::unique_ptr<FontSizeBox> m_xLabelHeightLB; + std::unique_ptr<weld::ComboBox> m_xIdxBox; + std::unique_ptr<FontSizeBox> m_xIndexHeightLB; + std::unique_ptr<weld::Button> m_xStandardPB; + + DECL_LINK(StandardHdl, weld::Button&, void ); + DECL_LINK(ModifyHdl, weld::ComboBox&, void ); + DECL_LINK(LoseFocusHdl, weld::Widget&, void ); + +public: + SwStdFontTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + virtual ~SwStdFontTabPage() override; + + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +class SwTableOptionsTabPage : public SfxTabPage +{ + SwWrtShell* m_pWrtShell; + bool m_bHTMLMode; + + std::unique_ptr<weld::CheckButton> m_xHeaderCB; + std::unique_ptr<weld::CheckButton> m_xRepeatHeaderCB; + std::unique_ptr<weld::CheckButton> m_xDontSplitCB; + std::unique_ptr<weld::CheckButton> m_xBorderCB; + + std::unique_ptr<weld::CheckButton> m_xNumFormattingCB; + std::unique_ptr<weld::CheckButton> m_xNumFormatFormattingCB; + std::unique_ptr<weld::CheckButton> m_xNumAlignmentCB; + + std::unique_ptr<weld::MetricSpinButton> m_xRowMoveMF; + std::unique_ptr<weld::MetricSpinButton> m_xColMoveMF; + + std::unique_ptr<weld::MetricSpinButton> m_xRowInsertMF; + std::unique_ptr<weld::MetricSpinButton> m_xColInsertMF; + + std::unique_ptr<weld::RadioButton> m_xFixRB; + std::unique_ptr<weld::RadioButton> m_xFixPropRB; + std::unique_ptr<weld::RadioButton> m_xVarRB; + + DECL_LINK(CheckBoxHdl, weld::Button&, void); + +public: + SwTableOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwTableOptionsTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet(SfxItemSet* rSet) override; + virtual void Reset(const SfxItemSet* rSet) override; + + virtual void PageCreated( const SfxAllItemSet& aSet) override; + +}; + +// TabPage for ShadowCursor +class SwShdwCursorOptionsTabPage : public SfxTabPage +{ + SwWrtShell * m_pWrtShell; + + //nonprinting characters + std::unique_ptr<weld::CheckButton> m_xParaCB; + std::unique_ptr<weld::CheckButton> m_xSHyphCB; + std::unique_ptr<weld::CheckButton> m_xSpacesCB; + std::unique_ptr<weld::CheckButton> m_xHSpacesCB; + std::unique_ptr<weld::CheckButton> m_xTabCB; + std::unique_ptr<weld::Label> m_xTabLabel; + std::unique_ptr<weld::CheckButton> m_xBreakCB; + std::unique_ptr<weld::CheckButton> m_xCharHiddenCB; + std::unique_ptr<weld::CheckButton> m_xBookmarkCB; + std::unique_ptr<weld::Label> m_xBookmarkLabel; + + std::unique_ptr<weld::Frame> m_xDirectCursorFrame; + std::unique_ptr<weld::CheckButton> m_xOnOffCB; + + std::unique_ptr<weld::ComboBox> m_xDirectCursorFillMode; + std::unique_ptr<weld::Frame> m_xCursorProtFrame; + std::unique_ptr<weld::CheckButton> m_xCursorInProtCB; + + std::unique_ptr<weld::CheckButton> m_xMathBaselineAlignmentCB; + +public: + SwShdwCursorOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwShdwCursorOptionsTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + virtual void PageCreated( const SfxAllItemSet& aSet ) override; +}; + +// mark preview +class SwMarkPreview : public weld::CustomWidgetController +{ + Color m_aBgCol; // background + Color m_aTransCol; // transparency + Color m_aMarkCol; // marks + Color m_aLineCol; // general lines + Color m_aShadowCol; // shadow + Color m_aTextCol; // text + Color m_aPrintAreaCol; // frame for print area + + tools::Rectangle aPage; + tools::Rectangle aLeftPagePrtArea; + tools::Rectangle aRightPagePrtArea; + + sal_uInt16 nMarkPos; + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + void PaintPage(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect); + void InitColors(); + +public: + SwMarkPreview(); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual ~SwMarkPreview() override; + + void SetColor(const Color& rCol) { m_aMarkCol = rCol; } + void SetMarkPos(sal_uInt16 nPos) { nMarkPos = nPos; } +}; + +// redlining options +class SwRedlineOptionsTabPage : public SfxTabPage +{ + std::unique_ptr<weld::ComboBox> m_xInsertLB; + std::unique_ptr<ColorListBox> m_xInsertColorLB; + std::unique_ptr<SvxFontPrevWindow> m_xInsertedPreviewWN; + std::unique_ptr<weld::CustomWeld> m_xInsertedPreview; + + std::unique_ptr<weld::ComboBox> m_xDeletedLB; + std::unique_ptr<ColorListBox> m_xDeletedColorLB; + std::unique_ptr<SvxFontPrevWindow> m_xDeletedPreviewWN; + std::unique_ptr<weld::CustomWeld> m_xDeletedPreview; + + std::unique_ptr<weld::ComboBox> m_xChangedLB; + std::unique_ptr<ColorListBox> m_xChangedColorLB; + std::unique_ptr<SvxFontPrevWindow> m_xChangedPreviewWN; + std::unique_ptr<weld::CustomWeld> m_xChangedPreview; + + std::unique_ptr<weld::ComboBox> m_xMarkPosLB; + std::unique_ptr<ColorListBox> m_xMarkColorLB; + std::unique_ptr<SwMarkPreview> m_xMarkPreviewWN; + std::unique_ptr<weld::CustomWeld> m_xMarkPreview; + + DECL_LINK(AttribHdl, weld::ComboBox&, void); + void ChangedMaskPrev(); + DECL_LINK(ChangedMaskPrevHdl, weld::ComboBox&, void); + DECL_LINK(ChangedMaskColorPrevHdl, ColorListBox&, void); + DECL_LINK(ColorHdl, ColorListBox&, void); + + static void InitFontStyle(SvxFontPrevWindow& rExampleWin, const OUString& rText); + +public: + SwRedlineOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwRedlineOptionsTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +// TabPage test settings for SW + +#ifdef DBG_UTIL + +class SwTestTabPage : public SfxTabPage +{ +public: + SwTestTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwTestTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + +private: + bool bAttrModified; + + std::unique_ptr<weld::CheckButton> m_xTest1CBox; + std::unique_ptr<weld::CheckButton> m_xTest2CBox; + std::unique_ptr<weld::CheckButton> m_xTest3CBox; + std::unique_ptr<weld::CheckButton> m_xTest4CBox; + std::unique_ptr<weld::CheckButton> m_xTest5CBox; + std::unique_ptr<weld::CheckButton> m_xTest6CBox; + std::unique_ptr<weld::CheckButton> m_xTest7CBox; + std::unique_ptr<weld::CheckButton> m_xTest8CBox; + std::unique_ptr<weld::CheckButton> m_xTest9CBox; + std::unique_ptr<weld::CheckButton> m_xTest10CBox; + + void Init(); + DECL_LINK(AutoClickHdl, weld::Button&, void); +}; +#endif // DBG_UTIL + +class SwCompareOptionsTabPage : public SfxTabPage +{ + std::unique_ptr<weld::RadioButton> m_xAutoRB; + std::unique_ptr<weld::RadioButton> m_xWordRB; + std::unique_ptr<weld::RadioButton> m_xCharRB; + std::unique_ptr<weld::CheckButton> m_xRsidCB; + std::unique_ptr<weld::CheckButton> m_xIgnoreCB; + std::unique_ptr<weld::SpinButton> m_xLenNF; + std::unique_ptr<weld::CheckButton> m_xStoreRsidCB; + + DECL_LINK(ComparisonHdl, weld::Button&, void); + DECL_LINK(IgnoreHdl, weld::Button&, void); + +public: + SwCompareOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + virtual ~SwCompareOptionsTabPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet ); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; +}; + +#endif +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/outline.hxx b/sw/source/uibase/inc/outline.hxx new file mode 100644 index 000000000..f9c3c1f15 --- /dev/null +++ b/sw/source/uibase/inc/outline.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_OUTLINE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_OUTLINE_HXX + +#include <memory> +#include <sfx2/tabdlg.hxx> +#include <swtypes.hxx> +#include "numprevw.hxx" +#include "numberingtypelistbox.hxx" +#include <rtl/ustring.hxx> + +class SwWrtShell; +class SwNumRule; +class SwChapterNumRules; + +class SwOutlineTabDialog final : public SfxTabDialogController +{ + static sal_uInt16 nNumLevel; + + OUString aCollNames[MAXLEVEL]; + + SwWrtShell& rWrtSh; + std::unique_ptr<SwNumRule> xNumRule; + SwChapterNumRules* pChapterNumRules; + + bool bModified : 1; + + std::unique_ptr<weld::MenuButton> m_xMenuButton; + + DECL_LINK(CancelHdl, weld::Button&, void); + DECL_LINK(FormHdl, weld::ToggleButton&, void); + DECL_LINK(MenuSelectHdl, const OString&, void); + + virtual void PageCreated(const OString& rPageId, SfxTabPage& rPage) override; + virtual short Ok() override; + +public: + SwOutlineTabDialog(weld::Window* pParent, const SfxItemSet* pSwItemSet, SwWrtShell &); + virtual ~SwOutlineTabDialog() override; + + SwNumRule* GetNumRule() { return xNumRule.get(); } + sal_uInt16 GetLevel(const OUString &rFormatName) const; + OUString* GetCollNames() {return aCollNames;} + + static sal_uInt16 GetActNumLevel() {return nNumLevel;} + static void SetActNumLevel(sal_uInt16 nSet) {nNumLevel = nSet;} +}; + +class SwOutlineSettingsTabPage : public SfxTabPage +{ + OUString aNoFormatName; + OUString aSaveCollNames[MAXLEVEL]; + SwWrtShell* pSh; + SwNumRule* pNumRule; + OUString* pCollNames; + sal_uInt16 nActLevel; + NumberingPreview m_aPreviewWIN; + + std::unique_ptr<weld::TreeView> m_xLevelLB; + std::unique_ptr<weld::ComboBox> m_xCollBox; + std::unique_ptr<SwNumberingTypeListBox> m_xNumberBox; + std::unique_ptr<weld::ComboBox> m_xCharFormatLB; + std::unique_ptr<weld::Label> m_xAllLevelFT; + std::unique_ptr<weld::SpinButton> m_xAllLevelNF; + std::unique_ptr<weld::Entry> m_xPrefixED; + std::unique_ptr<weld::Entry> m_xSuffixED; + std::unique_ptr<weld::SpinButton> m_xStartEdit; + std::unique_ptr<weld::CustomWeld> m_xPreviewWIN; + + DECL_LINK( LevelHdl, weld::TreeView&, void ); + DECL_LINK( ToggleComplete, weld::SpinButton&, void ); + DECL_LINK( CollSelect, weld::ComboBox&, void ); + void CollSave(); + DECL_LINK( NumberSelect, weld::ComboBox&, void ); + DECL_LINK( DelimModify, weld::Entry&, void ); + DECL_LINK( StartModified, weld::SpinButton&, void ); + DECL_LINK( CharFormatHdl, weld::ComboBox&, void ); + + void Update(); + + void SetModified() { m_aPreviewWIN.Invalidate(); } + void CheckForStartValue_Impl(sal_uInt16 nNumberingType); + +public: + SwOutlineSettingsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + virtual ~SwOutlineSettingsTabPage() override; + + void SetWrtShell(SwWrtShell* pShell); + + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + void SetNumRule(SwNumRule *pRule) + { + pNumRule = pRule; + m_aPreviewWIN.SetNumRule(pNumRule); + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/pardlg.hxx b/sw/source/uibase/inc/pardlg.hxx new file mode 100644 index 000000000..c417c7223 --- /dev/null +++ b/sw/source/uibase/inc/pardlg.hxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_PARDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PARDLG_HXX + +//DialogModes: +#define DLG_STD 0 +#define DLG_ENVELOP 2 + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/pattern.hxx b/sw/source/uibase/inc/pattern.hxx new file mode 100644 index 000000000..4be18c634 --- /dev/null +++ b/sw/source/uibase/inc/pattern.hxx @@ -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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_PATTERN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PATTERN_HXX + +#include <sfx2/basedlgs.hxx> + +class SwBackgroundDlg : public SfxSingleTabDialogController +{ +public: + SwBackgroundDlg(weld::Window* pParent, const SfxItemSet& rSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/pgfnote.hxx b/sw/source/uibase/inc/pgfnote.hxx new file mode 100644 index 000000000..c174aee48 --- /dev/null +++ b/sw/source/uibase/inc/pgfnote.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_UIBASE_INC_PGFNOTE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PGFNOTE_HXX + +#include <sfx2/tabdlg.hxx> + +#include <svtools/ctrlbox.hxx> +#include <svx/colorbox.hxx> + +// footnote settings TabPage +class SwFootNotePage: public SfxTabPage +{ + static const sal_uInt16 aPageRg[]; +public: + SwFootNotePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + virtual ~SwFootNotePage() override; + + static const sal_uInt16* GetRanges() { return aPageRg; } + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + +private: + + long lMaxHeight; + + std::unique_ptr<weld::RadioButton> m_xMaxHeightPageBtn; + std::unique_ptr<weld::RadioButton> m_xMaxHeightBtn; + std::unique_ptr<weld::MetricSpinButton> m_xMaxHeightEdit; + std::unique_ptr<weld::MetricSpinButton> m_xDistEdit; + std::unique_ptr<weld::ComboBox> m_xLinePosBox; + std::unique_ptr<SvtLineListBox> m_xLineTypeBox; + std::unique_ptr<weld::MetricSpinButton> m_xLineWidthEdit; + std::unique_ptr<ColorListBox> m_xLineColorBox; + std::unique_ptr<weld::MetricSpinButton> m_xLineLengthEdit; + std::unique_ptr<weld::MetricSpinButton> m_xLineDistEdit; + + DECL_LINK(HeightPage, weld::ToggleButton&, void); + DECL_LINK(HeightMetric, weld::ToggleButton&, void); + DECL_LINK(HeightModify, weld::MetricSpinButton&, void); + DECL_LINK(LineWidthChanged_Impl, weld::MetricSpinButton&, void); + DECL_LINK(LineColorSelected_Impl, ColorListBox&, void); + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/pggrid.hxx b/sw/source/uibase/inc/pggrid.hxx new file mode 100644 index 000000000..a43aee7b4 --- /dev/null +++ b/sw/source/uibase/inc/pggrid.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_UIBASE_INC_PGGRID_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PGGRID_HXX + +#include <sfx2/tabdlg.hxx> +#include "colex.hxx" +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> +#include <svx/colorbox.hxx> + +// TabPage Format/(Styles/)Page/Text grid +class SwTextGridPage: public SfxTabPage +{ + sal_Int32 m_nRubyUserValue; + bool m_bRubyUserValue; + Size m_aPageSize; + bool m_bVertical; + bool m_bSquaredMode; + bool m_bHRulerChanged; + bool m_bVRulerChanged; + + SwPageGridExample m_aExampleWN; + std::unique_ptr<weld::RadioButton> m_xNoGridRB; + std::unique_ptr<weld::RadioButton> m_xLinesGridRB; + std::unique_ptr<weld::RadioButton> m_xCharsGridRB; + std::unique_ptr<weld::CheckButton> m_xSnapToCharsCB; + std::unique_ptr<weld::CustomWeld> m_xExampleWN; + std::unique_ptr<weld::Widget> m_xLayoutFL; + std::unique_ptr<weld::SpinButton> m_xLinesPerPageNF; + std::unique_ptr<weld::Label> m_xLinesRangeFT; + std::unique_ptr<weld::MetricSpinButton> m_xTextSizeMF; + std::unique_ptr<weld::Label> m_xCharsPerLineFT; + std::unique_ptr<weld::SpinButton> m_xCharsPerLineNF; + std::unique_ptr<weld::Label> m_xCharsRangeFT; + std::unique_ptr<weld::Label> m_xCharWidthFT; + std::unique_ptr<weld::MetricSpinButton> m_xCharWidthMF; + std::unique_ptr<weld::Label> m_xRubySizeFT; + std::unique_ptr<weld::MetricSpinButton> m_xRubySizeMF; + std::unique_ptr<weld::CheckButton> m_xRubyBelowCB; + std::unique_ptr<weld::Widget> m_xDisplayFL; + std::unique_ptr<weld::CheckButton> m_xDisplayCB; + std::unique_ptr<weld::CheckButton> m_xPrintCB; + std::unique_ptr<ColorListBox> m_xColorLB; + + void UpdatePageSize(const SfxItemSet& rSet); + void PutGridItem(SfxItemSet& rSet); + static void SetLinesOrCharsRanges(weld::Label& rField, const sal_Int32 nValue); + + void GridModifyHdl(); + + DECL_LINK(GridTypeHdl, weld::ToggleButton&, void); + DECL_LINK(CharorLineChangedHdl, weld::SpinButton&, void); + DECL_LINK(TextSizeChangedHdl, weld::MetricSpinButton&, void); + DECL_LINK(ColorModifyHdl, ColorListBox&, void); + DECL_LINK(GridModifyClickHdl, weld::ToggleButton&, void); + DECL_LINK(DisplayGridHdl, weld::ToggleButton&, void); + +public: + SwTextGridPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwTextGridPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges(); + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/prcntfld.hxx b/sw/source/uibase/inc/prcntfld.hxx new file mode 100644 index 000000000..f9e682c51 --- /dev/null +++ b/sw/source/uibase/inc/prcntfld.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_UIBASE_INC_PRCNTFLD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PRCNTFLD_HXX + +#include <svtools/unitconv.hxx> +#include <vcl/weld.hxx> +#include <swdllapi.h> + +class SW_DLLPUBLIC SwPercentField +{ + std::unique_ptr<weld::MetricSpinButton> m_pField; + + int nRefValue; // 100% value for conversion (in Twips) + int nOldMax; + int nOldMin; + int nOldSpinSize; + int nOldPageSize; + int nLastPercent; + int nLastValue; + sal_uInt16 nOldDigits; + FieldUnit eOldUnit; + bool bLockAutoCalculation; //prevent recalculation of percent values when the + //reference value is changed + + SAL_DLLPRIVATE static int ImpPower10(sal_uInt16 n); + +public: + + SwPercentField(std::unique_ptr<weld::MetricSpinButton> pControl); + const weld::MetricSpinButton* get() const { return m_pField.get(); } + weld::MetricSpinButton* get() { return m_pField.get(); } + void connect_value_changed(const Link<weld::MetricSpinButton&, void>& rLink) { m_pField->connect_value_changed(rLink); } + void SetMetric(FieldUnit eUnit) { ::SetFieldUnit(*m_pField, eUnit); } + void set_sensitive(bool bEnable) { m_pField->set_sensitive(bEnable); } + void show() { m_pField->show(); } + bool has_focus() const { return m_pField->has_focus(); } + void save_value() { m_pField->save_value(); } + bool get_value_changed_from_saved() const { return m_pField->get_value_changed_from_saved(); } + void set_text(const OUString& rStr) { m_pField->set_text(rStr); } + void set_accessible_name(const OUString& rStr) { m_pField->set_accessible_name(rStr); } + void SetMetricFieldMin(int nNewMin) { m_pField->set_min(nNewMin, FieldUnit::NONE); } + void SetMetricFieldMax(int nNewMax) { m_pField->set_max(nNewMax, FieldUnit::NONE); } + + void set_value(int nNewValue, FieldUnit eInUnit = FieldUnit::NONE); + int get_value(FieldUnit eOutUnit = FieldUnit::NONE); + + void set_min(int nNewMin, FieldUnit eInUnit); + void set_max(int nNewMax, FieldUnit eInUnit); + + int get_min(FieldUnit eOutUnit = FieldUnit::NONE) const { return m_pField->get_min(eOutUnit); } + + int NormalizePercent(int nValue); + int DenormalizePercent(int nValue); + + void SetRefValue(int nValue); + int GetRealValue(FieldUnit eOutUnit); + + int Convert(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit); + + void ShowPercent(bool bPercent); + + void LockAutoCalculation(bool bLock) {bLockAutoCalculation = bLock;} +}; + + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_PRCNTFLD_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/pview.hxx b/sw/source/uibase/inc/pview.hxx new file mode 100644 index 000000000..38f743f9c --- /dev/null +++ b/sw/source/uibase/inc/pview.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_PVIEW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_PVIEW_HXX + +#include <tools/link.hxx> +#include <tools/fract.hxx> +#include <vcl/window.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewfac.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/zoomitem.hxx> +#include <swdllapi.h> +#include <shellid.hxx> + +class SwViewOption; +class SwDocShell; +class SwScrollbar; +class SwViewShell; +class SwPagePreview; +class ImageButton; +class Button; +class SwRect; +class DataChangedEvent; +class CommandEvent; +class SvtAccessibilityOptions; +class SwPagePreviewLayout; + +// Delete member <mnVirtPage> and its accessor +class SAL_DLLPUBLIC_RTTI SwPagePreviewWin : public vcl::Window +{ + SwViewShell* mpViewShell; + sal_uInt16 mnSttPage; + sal_uInt8 mnRow; + sal_uInt8 mnCol; + Size maPxWinSize; + Fraction maScale; + SwPagePreview& mrView; + bool mbCalcScaleForPreviewLayout; + tools::Rectangle maPaintedPreviewDocRect; + SwPagePreviewLayout* mpPgPreviewLayout; + + void SetPagePreview( sal_uInt8 nRow, sal_uInt8 nCol ); + + using Window::Scroll; + +public: + SwPagePreviewWin( vcl::Window* pParent, SwPagePreview& rView ); + virtual ~SwPagePreviewWin() override; + + // calls SwViewShell::Paint + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + virtual void KeyInput( const KeyEvent & ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void DataChanged( const DataChangedEvent& ) override; + + void SetViewShell( SwViewShell* pShell ); + + SwViewShell* GetViewShell() const + { + return mpViewShell; + } + + sal_uInt8 GetRow() const + { + return mnRow; + } + + sal_uInt8 GetCol() const + { + return mnCol; + } + + sal_uInt16 GetSttPage() const + { + return mnSttPage; + } + + void SetSttPage(sal_uInt16 n) + { + mnSttPage = n; + } + + /** get selected page number of document preview + + @return selected page number + */ + sal_uInt16 SelectedPage() const; + + /** set selected page number in document preview + + @param _nSelectedPageNum + input parameter - physical page number of page that will be the selected one. + */ + void SetSelectedPage( sal_uInt16 _nSelectedPageNum ); + + // If we only have one column we do not have a oth page + sal_uInt16 GetDefSttPage() const { return 1 == mnCol ? 1 : 0; } + + void CalcWish( sal_uInt8 nNewRow, sal_uInt8 nNewCol ); + + void SetWinSize( const Size& rNewSize ); + + // Add <MV_SELPAGE>, <MV_SCROLL> + enum MoveMode{ MV_CALC, MV_PAGE_UP, MV_PAGE_DOWN, MV_DOC_STT, MV_DOC_END, + MV_SELPAGE, MV_SCROLL, MV_NEWWINSIZE, MV_SPECIFIC_PAGE }; + bool MovePage( int eMoveMode ); + + // Create the status bar's string + OUString GetStatusStr( sal_uInt16 nPageCount ) const; + + void RepaintCoreRect( const SwRect& rRect ); + + /** Method to adjust preview to a new zoom factor + paint of preview is prepared for a new zoom factor + Zoom type has also been considered. + Thus, add new parameter <_eZoomType> + */ + void AdjustPreviewToNewZoom( const sal_uInt16 _nZoomFactor, + const SvxZoomType _eZoomType ); + + const tools::Rectangle& GetPaintedPreviewDocRect() const + { + return maPaintedPreviewDocRect; + } + + void Scroll(long nXMove, long nYMove, ScrollFlags nFlags = ScrollFlags::NONE) override; + + /** Method to enable/disable book preview + @param _bBookPreview + input parameter - boolean indicating, if book preview mode has to + switch on <true> or of <false> + + @return boolean indicating, if book preview mode has changed. + */ + bool SetBookPreviewMode( const bool _bBookPreview ); + + virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; +}; + +/** + * View of a document + */ +class SW_DLLPUBLIC SwPagePreview: public SfxViewShell +{ + // ViewWindow and handle to core + // current dispatcher shell + VclPtr<SwPagePreviewWin> m_pViewWin; + //viewdata of the previous SwView and the new cursor position + OUString m_sSwViewData; + //and the new cursor position if the user double click in the PagePreview + OUString m_sNewCursorPosition; + // to support keyboard the number of the page to go to can be set too + sal_uInt16 m_nNewPage; + // visible range + OUString m_sPageStr; + Size m_aDocSize; + tools::Rectangle m_aVisArea; + + // MDI control elements + VclPtr<SwScrollbar> m_pHScrollbar; + VclPtr<SwScrollbar> m_pVScrollbar; + bool mbHScrollbarEnabled : 1; + bool mbVScrollbarEnabled : 1; + // dummy window for filling the lower right edge when both scrollbars are active + VclPtr<vcl::Window> m_pScrollFill; + + sal_uInt16 mnPageCount; + bool m_bNormalPrint; + + // New members to reset design mode at draw view for form shell on switching + // back from writer page preview to normal view. + bool mbResetFormDesignMode:1; + bool mbFormDesignModeToReset:1; + + SAL_DLLPRIVATE void Init(); + SAL_DLLPRIVATE Point AlignToPixel(const Point& rPt) const; + + SAL_DLLPRIVATE void CreateScrollbar( bool bHori); + DECL_DLLPRIVATE_LINK(ScrollHdl, ScrollBar*, void); + DECL_DLLPRIVATE_LINK(EndScrollHdl, ScrollBar*, void); + SAL_DLLPRIVATE bool ChgPage( int eMvMode, bool bUpdateScrollbar = true ); + + SAL_DLLPRIVATE virtual SfxPrinter* GetPrinter( bool bCreate = false ) override; + SAL_DLLPRIVATE virtual sal_uInt16 SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL ) override; + SAL_DLLPRIVATE virtual bool HasPrintOptionsPage() const override; + SAL_DLLPRIVATE virtual std::unique_ptr<SfxTabPage> CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions) override; + + SAL_DLLPRIVATE void CalcAndSetBorderPixel( SvBorder &rToFill ); + + /** Helper method to execute SfxRequest FN_PAGE_UP and FN_PAGE_DOWN + + @param _bPgUp + input parameter - boolean that indicates, if FN_PAGE_UP or FN_PAGE_DOWN + has to be executed. + + @param _pReq + optional input parameter - pointer to the <SfxRequest> instance, if existing. + */ + SAL_DLLPRIVATE void ExecPgUpAndPgDown( const bool _bPgUp, + SfxRequest* _pReq ); + +protected: + virtual void InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange ) override; + virtual void OuterResizePixel( const Point &rOfs, const Size &rSize ) override; + + void SetZoom(SvxZoomType eSet, sal_uInt16 nFactor); + +public: + SFX_DECL_VIEWFACTORY(SwPagePreview); + SFX_DECL_INTERFACE(SW_PAGEPREVIEW) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwViewShell* GetViewShell() const + { return m_pViewWin->GetViewShell(); } + void RepaintCoreRect( const SwRect& rRect ) + { m_pViewWin->RepaintCoreRect( rRect ); } + + void DocSzChgd(const Size& rNewSize); + + void SetVisArea( const tools::Rectangle& ); + + void ScrollViewSzChg(); + void ScrollDocSzChg(); + void ShowHScrollbar(bool bShow); + void ShowVScrollbar(bool bShow); + void EnableHScrollbar(bool bEnable); + void EnableVScrollbar(bool bEnable); + + sal_uInt16 GetPageCount() const { return mnPageCount; } + sal_uInt16 GetSelectedPage() const {return m_pViewWin->SelectedPage();} + + bool HandleWheelCommands( const CommandEvent& ); + + const OUString& GetPrevSwViewData() const { return m_sSwViewData; } + void SetNewCursorPos( const OUString& rStr ) { m_sNewCursorPosition = rStr; } + const OUString& GetNewCursorPos() const { return m_sNewCursorPosition; } + + sal_uInt16 GetNewPage() const {return m_nNewPage;} + + // Handler + void Execute(SfxRequest&); + void GetState(SfxItemSet&); + static void StateUndo(SfxItemSet&); + + SwDocShell* GetDocShell(); + + // apply Accessibility options + void ApplyAccessibilityOptions(SvtAccessibilityOptions const & rAccessibilityOptions); + + // Inline method to request values of new members + // <mbResetFormDesignMode> and <mbFormDesignModeToReset> + bool ResetFormDesignMode() const + { + return mbResetFormDesignMode; + } + + bool FormDesignModeToReset() const + { + return mbFormDesignModeToReset; + } + + /** Adjust position of vertical scrollbar + + Currently used, if the complete preview layout rows fit into to the given + window, if a new page is selected and this page is visible. + + @param _nNewThumbPos + input parameter - new position, which will be assigned to the vertical + scrollbar. + */ + void SetVScrollbarThumbPos( const sal_uInt16 _nNewThumbPos ); + + SwPagePreview( SfxViewFrame* pFrame, SfxViewShell* ); + virtual ~SwPagePreview() override; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/redlndlg.hxx b/sw/source/uibase/inc/redlndlg.hxx new file mode 100644 index 000000000..910f23ce2 --- /dev/null +++ b/sw/source/uibase/inc/redlndlg.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_UIBASE_INC_REDLNDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_REDLNDLG_HXX +#include <swdllapi.h> +#include "chldwrap.hxx" +#include <redline.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/basedlgs.hxx> +#include <svl/lstner.hxx> +#include <svx/ctredlin.hxx> +#include <o3tl/sorted_vector.hxx> + +#include <memory> +#include <vector> + +class SwChildWinWrapper; + +struct SwRedlineDataChild +{ + const SwRedlineData* pChild; // link to original stacked data + const SwRedlineDataChild* pNext; // link to stacked data + std::unique_ptr<weld::TreeIter> xTLBChild; // corresponding TreeListBox entry +}; + +struct SwRedlineDataParent +{ + const SwRedlineData* pData; // RedlineDataPtr + const SwRedlineDataChild* pNext; // link to stacked data + std::unique_ptr<weld::TreeIter> xTLBParent; // corresponding TreeListBox entry + OUString sComment; // redline comment + + bool operator< ( const SwRedlineDataParent& rObj ) const + { return (pData && pData->GetSeqNo() < rObj.pData->GetSeqNo()); } +}; + +class SwRedlineDataParentSortArr : public o3tl::sorted_vector<SwRedlineDataParent*, o3tl::less_ptr_to<SwRedlineDataParent> > {}; + +class SW_DLLPUBLIC SwRedlineAcceptDlg final +{ + std::shared_ptr<weld::Window> m_xParentDlg; + std::vector<std::unique_ptr<SwRedlineDataParent>> m_RedlineParents; + std::vector<std::unique_ptr<SwRedlineDataChild>> + m_RedlineChildren; + SwRedlineDataParentSortArr m_aUsedSeqNo; + Timer m_aSelectTimer; + OUString m_sInserted; + OUString m_sDeleted; + OUString m_sFormated; + OUString m_sTableChgd; + OUString m_sFormatCollSet; + OUString m_sFilterAction; + OUString m_sAutoFormat; + bool m_bOnlyFormatedRedlines; + bool m_bRedlnAutoFormat; + + // prevent update dialog data during longer operations (cf #102657#) + bool m_bInhibitActivate; + + std::unique_ptr<SvxAcceptChgCtr> m_xTabPagesCTRL; + std::unique_ptr<weld::Menu> m_xPopup; + SvxTPView* m_pTPView; + SvxRedlinTable* m_pTable; // PB 2006/02/02 #i48648 now SvHeaderTabListBox + + DECL_DLLPRIVATE_LINK( AcceptHdl, SvxTPView*, void ); + DECL_DLLPRIVATE_LINK( AcceptAllHdl, SvxTPView*, void ); + DECL_DLLPRIVATE_LINK( RejectHdl, SvxTPView*, void ); + DECL_DLLPRIVATE_LINK( RejectAllHdl, SvxTPView*, void ); + DECL_DLLPRIVATE_LINK( UndoHdl, SvxTPView*, void ); + DECL_DLLPRIVATE_LINK( SelectHdl, weld::TreeView&, void ); + DECL_DLLPRIVATE_LINK( GotoHdl, Timer*, void ); + DECL_DLLPRIVATE_LINK( CommandHdl, const CommandEvent&, bool ); + + SAL_DLLPRIVATE SwRedlineTable::size_type CalcDiff(SwRedlineTable::size_type nStart, bool bChild); + SAL_DLLPRIVATE void InsertChildren(SwRedlineDataParent *pParent, const SwRangeRedline& rRedln, bool bHasRedlineAutoFormat); + SAL_DLLPRIVATE void InsertParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd = SwRedlineTable::npos); + SAL_DLLPRIVATE void RemoveParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd); + SAL_DLLPRIVATE void InitAuthors(); + + SAL_DLLPRIVATE static OUString GetActionImage(const SwRangeRedline& rRedln, sal_uInt16 nStack = 0); + SAL_DLLPRIVATE OUString GetActionText(const SwRangeRedline& rRedln, sal_uInt16 nStack = 0); + SAL_DLLPRIVATE SwRedlineTable::size_type GetRedlinePos(const weld::TreeIter& rEntry); + + SwRedlineAcceptDlg(SwRedlineAcceptDlg const&) = delete; + SwRedlineAcceptDlg& operator=(SwRedlineAcceptDlg const&) = delete; + +public: + SwRedlineAcceptDlg(const std::shared_ptr<weld::Window>& rParent, weld::Builder *pBuilder, weld::Container *pContentArea, bool bAutoFormat = false); + ~SwRedlineAcceptDlg(); + + DECL_LINK( FilterChangedHdl, SvxTPFilter*, void ); + + SvxAcceptChgCtr& GetChgCtrl() { return *m_xTabPagesCTRL; } + bool HasRedlineAutoFormat() const { return m_bRedlnAutoFormat; } + + void Init(SwRedlineTable::size_type nStart = 0); + void CallAcceptReject( bool bSelect, bool bAccept ); + + void Initialize(OUString &rExtraData); + void FillInfo(OUString &rExtraData) const; + + void Activate(); +}; + +class SwModelessRedlineAcceptDlg : public SfxModelessDialogController +{ + std::unique_ptr<weld::Container> m_xContentArea; + std::unique_ptr<SwRedlineAcceptDlg> m_xImplDlg; + SwChildWinWrapper* pChildWin; + +public: + SwModelessRedlineAcceptDlg(SfxBindings*, SwChildWinWrapper*, weld::Window *pParent); + virtual ~SwModelessRedlineAcceptDlg() override; + + virtual void Activate() override; + virtual void FillInfo(SfxChildWinInfo&) const override; + void Initialize(SfxChildWinInfo * pInfo); +}; + +class SwRedlineAcceptChild : public SwChildWinWrapper +{ +public: + SwRedlineAcceptChild(vcl::Window* , + sal_uInt16 nId, + SfxBindings*, + SfxChildWinInfo*); + + SFX_DECL_CHILDWINDOW_WITHID( SwRedlineAcceptChild ); + + virtual bool ReInitDlg(SwDocShell *pDocSh) override; +}; + +/// Redline (Manage Changes) panel for the sidebar. +class SwRedlineAcceptPanel : public PanelLayout, public SfxListener +{ + std::unique_ptr<SwRedlineAcceptDlg> mpImplDlg; + std::unique_ptr<weld::Container> mxContentArea; +public: + SwRedlineAcceptPanel(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame); + virtual ~SwRedlineAcceptPanel() override; + virtual void dispose() override; + + /// We need to be a SfxListener to be able to update the list of changes when we get SfxHintId::DocChanged. + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/regionsw.hxx b/sw/source/uibase/inc/regionsw.hxx new file mode 100644 index 000000000..adb2681ef --- /dev/null +++ b/sw/source/uibase/inc/regionsw.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_UIBASE_INC_REGIONSW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_REGIONSW_HXX + +#include <sfx2/basedlgs.hxx> +#include <sfx2/tabdlg.hxx> + +#include "condedit.hxx" +#include <section.hxx> +#include <fmtftntx.hxx> +#include "numberingtypelistbox.hxx" +#include <svx/paraprev.hxx> + +#include <memory> +#include <map> + +class SwWrtShell; + +namespace sfx2 +{ + class DocumentInserter; + class FileDialogHelper; +} + +// dialog "edit regions" +class SectRepr; +typedef std::map<size_t, std::unique_ptr<SectRepr>> SectReprs_t; + +class SwEditRegionDlg : public SfxDialogController +{ + bool m_bSubRegionsFilled; + + SwWrtShell& rSh; + SectReprs_t m_SectReprs; + const SwSection* pCurrSect; + std::unique_ptr<sfx2::DocumentInserter> m_pDocInserter; + + bool bDontCheckPasswd :1; + + std::unique_ptr<weld::Entry> m_xCurName; + std::unique_ptr<weld::TreeView> m_xTree; + std::unique_ptr<weld::CheckButton> m_xFileCB; + std::unique_ptr<weld::CheckButton> m_xDDECB; + std::unique_ptr<weld::Widget> m_xDDEFrame; + std::unique_ptr<weld::Label> m_xFileNameFT; + std::unique_ptr<weld::Label> m_xDDECommandFT; + std::unique_ptr<weld::Entry> m_xFileNameED; + std::unique_ptr<weld::Button> m_xFilePB; + std::unique_ptr<weld::Label> m_xSubRegionFT; + std::unique_ptr<weld::ComboBox> m_xSubRegionED; + std::unique_ptr<weld::CheckButton> m_xProtectCB; + std::unique_ptr<weld::CheckButton> m_xPasswdCB; + std::unique_ptr<weld::Button> m_xPasswdPB; + std::unique_ptr<weld::CheckButton> m_xHideCB; + std::unique_ptr<weld::Label> m_xConditionFT; + std::unique_ptr<ConditionEdit> m_xConditionED; + // #114856# edit in readonly sections + std::unique_ptr<weld::CheckButton> m_xEditInReadonlyCB; + std::unique_ptr<weld::Button> m_xOK; + std::unique_ptr<weld::Button> m_xOptionsPB; + std::unique_ptr<weld::Button> m_xDismiss; + std::unique_ptr<weld::Widget> m_xHideFrame; + + void RecurseList(const SwSectionFormat* pFormat, const weld::TreeIter* pIter); + size_t FindArrPos(const SwSectionFormat* pFormat); + + DECL_LINK( GetFirstEntryHdl, weld::TreeView&, void ); + + DECL_LINK( OkHdl, weld::Button&, void ); + DECL_LINK( NameEditHdl, weld::Entry&, void ); + DECL_LINK( ConditionEditHdl, weld::Entry&, void ); + + void ChangePasswd(bool bChange); + DECL_LINK( TogglePasswdHdl, weld::ToggleButton&, void ); + DECL_LINK( ChangePasswdHdl, weld::Button&, void ); + DECL_LINK( ChangeProtectHdl, weld::ToggleButton&, void ); + DECL_LINK( ChangeHideHdl, weld::ToggleButton&, void ); + // #114856# edit in readonly sections + DECL_LINK( ChangeEditInReadonlyHdl, weld::ToggleButton&, void ); + DECL_LINK( ChangeDismissHdl, weld::Button&, void); + DECL_LINK( UseFileHdl, weld::ToggleButton&, void ); + DECL_LINK( FileSearchHdl, weld::Button&, void ); + DECL_LINK( OptionsHdl, weld::Button&, void ); + DECL_LINK( FileNameComboBoxHdl, weld::ComboBox&, void ); + DECL_LINK( FileNameEntryHdl, weld::Entry&, void ); + DECL_LINK( DDEHdl, weld::ToggleButton&, void ); + DECL_LINK( DlgClosedHdl, sfx2::FileDialogHelper*, void ); + DECL_LINK( SubRegionEventHdl, weld::ComboBox&, void ); + + bool CheckPasswd(weld::ToggleButton* pBox = nullptr); + +public: + SwEditRegionDlg(weld::Window* pParent, SwWrtShell& rWrtSh); + virtual ~SwEditRegionDlg() override; + + void SelectSection(const OUString& rSectionName); + +}; + +// dialog "insert region" +class SwInsertSectionTabPage : public SfxTabPage +{ + OUString m_sFileName; + OUString m_sFilterName; + OUString m_sFilePasswd; + + css::uno::Sequence <sal_Int8 > m_aNewPasswd; + SwWrtShell* m_pWrtSh; + std::unique_ptr<sfx2::DocumentInserter> m_pDocInserter; + + std::unique_ptr<weld::EntryTreeView> m_xCurName; + std::unique_ptr<weld::CheckButton> m_xFileCB; + std::unique_ptr<weld::CheckButton> m_xDDECB; + std::unique_ptr<weld::Label> m_xDDECommandFT; + std::unique_ptr<weld::Label> m_xFileNameFT; + std::unique_ptr<weld::Entry> m_xFileNameED; + std::unique_ptr<weld::Button> m_xFilePB; + std::unique_ptr<weld::Label> m_xSubRegionFT; + std::unique_ptr<weld::ComboBox> m_xSubRegionED; + std::unique_ptr<weld::CheckButton> m_xProtectCB; + std::unique_ptr<weld::CheckButton> m_xPasswdCB; + std::unique_ptr<weld::Button> m_xPasswdPB; + std::unique_ptr<weld::CheckButton> m_xHideCB; + std::unique_ptr<weld::Label> m_xConditionFT; + std::unique_ptr<ConditionEdit> m_xConditionED; + // #114856# edit in readonly sections + std::unique_ptr<weld::CheckButton> m_xEditInReadonlyCB; + + void ChangePasswd(bool bChange); + + DECL_LINK( ChangeHideHdl, weld::ToggleButton&, void ); + DECL_LINK( ChangeProtectHdl, weld::ToggleButton&, void ); + DECL_LINK( ChangePasswdHdl, weld::Button&, void ); + DECL_LINK( TogglePasswdHdl, weld::ToggleButton&, void ); + DECL_LINK( NameEditHdl, weld::ComboBox&, void ); + DECL_LINK( UseFileHdl, weld::ToggleButton&, void ); + DECL_LINK( FileSearchHdl, weld::Button&, void ); + DECL_LINK( DDEHdl, weld::ToggleButton&, void ); + DECL_LINK( DlgClosedHdl, sfx2::FileDialogHelper*, void ); + +public: + SwInsertSectionTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet); + virtual ~SwInsertSectionTabPage() override; + + void SetWrtShell(SwWrtShell& rSh); + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); +}; + +class SwSectionFootnoteEndTabPage : public SfxTabPage +{ + std::unique_ptr<weld::CheckButton> m_xFootnoteNtAtTextEndCB; + std::unique_ptr<weld::CheckButton> m_xFootnoteNtNumCB; + std::unique_ptr<weld::Label> m_xFootnoteOffsetLbl; + std::unique_ptr<weld::SpinButton> m_xFootnoteOffsetField; + std::unique_ptr<weld::CheckButton> m_xFootnoteNtNumFormatCB; + std::unique_ptr<weld::Label> m_xFootnotePrefixFT; + std::unique_ptr<weld::Entry> m_xFootnotePrefixED; + std::unique_ptr<SwNumberingTypeListBox> m_xFootnoteNumViewBox; + std::unique_ptr<weld::Label> m_xFootnoteSuffixFT; + std::unique_ptr<weld::Entry> m_xFootnoteSuffixED; + std::unique_ptr<weld::CheckButton> m_xEndNtAtTextEndCB; + std::unique_ptr<weld::CheckButton> m_xEndNtNumCB; + std::unique_ptr<weld::Label> m_xEndOffsetLbl; + std::unique_ptr<weld::SpinButton> m_xEndOffsetField; + std::unique_ptr<weld::CheckButton> m_xEndNtNumFormatCB; + std::unique_ptr<weld::Label> m_xEndPrefixFT; + std::unique_ptr<weld::Entry> m_xEndPrefixED; + std::unique_ptr<SwNumberingTypeListBox> m_xEndNumViewBox; + std::unique_ptr<weld::Label> m_xEndSuffixFT; + std::unique_ptr<weld::Entry> m_xEndSuffixED; + + DECL_LINK(FootEndHdl, weld::ToggleButton&, void); + void ResetState( bool bFootnote, const SwFormatFootnoteEndAtTextEnd& ); + +public: + SwSectionFootnoteEndTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet); + virtual ~SwSectionFootnoteEndTabPage() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); +}; + +class SwSectionIndentTabPage : public SfxTabPage +{ + SvxParaPrevWindow m_aPreviewWin; + std::unique_ptr<weld::MetricSpinButton> m_xBeforeMF; + std::unique_ptr<weld::MetricSpinButton> m_xAfterMF; + std::unique_ptr<weld::CustomWeld> m_xPreviewWin; + + DECL_LINK(IndentModifyHdl, weld::MetricSpinButton&, void); +public: + SwSectionIndentTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet); + virtual ~SwSectionIndentTabPage() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + + void SetWrtShell(SwWrtShell const & rSh); +}; + +class SwInsertSectionTabDialog : public SfxTabDialogController +{ + SwWrtShell& rWrtSh; + std::unique_ptr<SwSectionData> m_pSectionData; + +protected: + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + virtual short Ok() override; +public: + SwInsertSectionTabDialog(weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell& rSh); + virtual ~SwInsertSectionTabDialog() override; + + void SetSectionData(SwSectionData const& rSect); + SwSectionData * GetSectionData() { return m_pSectionData.get(); } +}; + +class SwSectionPropertyTabDialog : public SfxTabDialogController +{ + SwWrtShell& rWrtSh; + +protected: + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; +public: + SwSectionPropertyTabDialog(weld::Window* pParent, const SfxItemSet& rSet, SwWrtShell& rSh); + virtual ~SwSectionPropertyTabDialog() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/rowht.hxx b/sw/source/uibase/inc/rowht.hxx new file mode 100644 index 000000000..58439eb89 --- /dev/null +++ b/sw/source/uibase/inc/rowht.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_UIBASE_INC_ROWHT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ROWHT_HXX + +#include <vcl/weld.hxx> + +class SwWrtShell; + +class SwTableHeightDlg : public weld::GenericDialogController +{ + SwWrtShell &m_rSh; + + std::unique_ptr<weld::MetricSpinButton> m_xHeightEdit; + std::unique_ptr<weld::CheckButton> m_xAutoHeightCB; + +private: + void Apply(); + +public: + SwTableHeightDlg(weld::Window *pParent, SwWrtShell &rS); + virtual short run() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/scroll.hxx b/sw/source/uibase/inc/scroll.hxx new file mode 100644 index 000000000..fff22eb0d --- /dev/null +++ b/sw/source/uibase/inc/scroll.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_UIBASE_INC_SCROLL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SCROLL_HXX +#include <vcl/scrbar.hxx> + +class SwScrollbar: public ScrollBar +{ + Size aDocSz; + bool bHori :1; // horizontal = salTrue, otherwise vertical + bool bAuto :1; // for scrolling mode + bool bVisible :1; // show/hide should only set this flag + bool bSizeSet :1; // was the size already set? + + void AutoShow(); + + using Window::Hide; + using Window::IsVisible; + +public: + void ExtendedShow( bool bVisible = true ); + void SetPosSizePixel( const Point& rNewPos, const Size& rNewSize ) override; + bool IsVisible(bool bReal) const { return bReal ? ScrollBar::IsVisible() : bVisible; } + + // changing of document size + void DocSzChgd(const Size &rNewSize); + // changing of visible region + void ViewPortChgd(const tools::Rectangle &rRectangle); + // what is it?? + bool IsHoriScroll() const { return bHori; } + + void SetAuto(bool bSet); + bool IsAuto() const { return bAuto;} + + SwScrollbar(vcl::Window *pParent, bool bHori ); + virtual ~SwScrollbar() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/selglos.hxx b/sw/source/uibase/inc/selglos.hxx new file mode 100644 index 000000000..b31f61a54 --- /dev/null +++ b/sw/source/uibase/inc/selglos.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_UIBASE_INC_SELGLOS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SELGLOS_HXX + +#include <vcl/weld.hxx> + +class SwSelGlossaryDlg final : public weld::GenericDialogController +{ + std::unique_ptr<weld::Frame> m_xFrame; + std::unique_ptr<weld::TreeView> m_xGlosBox; + + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); +public: + SwSelGlossaryDlg(weld::Window * pParent, const OUString &rShortName); + virtual ~SwSelGlossaryDlg() override; + + void InsertGlos(const OUString &rRegion, const OUString &rGlosName) + { + const OUString aTmp = rRegion + ":" + rGlosName; + m_xGlosBox->append_text(aTmp); + } + sal_Int32 GetSelectedIdx() const + { + return m_xGlosBox->get_selected_index(); + } + void SelectEntryPos(sal_Int32 nIdx) + { + m_xGlosBox->select(nIdx); + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/sharedconnection.hxx b/sw/source/uibase/inc/sharedconnection.hxx new file mode 100644 index 000000000..138cbf51e --- /dev/null +++ b/sw/source/uibase/inc/sharedconnection.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_UIBASE_INC_SHAREDCONNECTION_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SHAREDCONNECTION_HXX + +#include <unotools/sharedunocomponent.hxx> +#include <com/sun/star/sdbc/XConnection.hpp> + +typedef ::utl::SharedUNOComponent< css::sdbc::XConnection > SharedConnection; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_SHAREDCONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/shdwcrsr.hxx b/sw/source/uibase/inc/shdwcrsr.hxx new file mode 100644 index 000000000..2bd51c69c --- /dev/null +++ b/sw/source/uibase/inc/shdwcrsr.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_UIBASE_INC_SHDWCRSR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SHDWCRSR_HXX + +#include <tools/gen.hxx> +#include <tools/color.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> +#include <limits.h> + +class SwShadowCursor +{ + VclPtr<vcl::Window> pWin; + Color aCol; + Point aOldPt; + long nOldHeight; + sal_uInt16 nOldMode; + + void DrawTri( const Point& rPt, long nHeight, bool bLeft ); + void DrawCursor( const Point& rPt, long nHeight, sal_uInt16 nMode ); + +public: + SwShadowCursor( vcl::Window& rWin, const Color& rCol ) + : pWin( &rWin ), aCol( rCol ), nOldHeight(0), nOldMode( USHRT_MAX ) {} + ~SwShadowCursor(); + + void SetPos( const Point& rPt, long nHeight, sal_uInt16 nMode ); + + void Paint(); + + tools::Rectangle GetRect() const; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/splittbl.hxx b/sw/source/uibase/inc/splittbl.hxx new file mode 100644 index 000000000..ac75e72c6 --- /dev/null +++ b/sw/source/uibase/inc/splittbl.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_UIBASE_INC_SPLITTBL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SPLITTBL_HXX + +#include <vcl/weld.hxx> +#include <tblenum.hxx> + +class SwWrtShell; + +class SwSplitTableDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::RadioButton> m_xHorzBox; + std::unique_ptr<weld::RadioButton> m_xContentCopyRB; + std::unique_ptr<weld::RadioButton> m_xBoxAttrCopyWithParaRB; + std::unique_ptr<weld::RadioButton> m_xBoxAttrCopyNoParaRB; + std::unique_ptr<weld::RadioButton> m_xBorderCopyRB; + + SwWrtShell &rShell; + SplitTable_HeadlineOption m_nSplit; + + void Apply(); + +public: + SwSplitTableDlg(weld::Window *pParent, SwWrtShell &rSh); + + virtual short run() override + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + Apply(); + return nRet; + } + + SplitTable_HeadlineOption GetSplitMode() const { return m_nSplit; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/srcedtw.hxx b/sw/source/uibase/inc/srcedtw.hxx new file mode 100644 index 000000000..e48226f35 --- /dev/null +++ b/sw/source/uibase/inc/srcedtw.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_SRCEDTW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SRCEDTW_HXX + +#include <vcl/window.hxx> +#include <svl/lstner.hxx> +#include <vcl/timer.hxx> +#include <vcl/idle.hxx> + +#include <vcl/xtextedt.hxx> +#include <set> + +namespace com::sun::star::beans { class XMultiPropertySet; } +class ScrollBar; +class SwSrcView; +class SwSrcEditWindow; +class TextEngine; +class TextView; +class DataChangedEvent; + +class TextViewOutWin : public vcl::Window +{ + TextView* pTextView; + +protected: + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override; + virtual void KeyInput( const KeyEvent& rKeyEvt ) override; + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void DataChanged( const DataChangedEvent& ) override; + +public: + TextViewOutWin(vcl::Window* pParent, WinBits nBits) : + Window(pParent, nBits), pTextView(nullptr){} + + void SetTextView( TextView* pView ) {pTextView = pView;} + +}; + +class SwSrcEditWindow : public vcl::Window, public SfxListener +{ +private: + class ChangesListener; + friend class ChangesListener; + std::unique_ptr<TextView> m_pTextView; + std::unique_ptr<ExtTextEngine> m_pTextEngine; + + VclPtr<TextViewOutWin> m_pOutWin; + VclPtr<ScrollBar> m_pHScrollbar, + m_pVScrollbar; + + SwSrcView* m_pSrcView; + + rtl::Reference< ChangesListener > m_xListener; + osl::Mutex mutex_; + css::uno::Reference< css::beans::XMultiPropertySet > + m_xNotifier; + + long m_nCurTextWidth; + sal_uInt16 m_nStartLine; + rtl_TextEncoding m_eSourceEncoding; + bool m_bReadonly; + bool m_bHighlighting; + + Idle m_aSyntaxIdle; + std::set<sal_uInt16> m_aSyntaxLineTable; + + void ImpDoHighlight( const OUString& rSource, sal_uInt16 nLineOff ); + + using OutputDevice::SetFont; + void SetFont(); + + DECL_LINK( SyntaxTimerHdl, Timer *, void ); + + using Window::Invalidate; + +protected: + + virtual void Resize() override; + virtual void DataChanged( const DataChangedEvent& ) override; + virtual void GetFocus() override; +// virtual void LoseFocus(); + + void CreateTextEngine(); + void DoSyntaxHighlight( sal_uInt16 nPara ); + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + DECL_LINK(ScrollHdl, ScrollBar*, void); + +public: + SwSrcEditWindow( vcl::Window* pParent, SwSrcView* pParentView ); + virtual ~SwSrcEditWindow() override; + virtual void dispose() override; + + void SetScrollBarRanges(); + void InitScrollBars(); + void Read(SvStream& rInput) { m_pTextEngine->Read(rInput); } + void Write(SvStream& rOutput) { m_pTextEngine->Write(rOutput); } + + TextView* GetTextView() + {return m_pTextView.get();} + TextEngine* GetTextEngine() + {return m_pTextEngine.get();} + SwSrcView* GetSrcView() {return m_pSrcView;} + + TextViewOutWin* GetOutWin() {return m_pOutWin;} + + virtual void Invalidate( InvalidateFlags nFlags = InvalidateFlags::NONE ) override; + + void ClearModifyFlag() + { m_pTextEngine->SetModified(false); } + bool IsModified() const + { return m_pTextEngine->IsModified();} + + void SetReadonly(bool bSet){m_bReadonly = bSet;} + bool IsReadonly() const {return m_bReadonly;} + + void SetStartLine(sal_uInt16 nLine){m_nStartLine = nLine;} + + virtual void Command( const CommandEvent& rCEvt ) override; + void HandleWheelCommand( const CommandEvent& rCEvt ); + + void SetTextEncoding(rtl_TextEncoding eEncoding); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/srcview.hxx b/sw/source/uibase/inc/srcview.hxx new file mode 100644 index 000000000..a2e8277b9 --- /dev/null +++ b/sw/source/uibase/inc/srcview.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_UIBASE_INC_SRCVIEW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SRCVIEW_HXX + +#include <sfx2/viewfac.hxx> +#include <sfx2/viewsh.hxx> +#include <vcl/outdev.hxx> + +#include "srcedtw.hxx" +#include <shellid.hxx> + +class SwDocShell; +class SvxSearchItem; +class SfxMedium; + +class SwSrcView: public SfxViewShell +{ + VclPtr<SwSrcEditWindow> aEditWin; + + std::unique_ptr<SvxSearchItem> pSearchItem; + + bool bSourceSaved :1; + rtl_TextEncoding eLoadEncoding; + void Init(); + + // for read-only switching + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + +protected: + void StartSearchAndReplace(const SvxSearchItem& rItem, + bool bApi, + bool bRecursive = false); + +public: + SFX_DECL_VIEWFACTORY(SwSrcView); + SFX_DECL_INTERFACE(SW_SRC_VIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwSrcView(SfxViewFrame* pFrame, SfxViewShell*); + + virtual ~SwSrcView() override; + + SwDocShell* GetDocShell(); + void SaveContent(const OUString& rTmpFile); + void SaveContentTo(SfxMedium& rMed); + + bool IsModified() const {return aEditWin->IsModified();} + + void Execute(SfxRequest&); + void GetState(SfxItemSet&); + + SvxSearchItem* GetSearchItem(); + void SetSearchItem( const SvxSearchItem& rItem ); + + void Load(SwDocShell* pDocShell); + + virtual sal_uInt16 SetPrinter( SfxPrinter* pNew, SfxPrinterChangeFlags nDiff = SFX_PRINTER_ALL ) override; + virtual SfxPrinter* GetPrinter( bool bCreate = false ) override; + + sal_Int32 PrintSource( OutputDevice *pOutDev, sal_Int32 nPage, bool bCalcNumPagesOnly ); + + bool HasSourceSaved() const {return bSourceSaved;} + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/srtdlg.hxx b/sw/source/uibase/inc/srtdlg.hxx new file mode 100644 index 000000000..b4828dcf5 --- /dev/null +++ b/sw/source/uibase/inc/srtdlg.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_UIBASE_INC_SRTDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SRTDLG_HXX + +#include <svtools/collatorres.hxx> +#include <svx/langbox.hxx> +#include <vcl/weld.hxx> + +class SwWrtShell; + +class SwSortDlg : public weld::GenericDialogController +{ + weld::Window* m_pParent; + std::unique_ptr<weld::Label> m_xColLbl; + + std::unique_ptr<weld::CheckButton> m_xKeyCB1; + std::unique_ptr<weld::SpinButton> m_xColEdt1; + std::unique_ptr<weld::ComboBox> m_xTypDLB1; + std::unique_ptr<weld::RadioButton> m_xSortUp1RB; + std::unique_ptr<weld::RadioButton> m_xSortDn1RB; + + std::unique_ptr<weld::CheckButton> m_xKeyCB2; + std::unique_ptr<weld::SpinButton> m_xColEdt2; + std::unique_ptr<weld::ComboBox> m_xTypDLB2; + std::unique_ptr<weld::RadioButton> m_xSortUp2RB; + std::unique_ptr<weld::RadioButton> m_xSortDn2RB; + + std::unique_ptr<weld::CheckButton> m_xKeyCB3; + std::unique_ptr<weld::SpinButton> m_xColEdt3; + std::unique_ptr<weld::ComboBox> m_xTypDLB3; + std::unique_ptr<weld::RadioButton> m_xSortUp3RB; + std::unique_ptr<weld::RadioButton> m_xSortDn3RB; + + std::unique_ptr<weld::RadioButton> m_xColumnRB; + std::unique_ptr<weld::RadioButton> m_xRowRB; + + std::unique_ptr<weld::RadioButton> m_xDelimTabRB; + std::unique_ptr<weld::RadioButton> m_xDelimFreeRB; + std::unique_ptr<weld::Entry> m_xDelimEdt; + std::unique_ptr<weld::Button> m_xDelimPB; + + std::unique_ptr<SvxLanguageBox> m_xLangLB; + + std::unique_ptr<weld::CheckButton> m_xCaseCB; + + OUString aColText; + OUString aRowText; + OUString aNumericText; + + SwWrtShell &rSh; + std::unique_ptr<CollatorResource> m_xColRes; + + sal_uInt16 nX; + sal_uInt16 nY; + + void Apply(); + sal_Unicode GetDelimChar() const; + + DECL_LINK(CheckHdl, weld::ToggleButton&, void); + DECL_LINK(DelimHdl, weld::ToggleButton&, void ); + DECL_LINK(LanguageListBoxHdl, weld::ComboBox&, void); + void LanguageHdl(weld::ComboBox const*); + DECL_LINK(DelimCharHdl, weld::Button&,void); + +public: + SwSortDlg(weld::Window * pParent, SwWrtShell &rSh); + virtual short run() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swcont.hxx b/sw/source/uibase/inc/swcont.hxx new file mode 100644 index 000000000..76efc6d88 --- /dev/null +++ b/sw/source/uibase/inc/swcont.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 . + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SWCONT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWCONT_HXX + +#include <rtl/ustring.hxx> +#include <vcl/naturalsort.hxx> + +class SwContentType; + +enum class ContentTypeId +{ + OUTLINE = 0, + TABLE = 1, + FRAME = 2, + GRAPHIC = 3, + OLE = 4, + BOOKMARK = 5, + REGION = 6, + URLFIELD = 7, + REFERENCE = 8, + INDEX = 9, + POSTIT = 10, + DRAWOBJECT = 11, + LAST = DRAWOBJECT, + UNKNOWN = -1 +}; + +// strings for context menus +#define CONTEXT_COUNT 17 +#define GLOBAL_CONTEXT_COUNT 14 + +// modes for Drag 'n Drop +enum class RegionMode +{ + NONE = 0, + LINK = 1, + EMBEDDED = 2 +}; + +//mini rtti +class SwTypeNumber +{ + sal_uInt8 nTypeId; + + public: + SwTypeNumber(sal_uInt8 nId) :nTypeId(nId){} + virtual ~SwTypeNumber(); + + sal_uInt8 GetTypeId() const { return nTypeId;} +}; + +class SwContent : public SwTypeNumber +{ + const SwContentType* pParent; + OUString sContentName; + long nYPosition; + // some subclasses appear to use this for a tools/gen.hxx-style + // geometric Y position, while e.g. SwOutlineContent wants to store + // the index in its subtree + bool bInvisible; +public: + SwContent(const SwContentType* pCnt, const OUString& rName, long nYPos ); + + virtual bool IsProtect() const; + const SwContentType* GetParent() const {return pParent;} + const OUString& GetName() const {return sContentName;} + bool operator==(const SwContent& /*rCont*/) const + { + // they're never equal, otherwise they'd fall out of the array + return false; + } + bool operator<(const SwContent& rCont) const + { + // at first sort by position and then by name + if (nYPosition != rCont.nYPosition) + return nYPosition < rCont.nYPosition; + return vcl::NaturalSortCompare(sContentName, rCont.sContentName) < 0; + } + + bool IsInvisible() const {return bInvisible;} + void SetInvisible(){ bInvisible = true;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swdtflvr.hxx b/sw/source/uibase/inc/swdtflvr.hxx new file mode 100644 index 000000000..e231269e7 --- /dev/null +++ b/sw/source/uibase/inc/swdtflvr.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SWDTFLVR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWDTFLVR_HXX + +#include <sfx2/objsh.hxx> + +#include <vcl/transfer.hxx> +#include <vcl/graph.hxx> +#include <vcl/vclptr.hxx> +#include <sfx2/lnkbase.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <o3tl/deleter.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <svx/swframetypes.hxx> +#include <memory> + +#include <swdllapi.h> + +class Graphic; +class ImageMap; +class INetBookmark; +class INetImage; +class SfxAbstractPasteDialog; +class SwDoc; +class SwDocFac; +class SwTextBlocks; +class SwWrtShell; +class SvxClipboardFormatItem; +class SwFrameShell; +class SwView_Impl; +class SwPasteContext; +enum class SwPasteSdr; + +enum class TransferBufferType : sal_uInt16 +{ + NONE = 0x0000, + Document = 0x0001, + DocumentWord = 0x0002, + Graphic = 0x0004, + Table = 0x0008, + Ole = 0x0020, + InetField = 0x0040, + Drawing = 0x0081, // drawing is internal too! +}; +namespace o3tl { + template<> struct typed_flags<TransferBufferType> : is_typed_flags<TransferBufferType, 0x00ef> {}; +} +// paste table into a table +enum class PasteTableType +{ + PASTE_DEFAULT, // paste table by overwriting table cells + PASTE_ROW, // paste table as rows above + PASTE_COLUMN, // paste table as columns before + PASTE_TABLE // paste table as nested table +}; + +class SW_DLLPUBLIC SwTransferable : public TransferableHelper +{ + friend class SwView_Impl; + SfxObjectShellLock m_aDocShellRef; + TransferableObjectDescriptor m_aObjDesc; + tools::SvRef<sfx2::SvBaseLink> m_xDdeLink; + + SwWrtShell *m_pWrtShell; + /* #96392# Added pCreatorView to distinguish SwFrameShell from + SwWrtShell. */ + const SwFrameShell *m_pCreatorView; + std::unique_ptr<SwDocFac, o3tl::default_delete<SwDocFac>> m_pClpDocFac; + std::unique_ptr<Graphic> m_pClpGraphic; + std::unique_ptr<Graphic> m_pClpBitmap; + Graphic *m_pOrigGraphic; + std::unique_ptr<INetBookmark> m_pBookmark; // URL and description! + std::unique_ptr<ImageMap> m_pImageMap; + std::unique_ptr<INetImage> m_pTargetURL; + + TransferBufferType m_eBufferType; + + bool m_bOldIdle :1; //D&D Idle flag from the viewsettings + bool m_bCleanUp :1; //D&D cleanup after Drop (not by internal Drop) + + // helper methods for the copy + css::uno::Reference < css::embed::XEmbeddedObject > FindOLEObj( sal_Int64& nAspect ) const; + const Graphic* FindOLEReplacementGraphic() const; + void DeleteSelection(); + + // helper methods for the paste + static SwTransferable* GetSwTransferable( const TransferableDataHelper& rData ); + static void SetSelInShell( SwWrtShell& , bool , const Point* ); + static bool CheckForURLOrLNKFile( TransferableDataHelper& rData, + OUString& rFileName, OUString* pTitle = nullptr ); + static bool TestAllowedFormat( const TransferableDataHelper& rData, + SotClipboardFormatId nFormat, SotExchangeDest nDestination ); + + static bool PasteFileContent( TransferableDataHelper&, + SwWrtShell& rSh, SotClipboardFormatId nFormat, bool bMsg, bool bIgnoreComments = false ); + static bool PasteOLE( TransferableDataHelper& rData, SwWrtShell& rSh, + SotClipboardFormatId nFormat, SotExchangeActionFlags nActionFlags, bool bMsg ); + static bool PasteTargetURL( TransferableDataHelper& rData, SwWrtShell& rSh, + SwPasteSdr nAction, const Point* pPt, bool bInsertGRF ); + + static bool PasteDDE( TransferableDataHelper& rData, SwWrtShell& rWrtShell, + bool bReReadGrf, bool bMsg ); + + static bool PasteSdrFormat( TransferableDataHelper& rData, + SwWrtShell& rSh, SwPasteSdr nAction, + const Point* pPt, SotExchangeActionFlags nActionFlags, bool bNeedToSelectBeforePaste); + + static bool PasteGrf( TransferableDataHelper& rData, SwWrtShell& rSh, + SotClipboardFormatId nFormat, SwPasteSdr nAction, const Point* pPt, + SotExchangeActionFlags nActionFlags, sal_Int8 nDropAction, bool bNeedToSelectBeforePaste, RndStdIds nAnchorType = RndStdIds::FLY_AT_PARA ); + + static bool PasteImageMap( TransferableDataHelper& rData, + SwWrtShell& rSh ); + + static bool PasteAsHyperlink( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat ); + + static bool PasteFileName( TransferableDataHelper& rData, + SwWrtShell& rSh, SotClipboardFormatId nFormat, SwPasteSdr nAction, + const Point* pPt, SotExchangeActionFlags nActionFlags, bool * graphicInserted ); + + static bool PasteDBData( TransferableDataHelper& rData, SwWrtShell& rSh, + SotClipboardFormatId nFormat, bool bLink, const Point* pDragPt, + bool bMsg ); + + static bool PasteFileList( TransferableDataHelper& rData, + SwWrtShell& rSh, bool bLink, + const Point* pPt, bool bMsg ); + + bool PrivateDrop( SwWrtShell& rSh, const Point& rDragPt, bool bMove, + bool bIsXSelection ); + + bool PrivatePaste( SwWrtShell& rShell, SwPasteContext* pContext = nullptr, PasteTableType ePasteTable = PasteTableType::PASTE_DEFAULT ); + + void SetDataForDragAndDrop( const Point& rSttPos ); + + SwTransferable( const SwTransferable& ) = delete; + SwTransferable& operator=( const SwTransferable& ) = delete; + +protected: + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual bool WriteObject( tools::SvRef<SotStorageStream>& rxOStm, + void* pUserObject, + sal_uInt32 nUserObjectId, + const css::datatransfer::DataFlavor& rFlavor ) override; + virtual void DragFinished( sal_Int8 nDropAction ) override; + virtual void ObjectReleased() override; + virtual sal_Bool SAL_CALL isComplex() override; + + using TransferableHelper::StartDrag; + +public: + SwTransferable( SwWrtShell& ); + virtual ~SwTransferable() override; + + static SotExchangeDest GetSotDestination( const SwWrtShell& rSh ); + + // set properties on the document, like PageMargin, VisArea. + // And set real Size + static void InitOle( SfxObjectShell* pDoc ); + + // copy - methods and helper methods for the copy + int Cut(); + int Copy( bool bIsCut = false ); + int PrepareForCopy( bool bIsCut = false ); + void CalculateAndCopy(); // special for Calculator + bool CopyGlossary( SwTextBlocks& rGlossary, const OUString& rStr ); + + // remove the DDE-Link format promise + void RemoveDDELinkFormat( const vcl::Window& rWin ); + + // paste - methods and helper methods for the paste + static bool IsPaste( const SwWrtShell&, const TransferableDataHelper& ); + static bool Paste( SwWrtShell&, TransferableDataHelper&, RndStdIds nAnchorType = RndStdIds::FLY_AT_PARA, + bool bIgnoreComments = false, PasteTableType ePasteTable = PasteTableType::PASTE_DEFAULT ); + static bool PasteData( TransferableDataHelper& rData, + SwWrtShell& rSh, sal_uInt8 nAction, SotExchangeActionFlags nActionFlags, + SotClipboardFormatId nFormat, + SotExchangeDest nDestination, bool bIsPasteFormat, + bool bIsDefault, + const Point* pDDPos = nullptr, sal_Int8 nDropAction = 0, + bool bPasteSelection = false, RndStdIds nAnchorType = RndStdIds::FLY_AT_PARA, + bool bIgnoreComments = false, + SwPasteContext* pContext = nullptr, + PasteTableType nPaste = PasteTableType::PASTE_DEFAULT ); + + static bool IsPasteSpecial( const SwWrtShell& rWrtShell, + const TransferableDataHelper& ); + static bool IsPasteOwnFormat( const TransferableDataHelper& ); + static bool PasteUnformatted( SwWrtShell& rSh, TransferableDataHelper& ); + /** + * @brief PrePasteSpecial Prepares the given dialog without actually running it + * @param rSh + * @param rFormatUsed + */ + static void PrePasteSpecial( const SwWrtShell& rSh, TransferableDataHelper&, const VclPtr<SfxAbstractPasteDialog>& pDlg ); + static bool PasteFormat( SwWrtShell& rSh, TransferableDataHelper& rData, + SotClipboardFormatId nFormat ); + + static void FillClipFormatItem( const SwWrtShell& rSh, + const TransferableDataHelper& rData, + SvxClipboardFormatItem & rToFill ); + + // Interfaces for Drag & Drop + void StartDrag( vcl::Window* pWin, const Point& rPos ); + + SwWrtShell* GetShell() { return m_pWrtShell; } + void SetCleanUp( bool bFlag ) { m_bCleanUp = bFlag; } + + // Interfaces for Selection + /* #96392# Added pCreator to distinguish SwFrameShell from SwWrtShell. */ + static void CreateSelection( SwWrtShell & rSh, + const SwFrameShell * pCreator = nullptr ); + static void ClearSelection( SwWrtShell& rSh, + const SwFrameShell * pCreator = nullptr ); + + // the related SwView is being closed and the SwTransferable is invalid now + void Invalidate() {m_pWrtShell = nullptr;} + static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId(); + + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override; + + static void SelectPasteFormat(TransferableDataHelper& rData, sal_uInt8& nAction, + SotClipboardFormatId& nFormat); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swmessdialog.hxx b/sw/source/uibase/inc/swmessdialog.hxx new file mode 100644 index 000000000..0c95e5e6a --- /dev/null +++ b/sw/source/uibase/inc/swmessdialog.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SWMESSDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWMESSDIALOG_HXX + +#include <vcl/weld.hxx> + +class SwMessageAndEditDialog : public weld::MessageDialogController +{ +protected: + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xOKPB; +public: + SwMessageAndEditDialog(weld::Window* pParent, const OString& rID, + const OUString& rUIXMLDescription); +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_SWMESSDIALOG_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swmodalredlineacceptdlg.hxx b/sw/source/uibase/inc/swmodalredlineacceptdlg.hxx new file mode 100644 index 000000000..5c5450d53 --- /dev/null +++ b/sw/source/uibase/inc/swmodalredlineacceptdlg.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_SWMODALREDLINEACCEPTDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWMODALREDLINEACCEPTDLG_HXX + +#include <sfx2/basedlgs.hxx> + +class SwRedlineAcceptDlg; + +class SwModalRedlineAcceptDlg : public SfxDialogController +{ + std::unique_ptr<weld::Container> m_xContentArea; + std::unique_ptr<SwRedlineAcceptDlg> m_xImplDlg; + +public: + SwModalRedlineAcceptDlg(weld::Window *pParent); + virtual ~SwModalRedlineAcceptDlg() override; + + void AcceptAll( bool bAccept ); + virtual void Activate() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swrenamexnameddlg.hxx b/sw/source/uibase/inc/swrenamexnameddlg.hxx new file mode 100644 index 000000000..b4540857e --- /dev/null +++ b/sw/source/uibase/inc/swrenamexnameddlg.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_UIBASE_INC_SWRENAMEXNAMEDDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWRENAMEXNAMEDDLG_HXX + +#include <vcl/textfilter.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> + +class SwRenameXNamedDlg : public weld::GenericDialogController +{ + css::uno::Reference< css::container::XNamed > & xNamed; + css::uno::Reference< css::container::XNameAccess > & xNameAccess; + css::uno::Reference< css::container::XNameAccess > xSecondAccess; + css::uno::Reference< css::container::XNameAccess > xThirdAccess; + + TextFilter m_aTextFilter; + + std::unique_ptr<weld::Entry> m_xNewNameED; + std::unique_ptr<weld::Button> m_xOk; + + DECL_LINK(TextFilterHdl, OUString&, bool); + DECL_LINK(OkHdl, weld::Button&, void); + DECL_LINK(ModifyHdl, weld::Entry&, void); + +public: + SwRenameXNamedDlg(weld::Window* pParent, + css::uno::Reference< css::container::XNamed > & xNamed, + css::uno::Reference< css::container::XNameAccess > & xNameAccess ); + + void SetForbiddenChars(const OUString& rSet) + { + m_aTextFilter.SetForbiddenChars(rSet); + } + + void SetAlternativeAccess( + css::uno::Reference< css::container::XNameAccess > const & xSecond, + css::uno::Reference< css::container::XNameAccess > const & xThird ) + { + xSecondAccess = xSecond; + xThirdAccess = xThird; + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swruler.hxx b/sw/source/uibase/inc/swruler.hxx new file mode 100644 index 000000000..2dac3e6c7 --- /dev/null +++ b/sw/source/uibase/inc/swruler.hxx @@ -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/. + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SWRULER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWRULER_HXX + +#include <svx/ruler.hxx> +#include <vcl/timer.hxx> +#include <vcl/virdev.hxx> + +class SwViewShell; +class View; +namespace vcl { class Window; } +class SwEditWin; + +/** + * An horizontal ruler with a control for comment panel visibility for Writer. + * + * The comment control only appears when the document has comments already. + */ +class SwCommentRuler final : public SvxRuler +{ +public: + SwCommentRuler ( + SwViewShell* pViewSh, + vcl::Window* pParent, + SwEditWin* pWin, + SvxRulerSupportFlags nRulerFlags, + SfxBindings& rBindings, + WinBits nWinStyle); + virtual ~SwCommentRuler () override; + virtual void dispose() override; + + /** + * Paint the ruler. + * \param rRect ignored + */ + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + std::string CreateJsonNotification(); + +private: + SwViewShell * mpViewShell; //< Shell to check if there is any comments on doc and their visibility + VclPtr<SwEditWin> mpSwWin; //< Used to get SwView to change the SideBar visibility + bool mbIsHighlighted; //< If comment control is highlighted (mouse is over it) + Timer maFadeTimer; //< Timer for high/'low'light fading + int mnFadeRate; //< From 0 to 100. 0 means not highlighted. + ScopedVclPtr<VirtualDevice> maVirDev; //< VirtualDevice of this window. Just for convenience. + + void NotifyKit(); + /** + * Callback function to handle a mouse button down event. + * + * When on comment control, it toggles the comment panel visibility. + */ + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + /** + * Callback function to handle a mouse move event. + * + * When on comment control, it let the control responsive by highlighting. + */ + virtual void MouseMove(const MouseEvent& rMEvt) override; + /** + * Callback function to handle a context menu call (mouse right button click). + * + * When on comment control, it does not do anything. + */ + virtual void Command( const CommandEvent& rCEvt ) override; + /** + * Update the view. + * + * Update the ruler appearance according to the document view and + * current cursor position. + */ + virtual void Update() override; + + /** + * Get the rectangle area that should be used to draw the comment control. + * + * It is horizontally aligned to the SideBar panel. + * \return The area where the comment control is. + */ + tools::Rectangle GetCommentControlRegion(); + + /** + * Paint the comment control on VirtualDevice. + */ + void DrawCommentControl(vcl::RenderContext& rRenderContext); + + /** + * Update the tooltip text. + */ + void UpdateCommentHelpText(); + + /** + * Get the proper color between two options, according to current status. + * + * The return color can be one of the given colors, or a merged one. + * It depends on highlight fading status. + * + * \param rHighColor color used to highlight status + * \param rLowColor color used to normal status + * \return The proper color to used in moment + */ + Color GetFadedColor(const Color &rHighColor, const Color &rLowColor); + + /// Fade timer callback. + DECL_LINK(FadeHandler, Timer *, void); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swtablerep.hxx b/sw/source/uibase/inc/swtablerep.hxx new file mode 100644 index 000000000..82f9fb92b --- /dev/null +++ b/sw/source/uibase/inc/swtablerep.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_SWTABLEREP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWTABLEREP_HXX + +#include <memory> +#include <swdllapi.h> +#include <swtypes.hxx> + +class SwTabCols; +struct TColumn; +class SW_DLLPUBLIC SwTableRep +{ + std::unique_ptr<TColumn[]> m_pTColumns; + + SwTwips m_nTableWidth; + SwTwips m_nSpace; + SwTwips m_nLeftSpace; + SwTwips m_nRightSpace; + sal_uInt16 m_nAlign; + sal_uInt16 m_nColCount; + sal_uInt16 m_nAllCols; + sal_uInt16 m_nWidthPercent; + bool m_bLineSelected : 1; + bool m_bWidthChanged : 1; + bool m_bColsChanged : 1; + +public: + SwTableRep( const SwTabCols& rTabCol ); + ~SwTableRep(); + + bool FillTabCols( SwTabCols& rTabCol ) const; + + SwTwips GetLeftSpace() const {return m_nLeftSpace;} + void SetLeftSpace(SwTwips nSet) {m_nLeftSpace = nSet;} + + SwTwips GetRightSpace() const {return m_nRightSpace;} + void SetRightSpace(SwTwips nSet) {m_nRightSpace = nSet;} + + SwTwips GetWidth() const {return m_nTableWidth;} + void SetWidth(SwTwips nSet) {m_nTableWidth = nSet;} + + sal_uInt16 GetWidthPercent() const {return m_nWidthPercent;} + void SetWidthPercent(sal_uInt16 nSet) {m_nWidthPercent = nSet;} + + sal_uInt16 GetAlign() const {return m_nAlign;} + void SetAlign(sal_uInt16 nSet) {m_nAlign = nSet;} + + sal_uInt16 GetColCount() const {return m_nColCount;} + sal_uInt16 GetAllColCount() const {return m_nAllCols;} + + bool HasColsChanged() const {return m_bColsChanged;} + void SetColsChanged() {m_bColsChanged = true;} + + bool HasWidthChanged() const {return m_bWidthChanged;} + void SetWidthChanged() {m_bWidthChanged = true;} + + bool IsLineSelected() const {return m_bLineSelected;} + void SetLineSelected(bool bSet) {m_bLineSelected = bSet;} + + SwTwips GetSpace() const { return m_nSpace;} + void SetSpace(SwTwips nSet) {m_nSpace = nSet;} + + TColumn* GetColumns() const {return m_pTColumns.get();} +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swuiccoll.hxx b/sw/source/uibase/inc/swuiccoll.hxx new file mode 100644 index 000000000..4e0f5c2b2 --- /dev/null +++ b/sw/source/uibase/inc/swuiccoll.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_UIBASE_INC_SWUICCOLL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWUICCOLL_HXX + +#include <sfx2/tabdlg.hxx> + +#include <ccoll.hxx> + +class SwWrtShell; +class SwFormat; + +class SwCondCollPage : public SfxTabPage +{ + std::vector<OUString> m_aStrArr; + + SwWrtShell &m_rSh; + const CommandStruct*m_pCmds; + SwFormat* m_pFormat; + + bool m_bNewTemplate; + + std::unique_ptr<weld::CheckButton> m_xConditionCB; + std::unique_ptr<weld::TreeView> m_xTbLinks; + std::unique_ptr<weld::TreeView> m_xStyleLB; + std::unique_ptr<weld::ComboBox> m_xFilterLB; + std::unique_ptr<weld::Button> m_xRemovePB; + std::unique_ptr<weld::Button> m_xAssignPB; + + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + + DECL_LINK(OnOffHdl, weld::ToggleButton&, void); + DECL_LINK(AssignRemoveTreeListBoxHdl, weld::TreeView&, bool); + DECL_LINK(AssignRemoveClickHdl, weld::Button&, void); + DECL_LINK(SelectTreeListBoxHdl, weld::TreeView&, void); + DECL_LINK(SelectListBoxHdl, weld::ComboBox&, void); + void AssignRemove(const weld::Widget*); + void SelectHdl(const weld::Widget*); + + static const sal_uInt16 m_aPageRg[]; + +public: + SwCondCollPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + virtual ~SwCondCollPage() override; + + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + static const sal_uInt16* GetRanges() { return m_aPageRg; } + + virtual bool FillItemSet( SfxItemSet *rSet) override; + virtual void Reset (const SfxItemSet *rSet) override; + + void SetCollection( SwFormat* pFormat, bool bNew ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swuicnttab.hxx b/sw/source/uibase/inc/swuicnttab.hxx new file mode 100644 index 000000000..1628cf225 --- /dev/null +++ b/sw/source/uibase/inc/swuicnttab.hxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_SWUICNTTAB_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWUICNTTAB_HXX + +#include <sfx2/tabdlg.hxx> +#include <svx/langbox.hxx> +#include <vcl/weld.hxx> +#include <vcl/customweld.hxx> + +#include <tox.hxx> +#include "toxmgr.hxx" +#include "cnttab.hxx" +#include <vector> + +class IndexEntryResource; +class IndexEntrySupplierWrapper; +class SwTOXWidget; +class SwTOXEdit; +class SwTOXButton; +class SwTOXEntryTabPage; +class SwOneExampleFrame; +class SwWrtShell; + +namespace com::sun::star{ + namespace text{ + class XTextSection; + class XDocumentIndex; + } +} + +struct SwIndexSections_Impl +{ + css::uno::Reference< css::text::XTextSection > xContainerSection; + css::uno::Reference< css::text::XDocumentIndex > xDocumentIndex; +}; + +class SwMultiTOXTabDialog : public SfxTabDialogController +{ + std::unique_ptr<SwTOXMgr> m_pMgr; + SwWrtShell& m_rWrtShell; + + struct TypeData + { + std::unique_ptr<SwForm> m_pForm; + std::unique_ptr<SwTOXDescription> m_pDescription; + std::unique_ptr<SwIndexSections_Impl> m_pxIndexSections; + }; + std::vector<TypeData> m_vTypeData; + + SwTOXBase* m_pParamTOXBase; + + CurTOXType m_eCurrentTOXType; + + OUString m_sUserDefinedIndex; + sal_uInt16 m_nInitialTOXType; + + bool m_bEditTOX; + bool m_bExampleCreated; + bool m_bGlobalFlag; + + std::unique_ptr<SwOneExampleFrame> m_xExampleFrame; + std::unique_ptr<weld::CheckButton> m_xShowExampleCB; + std::unique_ptr<weld::CustomWeld> m_xExampleFrameWin; + + virtual short Ok() override; + std::unique_ptr<SwTOXDescription> CreateTOXDescFromTOXBase(const SwTOXBase*pCurTOX); + + DECL_LINK(CreateExample_Hdl, SwOneExampleFrame&, void); + DECL_LINK(ShowPreviewHdl, weld::ToggleButton&, void); + +public: + SwMultiTOXTabDialog(weld::Window* pParent, const SfxItemSet& rSet, + SwWrtShell &rShell, + SwTOXBase* pCurTOX, sal_uInt16 nToxType, + bool bGlobal); + virtual ~SwMultiTOXTabDialog() override; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + + SwForm* GetForm(CurTOXType eType); + + const CurTOXType& GetCurrentTOXType() const { return m_eCurrentTOXType;} + void SetCurrentTOXType(const CurTOXType& eSet) + { + m_eCurrentTOXType = eSet; + } + + bool IsTOXEditMode() const { return m_bEditTOX;} + + SwWrtShell& GetWrtShell() {return m_rWrtShell;} + + SwTOXDescription& GetTOXDescription(CurTOXType eTOXTypes); + void CreateOrUpdateExample( + TOXTypes nTOXIndex, sal_uInt16 nPage = 0, sal_uInt16 nCurLevel = USHRT_MAX); + + static bool IsNoNum(SwWrtShell& rSh, const OUString& rName); +}; + +class SwTOXSelectTabPage : public SfxTabPage +{ + std::unique_ptr<IndexEntryResource> pIndexRes; + + OUString aStyleArr[MAXLEVEL]; + OUString sAutoMarkURL; + OUString sAutoMarkType; + OUString sAddStyleUser; + OUString sAddStyleContent; + + std::unique_ptr<const IndexEntrySupplierWrapper> pIndexEntryWrapper; + + bool m_bWaitingInitialSettings; + + std::unique_ptr<weld::Entry> m_xTitleED; + std::unique_ptr<weld::Label> m_xTypeFT; + std::unique_ptr<weld::ComboBox> m_xTypeLB; + std::unique_ptr<weld::CheckButton> m_xReadOnlyCB; + + std::unique_ptr<weld::Widget> m_xAreaFrame; + std::unique_ptr<weld::ComboBox> m_xAreaLB; + std::unique_ptr<weld::Widget> m_xLevelFT; //content, user + std::unique_ptr<weld::SpinButton> m_xLevelNF; //content, user + + //content + std::unique_ptr<weld::Widget> m_xCreateFrame; // content, user, illustration + std::unique_ptr<weld::CheckButton> m_xFromHeadingsCB; + std::unique_ptr<weld::CheckButton> m_xStylesCB; + std::unique_ptr<weld::CheckButton> m_xAddStylesCB; + std::unique_ptr<weld::Button> m_xAddStylesPB; + //user + std::unique_ptr<weld::CheckButton> m_xFromTablesCB; + std::unique_ptr<weld::CheckButton> m_xFromFramesCB; + std::unique_ptr<weld::CheckButton> m_xFromGraphicsCB; + std::unique_ptr<weld::CheckButton> m_xFromOLECB; + std::unique_ptr<weld::CheckButton> m_xLevelFromChapterCB; + + //illustration + table + std::unique_ptr<weld::RadioButton> m_xFromCaptionsRB; + std::unique_ptr<weld::RadioButton> m_xFromObjectNamesRB; + + //illustration and tables + std::unique_ptr<weld::Label> m_xCaptionSequenceFT; + std::unique_ptr<weld::ComboBox> m_xCaptionSequenceLB; + std::unique_ptr<weld::Label> m_xDisplayTypeFT; + std::unique_ptr<weld::ComboBox> m_xDisplayTypeLB; + + //all but illustration and table + std::unique_ptr<weld::CheckButton> m_xTOXMarksCB; + + //index only + std::unique_ptr<weld::Widget> m_xIdxOptionsFrame; + std::unique_ptr<weld::CheckButton> m_xCollectSameCB; + std::unique_ptr<weld::CheckButton> m_xUseFFCB; + std::unique_ptr<weld::CheckButton> m_xUseDashCB; + std::unique_ptr<weld::CheckButton> m_xCaseSensitiveCB; + std::unique_ptr<weld::CheckButton> m_xInitialCapsCB; + std::unique_ptr<weld::CheckButton> m_xKeyAsEntryCB; + std::unique_ptr<weld::CheckButton> m_xFromFileCB; + std::unique_ptr<weld::MenuButton> m_xAutoMarkPB; + + // object only + std::unique_ptr<weld::TreeView> m_xFromObjCLB; + std::unique_ptr<weld::Widget> m_xFromObjFrame; + + std::unique_ptr<weld::CheckButton> m_xSequenceCB; + std::unique_ptr<weld::ComboBox> m_xBracketLB; + std::unique_ptr<weld::Widget> m_xAuthorityFrame; + + //all + std::unique_ptr<weld::Widget> m_xSortFrame; + std::unique_ptr<SvxLanguageBox> m_xLanguageLB; + std::unique_ptr<weld::ComboBox> m_xSortAlgorithmLB; + + DECL_LINK(TOXTypeHdl, weld::ComboBox&, void ); + DECL_LINK(AddStylesHdl, weld::Button&, void ); + DECL_LINK(MenuEnableHdl, weld::ToggleButton&, void); + DECL_LINK(MenuExecuteHdl, const OString&, void); + DECL_LINK(LanguageListBoxHdl, weld::ComboBox&, void); + void LanguageHdl(const weld::ComboBox*); + DECL_LINK(CheckBoxHdl, weld::ToggleButton&, void ); + DECL_LINK(RadioButtonHdl, weld::ToggleButton&, void); + DECL_LINK(ModifyEntryHdl, weld::Entry&, void); + DECL_LINK(ModifySpinHdl, weld::SpinButton&, void); + DECL_LINK(ModifyListBoxHdl, weld::ComboBox&, void); + + void ModifyHdl(); + void ApplyTOXDescription(); + void FillTOXDescription(); + +public: + SwTOXSelectTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet); + virtual ~SwTOXSelectTabPage() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + + virtual void ActivatePage( const SfxItemSet& ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + + void SelectType(TOXTypes eSet); //preset TOXType, GlobalDoc + void SetWrtShell(SwWrtShell const & rSh); +}; + +class SwTokenWindow +{ + SwForm* m_pForm; + sal_uInt16 m_nLevel; + bool m_bValid; + OUString m_aButtonTexts[TOKEN_END]; // Text of the buttons + OUString m_aButtonHelpTexts[TOKEN_END]; // QuickHelpText of the buttons + OUString m_sCharStyle; + Link<SwFormToken&,void> m_aButtonSelectedHdl; + SwTOXWidget* m_pActiveCtrl; + Link<LinkParamNone*,void> m_aModifyHdl; + OUString m_sAccessibleName; + OUString m_sAdditionalAccnameString1; + OUString m_sAdditionalAccnameString2; + OUString m_sAdditionalAccnameString3; + + Idle m_aAdjustPositionsIdle; + + SwTOXEntryTabPage* m_pParent; + std::unique_ptr<weld::Container> m_xParentWidget; + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Button> m_xLeftScrollWin; + std::unique_ptr<weld::Container> m_xCtrlParentWin; + std::unique_ptr<weld::ScrolledWindow> m_xScrollWin; + std::unique_ptr<weld::Button> m_xRightScrollWin; + std::vector<std::unique_ptr<SwTOXWidget>> m_aControlList; + + DECL_LINK(EditResize, SwTOXEdit&, void); + DECL_LINK(NextItemHdl, SwTOXEdit&, void); + DECL_LINK(TbxFocusHdl, SwTOXWidget&, void); + DECL_LINK(NextItemBtnHdl, SwTOXButton&, void); + DECL_LINK(TbxFocusBtnHdl, SwTOXWidget&, void); + DECL_LINK(ScrollBtnHdl, weld::Button&, void); + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + DECL_LINK(AdjustPositionsHdl, const Size&, void); + + void SetActiveControl(SwTOXWidget* pSet); + + SwTOXWidget* InsertItem(const OUString& rText, const SwFormToken& aToken); + void AdjustPositions(); + void AdjustScrolling(); + void MoveControls(long nOffset); + +public: + SwTokenWindow(std::unique_ptr<weld::Container> xParent); + weld::Container* get_child_container() { return m_xCtrlParentWin.get(); } + ~SwTokenWindow(); + + void SetTabPage(SwTOXEntryTabPage *pParent) { m_pParent = pParent; } + + void SetForm(SwForm& rForm, sal_uInt16 nLevel); + sal_uInt16 GetLastLevel()const {return m_nLevel;}; + + bool IsValid() const {return m_bValid;} + + void SetInvalid() {m_bValid = false;} + + OUString GetPattern() const; + + void SetButtonSelectedHdl(const Link<SwFormToken&,void>& rLink) + { m_aButtonSelectedHdl = rLink;} + + void SetModifyHdl(const Link<LinkParamNone*,void>& rLink){m_aModifyHdl = rLink;} + + SwTOXWidget* GetActiveControl() { return m_pActiveCtrl; } + + void InsertAtSelection(const SwFormToken& aToken); + void RemoveControl(const SwTOXButton* pDel, bool bInternalCall = false); + + bool Contains(FormTokenType) const; + + //helper for pattern buttons and edits + OUString CreateQuickHelp(const SwFormToken& rToken); + + void SetFocus2theAllBtn(); +private: + sal_uInt32 GetControlIndex(FormTokenType eType) const; +}; + +class SwTOXEntryTabPage : public SfxTabPage +{ + OUString sDelimStr; + OUString sLevelStr; + OUString sAuthTypeStr; + + OUString sNoCharStyle; + SwForm* m_pCurrentForm; + + CurTOXType aLastTOXType; + bool bInLevelHdl; + + std::unique_ptr<weld::Label> m_xTypeFT; + std::unique_ptr<weld::Label> m_xLevelFT; + std::unique_ptr<weld::TreeView> m_xLevelLB; + std::unique_ptr<weld::Button> m_xAllLevelsPB; + std::unique_ptr<weld::Button> m_xEntryNoPB; + std::unique_ptr<weld::Button> m_xEntryPB; + std::unique_ptr<weld::Button> m_xTabPB; + std::unique_ptr<weld::Button> m_xChapterInfoPB; + std::unique_ptr<weld::Button> m_xPageNoPB; + std::unique_ptr<weld::Button> m_xHyperLinkPB; + std::unique_ptr<weld::ComboBox> m_xAuthFieldsLB; + std::unique_ptr<weld::Button> m_xAuthInsertPB; + std::unique_ptr<weld::Button> m_xAuthRemovePB; + std::unique_ptr<weld::ComboBox> m_xCharStyleLB; // character style of the current token + std::unique_ptr<weld::Button> m_xEditStylePB; + std::unique_ptr<weld::Label> m_xChapterEntryFT; + std::unique_ptr<weld::ComboBox> m_xChapterEntryLB; // type of chapter info + std::unique_ptr<weld::Label> m_xNumberFormatFT; + std::unique_ptr<weld::ComboBox> m_xNumberFormatLB; //!< format for numbering (E#) + std::unique_ptr<weld::Label> m_xEntryOutlineLevelFT; //!< Fixed text, for i53420 + std::unique_ptr<weld::SpinButton> m_xEntryOutlineLevelNF; //!< level to evaluate outline level to, for i53420 + std::unique_ptr<weld::Label> m_xFillCharFT; + std::unique_ptr<weld::ComboBox> m_xFillCharCB; // fill char for tab stop + std::unique_ptr<weld::Label> m_xTabPosFT; + std::unique_ptr<weld::MetricSpinButton> m_xTabPosMF; // tab stop position + std::unique_ptr<weld::CheckButton> m_xAutoRightCB; + std::unique_ptr<weld::Widget> m_xFormatFrame; + std::unique_ptr<weld::Label> m_xMainEntryStyleFT; + std::unique_ptr<weld::ComboBox> m_xMainEntryStyleLB; // character style of main entries in indexes + std::unique_ptr<weld::CheckButton> m_xAlphaDelimCB; + std::unique_ptr<weld::CheckButton> m_xCommaSeparatedCB; + std::unique_ptr<weld::CheckButton> m_xRelToStyleCB; // position relative to the right margin of the para style + std::unique_ptr<weld::Widget> m_xSortingFrame; + std::unique_ptr<weld::RadioButton> m_xSortDocPosRB; + std::unique_ptr<weld::RadioButton> m_xSortContentRB; + std::unique_ptr<weld::Widget> m_xSortKeyFrame; + std::unique_ptr<weld::ComboBox> m_xFirstKeyLB; + std::unique_ptr<weld::RadioButton> m_xFirstSortUpRB; + std::unique_ptr<weld::RadioButton> m_xFirstSortDownRB; + std::unique_ptr<weld::ComboBox> m_xSecondKeyLB; + std::unique_ptr<weld::RadioButton> m_xSecondSortUpRB; + std::unique_ptr<weld::RadioButton> m_xSecondSortDownRB; + std::unique_ptr<weld::ComboBox> m_xThirdKeyLB; + std::unique_ptr<weld::RadioButton> m_xThirdSortUpRB; + std::unique_ptr<weld::RadioButton> m_xThirdSortDownRB; + std::unique_ptr<SwTokenWindow> m_xTokenWIN; + + DECL_LINK(StyleSelectHdl, weld::ComboBox&, void); + DECL_LINK(EditStyleHdl, weld::Button&, void); + DECL_LINK(InsertTokenHdl, weld::Button&, void); + DECL_LINK(LevelHdl, weld::TreeView&, void); + DECL_LINK(AutoRightHdl, weld::ToggleButton&, void); + DECL_LINK(TokenSelectedHdl, SwFormToken&, void); + DECL_LINK(TabPosHdl, weld::MetricSpinButton&, void); + DECL_LINK(FillCharHdl, weld::ComboBox&, void); + DECL_LINK(RemoveInsertAuthHdl, weld::Button&, void); + DECL_LINK(SortKeyHdl, weld::ToggleButton&, void); + DECL_LINK(ChapterInfoHdl, weld::ComboBox&, void); + DECL_LINK(ChapterInfoOutlineHdl, weld::SpinButton&, void); + DECL_LINK(NumberFormatHdl, weld::ComboBox&, void); + + DECL_LINK(AllLevelsHdl, weld::Button&, void); + + void WriteBackLevel(); + void UpdateDescriptor(); + DECL_LINK(ModifyHdl, LinkParamNone*, void); + void OnModify(bool bAllLevels); + DECL_LINK(ModifyClickHdl, weld::ToggleButton&, void); + +public: + SwTOXEntryTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet); + virtual ~SwTOXEntryTabPage() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + virtual void ActivatePage( const SfxItemSet& ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + void SetWrtShell(SwWrtShell& rSh); + + void PreTokenButtonRemoved(const SwFormToken& rToken); + void SetFocus2theAllBtn(); +}; + +class SwTOXStylesTabPage : public SfxTabPage +{ + std::unique_ptr<SwForm> m_pCurrentForm; + + std::unique_ptr<weld::TreeView> m_xLevelLB; + std::unique_ptr<weld::Button> m_xAssignBT; + std::unique_ptr<weld::TreeView> m_xParaLayLB; + std::unique_ptr<weld::Button> m_xStdBT; + std::unique_ptr<weld::Button> m_xEditStyleBT; + + DECL_LINK(EditStyleHdl, weld::Button&, void); + DECL_LINK(StdHdl, weld::Button&, void); + DECL_LINK(EnableSelectHdl, weld::TreeView&, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(AssignHdl, weld::Button&, void); + void Modify(); + + SwForm& GetForm() + { + SwMultiTOXTabDialog* pDlg = static_cast<SwMultiTOXTabDialog*>(GetDialogController()); + return *pDlg->GetForm(pDlg->GetCurrentTOXType()); + } + +public: + SwTOXStylesTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet); + virtual ~SwTOXStylesTabPage() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet* ) override; + + virtual void ActivatePage( const SfxItemSet& ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrSet); + +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_SWUICNTTAB_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swuiidxmrk.hxx b/sw/source/uibase/inc/swuiidxmrk.hxx new file mode 100644 index 000000000..2bd2fb4e4 --- /dev/null +++ b/sw/source/uibase/inc/swuiidxmrk.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_SWUIIDXMRK_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWUIIDXMRK_HXX + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/i18n/XExtendedIndexEntrySupplier.hpp> +#include <sfx2/basedlgs.hxx> +#include <sfx2/childwin.hxx> +#include <vcl/weld.hxx> +#include <toxe.hxx> +#include <memory> + +class SwWrtShell; +class SwTOXMgr; +class SwTOXMark; + +// insert mark for index entry +class SwIndexMarkFloatDlg; +class SwIndexMarkModalDlg; + +class SwIndexMarkPane +{ + std::shared_ptr<weld::Dialog> m_xDialog; + + friend class SwIndexMarkFloatDlg; + friend class SwIndexMarkModalDlg; + + OUString m_aOrgStr; + bool m_bDel; + bool const m_bNewMark; + bool m_bSelected; + + bool m_bPhoneticED0_ChangedByUser; + bool m_bPhoneticED1_ChangedByUser; + bool m_bPhoneticED2_ChangedByUser; + LanguageType m_nLangForPhoneticReading; //Language of current text used for phonetic reading proposal + bool m_bIsPhoneticReadingEnabled; //this value states whether phonetic reading is enabled in principle dependent of global cjk settings and language of current entry + css::uno::Reference< css::i18n::XExtendedIndexEntrySupplier > + m_xExtendedIndexEntrySupplier; + + std::unique_ptr<SwTOXMgr> + m_pTOXMgr; + SwWrtShell* m_pSh; + + std::unique_ptr<weld::Label> m_xTypeFT; + std::unique_ptr<weld::ComboBox> m_xTypeDCB; + std::unique_ptr<weld::Button> m_xNewBT; + std::unique_ptr<weld::Entry> m_xEntryED; + std::unique_ptr<weld::Button> m_xSyncED; + std::unique_ptr<weld::Label> m_xPhoneticFT0; + std::unique_ptr<weld::Entry> m_xPhoneticED0; + std::unique_ptr<weld::Label> m_xKey1FT; + std::unique_ptr<weld::ComboBox> m_xKey1DCB; + std::unique_ptr<weld::Label> m_xPhoneticFT1; + std::unique_ptr<weld::Entry> m_xPhoneticED1; + std::unique_ptr<weld::Label> m_xKey2FT; + std::unique_ptr<weld::ComboBox> m_xKey2DCB; + std::unique_ptr<weld::Label> m_xPhoneticFT2; + std::unique_ptr<weld::Entry> m_xPhoneticED2; + std::unique_ptr<weld::Label> m_xLevelFT; + std::unique_ptr<weld::SpinButton> m_xLevelNF; + std::unique_ptr<weld::CheckButton> m_xMainEntryCB; + std::unique_ptr<weld::CheckButton> m_xApplyToAllCB; + std::unique_ptr<weld::CheckButton> m_xSearchCaseSensitiveCB; + std::unique_ptr<weld::CheckButton> m_xSearchCaseWordOnlyCB; + std::unique_ptr<weld::Button> m_xOKBT; + std::unique_ptr<weld::Button> m_xCloseBT; + std::unique_ptr<weld::Button> m_xDelBT; + std::unique_ptr<weld::Button> m_xPrevSameBT; + std::unique_ptr<weld::Button> m_xNextSameBT; + std::unique_ptr<weld::Button> m_xPrevBT; + std::unique_ptr<weld::Button> m_xNextBT; + + void Apply(); + void InitControls(); + void InsertMark(); + void UpdateMark(); + + DECL_LINK(InsertHdl, weld::Button&, void); + DECL_LINK(CloseHdl, weld::Button&, void); + DECL_LINK(SyncSelectionHdl, weld::Button&, void); + DECL_LINK(DelHdl, weld::Button&, void); + DECL_LINK( NextHdl, weld::Button&, void ); + DECL_LINK( NextSameHdl, weld::Button&, void ); + DECL_LINK( PrevHdl, weld::Button&, void ); + DECL_LINK( PrevSameHdl, weld::Button&, void ); + DECL_LINK( ModifyListBoxHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyEditHdl, weld::Entry&, void ); + void ModifyHdl(const weld::Widget& rWidget); + DECL_LINK( KeyDCBModifyHdl, weld::ComboBox&, void ); + DECL_LINK( NewUserIdxHdl, weld::Button&, void ); + DECL_LINK( SearchTypeHdl, weld::ToggleButton&, void ); + DECL_LINK( PhoneticEDModifyHdl, weld::Entry&, void ); + + //this method updates the values from 'nLangForPhoneticReading' and 'bIsPhoneticReadingEnabled' + //it needs to be called ones if this dialog is opened to create a new entry (in InitControls), + //or otherwise it has to be called for each changed TOXMark (in UpdateDialog) + void UpdateLanguageDependenciesForPhoneticReading(); + OUString GetDefaultPhoneticReading( const OUString& rText ); + + void UpdateKeyBoxes(); + + void UpdateDialog(); + void InsertUpdate(); + + void Activate(); + +public: + + SwIndexMarkPane(const std::shared_ptr<weld::Dialog>& rDialog, weld::Builder& rBuilder, + bool bNewDlg, SwWrtShell& rWrtShell); + + ~SwIndexMarkPane(); + + void ReInitDlg(SwWrtShell& rWrtShell, SwTOXMark const * pCurTOXMark = nullptr); + bool IsTOXType(const OUString& rName) { return m_xTypeDCB->find_text(rName) != -1; } +}; + +class SwIndexMarkFloatDlg : public SfxModelessDialogController +{ + SwIndexMarkPane m_aContent; + + virtual void Activate() override; +public: + SwIndexMarkFloatDlg(SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo const * pInfo, + bool bNew); + void ReInitDlg(SwWrtShell& rWrtShell); +}; + +class SwIndexMarkModalDlg : public SfxDialogController +{ + SwIndexMarkPane m_aContent; +public: + SwIndexMarkModalDlg(weld::Window *pParent, SwWrtShell& rSh, SwTOXMark const * pCurTOXMark); + virtual ~SwIndexMarkModalDlg() override; + virtual short int run() override; +}; + +class SwAuthMarkModalDlg; + +class SwAuthorMarkPane +{ + weld::DialogController& m_rDialog; + + static bool bIsFromComponent; + + friend class SwAuthMarkModalDlg; + friend class SwAuthMarkFloatDlg; + + bool bNewEntry; + bool bBibAccessInitialized; + + SwWrtShell* pSh; + + OUString m_sColumnTitles[AUTH_FIELD_END]; + OUString m_sFields[AUTH_FIELD_END]; + + OUString m_sCreatedEntry[AUTH_FIELD_END]; + + css::uno::Reference< css::container::XNameAccess > xBibAccess; + + std::unique_ptr<weld::RadioButton> m_xFromComponentRB; + std::unique_ptr<weld::RadioButton> m_xFromDocContentRB; + std::unique_ptr<weld::Label> m_xAuthorFI; + std::unique_ptr<weld::Label> m_xTitleFI; + std::unique_ptr<weld::Entry> m_xEntryED; + std::unique_ptr<weld::ComboBox> m_xEntryLB; + std::unique_ptr<weld::Button> m_xActionBT; + std::unique_ptr<weld::Button> m_xCloseBT; + std::unique_ptr<weld::Button> m_xCreateEntryPB; + std::unique_ptr<weld::Button> m_xEditEntryPB; + + DECL_LINK(InsertHdl, weld::Button&, void); + DECL_LINK(CloseHdl, weld::Button&, void); + DECL_LINK(CreateEntryHdl, weld::Button&, void); + DECL_LINK(CompEntryHdl, weld::ComboBox&, void); + DECL_LINK(ChangeSourceHdl, weld::ToggleButton&, void); + DECL_LINK(IsEditAllowedHdl, weld::Entry&, bool); + DECL_LINK(IsEntryAllowedHdl, weld::Entry&, bool); + DECL_LINK(EditModifyHdl, weld::Entry&, void); + + void InitControls(); + void Activate(); + +public: + SwAuthorMarkPane(weld::DialogController& rDialog, weld::Builder& rBuilder, bool bNew); + void ReInitDlg(SwWrtShell& rWrtShell); +}; + +class SwAuthMarkFloatDlg : public SfxModelessDialogController +{ + SwAuthorMarkPane m_aContent; + virtual void Activate() override; +public: + SwAuthMarkFloatDlg(SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo const * pInfo, + bool bNew); + void ReInitDlg(SwWrtShell& rWrtShell); +}; + +class SwAuthMarkModalDlg : public SfxDialogController +{ + SwAuthorMarkPane m_aContent; + + void Apply(); +public: + SwAuthMarkModalDlg(weld::Window *pParent, SwWrtShell& rSh); + virtual short int run() override; +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_SWUIIDXMRK_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swuipardlg.hxx b/sw/source/uibase/inc/swuipardlg.hxx new file mode 100644 index 000000000..bd282a470 --- /dev/null +++ b/sw/source/uibase/inc/swuipardlg.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_UIBASE_INC_SWUIPARDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWUIPARDLG_HXX + +#include <view.hxx> + +#include <sfx2/tabdlg.hxx> + +class SwParaDlg: public SfxTabDialogController +{ + SwView& rView; + bool bDrawParaDlg; + + void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +public: + SwParaDlg(weld::Window *pParent, + SwView& rVw, + const SfxItemSet&, + sal_uInt8 nDialogMode, + const OUString *pCollName, + bool bDraw = false, + const OString& sDefPage = OString()); + virtual ~SwParaDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/swwrtshitem.hxx b/sw/source/uibase/inc/swwrtshitem.hxx new file mode 100644 index 000000000..45be074db --- /dev/null +++ b/sw/source/uibase/inc/swwrtshitem.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_UIBASE_INC_SWWRTSHITEM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SWWRTSHITEM_HXX +#include <svl/poolitem.hxx> +#include <swdllapi.h> + +class SwWrtShell; + +class SW_DLLPUBLIC SwWrtShellItem: public SfxPoolItem +{ + SwWrtShell* pWrtSh; + +public: + SwWrtShellItem( SwWrtShell* pWrtSh ); + + virtual bool operator==( const SfxPoolItem& ) const override; + virtual SwWrtShellItem* Clone( SfxItemPool *pPool = nullptr ) const override; + + SwWrtShell* GetValue() const { return pWrtSh; } + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/syncbtn.hxx b/sw/source/uibase/inc/syncbtn.hxx new file mode 100644 index 000000000..43ae7ac43 --- /dev/null +++ b/sw/source/uibase/inc/syncbtn.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_UIBASE_INC_SYNCBTN_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_SYNCBTN_HXX + +#include <sfx2/basedlgs.hxx> +#include <sfx2/childwin.hxx> + +class SwSyncBtnDlg : public SfxModelessDialogController +{ + std::unique_ptr<weld::Button> m_xSyncBtn; + + DECL_STATIC_LINK( SwSyncBtnDlg, BtnHdl, weld::Button&, void ); + +public: + SwSyncBtnDlg(SfxBindings*, SfxChildWindow*, weld::Window *pParent); + virtual ~SwSyncBtnDlg() override; +}; + +class SwSyncChildWin : public SfxChildWindow +{ +public: + SwSyncChildWin(vcl::Window*, + sal_uInt16 nId, + SfxBindings*, + SfxChildWinInfo*); + + SFX_DECL_CHILDWINDOW(SwSyncChildWin); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tabledlg.hxx b/sw/source/uibase/inc/tabledlg.hxx new file mode 100644 index 000000000..3d4af57d8 --- /dev/null +++ b/sw/source/uibase/inc/tabledlg.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_TABLEDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TABLEDLG_HXX + +#include <sfx2/tabdlg.hxx> + +class SwWrtShell; +struct TColumn; + +// table dialog +class SwTableTabDlg : public SfxTabDialogController +{ + SwWrtShell* pShell; + + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; +public: + SwTableTabDlg(weld::Window* pParent, const SfxItemSet* pItemSet, SwWrtShell* pSh); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tablemgr.hxx b/sw/source/uibase/inc/tablemgr.hxx new file mode 100644 index 000000000..227ac0744 --- /dev/null +++ b/sw/source/uibase/inc/tablemgr.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_UIBASE_INC_TABLEMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TABLEMGR_HXX + +#include <swdllapi.h> +#include <swtypes.hxx> +#include <tabcol.hxx> + +#include <vcl/weld.hxx> + +class SwFrameFormat; +class SwWrtShell; +namespace vcl { class Window; } +class SwFlyFrameFormat; + +namespace com::sun::star { + namespace frame { class XModel; } + namespace chart2 { + namespace data { + class XDataProvider; } } +} + +const SwTwips lAutoWidth = INVALID_TWIPS; +const char cParaDelim = 0x0a; + +class SW_DLLPUBLIC SwTableFUNC +{ + SwFrameFormat *pFormat; + SwWrtShell *pSh; + SwTabCols aCols; + +private: + SAL_DLLPRIVATE int GetRightSeparator(int nNum) const; + +public: + SwTableFUNC(SwWrtShell *pShell); + ~SwTableFUNC(); + + void InitTabCols(); + void ColWidthDlg(weld::Window *pParent); + SwTwips GetColWidth(sal_uInt16 nNum) const; + SwTwips GetMaxColWidth(sal_uInt16 nNum) const; + void SetColWidth(sal_uInt16 nNum, SwTwips nWidth ); + sal_uInt16 GetColCount() const; + sal_uInt16 GetCurColNum() const; + + SwWrtShell* GetShell() const { return pSh; } + + // @deprecated + void UpdateChart(); + + /// @return the XModel of the newly inserted chart if successful + css::uno::Reference< css::frame::XModel > + InsertChart( css::uno::Reference< css::chart2::data::XDataProvider > const &rxDataProvider, bool bFillWithData, const OUString &rCellRange, SwFlyFrameFormat** ppFlyFrameFormat = nullptr ); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tabsh.hxx b/sw/source/uibase/inc/tabsh.hxx new file mode 100644 index 000000000..c29d822a7 --- /dev/null +++ b/sw/source/uibase/inc/tabsh.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_UIBASE_INC_TABSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TABSH_HXX + +#include "basesh.hxx" +#include <swdllapi.h> + +class SfxItemSet; +class SwWrtShell; + +SW_DLLPUBLIC void ItemSetToTableParam( const SfxItemSet& rSet, SwWrtShell &rSh ); + +SW_DLLPUBLIC const sal_uInt16* SwuiGetUITableAttrRange(); + +class SwTableShell: public SwBaseShell +{ +public: + SFX_DECL_INTERFACE(SW_TABSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + void Execute(SfxRequest &); + void GetState(SfxItemSet &); + void GetFrameBorderState(SfxItemSet &rSet); + void GetLineStyleState(SfxItemSet &rSet); + void ExecTableStyle(SfxRequest& rReq); + + void ExecNumberFormat(SfxRequest const & rReq); + + SwTableShell(SwView &rView); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tautofmt.hxx b/sw/source/uibase/inc/tautofmt.hxx new file mode 100644 index 000000000..8438e5701 --- /dev/null +++ b/sw/source/uibase/inc/tautofmt.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_UIBASE_INC_TAUTOFMT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TAUTOFMT_HXX + +#include <tools/link.hxx> +#include <sfx2/basedlgs.hxx> +#include <sal/types.h> +#include <rtl/ustring.hxx> +#include <vcl/weld.hxx> + +#include "wrtsh.hxx" +#include "autoformatpreview.hxx" +#include <tblafmt.hxx> + +class SwTableAutoFormat; +class SwTableAutoFormatTable; +class SwWrtShell; + +class SwAutoFormatDlg : public SfxDialogController +{ + OUString m_aStrTitle; + OUString m_aStrLabel; + OUString m_aStrClose; + OUString m_aStrDelTitle; + OUString m_aStrDelMsg; + OUString m_aStrRenameTitle; + OUString m_aStrInvalidFormat; + + SwWrtShell* m_pShell; + sal_uInt8 m_nIndex; + sal_uInt8 m_nDfltStylePos; + bool m_bCoreDataChanged : 1; + bool m_bSetAutoFormat : 1; + + AutoFormatPreview m_aWndPreview; + std::unique_ptr<SwTableAutoFormatTable> m_xTableTable; + + std::unique_ptr<weld::TreeView> m_xLbFormat; + std::unique_ptr<weld::CheckButton> m_xBtnNumFormat; + std::unique_ptr<weld::CheckButton> m_xBtnBorder; + std::unique_ptr<weld::CheckButton> m_xBtnFont; + std::unique_ptr<weld::CheckButton> m_xBtnPattern; + std::unique_ptr<weld::CheckButton> m_xBtnAlignment; + std::unique_ptr<weld::Button> m_xBtnCancel; + std::unique_ptr<weld::Button> m_xBtnAdd; + std::unique_ptr<weld::Button> m_xBtnRemove; + std::unique_ptr<weld::Button> m_xBtnRename; + std::unique_ptr<weld::CustomWeld> m_xWndPreview; + + void Init( const SwTableAutoFormat* pSelFormat ); + void UpdateChecks( const SwTableAutoFormat&, bool bEnableBtn ); + + DECL_LINK(CheckHdl, weld::ToggleButton&, void); + DECL_LINK(AddHdl, weld::Button&, void); + DECL_LINK(RemoveHdl, weld::Button&, void); + DECL_LINK(RenameHdl, weld::Button&, void); + DECL_LINK(SelFormatHdl, weld::TreeView&, void); + +public: + SwAutoFormatDlg(weld::Window* pParent, SwWrtShell* pShell, + bool bSetAutoFormat, const SwTableAutoFormat* pSelFormat); + + virtual short run() override; + + std::unique_ptr<SwTableAutoFormat> FillAutoFormatOfIndex() const; + + virtual ~SwAutoFormatDlg() override; +}; + +#endif // SW_AUTOFMT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tblnumfm.hxx b/sw/source/uibase/inc/tblnumfm.hxx new file mode 100644 index 000000000..300688b92 --- /dev/null +++ b/sw/source/uibase/inc/tblnumfm.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_UIBASE_INC_TBLNUMFM_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TBLNUMFM_HXX + +#include <sfx2/basedlgs.hxx> + +namespace weld { class Window; } +class SfxItemSet; + +class SwNumFormatDlg : public SfxSingleTabDialogController +{ +public: + SwNumFormatDlg(weld::Widget* pParent, const SfxItemSet& rSet); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/textsh.hxx b/sw/source/uibase/inc/textsh.hxx new file mode 100644 index 000000000..849bffcf4 --- /dev/null +++ b/sw/source/uibase/inc/textsh.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_UIBASE_INC_TEXTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TEXTSH_HXX + +#include "basesh.hxx" +#include <unotools/caserotate.hxx> +#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp> + +class AbstractSvxPostItDialog; +class SwFieldMgr; +class SwFlyFrameAttrMgr; +class SvxHyperlinkItem; +class SwInsertChart; + +class SW_DLLPUBLIC SwTextShell: public SwBaseShell +{ + RotateTransliteration m_aRotateCase; + + void InsertSymbol( SfxRequest& ); + void InsertHyperlink( const SvxHyperlinkItem& rHlnkItem ); + bool InsertMediaDlg( SfxRequest const & ); + +public: + SFX_DECL_INTERFACE(SW_TEXTSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + DECL_LINK( RedlineNextHdl, AbstractSvxPostItDialog&, void ); + DECL_LINK( RedlinePrevHdl, AbstractSvxPostItDialog&, void ); + DECL_STATIC_LINK( SwTextShell, DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, void ); + + void Execute(SfxRequest &); + void GetState(SfxItemSet &); + + void ExecInsert(SfxRequest &); + void StateInsert(SfxItemSet&); + void ExecDelete(SfxRequest &); + void ExecEnterNum(SfxRequest &); + void ExecBasicMove(SfxRequest &); + void ExecMove(SfxRequest &); + void ExecMovePage(SfxRequest &); + void ExecMoveCol(SfxRequest &); + void ExecMoveLingu(SfxRequest &); + void ExecMoveMisc(SfxRequest &); + void ExecField(SfxRequest &rReq); + void ExecSetNumber(SfxRequest const &); + void StateField(SfxItemSet &); + void ExecIdx(SfxRequest const &); + void GetIdxState(SfxItemSet &); + void ExecGlossary(SfxRequest &); + + void ExecCharAttr(SfxRequest &); + void ExecCharAttrArgs(SfxRequest &); + void ExecParaAttr(SfxRequest &); + void ExecParaAttrArgs(SfxRequest &); + void ExecDB(SfxRequest const &); + void ExecTransliteration(SfxRequest const &); + void ExecRotateTransliteration(SfxRequest const &); + + void GetAttrState(SfxItemSet &); + + SwTextShell(SwView &rView); + virtual ~SwTextShell() override; + /// Create item set for the insert frame dialog. + SfxItemSet CreateInsertFrameItemSet(SwFlyFrameAttrMgr& rMgr); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/titlepage.hxx b/sw/source/uibase/inc/titlepage.hxx new file mode 100644 index 000000000..2679c0017 --- /dev/null +++ b/sw/source/uibase/inc/titlepage.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_TITLEPAGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TITLEPAGE_HXX + +#include <sfx2/basedlgs.hxx> + +#include <fmtpdsc.hxx> + +namespace vcl { class Window; } +class SwWrtShell; +class SwPageDesc; + +class SwTitlePageDlg : public SfxDialogController +{ +private: + SwWrtShell *mpSh; + + std::unique_ptr<const SwFormatPageDesc> mpPageFormatDesc; + + SwPageDesc *mpTitleDesc; + const SwPageDesc *mpIndexDesc; + const SwPageDesc *mpNormalDesc; + + std::unique_ptr<weld::RadioButton> m_xUseExistingPagesRB; + std::unique_ptr<weld::SpinButton> m_xPageCountNF; + std::unique_ptr<weld::RadioButton> m_xDocumentStartRB; + std::unique_ptr<weld::RadioButton> m_xPageStartRB; + std::unique_ptr<weld::SpinButton> m_xPageStartNF; + std::unique_ptr<weld::CheckButton> m_xRestartNumberingCB; + std::unique_ptr<weld::SpinButton> m_xRestartNumberingNF; + std::unique_ptr<weld::CheckButton> m_xSetPageNumberCB; + std::unique_ptr<weld::SpinButton> m_xSetPageNumberNF; + std::unique_ptr<weld::ComboBox> m_xPagePropertiesLB; + std::unique_ptr<weld::Button> m_xPagePropertiesPB; + std::unique_ptr<weld::Button> m_xOkPB; + + void FillList(); + + sal_uInt16 GetInsertPosition() const; + + DECL_LINK(OKHdl, weld::Button&, void); + DECL_LINK(EditHdl, weld::Button&, void); + DECL_LINK(RestartNumberingHdl, weld::ToggleButton&, void); + DECL_LINK(SetPageNumberHdl, weld::ToggleButton&, void); + DECL_LINK(ValueChangeHdl, weld::SpinButton&, void); + DECL_LINK(StartPageHdl, weld::ToggleButton&, void); +public: + SwTitlePageDlg(weld::Window *pParent); + virtual ~SwTitlePageDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tmpdlg.hxx b/sw/source/uibase/inc/tmpdlg.hxx new file mode 100644 index 000000000..95682aaa0 --- /dev/null +++ b/sw/source/uibase/inc/tmpdlg.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_UIBASE_INC_TMPDLG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TMPDLG_HXX + +#include <sfx2/styledlg.hxx> +#include <svl/style.hxx> + +class SfxItemSet; +class SwWrtShell; + +// the tab dialog carrier of TabPages +class SwTemplateDlgController : public SfxStyleDialogController +{ + + SfxStyleFamily nType; + sal_uInt16 nHtmlMode; + SwWrtShell* pWrtShell; + bool bNewStyle; + +public: + /// @param sPage + /// Identifies name of page to open at by default + SwTemplateDlgController(weld::Window* pParent, + SfxStyleSheetBase& rBase, + SfxStyleFamily nRegion, + const OString& sPage, + SwWrtShell* pActShell, + bool bNew); + + virtual void RefreshInputSet() override; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + virtual short Ok() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/tmplctrl.hxx b/sw/source/uibase/inc/tmplctrl.hxx new file mode 100644 index 000000000..80fa0fa61 --- /dev/null +++ b/sw/source/uibase/inc/tmplctrl.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_UIBASE_INC_TMPLCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TMPLCTRL_HXX + +#include <sfx2/stbitem.hxx> + +class SwTemplateControl : public SfxStatusBarControl +{ +protected: + virtual void Command( const CommandEvent& rCEvt ) override; +public: + virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + + SFX_DECL_STATUSBAR_CONTROL(); + + SwTemplateControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SwTemplateControl() override; + +private: + OUString sTemplate; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/toxmgr.hxx b/sw/source/uibase/inc/toxmgr.hxx new file mode 100644 index 000000000..057181f60 --- /dev/null +++ b/sw/source/uibase/inc/toxmgr.hxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_TOXMGR_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_TOXMGR_HXX + +#include <swdllapi.h> +#include <tox.hxx> +#include <authfld.hxx> +#include <memory> +#include <optional> + +class SwWrtShell; +class SwForm; + +// manager for directory functionality +//one single method will be sufficient to insert AND update indexes +class SW_DLLPUBLIC SwTOXDescription +{ + TOXTypes m_eTOXType; + OUString m_aStyleNames[MAXLEVEL]; + OUString m_sSequenceName; + OUString m_sMainEntryCharStyle; + OUString m_sAutoMarkURL; + std::optional<OUString> + m_aTitle; + std::optional<OUString> + m_aTOUName; + std::unique_ptr<SwForm> + m_pForm; + SwTOXElement m_nContent; + SwTOIOptions m_nIndexOptions; + SwTOOElements m_nOLEOptions; + LanguageType m_eLanguage; + OUString m_sSortAlgorithm; + + OUString m_sAuthBrackets; + SwCaptionDisplay m_eCaptionDisplay; + SwTOXSortKey m_eSortKey1; + SwTOXSortKey m_eSortKey2; + SwTOXSortKey m_eSortKey3; + sal_uInt8 m_nLevel; + bool m_bFromObjectNames : 1; + bool m_bFromChapter : 1; + bool m_bReadonly: 1; + bool m_bLevelFromChapter : 1; + bool m_bIsAuthSequence :1; + bool m_bSortByDocument :1; + + //TODO: TemplateNames + //const String* pTemplateName = 0, ??? + + SwTOXDescription(SwTOXDescription const &) = delete; + SwTOXDescription & operator= (SwTOXDescription const &) = delete; + +public: + // single argument ctors shall be explicit. + explicit SwTOXDescription(TOXTypes eType) : + m_eTOXType(eType), + m_nContent(SwTOXElement::Mark | SwTOXElement::OutlineLevel), + m_nIndexOptions(SwTOIOptions::SameEntry|SwTOIOptions::FF|SwTOIOptions::CaseSensitive), + m_nOLEOptions(SwTOOElements::NONE), + m_eLanguage(::GetAppLanguage()), + m_eCaptionDisplay(CAPTION_COMPLETE), + m_nLevel(MAXLEVEL), + m_bFromObjectNames(false), + m_bFromChapter(false), + m_bReadonly(true), + m_bLevelFromChapter(false), + m_bIsAuthSequence(false), + m_bSortByDocument(true) + {} + + TOXTypes GetTOXType() const { return m_eTOXType;} + + const OUString& GetStyleNames(sal_uInt16 nLvl) const + {return m_aStyleNames[nLvl];} + void SetStyleNames(const OUString& rSet, sal_uInt16 nLvl) + {m_aStyleNames[nLvl] = rSet; } + + const OUString& GetAutoMarkURL() const { return m_sAutoMarkURL;} + void SetAutoMarkURL(const OUString& rSet) {m_sAutoMarkURL = rSet;} + + void SetTitle(const OUString& rSet) { m_aTitle = rSet; } + std::optional<OUString> const & GetTitle() const {return m_aTitle; } + + void SetTOUName(const OUString& rSet) { m_aTOUName = rSet; } + std::optional<OUString> const & GetTOUName() const { return m_aTOUName; } + + void SetForm(const SwForm& rSet) { m_pForm.reset( new SwForm(rSet) );} + const SwForm* GetForm() const {return m_pForm.get();} + + void SetContentOptions(SwTOXElement nSet) { m_nContent = nSet;} + SwTOXElement GetContentOptions() const { return m_nContent;} + + void SetIndexOptions(SwTOIOptions nSet) { m_nIndexOptions = nSet;} + SwTOIOptions GetIndexOptions() const { return m_nIndexOptions;} + + const OUString& GetMainEntryCharStyle() const {return m_sMainEntryCharStyle;} + void SetMainEntryCharStyle(const OUString& rSet) {m_sMainEntryCharStyle = rSet;} + + void SetLevel(sal_uInt8 nSet) {m_nLevel = nSet;} + sal_uInt8 GetLevel()const {return m_nLevel; } + + void SetCreateFromObjectNames(bool bSet) { m_bFromObjectNames = bSet;} + bool IsCreateFromObjectNames() const {return m_bFromObjectNames;} + + const OUString& GetSequenceName() const {return m_sSequenceName;} + void SetSequenceName(const OUString& rSet) {m_sSequenceName = rSet;} + + SwCaptionDisplay GetCaptionDisplay() const { return m_eCaptionDisplay;} + void SetCaptionDisplay(SwCaptionDisplay eSet) {m_eCaptionDisplay = eSet;} + + void SetFromChapter(bool bSet) { m_bFromChapter = bSet;} + bool IsFromChapter() const {return m_bFromChapter;} + + void SetReadonly(bool bSet){m_bReadonly = bSet;} + bool IsReadonly() const {return m_bReadonly;} + + SwTOOElements GetOLEOptions() const {return m_nOLEOptions;} + void SetOLEOptions(SwTOOElements nOpt) {m_nOLEOptions = nOpt;} + + bool IsLevelFromChapter() const {return m_bLevelFromChapter;} + void SetLevelFromChapter(bool bSet) {m_bLevelFromChapter = bSet;} + + const OUString& GetAuthBrackets() const {return m_sAuthBrackets;} + void SetAuthBrackets(const OUString& rSet) {m_sAuthBrackets = rSet;} + + bool IsAuthSequence() const {return m_bIsAuthSequence;} + void SetAuthSequence(bool bSet){m_bIsAuthSequence = bSet;} + + bool IsSortByDocument()const {return m_bSortByDocument ;} + void SetSortByDocument(bool bSet) {m_bSortByDocument = bSet;} + + void SetSortKeys(SwTOXSortKey eKey1, + SwTOXSortKey eKey2, + SwTOXSortKey eKey3); + + const SwTOXSortKey& GetSortKey1() const {return m_eSortKey1;} + const SwTOXSortKey& GetSortKey2() const {return m_eSortKey2;} + const SwTOXSortKey& GetSortKey3() const {return m_eSortKey3;} + + LanguageType GetLanguage() const {return m_eLanguage;} + void SetLanguage(LanguageType nLang) {m_eLanguage = nLang;} + + const OUString& GetSortAlgorithm()const {return m_sSortAlgorithm;} + void SetSortAlgorithm(const OUString& rSet) {m_sSortAlgorithm = rSet;} + + void ApplyTo(SwTOXBase& rTOXBase); + +}; + +class SwTOXMarkDescription +{ + TOXTypes meTOXType; + int mnLevel; + bool mbMainEntry; + + std::optional<OUString> maPrimKey; + std::optional<OUString> maSecKey; + std::optional<OUString> maAltStr; + std::optional<OUString> maTOUName; + + std::optional<OUString> maPhoneticReadingOfAltStr; + std::optional<OUString> maPhoneticReadingOfPrimKey; + std::optional<OUString> maPhoneticReadingOfSecKey; + + SwTOXMarkDescription(SwTOXMarkDescription const &) = delete; + SwTOXMarkDescription & operator= (SwTOXMarkDescription const &) = delete; + +public: + // single argument ctors shall be explicit. + explicit SwTOXMarkDescription(TOXTypes eType) : + meTOXType(eType), + mnLevel(0), + mbMainEntry(false) + { + } + + TOXTypes GetTOXType()const {return meTOXType;} + + void SetLevel(int nSet) {mnLevel = nSet;} + int GetLevel() const {return mnLevel;} + + void SetMainEntry(bool bSet) {mbMainEntry = bSet;} + bool IsMainEntry() const {return mbMainEntry;} + + void SetPrimKey(const OUString& rSet) { maPrimKey = rSet; } + std::optional<OUString> const & GetPrimKey() const { return maPrimKey; } + + void SetSecKey(const OUString& rSet) { maSecKey = rSet; } + std::optional<OUString> const & GetSecKey() const { return maSecKey; } + + void SetAltStr(const OUString& rSet) { maAltStr = rSet; } + std::optional<OUString> const & GetAltStr() const { return maAltStr; } + + void SetTOUName(const OUString& rSet) { maTOUName = rSet; } + std::optional<OUString> const & GetTOUName() const { return maTOUName; } + + void SetPhoneticReadingOfAltStr(const OUString& rSet) { maPhoneticReadingOfAltStr = rSet; } + std::optional<OUString> const & GetPhoneticReadingOfAltStr() const { return maPhoneticReadingOfAltStr; } + + void SetPhoneticReadingOfPrimKey(const OUString& rSet) { maPhoneticReadingOfPrimKey = rSet; } + std::optional<OUString> const & GetPhoneticReadingOfPrimKey() const { return maPhoneticReadingOfPrimKey; } + + void SetPhoneticReadingOfSecKey(const OUString& rSet) { maPhoneticReadingOfSecKey = rSet; } + std::optional<OUString> const & GetPhoneticReadingOfSecKey() const { return maPhoneticReadingOfSecKey; } +}; + +class SW_DLLPUBLIC SwTOXMgr +{ + SwWrtShell* pSh; + SwTOXMark* pCurTOXMark; + SwTOXMarks aCurMarks; + + SAL_DLLPRIVATE sal_uInt16 GetUserTypeID(const OUString& rStr); + +public: + // single argument ctors shall be explicit. + explicit SwTOXMgr(SwWrtShell* pShell); + + // methods for directory marks + + void InsertTOXMark(const SwTOXMarkDescription& rDesc); + + void UpdateTOXMark(const SwTOXMarkDescription& rDesc); + + void DeleteTOXMark(); + void NextTOXMark(bool bSame=false); + void PrevTOXMark(bool bSame=false); + + // get current TOXmarks + sal_uInt16 GetTOXMarkCount() const; + SwTOXMark* GetTOXMark(sal_uInt16 nId); + SwTOXMark* GetCurTOXMark(); + void SetCurTOXMark(sal_uInt16 nId); + + // methods for directories + + bool UpdateOrInsertTOX(const SwTOXDescription& rDesc, SwTOXBase** ppBase, const SfxItemSet* pSet); + + const SwTOXType* GetTOXType(TOXTypes eTyp) const; + SwWrtShell * GetShell() { return pSh; } +}; + +// inlines +inline sal_uInt16 SwTOXMgr::GetTOXMarkCount() const + { return aCurMarks.size(); } + +inline SwTOXMark* SwTOXMgr::GetCurTOXMark() + { return pCurTOXMark; } + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uiborder.hxx b/sw/source/uibase/inc/uiborder.hxx new file mode 100644 index 000000000..f3764ad9f --- /dev/null +++ b/sw/source/uibase/inc/uiborder.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_UIBASE_INC_UIBORDER_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UIBORDER_HXX + +#include <sfx2/basedlgs.hxx> +#include <svx/flagsdef.hxx> + +class SwBorderDlg : public SfxSingleTabDialogController +{ +public: + + // nType may be: + // SW_BORDER_MODE_PARA + // SW_BORDER_MODE_TABLE + // SW_BORDER_MODE_FRAME + + SwBorderDlg(weld::Window* pParent, SfxItemSet& rSet, SwBorderModes nType); + virtual ~SwBorderDlg() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uiitems.hxx b/sw/source/uibase/inc/uiitems.hxx new file mode 100644 index 000000000..629013cf3 --- /dev/null +++ b/sw/source/uibase/inc/uiitems.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_UIITEMS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UIITEMS_HXX + +#include <memory> +#include <svl/poolitem.hxx> +#include <swdllapi.h> +#include <pagedesc.hxx> + +class SwNumRule; +class IntlWrapper; +class SwPaM; + +// container for FootNote +class SW_DLLPUBLIC SwPageFootnoteInfoItem : public SfxPoolItem +{ + SwPageFootnoteInfo aFootnoteInfo; + +public: + + SwPageFootnoteInfoItem(SwPageFootnoteInfo const & rInfo); + virtual ~SwPageFootnoteInfoItem() override; + + SwPageFootnoteInfoItem(SwPageFootnoteInfoItem const &) = default; + SwPageFootnoteInfoItem(SwPageFootnoteInfoItem &&) = default; + SwPageFootnoteInfoItem & operator =(SwPageFootnoteInfoItem const &) = delete; // due to SfxPoolItem + SwPageFootnoteInfoItem & operator =(SwPageFootnoteInfoItem &&) = delete; // due to SfxPoolItem + + virtual SwPageFootnoteInfoItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + virtual bool GetPresentation( SfxItemPresentation ePres, + MapUnit eCoreMetric, + MapUnit ePresMetric, + OUString &rText, + const IntlWrapper& rIntl ) const override; + + virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override; + virtual bool PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) override; + + SwPageFootnoteInfo& GetPageFootnoteInfo() { return aFootnoteInfo; } + const SwPageFootnoteInfo& GetPageFootnoteInfo() const { return aFootnoteInfo; } +}; + +class SW_DLLPUBLIC SwPtrItem : public SfxPoolItem +{ + void* pMisc; + +public: + SwPtrItem( const sal_uInt16 nId, void* pPtr); + + virtual SwPtrItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void* GetValue() const { return pMisc; } +}; + +class SW_DLLPUBLIC SwUINumRuleItem : public SfxPoolItem +{ + std::unique_ptr<SwNumRule> pRule; + +public: + SwUINumRuleItem( const SwNumRule& rRule ); + SwUINumRuleItem( const SwUINumRuleItem& rItem ); + virtual ~SwUINumRuleItem() override; + + virtual SwUINumRuleItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override; + virtual bool PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) override; + + const SwNumRule* GetNumRule() const { return pRule.get(); } + SwNumRule* GetNumRule() { return pRule.get(); } +}; + +class SwPaMItem : public SfxPoolItem +{ + SwPaM* m_pPaM; + +public: + SwPaMItem( const sal_uInt16 nId, SwPaM* pPaM); + + virtual SwPaMItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + SwPaM* GetValue() const { return m_pPaM; } +}; + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_UIITEMS_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uinums.hxx b/sw/source/uibase/inc/uinums.hxx new file mode 100644 index 000000000..7da897aa5 --- /dev/null +++ b/sw/source/uibase/inc/uinums.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_UINUMS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UINUMS_HXX + +#include <numrule.hxx> +#include <swdllapi.h> + +#include <memory> +#include <vector> + +class SfxPoolItem; +class SwWrtShell; +class SvStream; + +namespace sw { class StoredChapterNumberingRules; } + +#define MAX_NUM_RULES 9 + +class SW_DLLPUBLIC SwNumRulesWithName final +{ + OUString maName; + // the NumRule's formats _have_ to be independent of a document + // (They should always be there!) + class SAL_DLLPRIVATE SwNumFormatGlobal + { + friend class SwNumRulesWithName; + SwNumFormat aFormat; + OUString sCharFormatName; + sal_uInt16 nCharPoolId; + std::vector<std::unique_ptr<SfxPoolItem>> m_Items; + + SwNumFormatGlobal& operator=( const SwNumFormatGlobal& ) = delete; + + public: + SwNumFormatGlobal( const SwNumFormat& rFormat ); + SwNumFormatGlobal( const SwNumFormatGlobal& ); + ~SwNumFormatGlobal(); + + SwNumFormat MakeNumFormat(SwWrtShell& rSh) const; + }; + + std::unique_ptr<SwNumFormatGlobal> aFormats[ MAXLEVEL ]; + + friend class sw::StoredChapterNumberingRules; + friend class SwChapterNumRules; + void SetName(const OUString& rSet) {maName = rSet;} + void SetNumFormat(size_t, SwNumFormat const&, OUString const&); + SwNumRulesWithName() = default; + +public: + SwNumRulesWithName(const SwNumRule &, const OUString &); + SwNumRulesWithName( const SwNumRulesWithName & ); + ~SwNumRulesWithName(); + + SwNumRulesWithName &operator=(const SwNumRulesWithName &); + + const OUString& GetName() const { return maName; } + std::unique_ptr<SwNumRule> MakeNumRule(SwWrtShell& rSh) const; + + void GetNumFormat(size_t, SwNumFormat const*&, OUString const*&) const; +}; + +class SW_DLLPUBLIC SwChapterNumRules final +{ +public: + enum { nMaxRules = MAX_NUM_RULES }; // currently 9 defined forms +private: + std::unique_ptr<SwNumRulesWithName> pNumRules[ MAX_NUM_RULES ]; + + void Init(); + void Save(); + +public: + SwChapterNumRules(); + ~SwChapterNumRules(); + + inline const SwNumRulesWithName* GetRules(sal_uInt16 nIdx) const; + void CreateEmptyNumRule(sal_uInt16 nIdx); // for import + void ApplyNumRules( const SwNumRulesWithName &rCopy, + sal_uInt16 nIdx); +}; + +inline const SwNumRulesWithName *SwChapterNumRules::GetRules(sal_uInt16 nIdx) const +{ + assert(nIdx < nMaxRules); + return pNumRules[nIdx].get(); +} + + +namespace sw +{ + +void ExportStoredChapterNumberingRules( + SwChapterNumRules & rRules, SvStream & rStream, OUString const&); +void ImportStoredChapterNumberingRules( + SwChapterNumRules & rRules, SvStream & rStream, OUString const&); + +} // namespace sw + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uiobject.hxx b/sw/source/uibase/inc/uiobject.hxx new file mode 100644 index 000000000..97362b51a --- /dev/null +++ b/sw/source/uibase/inc/uiobject.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/. + */ + +#ifndef SW_SOURCE_UIBASE_INC_UIOBJECT_HXX +#define SW_SOURCE_UIBASE_INC_UIOBJECT_HXX + +#include <memory> +#include <vcl/uitest/uiobject.hxx> + +#include "edtwin.hxx" +#include "navipi.hxx" + +class SwEditWinUIObject : public WindowUIObject +{ +public: + + SwEditWinUIObject(const VclPtr<SwEditWin>& xEditWin); + + virtual StringMap get_state() override; + + virtual void execute(const OUString& rAction, + const StringMap& rParameters) override; + + static std::unique_ptr<UIObject> create(vcl::Window* pWindow); + +protected: + + virtual OUString get_name() const override; + +private: + + VclPtr<SwEditWin> mxEditWin; + +}; + +class SwNavigationPIUIObject : public WindowUIObject +{ + VclPtr<SwNavigationPI> mxSwNavigationPI; + +public: + + SwNavigationPIUIObject(const VclPtr<SwNavigationPI>& xSwNavigationPI); + + virtual StringMap get_state() override; + + virtual void execute(const OUString& rAction, + const StringMap& rParameters) override; + + static std::unique_ptr<UIObject> create(vcl::Window* pWindow); + +protected: + + OUString get_name() const override; +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uitool.hxx b/sw/source/uibase/inc/uitool.hxx new file mode 100644 index 000000000..ac782d806 --- /dev/null +++ b/sw/source/uibase/inc/uitool.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_UITOOL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UITOOL_HXX + +#include <vcl/weld.hxx> +#include <vcl/menu.hxx> +#include <swtypes.hxx> +#include <swdllapi.h> + +class SfxItemSet; +class SwPageDesc; +class SvxTabStopItem; +class SwWrtShell; +class ListBox; +namespace weld { class ComboBox; } +class SwDocShell; +class SwFrameFormat; +class SwTabCols; +class DateTime; +class SfxViewFrame; +class SwEditShell; + +// fill BoxInfo attribute +SW_DLLPUBLIC void PrepareBoxInfo(SfxItemSet& rSet, const SwWrtShell& rSh); + +/** + * Convert character specific attributes to general ones used by tab pages. + * + * @param[in,out] rSet the set in which character attributes are stored +**/ +SW_DLLPUBLIC void ConvertAttrCharToGen(SfxItemSet& rSet); + +/** + * Convert general attributes to the corresponding character attributes. + * This method is used after executed a character dialog. + * + * @param[in,out] rSet the set in which character attributes are stored + * @param[in] rOrigSet original itemset used as input for the dialog +**/ +SW_DLLPUBLIC void ConvertAttrGenToChar(SfxItemSet& rSet, const SfxItemSet& rOrigSet); + + +/** +* Apply character background on the shell. Need to use this to hide the mixed +* character background and character highlighting attribute, which were +* added for MSO compatibility where there are two kind of character background. +* +* @param[in] rBackgroundColor the color to apply on the shell +* @param[in,out] rShell the shell on which we apply the new attribute +**/ +void ApplyCharBackground(const Color& rBackgroundColor, SwWrtShell& rShell); + +// SfxItemSets <-> PageDesc +void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ); +void PageDescToItemSet( const SwPageDesc& rPageDesc, SfxItemSet& rSet); + +// fill tabs with default tabs +SW_DLLPUBLIC void MakeDefTabs(SwTwips nDefDist, SvxTabStopItem& rTabs); + +// erase DefaultTabs from TabStopArray +//void EraseDefTabs(SvxTabStopItem& rTabs); + +// determine space between 1st and 2nd element +SW_DLLPUBLIC SwTwips GetTabDist(const SvxTabStopItem& rTabs); + +// determine whether a Sfx-PageDesc combination exists in the set +// and set this in the set and delete the transport items +// (PageBreak & PageModel) from the set +void SwToSfxPageDescAttr( SfxItemSet& rSet ); +void SfxToSwPageDescAttr( const SwWrtShell& rShell, SfxItemSet& rSet ); + +SW_DLLPUBLIC FieldUnit GetDfltMetric(bool bWeb); +void SetDfltMetric(FieldUnit eMetric, bool bWeb); + +bool HasCharUnit( bool bWeb ); +void SetApplyCharUnit(bool bApplyChar, bool bWeb); + +// fill ListBox with all char style templates, except the standard ones +SW_DLLPUBLIC void FillCharStyleListBox(weld::ComboBox& rToFill, SwDocShell* pDocSh, bool bSorted = false, bool bWithDefault = false); + +//inserts a string sorted into a ListBox, +SW_DLLPUBLIC void InsertStringSorted(const OUString& rId, const OUString& rEntry, weld::ComboBox& rToFill, int nOffset); + +// Get table width and alignment +SwTwips GetTableWidth( SwFrameFormat const * pFormat, SwTabCols const & rCols, sal_uInt16 *pPercent, + SwWrtShell* pSh ); + +OUString GetAppLangDateTimeString( const DateTime& ); + +// search for a command string within the menu structure and execute it +// at the dispatcher if there is one, if executed return true +bool ExecuteMenuCommand( PopupMenu const & rMenu, SfxViewFrame const & rViewFrame, sal_uInt16 nId ); + +#endif // INCLUDED_SW_SOURCE_UIBASE_INC_UITOOL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/uivwimp.hxx b/sw/source/uibase/inc/uivwimp.hxx new file mode 100644 index 000000000..ae9f3d7b3 --- /dev/null +++ b/sw/source/uibase/inc/uivwimp.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_UIVWIMP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UIVWIMP_HXX + +#include <view.hxx> + +#include <sfx2/objsh.hxx> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +class SwXTextView; +class SfxRequest; +class SwTransferable; +class SfxRequest; + +namespace sfx2 { class DocumentInserter; } +namespace com::sun::star { + namespace frame { + class XDispatchProviderInterceptor; + } + namespace lang { + class XUnoTunnel; + } +} + +class SwScannerEventListener : public ::cppu::WeakImplHelper< + css::lang::XEventListener > +{ + SwView* pView; + +public: + + SwScannerEventListener( SwView& rView ) : pView( &rView ) {} + virtual ~SwScannerEventListener() override; + + // XEventListener + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEventObject ) override; + + void ViewDestroyed() { pView = nullptr; } +}; + +// Clipboard EventListener +class SwClipboardChangeListener : public ::cppu::WeakImplHelper< + css::datatransfer::clipboard::XClipboardListener > +{ + SwView* pView; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rEventObject ) override; + + // XClipboardListener + virtual void SAL_CALL changedContents( const css::datatransfer::clipboard::ClipboardEvent& rEventObject ) override; + +public: + SwClipboardChangeListener( SwView& rView ) : pView( &rView ) {} + virtual ~SwClipboardChangeListener() override; + + void ViewDestroyed() { pView = nullptr; } + + void AddRemoveListener( bool bAdd ); +}; + +class SwMailMergeConfigItem; + +class SwView_Impl +{ + css::uno::Reference< css::frame::XDispatchProviderInterceptor > xDisProvInterceptor; + css::uno::Reference< css::view::XSelectionSupplier > mxXTextView; // UNO object + std::vector< css::uno::WeakReference< css::lang::XUnoTunnel > > mxTransferables; + + // temporary document for printing text of selection / multi selection + // in PDF export. + SfxObjectShellLock xTmpSelDocSh; + + SwView* pView; + rtl::Reference<SwScannerEventListener> + mxScanEvtLstnr; + rtl::Reference<SwClipboardChangeListener> + mxClipEvtLstnr; + ShellMode eShellMode; + + std::shared_ptr<SwMailMergeConfigItem> + xConfigItem; + + std::unique_ptr<sfx2::DocumentInserter> m_pDocInserter; + std::unique_ptr<SfxRequest> m_pRequest; + sal_Int16 m_nParam; + + Point m_aEditingPosition; + bool m_bSelectObject; + bool m_bEditingPositionSet; + +public: + /// Redline author that's specific to this view. + OUString m_sRedlineAuthor; + + SwView_Impl(SwView* pShell); + ~SwView_Impl(); + + void SetShellMode(ShellMode eSet); + + css::view::XSelectionSupplier* GetUNOObject(); + SwXTextView* GetUNOObject_Impl(); + void Invalidate(); + + ShellMode GetShellMode() const {return eShellMode;} + + void ExecuteScan(SfxRequest& rReq); + SwScannerEventListener& GetScannerEventListener(); + + void AddClipboardListener(); + + void AddTransferable(SwTransferable& rTransferable); + + void SetMailMergeConfigItem(std::shared_ptr<SwMailMergeConfigItem> const & rItem) + { + xConfigItem = rItem; + } + std::shared_ptr<SwMailMergeConfigItem> const & GetMailMergeConfigItem() const {return xConfigItem;} + + //#i33307# restore editing position + void SetRestorePosition(const Point& rCursorPos, bool bSelectObj) + { + m_aEditingPosition = rCursorPos; + m_bSelectObject = bSelectObj; + m_bEditingPositionSet = true; + } + bool GetRestorePosition(Point& rCursorPos, bool& rbSelectObj) + { + rCursorPos = m_aEditingPosition; + rbSelectObj = m_bSelectObject; + return m_bEditingPositionSet; + } + + void StartDocumentInserter( + const OUString& rFactory, + const Link<sfx2::FileDialogHelper*,void>& rEndDialogHdl, + const sal_uInt16 nSlotId + ); + std::unique_ptr<SfxMedium> CreateMedium(); + void InitRequest( const SfxRequest& rRequest ); + + SfxRequest* GetRequest() const { return m_pRequest.get(); } + sal_Int16 GetParam() const { return m_nParam; } + void SetParam( sal_Int16 nParam ) { m_nParam = nParam; } +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/unoatxt.hxx b/sw/source/uibase/inc/unoatxt.hxx new file mode 100644 index 000000000..dba379b2c --- /dev/null +++ b/sw/source/uibase/inc/unoatxt.hxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_UNOATXT_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNOATXT_HXX + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/text/XAutoTextGroup.hpp> +#include <com/sun/star/text/XAutoTextEntry.hpp> +#include <com/sun/star/text/XAutoTextContainer2.hpp> +#include <com/sun/star/text/XText.hpp> +#include <svl/itemprop.hxx> +#include <svl/lstner.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> +#include <svtools/unoevent.hxx> + +class SwTextBlocks; +class SwGlossaries; +class SwDoc; +class SwDocShell; +class SwXBodyText; + +#ifndef SW_DECL_SWDOCSHELL_DEFINED +#define SW_DECL_SWDOCSHELL_DEFINED +#include <tools/ref.hxx> +typedef tools::SvRef<SwDocShell> SwDocShellRef; +#endif + +class SwXAutoTextContainer : public cppu::WeakImplHelper +< + css::text::XAutoTextContainer2, + css::lang::XServiceInfo +> +{ + SwGlossaries *pGlossaries; + +protected: + virtual ~SwXAutoTextContainer() override; // ref-counted objects are not to be deleted from outside -> protected dtor + +public: + SwXAutoTextContainer(); + + //XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override; + + //XNameAccess + virtual css::uno::Any SAL_CALL getByName(const OUString& Name) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName(const OUString& Name) override; + + //XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + //XAutoTextContainer + virtual css::uno::Reference< css::text::XAutoTextGroup > SAL_CALL insertNewByName(const OUString& aGroupName) override; + virtual void SAL_CALL removeByName(const OUString& aGroupName) 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 SwXAutoTextGroup : public cppu::WeakImplHelper +< + css::text::XAutoTextGroup, + css::beans::XPropertySet, + css::lang::XServiceInfo, + css::container::XIndexAccess, + css::container::XNamed, + css::lang::XUnoTunnel +> +{ + const SfxItemPropertySet* pPropSet; + SwGlossaries* pGlossaries; + OUString sName; + OUString m_sGroupName; // prefix m_ to disambiguate from some local vars in the implementation + +protected: + virtual ~SwXAutoTextGroup() override; // ref-counted objects are not to be deleted from outside -> protected dtor + +public: + SwXAutoTextGroup(const OUString& rName, SwGlossaries* pGloss); + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + //XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + //XAutoTextGroup + virtual css::uno::Sequence< OUString > SAL_CALL getTitles() override; + virtual void SAL_CALL renameByName(const OUString& aElementName, const OUString& aNewElementName, const OUString& aNewElementTitle) override; + virtual css::uno::Reference< css::text::XAutoTextEntry > SAL_CALL insertNewByName(const OUString& aName, const OUString& aTitle, const css::uno::Reference< css::text::XTextRange > & xTextRange) override; + virtual void SAL_CALL removeByName(const OUString& aEntryName) override; + + //XNamed + virtual OUString SAL_CALL getName() override; + virtual void SAL_CALL setName(const OUString& Name_) override; + + //XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override; + + //XNameAccess + virtual css::uno::Any SAL_CALL getByName(const OUString& Name) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName(const OUString& Name) override; + + //XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) 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; + + //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; + + void Invalidate(); +}; + +class SwXAutoTextEntry final + :public SfxListener + ,public cppu::BaseMutex + ,public cppu::WeakComponentImplHelper + < + css::text::XAutoTextEntry, + css::lang::XServiceInfo, + css::lang::XUnoTunnel, + css::text::XText, + css::document::XEventsSupplier + > +{ + SwGlossaries* pGlossaries; + OUString sGroupName; + OUString sEntryName; + SwDocShellRef xDocSh; + rtl::Reference<SwXBodyText> + mxBodyText; + + void EnsureBodyText () + { + if ( !mxBodyText.is() ) + GetBodyText(); + } + void GetBodyText (); + + void SAL_CALL disposing() override; + + /** ensure that the current content (which may only be in-memory so far) is flushed to the auto text group file + + <p>If somebody modifies an auto text via this class, then this is not directly reflected to the respective + glossaries file (on disk), instead we hold a copy of this text (in [p|x]BodyText). On the other hand, + in applyTo, we do not work with this _copy_, but just tell the target for the application to insert + the content which we're responsible for - and this target doesn't know about our copy, but only + about the persistent version.</br> + So we need to ensure that before somebody else does something with our auto text, we flush our + (in-memory) copy to disk.</p> + + */ + void implFlushDocument( bool _bCloseDoc = false ); + + // SfxListener overridables + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + virtual ~SwXAutoTextEntry() override; // ref-counted objects are not to be deleted from outside -> protected dtor + +public: + SwXAutoTextEntry(SwGlossaries* , const OUString& rGroupName, const OUString& rEntryName); + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + //XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + //XText + 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 > & aTextPosition) 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; + 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; + + //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; + + //XAutoTextEntry + virtual void SAL_CALL applyTo(const css::uno::Reference< css::text::XTextRange > & xRange) 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; + + // XEventsSupplier + virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; + + void Invalidate() {pGlossaries = nullptr;} + const SwGlossaries* GetGlossaries() const { return pGlossaries; } + const OUString& GetGroupName() const {return sGroupName;} + const OUString& GetEntryName() const {return sEntryName;} +}; + +/** Implement the XNameAccess for the AutoText events */ +class SwAutoTextEventDescriptor : public SvBaseEventDescriptor +{ + SwXAutoTextEntry& rAutoTextEntry; + + using SvBaseEventDescriptor::replaceByName; + using SvBaseEventDescriptor::getByName; + +public: + SwAutoTextEventDescriptor( SwXAutoTextEntry& rAutoText ); + + virtual ~SwAutoTextEventDescriptor() override; + + virtual OUString SAL_CALL getImplementationName() override; + +protected: + + virtual void replaceByName( + const SvMacroItemId nEvent, /// item ID of event + const SvxMacro& rMacro) /// event (will be copied) + override; + + virtual void getByName( + SvxMacro& rMacro, /// macro to be filled + const SvMacroItemId nEvent ) /// item ID of event + override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/unodispatch.hxx b/sw/source/uibase/inc/unodispatch.hxx new file mode 100644 index 000000000..316bcf015 --- /dev/null +++ b/sw/source/uibase/inc/unodispatch.hxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_UNODISPATCH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNODISPATCH_HXX + +#include <com/sun/star/frame/XDispatchProviderInterception.hpp> +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <cppuhelper/implbase.hxx> +#include <vector> +#include <vcl/svapp.hxx> + +class SwView; + +class SwXDispatchProviderInterceptor : public cppu::WeakImplHelper +< + css::frame::XDispatchProviderInterceptor, + css::lang::XEventListener, + css::lang::XUnoTunnel, + css::frame::XInterceptorInfo +> +{ + class DispatchMutexLock_Impl + { + //::osl::MutexGuard aGuard; #102295# solar mutex has to be used currently + SolarMutexGuard aGuard; + public: + DispatchMutexLock_Impl(); + ~DispatchMutexLock_Impl(); + }; + friend class DispatchMutexLock_Impl; + +// ::osl::Mutex m_aMutex;#102295# solar mutex has to be used currently + + // the component which's dispatches we're intercepting + css::uno::Reference< css::frame::XDispatchProviderInterception> m_xIntercepted; + + // chaining + css::uno::Reference< css::frame::XDispatchProvider> m_xSlaveDispatcher; + css::uno::Reference< css::frame::XDispatchProvider> m_xMasterDispatcher; + + css::uno::Reference< css::frame::XDispatch> m_xDispatch; + + SwView* m_pView; + +public: + SwXDispatchProviderInterceptor(SwView& rView); + virtual ~SwXDispatchProviderInterceptor() override; + + //XDispatchProvider + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) override; + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& aDescripts ) override; + + //XDispatchProviderInterceptor + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL getSlaveDispatchProvider( ) override; + virtual void SAL_CALL setSlaveDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewDispatchProvider ) override; + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL getMasterDispatchProvider( ) override; + virtual void SAL_CALL setMasterDispatchProvider( const css::uno::Reference< css::frame::XDispatchProvider >& xNewSupplier ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + //XUnoTunnel + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // XInterceptorInfo + virtual css::uno::Sequence<OUString> SAL_CALL getInterceptedURLs() override; + + // view destroyed + void Invalidate(); +}; + +struct StatusStruct_Impl +{ + css::uno::Reference< css::frame::XStatusListener> xListener; + css::util::URL aURL; +}; +class SwXDispatch : public cppu::WeakImplHelper +< + css::frame::XDispatch, + css::view::XSelectionChangeListener +> +{ + SwView* m_pView; + std::vector< StatusStruct_Impl > m_aStatusListenerVector; + bool m_bOldEnable; + bool m_bListenerAdded; +public: + SwXDispatch(SwView& rView); + virtual ~SwXDispatch() override; + + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; + virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xControl, const css::util::URL& aURL ) override; + + //XSelectionChangeListener + virtual void SAL_CALL selectionChanged( const css::lang::EventObject& aEvent ) override; + + //XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + static const char* GetDBChangeURL(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/unomailmerge.hxx b/sw/source/uibase/inc/unomailmerge.hxx new file mode 100644 index 000000000..2b35f7cd3 --- /dev/null +++ b/sw/source/uibase/inc/unomailmerge.hxx @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_UNOMAILMERGE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNOMAILMERGE_HXX + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/interfacecontainer.hxx> +#include <comphelper/interfacecontainer2.hxx> + +#include <com/sun/star/task/XJob.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/PropertyChangeEvent.hpp> +#include <com/sun/star/text/XMailMergeBroadcaster.hpp> +#include <com/sun/star/util/XCancellable.hpp> +#include <svl/itemprop.hxx> +#include <sfx2/objsh.hxx> + +namespace com::sun::star { + + namespace sdbc { + class XResultSet; + class XConnection; + } + namespace frame { + class XModel; + } + namespace lang { + class XMultiServiceFactory; + } + namespace text { + class XMailMergeListener; + struct MailMergeEvent; + } + namespace beans{ + struct PropertyValue; + } + +} + +typedef cppu::OMultiTypeInterfaceContainerHelperVar<sal_Int32> + OPropertyListenerContainerHelper; + +class SwDBManager; +class MailMergeExecuteFinalizer; + +class SwXMailMerge : + public cppu::WeakImplHelper + < + css::task::XJob, + css::util::XCancellable, + css::beans::XPropertySet, + css::text::XMailMergeBroadcaster, + css::lang::XComponent, + css::lang::XServiceInfo + > +{ + friend class MailMergeExecuteFinalizer; + + comphelper::OInterfaceContainerHelper2 m_aEvtListeners; + comphelper::OInterfaceContainerHelper2 m_aMergeListeners; + OPropertyListenerContainerHelper m_aPropListeners; + + const SfxItemPropertySet* m_pPropSet; + + SfxObjectShellRef m_xDocSh; // the document + + OUString m_aTmpFileName; + + // properties of mail merge service + css::uno::Sequence< css::uno::Any > m_aSelection; + css::uno::Reference< css::sdbc::XResultSet > m_xResultSet; + css::uno::Reference< css::sdbc::XConnection > m_xConnection; + css::uno::Reference< css::frame::XModel > m_xModel; + OUString m_aDataSourceName; + OUString m_aDataCommand; + OUString m_aFilter; + OUString m_aDocumentURL; + OUString m_aOutputURL; + OUString m_aFileNamePrefix; + sal_Int32 m_nDataCommandType; + sal_Int16 m_nOutputType; + bool m_bEscapeProcessing; + bool m_bSinglePrintJobs; + bool m_bFileNameFromColumn; + + OUString m_sInServerPassword; + OUString m_sOutServerPassword; + OUString m_sSubject; + OUString m_sAddressFromColumn; + OUString m_sMailBody; + OUString m_sAttachmentName; + OUString m_sAttachmentFilter; + css::uno::Sequence< OUString > m_aCopiesTo; + css::uno::Sequence< OUString > m_aBlindCopiesTo; + bool m_bSendAsHTML; + bool m_bSendAsAttachment; + + css::uno::Sequence< css::beans::PropertyValue > m_aPrintSettings; + + bool m_bSaveAsSingleFile; + OUString m_sSaveFilter; + OUString m_sSaveFilterOptions; + css::uno::Sequence< css::beans::PropertyValue > m_aSaveFilterData; + + bool m_bDisposing; + SwDBManager *m_pMgr; + + void launchEvent( const css::beans::PropertyChangeEvent &rEvt ) const; + + SwXMailMerge( const SwXMailMerge & ) = delete; + SwXMailMerge & operator = ( const SwXMailMerge & ) = delete; +protected: + virtual ~SwXMailMerge() override; +public: + SwXMailMerge(); + + void LaunchMailMergeEvent( const css::text::MailMergeEvent &rData ) const; + + // XJob + virtual css::uno::Any SAL_CALL execute( const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override; + + // XCancellable + virtual void SAL_CALL cancel() 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; + + // XMailMergeBroadcaster + virtual void SAL_CALL addMailMergeEventListener( const css::uno::Reference< css::text::XMailMergeListener >& xListener ) override; + virtual void SAL_CALL removeMailMergeEventListener( const css::uno::Reference< css::text::XMailMergeListener >& xListener ) 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 >& aListener ) 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/uibase/inc/unomod.hxx b/sw/source/uibase/inc/unomod.hxx new file mode 100644 index 000000000..1bd195689 --- /dev/null +++ b/sw/source/uibase/inc/unomod.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_UIBASE_INC_UNOMOD_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNOMOD_HXX + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/view/XPrintSettingsSupplier.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/ChainablePropertySet.hxx> +#include <tools/fldunit.hxx> + +class SwView; +class SwViewOption; +class SwPrintData; +class SwDoc; + +css::uno::Reference< css::uno::XInterface > SAL_CALL SwXModule_CreateInstance(const css::uno::Reference< css::lang::XMultiServiceFactory > & ); + +class SwXModule : public cppu::WeakImplHelper +< + css::view::XViewSettingsSupplier, + css::view::XPrintSettingsSupplier, + css::lang::XServiceInfo +> +{ + + css::uno::Reference< css::beans::XPropertySet > mxViewSettings; + css::uno::Reference< css::beans::XPropertySet > mxPrintSettings; + +protected: + virtual ~SwXModule() override; +public: + SwXModule(); + + //XViewSettings + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getViewSettings() override; + + //XPrintSettings + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getPrintSettings() 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; +}; + +enum class SwXPrintSettingsType +{ + Module, + Document +}; + +class SwXPrintSettings final : public comphelper::ChainablePropertySet +{ + friend class SwXDocumentSettings; + + SwXPrintSettingsType meType; + SwPrintData * mpPrtOpt; + SwDoc *mpDoc; + + virtual void _preSetValues () override; + virtual void _setSingleValue( const comphelper::PropertyInfo & rInfo, const css::uno::Any &rValue ) override; + virtual void _postSetValues () override; + + virtual void _preGetValues () override; + + virtual void _getSingleValue( const comphelper::PropertyInfo & rInfo, css::uno::Any & rValue ) override; + virtual void _postGetValues () override; + + virtual ~SwXPrintSettings() + throw() override; +public: + SwXPrintSettings( SwXPrintSettingsType eType, SwDoc * pDoc = nullptr ); + + //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 SwXViewSettings final : public comphelper::ChainablePropertySet +{ + friend class SwXDocumentSettings; + + SwView* pView; + std::unique_ptr<SwViewOption> mpViewOption; + const SwViewOption* mpConstViewOption; + bool bObjectValid:1, mbApplyZoom; + + FieldUnit eHRulerUnit; + bool mbApplyHRulerMetric; + FieldUnit eVRulerUnit; + bool mbApplyVRulerMetric; + + virtual void _preSetValues () override; + virtual void _setSingleValue( const comphelper::PropertyInfo & rInfo, const css::uno::Any &rValue ) override; + virtual void _postSetValues() override; + + virtual void _preGetValues () override; + virtual void _getSingleValue( const comphelper::PropertyInfo & rInfo, css::uno::Any & rValue ) override; + virtual void _postGetValues () override; + + virtual ~SwXViewSettings() + throw() override; +public: + SwXViewSettings(SwView* pView); + + //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; + + bool IsValid() const {return bObjectValid;} + void Invalidate() {bObjectValid = false;} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/unotools.hxx b/sw/source/uibase/inc/unotools.hxx new file mode 100644 index 000000000..4e72a5309 --- /dev/null +++ b/sw/source/uibase/inc/unotools.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_UNOTOOLS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNOTOOLS_HXX + +#include <vcl/idle.hxx> +#include <vcl/weld.hxx> +#include <vcl/customweld.hxx> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/text/XTextCursor.hpp> +#include <swdllapi.h> + +#define EX_SHOW_ONLINE_LAYOUT 0x01 +// hard zoom value +#define EX_SHOW_BUSINESS_CARDS 0x02 +//don't modify page size +#define EX_SHOW_DEFAULT_PAGE 0x04 +//replace sample toc strings in the template to localized versions +#define EX_LOCALIZE_TOC_STRINGS 0x08 + +class SwView; + +class SW_DLLPUBLIC SwOneExampleFrame : public weld::CustomWidgetController +{ + ScopedVclPtr<VirtualDevice> m_xVirDev; + css::uno::Reference< css::frame::XModel > m_xModel; + css::uno::Reference< css::frame::XController > m_xController; + css::uno::Reference< css::text::XTextCursor > m_xCursor; + + Idle m_aLoadedIdle; + Link<SwOneExampleFrame&,void> m_aInitializedLink; + + OUString m_sArgumentURL; + + SwView* m_pModuleView; + + sal_uInt32 m_nStyleFlags; + + bool m_bIsInitialized; + + DECL_DLLPRIVATE_LINK( TimeoutHdl, Timer*, void ); + void PopupHdl(const OString& rId); + + SAL_DLLPRIVATE void CreateControl(); + SAL_DLLPRIVATE void DisposeControl(); + +public: + SwOneExampleFrame(sal_uInt32 nStyleFlags, + const Link<SwOneExampleFrame&,void>* pInitalizedLink, + const OUString* pURL = nullptr); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual bool Command(const CommandEvent& rCEvt) override; + virtual ~SwOneExampleFrame() override; + + css::uno::Reference< css::frame::XModel > & GetModel() {return m_xModel;} + css::uno::Reference< css::text::XTextCursor > & GetTextCursor() {return m_xCursor;} + + void ClearDocument(); + + bool IsInitialized() const {return m_bIsInitialized;} + + bool CreatePopup(const Point& rPt); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/unotxvw.hxx b/sw/source/uibase/inc/unotxvw.hxx new file mode 100644 index 000000000..db2f6eae3 --- /dev/null +++ b/sw/source/uibase/inc/unotxvw.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_UNOTXVW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_UNOTXVW_HXX + +#include <sfx2/sfxbasecontroller.hxx> +#include <comphelper/interfacecontainer2.hxx> +#include <com/sun/star/text/XTextViewCursor.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/text/XRubySelection.hpp> +#include <com/sun/star/view/XFormLayerAccess.hpp> +#include <com/sun/star/view/XScreenCursor.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/view/XLineCursor.hpp> +#include <com/sun/star/view/XViewCursor.hpp> +#include <com/sun/star/text/XPageCursor.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/datatransfer/XTransferableSupplier.hpp> +#include <cppuhelper/implbase.hxx> +#include <svl/itemprop.hxx> +#include <TextCursorHelper.hxx> +#include <comphelper/uno3.hxx> + +#include <sfx2/objsh.hxx> + +class SdrObject; +class SwView; + +class SwXTextView : + public css::view::XSelectionSupplier, + public css::lang::XServiceInfo, + public css::view::XFormLayerAccess, + public css::text::XTextViewCursorSupplier, + public css::text::XRubySelection, + public css::view::XViewSettingsSupplier, + public css::beans::XPropertySet, + public css::datatransfer::XTransferableSupplier, + public SfxBaseController +{ + ::comphelper::OInterfaceContainerHelper2 m_SelChangedListeners; + + SwView* m_pView; + const SfxItemPropertySet* m_pPropSet; // property map for SwXTextView properties + // (not related to mxViewSettings!) + + css::uno::Reference< css::beans::XPropertySet > mxViewSettings; + css::uno::Reference< css::text::XTextViewCursor > mxTextViewCursor; + + SdrObject* GetControl( + const css::uno::Reference< css::awt::XControlModel > & Model, + css::uno::Reference< css::awt::XControl >& xToFill ); + +protected: + virtual ~SwXTextView() override; +public: + SwXTextView(SwView* pSwView); + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) throw() override; + virtual void SAL_CALL release( ) throw() override; + + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + //XSelectionSupplier + virtual css::uno::Any SAL_CALL getSelection() override; + virtual sal_Bool SAL_CALL select(const css::uno::Any& rInterface) override; + virtual void SAL_CALL addSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener > & xListener) override; + virtual void SAL_CALL removeSelectionChangeListener(const css::uno::Reference< css::view::XSelectionChangeListener > & xListener) override; + + // XFormLayerAccess + virtual css::uno::Reference< css::form::runtime::XFormController > SAL_CALL getFormController( const css::uno::Reference< css::form::XForm >& Form ) override; + virtual sal_Bool SAL_CALL isFormDesignMode( ) override; + virtual void SAL_CALL setFormDesignMode( sal_Bool DesignMode ) override; + + // XControlAccess + virtual css::uno::Reference< css::awt::XControl > SAL_CALL getControl(const css::uno::Reference< css::awt::XControlModel > & Model) override; + + //XTextViewCursorSupplier + virtual css::uno::Reference< css::text::XTextViewCursor > SAL_CALL getViewCursor() override; + + //XViewSettings + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getViewSettings() override; + + //XRubySelection + virtual css::uno::Sequence< + css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getRubyList( sal_Bool bAutomatic ) override; + + virtual void SAL_CALL setRubyList( + const css::uno::Sequence< + css::uno::Sequence< css::beans::PropertyValue > >& RubyList, sal_Bool bAutomatic ) 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; + + //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; + + //XTransferableSupplier + virtual css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL getTransferable( ) override; + virtual void SAL_CALL insertTransferable( const css::uno::Reference< css::datatransfer::XTransferable >& xTrans ) override; + + void NotifySelChanged(); + void NotifyDBChanged(); + + SwView* GetView() {return m_pView;} + void Invalidate(); + + // temporary document used for PDF export of selections/multi-selections + SfxObjectShellLock BuildTmpSelectionDoc(); +}; + +typedef cppu::WeakImplHelper< + css::text::XTextViewCursor, + css::lang::XServiceInfo, + css::text::XPageCursor, + css::view::XScreenCursor, + css::view::XViewCursor, + css::view::XLineCursor, + css::beans::XPropertySet, + css::beans::XPropertyState + > SwXTextViewCursor_Base; + +class SwXTextViewCursor final: public SwXTextViewCursor_Base, public OTextCursorHelper +{ + SwView* m_pView; + const SfxItemPropertySet* m_pPropSet; + bool IsTextSelection( bool bAllowTables = true ) const; + virtual ~SwXTextViewCursor() override; + +public: + SwXTextViewCursor(SwView* pVw); + + DECLARE_XINTERFACE() + + //XTextViewCursor + virtual sal_Bool SAL_CALL isVisible() override; + virtual void SAL_CALL setVisible(sal_Bool bVisible) override; + virtual css::awt::Point SAL_CALL getPosition() override; + + //XTextCursor - new + virtual void SAL_CALL collapseToStart() override; + virtual void SAL_CALL collapseToEnd() override; + virtual sal_Bool SAL_CALL isCollapsed() override; + virtual sal_Bool SAL_CALL goLeft( sal_Int16 nCount, sal_Bool bExpand ) override; + virtual sal_Bool SAL_CALL goRight( sal_Int16 nCount, sal_Bool bExpand ) override; + virtual void SAL_CALL gotoStart( sal_Bool bExpand ) override; + virtual void SAL_CALL gotoEnd( sal_Bool bExpand ) override; + virtual void SAL_CALL gotoRange( const css::uno::Reference< css::text::XTextRange >& xRange, sal_Bool bExpand ) override; + + //XPageCursor + virtual sal_Bool SAL_CALL jumpToFirstPage() override; + virtual sal_Bool SAL_CALL jumpToLastPage() override; + virtual sal_Bool SAL_CALL jumpToPage(sal_Int16 nPage) override; + virtual sal_Bool SAL_CALL jumpToNextPage() override; + virtual sal_Bool SAL_CALL jumpToPreviousPage() override; + virtual sal_Bool SAL_CALL jumpToEndOfPage() override; + virtual sal_Bool SAL_CALL jumpToStartOfPage() override; + virtual sal_Int16 SAL_CALL getPage() 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& aString) override; + + //XScreenCursor + virtual sal_Bool SAL_CALL screenDown() override; + virtual sal_Bool SAL_CALL screenUp() override; + + //XViewCursor + virtual sal_Bool SAL_CALL goDown(sal_Int16 nCount, sal_Bool bExpand) override; + virtual sal_Bool SAL_CALL goUp(sal_Int16 nCount, sal_Bool bExpand) override; + + //XLineCursor + virtual sal_Bool SAL_CALL isAtStartOfLine() override; + virtual sal_Bool SAL_CALL isAtEndOfLine() override; + virtual void SAL_CALL gotoEndOfLine(sal_Bool bExpand) override; + virtual void SAL_CALL gotoStartOfLine(sal_Bool bExpand) 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; + + //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; + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId(); + + //XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + void Invalidate(){m_pView = nullptr;} + + // ITextCursorHelper + virtual const SwPaM* GetPaM() const override; + virtual SwPaM* GetPaM() override; + virtual const SwDoc* GetDoc() const override; + virtual SwDoc* GetDoc() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/usrpref.hxx b/sw/source/uibase/inc/usrpref.hxx new file mode 100644 index 000000000..163654686 --- /dev/null +++ b/sw/source/uibase/inc/usrpref.hxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_USRPREF_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_USRPREF_HXX + +#include <memory> +#include <unotools/configitem.hxx> +#include <fldupde.hxx> +#include <viewopt.hxx> +#include <tools/fldunit.hxx> + +class SwMasterUsrPref; + +class SwContentViewConfig : public utl::ConfigItem +{ +private: + SwMasterUsrPref& rParent; + bool bWeb; + + css::uno::Sequence<OUString> GetPropertyNames() const; + + virtual void ImplCommit() override; + +public: + SwContentViewConfig(bool bWeb, SwMasterUsrPref& rParent); + virtual ~SwContentViewConfig() override; + + // utl::ConfigItem + virtual void Notify( const css::uno::Sequence< OUString > &rPropertyNames ) override; + + void Load(); + using ConfigItem::SetModified; +}; + +class SwLayoutViewConfig : public utl::ConfigItem +{ +private: + SwMasterUsrPref& rParent; + bool bWeb; + + css::uno::Sequence<OUString> GetPropertyNames() const; + + virtual void ImplCommit() override; + +public: + SwLayoutViewConfig(bool bWeb, SwMasterUsrPref& rParent); + virtual ~SwLayoutViewConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + void Load(); + using ConfigItem::SetModified; +}; + +class SwGridConfig : public utl::ConfigItem +{ +private: + SwMasterUsrPref& rParent; + + static css::uno::Sequence<OUString> GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwGridConfig(bool bWeb, SwMasterUsrPref& rParent); + virtual ~SwGridConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + void Load(); + using ConfigItem::SetModified; +}; + +class SwCursorConfig : public utl::ConfigItem +{ +private: + SwMasterUsrPref& rParent; + + static css::uno::Sequence<OUString> GetPropertyNames(); + + virtual void ImplCommit() override; + +public: + SwCursorConfig(SwMasterUsrPref& rParent); + virtual ~SwCursorConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + void Load(); + using ConfigItem::SetModified; +}; + +class SwWebColorConfig : public utl::ConfigItem +{ +private: + SwMasterUsrPref& rParent; + css::uno::Sequence<OUString> aPropNames; + + virtual void ImplCommit() override; + +public: + SwWebColorConfig(SwMasterUsrPref& rParent); + virtual ~SwWebColorConfig() override; + + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + void Load(); + using ConfigItem::SetModified; +}; + +class SwMasterUsrPref : public SwViewOption +{ + friend class SwContentViewConfig; + friend class SwLayoutViewConfig; + friend class SwGridConfig; + friend class SwCursorConfig; + friend class SwWebColorConfig; + + SwFieldUpdateFlags m_eFieldUpdateFlags; //update of fields and charts + sal_Int32 m_nLinkUpdateMode; + FieldUnit m_eUserMetric; + FieldUnit m_eHScrollMetric; + bool m_bIsHScrollMetricSet; + FieldUnit m_eVScrollMetric; + bool m_bIsVScrollMetricSet; + + sal_Int32 m_nDefTabInMm100; //default tab stop distance, in 1/100 mm + + bool m_bIsSquaredPageMode; //default page mode for text grid + bool m_bIsAlignMathObjectsToBaseline; + + SwContentViewConfig m_aContentConfig; + SwLayoutViewConfig m_aLayoutConfig; + SwGridConfig m_aGridConfig; + SwCursorConfig m_aCursorConfig; + std::unique_ptr<SwWebColorConfig> m_pWebColorConfig; + + bool m_bApplyCharUnit; // apply_char_unit +public: + SwMasterUsrPref(bool bWeb); + ~SwMasterUsrPref(); + + void SetUsrPref(const SwViewOption &rCopy); + + void SetModified() + { + m_aContentConfig.SetModified(); + m_aLayoutConfig.SetModified(); + m_aGridConfig.SetModified(); + m_aCursorConfig.SetModified(); + if(m_pWebColorConfig) + m_pWebColorConfig->SetModified(); + } + + void SetUpdateLinkMode(sal_Int32 nSet, bool bNoModify = false) + { + m_nLinkUpdateMode = nSet; + if(!bNoModify) + m_aContentConfig.SetModified(); + } + sal_Int32 GetUpdateLinkMode() const {return m_nLinkUpdateMode; } + + void SetUpdateFields(bool bSet) + { + if(bSet && m_eFieldUpdateFlags == AUTOUPD_OFF) + { + m_eFieldUpdateFlags = AUTOUPD_FIELD_ONLY; + } + else if(!bSet) + { + m_eFieldUpdateFlags = AUTOUPD_OFF; + } + }; + bool IsUpdateFields()const {return m_eFieldUpdateFlags != AUTOUPD_OFF; } + + SwFieldUpdateFlags GetFieldUpdateFlags()const {return m_eFieldUpdateFlags;} + void SetFieldUpdateFlags(SwFieldUpdateFlags eSet) + { + m_eFieldUpdateFlags = eSet; + m_aContentConfig.SetModified(); + } + + void SetUpdateCharts(bool bSet) + { + if(bSet) + { + m_eFieldUpdateFlags = AUTOUPD_FIELD_AND_CHARTS; + } + else if(m_eFieldUpdateFlags == AUTOUPD_FIELD_AND_CHARTS) + { + m_eFieldUpdateFlags = AUTOUPD_FIELD_ONLY; + } + }; + bool IsUpdateCharts()const {return m_eFieldUpdateFlags == AUTOUPD_FIELD_AND_CHARTS; } + + FieldUnit GetMetric() const { return m_eUserMetric;} + void SetMetric(FieldUnit eSet, bool bNoModify = false) + { + m_eUserMetric = eSet; + if(!bNoModify) + m_aLayoutConfig.SetModified(); + } + + bool IsHScrollMetric()const {return m_bIsHScrollMetricSet;} + FieldUnit GetHScrollMetric() const { return m_bIsHScrollMetricSet ? m_eHScrollMetric : m_eUserMetric;} + void SetHScrollMetric(FieldUnit eSet) + { + m_eHScrollMetric = eSet; m_bIsHScrollMetricSet = true; + m_aLayoutConfig.SetModified(); + } + + bool IsVScrollMetric()const {return m_bIsVScrollMetricSet;} + FieldUnit GetVScrollMetric() const { return m_bIsVScrollMetricSet ? m_eVScrollMetric : m_eUserMetric;} + void SetVScrollMetric(FieldUnit eSet) + { + m_eVScrollMetric = eSet; m_bIsVScrollMetricSet = true; + m_aLayoutConfig.SetModified(); + } + + bool IsApplyCharUnit() const + { + return m_bApplyCharUnit; + } + void SetApplyCharUnit(bool bSet, bool noModify = false) + { + m_bApplyCharUnit = bSet; + if (!noModify) { + m_aLayoutConfig.SetModified(); + } + } + + sal_Int32 GetDefTabInMm100() const { return m_nDefTabInMm100;} + void SetDefTabInMm100( sal_Int32 nSet, bool bNoModify = false ) + { + m_nDefTabInMm100 = nSet; + if(!bNoModify) + m_aLayoutConfig.SetModified(); + } + + //default page mode for text grid + bool IsSquaredPageMode() const {return m_bIsSquaredPageMode;} + void SetDefaultPageMode( bool bVal, bool bNoModify = false ) + { + m_bIsSquaredPageMode = bVal; + if(!bNoModify) + m_aLayoutConfig.SetModified(); + } + + bool IsAlignMathObjectsToBaseline() const { return m_bIsAlignMathObjectsToBaseline; } + void SetAlignMathObjectsToBaseline( bool bVal, bool noModify = false ) + { + m_bIsAlignMathObjectsToBaseline = bVal; + if (!noModify) { + m_aLayoutConfig.SetModified(); + } + } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/viewlayoutctrl.hxx b/sw/source/uibase/inc/viewlayoutctrl.hxx new file mode 100644 index 000000000..b531ef4f2 --- /dev/null +++ b/sw/source/uibase/inc/viewlayoutctrl.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_UIBASE_INC_VIEWLAYOUTCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_VIEWLAYOUTCTRL_HXX + +#include <sfx2/stbitem.hxx> +#include <memory> + +class SwViewLayoutControl : public SfxStatusBarControl +{ +private: + + struct SwViewLayoutControl_Impl; + std::unique_ptr<SwViewLayoutControl_Impl> mpImpl; + +public: + + SFX_DECL_STATUSBAR_CONTROL(); + + SwViewLayoutControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SwViewLayoutControl() override; + + virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + virtual bool MouseButtonDown( const MouseEvent & ) override; + virtual bool MouseMove( const MouseEvent & ) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/watermarkdialog.hxx b/sw/source/uibase/inc/watermarkdialog.hxx new file mode 100644 index 000000000..dff87a792 --- /dev/null +++ b/sw/source/uibase/inc/watermarkdialog.hxx @@ -0,0 +1,40 @@ +/* -*- 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_UIBASE_INC_WATERMARKDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WATERMARKDIALOG_HXX + +#include <sfx2/bindings.hxx> +#include <vcl/weld.hxx> +#include <svx/colorbox.hxx> +#include <sfx2/basedlgs.hxx> + +class SwWatermarkDialog : public SfxDialogController +{ +public: + SwWatermarkDialog(weld::Window* pParent, SfxBindings& rBindings); + virtual ~SwWatermarkDialog() override; + + void InitFields(); + +private: + DECL_LINK(OKButtonHdl, weld::Button&, void); + + SfxBindings& m_rBindings; + + std::unique_ptr<weld::Entry> m_xTextInput; + std::unique_ptr<weld::Button> m_xOKButton; + std::unique_ptr<weld::ComboBox> m_xFont; + std::unique_ptr<weld::MetricSpinButton> m_xAngle; + std::unique_ptr<weld::MetricSpinButton> m_xTransparency; + std::unique_ptr<ColorListBox> m_xColor; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/uibase/inc/wformsh.hxx b/sw/source/uibase/inc/wformsh.hxx new file mode 100644 index 000000000..dfce5359f --- /dev/null +++ b/sw/source/uibase/inc/wformsh.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_WFORMSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WFORMSH_HXX + +#include "drformsh.hxx" + +class SwWebDrawFormShell : public SwDrawFormShell +{ +public: + SwWebDrawFormShell(SwView &rShell); + virtual ~SwWebDrawFormShell() override; + + SFX_DECL_INTERFACE(SW_WEBDRAWFORMSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wfrmsh.hxx b/sw/source/uibase/inc/wfrmsh.hxx new file mode 100644 index 000000000..15cd8a8b7 --- /dev/null +++ b/sw/source/uibase/inc/wfrmsh.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_WFRMSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WFRMSH_HXX + +#include "frmsh.hxx" + +class SwWebFrameShell: public SwFrameShell +{ +public: + SFX_DECL_INTERFACE(SW_WEBFRAMESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebFrameShell(SwView &rView); + virtual ~SwWebFrameShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wgrfsh.hxx b/sw/source/uibase/inc/wgrfsh.hxx new file mode 100644 index 000000000..65987a423 --- /dev/null +++ b/sw/source/uibase/inc/wgrfsh.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_WGRFSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WGRFSH_HXX + +#include "grfsh.hxx" + +class SwWebGrfShell: public SwGrfShell +{ +public: + SwWebGrfShell(SwView &rShell); + virtual ~SwWebGrfShell() override; + + SFX_DECL_INTERFACE(SW_WEBGRFSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wlistsh.hxx b/sw/source/uibase/inc/wlistsh.hxx new file mode 100644 index 000000000..f3a1179a6 --- /dev/null +++ b/sw/source/uibase/inc/wlistsh.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_WLISTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WLISTSH_HXX + +#include "listsh.hxx" + +class SwWebListShell: public SwListShell +{ +public: + SFX_DECL_INTERFACE(SW_WEBLISTSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + + SwWebListShell(SwView &rView); + virtual ~SwWebListShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wolesh.hxx b/sw/source/uibase/inc/wolesh.hxx new file mode 100644 index 000000000..017768aac --- /dev/null +++ b/sw/source/uibase/inc/wolesh.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_UIBASE_INC_WOLESH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WOLESH_HXX + +#include "olesh.hxx" + +class SwWebOleShell: public SwOleShell +{ +public: + SFX_DECL_INTERFACE(SW_WEBOLESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebOleShell(SwView &rView); + virtual ~SwWebOleShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wordcountctrl.hxx b/sw/source/uibase/inc/wordcountctrl.hxx new file mode 100644 index 000000000..7b6f86204 --- /dev/null +++ b/sw/source/uibase/inc/wordcountctrl.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/. + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_WORDCOUNTCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WORDCOUNTCTRL_HXX + +#include <sfx2/stbitem.hxx> + +/** +Word count status bar control for Writer. + +@remarks This is a simple status bar control of type SfxStringItem, and it has no custom +logic whatsoever. The actual updating of the word count string happens in +SwView::StateStatusLine (see sw/source/ui/uiview/view2.cxx). +*/ +class SwWordCountStatusBarControl : public SfxStatusBarControl +{ +public: + SFX_DECL_STATUSBAR_CONTROL(); + + SwWordCountStatusBarControl(sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb); + virtual ~SwWordCountStatusBarControl() override; + + virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wordcountdialog.hxx b/sw/source/uibase/inc/wordcountdialog.hxx new file mode 100644 index 000000000..dafc87ea0 --- /dev/null +++ b/sw/source/uibase/inc/wordcountdialog.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_UIBASE_INC_WORDCOUNTDIALOG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WORDCOUNTDIALOG_HXX +#include <sfx2/basedlgs.hxx> +struct SwDocStat; +#include <sfx2/childwin.hxx> +#include <swabstdlg.hxx> + +class SwWordCountFloatDlg : public SfxModelessDialogController +{ + void SetValues(const SwDocStat& rCurrent, const SwDocStat& rDoc); + void showCJK(bool bShowCJK); + void showStandardizedPages(bool bShowStandardizedPages); + + std::unique_ptr<weld::Label> m_xCurrentWordFT; + std::unique_ptr<weld::Label> m_xCurrentCharacterFT; + std::unique_ptr<weld::Label> m_xCurrentCharacterExcludingSpacesFT; + std::unique_ptr<weld::Label> m_xCurrentCjkcharsFT; + std::unique_ptr<weld::Label> m_xCurrentStandardizedPagesFT; + std::unique_ptr<weld::Label> m_xDocWordFT; + std::unique_ptr<weld::Label> m_xDocCharacterFT; + std::unique_ptr<weld::Label> m_xDocCharacterExcludingSpacesFT; + std::unique_ptr<weld::Label> m_xDocCjkcharsFT; + std::unique_ptr<weld::Label> m_xDocStandardizedPagesFT; + std::unique_ptr<weld::Label> m_xCjkcharsLabelFT; + std::unique_ptr<weld::Label> m_xCjkcharsLabelFT2; + std::unique_ptr<weld::Label> m_xStandardizedPagesLabelFT; + std::unique_ptr<weld::Label> m_xStandardizedPagesLabelFT2; + +public: + SwWordCountFloatDlg(SfxBindings* pBindings, + SfxChildWindow* pChild, + weld::Window *pParent, + SfxChildWinInfo const * pInfo); + virtual ~SwWordCountFloatDlg() override; + void UpdateCounts(); + + void SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat); +}; + +class SwWordCountWrapper final : public SfxChildWindow +{ + VclPtr<AbstractSwWordCountFloatDlg> xAbstDlg; + +public: + SwWordCountWrapper( vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ); + SFX_DECL_CHILDWINDOW_WITHID(SwWordCountWrapper); + + virtual ~SwWordCountWrapper() override; + void UpdateCounts(); + void SetCounts(const SwDocStat &rCurrCnt, const SwDocStat &rDocStat); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/workctrl.hxx b/sw/source/uibase/inc/workctrl.hxx new file mode 100644 index 000000000..20949f728 --- /dev/null +++ b/sw/source/uibase/inc/workctrl.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_UIBASE_INC_WORKCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WORKCTRL_HXX + +#include <sfx2/tbxctrl.hxx> +#include <vcl/toolbox.hxx> + +class PopupMenu; +class SwView; + +// double entry! hrc and hxx +// these Ids say what the buttons below the scrollbar are doing +#define NID_START 20000 +#define NID_TBL 20000 +#define NID_FRM 20001 +#define NID_PGE 20002 +#define NID_DRW 20003 +#define NID_CTRL 20004 +#define NID_REG 20005 +#define NID_BKM 20006 +#define NID_GRF 20007 +#define NID_OLE 20008 +#define NID_OUTL 20009 +#define NID_SEL 20010 +#define NID_FTN 20011 +#define NID_MARK 20012 +#define NID_POSTIT 20013 +#define NID_SRCH_REP 20014 +#define NID_INDEX_ENTRY 20015 +#define NID_TABLE_FORMULA 20016 +#define NID_TABLE_FORMULA_ERROR 20017 +#define NID_COUNT 18 + +class SwTbxAutoTextCtrl : public SfxToolBoxControl +{ +public: + SFX_DECL_TOOLBOX_CONTROL(); + + SwTbxAutoTextCtrl( sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx ); + virtual ~SwTbxAutoTextCtrl() override; + + virtual void CreatePopupWindow() override; + virtual void StateChanged( sal_uInt16 nSID, + SfxItemState eState, + const SfxPoolItem* pState ) override; + + DECL_STATIC_LINK(SwTbxAutoTextCtrl, PopupHdl, Menu*, bool); +}; + +class SwPreviewZoomControl : public SfxToolBoxControl +{ +public: + SFX_DECL_TOOLBOX_CONTROL(); + + SwPreviewZoomControl( sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx ); + virtual ~SwPreviewZoomControl() override; + + virtual void StateChanged( sal_uInt16 nSID, + SfxItemState eState, + const SfxPoolItem* pState ) override; + + virtual VclPtr<InterimItemWindow> CreateItemWindow( vcl::Window *pParent ) override; +}; + +class SwJumpToSpecificPageControl : public SfxToolBoxControl +{ +public: + SFX_DECL_TOOLBOX_CONTROL(); + + SwJumpToSpecificPageControl( sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx ); + virtual ~SwJumpToSpecificPageControl() override; + + virtual VclPtr<InterimItemWindow> CreateItemWindow( vcl::Window *pParent ) override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wrap.hxx b/sw/source/uibase/inc/wrap.hxx new file mode 100644 index 000000000..60313ffd4 --- /dev/null +++ b/sw/source/uibase/inc/wrap.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_WRAP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WRAP_HXX + +#include <sfx2/tabdlg.hxx> +#include <sfx2/basedlgs.hxx> +#include <svx/swframetypes.hxx> + +namespace vcl { class Window; } +class SfxItemSet; +class SwWrtShell; + +class SwWrapDlg : public SfxSingleTabDialogController +{ +public: + SwWrapDlg(weld::Window* pParent, SfxItemSet& rSet, SwWrtShell* pSh, bool bDrawMode); +}; + +// circulation TabPage +class SwWrapTabPage: public SfxTabPage +{ + RndStdIds m_nAnchorId; + sal_uInt16 m_nHtmlMode; + + SwWrtShell* m_pWrtSh; + + bool m_bFormat; + bool m_bNew; + bool m_bHtmlMode; + bool m_bDrawMode; + bool m_bContourImage; + + // WRAPPING + std::unique_ptr<weld::RadioButton> m_xNoWrapRB; + std::unique_ptr<weld::RadioButton> m_xWrapLeftRB; + std::unique_ptr<weld::RadioButton> m_xWrapRightRB; + std::unique_ptr<weld::RadioButton> m_xWrapParallelRB; + std::unique_ptr<weld::RadioButton> m_xWrapThroughRB; + std::unique_ptr<weld::RadioButton> m_xIdealWrapRB; + + // MARGIN + std::unique_ptr<weld::MetricSpinButton> m_xLeftMarginED; + std::unique_ptr<weld::MetricSpinButton> m_xRightMarginED; + std::unique_ptr<weld::MetricSpinButton> m_xTopMarginED; + std::unique_ptr<weld::MetricSpinButton> m_xBottomMarginED; + + // OPTIONS + std::unique_ptr<weld::CheckButton> m_xWrapAnchorOnlyCB; + std::unique_ptr<weld::CheckButton> m_xWrapTransparentCB; + std::unique_ptr<weld::CheckButton> m_xWrapOutlineCB; + std::unique_ptr<weld::CheckButton> m_xWrapOutsideCB; + std::unique_ptr<weld::CheckButton> m_xAllowOverlapCB; + + void SetImages(); + virtual void ActivatePage(const SfxItemSet& rSet) override; + virtual DeactivateRC DeactivatePage(SfxItemSet *pSet) override; + + DECL_LINK(RangeModifyHdl, weld::MetricSpinButton&, void); + DECL_LINK(WrapTypeHdl, weld::ToggleButton&, void); + DECL_LINK(ContourHdl, weld::ToggleButton&, void); + + static const sal_uInt16 m_aWrapPageRg[]; + +public: + SwWrapTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rSet); + virtual ~SwWrapTabPage() override; + + virtual bool FillItemSet(SfxItemSet *rSet) override; + virtual void Reset(const SfxItemSet *rSet) override; + + static const sal_uInt16* GetRanges() { return m_aWrapPageRg; } + void SetNewFrame(bool bNewFrame) { m_bNew = bNewFrame; } + void SetFormatUsed(bool bFormat, bool bDrawMode) { m_bFormat = bFormat; m_bDrawMode = bDrawMode; } + void SetShell(SwWrtShell* pSh) { m_pWrtSh = pSh; } +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx new file mode 100644 index 000000000..4c02e5e46 --- /dev/null +++ b/sw/source/uibase/inc/wrtsh.hxx @@ -0,0 +1,652 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_WRTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WRTSH_HXX + +#include <swdllapi.h> +#include <fesh.hxx> +#include <swurl.hxx> +#include <IMark.hxx> +#include "navmgr.hxx" +#include <optional> +#include <o3tl/typed_flags_set.hxx> +#include <svx/swframetypes.hxx> +#include <vcl/weld.hxx> + +namespace vcl { class Window; } +class SbxArray; +class SwDoc; +class SwViewOption; +class SwFlyFrameAttrMgr; +class SwField; +class SwTOXBase; +class SwView; +class SvGlobalName; +class SwInputFieldList; +class SwSectionData; +class Timer; +class SvxMacro; +class SwFormatINetFormat; +class SvxAutoCorrect; +class NaviContentBookmark; +struct SwCallMouseEvent; +class SfxStringListItem; +enum class SvMacroItemId : sal_uInt16; +class SwFieldMgr; +class SfxRequest; + +namespace i18nutil { + struct SearchOptions2; +} + +enum class SelectionType : sal_Int32 +{ + NONE = 0x000000, + Text = CNT_TXT, // text, never frames too 0x0001 + Graphic = CNT_GRF, // graphic 0x0002 + Ole = CNT_OLE, // OLE 0x0010 + Frame = 0x000020, // frame, no content type + NumberList = 0x000040, // NumList + Table = 0x000080, // cursor is in table + TableCell = 0x000100, // table cells are selected + DrawObject = 0x000200, // drawing objects (rectangle, circle...) + DrawObjectEditMode = 0x000400, // draw-textobjects in edit mode + Ornament = 0x000800, // edit ornament objects + DbForm = 0x001000, // drawing objects: DB-Forms + FormControl = 0x002000, // a form control is focused. Neither set nor evaluated by the SwWrtShell itself, only by its clients. + Media = 0x004000, // Media object + ExtrudedCustomShape = 0x008000, // extruded custom shape + FontWork = 0x010000, // fontwork + PostIt = 0x020000, // annotation + TableRow = 0x040000, // table rows are selected + TableCol = 0x080000, // table columns are selected + All = 0x0ffff3, +}; +namespace o3tl { + template<> struct typed_flags<SelectionType> : is_typed_flags<SelectionType, 0x0ffff3> {}; +} + +/** Used by the UI to modify the document model. + +Eg. the Insert() method will take the current cursor position, insert the +string, and take care of undo etc. +*/ +class SW_DLLPUBLIC SwWrtShell: public SwFEShell +{ +private: + using SwCursorShell::Left; + using SwCursorShell::Right; + using SwCursorShell::Up; + using SwCursorShell::Down; + using SwCursorShell::LeftMargin; + using SwCursorShell::RightMargin; + using SwCursorShell::SelectTextAttr; + using SwCursorShell::GotoPage; + using SwFEShell::InsertObject; + using SwEditShell::AutoCorrect; + using SwCursorShell::GotoMark; + + typedef long (SwWrtShell::*SELECTFUNC)(const Point *, bool bProp ); + typedef void (SwWrtShell::*SELECTFUNC2)(const Point *, bool bProp ); + + SELECTFUNC2 m_fnDrag = &SwWrtShell::BeginDrag; + SELECTFUNC m_fnSetCursor = &SwWrtShell::SetCursor; + SELECTFUNC2 m_fnEndDrag = &SwWrtShell::DefaultEndDrag; + SELECTFUNC m_fnKillSel = &SwWrtShell::Ignore; + +public: + + using SwEditShell::Insert; + + long CallSetCursor(const Point* pPt, bool bProp) { return (this->*m_fnSetCursor)(pPt, bProp); } + void Drag (const Point* pPt, bool bProp) { (this->*m_fnDrag)(pPt, bProp); } + void EndDrag (const Point* pPt, bool bProp) { (this->*m_fnEndDrag)(pPt, bProp); } + long KillSelection(const Point* pPt, bool bProp) { return (this->*m_fnKillSel)(pPt, bProp); } + + bool IsSplitVerticalByDefault() const; + void SetSplitVerticalByDefault(bool value); + + // reset all selections + long ResetSelect( const Point *, bool ); + + // resets the cursorstack after movement with PageUp/-Down if a stack is built up + inline void ResetCursorStack(); + SelectionType GetSelectionType() const; + + bool IsModePushed() const { return nullptr != m_pModeStack; } + void PushMode(); + void PopMode(); + + void SttSelect(); + void EndSelect(); + bool IsInSelect() const { return m_bInSelect; } + void SetInSelect() { m_bInSelect = true; } + // is there a text- or frameselection? + bool HasSelection() const { return SwCursorShell::HasSelection() || + IsMultiSelection() || IsSelFrameMode() || IsObjSelected(); } + bool Pop(SwCursorShell::PopMode = SwCursorShell::PopMode::DeleteStack); + + void EnterStdMode(); + bool IsStdMode() const { return !m_bExtMode && !m_bAddMode && !m_bBlockMode; } + + void EnterExtMode(); + void LeaveExtMode(); + bool ToggleExtMode(); + bool IsExtMode() const { return m_bExtMode; } + + void EnterAddMode(); + void LeaveAddMode(); + void ToggleAddMode(); + bool IsAddMode() const { return m_bAddMode; } + + void EnterBlockMode(); + void LeaveBlockMode(); + bool ToggleBlockMode(); + bool IsBlockMode() const { return m_bBlockMode; } + + void SetInsMode( bool bOn = true ); + void ToggleInsMode() { SetInsMode( !m_bIns ); } + bool IsInsMode() const { return m_bIns; } + void SetRedlineFlagsAndCheckInsMode( RedlineFlags eMode ); + + void EnterSelFrameMode(const Point *pStartDrag = nullptr); + void LeaveSelFrameMode(); + bool IsSelFrameMode() const { return m_bLayoutMode; } + // reset selection of frames + void UnSelectFrame(); + + void Invalidate(); + + // select table cells for editing of formulas in the ribbonbar + inline void SelTableCells( const Link<SwWrtShell&,void> &rLink ); + inline void EndSelTableCells(); + + // leave per word or per line selection mode. Is usually called in MB-Up. + bool IsExtSel() const { return m_bSelWrd || m_bSelLn; } + + // query whether the active m_fnDrag pointer is set to BeginDrag + // is needed for MouseMove to work around bugs 55592/55931 + inline bool Is_FnDragEQBeginDrag() const; + + // base requests + bool IsEndWrd(); + bool IsSttOfPara() const { return IsSttPara(); } + bool IsEndOfPara() const { return IsEndPara(); } + + // select word / sentence + bool SelNearestWrd(); + bool SelWrd (const Point * = nullptr ); + // #i32329# Enhanced selection + void SelSentence (const Point *); + void SelPara (const Point *); + void SelAll(); + + // basecursortravelling +typedef bool (SwWrtShell:: *FNSimpleMove)(); + bool SimpleMove( FNSimpleMove, bool bSelect ); + + bool Left ( sal_uInt16 nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual = false ); + bool Right ( sal_uInt16 nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual = false ); + bool Up ( bool bSelect, sal_uInt16 nCount = 1, bool bBasicCall = false ); + bool Down ( bool bSelect, sal_uInt16 nCount = 1, bool bBasicCall = false ); + void NxtWrd ( bool bSelect = false ) { SimpleMove( &SwWrtShell::NxtWrd_, bSelect ); } + bool PrvWrd ( bool bSelect = false ) { return SimpleMove( &SwWrtShell::PrvWrd_, bSelect ); } + + bool LeftMargin ( bool bSelect, bool bBasicCall ); + bool RightMargin( bool bSelect, bool bBasicCall ); + + bool StartOfSection( bool bSelect = false ); + bool EndOfSection ( bool bSelect = false ); + + bool SttNxtPg ( bool bSelect = false ); + void SttPrvPg ( bool bSelect = false ); + void EndNxtPg ( bool bSelect = false ); + bool EndPrvPg ( bool bSelect = false ); + bool SttPg ( bool bSelect = false ); + bool EndPg ( bool bSelect = false ); + bool SttPara ( bool bSelect = false ); + void EndPara ( bool bSelect = false ); + bool FwdPara () + { return SimpleMove( &SwWrtShell::FwdPara_, false/*bSelect*/ ); } + void BwdPara () + { SimpleMove( &SwWrtShell::BwdPara_, false/*bSelect*/ ); } + void FwdSentence( bool bSelect = false ) + { SimpleMove( &SwWrtShell::FwdSentence_, bSelect ); } + void BwdSentence( bool bSelect = false ) + { SimpleMove( &SwWrtShell::BwdSentence_, bSelect ); } + + // #i20126# Enhanced table selection + bool SelectTableRowCol( const Point& rPt, const Point* pEnd = nullptr, bool bRowDrag = false ); + void SelectTableRow(); + void SelectTableCol(); + void SelectTableCell(); + + bool SelectTextAttr( sal_uInt16 nWhich, const SwTextAttr* pAttr = nullptr ); + + // per column jumps + void StartOfColumn (); + void EndOfColumn (); + void StartOfNextColumn (); + void EndOfNextColumn (); + void StartOfPrevColumn (); + void EndOfPrevColumn (); + + // set the cursor to page "nPage" at the beginning + // additionally to an identically named implementation in crsrsh.hxx + // here all existing selections are being reset before setting the + // cursor + bool GotoPage( sal_uInt16 nPage, bool bRecord ); + + // setting the cursor; remember the old position for turning back + DECL_LINK( ExecFlyMac, const SwFlyFrameFormat*, void ); + + bool PageCursor(SwTwips lOffset, bool bSelect); + + // update fields + void UpdateInputFields( SwInputFieldList* pLst = nullptr ); + + void NoEdit(bool bHideCursor = true); + void Edit(); + + bool IsRetainSelection() const { return m_bRetainSelection; } + void SetRetainSelection( bool bRet ) { m_bRetainSelection = bRet; } + + // change current data base and notify + void ChgDBData(const SwDBData& SwDBData); + + // delete + void DelToEndOfLine(); + void DelToStartOfLine(); + void DelLine(); + bool DelLeft(); + + // also deletes the frame or sets the cursor in the frame when bDelFrame == false + bool DelRight(); + void DelToEndOfPara(); + void DelToStartOfPara(); + bool DelToEndOfSentence(); + void DelToStartOfSentence(); + void DelNxtWord(); + void DelPrvWord(); + + // checks whether a word selection exists. + // According to the rules for intelligent Cut / Paste + // surrounding spaces are cut out. + // returns type of word selection (see enum) + enum word { + NO_WORD = 0, + WORD_SPACE_BEFORE = 1, + WORD_SPACE_AFTER = 2, + WORD_NO_SPACE = 3 + }; + int IntelligentCut(SelectionType nSelectionType, bool bCut = true); + + // edit + void Insert(SwField const &, SwPaM* pAnnotationRange = nullptr); + void Insert(const OUString &); + // graphic + void Insert( const OUString &rPath, const OUString &rFilter, + const Graphic &, SwFlyFrameAttrMgr * = nullptr, + RndStdIds nAnchorType = RndStdIds::FLY_AT_PARA); + + void InsertByWord( const OUString & ); + void InsertPageBreak(const OUString *pPageDesc = nullptr, const ::std::optional<sal_uInt16>& rPgNum = std::nullopt); + void InsertLineBreak(); + void InsertColumnBreak(); + void InsertFootnote(const OUString &, bool bEndNote = false, bool bEdit = true ); + void SplitNode( bool bAutoFormat = false ); + bool CanInsert(); + + // indexes + void InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet = nullptr); + void UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet = nullptr); + + // numbering and bullets + /** + Turns on numbering or bullets. + + @param bNum true: turn on numbering + false: turn on bullets + */ + void NumOrBulletOn(bool bNum); // #i29560# + void NumOrBulletOff(); // #i29560# + void NumOn(); + void BulletOn(); + + //OLE + void InsertObject( /*SvInPlaceObjectRef *pObj, */ // != 0 for clipboard + const svt::EmbeddedObjectRef&, + SvGlobalName const *pName, // != 0 create object accordingly + sal_uInt16 nSlotId = 0); // SlotId for dialog + + bool InsertOleObject( const svt::EmbeddedObjectRef& xObj, SwFlyFrameFormat **pFlyFrameFormat = nullptr ); + void LaunchOLEObj( long nVerb = 0 ); // start server + virtual void MoveObjectIfActive( svt::EmbeddedObjectRef& xObj, const Point& rOffset ) override; + virtual void CalcAndSetScale( svt::EmbeddedObjectRef& xObj, + const SwRect *pFlyPrtRect = nullptr, + const SwRect *pFlyFrameRect = nullptr, + const bool bNoTextFramePrtAreaChanged = false ) override; + virtual void ConnectObj( svt::EmbeddedObjectRef& xIPObj, const SwRect &rPrt, + const SwRect &rFrame ) override; + + // styles and formats + + // enum tells when should happen when the style was not found + enum GetStyle { GETSTYLE_NOCREATE, // create none + GETSTYLE_CREATESOME, // if on PoolId create mapt + GETSTYLE_CREATEANY }; // return standard if applicable + + SwTextFormatColl* GetParaStyle(const OUString &rCollName, + GetStyle eCreate = GETSTYLE_NOCREATE); + SwCharFormat* GetCharStyle(const OUString &rFormatName, + GetStyle eCreate = GETSTYLE_NOCREATE); + SwFrameFormat* GetTableStyle(const OUString &rFormatName); + + void SetPageStyle(const OUString &rCollName); + + OUString const & GetCurPageStyle() const; + + // change current style using the attributes in effect + void QuickUpdateStyle(); + + enum DoType { UNDO, REDO, REPEAT }; + + enum class FieldDialogPressedButton { NONE, Previous, Next }; + + void Do( DoType eDoType, sal_uInt16 nCnt = 1 ); + OUString GetDoString( DoType eDoType ) const; + OUString GetRepeatString() const; + void GetDoStrings( DoType eDoType, SfxStringListItem& rStrLstItem ) const; + + // search and replace + sal_uLong SearchPattern(const i18nutil::SearchOptions2& rSearchOpt, + bool bSearchInNotes, + SwDocPositions eStart, SwDocPositions eEnd, + FindRanges eFlags = FindRanges::InBody, + bool bReplace = false ); + + sal_uLong SearchTempl (const OUString &rTempl, + SwDocPositions eStart, SwDocPositions eEnd, + FindRanges eFlags = FindRanges::InBody, + const OUString* pReplTempl = nullptr ); + + sal_uLong SearchAttr (const SfxItemSet& rFindSet, + bool bNoColls, + SwDocPositions eStart, SwDocPositions eEnd, + FindRanges eFlags = FindRanges::InBody, + const i18nutil::SearchOptions2* pSearchOpt = nullptr, + const SfxItemSet* pReplaceSet = nullptr); + + void AutoCorrect( SvxAutoCorrect& rACorr, sal_Unicode cChar ); + + // action ahead of cursor movement + // resets selection if applicable, triggers timer and GCAttr() + void MoveCursor( bool bWithSelect = false ); + + // update input fields + bool StartInputFieldDlg(SwField*, bool bPrevButton, bool bNextButton, weld::Widget* pParentWin, FieldDialogPressedButton* pPressedButton = nullptr); + // update DropDown fields + bool StartDropDownFieldDlg(SwField*, bool bPrevButton, bool bNextButton, weld::Widget* pParentWin, FieldDialogPressedButton* pPressedButton = nullptr); + + //"Handler" for changes at DrawView - for controls. + virtual void DrawSelChanged( ) override; + + // jump to bookmark and set the "selections-flags" correctly again + void GotoMark( const ::sw::mark::IMark* const pMark ); + bool GotoMark( const ::sw::mark::IMark* const pMark, bool bSelect ); + void GotoMark( const OUString& rName ); + bool GoNextBookmark(); // true when there still was one + bool GoPrevBookmark(); + + bool GotoFieldmark(::sw::mark::IFieldmark const * const pMark); + + bool GotoField( const SwFormatField& rField ); + + // jump to the next / previous hyperlink - inside text and also + // on graphics + void SelectNextPrevHyperlink( bool bNext ); + + // determine corresponding SwView + const SwView& GetView() const { return m_rView; } + SwView& GetView() { return m_rView; } + + // Because nobody else is doing it, here is an ExecMacro() + void ExecMacro( const SvxMacro& rMacro, OUString* pRet = nullptr, SbxArray* pArgs = nullptr ); + // call into the dark Basic/JavaScript + sal_uInt16 CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent, + bool bCheckPtr = false ); + + // a click at the given field. the cursor is on it. + // execute the predefined actions. + void ClickToField( const SwField& rField ); + void ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter = LoadUrlFlags::NONE ); + bool ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter ); + inline bool IsInClickToEdit() const ; + + // if a URL-Button is selected, return its URL; otherwise an empty string + bool GetURLFromButton( OUString& rURL, OUString& rDescr ) const; + + void NavigatorPaste( const NaviContentBookmark& rBkmk, + const sal_uInt16 nAction ); + + virtual void ApplyViewOptions( const SwViewOption &rOpt ) override; + virtual void SetReadonlyOption( bool bSet ) override; + + // automatic update of styles + void AutoUpdateFrame(SwFrameFormat* pFormat, const SfxItemSet& rStyleSet); + void AutoUpdatePara(SwTextFormatColl* pColl, const SfxItemSet& rStyleSet, SwPaM* pPaM = nullptr ); + + // starts dialog for inserting ranges via Drag&Drop/Clipboard + void StartInsertRegionDialog(const SwSectionData&); + + // ctor, the first one is a kind of a controlled copy ctor for more views of a document + SwWrtShell( SwWrtShell&, vcl::Window *pWin, SwView &rShell); + SwWrtShell( SwDoc& rDoc, vcl::Window *pWin, SwView &rShell, + const SwViewOption *pViewOpt); + virtual ~SwWrtShell() override; + + bool TryRemoveIndent(); // #i23725# + + OUString GetSelDescr() const; + + SwNavigationMgr& GetNavigationMgr() { return m_aNavigationMgr; } + + void addCurrentPosition(); + bool GotoFly( const OUString& rName, FlyCntType eType = FLYCNTTYPE_ALL, + bool bSelFrame = true ); + bool GotoINetAttr( const SwTextINetFormat& rAttr ); + void GotoOutline( SwOutlineNodes::size_type nIdx ); + bool GotoOutline( const OUString& rName ); + bool GotoRegion( const OUString& rName ); + bool GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType = 0, + sal_uInt16 nSeqNo = 0 ); + bool GotoNextTOXBase( const OUString* pName = nullptr); + bool GotoTable( const OUString& rName ); + void GotoFormatField( const SwFormatField& rField ); + const SwRangeRedline* GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect); + + void ChangeHeaderOrFooter(const OUString& rStyleName, bool bHeader, bool bOn, bool bShowWarning); + virtual void SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) override; + + /// Inserts a new annotation/comment at the current cursor position / selection. + void InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq); + +private: + + SAL_DLLPRIVATE void OpenMark(); + SAL_DLLPRIVATE void CloseMark( bool bOkFlag ); + + struct ModeStack + { + ModeStack *pNext; + bool bAdd, + bBlock, + bExt, + bIns; + ModeStack(ModeStack *pNextMode, bool _bIns, bool _bExt, bool _bAdd, bool _bBlock): + pNext(pNextMode), + bAdd(_bAdd), + bBlock(_bBlock), + bExt(_bExt), + bIns(_bIns) + {} + } *m_pModeStack = nullptr; + + // carry cursor along when PageUp / -Down + enum PageMove + { + MV_NO, + MV_PAGE_UP, + MV_PAGE_DOWN + } m_ePageMove = MV_NO; + + struct CursorStack + { + Point aDocPos; + std::unique_ptr<CursorStack> pNext; + bool bValidCurPos : 1; + bool bIsFrameSel : 1; + SwTwips lOffset; + + CursorStack( bool bValid, bool bFrameSel, const Point &rDocPos, + SwTwips lOff, std::unique_ptr<CursorStack> pN ) + : aDocPos(rDocPos), + pNext(std::move(pN)), + bValidCurPos( bValid ), + bIsFrameSel( bFrameSel ), + lOffset(lOff) + { + } + + }; + std::unique_ptr<CursorStack> m_pCursorStack; + + SwView &m_rView; + SwNavigationMgr m_aNavigationMgr; + + Point m_aDest; + bool m_bDestOnStack = false; + bool HasCursorStack() const { return nullptr != m_pCursorStack; } + SAL_DLLPRIVATE bool PushCursor(SwTwips lOffset, bool bSelect); + SAL_DLLPRIVATE bool PopCursor(bool bUpdate, bool bSelect = false); + + // take END cursor along when PageUp / -Down + SAL_DLLPRIVATE void SttWrd(); + SAL_DLLPRIVATE void EndWrd(); + SAL_DLLPRIVATE bool NxtWrd_(); + SAL_DLLPRIVATE bool PrvWrd_(); + // #i92468# + SAL_DLLPRIVATE bool NxtWrdForDelete(); + SAL_DLLPRIVATE bool PrvWrdForDelete(); + SAL_DLLPRIVATE bool FwdSentence_(); + SAL_DLLPRIVATE bool BwdSentence_(); + bool FwdPara_(); + SAL_DLLPRIVATE bool BwdPara_(); + + // selections + bool m_bIns :1; + bool m_bInSelect :1; + bool m_bExtMode :1; + bool m_bAddMode :1; + bool m_bBlockMode :1; + bool m_bLayoutMode :1; + bool m_bSelWrd :1; + bool m_bSelLn :1; + bool m_bIsInClickToEdit:1; + bool m_bClearMark :1; // don't delete selection for ChartAutoPilot + bool m_bRetainSelection :1; // Do not remove selections + + Point m_aStart; + Link<SwWrtShell&,void> m_aSelTableLink; + + // resets the cursor stack after movement by PageUp/-Down + SAL_DLLPRIVATE void ResetCursorStack_(); + + using SwCursorShell::SetCursor; + SAL_DLLPRIVATE long SetCursor(const Point *, bool bProp=false ); + + SAL_DLLPRIVATE long SetCursorKillSel(const Point *, bool bProp ); + + SAL_DLLPRIVATE void BeginDrag(const Point *, bool bProp ); + SAL_DLLPRIVATE void DefaultDrag(const Point *, bool bProp ); + SAL_DLLPRIVATE void DefaultEndDrag(const Point *, bool bProp ); + + SAL_DLLPRIVATE void ExtSelWrd(const Point *, bool bProp ); + SAL_DLLPRIVATE void ExtSelLn(const Point *, bool bProp ); + + SAL_DLLPRIVATE void BeginFrameDrag(const Point *, bool bProp ); + + // after SSize/Move of a frame update; Point is destination. + SAL_DLLPRIVATE void UpdateLayoutFrame(const Point *, bool bProp ); + + SAL_DLLPRIVATE void SttLeaveSelect(); + SAL_DLLPRIVATE void AddLeaveSelect(); + SAL_DLLPRIVATE long Ignore(const Point *, bool bProp ); + + SAL_DLLPRIVATE void LeaveExtSel() { m_bSelWrd = m_bSelLn = false;} + + SAL_DLLPRIVATE bool GoStart(bool KeepArea, bool *, + bool bSelect, bool bDontMoveRegion = false); + SAL_DLLPRIVATE bool GoEnd(bool KeepArea = false, const bool * = nullptr); + + enum BookMarkMove + { + BOOKMARK_INDEX, + BOOKMARK_NEXT, + BOOKMARK_PREV + }; + + SAL_DLLPRIVATE bool MoveBookMark(BookMarkMove eFuncId, const ::sw::mark::IMark* const pMark=nullptr); +}; + +inline void SwWrtShell::ResetCursorStack() +{ + if ( HasCursorStack() ) + ResetCursorStack_(); +} + +inline void SwWrtShell::SelTableCells(const Link<SwWrtShell&,void> &rLink ) +{ + SetSelTableCells( true ); + m_bClearMark = true; + m_aSelTableLink = rLink; +} +inline void SwWrtShell::EndSelTableCells() +{ + SetSelTableCells( false ); + m_bClearMark = true; +} + +inline bool SwWrtShell::IsInClickToEdit() const { return m_bIsInClickToEdit; } + +inline bool SwWrtShell::Is_FnDragEQBeginDrag() const +{ +#ifdef __GNUC__ + SELECTFUNC2 fnTmp = &SwWrtShell::BeginDrag; + return m_fnDrag == fnTmp; +#else + return m_fnDrag == &SwWrtShell::BeginDrag; +#endif +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wtabsh.hxx b/sw/source/uibase/inc/wtabsh.hxx new file mode 100644 index 000000000..041175019 --- /dev/null +++ b/sw/source/uibase/inc/wtabsh.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_UIBASE_INC_WTABSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WTABSH_HXX + +#include "tabsh.hxx" + +class SwWebTableShell: public SwTableShell +{ +public: + SFX_DECL_INTERFACE(SW_WEBTABSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebTableShell(SwView &rView); + virtual ~SwWebTableShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wtextsh.hxx b/sw/source/uibase/inc/wtextsh.hxx new file mode 100644 index 000000000..7924fdf90 --- /dev/null +++ b/sw/source/uibase/inc/wtextsh.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_INC_WTEXTSH_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WTEXTSH_HXX + +#include "textsh.hxx" + +class SwWebTextShell: public SwTextShell +{ +public: + SFX_DECL_INTERFACE(SW_WEBTEXTSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebTextShell(SwView &rView); + virtual ~SwWebTextShell() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/wview.hxx b/sw/source/uibase/inc/wview.hxx new file mode 100644 index 000000000..cfd63881d --- /dev/null +++ b/sw/source/uibase/inc/wview.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_WVIEW_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_WVIEW_HXX + +#include <swdllapi.h> +#include <view.hxx> + +class SW_DLLPUBLIC SwWebView: public SwView +{ +protected: + virtual void SelectShell() override; + +public: + SFX_DECL_VIEWFACTORY(SwWebView); + SFX_DECL_INTERFACE(SW_WEBVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SwWebView(SfxViewFrame* pFrame, SfxViewShell*); + virtual ~SwWebView() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/zoomctrl.hxx b/sw/source/uibase/inc/zoomctrl.hxx new file mode 100644 index 000000000..d0802989a --- /dev/null +++ b/sw/source/uibase/inc/zoomctrl.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_UIBASE_INC_ZOOMCTRL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_INC_ZOOMCTRL_HXX + +#include <svx/zoomctrl.hxx> + +class SwZoomControl : public SvxZoomStatusBarControl +{ +private: + OUString sPreviewZoom; +public: + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void StateChanged( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + + SFX_DECL_STATUSBAR_CONTROL(); + + SwZoomControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SwZoomControl() override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/index/idxmrk.cxx b/sw/source/uibase/index/idxmrk.cxx new file mode 100644 index 000000000..a6deaa821 --- /dev/null +++ b/sw/source/uibase/index/idxmrk.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 <idxmrk.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> + +SFX_IMPL_CHILDWINDOW_WITHID(SwInsertIdxMarkWrapper, FN_INSERT_IDX_ENTRY_DLG) + +SwInsertIdxMarkWrapper::SwInsertIdxMarkWrapper( vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) : + SfxChildWindow(pParentWindow, nId) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + xAbstDlg = pFact->CreateIndexMarkFloatDlg(pBindings, this, pParentWindow->GetFrameWeld(), pInfo); + SetController(xAbstDlg->GetController()); +} + +SfxChildWinInfo SwInsertIdxMarkWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + + return aInfo; +} + +void SwInsertIdxMarkWrapper::ReInitDlg(SwWrtShell& rWrtShell) +{ + xAbstDlg->ReInitDlg(rWrtShell); +} + +SFX_IMPL_CHILDWINDOW_WITHID(SwInsertAuthMarkWrapper, FN_INSERT_AUTH_ENTRY_DLG) + +SwInsertAuthMarkWrapper::SwInsertAuthMarkWrapper( vcl::Window *pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) : + SfxChildWindow(pParentWindow, nId) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + xAbstDlg = pFact->CreateAuthMarkFloatDlg(pBindings, this, pParentWindow->GetFrameWeld(), pInfo); + SetController(xAbstDlg->GetController()); +} + +SfxChildWinInfo SwInsertAuthMarkWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + +void SwInsertAuthMarkWrapper::ReInitDlg(SwWrtShell& rWrtShell) +{ + xAbstDlg->ReInitDlg(rWrtShell); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/index/toxmgr.cxx b/sw/source/uibase/index/toxmgr.cxx new file mode 100644 index 000000000..4e7e65f9c --- /dev/null +++ b/sw/source/uibase/index/toxmgr.cxx @@ -0,0 +1,497 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <wrtsh.hxx> +#include <swwait.hxx> +#include <view.hxx> +#include <toxmgr.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swundo.hxx> + +// handle indexes with TOXMgr +SwTOXMgr::SwTOXMgr(SwWrtShell* pShell): + pSh(pShell) +{ + pSh->GetCurTOXMarks(aCurMarks); + SetCurTOXMark(0); +} + +SwTOXMark* SwTOXMgr::GetTOXMark(sal_uInt16 nId) +{ + if(!aCurMarks.empty()) + return aCurMarks[nId]; + return nullptr; +} + +void SwTOXMgr::DeleteTOXMark() +{ + SwTOXMark* pNext = nullptr; + if( pCurTOXMark ) + { + pNext = const_cast<SwTOXMark*>(&pSh->GotoTOXMark( *pCurTOXMark, TOX_NXT )); + if( pNext == pCurTOXMark ) + pNext = nullptr; + + pSh->DeleteTOXMark( pCurTOXMark ); + pSh->SetModified(); + } + // go to next one + pCurTOXMark = pNext; +} + +void SwTOXMgr::InsertTOXMark(const SwTOXMarkDescription& rDesc) +{ + SwTOXMark* pMark = nullptr; + switch(rDesc.GetTOXType()) + { + case TOX_CONTENT: + { + OSL_ENSURE(rDesc.GetLevel() > 0 && rDesc.GetLevel() <= MAXLEVEL, + "invalid InsertTOCMark level"); + pMark = new SwTOXMark(pSh->GetTOXType(TOX_CONTENT, 0)); + pMark->SetLevel( static_cast< sal_uInt16 >(rDesc.GetLevel()) ); + + if(rDesc.GetAltStr()) + pMark->SetAlternativeText(*rDesc.GetAltStr()); + } + break; + case TOX_INDEX: + { + pMark = new SwTOXMark(pSh->GetTOXType(TOX_INDEX, 0)); + + if( rDesc.GetPrimKey() && !rDesc.GetPrimKey()->isEmpty() ) + { + pMark->SetPrimaryKey( *rDesc.GetPrimKey() ); + if(rDesc.GetPhoneticReadingOfPrimKey()) + pMark->SetPrimaryKeyReading( *rDesc.GetPhoneticReadingOfPrimKey() ); + + if( rDesc.GetSecKey() && !rDesc.GetSecKey()->isEmpty() ) + { + pMark->SetSecondaryKey( *rDesc.GetSecKey() ); + if(rDesc.GetPhoneticReadingOfSecKey()) + pMark->SetSecondaryKeyReading( *rDesc.GetPhoneticReadingOfSecKey() ); + } + } + if(rDesc.GetAltStr()) + pMark->SetAlternativeText(*rDesc.GetAltStr()); + if(rDesc.GetPhoneticReadingOfAltStr()) + pMark->SetTextReading( *rDesc.GetPhoneticReadingOfAltStr() ); + pMark->SetMainEntry(rDesc.IsMainEntry()); + } + break; + case TOX_USER: + { + OSL_ENSURE(rDesc.GetLevel() > 0 && rDesc.GetLevel() <= MAXLEVEL, + "invalid InsertTOCMark level"); + sal_uInt16 nId = rDesc.GetTOUName() ? + GetUserTypeID(*rDesc.GetTOUName()) : 0; + pMark = new SwTOXMark(pSh->GetTOXType(TOX_USER, nId)); + pMark->SetLevel( static_cast< sal_uInt16 >(rDesc.GetLevel()) ); + + if(rDesc.GetAltStr()) + pMark->SetAlternativeText(*rDesc.GetAltStr()); + } + break; + case TOX_BIBLIOGRAPHY: + { + pMark = new SwTOXMark(pSh->GetTOXType(TOX_BIBLIOGRAPHY, 0)); + + if( rDesc.GetPrimKey() && !rDesc.GetPrimKey()->isEmpty() ) + { + pMark->SetPrimaryKey( *rDesc.GetPrimKey() ); + if(rDesc.GetPhoneticReadingOfPrimKey()) + pMark->SetPrimaryKeyReading( *rDesc.GetPhoneticReadingOfPrimKey() ); + + if( rDesc.GetSecKey() && !rDesc.GetSecKey()->isEmpty() ) + { + pMark->SetSecondaryKey( *rDesc.GetSecKey() ); + if(rDesc.GetPhoneticReadingOfSecKey()) + pMark->SetSecondaryKeyReading( *rDesc.GetPhoneticReadingOfSecKey() ); + } + } + if(rDesc.GetAltStr()) + pMark->SetAlternativeText(*rDesc.GetAltStr()); + if(rDesc.GetPhoneticReadingOfAltStr()) + pMark->SetTextReading( *rDesc.GetPhoneticReadingOfAltStr() ); + pMark->SetMainEntry(rDesc.IsMainEntry()); + } + break; + default:; //prevent warning + } + + if (!pMark) + return; + + pSh->StartAllAction(); + pSh->SwEditShell::Insert(*pMark); + pSh->EndAllAction(); +} + +// Update of TOXMarks +void SwTOXMgr::UpdateTOXMark(const SwTOXMarkDescription& rDesc) +{ + assert(pCurTOXMark && "no current TOXMark"); + pSh->StartAllAction(); + if(pCurTOXMark->GetTOXType()->GetType() == TOX_INDEX) + { + if(rDesc.GetPrimKey() && !rDesc.GetPrimKey()->isEmpty() ) + { + pCurTOXMark->SetPrimaryKey( *rDesc.GetPrimKey() ); + if(rDesc.GetPhoneticReadingOfPrimKey()) + pCurTOXMark->SetPrimaryKeyReading( *rDesc.GetPhoneticReadingOfPrimKey() ); + else + pCurTOXMark->SetPrimaryKeyReading(OUString()); + + if( rDesc.GetSecKey() && !rDesc.GetSecKey()->isEmpty() ) + { + pCurTOXMark->SetSecondaryKey( *rDesc.GetSecKey() ); + if(rDesc.GetPhoneticReadingOfSecKey()) + pCurTOXMark->SetSecondaryKeyReading( *rDesc.GetPhoneticReadingOfSecKey() ); + else + pCurTOXMark->SetSecondaryKeyReading(OUString()); + } + else + { + pCurTOXMark->SetSecondaryKey(OUString()); + pCurTOXMark->SetSecondaryKeyReading(OUString()); + } + } + else + { + pCurTOXMark->SetPrimaryKey(OUString()); + pCurTOXMark->SetPrimaryKeyReading(OUString()); + pCurTOXMark->SetSecondaryKey(OUString()); + pCurTOXMark->SetSecondaryKeyReading(OUString()); + } + if(rDesc.GetPhoneticReadingOfAltStr()) + pCurTOXMark->SetTextReading( *rDesc.GetPhoneticReadingOfAltStr() ); + else + pCurTOXMark->SetTextReading(OUString()); + pCurTOXMark->SetMainEntry(rDesc.IsMainEntry()); + } + else + pCurTOXMark->SetLevel( static_cast< sal_uInt16 >(rDesc.GetLevel()) ); + + if(rDesc.GetAltStr()) + { + // JP 26.08.96: Bug 30344 - either the text of a Doc or an alternative test, + // not both! + bool bReplace = pCurTOXMark->IsAlternativeText(); + if( bReplace ) + pCurTOXMark->SetAlternativeText( *rDesc.GetAltStr() ); + else + { + SwTOXMark aCpy( *pCurTOXMark ); + aCurMarks.clear(); + pSh->DeleteTOXMark(pCurTOXMark); + aCpy.SetAlternativeText( *rDesc.GetAltStr() ); + pSh->SwEditShell::Insert( aCpy ); + pCurTOXMark = nullptr; + } + } + pSh->SetModified(); + pSh->EndAllAction(); + // Bug 36207 pCurTOXMark points nowhere here! + if(!pCurTOXMark) + { + pSh->Left(CRSR_SKIP_CHARS, false, 1, false ); + pSh->GetCurTOXMarks(aCurMarks); + SetCurTOXMark(0); + } +} + +// determine UserTypeID +sal_uInt16 SwTOXMgr::GetUserTypeID(const OUString& rStr) +{ + sal_uInt16 nSize = pSh->GetTOXTypeCount(TOX_USER); + for(sal_uInt16 i=0; i < nSize; ++i) + { + const SwTOXType* pTmp = pSh->GetTOXType(TOX_USER, i); + if(pTmp && pTmp->GetTypeName() == rStr) + return i; + } + SwTOXType aUserType(*pSh->GetDoc(), TOX_USER, rStr); + pSh->InsertTOXType(aUserType); + return nSize; +} + +// traveling between TOXMarks +void SwTOXMgr::NextTOXMark(bool bSame) +{ + OSL_ENSURE(pCurTOXMark, "no current TOXMark"); + if( pCurTOXMark ) + { + SwTOXSearch eDir = bSame ? TOX_SAME_NXT : TOX_NXT; + pCurTOXMark = const_cast<SwTOXMark*>(&pSh->GotoTOXMark( *pCurTOXMark, eDir )); + } +} + +void SwTOXMgr::PrevTOXMark(bool bSame) +{ + OSL_ENSURE(pCurTOXMark, "no current TOXMark"); + if( pCurTOXMark ) + { + SwTOXSearch eDir = bSame ? TOX_SAME_PRV : TOX_PRV; + pCurTOXMark = const_cast<SwTOXMark*>(&pSh->GotoTOXMark(*pCurTOXMark, eDir )); + } +} + +const SwTOXType* SwTOXMgr::GetTOXType(TOXTypes eTyp) const +{ + return pSh->GetTOXType(eTyp, 0); +} + +void SwTOXMgr::SetCurTOXMark(sal_uInt16 nId) +{ + pCurTOXMark = (nId < aCurMarks.size()) ? aCurMarks[nId] : nullptr; +} + +bool SwTOXMgr::UpdateOrInsertTOX(const SwTOXDescription& rDesc, + SwTOXBase** ppBase, + const SfxItemSet* pSet) +{ + SwWait aWait( *pSh->GetView().GetDocShell(), true ); + bool bRet = true; + const SwTOXBase* pCurTOX = ppBase && *ppBase ? *ppBase : pSh->GetCurTOX(); + SwTOXBase* pTOX = const_cast<SwTOXBase*>(pCurTOX); + + SwTOXBase * pNewTOX = nullptr; + + if (pTOX) + pNewTOX = new SwTOXBase(*pTOX); + + TOXTypes eCurTOXType = rDesc.GetTOXType(); + if(pCurTOX && !ppBase && pSh->HasSelection()) + pSh->EnterStdMode(); + + switch(eCurTOXType) + { + case TOX_INDEX : + { + if(!pCurTOX || (ppBase && !(*ppBase))) + { + const SwTOXType* pType = pSh->GetTOXType(eCurTOXType, 0); + SwForm aForm(eCurTOXType); + pNewTOX = new SwTOXBase(pType, aForm, SwTOXElement::Mark, pType->GetTypeName()); + } + pNewTOX->SetOptions(rDesc.GetIndexOptions()); + pNewTOX->SetMainEntryCharStyle(rDesc.GetMainEntryCharStyle()); + pSh->SetTOIAutoMarkURL(rDesc.GetAutoMarkURL()); + pSh->ApplyAutoMark(); + } + break; + case TOX_CONTENT : + { + if(!pCurTOX || (ppBase && !(*ppBase))) + { + const SwTOXType* pType = pSh->GetTOXType(eCurTOXType, 0); + SwForm aForm(eCurTOXType); + pNewTOX = new SwTOXBase(pType, aForm, rDesc.GetContentOptions(), pType->GetTypeName()); + } + pNewTOX->SetCreate(rDesc.GetContentOptions()); + pNewTOX->SetLevel(rDesc.GetLevel()); + } + break; + case TOX_USER : + { + if(!pCurTOX || (ppBase && !(*ppBase))) + { + sal_uInt16 nPos = 0; + sal_uInt16 nSize = pSh->GetTOXTypeCount(eCurTOXType); + for(sal_uInt16 i=0; rDesc.GetTOUName() && i < nSize; ++i) + { const SwTOXType* pType = pSh->GetTOXType(TOX_USER, i); + if(pType->GetTypeName() == *rDesc.GetTOUName()) + { nPos = i; + break; + } + } + const SwTOXType* pType = pSh->GetTOXType(eCurTOXType, nPos); + + SwForm aForm(eCurTOXType); + pNewTOX = new SwTOXBase(pType, aForm, rDesc.GetContentOptions(), pType->GetTypeName()); + + } + else + { + const_cast<SwTOXBase*>( pCurTOX )->SetCreate(rDesc.GetContentOptions()); + } + pNewTOX->SetLevelFromChapter(rDesc.IsLevelFromChapter()); + } + break; + case TOX_CITATION: /** TODO */break; + case TOX_OBJECTS: + case TOX_TABLES: + case TOX_AUTHORITIES: + case TOX_BIBLIOGRAPHY: + case TOX_ILLUSTRATIONS: + { + //Special handling for TOX_AUTHORITY + if(TOX_AUTHORITIES == eCurTOXType) + { + SwAuthorityFieldType* pFType = static_cast<SwAuthorityFieldType*>( + pSh->GetFieldType(SwFieldIds::TableOfAuthorities, OUString())); + if (!pFType) + { + SwAuthorityFieldType const type(pSh->GetDoc()); + pFType = static_cast<SwAuthorityFieldType*>( + pSh->InsertFieldType(type)); + } + OUString const& rBrackets(rDesc.GetAuthBrackets()); + if (rBrackets.isEmpty()) + { + pFType->SetPreSuffix('\0', '\0'); + } + else + { + assert(rBrackets.getLength() == 2); + pFType->SetPreSuffix(rBrackets[0], rBrackets[1]); + } + pFType->SetSequence(rDesc.IsAuthSequence()); + SwTOXSortKey rArr[3]; + rArr[0] = rDesc.GetSortKey1(); + rArr[1] = rDesc.GetSortKey2(); + rArr[2] = rDesc.GetSortKey3(); + pFType->SetSortKeys(3, rArr); + pFType->SetSortByDocument(rDesc.IsSortByDocument()); + pFType->SetLanguage(rDesc.GetLanguage()); + pFType->SetSortAlgorithm(rDesc.GetSortAlgorithm()); + + pFType->UpdateFields(); + } + // TODO: consider properties of the current TOXType + if(!pCurTOX || (ppBase && !(*ppBase))) + { + const SwTOXType* pType = pSh->GetTOXType(eCurTOXType, 0); + SwForm aForm(eCurTOXType); + pNewTOX = new SwTOXBase( + pType, aForm, + TOX_AUTHORITIES == eCurTOXType ? SwTOXElement::Mark : SwTOXElement::NONE, + pType->GetTypeName()); + } + else + { + if((!ppBase || !(*ppBase)) && pSh->HasSelection()) + pSh->DelRight(); + pNewTOX = const_cast<SwTOXBase*>(pCurTOX); + } + pNewTOX->SetFromObjectNames(rDesc.IsCreateFromObjectNames()); + pNewTOX->SetOLEOptions(rDesc.GetOLEOptions()); + } + break; + } + + OSL_ENSURE(pNewTOX, "no TOXBase created!" ); + if(!pNewTOX) + return false; + + pNewTOX->SetFromChapter(rDesc.IsFromChapter()); + pNewTOX->SetSequenceName(rDesc.GetSequenceName()); + pNewTOX->SetCaptionDisplay(rDesc.GetCaptionDisplay()); + pNewTOX->SetProtected(rDesc.IsReadonly()); + + for(sal_uInt16 nLevel = 0; nLevel < MAXLEVEL; nLevel++) + pNewTOX->SetStyleNames(rDesc.GetStyleNames(nLevel), nLevel); + + if(rDesc.GetTitle()) + pNewTOX->SetTitle(*rDesc.GetTitle()); + if(rDesc.GetForm()) + pNewTOX->SetTOXForm(*rDesc.GetForm()); + pNewTOX->SetLanguage(rDesc.GetLanguage()); + pNewTOX->SetSortAlgorithm(rDesc.GetSortAlgorithm()); + + if(!pCurTOX || (ppBase && !(*ppBase)) ) + { + // when ppBase is passed over, TOXBase is only created here + // and then inserted in a global document by the dialog + if(ppBase) + (*ppBase) = pNewTOX; + else + { + pSh->InsertTableOf(*pNewTOX, pSet); + delete pNewTOX; + } + } + else + { + SwDoc * pDoc = pSh->GetDoc(); + + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::TOXCHANGE, nullptr); + } + + pDoc->ChangeTOX(*pTOX, *pNewTOX); + + pTOX->DisableKeepExpression(); + pSh->UpdateTableOf(*pTOX, pSet); + bRet = false; + pTOX->EnableKeepExpression(); + + if (pDoc->GetIDocumentUndoRedo().DoesUndo()) + { + pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::TOXCHANGE, nullptr); + } + } + + return bRet; +} + +void SwTOXDescription::SetSortKeys(SwTOXSortKey eKey1, + SwTOXSortKey eKey2, + SwTOXSortKey eKey3) +{ + SwTOXSortKey aArr[3]; + sal_uInt16 nPos = 0; + if(AUTH_FIELD_END > eKey1.eField) + aArr[nPos++] = eKey1; + if(AUTH_FIELD_END > eKey2.eField) + aArr[nPos++] = eKey2; + if(AUTH_FIELD_END > eKey3.eField) + aArr[nPos++] = eKey3; + + m_eSortKey1 = aArr[0]; + m_eSortKey2 = aArr[1]; + m_eSortKey3 = aArr[2]; +} + +void SwTOXDescription::ApplyTo(SwTOXBase& rTOXBase) +{ + for(sal_uInt16 i = 0; i < MAXLEVEL; i++) + rTOXBase.SetStyleNames(GetStyleNames(i), i); + rTOXBase.SetTitle(GetTitle() ? *GetTitle() : OUString()); + rTOXBase.SetCreate(GetContentOptions()); + + if(GetTOXType() == TOX_INDEX) + rTOXBase.SetOptions(GetIndexOptions()); + if(GetTOXType() != TOX_INDEX) + rTOXBase.SetLevel(GetLevel()); + rTOXBase.SetFromObjectNames(IsCreateFromObjectNames()); + rTOXBase.SetSequenceName(GetSequenceName()); + rTOXBase.SetCaptionDisplay(GetCaptionDisplay()); + rTOXBase.SetFromChapter(IsFromChapter()); + rTOXBase.SetProtected(IsReadonly()); + rTOXBase.SetOLEOptions(GetOLEOptions()); + rTOXBase.SetLevelFromChapter(IsLevelFromChapter()); + rTOXBase.SetLanguage(m_eLanguage); + rTOXBase.SetSortAlgorithm(m_sSortAlgorithm); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/hhcwrp.cxx b/sw/source/uibase/lingu/hhcwrp.cxx new file mode 100644 index 000000000..168f6246a --- /dev/null +++ b/sw/source/uibase/lingu/hhcwrp.cxx @@ -0,0 +1,693 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <view.hxx> +#include <wrtsh.hxx> +#include <swundo.hxx> +#include <splargs.hxx> + +#include <editeng/langitem.hxx> +#include <editeng/fontitem.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/text/RubyAdjust.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <hhcwrp.hxx> +#include "sdrhhcwrap.hxx" +#include <doc.hxx> +#include <docsh.hxx> +#include <mdiexp.hxx> +#include <edtwin.hxx> +#include <index.hxx> +#include <pam.hxx> +#include <swcrsr.hxx> +#include <ndtxt.hxx> +#include <fmtruby.hxx> +#include <breakit.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::i18n; + +// Description: Turn off frame/object shell if applicable + +static void lcl_ActivateTextShell( SwWrtShell & rWrtSh ) +{ + if( rWrtSh.IsSelFrameMode() || rWrtSh.IsObjSelected() ) + rWrtSh.EnterStdMode(); +} + +namespace { + +class SwKeepConversionDirectionStateContext +{ +public: + SwKeepConversionDirectionStateContext() + { + //!! hack to transport the current conversion direction state settings + //!! into the next incarnation that iterates over the drawing objects + //!! ( see SwHHCWrapper::~SwHHCWrapper() ) + editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( true ); + } + + ~SwKeepConversionDirectionStateContext() + { + editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( false ); + } +}; + +} + +SwHHCWrapper::SwHHCWrapper( + SwView* pSwView, + const uno::Reference< uno::XComponentContext >& rxContext, + LanguageType nSourceLanguage, + LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, + sal_Int32 nConvOptions, + bool bIsInteractive, + bool bStart, bool bOther, bool bSelection ) + : editeng::HangulHanjaConversion(pSwView->GetEditWin().GetFrameWeld(), rxContext, + LanguageTag::convertToLocale( nSourceLanguage ), + LanguageTag::convertToLocale( nTargetLanguage ), + pTargetFont, + nConvOptions, + bIsInteractive ) + , m_pView( pSwView ) + , m_pWin( &pSwView->GetEditWin() ) + , m_rWrtShell( pSwView->GetWrtShell() ) + , m_nLastPos( 0 ) + , m_nUnitOffset( 0 ) + , m_nPageCount( 0 ) + , m_nPageStart( 0 ) + , m_bIsDrawObj( false ) + , m_bIsOtherContent( bOther ) + , m_bStartChk( bOther ) + , m_bIsSelection( bSelection ) + , m_bStartDone( bOther || bStart ) + , m_bEndDone( false ) +{ +} + +SwHHCWrapper::~SwHHCWrapper() +{ + m_pConvArgs.reset(); + + SwViewShell::SetCareDialog(nullptr); + + // check for existence of a draw view which means that there are + // (or previously were) draw objects present in the document. + // I.e. we like to check those too. + if ( m_bIsDrawObj /*&& bLastRet*/ && m_pView->GetWrtShell().HasDrawView() ) + { + vcl::Cursor *pSave = m_pView->GetWindow()->GetCursor(); + { + SwKeepConversionDirectionStateContext aContext; + + SdrHHCWrapper aSdrConvWrap( m_pView, GetSourceLanguage(), + GetTargetLanguage(), GetTargetFont(), + GetConversionOptions(), IsInteractive() ); + aSdrConvWrap.StartTextConversion(); + } + m_pView->GetWindow()->SetCursor( pSave ); + } + + if( m_nPageCount ) + ::EndProgress( m_pView->GetDocShell() ); + + // finally for chinese translation we need to change the documents + // default language and font to the new ones to be used. + LanguageType nTargetLang = GetTargetLanguage(); + if (IsChinese( nTargetLang )) + { + SwDoc *pDoc = m_pView->GetDocShell()->GetDoc(); + + //!! Note: This also effects the default language of text boxes (EditEngine/EditView) !! + pDoc->SetDefault( SvxLanguageItem( nTargetLang, RES_CHRATR_CJK_LANGUAGE ) ); + + const vcl::Font *pFont = GetTargetFont(); + if (pFont) + { + SvxFontItem aFontItem( pFont->GetFamilyType(), pFont->GetFamilyName(), + pFont->GetStyleName(), pFont->GetPitch(), + pFont->GetCharSet(), RES_CHRATR_CJK_FONT ); + pDoc->SetDefault( aFontItem ); + } + + } +} + +void SwHHCWrapper::GetNextPortion( + OUString& rNextPortion, + LanguageType& rLangOfPortion, + bool bAllowChanges ) +{ + m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = bAllowChanges; + + FindConvText_impl(); + rNextPortion = m_pConvArgs->aConvText; + rLangOfPortion = m_pConvArgs->nConvTextLang; + + m_nUnitOffset = 0; + + // build last pos from currently selected text + SwPaM* pCursor = m_rWrtShell.GetCursor(); + m_nLastPos = pCursor->Start()->nContent.GetIndex(); +} + +void SwHHCWrapper::SelectNewUnit_impl( sal_Int32 nUnitStart, sal_Int32 nUnitEnd ) +{ + SwPaM *pCursor = m_rWrtShell.GetCursor(); + pCursor->GetPoint()->nContent = m_nLastPos; + pCursor->DeleteMark(); + + m_rWrtShell.Right( CRSR_SKIP_CHARS, /*bExpand*/ false, + static_cast<sal_uInt16>(m_nUnitOffset + nUnitStart), true ); + pCursor->SetMark(); + m_rWrtShell.Right( CRSR_SKIP_CHARS, /*bExpand*/ true, + static_cast<sal_uInt16>(nUnitEnd - nUnitStart), true ); + // end selection now. Otherwise SHIFT+HOME (extending the selection) + // won't work when the dialog is closed without any replacement. + // (see #116346#) + m_rWrtShell.EndSelect(); +} + +void SwHHCWrapper::HandleNewUnit( + const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd ) +{ + OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" ); + if (!(0 <= nUnitStart && nUnitStart <= nUnitEnd)) + return; + + lcl_ActivateTextShell( m_rWrtShell ); + + m_rWrtShell.StartAllAction(); + + // select current unit + SelectNewUnit_impl( nUnitStart, nUnitEnd ); + + m_rWrtShell.EndAllAction(); +} + +void SwHHCWrapper::ChangeText( const OUString &rNewText, + const OUString& rOrigText, + const uno::Sequence< sal_Int32 > *pOffsets, + SwPaM *pCursor ) +{ + //!! please see also TextConvWrapper::ChangeText with is a modified + //!! copy of this code + + OSL_ENSURE( !rNewText.isEmpty(), "unexpected empty string" ); + if (rNewText.isEmpty()) + return; + + if (pOffsets && pCursor) // try to keep as much attributation as possible ? + { + // remember cursor start position for later setting of the cursor + const SwPosition *pStart = pCursor->Start(); + const sal_Int32 nStartIndex = pStart->nContent.GetIndex(); + const SwNodeIndex aStartNodeIndex = pStart->nNode; + SwTextNode *pStartTextNode = aStartNodeIndex.GetNode().GetTextNode(); + + const sal_Int32 nIndices = pOffsets->getLength(); + const sal_Int32 *pIndices = pOffsets->getConstArray(); + sal_Int32 nConvTextLen = rNewText.getLength(); + sal_Int32 nPos = 0; + sal_Int32 nChgPos = -1; + sal_Int32 nChgLen = 0; + sal_Int32 nConvChgPos = -1; + sal_Int32 nConvChgLen = 0; + + // offset to calculate the position in the text taking into + // account that text may have been replaced with new text of + // different length. Negative values allowed! + long nCorrectionOffset = 0; + + OSL_ENSURE(nIndices == 0 || nIndices == nConvTextLen, + "mismatch between string length and sequence length!" ); + + // find all substrings that need to be replaced (and only those) + while (true) + { + // get index in original text that matches nPos in new text + sal_Int32 nIndex; + if (nPos < nConvTextLen) + nIndex = nPos < nIndices ? pIndices[nPos] : nPos; + else + { + nPos = nConvTextLen; + nIndex = rOrigText.getLength(); + } + + if (nPos == nConvTextLen || /* end of string also terminates non-matching char sequence */ + rOrigText[nIndex] == rNewText[nPos]) + { + // substring that needs to be replaced found? + if (nChgPos != -1 && nConvChgPos != -1) + { + nChgLen = nIndex - nChgPos; + nConvChgLen = nPos - nConvChgPos; + OUString aInNew( rNewText.copy( nConvChgPos, nConvChgLen ) ); + + // set selection to sub string to be replaced in original text + sal_Int32 nChgInNodeStartIndex = nStartIndex + nCorrectionOffset + nChgPos; + OSL_ENSURE( m_rWrtShell.GetCursor()->HasMark(), "cursor misplaced (nothing selected)" ); + m_rWrtShell.GetCursor()->GetMark()->nContent.Assign( pStartTextNode, nChgInNodeStartIndex ); + m_rWrtShell.GetCursor()->GetPoint()->nContent.Assign( pStartTextNode, nChgInNodeStartIndex + nChgLen ); + + // replace selected sub string with the corresponding + // sub string from the new text while keeping as + // much from the attributes as possible + ChangeText_impl( aInNew, true ); + + nCorrectionOffset += nConvChgLen - nChgLen; + + nChgPos = -1; + nConvChgPos = -1; + } + } + else + { + // begin of non-matching char sequence found ? + if (nChgPos == -1 && nConvChgPos == -1) + { + nChgPos = nIndex; + nConvChgPos = nPos; + } + } + if (nPos >= nConvTextLen) + break; + ++nPos; + } + + // set cursor to the end of all the new text + // (as it would happen after ChangeText_impl (Delete and Insert) + // of the whole text in the 'else' branch below) + m_rWrtShell.ClearMark(); + m_rWrtShell.GetCursor()->Start()->nContent.Assign( pStartTextNode, nStartIndex + nConvTextLen ); + } + else + { + ChangeText_impl( rNewText, false ); + } +} + +void SwHHCWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes ) +{ + if (bKeepAttributes) + { + // get item set with all relevant attributes + sal_uInt16 const aRanges[] { + RES_CHRATR_BEGIN, RES_FRMATR_END, + 0, 0, 0 }; + SfxItemSet aItemSet( m_rWrtShell.GetAttrPool(), aRanges ); + // get all attributes spanning the whole selection in order to + // restore those for the new text + m_rWrtShell.GetCurAttr( aItemSet ); + + m_rWrtShell.Delete(); + m_rWrtShell.Insert( rNewText ); + + // select new inserted text (currently the Point is right after the new text) + if (!m_rWrtShell.GetCursor()->HasMark()) + m_rWrtShell.GetCursor()->SetMark(); + SwPosition *pMark = m_rWrtShell.GetCursor()->GetMark(); + pMark->nContent = pMark->nContent.GetIndex() - rNewText.getLength(); + + // since 'SetAttr' below functions like merging with the attributes + // from the itemset with any existing ones we have to get rid of all + // all attributes now. (Those attributes that may take effect left + // to the position where the new text gets inserted after the old text + // was deleted) + m_rWrtShell.ResetAttr(); + // apply previously saved attributes to new text + m_rWrtShell.SetAttrSet( aItemSet ); + } + else + { + m_rWrtShell.Delete(); + m_rWrtShell.Insert( rNewText ); + } +} + +void SwHHCWrapper::ReplaceUnit( + const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd, + const OUString& rOrigText, + const OUString& rReplaceWith, + const uno::Sequence< sal_Int32 > &rOffsets, + ReplacementAction eAction, + LanguageType *pNewUnitLanguage ) +{ + OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" ); + if (!(nUnitStart >= 0 && nUnitEnd >= nUnitStart)) + return; + + lcl_ActivateTextShell( m_rWrtShell ); + + // replace the current word + m_rWrtShell.StartAllAction(); + + // select current unit + SelectNewUnit_impl( nUnitStart, nUnitEnd ); + + OUString aOrigText( m_rWrtShell.GetSelText() ); + OUString aNewText( rReplaceWith ); + OSL_ENSURE( aOrigText == rOrigText, "!! text mismatch !!" ); + std::unique_ptr<SwFormatRuby> pRuby; + bool bRubyBelow = false; + OUString aNewOrigText; + switch (eAction) + { + case eExchange : + break; + case eReplacementBracketed : + { + aNewText = aOrigText + "(" + rReplaceWith + ")"; + } + break; + case eOriginalBracketed : + { + aNewText = rReplaceWith + "(" + aOrigText + ")"; + } + break; + case eReplacementAbove : + { + pRuby.reset(new SwFormatRuby( rReplaceWith )); + } + break; + case eOriginalAbove : + { + pRuby.reset(new SwFormatRuby( aOrigText )); + aNewOrigText = rReplaceWith; + } + break; + case eReplacementBelow : + { + pRuby.reset(new SwFormatRuby( rReplaceWith )); + bRubyBelow = true; + } + break; + case eOriginalBelow : + { + pRuby.reset(new SwFormatRuby( aOrigText )); + aNewOrigText = rReplaceWith; + bRubyBelow = true; + } + break; + default: + OSL_FAIL("unexpected case" ); + } + m_nUnitOffset += nUnitStart + aNewText.getLength(); + + if (pRuby) + { + m_rWrtShell.StartUndo( SwUndoId::SETRUBYATTR ); + if (!aNewOrigText.isEmpty()) + { + // according to FT we currently should not bother about keeping + // attributes in Hangul/Hanja conversion + ChangeText( aNewOrigText, rOrigText, nullptr, nullptr ); + + //!! since Delete, Insert in 'ChangeText' do not set the WrtShells + //!! bInSelect flag + //!! back to false we do it now manually in order for the selection + //!! to be done properly in the following call to Left. + // We didn't fix it in Delete and Insert since it is currently + // unclear if someone depends on this incorrect behaviour + // of the flag. + m_rWrtShell.EndSelect(); + + m_rWrtShell.Left( 0, true, aNewOrigText.getLength(), true, true ); + } + + pRuby->SetPosition( static_cast<sal_uInt16>(bRubyBelow) ); + pRuby->SetAdjustment( RubyAdjust_CENTER ); + + m_rWrtShell.SetAttrItem(*pRuby); + pRuby.reset(); + m_rWrtShell.EndUndo( SwUndoId::SETRUBYATTR ); + } + else + { + m_rWrtShell.StartUndo( SwUndoId::OVERWRITE ); + + // according to FT we should currently not bother about keeping + // attributes in Hangul/Hanja conversion and leave that untouched. + // Thus we do this only for Chinese translation... + const bool bIsChineseConversion = IsChinese( GetSourceLanguage() ); + if (bIsChineseConversion) + ChangeText( aNewText, rOrigText, &rOffsets, m_rWrtShell.GetCursor() ); + else + ChangeText( aNewText, rOrigText, nullptr, nullptr ); + + // change language and font if necessary + if (bIsChineseConversion) + { + m_rWrtShell.SetMark(); + m_rWrtShell.GetCursor()->GetMark()->nContent -= aNewText.getLength(); + + OSL_ENSURE( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED || GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL, + "SwHHCWrapper::ReplaceUnit : unexpected target language" ); + + sal_uInt16 const aRanges[] { + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + 0, 0, 0 }; + + SfxItemSet aSet( m_rWrtShell.GetAttrPool(), aRanges ); + if (pNewUnitLanguage) + { + aSet.Put( SvxLanguageItem( *pNewUnitLanguage, RES_CHRATR_CJK_LANGUAGE ) ); + } + + const vcl::Font *pTargetFont = GetTargetFont(); + OSL_ENSURE( pTargetFont, "target font missing?" ); + if (pTargetFont && pNewUnitLanguage) + { + SvxFontItem aFontItem( aSet.Get( RES_CHRATR_CJK_FONT ) ); + aFontItem.SetFamilyName( pTargetFont->GetFamilyName()); + aFontItem.SetFamily( pTargetFont->GetFamilyType()); + aFontItem.SetStyleName( pTargetFont->GetStyleName()); + aFontItem.SetPitch( pTargetFont->GetPitch()); + aFontItem.SetCharSet( pTargetFont->GetCharSet() ); + aSet.Put( aFontItem ); + } + + m_rWrtShell.SetAttrSet( aSet ); + + m_rWrtShell.ClearMark(); + } + + m_rWrtShell.EndUndo( SwUndoId::OVERWRITE ); + } + + m_rWrtShell.EndAllAction(); +} + +bool SwHHCWrapper::HasRubySupport() const +{ + return true; +} + +void SwHHCWrapper::Convert() +{ + OSL_ENSURE( m_pConvArgs == nullptr, "NULL pointer expected" ); + { + SwPaM *pCursor = m_pView->GetWrtShell().GetCursor(); + SwPosition* pSttPos = pCursor->Start(); + SwPosition* pEndPos = pCursor->End(); + + if (pSttPos->nNode.GetNode().IsTextNode() && + pEndPos->nNode.GetNode().IsTextNode()) + { + m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), + pSttPos->nNode.GetNode().GetTextNode(), pSttPos->nContent, + pEndPos->nNode.GetNode().GetTextNode(), pEndPos->nContent ) ); + } + else // we are not in the text (maybe a graphic or OLE object is selected) let's start from the top + { + // get PaM that points to the start of the document + SwNode& rNode = m_pView->GetDocShell()->GetDoc()->GetNodes().GetEndOfContent(); + SwPaM aPam(rNode); + aPam.Move( fnMoveBackward, GoInDoc ); // move to start of document + + pSttPos = aPam.GetPoint(); //! using a PaM here makes sure we will get only text nodes + SwTextNode *pTextNode = pSttPos->nNode.GetNode().GetTextNode(); + // just in case we check anyway... + if (!pTextNode || !pTextNode->IsTextNode()) + return; + m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), + pTextNode, pSttPos->nContent, + pTextNode, pSttPos->nContent ) ); + } + OSL_ENSURE( m_pConvArgs->pStartNode && m_pConvArgs->pStartNode->IsTextNode(), + "failed to get proper start text node" ); + OSL_ENSURE( m_pConvArgs->pEndNode && m_pConvArgs->pEndNode->IsTextNode(), + "failed to get proper end text node" ); + + // chinese conversion specific settings + OSL_ENSURE( IsChinese( GetSourceLanguage() ) == IsChinese( GetTargetLanguage() ), + "source and target language mismatch?" ); + if (IsChinese( GetTargetLanguage() )) + { + m_pConvArgs->nConvTargetLang = GetTargetLanguage(); + m_pConvArgs->pTargetFont = GetTargetFont(); + m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = true; + } + + // if it is not just a selection and we are about to begin + // with the current conversion for the very first time + // we need to find the start of the current (initial) + // convertible unit in order for the text conversion to give + // the correct result for that. Since it is easier to obtain + // the start of the word we use that though. + if (!pCursor->HasMark()) // is not a selection? + { + // since #118246 / #117803 still occurs if the cursor is placed + // between the two chinese characters to be converted (because both + // of them are words on their own!) using the word boundary here does + // not work. Thus since chinese conversion is not interactive we start + // at the begin of the paragraph to solve the problem, i.e. have the + // TextConversion service get those characters together in the same call. + sal_Int32 nStartIdx = -1; + if (editeng::HangulHanjaConversion::IsChinese( GetSourceLanguage() ) ) + nStartIdx = 0; + else + { + OUString aText( m_pConvArgs->pStartNode->GetText() ); + const sal_Int32 nPos = m_pConvArgs->pStartIdx->GetIndex(); + Boundary aBoundary( g_pBreakIt->GetBreakIter()-> + getWordBoundary( aText, nPos, g_pBreakIt->GetLocale( m_pConvArgs->nConvSrcLang ), + WordType::DICTIONARY_WORD, true ) ); + + // valid result found? + if (aBoundary.startPos < aText.getLength() && + aBoundary.startPos != aBoundary.endPos) + { + nStartIdx = aBoundary.startPos; + } + } + + if (nStartIdx != -1) + *m_pConvArgs->pStartIdx = nStartIdx; + } + } + + if ( m_bIsOtherContent ) + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other ); + else + { + m_bStartChk = false; + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::BodyEnd ); + } + + ConvertDocument(); + + ConvEnd_impl( m_pConvArgs.get() ); +} + +bool SwHHCWrapper::ConvNext_impl( ) +{ + //! modified version of SvxSpellWrapper::SpellNext + + // no change of direction so the desired region is fully processed + if( m_bStartChk ) + m_bStartDone = true; + else + m_bEndDone = true; + + if( m_bIsOtherContent && m_bStartDone && m_bEndDone ) // document completely checked? + { + return false; + } + + bool bGoOn = false; + + if ( m_bIsOtherContent ) + { + m_bStartChk = false; + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Body ); + bGoOn = true; + } + else if ( m_bStartDone && m_bEndDone ) + { + // body region done, ask about special region + if( !m_bIsSelection && m_rWrtShell.HasOtherCnt() ) + { + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other ); + m_bIsOtherContent = bGoOn = true; + } + } + else + { + m_bStartChk = !m_bStartDone; + ConvStart_impl( m_pConvArgs.get(), m_bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd ); + bGoOn = true; + } + return bGoOn; +} + +void SwHHCWrapper::FindConvText_impl() +{ + //! modified version of SvxSpellWrapper::FindSpellError + + bool bFound = false; + + m_pWin->EnterWait(); + bool bConv = true; + + while ( bConv ) + { + bFound = ConvContinue_impl( m_pConvArgs.get() ); + if (bFound) + { + bConv = false; + } + else + { + ConvEnd_impl( m_pConvArgs.get() ); + bConv = ConvNext_impl(); + } + } + m_pWin->LeaveWait(); +} + +void SwHHCWrapper::ConvStart_impl( SwConversionArgs /* [out] */ *pConversionArgs, SvxSpellArea eArea ) +{ + m_bIsDrawObj = SvxSpellArea::Other == eArea; + m_pView->SpellStart( eArea, m_bStartDone, m_bEndDone, /* [out] */ pConversionArgs ); +} + +void SwHHCWrapper::ConvEnd_impl( SwConversionArgs const *pConversionArgs ) +{ + m_pView->SpellEnd( pConversionArgs ); +} + +bool SwHHCWrapper::ConvContinue_impl( SwConversionArgs *pConversionArgs ) +{ + bool bProgress = !m_bIsDrawObj && !m_bIsSelection; + pConversionArgs->aConvText.clear(); + pConversionArgs->nConvTextLang = LANGUAGE_NONE; + m_pView->GetWrtShell().SpellContinue( &m_nPageCount, bProgress ? &m_nPageStart : nullptr, pConversionArgs ); + return !pConversionArgs->aConvText.isEmpty(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/hyp.cxx b/sw/source/uibase/lingu/hyp.cxx new file mode 100644 index 000000000..d7d0c87a5 --- /dev/null +++ b/sw/source/uibase/lingu/hyp.cxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <edtwin.hxx> +#include <wrtsh.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/linguistic2/XLinguProperties.hpp> +#include <swwait.hxx> + +#include <hyp.hxx> +#include <mdiexp.hxx> +#include <strings.hrc> + +#include <memory> + +#define PSH (&pView->GetWrtShell()) + +using namespace ::com::sun::star; + +// interactive separation +SwHyphWrapper::SwHyphWrapper( SwView* pVw, + uno::Reference< linguistic2::XHyphenator > const &rxHyph, + bool bStart, bool bOther, bool bSelect ) : + SvxSpellWrapper( pVw->GetEditWin().GetFrameWeld(), rxHyph, bStart, bOther ), + pView( pVw ), + nPageCount( 0 ), + nPageStart( 0 ), + bInSelection( bSelect ), + bInfoBox( false ) +{ + uno::Reference< linguistic2::XLinguProperties > xProp( GetLinguPropertySet() ); + bAutomatic = xProp.is() && xProp->getIsHyphAuto(); +} + +void SwHyphWrapper::SpellStart( SvxSpellArea eSpell ) +{ + if( SvxSpellArea::Other == eSpell && nPageCount ) + { + ::EndProgress( pView->GetDocShell() ); + nPageCount = 0; + nPageStart = 0; + } + pView->HyphStart( eSpell ); +} + +void SwHyphWrapper::SpellContinue() +{ + // for automatic separation, make actions visible only at the end + std::unique_ptr<SwWait> pWait; + if( bAutomatic ) + { + PSH->StartAllAction(); + pWait.reset(new SwWait( *pView->GetDocShell(), true )); + } + + uno::Reference< uno::XInterface > xHyphWord = bInSelection ? + PSH->HyphContinue( nullptr, nullptr ) : + PSH->HyphContinue( &nPageCount, &nPageStart ); + SetLast( xHyphWord ); + + // for automatic separation, make actions visible only at the end + if( bAutomatic ) + { + PSH->EndAllAction(); + pWait.reset(); + } +} + +void SwHyphWrapper::SpellEnd() +{ + PSH->HyphEnd(); + SvxSpellWrapper::SpellEnd(); +} + +bool SwHyphWrapper::SpellMore() +{ + PSH->Push(); + bInfoBox = true; + PSH->Combine(); + return false; +} + +void SwHyphWrapper::InsertHyphen( const sal_Int32 nPos ) +{ + if( nPos) + SwEditShell::InsertSoftHyph(nPos + 1); // does nPos == 1 really mean + // insert hyphen after first char? + // (instead of nPos == 0) + else + PSH->HyphIgnore(); +} + +SwHyphWrapper::~SwHyphWrapper() +{ + if( nPageCount ) + ::EndProgress( pView->GetDocShell() ); + if( bInfoBox && !Application::IsHeadlessModeEnabled() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pView->GetEditWin().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_HYP_OK))); + xInfoBox->run(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/olmenu.cxx b/sw/source/uibase/lingu/olmenu.cxx new file mode 100644 index 000000000..27d4b19ff --- /dev/null +++ b/sw/source/uibase/lingu/olmenu.cxx @@ -0,0 +1,885 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <SwRewriter.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <doc.hxx> +#include <edtwin.hxx> +#include <helpids.h> +#include <langhelper.hxx> +#include <bitmaps.hlst> +#include <olmenu.hxx> +#include <swmodule.hxx> +#include <swtypes.hxx> +#include <swundo.hxx> +#include <view.hxx> +#include <wrtsh.hxx> + +#include <comphelper/lok.hxx> +#include <comphelper/anytostring.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <editeng/acorrcfg.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/splwrap.hxx> +#include <editeng/unolingu.hxx> +#include <editeng/editview.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <linguistic/misc.hxx> +#include <rtl/string.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/itemset.hxx> +#include <svl/languageoptions.hxx> +#include <svl/stritem.hxx> +#include <svtools/langtab.hxx> +#include <unotools/lingucfg.hxx> +#include <unotools/linguprops.hxx> +#include <vcl/settings.hxx> + +#include <map> + +#include <com/sun/star/document/XDocumentLanguages.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/linguistic2/XLanguageGuessing.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> + +using namespace ::com::sun::star; + +/// @returns : the language for the selected text that is set for the +/// specified attribute (script type). +/// If there are more than one languages used LANGUAGE_DONTKNOW will be returned. +/// @param nLangWhichId : one of +/// RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, +/// @returns: the language in use for the selected text. +/// 'In use' means the language(s) matching the script type(s) of the +/// selected text. Or in other words, the language a spell checker would use. +/// If there is more than one language LANGUAGE_DONTKNOW will be returned. +// check if nScriptType includes the script type associated to nLang +static bool lcl_checkScriptType( SvtScriptType nScriptType, LanguageType nLang ) +{ + return bool(nScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage( nLang )); +} + +void SwSpellPopup::fillLangPopupMenu( + PopupMenu *pPopupMenu, + sal_uInt16 nLangItemIdStart, + const uno::Sequence< OUString >& aSeq, + SwWrtShell* pWrtSh, + std::map< sal_Int16, OUString > &rLangTable ) +{ + if (!pPopupMenu) + return; + + // set of languages to be displayed in the sub menus + std::set< OUString > aLangItems; + + OUString aCurLang( aSeq[0] ); + SvtScriptType nScriptType = static_cast<SvtScriptType>(aSeq[1].toInt32()); + OUString aKeyboardLang( aSeq[2] ); + OUString aGuessedTextLang( aSeq[3] ); + + if (!aCurLang.isEmpty() && + LANGUAGE_DONTKNOW != SvtLanguageTable::GetLanguageType( aCurLang )) + aLangItems.insert( aCurLang ); + + //2--System + const AllSettings& rAllSettings = Application::GetSettings(); + LanguageType rSystemLanguage = rAllSettings.GetLanguageTag().getLanguageType(); + if (rSystemLanguage != LANGUAGE_DONTKNOW) + { + if (lcl_checkScriptType( nScriptType, rSystemLanguage )) + aLangItems.insert( SvtLanguageTable::GetLanguageString(rSystemLanguage) ); + } + + //3--UI + LanguageType rUILanguage = rAllSettings.GetUILanguageTag().getLanguageType(); + if (rUILanguage != LANGUAGE_DONTKNOW) + { + if (lcl_checkScriptType(nScriptType, rUILanguage )) + aLangItems.insert( SvtLanguageTable::GetLanguageString(rUILanguage) ); + } + + //4--guessed language + if (!aGuessedTextLang.isEmpty()) + { + if (lcl_checkScriptType(nScriptType, SvtLanguageTable::GetLanguageType(aGuessedTextLang))) + aLangItems.insert( aGuessedTextLang ); + } + + //5--keyboard language + if (!aKeyboardLang.isEmpty()) + { + if (lcl_checkScriptType(nScriptType, SvtLanguageTable::GetLanguageType(aKeyboardLang))) + aLangItems.insert( aKeyboardLang ); + } + + //6--all languages used in current document + uno::Reference< css::frame::XModel > xModel; + uno::Reference< css::frame::XController > xController = pWrtSh->GetView().GetViewFrame()->GetFrame().GetFrameInterface()->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + uno::Reference< document::XDocumentLanguages > xDocumentLanguages( xModel, uno::UNO_QUERY ); + /*the description of nScriptType flags + LATIN : 0x0001 + ASIAN : 0x0002 + COMPLEX: 0x0004 + */ + const sal_Int16 nMaxCount = 7; + if (xDocumentLanguages.is()) + { + const uno::Sequence< lang::Locale > rLocales( xDocumentLanguages->getDocumentLanguages( static_cast<sal_Int16>(nScriptType), nMaxCount ) ); + for (const lang::Locale& rLocale : rLocales) + { + if (aLangItems.size() == size_t(nMaxCount)) + break; + if (lcl_checkScriptType( nScriptType, SvtLanguageTable::GetLanguageType( rLocale.Language ))) + aLangItems.insert( rLocale.Language ); + } + } + + sal_uInt16 nItemId = nLangItemIdStart; + for (const OUString& aEntryText : aLangItems) + { + if (aEntryText != SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ) && + aEntryText != "*" && // multiple languages in current selection + !aEntryText.isEmpty()) // 'no language found' from language guessing + { + OSL_ENSURE( nLangItemIdStart <= nItemId && nItemId <= nLangItemIdStart + MN_MAX_NUM_LANG, + "nItemId outside of expected range!" ); + pPopupMenu->InsertItem( nItemId, aEntryText, MenuItemBits::RADIOCHECK ); + if (aEntryText == aCurLang) + { + //make a check mark for the current language + pPopupMenu->CheckItem( nItemId ); + } + rLangTable[ nItemId ] = aEntryText; + ++nItemId; + } + } + + pPopupMenu->InsertItem( nLangItemIdStart + MN_NONE_OFFSET, SwResId( STR_LANGSTATUS_NONE ), MenuItemBits::RADIOCHECK ); + if ( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ) == aCurLang ) + pPopupMenu->CheckItem( nLangItemIdStart + MN_NONE_OFFSET ); + + pPopupMenu->InsertItem( nLangItemIdStart + MN_RESET_OFFSET, SwResId( STR_RESET_TO_DEFAULT_LANGUAGE ) ); + pPopupMenu->InsertItem( nLangItemIdStart + MN_MORE_OFFSET, SwResId( STR_LANGSTATUS_MORE ) ); +} + +SwSpellPopup::SwSpellPopup( + SwWrtShell* pWrtSh, + const uno::Reference< linguistic2::XSpellAlternatives > &xAlt, + const OUString &rParaText) + : m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/spellmenu.ui", "") + , m_xPopupMenu(m_aBuilder.get_menu("menu")) + , m_nIgnoreWordId(m_xPopupMenu->GetItemId("ignoreall")) + , m_nAddMenuId(m_xPopupMenu->GetItemId("addmenu")) + , m_nAddId(m_xPopupMenu->GetItemId("add")) + , m_nSpellDialogId(m_xPopupMenu->GetItemId("spelldialog")) + , m_nCorrectMenuId(m_xPopupMenu->GetItemId("correctmenu")) + , m_nCorrectDialogId(m_xPopupMenu->GetItemId("correctdialog")) + , m_nLangSelectionMenuId(m_xPopupMenu->GetItemId("langselection")) + , m_nLangParaMenuId(m_xPopupMenu->GetItemId("langpara")) + , m_nRedlineAcceptId(m_xPopupMenu->GetItemId("accept")) + , m_nRedlineRejectId(m_xPopupMenu->GetItemId("reject")) + , m_nRedlineNextId(m_xPopupMenu->GetItemId("next")) + , m_nRedlinePrevId(m_xPopupMenu->GetItemId("prev")) + , m_pSh( pWrtSh ) + , m_xSpellAlt(xAlt) + , m_bGrammarResults(false) +{ + OSL_ENSURE(m_xSpellAlt.is(), "no spelling alternatives available"); + + m_xPopupMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + bool bUseImagesInMenus = Application::GetSettings().GetStyleSettings().GetUseImagesInMenus(); + + m_nCheckedLanguage = LANGUAGE_NONE; + css::uno::Sequence< OUString > aSuggestions; + if (m_xSpellAlt.is()) + { + m_nCheckedLanguage = LanguageTag( m_xSpellAlt->getLocale() ).getLanguageType(); + aSuggestions = m_xSpellAlt->getAlternatives(); + } + sal_Int16 nStringCount = static_cast< sal_Int16 >( aSuggestions.getLength() ); + + SvtLinguConfig aCfg; + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nCorrectMenuId); + pMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + bool bEnable = false; + if( nStringCount ) + { + Image aImage; + OUString aSuggestionImageUrl; + + if (bUseImagesInMenus) + { + uno::Reference< container::XNamed > xNamed( m_xSpellAlt, uno::UNO_QUERY ); + if (xNamed.is()) + { + aSuggestionImageUrl = aCfg.GetSpellAndGrammarContextSuggestionImage( xNamed->getName() ); + aImage = Image( aSuggestionImageUrl ); + } + } + + m_xPopupMenu->InsertSeparator(OString(), 0); + bEnable = true; + sal_uInt16 nAutoCorrItemId = MN_AUTOCORR_START; + sal_uInt16 nItemId = MN_SUGGESTION_START; + for (sal_uInt16 i = 0; i < nStringCount; ++i) + { + const OUString aEntry = aSuggestions[ i ]; + m_xPopupMenu->InsertItem(nItemId, aEntry, MenuItemBits::NONE, OString(), i); + m_xPopupMenu->SetHelpId(nItemId, HID_LINGU_REPLACE); + if (!aSuggestionImageUrl.isEmpty()) + m_xPopupMenu->SetItemImage(nItemId, aImage); + + pMenu->InsertItem( nAutoCorrItemId, aEntry ); + pMenu->SetHelpId( nAutoCorrItemId, HID_LINGU_AUTOCORR); + + ++nAutoCorrItemId; + ++nItemId; + } + } + + uno::Reference< frame::XFrame > xFrame = pWrtSh->GetView().GetViewFrame()->GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:SpellingAndGrammarDialog", aModuleName); + m_xPopupMenu->SetItemText(m_nSpellDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + } + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:AutoCorrectDlg", aModuleName); + m_xPopupMenu->SetItemText(m_nCorrectDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + } + + sal_uInt16 nItemPos = m_xPopupMenu->GetItemPos(m_nIgnoreWordId); + OUString aIgnoreSelection( SwResId( STR_IGNORE_SELECTION ) ); + m_xPopupMenu->InsertItem(MN_IGNORE_SELECTION, aIgnoreSelection, MenuItemBits::NONE, OString(), nItemPos); + m_xPopupMenu->SetHelpId(MN_IGNORE_SELECTION, HID_LINGU_IGNORE_SELECTION); + + m_xPopupMenu->EnableItem(m_nCorrectMenuId, bEnable); + + uno::Reference< linguistic2::XLanguageGuessing > xLG = SW_MOD()->GetLanguageGuesser(); + LanguageType nGuessLangWord = LANGUAGE_NONE; + LanguageType nGuessLangPara = LANGUAGE_NONE; + if (m_xSpellAlt.is() && xLG.is()) + { + nGuessLangWord = EditView::CheckLanguage( m_xSpellAlt->getWord(), ::GetSpellChecker(), xLG, false ); + nGuessLangPara = EditView::CheckLanguage( rParaText, ::GetSpellChecker(), xLG, true ); + } + if (nGuessLangWord != LANGUAGE_NONE || nGuessLangPara != LANGUAGE_NONE) + { + // make sure LANGUAGE_NONE gets not used as menu entry + if (nGuessLangWord == LANGUAGE_NONE) + nGuessLangWord = nGuessLangPara; + if (nGuessLangPara == LANGUAGE_NONE) + nGuessLangPara = nGuessLangWord; + } + + pMenu = m_xPopupMenu->GetPopupMenu(m_nAddMenuId); + pMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); //! necessary to retrieve the correct dictionary name in 'Execute' below + uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + sal_uInt16 nItemId = MN_DICTIONARIES_START; + if (xDicList.is()) + { + // add the default positive dictionary to dic-list (if not already done). + // This is to ensure that there is at least one dictionary to which + // words could be added. + uno::Reference< linguistic2::XDictionary > xDic( LinguMgr::GetStandardDic() ); + if (xDic.is()) + xDic->setActive( true ); + + m_aDics = xDicList->getDictionaries(); + + for( const uno::Reference< linguistic2::XDictionary >& rDic : std::as_const(m_aDics) ) + { + uno::Reference< linguistic2::XDictionary > xDicTmp = rDic; + if (!xDicTmp.is() || LinguMgr::GetIgnoreAllList() == xDicTmp) + continue; + + uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY ); + LanguageType nActLanguage = LanguageTag( xDicTmp->getLocale() ).getLanguageType(); + if( xDicTmp->isActive() + && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE + && (m_nCheckedLanguage == nActLanguage || LANGUAGE_NONE == nActLanguage ) + && (!xStor.is() || !xStor->isReadonly()) ) + { + // the extra 1 is because of the (possible) external + // linguistic entry above + pMenu->InsertItem( nItemId, xDicTmp->getName() ); + m_aDicNameSingle = xDicTmp->getName(); + + if (bUseImagesInMenus) + { + uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY ); + if (xSvcInfo.is()) + { + OUString aDictionaryImageUrl( aCfg.GetSpellAndGrammarContextDictionaryImage( + xSvcInfo->getImplementationName() ) ); + if (!aDictionaryImageUrl.isEmpty()) + { + Image aImage( aDictionaryImageUrl ); + pMenu->SetItemImage( nItemId, aImage ); + } + } + } + + ++nItemId; + } + } + } + m_xPopupMenu->EnableItem(m_nAddMenuId, (nItemId - MN_DICTIONARIES_START) > 1); + m_xPopupMenu->EnableItem(m_nAddId, (nItemId - MN_DICTIONARIES_START) == 1); + + //ADD NEW LANGUAGE MENU ITEM + + OUString aScriptTypesInUse( OUString::number( static_cast<int>(pWrtSh->GetScriptType()) ) ); + + // get keyboard language + OUString aKeyboardLang; + SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin(); + LanguageType nLang = rEditWin.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + nLang = SwLangHelper::GetCurrentLanguage( *pWrtSh ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq( 4 ); + aSeq[0] = aCurrentLang; + aSeq[1] = aScriptTypesInUse; + aSeq[2] = aKeyboardLang; + aSeq[3] = SvtLanguageTable::GetLanguageString(nGuessLangWord); + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_SELECTION_START, aSeq, pWrtSh, m_aLangTable_Text ); + m_xPopupMenu->EnableItem(m_nLangSelectionMenuId); + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_PARAGRAPH_START, aSeq, pWrtSh, m_aLangTable_Paragraph ); + m_xPopupMenu->EnableItem(m_nLangParaMenuId); + + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(m_nSpellDialogId, + vcl::CommandInfoProvider::GetImageForCommand(".uno:SpellingAndGrammarDialog", xFrame)); + + checkRedline(); + m_xPopupMenu->RemoveDisabledEntries( true, true ); + + InitItemCommands(aSuggestions); +} + +SwSpellPopup::SwSpellPopup( + SwWrtShell *pWrtSh, + const linguistic2::ProofreadingResult &rResult, + sal_Int32 nErrorInResult, + const uno::Sequence< OUString > &rSuggestions, + const OUString &rParaText ) + : m_aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/spellmenu.ui", "") + , m_xPopupMenu(m_aBuilder.get_menu("menu")) + , m_nIgnoreWordId(m_xPopupMenu->GetItemId("ignoreall")) + , m_nAddMenuId(m_xPopupMenu->GetItemId("addmenu")) + , m_nAddId(m_xPopupMenu->GetItemId("add")) + , m_nSpellDialogId(m_xPopupMenu->GetItemId("spelldialog")) + , m_nCorrectMenuId(m_xPopupMenu->GetItemId("correctmenu")) + , m_nCorrectDialogId(m_xPopupMenu->GetItemId("correctdialog")) + , m_nLangSelectionMenuId(m_xPopupMenu->GetItemId("langselection")) + , m_nLangParaMenuId(m_xPopupMenu->GetItemId("langpara")) + , m_nRedlineAcceptId(m_xPopupMenu->GetItemId("accept")) + , m_nRedlineRejectId(m_xPopupMenu->GetItemId("reject")) + , m_nRedlineNextId(m_xPopupMenu->GetItemId("next")) + , m_nRedlinePrevId(m_xPopupMenu->GetItemId("prev")) + , m_pSh(pWrtSh) + , m_sExplanationLink() + , m_bGrammarResults(true) +{ + m_nCheckedLanguage = LanguageTag::convertToLanguageType( rResult.aLocale ); + bool bUseImagesInMenus = Application::GetSettings().GetStyleSettings().GetUseImagesInMenus(); + + sal_uInt16 nPos = 0; + OUString aMessageText( rResult.aErrors[ nErrorInResult ].aShortComment ); + m_xPopupMenu->InsertSeparator(OString(), nPos++); + m_xPopupMenu->InsertItem(MN_SHORT_COMMENT, aMessageText, MenuItemBits::NOSELECT, OString(), nPos++); + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(MN_SHORT_COMMENT, Image(StockImage::Yes, BMP_INFO_16)); + + // Add an item to show detailed infos if the FullCommentURL property is defined + const beans::PropertyValues aProperties = rResult.aErrors[ nErrorInResult ].aProperties; + for ( const auto& rProp : aProperties ) + { + if ( rProp.Name == "FullCommentURL" ) + { + uno::Any aValue = rProp.Value; + aValue >>= m_sExplanationLink; + + if ( !m_sExplanationLink.isEmpty( ) ) + break; + } + } + + if ( !m_sExplanationLink.isEmpty( ) ) + { + m_xPopupMenu->InsertItem(MN_EXPLANATION_LINK, SwResId(STR_EXPLANATION_LINK), MenuItemBits::TEXT | MenuItemBits::HELP, OString(), nPos++); + } + + m_xPopupMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + + m_xPopupMenu->InsertSeparator(OString(), nPos++); + if ( rSuggestions.hasElements() ) // suggestions available... + { + Image aImage; + OUString aSuggestionImageUrl; + + if (bUseImagesInMenus) + { + uno::Reference< lang::XServiceInfo > xInfo( rResult.xProofreader, uno::UNO_QUERY ); + if (xInfo.is()) + { + aSuggestionImageUrl = SvtLinguConfig().GetSpellAndGrammarContextSuggestionImage( xInfo->getImplementationName() ); + aImage = Image( aSuggestionImageUrl ); + } + } + + sal_uInt16 nItemId = MN_SUGGESTION_START; + for (const OUString& aEntry : std::as_const(rSuggestions)) + { + m_xPopupMenu->InsertItem(nItemId, aEntry, MenuItemBits::NONE, OString(), nPos++); + m_xPopupMenu->SetHelpId(nItemId, HID_LINGU_REPLACE); + if (!aSuggestionImageUrl.isEmpty()) + m_xPopupMenu->SetItemImage(nItemId, aImage); + + ++nItemId; + } + m_xPopupMenu->InsertSeparator(OString(), nPos++); + } + + uno::Reference< frame::XFrame > xFrame = pWrtSh->GetView().GetViewFrame()->GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + OUString aIgnoreSelection( SwResId( STR_IGNORE_SELECTION ) ); + auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:SpellingAndGrammarDialog", aModuleName); + m_xPopupMenu->SetItemText(m_nSpellDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aCommandProperties)); + sal_uInt16 nItemPos = m_xPopupMenu->GetItemPos(m_nIgnoreWordId); + m_xPopupMenu->InsertItem(MN_IGNORE_SELECTION, aIgnoreSelection, MenuItemBits::NONE, OString(), nItemPos); + m_xPopupMenu->SetHelpId(MN_IGNORE_SELECTION, HID_LINGU_IGNORE_SELECTION); + + m_xPopupMenu->EnableItem(m_nCorrectMenuId, false); + m_xPopupMenu->EnableItem(m_nCorrectDialogId, false); + + uno::Reference< linguistic2::XLanguageGuessing > xLG = SW_MOD()->GetLanguageGuesser(); + LanguageType nGuessLangWord = LANGUAGE_NONE; + LanguageType nGuessLangPara = LANGUAGE_NONE; + if (xLG.is()) + { + nGuessLangPara = EditView::CheckLanguage( rParaText, ::GetSpellChecker(), xLG, true ); + } + if (nGuessLangWord != LANGUAGE_NONE || nGuessLangPara != LANGUAGE_NONE) + { + // make sure LANGUAGE_NONE gets not used as menu entry + if (nGuessLangWord == LANGUAGE_NONE) + nGuessLangWord = nGuessLangPara; + if (nGuessLangPara == LANGUAGE_NONE) + nGuessLangPara = nGuessLangWord; + } + + m_xPopupMenu->EnableItem(m_nAddMenuId, false); + m_xPopupMenu->EnableItem(m_nAddId, false); + + //ADD NEW LANGUAGE MENU ITEM + + OUString aScriptTypesInUse( OUString::number( static_cast<int>(pWrtSh->GetScriptType()) ) ); + + // get keyboard language + OUString aKeyboardLang; + SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin(); + LanguageType nLang = rEditWin.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + nLang = SwLangHelper::GetCurrentLanguage( *pWrtSh ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq( 4 ); + aSeq[0] = aCurrentLang; + aSeq[1] = aScriptTypesInUse; + aSeq[2] = aKeyboardLang; + aSeq[3] = SvtLanguageTable::GetLanguageString(nGuessLangWord); + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_SELECTION_START, aSeq, pWrtSh, m_aLangTable_Text ); + m_xPopupMenu->EnableItem(m_nLangSelectionMenuId); + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_PARAGRAPH_START, aSeq, pWrtSh, m_aLangTable_Paragraph ); + m_xPopupMenu->EnableItem(m_nLangParaMenuId); + + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(m_nSpellDialogId, + vcl::CommandInfoProvider::GetImageForCommand(".uno:SpellingAndGrammarDialog", xFrame)); + + checkRedline(); + m_xPopupMenu->RemoveDisabledEntries(true, true); + + SvtLinguConfig().SetProperty( UPN_IS_GRAMMAR_INTERACTIVE, uno::makeAny( true )); + + InitItemCommands(rSuggestions); +} + +SwSpellPopup::~SwSpellPopup() {} + +void SwSpellPopup::InitItemCommands(const css::uno::Sequence< OUString >& aSuggestions) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + // None is added only for LOK, it means there is no need to execute anything + m_xPopupMenu->SetItemCommand(MN_SHORT_COMMENT, ".uno:None"); + m_xPopupMenu->SetItemCommand(m_nSpellDialogId, ".uno:SpellingAndGrammarDialog"); + if(m_bGrammarResults) + m_xPopupMenu->SetItemCommand(m_nIgnoreWordId, ".uno:SpellCheckIgnoreAll?Type:string=Grammar"); + else + m_xPopupMenu->SetItemCommand(m_nIgnoreWordId, ".uno:SpellCheckIgnoreAll?Type:string=Spelling"); + if(m_bGrammarResults) + m_xPopupMenu->SetItemCommand(MN_IGNORE_SELECTION, ".uno:SpellCheckIgnore?Type:string=Grammar"); + else + m_xPopupMenu->SetItemCommand(MN_IGNORE_SELECTION, ".uno:SpellCheckIgnore?Type:string=Spelling"); + + for(int i = 0; i < aSuggestions.getLength(); ++i) + { + sal_uInt16 nItemId = MN_SUGGESTION_START + i; + OUString sCommandString = ".uno:SpellCheckApplySuggestion?ApplyRule:string="; + if(m_bGrammarResults) + sCommandString += "Grammar_"; + else if (m_xSpellAlt.is()) + sCommandString += "Spelling_"; + sCommandString += m_xPopupMenu->GetItemText(nItemId); + m_xPopupMenu->SetItemCommand(nItemId, sCommandString); + } + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + m_xPopupMenu->SetItemCommand(m_nLangSelectionMenuId, ".uno:SetSelectionLanguageMenu"); + if(pMenu) + { + for (const auto& item : m_aLangTable_Text) + { + OUString sCommandString = ".uno:LanguageStatus?Language:string=Current_" + item.second; + pMenu->SetItemCommand(item.first, sCommandString); + } + + pMenu->SetItemCommand(MN_SET_SELECTION_NONE, ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE"); + pMenu->SetItemCommand(MN_SET_SELECTION_RESET, ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES"); + pMenu->SetItemCommand(MN_SET_SELECTION_MORE, ".uno:FontDialog?Page:string=font"); + } + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + m_xPopupMenu->SetItemCommand(m_nLangParaMenuId, ".uno:SetParagraphLanguageMenu"); + if(pMenu) + { + for (const auto& item : m_aLangTable_Paragraph) + { + OUString sCommandString = ".uno:LanguageStatus?Language:string=Paragraph_" + item.second; + pMenu->SetItemCommand(item.first, sCommandString); + } + + pMenu->SetItemCommand(MN_SET_PARA_NONE, ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE"); + pMenu->SetItemCommand(MN_SET_PARA_RESET, ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES"); + pMenu->SetItemCommand(MN_SET_PARA_MORE, ".uno:FontDialogForParagraph"); + } + } +} + +void SwSpellPopup::checkRedline() +{ + // Let SwView::GetState() already has the logic on when to disable the + // accept/reject and the next/prev change items, let it do the decision. + + // Build an item set that contains a void item for each menu entry. The + // WhichId of each item is set, so SwView may clear it. + static const sal_uInt16 pRedlineIds[] = { + FN_REDLINE_ACCEPT_DIRECT, + FN_REDLINE_REJECT_DIRECT, + FN_REDLINE_NEXT_CHANGE, + FN_REDLINE_PREV_CHANGE + }; + SwDoc *pDoc = m_pSh->GetDoc(); + SfxItemSet aSet(pDoc->GetAttrPool(), svl::Items<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_PREV_CHANGE>{}); + for (sal_uInt16 nWhich : pRedlineIds) + { + aSet.Put(SfxVoidItem(nWhich)); + } + m_pSh->GetView().GetState(aSet); + + // Enable/disable items based on if the which id of the void items are + // cleared or not. + for (sal_uInt16 nWhich : pRedlineIds) + { + sal_uInt16 nId(0); + if (nWhich == FN_REDLINE_ACCEPT_DIRECT) + nId = m_nRedlineAcceptId; + else if (nWhich == FN_REDLINE_REJECT_DIRECT) + nId = m_nRedlineRejectId; + else if (nWhich == FN_REDLINE_NEXT_CHANGE) + nId = m_nRedlineNextId; + else if (nWhich == FN_REDLINE_PREV_CHANGE) + nId = m_nRedlinePrevId; + m_xPopupMenu->EnableItem(nId, aSet.Get(nWhich).Which() != 0); + } +} + +void SwSpellPopup::Execute( const tools::Rectangle& rWordPos, vcl::Window* pWin ) +{ + sal_uInt16 nRet = m_xPopupMenu->Execute(pWin, pWin->LogicToPixel(rWordPos)); + Execute( nRet ); +} + +void SwSpellPopup::Execute( sal_uInt16 nId ) +{ + if (nId == USHRT_MAX) + return; + + if (/*m_bGrammarResults && */nId == MN_SHORT_COMMENT) + return; // nothing to do since it is the error message (short comment) + + if (MN_SUGGESTION_START <= nId && nId <= MN_SUGGESTION_END) + { + OUString sApplyRule(""); + if(m_bGrammarResults) + sApplyRule += "Grammar_"; + else if (m_xSpellAlt.is()) + sApplyRule += "Spelling_"; + sApplyRule += m_xPopupMenu->GetItemText(nId); + + SfxStringItem aApplyItem(FN_PARAM_1, sApplyRule); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_SPELLCHECK_APPLY_SUGGESTION, SfxCallMode::SYNCHRON, { &aApplyItem }); + } + else if(MN_AUTOCORR_START <= nId && nId <= MN_AUTOCORR_END) + { + if (m_xSpellAlt.is()) + { + bool bOldIns = m_pSh->IsInsMode(); + m_pSh->SetInsMode(); + + PopupMenu* pMenu = m_xPopupMenu->GetPopupMenu(m_nCorrectMenuId); + assert(pMenu); + OUString aTmp( pMenu->GetItemText(nId) ); + OUString aOrig( m_xSpellAlt->getWord() ); + + // if original word has a trailing . (likely the end of a sentence) + // and the replacement text hasn't, then add it to the replacement + if (!aTmp.isEmpty() && !aOrig.isEmpty() && + aOrig.endsWith(".") && /* !IsAlphaNumeric ??*/ + !aTmp.endsWith(".")) + { + aTmp += "."; + } + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_pSh->GetCursorDescr()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + + OUString aTmpStr = SwResId(STR_START_QUOTE) + + aTmp + SwResId(STR_END_QUOTE); + aRewriter.AddRule(UndoArg3, aTmpStr); + + m_pSh->StartUndo(SwUndoId::UI_REPLACE, &aRewriter); + m_pSh->StartAction(); + + m_pSh->Replace(aTmp, false); + + /* #102505# EndAction/EndUndo moved down since insertion + of temporary auto correction is now undoable two and + must reside in the same undo group.*/ + + // record only if it's NOT already present in autocorrection + SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect(); + + OUString aOrigWord( m_xSpellAlt->getWord() ) ; + OUString aNewWord( pMenu->GetItemText(nId) ); + SvxPrepareAutoCorrect( aOrigWord, aNewWord ); + + pACorr->PutText( aOrigWord, aNewWord, m_nCheckedLanguage ); + + /* #102505# EndAction/EndUndo moved down since insertion + of temporary auto correction is now undoable two and + must reside in the same undo group.*/ + m_pSh->EndAction(); + m_pSh->EndUndo(); + + m_pSh->SetInsMode( bOldIns ); + } + } + else if (nId == m_nSpellDialogId) + { + m_pSh->Left(CRSR_SKIP_CHARS, false, 1, false ); + { + m_pSh->GetView().GetViewFrame()->GetDispatcher()-> + Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON ); + } + } + else if (nId == m_nCorrectDialogId) + { + m_pSh->GetView().GetViewFrame()->GetDispatcher()->Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); + } + else if (nId == MN_IGNORE_SELECTION) + { + SfxStringItem aIgnoreString(FN_PARAM_1, m_bGrammarResults ? OUString("Grammar") : OUString("Spelling")); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_SPELLCHECK_IGNORE, SfxCallMode::SYNCHRON, { &aIgnoreString }); + } + else if (nId == m_nIgnoreWordId) + { + SfxStringItem aIgnoreString(FN_PARAM_1, m_bGrammarResults ? OUString("Grammar") : OUString("Spelling")); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_SPELLCHECK_IGNORE_ALL, SfxCallMode::SYNCHRON, { &aIgnoreString }); + } + else if ((MN_DICTIONARIES_START <= nId && nId <= MN_DICTIONARIES_END) || nId == m_nAddId) + { + OUString sWord( m_xSpellAlt->getWord() ); + OUString aDicName; + + if (MN_DICTIONARIES_START <= nId && nId <= MN_DICTIONARIES_END) + { + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nAddMenuId); + aDicName = pMenu->GetItemText(nId); + } + else + aDicName = m_aDicNameSingle; + + uno::Reference< linguistic2::XDictionary > xDic; + uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + if (xDicList.is()) + xDic = xDicList->getDictionaryByName( aDicName ); + + if (xDic.is()) + { + linguistic::DictionaryError nAddRes = linguistic::AddEntryToDic(xDic, sWord, false, OUString()); + // save modified user-dictionary if it is persistent + uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY ); + if (xSavDic.is()) + xSavDic->store(); + + if (linguistic::DictionaryError::NONE != nAddRes && !xDic->getEntry(sWord).is()) + { + SvxDicError(m_pSh->GetView().GetFrameWeld(), nAddRes); + } + } + } + else if ( nId == MN_EXPLANATION_LINK && !m_sExplanationLink.isEmpty() ) + { + try + { + uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute( + css::system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) ); + xSystemShellExecute->execute( m_sExplanationLink, OUString(), + css::system::SystemShellExecuteFlags::URIS_ONLY ); + } + catch (const uno::Exception&) + { + uno::Any exc( ::cppu::getCaughtException() ); + OUString msg( ::comphelper::anyToString( exc ) ); + const SolarMutexGuard guard; + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pSh->GetView().GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, msg)); + xBox->set_title("Explanations"); + xBox->run(); + } + } + else if (nId == m_nRedlineAcceptId || nId == m_nRedlineRejectId + || nId == m_nRedlineNextId || nId == m_nRedlinePrevId) + { + if (nId == m_nRedlineAcceptId) + nId = FN_REDLINE_ACCEPT_DIRECT; + else if (nId == m_nRedlineRejectId) + nId = FN_REDLINE_REJECT_DIRECT; + else if (nId == m_nRedlineNextId) + nId = FN_REDLINE_NEXT_CHANGE; + else if (nId == m_nRedlinePrevId) + nId = FN_REDLINE_PREV_CHANGE; + // Let SwView::Execute() handle the redline actions. + SfxRequest aReq(m_pSh->GetView().GetViewFrame(), nId); + m_pSh->GetView().Execute(aReq); + } + else + { + if (MN_SET_LANGUAGE_SELECTION_START <= nId && nId <= MN_SET_LANGUAGE_SELECTION_END) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_" + m_aLangTable_Text[nId]); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_NONE) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_LANGUAGE_NONE"); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_RESET) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_RESET_LANGUAGES"); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_MORE) + { + SfxStringItem aDlgString(FN_PARAM_1, "font"); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_CHAR_DLG, SfxCallMode::SYNCHRON, { &aDlgString }); + } + else if (MN_SET_LANGUAGE_PARAGRAPH_START <= nId && nId <= MN_SET_LANGUAGE_PARAGRAPH_END) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_" + m_aLangTable_Paragraph[nId]); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_NONE) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_LANGUAGE_NONE"); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_RESET) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_RESET_LANGUAGES"); + m_pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_MORE) + { + m_pSh->GetView().GetViewFrame()->GetDispatcher()->Execute( SID_CHAR_DLG_FOR_PARAGRAPH ); + } + } + + m_pSh->EnterStdMode(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/sdrhhcwrap.cxx b/sw/source/uibase/lingu/sdrhhcwrap.cxx new file mode 100644 index 000000000..d21ffaa24 --- /dev/null +++ b/sw/source/uibase/lingu/sdrhhcwrap.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svdotext.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/printer.hxx> +#include <svx/svdview.hxx> +#include <drawdoc.hxx> +#include "sdrhhcwrap.hxx" +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <dcontact.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <edtwin.hxx> + +using namespace ::com::sun::star; + +SdrHHCWrapper::SdrHHCWrapper( SwView* pVw, + LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font* pTargetFnt, + sal_Int32 nConvOptions, + bool bInteractive ) : + SdrOutliner(pVw->GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()-> + GetDrawOutliner().GetEmptyItemSet().GetPool(), + OutlinerMode::TextObject ), + pView( pVw ), + pTextObj( nullptr ), + nOptions( nConvOptions ), + nDocIndex( 0 ), + nSourceLang( nSourceLanguage ), + nTargetLang( nTargetLanguage ), + pTargetFont( pTargetFnt ), + bIsInteractive( bInteractive ) +{ + SetRefDevice( pView->GetDocShell()->GetDoc()->getIDocumentDeviceAccess().getPrinter( false ) ); + + MapMode aMapMode (MapUnit::MapTwip); + SetRefMapMode(aMapMode); + + Size aSize( 1, 1 ); + SetPaperSize( aSize ); + + pOutlView.reset( new OutlinerView( this, &(pView->GetEditWin()) ) ); + pOutlView->GetOutliner()->SetRefDevice(pView->GetWrtShell().getIDocumentDeviceAccess().getPrinter( false )); + + // Hack: all SdrTextObj attributes should be transferred to EditEngine + pOutlView->SetBackgroundColor( COL_WHITE ); + + InsertView( pOutlView.get() ); + Point aPoint( 0, 0 ); + tools::Rectangle aRect( aPoint, aSize ); + pOutlView->SetOutputArea( aRect ); +// SetText( NULL ); + ClearModifyFlag(); +} + +SdrHHCWrapper::~SdrHHCWrapper() +{ + if (pTextObj) + { + SdrView *pSdrView = pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + pSdrView->SdrEndTextEdit( true ); + SetUpdateMode(false); + pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1, 1) ) ); + } + RemoveView( pOutlView.get() ); + pOutlView.reset(); +} + +void SdrHHCWrapper::StartTextConversion() +{ + pOutlView->StartTextConversion( nSourceLang, nTargetLang, pTargetFont, nOptions, bIsInteractive, true ); +} + +bool SdrHHCWrapper::ConvertNextDocument() +{ + bool bNextDoc = false; + + if ( pTextObj ) + { + SdrView *pSdrView = pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + pSdrView->SdrEndTextEdit( true ); + SetUpdateMode(false); + pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1, 1) ) ); + SetPaperSize( Size(1, 1) ); + Clear(); + pTextObj = nullptr; + } + + const auto n = nDocIndex; + + std::list<SdrTextObj*> aTextObjs; + SwDrawContact::GetTextObjectsFromFormat( aTextObjs, pView->GetDocShell()->GetDoc() ); + for (auto const& textObj : aTextObjs) + { + pTextObj = textObj; + if (textObj) + { + OutlinerParaObject* pParaObj = textObj->GetOutlinerParaObject(); + if ( pParaObj ) + { + SetPaperSize( textObj->GetLogicRect().GetSize() ); + SetText( *pParaObj ); + + ClearModifyFlag(); + + //!! update mode needs to be set to true otherwise + //!! the call to 'HasConvertibleTextPortion' will not always + //!! work correctly because the document may not be properly + //!! formatted when some information is accessed, and thus + //!! incorrect results get returned. + SetUpdateMode(true); + if (HasConvertibleTextPortion( nSourceLang )) + { + SdrView *pSdrView = pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + SdrPageView* pPV = pSdrView->GetSdrPageView(); + nDocIndex = n; + bNextDoc = true; + pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1,1))); + SetPaperSize( pTextObj->GetLogicRect().GetSize() ); + SetUpdateMode(true); + pView->GetWrtShell().MakeVisible(pTextObj->GetLogicRect()); + + pSdrView->SdrBeginTextEdit(pTextObj, pPV, &pView->GetEditWin(), false, this, pOutlView.get(), true, true); + } + else + SetUpdateMode(false); + } + + if ( !bNextDoc ) + pTextObj = nullptr; + else + break; + } + } + + ClearModifyFlag(); + + return bNextDoc; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/sdrhhcwrap.hxx b/sw/source/uibase/lingu/sdrhhcwrap.hxx new file mode 100644 index 000000000..133e47cc9 --- /dev/null +++ b/sw/source/uibase/lingu/sdrhhcwrap.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_LINGU_SDRHHCWRAP_HXX +#define INCLUDED_SW_SOURCE_UIBASE_LINGU_SDRHHCWRAP_HXX + +#include <svx/svdoutl.hxx> + +class SwView; +class SdrTextObj; +class OutlinerView; + +class SdrHHCWrapper : public SdrOutliner +{ + // modified version of SdrSpeller + + SwView* pView; + SdrTextObj* pTextObj; + std::unique_ptr<OutlinerView> pOutlView; + sal_Int32 nOptions; + sal_uInt16 nDocIndex; + LanguageType nSourceLang; + LanguageType nTargetLang; + const vcl::Font* pTargetFont; + bool bIsInteractive; + +public: + SdrHHCWrapper( SwView* pVw, + LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font* pTargetFnt, + sal_Int32 nConvOptions, bool bInteractive ); + + virtual ~SdrHHCWrapper() override; + + virtual bool ConvertNextDocument() override; + void StartTextConversion(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/glosdoc.cxx b/sw/source/uibase/misc/glosdoc.cxx new file mode 100644 index 000000000..868158732 --- /dev/null +++ b/sw/source/uibase/misc/glosdoc.cxx @@ -0,0 +1,627 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/container/XNamed.hpp> +#include <comphelper/servicehelper.hxx> + +#include <unotools/transliterationwrapper.hxx> + +#include <vcl/errinf.hxx> +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <svl/urihelper.hxx> +#include <svl/fstathelper.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/tempfile.hxx> +#include <swtypes.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <swunohelper.hxx> + +#include <unoatxt.hxx> +#include <swerror.h> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace +{ + +OUString lcl_FullPathName(const OUString& sPath, const OUString& sName) +{ + return sPath + "/" + sName + SwGlossaries::GetExtension(); +} + +OUString lcl_CheckFileName( const OUString& rNewFilePath, + const OUString& rNewGroupName ) +{ + const sal_Int32 nLen = rNewGroupName.getLength(); + OUStringBuffer aBuf(nLen); + //group name should contain only A-Z and a-z and spaces + for( sal_Int32 i=0; i < nLen; ++i ) + { + const sal_Unicode cChar = rNewGroupName[i]; + if (rtl::isAsciiAlphanumeric(cChar) || + cChar == '_' || cChar == 0x20) + { + aBuf.append(cChar); + } + } + + const OUString sRet = aBuf.makeStringAndClear().trim(); + if ( !sRet.isEmpty() ) + { + if (!FStatHelper::IsDocument( lcl_FullPathName(rNewFilePath, sRet) )) + return sRet; + } + + OUString rSG = SwGlossaries::GetExtension(); + //generate generic name + utl::TempFile aTemp("group", true, &rSG, &rNewFilePath); + aTemp.EnableKillingFile(); + + INetURLObject aTempURL( aTemp.GetURL() ); + return aTempURL.GetBase(); +} + +} + +// supplies the default group's name +OUString SwGlossaries::GetDefName() +{ + return "standard"; + +} + +// supplies the number of text block groups +size_t SwGlossaries::GetGroupCnt() +{ + return GetNameList().size(); +} + +// supplies the group's name +bool SwGlossaries::FindGroupName(OUString& rGroup) +{ + // if the group name doesn't contain a path, a suitable group entry + // can the searched for here; + const size_t nCount = GetGroupCnt(); + for(size_t i = 0; i < nCount; ++i) + { + const OUString sTemp(GetGroupName(i)); + if (rGroup==sTemp.getToken(0, GLOS_DELIM)) + { + rGroup = sTemp; + return true; + } + } + // you can search two times because for more directories the case sensitive + // name could occur multiple times + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + for(size_t i = 0; i < nCount; ++i) + { + const OUString sTemp( GetGroupName( i )); + sal_uInt16 nPath = sTemp.getToken(1, GLOS_DELIM).toUInt32(); + + if (!SWUnoHelper::UCB_IsCaseSensitiveFileName( m_PathArr[nPath] ) + && rSCmp.isEqual( rGroup, sTemp.getToken( 0, GLOS_DELIM) ) ) + { + rGroup = sTemp; + return true; + } + } + return false; +} + +OUString const & SwGlossaries::GetGroupName(size_t nGroupId) +{ + OSL_ENSURE(nGroupId < m_GlosArr.size(), + "SwGlossaries::GetGroupName: index out of bounds"); + return m_GlosArr[nGroupId]; +} + +OUString SwGlossaries::GetGroupTitle( const OUString& rGroupName ) +{ + OUString sRet; + OUString sGroup(rGroupName); + if (sGroup.indexOf(GLOS_DELIM)<0) + FindGroupName(sGroup); + std::unique_ptr<SwTextBlocks> pGroup = GetGroupDoc(sGroup); + if(pGroup) + { + sRet = pGroup->GetName(); + } + return sRet; +} + +// supplies the group rName's text block document +std::unique_ptr<SwTextBlocks> SwGlossaries::GetGroupDoc(const OUString &rName, + bool bCreate) +{ + // insert to the list of text blocks if applicable + if(bCreate && !m_GlosArr.empty()) + { + if (std::none_of(m_GlosArr.begin(), m_GlosArr.end(), + [&rName](const OUString& rEntry) { return rEntry == rName; })) + { // block not in the list + m_GlosArr.push_back(rName); + } + } + return GetGlosDoc( rName, bCreate ); +} + +// Creates a new document with the group name. temporarily also created as file +// so that groups remain there later (without access). +bool SwGlossaries::NewGroupDoc(OUString& rGroupName, const OUString& rTitle) +{ + const OUString sNewPath(rGroupName.getToken(1, GLOS_DELIM)); + sal_uInt16 nNewPath = static_cast<sal_uInt16>(sNewPath.toInt32()); + if (static_cast<size_t>(nNewPath) >= m_PathArr.size()) + return false; + const OUString sNewFilePath(m_PathArr[nNewPath]); + const OUString sNewGroup = lcl_CheckFileName(sNewFilePath, rGroupName.getToken(0, GLOS_DELIM)) + + OUStringChar(GLOS_DELIM) + sNewPath; + std::unique_ptr<SwTextBlocks> pBlock = GetGlosDoc( sNewGroup ); + if(pBlock) + { + GetNameList().push_back(sNewGroup); + pBlock->SetName(rTitle); + rGroupName = sNewGroup; + return true; + } + return false; +} + +bool SwGlossaries::RenameGroupDoc( + const OUString& rOldGroup, OUString& rNewGroup, const OUString& rNewTitle ) +{ + sal_uInt16 nOldPath = static_cast<sal_uInt16>(rOldGroup.getToken(1, GLOS_DELIM).toInt32()); + if (static_cast<size_t>(nOldPath) >= m_PathArr.size()) + return false; + + const OUString sOldFileURL = + lcl_FullPathName(m_PathArr[nOldPath], rOldGroup.getToken(0, GLOS_DELIM)); + + if (!FStatHelper::IsDocument( sOldFileURL )) + { + OSL_FAIL("group doesn't exist!"); + return false; + } + + sal_uInt16 nNewPath = static_cast<sal_uInt16>(rNewGroup.getToken(1, GLOS_DELIM).toInt32()); + if (static_cast<size_t>(nNewPath) >= m_PathArr.size()) + return false; + + const OUString sNewFileName = lcl_CheckFileName(m_PathArr[nNewPath], + rNewGroup.getToken(0, GLOS_DELIM)); + const OUString sNewFileURL = lcl_FullPathName(m_PathArr[nNewPath], sNewFileName); + + if (FStatHelper::IsDocument( sNewFileURL )) + { + OSL_FAIL("group already exists!"); + return false; + } + + if (!SWUnoHelper::UCB_MoveFile(sOldFileURL, sNewFileURL )) + return false; + + RemoveFileFromList( rOldGroup ); + + rNewGroup = sNewFileName + OUStringChar(GLOS_DELIM) + OUString::number(nNewPath); + if (m_GlosArr.empty()) + { + GetNameList(); + } + else + { + m_GlosArr.push_back(rNewGroup); + } + + std::unique_ptr<SwTextBlocks> pNewBlock(new SwTextBlocks( sNewFileURL )); + pNewBlock->SetName(rNewTitle); + + return true; +} + +// Deletes a text block group +bool SwGlossaries::DelGroupDoc(const OUString &rName) +{ + sal_uInt16 nPath = static_cast<sal_uInt16>(rName.getToken(1, GLOS_DELIM).toInt32()); + if (static_cast<size_t>(nPath) >= m_PathArr.size()) + return false; + const OUString sBaseName(rName.getToken(0, GLOS_DELIM)); + const OUString sFileURL = lcl_FullPathName(m_PathArr[nPath], sBaseName); + const OUString aName = sBaseName + OUStringChar(GLOS_DELIM) + OUString::number(nPath); + // Even if the file doesn't exist it has to be deleted from + // the list of text block regions + // no && because of CFfront + bool bRemoved = SWUnoHelper::UCB_DeleteFile( sFileURL ); + OSL_ENSURE(bRemoved, "file has not been removed"); + RemoveFileFromList( aName ); + return bRemoved; +} + +SwGlossaries::~SwGlossaries() +{ + InvalidateUNOOjects(); +} + +// read a block document +std::unique_ptr<SwTextBlocks> SwGlossaries::GetGlosDoc( const OUString &rName, bool bCreate ) const +{ + sal_uInt16 nPath = static_cast<sal_uInt16>(rName.getToken(1, GLOS_DELIM).toInt32()); + std::unique_ptr<SwTextBlocks> pTmp; + if (static_cast<size_t>(nPath) < m_PathArr.size()) + { + const OUString sFileURL = + lcl_FullPathName(m_PathArr[nPath], rName.getToken(0, GLOS_DELIM)); + + bool bExist = false; + if(!bCreate) + bExist = FStatHelper::IsDocument( sFileURL ); + + if (bCreate || bExist) + { + pTmp.reset(new SwTextBlocks( sFileURL )); + bool bOk = true; + if( pTmp->GetError() ) + { + ErrorHandler::HandleError( pTmp->GetError() ); + bOk = ! pTmp->GetError().IsError(); + } + + if( bOk && pTmp->GetName().isEmpty() ) + pTmp->SetName( rName ); + } + } + + return pTmp; +} + +// access to the list of names; read in if applicable +std::vector<OUString> & SwGlossaries::GetNameList() +{ + if (m_GlosArr.empty()) + { + const OUString sExt( SwGlossaries::GetExtension() ); + for (size_t i = 0; i < m_PathArr.size(); ++i) + { + std::vector<OUString> aFiles; + + SWUnoHelper::UCB_GetFileListOfFolder(m_PathArr[i], aFiles, &sExt); + for (const OUString& aTitle : aFiles) + { + const OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() ) + + OUStringChar(GLOS_DELIM) + OUString::number( static_cast<sal_Int16>(i) )); + m_GlosArr.push_back(sName); + } + } + if (m_GlosArr.empty()) + { + // the standard block is inside of the path's first part + m_GlosArr.emplace_back(SwGlossaries::GetDefName() + OUStringChar(GLOS_DELIM) + "0" ); + } + } + return m_GlosArr; +} + +SwGlossaries::SwGlossaries() +{ + UpdateGlosPath(true); +} + +// set new path and recreate internal array +static OUString lcl_makePath(const std::vector<OUString>& rPaths) +{ + std::vector<OUString>::const_iterator aIt(rPaths.begin()); + const std::vector<OUString>::const_iterator aEnd(rPaths.end()); + OUStringBuffer aPath(*aIt); + for (++aIt; aIt != aEnd; ++aIt) + { + aPath.append(SVT_SEARCHPATH_DELIMITER); + const INetURLObject aTemp(*aIt); + aPath.append(aTemp.GetFull()); + } + return aPath.getStr(); +} + +void SwGlossaries::UpdateGlosPath(bool bFull) +{ + SvtPathOptions aPathOpt; + const OUString& aNewPath( aPathOpt.GetAutoTextPath() ); + bool bPathChanged = m_aPath != aNewPath; + if (bFull || bPathChanged) + { + m_aPath = aNewPath; + + m_PathArr.clear(); + + std::vector<OUString> aDirArr; + std::vector<OUString> aInvalidPaths; + if (!m_aPath.isEmpty()) + { + sal_Int32 nIndex = 0; + do + { + const OUString sPth = URIHelper::SmartRel2Abs( + INetURLObject(), + m_aPath.getToken(0, SVT_SEARCHPATH_DELIMITER, nIndex), + URIHelper::GetMaybeFileHdl()); + if (!aDirArr.empty() && + std::find(aDirArr.begin(), aDirArr.end(), sPth) != aDirArr.end()) + { + continue; + } + aDirArr.push_back(sPth); + if( !FStatHelper::IsFolder( sPth ) ) + aInvalidPaths.push_back(sPth); + else + m_PathArr.push_back(sPth); + } + while (nIndex>=0); + } + + if (m_aPath.isEmpty() || !aInvalidPaths.empty()) + { + std::sort(aInvalidPaths.begin(), aInvalidPaths.end()); + aInvalidPaths.erase(std::unique(aInvalidPaths.begin(), aInvalidPaths.end()), aInvalidPaths.end()); + if (bPathChanged || (m_aInvalidPaths != aInvalidPaths)) + { + m_aInvalidPaths = aInvalidPaths; + // wrong path, that means AutoText directory doesn't exist + + ErrorHandler::HandleError( *new StringErrorInfo( + ERR_AUTOPATH_ERROR, lcl_makePath(m_aInvalidPaths), + DialogMask::ButtonsOk | DialogMask::MessageError ) ); + m_bError = true; + } + else + m_bError = false; + } + else + m_bError = false; + + if (!m_GlosArr.empty()) + { + m_GlosArr.clear(); + GetNameList(); + } + } +} + +void SwGlossaries::ShowError() +{ + ErrCode nPathError = *new StringErrorInfo(ERR_AUTOPATH_ERROR, + lcl_makePath(m_aInvalidPaths), DialogMask::ButtonsOk ); + ErrorHandler::HandleError( nPathError ); +} + +OUString SwGlossaries::GetExtension() +{ + return ".bau"; +} + +void SwGlossaries::RemoveFileFromList( const OUString& rGroup ) +{ + if (!m_GlosArr.empty()) + { + auto it = std::find(m_GlosArr.begin(), m_GlosArr.end(), rGroup); + if (it != m_GlosArr.end()) + { + { + // tell the UNO AutoTextGroup object that it's not valid anymore + for ( UnoAutoTextGroups::iterator aLoop = m_aGlossaryGroups.begin(); + aLoop != m_aGlossaryGroups.end(); + ) + { + Reference< container::XNamed > xNamed( aLoop->get(), UNO_QUERY ); + if ( !xNamed.is() ) + { + aLoop = m_aGlossaryGroups.erase(aLoop); + } + else if ( xNamed->getName() == rGroup ) + { + static_cast< SwXAutoTextGroup* >( xNamed.get() )->Invalidate(); + // note that this static_cast works because we know that the array only + // contains SwXAutoTextGroup implementation + m_aGlossaryGroups.erase( aLoop ); + break; + } else + ++aLoop; + } + } + + { + // tell all our UNO AutoTextEntry objects that they're not valid anymore + for ( UnoAutoTextEntries::iterator aLoop = m_aGlossaryEntries.begin(); + aLoop != m_aGlossaryEntries.end(); + ) + { + auto pEntry = comphelper::getUnoTunnelImplementation<SwXAutoTextEntry>(aLoop->get()); + if ( pEntry && ( pEntry->GetGroupName() == rGroup ) ) + { + pEntry->Invalidate(); + aLoop = m_aGlossaryEntries.erase( aLoop ); + } + else + ++aLoop; + } + } + + m_GlosArr.erase(it); + } + } +} + +OUString SwGlossaries::GetCompleteGroupName( const OUString& rGroupName ) +{ + const size_t nCount = GetGroupCnt(); + // when the group name was created internally the path is here as well + sal_Int32 nIndex = 0; + const OUString sGroupName(rGroupName.getToken(0, GLOS_DELIM, nIndex)); + const bool bPathLen = !rGroupName.getToken(0, GLOS_DELIM, nIndex).isEmpty(); + for ( size_t i = 0; i < nCount; i++ ) + { + const OUString sGrpName = GetGroupName(i); + if (bPathLen) + { + if (rGroupName == sGrpName) + return sGrpName; + } + else + { + if (sGroupName == sGrpName.getToken(0, GLOS_DELIM)) + return sGrpName; + } + } + return OUString(); +} + +void SwGlossaries::InvalidateUNOOjects() +{ + // invalidate all the AutoTextGroup-objects + for (const auto& rGroup : m_aGlossaryGroups) + { + Reference< text::XAutoTextGroup > xGroup( rGroup.get(), UNO_QUERY ); + if ( xGroup.is() ) + static_cast< SwXAutoTextGroup* >( xGroup.get() )->Invalidate(); + } + UnoAutoTextGroups aTmpg; + m_aGlossaryGroups.swap( aTmpg ); + + // invalidate all the AutoTextEntry-objects + for (const auto& rEntry : m_aGlossaryEntries) + { + auto pEntry = comphelper::getUnoTunnelImplementation<SwXAutoTextEntry>(rEntry.get()); + if ( pEntry ) + pEntry->Invalidate(); + } + UnoAutoTextEntries aTmpe; + m_aGlossaryEntries.swap( aTmpe ); +} + +Reference< text::XAutoTextGroup > SwGlossaries::GetAutoTextGroup( const OUString& _rGroupName ) +{ + bool _bCreate = true; + // first, find the name with path-extension + const OUString sCompleteGroupName = GetCompleteGroupName( _rGroupName ); + + Reference< text::XAutoTextGroup > xGroup; + + // look up the group in the cache + UnoAutoTextGroups::iterator aSearch = m_aGlossaryGroups.begin(); + for ( ; aSearch != m_aGlossaryGroups.end(); ) + { + auto pSwGroup = comphelper::getUnoTunnelImplementation<SwXAutoTextGroup>(aSearch->get()); + if ( !pSwGroup ) + { + // the object is dead in the meantime -> remove from cache + aSearch = m_aGlossaryGroups.erase( aSearch ); + continue; + } + + if ( _rGroupName == pSwGroup->getName() ) + { // the group is already cached + if ( !sCompleteGroupName.isEmpty() ) + { // the group still exists -> return it + xGroup = pSwGroup; + break; + } + else + { + // this group does not exist (anymore) -> release the cached UNO object for it + aSearch = m_aGlossaryGroups.erase( aSearch ); + // so it won't be created below + _bCreate = false; + break; + } + } + + ++aSearch; + } + + if ( !xGroup.is() && _bCreate ) + { + xGroup = new SwXAutoTextGroup( sCompleteGroupName, this ); + // cache it + m_aGlossaryGroups.emplace_back( xGroup ); + } + + return xGroup; +} + +Reference< text::XAutoTextEntry > SwGlossaries::GetAutoTextEntry( + const OUString& rCompleteGroupName, + const OUString& rGroupName, + const OUString& rEntryName ) +{ + //standard must be created + bool bCreate = ( rCompleteGroupName == GetDefName() ); + std::unique_ptr< SwTextBlocks > pGlosGroup( GetGroupDoc( rCompleteGroupName, bCreate ) ); + + if (!pGlosGroup || pGlosGroup->GetError()) + throw lang::WrappedTargetException(); + + sal_uInt16 nIdx = pGlosGroup->GetIndex( rEntryName ); + if ( USHRT_MAX == nIdx ) + throw container::NoSuchElementException(); + + Reference< text::XAutoTextEntry > xReturn; + + UnoAutoTextEntries::iterator aSearch( m_aGlossaryEntries.begin() ); + for ( ; aSearch != m_aGlossaryEntries.end(); ) + { + Reference< lang::XUnoTunnel > xEntryTunnel( aSearch->get(), UNO_QUERY ); + + SwXAutoTextEntry* pEntry = nullptr; + if ( xEntryTunnel.is() ) + pEntry = reinterpret_cast< SwXAutoTextEntry* >( xEntryTunnel->getSomething( SwXAutoTextEntry::getUnoTunnelId() ) ); + else + { + // the object is dead in the meantime -> remove from cache + aSearch = m_aGlossaryEntries.erase( aSearch ); + continue; + } + + if ( pEntry + && pEntry->GetGroupName() == rGroupName + && pEntry->GetEntryName() == rEntryName + ) + { + xReturn = pEntry; + break; + } + + ++aSearch; + } + + if ( !xReturn.is() ) + { + xReturn = new SwXAutoTextEntry( this, rGroupName, rEntryName ); + // cache it + m_aGlossaryEntries.emplace_back( xReturn ); + } + + return xReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/glshell.cxx b/sw/source/uibase/misc/glshell.cxx new file mode 100644 index 000000000..1e3c664f1 --- /dev/null +++ b/sw/source/uibase/misc/glshell.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/frame/XModel.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/request.hxx> +#include <svl/macitem.hxx> +#include <gloshdl.hxx> + +#include <editeng/acorrcfg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <glshell.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentState.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <initui.hxx> +#include <cmdid.h> +#include <strings.hrc> + +#define ShellClass_SwWebGlosDocShell +#define ShellClass_SwGlosDocShell + +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <memory> +#include <utility> + +using namespace ::com::sun::star; + +SFX_IMPL_SUPERCLASS_INTERFACE(SwGlosDocShell, SwDocShell) + +void SwGlosDocShell::InitInterface_Impl() +{ +} + +SFX_IMPL_SUPERCLASS_INTERFACE(SwWebGlosDocShell, SwWebDocShell) + +void SwWebGlosDocShell::InitInterface_Impl() +{ +} + + +static void lcl_Execute( SwDocShell& rSh, SfxRequest& rReq ) +{ + if ( rReq.GetSlot() == SID_SAVEDOC ) + { + if( !rSh.HasName() ) + { + rReq.SetReturnValue( SfxBoolItem( 0, rSh.Save() ) ); + } + else + { + const SfxBoolItem* pRes = static_cast< const SfxBoolItem* >( + rSh.ExecuteSlot( rReq, + rSh.SfxObjectShell::GetInterface() )); + if( pRes->GetValue() ) + rSh.GetDoc()->getIDocumentState().ResetModified(); + } + } +} + +static void lcl_GetState( SwDocShell& rSh, SfxItemSet& rSet ) +{ + if( SfxItemState::DEFAULT >= rSet.GetItemState( SID_SAVEDOC, false )) + { + if( !rSh.GetDoc()->getIDocumentState().IsModified() ) + rSet.DisableItem( SID_SAVEDOC ); + else + rSet.Put( SfxStringItem( SID_SAVEDOC, SwResId(STR_SAVE_GLOSSARY))); + } +} + +static bool lcl_Save( SwWrtShell& rSh, const OUString& rGroupName, + const OUString& rShortNm, const OUString& rLongNm ) +{ + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + std::unique_ptr<SwTextBlocks> pBlock(::GetGlossaries()->GetGroupDoc( rGroupName )); + + SvxMacro aStart { OUString(), OUString() }; + SvxMacro aEnd { OUString(), OUString() }; + SwGlossaryHdl* pGlosHdl; + + pGlosHdl = rSh.GetView().GetGlosHdl(); + pGlosHdl->GetMacros( rShortNm, aStart, aEnd, pBlock.get() ); + + sal_uInt16 nRet = rSh.SaveGlossaryDoc( *pBlock, rLongNm, rShortNm, + rCfg.IsSaveRelFile(), + pBlock->IsOnlyTextBlock( rShortNm ) ); + + if(aStart.HasMacro() || aEnd.HasMacro() ) + { + SvxMacro* pStart = aStart.HasMacro() ? &aStart : nullptr; + SvxMacro* pEnd = aEnd.HasMacro() ? &aEnd : nullptr; + pGlosHdl->SetMacros( rShortNm, pStart, pEnd, pBlock.get() ); + } + + rSh.EnterStdMode(); + if( USHRT_MAX != nRet ) + rSh.ResetModified(); + return nRet != USHRT_MAX; +} + +SwGlosDocShell::SwGlosDocShell(bool bNewShow) + : SwDocShell( bNewShow + ? SfxObjectCreateMode::STANDARD : SfxObjectCreateMode::INTERNAL ) +{ +} + +SwGlosDocShell::~SwGlosDocShell( ) +{ +} + +void SwGlosDocShell::Execute( SfxRequest& rReq ) +{ + ::lcl_Execute( *this, rReq ); +} + +void SwGlosDocShell::GetState( SfxItemSet& rSet ) +{ + ::lcl_GetState( *this, rSet ); +} + +bool SwGlosDocShell::Save() +{ + // In case of an API object which holds this document, it is possible that the WrtShell is already + // dead. For instance, if the doc is modified via this API object, and then, upon office shutdown, + // the document's view is closed (by the SFX framework) _before_ the API object is release and + // tries to save the doc, again. + // 96380 - 2002-03-03 - fs@openoffice.org + if ( GetWrtShell() ) + return ::lcl_Save( *GetWrtShell(), aGroupName, aShortName, aLongName ); + else + { + SetModified( false ); + return false; + } +} + +SwWebGlosDocShell::SwWebGlosDocShell() + : SwWebDocShell() +{ +} + +SwWebGlosDocShell::~SwWebGlosDocShell( ) +{ +} + +void SwWebGlosDocShell::Execute( SfxRequest& rReq ) +{ + ::lcl_Execute( *this, rReq ); +} + +void SwWebGlosDocShell::GetState( SfxItemSet& rSet ) +{ + ::lcl_GetState( *this, rSet ); +} + +bool SwWebGlosDocShell::Save() +{ + // same comment as in SwGlosDocShell::Save - see there + if ( GetWrtShell() ) + return ::lcl_Save( *GetWrtShell(), aGroupName, aShortName, aLongName ); + else + { + SetModified( false ); + return false; + } +} + +SwDocShellRef SwGlossaries::EditGroupDoc( const OUString& rGroup, const OUString& rShortName, bool bShow ) +{ + SwDocShellRef xDocSh; + + std::unique_ptr<SwTextBlocks> pGroup = GetGroupDoc( rGroup ); + if (pGroup && pGroup->GetCount()) + { + // query which view is registered. In WebWriter there is no normal view + SfxInterfaceId nViewId = nullptr != SwView::Factory() ? SFX_INTERFACE_SFXDOCSH : SfxInterfaceId(6); + const OUString sLongName = pGroup->GetLongName(pGroup->GetIndex( rShortName )); + + if( SfxInterfaceId(6) == nViewId ) + { + SwWebGlosDocShell* pDocSh = new SwWebGlosDocShell(); + xDocSh = pDocSh; + pDocSh->DoInitNew(); + pDocSh->SetLongName( sLongName ); + pDocSh->SetShortName( rShortName); + pDocSh->SetGroupName( rGroup ); + } + else + { + SwGlosDocShell* pDocSh = new SwGlosDocShell(bShow); + xDocSh = pDocSh; + pDocSh->DoInitNew(); + pDocSh->SetLongName( sLongName ); + pDocSh->SetShortName( rShortName ); + pDocSh->SetGroupName( rGroup ); + } + + // set document title + SfxViewFrame* pFrame = bShow ? SfxViewFrame::LoadDocument( *xDocSh, nViewId ) : SfxViewFrame::LoadHiddenDocument( *xDocSh, nViewId ); + const OUString aDocTitle(SwResId( STR_GLOSSARY ) + " " + sLongName); + + bool const bDoesUndo = + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoesUndo(); + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoUndo( false ); + + xDocSh->GetWrtShell()->InsertGlossary( *pGroup, rShortName ); + if( !xDocSh->GetDoc()->getIDocumentDeviceAccess().getPrinter( false ) ) + { + // we create a default SfxPrinter. + // ItemSet is deleted by Sfx! + auto pSet = std::make_unique<SfxItemSet>( + xDocSh->GetDoc()->GetAttrPool(), + svl::Items< + SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER>{}); + VclPtr<SfxPrinter> pPrinter = VclPtr<SfxPrinter>::Create( std::move(pSet) ); + + // and append it to the document. + xDocSh->GetDoc()->getIDocumentDeviceAccess().setPrinter( pPrinter, true, true ); + } + + xDocSh->SetTitle( aDocTitle ); + try + { + // set the UI-title + uno::Reference< frame::XTitle > xTitle( xDocSh->GetModel(), uno::UNO_QUERY_THROW ); + xTitle->setTitle( aDocTitle ); + } + catch (const uno::Exception&) + { + } + + xDocSh->GetDoc()->GetIDocumentUndoRedo().DoUndo( bDoesUndo ); + xDocSh->GetDoc()->getIDocumentState().ResetModified(); + if ( bShow ) + pFrame->GetFrame().Appear(); + } + return xDocSh; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/numberingtypelistbox.cxx b/sw/source/uibase/misc/numberingtypelistbox.cxx new file mode 100644 index 000000000..d484cce5d --- /dev/null +++ b/sw/source/uibase/misc/numberingtypelistbox.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 <numberingtypelistbox.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/text/DefaultNumberingProvider.hpp> +#include <com/sun/star/text/XDefaultNumberingProvider.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/text/XNumberingTypeInfo.hpp> +#include <editeng/numitem.hxx> +#include <svx/strarray.hxx> +#include <osl/diagnose.h> + +using namespace com::sun::star; + +struct SwNumberingTypeListBox_Impl +{ + uno::Reference<text::XNumberingTypeInfo> xInfo; +}; + +SwNumberingTypeListBox::SwNumberingTypeListBox(std::unique_ptr<weld::ComboBox> pWidget) + : m_xWidget(std::move(pWidget)) + , m_xImpl(new SwNumberingTypeListBox_Impl) +{ + uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<text::XDefaultNumberingProvider> xDefNum = text::DefaultNumberingProvider::create(xContext); + m_xImpl->xInfo.set(xDefNum, uno::UNO_QUERY); +} + +SwNumberingTypeListBox::~SwNumberingTypeListBox() +{ +} + +void SwNumberingTypeListBox::Reload(SwInsertNumTypes nTypeFlags) +{ + m_xWidget->clear(); + uno::Sequence<sal_Int16> aTypes; + if (nTypeFlags & SwInsertNumTypes::Extended) + { + if (m_xImpl->xInfo.is()) + aTypes = m_xImpl->xInfo->getSupportedNumberingTypes(); + } + + for(size_t i = 0; i < SvxNumberingTypeTable::Count(); i++) + { + sal_IntPtr nValue = SvxNumberingTypeTable::GetValue(i); + bool bInsert = true; + int nPos = -1; + switch(nValue) + { + case style::NumberingType::NUMBER_NONE: + bInsert = bool(nTypeFlags & SwInsertNumTypes::NoNumbering); + nPos = 0; + + break; + case style::NumberingType::CHAR_SPECIAL: + bInsert = false; + + break; + case style::NumberingType::PAGE_DESCRIPTOR: + bInsert = false; + + break; + case style::NumberingType::BITMAP: + bInsert = false; + + break; + case style::NumberingType::BITMAP | LINK_TOKEN: + bInsert = false; + + break; + default: + if (nValue > style::NumberingType::CHARS_LOWER_LETTER_N) + { + // Insert only if offered by i18n framework per configuration. + bInsert = std::find(aTypes.begin(), aTypes.end(), nValue) != aTypes.end(); + } + } + if (bInsert) + { + OUString sId(OUString::number(nValue)); + m_xWidget->insert(nPos, SvxNumberingTypeTable::GetString(i), &sId, nullptr, nullptr); + } + } + if (nTypeFlags & SwInsertNumTypes::Extended) + { + for (sal_Int16 nCurrent : aTypes) + { + if (nCurrent > style::NumberingType::CHARS_LOWER_LETTER_N) + { + if (m_xWidget->find_id(OUString::number(nCurrent)) == -1) + { + m_xWidget->append(OUString::number(nCurrent), m_xImpl->xInfo->getNumberingIdentifier(nCurrent)); + } + } + } + m_xWidget->set_active(0); + } +} + +SvxNumType SwNumberingTypeListBox::GetSelectedNumberingType() const +{ + SvxNumType nRet = SVX_NUM_CHARS_UPPER_LETTER; + int nSelPos = m_xWidget->get_active(); + if (nSelPos != -1) + nRet = static_cast<SvxNumType>(m_xWidget->get_id(nSelPos).toInt32()); +#if OSL_DEBUG_LEVEL > 0 + else + OSL_FAIL("NumberingTypeListBox not selected"); +#endif + return nRet; +} + +bool SwNumberingTypeListBox::SelectNumberingType(SvxNumType nType) +{ + int nPos = m_xWidget->find_id(OUString::number(nType)); + m_xWidget->set_active(nPos); + return nPos != -1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/redlndlg.cxx b/sw/source/uibase/misc/redlndlg.cxx new file mode 100644 index 000000000..a18506497 --- /dev/null +++ b/sw/source/uibase/misc/redlndlg.cxx @@ -0,0 +1,1261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <redline.hxx> +#include <tools/datetime.hxx> +#include <tools/lineend.hxx> +#include <svl/eitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ctredlin.hxx> +#include <svx/postattr.hxx> +#include <vcl/commandevent.hxx> +#include <swtypes.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <redlndlg.hxx> +#include <swwait.hxx> +#include <uitool.hxx> + +#include <helpids.h> +#include <cmdid.h> +#include <strings.hrc> + +// -> #111827# +#include <swundo.hxx> +#include <SwRewriter.hxx> +// <- #111827# + +#include <vector> +#include <svx/svxdlg.hxx> +#include <bitmaps.hlst> + +#include <docsh.hxx> + +#include <IDocumentRedlineAccess.hxx> +#include <memory> + +SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(SwRedlineAcceptChild, FN_REDLINE_ACCEPT) + +SwRedlineAcceptChild::SwRedlineAcceptChild(vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : SwChildWinWrapper(_pParent, nId) +{ + auto xDlg = std::make_shared<SwModelessRedlineAcceptDlg>(pBindings, this, _pParent->GetFrameWeld()); + SetController(xDlg); + xDlg->Initialize(pInfo); +} + +// newly initialise dialog after document switch +bool SwRedlineAcceptChild::ReInitDlg(SwDocShell *pDocSh) +{ + bool bRet = SwChildWinWrapper::ReInitDlg(pDocSh); + if (bRet) // update immediately, doc switch! + static_cast<SwModelessRedlineAcceptDlg*>(GetController().get())->Activate(); + + return bRet; +} + +SwModelessRedlineAcceptDlg::SwModelessRedlineAcceptDlg( + SfxBindings* _pBindings, SwChildWinWrapper* pChild, weld::Window *pParent) + : SfxModelessDialogController(_pBindings, pChild, pParent, + "svx/ui/acceptrejectchangesdialog.ui", "AcceptRejectChangesDialog") + , m_xContentArea(m_xDialog->weld_content_area()) + , pChildWin(pChild) +{ + m_xImplDlg.reset(new SwRedlineAcceptDlg(m_xDialog, m_xBuilder.get(), m_xContentArea.get())); +} + +void SwModelessRedlineAcceptDlg::Activate() +{ + SwView *pView = ::GetActiveView(); + if (!pView) // can happen when switching to another app, when a Listbox in dialog + return; // had the focus previously (actually THs Bug) + + SwDocShell *pDocSh = pView->GetDocShell(); + + if (pChildWin->GetOldDocShell() != pDocSh) + { // doc-switch + SwWait aWait( *pDocSh, false ); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + + pChildWin->SetOldDocShell(pDocSh); // avoid recursion (using modified-Hdl) + + bool bMod = pSh->IsModified(); + SfxBoolItem aShow(FN_REDLINE_SHOW, true); + pSh->GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + FN_REDLINE_SHOW, SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aShow }); + if (!bMod) + pSh->ResetModified(); + m_xImplDlg->Init(); + SfxModelessDialogController::Activate(); + + return; + } + + SfxModelessDialogController::Activate(); + m_xImplDlg->Activate(); +} + +void SwModelessRedlineAcceptDlg::Initialize(SfxChildWinInfo* pInfo) +{ + if (pInfo != nullptr) + m_xImplDlg->Initialize(pInfo->aExtraString); + + SfxModelessDialogController::Initialize(pInfo); +} + +void SwModelessRedlineAcceptDlg::FillInfo(SfxChildWinInfo& rInfo) const +{ + SfxModelessDialogController::FillInfo(rInfo); + m_xImplDlg->FillInfo(rInfo.aExtraString); +} + +SwModelessRedlineAcceptDlg::~SwModelessRedlineAcceptDlg() +{ +} + +SwRedlineAcceptDlg::SwRedlineAcceptDlg(const std::shared_ptr<weld::Window>& rParent, weld::Builder *pBuilder, + weld::Container *pContentArea, bool bAutoFormat) + : m_xParentDlg(rParent) + , m_sInserted(SwResId(STR_REDLINE_INSERTED)) + , m_sDeleted(SwResId(STR_REDLINE_DELETED)) + , m_sFormated(SwResId(STR_REDLINE_FORMATTED)) + , m_sTableChgd(SwResId(STR_REDLINE_TABLECHG)) + , m_sFormatCollSet(SwResId(STR_REDLINE_FMTCOLLSET)) + , m_sAutoFormat(SwResId(STR_REDLINE_AUTOFMT)) + , m_bOnlyFormatedRedlines(false) + , m_bRedlnAutoFormat(bAutoFormat) + , m_bInhibitActivate(false) + , m_xTabPagesCTRL(new SvxAcceptChgCtr(pContentArea, m_xParentDlg.get(), pBuilder)) + , m_xPopup(pBuilder->weld_menu("writermenu")) +{ + m_xTabPagesCTRL->set_help_id(HID_REDLINE_CTRL); + m_pTPView = m_xTabPagesCTRL->GetViewPage(); + + m_pTable = m_pTPView->GetTableControl(); + m_pTable->SetWriterView(); + + m_pTPView->SetAcceptClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptHdl)); + m_pTPView->SetAcceptAllClickHdl(LINK(this, SwRedlineAcceptDlg, AcceptAllHdl)); + m_pTPView->SetRejectClickHdl(LINK(this, SwRedlineAcceptDlg, RejectHdl)); + m_pTPView->SetRejectAllClickHdl(LINK(this, SwRedlineAcceptDlg, RejectAllHdl)); + m_pTPView->SetUndoClickHdl(LINK(this, SwRedlineAcceptDlg, UndoHdl)); + //tdf#89227 default to disabled, and only enable if possible to accept/reject + m_pTPView->EnableAccept(false); + m_pTPView->EnableReject(false); + m_pTPView->EnableAcceptAll(false); + m_pTPView->EnableRejectAll(false); + + m_xTabPagesCTRL->GetFilterPage()->SetReadyHdl(LINK(this, SwRedlineAcceptDlg, FilterChangedHdl)); + + weld::ComboBox* pActLB = m_xTabPagesCTRL->GetFilterPage()->GetLbAction(); + pActLB->append_text(m_sInserted); + pActLB->append_text(m_sDeleted); + pActLB->append_text(m_sFormated); + pActLB->append_text(m_sTableChgd); + + if (HasRedlineAutoFormat()) + { + pActLB->append_text(m_sFormatCollSet); + pActLB->append_text(m_sAutoFormat); + m_pTPView->ShowUndo(); + m_pTPView->DisableUndo(); // no UNDO events yet + } + + pActLB->set_active(0); + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + rTreeView.set_selection_mode(SelectionMode::Multiple); + + rTreeView.connect_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl)); + rTreeView.connect_popup_menu(LINK(this, SwRedlineAcceptDlg, CommandHdl)); + + // avoid multiple selection of the same texts: + m_aSelectTimer.SetTimeout(100); + m_aSelectTimer.SetInvokeHandler(LINK(this, SwRedlineAcceptDlg, GotoHdl)); +} + +SwRedlineAcceptDlg::~SwRedlineAcceptDlg() +{ +} + +void SwRedlineAcceptDlg::Init(SwRedlineTable::size_type nStart) +{ + SwWait aWait( *::GetActiveView()->GetDocShell(), false ); + weld::TreeView& rTreeView = m_pTable->GetWidget(); + m_aUsedSeqNo.clear(); + + rTreeView.freeze(); + if (nStart) + RemoveParents(nStart, m_RedlineParents.size() - 1); + else + { + rTreeView.clear(); + m_RedlineChildren.clear(); + m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.end()); + } + rTreeView.thaw(); + + // insert parents + InsertParents(nStart); + InitAuthors(); + + // #i69618# this moves the list box to the right position, visually + std::unique_ptr<weld::TreeIter> xSelEntry(rTreeView.make_iterator()); + if (rTreeView.get_selected(xSelEntry.get())) + rTreeView.scroll_to_row(*xSelEntry); //#i70937#, force the scroll +} + +void SwRedlineAcceptDlg::InitAuthors() +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + + if (!m_xTabPagesCTRL) + return; + + SvxTPFilter *pFilterPage = m_xTabPagesCTRL->GetFilterPage(); + + std::vector<OUString> aStrings; + OUString sOldAuthor(pFilterPage->GetSelectedAuthor()); + pFilterPage->ClearAuthors(); + + SwRedlineTable::size_type nCount = pSh->GetRedlineCount(); + + m_bOnlyFormatedRedlines = true; + bool bIsNotFormated = false; + + // determine authors + for ( SwRedlineTable::size_type i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + + if( m_bOnlyFormatedRedlines && RedlineType::Format != rRedln.GetType() ) + m_bOnlyFormatedRedlines = false; + + aStrings.push_back(rRedln.GetAuthorString()); + + for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++) + { + aStrings.push_back(rRedln.GetAuthorString(nStack)); + } + } + + std::sort(aStrings.begin(), aStrings.end()); + aStrings.erase(std::unique(aStrings.begin(), aStrings.end()), aStrings.end()); + + for (auto const & i: aStrings) + pFilterPage->InsertAuthor(i); + + if (pFilterPage->SelectAuthor(sOldAuthor) == -1 && !aStrings.empty()) + pFilterPage->SelectAuthor(aStrings[0]); + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + bool bEnable = rTreeView.n_children() != 0 && !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements(); + bool bSel = rTreeView.get_selected(nullptr); + + rTreeView.selected_foreach([this, pSh, &bIsNotFormated](weld::TreeIter& rEntry){ + // find the selected redline + // (fdo#57874: ignore, if the redline is already gone) + SwRedlineTable::size_type nPos = GetRedlinePos(rEntry); + if( nPos != SwRedlineTable::npos ) + { + const SwRangeRedline& rRedln = pSh->GetRedline( nPos ); + + bIsNotFormated |= RedlineType::Format != rRedln.GetType(); + } + return false; + }); + + m_pTPView->EnableAccept( bEnable && bSel ); + m_pTPView->EnableReject( bEnable && bSel ); + m_pTPView->EnableClearFormat( bEnable && !bIsNotFormated && bSel ); + m_pTPView->EnableAcceptAll( bEnable ); + m_pTPView->EnableRejectAll( bEnable ); + m_pTPView->EnableClearFormatAll( bEnable && + m_bOnlyFormatedRedlines ); +} + +OUString SwRedlineAcceptDlg::GetActionImage(const SwRangeRedline& rRedln, sal_uInt16 nStack) +{ + switch (rRedln.GetType(nStack)) + { + case RedlineType::Insert: return BMP_REDLINE_INSERTED; + case RedlineType::Delete: return BMP_REDLINE_DELETED; + case RedlineType::Format: return BMP_REDLINE_FORMATTED; + case RedlineType::ParagraphFormat: return BMP_REDLINE_FORMATTED; + case RedlineType::Table: return BMP_REDLINE_TABLECHG; + case RedlineType::FmtColl: return BMP_REDLINE_FMTCOLLSET; + default: break; + } + + return OUString(); +} + +OUString SwRedlineAcceptDlg::GetActionText(const SwRangeRedline& rRedln, sal_uInt16 nStack) +{ + switch( rRedln.GetType(nStack) ) + { + case RedlineType::Insert: return m_sInserted; + case RedlineType::Delete: return m_sDeleted; + case RedlineType::Format: return m_sFormated; + case RedlineType::ParagraphFormat: return m_sFormated; + case RedlineType::Table: return m_sTableChgd; + case RedlineType::FmtColl: return m_sFormatCollSet; + default:;//prevent warning + } + + return OUString(); +} + +// newly initialise after activation +void SwRedlineAcceptDlg::Activate() +{ + // prevent update if flag is set (#102547#) + if( m_bInhibitActivate ) + return; + + SwView *pView = ::GetActiveView(); + if (!pView) // can happen when switching to another app + { + m_pTPView->EnableAccept(false); + m_pTPView->EnableReject(false); + m_pTPView->EnableAcceptAll(false); + m_pTPView->EnableRejectAll(false); + return; // had the focus previously + } + + SwWait aWait( *pView->GetDocShell(), false ); + + m_aUsedSeqNo.clear(); + + // did something change? + SwWrtShell* pSh = pView->GetWrtShellPtr(); + SwRedlineTable::size_type nCount = pSh->GetRedlineCount(); + + // check the number of pointers + for ( SwRedlineTable::size_type i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + + if (i >= m_RedlineParents.size()) + { + // new entries have been appended + Init(i); + return; + } + + SwRedlineDataParent *const pParent = m_RedlineParents[i].get(); + if (&rRedln.GetRedlineData() != pParent->pData) + { + // Redline-Parents were inserted, changed or deleted + if ((i = CalcDiff(i, false)) == SwRedlineTable::npos) + return; + continue; + } + + const SwRedlineData *pRedlineData = rRedln.GetRedlineData().Next(); + const SwRedlineDataChild *pBackupData = pParent->pNext; + + if (!pRedlineData && pBackupData) + { + // Redline-Children were deleted + if ((i = CalcDiff(i, true)) == SwRedlineTable::npos) + return; + continue; + } + else + { + while (pRedlineData) + { + if (pRedlineData != pBackupData->pChild) + { + // Redline-Children were inserted, changed or deleted + if ((i = CalcDiff(i, true)) == SwRedlineTable::npos) + return; + continue; + } + pBackupData = pBackupData->pNext; + pRedlineData = pRedlineData->Next(); + } + } + } + + if (nCount != m_RedlineParents.size()) + { + // Redlines were deleted at the end + Init(nCount); + return; + } + + // check comment + weld::TreeView& rTreeView = m_pTable->GetWidget(); + for (SwRedlineTable::size_type i = 0; i < nCount; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + SwRedlineDataParent *const pParent = m_RedlineParents[i].get(); + + if(rRedln.GetComment() != pParent->sComment) + { + if (pParent->xTLBParent) + { + // update only comment + const OUString& sComment(rRedln.GetComment()); + rTreeView.set_text(*pParent->xTLBParent, sComment.replace('\n', ' '), 3); + } + pParent->sComment = rRedln.GetComment(); + } + } + + InitAuthors(); +} + +SwRedlineTable::size_type SwRedlineAcceptDlg::CalcDiff(SwRedlineTable::size_type nStart, bool bChild) +{ + if (!nStart) + { + Init(); + return SwRedlineTable::npos; + } + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + rTreeView.freeze(); + SwView *pView = ::GetActiveView(); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + bool bHasRedlineAutoFormat = HasRedlineAutoFormat(); + SwRedlineDataParent *const pParent = m_RedlineParents[nStart].get(); + const SwRangeRedline& rRedln = pSh->GetRedline(nStart); + + if (bChild) // should actually never happen, but just in case... + { + // throw away all entry's children and initialise newly + SwRedlineDataChild* pBackupData = const_cast<SwRedlineDataChild*>(pParent->pNext); + SwRedlineDataChild* pNext; + + while (pBackupData) + { + pNext = const_cast<SwRedlineDataChild*>(pBackupData->pNext); + if (pBackupData->xTLBChild) + rTreeView.remove(*pBackupData->xTLBChild); + + auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(), + [&pBackupData](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pBackupData; }); + if (it != m_RedlineChildren.end()) + m_RedlineChildren.erase(it); + + pBackupData = pNext; + } + pParent->pNext = nullptr; + + // insert new children + InsertChildren(pParent, rRedln, bHasRedlineAutoFormat); + + rTreeView.thaw(); + return nStart; + } + + // have entries been deleted? + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + for (SwRedlineTable::size_type i = nStart + 1; i < m_RedlineParents.size(); i++) + { + if (m_RedlineParents[i]->pData == pRedlineData) + { + // remove entries from nStart to i-1 + RemoveParents(nStart, i - 1); + rTreeView.thaw(); + return nStart - 1; + } + } + + // entries been inserted? + SwRedlineTable::size_type nCount = pSh->GetRedlineCount(); + pRedlineData = m_RedlineParents[nStart]->pData; + + for (SwRedlineTable::size_type i = nStart + 1; i < nCount; i++) + { + if (&pSh->GetRedline(i).GetRedlineData() == pRedlineData) + { + // insert entries from nStart to i-1 + InsertParents(nStart, i - 1); + rTreeView.thaw(); + return nStart - 1; + } + } + + rTreeView.thaw(); + Init(nStart); // adjust all entries until the end + return SwRedlineTable::npos; +} + +void SwRedlineAcceptDlg::InsertChildren(SwRedlineDataParent *pParent, const SwRangeRedline& rRedln, bool bHasRedlineAutoFormat) +{ + SwRedlineDataChild *pLastRedlineChild = nullptr; + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + bool bAutoFormatRedline = rRedln.IsAutoFormat(); + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + + OUString sAction = GetActionText(rRedln); + bool bValidParent = m_sFilterAction.isEmpty() || m_sFilterAction == sAction; + bValidParent = bValidParent && m_pTable->IsValidEntry(rRedln.GetAuthorString(), rRedln.GetTimeStamp(), rRedln.GetComment()); + if (bHasRedlineAutoFormat) + { + + if (pParent->pData->GetSeqNo()) + { + std::pair<SwRedlineDataParentSortArr::const_iterator,bool> const ret + = m_aUsedSeqNo.insert(pParent); + if (ret.second) // already there + { + if (pParent->xTLBParent) + { + rTreeView.set_text(*(*ret.first)->xTLBParent, m_sAutoFormat, 0); + rTreeView.remove(*pParent->xTLBParent); + pParent->xTLBParent.reset(); + } + return; + } + } + bValidParent = bValidParent && bAutoFormatRedline; + } + bool bValidTree = bValidParent; + + for (sal_uInt16 nStack = 1; nStack < rRedln.GetStackCount(); nStack++) + { + pRedlineData = pRedlineData->Next(); + + SwRedlineDataChild* pRedlineChild = new SwRedlineDataChild; + pRedlineChild->pChild = pRedlineData; + m_RedlineChildren.push_back(std::unique_ptr<SwRedlineDataChild>(pRedlineChild)); + + if ( pLastRedlineChild ) + pLastRedlineChild->pNext = pRedlineChild; + else + pParent->pNext = pRedlineChild; + + sAction = GetActionText(rRedln, nStack); + bool bValidChild = m_sFilterAction.isEmpty() || m_sFilterAction == sAction; + bValidChild = bValidChild && m_pTable->IsValidEntry(rRedln.GetAuthorString(nStack), rRedln.GetTimeStamp(nStack), rRedln.GetComment()); + if (bHasRedlineAutoFormat) + bValidChild = bValidChild && bAutoFormatRedline; + bValidTree |= bValidChild; + + if (bValidChild) + { + std::unique_ptr<RedlinData> pData(new RedlinData); + pData->pData = pRedlineChild; + pData->bDisabled = true; + + OUString sImage(GetActionImage(rRedln, nStack)); + OUString sAuthor = rRedln.GetAuthorString(nStack); + pData->aDateTime = rRedln.GetTimeStamp(nStack); + pData->eType = rRedln.GetType(nStack); + OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime); + OUString sComment = rRedln.GetComment(nStack); + + std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator()); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pData.release()))); + rTreeView.insert(pParent->xTLBParent.get(), -1, nullptr, &sId, nullptr, nullptr, + nullptr, false, xChild.get()); + + rTreeView.set_image(*xChild, sImage, -1); + rTreeView.set_text(*xChild, sAuthor, 1); + rTreeView.set_text(*xChild, sDateEntry, 2); + rTreeView.set_text(*xChild, sComment, 3); + + pRedlineChild->xTLBChild = std::move(xChild); + if (!bValidParent) + rTreeView.expand_row(*pParent->xTLBParent); + } + else + pRedlineChild->xTLBChild.reset(); + + pLastRedlineChild = pRedlineChild; + } + + if (pLastRedlineChild) + pLastRedlineChild->pNext = nullptr; + + if (!bValidTree && pParent->xTLBParent) + { + rTreeView.remove(*pParent->xTLBParent); + pParent->xTLBParent.reset(); + if (bHasRedlineAutoFormat) + m_aUsedSeqNo.erase(pParent); + } +} + +void SwRedlineAcceptDlg::RemoveParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + SwRedlineTable::size_type nCount = pSh->GetRedlineCount(); + + std::vector<const weld::TreeIter*> aLBoxArr; + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + + // because of Bug of TLB that ALWAYS calls the SelectHandler at Remove: + rTreeView.connect_changed(Link<weld::TreeView&,void>()); + + bool bChildrenRemoved = false; + rTreeView.thaw(); + rTreeView.unselect_all(); + + // set the cursor after the last entry because otherwise performance problem in TLB. + // TLB would otherwise reset the cursor at every Remove (expensive) + SwRedlineTable::size_type nPos = std::min(nCount, m_RedlineParents.size()); + weld::TreeIter *pCurEntry = nullptr; + while( ( pCurEntry == nullptr ) && ( nPos > 0 ) ) + { + --nPos; + pCurEntry = m_RedlineParents[nPos]->xTLBParent.get(); + } + + if (pCurEntry) + rTreeView.set_cursor(*pCurEntry); + + rTreeView.freeze(); + + for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++) + { + if (!bChildrenRemoved && m_RedlineParents[i]->pNext) + { + SwRedlineDataChild * pChildPtr = + const_cast<SwRedlineDataChild*>(m_RedlineParents[i]->pNext); + auto it = std::find_if(m_RedlineChildren.begin(), m_RedlineChildren.end(), + [&pChildPtr](const std::unique_ptr<SwRedlineDataChild>& rChildPtr) { return rChildPtr.get() == pChildPtr; }); + if (it != m_RedlineChildren.end()) + { + sal_uInt16 nChildren = 0; + while (pChildPtr) + { + pChildPtr = const_cast<SwRedlineDataChild*>(pChildPtr->pNext); + nChildren++; + } + + m_RedlineChildren.erase(it, it + nChildren); + bChildrenRemoved = true; + } + } + weld::TreeIter *const pEntry = m_RedlineParents[i]->xTLBParent.get(); + if (pEntry) + aLBoxArr.push_back(pEntry); + } + + std::sort(aLBoxArr.begin(), aLBoxArr.end(), [&rTreeView](const weld::TreeIter* a, const weld::TreeIter* b) { + return rTreeView.iter_compare(*a, *b) == -1; + }); + // clear TLB from behind + for (auto it = aLBoxArr.rbegin(); it != aLBoxArr.rend(); ++it) + { + const weld::TreeIter* pIter = *it; + rTreeView.remove(*pIter); + } + + rTreeView.thaw(); + rTreeView.connect_changed(LINK(this, SwRedlineAcceptDlg, SelectHdl)); + // unfortunately by Remove it was selected from the TLB always again ... + rTreeView.unselect_all(); + rTreeView.freeze(); + + m_RedlineParents.erase(m_RedlineParents.begin() + nStart, m_RedlineParents.begin() + nEnd + 1); +} + +void SwRedlineAcceptDlg::InsertParents(SwRedlineTable::size_type nStart, SwRedlineTable::size_type nEnd) +{ + SwView *pView = ::GetActiveView(); + SwWrtShell* pSh = pView->GetWrtShellPtr(); + bool bHasRedlineAutoFormat = HasRedlineAutoFormat(); + + SwRedlineTable::size_type nCount = pSh->GetRedlineCount(); + nEnd = std::min(nEnd, (nCount - 1)); // also treats nEnd=SwRedlineTable::npos (until the end) + + if (nEnd == SwRedlineTable::npos) + return; // no redlines in the document + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + + SwRedlineDataParent* pRedlineParent; + const SwRangeRedline* pCurrRedline; + if (!nStart && !rTreeView.get_selected(nullptr)) + { + pCurrRedline = pSh->GetCurrRedline(); + if( !pCurrRedline ) + { + pSh->SwCursorShell::Push(); + if( nullptr == (pCurrRedline = pSh->SelNextRedline())) + pCurrRedline = pSh->SelPrevRedline(); + pSh->SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + else + pCurrRedline = nullptr; + + rTreeView.freeze(); + if (m_pTable->IsSorted()) + rTreeView.make_unsorted(); + for (SwRedlineTable::size_type i = nStart; i <= nEnd; i++) + { + const SwRangeRedline& rRedln = pSh->GetRedline(i); + const SwRedlineData *pRedlineData = &rRedln.GetRedlineData(); + + pRedlineParent = new SwRedlineDataParent; + pRedlineParent->pData = pRedlineData; + pRedlineParent->pNext = nullptr; + const OUString& sComment(rRedln.GetComment()); + pRedlineParent->sComment = sComment.replace('\n', ' '); + m_RedlineParents.insert(m_RedlineParents.begin() + i, + std::unique_ptr<SwRedlineDataParent>(pRedlineParent)); + + std::unique_ptr<RedlinData> pData(new RedlinData); + pData->pData = pRedlineParent; + pData->bDisabled = false; + + OUString sImage = GetActionImage(rRedln); + OUString sAuthor = rRedln.GetAuthorString(0); + pData->aDateTime = rRedln.GetTimeStamp(0); + pData->eType = rRedln.GetType(0); + OUString sDateEntry = GetAppLangDateTimeString(pData->aDateTime); + + OUString sId = OUString::number(reinterpret_cast<sal_Int64>(pData.release())); + std::unique_ptr<weld::TreeIter> xParent(rTreeView.make_iterator()); + rTreeView.insert(nullptr, i, nullptr, &sId, nullptr, nullptr, nullptr, false, xParent.get()); + + rTreeView.set_image(*xParent, sImage, -1); + rTreeView.set_text(*xParent, sAuthor, 1); + rTreeView.set_text(*xParent, sDateEntry, 2); + rTreeView.set_text(*xParent, sComment, 3); + + if (pCurrRedline == &rRedln) + { + rTreeView.thaw(); + rTreeView.set_cursor(*xParent); + rTreeView.select(*xParent); + rTreeView.scroll_to_row(*xParent); + rTreeView.freeze(); + } + + pRedlineParent->xTLBParent = std::move(xParent); + + InsertChildren(pRedlineParent, rRedln, bHasRedlineAutoFormat); + } + rTreeView.thaw(); + if (m_pTable->IsSorted()) + rTreeView.make_sorted(); +} + +void SwRedlineAcceptDlg::CallAcceptReject( bool bSelect, bool bAccept ) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + int nPos = -1; + + typedef std::vector<std::unique_ptr<weld::TreeIter>> ListBoxEntries_t; + ListBoxEntries_t aRedlines; + + // don't activate + OSL_ENSURE( !m_bInhibitActivate, + "recursive call of CallAcceptReject?"); + m_bInhibitActivate = true; + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + + auto lambda = [this, pSh, bSelect, bAccept, &rTreeView, &nPos, &aRedlines](weld::TreeIter& rEntry) { + if (!rTreeView.get_iter_depth(rEntry)) + { + if (bSelect && nPos == -1) + nPos = rTreeView.get_iter_index_in_parent(rEntry); + + RedlinData *pData = reinterpret_cast<RedlinData*>(rTreeView.get_id(rEntry).toInt64()); + + bool bIsNotFormatted = true; + + // first remove only changes with insertion/deletion, if they exist + // (format-only changes haven't had real rejection yet, only an + // approximation: clear direct formatting, so try to warn + // with the extended button label "Reject All/Clear formatting") + if ( !bSelect && !bAccept && !m_bOnlyFormatedRedlines ) + { + SwRedlineTable::size_type nPosition = GetRedlinePos(rEntry); + const SwRangeRedline& rRedln = pSh->GetRedline(nPosition); + + if( RedlineType::Format == rRedln.GetType() ) + bIsNotFormatted = false; + } + + if (!pData->bDisabled && bIsNotFormatted) + aRedlines.emplace_back(rTreeView.make_iterator(&rEntry)); + } + return false; + }; + + // collect redlines-to-be-accepted/rejected in aRedlines vector + if (bSelect) + rTreeView.selected_foreach(lambda); + else + rTreeView.all_foreach(lambda); + + bool (SwEditShell:: *FnAccRej)( SwRedlineTable::size_type ) = &SwEditShell::AcceptRedline; + if( !bAccept ) + FnAccRej = &SwEditShell::RejectRedline; + + SwWait aWait( *pSh->GetView().GetDocShell(), true ); + pSh->StartAction(); + + if (aRedlines.size() > 1) + { + OUString aTmpStr; + { + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, + OUString::number(aRedlines.size())); + aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES)); + } + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aTmpStr); + + pSh->StartUndo(bAccept? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, + &aRewriter); + } + + // accept/reject the redlines in aRedlines. The absolute + // position may change during the process (e.g. when two redlines + // are merged in result of another one being deleted), so the + // position must be resolved late and checked before using it. + // (cf #102547#) + for (const auto& rRedLine : aRedlines) + { + SwRedlineTable::size_type nPosition = GetRedlinePos( *rRedLine ); + if( nPosition != SwRedlineTable::npos ) + (pSh->*FnAccRej)( nPosition ); + } + + if (aRedlines.size() > 1) + { + pSh->EndUndo(); + } + + pSh->EndAction(); + + m_bInhibitActivate = false; + Activate(); + + if (nPos != -1 && rTreeView.n_children()) + { + if (nPos >= rTreeView.n_children()) + nPos = rTreeView.n_children() - 1; + rTreeView.select(nPos); + rTreeView.scroll_to_row(nPos); + rTreeView.set_cursor(nPos); + SelectHdl(rTreeView); + } + m_pTPView->EnableUndo(); +} + +SwRedlineTable::size_type SwRedlineAcceptDlg::GetRedlinePos(const weld::TreeIter& rEntry) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + weld::TreeView& rTreeView = m_pTable->GetWidget(); + return pSh->FindRedlineOfData( *static_cast<SwRedlineDataParent*>(reinterpret_cast<RedlinData*>( + rTreeView.get_id(rEntry).toInt64())->pData)->pData ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptHdl, SvxTPView*, void) +{ + CallAcceptReject( true, true ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, AcceptAllHdl, SvxTPView*, void) +{ + CallAcceptReject( false, true ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectHdl, SvxTPView*, void) +{ + CallAcceptReject( true, false ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, RejectAllHdl, SvxTPView*, void) +{ + CallAcceptReject( false, false ); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, UndoHdl, SvxTPView*, void) +{ + SwView * pView = ::GetActiveView(); + pView->GetViewFrame()->GetDispatcher()-> + Execute(SID_UNDO, SfxCallMode::SYNCHRON); + m_pTPView->EnableUndo(pView->GetSlotState(SID_UNDO) != nullptr); + + Activate(); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, FilterChangedHdl, SvxTPFilter*, void) +{ + SvxTPFilter *pFilterTP = m_xTabPagesCTRL->GetFilterPage(); + + if (pFilterTP->IsAction()) + m_sFilterAction = pFilterTP->GetLbAction()->get_active_text(); + else + m_sFilterAction.clear(); + + Init(); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, SelectHdl, weld::TreeView&, void) +{ + m_aSelectTimer.Start(); +} + +IMPL_LINK_NOARG(SwRedlineAcceptDlg, GotoHdl, Timer *, void) +{ + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + m_aSelectTimer.Stop(); + + bool bIsNotFormated = false; + bool bSel = false; + + //#98883# don't select redlines while the dialog is not focused + //#107938# But not only ask pTable if it has the focus. To move + // the selection to the selected redline any child of pParentDlg + // may the focus. + if (!m_xParentDlg || m_xParentDlg->has_toplevel_focus()) + { + weld::TreeView& rTreeView = m_pTable->GetWidget(); + std::unique_ptr<weld::TreeIter> xActEntry(rTreeView.make_iterator()); + if (rTreeView.get_selected(xActEntry.get())) + { + pSh->StartAction(); + pSh->EnterStdMode(); + SwViewShell::SetCareDialog(m_xParentDlg); + + rTreeView.selected_foreach([this, pSh, &rTreeView, &xActEntry, &bIsNotFormated, &bSel](weld::TreeIter& rEntry){ + rTreeView.copy_iterator(rEntry, *xActEntry); + if (rTreeView.get_iter_depth(rEntry)) + { + rTreeView.iter_parent(*xActEntry); + if (rTreeView.is_selected(*xActEntry)) + return false; // don't select twice + } + else + bSel = true; + + // #98864# find the selected redline (ignore, if the redline is already gone) + SwRedlineTable::size_type nPos = GetRedlinePos(*xActEntry); + if (nPos != SwRedlineTable::npos) + { + + const SwRangeRedline& rRedln = pSh->GetRedline( nPos ); + bIsNotFormated |= RedlineType::Format != rRedln.GetType(); + + if (pSh->GotoRedline(nPos, true)) + { + pSh->SetInSelect(); + pSh->EnterAddMode(); + } + } + return false; + }); + + pSh->LeaveAddMode(); + pSh->EndAction(); + SwViewShell::SetCareDialog(nullptr); + } + } + + bool bEnable = !pSh->getIDocumentRedlineAccess().GetRedlinePassword().hasElements(); + m_pTPView->EnableAccept( bEnable && bSel /*&& !bReadonlySel*/ ); + m_pTPView->EnableReject( bEnable && bSel /*&& !bReadonlySel*/ ); + m_pTPView->EnableClearFormat( bEnable && bSel && !bIsNotFormated /*&& !bReadonlySel*/ ); + m_pTPView->EnableRejectAll( bEnable ); + m_pTPView->EnableClearFormatAll( bEnable && m_bOnlyFormatedRedlines ); +} + +IMPL_LINK(SwRedlineAcceptDlg, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + SwWrtShell* pSh = ::GetActiveView()->GetWrtShellPtr(); + const SwRangeRedline *pRed = nullptr; + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + bool bEntry = rTreeView.get_selected(xEntry.get()); + if (bEntry) + { + std::unique_ptr<weld::TreeIter> xTopEntry(rTreeView.make_iterator(xEntry.get())); + + if (rTreeView.get_iter_depth(*xTopEntry)) + rTreeView.iter_parent(*xTopEntry); + + SwRedlineTable::size_type nPos = GetRedlinePos(*xTopEntry); + + // disable commenting for protected areas + if (nPos != SwRedlineTable::npos && (pRed = pSh->GotoRedline(nPos, true)) != nullptr) + { + if( pSh->IsCursorPtAtEnd() ) + pSh->SwapPam(); + pSh->SetInSelect(); + } + } + + m_xPopup->set_sensitive("writeredit", bEntry && pRed && + !rTreeView.get_iter_depth(*xEntry) && + rTreeView.count_selected_rows() == 1); + m_xPopup->set_sensitive("writersort", rTreeView.n_children() != 0); + int nColumn = rTreeView.get_sort_column(); + if (nColumn == -1) + nColumn = 4; + for (sal_Int32 i = 0; i < 5; ++i) + m_xPopup->set_active("writersort" + OString::number(i), i == nColumn); + + OString sCommand = m_xPopup->popup_at_rect(&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + + if (sCommand == "writeredit") + { + if (bEntry) + { + if (rTreeView.get_iter_depth(*xEntry)) + rTreeView.iter_parent(*xEntry); + + SwRedlineTable::size_type nPos = GetRedlinePos(*xEntry); + + if (nPos == SwRedlineTable::npos) + return true; + + const SwRangeRedline &rRedline = pSh->GetRedline(nPos); + + /* enable again once we have redline comments in the margin + sComment = rRedline.GetComment(); + if ( !sComment.Len() ) + GetActiveView()->GetDocShell()->Broadcast(SwRedlineHint(&rRedline,SWREDLINE_INSERTED)); + const_cast<SwRangeRedline&>(rRedline).Broadcast(SwRedlineHint(&rRedline,SWREDLINE_FOCUS)); + */ + + OUString sComment = convertLineEnd(rRedline.GetComment(), GetSystemLineEnd()); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ::DialogGetRanges fnGetRange = pFact->GetDialogGetRangesFunc(); + SfxItemSet aSet( pSh->GetAttrPool(), fnGetRange() ); + + aSet.Put(SvxPostItTextItem(sComment, SID_ATTR_POSTIT_TEXT)); + aSet.Put(SvxPostItAuthorItem(rRedline.GetAuthorString(), SID_ATTR_POSTIT_AUTHOR)); + + aSet.Put(SvxPostItDateItem( GetAppLangDateTimeString( + rRedline.GetRedlineData().GetTimeStamp() ), + SID_ATTR_POSTIT_DATE )); + + ScopedVclPtr<AbstractSvxPostItDialog> pDlg(pFact->CreateSvxPostItDialog(&rTreeView, aSet)); + + pDlg->HideAuthor(); + + const char* pResId = nullptr; + switch( rRedline.GetType() ) + { + case RedlineType::Insert: + pResId = STR_REDLINE_INSERTED; + break; + case RedlineType::Delete: + pResId = STR_REDLINE_DELETED; + break; + case RedlineType::Format: + case RedlineType::ParagraphFormat: + pResId = STR_REDLINE_FORMATTED; + break; + case RedlineType::Table: + pResId = STR_REDLINE_TABLECHG; + break; + default:;//prevent warning + } + OUString sTitle(SwResId(STR_REDLINE_COMMENT)); + if (pResId) + sTitle += SwResId(pResId); + pDlg->SetText(sTitle); + + SwViewShell::SetCareDialog(pDlg->GetDialog()); + + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + OUString sMsg(pOutSet->Get(SID_ATTR_POSTIT_TEXT).GetValue()); + + // insert / change comment + pSh->SetRedlineComment(sMsg); + rTreeView.set_text(*xEntry, sMsg.replace('\n', ' '), 3); + Init(); + } + + SwViewShell::SetCareDialog(nullptr); + pDlg.disposeAndClear(); + } + } + else if (!sCommand.isEmpty()) + { + int nSortMode = sCommand.copy(10).toInt32(); + + if (nSortMode == 4 && nColumn == 4) + return true; // we already have it + if (nSortMode == 4) + nSortMode = -1; // unsorted / sorted by position + + SwWait aWait( *::GetActiveView()->GetDocShell(), false ); + m_pTable->HeaderBarClick(nSortMode); + if (nSortMode == -1) + Init(); // newly fill everything + } + return true; +} + +namespace +{ + OUString lcl_StripAcceptChgDat(OUString &rExtraString) + { + OUString aStr; + while(true) + { + sal_Int32 nPos = rExtraString.indexOf("AcceptChgDat:"); + if (nPos == -1) + break; + // try to read the alignment string "ALIGN:(...)"; if none existing, + // it's an old version + sal_Int32 n1 = rExtraString.indexOf('(', nPos); + if (n1 != -1) + { + sal_Int32 n2 = rExtraString.indexOf(')', n1); + if (n2 != -1) + { + // cut out the alignment string + aStr = rExtraString.copy(nPos, n2 - nPos + 1); + rExtraString = rExtraString.replaceAt(nPos, n2 - nPos + 1, ""); + aStr = aStr.copy(n1 - nPos + 1); + } + } + } + return aStr; + } +} + +void SwRedlineAcceptDlg::Initialize(OUString& rExtraString) +{ + if (!rExtraString.isEmpty()) + { + OUString aStr = lcl_StripAcceptChgDat(rExtraString); + if (!aStr.isEmpty()) + { + int nCount = aStr.toInt32(); + if (nCount > 2) + { + std::vector<int> aEndPos; + + for (int i = 0; i < nCount; ++i) + { + sal_Int32 n1 = aStr.indexOf(';'); + aStr = aStr.copy( n1+1 ); + aEndPos.push_back(aStr.toInt32()); + } + + bool bUseless = false; + + std::vector<int> aWidths; + for (int i = 1; i < nCount; ++i) + { + aWidths.push_back(aEndPos[i] - aEndPos[i - 1]); + if (aWidths.back() <= 0) + bUseless = true; + } + + if (!bUseless) + { + // turn column end points back to column widths, ignoring the small + // value used for the expander column + weld::TreeView& rTreeView = m_pTable->GetWidget(); + rTreeView.set_column_fixed_widths(aWidths); + } + } + } + } +} + +void SwRedlineAcceptDlg::FillInfo(OUString &rExtraData) const +{ + //remove any old one before adding a new one + lcl_StripAcceptChgDat(rExtraData); + rExtraData += "AcceptChgDat:("; + + const int nTabCount = 4; + + rExtraData += OUString::number(nTabCount); + rExtraData += ";"; + + weld::TreeView& rTreeView = m_pTable->GetWidget(); + std::vector<int> aWidths; + // turn column widths back into column end points for compatibility + // with how they used to be stored, including a small value for the + // expander column + aWidths.push_back(rTreeView.get_checkbox_column_width()); + for (int i = 0; i < nTabCount - 1; ++i) + { + int nWidth = rTreeView.get_column_width(i); + assert(nWidth > 0 && "suspicious to get a value like this"); + aWidths.push_back(aWidths.back() + nWidth); + } + + for (auto a : aWidths) + { + rExtraData += OUString::number(a); + rExtraData += ";"; + } + rExtraData += ")"; +} + +SwRedlineAcceptPanel::SwRedlineAcceptPanel(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame) + : PanelLayout(pParent, "ManageChangesPanel", "modules/swriter/ui/managechangessidebar.ui", rFrame) + , mxContentArea(m_xBuilder->weld_container("content_area")) +{ + mpImplDlg.reset(new SwRedlineAcceptDlg(nullptr, m_xBuilder.get(), mxContentArea.get())); + + mpImplDlg->Init(); + + // we want to receive SfxHintId::DocChanged + StartListening(*(SW_MOD()->GetView()->GetDocShell())); +} + +SwRedlineAcceptPanel::~SwRedlineAcceptPanel() +{ + disposeOnce(); +} + +void SwRedlineAcceptPanel::dispose() +{ + mpImplDlg.reset(); + mxContentArea.reset(); + PanelLayout::dispose(); +} + +void SwRedlineAcceptPanel::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + if (mpImplDlg && rHint.GetId() == SfxHintId::DocChanged) + mpImplDlg->Activate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/misc/swruler.cxx b/sw/source/uibase/misc/swruler.cxx new file mode 100644 index 000000000..680031a26 --- /dev/null +++ b/sw/source/uibase/misc/swruler.cxx @@ -0,0 +1,371 @@ +/* -*- 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/. + */ + +// Design proposal: https://wiki.documentfoundation.org/Design/Whiteboards/Comments_Ruler_Control + +#include <swruler.hxx> + +#include <viewsh.hxx> +#include <edtwin.hxx> +#include <PostItMgr.hxx> +#include <view.hxx> +#include <cmdid.h> +#include <sfx2/request.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/window.hxx> +#include <vcl/settings.hxx> +#include <strings.hrc> +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <boost/property_tree/json_parser.hpp> + +#define CONTROL_BORDER_WIDTH 1 + +namespace +{ +/** + * Draw a little arrow / triangle with different directions + * + * \param nX left coordinate of arrow square + * \param nY top coordinate of arrow square + * \param nSize size of the long triangle side / arrow square + * \param Color arrow color + * \param bCollapsed if the arrow should display the collapsed state + */ +void ImplDrawArrow(vcl::RenderContext& rRenderContext, long nX, long nY, long nSize, + const Color& rColor, bool bCollapsed) +{ + tools::Polygon aTriaglePolygon(4); + + if (bCollapsed) + { + if (AllSettings::GetLayoutRTL()) // < + { + aTriaglePolygon.SetPoint({ nX + nSize / 2, nY }, 0); + aTriaglePolygon.SetPoint({ nX + nSize / 2, nY + nSize }, 1); + aTriaglePolygon.SetPoint({ nX, nY + nSize / 2 }, 2); + aTriaglePolygon.SetPoint({ nX + nSize / 2, nY }, 3); + } + else // > + { + aTriaglePolygon.SetPoint({ nX, nY }, 0); + aTriaglePolygon.SetPoint({ nX + nSize / 2, nY + nSize / 2 }, 1); + aTriaglePolygon.SetPoint({ nX, nY + nSize }, 2); + aTriaglePolygon.SetPoint({ nX, nY }, 3); + } + } + else // v + { + aTriaglePolygon.SetPoint({ nX, nY + nSize / 2 }, 0); + aTriaglePolygon.SetPoint({ nX + nSize, nY + nSize / 2 }, 1); + aTriaglePolygon.SetPoint({ nX + nSize / 2, nY + nSize }, 2); + aTriaglePolygon.SetPoint({ nX, nY + nSize / 2 }, 3); + } + + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(rColor); + rRenderContext.DrawPolygon(aTriaglePolygon); +} +} + +// Constructor +SwCommentRuler::SwCommentRuler(SwViewShell* pViewSh, vcl::Window* pParent, SwEditWin* pWin, + SvxRulerSupportFlags nRulerFlags, SfxBindings& rBindings, + WinBits nWinStyle) + : SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle | WB_HSCROLL) + , mpViewShell(pViewSh) + , mpSwWin(pWin) + , mbIsHighlighted(false) + , mnFadeRate(0) + , maVirDev(VclPtr<VirtualDevice>::Create(*this)) +{ + // Set fading timeout: 5 x 40ms = 200ms + maFadeTimer.SetTimeout(40); + maFadeTimer.SetInvokeHandler(LINK(this, SwCommentRuler, FadeHandler)); + maFadeTimer.SetDebugName("sw::SwCommentRuler maFadeTimer"); + + // we have a little bit more space, as we don't draw ruler ticks + vcl::Font aFont(maVirDev->GetFont()); + aFont.SetFontHeight(aFont.GetFontHeight() + 1); + maVirDev->SetFont(aFont); +} + +SwCommentRuler::~SwCommentRuler() { disposeOnce(); } + +void SwCommentRuler::dispose() +{ + mpSwWin.clear(); + SvxRuler::dispose(); +} + +void SwCommentRuler::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (comphelper::LibreOfficeKit::isActive()) + return; // no need to waste time on startup + + SvxRuler::Paint(rRenderContext, rRect); + + // Don't draw if there is not any note + if (mpViewShell->GetPostItMgr() && mpViewShell->GetPostItMgr()->HasNotes()) + DrawCommentControl(rRenderContext); +} + +void SwCommentRuler::DrawCommentControl(vcl::RenderContext& rRenderContext) +{ + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const bool bIsCollapsed = !mpViewShell->GetPostItMgr()->ShowNotes(); + const tools::Rectangle aControlRect = GetCommentControlRegion(); + + maVirDev->SetOutputSizePixel(aControlRect.GetSize()); + + // set colors + if (!bIsCollapsed) + { + if (mbIsHighlighted) + maVirDev->SetFillColor( + GetFadedColor(rStyleSettings.GetHighlightColor(), rStyleSettings.GetDialogColor())); + else + maVirDev->SetFillColor(rStyleSettings.GetDialogColor()); + maVirDev->SetLineColor(rStyleSettings.GetShadowColor()); + } + else + { + if (mbIsHighlighted) + maVirDev->SetFillColor(GetFadedColor(rStyleSettings.GetHighlightColor(), + rStyleSettings.GetWorkspaceColor())); + else + maVirDev->SetFillColor(rStyleSettings.GetWorkspaceColor()); + maVirDev->SetLineColor(); + } + Color aTextColor = GetFadedColor(rStyleSettings.GetHighlightTextColor(), + rStyleSettings.GetButtonTextColor()); + maVirDev->SetTextColor(aTextColor); + + // calculate label and arrow positions + const OUString aLabel = SwResId(STR_COMMENTS_LABEL); + const long nTriangleSize = maVirDev->GetTextHeight() / 2 + 1; + const long nTrianglePad = maVirDev->GetTextHeight() / 4; + + Point aLabelPos(0, (aControlRect.GetHeight() - maVirDev->GetTextHeight()) / 2); + Point aArrowPos(0, (aControlRect.GetHeight() - nTriangleSize) / 2); + + if (!AllSettings::GetLayoutRTL()) // | > Comments | + { + aArrowPos.setX(nTrianglePad); + aLabelPos.setX(aArrowPos.X() + nTriangleSize + nTrianglePad); + } + else // RTL => | Comments < | + { + const long nLabelWidth = maVirDev->GetTextWidth(aLabel); + if (!bIsCollapsed) + { + aArrowPos.setX(aControlRect.GetWidth() - 1 - nTrianglePad - CONTROL_BORDER_WIDTH + - nTriangleSize); + aLabelPos.setX(aArrowPos.X() - nTrianglePad - nLabelWidth); + } + else + { + // if comments are collapsed, left align the text, because otherwise it's very likely to be invisible + aArrowPos.setX(nLabelWidth + nTrianglePad + nTriangleSize); + aLabelPos.setX(aArrowPos.X() - nTrianglePad - nLabelWidth); + } + } + + // draw control + maVirDev->DrawRect(tools::Rectangle(Point(), aControlRect.GetSize())); + maVirDev->DrawText(aLabelPos, aLabel); + ImplDrawArrow(*maVirDev, aArrowPos.X(), aArrowPos.Y(), nTriangleSize, aTextColor, bIsCollapsed); + rRenderContext.DrawOutDev(aControlRect.TopLeft(), aControlRect.GetSize(), Point(), + aControlRect.GetSize(), *maVirDev); +} + +// Just accept double-click outside comment control +void SwCommentRuler::Command(const CommandEvent& rCEvt) +{ + Point aMousePos = rCEvt.GetMousePosPixel(); + // Ignore command request if it is inside Comment Control + if (!mpViewShell->GetPostItMgr() || !mpViewShell->GetPostItMgr()->HasNotes() + || !GetCommentControlRegion().IsInside(aMousePos)) + SvxRuler::Command(rCEvt); +} + +void SwCommentRuler::MouseMove(const MouseEvent& rMEvt) +{ + SvxRuler::MouseMove(rMEvt); + if (!mpViewShell->GetPostItMgr() || !mpViewShell->GetPostItMgr()->HasNotes()) + return; + + UpdateCommentHelpText(); + + Point aMousePos = rMEvt.GetPosPixel(); + bool bWasHighlighted = mbIsHighlighted; + mbIsHighlighted = GetCommentControlRegion().IsInside(aMousePos); + if (mbIsHighlighted != bWasHighlighted) + // Do start fading + maFadeTimer.Start(); +} + +void SwCommentRuler::MouseButtonDown(const MouseEvent& rMEvt) +{ + Point aMousePos = rMEvt.GetPosPixel(); + if (!rMEvt.IsLeft() || IsTracking() || !GetCommentControlRegion().IsInside(aMousePos)) + { + SvxRuler::MouseButtonDown(rMEvt); + return; + } + + // Toggle notes visibility + SwView& rView = mpSwWin->GetView(); + SfxRequest aRequest(rView.GetViewFrame(), SID_TOGGLE_NOTES); + rView.ExecViewOptions(aRequest); + + // It is inside comment control, so update help text + UpdateCommentHelpText(); + + Invalidate(); +} + +std::string SwCommentRuler::CreateJsonNotification() +{ + boost::property_tree::ptree jsonNotif; + + // Note that GetMargin1(), GetMargin2(), GetNullOffset(), and GetPageOffset() return values in + // pixels. Not twips. So "converting" the returned values with convertTwipToMm100() is quite + // wrong. (Also, even if the return values actually were in twips, it is questionable why we + // would want to pass them in mm100, as all other length values in the LOKit protocol apparently + // are in twips.) + + // Anyway, as the consuming code in Online mostly seems to work anyway, it is likely that it + // would work as well even if the values in pixels were passed without a bogus "conversion" to + // mm100. But let's keep this as is for now. + + // Also note that in desktop LibreOffice, these pixel values for the ruler of course change as + // one changes the zoom level. (Can be seen if one temporarily modifies the NotifyKit() function + // below to call this CreateJsonNotification() function and print its result in all cases even + // without LibreOfficeKit::isActive().) But in both web-based Online and in the iOS app, the + // zoom level from the point of view of this code here apparently does not change even if one + // zooms from the Online code's point of view. + jsonNotif.put("margin1", convertTwipToMm100(GetMargin1())); + jsonNotif.put("margin2", convertTwipToMm100(GetMargin2())); + jsonNotif.put("leftOffset", convertTwipToMm100(GetNullOffset())); + jsonNotif.put("pageOffset", convertTwipToMm100(GetPageOffset())); + + // GetPageWidth() on the other hand does return a value in twips. + // So here convertTwipToMm100() really does produce actual mm100. Fun. + jsonNotif.put("pageWidth", convertTwipToMm100(GetPageWidth())); + + boost::property_tree::ptree tabs; + + // The RulerTab array elements that GetTabs() returns have their nPos field in twips. So these + // too are actual mm100. + for (auto const& tab : GetTabs()) + { + boost::property_tree::ptree element; + element.put("position", convertTwipToMm100(tab.nPos)); + element.put("style", tab.nStyle); + tabs.push_back(std::make_pair("", element)); + } + + jsonNotif.add_child("tabs", tabs); + + RulerUnitData aUnitData = GetCurrentRulerUnit(); + jsonNotif.put("unit", aUnitData.aUnitStr); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, jsonNotif); + return aStream.str(); +} + +void SwCommentRuler::NotifyKit() +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + const std::string test = CreateJsonNotification(); + mpViewShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_RULER_UPDATE, + test.c_str()); +} + +void SwCommentRuler::Update() +{ + tools::Rectangle aPreviousControlRect = GetCommentControlRegion(); + SvxRuler::Update(); + if (aPreviousControlRect != GetCommentControlRegion()) + Invalidate(); + NotifyKit(); +} + +void SwCommentRuler::UpdateCommentHelpText() +{ + const char* pTooltipResId; + if (mpViewShell->GetPostItMgr()->ShowNotes()) + pTooltipResId = STR_HIDE_COMMENTS; + else + pTooltipResId = STR_SHOW_COMMENTS; + SetQuickHelpText(SwResId(pTooltipResId)); +} + +// TODO Make Ruler return its central rectangle instead of margins. +tools::Rectangle SwCommentRuler::GetCommentControlRegion() +{ + SwPostItMgr* pPostItMgr = mpViewShell->GetPostItMgr(); + + //rhbz#1006850 When the SwPostItMgr ctor is called from SwView::SwView it + //triggers an update of the uiview, but the result of the ctor hasn't been + //set into the mpViewShell yet, so GetPostItMgr is temporarily still NULL + if (!pPostItMgr) + return tools::Rectangle(); + + const unsigned long nSidebarWidth = pPostItMgr->GetSidebarWidth(true); + + //FIXME When the page width is larger then screen, the ruler is misplaced by one pixel + long nLeft = GetPageOffset(); + if (GetTextRTL()) + nLeft += GetBorderOffset() - nSidebarWidth; + else + nLeft += GetWinOffset() + mpSwWin->LogicToPixel(Size(GetPageWidth(), 0)).Width(); + + // Ruler::ImplDraw uses RULER_OFF (value: 3px) as offset, and Ruler::ImplFormat adds one extra pixel + long nTop = 4; + // Somehow pPostItMgr->GetSidebarBorderWidth() returns border width already doubled + long nRight = nLeft + nSidebarWidth + pPostItMgr->GetSidebarBorderWidth(true); + long nBottom = nTop + GetRulerVirHeight() - 3; + + tools::Rectangle aRect(nLeft, nTop, nRight, nBottom); + return aRect; +} + +Color SwCommentRuler::GetFadedColor(const Color& rHighColor, const Color& rLowColor) +{ + if (!maFadeTimer.IsActive()) + return mbIsHighlighted ? rHighColor : rLowColor; + + Color aColor = rHighColor; + aColor.Merge(rLowColor, mnFadeRate * 255 / 100.0f); + return aColor; +} + +IMPL_LINK_NOARG(SwCommentRuler, FadeHandler, Timer*, void) +{ + const int nStep = 25; + if (mbIsHighlighted && mnFadeRate < 100) + mnFadeRate += nStep; + else if (!mbIsHighlighted && mnFadeRate > 0) + mnFadeRate -= nStep; + else + return; + + Invalidate(); + + if (mnFadeRate != 0 && mnFadeRate != 100) + maFadeTimer.Start(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/conarc.cxx b/sw/source/uibase/ribbar/conarc.cxx new file mode 100644 index 000000000..16625a4df --- /dev/null +++ b/sw/source/uibase/ribbar/conarc.cxx @@ -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 . + */ + +#include <svx/svdobj.hxx> +#include <svx/svxids.hrc> +#include <vcl/event.hxx> + +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <conarc.hxx> + +ConstArc::ConstArc(SwWrtShell* pWrtShell, SwEditWin* pEditWin, SwView* pSwView) + : SwDrawBase(pWrtShell, pEditWin, pSwView), m_nButtonUpCount(0) +{ +} + +bool ConstArc::MouseButtonDown( const MouseEvent& rMEvt ) +{ + bool bReturn = SwDrawBase::MouseButtonDown(rMEvt); + if (bReturn && !m_nButtonUpCount) + m_aStartPoint = m_pWin->PixelToLogic(rMEvt.GetPosPixel()); + return bReturn; +} + +bool ConstArc::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bReturn = false; + + if ((m_pSh->IsDrawCreate() || m_pWin->IsDrawAction()) && rMEvt.IsLeft()) + { + Point aPnt(m_pWin->PixelToLogic(rMEvt.GetPosPixel())); + if (!m_nButtonUpCount && aPnt == m_aStartPoint) + { + SwDrawBase::MouseButtonUp(rMEvt); + bReturn = true; + } + else + { m_nButtonUpCount++; + + if (m_nButtonUpCount == 3) // Generating of circular arc finished + { + SwDrawBase::MouseButtonUp(rMEvt); + m_nButtonUpCount = 0; + bReturn = true; + } + else + m_pSh->EndCreate(SdrCreateCmd::NextPoint); + } + } + + return bReturn; +} + +void ConstArc::Activate(const sal_uInt16 nSlotId) +{ + switch (nSlotId) + { + case SID_DRAW_ARC: + m_pWin->SetSdrDrawMode(OBJ_CARC); + break; + case SID_DRAW_PIE: + m_pWin->SetSdrDrawMode(OBJ_SECT); + break; + case SID_DRAW_CIRCLECUT: + m_pWin->SetSdrDrawMode(OBJ_CCUT); + break; + default: + m_pWin->SetSdrDrawMode(OBJ_NONE); + break; + } + + SwDrawBase::Activate(nSlotId); +} + +void ConstArc::Deactivate() +{ + m_nButtonUpCount = 0; + + SwDrawBase::Deactivate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/concustomshape.cxx b/sw/source/uibase/ribbar/concustomshape.cxx new file mode 100644 index 000000000..a90652f1b --- /dev/null +++ b/sw/source/uibase/ribbar/concustomshape.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/sdtagitm.hxx> +#include <svx/svdview.hxx> +#include <editeng/eeitem.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <concustomshape.hxx> +#include <svx/gallery.hxx> +#include <sfx2/request.hxx> +#include <svx/fmmodel.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdoashp.hxx> +#include <svx/xfillit0.hxx> +#include <editeng/adjustitem.hxx> + +#include <math.h> + +using namespace com::sun::star; + +ConstCustomShape::ConstCustomShape( SwWrtShell* pWrtShell, SwEditWin* pEditWin, SwView* pSwView, SfxRequest const & rReq ) + : SwDrawBase( pWrtShell, pEditWin, pSwView ) +{ + aCustomShape = ConstCustomShape::GetShapeTypeFromRequest( rReq ); +} + +const OUString& ConstCustomShape::GetShapeType() const +{ + return aCustomShape; +} + +OUString ConstCustomShape::GetShapeTypeFromRequest( SfxRequest const & rReq ) +{ + OUString aRet; + const SfxItemSet* pArgs = rReq.GetArgs(); + if ( pArgs ) + { + const SfxStringItem& rItm = static_cast<const SfxStringItem&>(pArgs->Get( rReq.GetSlot() )); + aRet = rItm.GetValue(); + } + return aRet; +} + +bool ConstCustomShape::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = SwDrawBase::MouseButtonDown(rMEvt); + if ( bReturn ) + { + SdrView *pSdrView = m_pSh->GetDrawView(); + if ( pSdrView ) + { + SdrObject* pObj = pSdrView->GetCreateObj(); + if ( pObj ) + { + SetAttributes( pObj ); + bool bForceNoFillStyle = false; + if ( static_cast<SdrObjCustomShape*>(pObj)->UseNoFillStyle() ) + bForceNoFillStyle = true; + + SfxItemSet aAttr( m_pView->GetPool() ); + if ( bForceNoFillStyle ) + aAttr.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + pObj->SetMergedItemSet(aAttr); + } + } + } + return bReturn; +} + +void ConstCustomShape::Activate(const sal_uInt16 nSlotId) +{ + m_pWin->SetSdrDrawMode( OBJ_CUSTOMSHAPE ); + + SwDrawBase::Activate(nSlotId); +} + +// applying attributes + +void ConstCustomShape::SetAttributes( SdrObject* pObj ) +{ + bool bAttributesAppliedFromGallery = false; + + if ( GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) ) + { + std::vector< OUString > aObjList; + if ( GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) ) + { + for ( std::vector<OUString>::size_type i = 0; i < aObjList.size(); i++ ) + { + if ( aObjList[ i ].equalsIgnoreAsciiCase( aCustomShape ) ) + { + FmFormModel aFormModel; + SfxItemPool& rPool(aFormModel.GetItemPool()); + rPool.FreezeIdRanges(); + + if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aFormModel ) ) + { + const SdrObject* pSourceObj = aFormModel.GetPage( 0 )->GetObj( 0 ); + if( pSourceObj ) + { + const SfxItemSet& rSource = pSourceObj->GetMergedItemSet(); + SfxItemSet aDest( + pObj->getSdrModelFromSdrObject().GetItemPool(), + svl::Items< + // Ranges from SdrAttrObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTDIRECTION, + // Graphic attributes, 3D properties, + // CustomShape properties: + SDRATTR_GRAF_FIRST, + SDRATTR_CUSTOMSHAPE_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>{}); + aDest.Set( rSource ); + pObj->SetMergedItemSet( aDest ); + sal_Int32 nAngle = pSourceObj->GetRotateAngle(); + if ( nAngle ) + { + double a = nAngle * F_PI18000; + pObj->NbcRotate( pObj->GetSnapRect().Center(), nAngle, sin( a ), cos( a ) ); + } + bAttributesAppliedFromGallery = true; + } + } + break; + } + } + } + } + if ( !bAttributesAppliedFromGallery ) + { + pObj->SetMergedItem( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) ); + pObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + pObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + pObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) ); + static_cast<SdrObjCustomShape*>(pObj)->MergeDefaultAttributes( &aCustomShape ); + } +} + +void ConstCustomShape::CreateDefaultObject() +{ + SwDrawBase::CreateDefaultObject(); + SdrView *pSdrView = m_pSh->GetDrawView(); + if ( pSdrView ) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if ( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if ( dynamic_cast< const SdrObjCustomShape *>( pObj ) ) + SetAttributes( pObj ); + } + } +} + +// #i33136# +bool ConstCustomShape::doConstructOrthogonal() const +{ + return SdrObjCustomShape::doConstructOrthogonal(aCustomShape); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/conform.cxx b/sw/source/uibase/ribbar/conform.cxx new file mode 100644 index 000000000..a61a0d725 --- /dev/null +++ b/sw/source/uibase/ribbar/conform.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svdview.hxx> +#include <vcl/ptrstyle.hxx> + +#include <swmodule.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <conform.hxx> + +ConstFormControl::ConstFormControl(SwWrtShell* pWrtShell, SwEditWin* pEditWin, SwView* pSwView) : + SwDrawBase(pWrtShell, pEditWin, pSwView) +{ + m_bInsForm = true; +} + +bool ConstFormControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + SdrView *pSdrView = m_pSh->GetDrawView(); + + pSdrView->SetOrtho(rMEvt.IsShift()); + pSdrView->SetAngleSnapEnabled(rMEvt.IsShift()); + + if (rMEvt.IsMod2()) + { + pSdrView->SetCreate1stPointAsCenter(true); + pSdrView->SetResizeAtCenter(true); + } + else + { + pSdrView->SetCreate1stPointAsCenter(false); + pSdrView->SetResizeAtCenter(false); + } + + SdrViewEvent aVEvt; + SdrHitKind eHit = pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + // Only new object; if not in base mode (or pure selection mode) + if (rMEvt.IsLeft() && !m_pWin->IsDrawAction() && + (eHit == SdrHitKind::UnmarkedObject || eHit == SdrHitKind::NONE || m_pSh->IsDrawCreate())) + { + g_bNoInterrupt = true; + m_pWin->CaptureMouse(); + + m_pWin->SetPointer(PointerStyle::DrawRect); + + m_aStartPos = m_pWin->PixelToLogic(rMEvt.GetPosPixel()); + bReturn = m_pSh->BeginCreate( static_cast< sal_uInt16 >(m_pWin->GetSdrDrawMode()), SdrInventor::FmForm, m_aStartPos); + + if (bReturn) + m_pWin->SetDrawAction(true); + } + else + bReturn = SwDrawBase::MouseButtonDown(rMEvt); + + return bReturn; +} + +void ConstFormControl::Activate(const sal_uInt16 nSlotId) +{ + m_pWin->SetSdrDrawMode( static_cast<SdrObjKind>(nSlotId) ); + SwDrawBase::Activate(nSlotId); + m_pSh->GetDrawView()->SetCurrentObj(nSlotId); + + m_pWin->SetPointer(PointerStyle::DrawRect); +} + +void ConstFormControl::CreateDefaultObject() +{ + Point aStartPos(GetDefaultCenterPos()); + Point aEndPos(aStartPos); + aStartPos.AdjustX( -(2 * MM50) ); + aStartPos.AdjustY( -(MM50) ); + aEndPos.AdjustX(2 * MM50 ); + aEndPos.AdjustY(MM50 ); + + if(!m_pSh->HasDrawView()) + m_pSh->MakeDrawView(); + + SdrView *pSdrView = m_pSh->GetDrawView(); + pSdrView->SetDesignMode(); + m_pSh->BeginCreate( static_cast< sal_uInt16 >(m_pWin->GetSdrDrawMode()), SdrInventor::FmForm, aStartPos); + m_pSh->MoveCreate(aEndPos); + m_pSh->EndCreate(SdrCreateCmd::ForceEnd); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/conpoly.cxx b/sw/source/uibase/ribbar/conpoly.cxx new file mode 100644 index 000000000..481722734 --- /dev/null +++ b/sw/source/uibase/ribbar/conpoly.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 <svx/svxids.hrc> +#include <vcl/event.hxx> + +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <conpoly.hxx> + +ConstPolygon::ConstPolygon(SwWrtShell* pWrtShell, SwEditWin* pEditWin, SwView* pSwView) : + SwDrawBase(pWrtShell, pEditWin, pSwView) +{ +} + +bool ConstPolygon::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + if (m_pSh->IsDrawCreate()) + { + if (rMEvt.IsLeft() && rMEvt.GetClicks() == 1 && + m_pWin->GetSdrDrawMode() != OBJ_FREELINE && + m_pWin->GetSdrDrawMode() != OBJ_FREEFILL) + { + if (!m_pSh->EndCreate(SdrCreateCmd::NextPoint)) + { + m_pSh->BreakCreate(); + EnterSelectMode(rMEvt); + return true; + } + } + else + { + bReturn = SwDrawBase::MouseButtonUp(rMEvt); + + // #i85045# removed double mechanism to check for AutoClose polygon + // after construction; the method here did not check for already closed and + // also worked only for a single polygon. Removing. + } + } + else + bReturn = SwDrawBase::MouseButtonUp(rMEvt); + + return bReturn; +} + +void ConstPolygon::Activate(const sal_uInt16 nSlotId) +{ + switch (nSlotId) + { + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON_NOFILL: + m_pWin->SetSdrDrawMode(OBJ_PLIN); + break; + + case SID_DRAW_POLYGON: + case SID_DRAW_XPOLYGON: + m_pWin->SetSdrDrawMode(OBJ_POLY); + break; + + case SID_DRAW_BEZIER_NOFILL: + m_pWin->SetSdrDrawMode(OBJ_PATHLINE); + break; + + case SID_DRAW_BEZIER_FILL: + m_pWin->SetSdrDrawMode(OBJ_PATHFILL); + break; + + case SID_DRAW_FREELINE_NOFILL: + m_pWin->SetSdrDrawMode(OBJ_FREELINE); + break; + + case SID_DRAW_FREELINE: + m_pWin->SetSdrDrawMode(OBJ_FREEFILL); + break; + + default: + break; + } + + SwDrawBase::Activate(nSlotId); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/conrect.cxx b/sw/source/uibase/ribbar/conrect.cxx new file mode 100644 index 000000000..3484c4a55 --- /dev/null +++ b/sw/source/uibase/ribbar/conrect.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 <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/sdtacitm.hxx> +#include <svx/svdobj.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtakitm.hxx> +#include <svx/sdtaditm.hxx> +#include <svx/sdtaaitm.hxx> +#include <svx/svdview.hxx> +#include <svx/svdocapt.hxx> +#include <editeng/outlobj.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <conrect.hxx> + +ConstRectangle::ConstRectangle( SwWrtShell* pWrtShell, SwEditWin* pEditWin, + SwView* pSwView ) + : SwDrawBase( pWrtShell, pEditWin, pSwView ) + , bMarquee(false) + , bCapVertical(false) + , mbVertical(false) +{ +} + +bool ConstRectangle::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = SwDrawBase::MouseButtonDown(rMEvt); + + if (bReturn) + { + if (m_pWin->GetSdrDrawMode() == OBJ_CAPTION) + { + m_pView->NoRotate(); + if (m_pView->IsDrawSelMode()) + { + m_pView->FlipDrawSelMode(); + m_pSh->GetDrawView()->SetFrameDragSingles(m_pView->IsDrawSelMode()); + } + } + else + { + SdrObject* pObj = m_pView->GetDrawView()->GetCreateObj(); + if (pObj) + { + SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool()); + SwFEShell::SetLineEnds(aAttr, *pObj, m_nSlotId); + pObj->SetMergedItemSet(aAttr); + } + } + } + + return bReturn; +} + +bool ConstRectangle::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bRet = SwDrawBase::MouseButtonUp(rMEvt); + if( bRet ) + { + SdrView *pSdrView = m_pSh->GetDrawView(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0) ? rMarkList.GetMark(0)->GetMarkedSdrObj() + : nullptr; + switch( m_pWin->GetSdrDrawMode() ) + { + case OBJ_TEXT: + if( bMarquee ) + { + m_pSh->ChgAnchor(RndStdIds::FLY_AS_CHAR); + + if( pObj ) + { + // Set the attributes needed for scrolling + SfxItemSet aItemSet( pSdrView->GetModel()->GetItemPool(), + svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST>{}); + + aItemSet.Put( makeSdrTextAutoGrowWidthItem( false ) ); + aItemSet.Put( makeSdrTextAutoGrowHeightItem( false ) ); + aItemSet.Put( SdrTextAniKindItem( SdrTextAniKind::Scroll ) ); + aItemSet.Put( SdrTextAniDirectionItem( SdrTextAniDirection::Left ) ); + aItemSet.Put( SdrTextAniCountItem( 0 ) ); + aItemSet.Put( SdrTextAniAmountItem( + static_cast<sal_Int16>(m_pWin->PixelToLogic(Size(2,1)).Width())) ); + + pObj->SetMergedItemSetAndBroadcast(aItemSet); + } + } + else if(mbVertical) + { + if (SdrTextObj* pText = dynamic_cast<SdrTextObj*>(pObj)) + { + SfxItemSet aSet(pSdrView->GetModel()->GetItemPool()); + + pText->SetVerticalWriting(true); + + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + pText->SetMergedItemSet(aSet); + } + } + + if( pObj ) + { + SdrPageView* pPV = pSdrView->GetSdrPageView(); + m_pView->BeginTextEdit( pObj, pPV, m_pWin, true ); + } + m_pView->LeaveDrawCreate(); // Switch to selection mode + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); + break; + + case OBJ_CAPTION: + { + SdrCaptionObj* pCaptObj = dynamic_cast<SdrCaptionObj*>(pObj); + if( bCapVertical && pCaptObj ) + { + pCaptObj->ForceOutlinerParaObject(); + OutlinerParaObject* pOPO = pCaptObj->GetOutlinerParaObject(); + if( pOPO && !pOPO->IsVertical() ) + pOPO->SetVertical( true ); + } + } + break; + default:; //prevent warning + } + } + return bRet; +} + +void ConstRectangle::Activate(const sal_uInt16 nSlotId) +{ + bMarquee = bCapVertical = false; + mbVertical = false; + + switch (nSlotId) + { + case SID_LINE_ARROW_END: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_ARROW_START: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + case SID_LINE_ARROWS: + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + m_pWin->SetSdrDrawMode(OBJ_LINE); + break; + + case SID_DRAW_MEASURELINE: + m_pWin->SetSdrDrawMode(OBJ_MEASURE); + break; + + case SID_DRAW_RECT: + m_pWin->SetSdrDrawMode(OBJ_RECT); + break; + + case SID_DRAW_ELLIPSE: + m_pWin->SetSdrDrawMode(OBJ_CIRC); + break; + + case SID_DRAW_TEXT_MARQUEE: + bMarquee = true; + m_pWin->SetSdrDrawMode(OBJ_TEXT); + break; + + case SID_DRAW_TEXT_VERTICAL: + mbVertical = true; + m_pWin->SetSdrDrawMode(OBJ_TEXT); + break; + + case SID_DRAW_TEXT: + m_pWin->SetSdrDrawMode(OBJ_TEXT); + break; + + case SID_DRAW_CAPTION_VERTICAL: + bCapVertical = true; + [[fallthrough]]; + case SID_DRAW_CAPTION: + m_pWin->SetSdrDrawMode(OBJ_CAPTION); + break; + + default: + m_pWin->SetSdrDrawMode(OBJ_NONE); + break; + } + + SwDrawBase::Activate(nSlotId); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/drawbase.cxx b/sw/source/uibase/ribbar/drawbase.cxx new file mode 100644 index 000000000..f2b13d8d1 --- /dev/null +++ b/sw/source/uibase/ribbar/drawbase.cxx @@ -0,0 +1,550 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/svdview.hxx> +#include <svx/svdobj.hxx> +#include <svl/ptitem.hxx> +#include <editeng/sizeitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <fmtclds.hxx> +#include <frmfmt.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <wrtsh.hxx> +#include <drawbase.hxx> +#include <edtwin.hxx> +#include <swmodule.hxx> +#include <swundo.hxx> +#include <SwCapObjType.hxx> +#include <SwRewriter.hxx> +#include <strings.hrc> + +using namespace ::com::sun::star; + +SwDrawBase::SwDrawBase(SwWrtShell* pSwWrtShell, SwEditWin* pWindow, SwView* pSwView) : + m_pView(pSwView), + m_pSh(pSwWrtShell), + m_pWin(pWindow), + m_nSlotId(USHRT_MAX), + m_bCreateObj(true), + m_bInsForm(false) +{ + if ( !m_pSh->HasDrawView() ) + m_pSh->MakeDrawView(); +} + +SwDrawBase::~SwDrawBase() +{ + if (m_pView->GetWrtShellPtr()) // In the view-dtor could the wrtsh already been deleted... + m_pSh->GetDrawView()->SetEditMode(); +} + +bool SwDrawBase::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + SdrView *pSdrView = m_pSh->GetDrawView(); + + // #i33136# + pSdrView->SetOrtho(doConstructOrthogonal() ? !rMEvt.IsShift() : rMEvt.IsShift()); + pSdrView->SetAngleSnapEnabled(rMEvt.IsShift()); + + if (rMEvt.IsMod2()) + { + pSdrView->SetCreate1stPointAsCenter(true); + pSdrView->SetResizeAtCenter(true); + } + else + { + pSdrView->SetCreate1stPointAsCenter(false); + pSdrView->SetResizeAtCenter(false); + } + + SdrViewEvent aVEvt; + SdrHitKind eHit = pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + // Only new object, if not in the basic mode (or pure selection mode). + if (rMEvt.IsLeft() && !m_pWin->IsDrawAction()) + { + if (IsCreateObj() && (eHit == SdrHitKind::UnmarkedObject || eHit == SdrHitKind::NONE || m_pSh->IsDrawCreate())) + { + g_bNoInterrupt = true; + m_pWin->CaptureMouse(); + + m_aStartPos = m_pWin->PixelToLogic(rMEvt.GetPosPixel()); + + bReturn = m_pSh->BeginCreate( static_cast< sal_uInt16 >(m_pWin->GetSdrDrawMode()), m_aStartPos); + + SetDrawPointer(); + + if ( bReturn ) + m_pWin->SetDrawAction(true); + } + else if (!pSdrView->IsAction()) + { + // BEZIER-EDITOR + m_pWin->CaptureMouse(); + m_aStartPos = m_pWin->PixelToLogic(rMEvt.GetPosPixel()); + sal_uInt16 nEditMode = m_pWin->GetBezierMode(); + + if (eHit == SdrHitKind::Handle && aVEvt.pHdl->GetKind() == SdrHdlKind::BezierWeight) + { + // Drag handle + g_bNoInterrupt = true; + bReturn = pSdrView->BegDragObj(m_aStartPos, nullptr, aVEvt.pHdl); + m_pWin->SetDrawAction(true); + } + else if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT) + { + // Insert gluepoint + g_bNoInterrupt = true; + bReturn = pSdrView->BegInsObjPoint(m_aStartPos, rMEvt.IsMod1()); + m_pWin->SetDrawAction(true); + } + else if (eHit == SdrHitKind::MarkedObject && rMEvt.IsMod1()) + { + // Select gluepoint + if (!rMEvt.IsShift()) + pSdrView->UnmarkAllPoints(); + + bReturn = pSdrView->BegMarkPoints(m_aStartPos); + m_pWin->SetDrawAction(true); + } + else if (eHit == SdrHitKind::MarkedObject && !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + // Move object + return false; + } + else if (eHit == SdrHitKind::Handle) + { + // Select gluepoint + if (pSdrView->HasMarkablePoints() && (!pSdrView->IsPointMarked(*aVEvt.pHdl) || rMEvt.IsShift())) + { + SdrHdl* pHdl = nullptr; + + if (!rMEvt.IsShift()) + { + pSdrView->UnmarkAllPoints(); + pHdl = pSdrView->PickHandle(m_aStartPos); + } + else + { + if (pSdrView->IsPointMarked(*aVEvt.pHdl)) + { + bReturn = pSdrView->UnmarkPoint(*aVEvt.pHdl); + pHdl = nullptr; + } + else + { + pHdl = pSdrView->PickHandle(m_aStartPos); + } + } + + if (pHdl) + { + g_bNoInterrupt = true; + pSdrView->MarkPoint(*pHdl); + } + } + } + else + { + // Select or drag object + if (m_pSh->IsObjSelectable(m_aStartPos) && eHit == SdrHitKind::UnmarkedObject) + { + if (pSdrView->HasMarkablePoints()) + pSdrView->UnmarkAllPoints(); + + g_bNoInterrupt = false; + // Use drag in edtwin + return false; + } + + g_bNoInterrupt = true; + + if (m_pSh->IsObjSelected()) + { + if (!rMEvt.IsShift()) + { + if (!pSdrView->HasMarkablePoints()) + { + bool bUnlockView = !m_pSh->IsViewLocked(); + m_pSh->LockView( true ); //lock visible section + m_pSh->SelectObj(Point(LONG_MAX, LONG_MAX)); // deselect all + if( bUnlockView ) + m_pSh->LockView( false ); + } + else + pSdrView->UnmarkAllPoints(); + } + } + if (!m_pSh->IsSelFrameMode()) + m_pSh->EnterSelFrameMode(); + + bReturn = m_pSh->BeginMark(m_aStartPos); + if( bReturn ) + m_pWin->SetDrawAction(true); + + SetDrawPointer(); + } + } + } + return bReturn; +} + +bool SwDrawBase::MouseMove(const MouseEvent& rMEvt) +{ + SdrView *pSdrView = m_pSh->GetDrawView(); + Point aPnt(m_pWin->PixelToLogic(rMEvt.GetPosPixel())); + bool bRet = false; + + if (IsCreateObj() && !m_pWin->IsDrawSelMode() && pSdrView->IsCreateObj()) + { + // #i33136# + pSdrView->SetOrtho(doConstructOrthogonal() ? !rMEvt.IsShift() : rMEvt.IsShift()); + pSdrView->SetAngleSnapEnabled(rMEvt.IsShift()); + + m_pSh->MoveCreate(aPnt); + bRet = true; + } + else if (pSdrView->IsAction() || pSdrView->IsInsObjPoint() || pSdrView->IsMarkPoints()) + { + m_pSh->MoveMark(aPnt); + bRet = true; + } + + return bRet; +} + +bool SwDrawBase::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + bool bCheckShell = false; + bool bAutoCap = false; + + Point aPnt(m_pWin->PixelToLogic(rMEvt.GetPosPixel())); + + if (IsCreateObj() && m_pSh->IsDrawCreate() && !m_pWin->IsDrawSelMode()) + { + const SdrObjKind nDrawMode = m_pWin->GetSdrDrawMode(); + //objects with multiple point may end at the start position + bool bMultiPoint = OBJ_PLIN == nDrawMode || + OBJ_POLY == nDrawMode || + OBJ_PATHLINE == nDrawMode || + OBJ_PATHFILL == nDrawMode || + OBJ_FREELINE == nDrawMode || + OBJ_FREEFILL == nDrawMode; + if(rMEvt.IsRight() || (aPnt == m_aStartPos && !bMultiPoint)) + { + m_pSh->BreakCreate(); + m_pView->LeaveDrawCreate(); + } + else + { + if (OBJ_NONE == nDrawMode) + { + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, SwResId(STR_FRAME)); + m_pSh->StartUndo(SwUndoId::INSERT, &aRewriter); + } + + m_pSh->EndCreate(SdrCreateCmd::ForceEnd); + if (OBJ_NONE == nDrawMode) // Text border inserted + { + uno::Reference< frame::XDispatchRecorder > xRecorder = + m_pSh->GetView().GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + SfxRequest aReq(m_pSh->GetView().GetViewFrame(),FN_INSERT_FRAME); + aReq .AppendItem(SfxUInt16Item( FN_INSERT_FRAME, + static_cast<sal_uInt16>(RndStdIds::FLY_AT_PARA) )); + aReq.AppendItem(SfxPointItem( FN_PARAM_1, m_pSh->GetAnchorObjDiff())); + aReq.AppendItem(SvxSizeItem( FN_PARAM_2, m_pSh->GetObjSize())); + aReq.Done(); + } + bAutoCap = true; + if(m_pWin->GetFrameColCount() > 1) + { + SfxItemSet aSet(m_pView->GetPool(),svl::Items<RES_COL,RES_COL>{}); + SwFormatCol aCol(aSet.Get(RES_COL)); + aCol.Init(m_pWin->GetFrameColCount(), aCol.GetGutterWidth(), aCol.GetWishWidth()); + aSet.Put(aCol); + // Template AutoUpdate + SwFrameFormat* pFormat = m_pSh->GetSelectedFrameFormat(); + if(pFormat && pFormat->IsAutoUpdateFormat()) + m_pSh->AutoUpdateFrame(pFormat, aSet); + else + m_pSh->SetFlyFrameAttr( aSet ); + } + } + if (m_pWin->GetSdrDrawMode() == OBJ_NONE) + { + m_pSh->EndUndo(); + } + } + + bReturn = true; + + EnterSelectMode(rMEvt); + } + else + { + SdrView *pSdrView = m_pSh->GetDrawView(); + + if (!pSdrView->HasMarkablePoints()) + { + // NO BEZIER_EDITOR + if ((m_pSh->GetDrawView()->IsMarkObj() || m_pSh->GetDrawView()->IsMarkPoints()) + && rMEvt.IsLeft()) + { + bReturn = m_pSh->EndMark(); + + m_pWin->SetDrawAction(false); + + if (aPnt == m_aStartPos && m_pSh->IsObjSelectable(aPnt)) + { + m_pSh->SelectObj(aPnt, ( rMEvt.IsShift() && + m_pSh->IsSelFrameMode()) ? SW_ADD_SELECT : 0); + + if (!m_pSh->IsObjSelected()) + { + m_pView->LeaveDrawCreate(); // Switch to selection mode + + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); + + if (m_pSh->IsSelFrameMode()) + m_pSh->LeaveSelFrameMode(); + } + m_pView->NoRotate(); + + bCheckShell = true; // if necessary turn on BezierShell + } + else if (!m_pSh->IsObjSelected() && !m_pWin->IsDrawAction()) + { + if (m_pSh->IsObjSelectable(aPnt)) + m_pSh->SelectObj(aPnt, ( rMEvt.IsShift() && + m_pSh->IsSelFrameMode() ) ? SW_ADD_SELECT : 0 ); + else + { + m_pView->LeaveDrawCreate(); + if (m_pSh->IsSelFrameMode()) + m_pSh->LeaveSelFrameMode(); + } + m_pView->NoRotate(); + + bReturn = true; + } + } + } + else + { + // BEZIER_EDITOR + if ( pSdrView->IsAction() ) + { + if ( pSdrView->IsInsObjPoint() ) + bReturn = pSdrView->EndInsObjPoint(SdrCreateCmd::ForceEnd); + else if (pSdrView->IsMarkPoints() ) + bReturn = pSdrView->EndMarkPoints(); + else + { + pSdrView->EndAction(); + bReturn = true; + } + m_pWin->SetDrawAction(false); + + if (aPnt == m_aStartPos) + { + if (!m_pSh->IsObjSelectable(aPnt)) + m_pSh->SelectObj(Point(LONG_MAX, LONG_MAX)); + else if (!bReturn) + { + if (!rMEvt.IsShift()) + pSdrView->UnmarkAllPoints(); + m_pSh->SelectObj(aPnt, (rMEvt.IsShift() && + m_pSh->IsSelFrameMode()) ? SW_ADD_SELECT :0); + } + + if (!m_pSh->IsObjSelected()) + { + m_pView->LeaveDrawCreate(); // Switch to selection mode + + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); + + if (m_pSh->IsSelFrameMode()) + m_pSh->LeaveSelFrameMode(); + } + m_pView->NoRotate(); + + bCheckShell = true; // if necessary turn on BezierShell + } + } + + SetDrawPointer(); + + if (!m_pSh->IsObjSelected() && !m_pWin->IsDrawAction()) + { + m_pView->LeaveDrawCreate(); + if (m_pSh->IsSelFrameMode()) + m_pSh->LeaveSelFrameMode(); + + m_pView->NoRotate(); + bReturn = true; + } + } + } + + if (bCheckShell) + m_pView->AttrChangedNotify(nullptr); // if necessary turn on BezierShell + + //!!!!!!!!!! Attention suicide !!!!!!!!!!! Everything should be renewed once + if ( bAutoCap ) + m_pView->AutoCaption(FRAME_CAP); //Can currently only be FRAME, otherwise convert + // to enums + return bReturn; +} + +void SwDrawBase::Activate(const sal_uInt16 nSlot) +{ + SetSlotId(nSlot); + SdrView *pSdrView = m_pSh->GetDrawView(); + + pSdrView->SetCurrentObj( static_cast< sal_uInt16 >(m_pWin->GetSdrDrawMode()) ); + pSdrView->SetEditMode(false); + + SetDrawPointer(); + m_pSh->NoEdit(); +} + +void SwDrawBase::Deactivate() +{ + SdrView *pSdrView = m_pSh->GetDrawView(); + pSdrView->SetOrtho(false); + pSdrView->SetAngleSnapEnabled(false); + + if (m_pWin->IsDrawAction() && m_pSh->IsDrawCreate()) + m_pSh->BreakCreate(); + + m_pWin->SetDrawAction(false); + + if (m_pWin->IsMouseCaptured()) + m_pWin->ReleaseMouse(); + g_bNoInterrupt = false; + + if (m_pWin->GetApplyTemplate()) + m_pWin->SetApplyTemplate(SwApplyTemplate()); + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); +} + +// Process keyboard events + +// If a KeyEvent is processed then the return value is true, otherwise +// false. + +void SwDrawBase::BreakCreate() +{ + m_pSh->BreakCreate(); + m_pWin->SetDrawAction(false); + m_pWin->ReleaseMouse(); + + Deactivate(); +} + +void SwDrawBase::SetDrawPointer() +{ + SdrView *pSdrView = m_pSh->GetDrawView(); + Point aPnt(m_pWin->OutputToScreenPixel(m_pWin->GetPointerPosPixel())); + aPnt = m_pWin->PixelToLogic(m_pWin->ScreenToOutputPixel(aPnt)); + PointerStyle aPointTyp = pSdrView->GetPreferredPointer(aPnt, m_pSh->GetOut()); + m_pWin->SetPointer(aPointTyp); +} + +// If necessary switch into selection mode + +void SwDrawBase::EnterSelectMode(const MouseEvent& rMEvt) +{ + m_pWin->SetDrawAction(false); + + if (!m_pSh->IsObjSelected() && !m_pWin->IsDrawAction()) + { + Point aPnt(m_pWin->PixelToLogic(rMEvt.GetPosPixel())); + + if (m_pSh->IsObjSelectable(aPnt)) + { + m_pSh->SelectObj(aPnt); + if (rMEvt.GetModifier() == KEY_SHIFT || !m_pSh->IsObjSelected()) + { + m_pView->LeaveDrawCreate(); // Switch to selection mode + + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); + } + } + else + { + m_pView->LeaveDrawCreate(); + if (m_pSh->IsSelFrameMode()) + m_pSh->LeaveSelFrameMode(); + } + m_pView->NoRotate(); + } +} + +void SwDrawBase::CreateDefaultObject() +{ + Point aStartPos = GetDefaultCenterPos(); + Point aEndPos(aStartPos); + aStartPos.AdjustX( -(6 * MM50) ); + aStartPos.AdjustY( -(6 * MM50) ); + aEndPos.AdjustX(6 * MM50 ); + aEndPos.AdjustY(6 * MM50 ); + tools::Rectangle aRect(aStartPos, aEndPos); + m_pSh->CreateDefaultShape( static_cast< sal_uInt16 >(m_pWin->GetSdrDrawMode()), aRect, m_nSlotId); +} + +Point SwDrawBase::GetDefaultCenterPos() const +{ + Size aDocSz(m_pSh->GetDocSize()); + + SwRect aVisArea(m_pSh->VisArea()); + if (comphelper::LibreOfficeKit::isActive()) + { + aVisArea = m_pSh->getLOKVisibleArea(); + aVisArea.Intersection(SwRect(Point(), aDocSz)); + } + + Point aCenter = aVisArea.Center(); + if (aVisArea.Width() > aDocSz.Width()) + aCenter.setX(aDocSz.Width() / 2 + aVisArea.Left()); + if (aVisArea.Height() > aDocSz.Height()) + aCenter.setY(aDocSz.Height() / 2 + aVisArea.Top()); + + return aCenter; +} + +// #i33136# +bool SwDrawBase::doConstructOrthogonal() const +{ + return ( m_nSlotId == SID_DRAW_XPOLYGON || m_nSlotId == SID_DRAW_XPOLYGON_NOFILL || m_nSlotId == SID_DRAW_XLINE ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/dselect.cxx b/sw/source/uibase/ribbar/dselect.cxx new file mode 100644 index 000000000..a70583fda --- /dev/null +++ b/sw/source/uibase/ribbar/dselect.cxx @@ -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 . + */ + +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <drawbase.hxx> +#include <dselect.hxx> + +DrawSelection::DrawSelection(SwWrtShell* pWrtShell, SwEditWin* pEditWin, SwView* pSwView) : + SwDrawBase(pWrtShell, pEditWin, pSwView) +{ + m_bCreateObj = false; +} + +void DrawSelection::Activate(const sal_uInt16 nSlotId) +{ + m_pWin->SetSdrDrawMode(OBJ_NONE); + m_pWin->SetObjectSelect( true ); + SwDrawBase::Activate(nSlotId); + + m_pSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/inputwin.cxx b/sw/source/uibase/ribbar/inputwin.cxx new file mode 100644 index 000000000..841dc4a82 --- /dev/null +++ b/sw/source/uibase/ribbar/inputwin.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 <sal/config.h> +#include <sal/log.hxx> + +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <officecfg/Office/Common.hxx> +#include <tools/gen.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ruler.hxx> +#include <svl/stritem.hxx> +#include <vcl/event.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <inputwin.hxx> +#include <fldbas.hxx> +#include <fldmgr.hxx> +#include <frmfmt.hxx> +#include <cellatr.hxx> +#include <edtwin.hxx> +#include <helpids.h> +#include <strings.hrc> +#include <bitmaps.hlst> + +// Only for the UpdateRange: Delete the box in which the stacked cursor is positioned. +#include <pam.hxx> + +#include <swundo.hxx> + +#include <IDocumentContentOperations.hxx> + +#define ED_POS 2 +#define ED_FORMULA 3 + +SFX_IMPL_POS_CHILDWINDOW_WITHID( SwInputChild, FN_EDIT_FORMULA, SFX_OBJECTBAR_OBJECT ) + +IMPL_LINK(PosEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +SwInputWindow::SwInputWindow(vcl::Window* pParent, SfxDispatcher const * pDispatcher) + : ToolBox(pParent, WB_3DLOOK|WB_BORDER) + , mxPos(VclPtr<PosEdit>::Create(this)) + , mxEdit(VclPtr<InputEdit>::Create(this)) + , pWrtShell(nullptr) + , pView(nullptr) + , m_bDoesUndo(true) + , m_bResetUndo(false) + , m_bCallUndo(false) +{ + bFirst = true; + bIsTable = bDelSel = false; + + InsertItem(FN_FORMULA_CALC, Image(StockImage::Yes, RID_BMP_FORMULA_CALC), + SwResId(STR_FORMULA_CALC)); + InsertItem(FN_FORMULA_CANCEL, Image(StockImage::Yes, RID_BMP_FORMULA_CANCEL), + SwResId(STR_FORMULA_CANCEL)); + InsertItem(FN_FORMULA_APPLY, Image(StockImage::Yes, RID_BMP_FORMULA_APPLY), + SwResId(STR_FORMULA_APPLY)); + + SetHelpId(FN_FORMULA_CALC, HID_TBX_FORMULA_CALC); + SetHelpId(FN_FORMULA_CANCEL, HID_TBX_FORMULA_CANCEL); + SetHelpId(FN_FORMULA_APPLY, HID_TBX_FORMULA_APPLY); + + SwView *pDispatcherView = dynamic_cast<SwView*>(pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr); + SwView* pActiveView = ::GetActiveView(); + if (pDispatcherView == pActiveView) + pView = pActiveView; + pWrtShell = pView ? pView->GetWrtShellPtr() : nullptr; + + InsertWindow(ED_POS, mxPos.get(), ToolBoxItemBits::NONE, 0); + SetItemText(ED_POS, SwResId(STR_ACCESS_FORMULA_TYPE)); + mxPos->set_accessible_name(SwResId(STR_ACCESS_FORMULA_TYPE)); + SetAccessibleName(SwResId(STR_ACCESS_FORMULA_TOOLBAR)); + InsertSeparator ( 1 ); + InsertSeparator (); + InsertWindow(ED_FORMULA, mxEdit.get()); + SetItemText(ED_FORMULA, SwResId(STR_ACCESS_FORMULA_TEXT)); + mxEdit->set_accessible_name(SwResId(STR_ACCESS_FORMULA_TEXT)); + SetHelpId(ED_FORMULA, HID_EDIT_FORMULA); + + SetItemBits( FN_FORMULA_CALC, GetItemBits( FN_FORMULA_CALC ) | ToolBoxItemBits::DROPDOWNONLY ); + SetDropdownClickHdl( LINK( this, SwInputWindow, DropdownClickHdl )); + + Size aSizeTbx = CalcWindowSizePixel(); + Size aEditSize = mxEdit->GetSizePixel(); + tools::Rectangle aItemRect( GetItemRect(FN_FORMULA_CALC) ); + long nMaxHeight = std::max(aEditSize.Height(), aItemRect.GetHeight()); + if( nMaxHeight+2 > aSizeTbx.Height() ) + aSizeTbx.setHeight( nMaxHeight+2 ); + Size aSize = GetSizePixel(); + aSize.setHeight( aSizeTbx.Height() ); + SetSizePixel( aSize ); + + // align edit and item vcentered + Size aPosSize = mxPos->GetSizePixel(); + aPosSize.setHeight( nMaxHeight ); + aEditSize.setHeight( nMaxHeight ); + Point aPosPos = mxPos->GetPosPixel(); + Point aEditPos = mxEdit->GetPosPixel(); + aPosPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); + aEditPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); + mxPos->SetPosSizePixel( aPosPos, aPosSize ); + mxEdit->SetPosSizePixel( aEditPos, aEditSize ); +} + +SwInputWindow::~SwInputWindow() +{ + disposeOnce(); +} + +void SwInputWindow::dispose() +{ + // wake rulers + if(pView) + { + pView->GetHRuler().SetActive(); + pView->GetVRuler().SetActive(); + } + pMgr.reset(); + if(pWrtShell) + pWrtShell->EndSelTableCells(); + + CleanupUglyHackWithUndo(); + + mxPos.disposeAndClear(); + mxEdit.disposeAndClear(); + ToolBox::dispose(); +} + +void SwInputWindow::CleanupUglyHackWithUndo() +{ + if (m_bResetUndo) + { + if (pWrtShell) + { + DelBoxContent(); + pWrtShell->DoUndo(m_bDoesUndo); + if (m_bCallUndo) + { + pWrtShell->Undo(); + } + } + m_bResetUndo = false; // #i117122# once is enough :) + } +} + +void SwInputWindow::Resize() +{ + ToolBox::Resize(); + + long nWidth = GetSizePixel().Width(); + long nLeft = mxEdit->GetPosPixel().X(); + Size aEditSize = mxEdit->GetSizePixel(); + + aEditSize.setWidth( std::max( static_cast<long>(nWidth - nLeft - 5), long(0) ) ); + mxEdit->SetSizePixel( aEditSize ); +} + +void SwInputWindow::ShowWin() +{ + bIsTable = false; + // stop rulers + if (pView) + { + pView->GetHRuler().SetActive( false ); + pView->GetVRuler().SetActive( false ); + + OSL_ENSURE(pWrtShell, "no WrtShell!"); + // Cursor in table + bIsTable = pWrtShell->IsCursorInTable(); + + if( bFirst ) + pWrtShell->SelTableCells( LINK( this, SwInputWindow, + SelTableCellsNotify) ); + if( bIsTable ) + { + const OUString& rPos = pWrtShell->GetBoxNms(); + sal_Int32 nPos = 0; + short nSrch = -1; + while( (nPos = rPos.indexOf( ':',nPos + 1 ) ) != -1 ) + nSrch = static_cast<short>(nPos); + mxPos->set_text( rPos.copy( ++nSrch ) ); + aCurrentTableName = pWrtShell->GetTableFormat()->GetName(); + } + else + mxPos->set_text(SwResId(STR_TBL_FORMULA)); + + // Edit current field + OSL_ENSURE(pMgr == nullptr, "FieldManager not deleted"); + pMgr.reset(new SwFieldMgr); + + // Form should always begin with "=" , so set here + OUString sEdit('='); + if( pMgr->GetCurField() && SwFieldTypesEnum::Formel == pMgr->GetCurTypeId() ) + { + sEdit += pMgr->GetCurFieldPar2(); + } + else if( bFirst && bIsTable ) + { + m_bResetUndo = true; + SAL_WARN_IF( + officecfg::Office::Common::Undo::Steps::get() <= 0, + "sw", "/org.openoffice.Office.Common/Undo/Steps <= 0"); + + m_bDoesUndo = pWrtShell->DoesUndo(); + if( !m_bDoesUndo ) + { + pWrtShell->DoUndo(); + } + + if( !pWrtShell->SwCursorShell::HasSelection() ) + { + pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); + pWrtShell->SetMark(); + pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); + } + if( pWrtShell->SwCursorShell::HasSelection() ) + { + pWrtShell->StartUndo( SwUndoId::DELETE ); + pWrtShell->Delete(); + if( SwUndoId::EMPTY != pWrtShell->EndUndo( SwUndoId::DELETE )) + { + m_bCallUndo = true; + } + } + pWrtShell->DoUndo(false); + + SfxItemSet aSet( pWrtShell->GetAttrPool(), svl::Items<RES_BOXATR_FORMULA, RES_BOXATR_FORMULA>{} ); + if( pWrtShell->GetTableBoxFormulaAttrs( aSet )) + sEdit += aSet.Get( RES_BOXATR_FORMULA ).GetFormula(); + } + + if( bFirst ) + { + // Set WrtShell flags correctly + pWrtShell->SttSelect(); + pWrtShell->EndSelect(); + } + + bFirst = false; + + mxEdit->connect_changed( LINK( this, SwInputWindow, ModifyHdl )); + + mxEdit->set_text( sEdit ); + sOldFormula = sEdit; + + // For input cut the UserInterface + + pView->GetEditWin().LockKeyInput(true); + pView->GetViewFrame()->GetDispatcher()->Lock(true); + pWrtShell->Push(); + } + + ToolBox::Show(); + + // grab focus after ToolBox is shown so focus isn't potentially lost elsewhere + if (pView) + { + int nPos = mxEdit->get_text().getLength(); + mxEdit->select_region(nPos, nPos); + mxEdit->GrabFocus(); + } +} + +IMPL_LINK( SwInputWindow, MenuHdl, Menu *, pMenu, bool ) +{ + OString aCommand = pMenu->GetCurItemIdent(); + if (!aCommand.isEmpty()) + { + aCommand += " "; + mxEdit->replace_selection(OStringToOUString(aCommand, RTL_TEXTENCODING_ASCII_US)); + } + return false; +} + +IMPL_LINK_NOARG(SwInputWindow, DropdownClickHdl, ToolBox *, void) +{ + sal_uInt16 nCurID = GetCurItemId(); + EndSelection(); // reset back CurItemId ! + if (nCurID == FN_FORMULA_CALC) + { + VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/inputwinmenu.ui", ""); + VclPtr<PopupMenu> aPopMenu(aBuilder.get_menu("menu")); + aPopMenu->SetSelectHdl(LINK(this, SwInputWindow, MenuHdl)); + aPopMenu->Execute(this, GetItemRect(FN_FORMULA_CALC), PopupMenuFlags::NoMouseUpClose); + } +} + +void SwInputWindow::Click( ) +{ + sal_uInt16 nCurID = GetCurItemId(); + EndSelection(); // reset back CurItemId ! + switch ( nCurID ) + { + case FN_FORMULA_CANCEL: + { + CancelFormula(); + } + break; + case FN_FORMULA_APPLY: + { + ApplyFormula(); + } + break; + } +} + +void SwInputWindow::ApplyFormula() +{ + pView->GetViewFrame()->GetDispatcher()->Lock(false); + pView->GetEditWin().LockKeyInput(false); + CleanupUglyHackWithUndo(); + pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + + // Form should always begin with "=", so remove it here again + OUString sEdit(comphelper::string::strip(mxEdit->get_text(), ' ')); + if( !sEdit.isEmpty() && '=' == sEdit[0] ) + sEdit = sEdit.copy( 1 ); + SfxStringItem aParam(FN_EDIT_FORMULA, sEdit); + + pWrtShell->EndSelTableCells(); + pView->GetEditWin().GrabFocus(); + const SfxPoolItem* aArgs[2]; + aArgs[0] = &aParam; + aArgs[1] = nullptr; + pView->GetViewFrame()->GetBindings().Execute( FN_EDIT_FORMULA, aArgs, SfxCallMode::ASYNCHRON ); +} + +void SwInputWindow::CancelFormula() +{ + if(pView) + { + pView->GetViewFrame()->GetDispatcher()->Lock( false ); + pView->GetEditWin().LockKeyInput(false); + CleanupUglyHackWithUndo(); + pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + + if( bDelSel ) + pWrtShell->EnterStdMode(); + + pWrtShell->EndSelTableCells(); + + pView->GetEditWin().GrabFocus(); + + pView->GetViewFrame()->GetDispatcher()->Execute( FN_EDIT_FORMULA, SfxCallMode::ASYNCHRON); + } +} + +const sal_Unicode CH_LRE = 0x202a; +const sal_Unicode CH_PDF = 0x202c; + +IMPL_LINK( SwInputWindow, SelTableCellsNotify, SwWrtShell&, rCaller, void ) +{ + if(bIsTable) + { + SwFrameFormat* pTableFormat = rCaller.GetTableFormat(); + OUString sBoxNms( rCaller.GetBoxNms() ); + OUString sTableNm; + if( pTableFormat && aCurrentTableName != pTableFormat->GetName() ) + sTableNm = pTableFormat->GetName(); + + mxEdit->UpdateRange( sBoxNms, sTableNm ); + + OUString sNew = OUStringChar(CH_LRE) + mxEdit->get_text() + + OUStringChar(CH_PDF); + + if( sNew != sOldFormula ) + { + // The WrtShell is in the table selection, + // then cancel the table selection otherwise, the cursor is + // positioned "in the forest" and the live update does not work! + pWrtShell->StartAllAction(); + + SwPaM aPam( *pWrtShell->GetStackCursor()->GetPoint() ); + aPam.Move( fnMoveBackward, GoInSection ); + aPam.SetMark(); + aPam.Move( fnMoveForward, GoInSection ); + + IDocumentContentOperations& rIDCO = pWrtShell->getIDocumentContentOperations(); + rIDCO.DeleteRange( aPam ); + rIDCO.InsertString( aPam, sNew ); + pWrtShell->EndAllAction(); + sOldFormula = sNew; + } + } + else + mxEdit->GrabFocus(); +} + +void SwInputWindow::SetFormula( const OUString& rFormula ) +{ + OUString sEdit('='); + if( !rFormula.isEmpty() ) + { + if( '=' == rFormula[0] ) + sEdit = rFormula; + else + sEdit += rFormula; + } + mxEdit->set_text( sEdit ); + mxEdit->select_region(sEdit.getLength(), sEdit.getLength()); + bDelSel = true; +} + +IMPL_LINK_NOARG(SwInputWindow, ModifyHdl, weld::Entry&, void) +{ + if (bIsTable && m_bResetUndo) + { + pWrtShell->StartAllAction(); + DelBoxContent(); + OUString sNew = OUStringChar(CH_LRE) + mxEdit->get_text() + + OUStringChar(CH_PDF); + pWrtShell->SwEditShell::Insert2( sNew ); + pWrtShell->EndAllAction(); + sOldFormula = sNew; + } +} + +void SwInputWindow::DelBoxContent() +{ + if( bIsTable ) + { + pWrtShell->StartAllAction(); + pWrtShell->ClearMark(); + pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + pWrtShell->Push(); + pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); + pWrtShell->SetMark(); + pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); + pWrtShell->SwEditShell::Delete(); + pWrtShell->EndAllAction(); + } +} + +IMPL_LINK(InputEdit, KeyInputHdl, const KeyEvent&, rEvent, bool) +{ + bool bHandled = false; + const vcl::KeyCode aCode = rEvent.GetKeyCode(); + if (aCode == KEY_RETURN || aCode == KEY_F2) + { + bHandled = ActivateHdl(*m_xWidget); + } + else if(aCode == KEY_ESCAPE ) + { + static_cast<SwInputWindow*>(GetParent())->CancelFormula(); + bHandled = true; + } + return bHandled || ChildKeyInput(rEvent); +} + +IMPL_LINK_NOARG(InputEdit, ActivateHdl, weld::Entry&, bool) +{ + static_cast<SwInputWindow*>(GetParent())->ApplyFormula(); + return true; +} + +void InputEdit::UpdateRange(const OUString& rBoxes, + const OUString& rName ) +{ + if( rBoxes.isEmpty() ) + { + GrabFocus(); + return; + } + const sal_Unicode cOpen = '<', cClose = '>', + cOpenBracket = '('; + OUString aPrefix = rName; + if(!rName.isEmpty()) + aPrefix += "."; + OUString aBoxes = aPrefix + rBoxes; + + int nSelStartPos, nSelEndPos; + m_xWidget->get_selection_bounds(nSelStartPos, nSelEndPos); + + Selection aSelection(nSelStartPos, nSelEndPos); + sal_uInt16 nSel = static_cast<sal_uInt16>(aSelection.Len()); + // OS: The following expression ensures that in the overwrite mode, + // the selected closing parenthesis will be not deleted. + if( nSel && ( nSel > 1 || + m_xWidget->get_text()[ static_cast<sal_uInt16>(aSelection.Min()) ] != cClose )) + m_xWidget->cut_clipboard(); + else + aSelection.Max() = aSelection.Min(); + OUString aActText(m_xWidget->get_text()); + const sal_uInt16 nLen = aActText.getLength(); + if( !nLen ) + { + OUString aStr = OUStringChar(cOpen) + aBoxes + OUStringChar(cClose); + m_xWidget->set_text(aStr); + sal_Int32 nPos = aStr.indexOf( cClose ); + OSL_ENSURE(nPos != -1, "delimiter not found"); + ++nPos; + m_xWidget->select_region(nPos, nPos); + } + else + { + bool bFound = false; + sal_Unicode cCh; + sal_uInt16 nPos, nEndPos = 0, nStartPos = static_cast<sal_uInt16>(aSelection.Min()); + if( nStartPos-- ) + { + do { + if( cOpen == (cCh = aActText[ nStartPos ] ) || + cOpenBracket == cCh ) + { + bFound = cCh == cOpen; + break; + } + } while( nStartPos-- > 0 ); + } + if( bFound ) + { + bFound = false; + nEndPos = nStartPos; + while( nEndPos < nLen ) + { + if( cClose == aActText[ nEndPos ] ) + { + bFound = true; + break; + } + ++nEndPos; + } + // Only if the current position lies in the range or right behind. + if( bFound && !( nStartPos < o3tl::make_unsigned(aSelection.Max()) && + static_cast<sal_uInt16>(aSelection.Max()) <= nEndPos + 1 )) + bFound = false; + } + if( bFound ) + { + nPos = ++nStartPos + 1; // We want behind + aActText = aActText.replaceAt( nStartPos, nEndPos - nStartPos, aBoxes ); + nPos = nPos + aBoxes.getLength(); + } + else + { + OUString aTmp = OUStringChar(cOpen) + aBoxes + OUStringChar(cClose); + nPos = static_cast<sal_uInt16>(aSelection.Min()); + aActText = aActText.replaceAt( nPos, 0, aTmp ); + nPos = nPos + aTmp.getLength(); + } + if( m_xWidget->get_text() != aActText ) + { + m_xWidget->set_text(aActText); + m_xWidget->select_region(nPos, nPos); + } + } + GrabFocus(); + +} + +SwInputChild::SwInputChild(vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings const * pBindings, + SfxChildWinInfo* ) : + SfxChildWindow( _pParent, nId ) +{ + pDispatch = pBindings->GetDispatcher(); + SetWindow(VclPtr<SwInputWindow>::Create(_pParent, pDispatch)); + static_cast<SwInputWindow*>(GetWindow())->ShowWin(); + SetAlignment(SfxChildAlignment::LOWESTTOP); +} + +SwInputChild::~SwInputChild() +{ + if(pDispatch) + pDispatch->Lock(false); +} + +SfxChildWinInfo SwInputChild::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/ribbar/workctrl.cxx b/sw/source/uibase/ribbar/workctrl.cxx new file mode 100644 index 000000000..3e93d5320 --- /dev/null +++ b/sw/source/uibase/ribbar/workctrl.cxx @@ -0,0 +1,1063 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <i18nutil/unicode.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <initui.hxx> +#include <docsh.hxx> +#include <gloshdl.hxx> +#include <gloslst.hxx> +#include <workctrl.hxx> +#include <strings.hrc> +#include <cmdid.h> +#include <helpids.h> +#include <wrtsh.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <rtl/ustring.hxx> +#include <swabstdlg.hxx> +#include <sfx2/zoomitem.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <bitmaps.hlst> +#include <toolkit/helper/vclunohelper.hxx> +#include <svx/srchdlg.hxx> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +// Size check +#define NAVI_ENTRIES 18 + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; + +SFX_IMPL_TOOLBOX_CONTROL( SwTbxAutoTextCtrl, SfxVoidItem ); + +SwTbxAutoTextCtrl::SwTbxAutoTextCtrl( + sal_uInt16 nSlotId, + sal_uInt16 nId, + ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ + rTbx.SetItemBits( nId, ToolBoxItemBits::DROPDOWN | rTbx.GetItemBits( nId ) ); +} + +SwTbxAutoTextCtrl::~SwTbxAutoTextCtrl() +{ +} + +void SwTbxAutoTextCtrl::CreatePopupWindow() +{ + SwView* pView = ::GetActiveView(); + if(pView && !pView->GetDocShell()->IsReadOnly() && + !pView->GetWrtShell().HasReadonlySel() ) + { + Link<Menu*,bool> aLnk = LINK(this, SwTbxAutoTextCtrl, PopupHdl); + + ScopedVclPtrInstance<PopupMenu> pPopup; + SwGlossaryList* pGlossaryList = ::GetGlossaryList(); + const size_t nGroupCount = pGlossaryList->GetGroupCount(); + for(size_t i = 1; i <= nGroupCount; ++i) + { + OUString sTitle = pGlossaryList->GetGroupTitle(i - 1); + const sal_uInt16 nBlockCount = pGlossaryList->GetBlockCount(i -1); + if(nBlockCount) + { + sal_uInt16 nIndex = static_cast<sal_uInt16>(100*i); + // but insert without extension + pPopup->InsertItem( i, sTitle); + VclPtrInstance<PopupMenu> pSub; + pSub->SetSelectHdl(aLnk); + pPopup->SetPopupMenu(i, pSub); + for(sal_uInt16 j = 0; j < nBlockCount; j++) + { + OUString sLongName(pGlossaryList->GetBlockLongName(i - 1, j)); + OUString sShortName(pGlossaryList->GetBlockShortName(i - 1, j)); + + OUString sEntry = sShortName + " - " + sLongName; + pSub->InsertItem(++nIndex, sEntry); + } + } + } + + ToolBox* pToolBox = &GetToolBox(); + sal_uInt16 nId = GetId(); + pToolBox->SetItemDown( nId, true ); + + pPopup->Execute( pToolBox, pToolBox->GetItemRect( nId ), + (pToolBox->GetAlign() == WindowAlign::Top || pToolBox->GetAlign() == WindowAlign::Bottom) ? + PopupMenuFlags::ExecuteDown : PopupMenuFlags::ExecuteRight ); + + pToolBox->SetItemDown( nId, false ); + } + GetToolBox().EndSelection(); +} + +void SwTbxAutoTextCtrl::StateChanged( sal_uInt16, + SfxItemState, + const SfxPoolItem* pState ) +{ + GetToolBox().EnableItem( GetId(), (GetItemState(pState) != SfxItemState::DISABLED) ); +} + +IMPL_STATIC_LINK(SwTbxAutoTextCtrl, PopupHdl, Menu*, pMenu, bool) +{ + sal_uInt16 nId = pMenu->GetCurItemId(); + + sal_uInt16 nBlock = nId / 100; + + SwGlossaryList* pGlossaryList = ::GetGlossaryList(); + OUString sGroup = pGlossaryList->GetGroupName(nBlock - 1); + OUString sShortName = + pGlossaryList->GetBlockShortName(nBlock - 1, nId - (100 * nBlock) - 1); + + SwGlossaryHdl* pGlosHdl = ::GetActiveView()->GetGlosHdl(); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossarySetActGroup fnSetActGroup = pFact->SetGlossaryActGroupFunc(); + if ( fnSetActGroup ) + (*fnSetActGroup)( sGroup ); + pGlosHdl->SetCurGroup(sGroup, true); + pGlosHdl->InsertGlossary(sShortName); + + return false; +} + +// Navigation-Popup +// determine the order of the toolbox items +static sal_uInt16 aNavigationInsertIds[ NAVI_ENTRIES ] = +{ + NID_TBL, + NID_FRM, + NID_GRF, + NID_OLE, + NID_PGE, + NID_OUTL, + NID_MARK, + NID_DRW, + NID_CTRL, + NID_REG, + NID_BKM, + NID_SEL, + NID_FTN, + NID_POSTIT, + NID_SRCH_REP, + NID_INDEX_ENTRY, + NID_TABLE_FORMULA, + NID_TABLE_FORMULA_ERROR +}; + +static OUStringLiteral const aNavigationImgIds[ NAVI_ENTRIES ] = +{ + RID_BMP_RIBBAR_TBL, + RID_BMP_RIBBAR_FRM, + RID_BMP_RIBBAR_GRF, + RID_BMP_RIBBAR_OLE, + RID_BMP_RIBBAR_PGE, + RID_BMP_RIBBAR_OUTL, + RID_BMP_RIBBAR_MARK, + RID_BMP_RIBBAR_DRW, + RID_BMP_RIBBAR_CTRL, + RID_BMP_RIBBAR_REG, + RID_BMP_RIBBAR_BKM, + RID_BMP_RIBBAR_SEL, + RID_BMP_RIBBAR_FTN, + RID_BMP_RIBBAR_POSTIT, + RID_BMP_RIBBAR_REP, + RID_BMP_RIBBAR_ENTRY, + RID_BMP_RIBBAR_FORMULA, + RID_BMP_RIBBAR_ERROR +}; + +static const char* aNavigationStrIds[ NAVI_ENTRIES ] = +{ + ST_TBL, + ST_FRM, + ST_GRF, + ST_OLE, + ST_PGE, + ST_OUTL, + ST_MARK, + ST_DRW, + ST_CTRL, + ST_REG, + ST_BKM, + ST_SEL, + ST_FTN, + ST_POSTIT, + ST_SRCH_REP, + ST_INDEX_ENTRY, + ST_TABLE_FORMULA, + ST_TABLE_FORMULA_ERROR +}; + +// these are global strings +static const char* STR_IMGBTN_ARY[] = +{ + STR_IMGBTN_TBL_DOWN, + STR_IMGBTN_FRM_DOWN, + STR_IMGBTN_PGE_DOWN, + STR_IMGBTN_DRW_DOWN, + STR_IMGBTN_CTRL_DOWN, + STR_IMGBTN_REG_DOWN, + STR_IMGBTN_BKM_DOWN, + STR_IMGBTN_GRF_DOWN, + STR_IMGBTN_OLE_DOWN, + STR_IMGBTN_OUTL_DOWN, + STR_IMGBTN_SEL_DOWN, + STR_IMGBTN_FTN_DOWN, + STR_IMGBTN_MARK_DOWN, + STR_IMGBTN_POSTIT_DOWN, + STR_IMGBTN_SRCH_REP_DOWN, + STR_IMGBTN_INDEX_ENTRY_DOWN, + STR_IMGBTN_TBLFML_DOWN, + STR_IMGBTN_TBLFML_ERR_DOWN, + STR_IMGBTN_TBL_UP, + STR_IMGBTN_FRM_UP, + STR_IMGBTN_PGE_UP, + STR_IMGBTN_DRW_UP, + STR_IMGBTN_CTRL_UP, + STR_IMGBTN_REG_UP, + STR_IMGBTN_BKM_UP, + STR_IMGBTN_GRF_UP, + STR_IMGBTN_OLE_UP, + STR_IMGBTN_OUTL_UP, + STR_IMGBTN_SEL_UP, + STR_IMGBTN_FTN_UP, + STR_IMGBTN_MARK_UP, + STR_IMGBTN_POSTIT_UP, + STR_IMGBTN_SRCH_REP_UP, + STR_IMGBTN_INDEX_ENTRY_UP, + STR_IMGBTN_TBLFML_UP, + STR_IMGBTN_TBLFML_ERR_UP +}; + +static OUString lcl_GetScrollToolTip(bool bNext) +{ + sal_uInt16 nResId = SwView::GetMoveType(); + if (!bNext) + nResId += NID_COUNT; + const char* id = STR_IMGBTN_ARY[nResId - NID_START]; + return id ? SwResId(id): OUString(); +} + +namespace { + +class SwZoomBox_Impl final : public InterimItemWindow +{ + std::unique_ptr<weld::ComboBox> m_xWidget; + sal_uInt16 nSlotId; + bool bRelease; + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + + void Select(); + + void ReleaseFocus(); + +public: + SwZoomBox_Impl(vcl::Window* pParent, sal_uInt16 nSlot); + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void save_value() + { + m_xWidget->save_value(); + } + + void set_entry_text(const OUString& rText) + { + m_xWidget->set_entry_text(rText); + } + + virtual ~SwZoomBox_Impl() override + { + disposeOnce(); + } +}; + +} + +SwZoomBox_Impl::SwZoomBox_Impl(vcl::Window* pParent, sal_uInt16 nSlot) + : InterimItemWindow(pParent, "modules/swriter/ui/zoombox.ui", "ZoomBox") + , m_xWidget(m_xBuilder->weld_combo_box("zoom")) + , nSlotId(nSlot) + , bRelease(true) +{ + m_xWidget->set_help_id(HID_PVIEW_ZOOM_LB); + m_xWidget->set_entry_completion(false); + m_xWidget->connect_changed(LINK(this, SwZoomBox_Impl, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, SwZoomBox_Impl, KeyInputHdl)); + m_xWidget->connect_entry_activate(LINK(this, SwZoomBox_Impl, ActivateHdl)); + m_xWidget->connect_focus_out(LINK(this, SwZoomBox_Impl, FocusOutHdl)); + + const char* const aZoomValues[] = + { RID_SVXSTR_ZOOM_25 , RID_SVXSTR_ZOOM_50 , + RID_SVXSTR_ZOOM_75 , RID_SVXSTR_ZOOM_100 , + RID_SVXSTR_ZOOM_150 , RID_SVXSTR_ZOOM_200 , + RID_SVXSTR_ZOOM_WHOLE_PAGE, RID_SVXSTR_ZOOM_PAGE_WIDTH , + RID_SVXSTR_ZOOM_OPTIMAL_VIEW }; + for(const char* pZoomValue : aZoomValues) + { + OUString sEntry = SvxResId(pZoomValue); + m_xWidget->append_text(sEntry); + } + + int nWidth = m_xWidget->get_pixel_size(SvxResId(RID_SVXSTR_ZOOM_200)).Width(); + m_xWidget->set_entry_width_chars(std::ceil(nWidth / m_xWidget->get_approximate_digit_width())); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +IMPL_LINK(SwZoomBox_Impl, SelectHdl, weld::ComboBox&, rComboBox, void) +{ + if (rComboBox.changed_by_direct_pick()) // only when picked from the list + Select(); +} + +IMPL_LINK_NOARG(SwZoomBox_Impl, ActivateHdl, weld::ComboBox&, bool) +{ + Select(); + return true; +} + +void SwZoomBox_Impl::Select() +{ + if( FN_PREVIEW_ZOOM == nSlotId ) + { + bool bNonNumeric = true; + + OUString sEntry = m_xWidget->get_active_text().replaceAll("%", ""); + SvxZoomItem aZoom(SvxZoomType::PERCENT,100); + if(sEntry == SvxResId( RID_SVXSTR_ZOOM_PAGE_WIDTH ) ) + aZoom.SetType(SvxZoomType::PAGEWIDTH); + else if(sEntry == SvxResId( RID_SVXSTR_ZOOM_OPTIMAL_VIEW ) ) + aZoom.SetType(SvxZoomType::OPTIMAL); + else if(sEntry == SvxResId( RID_SVXSTR_ZOOM_WHOLE_PAGE) ) + aZoom.SetType(SvxZoomType::WHOLEPAGE); + else + { + bNonNumeric = false; + + sal_uInt16 nZoom = static_cast<sal_uInt16>(sEntry.toInt32()); + if(nZoom < MINZOOM) + nZoom = MINZOOM; + if(nZoom > MAXZOOM) + nZoom = MAXZOOM; + aZoom.SetValue(nZoom); + } + + if (bNonNumeric) + { + // put old value back, in case its effectively the same + // as the picked option and no update to number comes + // back from writer + m_xWidget->set_entry_text(m_xWidget->get_saved_value()); + } + + SfxObjectShell* pCurrentShell = SfxObjectShell::Current(); + + pCurrentShell->GetDispatcher()->ExecuteList(SID_ATTR_ZOOM, + SfxCallMode::ASYNCHRON, { &aZoom }); + } + ReleaseFocus(); +} + +IMPL_LINK(SwZoomBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + + switch (nCode) + { + case KEY_TAB: + bRelease = false; + Select(); + break; + + case KEY_ESCAPE: + m_xWidget->set_entry_text(m_xWidget->get_saved_value()); + ReleaseFocus(); + bHandled = true; + break; + } + + return bHandled || ChildKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(SwZoomBox_Impl, FocusOutHdl, weld::Widget&, void) +{ + if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus + m_xWidget->set_entry_text(m_xWidget->get_saved_value()); +} + +void SwZoomBox_Impl::ReleaseFocus() +{ + if ( !bRelease ) + { + bRelease = true; + return; + } + SfxViewShell* pCurSh = SfxViewShell::Current(); + + if ( pCurSh ) + { + vcl::Window* pShellWnd = pCurSh->GetWindow(); + + if ( pShellWnd ) + pShellWnd->GrabFocus(); + } +} + +SFX_IMPL_TOOLBOX_CONTROL( SwPreviewZoomControl, SfxUInt16Item); + +SwPreviewZoomControl::SwPreviewZoomControl( + sal_uInt16 nSlotId, + sal_uInt16 nId, + ToolBox& rTbx) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ +} + +SwPreviewZoomControl::~SwPreviewZoomControl() +{ +} + +void SwPreviewZoomControl::StateChanged( sal_uInt16 /*nSID*/, + SfxItemState eState, + const SfxPoolItem* pState ) +{ + sal_uInt16 nId = GetId(); + GetToolBox().EnableItem( nId, (GetItemState(pState) != SfxItemState::DISABLED) ); + SwZoomBox_Impl* pBox = static_cast<SwZoomBox_Impl*>(GetToolBox().GetItemWindow( GetId() )); + if(SfxItemState::DEFAULT <= eState) + { + OUString sZoom(unicode::formatPercent(static_cast<const SfxUInt16Item*>(pState)->GetValue(), + Application::GetSettings().GetUILanguageTag())); + pBox->set_entry_text(sZoom); + pBox->save_value(); + } +} + +VclPtr<InterimItemWindow> SwPreviewZoomControl::CreateItemWindow( vcl::Window *pParent ) +{ + VclPtrInstance<SwZoomBox_Impl> pRet( pParent, GetSlotId() ); + return pRet.get(); +} + +namespace { + +class SwJumpToSpecificBox_Impl final : public InterimItemWindow +{ + std::unique_ptr<weld::Entry> m_xWidget; + + sal_uInt16 nSlotId; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(SelectHdl, weld::Entry&, bool); +public: + SwJumpToSpecificBox_Impl(vcl::Window* pParent, sal_uInt16 nSlot); + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + virtual ~SwJumpToSpecificBox_Impl() override + { + disposeOnce(); + } +}; + +} + +IMPL_LINK(SwJumpToSpecificBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +SwJumpToSpecificBox_Impl::SwJumpToSpecificBox_Impl(vcl::Window* pParent, sal_uInt16 nSlot) + : InterimItemWindow(pParent, "modules/swriter/ui/jumpposbox.ui", "JumpPosBox") + , m_xWidget(m_xBuilder->weld_entry("jumppos")) + , nSlotId(nSlot) +{ + m_xWidget->connect_key_press(LINK(this, SwJumpToSpecificBox_Impl, KeyInputHdl)); + m_xWidget->connect_activate(LINK(this, SwJumpToSpecificBox_Impl, SelectHdl)); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +IMPL_LINK_NOARG(SwJumpToSpecificBox_Impl, SelectHdl, weld::Entry&, bool) +{ + OUString sEntry(m_xWidget->get_text()); + SfxUInt16Item aPageNum(nSlotId); + aPageNum.SetValue(static_cast<sal_uInt16>(sEntry.toInt32())); + SfxObjectShell* pCurrentShell = SfxObjectShell::Current(); + pCurrentShell->GetDispatcher()->ExecuteList(nSlotId, SfxCallMode::ASYNCHRON, + { &aPageNum }); + return true; +} + +SFX_IMPL_TOOLBOX_CONTROL( SwJumpToSpecificPageControl, SfxUInt16Item); + +SwJumpToSpecificPageControl::SwJumpToSpecificPageControl( + sal_uInt16 nSlotId, + sal_uInt16 nId, + ToolBox& rTbx) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{} + +SwJumpToSpecificPageControl::~SwJumpToSpecificPageControl() +{} + +VclPtr<InterimItemWindow> SwJumpToSpecificPageControl::CreateItemWindow( vcl::Window *pParent ) +{ + VclPtrInstance<SwJumpToSpecificBox_Impl> pRet( pParent, GetSlotId() ); + return pRet.get(); +} + +namespace { + +class NavElementBox_Base; +class NavElementBox_Impl; + +class NavElementToolBoxControl : public svt::ToolboxController, + public lang::XServiceInfo +{ + public: + explicit NavElementToolBoxControl( + const css::uno::Reference< css::uno::XComponentContext >& rServiceManager ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire() throw () override; + virtual void SAL_CALL release() throw () 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; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + // XToolbarController + virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override; + virtual void SAL_CALL click() override; + virtual void SAL_CALL doubleClick() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override; + + void dispatchCommand( const css::uno::Sequence< css::beans::PropertyValue >& rArgs ); + using svt::ToolboxController::dispatchCommand; + + private: + VclPtr<NavElementBox_Impl> m_xVclBox; + std::unique_ptr<NavElementBox_Base> m_xWeldBox; + NavElementBox_Base* m_pBox; +}; + +class NavElementBox_Base +{ +public: + NavElementBox_Base(std::unique_ptr<weld::ComboBox> xWidget, + const uno::Reference<frame::XFrame>& _xFrame, + NavElementToolBoxControl& rCtrl); + + virtual ~NavElementBox_Base() + { + } + + void set_sensitive(bool bSensitive) + { + m_xWidget->set_sensitive(bSensitive); + } + + void UpdateBox(); + +protected: + std::unique_ptr<weld::ComboBox> m_xWidget; + NavElementToolBoxControl* m_pCtrl; + bool m_bRelease; + uno::Reference< frame::XFrame > m_xFrame; + + virtual bool DoKeyInput(const KeyEvent& rKEvt); + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + + void ReleaseFocus_Impl(); +}; + + +class NavElementBox_Impl final : public InterimItemWindow + , public NavElementBox_Base +{ +public: + NavElementBox_Impl(vcl::Window* pParent, + const uno::Reference<frame::XFrame>& _xFrame, + NavElementToolBoxControl& rCtrl); + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + virtual bool DoKeyInput(const KeyEvent& rKEvt) override; + + virtual ~NavElementBox_Impl() override + { + disposeOnce(); + } +}; + +} + +NavElementBox_Base::NavElementBox_Base( + std::unique_ptr<weld::ComboBox> xWidget, + const uno::Reference< frame::XFrame >& _xFrame, + NavElementToolBoxControl& _rCtrl ) + : m_xWidget(std::move(xWidget)) + , m_pCtrl(&_rCtrl) + , m_bRelease(true) + , m_xFrame(_xFrame) +{ + m_xWidget->set_size_request(150, -1); + + m_xWidget->make_sorted(); + m_xWidget->freeze(); + for (sal_uInt16 i = 0; i < NID_COUNT; i++) + m_xWidget->append(OUString::number(aNavigationInsertIds[i]), SwResId(aNavigationStrIds[i]), aNavigationImgIds[i]); + m_xWidget->thaw(); + + m_xWidget->connect_changed(LINK(this, NavElementBox_Base, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, NavElementBox_Base, KeyInputHdl)); +} + +NavElementBox_Impl::NavElementBox_Impl( + vcl::Window* _pParent, + const uno::Reference< frame::XFrame >& _xFrame, + NavElementToolBoxControl& _rCtrl ) + : InterimItemWindow(_pParent, "modules/swriter/ui/combobox.ui", "ComboBox") + , NavElementBox_Base(m_xBuilder->weld_combo_box("combobox"), _xFrame, _rCtrl) +{ + SetSizePixel(m_xContainer->get_preferred_size()); +} + +void NavElementBox_Base::ReleaseFocus_Impl() +{ + if ( !m_bRelease ) + { + m_bRelease = true; + return; + } + + if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() ) + m_xFrame->getContainerWindow()->setFocus(); +} + +IMPL_LINK(NavElementBox_Base, SelectHdl, weld::ComboBox&, rComboBox, void) +{ + if (rComboBox.changed_by_direct_pick()) // only when picked from the list + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + + sal_uInt16 nMoveType = rComboBox.get_active_id().toUInt32(); + SwView::SetMoveType( nMoveType ); + + css::uno::Sequence< css::beans::PropertyValue > aArgs; + + /* #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call. + This instance may be deleted in the meantime (i.e. when a dialog is opened + while in Dispatch()), accessing members will crash in this case. */ + ReleaseFocus_Impl(); + + m_pCtrl->dispatchCommand( aArgs ); + } +} + +void NavElementBox_Base::UpdateBox() +{ + sal_uInt16 nMoveType = SwView::GetMoveType(); + for ( size_t i = 0; i < SAL_N_ELEMENTS( aNavigationInsertIds ); ++i ) + { + if ( nMoveType == aNavigationInsertIds[i] ) + { + const char* id = aNavigationStrIds[i]; + OUString sText = SwResId( id ); + m_xWidget->set_active_text(sText); + break; + } + } +} + +IMPL_LINK(NavElementBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return DoKeyInput(rKEvt); +} + +bool NavElementBox_Base::DoKeyInput(const KeyEvent& rKEvt) +{ + bool bHandled = false; + + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = aKeyCode.GetCode(); + + switch ( nCode ) + { + case KEY_TAB: + { + m_bRelease = false; + SelectHdl(*m_xWidget); + break; + } + case KEY_RETURN: + { + bHandled = true; + SelectHdl(*m_xWidget); + break; + } + case KEY_ESCAPE: + ReleaseFocus_Impl(); + bHandled = true; + break; + } + + return bHandled; +} + +bool NavElementBox_Impl::DoKeyInput(const KeyEvent& rKEvt) +{ + return NavElementBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt); +} + + +NavElementToolBoxControl::NavElementToolBoxControl( const uno::Reference< uno::XComponentContext >& rxContext ) + : svt::ToolboxController( rxContext, + uno::Reference< frame::XFrame >(), + ".uno:NavElement" ), + m_pBox( nullptr ) +{ +} + +// XInterface +css::uno::Any SAL_CALL NavElementToolBoxControl::queryInterface( const css::uno::Type& aType ) +{ + uno::Any a = ToolboxController::queryInterface( aType ); + if ( a.hasValue() ) + return a; + + return ::cppu::queryInterface( aType, static_cast< lang::XServiceInfo* >( this ) ); +} + +void SAL_CALL NavElementToolBoxControl::acquire() throw () +{ + ToolboxController::acquire(); +} + +void SAL_CALL NavElementToolBoxControl::release() throw () +{ + ToolboxController::release(); +} + +// XServiceInfo +sal_Bool SAL_CALL NavElementToolBoxControl::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +OUString SAL_CALL NavElementToolBoxControl::getImplementationName() +{ + return "lo.writer.NavElementToolBoxController"; +} + +uno::Sequence< OUString > SAL_CALL NavElementToolBoxControl::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +// XComponent +void SAL_CALL NavElementToolBoxControl::dispose() +{ + svt::ToolboxController::dispose(); + + SolarMutexGuard aSolarMutexGuard; + m_xVclBox.disposeAndClear(); + m_xWeldBox.reset(); + m_pBox = nullptr; +} + +// XStatusListener +void SAL_CALL NavElementToolBoxControl::statusChanged( const frame::FeatureStateEvent& rEvent ) +{ + if (m_pBox) + { + SolarMutexGuard aSolarMutexGuard; + if ( rEvent.FeatureURL.Path == "NavElement" ) + { + if ( rEvent.IsEnabled ) + { + m_pBox->set_sensitive(true); + m_pBox->UpdateBox(); + } + else + m_pBox->set_sensitive(true); + } + } +} + +// XToolbarController +void SAL_CALL NavElementToolBoxControl::execute( sal_Int16 /*KeyModifier*/ ) +{ +} + +void SAL_CALL NavElementToolBoxControl::click() +{ +} + +void SAL_CALL NavElementToolBoxControl::doubleClick() +{ +} + +uno::Reference< awt::XWindow > SAL_CALL NavElementToolBoxControl::createPopupWindow() +{ + return uno::Reference< awt::XWindow >(); +} + +uno::Reference< awt::XWindow > SAL_CALL NavElementToolBoxControl::createItemWindow( + const uno::Reference< awt::XWindow >& xParent ) +{ + uno::Reference< awt::XWindow > xItemWindow; + + if (m_pBuilder) + { + SolarMutexGuard aSolarMutexGuard; + + std::unique_ptr<weld::ComboBox> xWidget(m_pBuilder->weld_combo_box("NavElementWidget")); + + xItemWindow = css::uno::Reference<css::awt::XWindow>(new weld::TransportAsXWindow(xWidget.get())); + + m_xWeldBox.reset(new NavElementBox_Base(std::move(xWidget), m_xFrame, *this)); + m_pBox = m_xWeldBox.get(); + } + else + { + VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent ); + if ( pParent ) + { + SolarMutexGuard aSolarMutexGuard; + m_xVclBox = VclPtr<NavElementBox_Impl>::Create( pParent, m_xFrame, *this ); + m_pBox = m_xVclBox.get(); + xItemWindow = VCLUnoHelper::GetInterface(m_xVclBox); + } + } + + return xItemWindow; +} + +void NavElementToolBoxControl::dispatchCommand( + const uno::Sequence< beans::PropertyValue >& rArgs ) +{ + uno::Reference< frame::XDispatchProvider > xDispatchProvider( m_xFrame, uno::UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + util::URL aURL; + uno::Reference< frame::XDispatch > xDispatch; + uno::Reference< util::XURLTransformer > xURLTransformer = getURLTransformer(); + + aURL.Complete = ".uno:NavElement"; + xURLTransformer->parseStrict( aURL ); + xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( aURL, rArgs ); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_NavElementToolBoxController_get_implementation( + css::uno::XComponentContext *rxContext, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire( new NavElementToolBoxControl( rxContext ) ); +} + +namespace { + +class PrevNextScrollToolboxController : public svt::ToolboxController, + public css::lang::XServiceInfo +{ +public: + enum Type { PREVIOUS, NEXT }; + + PrevNextScrollToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext, Type eType ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire() throw () override; + virtual void SAL_CALL release() throw () 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; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override; + +private: + Type meType; +}; + +} + +PrevNextScrollToolboxController::PrevNextScrollToolboxController( const css::uno::Reference< css::uno::XComponentContext > & rxContext, Type eType ) + : svt::ToolboxController( rxContext, + css::uno::Reference< css::frame::XFrame >(), + (eType == PREVIOUS) ? OUString( ".uno:ScrollToPrevious" ): OUString( ".uno:ScrollToNext" ) ), + meType( eType ) +{ + addStatusListener(".uno:NavElement"); +} + +// XInterface +css::uno::Any SAL_CALL PrevNextScrollToolboxController::queryInterface( const css::uno::Type& aType ) +{ + css::uno::Any a = ToolboxController::queryInterface( aType ); + if ( a.hasValue() ) + return a; + + return ::cppu::queryInterface( aType, static_cast< css::lang::XServiceInfo* >( this ) ); +} + +void SAL_CALL PrevNextScrollToolboxController::acquire() throw () +{ + ToolboxController::acquire(); +} + +void SAL_CALL PrevNextScrollToolboxController::release() throw () +{ + ToolboxController::release(); +} + +// XServiceInfo +OUString SAL_CALL PrevNextScrollToolboxController::getImplementationName() +{ + return meType == PrevNextScrollToolboxController::PREVIOUS? + OUString( "lo.writer.PreviousScrollToolboxController" ) : + OUString( "lo.writer.NextScrollToolboxController" ); +} + +sal_Bool SAL_CALL PrevNextScrollToolboxController::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL PrevNextScrollToolboxController::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +// XComponent +void SAL_CALL PrevNextScrollToolboxController::dispose() +{ + SolarMutexGuard aSolarMutexGuard; + + svt::ToolboxController::dispose(); +} + +// XStatusListener +void SAL_CALL PrevNextScrollToolboxController::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + if (rEvent.FeatureURL.Path == "NavElement") + { + if (m_pToolbar) + m_pToolbar->set_item_tooltip_text(m_aCommandURL.toUtf8(), lcl_GetScrollToolTip(meType != PrevNextScrollToolboxController::PREVIOUS)); + else + { + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if (getToolboxId(nId, &pToolBox)) + pToolBox->SetQuickHelpText(nId, lcl_GetScrollToolTip(meType != PrevNextScrollToolboxController::PREVIOUS)); + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_PreviousScrollToolboxController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire( new PrevNextScrollToolboxController( context, PrevNextScrollToolboxController::PREVIOUS ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_NextScrollToolboxController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire( new PrevNextScrollToolboxController( context, PrevNextScrollToolboxController::NEXT ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/annotsh.cxx b/sw/source/uibase/shells/annotsh.cxx new file mode 100644 index 000000000..5bcda2e9a --- /dev/null +++ b/sw/source/uibase/shells/annotsh.cxx @@ -0,0 +1,1812 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/TextConversionOption.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> + +#include <i18nutil/transliteration.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/fontitem.hxx> +#include <svx/clipfmtitem.hxx> +#include <svl/stritem.hxx> +#include <svl/slstitm.hxx> +#include <editeng/colritem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svl/whiter.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <unotools/useroptions.hxx> +#include <editeng/flditem.hxx> +#include <svx/hlnkitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/langitem.hxx> +#include <editeng/scripttypeitem.hxx> +#include <swundo.hxx> +#include <doc.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <chrdlgmodes.hxx> +#include <edtwin.hxx> +#include <SwRewriter.hxx> + +#include <cmdid.h> +#include <strings.hrc> +#include <breakit.hxx> +#include <annotsh.hxx> +#include <view.hxx> +#include <PostItMgr.hxx> +#include <AnnotationWin.hxx> + +#include <swtypes.hxx> + +#include <svx/svxdlg.hxx> + +#include <vcl/EnumContext.hxx> +#include <svl/itempool.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> + +#include <svl/languageoptions.hxx> + +#include <svl/undo.hxx> +#include <swabstdlg.hxx> + +#include <comphelper/string.hxx> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/bootstrap.hxx> + +#include <langhelper.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; + +#define ShellClass_SwAnnotationShell + +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwAnnotationShell, SfxShell) + +void SwAnnotationShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Text_Toolbox_Sw); + + GetStaticInterface()->RegisterPopupMenu("annotation"); +} + + +SfxItemPool* SwAnnotationShell::GetAnnotationPool(SwView const & rV) +{ + SwWrtShell &rSh = rV.GetWrtShell(); + return rSh.GetAttrPool().GetSecondaryPool(); +} + +SwAnnotationShell::SwAnnotationShell( SwView& r ) + : rView(r) +{ + SetPool(SwAnnotationShell::GetAnnotationPool(rView)); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Annotation)); +} + +SwAnnotationShell::~SwAnnotationShell() +{ +} + +SfxUndoManager* SwAnnotationShell::GetUndoManager() +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || + !pPostItMgr->HasActiveSidebarWin() ) + { + OSL_ENSURE(pPostItMgr,"PostItMgr::Layout(): We are looping forever"); + return nullptr; + } + return &pPostItMgr->GetActiveSidebarWin()->GetOutlinerView()->GetOutliner()->GetUndoManager(); +} + +void SwAnnotationShell::Exec( SfxRequest &rReq ) +{ + //TODO: clean this up!!!! + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + SfxItemSet aEditAttr(pOLV->GetAttribs()); + SfxItemSet aNewAttr(*aEditAttr.GetPool(), aEditAttr.GetRanges()); + + sal_uInt16 nSlot = rReq.GetSlot(); + sal_uInt16 nWhich = GetPool().GetWhich(nSlot); + const SfxItemSet *pNewAttrs = rReq.GetArgs(); + sal_uInt16 nEEWhich = 0; + switch (nSlot) + { + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SvxULSpaceItem aULSpace( aEditAttr.Get( EE_PARA_ULSPACE ) ); + sal_uInt16 nUpper = aULSpace.GetUpper(); + sal_uInt16 nLower = aULSpace.GetLower(); + + if ( nSlot == SID_PARASPACE_INCREASE ) + { + nUpper = std::min< sal_uInt16 >( nUpper + 57, 5670 ); + nLower = std::min< sal_uInt16 >( nLower + 57, 5670 ); + } + else + { + nUpper = std::max< sal_Int16 >( nUpper - 57, 0 ); + nLower = std::max< sal_Int16 >( nLower - 57, 0 ); + } + + aULSpace.SetUpper( nUpper ); + aULSpace.SetLower( nLower ); + aNewAttr.Put( aULSpace ); + rReq.Done(); + } + break; + case SID_ATTR_PARA_LRSPACE: + { + SvxLRSpaceItem aParaMargin(static_cast<const SvxLRSpaceItem&>(rReq. + GetArgs()->Get(nSlot))); + aParaMargin.SetWhich( EE_PARA_LRSPACE ); + + aNewAttr.Put(aParaMargin); + rReq.Done(); + break; + } + case SID_ATTR_PARA_LINESPACE: + { + SvxLineSpacingItem aParaMargin = static_cast<const SvxLineSpacingItem&>(pNewAttrs->Get( + GetPool().GetWhich(nSlot))); + aParaMargin.SetWhich( EE_PARA_SBL ); + + aNewAttr.Put(aParaMargin); + rReq.Done(); + break; + } + case SID_ATTR_PARA_ULSPACE: + { + SvxULSpaceItem aULSpace = static_cast<const SvxULSpaceItem&>(pNewAttrs->Get( + GetPool().GetWhich(nSlot))); + aULSpace.SetWhich( EE_PARA_ULSPACE ); + aNewAttr.Put( aULSpace ); + rReq.Done(); + } + break; + case FN_GROW_FONT_SIZE: + case FN_SHRINK_FONT_SIZE: + { + const SvxFontListItem* pFontListItem = static_cast< const SvxFontListItem* > + ( SfxObjectShell::Current()->GetItem( SID_ATTR_CHAR_FONTLIST ) ); + const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr; + pOLV->GetEditView().ChangeFontSize( nSlot == FN_GROW_FONT_SIZE, pFontList ); + } + break; + + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + { + SfxItemPool* pSecondPool = aEditAttr.GetPool()->GetSecondaryPool(); + if( !pSecondPool ) + pSecondPool = aEditAttr.GetPool(); + SvxScriptSetItem aSetItem( nSlot, *pSecondPool ); + aSetItem.PutItemForScriptType( pOLV->GetSelectedScriptType(), pNewAttrs->Get( nWhich )); + aNewAttr.Put( aSetItem.GetItemSet() ); + rReq.Done(); + break; + } + case SID_ATTR_CHAR_COLOR: nEEWhich = EE_CHAR_COLOR; break; + case SID_ATTR_CHAR_BACK_COLOR: nEEWhich = EE_CHAR_BKGCOLOR; break; + case SID_ATTR_CHAR_UNDERLINE: + { + if( rReq.GetArgs() ) + { + const SvxUnderlineItem* pItem = rReq.GetArg<SvxUnderlineItem>(SID_ATTR_CHAR_UNDERLINE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + else + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU != LINESTYLE_NONE ?LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE ) ); + } + } + break; + } + case SID_ATTR_CHAR_OVERLINE: + { + FontLineStyle eFO = aEditAttr.Get(EE_CHAR_OVERLINE).GetLineStyle(); + aNewAttr.Put(SvxOverlineItem(eFO == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_OVERLINE)); + break; + } + case SID_ATTR_CHAR_CONTOUR: nEEWhich = EE_CHAR_OUTLINE; break; + case SID_ATTR_CHAR_SHADOWED: nEEWhich = EE_CHAR_SHADOW; break; + case SID_ATTR_CHAR_STRIKEOUT: nEEWhich = EE_CHAR_STRIKEOUT; break; + case SID_ATTR_CHAR_WORDLINEMODE: nEEWhich = EE_CHAR_WLM; break; + case SID_ATTR_CHAR_RELIEF : nEEWhich = EE_CHAR_RELIEF; break; + case SID_ATTR_CHAR_LANGUAGE : nEEWhich = EE_CHAR_LANGUAGE;break; + case SID_ATTR_CHAR_KERNING : nEEWhich = EE_CHAR_KERNING; break; + case SID_ATTR_CHAR_SCALEWIDTH: nEEWhich = EE_CHAR_FONTWIDTH; break; + case SID_ATTR_CHAR_AUTOKERN : nEEWhich = EE_CHAR_PAIRKERNING; break; + case SID_ATTR_CHAR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT; break; + case SID_ATTR_PARA_ADJUST_LEFT: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Left, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_CENTER: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Right, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Block, EE_PARA_JUST)); + break; + + case SID_ATTR_PARA_LINESPACE_10: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(100); + aNewAttr.Put(aItem); + } + break; + case SID_ATTR_PARA_LINESPACE_15: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(150); + aNewAttr.Put(aItem); + } + break; + case SID_ATTR_PARA_LINESPACE_20: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(200); + aNewAttr.Put(aItem); + } + break; + case SID_SELECTALL: + { + Outliner * pOutliner = pOLV->GetOutliner(); + if(pOutliner) + { + sal_Int32 nParaCount = pOutliner->GetParagraphCount(); + if (nParaCount > 0) + pOLV->SelectRange(0, nParaCount ); + } + break; + } + case FN_FORMAT_RESET: + { + pPostItMgr->GetActiveSidebarWin()->ResetAttributes(); + rReq.Done(); + break; + } + case FN_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case FN_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_HYPERLINK_SETLINK: + { + const SfxPoolItem* pItem = nullptr; + if(pNewAttrs) + pNewAttrs->GetItemState(nSlot, false, &pItem); + + if(pItem) + { + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + SvxURLField aField(rHLinkItem.GetURL(), rHLinkItem.GetName(), SvxURLFormat::AppDefault); + aField.SetTargetFrame(rHLinkItem.GetTargetFrame()); + + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + + if (pFieldItem && dynamic_cast<const SvxURLField *>(pFieldItem->GetField()) != nullptr) + { + // Select the field so that it will be deleted during insert + ESelection aSel = pOLV->GetSelection(); + aSel.nEndPos++; + pOLV->SetSelection(aSel); + } + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + pOLV->InsertField(SvxFieldItem(aField, EE_FEATURE_FIELD)); + } + break; + } + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_ZWNBSP : + case SID_INSERT_ZWSP: + { + sal_Unicode cIns = 0; + switch(rReq.GetSlot()) + { + case FN_INSERT_SOFT_HYPHEN: cIns = CHAR_SOFTHYPHEN; break; + case FN_INSERT_HARDHYPHEN: cIns = CHAR_HARDHYPHEN; break; + case FN_INSERT_HARD_SPACE: cIns = CHAR_HARDBLANK; break; + case FN_INSERT_NNBSP: cIns = CHAR_NNBSP; break; + case SID_INSERT_RLM : cIns = CHAR_RLM ; break; + case SID_INSERT_LRM : cIns = CHAR_LRM ; break; + case SID_INSERT_ZWSP : cIns = CHAR_ZWSP ; break; + case SID_INSERT_ZWNBSP: cIns = CHAR_ZWNBSP; break; + } + pOLV->InsertText( OUString(cIns)); + rReq.Done(); + break; + } + case SID_CHARMAP: + { + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + InsertSymbol(rReq); + break; + } + case FN_INSERT_STRING: + { + const SfxPoolItem* pItem = nullptr; + if (pNewAttrs) + pNewAttrs->GetItemState(nSlot, false, &pItem ); + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + pOLV->InsertText(static_cast<const SfxStringItem *>(pItem)->GetValue()); + break; + } + case FN_FORMAT_FOOTNOTE_DLG: + { + rView.ExecFormatFootnote(); + break; + } + case FN_NUMBERING_OUTLINE_DLG: + { + rView.ExecNumberingOutline(GetPool()); + rReq.Done(); + } + break; + case SID_OPEN_XML_FILTERSETTINGS: + { + HandleOpenXmlFilterSettings(rReq); + } + break; + case FN_WORDCOUNT_DIALOG: + { + rView.UpdateWordCount(this, nSlot); + break; + } + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + + if( !pArgs || pItem ) + { + /* mod + SwView* pView = &GetView(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( pView) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, eMetric)); + */ + SfxItemSet aDlgAttr(GetPool(), svl::Items<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END>{}); + + // util::Language does not exist in the EditEngine! Therefore not included in the set. + + aDlgAttr.Put( aEditAttr ); + aDlgAttr.Put( SvxKerningItem(0, RES_CHRATR_KERNING) ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(rView.GetFrameWeld(), rView, aDlgAttr, SwCharDlgMode::Ann)); + if (nSlot == SID_CHAR_DLG_EFFECT) + { + pDlg->SetCurPageId("fonteffects"); + } + else if (pItem) + { + pDlg->SetCurPageId(OUStringToOString(pItem->GetValue(), RTL_TEXTENCODING_UTF8)); + } + + sal_uInt16 nRet = pDlg->Execute(); + if(RET_OK == nRet ) + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + aNewAttr.Put(*pDlg->GetOutputItemSet()); + } + if(RET_OK != nRet) + return ; + } + else + aNewAttr.Put(*pArgs); + break; + } + case SID_PARA_DLG: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (!pArgs) + { + /* mod todo ??? + SwView* pView = &GetView(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( pView) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, eMetric)); + */ + SfxItemSet aDlgAttr( + GetPool(), + svl::Items< + EE_ITEMS_START, EE_ITEMS_END, + SID_ATTR_PARA_HYPHENZONE, SID_ATTR_PARA_WIDOWS>{}); + + aDlgAttr.Put(aEditAttr); + + aDlgAttr.Put( SvxHyphenZoneItem( false, RES_PARATR_HYPHENZONE) ); + aDlgAttr.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) ); + aDlgAttr.Put( SvxFormatSplitItem( true, RES_PARATR_SPLIT ) ); + aDlgAttr.Put( SvxWidowsItem( 0, RES_PARATR_WIDOWS ) ); + aDlgAttr.Put( SvxOrphansItem( 0, RES_PARATR_ORPHANS ) ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwParaDlg(rView.GetFrameWeld(), rView, aDlgAttr, true)); + sal_uInt16 nRet = pDlg->Execute(); + if(RET_OK == nRet) + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + aNewAttr.Put(*pDlg->GetOutputItemSet()); + } + if(RET_OK != nRet) + return; + } + else + aNewAttr.Put(*pArgs); + break; + } + + case SID_AUTOSPELL_CHECK: + { + rView.ExecuteSlot(rReq); + break; + } + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + { + bool bLeftToRight = nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT; + + const SfxPoolItem* pPoolItem; + if( pNewAttrs && SfxItemState::SET == pNewAttrs->GetItemState( nSlot, true, &pPoolItem ) ) + { + if( !static_cast<const SfxBoolItem*>(pPoolItem)->GetValue() ) + bLeftToRight = !bLeftToRight; + } + SfxItemSet aAttr( + *aNewAttr.GetPool(), + svl::Items< + EE_PARA_WRITINGDIR, EE_PARA_WRITINGDIR, + EE_PARA_JUST, EE_PARA_JUST>{}); + + SvxAdjust nAdjust = SvxAdjust::Left; + if( SfxItemState::SET == aEditAttr.GetItemState(EE_PARA_JUST, true, &pPoolItem ) ) + nAdjust = static_cast<const SvxAdjustItem*>(pPoolItem)->GetAdjust(); + + if( bLeftToRight ) + { + aAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Right ) + aAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + else + { + aAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Left ) + aAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + pOLV->SetAttribs(aAttr); + break; + } + } + + if(nEEWhich && pNewAttrs) + { + aNewAttr.Put(pNewAttrs->Get(nWhich).CloneSetWhich(nEEWhich)); + } + + tools::Rectangle aOutRect = pOLV->GetOutputArea(); + if (tools::Rectangle() != aOutRect && aNewAttr.Count()) + pOLV->SetAttribs(aNewAttr); + + rView.GetViewFrame()->GetBindings().InvalidateAll(false); + if ( pOLV->GetOutliner()->IsModified() ) + rView.GetWrtShell().SetModified(); + +} + +void SwAnnotationShell::GetState(SfxItemSet& rSet) +{ + //TODO: clean this up!!! + // FN_SET_SUPER_SCRIPT + //SID_ATTR_PARA_ADJUST + //SID_ATTR_PARA_ADJUST_BLOCK + + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + SfxItemSet aEditAttr(pOLV->GetAttribs()); + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while(nWhich) + { + sal_uInt16 nEEWhich = 0; + sal_uInt16 nSlotId = GetPool().GetSlotId( nWhich ); + switch( nSlotId ) + { + case SID_ATTR_PARA_LRSPACE: + case SID_ATTR_PARA_LEFTSPACE: + case SID_ATTR_PARA_RIGHTSPACE: + case SID_ATTR_PARA_FIRSTLINESPACE: + { + SfxItemState eState = aEditAttr.GetItemState( EE_PARA_LRSPACE ); + if( eState >= SfxItemState::DEFAULT ) + { + SvxLRSpaceItem aLR = aEditAttr.Get( EE_PARA_LRSPACE ); + aLR.SetWhich(nSlotId); + rSet.Put(aLR); + } + else + rSet.InvalidateItem(nSlotId); + } + break; + case SID_ATTR_PARA_LINESPACE: + { + SfxItemState eState = aEditAttr.GetItemState( EE_PARA_SBL ); + if( eState >= SfxItemState::DEFAULT ) + { + const SvxLineSpacingItem& aLR = aEditAttr.Get( EE_PARA_SBL ); + rSet.Put(aLR); + } + else + rSet.InvalidateItem(nSlotId); + } + break; + case SID_ATTR_PARA_ULSPACE: + case SID_ATTR_PARA_ABOVESPACE: + case SID_ATTR_PARA_BELOWSPACE: + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SfxItemState eState = aEditAttr.GetItemState( EE_PARA_ULSPACE ); + if( eState >= SfxItemState::DEFAULT ) + { + SvxULSpaceItem aULSpace = aEditAttr.Get( EE_PARA_ULSPACE ); + if ( !aULSpace.GetUpper() && !aULSpace.GetLower() ) + rSet.DisableItem( SID_PARASPACE_DECREASE ); + else if ( aULSpace.GetUpper() >= 5670 && aULSpace.GetLower() >= 5670 ) + rSet.DisableItem( SID_PARASPACE_INCREASE ); + if ( nSlotId == SID_ATTR_PARA_ULSPACE + || nSlotId == SID_ATTR_PARA_BELOWSPACE + || nSlotId == SID_ATTR_PARA_ABOVESPACE + ) + { + aULSpace.SetWhich(nSlotId); + rSet.Put(aULSpace); + } + } + else + { + rSet.DisableItem( SID_PARASPACE_INCREASE ); + rSet.DisableItem( SID_PARASPACE_DECREASE ); + rSet.InvalidateItem( SID_ATTR_PARA_ULSPACE ); + rSet.InvalidateItem( SID_ATTR_PARA_ABOVESPACE ); + rSet.InvalidateItem( SID_ATTR_PARA_BELOWSPACE ); + } + } + break; + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + { + SvtScriptType nScriptType = pOLV->GetSelectedScriptType(); + SfxItemPool* pSecondPool = aEditAttr.GetPool()->GetSecondaryPool(); + if( !pSecondPool ) + pSecondPool = aEditAttr.GetPool(); + SvxScriptSetItem aSetItem( nSlotId, *pSecondPool ); + aSetItem.GetItemSet().Put( aEditAttr, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScriptType ); + if( pI ) + { + rSet.Put(pI->CloneSetWhich(nWhich)); + } + else + rSet.InvalidateItem( nWhich ); + } + break; + case SID_ATTR_CHAR_COLOR: nEEWhich = EE_CHAR_COLOR; break; + case SID_ATTR_CHAR_BACK_COLOR: nEEWhich = EE_CHAR_BKGCOLOR; break; + case SID_ATTR_CHAR_UNDERLINE: nEEWhich = EE_CHAR_UNDERLINE;break; + case SID_ATTR_CHAR_OVERLINE: nEEWhich = EE_CHAR_OVERLINE;break; + case SID_ATTR_CHAR_CONTOUR: nEEWhich = EE_CHAR_OUTLINE; break; + case SID_ATTR_CHAR_SHADOWED: nEEWhich = EE_CHAR_SHADOW;break; + case SID_ATTR_CHAR_STRIKEOUT: nEEWhich = EE_CHAR_STRIKEOUT;break; + case SID_ATTR_CHAR_LANGUAGE : nEEWhich = EE_CHAR_LANGUAGE;break; + case SID_ATTR_CHAR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT;break; + case SID_ATTR_CHAR_KERNING: nEEWhich = EE_CHAR_KERNING;break; + case FN_SET_SUPER_SCRIPT: + case FN_SET_SUB_SCRIPT: + { + SvxEscapement nEsc; + if (nWhich==FN_SET_SUPER_SCRIPT) + nEsc = SvxEscapement::Superscript; + else + nEsc = SvxEscapement::Subscript; + + const SfxPoolItem *pEscItem = &aEditAttr.Get( EE_CHAR_ESCAPEMENT ); + if( nEsc == static_cast<const SvxEscapementItem*>(pEscItem)->GetEscapement() ) + rSet.Put( SfxBoolItem( nWhich, true )); + else + rSet.InvalidateItem( nWhich ); + break; + } + case SID_ATTR_PARA_ADJUST_LEFT: + case SID_ATTR_PARA_ADJUST_RIGHT: + case SID_ATTR_PARA_ADJUST_CENTER: + case SID_ATTR_PARA_ADJUST_BLOCK: + { + SvxAdjust eAdjust = SvxAdjust::Left; + if (nWhich==SID_ATTR_PARA_ADJUST_LEFT) + eAdjust = SvxAdjust::Left; + else if (nWhich==SID_ATTR_PARA_ADJUST_RIGHT) + eAdjust = SvxAdjust::Right; + else if (nWhich==SID_ATTR_PARA_ADJUST_CENTER) + eAdjust = SvxAdjust::Center; + else if (nWhich==SID_ATTR_PARA_ADJUST_BLOCK) + eAdjust = SvxAdjust::Block; + + const SfxPoolItem *pAdjust = nullptr; + aEditAttr.GetItemState( EE_PARA_JUST, false, &pAdjust); + + if( !pAdjust || IsInvalidItem( pAdjust )) + { + rSet.InvalidateItem( nSlotId ); + } + else + { + if ( eAdjust == static_cast<const SvxAdjustItem*>(pAdjust)->GetAdjust()) + rSet.Put( SfxBoolItem( nWhich, true )); + else + rSet.InvalidateItem( nWhich ); + } + break; + } + case SID_ATTR_PARA_LINESPACE_10: + case SID_ATTR_PARA_LINESPACE_15: + case SID_ATTR_PARA_LINESPACE_20: + { + int nLSpace = 0; + if (nWhich==SID_ATTR_PARA_LINESPACE_10) + nLSpace = 100; + else if (nWhich==SID_ATTR_PARA_LINESPACE_15) + nLSpace = 150; + else if (nWhich==SID_ATTR_PARA_LINESPACE_20) + nLSpace = 200; + + const SfxPoolItem *pLSpace = nullptr; + aEditAttr.GetItemState( EE_PARA_SBL, false, &pLSpace ); + + if( !pLSpace || IsInvalidItem( pLSpace )) + { + rSet.InvalidateItem( nSlotId ); + } + else + { + if( nLSpace == static_cast<const SvxLineSpacingItem*>(pLSpace)->GetPropLineSpace() ) + rSet.Put( SfxBoolItem( nWhich, true )); + else + rSet.InvalidateItem( nWhich ); + } + break; + } + case SID_AUTOSPELL_CHECK: + { + const SfxPoolItem* pState = rView.GetSlotState(nWhich); + if (pState) + rSet.Put(SfxBoolItem(nWhich, static_cast<const SfxBoolItem*>(pState)->GetValue())); + else + rSet.DisableItem( nWhich ); + break; + } + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + { + if ( !SvtLanguageOptions().IsCTLFontEnabled() ) + rSet.DisableItem( nWhich ); + else + { + if(pOLV->GetOutliner() && pOLV->GetOutliner()->IsVertical()) + rSet.DisableItem( nWhich ); + else + { + bool bFlag = false; + switch( aEditAttr.Get( EE_PARA_WRITINGDIR ).GetValue() ) + { + case SvxFrameDirection::Horizontal_LR_TB: + { + bFlag = nWhich == SID_ATTR_PARA_LEFT_TO_RIGHT; + rSet.Put( SfxBoolItem( nWhich, bFlag )); + break; + } + case SvxFrameDirection::Horizontal_RL_TB: + { + bFlag = nWhich != SID_ATTR_PARA_LEFT_TO_RIGHT; + rSet.Put( SfxBoolItem( nWhich, bFlag )); + break; + } + default: + break; + } + } + } + } + break; + case SID_INSERT_RLM : + case SID_INSERT_LRM : + { + SvtCTLOptions aCTLOptions; + bool bEnabled = aCTLOptions.IsCTLFontEnabled(); + rView.GetViewFrame()->GetBindings().SetVisibleState( nWhich, bEnabled ); + if(!bEnabled) + rSet.DisableItem(nWhich); + } + break; + default: + rSet.InvalidateItem( nWhich ); + break; + } + + if(nEEWhich) + { + rSet.Put(aEditAttr.Get(nEEWhich).CloneSetWhich(nWhich)); + if(nEEWhich == EE_CHAR_KERNING) + { + SfxItemState eState = aEditAttr.GetItemState( EE_CHAR_KERNING ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_CHAR_KERNING); + } + } + } + + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()==SwPostItHelper::DELETED) + rSet.DisableItem( nWhich ); + + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::ExecSearch(SfxRequest& rReq) +{ + rView.ExecSearch(rReq); +} + +void SwAnnotationShell::StateSearch(SfxItemSet &rSet) +{ + rView.StateSearch(rSet); +} + +void SwAnnotationShell::ExecClpbrd(SfxRequest const &rReq) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + long aOldHeight = pPostItMgr->GetActiveSidebarWin()->GetPostItTextHeight(); + sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case SID_CUT: + if ( (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) && pOLV->HasSelection() ) + pOLV->Cut(); + break; + case SID_COPY: + if( pOLV->HasSelection() ) + pOLV->Copy(); + break; + case SID_PASTE: + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + pOLV->PasteSpecial(); + break; + case SID_PASTE_UNFORMATTED: + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + pOLV->Paste(); + break; + case SID_PASTE_SPECIAL: + { + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(rView.GetEditWin().GetFrameWeld())); + + pDlg->Insert( SotClipboardFormatId::STRING, OUString() ); + pDlg->Insert( SotClipboardFormatId::RTF, OUString() ); + pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() ); + + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( &rView.GetEditWin() ) ); + + SotClipboardFormatId nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() ); + + if (nFormat != SotClipboardFormatId::NONE) + { + if (nFormat == SotClipboardFormatId::STRING) + pOLV->Paste(); + else + pOLV->PasteSpecial(); + } + } + break; + } + case SID_CLIPBOARD_FORMAT_ITEMS: + { + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + const SfxPoolItem* pItem; + if (rReq.GetArgs() && rReq.GetArgs()->GetItemState(nSlot, true, &pItem) == SfxItemState::SET) + { + if (const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item *>(pItem)) + nFormat = static_cast<SotClipboardFormatId>(pUInt32Item->GetValue()); + } + + if ( nFormat != SotClipboardFormatId::NONE ) + { + if (SotClipboardFormatId::STRING == nFormat) + pOLV->Paste(); + else + pOLV->PasteSpecial(); + } + break; + } + } + pPostItMgr->GetActiveSidebarWin()->ResizeIfNecessary(aOldHeight,pPostItMgr->GetActiveSidebarWin()->GetPostItTextHeight()); +} + +void SwAnnotationShell::StateClpbrd(SfxItemSet &rSet) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( &rView.GetEditWin() ) ); + bool bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || aDataHelper.HasFormat( SotClipboardFormatId::RTF ) + || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT )); + bPastePossible = bPastePossible && (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()!=SwPostItHelper::DELETED); + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_CUT: + { + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus() == SwPostItHelper::DELETED) + rSet.DisableItem( nWhich ); + [[fallthrough]]; + } + case SID_COPY: + { + SfxObjectShell* pObjectShell = GetObjectShell(); + if (!pOLV->HasSelection() || (pObjectShell && pObjectShell->isContentExtractionLocked()) ) + rSet.DisableItem( nWhich ); + break; + } + case SID_PASTE: + case SID_PASTE_UNFORMATTED: + case SID_PASTE_SPECIAL: + { + if( !bPastePossible ) + rSet.DisableItem( nWhich ); + break; + } + case SID_CLIPBOARD_FORMAT_ITEMS: + { + if ( bPastePossible ) + { + SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS ); + if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ) + aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF ); + if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) + aFormats.AddClipbrdFormat( SotClipboardFormatId::RICHTEXT ); + aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING ); + rSet.Put( aFormats ); + } + else + rSet.DisableItem( nWhich ); + break; + } + } + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::StateStatusLine(SfxItemSet &rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while( nWhich ) + { + switch( nWhich ) + { + case FN_STAT_SELMODE: + { + rSet.Put(SfxUInt16Item(FN_STAT_SELMODE, 0)); + rSet.DisableItem( nWhich ); + break; + } + case FN_STAT_TEMPLATE: + { + rSet.DisableItem( nWhich ); + break; + } + } + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::StateInsert(SfxItemSet &rSet) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_HYPERLINK_GETLINK: + { + SvxHyperlinkItem aHLinkItem; + aHLinkItem.SetInsertMode(HLINK_FIELD); + + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + + if (pFieldItem) + { + if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField *>(pFieldItem->GetField())) + { + aHLinkItem.SetName(pURLField->GetRepresentation()); + aHLinkItem.SetURL(pURLField->GetURL()); + aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame()); + } + } + else + { + OUString sSel(pOLV->GetSelected()); + sSel = sSel.copy(0, std::min<sal_Int32>(255, sSel.getLength())); + aHLinkItem.SetName(comphelper::string::stripEnd(sSel, ' ')); + } + + sal_uInt16 nHtmlMode = ::GetHtmlMode(rView.GetDocShell()); + aHLinkItem.SetInsertMode(static_cast<SvxLinkInsertMode>(aHLinkItem.GetInsertMode() | + ((nHtmlMode & HTMLMODE_ON) != 0 ? HLINK_HTMLMODE : 0))); + + rSet.Put(aHLinkItem); + } + break; + } + + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()==SwPostItHelper::DELETED) + rSet.DisableItem( nWhich ); + + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::NoteExec(SfxRequest const &rReq) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr ) + return; + + sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case FN_REPLY: + case FN_POSTIT: + case FN_DELETE_COMMENT: + case FN_RESOLVE_NOTE: + if ( pPostItMgr->HasActiveSidebarWin() ) + pPostItMgr->GetActiveSidebarWin()->ExecuteCommand(nSlot); + break; + case FN_DELETE_ALL_NOTES: + pPostItMgr->Delete(); + break; + case FN_FORMAT_ALL_NOTES: + pPostItMgr->ExecuteFormatAllDialog(rView); + break; + case FN_DELETE_NOTE_AUTHOR: + { + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(nSlot); + if ( pItem ) + pPostItMgr->Delete( pItem->GetValue() ); + else if ( pPostItMgr->HasActiveSidebarWin() ) + pPostItMgr->Delete( pPostItMgr->GetActiveSidebarWin()->GetAuthor() ); + break; + } + case FN_HIDE_NOTE: + break; + case FN_HIDE_ALL_NOTES: + pPostItMgr->Hide(); + break; + case FN_HIDE_NOTE_AUTHOR: + { + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(nSlot); + if ( pItem ) + pPostItMgr->Hide( pItem->GetValue() ); + else if ( pPostItMgr->HasActiveSidebarWin() ) + pPostItMgr->Hide( pPostItMgr->GetActiveSidebarWin()->GetAuthor() ); + } + } +} + +void SwAnnotationShell::GetNoteState(SfxItemSet &rSet) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while(nWhich) + { + sal_uInt16 nSlotId = GetPool().GetSlotId( nWhich ); + switch( nSlotId ) + { + case FN_POSTIT: + case FN_DELETE_ALL_NOTES: + case FN_FORMAT_ALL_NOTES: + case FN_HIDE_NOTE: + case FN_HIDE_ALL_NOTES: + case FN_DELETE_COMMENT: + { + if( !pPostItMgr + || !pPostItMgr->HasActiveAnnotationWin() ) + { + rSet.DisableItem(nWhich); + } + break; + } + case FN_RESOLVE_NOTE: + { + if( !pPostItMgr + || !pPostItMgr->HasActiveAnnotationWin() ) + { + rSet.DisableItem(nWhich); + } + else + { + SfxBoolItem aBool(nWhich, pPostItMgr->GetActiveSidebarWin()->IsResolved()); + rSet.Put( aBool ); + } + break; + } + case FN_DELETE_NOTE_AUTHOR: + case FN_HIDE_NOTE_AUTHOR: + { + if( !pPostItMgr + || !pPostItMgr->HasActiveAnnotationWin() ) + { + rSet.DisableItem(nWhich); + } + else + { + OUString aText( nSlotId == FN_DELETE_NOTE_AUTHOR ? + SwResId( STR_DELETE_NOTE_AUTHOR ) : SwResId( STR_HIDE_NOTE_AUTHOR ) ); + SwRewriter aRewriter; + aRewriter.AddRule( UndoArg1, pPostItMgr->GetActiveSidebarWin()->GetAuthor() ); + aText = aRewriter.Apply( aText ); + SfxStringItem aItem( nSlotId, aText ); + rSet.Put( aItem ); + } + break; + } + case FN_REPLY: + { + if ( !pPostItMgr || + !pPostItMgr->HasActiveAnnotationWin() ) + { + rSet.DisableItem(nWhich); + } + else + { + SvtUserOptions aUserOpt; + OUString sAuthor; + if( (sAuthor = aUserOpt.GetFullName()).isEmpty() && + (sAuthor = aUserOpt.GetID()).isEmpty() ) + sAuthor = SwResId( STR_REDLINE_UNKNOWN_AUTHOR ); + if (sAuthor == pPostItMgr->GetActiveSidebarWin()->GetAuthor()) + rSet.DisableItem(nWhich); + } + break; + } + default: + rSet.InvalidateItem( nWhich ); + break; + } + + if (pPostItMgr && pPostItMgr->HasActiveSidebarWin()) + { + if ( (pPostItMgr->GetActiveSidebarWin()->IsProtected()) && + ( (nSlotId==FN_DELETE_COMMENT) || (nSlotId==FN_REPLY) ) ) + rSet.DisableItem( nWhich ); + } + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::ExecLingu(SfxRequest &rReq) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + sal_uInt16 nSlot = rReq.GetSlot(); + SwWrtShell &rSh = rView.GetWrtShell(); + bool bRestoreSelection = false; + ESelection aOldSelection; + + switch (nSlot) + { + case SID_LANGUAGE_STATUS: + { + aOldSelection = pOLV->GetSelection(); + if (!pOLV->GetEditView().HasSelection()) + { + pOLV->GetEditView().SelectCurrentWord(); + } + + bRestoreSelection = SwLangHelper::SetLanguageStatus(pOLV,rReq,rView,rSh); + break; + } + case SID_THES: + { + OUString aReplaceText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(SID_THES); + if (pItem2) + aReplaceText = pItem2->GetValue(); + if (!aReplaceText.isEmpty()) + ReplaceTextWithSynonym( pOLV->GetEditView(), aReplaceText ); + break; + } + case SID_THESAURUS: + { + pOLV->StartThesaurus(); + break; + } + case SID_HANGUL_HANJA_CONVERSION: + pOLV->StartTextConversion( LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, + i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true, false ); + break; + + case SID_CHINESE_CONVERSION: + { + //open ChineseTranslationDialog + Reference< XComponentContext > xContext( + ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one + if(xContext.is()) + { + Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() ); + if(xMCF.is()) + { + Reference< ui::dialogs::XExecutableDialog > xDialog( + xMCF->createInstanceWithContext( + "com.sun.star.linguistic2.ChineseTranslationDialog", xContext), + UNO_QUERY); + Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY ); + if( xInit.is() ) + { + // initialize dialog + uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(Reference<awt::XWindow>())} + })); + xInit->initialize( aSeq ); + + //execute dialog + sal_Int16 nDialogRet = xDialog->execute(); + if( RET_OK == nDialogRet ) + { + //get some parameters from the dialog + bool bToSimplified = true; + bool bUseVariants = true; + bool bCommonTerms = true; + Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY ); + if( xProp.is() ) + { + try + { + xProp->getPropertyValue( "IsDirectionToSimplified" ) >>= bToSimplified; + xProp->getPropertyValue( "IsUseCharacterVariants" ) >>= bUseVariants; + xProp->getPropertyValue( "IsTranslateCommonTerms" ) >>= bCommonTerms; + } + catch (const Exception&) + { + } + } + + //execute translation + LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; + LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; + sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0; + if( !bCommonTerms ) + nOptions = nOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER; + + vcl::Font aTargetFont = OutputDevice::GetDefaultFont( DefaultFontType::CJK_TEXT, + nTargetLang, GetDefaultFontFlags::OnlyOne ); + + pOLV->StartTextConversion( nSourceLang, nTargetLang, &aTargetFont, nOptions, false, false ); + } + } + Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); + } + } + } + break; + } + + if (bRestoreSelection) + { + // restore selection + pOLV->GetEditView().SetSelection( aOldSelection ); + } +} + +void SwAnnotationShell::GetLinguState(SfxItemSet &rSet) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while(nWhich) + { + switch (nWhich) + { + case SID_LANGUAGE_STATUS: + { + SwLangHelper::GetLanguageStatus(pOLV,rSet); + break; + } + + case SID_THES: + { + OUString aStatusVal; + LanguageType nLang = LANGUAGE_NONE; + bool bIsLookUpWord = GetStatusValueForThesaurusFromContext( aStatusVal, nLang, pOLV->GetEditView() ); + rSet.Put( SfxStringItem( SID_THES, aStatusVal ) ); + + // disable "Thesaurus" context menu entry if there is nothing to look up + uno::Reference< linguistic2::XThesaurus > xThes( ::GetThesaurus() ); + if (!bIsLookUpWord || + !xThes.is() || nLang == LANGUAGE_NONE || !xThes->hasLocale( LanguageTag::convertToLocale( nLang ) )) + rSet.DisableItem( SID_THES ); + break; + } + + // disable "Thesaurus" if the language is not supported + case SID_THESAURUS: + { + const SfxPoolItem &rItem = rView.GetWrtShell().GetDoc()->GetDefault( + GetWhichOfScript( RES_CHRATR_LANGUAGE, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage())) ); + LanguageType nLang = static_cast<const SvxLanguageItem &>( + rItem).GetLanguage(); + uno::Reference< linguistic2::XThesaurus > xThes( ::GetThesaurus() ); + if (!xThes.is() || nLang == LANGUAGE_NONE || + !xThes->hasLocale( LanguageTag::convertToLocale( nLang ) )) + rSet.DisableItem( SID_THESAURUS ); + } + break; + case SID_HANGUL_HANJA_CONVERSION: + case SID_CHINESE_CONVERSION: + { + if (!SvtCJKOptions().IsAnyEnabled()) + { + rView.GetViewFrame()->GetBindings().SetVisibleState( nWhich, false ); + rSet.DisableItem(nWhich); + } + else + rView.GetViewFrame()->GetBindings().SetVisibleState( nWhich, true ); + } + break; + } + + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()==SwPostItHelper::DELETED) + rSet.DisableItem( nWhich ); + + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::ExecTransliteration(SfxRequest const &rReq) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if (!pPostItMgr || !pPostItMgr->HasActiveSidebarWin()) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + if (!pOLV) + return; + + TransliterationFlags nMode = TransliterationFlags::NONE; + + switch( rReq.GetSlot() ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nMode = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nMode = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nMode = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nMode = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nMode = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + case SID_TRANSLITERATE_HALFWIDTH: + nMode = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nMode = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + case SID_TRANSLITERATE_HIRAGANA: + nMode = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nMode = TransliterationFlags::HIRAGANA_KATAKANA; + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + } + + if( nMode != TransliterationFlags::NONE ) + pOLV->TransliterateText( nMode ); +} + +void SwAnnotationShell::ExecRotateTransliteration( SfxRequest const & rReq ) +{ + if( rReq.GetSlot() == SID_TRANSLITERATE_ROTATE_CASE ) + { + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if (!pPostItMgr || !pPostItMgr->HasActiveSidebarWin()) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + if (!pOLV) + return; + + pOLV->TransliterateText(m_aRotateCase.getNextMode()); + } +} + +void SwAnnotationShell::ExecUndo(SfxRequest &rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + SfxUndoManager* pUndoManager = GetUndoManager(); + SwWrtShell &rSh = rView.GetWrtShell(); + SwUndoId nUndoId(SwUndoId::EMPTY); + + long aOldHeight = rView.GetPostItMgr()->HasActiveSidebarWin() + ? rView.GetPostItMgr()->GetActiveSidebarWin()->GetPostItTextHeight() + : 0; + + sal_uInt16 nId = rReq.GetSlot(); + sal_uInt16 nCnt = 1; + const SfxPoolItem* pItem=nullptr; + if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem ) ) + nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + switch( nId ) + { + case SID_UNDO: + { + rSh.GetLastUndoInfo(nullptr, &nUndoId); + if (nUndoId == SwUndoId::CONFLICT) + { + rReq.SetReturnValue( SfxUInt32Item(nId, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + break; + } + + if ( pUndoManager ) + { + sal_uInt16 nCount = pUndoManager->GetUndoActionCount(); + sal_uInt16 nSteps = nCnt; + if ( nCount < nCnt ) + { + nCnt = nCnt - nCount; + nSteps = nCount; + } + else + nCnt = 0; + + while( nSteps-- ) + pUndoManager->Undo(); + } + + if ( nCnt ) + rSh.Do( SwWrtShell::UNDO, nCnt ); + + break; + } + + case SID_REDO: + { + (void)rSh.GetFirstRedoInfo(nullptr, &nUndoId); + if (nUndoId == SwUndoId::CONFLICT) + { + rReq.SetReturnValue( SfxUInt32Item(nId, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + break; + } + + if ( pUndoManager ) + { + sal_uInt16 nCount = pUndoManager->GetRedoActionCount(); + sal_uInt16 nSteps = nCnt; + if ( nCount < nCnt ) + { + nCnt = nCnt - nCount; + nSteps = nCount; + } + else + nCnt = 0; + + while( nSteps-- ) + pUndoManager->Redo(); + } + + if ( nCnt ) + rSh.Do( SwWrtShell::REDO, nCnt ); + + break; + } + } + + rView.GetViewFrame()->GetBindings().InvalidateAll(false); + + if (rView.GetPostItMgr()->HasActiveSidebarWin()) + rView.GetPostItMgr()->GetActiveSidebarWin()->ResizeIfNecessary(aOldHeight,rView.GetPostItMgr()->GetActiveSidebarWin()->GetPostItTextHeight()); +} + +void SwAnnotationShell::StateUndo(SfxItemSet &rSet) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + SfxWhichIter aIter(rSet); + SwUndoId nUndoId(SwUndoId::EMPTY); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxUndoManager* pUndoManager = GetUndoManager(); + SfxViewFrame *pSfxViewFrame = rView.GetViewFrame(); + SwWrtShell &rSh = rView.GetWrtShell(); + + while( nWhich ) + { + switch ( nWhich ) + { + case SID_UNDO: + { + sal_uInt16 nCount = pUndoManager ? pUndoManager->GetUndoActionCount() : 0; + if ( nCount ) + pSfxViewFrame->GetSlotState( nWhich, pSfxViewFrame->GetInterface(), &rSet ); + else if (rSh.GetLastUndoInfo(nullptr, &nUndoId)) + { + rSet.Put( SfxStringItem( nWhich, rSh.GetDoString(SwWrtShell::UNDO)) ); + } + else if (nUndoId == SwUndoId::CONFLICT) + { + rSet.Put( SfxUInt32Item(nWhich, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + } + else + rSet.DisableItem(nWhich); + break; + } + case SID_REDO: + { + sal_uInt16 nCount = pUndoManager ? pUndoManager->GetRedoActionCount() : 0; + if ( nCount ) + pSfxViewFrame->GetSlotState( nWhich, pSfxViewFrame->GetInterface(), &rSet ); + else if (rSh.GetFirstRedoInfo(nullptr, &nUndoId)) + { + rSet.Put(SfxStringItem( nWhich, rSh.GetDoString(SwWrtShell::REDO)) ); + } + else if (nUndoId == SwUndoId::CONFLICT) + { + rSet.Put( SfxUInt32Item(nWhich, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + } + else + rSet.DisableItem(nWhich); + break; + } + case SID_GETUNDOSTRINGS: + case SID_GETREDOSTRINGS: + { + if( pUndoManager ) + { + OUString (SfxUndoManager:: *fnGetComment)( size_t, bool const ) const; + + sal_uInt16 nCount; + if( SID_GETUNDOSTRINGS == nWhich ) + { + nCount = pUndoManager->GetUndoActionCount(); + fnGetComment = &SfxUndoManager::GetUndoActionComment; + } + else + { + nCount = pUndoManager->GetRedoActionCount(); + fnGetComment = &SfxUndoManager::GetRedoActionComment; + } + + OUStringBuffer sList; + if( nCount ) + { + for( sal_uInt16 n = 0; n < nCount; ++n ) + sList.append( (pUndoManager->*fnGetComment)( n, SfxUndoManager::TopLevel ) ).append("\n"); + } + + SfxStringListItem aItem( nWhich ); + if ((nWhich == SID_GETUNDOSTRINGS) && + rSh.GetLastUndoInfo(nullptr, nullptr)) + { + rSh.GetDoStrings( SwWrtShell::UNDO, aItem ); + } + else if ((nWhich == SID_GETREDOSTRINGS) && + (rSh.GetFirstRedoInfo(nullptr, nullptr))) + { + rSh.GetDoStrings( SwWrtShell::REDO, aItem ); + } + + sList.append(aItem.GetString()); + aItem.SetString( sList.makeStringAndClear() ); + rSet.Put( aItem ); + } + else + rSet.DisableItem( nWhich ); + } + break; + + default: + { + pSfxViewFrame->GetSlotState( nWhich, pSfxViewFrame->GetInterface(), &rSet ); + break; + } + + } + + if (pPostItMgr->GetActiveSidebarWin()->GetLayoutStatus()==SwPostItHelper::DELETED) + rSet.DisableItem( nWhich ); + + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::StateDisableItems( SfxItemSet &rSet ) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } +} + +void SwAnnotationShell::InsertSymbol(SfxRequest& rReq) +{ + SwPostItMgr* pPostItMgr = rView.GetPostItMgr(); + if ( !pPostItMgr || !pPostItMgr->HasActiveSidebarWin() ) + return; + + OutlinerView* pOLV = pPostItMgr->GetActiveSidebarWin()->GetOutlinerView(); + + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if( pArgs ) + pArgs->GetItemState(GetPool().GetWhich(SID_CHARMAP), false, &pItem); + + OUString sSym; + OUString sFontName; + if ( pItem ) + { + sSym = static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SfxPoolItem* pFtItem = nullptr; + pArgs->GetItemState( GetPool().GetWhich(SID_ATTR_SPECIALCHAR), false, &pFtItem); + + if (const SfxStringItem* pFontItem = dynamic_cast<const SfxStringItem*>(pFtItem)) + sFontName = pFontItem->GetValue(); + } + + SfxItemSet aSet(pOLV->GetAttribs()); + SvtScriptType nScript = pOLV->GetSelectedScriptType(); + std::shared_ptr<SvxFontItem> aSetDlgFont(std::make_shared<SvxFontItem>(RES_CHRATR_FONT)); + { + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, *aSet.GetPool() ); + aSetItem.GetItemSet().Put( aSet, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript ); + if( pI ) + { + aSetDlgFont.reset(static_cast<SvxFontItem*>(pI->Clone())); + } + else + { + aSetDlgFont.reset(static_cast<SvxFontItem*>(aSet.Get( GetWhichOfScript( + SID_ATTR_CHAR_FONT, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ) )).Clone())); + } + + if (sFontName.isEmpty()) + sFontName = aSetDlgFont->GetFamilyName(); + } + + vcl::Font aFont(sFontName, Size(1,1)); + if( sSym.isEmpty() ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + SfxAllItemSet aAllSet( GetPool() ); + aAllSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + SwViewOption aOpt(*rView.GetWrtShell().GetViewOptions()); + const OUString& sSymbolFont = aOpt.GetSymbolFont(); + if( !sSymbolFont.isEmpty() ) + aAllSet.Put( SfxStringItem( SID_FONT_NAME, sSymbolFont ) ); + else + aAllSet.Put( SfxStringItem( SID_FONT_NAME, aSetDlgFont->GetFamilyName() ) ); + + // If character is selected then it can be shown. + auto xFrame = rView.GetViewFrame()->GetFrame().GetFrameInterface(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(rView.GetFrameWeld(), aAllSet, xFrame)); + pDlg->Execute(); + return; + } + + // do not flicker + pOLV->HideCursor(); + Outliner * pOutliner = pOLV->GetOutliner(); + pOutliner->SetUpdateMode(false); + + SfxItemSet aOldSet( pOLV->GetAttribs() ); + SfxItemSet aFontSet( + *aOldSet.GetPool(), + svl::Items< + EE_CHAR_FONTINFO, EE_CHAR_FONTINFO, + EE_CHAR_FONTINFO_CJK, EE_CHAR_FONTINFO_CTL>{}); + aFontSet.Set( aOldSet ); + + // Insert string + pOLV->InsertText( sSym); + + // Attributing (set font) + SfxItemSet aSetFont( *aFontSet.GetPool(), aFontSet.GetRanges() ); + SvxFontItem aFontItem (aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), + aFont.GetCharSet(), + EE_CHAR_FONTINFO ); + SvtScriptType nScriptBreak = g_pBreakIt->GetAllScriptsOfText( sSym ); + if( SvtScriptType::LATIN & nScriptBreak ) + aSetFont.Put( aFontItem ); + if( SvtScriptType::ASIAN & nScriptBreak ) + { + aFontItem.SetWhich(EE_CHAR_FONTINFO_CJK); + aSetFont.Put( aFontItem ); + } + if( SvtScriptType::COMPLEX & nScriptBreak ) + { + aFontItem.SetWhich(EE_CHAR_FONTINFO_CTL); + aSetFont.Put( aFontItem ); + } + pOLV->SetAttribs(aSetFont); + + // Erase selection + ESelection aSel(pOLV->GetSelection()); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOLV->SetSelection(aSel); + + // Restore old font + pOLV->SetAttribs( aFontSet ); + + // From now on show it again + pOutliner->SetUpdateMode(true); + pOLV->ShowCursor(); + + rReq.AppendItem( SfxStringItem( GetPool().GetWhich(SID_CHARMAP), sSym ) ); + if(!aFont.GetFamilyName().isEmpty()) + rReq.AppendItem( SfxStringItem( SID_ATTR_SPECIALCHAR, aFont.GetFamilyName() ) ); + rReq.Done(); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx new file mode 100644 index 000000000..44d78d6e5 --- /dev/null +++ b/sw/source/uibase/shells/basesh.cxx @@ -0,0 +1,3039 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> + +#include <hintids.hxx> +#include <svl/languageoptions.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/htmlmode.hxx> +#include <svx/imapdlg.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svl/whiter.hxx> +#include <svl/visitem.hxx> +#include <editeng/langitem.hxx> +#include <svx/clipfmtitem.hxx> +#include <svx/contdlg.hxx> +#include <vcl/graph.hxx> +#include <vcl/inputctx.hxx> +#include <svl/slstitm.hxx> +#include <svl/ptitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/sizeitem.hxx> +#include <svx/flagsdef.hxx> +#include <editeng/scripttypeitem.hxx> +#include <sfx2/objface.hxx> +#include <fmturl.hxx> +#include <fmthdft.hxx> +#include <fmtclds.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <swundo.hxx> +#include <fldbas.hxx> +#include <uitool.hxx> +#include <basesh.hxx> +#include <viewopt.hxx> +#include <fontcfg.hxx> +#include <fmtsrnd.hxx> +#include <fldmgr.hxx> +#include <frmmgr.hxx> +#include <tablemgr.hxx> +#include <mdiexp.hxx> +#include <swdtflvr.hxx> +#include <pagedesc.hxx> +#include <fmtcol.hxx> +#include <edtwin.hxx> +#include <tblafmt.hxx> +#include <swwait.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <unotxdoc.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swabstdlg.hxx> +#include <modcfg.hxx> +#include <svx/fmshell.hxx> +#include <SwRewriter.hxx> +#include <svx/galleryitem.hxx> +#include <com/sun/star/gallery/GalleryItemType.hpp> +#include <memory> + +#include <svx/unobrushitemhelper.hxx> +#include <comphelper/scopeguard.hxx> +#include <comphelper/lok.hxx> +#include <officecfg/Office/Common.hxx> + +#include <svx/svxdlg.hxx> + +#include <shellres.hxx> +#include <UndoTable.hxx> + +FlyMode SwBaseShell::eFrameMode = FLY_DRAG_END; + +// These variables keep the state of Gallery (slot SID_GALLERY_BG_BRUSH) +// detected by GetGalleryState() for the subsequent ExecuteGallery() call. + +static sal_uInt8 nParagraphPos; +static sal_uInt8 nGraphicPos; +static sal_uInt8 nOlePos; +static sal_uInt8 nFramePos; +static sal_uInt8 nTablePos; +static sal_uInt8 nTableRowPos; +static sal_uInt8 nTableCellPos; +static sal_uInt8 nPagePos; +static sal_uInt8 nHeaderPos; +static sal_uInt8 nFooterPos; + +#define ShellClass_SwBaseShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +#include <AccessibilityCheck.hxx> +#include <svx/AccessibilityCheckDialog.hxx> + +namespace +{ + SvxContourDlg* GetContourDlg(SwView const &rView) + { + SfxChildWindow *pWnd = rView.GetViewFrame()->GetChildWindow(SvxContourDlgChildWindow::GetChildWindowId()); + return pWnd ? static_cast<SvxContourDlg*>(pWnd->GetController().get()) : nullptr; + } + + SvxIMapDlg* GetIMapDlg(SwView const &rView) + { + SfxChildWindow* pWnd = rView.GetViewFrame()->GetChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + return pWnd ? static_cast<SvxIMapDlg*>(pWnd->GetController().get()) : nullptr; + } +} + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; + +SFX_IMPL_SUPERCLASS_INTERFACE(SwBaseShell, SfxShell) + +void SwBaseShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxContourDlgChildWindow::GetChildWindowId()); +} + + +static void lcl_UpdateIMapDlg( SwWrtShell& rSh ) +{ + Graphic aGrf( rSh.GetIMapGraphic() ); + GraphicType nGrfType = aGrf.GetType(); + void* pEditObj = GraphicType::NONE != nGrfType && GraphicType::Default != nGrfType + ? rSh.GetIMapInventor() : nullptr; + std::unique_ptr<TargetList> pList(new TargetList); + SfxFrame::GetDefaultTargetList(*pList); + + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + const SwFormatURL &rURL = aSet.Get( RES_URL ); + SvxIMapDlgChildWindow::UpdateIMapDlg( + aGrf, rURL.GetMap(), pList.get(), pEditObj ); +} + +static bool lcl_UpdateContourDlg( SwWrtShell &rSh, SelectionType nSel ) +{ + Graphic aGraf( rSh.GetIMapGraphic() ); + GraphicType nGrfType = aGraf.GetType(); + bool bRet = GraphicType::NONE != nGrfType && GraphicType::Default != nGrfType; + if( bRet ) + { + OUString aGrfName; + if ( nSel & SelectionType::Graphic ) + rSh.GetGrfNms( &aGrfName, nullptr ); + + SvxContourDlg *pDlg = GetContourDlg(rSh.GetView()); + if (pDlg) + { + pDlg->Update(aGraf, !aGrfName.isEmpty(), + rSh.GetGraphicPolygon(), rSh.GetIMapInventor()); + } + } + return bRet; +} + +void SwBaseShell::ExecDelete(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + SwEditWin& rTmpEditWin = GetView().GetEditWin(); + switch(rReq.GetSlot()) + { + case SID_DELETE: + rSh.DelRight(); + break; + + case FN_BACKSPACE: + + if( rSh.IsNoNum() ) + { + rSh.SttCursorMove(); + bool bLeft = rSh.Left( CRSR_SKIP_CHARS, true, 1, false ); + if( bLeft ) + { + rSh.DelLeft(); + } + else + // JP 15.07.96: If it no longer goes forward, cancel + // the numbering. For example at the beginning + // of a doc, frame, table or an area. + rSh.DelNumRules(); + + rSh.EndCursorMove(); + break; + } + + [[fallthrough]]; // otherwise call DelLeft + case FN_SHIFT_BACKSPACE: + rSh.DelLeft(); + break; + default: + OSL_FAIL("wrong Dispatcher"); + return; + } + rReq.Done(); + + //#i42732# - notify the edit window that from now on we do not use the input language + rTmpEditWin.SetUseInputLanguage( false ); +} + +void SwBaseShell::ExecClpbrd(SfxRequest &rReq) +{ + // Attention: At risk of suicide! + // After paste, paste special the shell can be destroy. + + SwWrtShell &rSh = GetShell(); + sal_uInt16 nId = rReq.GetSlot(); + bool bIgnore = false; + PasteTableType ePasteTable = PasteTableType::PASTE_DEFAULT; + + switch( nId ) + { + case SID_CUT: + case SID_COPY: + rView.GetEditWin().FlushInBuffer(); + if ( rSh.HasSelection() ) + { + rtl::Reference<SwTransferable> pTransfer = new SwTransferable( rSh ); + + if ( nId == SID_CUT && FlyProtectFlags::NONE == rSh.IsSelObjProtected(FlyProtectFlags::Content|FlyProtectFlags::Parent) ) + pTransfer->Cut(); + else + { + const bool bLockedView = rSh.IsViewLocked(); + rSh.LockView( true ); //lock visible section + pTransfer->Copy(); + rSh.LockView( bLockedView ); + } + break; + } + return; + + case FN_PASTE_NESTED_TABLE: + case FN_TABLE_PASTE_ROW_BEFORE: + case FN_TABLE_PASTE_COL_BEFORE: + switch ( nId ) + { + case FN_PASTE_NESTED_TABLE: + ePasteTable = PasteTableType::PASTE_TABLE; + break; + case FN_TABLE_PASTE_ROW_BEFORE: + ePasteTable = PasteTableType::PASTE_ROW; + break; + case FN_TABLE_PASTE_COL_BEFORE: + ePasteTable = PasteTableType::PASTE_COLUMN; + break; + default: + ; + } + [[fallthrough]]; + case SID_PASTE: + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( &rSh.GetView().GetEditWin() ) ); + if( aDataHelper.GetXTransferable().is() + && SwTransferable::IsPaste( rSh, aDataHelper ) ) + { + // Temporary variables, because the shell could already be + // destroyed after the paste. + SwView* pView = &rView; + + RndStdIds nAnchorType = RndStdIds::FLY_AT_PARA; + const SfxUInt16Item* pAnchorType = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1); + if (pAnchorType) + nAnchorType = static_cast<RndStdIds>(pAnchorType->GetValue()); + bool bIgnoreComments = false; + const SfxBoolItem* pIgnoreComments = rReq.GetArg<SfxBoolItem>(FN_PARAM_2); + if (pIgnoreComments) + bIgnoreComments = pIgnoreComments->GetValue(); + SwTransferable::Paste(rSh, aDataHelper, nAnchorType, bIgnoreComments, ePasteTable); + + if( rSh.IsFrameSelected() || rSh.IsObjSelected() ) + rSh.EnterSelFrameMode(); + pView->AttrChangedNotify(nullptr); + } + else + return; + } + break; + + case SID_CLIPBOARD_FORMAT_ITEMS: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pFormat; + if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pFormat ) ) + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + &rSh.GetView().GetEditWin()) ); + if( aDataHelper.GetXTransferable().is() + /*&& SwTransferable::IsPaste( rSh, aDataHelper )*/ ) + { + // Temporary variables, because the shell could already be + // destroyed after the paste. + SwView* pView = &rView; + + SwTransferable::PasteFormat( rSh, aDataHelper, + static_cast<SotClipboardFormatId>(static_cast<const SfxUInt32Item*>(pFormat)->GetValue()) ); + + //Done() has to be called before the shell has been removed + rReq.Done(); + bIgnore = true; + if( rSh.IsFrameSelected() || rSh.IsObjSelected()) + rSh.EnterSelFrameMode(); + pView->AttrChangedNotify(nullptr); + } + } + } + break; + + case SID_PASTE_UNFORMATTED: + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( &rSh.GetView().GetEditWin()) ); + if( aDataHelper.GetXTransferable().is() + && SwTransferable::IsPaste( rSh, aDataHelper ) ) + { + // Temporary variables, because the shell could already be + // destroyed after the paste. + SwView* pView = &rView; + rReq.Ignore(); + bIgnore = true; + if(SwTransferable::PasteUnformatted( rSh, aDataHelper )) + { + SfxViewFrame* pViewFrame = pView->GetViewFrame(); + uno::Reference< frame::XDispatchRecorder > xRecorder = + pViewFrame->GetBindings().GetRecorder(); + if(xRecorder.is()) { + SfxRequest aReq( pViewFrame, SID_CLIPBOARD_FORMAT_ITEMS ); + aReq.AppendItem( SfxUInt32Item( SID_CLIPBOARD_FORMAT_ITEMS, static_cast<sal_uInt32>(SotClipboardFormatId::STRING) ) ); + aReq.Done(); + } + } + + if (rSh.IsFrameSelected() || rSh.IsObjSelected()) + rSh.EnterSelFrameMode(); + pView->AttrChangedNotify(nullptr); + } + else + return; + } + break; + + case SID_PASTE_SPECIAL: + { + std::shared_ptr<TransferableDataHelper> aDataHelper = + std::make_shared<TransferableDataHelper>(TransferableDataHelper::CreateFromSystemClipboard( &rSh.GetView().GetEditWin())); + + if( aDataHelper->GetXTransferable().is() + && SwTransferable::IsPaste( rSh, *aDataHelper ) + && !rSh.CursorInsideInputField() ) + { + rReq.Ignore(); + bIgnore = true; + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog( rReq.GetFrameWeld() )); + + // Prepare the dialog + SwTransferable::PrePasteSpecial(rSh, *aDataHelper, pDlg); + pDlg->PreGetFormat(*aDataHelper); + + + pDlg->StartExecuteAsync([aDataHelper, pDlg, &rSh, this](sal_Int32 nResult){ + if (nResult == RET_OK) + { + // Temporary variables, because the shell could already be + // destroyed after the paste. + SwView* pView = &rView; + bool bRet = false; + SotClipboardFormatId nFormatId = pDlg->GetFormatOnly(); + + if( nFormatId != SotClipboardFormatId::NONE ) + bRet = SwTransferable::PasteFormat( rSh, *aDataHelper, nFormatId ); + + if (bRet) + { + SfxViewFrame* pViewFrame = pView->GetViewFrame(); + uno::Reference< frame::XDispatchRecorder > xRecorder = + pViewFrame->GetBindings().GetRecorder(); + if(xRecorder.is()) { + SfxRequest aReq( pViewFrame, SID_CLIPBOARD_FORMAT_ITEMS ); + aReq.AppendItem( SfxUInt32Item( SID_CLIPBOARD_FORMAT_ITEMS, static_cast<sal_uInt32>(nFormatId) ) ); + aReq.Done(); + } + } + + if (rSh.IsFrameSelected() || rSh.IsObjSelected()) + rSh.EnterSelFrameMode(); + pView->AttrChangedNotify(nullptr); + } + + pDlg->disposeOnce(); + + }); + } + else + return; + } + break; + + default: + OSL_FAIL("wrong Dispatcher"); + return; + } + if(!bIgnore) + rReq.Done(); +} + +// ClipBoard state + +void SwBaseShell::StateClpbrd(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter(rSet); + + const bool bCopy = rSh.HasSelection(); + + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_CUT: + if( FlyProtectFlags::NONE != rSh.IsSelObjProtected(FlyProtectFlags::Content|FlyProtectFlags::Parent ) ) + { + rSet.DisableItem( nWhich ); + break; + } + [[fallthrough]]; + case SID_COPY: + if( !bCopy || GetObjectShell()->isContentExtractionLocked()) + rSet.DisableItem( nWhich ); + break; + + case FN_PASTE_NESTED_TABLE: + case FN_TABLE_PASTE_ROW_BEFORE: + case FN_TABLE_PASTE_COL_BEFORE: + if( !rSh.IsCursorInTable() + || !GetView().IsPasteSpecialAllowed() + || rSh.CursorInsideInputField() + // disable if not a native Writer table and not a spreadsheet format + || !GetView().IsPasteSpreadsheet(rSh.GetTableCopied()) ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_PASTE: + if( !GetView().IsPasteAllowed() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_PASTE_SPECIAL: + if( !GetView().IsPasteSpecialAllowed() + || rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_PASTE_UNFORMATTED: + if( !GetView().IsPasteSpecialAllowed() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_CLIPBOARD_FORMAT_ITEMS: + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + &rSh.GetView().GetEditWin()) ); + + SvxClipboardFormatItem aFormatItem( nWhich ); + SwTransferable::FillClipFormatItem( rSh, aDataHelper, aFormatItem ); + rSet.Put( aFormatItem ); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +// Perform undo + +void SwBaseShell::ExecUndo(SfxRequest &rReq) +{ + SwWrtShell &rWrtShell = GetShell(); + + SwUndoId nUndoId(SwUndoId::EMPTY); + sal_uInt16 nId = rReq.GetSlot(), nCnt = 1; + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem )) + nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + + // Repair mode: allow undo/redo of all undo actions, even if access would + // be limited based on the view shell ID. + bool bRepair = false; + if (pArgs && pArgs->GetItemState(SID_REPAIRPACKAGE, false, &pItem) == SfxItemState::SET) + bRepair = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + + // #i106349#: save pointer: undo/redo may delete the shell, i.e., this! + SfxViewFrame *const pViewFrame( GetView().GetViewFrame() ); + + IDocumentUndoRedo& rUndoRedo = rWrtShell.GetIDocumentUndoRedo(); + bool bWasRepair = rUndoRedo.DoesRepair(); + rUndoRedo.DoRepair(bRepair); + comphelper::ScopeGuard aGuard([&rUndoRedo, bWasRepair]() + { + rUndoRedo.DoRepair(bWasRepair); + }); + + switch( nId ) + { + case SID_UNDO: + if (rUndoRedo.GetLastUndoInfo(nullptr, &nUndoId, &rWrtShell.GetView())) + { + for (SwViewShell& rShell : rWrtShell.GetRingContainer()) + rShell.LockPaint(); + rWrtShell.Do( SwWrtShell::UNDO, nCnt ); + for (SwViewShell& rShell : rWrtShell.GetRingContainer()) + rShell.UnlockPaint(); + } + break; + + case SID_REDO: + if (rUndoRedo.GetFirstRedoInfo(nullptr, &nUndoId, &rWrtShell.GetView())) + { + for (SwViewShell& rShell : rWrtShell.GetRingContainer()) + rShell.LockPaint(); + rWrtShell.Do( SwWrtShell::REDO, nCnt ); + for (SwViewShell& rShell : rWrtShell.GetRingContainer()) + rShell.UnlockPaint(); + } + break; + + case SID_REPEAT: + rWrtShell.Do( SwWrtShell::REPEAT ); + break; + default: + OSL_FAIL("wrong Dispatcher"); + } + + if (nUndoId == SwUndoId::CONFLICT) + { + rReq.SetReturnValue( SfxUInt32Item(nId, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + } + else if (nUndoId == SwUndoId::INSFMTATTR) + { + rWrtShell.GetDoc()->GetDocShell()->GetStyleSheetPool()->Broadcast(SfxHint(SfxHintId::StyleSheetModified)); + } + + if (pViewFrame) { pViewFrame->GetBindings().InvalidateAll(false); } +} + +// State of undo + +void SwBaseShell::StateUndo(SfxItemSet &rSet) +{ + SwUndoId nUndoId(SwUndoId::EMPTY); + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while(nWhich) + { + switch(nWhich) + { + case SID_UNDO: + { + if (rSh.GetLastUndoInfo(nullptr, &nUndoId, &rSh.GetView())) + { + rSet.Put( SfxStringItem(nWhich, + rSh.GetDoString(SwWrtShell::UNDO))); + } + else if (nUndoId == SwUndoId::CONFLICT) + { + rSet.Put( SfxUInt32Item(nWhich, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + } + else + rSet.DisableItem(nWhich); + + break; + } + case SID_REDO: + { + if (rSh.GetFirstRedoInfo(nullptr, &nUndoId, &rSh.GetView())) + { + rSet.Put(SfxStringItem(nWhich, + rSh.GetDoString(SwWrtShell::REDO))); + } + else if (nUndoId == SwUndoId::CONFLICT) + { + rSet.Put( SfxInt32Item(nWhich, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)) ); + } + else + rSet.DisableItem(nWhich); + break; + } + case SID_REPEAT: + { // Repeat is only possible if no REDO is possible - UI-Restriction + if ((!rSh.GetFirstRedoInfo(nullptr, nullptr)) && + !rSh.IsSelFrameMode() && + (SwUndoId::EMPTY != rSh.GetRepeatInfo(nullptr))) + { + rSet.Put(SfxStringItem(nWhich, rSh.GetRepeatString())); + } + else + rSet.DisableItem(nWhich); + break; + } + + case SID_GETUNDOSTRINGS: + if (rSh.GetLastUndoInfo(nullptr, nullptr)) + { + SfxStringListItem aStrLst( nWhich ); + rSh.GetDoStrings( SwWrtShell::UNDO, aStrLst ); + rSet.Put( aStrLst ); + } + else + rSet.DisableItem( nWhich ); + break; + + case SID_GETREDOSTRINGS: + if (rSh.GetFirstRedoInfo(nullptr, nullptr)) + { + SfxStringListItem aStrLst( nWhich ); + rSh.GetDoStrings( SwWrtShell::REDO, aStrLst ); + rSet.Put( aStrLst ); + } + else + rSet.DisableItem( nWhich ); + break; + } + nWhich = aIter.NextWhich(); + } +} + +// Evaluate respectively dispatching the slot Id + +void SwBaseShell::Execute(SfxRequest &rReq) +{ + const SfxPoolItem *pItem; + SwWrtShell &rSh = GetShell(); + const SfxItemSet* pArgs = rReq.GetArgs(); + bool bMore = false; + + sal_uInt16 nSlot = rReq.GetSlot(); + switch(nSlot) + { + case FN_REPAGINATE: + { + Reference < XModel > xModel = GetView().GetDocShell()->GetModel(); + auto pDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(xModel); + pDoc->NotifyRefreshListeners(); + rSh.CalcLayout(); + } + break; + case FN_UPDATE_FIELDS: + { + rSh.UpdateDocStat(); + rSh.EndAllTableBoxEdit(); + rSh.SwViewShell::UpdateFields(true); + + if( rSh.IsCursorInTable() ) + { + if( !rSh.IsTableComplexForChart() ) + SwTableFUNC( &rSh ).UpdateChart(); + rSh.ClearTableBoxContent(); + rSh.SaveTableBoxContent(); + } + } + break; + case FN_UPDATE_CHARTS: + { + SwWait aWait( *rView.GetDocShell(), true ); + rSh.UpdateAllCharts(); + } + break; + + case FN_UPDATE_ALL: + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer + = GetObjectShell()->getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + + SwView& rTempView = GetView(); + rSh.EnterStdMode(); + if( !rSh.GetLinkManager().GetLinks().empty() ) + { + rSh.StartAllAction(); + rSh.GetLinkManager().UpdateAllLinks( false, true, nullptr ); + rSh.EndAllAction(); + } + SfxDispatcher &rDis = *rTempView.GetViewFrame()->GetDispatcher(); + rDis.Execute( FN_UPDATE_FIELDS ); + rDis.Execute( FN_UPDATE_TOX ); + rDis.Execute( FN_UPDATE_CHARTS ); + rSh.CalcLayout(); + } + break; + + case FN_UPDATE_INPUTFIELDS: + rSh.UpdateInputFields(); + break; + + case FN_PREV_BOOKMARK: + rReq.SetReturnValue(SfxBoolItem( nSlot, rSh.GoPrevBookmark())); + break; + case FN_NEXT_BOOKMARK: + rReq.SetReturnValue(SfxBoolItem( nSlot, rSh.GoNextBookmark())); + break; + + case FN_GOTO_NEXT_MARK: + case FN_GOTO_PREV_MARK: + { + SwFieldMgr aFieldMgr; + SwFieldType* pFieldType = aFieldMgr.GetFieldType(SwFieldIds::JumpEdit); + + if (pFieldType) + { + if (rSh.IsSelFrameMode()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + + if (rSh.HasMark()) + { + SwMvContext aMvContext(&rSh); + if (rSh.IsCursorPtAtEnd()) + rSh.SwapPam(); + rSh.ClearMark(); + rSh.EndSelect(); + } + bool bRet = rSh.MoveFieldType( pFieldType, nSlot == FN_GOTO_NEXT_MARK ); + SwField* pCurField = bRet ? rSh.GetCurField() : nullptr; + if (pCurField) + rSh.ClickToField(*pCurField); + rReq.SetReturnValue(SfxBoolItem( nSlot, bRet)); + } + } + break; + + case FN_START_DOC_DIRECT: + case FN_END_DOC_DIRECT: + { + if (rSh.IsSelFrameMode()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + rSh.EnterStdMode(); + nSlot == FN_START_DOC_DIRECT ? + rSh.SttEndDoc(true) : + rSh.SttEndDoc(false); + } + break; + case FN_GOTO_PREV_OBJ: + case FN_GOTO_NEXT_OBJ: + { + bool bSuccess = rSh.GotoObj( nSlot == FN_GOTO_NEXT_OBJ ); + rReq.SetReturnValue(SfxBoolItem(nSlot, bSuccess)); + if (bSuccess && !rSh.IsSelFrameMode()) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + GetView().AttrChangedNotify(nullptr); + } + } + break; + case SID_GALLERY_FORMATS: + { + const SvxGalleryItem* pGalleryItem = SfxItemSet::GetItem<SvxGalleryItem>(pArgs, SID_GALLERY_FORMATS, false); + if ( !pGalleryItem ) + break; + + const SelectionType nSelType = rSh.GetSelectionType(); + sal_Int8 nGalleryItemType( pGalleryItem->GetType() ); + + if ( (!rSh.IsSelFrameMode() || nSelType & SelectionType::Graphic) && + nGalleryItemType == css::gallery::GalleryItemType::GRAPHIC ) + { + SwWait aWait( *rView.GetDocShell(), true ); + + OUString aGrfName, aFltName; + const Graphic aGrf( pGalleryItem->GetGraphic() ); + + if ( nSelType & SelectionType::Graphic ) + rSh.ReRead( aGrfName, aFltName, &aGrf ); + else + rSh.Insert( aGrfName, aFltName, aGrf ); + + GetView().GetEditWin().GrabFocus(); + } + else if(!rSh.IsSelFrameMode() && + nGalleryItemType == css::gallery::GalleryItemType::MEDIA ) + { + const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, pGalleryItem->GetURL() ); + GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + SID_INSERT_AVMEDIA, SfxCallMode::SYNCHRON, + { &aMediaURLItem }); + } + } + break; + case FN_PAGE_STYLE_SET_COLS: + { + if (pArgs) + { + // Determine the current PageDescriptor and fill the set with that. + const size_t nCurIdx = rSh.GetCurPageDesc(); + SwPageDesc aPageDesc(rSh.GetPageDesc(nCurIdx)); + + SwFrameFormat &rFormat = aPageDesc.GetMaster(); + + SwFormatCol aFormatCol = rFormat.GetCol(); + + sal_uInt16 nCount; + if(SfxItemState::SET == pArgs->GetItemState(nSlot)) + nCount = static_cast<const SfxUInt16Item &>(pArgs->Get(nSlot)).GetValue(); + else + nCount = pArgs->Get(SID_ATTR_COLUMNS).GetValue(); + sal_uInt16 nGutterWidth = DEF_GUTTER_WIDTH; + + aFormatCol.Init(nCount ? nCount : 1, nGutterWidth, USHRT_MAX); + aFormatCol.SetWishWidth(USHRT_MAX); + aFormatCol.SetGutterWidth(nGutterWidth, USHRT_MAX); + + rFormat.SetFormatAttr(aFormatCol); + + rSh.ChgPageDesc(nCurIdx, aPageDesc); + } + else + GetView().GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_PAGE_COLUMN_DLG); + } + break; + case FN_CONVERT_TABLE_TO_TEXT: + case FN_CONVERT_TEXT_TO_TABLE: + case FN_CONVERT_TEXT_TABLE: + { + sal_Unicode cDelim = 0; + bool bToTable = false; + if( nSlot == FN_CONVERT_TEXT_TO_TABLE || + ( nSlot == FN_CONVERT_TEXT_TABLE && nullptr == rSh.GetTableFormat() )) + bToTable = true; + SwInsertTableOptions aInsTableOpts( SwInsertTableFlags::All, 1 ); + SwTableAutoFormat const* pTAFormat = nullptr; + std::unique_ptr<SwTableAutoFormatTable> pAutoFormatTable; + bool bDeleteFormat = true; + if(pArgs && SfxItemState::SET == pArgs->GetItemState( FN_PARAM_1, true, &pItem)) + { + aInsTableOpts.mnInsMode = SwInsertTableFlags::NONE; + // Delimiter + OUString sDelim = static_cast< const SfxStringItem* >(pItem)->GetValue(); + if(!sDelim.isEmpty()) + cDelim = sDelim[0]; + // AutoFormat + if(SfxItemState::SET == pArgs->GetItemState( FN_PARAM_2, true, &pItem)) + { + OUString sAutoFormat = static_cast< const SfxStringItem* >(pItem)->GetValue(); + + pAutoFormatTable.reset(new SwTableAutoFormatTable); + pAutoFormatTable->Load(); + + for( sal_uInt16 i = 0, nCount = pAutoFormatTable->size(); i < nCount; i++ ) + { + SwTableAutoFormat const*const pFormat = &(*pAutoFormatTable)[ i ]; + if( pFormat->GetName() == sAutoFormat ) + { + pTAFormat = pFormat; + bDeleteFormat = false; + break; + } + } + } + //WithHeader + if(SfxItemState::SET == pArgs->GetItemState( FN_PARAM_3, true, &pItem) && + static_cast< const SfxBoolItem* >(pItem)->GetValue()) + aInsTableOpts.mnInsMode |= SwInsertTableFlags::Headline; + // RepeatHeaderLines + if(SfxItemState::SET == pArgs->GetItemState( FN_PARAM_4, true, &pItem)) + aInsTableOpts.mnRowsToRepeat = + static_cast<sal_uInt16>(static_cast< const SfxInt16Item* >(pItem)->GetValue()); + //WithBorder + if(SfxItemState::SET == pArgs->GetItemState( FN_PARAM_5, true, &pItem) && + static_cast< const SfxBoolItem* >(pItem)->GetValue()) + aInsTableOpts.mnInsMode |= SwInsertTableFlags::DefaultBorder; + //DontSplitTable + if(SfxItemState::SET == pArgs->GetItemState( FN_PARAM_6, true, &pItem) && + !static_cast< const SfxBoolItem* >(pItem)->GetValue() ) + aInsTableOpts.mnInsMode |= SwInsertTableFlags::SplitLayout; + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwConvertTableDlg> pDlg(pFact->CreateSwConvertTableDlg(GetView(), bToTable)); + if( RET_OK == pDlg->Execute() ) + { + pDlg->GetValues( cDelim, aInsTableOpts, pTAFormat ); + + } + } + + if( cDelim ) + { + //Shell change! + SwView& rSaveView = rView; + bool bInserted = false; + //recording: + SfxViewFrame* pViewFrame = GetView().GetViewFrame(); + if( SfxRequest::HasMacroRecorder(pViewFrame) ) + { + SfxRequest aReq( pViewFrame, nSlot); + aReq.AppendItem( SfxStringItem( FN_PARAM_1, OUString(cDelim) )); + if(bToTable) + { + if(pTAFormat) + aReq.AppendItem( SfxStringItem( FN_PARAM_2, pTAFormat->GetName())); + aReq.AppendItem( SfxBoolItem ( FN_PARAM_3, bool(aInsTableOpts.mnInsMode & SwInsertTableFlags::Headline))); + aReq.AppendItem( SfxInt16Item( FN_PARAM_4, static_cast<short>(aInsTableOpts.mnRowsToRepeat) )); + aReq.AppendItem( SfxBoolItem ( FN_PARAM_5, bool(aInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )); + aReq.AppendItem( SfxBoolItem ( FN_PARAM_6, !(aInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout))); + } + aReq.Done(); + } + + if( !bToTable ) + rSh.TableToText( cDelim ); + else + { + bInserted = rSh.TextToTable( aInsTableOpts, cDelim, pTAFormat ); + } + rSh.EnterStdMode(); + + if( bInserted ) + rSaveView.AutoCaption( TABLE_CAP ); + } + if(bDeleteFormat) + delete pTAFormat; + } + break; + case SID_STYLE_WATERCAN: + case SID_STYLE_UPDATE_BY_EXAMPLE: + case SID_STYLE_NEW_BY_EXAMPLE: + case SID_STYLE_APPLY: + { + ShellMode eMode = GetView().GetShellMode(); + if ( ShellMode::Draw != eMode && + ShellMode::DrawForm != eMode && + ShellMode::DrawText != eMode && + ShellMode::Bezier != eMode ) + { + // oj #107754# + if ( SID_STYLE_WATERCAN == nSlot ) + { + const bool bLockedView = rSh.IsViewLocked(); + rSh.LockView( true ); //lock visible section + + GetView().GetDocShell()->ExecStyleSheet(rReq); + + rSh.LockView( bLockedView ); + } + else + // Will be recorded from the DocShell + GetView().GetDocShell()->ExecStyleSheet(rReq); + } + } + break; + case SID_CLASSIFICATION_APPLY: + { + GetView().GetDocShell()->Execute(rReq); + } + break; + case SID_CLASSIFICATION_DIALOG: + { + GetView().GetDocShell()->Execute(rReq); + } + break; + case SID_PARAGRAPH_SIGN_CLASSIFY_DLG: + { + GetView().GetDocShell()->Execute(rReq); + } + break; + case SID_WATERMARK: + { + GetView().GetDocShell()->Execute(rReq); + } + break; + case FN_ESCAPE: + GetView().ExecuteSlot(rReq); + break; + case SID_IMAP: + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + pVFrame->ToggleChildWindow( nId ); + pVFrame->GetBindings().Invalidate( SID_IMAP ); + + if ( pVFrame->HasChildWindow( nId ) && rSh.IsFrameSelected() ) + lcl_UpdateIMapDlg( rSh ); + } + break; + case SID_IMAP_EXEC: + { + SvxIMapDlg* pDlg = GetIMapDlg(GetView()); + + // Check, if the allocation is useful or allowed at all. + if ( rSh.IsFrameSelected() && + pDlg->GetEditingObject() == rSh.GetIMapInventor() ) + { + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + aURL.SetMap( &pDlg->GetImageMap() ); + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + } + } + break; + case SID_CONTOUR_DLG: + { + sal_uInt16 nId = SvxContourDlgChildWindow::GetChildWindowId(); + + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + pVFrame->ToggleChildWindow( nId ); + pVFrame->GetBindings().Invalidate( SID_CONTOUR_DLG ); + + SelectionType nSel = rSh.GetSelectionType(); + if ( pVFrame->HasChildWindow( nId ) && + (nSel & (SelectionType::Graphic|SelectionType::Ole)) ) + { + lcl_UpdateContourDlg( rSh, nSel ); + } + } + break; + case SID_CONTOUR_EXEC: + { + SvxContourDlg *pDlg = GetContourDlg(GetView()); + // Check, if the allocation is useful or allowed at all. + SelectionType nSel = rSh.GetSelectionType(); + if ( nSel & (SelectionType::Graphic|SelectionType::Ole) ) + { + if (pDlg && pDlg->GetEditingObject() == rSh.GetIMapInventor()) + { + rSh.StartAction(); + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_SURROUND, RES_SURROUND>{}); + rSh.GetFlyFrameAttr( aSet ); + SwFormatSurround aSur( aSet.Get( RES_SURROUND ) ); + if ( !aSur.IsContour() ) + { + aSur.SetContour( true ); + if ( aSur.GetSurround() == css::text::WrapTextMode_NONE ) + aSur.SetSurround( css::text::WrapTextMode_PARALLEL ); + aSet.Put( aSur ); + rSh.SetFlyFrameAttr( aSet ); + } + const tools::PolyPolygon aPoly( pDlg->GetPolyPolygon() ); + rSh.SetGraphicPolygon( &aPoly ); + if ( pDlg->IsGraphicChanged() ) + rSh.ReRead( OUString(), OUString(), &pDlg->GetGraphic()); + rSh.EndAction(); + } + } + } + break; + case FN_FRAME_TO_ANCHOR: + { + rSh.GotoFlyAnchor(); + rSh.EnterStdMode(); + rSh.CallChgLnk(); + } + break; + case FN_TOOL_ANCHOR_PAGE: + case FN_TOOL_ANCHOR_PARAGRAPH: + case FN_TOOL_ANCHOR_CHAR: + case FN_TOOL_ANCHOR_AT_CHAR: + case FN_TOOL_ANCHOR_FRAME: + { + RndStdIds eSet = nSlot == FN_TOOL_ANCHOR_PAGE + ? RndStdIds::FLY_AT_PAGE + : nSlot == FN_TOOL_ANCHOR_PARAGRAPH + ? RndStdIds::FLY_AT_PARA + : nSlot == FN_TOOL_ANCHOR_FRAME + ? RndStdIds::FLY_AT_FLY + : nSlot == FN_TOOL_ANCHOR_CHAR + ? RndStdIds::FLY_AS_CHAR + : RndStdIds::FLY_AT_CHAR; + rSh.StartUndo(); + if (rSh.IsObjSelected()) + rSh.ChgAnchor(eSet); + else if (rSh.IsFrameSelected()) + { + SwFormatAnchor aAnc(eSet, rSh.GetPhyPageNum()); + SfxItemSet aSet(SwFEShell::makeItemSetFromFormatAnchor(GetPool(), aAnc)); + rSh.SetFlyFrameAttr(aSet); + } + // if new anchor is 'as char' and it is a Math object and the usual + // pre-conditions are met then align the formula to the baseline of the text + const uno::Reference < embed::XEmbeddedObject > xObj( rSh.GetOleRef() ); + const bool bDoMathBaselineAlignment = xObj.is() && SotExchange::IsMath( xObj->getClassID() ) + && RndStdIds::FLY_AS_CHAR == eSet && rSh.GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ); + if (bDoMathBaselineAlignment) + rSh.AlignFormulaToBaseline( xObj ); + + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + if( nHtmlMode ) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_SURROUND, RES_HORI_ORIENT>{}); + rSh.GetFlyFrameAttr(aSet); + + const SwFormatSurround& rSurround = aSet.Get(RES_SURROUND); + const SwFormatVertOrient& rVert = aSet.Get(RES_VERT_ORIENT); + const SwFormatHoriOrient& rHori = aSet.Get(RES_HORI_ORIENT); + sal_Int16 eVOrient = rVert.GetVertOrient(); + sal_Int16 eHOrient = rHori.GetHoriOrient(); + css::text::WrapTextMode eSurround = rSurround.GetSurround(); + + switch( eSet ) + { + case RndStdIds::FLY_AT_FLY: + case RndStdIds::FLY_AT_PAGE: + //Wrap through, left or from left, top, from top + if(eSurround != css::text::WrapTextMode_THROUGH) + aSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH)); + + if( eVOrient != text::VertOrientation::TOP && eVOrient != text::VertOrientation::NONE) + aSet.Put(SwFormatVertOrient(0, text::VertOrientation::TOP)); + + if (eHOrient != text::HoriOrientation::NONE && eHOrient != text::HoriOrientation::LEFT) + aSet.Put(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); + break; + + case RndStdIds::FLY_AT_PARA: + // left, from left, right, top, no wrap, wrap left and right + if (eSurround != css::text::WrapTextMode_LEFT && eSurround != css::text::WrapTextMode_RIGHT) + aSet.Put(SwFormatSurround(css::text::WrapTextMode_LEFT)); + + if( eVOrient != text::VertOrientation::TOP) + aSet.Put(SwFormatVertOrient(0, text::VertOrientation::TOP)); + + if (eHOrient != text::HoriOrientation::NONE && eHOrient != text::HoriOrientation::LEFT && eHOrient != text::HoriOrientation::RIGHT) + aSet.Put(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); + break; + + case RndStdIds::FLY_AT_CHAR: + // left, from left, right, top, wrap through + if(eSurround != css::text::WrapTextMode_THROUGH) + aSet.Put(SwFormatSurround(css::text::WrapTextMode_THROUGH)); + + if( eVOrient != text::VertOrientation::TOP) + aSet.Put(SwFormatVertOrient(0, text::VertOrientation::TOP)); + + if (eHOrient != text::HoriOrientation::NONE && eHOrient != text::HoriOrientation::LEFT && eHOrient != text::HoriOrientation::RIGHT) + aSet.Put(SwFormatHoriOrient(0, text::HoriOrientation::LEFT)); + break; + + default: + ; + } + + if( aSet.Count() ) + rSh.SetFlyFrameAttr( aSet ); + } + rSh.EndUndo(); + + GetView().GetViewFrame()->GetBindings().Invalidate( SID_ANCHOR_MENU ); + } + break; + + case FN_FRAME_NOWRAP: + case FN_FRAME_WRAP: + case FN_FRAME_WRAP_IDEAL: + case FN_FRAME_WRAPTHRU: + case FN_FRAME_WRAPTHRU_TRANSP: + case FN_FRAME_WRAPTHRU_TOGGLE: + case FN_FRAME_WRAP_CONTOUR: + case FN_WRAP_ANCHOR_ONLY: + case FN_FRAME_WRAP_LEFT: + case FN_FRAME_WRAP_RIGHT: + SetWrapMode( nSlot ); + break; + + case FN_UPDATE_ALL_LINKS: + { + if( !rSh.GetLinkManager().GetLinks().empty() ) + { + rSh.EnterStdMode(); + rSh.StartAllAction(); + rSh.GetLinkManager().UpdateAllLinks( false, false, nullptr ); + rSh.EndAllAction(); + } + } + break; + + case FN_XFORMS_DESIGN_MODE: + if (pArgs && pArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET) + { + if (const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>(pItem)) + { + bool bDesignMode = pBoolItem->GetValue(); + + // set from design mode + OSL_ENSURE( GetView().GetFormShell() != nullptr, "form shell?" ); + SfxRequest aReq( GetView().GetViewFrame(), SID_FM_DESIGN_MODE ); + aReq.AppendItem( SfxBoolItem( SID_FM_DESIGN_MODE, bDesignMode ) ); + GetView().GetFormShell()->Execute( aReq ); + aReq.Done(); + + // also set suitable view options + SwViewOption aViewOption = *rSh.GetViewOptions(); + aViewOption.SetFormView( ! bDesignMode ); + rSh.ApplyViewOptions( aViewOption ); + } + } + break; + + default: + bMore = true; + } + + + if(!bMore || !pArgs) + return; + + pItem = nullptr; + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + if(pItem) + switch(nSlot) + { + case SID_ATTR_BRUSH: + case SID_ATTR_BORDER_SHADOW: + case RES_SHADOW: + { + rSh.StartAllAction(); + // Tabele cell(s) selected? + if ( rSh.IsTableMode() ) + { + SwFrameFormat *pFormat = rSh.GetTableFormat(); + pFormat->SetFormatAttr( *pItem ); + } + else if ( rSh.IsFrameSelected() ) + { + // Set border attributes via Frame-Manager. + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + aMgr.SetAttrSet( *pArgs ); + aMgr.UpdateFlyFrame(); + } + else + { + rSh.SetAttrSet( *pArgs ); + } + rSh.EndAllAction(); + } + break; + case FN_PAGE_STYLE_SET_LR_MARGIN: + case FN_PAGE_STYLE_SET_UL_MARGIN: + case FN_PAGE_STYLE_SET_NUMBER_FORMAT: + case FN_PAGE_STYLE_SET_PAPER_SIZE: + case FN_PAGE_STYLE_SET_PAPER_BIN: + { + OSL_FAIL("not implemented"); + } + break; + + case SID_ATTR_BORDER_OUTER: + { + // Tabele cell(s) selected? + if ( rSh.IsTableMode() ) + { + // Set border attributes Get/SetTabBorders() + rSh.SetTabBorders(*pArgs); + } + else if ( rSh.IsFrameSelected() ) + { + // Set border attributes via Frame-Manager. + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + aMgr.SetAttrSet(*pArgs); + aMgr.UpdateFlyFrame(); + } + else + { + // Set border attributes via shell quite normally. + rSh.SetAttrItem( *pItem ); + } + } + break; + default: + OSL_FAIL("wrong Dispatcher"); + } +} + +// Here the state for SID_IMAP / SID_CONTOUR will be handled +// until the swapping of the graphic is finished. + +IMPL_LINK_NOARG(SwBaseShell, GraphicArrivedHdl, SwCursorShell&, void) +{ + SwWrtShell &rSh = GetShell(); + if (CNT_GRF != rSh.SwEditShell::GetCntType()) + return; + GraphicType const nGrfType(rSh.GetGraphicType()); + if (GraphicType::NONE != nGrfType && + !aGrfUpdateSlots.empty() ) + { + bool bProtect = FlyProtectFlags::NONE != rSh.IsSelObjProtected(FlyProtectFlags::Content|FlyProtectFlags::Parent); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + for( const auto nSlot : aGrfUpdateSlots ) + { + bool bSetState = false; + bool bState = false; + switch( nSlot ) + { + case SID_IMAP: + case SID_IMAP_EXEC: + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + SfxChildWindow *pChildWindow = pVFrame->HasChildWindow(nId) ? + pVFrame->GetChildWindow(nId) : nullptr; + SvxIMapDlg *pDlg = pChildWindow ? + static_cast<SvxIMapDlg*>(pChildWindow->GetController().get()) : nullptr; + + if( pDlg && ( SID_IMAP_EXEC == nSlot || + ( SID_IMAP == nSlot && !bProtect)) && + pDlg->GetEditingObject() != rSh.GetIMapInventor()) + lcl_UpdateIMapDlg( rSh ); + + if( !bProtect && SID_IMAP == nSlot ) + { + bSetState = true; + bState = nullptr != pDlg; + } + } + break; + + case SID_CONTOUR_DLG: + if( !bProtect ) + { + sal_uInt16 nId = SvxContourDlgChildWindow::GetChildWindowId(); + SfxChildWindow *pChildWindow = pVFrame->HasChildWindow(nId) ? + pVFrame->GetChildWindow(nId) : nullptr; + SvxIMapDlg *pDlg = pChildWindow ? + static_cast<SvxIMapDlg*>(pChildWindow->GetController().get()) : nullptr; + if( pDlg && pDlg->GetEditingObject() != + rSh.GetIMapInventor() ) + lcl_UpdateContourDlg( rSh, SelectionType::Graphic ); + + bSetState = true; + bState = nullptr != pDlg; + } + break; + + case FN_FRAME_WRAP_CONTOUR: + if( !bProtect ) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_SURROUND, RES_SURROUND>{}); + rSh.GetFlyFrameAttr(aSet); + const SwFormatSurround& rWrap = aSet.Get(RES_SURROUND); + bSetState = true; + bState = rWrap.IsContour(); + } + break; + + case SID_GRFFILTER: + case SID_GRFFILTER_INVERT: + case SID_GRFFILTER_SMOOTH: + case SID_GRFFILTER_SHARPEN: + case SID_GRFFILTER_REMOVENOISE: + case SID_GRFFILTER_SOBEL: + case SID_GRFFILTER_MOSAIC: + case SID_GRFFILTER_EMBOSS: + case SID_GRFFILTER_POSTER: + case SID_GRFFILTER_POPART: + case SID_GRFFILTER_SEPIA: + case SID_GRFFILTER_SOLARIZE: + bSetState = bState = GraphicType::Bitmap == nGrfType; + break; + } + + if( bSetState ) + { + SfxBoolItem aBool( nSlot, bState ); + if( pGetStateSet ) + pGetStateSet->Put( aBool ); + else + pVFrame->GetBindings().SetState( aBool ); + } + } + aGrfUpdateSlots.clear(); + } +} + +void SwBaseShell::GetState( SfxItemSet &rSet ) +{ + SwWrtShell &rSh = GetShell(); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + pGetStateSet = &rSet; + while ( nWhich ) + { + switch ( nWhich ) + { + case SID_GALLERY_FORMATS: + if ( rSh.IsObjSelected() || + (rSh.IsSelFrameMode() && + !(rSh.GetSelectionType() & SelectionType::Graphic)) ) + rSet.DisableItem( nWhich ); + break; + case SID_GALLERY_ENABLE_ADDCOPY: + // #108230# allow copy from gallery in Writer AND Writer/Web! + rSet.Put( SfxBoolItem( SID_GALLERY_ENABLE_ADDCOPY, true ) ); + break; + case FN_EDIT_REGION: + if( !rSh.IsAnySectionInDoc() ) + rSet.DisableItem(nWhich); + break; + + case FN_EDIT_CURRENT_REGION: + //tdf#112808 if cursor is in an index, don't show the edit section. + if( !rSh.GetCurrSection() || + (rSh.GetCurrSection()->GetType() != SectionType::Content && + rSh.GetCurrSection()->GetType() != SectionType::FileLink )) + { + rSet.DisableItem(nWhich); + } + break; + + case FN_INSERT_REGION: + if( rSh.CursorInsideInputField() + || rSh.IsSelFrameMode() + || !rSh.IsInsRegionAvailable() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_CONVERT_TABLE_TO_TEXT: + { + FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,true); + if( (eFrameType & FrameTypeFlags::FOOTNOTE) || + !rSh.GetTableFormat() ) + rSet.DisableItem( nWhich ); + } + break; + case FN_CONVERT_TEXT_TO_TABLE: + { + FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,true); + if( (eFrameType & FrameTypeFlags::FOOTNOTE) || + !rSh.IsTextToTableAvailable() ) + rSet.DisableItem( nWhich ); + } + break; + case FN_CONVERT_TEXT_TABLE: + { + FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,true); + if( (eFrameType & FrameTypeFlags::FOOTNOTE) || + (!rSh.GetTableFormat() && !rSh.IsTextToTableAvailable() ) ) + rSet.DisableItem( nWhich ); + } + break; + case RES_SHADOW: + { + SfxItemSet aSet( rSh.GetAttrPool(), + svl::Items<RES_SHADOW, RES_SHADOW>{} ); + + // Table cell(s) selected? + if ( rSh.IsTableMode() ) + { + SwFrameFormat *pFormat = rSh.GetTableFormat(); + aSet.Put(pFormat->GetFormatAttr( nWhich )); + } + else if( rSh.IsFrameSelected() ) + { + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + aSet.Put( aMgr.GetAttrSet() ); + } + else + rSh.GetCurAttr( aSet ); + + const SvxShadowItem& rShItem = static_cast<const SvxShadowItem&>(aSet.Get(nWhich)); + rSet.Put(rShItem); + } + break; + case SID_IMAP: + { + // #i59688# + // Improve efficiency: + // If selected object is protected, item has to disabled. + const bool bProtect = FlyProtectFlags::NONE != rSh.IsSelObjProtected(FlyProtectFlags::Content|FlyProtectFlags::Parent); + if ( bProtect ) + { + rSet.DisableItem( nWhich ); + } + else + { + const sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + const bool bHas = pVFrame->HasChildWindow( nId ); + const bool bFrameSel = rSh.IsFrameSelected(); + const bool bIsGraphicSelection = + rSh.GetSelectionType() == SelectionType::Graphic; + + // #i59688# + // Avoid unnecessary loading of selected graphic. + // The graphic is only needed, if the dialog is open. + // If the swapping of the graphic is finished, the status + // must be determined asynchronously, until this the slot + // will be disabled. + if ( bHas && bIsGraphicSelection && rSh.IsLinkedGrfSwapOut() ) + { + if( AddGrfUpdateSlot( nWhich )) + rSh.GetGraphic(false); // start the loading + } + else + { + if ( !bHas && + ( !bFrameSel || + ( bIsGraphicSelection && + rSh.GetGraphicType() == GraphicType::NONE ) ) ) + { + rSet.DisableItem( nWhich ); + } + else + { + SfxBoolItem aBool(nWhich, bHas); + if ( bHas && bFrameSel ) + lcl_UpdateIMapDlg( rSh ); + rSet.Put(aBool); + } + } + } + } + break; + case SID_IMAP_EXEC: + { + bool bDisable = false; + if( !rSh.IsFrameSelected()) + bDisable = true; + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + if(!bDisable && pVFrame->HasChildWindow( nId )) + { + if(rSh.GetSelectionType() == SelectionType::Graphic + && rSh.IsLinkedGrfSwapOut()) + { + if( AddGrfUpdateSlot( nWhich )) + rSh.GetGraphic(false); // start the loading + } + else + { + SvxIMapDlg *pDlg = GetIMapDlg(GetView()); + if (pDlg && pDlg->GetEditingObject() != rSh.GetIMapInventor()) + lcl_UpdateIMapDlg( rSh ); + } + } + rSet.Put(SfxBoolItem(nWhich, bDisable)); + } + break; + + case FN_BACKSPACE: + case SID_DELETE: + if ( ( rSh.HasReadonlySel() && !rSh.CursorInsideInputField() ) + || rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_CONTOUR_DLG: + { + bool bParentCntProt = FlyProtectFlags::NONE != rSh.IsSelObjProtected(FlyProtectFlags::Content|FlyProtectFlags::Parent ); + + if( bParentCntProt || 0 != (HTMLMODE_ON & ::GetHtmlMode( + GetView().GetDocShell() )) ) + rSet.DisableItem( nWhich ); + else + { + sal_uInt16 nId = SvxContourDlgChildWindow::GetChildWindowId(); + bool bHas = GetView().GetViewFrame()->HasChildWindow( nId ); + SelectionType nSel = rSh.GetSelectionType(); + bool bOk(nSel & (SelectionType::Graphic|SelectionType::Ole)); + + bool bDisable = false; + if( !bHas && !bOk ) + bDisable = true; + // #i59688# + // Avoid unnecessary loading of selected graphic. + // The graphic is only needed, if the dialog is open. + // If the swapping of the graphic is finished, the status + // must be determined asynchronously, until this the slot + // will be disabled. + else if ( bHas && (nSel & SelectionType::Graphic) && + rSh.IsLinkedGrfSwapOut() ) + { + if( AddGrfUpdateSlot( nWhich )) + rSh.GetGraphic(false); // start the loading + // #i75481# + bDisable = true; + } + else if( bHas && bOk ) + bDisable = !lcl_UpdateContourDlg( rSh, nSel ); + else if( bOk ) + { + // #i75481# + // apply fix #i59688# only for selected graphics + if ( nSel & SelectionType::Graphic ) + bDisable = GraphicType::NONE == rSh.GetGraphicType(); + else + bDisable = GraphicType::NONE == rSh.GetIMapGraphic().GetType(); + } + + if( bDisable ) + rSet.DisableItem( nWhich ); + else + rSet.Put( SfxBoolItem(nWhich, bHas) ); + } + } + break; + case SID_CONTOUR_EXEC: + { + bool bDisable = false; + SelectionType nSel = rSh.GetSelectionType(); + if( !(nSel & (SelectionType::Graphic|SelectionType::Ole)) ) + bDisable = true; + sal_uInt16 nId = SvxContourDlgChildWindow::GetChildWindowId(); + if( !bDisable && GetView().GetViewFrame()->HasChildWindow( nId )) + { + SvxContourDlg *pDlg = GetContourDlg(GetView()); + if (pDlg && pDlg->GetEditingObject() != rSh.GetIMapInventor()) + bDisable = true; + } + rSet.Put(SfxBoolItem(nWhich, bDisable)); + } + break; + + case SID_ANCHOR_MENU: + case FN_TOOL_ANCHOR_PAGE: + case FN_TOOL_ANCHOR_PARAGRAPH: + case FN_TOOL_ANCHOR_CHAR: + case FN_TOOL_ANCHOR_AT_CHAR: + case FN_TOOL_ANCHOR_FRAME: + { + bool bObj = 0 != rSh.IsObjSelected(); + bool bParentCntProt = rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + if( !bParentCntProt && (bObj || rSh.IsFrameSelected())) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_ANCHOR, RES_ANCHOR>{}); + if(bObj) + rSh.GetObjAttr(aSet); + else + rSh.GetFlyFrameAttr(aSet); + RndStdIds eSet = aSet.Get(RES_ANCHOR).GetAnchorId(); + const bool bSet = + ((nWhich == FN_TOOL_ANCHOR_PAGE) && + (eSet == RndStdIds::FLY_AT_PAGE)) + || ((nWhich == FN_TOOL_ANCHOR_PARAGRAPH) && + (eSet == RndStdIds::FLY_AT_PARA)) + || ((nWhich == FN_TOOL_ANCHOR_FRAME) && + (eSet == RndStdIds::FLY_AT_FLY)) + || ((nWhich == FN_TOOL_ANCHOR_AT_CHAR) && + (eSet == RndStdIds::FLY_AT_CHAR)) + || ((nWhich == FN_TOOL_ANCHOR_CHAR) && + (eSet == RndStdIds::FLY_AS_CHAR)); + + if( nWhich == FN_TOOL_ANCHOR_FRAME && !rSh.IsFlyInFly() ) + rSet.DisableItem(nWhich); + else if(nWhich != SID_ANCHOR_MENU) + rSet.Put(SfxBoolItem(nWhich, bSet)); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (nWhich == FN_TOOL_ANCHOR_PAGE || nWhich == FN_TOOL_ANCHOR_PARAGRAPH + || nWhich == FN_TOOL_ANCHOR_FRAME) + { + rSet.DisableItem(nWhich); + } + } + } + else + rSet.DisableItem( nWhich ); + } + break; + case FN_FRAME_NOWRAP: + case FN_FRAME_WRAP: + case FN_FRAME_WRAP_IDEAL: + case FN_FRAME_WRAPTHRU: + case FN_FRAME_WRAPTHRU_TRANSP: + case FN_FRAME_WRAPTHRU_TOGGLE: + case FN_FRAME_WRAP_CONTOUR: + case FN_WRAP_ANCHOR_ONLY: + case FN_FRAME_WRAP_LEFT: + case FN_FRAME_WRAP_RIGHT: + { + bool bObj = 0 != rSh.IsObjSelected(); + bool bParentCntProt = rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + if( !bParentCntProt && (bObj || rSh.IsFrameSelected())) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_OPAQUE, RES_ANCHOR>{}); + RndStdIds nAnchorType; + if(bObj) + { + rSh.GetObjAttr(aSet); + nAnchorType = rSh.GetAnchorId(); + } + else + { + rSh.GetFlyFrameAttr(aSet); + nAnchorType = aSet.Get(RES_ANCHOR).GetAnchorId(); + } + const SwFormatSurround& rWrap = aSet.Get(RES_SURROUND); + + const SvxOpaqueItem& rOpaque = aSet.Get(RES_OPAQUE); + bool bOpaque = rOpaque.GetValue(); + css::text::WrapTextMode nSurround = rWrap.GetSurround(); + bool bSet = false; + + bool bDisable = + (nAnchorType == RndStdIds::UNKNOWN) || (nAnchorType == RndStdIds::FLY_AS_CHAR); + const bool bHtmlMode = + 0 != ::GetHtmlMode(GetView().GetDocShell()); + + switch( nWhich ) + { + case FN_FRAME_NOWRAP: + bDisable |= + ( (nAnchorType != RndStdIds::FLY_AT_PARA) + && (nAnchorType != RndStdIds::FLY_AT_CHAR) + && (nAnchorType != RndStdIds::FLY_AT_PAGE)); + bSet = nSurround == css::text::WrapTextMode_NONE; + break; + case FN_FRAME_WRAP: + bDisable |= bHtmlMode; + bSet = nSurround == css::text::WrapTextMode_PARALLEL; + break; + case FN_FRAME_WRAP_IDEAL: + bDisable |= bHtmlMode; + bSet = nSurround == css::text::WrapTextMode_DYNAMIC; + break; + case FN_FRAME_WRAPTHRU: + bDisable |= (bHtmlMode || + ( (nAnchorType != RndStdIds::FLY_AT_PARA) + && (nAnchorType != RndStdIds::FLY_AT_CHAR) + && (nAnchorType != RndStdIds::FLY_AT_PAGE))); + if(bObj) + bSet = nSurround == css::text::WrapTextMode_THROUGH && rSh.GetLayerId(); + else + bSet = nSurround == css::text::WrapTextMode_THROUGH && bOpaque; + break; + case FN_FRAME_WRAPTHRU_TRANSP: + case FN_FRAME_WRAPTHRU_TOGGLE: + bDisable |= bHtmlMode; + if(bObj) + bSet = nSurround == css::text::WrapTextMode_THROUGH && !rSh.GetLayerId(); + else + bSet = nSurround == css::text::WrapTextMode_THROUGH && !bOpaque; + break; + case FN_FRAME_WRAP_CONTOUR: + bDisable |= bHtmlMode; + //no contour available whenn no wrap or wrap through is set + bDisable |= (nSurround == css::text::WrapTextMode_NONE || nSurround == css::text::WrapTextMode_THROUGH); + if( !bDisable ) + { + SelectionType nSel = rSh.GetSelectionType(); + if( (nSel & SelectionType::Graphic) && + rSh.IsLinkedGrfSwapOut()) + { + if( AddGrfUpdateSlot( nWhich )) + rSh.GetGraphic(false); // start the loading + } + else if( rSh.IsFrameSelected() ) + { + // #i102253# applied patch from OD (see task) + bDisable = + nSel & SelectionType::Frame || + GraphicType::NONE == rSh.GetIMapGraphic().GetType(); + } + } + bSet = !bDisable && rWrap.IsContour(); + + break; + case FN_WRAP_ANCHOR_ONLY: + bDisable |= (bHtmlMode || + (nAnchorType != RndStdIds::FLY_AT_PARA)); + bSet = rWrap.IsAnchorOnly(); + break; + case FN_FRAME_WRAP_LEFT: + bSet = nSurround == css::text::WrapTextMode_LEFT; + break; + case FN_FRAME_WRAP_RIGHT: + bSet = nSurround == css::text::WrapTextMode_RIGHT; + break; + } + + if(bDisable) + rSet.DisableItem(nWhich); + else + rSet.Put(SfxBoolItem(nWhich, bSet)); + } + else + rSet.DisableItem(nWhich); + } + break; + case FN_UPDATE_CHARTS: + if( !rSh.HasCharts() ) + rSet.DisableItem( nWhich ); + break; + case FN_UPDATE_ALL_LINKS: + if ( rSh.GetLinkManager().GetLinks().empty() ) + rSet.DisableItem(nWhich); + break; + case FN_XFORMS_DESIGN_MODE: + // enable if in XForms document + if( rSh.GetDoc()->isXForms() ) + { + // determine current state from view options + bool bValue = ! rSh.GetViewOptions()->IsFormView(); + rSet.Put( SfxBoolItem( nWhich, bValue ) ); + } + else + rSet.Put( SfxVisibilityItem( nWhich, false ) ); + break; + case SID_ACCESSIBILITY_CHECK: + { + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + rSet.Put(SfxVisibilityItem(nWhich, false)); + } + break; + } + nWhich = aIter.NextWhich(); + } + pGetStateSet = nullptr; +} + +// Disable the slots with this status method + +void SwBaseShell::StateDisableItems( SfxItemSet &rSet ) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while (nWhich) + { + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } +} + +// Disable the slots with this status method + +void SwBaseShell::StateStyle( SfxItemSet &rSet ) +{ + bool bParentCntProt = GetShell().IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + ShellMode eMode = GetView().GetShellMode(); + + if ( bParentCntProt || + ShellMode::Draw == eMode || + ShellMode::DrawForm == eMode || + ShellMode::DrawText == eMode || + ShellMode::Bezier == eMode ) + { + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } + } + else + GetView().GetDocShell()->StateStyleSheet(rSet, &GetShell()); +} + +void SwBaseShell::SetWrapMode( sal_uInt16 nSlot ) +{ + SwWrtShell &rSh = GetShell(); + bool bObj = 0 != rSh.IsObjSelected(); + if( !bObj && !rSh.IsFrameSelected()) + return; + + SfxItemSet aSet(GetPool(), svl::Items<RES_OPAQUE, RES_SURROUND>{}); + if(bObj) + rSh.GetObjAttr(aSet); + else + rSh.GetFlyFrameAttr(aSet); + SwFormatSurround aWrap( aSet.Get(RES_SURROUND) ); + css::text::WrapTextMode nOldSurround(aWrap.GetSurround()); + css::text::WrapTextMode nSurround = css::text::WrapTextMode_PARALLEL; + + switch (nSlot) + { + case FN_FRAME_NOWRAP: + nSurround = css::text::WrapTextMode_NONE; + if (aWrap.IsContour()) + aWrap.SetContour(false); + break; + case FN_FRAME_WRAP_IDEAL: + nSurround = css::text::WrapTextMode_DYNAMIC; + break; + case FN_WRAP_ANCHOR_ONLY: + aWrap.SetAnchorOnly(!aWrap.IsAnchorOnly()); + + // keep previous wrapping + + // switch to wrap css::text::WrapTextMode_PARALLEL, if previous wrap is css::text::WrapTextMode_NONE + if ( nOldSurround != css::text::WrapTextMode_NONE ) + { + nSurround = nOldSurround; + } + break; + case FN_FRAME_WRAP_CONTOUR: + aWrap.SetContour(!aWrap.IsContour()); + break; + case FN_FRAME_WRAPTHRU_TRANSP: + case FN_FRAME_WRAPTHRU_TOGGLE: + if (aWrap.IsContour()) + aWrap.SetContour(false); + [[fallthrough]]; + case FN_FRAME_WRAPTHRU: + nSurround = css::text::WrapTextMode_THROUGH; + break; + + case FN_FRAME_WRAP_LEFT: + nSurround = css::text::WrapTextMode_LEFT; + break; + + case FN_FRAME_WRAP_RIGHT: + nSurround = css::text::WrapTextMode_RIGHT; + break; + + default: + break; + } + aWrap.SetSurround(nSurround); + + if (nSlot != FN_FRAME_WRAP_CONTOUR) + { + // Defaulting the contour wrap on draw objects. + if (bObj && nOldSurround != nSurround && + (nOldSurround == css::text::WrapTextMode_NONE || nOldSurround == css::text::WrapTextMode_THROUGH)) + { + aWrap.SetContour(true); + } + } + + aSet.Put( aWrap ); + + bool bOpaque = nSlot != FN_FRAME_WRAPTHRU_TRANSP && nSlot != FN_FRAME_WRAPTHRU_TOGGLE; + if( nSlot == FN_FRAME_WRAPTHRU_TOGGLE ) + { + if( bObj ) + bOpaque = !rSh.GetLayerId(); + else + { + const SvxOpaqueItem& aOpaque( aSet.Get(RES_OPAQUE) ); + bOpaque = !aOpaque.GetValue(); + } + } + aSet.Put(SvxOpaqueItem(RES_OPAQUE, bOpaque )); + + if(bObj) + { + rSh.SetObjAttr(aSet); + if ( bOpaque ) + rSh.SelectionToHeaven(); + else + rSh.SelectionToHell(); + } + else + rSh.SetFlyFrameAttr(aSet); + +} + +//Force update of the status line + +void SwBaseShell::SetFrameMode(FlyMode eMode, SwWrtShell *pSh ) +{ + eFrameMode = eMode; + SfxBindings &rBnd = pSh->GetView().GetViewFrame()->GetBindings(); + + if( eMode == FLY_DRAG || pSh->IsFrameSelected() || pSh->IsObjSelected() ) + { + const SfxPointItem aTmp1( SID_ATTR_POSITION, pSh->GetAnchorObjDiff()); + const SvxSizeItem aTmp2( SID_ATTR_SIZE, pSh->GetObjSize()); + rBnd.SetState( aTmp1 ); + rBnd.SetState( aTmp2 ); + } + else if( eMode == FLY_DRAG_END ) + { + static sal_uInt16 aInval[] = + { + SID_ATTR_POSITION, SID_ATTR_SIZE, 0 + }; + rBnd.Invalidate(aInval); + } +} + +SwBaseShell::SwBaseShell(SwView& rVw) : + SfxShell( &rVw ), + rView(rVw), + pGetStateSet(nullptr) +{ + SwWrtShell& rWrtSh = rView.GetWrtShell(); + + SetPool(&rWrtSh.GetAttrPool()); + SetName("Base"); + rWrtSh.SetGrfArrivedLnk( LINK( this, SwBaseShell, GraphicArrivedHdl)); +} + +SwBaseShell::~SwBaseShell() +{ + if( rView.GetCurShell() == this ) + rView.ResetSubShell(); + + Link<SwCursorShell&,void> aTmp( LINK( this, SwBaseShell, GraphicArrivedHdl)); + if( aTmp == rView.GetWrtShell().GetGrfArrivedLnk() ) + rView.GetWrtShell().SetGrfArrivedLnk( Link<SwCursorShell&,void>() ); +} + +void SwBaseShell::ExecTextCtrl( SfxRequest& rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + + if( pArgs) + { + SwWrtShell &rSh = GetShell(); + std::unique_ptr<SvxScriptSetItem> pSSetItem; + sal_uInt16 nSlot = rReq.GetSlot(); + SfxItemPool& rPool = rSh.GetAttrPool(); + sal_uInt16 nWhich = rPool.GetWhich( nSlot ); + SvtScriptType nScripts = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; + SfxItemSet aHeightSet( GetPool(), svl::Items<RES_CHRATR_FONTSIZE, RES_CHRATR_FONTSIZE, + RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CJK_FONTSIZE, + RES_CHRATR_CTL_FONTSIZE, RES_CHRATR_CTL_FONTSIZE>{}); + + switch( nSlot ) + { + case SID_ATTR_CHAR_FONT: + { + nScripts = rSh.GetScriptType(); + // #i42732# input language should be preferred over + // current cursor position to detect script type + if(!rSh.HasSelection()) + { + LanguageType nInputLang = GetView().GetEditWin().GetInputLanguage(); + if(nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) + nScripts = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); + } + [[fallthrough]]; + } + case SID_ATTR_CHAR_POSTURE: + case SID_ATTR_CHAR_WEIGHT: + { + pSSetItem.reset(new SvxScriptSetItem( nSlot, rPool )); + pSSetItem->PutItemForScriptType( nScripts, pArgs->Get( nWhich )); + pArgs = &pSSetItem->GetItemSet(); + } + break; + case SID_ATTR_CHAR_FONTHEIGHT: + { + if(rSh.HasSelection()) + { + pSSetItem.reset(new SvxScriptSetItem( nSlot, rPool )); + pSSetItem->PutItemForScriptType( nScripts, pArgs->Get( nWhich )); + pArgs = &pSSetItem->GetItemSet(); + } + else + { + nScripts = rSh.GetScriptType(); + LanguageType nInputLang = GetView().GetEditWin().GetInputLanguage(); + if(nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) + nScripts = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); + sal_uInt32 nHeight = static_cast< const SvxFontHeightItem& >(pArgs->Get( nWhich )).GetHeight(); + SwStdFontConfig* pStdFont = SW_MOD()->GetStdFontConfig(); + + SfxItemSet aLangSet( GetPool(), svl::Items<RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE>{}); + rSh.GetCurAttr( aLangSet ); + + sal_Int32 nWesternSize = + pStdFont->GetFontHeight(FONT_STANDARD, FONT_GROUP_DEFAULT, + aLangSet.Get( RES_CHRATR_LANGUAGE).GetLanguage()); + sal_Int32 nCJKSize = + pStdFont->GetFontHeight(FONT_STANDARD, FONT_GROUP_CJK, + aLangSet.Get( RES_CHRATR_CJK_LANGUAGE).GetLanguage()); + sal_Int32 nCTLSize = + pStdFont->GetFontHeight(FONT_STANDARD, FONT_GROUP_CTL, + aLangSet.Get( RES_CHRATR_CTL_LANGUAGE).GetLanguage()); + + switch(nScripts) + { + case SvtScriptType::LATIN: + nCJKSize = nHeight * nCJKSize / nWesternSize; + nCTLSize = nHeight * nCTLSize / nWesternSize; + nWesternSize = static_cast<sal_Int32>(nHeight); + break; + case SvtScriptType::ASIAN: + nCTLSize = nHeight* nCTLSize / nCJKSize; + nWesternSize = nHeight * nWesternSize / nCJKSize; + nCJKSize = static_cast<sal_Int32>(nHeight); + break; + case SvtScriptType::COMPLEX: + nCJKSize = nHeight * nCJKSize / nCTLSize; + nWesternSize = nHeight * nWesternSize / nCTLSize; + nCTLSize = static_cast<sal_Int32>(nHeight); + break; + default: break; + } + aHeightSet.Put( SvxFontHeightItem( static_cast<sal_uInt32>(nWesternSize), 100, RES_CHRATR_FONTSIZE )); + aHeightSet.Put( SvxFontHeightItem( static_cast<sal_uInt32>(nCJKSize), 100, RES_CHRATR_CJK_FONTSIZE )); + aHeightSet.Put( SvxFontHeightItem( static_cast<sal_uInt32>(nCTLSize), 100, RES_CHRATR_CTL_FONTSIZE )); + pArgs = &aHeightSet; + } + } + break; + } + + if( pArgs ) + { + bool bAuto = false; + if ( !isCHRATR(nWhich) || + ( rSh.HasSelection() && rSh.IsSelFullPara() ) ) + { + SwTextFormatColl * pColl = rSh.GetCurTextFormatColl(); + if ( pColl && pColl->IsAutoUpdateFormat() ) + { + rSh.AutoUpdatePara( pColl, *pArgs ); + bAuto = true; + } + } + + if (!bAuto) + { + rSh.SetAttrSet( *pArgs ); + } + } + } + else + GetView().GetViewFrame()->GetDispatcher()->Execute( SID_CHAR_DLG ); + rReq.Done(); +} + +void SwBaseShell::GetTextCtrlState( SfxItemSet& rSet ) +{ + SwWrtShell &rSh = GetShell(); + rSh.GetCurAttr( rSet ); +} + +void SwBaseShell::GetTextFontCtrlState( SfxItemSet& rSet ) +{ + SwWrtShell &rSh = GetShell(); + bool bFirst = true; + std::unique_ptr<SfxItemSet> pFntCoreSet; + SvtScriptType nScriptType = SvtScriptType::LATIN; + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + switch( nWhich ) + { + case RES_CHRATR_FONT: + case RES_CHRATR_FONTSIZE: + case RES_CHRATR_WEIGHT: + case RES_CHRATR_POSTURE: + { + if( !pFntCoreSet ) + { + pFntCoreSet.reset(new SfxItemSet( *rSet.GetPool(), + svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{} )); + rSh.GetCurAttr( *pFntCoreSet ); + nScriptType = rSh.GetScriptType(); + // #i42732# input language should be preferred over + // current cursor position to detect script type + SwEditWin& rEditWin = GetView().GetEditWin(); + if( rEditWin.IsUseInputLanguage() ) + { + if(!rSh.HasSelection() && ( + nWhich == RES_CHRATR_FONT || + nWhich == RES_CHRATR_FONTSIZE )) + { + LanguageType nInputLang = rEditWin.GetInputLanguage(); + if(nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) + nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); + } + } + } + SfxItemPool& rPool = *rSet.GetPool(); + SvxScriptSetItem aSetItem( rPool.GetSlotId( nWhich ), rPool ); + aSetItem.GetItemSet().Put( *pFntCoreSet, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScriptType ); + if( pI ) + { + rSet.Put( pI->CloneSetWhich(nWhich) ); + } + else + rSet.InvalidateItem( nWhich ); + // Set input context of the SwEditWin according to the selected font and script type + if(RES_CHRATR_FONT == nWhich) + { + vcl::Font aFont; + if (const SvxFontItem* pFontItem = dynamic_cast<const SvxFontItem*>(pI)) + { + aFont.SetFamilyName(pFontItem->GetFamilyName()); + aFont.SetStyleName(pFontItem->GetStyleName()); + aFont.SetFamily(pFontItem->GetFamily()); + aFont.SetPitch(pFontItem->GetPitch()); + aFont.SetCharSet(pFontItem->GetCharSet()); + } + + bool bVertical = rSh.IsInVerticalText(); + aFont.SetOrientation(bVertical ? 2700 : 0); + aFont.SetVertical(bVertical); + GetView().GetEditWin().SetInputContext( InputContext( aFont, InputContextFlags::Text | + InputContextFlags::ExtText ) ); + } + } + break; + + default: + if( bFirst ) + { + rSh.GetCurAttr( rSet ); + bFirst = false; + } + } + nWhich = aIter.NextWhich(); + } +} + +void SwBaseShell::GetBckColState(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + SelectionType nSelType(rSh.GetSelectionType()); + std::unique_ptr<SvxBrushItem> aBrushItem(std::make_unique<SvxBrushItem>(RES_BACKGROUND)); + + if( nWhich == SID_TABLE_CELL_BACKGROUND_COLOR ) + { + rSh.GetBoxBackground( aBrushItem ); + } + else + { + // Adapt to new DrawingLayer FillStyle; use a parent which has XFILL_NONE set + SfxItemSet aCoreSet(GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + aCoreSet.SetParent(&GetView().GetDocShell()->GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + if(nSelType & SelectionType::Graphic || SelectionType::Frame & nSelType) + { + rSh.GetFlyFrameAttr(aCoreSet); + } + else + { + rSh.GetCurAttr(aCoreSet); + } + + aBrushItem = getSvxBrushItemFromSourceSet(aCoreSet, RES_BACKGROUND); + } + + while(nWhich) + { + switch(nWhich) + { + case SID_BACKGROUND_COLOR: + case SID_TABLE_CELL_BACKGROUND_COLOR: + { + SvxColorItem aColorItem(aBrushItem->GetColor(), nWhich); + rSet.Put(aColorItem); + break; + } + case SID_ATTR_BRUSH: + case RES_BACKGROUND: + { + // if this was intended to have a independent copy of the Item to be set + // this is not needed due to the ItemSet/Pool cloning Items which get set anyways. + // Keeping code as reference - it may have had other reasons I do notz see (?!?) + // std::unique_ptr<SfxPoolItem> pNewItem(aBrushItem.CloneSetWhich(GetPool().GetWhich(nWhich))); + rSet.Put(*aBrushItem); + break; + } + } + + nWhich = aIter.NextWhich(); + } +} + +void SwBaseShell::ExecBckCol(SfxRequest& rReq) +{ + SwWrtShell &rSh = GetShell(); + SelectionType nSelType(rSh.GetSelectionType()); + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot(rReq.GetSlot()); + + if (!pArgs && nSlot != SID_BACKGROUND_COLOR && nSlot != SID_TABLE_CELL_BACKGROUND_COLOR) + { + return; + } + + std::unique_ptr<SvxBrushItem> aBrushItem(std::make_unique<SvxBrushItem>(RES_BACKGROUND)); + + if ( nSlot == SID_TABLE_CELL_BACKGROUND_COLOR ) + { + rSh.GetBoxBackground( aBrushItem ); + } + else + { + // Adapt to new DrawingLayer FillStyle; use a parent which has XFILL_NONE set + SfxItemSet aCoreSet(GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + aCoreSet.SetParent(&GetView().GetDocShell()->GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + if((SelectionType::Frame & nSelType) || (SelectionType::Graphic & nSelType)) + { + rSh.GetFlyFrameAttr(aCoreSet); + } + else + { + rSh.GetCurAttr(aCoreSet); + } + + aBrushItem = getSvxBrushItemFromSourceSet(aCoreSet, RES_BACKGROUND); + } + + switch(nSlot) + { + case SID_BACKGROUND_COLOR: + case SID_TABLE_CELL_BACKGROUND_COLOR: + { + const SfxPoolItem* pColorStringItem = nullptr; + bool bIsTransparent = false; + + aBrushItem->SetGraphicPos(GPOS_NONE); + + sal_uInt16 nSlotId = (nSlot == SID_BACKGROUND_COLOR) ? SID_BACKGROUND_COLOR : SID_TABLE_CELL_BACKGROUND_COLOR; + if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pColorStringItem)) + { + OUString sColor = static_cast<const SfxStringItem*>(pColorStringItem)->GetValue(); + if (sColor == "transparent") + { + bIsTransparent = true; + } + else + { + Color aColor(sColor.toInt32(16)); + + aBrushItem->SetColor(aColor); + + SvxColorItem aNewColorItem(nSlotId); + aNewColorItem.SetValue(aColor); + + GetView().GetViewFrame()->GetBindings().SetState(aNewColorItem); + } + } + else if (pArgs) + { + const SvxColorItem& rNewColorItem = static_cast<const SvxColorItem&>(pArgs->Get(nSlotId)); + const Color& rNewColor = rNewColorItem.GetValue(); + aBrushItem->SetColor(rNewColor); + GetView().GetViewFrame()->GetBindings().SetState(rNewColorItem); + } + else + { + bIsTransparent = true; + } + + if (bIsTransparent) + { + aBrushItem->SetColor(COL_TRANSPARENT); + rReq.AppendItem(SvxColorItem(COL_TRANSPARENT,nSlot)); + } + break; + } + + case SID_ATTR_BRUSH: + case RES_BACKGROUND: + { + assert(pArgs && "only SID_BACKGROUND_COLOR can have !pArgs, checked at entry"); + aBrushItem.reset(static_cast<SvxBrushItem*>(pArgs->Get(GetPool().GetWhich(nSlot)).Clone())); + break; + } + default: + { + rReq.Ignore(); + OSL_FAIL("unknown message in ExecuteAttr!" ); + return; + } + } + + if ( nSlot == SID_TABLE_CELL_BACKGROUND_COLOR ) + { + rSh.SetBoxBackground( *aBrushItem ); + } + else + { + // Adapt to new DrawingLayer FillStyle; use a parent which has XFILL_NONE set + SfxItemSet aCoreSet(GetPool(), svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>{}); + + aCoreSet.SetParent(&GetView().GetDocShell()->GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + setSvxBrushItemAsFillAttributesToTargetSet(*aBrushItem, aCoreSet); + + if((SelectionType::Frame & nSelType) || (SelectionType::Graphic & nSelType)) + { + // Template autoupdate + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + + if(pFormat && pFormat->IsAutoUpdateFormat()) + { + rSh.AutoUpdateFrame(pFormat, aCoreSet); + } + else + { + rSh.SetFlyFrameAttr(aCoreSet); + } + } + else + { + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + + if(pColl && pColl->IsAutoUpdateFormat()) + { + rSh.AutoUpdatePara(pColl, aCoreSet); + } + else + { + rSh.SetAttrSet(aCoreSet); + } + } + } + + rReq.Done(); +} + +void SwBaseShell::GetBorderState(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + // Tabele cell(s) selected? + bool bPrepare = true; + bool bTableMode = rSh.IsTableMode(); + if ( bTableMode ) + { + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{} ); + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + aCoreSet.Put( aBoxInfo ); + rSh.GetTabBorders( aCoreSet ); + rSet.Put( aCoreSet ); + } + else if ( rSh.IsFrameSelected() ) + { + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + rSet.Put( aMgr.GetAttrSet() ); + bPrepare = false; + } + else + // Get border attributes via shell quite normal + rSh.GetCurAttr( rSet ); + if ( bPrepare ) + ::PrepareBoxInfo( rSet, rSh ); + // Switch the border toolbox controller mode + rSet.Put( SfxBoolItem( SID_BORDER_REDUCED_MODE, !bTableMode )); +} + +void SwBaseShell::ExecDlg(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + weld::Window* pMDI = GetView().GetFrameWeld(); + // So that from the basic no dialogues for the background views are called: + bool bBackground = (&GetView() != GetActiveView()); + const SfxPoolItem* pItem = nullptr; + const SfxItemSet* pArgs = rReq.GetArgs(); + + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxItemSet* pOutSet = nullptr; + bool bDone = false; + if(pArgs) + pArgs->GetItemState( GetPool().GetWhich(nSlot), false, &pItem ); + + switch ( nSlot ) + { + case FN_FORMAT_TITLEPAGE_DLG: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateTitlePageDlg(pMDI)); + pDlg->Execute(); + } + break; + case FN_FORMAT_PAGE_DLG: + case FN_FORMAT_PAGE_COLUMN_DLG: + case FN_FORMAT_PAGE_SETTING_DLG: + { + if( !bBackground ) + { + const size_t nCurIdx = rSh.GetCurPageDesc(); + const SwPageDesc& rPageDesc = rSh.GetPageDesc( nCurIdx ); + // Temporary view, because the shell does not need to be valid after the dialog + // for example disable header + SwView& rTempView = GetView(); + + OString sPageId; + switch (nSlot) + { + case FN_FORMAT_PAGE_COLUMN_DLG: + sPageId = "columns"; + break; + case FN_FORMAT_PAGE_SETTING_DLG: + sPageId = "page"; + break; + case FN_FORMAT_PAGE_DLG: + if (pItem) + sPageId = OUStringToOString(static_cast<const SfxStringItem*>(pItem)->GetValue(), RTL_TEXTENCODING_UTF8); + break; + } + rTempView.GetDocShell()->FormatPage(rPageDesc.GetName(), sPageId, rSh, &rReq); + rTempView.InvalidateRulerPos(); + + bDone = true; // FormatPage() takes care of calling Done() + } + } + break; + case FN_FORMAT_BORDER_DLG: + { + SfxItemSet aSet( rSh.GetAttrPool(), + svl::Items<RES_BOX , RES_SHADOW, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{} ); + ScopedVclPtr<SfxAbstractDialog> pDlg; + // Table cell(s) selected? + if ( rSh.IsTableMode() ) + { + // Set border attributes Get/SetTabBorders() + ::PrepareBoxInfo( aSet, rSh ); + rSh.GetTabBorders( aSet ); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSwBorderDlg(pMDI, aSet, SwBorderModes::TABLE)); + if ( pDlg->Execute() == RET_OK ) + { + rSh.SetTabBorders( *pDlg->GetOutputItemSet() ); + pOutSet = pDlg->GetOutputItemSet(); + } + } + else if ( rSh.IsFrameSelected() ) + { + // Set border attributes via Frame-Manager + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + aSet.Put( aMgr.GetAttrSet() ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSwBorderDlg(pMDI, aSet, SwBorderModes::FRAME)); + if ( pDlg->Execute() == RET_OK ) + { + aMgr.SetAttrSet( *pDlg->GetOutputItemSet() ); + aMgr.UpdateFlyFrame(); + pOutSet = pDlg->GetOutputItemSet(); + } + } + else + { + // Set border attributes via Shell quite normal + rSh.GetCurAttr( aSet ); + ::PrepareBoxInfo( aSet, rSh ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSwBorderDlg(pMDI, aSet, SwBorderModes::PARA)); + if ( pDlg->Execute() == RET_OK ) + { + rSh.SetAttrSet( *pDlg->GetOutputItemSet() ); + pOutSet = pDlg->GetOutputItemSet(); + } + } + if(pOutSet) + { + rReq.Done(*pOutSet); + bDone = true; + } + } + break; + case FN_FORMAT_BACKGROUND_DLG: + { + SfxItemSet aSet( rSh.GetAttrPool(), + svl::Items<RES_BACKGROUND, RES_BACKGROUND, + XATTR_FILL_FIRST, XATTR_FILL_LAST>{} ); + + ScopedVclPtr<SfxAbstractDialog> pDlg; + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + + // Table cell(s) selected? + if ( rSh.IsTableMode() ) + { + // Get background attributes of the table and put it in the set + std::unique_ptr<SvxBrushItem> aBrush; + rSh.GetBoxBackground( aBrush ); + pDlg.disposeAndReset(pFact->CreateSwBackgroundDialog(pMDI, aSet)); + aSet.Put( *aBrush ); + if ( pDlg->Execute() == RET_OK ) + { + + rSh.SetBoxBackground( pDlg->GetOutputItemSet()->Get( RES_BACKGROUND ) ); + pOutSet = pDlg->GetOutputItemSet(); + } + } + else if ( rSh.IsFrameSelected() ) + { + + rSh.GetFlyFrameAttr( aSet ); + + pDlg.disposeAndReset(pFact->CreateSwBackgroundDialog(pMDI, aSet)); + if ( pDlg->Execute() == RET_OK ) + { + rSh.SetFlyFrameAttr(const_cast<SfxItemSet &>(*pDlg->GetOutputItemSet()) ); + pOutSet = pDlg->GetOutputItemSet(); + } + } + else + { + // Set border attributes Umrandungsattribute with the shell quite normal. + rSh.GetCurAttr( aSet ); + + pDlg.disposeAndReset(pFact->CreateSwBackgroundDialog(pMDI, aSet)); + if ( pDlg->Execute() == RET_OK ) + { + rSh.SetAttrSet( *pDlg->GetOutputItemSet() ); + pOutSet = pDlg->GetOutputItemSet(); + } + } + if(pOutSet) + { + rReq.Done(*pOutSet); + bDone = true; + } + } + break; + case SID_ACCESSIBILITY_CHECK: + { + sw::AccessibilityCheck aCheck(rSh.GetDoc()); + aCheck.check(); + svx::AccessibilityCheckDialog aDialog(pMDI, aCheck.getIssueCollecton()); + aDialog.run(); + } + break; + default:OSL_FAIL("wrong Dispatcher (basesh.cxx)"); + } + if(!bDone) + rReq.Done(); +} + +SwWrtShell& SwBaseShell::GetShell() +{ + return rView.GetWrtShell(); +} + +SwWrtShell* SwBaseShell::GetShellPtr() +{ + return rView.GetWrtShellPtr(); +} + +static void EndUndo(SwWrtShell& rSh) +{ + SwRewriter aRewriter; + + if (rSh.GetTableFormat()) + { + aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriter.AddRule(UndoArg2, rSh.GetTableFormat()->GetName()); + aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + + } + rSh.EndUndo(SwUndoId::INSTABLE, &aRewriter); // If possible change the Shell +} + +static void InsertTableImpl(SwWrtShell& rSh, + SwView &rTempView, + const OUString& aTableName, + sal_uInt16 nRows, + sal_uInt16 nCols, + SwInsertTableOptions aInsTableOpts, + const OUString& aAutoName, + const std::unique_ptr<SwTableAutoFormat>& pTAFormat) +{ + rSh.StartUndo(SwUndoId::INSTABLE); + + rSh.StartAllAction(); + if( rSh.HasSelection() ) + rSh.DelRight(); + + rSh.InsertTable( aInsTableOpts, nRows, nCols, pTAFormat.get() ); + rSh.MoveTable( GotoPrevTable, fnTableStart ); + + if( !aTableName.isEmpty() && !rSh.GetTableStyle( aTableName ) ) + rSh.GetTableFormat()->SetName( aTableName ); + + if( pTAFormat != nullptr && !aAutoName.isEmpty() + && aAutoName != SwViewShell::GetShellRes()->aStrNone ) + { + SwTableNode* pTableNode = const_cast<SwTableNode*>( rSh.IsCursorInTable() ); + if ( pTableNode ) + { + pTableNode->GetTable().SetTableStyleName( aAutoName ); + SwUndoTableAutoFormat* pUndo = new SwUndoTableAutoFormat( *pTableNode, *pTAFormat ); + if ( pUndo ) + rSh.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) ); + } + } + + rSh.EndAllAction(); + rTempView.AutoCaption(TABLE_CAP); +} + +void SwBaseShell::InsertTable( SfxRequest& _rRequest ) +{ + const SfxItemSet* pArgs = _rRequest.GetArgs(); + SwWrtShell& rSh = GetShell(); + + if ( !( rSh.GetFrameType( nullptr, true ) & FrameTypeFlags::FOOTNOTE ) ) + { + SwView &rTempView = GetView(); // Because GetView() does not work after the shell exchange + bool bHTMLMode = 0 != (::GetHtmlMode(rTempView.GetDocShell())&HTMLMODE_ON); + bool bCallEndUndo = false; + + if( !pArgs && rSh.IsSelection() && !rSh.IsInClickToEdit() && + !rSh.IsTableMode() ) + { + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + SwInsertTableOptions aInsTableOpts = pModOpt->GetInsTableFlags(bHTMLMode); + + rSh.StartUndo(SwUndoId::INSTABLE); + bCallEndUndo = true; + + bool bInserted = rSh.TextToTable( aInsTableOpts, '\t' ); + rSh.EnterStdMode(); + if (bInserted) + rTempView.AutoCaption(TABLE_CAP); + _rRequest.Done(); + } + else + { + sal_uInt16 nColsIn = 0; + sal_uInt16 nRowsIn = 0; + SwInsertTableOptions aInsTableOptsIn( SwInsertTableFlags::All, 1 ); + OUString aTableNameIn; + OUString aAutoNameIn; + std::unique_ptr<SwTableAutoFormat> pTAFormatIn; + + if( pArgs && pArgs->Count() >= 2 ) + { + const SfxStringItem* pName = _rRequest.GetArg<SfxStringItem>(FN_INSERT_TABLE); + const SfxUInt16Item* pCols = _rRequest.GetArg<SfxUInt16Item>(SID_ATTR_TABLE_COLUMN); + const SfxUInt16Item* pRows = _rRequest.GetArg<SfxUInt16Item>(SID_ATTR_TABLE_ROW); + const SfxInt32Item* pFlags = _rRequest.GetArg<SfxInt32Item>(FN_PARAM_1); + const SfxStringItem* pAuto = _rRequest.GetArg<SfxStringItem>(FN_PARAM_2); + + if ( pName ) + aTableNameIn = pName->GetValue(); + if ( pCols ) + nColsIn = pCols->GetValue(); + if ( pRows ) + nRowsIn = pRows->GetValue(); + if ( pAuto ) + { + aAutoNameIn = pAuto->GetValue(); + if ( !aAutoNameIn.isEmpty() ) + { + SwTableAutoFormatTable aTableTable; + aTableTable.Load(); + for ( size_t n=0; n<aTableTable.size(); n++ ) + { + if ( aTableTable[n].GetName() == aAutoNameIn ) + { + pTAFormatIn.reset(new SwTableAutoFormat( aTableTable[n] )); + break; + } + } + } + } + + if ( pFlags ) + aInsTableOptsIn.mnInsMode = static_cast<SwInsertTableFlags>(pFlags->GetValue()); + else + { + const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + aInsTableOptsIn = pModOpt->GetInsTableFlags(bHTMLMode); + } + } + + if( !nColsIn || !nRowsIn ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + std::shared_ptr<AbstractInsTableDlg> pAbstractDialog(pFact->CreateInsTableDlg(rTempView)); + std::shared_ptr<weld::DialogController> pDialogController(pAbstractDialog->getDialogController()); + + weld::DialogController::runAsync(pDialogController, + [pAbstractDialog, &rSh, &rTempView, aTableNameIn, nRowsIn, nColsIn, aInsTableOptsIn, aAutoNameIn] (sal_Int32 nResult) { + if( RET_OK == nResult ) + { + sal_uInt16 nCols = nColsIn; + sal_uInt16 nRows = nRowsIn; + SwInsertTableOptions aInsTableOpts = aInsTableOptsIn; + OUString aTableName = aTableNameIn; + OUString aAutoName = aAutoNameIn; + std::unique_ptr<SwTableAutoFormat> pTAFormat; + + pAbstractDialog->GetValues( aTableName, nRows, nCols, aInsTableOpts, aAutoName, pTAFormat ); + + if( nCols && nRows ) + { + InsertTableImpl( rSh, rTempView, aTableName, nRows, nCols, aInsTableOpts, aAutoName, pTAFormat ); + EndUndo(rSh); + } + } + } + ); + } + else + { + // record before shell change + _rRequest.AppendItem( SfxStringItem( FN_INSERT_TABLE, aTableNameIn ) ); + if ( !aAutoNameIn.isEmpty() ) + _rRequest.AppendItem( SfxStringItem( FN_PARAM_2, aAutoNameIn ) ); + _rRequest.AppendItem( SfxUInt16Item( SID_ATTR_TABLE_COLUMN, nColsIn ) ); + _rRequest.AppendItem( SfxUInt16Item( SID_ATTR_TABLE_ROW, nRowsIn ) ); + _rRequest.AppendItem( SfxInt32Item( FN_PARAM_1, static_cast<sal_Int32>(aInsTableOptsIn.mnInsMode) ) ); + _rRequest.Done(); + + InsertTableImpl( rSh, rTempView, aTableNameIn, nRowsIn, nColsIn, aInsTableOptsIn, aAutoNameIn, pTAFormatIn ); + + bCallEndUndo = true; + } + } + + if( bCallEndUndo ) + EndUndo(rSh); + } +} + +void SwBaseShell::GetGalleryState( SfxItemSet &rSet ) +{ + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + switch ( nWhich ) + { + case SID_GALLERY_BG_BRUSH: + { + SelectionType nSel = rSh.GetSelectionType(); + SfxStringListItem aLst( nWhich ); + std::vector<OUString> &rLst = aLst.GetList(); + nParagraphPos = nGraphicPos = nOlePos = nFramePos = nTablePos = + nTableRowPos = nTableCellPos = nPagePos = + nHeaderPos = nFooterPos = 0; + sal_uInt8 nPos = 1; + rLst.push_back( SwResId( STR_SWBG_PAGE ) ); + nPagePos = nPos++; + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + bool bHtmlMode = 0 != (nHtmlMode & HTMLMODE_ON); + + if ( (!bHtmlMode || (nHtmlMode & HTMLMODE_FULL_STYLES)) && + (nSel & SelectionType::Text) ) + { + rLst.push_back( SwResId( STR_SWBG_PARAGRAPH ) ); + nParagraphPos = nPos++; + } + if ( (!bHtmlMode || (nHtmlMode & HTMLMODE_SOME_STYLES)) && + nSel & (SelectionType::Table|SelectionType::TableCell) ) + { + rLst.push_back( SwResId( STR_SWBG_TABLE ) ); + nTablePos = nPos++; + + if(!bHtmlMode) + { + rLst.push_back( SwResId( STR_SWBG_TABLE_ROW ) ); + nTableRowPos = nPos++; + } + + rLst.push_back( SwResId( STR_SWBG_TABLE_CELL) ); + nTableCellPos = nPos++; + } + if(!bHtmlMode) + { + if ( nSel & SelectionType::Frame ) + { + rLst.push_back( SwResId( STR_SWBG_FRAME ) ); + nFramePos = nPos++; + } + if ( nSel & SelectionType::Graphic ) + { + rLst.push_back( SwResId( STR_SWBG_GRAPHIC ) ); + nGraphicPos = nPos++; + } + if ( nSel & SelectionType::Ole ) + { + rLst.push_back( SwResId( STR_SWBG_OLE ) ); + nOlePos = nPos++; + } + const FrameTypeFlags nType = rSh.GetFrameType(nullptr,true); + if ( nType & FrameTypeFlags::HEADER ) + { + rLst.push_back( SwResId( STR_SWBG_HEADER ) ); + nHeaderPos = nPos++; + } + if ( nType & FrameTypeFlags::FOOTER ) + { + rLst.push_back( SwResId( STR_SWBG_FOOTER ) ); + nFooterPos = nPos; + } + } + if ( rLst.empty() ) + rSet.DisableItem( nWhich ); + else + rSet.Put( aLst ); + break; + } + } +} + +void SwBaseShell::ExecuteGallery(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + rSh.StartAction(); + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + switch(nSlot) + { + case SID_GALLERY_BG_BRUSH: + { + if ( !pArgs ) + break; + + SelectionType nSel = rSh.GetSelectionType(); + if ( nSel & SelectionType::DrawObjectEditMode ) + break; + + const SfxUInt16Item* pPos = rReq.GetArg<SfxUInt16Item>(SID_GALLERY_BG_POS); + const SvxBrushItem* pBrush = rReq.GetArg<SvxBrushItem>(SID_GALLERY_BG_BRUSH); + if ( !pPos || !pBrush ) + break; + + sal_uInt8 nPos = pPos->GetValue(); + ++nPos; + + SvxBrushItem aBrush( *pBrush ); + aBrush.SetWhich( RES_BACKGROUND ); + if ( nPos == nParagraphPos ) + rSh.SetAttrItem( aBrush ); + else if ( nPos == nTablePos ) + rSh.SetTabBackground( aBrush ); + else if ( nPos == nTableRowPos ) + rSh.SetRowBackground( aBrush ); + else if ( nPos == nTableCellPos ) + rSh.SetBoxBackground( aBrush ); + else if ( nPos == nFramePos || nPos == nGraphicPos || nPos == nOlePos ) + { + SfxItemSet aCoreSet(GetPool(), svl::Items<RES_BACKGROUND, RES_BACKGROUND>{}); + aCoreSet.Put( aBrush ); + rSh.SetFlyFrameAttr( aCoreSet ); + } + else if ( nPos == nPagePos || nPos == nHeaderPos || nPos == nFooterPos ) + { + sal_uInt16 nDesc = rSh.GetCurPageDesc(); + SwPageDesc aDesc( rSh.GetPageDesc( nDesc ) ); + if ( nPos == nPagePos ) + aDesc.GetMaster().SetFormatAttr( aBrush ); + else if ( nPos == nHeaderPos ) + { + SwFormatHeader aHead( aDesc.GetMaster().GetHeader() ); + aHead.GetHeaderFormat()->SetFormatAttr( aBrush ); + aDesc.GetMaster().SetFormatAttr( aHead ); + } + else if ( nPos == nFooterPos ) + { + SwFormatFooter aFoot( aDesc.GetMaster().GetFooter() ); + aFoot.GetFooterFormat()->SetFormatAttr( aBrush ); + aDesc.GetMaster().SetFormatAttr( aFoot ); + } + rSh.ChgPageDesc( nDesc, aDesc ); + } + break; + } + } + rSh.EndAction(); + rReq.Done(); +} + +void SwBaseShell::ExecField( SfxRequest const & rReq ) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch( nSlot ) + { +#if HAVE_FEATURE_DBCONNECTIVITY + case FN_CHANGE_DBFIELD: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwChangeDBDlg(GetView())); + pDlg->Execute(); + } + break; +#endif + default: + OSL_FAIL("wrong dispatcher"); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/beziersh.cxx b/sw/source/uibase/shells/beziersh.cxx new file mode 100644 index 000000000..4764deced --- /dev/null +++ b/sw/source/uibase/shells/beziersh.cxx @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svx/svdview.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <svx/svdopath.hxx> +#include <vcl/EnumContext.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <drawbase.hxx> +#include <beziersh.hxx> +#define ShellClass_SwBezierShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwBezierShell, SwBaseShell) + +void SwBezierShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("draw"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Bezier_Toolbox_Sw); +} + + +SwBezierShell::SwBezierShell(SwView &_rView): + SwBaseShell( _rView ) +{ + SetName("Bezier"); + + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + pSdrView->SetEliminatePolyPointLimitAngle(1500); + + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Draw)); +} + +void SwBezierShell::Execute(SfxRequest const &rReq) +{ + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + const SfxItemSet *pArgs = rReq.GetArgs(); + sal_uInt16 nSlotId = rReq.GetSlot(); + bool bChanged = pSdrView->GetModel()->IsChanged(); + pSdrView->GetModel()->SetChanged(false); + const SfxPoolItem* pItem; + if(pArgs) + pArgs->GetItemState(nSlotId, false, &pItem); + + switch (nSlotId) + { + case SID_DELETE: + case FN_BACKSPACE: + if (pSh->IsObjSelected()) + { + if (pSdrView->HasMarkedPoints()) + pSh->GetView().GetViewFrame()->GetDispatcher()->Execute(SID_BEZIER_DELETE); + else + { + pSh->DelSelectedObj(); + if (pSh->IsSelFrameMode()) + { + pSh->LeaveSelFrameMode(); + pSh->NoEdit(); + } + GetView().AttrChangedNotify(nullptr); // Shell change if applicable... + } + } + break; + + case FN_ESCAPE: + if (pSdrView->HasMarkedPoints()) + pSdrView->UnmarkAllPoints(); + else + { + if ( pSh->IsDrawCreate() ) + { + GetView().GetDrawFuncPtr()->BreakCreate(); + GetView().AttrChangedNotify(nullptr); // Shell change if applicable... + } + else if ( pSh->HasSelection() || GetView().IsDrawMode() ) + { + GetView().LeaveDrawCreate(); + pSh->EnterStdMode(); + GetView().AttrChangedNotify(nullptr); // Shell change if applicable... + } + } + break; + + case SID_BEZIER_MOVE: + case SID_BEZIER_INSERT: + { + GetView().GetEditWin().SetBezierMode(nSlotId); + static sal_uInt16 aInva[] = + { + SID_BEZIER_INSERT, + SID_BEZIER_MOVE, + 0 + }; + GetView().GetViewFrame()->GetBindings().Invalidate(aInva); + } + break; + + case SID_BEZIER_DELETE: + case SID_BEZIER_CUTLINE: + case SID_BEZIER_CONVERT: + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + case SID_BEZIER_CLOSE: + case SID_BEZIER_ELIMINATE_POINTS: + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + + if (rMarkList.GetMark(0) && !pSdrView->IsAction()) + { + switch (nSlotId) + { + case SID_BEZIER_DELETE: + pSdrView->DeleteMarkedPoints(); + break; + + case SID_BEZIER_CUTLINE: + { + pSdrView->RipUpAtMarkedPoints(); + pSh->CheckUnboundObjects(); + } + break; + + case SID_BEZIER_CONVERT: + { + pSdrView->SetMarkedSegmentsKind(SdrPathSegmentKind::Toggle); + break; + } + + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + { + SdrPathSmoothKind eKind = SdrPathSmoothKind::Asymmetric; + + switch (nSlotId) + { + case SID_BEZIER_EDGE: eKind = SdrPathSmoothKind::Angular; break; + case SID_BEZIER_SMOOTH: eKind = SdrPathSmoothKind::Asymmetric; break; + case SID_BEZIER_SYMMTR: eKind = SdrPathSmoothKind::Symmetric; break; + } + + SdrPathSmoothKind eSmooth = pSdrView->GetMarkedPointsSmooth(); + if (eKind != eSmooth) + { + pSdrView->SetMarkedPointsSmooth(eKind); + + static sal_uInt16 aInva[] = + { + SID_BEZIER_SMOOTH, + SID_BEZIER_EDGE, + SID_BEZIER_SYMMTR, + 0 + }; + GetView().GetViewFrame()->GetBindings().Invalidate(aInva); + } + break; + } + + case SID_BEZIER_CLOSE: + { + SdrPathObj* pPathObj = static_cast<SdrPathObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + pSdrView->UnmarkAllPoints(); + // Size aDist(GetView().GetEditWin().PixelToLogic(Size(8,8))); + pPathObj->ToggleClosed(); // aDist.Width()); + break; + } + + case SID_BEZIER_ELIMINATE_POINTS: + pSdrView->SetEliminatePolyPoints(!pSdrView->IsEliminatePolyPoints()); + break; + } + } + } + break; + + default: + break; + } + + if (pSdrView->GetModel()->IsChanged()) + GetShell().SetModified(); + else if (bChanged) + pSdrView->GetModel()->SetChanged(); +} + +void SwBezierShell::GetState(SfxItemSet &rSet) +{ + SdrView* pSdrView = GetShell().GetDrawView(); + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while( nWhich ) + { + switch( nWhich ) + { + case SID_BEZIER_MOVE: + case SID_BEZIER_INSERT: + { + sal_uInt16 nEditMode = GetView().GetEditWin().GetBezierMode(); + + rSet.Put(SfxBoolItem(nWhich, nEditMode == nWhich)); + } + break; + + case SID_BEZIER_CUTLINE: + if (!pSdrView->IsRipUpAtMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_CUTLINE); + } + break; + + case SID_BEZIER_DELETE: + if (!pSdrView->IsDeleteMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_DELETE); + } + break; + + case SID_BEZIER_CONVERT: + if (!pSdrView->IsSetMarkedSegmentsKindPossible()) + { + rSet.DisableItem(SID_BEZIER_CONVERT); + } + else + { + SdrPathSegmentKind eSegm = pSdrView->GetMarkedSegmentsKind(); + switch (eSegm) + { + case SdrPathSegmentKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CONVERT); break; + case SdrPathSegmentKind::Line : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,false)); break; // Button pressed = curve + case SdrPathSegmentKind::Curve : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,true)); break; + default:; //prevent warning + } + } + break; + + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + if (!pSdrView->IsSetMarkedPointsSmoothPossible()) + rSet.DisableItem(nWhich); + else + { + SdrPathSmoothKind eSmooth = pSdrView->GetMarkedPointsSmooth(); + bool bEnable = false; + switch (eSmooth) + { + case SdrPathSmoothKind::DontCare : + break; + case SdrPathSmoothKind::Angular : + bEnable = nWhich == SID_BEZIER_EDGE; + break; + case SdrPathSmoothKind::Asymmetric: + bEnable = nWhich == SID_BEZIER_SMOOTH; + break; + case SdrPathSmoothKind::Symmetric : + bEnable = nWhich == SID_BEZIER_SYMMTR; + break; + } + rSet.Put(SfxBoolItem(nWhich, bEnable)); + } + break; + + case SID_BEZIER_CLOSE: + if (!pSdrView->IsOpenCloseMarkedObjectsPossible()) + { + rSet.DisableItem(SID_BEZIER_CLOSE); + } + else + { + SdrObjClosedKind eClose = pSdrView->GetMarkedObjectsClosedState(); + switch (eClose) + { + case SdrObjClosedKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CLOSE); break; + case SdrObjClosedKind::Open : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,false)); break; + case SdrObjClosedKind::Closed : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,true)); break; + default:; //prevent warning + } + } + break; + + case SID_BEZIER_ELIMINATE_POINTS: + rSet.Put(SfxBoolItem(SID_BEZIER_ELIMINATE_POINTS, pSdrView->IsEliminatePolyPoints())); + break; + + default: + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drawdlg.cxx b/sw/source/uibase/shells/drawdlg.cxx new file mode 100644 index 000000000..36d231bc4 --- /dev/null +++ b/sw/source/uibase/shells/drawdlg.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 <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdview.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> + +#include <drawsh.hxx> +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> +#include <memory> +#include <svl/stritem.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xflclit.hxx> +#include <svx/chrtitem.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xfltrit.hxx> +#include <comphelper/lok.hxx> + +using namespace com::sun::star::drawing; + +void SwDrawShell::ExecDrawDlg(SfxRequest& rReq) +{ + SwWrtShell* pSh = &GetShell(); + SdrView* pView = pSh->GetDrawView(); + SdrModel* pDoc = pView->GetModel(); + bool bChanged = pDoc->IsChanged(); + pDoc->SetChanged(false); + + SfxItemSet aNewAttr( pDoc->GetItemPool() ); + pView->GetAttributes( aNewAttr ); + + GetView().NoRotate(); + + switch (rReq.GetSlot()) + { + case FN_DRAWTEXT_ATTR_DLG: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTextTabDialog(rReq.GetFrameWeld(), &aNewAttr, pView)); + sal_uInt16 nResult = pDlg->Execute(); + + if (nResult == RET_OK) + { + if (pView->AreObjectsMarked()) + { + pSh->StartAction(); + pView->SetAttributes(*pDlg->GetOutputItemSet()); + rReq.Done(*(pDlg->GetOutputItemSet())); + pSh->EndAction(); + } + } + } + break; + + case SID_MEASURE_DLG: + { + bool bHasMarked = pView->AreObjectsMarked(); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSfxDialog(rReq.GetFrameWeld(), + aNewAttr, pView, RID_SVXPAGE_MEASURE)); + if (pDlg->Execute() == RET_OK) + { + pSh->StartAction(); + if (bHasMarked) + pView->SetAttrToMarked(*pDlg->GetOutputItemSet(), false); + else + pView->SetDefaultAttr(*pDlg->GetOutputItemSet(), false); + pSh->EndAction(); + } + } + break; + + case SID_ATTRIBUTES_AREA: + { + bool bHasMarked = pView->AreObjectsMarked(); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<AbstractSvxAreaTabDialog> pDlg(pFact->CreateSvxAreaTabDialog(rReq.GetFrameWeld(), + &aNewAttr, + pDoc, + true)); + + pDlg->StartExecuteAsync([bChanged, bHasMarked, pDoc, pDlg, pSh, pView, this]( + sal_Int32 nResult){ + if (nResult == RET_OK) + { + pSh->StartAction(); + if (bHasMarked) + pView->SetAttributes(*pDlg->GetOutputItemSet()); + else + pView->SetDefaultAttr(*pDlg->GetOutputItemSet(), false); + pSh->EndAction(); + + static sal_uInt16 aInval[] = + { + SID_ATTR_FILL_STYLE, + SID_ATTR_FILL_COLOR, + SID_ATTR_FILL_TRANSPARENCE, + SID_ATTR_FILL_FLOATTRANSPARENCE, + 0 + }; + SfxBindings &rBnd = GetView().GetViewFrame()->GetBindings(); + rBnd.Invalidate(aInval); + rBnd.Update(SID_ATTR_FILL_STYLE); + rBnd.Update(SID_ATTR_FILL_COLOR); + rBnd.Update(SID_ATTR_FILL_TRANSPARENCE); + rBnd.Update(SID_ATTR_FILL_FLOATTRANSPARENCE); + } + + if (pDoc->IsChanged()) + GetShell().SetModified(); + else if (bChanged) + pDoc->SetChanged(); + + pDlg->disposeOnce(); + }); + } + break; + + case SID_ATTRIBUTES_LINE: + { + bool bHasMarked = pView->AreObjectsMarked(); + + const SdrObject* pObj = nullptr; + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxLineTabDialog(rReq.GetFrameWeld(), + &aNewAttr, + pDoc, + pObj, + bHasMarked)); + + pDlg->StartExecuteAsync([bChanged, bHasMarked, pDoc, pDlg, pSh, pView, this]( + sal_Int32 nResult){ + if (nResult == RET_OK) + { + pSh->StartAction(); + if(bHasMarked) + pView->SetAttrToMarked(*pDlg->GetOutputItemSet(), false); + else + pView->SetDefaultAttr(*pDlg->GetOutputItemSet(), false); + pSh->EndAction(); + + static sal_uInt16 aInval[] = + { + SID_ATTR_LINE_STYLE, // ( SID_SVX_START + 169 ) + SID_ATTR_LINE_DASH, // ( SID_SVX_START + 170 ) + SID_ATTR_LINE_WIDTH, // ( SID_SVX_START + 171 ) + SID_ATTR_LINE_COLOR, // ( SID_SVX_START + 172 ) + SID_ATTR_LINE_START, // ( SID_SVX_START + 173 ) + SID_ATTR_LINE_END, // ( SID_SVX_START + 174 ) + SID_ATTR_LINE_TRANSPARENCE, // (SID_SVX_START+1107) + SID_ATTR_LINE_JOINT, // (SID_SVX_START+1110) + SID_ATTR_LINE_CAP, // (SID_SVX_START+1111) + 0 + }; + + GetView().GetViewFrame()->GetBindings().Invalidate(aInval); + } + + if (pDoc->IsChanged()) + GetShell().SetModified(); + else if (bChanged) + pDoc->SetChanged(); + + pDlg->disposeOnce(); + }); + } + break; + + default: + break; + } + + if (pDoc->IsChanged()) + GetShell().SetModified(); + else + if (bChanged) + pDoc->SetChanged(); +} + +namespace +{ + void lcl_convertStringArguments(sal_uInt16 nSlot, std::unique_ptr<SfxItemSet>& pArgs) + { + Color aColor; + OUString sColor; + const SfxPoolItem* pItem = nullptr; + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + sColor = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_LINE_COLOR: + { + XLineColorItem aLineColorItem(OUString(), aColor); + pArgs->Put(aLineColorItem); + break; + } + + case SID_ATTR_FILL_COLOR: + { + XFillColorItem aFillColorItem(OUString(), aColor); + pArgs->Put(aFillColorItem); + break; + } + } + } + else if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_LINE_WIDTH_ARG, false, &pItem)) + { + double fValue = static_cast<const SvxDoubleItem*>(pItem)->GetValue(); + // FIXME: different units... + int nPow = 100; + int nValue = fValue * nPow; + + XLineWidthItem aItem(nValue); + pArgs->Put(aItem); + } + if (SfxItemState::SET == pArgs->GetItemState(SID_FILL_GRADIENT_JSON, false, &pItem)) + { + const SfxStringItem* pJSON = static_cast<const SfxStringItem*>(pItem); + if (pJSON) + { + XGradient aGradient = XGradient::fromJSON(pJSON->GetValue()); + XFillGradientItem aItem(aGradient); + pArgs->Put(aItem); + } + } + } +} + +void SwDrawShell::ExecDrawAttrArgs(SfxRequest const & rReq) +{ + SwWrtShell* pSh = &GetShell(); + SdrView* pView = pSh->GetDrawView(); + const SfxItemSet* pArgs = rReq.GetArgs(); + bool bChanged = pView->GetModel()->IsChanged(); + pView->GetModel()->SetChanged(false); + + GetView().NoRotate(); + + if (pArgs) + { + if(pView->AreObjectsMarked()) + { + std::unique_ptr<SfxItemSet> pNewArgs = pArgs->Clone(); + lcl_convertStringArguments(rReq.GetSlot(), pNewArgs); + pView->SetAttrToMarked(*pNewArgs, false); + } + else + pView->SetDefaultAttr(*rReq.GetArgs(), false); + } + else + { + SfxDispatcher* pDis = pSh->GetView().GetViewFrame()->GetDispatcher(); + switch (rReq.GetSlot()) + { + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + pDis->Execute(SID_ATTRIBUTES_AREA); + break; + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + pDis->Execute(SID_ATTRIBUTES_LINE); + break; + } + } + if (pView->GetModel()->IsChanged()) + GetShell().SetModified(); + else + if (bChanged) + pView->GetModel()->SetChanged(); +} + +static void lcl_unifyFillTransparencyItems(SfxItemSet& rSet) +{ + // Transparent fill options are None, Solid, Linear, Axial, Radial, Elliptical, Quadratic, Square. + // But this is represented across two items namely XFillTransparenceItem (for None and Solid) + // and XFillFloatTransparenceItem (for the rest). To simplify the representation in LOKit case let's + // use XFillFloatTransparenceItem to carry the information of XFillTransparenceItem when gradients + // are disabled. When gradient transparency is disabled, all fields of XFillFloatTransparenceItem are invalid + // and not used. So convert XFillTransparenceItem's constant transparency percentage as an intensity + // and assign this to the XFillFloatTransparenceItem's start-intensity and end-intensity fields. + // Now the LOK clients need only listen to statechange messages of XFillFloatTransparenceItem + // to get fill-transparency settings instead of listening to two separate items. + + XFillFloatTransparenceItem* pFillFloatTranspItem = + const_cast<XFillFloatTransparenceItem*> + (rSet.GetItem<XFillFloatTransparenceItem>(XATTR_FILLFLOATTRANSPARENCE)); + if (!pFillFloatTranspItem || pFillFloatTranspItem->IsEnabled()) + return; + + const XFillTransparenceItem* pFillTranspItem = + rSet.GetItem<XFillTransparenceItem>(XATTR_FILLTRANSPARENCE); + + if (!pFillTranspItem) + return; + + XGradient aTmpGradient = pFillFloatTranspItem->GetGradientValue(); + sal_uInt16 nTranspPercent = pFillTranspItem->GetValue(); + // Encode transparency percentage as intensity + sal_uInt16 nIntensity = 100 - std::min<sal_uInt16> + (std::max<sal_uInt16>(nTranspPercent, 0), 100); + aTmpGradient.SetStartIntens(nIntensity); + aTmpGradient.SetEndIntens(nIntensity); + pFillFloatTranspItem->SetGradientValue(aTmpGradient); +} + +void SwDrawShell::GetDrawAttrState(SfxItemSet& rSet) +{ + SdrView* pSdrView = GetShell().GetDrawView(); + + if (pSdrView->AreObjectsMarked()) + { + bool bDisable = Disable( rSet ); + + if( !bDisable ) + { + pSdrView->GetAttributes( rSet ); + if (comphelper::LibreOfficeKit::isActive()) + lcl_unifyFillTransparencyItems(rSet); + } + } + else + rSet.Put(pSdrView->GetDefaultAttr()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drawsh.cxx b/sw/source/uibase/shells/drawsh.cxx new file mode 100644 index 000000000..958a38b2a --- /dev/null +++ b/sw/source/uibase/shells/drawsh.cxx @@ -0,0 +1,603 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/svdview.hxx> +#include <svx/svdotext.hxx> +#include <svl/whiter.hxx> +#include <svx/fontwork.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <uitool.hxx> +#include <dcontact.hxx> +#include <textboxhelper.hxx> +#include <wview.hxx> +#include <swmodule.hxx> +#include <doc.hxx> +#include <docsh.hxx> + +#include <svx/svdoashp.hxx> +#include <svx/xfillit0.hxx> +#include <vcl/EnumContext.hxx> +#include <svx/svdoole2.hxx> +#include <sfx2/opengrf.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdundo.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/sdasitm.hxx> + +#include <swundo.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <drwbassh.hxx> +#include <drawsh.hxx> + +#define ShellClass_SwDrawShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +SFX_IMPL_INTERFACE(SwDrawShell, SwDrawBaseShell) + +void SwDrawShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("draw"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Draw_Toolbox_Sw); + + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); +} + + +// #i123922# check as the name implies +SdrObject* SwDrawShell::IsSingleFillableNonOLESelected() +{ + SwWrtShell &rSh = GetShell(); + SdrView* pSdrView = rSh.GetDrawView(); + + if(!pSdrView) + { + return nullptr; + } + + if(1 != pSdrView->GetMarkedObjectCount()) + { + return nullptr; + } + + SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0); + + if(!pPickObj) + { + return nullptr; + } + + if(!pPickObj->IsClosedObj()) + { + return nullptr; + } + + if(dynamic_cast< SdrOle2Obj* >(pPickObj)) + { + return nullptr; + } + + return pPickObj; +} + +// #i123922# insert given graphic data dependent of the object type in focus +void SwDrawShell::InsertPictureFromFile(SdrObject& rObject) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pSdrView = rSh.GetDrawView(); + + if(pSdrView) + { + SvxOpenGraphicDialog aDlg(SwResId(STR_INSERT_GRAPHIC), GetView().GetFrameWeld()); + + if (ERRCODE_NONE == aDlg.Execute()) + { + Graphic aGraphic; + ErrCode nError = aDlg.GetGraphic(aGraphic); + + if(ERRCODE_NONE == nError) + { + const bool bAsLink(aDlg.IsAsLink()); + SdrObject* pResult = &rObject; + + rSh.StartUndo(SwUndoId::PASTE_CLIPBOARD); + + if (SdrGrafObj* pSdrGrafObj = dynamic_cast<SdrGrafObj*>(&rObject)) + { + SdrGrafObj* pNewGrafObj(pSdrGrafObj->CloneSdrObject(pSdrGrafObj->getSdrModelFromSdrObject())); + + pNewGrafObj->SetGraphic(aGraphic); + + // #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 + pSdrView->ReplaceObjectAtView(&rObject, *pSdrView->GetSdrPageView(), pNewGrafObj); + + OUString aReferer; + SwDocShell *pDocShell = rSh.GetDoc()->GetDocShell(); + if (pDocShell->HasName()) { + aReferer = pDocShell->GetMedium()->GetName(); + } + + // set in all cases - the Clone() will have copied an existing link (!) + pNewGrafObj->SetGraphicLink( + bAsLink ? aDlg.GetPath() : OUString(), + aReferer, + bAsLink ? aDlg.GetDetectedFilter() : OUString()); + + pResult = pNewGrafObj; + } + else // if(rObject.IsClosedObj() && !dynamic_cast< SdrOle2Obj* >(&rObject)) + { + pSdrView->AddUndo(std::make_unique<SdrUndoAttrObj>(rObject)); + + SfxItemSet aSet(pSdrView->GetModel()->GetItemPool(), svl::Items<XATTR_FILLSTYLE, XATTR_FILLBITMAP>{}); + + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(OUString(), aGraphic)); + rObject.SetMergedItemSetAndBroadcast(aSet); + } + + rSh.EndUndo( SwUndoId::END ); + + if(pResult) + { + // we are done; mark the modified/new object + pSdrView->MarkObj(pResult, pSdrView->GetSdrPageView()); + } + } + } + } +} + +void SwDrawShell::Execute(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + SdrView *pSdrView = rSh.GetDrawView(); + const SfxItemSet *pArgs = rReq.GetArgs(); + SfxBindings &rBnd = GetView().GetViewFrame()->GetBindings(); + sal_uInt16 nSlotId = rReq.GetSlot(); + bool bChanged = pSdrView->GetModel()->IsChanged(); + + pSdrView->GetModel()->SetChanged(false); + + const SfxPoolItem* pItem; + if(pArgs) + pArgs->GetItemState(nSlotId, false, &pItem); + + bool bMirror = true; + + switch (nSlotId) + { + case SID_OBJECT_ROTATE: + if (rSh.IsObjSelected() && pSdrView->IsRotateAllowed()) + { + if (GetView().IsDrawRotate()) + rSh.SetDragMode(SdrDragMode::Move); + else + rSh.SetDragMode(SdrDragMode::Rotate); + + GetView().FlipDrawRotate(); + } + break; + + case SID_BEZIER_EDIT: + if (GetView().IsDrawRotate()) + { + rSh.SetDragMode(SdrDragMode::Move); + GetView().FlipDrawRotate(); + } + GetView().FlipDrawSelMode(); + pSdrView->SetFrameDragSingles(GetView().IsDrawSelMode()); + GetView().AttrChangedNotify(nullptr); // Shell switch + break; + + case SID_OBJECT_HELL: + if (rSh.IsObjSelected()) + { + rSh.StartUndo( SwUndoId::START ); + SetWrapMode(FN_FRAME_WRAPTHRU_TRANSP); + rSh.SelectionToHell(); + rSh.EndUndo( SwUndoId::END ); + rBnd.Invalidate(SID_OBJECT_HEAVEN); + } + break; + + case SID_OBJECT_HEAVEN: + if (rSh.IsObjSelected()) + { + rSh.StartUndo( SwUndoId::START ); + SetWrapMode(FN_FRAME_WRAPTHRU); + rSh.SelectionToHeaven(); + rSh.EndUndo( SwUndoId::END ); + rBnd.Invalidate(SID_OBJECT_HELL); + } + break; + + case FN_TOOL_HIERARCHIE: + if (rSh.IsObjSelected()) + { + rSh.StartUndo( SwUndoId::START ); + if (rSh.GetLayerId() == SdrLayerID(0)) + { + SetWrapMode(FN_FRAME_WRAPTHRU); + rSh.SelectionToHeaven(); + } + else + { + SetWrapMode(FN_FRAME_WRAPTHRU_TRANSP); + rSh.SelectionToHell(); + } + rSh.EndUndo( SwUndoId::END ); + rBnd.Invalidate( SID_OBJECT_HELL ); + rBnd.Invalidate( SID_OBJECT_HEAVEN ); + } + break; + + case SID_FLIP_VERTICAL: + bMirror = false; + [[fallthrough]]; + case SID_FLIP_HORIZONTAL: + rSh.MirrorSelection( bMirror ); + break; + + case SID_FONTWORK: + { + FieldUnit eMetric = ::GetDfltMetric( dynamic_cast<SwWebView*>( &rSh.GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if (pArgs) + { + pVFrame->SetChildWindow(SvxFontWorkChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>((pArgs->Get(SID_FONTWORK))).GetValue()); + } + else + pVFrame->ToggleChildWindow( SvxFontWorkChildWindow::GetChildWindowId() ); + pVFrame->GetBindings().Invalidate(SID_FONTWORK); + } + break; + case FN_FORMAT_FOOTNOTE_DLG: + { + GetView().ExecFormatFootnote(); + break; + } + case FN_NUMBERING_OUTLINE_DLG: + { + GetView().ExecNumberingOutline(GetPool()); + rReq.Done(); + } + break; + case SID_OPEN_XML_FILTERSETTINGS: + { + HandleOpenXmlFilterSettings(rReq); + } + break; + case FN_WORDCOUNT_DIALOG: + { + GetView().UpdateWordCount(this, nSlotId); + } + break; + case SID_EXTRUSION_TOGGLE: + case SID_EXTRUSION_TILT_DOWN: + case SID_EXTRUSION_TILT_UP: + case SID_EXTRUSION_TILT_LEFT: + case SID_EXTRUSION_TILT_RIGHT: + case SID_EXTRUSION_3D_COLOR: + case SID_EXTRUSION_DEPTH: + case SID_EXTRUSION_DIRECTION: + case SID_EXTRUSION_PROJECTION: + case SID_EXTRUSION_LIGHTING_DIRECTION: + case SID_EXTRUSION_LIGHTING_INTENSITY: + case SID_EXTRUSION_SURFACE: + case SID_EXTRUSION_DEPTH_FLOATER: + case SID_EXTRUSION_DIRECTION_FLOATER: + case SID_EXTRUSION_LIGHTING_FLOATER: + case SID_EXTRUSION_SURFACE_FLOATER: + case SID_EXTRUSION_DEPTH_DIALOG: + svx::ExtrusionBar::execute( pSdrView, rReq, rBnd ); + rReq.Ignore (); + break; + + case SID_FONTWORK_SHAPE: + case SID_FONTWORK_SHAPE_TYPE: + case SID_FONTWORK_ALIGNMENT: + case SID_FONTWORK_SAME_LETTER_HEIGHTS: + case SID_FONTWORK_CHARACTER_SPACING: + case SID_FONTWORK_KERN_CHARACTER_PAIRS: + case SID_FONTWORK_CHARACTER_SPACING_FLOATER: + case SID_FONTWORK_ALIGNMENT_FLOATER: + case SID_FONTWORK_CHARACTER_SPACING_DIALOG: + svx::FontworkBar::execute(*pSdrView, rReq, rBnd); + rReq.Ignore (); + break; + + case SID_INSERT_GRAPHIC: + { + // #i123922# check if we can do something + SdrObject* pObj = IsSingleFillableNonOLESelected(); + + if(pObj) + { + // ...and if yes, do something + InsertPictureFromFile(*pObj); + } + + break; + } + + case FN_ADD_TEXT_BOX: + { + if (SdrObject* pObj = IsSingleFillableNonOLESelected()) + { + SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj); + if (pFrameFormat) + SwTextBoxHelper::create(pFrameFormat); + } + break; + } + case FN_REMOVE_TEXT_BOX: + { + if (SdrObject* pObj = IsSingleFillableNonOLESelected()) + { + SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj); + if (pFrameFormat) + SwTextBoxHelper::destroy(pFrameFormat); + } + break; + } + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } + if (pSdrView->GetModel()->IsChanged()) + rSh.SetModified(); + else if (bChanged) + pSdrView->GetModel()->SetChanged(); +} + +void SwDrawShell::GetState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + bool bProtected = rSh.IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE; + + if (!bProtected) // Check the parent + bProtected |= rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + while( nWhich ) + { + switch( nWhich ) + { + case SID_OBJECT_HELL: + if ( !rSh.IsObjSelected() || rSh.GetLayerId() == SdrLayerID(0) || bProtected ) + rSet.DisableItem( nWhich ); + break; + + case SID_OBJECT_HEAVEN: + if ( !rSh.IsObjSelected() || rSh.GetLayerId() == SdrLayerID(1) || bProtected ) + rSet.DisableItem( nWhich ); + break; + + case FN_TOOL_HIERARCHIE: + if ( !rSh.IsObjSelected() || bProtected ) + rSet.DisableItem( nWhich ); + break; + + case SID_OBJECT_ROTATE: + { + const bool bIsRotate = GetView().IsDrawRotate(); + if ( (!bIsRotate && !pSdrView->IsRotateAllowed()) || bProtected ) + rSet.DisableItem( nWhich ); + else + rSet.Put( SfxBoolItem( nWhich, bIsRotate ) ); + } + break; + + case SID_BEZIER_EDIT: + if (!Disable(rSet, nWhich)) + rSet.Put( SfxBoolItem( nWhich, !GetView().IsDrawSelMode())); + break; + + case SID_FLIP_VERTICAL: + if ( !pSdrView->IsMirrorAllowed() || bProtected ) + { + rSet.DisableItem( nWhich ); + } + else + { + // TTTT - needs to be adapted in aw080: + // state is not kept for drawing objects --> provide not flipped state + rSet.Put( SfxBoolItem( nWhich, false ) ); + } + break; + + case SID_FLIP_HORIZONTAL: + if ( !pSdrView->IsMirrorAllowed() || bProtected ) + { + rSet.DisableItem( nWhich ); + } + else + { + // TTTT - needs to be adapted in aw080: + // state is not kept for drawing objects --> provide not flipped state + rSet.Put( SfxBoolItem( nWhich, false ) ); + } + break; + + case SID_FONTWORK: + { + if (bProtected) + rSet.DisableItem( nWhich ); + else + { + const sal_uInt16 nId = SvxFontWorkChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem( nWhich , GetView().GetViewFrame()->HasChildWindow(nId))); + } + } + break; + + case SID_INSERT_GRAPHIC: + { + // #i123922# check if we can do something + SdrObject* pObj = IsSingleFillableNonOLESelected(); + + if(!pObj) + { + rSet.DisableItem(nWhich); + } + + break; + } + case FN_ADD_TEXT_BOX: + { + bool bDisable = true; + if (SdrObject* pObj = IsSingleFillableNonOLESelected()) + { + SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj); + // Allow creating a TextBox only in case this is a draw format without a TextBox so far. + if (pFrameFormat && pFrameFormat->Which() == RES_DRAWFRMFMT && !SwTextBoxHelper::isTextBox(pFrameFormat, RES_DRAWFRMFMT)) + { + if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pObj) ) + { + const SdrCustomShapeGeometryItem& rGeometryItem = pCustomShape->GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY); + if (const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type")) + // But still disallow fontwork shapes. + bDisable = pAny->get<OUString>().startsWith("fontwork-"); + } + } + } + + if (bDisable) + rSet.DisableItem(nWhich); + break; + } + case FN_REMOVE_TEXT_BOX: + { + bool bDisable = true; + if (SdrObject* pObj = IsSingleFillableNonOLESelected()) + { + SwFrameFormat* pFrameFormat = ::FindFrameFormat(pObj); + // Allow removing a TextBox only in case it has one. + if (pFrameFormat && SwTextBoxHelper::isTextBox(pFrameFormat, RES_DRAWFRMFMT)) + bDisable = false; + } + + if (bDisable) + rSet.DisableItem(nWhich); + break; + } + } + nWhich = aIter.NextWhich(); + } + svx::ExtrusionBar::getState( pSdrView, rSet ); + svx::FontworkBar::getState( pSdrView, rSet ); +} + +SwDrawShell::SwDrawShell(SwView &_rView) : + SwDrawBaseShell(_rView) +{ + SetName("Draw"); + + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Draw)); +} + +// Edit SfxRequests for FontWork + +void SwDrawShell::ExecFormText(SfxRequest const & rReq) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pDrView = rSh.GetDrawView(); + bool bChanged = pDrView->GetModel()->IsChanged(); + pDrView->GetModel()->SetChanged(false); + + const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() ) + { + const SfxItemSet& rSet = *rReq.GetArgs(); + + if ( pDrView->IsTextEdit() ) + { + pDrView->SdrEndTextEdit( true ); + GetView().AttrChangedNotify(nullptr); + } + + pDrView->SetAttributes(rSet); + } + if (pDrView->GetModel()->IsChanged()) + rSh.SetModified(); + else + if (bChanged) + pDrView->GetModel()->SetChanged(); +} + +//Return status values for FontWork + +void SwDrawShell::GetFormTextState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pDrView = rSh.GetDrawView(); + const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(pObj); + const bool bDeactivate( + !pObj || + !pTextObj || + !pTextObj->HasText() || + dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes + + if(bDeactivate) + { + rSet.DisableItem(XATTR_FORMTXTSTYLE); + rSet.DisableItem(XATTR_FORMTXTADJUST); + rSet.DisableItem(XATTR_FORMTXTDISTANCE); + rSet.DisableItem(XATTR_FORMTXTSTART); + rSet.DisableItem(XATTR_FORMTXTMIRROR); + rSet.DisableItem(XATTR_FORMTXTHIDEFORM); + rSet.DisableItem(XATTR_FORMTXTOUTLINE); + rSet.DisableItem(XATTR_FORMTXTSHADOW); + rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR); + rSet.DisableItem(XATTR_FORMTXTSHDWXVAL); + rSet.DisableItem(XATTR_FORMTXTSHDWYVAL); + } + else + { + pDrView->GetAttributes( rSet ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drformsh.cxx b/sw/source/uibase/shells/drformsh.cxx new file mode 100644 index 000000000..2e2f5fd52 --- /dev/null +++ b/sw/source/uibase/shells/drformsh.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 <svx/hlnkitem.hxx> +#include <svx/svdview.hxx> +#include <svl/whiter.hxx> +#include <sfx2/request.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/EnumContext.hxx> +#include <svx/svdouno.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <sfx2/htmlmode.hxx> +#include <tools/urlobj.hxx> + +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <drwbassh.hxx> +#include <drformsh.hxx> +#include <svl/urihelper.hxx> +#include <view.hxx> +#include <sfx2/docfile.hxx> +#include <docsh.hxx> + +#define ShellClass_SwDrawFormShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +using namespace ::com::sun::star; + +SFX_IMPL_INTERFACE(SwDrawFormShell, SwDrawBaseShell) + +void SwDrawFormShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("form"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Text_Toolbox_Sw); +} + + +void SwDrawFormShell::Execute(SfxRequest const &rReq) +{ + SwWrtShell &rSh = GetShell(); + const SfxPoolItem* pItem = nullptr; + const SfxItemSet *pArgs = rReq.GetArgs(); + + switch ( rReq.GetSlot() ) + { + case SID_HYPERLINK_SETLINK: + { + if(pArgs) + pArgs->GetItemState(SID_HYPERLINK_SETLINK, false, &pItem); + if(pItem) + { + SdrView *pSdrView = rSh.GetDrawView(); + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + bool bConvertToText = rHLinkItem.GetInsertMode() == HLINK_DEFAULT || + rHLinkItem.GetInsertMode() == HLINK_FIELD; + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (rMarkList.GetMark(0)) + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + if(bConvertToText) + { + //remove object -> results in destruction of this! + SwView& rTempView = GetView(); + rTempView.GetViewFrame()->GetDispatcher()->Execute(SID_DELETE, SfxCallMode::SYNCHRON ); + rTempView.StopShellTimer(); + //issue a new command to insert the link + rTempView.GetViewFrame()->GetDispatcher()->ExecuteList( + SID_HYPERLINK_SETLINK, SfxCallMode::ASYNCHRON, + { &rHLinkItem }); + } + else + { + const uno::Reference< awt::XControlModel >& xControlModel = pUnoCtrl->GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" ); + if( !xControlModel.is() ) + return; + + uno::Reference< beans::XPropertySet > xPropSet(xControlModel, uno::UNO_QUERY); + + // Can we set a URL to the object? + OUString sTargetURL( "TargetURL" ); + uno::Reference< beans::XPropertySetInfo > xPropInfoSet = xPropSet->getPropertySetInfo(); + if( xPropInfoSet->hasPropertyByName( sTargetURL )) + { + beans::Property aProp = xPropInfoSet->getPropertyByName( sTargetURL ); + if( !aProp.Name.isEmpty() ) + { + uno::Any aTmp; + // Yes! + OUString sLabel("Label"); + if( xPropInfoSet->hasPropertyByName(sLabel) ) + { + aTmp <<= rHLinkItem.GetName(); + xPropSet->setPropertyValue(sLabel, aTmp ); + } + + SfxMedium* pMedium = GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + aTmp <<= URIHelper::SmartRel2Abs(aAbs, rHLinkItem.GetURL()); + xPropSet->setPropertyValue( sTargetURL, aTmp ); + + if( !rHLinkItem.GetTargetFrame().isEmpty() ) + { + aTmp <<= rHLinkItem.GetTargetFrame(); + xPropSet->setPropertyValue( "TargetFrame", aTmp ); + } + + aTmp <<= form::FormButtonType_URL; + xPropSet->setPropertyValue( "ButtonType", aTmp ); + } + } + } + } + } + } + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +void SwDrawFormShell::GetState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while( nWhich ) + { + switch( nWhich ) + { + case SID_HYPERLINK_GETLINK: + { + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SvxHyperlinkItem aHLinkItem; + if (rMarkList.GetMark(0)) + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference< awt::XControlModel >& xControlModel = pUnoCtrl->GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" ); + if( !xControlModel.is() ) + return; + + uno::Reference< beans::XPropertySet > xPropSet(xControlModel, uno::UNO_QUERY); + + uno::Any aTmp; + uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo(); + if(xInfo->hasPropertyByName( "ButtonType" )) + { + form::FormButtonType eButtonType = form::FormButtonType_URL; + aTmp = xPropSet->getPropertyValue( "ButtonType" ); + if( aTmp >>= eButtonType ) + { + // Label + if(xInfo->hasPropertyByName( "Label" )) + { + aTmp = xPropSet->getPropertyValue( "Label" ); + OUString sTmp; + if( (aTmp >>= sTmp) && !sTmp.isEmpty()) + { + aHLinkItem.SetName(sTmp); + } + } + + // URL + if(xInfo->hasPropertyByName( "TargetURL" )) + { + aTmp = xPropSet->getPropertyValue( "TargetURL" ); + OUString sTmp; + if( (aTmp >>= sTmp) && !sTmp.isEmpty()) + { + aHLinkItem.SetURL(sTmp); + } + } + + // Target + if(xInfo->hasPropertyByName( "TargetFrame" )) + { + aTmp = xPropSet->getPropertyValue( "TargetFrame" ); + OUString sTmp; + if( (aTmp >>= sTmp) && !sTmp.isEmpty()) + { + aHLinkItem.SetTargetFrame(sTmp); + } + } + aHLinkItem.SetInsertMode(HLINK_BUTTON); + } + } + } + } + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + aHLinkItem.SetInsertMode(static_cast<SvxLinkInsertMode>(aHLinkItem.GetInsertMode() | + ((nHtmlMode & HTMLMODE_ON) != 0 ? HLINK_HTMLMODE : 0))); + + rSet.Put(aHLinkItem); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +SwDrawFormShell::SwDrawFormShell(SwView &_rView) : + SwDrawBaseShell(_rView) +{ + GetShell().NoEdit(); + SetName("DrawForm"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Form)); +} + +SwDrawFormShell::~SwDrawFormShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drwbassh.cxx b/sw/source/uibase/shells/drwbassh.cxx new file mode 100644 index 000000000..c707b8037 --- /dev/null +++ b/sw/source/uibase/shells/drwbassh.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 <swtypes.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdview.hxx> +#include <svl/whiter.hxx> +#include <svx/swframevalidation.hxx> +#include <svx/anchorid.hxx> +#include <drawdoc.hxx> +#include <uitool.hxx> +#include <fmtornt.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> +#include <edtwin.hxx> +#include <viewopt.hxx> +#include <dcontact.hxx> +#include <frmfmt.hxx> +#include <drawbase.hxx> +#include <drwbassh.hxx> +#include <swdtflvr.hxx> +#include <svx/svditer.hxx> +#define ShellClass_SwDrawBaseShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <svx/svxdlg.hxx> +#include <swabstdlg.hxx> +#include <swundo.hxx> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <IDocumentDrawModelAccess.hxx> +#include <fmtfollowtextflow.hxx> +#include <textboxhelper.hxx> + +using namespace ::com::sun::star; + +SFX_IMPL_SUPERCLASS_INTERFACE(SwDrawBaseShell, SwBaseShell) + +void SwDrawBaseShell::InitInterface_Impl() +{ +} + + +SwDrawBaseShell::SwDrawBaseShell(SwView &_rView) + : SwBaseShell(_rView) +{ + GetShell().NoEdit(); + + SwEditWin& rWin = GetView().GetEditWin(); + + rWin.SetBezierMode(SID_BEZIER_MOVE); + + if ( !_rView.GetDrawFuncPtr() ) + _rView.GetEditWin().StdDrawMode( OBJ_NONE, true ); + + SwTransferable::CreateSelection( GetShell() ); +} + +SwDrawBaseShell::~SwDrawBaseShell() +{ + GetView().ExitDraw(); + GetShell().Edit(); + SwTransferable::ClearSelection( GetShell() ); +} + +void SwDrawBaseShell::Execute(SfxRequest const &rReq) +{ + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + const SfxItemSet *pArgs = rReq.GetArgs(); + sal_uInt16 nSlotId = rReq.GetSlot(); + bool bChanged = pSdrView->GetModel()->IsChanged(); + pSdrView->GetModel()->SetChanged(false); + const SfxPoolItem* pItem = nullptr; + if(pArgs) + pArgs->GetItemState(nSlotId, false, &pItem); + + bool bAlignPossible = pSh->IsAlignPossible(); + + bool bTopParam = true, bBottomParam = true; + bool bDone = false; + SfxBindings& rBind = GetView().GetViewFrame()->GetBindings(); + + switch (nSlotId) + { + case FN_DRAW_WRAP_DLG: + { + if(pSdrView->AreObjectsMarked()) + { + if(!pArgs) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if( rMarkList.GetMark(0) != nullptr ) + { + SfxItemSet aSet( + GetPool(), + svl::Items< + RES_LR_SPACE, RES_UL_SPACE, + RES_SURROUND, RES_SURROUND, + RES_ANCHOR, RES_ANCHOR, + RES_WRAP_INFLUENCE_ON_OBJPOS, RES_WRAP_INFLUENCE_ON_OBJPOS, + SID_HTML_MODE, SID_HTML_MODE, + FN_DRAW_WRAP_DLG, FN_DRAW_WRAP_DLG>{}); + + aSet.Put(SfxBoolItem(SID_HTML_MODE, + 0 != ::GetHtmlMode(pSh->GetView().GetDocShell()))); + + aSet.Put(SfxInt16Item(FN_DRAW_WRAP_DLG, sal_uInt8(pSh->GetLayerId()))); + + pSh->GetObjAttr(aSet); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSwWrapDlg(GetView().GetFrameWeld(), aSet, pSh)); + + if (pDlg->Execute() == RET_OK) + { + const SfxPoolItem* pWrapItem; + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + if(SfxItemState::SET == pOutSet->GetItemState(FN_DRAW_WRAP_DLG, false, &pWrapItem)) + { + short nLayer = static_cast<const SfxInt16Item*>(pWrapItem)->GetValue(); + if (nLayer == 1) + pSh->SelectionToHeaven(); + else + pSh->SelectionToHell(); + } + + pSh->SetObjAttr(*pOutSet); + } + } + } + } + } + break; + + case SID_ATTR_TRANSFORM: + { + if(pSdrView->AreObjectsMarked()) + { + if(!pArgs) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if( rMarkList.GetMark(0) != nullptr ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + VclPtr<SfxAbstractTabDialog> pDlg; + bool bCaption = false; + + // Allowed anchorages: + RndStdIds nAnchor = pSh->GetAnchorId(); + SvxAnchorIds nAllowedAnchors = SvxAnchorIds::Paragraph | SvxAnchorIds::Character | SvxAnchorIds::Page; + sal_uInt16 nHtmlMode = ::GetHtmlMode(pSh->GetView().GetDocShell()); + + if ( pSh->IsFlyInFly() ) + nAllowedAnchors |= SvxAnchorIds::Fly; + + if (pObj->GetObjIdentifier() == OBJ_CAPTION ) + bCaption = true; + + if (bCaption) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<AbstractSvxCaptionDialog> pCaptionDlg = + pFact->CreateCaptionDialog( rReq.GetFrameWeld(), pSdrView, nAllowedAnchors ); + pDlg.reset(pCaptionDlg); + pCaptionDlg->SetValidateFramePosLink( LINK(this, SwDrawBaseShell, ValidatePosition) ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<AbstractSvxTransformTabDialog> pTransform = + pFact->CreateSvxTransformTabDialog(rReq.GetFrameWeld(), nullptr, pSdrView, nAllowedAnchors); + pDlg.reset(pTransform); + pTransform->SetValidateFramePosLink( LINK(this, SwDrawBaseShell, ValidatePosition) ); + } + SfxItemSet aNewAttr(pSdrView->GetGeoAttrFromMarked()); + + const sal_uInt16* pRange = pDlg->GetInputRanges( *aNewAttr.GetPool() ); + SfxItemSet aSet( *aNewAttr.GetPool(), pRange ); + FieldUnit eMetric = ::GetDfltMetric( dynamic_cast<SwWebView*>(&GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + + aSet.Put( aNewAttr, false ); + + if (bCaption) + pSdrView->GetAttributes( aSet ); + + aSet.Put(SfxInt16Item(SID_ATTR_TRANSFORM_ANCHOR, static_cast<sal_Int16>(nAnchor))); + bool bRTL; + bool bVertL2R; + aSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_IN_VERTICAL_TEXT, pSh->IsFrameVertical(true, bRTL, bVertL2R))); + aSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_IN_RTL_TEXT, bRTL)); + + SwFrameFormat* pFrameFormat = FindFrameFormat( pObj ); + + aSet.Put( pFrameFormat->GetFormatAttr(RES_FOLLOW_TEXT_FLOW) ); + + SwFormatVertOrient aVOrient(pFrameFormat->GetFormatAttr(RES_VERT_ORIENT)); + aSet.Put(SfxInt16Item(SID_ATTR_TRANSFORM_VERT_ORIENT, aVOrient.GetVertOrient())); + aSet.Put(SfxInt16Item(SID_ATTR_TRANSFORM_VERT_RELATION, aVOrient.GetRelationOrient() )); + aSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_VERT_POSITION, aVOrient.GetPos())); + + SwFormatHoriOrient aHOrient(pFrameFormat->GetFormatAttr(RES_HORI_ORIENT)); + aSet.Put(SfxInt16Item(SID_ATTR_TRANSFORM_HORI_ORIENT, aHOrient.GetHoriOrient())); + aSet.Put(SfxInt16Item(SID_ATTR_TRANSFORM_HORI_RELATION, aHOrient.GetRelationOrient() )); + aSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_HORI_MIRROR, aHOrient.IsPosToggle())); + aSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_HORI_POSITION, aHOrient.GetPos())); + + aSet.Put(SfxUInt16Item(SID_HTML_MODE, nHtmlMode)); + + pDlg->SetInputSet( &aSet ); + + pDlg->StartExecuteAsync([bCaption, bChanged, pDlg, pFrameFormat, pSdrView, + pSh, &rMarkList, this]( + sal_Int32 nResult){ + if (nResult == RET_OK) + { + SwFormatVertOrient aVOrientFinal(pFrameFormat->GetFormatAttr(RES_VERT_ORIENT)); + SwFormatHoriOrient aHOrientFinal(pFrameFormat->GetFormatAttr(RES_HORI_ORIENT)); + + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + pSh->StartAllAction(); + + // #i30451# + pSh->StartUndo(SwUndoId::INSFMTATTR); + + pSdrView->SetGeoAttrToMarked(*pOutSet); + + if (bCaption) + pSdrView->SetAttributes(*pOutSet); + + bool bPosCorr = + SfxItemState::SET != pOutSet->GetItemState( + SID_ATTR_TRANSFORM_POS_X, false ) && + SfxItemState::SET != pOutSet->GetItemState( + SID_ATTR_TRANSFORM_POS_Y, false ); + + SfxItemSet aFrameAttrSet(GetPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{}); + + bool bSingleSelection = rMarkList.GetMarkCount() == 1; + + const SfxPoolItem* pAnchorItem; + if(SfxItemState::SET == pOutSet->GetItemState( + SID_ATTR_TRANSFORM_ANCHOR, false, &pAnchorItem)) + { + if(!bSingleSelection) + pSh->ChgAnchor(static_cast<RndStdIds>(static_cast<const SfxInt16Item*>(pAnchorItem) + ->GetValue()), false, bPosCorr ); + else + { + SwFormatAnchor aAnchor(pFrameFormat->GetAnchor()); + aAnchor.SetType(static_cast<RndStdIds>(static_cast<const SfxInt16Item*>(pAnchorItem)->GetValue())); + aFrameAttrSet.Put( aAnchor ); + } + } + const SfxPoolItem* pHoriOrient = nullptr; + const SfxPoolItem* pHoriRelation = nullptr; + const SfxPoolItem* pHoriPosition = nullptr; + const SfxPoolItem* pHoriMirror = nullptr; + pOutSet->GetItemState(SID_ATTR_TRANSFORM_HORI_ORIENT, false, &pHoriOrient); + pOutSet->GetItemState(SID_ATTR_TRANSFORM_HORI_RELATION, false, &pHoriRelation); + pOutSet->GetItemState(SID_ATTR_TRANSFORM_HORI_POSITION, false, &pHoriPosition); + pOutSet->GetItemState(SID_ATTR_TRANSFORM_HORI_MIRROR, false, &pHoriMirror); + if(pHoriOrient || pHoriRelation || pHoriPosition || pHoriMirror) + { + if(pHoriOrient) + aHOrientFinal.SetHoriOrient( + static_cast<const SfxInt16Item*>(pHoriOrient)->GetValue()); + if(pHoriRelation) + aHOrientFinal.SetRelationOrient( + static_cast<const SfxInt16Item*>(pHoriRelation)->GetValue()); + if(pHoriPosition) + aHOrientFinal.SetPos( static_cast<const SfxInt32Item*>(pHoriPosition)->GetValue()); + if(pHoriMirror) + aHOrientFinal.SetPosToggle( static_cast<const SfxBoolItem*>(pHoriMirror)->GetValue()); + aFrameAttrSet.Put(aHOrientFinal); + } + + const SfxPoolItem* pVertOrient = nullptr; + const SfxPoolItem* pVertRelation = nullptr; + const SfxPoolItem* pVertPosition = nullptr; + pOutSet->GetItemState(SID_ATTR_TRANSFORM_VERT_ORIENT, false, &pVertOrient); + pOutSet->GetItemState(SID_ATTR_TRANSFORM_VERT_RELATION, false, &pVertRelation); + pOutSet->GetItemState(SID_ATTR_TRANSFORM_VERT_POSITION, false, &pVertPosition); + if(pVertOrient || pVertRelation || pVertPosition ) + { + if(pVertOrient) + aVOrientFinal.SetVertOrient( + static_cast<const SfxInt16Item*>(pVertOrient)->GetValue()); + if(pVertRelation) + aVOrientFinal.SetRelationOrient( + static_cast<const SfxInt16Item*>(pVertRelation)->GetValue()); + if(pVertPosition) + aVOrientFinal.SetPos( static_cast<const SfxInt32Item*>(pVertPosition)->GetValue()); + aFrameAttrSet.Put( aVOrientFinal ); + } + const SfxPoolItem* pFollowItem = nullptr; + pOutSet->GetItemState(RES_FOLLOW_TEXT_FLOW, false, &pFollowItem); + if(pFollowItem) + aFrameAttrSet.Put(*pFollowItem); + + if(aFrameAttrSet.Count()) + pSh->SetDrawingAttr(aFrameAttrSet); + + GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + + // #i30451# + pSh->EndUndo( SwUndoId::INSFMTATTR ); + + pSh->EndAllAction(); + } + + if (pSdrView->GetModel()->IsChanged()) + pSh->SetModified(); + else if (bChanged) + pSdrView->GetModel()->SetChanged(); + + pDlg->disposeOnce(); + }); + } + } + else + { + pSh->StartAllAction(); + pSdrView->SetGeoAttrToMarked( *pArgs ); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if (pObj) + { + SwFrameFormat* pFrameFormat = FindFrameFormat(pObj); + if (pFrameFormat) + { + const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); + // Don't change shape position / size, just update the anchor doc model + // position. + pSh->ChgAnchor(rAnchor.GetAnchorId(), /*bSameOnly=*/true); + } + } + pSh->EndAllAction(); + } + } + } + break; + + case SID_DELETE: + case FN_BACKSPACE: + if (pSh->IsObjSelected() && !pSdrView->IsTextEdit()) + { + bDone = true; + + if( GetView().IsDrawRotate() ) + { + pSh->SetDragMode( SdrDragMode::Move ); + GetView().FlipDrawRotate(); + } + + pSh->SetModified(); + pSh->DelSelectedObj(); + + if (rReq.IsAPI() || + GetView().GetEditWin().IsObjectSelect() ) + { + // If basic call, then back to the text shell, because the + // Basic otherwise has no possibility to return. + if (GetView().GetDrawFuncPtr()) + { + GetView().GetDrawFuncPtr()->Deactivate(); + GetView().SetDrawFuncPtr(nullptr); + } + GetView().LeaveDrawCreate(); // Switch to selection mode + } + + if (pSh->IsSelFrameMode()) + { + pSh->LeaveSelFrameMode(); + // #105852# FME + } + } + break; + + case SID_GROUP: + if (pSh->IsObjSelected() > 1 && pSh->IsGroupAllowed()) + { + pSh->GroupSelection(); + rBind.Invalidate(SID_UNGROUP); + } + break; + + case SID_UNGROUP: + if (pSh->IsGroupSelected() && pSh->IsUnGroupAllowed()) + { + pSh->UnGroupSelection(); + rBind.Invalidate(SID_GROUP); + } + break; + + case SID_ENTER_GROUP: + if (pSh->IsGroupSelected()) + { + pSdrView->EnterMarkedGroup(); + rBind.InvalidateAll(false); + } + break; + + case SID_LEAVE_GROUP: + if (pSdrView->IsGroupEntered()) + { + pSdrView->LeaveOneGroup(); + rBind.Invalidate(SID_ENTER_GROUP); + rBind.Invalidate(SID_UNGROUP); + } + break; + + case SID_OBJECT_ALIGN_LEFT: + case SID_OBJECT_ALIGN_CENTER: + case SID_OBJECT_ALIGN_RIGHT: + case SID_OBJECT_ALIGN_UP: + case SID_OBJECT_ALIGN_MIDDLE: + case SID_OBJECT_ALIGN_DOWN: + { + if ( bAlignPossible ) + { + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if ( rMarkList.GetMarkCount() == 1 ) + { + sal_Int16 nHorizOrient = -1, nVertOrient = -1; + + switch (nSlotId) + { + case SID_OBJECT_ALIGN_LEFT: + nHorizOrient = text::HoriOrientation::LEFT; + break; + case SID_OBJECT_ALIGN_CENTER: + nHorizOrient = text::HoriOrientation::CENTER; + break; + case SID_OBJECT_ALIGN_RIGHT: + nHorizOrient = text::HoriOrientation::RIGHT; + break; + case SID_OBJECT_ALIGN_UP: + nVertOrient = text::VertOrientation::TOP; + break; + case SID_OBJECT_ALIGN_MIDDLE: + nVertOrient = text::VertOrientation::CENTER; + break; + case SID_OBJECT_ALIGN_DOWN: + nVertOrient = text::VertOrientation::BOTTOM; + break; + default: + break; + } + + if (nHorizOrient != -1) + { + pSh->StartAction(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SwFrameFormat* pFrameFormat = FindFrameFormat( pObj ); + SwFormatHoriOrient aHOrient(pFrameFormat->GetFormatAttr(RES_HORI_ORIENT)); + aHOrient.SetHoriOrient( nHorizOrient ); + pFrameFormat->SetFormatAttr(aHOrient); + if (auto pTxFrm = SwTextBoxHelper::getOtherTextBoxFormat(pFrameFormat, RES_DRAWFRMFMT)) + pTxFrm->SetFormatAttr(aHOrient); + pSh->EndAction(); + } + + if (nVertOrient != -1) + { + pSh->StartAction(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SwFrameFormat* pFrameFormat = FindFrameFormat( pObj ); + SwFormatVertOrient aVOrient(pFrameFormat->GetFormatAttr(RES_VERT_ORIENT)); + aVOrient.SetVertOrient( nVertOrient ); + pFrameFormat->SetFormatAttr(aVOrient); + if (auto pTxFrm = SwTextBoxHelper::getOtherTextBoxFormat(pFrameFormat, RES_DRAWFRMFMT)) + pTxFrm->SetFormatAttr(aVOrient); + pSh->EndAction(); + } + + break; + } + + pSh->StartAction(); + switch (nSlotId) + { + case SID_OBJECT_ALIGN_LEFT: + pSdrView->AlignMarkedObjects(SdrHorAlign::Left, SdrVertAlign::NONE); + break; + case SID_OBJECT_ALIGN_CENTER: + pSdrView->AlignMarkedObjects(SdrHorAlign::Center, SdrVertAlign::NONE); + break; + case SID_OBJECT_ALIGN_RIGHT: + pSdrView->AlignMarkedObjects(SdrHorAlign::Right, SdrVertAlign::NONE); + break; + case SID_OBJECT_ALIGN_UP: + pSdrView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Top); + break; + case SID_OBJECT_ALIGN_MIDDLE: + pSdrView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Center); + break; + case SID_OBJECT_ALIGN_DOWN: + pSdrView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Bottom); + break; + } + pSh->EndAction(); + } + } + break; + + case FN_FRAME_UP: + bTopParam = false; + [[fallthrough]]; + case SID_FRAME_TO_TOP: + pSh->SelectionToTop( bTopParam ); + break; + + case FN_FRAME_DOWN: + bBottomParam = false; + [[fallthrough]]; + case SID_FRAME_TO_BOTTOM: + pSh->SelectionToBottom( bBottomParam ); + break; + + case FN_NAME_SHAPE: + { + bDone = true; + + if(1 == pSdrView->GetMarkedObjectCount()) + { + // #i68101# + SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aName(pSelected->GetName()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectNameDialog> pDlg(pFact->CreateSvxObjectNameDialog(GetView().GetFrameWeld(), aName)); + + pDlg->SetCheckNameHdl(LINK(this, SwDrawBaseShell, CheckGroupShapeNameHdl)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetName(aName); + pSelected->SetName(aName); + pSh->SetModified(); + } + } + + break; + } + + // #i68101# + case FN_TITLE_DESCRIPTION_SHAPE: + { + bDone = true; + + if(1 == pSdrView->GetMarkedObjectCount()) + { + SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aTitle(pSelected->GetTitle()); + OUString aDescription(pSelected->GetDescription()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg(pFact->CreateSvxObjectTitleDescDialog(GetView().GetFrameWeld(), + aTitle, aDescription)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetTitle(aTitle); + pDlg->GetDescription(aDescription); + + pSelected->SetTitle(aTitle); + pSelected->SetDescription(aDescription); + + pSh->SetModified(); + } + } + + break; + } + + default: + OSL_ENSURE(false, "wrong Dispatcher"); + return; + } + if(!bDone) + { + if(nSlotId >= SID_OBJECT_ALIGN_LEFT && nSlotId <= SID_OBJECT_ALIGN_DOWN) + rBind.Invalidate(SID_ATTR_LONG_LRSPACE); + if (pSdrView->GetModel()->IsChanged()) + pSh->SetModified(); + else if (bChanged) + pSdrView->GetModel()->SetChanged(); + } +} + +// Checks whether a given name is allowed for a group shape + +IMPL_LINK( SwDrawBaseShell, CheckGroupShapeNameHdl, AbstractSvxObjectNameDialog&, rNameDialog, bool ) +{ + SwWrtShell &rSh = GetShell(); + SdrView *pSdrView = rSh.GetDrawView(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + OSL_ENSURE(rMarkList.GetMarkCount() == 1, "wrong draw selection"); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const OUString sCurrentName = pObj->GetName(); + OUString sNewName; + rNameDialog.GetName(sNewName); + bool bRet = false; + if (sNewName.isEmpty() || sCurrentName == sNewName) + bRet = true; + else + { + bRet = true; + SwDrawModel* pModel = rSh.getIDocumentDrawModelAccess().GetDrawModel(); + SdrObjListIter aIter( pModel->GetPage(0), SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pTempObj = aIter.Next(); + if ( pObj != pTempObj && pTempObj->GetName() == sNewName ) + { + bRet = false; + break; + } + } + } + return bRet; +} + +void SwDrawBaseShell::GetState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + bool bProtected = rSh.IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE; + + if (!bProtected) // Look in the parent + bProtected |= rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + while( nWhich ) + { + switch( nWhich ) + { + case FN_DRAW_WRAP_DLG: + case SID_ATTR_TRANSFORM: + case SID_FRAME_TO_TOP: + case SID_FRAME_TO_BOTTOM: + case FN_FRAME_UP: + case FN_FRAME_DOWN: + case SID_DELETE: + case FN_BACKSPACE: + if( bProtected || !rSh.IsObjSelected() ) + rSet.DisableItem( nWhich ); + break; + case SID_GROUP: + if ( rSh.IsObjSelected() < 2 || bProtected || !rSh.IsGroupAllowed() ) + rSet.DisableItem( nWhich ); + break; + case SID_UNGROUP: + if ( !rSh.IsGroupSelected() || bProtected || !rSh.IsUnGroupAllowed() ) + rSet.DisableItem( nWhich ); + break; + case SID_ENTER_GROUP: + if ( !rSh.IsGroupSelected() ) + rSet.DisableItem( nWhich ); + break; + case SID_LEAVE_GROUP: + if ( !pSdrView->IsGroupEntered() ) + rSet.DisableItem( nWhich ); + break; + case SID_OBJECT_ALIGN_LEFT: + case SID_OBJECT_ALIGN_CENTER: + case SID_OBJECT_ALIGN_RIGHT: + case SID_OBJECT_ALIGN_UP: + case SID_OBJECT_ALIGN_MIDDLE: + case SID_OBJECT_ALIGN_DOWN: + case SID_OBJECT_ALIGN: + { + bool bDisableThis = false; + bool bDisableHoriz = false; + bool bHoriz = (nWhich == SID_OBJECT_ALIGN_LEFT || nWhich == SID_OBJECT_ALIGN_CENTER || + nWhich == SID_OBJECT_ALIGN_RIGHT); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if ( !rSh.IsAlignPossible() || bProtected ) + { + bDisableThis = true; + rSet.DisableItem( nWhich ); + } + else if ( rSh.GetAnchorId() == RndStdIds::FLY_AS_CHAR ) + { + //if only one object is selected it can only be vertically + // aligned because it is character bound + if( rMarkList.GetMarkCount() == 1 ) + { + bDisableHoriz = true; + rSet.DisableItem(SID_OBJECT_ALIGN_LEFT); + rSet.DisableItem(SID_OBJECT_ALIGN_CENTER); + rSet.DisableItem(SID_OBJECT_ALIGN_RIGHT); + } + } + + if (bHoriz && !bDisableThis && !bDisableHoriz && + rMarkList.GetMarkCount() == 1) + { + sal_Int16 nHoriOrient = -1; + switch(nWhich) + { + case SID_OBJECT_ALIGN_LEFT: + nHoriOrient = text::HoriOrientation::LEFT; + break; + case SID_OBJECT_ALIGN_CENTER: + nHoriOrient = text::HoriOrientation::CENTER; + break; + case SID_OBJECT_ALIGN_RIGHT: + nHoriOrient = text::HoriOrientation::RIGHT; + break; + default: + break; + } + + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SwFrameFormat* pFrameFormat = FindFrameFormat(pObj); + SwFormatHoriOrient aHOrient(pFrameFormat->GetFormatAttr(RES_HORI_ORIENT)); + rSet.Put(SfxBoolItem(nWhich, aHOrient.GetHoriOrient() == nHoriOrient)); + } + } + break; + + case FN_NAME_SHAPE : + { + if(1 != pSdrView->GetMarkedObjectCount()) + { + rSet.DisableItem( nWhich ); + } + } + break; + + // #i68101# + case FN_TITLE_DESCRIPTION_SHAPE: + { + const bool bIsWebView(nullptr != dynamic_cast<SwWebView*>(&GetView())); + + if(!bIsWebView && 1 != pSdrView->GetMarkedObjectCount()) + { + rSet.DisableItem( nWhich ); + } + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SwDrawBaseShell::GetDrawAttrStateForIFBX( SfxItemSet& rSet ) +{ + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if( rMarkList.GetMark(0) != nullptr ) + { + SfxItemSet aNewAttr(pSdrView->GetGeoAttrFromMarked()); + rSet.Put(aNewAttr,false); + } +} + +bool SwDrawBaseShell::Disable(SfxItemSet& rSet, sal_uInt16 nWhich) +{ + bool bDisable = GetShell().IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE; + + if (bDisable) + { + if (nWhich) + rSet.DisableItem( nWhich ); + else + { + SfxWhichIter aIter( rSet ); + nWhich = aIter.FirstWhich(); + while (nWhich) + { + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } + } + } + + return bDisable; +} + +void SwDrawBaseShell::DisableState( SfxItemSet& rSet ) +{ + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + bool bShowArea = true, bShowMeasure = true; + + for (size_t i = 0; i < nMarkCount && i < 50; ++i) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + sal_uInt16 nObjType = pObj->GetObjIdentifier(); + + if ( nObjType != OBJ_MEASURE ) + bShowMeasure = false; + + // If marked object is 2D, disable format area command. + if ( nObjType == OBJ_PLIN || + nObjType == OBJ_LINE || + nObjType == OBJ_PATHLINE || + nObjType == OBJ_FREELINE || + nObjType == OBJ_EDGE || + nObjType == OBJ_CARC || + bShowMeasure ) + bShowArea = false; + + if (!bShowArea && !bShowMeasure) + break; + } + + if (!bShowArea) + rSet.DisableItem(SID_ATTRIBUTES_AREA); + + if (!bShowMeasure) + rSet.DisableItem(SID_MEASURE_DLG); + + Disable(rSet); + +} + +// Validate of drawing positions + +IMPL_LINK(SwDrawBaseShell, ValidatePosition, SvxSwFrameValidation&, rValidation, void ) +{ + SwWrtShell *pSh = &GetShell(); + rValidation.nMinHeight = MINFLY; + rValidation.nMinWidth = MINFLY; + + SwRect aBoundRect; + + // OD 18.09.2003 #i18732# - adjustment for allowing vertical position + // aligned to page for fly frame anchored to paragraph or to character. + const RndStdIds eAnchorType = rValidation.nAnchorType; + const SwPosition* pContentPos = nullptr; + SdrView* pSdrView = pSh->GetDrawView(); + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SwFrameFormat* pFrameFormat = FindFrameFormat( pObj ); + pContentPos = pFrameFormat->GetAnchor().GetContentAnchor(); + } + + pSh->CalcBoundRect( aBoundRect, eAnchorType, + rValidation.nHRelOrient, + rValidation.nVRelOrient, + pContentPos, + rValidation.bFollowTextFlow, + rValidation.bMirror, nullptr, &rValidation.aPercentSize); + + bool bIsInVertical( false ); + { + bool bRTL; + bool bVertL2R; + bIsInVertical = pSh->IsFrameVertical(true, bRTL, bVertL2R); + } + if(bIsInVertical) + { + Point aPos(aBoundRect.Pos()); + long nTmp = aPos.X(); + aPos.setX( aPos.Y() ); + aPos.setY( nTmp ); + Size aSize(aBoundRect.SSize()); + nTmp = aSize.Width(); + aSize.setWidth( aSize.Height() ); + aSize.setHeight( nTmp ); + aBoundRect.Chg( aPos, aSize ); + //exchange width/height to enable correct values + nTmp = rValidation.nWidth; + rValidation.nWidth = rValidation.nHeight; + rValidation.nHeight = nTmp; + } + if ((eAnchorType == RndStdIds::FLY_AT_PAGE) || (eAnchorType == RndStdIds::FLY_AT_FLY)) + { + // MinimalPosition + rValidation.nMinHPos = aBoundRect.Left(); + rValidation.nMinVPos = aBoundRect.Top(); + SwTwips nH = rValidation.nHPos; + SwTwips nV = rValidation.nVPos; + + if (rValidation.nHPos + rValidation.nWidth > aBoundRect.Right()) + { + if (rValidation.nHoriOrient == text::HoriOrientation::NONE) + { + rValidation.nHPos -= ((rValidation.nHPos + rValidation.nWidth) - aBoundRect.Right()); + nH = rValidation.nHPos; + } + else + rValidation.nWidth = aBoundRect.Right() - rValidation.nHPos; + } + + if (rValidation.nHPos + rValidation.nWidth > aBoundRect.Right()) + rValidation.nWidth = aBoundRect.Right() - rValidation.nHPos; + + if (rValidation.nVPos + rValidation.nHeight > aBoundRect.Bottom()) + { + if (rValidation.nVertOrient == text::VertOrientation::NONE) + { + rValidation.nVPos -= ((rValidation.nVPos + rValidation.nHeight) - aBoundRect.Bottom()); + nV = rValidation.nVPos; + } + else + rValidation.nHeight = aBoundRect.Bottom() - rValidation.nVPos; + } + + if (rValidation.nVPos + rValidation.nHeight > aBoundRect.Bottom()) + rValidation.nHeight = aBoundRect.Bottom() - rValidation.nVPos; + + if ( rValidation.nVertOrient != text::VertOrientation::NONE ) + nV = aBoundRect.Top(); + + if ( rValidation.nHoriOrient != text::HoriOrientation::NONE ) + nH = aBoundRect.Left(); + + rValidation.nMaxHPos = aBoundRect.Right() - rValidation.nWidth; + rValidation.nMaxHeight = aBoundRect.Bottom() - nV; + + rValidation.nMaxVPos = aBoundRect.Bottom() - rValidation.nHeight; + rValidation.nMaxWidth = aBoundRect.Right() - nH; + } + else if ((eAnchorType == RndStdIds::FLY_AT_PARA) || (eAnchorType == RndStdIds::FLY_AT_CHAR)) + { + if (rValidation.nHPos + rValidation.nWidth > aBoundRect.Right()) + { + if (rValidation.nHoriOrient == text::HoriOrientation::NONE) + { + rValidation.nHPos -= ((rValidation.nHPos + rValidation.nWidth) - aBoundRect.Right()); + } + else + rValidation.nWidth = aBoundRect.Right() - rValidation.nHPos; + } + + // OD 29.09.2003 #i17567#, #i18732# - consider following the text flow + // and alignment at page areas. + const bool bMaxVPosAtBottom = !rValidation.bFollowTextFlow || + rValidation.nVRelOrient == text::RelOrientation::PAGE_FRAME || + rValidation.nVRelOrient == text::RelOrientation::PAGE_PRINT_AREA || + rValidation.nVRelOrient == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM; + { + SwTwips nTmpMaxVPos = ( bMaxVPosAtBottom + ? aBoundRect.Bottom() + : aBoundRect.Height() ) - + rValidation.nHeight; + if ( rValidation.nVPos > nTmpMaxVPos ) + { + if (rValidation.nVertOrient == text::VertOrientation::NONE) + { + rValidation.nVPos = nTmpMaxVPos; + } + else + { + rValidation.nHeight = ( bMaxVPosAtBottom + ? aBoundRect.Bottom() + : aBoundRect.Height() ) - rValidation.nVPos; + } + } + } + + rValidation.nMinHPos = aBoundRect.Left(); + rValidation.nMaxHPos = aBoundRect.Right() - rValidation.nWidth; + + rValidation.nMinVPos = aBoundRect.Top(); + // OD 26.09.2003 #i17567#, #i18732# - determine maximum vertical position + if ( bMaxVPosAtBottom ) + { + rValidation.nMaxVPos = aBoundRect.Bottom() - rValidation.nHeight; + } + else + { + rValidation.nMaxVPos = aBoundRect.Height() - rValidation.nHeight; + } + + // Maximum width height + const SwTwips nH = ( rValidation.nHoriOrient != text::HoriOrientation::NONE ) + ? aBoundRect.Left() + : rValidation.nHPos; + const SwTwips nV = ( rValidation.nVertOrient != text::VertOrientation::NONE ) + ? aBoundRect.Top() + : rValidation.nVPos; + rValidation.nMaxHeight = rValidation.nMaxVPos + rValidation.nHeight - nV; + rValidation.nMaxWidth = rValidation.nMaxHPos + rValidation.nWidth - nH; + } + else if (eAnchorType == RndStdIds::FLY_AS_CHAR) + { + rValidation.nMinHPos = 0; + rValidation.nMaxHPos = 0; + + rValidation.nMaxHeight = aBoundRect.Height(); + rValidation.nMaxWidth = aBoundRect.Width(); + + rValidation.nMaxVPos = aBoundRect.Height(); + rValidation.nMinVPos = -aBoundRect.Height() + rValidation.nHeight; + if (rValidation.nMaxVPos < rValidation.nMinVPos) + { + rValidation.nMinVPos = rValidation.nMaxVPos; + rValidation.nMaxVPos = -aBoundRect.Height(); + } + } + if(bIsInVertical) + { + //restore width/height exchange + long nTmp = rValidation.nWidth; + rValidation.nWidth = rValidation.nHeight; + rValidation.nHeight = nTmp; + } + + if (rValidation.nMaxWidth < rValidation.nWidth) + rValidation.nWidth = rValidation.nMaxWidth; + if (rValidation.nMaxHeight < rValidation.nHeight) + rValidation.nHeight = rValidation.nMaxHeight; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drwtxtex.cxx b/sw/source/uibase/shells/drwtxtex.cxx new file mode 100644 index 000000000..185b79dd3 --- /dev/null +++ b/sw/source/uibase/shells/drwtxtex.cxx @@ -0,0 +1,1251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/XThesaurus.hpp> + +#include <comphelper/string.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <svx/svdview.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/urlfieldhelper.hxx> +#include <svx/svdoutl.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <svl/languageoptions.hxx> +#include <editeng/flditem.hxx> +#include <editeng/editstat.hxx> +#include <svx/clipfmtitem.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/svxdlg.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/langitem.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/writingmodeitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editdata.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <vcl/unohelp2.hxx> +#include <editeng/hyphenzoneitem.hxx> + +#include <cmdid.h> +#include <doc.hxx> +#include <drwtxtsh.hxx> +#include <edtwin.hxx> +#include <hintids.hxx> +#include <langhelper.hxx> +#include <chrdlgmodes.hxx> +#include <swmodule.hxx> +#include <uitool.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> + +#include <swabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star; + +namespace +{ + void lcl_convertStringArguments(sal_uInt16 nSlot, std::unique_ptr<SfxItemSet>& pArgs) + { + Color aColor; + OUString sColor; + const SfxPoolItem* pItem = nullptr; + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + sColor = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_CHAR_COLOR: + { + SvxColorItem aColorItem(aColor, EE_CHAR_COLOR); + pArgs->Put(aColorItem); + break; + } + + case SID_ATTR_CHAR_BACK_COLOR: + { + SvxBackgroundColorItem pBackgroundItem(aColor, EE_CHAR_BKGCOLOR); + pArgs->Put(pBackgroundItem); + break; + } + } + } + } +} + +void SwDrawTextShell::Execute( SfxRequest &rReq ) +{ + SwWrtShell &rSh = GetShell(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + SfxItemSet aEditAttr(pOLV->GetAttribs()); + SfxItemSet aNewAttr(*aEditAttr.GetPool(), aEditAttr.GetRanges()); + + const sal_uInt16 nSlot = rReq.GetSlot(); + + const sal_uInt16 nWhich = GetPool().GetWhich(nSlot); + std::unique_ptr<SfxItemSet> pNewAttrs(rReq.GetArgs() ? rReq.GetArgs()->Clone() : nullptr); + + bool bRestoreSelection = false; + ESelection aOldSelection; + + sal_uInt16 nEEWhich = 0; + switch (nSlot) + { + case SID_LANGUAGE_STATUS: + { + aOldSelection = pOLV->GetSelection(); + if (!pOLV->GetEditView().HasSelection()) + { + pOLV->GetEditView().SelectCurrentWord(); + } + + bRestoreSelection = SwLangHelper::SetLanguageStatus(pOLV,rReq,GetView(),rSh); + break; + } + + case SID_THES: + { + OUString aReplaceText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(SID_THES); + if (pItem2) + aReplaceText = pItem2->GetValue(); + if (!aReplaceText.isEmpty()) + ReplaceTextWithSynonym( pOLV->GetEditView(), aReplaceText ); + break; + } + + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + { + SfxItemPool* pPool2 = aEditAttr.GetPool()->GetSecondaryPool(); + if( !pPool2 ) + pPool2 = aEditAttr.GetPool(); + SvxScriptSetItem aSetItem( nSlot, *pPool2 ); + + // #i78017 establish the same behaviour as in Writer + SvtScriptType nScriptTypes = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; + if (nSlot == SID_ATTR_CHAR_FONT) + nScriptTypes = pOLV->GetSelectedScriptType(); + + if (pNewAttrs) + { + aSetItem.PutItemForScriptType( nScriptTypes, pNewAttrs->Get( nWhich ) ); + aNewAttr.Put( aSetItem.GetItemSet() ); + } + } + break; + + case SID_ATTR_CHAR_COLOR: nEEWhich = EE_CHAR_COLOR; break; + case SID_ATTR_CHAR_BACK_COLOR: nEEWhich = EE_CHAR_BKGCOLOR; break; + + case SID_ATTR_CHAR_UNDERLINE: + { + if ( pNewAttrs ) + { + const SvxTextLineItem& rTextLineItem = static_cast< const SvxTextLineItem& >( pNewAttrs->Get( pNewAttrs->GetPool()->GetWhich(nSlot) ) ); + aNewAttr.Put( SvxUnderlineItem( rTextLineItem.GetLineStyle(), EE_CHAR_UNDERLINE ) ); + } + else + { + FontLineStyle eFU = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem(eFU == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE) ); + } + } + break; + + case SID_ATTR_CHAR_OVERLINE: + { + FontLineStyle eFO = aEditAttr.Get(EE_CHAR_OVERLINE).GetLineStyle(); + aNewAttr.Put(SvxOverlineItem(eFO == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_OVERLINE)); + } + break; + + case SID_ATTR_CHAR_CONTOUR: nEEWhich = EE_CHAR_OUTLINE; break; + case SID_ATTR_CHAR_SHADOWED: nEEWhich = EE_CHAR_SHADOW; break; + case SID_ATTR_CHAR_STRIKEOUT: nEEWhich = EE_CHAR_STRIKEOUT; break; + case SID_ATTR_CHAR_WORDLINEMODE: nEEWhich = EE_CHAR_WLM; break; + case SID_ATTR_CHAR_RELIEF : nEEWhich = EE_CHAR_RELIEF; break; + case SID_ATTR_CHAR_LANGUAGE : nEEWhich = EE_CHAR_LANGUAGE;break; + case SID_ATTR_CHAR_KERNING : nEEWhich = EE_CHAR_KERNING; break; + case SID_ATTR_CHAR_SCALEWIDTH: nEEWhich = EE_CHAR_FONTWIDTH; break; + case SID_ATTR_CHAR_AUTOKERN : nEEWhich = EE_CHAR_PAIRKERNING; break; + case SID_ATTR_CHAR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT; break; + case SID_ATTR_PARA_ADJUST_LEFT: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Left, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_CENTER: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Right, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + aNewAttr.Put(SvxAdjustItem(SvxAdjust::Block, EE_PARA_JUST)); + break; + case SID_ATTR_PARA_LRSPACE: + { + SvxLRSpaceItem aParaMargin(static_cast<const SvxLRSpaceItem&>(rReq. + GetArgs()->Get(nSlot))); + aParaMargin.SetWhich( EE_PARA_LRSPACE ); + aNewAttr.Put(aParaMargin); + rReq.Done(); + } + break; + case SID_HANGING_INDENT: + { + SfxItemState eState = aEditAttr.GetItemState( EE_PARA_LRSPACE ); + if( eState >= SfxItemState::DEFAULT ) + { + SvxLRSpaceItem aParaMargin = aEditAttr.Get( EE_PARA_LRSPACE ); + aParaMargin.SetWhich( EE_PARA_LRSPACE ); + short int nFirstLineOffset = aParaMargin.GetTextFirstLineOffset(); + aParaMargin.SetTextLeft( aParaMargin.GetTextLeft() + nFirstLineOffset ); + aParaMargin.SetRight( aParaMargin.GetRight() ); + aParaMargin.SetTextFirstLineOffset( nFirstLineOffset * -1 ); + aNewAttr.Put(aParaMargin); + rReq.Done(); + } + } + break; + case SID_ATTR_PARA_LINESPACE: + { + SvxLineSpacingItem aLineSpace = static_cast<const SvxLineSpacingItem&>(pNewAttrs->Get( + GetPool().GetWhich(nSlot))); + aLineSpace.SetWhich( EE_PARA_SBL ); + aNewAttr.Put( aLineSpace ); + rReq.Done(); + } + break; + case SID_ATTR_PARA_ULSPACE: + { + SvxULSpaceItem aULSpace = static_cast<const SvxULSpaceItem&>(pNewAttrs->Get( + GetPool().GetWhich(nSlot))); + aULSpace.SetWhich( EE_PARA_ULSPACE ); + aNewAttr.Put( aULSpace ); + rReq.Done(); + } + break; + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SvxULSpaceItem aULSpace( aEditAttr.Get( EE_PARA_ULSPACE ) ); + sal_uInt16 nUpper = aULSpace.GetUpper(); + sal_uInt16 nLower = aULSpace.GetLower(); + + if ( nSlot == SID_PARASPACE_INCREASE ) + { + nUpper = std::min< sal_uInt16 >( nUpper + 57, 5670 ); + nLower = std::min< sal_uInt16 >( nLower + 57, 5670 ); + } + else + { + nUpper = std::max< sal_Int16 >( nUpper - 57, 0 ); + nLower = std::max< sal_Int16 >( nLower - 57, 0 ); + } + + aULSpace.SetUpper( nUpper ); + aULSpace.SetLower( nLower ); + aNewAttr.Put( aULSpace ); + rReq.Done(); + } + break; + case SID_ATTR_PARA_LINESPACE_10: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(100); + aNewAttr.Put(aItem); + } + break; + case SID_ATTR_PARA_LINESPACE_15: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(150); + aNewAttr.Put(aItem); + } + break; + case SID_ATTR_PARA_LINESPACE_20: + { + SvxLineSpacingItem aItem(LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL); + aItem.SetPropLineSpace(200); + aNewAttr.Put(aItem); + } + break; + + case FN_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case FN_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: + case SID_CHAR_DLG_FOR_PARAGRAPH: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + + if( !pArgs || pItem ) + { + aOldSelection = pOLV->GetSelection(); + if (nSlot == SID_CHAR_DLG_FOR_PARAGRAPH) + { + // select current paragraph (and restore selection later on...) + EditView & rEditView = pOLV->GetEditView(); + SwLangHelper::SelectPara( rEditView, rEditView.GetSelection() ); + bRestoreSelection = true; + } + + SwView* pView = &GetView(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( pView) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + SfxItemSet aDlgAttr(GetPool(), svl::Items<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END>{}); + + // util::Language does not exists in the EditEngine! That is why not in set. + + aDlgAttr.Put( aEditAttr ); + aDlgAttr.Put( SvxKerningItem(0, RES_CHRATR_KERNING) ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(pView->GetFrameWeld(), *pView, aDlgAttr, SwCharDlgMode::Draw)); + if (nSlot == SID_CHAR_DLG_EFFECT) + { + pDlg->SetCurPageId("fonteffects"); + } + else if (nSlot == SID_CHAR_DLG_FOR_PARAGRAPH) + { + pDlg->SetCurPageId("font"); + } + else if (pItem) + { + pDlg->SetCurPageId(OUStringToOString(pItem->GetValue(), RTL_TEXTENCODING_UTF8)); + } + + sal_uInt16 nRet = pDlg->Execute(); + if(RET_OK == nRet ) + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + aNewAttr.Put(*pDlg->GetOutputItemSet()); + } + if(RET_OK != nRet) + return ; + } + else + aNewAttr.Put(*pArgs); + } + break; + case FN_FORMAT_FOOTNOTE_DLG: + { + GetView().ExecFormatFootnote(); + break; + } + case FN_NUMBERING_OUTLINE_DLG: + { + GetView().ExecNumberingOutline(GetPool()); + rReq.Done(); + } + break; + case SID_OPEN_XML_FILTERSETTINGS: + { + HandleOpenXmlFilterSettings(rReq); + } + break; + case FN_WORDCOUNT_DIALOG: + { + GetView().UpdateWordCount(this, nSlot); + } + break; + case SID_PARA_DLG: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (!pArgs) + { + SwView* pView = &GetView(); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( pView) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + SfxItemSet aDlgAttr( + GetPool(), + svl::Items< + EE_ITEMS_START, EE_ITEMS_END, + SID_ATTR_PARA_HYPHENZONE, SID_ATTR_PARA_WIDOWS>{}); + + aDlgAttr.Put(aEditAttr); + + aDlgAttr.Put( SvxHyphenZoneItem( false, RES_PARATR_HYPHENZONE) ); + aDlgAttr.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) ); + aDlgAttr.Put( SvxFormatSplitItem( true, RES_PARATR_SPLIT ) ); + aDlgAttr.Put( SvxWidowsItem( 0, RES_PARATR_WIDOWS ) ); + aDlgAttr.Put( SvxOrphansItem( 0, RES_PARATR_ORPHANS ) ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwParaDlg(GetView().GetFrameWeld(), GetView(), aDlgAttr, true)); + sal_uInt16 nRet = pDlg->Execute(); + if(RET_OK == nRet) + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + aNewAttr.Put(*pDlg->GetOutputItemSet()); + } + if(RET_OK != nRet) + return; + } + else + aNewAttr.Put(*pArgs); + } + break; + case SID_AUTOSPELL_CHECK: + { +//!! JP 16.03.2001: why?? pSdrView = rSh.GetDrawView(); +//!! JP 16.03.2001: why?? pOutliner = pSdrView->GetTextEditOutliner(); + SdrOutliner * pOutliner = pSdrView->GetTextEditOutliner(); + EEControlBits nCtrl = pOutliner->GetControlWord(); + + bool bSet = static_cast<const SfxBoolItem&>(rReq.GetArgs()->Get( + nSlot)).GetValue(); + if(bSet) + nCtrl |= EEControlBits::ONLINESPELLING|EEControlBits::ALLOWBIGOBJS; + else + nCtrl &= ~EEControlBits::ONLINESPELLING; + pOutliner->SetControlWord(nCtrl); + + rView.ExecuteSlot(rReq); + } + break; + case SID_HYPERLINK_SETLINK: + { + const SfxPoolItem* pItem = nullptr; + if(pNewAttrs) + pNewAttrs->GetItemState(nSlot, false, &pItem); + + if(pItem) + { + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + SvxURLField aField(rHLinkItem.GetURL(), rHLinkItem.GetName(), SvxURLFormat::AppDefault); + aField.SetTargetFrame(rHLinkItem.GetTargetFrame()); + + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + + if (pFieldItem && dynamic_cast< const SvxURLField *>( pFieldItem->GetField() ) != nullptr ) + { + // Select field so that it will be deleted during insert + ESelection aSel = pOLV->GetSelection(); + aSel.nEndPos++; + pOLV->SetSelection(aSel); + } + pOLV->InsertField(SvxFieldItem(aField, EE_FEATURE_FIELD)); + } + } + break; + + case SID_EDIT_HYPERLINK: + { + // Ensure the field is selected first + pOLV->SelectFieldAtCursor(); + GetView().GetViewFrame()->GetDispatcher()->Execute(SID_HYPERLINK_DIALOG); + } + break; + + case SID_REMOVE_HYPERLINK: + { + URLFieldHelper::RemoveURLField(pOLV->GetEditView()); + } + break; + + case SID_OPEN_HYPERLINK: + { + const SvxFieldData* pField = pOLV->GetFieldAtCursor(); + if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField)) + { + SfxStringItem aUrl(SID_FILE_NAME, pURLField->GetURL()); + SfxStringItem aTarget(SID_TARGETNAME, pURLField->GetTargetFrame()); + SfxBoolItem aNewView(SID_OPEN_NEW_VIEW, false); + SfxBoolItem aBrowsing(SID_BROWSE, true); + GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::SYNCHRON, { &aUrl, &aTarget, &aNewView, &aBrowsing }); + } + } + break; + + case SID_COPY_HYPERLINK_LOCATION: + { + const SvxFieldData* pField = pOLV->GetFieldAtCursor(); + if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField)) + { + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard + = GetView().GetEditWin().GetClipboard(); + vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard); + } + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + // Shell switch! + { + SdrObject* pTmpObj = pSdrView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + SdrPageView* pTmpPV = pSdrView->GetSdrPageView(); + SdrView* pTmpView = pSdrView; + + pSdrView->SdrEndTextEdit(true); + + SfxItemSet aAttr( *aNewAttr.GetPool(), + svl::Items<SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTDIRECTION>{} ); + + aAttr.Put( SvxWritingModeItem( + nSlot == SID_TEXTDIRECTION_LEFT_TO_RIGHT ? + text::WritingMode_LR_TB + : text::WritingMode_TB_RL, SDRATTR_TEXTDIRECTION ) ); + pTmpView->SetAttributes( aAttr ); + + rSh.GetView().BeginTextEdit( pTmpObj, pTmpPV, &rSh.GetView().GetEditWin()); + rSh.GetView().AttrChangedNotify(nullptr); + } + return; + + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + { + SdrObject* pTmpObj = pSdrView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + SdrPageView* pTmpPV = pSdrView->GetSdrPageView(); + SdrView* pTmpView = pSdrView; + + pSdrView->SdrEndTextEdit(true); + bool bLeftToRight = nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT; + + const SfxPoolItem* pPoolItem; + if( pNewAttrs && SfxItemState::SET == pNewAttrs->GetItemState( nSlot, true, &pPoolItem ) ) + { + if( !static_cast<const SfxBoolItem*>(pPoolItem)->GetValue() ) + bLeftToRight = !bLeftToRight; + } + SfxItemSet aAttr( + *aNewAttr.GetPool(), + svl::Items< + EE_PARA_WRITINGDIR, EE_PARA_WRITINGDIR, + EE_PARA_JUST, EE_PARA_JUST>{}); + + SvxAdjust nAdjust = SvxAdjust::Left; + if( SfxItemState::SET == aEditAttr.GetItemState(EE_PARA_JUST, true, &pPoolItem ) ) + nAdjust = static_cast<const SvxAdjustItem*>(pPoolItem)->GetAdjust(); + + if( bLeftToRight ) + { + aAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Right ) + aAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + else + { + aAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Left ) + aAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + pTmpView->SetAttributes( aAttr ); + rSh.GetView().BeginTextEdit( pTmpObj, pTmpPV, &rSh.GetView().GetEditWin() ); + rSh.GetView().AttrChangedNotify(nullptr); + } + return; + + case FN_GROW_FONT_SIZE: + case FN_SHRINK_FONT_SIZE: + { + const SvxFontListItem* pFontListItem = static_cast< const SvxFontListItem* > + ( SfxObjectShell::Current()->GetItem( SID_ATTR_CHAR_FONTLIST ) ); + const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr; + pOLV->GetEditView().ChangeFontSize( nSlot == FN_GROW_FONT_SIZE, pFontList ); + } + break; + + default: + assert(false && "wrong dispatcher"); + return; + } + if (nEEWhich && pNewAttrs) + { + lcl_convertStringArguments(nSlot, pNewAttrs); + + aNewAttr.Put(pNewAttrs->Get(nWhich).CloneSetWhich(nEEWhich)); + } + + SetAttrToMarked(aNewAttr); + + GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + + if (IsTextEdit() && pOLV->GetOutliner()->IsModified()) + rSh.SetModified(); + + if (bRestoreSelection) + { + // restore selection + pOLV->GetEditView().SetSelection( aOldSelection ); + } +} + +void SwDrawTextShell::GetState(SfxItemSet& rSet) +{ + if (!IsTextEdit()) // Otherwise sometimes crash! + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + SfxItemSet aEditAttr(pOLV->GetAttribs()); + const SfxPoolItem *pAdjust = nullptr, *pLSpace = nullptr, *pEscItem = nullptr; + SvxAdjust eAdjust; + int nLSpace; + SvxEscapement nEsc; + + while (nWhich) + { + sal_uInt16 nSlotId = GetPool().GetSlotId(nWhich); + bool bFlag = false; + switch (nSlotId) + { + case SID_LANGUAGE_STATUS: //20412: + { + SwLangHelper::GetLanguageStatus(pOLV, rSet); + nSlotId = 0; + break; + } + + case SID_THES: + { + OUString aStatusVal; + LanguageType nLang = LANGUAGE_NONE; + bool bIsLookUpWord + = GetStatusValueForThesaurusFromContext(aStatusVal, nLang, pOLV->GetEditView()); + rSet.Put(SfxStringItem(SID_THES, aStatusVal)); + + // disable "Thesaurus" context menu entry if there is nothing to look up + uno::Reference<linguistic2::XThesaurus> xThes(::GetThesaurus()); + if (!bIsLookUpWord || !xThes.is() || nLang == LANGUAGE_NONE + || !xThes->hasLocale(LanguageTag::convertToLocale(nLang))) + rSet.DisableItem(SID_THES); + + //! avoid putting the same item as SfxBoolItem at the end of this function + nSlotId = 0; + break; + } + + case SID_ATTR_PARA_ADJUST_LEFT: + eAdjust = SvxAdjust::Left; + goto ASK_ADJUST; + case SID_ATTR_PARA_ADJUST_RIGHT: + eAdjust = SvxAdjust::Right; + goto ASK_ADJUST; + case SID_ATTR_PARA_ADJUST_CENTER: + eAdjust = SvxAdjust::Center; + goto ASK_ADJUST; + case SID_ATTR_PARA_ADJUST_BLOCK: + eAdjust = SvxAdjust::Block; + goto ASK_ADJUST; + ASK_ADJUST: + { + if (!pAdjust) + aEditAttr.GetItemState(EE_PARA_JUST, false, &pAdjust); + + if (!pAdjust || IsInvalidItem(pAdjust)) + { + rSet.InvalidateItem(nSlotId); + nSlotId = 0; + } + else + bFlag = eAdjust == static_cast<const SvxAdjustItem*>(pAdjust)->GetAdjust(); + } + break; + + case SID_ATTR_PARA_LRSPACE: + case SID_ATTR_PARA_LEFTSPACE: + case SID_ATTR_PARA_RIGHTSPACE: + case SID_ATTR_PARA_FIRSTLINESPACE: + { + SfxItemState eState = aEditAttr.GetItemState(EE_PARA_LRSPACE); + if (eState >= SfxItemState::DEFAULT) + { + SvxLRSpaceItem aLR = aEditAttr.Get(EE_PARA_LRSPACE); + aLR.SetWhich(SID_ATTR_PARA_LRSPACE); + rSet.Put(aLR); + } + else + rSet.InvalidateItem(nSlotId); + nSlotId = 0; + } + break; + case SID_ATTR_PARA_LINESPACE: + { + SfxItemState eState = aEditAttr.GetItemState(EE_PARA_SBL); + if (eState >= SfxItemState::DEFAULT) + { + const SvxLineSpacingItem& aLR = aEditAttr.Get(EE_PARA_SBL); + rSet.Put(aLR); + } + else + rSet.InvalidateItem(nSlotId); + nSlotId = 0; + } + break; + case SID_ATTR_PARA_ULSPACE: + case SID_ATTR_PARA_BELOWSPACE: + case SID_ATTR_PARA_ABOVESPACE: + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SfxItemState eState = aEditAttr.GetItemState(EE_PARA_ULSPACE); + if (eState >= SfxItemState::DEFAULT) + { + SvxULSpaceItem aULSpace = aEditAttr.Get(EE_PARA_ULSPACE); + if (!aULSpace.GetUpper() && !aULSpace.GetLower()) + rSet.DisableItem(SID_PARASPACE_DECREASE); + else if (aULSpace.GetUpper() >= 5670 && aULSpace.GetLower() >= 5670) + rSet.DisableItem(SID_PARASPACE_INCREASE); + if (nSlotId == SID_ATTR_PARA_ULSPACE || nSlotId == SID_ATTR_PARA_ABOVESPACE + || nSlotId == SID_ATTR_PARA_BELOWSPACE) + { + aULSpace.SetWhich(nSlotId); + rSet.Put(aULSpace); + } + } + else + { + rSet.DisableItem(SID_PARASPACE_INCREASE); + rSet.DisableItem(SID_PARASPACE_DECREASE); + rSet.InvalidateItem(SID_ATTR_PARA_ULSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_ABOVESPACE); + rSet.InvalidateItem(SID_ATTR_PARA_BELOWSPACE); + } + nSlotId = 0; + } + break; + + case SID_ATTR_PARA_LINESPACE_10: + nLSpace = 100; + goto ASK_LINESPACE; + case SID_ATTR_PARA_LINESPACE_15: + nLSpace = 150; + goto ASK_LINESPACE; + case SID_ATTR_PARA_LINESPACE_20: + nLSpace = 200; + goto ASK_LINESPACE; + ASK_LINESPACE: + { + if (!pLSpace) + aEditAttr.GetItemState(EE_PARA_SBL, false, &pLSpace); + + if (!pLSpace || IsInvalidItem(pLSpace)) + { + rSet.InvalidateItem(nSlotId); + nSlotId = 0; + } + else if (nLSpace + == static_cast<const SvxLineSpacingItem*>(pLSpace)->GetPropLineSpace()) + bFlag = true; + else + nSlotId = 0; + } + break; + + case FN_SET_SUPER_SCRIPT: + nEsc = SvxEscapement::Superscript; + goto ASK_ESCAPE; + case FN_SET_SUB_SCRIPT: + nEsc = SvxEscapement::Subscript; + goto ASK_ESCAPE; + ASK_ESCAPE: + { + if (!pEscItem) + pEscItem = &aEditAttr.Get(EE_CHAR_ESCAPEMENT); + + if (nEsc == static_cast<const SvxEscapementItem*>(pEscItem)->GetEscapement()) + bFlag = true; + else + nSlotId = 0; + } + break; + + case SID_THESAURUS: + { + // disable "Thesaurus" if the language is not supported + const SfxPoolItem& rItem = GetShell().GetDoc()->GetDefault(GetWhichOfScript( + RES_CHRATR_LANGUAGE, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage(GetAppLanguage()))); + LanguageType nLang = static_cast<const SvxLanguageItem&>(rItem).GetLanguage(); + + uno::Reference<linguistic2::XThesaurus> xThes(::GetThesaurus()); + if (!xThes.is() || nLang == LANGUAGE_NONE + || !xThes->hasLocale(LanguageTag::convertToLocale(nLang))) + rSet.DisableItem(SID_THESAURUS); + nSlotId = 0; + } + break; + case SID_HANGUL_HANJA_CONVERSION: + case SID_CHINESE_CONVERSION: + { + if (!SvtCJKOptions().IsAnyEnabled()) + { + GetView().GetViewFrame()->GetBindings().SetVisibleState(nWhich, false); + rSet.DisableItem(nWhich); + } + else + GetView().GetViewFrame()->GetBindings().SetVisibleState(nWhich, true); + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + if (!SvtLanguageOptions().IsVerticalTextEnabled()) + { + rSet.DisableItem(nSlotId); + nSlotId = 0; + } + else + { + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + if (pOutliner) + bFlag = pOutliner->IsVertical() + == (SID_TEXTDIRECTION_TOP_TO_BOTTOM == nSlotId); + else + { + text::WritingMode eMode = aEditAttr.Get(SDRATTR_TEXTDIRECTION).GetValue(); + + if (nSlotId == SID_TEXTDIRECTION_LEFT_TO_RIGHT) + { + bFlag = eMode == text::WritingMode_LR_TB; + } + else + { + bFlag = eMode != text::WritingMode_TB_RL; + } + } + } + break; + case SID_ATTR_PARA_LEFT_TO_RIGHT: + case SID_ATTR_PARA_RIGHT_TO_LEFT: + { + if (!SvtLanguageOptions().IsCTLFontEnabled()) + { + rSet.DisableItem(nWhich); + nSlotId = 0; + } + else + { + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + if (pOutliner && pOutliner->IsVertical()) + { + rSet.DisableItem(nWhich); + nSlotId = 0; + } + else + { + switch (aEditAttr.Get(EE_PARA_WRITINGDIR).GetValue()) + { + case SvxFrameDirection::Horizontal_LR_TB: + bFlag = nWhich == SID_ATTR_PARA_LEFT_TO_RIGHT; + break; + + case SvxFrameDirection::Horizontal_RL_TB: + bFlag = nWhich != SID_ATTR_PARA_LEFT_TO_RIGHT; + break; + default: + break; + } + } + } + } + break; + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + SvtCJKOptions aCJKOptions; + if (!aCJKOptions.IsChangeCaseMapEnabled()) + { + rSet.DisableItem(nWhich); + GetView().GetViewFrame()->GetBindings().SetVisibleState(nWhich, false); + } + else + GetView().GetViewFrame()->GetBindings().SetVisibleState(nWhich, true); + } + break; + case SID_INSERT_RLM: + case SID_INSERT_LRM: + { + SvtCTLOptions aCTLOptions; + bool bEnabled = aCTLOptions.IsCTLFontEnabled(); + GetView().GetViewFrame()->GetBindings().SetVisibleState(nWhich, bEnabled); + if (!bEnabled) + rSet.DisableItem(nWhich); + } + break; + case SID_REMOVE_HYPERLINK: + case SID_EDIT_HYPERLINK: + case SID_OPEN_HYPERLINK: + case SID_COPY_HYPERLINK_LOCATION: + { + if (!URLFieldHelper::IsCursorAtURLField(pOLV)) + rSet.DisableItem(nWhich); + } + break; + default: + nSlotId = 0; // don't know this slot + break; + } + + if (nSlotId && bFlag) + rSet.Put(SfxBoolItem(nWhich, bFlag)); + + nWhich = aIter.NextWhich(); + } +} + +void SwDrawTextShell::GetDrawTextCtrlState(SfxItemSet& rSet) +{ + if (!IsTextEdit()) // Otherwise crash! + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + SfxItemSet aEditAttr(pOLV->GetAttribs()); + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + SvtScriptType nScriptType = pOLV->GetSelectedScriptType(); + while(nWhich) + { + sal_uInt16 nEEWhich = 0; + sal_uInt16 nSlotId = GetPool().GetSlotId( nWhich ); + switch( nSlotId ) + { + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + { + SfxItemPool* pEditPool = aEditAttr.GetPool()->GetSecondaryPool(); + if( !pEditPool ) + pEditPool = aEditAttr.GetPool(); + SvxScriptSetItem aSetItem( nSlotId, *pEditPool ); + aSetItem.GetItemSet().Put( aEditAttr, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScriptType ); + if( pI ) + { + rSet.Put(pI->CloneSetWhich(nWhich)); + } + else + rSet.InvalidateItem( nWhich ); + } + break; + case SID_ATTR_CHAR_COLOR: nEEWhich = EE_CHAR_COLOR; break; + case SID_ATTR_CHAR_BACK_COLOR: nEEWhich = EE_CHAR_BKGCOLOR; break; + case SID_ATTR_CHAR_UNDERLINE: nEEWhich = EE_CHAR_UNDERLINE;break; + case SID_ATTR_CHAR_OVERLINE: nEEWhich = EE_CHAR_OVERLINE;break; + case SID_ATTR_CHAR_CONTOUR: nEEWhich = EE_CHAR_OUTLINE; break; + case SID_ATTR_CHAR_SHADOWED: nEEWhich = EE_CHAR_SHADOW;break; + case SID_ATTR_CHAR_STRIKEOUT: nEEWhich = EE_CHAR_STRIKEOUT;break; + case SID_AUTOSPELL_CHECK: + { + const SfxPoolItem* pState = rView.GetSlotState(nWhich); + if (pState) + rSet.Put(SfxBoolItem(nWhich, static_cast<const SfxBoolItem*>(pState)->GetValue())); + else + rSet.DisableItem( nWhich ); + break; + } + case SID_ATTR_CHAR_WORDLINEMODE: nEEWhich = EE_CHAR_WLM; break; + case SID_ATTR_CHAR_RELIEF : nEEWhich = EE_CHAR_RELIEF; break; + case SID_ATTR_CHAR_LANGUAGE : nEEWhich = EE_CHAR_LANGUAGE;break; + case SID_ATTR_CHAR_KERNING : nEEWhich = EE_CHAR_KERNING; break; + case SID_ATTR_CHAR_SCALEWIDTH: nEEWhich = EE_CHAR_FONTWIDTH;break; + case SID_ATTR_CHAR_AUTOKERN : nEEWhich = EE_CHAR_PAIRKERNING; break; + case SID_ATTR_CHAR_ESCAPEMENT: nEEWhich = EE_CHAR_ESCAPEMENT; break; + case FN_GROW_FONT_SIZE: + case FN_SHRINK_FONT_SIZE: + { + SfxItemPool* pEditPool = aEditAttr.GetPool()->GetSecondaryPool(); + if( !pEditPool ) + pEditPool = aEditAttr.GetPool(); + + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONTHEIGHT, *pEditPool ); + aSetItem.GetItemSet().Put( aEditAttr, false ); + const SvxFontHeightItem* pSize( static_cast<const SvxFontHeightItem*>( aSetItem.GetItemOfScript( nScriptType ) ) ); + + if( pSize ) + { + sal_uInt32 nSize = pSize->GetHeight(); + if( nSize >= 19998 ) + rSet.DisableItem( FN_GROW_FONT_SIZE ); + else if( nSize <= 40 ) + rSet.DisableItem( FN_SHRINK_FONT_SIZE ); + } + } + } + if(nEEWhich) + { + rSet.Put(aEditAttr.Get(nEEWhich).CloneSetWhich(nWhich)); + } + + nWhich = aIter.NextWhich(); + } +} + +void SwDrawTextShell::ExecClpbrd(SfxRequest const &rReq) +{ + if (!IsTextEdit()) // Otherwise crash! + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + + ESelection aSel(pOLV->GetSelection()); + const bool bCopy = (aSel.nStartPara != aSel.nEndPara) || (aSel.nStartPos != aSel.nEndPos); + sal_uInt16 nId = rReq.GetSlot(); + switch( nId ) + { + case SID_CUT: + if (bCopy) + pOLV->Cut(); + return; + + case SID_COPY: + if (bCopy) + pOLV->Copy(); + return; + + case SID_PASTE: + pOLV->PasteSpecial(); + break; + + case SID_PASTE_UNFORMATTED: + pOLV->Paste(); + break; + + case SID_PASTE_SPECIAL: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(GetView().GetEditWin().GetFrameWeld())); + + pDlg->Insert(SotClipboardFormatId::STRING, OUString()); + pDlg->Insert(SotClipboardFormatId::RTF, OUString()); + pDlg->Insert(SotClipboardFormatId::RICHTEXT, OUString()); + + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(&GetView().GetEditWin())); + SotClipboardFormatId nFormat = pDlg->GetFormat(aDataHelper.GetTransferable()); + + if (nFormat != SotClipboardFormatId::NONE) + { + if (nFormat == SotClipboardFormatId::STRING) + pOLV->Paste(); + else + pOLV->PasteSpecial(); + } + + break; + } + + case SID_CLIPBOARD_FORMAT_ITEMS: + { + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + const SfxPoolItem* pItem; + if (rReq.GetArgs() && rReq.GetArgs()->GetItemState(nId, true, &pItem) == SfxItemState::SET) + { + if (const SfxUInt32Item* pUInt32Item = dynamic_cast<const SfxUInt32Item *>(pItem)) + nFormat = static_cast<SotClipboardFormatId>(pUInt32Item->GetValue()); + } + + if (nFormat != SotClipboardFormatId::NONE) + { + if (nFormat == SotClipboardFormatId::STRING) + pOLV->Paste(); + else + pOLV->PasteSpecial(); + } + + break; + } + + default: + OSL_FAIL("wrong dispatcher"); + return; + } +} + +void SwDrawTextShell::StateClpbrd(SfxItemSet &rSet) +{ + if (!IsTextEdit()) // Otherwise crash! + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + ESelection aSel(pOLV->GetSelection()); + const bool bCopy = (aSel.nStartPara != aSel.nEndPara) || + (aSel.nStartPos != aSel.nEndPos); + + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( &GetView().GetEditWin() ) ); + const bool bPaste = aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || + aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || + aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ); + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_CUT: + case SID_COPY: + if( !bCopy || GetObjectShell()->isContentExtractionLocked()) + rSet.DisableItem( nWhich ); + break; + + case SID_PASTE: + case SID_PASTE_UNFORMATTED: + case SID_PASTE_SPECIAL: + if( !bPaste ) + rSet.DisableItem( nWhich ); + break; + + case SID_CLIPBOARD_FORMAT_ITEMS: + if( bPaste ) + { + SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS ); + + if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) + aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING ); + if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ) + aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF ); + if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) + aFormats.AddClipbrdFormat( SotClipboardFormatId::RICHTEXT ); + + rSet.Put( aFormats ); + } + else + rSet.DisableItem( nWhich ); + break; + } + + nWhich = aIter.NextWhich(); + } +} + +// Hyperlink status + +void SwDrawTextShell::StateInsert(SfxItemSet &rSet) +{ + if (!IsTextEdit()) // Otherwise crash! + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_HYPERLINK_GETLINK: + { + SvxHyperlinkItem aHLinkItem; + aHLinkItem.SetInsertMode(HLINK_FIELD); + + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + + if (pFieldItem) + { + const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pFieldItem->GetField()); + + if (pURLField) + { + aHLinkItem.SetName(pURLField->GetRepresentation()); + aHLinkItem.SetURL(pURLField->GetURL()); + aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame()); + } + } + else + { + OUString sSel(pOLV->GetSelected()); + sSel = sSel.copy(0, std::min<sal_Int32>(255, sSel.getLength())); + aHLinkItem.SetName(comphelper::string::stripEnd(sSel, ' ')); + } + + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + aHLinkItem.SetInsertMode(static_cast<SvxLinkInsertMode>(aHLinkItem.GetInsertMode() | + ((nHtmlMode & HTMLMODE_ON) != 0 ? HLINK_HTMLMODE : 0))); + + rSet.Put(aHLinkItem); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/drwtxtsh.cxx b/sw/source/uibase/shells/drwtxtsh.cxx new file mode 100644 index 000000000..b8d79ebb9 --- /dev/null +++ b/sw/source/uibase/shells/drwtxtsh.cxx @@ -0,0 +1,846 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/lang.h> +#include <i18nutil/transliteration.hxx> +#include <rtl/ustrbuf.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <editeng/fontitem.hxx> +#include <svx/svdview.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objface.hxx> +#include <svx/svdotext.hxx> +#include <svx/sdooitm.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/scripttypeitem.hxx> +#include <sfx2/bindings.hxx> +#include <svx/fontwork.hxx> +#include <sfx2/request.hxx> +#include <vcl/EnumContext.hxx> +#include <svl/whiter.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editstat.hxx> +#include <svx/svdoutl.hxx> +#include <com/sun/star/i18n/TextConversionOption.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/propertysequence.hxx> +#include <swtypes.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <drwtxtsh.hxx> +#include <breakit.hxx> + +#include <cmdid.h> + +#define ShellClass_SwDrawTextShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <uitool.hxx> +#include <wview.hxx> +#include <swmodule.hxx> +#include <svx/svdoashp.hxx> +#include <svx/svxdlg.hxx> +#include <comphelper/processfactory.hxx> +#include <IDocumentUndoRedo.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; + +SFX_IMPL_INTERFACE(SwDrawTextShell, SfxShell) + +void SwDrawTextShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("drawtext"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Draw_Text_Toolbox_Sw); + + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); +} + + +void SwDrawTextShell::Init() +{ + SwWrtShell &rSh = GetShell(); + pSdrView = rSh.GetDrawView(); + SdrOutliner * pOutliner = pSdrView->GetTextEditOutliner(); + //#97471# mouse click _and_ key input at the same time + if( !pOutliner ) + return ; + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + EEControlBits nCtrl = pOutliner->GetControlWord(); + nCtrl |= EEControlBits::AUTOCORRECT; + + SetUndoManager(&pOutliner->GetUndoManager()); + + // Now let's try an AutoSpell. + + const SwViewOption* pVOpt = rSh.GetViewOptions(); + if(pVOpt->IsOnlineSpell()) + { + nCtrl |= EEControlBits::ONLINESPELLING|EEControlBits::ALLOWBIGOBJS; + } + else + nCtrl &= ~EEControlBits::ONLINESPELLING; + + pOutliner->SetControlWord(nCtrl); + pOLV->ShowCursor(); +} + +SwDrawTextShell::SwDrawTextShell(SwView &rV) : + SfxShell(&rV), + rView(rV) +{ + SwWrtShell &rSh = GetShell(); + SetPool(rSh.GetAttrPool().GetSecondaryPool()); + + // Initialize and show cursor to start editing. + Init(); + + SetName("ObjectText"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::DrawText)); +} + +SwDrawTextShell::~SwDrawTextShell() +{ + if ( GetView().GetCurShell() == this ) + rView.ResetSubShell(); +} + +SwWrtShell& SwDrawTextShell::GetShell() +{ + return rView.GetWrtShell(); +} + +// Disable slots with this status method + +void SwDrawTextShell::StateDisableItems( SfxItemSet &rSet ) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while (nWhich) + { + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } +} + +void SwDrawTextShell::SetAttrToMarked(const SfxItemSet& rAttr) +{ + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + tools::Rectangle aOutRect = pOLV->GetOutputArea(); + + if (tools::Rectangle() != aOutRect) + { + GetShell().GetDrawView()->SetAttributes(rAttr); + } +} + +bool SwDrawTextShell::IsTextEdit() const +{ + return pSdrView->IsTextEdit(); +} + +void SwDrawTextShell::ExecFontWork(SfxRequest const & rReq) +{ + SwWrtShell &rSh = GetShell(); + FieldUnit eMetric = ::GetDfltMetric( dynamic_cast<SwWebView*>( &rSh.GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if ( rReq.GetArgs() ) + { + pVFrame->SetChildWindow(SvxFontWorkChildWindow::GetChildWindowId(), + static_cast<const SfxBoolItem&>( (rReq.GetArgs()-> + Get(SID_FONTWORK))).GetValue()); + } + else + pVFrame->ToggleChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); + + pVFrame->GetBindings().Invalidate(SID_FONTWORK); +} + +void SwDrawTextShell::StateFontWork(SfxItemSet& rSet) +{ + const sal_uInt16 nId = SvxFontWorkChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem(SID_FONTWORK, GetView().GetViewFrame()->HasChildWindow(nId))); +} + +// Edit SfxRequests for FontWork + +void SwDrawTextShell::ExecFormText(SfxRequest const & rReq) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pDrView = rSh.GetDrawView(); + + const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() ) + { + const SfxItemSet& rSet = *rReq.GetArgs(); + + if ( pDrView->IsTextEdit() ) + { + //#111733# Sometimes SdrEndTextEdit() initiates the change in selection and + // 'this' is not valid anymore + SwView& rTempView = GetView(); + pDrView->SdrEndTextEdit(true); + //this removes the current shell from the dispatcher stack!! + rTempView.AttrChangedNotify(nullptr); + } + + pDrView->SetAttributes(rSet); + } + +} + +// Return Status values back to FontWork + +void SwDrawTextShell::GetFormTextState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + SdrView* pDrView = rSh.GetDrawView(); + const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(pObj); + const bool bDeactivate( + !pObj || + !pTextObj || + !pTextObj->HasText() || + dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes + + if (bDeactivate) + { + rSet.DisableItem(XATTR_FORMTXTSTYLE); + rSet.DisableItem(XATTR_FORMTXTADJUST); + rSet.DisableItem(XATTR_FORMTXTDISTANCE); + rSet.DisableItem(XATTR_FORMTXTSTART); + rSet.DisableItem(XATTR_FORMTXTMIRROR); + rSet.DisableItem(XATTR_FORMTXTHIDEFORM); + rSet.DisableItem(XATTR_FORMTXTOUTLINE); + rSet.DisableItem(XATTR_FORMTXTSHADOW); + rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR); + rSet.DisableItem(XATTR_FORMTXTSHDWXVAL); + rSet.DisableItem(XATTR_FORMTXTSHDWYVAL); + } + else + { + pDrView->GetAttributes( rSet ); + } +} + +void SwDrawTextShell::ExecDrawLingu(SfxRequest const &rReq) +{ + SwWrtShell &rSh = GetShell(); + OutlinerView* pOutlinerView = pSdrView->GetTextEditOutlinerView(); + if( rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() ) + { + switch(rReq.GetSlot()) + { + case SID_THESAURUS: + pOutlinerView->StartThesaurus(); + break; + + case SID_HANGUL_HANJA_CONVERSION: + pOutlinerView->StartTextConversion(LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, + i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true, false); + break; + + case SID_CHINESE_CONVERSION: + { + //open ChineseTranslationDialog + Reference<XComponentContext> xContext = comphelper::getProcessComponentContext(); + if (!xContext.is()) + return; + + Reference<lang::XMultiComponentFactory> xMCF(xContext->getServiceManager()); + if (!xMCF.is()) + return; + + Reference<ui::dialogs::XExecutableDialog> xDialog( + xMCF->createInstanceWithContext("com.sun.star.linguistic2.ChineseTranslationDialog", xContext), UNO_QUERY); + + Reference<lang::XInitialization> xInit(xDialog, UNO_QUERY); + + if (!xInit.is()) + return; + + // initialize dialog + uno::Sequence<uno::Any> aSequence(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(Reference<awt::XWindow>())} + })); + xInit->initialize( aSequence ); + + //execute dialog + sal_Int16 nDialogRet = xDialog->execute(); + if(RET_OK == nDialogRet) + { + //get some parameters from the dialog + bool bToSimplified = true; + bool bUseVariants = true; + bool bCommonTerms = true; + Reference<beans::XPropertySet> xPropertySet(xDialog, UNO_QUERY); + if (xPropertySet.is()) + { + try + { + xPropertySet->getPropertyValue("IsDirectionToSimplified") >>= bToSimplified; + xPropertySet->getPropertyValue("IsUseCharacterVariants") >>= bUseVariants; + xPropertySet->getPropertyValue("IsTranslateCommonTerms") >>= bCommonTerms; + } + catch (const Exception&) + { + } + } + + //execute translation + LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; + LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; + sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0; + if(!bCommonTerms) + nOptions = nOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER; + + vcl::Font aTargetFont = OutputDevice::GetDefaultFont(DefaultFontType::CJK_TEXT, nTargetLang, GetDefaultFontFlags::OnlyOne); + + pOutlinerView->StartTextConversion(nSourceLang, nTargetLang, &aTargetFont, nOptions, false, false); + } + + Reference<lang::XComponent> xComponent(xDialog, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } + break; + + default: + OSL_ENSURE(false, "unexpected slot-id"); + } + } +} + +void SwDrawTextShell::ExecDraw(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + pSdrView = rSh.GetDrawView(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + + switch (rReq.GetSlot()) + { + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_ZWNBSP : + case SID_INSERT_ZWSP: + { + sal_Unicode cIns = 0; + switch(rReq.GetSlot()) + { + case FN_INSERT_SOFT_HYPHEN: cIns = CHAR_SOFTHYPHEN; break; + case FN_INSERT_HARDHYPHEN: cIns = CHAR_HARDHYPHEN; break; + case FN_INSERT_HARD_SPACE: cIns = CHAR_HARDBLANK; break; + case FN_INSERT_NNBSP: cIns = CHAR_NNBSP; break; + case SID_INSERT_RLM : cIns = CHAR_RLM ; break; + case SID_INSERT_LRM : cIns = CHAR_LRM ; break; + case SID_INSERT_ZWSP : cIns = CHAR_ZWSP ; break; + case SID_INSERT_ZWNBSP: cIns = CHAR_ZWNBSP; break; + } + pOLV->InsertText( OUString(cIns)); + rReq.Done(); + } + break; + case SID_CHARMAP: + { // Insert special character + InsertSymbol(rReq); + break; + } + case FN_INSERT_STRING: + { + const SfxItemSet *pNewAttrs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxPoolItem* pItem = nullptr; + if(pNewAttrs) + { + pNewAttrs->GetItemState(nSlot, false, &pItem ); + pOLV->InsertText(static_cast<const SfxStringItem *>(pItem)->GetValue()); + } + break; + } + + case SID_SELECTALL: + { + SdrOutliner * pOutliner = pSdrView->GetTextEditOutliner(); + if(pOutliner) + { + sal_Int32 nParaCount = pOutliner->GetParagraphCount(); + if (nParaCount > 0) + pOLV->SelectRange(0, nParaCount ); + } + } + break; + + case FN_FORMAT_RESET: // delete hard text attributes + { + pOLV->RemoveAttribsKeepLanguages( true ); + pOLV->GetEditView().GetEditEngine()->RemoveFields(); + rReq.Done(); + } + break; + + case FN_ESCAPE: + if (pSdrView->IsTextEdit()) + { + // Shell switch! + rSh.EndTextEdit(); + SwView& rTempView = rSh.GetView(); + rTempView.ExitDraw(); + rSh.Edit(); + return; + } + break; + case FN_DRAWTEXT_ATTR_DLG: + { + SfxItemSet aNewAttr( pSdrView->GetModel()->GetItemPool() ); + pSdrView->GetAttributes( aNewAttr ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTextTabDialog( + GetView().GetFrameWeld(), + &aNewAttr, pSdrView )); + sal_uInt16 nResult = pDlg->Execute(); + + if (nResult == RET_OK) + { + if (pSdrView->AreObjectsMarked()) + { + pSdrView->SetAttributes(*pDlg->GetOutputItemSet()); + rReq.Done(*(pDlg->GetOutputItemSet())); + } + } + } + break; + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + { + sal_uInt16 nSId = rReq.GetSlot(); + if (pSdrView->AreObjectsMarked()) + { + SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_TOP; + if (nSId == SID_TABLE_VERT_CENTER) + eTVA = SDRTEXTVERTADJUST_CENTER; + else if (nSId == SID_TABLE_VERT_BOTTOM) + eTVA = SDRTEXTVERTADJUST_BOTTOM; + + SfxItemSet aNewAttr( pSdrView->GetModel()->GetItemPool() ); + pSdrView->GetAttributes( aNewAttr ); + aNewAttr.Put(SdrTextVertAdjustItem(eTVA)); + pSdrView->SetAttributes(aNewAttr); + rReq.Done(); + } + + } + break; + + default: + OSL_ENSURE(false, "unexpected slot-id"); + return; + } + + GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + + if (IsTextEdit() && pOLV->GetOutliner()->IsModified()) + rSh.SetModified(); +} + +// Execute undo + +void SwDrawTextShell::ExecUndo(SfxRequest &rReq) +{ + if( IsTextEdit() ) + { + bool bCallBase = true; + const SfxItemSet* pArgs = rReq.GetArgs(); + if( pArgs ) + { + sal_uInt16 nId = rReq.GetSlot(), nCnt = 1; + const SfxPoolItem* pItem; + switch( nId ) + { + case SID_UNDO: + case SID_REDO: + if( SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem ) && + 1 < (nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue()) ) + { + // then we make by ourself. + SfxUndoManager* pUndoManager = GetUndoManager(); + if( pUndoManager ) + { + if( SID_UNDO == nId ) + while( nCnt-- ) + pUndoManager->Undo(); + else + while( nCnt-- ) + pUndoManager->Redo(); + } + bCallBase = false; + GetView().GetViewFrame()->GetBindings().InvalidateAll(false); + } + break; + } + } + if( bCallBase ) + { + SfxViewFrame *pSfxViewFrame = GetView().GetViewFrame(); + pSfxViewFrame->ExecuteSlot(rReq, pSfxViewFrame->GetInterface()); + } + } +} + +// State of undo + +void SwDrawTextShell::StateUndo(SfxItemSet &rSet) +{ + if ( !IsTextEdit() ) + return; + + SfxViewFrame *pSfxViewFrame = GetView().GetViewFrame(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + switch ( nWhich ) + { + case SID_GETUNDOSTRINGS: + case SID_GETREDOSTRINGS: + { + SfxUndoManager* pUndoManager = GetUndoManager(); + if( pUndoManager ) + { + OUString (SfxUndoManager:: *fnGetComment)( size_t, bool const ) const; + + sal_uInt16 nCount; + if( SID_GETUNDOSTRINGS == nWhich ) + { + nCount = pUndoManager->GetUndoActionCount(); + fnGetComment = &SfxUndoManager::GetUndoActionComment; + } + else + { + nCount = pUndoManager->GetRedoActionCount(); + fnGetComment = &SfxUndoManager::GetRedoActionComment; + } + if( nCount ) + { + OUStringBuffer sList; + for( sal_uInt16 n = 0; n < nCount; ++n ) + sList.append( (pUndoManager->*fnGetComment)( n, SfxUndoManager::TopLevel ) ).append("\n"); + + SfxStringListItem aItem( nWhich ); + aItem.SetString( sList.makeStringAndClear() ); + rSet.Put( aItem ); + } + } + else + rSet.DisableItem( nWhich ); + } + break; + + default: + { + auto* pUndoManager = dynamic_cast<IDocumentUndoRedo*>(GetUndoManager()); + if (pUndoManager) + pUndoManager->SetView(&GetView()); + pSfxViewFrame->GetSlotState(nWhich, pSfxViewFrame->GetInterface(), &rSet); + if (pUndoManager) + pUndoManager->SetView(nullptr); + } + } + + nWhich = aIter.NextWhich(); + } +} + +void SwDrawTextShell::ExecTransliteration( SfxRequest const & rReq ) +{ + if (!pSdrView) + return; + + using namespace i18n; + + TransliterationFlags nMode = TransliterationFlags::NONE; + + switch( rReq.GetSlot() ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nMode = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nMode = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nMode = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nMode = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nMode = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + + case SID_TRANSLITERATE_HALFWIDTH: + nMode = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nMode = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + + case SID_TRANSLITERATE_HIRAGANA: + nMode = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nMode = TransliterationFlags::HIRAGANA_KATAKANA; + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + } + + if( nMode != TransliterationFlags::NONE ) + { + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + + if (!pOLV) + return; + + pOLV->TransliterateText( nMode ); + } +} + +void SwDrawTextShell::ExecRotateTransliteration( SfxRequest const & rReq ) +{ + if( rReq.GetSlot() == SID_TRANSLITERATE_ROTATE_CASE ) + { + if (!pSdrView) + return; + + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + + if (!pOLV) + return; + + pOLV->TransliterateText( m_aRotateCase.getNextMode() ); + } +} + +// Insert special character (see SDraw: FUBULLET.CXX) + +void SwDrawTextShell::InsertSymbol(SfxRequest& rReq) +{ + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + if(!pOLV) + return; + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if( pArgs ) + pArgs->GetItemState(GetPool().GetWhich(SID_CHARMAP), false, &pItem); + + OUString sSym; + OUString sFontName; + if ( pItem ) + { + sSym = static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SfxPoolItem* pFtItem = nullptr; + pArgs->GetItemState( GetPool().GetWhich(SID_ATTR_SPECIALCHAR), false, &pFtItem); + const SfxStringItem* pFontItem = dynamic_cast<const SfxStringItem*>( pFtItem ); + if ( pFontItem ) + sFontName = pFontItem->GetValue(); + } + + SfxItemSet aSet(pOLV->GetAttribs()); + SvtScriptType nScript = pOLV->GetSelectedScriptType(); + std::shared_ptr<SvxFontItem> aSetDlgFont(std::make_shared<SvxFontItem>(RES_CHRATR_FONT)); + { + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, *aSet.GetPool() ); + aSetItem.GetItemSet().Put( aSet, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript ); + if( pI ) + aSetDlgFont.reset(static_cast<SvxFontItem*>(pI->Clone())); + else + aSetDlgFont.reset(static_cast<SvxFontItem*>(aSet.Get( GetWhichOfScript( + SID_ATTR_CHAR_FONT, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ) )).Clone())); + if (sFontName.isEmpty()) + sFontName = aSetDlgFont->GetFamilyName(); + } + + vcl::Font aFont(sFontName, Size(1,1)); + if(sSym.isEmpty()) + { + SfxAllItemSet aAllSet( GetPool() ); + aAllSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + SwViewOption aOpt(*rView.GetWrtShell().GetViewOptions()); + const OUString& sSymbolFont = aOpt.GetSymbolFont(); + if( !sSymbolFont.isEmpty() ) + aAllSet.Put( SfxStringItem( SID_FONT_NAME, sSymbolFont ) ); + else + aAllSet.Put( SfxStringItem( SID_FONT_NAME, aSetDlgFont->GetFamilyName() ) ); + + // If character is selected, it can be shown + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + auto xFrame = rView.GetViewFrame()->GetFrame().GetFrameInterface(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(rView.GetFrameWeld(), aAllSet, xFrame)); + pDlg->Execute(); + return; + } + + // do not flicker + pOLV->HideCursor(); + SdrOutliner * pOutliner = pSdrView->GetTextEditOutliner(); + pOutliner->SetUpdateMode(false); + + SfxItemSet aOldSet( pOLV->GetAttribs() ); + SfxItemSet aFontSet( + *aOldSet.GetPool(), + svl::Items< + EE_CHAR_FONTINFO, EE_CHAR_FONTINFO, + EE_CHAR_FONTINFO_CJK, EE_CHAR_FONTINFO_CTL>{}); + aFontSet.Set( aOldSet ); + + // Insert string + pOLV->InsertText( sSym ); + + // assign attributes (Set font) + SfxItemSet aFontAttribSet( *aFontSet.GetPool(), aFontSet.GetRanges() ); + SvxFontItem aFontItem (aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), + aFont.GetCharSet(), + EE_CHAR_FONTINFO ); + nScript = g_pBreakIt->GetAllScriptsOfText( sSym ); + if( SvtScriptType::LATIN & nScript ) + aFontAttribSet.Put( aFontItem ); + if( SvtScriptType::ASIAN & nScript ) + { + aFontItem.SetWhich(EE_CHAR_FONTINFO_CJK); + aFontAttribSet.Put( aFontItem ); + } + if( SvtScriptType::COMPLEX & nScript ) + { + aFontItem.SetWhich(EE_CHAR_FONTINFO_CTL); + aFontAttribSet.Put( aFontItem ); + } + pOLV->SetAttribs(aFontAttribSet); + + // Remove selection + ESelection aSel(pOLV->GetSelection()); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOLV->SetSelection(aSel); + + // Restore old font + pOLV->SetAttribs( aFontSet ); + + // From now on show again + pOutliner->SetUpdateMode(true); + pOLV->ShowCursor(); + + rReq.AppendItem( SfxStringItem( GetPool().GetWhich(SID_CHARMAP), sSym ) ); + if(!aFont.GetFamilyName().isEmpty()) + rReq.AppendItem( SfxStringItem( SID_ATTR_SPECIALCHAR, aFont.GetFamilyName() ) ); + rReq.Done(); + +} + +SfxUndoManager* SwDrawTextShell::GetUndoManager() +{ + SwWrtShell &rSh = GetShell(); + pSdrView = rSh.GetDrawView(); + SdrOutliner * pOutliner = pSdrView->GetTextEditOutliner(); + return &pOutliner->GetUndoManager(); +} + +void SwDrawTextShell::GetStatePropPanelAttr(SfxItemSet &rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + SwWrtShell &rSh = GetShell(); + pSdrView = rSh.GetDrawView(); + + SfxItemSet aAttrs( pSdrView->GetModel()->GetItemPool() ); + pSdrView->GetAttributes( aAttrs ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + switch ( nSlotId ) + { + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + bool bContour = false; + SfxItemState eConState = aAttrs.GetItemState( SDRATTR_TEXT_CONTOURFRAME ); + if( eConState != SfxItemState::DONTCARE ) + { + bContour = aAttrs.Get( SDRATTR_TEXT_CONTOURFRAME ).GetValue(); + } + if (bContour) break; + + SfxItemState eVState = aAttrs.GetItemState( SDRATTR_TEXT_VERTADJUST ); + //SfxItemState eHState = aAttrs.GetItemState( SDRATTR_TEXT_HORZADJUST ); + + //if(SfxItemState::DONTCARE != eVState && SfxItemState::DONTCARE != eHState) + if(SfxItemState::DONTCARE != eVState) + { + SdrTextVertAdjust eTVA = aAttrs.Get(SDRATTR_TEXT_VERTADJUST).GetValue(); + bool bSet = (nSlotId == SID_TABLE_VERT_NONE && eTVA == SDRTEXTVERTADJUST_TOP) || + (nSlotId == SID_TABLE_VERT_CENTER && eTVA == SDRTEXTVERTADJUST_CENTER) || + (nSlotId == SID_TABLE_VERT_BOTTOM && eTVA == SDRTEXTVERTADJUST_BOTTOM); + rSet.Put(SfxBoolItem(nSlotId, bSet)); + } + else + { + rSet.Put(SfxBoolItem(nSlotId, false)); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/frmsh.cxx b/sw/source/uibase/shells/frmsh.cxx new file mode 100644 index 000000000..730009e1c --- /dev/null +++ b/sw/source/uibase/shells/frmsh.cxx @@ -0,0 +1,1458 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/whiter.hxx> +#include <sfx2/viewfrm.hxx> +#include <basic/sbstar.hxx> +#include <svl/ptitem.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/lineitem.hxx> +#include <editeng/boxitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/objface.hxx> +#include <vcl/EnumContext.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/svdview.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <sal/log.hxx> + +#include <doc.hxx> +#include <drawdoc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <fmturl.hxx> +#include <fmtclds.hxx> +#include <fmtcnct.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> +#include <uitool.hxx> +#include <frmfmt.hxx> +#include <frmsh.hxx> +#include <frmmgr.hxx> +#include <edtwin.hxx> +#include <swdtflvr.hxx> +#include <viewopt.hxx> + +#include <cmdid.h> +#include <strings.hrc> +#include <swabstdlg.hxx> + +#include <svx/svxdlg.hxx> + +#include <docsh.hxx> +#include <svx/drawitem.hxx> +#include <memory> + +#define ShellClass_SwFrameShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <grfatr.hxx> +#include <fldmgr.hxx> +#include <flyfrm.hxx> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +// Prototypes +static void lcl_FrameGetMaxLineWidth(const SvxBorderLine* pBorderLine, SvxBorderLine& rBorderLine); +static const SwFrameFormat* lcl_GetFrameFormatByName(SwWrtShell const & rSh, const OUString& rName) +{ + const size_t nCount = rSh.GetFlyCount(FLYCNTTYPE_FRM); + for( size_t i = 0; i < nCount; ++i ) + { + const SwFrameFormat* pFormat = rSh.GetFlyNum(i, FLYCNTTYPE_FRM); + if(pFormat->GetName() == rName) + return pFormat; + } + return nullptr; +} + +SFX_IMPL_INTERFACE(SwFrameShell, SwBaseShell) + +void SwFrameShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("frame"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Frame_Toolbox); +} + +void SwFrameShell::ExecMove(SfxRequest& rReq) +{ + SwWrtShell& rSh = GetShell(); + sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case SID_SELECTALL: + rSh.SelAll(); + rReq.Done(); + break; + } +} + +void SwFrameShell::ExecField(const SfxRequest& rReq) +{ + SwWrtShell& rSh = GetShell(); + sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case FN_POSTIT: + SwFieldMgr aFieldMgr(&rSh); + rSh.InsertPostIt(aFieldMgr, rReq); + break; + } +} + +void SwFrameShell::Execute(SfxRequest &rReq) +{ + //First those who do not need FrameMgr. + SwWrtShell &rSh = GetShell(); + bool bMore = false; + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + sal_uInt16 nSlot = rReq.GetSlot(); + + switch ( nSlot ) + { + case FN_FRAME_TO_ANCHOR: + if ( rSh.IsFrameSelected() ) + { + rSh.GotoFlyAnchor(); + rSh.EnterStdMode(); + rSh.CallChgLnk(); + } + break; + case SID_FRAME_TO_TOP: + rSh.SelectionToTop(); + break; + + case SID_FRAME_TO_BOTTOM: + rSh.SelectionToBottom(); + break; + + case FN_FRAME_UP: + rSh.SelectionToTop( false ); + break; + + case FN_FRAME_DOWN: + rSh.SelectionToBottom( false ); + break; + case FN_INSERT_FRAME: + if (!pArgs) + { + // Frame already exists, open frame dialog for editing. + SfxStringItem aDefPage(FN_FORMAT_FRAME_DLG, "columns"); + rSh.GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + FN_FORMAT_FRAME_DLG, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aDefPage }); + + } + else + { + // Frame already exists, only the number of columns will be changed. + sal_uInt16 nCols = 1; + if(pArgs->GetItemState(SID_ATTR_COLUMNS, false, &pItem) == SfxItemState::SET) + nCols = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + + SfxItemSet aSet(GetPool(),svl::Items<RES_COL,RES_COL>{}); + rSh.GetFlyFrameAttr( aSet ); + SwFormatCol aCol(aSet.Get(RES_COL)); + // GutterWidth will not always passed, hence get firstly + // (see view2: Execute on this slot) + sal_uInt16 nGutterWidth = aCol.GetGutterWidth(); + if(!nCols ) + nCols++; + aCol.Init(nCols, nGutterWidth, aCol.GetWishWidth()); + aSet.Put(aCol); + // Template AutoUpdate + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + if(pFormat && pFormat->IsAutoUpdateFormat()) + { + rSh.AutoUpdateFrame(pFormat, aSet); + } + else + { + rSh.StartAllAction(); + rSh.SetFlyFrameAttr( aSet ); + rSh.SetModified(); + rSh.EndAllAction(); + } + + } + return; + + case SID_HYPERLINK_SETLINK: + { + if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_HYPERLINK_SETLINK, false, &pItem)) + { + const SvxHyperlinkItem& rHLinkItem = *static_cast<const SvxHyperlinkItem *>(pItem); + const OUString& rURL = rHLinkItem.GetURL(); + const OUString& rTarget = rHLinkItem.GetTargetFrame(); + + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_URL, RES_URL>{} ); + rSh.GetFlyFrameAttr( aSet ); + SwFormatURL aURL( aSet.Get( RES_URL ) ); + + OUString sOldName(rHLinkItem.GetName().toAsciiUpperCase()); + OUString sFlyName(rSh.GetFlyName().toAsciiUpperCase()); + if (sOldName != sFlyName) + { + OUString sName(sOldName); + sal_uInt16 i = 1; + while (rSh.FindFlyByName(sName)) + { + sName = sOldName + "_" + OUString::number(i++); + } + rSh.SetFlyName(sName); + } + aURL.SetURL( rURL, false ); + aURL.SetTargetFrameName(rTarget); + + aSet.Put( aURL ); + rSh.SetFlyFrameAttr( aSet ); + } + } + break; + + case FN_FRAME_CHAIN: + rSh.GetView().GetEditWin().SetChainMode( !rSh.GetView().GetEditWin().IsChainMode() ); + break; + + case FN_FRAME_UNCHAIN: + rSh.Unchain( *rSh.GetFlyFrameFormat() ); + GetView().GetViewFrame()->GetBindings().Invalidate(FN_FRAME_CHAIN); + break; + case FN_FORMAT_FOOTNOTE_DLG: + { + GetView().ExecFormatFootnote(); + break; + } + case FN_NUMBERING_OUTLINE_DLG: + { + GetView().ExecNumberingOutline(GetPool()); + rReq.Done(); + break; + } + case SID_OPEN_XML_FILTERSETTINGS: + { + HandleOpenXmlFilterSettings(rReq); + } + break; + case FN_WORDCOUNT_DIALOG: + { + GetView().UpdateWordCount(this, nSlot); + break; + } + default: bMore = true; + } + + if ( !bMore ) + { + return; + } + + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + bool bUpdateMgr = true; + bool bCopyToFormat = false; + switch ( nSlot ) + { + case SID_OBJECT_ALIGN_MIDDLE: + case FN_FRAME_ALIGN_VERT_CENTER: + aMgr.SetVertOrientation( text::VertOrientation::CENTER ); + break; + case SID_OBJECT_ALIGN_DOWN : + case FN_FRAME_ALIGN_VERT_BOTTOM: + aMgr.SetVertOrientation( text::VertOrientation::BOTTOM ); + break; + case SID_OBJECT_ALIGN_UP : + case FN_FRAME_ALIGN_VERT_TOP: + aMgr.SetVertOrientation( text::VertOrientation::TOP ); + break; + + case FN_FRAME_ALIGN_VERT_CHAR_CENTER: + aMgr.SetVertOrientation( text::VertOrientation::CHAR_CENTER ); + break; + + case FN_FRAME_ALIGN_VERT_CHAR_BOTTOM: + aMgr.SetVertOrientation( text::VertOrientation::CHAR_BOTTOM ); + break; + + case FN_FRAME_ALIGN_VERT_CHAR_TOP: + aMgr.SetVertOrientation( text::VertOrientation::CHAR_TOP ); + break; + + case FN_FRAME_ALIGN_VERT_ROW_CENTER: + aMgr.SetVertOrientation( text::VertOrientation::LINE_CENTER ); + break; + + case FN_FRAME_ALIGN_VERT_ROW_BOTTOM: + aMgr.SetVertOrientation( text::VertOrientation::LINE_BOTTOM ); + break; + + case FN_FRAME_ALIGN_VERT_ROW_TOP: + aMgr.SetVertOrientation( text::VertOrientation::LINE_TOP ); + break; + case SID_OBJECT_ALIGN_CENTER : + case FN_FRAME_ALIGN_HORZ_CENTER: + aMgr.SetHorzOrientation( text::HoriOrientation::CENTER ); + break; + case SID_OBJECT_ALIGN_RIGHT: + case FN_FRAME_ALIGN_HORZ_RIGHT: + aMgr.SetHorzOrientation( text::HoriOrientation::RIGHT ); + break; + case SID_OBJECT_ALIGN_LEFT: + case FN_FRAME_ALIGN_HORZ_LEFT: + aMgr.SetHorzOrientation( text::HoriOrientation::LEFT ); + break; + + case FN_SET_FRM_POSITION: + { + aMgr.SetAbsPos(static_cast<const SfxPointItem &>(pArgs->Get + (FN_SET_FRM_POSITION)).GetValue()); + } + break; + case SID_ATTR_BRUSH: + { + if(pArgs) + { + aMgr.SetAttrSet( *pArgs ); + bCopyToFormat = true; + } + } + break; + case SID_ATTR_ULSPACE: + case SID_ATTR_LRSPACE: + { + if(pArgs && SfxItemState::SET == pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem)) + { + aMgr.SetAttrSet( *pArgs ); + bCopyToFormat = true; + } + } + break; + + case SID_ATTR_TRANSFORM: + { + bool bApplyNewPos = false; + bool bApplyNewSize = false; + + Point aNewPos = aMgr.GetPos(); + if (pArgs && + SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_POS_X, false, &pItem)) + { + aNewPos.setX( static_cast<const SfxInt32Item*>(pItem)->GetValue() ); + bApplyNewPos = true; + } + if (pArgs && + SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_POS_Y, false, &pItem)) + { + aNewPos.setY( static_cast<const SfxInt32Item*>(pItem)->GetValue() ); + bApplyNewPos = true; + } + + Size aNewSize = aMgr.GetSize(); + if (pArgs && + SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_WIDTH, false, &pItem)) + { + aNewSize.setWidth( static_cast< const SfxUInt32Item* >(pItem)->GetValue() ); + bApplyNewSize = true; + } + + if (pArgs && + SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_HEIGHT, false, &pItem)) + { + aNewSize.setHeight( static_cast< const SfxUInt32Item* >(pItem)->GetValue() ); + bApplyNewSize = true; + } + + if (pArgs && (pArgs->HasItem(SID_ATTR_TRANSFORM_ANGLE) || pArgs->HasItem(SID_ATTR_TRANSFORM_DELTA_ANGLE))) + { + SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} ); + rSh.GetCurAttr(aSet); + const SwRotationGrf& rRotation = aSet.Get(RES_GRFATR_ROTATION); + const sal_uInt32 nOldRot(rRotation.GetValue()); + + if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_DELTA_ANGLE, false, &pItem)) + { + const sal_uInt32 nDeltaRot(static_cast<const SfxUInt32Item*>(pItem)->GetValue() / 10); + aMgr.SetRotation(nOldRot, nOldRot + nDeltaRot, rRotation.GetUnrotatedSize()); + } + + // RotGrfFlyFrame: Get Value and disable is in SwGrfShell::GetAttrStateForRotation, but the + // value setter uses SID_ATTR_TRANSFORM and a group of three values. Rotation is + // added now, so use it in this central place. Do no forget to convert angle from + // 100th degrees in SID_ATTR_TRANSFORM_ANGLE to 10th degrees in RES_GRFATR_ROTATION + if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TRANSFORM_ANGLE, false, &pItem)) + { + const sal_uInt32 nNewRot(static_cast<const SfxUInt32Item*>(pItem)->GetValue() / 10); + + // RotGrfFlyFrame: Rotation change here, SwFlyFrameAttrMgr aMgr is available + aMgr.SetRotation(nOldRot, nNewRot, rRotation.GetUnrotatedSize()); + } + } + + if (bApplyNewPos) + { + aMgr.SetAbsPos(aNewPos); + } + if ( bApplyNewSize ) + { + aMgr.SetSize( aNewSize ); + } + if (!bApplyNewPos && !bApplyNewSize) + { + bUpdateMgr = false; + } + + } + break; + + case FN_FORMAT_FRAME_DLG: + case FN_DRAW_WRAP_DLG: + { + const SelectionType nSel = rSh.GetSelectionType(); + if (nSel & SelectionType::Graphic) + { + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_GRAFIC_DLG); + bUpdateMgr = false; + } + else + { + SfxItemSet aSet( + GetPool(), + svl::Items< + RES_FRMATR_BEGIN, RES_FRMATR_END - 1, + // FillAttribute support: + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_DOCFRAME, SID_DOCFRAME, + SID_ATTR_BRUSH, SID_ATTR_BRUSH, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_LRSPACE, SID_ATTR_ULSPACE, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + // Items to hand over XPropertyList things like + // XColorList, XHatchList, XGradientList, and + // XBitmapList to the Area TabPage + SID_COLOR_TABLE, SID_PATTERN_LIST, + SID_HTML_MODE, SID_HTML_MODE, + FN_GET_PRINT_AREA, FN_GET_PRINT_AREA, + FN_SURROUND, FN_KEEP_ASPECT_RATIO, + FN_SET_FRM_ALT_NAME, FN_SET_FRM_ALT_NAME, + FN_UNO_DESCRIPTION, FN_UNO_DESCRIPTION, + FN_OLE_IS_MATH, FN_MATH_BASELINE_ALIGNMENT, + FN_PARAM_CHAIN_PREVIOUS, FN_PARAM_CHAIN_NEXT>{}); + + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + const SwDrawModel* pDrawModel = rSh.GetView().GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + pDrawModel->PutAreaListItems(aSet); + + const SwViewOption* pVOpt = rSh.GetViewOptions(); + if(nSel & SelectionType::Ole) + aSet.Put( SfxBoolItem(FN_KEEP_ASPECT_RATIO, pVOpt->IsKeepRatio()) ); + aSet.Put(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(GetView().GetDocShell()))); + aSet.Put(SfxStringItem(FN_SET_FRM_NAME, rSh.GetFlyName())); + aSet.Put(SfxStringItem(FN_UNO_DESCRIPTION, rSh.GetObjDescription())); + if( nSel & SelectionType::Ole ) + { + // #i73249# + aSet.Put( SfxStringItem( FN_SET_FRM_ALT_NAME, rSh.GetObjTitle() ) ); + } + + const SwRect &rPg = rSh.GetAnyCurRect(CurRectType::Page); + SwFormatFrameSize aFrameSize(SwFrameSize::Variable, rPg.Width(), rPg.Height()); + aFrameSize.SetWhich(GetPool().GetWhich(SID_ATTR_PAGE_SIZE)); + aSet.Put(aFrameSize); + + const SwRect &rPr = rSh.GetAnyCurRect(CurRectType::PagePrt); + SwFormatFrameSize aPrtSize(SwFrameSize::Variable, rPr.Width(), rPr.Height()); + aPrtSize.SetWhich(GetPool().GetWhich(FN_GET_PRINT_AREA)); + aSet.Put(aPrtSize); + + aSet.Put(aMgr.GetAttrSet()); + aSet.SetParent( aMgr.GetAttrSet().GetParent() ); + + // On % values initialize size + SwFormatFrameSize& rSize = const_cast<SwFormatFrameSize&>(aSet.Get(RES_FRM_SIZE)); + if (rSize.GetWidthPercent() && rSize.GetWidthPercent() != SwFormatFrameSize::SYNCED) + rSize.SetWidth(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Width()); + if (rSize.GetHeightPercent() && rSize.GetHeightPercent() != SwFormatFrameSize::SYNCED) + rSize.SetHeight(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Height()); + + // disable vertical positioning for Math Objects anchored 'as char' if baseline alignment is activated + aSet.Put( SfxBoolItem( FN_MATH_BASELINE_ALIGNMENT, + rSh.GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ) ) ); + const uno::Reference < embed::XEmbeddedObject > xObj( rSh.GetOleRef() ); + aSet.Put( SfxBoolItem( FN_OLE_IS_MATH, xObj.is() && SotExchange::IsMath( xObj->getClassID() ) ) ); + + OString sDefPage; + if(pArgs && pArgs->GetItemState(FN_FORMAT_FRAME_DLG, false, &pItem) == SfxItemState::SET) + sDefPage = OUStringToOString(static_cast<const SfxStringItem *>(pItem)->GetValue(), RTL_TEXTENCODING_UTF8); + + aSet.Put(SfxFrameItem( SID_DOCFRAME, &GetView().GetViewFrame()->GetFrame())); + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric) )); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateFrameTabDialog( + nSel & SelectionType::Graphic ? OUString("PictureDialog") : + nSel & SelectionType::Ole ? OUString("ObjectDialog"): + OUString("FrameDialog"), + GetView().GetViewFrame(), + GetView().GetFrameWeld(), + aSet, + false, + sDefPage)); + + if ( nSlot == FN_DRAW_WRAP_DLG ) + { + pDlg->SetCurPageId("wrap"); + } + + if ( pDlg->Execute() ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + if(pOutSet) + { + rReq.Done(*pOutSet); + if(nSel & SelectionType::Ole && + SfxItemState::SET == pOutSet->GetItemState(FN_KEEP_ASPECT_RATIO, true, &pItem)) + { + SwViewOption aUsrPref( *pVOpt ); + aUsrPref.SetKeepRatio(static_cast<const SfxBoolItem*>(pItem)->GetValue()); + SW_MOD()->ApplyUsrPref(aUsrPref, &GetView()); + } + if (SfxItemState::SET == pOutSet->GetItemState(FN_SET_FRM_ALT_NAME, true, &pItem)) + { + // #i73249# + rSh.SetObjTitle(static_cast<const SfxStringItem*>(pItem)->GetValue()); + } + if (SfxItemState::SET == pOutSet->GetItemState(FN_UNO_DESCRIPTION, true, &pItem)) + rSh.SetObjDescription(static_cast<const SfxStringItem*>(pItem)->GetValue()); + + // Template AutoUpdate + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + if(pFormat && pFormat->IsAutoUpdateFormat()) + { + rSh.AutoUpdateFrame(pFormat, *pOutSet); + // Anything which is not supported by the format must be set hard. + if(SfxItemState::SET == pOutSet->GetItemState(FN_SET_FRM_NAME, false, &pItem)) + rSh.SetFlyName(static_cast<const SfxStringItem*>(pItem)->GetValue()); + SfxItemSet aShellSet( + GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR>{}); + aShellSet.Put(*pOutSet); + aMgr.SetAttrSet(aShellSet); + if(SfxItemState::SET == pOutSet->GetItemState(FN_SET_FRM_NAME, false, &pItem)) + rSh.SetFlyName(static_cast<const SfxStringItem*>(pItem)->GetValue()); + } + else + aMgr.SetAttrSet( *pOutSet ); + + const SwFrameFormat* pCurrFlyFormat = rSh.GetFlyFrameFormat(); + if(SfxItemState::SET == + pOutSet->GetItemState(FN_PARAM_CHAIN_PREVIOUS, + false, &pItem)) + { + rSh.HideChainMarker(); + + OUString sPrevName = + static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SwFormatChain &rChain = pCurrFlyFormat->GetChain(); + //needs cast - no non-const method available + SwFlyFrameFormat* pFlyFormat = + rChain.GetPrev(); + if(pFlyFormat) + { + if (pFlyFormat->GetName() != sPrevName) + { + rSh.Unchain(*pFlyFormat); + } + else + sPrevName.clear(); + } + + if (!sPrevName.isEmpty()) + { + //needs cast - no non-const method available + SwFrameFormat* pPrevFormat = const_cast<SwFrameFormat*>( + lcl_GetFrameFormatByName(rSh, sPrevName)); + SAL_WARN_IF(!pPrevFormat, "sw.ui", "No frame found!"); + if(pPrevFormat) + { + rSh.Chain(*pPrevFormat, *pCurrFlyFormat); + } + } + rSh.SetChainMarker(); + } + if(SfxItemState::SET == + pOutSet->GetItemState(FN_PARAM_CHAIN_NEXT, false, + &pItem)) + { + rSh.HideChainMarker(); + OUString sNextName = + static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SwFormatChain &rChain = pCurrFlyFormat->GetChain(); + //needs cast - no non-const method available + SwFlyFrameFormat* pFlyFormat = + rChain.GetNext(); + if(pFlyFormat) + { + if (pFlyFormat->GetName() != sNextName) + { + rSh.Unchain(*const_cast<SwFlyFrameFormat*>(static_cast<const SwFlyFrameFormat*>( pCurrFlyFormat))); + } + else + sNextName.clear(); + } + + if (!sNextName.isEmpty()) + { + //needs cast - no non-const method available + SwFrameFormat* pNextFormat = const_cast<SwFrameFormat*>( + lcl_GetFrameFormatByName(rSh, sNextName)); + SAL_WARN_IF(!pNextFormat, "sw.ui", "No frame found!"); + if(pNextFormat) + { + rSh.Chain(*const_cast<SwFrameFormat*>( + pCurrFlyFormat), *pNextFormat); + } + } + rSh.SetChainMarker(); + } + } + } + else + bUpdateMgr = false; + } + } + break; + case FN_FRAME_MIRROR_ON_EVEN_PAGES: + { + SwFormatHoriOrient aHori(aMgr.GetHoriOrient()); + bool bMirror = !aHori.IsPosToggle(); + aHori.SetPosToggle(bMirror); + SfxItemSet aSet(GetPool(), svl::Items<RES_HORI_ORIENT, RES_HORI_ORIENT>{}); + aSet.Put(aHori); + aMgr.SetAttrSet(aSet); + bCopyToFormat = true; + rReq.SetReturnValue(SfxBoolItem(nSlot, bMirror)); + } + break; + case FN_NAME_SHAPE: + { + bUpdateMgr = false; + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + if ( pSdrView && + pSdrView->GetMarkedObjectCount() == 1 ) + { + OUString aName(rSh.GetFlyName()); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectNameDialog> pDlg( + pFact->CreateSvxObjectNameDialog(GetView().GetFrameWeld(), aName)); + + if ( pDlg->Execute() == RET_OK ) + { + pDlg->GetName(aName); + rSh.SetFlyName(aName); + } + } + } + break; + // #i73249# + case FN_TITLE_DESCRIPTION_SHAPE: + { + bUpdateMgr = false; + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + if ( pSdrView && + pSdrView->GetMarkedObjectCount() == 1 ) + { + OUString aDescription(rSh.GetObjDescription()); + OUString aTitle(rSh.GetObjTitle()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg( + pFact->CreateSvxObjectTitleDescDialog(GetView().GetFrameWeld(), + aTitle, aDescription )); + + if ( pDlg->Execute() == RET_OK ) + { + pDlg->GetDescription(aDescription); + pDlg->GetTitle(aTitle); + + rSh.SetObjDescription(aDescription); + rSh.SetObjTitle(aTitle); + } + } + } + break; + default: + assert(!"wrong dispatcher"); + return; + } + if ( bUpdateMgr ) + { + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + if ( bCopyToFormat && pFormat && pFormat->IsAutoUpdateFormat() ) + { + rSh.AutoUpdateFrame(pFormat, aMgr.GetAttrSet()); + } + else + { + aMgr.UpdateFlyFrame(); + } + } + +} + +void SwFrameShell::GetState(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetShell(); + bool bHtmlMode = 0 != ::GetHtmlMode(rSh.GetView().GetDocShell()); + if (rSh.IsFrameSelected()) + { + SfxItemSet aSet( + rSh.GetAttrPool(), + svl::Items< + RES_LR_SPACE, RES_UL_SPACE, + RES_PRINT, RES_HORI_ORIENT>{}); + rSh.GetFlyFrameAttr( aSet ); + + bool bProtect = rSh.IsSelObjProtected(FlyProtectFlags::Pos) != FlyProtectFlags::NONE; + bool bParentCntProt = rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + bProtect |= bParentCntProt; + + const FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,true); + SwFlyFrameAttrMgr aMgr( false, &rSh, Frmmgr_Type::NONE, nullptr ); + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + switch ( nWhich ) + { + case RES_FRM_SIZE: + { + const SwFormatFrameSize& aSz(aMgr.GetFrameSize()); + rSet.Put(aSz); + } + break; + case RES_VERT_ORIENT: + case RES_HORI_ORIENT: + case SID_ATTR_ULSPACE: + case SID_ATTR_LRSPACE: + case RES_LR_SPACE: + case RES_UL_SPACE: + case RES_PROTECT: + case RES_OPAQUE: + case RES_PRINT: + case RES_SURROUND: + { + rSet.Put(aSet.Get(GetPool().GetWhich(nWhich))); + } + break; + case SID_OBJECT_ALIGN: + { + if ( bProtect ) + rSet.DisableItem( nWhich ); + } + break; + case SID_OBJECT_ALIGN_LEFT : + case SID_OBJECT_ALIGN_CENTER : + case SID_OBJECT_ALIGN_RIGHT : + case FN_FRAME_ALIGN_HORZ_CENTER: + case FN_FRAME_ALIGN_HORZ_RIGHT: + case FN_FRAME_ALIGN_HORZ_LEFT: + if ( (eFrameType & FrameTypeFlags::FLY_INCNT) || + bProtect || + ((nWhich == FN_FRAME_ALIGN_HORZ_CENTER || nWhich == SID_OBJECT_ALIGN_CENTER) && + bHtmlMode )) + { + rSet.DisableItem( nWhich ); + } + else + { + sal_Int16 nHoriOrient = -1; + switch(nWhich) + { + case SID_OBJECT_ALIGN_LEFT: + nHoriOrient = text::HoriOrientation::LEFT; + break; + case SID_OBJECT_ALIGN_CENTER: + nHoriOrient = text::HoriOrientation::CENTER; + break; + case SID_OBJECT_ALIGN_RIGHT: + nHoriOrient = text::HoriOrientation::RIGHT; + break; + default: + break; + } + SwFormatHoriOrient aHOrient(aMgr.GetHoriOrient()); + if (nHoriOrient != -1) + rSet.Put(SfxBoolItem(nWhich, nHoriOrient == aHOrient.GetHoriOrient())); + } + break; + case FN_FRAME_ALIGN_VERT_ROW_TOP: + case FN_FRAME_ALIGN_VERT_ROW_CENTER: + case FN_FRAME_ALIGN_VERT_ROW_BOTTOM: + case FN_FRAME_ALIGN_VERT_CHAR_TOP: + case FN_FRAME_ALIGN_VERT_CHAR_CENTER: + case FN_FRAME_ALIGN_VERT_CHAR_BOTTOM: + if ( !(eFrameType & FrameTypeFlags::FLY_INCNT) || bProtect + || (bHtmlMode && FN_FRAME_ALIGN_VERT_CHAR_BOTTOM == nWhich) ) + rSet.DisableItem( nWhich ); + break; + + case SID_OBJECT_ALIGN_UP : + case SID_OBJECT_ALIGN_MIDDLE : + case SID_OBJECT_ALIGN_DOWN : + + case FN_FRAME_ALIGN_VERT_TOP: + case FN_FRAME_ALIGN_VERT_CENTER: + case FN_FRAME_ALIGN_VERT_BOTTOM: + if ( bProtect || (bHtmlMode && eFrameType & FrameTypeFlags::FLY_ATCNT)) + rSet.DisableItem( nWhich ); + else + { + // These slots need different labels depending on whether they are anchored in a character + // or on a paragraph/page etc. + OUString sNewLabel; + if (eFrameType & FrameTypeFlags::FLY_INCNT) + { + switch (nWhich) + { + case SID_OBJECT_ALIGN_UP : + case FN_FRAME_ALIGN_VERT_TOP: + sNewLabel = SwResId(STR_FRMUI_TOP_BASE); + break; + case SID_OBJECT_ALIGN_MIDDLE : + case FN_FRAME_ALIGN_VERT_CENTER: + sNewLabel = SwResId(STR_FRMUI_CENTER_BASE); + break; + case SID_OBJECT_ALIGN_DOWN : + case FN_FRAME_ALIGN_VERT_BOTTOM: + if(!bHtmlMode) + sNewLabel = SwResId(STR_FRMUI_BOTTOM_BASE); + else + rSet.DisableItem( nWhich ); + break; + } + } + else + { + if (nWhich != FN_FRAME_ALIGN_VERT_TOP && + nWhich != SID_OBJECT_ALIGN_UP ) + { + if (aMgr.GetAnchor() == RndStdIds::FLY_AT_FLY) + { + const SwFrameFormat* pFormat = rSh.IsFlyInFly(); + if (pFormat) + { + const SwFormatFrameSize& rFrameSz = pFormat->GetFrameSize(); + if (rFrameSz.GetHeightSizeType() != SwFrameSize::Fixed) + { + rSet.DisableItem( nWhich ); + break; + } + } + } + } + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(GetFrame()->GetFrame().GetFrameInterface())); + switch (nWhich) + { + case SID_OBJECT_ALIGN_UP : + case FN_FRAME_ALIGN_VERT_TOP: + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:AlignTop", aModuleName); + sNewLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + break; + } + case SID_OBJECT_ALIGN_MIDDLE: + case FN_FRAME_ALIGN_VERT_CENTER: + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:AlignVerticalCenter", aModuleName); + sNewLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + break; + } + case SID_OBJECT_ALIGN_DOWN: + case FN_FRAME_ALIGN_VERT_BOTTOM: + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:AlignBottom", aModuleName); + sNewLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + break; + } + } + } + if ( !sNewLabel.isEmpty() ) + rSet.Put( SfxStringItem( nWhich, sNewLabel )); + } + break; + case SID_HYPERLINK_GETLINK: + { + SvxHyperlinkItem aHLinkItem; + const SfxPoolItem* pItem; + + SfxItemSet aURLSet(GetPool(), svl::Items<RES_URL, RES_URL>{}); + rSh.GetFlyFrameAttr( aURLSet ); + + if(SfxItemState::SET == aURLSet.GetItemState(RES_URL, true, &pItem)) + { + const SwFormatURL* pFormatURL = static_cast<const SwFormatURL*>(pItem); + aHLinkItem.SetURL(pFormatURL->GetURL()); + aHLinkItem.SetTargetFrame(pFormatURL->GetTargetFrameName()); + aHLinkItem.SetName(rSh.GetFlyName()); + } + + aHLinkItem.SetInsertMode(static_cast<SvxLinkInsertMode>(aHLinkItem.GetInsertMode() | + (bHtmlMode ? HLINK_HTMLMODE : 0))); + + rSet.Put(aHLinkItem); + } + break; + + case FN_FRAME_CHAIN: + { + const SelectionType nSel = rSh.GetSelectionType(); + if (nSel & SelectionType::Graphic || nSel & SelectionType::Ole) + rSet.DisableItem( FN_FRAME_CHAIN ); + else + { + const SwFrameFormat *pFormat = rSh.GetFlyFrameFormat(); + if ( bParentCntProt || rSh.GetView().GetEditWin().GetApplyTemplate() || + !pFormat || pFormat->GetChain().GetNext() ) + { + rSet.DisableItem( FN_FRAME_CHAIN ); + } + else + { + bool bChainMode = rSh.GetView().GetEditWin().IsChainMode(); + rSet.Put( SfxBoolItem( FN_FRAME_CHAIN, bChainMode ) ); + } + } + } + break; + case FN_FRAME_UNCHAIN: + { + const SelectionType nSel = rSh.GetSelectionType(); + if (nSel & SelectionType::Graphic || nSel & SelectionType::Ole) + rSet.DisableItem( FN_FRAME_UNCHAIN ); + else + { + const SwFrameFormat *pFormat = rSh.GetFlyFrameFormat(); + if ( bParentCntProt || rSh.GetView().GetEditWin().GetApplyTemplate() || + !pFormat || !pFormat->GetChain().GetNext() ) + { + rSet.DisableItem( FN_FRAME_UNCHAIN ); + } + } + } + break; + case SID_FRAME_TO_TOP: + case SID_FRAME_TO_BOTTOM: + case FN_FRAME_UP: + case FN_FRAME_DOWN: + if ( bParentCntProt ) + rSet.DisableItem( nWhich ); + break; + + case SID_ATTR_TRANSFORM: + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_ATTR_TRANSFORM_PROTECT_SIZE: + { + const FlyProtectFlags eProtection = rSh.IsSelObjProtected( FlyProtectFlags::Size ); + if ( ( eProtection & FlyProtectFlags::Content ) || + ( eProtection & FlyProtectFlags::Size ) ) + { + rSet.Put( SfxBoolItem( SID_ATTR_TRANSFORM_PROTECT_SIZE, true ) ); + } + else + { + rSet.Put( SfxBoolItem( SID_ATTR_TRANSFORM_PROTECT_SIZE, false ) ); + } + } + break; + + case SID_ATTR_TRANSFORM_WIDTH: + { + rSet.Put( SfxUInt32Item( SID_ATTR_TRANSFORM_WIDTH, aMgr.GetSize().getWidth() ) ); + } + break; + + case SID_ATTR_TRANSFORM_HEIGHT: + { + rSet.Put( SfxUInt32Item( SID_ATTR_TRANSFORM_HEIGHT, aMgr.GetSize().getHeight() ) ); + } + break; + + case FN_FORMAT_FRAME_DLG: + { + const SelectionType nSel = rSh.GetSelectionType(); + if ( bParentCntProt || nSel & SelectionType::Graphic) + rSet.DisableItem( nWhich ); + } + break; + // #i73249# + case FN_TITLE_DESCRIPTION_SHAPE: + case FN_NAME_SHAPE: + { + SwWrtShell &rWrtSh = GetShell(); + SdrView* pSdrView = rWrtSh.GetDrawViewWithValidMarkList(); + if ( !pSdrView || + pSdrView->GetMarkedObjectCount() != 1 ) + { + rSet.DisableItem( nWhich ); + } + } + break; + + case FN_POSTIT: + { + SwFlyFrame* pFly = rSh.GetSelectedFlyFrame(); + if (pFly) + { + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat) + { + RndStdIds eAnchorId = pFormat->GetAnchor().GetAnchorId(); + // SwWrtShell::InsertPostIt() only works on as-char and at-char anchored + // images. + if (eAnchorId != RndStdIds::FLY_AS_CHAR && eAnchorId != RndStdIds::FLY_AT_CHAR) + { + rSet.DisableItem(nWhich); + } + } + } + } + break; + + default: + /* do nothing */; + break; + } + nWhich = aIter.NextWhich(); + } + } +} + +SwFrameShell::SwFrameShell(SwView &_rView) : + SwBaseShell( _rView ) +{ + SetName("Frame"); + + // #96392# Use this to announce it is the frame shell who creates the selection. + SwTransferable::CreateSelection( _rView.GetWrtShell(), this ); + + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Frame)); +} + +SwFrameShell::~SwFrameShell() +{ + // #96392# Only clear the selection if it was this frame shell who created it. + SwTransferable::ClearSelection( GetShell(), this ); +} + +void SwFrameShell::ExecFrameStyle(SfxRequest const & rReq) +{ + SwWrtShell &rSh = GetShell(); + bool bDefault = false; + if (!rSh.IsFrameSelected()) + return; + // At first pick the default BoxItem out of the pool. + // If unequal to regular box item, then it has already + // been changed (New one is no default). + const SvxBoxItem* pPoolBoxItem = ::GetDfltAttr(RES_BOX); + + const SfxItemSet *pArgs = rReq.GetArgs(); + SfxItemSet aFrameSet(rSh.GetAttrPool(), svl::Items<RES_BOX, RES_BOX>{}); + + rSh.GetFlyFrameAttr( aFrameSet ); + const SvxBoxItem& rBoxItem = aFrameSet.Get(RES_BOX); + + if (pPoolBoxItem == &rBoxItem) + bDefault = true; + + std::unique_ptr<SvxBoxItem> aBoxItem(rBoxItem.Clone()); + + SvxBorderLine aBorderLine; + const SfxPoolItem *pItem = nullptr; + + if(pArgs) // Any controller can sometimes deliver nothing #48169# + { + switch (rReq.GetSlot()) + { + case SID_ATTR_BORDER: + { + if (pArgs->GetItemState(RES_BOX, true, &pItem) == SfxItemState::SET) + { + std::unique_ptr<SvxBoxItem> aNewBox(static_cast<SvxBoxItem*>(pItem->Clone())); + const SvxBorderLine* pBorderLine; + + if ((pBorderLine = aBoxItem->GetTop()) != nullptr) + lcl_FrameGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = aBoxItem->GetBottom()) != nullptr) + lcl_FrameGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = aBoxItem->GetLeft()) != nullptr) + lcl_FrameGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = aBoxItem->GetRight()) != nullptr) + lcl_FrameGetMaxLineWidth(pBorderLine, aBorderLine); + + if(aBorderLine.GetOutWidth() == 0) + { + aBorderLine.SetBorderLineStyle( + SvxBorderLineStyle::SOLID); + aBorderLine.SetWidth( DEF_LINE_WIDTH_0 ); + } + //Set distance only if the request is received from the controller. + +#if HAVE_FEATURE_SCRIPTING + if(!StarBASIC::IsRunning()) +#endif + { + // TODO: should this copy 4 individual Dist instead? + aNewBox->SetAllDistances(rBoxItem.GetSmallestDistance()); + } + + aBoxItem = std::move(aNewBox); + + if( aBoxItem->GetTop() != nullptr ) + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::TOP); + if( aBoxItem->GetBottom() != nullptr ) + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + if( aBoxItem->GetLeft() != nullptr ) + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::LEFT); + if( aBoxItem->GetRight() != nullptr ) + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::RIGHT); + } + } + break; + + case SID_FRAME_LINESTYLE: + { + if (pArgs->GetItemState(SID_FRAME_LINESTYLE, false, &pItem) == SfxItemState::SET) + { + const SvxLineItem* pLineItem = + static_cast<const SvxLineItem*>(pItem); + + if ( pLineItem->GetLine() ) + { + aBorderLine = *(pLineItem->GetLine()); + + if (!aBoxItem->GetTop() && !aBoxItem->GetBottom() && + !aBoxItem->GetLeft() && !aBoxItem->GetRight()) + { + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::TOP); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::LEFT); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::RIGHT); + } + else + { + if( aBoxItem->GetTop() ) + { + aBorderLine.SetColor( aBoxItem->GetTop()->GetColor() ); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::TOP); + } + if( aBoxItem->GetBottom() ) + { + aBorderLine.SetColor( aBoxItem->GetBottom()->GetColor()); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + } + if( aBoxItem->GetLeft() ) + { + aBorderLine.SetColor( aBoxItem->GetLeft()->GetColor()); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::LEFT); + } + if( aBoxItem->GetRight() ) + { + aBorderLine.SetColor(aBoxItem->GetRight()->GetColor()); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::RIGHT); + } + } + } + else + { + aBoxItem->SetLine(nullptr, SvxBoxItemLine::TOP); + aBoxItem->SetLine(nullptr, SvxBoxItemLine::BOTTOM); + aBoxItem->SetLine(nullptr, SvxBoxItemLine::LEFT); + aBoxItem->SetLine(nullptr, SvxBoxItemLine::RIGHT); + } + } + } + break; + + case SID_FRAME_LINECOLOR: + { + if (pArgs->GetItemState(SID_FRAME_LINECOLOR, false, &pItem) == SfxItemState::SET) + { + const Color& rNewColor = static_cast<const SvxColorItem*>(pItem)->GetValue(); + + if (!aBoxItem->GetTop() && !aBoxItem->GetBottom() && + !aBoxItem->GetLeft() && !aBoxItem->GetRight()) + { + aBorderLine.SetColor( rNewColor ); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::TOP); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::LEFT); + aBoxItem->SetLine(&aBorderLine, SvxBoxItemLine::RIGHT); + } + else + { + if ( aBoxItem->GetTop() ) + const_cast<SvxBorderLine*>(aBoxItem->GetTop())->SetColor( rNewColor ); + if ( aBoxItem->GetBottom() ) + const_cast<SvxBorderLine*>(aBoxItem->GetBottom())->SetColor( rNewColor ); + if ( aBoxItem->GetLeft() ) + const_cast<SvxBorderLine*>(aBoxItem->GetLeft())->SetColor( rNewColor ); + if ( aBoxItem->GetRight() ) + const_cast<SvxBorderLine*>(aBoxItem->GetRight())->SetColor( rNewColor ); + } + } + } + break; + } + } + if (bDefault && (aBoxItem->GetTop() || aBoxItem->GetBottom() || + aBoxItem->GetLeft() || aBoxItem->GetRight())) + { + aBoxItem->SetAllDistances(MIN_BORDER_DIST); + } + aFrameSet.Put( *aBoxItem ); + // Template AutoUpdate + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + if(pFormat && pFormat->IsAutoUpdateFormat()) + { + rSh.AutoUpdateFrame(pFormat, aFrameSet); + } + else + rSh.SetFlyFrameAttr( aFrameSet ); + +} + +static void lcl_FrameGetMaxLineWidth(const SvxBorderLine* pBorderLine, SvxBorderLine& rBorderLine) +{ + if(pBorderLine->GetWidth() > rBorderLine.GetWidth()) + rBorderLine.SetWidth(pBorderLine->GetWidth()); + + rBorderLine.SetBorderLineStyle(pBorderLine->GetBorderLineStyle()); + rBorderLine.SetColor(pBorderLine->GetColor()); +} + +void SwFrameShell::GetLineStyleState(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + bool bParentCntProt = rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ) != FlyProtectFlags::NONE; + + if (bParentCntProt) + { + if (rSh.IsFrameSelected()) + rSet.DisableItem( SID_FRAME_LINECOLOR ); + + rSet.DisableItem( SID_ATTR_BORDER ); + rSet.DisableItem( SID_FRAME_LINESTYLE ); + } + else + { + if (rSh.IsFrameSelected()) + { + SfxItemSet aFrameSet( rSh.GetAttrPool(), svl::Items<RES_BOX, RES_BOX>{} ); + + rSh.GetFlyFrameAttr(aFrameSet); + + const SvxBorderLine* pLine = aFrameSet.Get(RES_BOX).GetTop(); + rSet.Put(SvxColorItem(pLine ? pLine->GetColor() : Color(), SID_FRAME_LINECOLOR)); + } + } +} + +void SwFrameShell::StateInsert(SfxItemSet &rSet) +{ + const SelectionType nSel = GetShell().GetSelectionType(); + if ( (nSel & SelectionType::Graphic) + || (nSel & SelectionType::Ole) ) + { + rSet.DisableItem(FN_INSERT_FRAME); + } + else if ( GetShell().CursorInsideInputField() ) + { + rSet.DisableItem(FN_INSERT_FRAME); + } +} + +void SwFrameShell::GetDrawAttrStateTextFrame(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + + if(rSh.IsFrameSelected()) + { + rSh.GetFlyFrameAttr(rSet); + } + else + { + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(pSdrView) + { + rSet.Put(pSdrView->GetDefaultAttr()); + } + } +} + +void SwFrameShell::ExecDrawAttrArgsTextFrame(SfxRequest const & rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + SwWrtShell& rSh = GetShell(); + + if(pArgs) + { + if(rSh.IsFrameSelected()) + { + rSh.SetFlyFrameAttr(const_cast< SfxItemSet& >(*pArgs)); + } + else + { + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(pSdrView) + { + pSdrView->SetDefaultAttr(*pArgs, false); + } + } + } + else + { + SfxDispatcher* pDis = rSh.GetView().GetViewFrame()->GetDispatcher(); + + switch(rReq.GetSlot()) + { + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + { + pDis->Execute(SID_ATTRIBUTES_AREA); + break; + } + } + } +} + +void SwFrameShell::ExecDrawDlgTextFrame(SfxRequest const & rReq) +{ + switch(rReq.GetSlot()) + { + case SID_ATTRIBUTES_AREA: + { + SwWrtShell& rSh = GetShell(); + + if(rSh.IsFrameSelected()) + { + SdrModel* pDoc = rSh.GetDrawView()->GetModel(); + SfxItemSet aNewAttr(pDoc->GetItemPool()); + + // get attributes from FlyFrame + rSh.GetFlyFrameAttr(aNewAttr); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr<AbstractSvxAreaTabDialog> pDlg(pFact->CreateSvxAreaTabDialog( + GetView().GetFrameWeld(), + &aNewAttr, + pDoc, + false)); + + pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult){ + if(nResult == RET_OK) + { + // set attributes at FlyFrame + GetShell().SetFlyFrameAttr(const_cast< SfxItemSet& >(*pDlg->GetOutputItemSet())); + + static sal_uInt16 aInval[] = + { + SID_ATTR_FILL_STYLE, + SID_ATTR_FILL_COLOR, + SID_ATTR_FILL_TRANSPARENCE, + SID_ATTR_FILL_FLOATTRANSPARENCE, + 0 + }; + + SfxBindings &rBnd = GetView().GetViewFrame()->GetBindings(); + + rBnd.Invalidate(aInval); + rBnd.Update(SID_ATTR_FILL_STYLE); + rBnd.Update(SID_ATTR_FILL_COLOR); + rBnd.Update(SID_ATTR_FILL_TRANSPARENCE); + rBnd.Update(SID_ATTR_FILL_FLOATTRANSPARENCE); + } + pDlg->disposeOnce(); + }); + } + + break; + } + } +} + +void SwFrameShell::DisableStateTextFrame(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + + while(nWhich) + { + switch(nWhich) + { + case SID_ATTRIBUTES_AREA: + { + SwWrtShell& rSh = GetShell(); + + if(!rSh.IsFrameSelected()) + { + rSet.DisableItem(nWhich); + } + + break; + } + default: + { + rSet.DisableItem(nWhich); + break; + } + } + + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/grfsh.cxx b/sw/source/uibase/shells/grfsh.cxx new file mode 100644 index 000000000..f48e3d79e --- /dev/null +++ b/sw/source/uibase/shells/grfsh.cxx @@ -0,0 +1,1016 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <hintids.hxx> +#include <tools/urlobj.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <svl/urihelper.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/sizeitem.hxx> +#include <sfx2/request.hxx> +#include <vcl/EnumContext.hxx> +#include <sfx2/htmlmode.hxx> +#include <svx/svdview.hxx> +#include <editeng/brushitem.hxx> +#include <svx/grfflt.hxx> +#include <svx/compressgraphicdialog.hxx> +#include <svx/tbxcolor.hxx> +#include <drawdoc.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <swmodule.hxx> +#include <swundo.hxx> +#include <uitool.hxx> +#include <docsh.hxx> +#include <grfsh.hxx> +#include <frmmgr.hxx> +#include <frmfmt.hxx> +#include <grfatr.hxx> +#include <swwait.hxx> +#include <svx/extedit.hxx> +#include <svx/graphichelper.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <svx/drawitem.hxx> +#define ShellClass_SwGrfShell + +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <swabstdlg.hxx> +#include <unocrsr.hxx> +#include <memory> + +#define TOOLBOX_NAME "colorbar" + +class SwGrfShell::SwExternalToolEdit + : public ExternalToolEdit +{ +private: + SwWrtShell *const m_pShell; + std::shared_ptr<SwUnoCursor> const m_pCursor; + +public: + explicit SwExternalToolEdit(SwWrtShell *const pShell) + : m_pShell(pShell) + , m_pCursor( // need only Point, must point to SwGrfNode + pShell->GetDoc()->CreateUnoCursor( + *pShell->GetCurrentShellCursor().GetPoint())) + { + } + + virtual void Update(Graphic & rGraphic) override + { + DBG_TESTSOLARMUTEX(); + m_pShell->Push(); + m_pShell->GetCurrentShellCursor().DeleteMark(); + *m_pShell->GetCurrentShellCursor().GetPoint() = *m_pCursor->GetPoint(); + m_pShell->ReRead(OUString(), OUString(), &rGraphic); + m_pShell->Pop(); + } +}; + +SFX_IMPL_INTERFACE(SwGrfShell, SwBaseShell) + +void SwGrfShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("graphic"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Grafik_Toolbox); +} + +void SwGrfShell::Execute(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + + sal_uInt16 nSlot = rReq.GetSlot(); + switch(nSlot) + { + case SID_OBJECT_ROTATE: + { + // RotGrfFlyFrame: start rotation when possible + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(rSh.IsRotationOfSwGrfNodePossible() && pSdrView->IsRotateAllowed()) + { + if(GetView().IsDrawRotate()) + { + rSh.SetDragMode(SdrDragMode::Move); + } + else + { + rSh.SetDragMode(SdrDragMode::Rotate); + } + + GetView().FlipDrawRotate(); + } + } + break; + + case SID_TWAIN_TRANSFER: + { + GetView().ExecuteScan( rReq ); + break; + } + + case SID_SAVE_GRAPHIC: + { + GraphicAttr aGraphicAttr; + rSh.GetGraphicAttr(aGraphicAttr); + + short nState = RET_CANCEL; + if (aGraphicAttr != GraphicAttr()) // the image has been modified + { + weld::Window* pWin = GetView().GetFrameWeld(); + if (pWin) + { + nState = GraphicHelper::HasToSaveTransformedImage(pWin); + } + } + else + { + nState = RET_NO; + } + + if (nState == RET_YES) + { + const GraphicObject* pGraphicObj = rSh.GetGraphicObj(); + if (pGraphicObj) + { + Graphic aGraphic = pGraphicObj->GetTransformedGraphic(pGraphicObj->GetPrefSize(), pGraphicObj->GetPrefMapMode(), aGraphicAttr); + OUString sGrfNm; + OUString sFilterNm; + rSh.GetGrfNms( &sGrfNm, &sFilterNm ); + GraphicHelper::ExportGraphic(GetView().GetFrameWeld(), aGraphic, sGrfNm); + } + } + else if (nState == RET_NO) + { + const Graphic *pGraphic; + if(nullptr != (pGraphic = rSh.GetGraphic())) + { + OUString sGrfNm; + OUString sFilterNm; + rSh.GetGrfNms( &sGrfNm, &sFilterNm ); + GraphicHelper::ExportGraphic(GetView().GetFrameWeld(), *pGraphic, sGrfNm); + } + } + } + break; + + case SID_COMPRESS_GRAPHIC: + { + const Graphic* pGraphic = rSh.GetGraphic(); + if( pGraphic ) + { + Size aSize ( + convertTwipToMm100(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Width()), + convertTwipToMm100(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Height())); + + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_GRFATR_MIRRORGRF, RES_GRFATR_CROPGRF>{} ); + rSh.GetCurAttr( aSet ); + SwMirrorGrf aMirror( aSet.Get(RES_GRFATR_MIRRORGRF) ); + SwCropGrf aCrop( aSet.Get(RES_GRFATR_CROPGRF) ); + + tools::Rectangle aCropRectangle( + convertTwipToMm100(aCrop.GetLeft()), + convertTwipToMm100(aCrop.GetTop()), + convertTwipToMm100(aCrop.GetRight()), + convertTwipToMm100(aCrop.GetBottom()) ); + + Graphic aGraphic = *pGraphic; + + CompressGraphicsDialog aDialog(GetView().GetFrameWeld(), aGraphic, aSize, aCropRectangle, GetView().GetViewFrame()->GetBindings()); + if (aDialog.run() == RET_OK) + { + rSh.StartAllAction(); + rSh.StartUndo(SwUndoId::START); + tools::Rectangle aScaledCropedRectangle = aDialog.GetScaledCropRectangle(); + + aCrop.SetLeft( convertMm100ToTwip( aScaledCropedRectangle.Left() )); + aCrop.SetTop( convertMm100ToTwip( aScaledCropedRectangle.Top() )); + aCrop.SetRight( convertMm100ToTwip( aScaledCropedRectangle.Right() )); + aCrop.SetBottom( convertMm100ToTwip( aScaledCropedRectangle.Bottom() )); + + Graphic aCompressedGraphic( aDialog.GetCompressedGraphic() ); + rSh.ReRead(OUString(), OUString(), const_cast<const Graphic*>(&aCompressedGraphic)); + + rSh.SetAttrItem(aCrop); + rSh.SetAttrItem(aMirror); + + rSh.EndUndo(SwUndoId::END); + rSh.EndAllAction(); + } + } + } + break; + case SID_EXTERNAL_EDIT: + { + // When the graphic is selected to be opened via some external tool + // for advanced editing + GraphicObject const*const pGraphicObject(rSh.GetGraphicObj()); + if(nullptr != pGraphicObject) + { + m_ExternalEdits.push_back(std::make_unique<SwExternalToolEdit>( + &rSh)); + m_ExternalEdits.back()->Edit(pGraphicObject); + } + } + break; + case SID_CHANGE_PICTURE: + case SID_INSERT_GRAPHIC: + { + // #i123922# implement slot independent from the two below to + // bring up the insert graphic dialog and associated actions + SwView& rLclView = GetView(); + rReq.SetReturnValue(SfxBoolItem(nSlot, rLclView.InsertGraphicDlg( rReq ))); + break; + } + case FN_FORMAT_GRAFIC_DLG: + case FN_DRAW_WRAP_DLG: + { + SwFlyFrameAttrMgr aMgr( false, &rSh, rSh.IsFrameSelected() ? + Frmmgr_Type::NONE : Frmmgr_Type::GRF, nullptr); + const SwViewOption* pVOpt = rSh.GetViewOptions(); + SwViewOption aUsrPref( *pVOpt ); + + SfxItemSet aSet( + GetPool(), + svl::Items< + RES_FRMATR_BEGIN, RES_GRFATR_CROPGRF, + // FillAttribute support: + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_DOCFRAME, SID_DOCFRAME, + SID_REFERER, SID_REFERER, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, // 10051 + // RotGrfFlyFrame: Need RotationAngle now + SID_ATTR_TRANSFORM_ANGLE, SID_ATTR_TRANSFORM_ANGLE, // 10095 + // Items to hand over XPropertyList things like + // XColorList, XHatchList, XGradientList, and XBitmapList to + // the Area TabPage: + SID_COLOR_TABLE, SID_PATTERN_LIST, //10179 + SID_HTML_MODE, SID_HTML_MODE, //10414 + SID_ATTR_GRAF_KEEP_ZOOM, SID_ATTR_GRAF_KEEP_ZOOM, //10882 + SID_ATTR_GRAF_FRMSIZE, SID_ATTR_GRAF_GRAPHIC, // 10884 + // contains SID_ATTR_GRAF_FRMSIZE_PERCENT + FN_GET_PRINT_AREA, FN_GET_PRINT_AREA, + FN_PARAM_GRF_CONNECT, FN_PARAM_GRF_CONNECT, + FN_PARAM_GRF_DIALOG, FN_PARAM_GRF_DIALOG, + FN_SET_FRM_NAME, FN_KEEP_ASPECT_RATIO, + FN_SET_FRM_ALT_NAME, FN_SET_FRM_ALT_NAME, + FN_UNO_DESCRIPTION, FN_UNO_DESCRIPTION>{}); + + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + const SwDrawModel* pDrawModel = rSh.GetView().GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + + aSet.Put(SvxColorListItem(pDrawModel->GetColorList(), SID_COLOR_TABLE)); + aSet.Put(SvxGradientListItem(pDrawModel->GetGradientList(), SID_GRADIENT_LIST)); + aSet.Put(SvxHatchListItem(pDrawModel->GetHatchList(), SID_HATCH_LIST)); + aSet.Put(SvxBitmapListItem(pDrawModel->GetBitmapList(), SID_BITMAP_LIST)); + aSet.Put(SvxPatternListItem(pDrawModel->GetPatternList(), SID_PATTERN_LIST)); + + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + aSet.Put(SfxUInt16Item(SID_HTML_MODE, nHtmlMode)); + FieldUnit eMetric = ::GetDfltMetric(0 != (nHtmlMode&HTMLMODE_ON)); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)) ); + + const SwRect* pRect = &rSh.GetAnyCurRect(CurRectType::Page); + SwFormatFrameSize aFrameSize( SwFrameSize::Variable, pRect->Width(), pRect->Height()); + aFrameSize.SetWhich( GetPool().GetWhich( SID_ATTR_PAGE_SIZE ) ); + aSet.Put( aFrameSize ); + + aSet.Put(SfxStringItem(FN_SET_FRM_NAME, rSh.GetFlyName())); + aSet.Put(SfxStringItem(FN_UNO_DESCRIPTION, rSh.GetObjDescription())); + if ( nSlot == FN_FORMAT_GRAFIC_DLG ) + { + // #i73249# + aSet.Put( SfxStringItem( FN_SET_FRM_ALT_NAME, rSh.GetObjTitle() ) ); + } + + pRect = &rSh.GetAnyCurRect(CurRectType::PagePrt); + aFrameSize.SetWidth( pRect->Width() ); + aFrameSize.SetHeight( pRect->Height() ); + aFrameSize.SetWhich( GetPool().GetWhich(FN_GET_PRINT_AREA) ); + aSet.Put( aFrameSize ); + + aSet.Put( aMgr.GetAttrSet() ); + aSet.SetParent( aMgr.GetAttrSet().GetParent() ); + + // At percentage values initialize size + SwFormatFrameSize aSizeCopy = aSet.Get(RES_FRM_SIZE); + if (aSizeCopy.GetWidthPercent() && aSizeCopy.GetWidthPercent() != SwFormatFrameSize::SYNCED) + aSizeCopy.SetWidth(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Width()); + if (aSizeCopy.GetHeightPercent() && aSizeCopy.GetHeightPercent() != SwFormatFrameSize::SYNCED) + aSizeCopy.SetHeight(rSh.GetAnyCurRect(CurRectType::FlyEmbedded).Height()); + // and now set the size for "external" tabpages + { + SvxSizeItem aSzItm( SID_ATTR_GRAF_FRMSIZE, aSizeCopy.GetSize() ); + aSet.Put( aSzItm ); + + Size aSz( aSizeCopy.GetWidthPercent(), aSizeCopy.GetHeightPercent() ); + if( SwFormatFrameSize::SYNCED == aSz.Width() ) aSz.setWidth( 0 ); + if( SwFormatFrameSize::SYNCED == aSz.Height() ) aSz.setHeight( 0 ); + + aSzItm.SetSize( aSz ); + aSzItm.SetWhich( SID_ATTR_GRAF_FRMSIZE_PERCENT ); + aSet.Put( aSzItm ); + } + + OUString sGrfNm; + OUString sFilterNm; + rSh.GetGrfNms( &sGrfNm, &sFilterNm ); + if( !sGrfNm.isEmpty() ) + { + aSet.Put( SvxBrushItem( INetURLObject::decode( sGrfNm, + INetURLObject::DecodeMechanism::Unambiguous ), + sFilterNm, GPOS_LT, + SID_ATTR_GRAF_GRAPHIC )); + } + else + { + // #119353# - robust + const GraphicObject* pGrfObj = rSh.GetGraphicObj(); + if ( pGrfObj ) + { + aSet.Put( SvxBrushItem( *pGrfObj, GPOS_LT, + SID_ATTR_GRAF_GRAPHIC ) ); + } + } + aSet.Put( SfxBoolItem( FN_PARAM_GRF_CONNECT, !sGrfNm.isEmpty() ) ); + + // get Mirror and Crop + { + SfxItemSet aTmpSet( rSh.GetAttrPool(), + svl::Items<RES_GRFATR_MIRRORGRF, RES_GRFATR_CROPGRF>{} ); + + rSh.GetCurAttr( aTmpSet ); + aSet.Put( aTmpSet ); + } + + aSet.Put(SfxBoolItem(FN_KEEP_ASPECT_RATIO, aUsrPref.IsKeepRatio())); + aSet.Put(SfxBoolItem( SID_ATTR_GRAF_KEEP_ZOOM, aUsrPref.IsGrfKeepZoom())); + + aSet.Put(SfxFrameItem( SID_DOCFRAME, &GetView().GetViewFrame()->GetFrame())); + + SfxObjectShell * sh = rSh.GetDoc()->GetPersist(); + if (sh != nullptr && sh->HasName()) + { + aSet.Put( + SfxStringItem(SID_REFERER, sh->GetMedium()->GetName())); + } + + Size aUnrotatedSize; + sal_uInt16 nCurrentRotation(0); + { // RotGrfFlyFrame: Add current RotationAngle value, convert from + // RES_GRFATR_ROTATION to SID_ATTR_TRANSFORM_ANGLE. Do not forget to + // convert from 10th degrees to 100th degrees + SfxItemSet aTmpSet( rSh.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} ); + rSh.GetCurAttr( aTmpSet ); + const SwRotationGrf& rRotation = aTmpSet.Get(RES_GRFATR_ROTATION); + nCurrentRotation = rRotation.GetValue(); + aUnrotatedSize = rRotation.GetUnrotatedSize(); + aSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ANGLE, nCurrentRotation * 10)); + } + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateFrameTabDialog("PictureDialog", + GetView().GetViewFrame(), + GetView().GetFrameWeld(), + aSet, false)); + if (nSlot == FN_DRAW_WRAP_DLG) + pDlg->SetCurPageId("wrap"); + + if (pDlg->Execute() == RET_OK) + { + rSh.StartAllAction(); + rSh.StartUndo(SwUndoId::START); + const SfxPoolItem* pItem; + SfxItemSet* pSet = const_cast<SfxItemSet*>(pDlg->GetOutputItemSet()); + rReq.Done(*pSet); + // change the 2 frmsize SizeItems to the correct SwFrameSizeItem + if( SfxItemState::SET == pSet->GetItemState( + SID_ATTR_GRAF_FRMSIZE, false, &pItem )) + { + SwFormatFrameSize aSize; + const Size& rSz = static_cast<const SvxSizeItem*>(pItem)->GetSize(); + aSize.SetWidth( rSz.Width() ); + aSize.SetHeight( rSz.Height() ); + + if( SfxItemState::SET == pSet->GetItemState( + SID_ATTR_GRAF_FRMSIZE_PERCENT, false, &pItem )) + { + const Size& rRelativeSize = static_cast<const SvxSizeItem*>(pItem)->GetSize(); + aSize.SetWidthPercent( static_cast< sal_uInt8 >( rRelativeSize.Width() ) ); + aSize.SetHeightPercent( static_cast< sal_uInt8 >( rRelativeSize.Height() ) ); + } + pSet->Put( aSize ); + } + + // Templates AutoUpdate + SwFrameFormat* pFormat = rSh.GetSelectedFrameFormat(); + if(pFormat && pFormat->IsAutoUpdateFormat()) + { + pFormat->SetFormatAttr(*pSet); + SfxItemSet aShellSet( + GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR>{}); + aShellSet.Put(*pSet); + aMgr.SetAttrSet(aShellSet); + } + else + { + aMgr.SetAttrSet(*pSet); + } + aMgr.UpdateFlyFrame(); + + bool bApplyUsrPref = false; + if (SfxItemState::SET == pSet->GetItemState( + FN_KEEP_ASPECT_RATIO, true, &pItem )) + { + aUsrPref.SetKeepRatio( + static_cast<const SfxBoolItem*>(pItem)->GetValue() ); + bApplyUsrPref = true; + } + if( SfxItemState::SET == pSet->GetItemState( + SID_ATTR_GRAF_KEEP_ZOOM, true, &pItem )) + { + aUsrPref.SetGrfKeepZoom( + static_cast<const SfxBoolItem*>(pItem)->GetValue() ); + bApplyUsrPref = true; + } + + if( bApplyUsrPref ) + SW_MOD()->ApplyUsrPref(aUsrPref, &GetView()); + + // and now set all the graphic attributes and other stuff + if( SfxItemState::SET == pSet->GetItemState( + SID_ATTR_GRAF_GRAPHIC, true, &pItem )) + { + if( !static_cast<const SvxBrushItem*>(pItem)->GetGraphicLink().isEmpty() ) + sGrfNm = static_cast<const SvxBrushItem*>(pItem)->GetGraphicLink(); + else + sGrfNm.clear(); + + if( !static_cast<const SvxBrushItem*>(pItem)->GetGraphicFilter().isEmpty() ) + sFilterNm = static_cast<const SvxBrushItem*>(pItem)->GetGraphicFilter(); + else + sFilterNm.clear(); + + if( !sGrfNm.isEmpty() ) + { + SwDocShell* pDocSh = GetView().GetDocShell(); + SwWait aWait( *pDocSh, true ); + SfxMedium* pMedium = pDocSh->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + rSh.ReRead( URIHelper::SmartRel2Abs( + aAbs, sGrfNm, + URIHelper::GetMaybeFileHdl() ), + sFilterNm ); + } + } + if ( SfxItemState::SET == pSet->GetItemState( + FN_SET_FRM_ALT_NAME, true, &pItem )) + { + // #i73249# + rSh.SetObjTitle( static_cast<const SfxStringItem*>(pItem)->GetValue() ); + } + + if ( SfxItemState::SET == pSet->GetItemState( + FN_UNO_DESCRIPTION, true, &pItem )) + rSh.SetObjDescription( static_cast<const SfxStringItem*>(pItem)->GetValue() ); + + // RotGrfFlyFrame: Get and process evtl. changed RotationAngle + if ( SfxItemState::SET == pSet->GetItemState(SID_ATTR_TRANSFORM_ANGLE, false, &pItem )) + { + const sal_Int32 aNewRotation((static_cast<const SfxInt32Item*>(pItem)->GetValue() / 10) % 3600); + + // RotGrfFlyFrame: Possible rotation change here, SwFlyFrameAttrMgr aMgr is available + aMgr.SetRotation(nCurrentRotation, aNewRotation, aUnrotatedSize); + } + + SfxItemSet aGrfSet( rSh.GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, + RES_GRFATR_END-1>{} ); + aGrfSet.Put( *pSet ); + if( aGrfSet.Count() ) + rSh.SetAttrSet( aGrfSet ); + + rSh.EndUndo(SwUndoId::END); + rSh.EndAllAction(); + } + } + break; + + case FN_GRAPHIC_MIRROR_ON_EVEN_PAGES: + { + SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_GRFATR_MIRRORGRF, RES_GRFATR_MIRRORGRF>{}); + rSh.GetCurAttr( aSet ); + SwMirrorGrf aGrf(aSet.Get(RES_GRFATR_MIRRORGRF)); + aGrf.SetGrfToggle(!aGrf.IsGrfToggle()); + rSh.SetAttrItem(aGrf); + } + break; + + case SID_OBJECT_CROP: + { + GraphicObject const *pGraphicObject = rSh.GetGraphicObj(); + if (nullptr != pGraphicObject && SdrDragMode::Crop != rSh.GetDragMode()) { + rSh.StartCropImage(); + } + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +void SwGrfShell::ExecAttr( SfxRequest const &rReq ) +{ + GraphicType nGrfType = GraphicType::NONE; + if (CNT_GRF == GetShell().GetCntType()) + nGrfType = GetShell().GetGraphicType(); + if (GraphicType::Bitmap == nGrfType || + GraphicType::GdiMetafile == nGrfType) + { + SfxItemSet aGrfSet( GetShell().GetAttrPool(), svl::Items<RES_GRFATR_BEGIN, + RES_GRFATR_END -1>{} ); + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + sal_uInt16 nSlot = rReq.GetSlot(); + if( !pArgs || SfxItemState::SET != pArgs->GetItemState( nSlot, false, &pItem )) + pItem = nullptr; + + switch( nSlot ) + { + case SID_FLIP_VERTICAL: + case SID_FLIP_HORIZONTAL: + { + GetShell().GetCurAttr( aGrfSet ); + SwMirrorGrf aMirror( aGrfSet.Get( RES_GRFATR_MIRRORGRF ) ); + MirrorGraph nMirror = aMirror.GetValue(); + if ( nSlot==SID_FLIP_HORIZONTAL ) + switch( nMirror ) + { + case MirrorGraph::Dont: nMirror = MirrorGraph::Vertical; + break; + case MirrorGraph::Horizontal: nMirror = MirrorGraph::Both; + break; + case MirrorGraph::Vertical: nMirror = MirrorGraph::Dont; + break; + case MirrorGraph::Both: nMirror = MirrorGraph::Horizontal; + break; + } + else + switch( nMirror ) + { + case MirrorGraph::Dont: nMirror = MirrorGraph::Horizontal; + break; + case MirrorGraph::Vertical: nMirror = MirrorGraph::Both; + break; + case MirrorGraph::Horizontal: nMirror = MirrorGraph::Dont; + break; + case MirrorGraph::Both: nMirror = MirrorGraph::Vertical; + break; + } + aMirror.SetValue( nMirror ); + aGrfSet.ClearItem(); + aGrfSet.Put( aMirror ); + } + break; + + case SID_ATTR_GRAF_LUMINANCE: + if( pItem ) + aGrfSet.Put( SwLuminanceGrf( + static_cast<const SfxInt16Item*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_CONTRAST: + if( pItem ) + aGrfSet.Put( SwContrastGrf( + static_cast<const SfxInt16Item*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_RED: + if( pItem ) + aGrfSet.Put( SwChannelRGrf( + static_cast<const SfxInt16Item*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_GREEN: + if( pItem ) + aGrfSet.Put( SwChannelGGrf( + static_cast<const SfxInt16Item*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_BLUE: + if( pItem ) + aGrfSet.Put( SwChannelBGrf( + static_cast<const SfxInt16Item*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_GAMMA: + if( pItem ) + { + double fVal = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + aGrfSet.Put( SwGammaGrf(fVal/100. )); + } + break; + + case SID_ATTR_GRAF_TRANSPARENCE: + if( pItem ) + aGrfSet.Put( SwTransparencyGrf( + static_cast< sal_Int8 >( static_cast<const SfxUInt16Item*>(pItem )->GetValue() ) ) ); + break; + + case SID_ATTR_GRAF_INVERT: + if( pItem ) + aGrfSet.Put( SwInvertGrf( + static_cast<const SfxBoolItem*>(pItem)->GetValue() )); + break; + + case SID_ATTR_GRAF_MODE: + if( pItem ) + aGrfSet.Put( SwDrawModeGrf( + static_cast<GraphicDrawMode>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()) )); + break; + + case SID_COLOR_SETTINGS: + { + svx::ToolboxAccess aToolboxAccess( TOOLBOX_NAME ); + aToolboxAccess.toggleToolbox(); + break; + } + + case SID_GRFFILTER: + case SID_GRFFILTER_INVERT: + case SID_GRFFILTER_SMOOTH: + case SID_GRFFILTER_SHARPEN: + case SID_GRFFILTER_REMOVENOISE: + case SID_GRFFILTER_SOBEL: + case SID_GRFFILTER_MOSAIC: + case SID_GRFFILTER_EMBOSS: + case SID_GRFFILTER_POSTER: + case SID_GRFFILTER_POPART: + case SID_GRFFILTER_SEPIA: + case SID_GRFFILTER_SOLARIZE: + if( GraphicType::Bitmap == nGrfType ) + { + // #119353# - robust + const GraphicObject* pFilterObj( GetShell().GetGraphicObj() ); + if ( pFilterObj ) + { + GraphicObject aFilterObj( *pFilterObj ); + if( SvxGraphicFilterResult::NONE == + SvxGraphicFilter::ExecuteGrfFilterSlot( rReq, aFilterObj )) + GetShell().ReRead( OUString(), OUString(), + &aFilterObj.GetGraphic() ); + } + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + } + + if( aGrfSet.Count() ) + GetShell().SetAttrSet( aGrfSet ); + } + GetView().GetViewFrame()->GetBindings().Invalidate(rReq.GetSlot()); +} + +void SwGrfShell::GetAttrState(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + SfxItemSet aCoreSet( GetPool(), aNoTextNodeSetRange ); + rSh.GetCurAttr( aCoreSet ); + bool bParentCntProt = FlyProtectFlags::NONE != rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ); + bool bIsGrfContent = CNT_GRF == GetShell().GetCntType(); + + SetGetStateSet( &rSet ); + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while( nWhich ) + { + bool bDisable = bParentCntProt; + switch( nWhich ) + { + case SID_OBJECT_ROTATE: + { + // RotGrfFlyFrame: steer rotation state + const bool bIsRotate(GetView().IsDrawRotate()); + SdrView* pSdrView = rSh.GetDrawViewWithValidMarkList(); + + if(!bIsRotate && !pSdrView->IsRotateAllowed()) + { + rSet.DisableItem(nWhich); + } + else + { + rSet.Put(SfxBoolItem(nWhich, bIsRotate)); + } + + break; + } + case SID_INSERT_GRAPHIC: + case FN_FORMAT_GRAFIC_DLG: + case SID_TWAIN_TRANSFER: + if( bParentCntProt || !bIsGrfContent ) + bDisable = true; + else if ( nWhich == SID_INSERT_GRAPHIC + && rSh.CursorInsideInputField() ) + { + bDisable = true; + } + break; + + case SID_SAVE_GRAPHIC: + case SID_EXTERNAL_EDIT: + if( rSh.GetGraphicType() == GraphicType::NONE || GetObjectShell()->isExportLocked()) + bDisable = true; + break; + + case SID_COLOR_SETTINGS: + { + if ( bParentCntProt || !bIsGrfContent ) + bDisable = true; + else + { + svx::ToolboxAccess aToolboxAccess( TOOLBOX_NAME ); + rSet.Put( SfxBoolItem( nWhich, aToolboxAccess.isToolboxVisible() ) ); + } + break; + } + + case SID_FLIP_HORIZONTAL: + if( !bParentCntProt ) + { + MirrorGraph nState = aCoreSet.Get( + RES_GRFATR_MIRRORGRF ).GetValue(); + + rSet.Put(SfxBoolItem( nWhich, nState == MirrorGraph::Vertical || + nState == MirrorGraph::Both)); + } + break; + + case SID_FLIP_VERTICAL: + if( !bParentCntProt ) + { + MirrorGraph nState = aCoreSet.GetItem<SwMirrorGrf>( RES_GRFATR_MIRRORGRF )->GetValue(); + rSet.Put(SfxBoolItem( nWhich, nState == MirrorGraph::Horizontal || + nState == MirrorGraph::Both)); + } + break; + + case SID_ATTR_GRAF_LUMINANCE: + if( !bParentCntProt ) + rSet.Put( SfxInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_LUMINANCE).GetValue() )); + break; + + case SID_ATTR_GRAF_CONTRAST: + if( !bParentCntProt ) + rSet.Put( SfxInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_CONTRAST).GetValue() )); + break; + + case SID_ATTR_GRAF_RED: + if( !bParentCntProt ) + rSet.Put( SfxInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_CHANNELR).GetValue() )); + break; + + case SID_ATTR_GRAF_GREEN: + if( !bParentCntProt ) + rSet.Put( SfxInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_CHANNELG).GetValue() )); + break; + + case SID_ATTR_GRAF_BLUE: + if( !bParentCntProt ) + rSet.Put( SfxInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_CHANNELB).GetValue() )); + break; + + case SID_ATTR_GRAF_GAMMA: + if( !bParentCntProt ) + rSet.Put( SfxUInt32Item( nWhich, static_cast< sal_uInt32 >( + aCoreSet.Get( RES_GRFATR_GAMMA ).GetValue() * 100 ) ) ); + break; + + case SID_ATTR_GRAF_TRANSPARENCE: + if( !bParentCntProt ) + { + // #119353# - robust + const GraphicObject* pGrafObj = rSh.GetGraphicObj(); + if ( pGrafObj ) + { + if( pGrafObj->IsAnimated() || + GraphicType::GdiMetafile == pGrafObj->GetType() ) + bDisable = true; + else + rSet.Put( SfxUInt16Item( nWhich, + aCoreSet.Get(RES_GRFATR_TRANSPARENCY).GetValue() )); + } + } + break; + + case SID_ATTR_GRAF_INVERT: + if( !bParentCntProt ) + rSet.Put( SfxBoolItem( nWhich, + aCoreSet.Get(RES_GRFATR_INVERT).GetValue() )); + break; + + case SID_ATTR_GRAF_MODE: + if( !bParentCntProt ) + rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(aCoreSet.Get(RES_GRFATR_DRAWMODE).GetValue()) )); + break; + + case SID_GRFFILTER: + case SID_GRFFILTER_INVERT: + case SID_GRFFILTER_SMOOTH: + case SID_GRFFILTER_SHARPEN: + case SID_GRFFILTER_REMOVENOISE: + case SID_GRFFILTER_SOBEL: + case SID_GRFFILTER_MOSAIC: + case SID_GRFFILTER_EMBOSS: + case SID_GRFFILTER_POSTER: + case SID_GRFFILTER_POPART: + case SID_GRFFILTER_SEPIA: + case SID_GRFFILTER_SOLARIZE: + { + if( bParentCntProt || !bIsGrfContent ) + bDisable = true; + // #i59688# load graphic only if type is unknown + else + { + const GraphicType eGraphicType( rSh.GetGraphicType() ); + if ( ( eGraphicType == GraphicType::NONE || + eGraphicType == GraphicType::Default ) && + rSh.IsLinkedGrfSwapOut() ) + { + rSet.DisableItem( nWhich ); + if( AddGrfUpdateSlot( nWhich )) + rSh.GetGraphic(false); // start the loading + } + else + { + bDisable = eGraphicType != GraphicType::Bitmap; + } + } + } + break; + + case SID_OBJECT_CROP: + { + bDisable = FlyProtectFlags::NONE != rSh.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ); + if( rSh.GetGraphicType() == GraphicType::NONE ) + bDisable = true; + } + break; + + default: + bDisable = false; + } + + if( bDisable ) + rSet.DisableItem( nWhich ); + nWhich = aIter.NextWhich(); + } + SetGetStateSet( nullptr ); +} + +void SwGrfShell::ExecuteRotation(SfxRequest const &rReq) +{ + // RotGrfFlyFrame: Modify rotation attribute instead of manipulating the graphic + sal_uInt16 aRotation(0); + + if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_LEFT) + { + aRotation = 900; + } + else if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_RIGHT) + { + aRotation = 2700; + } + else if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_180) + { + aRotation = 1800; + } + + if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_RESET || 0 != aRotation) + { + SwWrtShell& rShell = GetShell(); + SfxItemSet aSet( rShell.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} ); + rShell.GetCurAttr( aSet ); + const SwRotationGrf& rRotation = aSet.Get(RES_GRFATR_ROTATION); + SwFlyFrameAttrMgr aMgr(false, &rShell, rShell.IsFrameSelected() ? Frmmgr_Type::NONE : Frmmgr_Type::GRF, nullptr); + + // RotGrfFlyFrame: Possible rotation change here, SwFlyFrameAttrMgr aMgr is available + if (rReq.GetSlot() == SID_ROTATE_GRAPHIC_RESET) + { + aMgr.SetRotation(rRotation.GetValue(), 0, rRotation.GetUnrotatedSize()); + } + else if(0 != aRotation) + { + const sal_uInt16 aNewRotation((aRotation + rRotation.GetValue()) % 3600); + + aMgr.SetRotation(rRotation.GetValue(), aNewRotation, rRotation.GetUnrotatedSize()); + } + } +} + +void SwGrfShell::GetAttrStateForRotation(SfxItemSet &rSet) +{ + SwWrtShell& rShell = GetShell(); + bool bIsParentContentProtected = FlyProtectFlags::NONE != rShell.IsSelObjProtected( FlyProtectFlags::Content|FlyProtectFlags::Parent ); + + SetGetStateSet( &rSet ); + + SfxWhichIter aIterator( rSet ); + sal_uInt16 nWhich = aIterator.FirstWhich(); + while( nWhich ) + { + bool bDisable = bIsParentContentProtected; + switch( nWhich ) + { + case SID_ROTATE_GRAPHIC_LEFT: + case SID_ROTATE_GRAPHIC_RIGHT: + case SID_ROTATE_GRAPHIC_180: + { + if( rShell.GetGraphicType() == GraphicType::NONE ) + { + bDisable = true; + } + break; + } + case SID_ROTATE_GRAPHIC_RESET: + { + // RotGrfFlyFrame: disable when already no rotation + SfxItemSet aSet( rShell.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} ); + rShell.GetCurAttr( aSet ); + const SwRotationGrf& rRotation = aSet.Get(RES_GRFATR_ROTATION); + bDisable = (0 == rRotation.GetValue()); + break; + } + case SID_ATTR_TRANSFORM_ANGLE: + { + // RotGrfFlyFrame: get rotation value from RES_GRFATR_ROTATION and copy to rSet as + // SID_ATTR_TRANSFORM_ANGLE, convert from 10th degrees to 100th degrees + SfxItemSet aSet( rShell.GetAttrPool(), svl::Items<RES_GRFATR_ROTATION, RES_GRFATR_ROTATION>{} ); + rShell.GetCurAttr( aSet ); + const SwRotationGrf& rRotation = aSet.Get(RES_GRFATR_ROTATION); + rSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ANGLE, rRotation.GetValue() * 10)); + break; + } + default: + bDisable = false; + } + + if( bDisable ) + rSet.DisableItem( nWhich ); + nWhich = aIterator.NextWhich(); + } + SetGetStateSet( nullptr ); +} + +SwGrfShell::~SwGrfShell() +{ +} + +SwGrfShell::SwGrfShell(SwView &_rView) : + SwBaseShell(_rView) +{ + SetName("Graphic"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Graphic)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/grfshex.cxx b/sw/source/uibase/shells/grfshex.cxx new file mode 100644 index 000000000..5cd0e1cc5 --- /dev/null +++ b/sw/source/uibase/shells/grfshex.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 <wrtsh.hxx> +#include <view.hxx> +#include <textsh.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <docsh.hxx> +#include <svx/svdomedia.hxx> +#include <sfx2/filedlghelper.hxx> + +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <avmedia/mediawindow.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::sfx2; + +bool SwTextShell::InsertMediaDlg( SfxRequest const & rReq ) +{ + OUString aURL; + const SfxItemSet* pReqArgs = rReq.GetArgs(); + vcl::Window& rWindow = GetView().GetViewFrame()->GetWindow(); + bool bAPI = false, bRet = false; + + if( pReqArgs ) + { + const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>( &pReqArgs->Get( rReq.GetSlot() ) ); + if( pStringItem ) + { + aURL = pStringItem->GetValue(); + bAPI = !aURL.isEmpty(); + } + } + + bool bLink(true); + if (bAPI || ::avmedia::MediaWindow::executeMediaURLDialog(rWindow.GetFrameWeld(), aURL, & bLink)) + { + Size aPrefSize; + + rWindow.EnterWait(); + + if( !::avmedia::MediaWindow::isMediaURL( aURL, "", true, &aPrefSize ) ) + { + rWindow.LeaveWait(); + + if( !bAPI ) + ::avmedia::MediaWindow::executeFormatErrorBox(rWindow.GetFrameWeld()); + } + else + { + SwWrtShell& rSh = GetShell(); + + if( !rSh.HasDrawView() ) + rSh.MakeDrawView(); + + Size aDocSz( rSh.GetDocSize() ); + const SwRect& rVisArea = rSh.VisArea(); + Point aPos( rVisArea.Center() ); + Size aSize; + + if( rVisArea.Width() > aDocSz.Width()) + aPos.setX( aDocSz.Width() / 2 + rVisArea.Left() ); + + if(rVisArea.Height() > aDocSz.Height()) + aPos.setY( aDocSz.Height() / 2 + rVisArea.Top() ); + + if( aPrefSize.Width() && aPrefSize.Height() ) + aSize = rWindow.PixelToLogic(aPrefSize, MapMode(MapUnit::MapTwip)); + else + aSize = Size( 2835, 2835 ); + + OUString realURL; + if (bLink) + { + realURL = aURL; + } + else + { + uno::Reference<frame::XModel> const xModel( + rSh.GetDoc()->GetDocShell()->GetModel()); + bRet = ::avmedia::EmbedMedia(xModel, aURL, realURL); + if (!bRet) { return bRet; } + } + + SdrMediaObj* pObj = new SdrMediaObj( + *rSh.GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(), + tools::Rectangle(aPos, aSize)); + + pObj->setURL( realURL, "" ); + rSh.EnterStdMode(); + rSh.SwFEShell::InsertDrawObj( *pObj, aPos ); + bRet = true; + + rWindow.LeaveWait(); + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/langhelper.cxx b/sw/source/uibase/shells/langhelper.cxx new file mode 100644 index 000000000..90904b37d --- /dev/null +++ b/sw/source/uibase/shells/langhelper.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 <string.h> + +#include <vcl/window.hxx> + +#include <wrtsh.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <charfmt.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editdata.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <editeng/langitem.hxx> + +#include <svl/languageoptions.hxx> +#include <svtools/langtab.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <svx/svxids.hrc> + +#include <ndtxt.hxx> +#include <pam.hxx> +#include <view.hxx> +#include <viewopt.hxx> + +#include <langhelper.hxx> +#include <memory> + +using namespace ::com::sun::star; + +namespace SwLangHelper +{ + + void GetLanguageStatus( OutlinerView* pOLV, SfxItemSet& rSet ) + { + ESelection aSelection = pOLV->GetSelection(); + EditView& rEditView=pOLV->GetEditView(); + EditEngine* pEditEngine=rEditView.GetEditEngine(); + + // the value of used script types + const SvtScriptType nScriptType =pOLV->GetSelectedScriptType(); + OUString aScriptTypesInUse( OUString::number( static_cast<int>(nScriptType) ) );//pEditEngine->GetScriptType(aSelection) + + // get keyboard language + OUString aKeyboardLang; + LanguageType nLang = LANGUAGE_DONTKNOW; + + vcl::Window* pWin = rEditView.GetWindow(); + if(pWin) + nLang = pWin->GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + SfxItemSet aSet(pOLV->GetAttribs()); + nLang = SwLangHelper::GetCurrentLanguage( aSet,nScriptType ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq( 4 ); + aSeq[0] = aCurrentLang; + aSeq[1] = aScriptTypesInUse; + aSeq[2] = aKeyboardLang; + aSeq[3] = SwLangHelper::GetTextForLanguageGuessing( pEditEngine, aSelection ); + + // set sequence as status value + SfxStringListItem aItem( SID_LANGUAGE_STATUS ); + aItem.SetStringList( aSeq ); + rSet.Put( aItem ); + } + + bool SetLanguageStatus( OutlinerView* pOLV, SfxRequest &rReq, SwView const &rView, SwWrtShell &rSh ) + { + bool bRestoreSelection = false; + SfxItemSet aEditAttr(pOLV->GetAttribs()); + ESelection aSelection = pOLV->GetSelection(); + EditView & rEditView = pOLV->GetEditView(); + EditEngine * pEditEngine = rEditView.GetEditEngine(); + + // get the language + OUString aNewLangText; + + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(SID_LANGUAGE_STATUS); + if (pItem) + aNewLangText = pItem->GetValue(); + + //!! Remember the view frame right now... + //!! (call to GetView().GetViewFrame() will break if the + //!! SwTextShell got destroyed meanwhile.) + SfxViewFrame *pViewFrame = rView.GetViewFrame(); + + if (aNewLangText == "*" ) + { + // open the dialog "Tools/Options/Language Settings - Language" + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog( rView.GetFrameWeld(), SID_LANGUAGE_OPTIONS )); + pDlg->Execute(); + } + else + { + // setting the new language... + if (!aNewLangText.isEmpty()) + { + const OUString aSelectionLangPrefix("Current_"); + const OUString aParagraphLangPrefix("Paragraph_"); + const OUString aDocumentLangPrefix("Default_"); + + sal_Int32 nPos = 0; + bool bForSelection = true; + bool bForParagraph = false; + if (-1 != (nPos = aNewLangText.indexOf( aSelectionLangPrefix ))) + { + // ... for the current selection + aNewLangText = aNewLangText.replaceAt(nPos, aSelectionLangPrefix.getLength(), ""); + bForSelection = true; + } + else if (-1 != (nPos = aNewLangText.indexOf( aParagraphLangPrefix ))) + { + // ... for the current paragraph language + aNewLangText = aNewLangText.replaceAt(nPos, aParagraphLangPrefix.getLength(), ""); + bForSelection = true; + bForParagraph = true; + } + else if (-1 != (nPos = aNewLangText.indexOf( aDocumentLangPrefix ))) + { + // ... as default document language + aNewLangText = aNewLangText.replaceAt(nPos, aDocumentLangPrefix.getLength(), ""); + bForSelection = false; + } + + if (bForParagraph) + { + bRestoreSelection = true; + SwLangHelper::SelectPara( rEditView, aSelection ); + aSelection = pOLV->GetSelection(); + } + if (!bForSelection) // document language to be changed... + { + rSh.StartAction(); + rSh.LockView( true ); + rSh.Push(); + + // prepare to apply new language to all text in document + rSh.SelAll(); + rSh.ExtendedSelectAll(); + } + + if (aNewLangText == "LANGUAGE_NONE") + SwLangHelper::SetLanguage_None( rSh, pOLV, aSelection, bForSelection, aEditAttr ); + else if (aNewLangText == "RESET_LANGUAGES") + SwLangHelper::ResetLanguages( rSh, pOLV ); + else + SwLangHelper::SetLanguage( rSh, pOLV, aSelection, aNewLangText, bForSelection, aEditAttr ); + + // ugly hack, as it seems that EditView/EditEngine does not update their spellchecking marks + // when setting a new language attribute + if (bForSelection) + { + const SwViewOption* pVOpt = rView.GetWrtShellPtr()->GetViewOptions(); + EEControlBits nCntrl = pEditEngine->GetControlWord(); + // turn off + nCntrl &= ~EEControlBits::ONLINESPELLING; + pEditEngine->SetControlWord(nCntrl); + + //turn back on + if (pVOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + pEditEngine->SetControlWord(nCntrl); + + pEditEngine->CompleteOnlineSpelling(); + rEditView.Invalidate(); + } + + if (!bForSelection) + { + // need to release view and restore selection... + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.LockView( false ); + rSh.EndAction(); + } + } + } + + // invalidate slot to get the new language displayed + pViewFrame->GetBindings().Invalidate( rReq.GetSlot() ); + + rReq.Done(); + return bRestoreSelection; + } + + void SetLanguage( SwWrtShell &rWrtSh, const OUString &rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + SetLanguage( rWrtSh, nullptr , ESelection(), rLangText, bIsForSelection, rCoreSet ); + } + + void SetLanguage( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, const OUString &rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + const LanguageType nLang = SvtLanguageTable::GetLanguageType( rLangText ); + if (nLang != LANGUAGE_DONTKNOW) + { + EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr; + OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" ); + + //get ScriptType + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (SvtLanguageOptions::GetScriptTypeOfLanguage( nLang )) + { + case SvtScriptType::LATIN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE) : sal_uInt16(RES_CHRATR_LANGUAGE); break; + case SvtScriptType::ASIAN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CJK) : sal_uInt16(RES_CHRATR_CJK_LANGUAGE); break; + case SvtScriptType::COMPLEX : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CTL) : sal_uInt16(RES_CHRATR_CTL_LANGUAGE); break; + default: + bIsSingleScriptType = false; + OSL_FAIL("unexpected case" ); + } + if (bIsSingleScriptType) + { + // change language for selection or paragraph + // (for paragraph is handled by previously having set the selection to the + // whole paragraph) + if (bIsForSelection) + { + // apply language to current selection + if (pEditEngine) + { + rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId )); + pEditEngine->QuickSetAttribs(rCoreSet, rSelection); + } + else + { + rWrtSh.GetCurAttr( rCoreSet ); + rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId )); + rWrtSh.SetAttrSet( rCoreSet ); + } + } + else // change language for all text + { + // set document default language + switch (nLangWhichId) + { + case EE_CHAR_LANGUAGE : nLangWhichId = RES_CHRATR_LANGUAGE; break; + case EE_CHAR_LANGUAGE_CJK : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break; + case EE_CHAR_LANGUAGE_CTL : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break; + } + //Set the default document language + rWrtSh.SetDefault( SvxLanguageItem( nLang, nLangWhichId ) ); + + //Resolves: fdo#35282 Clear the language from all Text Styles, and + //fallback to default document language + const SwTextFormatColls *pColls = rWrtSh.GetDoc()->GetTextFormatColls(); + for(size_t i = 0, nCount = pColls->size(); i < nCount; ++i) + { + SwTextFormatColl &rTextColl = *(*pColls)[ i ]; + rTextColl.ResetFormatAttr(nLangWhichId); + } + //Resolves: fdo#35282 Clear the language from all Character Styles, + //and fallback to default document language + const SwCharFormats *pCharFormats = rWrtSh.GetDoc()->GetCharFormats(); + for(size_t i = 0, nCount = pCharFormats->size(); i < nCount; ++i) + { + SwCharFormat &rCharFormat = *(*pCharFormats)[ i ]; + rCharFormat.ResetFormatAttr(nLangWhichId); + } + + // #i102191: hard set respective language attribute in text document + // (for all text in the document - which should be selected by now...) + rWrtSh.SetAttrItem( SvxLanguageItem( nLang, nLangWhichId ) ); + } + } + } + } + + void SetLanguage_None( SwWrtShell &rWrtSh, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + SetLanguage_None( rWrtSh,nullptr,ESelection(),bIsForSelection,rCoreSet ); + } + + void SetLanguage_None( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + // EditEngine IDs + const sal_uInt16 aLangWhichId_EE[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + + // Writer IDs + const sal_uInt16 aLangWhichId_Writer[3] = + { + RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE + }; + + if (bIsForSelection) + { + // change language for selection or paragraph + // (for paragraph is handled by previously having set the selection to the + // whole paragraph) + + EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr; + OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" ); + if (pEditEngine) + { + for (sal_uInt16 i : aLangWhichId_EE) + rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i )); + pEditEngine->QuickSetAttribs(rCoreSet, rSelection); + } + else + { + rWrtSh.GetCurAttr( rCoreSet ); + for (sal_uInt16 i : aLangWhichId_Writer) + rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i )); + rWrtSh.SetAttrSet( rCoreSet ); + } + } + else // change language for all text + { + std::set<sal_uInt16> aAttribs; + for (sal_uInt16 i : aLangWhichId_Writer) + { + rWrtSh.SetDefault( SvxLanguageItem( LANGUAGE_NONE, i ) ); + aAttribs.insert( i ); + } + + // set all language attributes to default + // (for all text in the document - which should be selected by now...) + rWrtSh.ResetAttr( aAttribs ); + } + } + + void ResetLanguages( SwWrtShell &rWrtSh, OutlinerView const * pOLV ) + { + // reset language for current selection. + // The selection should already have been expanded to the whole paragraph or + // to all text in the document if those are the ranges where to reset + // the language attributes + + if (pOLV) + { + EditView &rEditView = pOLV->GetEditView(); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE ); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CJK ); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CTL ); + } + else + { + std::set<sal_uInt16> aAttribs; + aAttribs.insert( RES_CHRATR_LANGUAGE ); + aAttribs.insert( RES_CHRATR_CJK_LANGUAGE ); + aAttribs.insert( RES_CHRATR_CTL_LANGUAGE ); + rWrtSh.ResetAttr( aAttribs ); + } + } + + /// @returns : the language for the selected text that is set for the + /// specified attribute (script type). + /// If there are more than one languages used LANGUAGE_DONTKNOW will be returned. + /// @param nLangWhichId : one of + /// RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, + LanguageType GetLanguage( SwWrtShell &rSh, sal_uInt16 nLangWhichId ) + { + SfxItemSet aSet( rSh.GetAttrPool(), {{nLangWhichId, nLangWhichId}} ); + rSh.GetCurAttr( aSet ); + + return GetLanguage(aSet,nLangWhichId); + } + + LanguageType GetLanguage( SfxItemSet const & aSet, sal_uInt16 nLangWhichId ) + { + + LanguageType nLang = LANGUAGE_SYSTEM; + + const SfxPoolItem *pItem = nullptr; + SfxItemState nState = aSet.GetItemState( nLangWhichId, true, &pItem ); + if (nState > SfxItemState::DEFAULT && pItem) + { + // the item is set and can be used + nLang = dynamic_cast<const SvxLanguageItem&>(*pItem).GetLanguage(); + } + else if (nState == SfxItemState::DEFAULT) + { + // since the attribute is not set: retrieve the default value + nLang = dynamic_cast<const SvxLanguageItem&>(aSet.GetPool()->GetDefaultItem( nLangWhichId )).GetLanguage(); + } + else if (nState == SfxItemState::DONTCARE) + { + // there is more than one language... + nLang = LANGUAGE_DONTKNOW; + } + OSL_ENSURE( nLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nLang; + } + + /// @returns: the language in use for the selected text. + /// 'In use' means the language(s) matching the script type(s) of the + /// selected text. Or in other words, the language a spell checker would use. + /// If there is more than one language LANGUAGE_DONTKNOW will be returned. + LanguageType GetCurrentLanguage( SwWrtShell &rSh ) + { + //set language attribute to use according to the script type + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (rSh.GetScriptType()) + { + case SvtScriptType::LATIN : nLangWhichId = RES_CHRATR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break; + case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break; + default: bIsSingleScriptType = false; break; + } + + // get language according to the script type(s) in use + LanguageType nCurrentLang = LANGUAGE_SYSTEM; + if (bIsSingleScriptType) + nCurrentLang = GetLanguage( rSh, nLangWhichId ); + else + { + // check if all script types are set to LANGUAGE_NONE and return + // that if this is the case. Otherwise, having multiple script types + // in use always means there are several languages in use... + const sal_uInt16 aScriptTypes[3] = + { + RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE + }; + nCurrentLang = LANGUAGE_NONE; + for (sal_uInt16 aScriptType : aScriptTypes) + { + LanguageType nTmpLang = GetLanguage( rSh, aScriptType ); + if (nTmpLang != LANGUAGE_NONE) + { + nCurrentLang = LANGUAGE_DONTKNOW; + break; + } + } + } + OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nCurrentLang; + } + + /// @returns: the language in use for the selected text. + /// 'In use' means the language(s) matching the script type(s) of the + /// selected text. Or in other words, the language a spell checker would use. + /// If there is more than one language LANGUAGE_DONTKNOW will be returned. + LanguageType GetCurrentLanguage( SfxItemSet const & aSet, SvtScriptType nScriptType ) + { + //set language attribute to use according to the script type + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: bIsSingleScriptType = false; + } + + // get language according to the script type(s) in use + LanguageType nCurrentLang = LANGUAGE_SYSTEM; + if (bIsSingleScriptType) + nCurrentLang = GetLanguage( aSet, nLangWhichId ); + else + { + // check if all script types are set to LANGUAGE_NONE and return + // that if this is the case. Otherwise, having multiple script types + // in use always means there are several languages in use... + const sal_uInt16 aScriptTypes[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + nCurrentLang = LANGUAGE_NONE; + for (sal_uInt16 aScriptType : aScriptTypes) + { + LanguageType nTmpLang = GetLanguage( aSet, aScriptType ); + if (nTmpLang != LANGUAGE_NONE) + { + nCurrentLang = LANGUAGE_DONTKNOW; + break; + } + } + } + OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nCurrentLang; + } + + OUString GetTextForLanguageGuessing( SwWrtShell const &rSh ) + { + // string for guessing language + OUString aText; + SwPaM *pCursor = rSh.GetCursor(); + SwTextNode *pNode = pCursor->GetNode().GetTextNode(); + if (pNode) + { + aText = pNode->GetText(); + if (!aText.isEmpty()) + { + sal_Int32 nEnd = pCursor->GetPoint()->nContent.GetIndex(); + // at most 100 chars to the left... + const sal_Int32 nStt = nEnd > 100 ? nEnd - 100 : 0; + // ... and 100 to the right of the cursor position + nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength(); + aText = aText.copy( nStt, nEnd - nStt ); + } + } + return aText; + } + + OUString GetTextForLanguageGuessing(EditEngine const * rEditEngine, const ESelection& rDocSelection) + { + // string for guessing language + + // get the full text of the paragraph that the end of selection is in + OUString aText = rEditEngine->GetText(rDocSelection.nEndPos); + if (!aText.isEmpty()) + { + sal_Int32 nStt = 0; + sal_Int32 nEnd = rDocSelection.nEndPos; + // at most 100 chars to the left... + nStt = nEnd > 100 ? nEnd - 100 : 0; + // ... and 100 to the right of the cursor position + nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength(); + aText = aText.copy( nStt, nEnd - nStt ); + } + + return aText; + } + + void SelectPara( EditView &rEditView, const ESelection &rCurSel ) + { + ESelection aParaSel( rCurSel.nStartPara, 0, rCurSel.nStartPara, EE_TEXTPOS_ALL ); + rEditView.SetSelection( aParaSel ); + } + + void SelectCurrentPara( SwWrtShell &rWrtSh ) + { + // select current para + if (!rWrtSh.IsSttPara()) + rWrtSh.MovePara( GoCurrPara, fnParaStart ); + if (!rWrtSh.HasMark()) + rWrtSh.SetMark(); + rWrtSh.SwapPam(); + if (!rWrtSh.IsEndPara()) + rWrtSh.MovePara( GoCurrPara, fnParaEnd ); + #if OSL_DEBUG_LEVEL > 1 + OUString aSelText; + rWrtSh.GetSelectedText( aSelText ); + (void) aSelText; + #endif + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/listsh.cxx b/sw/source/uibase/shells/listsh.cxx new file mode 100644 index 000000000..a8d7eb91f --- /dev/null +++ b/sw/source/uibase/shells/listsh.cxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/request.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> + +#include <numrule.hxx> +#include <wrtsh.hxx> +#include <listsh.hxx> +#include <view.hxx> + +#define ShellClass_SwListShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +#include <IDocumentOutlineNodes.hxx> + +SFX_IMPL_INTERFACE(SwListShell, SwBaseShell) + +void SwListShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Num_Toolbox); +} + + +// #i35572# Functionality of Numbering/Bullet toolbar +// for outline numbered paragraphs should match the functions for outlines +// available in the navigator. Therefore the code in the following +// function is quite similar the code in SwContentTree::ExecCommand. +static void lcl_OutlineUpDownWithSubPoints( SwWrtShell& rSh, bool bMove, bool bUp ) +{ + const SwOutlineNodes::size_type nActPos = rSh.GetOutlinePos(); + if ( nActPos < SwOutlineNodes::npos && rSh.IsOutlineMovable( nActPos ) ) + { + rSh.Push(); + rSh.MakeOutlineSel( nActPos, nActPos, true ); + + if ( bMove ) + { + const IDocumentOutlineNodes* pIDoc( rSh.getIDocumentOutlineNodesAccess() ); + const int nActLevel = pIDoc->getOutlineLevel( nActPos ); + SwOutlineNodes::difference_type nDir = 0; + + if ( !bUp ) + { + // Move down with subpoints: + SwOutlineNodes::size_type nActEndPos = nActPos + 1; + while ( nActEndPos < pIDoc->getOutlineNodesCount() && + (!pIDoc->isOutlineInLayout(nActEndPos, *rSh.GetLayout()) + || nActLevel < pIDoc->getOutlineLevel(nActEndPos))) + { + ++nActEndPos; + } + + if ( nActEndPos < pIDoc->getOutlineNodesCount() ) + { + // The current subpoint which should be moved + // starts at nActPos and ends at nActEndPos - 1 + --nActEndPos; + SwOutlineNodes::size_type nDest = nActEndPos + 2; + while ( nDest < pIDoc->getOutlineNodesCount() && + (!pIDoc->isOutlineInLayout(nDest, *rSh.GetLayout()) + || nActLevel < pIDoc->getOutlineLevel(nDest))) + { + ++nDest; + } + + nDir = nDest - 1 - nActEndPos; + } + } + else + { + // Move up with subpoints: + if ( nActPos > 0 ) + { + SwOutlineNodes::size_type nDest = nActPos - 1; + while (nDest > 0 && + (!pIDoc->isOutlineInLayout(nDest, *rSh.GetLayout()) + || nActLevel < pIDoc->getOutlineLevel(nDest))) + { + --nDest; + } + + nDir = nDest - nActPos; + } + } + + if ( nDir ) + { + rSh.MoveOutlinePara( nDir ); + rSh.GotoOutline( nActPos + nDir ); + } + } + else + { + // Up/down with subpoints: + rSh.OutlineUpDown( bUp ? -1 : 1 ); + } + + rSh.ClearMark(); + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } +} + +void SwListShell::Execute(SfxRequest &rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + const sal_uInt16 nSlot = rReq.GetSlot(); + SwWrtShell& rSh = GetShell(); + + // #i35572# + const SwNumRule* pCurRule = rSh.GetNumRuleAtCurrCursorPos(); + OSL_ENSURE( pCurRule, "SwListShell::Execute without NumRule" ); + bool bOutline = pCurRule && pCurRule->IsOutlineRule(); + + switch (nSlot) + { + case FN_NUM_BULLET_DOWN: + case FN_NUM_BULLET_UP: + { + SfxViewFrame * pFrame = GetView().GetViewFrame(); + + rReq.Done(); + rSh.NumUpDown( nSlot == FN_NUM_BULLET_DOWN ); + pFrame->GetBindings().Invalidate( SID_TABLE_CELL ); // Update status line! + } + break; + + case FN_NUM_BULLET_NEXT: + rSh.GotoNextNum(); + rReq.Done(); + break; + + case FN_NUM_BULLET_NONUM: + rSh.NoNum(); + rReq.Done(); + break; + + case FN_NUM_BULLET_OFF: + { + rReq.Ignore(); + SfxRequest aReq( GetView().GetViewFrame(), FN_NUM_BULLET_ON ); + aReq.AppendItem( SfxBoolItem( FN_PARAM_1, false ) ); + aReq.Done(); + rSh.DelNumRules(); + break; + } + + case FN_NUM_BULLET_OUTLINE_DOWN: + if ( bOutline ) + lcl_OutlineUpDownWithSubPoints( rSh, false, false ); + else + rSh.MoveNumParas(false, false); + rReq.Done(); + break; + + case FN_NUM_BULLET_OUTLINE_MOVEDOWN: + if ( bOutline ) + lcl_OutlineUpDownWithSubPoints( rSh, true, false ); + else + rSh.MoveNumParas(true, false); + rReq.Done(); + break; + + case FN_NUM_BULLET_OUTLINE_MOVEUP: + if ( bOutline ) + lcl_OutlineUpDownWithSubPoints( rSh, true, true ); + else + rSh.MoveNumParas(true, true); + rReq.Done(); + break; + + case FN_NUM_BULLET_OUTLINE_UP: + if ( bOutline ) + lcl_OutlineUpDownWithSubPoints( rSh, false, true ); + else + rSh.MoveNumParas(false, true); + rReq.Done(); + break; + + case FN_NUM_BULLET_PREV: + rSh.GotoPrevNum(); + rReq.Done(); + break; + + case FN_NUM_OR_NONUM: + { + bool bApi = rReq.IsAPI(); + bool bDelete = !rSh.IsNoNum(!bApi); + if(pArgs ) + bDelete = static_cast<const SfxBoolItem &>(pArgs->Get(rReq.GetSlot())).GetValue(); + rSh.NumOrNoNum( bDelete, !bApi ); + rReq.AppendItem( SfxBoolItem( nSlot, bDelete ) ); + rReq.Done(); + } + break; + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +void SwListShell::GetState(SfxItemSet &rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SwWrtShell& rSh = GetShell(); + sal_uInt8 nCurrentNumLevel = rSh.GetNumLevel(); + while ( nWhich ) + { + switch( nWhich ) + { + case FN_NUM_OR_NONUM: + rSet.Put(SfxBoolItem(nWhich, GetShell().IsNoNum(false))); + break; + case FN_NUM_BULLET_OUTLINE_UP: + case FN_NUM_BULLET_UP: + if(!nCurrentNumLevel) + rSet.DisableItem(nWhich); + break; + case FN_NUM_BULLET_OUTLINE_DOWN : + { + sal_uInt8 nUpper = 0; + sal_uInt8 nLower = 0; + rSh.GetCurrentOutlineLevels( nUpper, nLower ); + if(nLower == (MAXLEVEL - 1)) + rSet.DisableItem(nWhich); + } + break; + case FN_NUM_BULLET_DOWN: + if(nCurrentNumLevel == (MAXLEVEL - 1)) + rSet.DisableItem(nWhich); + break; + + case FN_NUM_BULLET_NONUM: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +SwListShell::SwListShell(SwView &_rView) : + SwBaseShell(_rView) +{ + SetName("List"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/mediash.cxx b/sw/source/uibase/shells/mediash.cxx new file mode 100644 index 000000000..932b2feb6 --- /dev/null +++ b/sw/source/uibase/shells/mediash.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/whiter.hxx> +#include <sfx2/request.hxx> +#include <svx/svdview.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <mediash.hxx> + +#include <sfx2/objface.hxx> +#include <vcl/EnumContext.hxx> +#include <svx/svdomedia.hxx> +#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx> +#include <avmedia/mediaitem.hxx> + +#define ShellClass_SwMediaShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <memory> + +SFX_IMPL_INTERFACE(SwMediaShell, SwBaseShell) + +void SwMediaShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("media"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Media_Toolbox); +} + +void SwMediaShell::ExecMedia(SfxRequest const &rReq) +{ + SwWrtShell* pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + + if( pSdrView ) + { + const SfxItemSet* pArgs = rReq.GetArgs(); + bool bChanged = pSdrView->GetModel()->IsChanged(); + + pSdrView->GetModel()->SetChanged( false ); + + switch( rReq.GetSlot() ) + { + case SID_DELETE: + { + if( pSh->IsObjSelected() ) + { + pSh->SetModified(); + pSh->DelSelectedObj(); + + if( pSh->IsSelFrameMode() ) + pSh->LeaveSelFrameMode(); + + GetView().AttrChangedNotify(nullptr); + } + } + break; + + case SID_AVMEDIA_TOOLBOX: + { + if( pSh->IsObjSelected() ) + { + const SfxPoolItem* pItem; + + if( !pArgs || ( SfxItemState::SET != pArgs->GetItemState( SID_AVMEDIA_TOOLBOX, false, &pItem ) ) ) + pItem = nullptr; + + if( pItem ) + { + std::unique_ptr<SdrMarkList> pMarkList(new SdrMarkList( pSdrView->GetMarkedObjectList() )); + + if( 1 == pMarkList->GetMarkCount() ) + { + SdrObject* pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj(); + + if( dynamic_cast< const SdrMediaObj *>( pObj ) ) + { + static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( pObj->GetViewContact() ).executeMediaItem( + static_cast< const ::avmedia::MediaItem& >( *pItem ) ); + } + } + } + } + } + break; + + default: + break; + } + + if( pSdrView->GetModel()->IsChanged() ) + GetShell().SetModified(); + else if( bChanged ) + pSdrView->GetModel()->SetChanged(); + } +} + +void SwMediaShell::GetMediaState(SfxItemSet &rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while( nWhich ) + { + if( SID_AVMEDIA_TOOLBOX == nWhich ) + { + SwWrtShell& rSh = GetShell(); + SdrView* pView = rSh.GetDrawView(); + + if( pView ) + { + bool bDisable = true; + std::unique_ptr<SdrMarkList> pMarkList(new SdrMarkList( pView->GetMarkedObjectList() )); + + if( 1 == pMarkList->GetMarkCount() ) + { + SdrObject* pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj(); + + if( dynamic_cast< const SdrMediaObj *>( pObj ) ) + { + ::avmedia::MediaItem aItem( SID_AVMEDIA_TOOLBOX ); + + static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( pObj->GetViewContact() ).updateMediaItem( aItem ); + rSet.Put( aItem ); + bDisable = false; + } + } + + if( bDisable ) + rSet.DisableItem( SID_AVMEDIA_TOOLBOX ); + } + } + + nWhich = aIter.NextWhich(); + } +} + +SwMediaShell::SwMediaShell(SwView &_rView) : + SwBaseShell(_rView) + +{ + SetName("Media Playback"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Media)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/navsh.cxx b/sw/source/uibase/shells/navsh.cxx new file mode 100644 index 000000000..b5b9021cc --- /dev/null +++ b/sw/source/uibase/shells/navsh.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/. + */ + +#include <cmdid.h> +#include <svx/svdview.hxx> +#include <svl/whiter.hxx> +#include <sfx2/request.hxx> +#include <sfx2/objface.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <navsh.hxx> +#define ShellClass_SwNavigationShell +#include <swslots.hxx> +#include <navmgr.hxx> + +SFX_IMPL_INTERFACE(SwNavigationShell, SwBaseShell) + +void SwNavigationShell::InitInterface_Impl() +{ +} + +SwNavigationShell::SwNavigationShell(SwView &_rView) + : SwBaseShell( _rView ) +{ + SetName("Navigation"); +} + +void SwNavigationShell::Execute(SfxRequest const &rReq) +{ + SwWrtShell *pSh = &GetShell(); + SdrView* pSdrView = pSh->GetDrawView(); + const SfxItemSet *pArgs = rReq.GetArgs(); + const sal_uInt16 nSlotId = rReq.GetSlot(); + bool bChanged = pSdrView->GetModel()->IsChanged(); + pSdrView->GetModel()->SetChanged(false); + SwNavigationMgr& aSwNavigationMgr = pSh->GetNavigationMgr(); + const SfxPoolItem* pItem; + if(pArgs) + pArgs->GetItemState(nSlotId, false, &pItem); + switch (nSlotId) + { + case FN_NAVIGATION_BACK: + aSwNavigationMgr.goBack(); + break; + + case FN_NAVIGATION_FORWARD: + aSwNavigationMgr.goForward(); + break; + default: + break; + } + if (pSdrView->GetModel()->IsChanged()) + GetShell().SetModified(); + else if (bChanged) + pSdrView->GetModel()->SetChanged(); +} + +// determine if the buttons should be enabled/disabled + +void SwNavigationShell::GetState(SfxItemSet &rSet) +{ + SwWrtShell *pSh = &GetShell(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SwNavigationMgr& aNavigationMgr = pSh->GetNavigationMgr(); + while( nWhich ) + { + switch( nWhich ) + { + case FN_NAVIGATION_BACK: + if (!aNavigationMgr.backEnabled()) + { + rSet.DisableItem(FN_NAVIGATION_BACK); + } + break; + case FN_NAVIGATION_FORWARD: + if (!aNavigationMgr.forwardEnabled()) + { + rSet.DisableItem(FN_NAVIGATION_FORWARD); + } + break; + default: + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/olesh.cxx b/sw/source/uibase/shells/olesh.cxx new file mode 100644 index 000000000..b8207d200 --- /dev/null +++ b/sw/source/uibase/shells/olesh.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 <sfx2/objface.hxx> +#include <vcl/EnumContext.hxx> +#include <view.hxx> +#include <frmsh.hxx> +#include <olesh.hxx> + +#define ShellClass_SwOleShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwOleShell, SwFrameShell) + +void SwOleShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("oleobject"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Ole_Toolbox); +} + +SwOleShell::SwOleShell(SwView &_rView) : + SwFrameShell(_rView) + +{ + SetName("Object"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::OLE)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/slotadd.cxx b/sw/source/uibase/shells/slotadd.cxx new file mode 100644 index 000000000..76de1a6dc --- /dev/null +++ b/sw/source/uibase/shells/slotadd.cxx @@ -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 . + */ + +#include <config_options.h> + +#include <cmdid.h> +#include <unomid.h> +#include <svx/unomid.hxx> + +#include <svl/globalnameitem.hxx> +#include <editeng/memberids.h> +#include <svl/imageitm.hxx> +#include <svl/rectitem.hxx> +#include <sfx2/objitem.hxx> +#include <sfx2/objsh.hxx> +#include <svx/rulritem.hxx> +#include <sfx2/zoomitem.hxx> +#include <svx/viewlayoutitem.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/SmartTagItem.hxx> +#include <svl/ptitem.hxx> +#include <svx/pageitem.hxx> +#include <svl/srchitem.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/watermarkitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/protitem.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/autokernitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/pbinitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/prntitem.hxx> +#include <editeng/orphitem.hxx> +#include <editeng/widwitem.hxx> +#include <editeng/lineitem.hxx> +#include <editeng/pmdlitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/hyphenzoneitem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xtextit0.hxx> +#include <svx/xftadit.hxx> +#include <svx/xftdiit.hxx> +#include <svx/xftstit.hxx> +#include <svx/xftmrit.hxx> +#include <svx/xftouit.hxx> +#include <svx/xftshit.hxx> +#include <svx/xftshcit.hxx> +#include <svx/xftshxy.hxx> +#include <svx/grafctrl.hxx> + +#include <paratr.hxx> +#include <fmtinfmt.hxx> +#include <fmtsrnd.hxx> +#include <envimg.hxx> +#include <fmtline.hxx> +#include <svx/clipfmtitem.hxx> +#include <editeng/blinkitem.hxx> +#include <svl/slstitm.hxx> +#include <editeng/paravertalignitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <svx/postattr.hxx> +#include <sfx2/frame.hxx> +#include <svx/chrtitem.hxx> +#include <svx/drawitem.hxx> +#include <avmedia/mediaitem.hxx> + +#define avmedia_MediaItem ::avmedia::MediaItem + +#include <svx/xflftrit.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/galleryitem.hxx> + +#define SFX_TYPEMAP +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/tabsh.cxx b/sw/source/uibase/shells/tabsh.cxx new file mode 100644 index 000000000..d2e238044 --- /dev/null +++ b/sw/source/uibase/shells/tabsh.cxx @@ -0,0 +1,1602 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/zforlist.hxx> +#include <svl/stritem.hxx> +#include <svl/whiter.hxx> +#include <unotools/moduleoptions.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/spltitem.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/lineitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/numinf.hxx> +#include <svx/svddef.hxx> +#include <svx/svxdlg.hxx> +#include <sfx2/bindings.hxx> +#include <vcl/weld.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/EnumContext.hxx> +#include <o3tl/enumrange.hxx> +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <editeng/itemtype.hxx> + +#include <fmtornt.hxx> +#include <fmtlsplt.hxx> +#include <fmtrowsplt.hxx> +#include <fmtfsize.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> +#include <frmatr.hxx> +#include <uitool.hxx> +#include <inputwin.hxx> +#include <uiitems.hxx> +#include <tabsh.hxx> +#include <swtablerep.hxx> +#include <tablemgr.hxx> +#include <cellatr.hxx> +#include <frmfmt.hxx> +#include <swundo.hxx> +#include <swtable.hxx> +#include <docsh.hxx> +#include <tblsel.hxx> +#include <viewopt.hxx> + +#include <strings.hrc> +#include <cmdid.h> +#include <unobaseclass.hxx> + +#define ShellClass_SwTableShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +#include <swabstdlg.hxx> + +#include <memory> + +using ::editeng::SvxBorderLine; +using namespace ::com::sun::star; + +SFX_IMPL_INTERFACE(SwTableShell, SwBaseShell) + +void SwTableShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("table"); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Table_Toolbox); +} + + +static const sal_uInt16 aUITableAttrRange[] = +{ + XATTR_FILL_FIRST, XATTR_FILL_LAST, + FN_PARAM_TABLE_NAME, FN_PARAM_TABLE_NAME, + FN_PARAM_TABLE_HEADLINE, FN_PARAM_TABLE_HEADLINE, + FN_PARAM_TABLE_SPACE, FN_PARAM_TABLE_SPACE, + FN_TABLE_REP, FN_TABLE_REP, + SID_RULER_BORDERS, SID_RULER_BORDERS, + RES_LR_SPACE, RES_UL_SPACE, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_SHADOW, + RES_BOX, RES_SHADOW, + RES_BACKGROUND, RES_BACKGROUND, + SID_BACKGRND_DESTINATION, SID_BACKGRND_DESTINATION, + SID_HTML_MODE, SID_HTML_MODE, + SID_ATTR_BRUSH_ROW, SID_ATTR_BRUSH_TABLE, + RES_PAGEDESC, RES_BREAK, + RES_KEEP, RES_KEEP, + RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT, + FN_TABLE_SET_VERT_ALIGN, FN_TABLE_SET_VERT_ALIGN, + RES_FRAMEDIR, RES_FRAMEDIR, + RES_ROW_SPLIT, RES_ROW_SPLIT, + FN_TABLE_BOX_TEXTORIENTATION, FN_TABLE_BOX_TEXTORIENTATION, +// #i29550# + RES_COLLAPSING_BORDERS, RES_COLLAPSING_BORDERS, +// <-- collapsing borders + 0 +}; + +const sal_uInt16* SwuiGetUITableAttrRange() +{ + return aUITableAttrRange; +} + +static void lcl_SetAttr( SwWrtShell &rSh, const SfxPoolItem &rItem ) +{ + SfxItemSet aSet( rSh.GetView().GetPool(), {{rItem.Which(), rItem.Which()}}); + aSet.Put( rItem ); + rSh.SetTableAttr( aSet ); +} + +static std::shared_ptr<SwTableRep> lcl_TableParamToItemSet( SfxItemSet& rSet, SwWrtShell &rSh ) +{ + std::shared_ptr<SwTableRep> pRep; + + SwFrameFormat *pFormat = rSh.GetTableFormat(); + SwTabCols aCols; + rSh.GetTabCols( aCols ); + + //At first get the simple attributes. + rSet.Put( SfxStringItem( FN_PARAM_TABLE_NAME, pFormat->GetName())); + rSet.Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE, rSh.GetRowsToRepeat() ) ); + rSet.Put( pFormat->GetShadow() ); + rSet.Put(SfxUInt16Item(FN_TABLE_SET_VERT_ALIGN, rSh.GetBoxAlign())); + rSet.Put( pFormat->GetFrameDir() ); + + SvxULSpaceItem aULSpace( pFormat->GetULSpace() ); + rSet.Put( aULSpace ); + + const sal_uInt16 nBackgroundDestination = rSh.GetViewOptions()->GetTableDest(); + rSet.Put(SfxUInt16Item(SID_BACKGRND_DESTINATION, nBackgroundDestination )); + std::unique_ptr<SvxBrushItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND)); + if(rSh.GetRowBackground(aBrush)) + { + aBrush->SetWhich(SID_ATTR_BRUSH_ROW); + rSet.Put( *aBrush ); + } + else + rSet.InvalidateItem(SID_ATTR_BRUSH_ROW); + rSh.GetTabBackground(aBrush); + aBrush->SetWhich(SID_ATTR_BRUSH_TABLE); + rSet.Put( *aBrush ); + + // text direction in boxes + std::unique_ptr<SvxFrameDirectionItem> aBoxDirection(std::make_unique<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)); + if(rSh.GetBoxDirection( aBoxDirection )) + { + aBoxDirection->SetWhich(FN_TABLE_BOX_TEXTORIENTATION); + rSet.Put(*aBoxDirection); + } + + bool bSelectAll = rSh.StartsWithTable() && rSh.ExtendedSelectedAll(); + bool bTableSel = rSh.IsTableMode() || bSelectAll; + if(!bTableSel) + { + rSh.StartAllAction(); + rSh.Push(); + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute( FN_TABLE_SELECT_ALL ); + } + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + + // Table variant: If multiple table cells are selected. + rSh.GetCursor(); //Thus GetCursorCnt() returns the right thing + aBoxInfo.SetTable ((rSh.IsTableMode() && rSh.GetCursorCnt() > 1) || + !bTableSel); + // Always show distance field. + aBoxInfo.SetDist (true); + // Set minimum size in tables and paragraphs. + aBoxInfo.SetMinDist( !bTableSel || rSh.IsTableMode() || + rSh.GetSelectionType() & + (SelectionType::Text | SelectionType::Table)); + // Always set the default spacing. + aBoxInfo.SetDefDist (MIN_BORDER_DIST); + // Individual lines can have DontCare status only in tables. + aBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISABLE, !bTableSel || !rSh.IsTableMode() ); + + rSet.Put(aBoxInfo); + rSh.GetTabBorders( rSet ); + + //row split + std::unique_ptr<SwFormatRowSplit> pSplit = rSh.GetRowSplit(); + if(pSplit) + rSet.Put(std::move(pSplit)); + + if(!bTableSel) + { + rSh.ClearMark(); + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.EndAllAction(); + } + + SwTabCols aTabCols; + rSh.GetTabCols( aTabCols ); + + // Pointer will be deleted after the dialogue execution. + pRep = std::make_shared<SwTableRep>(aTabCols); + pRep->SetSpace(aCols.GetRightMax()); + + sal_uInt16 nPercent = 0; + auto nWidth = ::GetTableWidth(pFormat, aCols, &nPercent, &rSh ); + // The table width is wrong for relative values. + if (nPercent) + nWidth = pRep->GetSpace() * nPercent / 100; + const sal_uInt16 nAlign = pFormat->GetHoriOrient().GetHoriOrient(); + pRep->SetAlign(nAlign); + SvxLRSpaceItem aLRSpace( pFormat->GetLRSpace() ); + SwTwips nLeft = aLRSpace.GetLeft(); + SwTwips nRight = aLRSpace.GetRight(); + SwTwips nDiff = pRep->GetSpace() - nRight - nLeft - nWidth; + if(nAlign != text::HoriOrientation::FULL && std::abs(nDiff) > 2) + { + SwTwips nLR = pRep->GetSpace() - nWidth; + switch ( nAlign ) + { + case text::HoriOrientation::CENTER: + nLeft = nRight = nLR / 2; + break; + case text::HoriOrientation::LEFT: + nRight = nLR; + nLeft = 0; + break; + case text::HoriOrientation::RIGHT: + nLeft = nLR; + nRight = 0; + break; + case text::HoriOrientation::LEFT_AND_WIDTH: + nRight = nLR - nLeft; + break; + case text::HoriOrientation::NONE: + if(!nPercent) + nWidth = pRep->GetSpace() - nLeft - nRight; + break; + } + } + pRep->SetLeftSpace(nLeft); + pRep->SetRightSpace(nRight); + + pRep->SetWidth(nWidth); + pRep->SetWidthPercent(nPercent); + // Are individual rows / cells are selected, the column processing will be changed. + pRep->SetLineSelected(bTableSel && ! rSh.HasWholeTabSelection()); + rSet.Put(SwPtrItem(FN_TABLE_REP, pRep.get())); + return pRep; +} + +void ItemSetToTableParam( const SfxItemSet& rSet, + SwWrtShell &rSh ) +{ + rSh.StartAllAction(); + rSh.StartUndo( SwUndoId::TABLE_ATTR ); + const SfxPoolItem* pItem = nullptr; + + if(SfxItemState::SET == rSet.GetItemState(SID_BACKGRND_DESTINATION, false, &pItem)) + { + SwViewOption aUsrPref( *rSh.GetViewOptions() ); + aUsrPref.SetTableDest(static_cast<sal_uInt8>(static_cast<const SfxUInt16Item*>(pItem)->GetValue())); + SW_MOD()->ApplyUsrPref(aUsrPref, &rSh.GetView()); + } + bool bBorder = ( SfxItemState::SET == rSet.GetItemState( RES_BOX ) || + SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER ) ); + pItem = nullptr; + bool bBackground = SfxItemState::SET == rSet.GetItemState( RES_BACKGROUND, false, &pItem ); + const SfxPoolItem* pRowItem = nullptr, *pTableItem = nullptr; + bBackground |= SfxItemState::SET == rSet.GetItemState( SID_ATTR_BRUSH_ROW, false, &pRowItem ); + bBackground |= SfxItemState::SET == rSet.GetItemState( SID_ATTR_BRUSH_TABLE, false, &pTableItem ); + const SfxPoolItem* pSplit = nullptr; + bool bRowSplit = SfxItemState::SET == rSet.GetItemState( RES_ROW_SPLIT, false, &pSplit ); + const SfxPoolItem* pBoxDirection = nullptr; + bool bBoxDirection = SfxItemState::SET == rSet.GetItemState( FN_TABLE_BOX_TEXTORIENTATION, false, &pBoxDirection ); + if( bBackground || bBorder || bRowSplit || bBoxDirection) + { + // The border will be applied to the present selection. + // If there is no selection, the table will be completely selected. + // The background will always be applied to the current state. + bool bTableSel = rSh.IsTableMode(); + rSh.StartAllAction(); + + if(bBackground) + { + if(pItem) + rSh.SetBoxBackground( *static_cast<const SvxBrushItem*>(pItem) ); + if(pRowItem) + { + std::unique_ptr<SvxBrushItem> aBrush(static_cast<SvxBrushItem*>(pRowItem->Clone())); + aBrush->SetWhich(RES_BACKGROUND); + rSh.SetRowBackground(*aBrush); + } + if(pTableItem) + { + std::unique_ptr<SvxBrushItem> aBrush(static_cast<SvxBrushItem*>(pTableItem->Clone())); + aBrush->SetWhich(RES_BACKGROUND); + rSh.SetTabBackground( *aBrush ); + } + } + + if(bBoxDirection) + { + SvxFrameDirectionItem aDirection( SvxFrameDirection::Environment, RES_FRAMEDIR ); + aDirection.SetValue(static_cast< const SvxFrameDirectionItem* >(pBoxDirection)->GetValue()); + rSh.SetBoxDirection(aDirection); + } + + if(bBorder || bRowSplit) + { + rSh.Push(); + if(!bTableSel) + { + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute( FN_TABLE_SELECT_ALL ); + } + if(bBorder) + rSh.SetTabBorders( rSet ); + + if(bRowSplit) + { + rSh.SetRowSplit(*static_cast<const SwFormatRowSplit*>(pSplit)); + } + + if(!bTableSel) + { + rSh.ClearMark(); + } + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + } + + rSh.EndAllAction(); + } + + SwTabCols aTabCols; + bool bTabCols = false; + SwTableRep* pRep = nullptr; + SwFrameFormat *pFormat = rSh.GetTableFormat(); + SfxItemSet aSet( rSh.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} ); + if(SfxItemState::SET == rSet.GetItemState( FN_TABLE_REP, false, &pItem )) + { + pRep = static_cast<SwTableRep*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + + const SwTwips nWidth = pRep->GetWidth(); + if ( text::HoriOrientation::FULL == pRep->GetAlign() ) + { + SwFormatHoriOrient aAttr( pFormat->GetHoriOrient() ); + aAttr.SetHoriOrient( text::HoriOrientation::FULL ); + aSet.Put( aAttr ); + } + else + { + SwFormatFrameSize aSz( SwFrameSize::Variable, nWidth ); + if(pRep->GetWidthPercent()) + { + aSz.SetWidthPercent( static_cast<sal_uInt8>(pRep->GetWidthPercent()) ); + } + aSet.Put(aSz); + } + + SvxLRSpaceItem aLRSpace( RES_LR_SPACE ); + aLRSpace.SetLeft(pRep->GetLeftSpace()); + aLRSpace.SetRight(pRep->GetRightSpace()); + aSet.Put( aLRSpace ); + + sal_Int16 eOrient = pRep->GetAlign(); + SwFormatHoriOrient aAttr( 0, eOrient ); + aSet.Put( aAttr ); + // The item must only be recorded while manual alignment, so that the + // alignment is not overwritten by the distances while recording. + if(eOrient != text::HoriOrientation::NONE) + const_cast<SfxItemSet&>(rSet).ClearItem( SID_ATTR_LRSPACE ); + + if(pRep->HasColsChanged()) + { + bTabCols = true; + } + } + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_TABLE_HEADLINE, false, &pItem)) + rSh.SetRowsToRepeat( static_cast<const SfxUInt16Item*>(pItem)->GetValue() ); + + if( SfxItemState::SET == rSet.GetItemState( FN_TABLE_SET_VERT_ALIGN, false, &pItem)) + rSh.SetBoxAlign(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_TABLE_NAME, false, &pItem )) + rSh.SetTableName( *pFormat, static_cast<const SfxStringItem*>(pItem)->GetValue() ); + + // Copy the chosen attributes in the ItemSet. + static const sal_uInt16 aIds[] = + { + RES_PAGEDESC, + RES_BREAK, + RES_KEEP, + RES_LAYOUT_SPLIT, + RES_UL_SPACE, + RES_SHADOW, + RES_FRAMEDIR, + // #i29550# + RES_COLLAPSING_BORDERS, + // <-- collapsing borders + 0 + }; + for( const sal_uInt16* pIds = aIds; *pIds; ++pIds ) + if( SfxItemState::SET == rSet.GetItemState( *pIds, false, &pItem)) + aSet.Put( *pItem ); + + if( aSet.Count() ) + rSh.SetTableAttr( aSet ); + + if(bTabCols) + { + rSh.GetTabCols( aTabCols ); + bool bSingleLine = pRep->FillTabCols( aTabCols ); + rSh.SetTabCols( aTabCols, bSingleLine ); + } + + rSh.EndUndo( SwUndoId::TABLE_ATTR ); + rSh.EndAllAction(); +} + +static void lcl_TabGetMaxLineWidth(const SvxBorderLine* pBorderLine, SvxBorderLine& rBorderLine) +{ + if(pBorderLine->GetWidth() > rBorderLine.GetWidth()) + rBorderLine.SetWidth(pBorderLine->GetWidth()); + + rBorderLine.SetBorderLineStyle(pBorderLine->GetBorderLineStyle()); + rBorderLine.SetColor(pBorderLine->GetColor()); +} + +void SwTableShell::Execute(SfxRequest &rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + SwWrtShell &rSh = GetShell(); + + // At first the slots which doesn't need a FrameMgr. + bool bMore = false; + const SfxPoolItem* pItem = nullptr; + sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + bool bCallDone = false; + switch ( nSlot ) + { + case SID_ATTR_BORDER: + { + if(!pArgs) + break; + // Create items, because we have to rework anyway. + std::shared_ptr<SvxBoxItem> aBox(std::make_shared<SvxBoxItem>(RES_BOX)); + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + SvxBoxInfoItem aCoreInfo( SID_ATTR_BORDER_INNER ); + aCoreSet.Put(aCoreInfo); + rSh.GetTabBorders( aCoreSet ); + const SvxBoxItem& rCoreBox = aCoreSet.Get(RES_BOX); + const SfxPoolItem *pBoxItem = nullptr; + if ( pArgs->GetItemState(RES_BOX, true, &pBoxItem) == SfxItemState::SET ) + { + aBox.reset(static_cast<SvxBoxItem*>(pBoxItem->Clone())); + sal_uInt16 nDefValue = MIN_BORDER_DIST; + if ( !rReq.IsAPI() ) + nDefValue = 55; + if (!rReq.IsAPI() || aBox->GetSmallestDistance() < MIN_BORDER_DIST) + { + for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() ) + aBox->SetDistance( std::max(rCoreBox.GetDistance(k), nDefValue) , k ); + } + } + else + OSL_ENSURE( false, "where is BoxItem?" ); + + //since the drawing layer also supports borders the which id might be a different one + std::shared_ptr<SvxBoxInfoItem> aInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + if (pArgs->GetItemState(SID_ATTR_BORDER_INNER, true, &pBoxItem) == SfxItemState::SET) + { + aInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxItem->Clone())); + } + else if( pArgs->GetItemState(SDRATTR_TABLE_BORDER_INNER, true, &pBoxItem) == SfxItemState::SET ) + { + aInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxItem->Clone())); + aInfo->SetWhich(SID_ATTR_BORDER_INNER); + } + + aInfo->SetTable( true ); + aInfo->SetValid( SvxBoxInfoItemValidFlags::DISABLE, false ); + +// The attributes of all lines will be read and the strongest wins. + const SvxBorderLine* pBorderLine; + SvxBorderLine aBorderLine; + if ((pBorderLine = rCoreBox.GetTop()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = rCoreBox.GetBottom()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = rCoreBox.GetLeft()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = rCoreBox.GetRight()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = aCoreInfo.GetHori()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + if ((pBorderLine = aCoreInfo.GetVert()) != nullptr) + lcl_TabGetMaxLineWidth(pBorderLine, aBorderLine); + + if(aBorderLine.GetOutWidth() == 0) + { + aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID); + aBorderLine.SetWidth( DEF_LINE_WIDTH_5 ); + } + + if( aBox->GetTop() != nullptr ) + { + aBox->SetLine(&aBorderLine, SvxBoxItemLine::TOP); + } + if( aBox->GetBottom() != nullptr ) + { + aBox->SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + } + if( aBox->GetLeft() != nullptr ) + { + aBox->SetLine(&aBorderLine, SvxBoxItemLine::LEFT); + } + if( aBox->GetRight() != nullptr ) + { + aBox->SetLine(&aBorderLine, SvxBoxItemLine::RIGHT); + } + if( aInfo->GetHori() != nullptr ) + { + aInfo->SetLine(&aBorderLine, SvxBoxInfoItemLine::HORI); + } + if( aInfo->GetVert() != nullptr ) + { + aInfo->SetLine(&aBorderLine, SvxBoxInfoItemLine::VERT); + } + + aCoreSet.Put( *aBox ); + aCoreSet.Put( *aInfo ); + rSh.SetTabBorders( aCoreSet ); + + // we must record the "real" values because otherwise the lines can't be reconstructed on playtime + // the coding style of the controller (setting lines with width 0) is not transportable via Query/PutValue in + // the SvxBoxItem + rReq.AppendItem( *aBox ); + rReq.AppendItem( *aInfo ); + bCallDone = true; + break; + } + case FN_INSERT_TABLE: + InsertTable( rReq ); + break; + case FN_FORMAT_TABLE_DLG: + { + //#127012# get the bindings before the dialog is called + // it might happen that this shell is removed after closing the dialog + SfxBindings& rBindings = GetView().GetViewFrame()->GetBindings(); + SfxItemSet aCoreSet( GetPool(), aUITableAttrRange); + + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &rSh.GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + std::shared_ptr<SwTableRep> pTableRep(::lcl_TableParamToItemSet(aCoreSet, rSh)); + + aCoreSet.Put(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(GetView().GetDocShell()))); + rSh.GetTableAttr(aCoreSet); + // GetTableAttr overwrites the background! + std::unique_ptr<SvxBrushItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND)); + if(rSh.GetBoxBackground(aBrush)) + aCoreSet.Put( *aBrush ); + else + aCoreSet.InvalidateItem( RES_BACKGROUND ); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwTableTabDlg(GetView().GetFrameWeld(), &aCoreSet, &rSh)); + + if (pDlg) + { + if (pItem) + pDlg->SetCurPageId(OUStringToOString(static_cast<const SfxStringItem *>(pItem)->GetValue(), RTL_TEXTENCODING_UTF8)); + + auto pRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + + auto xPaM(std::make_shared<SwPaM>(*rSh.GetCursor(), nullptr)); // tdf#135636 make a copy to use at later apply + pDlg->StartExecuteAsync([pDlg, pRequest, pTableRep, &rBindings, &rSh, xPaM](sal_Int32 nResult){ + if (RET_OK == nResult) + { + rSh.SetSelection(*xPaM); // tdf#135636 set the table selected at dialog launch as current selection + + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + + //to record FN_INSERT_TABLE correctly + pRequest->SetSlot(FN_FORMAT_TABLE_DLG); + pRequest->Done(*pOutSet); + + ItemSetToTableParam(*pOutSet, rSh); + } + + rBindings.Update(SID_RULER_BORDERS); + rBindings.Update(SID_ATTR_TABSTOP); + rBindings.Update(SID_RULER_BORDERS_VERTICAL); + rBindings.Update(SID_ATTR_TABSTOP_VERTICAL); + + pDlg->disposeOnce(); + }); + } + else + { + if (rReq.GetArgs()) + ItemSetToTableParam(*rReq.GetArgs(), rSh); + + rBindings.Update(SID_RULER_BORDERS); + rBindings.Update(SID_ATTR_TABSTOP); + rBindings.Update(SID_RULER_BORDERS_VERTICAL); + rBindings.Update(SID_ATTR_TABSTOP_VERTICAL); + } + + break; + } + case SID_ATTR_BRUSH: + case SID_ATTR_BRUSH_ROW : + case SID_ATTR_BRUSH_TABLE : + if(rReq.GetArgs()) + ItemSetToTableParam(*rReq.GetArgs(), rSh); + break; + case FN_NUM_FORMAT_TABLE_DLG: + { + SwView* pView = GetActiveView(); + if(pView) + { + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( pView) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + SfxItemSet aCoreSet( + GetPool(), + svl::Items< + SID_ATTR_NUMBERFORMAT_VALUE, + SID_ATTR_NUMBERFORMAT_INFO>{}); + + SfxItemSet aBoxSet( *aCoreSet.GetPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_FORMAT, + RES_BOXATR_VALUE, RES_BOXATR_VALUE>{} ); + rSh.GetTableBoxFormulaAttrs( aBoxSet ); + + SfxItemState eState = aBoxSet.GetItemState(RES_BOXATR_FORMAT); + if(eState == SfxItemState::DEFAULT) + { + aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, + pFormatter->GetFormatIndex(NF_TEXT, LANGUAGE_SYSTEM))); + } + else + aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, + aBoxSet.Get( + RES_BOXATR_FORMAT ).GetValue() )); + + OUString sCurText( rSh.GetTableBoxText() ); + aCoreSet.Put( SvxNumberInfoItem( pFormatter, + aBoxSet.Get( + RES_BOXATR_VALUE).GetValue(), + sCurText, SID_ATTR_NUMBERFORMAT_INFO )); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateNumFormatDialog(GetView().GetFrameWeld(), aCoreSet)); + + if (RET_OK == pDlg->Execute()) + { + const SvxNumberInfoItem* pNumberFormatItem + = GetView().GetDocShell()->GetItem( SID_ATTR_NUMBERFORMAT_INFO ); + + if( pNumberFormatItem ) + { + for ( sal_uInt32 key : pNumberFormatItem->GetDelFormats() ) + pNumberFormatItem->GetNumberFormatter()->DeleteEntry( key ); + } + + const SfxPoolItem* pNumberFormatValueItem = nullptr; + if( SfxItemState::SET == pDlg->GetOutputItemSet()->GetItemState( + SID_ATTR_NUMBERFORMAT_VALUE, false, &pNumberFormatValueItem )) + { + SfxItemSet aBoxFormatSet( *aCoreSet.GetPool(), + svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_FORMAT>{} ); + aBoxFormatSet.Put( SwTableBoxNumFormat( + static_cast<const SfxUInt32Item*>(pNumberFormatValueItem)->GetValue() )); + rSh.SetTableBoxFormulaAttrs( aBoxFormatSet ); + + } + } + } + break; + } + case FN_CALC_TABLE: + rSh.UpdateTable(); + bCallDone = true; + break; + case FN_TABLE_DELETE_COL: + if ( rSh.DeleteCol() && rSh.HasSelection() ) + rSh.EnterStdMode(); + bCallDone = true; + break; + case FN_END_TABLE: + rSh.MoveTable( GotoCurrTable, fnTableEnd ); + bCallDone = true; + break; + case FN_START_TABLE: + rSh.MoveTable( GotoCurrTable, fnTableStart ); + bCallDone = true; + break; + case FN_GOTO_NEXT_CELL: + { + bool bAppendLine = true; + if( pItem ) + bAppendLine = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + rReq.SetReturnValue( SfxBoolItem( nSlot, + rSh.GoNextCell( bAppendLine ) ) ); + bCallDone = true; + break; + } + case FN_GOTO_PREV_CELL: + rReq.SetReturnValue( SfxBoolItem( nSlot, rSh.GoPrevCell() ) ); + bCallDone = true; + break; + case FN_TABLE_DELETE_ROW: + if ( rSh.DeleteRow() && rSh.HasSelection() ) + rSh.EnterStdMode(); + bCallDone = true; + break; + case FN_TABLE_MERGE_CELLS: + if ( rSh.IsTableMode() ) + switch ( rSh.MergeTab() ) + { + case TableMergeErr::Ok: + bCallDone = true; + [[fallthrough]]; + case TableMergeErr::NoSelection: + break; + case TableMergeErr::TooComplex: + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetView().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_ERR_TABLE_MERGE))); + xInfoBox->run(); + break; + } + default: + OSL_ENSURE( false, "unknown return value MergeTab."); + break; + } + break; + case SID_TABLE_MINIMAL_COLUMN_WIDTH: + case FN_TABLE_ADJUST_CELLS: + case FN_TABLE_BALANCE_CELLS: + { + bool bBalance = (FN_TABLE_BALANCE_CELLS == nSlot); + const bool bNoShrink = FN_TABLE_ADJUST_CELLS == nSlot; + if ( rSh.IsAdjustCellWidthAllowed(bBalance) ) + { + { + // remove actions to make a valid table selection + UnoActionRemoveContext aRemoveContext(rSh.GetDoc()); + } + rSh.AdjustCellWidth(bBalance, bNoShrink); + } + bCallDone = true; + break; + } + case SID_TABLE_MINIMAL_ROW_HEIGHT: + { + const SwFormatFrameSize aSz; + rSh.SetRowHeight( aSz ); + bCallDone = true; + break; + } + case FN_TABLE_OPTIMAL_HEIGHT: + { + rSh.BalanceRowHeight(/*bTstOnly=*/false, /*bOptimize=*/true); + rSh.BalanceRowHeight(/*bTstOnly=*/false, /*bOptimize=*/false); + bCallDone = true; + break; + } + case FN_TABLE_BALANCE_ROWS: + if ( rSh.BalanceRowHeight(true) ) + rSh.BalanceRowHeight(false); + bCallDone = true; + break; + case FN_TABLE_SELECT_ALL: + rSh.EnterStdMode(); + rSh.MoveTable( GotoCurrTable, fnTableStart ); + rSh.SttSelect(); + rSh.MoveTable( GotoCurrTable, fnTableEnd ); + rSh.EndSelect(); + bCallDone = true; + break; + case FN_TABLE_SELECT_COL: + rSh.EnterStdMode(); + rSh.SelectTableCol(); + bCallDone = true; + break; + case FN_TABLE_SELECT_ROW: + rSh.EnterStdMode(); + rSh.SelectTableRow(); + bCallDone = true; + break; + case FN_TABLE_SET_READ_ONLY_CELLS: + rSh.ProtectCells(); + rSh.ResetSelect( nullptr, false ); + bCallDone = true; + break; + case FN_TABLE_UNSET_READ_ONLY_CELLS: + rSh.UnProtectCells(); + bCallDone = true; + break; + case SID_AUTOFORMAT: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwAutoFormatDlg> pDlg(pFact->CreateSwAutoFormatDlg(GetView().GetFrameWeld(), &rSh)); + pDlg->Execute(); + break; + } + case FN_TABLE_SET_ROW_HEIGHT: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwTableHeightDialog(GetView().GetFrameWeld(), rSh)); + pDlg->Execute(); + break; + } + case FN_NUMBER_BULLETS: + case FN_NUM_BULLET_ON: + OSL_ENSURE( false, "function may not be called now." ); + break; + + + // 2015/06 The following two are deprecated but kept for ascending + // compatibility + case FN_TABLE_INSERT_COL: + case FN_TABLE_INSERT_ROW: + // fallback + case FN_TABLE_INSERT_COL_BEFORE: + case FN_TABLE_INSERT_ROW_BEFORE: + case FN_TABLE_INSERT_COL_AFTER: + case FN_TABLE_INSERT_ROW_AFTER: + { + bool bColumn = rReq.GetSlot() == FN_TABLE_INSERT_COL_BEFORE + || rReq.GetSlot() == FN_TABLE_INSERT_COL_AFTER + || rReq.GetSlot() == FN_TABLE_INSERT_COL; + sal_uInt16 nCount = 0; + bool bAfter = true; + if (pItem) + { + nCount = static_cast<const SfxInt16Item* >(pItem)->GetValue(); + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_INSERT_AFTER, true, &pItem)) + bAfter = static_cast<const SfxBoolItem* >(pItem)->GetValue(); + } + else if( !rReq.IsAPI() ) + { + SwSelBoxes aBoxes; + ::GetTableSel( rSh, aBoxes ); + if ( !aBoxes.empty() ) + { + long maxX = 0; + long maxY = 0; + long minX = std::numeric_limits<long>::max(); + long minY = std::numeric_limits<long>::max(); + long nbBoxes = aBoxes.size(); + for ( long i = 0; i < nbBoxes; i++ ) + { + Point aCoord ( aBoxes[i]->GetCoordinates() ); + if ( aCoord.X() < minX ) minX = aCoord.X(); + if ( aCoord.X() > maxX ) maxX = aCoord.X(); + if ( aCoord.Y() < minY ) minY = aCoord.Y(); + if ( aCoord.Y() > maxY ) maxY = aCoord.Y(); + } + if (bColumn) + nCount = maxX - minX + 1; + else + nCount = maxY - minY + 1; + } + bAfter = rReq.GetSlot() == FN_TABLE_INSERT_COL_AFTER + || rReq.GetSlot() == FN_TABLE_INSERT_ROW_AFTER + || rReq.GetSlot() == FN_TABLE_INSERT_ROW + || rReq.GetSlot() == FN_TABLE_INSERT_COL; + } + + if( nCount ) + { + // i74180: Table border patch submitted by chensuchun: + // -->get the SvxBoxInfoItem of the table before insert + SfxItemSet aCoreSet( GetPool(), aUITableAttrRange); + ::lcl_TableParamToItemSet( aCoreSet, rSh ); + bool bSetInnerBorders = false; + SwUndoId nUndoId = SwUndoId::EMPTY; + // <--End + + if( bColumn ) + { + rSh.StartUndo( SwUndoId::TABLE_INSCOL ); + rSh.InsertCol( nCount, bAfter ); + bSetInnerBorders = true; + nUndoId = SwUndoId::TABLE_INSCOL; + } + else if ( !rSh.IsInRepeatedHeadline() ) + { + rSh.StartUndo( SwUndoId::TABLE_INSROW ); + rSh.InsertRow( nCount, bAfter ); + bSetInnerBorders = true; + nUndoId = SwUndoId::TABLE_INSROW; + } + + // -->after inserting,reset the inner table borders + if ( bSetInnerBorders ) + { + const SvxBoxInfoItem& aBoxInfo(aCoreSet.Get(SID_ATTR_BORDER_INNER)); + SfxItemSet aSet( GetPool(), svl::Items<SID_ATTR_BORDER_INNER, + SID_ATTR_BORDER_INNER>{}); + aSet.Put( aBoxInfo ); + ItemSetToTableParam( aSet, rSh ); + rSh.EndUndo( nUndoId ); + } + + bCallDone = true; + break; + } + + nSlot = bColumn ? FN_TABLE_INSERT_COL_DLG : FN_TABLE_INSERT_ROW_DLG; + + [[fallthrough]]; // on Count = 0 appears the dialog + } + case FN_TABLE_INSERT_COL_DLG: + case FN_TABLE_INSERT_ROW_DLG: + { + const SfxSlot* pSlot = GetStaticInterface()->GetSlot(nSlot); + if ( FN_TABLE_INSERT_ROW_DLG != nSlot || !rSh.IsInRepeatedHeadline()) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SvxAbstractInsRowColDlg> pDlg(pFact->CreateSvxInsRowColDlg(GetView().GetFrameWeld(), + nSlot == FN_TABLE_INSERT_COL_DLG, pSlot->GetCommand())); + if( pDlg->Execute() == 1 ) + { + const sal_uInt16 nDispatchSlot = (nSlot == FN_TABLE_INSERT_COL_DLG) + ? FN_TABLE_INSERT_COL_AFTER : FN_TABLE_INSERT_ROW_AFTER; + SfxUInt16Item aCountItem( nDispatchSlot, pDlg->getInsertCount() ); + SfxBoolItem aAfter( FN_PARAM_INSERT_AFTER, !pDlg->isInsertBefore() ); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if( pVFrame ) + pVFrame->GetDispatcher()->ExecuteList(nDispatchSlot, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aCountItem, &aAfter }); + } + } + break; + } + case FN_TABLE_SPLIT_CELLS: + { + long nCount=0; + bool bHorizontal=true; + bool bProportional = false; + const SfxInt32Item* pSplit = rReq.GetArg<SfxInt32Item>(FN_TABLE_SPLIT_CELLS); + const SfxBoolItem* pHor = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + const SfxBoolItem* pProp = rReq.GetArg<SfxBoolItem>(FN_PARAM_2); + if ( pSplit ) + { + nCount = pSplit->GetValue(); + if ( pHor ) + bHorizontal = pHor->GetValue(); + if ( pProp ) + bProportional = pProp->GetValue(); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + const long nMaxVert = rSh.GetAnyCurRect( CurRectType::Frame ).Width() / MINLAY; + ScopedVclPtr<SvxAbstractSplitTableDialog> pDlg(pFact->CreateSvxSplitTableDialog(GetView().GetFrameWeld(), rSh.IsTableVertical(), nMaxVert)); + if(rSh.IsSplitVerticalByDefault()) + pDlg->SetSplitVerticalByDefault(); + if( pDlg->Execute() == RET_OK ) + { + nCount = pDlg->GetCount(); + bHorizontal = pDlg->IsHorizontal(); + bProportional = pDlg->IsProportional(); + rReq.AppendItem( SfxInt32Item( FN_TABLE_SPLIT_CELLS, nCount ) ); + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bHorizontal ) ); + rReq.AppendItem( SfxBoolItem( FN_PARAM_2, bProportional ) ); + + // tdf#60242: remember choice for next time + bool bVerticalWasChecked = !pDlg->IsHorizontal(); + rSh.SetSplitVerticalByDefault(bVerticalWasChecked); + } + } + + if ( nCount>1 ) + { + rSh.SplitTab(!bHorizontal, static_cast< sal_uInt16 >( nCount-1 ), bProportional ); + bCallDone = true; + } + else + rReq.Ignore(); + break; + } + + case FN_TABLE_SPLIT_TABLE: + { + const SfxUInt16Item* pType = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1); + if( pType ) + { + switch( static_cast<SplitTable_HeadlineOption>(pType->GetValue()) ) + { + case SplitTable_HeadlineOption::NONE : + case SplitTable_HeadlineOption::BorderCopy: + case SplitTable_HeadlineOption::ContentCopy: + case SplitTable_HeadlineOption::BoxAttrCopy: + case SplitTable_HeadlineOption::BoxAttrAllCopy: + rSh.SplitTable(static_cast<SplitTable_HeadlineOption>(pType->GetValue())) ; + break; + default: ;//wrong parameter, do nothing + } + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSplitTableDialog> pDlg(pFact->CreateSplitTableDialog(GetView().GetFrameWeld(), rSh)); + pDlg->Execute(); + rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(pDlg->GetSplitMode()) ) ); + bCallDone = true; + } + break; + } + + case FN_TABLE_MERGE_TABLE: + { + bool bPrev = rSh.CanMergeTable(); + bool bNext = rSh.CanMergeTable( false ); + + if( bPrev && bNext ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateTableMergeDialog(GetView().GetFrameWeld(), bPrev)); + if( RET_OK != pDlg->Execute()) + bPrev = bNext = false; + } + + if( bPrev || bNext ) + rSh.MergeTable( bPrev ); + break; + } + + case FN_TABLE_MODE_FIX : + case FN_TABLE_MODE_FIX_PROP : + case FN_TABLE_MODE_VARIABLE : + { + rSh.SetTableChgMode( FN_TABLE_MODE_FIX == nSlot + ? TableChgMode::FixedWidthChangeAbs + : FN_TABLE_MODE_FIX_PROP == nSlot + ? TableChgMode::FixedWidthChangeProp + : TableChgMode::VarWidthChangeAbs ); + + SfxBindings& rBind = GetView().GetViewFrame()->GetBindings(); + static sal_uInt16 aInva[] = + { FN_TABLE_MODE_FIX, + FN_TABLE_MODE_FIX_PROP, + FN_TABLE_MODE_VARIABLE, + 0 + }; + rBind.Invalidate( aInva ); + bCallDone = true; + break; + } + case FN_TABLE_AUTOSUM: + { + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + pVFrame->GetDispatcher()->Execute(FN_EDIT_FORMULA, SfxCallMode::SYNCHRON); + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + SwInputChild* pChildWin = static_cast<SwInputChild*>(pVFrame-> + GetChildWindow( nId )); + OUString sSum; + GetShell().GetAutoSum(sSum); + if( pChildWin ) + pChildWin->SetFormula( sSum ); + + break; + } + case FN_TABLE_HEADLINE_REPEAT: + if(0 != rSh.GetRowsToRepeat()) + rSh.SetRowsToRepeat( 0 ); + else + rSh.SetRowsToRepeat(rSh.GetRowSelectionFromTop()); + break; + case FN_TABLE_SELECT_CELL : + rSh.SelectTableCell(); + break; + case FN_TABLE_DELETE_TABLE : + { + rSh.StartAction(); + rSh.StartUndo(); + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + rSh.DeleteTable(); + rSh.EndUndo(); + rSh.EndAction(); + //'this' is already destroyed + return; + } + case SID_ATTR_TABLE_ROW_HEIGHT: + { + const SfxUInt32Item* pItem2 = rReq.GetArg<SfxUInt32Item>(SID_ATTR_TABLE_ROW_HEIGHT); + if (pItem2) + { + long nNewHeight = pItem2->GetValue(); + std::unique_ptr<SwFormatFrameSize> pHeight = rSh.GetRowHeight(); + if ( pHeight ) + { + if (pHeight->GetHeightSizeType() == SwFrameSize::Variable) + pHeight->SetHeightSizeType(SwFrameSize::Minimum); + pHeight->SetHeight(nNewHeight); + rSh.SetRowHeight(*pHeight); + } + } + return; + } + case SID_ATTR_TABLE_COLUMN_WIDTH: + { + const SfxUInt32Item* pItem2 = rReq.GetArg<SfxUInt32Item>(SID_ATTR_TABLE_COLUMN_WIDTH); + if (pItem2) + { + long nNewWidth = pItem2->GetValue(); + SwTableFUNC aFunc( &rSh ); + aFunc.InitTabCols(); + aFunc.SetColWidth(aFunc.GetCurColNum(), nNewWidth); + } + return; + } + default: + bMore = true; + } + + if ( !bMore ) + { + if(bCallDone) + rReq.Done(); + return; + } + + // Now the slots which are working directly on the TableFormat. + switch ( nSlot ) + { + case SID_ATTR_ULSPACE: + if(pItem) + { + SvxULSpaceItem aULSpace( *static_cast<const SvxULSpaceItem*>(pItem) ); + aULSpace.SetWhich( RES_UL_SPACE ); + ::lcl_SetAttr( rSh, aULSpace ); + } + break; + + case SID_ATTR_LRSPACE: + if(pItem) + { + SfxItemSet aSet( GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE, + RES_HORI_ORIENT, RES_HORI_ORIENT>{} ); + SvxLRSpaceItem aLRSpace( *static_cast<const SvxLRSpaceItem*>(pItem) ); + aLRSpace.SetWhich( RES_LR_SPACE ); + aSet.Put( aLRSpace ); + rSh.SetTableAttr( aSet ); + } + break; + // The last case branch which needs a table manager!! + case FN_TABLE_SET_COL_WIDTH: + { + SwTableFUNC aMgr( &rSh ); + aMgr.ColWidthDlg(GetView().GetFrameWeld()); + break; + } + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + { + const sal_uInt16 nAlign = nSlot == SID_TABLE_VERT_NONE ? + text::VertOrientation::NONE : + nSlot == SID_TABLE_VERT_CENTER ? + text::VertOrientation::CENTER : text::VertOrientation::BOTTOM; + rSh.SetBoxAlign(nAlign); + bCallDone = true; + break; + } + + case SID_ATTR_PARA_SPLIT: + if ( pItem ) + { + SwFormatLayoutSplit aSplit( static_cast<const SvxFormatSplitItem*>(pItem)->GetValue()); + SfxItemSet aSet(GetPool(), svl::Items<RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT>{} ); + aSet.Put(aSplit); + rSh.SetTableAttr(aSet); + } + break; + + case SID_ATTR_PARA_KEEP: + if ( pItem ) + { + SvxFormatKeepItem aKeep( *static_cast<const SvxFormatKeepItem*>(pItem) ); + aKeep.SetWhich( RES_KEEP ); + SfxItemSet aSet(GetPool(), svl::Items<RES_KEEP, RES_KEEP>{} ); + aSet.Put(aKeep); + rSh.SetTableAttr(aSet); + } + break; + case FN_TABLE_ROW_SPLIT : + { + const SfxBoolItem* pBool = static_cast<const SfxBoolItem*>(pItem); + std::unique_ptr<SwFormatRowSplit> pSplit; + if(!pBool) + { + pSplit = rSh.GetRowSplit(); + if(pSplit) + pSplit->SetValue(!pSplit->GetValue()); + else + pSplit.reset(new SwFormatRowSplit(true)); + } + else + { + pSplit.reset(new SwFormatRowSplit(pBool->GetValue())); + } + rSh.SetRowSplit( *pSplit ); + break; + } + + default: + OSL_ENSURE( false, "wrong Dispatcher" ); + return; + } + if(bCallDone) + rReq.Done(); +} + +void SwTableShell::GetState(SfxItemSet &rSet) +{ + SfxWhichIter aIter( rSet ); + SwWrtShell &rSh = GetShell(); + SwFrameFormat *pFormat = rSh.GetTableFormat(); + // os #124829# crash report: in case of an invalid shell selection return immediately + if(!pFormat) + return; + sal_uInt16 nSlot = aIter.FirstWhich(); + while ( nSlot ) + { + switch ( nSlot ) + { + case FN_TABLE_MERGE_CELLS: + if ( !rSh.IsTableMode() ) + rSet.DisableItem(FN_TABLE_MERGE_CELLS); + break; + case SID_TABLE_MINIMAL_COLUMN_WIDTH: + case FN_TABLE_ADJUST_CELLS: + if ( !rSh.IsAdjustCellWidthAllowed() ) + rSet.DisableItem(nSlot); + break; + + case FN_TABLE_BALANCE_CELLS: + if ( !rSh.IsAdjustCellWidthAllowed(true) ) + rSet.DisableItem(FN_TABLE_BALANCE_CELLS); + break; + + case FN_TABLE_OPTIMAL_HEIGHT: + case FN_TABLE_BALANCE_ROWS: + if ( !rSh.BalanceRowHeight(true) ) + rSet.DisableItem(nSlot); + break; + case FN_OPTIMIZE_TABLE: + if ( !rSh.IsTableMode() && + !rSh.IsAdjustCellWidthAllowed() && + !rSh.IsAdjustCellWidthAllowed(true) && + !rSh.BalanceRowHeight(true) ) + rSet.DisableItem(FN_OPTIMIZE_TABLE); + break; + case SID_INSERT_DIAGRAM: + { + SvtModuleOptions aMOpt; + if ( !aMOpt.IsMath() || rSh.IsTableComplexForChart() ) + rSet.DisableItem(nSlot); + } + break; + + case FN_INSERT_TABLE: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nSlot ); + } + break; + + case SID_TABLE_MINIMAL_ROW_HEIGHT: + { + // Disable if auto height already is enabled. + std::unique_ptr<SwFormatFrameSize> pSz = rSh.GetRowHeight(); + if ( pSz ) + { + if ( SwFrameSize::Variable == pSz->GetHeightSizeType() ) + rSet.DisableItem( nSlot ); + } + break; + } + case FN_TABLE_INSERT_ROW: + case FN_TABLE_INSERT_ROW_AFTER: + case FN_TABLE_INSERT_ROW_DLG: + if ( rSh.IsInRepeatedHeadline() ) + rSet.DisableItem( nSlot ); + break; + case RES_LR_SPACE: + rSet.Put(pFormat->GetLRSpace()); + break; + case RES_UL_SPACE: + rSet.Put(pFormat->GetULSpace()); + break; + + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + { + const sal_uInt16 nAlign = rSh.GetBoxAlign(); + bool bSet = (nSlot == SID_TABLE_VERT_NONE && nAlign == text::VertOrientation::NONE) || + (nSlot == SID_TABLE_VERT_CENTER && nAlign == text::VertOrientation::CENTER) || + (nSlot == SID_TABLE_VERT_BOTTOM && nAlign == text::VertOrientation::BOTTOM); + rSet.Put(SfxBoolItem(nSlot, bSet)); + break; + } + + case FN_TABLE_MODE_FIX : + case FN_TABLE_MODE_FIX_PROP : + case FN_TABLE_MODE_VARIABLE : + { + TableChgMode nMode = rSh.GetTableChgMode(); + bool bSet = (nSlot == FN_TABLE_MODE_FIX && nMode == TableChgMode::FixedWidthChangeAbs) || + (nSlot == FN_TABLE_MODE_FIX_PROP && nMode == TableChgMode::FixedWidthChangeProp) || + (nSlot == FN_TABLE_MODE_VARIABLE && nMode == TableChgMode::VarWidthChangeAbs); + rSet.Put(SfxBoolItem(nSlot, bSet)); + } + break; + + case SID_ATTR_PARA_SPLIT: + rSet.Put( pFormat->GetKeep() ); + break; + + case SID_ATTR_PARA_KEEP: + rSet.Put( pFormat->GetLayoutSplit() ); + break; + case FN_TABLE_SPLIT_TABLE: + if ( rSh.IsInHeadline() ) + rSet.DisableItem( nSlot ); + break; + case FN_TABLE_MERGE_TABLE: + { + bool bAsk; + if( !rSh.CanMergeTable( true, &bAsk )) + rSet.DisableItem( nSlot ); + break; + } + + case FN_TABLE_DELETE_ROW: + { + SwSelBoxes aBoxes; + ::GetTableSel( rSh, aBoxes, SwTableSearchType::Row ); + if( ::HasProtectedCells( aBoxes )) + rSet.DisableItem( nSlot ); + } + break; + case FN_TABLE_DELETE_COL: + { + SwSelBoxes aBoxes; + ::GetTableSel( rSh, aBoxes, SwTableSearchType::Col ); + if( ::HasProtectedCells( aBoxes )) + rSet.DisableItem( nSlot ); + } + break; + + case FN_TABLE_UNSET_READ_ONLY_CELLS: + // disable in readonly sections, but enable in protected cells + if( !rSh.CanUnProtectCells() ) + rSet.DisableItem( nSlot ); + break; + case RES_ROW_SPLIT: + { + const SwFormatLayoutSplit& rTabSplit = pFormat->GetLayoutSplit(); + if ( !rTabSplit.GetValue() ) + { + rSet.DisableItem( nSlot ); + } + else + { + std::unique_ptr<SwFormatRowSplit> pSplit = rSh.GetRowSplit(); + if(pSplit) + rSet.Put(std::move(pSplit)); + else + rSet.InvalidateItem( nSlot ); + } + break; + } + case FN_TABLE_HEADLINE_REPEAT: + if(0 != rSh.GetRowsToRepeat()) + rSet.Put(SfxBoolItem(nSlot, true)); + else if(!rSh.GetRowSelectionFromTop()) + rSet.DisableItem( nSlot ); + else + rSet.Put(SfxBoolItem(nSlot, false)); + break; + case FN_TABLE_SELECT_CELL : + if(rSh.HasBoxSelection()) + rSet.DisableItem( nSlot ); + break; + case SID_ATTR_TABLE_ROW_HEIGHT: + { + SfxUInt32Item aRowHeight(SID_ATTR_TABLE_ROW_HEIGHT); + std::unique_ptr<SwFormatFrameSize> pHeight = rSh.GetRowHeight(); + if (pHeight) + { + long nHeight = pHeight->GetHeight(); + aRowHeight.SetValue(nHeight); + rSet.Put(aRowHeight); + + if (comphelper::LibreOfficeKit::isActive()) + { + // TODO: set correct unit + MapUnit eTargetUnit = MapUnit::MapInch; + OUString sHeight = GetMetricText(nHeight, + MapUnit::MapTwip, eTargetUnit, nullptr); + + OUString sPayload = ".uno:TableRowHeight=" + sHeight; + + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US).getStr()); + } + } + break; + } + case SID_ATTR_TABLE_COLUMN_WIDTH: + { + SfxUInt32Item aColumnWidth(SID_ATTR_TABLE_COLUMN_WIDTH); + SwTableFUNC aFunc( &rSh ); + aFunc.InitTabCols(); + SwTwips nWidth = aFunc.GetColWidth(aFunc.GetCurColNum()); + aColumnWidth.SetValue(nWidth); + rSet.Put(aColumnWidth); + + if (comphelper::LibreOfficeKit::isActive()) + { + // TODO: set correct unit + MapUnit eTargetUnit = MapUnit::MapInch; + OUString sWidth = GetMetricText(nWidth, + MapUnit::MapTwip, eTargetUnit, nullptr); + + OUString sPayload = ".uno:TableColumWidth=" + sWidth; + + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US).getStr()); + } + + break; + } + } + nSlot = aIter.NextWhich(); + } +} + +SwTableShell::SwTableShell(SwView &_rView) : + SwBaseShell(_rView) +{ + SetName("Table"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Table)); +} + +void SwTableShell::GetFrameBorderState(SfxItemSet &rSet) +{ + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{} ); + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + aCoreSet.Put( aBoxInfo ); + GetShell().GetTabBorders( aCoreSet ); + rSet.Put( aCoreSet ); +} + +void SwTableShell::ExecTableStyle(SfxRequest& rReq) +{ + SwWrtShell &rSh = GetShell(); + const SfxItemSet *pArgs = rReq.GetArgs(); + if(pArgs) + switch ( rReq.GetSlot() ) + { + case SID_FRAME_LINESTYLE: + case SID_FRAME_LINECOLOR: + if ( rReq.GetSlot() == SID_FRAME_LINESTYLE ) + { + const SvxLineItem &rLineItem = pArgs->Get( SID_FRAME_LINESTYLE ); + const SvxBorderLine* pBorderLine = rLineItem.GetLine(); + rSh.SetTabLineStyle( nullptr, true, pBorderLine); + } + else + { + const SvxColorItem &rNewColorItem = pArgs->Get( SID_FRAME_LINECOLOR ); + rSh.SetTabLineStyle( &rNewColorItem.GetValue() ); + } + + rReq.Done(); + + break; + } +} + +void SwTableShell::GetLineStyleState(SfxItemSet &rSet) +{ + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{}); + SvxBoxInfoItem aCoreInfo( SID_ATTR_BORDER_INNER ); + aCoreSet.Put(aCoreInfo); + GetShell().GetTabBorders( aCoreSet ); + + const SvxBoxItem& rBoxItem = aCoreSet.Get( RES_BOX ); + const SvxBorderLine* pLine = rBoxItem.GetTop(); + + rSet.Put( SvxColorItem( pLine ? pLine->GetColor() : Color(), SID_FRAME_LINECOLOR ) ); + SvxLineItem aLine( SID_FRAME_LINESTYLE ); + aLine.SetLine(pLine); + rSet.Put( aLine ); +} + +void SwTableShell::ExecNumberFormat(SfxRequest const & rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + SwWrtShell &rSh = GetShell(); + + // At first the slots, which doesn't need a FrameMgr. + const SfxPoolItem* pItem = nullptr; + const sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + + // Always acquire the language from the current cursor position. + LanguageType eLang = rSh.GetCurLang(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; + SvNumFormatType nFormatType = SvNumFormatType::ALL; + sal_uInt16 nOffset = 0; + + switch ( nSlot ) + { + case FN_NUMBER_FORMAT: + if( pItem ) + { + // Determine index for string. + OUString aCode( static_cast<const SfxStringItem*>(pItem)->GetValue() ); + nNumberFormat = pFormatter->GetEntryKey( aCode, eLang ); + if( NUMBERFORMAT_ENTRY_NOT_FOUND == nNumberFormat ) + { + // Re-enter + sal_Int32 nErrPos; + SvNumFormatType nType; + if( !pFormatter->PutEntry( aCode, nErrPos, nType, + nNumberFormat, eLang )) + nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; + } + } + break; + case FN_NUMBER_STANDARD: nFormatType = SvNumFormatType::NUMBER; break; + case FN_NUMBER_SCIENTIFIC: nFormatType = SvNumFormatType::SCIENTIFIC; break; + case FN_NUMBER_DATE: nFormatType = SvNumFormatType::DATE; break; + case FN_NUMBER_TIME: nFormatType = SvNumFormatType::TIME; break; + case FN_NUMBER_CURRENCY: nFormatType = SvNumFormatType::CURRENCY; break; + case FN_NUMBER_PERCENT: nFormatType = SvNumFormatType::PERCENT; break; + + case FN_NUMBER_TWODEC: // #.##0,00 + nFormatType = SvNumFormatType::NUMBER; + nOffset = NF_NUMBER_1000DEC2; + break; + + default: + OSL_FAIL("wrong dispatcher"); + return; + } + + if( nFormatType != SvNumFormatType::ALL ) + nNumberFormat = pFormatter->GetStandardFormat( nFormatType, eLang ) + nOffset; + + if( NUMBERFORMAT_ENTRY_NOT_FOUND != nNumberFormat ) + { + SfxItemSet aBoxSet( GetPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_FORMAT>{} ); + aBoxSet.Put( SwTableBoxNumFormat( nNumberFormat )); + rSh.SetTableBoxFormulaAttrs( aBoxSet ); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textdrw.cxx b/sw/source/uibase/shells/textdrw.cxx new file mode 100644 index 000000000..0303576e2 --- /dev/null +++ b/sw/source/uibase/shells/textdrw.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 <config_features.h> + +#include <svx/svdview.hxx> +#include <tools/urlobj.hxx> +#include <svx/fmglob.hxx> +#include <svx/svdouno.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <swundo.hxx> +#include <basesh.hxx> + +#include <docsh.hxx> +#include <sfx2/docfile.hxx> +#include <svl/urihelper.hxx> +#include <avmedia/mediawindow.hxx> + +using namespace ::com::sun::star; + +void SwBaseShell::InsertURLButton(const OUString& rURL, const OUString& rTarget, const OUString& rText) +{ + SwWrtShell& rSh = GetShell(); + + if (!rSh.HasDrawView()) + rSh.MakeDrawView(); + SdrView *pSdrView = rSh.GetDrawView(); + + // OBJ_FM_BUTTON + pSdrView->SetDesignMode(); + pSdrView->SetCurrentObj(OBJ_FM_BUTTON); + pSdrView->SetEditMode(false); + + Point aStartPos(rSh.GetCharRect().Pos() + Point(0, 1)); + + rSh.StartAction(); + rSh.StartUndo( SwUndoId::UI_INSERT_URLBTN ); + if (rSh.BeginCreate(OBJ_FM_BUTTON, SdrInventor::FmForm, aStartPos)) + { + pSdrView->SetOrtho(false); + Size aSz(GetView().GetEditWin().PixelToLogic(Size(140, 20))); + Point aEndPos(aSz.Width(), aSz.Height()); + + rSh.MoveCreate(aStartPos + aEndPos); + rSh.EndCreate(SdrCreateCmd::ForceEnd); + + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + if (rMarkList.GetMark(0)) + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + OSL_ENSURE( pUnoCtrl, "not an SdrUnoObj" ); + if (!pUnoCtrl) + return; + + uno::Reference< awt::XControlModel > xControlModel = pUnoCtrl->GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" ); + if (!xControlModel.is()) + return; + + uno::Reference< beans::XPropertySet > xPropSet(xControlModel, uno::UNO_QUERY); + + uno::Any aTmp; + + aTmp <<= rText; + xPropSet->setPropertyValue( "Label", aTmp ); + + SfxMedium* pMedium = rSh.GetView().GetDocShell()->GetMedium(); + INetURLObject aAbs; + if( pMedium ) + aAbs = pMedium->GetURLObject(); + + aTmp <<= URIHelper::SmartRel2Abs(aAbs, rURL); + xPropSet->setPropertyValue( "TargetURL", aTmp ); + + if( !rTarget.isEmpty() ) + { + aTmp <<= rTarget; + xPropSet->setPropertyValue( "TargetFrame", aTmp ); + } + + aTmp <<= form::FormButtonType_URL; + xPropSet->setPropertyValue( "ButtonType", aTmp ); + +#if HAVE_FEATURE_AVMEDIA + if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) ) + { + // #105638# OJ + aTmp <<= true; + xPropSet->setPropertyValue("DispatchURLInternal", aTmp ); + } +#endif + } + + if (rSh.IsObjSelected()) + { + rSh.UnSelectFrame(); + } + } + rSh.EndUndo( SwUndoId::UI_INSERT_URLBTN ); + rSh.EndAction(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx new file mode 100644 index 000000000..9b7d3b10a --- /dev/null +++ b/sw/source/uibase/shells/textfld.cxx @@ -0,0 +1,1096 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <AnnotationWin.hxx> +#include <comphelper/lok.hxx> +#include <hintids.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/lnkbase.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <svl/itempool.hxx> +#include <tools/lineend.hxx> +#include <svl/whiter.hxx> +#include <svl/eitem.hxx> +#include <svl/macitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svx/postattr.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/svxdlg.hxx> +#include <fmtinfmt.hxx> +#include <fldwrap.hxx> +#include <redline.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wrtsh.hxx> +#include <textsh.hxx> +#include <docufld.hxx> +#include <ddefld.hxx> +#include <fldmgr.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <sfx2/event.hxx> +#include <swabstdlg.hxx> +#include <doc.hxx> +#include <PostItMgr.hxx> +#include <swmodule.hxx> +#include <xmloff/odffields.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentUndoRedo.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <IMark.hxx> +#include <officecfg/Office/Compatibility.hxx> +#include <ndtxt.hxx> + + +using namespace nsSwDocInfoSubType; + +static OUString lcl_BuildTitleWithRedline( const SwRangeRedline *pRedline ) +{ + const OUString sTitle(SwResId(STR_REDLINE_COMMENT)); + + const char* pResId = nullptr; + switch( pRedline->GetType() ) + { + case RedlineType::Insert: + pResId = STR_REDLINE_INSERTED; + break; + case RedlineType::Delete: + pResId = STR_REDLINE_DELETED; + break; + case RedlineType::Format: + case RedlineType::ParagraphFormat: + pResId = STR_REDLINE_FORMATTED; + break; + case RedlineType::Table: + pResId = STR_REDLINE_TABLECHG; + break; + case RedlineType::FmtColl: + pResId = STR_REDLINE_FMTCOLLSET; + break; + default: + return sTitle; + } + + return sTitle + SwResId(pResId); +} + +void SwTextShell::ExecField(SfxRequest &rReq) +{ + SwWrtShell& rSh = GetShell(); + const SfxPoolItem* pItem = nullptr; + + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxItemSet* pArgs = rReq.GetArgs(); + if(pArgs) + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + + bool bMore = false; + bool bIsText = true; + SwFieldTypesEnum nInsertType = SwFieldTypesEnum::Date; + sal_uInt16 nInsertSubType = 0; + sal_uInt32 nInsertFormat = 0; + + switch(nSlot) + { + case FN_EDIT_FIELD: + { + SwField* pField = rSh.GetCurField(); + if( pField ) + { + switch ( pField->GetTypeId() ) + { + case SwFieldTypesEnum::DDE: + { + ::sfx2::SvBaseLink& rLink = static_cast<SwDDEFieldType*>(pField->GetTyp())-> + GetBaseLink(); + if(rLink.IsVisible()) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractLinksDialog> pDlg(pFact->CreateLinksDialog(GetView().GetFrameWeld(), &rSh.GetLinkManager(), false, &rLink)); + pDlg->Execute(); + } + break; + } + default: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSwFieldEditDlg( GetView() )); + pDlg->Execute(); + } + } + } + break; + } + case FN_EXECUTE_MACROFIELD: + { + SwField* pField = rSh.GetCurField(); + if(pField && pField->GetTyp()->Which() == SwFieldIds::Macro) + { + + const OUString& rMacro = static_cast<SwMacroField*>(pField)->GetMacro(); + sal_Int32 nPos = rMacro.indexOf('.'); + if(nPos != -1) + { + SvxMacro aMacro( rMacro.copy(nPos + 1), rMacro.copy(0,nPos), STARBASIC ); + rSh.ExecMacro(aMacro); + } + } + } + break; + + case FN_GOTO_NEXT_INPUTFLD: + case FN_GOTO_PREV_INPUTFLD: + { + bool bRet = false; + SwFieldType* pField = rSh.GetFieldType( 0, SwFieldIds::Input ); + const bool bAddSetExpressionFields = !( rSh.GetViewOptions()->IsReadonly() ); + if ( pField != nullptr + && rSh.MoveFieldType( + pField, + FN_GOTO_NEXT_INPUTFLD == nSlot, + SwFieldIds::Unknown, + bAddSetExpressionFields ) ) + { + rSh.ClearMark(); + if (!rSh.IsMultiSelection() + && (nullptr != dynamic_cast<const SwTextInputField*>( + SwCursorShell::GetTextFieldAtCursor(rSh.GetCursor(), true)))) + { + rSh.SttSelect(); + rSh.SelectText( + SwCursorShell::StartOfInputFieldAtPos( *(rSh.GetCursor()->Start()) ) + 1, + SwCursorShell::EndOfInputFieldAtPos( *(rSh.GetCursor()->Start()) ) - 1 ); + } + else + { + rSh.StartInputFieldDlg(rSh.GetCurField(true), false, false, GetView().GetFrameWeld()); + } + bRet = true; + } + + rReq.SetReturnValue( SfxBoolItem( nSlot, bRet )); + } + break; + + default: + bMore = true; + } + if(bMore) + { + // Here come the slots with FieldMgr. + SwFieldMgr aFieldMgr(GetShellPtr()); + switch(nSlot) + { + case FN_INSERT_DBFIELD: + { + bool bRes = false; + if( pItem ) + { + sal_uInt32 nFormat = 0; + SwFieldTypesEnum nType = SwFieldTypesEnum::Date; + OUString aPar1 = static_cast<const SfxStringItem *>(pItem)->GetValue(); + OUString aPar2; + sal_Int32 nCommand = 0; + + if( SfxItemState::SET == pArgs->GetItemState( FN_PARAM_FIELD_TYPE, + false, &pItem )) + nType = static_cast<SwFieldTypesEnum>(static_cast<const SfxUInt16Item *>(pItem)->GetValue()); + aPar1 += OUStringChar(DB_DELIM); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_1, false, &pItem )) + { + aPar1 += static_cast<const SfxStringItem *>(pItem)->GetValue(); + } + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_3, false, &pItem )) + nCommand = static_cast<const SfxInt32Item*>(pItem)->GetValue(); + aPar1 += OUStringChar(DB_DELIM) + + OUString::number(nCommand) + + OUStringChar(DB_DELIM); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_2, false, &pItem )) + { + aPar1 += static_cast<const SfxStringItem *>(pItem)->GetValue(); + } + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_FIELD_CONTENT, false, &pItem )) + aPar2 = static_cast<const SfxStringItem *>(pItem)->GetValue(); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_FIELD_FORMAT, false, &pItem )) + nFormat = static_cast<const SfxUInt32Item *>(pItem)->GetValue(); + OSL_FAIL("Command is not yet used"); + SwInsertField_Data aData(nType, 0, aPar1, aPar2, nFormat, GetShellPtr(), ' '/*separator*/ ); + bRes = aFieldMgr.InsertField(aData); + } + rReq.SetReturnValue(SfxBoolItem( nSlot, bRes )); + } + break; + case FN_INSERT_FIELD_CTRL: + case FN_INSERT_FIELD: + { + bool bRes = false; + if( pItem && nSlot != FN_INSERT_FIELD_CTRL) + { + sal_uInt32 nFormat = 0; + SwFieldTypesEnum nType = SwFieldTypesEnum::Date; + sal_uInt16 nSubType = 0; + OUString aPar1 = static_cast<const SfxStringItem *>(pItem)->GetValue(); + OUString aPar2; + sal_Unicode cSeparator = ' '; + + if( SfxItemState::SET == pArgs->GetItemState( FN_PARAM_FIELD_TYPE, + false, &pItem )) + nType = static_cast<SwFieldTypesEnum>(static_cast<const SfxUInt16Item *>(pItem)->GetValue()); + if( SfxItemState::SET == pArgs->GetItemState( FN_PARAM_FIELD_SUBTYPE, + false, &pItem )) + nSubType = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_FIELD_CONTENT, false, &pItem )) + aPar2 = static_cast<const SfxStringItem *>(pItem)->GetValue(); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_FIELD_FORMAT, false, &pItem )) + nFormat = static_cast<const SfxUInt32Item *>(pItem)->GetValue(); + if( SfxItemState::SET == pArgs->GetItemState( + FN_PARAM_3, false, &pItem )) + { + OUString sTmp = static_cast<const SfxStringItem *>(pItem)->GetValue(); + if(!sTmp.isEmpty()) + cSeparator = sTmp[0]; + } + SwInsertField_Data aData(nType, nSubType, aPar1, aPar2, nFormat, GetShellPtr(), cSeparator ); + bRes = aFieldMgr.InsertField( aData ); + } + else + { + //#i5788# prevent closing of the field dialog while a modal dialog ( Input field dialog ) is active + if(!GetView().GetViewFrame()->IsInModalMode()) + { + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + pVFrame->ToggleChildWindow(FN_INSERT_FIELD); + bRes = pVFrame->GetChildWindow( nSlot ) != nullptr; + Invalidate(rReq.GetSlot()); + Invalidate(FN_INSERT_FIELD_CTRL); + rReq.Ignore(); + } + } + rReq.SetReturnValue(SfxBoolItem( nSlot, bRes )); + } + break; + + case FN_INSERT_REF_FIELD: + { + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if (!pVFrame->HasChildWindow(FN_INSERT_FIELD)) + pVFrame->ToggleChildWindow(FN_INSERT_FIELD); // Show dialog + + // Switch Fielddlg at a new TabPage + sal_uInt16 nId = SwFieldDlgWrapper::GetChildWindowId(); + SwFieldDlgWrapper *pWrp = static_cast<SwFieldDlgWrapper*>(pVFrame->GetChildWindow(nId)); + if (pWrp) + pWrp->ShowReferencePage(); + rReq.Ignore(); + } + break; + case FN_DELETE_COMMENT: + { + const SvxPostItIdItem* pIdItem = rReq.GetArg<SvxPostItIdItem>(SID_ATTR_POSTIT_ID); + if (pIdItem && !pIdItem->GetValue().isEmpty() && GetView().GetPostItMgr()) + { + GetView().GetPostItMgr()->Delete(pIdItem->GetValue().toUInt32()); + } + else if ( GetView().GetPostItMgr() && + GetView().GetPostItMgr()->HasActiveSidebarWin() ) + { + GetView().GetPostItMgr()->DeleteActiveSidebarWin(); + } + } + break; + case FN_RESOLVE_NOTE: + { + const SvxPostItIdItem* pIdItem = rReq.GetArg<SvxPostItIdItem>(SID_ATTR_POSTIT_ID); + if (pIdItem && !pIdItem->GetValue().isEmpty() && GetView().GetPostItMgr()) + { + GetView().GetPostItMgr()->ToggleResolvedForThread(pIdItem->GetValue().toUInt32()); + } + } + break; + case FN_DELETE_ALL_NOTES: + if ( GetView().GetPostItMgr() ) + GetView().GetPostItMgr()->Delete(); + break; + case FN_FORMAT_ALL_NOTES: + { + SwPostItMgr* pPostItMgr = GetView().GetPostItMgr(); + if (pPostItMgr) + pPostItMgr->ExecuteFormatAllDialog(GetView()); + } + break; + case FN_DELETE_NOTE_AUTHOR: + { + const SfxStringItem* pNoteItem = rReq.GetArg<SfxStringItem>(nSlot); + if ( pNoteItem && GetView().GetPostItMgr() ) + GetView().GetPostItMgr()->Delete( pNoteItem->GetValue() ); + } + break; + case FN_HIDE_NOTE: + if ( GetView().GetPostItMgr() && + GetView().GetPostItMgr()->HasActiveSidebarWin() ) + { + GetView().GetPostItMgr()->HideActiveSidebarWin(); + } + break; + case FN_HIDE_ALL_NOTES: + if ( GetView().GetPostItMgr() ) + GetView().GetPostItMgr()->Hide(); + break; + case FN_HIDE_NOTE_AUTHOR: + { + const SfxStringItem* pNoteItem = rReq.GetArg<SfxStringItem>(nSlot); + if ( pNoteItem && GetView().GetPostItMgr() ) + GetView().GetPostItMgr()->Hide( pNoteItem->GetValue() ); + } + break; + case FN_REPLY: + { + const SvxPostItIdItem* pIdItem = rReq.GetArg<SvxPostItIdItem>(SID_ATTR_POSTIT_ID); + if (pIdItem && !pIdItem->GetValue().isEmpty()) + { + SwFieldType* pType = rSh.GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false); + if(pType->FindFormatForPostItId(pIdItem->GetValue().toUInt32())) + { + auto pMgr = GetView().GetPostItMgr(); + auto pWin = pMgr->GetAnnotationWin(pIdItem->GetValue().toUInt32()); + if(pWin) + { + OUString sText; + if(const auto pTextItem = rReq.GetArg<SvxPostItTextItem>(SID_ATTR_POSTIT_TEXT)) + sText = pTextItem->GetValue(); + pMgr->RegisterAnswerText(sText); + pWin->ExecuteCommand(nSlot); + } + } + } + } + break; + case FN_POSTIT: + { + rSh.InsertPostIt(aFieldMgr, rReq); + } + break; + case SID_EDIT_POSTIT: + { + const SvxPostItIdItem* pIdItem = rReq.GetArg<SvxPostItIdItem>(SID_ATTR_POSTIT_ID); + if (pIdItem && !pIdItem->GetValue().isEmpty()) + { + const SvxPostItTextItem* pTextItem = rReq.GetArg<SvxPostItTextItem>(SID_ATTR_POSTIT_TEXT); + OUString sText; + if ( pTextItem ) + sText = pTextItem->GetValue(); + + sw::annotation::SwAnnotationWin* pAnnotationWin = GetView().GetPostItMgr()->GetAnnotationWin(pIdItem->GetValue().toUInt32()); + if (pAnnotationWin) + { + pAnnotationWin->UpdateText(sText); + + // explicit state update to get the Undo state right + GetView().AttrChangedNotify(nullptr); + } + } + } + break; + case FN_REDLINE_COMMENT: + { + /* this code can be used once we want redline comments in the margin, all other stuff can + then be deleted + String sComment; + const SwRangeRedline *pRedline = rSh.GetCurrRedline(); + + if (pRedline) + { + sComment = pRedline->GetComment(); + if ( !sComment.Len() ) + GetView().GetDocShell()->Broadcast(SwRedlineHint(pRedline,SWREDLINE_INSERTED)); + const_cast<SwRangeRedline*>(pRedline)->Broadcast(SwRedlineHint(pRedline,SWREDLINE_FOCUS,&GetView())); + } + */ + + const SwRangeRedline *pRedline = rSh.GetCurrRedline(); + SwDoc *pDoc = rSh.GetDoc(); + // If index is specified, goto and select the appropriate redline + if (pArgs && pArgs->GetItemState(nSlot, false, &pItem) == SfxItemState::SET) + { + const sal_uInt32 nChangeId = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + const SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + for (SwRedlineTable::size_type nRedline = 0; nRedline < rRedlineTable.size(); ++nRedline) + { + if (nChangeId == rRedlineTable[nRedline]->GetId()) + pRedline = rSh.GotoRedline(nRedline, true); + } + } + + OUString sCommentText; + const SfxStringItem* pTextItem = rReq.GetArg<SvxPostItTextItem>(SID_ATTR_POSTIT_TEXT); + if (pTextItem) + sCommentText = pTextItem->GetValue(); + + if (pRedline) + { + // In case of LOK and comment text is already provided, skip + // dialog creation and just change the redline comment directly + if (comphelper::LibreOfficeKit::isActive() && !sCommentText.isEmpty()) + { + rSh.SetRedlineComment(sCommentText); + GetView().AttrChangedNotify(nullptr); + MaybeNotifyRedlineModification(const_cast<SwRangeRedline*>(pRedline), pRedline->GetDoc()); + break; + } + + OUString sComment = convertLineEnd(pRedline->GetComment(), GetSystemLineEnd()); + + bool bTravel = false; + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ::DialogGetRanges fnGetRange = pFact->GetDialogGetRangesFunc(); + SfxItemSet aSet(GetPool(), fnGetRange()); + aSet.Put(SvxPostItTextItem(sComment, SID_ATTR_POSTIT_TEXT)); + aSet.Put(SvxPostItAuthorItem(pRedline->GetAuthorString(), SID_ATTR_POSTIT_AUTHOR)); + + aSet.Put( SvxPostItDateItem( GetAppLangDateTimeString( + pRedline->GetRedlineData().GetTimeStamp() ), + SID_ATTR_POSTIT_DATE )); + + // Traveling only if more than one field. + rSh.StartAction(); + + rSh.Push(); + const SwRangeRedline *pActRed = rSh.SelPrevRedline(); + + if (pActRed == pRedline) + { // New cursor is at the beginning of the current redlines. + rSh.Pop(); // Throw old cursor away + rSh.Push(); + pActRed = rSh.SelPrevRedline(); + } + + bool bPrev = pActRed != nullptr; + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.EndAction(); + + rSh.ClearMark(); + // Select current redline. + pActRed = rSh.SelNextRedline(); + if (pActRed != pRedline) + rSh.SelPrevRedline(); + + rSh.StartAction(); + rSh.Push(); + pActRed = rSh.SelNextRedline(); + bool bNext = pActRed != nullptr; + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore cursor position + + if( rSh.IsCursorPtAtEnd() ) + rSh.SwapPam(); + + rSh.EndAction(); + + bTravel |= bNext || bPrev; + + SvxAbstractDialogFactory* pFact2 = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxPostItDialog> pDlg(pFact2->CreateSvxPostItDialog(GetView().GetFrameWeld(), aSet, bTravel)); + pDlg->HideAuthor(); + + pDlg->SetText(lcl_BuildTitleWithRedline(pRedline)); + + if (bTravel) + { + pDlg->EnableTravel(bNext, bPrev); + pDlg->SetPrevHdl(LINK(this, SwTextShell, RedlinePrevHdl)); + pDlg->SetNextHdl(LINK(this, SwTextShell, RedlineNextHdl)); + } + + SwViewShell::SetCareDialog(pDlg->GetDialog()); + g_bNoInterrupt = true; + + if ( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + OUString sMsg(pOutSet->Get(SID_ATTR_POSTIT_TEXT).GetValue()); + + // Insert or change a comment + rSh.SetRedlineComment(sMsg); + } + + SwViewShell::SetCareDialog(nullptr); + pDlg.disposeAndClear(); + g_bNoInterrupt = false; + rSh.ClearMark(); + GetView().AttrChangedNotify(nullptr); + } + } + break; + + case FN_JAVAEDIT: + { + OUString aType, aText; + bool bIsUrl=false; + bool bNew=false; + bool bUpdate = false; + SwFieldMgr aMgr; + if ( pItem ) + { + aText = static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SfxStringItem* pType = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + const SfxBoolItem* pIsUrl = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + if ( pType ) + aType = pType->GetValue(); + if ( pIsUrl ) + bIsUrl = pIsUrl->GetValue(); + + SwScriptField* pField = static_cast<SwScriptField*>(aMgr.GetCurField()); + bNew = !pField || (pField->GetTyp()->Which() != SwFieldIds::Script); + bUpdate = pField && ( bIsUrl != static_cast<bool>(pField->GetFormat()) || pField->GetPar2() != aType || pField->GetPar1() != aText ); + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractJavaEditDialog> pDlg(pFact->CreateJavaEditDialog(GetView().GetFrameWeld(), &rSh)); + if ( pDlg->Execute() ) + { + aType = pDlg->GetScriptType(); + aText = pDlg->GetScriptText(); + bIsUrl = pDlg->IsUrl(); + bNew = pDlg->IsNew(); + bUpdate = pDlg->IsUpdate(); + rReq.AppendItem( SfxStringItem( FN_JAVAEDIT, aText ) ); + rReq.AppendItem( SfxStringItem( FN_PARAM_2, aType ) ); + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bIsUrl ) ); + } + } + + if( bNew ) + { + SwInsertField_Data aData(SwFieldTypesEnum::Script, 0, aType, aText, bIsUrl ? 1 : 0); + aMgr.InsertField(aData); + rReq.Done(); + } + else if( bUpdate ) + { + aMgr.UpdateCurField( bIsUrl ? 1 : 0, aType, aText ); + rSh.SetUndoNoResetModified(); + rReq.Done(); + } + else + rReq.Ignore(); + } + break; + + case FN_INSERT_FLD_DATE : + { + nInsertType = SwFieldTypesEnum::Date; + bIsText = false; + // use long date format for Hungarian + SwPaM* pCursorPos = rSh.GetCursor(); + if( pCursorPos ) + { + LanguageType nLang = pCursorPos->GetPoint()->nNode.GetNode().GetTextNode()->GetLang(pCursorPos->GetPoint()->nContent.GetIndex()); + if (nLang == LANGUAGE_HUNGARIAN) + nInsertFormat = rSh.GetNumberFormatter()->GetFormatIndex(NF_DATE_SYSTEM_LONG, nLang); + } + goto FIELD_INSERT; + } + case FN_INSERT_FLD_TIME : + nInsertType = SwFieldTypesEnum::Time; + bIsText = false; + goto FIELD_INSERT; + case FN_INSERT_FLD_PGNUMBER: + nInsertType = SwFieldTypesEnum::PageNumber; + nInsertFormat = SVX_NUM_PAGEDESC; // Like page template + bIsText = false; + goto FIELD_INSERT; + case FN_INSERT_FLD_PGCOUNT : + nInsertType = SwFieldTypesEnum::DocumentStatistics; + nInsertSubType = 0; + bIsText = false; + nInsertFormat = SVX_NUM_PAGEDESC; + goto FIELD_INSERT; + case FN_INSERT_FLD_TOPIC : + nInsertType = SwFieldTypesEnum::DocumentInfo; + nInsertSubType = DI_SUBJECT; + goto FIELD_INSERT; + case FN_INSERT_FLD_TITLE : + nInsertType = SwFieldTypesEnum::DocumentInfo; + nInsertSubType = DI_TITLE; + goto FIELD_INSERT; + case FN_INSERT_FLD_AUTHOR : + nInsertType = SwFieldTypesEnum::DocumentInfo; + nInsertSubType = DI_CREATE|DI_SUB_AUTHOR; + +FIELD_INSERT: + { + //format conversion should only be done for number formatter formats + if(!nInsertFormat) + nInsertFormat = aFieldMgr.GetDefaultFormat(nInsertType, bIsText, rSh.GetNumberFormatter()); + SwInsertField_Data aData(nInsertType, nInsertSubType, + OUString(), OUString(), nInsertFormat); + aFieldMgr.InsertField(aData); + rReq.Done(); + } + break; + + case FN_INSERT_TEXT_FORMFIELD: + { + rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + + SwPaM* pCursorPos = rSh.GetCursor(); + if(pCursorPos) + { + // Insert five En Space into the text field so the field has extent + static const sal_Unicode vEnSpaces[ODF_FORMFIELD_DEFAULT_LENGTH] = {8194, 8194, 8194, 8194, 8194}; + bool bSuccess = rSh.GetDoc()->getIDocumentContentOperations().InsertString(*pCursorPos, OUString(vEnSpaces, ODF_FORMFIELD_DEFAULT_LENGTH)); + if(bSuccess) + { + IDocumentMarkAccess* pMarksAccess = rSh.GetDoc()->getIDocumentMarkAccess(); + SwPaM aFieldPam(pCursorPos->GetPoint()->nNode, pCursorPos->GetPoint()->nContent.GetIndex() - ODF_FORMFIELD_DEFAULT_LENGTH, + pCursorPos->GetPoint()->nNode, pCursorPos->GetPoint()->nContent.GetIndex()); + pMarksAccess->makeFieldBookmark(aFieldPam, OUString(), ODF_FORMTEXT, + aFieldPam.Start()); + } + } + + rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + rSh.GetView().GetViewFrame()->GetBindings().Invalidate( SID_UNDO ); + } + break; + case FN_INSERT_CHECKBOX_FORMFIELD: + { + rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + + SwPaM* pCursorPos = rSh.GetCursor(); + if(pCursorPos) + { + IDocumentMarkAccess* pMarksAccess = rSh.GetDoc()->getIDocumentMarkAccess(); + pMarksAccess->makeNoTextFieldBookmark(*pCursorPos, OUString(), ODF_FORMCHECKBOX); + } + + rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + rSh.GetView().GetViewFrame()->GetBindings().Invalidate( SID_UNDO ); + } + break; + case FN_INSERT_DROPDOWN_FORMFIELD: + { + rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + + SwPaM* pCursorPos = rSh.GetCursor(); + if(pCursorPos) + { + IDocumentMarkAccess* pMarksAccess = rSh.GetDoc()->getIDocumentMarkAccess(); + pMarksAccess->makeNoTextFieldBookmark(*pCursorPos, OUString(), ODF_FORMDROPDOWN); + } + + rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + rSh.GetView().GetViewFrame()->GetBindings().Invalidate( SID_UNDO ); + } + break; + case FN_INSERT_DATE_FORMFIELD: + { + rSh.GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + + SwPaM* pCursorPos = rSh.GetCursor(); + if(pCursorPos) + { + // Insert five enspaces into the text field so the field has extent + sal_Unicode vEnSpaces[ODF_FORMFIELD_DEFAULT_LENGTH] = {8194, 8194, 8194, 8194, 8194}; + bool bSuccess = rSh.GetDoc()->getIDocumentContentOperations().InsertString(*pCursorPos, OUString(vEnSpaces, ODF_FORMFIELD_DEFAULT_LENGTH)); + if(bSuccess) + { + IDocumentMarkAccess* pMarksAccess = rSh.GetDoc()->getIDocumentMarkAccess(); + SwPaM aFieldPam(pCursorPos->GetPoint()->nNode, pCursorPos->GetPoint()->nContent.GetIndex() - ODF_FORMFIELD_DEFAULT_LENGTH, + pCursorPos->GetPoint()->nNode, pCursorPos->GetPoint()->nContent.GetIndex()); + sw::mark::IFieldmark* pFieldBM = pMarksAccess->makeFieldBookmark(aFieldPam, OUString(), ODF_FORMDATE, + aFieldPam.Start()); + + // Use a default date format and language + sw::mark::IFieldmark::parameter_map_t* pParameters = pFieldBM->GetParameters(); + SvNumberFormatter* pFormatter = rSh.GetDoc()->GetNumberFormatter(); + sal_uInt32 nStandardFormat = pFormatter->GetStandardFormat(SvNumFormatType::DATE); + const SvNumberformat* pFormat = pFormatter->GetEntry(nStandardFormat); + + (*pParameters)[ODF_FORMDATE_DATEFORMAT] <<= pFormat->GetFormatstring(); + (*pParameters)[ODF_FORMDATE_DATEFORMAT_LANGUAGE] <<= LanguageTag(pFormat->GetLanguage()).getBcp47(); + } + } + + rSh.GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT_FORM_FIELD, nullptr); + rSh.GetView().GetViewFrame()->GetBindings().Invalidate( SID_UNDO ); + } + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + } +} + +void SwTextShell::StateField( SfxItemSet &rSet ) +{ + SwWrtShell& rSh = GetShell(); + SfxWhichIter aIter( rSet ); + const SwField* pField = nullptr; + bool bGetField = false; + sal_uInt16 nWhich = aIter.FirstWhich(); + + while (nWhich) + { + switch (nWhich) + { + case FN_DELETE_COMMENT: + case FN_DELETE_NOTE_AUTHOR: + case FN_DELETE_ALL_NOTES: + case FN_FORMAT_ALL_NOTES: + case FN_HIDE_NOTE: + case FN_HIDE_NOTE_AUTHOR: + case FN_HIDE_ALL_NOTES: + { + SwPostItMgr* pPostItMgr = GetView().GetPostItMgr(); + if ( !pPostItMgr ) + rSet.InvalidateItem( nWhich ); + else if ( !pPostItMgr->HasActiveSidebarWin() ) + { + rSet.InvalidateItem( FN_DELETE_COMMENT ); + rSet.InvalidateItem( FN_HIDE_NOTE ); + } + } + break; + + case FN_EDIT_FIELD: + { + if( !bGetField ) + { + pField = rSh.GetCurField(); + bGetField = true; + } + + SwFieldIds nTempWhich = pField ? pField->GetTyp()->Which() : SwFieldIds::Unknown; + if( SwFieldIds::Unknown == nTempWhich || + SwFieldIds::Postit == nTempWhich || + SwFieldIds::Script == nTempWhich || + SwFieldIds::TableOfAuthorities == nTempWhich ) + rSet.DisableItem( nWhich ); + else if( SwFieldIds::Dde == nTempWhich && + !static_cast<SwDDEFieldType*>(pField->GetTyp())->GetBaseLink().IsVisible()) + { + // nested links cannot be edited + rSet.DisableItem( nWhich ); + } + } + break; + + case FN_EXECUTE_MACROFIELD: + { + if(!bGetField) + { + pField = rSh.GetCurField(); + bGetField = true; + } + if(!pField || pField->GetTyp()->Which() != SwFieldIds::Macro) + rSet.DisableItem(nWhich); + } + break; + + case FN_INSERT_FIELD: + { + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + else + { + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + //#i5788# prevent closing of the field dialog while a modal dialog ( Input field dialog ) is active + if(!pVFrame->IsInModalMode() && + pVFrame->KnowsChildWindow(FN_INSERT_FIELD) && !pVFrame->HasChildWindow(FN_INSERT_FIELD_DATA_ONLY) ) + rSet.Put(SfxBoolItem( FN_INSERT_FIELD, pVFrame->HasChildWindow(nWhich))); + else + rSet.DisableItem(FN_INSERT_FIELD); + } + } + break; + + case FN_INSERT_REF_FIELD: + { + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if ( !pVFrame->KnowsChildWindow(FN_INSERT_FIELD) + || rSh.CursorInsideInputField() ) + { + rSet.DisableItem(FN_INSERT_REF_FIELD); + } + } + break; + + case FN_INSERT_FIELD_CTRL: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + else + { + rSet.Put(SfxBoolItem( nWhich, GetView().GetViewFrame()->HasChildWindow(FN_INSERT_FIELD))); + } + break; + + case FN_REDLINE_COMMENT: + if (!comphelper::LibreOfficeKit::isActive() && !rSh.GetCurrRedline()) + rSet.DisableItem(nWhich); + break; + + case FN_REPLY: + if (!comphelper::LibreOfficeKit::isActive()) + rSet.DisableItem(nWhich); + break; + + case FN_POSTIT : + case FN_JAVAEDIT : + { + bool bCurField = false; + pField = rSh.GetCurField(); + if(nWhich == FN_POSTIT) + bCurField = pField && pField->GetTyp()->Which() == SwFieldIds::Postit; + else + bCurField = pField && pField->GetTyp()->Which() == SwFieldIds::Script; + + if( !bCurField && rSh.IsReadOnlyAvailable() && rSh.HasReadonlySel() ) + { + rSet.DisableItem(nWhich); + } + else if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + // tdf#86188 Allow disabling comment insertion on footnote/endnote for better OOXML interoperability + else if ( rSh.IsCursorInFootnote() && !officecfg::Office::Compatibility::View::AllowCommentsInFootnotes::get() ) + { + rSet.DisableItem(nWhich); + } + } + + break; + + case FN_INSERT_FLD_AUTHOR: + case FN_INSERT_FLD_DATE: + case FN_INSERT_FLD_PGCOUNT: + case FN_INSERT_FLD_PGNUMBER: + case FN_INSERT_FLD_TIME: + case FN_INSERT_FLD_TITLE: + case FN_INSERT_FLD_TOPIC: + case FN_INSERT_DBFIELD: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + break; + + case FN_INSERT_TEXT_FORMFIELD: + case FN_INSERT_CHECKBOX_FORMFIELD: + case FN_INSERT_DROPDOWN_FORMFIELD: + case FN_INSERT_DATE_FORMFIELD: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + else + { + // Check whether we are in a text form field + SwPosition aCursorPos(*rSh.GetCursor()->GetPoint()); + sw::mark::IFieldmark* pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aCursorPos); + if ((!pFieldBM || pFieldBM->GetFieldname() != ODF_FORMTEXT) + && aCursorPos.nContent.GetIndex() > 0) + { + SwPosition aPos(aCursorPos); + --aPos.nContent; + pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aPos); + } + if (pFieldBM && pFieldBM->GetFieldname() == ODF_FORMTEXT && + (aCursorPos > pFieldBM->GetMarkStart() && aCursorPos < pFieldBM->GetMarkEnd() )) + { + rSet.DisableItem(nWhich); + } + } + break; + + } + nWhich = aIter.NextWhich(); + } +} + +void SwTextShell::InsertHyperlink(const SvxHyperlinkItem& rHlnkItem) +{ + const OUString& rName = rHlnkItem.GetName(); + const OUString& rURL = rHlnkItem.GetURL(); + const OUString& rTarget = rHlnkItem.GetTargetFrame(); + sal_uInt16 nType = static_cast<sal_uInt16>(rHlnkItem.GetInsertMode()); + nType &= ~HLINK_HTMLMODE; + const SvxMacroTableDtor* pMacroTable = rHlnkItem.GetMacroTable(); + + SwWrtShell& rSh = GetShell(); + + if( rSh.GetSelectionType() & SelectionType::Text ) + { + rSh.StartAction(); + SfxItemSet aSet(GetPool(), svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr( aSet ); + + const SfxPoolItem* pItem; + if(SfxItemState::SET == aSet.GetItemState(RES_TXTATR_INETFMT, false, &pItem)) + { + // Select links + rSh.SwCursorShell::SelectTextAttr(RES_TXTATR_INETFMT, false); + } + switch (nType) + { + case HLINK_DEFAULT: + case HLINK_FIELD: + { + SwFormatINetFormat aINetFormat( rURL, rTarget ); + aINetFormat.SetName(rHlnkItem.GetIntName()); + if(pMacroTable) + { + const SvxMacro *pMacro = pMacroTable->Get( SvMacroItemId::OnMouseOver ); + if( pMacro ) + aINetFormat.SetMacro(SvMacroItemId::OnMouseOver, *pMacro); + pMacro = pMacroTable->Get( SvMacroItemId::OnClick ); + if( pMacro ) + aINetFormat.SetMacro(SvMacroItemId::OnClick, *pMacro); + pMacro = pMacroTable->Get( SvMacroItemId::OnMouseOut ); + if( pMacro ) + aINetFormat.SetMacro(SvMacroItemId::OnMouseOut, *pMacro); + } + rSh.SttSelect(); + rSh.InsertURL( aINetFormat, rName, true ); + rSh.EndSelect(); + } + break; + + case HLINK_BUTTON: + bool bSel = rSh.HasSelection(); + if(bSel) + rSh.DelRight(); + InsertURLButton( rURL, rTarget, rName ); + rSh.EnterStdMode(); + break; + } + rSh.EndAction(); + } +} + +IMPL_LINK( SwTextShell, RedlineNextHdl, AbstractSvxPostItDialog&, rDlg, void ) +{ + SwWrtShell* pSh = GetShellPtr(); + + // Insert or change a comment. + pSh->SetRedlineComment(rDlg.GetNote()); + + const SwRangeRedline *pRedline = pSh->GetCurrRedline(); + + if (!pRedline) + return; + + // Traveling only if more than one field. + if( !pSh->IsCursorPtAtEnd() ) + pSh->SwapPam(); // Move the cursor behind the Redline. + + pSh->Push(); + const SwRangeRedline *pActRed = pSh->SelNextRedline(); + pSh->Pop((pActRed != nullptr) ? SwCursorShell::PopMode::DeleteStack : SwCursorShell::PopMode::DeleteCurrent); + + bool bEnable = false; + + if (pActRed) + { + pSh->StartAction(); + pSh->Push(); + bEnable = pSh->SelNextRedline() != nullptr; + pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + pSh->EndAction(); + } + + rDlg.EnableTravel(bEnable, true); + + if( pSh->IsCursorPtAtEnd() ) + pSh->SwapPam(); + + pRedline = pSh->GetCurrRedline(); + OUString sComment = convertLineEnd(pRedline->GetComment(), GetSystemLineEnd()); + + rDlg.SetNote(sComment); + rDlg.ShowLastAuthor( pRedline->GetAuthorString(), + GetAppLangDateTimeString( + pRedline->GetRedlineData().GetTimeStamp() )); + + rDlg.SetText(lcl_BuildTitleWithRedline(pRedline)); + +} + +IMPL_LINK( SwTextShell, RedlinePrevHdl, AbstractSvxPostItDialog&, rDlg, void ) +{ + SwWrtShell* pSh = GetShellPtr(); + + // Insert or change a comment. + pSh->SetRedlineComment(rDlg.GetNote()); + + const SwRangeRedline *pRedline = pSh->GetCurrRedline(); + + if (!pRedline) + return; + + // Traveling only if more than one field. + pSh->Push(); + const SwRangeRedline *pActRed = pSh->SelPrevRedline(); + pSh->Pop((pActRed != nullptr) ? SwCursorShell::PopMode::DeleteStack : SwCursorShell::PopMode::DeleteCurrent); + + bool bEnable = false; + + if (pActRed) + { + pSh->StartAction(); + pSh->Push(); + bEnable = pSh->SelPrevRedline() != nullptr; + pSh->Pop(SwCursorShell::PopMode::DeleteCurrent); + pSh->EndAction(); + } + + rDlg.EnableTravel(true, bEnable); + + pRedline = pSh->GetCurrRedline(); + OUString sComment = convertLineEnd(pRedline->GetComment(), GetSystemLineEnd()); + + rDlg.SetNote(sComment); + rDlg.ShowLastAuthor(pRedline->GetAuthorString(), + GetAppLangDateTimeString( + pRedline->GetRedlineData().GetTimeStamp() )); + + rDlg.SetText(lcl_BuildTitleWithRedline(pRedline)); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textglos.cxx b/sw/source/uibase/shells/textglos.cxx new file mode 100644 index 000000000..b6aee47f7 --- /dev/null +++ b/sw/source/uibase/shells/textglos.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/request.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> + +#include <view.hxx> +#include <cmdid.h> +#include <textsh.hxx> +#include <initui.hxx> +#include <gloshdl.hxx> +#include <glosdoc.hxx> +#include <gloslst.hxx> +#include <swabstdlg.hxx> + +void SwTextShell::ExecGlossary(SfxRequest &rReq) +{ + const sal_uInt16 nSlot = rReq.GetSlot(); + ::GetGlossaries()->UpdateGlosPath(!rReq.IsAPI() || + FN_GLOSSARY_DLG == nSlot ); + SwGlossaryHdl* pGlosHdl = GetView().GetGlosHdl(); + // Update SwGlossaryList? + bool bUpdateList = false; + + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if(pArgs) + pArgs->GetItemState(nSlot, false, &pItem ); + + switch( nSlot ) + { + case FN_GLOSSARY_DLG: + pGlosHdl->GlossaryDlg(); + bUpdateList = true; + rReq.Ignore(); + break; + case FN_EXPAND_GLOSSARY: + { + bool bReturn; + bReturn = pGlosHdl->ExpandGlossary(rReq.GetFrameWeld()); + rReq.SetReturnValue( SfxBoolItem( nSlot, bReturn ) ); + rReq.Done(); + } + break; + case FN_NEW_GLOSSARY: + if(pItem && pArgs->Count() == 3 ) + { + OUString aGroup = static_cast<const SfxStringItem *>(pItem)->GetValue(); + OUString aName; + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_1, false, &pItem )) + aName = static_cast<const SfxStringItem *>(pItem)->GetValue(); + OUString aShortName; + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_2, false, &pItem )) + aShortName = static_cast<const SfxStringItem *>(pItem)->GetValue(); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossarySetActGroup fnSetActGroup = pFact->SetGlossaryActGroupFunc(); + if ( fnSetActGroup ) + (*fnSetActGroup)( aGroup ); + pGlosHdl->SetCurGroup(aGroup, true); + // Chosen group must be created in NewGlossary if necessary! + pGlosHdl->NewGlossary( aName, aShortName, true ); + rReq.Done(); + } + bUpdateList = true; + break; + case FN_SET_ACT_GLOSSARY: + if(pItem) + { + OUString aGroup = static_cast<const SfxStringItem *>(pItem)->GetValue(); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossarySetActGroup fnSetActGroup = pFact->SetGlossaryActGroupFunc(); + if ( fnSetActGroup ) + (*fnSetActGroup)( aGroup ); + rReq.Done(); + } + break; + case FN_INSERT_GLOSSARY: + { + if(pItem && pArgs->Count() > 1) + { + OUString aGroup = static_cast<const SfxStringItem *>(pItem)->GetValue(); + OUString aName; + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_1, false, &pItem )) + aName = static_cast<const SfxStringItem *>(pItem)->GetValue(); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ::GlossarySetActGroup fnSetActGroup = pFact->SetGlossaryActGroupFunc(); + if ( fnSetActGroup ) + (*fnSetActGroup)( aGroup ); + pGlosHdl->SetCurGroup(aGroup, true); + rReq.SetReturnValue(SfxBoolItem(nSlot, pGlosHdl->InsertGlossary( aName ))); + rReq.Done(); + } + } + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + if(bUpdateList) + { + SwGlossaryList* pList = ::GetGlossaryList(); + if(pList->IsActive()) + pList->Update(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textidx.cxx b/sw/source/uibase/shells/textidx.cxx new file mode 100644 index 000000000..8247dbdbf --- /dev/null +++ b/sw/source/uibase/shells/textidx.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 <hintids.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <editeng/sizeitem.hxx> +#include <fmtfsize.hxx> +#include <fldbas.hxx> +#include <uiitems.hxx> +#include <viewopt.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <wrtsh.hxx> +#include <textsh.hxx> +#include <idxmrk.hxx> +#include <toxmgr.hxx> +#include <swabstdlg.hxx> + +void SwTextShell::ExecIdx(SfxRequest const &rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + const sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(nSlot, false, &pItem ); + + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + + switch( nSlot ) + { + case FN_EDIT_AUTH_ENTRY_DLG : + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwAutoMarkDialog(GetView().GetFrameWeld(), GetShell())); + pDlg->Execute(); + } + break; + case FN_INSERT_AUTH_ENTRY_DLG: + { + // no BASIC support + pVFrame->ToggleChildWindow(FN_INSERT_AUTH_ENTRY_DLG); + Invalidate(rReq.GetSlot()); + } + break; + case FN_INSERT_IDX_ENTRY_DLG: + { + pVFrame->ToggleChildWindow(FN_INSERT_IDX_ENTRY_DLG); + Invalidate(rReq.GetSlot()); + } + break; + case FN_EDIT_IDX_ENTRY_DLG: + { + SwTOXMgr aMgr(GetShellPtr()); + short nRet = RET_OK; + if(aMgr.GetTOXMarkCount() > 1) + { // Several marks, which should it be? + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pMultDlg(pFact->CreateMultiTOXMarkDlg(GetView().GetFrameWeld(), aMgr)); + nRet = pMultDlg->Execute(); + } + if( nRet == RET_OK) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateIndexMarkModalDlg(GetView().GetFrameWeld(), GetShell(), aMgr.GetCurTOXMark())); + pDlg->Execute(); + } + break; + } + case FN_IDX_MARK_TO_IDX: + { + GetShell().GotoTOXMarkBase(); + break; + } + case FN_INSERT_MULTI_TOX: + { + SfxItemSet aSet( + GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + FN_PARAM_TOX_TYPE, FN_PARAM_TOX_TYPE>{}); + SwWrtShell& rSh = GetShell(); + SwRect aRect; + rSh.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + + long nWidth = aRect.Width(); + aSet.Put(SwFormatFrameSize(SwFrameSize::Variable, nWidth)); + // Height = width for a more consistent preview (analogous to edit range) + aSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(nWidth, nWidth))); + const SwTOXBase* pCurTOX = nullptr; + bool bGlobal = false; + if(pItem) + { + pCurTOX = static_cast<const SwTOXBase*>(static_cast<const SwPtrItem*>(pItem)->GetValue()); + bGlobal = true; + } + else + pCurTOX = rSh.GetCurTOX(); + if(pCurTOX) + { + const SfxItemSet* pSet = pCurTOX->GetAttrSet(); + if(pSet) + aSet.Put(*pSet); + } + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<AbstractMultiTOXTabDialog> pDlg(pFact->CreateMultiTOXTabDialog( + GetView().GetFrameWeld(), aSet, rSh, const_cast<SwTOXBase*>(pCurTOX), + bGlobal)); + pDlg->StartExecuteAsync([pDlg](sal_Int32 /*nResult*/){ + pDlg->disposeOnce(); + }); + } + break; + case FN_REMOVE_CUR_TOX: + { + SwWrtShell& rSh = GetShell(); + const SwTOXBase* pBase = rSh.GetCurTOX(); + OSL_ENSURE(pBase, "no TOXBase to remove"); + if( pBase ) + rSh.DeleteTOX(*pBase, true); + } + break; + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +void SwTextShell::GetIdxState(SfxItemSet &rSet) +{ + SwWrtShell& rSh = GetShell(); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + SwInsertIdxMarkWrapper *pIdxMrk = static_cast<SwInsertIdxMarkWrapper*>( + pVFrame->GetChildWindow(FN_INSERT_IDX_ENTRY_DLG)); + + SfxChildWindow* pAuthMark = pVFrame->GetChildWindow(FN_INSERT_AUTH_ENTRY_DLG); + + const bool bHtmlMode = 0 != ::GetHtmlMode( GetView().GetDocShell() ); + const SwTOXBase* pBase = nullptr; + if( bHtmlMode || nullptr != ( pBase = rSh.GetCurTOX()) ) + { + if( pBase ) + { + if(pBase->IsTOXBaseInReadonly()) + { + rSet.DisableItem( FN_INSERT_MULTI_TOX ); + } + } + + rSet.DisableItem( FN_EDIT_IDX_ENTRY_DLG ); + rSet.DisableItem( FN_EDIT_AUTH_ENTRY_DLG ); + + if(!pIdxMrk) + rSet.DisableItem( FN_INSERT_IDX_ENTRY_DLG ); + else + rSet.Put(SfxBoolItem(FN_INSERT_IDX_ENTRY_DLG, true)); + + if(!pAuthMark) + rSet.DisableItem( FN_INSERT_AUTH_ENTRY_DLG ); + else + rSet.Put(SfxBoolItem(FN_INSERT_AUTH_ENTRY_DLG, true)); + + } + else if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem( FN_INSERT_IDX_ENTRY_DLG ); + rSet.DisableItem( FN_INSERT_AUTH_ENTRY_DLG ); + rSet.DisableItem( FN_EDIT_AUTH_ENTRY_DLG ); + rSet.DisableItem( FN_EDIT_IDX_ENTRY_DLG ); + rSet.DisableItem( FN_INSERT_MULTI_TOX ); + rSet.DisableItem( FN_REMOVE_CUR_TOX ); + } + else + { + + bool bEnableEdit = true; + bool bInReadonly = rSh.HasReadonlySel(); + if( rSh.HasSelection() || bInReadonly) + bEnableEdit = false; + else + { + SwTOXMarks aArr; + rSh.GetCurTOXMarks( aArr ); + if( aArr.empty()) + bEnableEdit = false; + } + + if(!bEnableEdit) + rSet.DisableItem( FN_EDIT_IDX_ENTRY_DLG ); + + if(bInReadonly) + { + rSet.DisableItem(FN_INSERT_IDX_ENTRY_DLG); + rSet.DisableItem( FN_INSERT_MULTI_TOX ); + } + else + rSet.Put(SfxBoolItem(FN_INSERT_IDX_ENTRY_DLG, + nullptr != pIdxMrk)); + + SwField* pField = rSh.GetCurField(); + + if(bInReadonly) + rSet.DisableItem(FN_INSERT_AUTH_ENTRY_DLG); + else + rSet.Put(SfxBoolItem(FN_INSERT_AUTH_ENTRY_DLG, nullptr != pAuthMark)); + + if( bInReadonly || !pField || + pField->GetTyp()->Which() != SwFieldIds::TableOfAuthorities) + rSet.DisableItem(FN_EDIT_AUTH_ENTRY_DLG); + rSet.DisableItem(FN_REMOVE_CUR_TOX); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textsh.cxx b/sw/source/uibase/shells/textsh.cxx new file mode 100644 index 000000000..85831dc92 --- /dev/null +++ b/sw/source/uibase/shells/textsh.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 <config_features.h> + +#include <hintids.hxx> +#include <comphelper/string.hxx> +#include <svl/globalnameitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/frmdescr.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> + +#include <i18nutil/transliteration.hxx> +#include <svl/eitem.hxx> +#include <svl/ptitem.hxx> +#include <svl/stritem.hxx> +#include <unotools/moduleoptions.hxx> +#include <svx/hlnkitem.hxx> +#include <svl/whiter.hxx> +#include <sfx2/request.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/scripttypeitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <svtools/htmlcfg.hxx> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <comphelper/classids.hxx> +#include <editeng/acorrcfg.hxx> +#include <wdocsh.hxx> +#include <fmtinfmt.hxx> +#include <fmtclds.hxx> +#include <fmtfsize.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <IDocumentChartDataProviderAccess.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <globals.hrc> +#include <frmmgr.hxx> +#include <textsh.hxx> +#include <frmfmt.hxx> +#include <tablemgr.hxx> +#include <swundo.hxx> +#include <breakit.hxx> +#include <edtwin.hxx> +#include <strings.hrc> +#include <unochart.hxx> +#include <chartins.hxx> +#include <viewopt.hxx> + +#define ShellClass_SwTextShell +#include <sfx2/msg.hxx> +#include <vcl/EnumContext.hxx> +#include <swslots.hxx> +#include <SwRewriter.hxx> +#include <SwCapObjType.hxx> + +using namespace ::com::sun::star; + +#include <svx/svxdlg.hxx> +#include <swabstdlg.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <drawdoc.hxx> +#include <svtools/embedhlp.hxx> +#include <sfx2/event.hxx> +#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <IDocumentUndoRedo.hxx> + +SFX_IMPL_INTERFACE(SwTextShell, SwBaseShell) + +IMPL_STATIC_LINK( SwTextShell, DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvent, void ) +{ + SwView* pView = ::GetActiveView(); + SwWrtShell& rWrtShell = pView->GetWrtShell(); + + sal_Int16 nDialogRet = pEvent->DialogResult; + if( nDialogRet == ui::dialogs::ExecutableDialogResults::CANCEL ) + { + rWrtShell.Undo(); + rWrtShell.GetIDocumentUndoRedo().ClearRedo(); + } + else + { + OSL_ENSURE( nDialogRet == ui::dialogs::ExecutableDialogResults::OK, + "dialog execution failed" ); + } +} + +void SwTextShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("text"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Text_Toolbox_Sw); + + GetStaticInterface()->RegisterChildWindow(FN_EDIT_FORMULA); + GetStaticInterface()->RegisterChildWindow(FN_INSERT_FIELD); + GetStaticInterface()->RegisterChildWindow(FN_INSERT_IDX_ENTRY_DLG); + GetStaticInterface()->RegisterChildWindow(FN_INSERT_AUTH_ENTRY_DLG); + GetStaticInterface()->RegisterChildWindow(SID_RUBY_DIALOG); + GetStaticInterface()->RegisterChildWindow(FN_WORDCOUNT_DIALOG); +} + + +void SwTextShell::ExecInsert(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + + OSL_ENSURE( !rSh.IsObjSelected() && !rSh.IsFrameSelected(), + "wrong shell on dispatcher" ); + + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + const sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(nSlot, false, &pItem ); + + switch( nSlot ) + { + case FN_INSERT_STRING: + if( pItem ) + rSh.InsertByWord(static_cast<const SfxStringItem *>(pItem)->GetValue()); + break; + + case FN_INSERT_SOFT_HYPHEN: + if( CHAR_SOFTHYPHEN != rSh.SwCursorShell::GetChar() && + CHAR_SOFTHYPHEN != rSh.SwCursorShell::GetChar( true, -1 )) + rSh.Insert( OUString( CHAR_SOFTHYPHEN ) ); + break; + + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + { + const sal_Unicode cIns = FN_INSERT_HARD_SPACE == nSlot ? CHAR_HARDBLANK : CHAR_HARDHYPHEN; + + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect(); + if( pACorr && rACfg.IsAutoFormatByInput() + && pACorr->IsAutoCorrFlag( + ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord | + ACFlags::AddNonBrkSpace | ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL | + ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect ) ) + { + rSh.AutoCorrect( *pACorr, cIns ); + } + else + { + rSh.Insert( OUString( cIns ) ); + } + } + break; + + case FN_INSERT_NNBSP: // shift+mod2/alt+space inserts some other character w/o going through SwEditWin::KeyInput(), at least on macOS + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_ZWNBSP : + case SID_INSERT_ZWSP: + { + sal_Unicode cIns = 0; + switch(nSlot) + { + case SID_INSERT_RLM : cIns = CHAR_RLM ; break; + case SID_INSERT_LRM : cIns = CHAR_LRM ; break; + case SID_INSERT_ZWSP : cIns = CHAR_ZWSP ; break; + case SID_INSERT_ZWNBSP: cIns = CHAR_ZWNBSP; break; + case FN_INSERT_NNBSP: cIns = CHAR_NNBSP; break; + } + rSh.Insert( OUString( cIns ) ); + } + break; + + case FN_INSERT_BREAK: + { + if( !rSh.CursorInsideInputField() ) + { + rSh.SplitNode(); + } + else + { + rSh.InsertLineBreak(); + } + } + rReq.Done(); + break; + + case FN_INSERT_PAGEBREAK: + rSh.InsertPageBreak(); + rReq.Done(); + break; + + case FN_INSERT_LINEBREAK: + rSh.InsertLineBreak(); + rReq.Done(); + break; + + case FN_INSERT_COLUMN_BREAK: + rSh.InsertColumnBreak(); + rReq.Done(); + break; + + case SID_HYPERLINK_SETLINK: + if (pItem) + InsertHyperlink(*static_cast<const SvxHyperlinkItem *>(pItem)); + rReq.Done(); + break; + +#if HAVE_FEATURE_AVMEDIA + case SID_INSERT_AVMEDIA: + rReq.SetReturnValue(SfxBoolItem(nSlot, InsertMediaDlg( rReq ))); + break; +#endif + + case SID_INSERT_OBJECT: + { + const SfxGlobalNameItem* pNameItem = rReq.GetArg<SfxGlobalNameItem>(SID_INSERT_OBJECT); + SvGlobalName *pName = nullptr; + SvGlobalName aName; + if ( pNameItem ) + { + aName = pNameItem->GetValue(); + pName = &aName; + } + + svt::EmbeddedObjectRef xObj; + rSh.InsertObject( xObj, pName, nSlot); + rReq.Done(); + break; + } + case SID_INSERT_FLOATINGFRAME: + { + svt::EmbeddedObjectRef xObj; + const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + const SfxStringItem* pURLItem = rReq.GetArg<SfxStringItem>(FN_PARAM_2); + const SvxSizeItem* pMarginItem = rReq.GetArg<SvxSizeItem>(FN_PARAM_3); + const SfxByteItem* pScrollingItem = rReq.GetArg<SfxByteItem>(FN_PARAM_4); + const SfxBoolItem* pBorderItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_5); + + if(pURLItem) // URL is a _must_ + { + comphelper::EmbeddedObjectContainer aCnt; + OUString aName; + xObj.Assign( aCnt.CreateEmbeddedObject( SvGlobalName( SO3_IFRAME_CLASSID ).GetByteSequence(), aName ), + embed::Aspects::MSOLE_CONTENT ); + svt::EmbeddedObjectRef::TryRunningState( xObj.GetObject() ); + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + try + { + ScrollingMode eScroll = ScrollingMode::Auto; + if( pScrollingItem && pScrollingItem->GetValue() <= int(ScrollingMode::Auto) ) + eScroll = static_cast<ScrollingMode>(pScrollingItem->GetValue()); + + Size aMargin; + if ( pMarginItem ) + aMargin = pMarginItem->GetSize(); + + xSet->setPropertyValue("FrameURL", uno::makeAny( pURLItem->GetValue() ) ); + if ( pNameItem ) + xSet->setPropertyValue("FrameName", uno::makeAny( pNameItem->GetValue() ) ); + + if ( eScroll == ScrollingMode::Auto ) + xSet->setPropertyValue("FrameIsAutoScroll", + uno::makeAny( true ) ); + else + xSet->setPropertyValue("FrameIsScrollingMode", + uno::makeAny( eScroll == ScrollingMode::Yes ) ); + + if ( pBorderItem ) + xSet->setPropertyValue("FrameIsBorder", + uno::makeAny( pBorderItem->GetValue() ) ); + + if ( pMarginItem ) + { + xSet->setPropertyValue("FrameMarginWidth", + uno::makeAny( sal_Int32( aMargin.Width() ) ) ); + + xSet->setPropertyValue("FrameMarginHeight", + uno::makeAny( sal_Int32( aMargin.Height() ) ) ); + } + } + catch (const uno::Exception&) + { + } + } + + rSh.InsertOleObject( xObj ); + } + else + { + rSh.InsertObject( xObj, nullptr, nSlot); + rReq.Done(); + } + } + break; + case SID_INSERT_DIAGRAM: + { + SvtModuleOptions aMOpt; + if ( !aMOpt.IsChart() ) + break; + if(!rReq.IsAPI()) + { + SwInsertChart( LINK( this, SwTextShell, DialogClosedHdl ) ); + } + else + { + uno::Reference< chart2::data::XDataProvider > xDataProvider; + bool bFillWithData = true; + OUString aRangeString; + if (!GetShell().IsTableComplexForChart()) + { + SwFrameFormat* pTableFormat = GetShell().GetTableFormat(); + aRangeString = pTableFormat->GetName() + "." + + GetShell().GetBoxNms(); + + // get table data provider + xDataProvider.set( GetView().GetDocShell()->getIDocumentChartDataProviderAccess().GetChartDataProvider() ); + } + else + bFillWithData = false; // will create chart with only it's default image + + SwTableFUNC( &rSh ).InsertChart( xDataProvider, bFillWithData, aRangeString ); + rSh.LaunchOLEObj(); + + svt::EmbeddedObjectRef& xObj = rSh.GetOLEObject(); + if(pItem && xObj.is()) + { + Size aSize(static_cast<const SvxSizeItem*>(pItem)->GetSize()); + aSize = OutputDevice::LogicToLogic + ( aSize, MapMode( MapUnit::MapTwip ), MapMode( MapUnit::Map100thMM ) ); + + if(aSize.Width() > MINLAY&& aSize.Height()> MINLAY) + { + awt::Size aSz; + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( xObj.GetViewAspect(), aSz ); + } + } + } + } + break; + + case FN_INSERT_SMA: + { + // #i34343# Inserting a math object into an autocompletion crashes + // the suggestion has to be removed before + GetView().GetEditWin().StopQuickHelp(); + SvGlobalName aGlobalName( SO3_SM_CLASSID ); + rSh.InsertObject( svt::EmbeddedObjectRef(), &aGlobalName ); + } + break; + + case FN_INSERT_TABLE: + InsertTable( rReq ); + break; + + case FN_INSERT_FRAME_INTERACT_NOCOL: + case FN_INSERT_FRAME_INTERACT: + { + sal_uInt16 nCols = 1; + bool bModifier1 = rReq.GetModifier() == KEY_MOD1; + if(pArgs) + { + if(FN_INSERT_FRAME_INTERACT_NOCOL != nSlot && + pArgs->GetItemState(SID_ATTR_COLUMNS, false, &pItem) == SfxItemState::SET) + nCols = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + if(pArgs->GetItemState(SID_MODIFIER, false, &pItem) == SfxItemState::SET) + bModifier1 |= KEY_MOD1 == static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + } + if(bModifier1 ) + { + SwEditWin& rEdtWin = GetView().GetEditWin(); + Size aWinSize = rEdtWin.GetSizePixel(); + Point aStartPos(aWinSize.Width()/2, aWinSize.Height() / 2); + aStartPos = rEdtWin.PixelToLogic(aStartPos); + aStartPos.AdjustX( -(8 * MM50) ); + aStartPos.AdjustY( -(4 * MM50) ); + Size aSize(16 * MM50, 8 * MM50); + GetShell().LockPaint(); + GetShell().StartAllAction(); + SwFlyFrameAttrMgr aMgr( true, GetShellPtr(), Frmmgr_Type::TEXT, nullptr ); + if(nCols > 1) + { + SwFormatCol aCol; + aCol.Init( nCols, aCol.GetGutterWidth(), aCol.GetWishWidth() ); + aMgr.SetCol( aCol ); + } + aMgr.InsertFlyFrame(RndStdIds::FLY_AT_PARA, aStartPos, aSize); + GetShell().EndAllAction(); + GetShell().UnlockPaint(); + } + else + { + GetView().InsFrameMode(nCols); + } + rReq.Ignore(); + } + break; + case FN_INSERT_FRAME: + { + bool bSingleCol = false; + if( nullptr!= dynamic_cast< SwWebDocShell*>( GetView().GetDocShell()) ) + { + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + if( HTML_CFG_MSIE == rHtmlOpt.GetExportMode() ) + { + bSingleCol = true; + } + + } + // Create new border + SwFlyFrameAttrMgr aMgr( true, GetShellPtr(), Frmmgr_Type::TEXT, nullptr ); + if(pArgs) + { + Size aSize(aMgr.GetSize()); + aSize.setWidth( GetShell().GetAnyCurRect(CurRectType::PagePrt).Width() ); + Point aPos = aMgr.GetPos(); + RndStdIds eAnchor = RndStdIds::FLY_AT_PARA; + if(pArgs->GetItemState(nSlot, false, &pItem) == SfxItemState::SET) + eAnchor = static_cast<RndStdIds>(static_cast<const SfxUInt16Item *>(pItem)->GetValue()); + if(pArgs->GetItemState(FN_PARAM_1, false, &pItem) == SfxItemState::SET) + aPos = static_cast<const SfxPointItem *>(pItem)->GetValue(); + if(pArgs->GetItemState(FN_PARAM_2, false, &pItem) == SfxItemState::SET) + aSize = static_cast<const SvxSizeItem *>(pItem)->GetSize(); + if(pArgs->GetItemState(SID_ATTR_COLUMNS, false, &pItem) == SfxItemState::SET) + { + const sal_uInt16 nCols = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + if( !bSingleCol && 1 < nCols ) + { + SwFormatCol aFormatCol; + aFormatCol.Init( nCols , (rReq.IsAPI() ? 0 + : DEF_GUTTER_WIDTH), USHRT_MAX ); + aMgr.SetCol(aFormatCol); + } + } + + GetShell().LockPaint(); + GetShell().StartAllAction(); + + aMgr.InsertFlyFrame(eAnchor, aPos, aSize); + + GetShell().EndAllAction(); + GetShell().UnlockPaint(); + } + else + { + SfxItemSet aSet = CreateInsertFrameItemSet(aMgr); + + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebDocShell*>( GetView().GetDocShell()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateFrameTabDialog("FrameDialog", + GetView().GetViewFrame(), + GetView().GetFrameWeld(), + aSet)); + if(pDlg->Execute() == RET_OK && pDlg->GetOutputItemSet()) + { + //local variable necessary at least after call of .AutoCaption() because this could be deleted at this point + SwWrtShell& rShell = GetShell(); + rShell.LockPaint(); + rShell.StartAllAction(); + rShell.StartUndo(SwUndoId::INSERT); + + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + aMgr.SetAttrSet(*pOutSet); + + // At first delete the selection at the ClickToEditField. + if( rShell.IsInClickToEdit() ) + rShell.DelRight(); + + aMgr.InsertFlyFrame(); + + uno::Reference< frame::XDispatchRecorder > xRecorder = + GetView().GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + //FN_INSERT_FRAME + sal_uInt16 nAnchor = static_cast<sal_uInt16>(aMgr.GetAnchor()); + rReq.AppendItem(SfxUInt16Item(nSlot, nAnchor)); + rReq.AppendItem(SfxPointItem(FN_PARAM_1, rShell.GetObjAbsPos())); + rReq.AppendItem(SvxSizeItem(FN_PARAM_2, rShell.GetObjSize())); + rReq.Done(); + } + + GetView().AutoCaption(FRAME_CAP); + + { + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, SwResId(STR_FRAME)); + + rShell.EndUndo(SwUndoId::INSERT, &aRewriter); + } + rShell.EndAllAction(); + rShell.UnlockPaint(); + } + } + break; + } + case FN_FORMAT_COLUMN : + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + VclPtr<VclAbstractDialog> pColDlg(pFact->CreateSwColumnDialog(GetView().GetFrameWeld(), rSh)); + pColDlg->StartExecuteAsync([=](sal_Int32 /*nResult*/){ + pColDlg->disposeOnce(); + }); + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +static bool lcl_IsMarkInSameSection( SwWrtShell& rWrtSh, const SwSection* pSect ) +{ + rWrtSh.SwapPam(); + bool bRet = pSect == rWrtSh.GetCurrSection(); + rWrtSh.SwapPam(); + return bRet; +} + +void SwTextShell::StateInsert( SfxItemSet &rSet ) +{ + const bool bHtmlModeOn = ::GetHtmlMode(GetView().GetDocShell()) & HTMLMODE_ON; + SfxWhichIter aIter( rSet ); + SwWrtShell &rSh = GetShell(); + sal_uInt16 nWhich = aIter.FirstWhich(); + SvtModuleOptions aMOpt; + SfxObjectCreateMode eCreateMode = + GetView().GetDocShell()->GetCreateMode(); + + bool bCursorInHidden = false; + if( !rSh.HasMark() ) + { + rSh.Push(); + bCursorInHidden = rSh.SelectHiddenRange(); + rSh.Pop(); + } + + while ( nWhich ) + { + switch ( nWhich ) + { + case SID_INSERT_AVMEDIA: + if ( GetShell().IsSelFrameMode() + || GetShell().CursorInsideInputField() + || SfxObjectCreateMode::EMBEDDED == eCreateMode + || bCursorInHidden ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_INSERT_DIAGRAM: + if( !aMOpt.IsChart() + || GetShell().CursorInsideInputField() + || eCreateMode == SfxObjectCreateMode::EMBEDDED + || bCursorInHidden ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_INSERT_SMA: + if( !aMOpt.IsMath() + || eCreateMode == SfxObjectCreateMode::EMBEDDED + || bCursorInHidden + || rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_INSERT_FLOATINGFRAME: + case SID_INSERT_OBJECT: + { + if( eCreateMode == SfxObjectCreateMode::EMBEDDED || bCursorInHidden ) + { + rSet.DisableItem( nWhich ); + } + else if( GetShell().IsSelFrameMode() + || GetShell().CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + else if(SID_INSERT_FLOATINGFRAME == nWhich && bHtmlModeOn) + { + SvxHtmlOptions& rHtmlOpt = SvxHtmlOptions::Get(); + const sal_uInt16 nExport = rHtmlOpt.GetExportMode(); + if(HTML_CFG_MSIE != nExport && HTML_CFG_WRITER != nExport ) + rSet.DisableItem(nWhich); + } + } + break; + + case FN_INSERT_FRAME_INTERACT_NOCOL : + case FN_INSERT_FRAME_INTERACT: + { + if( GetShell().IsSelFrameMode() + || rSh.IsTableMode() + || GetShell().CursorInsideInputField() + || bCursorInHidden ) + rSet.DisableItem(nWhich); + } + break; + + case SID_HYPERLINK_GETLINK: + { + SfxItemSet aSet(GetPool(), svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr( aSet ); + + SvxHyperlinkItem aHLinkItem; + const SfxPoolItem* pItem; + if(SfxItemState::SET == aSet.GetItemState(RES_TXTATR_INETFMT, false, &pItem)) + { + const SwFormatINetFormat* pINetFormat = static_cast<const SwFormatINetFormat*>(pItem); + aHLinkItem.SetURL(pINetFormat->GetValue()); + aHLinkItem.SetTargetFrame(pINetFormat->GetTargetFrame()); + aHLinkItem.SetIntName(pINetFormat->GetName()); + const SvxMacro *pMacro = pINetFormat->GetMacro( SvMacroItemId::OnMouseOver ); + if( pMacro ) + aHLinkItem.SetMacro(HyperDialogEvent::MouseOverObject, *pMacro); + + pMacro = pINetFormat->GetMacro( SvMacroItemId::OnClick ); + if( pMacro ) + aHLinkItem.SetMacro(HyperDialogEvent::MouseClickObject, *pMacro); + + pMacro = pINetFormat->GetMacro( SvMacroItemId::OnMouseOut ); + if( pMacro ) + aHLinkItem.SetMacro(HyperDialogEvent::MouseOutObject, *pMacro); + + // Get the text of the Link. + rSh.StartAction(); + const bool bAtEnd(rSh.IsCursorPtAtEnd()); + if(!bAtEnd) // tdf#91832: ensure forward selection + rSh.SwapPam(); + rSh.CreateCursor(); + if(!bAtEnd) + rSh.SwapPam(); + rSh.SwCursorShell::SelectTextAttr(RES_TXTATR_INETFMT,true); + OUString sLinkName = rSh.GetSelText(); + aHLinkItem.SetName(sLinkName); + aHLinkItem.SetInsertMode(HLINK_FIELD); + rSh.DestroyCursor(); + rSh.EndAction(); + } + else + { + OUString sReturn = rSh.GetSelText(); + sReturn = sReturn.copy(0, std::min<sal_Int32>(255, sReturn.getLength())); + aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' ')); + } + + aHLinkItem.SetInsertMode(static_cast<SvxLinkInsertMode>(aHLinkItem.GetInsertMode() | + (bHtmlModeOn ? HLINK_HTMLMODE : 0))); + aHLinkItem.SetMacroEvents ( HyperDialogEvent::MouseOverObject| + HyperDialogEvent::MouseClickObject | HyperDialogEvent::MouseOutObject ); + + rSet.Put(aHLinkItem); + } + break; + + case FN_INSERT_FRAME: + if (rSh.IsSelFrameMode() ) + { + const SelectionType nSel = rSh.GetSelectionType(); + if( ((SelectionType::Graphic | SelectionType::Ole ) & nSel ) || bCursorInHidden ) + rSet.DisableItem(nWhich); + } + else if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + break; + + case FN_FORMAT_COLUMN : + { + //#i80458# column dialog cannot work if the selection contains different page styles and different sections + bool bDisable = true; + if( rSh.GetFlyFrameFormat() || rSh.GetSelectedPageDescs() ) + bDisable = false; + if( bDisable ) + { + const SwSection* pCurrSection = rSh.GetCurrSection(); + const sal_uInt16 nFullSectCnt = rSh.GetFullSelectedSectionCount(); + if( pCurrSection && ( !rSh.HasSelection() || 0 != nFullSectCnt )) + bDisable = false; + else if( + rSh.HasSelection() && rSh.IsInsRegionAvailable() && + ( !pCurrSection || ( 1 != nFullSectCnt && + lcl_IsMarkInSameSection( rSh, pCurrSection ) ))) + bDisable = false; + } + if(bDisable) + rSet.DisableItem(nWhich); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SwTextShell::ExecDelete(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + switch( rReq.GetSlot() ) + { + case FN_DELETE_SENT: + if( rSh.IsTableMode() ) + { + rSh.DeleteRow(); + rSh.EnterStdMode(); + } + else + rSh.DelToEndOfSentence(); + break; + case FN_DELETE_BACK_SENT: + rSh.DelToStartOfSentence(); + break; + case FN_DELETE_WORD: + rSh.DelNxtWord(); + break; + case FN_DELETE_BACK_WORD: + rSh.DelPrvWord(); + break; + case FN_DELETE_LINE: + rSh.DelToEndOfLine(); + break; + case FN_DELETE_BACK_LINE: + rSh.DelToStartOfLine(); + break; + case FN_DELETE_PARA: + rSh.DelToEndOfPara(); + break; + case FN_DELETE_BACK_PARA: + rSh.DelToStartOfPara(); + break; + case FN_DELETE_WHOLE_LINE: + rSh.DelLine(); + break; + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } + rReq.Done(); +} + +void SwTextShell::ExecTransliteration( SfxRequest const & rReq ) +{ + using namespace ::com::sun::star::i18n; + TransliterationFlags nMode = TransliterationFlags::NONE; + + switch( rReq.GetSlot() ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nMode = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nMode = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nMode = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nMode = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nMode = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + + case SID_TRANSLITERATE_HALFWIDTH: + nMode = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nMode = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + + case SID_TRANSLITERATE_HIRAGANA: + nMode = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nMode = TransliterationFlags::HIRAGANA_KATAKANA; + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + } + + if( nMode != TransliterationFlags::NONE ) + GetShell().TransliterateText( nMode ); +} + +void SwTextShell::ExecRotateTransliteration( SfxRequest const & rReq ) +{ + if( rReq.GetSlot() == SID_TRANSLITERATE_ROTATE_CASE ) + GetShell().TransliterateText( m_aRotateCase.getNextMode() ); +} + +SwTextShell::SwTextShell(SwView &_rView) : + SwBaseShell(_rView) +{ + SetName("Text"); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Text)); +} + +SwTextShell::~SwTextShell() +{ +} + +SfxItemSet SwTextShell::CreateInsertFrameItemSet(SwFlyFrameAttrMgr& rMgr) +{ + static const sal_uInt16 aFrameAttrRange[] = + { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + FN_GET_PRINT_AREA, FN_GET_PRINT_AREA, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + FN_SET_FRM_NAME, FN_SET_FRM_NAME, + SID_HTML_MODE, SID_HTML_MODE, + SID_COLOR_TABLE, SID_PATTERN_LIST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, // tdf#95003 + 0 + }; + + SfxItemSet aSet(GetPool(), aFrameAttrRange ); + aSet.Put(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(GetView().GetDocShell()))); + + // For the Area tab page. + GetShell().GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->PutAreaListItems(aSet); + + const SwRect &rPg = GetShell().GetAnyCurRect(CurRectType::Page); + SwFormatFrameSize aFrameSize(SwFrameSize::Variable, rPg.Width(), rPg.Height()); + aFrameSize.SetWhich(GetPool().GetWhich(SID_ATTR_PAGE_SIZE)); + aSet.Put(aFrameSize); + + const SwRect &rPr = GetShell().GetAnyCurRect(CurRectType::PagePrt); + SwFormatFrameSize aPrtSize(SwFrameSize::Variable, rPr.Width(), rPr.Height()); + aPrtSize.SetWhich(GetPool().GetWhich(FN_GET_PRINT_AREA)); + aSet.Put(aPrtSize); + + aSet.Put(rMgr.GetAttrSet()); + aSet.SetParent( rMgr.GetAttrSet().GetParent() ); + + // Delete minimum size in columns. + SvxBoxInfoItem aBoxInfo(aSet.Get(SID_ATTR_BORDER_INNER)); + const SvxBoxItem& rBox = aSet.Get(RES_BOX); + aBoxInfo.SetMinDist(false); + aBoxInfo.SetDefDist(rBox.GetDistance(SvxBoxItemLine::LEFT)); + aSet.Put(aBoxInfo); + + return aSet; +} + +void SwTextShell::InsertSymbol( SfxRequest& rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if( pArgs ) + pArgs->GetItemState(GetPool().GetWhich(SID_CHARMAP), false, &pItem); + + OUString aChars, aFontName; + if ( pItem ) + { + aChars = static_cast<const SfxStringItem*>(pItem)->GetValue(); + const SfxPoolItem* pFtItem = nullptr; + pArgs->GetItemState( GetPool().GetWhich(SID_ATTR_SPECIALCHAR), false, &pFtItem); + const SfxStringItem* pFontItem = dynamic_cast<const SfxStringItem*>( pFtItem ); + if ( pFontItem ) + aFontName = pFontItem->GetValue(); + } + + SwWrtShell &rSh = GetShell(); + SfxItemSet aSet( GetPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONT>{} ); + rSh.GetCurAttr( aSet ); + SvtScriptType nScript = rSh.GetScriptType(); + + std::shared_ptr<SvxFontItem> aFont(std::make_shared<SvxFontItem>(RES_CHRATR_FONT)); + { + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, *aSet.GetPool() ); + aSetItem.GetItemSet().Put( aSet, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript ); + if( pI ) + { + aFont.reset(static_cast<SvxFontItem*>(pI->Clone())); + } + else + { + aFont.reset(static_cast<SvxFontItem*>( + aSet.Get( + GetWhichOfScript( + RES_CHRATR_FONT, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ) )).Clone())); + } + + if (aFontName.isEmpty()) + aFontName = aFont->GetFamilyName(); + } + + vcl::Font aNewFont(aFontName, Size(1,1)); // Size only because CTOR. + if( aChars.isEmpty() ) + { + // Set selected font as default. + SfxAllItemSet aAllSet( rSh.GetAttrPool() ); + aAllSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + SwViewOption aOpt(*GetShell().GetViewOptions()); + const OUString& sSymbolFont = aOpt.GetSymbolFont(); + if( aFontName.isEmpty() && !sSymbolFont.isEmpty() ) + aAllSet.Put( SfxStringItem( SID_FONT_NAME, sSymbolFont ) ); + else + aAllSet.Put( SfxStringItem( SID_FONT_NAME, aFont->GetFamilyName() ) ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + auto xFrame = GetView().GetViewFrame()->GetFrame().GetFrameInterface(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(GetView().GetFrameWeld(), aAllSet, xFrame)); + pDlg->Execute(); + return; + } + + if( !aChars.isEmpty() ) + { + rSh.StartAllAction(); + + // Delete selected content. + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_SPECIALCHAR)); + + rSh.StartUndo( SwUndoId::INSERT, &aRewriter ); + if ( rSh.HasSelection() ) + { + rSh.DelRight(); + aSet.ClearItem(); + rSh.GetCurAttr( aSet ); + + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, *aSet.GetPool() ); + aSetItem.GetItemSet().Put( aSet, false ); + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript ); + if( pI ) + { + aFont.reset(static_cast<SvxFontItem*>(pI->Clone())); + } + else + { + aFont.reset(static_cast<SvxFontItem*>(aSet.Get( GetWhichOfScript( + RES_CHRATR_FONT, + SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ) )).Clone())); + } + } + + // Insert character. + rSh.Insert( aChars ); + + // #108876# a font attribute has to be set always due to a guessed script type + if( !aNewFont.GetFamilyName().isEmpty() ) + { + std::unique_ptr<SvxFontItem> aNewFontItem(aFont->Clone()); + aNewFontItem->SetFamilyName( aNewFont.GetFamilyName() ); + aNewFontItem->SetFamily( aNewFont.GetFamilyType()); + aNewFontItem->SetPitch( aNewFont.GetPitch()); + aNewFontItem->SetCharSet( aNewFont.GetCharSet() ); + + SfxItemSet aRestoreSet( GetPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT, + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONT>{} ); + + nScript = g_pBreakIt->GetAllScriptsOfText( aChars ); + if( SvtScriptType::LATIN & nScript ) + { + aRestoreSet.Put( aSet.Get( RES_CHRATR_FONT ) ); + aNewFontItem->SetWhich(RES_CHRATR_FONT); + aSet.Put( *aNewFontItem ); + } + if( SvtScriptType::ASIAN & nScript ) + { + aRestoreSet.Put( aSet.Get( RES_CHRATR_CJK_FONT ) ); + aNewFontItem->SetWhich(RES_CHRATR_CJK_FONT); + aSet.Put( *aNewFontItem ); + } + if( SvtScriptType::COMPLEX & nScript ) + { + aRestoreSet.Put( aSet.Get( RES_CHRATR_CTL_FONT ) ); + aNewFontItem->SetWhich(RES_CHRATR_CTL_FONT); + aSet.Put( *aNewFontItem ); + } + + rSh.SetMark(); + rSh.ExtendSelection( false, aChars.getLength() ); + rSh.SetAttrSet( aSet, SetAttrMode::DONTEXPAND | SetAttrMode::NOFORMATATTR ); + if( !rSh.IsCursorPtAtEnd() ) + rSh.SwapPam(); + + rSh.ClearMark(); + + // #i75891# + // SETATTR_DONTEXPAND does not work if there are already hard attributes. + // Therefore we have to restore the font attributes. + rSh.SetMark(); + rSh.SetAttrSet( aRestoreSet ); + rSh.ClearMark(); + + rSh.UpdateAttr(); + + // Why was this done? aFont is not used anymore below, we are not + // in a loop and it's a local variable...? + // aFont = aNewFontItem; + } + + rSh.EndAllAction(); + rSh.EndUndo(); + + if ( !aChars.isEmpty() ) + { + rReq.AppendItem( SfxStringItem( GetPool().GetWhich(SID_CHARMAP), aChars ) ); + rReq.AppendItem( SfxStringItem( SID_ATTR_SPECIALCHAR, aNewFont.GetFamilyName() ) ); + rReq.Done(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx new file mode 100644 index 000000000..4342ed713 --- /dev/null +++ b/sw/source/uibase/shells/textsh1.cxx @@ -0,0 +1,2179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/WordType.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> + +#include <hintids.hxx> +#include <cmdid.h> +#include <comphelper/lok.hxx> + +#include <i18nutil/unicode.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <svtools/langtab.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <sfx2/htmlmode.hxx> +#include <svl/whiter.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/unohelp2.hxx> +#include <vcl/weld.hxx> +#include <sfx2/request.hxx> +#include <svl/eitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/svxacorr.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <charfmt.hxx> +#include <svx/SmartTagItem.hxx> +#include <fmtinfmt.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> +#include <swmodule.hxx> +#include <viewopt.hxx> +#include <uitool.hxx> +#include <textsh.hxx> +#include <IMark.hxx> +#include <swdtflvr.hxx> +#include <swundo.hxx> +#include <reffld.hxx> +#include <docsh.hxx> +#include <inputwin.hxx> +#include <chrdlgmodes.hxx> +#include <fmtcol.hxx> +#include <cellatr.hxx> +#include <edtwin.hxx> +#include <fldmgr.hxx> +#include <strings.hrc> +#include <paratr.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/app.hxx> +#include <breakit.hxx> +#include <SwSmartTagMgr.hxx> +#include <editeng/acorrcfg.hxx> +#include <swabstdlg.hxx> +#include <sfx2/sfxdlg.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/linguistic2/ProofreadingResult.hpp> +#include <com/sun/star/linguistic2/XDictionary.hpp> +#include <com/sun/star/linguistic2/XSpellAlternatives.hpp> +#include <editeng/unolingu.hxx> +#include <doc.hxx> +#include <drawdoc.hxx> +#include <view.hxx> +#include <pam.hxx> +#include <sfx2/objface.hxx> +#include <langhelper.hxx> +#include <uiitems.hxx> +#include <svx/nbdtmgfact.hxx> +#include <svx/nbdtmg.hxx> +#include <SwRewriter.hxx> +#include <svx/drawitem.hxx> +#include <numrule.hxx> +#include <memory> +#include <xmloff/odffields.hxx> +#include <bookmrk.hxx> +#include <linguistic/misc.hxx> +#include <editeng/splwrap.hxx> + +using namespace ::com::sun::star; +using namespace com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace com::sun::star::style; +using namespace svx::sidebar; + +static void sw_CharDialogResult(const SfxItemSet* pSet, SwWrtShell &rWrtSh, std::shared_ptr<SfxItemSet> const & pCoreSet, bool bSel, bool bSelectionPut, SfxRequest *pReq); + +static void sw_CharDialog(SwWrtShell &rWrtSh, bool bUseDialog, sal_uInt16 nSlot, const SfxItemSet *pArgs, SfxRequest *pReq ) +{ + FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &rWrtSh.GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + auto pCoreSet = std::make_shared<SfxItemSet>( + rWrtSh.GetView().GetPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_INETFMT, RES_TXTATR_INETFMT, + RES_BACKGROUND, RES_SHADOW, + XATTR_FILLSTYLE, XATTR_FILLCOLOR, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_HTML_MODE, SID_HTML_MODE, + SID_ATTR_CHAR_WIDTH_FIT_TO_LINE, SID_ATTR_CHAR_WIDTH_FIT_TO_LINE, + FN_PARAM_SELECTION, FN_PARAM_SELECTION>{}); + rWrtSh.GetCurAttr(*pCoreSet); + + bool bSel = rWrtSh.HasSelection(); + bool bSelectionPut = false; + if(bSel || rWrtSh.IsInWord()) + { + if(!bSel) + { + rWrtSh.StartAction(); + rWrtSh.Push(); + if(!rWrtSh.SelectTextAttr( RES_TXTATR_INETFMT )) + rWrtSh.SelWrd(); + } + pCoreSet->Put(SfxStringItem(FN_PARAM_SELECTION, rWrtSh.GetSelText())); + bSelectionPut = true; + if(!bSel) + { + rWrtSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rWrtSh.EndAction(); + } + } + pCoreSet->Put(SfxUInt16Item(SID_ATTR_CHAR_WIDTH_FIT_TO_LINE, rWrtSh.GetScalingOfSelectedText())); + + ::ConvertAttrCharToGen(*pCoreSet); + + // Setting the BoxInfo + ::PrepareBoxInfo(*pCoreSet, rWrtSh); + + pCoreSet->Put(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(rWrtSh.GetView().GetDocShell()))); + VclPtr<SfxAbstractTabDialog> pDlg; + if ( bUseDialog && GetActiveView() ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateSwCharDlg(rWrtSh.GetView().GetFrameWeld(), rWrtSh.GetView(), *pCoreSet, SwCharDlgMode::Std)); + + if (nSlot == FN_INSERT_HYPERLINK) + pDlg->SetCurPageId("hyperlink"); + else if (nSlot == SID_CHAR_DLG_EFFECT) + pDlg->SetCurPageId("fonteffects"); + else if (nSlot == SID_CHAR_DLG_FOR_PARAGRAPH) + pDlg->SetCurPageId("font"); + else if (pReq) + { + const SfxStringItem* pItem = (*pReq).GetArg<SfxStringItem>(FN_PARAM_1); + if (pItem) + pDlg->SetCurPageId(OUStringToOString(pItem->GetValue(), RTL_TEXTENCODING_UTF8)); + } + } + + if (bUseDialog) + { + std::shared_ptr<SfxRequest> pRequest; + if (pReq) + { + pRequest = std::make_shared<SfxRequest>(*pReq); + pReq->Ignore(); // the 'old' request is not relevant any more + } + pDlg->StartExecuteAsync([pDlg, &rWrtSh, pCoreSet, bSel, bSelectionPut, pRequest](sal_Int32 nResult){ + if (nResult == RET_OK) + { + sw_CharDialogResult(pDlg->GetOutputItemSet(), rWrtSh, pCoreSet, bSel, bSelectionPut, pRequest.get()); + } + pDlg->disposeOnce(); + }); + } + else if (pArgs) + { + sw_CharDialogResult(pArgs, rWrtSh, pCoreSet, bSel, bSelectionPut, pReq); + } +} + +static void sw_CharDialogResult(const SfxItemSet* pSet, SwWrtShell &rWrtSh, std::shared_ptr<SfxItemSet> const & pCoreSet, bool bSel, bool bSelectionPut, SfxRequest *pReq) +{ + SfxItemSet aTmpSet( *pSet ); + ::ConvertAttrGenToChar(aTmpSet, *pCoreSet); + + const SfxPoolItem* pSelectionItem; + bool bInsert = false; + sal_Int32 nInsert = 0; + + // The old item is for unknown reasons back in the set again. + if( !bSelectionPut && SfxItemState::SET == aTmpSet.GetItemState(FN_PARAM_SELECTION, false, &pSelectionItem) ) + { + OUString sInsert = static_cast<const SfxStringItem*>(pSelectionItem)->GetValue(); + bInsert = !sInsert.isEmpty(); + if(bInsert) + { + nInsert = sInsert.getLength(); + rWrtSh.StartAction(); + rWrtSh.Insert( sInsert ); + rWrtSh.SetMark(); + rWrtSh.ExtendSelection(false, sInsert.getLength()); + SfxRequest aReq( rWrtSh.GetView().GetViewFrame(), FN_INSERT_STRING ); + aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, sInsert ) ); + aReq.Done(); + SfxRequest aReq1( rWrtSh.GetView().GetViewFrame(), FN_CHAR_LEFT ); + aReq1.AppendItem( SfxInt32Item(FN_PARAM_MOVE_COUNT, nInsert) ); + aReq1.AppendItem( SfxBoolItem(FN_PARAM_MOVE_SELECTION, true) ); + aReq1.Done(); + } + } + aTmpSet.ClearItem(FN_PARAM_SELECTION); + + SwTextFormatColl* pColl = rWrtSh.GetCurTextFormatColl(); + if(bSel && rWrtSh.IsSelFullPara() && pColl && pColl->IsAutoUpdateFormat()) + { + rWrtSh.AutoUpdatePara(pColl, aTmpSet); + } + else + rWrtSh.SetAttrSet( aTmpSet ); + if (pReq) + pReq->Done(aTmpSet); + if(bInsert) + { + SfxRequest aReq1( rWrtSh.GetView().GetViewFrame(), FN_CHAR_RIGHT ); + aReq1.AppendItem( SfxInt32Item(FN_PARAM_MOVE_COUNT, nInsert) ); + aReq1.AppendItem( SfxBoolItem(FN_PARAM_MOVE_SELECTION, false) ); + aReq1.Done(); + rWrtSh.SwapPam(); + rWrtSh.ClearMark(); + rWrtSh.DontExpandFormat(); + rWrtSh.EndAction(); + } + +} + +static short lcl_AskRedlineFlags(weld::Window *pWin) +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pWin, "modules/swriter/ui/queryredlinedialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryRedlineDialog")); + return xQBox->run(); +} + +static void sw_ParagraphDialogResult(SfxItemSet* pSet, SwWrtShell &rWrtSh, SfxRequest& rReq, SwPaM* pPaM) +{ + if (!pSet) + return; + + rReq.Done( *pSet ); + ::SfxToSwPageDescAttr( rWrtSh, *pSet ); + // #i56253# + // enclose all undos. + // Thus, check conditions, if actions will be performed. + const bool bUndoNeeded( pSet->Count() || + SfxItemState::SET == pSet->GetItemState(FN_NUMBER_NEWSTART) || + SfxItemState::SET == pSet->GetItemState(FN_NUMBER_NEWSTART_AT) ); + if ( bUndoNeeded ) + { + rWrtSh.StartUndo( SwUndoId::INSATTR ); + } + if( pSet->Count() ) + { + rWrtSh.StartAction(); + const SfxPoolItem* pItem = nullptr; + if ( SfxItemState::SET == pSet->GetItemState(FN_DROP_TEXT, false, &pItem) ) + { + if ( !static_cast<const SfxStringItem*>(pItem)->GetValue().isEmpty() ) + rWrtSh.ReplaceDropText(static_cast<const SfxStringItem*>(pItem)->GetValue(), pPaM); + } + rWrtSh.SetAttrSet(*pSet, SetAttrMode::DEFAULT, pPaM); + rWrtSh.EndAction(); + SwTextFormatColl* pColl = rWrtSh.GetPaMTextFormatColl(pPaM); + if(pColl && pColl->IsAutoUpdateFormat()) + { + rWrtSh.AutoUpdatePara(pColl, *pSet, pPaM); + } + } + + if( SfxItemState::SET == pSet->GetItemState(FN_NUMBER_NEWSTART) ) + { + //SetNumRuleStart(true) restarts the numbering at the value + //that is defined at the starting point of the numbering level + //otherwise the SetNodeNumStart() value determines the start + //if it's set to something different than USHRT_MAX + + bool bStart = static_cast<const SfxBoolItem&>(pSet->Get(FN_NUMBER_NEWSTART)).GetValue(); + + // Default value for restart value has to be USHRT_MAX + // in order to indicate that the restart value of the list + // style has to be used on restart. + sal_uInt16 nNumStart = USHRT_MAX; + if( SfxItemState::SET == pSet->GetItemState(FN_NUMBER_NEWSTART_AT) ) + { + nNumStart = static_cast<const SfxUInt16Item&>(pSet->Get(FN_NUMBER_NEWSTART_AT)).GetValue(); + } + rWrtSh.SetNumRuleStart(bStart, pPaM); + rWrtSh.SetNodeNumStart(nNumStart); + } + else if( SfxItemState::SET == pSet->GetItemState(FN_NUMBER_NEWSTART_AT) ) + { + rWrtSh.SetNodeNumStart(static_cast<const SfxUInt16Item&>(pSet->Get(FN_NUMBER_NEWSTART_AT)).GetValue()); + rWrtSh.SetNumRuleStart(false, pPaM); + } + // #i56253# + if ( bUndoNeeded ) + { + rWrtSh.EndUndo( SwUndoId::INSATTR ); + } +} + +void SwTextShell::Execute(SfxRequest &rReq) +{ + bool bUseDialog = true; + const SfxItemSet *pArgs = rReq.GetArgs(); + SwWrtShell& rWrtSh = GetShell(); + const SfxPoolItem* pItem = nullptr; + const sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + switch( nSlot ) + { + case SID_UNICODE_NOTATION_TOGGLE: + { + long nMaxUnits = 256; + sal_Int32 nSelLength = rWrtSh.GetSelText().getLength(); + if( rWrtSh.IsSelection() && !rWrtSh.IsMultiSelection() && (nSelLength < nMaxUnits) ) + nMaxUnits = nSelLength; + + long index = 0; + ToggleUnicodeCodepoint aToggle; + while( nMaxUnits-- && aToggle.AllowMoreInput(rWrtSh.GetChar(true, index-1)) ) + --index; + + OUString sReplacement = aToggle.ReplacementString(); + if( !sReplacement.isEmpty() ) + { + if (rWrtSh.HasReadonlySel() && !rWrtSh.CursorInsideInputField()) + { + // Only break if there's something to do; don't nag with the dialog otherwise + auto xInfo(std::make_unique<weld::GenericDialogController>( + rWrtSh.GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", + "InfoReadonlyDialog")); + xInfo->run(); + break; + } + SwRewriter aRewriter; + aRewriter.AddRule( UndoArg1, aToggle.StringToReplace() ); + aRewriter.AddRule( UndoArg2, SwResId(STR_YIELDS) ); + aRewriter.AddRule( UndoArg3, sReplacement ); + rWrtSh.StartUndo(SwUndoId::REPLACE, &aRewriter); + rWrtSh.GetCursor()->Normalize(false); + + rWrtSh.ClearMark(); + if( rWrtSh.IsInSelect() ) // cancel any in-progress keyboard selection as well + rWrtSh.EndSelect(); + + for( sal_uInt32 i=aToggle.CharsToDelete(); i > 0; --i ) + rWrtSh.DelLeft(); + rWrtSh.Insert2( sReplacement ); + rWrtSh.EndUndo(SwUndoId::REPLACE, &aRewriter); + } + } + break; + + case SID_LANGUAGE_STATUS: + { + // get the language + OUString aNewLangText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(SID_LANGUAGE_STATUS); + if (pItem2) + aNewLangText = pItem2->GetValue(); + + //!! Remember the view frame right now... + //!! (call to GetView().GetViewFrame() will break if the + //!! SwTextShell got destroyed meanwhile.) + SfxViewFrame *pViewFrame = GetView().GetViewFrame(); + + if (aNewLangText == "*") + { + // open the dialog "Tools/Options/Language Settings - Language" + // to set the documents default language + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog(GetView().GetFrameWeld(), SID_LANGUAGE_OPTIONS)); + pDlg->Execute(); + } + else + { + //!! We have to use StartAction / EndAction bracketing in + //!! order to prevent possible destruction of the SwTextShell + //!! due to the selection changes coming below. + rWrtSh.StartAction(); + // prevent view from jumping because of (temporary) selection changes + rWrtSh.LockView( true ); + + // setting the new language... + if (!aNewLangText.isEmpty()) + { + const OUString aSelectionLangPrefix("Current_"); + const OUString aParagraphLangPrefix("Paragraph_"); + const OUString aDocumentLangPrefix("Default_"); + const OUString aStrNone("LANGUAGE_NONE"); + const OUString aStrResetLangs("RESET_LANGUAGES"); + + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE>{} ); + + sal_Int32 nPos = 0; + bool bForSelection = true; + bool bForParagraph = false; + if (-1 != (nPos = aNewLangText.indexOf( aSelectionLangPrefix ))) + { + // ... for the current selection + aNewLangText = aNewLangText.replaceAt(nPos, aSelectionLangPrefix.getLength(), ""); + bForSelection = true; + } + else if (-1 != (nPos = aNewLangText.indexOf(aParagraphLangPrefix))) + { + // ... for the current paragraph language + aNewLangText = aNewLangText.replaceAt(nPos, aParagraphLangPrefix.getLength(), ""); + bForSelection = true; + bForParagraph = true; + } + else if (-1 != (nPos = aNewLangText.indexOf(aDocumentLangPrefix))) + { + // ... as default document language + aNewLangText = aNewLangText.replaceAt(nPos, aDocumentLangPrefix.getLength(), ""); + bForSelection = false; + } + + if (bForParagraph || !bForSelection) + { + rWrtSh.Push(); // save selection for later restoration + rWrtSh.ClearMark(); // fdo#67796: invalidate table crsr + } + + if (bForParagraph) + SwLangHelper::SelectCurrentPara( rWrtSh ); + + if (!bForSelection) // document language to be changed... + { + rWrtSh.SelAll(); + rWrtSh.ExtendedSelectAll(); + } + + rWrtSh.StartUndo( ( !bForParagraph && !bForSelection ) ? SwUndoId::SETDEFTATTR : SwUndoId::EMPTY ); + if (aNewLangText == aStrNone) + SwLangHelper::SetLanguage_None( rWrtSh, bForSelection, aCoreSet ); + else if (aNewLangText == aStrResetLangs) + SwLangHelper::ResetLanguages( rWrtSh ); + else + SwLangHelper::SetLanguage( rWrtSh, aNewLangText, bForSelection, aCoreSet ); + rWrtSh.EndUndo(); + + if (bForParagraph || !bForSelection) + { + rWrtSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // restore selection... + } + } + + rWrtSh.LockView( false ); + rWrtSh.EndAction(); + } + + // invalidate slot to get the new language displayed + pViewFrame->GetBindings().Invalidate( nSlot ); + + rReq.Done(); + break; + } + + case SID_THES: + { + // replace word/selection with text from selected sub menu entry + OUString aReplaceText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(SID_THES); + if (pItem2) + aReplaceText = pItem2->GetValue(); + if (!aReplaceText.isEmpty()) + { + SwView &rView2 = rWrtSh.GetView(); + const bool bSelection = rWrtSh.HasSelection(); + const OUString aLookUpText = rView2.GetThesaurusLookUpText( bSelection ); + rView2.InsertThesaurusSynonym( aReplaceText, aLookUpText, bSelection ); + } + } + break; + + case SID_CHARMAP: + { + InsertSymbol( rReq ); + } + break; + case FN_INSERT_FOOTNOTE: + case FN_INSERT_ENDNOTE: + { + OUString aStr; + const SfxStringItem* pFont = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(nSlot); + if ( pNameItem ) + aStr = pNameItem->GetValue(); + bool bFont = pFont && !pFont->GetValue().isEmpty(); + rWrtSh.StartUndo( SwUndoId::UI_INSERT_FOOTNOTE ); + rWrtSh.InsertFootnote( aStr, nSlot == FN_INSERT_ENDNOTE, !bFont ); + if ( bFont ) + { + rWrtSh.Left( CRSR_SKIP_CHARS, true, 1, false ); + SfxItemSet aSet( rWrtSh.GetAttrPool(), svl::Items<RES_CHRATR_FONT, RES_CHRATR_FONT>{} ); + rWrtSh.GetCurAttr( aSet ); + rWrtSh.SetAttrSet( aSet, SetAttrMode::DONTEXPAND ); + rWrtSh.ResetSelect(nullptr, false); + rWrtSh.EndSelect(); + rWrtSh.GotoFootnoteText(); + } + rWrtSh.EndUndo( SwUndoId::UI_INSERT_FOOTNOTE ); + rReq.Done(); + } + break; + case FN_INSERT_FOOTNOTE_DLG: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractInsFootNoteDlg> pDlg(pFact->CreateInsFootNoteDlg( + GetView().GetFrameWeld(), rWrtSh)); + pDlg->SetHelpId(GetStaticInterface()->GetSlot(nSlot)->GetCommand()); + if ( pDlg->Execute() == RET_OK ) + { + const sal_uInt16 nId = pDlg->IsEndNote() ? FN_INSERT_ENDNOTE : FN_INSERT_FOOTNOTE; + SfxRequest aReq( GetView().GetViewFrame(), nId ); + if ( !pDlg->GetStr().isEmpty() ) + aReq.AppendItem( SfxStringItem( nId, pDlg->GetStr() ) ); + if ( !pDlg->GetFontName().isEmpty() ) + aReq.AppendItem( SfxStringItem( FN_PARAM_1, pDlg->GetFontName() ) ); + ExecuteSlot( aReq ); + } + + rReq.Ignore(); + } + break; + case FN_FORMAT_FOOTNOTE_DLG: + case FN_FORMAT_CURRENT_FOOTNOTE_DLG: + { + GetView().ExecFormatFootnote(); + break; + } + case SID_INSERTDOC: + { + GetView().ExecuteInsertDoc( rReq, pItem ); + break; + } + case FN_FORMAT_RESET: + { + // #i78856, reset all attributes but not the language attributes + // (for this build an array of all relevant attributes and + // remove the languages from that) + std::set<sal_uInt16> aAttribs; + + sal_uInt16 aResetableSetRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_CHRATR_BEGIN, RES_CHRATR_LANGUAGE - 1, + RES_CHRATR_LANGUAGE + 1, RES_CHRATR_CJK_LANGUAGE - 1, + RES_CHRATR_CJK_LANGUAGE + 1, RES_CHRATR_CTL_LANGUAGE - 1, + RES_CHRATR_CTL_LANGUAGE + 1, RES_CHRATR_END-1, + RES_PARATR_BEGIN, RES_PARATR_END-1, + RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 + }; + const sal_uInt16 *pUShorts = aResetableSetRange; + while (*pUShorts) + { + for (sal_uInt16 i = pUShorts[0]; i <= pUShorts[1]; ++i) + aAttribs.insert( aAttribs.end(), i ); + pUShorts += 2; + } + // we don't want to change writing direction. + aAttribs.erase( RES_FRAMEDIR ); + rWrtSh.ResetAttr( aAttribs ); + + // also clear the direct formatting flag inside SwTableBox(es) + GetView().GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(nullptr, true); + + rReq.Done(); + break; + } + case FN_INSERT_BREAK_DLG: + { + sal_uInt16 nKind=0; + ::std::optional<sal_uInt16> oPageNumber; + OUString aTemplateName; + if ( pItem ) + { + nKind = static_cast<const SfxInt16Item*>(pItem)->GetValue(); + const SfxStringItem* pTemplate = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + const SfxUInt16Item* pNumber = rReq.GetArg<SfxUInt16Item>(FN_PARAM_2); + const SfxBoolItem* pIsNumberFilled = rReq.GetArg<SfxBoolItem>(FN_PARAM_3); + if ( pTemplate ) + aTemplateName = pTemplate->GetValue(); + if ( pNumber && pIsNumberFilled && pIsNumberFilled->GetValue() ) + oPageNumber = pNumber->GetValue(); + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwBreakDlg> pDlg(pFact->CreateSwBreakDlg(GetView().GetFrameWeld(), rWrtSh)); + if ( pDlg->Execute() == RET_OK ) + { + nKind = pDlg->GetKind(); + aTemplateName = pDlg->GetTemplateName(); + oPageNumber = pDlg->GetPageNumber(); + + bool bIsNumberFilled = false; + sal_uInt16 nPageNumber = 0; + + if (oPageNumber) + { + bIsNumberFilled = true; + nPageNumber = *oPageNumber; + } + + rReq.AppendItem( SfxInt16Item ( FN_INSERT_BREAK_DLG, nKind ) ); + rReq.AppendItem( SfxStringItem( FN_PARAM_1, aTemplateName ) ); + rReq.AppendItem( SfxUInt16Item( FN_PARAM_2, nPageNumber ) ); + rReq.AppendItem( SfxBoolItem ( FN_PARAM_3, bIsNumberFilled ) ); + rReq.Done(); + } + else + rReq.Ignore(); + } + + switch ( nKind ) + { + case 1 : + rWrtSh.InsertLineBreak(); break; + case 2 : + rWrtSh.InsertColumnBreak(); break; + case 3 : + { + rWrtSh.StartAllAction(); + if( !aTemplateName.isEmpty() ) + rWrtSh.InsertPageBreak( &aTemplateName, oPageNumber ); + else + rWrtSh.InsertPageBreak(); + rWrtSh.EndAllAction(); + } + } + + break; + } + case FN_INSERT_BOOKMARK: + { + if ( pItem ) + { + OUString sName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + rWrtSh.SetBookmark( vcl::KeyCode(), sName ); + } + else + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwInsertBookmarkDlg(GetView().GetFrameWeld(), rWrtSh, rReq)); + pDlg->Execute(); + } + + break; + } + case FN_DELETE_BOOKMARK: + { + if (pItem && !rWrtSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)) + { + IDocumentMarkAccess* const pMarkAccess = rWrtSh.getIDocumentMarkAccess(); + pMarkAccess->deleteMark( pMarkAccess->findMark(static_cast<const SfxStringItem*>(pItem)->GetValue()) ); + } + break; + } + case FN_SET_REMINDER: + { + // collect and sort navigator reminder names + IDocumentMarkAccess* const pMarkAccess = rWrtSh.getIDocumentMarkAccess(); + std::vector< OUString > vNavMarkNames; + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); + ppMark != pMarkAccess->getAllMarksEnd(); + ++ppMark) + { + if( IDocumentMarkAccess::GetType(**ppMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER ) + vNavMarkNames.push_back((*ppMark)->GetName()); + } + std::sort(vNavMarkNames.begin(), vNavMarkNames.end()); + + // we are maxed out so delete the first one + // this assumes that IDocumentMarkAccess generates Names in ascending order + if(vNavMarkNames.size() == MAX_MARKS) + pMarkAccess->deleteMark(pMarkAccess->findMark(vNavMarkNames[0])); + + rWrtSh.SetBookmark(vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER); + SwView::SetActMark(vNavMarkNames.size() < MAX_MARKS ? vNavMarkNames.size() : MAX_MARKS-1); + + break; + } + case FN_AUTOFORMAT_REDLINE_APPLY: + { + SvxSwAutoFormatFlags aFlags(SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags()); + // This must always be false for the postprocessing. + aFlags.bAFormatByInput = false; + aFlags.bWithRedlining = true; + rWrtSh.AutoFormat( &aFlags ); + aFlags.bWithRedlining = false; + + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if (pVFrame->HasChildWindow(FN_REDLINE_ACCEPT)) + pVFrame->ToggleChildWindow(FN_REDLINE_ACCEPT); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwModalRedlineAcceptDlg> xDlg(pFact->CreateSwModalRedlineAcceptDlg(GetView().GetEditWin().GetFrameWeld())); + + switch (lcl_AskRedlineFlags(GetView().GetFrameWeld())) + { + case RET_OK: + { + xDlg->AcceptAll(true); + SfxRequest aReq( pVFrame, FN_AUTOFORMAT_APPLY ); + aReq.Done(); + rReq.Ignore(); + break; + } + + case RET_CANCEL: + xDlg->AcceptAll(false); + rReq.Ignore(); + break; + + case 102: + xDlg->Execute(); + rReq.Done(); + break; + } + } + break; + + case FN_AUTOFORMAT_APPLY: + { + SvxSwAutoFormatFlags aFlags(SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags()); + // This must always be false for the postprocessing. + aFlags.bAFormatByInput = false; + rWrtSh.AutoFormat( &aFlags ); + rReq.Done(); + } + break; + case FN_AUTOFORMAT_AUTO: + { + SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get(); + bool bSet = pItem ? static_cast<const SfxBoolItem*>(pItem)->GetValue() : !rACfg.IsAutoFormatByInput(); + if( bSet != rACfg.IsAutoFormatByInput() ) + { + rACfg.SetAutoFormatByInput( bSet ); + rACfg.Commit(); + GetView().GetViewFrame()->GetBindings().Invalidate( nSlot ); + if ( !pItem ) + rReq.AppendItem( SfxBoolItem( GetPool().GetWhich(nSlot), bSet ) ); + rReq.Done(); + } + } + break; + case FN_AUTO_CORRECT: + { + // At first set to blank as default. + rWrtSh.AutoCorrect( *SvxAutoCorrCfg::Get().GetAutoCorrect(), ' ' ); + rReq.Done(); + } + break; + case FN_TABLE_SORT_DIALOG: + case FN_SORTING_DLG: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwSortingDialog(GetView().GetFrameWeld(), rWrtSh)); + pDlg->Execute(); + rReq.Done(); + } + break; + case FN_NUMBERING_OUTLINE_DLG: + { + GetView().ExecNumberingOutline(GetPool()); + rReq.Done(); + } + break; + case FN_CALCULATE: + { + rtl::Reference<SwTransferable> pTransfer = new SwTransferable( rWrtSh ); + pTransfer->CalculateAndCopy(); + rReq.Done(); + } + break; + case FN_GOTO_REFERENCE: + { + SwField *pField = rWrtSh.GetCurField(); + if(pField && pField->GetTypeId() == SwFieldTypesEnum::GetRef) + { + rWrtSh.StartAllAction(); + rWrtSh.SwCursorShell::GotoRefMark( static_cast<SwGetRefField*>(pField)->GetSetRefName(), + static_cast<SwGetRefField*>(pField)->GetSubType(), + static_cast<SwGetRefField*>(pField)->GetSeqNo() ); + rWrtSh.EndAllAction(); + rReq.Done(); + } + } + break; + case FN_EDIT_FORMULA: + { + const sal_uInt16 nId = SwInputChild::GetChildWindowId(); + SfxViewFrame* pVFrame = GetView().GetViewFrame(); + if(pItem) + { + //if the ChildWindow is active it has to be removed + if( pVFrame->HasChildWindow( nId ) ) + { + pVFrame->ToggleChildWindow( nId ); + pVFrame->GetBindings().InvalidateAll( true ); + } + + OUString sFormula(static_cast<const SfxStringItem*>(pItem)->GetValue()); + SwFieldMgr aFieldMgr; + rWrtSh.StartAllAction(); + bool bDelSel = rWrtSh.HasSelection(); + if( bDelSel ) + { + rWrtSh.StartUndo( SwUndoId::START ); + rWrtSh.DelRight(); + } + else + { + rWrtSh.EnterStdMode(); + } + + if( !bDelSel && aFieldMgr.GetCurField() && SwFieldTypesEnum::Formel == aFieldMgr.GetCurTypeId() ) + aFieldMgr.UpdateCurField( aFieldMgr.GetCurField()->GetFormat(), OUString(), sFormula ); + else if( !sFormula.isEmpty() ) + { + if( rWrtSh.IsCursorInTable() ) + { + SfxItemSet aSet( rWrtSh.GetAttrPool(), svl::Items<RES_BOXATR_FORMULA, RES_BOXATR_FORMULA>{} ); + aSet.Put( SwTableBoxFormula( sFormula )); + rWrtSh.SetTableBoxFormulaAttrs( aSet ); + rWrtSh.UpdateTable(); + } + else + { + SvNumberFormatter* pFormatter = rWrtSh.GetNumberFormatter(); + const sal_uInt32 nSysNumFormat = pFormatter->GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_SYSTEM); + SwInsertField_Data aData(SwFieldTypesEnum::Formel, nsSwGetSetExpType::GSE_FORMULA, OUString(), sFormula, nSysNumFormat); + aFieldMgr.InsertField(aData); + } + } + + if( bDelSel ) + rWrtSh.EndUndo( SwUndoId::END ); + rWrtSh.EndAllAction(); + rReq.Done(); + } + else + { + rWrtSh.EndAllTableBoxEdit(); + pVFrame->ToggleChildWindow( nId ); + if( !pVFrame->HasChildWindow( nId ) ) + pVFrame->GetBindings().InvalidateAll( true ); + rReq.Ignore(); + } + } + + break; + case FN_TABLE_UNSET_READ_ONLY: + { + rWrtSh.UnProtectTables(); + } + break; + case SID_EDIT_HYPERLINK: + GetView().GetViewFrame()->SetChildWindow(SID_HYPERLINK_DIALOG, true); + break; + case SID_REMOVE_HYPERLINK: + { + bool bSel = rWrtSh.HasSelection(); + if(!bSel) + { + rWrtSh.StartAction(); + rWrtSh.Push(); + if(!rWrtSh.SelectTextAttr( RES_TXTATR_INETFMT )) + rWrtSh.SelWrd(); + } + //now remove the attribute + std::set<sal_uInt16> aAttribs; + aAttribs.insert( RES_TXTATR_INETFMT ); + rWrtSh.ResetAttr( aAttribs ); + if(!bSel) + { + rWrtSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rWrtSh.EndAction(); + } + } + break; + case SID_ATTR_BRUSH_CHAR : + case SID_ATTR_CHAR_SCALEWIDTH : + case SID_ATTR_CHAR_ROTATED : + case FN_TXTATR_INET : + case FN_INSERT_HYPERLINK: + { + const sal_uInt16 nWhich = GetPool().GetWhich( nSlot ); + if ( pArgs && pArgs->GetItemState( nWhich ) == SfxItemState::SET ) + bUseDialog = false; + [[fallthrough]]; + } + case SID_CHAR_DLG: + case SID_CHAR_DLG_EFFECT: + { + sw_CharDialog( rWrtSh, bUseDialog, nSlot, pArgs, &rReq ); + } + break; + case SID_CHAR_DLG_FOR_PARAGRAPH: + { + rWrtSh.Push(); //save current cursor + SwLangHelper::SelectCurrentPara( rWrtSh ); + sw_CharDialog( rWrtSh, bUseDialog, nSlot, pArgs, &rReq ); + rWrtSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // restore old cursor + } + break; + case SID_ATTR_LRSPACE : + case SID_ATTR_ULSPACE : + case SID_ATTR_BRUSH : + case SID_PARA_VERTALIGN : + case SID_ATTR_PARA_NUMRULE : + case SID_ATTR_PARA_REGISTER : + case SID_ATTR_PARA_PAGENUM : + case FN_FORMAT_LINENUMBER : + case FN_NUMBER_NEWSTART : + case FN_NUMBER_NEWSTART_AT : + case FN_FORMAT_DROPCAPS : + case FN_DROP_TEXT: + case SID_ATTR_PARA_LRSPACE: + { + const sal_uInt16 nWhich = GetPool().GetWhich( nSlot ); + if ( pArgs && pArgs->GetItemState( nWhich ) == SfxItemState::SET ) + bUseDialog = false; + [[fallthrough]]; + } + case SID_PARA_DLG: + { + SwPaM* pPaM = nullptr; + + if ( pArgs ) + { + const SfxPoolItem* pPaMItem = nullptr; + pArgs->GetItemState( GetPool().GetWhich( FN_PARAM_PAM ), false, &pPaMItem ); + if ( pPaMItem ) + pPaM = static_cast< const SwPaMItem* >( pPaMItem )->GetValue( ); + } + + if ( !pPaM ) + pPaM = rWrtSh.GetCursor(); + + FieldUnit eMetric = ::GetDfltMetric( dynamic_cast<SwWebView*>( &GetView()) != nullptr ); + SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric))); + + bool bApplyCharUnit = ::HasCharUnit( dynamic_cast<SwWebView*>( &GetView()) != nullptr ); + SW_MOD()->PutItem(SfxBoolItem(SID_ATTR_APPLYCHARUNIT, bApplyCharUnit)); + + SfxItemSet aCoreSet( + GetPool(), + svl::Items< + RES_PARATR_BEGIN, RES_FRMATR_END - 1, + // FillAttribute support: + XATTR_FILL_FIRST, XATTR_FILL_LAST, + // Includes SID_ATTR_TABSTOP_POS: + SID_ATTR_TABSTOP_DEFAULTS, SID_ATTR_TABSTOP_OFFSET, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP, + // Items to hand over XPropertyList things like XColorList, + // XHatchList, XGradientList, and XBitmapList to the Area + // TabPage: + SID_COLOR_TABLE, SID_PATTERN_LIST, + SID_HTML_MODE, SID_HTML_MODE, + SID_ATTR_PARA_PAGENUM, SID_ATTR_PARA_PAGENUM, + FN_PARAM_1, FN_PARAM_1, + FN_NUMBER_NEWSTART, FN_NUMBER_NEWSTART_AT, + FN_DROP_TEXT, FN_DROP_CHAR_STYLE_NAME>{}); + + // get also the list level indent values merged as LR-SPACE item, if needed. + rWrtSh.GetPaMAttr( pPaM, aCoreSet, true ); + + // create needed items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + // Do this after GetCurAttr, this resets the ItemSet content again + const SwDrawModel* pDrawModel = GetView().GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + + aCoreSet.Put(SvxColorListItem(pDrawModel->GetColorList(), SID_COLOR_TABLE)); + aCoreSet.Put(SvxGradientListItem(pDrawModel->GetGradientList(), SID_GRADIENT_LIST)); + aCoreSet.Put(SvxHatchListItem(pDrawModel->GetHatchList(), SID_HATCH_LIST)); + aCoreSet.Put(SvxBitmapListItem(pDrawModel->GetBitmapList(), SID_BITMAP_LIST)); + aCoreSet.Put(SvxPatternListItem(pDrawModel->GetPatternList(), SID_PATTERN_LIST)); + aCoreSet.Put(SfxUInt16Item(SID_HTML_MODE, + ::GetHtmlMode(GetView().GetDocShell()))); + + // Tabulators: Put DefaultTabs into ItemSet + const SvxTabStopItem& rDefTabs = + GetPool().GetDefaultItem(RES_PARATR_TABSTOP); + + const sal_uInt16 nDefDist = static_cast<sal_uInt16>(::GetTabDist( rDefTabs )); + SfxUInt16Item aDefDistItem( SID_ATTR_TABSTOP_DEFAULTS, nDefDist ); + aCoreSet.Put( aDefDistItem ); + + // Current tabulator + SfxUInt16Item aTabPos( SID_ATTR_TABSTOP_POS, 0 ); + aCoreSet.Put( aTabPos ); + + // Left border as offset + //#i24363# tab stops relative to indent + const long nOff = rWrtSh.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) ? + aCoreSet.Get( RES_LR_SPACE ).GetTextLeft() : 0; + SfxInt32Item aOff( SID_ATTR_TABSTOP_OFFSET, nOff ); + aCoreSet.Put( aOff ); + + // Setting the BoxInfo + ::PrepareBoxInfo( aCoreSet, rWrtSh ); + + // Current page format + ::SwToSfxPageDescAttr( aCoreSet ); + + // Properties of numbering + if (rWrtSh.GetNumRuleAtCurrCursorPos()) + { + SfxBoolItem aStart( FN_NUMBER_NEWSTART, rWrtSh.IsNumRuleStart( pPaM ) ); + aCoreSet.Put(aStart); + SfxUInt16Item aStartAt( FN_NUMBER_NEWSTART_AT, + rWrtSh.GetNodeNumStart( pPaM ) ); + aCoreSet.Put(aStartAt); + } + VclPtr<SfxAbstractTabDialog> pDlg; + + if ( bUseDialog && GetActiveView() ) + { + OString sDefPage; + if (pItem) + sDefPage = OUStringToOString(static_cast<const SfxStringItem*>(pItem)->GetValue(), RTL_TEXTENCODING_UTF8); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateSwParaDlg(GetView().GetFrameWeld(), GetView(), aCoreSet, false, sDefPage)); + } + + if ( !bUseDialog ) + { + if ( nSlot == SID_ATTR_PARA_LRSPACE) + { + SvxLRSpaceItem aParaMargin(static_cast<const SvxLRSpaceItem&>(pArgs->Get(nSlot))); + aParaMargin.SetWhich( RES_LR_SPACE); + aCoreSet.Put(aParaMargin); + + sw_ParagraphDialogResult(&aCoreSet, rWrtSh, rReq, pPaM); + } + else + sw_ParagraphDialogResult(const_cast<SfxItemSet*>(pArgs), rWrtSh, rReq, pPaM); + } + else if (pDlg) + { + auto pRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + + auto xPaM(std::make_shared<SwPaM>(*pPaM, nullptr)); // tdf#134439 make a copy to use at later apply + pDlg->StartExecuteAsync([pDlg, &rWrtSh, pRequest, nDefDist, xPaM](sal_Int32 nResult){ + if (nResult == RET_OK) + { + // Apply defaults if necessary. + SfxItemSet* pSet = const_cast<SfxItemSet*>(pDlg->GetOutputItemSet()); + sal_uInt16 nNewDist; + const SfxPoolItem* pItem2 = nullptr; + if (SfxItemState::SET == pSet->GetItemState(SID_ATTR_TABSTOP_DEFAULTS, false, &pItem2) && + nDefDist != (nNewDist = static_cast<const SfxUInt16Item*>(pItem2)->GetValue()) ) + { + SvxTabStopItem aDefTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + MakeDefTabs( nNewDist, aDefTabs ); + rWrtSh.SetDefault( aDefTabs ); + pSet->ClearItem( SID_ATTR_TABSTOP_DEFAULTS ); + } + + if (SfxItemState::SET == pSet->GetItemState(FN_PARAM_1, false, &pItem2)) + { + pSet->Put(SfxStringItem(FN_DROP_TEXT, static_cast<const SfxStringItem*>(pItem2)->GetValue())); + pSet->ClearItem(FN_PARAM_1); + } + + if (SfxItemState::SET == pSet->GetItemState(RES_PARATR_DROP, false, &pItem2)) + { + OUString sCharStyleName; + if (static_cast<const SwFormatDrop*>(pItem2)->GetCharFormat()) + sCharStyleName = static_cast<const SwFormatDrop*>(pItem2)->GetCharFormat()->GetName(); + pSet->Put(SfxStringItem(FN_DROP_CHAR_STYLE_NAME, sCharStyleName)); + } + + sw_ParagraphDialogResult(pSet, rWrtSh, *pRequest, xPaM.get()); + } + pDlg->disposeOnce(); + }); + } + } + break; + case FN_NUM_CONTINUE: + { + OUString sContinuedListId; + const SwNumRule* pRule = + rWrtSh.SearchNumRule( true, sContinuedListId ); + // #i86492# + // Search also for bullet list + if ( !pRule ) + { + pRule = rWrtSh.SearchNumRule( false, sContinuedListId ); + } + if ( pRule ) + { + rWrtSh.SetCurNumRule( *pRule, false, sContinuedListId ); + } + } + break; + + case FN_SELECT_PARA: + { + if ( !rWrtSh.IsSttOfPara() ) + rWrtSh.SttPara(); + else + rWrtSh.EnterStdMode(); + rWrtSh.EndPara( true ); + } + break; + + case SID_DEC_INDENT: + case SID_INC_INDENT: + //According to the requirement, modified the behavior when user + //using the indent button on the toolbar. Now if we increase/decrease indent for a + //paragraph which has bullet style it will increase/decrease the bullet level. + { + //If the current paragraph has bullet call the function to + //increase or decrease the bullet level. + //Why could I know whether a paragraph has bullet or not by checking the below conditions? + //Please refer to the "case KEY_TAB:" section in SwEditWin::KeyInput(..) : + // if( rSh.GetCurNumRule() && rSh.IsSttOfPara() && + // !rSh.HasReadonlySel() ) + // eKeyState = KS_NumDown; + //Above code demonstrates that when the cursor is at the start of a paragraph which has bullet, + //press TAB will increase the bullet level. + //So I copied from that ^^ + if ( rWrtSh.GetNumRuleAtCurrCursorPos() && !rWrtSh.HasReadonlySel() ) + { + rWrtSh.NumUpDown( SID_INC_INDENT == nSlot ); + } + else //execute the original processing functions + { + //below is copied of the old codes + rWrtSh.MoveLeftMargin( SID_INC_INDENT == nSlot, rReq.GetModifier() != KEY_MOD1 ); + } + } + rReq.Done(); + break; + + case FN_DEC_INDENT_OFFSET: + case FN_INC_INDENT_OFFSET: + rWrtSh.MoveLeftMargin( FN_INC_INDENT_OFFSET == nSlot, rReq.GetModifier() == KEY_MOD1 ); + rReq.Done(); + break; + + case SID_ATTR_CHAR_COLOR2: + { + Color aSet; + OUString sColor; + const SfxPoolItem* pColorStringItem = nullptr; + bool bHasItem = false; + + if(pItem) + { + aSet = static_cast<const SvxColorItem*>(pItem)->GetValue(); + bHasItem = true; + } + else if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pColorStringItem)) + { + sColor = static_cast<const SfxStringItem*>(pColorStringItem)->GetValue(); + aSet = Color(sColor.toInt32(16)); + bHasItem = true; + } + + if (bHasItem) + { + SwEditWin& rEditWin = GetView().GetEditWin(); + rEditWin.SetWaterCanTextColor(aSet); + SwApplyTemplate* pApply = rEditWin.GetApplyTemplate(); + + // If there is a selection, then set the color on it + // otherwise, it'll be the color for the next text to be typed + if(!pApply || pApply->nColor != SID_ATTR_CHAR_COLOR_EXT) + { + rWrtSh.SetAttrItem(SvxColorItem (aSet, RES_CHRATR_COLOR)); + } + + rReq.Done(); + } + } + break; + case SID_ATTR_CHAR_COLOR_BACKGROUND: + case SID_ATTR_CHAR_COLOR_BACKGROUND_EXT: + case SID_ATTR_CHAR_COLOR_EXT: + { + Color aSet; + OUString sColor; + const SfxPoolItem* pColorStringItem = nullptr; + + if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pColorStringItem)) + { + sColor = static_cast<const SfxStringItem*>(pColorStringItem)->GetValue(); + if (sColor == "transparent") + aSet = COL_TRANSPARENT; + else + aSet = Color(sColor.toInt32(16)); + } + else if (pItem) + aSet = static_cast<const SvxColorItem*>(pItem)->GetValue(); + else + aSet = COL_TRANSPARENT; + + SwEditWin& rEdtWin = GetView().GetEditWin(); + if (nSlot != SID_ATTR_CHAR_COLOR_EXT) + rEdtWin.SetWaterCanTextBackColor(aSet); + else if (pItem) + rEdtWin.SetWaterCanTextColor(aSet); + + SwApplyTemplate* pApply = rEdtWin.GetApplyTemplate(); + SwApplyTemplate aTempl; + if (!pApply && (rWrtSh.HasSelection() || rReq.IsAPI())) + { + if (nSlot != SID_ATTR_CHAR_COLOR_EXT) + { + SfxItemSet aCoreSet( rWrtSh.GetView().GetPool(), svl::Items< + RES_CHRATR_BACKGROUND, RES_CHRATR_BACKGROUND>{} ); + + rWrtSh.GetCurAttr( aCoreSet ); + + // Remove highlight if already set of the same color + const SvxBrushItem& rBrushItem = aCoreSet.Get(RES_CHRATR_BACKGROUND); + if ( aSet == rBrushItem.GetColor() ) + aSet = COL_TRANSPARENT; + + ApplyCharBackground(aSet, rWrtSh); + } + else + rWrtSh.SetAttrItem( + SvxColorItem(aSet, RES_CHRATR_COLOR) ); + } + else if (nSlot == SID_ATTR_CHAR_COLOR_BACKGROUND) + { + if (!pApply || pApply->nColor != SID_ATTR_CHAR_COLOR_BACKGROUND_EXT) + { + aTempl.nColor = SID_ATTR_CHAR_COLOR_BACKGROUND_EXT; + rEdtWin.SetApplyTemplate(aTempl); + } + } + else + { + if(!pApply || pApply->nColor != nSlot) + aTempl.nColor = nSlot; + rEdtWin.SetApplyTemplate(aTempl); + } + + rReq.Done(); + } + break; + + case FN_NUM_BULLET_MOVEDOWN: + if (!rWrtSh.IsAddMode()) + rWrtSh.MoveParagraph(); + rReq.Done(); + break; + + case FN_NUM_BULLET_MOVEUP: + if (!rWrtSh.IsAddMode()) + rWrtSh.MoveParagraph(-1); + rReq.Done(); + break; + case SID_RUBY_DIALOG: + case SID_HYPERLINK_DIALOG: + { + SfxRequest aReq(nSlot, SfxCallMode::SLOT, SfxGetpApp()->GetPool()); + GetView().GetViewFrame()->ExecuteSlot( aReq); + rReq.Ignore(); + } + break; + case FN_INSERT_PAGEHEADER: + case FN_INSERT_PAGEFOOTER: + if(pArgs && pArgs->Count()) + { + OUString sStyleName; + if(pItem) + sStyleName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + bool bOn = true; + if( SfxItemState::SET == pArgs->GetItemState(FN_PARAM_1, false, &pItem)) + bOn = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + rWrtSh.ChangeHeaderOrFooter(sStyleName, FN_INSERT_PAGEHEADER == nSlot, bOn, !rReq.IsAPI()); + rReq.Done(); + } + break; + case FN_READONLY_SELECTION_MODE : + if(GetView().GetDocShell()->IsReadOnly()) + { + rWrtSh.SetReadonlySelectionOption( + !rWrtSh.GetViewOptions()->IsSelectionInReadonly()); + rWrtSh.ShowCursor(); + } + break; + case FN_SELECTION_MODE_DEFAULT: + case FN_SELECTION_MODE_BLOCK : + { + bool bSetBlockMode = !rWrtSh.IsBlockMode(); + if( pArgs && SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem)) + bSetBlockMode = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + if( ( nSlot == FN_SELECTION_MODE_DEFAULT ) != bSetBlockMode ) + rWrtSh.EnterBlockMode(); + else + rWrtSh.EnterStdMode(); + SfxBindings &rBnd = GetView().GetViewFrame()->GetBindings(); + rBnd.Invalidate(FN_STAT_SELMODE); + rBnd.Update(FN_STAT_SELMODE); + } + break; + case SID_OPEN_HYPERLINK: + case SID_COPY_HYPERLINK_LOCATION: + { + SfxItemSet aSet(GetPool(), + svl::Items<RES_TXTATR_INETFMT, + RES_TXTATR_INETFMT>{}); + rWrtSh.GetCurAttr(aSet); + if(SfxItemState::SET <= aSet.GetItemState( RES_TXTATR_INETFMT )) + { + const SwFormatINetFormat& rINetFormat = dynamic_cast<const SwFormatINetFormat&>( aSet.Get(RES_TXTATR_INETFMT) ); + if( nSlot == SID_COPY_HYPERLINK_LOCATION ) + { + ::uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetView().GetEditWin().GetClipboard(); + vcl::unohelper::TextDataObject::CopyStringTo( + rINetFormat.GetValue(), + xClipboard ); + } + else + rWrtSh.ClickToINetAttr(rINetFormat); + } + } + break; + case SID_OPEN_XML_FILTERSETTINGS: + { + HandleOpenXmlFilterSettings(rReq); + } + break; + case FN_FORMAT_APPLY_HEAD1: + { + } + break; + case FN_FORMAT_APPLY_HEAD2: + { + } + break; + case FN_FORMAT_APPLY_HEAD3: + { + } + break; + case FN_FORMAT_APPLY_DEFAULT: + { + } + break; + case FN_FORMAT_APPLY_TEXTBODY: + { + } + break; + case FN_WORDCOUNT_DIALOG: + { + GetView().UpdateWordCount(this, nSlot); + } + break; + case FN_PROTECT_FIELDS: + case FN_PROTECT_BOOKMARKS: + { + IDocumentSettingAccess& rIDSA = rWrtSh.getIDocumentSettingAccess(); + DocumentSettingId aSettingId = nSlot == FN_PROTECT_FIELDS + ? DocumentSettingId::PROTECT_FIELDS + : DocumentSettingId::PROTECT_BOOKMARKS; + rIDSA.set(aSettingId, !rIDSA.get(aSettingId)); + // Invalidate so that toggle state gets updated + SfxViewFrame* pViewFrame = GetView().GetViewFrame(); + pViewFrame->GetBindings().Invalidate(nSlot); + pViewFrame->GetBindings().Update(nSlot); + } + break; + case SID_FM_CTL_PROPERTIES: + { + SwPosition aPos(*GetShell().GetCursor()->GetPoint()); + sw::mark::IFieldmark* pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aPos); + if ( !pFieldBM ) + { + --aPos.nContent; + pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aPos); + } + + if ( pFieldBM && pFieldBM->GetFieldname() == ODF_FORMDROPDOWN ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateDropDownFormFieldDialog(rWrtSh.GetView().GetFrameWeld(), pFieldBM)); + if (pDlg->Execute() == RET_OK) + { + pFieldBM->Invalidate(); + rWrtSh.InvalidateWindows( rWrtSh.GetView().GetVisArea() ); + rWrtSh.UpdateCursor(); // cursor position might be invalid + // Hide the button here and make it visible later, to make transparent background work with SAL_USE_VCLPLUGIN=gen + dynamic_cast<::sw::mark::DropDownFieldmark&>(*pFieldBM).HideButton(); + } + } + else if ( pFieldBM && pFieldBM->GetFieldname() == ODF_FORMDATE ) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + sw::mark::DateFieldmark& rDateField = dynamic_cast<sw::mark::DateFieldmark&>(*pFieldBM); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateDateFormFieldDialog(rWrtSh.GetView().GetFrameWeld(), &rDateField, GetView().GetDocShell()->GetDoc())); + if (pDlg->Execute() == RET_OK) + { + rDateField.Invalidate(); + rWrtSh.InvalidateWindows( rWrtSh.GetView().GetVisArea() ); + rWrtSh.UpdateCursor(); // cursor position might be invalid + // Hide the button here and make it visible later, to make transparent background work with SAL_USE_VCLPLUGIN=gen + rDateField.HideButton(); + } + } + else + { + SfxRequest aReq( GetView().GetViewFrame(), SID_FM_CTL_PROPERTIES ); + aReq.AppendItem( SfxBoolItem( SID_FM_CTL_PROPERTIES, true ) ); + rWrtSh.GetView().GetFormShell()->Execute( aReq ); + } + } + break; + case SID_SPELLCHECK_IGNORE: + { + SwPaM *pPaM = rWrtSh.GetCursor(); + if (pPaM) + SwEditShell::IgnoreGrammarErrorAt( *pPaM ); + } + break; + case SID_SPELLCHECK_IGNORE_ALL: + { + OUString sApplyText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pItem2) + sApplyText = pItem2->GetValue(); + + const OUString sGrammarType("Grammar"); + const OUString sSpellingType("Spelling"); + + if(sApplyText == sGrammarType) + { + linguistic2::ProofreadingResult aGrammarCheckRes; + sal_Int32 nErrorInResult = -1; + uno::Sequence< OUString > aSuggestions; + sal_Int32 nErrorPosInText = -1; + SwRect aToFill; + bool bCorrectionRes = rWrtSh.GetGrammarCorrection( aGrammarCheckRes, nErrorPosInText, nErrorInResult, aSuggestions, nullptr, aToFill ); + if(bCorrectionRes) + { + try { + uno::Reference< linguistic2::XDictionary > xDictionary = LinguMgr::GetIgnoreAllList(); + aGrammarCheckRes.xProofreader->ignoreRule( + aGrammarCheckRes.aErrors[ nErrorInResult ].aRuleIdentifier, + aGrammarCheckRes.aLocale ); + // refresh the layout of the actual paragraph (faster) + SwPaM *pPaM = rWrtSh.GetCursor(); + if (pPaM) + SwEditShell::IgnoreGrammarErrorAt( *pPaM ); + // refresh the layout of all paragraphs (workaround to launch a dictionary event) + xDictionary->setActive(false); + xDictionary->setActive(true); + } + catch( const uno::Exception& ) + { + } + } + } + else if (sApplyText == sSpellingType) + { + SwRect aToFill; + uno::Reference< linguistic2::XSpellAlternatives > xSpellAlt( rWrtSh.GetCorrection(nullptr, aToFill) ); + uno::Reference< linguistic2::XDictionary > xDictionary = LinguMgr::GetIgnoreAllList(); + OUString sWord(xSpellAlt->getWord()); + linguistic::DictionaryError nAddRes = linguistic::AddEntryToDic( xDictionary, + sWord, false, OUString() ); + if (linguistic::DictionaryError::NONE != nAddRes && !xDictionary->getEntry(sWord).is()) + { + SvxDicError(rWrtSh.GetView().GetFrameWeld(), nAddRes); + } + } + } + break; + case SID_SPELLCHECK_APPLY_SUGGESTION: + { + OUString sApplyText; + const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if (pItem2) + sApplyText = pItem2->GetValue(); + + const OUString sSpellingRule("Spelling_"); + const OUString sGrammarRule("Grammar_"); + + bool bGrammar = false; + sal_Int32 nPos = 0; + uno::Reference< linguistic2::XSpellAlternatives > xSpellAlt; + if(-1 != (nPos = sApplyText.indexOf( sGrammarRule ))) + { + sApplyText = sApplyText.replaceAt(nPos, sGrammarRule.getLength(), ""); + bGrammar = true; + } + else if (-1 != (nPos = sApplyText.indexOf( sSpellingRule ))) + { + sApplyText = sApplyText.replaceAt(nPos, sSpellingRule.getLength(), ""); + SwRect aToFill; + xSpellAlt.set(rWrtSh.GetCorrection(nullptr, aToFill)); + bGrammar = false; + } + + if (!bGrammar && !xSpellAlt.is()) + return; + + bool bOldIns = rWrtSh.IsInsMode(); + rWrtSh.SetInsMode(); + + OUString aTmp( sApplyText ); + OUString aOrig( bGrammar ? OUString() : xSpellAlt->getWord() ); + + // if original word has a trailing . (likely the end of a sentence) + // and the replacement text hasn't, then add it to the replacement + if (!aTmp.isEmpty() && !aOrig.isEmpty() && + aOrig.endsWith(".") && /* !IsAlphaNumeric ??*/ + !aTmp.endsWith(".")) + { + aTmp += "."; + } + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, rWrtSh.GetCursorDescr()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + + OUString aTmpStr = SwResId(STR_START_QUOTE) + + aTmp + SwResId(STR_END_QUOTE); + aRewriter.AddRule(UndoArg3, aTmpStr); + + rWrtSh.StartUndo(SwUndoId::UI_REPLACE, &aRewriter); + rWrtSh.StartAction(); + + rWrtSh.Replace(aTmp, false); + + rWrtSh.EndAction(); + rWrtSh.EndUndo(); + + rWrtSh.SetInsMode( bOldIns ); + } + break; + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +void SwTextShell::GetState( SfxItemSet &rSet ) +{ + SwWrtShell &rSh = GetShell(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + switch ( nWhich ) + { + case FN_FORMAT_CURRENT_FOOTNOTE_DLG: + if( !rSh.IsCursorInFootnote() ) + rSet.DisableItem( nWhich ); + break; + + case SID_LANGUAGE_STATUS: + { + // the value of used script types + OUString aScriptTypesInUse( OUString::number( static_cast<int>(rSh.GetScriptType()) ) ); + + // get keyboard language + OUString aKeyboardLang; + SwEditWin& rEditWin = GetView().GetEditWin(); + LanguageType nLang = rEditWin.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang = "*"; + nLang = SwLangHelper::GetCurrentLanguage( rSh ); + if (nLang != LANGUAGE_DONTKNOW) + { + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + if (comphelper::LibreOfficeKit::isActive()) + { + if (nLang == LANGUAGE_NONE) + { + aCurrentLang += ";-"; + } + else + { + aCurrentLang += ";" + LanguageTag(nLang).getBcp47(false); + } + } + } + + // build sequence for status value + uno::Sequence< OUString > aSeq( 4 ); + aSeq[0] = aCurrentLang; + aSeq[1] = aScriptTypesInUse; + aSeq[2] = aKeyboardLang; + aSeq[3] = SwLangHelper::GetTextForLanguageGuessing( rSh ); + + // set sequence as status value + SfxStringListItem aItem( SID_LANGUAGE_STATUS ); + aItem.SetStringList( aSeq ); + rSet.Put( aItem ); + } + break; + + case SID_THES: + { + // is there a valid selection to get text from? + OUString aText; + bool bValid = !rSh.HasSelection() || + (rSh.IsSelOnePara() && !rSh.IsMultiSelection()); + // prevent context menu from showing when cursor is not in or at the end of a word + // (GetCurWord will return the next word if there is none at the current position...) + const sal_Int16 nWordType = ::i18n::WordType::DICTIONARY_WORD; + bool bWord = rSh.IsInWord( nWordType ) || rSh.IsStartWord( nWordType ) || rSh.IsEndWord( nWordType ); + if (bValid && bWord) + aText = rSh.HasSelection()? rSh.GetSelText() : rSh.GetCurWord(); + + LanguageType nLang = rSh.GetCurLang(); + LanguageTag aLanguageTag( nLang); + const lang::Locale& aLocale( aLanguageTag.getLocale()); + + // disable "Thesaurus" context menu entry if there is nothing to look up + uno::Reference< linguistic2::XThesaurus > xThes( ::GetThesaurus() ); + if (aText.isEmpty() || + !xThes.is() || nLang == LANGUAGE_NONE || !xThes->hasLocale( aLocale )) + rSet.DisableItem( SID_THES ); + else + { + // set word and locale to look up as status value + OUString aStatusVal = aText + "#" + aLanguageTag.getBcp47(); + rSet.Put( SfxStringItem( SID_THES, aStatusVal ) ); + } + } + break; + + case FN_NUMBER_NEWSTART : + if(!rSh.GetNumRuleAtCurrCursorPos()) + rSet.DisableItem(nWhich); + else + rSet.Put(SfxBoolItem(FN_NUMBER_NEWSTART, + rSh.IsNumRuleStart())); + break; + + case FN_EDIT_FORMULA: + case SID_CHARMAP: + case SID_EMOJI_CONTROL: + case SID_CHARMAP_CONTROL: + { + const SelectionType nType = rSh.GetSelectionType(); + if (!(nType & SelectionType::Text) && + !(nType & SelectionType::Table) && + !(nType & SelectionType::NumberList)) + { + rSet.DisableItem(nWhich); + } + else if ( nWhich == FN_EDIT_FORMULA + && rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + } + break; + + case FN_INSERT_ENDNOTE: + case FN_INSERT_FOOTNOTE: + case FN_INSERT_FOOTNOTE_DLG: + { + const FrameTypeFlags nNoType = + FrameTypeFlags::FLY_ANY | FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE; + if ( rSh.GetFrameType(nullptr,true) & nNoType ) + rSet.DisableItem(nWhich); + + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + } + break; + + case FN_INSERT_HYPERLINK: + case SID_INSERTDOC: + case FN_INSERT_GLOSSARY: + case FN_EXPAND_GLOSSARY: + if ( rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_INSERT_TABLE: + if ( rSh.CursorInsideInputField() + || rSh.GetTableFormat() + || (rSh.GetFrameType(nullptr,true) & FrameTypeFlags::FOOTNOTE) ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_CALCULATE: + if ( !rSh.IsSelection() ) + rSet.DisableItem(nWhich); + break; + case FN_GOTO_REFERENCE: + { + SwField *pField = rSh.GetCurField(); + if ( !pField || (pField->GetTypeId() != SwFieldTypesEnum::GetRef) ) + rSet.DisableItem(nWhich); + } + break; + case FN_AUTOFORMAT_AUTO: + { + rSet.Put( SfxBoolItem( nWhich, SvxAutoCorrCfg::Get().IsAutoFormatByInput() )); + } + break; + + case SID_DEC_INDENT: + case SID_INC_INDENT: + { + //if the paragraph has bullet we'll do the following things: + //1: if the bullet level is the first level, disable the decrease-indent button + //2: if the bullet level is the last level, disable the increase-indent button + if ( rSh.GetNumRuleAtCurrCursorPos() && !rSh.HasReadonlySel() ) + { + const sal_uInt8 nLevel = rSh.GetNumLevel(); + if ( ( nLevel == ( MAXLEVEL - 1 ) && nWhich == SID_INC_INDENT ) + || ( nLevel == 0 && nWhich == SID_DEC_INDENT ) ) + { + rSet.DisableItem( nWhich ); + } + } + else + { + sal_uInt16 nHtmlMode = ::GetHtmlMode( GetView().GetDocShell() ); + nHtmlMode &= HTMLMODE_ON | HTMLMODE_SOME_STYLES; + if ( ( nHtmlMode == HTMLMODE_ON ) + || !rSh.IsMoveLeftMargin( SID_INC_INDENT == nWhich ) ) + { + rSet.DisableItem( nWhich ); + } + } + } + break; + + case FN_DEC_INDENT_OFFSET: + case FN_INC_INDENT_OFFSET: + { + sal_uInt16 nHtmlMode = ::GetHtmlMode(GetView().GetDocShell()); + nHtmlMode &= HTMLMODE_ON|HTMLMODE_SOME_STYLES; + if( (nHtmlMode == HTMLMODE_ON) || + !rSh.IsMoveLeftMargin( FN_INC_INDENT_OFFSET == nWhich, + false )) + rSet.DisableItem( nWhich ); + } + break; + + case SID_ATTR_CHAR_COLOR2: + { + SfxItemSet aSet( GetPool() ); + rSh.GetCurAttr( aSet ); + const SvxColorItem& aColorItem = aSet.Get(RES_CHRATR_COLOR); + rSet.Put( aColorItem.CloneSetWhich(SID_ATTR_CHAR_COLOR2) ); + } + break; + case SID_ATTR_CHAR_COLOR_BACKGROUND: + { + // Always use the visible background + SfxItemSet aSet( GetPool() ); + rSh.GetCurAttr( aSet ); + const SvxBrushItem& aBrushItem = aSet.Get(RES_CHRATR_HIGHLIGHT); + if( aBrushItem.GetColor() != COL_TRANSPARENT ) + { + rSet.Put( SvxColorItem(aBrushItem.GetColor(), nWhich) ); + } + else + { + const SvxBrushItem& aBrushItem2 = aSet.Get(RES_CHRATR_BACKGROUND); + rSet.Put( SvxColorItem(aBrushItem2.GetColor(), nWhich) ); + } + } + break; + case SID_ATTR_CHAR_COLOR_BACKGROUND_EXT: + case SID_ATTR_CHAR_COLOR_EXT: + { + SwEditWin& rEdtWin = GetView().GetEditWin(); + SwApplyTemplate* pApply = rEdtWin.GetApplyTemplate(); + rSet.Put(SfxBoolItem(nWhich, pApply && pApply->nColor == nWhich)); + } + break; + case FN_SET_REMINDER: + case FN_INSERT_BOOKMARK: + if( rSh.IsTableMode() + || rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_INSERT_BREAK: + if ( rSh.HasReadonlySel() + && !rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_INSERT_BREAK_DLG: + case FN_INSERT_COLUMN_BREAK: + case FN_INSERT_PAGEBREAK: + if( rSh.CursorInsideInputField() ) + { + rSet.DisableItem( nWhich ); + } + break; + + case FN_INSERT_PAGEHEADER: + case FN_INSERT_PAGEFOOTER: + if (comphelper::LibreOfficeKit::isActive()) + { + bool bState = false; + bool bAllState = true; + bool bIsPhysical = false; + + OUString aStyleName; + std::vector<OUString> aList; + const OUString sPhysical("IsPhysical"); + const OUString sDisplay("DisplayName"); + const OUString sHeaderOn(nWhich == FN_INSERT_PAGEHEADER ? OUString("HeaderIsOn") : OUString("FooterIsOn")); + + uno::Reference< XStyleFamiliesSupplier > xSupplier(GetView().GetDocShell()->GetBaseModel(), uno::UNO_QUERY); + if (xSupplier.is()) + { + uno::Reference< XNameContainer > xContainer; + uno::Reference< XNameAccess > xFamilies = xSupplier->getStyleFamilies(); + if (xFamilies->getByName("PageStyles") >>= xContainer) + { + const uno::Sequence< OUString > aSeqNames = xContainer->getElementNames(); + for (const auto& rName : aSeqNames) + { + aStyleName = rName; + uno::Reference<XPropertySet> xPropSet(xContainer->getByName(aStyleName), uno::UNO_QUERY); + if (xPropSet.is() && (xPropSet->getPropertyValue(sPhysical) >>= bIsPhysical) && bIsPhysical) + { + xPropSet->getPropertyValue(sDisplay) >>= aStyleName; + if ((xPropSet->getPropertyValue(sHeaderOn)>>= bState) && bState) + aList.push_back(aStyleName); + else + bState = false; + + // Check if all entries have the same state + bAllState &= bState; + } + else + bIsPhysical = false; + } + } + } + + if (bAllState && aList.size() > 1) + aList.push_back("_ALL_"); + + rSet.Put(SfxStringListItem(nWhich, &aList)); + } + else + { + rSet.Put( SfxObjectShellItem( nWhich, GetView().GetDocShell() )); + } + break; + case FN_TABLE_SORT_DIALOG: + case FN_SORTING_DLG: + if(!rSh.HasSelection() || + (FN_TABLE_SORT_DIALOG == nWhich && !rSh.GetTableFormat())) + rSet.DisableItem( nWhich ); + break; + + case SID_RUBY_DIALOG: + { + SvtCJKOptions aCJKOptions; + if( !aCJKOptions.IsRubyEnabled() + || rSh.CursorInsideInputField() ) + { + GetView().GetViewFrame()->GetBindings().SetVisibleState( nWhich, false ); + rSet.DisableItem(nWhich); + } + else + GetView().GetViewFrame()->GetBindings().SetVisibleState( nWhich, true ); + } + break; + + case SID_HYPERLINK_DIALOG: + if( GetView().GetDocShell()->IsReadOnly() + || ( !GetView().GetViewFrame()->HasChildWindow(nWhich) + && rSh.HasReadonlySel() ) + || rSh.CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + else + { + rSet.Put(SfxBoolItem( nWhich, nullptr != GetView().GetViewFrame()->GetChildWindow( nWhich ) )); + } + break; + + case SID_EDIT_HYPERLINK: + case SID_COPY_HYPERLINK_LOCATION: + { + SfxItemSet aSet(GetPool(), + svl::Items<RES_TXTATR_INETFMT, + RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr(aSet); + if(SfxItemState::SET > aSet.GetItemState( RES_TXTATR_INETFMT ) || rSh.HasReadonlySel()) + { + rSet.DisableItem(nWhich); + } + } + break; + case SID_REMOVE_HYPERLINK: + { + SfxItemSet aSet(GetPool(), + svl::Items<RES_TXTATR_INETFMT, + RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr(aSet); + + // If a hyperlink is selected, either alone or along with other text... + if ((aSet.GetItemState(RES_TXTATR_INETFMT) < SfxItemState::SET && + aSet.GetItemState(RES_TXTATR_INETFMT) != SfxItemState::DONTCARE) || + rSh.HasReadonlySel()) + { + rSet.DisableItem(nWhich); + } + } + break; + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + SvtCJKOptions aCJKOptions; + if(!aCJKOptions.IsChangeCaseMapEnabled()) + { + GetView().GetViewFrame()->GetBindings().SetVisibleState( nWhich, false ); + rSet.DisableItem(nWhich); + } + else + GetView().GetViewFrame()->GetBindings().SetVisibleState( nWhich, true ); + } + break; + case FN_READONLY_SELECTION_MODE : + if(!GetView().GetDocShell()->IsReadOnly()) + rSet.DisableItem( nWhich ); + else + { + rSet.Put(SfxBoolItem(nWhich, rSh.GetViewOptions()->IsSelectionInReadonly())); + } + break; + case FN_SELECTION_MODE_DEFAULT: + case FN_SELECTION_MODE_BLOCK : + rSet.Put(SfxBoolItem(nWhich, (nWhich == FN_SELECTION_MODE_DEFAULT) != rSh.IsBlockMode())); + break; + case SID_OPEN_HYPERLINK: + { + SfxItemSet aSet(GetPool(), + svl::Items<RES_TXTATR_INETFMT, + RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr(aSet); + if(SfxItemState::SET > aSet.GetItemState( RES_TXTATR_INETFMT, false )) + rSet.DisableItem(nWhich); + } + break; + case SID_OPEN_SMARTTAGMENU: + { + std::vector< OUString > aSmartTagTypes; + uno::Sequence< uno::Reference< container::XStringKeyMap > > aStringKeyMaps; + uno::Reference<text::XTextRange> xRange; + + rSh.GetSmartTagTerm( aSmartTagTypes, aStringKeyMaps, xRange ); + + if ( xRange.is() && !aSmartTagTypes.empty() ) + { + uno::Sequence < uno::Sequence< uno::Reference< smarttags::XSmartTagAction > > > aActionComponentsSequence; + uno::Sequence < uno::Sequence< sal_Int32 > > aActionIndicesSequence; + + const SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get(); + rSmartTagMgr.GetActionSequences( aSmartTagTypes, + aActionComponentsSequence, + aActionIndicesSequence ); + + uno::Reference <frame::XController> xController = GetView().GetController(); + const lang::Locale aLocale( SW_BREAKITER()->GetLocale( GetAppLanguageTag() ) ); + const OUString& aApplicationName( rSmartTagMgr.GetApplicationName() ); + const OUString aRangeText = xRange->getString(); + + const SvxSmartTagItem aItem( nWhich, + aActionComponentsSequence, + aActionIndicesSequence, + aStringKeyMaps, + xRange, + xController, + aLocale, + aApplicationName, + aRangeText ); + + rSet.Put( aItem ); + } + else + rSet.DisableItem(nWhich); + } + break; + + case FN_NUM_NUMBERING_ON: + rSet.Put(SfxBoolItem(FN_NUM_NUMBERING_ON,rSh.SelectionHasNumber())); + break; + + case FN_NUM_BULLET_ON: + rSet.Put(SfxBoolItem(FN_NUM_BULLET_ON,rSh.SelectionHasBullet())); + break; + + case FN_BUL_NUM_RULE_INDEX: + case FN_NUM_NUM_RULE_INDEX: + case FN_OUTLINE_RULE_INDEX: + { + SwNumRule* pCurRule = const_cast<SwNumRule*>(GetShell().GetNumRuleAtCurrCursorPos()); + sal_uInt16 nActNumLvl = USHRT_MAX; + if( pCurRule ) + { + nActNumLvl = GetShell().GetNumLevel(); + if( nActNumLvl < MAXLEVEL ) + { + nActNumLvl = 1<<nActNumLvl; + } + SvxNumRule aSvxRule = pCurRule->MakeSvxNumRule(); + if ( GetShell().HasBullet()) + { + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX, USHRT_MAX)); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX, USHRT_MAX)); + NBOTypeMgrBase* pBullets = NBOutlineTypeMgrFact::CreateInstance(NBOType::Bullets); + if ( pBullets ) + { + const sal_uInt16 nBulIndex = pBullets->GetNBOIndexForNumRule(aSvxRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX,nBulIndex)); + } + }else if ( GetShell().HasNumber() ) + { + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX, USHRT_MAX)); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX, USHRT_MAX)); + NBOTypeMgrBase* pNumbering = NBOutlineTypeMgrFact::CreateInstance(NBOType::Numbering); + if ( pNumbering ) + { + const sal_uInt16 nBulIndex = pNumbering->GetNBOIndexForNumRule(aSvxRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX,nBulIndex)); + } + } + + if ( nWhich == FN_OUTLINE_RULE_INDEX ) + { + rSet.Put(SfxUInt16Item(FN_OUTLINE_RULE_INDEX, USHRT_MAX)); + NBOTypeMgrBase* pOutline = NBOutlineTypeMgrFact::CreateInstance(NBOType::Outline); + if ( pOutline ) + { + const sal_uInt16 nIndex = pOutline->GetNBOIndexForNumRule(aSvxRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_OUTLINE_RULE_INDEX,nIndex)); + } + } + } + } + break; + case FN_NUM_CONTINUE: + { + // #i86492# + // Search also for bullet list + OUString aDummy; + const SwNumRule* pRule = + rSh.SearchNumRule( true, aDummy ); + if ( !pRule ) + { + pRule = rSh.SearchNumRule( false, aDummy ); + } + if ( !pRule ) + rSet.DisableItem(nWhich); + } + break; + case SID_INSERT_RLM : + case SID_INSERT_LRM : + { + SvtCTLOptions aCTLOptions; + bool bEnabled = aCTLOptions.IsCTLFontEnabled(); + GetView().GetViewFrame()->GetBindings().SetVisibleState( nWhich, bEnabled ); + if(!bEnabled) + rSet.DisableItem(nWhich); + } + break; + case SID_FM_CTL_PROPERTIES: + { + bool bDisable = false; + + // First get the state from the form shell + SfxItemSet aSet(GetShell().GetAttrPool(), svl::Items<SID_FM_CTL_PROPERTIES, SID_FM_CTL_PROPERTIES>{}); + aSet.Put(SfxBoolItem( SID_FM_CTL_PROPERTIES, true )); + GetShell().GetView().GetFormShell()->GetState( aSet ); + + if(SfxItemState::DISABLED == aSet.GetItemState(SID_FM_CTL_PROPERTIES)) + { + bDisable = true; + } + + // Enable it if we have a valid object other than what form shell knows + SwPosition aPos(*GetShell().GetCursor()->GetPoint()); + sw::mark::IFieldmark* pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aPos); + if ( !pFieldBM && aPos.nContent.GetIndex() > 0) + { + --aPos.nContent; + pFieldBM = GetShell().getIDocumentMarkAccess()->getFieldmarkFor(aPos); + } + if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN || pFieldBM->GetFieldname() == ODF_FORMDATE) ) + { + bDisable = false; + } + + if(bDisable) + rSet.DisableItem(nWhich); + } + break; + case SID_COPY: + case SID_CUT: + { + if (GetObjectShell()->isContentExtractionLocked()) + rSet.DisableItem(nWhich); + break; + } + case FN_PROTECT_FIELDS: + case FN_PROTECT_BOOKMARKS: + { + DocumentSettingId aSettingId = nWhich == FN_PROTECT_FIELDS + ? DocumentSettingId::PROTECT_FIELDS + : DocumentSettingId::PROTECT_BOOKMARKS; + bool bProtected = rSh.getIDocumentSettingAccess().get(aSettingId); + rSet.Put(SfxBoolItem(nWhich, bProtected)); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/textsh2.cxx b/sw/source/uibase/shells/textsh2.cxx new file mode 100644 index 000000000..0438d42aa --- /dev/null +++ b/sw/source/uibase/shells/textsh2.cxx @@ -0,0 +1,259 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <svl/itemset.hxx> +#include <sfx2/request.hxx> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <comphelper/types.hxx> +#include <sfx2/frame.hxx> +#include <fldmgr.hxx> +#include <fldbas.hxx> +#include <dbmgr.hxx> +#include <svx/dataaccessdescriptor.hxx> + +#include <vcl/svapp.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <swtypes.hxx> +#include <cmdid.h> +#include <textsh.hxx> +#include <swabstdlg.hxx> + +using namespace ::svx; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; + +struct DBTextStruct_Impl +{ + SwDBData aDBData; + Sequence<Any> aSelection; + Reference<XResultSet> xCursor; + Reference<XConnection> xConnection; +}; + +void SwTextShell::ExecDB(SfxRequest const &rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + SwDBManager* pDBManager = GetShell().GetDBManager(); + OUString sSourceArg, sCommandArg; + sal_Int32 nCommandTypeArg = 0; + + const SfxPoolItem* pSourceItem = nullptr; + const SfxPoolItem* pCursorItem = nullptr; + const SfxPoolItem* pConnectionItem = nullptr; + const SfxPoolItem* pCommandItem = nullptr; + const SfxPoolItem* pCommandTypeItem = nullptr; + const SfxPoolItem* pSelectionItem = nullptr; + + // first get the selection of rows to be inserted + pArgs->GetItemState(FN_DB_DATA_SELECTION_ANY, false, &pSelectionItem); + + Sequence<Any> aSelection; + if(pSelectionItem) + static_cast<const SfxUnoAnyItem*>(pSelectionItem)->GetValue() >>= aSelection; + + // get the data source name + pArgs->GetItemState(FN_DB_DATA_SOURCE_ANY, false, &pSourceItem); + if(pSourceItem) + static_cast<const SfxUnoAnyItem*>(pSourceItem)->GetValue() >>= sSourceArg; + + // get the command + pArgs->GetItemState(FN_DB_DATA_COMMAND_ANY, false, &pCommandItem); + if(pCommandItem) + static_cast<const SfxUnoAnyItem*>(pCommandItem)->GetValue() >>= sCommandArg; + + // get the command type + pArgs->GetItemState(FN_DB_DATA_COMMAND_TYPE_ANY, false, &pCommandTypeItem); + if(pCommandTypeItem) + static_cast<const SfxUnoAnyItem*>(pCommandTypeItem)->GetValue() >>= nCommandTypeArg; + + Reference<XConnection> xConnection; + pArgs->GetItemState(FN_DB_CONNECTION_ANY, false, &pConnectionItem); + if ( pConnectionItem ) + static_cast<const SfxUnoAnyItem*>(pConnectionItem)->GetValue() >>= xConnection; + // may be we even get no connection + if ( !xConnection.is() ) + { + Reference<XDataSource> xSource; + SwView &rSwView = GetView(); + xConnection = SwDBManager::GetConnection(sSourceArg, xSource, &rSwView); + } + if(!xConnection.is()) + return ; + + // get the cursor, we use to travel, may be NULL + Reference<XResultSet> xCursor; + pArgs->GetItemState(FN_DB_DATA_CURSOR_ANY, false, &pCursorItem); + if ( pCursorItem ) + static_cast<const SfxUnoAnyItem*>(pCursorItem)->GetValue() >>= xCursor; + + switch (rReq.GetSlot()) + { + case FN_QRY_INSERT: + { + if(pSourceItem && pCommandItem && pCommandTypeItem) + { + DBTextStruct_Impl* pNew = new DBTextStruct_Impl; + pNew->aDBData.sDataSource = sSourceArg; + pNew->aDBData.sCommand = sCommandArg; + pNew->aDBData.nCommandType = nCommandTypeArg; + pNew->aSelection = aSelection; + //if the cursor is NULL, it must be created inside InsertDBTextHdl + // because it called via a PostUserEvent + pNew->xCursor = xCursor; + pNew->xConnection = xConnection; + + Application::PostUserEvent( LINK( this, SwBaseShell, InsertDBTextHdl ), pNew ); + // the pNew will be removed in InsertDBTextHdl !! + } + } + break; + + case FN_QRY_MERGE_FIELD: + { + // we don't get any cursor, so we must create our own + bool bDisposeResultSet = false; + if ( !xCursor.is() ) + { + SwView &rSwView = GetView(); + xCursor = SwDBManager::createCursor(sSourceArg,sCommandArg,nCommandTypeArg,xConnection,&rSwView); + bDisposeResultSet = xCursor.is(); + } + + ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(sSourceArg); + aDescriptor[DataAccessDescriptorProperty::Command] <<= sCommandArg; + aDescriptor[DataAccessDescriptorProperty::Cursor] <<= xCursor; + aDescriptor[DataAccessDescriptorProperty::Selection] <<= aSelection; + aDescriptor[DataAccessDescriptorProperty::CommandType] <<= nCommandTypeArg; + + SwMergeDescriptor aMergeDesc( DBMGR_MERGE, *GetShellPtr(), aDescriptor ); + pDBManager->Merge(aMergeDesc); + + if ( bDisposeResultSet ) + ::comphelper::disposeComponent(xCursor); + } + break; + + case FN_QRY_INSERT_FIELD: + { + const SfxPoolItem* pColumnItem = nullptr; + const SfxPoolItem* pColumnNameItem = nullptr; + + pArgs->GetItemState(FN_DB_COLUMN_ANY, false, &pColumnItem); + pArgs->GetItemState(FN_DB_DATA_COLUMN_NAME_ANY, false, &pColumnNameItem); + + OUString sColumnName; + if(pColumnNameItem) + static_cast<const SfxUnoAnyItem*>(pColumnNameItem)->GetValue() >>= sColumnName; + OUString sDBName = sSourceArg + OUStringChar(DB_DELIM) + + sCommandArg + OUStringChar(DB_DELIM) + + OUString::number(nCommandTypeArg) + + OUStringChar(DB_DELIM) + sColumnName; + + SwFieldMgr aFieldMgr(GetShellPtr()); + SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, sDBName, OUString(), 0); + if(pConnectionItem) + aData.m_aDBConnection = static_cast<const SfxUnoAnyItem*>(pConnectionItem)->GetValue(); + if(pColumnItem) + aData.m_aDBColumn = static_cast<const SfxUnoAnyItem*>(pColumnItem)->GetValue(); + aFieldMgr.InsertField(aData); + SfxViewFrame* pViewFrame = GetView().GetViewFrame(); + uno::Reference< XDispatchRecorder > xRecorder = + pViewFrame->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + SfxRequest aReq( pViewFrame, FN_INSERT_DBFIELD ); + aReq.AppendItem( SfxUInt16Item(FN_PARAM_FIELD_TYPE, static_cast<sal_uInt16>(SwFieldTypesEnum::Database))); + aReq.AppendItem( SfxStringItem( FN_INSERT_DBFIELD, sDBName )); + aReq.AppendItem( SfxStringItem( FN_PARAM_1, sCommandArg )); + aReq.AppendItem( SfxStringItem( FN_PARAM_2, sColumnName )); + aReq.AppendItem( SfxInt32Item( FN_PARAM_3, nCommandTypeArg)); + aReq.Done(); + } + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +IMPL_LINK( SwBaseShell, InsertDBTextHdl, void*, p, void ) +{ + DBTextStruct_Impl* pDBStruct = static_cast<DBTextStruct_Impl*>(p); + if( pDBStruct ) + { + bool bDispose = false; + Reference< sdbc::XConnection> xConnection = pDBStruct->xConnection; + Reference<XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,pDBStruct->aDBData.sDataSource); + // #111987# the connection is disposed and so no parent has been found + if(xConnection.is() && !xSource.is()) + return; + + if ( !xConnection.is() ) + { + SwView &rSwView = GetView(); + xConnection = SwDBManager::GetConnection(pDBStruct->aDBData.sDataSource, xSource, &rSwView); + bDispose = true; + } + + Reference< XColumnsSupplier> xColSupp; + if(xConnection.is()) + xColSupp = SwDBManager::GetColumnSupplier(xConnection, + pDBStruct->aDBData.sCommand, + pDBStruct->aDBData.nCommandType == CommandType::QUERY ? + SwDBSelect::QUERY : SwDBSelect::TABLE); + + if( xColSupp.is() ) + { + SwDBData aDBData = pDBStruct->aDBData; + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwInsertDBColAutoPilot>pDlg (pFact->CreateSwInsertDBColAutoPilot(GetView(), + xSource, + xColSupp, + aDBData)); + if( RET_OK == pDlg->Execute() ) + { + Reference <XResultSet> xResSet = pDBStruct->xCursor; + pDlg->DataToDoc( pDBStruct->aSelection, xSource, xConnection, xResSet); + } + } + if ( bDispose ) + ::comphelper::disposeComponent(xConnection); + } + + delete pDBStruct; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/txtattr.cxx b/sw/source/uibase/shells/txtattr.cxx new file mode 100644 index 000000000..839e7f760 --- /dev/null +++ b/sw/source/uibase/shells/txtattr.cxx @@ -0,0 +1,845 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/whiter.hxx> +#include <svl/stritem.hxx> +#include <svl/ctloptions.hxx> +#include <swmodule.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/escapementitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/cmapitem.hxx> +#include <paratr.hxx> + +#include <fmtinfmt.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <uitool.hxx> +#include <textsh.hxx> +#include <swundo.hxx> +#include <fmtcol.hxx> + +#include <cmdid.h> +#include <globals.h> +#include <SwStyleNameMapper.hxx> +#include <swabstdlg.hxx> +#include <memory> + +const sal_uInt32 nFontInc = 40; // 2pt +const sal_uInt32 nFontMaxSz = 19998; // 999.9pt + +void SwTextShell::ExecCharAttr(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + const SfxItemSet *pArgs = rReq.GetArgs(); + int eState = STATE_TOGGLE; + sal_uInt16 nWhich = rReq.GetSlot(); + + if(pArgs ) + { + const SfxPoolItem* pItem; + pArgs->GetItemState(nWhich, false, &pItem); + eState = static_cast<const SfxBoolItem &>( pArgs-> + Get( nWhich )).GetValue() ? STATE_ON : STATE_OFF; + } + + SfxItemSet aSet( GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END-1>{} ); + if (STATE_TOGGLE == eState) + rSh.GetCurAttr( aSet ); + + switch ( nWhich ) + { + case FN_SET_SUB_SCRIPT: + case FN_SET_SUPER_SCRIPT: + { + SvxEscapement eEscape = SvxEscapement::Subscript; + switch (eState) + { + case STATE_TOGGLE: + { + short nTmpEsc = aSet.Get( RES_CHRATR_ESCAPEMENT ).GetEsc(); + eEscape = nWhich == FN_SET_SUPER_SCRIPT ? + SvxEscapement::Superscript: + SvxEscapement::Subscript; + if( (nWhich == FN_SET_SUB_SCRIPT && nTmpEsc < 0) || + (nWhich == FN_SET_SUPER_SCRIPT && nTmpEsc > 0) ) + eEscape = SvxEscapement::Off; + + SfxBindings& rBind = GetView().GetViewFrame()->GetBindings(); + if( nWhich == FN_SET_SUB_SCRIPT ) + rBind.SetState( SfxBoolItem( FN_SET_SUPER_SCRIPT, + false ) ); + else + rBind.SetState( SfxBoolItem( FN_SET_SUB_SCRIPT, + false ) ); + + } + break; + case STATE_ON: + eEscape = nWhich == FN_SET_SUPER_SCRIPT ? + SvxEscapement::Superscript: + SvxEscapement::Subscript; + break; + case STATE_OFF: + eEscape = SvxEscapement::Off; + break; + } + SvxEscapementItem aEscape( eEscape, RES_CHRATR_ESCAPEMENT ); + if(eEscape == SvxEscapement::Superscript) + aEscape.GetEsc() = DFLT_ESC_AUTO_SUPER; + else if(eEscape == SvxEscapement::Subscript) + aEscape.GetEsc() = DFLT_ESC_AUTO_SUB; + rSh.SetAttrItem( aEscape ); + rReq.AppendItem( aEscape ); + rReq.Done(); + } + break; + + case FN_SET_SMALL_CAPS: + { + SvxCaseMap eCaseMap = SvxCaseMap::SmallCaps; + switch (eState) + { + case STATE_TOGGLE: + { + SvxCaseMap eTmpCaseMap = aSet.Get(RES_CHRATR_CASEMAP).GetCaseMap(); + if (eTmpCaseMap == SvxCaseMap::SmallCaps) + eCaseMap = SvxCaseMap::NotMapped; + } + break; + case STATE_ON: + // Nothing to do, already set. + break; + case STATE_OFF: + eCaseMap = SvxCaseMap::NotMapped; + break; + } + SvxCaseMapItem aCaseMap(eCaseMap, RES_CHRATR_CASEMAP); + rSh.SetAttrItem(aCaseMap); + rReq.AppendItem(aCaseMap); + rReq.Done(); + } + break; + + case FN_UPDATE_STYLE_BY_EXAMPLE: + rSh.QuickUpdateStyle(); + rReq.Done(); + break; + + case SID_ULINE_VAL_NONE: + { + SvxUnderlineItem aUnderline(LINESTYLE_NONE, RES_CHRATR_UNDERLINE ); + rSh.SetAttrItem( aUnderline ); + rReq.AppendItem( aUnderline ); + rReq.Done(); + break; + } + + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + FontLineStyle eOld = aSet.Get(RES_CHRATR_UNDERLINE).GetLineStyle(); + FontLineStyle eNew = eOld; + + switch (nWhich) + { + case SID_ULINE_VAL_SINGLE: + eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; + break; + case SID_ULINE_VAL_DOUBLE: + eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; + break; + case SID_ULINE_VAL_DOTTED: + eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; + break; + } + + SvxUnderlineItem aUnderline(eNew, RES_CHRATR_UNDERLINE ); + rSh.SetAttrItem( aUnderline ); + rReq.AppendItem( aUnderline ); + rReq.Done(); + } + break; + case FN_REMOVE_DIRECT_CHAR_FORMATS: + if( !rSh.HasReadonlySel() && rSh.IsEndPara()) + rSh.DontExpandFormat(); + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } +} + +void SwTextShell::ExecCharAttrArgs(SfxRequest &rReq) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxItemSet* pArgs = rReq.GetArgs(); + bool bArgs = pArgs != nullptr && pArgs->Count() > 0; + SwWrtShell& rWrtSh = GetShell(); + SwTextFormatColl* pColl = nullptr; + + // Is only set if the whole paragraph is selected and AutoUpdateFormat is set. + if (rWrtSh.HasSelection() && rWrtSh.IsSelFullPara()) + { + pColl = rWrtSh.GetCurTextFormatColl(); + if ( pColl && !pColl->IsAutoUpdateFormat() ) + pColl = nullptr; + } + SfxItemPool& rPool = GetPool(); + sal_uInt16 nWhich = rPool.GetWhich( nSlot ); + switch (nSlot) + { + case FN_TXTATR_INET: + // Special treatment of the PoolId of the SwFormatInetFormat + if(bArgs) + { + const SfxPoolItem& rItem = pArgs->Get( nWhich ); + + SwFormatINetFormat aINetFormat( static_cast<const SwFormatINetFormat&>(rItem) ); + if ( USHRT_MAX == aINetFormat.GetVisitedFormatId() ) + { + OSL_ENSURE( false, "<SwTextShell::ExecCharAttrArgs(..)> - unexpected visited character format ID at hyperlink attribute" ); + aINetFormat.SetVisitedFormatAndId( + aINetFormat.GetVisitedFormat(), + SwStyleNameMapper::GetPoolIdFromUIName( aINetFormat.GetVisitedFormat(), SwGetPoolIdFromName::ChrFmt ) ); + } + if ( USHRT_MAX == aINetFormat.GetINetFormatId() ) + { + OSL_ENSURE( false, "<SwTextShell::ExecCharAttrArgs(..)> - unexpected unvisited character format ID at hyperlink attribute" ); + aINetFormat.SetINetFormatAndId( + aINetFormat.GetINetFormat(), + SwStyleNameMapper::GetPoolIdFromUIName( aINetFormat.GetINetFormat(), SwGetPoolIdFromName::ChrFmt ) ); + } + + if ( pColl ) + pColl->SetFormatAttr( aINetFormat ); + else + rWrtSh.SetAttrItem( aINetFormat ); + rReq.Done(); + } + break; + + case FN_GROW_FONT_SIZE: + case FN_SHRINK_FONT_SIZE: + { + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONTHEIGHT, rPool ); + rWrtSh.GetCurAttr( aSetItem.GetItemSet() ); + SfxItemSet aAttrSet( rPool, aSetItem.GetItemSet().GetRanges() ); + + SvtScriptType nScriptTypes = rWrtSh.GetScriptType(); + const SvxFontHeightItem* pSize( static_cast<const SvxFontHeightItem*>( + aSetItem.GetItemOfScript( nScriptTypes ) ) ); + std::vector<std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM> >> vItems; + // simple case where selected text has one size and + // (tdf#124919) selection is not multiple table cells + if (pSize && !rWrtSh.IsTableMode()) + { + // must create new one, otherwise document is without pam + SwPaM* pPaM = rWrtSh.GetCursor(); + vItems.emplace_back( pSize, std::make_unique<SwPaM>( *(pPaM->GetMark()), *(pPaM->GetPoint())) ); + } + else + vItems = rWrtSh.GetItemWithPaM( RES_CHRATR_FONTSIZE ); + + rWrtSh.StartUndo( SwUndoId::INSATTR ); + for( std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM> >& iPair : vItems ) + { + std::unique_ptr<SwPaM> pPaM = std::move(iPair.second); + const SfxPoolItem* pItem = iPair.first; + aSetItem.GetItemSet().ClearItem(); + rWrtSh.GetPaMAttr( pPaM.get(), aSetItem.GetItemSet() ); + aAttrSet.SetRanges( aSetItem.GetItemSet().GetRanges() ); + + pSize = static_cast<const SvxFontHeightItem*>( pItem ); + if (pSize) + { + SvxFontHeightItem aSize(*pSize); + + sal_uInt32 nSize = aSize.GetHeight(); + + if ( nSlot == FN_GROW_FONT_SIZE && ( nSize += nFontInc ) > nFontMaxSz ) + nSize = nFontMaxSz; + else if ( nSlot == FN_SHRINK_FONT_SIZE && ( nSize -= nFontInc ) < nFontInc ) + nSize = nFontInc; + + aSize.SetHeight( nSize ); + aSetItem.PutItemForScriptType( nScriptTypes, aSize ); + aAttrSet.Put( aSetItem.GetItemSet() ); + if( pColl ) + pColl->SetFormatAttr( aAttrSet ); + else + rWrtSh.SetAttrSet( aAttrSet, SetAttrMode::DEFAULT, pPaM.get() ); + } + } + rWrtSh.EndUndo( SwUndoId::INSATTR ); + rReq.Done(); + } + break; + + default: + OSL_FAIL("wrong dispatcher"); + return; + } +} + +void SwTextShell::ExecParaAttr(SfxRequest &rReq) +{ + SvxAdjust eAdjst; + sal_uInt16 ePropL; + const SfxItemSet* pArgs = rReq.GetArgs(); + + // Get both attributes immediately isn't more expensive!! + SfxItemSet aSet( GetPool(), + svl::Items<RES_PARATR_LINESPACING, RES_PARATR_ADJUST, + RES_FRAMEDIR, RES_FRAMEDIR>{} ); + + sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case SID_ATTR_PARA_ADJUST: + { + if( pArgs && SfxItemState::SET == pArgs->GetItemState(RES_PARATR_ADJUST) ) + { + const SvxAdjustItem& rAdj = pArgs->Get(RES_PARATR_ADJUST); + SvxAdjustItem aAdj( rAdj.GetAdjust(), RES_PARATR_ADJUST ); + if ( rAdj.GetAdjust() == SvxAdjust::Block ) + { + aAdj.SetLastBlock( rAdj.GetLastBlock() ); + aAdj.SetOneWord( rAdj.GetOneWord() ); + } + + aSet.Put(aAdj); + } + } + break; + case SID_ATTR_PARA_ADJUST_LEFT: eAdjst = SvxAdjust::Left; goto SET_ADJUST; + case SID_ATTR_PARA_ADJUST_RIGHT: eAdjst = SvxAdjust::Right; goto SET_ADJUST; + case SID_ATTR_PARA_ADJUST_CENTER: eAdjst = SvxAdjust::Center; goto SET_ADJUST; + case SID_ATTR_PARA_ADJUST_BLOCK: eAdjst = SvxAdjust::Block; goto SET_ADJUST; +SET_ADJUST: + { + aSet.Put(SvxAdjustItem(eAdjst,RES_PARATR_ADJUST)); + rReq.AppendItem( SfxBoolItem( GetPool().GetWhich(nSlot), true ) ); + } + break; + + case SID_ATTR_PARA_LINESPACE: + if(pArgs && SfxItemState::SET == pArgs->GetItemState( GetPool().GetWhich(nSlot) )) + { + SvxLineSpacingItem aLineSpace = static_cast<const SvxLineSpacingItem&>( pArgs->Get( + GetPool().GetWhich(nSlot))); + aSet.Put( aLineSpace ); + } + break; + case SID_ATTR_PARA_LINESPACE_10: ePropL = 100; goto SET_LINESPACE; + case SID_ATTR_PARA_LINESPACE_15: ePropL = 150; goto SET_LINESPACE; + case SID_ATTR_PARA_LINESPACE_20: ePropL = 200; goto SET_LINESPACE; + +SET_LINESPACE: + { + + SvxLineSpacingItem aLineSpacing(ePropL, RES_PARATR_LINESPACING ); + aLineSpacing.SetLineSpaceRule( SvxLineSpaceRule::Auto ); + if( 100 == ePropL ) + aLineSpacing.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off ); + else + aLineSpacing.SetPropLineSpace(ePropL); + aSet.Put( aLineSpacing ); + } + break; + + case SID_ATTR_PARA_LEFT_TO_RIGHT : + case SID_ATTR_PARA_RIGHT_TO_LEFT : + { + SfxItemSet aAdjustSet( GetPool(), + svl::Items<RES_PARATR_ADJUST, RES_PARATR_ADJUST>{} ); + GetShell().GetCurAttr(aAdjustSet); + bool bChgAdjust = false; + SfxItemState eAdjustState = aAdjustSet.GetItemState(RES_PARATR_ADJUST, false); + if(eAdjustState >= SfxItemState::DEFAULT) + { + SvxAdjust eAdjust = + aAdjustSet.Get(RES_PARATR_ADJUST).GetAdjust(); + bChgAdjust = (SvxAdjust::Left == eAdjust && SID_ATTR_PARA_RIGHT_TO_LEFT == nSlot) || + (SvxAdjust::Right == eAdjust && SID_ATTR_PARA_LEFT_TO_RIGHT == nSlot); + } + else + bChgAdjust = true; + + SvxFrameDirection eFrameDirection = + (SID_ATTR_PARA_LEFT_TO_RIGHT == nSlot) ? + SvxFrameDirection::Horizontal_LR_TB : SvxFrameDirection::Horizontal_RL_TB; + aSet.Put( SvxFrameDirectionItem( eFrameDirection, RES_FRAMEDIR ) ); + + if (bChgAdjust) + { + SvxAdjust eAdjust = (SID_ATTR_PARA_LEFT_TO_RIGHT == nSlot) ? + SvxAdjust::Left : SvxAdjust::Right; + SvxAdjustItem aAdjust( eAdjust, RES_PARATR_ADJUST ); + aSet.Put( aAdjust ); + aAdjust.SetWhich(SID_ATTR_PARA_ADJUST); + GetView().GetViewFrame()->GetBindings().SetState( aAdjust ); + // Toggle numbering alignment + const SwNumRule* pCurRule = GetShell().GetNumRuleAtCurrCursorPos(); + if( pCurRule ) + { + SvxNumRule aRule = pCurRule->MakeSvxNumRule(); + + for(sal_uInt16 i = 0; i < aRule.GetLevelCount(); i++) + { + SvxNumberFormat aFormat(aRule.GetLevel(i)); + if(SvxAdjust::Left == aFormat.GetNumAdjust()) + aFormat.SetNumAdjust( SvxAdjust::Right ); + + else if(SvxAdjust::Right == aFormat.GetNumAdjust()) + aFormat.SetNumAdjust( SvxAdjust::Left ); + + aRule.SetLevel(i, aFormat, aRule.Get(i) != nullptr); + } + SwNumRule aSetRule( pCurRule->GetName(), + pCurRule->Get( 0 ).GetPositionAndSpaceMode() ); + aSetRule.SetSvxRule( aRule, GetShell().GetDoc()); + aSetRule.SetAutoRule( true ); + // no start or continuation of a list - list style is only changed + GetShell().SetCurNumRule( aSetRule, false ); + } + } + } + break; + + default: + OSL_FAIL("wrong dispatcher"); + return; + } + SwWrtShell& rWrtSh = GetShell(); + SwTextFormatColl* pColl = rWrtSh.GetCurTextFormatColl(); + if(pColl && pColl->IsAutoUpdateFormat()) + { + rWrtSh.AutoUpdatePara(pColl, aSet); + } + else + rWrtSh.SetAttrSet( aSet, SetAttrMode::DEFAULT, nullptr, true); + rReq.Done(); +} + +void SwTextShell::ExecParaAttrArgs(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem *pItem = nullptr; + + sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState(GetPool().GetWhich(nSlot), false, &pItem); + switch ( nSlot ) + { + case FN_DROP_CHAR_STYLE_NAME: + if( pItem ) + { + OUString sCharStyleName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + SfxItemSet aSet(GetPool(), svl::Items<RES_PARATR_DROP, RES_PARATR_DROP>{}); + rSh.GetCurAttr(aSet); + SwFormatDrop aDropItem(aSet.Get(RES_PARATR_DROP)); + SwCharFormat* pFormat = nullptr; + if(!sCharStyleName.isEmpty()) + pFormat = rSh.FindCharFormatByName( sCharStyleName ); + aDropItem.SetCharFormat( pFormat ); + aSet.Put(aDropItem); + rSh.SetAttrSet(aSet); + } + break; + case FN_FORMAT_DROPCAPS: + { + if(pItem) + { + rSh.SetAttrItem(*pItem); + rReq.Done(); + } + else + { + SfxItemSet aSet(GetPool(), svl::Items<RES_PARATR_DROP, RES_PARATR_DROP, + HINT_END, HINT_END>{}); + rSh.GetCurAttr(aSet); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSwDropCapsDialog(GetView().GetFrameWeld(), aSet)); + if (pDlg->Execute() == RET_OK) + { + rSh.StartAction(); + rSh.StartUndo( SwUndoId::START ); + if ( SfxItemState::SET == aSet.GetItemState(HINT_END,false,&pItem) ) + { + if ( !static_cast<const SfxStringItem*>(pItem)->GetValue().isEmpty() ) + rSh.ReplaceDropText(static_cast<const SfxStringItem*>(pItem)->GetValue()); + } + rSh.SetAttrSet(*pDlg->GetOutputItemSet()); + rSh.EndUndo( SwUndoId::END ); + rSh.EndAction(); + rReq.Done(*pDlg->GetOutputItemSet()); + } + } + } + break; + case SID_ATTR_PARA_PAGEBREAK: + if(pItem) + { + rSh.SetAttrItem( *pItem ); + rReq.Done(); + } + break; + case SID_ATTR_PARA_MODEL: + { + if(pItem) + { + SfxItemSet aCoreSet( GetPool(), + svl::Items<RES_PAGEDESC, RES_PAGEDESC, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL>{}); + aCoreSet.Put(*pItem); + SfxToSwPageDescAttr( rSh, aCoreSet); + rSh.SetAttrSet(aCoreSet); + rReq.Done(); + } + } + break; + + default: + OSL_FAIL("wrong dispatcher"); + return; + } +} + +void SwTextShell::GetAttrState(SfxItemSet &rSet) +{ + SwWrtShell &rSh = GetShell(); + SfxItemPool& rPool = GetPool(); + SfxItemSet aCoreSet(rPool, aTextFormatCollSetRange); + // Request *all* text attributes from the core. + // fdo#78737: this is called from SvxRuler, which requires the list indents! + rSh.GetCurAttr(aCoreSet, /* bMergeIndentValuesOfNumRule = */ true); + + SfxWhichIter aIter(rSet); + sal_uInt16 nSlot = aIter.FirstWhich(); + bool bFlag = false; + SfxBoolItem aFlagItem; + const SfxPoolItem* pItem = nullptr; + SvxAdjust eAdjust = SvxAdjust::Left; + bool bAdjustGood = false; + SfxItemState eState = aCoreSet.GetItemState(RES_PARATR_ADJUST, false, &pItem); + + if( SfxItemState::DEFAULT == eState ) + pItem = &rPool.GetDefaultItem(RES_PARATR_ADJUST); + if( SfxItemState::DEFAULT <= eState ) + { + eAdjust = static_cast<const SvxAdjustItem* >( pItem)->GetAdjust(); + bAdjustGood = true; + } + + short nEsc = 0; + eState = aCoreSet.GetItemState(RES_CHRATR_ESCAPEMENT, false, &pItem); + if( SfxItemState::DEFAULT == eState ) + pItem = &rPool.GetDefaultItem(RES_CHRATR_ESCAPEMENT); + if( eState >= SfxItemState::DEFAULT ) + nEsc = static_cast<const SvxEscapementItem* >(pItem)->GetEsc(); + + sal_uInt16 nLineSpace = 0; + eState = aCoreSet.GetItemState(RES_PARATR_LINESPACING, false, &pItem); + if( SfxItemState::DEFAULT == eState ) + pItem = &rPool.GetDefaultItem(RES_PARATR_LINESPACING); + if( SfxItemState::DEFAULT <= eState && + static_cast<const SvxLineSpacingItem* >(pItem)->GetLineSpaceRule() == SvxLineSpaceRule::Auto ) + { + if(SvxInterLineSpaceRule::Off == + static_cast<const SvxLineSpacingItem* >(pItem)->GetInterLineSpaceRule()) + nLineSpace = 100; + else + nLineSpace = static_cast<const SvxLineSpacingItem* >(pItem)->GetPropLineSpace(); + } + + SvxCaseMap eCaseMap = SvxCaseMap::NotMapped; + eState = aCoreSet.GetItemState(RES_CHRATR_CASEMAP, false, &pItem); + if (eState == SfxItemState::DEFAULT) + pItem = &rPool.GetDefaultItem(RES_CHRATR_CASEMAP); + if (eState >= SfxItemState::DEFAULT) + eCaseMap = static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap(); + + while (nSlot) + { + switch(nSlot) + { + case FN_SET_SUPER_SCRIPT: + bFlag = 0 < nEsc; + break; + case FN_SET_SUB_SCRIPT: + bFlag = 0 > nEsc; + break; + case FN_SET_SMALL_CAPS: + bFlag = eCaseMap == SvxCaseMap::SmallCaps; + break; + case SID_ATTR_PARA_ADJUST_LEFT: + if (!bAdjustGood) + { + rSet.InvalidateItem( nSlot ); + nSlot = 0; + } + else + bFlag = SvxAdjust::Left == eAdjust; + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + if (!bAdjustGood) + { + rSet.InvalidateItem( nSlot ); + nSlot = 0; + } + else + bFlag = SvxAdjust::Right == eAdjust; + break; + case SID_ATTR_PARA_ADJUST_CENTER: + if (!bAdjustGood) + { + rSet.InvalidateItem( nSlot ); + nSlot = 0; + } + else + bFlag = SvxAdjust::Center == eAdjust; + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + if (!bAdjustGood) + { + rSet.InvalidateItem( nSlot ); + nSlot = 0; + } + else + { + bFlag = SvxAdjust::Block == eAdjust; + sal_uInt16 nHtmlMode = GetHtmlMode(rSh.GetView().GetDocShell()); + if((nHtmlMode & HTMLMODE_ON) && !(nHtmlMode & HTMLMODE_FULL_STYLES )) + { + rSet.DisableItem( nSlot ); + nSlot = 0; + } + } + } + break; + case SID_ATTR_PARA_LINESPACE_10: + bFlag = nLineSpace == 100; + break; + case SID_ATTR_PARA_LINESPACE_15: + bFlag = nLineSpace == 150; + break; + case SID_ATTR_PARA_LINESPACE_20: + bFlag = nLineSpace == 200; + break; + case FN_GROW_FONT_SIZE: + case FN_SHRINK_FONT_SIZE: + { + SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONTHEIGHT, + *rSet.GetPool() ); + aSetItem.GetItemSet().Put( aCoreSet, false ); + const SvxFontHeightItem* pSize( static_cast<const SvxFontHeightItem*>( + aSetItem.GetItemOfScript( rSh.GetScriptType() ) ) ); + + if( pSize ) // selection is of one size + { + sal_uInt32 nSize = pSize->GetHeight(); + if( nSize == nFontMaxSz ) + rSet.DisableItem( FN_GROW_FONT_SIZE ); + else if( nSize == nFontInc ) + rSet.DisableItem( FN_SHRINK_FONT_SIZE ); + } + else + { + std::vector<std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM> >> + vFontHeight = rSh.GetItemWithPaM( RES_CHRATR_FONTSIZE ); + for ( const std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM>>& aIt : vFontHeight ) + { + if (!aIt.first) + { + rSet.DisableItem(FN_GROW_FONT_SIZE); + rSet.DisableItem(FN_SHRINK_FONT_SIZE); + break; + } + pSize = static_cast<const SvxFontHeightItem*>( aIt.first ); + sal_uInt32 nSize = pSize->GetHeight(); + if( nSize == nFontMaxSz ) + rSet.DisableItem( FN_GROW_FONT_SIZE ); + else if( nSize == nFontInc ) + rSet.DisableItem( FN_SHRINK_FONT_SIZE ); + } + } + nSlot = 0; + } + break; + case SID_ULINE_VAL_NONE: + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + eState = aCoreSet.GetItemState(RES_CHRATR_UNDERLINE); + if( eState >= SfxItemState::DEFAULT ) + { + FontLineStyle eLineStyle = aCoreSet.Get(RES_CHRATR_UNDERLINE).GetLineStyle(); + + switch (nSlot) + { + case SID_ULINE_VAL_NONE: + rSet.Put(SfxBoolItem(nSlot, eLineStyle == LINESTYLE_NONE)); + break; + case SID_ULINE_VAL_SINGLE: + rSet.Put(SfxBoolItem(nSlot, eLineStyle == LINESTYLE_SINGLE)); + break; + case SID_ULINE_VAL_DOUBLE: + rSet.Put(SfxBoolItem(nSlot, eLineStyle == LINESTYLE_DOUBLE)); + break; + case SID_ULINE_VAL_DOTTED: + rSet.Put(SfxBoolItem(nSlot, eLineStyle == LINESTYLE_DOTTED)); + break; + } + } + else + rSet.InvalidateItem(nSlot); + nSlot = 0; + } + break; + case SID_ATTR_PARA_ADJUST: + if (!bAdjustGood) + rSet.InvalidateItem( nSlot ); + else + rSet.Put(SvxAdjustItem(eAdjust, SID_ATTR_PARA_ADJUST )); + nSlot = 0; + break; + case SID_ATTR_PARA_LRSPACE: + case SID_ATTR_PARA_LEFTSPACE: + case SID_ATTR_PARA_RIGHTSPACE: + case SID_ATTR_PARA_FIRSTLINESPACE: + { + eState = aCoreSet.GetItemState(RES_LR_SPACE); + if( eState >= SfxItemState::DEFAULT ) + { + SvxLRSpaceItem aLR = aCoreSet.Get( RES_LR_SPACE ); + aLR.SetWhich(nSlot); + rSet.Put(aLR); + } + else + rSet.InvalidateItem(nSlot); + nSlot = 0; + } + break; + + case SID_ATTR_PARA_LEFT_TO_RIGHT : + case SID_ATTR_PARA_RIGHT_TO_LEFT : + { + if ( !SW_MOD()->GetCTLOptions().IsCTLFontEnabled() ) + { + rSet.DisableItem( nSlot ); + nSlot = 0; + } + else + { + // is the item set? + sal_uInt16 nHtmlMode = GetHtmlMode(rSh.GetView().GetDocShell()); + if((!(nHtmlMode & HTMLMODE_ON) || (0 != (nHtmlMode & HTMLMODE_SOME_STYLES))) && + aCoreSet.GetItemState( RES_FRAMEDIR, false ) >= SfxItemState::DEFAULT) + { + SvxFrameDirection eFrameDir = + aCoreSet.Get(RES_FRAMEDIR).GetValue(); + if (SvxFrameDirection::Environment == eFrameDir) + { + eFrameDir = rSh.IsInRightToLeftText() ? + SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB; + } + bFlag = (SID_ATTR_PARA_LEFT_TO_RIGHT == nSlot && + SvxFrameDirection::Horizontal_LR_TB == eFrameDir) || + (SID_ATTR_PARA_RIGHT_TO_LEFT == nSlot && + SvxFrameDirection::Horizontal_RL_TB == eFrameDir); + } + else + { + rSet.InvalidateItem(nSlot); + nSlot = 0; + } + } + } + break; + + case SID_ATTR_CHAR_LANGUAGE: + case SID_ATTR_CHAR_KERNING: + case RES_PARATR_DROP: + { +#if OSL_DEBUG_LEVEL > 1 + const SfxPoolItem& rItem = aCoreSet.Get(GetPool().GetWhich(nSlot), true); + rSet.Put(rItem); +#else + rSet.Put(aCoreSet.Get( GetPool().GetWhich(nSlot))); +#endif + nSlot = 0; + } + break; + case SID_ATTR_PARA_MODEL: + { + SfxItemSet aTemp(GetPool(), + svl::Items<RES_PAGEDESC,RES_PAGEDESC, + SID_ATTR_PARA_MODEL,SID_ATTR_PARA_MODEL>{}); + aTemp.Put(aCoreSet); + ::SwToSfxPageDescAttr(aTemp); + rSet.Put(aTemp.Get(SID_ATTR_PARA_MODEL)); + nSlot = 0; + } + break; + case RES_TXTATR_INETFMT: + { + SfxItemSet aSet(GetPool(), svl::Items<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>{}); + rSh.GetCurAttr(aSet); + const SfxPoolItem& rItem = aSet.Get(RES_TXTATR_INETFMT); + rSet.Put(rItem); + nSlot = 0; + } + break; + + default: + // Do nothing + nSlot = 0; + break; + + } + if( nSlot ) + { + aFlagItem.SetWhich( nSlot ); + aFlagItem.SetValue( bFlag ); + rSet.Put( aFlagItem ); + } + nSlot = aIter.NextWhich(); + } + + rSet.Put(aCoreSet,false); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/txtcrsr.cxx b/sw/source/uibase/shells/txtcrsr.cxx new file mode 100644 index 000000000..4ca50d6da --- /dev/null +++ b/sw/source/uibase/shells/txtcrsr.cxx @@ -0,0 +1,459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <memory> + +#include <sfx2/request.hxx> +#include <svl/eitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <textsh.hxx> +#include <edtwin.hxx> +#include <doc.hxx> +#include <docsh.hxx> + +#include <cmdid.h> +#include <globals.hrc> + +#include <svx/svdouno.hxx> +#include <svx/fmshell.hxx> +#include <svx/sdrobjectfilter.hxx> + +using namespace ::com::sun::star; + +void SwTextShell::ExecBasicMove(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + GetView().GetEditWin().FlushInBuffer(); + const SfxItemSet *pArgs = rReq.GetArgs(); + bool bSelect = false; + sal_Int32 nCount = 1; + if(pArgs) + { + const SfxPoolItem *pItem; + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_MOVE_COUNT, true, &pItem)) + nCount = static_cast<const SfxInt32Item *>(pItem)->GetValue(); + if(SfxItemState::SET == pArgs->GetItemState(FN_PARAM_MOVE_SELECTION, true, &pItem)) + bSelect = static_cast<const SfxBoolItem *>(pItem)->GetValue(); + } + switch(rReq.GetSlot()) + { + case FN_CHAR_LEFT_SEL: + rReq.SetSlot( FN_CHAR_LEFT ); + bSelect = true; + break; + case FN_CHAR_RIGHT_SEL: + rReq.SetSlot( FN_CHAR_RIGHT ); + bSelect = true; + break; + case FN_LINE_UP_SEL: + rReq.SetSlot( FN_LINE_UP ); + bSelect = true; + break; + case FN_LINE_DOWN_SEL: + rReq.SetSlot( FN_LINE_DOWN ); + bSelect = true; + break; + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + GetView().GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + rReq.AppendItem( SfxInt32Item(FN_PARAM_MOVE_COUNT, nCount) ); + rReq.AppendItem( SfxBoolItem(FN_PARAM_MOVE_SELECTION, bSelect) ); + } + const sal_uInt16 nSlot = rReq.GetSlot(); + rReq.Done(); + // Get EditWin before calling the move functions (shell change may occur!) + SwEditWin& rTmpEditWin = GetView().GetEditWin(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + switch(nSlot) + { + case FN_CHAR_LEFT: + rSh.Left( CRSR_SKIP_CELLS, bSelect, 1, false, true ); + break; + case FN_CHAR_RIGHT: + rSh.Right( CRSR_SKIP_CELLS, bSelect, 1, false, true ); + break; + case FN_LINE_UP: + rSh.Up( bSelect ); + break; + case FN_LINE_DOWN: + rSh.Down( bSelect ); + break; + default: + OSL_FAIL("wrong Dispatcher"); + return; + } + } + + //#i42732# - notify the edit window that from now on we do not use the input language + rTmpEditWin.SetUseInputLanguage( false ); +} + +void SwTextShell::ExecMove(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + SwEditWin& rTmpEditWin = GetView().GetEditWin(); + rTmpEditWin.FlushInBuffer(); + + bool bRet = false; + switch ( rReq.GetSlot() ) + { + case FN_START_OF_LINE_SEL: + bRet = rSh.LeftMargin( true, false ); + break; + case FN_START_OF_LINE: + bRet = rSh.LeftMargin( false, false ); + break; + case FN_END_OF_LINE_SEL: + bRet = rSh.RightMargin( true, false ); + break; + case FN_END_OF_LINE: + bRet = rSh.RightMargin( false, false ); + break; + case FN_START_OF_DOCUMENT_SEL: + bRet = rSh.StartOfSection( true ); + break; + case FN_START_OF_DOCUMENT: + bRet = rSh.StartOfSection(); + break; + case FN_END_OF_DOCUMENT_SEL: + bRet = rSh.EndOfSection( true ); + break; + case FN_END_OF_DOCUMENT: + bRet = rSh.EndOfSection(); + break; + case FN_SELECT_WORD: + bRet = rSh.SelNearestWrd(); + break; + case FN_SELECT_SENTENCE: + rSh.SelSentence( nullptr ); + bRet = true; + break; + case SID_SELECTALL: + rSh.SelAll(); + bRet = true; + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + + if ( bRet ) + rReq.Done(); + else + rReq.Ignore(); + + //#i42732# - notify the edit window that from now on we do not use the input language + rTmpEditWin.SetUseInputLanguage( false ); +} + +void SwTextShell::ExecMovePage(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + GetView().GetEditWin().FlushInBuffer(); + + switch( rReq.GetSlot() ) + { + case FN_START_OF_NEXT_PAGE_SEL : + rSh.SttNxtPg( true ); + break; + case FN_START_OF_NEXT_PAGE: + rSh.SttNxtPg(); + break; + case FN_END_OF_NEXT_PAGE_SEL: + rSh.EndNxtPg( true ); + break; + case FN_END_OF_NEXT_PAGE: + rSh.EndNxtPg(); + break; + case FN_START_OF_PREV_PAGE_SEL: + rSh.SttPrvPg( true ); + break; + case FN_START_OF_PREV_PAGE: + rSh.SttPrvPg(); + break; + case FN_END_OF_PREV_PAGE_SEL: + rSh.EndPrvPg( true ); + break; + case FN_END_OF_PREV_PAGE: + rSh.EndPrvPg(); + break; + case FN_START_OF_PAGE_SEL: + rSh.SttPg( true ); + break; + case FN_START_OF_PAGE: + rSh.SttPg(); + break; + case FN_END_OF_PAGE_SEL: + rSh.EndPg( true ); + break; + case FN_END_OF_PAGE: + rSh.EndPg(); + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + rReq.Done(); +} + +void SwTextShell::ExecMoveCol(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + switch ( rReq.GetSlot() ) + { + case FN_START_OF_COLUMN: + rSh.StartOfColumn(); + break; + case FN_END_OF_COLUMN: + rSh.EndOfColumn(); + break; + case FN_START_OF_NEXT_COLUMN: + rSh.StartOfNextColumn() ; + break; + case FN_END_OF_NEXT_COLUMN: + rSh.EndOfNextColumn(); + break; + case FN_START_OF_PREV_COLUMN: + rSh.StartOfPrevColumn(); + break; + case FN_END_OF_PREV_COLUMN: + rSh.EndOfPrevColumn(); + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + rReq.Done(); +} + +void SwTextShell::ExecMoveLingu(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + GetView().GetEditWin().FlushInBuffer(); + + switch ( rReq.GetSlot() ) + { + case FN_NEXT_WORD_SEL: + rSh.NxtWrd( true ); + break; + case FN_NEXT_WORD: + rSh.NxtWrd(); + break; + case FN_START_OF_PARA_SEL: + rSh.SttPara( true ); + break; + case FN_START_OF_PARA: + rSh.SttPara(); + break; + case FN_END_OF_PARA_SEL: + rSh.EndPara( true ); + break; + case FN_END_OF_PARA: + rSh.EndPara(); + break; + case FN_PREV_WORD_SEL: + rSh.PrvWrd( true ); + break; + case FN_PREV_WORD: + rSh.PrvWrd(); + break; + case FN_NEXT_SENT_SEL: + rSh.FwdSentence( true ); + break; + case FN_NEXT_SENT: + rSh.FwdSentence(); + break; + case FN_PREV_SENT_SEL: + rSh.BwdSentence( true ); + break; + case FN_PREV_SENT: + rSh.BwdSentence(); + break; + case FN_NEXT_PARA: + rSh.FwdPara(); + break; + case FN_PREV_PARA: + rSh.BwdPara(); + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + rReq.Done(); +} + +void SwTextShell::ExecMoveMisc(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetShell(); + const sal_uInt16 nSlot = rReq.GetSlot(); + bool bSetRetVal = true, bRet = true; + switch ( nSlot ) + { + case SID_FM_TOGGLECONTROLFOCUS: + { + const SwDoc* pDoc = rSh.GetDoc(); + const SwDocShell* pDocShell = pDoc ? pDoc->GetDocShell() : nullptr; + const SwView* pView = pDocShell ? pDocShell->GetView() : nullptr; + const FmFormShell* pFormShell = pView ? pView->GetFormShell() : nullptr; + SdrView* pDrawView = pView ? pView->GetDrawView() : nullptr; + vcl::Window* pWindow = pView ? pView->GetWrtShell().GetWin() : nullptr; + + OSL_ENSURE( pFormShell && pDrawView && pWindow, "SwXTextView::ExecMoveMisc: no chance!" ); + if ( !pFormShell || !pDrawView || !pWindow ) + break; + + std::unique_ptr< svx::ISdrObjectFilter > pFilter( FmFormShell::CreateFocusableControlFilter( + *pDrawView, *pWindow ) ); + if (!pFilter) + break; + + const SdrObject* pNearestControl = rSh.GetBestObject( true, GotoObjFlags::DrawControl, false, pFilter.get() ); + if ( !pNearestControl ) + break; + + const SdrUnoObj* pUnoObject = dynamic_cast< const SdrUnoObj* >( pNearestControl ); + OSL_ENSURE( pUnoObject, "SwTextShell::ExecMoveMisc: GetBestObject returned nonsense!" ); + if ( !pUnoObject ) + break; + + pFormShell->ToggleControlFocus( *pUnoObject, *pDrawView, *pWindow ); + } + break; + case FN_CNTNT_TO_NEXT_FRAME: + bRet = rSh.GotoObj(true, GotoObjFlags::Any); + if(bRet) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + break; + case FN_NEXT_FOOTNOTE: + rSh.MoveCursor(); + bRet = rSh.GotoNextFootnoteAnchor(); + break; + case FN_PREV_FOOTNOTE: + rSh.MoveCursor(); + bRet = rSh.GotoPrevFootnoteAnchor(); + break; + case FN_TO_HEADER: + rSh.MoveCursor(); + if ( FrameTypeFlags::HEADER & rSh.GetFrameType(nullptr,false) ) + rSh.SttPg(); + else + { + bool bMoved = rSh.GotoHeaderText(); + if ( !bMoved ) + rSh.SttPg(); + } + bSetRetVal = false; + break; + case FN_TO_FOOTER: + rSh.MoveCursor(); + if ( FrameTypeFlags::FOOTER & rSh.GetFrameType(nullptr,false) ) + rSh.EndPg(); + else + { + bool bMoved = rSh.GotoFooterText(); + if ( !bMoved ) + rSh.EndPg(); + } + bSetRetVal = false; + break; + case FN_FOOTNOTE_TO_ANCHOR: + rSh.MoveCursor(); + if ( FrameTypeFlags::FOOTNOTE & rSh.GetFrameType(nullptr,false) ) + rSh.GotoFootnoteAnchor(); + else + rSh.GotoFootnoteText(); + bSetRetVal = false; + break; + case FN_TO_FOOTNOTE_AREA : + rSh.GotoFootnoteText(); + break; + case FN_PREV_TABLE: + bRet = rSh.MoveTable( GotoPrevTable, fnTableStart); + break; + case FN_NEXT_TABLE: + bRet = rSh.MoveTable(GotoNextTable, fnTableStart); + break; + case FN_GOTO_NEXT_REGION : + bRet = rSh.MoveRegion(GotoNextRegion, fnRegionStart); + break; + case FN_GOTO_PREV_REGION : + bRet = rSh.MoveRegion(GotoPrevRegion, fnRegionStart); + break; + case FN_NEXT_TOXMARK: + bRet = rSh.GotoNxtPrvTOXMark(); + break; + case FN_PREV_TOXMARK: + bRet = rSh.GotoNxtPrvTOXMark( false ); + break; + case FN_NEXT_TBLFML: + bRet = rSh.GotoNxtPrvTableFormula(); + break; + case FN_PREV_TBLFML: + bRet = rSh.GotoNxtPrvTableFormula( false ); + break; + case FN_NEXT_TBLFML_ERR: + bRet = rSh.GotoNxtPrvTableFormula( true, true ); + break; + case FN_PREV_TBLFML_ERR: + bRet = rSh.GotoNxtPrvTableFormula( false, true ); + break; + default: + OSL_FAIL("wrong dispatcher"); + return; + } + + if( bSetRetVal ) + rReq.SetReturnValue(SfxBoolItem( nSlot, bRet )); + rReq.Done(); + + bool bInHeader = true; + if ( rSh.IsInHeaderFooter( &bInHeader ) ) + { + if ( !bInHeader ) + { + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, true ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, false ); + } + else + { + rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, true ); + rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, false ); + } + + // Force repaint + rSh.GetWin()->Invalidate(); + } + if ( rSh.IsInHeaderFooter() != rSh.IsHeaderFooterEdit() ) + rSh.ToggleHeaderFooterEdit(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/txtnum.cxx b/sw/source/uibase/shells/txtnum.cxx new file mode 100644 index 000000000..aadc698ac --- /dev/null +++ b/sw/source/uibase/shells/txtnum.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/request.hxx> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <editeng/numitem.hxx> +#include <editeng/brushitem.hxx> +#include <numrule.hxx> + +#include <cmdid.h> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <wdocsh.hxx> +#include <poolfmt.hxx> +#include <textsh.hxx> +#include <swabstdlg.hxx> +#include <SwStyleNameMapper.hxx> +#include <sfx2/tabdlg.hxx> +#include <svx/nbdtmg.hxx> +#include <svx/nbdtmgfact.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <memory> + +void SwTextShell::ExecEnterNum(SfxRequest &rReq) +{ + //Because the record before any shell exchange. + switch(rReq.GetSlot()) + { + case FN_NUM_NUMBERING_ON: + { + GetShell().StartAllAction(); + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + bool bMode = !GetShell().SelectionHasNumber(); // #i29560# + if ( pItem ) + bMode = pItem->GetValue(); + else + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bMode ) ); + + if ( bMode != (GetShell().SelectionHasNumber()) ) // #i29560# + { + rReq.Done(); + if( bMode ) + GetShell().NumOn(); + else + GetShell().NumOrBulletOff(); // #i29560# + } + bool bNewResult = GetShell().SelectionHasNumber(); + if (bNewResult!=bMode) { + SfxBindings& rBindings = GetView().GetViewFrame()->GetBindings(); + SfxBoolItem aItem(FN_NUM_NUMBERING_ON,!bNewResult); + rBindings.SetState(aItem); + SfxBoolItem aNewItem(FN_NUM_NUMBERING_ON,bNewResult); + rBindings.SetState(aNewItem); + } + GetShell().EndAllAction(); + } + break; + case FN_NUM_BULLET_ON: + { + GetShell().StartAllAction(); + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + bool bMode = !GetShell().SelectionHasBullet(); // #i29560# + if ( pItem ) + bMode = pItem->GetValue(); + else + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bMode ) ); + + if ( bMode != (GetShell().SelectionHasBullet()) ) // #i29560# + { + rReq.Done(); + if( bMode ) + GetShell().BulletOn(); + else + GetShell().NumOrBulletOff(); // #i29560# + } + bool bNewResult = GetShell().SelectionHasBullet(); + if (bNewResult!=bMode) { + SfxBindings& rBindings = GetView().GetViewFrame()->GetBindings(); + SfxBoolItem aItem(FN_NUM_BULLET_ON,!bNewResult); + rBindings.SetState(aItem); + SfxBoolItem aNewItem(FN_NUM_BULLET_ON,bNewResult); + rBindings.SetState(aNewItem); + } + GetShell().EndAllAction(); + } + break; + + case FN_NUMBER_BULLETS: + case SID_OUTLINE_BULLET: + { + SfxItemSet aSet( GetPool(), + svl::Items<SID_HTML_MODE, SID_HTML_MODE, + SID_ATTR_NUMBERING_RULE, SID_PARAM_CUR_NUM_LEVEL>{} ); + SwDocShell* pDocSh = GetView().GetDocShell(); + const bool bHtml = dynamic_cast<SwWebDocShell*>( pDocSh ) != nullptr; + const SwNumRule* pNumRuleAtCurrentSelection = GetShell().GetNumRuleAtCurrentSelection(); + if ( pNumRuleAtCurrentSelection != nullptr ) + { + SvxNumRule aRule = pNumRuleAtCurrentSelection->MakeSvxNumRule(); + + //convert type of linked bitmaps from SVX_NUM_BITMAP to (SVX_NUM_BITMAP|LINK_TOKEN) + for ( sal_uInt16 i = 0; i < aRule.GetLevelCount(); i++ ) + { + SvxNumberFormat aFormat( aRule.GetLevel( i ) ); + if ( SVX_NUM_BITMAP == aFormat.GetNumberingType() ) + { + const SvxBrushItem* pBrush = aFormat.GetBrush(); + if(pBrush && !pBrush->GetGraphicLink().isEmpty()) + aFormat.SetNumberingType(SvxNumType(SVX_NUM_BITMAP|LINK_TOKEN)); + aRule.SetLevel(i, aFormat, aRule.Get(i) != nullptr); + } + } + if(bHtml) + aRule.SetFeatureFlag(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP, false); + + aSet.Put(SvxNumBulletItem(aRule)); + OSL_ENSURE( GetShell().GetNumLevel() < MAXLEVEL, + "<SwTextShell::ExecEnterNum()> - numbered node without valid list level. Serious defect." ); + sal_uInt16 nLevel = GetShell().GetNumLevel(); + if( nLevel < MAXLEVEL ) + { + nLevel = 1 << nLevel; + aSet.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, nLevel ) ); + } + } + else + { + SwNumRule aRule( GetShell().GetUniqueNumRuleName(), + // #i89178# + numfunc::GetDefaultPositionAndSpaceMode() ); + SvxNumRule aSvxRule = aRule.MakeSvxNumRule(); + const bool bRightToLeft = GetShell().IsInRightToLeftText(); + + if ( bHtml || bRightToLeft ) + { + for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n ) + { + SvxNumberFormat aFormat( aSvxRule.GetLevel( n ) ); + if ( n && bHtml ) + { + // 1/2" for HTML + aFormat.SetAbsLSpace(n * 720); + } + // #i38904# Default alignment for + // numbering/bullet should be rtl in rtl paragraph: + if ( bRightToLeft ) + { + aFormat.SetNumAdjust( SvxAdjust::Right ); + } + aSvxRule.SetLevel( n, aFormat, false ); + } + aSvxRule.SetFeatureFlag(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP, false); + } + aSet.Put( SvxNumBulletItem( aSvxRule ) ); + } + + aSet.Put( SfxBoolItem( SID_PARAM_NUM_PRESET,false )); + + // Before the dialogue of the HTML mode will be dropped at the Docshell. + pDocSh->PutItem(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(pDocSh))); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + weld::Window *pParent = rReq.GetFrameWeld(); + VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxNumBulletTabDialog(pParent, &aSet, GetShell())); + const SfxStringItem* pPageItem = rReq.GetArg<SfxStringItem>(FN_PARAM_1); + if ( pPageItem ) + pDlg->SetCurPageId( OUStringToOString( pPageItem->GetValue(), RTL_TEXTENCODING_UTF8 ) ); + + auto pRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + + pDlg->StartExecuteAsync([aSet, pDlg, pNumRuleAtCurrentSelection, pRequest, this](sal_Int32 nResult){ + if (RET_OK == nResult) + { + const SfxPoolItem* pItem; + if (SfxItemState::SET == pDlg->GetOutputItemSet()->GetItemState(SID_ATTR_NUMBERING_RULE, false, &pItem)) + { + pRequest->AppendItem(*pItem); + pRequest->Done(); + SvxNumRule* pSetRule = static_cast<const SvxNumBulletItem*>(pItem)->GetNumRule(); + pSetRule->UnLinkGraphics(); + SwNumRule aSetRule(pNumRuleAtCurrentSelection != nullptr + ? pNumRuleAtCurrentSelection->GetName() + : GetShell().GetUniqueNumRuleName(), + numfunc::GetDefaultPositionAndSpaceMode()); + aSetRule.SetSvxRule(*pSetRule, GetShell().GetDoc()); + aSetRule.SetAutoRule(true); + // No start of new list, if an existing list style is edited. + // Otherwise start a new list. + const bool bCreateList = (pNumRuleAtCurrentSelection == nullptr); + GetShell().SetCurNumRule(aSetRule, bCreateList); + } + // If the Dialog was leaved with OK but nothing was chosen then the + // numbering must be at least activated, if it is not already. + else if (pNumRuleAtCurrentSelection == nullptr + && SfxItemState::SET == aSet.GetItemState(SID_ATTR_NUMBERING_RULE, false, &pItem)) + { + pRequest->AppendItem(*pItem); + pRequest->Done(); + SvxNumRule* pSetRule = static_cast<const SvxNumBulletItem*>(pItem)->GetNumRule(); + SwNumRule aSetRule( + GetShell().GetUniqueNumRuleName(), + numfunc::GetDefaultPositionAndSpaceMode()); + aSetRule.SetSvxRule(*pSetRule, GetShell().GetDoc()); + aSetRule.SetAutoRule(true); + // start new list + GetShell().SetCurNumRule(aSetRule, true); + } + } + else if (RET_USER == nResult) + GetShell().DelNumRules(); + pDlg->disposeOnce(); + }); + } + break; + + default: + OSL_FAIL("wrong dispatcher"); + return; + } +} + + +void SwTextShell::ExecSetNumber(SfxRequest const &rReq) +{ + const sal_uInt16 nSlot = rReq.GetSlot(); + switch ( nSlot ) + { + case FN_SVX_SET_NUMBER: + case FN_SVX_SET_BULLET: + case FN_SVX_SET_OUTLINE: + { + const SfxUInt16Item* pItem = rReq.GetArg<SfxUInt16Item>(nSlot); + if ( pItem != nullptr ) + { + const sal_uInt16 nChoosenItemIdx = pItem->GetValue(); + svx::sidebar::NBOType nNBOType = svx::sidebar::NBOType::Bullets; + if ( nSlot == FN_SVX_SET_NUMBER ) + nNBOType = svx::sidebar::NBOType::Numbering; + else if ( nSlot == FN_SVX_SET_OUTLINE ) + nNBOType = svx::sidebar::NBOType::Outline; + + svx::sidebar::NBOTypeMgrBase* pNBOTypeMgr = svx::sidebar::NBOutlineTypeMgrFact::CreateInstance( nNBOType ); + + if ( pNBOTypeMgr != nullptr ) + { + const SwNumRule* pNumRuleAtCurrentSelection = GetShell().GetNumRuleAtCurrentSelection(); + sal_uInt16 nActNumLvl = USHRT_MAX; + if ( pNumRuleAtCurrentSelection != nullptr ) + { + const sal_uInt16 nLevel = GetShell().GetNumLevel(); + if ( nLevel < MAXLEVEL ) + { + nActNumLvl = 1 << nLevel; + } + } + SwNumRule aNewNumRule( + pNumRuleAtCurrentSelection != nullptr ? pNumRuleAtCurrentSelection->GetName() : GetShell().GetUniqueNumRuleName(), + numfunc::GetDefaultPositionAndSpaceMode() ); + SvxNumRule aNewSvxNumRule = pNumRuleAtCurrentSelection != nullptr + ? pNumRuleAtCurrentSelection->MakeSvxNumRule() + : aNewNumRule.MakeSvxNumRule(); + + OUString aNumCharFormat, aBulletCharFormat; + SwStyleNameMapper::FillUIName( RES_POOLCHR_NUM_LEVEL, aNumCharFormat ); + SwStyleNameMapper::FillUIName( RES_POOLCHR_BULLET_LEVEL, aBulletCharFormat ); + + SfxAllItemSet aSet( GetPool() ); + aSet.Put( SfxStringItem( SID_NUM_CHAR_FMT, aNumCharFormat ) ); + aSet.Put( SfxStringItem( SID_BULLET_CHAR_FMT, aBulletCharFormat ) ); + aSet.Put( SvxNumBulletItem( aNewSvxNumRule, SID_ATTR_NUMBERING_RULE ) ); + + pNBOTypeMgr->SetItems( &aSet ); + pNBOTypeMgr->ApplyNumRule( aNewSvxNumRule, nChoosenItemIdx - 1, nActNumLvl ); + + aNewNumRule.SetSvxRule( aNewSvxNumRule, GetShell().GetDoc() ); + aNewNumRule.SetAutoRule( true ); + const bool bCreateNewList = ( pNumRuleAtCurrentSelection == nullptr ); + GetShell().SetCurNumRule( aNewNumRule, bCreateNewList ); + } + } + } + break; + + default: + OSL_ENSURE(false, "wrong Dispatcher"); + return; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageColumnControl.cxx b/sw/source/uibase/sidebar/PageColumnControl.cxx new file mode 100644 index 000000000..1675867ac --- /dev/null +++ b/sw/source/uibase/sidebar/PageColumnControl.cxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "PageColumnControl.hxx" +#include <PageColumnPopup.hxx> + +#include <cmdid.h> + +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/intitem.hxx> +#include <svx/pageitem.hxx> + +namespace sw::sidebar { + +PageColumnControl::PageColumnControl(PageColumnPopup* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/swriter/ui/pagecolumncontrol.ui", "PageColumnControl") + , m_xMoreButton(m_xBuilder->weld_button("moreoptions")) + , m_xControl(pControl) +{ + bool bLandscape = false; + const SfxPoolItem *pItem; + if ( SfxViewFrame::Current() ) + { + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE, pItem ); + bLandscape = static_cast<const SvxPageItem*>(pItem)->IsLandscape(); + } + + if ( bLandscape ) + { + m_xOneColumn = m_xBuilder->weld_button("column1L"); + m_xTwoColumns = m_xBuilder->weld_button("column2L"); + m_xThreeColumns = m_xBuilder->weld_button("column3L"); + m_xLeft = m_xBuilder->weld_button("columnleftL"); + m_xRight = m_xBuilder->weld_button("columnrightL"); + } + else + { + m_xOneColumn = m_xBuilder->weld_button("column1"); + m_xTwoColumns = m_xBuilder->weld_button( "column2"); + m_xThreeColumns = m_xBuilder->weld_button("column3"); + m_xLeft = m_xBuilder->weld_button("columnleft"); + m_xRight = m_xBuilder->weld_button("columnright"); + } + + m_xOneColumn->show(); + m_xTwoColumns->show(); + m_xThreeColumns->show(); + m_xLeft->show(); + m_xRight->show(); + + m_xOneColumn->connect_clicked( LINK( this, PageColumnControl, ColumnButtonClickHdl_Impl ) ); + m_xTwoColumns->connect_clicked( LINK( this, PageColumnControl, ColumnButtonClickHdl_Impl ) ); + m_xThreeColumns->connect_clicked( LINK( this, PageColumnControl, ColumnButtonClickHdl_Impl ) ); + m_xLeft->connect_clicked( LINK( this, PageColumnControl, ColumnButtonClickHdl_Impl ) ); + m_xRight->connect_clicked( LINK( this, PageColumnControl, ColumnButtonClickHdl_Impl ) ); + + m_xMoreButton->connect_clicked( LINK( this, PageColumnControl, MoreButtonClickHdl_Impl ) ); +} + +void PageColumnControl::GrabFocus() +{ + m_xMoreButton->grab_focus(); +} + +PageColumnControl::~PageColumnControl() +{ +} + +void PageColumnControl::ExecuteColumnChange( const sal_uInt16 nColumnType ) +{ + std::unique_ptr<SfxInt16Item> mpPageColumnTypeItem( new SfxInt16Item(SID_ATTR_PAGE_COLUMN) ); + mpPageColumnTypeItem->SetValue( nColumnType ); + if ( SfxViewFrame::Current() ) + SfxViewFrame::Current()->GetBindings().GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLUMN, + SfxCallMode::RECORD, { mpPageColumnTypeItem.get() }); +} + +IMPL_LINK( PageColumnControl, ColumnButtonClickHdl_Impl, weld::Button&, rButton, void ) +{ + if ( &rButton == m_xOneColumn.get() ) + ExecuteColumnChange( 1 ); + else if ( &rButton == m_xTwoColumns.get() ) + ExecuteColumnChange( 2 ); + else if ( &rButton == m_xThreeColumns.get() ) + ExecuteColumnChange( 3 ); + else if ( &rButton == m_xLeft.get() ) + ExecuteColumnChange( 4 ); + else if ( &rButton == m_xRight.get() ) + ExecuteColumnChange( 5 ); + + m_xControl->EndPopupMode(); +} + +IMPL_LINK_NOARG( PageColumnControl, MoreButtonClickHdl_Impl, weld::Button&, void ) +{ + if ( SfxViewFrame::Current() ) + SfxViewFrame::Current()->GetBindings().GetDispatcher()->Execute( FN_FORMAT_PAGE_COLUMN_DLG, SfxCallMode::ASYNCHRON ); + m_xControl->EndPopupMode(); +} + +} // end of namespace sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageColumnControl.hxx b/sw/source/uibase/sidebar/PageColumnControl.hxx new file mode 100644 index 000000000..140c94872 --- /dev/null +++ b/sw/source/uibase/sidebar/PageColumnControl.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_SIDEBAR_PAGECOLUMNCONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGECOLUMNCONTROL_HXX + +#include <svtools/toolbarmenu.hxx> +#include <vcl/weld.hxx> + +class PageColumnPopup; + +namespace sw::sidebar { + +class PageColumnControl final : public WeldToolbarPopup +{ +public: + explicit PageColumnControl(PageColumnPopup* pControl, weld::Widget* pParent); + virtual void GrabFocus() override; + virtual ~PageColumnControl() override; + +private: + std::unique_ptr<weld::Button> m_xOneColumn; + std::unique_ptr<weld::Button> m_xTwoColumns; + std::unique_ptr<weld::Button> m_xThreeColumns; + std::unique_ptr<weld::Button> m_xLeft; + std::unique_ptr<weld::Button> m_xRight; + std::unique_ptr<weld::Button> m_xMoreButton; + + rtl::Reference<PageColumnPopup> m_xControl; + + static void ExecuteColumnChange( const sal_uInt16 nColumnType ); + + DECL_LINK( ColumnButtonClickHdl_Impl, weld::Button&, void ); + DECL_LINK( MoreButtonClickHdl_Impl, weld::Button&, void ); +}; + +} // end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageColumnPopup.cxx b/sw/source/uibase/sidebar/PageColumnPopup.cxx new file mode 100644 index 000000000..b0b9e07fc --- /dev/null +++ b/sw/source/uibase/sidebar/PageColumnPopup.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 <PageColumnPopup.hxx> +#include "PageColumnControl.hxx" +#include <svl/intitem.hxx> +#include <vcl/toolbox.hxx> + +PageColumnPopup::PageColumnPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext) + : PopupWindowController(rContext, nullptr, OUString()) +{ +} + +void PageColumnPopup::initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) +{ + PopupWindowController::initialize(rArguments); + + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL) + pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId)); +} + +PageColumnPopup::~PageColumnPopup() +{ +} + +std::unique_ptr<WeldToolbarPopup> PageColumnPopup::weldPopupWindow() +{ + return std::make_unique<sw::sidebar::PageColumnControl>(this, m_pToolbar); +} + +VclPtr<vcl::Window> PageColumnPopup::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sw::sidebar::PageColumnControl>(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +OUString PageColumnPopup::getImplementationName() +{ + return "lo.writer.PageColumnToolBoxControl"; +} + +css::uno::Sequence<OUString> PageColumnPopup::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_PageColumnToolBoxControl_get_implementation( + css::uno::XComponentContext* rContext, + css::uno::Sequence<css::uno::Any> const & ) +{ + return cppu::acquire(new PageColumnPopup(rContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageFooterPanel.cxx b/sw/source/uibase/sidebar/PageFooterPanel.cxx new file mode 100644 index 000000000..f7efb76a0 --- /dev/null +++ b/sw/source/uibase/sidebar/PageFooterPanel.cxx @@ -0,0 +1,295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svx/dlgutil.hxx> +#include <svx/rulritem.hxx> +#include <svx/svdtrans.hxx> +#include "PageFooterPanel.hxx" +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <cmdid.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +namespace sw::sidebar{ + +VclPtr<vcl::Window> PageFooterPanel::Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no parent window given to PageFooterPanel::Create", nullptr, 0); + if( !rxFrame.is() ) + throw ::com::sun::star::lang::IllegalArgumentException("no XFrame given to PageFooterPanel::Create", nullptr, 0); + + return VclPtr<PageFooterPanel>::Create(pParent, rxFrame, pBindings); +} + +void PageFooterPanel::SetMarginsAndSpacingFieldUnit() +{ + SpacingListBox::Fill(IsInch(meFUnit) ? SpacingType::SPACING_INCH : SpacingType::SPACING_CM, *mxFooterSpacingLB); + SpacingListBox::Fill(IsInch(meFUnit) ? SpacingType::MARGINS_INCH : SpacingType::MARGINS_CM, *mxFooterMarginPresetLB); +} + +PageFooterPanel::PageFooterPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) : + PanelLayout(pParent, "PageFooterPanel", "modules/swriter/ui/pagefooterpanel.ui", rxFrame), + mpBindings( pBindings ), + maHFToggleController(SID_ATTR_PAGE_FOOTER, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings,*this), + maFooterLRMarginController(SID_ATTR_PAGE_FOOTER_LRMARGIN, *pBindings, *this), + maFooterSpacingController(SID_ATTR_PAGE_FOOTER_SPACING, *pBindings, *this), + maFooterLayoutController(SID_ATTR_PAGE_FOOTER_LAYOUT, *pBindings, *this), + meFUnit(GetModuleFieldUnit()), + aCustomEntry(), + mpFooterItem( new SfxBoolItem(SID_ATTR_PAGE_FOOTER) ), + mpFooterLRMarginItem( new SvxLongLRSpaceItem(0, 0, SID_ATTR_PAGE_FOOTER_LRMARGIN)), + mpFooterSpacingItem( new SvxLongULSpaceItem(0, 0, SID_ATTR_PAGE_FOOTER_SPACING)), + mpFooterLayoutItem( new SfxInt16Item(SID_ATTR_PAGE_FOOTER_LAYOUT)), + mxFooterToggle(m_xBuilder->weld_check_button("footertoggle")), + mxFooterSpacingLB(m_xBuilder->weld_combo_box("spacingpreset")), + mxFooterMarginPresetLB(m_xBuilder->weld_combo_box("footermarginpreset")), + mxFooterLayoutLB(m_xBuilder->weld_combo_box("samecontentLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")) +{ + Initialize(); +} + +PageFooterPanel::~PageFooterPanel() +{ + disposeOnce(); +} + +void PageFooterPanel::dispose() +{ + mxFooterToggle.reset(); + maMetricController.dispose(); + mxFooterSpacingLB.reset(); + mxFooterLayoutLB.reset(); + mxFooterMarginPresetLB.reset(); + mxCustomEntry.reset(); + + PanelLayout::dispose(); +} + +FieldUnit PageFooterPanel::GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState) +{ + FieldUnit eUnit; + + if (pState && eState >= SfxItemState::DEFAULT) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + else + eUnit = GetModuleFieldUnit(); + + return eUnit; +} + +void PageFooterPanel::Initialize() +{ + SameContentListBox::Fill(*mxFooterLayoutLB); + + SetMarginsAndSpacingFieldUnit(); + + aCustomEntry = mxCustomEntry->get_label(); + mxFooterToggle->connect_toggled( LINK(this, PageFooterPanel, FooterToggleHdl) ); + mxFooterMarginPresetLB->connect_changed( LINK(this, PageFooterPanel, FooterLRMarginHdl)); + mxFooterSpacingLB->connect_changed( LINK(this, PageFooterPanel, FooterSpacingHdl)); + mxFooterLayoutLB->connect_changed( LINK(this, PageFooterPanel, FooterLayoutHdl)); + + mpBindings->Invalidate(SID_ATTR_METRIC); + mpBindings->Invalidate(SID_ATTR_PAGE_FOOTER); + mpBindings->Invalidate(SID_ATTR_PAGE_FOOTER_LRMARGIN); + mpBindings->Invalidate(SID_ATTR_PAGE_FOOTER_SPACING); + mpBindings->Invalidate(SID_ATTR_PAGE_FOOTER_LAYOUT); +} + +void PageFooterPanel::UpdateFooterCheck() +{ + if (mxFooterToggle->get_active()) + { + mxFooterSpacingLB->set_sensitive(true); + mxFooterLayoutLB->set_sensitive(true); + mxFooterMarginPresetLB->set_sensitive(true); + } + else + { + mxFooterSpacingLB->set_sensitive(false); + mxFooterLayoutLB->set_sensitive(false); + mxFooterMarginPresetLB->set_sensitive(false); + } +} + +void PageFooterPanel::UpdateMarginControl() +{ + sal_uInt16 nLeft = mpFooterLRMarginItem->GetLeft(); + sal_uInt16 nRight = mpFooterLRMarginItem->GetRight(); + sal_uInt16 nCount = mxFooterMarginPresetLB->get_count(); + if(nLeft == nRight) + { + for (sal_uInt16 i = 0; i < nCount; ++i) + { + if (mxFooterMarginPresetLB->get_id(i).toUInt32() == nLeft) + { + mxFooterMarginPresetLB->set_active(i); + int nCustomEntry = mxFooterMarginPresetLB->find_text(aCustomEntry); + if (nCustomEntry != -1) + mxFooterMarginPresetLB->remove(nCustomEntry); + return; + } + } + } + mxFooterMarginPresetLB->append_text(aCustomEntry); + mxFooterMarginPresetLB->set_active_text(aCustomEntry); +} + +void PageFooterPanel::UpdateSpacingControl() +{ + sal_uInt16 nBottom = mpFooterSpacingItem->GetUpper(); + sal_uInt16 nCount = mxFooterSpacingLB->get_count(); + for (sal_uInt16 i = 0; i < nCount; ++i) + { + if (mxFooterSpacingLB->get_id(i).toUInt32() == nBottom) + { + mxFooterSpacingLB->set_active(i); + int nCustomEntry = mxFooterSpacingLB->find_text(aCustomEntry); + if (nCustomEntry != -1) + mxFooterSpacingLB->remove(nCustomEntry); + return; + } + } + mxFooterSpacingLB->append_text(aCustomEntry); + mxFooterSpacingLB->set_active_text(aCustomEntry); +} + +void PageFooterPanel::UpdateLayoutControl() +{ + sal_uInt16 nLayout = mpFooterLayoutItem->GetValue(); + mxFooterLayoutLB->set_active(nLayout); +} + +void PageFooterPanel::NotifyItemUpdate( + const sal_uInt16 nSid, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + if (IsDisposed()) + return; + + switch(nSid) + { + case SID_ATTR_PAGE_FOOTER: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SfxBoolItem*>( pState) ) + { + mpFooterItem.reset( static_cast<SfxBoolItem*>(pState->Clone()) ); + mxFooterToggle->set_active(mpFooterItem->GetValue()); + UpdateFooterCheck(); + } + } + break; + case SID_ATTR_PAGE_FOOTER_LRMARGIN: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SvxLongLRSpaceItem*>( pState) ) + { + mpFooterLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pState->Clone()) ); + UpdateMarginControl(); + } + } + break; + case SID_ATTR_PAGE_FOOTER_SPACING: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SvxLongULSpaceItem*>( pState) ) + { + mpFooterSpacingItem.reset(static_cast<SvxLongULSpaceItem*>(pState->Clone()) ); + UpdateSpacingControl(); + } + } + break; + case SID_ATTR_PAGE_FOOTER_LAYOUT: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SfxInt16Item*>( pState) ) + { + mpFooterLayoutItem.reset(static_cast<SfxInt16Item*>(pState->Clone()) ); + UpdateLayoutControl(); + } + } + break; + case SID_ATTR_METRIC: + { + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (meFUnit != eFUnit) + { + meFUnit = eFUnit; + SetMarginsAndSpacingFieldUnit(); + UpdateSpacingControl(); + UpdateMarginControl(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG( PageFooterPanel, FooterToggleHdl, weld::ToggleButton&, void ) +{ + bool IsChecked = mxFooterToggle->get_active(); + mpFooterItem->SetValue(IsChecked); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_FOOTER, SfxCallMode::RECORD, { mpFooterItem.get() } ); + UpdateFooterCheck(); +} + +IMPL_LINK_NOARG( PageFooterPanel, FooterLRMarginHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxFooterMarginPresetLB->get_active_id().toUInt32(); + mpFooterLRMarginItem->SetLeft(nVal); + mpFooterLRMarginItem->SetRight(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_FOOTER_LRMARGIN, + SfxCallMode::RECORD, { mpFooterLRMarginItem.get() } ); +} + +IMPL_LINK_NOARG( PageFooterPanel, FooterSpacingHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxFooterSpacingLB->get_active_id().toUInt32(); + mpFooterSpacingItem->SetUpper(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_FOOTER_SPACING, + SfxCallMode::RECORD, { mpFooterSpacingItem.get() } ); + +} +IMPL_LINK_NOARG( PageFooterPanel, FooterLayoutHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxFooterLayoutLB->get_active(); + mpFooterLayoutItem->SetValue(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_FOOTER_LAYOUT, + SfxCallMode::RECORD, { mpFooterLayoutItem.get() } ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageFooterPanel.hxx b/sw/source/uibase/sidebar/PageFooterPanel.hxx new file mode 100644 index 000000000..849a3165d --- /dev/null +++ b/sw/source/uibase/sidebar/PageFooterPanel.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_SIDEBAR_PAGEFOOTERPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEFOOTERPANEL_HXX + +#include <memory> +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <sfx2/sidebar/ControllerItem.hxx> + +#include <svx/rulritem.hxx> + +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <svl/eitem.hxx> +#include <svx/spacinglistbox.hxx> +#include <svx/samecontentlistbox.hxx> + +namespace sw::sidebar { + +class PageFooterPanel: + public PanelLayout, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + static VclPtr<vcl::Window> Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + + virtual void NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + SfxBindings* GetBindings() const { return mpBindings; } + PageFooterPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + virtual ~PageFooterPanel() override; + virtual void dispose() override; + +private: + + SfxBindings* mpBindings; + + ::sfx2::sidebar::ControllerItem maHFToggleController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maFooterLRMarginController; + ::sfx2::sidebar::ControllerItem maFooterSpacingController; + ::sfx2::sidebar::ControllerItem maFooterLayoutController; + + FieldUnit meFUnit; + + OUString aCustomEntry; + + void Initialize(); + void SetMarginsAndSpacingFieldUnit(); + void UpdateFooterCheck(); + void UpdateMarginControl(); + void UpdateSpacingControl(); + void UpdateLayoutControl(); + + ::std::unique_ptr<SfxBoolItem> mpFooterItem; + ::std::unique_ptr<SvxLongLRSpaceItem> mpFooterLRMarginItem; + ::std::unique_ptr<SvxLongULSpaceItem> mpFooterSpacingItem; + ::std::unique_ptr<SfxInt16Item> mpFooterLayoutItem; + + std::unique_ptr<weld::CheckButton> mxFooterToggle; + std::unique_ptr<weld::ComboBox> mxFooterSpacingLB; + std::unique_ptr<weld::ComboBox> mxFooterMarginPresetLB; + std::unique_ptr<weld::ComboBox> mxFooterLayoutLB; + std::unique_ptr<weld::Label> mxCustomEntry; + + static FieldUnit GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState); + + DECL_LINK( FooterToggleHdl, weld::ToggleButton&, void ); + DECL_LINK( FooterLRMarginHdl, weld::ComboBox&, void); + DECL_LINK( FooterSpacingHdl, weld::ComboBox&, void); + DECL_LINK( FooterLayoutHdl, weld::ComboBox&, void); +}; + +} //end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageFormatPanel.cxx b/sw/source/uibase/sidebar/PageFormatPanel.cxx new file mode 100644 index 000000000..7a13c6553 --- /dev/null +++ b/sw/source/uibase/sidebar/PageFormatPanel.cxx @@ -0,0 +1,432 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swtypes.hxx> +#include <svl/intitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/paperinf.hxx> +#include <svx/dlgutil.hxx> +#include <svx/rulritem.hxx> +#include <svx/svdtrans.hxx> +#include "PageFormatPanel.hxx" +#include "PageMarginUtils.hxx" +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/module.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewfrm.hxx> +#include <pageformatpanel.hrc> +#include <cmdid.h> +#include <svtools/optionsdrawinglayer.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +namespace sw::sidebar{ + +VclPtr<vcl::Window> PageFormatPanel::Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no parent window given to PageFormatPanel::Create", nullptr, 0); + if( !rxFrame.is() ) + throw ::com::sun::star::lang::IllegalArgumentException("no XFrame given to PageFormatPanel::Create", nullptr, 0); + + return VclPtr<PageFormatPanel>::Create(pParent, rxFrame, pBindings); +} + +void PageFormatPanel::SetMarginFieldUnit() +{ + auto nSelected = mxMarginSelectBox->get_active(); + mxMarginSelectBox->clear(); + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + if (IsInch(meFUnit)) + { + OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_INCH); ++i) + { + OUString sStr = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_INCH[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SwResId(RID_PAGEFORMATPANEL_MARGINS_INCH[i].first).replaceFirst("%1", sStr)); + } + } + else + { + OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::CM); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_CM); ++i) + { + OUString sStr = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_CM[i].second, 2, true, false) + " " + sSuffix; + mxMarginSelectBox->append_text(SwResId(RID_PAGEFORMATPANEL_MARGINS_CM[i].first).replaceFirst("%1", sStr)); + } + } + mxMarginSelectBox->set_active(nSelected); +} + +PageFormatPanel::PageFormatPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) : + PanelLayout(pParent, "PageFormatPanel", "modules/swriter/ui/pageformatpanel.ui", rxFrame), + mpBindings( pBindings ), + mxPaperSizeBox(new SvxPaperSizeListBox(m_xBuilder->weld_combo_box("papersize"))), + mxPaperWidth(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("paperwidth", FieldUnit::CM))), + mxPaperHeight(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("paperheight", FieldUnit::CM))), + mxPaperOrientation(m_xBuilder->weld_combo_box("paperorientation")), + mxMarginSelectBox(m_xBuilder->weld_combo_box("marginLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")), + maPaperSizeController(SID_ATTR_PAGE_SIZE, *pBindings, *this), + maPaperOrientationController(SID_ATTR_PAGE, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings,*this), + maSwPageLRControl(SID_ATTR_PAGE_LRSPACE, *pBindings, *this), + maSwPageULControl(SID_ATTR_PAGE_ULSPACE, *pBindings, *this), + mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ), + mpPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ), + mpPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ), + meFUnit(GetModuleFieldUnit()), + meUnit(), + aCustomEntry() +{ + Initialize(); +} + +PageFormatPanel::~PageFormatPanel() +{ + disposeOnce(); +} + +void PageFormatPanel::dispose() +{ + mxPaperSizeBox.reset(); + mxPaperWidth.reset(); + mxPaperHeight.reset(); + mxPaperOrientation.reset(); + mxMarginSelectBox.reset(); + mxCustomEntry.reset(); + + maMetricController.dispose(); + maPaperOrientationController.dispose(); + maPaperSizeController.dispose(); + maSwPageLRControl.dispose(); + maSwPageULControl.dispose(); + mpPageULMarginItem.reset(); + mpPageLRMarginItem.reset(); + mpPageItem.reset(); + + PanelLayout::dispose(); +} + +void PageFormatPanel::Initialize() +{ + mxPaperSizeBox->FillPaperSizeEntries( PaperSizeApp::Std ); + meUnit = maPaperSizeController.GetCoreMetric(); + mxPaperWidth->SetFieldUnit(meFUnit); + mxPaperHeight->SetFieldUnit(meFUnit); + SetMarginFieldUnit(); + aCustomEntry = mxCustomEntry->get_label(); + + const SvtOptionsDrawinglayer aDrawinglayerOpt; + mxPaperWidth->set_max(mxPaperWidth->normalize(aDrawinglayerOpt.GetMaximumPaperWidth()), FieldUnit::CM); + mxPaperHeight->set_max(mxPaperHeight->normalize(aDrawinglayerOpt.GetMaximumPaperHeight()), FieldUnit::CM); + + mxPaperSizeBox->connect_changed( LINK(this, PageFormatPanel, PaperFormatModifyHdl )); + mxPaperOrientation->connect_changed( LINK(this, PageFormatPanel, PaperFormatModifyHdl )); + mxPaperHeight->connect_value_changed( LINK(this, PageFormatPanel, PaperSizeModifyHdl )); + mxPaperWidth->connect_value_changed( LINK(this, PageFormatPanel, PaperSizeModifyHdl )); + mxMarginSelectBox->connect_changed( LINK(this, PageFormatPanel, PaperModifyMarginHdl)); + + mpBindings->Update(SID_ATTR_METRIC); + mpBindings->Update(SID_ATTR_PAGE); + mpBindings->Update(SID_ATTR_PAGE_SIZE); + mpBindings->Update(SID_ATTR_PAGE_LRSPACE); + mpBindings->Update(SID_ATTR_PAGE_ULSPACE); + + UpdateMarginBox(); +} + +void PageFormatPanel::NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch(nSId) + { + case SID_ATTR_PAGE_SIZE: + { + const SvxSizeItem* pSizeItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pSizeItem = dynamic_cast< const SvxSizeItem* >(pState); + if (pSizeItem) + { + Size aPaperSize = pSizeItem->GetSize(); + + mxPaperWidth->set_value(mxPaperWidth->normalize(aPaperSize.Width()), FieldUnit::TWIP); + mxPaperHeight->set_value(mxPaperHeight->normalize(aPaperSize.Height()), FieldUnit::TWIP); + + if(mxPaperOrientation->get_active() == 1) + Swap(aPaperSize); + + Paper ePaper = SvxPaperInfo::GetSvxPaper(aPaperSize, meUnit); + mxPaperSizeBox->set_active_id( ePaper ); + } + } + break; + case SID_ATTR_METRIC: + { + meUnit = maPaperSizeController.GetCoreMetric(); + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (eFUnit != meFUnit) + { + meFUnit = eFUnit; + mxPaperHeight->SetFieldUnit(meFUnit); + mxPaperWidth->SetFieldUnit(meFUnit); + SetMarginFieldUnit(); + UpdateMarginBox(); + } + } + break; + case SID_ATTR_PAGE: + { + if ( eState >= SfxItemState::DEFAULT && + dynamic_cast< const SvxPageItem *>( pState ) ) + { + mpPageItem.reset( static_cast<SvxPageItem*>(pState->Clone()) ); + if ( mpPageItem->IsLandscape() ) + mxPaperOrientation->set_active(1); + else + mxPaperOrientation->set_active(0); + } + } + break; + case SID_ATTR_PAGE_LRSPACE: + { + if ( eState >= SfxItemState::DEFAULT && + dynamic_cast< const SvxLongLRSpaceItem *>( pState ) ) + { + mpPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + case SID_ATTR_PAGE_ULSPACE: + { + if ( eState >= SfxItemState::DEFAULT && + dynamic_cast< const SvxLongULSpaceItem *>( pState ) ) + { + mpPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(PageFormatPanel, PaperFormatModifyHdl, weld::ComboBox&, void) +{ + Paper ePaper = mxPaperSizeBox->get_active_id(); + Size aSize; + + if(ePaper!=PAPER_USER) + aSize = SvxPaperInfo::GetPaperSize(ePaper, meUnit); + else + aSize = Size(mxPaperWidth->GetCoreValue(meUnit), mxPaperHeight->GetCoreValue(meUnit)); + + if (mxPaperOrientation->get_active() == 1 || ePaper==PAPER_USER) + Swap(aSize); + + mpPageItem->SetLandscape(mxPaperOrientation->get_active() == 1); + SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, aSize); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, SfxCallMode::RECORD, { &aSizeItem, mpPageItem.get() }); +} + +IMPL_LINK_NOARG(PageFormatPanel, PaperSizeModifyHdl, weld::MetricSpinButton&, void) +{ + Size aSize(mxPaperWidth->GetCoreValue(meUnit), mxPaperHeight->GetCoreValue(meUnit)); + SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, aSize); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, SfxCallMode::RECORD, { &aSizeItem }); +} + +IMPL_LINK_NOARG(PageFormatPanel, PaperModifyMarginHdl, weld::ComboBox&, void) +{ + bool bMirrored = false; + bool bApplyNewPageMargins = true; + switch (mxMarginSelectBox->get_active()) + { + case 0: + SetNone(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 1: + SetNarrow(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 2: + SetModerate(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 3: + SetNormal075(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 4: + SetNormal100(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 5: + SetNormal125(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 6: + SetWide(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + case 7: + SetMirrored(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored); + break; + default: + bApplyNewPageMargins = false; + break; + } + + if(bApplyNewPageMargins) + { + ExecuteMarginLRChange( mnPageLeftMargin, mnPageRightMargin ); + ExecuteMarginULChange( mnPageTopMargin, mnPageBottomMargin ); + if(bMirrored != (mpPageItem->GetPageUsage() == SvxPageUsage::Mirror)) + { + mpPageItem->SetPageUsage( bMirrored ? SvxPageUsage::Mirror : SvxPageUsage::All ); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE, + SfxCallMode::RECORD, { mpPageItem.get() }); + } + } +} + +FieldUnit PageFormatPanel::GetCurrentUnit( SfxItemState eState, const SfxPoolItem* pState ) +{ + FieldUnit eUnit = FieldUnit::NONE; + + if ( pState && eState >= SfxItemState::DEFAULT ) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + else + { + SfxViewFrame* pFrame = SfxViewFrame::Current(); + SfxObjectShell* pSh = nullptr; + if ( pFrame ) + pSh = pFrame->GetObjectShell(); + if ( pSh ) + { + SfxModule* pModule = pSh->GetModule(); + if ( pModule ) + { + const SfxPoolItem* pItem = pModule->GetItem( SID_ATTR_METRIC ); + if ( pItem ) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + } + else + { + SAL_WARN("sw.ui", "GetModuleFieldUnit(): no module found"); + } + } + } + + return eUnit; +} + +void PageFormatPanel::ExecuteMarginLRChange( const long nPageLeftMargin, const long nPageRightMargin ) +{ + mpPageLRMarginItem->SetLeft( nPageLeftMargin ); + mpPageLRMarginItem->SetRight( nPageRightMargin ); + mpBindings->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_LRSPACE, SfxCallMode::RECORD, { mpPageLRMarginItem.get() }); +} + +void PageFormatPanel::ExecuteMarginULChange(const long nPageTopMargin, const long nPageBottomMargin) +{ + mpPageULMarginItem->SetUpper( nPageTopMargin ); + mpPageULMarginItem->SetLower( nPageBottomMargin ); + mpBindings->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_ULSPACE, SfxCallMode::RECORD, { mpPageULMarginItem.get() }); +} + +void PageFormatPanel::UpdateMarginBox() +{ + mnPageLeftMargin = mpPageLRMarginItem->GetLeft(); + mnPageRightMargin = mpPageLRMarginItem->GetRight(); + mnPageTopMargin = mpPageULMarginItem->GetUpper(); + mnPageBottomMargin = mpPageULMarginItem->GetLower(); + + int nCustomEntry = mxMarginSelectBox->find_text(aCustomEntry); + + bool bMirrored = (mpPageItem->GetPageUsage() == SvxPageUsage::Mirror); + if( IsNone(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(0); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsNarrow(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(1); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsModerate(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(2); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsNormal075(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(3); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsNormal100(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(4); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsNormal125(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(5); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsWide(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(6); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else if( IsMirrored(mnPageLeftMargin, mnPageRightMargin, mnPageTopMargin, mnPageBottomMargin, bMirrored) ) + { + mxMarginSelectBox->set_active(7); + if (nCustomEntry != -1) + mxMarginSelectBox->remove(nCustomEntry); + } + else + { + if (nCustomEntry == -1) + mxMarginSelectBox->append_text(aCustomEntry); + mxMarginSelectBox->set_active_text(aCustomEntry); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageFormatPanel.hxx b/sw/source/uibase/sidebar/PageFormatPanel.hxx new file mode 100644 index 000000000..4ea09bb50 --- /dev/null +++ b/sw/source/uibase/sidebar/PageFormatPanel.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEFORMATPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEFORMATPANEL_HXX + +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <sfx2/sidebar/ControllerItem.hxx> + +#include <svx/pageitem.hxx> +#include <svx/rulritem.hxx> +#include <svx/papersizelistbox.hxx> + +#include <tools/fldunit.hxx> +#include <svl/poolitem.hxx> +#include <svx/relfld.hxx> + +#include <memory> + +namespace sw::sidebar { + +class PageFormatPanel: + public PanelLayout, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + static VclPtr<vcl::Window> Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + + virtual void NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + PageFormatPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + virtual ~PageFormatPanel() override; + virtual void dispose() override; + + static FieldUnit GetCurrentUnit( SfxItemState eState, const SfxPoolItem* pState ); + +private: + + SfxBindings* mpBindings; + + std::unique_ptr<SvxPaperSizeListBox> mxPaperSizeBox; + std::unique_ptr<SvxRelativeField> mxPaperWidth; + std::unique_ptr<SvxRelativeField> mxPaperHeight; + std::unique_ptr<weld::ComboBox> mxPaperOrientation; + std::unique_ptr<weld::ComboBox> mxMarginSelectBox; + std::unique_ptr<weld::Label> mxCustomEntry; + + ::sfx2::sidebar::ControllerItem maPaperSizeController; + ::sfx2::sidebar::ControllerItem maPaperOrientationController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maSwPageLRControl; + ::sfx2::sidebar::ControllerItem maSwPageULControl; + + std::unique_ptr<SvxPageItem> mpPageItem; + std::unique_ptr<SvxLongLRSpaceItem> mpPageLRMarginItem; + std::unique_ptr<SvxLongULSpaceItem> mpPageULMarginItem; + + FieldUnit meFUnit; + MapUnit meUnit; + + long mnPageLeftMargin; + long mnPageRightMargin; + long mnPageTopMargin; + long mnPageBottomMargin; + OUString aCustomEntry; + + void Initialize(); + void SetMarginFieldUnit(); + void UpdateMarginBox(); + void ExecuteMarginLRChange( const long nPageLeftMargin, const long nPageRightMargin ); + void ExecuteMarginULChange( const long nPageTopMargin, const long nPageBottomMargin); + DECL_LINK(PaperFormatModifyHdl, weld::ComboBox&, void); + DECL_LINK(PaperSizeModifyHdl, weld::MetricSpinButton&, void); + DECL_LINK(PaperModifyMarginHdl, weld::ComboBox&, void ); +}; + +} //end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageHeaderPanel.cxx b/sw/source/uibase/sidebar/PageHeaderPanel.cxx new file mode 100644 index 000000000..a81d07a29 --- /dev/null +++ b/sw/source/uibase/sidebar/PageHeaderPanel.cxx @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <svx/dlgutil.hxx> +#include <svx/rulritem.hxx> +#include <svx/svdtrans.hxx> +#include "PageHeaderPanel.hxx" +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <cmdid.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +namespace sw::sidebar{ + +VclPtr<vcl::Window> PageHeaderPanel::Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no parent window given to PageHeaderPanel::Create", nullptr, 0); + if( !rxFrame.is() ) + throw ::com::sun::star::lang::IllegalArgumentException("no XFrame given to PageHeaderPanel::Create", nullptr, 0); + if( pBindings == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no SfxBindings given to PageHeaderPanel::Create", nullptr, 0); + + return VclPtr<PageHeaderPanel>::Create(pParent, rxFrame, pBindings); +} + +void PageHeaderPanel::SetMarginsAndSpacingFieldUnit() +{ + SpacingListBox::Fill(IsInch(meFUnit) ? SpacingType::SPACING_INCH : SpacingType::SPACING_CM, *mxHeaderSpacingLB); + SpacingListBox::Fill(IsInch(meFUnit) ? SpacingType::MARGINS_INCH : SpacingType::MARGINS_CM, *mxHeaderMarginPresetLB); +} + +PageHeaderPanel::PageHeaderPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings + ) : + PanelLayout(pParent, "PageHeaderPanel", "modules/swriter/ui/pageheaderpanel.ui", rxFrame), + mpBindings( pBindings ), + maHFToggleController(SID_ATTR_PAGE_HEADER, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings,*this), + maHeaderLRMarginController(SID_ATTR_PAGE_HEADER_LRMARGIN, *pBindings, *this), + maHeaderSpacingController(SID_ATTR_PAGE_HEADER_SPACING, *pBindings, *this), + maHeaderLayoutController(SID_ATTR_PAGE_HEADER_LAYOUT, *pBindings, *this), + meFUnit(GetModuleFieldUnit()), + aCustomEntry(), + mpHeaderItem( new SfxBoolItem(SID_ATTR_PAGE_HEADER) ), + mpHeaderLRMarginItem( new SvxLongLRSpaceItem(0, 0, SID_ATTR_PAGE_HEADER_LRMARGIN)), + mpHeaderSpacingItem( new SvxLongULSpaceItem(0, 0, SID_ATTR_PAGE_HEADER_SPACING)), + mpHeaderLayoutItem( new SfxInt16Item(SID_ATTR_PAGE_HEADER_LAYOUT)), + mxHeaderToggle(m_xBuilder->weld_check_button("headertoggle")), + mxHeaderSpacingLB(m_xBuilder->weld_combo_box("spacingpreset")), + mxHeaderMarginPresetLB(m_xBuilder->weld_combo_box("headermarginpreset")), + mxHeaderLayoutLB(m_xBuilder->weld_combo_box("samecontentLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")) +{ + Initialize(); +} + +PageHeaderPanel::~PageHeaderPanel() +{ + disposeOnce(); +} + +void PageHeaderPanel::dispose() +{ + mxHeaderToggle.reset(); + mxHeaderSpacingLB.reset(); + mxHeaderLayoutLB.reset(); + mxHeaderMarginPresetLB.reset(); + mxCustomEntry.reset(); + + PanelLayout::dispose(); +} + +FieldUnit PageHeaderPanel::GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState) +{ + FieldUnit eUnit; + + if (pState && eState >= SfxItemState::DEFAULT) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + else + eUnit = GetModuleFieldUnit(); + + return eUnit; +} + +void PageHeaderPanel::Initialize() +{ + SameContentListBox::Fill(*mxHeaderLayoutLB); + + SetMarginsAndSpacingFieldUnit(); + + aCustomEntry = mxCustomEntry->get_label(); + mxHeaderToggle->connect_toggled( LINK(this, PageHeaderPanel, HeaderToggleHdl) ); + mxHeaderMarginPresetLB->connect_changed( LINK(this, PageHeaderPanel, HeaderLRMarginHdl)); + mxHeaderSpacingLB->connect_changed( LINK(this, PageHeaderPanel, HeaderSpacingHdl)); + mxHeaderLayoutLB->connect_changed( LINK(this, PageHeaderPanel, HeaderLayoutHdl)); + + mpBindings->Invalidate(SID_ATTR_METRIC); + mpBindings->Invalidate(SID_ATTR_PAGE_HEADER); + mpBindings->Invalidate(SID_ATTR_PAGE_HEADER_LRMARGIN); + mpBindings->Invalidate(SID_ATTR_PAGE_HEADER_SPACING); + mpBindings->Invalidate(SID_ATTR_PAGE_HEADER_LAYOUT); +} + +void PageHeaderPanel::UpdateHeaderCheck() +{ + if (mxHeaderToggle->get_active()) + { + mxHeaderSpacingLB->set_sensitive(true); + mxHeaderLayoutLB->set_sensitive(true); + mxHeaderMarginPresetLB->set_sensitive(true); + } + else + { + mxHeaderSpacingLB->set_sensitive(false); + mxHeaderLayoutLB->set_sensitive(false); + mxHeaderMarginPresetLB->set_sensitive(false); + } +} + +void PageHeaderPanel::UpdateMarginControl() +{ + sal_uInt16 nLeft = mpHeaderLRMarginItem->GetLeft(); + sal_uInt16 nRight = mpHeaderLRMarginItem->GetRight(); + sal_uInt16 nCount = mxHeaderMarginPresetLB->get_count(); + if(nLeft == nRight) + { + for (sal_uInt16 i = 0; i < nCount; ++i) + { + if (mxHeaderMarginPresetLB->get_id(i).toUInt32() == nLeft) + { + mxHeaderMarginPresetLB->set_active(i); + int nCustomEntry = mxHeaderMarginPresetLB->find_text(aCustomEntry); + if (nCustomEntry != -1) + mxHeaderMarginPresetLB->remove(nCustomEntry); + return; + } + } + } + mxHeaderMarginPresetLB->append_text(aCustomEntry); + mxHeaderMarginPresetLB->set_active_text(aCustomEntry); +} + +void PageHeaderPanel::UpdateSpacingControl() +{ + sal_uInt16 nBottom = mpHeaderSpacingItem->GetLower(); + sal_uInt16 nCount = mxHeaderSpacingLB->get_count(); + for (sal_uInt16 i = 0; i < nCount; ++i) + { + if (mxHeaderSpacingLB->get_id(i).toUInt32() == nBottom) + { + mxHeaderSpacingLB->set_active(i); + int nCustomEntry = mxHeaderSpacingLB->find_text(aCustomEntry); + if (nCustomEntry != -1) + mxHeaderSpacingLB->remove(nCustomEntry); + return; + } + } + mxHeaderSpacingLB->append_text(aCustomEntry); + mxHeaderSpacingLB->set_active_text(aCustomEntry); +} + +void PageHeaderPanel::UpdateLayoutControl() +{ + sal_uInt16 nLayout = mpHeaderLayoutItem->GetValue(); + mxHeaderLayoutLB->set_active(nLayout); +} + +void PageHeaderPanel::NotifyItemUpdate( + const sal_uInt16 nSid, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + if (IsDisposed()) + return; + + switch(nSid) + { + case SID_ATTR_PAGE_HEADER: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SfxBoolItem*>( pState) ) + { + mpHeaderItem.reset( static_cast<SfxBoolItem*>(pState->Clone()) ); + mxHeaderToggle->set_active(mpHeaderItem->GetValue()); + UpdateHeaderCheck(); + } + } + break; + case SID_ATTR_PAGE_HEADER_LRMARGIN: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SvxLongLRSpaceItem*>( pState) ) + { + mpHeaderLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pState->Clone()) ); + UpdateMarginControl(); + } + } + break; + case SID_ATTR_PAGE_HEADER_SPACING: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SvxLongULSpaceItem*>( pState) ) + { + mpHeaderSpacingItem.reset(static_cast<SvxLongULSpaceItem*>(pState->Clone()) ); + UpdateSpacingControl(); + } + } + break; + case SID_ATTR_PAGE_HEADER_LAYOUT: + { + if(eState >= SfxItemState::DEFAULT && + dynamic_cast<const SfxInt16Item*>( pState) ) + { + mpHeaderLayoutItem.reset(static_cast<SfxInt16Item*>(pState->Clone()) ); + UpdateLayoutControl(); + } + } + break; + case SID_ATTR_METRIC: + { + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (meFUnit != eFUnit) + { + meFUnit = eFUnit; + SetMarginsAndSpacingFieldUnit(); + UpdateSpacingControl(); + UpdateMarginControl(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG( PageHeaderPanel, HeaderToggleHdl, weld::ToggleButton&, void ) +{ + bool IsChecked = mxHeaderToggle->get_active(); + mpHeaderItem->SetValue(IsChecked); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_HEADER, SfxCallMode::RECORD, { mpHeaderItem.get() } ); + UpdateHeaderCheck(); +} + +IMPL_LINK_NOARG( PageHeaderPanel, HeaderLRMarginHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxHeaderMarginPresetLB->get_active_id().toUInt32(); + mpHeaderLRMarginItem->SetLeft(nVal); + mpHeaderLRMarginItem->SetRight(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_HEADER_LRMARGIN, + SfxCallMode::RECORD, { mpHeaderLRMarginItem.get() } ); +} + +IMPL_LINK_NOARG( PageHeaderPanel, HeaderSpacingHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxHeaderSpacingLB->get_active_id().toUInt32(); + mpHeaderSpacingItem->SetLower(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_HEADER_SPACING, + SfxCallMode::RECORD, { mpHeaderSpacingItem.get() } ); +} +IMPL_LINK_NOARG( PageHeaderPanel, HeaderLayoutHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nVal = mxHeaderLayoutLB->get_active(); + mpHeaderLayoutItem->SetValue(nVal); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_HEADER_LAYOUT, + SfxCallMode::RECORD, { mpHeaderLayoutItem.get() } ); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageHeaderPanel.hxx b/sw/source/uibase/sidebar/PageHeaderPanel.hxx new file mode 100644 index 000000000..c7701f9e7 --- /dev/null +++ b/sw/source/uibase/sidebar/PageHeaderPanel.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_SIDEBAR_PAGEHEADERPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEHEADERPANEL_HXX + +#include <memory> +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <sfx2/sidebar/ControllerItem.hxx> + +#include <svx/rulritem.hxx> + +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <svl/eitem.hxx> +#include <svx/spacinglistbox.hxx> +#include <svx/samecontentlistbox.hxx> + +namespace sw::sidebar { + +class PageHeaderPanel: + public PanelLayout, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + static VclPtr<vcl::Window> Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + + virtual void NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + SfxBindings* GetBindings() const { return mpBindings; } + PageHeaderPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + virtual ~PageHeaderPanel() override; + virtual void dispose() override; + +private: + + SfxBindings* mpBindings; + + ::sfx2::sidebar::ControllerItem maHFToggleController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maHeaderLRMarginController; + ::sfx2::sidebar::ControllerItem maHeaderSpacingController; + ::sfx2::sidebar::ControllerItem maHeaderLayoutController; + + FieldUnit meFUnit; + + OUString aCustomEntry; + + void Initialize(); + void SetMarginsAndSpacingFieldUnit(); + void UpdateHeaderCheck(); + void UpdateMarginControl(); + void UpdateSpacingControl(); + void UpdateLayoutControl(); + + ::std::unique_ptr<SfxBoolItem> mpHeaderItem; + ::std::unique_ptr<SvxLongLRSpaceItem> mpHeaderLRMarginItem; + ::std::unique_ptr<SvxLongULSpaceItem> mpHeaderSpacingItem; + ::std::unique_ptr<SfxInt16Item> mpHeaderLayoutItem; + + std::unique_ptr<weld::CheckButton> mxHeaderToggle; + std::unique_ptr<weld::ComboBox> mxHeaderSpacingLB; + std::unique_ptr<weld::ComboBox> mxHeaderMarginPresetLB; + std::unique_ptr<weld::ComboBox> mxHeaderLayoutLB; + std::unique_ptr<weld::Label> mxCustomEntry; + + static FieldUnit GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState); + + DECL_LINK( HeaderToggleHdl, weld::ToggleButton&, void ); + DECL_LINK( HeaderLRMarginHdl, weld::ComboBox&, void); + DECL_LINK( HeaderSpacingHdl, weld::ComboBox&, void); + DECL_LINK( HeaderLayoutHdl, weld::ComboBox&, void); +}; + +} //end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageMarginControl.cxx b/sw/source/uibase/sidebar/PageMarginControl.cxx new file mode 100644 index 000000000..258b5996d --- /dev/null +++ b/sw/source/uibase/sidebar/PageMarginControl.cxx @@ -0,0 +1,586 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> + +#include "PageMarginControl.hxx" +#include <strings.hrc> + +#include <editeng/sizeitem.hxx> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/pageitem.hxx> +#include <svx/rulritem.hxx> +#include <svl/itempool.hxx> +#include <svl/intitem.hxx> +#include <svtools/unitconv.hxx> +#include <unotools/viewoptions.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <PageMarginPopup.hxx> + +#include <com/sun/star/document/XUndoManagerSupplier.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +#define SWPAGE_LEFT_GVALUE "Sw_Page_Left" +#define SWPAGE_RIGHT_GVALUE "Sw_Page_Right" +#define SWPAGE_TOP_GVALUE "Sw_Page_Top" +#define SWPAGE_DOWN_GVALUE "Sw_Page_Down" +#define SWPAGE_MIRROR_GVALUE "Sw_Page_Mirrored" + +namespace +{ + FieldUnit lcl_GetFieldUnit() + { + FieldUnit eUnit = FieldUnit::INCH; + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_METRIC, pItem ); + if ( pItem && eState >= SfxItemState::DEFAULT ) + { + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>( pItem )->GetValue()); + } + else + { + return SfxModule::GetCurrentFieldUnit(); + } + + return eUnit; + } + + MapUnit lcl_GetUnit() + { + SfxItemPool &rPool = SfxGetpApp()->GetPool(); + sal_uInt16 nWhich = rPool.GetWhich( SID_ATTR_PAGE_SIZE ); + return rPool.GetMetric( nWhich ); + } + + css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame ) + { + const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController(); + if ( xController.is() ) + { + const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel(); + if ( xModel.is() ) + { + const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW ); + return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW ); + } + } + + return css::uno::Reference< css::document::XUndoManager > (); + } +} + +namespace sw::sidebar { + +PageMarginControl::PageMarginControl(PageMarginPopup* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/swriter/ui/pagemargincontrol.ui", "PageMarginControl") + , m_xLeft(m_xBuilder->weld_label("leftLabel")) + , m_xRight(m_xBuilder->weld_label("rightLabel")) + , m_xInner(m_xBuilder->weld_label("innerLabel")) + , m_xOuter(m_xBuilder->weld_label("outerLabel")) + , m_xLeftMarginEdit(m_xBuilder->weld_metric_spin_button("left", FieldUnit::CM)) + , m_xRightMarginEdit(m_xBuilder->weld_metric_spin_button("right", FieldUnit::CM)) + , m_xTopMarginEdit(m_xBuilder->weld_metric_spin_button("top", FieldUnit::CM)) + , m_xBottomMarginEdit(m_xBuilder->weld_metric_spin_button("bottom", FieldUnit::CM)) + , m_xWidthHeightField(m_xBuilder->weld_metric_spin_button("hidden", FieldUnit::CM)) + , m_xControl(pControl) + , m_nPageLeftMargin(0) + , m_nPageRightMargin(0) + , m_nPageTopMargin(0) + , m_nPageBottomMargin(0) + , m_bMirrored(false) + , m_eUnit( lcl_GetUnit() ) + , m_bUserCustomValuesAvailable( false ) + , m_nUserCustomPageLeftMargin( 0 ) + , m_nUserCustomPageRightMargin( 0 ) + , m_nUserCustomPageTopMargin( 0 ) + , m_nUserCustomPageBottomMargin( 0 ) + , m_bUserCustomMirrored( false ) + , m_bCustomValuesUsed( false ) +{ + m_xWidthHeightField->set_unit(FieldUnit::CM); + m_xWidthHeightField->set_range(0, 9999, FieldUnit::NONE); + m_xWidthHeightField->set_digits(2); + m_xWidthHeightField->set_increments(10, 100, FieldUnit::NONE); + SetFieldUnit( *m_xWidthHeightField, lcl_GetFieldUnit() ); + + bool bLandscape = false; + const SfxPoolItem* pItem; + const SvxSizeItem* pSize = nullptr; + const SvxLongLRSpaceItem* pLRItem = nullptr; + const SvxLongULSpaceItem* pULItem = nullptr; + if ( SfxViewFrame::Current() ) + { + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE, pItem ); + bLandscape = static_cast<const SvxPageItem*>( pItem )->IsLandscape(); + m_bMirrored = static_cast<const SvxPageItem*>( pItem )->GetPageUsage() == SvxPageUsage::Mirror; + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE_SIZE, pItem ); + pSize = static_cast<const SvxSizeItem*>( pItem ); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE_LRSPACE, pItem ); + pLRItem = static_cast<const SvxLongLRSpaceItem*>( pItem ); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE_ULSPACE, pItem ); + pULItem = static_cast<const SvxLongULSpaceItem*>( pItem ); + } + + if ( pLRItem ) + { + m_nPageLeftMargin = pLRItem->GetLeft(); + m_nPageRightMargin = pLRItem->GetRight(); + } + + if ( pULItem ) + { + m_nPageTopMargin = pULItem->GetUpper(); + m_nPageBottomMargin = pULItem->GetLower(); + } + + if ( bLandscape ) + { + m_xNarrow = m_xBuilder->weld_button("narrowL"); + m_xNormal = m_xBuilder->weld_button("normalL"); + m_xWide = m_xBuilder->weld_button("wideL"); + m_xMirrored = m_xBuilder->weld_button("mirroredL"); + m_xLast = m_xBuilder->weld_button("lastL"); + } + else + { + m_xNarrow = m_xBuilder->weld_button("narrow"); + m_xNormal = m_xBuilder->weld_button("normal"); + m_xWide = m_xBuilder->weld_button("wide"); + m_xMirrored = m_xBuilder->weld_button("mirrored"); + m_xLast = m_xBuilder->weld_button("last"); + } + + m_xNarrow->show(); + m_xNormal->show(); + m_xWide->show(); + m_xMirrored->show(); + m_xLast->show(); + + m_xNarrow->connect_clicked( LINK( this, PageMarginControl, SelectMarginHdl ) ); + m_xNormal->connect_clicked( LINK( this, PageMarginControl, SelectMarginHdl ) ); + m_xWide->connect_clicked( LINK( this, PageMarginControl, SelectMarginHdl ) ); + m_xMirrored->connect_clicked( LINK( this, PageMarginControl, SelectMarginHdl ) ); + m_xLast->connect_clicked( LINK( this, PageMarginControl, SelectMarginHdl ) ); + + m_bUserCustomValuesAvailable = GetUserCustomValues(); + + FillHelpText( m_bUserCustomValuesAvailable ); + + Link<weld::MetricSpinButton&,void> aLinkLR = LINK( this, PageMarginControl, ModifyLRMarginHdl ); + m_xLeftMarginEdit->connect_value_changed( aLinkLR ); + SetMetricValue( *m_xLeftMarginEdit, m_nPageLeftMargin, m_eUnit ); + SetFieldUnit( *m_xLeftMarginEdit, lcl_GetFieldUnit() ); + + m_xRightMarginEdit->connect_value_changed( aLinkLR ); + SetMetricValue( *m_xRightMarginEdit, m_nPageRightMargin, m_eUnit ); + SetFieldUnit( *m_xRightMarginEdit, lcl_GetFieldUnit() ); + + Link<weld::MetricSpinButton&,void> aLinkUL = LINK( this, PageMarginControl, ModifyULMarginHdl ); + m_xTopMarginEdit->connect_value_changed( aLinkUL ); + SetMetricValue( *m_xTopMarginEdit, m_nPageTopMargin, m_eUnit ); + SetFieldUnit( *m_xTopMarginEdit, lcl_GetFieldUnit() ); + + m_xBottomMarginEdit->connect_value_changed( aLinkUL ); + SetMetricValue( *m_xBottomMarginEdit, m_nPageBottomMargin, m_eUnit ); + SetFieldUnit( *m_xBottomMarginEdit, lcl_GetFieldUnit() ); + + m_aPageSize = pSize->GetSize(); + SetMetricFieldMaxValues( m_aPageSize ); + + if ( m_bMirrored ) + { + m_xLeft->hide(); + m_xRight->hide(); + m_xInner->show(); + m_xOuter->show(); + } + else + { + m_xLeft->show(); + m_xRight->show(); + m_xInner->hide(); + m_xOuter->hide(); + } +} + +void PageMarginControl::GrabFocus() +{ + m_xNarrow->grab_focus(); +} + +PageMarginControl::~PageMarginControl() +{ + StoreUserCustomValues(); +} + +void PageMarginControl::SetMetricFieldMaxValues( const Size& rPageSize ) +{ + const long nML = m_xLeftMarginEdit->denormalize( m_xLeftMarginEdit->get_value( FieldUnit::TWIP ) ); + const long nMR = m_xRightMarginEdit->denormalize( m_xRightMarginEdit->get_value( FieldUnit::TWIP ) ); + const long nMT = m_xTopMarginEdit->denormalize( m_xTopMarginEdit->get_value( FieldUnit::TWIP ) ); + const long nMB = m_xBottomMarginEdit->denormalize( m_xBottomMarginEdit->get_value( FieldUnit::TWIP ) ); + + const long nPH = OutputDevice::LogicToLogic( rPageSize.Height(), m_eUnit, MapUnit::MapTwip ); + const long nPW = OutputDevice::LogicToLogic( rPageSize.Width(), m_eUnit, MapUnit::MapTwip ); + + // Left + long nMax = nPW - nMR - MINBODY; + m_xLeftMarginEdit->set_max( m_xLeftMarginEdit->normalize( nMax ), FieldUnit::TWIP ); + + // Right + nMax = nPW - nML - MINBODY; + m_xRightMarginEdit->set_max( m_xRightMarginEdit->normalize( nMax ), FieldUnit::TWIP ); + + //Top + nMax = nPH - nMB - MINBODY; + m_xTopMarginEdit->set_max( m_xTopMarginEdit->normalize( nMax ), FieldUnit::TWIP ); + + //Bottom + nMax = nPH - nMT - MINBODY; + m_xBottomMarginEdit->set_max( m_xTopMarginEdit->normalize( nMax ), FieldUnit::TWIP ); +} + +void PageMarginControl::FillHelpText( const bool bUserCustomValuesAvailable ) +{ + const OUString aLeft = SwResId( STR_MARGIN_TOOLTIP_LEFT ); + const OUString aRight = SwResId( STR_MARGIN_TOOLTIP_RIGHT ); + const OUString aTop = SwResId( STR_MARGIN_TOOLTIP_TOP ); + const OUString aBottom = SwResId( STR_MARGIN_TOOLTIP_BOT ); + + SetMetricValue( *m_xWidthHeightField, SWPAGE_NARROW_VALUE, m_eUnit ); + const OUString aNarrowValText = m_xWidthHeightField->get_text(); + OUString aHelpText = aLeft + + aNarrowValText + + aRight + + aNarrowValText + + aTop + + aNarrowValText + + aBottom + + aNarrowValText; + m_xNarrow->set_tooltip_text( aHelpText ); + + SetMetricValue( *m_xWidthHeightField, SWPAGE_NORMAL_VALUE, m_eUnit ); + const OUString aNormalValText = m_xWidthHeightField->get_text(); + aHelpText = aLeft + + aNormalValText + + aRight + + aNormalValText + + aTop + + aNormalValText + + aBottom + + aNormalValText; + m_xNormal->set_tooltip_text( aHelpText ); + + SetMetricValue( *m_xWidthHeightField, SWPAGE_WIDE_VALUE1, m_eUnit ); + const OUString aWide1ValText = m_xWidthHeightField->get_text(); + SetMetricValue( *m_xWidthHeightField, SWPAGE_WIDE_VALUE2, m_eUnit ); + const OUString aWide2ValText = m_xWidthHeightField->get_text(); + aHelpText = aLeft + + aWide2ValText + + aRight + + aWide2ValText + + aTop + + aWide1ValText + + aBottom + + aWide1ValText; + m_xWide->set_tooltip_text( aHelpText ); + + const OUString aInner = SwResId( STR_MARGIN_TOOLTIP_INNER ); + const OUString aOuter = SwResId( STR_MARGIN_TOOLTIP_OUTER ); + + SetMetricValue( *m_xWidthHeightField, SWPAGE_WIDE_VALUE3, m_eUnit ); + const OUString aWide3ValText = m_xWidthHeightField->get_text(); + aHelpText = aInner + + aWide3ValText + + aOuter + + aWide1ValText + + aTop + + aWide1ValText + + aBottom + + aWide1ValText; + m_xMirrored->set_tooltip_text( aHelpText ); + + if ( bUserCustomValuesAvailable ) + { + aHelpText = m_bUserCustomMirrored ? aInner : aLeft; + SetMetricValue( *m_xWidthHeightField, m_nUserCustomPageLeftMargin, m_eUnit ); + aHelpText += m_xWidthHeightField->get_text(); + aHelpText += m_bUserCustomMirrored ? aOuter : aRight; + SetMetricValue( *m_xWidthHeightField, m_nUserCustomPageRightMargin, m_eUnit ); + aHelpText += m_xWidthHeightField->get_text(); + aHelpText += aTop; + SetMetricValue( *m_xWidthHeightField, m_nUserCustomPageTopMargin, m_eUnit ); + aHelpText += m_xWidthHeightField->get_text(); + aHelpText += aBottom; + SetMetricValue( *m_xWidthHeightField, m_nUserCustomPageBottomMargin, m_eUnit ); + aHelpText += m_xWidthHeightField->get_text(); + } + else + { + aHelpText.clear(); + } + m_xLast->set_tooltip_text( aHelpText ); +} + +IMPL_LINK( PageMarginControl, SelectMarginHdl, weld::Button&, rControl, void ) +{ + bool bMirrored = false; + bool bApplyNewPageMargins = true; + if( &rControl == m_xNarrow.get() ) + { + m_nPageLeftMargin = SWPAGE_NARROW_VALUE; + m_nPageRightMargin = SWPAGE_NARROW_VALUE; + m_nPageTopMargin = SWPAGE_NARROW_VALUE; + m_nPageBottomMargin = SWPAGE_NARROW_VALUE; + bMirrored = false; + } + if( &rControl == m_xNormal.get() ) + { + m_nPageLeftMargin = SWPAGE_NORMAL_VALUE; + m_nPageRightMargin = SWPAGE_NORMAL_VALUE; + m_nPageTopMargin = SWPAGE_NORMAL_VALUE; + m_nPageBottomMargin = SWPAGE_NORMAL_VALUE; + bMirrored = false; + } + if( &rControl == m_xWide.get() ) + { + m_nPageLeftMargin = SWPAGE_WIDE_VALUE2; + m_nPageRightMargin = SWPAGE_WIDE_VALUE2; + m_nPageTopMargin = SWPAGE_WIDE_VALUE1; + m_nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = false; + } + if( &rControl == m_xMirrored.get() ) + { + m_nPageLeftMargin = SWPAGE_WIDE_VALUE3; + m_nPageRightMargin = SWPAGE_WIDE_VALUE1; + m_nPageTopMargin = SWPAGE_WIDE_VALUE1; + m_nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = true; + } + if( &rControl == m_xLast.get() ) + { + if ( m_bUserCustomValuesAvailable ) + { + m_nPageLeftMargin = m_nUserCustomPageLeftMargin; + m_nPageRightMargin = m_nUserCustomPageRightMargin; + m_nPageTopMargin = m_nUserCustomPageTopMargin; + m_nPageBottomMargin = m_nUserCustomPageBottomMargin; + bMirrored = m_bUserCustomMirrored; + } + else + { + bApplyNewPageMargins = false; + } + } + + if ( bApplyNewPageMargins ) + { + const css::uno::Reference< css::document::XUndoManager > xUndoManager( getUndoManager( SfxViewFrame::Current()->GetFrame().GetFrameInterface() ) ); + if ( xUndoManager.is() ) + xUndoManager->enterUndoContext( "" ); + + ExecuteMarginLRChange( m_nPageLeftMargin, m_nPageRightMargin ); + ExecuteMarginULChange( m_nPageTopMargin, m_nPageBottomMargin ); + if ( m_bMirrored != bMirrored ) + { + m_bMirrored = bMirrored; + ExecutePageLayoutChange( m_bMirrored ); + } + + if ( xUndoManager.is() ) + xUndoManager->leaveUndoContext(); + + m_bCustomValuesUsed = false; + m_xControl->EndPopupMode(); + } +} + +void PageMarginControl::ExecuteMarginLRChange( + const long nPageLeftMargin, + const long nPageRightMargin ) +{ + if ( SfxViewFrame::Current() ) + { + std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ); + pPageLRMarginItem->SetLeft( nPageLeftMargin ); + pPageLRMarginItem->SetRight( nPageRightMargin ); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->ExecuteList( SID_ATTR_PAGE_LRSPACE, + SfxCallMode::RECORD, { pPageLRMarginItem.get() } ); + pPageLRMarginItem.reset(); + } +} + +void PageMarginControl::ExecuteMarginULChange( + const long nPageTopMargin, + const long nPageBottomMargin ) +{ + if ( SfxViewFrame::Current() ) + { + std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ); + pPageULMarginItem->SetUpper( nPageTopMargin ); + pPageULMarginItem->SetLower( nPageBottomMargin ); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->ExecuteList( SID_ATTR_PAGE_ULSPACE, + SfxCallMode::RECORD, { pPageULMarginItem.get() } ); + pPageULMarginItem.reset(); + } +} + +void PageMarginControl::ExecutePageLayoutChange( const bool bMirrored ) +{ + if ( SfxViewFrame::Current() ) + { + std::unique_ptr<SvxPageItem> pPageItem( new SvxPageItem( SID_ATTR_PAGE ) ); + pPageItem->SetPageUsage( bMirrored ? SvxPageUsage::Mirror : SvxPageUsage::All ); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->ExecuteList( SID_ATTR_PAGE, + SfxCallMode::RECORD, { pPageItem.get() } ); + pPageItem.reset(); + } +} + +IMPL_LINK_NOARG( PageMarginControl, ModifyLRMarginHdl, weld::MetricSpinButton&, void ) +{ + m_nPageLeftMargin = GetCoreValue( *m_xLeftMarginEdit, m_eUnit ); + m_nPageRightMargin = GetCoreValue( *m_xRightMarginEdit, m_eUnit ); + ExecuteMarginLRChange( m_nPageLeftMargin, m_nPageRightMargin ); + SetMetricFieldMaxValues( m_aPageSize ); + m_bCustomValuesUsed = true; +} + +IMPL_LINK_NOARG( PageMarginControl, ModifyULMarginHdl, weld::MetricSpinButton&, void ) +{ + m_nPageTopMargin = GetCoreValue( *m_xTopMarginEdit, m_eUnit ); + m_nPageBottomMargin = GetCoreValue( *m_xBottomMarginEdit, m_eUnit ); + ExecuteMarginULChange( m_nPageTopMargin, m_nPageBottomMargin ); + SetMetricFieldMaxValues( m_aPageSize ); + m_bCustomValuesUsed = true; +} + +bool PageMarginControl::GetUserCustomValues() +{ + bool bUserCustomValuesAvailable = false; + + SvtViewOptions aWinOpt( EViewType::Window, SWPAGE_LEFT_GVALUE ); + if ( aWinOpt.Exists() ) + { + css::uno::Sequence < css::beans::NamedValue > aSeq = aWinOpt.GetUserData(); + OUString aTmp; + if ( aSeq.hasElements()) + aSeq[0].Value >>= aTmp; + OUString aWinData( aTmp ); + m_nUserCustomPageLeftMargin = aWinData.toInt32(); + bUserCustomValuesAvailable = true; + } + + SvtViewOptions aWinOpt2( EViewType::Window, SWPAGE_RIGHT_GVALUE ); + if ( aWinOpt2.Exists() ) + { + css::uno::Sequence < css::beans::NamedValue > aSeq = aWinOpt2.GetUserData(); + OUString aTmp; + if ( aSeq.hasElements()) + aSeq[0].Value >>= aTmp; + OUString aWinData( aTmp ); + m_nUserCustomPageRightMargin = aWinData.toInt32(); + bUserCustomValuesAvailable = true; + } + + SvtViewOptions aWinOpt3( EViewType::Window, SWPAGE_TOP_GVALUE ); + if ( aWinOpt3.Exists() ) + { + css::uno::Sequence < css::beans::NamedValue > aSeq = aWinOpt3.GetUserData(); + OUString aTmp; + if ( aSeq.hasElements() ) + aSeq[0].Value >>= aTmp; + OUString aWinData( aTmp ); + m_nUserCustomPageTopMargin = aWinData.toInt32(); + bUserCustomValuesAvailable = true; + } + + SvtViewOptions aWinOpt4( EViewType::Window, SWPAGE_DOWN_GVALUE ); + if ( aWinOpt4.Exists() ) + { + css::uno::Sequence < css::beans::NamedValue > aSeq = aWinOpt4.GetUserData(); + OUString aTmp; + if ( aSeq.hasElements()) + aSeq[0].Value >>= aTmp; + OUString aWinData( aTmp ); + m_nUserCustomPageBottomMargin = aWinData.toInt32(); + bUserCustomValuesAvailable = true; + } + + SvtViewOptions aWinOpt5( EViewType::Window, SWPAGE_MIRROR_GVALUE ); + if ( aWinOpt5.Exists() ) + { + css::uno::Sequence < css::beans::NamedValue > aSeq = aWinOpt5.GetUserData(); + OUString aTmp; + if ( aSeq.hasElements()) + aSeq[0].Value >>= aTmp; + OUString aWinData( aTmp ); + m_bUserCustomMirrored = aWinData.toInt32() != 0; + bUserCustomValuesAvailable = true; + } + + return bUserCustomValuesAvailable; +} + +void PageMarginControl::StoreUserCustomValues() +{ + if ( !m_bCustomValuesUsed ) + { + return; + } + + css::uno::Sequence < css::beans::NamedValue > aSeq( 1 ); + SvtViewOptions aWinOpt( EViewType::Window, SWPAGE_LEFT_GVALUE ); + + aSeq[0].Name = "mnPageLeftMargin"; + aSeq[0].Value <<= OUString::number( m_nPageLeftMargin ); + aWinOpt.SetUserData( aSeq ); + + SvtViewOptions aWinOpt2( EViewType::Window, SWPAGE_RIGHT_GVALUE ); + aSeq[0].Name = "mnPageRightMargin"; + aSeq[0].Value <<= OUString::number( m_nPageRightMargin ); + aWinOpt2.SetUserData( aSeq ); + + SvtViewOptions aWinOpt3( EViewType::Window, SWPAGE_TOP_GVALUE ); + aSeq[0].Name = "mnPageTopMargin"; + aSeq[0].Value <<= OUString::number( m_nPageTopMargin ); + aWinOpt3.SetUserData( aSeq ); + + SvtViewOptions aWinOpt4( EViewType::Window, SWPAGE_DOWN_GVALUE ); + aSeq[0].Name = "mnPageBottomMargin"; + aSeq[0].Value <<= OUString::number( m_nPageBottomMargin ); + aWinOpt4.SetUserData( aSeq ); + + SvtViewOptions aWinOpt5( EViewType::Window, SWPAGE_MIRROR_GVALUE ); + aSeq[0].Name = "mbMirrored"; + aSeq[0].Value <<= OUString::number( (m_bMirrored ? 1 : 0) ); + aWinOpt5.SetUserData( aSeq ); +} + +} // end of namespace sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageMarginControl.hxx b/sw/source/uibase/sidebar/PageMarginControl.hxx new file mode 100644 index 000000000..1c945e815 --- /dev/null +++ b/sw/source/uibase/sidebar/PageMarginControl.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEMARGINCONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEMARGINCONTROL_HXX + +#include <svtools/toolbarmenu.hxx> + +#define SWPAGE_NARROW_VALUE 720 +#define SWPAGE_NORMAL_VALUE 1136 +#define SWPAGE_WIDE_VALUE1 1440 +#define SWPAGE_WIDE_VALUE2 2880 +#define SWPAGE_WIDE_VALUE3 1800 + +// #i19922# - tdf#126051 see cui/source/tabpages/page.cxx and svx/source/dialog/hdft.cxx +static const long MINBODY = 56; // 1mm in twips rounded + +class PageMarginPopup; + +namespace sw::sidebar { + +class PageMarginControl final : public WeldToolbarPopup +{ +public: + explicit PageMarginControl(PageMarginPopup* pControl, weld::Widget* pParent); + virtual void GrabFocus() override; + virtual ~PageMarginControl() override; + +private: + std::unique_ptr<weld::Button> m_xNarrow; + std::unique_ptr<weld::Button> m_xNormal; + std::unique_ptr<weld::Button> m_xWide; + std::unique_ptr<weld::Button> m_xMirrored; + std::unique_ptr<weld::Button> m_xLast; + + std::unique_ptr<weld::Label> m_xLeft; + std::unique_ptr<weld::Label> m_xRight; + std::unique_ptr<weld::Label> m_xInner; + std::unique_ptr<weld::Label> m_xOuter; + + std::unique_ptr<weld::MetricSpinButton> m_xLeftMarginEdit; + std::unique_ptr<weld::MetricSpinButton> m_xRightMarginEdit; + std::unique_ptr<weld::MetricSpinButton> m_xTopMarginEdit; + std::unique_ptr<weld::MetricSpinButton> m_xBottomMarginEdit; + + // hidden metric field + std::unique_ptr<weld::MetricSpinButton> m_xWidthHeightField; + + rtl::Reference<PageMarginPopup> m_xControl; + + long m_nPageLeftMargin; + long m_nPageRightMargin; + long m_nPageTopMargin; + long m_nPageBottomMargin; + bool m_bMirrored; + + const MapUnit m_eUnit; + + Size m_aPageSize; + + bool m_bUserCustomValuesAvailable; + long m_nUserCustomPageLeftMargin; + long m_nUserCustomPageRightMargin; + long m_nUserCustomPageTopMargin; + long m_nUserCustomPageBottomMargin; + bool m_bUserCustomMirrored; + + bool m_bCustomValuesUsed; + + DECL_LINK( SelectMarginHdl, weld::Button&, void ); + DECL_LINK( ModifyLRMarginHdl, weld::MetricSpinButton&, void ); + DECL_LINK( ModifyULMarginHdl, weld::MetricSpinButton&, void ); + + static void ExecuteMarginLRChange( + const long nPageLeftMargin, + const long nPageRightMargin ); + static void ExecuteMarginULChange( + const long nPageTopMargin, + const long nPageBottomMargin ); + static void ExecutePageLayoutChange( const bool bMirrored ); + + void SetMetricFieldMaxValues(const Size& rPageSize); + + bool GetUserCustomValues(); + void StoreUserCustomValues(); + + void FillHelpText( const bool bUserCustomValuesAvailable ); +}; + +} // end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageMarginPopup.cxx b/sw/source/uibase/sidebar/PageMarginPopup.cxx new file mode 100644 index 000000000..8223386c2 --- /dev/null +++ b/sw/source/uibase/sidebar/PageMarginPopup.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 <PageMarginPopup.hxx> +#include "PageMarginControl.hxx" +#include <vcl/toolbox.hxx> + +PageMarginPopup::PageMarginPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext) + : PopupWindowController(rContext, nullptr, OUString()) +{ +} + +void PageMarginPopup::initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) +{ + PopupWindowController::initialize(rArguments); + + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL) + pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId)); +} + +PageMarginPopup::~PageMarginPopup() +{ +} + +std::unique_ptr<WeldToolbarPopup> PageMarginPopup::weldPopupWindow() +{ + return std::make_unique<sw::sidebar::PageMarginControl>(this, m_pToolbar); +} + +VclPtr<vcl::Window> PageMarginPopup::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sw::sidebar::PageMarginControl>(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +OUString PageMarginPopup::getImplementationName() +{ + return "lo.writer.PageMarginToolBoxControl"; +} + +css::uno::Sequence<OUString> PageMarginPopup::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_PageMarginToolBoxControl_get_implementation( + css::uno::XComponentContext* rContext, + css::uno::Sequence<css::uno::Any> const & ) +{ + return cppu::acquire(new PageMarginPopup(rContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageMarginUtils.hxx b/sw/source/uibase/sidebar/PageMarginUtils.hxx new file mode 100644 index 000000000..1361361a1 --- /dev/null +++ b/sw/source/uibase/sidebar/PageMarginUtils.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 . + */ +#include <cmath> +#define SWPAGE_NO_MARGIN 0 +#define SWPAGE_NARROW_VALUE 720 +#define SWPAGE_MODERATE_LR 1080 +#define SWPAGE_NORMAL_VALUE 1136 +#define SWPAGE_WIDE_VALUE1 1440 +#define SWPAGE_WIDE_VALUE2 2880 +#define SWPAGE_WIDE_VALUE3 1800 +#define SWPAGE_UNIT_THRESHOLD 5 + +namespace sw::sidebar{ + +bool IsNone( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_NO_MARGIN) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_NO_MARGIN ) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_NO_MARGIN) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_NO_MARGIN) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetNone( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_NO_MARGIN; + nPageRightMargin = SWPAGE_NO_MARGIN; + nPageTopMargin = SWPAGE_NO_MARGIN; + nPageBottomMargin = SWPAGE_NO_MARGIN; + bMirrored = false; +} + +bool IsNarrow( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_NARROW_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_NARROW_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_NARROW_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_NARROW_VALUE) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetNarrow( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_NARROW_VALUE; + nPageRightMargin = SWPAGE_NARROW_VALUE; + nPageTopMargin = SWPAGE_NARROW_VALUE; + nPageBottomMargin = SWPAGE_NARROW_VALUE; + bMirrored = false; +} + +bool IsModerate( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_MODERATE_LR) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_MODERATE_LR) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetModerate( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_MODERATE_LR; + nPageRightMargin = SWPAGE_MODERATE_LR; + nPageTopMargin = SWPAGE_WIDE_VALUE1; + nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = false; +} + +bool IsNormal075( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_NORMAL_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_NORMAL_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_NORMAL_VALUE) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_NORMAL_VALUE) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetNormal075( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_NORMAL_VALUE; + nPageRightMargin = SWPAGE_NORMAL_VALUE; + nPageTopMargin = SWPAGE_NORMAL_VALUE; + nPageBottomMargin = SWPAGE_NORMAL_VALUE; + bMirrored = false; +} + +bool IsNormal100( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetNormal100( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_WIDE_VALUE1; + nPageRightMargin = SWPAGE_WIDE_VALUE1; + nPageTopMargin = SWPAGE_WIDE_VALUE1; + nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = false; +} + +bool IsNormal125( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_WIDE_VALUE3) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_WIDE_VALUE3) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetNormal125( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_WIDE_VALUE3; + nPageRightMargin = SWPAGE_WIDE_VALUE3; + nPageTopMargin = SWPAGE_WIDE_VALUE1; + nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = false; +} + +bool IsWide( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_WIDE_VALUE2) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_WIDE_VALUE2) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + !bMirrored ); +} + +void SetWide( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_WIDE_VALUE2; + nPageRightMargin = SWPAGE_WIDE_VALUE2; + nPageTopMargin = SWPAGE_WIDE_VALUE1; + nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = false; +} + +bool IsMirrored( const long nPageLeftMargin, const long nPageRightMargin, + const long nPageTopMargin, const long nPageBottomMargin, bool bMirrored) +{ + return( std::abs(nPageLeftMargin - SWPAGE_WIDE_VALUE3) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SWPAGE_WIDE_VALUE1) <= SWPAGE_UNIT_THRESHOLD && + bMirrored ); +} + +void SetMirrored( long& nPageLeftMargin, long& nPageRightMargin, + long& nPageTopMargin, long& nPageBottomMargin, bool& bMirrored) +{ + nPageLeftMargin = SWPAGE_WIDE_VALUE3; + nPageRightMargin = SWPAGE_WIDE_VALUE1; + nPageTopMargin = SWPAGE_WIDE_VALUE1; + nPageBottomMargin = SWPAGE_WIDE_VALUE1; + bMirrored = true; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageOrientationControl.cxx b/sw/source/uibase/sidebar/PageOrientationControl.cxx new file mode 100644 index 000000000..d44dd3a5b --- /dev/null +++ b/sw/source/uibase/sidebar/PageOrientationControl.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PageOrientationControl.hxx" +#include "PageMarginControl.hxx" +#include <PageOrientationPopup.hxx> +#include <com/sun/star/document/XUndoManager.hpp> +#include <com/sun/star/document/XUndoManagerSupplier.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/viewsh.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <cmdid.h> + +namespace { + css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame ) + { + const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController(); + if ( xController.is() ) + { + const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel(); + if ( xModel.is() ) + { + const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW ); + return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW ); + } + } + + return css::uno::Reference< css::document::XUndoManager > (); + } +} + +namespace sw::sidebar { + +PageOrientationControl::PageOrientationControl(PageOrientationPopup* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/swriter/ui/pageorientationcontrol.ui", "PageOrientationControl") + , m_xPortrait(m_xBuilder->weld_button("portrait")) + , m_xLandscape(m_xBuilder->weld_button("landscape")) + , m_xControl(pControl) + , mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ) + , mpPageSizeItem( new SvxSizeItem(SID_ATTR_PAGE_SIZE) ) + , mpPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ) + , mpPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ) +{ + m_xPortrait->connect_clicked( LINK( this, PageOrientationControl,ImplOrientationHdl ) ); + m_xLandscape->connect_clicked( LINK( this, PageOrientationControl,ImplOrientationHdl ) ); +} + +void PageOrientationControl::GrabFocus() +{ + m_xPortrait->grab_focus(); +} + +PageOrientationControl::~PageOrientationControl() +{ +} + +void PageOrientationControl::ExecuteMarginLRChange( + const long nPageLeftMargin, + const long nPageRightMargin ) +{ + mpPageLRMarginItem->SetLeft( nPageLeftMargin ); + mpPageLRMarginItem->SetRight( nPageRightMargin ); + SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE, + SfxCallMode::RECORD, { mpPageLRMarginItem.get() }); +} + +void PageOrientationControl::ExecuteMarginULChange( + const long nPageTopMargin, + const long nPageBottomMargin ) +{ + mpPageULMarginItem->SetUpper( nPageTopMargin ); + mpPageULMarginItem->SetLower( nPageBottomMargin ); + SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE, + SfxCallMode::RECORD, { mpPageULMarginItem.get() }); +} + +void PageOrientationControl::ExecuteOrientationChange( const bool bLandscape ) +{ + css::uno::Reference< css::document::XUndoManager > mxUndoManager( + getUndoManager( SfxViewFrame::Current()->GetFrame().GetFrameInterface() ) ); + + if ( mxUndoManager.is() ) + mxUndoManager->enterUndoContext( "" ); + + const SfxPoolItem* pItem; + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pItem); + mpPageSizeItem.reset( static_cast<SvxSizeItem*>(pItem->Clone()) ); + + // Prevent accidental toggling of page orientation + if ((mpPageSizeItem->GetWidth() > mpPageSizeItem->GetHeight()) == bLandscape) + { + if ( mxUndoManager.is() ) + mxUndoManager->leaveUndoContext(); + return; + } + + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pItem); + mpPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pItem->Clone()) ); + + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pItem); + mpPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pItem->Clone()) ); + + { + // set new page orientation + mpPageItem->SetLandscape( bLandscape ); + + // swap the width and height of the page size + const long nRotatedWidth = mpPageSizeItem->GetSize().Height(); + const long nRotatedHeight = mpPageSizeItem->GetSize().Width(); + mpPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight)); + + // apply changed attributes + if (SfxViewShell::Current()) + { + SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, + SfxCallMode::RECORD, { mpPageSizeItem.get(), mpPageItem.get() }); + } + } + + // check, if margin values still fit to the changed page size. + // if not, adjust margin values + { + const long nML = mpPageLRMarginItem->GetLeft(); + const long nMR = mpPageLRMarginItem->GetRight(); + const long nTmpPW = nML + nMR + MINBODY; + + const long nPW = mpPageSizeItem->GetSize().Width(); + + if ( nTmpPW > nPW ) + { + if ( nML <= nMR ) + { + ExecuteMarginLRChange( mpPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ) ); + } + else + { + ExecuteMarginLRChange( nML - (nTmpPW - nPW ), mpPageLRMarginItem->GetRight() ); + } + } + + const long nMT = mpPageULMarginItem->GetUpper(); + const long nMB = mpPageULMarginItem->GetLower(); + const long nTmpPH = nMT + nMB + MINBODY; + + const long nPH = mpPageSizeItem->GetSize().Height(); + + if ( nTmpPH > nPH ) + { + if ( nMT <= nMB ) + { + ExecuteMarginULChange( mpPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ) ); + } + else + { + ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), mpPageULMarginItem->GetLower() ); + } + } + } + + if ( mxUndoManager.is() ) + mxUndoManager->leaveUndoContext(); +} + +IMPL_LINK(PageOrientationControl, ImplOrientationHdl, weld::Button&, rControl, void) +{ + if (&rControl == m_xPortrait.get()) + ExecuteOrientationChange( false ); + else + ExecuteOrientationChange( true ); + + m_xControl->EndPopupMode(); +} + +} // end of namespace sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageOrientationControl.hxx b/sw/source/uibase/sidebar/PageOrientationControl.hxx new file mode 100644 index 000000000..7cb825406 --- /dev/null +++ b/sw/source/uibase/sidebar/PageOrientationControl.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEORIENTATIONCONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGEORIENTATIONCONTROL_HXX + +#include <memory> +#include <svtools/toolbarmenu.hxx> +#include <svx/pageitem.hxx> +#include <svx/rulritem.hxx> +#include <editeng/sizeitem.hxx> + +class PageOrientationPopup; + +namespace sw::sidebar { + +class PageOrientationControl final : public WeldToolbarPopup +{ +public: + explicit PageOrientationControl(PageOrientationPopup* pControl, weld::Widget* pParent); + virtual void GrabFocus() override; + virtual ~PageOrientationControl() override; + +private: + std::unique_ptr<weld::Button> m_xPortrait; + std::unique_ptr<weld::Button> m_xLandscape; + rtl::Reference<PageOrientationPopup> m_xControl; + + std::unique_ptr<SvxPageItem> mpPageItem; + std::unique_ptr<SvxSizeItem> mpPageSizeItem; + std::unique_ptr<SvxLongLRSpaceItem> mpPageLRMarginItem; + std::unique_ptr<SvxLongULSpaceItem> mpPageULMarginItem; + + void ExecuteMarginULChange(const long nPageTopMargin, const long nPageBottomMargin); + void ExecuteMarginLRChange(const long nPageLeftMargin, const long nPageRightMargin); + void ExecuteOrientationChange(const bool bLandscape); + + DECL_LINK(ImplOrientationHdl, weld::Button&, void); +}; + +} // end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageOrientationPopup.cxx b/sw/source/uibase/sidebar/PageOrientationPopup.cxx new file mode 100644 index 000000000..9b34f3c1d --- /dev/null +++ b/sw/source/uibase/sidebar/PageOrientationPopup.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 <PageOrientationPopup.hxx> +#include "PageOrientationControl.hxx" +#include <svx/pageitem.hxx> +#include <vcl/toolbox.hxx> + +PageOrientationPopup::PageOrientationPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext) + : PopupWindowController(rContext, nullptr, OUString()) +{ +} + +void PageOrientationPopup::initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) +{ + PopupWindowController::initialize(rArguments); + + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL) + pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId)); +} + +PageOrientationPopup::~PageOrientationPopup() +{ +} + +std::unique_ptr<WeldToolbarPopup> PageOrientationPopup::weldPopupWindow() +{ + return std::make_unique<sw::sidebar::PageOrientationControl>(this, m_pToolbar); +} + +VclPtr<vcl::Window> PageOrientationPopup::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sw::sidebar::PageOrientationControl>(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +OUString PageOrientationPopup::getImplementationName() +{ + return "lo.writer.PageOrientationToolBoxControl"; +} + +css::uno::Sequence<OUString> PageOrientationPopup::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_PageOrientationToolBoxControl_get_implementation( + css::uno::XComponentContext* rContext, + css::uno::Sequence<css::uno::Any> const & ) +{ + return cppu::acquire(new PageOrientationPopup(rContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageSizeControl.cxx b/sw/source/uibase/sidebar/PageSizeControl.cxx new file mode 100644 index 000000000..5abdb4788 --- /dev/null +++ b/sw/source/uibase/sidebar/PageSizeControl.cxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 "PageSizeControl.hxx" + +#include <cmdid.h> +#include <svx/pageitem.hxx> +#include <svx/sidebar/ValueSetWithTextControl.hxx> + +#include <unotools/localedatawrapper.hxx> +#include <rtl/character.hxx> +#include <editeng/paperinf.hxx> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewfrm.hxx> + +#include <vcl/settings.hxx> +#include <svl/itempool.hxx> +#include <svl/intitem.hxx> +#include <svtools/unitconv.hxx> +#include <editeng/sizeitem.hxx> + +#include <PageSizePopup.hxx> + +namespace +{ + FieldUnit lcl_GetFieldUnit() + { + FieldUnit eUnit = FieldUnit::INCH; + const SfxPoolItem* pItem = nullptr; + SfxItemState eState = SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_METRIC, pItem ); + if ( pItem && eState >= SfxItemState::DEFAULT ) + { + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + } + else + { + return SfxModule::GetCurrentFieldUnit(); + } + + return eUnit; + } + + MapUnit lcl_GetUnit() + { + SfxItemPool &rPool = SfxGetpApp()->GetPool(); + sal_uInt16 nWhich = rPool.GetWhich( SID_ATTR_PAGE_SIZE ); + return rPool.GetMetric( nWhich ); + } +} + +namespace sw::sidebar { + +PageSizeControl::PageSizeControl(PageSizePopup* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/swriter/ui/pagesizecontrol.ui", "PageSizeControl") + , mxMoreButton(m_xBuilder->weld_button("moreoptions")) + , mxWidthHeightField(m_xBuilder->weld_metric_spin_button("metric", FieldUnit::CM)) + , mxSizeValueSet(new svx::sidebar::ValueSetWithTextControl) + , mxSizeValueSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxSizeValueSet)) + , mxControl(pControl) + , maPaperList() +{ + mxWidthHeightField->set_unit(FieldUnit::CM); + mxWidthHeightField->set_range(0, 9999, FieldUnit::NONE); + mxWidthHeightField->set_digits(2); + mxWidthHeightField->set_increments(10, 100, FieldUnit::NONE); + SetFieldUnit( *mxWidthHeightField, lcl_GetFieldUnit() ); + + maPaperList.push_back( PAPER_A3 ); + maPaperList.push_back( PAPER_A4 ); + maPaperList.push_back( PAPER_A5 ); + maPaperList.push_back( PAPER_B4_ISO ); + maPaperList.push_back( PAPER_B5_ISO ); + maPaperList.push_back( PAPER_ENV_C5 ); + maPaperList.push_back( PAPER_LETTER ); + maPaperList.push_back( PAPER_LEGAL ); + + mxSizeValueSet->SetStyle( mxSizeValueSet->GetStyle() | WB_3DLOOK | WB_NO_DIRECTSELECT ); + mxSizeValueSet->SetColor( Application::GetSettings().GetStyleSettings().GetMenuColor() ); + + sal_uInt16 nSelectedItem = 0; + { + OUString aMetricStr; + { + const OUString aText = mxWidthHeightField->get_text(); + for (short i = aText.getLength() - 1; i >= 0; i--) + { + sal_Unicode c = aText[i]; + if ( rtl::isAsciiAlpha(c) || (c == '\'') || (c == '\"') || (c == '%') ) + { + aMetricStr = OUStringChar(c) + aMetricStr; + } + else + { + if (!aMetricStr.isEmpty()) + { + break; + } + } + } + } + + bool bLandscape = false; + const SfxPoolItem* pItem; + const SvxSizeItem* pSize = nullptr; + if ( SfxViewFrame::Current() ) + { + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE, pItem ); + bLandscape = static_cast<const SvxPageItem*>(pItem)->IsLandscape(); + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE_SIZE, pItem ); + pSize = static_cast<const SvxSizeItem*>(pItem); + } + + const LocaleDataWrapper& localeDataWrapper = Application::GetSettings().GetLocaleDataWrapper(); + OUString aWidthStr; + OUString aHeightStr; + OUString aItemText2; + for ( std::vector< Paper >::size_type nPaperIdx = 0; + nPaperIdx < maPaperList.size(); + ++nPaperIdx ) + { + Size aPaperSize = SvxPaperInfo::GetPaperSize( maPaperList[ nPaperIdx ] ); + if ( bLandscape ) + { + Swap( aPaperSize ); + } + + mxWidthHeightField->set_value( mxWidthHeightField->normalize( aPaperSize.Width() ), FieldUnit::TWIP ); + aWidthStr = localeDataWrapper.getNum( + mxWidthHeightField->get_value(FieldUnit::NONE), + mxWidthHeightField->get_digits(), + true, + true ); + + mxWidthHeightField->set_value( mxWidthHeightField->normalize( aPaperSize.Height() ), FieldUnit::TWIP); + aHeightStr = localeDataWrapper.getNum( + mxWidthHeightField->get_value(FieldUnit::NONE), + mxWidthHeightField->get_digits(), + true, + true ); + + aItemText2 = aWidthStr + " x " + aHeightStr + " " + aMetricStr; + + mxSizeValueSet->AddItem( + SvxPaperInfo::GetName( maPaperList[ nPaperIdx ] ), + aItemText2 ); + + if ( pSize && aPaperSize == pSize->GetSize() ) + { + nSelectedItem = nPaperIdx + 1; + } + } + } + mxSizeValueSet->SetNoSelection(); + mxSizeValueSet->SetSelectHdl( LINK(this, PageSizeControl, ImplSizeHdl ) ); + mxSizeValueSet->Show(); + mxSizeValueSet->Resize(); + + mxSizeValueSet->SelectItem( nSelectedItem ); + mxSizeValueSet->SetFormat(); + mxSizeValueSet->Invalidate(); + + mxMoreButton->connect_clicked( LINK( this, PageSizeControl, MoreButtonClickHdl_Impl ) ); + mxMoreButton->grab_focus(); +} + +void PageSizeControl::GrabFocus() +{ + mxSizeValueSet->GrabFocus(); +} + +PageSizeControl::~PageSizeControl() +{ +} + +void PageSizeControl::ExecuteSizeChange( const Paper ePaper ) +{ + bool bLandscape = false; + const SfxPoolItem *pItem; + MapUnit eUnit = lcl_GetUnit(); + if ( SfxViewFrame::Current() ) + { + SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState( SID_ATTR_PAGE, pItem ); + bLandscape = static_cast<const SvxPageItem*>(pItem)->IsLandscape(); + + std::unique_ptr<SvxSizeItem> pPageSizeItem( new SvxSizeItem(SID_ATTR_PAGE_SIZE) ); + Size aPageSize = SvxPaperInfo::GetPaperSize( ePaper, eUnit ); + if ( bLandscape ) + { + Swap( aPageSize ); + } + pPageSizeItem->SetSize( aPageSize ); + + SfxViewFrame::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, + SfxCallMode::RECORD, { pPageSizeItem.get() }); + } +} + + +IMPL_LINK_NOARG(PageSizeControl, ImplSizeHdl, ValueSet*, void) +{ + mxSizeValueSet->SetNoSelection(); + const sal_uInt16 nSelectedPaper = mxSizeValueSet->GetSelectedItemId(); + const Paper ePaper = maPaperList[nSelectedPaper - 1]; + ExecuteSizeChange( ePaper ); + + mxControl->EndPopupMode(); +} + +IMPL_LINK_NOARG(PageSizeControl, MoreButtonClickHdl_Impl, weld::Button&, void) +{ + if ( SfxViewFrame::Current() ) + SfxViewFrame::Current()->GetDispatcher()->Execute( FN_FORMAT_PAGE_SETTING_DLG, SfxCallMode::ASYNCHRON ); + mxControl->EndPopupMode(); +} + +} // end of namespace sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageSizeControl.hxx b/sw/source/uibase/sidebar/PageSizeControl.hxx new file mode 100644 index 000000000..be43d0900 --- /dev/null +++ b/sw/source/uibase/sidebar/PageSizeControl.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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGESIZECONTROL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGESIZECONTROL_HXX + +#include <i18nutil/paper.hxx> + +#include <svtools/toolbarmenu.hxx> +#include <svtools/valueset.hxx> + +#include <vector> + +namespace svx::sidebar { class ValueSetWithTextControl; } +class PageSizePopup; +class ValueSet; + +namespace sw::sidebar { + +class PageSizeControl final : public WeldToolbarPopup +{ +public: + explicit PageSizeControl(PageSizePopup* pControl, weld::Widget* pParent); + virtual void GrabFocus() override; + virtual ~PageSizeControl() override; + +private: + std::unique_ptr<weld::Button> mxMoreButton; + // hidden metric field + std::unique_ptr<weld::MetricSpinButton> mxWidthHeightField; + std::unique_ptr<svx::sidebar::ValueSetWithTextControl> mxSizeValueSet; + std::unique_ptr<weld::CustomWeld> mxSizeValueSetWin; + rtl::Reference<PageSizePopup> mxControl; + + std::vector< Paper > maPaperList; + + static void ExecuteSizeChange( const Paper ePaper ); + + DECL_LINK(ImplSizeHdl, ValueSet*, void); + DECL_LINK(MoreButtonClickHdl_Impl, weld::Button&, void); +}; + +} // end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageSizePopup.cxx b/sw/source/uibase/sidebar/PageSizePopup.cxx new file mode 100644 index 000000000..0c69f73ba --- /dev/null +++ b/sw/source/uibase/sidebar/PageSizePopup.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 <PageSizePopup.hxx> +#include "PageSizeControl.hxx" +#include <editeng/sizeitem.hxx> +#include <vcl/toolbox.hxx> + +PageSizePopup::PageSizePopup(const css::uno::Reference<css::uno::XComponentContext>& rContext) + : PopupWindowController(rContext, nullptr, OUString()) +{ +} + +void PageSizePopup::initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) +{ + PopupWindowController::initialize(rArguments); + + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL) + pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId)); +} + +PageSizePopup::~PageSizePopup() +{ +} + +std::unique_ptr<WeldToolbarPopup> PageSizePopup::weldPopupWindow() +{ + return std::make_unique<sw::sidebar::PageSizeControl>(this, m_pToolbar); +} + +VclPtr<vcl::Window> PageSizePopup::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent, + std::make_unique<sw::sidebar::PageSizeControl>(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +OUString PageSizePopup::getImplementationName() +{ + return "lo.writer.PageSizeToolBoxControl"; +} + +css::uno::Sequence<OUString> PageSizePopup::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ToolbarController" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +lo_writer_PageSizeToolBoxControl_get_implementation( + css::uno::XComponentContext* rContext, + css::uno::Sequence<css::uno::Any> const & ) +{ + return cppu::acquire(new PageSizePopup(rContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageStylesPanel.cxx b/sw/source/uibase/sidebar/PageStylesPanel.cxx new file mode 100644 index 000000000..1815466c1 --- /dev/null +++ b/sw/source/uibase/sidebar/PageStylesPanel.cxx @@ -0,0 +1,604 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/intitem.hxx> +#include <svx/colorbox.hxx> +#include <svx/drawitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/SvxNumOptionsTabPageHelper.hxx> +#include "PageStylesPanel.hxx" +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/objsh.hxx> +#include <cmdid.h> + +using namespace ::com::sun::star; + +namespace sw::sidebar{ + +namespace { + +enum eFillStyle +{ + NONE, + SOLID, + GRADIENT, + HATCH, + BITMAP, + PATTERN +}; + +} + +const SvxPageUsage aArr[] = +{ + SvxPageUsage::All, + SvxPageUsage::Mirror, + SvxPageUsage::Right, + SvxPageUsage::Left +}; + + +static sal_uInt16 PageUsageToPos_Impl( SvxPageUsage nUsage ) +{ + for ( size_t i = 0; i < SAL_N_ELEMENTS(aArr); ++i ) + if ( aArr[i] == nUsage ) + return i; + return 3; +} + + +static SvxPageUsage PosToPageUsage_Impl( sal_uInt16 nPos ) +{ + if ( nPos >= SAL_N_ELEMENTS(aArr) ) + return SvxPageUsage::NONE; + return aArr[nPos]; +} + +VclPtr<vcl::Window> PageStylesPanel::Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no parent window given to PageStylesPanel::Create", nullptr, 0); + if( !rxFrame.is() ) + throw ::com::sun::star::lang::IllegalArgumentException("no XFrame given to PageStylesPanel::Create", nullptr, 0); + if( pBindings == nullptr ) + throw ::com::sun::star::lang::IllegalArgumentException("no SfxBindings given to PageStylesPanel::Create", nullptr, 0); + + return VclPtr<PageStylesPanel>::Create(pParent, rxFrame, pBindings); +} + +PageStylesPanel::PageStylesPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings + ) : + PanelLayout(pParent, "PageStylesPanel", "modules/swriter/ui/pagestylespanel.ui", rxFrame), + mpBindings( pBindings ), + mpPageColumnItem( new SfxInt16Item(SID_ATTR_PAGE_COLUMN) ), + mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ), + maPageColumnControl(SID_ATTR_PAGE_COLUMN, *pBindings, *this), + maPageNumFormatControl( SID_ATTR_PAGE, *pBindings, *this ), + maBgColorControl( SID_ATTR_PAGE_COLOR, *pBindings, *this ), + maBgHatchingControl( SID_ATTR_PAGE_HATCH, *pBindings, *this ), + maBgGradientControl( SID_ATTR_PAGE_GRADIENT, *pBindings, *this ), + maBgBitmapControl( SID_ATTR_PAGE_BITMAP, *pBindings, *this ), + maBgFillStyleControl(SID_ATTR_PAGE_FILLSTYLE, *pBindings, *this), + mxBgColorLB(new ColorListBox(m_xBuilder->weld_menu_button("lbcolor"), GetFrameWeld())), + mxBgHatchingLB(m_xBuilder->weld_combo_box("lbhatching")), + mxBgGradientLB(new ColorListBox(m_xBuilder->weld_menu_button("lbgradient"), GetFrameWeld())), + mxBgBitmapLB(m_xBuilder->weld_combo_box("lbbitmap")), + mxLayoutSelectLB(m_xBuilder->weld_combo_box("layoutbox")), + mxColumnCount(m_xBuilder->weld_combo_box("columnbox")), + mxNumberSelectLB(new SvxPageNumberListBox(m_xBuilder->weld_combo_box("numberbox"))), + mxBgFillType(m_xBuilder->weld_combo_box("bgselect")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")), + aCustomEntry() +{ + Initialize(); +} + +PageStylesPanel::~PageStylesPanel() +{ + disposeOnce(); +} + +void PageStylesPanel::dispose() +{ + mxColumnCount.reset(); + mxNumberSelectLB.reset(); + mxBgFillType.reset(); + mxBgColorLB.reset(); + mxBgHatchingLB.reset(); + mxBgGradientLB.reset(); + mxBgBitmapLB.reset(); + mxLayoutSelectLB.reset(); + mxCustomEntry.reset(); + + maBgBitmapControl.dispose(); + maBgColorControl.dispose(); + maBgFillStyleControl.dispose(); + maBgGradientControl.dispose(); + maBgHatchingControl.dispose(); + maPageColumnControl.dispose(); + maPageNumFormatControl.dispose(); + PanelLayout::dispose(); +} + +void PageStylesPanel::Initialize() +{ + SvxFillTypeBox::Fill(*mxBgFillType); + + aCustomEntry = mxCustomEntry->get_label(); + mpBindings->Invalidate(SID_ATTR_PAGE_COLUMN); + mpBindings->Invalidate(SID_ATTR_PAGE); + mpBindings->Invalidate(SID_ATTR_PAGE_FILLSTYLE); + Update(); + + mxColumnCount->connect_changed( LINK(this, PageStylesPanel, ModifyColumnCountHdl) ); + SvxNumOptionsTabPageHelper::GetI18nNumbering(mxNumberSelectLB->get_widget(), ::std::numeric_limits<sal_uInt16>::max()); + mxNumberSelectLB->connect_changed( LINK(this, PageStylesPanel, ModifyNumberingHdl) ); + mxLayoutSelectLB->connect_changed( LINK(this, PageStylesPanel, ModifyLayoutHdl) ); + mxBgFillType->connect_changed( LINK(this, PageStylesPanel, ModifyFillStyleHdl)); + mxBgColorLB->SetSelectHdl( LINK(this, PageStylesPanel, ModifyFillColorListHdl)); + mxBgGradientLB->SetSelectHdl( LINK(this, PageStylesPanel, ModifyFillColorListHdl)); + mxBgHatchingLB->connect_changed( LINK(this, PageStylesPanel, ModifyFillColorHdl)); + mxBgBitmapLB->connect_changed( LINK(this, PageStylesPanel, ModifyFillColorHdl)); +} + +void PageStylesPanel::Update() +{ + const eFillStyle eXFS = static_cast<eFillStyle>(mxBgFillType->get_active()); + SfxObjectShell* pSh = SfxObjectShell::Current(); + switch(eXFS) + { + case NONE: + { + mxBgColorLB->hide(); + mxBgHatchingLB->hide(); + mxBgGradientLB->hide(); + mxBgBitmapLB->hide(); + } + break; + case SOLID: + { + mxBgBitmapLB->hide(); + mxBgGradientLB->hide(); + mxBgHatchingLB->hide(); + mxBgColorLB->show(); + const Color aColor = GetColorSetOrDefault(); + mxBgColorLB->SelectEntry(aColor); + } + break; + case GRADIENT: + { + mxBgBitmapLB->hide(); + mxBgHatchingLB->hide(); + mxBgColorLB->show(); + mxBgGradientLB->show(); + + const XGradient xGradient = GetGradientSetOrDefault(); + const Color aStartColor = xGradient.GetStartColor(); + mxBgColorLB->SelectEntry(aStartColor); + const Color aEndColor = xGradient.GetEndColor(); + mxBgGradientLB->SelectEntry(aEndColor); + } + break; + + case HATCH: + { + mxBgColorLB->hide(); + mxBgGradientLB->hide(); + mxBgBitmapLB->hide(); + mxBgHatchingLB->show(); + mxBgHatchingLB->clear(); + SvxFillAttrBox::Fill(*mxBgHatchingLB, pSh->GetItem(SID_HATCH_LIST)->GetHatchList()); + + const OUString aHatchName = GetHatchingSetOrDefault(); + mxBgHatchingLB->set_active_text( aHatchName ); + } + break; + + case BITMAP: + case PATTERN: + { + mxBgColorLB->hide(); + mxBgGradientLB->hide(); + mxBgHatchingLB->hide(); + mxBgBitmapLB->show(); + mxBgBitmapLB->clear(); + OUString aBitmapName; + + if( eXFS == BITMAP ) + { + SvxFillAttrBox::Fill(*mxBgBitmapLB, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList()); + aBitmapName = GetBitmapSetOrDefault(); + } + else + { + SvxFillAttrBox::Fill(*mxBgBitmapLB, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList()); + aBitmapName = GetPatternSetOrDefault(); + } + + mxBgBitmapLB->set_active_text( aBitmapName ); + } + break; + + default: + break; + } +} + +Color const & PageStylesPanel::GetColorSetOrDefault() +{ + if ( !mpBgColorItem ) + mpBgColorItem.reset( new XFillColorItem( OUString(), Color(0x72, 0x9f, 0xcf) ) ); + + return mpBgColorItem->GetColorValue(); +} + +XGradient const & PageStylesPanel::GetGradientSetOrDefault() +{ + if( !mpBgGradientItem ) + { + SfxObjectShell* pSh = SfxObjectShell::Current(); + const SvxGradientListItem * pGradListItem = pSh->GetItem(SID_GRADIENT_LIST); + const XGradient aGradient = pGradListItem->GetGradientList()->GetGradient(0)->GetGradient(); + const OUString aGradientName = pGradListItem->GetGradientList()->GetGradient(0)->GetName(); + + mpBgGradientItem.reset( new XFillGradientItem( aGradientName, aGradient ) ); + } + + return mpBgGradientItem->GetGradientValue(); +} + +OUString const & PageStylesPanel::GetHatchingSetOrDefault() +{ + if( !mpBgHatchItem ) + { + SfxObjectShell* pSh = SfxObjectShell::Current(); + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + const XHatch aHatch = pHatchListItem->GetHatchList()->GetHatch(0)->GetHatch(); + const OUString aHatchName = pHatchListItem->GetHatchList()->GetHatch(0)->GetName(); + + mpBgHatchItem.reset( new XFillHatchItem( aHatchName, aHatch ) ); + } + + return mpBgHatchItem->GetName(); +} + +OUString const & PageStylesPanel::GetBitmapSetOrDefault() +{ + if( !mpBgBitmapItem || mpBgBitmapItem->isPattern() ) + { + SfxObjectShell* pSh = SfxObjectShell::Current(); + const SvxBitmapListItem * pBmpListItem = pSh->GetItem(SID_BITMAP_LIST); + const GraphicObject aGraphObj = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetGraphicObject(); + const OUString aBmpName = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetName(); + + mpBgBitmapItem.reset( new XFillBitmapItem( aBmpName, aGraphObj ) ); + } + + return mpBgBitmapItem->GetName(); +} + +OUString const & PageStylesPanel::GetPatternSetOrDefault() +{ + if( !mpBgBitmapItem || !mpBgBitmapItem->isPattern() ) + { + SfxObjectShell* pSh = SfxObjectShell::Current(); + const SvxPatternListItem * pPatternListItem = pSh->GetItem(SID_PATTERN_LIST); + const GraphicObject aGraphObj = pPatternListItem->GetPatternList()->GetBitmap(0)->GetGraphicObject(); + const OUString aPatternName = pPatternListItem->GetPatternList()->GetBitmap(0)->GetName(); + + mpBgBitmapItem.reset( new XFillBitmapItem( aPatternName, aGraphObj ) ); + } + + return mpBgBitmapItem->GetName(); +} + +void PageStylesPanel::NotifyItemUpdate( + const sal_uInt16 nSid, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + if(IsDisposed()) + return; + + switch(nSid) + { + case SID_ATTR_PAGE_COLUMN: + { + if ( eState >= SfxItemState::DEFAULT && + dynamic_cast< const SfxInt16Item *>( pState ) ) + { + mpPageColumnItem.reset( static_cast<SfxInt16Item*>(pState->Clone()) ); + if(mpPageColumnItem->GetValue() <= 5) + { + mxColumnCount->set_active(mpPageColumnItem->GetValue() - 1); + int nIndex = mxColumnCount->find_text(aCustomEntry); + if (nIndex != -1) + mxColumnCount->remove(nIndex); + } + else + { + if (mxColumnCount->find_text(aCustomEntry) == -1) + mxColumnCount->append_text(aCustomEntry); + mxColumnCount->set_active_text(aCustomEntry); + } + } + } + break; + + case SID_ATTR_PAGE: + { + if( eState >= SfxItemState::DEFAULT && + dynamic_cast< const SvxPageItem*>( pState ) ) + { + mpPageItem.reset( static_cast<SvxPageItem*>(pState->Clone()) ); + SvxNumType eNumType = mpPageItem->GetNumType(); + mxNumberSelectLB->set_active_id(eNumType); + + SvxPageUsage nUse = mpPageItem->GetPageUsage(); + mxLayoutSelectLB->set_active( PageUsageToPos_Impl( nUse ) ); + } + } + break; + + case SID_ATTR_PAGE_COLOR: + { + if(eState >= SfxItemState::DEFAULT) + { + mxBgFillType->set_active( static_cast<sal_Int32>(SOLID) ); + mpBgColorItem.reset(pState ? static_cast< XFillColorItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + if(eState >= SfxItemState::DEFAULT) + { + mxBgFillType->set_active( static_cast<sal_Int32>(HATCH) ); + mpBgHatchItem.reset(pState ? static_cast < XFillHatchItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if(eState >= SfxItemState::DEFAULT) + { + mxBgFillType->set_active( static_cast<sal_Int32>(GRADIENT) ); + mpBgGradientItem.reset(pState ? static_cast< XFillGradientItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + case SID_ATTR_PAGE_BITMAP: + { + if(eState >= SfxItemState::DEFAULT) + { + mpBgBitmapItem.reset(pState ? static_cast< XFillBitmapItem* >(pState->Clone()) : nullptr); + if (mpBgBitmapItem) + { + if (mpBgBitmapItem->isPattern()) + mxBgFillType->set_active( static_cast<sal_Int32>(PATTERN) ); + else + mxBgFillType->set_active( static_cast<sal_Int32>(BITMAP) ); + } + Update(); + } + } + break; + + case SID_ATTR_PAGE_FILLSTYLE: + { + const XFillStyleItem* pFillStyleItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pFillStyleItem = dynamic_cast< const XFillStyleItem* >(pState); + if (pFillStyleItem) + { + css::drawing::FillStyle eXFS = pFillStyleItem->GetValue(); + switch(eXFS) + { + case drawing::FillStyle_NONE: + mxBgFillType->set_active( static_cast<sal_Int32>(NONE) ); + break; + case drawing::FillStyle_SOLID: + mxBgFillType->set_active( static_cast<sal_Int32>(SOLID) ); + break; + case drawing::FillStyle_GRADIENT: + mxBgFillType->set_active( static_cast<sal_Int32>(GRADIENT) ); + break; + case drawing::FillStyle_HATCH: + mxBgFillType->set_active( static_cast<sal_Int32>(HATCH) ); + break; + case drawing::FillStyle_BITMAP: + if (mpBgBitmapItem->isPattern()) + mxBgFillType->set_active( static_cast<sal_Int32>(PATTERN) ); + else + mxBgFillType->set_active( static_cast<sal_Int32>(BITMAP) ); + break; + default: + break; + } + Update(); + } + } + break; + + default: + break; + } +} + +IMPL_LINK_NOARG( PageStylesPanel, ModifyColumnCountHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nColumnType = mxColumnCount->get_active() + 1; + mpPageColumnItem->SetValue( nColumnType ); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLUMN, + SfxCallMode::RECORD, { mpPageColumnItem.get() }); +} + +IMPL_LINK_NOARG( PageStylesPanel, ModifyNumberingHdl, weld::ComboBox&, void ) +{ + SvxNumType nEntryData = mxNumberSelectLB->get_active_id(); + mpPageItem->SetNumType(nEntryData); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE, SfxCallMode::RECORD, { mpPageItem.get() }); +} + +IMPL_LINK_NOARG( PageStylesPanel, ModifyLayoutHdl, weld::ComboBox&, void ) +{ + sal_uInt16 nUse = mxLayoutSelectLB->get_active(); + mpPageItem->SetPageUsage(PosToPageUsage_Impl(nUse)); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_PAGE, SfxCallMode::RECORD, { mpPageItem.get() }); +} + +IMPL_LINK_NOARG(PageStylesPanel, ModifyFillStyleHdl, weld::ComboBox&, void) +{ + const eFillStyle eXFS = static_cast<eFillStyle>(mxBgFillType->get_active()); + Update(); + + switch (eXFS) + { + case NONE: + { + const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_FILLSTYLE, SfxCallMode::RECORD, { &aXFillStyleItem }); + } + break; + + case SOLID: + { + XFillColorItem aItem( OUString(), mpBgColorItem->GetColorValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + break; + + case GRADIENT: + { + XFillGradientItem aItem( mpBgGradientItem->GetName(), mpBgGradientItem->GetGradientValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + break; + + case HATCH: + { + XFillHatchItem aItem( mpBgHatchItem->GetName(), mpBgHatchItem->GetHatchValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + break; + + case BITMAP: + case PATTERN: + { + XFillBitmapItem aItem( mpBgBitmapItem->GetName(), mpBgBitmapItem->GetGraphicObject() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + break; + + default: + break; + } +} + +void PageStylesPanel::ModifyFillColor() +{ + const eFillStyle eXFS = static_cast<eFillStyle>(mxBgFillType->get_active()); + SfxObjectShell* pSh = SfxObjectShell::Current(); + switch(eXFS) + { + case SOLID: + { + XFillColorItem aItem(OUString(), mxBgColorLB->GetSelectEntryColor()); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + break; + case GRADIENT: + { + XGradient aGradient; + aGradient.SetStartColor(mxBgColorLB->GetSelectEntryColor()); + aGradient.SetEndColor(mxBgGradientLB->GetSelectEntryColor()); + + XFillGradientItem aItem(aGradient); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + break; + case HATCH: + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + sal_uInt16 nPos = mxBgHatchingLB->get_active(); + XHatch aHatch = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetHatch(); + const OUString aHatchName = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetName(); + + XFillHatchItem aItem(aHatchName, aHatch); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + break; + case BITMAP: + case PATTERN: + { + sal_Int16 nPos = mxBgBitmapLB->get_active(); + GraphicObject aBitmap; + OUString aBitmapName; + + if ( eXFS == BITMAP ) + { + SvxBitmapListItem const * pBitmapListItem = pSh->GetItem(SID_BITMAP_LIST); + aBitmap = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetGraphicObject(); + aBitmapName = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetName(); + } + else + { + SvxPatternListItem const * pPatternListItem = pSh->GetItem(SID_PATTERN_LIST); + aBitmap = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetGraphicObject(); + aBitmapName = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetName(); + } + + XFillBitmapItem aItem(aBitmapName, aBitmap); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(PageStylesPanel, ModifyFillColorHdl, weld::ComboBox&, void) +{ + ModifyFillColor(); +} + +IMPL_LINK_NOARG(PageStylesPanel, ModifyFillColorListHdl, ColorListBox&, void) +{ + ModifyFillColor(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/PageStylesPanel.hxx b/sw/source/uibase/sidebar/PageStylesPanel.hxx new file mode 100644 index 000000000..7e3c09d2e --- /dev/null +++ b/sw/source/uibase/sidebar/PageStylesPanel.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGESTYLESPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_PAGESTYLESPANEL_HXX + +#include <memory> +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <sfx2/sidebar/ControllerItem.hxx> + +#include <svx/pageitem.hxx> + +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/itemwin.hxx> +#include <svx/pagenumberlistbox.hxx> + +class List; +class ColorListBox; +namespace sw::sidebar { + +class PageStylesPanel: + public PanelLayout, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + static VclPtr<vcl::Window> Create( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + + virtual void NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + SfxBindings* GetBindings() const { return mpBindings; } + PageStylesPanel( + vcl::Window* pParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rxFrame, + SfxBindings* pBindings); + virtual ~PageStylesPanel() override; + virtual void dispose() override; + +private: + + SfxBindings* mpBindings; + + ::std::unique_ptr<SfxInt16Item> mpPageColumnItem; + ::std::unique_ptr<SvxPageItem> mpPageItem; + ::std::unique_ptr<XFillColorItem> mpBgColorItem; + ::std::unique_ptr<XFillGradientItem> mpBgGradientItem; + ::std::unique_ptr<XFillHatchItem> mpBgHatchItem; + ::std::unique_ptr<XFillBitmapItem> mpBgBitmapItem; + + ::sfx2::sidebar::ControllerItem maPageColumnControl; + ::sfx2::sidebar::ControllerItem maPageNumFormatControl; + ::sfx2::sidebar::ControllerItem maBgColorControl; + ::sfx2::sidebar::ControllerItem maBgHatchingControl; + ::sfx2::sidebar::ControllerItem maBgGradientControl; + ::sfx2::sidebar::ControllerItem maBgBitmapControl; + ::sfx2::sidebar::ControllerItem maBgFillStyleControl; + + std::unique_ptr<ColorListBox> mxBgColorLB; + std::unique_ptr<weld::ComboBox> mxBgHatchingLB; + std::unique_ptr<ColorListBox> mxBgGradientLB; + std::unique_ptr<weld::ComboBox> mxBgBitmapLB; + std::unique_ptr<weld::ComboBox> mxLayoutSelectLB; + std::unique_ptr<weld::ComboBox> mxColumnCount; + std::unique_ptr<SvxPageNumberListBox> mxNumberSelectLB; + std::unique_ptr<weld::ComboBox> mxBgFillType; + std::unique_ptr<weld::Label> mxCustomEntry; + OUString aCustomEntry; + + void Initialize(); + void Update(); + Color const & GetColorSetOrDefault(); + XGradient const & GetGradientSetOrDefault(); + OUString const & GetHatchingSetOrDefault(); + OUString const & GetBitmapSetOrDefault(); + OUString const & GetPatternSetOrDefault(); + + void ModifyFillColor(); + + DECL_LINK( ModifyColumnCountHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyNumberingHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyLayoutHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyFillStyleHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyFillColorHdl, weld::ComboBox&, void ); + DECL_LINK( ModifyFillColorListHdl, ColorListBox&, void ); +}; + +} //end of namespace sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/StylePresetsPanel.cxx b/sw/source/uibase/sidebar/StylePresetsPanel.cxx new file mode 100644 index 000000000..4d82afe29 --- /dev/null +++ b/sw/source/uibase/sidebar/StylePresetsPanel.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/. + * + */ + +#include <sal/config.h> + +#include "StylePresetsPanel.hxx" + +#include <vcl/image.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> + +#include <sfx2/objsh.hxx> +#include <sfx2/StylePreviewRenderer.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <sfx2/doctempl.hxx> + +#include <shellio.hxx> +#include <docsh.hxx> + +#include <sfx2/docfile.hxx> + +namespace sw::sidebar { + +namespace { + +void renderPreview(sfx2::StyleManager* pStyleManager, OutputDevice& aOutputDevice, + OUString const & sName, sal_Int32 nHeight, tools::Rectangle const & aRect) +{ + SfxStyleSheetBase* pStyleSheet = pStyleManager->Search(sName, SfxStyleFamily::Para); + + if (pStyleSheet) + { + std::unique_ptr<sfx2::StylePreviewRenderer> pStylePreviewRenderer + = pStyleManager->CreateStylePreviewRenderer(aOutputDevice, pStyleSheet, nHeight); + pStylePreviewRenderer->recalculate(); + pStylePreviewRenderer->render(aRect, sfx2::StylePreviewRenderer::RenderAlign::TOP); + } +} + +BitmapEx GenerateStylePreview(SfxObjectShell& rSource, OUString const & aName) +{ + sfx2::StyleManager* pStyleManager = rSource.GetStyleManager(); + + ScopedVclPtrInstance<VirtualDevice> pVirtualDev(*Application::GetDefaultDevice()); + + float fScalingFactor = pVirtualDev->GetDPIScaleFactor(); + + sal_Int32 nMargin = 6 * fScalingFactor; + + sal_Int32 nPreviewWidth = 144 * fScalingFactor; + + sal_Int32 nNameHeight = 16 * fScalingFactor; + sal_Int32 nTitleHeight = 32 * fScalingFactor; + sal_Int32 nHeadingHeight = 24 * fScalingFactor; + sal_Int32 nTextBodyHeight = 16 * fScalingFactor; + sal_Int32 nBottomMargin = 2 * fScalingFactor; + + sal_Int32 nNameFontSize = 12 * fScalingFactor; + + sal_Int32 nPreviewHeight = nNameHeight + nTitleHeight + nHeadingHeight + nTextBodyHeight + nBottomMargin; + + Size aSize(nPreviewWidth, nPreviewHeight); + + pVirtualDev->SetOutputSizePixel(aSize); + + pVirtualDev->SetLineColor(COL_LIGHTGRAY); + pVirtualDev->SetFillColor(); + + long y = 0; + { + pVirtualDev->SetFillColor(COL_LIGHTGRAY); + tools::Rectangle aNameRect(0, y, nPreviewWidth, nNameHeight); + pVirtualDev->DrawRect(aNameRect); + + vcl::Font aFont; + aFont.SetFontSize(Size(0, nNameFontSize)); + + pVirtualDev->SetFont(aFont); + + Size aTextSize(pVirtualDev->GetTextWidth(aName), pVirtualDev->GetTextHeight()); + + Point aPoint((aNameRect.GetWidth() / 2.0) - (aTextSize.Width() / 2.0), + y + (aNameRect.GetHeight() / 2.0) - (aTextSize.Height() / 2.0)); + + pVirtualDev->DrawText(aPoint, aName); + + y += nNameHeight; + } + + { + tools::Rectangle aRenderRect(Point(nMargin, y), aSize); + renderPreview(pStyleManager, *pVirtualDev, "Title", nTitleHeight, aRenderRect); + y += nTitleHeight; + } + + { + tools::Rectangle aRenderRect(Point(nMargin, y), aSize); + renderPreview(pStyleManager, *pVirtualDev, "Heading 1", nHeadingHeight, aRenderRect); + y += nHeadingHeight; + } + { + tools::Rectangle aRenderRect(Point(nMargin, y), aSize); + renderPreview(pStyleManager, *pVirtualDev, "Text Body", nTextBodyHeight, aRenderRect); + } + + return pVirtualDev->GetBitmapEx(Point(), aSize); +} + +BitmapEx CreatePreview(OUString const & aUrl, OUString const & aName) +{ + SfxMedium aMedium(aUrl, StreamMode::STD_READWRITE); + SfxObjectShell* pObjectShell = SfxObjectShell::Current(); + SfxObjectShellLock xTemplDoc = SfxObjectShell::CreateObjectByFactoryName(pObjectShell->GetFactory().GetFactoryName(), SfxObjectCreateMode::ORGANIZER); + xTemplDoc->DoInitNew(); + if (xTemplDoc->LoadFrom(aMedium)) + { + return GenerateStylePreview(*xTemplDoc, aName); + } + return BitmapEx(); +} + +} + +VclPtr<vcl::Window> StylePresetsPanel::Create (vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame) +{ + if (pParent == nullptr) + throw css::lang::IllegalArgumentException("no parent Window given to StylePresetsPanel::Create", nullptr, 0); + if (!rxFrame.is()) + throw css::lang::IllegalArgumentException("no XFrame given to StylePresetsPanel::Create", nullptr, 1); + + return VclPtr<StylePresetsPanel>::Create(pParent, rxFrame); +} + +StylePresetsPanel::StylePresetsPanel(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame) + : PanelLayout(pParent, "StylePresetsPanel", "modules/swriter/ui/sidebarstylepresets.ui", rxFrame) + , mxValueSet(new ValueSet(nullptr)) + , mxValueSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxValueSet)) +{ + mxValueSet->SetColCount(2); + + mxValueSet->SetDoubleClickHdl(LINK(this, StylePresetsPanel, DoubleClickHdl)); + + RefreshList(); +} + +void StylePresetsPanel::RefreshList() +{ + SfxDocumentTemplates aTemplates; + sal_uInt16 nCount = aTemplates.GetRegionCount(); + for (sal_uInt16 i = 0; i < nCount; ++i) + { + OUString aRegionName(aTemplates.GetFullRegionName(i)); + if (aRegionName == "Styles") + { + for (sal_uInt16 j = 0; j < aTemplates.GetCount(i); ++j) + { + OUString aName = aTemplates.GetName(i,j); + OUString aURL = aTemplates.GetPath(i,j); + BitmapEx aPreview = CreatePreview(aURL, aName); + sal_uInt16 nId = j + 1; + mxValueSet->InsertItem(nId, Image(aPreview), aName); + maTemplateEntries.push_back(std::make_unique<TemplateEntry>(aURL)); + mxValueSet->SetItemData(nId, maTemplateEntries.back().get()); + } + mxValueSet->SetOptimalSize(); + } + } +} + +StylePresetsPanel::~StylePresetsPanel() +{ + disposeOnce(); +} + +void StylePresetsPanel::dispose() +{ + mxValueSetWin.reset(); + mxValueSet.reset(); + + PanelLayout::dispose(); +} + +IMPL_LINK_NOARG(StylePresetsPanel, DoubleClickHdl, ValueSet*, void) +{ + sal_Int32 nItemId = mxValueSet->GetSelectedItemId(); + TemplateEntry* pEntry = static_cast<TemplateEntry*>(mxValueSet->GetItemData(nItemId)); + + SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current()); + if (pDocSh) + { + SwgReaderOption aOption; + aOption.SetTextFormats(true); + aOption.SetNumRules(true); + pDocSh->LoadStylesFromFile(pEntry->maURL, aOption, false); + } +} + +void StylePresetsPanel::NotifyItemUpdate(const sal_uInt16 /*nSId*/, + const SfxItemState /*eState*/, + const SfxPoolItem* /*pState*/) +{ +} + +} // end of namespace ::sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/StylePresetsPanel.hxx b/sw/source/uibase/sidebar/StylePresetsPanel.hxx new file mode 100644 index 000000000..c217710be --- /dev/null +++ b/sw/source/uibase/sidebar/StylePresetsPanel.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/. + * + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_STYLEPRESETSPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_STYLEPRESETSPANEL_HXX + +#include <memory> +#include <com/sun/star/frame/XFrame.hpp> + +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <sfx2/sidebar/ControllerItem.hxx> + +#include <svtools/valueset.hxx> + +namespace sw::sidebar { + +class StylePresetsPanel : public PanelLayout, + public sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ + friend class VclPtr<StylePresetsPanel>; +public: + static VclPtr<vcl::Window> Create(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame); + + virtual void NotifyItemUpdate(const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + +private: + struct TemplateEntry + { + explicit TemplateEntry(const OUString& rURL) + : maURL(rURL) + {} + + OUString maURL; + }; + + void RefreshList(); + + StylePresetsPanel(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame); + + virtual ~StylePresetsPanel() override; + virtual void dispose() override; + + std::unique_ptr<ValueSet> mxValueSet; + std::unique_ptr<weld::CustomWeld> mxValueSetWin; + + std::vector<std::unique_ptr<TemplateEntry>> maTemplateEntries; + + DECL_LINK(DoubleClickHdl, ValueSet*, void); +}; + +} // end of namespace sw::sidebar + +#endif // INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_STYLEPRESETSPANEL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/SwPanelFactory.cxx b/sw/source/uibase/sidebar/SwPanelFactory.cxx new file mode 100644 index 000000000..37b6694e4 --- /dev/null +++ b/sw/source/uibase/sidebar/SwPanelFactory.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/ui/XUIElementFactory.hpp> + +#include "ThemePanel.hxx" +#include "StylePresetsPanel.hxx" +#include "PageStylesPanel.hxx" +#include "PageFormatPanel.hxx" +#include "PageHeaderPanel.hxx" +#include "PageFooterPanel.hxx" +#include "WrapPropertyPanel.hxx" +#include "TableEditPanel.hxx" +#include <navipi.hxx> +#include <redlndlg.hxx> + +#include <sfx2/sidebar/SidebarPanelBase.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/supportsservice.hxx> + + +using namespace css; +using namespace css::uno; + +namespace { + +typedef ::cppu::WeakComponentImplHelper < + css::ui::XUIElementFactory, css::lang::XServiceInfo + > PanelFactoryInterfaceBase; + +class SwPanelFactory + : private ::cppu::BaseMutex + , public PanelFactoryInterfaceBase +{ +private: + SwPanelFactory(SwPanelFactory const&) = delete; + SwPanelFactory& operator=(SwPanelFactory const&) = delete; + +public: + SwPanelFactory(); + + // XUIElementFactory + css::uno::Reference<css::ui::XUIElement> SAL_CALL createUIElement( + const OUString& rsResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) override; + + OUString SAL_CALL getImplementationName() override + { return "org.apache.openoffice.comp.sw.sidebar.SwPanelFactory"; } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { return css::uno::Sequence<OUString>{"com.sun.star.ui.UIElementFactory"}; } +}; + +SwPanelFactory::SwPanelFactory() + : PanelFactoryInterfaceBase(m_aMutex) +{ +} + +Reference<ui::XUIElement> SAL_CALL SwPanelFactory::createUIElement ( + const OUString& rsResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + Reference<ui::XUIElement> xElement; + + const ::comphelper::NamedValueCollection aArguments (rArguments); + Reference<frame::XFrame> xFrame (aArguments.getOrDefault("Frame", Reference<frame::XFrame>())); + Reference<awt::XWindow> xParentWindow (aArguments.getOrDefault("ParentWindow", Reference<awt::XWindow>())); + const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0))); + SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue); + + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(xParentWindow); + if ( ! xParentWindow.is() || pParentWindow==nullptr) + throw RuntimeException( + "PanelFactory::createUIElement called without ParentWindow", + nullptr); + if ( ! xFrame.is()) + throw RuntimeException( + "PanelFactory::createUIElement called without Frame", + nullptr); + if (pBindings == nullptr) + throw RuntimeException( + "PanelFactory::createUIElement called without SfxBindings", + nullptr); + + if(rsResourceURL.endsWith("/PageStylesPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::PageStylesPanel::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if(rsResourceURL.endsWith("/PageFormatPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::PageFormatPanel::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if(rsResourceURL.endsWith("/PageHeaderPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::PageHeaderPanel::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if(rsResourceURL.endsWith("/PageFooterPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::PageFooterPanel::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if (rsResourceURL.endsWith("/WrapPropertyPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::WrapPropertyPanel::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if (rsResourceURL.endsWith("/NavigatorPanel")) + { + VclPtr<vcl::Window> pPanel = SwNavigationPI::Create( pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(0,-1,-1)); + } + else if (rsResourceURL.endsWith("/ManageChangesPanel")) + { + VclPtrInstance<SwRedlineAcceptPanel> pPanel(pParentWindow, xFrame); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, + xFrame, + pPanel, + ui::LayoutSize(-1,-1,-1)); + } + else if (rsResourceURL.endsWith("/StylePresetsPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::StylePresetsPanel::Create(pParentWindow, xFrame); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, xFrame, pPanel, ui::LayoutSize(-1,-1,-1)); + } + else if (rsResourceURL.endsWith("/ThemePanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::ThemePanel::Create(pParentWindow, xFrame); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, xFrame, pPanel, ui::LayoutSize(-1,-1,-1)); + } + else if (rsResourceURL.endsWith("/TableEditPanel")) + { + VclPtr<vcl::Window> pPanel = sw::sidebar::TableEditPanel::Create(pParentWindow, xFrame, pBindings ); + xElement = sfx2::sidebar::SidebarPanelBase::Create( + rsResourceURL, xFrame, pPanel, ui::LayoutSize(-1,-1,-1)); + } + + return xElement; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +org_apache_openoffice_comp_sw_sidebar_SwPanelFactory_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwPanelFactory()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/TableEditPanel.cxx b/sw/source/uibase/sidebar/TableEditPanel.cxx new file mode 100644 index 000000000..46f75cd9a --- /dev/null +++ b/sw/source/uibase/sidebar/TableEditPanel.cxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 "TableEditPanel.hxx" +#include <sal/config.h> +#include <swtypes.hxx> +#include <cmdid.h> +#include <svl/intitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svtools/unitconv.hxx> +#include <svx/dlgctrl.hxx> +#include <swmodule.hxx> +#include <usrpref.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +namespace sw::sidebar +{ +VclPtr<vcl::Window> TableEditPanel::Create(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) +{ + if (pParent == nullptr) + throw css::lang::IllegalArgumentException( + "no parent Window given to TableEditPanel::Create", nullptr, 0); + if (!rxFrame.is()) + throw css::lang::IllegalArgumentException("no XFrame given to TableEditPanel::Create", + nullptr, 1); + + return VclPtr<TableEditPanel>::Create(pParent, rxFrame, pBindings); +} + +void TableEditPanel::NotifyItemUpdate(const sal_uInt16 nSID, const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch (nSID) + { + case SID_ATTR_TABLE_ROW_HEIGHT: + { + bool bDisabled = eState == SfxItemState::DISABLED; + m_xRowHeightEdit->set_sensitive(!bDisabled); + + if (pState && eState >= SfxItemState::DEFAULT) + { + const SfxUInt32Item* pItem = static_cast<const SfxUInt32Item*>(pState); + if (pItem) + { + long nNewHeight = pItem->GetValue(); + nNewHeight = m_xRowHeightEdit->normalize(nNewHeight); + m_xRowHeightEdit->set_value(nNewHeight, FieldUnit::TWIP); + } + } + else if (eState != SfxItemState::DISABLED) + m_xRowHeightEdit->set_text(""); + + break; + } + case SID_ATTR_TABLE_COLUMN_WIDTH: + { + bool bDisabled = eState == SfxItemState::DISABLED; + m_xColumnWidthEdit->set_sensitive(!bDisabled); + + if (pState && eState >= SfxItemState::DEFAULT) + { + const SfxUInt32Item* pItem = static_cast<const SfxUInt32Item*>(pState); + if (pItem) + { + long nNewWidth = pItem->GetValue(); + nNewWidth = m_xColumnWidthEdit->normalize(nNewWidth); + m_xColumnWidthEdit->set_value(nNewWidth, FieldUnit::TWIP); + } + } + else if (eState != SfxItemState::DISABLED) + m_xColumnWidthEdit->set_text(""); + + break; + } + } +} + +TableEditPanel::TableEditPanel(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) + : PanelLayout(pParent, "TableEditPanel", "modules/swriter/ui/sidebartableedit.ui", rxFrame) + , m_pBindings(pBindings) + , m_xRowHeightEdit( + new SvxRelativeField(m_xBuilder->weld_metric_spin_button("rowheight", FieldUnit::CM))) + , m_xColumnWidthEdit( + new SvxRelativeField(m_xBuilder->weld_metric_spin_button("columnwidth", FieldUnit::CM))) + , m_xInsert(m_xBuilder->weld_toolbar("insert")) + , m_xInsertDispatch(new ToolbarUnoDispatcher(*m_xInsert, *m_xBuilder, rxFrame)) + , m_xSelect(m_xBuilder->weld_toolbar("select")) + , m_xSelectDispatch(new ToolbarUnoDispatcher(*m_xSelect, *m_xBuilder, rxFrame)) + , m_xRowSizing(m_xBuilder->weld_toolbar("rowsizing")) + , m_xRowSizingDispatch(new ToolbarUnoDispatcher(*m_xRowSizing, *m_xBuilder, rxFrame)) + , m_xColumnSizing(m_xBuilder->weld_toolbar("columnsizing")) + , m_xColumnSizingDispatch(new ToolbarUnoDispatcher(*m_xColumnSizing, *m_xBuilder, rxFrame)) + , m_xDelete(m_xBuilder->weld_toolbar("delete")) + , m_xDeleteDispatch(new ToolbarUnoDispatcher(*m_xDelete, *m_xBuilder, rxFrame)) + , m_xSplitMerge(m_xBuilder->weld_toolbar("split_merge")) + , m_xSplitMergeDispatch(new ToolbarUnoDispatcher(*m_xSplitMerge, *m_xBuilder, rxFrame)) + , m_xMisc(m_xBuilder->weld_toolbar("misc")) + , m_xMiscDispatch(new ToolbarUnoDispatcher(*m_xMisc, *m_xBuilder, rxFrame)) + , m_aRowHeightController(SID_ATTR_TABLE_ROW_HEIGHT, *pBindings, *this) + , m_aColumnWidthController(SID_ATTR_TABLE_COLUMN_WIDTH, *pBindings, *this) + , m_aInsertRowsBeforeController(FN_TABLE_INSERT_ROW_BEFORE, *pBindings, *this) + , m_aInsertRowsAfterController(FN_TABLE_INSERT_ROW_AFTER, *pBindings, *this) + , m_aInsertColumnsBeforeController(FN_TABLE_INSERT_COL_BEFORE, *pBindings, *this) + , m_aInsertColumnsAfterController(FN_TABLE_INSERT_COL_AFTER, *pBindings, *this) + , m_aDeleteRowsController(FN_TABLE_DELETE_ROW, *pBindings, *this) + , m_aDeleteColumnsController(FN_TABLE_DELETE_COL, *pBindings, *this) + , m_aDeleteTableController(FN_TABLE_DELETE_TABLE, *pBindings, *this) + , m_aSetMinimalRowHeightController(SID_TABLE_MINIMAL_ROW_HEIGHT, *pBindings, *this) + , m_aSetOptimalRowHeightController(FN_TABLE_OPTIMAL_HEIGHT, *pBindings, *this) + , m_aDistributeRowsController(FN_TABLE_BALANCE_ROWS, *pBindings, *this) + , m_aSetMinimalColumnWidthController(SID_TABLE_MINIMAL_COLUMN_WIDTH, *pBindings, *this) + , m_aSetOptimalColumnWidthController(FN_TABLE_ADJUST_CELLS, *pBindings, *this) + , m_aDistributeColumnsController(FN_TABLE_BALANCE_CELLS, *pBindings, *this) + , m_aMergeCellsController(FN_TABLE_MERGE_CELLS, *pBindings, *this) +{ + // tdf#130197 Give this toolbar a width as if it had 5 entries (the parent + // grid has homogeneous width set so both columns will have the same + // width). This will align this TableEditPanel's columns with + // ParaPropertyPanel's columns + padWidthForSidebar(*m_xSplitMerge, rxFrame); + + InitRowHeightToolitem(); + InitColumnWidthToolitem(); +} + +TableEditPanel::~TableEditPanel() { disposeOnce(); } + +void TableEditPanel::InitRowHeightToolitem() +{ + Link<weld::MetricSpinButton&, void> aLink = LINK(this, TableEditPanel, RowHeightMofiyHdl); + m_xRowHeightEdit->connect_value_changed(aLink); + + FieldUnit eFieldUnit = SW_MOD()->GetUsrPref(false)->GetMetric(); + m_xRowHeightEdit->SetFieldUnit(eFieldUnit); + + m_xRowHeightEdit->set_min(MINLAY, FieldUnit::TWIP); + m_xRowHeightEdit->set_max(SAL_MAX_INT32, FieldUnit::TWIP); + + limitWidthForSidebar(*m_xRowHeightEdit); +} + +void TableEditPanel::InitColumnWidthToolitem() +{ + Link<weld::MetricSpinButton&, void> aLink = LINK(this, TableEditPanel, ColumnWidthMofiyHdl); + m_xColumnWidthEdit->connect_value_changed(aLink); + + FieldUnit eFieldUnit = SW_MOD()->GetUsrPref(false)->GetMetric(); + m_xColumnWidthEdit->SetFieldUnit(eFieldUnit); + + m_xColumnWidthEdit->set_min(MINLAY, FieldUnit::TWIP); + m_xColumnWidthEdit->set_max(SAL_MAX_INT32, FieldUnit::TWIP); + + limitWidthForSidebar(*m_xColumnWidthEdit); +} + +void TableEditPanel::dispose() +{ + m_xRowHeightEdit.reset(); + m_xColumnWidthEdit.reset(); + + m_xMiscDispatch.reset(); + m_xMisc.reset(); + + m_xSplitMergeDispatch.reset(); + m_xSplitMerge.reset(); + + m_xDeleteDispatch.reset(); + m_xDelete.reset(); + + m_xColumnSizingDispatch.reset(); + m_xColumnSizing.reset(); + + m_xRowSizingDispatch.reset(); + m_xRowSizing.reset(); + + m_xSelectDispatch.reset(); + m_xSelect.reset(); + + m_xInsertDispatch.reset(); + m_xInsert.reset(); + + m_aRowHeightController.dispose(); + m_aColumnWidthController.dispose(); + m_aInsertRowsBeforeController.dispose(); + m_aInsertRowsAfterController.dispose(); + m_aInsertColumnsBeforeController.dispose(); + m_aInsertColumnsAfterController.dispose(); + m_aDeleteRowsController.dispose(); + m_aDeleteColumnsController.dispose(); + m_aDeleteTableController.dispose(); + m_aSetMinimalRowHeightController.dispose(); + m_aSetOptimalRowHeightController.dispose(); + m_aDistributeRowsController.dispose(); + m_aSetMinimalColumnWidthController.dispose(); + m_aSetOptimalColumnWidthController.dispose(); + m_aDistributeColumnsController.dispose(); + m_aMergeCellsController.dispose(); + + PanelLayout::dispose(); +} + +IMPL_LINK_NOARG(TableEditPanel, RowHeightMofiyHdl, weld::MetricSpinButton&, void) +{ + SwTwips nNewHeight = static_cast<SwTwips>( + m_xRowHeightEdit->denormalize(m_xRowHeightEdit->get_value(FieldUnit::TWIP))); + SfxUInt32Item aRowHeight(SID_ATTR_TABLE_ROW_HEIGHT); + aRowHeight.SetValue(nNewHeight); + + m_pBindings->GetDispatcher()->ExecuteList(SID_ATTR_TABLE_ROW_HEIGHT, SfxCallMode::RECORD, + { &aRowHeight }); +} + +IMPL_LINK_NOARG(TableEditPanel, ColumnWidthMofiyHdl, weld::MetricSpinButton&, void) +{ + SwTwips nNewWidth = static_cast<SwTwips>( + m_xColumnWidthEdit->denormalize(m_xColumnWidthEdit->get_value(FieldUnit::TWIP))); + SfxUInt32Item aColumnWidth(SID_ATTR_TABLE_COLUMN_WIDTH); + aColumnWidth.SetValue(nNewWidth); + + m_pBindings->GetDispatcher()->ExecuteList(SID_ATTR_TABLE_COLUMN_WIDTH, SfxCallMode::RECORD, + { &aColumnWidth }); +} +} // end of namespace ::sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/TableEditPanel.hxx b/sw/source/uibase/sidebar/TableEditPanel.hxx new file mode 100644 index 000000000..361e86009 --- /dev/null +++ b/sw/source/uibase/sidebar/TableEditPanel.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/. + * + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_TABLEEDITPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_TABLEEDITPANEL_HXX + +#include <com/sun/star/frame/XFrame.hpp> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <sfx2/weldutils.hxx> +#include <svx/relfld.hxx> + +namespace sw +{ +namespace sidebar +{ +class TableEditPanel : public PanelLayout, + public sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ + friend class VclPtr<TableEditPanel>; + +public: + static VclPtr<vcl::Window> Create(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings); + + virtual void NotifyItemUpdate(const sal_uInt16 nSId, const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState(const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override{}; + +private: + TableEditPanel(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings); + virtual ~TableEditPanel() override; + virtual void dispose() override; + + void InitRowHeightToolitem(); + void InitColumnWidthToolitem(); + + SfxBindings* m_pBindings; + + std::unique_ptr<SvxRelativeField> m_xRowHeightEdit; + std::unique_ptr<SvxRelativeField> m_xColumnWidthEdit; + std::unique_ptr<weld::Toolbar> m_xInsert; + std::unique_ptr<ToolbarUnoDispatcher> m_xInsertDispatch; + std::unique_ptr<weld::Toolbar> m_xSelect; + std::unique_ptr<ToolbarUnoDispatcher> m_xSelectDispatch; + std::unique_ptr<weld::Toolbar> m_xRowSizing; + std::unique_ptr<ToolbarUnoDispatcher> m_xRowSizingDispatch; + std::unique_ptr<weld::Toolbar> m_xColumnSizing; + std::unique_ptr<ToolbarUnoDispatcher> m_xColumnSizingDispatch; + std::unique_ptr<weld::Toolbar> m_xDelete; + std::unique_ptr<ToolbarUnoDispatcher> m_xDeleteDispatch; + std::unique_ptr<weld::Toolbar> m_xSplitMerge; + std::unique_ptr<ToolbarUnoDispatcher> m_xSplitMergeDispatch; + std::unique_ptr<weld::Toolbar> m_xMisc; + std::unique_ptr<ToolbarUnoDispatcher> m_xMiscDispatch; + + ::sfx2::sidebar::ControllerItem m_aRowHeightController; + ::sfx2::sidebar::ControllerItem m_aColumnWidthController; + ::sfx2::sidebar::ControllerItem m_aInsertRowsBeforeController; + ::sfx2::sidebar::ControllerItem m_aInsertRowsAfterController; + ::sfx2::sidebar::ControllerItem m_aInsertColumnsBeforeController; + ::sfx2::sidebar::ControllerItem m_aInsertColumnsAfterController; + ::sfx2::sidebar::ControllerItem m_aDeleteRowsController; + ::sfx2::sidebar::ControllerItem m_aDeleteColumnsController; + ::sfx2::sidebar::ControllerItem m_aDeleteTableController; + ::sfx2::sidebar::ControllerItem m_aSetMinimalRowHeightController; + ::sfx2::sidebar::ControllerItem m_aSetOptimalRowHeightController; + ::sfx2::sidebar::ControllerItem m_aDistributeRowsController; + ::sfx2::sidebar::ControllerItem m_aSetMinimalColumnWidthController; + ::sfx2::sidebar::ControllerItem m_aSetOptimalColumnWidthController; + ::sfx2::sidebar::ControllerItem m_aDistributeColumnsController; + ::sfx2::sidebar::ControllerItem m_aMergeCellsController; + + DECL_LINK(RowHeightMofiyHdl, weld::MetricSpinButton&, void); + DECL_LINK(ColumnWidthMofiyHdl, weld::MetricSpinButton&, void); +}; +} +} // end of namespace sw::sidebar + +#endif // INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_TABLEEDITPANEL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/ThemePanel.cxx b/sw/source/uibase/sidebar/ThemePanel.cxx new file mode 100644 index 000000000..290ca4106 --- /dev/null +++ b/sw/source/uibase/sidebar/ThemePanel.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/. + * + */ + +#include <sal/config.h> + +#include "ThemePanel.hxx" + +#include <sfx2/objsh.hxx> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include <editeng/fontitem.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/image.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <charatr.hxx> +#include <charfmt.hxx> +#include <docsh.hxx> +#include <docstyle.hxx> +#include <fmtcol.hxx> +#include <format.hxx> + +namespace +{ + +class FontSet +{ +public: + OUString maName; + OUString msMonoFont; + OUString msHeadingFont; + OUString msBaseFont; +}; + +class ColorVariable +{ +public: + long mnIndex; + sal_Int16 mnTintShade; + + ColorVariable() + : mnIndex(-1) + , mnTintShade() + {} + + ColorVariable(long nIndex, sal_Int16 nTintShade) + : mnIndex(nIndex) + , mnTintShade(nTintShade) + {} +}; + +class StyleRedefinition +{ + ColorVariable maVariable; + +public: + OUString maElementName; + +public: + explicit StyleRedefinition(const OUString& aElementName) + : maElementName(aElementName) + {} + + void setColorVariable(ColorVariable aVariable) + { + maVariable = aVariable; + } + + Color getColor(svx::ColorSet const & rColorSet) + { + Color aColor; + if (maVariable.mnIndex > -1) + { + aColor = rColorSet.getColor(maVariable.mnIndex); + aColor.ApplyTintOrShade(maVariable.mnTintShade); + } + else + { + aColor = COL_BLACK; + } + return aColor; + } +}; + +class StyleSet +{ + std::vector<StyleRedefinition> maStyles; + +public: + explicit StyleSet() + : maStyles() + {} + + void add(StyleRedefinition const & aRedefinition) + { + maStyles.push_back(aRedefinition); + } + + StyleRedefinition* get(const OUString& aString) + { + for (StyleRedefinition & rStyle : maStyles) + { + if (rStyle.maElementName == aString) + { + return &rStyle; + } + } + return nullptr; + } +}; + +StyleSet setupThemes() +{ + StyleSet aSet; + + { + StyleRedefinition aRedefinition("Heading 1"); + aRedefinition.setColorVariable(ColorVariable(10, -1000)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 2"); + aRedefinition.setColorVariable(ColorVariable(7, -500)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 3"); + aRedefinition.setColorVariable(ColorVariable(5, 0)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 4"); + aRedefinition.setColorVariable(ColorVariable(6, -1000)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 5"); + aRedefinition.setColorVariable(ColorVariable(4, -1500)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 6"); + aRedefinition.setColorVariable(ColorVariable(3, -2500)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 7"); + aRedefinition.setColorVariable(ColorVariable(3, -2500)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 8"); + aRedefinition.setColorVariable(ColorVariable(2, 0)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 9"); + aRedefinition.setColorVariable(ColorVariable(2, 0)); + aSet.add(aRedefinition); + } + + { + StyleRedefinition aRedefinition("Heading 10"); + aRedefinition.setColorVariable(ColorVariable(0, 0)); + aSet.add(aRedefinition); + } + + return aSet; +} + +void changeFont(SwFormat* pFormat, SwDocStyleSheet const * pStyle, FontSet const & rFontSet) +{ + if (pStyle->GetName() != "Default Style" && pFormat->GetAttrSet().GetItem(RES_CHRATR_FONT, false) == nullptr) + { + return; + } + + SvxFontItem aFontItem(pFormat->GetFont(false)); + + FontPitch ePitch = aFontItem.GetPitch(); + + if (ePitch == PITCH_FIXED) + { + aFontItem.SetFamilyName(rFontSet.msMonoFont); + } + else + { + if (pStyle->GetName() == "Heading") + { + aFontItem.SetFamilyName(rFontSet.msHeadingFont); + } + else + { + aFontItem.SetFamilyName(rFontSet.msBaseFont); + } + } + + pFormat->SetFormatAttr(aFontItem); +} + +/*void changeBorder(SwTextFormatColl* pCollection, SwDocStyleSheet* pStyle, StyleSet& rStyleSet) +{ + if (pStyle->GetName() == "Heading") + { + SvxBoxItem aBoxItem(pCollection->GetBox()); + editeng::SvxBorderLine aBorderLine; + aBorderLine.SetWidth(40); //20 = 1pt + aBorderLine.SetColor(rColorSet.mBaseColors[0]); + aBoxItem.SetLine(&aBorderLine, SvxBoxItemLine::BOTTOM); + + pCollection->SetFormatAttr(aBoxItem); + } +}*/ + +void changeColor(SwTextFormatColl* pCollection, svx::ColorSet const & rColorSet, StyleRedefinition* pRedefinition) +{ + Color aColor = pRedefinition->getColor(rColorSet); + + SvxColorItem aColorItem(pCollection->GetColor()); + aColorItem.SetValue(aColor); + pCollection->SetFormatAttr(aColorItem); +} + +std::vector<FontSet> initFontSets() +{ + std::vector<FontSet> aFontSets; + { + FontSet aFontSet; + aFontSet.maName = "Liberation Family"; + aFontSet.msHeadingFont = "Liberation Sans"; + aFontSet.msBaseFont = "Liberation Serif"; + aFontSet.msMonoFont = "Liberation Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "DejaVu Family"; + aFontSet.msHeadingFont = "DejaVu Sans"; + aFontSet.msBaseFont = "DejaVu Serif"; + aFontSet.msMonoFont = "DejaVu Sans Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Croscore Modern"; + aFontSet.msHeadingFont = "Caladea"; + aFontSet.msBaseFont = "Carlito"; + aFontSet.msMonoFont = "Liberation Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Carlito"; + aFontSet.msHeadingFont = "Carlito"; + aFontSet.msBaseFont = "Carlito"; + aFontSet.msMonoFont = "Liberation Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Source Sans Family"; + aFontSet.msHeadingFont = "Source Sans Pro"; + aFontSet.msBaseFont = "Source Sans Pro"; + aFontSet.msMonoFont = "Source Code Pro"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Source Sans Family 2"; + aFontSet.msHeadingFont = "Source Sans Pro"; + aFontSet.msBaseFont = "Source Sans Pro Light"; + aFontSet.msMonoFont = "Source Code Pro"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Libertine Family"; + aFontSet.msHeadingFont = "Linux Biolinum G"; + aFontSet.msBaseFont = "Linux Libertine G"; + aFontSet.msMonoFont = "Liberation Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Open Sans"; + aFontSet.msHeadingFont = "Open Sans"; + aFontSet.msBaseFont = "Open Sans"; + aFontSet.msMonoFont = "Droid Sans Mono"; + aFontSets.push_back(aFontSet); + } + { + FontSet aFontSet; + aFontSet.maName = "Droid Sans"; + aFontSet.msHeadingFont = "Droid Sans"; + aFontSet.msBaseFont = "Droid Sans"; + aFontSet.msMonoFont = "Droid Sans Mono"; + aFontSets.push_back(aFontSet); + } + return aFontSets; +} + +FontSet getFontSet(const OUString& rFontVariant, std::vector<FontSet>& aFontSets) +{ + for (const FontSet & rFontSet : aFontSets) + { + if (rFontSet.maName == rFontVariant) + return rFontSet; + } + return aFontSets[0]; +} + +void applyTheme(SfxStyleSheetBasePool* pPool, const OUString& sFontSetName, const OUString& sColorSetName, + StyleSet& rStyleSet, svx::ColorSets& rColorSets) +{ + SwDocStyleSheet* pStyle; + + std::vector<FontSet> aFontSets = initFontSets(); + FontSet aFontSet = getFontSet(sFontSetName, aFontSets); + + svx::ColorSet aColorSet = rColorSets.getColorSet(sColorSetName); + + pStyle = static_cast<SwDocStyleSheet*>(pPool->First(SfxStyleFamily::Para)); + while (pStyle) + { + SwTextFormatColl* pCollection = pStyle->GetCollection(); + + changeFont(pCollection, pStyle, aFontSet); + + StyleRedefinition* pRedefinition = rStyleSet.get(pStyle->GetName()); + + if (pRedefinition) + { + changeColor(pCollection, aColorSet, pRedefinition); + } + + pStyle = static_cast<SwDocStyleSheet*>(pPool->Next()); + } + + pStyle = static_cast<SwDocStyleSheet*>(pPool->First(SfxStyleFamily::Char)); + while (pStyle) + { + SwCharFormat* pCharFormat = pStyle->GetCharFormat(); + + changeFont(static_cast<SwFormat*>(pCharFormat), pStyle, aFontSet); + + pStyle = static_cast<SwDocStyleSheet*>(pPool->Next()); + } +} + +BitmapEx GenerateColorPreview(const svx::ColorSet& rColorSet) +{ + ScopedVclPtrInstance<VirtualDevice> pVirtualDev(*Application::GetDefaultDevice()); + float fScaleFactor = pVirtualDev->GetDPIScaleFactor(); + long BORDER = 2 * fScaleFactor; + long SIZE = 12 * fScaleFactor; + + Size aSize(BORDER * 7 + SIZE * 6, BORDER * 3 + SIZE * 2); + pVirtualDev->SetOutputSizePixel(aSize); + + long x = BORDER; + long y1 = BORDER; + long y2 = y1 + SIZE + BORDER; + + pVirtualDev->SetLineColor(COL_LIGHTGRAY); + + for (sal_uInt32 i = 0; i < 12; i += 2) + { + pVirtualDev->SetFillColor(rColorSet.getColor(i)); + pVirtualDev->DrawRect(tools::Rectangle(x, y1, x + SIZE, y1 + SIZE)); + + pVirtualDev->SetFillColor(rColorSet.getColor(i + 1)); + pVirtualDev->DrawRect(tools::Rectangle(x, y2, x + SIZE, y2 + SIZE)); + + x += SIZE + BORDER; + } + + return pVirtualDev->GetBitmapEx(Point(), aSize); +} + +} // end anonymous namespace + +namespace sw::sidebar { + +VclPtr<vcl::Window> ThemePanel::Create (vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame) +{ + if (pParent == nullptr) + throw css::lang::IllegalArgumentException("no parent Window given to PagePropertyPanel::Create", nullptr, 0); + if (!rxFrame.is()) + throw css::lang::IllegalArgumentException("no XFrame given to PagePropertyPanel::Create", nullptr, 1); + + return VclPtr<ThemePanel>::Create(pParent, rxFrame); +} + +ThemePanel::ThemePanel(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame) + : PanelLayout(pParent, "ThemePanel", "modules/swriter/ui/sidebartheme.ui", rxFrame) + , mxListBoxFonts(m_xBuilder->weld_tree_view("listbox_fonts")) + , mxValueSetColors(new ValueSet(nullptr)) + , mxValueSetColorsWin(new weld::CustomWeld(*m_xBuilder, "valueset_colors", *mxValueSetColors)) + , mxApplyButton(m_xBuilder->weld_button("apply")) + , maColorSets() +{ + mxValueSetColors->SetColCount(2); + mxValueSetColors->SetLineCount(3); + + mxApplyButton->connect_clicked(LINK(this, ThemePanel, ClickHdl)); + mxListBoxFonts->connect_row_activated(LINK(this, ThemePanel, DoubleClickHdl)); + mxValueSetColors->SetDoubleClickHdl(LINK(this, ThemePanel, DoubleClickValueSetHdl)); + + std::vector<FontSet> aFontSets = initFontSets(); + for (const FontSet & rFontSet : aFontSets) + mxListBoxFonts->append_text(rFontSet.maName); + mxListBoxFonts->set_size_request(-1, mxListBoxFonts->get_height_rows(aFontSets.size())); + + maColorSets.init(); + + const std::vector<svx::ColorSet>& aColorSets = maColorSets.getColorSets(); + for (size_t i = 0; i < aColorSets.size(); ++i) + { + const svx::ColorSet& rColorSet = aColorSets[i]; + + const OUString& aName = rColorSet.getName(); + BitmapEx aPreview = GenerateColorPreview(rColorSet); + + sal_uInt16 nId = i + 1; + mxValueSetColors->InsertItem(nId, Image(aPreview), aName); + } + + mxValueSetColors->SetOptimalSize(); + + if (!aColorSets.empty()) + mxValueSetColors->SelectItem(1); // ItemId 1, position 0 +} + +ThemePanel::~ThemePanel() +{ + disposeOnce(); +} + +void ThemePanel::dispose() +{ + mxListBoxFonts.reset(); + mxValueSetColorsWin.reset(); + mxValueSetColors.reset(); + mxApplyButton.reset(); + + PanelLayout::dispose(); +} + +IMPL_LINK_NOARG(ThemePanel, ClickHdl, weld::Button&, void) +{ + DoubleClickHdl(); +} + +IMPL_LINK_NOARG(ThemePanel, DoubleClickValueSetHdl, ValueSet*, void) +{ + DoubleClickHdl(); +} + +IMPL_LINK_NOARG(ThemePanel, DoubleClickHdl, weld::TreeView&, bool) +{ + DoubleClickHdl(); + return true; +} + +void ThemePanel::DoubleClickHdl() +{ + SwDocShell* pDocSh = static_cast<SwDocShell*>(SfxObjectShell::Current()); + if (!pDocSh) + return; + + sal_uInt32 nItemId = mxValueSetColors->GetSelectedItemId(); + if (!nItemId) + return; + OUString sEntryFonts = mxListBoxFonts->get_selected_text(); + sal_uInt32 nIndex = nItemId - 1; + OUString sEntryColors = maColorSets.getColorSet(nIndex).getName(); + + StyleSet aStyleSet = setupThemes(); + + applyTheme(pDocSh->GetStyleSheetPool(), sEntryFonts, sEntryColors, aStyleSet, maColorSets); +} + +void ThemePanel::NotifyItemUpdate(const sal_uInt16 /*nSId*/, + const SfxItemState /*eState*/, + const SfxPoolItem* /*pState*/) +{ +} + +} // end of namespace ::sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/ThemePanel.hxx b/sw/source/uibase/sidebar/ThemePanel.hxx new file mode 100644 index 000000000..a050f2d4c --- /dev/null +++ b/sw/source/uibase/sidebar/ThemePanel.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/. + * + */ + +#ifndef INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_THEMEPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_THEMEPANEL_HXX + +#include <com/sun/star/frame/XFrame.hpp> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <svtools/valueset.hxx> +#include <svx/ColorSets.hxx> + +namespace sw::sidebar { + +class ThemePanel : public PanelLayout, + public sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ + friend class VclPtr<ThemePanel>; +public: + static VclPtr<vcl::Window> Create(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame); + + virtual void NotifyItemUpdate(const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + +private: + ThemePanel(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame); + virtual ~ThemePanel() override; + + virtual void dispose() override; + + std::unique_ptr<weld::TreeView> mxListBoxFonts; + std::unique_ptr<ValueSet> mxValueSetColors; + std::unique_ptr<weld::CustomWeld> mxValueSetColorsWin; + std::unique_ptr<weld::Button> mxApplyButton; + + svx::ColorSets maColorSets; + + DECL_LINK(ClickHdl, weld::Button&, void); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(DoubleClickValueSetHdl, ValueSet*, void); + void DoubleClickHdl(); + +}; + +} // end of namespace sw::sidebar + +#endif // INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_THEMEPANEL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/WrapPropertyPanel.cxx b/sw/source/uibase/sidebar/WrapPropertyPanel.cxx new file mode 100644 index 000000000..bc64fd8b7 --- /dev/null +++ b/sw/source/uibase/sidebar/WrapPropertyPanel.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 "WrapPropertyPanel.hxx" + +#include <cmdid.h> +#include <svx/spacinglistbox.hxx> +#include <svx/svdtrans.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/eitem.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/settings.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <hintids.hxx> +#include <strings.hrc> +#include <uitool.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <comphelper/lok.hxx> + +namespace sw::sidebar { + +VclPtr<vcl::Window> WrapPropertyPanel::Create ( + vcl::Window* pParent, + const css::uno::Reference< css::frame::XFrame >& rxFrame, + SfxBindings* pBindings) +{ + if (pParent == nullptr) + throw css::lang::IllegalArgumentException("no parent Window given to WrapPropertyPanel::Create", nullptr, 0); + if ( ! rxFrame.is()) + throw css::lang::IllegalArgumentException("no XFrame given to WrapPropertyPanel::Create", nullptr, 1); + if (pBindings == nullptr) + throw css::lang::IllegalArgumentException("no SfxBindings given to WrapPropertyPanel::Create", nullptr, 2); + + return VclPtr<WrapPropertyPanel>::Create( + pParent, + rxFrame, + pBindings); +} + +WrapPropertyPanel::WrapPropertyPanel( + vcl::Window* pParent, + const css::uno::Reference< css::frame::XFrame >& rxFrame, + SfxBindings* pBindings ) + : PanelLayout(pParent, "WrapPropertyPanel", "modules/swriter/ui/sidebarwrap.ui", rxFrame) + , mpBindings(pBindings) + // spacing + , nTop(0) + , nBottom(0) + , nLeft(0) + , nRight(0) + // resources + , aCustomEntry(SwResId(STR_WRAP_PANEL_CUSTOM_STR)) + // controller items + , maSwLRSpacingControl(SID_ATTR_LRSPACE, *pBindings, *this) + , maSwULSpacingControl(SID_ATTR_ULSPACE, *pBindings, *this) + , mxWrapOptions(m_xBuilder->weld_toolbar("wrapoptions")) + , mxWrapOptionsDispatch(new ToolbarUnoDispatcher(*mxWrapOptions, *m_xBuilder, rxFrame)) + , mxSpacingLB(m_xBuilder->weld_combo_box("spacingLB")) +{ + FieldUnit eMetric = ::GetDfltMetric(false); + SpacingListBox::Fill(IsInch(eMetric) ? SpacingType::SPACING_INCH : SpacingType::SPACING_CM, *mxSpacingLB); + + Initialize(); +} + +WrapPropertyPanel::~WrapPropertyPanel() +{ + disposeOnce(); +} + +void WrapPropertyPanel::dispose() +{ + mxSpacingLB.reset(); + + mxWrapOptionsDispatch.reset(); + mxWrapOptions.reset(); + + maSwLRSpacingControl.dispose(); + maSwULSpacingControl.dispose(); + + PanelLayout::dispose(); +} + +void WrapPropertyPanel::Initialize() +{ + mxSpacingLB->connect_changed(LINK(this, WrapPropertyPanel, SpacingLBHdl)); + + mpBindings->Update( SID_ATTR_LRSPACE ); + mpBindings->Update( SID_ATTR_ULSPACE ); +} + +void WrapPropertyPanel::UpdateSpacingLB() +{ + if( (nLeft == nRight) && (nTop == nBottom) && (nLeft == nTop) ) + { + sal_Int32 nCount = mxSpacingLB->get_count(); + for (sal_Int32 i = 0; i < nCount; i++) + { + if (mxSpacingLB->get_id(i).toUInt32() == nLeft) + { + mxSpacingLB->set_active(i); + int nCustomEntry = mxSpacingLB->find_text(aCustomEntry); + if (nCustomEntry != -1) + mxSpacingLB->remove(nCustomEntry); + return; + } + } + } + + if (mxSpacingLB->find_text(aCustomEntry) == -1) + mxSpacingLB->append_text(aCustomEntry); + mxSpacingLB->set_active_text(aCustomEntry); +} + +IMPL_LINK(WrapPropertyPanel, SpacingLBHdl, weld::ComboBox&, rBox, void) +{ + sal_uInt16 nVal = rBox.get_active_id().toUInt32(); + + SvxLRSpaceItem aLRItem(nVal, nVal, 0, 0, RES_LR_SPACE); + SvxULSpaceItem aULItem(nVal, nVal, RES_UL_SPACE); + + nTop = nBottom = nLeft = nRight = nVal; + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_LRSPACE, + SfxCallMode::RECORD, { &aLRItem }); + mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_ULSPACE, + SfxCallMode::RECORD, { &aULItem }); +} + +void WrapPropertyPanel::NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch(nSId) + { + case SID_ATTR_LRSPACE: + { + if(eState >= SfxItemState::DEFAULT) + { + const SvxLRSpaceItem* pItem = dynamic_cast< const SvxLRSpaceItem* >(pState); + if(pItem) + { + nLeft = pItem->GetLeft(); + nRight = pItem->GetRight(); + + UpdateSpacingLB(); + } + } + } + break; + case SID_ATTR_ULSPACE: + { + if(eState >= SfxItemState::DEFAULT) + { + const SvxULSpaceItem* pItem = dynamic_cast< const SvxULSpaceItem* >(pState); + if(pItem) + { + nTop = pItem->GetUpper(); + nBottom = pItem->GetLower(); + + UpdateSpacingLB(); + } + } + } + break; + } +} + +} // end of namespace ::sw::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/sidebar/WrapPropertyPanel.hxx b/sw/source/uibase/sidebar/WrapPropertyPanel.hxx new file mode 100644 index 000000000..1f4d16d80 --- /dev/null +++ b/sw/source/uibase/sidebar/WrapPropertyPanel.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_UIBASE_SIDEBAR_WRAPPROPERTYPANEL_HXX +#define INCLUDED_SW_SOURCE_UIBASE_SIDEBAR_WRAPPROPERTYPANEL_HXX + +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <sfx2/weldutils.hxx> +#include <com/sun/star/frame/XFrame.hpp> +#include <vcl/weld.hxx> + +namespace sw::sidebar { + + class WrapPropertyPanel + : public PanelLayout + , public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface + { + public: + static VclPtr<vcl::Window> Create( + vcl::Window* pParent, + const css::uno::Reference< css::frame::XFrame>& rxFrame, + SfxBindings* pBindings ); + + // interface of ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface + virtual void NotifyItemUpdate( + const sal_uInt16 nSId, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + virtual ~WrapPropertyPanel() override; + virtual void dispose() override; + + WrapPropertyPanel( + vcl::Window* pParent, + const css::uno::Reference< css::frame::XFrame >& rxFrame, + SfxBindings* pBindings ); + private: + SfxBindings* mpBindings; + + //Spacing + sal_uInt16 nTop; + sal_uInt16 nBottom; + sal_uInt16 nLeft; + sal_uInt16 nRight; + + //custom entry + OUString aCustomEntry; + + // Controller Items + ::sfx2::sidebar::ControllerItem maSwLRSpacingControl; + ::sfx2::sidebar::ControllerItem maSwULSpacingControl; + + std::unique_ptr<weld::Toolbar> mxWrapOptions; + std::unique_ptr<ToolbarUnoDispatcher> mxWrapOptionsDispatch; + + std::unique_ptr<weld::ComboBox> mxSpacingLB; + + void Initialize(); + void UpdateSpacingLB(); + + DECL_LINK(SpacingLBHdl, weld::ComboBox&, void); + }; + +} // end of namespace ::sw::sidebar + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/table/chartins.cxx b/sw/source/uibase/table/chartins.cxx new file mode 100644 index 000000000..fd73a71ef --- /dev/null +++ b/sw/source/uibase/table/chartins.cxx @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentChartDataProviderAccess.hxx> + +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <view.hxx> +#include <chartins.hxx> +#include <tablemgr.hxx> +#include <frmfmt.hxx> +#include <unochart.hxx> + +#include <edtwin.hxx> + +#include <cmdid.h> +#include <anchoredobject.hxx> + +#include <cppuhelper/bootstrap.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <svtools/dialogclosedlistener.hxx> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +Point SwGetChartDialogPos( const vcl::Window *pParentWin, const Size& rDialogSize, const tools::Rectangle& rLogicChart ) +{ + // positioning code according to spec; similar to Calc fuins2.cxx + Point aRet; + + OSL_ENSURE( pParentWin, "Window not found" ); + if (pParentWin) + { + tools::Rectangle aObjPixel = pParentWin->LogicToPixel( rLogicChart, pParentWin->GetMapMode() ); + tools::Rectangle aObjAbs( pParentWin->OutputToAbsoluteScreenPixel( aObjPixel.TopLeft() ), + pParentWin->OutputToAbsoluteScreenPixel( aObjPixel.BottomRight() ) ); + + tools::Rectangle aDesktop = pParentWin->GetDesktopRectPixel(); + Size aSpace = pParentWin->LogicToPixel(Size(8, 12), MapMode(MapUnit::MapAppFont)); + + bool bLayoutRTL = ::GetActiveView()->GetWrtShell().IsTableRightToLeft(); + bool bCenterHor = false; + + if ( aDesktop.Bottom() - aObjAbs.Bottom() >= rDialogSize.Height() + aSpace.Height() ) + { + // first preference: below the chart + aRet.setY( aObjAbs.Bottom() + aSpace.Height() ); + bCenterHor = true; + } + else if ( aObjAbs.Top() - aDesktop.Top() >= rDialogSize.Height() + aSpace.Height() ) + { + // second preference: above the chart + aRet.setY( aObjAbs.Top() - rDialogSize.Height() - aSpace.Height() ); + bCenterHor = true; + } + else + { + bool bFitLeft = ( aObjAbs.Left() - aDesktop.Left() >= rDialogSize.Width() + aSpace.Width() ); + bool bFitRight = ( aDesktop.Right() - aObjAbs.Right() >= rDialogSize.Width() + aSpace.Width() ); + + if ( bFitLeft || bFitRight ) + { + // if both fit, prefer right in RTL mode, left otherwise + bool bPutRight = bFitRight && ( bLayoutRTL || !bFitLeft ); + if ( bPutRight ) + aRet.setX( aObjAbs.Right() + aSpace.Width() ); + else + aRet.setX( aObjAbs.Left() - rDialogSize.Width() - aSpace.Width() ); + + // center vertically + aRet.setY( aObjAbs.Top() + ( aObjAbs.GetHeight() - rDialogSize.Height() ) / 2 ); + } + else + { + // doesn't fit on any edge - put at the bottom of the screen + aRet.setY( aDesktop.Bottom() - rDialogSize.Height() ); + bCenterHor = true; + } + } + if ( bCenterHor ) + aRet.setX( aObjAbs.Left() + ( aObjAbs.GetWidth() - rDialogSize.Width() ) / 2 ); + + // limit to screen (centering might lead to invalid positions) + if ( aRet.X() + rDialogSize.Width() - 1 > aDesktop.Right() ) + aRet.setX( aDesktop.Right() - rDialogSize.Width() + 1 ); + if ( aRet.X() < aDesktop.Left() ) + aRet.setX( aDesktop.Left() ); + if ( aRet.Y() + rDialogSize.Height() - 1 > aDesktop.Bottom() ) + aRet.setY( aDesktop.Bottom() - rDialogSize.Height() + 1 ); + if ( aRet.Y() < aDesktop.Top() ) + aRet.setY( aDesktop.Top() ); + } + + return aRet; +} + +SwInsertChart::SwInsertChart( const Link<css::ui::dialogs::DialogClosedEvent*, void>& rLink ) +{ + SwView *pView = ::GetActiveView(); + + // get range string of marked data + SwWrtShell &rWrtShell = pView->GetWrtShell(); + uno::Reference< chart2::data::XDataProvider > xDataProvider; + uno::Reference< frame::XModel > xChartModel; + OUString aRangeString; + + if( rWrtShell.IsCursorInTable()) + { + if (!rWrtShell.IsTableMode()) + { + // select whole table + rWrtShell.GetView().GetViewFrame()->GetDispatcher()-> + Execute(FN_TABLE_SELECT_ALL, SfxCallMode::SYNCHRON); + } + if( ! rWrtShell.IsTableComplexForChart()) + { + SwFrameFormat* pTableFormat = rWrtShell.GetTableFormat(); + aRangeString = pTableFormat->GetName() + "." + rWrtShell.GetBoxNms(); + + // get table data provider + xDataProvider.set( pView->GetDocShell()->getIDocumentChartDataProviderAccess().GetChartDataProvider( true ) ); + } + } + + SwFlyFrameFormat *pFlyFrameFormat = nullptr; + xChartModel.set( SwTableFUNC( &rWrtShell ).InsertChart( xDataProvider, xDataProvider.is(), aRangeString, &pFlyFrameFormat )); + + //open wizard + //@todo get context from writer if that has one + uno::Reference< uno::XComponentContext > xContext( + ::cppu::defaultBootstrap_InitialComponentContext() ); + if( xContext.is() && xChartModel.is() && xDataProvider.is()) + { + uno::Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() ); + if(xMCF.is()) + { + uno::Reference< ui::dialogs::XAsynchronousExecutableDialog > xDialog( + xMCF->createInstanceWithContext( + "com.sun.star.comp.chart2.WizardDialog", xContext), + uno::UNO_QUERY); + uno::Reference< lang::XInitialization > xInit( xDialog, uno::UNO_QUERY ); + if( xInit.is() ) + { + // initialize dialog + uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(uno::Reference< awt::XWindow >())}, + {"ChartModel", uno::Any(xChartModel)} + })); + xInit->initialize( aSeq ); + + // try to set the dialog's position so it doesn't hide the chart + uno::Reference < beans::XPropertySet > xDialogProps( xDialog, uno::UNO_QUERY ); + if ( xDialogProps.is() ) + { + try + { + //get dialog size: + awt::Size aDialogAWTSize; + if( xDialogProps->getPropertyValue("Size") + >>= aDialogAWTSize ) + { + Size aDialogSize( aDialogAWTSize.Width, aDialogAWTSize.Height ); + if ( !aDialogSize.IsEmpty() ) + { + //calculate and set new position + SwRect aSwRect; + if (pFlyFrameFormat) + aSwRect = pFlyFrameFormat->GetAnchoredObj()->GetObjRectWithSpaces(); + tools::Rectangle aRect( aSwRect.SVRect() ); + Point aDialogPos = SwGetChartDialogPos( &rWrtShell.GetView().GetEditWin(), aDialogSize, aRect ); + xDialogProps->setPropertyValue("Position", + uno::makeAny( awt::Point(aDialogPos.getX(),aDialogPos.getY()) ) ); + } + } + } + catch (const uno::Exception&) + { + OSL_FAIL("Chart wizard couldn't be positioned automatically" ); + } + } + + ::svt::DialogClosedListener* pListener = new ::svt::DialogClosedListener(); + pListener->SetDialogClosedLink( rLink ); + css::uno::Reference<css::ui::dialogs::XDialogClosedListener> xListener( pListener ); + + xDialog->startExecuteModal( xListener ); + } + else + { + uno::Reference< lang::XComponent > xComponent( xDialog, uno::UNO_QUERY ); + if( xComponent.is()) + xComponent->dispose(); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/table/swtablerep.cxx b/sw/source/uibase/table/swtablerep.cxx new file mode 100644 index 000000000..97e852233 --- /dev/null +++ b/sw/source/uibase/table/swtablerep.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 <tabcol.hxx> +#include "tablepg.hxx" + +#include <swtablerep.hxx> +#include <memory> + +SwTableRep::SwTableRep( const SwTabCols& rTabCol ) + : + m_nTableWidth(0), + m_nSpace(0), + m_nLeftSpace(0), + m_nRightSpace(0), + m_nAlign(0), + m_nWidthPercent(0), + m_bLineSelected(false), + m_bWidthChanged(false), + m_bColsChanged(false) +{ + m_nAllCols = m_nColCount = rTabCol.Count(); + m_pTColumns.reset( new TColumn[ m_nColCount + 1 ] ); + SwTwips nStart = 0, + nEnd; + for( sal_uInt16 i = 0; i < m_nAllCols; ++i ) + { + nEnd = rTabCol[ i ] - rTabCol.GetLeft(); + m_pTColumns[ i ].nWidth = nEnd - nStart; + m_pTColumns[ i ].bVisible = !rTabCol.IsHidden(i); + if(!m_pTColumns[ i ].bVisible) + m_nColCount --; + nStart = nEnd; + } + m_pTColumns[ m_nAllCols ].nWidth = rTabCol.GetRight() - rTabCol.GetLeft() - nStart; + m_pTColumns[ m_nAllCols ].bVisible = true; + m_nColCount++; + m_nAllCols++; +} + +SwTableRep::~SwTableRep() +{ +} + +bool SwTableRep::FillTabCols( SwTabCols& rTabCols ) const +{ + long nOldLeft = rTabCols.GetLeft(), + nOldRight = rTabCols.GetRight(); + + bool bSingleLine = false; + + for ( size_t i = 0; i < rTabCols.Count(); ++i ) + if(!m_pTColumns[i].bVisible) + { + bSingleLine = true; + break; + } + + SwTwips nPos = 0; + const SwTwips nLeft = GetLeftSpace(); + rTabCols.SetLeft(nLeft); + if(bSingleLine) + { + // The invisible separators are taken from the old TabCols, + // the visible coming from pTColumns. + std::unique_ptr<TColumn[]> pOldTColumns(new TColumn[m_nAllCols + 1]); + SwTwips nStart = 0; + for ( sal_uInt16 i = 0; i < m_nAllCols - 1; ++i ) + { + const SwTwips nEnd = rTabCols[i] - rTabCols.GetLeft(); + pOldTColumns[i].nWidth = nEnd - nStart; + pOldTColumns[i].bVisible = !rTabCols.IsHidden(i); + nStart = nEnd; + } + pOldTColumns[m_nAllCols - 1].nWidth = rTabCols.GetRight() - rTabCols.GetLeft() - nStart; + pOldTColumns[m_nAllCols - 1].bVisible = true; + + sal_uInt16 nOldPos = 0; + sal_uInt16 nNewPos = 0; + SwTwips nOld = 0; + SwTwips nNew = 0; + bool bOld = false; + bool bFirst = true; + + for ( sal_uInt16 i = 0; i < m_nAllCols - 1; ++i ) + { + while((bFirst || bOld ) && nOldPos < m_nAllCols ) + { + nOld += pOldTColumns[nOldPos].nWidth; + nOldPos++; + if(!pOldTColumns[nOldPos - 1].bVisible) + break; + } + while((bFirst || !bOld ) && nNewPos < m_nAllCols ) + { + nNew += m_pTColumns[nNewPos].nWidth; + nNewPos++; + if(pOldTColumns[nNewPos - 1].bVisible) + break; + } + bFirst = false; + // They have to be inserted sorted. + bOld = nOld < nNew; + nPos = bOld ? nOld : nNew; + rTabCols[i] = nPos + nLeft; + rTabCols.SetHidden( i, bOld ); + } + rTabCols.SetRight(nLeft + m_nTableWidth); + } + else + { + for ( sal_uInt16 i = 0; i < m_nAllCols - 1; ++i ) + { + nPos += m_pTColumns[i].nWidth; + rTabCols[i] = nPos + rTabCols.GetLeft(); + rTabCols.SetHidden( i, !m_pTColumns[i].bVisible ); + rTabCols.SetRight(nLeft + m_pTColumns[m_nAllCols - 1].nWidth + nPos); + } + } + + // intercept rounding errors + if(std::abs(nOldLeft - rTabCols.GetLeft()) < 3) + rTabCols.SetLeft(nOldLeft); + + if(std::abs(nOldRight - rTabCols.GetRight()) < 3) + rTabCols.SetRight(nOldRight); + + if(GetRightSpace() >= 0 && + rTabCols.GetRight() > rTabCols.GetRightMax()) + rTabCols.SetRight(rTabCols.GetRightMax()); + return bSingleLine; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/table/tablemgr.cxx b/sw/source/uibase/table/tablemgr.cxx new file mode 100644 index 000000000..ecf9b6dea --- /dev/null +++ b/sw/source/uibase/table/tablemgr.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 <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/XComponentSupplier.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <comphelper/classids.hxx> +#include <svx/charthelper.hxx> +#include <svtools/embedhlp.hxx> + +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <swundo.hxx> +#include <tablemgr.hxx> +#include <frmfmt.hxx> +#include <swabstdlg.hxx> +#include <swcli.hxx> +#include <docsh.hxx> +#include <unotbl.hxx> +#include <unochart.hxx> + +#include <comphelper/lok.hxx> + +using namespace ::com::sun::star; + +// Adjust line height (dialogue) +void SwTableFUNC::ColWidthDlg(weld::Window *pParent) +{ + InitTabCols(); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwTableWidthDlg(pParent, *this)); + pDlg->Execute(); +} + +// Determine the width +SwTwips SwTableFUNC::GetColWidth(sal_uInt16 nNum) const +{ + SwTwips nWidth = 0; + + if( aCols.Count() > 0 ) + { + if(aCols.Count() == GetColCount()) + { + if(nNum == aCols.Count()) + nWidth = aCols.GetRight() - aCols[nNum-1]; + else + { + if(nNum == 0) + nWidth = aCols[nNum] - aCols.GetLeft(); + else + nWidth = aCols[nNum] - aCols[nNum-1]; + } + } + else + { + SwTwips nRValid = nNum < GetColCount() ? + aCols[GetRightSeparator(nNum)] : + aCols.GetRight(); + SwTwips nLValid = nNum ? + aCols[GetRightSeparator(nNum - 1)] : + aCols.GetLeft(); + nWidth = nRValid - nLValid; + } + } + else + nWidth = aCols.GetRight(); + return nWidth; +} + +SwTwips SwTableFUNC::GetMaxColWidth( sal_uInt16 nNum ) const +{ + OSL_ENSURE(nNum <= aCols.Count(), "Index out of Area"); + + if ( GetColCount() > 0 ) + { + // The maximum width arises from the own width and + // the width each of the neighbor cells reduced by MINLAY. + SwTwips nMax; + if(nNum == 0) + nMax = GetColWidth(1) - MINLAY; + else + { + nMax = GetColWidth(nNum-1); + if(nNum == GetColCount()) + nMax -= MINLAY; + else + nMax += GetColWidth(nNum+1) - 2 * MINLAY; + } + return nMax + GetColWidth(nNum); + } + else + return GetColWidth(nNum); +} + +void SwTableFUNC::SetColWidth(sal_uInt16 nNum, SwTwips nNewWidth ) +{ + // set current width + // move all of the following + bool bCurrentOnly = false; + + if ( aCols.Count() > 0 ) + { + if(aCols.Count() != GetColCount()) + bCurrentOnly = true; + SwTwips nWidth = GetColWidth(nNum); + + int nDiff = static_cast<int>(nNewWidth - nWidth); + if( !nNum ) + aCols[ GetRightSeparator(0) ] += nDiff; + else if( nNum < GetColCount() ) + { + if(nDiff < GetColWidth(nNum + 1) - MINLAY) + aCols[ GetRightSeparator(nNum) ] += nDiff; + else + { + int nDiffLeft = nDiff - static_cast<int>(GetColWidth(nNum + 1)) + int(MINLAY); + aCols[ GetRightSeparator(nNum) ] += (nDiff - nDiffLeft); + aCols[ GetRightSeparator(nNum - 1) ] -= nDiffLeft; + } + } + else + aCols[ GetRightSeparator(nNum-1) ] -= nDiff; + } + else + aCols.SetRight( std::min( nNewWidth, aCols.GetRightMax()) ); + + pSh->StartAllAction(); + pSh->SetTabCols( aCols, bCurrentOnly ); + pSh->EndAllAction(); +} + +void SwTableFUNC::InitTabCols() +{ + OSL_ENSURE(pSh, "no Shell"); + + if( pFormat && pSh) + pSh->GetTabCols( aCols ); +} + +SwTableFUNC::SwTableFUNC(SwWrtShell *pShell) + : pFormat(pShell->GetTableFormat()), + pSh(pShell) +{ +} + +SwTableFUNC::~SwTableFUNC() +{ +} + +void SwTableFUNC::UpdateChart() +{ + //Update of the fields triggered by the user, all Charts of + //the table will be brought up to date + SwFrameFormat *pFormat2 = pSh->GetTableFormat(); + if ( pFormat2 && pSh->HasOLEObj( pFormat2->GetName() ) ) + { + pSh->StartAllAction(); + pSh->UpdateCharts( pFormat2->GetName() ); + pSh->EndAllAction(); + } +} + +uno::Reference< frame::XModel > SwTableFUNC::InsertChart( + uno::Reference< chart2::data::XDataProvider > const &rxDataProvider, + bool bFillWithData, + const OUString &rCellRange, + SwFlyFrameFormat** ppFlyFrameFormat ) +{ + uno::Reference< frame::XModel > xChartModel; + pSh->StartUndo( SwUndoId::UI_INSERT_CHART ); + pSh->StartAllAction(); + + OUString aName; + if (pSh->IsCursorInTable()) + { + aName = pSh->GetTableFormat()->GetName(); + // insert node before table + pSh->MoveTable( GotoCurrTable, fnTableStart ); + pSh->Up( false ); + if ( pSh->IsCursorInTable() ) + { + if ( aName != pSh->GetTableFormat()->GetName() ) + pSh->Down( false ); // two adjacent tables + } + pSh->SplitNode(); + } + + // insert chart + OUString aObjName; + comphelper::EmbeddedObjectContainer aCnt; + uno::Reference < embed::XEmbeddedObject > xObj = + aCnt.CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aObjName ); + + ::svt::EmbeddedObjectRef aEmbObjRef( xObj, css::embed::Aspects::MSOLE_CONTENT ); + if ( xObj.is() ) + { + + SwFlyFrameFormat* pTmp = nullptr; + pSh->InsertOleObject( aEmbObjRef, &pTmp ); + if (ppFlyFrameFormat) + *ppFlyFrameFormat = pTmp; + + xChartModel.set( xObj->getComponent(), uno::UNO_QUERY ); + if( xChartModel.is() ) + { + // Create a default chart type. + uno::Reference<chart2::XChartDocument> xChartDoc(xChartModel, uno::UNO_QUERY); + if (xChartDoc.is()) + xChartDoc->createDefaultChart(); + + xChartModel->lockControllers(); //#i79578# don't request a new replacement image for charts to often - block change notifications + } + + // set the table name at the OLE-node + if (!aName.isEmpty()) + pSh->SetChartName( aName ); + } + pSh->EndAllAction(); + + if (xObj.is() && !comphelper::LibreOfficeKit::isActive()) + { + // Let the chart be activated after the inserting (unless + // via LibreOfficeKit) + SfxInPlaceClient* pClient = pSh->GetView().FindIPClient( xObj, &pSh->GetView().GetEditWin() ); + if ( !pClient ) + { + pClient = new SwOleClient( &pSh->GetView(), &pSh->GetView().GetEditWin(), aEmbObjRef ); + pSh->SetCheckForOLEInCaption( true ); + } + pSh->CalcAndSetScale( aEmbObjRef ); + //#50270# We don't need to handle errors, + //this does the DoVerb in the SfxViewShell. + ErrCode nErr = pClient->DoVerb(embed::EmbedVerbs::MS_OLEVERB_SHOW); + (void) nErr; + + // #i121334# + ChartHelper::AdaptDefaultsForChart( xObj ); + } + + uno::Reference< chart2::data::XDataReceiver > xDataReceiver( xChartModel, uno::UNO_QUERY ); + if (bFillWithData && xDataReceiver.is() && rxDataProvider.is()) + { + xDataReceiver->attachDataProvider( rxDataProvider ); + + uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( pSh->GetView().GetDocShell()->GetModel(), uno::UNO_QUERY ); + xDataReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier ); + + // default values for ranges that do not consist of a single row or column + bool bHasCategories = true; + bool bFirstCellAsLabel = true; + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + + SwRangeDescriptor aDesc; + FillRangeDescriptor( aDesc, rCellRange ); + bool bSingleRowCol = aDesc.nTop == aDesc.nBottom || aDesc.nLeft == aDesc.nRight; + if (bSingleRowCol) + { + aDesc.Normalize(); + sal_Int32 nRowLen = aDesc.nRight - aDesc.nLeft + 1; + sal_Int32 nColLen = aDesc.nBottom - aDesc.nTop + 1; + + bHasCategories = false; + if (nRowLen == 1 && nColLen == 1) + bFirstCellAsLabel = false; + else if (nRowLen > 1) + eDataRowSource = chart::ChartDataRowSource_ROWS; + else if (nColLen > 1) + eDataRowSource = chart::ChartDataRowSource_COLUMNS; + else { + OSL_FAIL("unexpected state" ); + } + } + + uno::Sequence< beans::PropertyValue > aArgs( 4 ); + aArgs[0] = beans::PropertyValue( + "CellRangeRepresentation", -1, + uno::makeAny( rCellRange ), beans::PropertyState_DIRECT_VALUE ); + aArgs[1] = beans::PropertyValue( + "HasCategories", -1, + uno::makeAny( bHasCategories ), beans::PropertyState_DIRECT_VALUE ); + aArgs[2] = beans::PropertyValue( + "FirstCellAsLabel", -1, + uno::makeAny( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE ); + aArgs[3] = beans::PropertyValue( + "DataRowSource", -1, + uno::makeAny( eDataRowSource ), beans::PropertyState_DIRECT_VALUE ); + xDataReceiver->setArguments( aArgs ); + } + + pSh->EndUndo( SwUndoId::UI_INSERT_CHART ); + + if( xChartModel.is() ) + xChartModel->unlockControllers(); //#i79578# don't request a new replacement image for charts to often + return xChartModel; +} + +sal_uInt16 SwTableFUNC::GetCurColNum() const +{ + const size_t nPos = pSh->GetCurTabColNum(); + size_t nCount = 0; + for( size_t i = 0; i < nPos; i++ ) + if(aCols.IsHidden(i)) + nCount ++; + return nPos - nCount; +} + +sal_uInt16 SwTableFUNC::GetColCount() const +{ + size_t nCount = 0; + for(size_t i = 0; i < aCols.Count(); i++ ) + if(aCols.IsHidden(i)) + nCount ++; + return aCols.Count() - nCount; +} + +int SwTableFUNC::GetRightSeparator(int nNum) const +{ + OSL_ENSURE( nNum < static_cast<int>(GetColCount()) ,"Index out of range"); + int i = 0; + while( nNum >= 0 ) + { + if( !aCols.IsHidden(i) ) + nNum--; + i++; + } + return i - 1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/table/tablepg.hxx b/sw/source/uibase/table/tablepg.hxx new file mode 100644 index 000000000..f2a6311fb --- /dev/null +++ b/sw/source/uibase/table/tablepg.hxx @@ -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 . + */ +#ifndef INCLUDED_SW_SOURCE_UIBASE_TABLE_TABLEPG_HXX +#define INCLUDED_SW_SOURCE_UIBASE_TABLE_TABLEPG_HXX +#include <sfx2/tabdlg.hxx> +#include <prcntfld.hxx> +#include <swtypes.hxx> +#include <svx/frmdirlbox.hxx> + +class SwWrtShell; +class SwTableRep; +struct ImplSVEvent; + +struct TColumn +{ + SwTwips nWidth; + bool bVisible; +}; + +class SwFormatTablePage : public SfxTabPage +{ + SwTableRep* pTableData; + SwTwips nSaveWidth; + SwTwips nMinTableWidth; + bool bModified; + bool bFull:1; + bool bHtmlMode : 1; + + std::unique_ptr<weld::Entry> m_xNameED; + std::unique_ptr<weld::Label> m_xWidthFT; + std::unique_ptr<SwPercentField> m_xWidthMF; + std::unique_ptr<weld::CheckButton> m_xRelWidthCB; + + std::unique_ptr<weld::RadioButton> m_xFullBtn; + std::unique_ptr<weld::RadioButton> m_xLeftBtn; + std::unique_ptr<weld::RadioButton> m_xFromLeftBtn; + std::unique_ptr<weld::RadioButton> m_xRightBtn; + std::unique_ptr<weld::RadioButton> m_xCenterBtn; + std::unique_ptr<weld::RadioButton> m_xFreeBtn; + + std::unique_ptr<weld::Label> m_xLeftFT; + std::unique_ptr<SwPercentField> m_xLeftMF; + std::unique_ptr<weld::Label> m_xRightFT; + std::unique_ptr<SwPercentField> m_xRightMF; + std::unique_ptr<weld::Label> m_xTopFT; + std::unique_ptr<weld::MetricSpinButton> m_xTopMF; + std::unique_ptr<weld::Label> m_xBottomFT; + std::unique_ptr<weld::MetricSpinButton> m_xBottomMF; + + std::unique_ptr<svx::FrameDirectionListBox> m_xTextDirectionLB; + std::unique_ptr<weld::Widget> m_xProperties; + + void Init(); + void ModifyHdl(const weld::MetricSpinButton& rEdit); + + DECL_LINK(AutoClickHdl, weld::ToggleButton&, void); + DECL_LINK(RelWidthClickHdl, weld::ToggleButton&, void); + void RightModify(); + DECL_LINK(ValueChangedHdl, weld::MetricSpinButton&, void); + +public: + SwFormatTablePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + virtual ~SwFormatTablePage() override; + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; +}; + +// TabPage Format/Table/Columns +#define MET_FIELDS 6 //Number of the used MetricFields + +class SwTableColumnPage : public SfxTabPage +{ + SwTableRep* m_pTableData; + ImplSVEvent* m_pSizeHdlEvent; + SwTwips m_nTableWidth; + SwTwips m_nMinWidth; + sal_uInt16 m_nMetFields; + sal_uInt16 m_nNoOfCols; + sal_uInt16 m_nNoOfVisibleCols; + // Remember the width, when switching to autoalign + sal_uInt16 m_aValueTable[MET_FIELDS];// primary assignment of the MetricFields + bool m_bModified:1; + bool m_bModifyTable:1; + bool m_bPercentMode:1; + + SwPercentField m_aFieldArr[MET_FIELDS]; + std::unique_ptr<weld::Label> m_aTextArr[MET_FIELDS]; + std::unique_ptr<weld::CheckButton> m_xModifyTableCB; + std::unique_ptr<weld::CheckButton> m_xProportionalCB; + std::unique_ptr<weld::Label> m_xSpaceFT; + std::unique_ptr<weld::MetricSpinButton> m_xSpaceED; + std::unique_ptr<weld::Button> m_xUpBtn; + std::unique_ptr<weld::Button> m_xDownBtn; + + void Init(bool bWeb); + DECL_LINK(AutoClickHdl, weld::Button&, void); + void ModifyHdl(const weld::MetricSpinButton* pEdit); + DECL_LINK(ValueChangedHdl, weld::MetricSpinButton&, void); + DECL_LINK(ModeHdl, weld::ToggleButton&, void); + void UpdateCols( sal_uInt16 nCurrentPos ); + SwTwips GetVisibleWidth(sal_uInt16 nPos); + void SetVisibleWidth(sal_uInt16 nPos, SwTwips nNewWidth); + DECL_LINK(SizeHdl, void*, void); + +public: + SwTableColumnPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + virtual ~SwTableColumnPage() override; + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + +}; + +class SwTextFlowPage : public SfxTabPage +{ + SwWrtShell* pShell; + bool bPageBreak; + bool bHtmlMode; + + std::unique_ptr<weld::CheckButton> m_xPgBrkCB; + std::unique_ptr<weld::RadioButton> m_xPgBrkRB; + std::unique_ptr<weld::RadioButton> m_xColBrkRB; + std::unique_ptr<weld::RadioButton> m_xPgBrkBeforeRB; + std::unique_ptr<weld::RadioButton> m_xPgBrkAfterRB; + std::unique_ptr<weld::CheckButton> m_xPageCollCB; + std::unique_ptr<weld::ComboBox> m_xPageCollLB; + std::unique_ptr<weld::CheckButton> m_xPageNoCB; + std::unique_ptr<weld::SpinButton> m_xPageNoNF; + std::unique_ptr<weld::CheckButton> m_xSplitCB; + std::unique_ptr<weld::CheckButton> m_xSplitRowCB; + std::unique_ptr<weld::CheckButton> m_xKeepCB; + std::unique_ptr<weld::CheckButton> m_xHeadLineCB; + std::unique_ptr<weld::Widget> m_xRepeatHeaderCombo; + std::unique_ptr<weld::SpinButton> m_xRepeatHeaderNF; + std::unique_ptr<weld::ComboBox> m_xTextDirectionLB; + std::unique_ptr<weld::ComboBox> m_xVertOrientLB; + + DECL_LINK(PageBreakHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(ApplyCollClickHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(PageBreakPosHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(PageBreakTypeHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(PageNoClickHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(SplitHdl_Impl, weld::ToggleButton&, void); + DECL_LINK(HeadLineCBClickHdl, weld::ToggleButton&, void); + +public: + SwTextFlowPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet); + virtual ~SwTextFlowPage() override; + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + + void SetShell(SwWrtShell* pSh); + + void DisablePageBreak(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uitest/uiobject.cxx b/sw/source/uibase/uitest/uiobject.cxx new file mode 100644 index 000000000..7f922dc9c --- /dev/null +++ b/sw/source/uibase/uitest/uiobject.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.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 <uiobject.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <navipi.hxx> +#include <sfx2/sidebar/Sidebar.hxx> +#include <sfx2/viewfrm.hxx> + +SwEditWinUIObject::SwEditWinUIObject(const VclPtr<SwEditWin>& xEditWin): + WindowUIObject(xEditWin), + mxEditWin(xEditWin) +{ +} + +namespace { + +SwWrtShell& getWrtShell(VclPtr<SwEditWin> const & xEditWin) +{ + return xEditWin->GetView().GetWrtShell(); +} + +} + +StringMap SwEditWinUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + aMap["SelectedText"] = mxEditWin->GetView().GetSelectionText(); + + sal_uInt16 nPageNum = 0; + sal_uInt16 nVirtPageNum = 0; + SwWrtShell& rWrtShell = getWrtShell(mxEditWin); + rWrtShell.GetPageNum(nPageNum, nVirtPageNum); + aMap["CurrentPage"] = OUString::number(nPageNum); + rWrtShell.GetPageNum(nPageNum, nVirtPageNum, false); + aMap["TopVisiblePage"] = OUString::number(nPageNum); + + sal_uInt16 nPages = rWrtShell.GetPageCnt(); + aMap["Pages"] = OUString::number(nPages); + + aMap["StartWord"] = OUString::boolean(rWrtShell.IsStartWord()); + aMap["EndWord"] = OUString::boolean(rWrtShell.IsEndWord()); + aMap["StartSentence"] = OUString::boolean(rWrtShell.IsStartSentence()); + aMap["EndSentence"] = OUString::boolean(rWrtShell.IsEndSentence()); + aMap["StartPara"] = OUString::boolean(rWrtShell.IsSttPara()); + aMap["EndPara"] = OUString::boolean(rWrtShell.IsEndPara()); + aMap["StartDoc"] = OUString::boolean(rWrtShell.IsStartOfDoc()); + aMap["EndDoc"] = OUString::boolean(rWrtShell.IsEndOfDoc()); + + return aMap; +} + +void SwEditWinUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "SET") + { + if (rParameters.find("ZOOM") != rParameters.end()) + { + auto itr = rParameters.find("ZOOM"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + mxEditWin->GetView().SetZoom(SvxZoomType::PERCENT, nVal); + } + } + else if (rAction == "GOTO") + { + if (rParameters.find("PAGE") != rParameters.end()) + { + auto itr = rParameters.find("PAGE"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + getWrtShell(mxEditWin).GotoPage(nVal, false); + } + } + else if (rAction == "SELECT") + { + if (rParameters.find("START_POS") != rParameters.end()) + { + auto itr = rParameters.find("START_POS"); + OUString aStartPos = itr->second; + sal_Int32 nStartPos = aStartPos.toInt32(); + + itr = rParameters.find("END_POS"); + assert(itr != rParameters.end()); + OUString aEndPos = itr->second; + sal_Int32 nEndPos = aEndPos.toInt32(); + + getWrtShell(mxEditWin).SelectText(nStartPos, nEndPos); + } + } + else if (rAction == "SIDEBAR") + { + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + DBG_ASSERT(pViewFrm, "SwEditWinUIObject::execute: no viewframe"); + pViewFrm->ShowChildWindow(SID_SIDEBAR); + + if (rParameters.find("PANEL") != rParameters.end()) + { + auto itr = rParameters.find("PANEL"); + OUString aVal = itr->second; + ::sfx2::sidebar::Sidebar::ShowPanel(aVal, pViewFrm->GetFrame().GetFrameInterface()); + } + } + else + WindowUIObject::execute(rAction, rParameters); +} + +OUString SwEditWinUIObject::get_name() const +{ + return "SwEditWinUIObject"; +} + +std::unique_ptr<UIObject> SwEditWinUIObject::create(vcl::Window* pWindow) +{ + SwEditWin* pEditWin = dynamic_cast<SwEditWin*>(pWindow); + assert(pEditWin); + return std::unique_ptr<UIObject>(new SwEditWinUIObject(pEditWin)); +} + +SwNavigationPIUIObject::SwNavigationPIUIObject(const VclPtr<SwNavigationPI>& xSwNavigationPI): + WindowUIObject(xSwNavigationPI), + mxSwNavigationPI(xSwNavigationPI) +{ +} + +StringMap SwNavigationPIUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + aMap["selectioncount"] = OUString::number(mxSwNavigationPI->m_xContentTree->count_selected_rows()); + aMap["selectedtext"] = mxSwNavigationPI->m_xContentTree->get_selected_text(); + + return aMap; +} + +void SwNavigationPIUIObject::execute(const OUString& rAction, + const StringMap& rParameters) +{ + if (rAction == "ROOT") + { + mxSwNavigationPI->m_xContentTree->grab_focus(); + mxSwNavigationPI->ToolBoxSelectHdl("root"); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +std::unique_ptr<UIObject> SwNavigationPIUIObject::create(vcl::Window* pWindow) +{ + SwNavigationPI* pSwNavigationPI = dynamic_cast<SwNavigationPI*>(pWindow); + assert(pSwNavigationPI); + return std::unique_ptr<UIObject>(new SwNavigationPIUIObject(pSwNavigationPI)); +} + +OUString SwNavigationPIUIObject::get_name() const +{ + return "SwNavigationPIUIObject"; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/formatclipboard.cxx b/sw/source/uibase/uiview/formatclipboard.cxx new file mode 100644 index 000000000..e9268bffa --- /dev/null +++ b/sw/source/uibase/uiview/formatclipboard.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 <memory> +#include <utility> + +#include <formatclipboard.hxx> + +#include <cmdid.h> +#include <charfmt.hxx> +#include <frmfmt.hxx> +#include <docstyle.hxx> +#include <fchrfmt.hxx> +#include <svx/svdview.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/formatbreakitem.hxx> +#include <fmtlsplt.hxx> +#include <editeng/keepitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <fmtpdsc.hxx> +#include <fmtrowsplt.hxx> +#include <frmatr.hxx> + +namespace +{ + +std::unique_ptr<SfxItemSet> lcl_CreateEmptyItemSet( SelectionType nSelectionType, SfxItemPool& rPool, bool bNoParagraphFormats = false ) +{ + std::unique_ptr<SfxItemSet> pItemSet; + if( nSelectionType & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic) ) + { + pItemSet = std::make_unique<SfxItemSet>( + rPool, + svl::Items< + RES_FRMATR_BEGIN, RES_FILL_ORDER, + // no RES_FRM_SIZE + RES_PAPER_BIN, RES_SURROUND, + // no RES_VERT_ORIENT + // no RES_HORI_ORIENT + // no RES_ANCHOR + RES_BACKGROUND, RES_SHADOW, + // no RES_FRMMACRO + RES_COL, RES_KEEP, + // no RES_URL + RES_EDIT_IN_READONLY, RES_LAYOUT_SPLIT, + // no RES_CHAIN + RES_TEXTGRID, RES_FRMATR_END - 1>{}); + } + else if( nSelectionType & SelectionType::DrawObject ) + { + //is handled different + } + else if( nSelectionType & SelectionType::Text ) + { + if( bNoParagraphFormats ) + pItemSet = std::make_unique<SfxItemSet>(rPool, + svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>{}); + else + pItemSet = std::make_unique<SfxItemSet>( + rPool, + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_PARATR_BEGIN, RES_FILL_ORDER, + // no RES_FRM_SIZE + RES_PAPER_BIN, RES_SURROUND, + // no RES_VERT_ORIENT + // no RES_HORI_ORIENT + // no RES_ANCHOR + RES_BACKGROUND, RES_SHADOW, + // no RES_FRMMACRO + RES_COL, RES_KEEP, + // no RES_URL + RES_EDIT_IN_READONLY, RES_LAYOUT_SPLIT, + // no RES_CHAIN + RES_TEXTGRID, RES_FRMATR_END - 1>{}); + } + return pItemSet; +} + +void lcl_getTableAttributes( SfxItemSet& rSet, SwWrtShell &rSh ) +{ + std::unique_ptr<SvxBrushItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND)); + rSh.GetBoxBackground(aBrush); + rSet.Put( *aBrush ); + if(rSh.GetRowBackground(aBrush)) + { + aBrush->SetWhich(SID_ATTR_BRUSH_ROW); + rSet.Put( *aBrush ); + } + else + rSet.InvalidateItem(SID_ATTR_BRUSH_ROW); + rSh.GetTabBackground(aBrush); + aBrush->SetWhich(SID_ATTR_BRUSH_TABLE); + rSet.Put( *aBrush ); + + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + rSet.Put(aBoxInfo); + rSh.GetTabBorders( rSet ); + + std::unique_ptr<SvxFrameDirectionItem> aBoxDirection(std::make_unique<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)); + if(rSh.GetBoxDirection( aBoxDirection )) + { + aBoxDirection->SetWhich(FN_TABLE_BOX_TEXTORIENTATION); + rSet.Put(*aBoxDirection); + } + + rSet.Put(SfxUInt16Item(FN_TABLE_SET_VERT_ALIGN, rSh.GetBoxAlign())); + + rSet.Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE, rSh.GetRowsToRepeat() ) ); + + SwFrameFormat *pFrameFormat = rSh.GetTableFormat(); + if(pFrameFormat) + { + rSet.Put( pFrameFormat->GetShadow() ); + rSet.Put( pFrameFormat->GetBreak() ); + rSet.Put( pFrameFormat->GetPageDesc() ); + rSet.Put( pFrameFormat->GetLayoutSplit() ); + rSet.Put( pFrameFormat->GetKeep() ); + rSet.Put( pFrameFormat->GetFrameDir() ); + } + + std::unique_ptr<SwFormatRowSplit> pSplit = rSh.GetRowSplit(); + if(pSplit) + rSet.Put(std::move(pSplit)); +} + +void lcl_setTableAttributes( const SfxItemSet& rSet, SwWrtShell &rSh ) +{ + const SfxPoolItem* pItem = nullptr; + bool bBorder = ( SfxItemState::SET == rSet.GetItemState( RES_BOX ) || + SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER ) ); + bool bBackground = SfxItemState::SET == rSet.GetItemState( RES_BACKGROUND, false, &pItem ); + const SfxPoolItem* pRowItem = nullptr, *pTableItem = nullptr; + bBackground |= SfxItemState::SET == rSet.GetItemState( SID_ATTR_BRUSH_ROW, false, &pRowItem ); + bBackground |= SfxItemState::SET == rSet.GetItemState( SID_ATTR_BRUSH_TABLE, false, &pTableItem ); + + if(bBackground) + { + if(pItem) + rSh.SetBoxBackground( *static_cast<const SvxBrushItem*>(pItem) ); + if(pRowItem) + { + std::unique_ptr<SvxBrushItem> aBrush(static_cast<SvxBrushItem*>(pRowItem->Clone())); + aBrush->SetWhich(RES_BACKGROUND); + rSh.SetRowBackground(*aBrush); + } + if(pTableItem) + { + std::unique_ptr<SvxBrushItem> aBrush(static_cast<SvxBrushItem*>(pTableItem->Clone())); + aBrush->SetWhich(RES_BACKGROUND); + rSh.SetTabBackground(*aBrush); + } + } + if(bBorder) + rSh.SetTabBorders( rSet ); + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_TABLE_HEADLINE, false, &pItem) ) + rSh.SetRowsToRepeat( static_cast<const SfxUInt16Item*>(pItem)->GetValue() ); + + SwFrameFormat* pFrameFormat = rSh.GetTableFormat(); + if(pFrameFormat) + { + //RES_SHADOW + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_SHADOW), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + + //RES_BREAK + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_BREAK), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + + //RES_PAGEDESC + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_PAGEDESC), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + + //RES_LAYOUT_SPLIT + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_LAYOUT_SPLIT), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + + //RES_KEEP + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_KEEP), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + + //RES_FRAMEDIR + pItem=nullptr; + rSet.GetItemState(rSet.GetPool()->GetWhich(RES_FRAMEDIR), false, &pItem); + if(pItem) + pFrameFormat->SetFormatAttr( *pItem ); + } + + if( SfxItemState::SET == rSet.GetItemState( FN_TABLE_BOX_TEXTORIENTATION, false, &pItem) ) + { + SvxFrameDirectionItem aDirection( SvxFrameDirection::Environment, RES_FRAMEDIR ); + aDirection.SetValue(static_cast< const SvxFrameDirectionItem* >(pItem)->GetValue()); + rSh.SetBoxDirection(aDirection); + } + + if( SfxItemState::SET == rSet.GetItemState( FN_TABLE_SET_VERT_ALIGN, false, &pItem)) + rSh.SetBoxAlign(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + + if( SfxItemState::SET == rSet.GetItemState( RES_ROW_SPLIT, false, &pItem) ) + rSh.SetRowSplit(*static_cast<const SwFormatRowSplit*>(pItem)); +} +}//end anonymous namespace + +SwFormatClipboard::SwFormatClipboard() + : m_nSelectionType(SelectionType::NONE) + , m_bPersistentCopy(false) +{ +} + +bool SwFormatClipboard::HasContent() const +{ + return m_pItemSet_TextAttr!=nullptr + || m_pItemSet_ParAttr!=nullptr + || m_pTableItemSet != nullptr + || !m_aCharStyle.isEmpty() + || !m_aParaStyle.isEmpty() + ; +} +bool SwFormatClipboard::HasContentForThisType( SelectionType nSelectionType ) const +{ + if( !HasContent() ) + return false; + + if( m_nSelectionType == nSelectionType ) + return true; + + if( ( nSelectionType & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic) ) + && + ( m_nSelectionType & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic) ) + ) + return true; + + if( nSelectionType & SelectionType::Text && m_nSelectionType & SelectionType::Text ) + return true; + + return false; +} + +bool SwFormatClipboard::CanCopyThisType( SelectionType nSelectionType ) +{ + return bool(nSelectionType + & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic + | SelectionType::Text | SelectionType::DrawObject | SelectionType::Table | SelectionType::TableCell )); +} + +void SwFormatClipboard::Copy( SwWrtShell& rWrtShell, SfxItemPool& rPool, bool bPersistentCopy ) +{ + // first clear the previously stored attributes + Erase(); + m_bPersistentCopy = bPersistentCopy; + + SelectionType nSelectionType = rWrtShell.GetSelectionType(); + auto pItemSet_TextAttr = lcl_CreateEmptyItemSet( nSelectionType, rPool, true ); + auto pItemSet_ParAttr = lcl_CreateEmptyItemSet( nSelectionType, rPool ); + + rWrtShell.StartAction(); + rWrtShell.Push(); + + // modify the "Point and Mark" of the cursor + // in order to select only the last character of the + // selection(s) and then to get the attributes of this single character + if( nSelectionType == SelectionType::Text ) + { + // get the current PaM, the cursor + // if there several selection it currently point + // on the last (sort by there creation time) selection + SwPaM* pCursor = rWrtShell.GetCursor(); + + bool bHasSelection = pCursor->HasMark(); + bool bForwardSelection = false; + + if(!bHasSelection && pCursor->IsMultiSelection()) + { + // if cursor has multiple selections + + // clear all the selections except the last + rWrtShell.KillPams(); + + // reset the cursor to the remaining selection + pCursor = rWrtShell.GetCursor(); + bHasSelection = true; + } + + bool dontMove = false; + if (bHasSelection) + { + bForwardSelection = (*pCursor->GetPoint()) > (*pCursor->GetMark()); + + // clear the selection leaving just the cursor + pCursor->DeleteMark(); + pCursor->SetMark(); + } + else + { + bool rightToLeft = rWrtShell.IsInRightToLeftText(); + // if there were no selection (only a cursor) and the cursor was at + // the end of the paragraph then don't move + if ( rWrtShell.IsEndPara() && !rightToLeft ) + dontMove = true; + + // revert left and right + if ( rightToLeft ) + { + if (pCursor->GetPoint()->nContent == 0) + dontMove = true; + else + bForwardSelection = !bForwardSelection; + } + } + + // move the cursor in order to select one character + if (!dontMove) + pCursor->Move( bForwardSelection ? fnMoveBackward : fnMoveForward ); + } + + if(pItemSet_TextAttr) + { + if( nSelectionType & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic) ) + rWrtShell.GetFlyFrameAttr(*pItemSet_TextAttr); + else + { + // get the text attributes from named and automatic formatting + rWrtShell.GetCurAttr(*pItemSet_TextAttr); + + if( nSelectionType & SelectionType::Text ) + { + // get the paragraph attributes (could be character properties) + // from named and automatic formatting + rWrtShell.GetCurParAttr(*pItemSet_ParAttr); + } + } + } + else if ( nSelectionType & SelectionType::DrawObject ) + { + SdrView* pDrawView = rWrtShell.GetDrawView(); + if(pDrawView) + { + if( pDrawView->AreObjectsMarked() ) + { + pItemSet_TextAttr = std::make_unique<SfxItemSet>( pDrawView->GetAttrFromMarked(true/*bOnlyHardAttr*/) ); + //remove attributes defining the type/data of custom shapes + pItemSet_TextAttr->ClearItem(SDRATTR_CUSTOMSHAPE_ENGINE); + pItemSet_TextAttr->ClearItem(SDRATTR_CUSTOMSHAPE_DATA); + pItemSet_TextAttr->ClearItem(SDRATTR_CUSTOMSHAPE_GEOMETRY); + } + } + } + + if( nSelectionType & SelectionType::TableCell )//only copy table attributes if really cells are selected (not only text in tables) + { + m_pTableItemSet = std::make_unique<SfxItemSet>( + rPool, + svl::Items< + RES_PAGEDESC, RES_BREAK, + RES_BACKGROUND, RES_SHADOW, // RES_BOX is inbetween + RES_KEEP, RES_KEEP, + RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT, + RES_FRAMEDIR, RES_FRAMEDIR, + RES_ROW_SPLIT, RES_ROW_SPLIT, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_SHADOW, + // SID_ATTR_BORDER_OUTER is inbetween + SID_ATTR_BRUSH_ROW, SID_ATTR_BRUSH_TABLE, + FN_TABLE_SET_VERT_ALIGN, FN_TABLE_SET_VERT_ALIGN, + FN_TABLE_BOX_TEXTORIENTATION, FN_TABLE_BOX_TEXTORIENTATION, + FN_PARAM_TABLE_HEADLINE, FN_PARAM_TABLE_HEADLINE>{}); + lcl_getTableAttributes( *m_pTableItemSet, rWrtShell ); + } + + m_nSelectionType = nSelectionType; + m_pItemSet_TextAttr = std::move(pItemSet_TextAttr); + m_pItemSet_ParAttr = std::move(pItemSet_ParAttr); + + if( nSelectionType & SelectionType::Text ) + { + // if text is selected save the named character format + SwFormat* pFormat = rWrtShell.GetCurCharFormat(); + if( pFormat ) + m_aCharStyle = pFormat->GetName(); + + // and the named paragraph format + pFormat = rWrtShell.GetCurTextFormatColl(); + if( pFormat ) + m_aParaStyle = pFormat->GetName(); + } + + rWrtShell.Pop(SwCursorShell::PopMode::DeleteCurrent); + rWrtShell.EndAction(); +} + +typedef std::vector< std::unique_ptr< SfxPoolItem > > ItemVector; +// collect all PoolItems from the applied styles +static void lcl_AppendSetItems( ItemVector& rItemVector, const SfxItemSet& rStyleAttrSet ) +{ + const sal_uInt16* pRanges = rStyleAttrSet.GetRanges(); + while( *pRanges ) + { + for ( sal_uInt16 nWhich = *pRanges; nWhich <= *(pRanges+1); ++nWhich ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rStyleAttrSet.GetItemState( nWhich, false, &pItem ) ) + { + rItemVector.emplace_back( pItem->Clone() ); + } + } + pRanges += 2; + } +} +// remove all items that are inherited from the styles +static void lcl_RemoveEqualItems( SfxItemSet& rTemplateItemSet, const ItemVector& rItemVector ) +{ + for( const auto& rItem : rItemVector ) + { + const SfxPoolItem* pItem; + if( SfxItemState::SET == rTemplateItemSet.GetItemState( rItem->Which(), true, &pItem ) && + *pItem == *rItem ) + { + rTemplateItemSet.ClearItem( rItem->Which() ); + } + } +} + +void SwFormatClipboard::Paste( SwWrtShell& rWrtShell, SfxStyleSheetBasePool* pPool + , bool bNoCharacterFormats, bool bNoParagraphFormats ) +{ + SelectionType nSelectionType = rWrtShell.GetSelectionType(); + if( !HasContentForThisType(nSelectionType) ) + { + if(!m_bPersistentCopy) + Erase(); + return; + } + + rWrtShell.StartAction(); + rWrtShell.StartUndo(SwUndoId::INSATTR); + + ItemVector aItemVector; + + if( nSelectionType & SelectionType::Text ) + { + // apply the named text and paragraph formatting + if( pPool ) + { + // if there is a named text format recorded and the user wants to apply it + if(!m_aCharStyle.isEmpty() && !bNoCharacterFormats ) + { + // look for the named text format in the pool + SwDocStyleSheet* pStyle = static_cast<SwDocStyleSheet*>(pPool->Find(m_aCharStyle, SfxStyleFamily::Char)); + + // if the style is found + if( pStyle ) + { + SwFormatCharFormat aFormat(pStyle->GetCharFormat()); + // store the attributes from this style in aItemVector in order + // not to apply them as automatic formatting attributes later in the code + lcl_AppendSetItems( aItemVector, aFormat.GetCharFormat()->GetAttrSet()); + + // apply the named format + rWrtShell.SetAttrItem( aFormat ); + } + } + + // if there is a named paragraph format recorded and the user wants to apply it + if(!m_aParaStyle.isEmpty() && !bNoParagraphFormats ) + { + // look for the named pragraph format in the pool + SwDocStyleSheet* pStyle = static_cast<SwDocStyleSheet*>(pPool->Find(m_aParaStyle, SfxStyleFamily::Para)); + if( pStyle ) + { + // store the attributes from this style in aItemVector in order + // not to apply them as automatic formatting attributes later in the code + lcl_AppendSetItems( aItemVector, pStyle->GetCollection()->GetAttrSet()); + + // apply the named format + rWrtShell.SetTextFormatColl( pStyle->GetCollection() ); + } + } + } + + // apply the paragraph automatic attributes + if ( m_pItemSet_ParAttr && m_pItemSet_ParAttr->Count() != 0 && !bNoParagraphFormats ) + { + // temporary SfxItemSet + std::unique_ptr<SfxItemSet> pTemplateItemSet(lcl_CreateEmptyItemSet( + nSelectionType, *m_pItemSet_ParAttr->GetPool())); + // no need to verify the existence of pTemplateItemSet as we + // know that here the selection type is SEL_TXT + + pTemplateItemSet->Put( *m_pItemSet_ParAttr ); + + // remove attribute that were applied by named text and paragraph formatting + lcl_RemoveEqualItems( *pTemplateItemSet, aItemVector ); + + // apply the paragraph automatic attributes to all the nodes in the selection + rWrtShell.SetAttrSet(*pTemplateItemSet); + + // store the attributes in aItemVector in order not to apply them as + // text automatic formatting attributes later in the code + lcl_AppendSetItems( aItemVector, *pTemplateItemSet); + } + } + + if(m_pItemSet_TextAttr) + { + if( nSelectionType & SelectionType::DrawObject ) + { + SdrView* pDrawView = rWrtShell.GetDrawView(); + if(pDrawView) + { + pDrawView->SetAttrToMarked(*m_pItemSet_TextAttr, true/*bReplaceAll*/); + } + } + else + { + // temporary SfxItemSet + std::unique_ptr<SfxItemSet> pTemplateItemSet(lcl_CreateEmptyItemSet( + nSelectionType, *m_pItemSet_TextAttr->GetPool(), true )); + + if(pTemplateItemSet) + { + // copy the stored automatic text attributes in a temporary SfxItemSet + pTemplateItemSet->Put( *m_pItemSet_TextAttr ); + + // only attributes that were not apply by named style attributes and automatic + // paragraph attributes should be applied + lcl_RemoveEqualItems( *pTemplateItemSet, aItemVector ); + + // apply the character automatic attributes + if( nSelectionType & (SelectionType::Frame | SelectionType::Ole | SelectionType::Graphic) ) + rWrtShell.SetFlyFrameAttr(*pTemplateItemSet); + else if ( !bNoCharacterFormats ) + rWrtShell.SetAttrSet(*pTemplateItemSet); + } + } + } + + if( m_pTableItemSet && nSelectionType & (SelectionType::Table | SelectionType::TableCell) ) + lcl_setTableAttributes( *m_pTableItemSet, rWrtShell ); + + rWrtShell.EndUndo(SwUndoId::INSATTR); + rWrtShell.EndAction(); + + if(!m_bPersistentCopy) + Erase(); +} + +void SwFormatClipboard::Erase() +{ + m_nSelectionType = SelectionType::NONE; + + m_pItemSet_TextAttr.reset(); + + m_pItemSet_ParAttr.reset(); + + m_pTableItemSet.reset(); + + if( !m_aCharStyle.isEmpty() ) + m_aCharStyle.clear(); + if( !m_aParaStyle.isEmpty() ) + m_aParaStyle.clear(); + + m_bPersistentCopy = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/pview.cxx b/sw/source/uibase/uiview/pview.cxx new file mode 100644 index 000000000..fae7d9cef --- /dev/null +++ b/sw/source/uibase/uiview/pview.cxx @@ -0,0 +1,1887 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/objface.hxx> +#include <vcl/help.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syswin.hxx> +#include <vcl/weld.hxx> + +#include <rtl/ustrbuf.hxx> +#include <svl/whiter.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/dispatch.hxx> +#include <editeng/paperinf.hxx> +#include <svx/svdview.hxx> +#include <svx/zoomslideritem.hxx> +#include <tools/svborder.hxx> + +#include <globdoc.hxx> +#include <wdocsh.hxx> +#include <pvprtdat.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <viewopt.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <pview.hxx> +#include <view.hxx> +#include <scroll.hxx> +#include <prtopt.hxx> +#include <usrpref.hxx> +#include "viewfunc.hxx" + +#include <helpids.h> +#include <cmdid.h> +#include <strings.hrc> + +#define ShellClass_SwPagePreview +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <pagepreviewlayout.hxx> + +#include <svx/svxdlg.hxx> + +#include <memory> +#include <vcl/EnumContext.hxx> +#include <vcl/notebookbar.hxx> + +using namespace ::com::sun::star; +SFX_IMPL_NAMED_VIEWFACTORY(SwPagePreview, "PrintPreview") +{ + SFX_VIEW_REGISTRATION(SwDocShell); + SFX_VIEW_REGISTRATION(SwWebDocShell); + SFX_VIEW_REGISTRATION(SwGlobalDocShell); +} + +SFX_IMPL_INTERFACE(SwPagePreview, SfxViewShell) + +void SwPagePreview::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("preview"); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, + SfxVisibilityFlags::Standard|SfxVisibilityFlags::Client|SfxVisibilityFlags::FullScreen|SfxVisibilityFlags::ReadonlyDoc, + ToolbarId::PView_Toolbox); +} + + +#define SWVIEWFLAGS SfxViewShellFlags::HAS_PRINTOPTIONS + +#define MIN_PREVIEW_ZOOM 25 +#define MAX_PREVIEW_ZOOM 600 + +static sal_uInt16 lcl_GetNextZoomStep(sal_uInt16 nCurrentZoom, bool bZoomIn) +{ + static const sal_uInt16 aZoomArr[] = + { + 25, 50, 75, 100, 150, 200, 400, 600 + }; + const int nZoomArrSize = static_cast<int>(SAL_N_ELEMENTS(aZoomArr)); + if (bZoomIn) + { + for(int i = nZoomArrSize - 1; i >= 0; --i) + { + if(nCurrentZoom > aZoomArr[i] || !i) + return aZoomArr[i]; + } + } + else + { + for(sal_uInt16 i : aZoomArr) + { + if(nCurrentZoom < i) + return i; + } + } + return bZoomIn ? MAX_PREVIEW_ZOOM : MIN_PREVIEW_ZOOM; +}; + +static void lcl_InvalidateZoomSlots(SfxBindings& rBindings) +{ + static sal_uInt16 const aInval[] = + { + SID_ATTR_ZOOM, SID_ZOOM_OUT, SID_ZOOM_IN, SID_ATTR_ZOOMSLIDER, FN_PREVIEW_ZOOM, FN_STAT_ZOOM, + 0 + }; + rBindings.Invalidate( aInval ); +} + +namespace { + +// At first the zoom dialog +class SwPreviewZoomDlg : public weld::GenericDialogController +{ + SwPagePreviewWin& m_rParent; + std::unique_ptr<weld::SpinButton> m_xRowEdit; + std::unique_ptr<weld::SpinButton> m_xColEdit; + +public: + SwPreviewZoomDlg(SwPagePreviewWin& rParent) + : GenericDialogController(rParent.GetFrameWeld(), "modules/swriter/ui/previewzoomdialog.ui", "PreviewZoomDialog") + , m_rParent(rParent) + , m_xRowEdit(m_xBuilder->weld_spin_button("rows")) + , m_xColEdit(m_xBuilder->weld_spin_button("cols")) + { + m_xRowEdit->set_value(rParent.GetRow()); + m_xColEdit->set_value(rParent.GetCol()); + } + + void execute() + { + if (run() == RET_OK) + { + m_rParent.CalcWish(sal_uInt8(m_xRowEdit->get_value()), sal_uInt8(m_xColEdit->get_value())); + } + } +}; + +} + +// all for SwPagePreviewWin +SwPagePreviewWin::SwPagePreviewWin( vcl::Window *pParent, SwPagePreview& rPView ) + : Window(pParent, WinBits(WB_CLIPCHILDREN)) + , mpViewShell(nullptr) + , mrView(rPView) + , mbCalcScaleForPreviewLayout(true) + , maPaintedPreviewDocRect(tools::Rectangle(0,0,0,0)) + , mpPgPreviewLayout(nullptr) +{ + SetOutDevViewType( OutDevViewType::PrintPreview ); + SetHelpId(HID_PAGEPREVIEW); + SetFillColor( GetBackground().GetColor() ); + SetLineColor( GetBackground().GetColor()); + SetMapMode( MapMode(MapUnit::MapTwip) ); + + const SwMasterUsrPref *pUsrPref = SW_MOD()->GetUsrPref(false); + mnRow = pUsrPref->GetPagePrevRow(); // 1 row + mnCol = pUsrPref->GetPagePrevCol(); // 1 column + mnSttPage = USHRT_MAX; +} + +SwPagePreviewWin::~SwPagePreviewWin() +{ +} + +void SwPagePreviewWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (!mpViewShell || !mpViewShell->GetLayout()) + return; + + if (USHRT_MAX == mnSttPage) // was never calculated ? (Init-Phase!) + { + // This is the size to which I always relate. + if (!maPxWinSize.Height() || !maPxWinSize.Width()) + maPxWinSize = GetOutputSizePixel(); + + tools::Rectangle aRect(rRenderContext.LogicToPixel(rRect)); + mpPgPreviewLayout->Prepare(1, Point(0,0), maPxWinSize, + mnSttPage, maPaintedPreviewDocRect); + SetSelectedPage(1); + mpPgPreviewLayout->Paint(rRenderContext, rRenderContext.PixelToLogic(aRect)); + SetPagePreview(mnRow, mnCol); + } + else + { + MapMode aMM(rRenderContext.GetMapMode()); + aMM.SetScaleX(maScale); + aMM.SetScaleY(maScale); + rRenderContext.SetMapMode(aMM); + mpPgPreviewLayout->GetParentViewShell().setOutputToWindow(true); + mpPgPreviewLayout->Paint(rRenderContext, rRect); + mpPgPreviewLayout->GetParentViewShell().setOutputToWindow(false); + } +} + +void SwPagePreviewWin::CalcWish( sal_uInt8 nNewRow, sal_uInt8 nNewCol ) +{ + if( !mpViewShell || !mpViewShell->GetLayout() ) + return; + + const sal_uInt8 nOldCol = mnCol; + mnRow = nNewRow; + mnCol = nNewCol; + const sal_uInt16 nPages = mnRow * mnCol; + const sal_uInt16 nLastSttPg = mrView.GetPageCount()+1 > nPages + ? mrView.GetPageCount()+1 - nPages : 0; + if( mnSttPage > nLastSttPg ) + mnSttPage = nLastSttPg; + + mpPgPreviewLayout->Init( mnCol, mnRow, maPxWinSize ); + mpPgPreviewLayout->Prepare( mnSttPage, Point(0,0), maPxWinSize, + mnSttPage, maPaintedPreviewDocRect ); + SetSelectedPage( mnSttPage ); + SetPagePreview(mnRow, mnCol); + maScale = GetMapMode().GetScaleX(); + + // If changes have taken place at the columns, the special case "single column" + // must be considered and corrected if necessary. + if( (1 == nOldCol) != (1 == mnCol) ) + mrView.ScrollDocSzChg(); + + // Order must be maintained! + // additional invalidate page status. + static sal_uInt16 aInval[] = + { + SID_ATTR_ZOOM, SID_ZOOM_OUT, SID_ZOOM_IN, + FN_PREVIEW_ZOOM, + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, FN_PAGEUP, FN_PAGEDOWN, + FN_STAT_PAGE, FN_STAT_ZOOM, + FN_SHOW_TWO_PAGES, FN_SHOW_MULTIPLE_PAGES, + 0 + }; + SfxBindings& rBindings = mrView.GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + rBindings.Update( FN_SHOW_TWO_PAGES ); + rBindings.Update( FN_SHOW_MULTIPLE_PAGES ); + // adjust scrollbars + mrView.ScrollViewSzChg(); +} + +// mnSttPage is Absolute +bool SwPagePreviewWin::MovePage( int eMoveMode ) +{ + // number of pages up + const sal_uInt16 nPages = mnRow * mnCol; + sal_uInt16 nNewSttPage = mnSttPage; + const sal_uInt16 nPageCount = mrView.GetPageCount(); + const sal_uInt16 nDefSttPg = GetDefSttPage(); + bool bPaintPageAtFirstCol = true; + + switch( eMoveMode ) + { + case MV_PAGE_UP: + { + const sal_uInt16 nRelSttPage = mpPgPreviewLayout->ConvertAbsoluteToRelativePageNum( mnSttPage ); + const sal_uInt16 nNewAbsSttPage = nRelSttPage - nPages > 0 ? + mpPgPreviewLayout->ConvertRelativeToAbsolutePageNum( nRelSttPage - nPages ) : + nDefSttPg; + nNewSttPage = nNewAbsSttPage; + + const sal_uInt16 nRelSelPage = mpPgPreviewLayout->ConvertAbsoluteToRelativePageNum( SelectedPage() ); + const sal_uInt16 nNewRelSelPage = nRelSelPage - nPages > 0 ? + nRelSelPage - nPages : + 1; + SetSelectedPage( mpPgPreviewLayout->ConvertRelativeToAbsolutePageNum( nNewRelSelPage ) ); + + break; + } + case MV_PAGE_DOWN: + { + const sal_uInt16 nRelSttPage = mpPgPreviewLayout->ConvertAbsoluteToRelativePageNum( mnSttPage ); + const sal_uInt16 nNewAbsSttPage = mpPgPreviewLayout->ConvertRelativeToAbsolutePageNum( nRelSttPage + nPages ); + nNewSttPage = std::min(nNewAbsSttPage, nPageCount); + + const sal_uInt16 nRelSelPage = mpPgPreviewLayout->ConvertAbsoluteToRelativePageNum( SelectedPage() ); + const sal_uInt16 nNewAbsSelPage = mpPgPreviewLayout->ConvertRelativeToAbsolutePageNum( nRelSelPage + nPages ); + SetSelectedPage( std::min(nNewAbsSelPage, nPageCount) ); + + break; + } + case MV_DOC_STT: + nNewSttPage = nDefSttPg; + SetSelectedPage( mpPgPreviewLayout->ConvertRelativeToAbsolutePageNum( nNewSttPage ? nNewSttPage : 1 ) ); + break; + case MV_DOC_END: + // correct calculation of new start page. + nNewSttPage = nPageCount; + SetSelectedPage( nPageCount ); + break; + + case MV_SELPAGE: + // <nNewSttPage> and <SelectedPage()> are already set. + // not start at first column, only if the + // complete preview layout columns doesn't fit into window. + if ( !mpPgPreviewLayout->DoesPreviewLayoutColsFitIntoWindow() ) + bPaintPageAtFirstCol = false; + break; + case MV_SCROLL: + // check, if paint page at first column + // has to be avoided + if ( !mpPgPreviewLayout->DoesPreviewLayoutRowsFitIntoWindow() || + !mpPgPreviewLayout->DoesPreviewLayoutColsFitIntoWindow() ) + bPaintPageAtFirstCol = false; + break; + case MV_NEWWINSIZE: + // nothing special to do. + break; + case MV_CALC: + // re-init page preview layout. + mpPgPreviewLayout->ReInit(); + + // correct calculation of new start page. + if( nNewSttPage > nPageCount ) + nNewSttPage = nPageCount; + + // correct selected page number + if( SelectedPage() > nPageCount ) + SetSelectedPage( nNewSttPage ? nNewSttPage : 1 ); + } + + mpPgPreviewLayout->Prepare( nNewSttPage, Point(0,0), maPxWinSize, + nNewSttPage, + maPaintedPreviewDocRect, bPaintPageAtFirstCol ); + if( nNewSttPage == mnSttPage && + eMoveMode != MV_SELPAGE ) + return false; + + SetPagePreview(mnRow, mnCol); + mnSttPage = nNewSttPage; + + // additional invalidate page status. + static sal_uInt16 aInval[] = + { + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, FN_PAGEUP, FN_PAGEDOWN, + FN_STAT_PAGE, 0 + }; + + SfxBindings& rBindings = mrView.GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + + return true; +} + +void SwPagePreviewWin::SetWinSize( const Size& rNewSize ) +{ + // We always want the size as pixel units. + maPxWinSize = LogicToPixel( rNewSize ); + + if( USHRT_MAX == mnSttPage ) + { + mnSttPage = GetDefSttPage(); + SetSelectedPage( GetDefSttPage() ); + } + + if ( mbCalcScaleForPreviewLayout ) + { + mpPgPreviewLayout->Init( mnCol, mnRow, maPxWinSize ); + maScale = GetMapMode().GetScaleX(); + } + mpPgPreviewLayout->Prepare( mnSttPage, Point(0,0), maPxWinSize, + mnSttPage, maPaintedPreviewDocRect ); + if ( mbCalcScaleForPreviewLayout ) + { + SetSelectedPage( mnSttPage ); + mbCalcScaleForPreviewLayout = false; + } + SetPagePreview(mnRow, mnCol); + maScale = GetMapMode().GetScaleX(); +} + +OUString SwPagePreviewWin::GetStatusStr( sal_uInt16 nPageCnt ) const +{ + // show physical and virtual page number of + // selected page, if it's visible. + const sal_uInt16 nPageNum = mpPgPreviewLayout->IsPageVisible( mpPgPreviewLayout->SelectedPage() ) + ? mpPgPreviewLayout->SelectedPage() : std::max<sal_uInt16>(mnSttPage, 1); + + OUStringBuffer aStatusStr; + const sal_uInt16 nVirtPageNum = mpPgPreviewLayout->GetVirtPageNumByPageNum( nPageNum ); + if( nVirtPageNum && nVirtPageNum != nPageNum ) + { + aStatusStr.append( OUString::number(nVirtPageNum) ).append( " " ); + } + aStatusStr.append( OUString::number(nPageNum) ).append( " / " ).append( OUString::number(nPageCnt) ); + return aStatusStr.makeStringAndClear(); +} + +void SwPagePreviewWin::KeyInput( const KeyEvent &rKEvt ) +{ + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + bool bHandled = false; + if(!rKeyCode.GetModifier()) + { + sal_uInt16 nSlot = 0; + switch(rKeyCode.GetCode()) + { + case KEY_ADD : nSlot = SID_ZOOM_OUT; break; + case KEY_ESCAPE: nSlot = FN_CLOSE_PAGEPREVIEW; break; + case KEY_SUBTRACT : nSlot = SID_ZOOM_IN; break; + } + if(nSlot) + { + bHandled = true; + mrView.GetViewFrame()->GetDispatcher()->Execute( + nSlot, SfxCallMode::ASYNCHRON ); + } + } + if( !bHandled && !mrView.KeyInput( rKEvt ) ) + Window::KeyInput( rKEvt ); +} + +void SwPagePreviewWin::Command( const CommandEvent& rCEvt ) +{ + bool bCallBase = true; + switch( rCEvt.GetCommand() ) + { + case CommandEventId::ContextMenu: + SfxDispatcher::ExecutePopup(); + bCallBase = false; + break; + + case CommandEventId::Wheel: + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( pData ) + { + const CommandWheelData aDataNew(pData->GetDelta(),pData->GetNotchDelta(),COMMAND_WHEEL_PAGESCROLL, + pData->GetMode(),pData->GetModifier(),pData->IsHorz(), pData->IsDeltaPixel()); + const CommandEvent aEvent( rCEvt.GetMousePosPixel(),rCEvt.GetCommand(),rCEvt.IsMouseEvent(),&aDataNew); + bCallBase = !mrView.HandleWheelCommands( aEvent ); + } + else + bCallBase = !mrView.HandleWheelCommands( rCEvt ); + } + break; + default: + ; + } + + if( bCallBase ) + Window::Command( rCEvt ); +} + +void SwPagePreviewWin::MouseButtonDown( const MouseEvent& rMEvt ) +{ + // consider single-click to set selected page + if( MOUSE_LEFT == ( rMEvt.GetModifier() + rMEvt.GetButtons() ) ) + { + Point aPreviewPos( PixelToLogic( rMEvt.GetPosPixel() ) ); + Point aDocPos; + bool bPosInEmptyPage; + sal_uInt16 nNewSelectedPage; + bool bIsDocPos = + mpPgPreviewLayout->IsPreviewPosInDocPreviewPage( aPreviewPos, + aDocPos, bPosInEmptyPage, nNewSelectedPage ); + if ( bIsDocPos && rMEvt.GetClicks() == 2 ) + { + // close page preview, set new cursor position and switch to + // normal view. + OUString sNewCursorPos = OUString::number( aDocPos.X() ) + ";" + + OUString::number( aDocPos.Y() ) + ";"; + mrView.SetNewCursorPos( sNewCursorPos ); + + SfxViewFrame *pTmpFrame = mrView.GetViewFrame(); + pTmpFrame->GetBindings().Execute( SID_VIEWSHELL0, nullptr, + SfxCallMode::ASYNCHRON ); + } + else if ( bIsDocPos || bPosInEmptyPage ) + { + // show clicked page as the selected one + mpPgPreviewLayout->MarkNewSelectedPage( nNewSelectedPage ); + GetViewShell()->ShowPreviewSelection( nNewSelectedPage ); + // adjust position at vertical scrollbar. + if ( mpPgPreviewLayout->DoesPreviewLayoutRowsFitIntoWindow() ) + { + mrView.SetVScrollbarThumbPos( nNewSelectedPage ); + } + // invalidate page status. + static sal_uInt16 aInval[] = + { + FN_STAT_PAGE, 0 + }; + SfxBindings& rBindings = mrView.GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + } + } +} + +// Set user prefs or view options + +void SwPagePreviewWin::SetPagePreview( sal_uInt8 nRow, sal_uInt8 nCol ) +{ + SwMasterUsrPref *pOpt = const_cast<SwMasterUsrPref *>(SW_MOD()->GetUsrPref(false)); + + if (nRow != pOpt->GetPagePrevRow() || nCol != pOpt->GetPagePrevCol()) + { + pOpt->SetPagePrevRow( nRow ); + pOpt->SetPagePrevCol( nCol ); + pOpt->SetModified(); + + // Update scrollbar! + mrView.ScrollViewSzChg(); + } +} + +/** get selected page in document preview */ +sal_uInt16 SwPagePreviewWin::SelectedPage() const +{ + return mpPgPreviewLayout->SelectedPage(); +} + +/** set selected page number in document preview */ +void SwPagePreviewWin::SetSelectedPage( sal_uInt16 _nSelectedPageNum ) +{ + mpPgPreviewLayout->SetSelectedPage( _nSelectedPageNum ); +} + +/** method to enable/disable book preview */ +bool SwPagePreviewWin::SetBookPreviewMode( const bool _bBookPreview ) +{ + return mpPgPreviewLayout->SetBookPreviewMode( _bBookPreview, + mnSttPage, + maPaintedPreviewDocRect ); +} + +void SwPagePreviewWin::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + switch( rDCEvt.GetType() ) + { + case DataChangedEventType::SETTINGS: + // Rearrange the scrollbars or trigger resize, because the + // size of the scrollbars may have be changed. Also the + // size of the scrollbars has to be retrieved from the settings + // out of the resize handler. + if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE ) + mrView.InvalidateBorder(); // Scrollbar widths + // zoom has to be disabled if Accessibility support is switched on + lcl_InvalidateZoomSlots(mrView.GetViewFrame()->GetBindings()); + break; + + case DataChangedEventType::PRINTER: + case DataChangedEventType::DISPLAY: + case DataChangedEventType::FONTS: + case DataChangedEventType::FONTSUBSTITUTION: + mrView.GetDocShell()->UpdateFontList(); // Font change + mpViewShell->InvalidateLayout(true); + if ( mpViewShell->GetWin() ) + mpViewShell->GetWin()->Invalidate(); + break; + default: break; + } +} + +/** help method to execute SfxRequest FN_PAGEUP and FN_PAGEDOWN */ +void SwPagePreview::ExecPgUpAndPgDown( const bool _bPgUp, + SfxRequest* _pReq ) +{ + SwPagePreviewLayout* pPagePreviewLay = GetViewShell()->PagePreviewLayout(); + // check, if top/bottom of preview is *not* already visible. + if( pPagePreviewLay->GetWinPagesScrollAmount( _bPgUp ? -1 : 1 ) != 0 ) + { + if ( pPagePreviewLay->DoesPreviewLayoutRowsFitIntoWindow() && + pPagePreviewLay->DoesPreviewLayoutColsFitIntoWindow() ) + { + const int eMvMode = _bPgUp ? + SwPagePreviewWin::MV_PAGE_UP : + SwPagePreviewWin::MV_PAGE_DOWN; + if ( ChgPage( eMvMode ) ) + m_pViewWin->Invalidate(); + } + else + { + SwTwips nScrollAmount; + sal_uInt16 nNewSelectedPageNum = 0; + const sal_uInt16 nVisPages = m_pViewWin->GetRow() * m_pViewWin->GetCol(); + if( _bPgUp ) + { + if ( pPagePreviewLay->DoesPreviewLayoutRowsFitIntoWindow() ) + { + nScrollAmount = pPagePreviewLay->GetWinPagesScrollAmount( -1 ); + if ( (m_pViewWin->SelectedPage() - nVisPages) > 0 ) + nNewSelectedPageNum = m_pViewWin->SelectedPage() - nVisPages; + else + nNewSelectedPageNum = 1; + } + else + nScrollAmount = - std::min( m_pViewWin->GetOutputSize().Height(), + m_pViewWin->GetPaintedPreviewDocRect().Top() ); + } + else + { + if ( pPagePreviewLay->DoesPreviewLayoutRowsFitIntoWindow() ) + { + nScrollAmount = pPagePreviewLay->GetWinPagesScrollAmount( 1 ); + if ( (m_pViewWin->SelectedPage() + nVisPages) <= mnPageCount ) + nNewSelectedPageNum = m_pViewWin->SelectedPage() + nVisPages; + else + nNewSelectedPageNum = mnPageCount; + } + else + nScrollAmount = std::min( m_pViewWin->GetOutputSize().Height(), + ( pPagePreviewLay->GetPreviewDocSize().Height() - + m_pViewWin->GetPaintedPreviewDocRect().Bottom() ) ); + } + m_pViewWin->Scroll( 0, nScrollAmount ); + if ( nNewSelectedPageNum != 0 ) + { + m_pViewWin->SetSelectedPage( nNewSelectedPageNum ); + } + ScrollViewSzChg(); + // additional invalidate page status. + static sal_uInt16 aInval[] = + { + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, FN_PAGEUP, FN_PAGEDOWN, + FN_STAT_PAGE, 0 + }; + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + m_pViewWin->Invalidate(); + } + } + + if ( _pReq ) + _pReq->Done(); +} + +// Then all for the SwPagePreview +void SwPagePreview::Execute( SfxRequest &rReq ) +{ + int eMvMode = SwPagePreviewWin::MV_DOC_END; + sal_uInt8 nRow = 1; + bool bRefresh = true; + + switch(rReq.GetSlot()) + { + case FN_REFRESH_VIEW: + case FN_STAT_PAGE: + case FN_STAT_ZOOM: + break; + + case FN_SHOW_MULTIPLE_PAGES: + { + const SfxItemSet *pArgs = rReq.GetArgs(); + if( pArgs && pArgs->Count() >= 2 ) + { + sal_uInt8 nCols = static_cast<sal_uInt8>(pArgs->Get(SID_ATTR_TABLE_COLUMN).GetValue()); + sal_uInt8 nRows = static_cast<sal_uInt8>(pArgs->Get(SID_ATTR_TABLE_ROW).GetValue()); + m_pViewWin->CalcWish( nRows, nCols ); + + } + else + { + SwPreviewZoomDlg aDlg(*m_pViewWin); + aDlg.execute(); + } + } + break; + case FN_SHOW_BOOKVIEW: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + bool bBookPreview = GetViewShell()->GetViewOptions()->IsPagePrevBookview(); + if( pArgs && SfxItemState::SET == pArgs->GetItemState( FN_SHOW_BOOKVIEW, false, &pItem ) ) + { + bBookPreview = static_cast< const SfxBoolItem* >( pItem )->GetValue(); + const_cast<SwViewOption*>(GetViewShell()->GetViewOptions())->SetPagePrevBookview( bBookPreview ); + // cast is not gentleman like, but it's common use in writer and in this case + } + if ( m_pViewWin->SetBookPreviewMode( bBookPreview ) ) + { + // book preview mode changed. Thus, adjust scrollbars and + // invalidate corresponding states. + ScrollViewSzChg(); + static sal_uInt16 aInval[] = + { + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, FN_PAGEUP, FN_PAGEDOWN, + FN_STAT_PAGE, FN_SHOW_BOOKVIEW, 0 + }; + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + m_pViewWin->Invalidate(); + } + + } + break; + case FN_SHOW_TWO_PAGES: + m_pViewWin->CalcWish( nRow, 2 ); + break; + + case FN_SHOW_SINGLE_PAGE: + m_pViewWin->CalcWish( nRow, 1 ); + break; + + case FN_PREVIEW_ZOOM: + case SID_ATTR_ZOOM: + { + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + ScopedVclPtr<AbstractSvxZoomDialog> pDlg; + if(!pArgs) + { + SfxItemSet aCoreSet(GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{}); + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + SvxZoomItem aZoom( pVOpt->GetZoomType(), pVOpt->GetZoom() ); + aZoom.SetValueSet( + SvxZoomEnableFlags::N50| + SvxZoomEnableFlags::N75| + SvxZoomEnableFlags::N100| + SvxZoomEnableFlags::N150| + SvxZoomEnableFlags::N200| + SvxZoomEnableFlags::WHOLEPAGE); + aCoreSet.Put( aZoom ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aCoreSet)); + pDlg->SetLimits( MINZOOM, MAXZOOM ); + + if( pDlg->Execute() != RET_CANCEL ) + pArgs = pDlg->GetOutputItemSet(); + } + if( pArgs ) + { + SvxZoomType eType = SvxZoomType::PERCENT; + sal_uInt16 nZoomFactor = USHRT_MAX; + if(SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOM, true, &pItem)) + { + eType = static_cast<const SvxZoomItem *>(pItem)->GetType(); + nZoomFactor = static_cast<const SvxZoomItem *>(pItem)->GetValue(); + } + else if(SfxItemState::SET == pArgs->GetItemState(FN_PREVIEW_ZOOM, true, &pItem)) + nZoomFactor = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + if(USHRT_MAX != nZoomFactor) + SetZoom(eType, nZoomFactor); + } + } + break; + case SID_ATTR_ZOOMSLIDER : + { + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) ) + { + const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue(); + SetZoom( SvxZoomType::PERCENT, nCurrentZoom ); + } + } + break; + case SID_ZOOM_IN: + case SID_ZOOM_OUT: + { + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + SetZoom(SvxZoomType::PERCENT, + lcl_GetNextZoomStep(pVOpt->GetZoom(), SID_ZOOM_IN == rReq.GetSlot())); + } + break; + case FN_CHAR_LEFT: + case FN_CHAR_RIGHT: + case FN_LINE_UP: + case FN_LINE_DOWN: + { + SwPagePreviewLayout* pPagePreviewLay = GetViewShell()->PagePreviewLayout(); + sal_uInt16 nNewSelectedPage; + sal_uInt16 nNewStartPage; + Point aNewStartPos; + sal_Int16 nHoriMove = 0; + sal_Int16 nVertMove = 0; + switch(rReq.GetSlot()) + { + case FN_CHAR_LEFT: nHoriMove = -1; break; + case FN_CHAR_RIGHT: nHoriMove = 1; break; + case FN_LINE_UP: nVertMove = -1; break; + case FN_LINE_DOWN: nVertMove = 1; break; + } + pPagePreviewLay->CalcStartValuesForSelectedPageMove( nHoriMove, nVertMove, + nNewSelectedPage, nNewStartPage, aNewStartPos ); + if ( m_pViewWin->SelectedPage() != nNewSelectedPage ) + { + if ( pPagePreviewLay->IsPageVisible( nNewSelectedPage ) ) + { + pPagePreviewLay->MarkNewSelectedPage( nNewSelectedPage ); + // adjust position at vertical scrollbar. + SetVScrollbarThumbPos( nNewSelectedPage ); + bRefresh = false; + } + else + { + m_pViewWin->SetSelectedPage( nNewSelectedPage ); + m_pViewWin->SetSttPage( nNewStartPage ); + bRefresh = ChgPage( SwPagePreviewWin::MV_SELPAGE ); + } + GetViewShell()->ShowPreviewSelection( nNewSelectedPage ); + // invalidate page status. + static sal_uInt16 aInval[] = + { + FN_STAT_PAGE, 0 + }; + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + rReq.Done(); + } + else + { + bRefresh = false; + } + break; + } + case FN_PAGEUP: + case FN_PAGEDOWN: + { + ExecPgUpAndPgDown( rReq.GetSlot() == FN_PAGEUP, &rReq ); + break; + } + case SID_JUMP_TO_SPECIFIC_PAGE: + { + sal_uInt16 nPageNum = 1; + const SfxItemSet *pArgs = rReq.GetArgs(); + if( pArgs && pArgs->Count()) + { + nPageNum = static_cast<const SfxUInt16Item &>(pArgs->Get(SID_JUMP_TO_SPECIFIC_PAGE)).GetValue(); + + if( nPageNum > 0 && nPageNum <= mnPageCount ) + { + m_pViewWin->SetSttPage( nPageNum); + m_pViewWin->SetSelectedPage( nPageNum ); + ChgPage( SwPagePreviewWin::MV_SPECIFIC_PAGE, false ); + ScrollViewSzChg(); + } + } + } + break; + case FN_START_OF_LINE: + case FN_START_OF_DOCUMENT: + eMvMode = SwPagePreviewWin::MV_DOC_STT; + [[fallthrough]]; + case FN_END_OF_LINE: + case FN_END_OF_DOCUMENT: + m_pViewWin->SetSelectedPage(eMvMode == SwPagePreviewWin::MV_DOC_STT ? 1 : mnPageCount); + { + bool bRet = ChgPage( eMvMode ); + // return value for Basic + rReq.SetReturnValue(SfxBoolItem(rReq.GetSlot(), !bRet)); + + bRefresh = bRet; + rReq.Done(); + } + break; + + case FN_PRINT_PAGEPREVIEW: + { + const SwPagePreviewPrtData* pPPVPD = m_pViewWin->GetViewShell()->GetDoc()->GetPreviewPrtData(); + // The thing with the orientation + if(pPPVPD) + { + SfxPrinter* pPrinter = GetPrinter( true ); + if((pPrinter->GetOrientation() == Orientation::Landscape) + != pPPVPD->GetLandscape()) + pPrinter->SetOrientation(pPPVPD->GetLandscape() ? Orientation::Landscape : Orientation::Portrait); + } + ::SetAppPrintOptions( m_pViewWin->GetViewShell(), false ); + m_bNormalPrint = false; + rReq.SetSlot( SID_PRINTDOC ); + SfxViewShell::ExecuteSlot( rReq, SfxViewShell::GetInterface() ); + rReq.SetSlot( FN_PRINT_PAGEPREVIEW ); + return; + } + case SID_PRINTDOCDIRECT: + case SID_PRINTDOC: + ::SetAppPrintOptions( m_pViewWin->GetViewShell(), false ); + m_bNormalPrint = true; + SfxViewShell::ExecuteSlot( rReq, SfxViewShell::GetInterface() ); + return; + case FN_CLOSE_PAGEPREVIEW: + case SID_PRINTPREVIEW: + // print preview is now always in the same frame as the tab view + // -> always switch this frame back to normal view + // (ScTabViewShell ctor reads stored view data) + GetViewFrame()->GetDispatcher()->Execute( SID_VIEWSHELL0, SfxCallMode::ASYNCHRON ); + break; + case FN_INSERT_BREAK: + { + sal_uInt16 nSelPage = m_pViewWin->SelectedPage(); + //if a dummy page is selected (e.g. a non-existing right/left page) + //the direct neighbor is used + if(GetViewShell()->IsDummyPage( nSelPage ) && GetViewShell()->IsDummyPage( --nSelPage )) + nSelPage +=2; + m_nNewPage = nSelPage; + SfxViewFrame *pTmpFrame = GetViewFrame(); + pTmpFrame->GetBindings().Execute( SID_VIEWSHELL0, nullptr, + SfxCallMode::ASYNCHRON ); + } + break; + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } + + if( bRefresh ) + m_pViewWin->Invalidate(); +} + +void SwPagePreview::GetState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + OSL_ENSURE(nWhich, "empty set"); + SwPagePreviewLayout* pPagePreviewLay = GetViewShell()->PagePreviewLayout(); + + while(nWhich) + { + switch(nWhich) + { + case SID_BROWSER_MODE: + case FN_PRINT_LAYOUT: + rSet.DisableItem(nWhich); + break; + case FN_START_OF_DOCUMENT: + { + if ( pPagePreviewLay->IsPageVisible( 1 ) ) + rSet.DisableItem(nWhich); + break; + } + case FN_END_OF_DOCUMENT: + { + if ( pPagePreviewLay->IsPageVisible( mnPageCount ) ) + rSet.DisableItem(nWhich); + break; + } + case FN_PAGEUP: + { + if( pPagePreviewLay->GetWinPagesScrollAmount( -1 ) == 0 ) + rSet.DisableItem(nWhich); + break; + } + case FN_PAGEDOWN: + { + if( pPagePreviewLay->GetWinPagesScrollAmount( 1 ) == 0 ) + rSet.DisableItem(nWhich); + break; + } + + case FN_STAT_PAGE: + { + OUString aStr = m_sPageStr + m_pViewWin->GetStatusStr( mnPageCount ); + rSet.Put( SfxStringItem( nWhich, aStr) ); + } + break; + + case SID_ATTR_ZOOM: + case FN_STAT_ZOOM: + { + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + SvxZoomItem aZoom(pVOpt->GetZoomType(), pVOpt->GetZoom()); + aZoom.SetValueSet( + SvxZoomEnableFlags::N50| + SvxZoomEnableFlags::N75| + SvxZoomEnableFlags::N100| + SvxZoomEnableFlags::N150| + SvxZoomEnableFlags::N200); + rSet.Put( aZoom ); + } + break; + case SID_ATTR_ZOOMSLIDER : + { + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + const sal_uInt16 nCurrentZoom = pVOpt->GetZoom(); + SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM ); + aZoomSliderItem.AddSnappingPoint( 100 ); + rSet.Put( aZoomSliderItem ); + } + break; + case FN_PREVIEW_ZOOM: + { + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + rSet.Put(SfxUInt16Item(nWhich, pVOpt->GetZoom())); + } + break; + case SID_ZOOM_IN: + case SID_ZOOM_OUT: + { + const SwViewOption* pVOpt = GetViewShell()->GetViewOptions(); + if((SID_ZOOM_OUT == nWhich && pVOpt->GetZoom() >= MAX_PREVIEW_ZOOM)|| + (SID_ZOOM_IN == nWhich && pVOpt->GetZoom() <= MIN_PREVIEW_ZOOM)) + { + rSet.DisableItem(nWhich); + } + } + break; + case FN_SHOW_MULTIPLE_PAGES: + // should never be disabled + break; + case FN_SHOW_BOOKVIEW: + { + bool b = GetViewShell()->GetViewOptions()->IsPagePrevBookview(); + rSet.Put(SfxBoolItem(nWhich, b)); + } + break; + + case FN_SHOW_TWO_PAGES: + if( 2 == m_pViewWin->GetCol() && 1 == m_pViewWin->GetRow() ) + rSet.DisableItem( nWhich ); + break; + + case FN_PRINT_PAGEPREVIEW: + // has the same status like the normal printing + { + const SfxPoolItem* pItem; + SfxItemSet aSet( *rSet.GetPool(), svl::Items<SID_PRINTDOC, SID_PRINTDOC>{} ); + GetSlotState( SID_PRINTDOC, SfxViewShell::GetInterface(), &aSet ); + if( SfxItemState::DISABLED == aSet.GetItemState( SID_PRINTDOC, + false, &pItem )) + rSet.DisableItem( nWhich ); + else if( SfxItemState::SET == aSet.GetItemState( SID_PRINTDOC, + false, &pItem )) + { + const_cast<SfxPoolItem*>(pItem)->SetWhich( FN_PRINT_PAGEPREVIEW ); + rSet.Put( *pItem ); + } + } + break; + + case SID_PRINTPREVIEW: + rSet.Put( SfxBoolItem( nWhich, true ) ); + break; + + case SID_PRINTDOC: + case SID_PRINTDOCDIRECT: + GetSlotState( nWhich, SfxViewShell::GetInterface(), &rSet ); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SwPagePreview::StateUndo(SfxItemSet& rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while (nWhich) + { + rSet.DisableItem(nWhich); + nWhich = aIter.NextWhich(); + } +} + +void SwPagePreview::Init() +{ + if ( GetViewShell()->HasDrawView() ) + GetViewShell()->GetDrawView()->SetAnimationEnabled( false ); + + m_bNormalPrint = true; + + // Check and process the DocSize. The shell could not be found via + // the handler, because the shell is unknown to the SFX management + // within the CTOR phase. + + const SwViewOption * pPrefs = SW_MOD()->GetUsrPref(false); + + mbHScrollbarEnabled = pPrefs->IsViewHScrollBar(); + mbVScrollbarEnabled = pPrefs->IsViewVScrollBar(); + + // Update the fields + // ATTENTION: Do cast the EditShell up, to use the SS. + // At the methods the current shell will be queried! + SwEditShell* pESh = dynamic_cast<SwEditShell*>(GetViewShell()); + bool bIsModified = pESh != nullptr && pESh->IsModified(); + + SwViewOption aOpt( *pPrefs ); + aOpt.SetPagePreview(true); + aOpt.SetTab( false ); + aOpt.SetBlank( false ); + aOpt.SetHardBlank( false ); + aOpt.SetParagraph( false ); + aOpt.SetLineBreak( false ); + aOpt.SetPageBreak( false ); + aOpt.SetColumnBreak( false ); + aOpt.SetSoftHyph( false ); + aOpt.SetFieldName( false ); + aOpt.SetPostIts( false ); + aOpt.SetShowBookmarks( false ); + aOpt.SetShowHiddenChar( false ); + aOpt.SetShowHiddenField( false ); + aOpt.SetShowHiddenPara( false ); + aOpt.SetViewHRuler( false ); + aOpt.SetViewVRuler( false ); + aOpt.SetGraphic( true ); + aOpt.SetTable( true ); + aOpt.SetSnap( false ); + aOpt.SetGridVisible( false ); + aOpt.SetOnlineSpell( false ); + aOpt.SetHideWhitespaceMode( false ); + + GetViewShell()->ApplyViewOptions( aOpt ); + GetViewShell()->ApplyAccessibilityOptions(SW_MOD()->GetAccessibilityOptions()); + + // adjust view shell option to the same as for print + SwPrintData const aPrintOptions = *SW_MOD()->GetPrtOptions(false); + GetViewShell()->AdjustOptionsForPagePreview( aPrintOptions ); + + GetViewShell()->CalcLayout(); + DocSzChgd( GetViewShell()->GetDocSize() ); + + if( !bIsModified && pESh != nullptr ) + pESh->ResetModified(); +} + +SwPagePreview::SwPagePreview(SfxViewFrame *pViewFrame, SfxViewShell* pOldSh): + SfxViewShell( pViewFrame, SWVIEWFLAGS ), + m_pViewWin( VclPtr<SwPagePreviewWin>::Create(&GetViewFrame()->GetWindow(), *this ) ), + m_nNewPage(USHRT_MAX), + m_sPageStr(SwResId(STR_PAGE)), + m_pHScrollbar(nullptr), + m_pVScrollbar(nullptr), + m_pScrollFill(VclPtr<ScrollBarBox>::Create( &pViewFrame->GetWindow(), WB_SIZEABLE )), + mnPageCount( 0 ), + mbResetFormDesignMode( false ), + mbFormDesignModeToReset( false ) +{ + SetName("PageView"); + SetWindow( m_pViewWin ); + CreateScrollbar( true ); + CreateScrollbar( false ); + + //notify notebookbar change in context + SfxShell::SetContextBroadcasterEnabled(true); + SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Printpreview)); + SfxShell::BroadcastContextForActivation(true); + //removelisteners for notebookbar + if (auto& pBar = SfxViewFrame::Current()->GetWindow().GetSystemWindow()->GetNotebookBar()) + pBar->ControlListenerForCurrentController(false); + + SfxObjectShell* pObjShell = pViewFrame->GetObjectShell(); + if ( !pOldSh ) + { + // Exists already a view on the document? + SfxViewFrame *pF = SfxViewFrame::GetFirst( pObjShell ); + if ( pF == pViewFrame ) + pF = SfxViewFrame::GetNext( *pF, pObjShell ); + if ( pF ) + pOldSh = pF->GetViewShell(); + } + + SwViewShell *pVS, *pNew; + + if (SwPagePreview* pPagePreview = dynamic_cast<SwPagePreview*>(pOldSh)) + pVS = pPagePreview->GetViewShell(); + else + { + if (SwView* pView = dynamic_cast<SwView *>(pOldSh)) + { + pVS = pView->GetWrtShellPtr(); + // save the current ViewData of the previous SwView + pOldSh->WriteUserData( m_sSwViewData ); + } + else + pVS = GetDocShell()->GetWrtShell(); + if( pVS ) + { + // Set the current page as the first. + sal_uInt16 nPhysPg, nVirtPg; + static_cast<SwCursorShell*>(pVS)->GetPageNum( nPhysPg, nVirtPg, true, false ); + if( 1 != m_pViewWin->GetCol() && 1 == nPhysPg ) + --nPhysPg; + m_pViewWin->SetSttPage( nPhysPg ); + } + } + + // for form shell remember design mode of draw view + // of previous view shell + if ( pVS && pVS->HasDrawView() ) + { + mbResetFormDesignMode = true; + mbFormDesignModeToReset = pVS->GetDrawView()->IsDesignMode(); + } + + if( pVS ) + pNew = new SwViewShell( *pVS, m_pViewWin, nullptr, VSHELLFLAG_ISPREVIEW ); + else + pNew = new SwViewShell( + *static_cast<SwDocShell*>(pViewFrame->GetObjectShell())->GetDoc(), + m_pViewWin, nullptr, nullptr, VSHELLFLAG_ISPREVIEW ); + + m_pViewWin->SetViewShell( pNew ); + pNew->SetSfxViewShell( this ); + Init(); +} + +SwPagePreview::~SwPagePreview() +{ + SetWindow( nullptr ); + SwViewShell* pVShell = m_pViewWin->GetViewShell(); + pVShell->SetWin(nullptr); + delete pVShell; + + m_pViewWin.disposeAndClear(); + if (SfxViewFrame* pCurrent = SfxViewFrame::Current()) + if (auto& pBar = pCurrent->GetWindow().GetSystemWindow()->GetNotebookBar()) + pBar->ControlListenerForCurrentController(true); // start listening now + m_pScrollFill.disposeAndClear(); + m_pHScrollbar.disposeAndClear(); + m_pVScrollbar.disposeAndClear(); +} + +SwDocShell* SwPagePreview::GetDocShell() +{ + return dynamic_cast<SwDocShell*>( GetViewFrame()->GetObjectShell() ); +} + +void SwPagePreview::CreateScrollbar( bool bHori ) +{ + vcl::Window *pMDI = &GetViewFrame()->GetWindow(); + VclPtr<SwScrollbar>& ppScrollbar = bHori ? m_pHScrollbar : m_pVScrollbar; + + assert(!ppScrollbar); //check beforehand! + + ppScrollbar = VclPtr<SwScrollbar>::Create( pMDI, bHori ); + + ScrollDocSzChg(); + ppScrollbar->EnableDrag(); + ppScrollbar->SetEndScrollHdl( LINK( this, SwPagePreview, EndScrollHdl )); + + ppScrollbar->SetScrollHdl( LINK( this, SwPagePreview, ScrollHdl )); + + InvalidateBorder(); + ppScrollbar->ExtendedShow(); +} + +bool SwPagePreview::ChgPage( int eMvMode, bool bUpdateScrollbar ) +{ + tools::Rectangle aPixVisArea( m_pViewWin->LogicToPixel( m_aVisArea ) ); + bool bChg = m_pViewWin->MovePage( eMvMode ) || + eMvMode == SwPagePreviewWin::MV_CALC || + eMvMode == SwPagePreviewWin::MV_NEWWINSIZE; + m_aVisArea = m_pViewWin->PixelToLogic( aPixVisArea ); + + if( bChg ) + { + // Update statusbar + OUString aStr = m_sPageStr + m_pViewWin->GetStatusStr( mnPageCount ); + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + + if( bUpdateScrollbar ) + { + ScrollViewSzChg(); + + static sal_uInt16 aInval[] = + { + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, + FN_PAGEUP, FN_PAGEDOWN, 0 + }; + rBindings.Invalidate( aInval ); + } + rBindings.SetState( SfxStringItem( FN_STAT_PAGE, aStr ) ); + } + return bChg; +} + +// From here, everything was taken from the SwView. +void SwPagePreview::CalcAndSetBorderPixel( SvBorder &rToFill ) +{ + const StyleSettings &rSet = m_pViewWin->GetSettings().GetStyleSettings(); + const long nTmp = rSet.GetScrollBarSize(); + if ( m_pVScrollbar->IsVisible( true ) ) + rToFill.Right() = nTmp; + if ( m_pHScrollbar->IsVisible( true ) ) + rToFill.Bottom() = nTmp; + SetBorderPixel( rToFill ); +} + +void SwPagePreview::InnerResizePixel( const Point &rOfst, const Size &rSize, bool ) +{ + SvBorder aBorder; + CalcAndSetBorderPixel( aBorder ); + tools::Rectangle aRect( rOfst, rSize ); + aRect += aBorder; + ViewResizePixel( *m_pViewWin, aRect.TopLeft(), aRect.GetSize(), + m_pViewWin->GetOutputSizePixel(), + *m_pVScrollbar, *m_pHScrollbar, *m_pScrollFill ); + + // Never set EditWin ! + // Never set VisArea ! +} + +void SwPagePreview::OuterResizePixel( const Point &rOfst, const Size &rSize ) +{ + SvBorder aBorder; + CalcAndSetBorderPixel( aBorder ); + + // Never set EditWin ! + + Size aTmpSize( m_pViewWin->GetOutputSizePixel() ); + Point aBottomRight( m_pViewWin->PixelToLogic( Point( aTmpSize.Width(), aTmpSize.Height() ) ) ); + SetVisArea( tools::Rectangle( Point(), aBottomRight ) ); + + // Call of the DocSzChgd-Method of the scrollbars is necessary, + // because from the maximum scroll range half the height of the + // VisArea is always deducted. + if ( m_pVScrollbar && !aTmpSize.IsEmpty() ) + { + ScrollDocSzChg(); + } + + SvBorder aBorderNew; + CalcAndSetBorderPixel( aBorderNew ); + ViewResizePixel( *m_pViewWin, rOfst, rSize, m_pViewWin->GetOutputSizePixel(), + *m_pVScrollbar, *m_pHScrollbar, *m_pScrollFill ); +} + +void SwPagePreview::SetVisArea( const tools::Rectangle &rRect ) +{ + const Point aTopLeft(AlignToPixel(rRect.TopLeft())); + const Point aBottomRight(AlignToPixel(rRect.BottomRight())); + tools::Rectangle aLR(aTopLeft,aBottomRight); + + if(aLR == m_aVisArea) + return; + // No negative position, no negative size + + if(aLR.Top() < 0) + { + aLR.AdjustBottom(std::abs(aLR.Top()) ); + aLR.SetTop( 0 ); + } + + if(aLR.Left() < 0) + { + aLR.AdjustRight(std::abs(aLR.Left()) ); + aLR.SetLeft( 0 ); + } + if(aLR.Right() < 0) aLR.SetRight( 0 ); + if(aLR.Bottom() < 0) aLR.SetBottom( 0 ); + if(aLR == m_aVisArea || + // Ignore empty rectangle + ( 0 == aLR.Bottom() - aLR.Top() && 0 == aLR.Right() - aLR.Left() ) ) + return; + + if( aLR.Left() > aLR.Right() || aLR.Top() > aLR.Bottom() ) + return; + + // Before the data can be changed call an update if necessary. + // Thereby ensured, that adjacent paints are correctly converted into + // document coordinates. + // As a precaution, we do this only when at the shell runs an action, + // because then we do not really paint but the rectangles are just + // bookmarked (in document coordinates). + if( GetViewShell()->ActionPend() ) + m_pViewWin->PaintImmediately(); + + // Set at View-Win the current size + m_aVisArea = aLR; + m_pViewWin->SetWinSize( aLR.GetSize() ); + ChgPage( SwPagePreviewWin::MV_NEWWINSIZE ); + + m_pViewWin->Invalidate(); +} + +IMPL_LINK( SwPagePreview, ScrollHdl, ScrollBar *, p, void ) +{ + SwScrollbar* pScrollbar = static_cast<SwScrollbar*>(p); + if(!GetViewShell()) + return; + if( !pScrollbar->IsHoriScroll() && + pScrollbar->GetType() == ScrollType::Drag && + Help::IsQuickHelpEnabled() && + GetViewShell()->PagePreviewLayout()->DoesPreviewLayoutRowsFitIntoWindow()) + { + // Scroll how many pages?? + OUString sStateStr(m_sPageStr); + long nThmbPos = pScrollbar->GetThumbPos(); + if( 1 == m_pViewWin->GetCol() || !nThmbPos ) + ++nThmbPos; + sStateStr += OUString::number( nThmbPos ); + Point aPos = pScrollbar->GetParent()->OutputToScreenPixel( + pScrollbar->GetPosPixel()); + aPos.setY( pScrollbar->OutputToScreenPixel(pScrollbar->GetPointerPosPixel()).Y() ); + tools::Rectangle aRect; + aRect.SetLeft( aPos.X() -8 ); + aRect.SetRight( aRect.Left() ); + aRect.SetTop( aPos.Y() ); + aRect.SetBottom( aRect.Top() ); + + Help::ShowQuickHelp(pScrollbar, aRect, sStateStr, + QuickHelpFlags::Right|QuickHelpFlags::VCenter); + + } + else + EndScrollHdl( pScrollbar ); +} + +IMPL_LINK( SwPagePreview, EndScrollHdl, ScrollBar *, p, void ) +{ + SwScrollbar* pScrollbar = static_cast<SwScrollbar*>(p); + if(!GetViewShell()) + return; + + // boolean to avoid unnecessary invalidation of the window. + bool bInvalidateWin = true; + + if( !pScrollbar->IsHoriScroll() ) // scroll vertically + { + if ( Help::IsQuickHelpEnabled() ) + Help::ShowQuickHelp(pScrollbar, tools::Rectangle(), OUString()); + if ( GetViewShell()->PagePreviewLayout()->DoesPreviewLayoutRowsFitIntoWindow() ) + { + // Scroll how many pages ?? + const sal_uInt16 nThmbPos = static_cast<sal_uInt16>(pScrollbar->GetThumbPos()); + // adjust to new preview functionality + if( nThmbPos != m_pViewWin->SelectedPage() ) + { + // consider case that page <nThmbPos> + // is already visible + SwPagePreviewLayout* pPagePreviewLay = GetViewShell()->PagePreviewLayout(); + if ( pPagePreviewLay->IsPageVisible( nThmbPos ) ) + { + pPagePreviewLay->MarkNewSelectedPage( nThmbPos ); + // invalidation of window is unnecessary + bInvalidateWin = false; + } + else + { + // consider whether layout columns + // fit or not. + if ( !pPagePreviewLay->DoesPreviewLayoutColsFitIntoWindow() ) + { + m_pViewWin->SetSttPage( nThmbPos ); + m_pViewWin->SetSelectedPage( nThmbPos ); + ChgPage( SwPagePreviewWin::MV_SCROLL, false ); + // update scrollbars + ScrollViewSzChg(); + } + else + { + // correct scroll amount + const sal_Int16 nPageDiff = nThmbPos - m_pViewWin->SelectedPage(); + const sal_uInt16 nVisPages = m_pViewWin->GetRow() * m_pViewWin->GetCol(); + sal_Int16 nWinPagesToScroll = nPageDiff / nVisPages; + if ( nPageDiff % nVisPages ) + { + // decrease/increase number of preview pages to scroll + nPageDiff < 0 ? --nWinPagesToScroll : ++nWinPagesToScroll; + } + m_pViewWin->SetSelectedPage( nThmbPos ); + m_pViewWin->Scroll( 0, pPagePreviewLay->GetWinPagesScrollAmount( nWinPagesToScroll ) ); + } + } + // update accessibility + GetViewShell()->ShowPreviewSelection( nThmbPos ); + } + else + { + // invalidation of window is unnecessary + bInvalidateWin = false; + } + } + else + { + long nThmbPos = pScrollbar->GetThumbPos(); + m_pViewWin->Scroll(0, nThmbPos - m_pViewWin->GetPaintedPreviewDocRect().Top()); + } + } + else + { + long nThmbPos = pScrollbar->GetThumbPos(); + m_pViewWin->Scroll(nThmbPos - m_pViewWin->GetPaintedPreviewDocRect().Left(), 0); + } + // additional invalidate page status. + static sal_uInt16 aInval[] = + { + FN_START_OF_DOCUMENT, FN_END_OF_DOCUMENT, FN_PAGEUP, FN_PAGEDOWN, + FN_STAT_PAGE, 0 + }; + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( aInval ); + // control invalidation of window + if ( bInvalidateWin ) + { + m_pViewWin->Invalidate(); + } +} + +Point SwPagePreview::AlignToPixel(const Point &rPt) const +{ + return m_pViewWin->PixelToLogic( m_pViewWin->LogicToPixel( rPt ) ); +} + +void SwPagePreview::DocSzChgd( const Size &rSz ) +{ + if( m_aDocSize == rSz ) + return; + + m_aDocSize = rSz; + + // #i96726# + // Due to the multiple page layout it is needed to trigger recalculation + // of the page preview layout, even if the count of pages is not changing. + mnPageCount = GetViewShell()->GetNumPages(); + + if( m_aVisArea.GetWidth() ) + { + ChgPage( SwPagePreviewWin::MV_CALC ); + ScrollDocSzChg(); + + m_pViewWin->Invalidate(); + } +} + +void SwPagePreview::ScrollViewSzChg() +{ + if(!GetViewShell()) + return ; + + bool bShowVScrollbar = false, bShowHScrollbar = false; + + if(m_pVScrollbar) + { + if(GetViewShell()->PagePreviewLayout()->DoesPreviewLayoutRowsFitIntoWindow()) + { + //vertical scrolling by row + // adjust to new preview functionality + const sal_uInt16 nVisPages = m_pViewWin->GetRow() * m_pViewWin->GetCol(); + + m_pVScrollbar->SetVisibleSize( nVisPages ); + // set selected page as scroll bar position, + // if it is visible. + SwPagePreviewLayout* pPagePreviewLay = GetViewShell()->PagePreviewLayout(); + if ( pPagePreviewLay->IsPageVisible( m_pViewWin->SelectedPage() ) ) + { + m_pVScrollbar->SetThumbPos( m_pViewWin->SelectedPage() ); + } + else + { + m_pVScrollbar->SetThumbPos( m_pViewWin->GetSttPage() ); + } + m_pVScrollbar->SetLineSize( m_pViewWin->GetCol() ); + m_pVScrollbar->SetPageSize( nVisPages ); + // calculate and set scrollbar range + Range aScrollbarRange( 1, mnPageCount ); + // increase range by one, because left-top-corner is left blank. + ++aScrollbarRange.Max(); + // increase range in order to access all pages + aScrollbarRange.Max() += ( nVisPages - 1 ); + m_pVScrollbar->SetRange( aScrollbarRange ); + + bShowVScrollbar = nVisPages < mnPageCount; + } + else //vertical scrolling by pixel + { + const tools::Rectangle& rDocRect = m_pViewWin->GetPaintedPreviewDocRect(); + const Size& rPreviewSize = + GetViewShell()->PagePreviewLayout()->GetPreviewDocSize(); + m_pVScrollbar->SetRangeMax(rPreviewSize.Height()) ; + long nVisHeight = rDocRect.GetHeight(); + m_pVScrollbar->SetVisibleSize( nVisHeight ); + m_pVScrollbar->SetThumbPos( rDocRect.Top() ); + m_pVScrollbar->SetLineSize( nVisHeight / 10 ); + m_pVScrollbar->SetPageSize( nVisHeight / 2 ); + + bShowVScrollbar = true; + } + + if (!mbVScrollbarEnabled) + bShowVScrollbar = false; + + ShowVScrollbar(bShowVScrollbar); + } + if(m_pHScrollbar) + { + const tools::Rectangle& rDocRect = m_pViewWin->GetPaintedPreviewDocRect(); + const Size& rPreviewSize = + GetViewShell()->PagePreviewLayout()->GetPreviewDocSize(); + Range aRange(0,0); + + if(rDocRect.GetWidth() < rPreviewSize.Width()) + { + bShowHScrollbar = true; + + long nVisWidth = rDocRect.GetWidth(); + long nThumb = rDocRect.Left(); + aRange = Range(0, rPreviewSize.Width()); + + m_pHScrollbar->SetRange( aRange ); + m_pHScrollbar->SetVisibleSize( nVisWidth ); + m_pHScrollbar->SetThumbPos( nThumb ); + m_pHScrollbar->SetLineSize( nVisWidth / 10 ); + m_pHScrollbar->SetPageSize( nVisWidth / 2 ); + } + + if (!mbHScrollbarEnabled) + bShowHScrollbar = false; + + ShowHScrollbar(bShowHScrollbar); + } + m_pScrollFill->Show(bShowVScrollbar && bShowHScrollbar); +} + +void SwPagePreview::ScrollDocSzChg() +{ + ScrollViewSzChg(); +} + +// All about printing +SfxPrinter* SwPagePreview::GetPrinter( bool bCreate ) +{ + return m_pViewWin->GetViewShell()->getIDocumentDeviceAccess().getPrinter( bCreate ); +} + +sal_uInt16 SwPagePreview::SetPrinter( SfxPrinter *pNew, SfxPrinterChangeFlags nDiffFlags ) +{ + SwViewShell &rSh = *GetViewShell(); + SfxPrinter* pOld = rSh.getIDocumentDeviceAccess().getPrinter( false ); + if ( pOld && pOld->IsPrinting() ) + return SFX_PRINTERROR_BUSY; + + SwEditShell &rESh = static_cast<SwEditShell&>(rSh); //Buh... + if( ( SfxPrinterChangeFlags::PRINTER | SfxPrinterChangeFlags::JOBSETUP ) & nDiffFlags ) + { + rSh.getIDocumentDeviceAccess().setPrinter( pNew, true, true ); + if( nDiffFlags & SfxPrinterChangeFlags::PRINTER ) + rESh.SetModified(); + } + if ( ( nDiffFlags & SfxPrinterChangeFlags::OPTIONS ) == SfxPrinterChangeFlags::OPTIONS ) + ::SetPrinter( &rSh.getIDocumentDeviceAccess(), pNew, false ); + + const bool bChgOri = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION); + const bool bChgSize = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE); + if ( bChgOri || bChgSize ) + { + rESh.StartAllAction(); + if ( bChgOri ) + rSh.ChgAllPageOrientation( pNew->GetOrientation() ); + if ( bChgSize ) + { + Size aSz( SvxPaperInfo::GetPaperSize( pNew ) ); + rSh.ChgAllPageSize( aSz ); + } + if( !m_bNormalPrint ) + m_pViewWin->CalcWish( m_pViewWin->GetRow(), m_pViewWin->GetCol() ); + rESh.SetModified(); + rESh.EndAllAction(); + + static sal_uInt16 aInval[] = + { + SID_ATTR_LONG_ULSPACE, SID_ATTR_LONG_LRSPACE, + SID_RULER_BORDERS, SID_RULER_PAGE_POS, 0 + }; +#if OSL_DEBUG_LEVEL > 0 + { + const sal_uInt16* pPtr = aInval + 1; + do { + OSL_ENSURE( *(pPtr - 1) < *pPtr, "wrong sorting!" ); + } while( *++pPtr ); + } +#endif + + GetViewFrame()->GetBindings().Invalidate(aInval); + } + + return 0; +} + +bool SwPagePreview::HasPrintOptionsPage() const +{ + return true; +} + +std::unique_ptr<SfxTabPage> SwPagePreview::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet &rOptions) +{ + return ::CreatePrintOptionsPage(pPage, pController, rOptions, !m_bNormalPrint); +} + +void SwPagePreviewWin::SetViewShell( SwViewShell* pShell ) +{ + mpViewShell = pShell; + if ( mpViewShell && mpViewShell->IsPreview() ) + { + mpPgPreviewLayout = mpViewShell->PagePreviewLayout(); + } +} + +void SwPagePreviewWin::RepaintCoreRect( const SwRect& rRect ) +{ + // #i24183# + if ( mpPgPreviewLayout->PreviewLayoutValid() ) + { + mpPgPreviewLayout->Repaint( tools::Rectangle( rRect.Pos(), rRect.SSize() ) ); + } +} + +/** method to adjust preview to a new zoom factor + + #i19975# also consider zoom type - adding parameter <_eZoomType> +*/ +void SwPagePreviewWin::AdjustPreviewToNewZoom( const sal_uInt16 _nZoomFactor, + const SvxZoomType _eZoomType ) +{ + // #i19975# consider zoom type + if ( _eZoomType == SvxZoomType::WHOLEPAGE ) + { + mnRow = 1; + mnCol = 1; + mpPgPreviewLayout->Init( mnCol, mnRow, maPxWinSize ); + mpPgPreviewLayout->Prepare( mnSttPage, Point(0,0), maPxWinSize, + mnSttPage, maPaintedPreviewDocRect ); + SetSelectedPage( mnSttPage ); + SetPagePreview(mnRow, mnCol); + maScale = GetMapMode().GetScaleX(); + } + else if ( _nZoomFactor != 0 ) + { + // calculate new scaling and set mapping mode appropriately. + Fraction aNewScale( _nZoomFactor, 100 ); + MapMode aNewMapMode = GetMapMode(); + aNewMapMode.SetScaleX( aNewScale ); + aNewMapMode.SetScaleY( aNewScale ); + SetMapMode( aNewMapMode ); + + // calculate new start position for preview paint + Size aNewWinSize = PixelToLogic( maPxWinSize ); + Point aNewPaintStartPos = + mpPgPreviewLayout->GetPreviewStartPosForNewScale( aNewScale, maScale, aNewWinSize ); + + // remember new scaling and prepare preview paint + // Note: paint of preview will be performed by a corresponding invalidate + // due to property changes. + maScale = aNewScale; + mpPgPreviewLayout->Prepare( 0, aNewPaintStartPos, maPxWinSize, + mnSttPage, maPaintedPreviewDocRect ); + } + +} + +/** + * pixel scrolling - horizontally always or vertically + * when less than the desired number of rows fits into + * the view + */ +void SwPagePreviewWin::Scroll(long nXMove, long nYMove, ScrollFlags /*nFlags*/) +{ + maPaintedPreviewDocRect.Move(nXMove, nYMove); + mpPgPreviewLayout->Prepare( 0, maPaintedPreviewDocRect.TopLeft(), + maPxWinSize, mnSttPage, + maPaintedPreviewDocRect ); + +} + +bool SwPagePreview::HandleWheelCommands( const CommandEvent& rCEvt ) +{ + bool bOk = false; + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if( pWData && CommandWheelMode::ZOOM == pWData->GetMode() ) + { + //only the Preference shouldn't control the Zoom, it is better to detect AT tools running. So the bridge can be used here + if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport()) + { + sal_uInt16 nFactor = GetViewShell()->GetViewOptions()->GetZoom(); + const sal_uInt16 nOffset = 10; + if( 0L > pWData->GetDelta() ) + { + nFactor -= nOffset; + if(nFactor < MIN_PREVIEW_ZOOM) + nFactor = MIN_PREVIEW_ZOOM; + } + else + { + nFactor += nOffset; + if(nFactor > MAX_PREVIEW_ZOOM) + nFactor = MAX_PREVIEW_ZOOM; + } + SetZoom(SvxZoomType::PERCENT, nFactor); + } + bOk = true; + } + else + bOk = m_pViewWin->HandleScrollCommand( rCEvt, m_pHScrollbar, m_pVScrollbar ); + return bOk; +} + +uno::Reference< css::accessibility::XAccessible > + SwPagePreviewWin::CreateAccessible() +{ + SolarMutexGuard aGuard; // this should have happened already!!! + + OSL_ENSURE( GetViewShell() != nullptr, "We need a view shell" ); + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible( false ); + if (xAcc.is()) + { + return xAcc; + } + if (mpViewShell) + { + css::uno::Reference< css::accessibility::XAccessible > xAccPreview = mpViewShell->CreateAccessiblePreview(); + SetAccessible(xAccPreview); + } + return GetAccessible( false ); +} + +void SwPagePreview::ApplyAccessibilityOptions(SvtAccessibilityOptions const & rAccessibilityOptions) +{ + GetViewShell()->ApplyAccessibilityOptions(rAccessibilityOptions); +} + +void SwPagePreview::ShowHScrollbar(bool bShow) +{ + m_pHScrollbar->Show(bShow); + InvalidateBorder(); +} + +void SwPagePreview::ShowVScrollbar(bool bShow) +{ + m_pVScrollbar->Show(bShow); + InvalidateBorder(); +} + +void SwPagePreview::EnableHScrollbar(bool bEnable) +{ + if (mbHScrollbarEnabled != bEnable) + { + mbHScrollbarEnabled = bEnable; + ScrollViewSzChg(); + } +} + +void SwPagePreview::EnableVScrollbar(bool bEnable) +{ + if (mbVScrollbarEnabled != bEnable) + { + mbVScrollbarEnabled = bEnable; + ScrollViewSzChg(); + } +} + +void SwPagePreview::SetZoom(SvxZoomType eType, sal_uInt16 nFactor) +{ + SwViewShell& rSh = *GetViewShell(); + SwViewOption aOpt(*rSh.GetViewOptions()); + // perform action only on changes of zoom or zoom type. + if ( aOpt.GetZoom() != nFactor || + aOpt.GetZoomType() != eType ) + { + aOpt.SetZoom(nFactor); + aOpt.SetZoomType(eType); + rSh.ApplyViewOptions( aOpt ); + lcl_InvalidateZoomSlots(GetViewFrame()->GetBindings()); + // #i19975# also consider zoom type + m_pViewWin->AdjustPreviewToNewZoom( nFactor, eType ); + ScrollViewSzChg(); + } +} + +/** adjust position of vertical scrollbar */ +void SwPagePreview::SetVScrollbarThumbPos( const sal_uInt16 _nNewThumbPos ) +{ + if ( m_pVScrollbar ) + { + m_pVScrollbar->SetThumbPos( _nNewThumbPos ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/scroll.cxx b/sw/source/uibase/uiview/scroll.cxx new file mode 100644 index 000000000..e44154b34 --- /dev/null +++ b/sw/source/uibase/uiview/scroll.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 <scroll.hxx> + +#define SCROLL_LINE_SIZE 250 + +SwScrollbar::SwScrollbar( vcl::Window *pWin, bool bHoriz ) : + ScrollBar( pWin, + WinBits( WB_3DLOOK | WB_HIDE | ( bHoriz ? WB_HSCROLL : WB_VSCROLL) ) ), + bHori( bHoriz ), + bAuto( false ), + bVisible(false), + bSizeSet(false) +{ + // No mirroring for horizontal scrollbars + if( bHoriz ) + EnableRTL( false ); +} + + SwScrollbar::~SwScrollbar() {} + +// Will be called after a change of the document size +// to refresh the range of the scrollbars. + +void SwScrollbar::DocSzChgd( const Size &rSize ) +{ + aDocSz = rSize; + SetRange( Range( 0, bHori ? rSize.Width() : rSize.Height()) ); + const sal_uLong nVisSize = GetVisibleSize(); + SetLineSize( SCROLL_LINE_SIZE ); + SetPageSize( nVisSize * 77 / 100 ); +} + +// Will be called after a change of the visible view section. + +void SwScrollbar::ViewPortChgd( const tools::Rectangle &rRect ) +{ + long nThumb, nVisible; + if( bHori ) + { + nThumb = rRect.Left(); + nVisible = rRect.GetWidth(); + } + else + { + nThumb = rRect.Top(); + nVisible = rRect.GetHeight(); + } + + SetVisibleSize( nVisible ); + DocSzChgd(aDocSz); + SetThumbPos( nThumb ); + if(bAuto) + AutoShow(); +} + +void SwScrollbar::ExtendedShow( bool bSet ) +{ + bVisible = bSet; + if( (!bSet || !bAuto) && IsUpdateMode() && bSizeSet) + ScrollBar::Show(bSet); +} + +void SwScrollbar::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize ) +{ + ScrollBar::SetPosSizePixel(rNewPos, rNewSize); + bSizeSet = true; + if(bVisible) + ExtendedShow(); + +} + +void SwScrollbar::SetAuto(bool bSet) +{ + if(bAuto != bSet) + { + bAuto = bSet; + + // hide automatically - then show + if(!bAuto && bVisible && !ScrollBar::IsVisible()) + ExtendedShow(); + else if(bAuto) + AutoShow(); // or hide automatically + } +} + +void SwScrollbar::AutoShow() +{ + long nVis = GetVisibleSize(); + long nLen = GetRange().Len(); + if( nVis >= nLen - 1) + { + if(ScrollBar::IsVisible()) + ScrollBar::Show(false); + } + else if ( !ScrollBar::IsVisible() ) + { + ScrollBar::Show(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/srcview.cxx b/sw/source/uibase/uiview/srcview.cxx new file mode 100644 index 000000000..0c3aaa4a3 --- /dev/null +++ b/sw/source/uibase/uiview/srcview.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 <rtl/tencinfo.h> +#include <osl/diagnose.h> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <unotools/tempfile.hxx> +#include <tools/urlobj.hxx> +#include <vcl/errinf.hxx> +#include <vcl/weld.hxx> +#include <vcl/textview.hxx> +#include <vcl/svapp.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <unotools/pathoptions.hxx> +#include <svl/undo.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <unotools/saveopt.hxx> +#include <vcl/transfer.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> +#include <svtools/htmlcfg.hxx> +#include <sfx2/app.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/request.hxx> +#include <sfx2/docfile.hxx> +#include <svx/srchdlg.hxx> +#include <svl/srchitem.hxx> +#include <sfx2/sfxhtml.hxx> +#include <swtypes.hxx> +#include <docsh.hxx> +#include <wdocsh.hxx> +#include <srcview.hxx> +#include "viewfunc.hxx" +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentState.hxx> +#include <sfx2/msg.hxx> +#include <shellio.hxx> + +#include <cmdid.h> +#include <strings.hrc> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <sfx2/filedlghelper.hxx> +#define ShellClass_SwSrcView +#include <swslots.hxx> + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::sfx2; + +#define SWSRCVIEWFLAGS SfxViewShellFlags::NO_NEWWINDOW + +#define SRC_SEARCHOPTIONS (SearchOptionFlags::ALL & ~SearchOptionFlags(SearchOptionFlags::FORMAT|SearchOptionFlags::FAMILIES|SearchOptionFlags::SEARCHALL)) + +// Printing margins -> like Basic - Ide +#define LMARGPRN 1700 +#define RMARGPRN 900 +#define TMARGPRN 2000 +#define BMARGPRN 1000 +#define BORDERPRN 300 + +SFX_IMPL_NAMED_VIEWFACTORY(SwSrcView, "SourceView") +{ + SFX_VIEW_REGISTRATION(SwWebDocShell); +} + +SFX_IMPL_SUPERCLASS_INTERFACE(SwSrcView, SfxViewShell) + +void SwSrcView::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("source"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, + SfxVisibilityFlags::Standard|SfxVisibilityFlags::Server, + ToolbarId::Webtools_Toolbox); + + GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId()); +} + + +static void lcl_PrintHeader( vcl::RenderContext &rOutDev, sal_Int32 nPages, sal_Int32 nCurPage, const OUString& rTitle ) +{ + short nLeftMargin = LMARGPRN; + Size aSz = rOutDev.GetOutputSize(); + short nBorder = BORDERPRN; + + Color aOldFillColor( rOutDev.GetFillColor() ); + vcl::Font aOldFont( rOutDev.GetFont() ); + + rOutDev.SetFillColor( COL_TRANSPARENT ); + + vcl::Font aFont( aOldFont ); + aFont.SetWeight( WEIGHT_BOLD ); + aFont.SetAlignment( ALIGN_BOTTOM ); + rOutDev.SetFont( aFont ); + + long nFontHeight = rOutDev.GetTextHeight(); + + // 1.Border => Line, 2+3 Border = Space. + long nYTop = TMARGPRN-3*nBorder-nFontHeight; + + long nXLeft = nLeftMargin-nBorder; + long nXRight = aSz.Width()-RMARGPRN+nBorder; + + rOutDev.DrawRect( tools::Rectangle( + Point( nXLeft, nYTop ), + Size( nXRight-nXLeft, aSz.Height() - nYTop - BMARGPRN + nBorder ) ) ); + + long nY = TMARGPRN-2*nBorder; + Point aPos( nLeftMargin, nY ); + rOutDev.DrawText( aPos, rTitle ); + if ( nPages != 1 ) + { + aFont.SetWeight( WEIGHT_NORMAL ); + rOutDev.SetFont( aFont ); + OUString aPageStr = " [" + SwResId( STR_PAGE ) + " " + OUString::number( nCurPage ) + "]"; + aPos.AdjustX(rOutDev.GetTextWidth( rTitle ) ); + rOutDev.DrawText( aPos, aPageStr ); + } + + nY = TMARGPRN-nBorder; + + rOutDev.DrawLine( Point( nXLeft, nY ), Point( nXRight, nY ) ); + + rOutDev.SetFont( aOldFont ); + rOutDev.SetFillColor( aOldFillColor ); +} + +static rtl_TextEncoding lcl_GetStreamCharSet(rtl_TextEncoding eLoadEncoding) +{ + rtl_TextEncoding eRet = eLoadEncoding; + if(RTL_TEXTENCODING_DONTKNOW == eRet) + { + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + const char *pCharSet = + rtl_getBestMimeCharsetFromTextEncoding( rHtmlOptions.GetTextEncoding() ); + eRet = rtl_getTextEncodingFromMimeCharset( pCharSet ); + } + return eRet; +} + +static OUString lcl_ConvertTabsToSpaces( const OUString& sLine ) +{ + if (sLine.isEmpty()) + return sLine; + + OUString aRet = sLine; + const sal_Unicode aPadSpaces[4] = {' ', ' ', ' ', ' '}; + sal_Int32 nPos = 0; + for (;;) + { + nPos = aRet.indexOf('\t', nPos); + if (nPos<0) + { + break; + } + // Not 4 blanks, but on 4th TabPos: + const sal_Int32 nPadLen = 4 - (nPos % 4); + aRet = aRet.replaceAt(nPos, 1, OUString(aPadSpaces, nPadLen)); + nPos += nPadLen; + } + return aRet; +} + +SwSrcView::SwSrcView(SfxViewFrame* pViewFrame, SfxViewShell*) : + SfxViewShell( pViewFrame, SWSRCVIEWFLAGS ), + aEditWin( VclPtr<SwSrcEditWindow>::Create( &pViewFrame->GetWindow(), this ) ), + bSourceSaved(false), + eLoadEncoding(RTL_TEXTENCODING_DONTKNOW) +{ + Init(); +} + +SwSrcView::~SwSrcView() +{ + SwDocShell* pDocShell = GetDocShell(); + OSL_ENSURE(dynamic_cast<SwWebDocShell*>( pDocShell), "Why no WebDocShell?" ); + const TextSelection& rSel = aEditWin->GetTextView()->GetSelection(); + static_cast<SwWebDocShell*>(pDocShell)->SetSourcePara( static_cast< sal_uInt16 >( rSel.GetStart().GetPara() ) ); + + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDocShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + OUString url = xDocProps->getAutoloadURL(); + sal_Int32 delay = xDocProps->getAutoloadSecs(); + pDocShell->SetAutoLoad(INetURLObject(url), delay, + (delay != 0) || !url.isEmpty()); + EndListening(*pDocShell); + pSearchItem.reset(); + + aEditWin.disposeAndClear(); +} + +void SwSrcView::SaveContentTo(SfxMedium& rMed) +{ + SvStream* pOutStream = rMed.GetOutStream(); + pOutStream->SetStreamCharSet(lcl_GetStreamCharSet(eLoadEncoding)); + aEditWin->Write( *pOutStream ); +} + +void SwSrcView::Init() +{ + SetName("Source"); + SetWindow( aEditWin.get() ); + SwDocShell* pDocShell = GetDocShell(); + // If the doc is still loading, then the DocShell must fire up + // the Load if the loading is completed. + if(!pDocShell->IsLoading()) + Load(pDocShell); + else + { + aEditWin->SetReadonly(true); + } + + SetNewWindowAllowed( false ); + StartListening(*pDocShell, DuplicateHandling::Prevent); +} + +SwDocShell* SwSrcView::GetDocShell() +{ + SfxObjectShell* pObjShell = GetViewFrame()->GetObjectShell(); + return dynamic_cast<SwDocShell*>( pObjShell ); +} + +void SwSrcView::SaveContent(const OUString& rTmpFile) +{ + SfxMedium aMedium( rTmpFile, StreamMode::WRITE); + SvStream* pOutStream = aMedium.GetOutStream(); + pOutStream->SetStreamCharSet( lcl_GetStreamCharSet(eLoadEncoding) ); + aEditWin->Write(*pOutStream); + aMedium.Commit(); +} + +void SwSrcView::Execute(SfxRequest& rReq) +{ + TextView* pTextView = aEditWin->GetTextView(); + switch( rReq.GetSlot() ) + { + case SID_SAVEACOPY: + case SID_SAVEASDOC: + { + SvtPathOptions aPathOpt; + // filesave dialog with autoextension + FileDialogHelper aDlgHelper( + TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, aEditWin->GetFrameWeld()); + uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker(); + + // search for an html filter for export + SfxFilterContainer* pFilterCont = GetObjectShell()->GetFactory().GetFilterContainer(); + std::shared_ptr<const SfxFilter> pFilter = + pFilterCont->GetFilter4Extension( "html", SfxFilterFlags::EXPORT ); + if ( pFilter ) + { + // filter found -> use its uiname and wildcard + const OUString& rUIName = pFilter->GetUIName(); + const WildCard& rCard = pFilter->GetWildcard(); + xFP->appendFilter( rUIName, rCard.getGlob() ); + xFP->setCurrentFilter( rUIName ) ; + } + else + { + // filter not found + OUString sHtml("HTML"); + xFP->appendFilter( sHtml, "*.html;*.htm" ); + xFP->setCurrentFilter( sHtml ) ; + } + + xFP->setDisplayDirectory( aPathOpt.GetWorkPath() ); + if( aDlgHelper.Execute() == ERRCODE_NONE) + { + SfxMedium aMedium( xFP->getSelectedFiles().getConstArray()[0], + StreamMode::WRITE | StreamMode::SHARE_DENYNONE ); + SvStream* pOutStream = aMedium.GetOutStream(); + pOutStream->SetStreamCharSet(lcl_GetStreamCharSet(eLoadEncoding)); + aEditWin->Write( *pOutStream ); + aMedium.Commit(); + } + } + break; + case SID_SAVEDOC: + { + SwDocShell* pDocShell = GetDocShell(); + assert(pDocShell); + SfxMedium* pMed = nullptr; + if(pDocShell->HasName()) + pMed = pDocShell->GetMedium(); + else + { + const SfxBoolItem* pItem = static_cast<const SfxBoolItem*>(pDocShell->ExecuteSlot(rReq, pDocShell->GetInterface())); + if(pItem && pItem->GetValue()) + pMed = pDocShell->GetMedium(); + } + if(pMed) + { + SvStream* pOutStream = pMed->GetOutStream(); + pOutStream->Seek(0); + pOutStream->SetStreamSize(0); + pOutStream->SetStreamCharSet(lcl_GetStreamCharSet(eLoadEncoding)); + aEditWin->Write( *pOutStream ); + pMed->CloseOutStream(); + pMed->Commit(); + pDocShell->GetDoc()->getIDocumentState().ResetModified(); + bSourceSaved = true; + aEditWin->ClearModifyFlag(); + } + } + break; + case FID_SEARCH_NOW: + { + const SfxItemSet* pTmpArgs = rReq.GetArgs(); + + const sal_uInt16 nWhich = pTmpArgs->GetWhichByPos( 0 ); + OSL_ENSURE( nWhich, "Which for SearchItem ?" ); + const SfxPoolItem& rItem = pTmpArgs->Get( nWhich ); + SetSearchItem( static_cast<const SvxSearchItem&>(rItem)); + StartSearchAndReplace( static_cast<const SvxSearchItem&>(rItem), rReq.IsAPI() ); + if(aEditWin->IsModified()) + { + SwDocShell* pDocShell = GetDocShell(); + assert(pDocShell); + pDocShell->GetDoc()->getIDocumentState().SetModified(); + } + } + break; + case FN_REPEAT_SEARCH: + { + SvxSearchItem* pSrchItem = GetSearchItem(); + if(pSrchItem) + { + StartSearchAndReplace( *pSrchItem, rReq.IsAPI() ); + if(aEditWin->IsModified()) + GetDocShell()->GetDoc()->getIDocumentState().SetModified(); + } + } + break; + case SID_PRINTDOC: + case SID_PRINTDOCDIRECT: + { + SfxViewShell::ExecuteSlot( rReq, SfxViewShell::GetInterface() ); + } + break; + case SID_UNDO: + pTextView->Undo(); + GetViewFrame()->GetBindings().InvalidateAll(false); + break; + case SID_REDO: + pTextView->Redo(); + GetViewFrame()->GetBindings().InvalidateAll(false); + break; + case SID_REPEAT: + break; + case SID_CUT: + if(pTextView->HasSelection()) + pTextView->Cut(); + break; + case SID_COPY: + if(pTextView->HasSelection()) + pTextView->Copy(); + break; + case SID_PASTE: + pTextView->Paste(); + break; + case SID_SELECTALL: + pTextView->SetSelection( TextSelection( TextPaM( 0, 0 ), TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ) ) ); + break; + } + aEditWin->Invalidate(); +} + +void SwSrcView::GetState(SfxItemSet& rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + TextView* pTextView = aEditWin->GetTextView(); + + while(nWhich) + { + switch(nWhich) + { + case SID_SAVEASDOC: + rSet.Put(SfxStringItem(nWhich, SwResId(STR_SAVEAS_SRC))); + break; + case SID_SAVEACOPY: + rSet.Put(SfxStringItem(nWhich, SwResId(STR_SAVEACOPY_SRC))); + break; + case SID_SAVEDOC: + { + SwDocShell* pDocShell = GetDocShell(); + assert(pDocShell); + if(!pDocShell->IsModified()) + rSet.DisableItem(nWhich); + } + break; + case SID_PRINTDOC: + case SID_PRINTDOCDIRECT: + break; + case SID_TABLE_CELL: + { + OUString aPos( SwResId(STR_SRCVIEW_ROW) ); + TextSelection aSel = pTextView->GetSelection(); + aPos += OUString::number( aSel.GetEnd().GetPara()+1 ); + aPos += " : " + + SwResId(STR_SRCVIEW_COL); + aPos += OUString::number( aSel.GetEnd().GetIndex()+1 ); + SfxStringItem aItem( nWhich, aPos ); + rSet.Put( aItem ); + } + break; + case SID_SEARCH_OPTIONS: + { + SearchOptionFlags nOpt = SRC_SEARCHOPTIONS; + SwDocShell* pDocShell = GetDocShell(); + assert(pDocShell); + if (pDocShell->IsReadOnly()) + nOpt &= ~SearchOptionFlags(SearchOptionFlags::REPLACE|SearchOptionFlags::REPLACE_ALL); + + rSet.Put( SfxUInt16Item( SID_SEARCH_OPTIONS, static_cast<sal_uInt16>(nOpt) ) ); + } + break; + case SID_SEARCH_ITEM: + { + OUString sSelected; + if ( !pTextView->HasSelection() ) + { + const TextSelection& rSel = pTextView->GetSelection(); + sSelected = aEditWin->GetTextEngine()->GetWord( rSel.GetStart()); + } + else + { + sSelected = pTextView->GetSelected(); + } + SvxSearchItem * pSrchItem = GetSearchItem(); + pSrchItem->SetSearchString( sSelected ); + rSet.Put( *pSrchItem ); + } + break; + case FN_REPEAT_SEARCH: + { + if(!GetSearchItem()) + rSet.DisableItem(nWhich); + } + break; + case SID_UNDO: + case SID_REDO: + { + SfxUndoManager& rMgr = pTextView->GetTextEngine()->GetUndoManager(); + sal_uInt16 nCount = 0; + if(nWhich == SID_UNDO) + { + nCount = rMgr.GetUndoActionCount(); + if(nCount) + { + OUString aStr(SvtResId( STR_UNDO)); + aStr += rMgr.GetUndoActionComment(--nCount); + rSet.Put(SfxStringItem(nWhich, aStr)); + } + else + rSet.DisableItem(nWhich); + } + else + { + nCount = rMgr.GetRedoActionCount(); + if(nCount) + { + OUString aStr(SvtResId( STR_REDO)); + aStr += rMgr.GetRedoActionComment(--nCount); + rSet.Put(SfxStringItem(nWhich,aStr)); + } + else + rSet.DisableItem(nWhich); + } + } + break; + case SID_MAIL_SENDDOCASPDF: + case SID_MAIL_SENDDOC : + case SID_EXPORTDOCASPDF: + case SID_DIRECTEXPORTDOCASPDF: + case SID_EXPORTDOC: + case SID_REPEAT: + case SID_BROWSER_MODE: + case FN_PRINT_LAYOUT: + rSet.DisableItem(nWhich); + break; + case SID_CUT: + case SID_COPY: + if(!pTextView->HasSelection()) + rSet.DisableItem(nWhich); + break; + case SID_PASTE: + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + aEditWin.get()) ); + bool bDisable = !aDataHelper.GetXTransferable().is() || + 0 == aDataHelper.GetFormatCount(); + if( bDisable ) + rSet.DisableItem(nWhich); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +SvxSearchItem* SwSrcView::GetSearchItem() +{ + if(!pSearchItem) + { + pSearchItem.reset(new SvxSearchItem(SID_SEARCH_ITEM)); + } + return pSearchItem.get(); +} + +void SwSrcView::SetSearchItem( const SvxSearchItem& rItem ) +{ + pSearchItem.reset(rItem.Clone()); +} + +void SwSrcView::StartSearchAndReplace(const SvxSearchItem& rSearchItem, + bool bApi, + bool bRecursive) +{ + TextView* pTextView = aEditWin->GetTextView(); + TextPaM aPaM; + + bool bForward = !rSearchItem.GetBackward(); + bool bAtStart = pTextView->GetSelection() == TextSelection( aPaM, aPaM ); + + if( !bForward ) + aPaM = TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ); + + i18nutil::SearchOptions2 aSearchOpt( rSearchItem.GetSearchOptions() ); + aSearchOpt.Locale = GetAppLanguageTag().getLocale(); + + sal_uInt16 nFound; + bool bAll = false; + switch( rSearchItem.GetCommand() ) + { + case SvxSearchCmd::FIND: + case SvxSearchCmd::FIND_ALL: + nFound = pTextView->Search( aSearchOpt, bForward ) ? 1 : 0; + break; + + case SvxSearchCmd::REPLACE_ALL: bAll = true; + [[fallthrough]]; + case SvxSearchCmd::REPLACE: + nFound = pTextView->Replace( aSearchOpt, bAll, bForward ); + break; + + default: + nFound = 0; + } + + if( !nFound ) + { + bool bNotFoundMessage = false; + if(!bRecursive) + { + bNotFoundMessage = bAtStart; + } + else if(bAtStart) + { + bNotFoundMessage = true; + } + + if(!bApi) + { + if(bNotFoundMessage) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/infonotfounddialog.ui")); + std::unique_ptr<weld::MessageDialog> xInfoBox(xBuilder->weld_message_dialog("InfoNotFoundDialog")); + xInfoBox->run(); + } + else if(!bRecursive) + { + int nRet; + + if (!bForward) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/querycontinueenddialog.ui")); + std::unique_ptr<weld::MessageDialog> xQueryBox(xBuilder->weld_message_dialog("QueryContinueEndDialog")); + nRet = xQueryBox->run(); + } + else + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/querycontinuebegindialog.ui")); + std::unique_ptr<weld::MessageDialog> xQueryBox(xBuilder->weld_message_dialog("QueryContinueBeginDialog")); + nRet = xQueryBox->run(); + } + + if (nRet == RET_YES) + { + pTextView->SetSelection( TextSelection( aPaM, aPaM ) ); + StartSearchAndReplace( rSearchItem, false, true ); + } + } + } + } +} + +sal_uInt16 SwSrcView::SetPrinter(SfxPrinter* pNew, SfxPrinterChangeFlags nDiffFlags ) +{ + SwDocShell* pDocSh = GetDocShell(); + assert(pDocSh); + if ( (SfxPrinterChangeFlags::JOBSETUP | SfxPrinterChangeFlags::PRINTER) & nDiffFlags ) + { + pDocSh->GetDoc()->getIDocumentDeviceAccess().setPrinter( pNew, true, true ); + if ( nDiffFlags & SfxPrinterChangeFlags::PRINTER ) + pDocSh->SetModified(); + } + if ( nDiffFlags & SfxPrinterChangeFlags::OPTIONS ) + ::SetPrinter( &pDocSh->getIDocumentDeviceAccess(), pNew, true ); + + const bool bChgOri = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION); + const bool bChgSize = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE); + if ( bChgOri || bChgSize ) + { + pDocSh->SetModified(); + } + return 0; +} + +SfxPrinter* SwSrcView::GetPrinter( bool bCreate ) +{ + SwDocShell* pDocSh = GetDocShell(); + assert(pDocSh); + return pDocSh->GetDoc()->getIDocumentDeviceAccess().getPrinter(bCreate); +} + +sal_Int32 SwSrcView::PrintSource( + OutputDevice *pOutDev, + sal_Int32 nPage, + bool bCalcNumPagesOnly ) +{ + if (!pOutDev || nPage <= 0) + return 0; + + //! This algorithm for printing the n-th page is very poor since it + //! needs to go over the text of all previous pages to get to the correct one. + //! But since HTML source code is expected to be just a small number of pages + //! even this poor algorithm should be enough... + + pOutDev->Push(); + + TextEngine* pTextEngine = aEditWin->GetTextEngine(); + pOutDev->SetMapMode(MapMode(MapUnit::Map100thMM)); + vcl::Font aFont( aEditWin->GetOutWin()->GetFont() ); + Size aSize( aFont.GetFontSize() ); + aSize = aEditWin->GetOutWin()->PixelToLogic(aSize, MapMode(MapUnit::Map100thMM)); + aFont.SetFontSize( aSize ); + aFont.SetColor( COL_BLACK ); + pOutDev->SetFont( aFont ); + + OUString aTitle( GetViewFrame()->GetWindow().GetText() ); + + const long nLineHeight = pOutDev->GetTextHeight(); // slightly more + const long nParaSpace = 10; + + Size aPaperSz = pOutDev->GetOutputSize(); + aPaperSz.AdjustWidth( -(LMARGPRN + RMARGPRN) ); + aPaperSz.AdjustHeight( -(TMARGPRN + BMARGPRN) ); + + // nLinepPage is not true, if lines have to be wrapped... + const long nLinespPage = nLineHeight ? aPaperSz.Height() / nLineHeight : 1; + const long nCharWidth = pOutDev->GetTextWidth("X"); + const sal_Int32 nCharspLine = nCharWidth ? static_cast<sal_Int32>(aPaperSz.Width() / nCharWidth) : 1; + const sal_uInt32 nParas = pTextEngine->GetParagraphCount(); + + const sal_Int32 nPages = static_cast<sal_Int32>(nParas / nLinespPage + 1 ); + sal_Int32 nCurPage = 1; + + // Print header... + if (!bCalcNumPagesOnly && nPage == nCurPage) + lcl_PrintHeader( *pOutDev, nPages, nCurPage, aTitle ); + const Point aStartPos( LMARGPRN, TMARGPRN ); + Point aPos( aStartPos ); + for ( sal_uInt32 nPara = 0; nPara < nParas; ++nPara ) + { + const OUString aLine( lcl_ConvertTabsToSpaces(pTextEngine->GetText( nPara )) ); + const sal_Int32 nLineLen = aLine.getLength(); + const sal_Int32 nLines = (nLineLen+nCharspLine-1) / nCharspLine; + for ( sal_Int32 nLine = 0; nLine < nLines; ++nLine ) + { + aPos.AdjustY(nLineHeight ); + if ( aPos.Y() > ( aPaperSz.Height() + TMARGPRN - nLineHeight/2 ) ) + { + ++nCurPage; + if (!bCalcNumPagesOnly && nPage == nCurPage) + lcl_PrintHeader( *pOutDev, nPages, nCurPage, aTitle ); + aPos = aStartPos; + } + if (!bCalcNumPagesOnly && nPage == nCurPage) + { + const sal_Int32 nStart = nLine * nCharspLine; + const sal_Int32 nLen = std::min(nLineLen-nStart, nCharspLine); + pOutDev->DrawText( aPos, aLine.copy(nStart, nLen) ); + } + } + aPos.AdjustY(nParaSpace ); + } + + pOutDev->Pop(); + + OSL_ENSURE( bCalcNumPagesOnly || nPage <= nCurPage, "page number out of range" ); + return nCurPage; +} + +void SwSrcView::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if (rHint.GetId() == SfxHintId::ModeChanged || rHint.GetId() == SfxHintId::TitleChanged) + { + const SwDocShell* pDocSh = GetDocShell(); + assert(pDocSh); + if (!(rHint.GetId() == SfxHintId::TitleChanged + && (pDocSh->IsReadOnly() || !aEditWin->IsReadonly()))) + { + // Broadcast only comes once! + const bool bReadonly = pDocSh->IsReadOnly(); + aEditWin->SetReadonly(bReadonly); + } + } + SfxViewShell::Notify(rBC, rHint); +} + +void SwSrcView::Load(SwDocShell* pDocShell) +{ + SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get(); + const char *pCharSet = + rtl_getBestMimeCharsetFromTextEncoding( rHtmlOptions.GetTextEncoding() ); + rtl_TextEncoding eDestEnc = rtl_getTextEncodingFromMimeCharset( pCharSet ); + + aEditWin->SetReadonly(pDocShell->IsReadOnly()); + aEditWin->SetTextEncoding(eDestEnc); + SfxMedium* pMedium = pDocShell->GetMedium(); + + std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter(); + bool bHtml = pFilter && pFilter->GetUserData() == "HTML"; + bool bDocModified = pDocShell->IsModified(); + if(bHtml && !bDocModified && pDocShell->HasName()) + { + SvStream* pStream = pMedium->GetInStream(); + if(pStream && ERRCODE_NONE == pStream->GetError() ) + { + rtl_TextEncoding eHeaderEnc = + SfxHTMLParser::GetEncodingByHttpHeader( + pDocShell->GetHeaderAttributes() ); + if( RTL_TEXTENCODING_DONTKNOW == eHeaderEnc ) + { + const char *pTmpCharSet = + rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ); + eHeaderEnc = rtl_getTextEncodingFromMimeCharset( pTmpCharSet ); + } + if( RTL_TEXTENCODING_DONTKNOW != eHeaderEnc && + eDestEnc != eHeaderEnc ) + { + eDestEnc = eHeaderEnc; + aEditWin->SetTextEncoding(eDestEnc); + } + pStream->SetStreamCharSet( eDestEnc ); + pStream->Seek(0); + TextEngine* pTextEngine = aEditWin->GetTextEngine(); + pTextEngine->EnableUndo(false); + aEditWin->Read(*pStream); + pTextEngine->EnableUndo(true); + } + else + { + vcl::Window& rTmpWindow = GetViewFrame()->GetWindow(); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(rTmpWindow.GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_ERR_SRCSTREAM))); + xBox->run(); + } + } + else + { + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + const OUString sFileURL( aTempFile.GetURL() ); + + { + SfxMedium aMedium( sFileURL,StreamMode::READWRITE ); + SwWriter aWriter( aMedium, *pDocShell->GetDoc() ); + WriterRef xWriter; + ::GetHTMLWriter(OUString(), aMedium.GetBaseURL( true ), xWriter); + const OUString sWriteName = pDocShell->HasName() + ? pMedium->GetName() + : sFileURL; + ErrCode nRes = aWriter.Write(xWriter, &sWriteName); + if(nRes) + { + ErrorHandler::HandleError(nRes); + aEditWin->SetReadonly(true); + } + aMedium.Commit(); + SvStream* pInStream = aMedium.GetInStream(); + pInStream->Seek(0); + pInStream->SetStreamCharSet( eDestEnc ); + + aEditWin->Read(*pInStream); + } + } + aEditWin->ClearModifyFlag(); + + eLoadEncoding = eDestEnc; + + if(bDocModified) + pDocShell->SetModified();// The flag will be reset in between times. + // Disable AutoLoad + pDocShell->SetAutoLoad(INetURLObject(), 0, false); + OSL_ENSURE(dynamic_cast<SwWebDocShell*>( pDocShell), "Why no WebDocShell?" ); + sal_uInt16 nLine = static_cast<SwWebDocShell*>(pDocShell)->GetSourcePara(); + aEditWin->SetStartLine(nLine); + aEditWin->GetTextEngine()->ResetUndo(); + aEditWin->GetOutWin()->GrabFocus(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/swcli.cxx b/sw/source/uibase/uiview/swcli.cxx new file mode 100644 index 000000000..05f5f6ccc --- /dev/null +++ b/sw/source/uibase/uiview/swcli.cxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <view.hxx> +#include <edtwin.hxx> +#include <swcli.hxx> +#include <svtools/embedhlp.hxx> + +#include <toolkit/helper/vclunohelper.hxx> + +using namespace com::sun::star; + +SwOleClient::SwOleClient(SwView *pView, SwEditWin *pWin, const svt::EmbeddedObjectRef& xObj) + : SfxInPlaceClient( pView, pWin, xObj.GetViewAspect() ) + , m_IsInDoVerb(false) + , m_IsOldCheckForOLEInCaption(pView->GetWrtShell().IsCheckForOLEInCaption()) +{ + SetObject( xObj.GetObject() ); +} + +void SwOleClient::RequestNewObjectArea( tools::Rectangle& aLogRect ) +{ + // The server wants to change the client size. + // We put the desired size in the core. The attributes of the frame + // are set to the desired value. This value will be passed on to the + // InPlaceClient. + // The core accepts or formats the adjusted values not necessarily. + // If the Ole-Frame is formatted, then the CalcAndSetScale() of the WrtShell + // will be called. There the scaling of the SwOleClient is set if necessary. + + SwWrtShell &rSh = static_cast<SwView*>(GetViewShell())->GetWrtShell(); + + rSh.StartAllAction(); + + // the aLogRect will get the preliminary size now + aLogRect.SetSize( rSh.RequestObjectResize( SwRect( aLogRect ), GetObject() ) ); + + // the EndAllAction() call will trigger CalcAndSetScale() call, + // so the embedded object must get the correct size before + if ( aLogRect.GetSize() != GetScaledObjArea().GetSize() ) + { + // size has changed, so first change visual area of the object before we resize its view + // without this the object always would be scaled - now it has the choice + + // TODO/LEAN: getMapUnit can switch object to running state + MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( GetObject()->getMapUnit( GetAspect() ) ) ); + MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() ); + + Size aNewObjSize( long( aLogRect.GetWidth() / GetScaleWidth() ), + long( aLogRect.GetHeight() / GetScaleHeight() ) ); + + // convert to logical coordinates of the embedded object + Size aNewSize = GetEditWin()->LogicToLogic( aNewObjSize, &aClientMap, &aObjectMap ); + GetObject()->setVisualAreaSize( GetAspect(), awt::Size( aNewSize.Width(), aNewSize.Height() ) ); + } + + rSh.EndAllAction(); + + SwRect aFrame( rSh.GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, GetObject() )), + aPrt( rSh.GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, GetObject() )); + aLogRect.SetPos( aPrt.Pos() + aFrame.Pos() ); + aLogRect.SetSize( aPrt.SSize() ); +} + +void SwOleClient::ObjectAreaChanged() +{ + SwWrtShell &rSh = static_cast<SwView*>(GetViewShell())->GetWrtShell(); + SwRect aFrame( rSh.GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, GetObject() )); + if ( !aFrame.IsOver( rSh.VisArea() ) ) + rSh.MakeVisible( aFrame ); +} + +void SwOleClient::ViewChanged() +{ + if (m_IsInDoVerb) + return; + + if ( GetAspect() == embed::Aspects::MSOLE_ICON ) + { + // the iconified object seems not to need such a scaling handling + // since the replacement image and the size a completely controlled by the container + // TODO/LATER: when the icon exchange is implemented the scaling handling + // might be required again here + return; + } + + SwWrtShell &rSh = static_cast<SwView*>(GetViewShell())->GetWrtShell(); + + // Adjust the size of the object in the core. The Scaling must + // be considered. Repercussions on the object are considered by + // CalcAndSetScale() of the WrtShell if the size / position of + // the frame in the core changes. + + // TODO/LEAN: getMapUnit can switch object to running state + awt::Size aSz; + try + { + aSz = GetObject()->getVisualAreaSize( GetAspect() ); + } + catch (const embed::NoVisualAreaSizeException&) + { + // Nothing will be done + } + catch (const uno::Exception&) + { + OSL_FAIL( "Something goes wrong on requesting object size!" ); + } + + Size aVisSize( aSz.Width, aSz.Height ); + + // As long as from the object comes no reasonable size + // nothing can be scaled. + if( !aVisSize.Width() || !aVisSize.Height() ) + return; + + // first convert to TWIPS before scaling, because scaling factors are calculated for + // the TWIPS mapping and so they will produce the best results if applied to TWIPS based + // coordinates + const MapMode aMyMap ( MapUnit::MapTwip ); + const MapMode aObjMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( GetObject()->getMapUnit( GetAspect() ) ) ); + aVisSize = OutputDevice::LogicToLogic( aVisSize, aObjMap, aMyMap ); + + aVisSize.setWidth( long(aVisSize.Width() * GetScaleWidth()) ); + aVisSize.setHeight( long(aVisSize.Height() * GetScaleHeight()) ); + + SwRect aRect( Point( LONG_MIN, LONG_MIN ), aVisSize ); + rSh.LockView( true ); // Prevent scrolling in the EndAction + rSh.StartAllAction(); + rSh.RequestObjectResize( aRect, GetObject() ); + rSh.EndAllAction(); + rSh.LockView( false ); +} + +void SwOleClient::FormatChanged() +{ + const uno::Reference < embed::XEmbeddedObject >& xObj( GetObject() ); + SwView * pView = dynamic_cast< SwView * >( GetViewShell() ); + if ( pView && xObj.is() && SotExchange::IsMath( xObj->getClassID() ) ) + { + SwWrtShell & rWrtSh = pView->GetWrtShell(); + if (rWrtSh.GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT )) + rWrtSh.AlignFormulaToBaseline( xObj ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/uivwimp.cxx b/sw/source/uibase/uiview/uivwimp.cxx new file mode 100644 index 000000000..6a45751ad --- /dev/null +++ b/sw/source/uibase/uiview/uivwimp.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 <config_features.h> + +#include <cmdid.h> + +#include <com/sun/star/scanner/XScannerManager2.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <comphelper/propertysequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/weld.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> + +#include <sfx2/docinsert.hxx> +#include <sfx2/request.hxx> +#include <uivwimp.hxx> +#include <unotxvw.hxx> +#include <unodispatch.hxx> +#include <swmodule.hxx> +#include <swdtflvr.hxx> + +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::scanner; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::datatransfer::clipboard; + +SwView_Impl::SwView_Impl(SwView* pShell) + : mxXTextView() + , pView(pShell) + , eShellMode(ShellMode::Text) + , m_nParam(0) + , m_bSelectObject(false) + , m_bEditingPositionSet(false) +{ + mxXTextView = new SwXTextView(pView); + xDisProvInterceptor = new SwXDispatchProviderInterceptor(*pView); +} + +SwView_Impl::~SwView_Impl() +{ + auto pInterceptor = comphelper::getUnoTunnelImplementation<SwXDispatchProviderInterceptor>(xDisProvInterceptor); + if(pInterceptor) + pInterceptor->Invalidate(); + view::XSelectionSupplier* pTextView = mxXTextView.get(); + static_cast<SwXTextView*>(pTextView)->Invalidate(); + mxXTextView.clear(); + if( mxScanEvtLstnr.is() ) + mxScanEvtLstnr->ViewDestroyed(); + if( mxClipEvtLstnr.is() ) + { + mxClipEvtLstnr->AddRemoveListener( false ); + mxClipEvtLstnr->ViewDestroyed(); + } +#if HAVE_FEATURE_DBCONNECTIVITY + xConfigItem.reset(); +#endif + m_pDocInserter.reset(); + m_pRequest.reset(); +} + +void SwView_Impl::SetShellMode(ShellMode eSet) +{ + eShellMode = eSet; +} + +view::XSelectionSupplier* SwView_Impl::GetUNOObject() +{ + return mxXTextView.get(); +} + +SwXTextView* SwView_Impl::GetUNOObject_Impl() +{ + view::XSelectionSupplier* pTextView = mxXTextView.get(); + return static_cast<SwXTextView*>(pTextView); +} + +void SwView_Impl::ExecuteScan( SfxRequest& rReq ) +{ + switch(rReq.GetSlot()) + { + case SID_TWAIN_SELECT: + { + bool bDone = false; + Reference< XScannerManager2 > xScanMgr = SW_MOD()->GetScannerManager(); + + if( xScanMgr.is() ) + { + try + { + SwScannerEventListener& rListener = GetScannerEventListener(); + const Sequence< ScannerContext > + aContexts( xScanMgr->getAvailableScanners() ); + + if( aContexts.hasElements() ) + { + Reference< XEventListener > xLstner = &rListener; + ScannerContext aContext( aContexts.getConstArray()[ 0 ] ); + + Reference<lang::XInitialization> xInit(xScanMgr, UNO_QUERY); + if (xInit.is()) + { + // initialize dialog + weld::Window* pWindow = rReq.GetFrameWeld(); + uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", pWindow ? uno::Any(pWindow->GetXWindow()) : uno::Any(Reference<awt::XWindow>())} + })); + xInit->initialize( aSeq ); + } + + bDone = xScanMgr->configureScannerAndScan( aContext, xLstner ); + } + } + catch(...) + { + } + + } + if( bDone ) + rReq.Done(); + else + { + rReq.Ignore(); + } + } + break; + + case SID_TWAIN_TRANSFER: + { + bool bDone = false; + + Reference< XScannerManager2 > xScanMgr = SW_MOD()->GetScannerManager(); + if( xScanMgr.is() ) + { + SwScannerEventListener& rListener = GetScannerEventListener(); + try + { + const Sequence< scanner::ScannerContext >aContexts( xScanMgr->getAvailableScanners() ); + if( aContexts.hasElements() ) + { + Reference< XEventListener > xLstner = &rListener; + xScanMgr->startScan( aContexts.getConstArray()[ 0 ], xLstner ); + bDone = true; + } + } + catch(...) + { + } + } + + if( !bDone ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(rReq.GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_SCAN_NOSOURCE))); + xBox->run(); + rReq.Ignore(); + } + else + { + rReq.Done(); + SfxBindings& rBind = pView->GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_TWAIN_SELECT ); + rBind.Invalidate( SID_TWAIN_TRANSFER ); + } + } + break; + } +} + +SwScannerEventListener& SwView_Impl::GetScannerEventListener() +{ + if(!mxScanEvtLstnr.is()) + mxScanEvtLstnr = new SwScannerEventListener(*pView); + return *mxScanEvtLstnr; +} + +void SwView_Impl::AddClipboardListener() +{ + if(!mxClipEvtLstnr.is()) + { + mxClipEvtLstnr = new SwClipboardChangeListener( *pView ); + mxClipEvtLstnr->AddRemoveListener( true ); + } +} + +void SwView_Impl::Invalidate() +{ + GetUNOObject_Impl()->Invalidate(); + for (const auto& xTransferable: mxTransferables) + { + auto pTransferable = comphelper::getUnoTunnelImplementation<SwTransferable>(xTransferable.get()); + if(pTransferable) + pTransferable->Invalidate(); + } +} + +void SwView_Impl::AddTransferable(SwTransferable& rTransferable) +{ + //prevent removing of the non-referenced SwTransferable + osl_atomic_increment(&rTransferable.m_refCount); + { + // Remove previously added, but no longer existing weak references. + mxTransferables.erase(std::remove_if(mxTransferables.begin(), mxTransferables.end(), + [](const css::uno::WeakReference<css::lang::XUnoTunnel>& rTunnel) { + uno::Reference<lang::XUnoTunnel> xTunnel(rTunnel.get(), uno::UNO_QUERY); + return !xTunnel.is(); + }), mxTransferables.end()); + + mxTransferables.emplace_back(uno::Reference<lang::XUnoTunnel>(&rTransferable)); + } + osl_atomic_decrement(&rTransferable.m_refCount); +} + +void SwView_Impl::StartDocumentInserter( + const OUString& rFactory, + const Link<sfx2::FileDialogHelper*,void>& rEndDialogHdl, + const sal_uInt16 nSlotId +) +{ + sfx2::DocumentInserter::Mode mode {sfx2::DocumentInserter::Mode::Insert}; + switch( nSlotId ) + { + case SID_DOCUMENT_MERGE: + mode = sfx2::DocumentInserter::Mode::Merge; + break; + case SID_DOCUMENT_COMPARE: + mode = sfx2::DocumentInserter::Mode::Compare; + break; + default: + break; + } + + m_pDocInserter.reset(new ::sfx2::DocumentInserter(pView->GetFrameWeld(), rFactory, mode)); + m_pDocInserter->StartExecuteModal( rEndDialogHdl ); +} + +std::unique_ptr<SfxMedium> SwView_Impl::CreateMedium() +{ + return m_pDocInserter->CreateMedium(); +} + +void SwView_Impl::InitRequest( const SfxRequest& rRequest ) +{ + m_pRequest.reset(new SfxRequest( rRequest )); +} + +SwScannerEventListener::~SwScannerEventListener() +{ +} + +void SAL_CALL SwScannerEventListener::disposing( const EventObject& /*rEventObject*/) +{ +#if defined(_WIN32) || defined UNX + SolarMutexGuard aGuard; + if( pView ) + pView->ScannerEventHdl(); +#endif +} + +SwClipboardChangeListener::~SwClipboardChangeListener() +{ +} + +void SAL_CALL SwClipboardChangeListener::disposing( const EventObject& /*rEventObject*/ ) +{ +} + +void SAL_CALL SwClipboardChangeListener::changedContents( const css::datatransfer::clipboard::ClipboardEvent& rEventObject ) + +{ + const SolarMutexGuard aGuard; + if( pView ) + { + { + TransferableDataHelper aDataHelper( rEventObject.Contents ); + SwWrtShell& rSh = pView->GetWrtShell(); + + pView->m_nLastPasteDestination = SwTransferable::GetSotDestination( rSh ); + pView->m_bPasteState = aDataHelper.GetXTransferable().is() && + SwTransferable::IsPaste( rSh, aDataHelper ); + + pView->m_bPasteSpecialState = aDataHelper.GetXTransferable().is() && + SwTransferable::IsPasteSpecial( rSh, aDataHelper ); + } + + SfxBindings& rBind = pView->GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_PASTE ); + rBind.Invalidate( SID_PASTE_SPECIAL ); + rBind.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); + } +} + +void SwClipboardChangeListener::AddRemoveListener( bool bAdd ) +{ + pView->AddRemoveClipboardListener( Reference< XClipboardListener >( this ), bAdd ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx new file mode 100644 index 000000000..f2d10b466 --- /dev/null +++ b/sw/source/uibase/uiview/view.cxx @@ -0,0 +1,1871 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <stdlib.h> +#include <hintids.hxx> +#include <comphelper/string.hxx> +#include <o3tl/any.hxx> +#include <officecfg/Office/Common.hxx> +#include <vcl/graph.hxx> +#include <vcl/inputctx.hxx> +#include <svl/eitem.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/lingucfg.hxx> +#include <unotools/useroptions.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/request.hxx> +#include <svx/ruler.hxx> +#include <svx/srchdlg.hxx> +#include <svx/fmshell.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <svx/fmview.hxx> +#include <unotxvw.hxx> +#include <cmdid.h> +#include <svl/hint.hxx> +#include <swmodule.hxx> +#include <inputwin.hxx> +#include <uivwimp.hxx> +#include <edtwin.hxx> +#include <textsh.hxx> +#include <listsh.hxx> +#include <tabsh.hxx> +#include <grfsh.hxx> +#include <mediash.hxx> +#include <docsh.hxx> +#include <frmsh.hxx> +#include <olesh.hxx> +#include <drawsh.hxx> +#include <drawbase.hxx> +#include <drformsh.hxx> +#include <drwtxtsh.hxx> +#include <beziersh.hxx> +#include <navsh.hxx> +#include <globdoc.hxx> +#include <scroll.hxx> +#include <gloshdl.hxx> +#include <usrpref.hxx> +#include <srcview.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <DocumentFieldsManager.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <drawdoc.hxx> +#include <wdocsh.hxx> +#include <wrtsh.hxx> +#include <barcfg.hxx> +#include <pview.hxx> +#include <swdtflvr.hxx> +#include <prtopt.hxx> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/scanner/ScannerContext.hpp> +#include <com/sun/star/scanner/XScannerManager2.hpp> +#include <toolkit/helper/vclunohelper.hxx> +#include <sal/log.hxx> + +#include <formatclipboard.hxx> +#include <PostItMgr.hxx> +#include <annotsh.hxx> +#include <swruler.hxx> + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <svtools/embedhlp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::scanner; + +#define SWVIEWFLAGS SfxViewShellFlags::HAS_PRINTOPTIONS + +// Statics. OMG. + +bool bDocSzUpdated = true; + +SvxSearchItem* SwView::s_pSrchItem = nullptr; + +bool SwView::s_bExtra = false; +bool SwView::s_bFound = false; +bool SwView::s_bJustOpened = false; + +SearchAttrItemList* SwView::s_pSearchList = nullptr; +SearchAttrItemList* SwView::s_pReplaceList = nullptr; + +SfxDispatcher &SwView::GetDispatcher() +{ + return *GetViewFrame()->GetDispatcher(); +} + +void SwView::ImpSetVerb( SelectionType nSelType ) +{ + bool bResetVerbs = m_bVerbsActive; + if ( !GetViewFrame()->GetFrame().IsInPlace() && + (SelectionType::Ole|SelectionType::Graphic) & nSelType ) + { + if ( m_pWrtShell->IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE ) + { + if ( nSelType & SelectionType::Ole ) + { + SetVerbs( GetWrtShell().GetOLEObject()->getSupportedVerbs() ); + m_bVerbsActive = true; + bResetVerbs = false; + } + } + } + if ( bResetVerbs ) + { + SetVerbs( Sequence< embed::VerbDescriptor >() ); + m_bVerbsActive = false; + } +} + +// Called by the SwEditWin when it gets the focus. + +void SwView::GotFocus() const +{ + // if we got the focus, and the form shell *is* on the top of the dispatcher + // stack, then we need to rebuild the stack (the form shell doesn't belong to + // the top then) + const SfxDispatcher& rDispatcher = const_cast< SwView* >( this )->GetDispatcher(); + SfxShell* pTopShell = rDispatcher.GetShell( 0 ); + FmFormShell* pAsFormShell = dynamic_cast<FmFormShell*>( pTopShell ); + if ( pAsFormShell ) + { + pAsFormShell->ForgetActiveControl(); + const_cast< SwView* >( this )->AttrChangedNotify(nullptr); + } + else if ( m_pPostItMgr ) + { + SwAnnotationShell* pAsAnnotationShell = dynamic_cast<SwAnnotationShell*>( pTopShell ); + if ( pAsAnnotationShell ) + { + m_pPostItMgr->SetActiveSidebarWin(nullptr); + const_cast< SwView* >( this )->AttrChangedNotify(nullptr); + } + } + if( GetWrtShellPtr() ) + { + SwWrtShell& rWrtShell = GetWrtShell(); + rWrtShell.GetDoc()->getIDocumentLayoutAccess().SetCurrentViewShell( GetWrtShellPtr() ); + rWrtShell.GetDoc()->getIDocumentSettingAccess().set( DocumentSettingId::BROWSE_MODE, + rWrtShell.GetViewOptions()->getBrowseMode() ); + } +} + +// called by the FormShell when a form control is focused. This is +// a request to put the form shell on the top of the dispatcher stack + +IMPL_LINK_NOARG(SwView, FormControlActivated, LinkParamNone*, void) +{ + // if a form control has been activated, and the form shell is not on the top + // of the dispatcher stack, then we need to activate it + const SfxDispatcher& rDispatcher = GetDispatcher(); + const SfxShell* pTopShell = rDispatcher.GetShell( 0 ); + const FmFormShell* pAsFormShell = dynamic_cast<const FmFormShell*>( pTopShell ); + if ( !pAsFormShell ) + { + // if we're editing text currently, cancel this + SdrView *pSdrView = m_pWrtShell ? m_pWrtShell->GetDrawView() : nullptr; + if ( pSdrView && pSdrView->IsTextEdit() ) + pSdrView->SdrEndTextEdit( true ); + + AttrChangedNotify(nullptr); + } +} + +namespace +{ +uno::Reference<frame::XLayoutManager> getLayoutManager(const SfxViewFrame& rViewFrame) +{ + uno::Reference<frame::XLayoutManager> xLayoutManager; + uno::Reference<beans::XPropertySet> xPropSet(rViewFrame.GetFrame().GetFrameInterface(), + uno::UNO_QUERY); + if (xPropSet.is()) + { + try + { + xLayoutManager.set(xPropSet->getPropertyValue("LayoutManager"), uno::UNO_QUERY); + } + catch (const Exception& e) + { + SAL_WARN("sw.ui", "Failure getting layout manager: " + e.Message); + } + } + return xLayoutManager; +} +} + +void SwView::ShowUIElement(const OUString& sElementURL) const +{ + if (auto xLayoutManager = getLayoutManager(*GetViewFrame())) + { + if (!xLayoutManager->getElement(sElementURL).is()) + { + xLayoutManager->createElement(sElementURL); + xLayoutManager->showElement(sElementURL); + } + } +} + +void SwView::SelectShell() +{ + // Attention: Maintain the SelectShell for the WebView additionally + + if(m_bInDtor) + return; + + // Decision if the UpdateTable has to be called + bool bUpdateTable = false; + const SwFrameFormat* pCurTableFormat = m_pWrtShell->GetTableFormat(); + if(pCurTableFormat && pCurTableFormat != m_pLastTableFormat) + { + bUpdateTable = true; // can only be executed later + } + m_pLastTableFormat = pCurTableFormat; + + //SEL_TBL and SEL_TBL_CELLS can be ORed! + SelectionType nNewSelectionType = m_pWrtShell->GetSelectionType() + & ~SelectionType::TableCell; + + if ( m_pFormShell && m_pFormShell->IsActiveControl() ) + nNewSelectionType |= SelectionType::FormControl; + + if ( nNewSelectionType == m_nSelectionType ) + { + GetViewFrame()->GetBindings().InvalidateAll( false ); + if ( m_nSelectionType & SelectionType::Ole || + m_nSelectionType & SelectionType::Graphic ) + // For graphs and OLE the verb can be modified of course! + ImpSetVerb( nNewSelectionType ); + } + else + { + + SfxDispatcher &rDispatcher = GetDispatcher(); + SwToolbarConfigItem *pBarCfg = SW_MOD()->GetToolbarConfig(); + + if ( m_pShell ) + { + rDispatcher.Flush(); // Really erase all cached shells + //Remember to the old selection which toolbar was visible + ToolbarId eId = rDispatcher.GetObjectBarId(SFX_OBJECTBAR_OBJECT); + if (eId != ToolbarId::None) + pBarCfg->SetTopToolbar(m_nSelectionType, eId); + + for ( sal_uInt16 i = 0; true; ++i ) + { + SfxShell *pSfxShell = rDispatcher.GetShell( i ); + if ( dynamic_cast< const SwBaseShell *>( pSfxShell ) != nullptr + || dynamic_cast< const SwDrawTextShell *>( pSfxShell ) != nullptr + || dynamic_cast< const svx::ExtrusionBar*>( pSfxShell ) != nullptr + || dynamic_cast< const svx::FontworkBar*>( pSfxShell ) != nullptr + || dynamic_cast< const SwAnnotationShell *>( pSfxShell ) != nullptr + ) + { + rDispatcher.Pop( *pSfxShell, SfxDispatcherPopFlags::POP_DELETE ); + } + else if ( dynamic_cast< const FmFormShell *>( pSfxShell ) != nullptr ) + { + rDispatcher.Pop( *pSfxShell ); + } + else + break; + } + } + + bool bInitFormShell = false; + if (!m_pFormShell) + { + bInitFormShell = true; + m_pFormShell = new FmFormShell( this ); + m_pFormShell->SetControlActivationHandler( LINK( this, SwView, FormControlActivated ) ); + StartListening(*m_pFormShell); + } + + bool bSetExtInpCntxt = false; + m_nSelectionType = nNewSelectionType; + ShellMode eShellMode; + + if ( !( m_nSelectionType & SelectionType::FormControl ) ) + rDispatcher.Push( *m_pFormShell ); + + m_pShell = new SwNavigationShell( *this ); + rDispatcher.Push( *m_pShell ); + + if ( m_nSelectionType & SelectionType::Ole ) + { + eShellMode = ShellMode::Object; + m_pShell = new SwOleShell( *this ); + rDispatcher.Push( *m_pShell ); + } + else if ( m_nSelectionType & SelectionType::Frame + || m_nSelectionType & SelectionType::Graphic) + { + eShellMode = ShellMode::Frame; + m_pShell = new SwFrameShell( *this ); + rDispatcher.Push( *m_pShell ); + if(m_nSelectionType & SelectionType::Graphic ) + { + eShellMode = ShellMode::Graphic; + m_pShell = new SwGrfShell( *this ); + rDispatcher.Push( *m_pShell ); + } + } + else if ( m_nSelectionType & SelectionType::DrawObject ) + { + eShellMode = ShellMode::Draw; + m_pShell = new SwDrawShell( *this ); + rDispatcher.Push( *m_pShell ); + + if ( m_nSelectionType & SelectionType::Ornament ) + { + eShellMode = ShellMode::Bezier; + m_pShell = new SwBezierShell( *this ); + rDispatcher.Push( *m_pShell ); + } +#if HAVE_FEATURE_AVMEDIA + else if( m_nSelectionType & SelectionType::Media ) + { + eShellMode = ShellMode::Media; + m_pShell = new SwMediaShell( *this ); + rDispatcher.Push( *m_pShell ); + } +#endif + if (m_nSelectionType & SelectionType::ExtrudedCustomShape) + { + eShellMode = ShellMode::ExtrudedCustomShape; + m_pShell = new svx::ExtrusionBar(this); + rDispatcher.Push( *m_pShell ); + } + if (m_nSelectionType & SelectionType::FontWork) + { + eShellMode = ShellMode::FontWork; + m_pShell = new svx::FontworkBar(this); + rDispatcher.Push( *m_pShell ); + } + } + else if ( m_nSelectionType & SelectionType::DbForm ) + { + eShellMode = ShellMode::DrawForm; + m_pShell = new SwDrawFormShell( *this ); + + rDispatcher.Push( *m_pShell ); + } + else if ( m_nSelectionType & SelectionType::DrawObjectEditMode ) + { + bSetExtInpCntxt = true; + eShellMode = ShellMode::DrawText; + rDispatcher.Push( *(new SwBaseShell( *this )) ); + m_pShell = new SwDrawTextShell( *this ); + rDispatcher.Push( *m_pShell ); + } + else if ( m_nSelectionType & SelectionType::PostIt ) + { + eShellMode = ShellMode::PostIt; + m_pShell = new SwAnnotationShell( *this ); + rDispatcher.Push( *m_pShell ); + } + else + { + bSetExtInpCntxt = true; + eShellMode = ShellMode::Text; + if ( m_nSelectionType & SelectionType::NumberList ) + { + eShellMode = ShellMode::ListText; + m_pShell = new SwListShell( *this ); + rDispatcher.Push( *m_pShell ); + } + m_pShell = new SwTextShell(*this); + rDispatcher.Push( *m_pShell ); + if ( m_nSelectionType & SelectionType::Table ) + { + eShellMode = eShellMode == ShellMode::ListText ? ShellMode::TableListText + : ShellMode::TableText; + m_pShell = new SwTableShell( *this ); + rDispatcher.Push( *m_pShell ); + } + } + + if ( m_nSelectionType & SelectionType::FormControl ) + rDispatcher.Push( *m_pFormShell ); + + m_pViewImpl->SetShellMode(eShellMode); + ImpSetVerb( m_nSelectionType ); + + if( !GetDocShell()->IsReadOnly() ) + { + if( bSetExtInpCntxt && GetWrtShell().HasReadonlySel() ) + bSetExtInpCntxt = false; + + InputContext aCntxt( GetEditWin().GetInputContext() ); + aCntxt.SetOptions( bSetExtInpCntxt + ? (aCntxt.GetOptions() | + ( InputContextFlags::Text | + InputContextFlags::ExtText )) + : (aCntxt.GetOptions() & ~ + InputContextFlags( InputContextFlags::Text | + InputContextFlags::ExtText )) ); + GetEditWin().SetInputContext( aCntxt ); + } + + // Show Mail Merge toolbar initially for documents with Database fields + if (!m_bInitOnceCompleted && GetWrtShell().IsAnyDatabaseFieldInDoc()) + ShowUIElement("private:resource/toolbar/mailmerge"); + + // Activate the toolbar to the new selection which also was active last time. + // Before a flush () must be, but does not affect the UI according to MBA and + // is not a performance problem. + // TODO/LATER: maybe now the Flush() command is superfluous?! + rDispatcher.Flush(); + + Point aPnt = GetEditWin().OutputToScreenPixel(GetEditWin().GetPointerPosPixel()); + aPnt = GetEditWin().PixelToLogic(aPnt); + GetEditWin().UpdatePointer(aPnt); + + SdrView* pDView = GetWrtShell().GetDrawView(); + if ( bInitFormShell && pDView ) + m_pFormShell->SetView(dynamic_cast<FmFormView*>( pDView) ); + + } + // Opportune time for the communication with OLE objects? + if ( GetDocShell()->GetDoc()->IsOLEPrtNotifyPending() ) + GetDocShell()->GetDoc()->PrtOLENotify( false ); + + // now the table-update + if(bUpdateTable) + m_pWrtShell->UpdateTable(); + + GetViewImpl()->GetUNOObject_Impl()->NotifySelChanged(); + + m_bInitOnceCompleted = true; +} + +// Interaction: AttrChangedNotify() and TimeoutHdl. +// No Update if actions are still open, since the cursor on the core side +// can be somewhere in no man's land. +// But since we can no longer supply status and we want instead lock +// the dispatcher. + +extern "C" +{ + static int lcl_CmpIds( const void *pFirst, const void *pSecond) + { + return *static_cast<sal_uInt16 const *>(pFirst) - *static_cast<sal_uInt16 const *>(pSecond); + } +} + +IMPL_LINK_NOARG(SwView, AttrChangedNotify, LinkParamNone*, void) +{ + if ( GetEditWin().IsChainMode() ) + GetEditWin().SetChainMode( false ); + + //Opt: Not if PaintLocked. During unlock a notify will be once more triggered. + if( !m_pWrtShell->IsPaintLocked() && !g_bNoInterrupt && + GetDocShell()->IsReadOnly() ) + CheckReadonlyState(); + + if( !m_pWrtShell->IsPaintLocked() && !g_bNoInterrupt ) + CheckReadonlySelection(); + + if( !m_bAttrChgNotified ) + { + if (m_pWrtShell->ActionPend() || g_bNoInterrupt || + GetDispatcher().IsLocked() || //do not confuse the SFX + GetViewFrame()->GetBindings().IsInUpdate() )//do not confuse the SFX + { + m_bAttrChgNotified = true; + m_aTimer.Start(); + + const SfxPoolItem *pItem; + if ( SfxItemState::SET != GetObjectShell()->GetMedium()->GetItemSet()-> + GetItemState( SID_HIDDEN, false, &pItem ) || + !static_cast<const SfxBoolItem*>(pItem)->GetValue() ) + { + GetViewFrame()->GetBindings().ENTERREGISTRATIONS(); + m_bAttrChgNotifiedWithRegistrations = true; + } + + } + else + SelectShell(); + + } + + // change ui if cursor is at a SwPostItField + if (m_pPostItMgr) + { + // only perform the code that is needed to determine, if at the + // actual cursor position is a post-it field + m_pPostItMgr->SetShadowState( m_pWrtShell->GetPostItFieldAtCursor() ); + } +} + +IMPL_LINK_NOARG(SwView, TimeoutHdl, Timer *, void) +{ + if (m_pWrtShell->ActionPend() || g_bNoInterrupt) + { + m_aTimer.Start(); + return; + } + + if ( m_bAttrChgNotifiedWithRegistrations ) + { + GetViewFrame()->GetBindings().LEAVEREGISTRATIONS(); + m_bAttrChgNotifiedWithRegistrations = false; + } + + CheckReadonlyState(); + CheckReadonlySelection(); + + bool bOldUndo = m_pWrtShell->DoesUndo(); + m_pWrtShell->DoUndo( false ); + SelectShell(); + m_pWrtShell->DoUndo( bOldUndo ); + m_bAttrChgNotified = false; + GetViewImpl()->GetUNOObject_Impl()->NotifySelChanged(); +} + +void SwView::CheckReadonlyState() +{ + SfxDispatcher &rDis = GetDispatcher(); + // To be able to recognize if it is already disabled! + SfxItemState eStateRO, eStateProtAll; + const SfxPoolItem *pItem; + // Query the status from a slot which is only known to us. + // Otherwise the slot is known from other; like the BasicIde + eStateRO = rDis.QueryState( FN_INSERT_BOOKMARK, pItem ); + eStateProtAll = rDis.QueryState( FN_EDIT_REGION, pItem ); + bool bChgd = false; + + if ( !m_pWrtShell->IsCursorReadonly() ) + { + static sal_uInt16 aROIds[] = + { + SID_DELETE, FN_BACKSPACE, FN_SHIFT_BACKSPACE, + SID_UNDO, + SID_REDO, SID_REPEAT, SID_PASTE, + SID_PASTE_UNFORMATTED, FN_PASTE_NESTED_TABLE, FN_TABLE_PASTE_ROW_BEFORE, + FN_TABLE_PASTE_COL_BEFORE, SID_PASTE_SPECIAL, SID_SBA_BRW_INSERT, + SID_BACKGROUND_COLOR, FN_INSERT_BOOKMARK, SID_CHARMAP_CONTROL, + SID_CHARMAP, SID_EMOJI_CONTROL, FN_INSERT_SOFT_HYPHEN, + FN_INSERT_HARDHYPHEN, FN_INSERT_HARD_SPACE, FN_INSERT_NNBSP, + FN_INSERT_BREAK, FN_INSERT_LINEBREAK, FN_INSERT_COLUMN_BREAK, + FN_INSERT_BREAK_DLG, + FN_DELETE_SENT, FN_DELETE_BACK_SENT, FN_DELETE_WORD, + FN_DELETE_BACK_WORD, FN_DELETE_LINE, FN_DELETE_BACK_LINE, + FN_DELETE_PARA, FN_DELETE_BACK_PARA, FN_DELETE_WHOLE_LINE, + FN_CALCULATE, FN_FORMAT_RESET, + FN_POSTIT, FN_JAVAEDIT, SID_ATTR_PARA_ADJUST_LEFT, + SID_ATTR_PARA_ADJUST_RIGHT, SID_ATTR_PARA_ADJUST_CENTER,SID_ATTR_PARA_ADJUST_BLOCK, + SID_ATTR_PARA_LINESPACE_10, SID_ATTR_PARA_LINESPACE_15, SID_ATTR_PARA_LINESPACE_20, + SID_ATTR_CHAR_FONT, SID_ATTR_CHAR_FONTHEIGHT, SID_ATTR_CHAR_COLOR_BACKGROUND, + SID_ATTR_CHAR_COLOR_BACKGROUND_EXT, SID_ATTR_CHAR_COLOR_EXT, + SID_ATTR_CHAR_COLOR, SID_ATTR_CHAR_WEIGHT, SID_ATTR_CHAR_POSTURE, + SID_ATTR_CHAR_OVERLINE, + SID_ATTR_CHAR_UNDERLINE, SID_ATTR_FLASH, SID_ATTR_CHAR_STRIKEOUT, + SID_ULINE_VAL_SINGLE, SID_ULINE_VAL_DOUBLE, SID_ULINE_VAL_DOTTED, + SID_ATTR_CHAR_CONTOUR, SID_ATTR_CHAR_SHADOWED, + SID_ATTR_CHAR_AUTOKERN, SID_ATTR_CHAR_ESCAPEMENT, FN_SET_SUPER_SCRIPT, + FN_SET_SUB_SCRIPT, SID_ATTR_CHAR_CASEMAP, SID_ATTR_CHAR_LANGUAGE, + SID_ATTR_CHAR_KERNING, SID_CHAR_DLG, SID_ATTR_CHAR_WORDLINEMODE, + FN_GROW_FONT_SIZE, FN_SHRINK_FONT_SIZE, FN_TXTATR_INET, + FN_FORMAT_DROPCAPS, SID_ATTR_PARA_ADJUST, SID_ATTR_PARA_LINESPACE, + SID_ATTR_PARA_SPLIT, SID_ATTR_PARA_KEEP, SID_ATTR_PARA_WIDOWS, + SID_ATTR_PARA_ORPHANS, + SID_ATTR_PARA_MODEL, SID_PARA_DLG, + FN_SELECT_PARA, SID_DEC_INDENT, + SID_INC_INDENT + }; + static bool bFirst = true; + if ( bFirst ) + { + qsort( static_cast<void*>(aROIds), SAL_N_ELEMENTS(aROIds), sizeof(sal_uInt16), lcl_CmpIds ); + bFirst = false; + } + if ( SfxItemState::DISABLED == eStateRO ) + { + rDis.SetSlotFilter( SfxSlotFilterState::ENABLED_READONLY, aROIds ); + bChgd = true; + } + } + else if( m_pWrtShell->IsAllProtect() ) + { + if ( SfxItemState::DISABLED == eStateProtAll ) + { + static sal_uInt16 aAllProtIds[] = { SID_SAVEDOC, FN_EDIT_REGION }; + static bool bAllProtFirst = true; + if ( bAllProtFirst ) + { + qsort( static_cast<void*>(aAllProtIds), SAL_N_ELEMENTS(aAllProtIds), sizeof(sal_uInt16), lcl_CmpIds ); + bAllProtFirst = false; + } + rDis.SetSlotFilter( SfxSlotFilterState::ENABLED_READONLY, aAllProtIds ); + bChgd = true; + } + } + else if ( SfxItemState::DISABLED != eStateRO || + SfxItemState::DISABLED != eStateProtAll ) + { + bChgd = true; + rDis.SetSlotFilter(); + } + if ( bChgd ) + GetViewFrame()->GetBindings().InvalidateAll(true); +} + +void SwView::CheckReadonlySelection() +{ + SfxDisableFlags nDisableFlags = SfxDisableFlags::NONE; + SfxDispatcher &rDis = GetDispatcher(); + + if( m_pWrtShell->HasReadonlySel() && + ( !m_pWrtShell->GetDrawView() || + !m_pWrtShell->GetDrawView()->GetMarkedObjectList().GetMarkCount() )) + nDisableFlags |= SfxDisableFlags::SwOnProtectedCursor; + + if( (SfxDisableFlags::SwOnProtectedCursor & nDisableFlags ) != + (SfxDisableFlags::SwOnProtectedCursor & rDis.GetDisableFlags() ) ) + { + // Additionally move at the Window the InputContext, so that + // in japanese / chinese versions the external input will be + // turned on or off. This but only if the correct shell is on + // the stack. + switch( m_pViewImpl->GetShellMode() ) + { + case ShellMode::Text: + case ShellMode::ListText: + case ShellMode::TableText: + case ShellMode::TableListText: + { +// Temporary solution!!! Should set the font of the current insertion point +// at each cursor movement, so outside of this "if". But TH does not +// evaluates the font at this time and the "purchase" appears to me +// as too expensive. +// Moreover, we don't have a font, but only attributes from which the +// text formatting and the correct font will be build together. + + InputContext aCntxt( GetEditWin().GetInputContext() ); + aCntxt.SetOptions( SfxDisableFlags::SwOnProtectedCursor & nDisableFlags + ? (aCntxt.GetOptions() & ~ + InputContextFlags( InputContextFlags::Text | + InputContextFlags::ExtText )) + : (aCntxt.GetOptions() | + ( InputContextFlags::Text | + InputContextFlags::ExtText )) ); + GetEditWin().SetInputContext( aCntxt ); + } + break; + default: + ; + } + + } + + if( nDisableFlags != rDis.GetDisableFlags() ) + { + rDis.SetDisableFlags( nDisableFlags ); + GetViewFrame()->GetBindings().InvalidateAll( true ); + } +} + +SwView::SwView( SfxViewFrame *_pFrame, SfxViewShell* pOldSh ) + : SfxViewShell( _pFrame, SWVIEWFLAGS ), + m_nNewPage(USHRT_MAX), + m_nOldPageNum(0), + m_pNumRuleNodeFromDoc(nullptr), + m_pEditWin( VclPtr<SwEditWin>::Create( &_pFrame->GetWindow(), *this ) ), + m_pShell(nullptr), + m_pFormShell(nullptr), + m_pHScrollbar(nullptr), + m_pVScrollbar(nullptr), + m_pScrollFill(VclPtr<ScrollBarBox>::Create( &_pFrame->GetWindow(), WB_SIZEABLE )), + m_pVRuler(VclPtr<SvxRuler>::Create(&GetViewFrame()->GetWindow(), m_pEditWin, + SvxRulerSupportFlags::TABS | SvxRulerSupportFlags::PARAGRAPH_MARGINS_VERTICAL| + SvxRulerSupportFlags::BORDERS | SvxRulerSupportFlags::REDUCED_METRIC, + GetViewFrame()->GetBindings(), + WB_VSCROLL | WB_EXTRAFIELD | WB_BORDER )), + m_pLastTableFormat(nullptr), + m_pFormatClipboard(new SwFormatClipboard()), + m_nSelectionType( SelectionType::All ), + m_nPageCnt(0), + m_nDrawSfxId( USHRT_MAX ), + m_nFormSfxId( USHRT_MAX ), + m_nLastPasteDestination( static_cast<SotExchangeDest>(0xFFFF) ), + m_nLeftBorderDistance( 0 ), + m_nRightBorderDistance( 0 ), + m_eLastSearchCommand( static_cast<SvxSearchCmd>(0xFFFF) ), + m_bWheelScrollInProgress(false), + m_bCenterCursor(false), + m_bTopCursor(false), + m_bTabColFromDoc(false), + m_bTabRowFromDoc(false), + m_bSetTabColFromDoc(false), + m_bSetTabRowFromDoc(false), + m_bAttrChgNotified(false), + m_bAttrChgNotifiedWithRegistrations(false), + m_bVerbsActive(false), + m_bDrawRotate(false), + m_bDrawSelMode(true), + m_bShowAtResize(true), + m_bInOuterResizePixel(false), + m_bInInnerResizePixel(false), + m_bPasteState(false), + m_bPasteSpecialState(false), + m_bInMailMerge(false), + m_bInDtor(false), + m_bOldShellWasPagePreview(false), + m_bIsPreviewDoubleClick(false), + m_bMakeSelectionVisible(false), + m_nLOKPageUpDownOffset(0) +{ + static bool bRequestDoubleBuffering = getenv("VCL_DOUBLEBUFFERING_ENABLE"); + if (bRequestDoubleBuffering) + m_pEditWin->RequestDoubleBuffering(true); + + // According to discussion with MBA and further + // investigations, no old SfxViewShell will be set as parameter <pOldSh>, + // if function "New Window" is performed to open an additional view beside + // an already existing one. + // If the view is switch from one to another, the 'old' view is given by + // parameter <pOldSh>. + + bDocSzUpdated = true; + + CreateScrollbar( true ); + CreateScrollbar( false ); + + m_pViewImpl.reset(new SwView_Impl(this)); + SetName("View"); + SetWindow( m_pEditWin ); + + m_aTimer.SetTimeout( 120 ); + + SwDocShell& rDocSh = dynamic_cast<SwDocShell&>(*_pFrame->GetObjectShell()); + bool bOldModifyFlag = rDocSh.IsEnableSetModified(); + if (bOldModifyFlag) + rDocSh.EnableSetModified( false ); + // HACK: SwDocShell has some cached font info, VCL informs about font updates, + // but loading of docs with embedded fonts happens after SwDocShell is created + // but before SwEditWin (which handles the VCL event) is created. So update + // manually. + if (rDocSh.GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS )) + rDocSh.UpdateFontList(); + bool bWebDShell = dynamic_cast<const SwWebDocShell*>(&rDocSh) != nullptr; + + const SwMasterUsrPref *pUsrPref = SW_MOD()->GetUsrPref(bWebDShell); + SwViewOption aUsrPref( *pUsrPref); + + //! get lingu options without loading lingu DLL + SvtLinguOptions aLinguOpt; + SvtLinguConfig().GetOptions( aLinguOpt ); + aUsrPref.SetOnlineSpell( aLinguOpt.bIsSpellAuto ); + + bool bOldShellWasSrcView = false; + + // determine if there is an existing view for + // document + SfxViewShell* pExistingSh = nullptr; + if ( pOldSh ) + { + pExistingSh = pOldSh; + // determine type of existing view + if (SwPagePreview* pPagePreview = dynamic_cast<SwPagePreview *>(pExistingSh)) + { + m_sSwViewData = pPagePreview->GetPrevSwViewData(); + m_sNewCursorPos = pPagePreview->GetNewCursorPos(); + m_nNewPage = pPagePreview->GetNewPage(); + m_bOldShellWasPagePreview = true; + m_bIsPreviewDoubleClick = !m_sNewCursorPos.isEmpty() || m_nNewPage != USHRT_MAX; + } + else if (dynamic_cast<const SwSrcView *>(pExistingSh) != nullptr) + bOldShellWasSrcView = true; + } + + SAL_INFO( "sw.ui", "before create WrtShell" ); + if (SwView *pView = dynamic_cast<SwView*>(pExistingSh)) + { + m_pWrtShell.reset(new SwWrtShell(*pView->m_pWrtShell, m_pEditWin, *this)); + } + else if (SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>(rDocSh.GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell())) + { + m_pWrtShell.reset(new SwWrtShell(*pWrtShell, m_pEditWin, *this)); + } + else + { + SwDoc& rDoc = *rDocSh.GetDoc(); + + if( !bOldShellWasSrcView && bWebDShell && !m_bOldShellWasPagePreview ) + aUsrPref.setBrowseMode( true ); + else + aUsrPref.setBrowseMode( rDoc.getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) ); + + //For the BrowseMode we do not assume a factor. + if( aUsrPref.getBrowseMode() && aUsrPref.GetZoomType() != SvxZoomType::PERCENT ) + { + aUsrPref.SetZoomType( SvxZoomType::PERCENT ); + aUsrPref.SetZoom( 100 ); + } + if (rDocSh.IsPreview()) + { + aUsrPref.SetZoomType( SvxZoomType::WHOLEPAGE ); + aUsrPref.SetViewLayoutBookMode( false ); + aUsrPref.SetViewLayoutColumns( 1 ); + } + m_pWrtShell.reset(new SwWrtShell(rDoc, m_pEditWin, *this, &aUsrPref)); + // creating an SwView from a SwPagePreview needs to + // add the SwViewShell to the ring of the other SwViewShell(s) + if(m_bOldShellWasPagePreview) + { + SwViewShell& rPreviewViewShell = *static_cast<SwPagePreview*>(pExistingSh)->GetViewShell(); + m_pWrtShell->MoveTo(&rPreviewViewShell); + // to update the field command et.al. if necessary + const SwViewOption* pPreviewOpt = rPreviewViewShell.GetViewOptions(); + if( pPreviewOpt->IsFieldName() != aUsrPref.IsFieldName() || + pPreviewOpt->IsShowHiddenField() != aUsrPref.IsShowHiddenField() || + pPreviewOpt->IsShowHiddenPara() != aUsrPref.IsShowHiddenPara() || + pPreviewOpt->IsShowHiddenChar() != aUsrPref.IsShowHiddenChar() ) + rPreviewViewShell.ApplyViewOptions(aUsrPref); + // reset design mode at draw view for form + // shell, if needed. + if ( static_cast<SwPagePreview*>(pExistingSh)->ResetFormDesignMode() && + m_pWrtShell->HasDrawView() ) + { + SdrView* pDrawView = m_pWrtShell->GetDrawView(); + pDrawView->SetDesignMode( static_cast<SwPagePreview*>(pExistingSh)->FormDesignModeToReset() ); + } + } + } + SAL_INFO( "sw.ui", "after create WrtShell" ); + m_pHRuler = VclPtr<SwCommentRuler>::Create(m_pWrtShell.get(), &GetViewFrame()->GetWindow(), m_pEditWin, + SvxRulerSupportFlags::TABS | + SvxRulerSupportFlags::PARAGRAPH_MARGINS | + SvxRulerSupportFlags::BORDERS | + SvxRulerSupportFlags::NEGATIVE_MARGINS| + SvxRulerSupportFlags::REDUCED_METRIC, + GetViewFrame()->GetBindings(), + WB_STDRULER | WB_EXTRAFIELD | WB_BORDER); + + // assure that modified state of document + // isn't reset, if document is already modified. + const bool bIsDocModified = m_pWrtShell->GetDoc()->getIDocumentState().IsModified(); + + // Thus among other things, the HRuler is not displayed in the read-only case. + aUsrPref.SetReadonly( m_pWrtShell->GetViewOptions()->IsReadonly() ); + + // no margin for OLE! + Size aBrwsBorder; + if( SfxObjectCreateMode::EMBEDDED != rDocSh.GetCreateMode() ) + aBrwsBorder = GetMargin(); + + m_pWrtShell->SetBrowseBorder( aBrwsBorder ); + + // In CTOR no shell changes may take place, which must be temporarily stored + // with the timer. Otherwise, the SFX removes them from the stack! + bool bOld = g_bNoInterrupt; + g_bNoInterrupt = true; + + m_pHRuler->SetActive(); + m_pVRuler->SetActive(); + + SfxViewFrame* pViewFrame = GetViewFrame(); + + StartListening(*pViewFrame, DuplicateHandling::Prevent); + StartListening(rDocSh, DuplicateHandling::Prevent); + + // Set Zoom-factor from HRuler + Fraction aZoomFract( aUsrPref.GetZoom(), 100 ); + m_pHRuler->SetZoom( aZoomFract ); + m_pVRuler->SetZoom( aZoomFract ); + m_pHRuler->SetDoubleClickHdl(LINK( this, SwView, ExecRulerClick )); + FieldUnit eMetric = pUsrPref->GetHScrollMetric(); + m_pHRuler->SetUnit( eMetric ); + + eMetric = pUsrPref->GetVScrollMetric(); + m_pVRuler->SetUnit( eMetric ); + + m_pHRuler->SetCharWidth( 371 ); // default character width + m_pVRuler->SetLineHeight( 551 ); // default line height + + // Set DocShell + m_xGlueDocShell.reset(new SwViewGlueDocShell(*this, rDocSh)); + m_pPostItMgr.reset(new SwPostItMgr(this)); + + // Check and process the DocSize. Via the handler, the shell could not + // be found, because the shell is not known in the SFX management + // within the CTOR phase. + DocSzChgd( m_pWrtShell->GetDocSize() ); + + // Set AttrChangedNotify link + m_pWrtShell->SetChgLnk(LINK(this, SwView, AttrChangedNotify)); + + if (rDocSh.GetCreateMode() == SfxObjectCreateMode::EMBEDDED && + !rDocSh.GetVisArea(ASPECT_CONTENT).IsEmpty()) + SetVisArea(rDocSh.GetVisArea(ASPECT_CONTENT),false); + + SAL_WARN_IF( + officecfg::Office::Common::Undo::Steps::get() <= 0, + "sw.ui", "/org.openoffice.Office.Common/Undo/Steps <= 0"); + if (!utl::ConfigManager::IsFuzzing() && 0 < officecfg::Office::Common::Undo::Steps::get()) + { + m_pWrtShell->DoUndo(); + } + + const bool bBrowse = m_pWrtShell->GetViewOptions()->getBrowseMode(); + // Disable "multiple window" + SetNewWindowAllowed(!bBrowse); + // End of disabled multiple window + + m_bVScrollbarEnabled = aUsrPref.IsViewVScrollBar(); + m_bHScrollbarEnabled = aUsrPref.IsViewHScrollBar(); + m_pHScrollbar->SetAuto(bBrowse); + if( aUsrPref.IsViewHRuler() ) + CreateTab(); + if( aUsrPref.IsViewVRuler() ) + CreateVRuler(); + + m_pWrtShell->SetUIOptions( aUsrPref ); + m_pWrtShell->SetReadOnlyAvailable( aUsrPref.IsCursorInProtectedArea() ); + m_pWrtShell->ApplyAccessibilityOptions(SW_MOD()->GetAccessibilityOptions()); + + if( m_pWrtShell->GetDoc()->getIDocumentState().IsUpdateExpField() ) + { + if (m_pWrtShell->GetDoc()->GetDocumentFieldsManager().containsUpdatableFields()) + { + SET_CURR_SHELL(m_pWrtShell.get()); + m_pWrtShell->StartAction(); + m_pWrtShell->CalcLayout(); + m_pWrtShell->GetDoc()->getIDocumentFieldsAccess().UpdateFields(false); + m_pWrtShell->EndAction(); + } + m_pWrtShell->GetDoc()->getIDocumentState().SetUpdateExpFieldStat( false ); + } + + // Update all tables if necessary: + if( m_pWrtShell->GetDoc()->IsUpdateTOX() ) + { + SfxRequest aSfxRequest( FN_UPDATE_TOX, SfxCallMode::SLOT, GetPool() ); + Execute( aSfxRequest ); + m_pWrtShell->GetDoc()->SetUpdateTOX( false ); // reset again + m_pWrtShell->SttEndDoc(true); + } + + // No ResetModified, if there is already a view to this doc. + SfxViewFrame* pVFrame = GetViewFrame(); + SfxViewFrame* pFirst = SfxViewFrame::GetFirst(&rDocSh); + // Currently(360) the view is registered firstly after the CTOR, + // the following expression is also working if this changes. + // If the modification cannot be canceled by undo, then do NOT set + // the modify back. + // no reset of modified state, if document + // was already modified. + if (!m_pWrtShell->GetDoc()->GetIDocumentUndoRedo().IsUndoNoResetModified() && + ( !pFirst || pFirst == pVFrame ) && + !bIsDocModified ) + { + m_pWrtShell->ResetModified(); + } + + g_bNoInterrupt = bOld; + + // If a new GlobalDoc will be created, the navigator will also be generated. + if( dynamic_cast<const SwGlobalDocShell*>(&rDocSh) != nullptr && + !pVFrame->GetChildWindow( SID_NAVIGATOR )) + { + SfxBoolItem aNavi(SID_NAVIGATOR, true); + GetDispatcher().ExecuteList(SID_NAVIGATOR, SfxCallMode::ASYNCHRON, { &aNavi }); + } + + uno::Reference< frame::XFrame > xFrame = pVFrame->GetFrame().GetFrameInterface(); + + uno::Reference< frame::XFrame > xBeamerFrame = xFrame->findFrame( + "_beamer", frame::FrameSearchFlag::CHILDREN); + if(xBeamerFrame.is()) + { + SwDBData aData = m_pWrtShell->GetDBData(); + SwModule::ShowDBObj( *this, aData ); + } + + // has anybody calls the attrchanged handler in the constructor? + if( m_bAttrChgNotifiedWithRegistrations ) + { + GetViewFrame()->GetBindings().LEAVEREGISTRATIONS(); + if( m_aTimer.IsActive() ) + m_aTimer.Stop(); + } + + m_aTimer.SetInvokeHandler(LINK(this, SwView, TimeoutHdl)); + m_aTimer.SetDebugName( "sw::SwView m_aTimer" ); + m_bAttrChgNotified = m_bAttrChgNotifiedWithRegistrations = false; + if (bOldModifyFlag) + rDocSh.EnableSetModified(); + InvalidateBorder(); + + if( !m_pHScrollbar->IsVisible( true ) ) + ShowHScrollbar( false ); + if( !m_pVScrollbar->IsVisible( true ) ) + ShowVScrollbar( false ); + + GetViewFrame()->GetWindow().AddChildEventListener( LINK( this, SwView, WindowChildEventListener ) ); +} + +SwViewGlueDocShell::SwViewGlueDocShell(SwView& rView, SwDocShell& rDocSh) + : m_rView(rView) +{ + // Set DocShell + rDocSh.SetView(&m_rView); + SW_MOD()->SetView(&m_rView); +} + +SwViewGlueDocShell::~SwViewGlueDocShell() +{ + SwDocShell* pDocSh = m_rView.GetDocShell(); + if (pDocSh && pDocSh->GetView() == &m_rView) + pDocSh->SetView(nullptr); + if (SW_MOD()->GetView() == &m_rView) + SW_MOD()->SetView(nullptr); +} + +SwView::~SwView() +{ + // Notify other LOK views that we are going away. + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"); + + // Need to remove activated field's button before disposing EditWin. + GetWrtShell().getIDocumentMarkAccess()->ClearFieldActivation(); + + GetViewFrame()->GetWindow().RemoveChildEventListener( LINK( this, SwView, WindowChildEventListener ) ); + m_pPostItMgr.reset(); + + m_bInDtor = true; + m_pEditWin->Hide(); // prevent problems with painting + + // Set pointer in SwDocShell to the view again + m_xGlueDocShell.reset(); + + if( m_aTimer.IsActive() && m_bAttrChgNotifiedWithRegistrations ) + GetViewFrame()->GetBindings().LEAVEREGISTRATIONS(); + + // the last view must end the text edit + SdrView *pSdrView = m_pWrtShell ? m_pWrtShell->GetDrawView() : nullptr; + if( pSdrView && pSdrView->IsTextEdit() ) + pSdrView->SdrEndTextEdit( true ); + + SetWindow( nullptr ); + + m_pViewImpl->Invalidate(); + EndListening(*GetViewFrame()); + EndListening(*GetDocShell()); + m_pScrollFill.disposeAndClear(); + m_pWrtShell.reset(); // reset here so that it is not accessible by the following dtors. + m_pHScrollbar.disposeAndClear(); + m_pVScrollbar.disposeAndClear(); + m_pHRuler.disposeAndClear(); + m_pVRuler.disposeAndClear(); + m_pGlosHdl.reset(); + m_pViewImpl.reset(); + + // If this was enabled in the ctor for the frame, then disable it here. + static bool bRequestDoubleBuffering = getenv("VCL_DOUBLEBUFFERING_ENABLE"); + if (bRequestDoubleBuffering) + m_pEditWin->RequestDoubleBuffering(false); + m_pEditWin.disposeAndClear(); + + m_pFormatClipboard.reset(); +} + +SwDocShell* SwView::GetDocShell() +{ + SfxObjectShell* pDocShell = GetViewFrame()->GetObjectShell(); + return dynamic_cast<SwDocShell*>( pDocShell ); +} + +// Remember CursorPos + +void SwView::WriteUserData( OUString &rUserData, bool bBrowse ) +{ + // The browse flag will be passed from Sfx when documents are browsed + // (not to be confused with the BrowseMode). + // Then that stored data are not persistent! + + const SwRect& rRect = m_pWrtShell->GetCharRect(); + const tools::Rectangle& rVis = GetVisArea(); + + rUserData = OUString::number( rRect.Left() ); + rUserData += ";"; + rUserData += OUString::number( rRect.Top() ); + rUserData += ";"; + rUserData += OUString::number( m_pWrtShell->GetViewOptions()->GetZoom() ); + rUserData += ";"; + rUserData += OUString::number( rVis.Left() ); + rUserData += ";"; + rUserData += OUString::number( rVis.Top() ); + rUserData += ";"; + rUserData += OUString::number( bBrowse ? SAL_MIN_INT32 : rVis.Right()); + rUserData += ";"; + rUserData += OUString::number( bBrowse ? SAL_MIN_INT32 : rVis.Bottom()); + rUserData += ";"; + rUserData += OUString::number( + static_cast<sal_uInt16>(m_pWrtShell->GetViewOptions()->GetZoomType()));//eZoom; + rUserData += ";"; + rUserData += FrameTypeFlags::NONE == m_pWrtShell->GetSelFrameType() ? OUStringLiteral("0") : OUStringLiteral("1"); +} + +// Set CursorPos + +static bool lcl_IsOwnDocument( SwView& rView ) +{ + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + rView.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + OUString Created = xDocProps->getAuthor(); + OUString Changed = xDocProps->getModifiedBy(); + OUString FullName = SW_MOD()->GetUserOptions().GetFullName(); + return (!FullName.isEmpty() && + (!Changed.isEmpty() && Changed == FullName )) || + (Changed.isEmpty() && !Created.isEmpty() && Created == FullName ); +} + +void SwView::ReadUserData( const OUString &rUserData, bool bBrowse ) +{ + if ( rUserData.indexOf(';')>=0 && // more than one token + // For document without layout only in the onlinelayout or + // while forward/backward + (!m_pWrtShell->IsNewLayout() || m_pWrtShell->GetViewOptions()->getBrowseMode() || bBrowse) ) + { + bool bIsOwnDocument = lcl_IsOwnDocument( *this ); + + SET_CURR_SHELL(m_pWrtShell.get()); + + sal_Int32 nPos = 0; + + // No it is *not* a good idea to call GetToken within Point constr. immediately, + // because which parameter is evaluated first? + long nX = rUserData.getToken( 0, ';', nPos ).toInt32(), + nY = rUserData.getToken( 0, ';', nPos ).toInt32(); + Point aCursorPos( nX, nY ); + + sal_uInt16 nZoomFactor = + static_cast< sal_uInt16 >( rUserData.getToken(0, ';', nPos ).toInt32() ); + + long nLeft = rUserData.getToken(0, ';', nPos ).toInt32(), + nTop = rUserData.getToken(0, ';', nPos ).toInt32(), + nRight = rUserData.getToken(0, ';', nPos ).toInt32(), + nBottom= rUserData.getToken(0, ';', nPos ).toInt32(); + + const long nAdd = m_pWrtShell->GetViewOptions()->getBrowseMode() ? DOCUMENTBORDER : DOCUMENTBORDER*2; + if ( nBottom <= (m_pWrtShell->GetDocSize().Height()+nAdd) ) + { + m_pWrtShell->EnableSmooth( false ); + + const tools::Rectangle aVis( nLeft, nTop, nRight, nBottom ); + + sal_Int32 nOff = 0; + SvxZoomType eZoom; + if( !m_pWrtShell->GetViewOptions()->getBrowseMode() ) + eZoom = static_cast<SvxZoomType>(static_cast<sal_uInt16>(rUserData.getToken(nOff, ';', nPos ).toInt32())); + else + { + eZoom = SvxZoomType::PERCENT; + ++nOff; + } + + bool bSelectObj = (0 != rUserData.getToken( nOff, ';', nPos ).toInt32()) + && m_pWrtShell->IsObjSelectable( aCursorPos ); + + // restore editing position + m_pViewImpl->SetRestorePosition(aCursorPos, bSelectObj); + // set flag value to avoid macro execution. + bool bSavedFlagValue = m_pWrtShell->IsMacroExecAllowed(); + m_pWrtShell->SetMacroExecAllowed( false ); +// os: changed: The user data has to be read if the view is switched back from page preview +// go to the last editing position when opening own files + if(m_bOldShellWasPagePreview || bIsOwnDocument) + { + m_pWrtShell->SwCursorShell::SetCursor( aCursorPos, !bSelectObj ); + if( bSelectObj ) + { + m_pWrtShell->SelectObj( aCursorPos ); + m_pWrtShell->EnterSelFrameMode( &aCursorPos ); + } + } + + // reset flag value + m_pWrtShell->SetMacroExecAllowed( bSavedFlagValue ); + + // set visible area before applying + // information from print preview. Otherwise, the applied information + // is lost. +// os: changed: The user data has to be read if the view is switched back from page preview +// go to the last editing position when opening own files + if(m_bOldShellWasPagePreview || bIsOwnDocument ) + { + if ( bBrowse ) + SetVisArea( aVis.TopLeft() ); + else + SetVisArea( aVis ); + } + + //apply information from print preview - if available + if( !m_sNewCursorPos.isEmpty() ) + { + sal_Int32 nIdx{ 0 }; + const long nXTmp = m_sNewCursorPos.getToken( 0, ';', nIdx ).toInt32(); + const long nYTmp = m_sNewCursorPos.getToken( 0, ';', nIdx ).toInt32(); + Point aCursorPos2( nXTmp, nYTmp ); + bSelectObj = m_pWrtShell->IsObjSelectable( aCursorPos2 ); + + m_pWrtShell->SwCursorShell::SetCursor( aCursorPos2 ); + if( bSelectObj ) + { + m_pWrtShell->SelectObj( aCursorPos2 ); + m_pWrtShell->EnterSelFrameMode( &aCursorPos2 ); + } + m_pWrtShell->MakeSelVisible(); + m_sNewCursorPos.clear(); + } + else if(USHRT_MAX != m_nNewPage) + { + m_pWrtShell->GotoPage(m_nNewPage, true); + m_nNewPage = USHRT_MAX; + } + + SelectShell(); + + m_pWrtShell->StartAction(); + const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); + if( pVOpt->GetZoom() != nZoomFactor || pVOpt->GetZoomType() != eZoom ) + SetZoom( eZoom, nZoomFactor); + + m_pWrtShell->LockView( true ); + m_pWrtShell->EndAction(); + m_pWrtShell->LockView( false ); + m_pWrtShell->EnableSmooth( true ); + } + } +} + +void SwView::ReadUserDataSequence ( const uno::Sequence < beans::PropertyValue >& rSequence ) +{ + if(GetDocShell()->IsPreview()||m_bIsPreviewDoubleClick) + return; + bool bIsOwnDocument = lcl_IsOwnDocument( *this ); + if (!rSequence.hasElements()) + return; + + SET_CURR_SHELL(m_pWrtShell.get()); + const SwRect& rRect = m_pWrtShell->GetCharRect(); + const tools::Rectangle &rVis = GetVisArea(); + const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); + + sal_Int64 nX = rRect.Left(), nY = rRect.Top(), nLeft = rVis.Left(), nTop = rVis.Top(); + sal_Int64 nRight = nLeft; + sal_Int64 nBottom = LONG_MIN; + sal_Int16 nZoomType = static_cast< sal_Int16 >(pVOpt->GetZoomType()); + sal_Int16 nZoomFactor = static_cast < sal_Int16 > (pVOpt->GetZoom()); + bool bViewLayoutBookMode = pVOpt->IsViewLayoutBookMode(); + sal_Int16 nViewLayoutColumns = pVOpt->GetViewLayoutColumns(); + + bool bSelectedFrame = ( m_pWrtShell->GetSelFrameType() != FrameTypeFlags::NONE ), + bGotVisibleLeft = false, + bGotVisibleTop = false, bGotVisibleRight = false, + bGotVisibleBottom = false, bGotZoomType = false, + bGotZoomFactor = false, bGotIsSelectedFrame = false, + bGotViewLayoutColumns = false, bGotViewLayoutBookMode = false, + bBrowseMode = false, bGotBrowseMode = false; + + for (const beans::PropertyValue& rValue : rSequence) + { + if ( rValue.Name == "ViewLeft" ) + { + rValue.Value >>= nX; + nX = convertMm100ToTwip( nX ); + } + else if ( rValue.Name == "ViewTop" ) + { + rValue.Value >>= nY; + nY = convertMm100ToTwip( nY ); + } + else if ( rValue.Name == "VisibleLeft" ) + { + rValue.Value >>= nLeft; + nLeft = convertMm100ToTwip( nLeft ); + bGotVisibleLeft = true; + } + else if ( rValue.Name == "VisibleTop" ) + { + rValue.Value >>= nTop; + nTop = convertMm100ToTwip( nTop ); + bGotVisibleTop = true; + } + else if ( rValue.Name == "VisibleRight" ) + { + rValue.Value >>= nRight; + nRight = convertMm100ToTwip( nRight ); + bGotVisibleRight = true; + } + else if ( rValue.Name == "VisibleBottom" ) + { + rValue.Value >>= nBottom; + nBottom = convertMm100ToTwip( nBottom ); + bGotVisibleBottom = true; + } + else if ( rValue.Name == "ZoomType" ) + { + rValue.Value >>= nZoomType; + bGotZoomType = true; + } + else if ( rValue.Name == "ZoomFactor" ) + { + rValue.Value >>= nZoomFactor; + bGotZoomFactor = true; + } + else if ( rValue.Name == "ViewLayoutColumns" ) + { + rValue.Value >>= nViewLayoutColumns; + bGotViewLayoutColumns = true; + } + else if ( rValue.Name == "ViewLayoutBookMode" ) + { + bViewLayoutBookMode = *o3tl::doAccess<bool>(rValue.Value); + bGotViewLayoutBookMode = true; + } + else if ( rValue.Name == "IsSelectedFrame" ) + { + rValue.Value >>= bSelectedFrame; + bGotIsSelectedFrame = true; + } + else if (rValue.Name == "ShowOnlineLayout") + { + rValue.Value >>= bBrowseMode; + bGotBrowseMode = true; + } + // Fallback to common SdrModel processing + else + GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->ReadUserDataSequenceValue(&rValue); + } + if (bGotBrowseMode) + { + // delegate further + GetViewImpl()->GetUNOObject_Impl()->getViewSettings()->setPropertyValue("ShowOnlineLayout", uno::Any(bBrowseMode)); + } + if (bGotVisibleBottom) + { + Point aCursorPos( nX, nY ); + const long nAdd = m_pWrtShell->GetViewOptions()->getBrowseMode() ? DOCUMENTBORDER : DOCUMENTBORDER*2; + if (nBottom <= (m_pWrtShell->GetDocSize().Height()+nAdd) ) + { + m_pWrtShell->EnableSmooth( false ); + const tools::Rectangle aVis( nLeft, nTop, nRight, nBottom ); + + SvxZoomType eZoom; + if ( !m_pWrtShell->GetViewOptions()->getBrowseMode() ) + eZoom = static_cast < SvxZoomType > ( nZoomType ); + else + { + eZoom = SvxZoomType::PERCENT; + } + if (bGotIsSelectedFrame) + { + bool bSelectObj = bSelectedFrame && m_pWrtShell->IsObjSelectable( aCursorPos ); + + // set flag value to avoid macro execution. + bool bSavedFlagValue = m_pWrtShell->IsMacroExecAllowed(); + m_pWrtShell->SetMacroExecAllowed( false ); +// os: changed: The user data has to be read if the view is switched back from page preview +// go to the last editing position when opening own files + m_pViewImpl->SetRestorePosition(aCursorPos, bSelectObj); + if(m_bOldShellWasPagePreview|| bIsOwnDocument) + { + m_pWrtShell->SwCursorShell::SetCursor( aCursorPos, !bSelectObj ); + + // Update the shell to toggle Header/Footer edit if needed + bool bInHeader = true; + if ( m_pWrtShell->IsInHeaderFooter( &bInHeader ) ) + { + if ( !bInHeader ) + { + m_pWrtShell->SetShowHeaderFooterSeparator( FrameControlType::Footer, true ); + m_pWrtShell->SetShowHeaderFooterSeparator( FrameControlType::Header, false ); + } + else + { + m_pWrtShell->SetShowHeaderFooterSeparator( FrameControlType::Header, true ); + m_pWrtShell->SetShowHeaderFooterSeparator( FrameControlType::Footer, false ); + } + + // Force repaint + m_pWrtShell->GetWin()->Invalidate(); + } + if ( m_pWrtShell->IsInHeaderFooter() != m_pWrtShell->IsHeaderFooterEdit() ) + m_pWrtShell->ToggleHeaderFooterEdit(); + + if( bSelectObj ) + { + m_pWrtShell->SelectObj( aCursorPos ); + m_pWrtShell->EnterSelFrameMode( &aCursorPos ); + } + } + + // reset flag value + m_pWrtShell->SetMacroExecAllowed( bSavedFlagValue ); + } + SelectShell(); + + // Set ViewLayoutSettings + const bool bSetViewLayoutSettings = bGotViewLayoutColumns && bGotViewLayoutBookMode && + ( pVOpt->GetViewLayoutColumns() != nViewLayoutColumns || pVOpt->IsViewLayoutBookMode() != bViewLayoutBookMode ); + + const bool bSetViewSettings = bGotZoomType && bGotZoomFactor && + ( pVOpt->GetZoom() != nZoomFactor || pVOpt->GetZoomType() != eZoom ); + + // In case we have a 'fixed' view layout of 2 or more columns, + // we have to apply the view options *before* starting the action. + // Otherwise the SetZoom function cannot work correctly, because + // the view layout hasn't been calculated. + const bool bZoomNeedsViewLayout = bSetViewLayoutSettings && + 1 < nViewLayoutColumns && + bSetViewSettings && + eZoom != SvxZoomType::PERCENT; + + if ( !bZoomNeedsViewLayout ) + m_pWrtShell->StartAction(); + + if ( bSetViewLayoutSettings ) + SetViewLayout( nViewLayoutColumns, bViewLayoutBookMode, true ); + + if ( bZoomNeedsViewLayout ) + m_pWrtShell->StartAction(); + + if ( bSetViewSettings ) + SetZoom( eZoom, nZoomFactor, true ); + +// os: changed: The user data has to be read if the view is switched back from page preview +// go to the last editing position when opening own files + if(m_bOldShellWasPagePreview||bIsOwnDocument) + { + if ( bGotVisibleLeft && bGotVisibleTop ) + { + Point aTopLeft(aVis.TopLeft()); + // make sure the document is still centered + const SwTwips lBorder = IsDocumentBorder() ? DOCUMENTBORDER : 2 * DOCUMENTBORDER; + SwTwips nEditWidth = GetEditWin().GetOutputSize().Width(); + if(nEditWidth > (m_aDocSz.Width() + lBorder )) + aTopLeft.setX( ( m_aDocSz.Width() + lBorder - nEditWidth ) / 2 ); + else + { + //check if the values are possible + long nXMax = m_pHScrollbar->GetRangeMax() - m_pHScrollbar->GetVisibleSize(); + if( aTopLeft.X() > nXMax ) + aTopLeft.setX( nXMax < 0 ? 0 : nXMax ); + } + SetVisArea( aTopLeft ); + } + else if (bGotVisibleLeft && bGotVisibleTop && bGotVisibleRight && bGotVisibleBottom ) + SetVisArea( aVis ); + } + + m_pWrtShell->LockView( true ); + m_pWrtShell->EndAction(); + m_pWrtShell->LockView( false ); + m_pWrtShell->EnableSmooth( true ); + } + } + +} + +void SwView::WriteUserDataSequence ( uno::Sequence < beans::PropertyValue >& rSequence ) +{ + const SwRect& rRect = m_pWrtShell->GetCharRect(); + const tools::Rectangle& rVis = GetVisArea(); + + std::vector<beans::PropertyValue> aVector; + + sal_uInt16 nViewID( GetViewFrame()->GetCurViewId()); + aVector.push_back(comphelper::makePropertyValue("ViewId", "view" + OUString::number(nViewID))); + + aVector.push_back(comphelper::makePropertyValue("ViewLeft", convertTwipToMm100 ( rRect.Left() ))); + + aVector.push_back(comphelper::makePropertyValue("ViewTop", convertTwipToMm100 ( rRect.Top() ))); + + auto visibleLeft = convertTwipToMm100 ( rVis.Left() ); + aVector.push_back(comphelper::makePropertyValue("VisibleLeft", visibleLeft)); + + auto visibleTop = convertTwipToMm100 ( rVis.Top() ); + aVector.push_back(comphelper::makePropertyValue("VisibleTop", visibleTop)); + + auto visibleRight = rVis.IsWidthEmpty() ? visibleLeft : convertTwipToMm100 ( rVis.Right() ); + aVector.push_back(comphelper::makePropertyValue("VisibleRight", visibleRight)); + + auto visibleBottom = rVis.IsHeightEmpty() ? visibleTop : convertTwipToMm100 ( rVis.Bottom() ); + aVector.push_back(comphelper::makePropertyValue("VisibleBottom", visibleBottom)); + + const sal_Int16 nZoomType = static_cast< sal_Int16 >(m_pWrtShell->GetViewOptions()->GetZoomType()); + aVector.push_back(comphelper::makePropertyValue("ZoomType", nZoomType)); + + const sal_Int16 nViewLayoutColumns = static_cast< sal_Int16 >(m_pWrtShell->GetViewOptions()->GetViewLayoutColumns()); + aVector.push_back(comphelper::makePropertyValue("ViewLayoutColumns", nViewLayoutColumns)); + + aVector.push_back(comphelper::makePropertyValue("ViewLayoutBookMode", m_pWrtShell->GetViewOptions()->IsViewLayoutBookMode())); + + aVector.push_back(comphelper::makePropertyValue("ZoomFactor", static_cast < sal_Int16 > (m_pWrtShell->GetViewOptions()->GetZoom()))); + + aVector.push_back(comphelper::makePropertyValue("IsSelectedFrame", FrameTypeFlags::NONE != m_pWrtShell->GetSelFrameType())); + + rSequence = comphelper::containerToSequence(aVector); + + // Common SdrModel processing + GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->WriteUserDataSequence(rSequence); +} + +void SwView::ShowCursor( bool bOn ) +{ + //don't scroll the cursor into the visible area + bool bUnlockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); //lock visible section + + if( !bOn ) + m_pWrtShell->HideCursor(); + else if( !m_pWrtShell->IsFrameSelected() && !m_pWrtShell->IsObjSelected() ) + m_pWrtShell->ShowCursor(); + + if( bUnlockView ) + m_pWrtShell->LockView( false ); +} + +ErrCode SwView::DoVerb( long nVerb ) +{ + if ( !GetViewFrame()->GetFrame().IsInPlace() ) + { + SwWrtShell &rSh = GetWrtShell(); + const SelectionType nSel = rSh.GetSelectionType(); + if ( nSel & SelectionType::Ole ) + rSh.LaunchOLEObj( nVerb ); + } + return ERRCODE_NONE; +} + +// only return true for a text selection + +bool SwView::HasSelection( bool bText ) const +{ + return bText ? GetWrtShell().SwCursorShell::HasSelection() + : GetWrtShell().HasSelection(); +} + +OUString SwView::GetSelectionText( bool bCompleteWrds ) +{ + return GetSelectionTextParam( bCompleteWrds, true ); +} + +OUString SwView::GetSelectionTextParam( bool bCompleteWrds, bool bEraseTrail ) +{ + OUString sReturn; + if( bCompleteWrds && !GetWrtShell().HasSelection() ) + GetWrtShell().SelWrd(); + + GetWrtShell().GetSelectedText( sReturn ); + if( bEraseTrail ) + sReturn = comphelper::string::stripEnd(sReturn, ' '); + return sReturn; +} + +SwGlossaryHdl* SwView::GetGlosHdl() +{ + if(!m_pGlosHdl) + m_pGlosHdl.reset(new SwGlossaryHdl(GetViewFrame(), m_pWrtShell.get())); + return m_pGlosHdl.get(); +} + +void SwView::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + bool bCallBase = true; + if(dynamic_cast<const FmDesignModeChangedHint*>(&rHint)) + { + bool bDesignMode = static_cast<const FmDesignModeChangedHint&>(rHint).GetDesignMode(); + if (!bDesignMode && GetDrawFuncPtr()) + { + GetDrawFuncPtr()->Deactivate(); + SetDrawFuncPtr(nullptr); + LeaveDrawCreate(); + AttrChangedNotify(nullptr); + } + } + else + { + SfxHintId nId = rHint.GetId(); + switch ( nId ) + { + // sub shells will be destroyed by the + // dispatcher, if the view frame is dying. Thus, reset member <pShell>. + case SfxHintId::Dying: + { + if ( &rBC == GetViewFrame() ) + { + ResetSubShell(); + } + } + break; + case SfxHintId::ModeChanged: + { + // Modal mode change-over? + bool bModal = GetDocShell()->IsInModalMode(); + m_pHRuler->SetActive( !bModal ); + m_pVRuler->SetActive( !bModal ); + } + + [[fallthrough]]; + + case SfxHintId::TitleChanged: + if ( GetDocShell()->IsReadOnly() != GetWrtShell().GetViewOptions()->IsReadonly() ) + { + SwWrtShell &rSh = GetWrtShell(); + rSh.SetReadonlyOption( GetDocShell()->IsReadOnly() ); + + if ( rSh.GetViewOptions()->IsViewVRuler() ) + CreateVRuler(); + else + KillVRuler(); + if ( rSh.GetViewOptions()->IsViewHRuler() ) + CreateTab(); + else + KillTab(); + bool bReadonly = GetDocShell()->IsReadOnly(); + // if document is to be opened in alive-mode then this has to be + // regarded while switching from readonly-mode to edit-mode + if( !bReadonly ) + { + SwDrawModel * pDrawDoc = GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + if (pDrawDoc) + { + if( !pDrawDoc->GetOpenInDesignMode() ) + break;// don't touch the design mode + } + } + SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadonly); + GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::ASYNCHRON, { &aItem }); + } + break; + + case SfxHintId::SwDrawViewsCreated: + { + bCallBase = false; + if ( GetFormShell() ) + { + GetFormShell()->SetView(dynamic_cast<FmFormView*>(GetWrtShell().GetDrawView())); + SfxBoolItem aItem( SID_FM_DESIGN_MODE, !GetDocShell()->IsReadOnly()); + GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::SYNCHRON, { &aItem }); + } + } + break; + case SfxHintId::RedlineChanged: + { + static sal_uInt16 const aSlotRedLine[] = { + FN_REDLINE_ACCEPT_DIRECT, + FN_REDLINE_REJECT_DIRECT, + FN_REDLINE_NEXT_CHANGE, + FN_REDLINE_PREV_CHANGE, + FN_REDLINE_ACCEPT_ALL, + FN_REDLINE_REJECT_ALL, + 0 + }; + GetViewFrame()->GetBindings().Invalidate(aSlotRedLine); + } + break; + + default: break; + } + } + + if ( bCallBase ) + SfxViewShell::Notify(rBC, rHint); +} + +#if defined(_WIN32) || defined UNX + +void SwView::ScannerEventHdl() +{ + uno::Reference< XScannerManager2 > xScanMgr = SW_MOD()->GetScannerManager(); + if( xScanMgr.is() ) + { + const ScannerContext aContext( xScanMgr->getAvailableScanners().getConstArray()[ 0 ] ); + const ScanError eError = xScanMgr->getError( aContext ); + + if( ScanError_ScanErrorNone == eError ) + { + const uno::Reference< awt::XBitmap > xBitmap( xScanMgr->getBitmap( aContext ) ); + + if( xBitmap.is() ) + { + const BitmapEx aScanBmp( VCLUnoHelper::GetBitmap( xBitmap ) ); + + if( !!aScanBmp ) + { + Graphic aGrf(aScanBmp); + m_pWrtShell->Insert( OUString(), OUString(), aGrf ); + } + } + } + } + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_TWAIN_SELECT ); + rBind.Invalidate( SID_TWAIN_TRANSFER ); +} +#endif + +void SwView::StopShellTimer() +{ + if(m_aTimer.IsActive()) + { + m_aTimer.Stop(); + if ( m_bAttrChgNotifiedWithRegistrations ) + { + GetViewFrame()->GetBindings().LEAVEREGISTRATIONS(); + m_bAttrChgNotifiedWithRegistrations = false; + } + SelectShell(); + m_bAttrChgNotified = false; + } +} + +bool SwView::PrepareClose( bool bUI ) +{ + SfxViewFrame* pVFrame = GetViewFrame(); + pVFrame->SetChildWindow( SwInputChild::GetChildWindowId(), false ); + if( pVFrame->GetDispatcher()->IsLocked() ) + pVFrame->GetDispatcher()->Lock(false); + + if ( m_pFormShell && !m_pFormShell->PrepareClose( bUI ) ) + { + return false; + } + return SfxViewShell::PrepareClose( bUI ); +} + +// status methods for clipboard. +// Status changes now notified from the clipboard. +bool SwView::IsPasteAllowed() +{ + SotExchangeDest nPasteDestination = SwTransferable::GetSotDestination( *m_pWrtShell ); + if( m_nLastPasteDestination != nPasteDestination ) + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + &GetEditWin()) ); + if( aDataHelper.GetXTransferable().is() ) + { + m_bPasteState = SwTransferable::IsPaste( *m_pWrtShell, aDataHelper ); + m_bPasteSpecialState = SwTransferable::IsPasteSpecial( + *m_pWrtShell, aDataHelper ); + } + else + m_bPasteState = m_bPasteSpecialState = false; + + if( static_cast<SotExchangeDest>(0xFFFF) == m_nLastPasteDestination ) // the init value + m_pViewImpl->AddClipboardListener(); + m_nLastPasteDestination = nPasteDestination; + } + return m_bPasteState; +} + +bool SwView::IsPasteSpecialAllowed() +{ + if ( m_pFormShell && m_pFormShell->IsActiveControl() ) + return false; + + SotExchangeDest nPasteDestination = SwTransferable::GetSotDestination( *m_pWrtShell ); + if( m_nLastPasteDestination != nPasteDestination ) + { + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + &GetEditWin()) ); + if( aDataHelper.GetXTransferable().is() ) + { + m_bPasteState = SwTransferable::IsPaste( *m_pWrtShell, aDataHelper ); + m_bPasteSpecialState = SwTransferable::IsPasteSpecial( + *m_pWrtShell, aDataHelper ); + } + else + m_bPasteState = m_bPasteSpecialState = false; + + if( static_cast<SotExchangeDest>(0xFFFF) == m_nLastPasteDestination ) // the init value + m_pViewImpl->AddClipboardListener(); + } + return m_bPasteSpecialState; +} + +bool SwView::IsPasteSpreadsheet(bool bHasOwnTableCopied) +{ + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromSystemClipboard( + &GetEditWin()) ); + if( aDataHelper.GetXTransferable().is() ) + { + if (bHasOwnTableCopied && SwTransferable::IsPasteOwnFormat( aDataHelper )) + return true; + return aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) || aDataHelper.HasFormat( SotClipboardFormatId::SYLK_BIGCAPS ); + } + return false; +} + +void SwView::NotifyDBChanged() +{ + GetViewImpl()->GetUNOObject_Impl()->NotifyDBChanged(); +} + +// Printing + +SfxObjectShellLock SwView::CreateTmpSelectionDoc() +{ + SwXTextView *const pTempImpl = GetViewImpl()->GetUNOObject_Impl(); + return pTempImpl->BuildTmpSelectionDoc(); +} + +void SwView::AddTransferable(SwTransferable& rTransferable) +{ + GetViewImpl()->AddTransferable(rTransferable); +} + +namespace sw { + +void InitPrintOptionsFromApplication(SwPrintData & o_rData, bool const bWeb) +{ + o_rData = *SW_MOD()->GetPrtOptions(bWeb); +} + +} // namespace sw + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/view0.cxx b/sw/source/uibase/uiview/view0.cxx new file mode 100644 index 000000000..d15c0bcc1 --- /dev/null +++ b/sw/source/uibase/uiview/view0.cxx @@ -0,0 +1,648 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <SwSpellDialogChildWindow.hxx> +#include <svl/eitem.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/linguprops.hxx> +#include <unotools/lingucfg.hxx> +#include <viewopt.hxx> +#include <globals.h> +#include <sfx2/infobar.hxx> +#include <sfx2/request.hxx> +#include <svl/whiter.hxx> +#include <svx/srchdlg.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <uivwimp.hxx> +#include <avmedia/mediaplayer.hxx> +#include <swmodule.hxx> +#include <com/sun/star/linguistic2/XLinguProperties.hpp> + +#include <sfx2/objface.hxx> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <globals.hrc> +#include <cmdid.h> +#include <globdoc.hxx> +#include <wview.hxx> + +#define ShellClass_SwView +#define ShellClass_Text +#define ShellClass_TextDrawText + +#include <sfx2/msg.hxx> +#include <swslots.hxx> +#include <PostItMgr.hxx> + +using namespace ::com::sun::star; + +#include <unotools/moduleoptions.hxx> +#include <sfx2/viewfac.hxx> + +#include <memory> +#include <swabstdlg.hxx> + +SFX_IMPL_NAMED_VIEWFACTORY(SwView, "Default") +{ + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsWriter()) + { + SFX_VIEW_REGISTRATION(SwDocShell); + SFX_VIEW_REGISTRATION(SwGlobalDocShell); + } +} + +SFX_IMPL_INTERFACE(SwView, SfxViewShell) + +void SwView::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true); + + GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SwSpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(FN_REDLINE_ACCEPT); + GetStaticInterface()->RegisterChildWindow(SID_HYPERLINK_DIALOG); + GetStaticInterface()->RegisterChildWindow(FN_WORDCOUNT_DIALOG); +#if HAVE_FEATURE_AVMEDIA + GetStaticInterface()->RegisterChildWindow(::avmedia::MediaPlayer::GetChildWindowId()); +#endif + GetStaticInterface()->RegisterChildWindow(FN_INSERT_FIELD_DATA_ONLY); + + GetStaticInterface()->RegisterChildWindow(FN_SYNC_LABELS, false, SfxShellFeature::SwChildWindowLabel); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard|SfxVisibilityFlags::Server, + ToolbarId::Tools_Toolbox); +} + + +ShellMode SwView::GetShellMode() const +{ + return m_pViewImpl->GetShellMode(); +} + +view::XSelectionSupplier* SwView::GetUNOObject() +{ + return m_pViewImpl->GetUNOObject(); +} + +void SwView::ApplyAccessibilityOptions(SvtAccessibilityOptions const & rAccessibilityOptions) +{ + m_pWrtShell->ApplyAccessibilityOptions(rAccessibilityOptions); + //to enable the right state of the selection cursor in readonly documents + if(GetDocShell()->IsReadOnly()) + m_pWrtShell->ShowCursor(); + +} + +void SwView::SetMailMergeConfigItem(std::shared_ptr<SwMailMergeConfigItem> const & rConfigItem) +{ + m_pViewImpl->SetMailMergeConfigItem(rConfigItem); + UIFeatureChanged(); +} + +std::shared_ptr<SwMailMergeConfigItem> const & SwView::GetMailMergeConfigItem() const +{ + return m_pViewImpl->GetMailMergeConfigItem(); +} + +static bool lcl_IsViewMarks( const SwViewOption& rVOpt ) +{ + return rVOpt.IsHardBlank() && + rVOpt.IsSoftHyph() && + SwViewOption::IsFieldShadings(); +} +static void lcl_SetViewMarks(SwViewOption& rVOpt, bool bOn ) +{ + rVOpt.SetHardBlank(bOn); + rVOpt.SetSoftHyph(bOn); + SwViewOption::SetAppearanceFlag( + ViewOptFlags::FieldShadings, bOn, true); +} + +static void lcl_SetViewMetaChars( SwViewOption& rVOpt, bool bOn) +{ + rVOpt.SetViewMetaChars( bOn ); + if(bOn && !(rVOpt.IsParagraph() || + rVOpt.IsTab() || + rVOpt.IsLineBreak() || + rVOpt.IsShowHiddenChar() || + rVOpt.IsShowBookmarks() || + rVOpt.IsBlank())) + { + rVOpt.SetParagraph(bOn); + rVOpt.SetTab(bOn); + rVOpt.SetLineBreak(bOn); + rVOpt.SetBlank(bOn); + rVOpt.SetShowHiddenChar(bOn); + rVOpt.SetShowBookmarks(bOn); + } +} + +void SwView::RecheckBrowseMode() +{ + // OS: pay attention to numerical order! + static sal_uInt16 const aInva[] = + { + //SID_NEWWINDOW,/*5620*/ + SID_BROWSER_MODE, /*6313*/ + SID_RULER_BORDERS, SID_RULER_PAGE_POS, + //SID_ATTR_LONG_LRSPACE, + SID_HTML_MODE, + SID_RULER_PROTECT, /* 10915 */ + //SID_AUTOSPELL_CHECK, + //SID_AUTOSPELL_MARKOFF, + SID_TOGGLE_RESOLVED_NOTES, /* 11672*/ + FN_RULER, /*20211*/ + FN_VIEW_GRAPHIC, /*20213*/ + FN_VIEW_BOUNDS, /**/ + FN_VIEW_FIELDS, /*20215*/ + FN_VLINEAL, /*20216*/ + FN_VSCROLLBAR, /*20217*/ + FN_HSCROLLBAR, /*20218*/ + FN_VIEW_META_CHARS, /**/ + FN_VIEW_MARKS, /**/ + //FN_VIEW_FIELDNAME, /**/ + FN_VIEW_TABLEGRID, /*20227*/ + FN_PRINT_LAYOUT, /*20237*/ + FN_QRY_MERGE, /*20364*/ + FN_SHADOWCURSOR, /**/ + 0 + }; + // the view must not exist! + GetViewFrame()->GetBindings().Invalidate(aInva); + CheckVisArea(); + + SvxZoomType eType; + if( GetWrtShell().GetViewOptions()->getBrowseMode() && SvxZoomType::PERCENT != (eType = + GetWrtShell().GetViewOptions()->GetZoomType()) ) + SetZoom( eType ); + InvalidateBorder(); +} + +// State of view options + +void SwView::StateViewOptions(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxBoolItem aBool; + const SwViewOption* pOpt = GetWrtShell().GetViewOptions(); + + while(nWhich) + { + bool bReadonly = GetDocShell()->IsReadOnly(); + if ( bReadonly && nWhich != FN_VIEW_GRAPHIC ) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + switch(nWhich) + { + case FN_RULER: + { + if(!pOpt->IsViewHRuler(true) && !pOpt->IsViewVRuler(true)) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + else + aBool.SetValue( pOpt->IsViewAnyRuler()); + } + break; + case SID_BROWSER_MODE: + case FN_PRINT_LAYOUT: + { + bool bState = pOpt->getBrowseMode(); + if(FN_PRINT_LAYOUT == nWhich) + bState = !bState; + aBool.SetValue( bState ); + } + break; + case FN_VIEW_BOUNDS: + aBool.SetValue( SwViewOption::IsDocBoundaries()); break; + case FN_VIEW_GRAPHIC: + aBool.SetValue( pOpt->IsGraphic() ); break; + case FN_VIEW_FIELDS: + aBool.SetValue( SwViewOption::IsFieldShadings() ); break; + case FN_VIEW_FIELDNAME: + aBool.SetValue( pOpt->IsFieldName() ); break; + case FN_VIEW_MARKS: + aBool.SetValue( lcl_IsViewMarks(*pOpt) ); break; + case FN_VIEW_META_CHARS: + aBool.SetValue( pOpt->IsViewMetaChars() ); break; + case FN_VIEW_TABLEGRID: + aBool.SetValue( SwViewOption::IsTableBoundaries() ); break; + case SID_TOGGLE_NOTES: + { + if (!GetPostItMgr()->HasNotes()) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + else + aBool.SetValue( pOpt->IsPostIts()); + break; + } + case SID_TOGGLE_RESOLVED_NOTES: + { + if (!GetPostItMgr()->HasNotes()) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + else + aBool.SetValue( pOpt->IsResolvedPostIts()); + break; + } + case FN_VIEW_HIDDEN_PARA: + aBool.SetValue( pOpt->IsShowHiddenPara()); break; + case FN_VIEW_HIDE_WHITESPACE: + { + if (pOpt->getBrowseMode() || !pOpt->CanHideWhitespace()) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + else + aBool.SetValue(pOpt->IsHideWhitespaceMode()); + break; + } + case FN_VIEW_SHOW_WHITESPACE: + { + aBool.SetValue(!pOpt->IsHideWhitespaceMode()); + break; + } + case SID_GRID_VISIBLE: + aBool.SetValue( pOpt->IsGridVisible() ); break; + case SID_GRID_USE: + aBool.SetValue( pOpt->IsSnap() ); break; + case SID_HELPLINES_MOVE: + aBool.SetValue( pOpt->IsCrossHair() ); break; + case FN_VIEW_SMOOTH_SCROLL: + aBool.SetValue( pOpt->IsSmoothScroll()); break; + case FN_VLINEAL: + aBool.SetValue( StatVRuler() ); break; + case FN_HSCROLLBAR: + if( pOpt->getBrowseMode() ) + { + rSet.DisableItem(nWhich); + nWhich = 0; + } + else + aBool.SetValue( IsHScrollbarVisible() ); + break; + case FN_VSCROLLBAR: + aBool.SetValue( IsVScrollbarVisible() ); break; + case SID_AUTOSPELL_CHECK: + aBool.SetValue( pOpt->IsOnlineSpell() ); + break; + case FN_SHADOWCURSOR: + if ( pOpt->getBrowseMode() ) + { + rSet.DisableItem( nWhich ); + nWhich = 0; + } + else + aBool.SetValue( pOpt->IsShadowCursor() ); + break; + case FN_SHOW_INLINETOOLTIPS: + aBool.SetValue( pOpt->IsShowInlineTooltips() ); + break; + case FN_USE_HEADERFOOTERMENU: + aBool.SetValue( pOpt->IsUseHeaderFooterMenu() ); + break; + } + + if( nWhich ) + { + aBool.SetWhich( nWhich ); + rSet.Put( aBool ); + } + nWhich = aIter.NextWhich(); + } +} + +// execute view options + +void SwView::ExecViewOptions(SfxRequest &rReq) +{ + std::unique_ptr<SwViewOption> pOpt(new SwViewOption( *GetWrtShell().GetViewOptions() )); + bool bModified = GetWrtShell().IsModified(); + + int eState = STATE_TOGGLE; + bool bSet = false; + bool bBrowseModeChanged = false; + + const SfxItemSet *pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + const SfxPoolItem* pAttr=nullptr; + + if( pArgs && SfxItemState::SET == pArgs->GetItemState( nSlot , false, &pAttr )) + { + bSet = static_cast<const SfxBoolItem*>(pAttr)->GetValue(); + eState = bSet ? STATE_ON : STATE_OFF; + } + + bool bFlag = STATE_ON == eState; + uno::Reference< linguistic2::XLinguProperties > xLngProp( ::GetLinguPropertySet() ); + + switch ( nSlot ) + { + case FN_VIEW_GRAPHIC: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsGraphic(); + pOpt->SetGraphic( bFlag ); + break; + + case FN_VIEW_FIELDS: + if( STATE_TOGGLE == eState ) + bFlag = !SwViewOption::IsFieldShadings() ; + SwViewOption::SetAppearanceFlag(ViewOptFlags::FieldShadings, bFlag, true ); + break; + + case FN_VIEW_BOUNDS: + if( STATE_TOGGLE == eState ) + bFlag = !SwViewOption::IsDocBoundaries(); + SwViewOption::SetAppearanceFlag(ViewOptFlags::DocBoundaries, bFlag, true ); + break; + + case SID_GRID_VISIBLE: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsGridVisible(); + + pOpt->SetGridVisible( bFlag ); + break; + + case SID_GRID_USE: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsSnap(); + + pOpt->SetSnap( bFlag ); + break; + + case SID_HELPLINES_MOVE: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsCrossHair(); + + pOpt->SetCrossHair( bFlag ); + break; + + case SID_BROWSER_MODE: + bBrowseModeChanged = !pOpt->getBrowseMode(); + pOpt->setBrowseMode(true ); + break; + + case FN_PRINT_LAYOUT: + bBrowseModeChanged = pOpt->getBrowseMode(); + pOpt->setBrowseMode( false ); + break; + + case SID_TOGGLE_NOTES: + if ( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsPostIts(); + + GetPostItMgr()->SetLayout(); + pOpt->SetPostIts( bFlag ); + if (pOpt->IsPostIts()) + GetPostItMgr()->CheckMetaText(); + break; + + case SID_TOGGLE_RESOLVED_NOTES: + if ( STATE_TOGGLE == eState ) + bFlag = pOpt->IsResolvedPostIts(); + + GetPostItMgr()->ShowHideResolvedNotes(!bFlag); + + GetPostItMgr()->SetLayout(); + pOpt->SetResolvedPostIts( !bFlag ); + + break; + + case FN_VIEW_HIDDEN_PARA: + if ( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsShowHiddenPara(); + + pOpt->SetShowHiddenPara( bFlag ); + break; + + case FN_VIEW_HIDE_WHITESPACE: + if ( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsHideWhitespaceMode(); + + pOpt->SetHideWhitespaceMode(bFlag); + break; + + case FN_VIEW_SHOW_WHITESPACE: + if ( STATE_TOGGLE == eState ) + bFlag = pOpt->IsHideWhitespaceMode(); + + pOpt->SetHideWhitespaceMode(!bFlag); + break; + + case FN_VIEW_SMOOTH_SCROLL: + + if ( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsSmoothScroll(); + + pOpt->SetSmoothScroll( bFlag ); + break; + + case FN_VLINEAL: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsViewVRuler(); + + pOpt->SetViewVRuler( bFlag ); + break; + + case FN_VSCROLLBAR: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsViewVScrollBar(); + + pOpt->SetViewVScrollBar( bFlag ); + break; + + case FN_HSCROLLBAR: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsViewHScrollBar(); + + pOpt->SetViewHScrollBar( bFlag ); + break; + + case FN_RULER: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsViewAnyRuler(); + + pOpt->SetViewAnyRuler( bFlag ); + break; + + case FN_VIEW_TABLEGRID: + if( STATE_TOGGLE == eState ) + bFlag = !SwViewOption::IsTableBoundaries(); + SwViewOption::SetAppearanceFlag(ViewOptFlags::TableBoundaries, bFlag, true ); + break; + + case FN_VIEW_FIELDNAME: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsFieldName() ; + + pOpt->SetFieldName( bFlag ); + break; + + case FN_VIEW_MARKS: + if( STATE_TOGGLE == eState ) + bFlag = !lcl_IsViewMarks(*pOpt) ; + + lcl_SetViewMarks( *pOpt, bFlag ); + break; + + case FN_VIEW_META_CHARS: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsViewMetaChars(); + + lcl_SetViewMetaChars( *pOpt, bFlag ); + break; + + case SID_AUTOSPELL_CHECK: + if( STATE_TOGGLE == eState ) + { + bFlag = !pOpt->IsOnlineSpell(); + bSet = bFlag; + } + + pOpt->SetOnlineSpell(bSet); + { + SvtLinguConfig aCfg; + aCfg.SetProperty( UPN_IS_SPELL_AUTO, uno::makeAny( bSet ) ); + + if (xLngProp.is()) + xLngProp->setIsSpellAuto( bSet ); + + // for the time being we do not have a specific option for grammarchecking. + // thus we'll use the one for spell checking... + if (bSet) + { + SwDocShell *pDocSh = GetDocShell(); + SwDoc *pDoc = pDocSh? pDocSh->GetDoc() : nullptr; + + // right now we don't have view options for automatic grammar checking. Thus... + bool bIsAutoGrammar = false; + aCfg.GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bIsAutoGrammar; + + if (pDoc && bIsAutoGrammar) + pDoc->StartGrammarChecking(); + } + } + break; + + case FN_SHADOWCURSOR: + if( STATE_TOGGLE == eState ) + { + bFlag = !pOpt->IsShadowCursor(); + bSet = bFlag; + } + + pOpt->SetShadowCursor(bSet); + break; + + case FN_SHOW_INLINETOOLTIPS: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsShowInlineTooltips(); + + pOpt->SetShowInlineTooltips( bFlag ); + break; + + case FN_USE_HEADERFOOTERMENU: + if( STATE_TOGGLE == eState ) + bFlag = !pOpt->IsUseHeaderFooterMenu(); + + pOpt->SetUseHeaderFooterMenu( bFlag ); + break; + + default: + OSL_FAIL("wrong request method"); + return; + } + + // Set UserPrefs, mark request as modified + bool bWebView = dynamic_cast<const SwWebView*>(this) != nullptr; + SwWrtShell &rSh = GetWrtShell(); + rSh.StartAction(); + SwModule* pModule = SW_MOD(); + if( *rSh.GetViewOptions() != *pOpt ) + { + rSh.ApplyViewOptions( *pOpt ); + if( bBrowseModeChanged ) + { + GetDocShell()->ToggleLayoutMode(this); + } + + // The UsrPref must be marked as modified. + // call for initialization + pModule->GetUsrPref(bWebView); + SwModule::CheckSpellChanges( pOpt->IsOnlineSpell(), false, false, false ); + } + //OS: Set back modified again, because view/fields sets the Doc modified. + if( !bModified ) + rSh.ResetModified(); + + pModule->ApplyUsrPref( *pOpt, this, bWebView ? SvViewOpt::DestWeb : SvViewOpt::DestText ); + + // #i6193# let postits know about new spellcheck setting + if ( nSlot == SID_AUTOSPELL_CHECK ) + GetPostItMgr()->SetSpellChecking(); + + const bool bLockedView = rSh.IsViewLocked(); + rSh.LockView( true ); //lock visible section + GetWrtShell().EndAction(); + if( bBrowseModeChanged && !bFlag ) + CalcVisArea( GetEditWin().GetOutputSizePixel() ); + rSh.LockView( bLockedView ); + + pOpt.reset(); + Invalidate(rReq.GetSlot()); + if(!pArgs) + rReq.AppendItem(SfxBoolItem(nSlot, bFlag)); + rReq.Done(); +} + +void SwView::ExecFormatFootnote() +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSwFootNoteOptionDlg(GetFrameWeld(), GetWrtShell())); + pDlg->Execute(); +} + +void SwView::ExecNumberingOutline(SfxItemPool & rPool) +{ + SfxItemSet aTmp(rPool, svl::Items<FN_PARAM_1, FN_PARAM_1>{}); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateOutlineTabDialog(GetFrameWeld(), &aTmp, GetWrtShell())); + pDlg->Execute(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/view1.cxx b/sw/source/uibase/uiview/view1.cxx new file mode 100644 index 000000000..1484b3005 --- /dev/null +++ b/sw/source/uibase/uiview/view1.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 <svl/eitem.hxx> +#include <svx/ruler.hxx> +#include <idxmrk.hxx> +#include <view.hxx> +#include <basesh.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <docsh.hxx> +#include <fldwrap.hxx> +#include <redlndlg.hxx> +#include <edtwin.hxx> +#include <formatclipboard.hxx> +#include <cmdid.h> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <wordcountdialog.hxx> + +void SwView::Activate(bool bMDIActivate) +{ + // fdo#40438 Update the layout to make sure everything is correct before showing the content + m_pWrtShell->StartAction(); + m_pWrtShell->EndAction( true ); + + // Register the current View at the DocShell. + // The view remains active at the DocShell until it will + // be destroyed or by Activate a new one will be set. + SwDocShell* pDocSh = GetDocShell(); + if(pDocSh) + pDocSh->SetView(this); + SwModule* pSwMod = SW_MOD(); + pSwMod->SetView(this); + + // Document size has changed. + if(!bDocSzUpdated) + DocSzChgd(m_aDocSz); + + // make selection visible + if(m_bMakeSelectionVisible) + { + m_pWrtShell->MakeSelVisible(); + m_bMakeSelectionVisible = false; + } + m_pHRuler->SetActive(); + m_pVRuler->SetActive(); + + if ( bMDIActivate ) + { + if ( m_pShell ) + { + SfxDispatcher &rDispatcher = GetDispatcher(); + SfxShell *pTopShell = rDispatcher.GetShell( 0 ); + + // this SwView is the top-most shell on the stack + if ( pTopShell == this ) + { + for ( sal_uInt16 i = 1; true; ++i ) + { + SfxShell *pSfxShell = rDispatcher.GetShell( i ); + // does the stack contain any shells spawned by this SwView already? + if ( ( dynamic_cast< const SwBaseShell *>( pSfxShell ) != nullptr + || dynamic_cast< const FmFormShell *>( pSfxShell ) != nullptr ) + && ( pSfxShell->GetViewShell() == this ) ) + { + // it shouldn't b/c we haven't been activated yet + // so assert that 'cause it'll crash during dispose at the latest + assert( pSfxShell && "Corrupted shell stack: dependent shell positioned below its view"); + } + else + break; + } + } + } + + m_pWrtShell->ShellGetFocus(); // Selections visible + + if( !m_sSwViewData.isEmpty() ) + { + ReadUserData(m_sSwViewData); + m_sSwViewData.clear(); + } + + AttrChangedNotify(nullptr); + + // Initialize Fielddlg newly if necessary (e.g. for TYP_SETVAR) + sal_uInt16 nId = SwFieldDlgWrapper::GetChildWindowId(); + SfxViewFrame* pVFrame = GetViewFrame(); + SwFieldDlgWrapper *pWrp = static_cast<SwFieldDlgWrapper*>(pVFrame->GetChildWindow(nId)); + if (pWrp) + pWrp->ReInitDlg(GetDocShell()); + + // Initialize RedlineDlg newly if necessary + nId = SwRedlineAcceptChild::GetChildWindowId(); + SwRedlineAcceptChild *pRed = static_cast<SwRedlineAcceptChild*>(pVFrame->GetChildWindow(nId)); + if (pRed) + pRed->ReInitDlg(GetDocShell()); + + // reinit IdxMarkDlg + nId = SwInsertIdxMarkWrapper::GetChildWindowId(); + SwInsertIdxMarkWrapper *pIdxMrk = static_cast<SwInsertIdxMarkWrapper*>(pVFrame->GetChildWindow(nId)); + if (pIdxMrk) + pIdxMrk->ReInitDlg(*m_pWrtShell); + + // reinit AuthMarkDlg + nId = SwInsertAuthMarkWrapper::GetChildWindowId(); + SwInsertAuthMarkWrapper *pAuthMrk = static_cast<SwInsertAuthMarkWrapper*>(pVFrame-> + GetChildWindow(nId)); + if (pAuthMrk) + pAuthMrk->ReInitDlg(*m_pWrtShell); + } + else + // At least call the Notify (as a precaution because of the SlotFilter). + AttrChangedNotify(nullptr); + + SfxViewShell::Activate(bMDIActivate); +} + +void SwView::Deactivate(bool bMDIActivate) +{ + if( g_bFlushCharBuffer ) // Are Characters still in the input buffer? + GetEditWin().FlushInBuffer(); + + if( bMDIActivate ) + { + m_pWrtShell->ShellLoseFocus(); // Selections invisible + + m_pHRuler->SetActive( false ); + m_pVRuler->SetActive( false ); + } + SfxViewShell::Deactivate(bMDIActivate); +} + +void SwView::MarginChanged() +{ + GetWrtShell().SetBrowseBorder( GetMargin() ); +} + +void SwView::ExecFormatPaintbrush(SfxRequest const & rReq) +{ + if(!m_pFormatClipboard) + return; + + if( m_pFormatClipboard->HasContent() ) + { + m_pFormatClipboard->Erase(); + + SwApplyTemplate aTemplate; + GetEditWin().SetApplyTemplate(aTemplate); + } + else + { + bool bPersistentCopy = false; + const SfxItemSet *pArgs = rReq.GetArgs(); + if( pArgs && pArgs->Count() >= 1 ) + { + bPersistentCopy = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue(); + } + + m_pFormatClipboard->Copy( GetWrtShell(), GetPool(), bPersistentCopy ); + + SwApplyTemplate aTemplate; + aTemplate.m_pFormatClipboard = m_pFormatClipboard.get(); + GetEditWin().SetApplyTemplate(aTemplate); + } + GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); +} + +void SwView::StateFormatPaintbrush(SfxItemSet &rSet) +{ + if(!m_pFormatClipboard) + return; + + const bool bHasContent = m_pFormatClipboard->HasContent(); + if( !bHasContent && + !SwFormatClipboard::CanCopyThisType( GetWrtShell().GetSelectionType()) + ) + { + rSet.DisableItem( SID_FORMATPAINTBRUSH ); + } + else + rSet.Put(SfxBoolItem(SID_FORMATPAINTBRUSH, bHasContent)); +} + +void SwView::UpdateWordCount(SfxShell* pShell, sal_uInt16 nSlot) +{ + SfxViewFrame* pVFrame = GetViewFrame(); + if (pVFrame != nullptr) + { + pVFrame->ToggleChildWindow(FN_WORDCOUNT_DIALOG); + pShell->Invalidate(nSlot); + + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(pVFrame->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/view2.cxx b/sw/source/uibase/uiview/view2.cxx new file mode 100644 index 000000000..6245bf7f4 --- /dev/null +++ b/sw/source/uibase/uiview/view2.cxx @@ -0,0 +1,2573 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/util/SearchAlgorithms2.hpp> +#include <o3tl/any.hxx> +#include <vcl/graphicfilter.hxx> +#include <com/sun/star/sdb/DatabaseContext.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ListboxControlActions.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/linguistic2/XProofreadingIterator.hpp> +#include <com/sun/star/linguistic2/XDictionary.hpp> +#include <SwCapObjType.hxx> +#include <SwStyleNameMapper.hxx> +#include <docary.hxx> +#include <hintids.hxx> +#include <SwRewriter.hxx> +#include <numrule.hxx> +#include <swundo.hxx> +#include <svl/PasswordHelper.hxx> +#include <svl/urihelper.hxx> +#include <svtools/miscopt.hxx> +#include <sfx2/passwd.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/filedlghelper.hxx> +#include <editeng/langitem.hxx> +#include <svx/viewlayoutitem.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/linkwarn.hxx> +#include <sfx2/htmlmode.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/app.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <editeng/lrspitem.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <editeng/unolingu.hxx> +#include <vcl/weld.hxx> +#include <editeng/tstpitem.hxx> +#include <sfx2/event.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <editeng/sizeitem.hxx> +#include <sfx2/dispatch.hxx> +#include <svl/whiter.hxx> +#include <svl/ptitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/errinf.hxx> +#include <tools/urlobj.hxx> +#include <svx/svdview.hxx> +#include <swtypes.hxx> +#include <swwait.hxx> +#include <redlndlg.hxx> +#include <gotodlg.hxx> +#include <view.hxx> +#include <uivwimp.hxx> +#include <docsh.hxx> +#include <doc.hxx> +#include <printdata.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <basesh.hxx> +#include <swmodule.hxx> +#include <uitool.hxx> +#include <shellio.hxx> +#include <fmtinfmt.hxx> +#include <mdiexp.hxx> +#include <drawbase.hxx> +#include <frmatr.hxx> +#include <frmmgr.hxx> +#include <pagedesc.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <edtwin.hxx> +#include <wview.hxx> +#include <cmdid.h> +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <strings.hrc> +#include <swerror.h> +#include <globals.hrc> +#include <fmtclds.hxx> +#include <sfx2/templatedlg.hxx> +#include <dbconfig.hxx> +#include <dbmgr.hxx> +#include <reffld.hxx> +#include <comphelper/lok.hxx> + +#include <PostItMgr.hxx> + +#include <comphelper/processfactory.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <svx/svxdlg.hxx> +#include <swabstdlg.hxx> +#include <fmthdft.hxx> +#include <unotextrange.hxx> +#include <docstat.hxx> +#include <wordcountdialog.hxx> +#include <sfx2/sidebar/Sidebar.hxx> + +#include <vcl/GraphicNativeTransform.hxx> +#include <vcl/GraphicNativeMetadata.hxx> +#include <vcl/settings.hxx> +#include <i18nutil/searchopt.hxx> +#include <paratr.hxx> +#include <rootfrm.hxx> +#include <frameformats.hxx> + +#include <memory> + +const char sStatusDelim[] = " : "; + +using namespace sfx2; +using namespace ::com::sun::star; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::scanner; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::ui::dialogs; + +static void lcl_SetAllTextToDefaultLanguage( SwWrtShell &rWrtSh, sal_uInt16 nWhichId ) +{ + if (!(nWhichId == RES_CHRATR_LANGUAGE || + nWhichId == RES_CHRATR_CJK_LANGUAGE || + nWhichId == RES_CHRATR_CTL_LANGUAGE)) + return; + + rWrtSh.StartAction(); + rWrtSh.LockView( true ); + rWrtSh.Push(); + + // prepare to apply new language to all text in document + rWrtSh.SelAll(); + rWrtSh.ExtendedSelectAll(); + + // set language attribute to default for all text + std::set<sal_uInt16> aAttribs; + aAttribs.insert( nWhichId ); + rWrtSh.ResetAttr( aAttribs ); + + rWrtSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rWrtSh.LockView( false ); + rWrtSh.EndAction(); + +} + +/** + * Create string for showing the page number in the statusbar + * + * @param nPhyNum The physical page number + * @param nVirtNum The logical page number (user-assigned) + * @param rPgStr User-defined page name (will be shown if different from logical page number) + * + * @return OUString Formatted string: Page 1 of 10 (Page 1 of 8 to print OR Page nVirtNumv/rPgStr) + **/ +OUString SwView::GetPageStr(sal_uInt16 nPhyNum, sal_uInt16 nVirtNum, const OUString& rPgStr) +{ + // Show user-defined page number in brackets if any. + OUString extra; + if (!rPgStr.isEmpty() && OUString::number(nPhyNum) != rPgStr) + extra = rPgStr; + else if (nPhyNum != nVirtNum) + extra = OUString::number(nVirtNum); + + sal_uInt16 nPageCount = GetWrtShell().GetPageCnt(); + sal_uInt16 nPrintedPhyNum = nPhyNum; + sal_uInt16 nPrintedPageCount = nPageCount; + if (!GetWrtShell().getIDocumentDeviceAccess().getPrintData().IsPrintEmptyPages()) + SwDoc::CalculateNonBlankPages(*m_pWrtShell->GetLayout(), nPrintedPageCount, nPrintedPhyNum); + // Show printed page numbers only, when they are different + OUString aStr( nPageCount != nPrintedPageCount + ? SwResId(STR_PAGE_COUNT_PRINTED) + : (extra.isEmpty() ? SwResId(STR_PAGE_COUNT) : SwResId(STR_PAGE_COUNT_CUSTOM))); + aStr = aStr.replaceFirst("%1", OUString::number(nPhyNum)); + aStr = aStr.replaceFirst("%2", OUString::number(nPageCount)); + if (nPageCount != nPrintedPageCount) + { + aStr = aStr.replaceFirst("%3", OUString::number(nPrintedPhyNum)); + aStr = aStr.replaceFirst("%4", OUString::number(nPrintedPageCount)); + } + else + aStr = aStr.replaceFirst("%3", extra); + + return aStr; +} + +ErrCode SwView::InsertGraphic( const OUString &rPath, const OUString &rFilter, + bool bLink, GraphicFilter *pFilter ) +{ + SwWait aWait( *GetDocShell(), true ); + + Graphic aGraphic; + ErrCode aResult = ERRCODE_NONE; + if( !pFilter ) + { + pFilter = &GraphicFilter::GetGraphicFilter(); + } + aResult = GraphicFilter::LoadGraphic( rPath, rFilter, aGraphic, pFilter ); + + if( ERRCODE_NONE == aResult ) + { + GraphicNativeMetadata aMetadata; + if ( aMetadata.read(aGraphic) ) + { + const sal_uInt16 aRotation = aMetadata.getRotation(); + if (aRotation != 0) + { + GraphicNativeTransform aTransform( aGraphic ); + aTransform.rotate( aRotation ); + } + } + + SwFlyFrameAttrMgr aFrameManager( true, GetWrtShellPtr(), Frmmgr_Type::GRF, nullptr ); + SwWrtShell& rShell = GetWrtShell(); + + // #i123922# determine if we really want to insert or replace the graphic at a selected object + const bool bReplaceMode(rShell.HasSelection() && SelectionType::Frame == rShell.GetSelectionType()); + + if(bReplaceMode) + { + // #i123922# Do same as in D&D, ReRead graphic and all is done + rShell.ReRead( + bLink ? rPath : OUString(), + bLink ? rFilter : OUString(), + &aGraphic); + } + else + { + rShell.StartAction(); + if( bLink ) + { + SwDocShell* pDocSh = GetDocShell(); + INetURLObject aTemp( + pDocSh->HasName() ? + pDocSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) : + OUString()); + + OUString sURL = URIHelper::SmartRel2Abs( + aTemp, rPath, URIHelper::GetMaybeFileHdl() ); + aGraphic.setOriginURL(sURL); + rShell.Insert( sURL, rFilter, aGraphic, &aFrameManager ); + } + else + { + rShell.Insert( OUString(), OUString(), aGraphic, &aFrameManager ); + } + + // it is too late after "EndAction" because the Shell can already be destroyed. + rShell.EndAction(); + } + } + return aResult; +} + +bool SwView::InsertGraphicDlg( SfxRequest& rReq ) +{ + bool bReturn = false; + SwDocShell* pDocShell = GetDocShell(); + SwDoc* pDoc = pDocShell->GetDoc(); + + OUString sGraphicFormat = SwResId(STR_POOLFRM_GRAPHIC); + +// No file pickers in a non-desktop (mobile app) build. + +#if HAVE_FEATURE_DESKTOP + // when in HTML mode insert only as a link + const sal_uInt16 nHtmlMode = ::GetHtmlMode(pDocShell); + std::unique_ptr<FileDialogHelper> pFileDlg(new FileDialogHelper( + ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE, + FileDialogFlags::Graphic, GetFrameWeld())); + pFileDlg->SetTitle(SwResId(STR_INSERT_GRAPHIC )); + pFileDlg->SetContext( FileDialogHelper::SW_INSERT_GRAPHIC ); + + uno::Reference < XFilePicker3 > xFP = pFileDlg->GetFilePicker(); + uno::Reference < XFilePickerControlAccess > xCtrlAcc(xFP, UNO_QUERY); + if(nHtmlMode & HTMLMODE_ON) + { + xCtrlAcc->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, makeAny(true)); + xCtrlAcc->enableControl( ExtendedFilePickerElementIds::CHECKBOX_LINK, false); + } + + std::vector<OUString> aFormats; + const size_t nArrLen = pDoc->GetFrameFormats()->size(); + for( size_t i = 0; i < nArrLen; ++i ) + { + const SwFrameFormat* pFormat = (*pDoc->GetFrameFormats())[ i ]; + if(pFormat->IsDefault() || pFormat->IsAuto()) + continue; + aFormats.push_back(pFormat->GetName()); + } + + // pool formats + + const std::vector<OUString>& rFramePoolArr( + SwStyleNameMapper::GetFrameFormatUINameArray()); + for(const auto & i : rFramePoolArr) + { + aFormats.push_back(i); + } + + std::sort(aFormats.begin(), aFormats.end()); + aFormats.erase(std::unique(aFormats.begin(), aFormats.end()), aFormats.end()); + + Sequence<OUString> aListBoxEntries(aFormats.size()); + OUString* pEntries = aListBoxEntries.getArray(); + sal_Int16 nSelect = 0; + for( size_t i = 0; i < aFormats.size(); ++i ) + { + pEntries[i] = aFormats[i]; + if(pEntries[i] == sGraphicFormat) + nSelect = i; + } + try + { + Any aTemplates(&aListBoxEntries, cppu::UnoType<decltype(aListBoxEntries)>::get()); + + xCtrlAcc->setValue( ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE, + ListboxControlActions::ADD_ITEMS , aTemplates ); + + Any aSelectPos(&nSelect, cppu::UnoType<decltype(nSelect)>::get()); + xCtrlAcc->setValue( ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE, + ListboxControlActions::SET_SELECT_ITEM, aSelectPos ); + } + catch (const Exception&) + { + OSL_FAIL("control access failed"); + } +#endif + + const SfxStringItem* pName = rReq.GetArg<SfxStringItem>(SID_INSERT_GRAPHIC); + bool bShowError = !pName; + if( pName +#if HAVE_FEATURE_DESKTOP + || ERRCODE_NONE == pFileDlg->Execute() +#endif + ) + { + + OUString aFileName, aFilterName; + if ( pName ) + { + aFileName = pName->GetValue(); + const SfxStringItem* pFilter = rReq.GetArg<SfxStringItem>(FN_PARAM_FILTER); + if ( pFilter ) + aFilterName = pFilter->GetValue(); + } +#if HAVE_FEATURE_DESKTOP + else + { + aFileName = pFileDlg->GetPath(); + aFilterName = pFileDlg->GetCurrentFilter(); + rReq.AppendItem( SfxStringItem( SID_INSERT_GRAPHIC, aFileName ) ); + rReq.AppendItem( SfxStringItem( FN_PARAM_FILTER, aFilterName ) ); + + bool bAsLink = false; + if(nHtmlMode & HTMLMODE_ON) + bAsLink = true; + else + { + try + { + Any aVal = xCtrlAcc->getValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0); + OSL_ENSURE(aVal.hasValue(), "Value CBX_INSERT_AS_LINK not found"); + bAsLink = !aVal.hasValue() || *o3tl::doAccess<bool>(aVal); + Any aTemplateValue = xCtrlAcc->getValue( + ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE, + ListboxControlActions::GET_SELECTED_ITEM ); + OUString sTmpl; + aTemplateValue >>= sTmpl; + rReq.AppendItem( SfxStringItem( FN_PARAM_2, sTmpl) ); + } + catch (const Exception&) + { + OSL_FAIL("control access failed"); + } + } + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bAsLink ) ); + } + const SfxBoolItem* pAsLink = rReq.GetArg<SfxBoolItem>(FN_PARAM_1); + const SfxStringItem* pStyle = rReq.GetArg<SfxStringItem>(FN_PARAM_2); +#endif + + bool bAsLink = false; + +#if HAVE_FEATURE_DESKTOP + if( nHtmlMode & HTMLMODE_ON ) + bAsLink = true; + else + { + if ( rReq.GetArgs() ) + { + if ( pAsLink ) + bAsLink = pAsLink->GetValue(); + if ( pStyle && !pStyle->GetValue().isEmpty() ) + sGraphicFormat = pStyle->GetValue(); + } + else + { + Any aVal = xCtrlAcc->getValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0); + OSL_ENSURE(aVal.hasValue(), "Value CBX_INSERT_AS_LINK not found"); + bAsLink = !aVal.hasValue() || *o3tl::doAccess<bool>(aVal); + Any aTemplateValue = xCtrlAcc->getValue( + ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE, + ListboxControlActions::GET_SELECTED_ITEM ); + OUString sTmpl; + aTemplateValue >>= sTmpl; + if( !sTmpl.isEmpty() ) + sGraphicFormat = sTmpl; + rReq.AppendItem( SfxStringItem( FN_PARAM_2, sGraphicFormat ) ); + rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bAsLink ) ); + } + + // really store as link only? + if( bAsLink && SvtMiscOptions().ShowLinkWarningDialog() ) + { + SvxLinkWarningDialog aWarnDlg(GetFrameWeld(), pFileDlg->GetPath()); + if (aWarnDlg.run() != RET_OK) + bAsLink=false; // don't store as link + } + } +#endif + + SwWrtShell& rSh = GetWrtShell(); + rSh.LockPaint(); + rSh.StartAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_GRAPHIC_DEFNAME)); + + // #i123922# determine if we really want to insert or replace the graphic at a selected object + const bool bReplaceMode(rSh.HasSelection() && SelectionType::Frame == rSh.GetSelectionType()); + + rSh.StartUndo(SwUndoId::INSERT, &aRewriter); + + ErrCode nError = InsertGraphic( aFileName, aFilterName, bAsLink, &GraphicFilter::GetGraphicFilter() ); + + // format not equal to current filter (with autodetection) + if( nError == ERRCODE_GRFILTER_FORMATERROR ) + nError = InsertGraphic( aFileName, OUString(), bAsLink, &GraphicFilter::GetGraphicFilter() ); + + // #i123922# no new FrameFormat for replace mode, only when new object was created, + // else this would reset the current setting for the frame holding the graphic + if ( !bReplaceMode && rSh.IsFrameSelected() ) + { + SwFrameFormat* pFormat = pDoc->FindFrameFormatByName( sGraphicFormat ); + if(!pFormat) + pFormat = pDoc->MakeFrameFormat(sGraphicFormat, + pDocShell->GetDoc()->GetDfltFrameFormat(), + true, false); + rSh.SetFrameFormat( pFormat ); + } + + const char* pResId(nullptr); + if( nError == ERRCODE_GRFILTER_OPENERROR ) + pResId = STR_GRFILTER_OPENERROR; + else if( nError == ERRCODE_GRFILTER_IOERROR ) + pResId = STR_GRFILTER_IOERROR; + else if( nError ==ERRCODE_GRFILTER_FORMATERROR ) + pResId = STR_GRFILTER_FORMATERROR; + else if( nError ==ERRCODE_GRFILTER_VERSIONERROR ) + pResId = STR_GRFILTER_VERSIONERROR; + else if( nError ==ERRCODE_GRFILTER_FILTERERROR ) + pResId = STR_GRFILTER_FILTERERROR; + else if( nError ==ERRCODE_GRFILTER_TOOBIG ) + pResId = STR_GRFILTER_TOOBIG; + + rSh.EndAction(); + rSh.UnlockPaint(); + if (pResId) + { + if( bShowError ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(pResId))); + xInfoBox->run(); + } + rReq.Ignore(); + } + else + { + // set the specific graphic attributes to the graphic + bReturn = true; + AutoCaption( GRAPHIC_CAP ); + rReq.Done(); + } + + rSh.EndUndo(); // due to possible change of Shell + } + + return bReturn; +} + +void SwView::Execute(SfxRequest &rReq) +{ + const sal_uInt16 nSlot = rReq.GetSlot(); + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + bool bIgnore = false; + switch( nSlot ) + { + case SID_CREATE_SW_DRAWVIEW: + m_pWrtShell->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + break; + + case FN_LINE_NUMBERING_DLG: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclSwViewDialog(*this)); + pDlg->Execute(); + break; + } + case FN_EDIT_LINK_DLG: + EditLinkDlg(); + break; + case FN_REFRESH_VIEW: + GetEditWin().Invalidate(); + break; + case FN_PAGEUP: + case FN_PAGEUP_SEL: + case FN_PAGEDOWN: + case FN_PAGEDOWN_SEL: + { + tools::Rectangle aVis( GetVisArea() ); + SwEditWin& rTmpWin = GetEditWin(); + if ( FN_PAGEUP == nSlot || FN_PAGEUP_SEL == nSlot ) + PageUpCursor(FN_PAGEUP_SEL == nSlot); + else + PageDownCursor(FN_PAGEDOWN_SEL == nSlot); + + rReq.SetReturnValue(SfxBoolItem(nSlot, + aVis != GetVisArea())); + //#i42732# - notify the edit window that from now on we do not use the input language + rTmpWin.SetUseInputLanguage( false ); + } + break; + case FN_REDLINE_ON: + { + if( pArgs && + SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem )) + { + IDocumentRedlineAccess& rIDRA = m_pWrtShell->getIDocumentRedlineAccess(); + Sequence <sal_Int8> aPasswd = rIDRA.GetRedlinePassword(); + if( aPasswd.hasElements() ) + { + OSL_ENSURE( !static_cast<const SfxBoolItem*>(pItem)->GetValue(), "SwView::Execute(): password set and redlining off doesn't match!" ); + + // dummy password from OOXML import: only confirmation dialog + if (aPasswd.getLength() == 1 && aPasswd[0] == 1) + { + std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_pWrtShell->GetView().GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::YesNo, + SfxResId(RID_SVXSTR_END_REDLINING_WARNING))); + xWarn->set_default_response(RET_NO); + if (xWarn->run() == RET_YES) + rIDRA.SetRedlinePassword(Sequence <sal_Int8> ()); + else + break; + } + else + { + // xmlsec05: new password dialog + SfxPasswordDialog aPasswdDlg(GetFrameWeld()); + aPasswdDlg.SetMinLen(1); + //#i69751# the result of Execute() can be ignored + (void)aPasswdDlg.run(); + OUString sNewPasswd(aPasswdDlg.GetPassword()); + Sequence <sal_Int8> aNewPasswd = rIDRA.GetRedlinePassword(); + SvPasswordHelper::GetHashPassword( aNewPasswd, sNewPasswd ); + if(SvPasswordHelper::CompareHashPassword(aPasswd, sNewPasswd)) + rIDRA.SetRedlinePassword(Sequence <sal_Int8> ()); + else + { // xmlsec05: message box for wrong password + break; + } + } + } + + const RedlineFlags nOn = static_cast<const SfxBoolItem*>(pItem)->GetValue() + ? RedlineFlags::On : RedlineFlags::NONE; + const RedlineFlags nMode = m_pWrtShell->GetRedlineFlags(); + m_pWrtShell->SetRedlineFlagsAndCheckInsMode( (nMode & ~RedlineFlags::On) | nOn); + + // Notify all view shells of this document, as the track changes mode is document-global. + SwDocShell* pDocShell = GetDocShell(); + for (SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pDocShell); pViewFrame; pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pDocShell)) + { + pViewFrame->GetBindings().Invalidate(FN_REDLINE_ON); + pViewFrame->GetBindings().Update(FN_REDLINE_ON); + } + } + } + break; + case FN_REDLINE_PROTECT : + { + IDocumentRedlineAccess& rIDRA = m_pWrtShell->getIDocumentRedlineAccess(); + Sequence <sal_Int8> aPasswd = rIDRA.GetRedlinePassword(); + if( pArgs && SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem ) + && static_cast<const SfxBoolItem*>(pItem)->GetValue() == aPasswd.hasElements() ) + break; + + // xmlsec05: new password dialog + // message box for wrong password + SfxPasswordDialog aPasswdDlg(GetFrameWeld()); + aPasswdDlg.SetMinLen(1); + if (!aPasswd.hasElements()) + aPasswdDlg.ShowExtras(SfxShowExtras::CONFIRM); + if (aPasswdDlg.run()) + { + RedlineFlags nOn = RedlineFlags::On; + OUString sNewPasswd(aPasswdDlg.GetPassword()); + Sequence <sal_Int8> aNewPasswd = + rIDRA.GetRedlinePassword(); + SvPasswordHelper::GetHashPassword( aNewPasswd, sNewPasswd ); + if(!aPasswd.hasElements()) + { + rIDRA.SetRedlinePassword(aNewPasswd); + } + else if(SvPasswordHelper::CompareHashPassword(aPasswd, sNewPasswd)) + { + rIDRA.SetRedlinePassword(Sequence <sal_Int8> ()); + nOn = RedlineFlags::NONE; + } + const RedlineFlags nMode = rIDRA.GetRedlineFlags(); + m_pWrtShell->SetRedlineFlagsAndCheckInsMode( (nMode & ~RedlineFlags::On) | nOn); + rReq.AppendItem( SfxBoolItem( FN_REDLINE_PROTECT, !(nMode&RedlineFlags::On) ) ); + } + else + bIgnore = true; + } + break; + case FN_REDLINE_SHOW: + + if( pArgs && + SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem)) + { + // tdf#125754 avoid recursive layout + // because all views share the layout, have to use AllAction + m_pWrtShell->StartAllAction(); + m_pWrtShell->GetLayout()->SetHideRedlines( + !static_cast<const SfxBoolItem*>(pItem)->GetValue()); + m_pWrtShell->EndAllAction(); + if (m_pWrtShell->IsRedlineOn()) + m_pWrtShell->SetInsMode(); + } + break; + case FN_MAILMERGE_SENDMAIL_CHILDWINDOW: + case FN_REDLINE_ACCEPT: + GetViewFrame()->ToggleChildWindow(nSlot); + break; + case FN_REDLINE_ACCEPT_DIRECT: + case FN_REDLINE_REJECT_DIRECT: + case FN_REDLINE_ACCEPT_TONEXT: + case FN_REDLINE_REJECT_TONEXT: + { + SwDoc *pDoc = m_pWrtShell->GetDoc(); + SwPaM *pCursor = m_pWrtShell->GetCursor(); + const SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + SwRedlineTable::size_type nRedline = SwRedlineTable::npos; + if (pArgs && pArgs->GetItemState(nSlot, false, &pItem) == SfxItemState::SET) + { + const sal_Int64 nChangeId = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + for (SwRedlineTable::size_type i = 0; i < rRedlineTable.size(); ++i) + { + if (nChangeId == rRedlineTable[i]->GetId()) + nRedline = i; + } + } + + if( pCursor->HasMark() && nRedline == SwRedlineTable::npos) + { + if (FN_REDLINE_ACCEPT_DIRECT == nSlot || FN_REDLINE_ACCEPT_TONEXT == nSlot) + m_pWrtShell->AcceptRedlinesInSelection(); + else + m_pWrtShell->RejectRedlinesInSelection(); + } + else + { + // We check for a redline at the start of the selection/cursor, not the point. + // This ensures we work properly with FN_REDLINE_NEXT_CHANGE, which leaves the + // point at the *end* of the redline and the mark at the start (so GetRedline + // would return NULL if called on the point) + const SwRangeRedline* pRedline = nullptr; + if (nRedline != SwRedlineTable::npos) + { + // A redline was explicitly requested by specifying an + // index, don't guess based on the cursor position. + + if (nRedline < rRedlineTable.size()) + pRedline = rRedlineTable[nRedline]; + } + else + pRedline = pDoc->getIDocumentRedlineAccess().GetRedline(*pCursor->Start(), &nRedline); + + assert(pRedline != nullptr); + if (pRedline) + { + if (FN_REDLINE_ACCEPT_DIRECT == nSlot || FN_REDLINE_ACCEPT_TONEXT == nSlot) + m_pWrtShell->AcceptRedline(nRedline); + else + m_pWrtShell->RejectRedline(nRedline); + } + } + if (FN_REDLINE_ACCEPT_TONEXT == nSlot || FN_REDLINE_REJECT_TONEXT == nSlot) + { + // Go to next change after accepting or rejecting one (tdf#101977) + GetViewFrame()->GetDispatcher()->Execute(FN_REDLINE_NEXT_CHANGE, SfxCallMode::ASYNCHRON); + } + } + break; + + case FN_REDLINE_NEXT_CHANGE: + { + // If a parameter is provided, try going to the nth change, not to + // the next one. + SwDoc* pDoc = m_pWrtShell->GetDoc(); + const SwRedlineTable& rRedlineTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + SwRedlineTable::size_type nRedline = SwRedlineTable::npos; + if (pArgs && pArgs->GetItemState(nSlot, false, &pItem) == SfxItemState::SET) + { + const sal_uInt32 nChangeId = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + for (SwRedlineTable::size_type i = 0; i < rRedlineTable.size(); ++i) + { + if (nChangeId == rRedlineTable[i]->GetId()) + nRedline = i; + } + } + + const SwRangeRedline *pNext = nullptr; + if (nRedline < rRedlineTable.size()) + pNext = m_pWrtShell->GotoRedline(nRedline, true); + else + pNext = m_pWrtShell->SelNextRedline(); + + if (pNext) + { + if (comphelper::LibreOfficeKit::isActive()) + { + OString aPayload(".uno:CurrentTrackedChangeId="); + sal_uInt32 nRedlineId = pNext->GetId(); + aPayload += OString::number(nRedlineId); + libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aPayload.getStr()); + } + + m_pWrtShell->SetInSelect(); + } + + } + break; + + case FN_REDLINE_PREV_CHANGE: + { + const SwRangeRedline *pPrev = m_pWrtShell->SelPrevRedline(); + + if (pPrev) + { + if (comphelper::LibreOfficeKit::isActive()) + { + OString aPayload(".uno:CurrentTrackedChangeId="); + sal_uInt32 nRedlineId = pPrev->GetId(); + aPayload += OString::number(nRedlineId); + libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aPayload.getStr()); + } + + m_pWrtShell->SetInSelect(); + } + } + break; + + case SID_DOCUMENT_COMPARE: + case SID_DOCUMENT_MERGE: + { + OUString sFileName, sFilterName; + sal_Int16 nVersion = 0; + bool bHasFileName = false; + m_pViewImpl->SetParam( 0 ); + bool bNoAcceptDialog = false; + + if( pArgs ) + { + if( SfxItemState::SET == pArgs->GetItemState( SID_FILE_NAME, false, &pItem )) + sFileName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + bHasFileName = !sFileName.isEmpty(); + + if( SfxItemState::SET == pArgs->GetItemState( SID_FILTER_NAME, false, &pItem )) + sFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue(); + + if( SfxItemState::SET == pArgs->GetItemState( SID_VERSION, false, &pItem )) + { + nVersion = static_cast<const SfxInt16Item *>(pItem)->GetValue(); + m_pViewImpl->SetParam( nVersion ); + } + if( SfxItemState::SET == pArgs->GetItemState( SID_NO_ACCEPT_DIALOG, false, &pItem )) + { + bNoAcceptDialog = static_cast<const SfxBoolItem *>(pItem)->GetValue(); + } + } + + m_pViewImpl->InitRequest( rReq ); + long nFound = InsertDoc( nSlot, sFileName, sFilterName, nVersion ); + + if ( bHasFileName ) + { + rReq.SetReturnValue( SfxInt32Item( nSlot, nFound )); + + if (nFound > 0 && !bNoAcceptDialog) // show Redline browser + { + SfxViewFrame* pVFrame = GetViewFrame(); + pVFrame->ShowChildWindow(FN_REDLINE_ACCEPT); + + // re-initialize the Redline dialog + const sal_uInt16 nId = SwRedlineAcceptChild::GetChildWindowId(); + SwRedlineAcceptChild *pRed = static_cast<SwRedlineAcceptChild*>( + pVFrame->GetChildWindow(nId)); + if (pRed) + pRed->ReInitDlg(GetDocShell()); + } + } + else + bIgnore = true; + } + break; + case FN_SYNC_LABELS: + GetViewFrame()->ShowChildWindow(nSlot); + break; + case FN_ESCAPE: + { + if ( m_pWrtShell->HasDrawViewDrag() ) + { + m_pWrtShell->BreakDrag(); + m_pWrtShell->EnterSelFrameMode(); + } + else if ( m_pWrtShell->IsDrawCreate() ) + { + GetDrawFuncPtr()->BreakCreate(); + AttrChangedNotify(nullptr); // shell change if needed + } + else if ( m_pWrtShell->HasSelection() || IsDrawMode() ) + { + SdrView *pSdrView = m_pWrtShell->HasDrawView() ? m_pWrtShell->GetDrawView() : nullptr; + if(pSdrView && pSdrView->AreObjectsMarked() && + pSdrView->GetHdlList().GetFocusHdl()) + { + const_cast<SdrHdlList&>(pSdrView->GetHdlList()).ResetFocusHdl(); + } + else + { + if(pSdrView) + { + LeaveDrawCreate(); + Point aPt(LONG_MIN, LONG_MIN); + //go out of the frame + m_pWrtShell->SelectObj(aPt, SW_LEAVE_FRAME); + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( SID_ATTR_SIZE ); + } + m_pWrtShell->EnterStdMode(); + AttrChangedNotify(nullptr); // shell change if necessary + } + } + else if ( GetEditWin().GetApplyTemplate() ) + { + GetEditWin().SetApplyTemplate(SwApplyTemplate()); + } + else if( static_cast<SfxObjectShell*>(GetDocShell())->IsInPlaceActive() ) + { + Escape(); + } + else if ( GetEditWin().IsChainMode() ) + { + GetEditWin().SetChainMode( false ); + } + else if( m_pWrtShell->GetFlyFrameFormat() ) + { + const SwFrameFormat* pFormat = m_pWrtShell->GetFlyFrameFormat(); + if(m_pWrtShell->GotoFly( pFormat->GetName(), FLYCNTTYPE_FRM )) + { + m_pWrtShell->HideCursor(); + m_pWrtShell->EnterSelFrameMode(); + } + } + else + { + SfxBoolItem aItem( SID_WIN_FULLSCREEN, false ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_WIN_FULLSCREEN, + SfxCallMode::RECORD, { &aItem }); + bIgnore = true; + } + } + break; + case SID_ATTR_BORDER_INNER: + case SID_ATTR_BORDER_OUTER: + case SID_ATTR_BORDER_SHADOW: + if(pArgs) + m_pWrtShell->SetAttrSet(*pArgs); + break; + + case SID_ATTR_PAGE: + case SID_ATTR_PAGE_SIZE: + case SID_ATTR_PAGE_MAXSIZE: + case SID_ATTR_PAGE_PAPERBIN: + case SID_ATTR_PAGE_EXT1: + case FN_PARAM_FTN_INFO: + { + if(pArgs) + { + const size_t nCurIdx = m_pWrtShell->GetCurPageDesc(); + SwPageDesc aPageDesc( m_pWrtShell->GetPageDesc( nCurIdx ) ); + ::ItemSetToPageDesc( *pArgs, aPageDesc ); + // change the descriptor of the core + m_pWrtShell->ChgPageDesc( nCurIdx, aPageDesc ); + } + } + break; + case FN_GOTO_PAGE: + { + SwGotoPageDlg aDlg(GetViewFrame()->GetWindow().GetFrameWeld(), &GetViewFrame()->GetBindings()); + if (aDlg.run() == RET_OK) + GetWrtShell().GotoPage(aDlg.GetPageSelection(), true); + } + break; + case FN_EDIT_CURRENT_TOX: + { + GetViewFrame()->GetDispatcher()->Execute( + FN_INSERT_MULTI_TOX, SfxCallMode::ASYNCHRON); + } + break; + case FN_UPDATE_CUR_TOX: + { + const SwTOXBase* pBase = m_pWrtShell->GetCurTOX(); + if(pBase) + { + // tdf#106374: don't jump view on the update + const bool bWasLocked = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView(true); + m_pWrtShell->StartAction(); + if(TOX_INDEX == pBase->GetType()) + m_pWrtShell->ApplyAutoMark(); + m_pWrtShell->UpdateTableOf( *pBase ); + m_pWrtShell->EndAction(); + if (!bWasLocked) + m_pWrtShell->LockView(false); + } + } + break; + case FN_UPDATE_TOX: + { + m_pWrtShell->StartAction(); + m_pWrtShell->EnterStdMode(); + bool bOldCursorInReadOnly = m_pWrtShell->IsReadOnlyAvailable(); + m_pWrtShell->SetReadOnlyAvailable( true ); + + for( int i = 0; i < 2; ++i ) + { + if( m_pWrtShell->GetTOXCount() == 1 ) + ++i; + + while( m_pWrtShell->GotoPrevTOXBase() ) + ; // jump to the first "table of ..." + + // if we are not in one, jump to next + const SwTOXBase* pBase = m_pWrtShell->GetCurTOX(); + if( !pBase ) + { + m_pWrtShell->GotoNextTOXBase(); + pBase = m_pWrtShell->GetCurTOX(); + } + + bool bAutoMarkApplied = false; + while( pBase ) + { + if(TOX_INDEX == pBase->GetType() && !bAutoMarkApplied) + { + m_pWrtShell->ApplyAutoMark(); + bAutoMarkApplied = true; + } + // pBase is needed only for the interface. Should be changed in future! (JP 1996) + m_pWrtShell->UpdateTableOf( *pBase ); + + if( m_pWrtShell->GotoNextTOXBase() ) + pBase = m_pWrtShell->GetCurTOX(); + else + pBase = nullptr; + } + } + m_pWrtShell->SetReadOnlyAvailable( bOldCursorInReadOnly ); + m_pWrtShell->EndAction(); + } + break; + case SID_ATTR_BRUSH: + { + if(pArgs && SfxItemState::SET == pArgs->GetItemState(RES_BACKGROUND, false, &pItem)) + { + const size_t nCurIdx = m_pWrtShell->GetCurPageDesc(); + SwPageDesc aDesc( m_pWrtShell->GetPageDesc( nCurIdx )); + SwFrameFormat& rMaster = aDesc.GetMaster(); + rMaster.SetFormatAttr(*pItem); + m_pWrtShell->ChgPageDesc( nCurIdx, aDesc); + } + } + break; + case SID_CLEARHISTORY: + { + m_pWrtShell->DelAllUndoObj(); + } + break; + case SID_UNDO: + { + m_pShell->ExecuteSlot(rReq); + } + break; +#if defined(_WIN32) || defined UNX + case SID_TWAIN_SELECT: + case SID_TWAIN_TRANSFER: + GetViewImpl()->ExecuteScan( rReq ); + break; +#endif + + case SID_ATTR_DEFTABSTOP: + { + if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_DEFTABSTOP, false, &pItem)) + { + SvxTabStopItem aDefTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP ); + const sal_uInt16 nTab = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + MakeDefTabs( nTab, aDefTabs ); + m_pWrtShell->SetDefault( aDefTabs ); + } + } + break; + case SID_ATTR_LANGUAGE : + if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_LANGUAGE, false, &pItem)) + { + SvxLanguageItem aLang(static_cast<const SvxLanguageItem*>(pItem)->GetLanguage(), RES_CHRATR_LANGUAGE); + m_pWrtShell->SetDefault( aLang ); + lcl_SetAllTextToDefaultLanguage( *m_pWrtShell, RES_CHRATR_LANGUAGE ); + } + break; + case SID_ATTR_CHAR_CTL_LANGUAGE: + if(pArgs && SfxItemState::SET == pArgs->GetItemState(RES_CHRATR_CTL_LANGUAGE, false, &pItem)) + { + m_pWrtShell->SetDefault( *pItem ); + lcl_SetAllTextToDefaultLanguage( *m_pWrtShell, RES_CHRATR_CTL_LANGUAGE ); + } + break; + case SID_ATTR_CHAR_CJK_LANGUAGE: + if(pArgs && SfxItemState::SET == pArgs->GetItemState(RES_CHRATR_CJK_LANGUAGE, false, &pItem)) + { + m_pWrtShell->SetDefault( *pItem ); + lcl_SetAllTextToDefaultLanguage( *m_pWrtShell, RES_CHRATR_CJK_LANGUAGE ); + } + break; + case FN_NAV_ELEMENT: + { + // nothing here on purpose - if removed only the listbox that changed is changed + } + break; + case FN_SCROLL_PREV: + case FN_SCROLL_NEXT: + { + bool *pbNext = new bool(true); // FN_SCROLL_NEXT + if (nSlot == FN_SCROLL_PREV) + *pbNext = false; + // #i75416# move the execution of the search to an asynchronously called static link + Application::PostUserEvent( LINK(this, SwView, MoveNavigationHdl), pbNext ); + } + break; + case SID_JUMPTOMARK: + if( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_JUMPTOMARK, false, &pItem)) + JumpToSwMark( static_cast<const SfxStringItem*>(pItem)->GetValue() ); + break; + case SID_GALLERY : + // First make sure that the sidebar is visible + GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + + ::sfx2::sidebar::Sidebar::ShowPanel( + "GalleryPanel", + GetViewFrame()->GetFrame().GetFrameInterface()); + break; + case SID_AVMEDIA_PLAYER : + GetViewFrame()->ChildWindowExecute(rReq); + break; + case SID_VIEW_DATA_SOURCE_BROWSER: + { + SfxViewFrame* pVFrame = GetViewFrame(); + pVFrame->ChildWindowExecute(rReq); + if(pVFrame->HasChildWindow(SID_BROWSER)) + { + const SwDBData& rData = GetWrtShell().GetDBDesc(); + SwModule::ShowDBObj(*this, rData); + } + } + break; + case FN_INSERT_FIELD_DATA_ONLY: + { + bool bShow = false; + if( pArgs && + SfxItemState::SET == pArgs->GetItemState(nSlot, false, &pItem )) + bShow = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + if((bShow && m_bInMailMerge) != GetViewFrame()->HasChildWindow(nSlot)) + GetViewFrame()->ToggleChildWindow(nSlot); + //if fields have been successfully inserted call the "real" + //mail merge dialog +#if HAVE_FEATURE_DBCONNECTIVITY + SwWrtShell &rSh = GetWrtShell(); + if(m_bInMailMerge && rSh.IsAnyDatabaseFieldInDoc()) + { + SwDBManager* pDBManager = rSh.GetDBManager(); + if (pDBManager) + { + SwDBData aData = rSh.GetDBData(); + rSh.EnterStdMode(); // force change in text shell; necessary for mixing DB fields + AttrChangedNotify(nullptr); + + Sequence<PropertyValue> aProperties(3); + PropertyValue* pValues = aProperties.getArray(); + pValues[0].Name = "DataSourceName"; + pValues[1].Name = "Command"; + pValues[2].Name = "CommandType"; + pValues[0].Value <<= aData.sDataSource; + pValues[1].Value <<= aData.sCommand; + pValues[2].Value <<= aData.nCommandType; + pDBManager->ExecuteFormLetter(rSh, aProperties); + } + } +#endif + m_bInMailMerge &= bShow; + GetViewFrame()->GetBindings().Invalidate(FN_INSERT_FIELD); + } + break; + case FN_QRY_MERGE: + { + bool bUseCurrentDocument = true; + bool bQuery = !pArgs || SfxItemState::SET != pArgs->GetItemState(nSlot); + if(bQuery) + { + SfxViewFrame* pTmpFrame = GetViewFrame(); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractMailMergeCreateFromDlg> pDlg(pFact->CreateMailMergeCreateFromDlg(pTmpFrame->GetWindow().GetFrameWeld())); + if (RET_OK == pDlg->Execute()) + bUseCurrentDocument = pDlg->IsThisDocument(); + else + break; + } + GenerateFormLetter(bUseCurrentDocument); + } + break; + case SID_RECHECK_DOCUMENT: + { + SwDocShell* pDocShell = GetDocShell(); + SwDoc* pDoc = pDocShell->GetDoc(); + uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( pDoc->GetGCIterator() ); + if( xGCIterator.is() ) + { + xGCIterator->resetIgnoreRules(); + } + // reset ignore lists + pDoc->SpellItAgainSam( true, false, false ); + // clear ignore dictionary + uno::Reference< linguistic2::XDictionary > xDictionary = LinguMgr::GetIgnoreAllList(); + if( xDictionary.is() ) + xDictionary->clear(); + // put cursor to the start of the document + m_pWrtShell->StartOfSection(); + [[fallthrough]]; // call spell/grammar dialog + } + case FN_SPELL_GRAMMAR_DIALOG: + { + SfxViewFrame* pViewFrame = GetViewFrame(); + if (rReq.GetArgs() != nullptr) + pViewFrame->SetChildWindow (FN_SPELL_GRAMMAR_DIALOG, + static_cast<const SfxBoolItem&>( (rReq.GetArgs()-> + Get(FN_SPELL_GRAMMAR_DIALOG))).GetValue()); + else + pViewFrame->ToggleChildWindow(FN_SPELL_GRAMMAR_DIALOG); + + pViewFrame->GetBindings().Invalidate(FN_SPELL_GRAMMAR_DIALOG); + rReq.Ignore (); + } + break; + case SID_ALIGN_ANY_LEFT : + case SID_ALIGN_ANY_HCENTER : + case SID_ALIGN_ANY_RIGHT : + case SID_ALIGN_ANY_JUSTIFIED: + case SID_ALIGN_ANY_TOP : + case SID_ALIGN_ANY_VCENTER : + case SID_ALIGN_ANY_BOTTOM : + case SID_ALIGN_ANY_HDEFAULT : + case SID_ALIGN_ANY_VDEFAULT : + { + sal_uInt16 nAlias = 0; + if( m_nSelectionType & (SelectionType::DrawObjectEditMode|SelectionType::Text) ) + { + switch( nSlot ) + { + case SID_ALIGN_ANY_LEFT : nAlias = SID_ATTR_PARA_ADJUST_LEFT; break; + case SID_ALIGN_ANY_HCENTER : nAlias = SID_ATTR_PARA_ADJUST_CENTER; break; + case SID_ALIGN_ANY_RIGHT : nAlias = SID_ATTR_PARA_ADJUST_RIGHT; break; + case SID_ALIGN_ANY_JUSTIFIED: nAlias = SID_ATTR_PARA_ADJUST_BLOCK; break; + case SID_ALIGN_ANY_TOP : nAlias = SID_TABLE_VERT_NONE; break; + case SID_ALIGN_ANY_VCENTER : nAlias = SID_TABLE_VERT_CENTER; break; + case SID_ALIGN_ANY_BOTTOM : nAlias = SID_TABLE_VERT_BOTTOM; break; + } + } + else + { + switch( nSlot ) + { + case SID_ALIGN_ANY_LEFT : nAlias = SID_OBJECT_ALIGN_LEFT ; break; + case SID_ALIGN_ANY_HCENTER : nAlias = SID_OBJECT_ALIGN_CENTER ; break; + case SID_ALIGN_ANY_RIGHT : nAlias = SID_OBJECT_ALIGN_RIGHT ; break; + case SID_ALIGN_ANY_TOP : nAlias = SID_OBJECT_ALIGN_UP ; break; + case SID_ALIGN_ANY_VCENTER : nAlias = SID_OBJECT_ALIGN_MIDDLE ; break; + case SID_ALIGN_ANY_BOTTOM : nAlias = SID_OBJECT_ALIGN_DOWN ; break; + } + } + //these slots are either re-mapped to text or object alignment + if (nAlias) + GetViewFrame()->GetDispatcher()->Execute( + nAlias, SfxCallMode::ASYNCHRON); + } + break; + case SID_RESTORE_EDITING_VIEW: + { + //#i33307# restore editing position + Point aCursorPos; + bool bSelectObj; + if(m_pViewImpl->GetRestorePosition(aCursorPos, bSelectObj)) + { + m_pWrtShell->SwCursorShell::SetCursor( aCursorPos, !bSelectObj ); + if( bSelectObj ) + { + m_pWrtShell->SelectObj( aCursorPos ); + m_pWrtShell->EnterSelFrameMode( &aCursorPos ); + } + } + } + break; + case SID_INSERT_GRAPHIC: + { + rReq.SetReturnValue(SfxBoolItem(nSlot, InsertGraphicDlg( rReq ))); + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } + if(!bIgnore) + rReq.Done(); +} + +bool SwView::IsConditionalFastCall( const SfxRequest &rReq ) +{ + sal_uInt16 nId = rReq.GetSlot(); + bool bRet = false; + + if (nId == FN_REDLINE_ACCEPT_DIRECT || nId == FN_REDLINE_REJECT_DIRECT) + { + if (comphelper::LibreOfficeKit::isActive()) + bRet = true; + } + return bRet || SfxShell::IsConditionalFastCall(rReq); + +} + +/// invalidate page numbering field +void SwView::UpdatePageNums(sal_uInt16 nPhyNum, sal_uInt16 nVirtNum, const OUString& rPgStr) +{ + OUString sTemp(GetPageStr( nPhyNum, nVirtNum, rPgStr )); + const SfxStringItem aTmp( FN_STAT_PAGE, sTemp ); + // Used to distinguish which tooltip to show + const SfxBoolItem bExtendedTooltip( FN_STAT_PAGE, + !rPgStr.isEmpty() && OUString::number(nPhyNum) != rPgStr + && nPhyNum != nVirtNum ); + + SfxBindings &rBnd = GetViewFrame()->GetBindings(); + rBnd.SetState( aTmp ); + rBnd.Update( FN_STAT_PAGE ); + rBnd.SetState( bExtendedTooltip ); + rBnd.Update( FN_STAT_PAGE ); +} + +void SwView::UpdateDocStats() +{ + SfxBindings &rBnd = GetViewFrame()->GetBindings(); + rBnd.Invalidate( FN_STAT_WORDCOUNT ); + rBnd.Update( FN_STAT_WORDCOUNT ); +} + +/// get status of the status line +void SwView::StateStatusLine(SfxItemSet &rSet) +{ + SwWrtShell& rShell = GetWrtShell(); + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + OSL_ENSURE( nWhich, "empty set"); + + //get section change event + const SwSection* CurrSect = rShell.GetCurrSection(); + if( CurrSect ) + { + const OUString& sCurrentSectionName = CurrSect->GetSectionName(); + if(sCurrentSectionName != m_sOldSectionName) + { + SwCursorShell::FireSectionChangeEvent(2, 1); + } + m_sOldSectionName = sCurrentSectionName; + } + else if (!m_sOldSectionName.isEmpty()) + { + SwCursorShell::FireSectionChangeEvent(2, 1); + m_sOldSectionName= OUString(); + } + //get column change event + if(rShell.bColumnChange()) + { + SwCursorShell::FireColumnChangeEvent(2, 1); + } + + while( nWhich ) + { + switch( nWhich ) + { + case FN_STAT_PAGE: { + // number of pages, log. page number + sal_uInt16 nPage, nLogPage; + OUString sDisplay; + rShell.GetPageNumber( -1, rShell.IsCursorVisible(), nPage, nLogPage, sDisplay ); + OUString sTemp( GetPageStr( nPage, nLogPage, sDisplay ) ); + const SfxStringItem aTmp( FN_STAT_PAGE, sTemp ); + GetViewFrame()->GetBindings().SetState( aTmp ); + // Used to distinguish which tooltip to show + const SfxBoolItem bExtendedTooltip( FN_STAT_PAGE, !sDisplay.isEmpty() && + OUString::number( nPage ) != sDisplay && + nPage != nLogPage ); + GetViewFrame()->GetBindings().SetState( bExtendedTooltip ); + //if existing page number is not equal to old page number, send out this event. + if (m_nOldPageNum != nLogPage ) + { + if (m_nOldPageNum != 0) + SwCursorShell::FirePageChangeEvent(m_nOldPageNum, nLogPage); + m_nOldPageNum = nLogPage; + } + const sal_uInt16 nCnt = GetWrtShell().GetPageCnt(); + if (m_nPageCnt != nCnt) // notify Basic + { + m_nPageCnt = nCnt; + SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwEventPageCount, SwDocShell::GetEventName(STR_SW_EVENT_PAGE_COUNT), GetViewFrame()->GetObjectShell()), false); + } + } + break; + + case FN_STAT_WORDCOUNT: + { + SwDocStat selectionStats; + SwDocStat documentStats; + rShell.CountWords(selectionStats); + documentStats = rShell.GetDoc()->getIDocumentStatistics().GetUpdatedDocStat( true /* complete-async */, false /* don't update fields */ ); + + sal_uLong nWord = selectionStats.nWord ? selectionStats.nWord : documentStats.nWord; + sal_uLong nChar = selectionStats.nChar ? selectionStats.nChar : documentStats.nChar; + const char* pResId = selectionStats.nWord ? STR_WORDCOUNT : STR_WORDCOUNT_NO_SELECTION; + const char* pWordResId = selectionStats.nWord ? STR_WORDCOUNT_WORDARG : STR_WORDCOUNT_WORDARG_NO_SELECTION; + const char* pCharResId = selectionStats.nWord ? STR_WORDCOUNT_CHARARG : STR_WORDCOUNT_CHARARG_NO_SELECTION; + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetUILocaleDataWrapper(); + OUString aWordArg = SwResId(pWordResId, nWord).replaceAll("$1", rLocaleData.getNum(nWord, 0)); + OUString aCharArg = SwResId(pCharResId, nChar).replaceAll("$1", rLocaleData.getNum(nChar, 0)); + OUString aWordCount(SwResId(pResId)); + aWordCount = aWordCount.replaceAll("$1", aWordArg); + aWordCount = aWordCount.replaceAll("$2", aCharArg); + + rSet.Put( SfxStringItem( FN_STAT_WORDCOUNT, aWordCount ) ); + + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->SetCounts(selectionStats, documentStats); + } + break; + + case FN_STAT_TEMPLATE: + { + rSet.Put(SfxStringItem( FN_STAT_TEMPLATE, + rShell.GetCurPageStyle())); + + } + break; + case SID_ATTR_ZOOM: + { + if ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) + { + const SwViewOption* pVOpt = rShell.GetViewOptions(); + SvxZoomType eZoom = pVOpt->GetZoomType(); + SvxZoomItem aZoom(eZoom, + pVOpt->GetZoom()); + if( pVOpt->getBrowseMode() ) + { + aZoom.SetValueSet( + SvxZoomEnableFlags::N50| + SvxZoomEnableFlags::N75| + SvxZoomEnableFlags::N100| + SvxZoomEnableFlags::N150| + SvxZoomEnableFlags::N200); + } + rSet.Put( aZoom ); + } + else + rSet.DisableItem( SID_ATTR_ZOOM ); + } + break; + case SID_ATTR_VIEWLAYOUT: + { + if ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) + { + const SwViewOption* pVOpt = rShell.GetViewOptions(); + const sal_uInt16 nColumns = pVOpt->GetViewLayoutColumns(); + const bool bBookMode = pVOpt->IsViewLayoutBookMode(); + SvxViewLayoutItem aViewLayout(nColumns, bBookMode); + rSet.Put( aViewLayout ); + } + else + rSet.DisableItem( SID_ATTR_VIEWLAYOUT ); + } + break; + case SID_ATTR_ZOOMSLIDER: + { + if ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) + { + const SwViewOption* pVOpt = rShell.GetViewOptions(); + const sal_uInt16 nCurrentZoom = pVOpt->GetZoom(); + SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM ); + aZoomSliderItem.AddSnappingPoint( 100 ); + + if ( !m_pWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) ) + { + const sal_uInt16 nColumns = pVOpt->GetViewLayoutColumns(); + const bool bAutomaticViewLayout = 0 == nColumns; + const SwPostItMgr* pMgr = GetPostItMgr(); + + // snapping points: + // automatic mode: 1 Page, 2 Pages, 100% + // n Columns mode: n Pages, 100% + // n Columns book mode: nPages without gaps, 100% + const SwRect aPageRect( m_pWrtShell->GetAnyCurRect( CurRectType::PageCalc ) ); + const SwRect aRootRect( m_pWrtShell->GetAnyCurRect( CurRectType::PagesArea ) ); // width of columns + Size aPageSize( aPageRect.SSize() ); + aPageSize.AdjustWidth(pMgr->HasNotes() && pMgr->ShowNotes() ? + pMgr->GetSidebarWidth() + pMgr->GetSidebarBorderWidth() : + 0 ); + + Size aRootSize( aRootRect.SSize() ); + + const MapMode aTmpMap( MapUnit::MapTwip ); + const Size& rEditSize = GetEditWin().GetOutputSizePixel(); + const Size aWindowSize( GetEditWin().PixelToLogic( rEditSize, aTmpMap ) ); + + const long nOf = pVOpt->GetDocumentBorder() * 2; + long nTmpWidth = bAutomaticViewLayout ? aPageSize.Width() : aRootSize.Width(); + nTmpWidth += nOf; + aPageSize.AdjustHeight(nOf ); + long nFac = aWindowSize.Width() * 100 / nTmpWidth; + + long nVisPercent = aWindowSize.Height() * 100 / aPageSize.Height(); + nFac = std::min( nFac, nVisPercent ); + + if (nFac >= MINZOOM) + { + aZoomSliderItem.AddSnappingPoint( nFac ); + } + + if ( bAutomaticViewLayout ) + { + nTmpWidth += aPageSize.Width() + pVOpt->GetGapBetweenPages(); + nFac = aWindowSize.Width() * 100 / nTmpWidth; + nFac = std::min( nFac, nVisPercent ); + if (nFac >= MINZOOM) + { + aZoomSliderItem.AddSnappingPoint( nFac ); + } + } + } + + rSet.Put( aZoomSliderItem ); + } + else + rSet.DisableItem( SID_ATTR_ZOOMSLIDER ); + } + break; + case SID_ATTR_POSITION: + case SID_ATTR_SIZE: + { + if( !rShell.IsFrameSelected() && !rShell.IsObjSelected() ) + SwBaseShell::SetFrameMode_( FLY_DRAG_END ); + else + { + FlyMode eFrameMode = SwBaseShell::GetFrameMode(); + if ( eFrameMode == FLY_DRAG_START || eFrameMode == FLY_DRAG ) + { + if ( nWhich == SID_ATTR_POSITION ) + rSet.Put( SfxPointItem( SID_ATTR_POSITION, + rShell.GetAnchorObjDiff())); + else + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, + rShell.GetObjSize())); + } + } + } + break; + case SID_TABLE_CELL: + + if( rShell.IsFrameSelected() || rShell.IsObjSelected() ) + { + // #i39171# Don't put a SvxSizeItem into a slot which is defined as SfxStringItem. + // SvxPosSizeStatusBarControl no longer resets to empty display if only one slot + // has no item, so SID_TABLE_CELL can remain empty (the SvxSizeItem is supplied + // in SID_ATTR_SIZE). + } + else + { + OUString sStr; + if( rShell.IsCursorInTable() ) + { + // table name + cell coordinate + sStr = rShell.GetTableFormat()->GetName() + ":"; + sStr += rShell.GetBoxNms(); + } + else + { + const SwSection* pCurrSect = rShell.GetCurrSection(); + if( pCurrSect ) + { + switch( pCurrSect->GetType() ) + { + case SectionType::ToxHeader: + case SectionType::ToxContent: + { + const SwTOXBase* pTOX = m_pWrtShell->GetCurTOX(); + if( pTOX ) + sStr = pTOX->GetTOXName(); + else + { + OSL_ENSURE( false, + "Unknown kind of section" ); + sStr = pCurrSect->GetSectionName(); + } + } + break; + default: + sStr = pCurrSect->GetSectionName(); + break; + } + } + } + + const SwNumRule* pNumRule = rShell.GetNumRuleAtCurrCursorPos(); + const bool bOutlineNum = pNumRule && pNumRule->IsOutlineRule(); + + if (pNumRule && !bOutlineNum ) // cursor in numbering + { + sal_uInt8 nNumLevel = rShell.GetNumLevel(); + if ( nNumLevel < MAXLEVEL ) + { + if(!pNumRule->IsAutoRule()) + { + SfxItemSet aSet(GetPool(), + svl::Items<RES_PARATR_NUMRULE, RES_PARATR_NUMRULE>{}); + rShell.GetCurAttr(aSet); + if(SfxItemState::DEFAULT <= + aSet.GetItemState(RES_PARATR_NUMRULE)) + { + const OUString& rNumStyle = + aSet.Get(RES_PARATR_NUMRULE).GetValue(); + if(!rNumStyle.isEmpty()) + { + if(!sStr.isEmpty()) + sStr += sStatusDelim; + sStr += rNumStyle; + } + } + } + if (!sStr.isEmpty()) + sStr += sStatusDelim; + sStr += SwResId(STR_NUM_LEVEL) + OUString::number( nNumLevel + 1 ); + + } + } + const int nOutlineLevel = rShell.GetCurrentParaOutlineLevel(); + if( nOutlineLevel != 0 ) + { + if (!sStr.isEmpty()) + sStr += " , "; + if( bOutlineNum ) + { + sStr += SwResId(STR_OUTLINE_NUMBERING) + + sStatusDelim + SwResId(STR_NUM_LEVEL); + } + else + sStr += SwResId(STR_NUM_OUTLINE); + sStr += OUString::number( nOutlineLevel); + } + + if( rShell.HasReadonlySel() ) + { + if (!sStr.isEmpty()) + sStr = sStatusDelim + sStr; + sStr = SwResId(SW_STR_READONLY) + sStr; + } + if (!sStr.isEmpty()) + rSet.Put( SfxStringItem( SID_TABLE_CELL, sStr )); + } + break; + case FN_STAT_SELMODE: + { + if(rShell.IsStdMode()) + rSet.Put(SfxUInt16Item(FN_STAT_SELMODE, 0)); + else if(rShell.IsAddMode()) + rSet.Put(SfxUInt16Item(FN_STAT_SELMODE, 2)); + else if(rShell.IsBlockMode()) + rSet.Put(SfxUInt16Item(FN_STAT_SELMODE, 3)); + else + rSet.Put(SfxUInt16Item(FN_STAT_SELMODE, 1)); + break; + } + case SID_ATTR_INSERT: + if( rShell.IsRedlineOn() ) + rSet.DisableItem( nWhich ); + else + { + rSet.Put(SfxBoolItem(SID_ATTR_INSERT,rShell.IsInsMode())); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +/** execute method for the status line + * + * @param rReq ??? + */ +void SwView::ExecuteStatusLine(SfxRequest &rReq) +{ + SwWrtShell &rSh = GetWrtShell(); + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem=nullptr; + bool bUp = false; + sal_uInt16 nWhich = rReq.GetSlot(); + switch( nWhich ) + { + case FN_STAT_PAGE: + { + GetViewFrame()->GetDispatcher()->Execute( FN_GOTO_PAGE, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); + } + break; + + case FN_STAT_WORDCOUNT: + { + GetViewFrame()->GetDispatcher()->Execute(FN_WORDCOUNT_DIALOG, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); + } + break; + + case FN_STAT_BOOKMARK: + if ( pArgs ) + { + if (SfxItemState::SET == pArgs->GetItemState( nWhich, true, &pItem)) + { + const IDocumentMarkAccess* pMarkAccess = rSh.getIDocumentMarkAccess(); + const sal_Int32 nIdx = static_cast<const SfxUInt16Item*>(pItem)->GetValue(); + if(nIdx < pMarkAccess->getBookmarksCount()) + { + const IDocumentMarkAccess::const_iterator_t ppBookmark = rSh.getIDocumentMarkAccess()->getBookmarksBegin() + nIdx; + rSh.EnterStdMode(); + rSh.GotoMark( *ppBookmark ); + } + else + OSL_FAIL("SwView::ExecuteStatusLine(..)" + " - Ignoring out of range bookmark index"); + } + } + break; + + case FN_STAT_TEMPLATE: + { + GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_PAGE_DLG, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); + } + break; + case SID_ATTR_ZOOM: + { + if ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) + { + const SfxItemSet *pSet = nullptr; + ScopedVclPtr<AbstractSvxZoomDialog> pDlg; + if ( pArgs ) + pSet = pArgs; + else + { + const SwViewOption& rViewOptions = *rSh.GetViewOptions(); + SfxItemSet aCoreSet(m_pShell->GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM, SID_ATTR_VIEWLAYOUT, SID_ATTR_VIEWLAYOUT>{} ); + SvxZoomItem aZoom( rViewOptions.GetZoomType(), rViewOptions.GetZoom() ); + + const bool bBrowseMode = rSh.GetViewOptions()->getBrowseMode(); + if( bBrowseMode ) + { + aZoom.SetValueSet( + SvxZoomEnableFlags::N50| + SvxZoomEnableFlags::N75| + SvxZoomEnableFlags::N100| + SvxZoomEnableFlags::N150| + SvxZoomEnableFlags::N200); + } + aCoreSet.Put( aZoom ); + + if ( !bBrowseMode ) + { + const SvxViewLayoutItem aViewLayout( rViewOptions.GetViewLayoutColumns(), rViewOptions.IsViewLayoutBookMode() ); + aCoreSet.Put( aViewLayout ); + } + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.disposeAndReset(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aCoreSet)); + pDlg->SetLimits( MINZOOM, MAXZOOM ); + if( pDlg->Execute() != RET_CANCEL ) + pSet = pDlg->GetOutputItemSet(); + } + + const SfxPoolItem* pViewLayoutItem = nullptr; + if ( pSet && SfxItemState::SET == pSet->GetItemState(SID_ATTR_VIEWLAYOUT, true, &pViewLayoutItem)) + { + const sal_uInt16 nColumns = static_cast<const SvxViewLayoutItem *>(pViewLayoutItem)->GetValue(); + const bool bBookMode = static_cast<const SvxViewLayoutItem *>(pViewLayoutItem)->IsBookMode(); + SetViewLayout( nColumns, bBookMode ); + } + + if ( pSet && SfxItemState::SET == pSet->GetItemState(SID_ATTR_ZOOM, true, &pItem)) + { + SvxZoomType eType = static_cast<const SvxZoomItem *>(pItem)->GetType(); + SetZoom( eType, static_cast<const SvxZoomItem *>(pItem)->GetValue() ); + } + bUp = true; + if ( pItem ) + rReq.AppendItem( *pItem ); + rReq.Done(); + } + } + break; + + case SID_ATTR_VIEWLAYOUT: + { + if ( pArgs && !rSh.getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) && + ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) ) + { + if ( SfxItemState::SET == pArgs->GetItemState(SID_ATTR_VIEWLAYOUT, true, &pItem )) + { + const sal_uInt16 nColumns = static_cast<const SvxViewLayoutItem *>(pItem)->GetValue(); + const bool bBookMode = !(0 == nColumns || 0 != (nColumns % 2)) && + static_cast<const SvxViewLayoutItem *>(pItem)->IsBookMode(); + + SetViewLayout( nColumns, bBookMode ); + } + + bUp = true; + rReq.Done(); + + InvalidateRulerPos(); + } + } + break; + + case SID_ATTR_ZOOMSLIDER: + { + if ( pArgs && ( ( GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) || !GetDocShell()->IsInPlaceActive() ) ) + { + if ( SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem )) + { + const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue(); + SetZoom( SvxZoomType::PERCENT, nCurrentZoom ); + } + + bUp = true; + rReq.Done(); + } + } + break; + + case SID_ATTR_SIZE: + { + sal_uInt16 nId = 0; + if( rSh.IsCursorInTable() ) + nId = FN_FORMAT_TABLE_DLG; + else if( rSh.GetCurTOX() ) + nId = FN_INSERT_MULTI_TOX; + else if( rSh.GetCurrSection() ) + nId = FN_EDIT_REGION; + else + { + const SwNumRule* pNumRule = rSh.GetNumRuleAtCurrCursorPos(); + if( pNumRule ) // cursor in numbering + { + if( pNumRule->IsAutoRule() ) + nId = FN_NUMBER_BULLETS; + else + { + // start dialog of the painter + nId = 0; + } + } + else if( rSh.IsFrameSelected() ) + nId = FN_FORMAT_FRAME_DLG; + else if( rSh.IsObjSelected() ) + nId = SID_ATTR_TRANSFORM; + } + if( nId ) + GetViewFrame()->GetDispatcher()->Execute(nId, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD ); + } + break; + + case FN_STAT_SELMODE: + { + if ( pArgs ) + { + if (SfxItemState::SET == pArgs->GetItemState( nWhich, true, &pItem)) + { + switch ( static_cast<const SfxUInt16Item *>(pItem)->GetValue() ) + { + case 0: rSh.EnterStdMode(); break; + case 1: rSh.EnterExtMode(); break; + case 2: rSh.EnterAddMode(); break; + case 3: rSh.EnterBlockMode(); break; + } + } + } + else + { + + if( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() ) + rSh.ToggleExtMode(); + else if ( rSh.IsExtMode() ) + { + rSh.ToggleExtMode(); + rSh.ToggleAddMode(); + } + else if ( rSh.IsAddMode() ) + { + rSh.ToggleAddMode(); + rSh.ToggleBlockMode(); + } + else + rSh.ToggleBlockMode(); + } + bUp = true; + break; + } + case FN_SET_ADD_MODE: + rSh.ToggleAddMode(); + nWhich = FN_STAT_SELMODE; + bUp = true; + break; + case FN_SET_BLOCK_MODE: + rSh.ToggleBlockMode(); + nWhich = FN_STAT_SELMODE; + bUp = true; + break; + case FN_SET_EXT_MODE: + rSh.ToggleExtMode(); + nWhich = FN_STAT_SELMODE; + bUp = true; + break; + case SID_ATTR_INSERT: + SwPostItMgr* pMgr = GetPostItMgr(); + if ( pMgr && pMgr->HasActiveSidebarWin() ) + { + pMgr->ToggleInsModeOnActiveSidebarWin(); + } + else + rSh.ToggleInsMode(); + bUp = true; + break; + + } + if ( bUp ) + { + SfxBindings &rBnd = GetViewFrame()->GetBindings(); + rBnd.Invalidate(nWhich); + rBnd.Update(nWhich); + } +} + +void SwView::InsFrameMode(sal_uInt16 nCols) +{ + if ( m_pWrtShell->HasWholeTabSelection() ) + { + SwFlyFrameAttrMgr aMgr( true, m_pWrtShell.get(), Frmmgr_Type::TEXT, nullptr ); + + const SwFrameFormat &rPageFormat = + m_pWrtShell->GetPageDesc(m_pWrtShell->GetCurPageDesc()).GetMaster(); + SwTwips lWidth = rPageFormat.GetFrameSize().GetWidth(); + const SvxLRSpaceItem &rLR = rPageFormat.GetLRSpace(); + lWidth -= rLR.GetLeft() + rLR.GetRight(); + aMgr.SetSize(Size(lWidth, aMgr.GetSize().Height())); + if(nCols > 1) + { + SwFormatCol aCol; + aCol.Init( nCols, aCol.GetGutterWidth(), aCol.GetWishWidth() ); + aMgr.SetCol( aCol ); + } + aMgr.InsertFlyFrame(); + } + else + GetEditWin().InsFrame(nCols); +} + +/// show "edit link" dialog +void SwView::EditLinkDlg() +{ + bool bWeb = dynamic_cast<SwWebView*>( this ) != nullptr; + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractLinksDialog> pDlg(pFact->CreateLinksDialog(GetViewFrame()->GetWindow().GetFrameWeld(), &GetWrtShell().GetLinkManager(), bWeb)); + pDlg->Execute(); +} + +bool SwView::JumpToSwMark( const OUString& rMark ) +{ + bool bRet = false; + if( !rMark.isEmpty() ) + { + // place bookmark at top-center + bool bSaveCC = m_bCenterCursor; + bool bSaveCT = m_bTopCursor; + SetCursorAtTop( true ); + + // For scrolling the FrameSet, the corresponding shell needs to have the focus. + bool bHasShFocus = m_pWrtShell->HasShellFocus(); + if( !bHasShFocus ) + m_pWrtShell->ShellGetFocus(); + + const SwFormatINetFormat* pINet; + OUString sCmp; + OUString sMark( INetURLObject::decode( rMark, + INetURLObject::DecodeMechanism::WithCharset )); + + sal_Int32 nLastPos, nPos = sMark.indexOf( cMarkSeparator ); + if( -1 != nPos ) + while( -1 != ( nLastPos = sMark.indexOf( cMarkSeparator, nPos + 1 )) ) + nPos = nLastPos; + + IDocumentMarkAccess::const_iterator_t ppMark; + IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); + if( -1 != nPos ) + sCmp = sMark.copy(nPos + 1).replaceAll(" ", ""); + + if( !sCmp.isEmpty() ) + { + OUString sName( sMark.copy( 0, nPos ) ); + sCmp = sCmp.toAsciiLowerCase(); + FlyCntType eFlyType = FLYCNTTYPE_ALL; + + if( sCmp == "region" ) + { + m_pWrtShell->EnterStdMode(); + bRet = m_pWrtShell->GotoRegion( sName ); + } + else if( sCmp == "outline" ) + { + m_pWrtShell->EnterStdMode(); + bRet = m_pWrtShell->GotoOutline( sName ); + } + else if( sCmp == "frame" ) + eFlyType = FLYCNTTYPE_FRM; + else if( sCmp == "graphic" ) + eFlyType = FLYCNTTYPE_GRF; + else if( sCmp == "ole" ) + eFlyType = FLYCNTTYPE_OLE; + else if( sCmp == "table" ) + { + m_pWrtShell->EnterStdMode(); + bRet = m_pWrtShell->GotoTable( sName ); + } + else if( sCmp == "sequence" ) + { + m_pWrtShell->EnterStdMode(); + sal_Int32 nNoPos = sName.indexOf( cSequenceMarkSeparator ); + if ( nNoPos != -1 ) + { + sal_uInt16 nSeqNo = sName.copy( nNoPos + 1 ).toInt32(); + sName = sName.copy( 0, nNoPos ); + bRet = m_pWrtShell->GotoRefMark(sName, REF_SEQUENCEFLD, nSeqNo); + } + } + else if( sCmp == "text" ) + { + // normal text search + m_pWrtShell->EnterStdMode(); + + i18nutil::SearchOptions2 aSearchOpt( + SearchAlgorithms_ABSOLUTE, 0, + sName, OUString(), + SvtSysLocale().GetLanguageTag().getLocale(), + 0,0,0, + TransliterationFlags::IGNORE_CASE, + SearchAlgorithms2::ABSOLUTE, + '\\' ); + + //todo/mba: assuming that notes shouldn't be searched + if( m_pWrtShell->SearchPattern( aSearchOpt, false/*bSearchInNotes*/, SwDocPositions::Start, SwDocPositions::End )) + { + m_pWrtShell->EnterStdMode(); // remove the selection + bRet = true; + } + } + else if( pMarkAccess->getAllMarksEnd() != (ppMark = pMarkAccess->findMark(sMark)) ) + { + bRet = m_pWrtShell->GotoMark( *ppMark, false ); + } + else if( nullptr != ( pINet = m_pWrtShell->FindINetAttr( sMark ) )) { + m_pWrtShell->addCurrentPosition(); + bRet = m_pWrtShell->GotoINetAttr( *pINet->GetTextINetFormat() ); + } + + // for all types of Flys + if( FLYCNTTYPE_ALL != eFlyType && m_pWrtShell->GotoFly( sName, eFlyType )) + { + bRet = true; + if( FLYCNTTYPE_FRM == eFlyType ) + { + // TextFrames: set Cursor in the frame + m_pWrtShell->UnSelectFrame(); + m_pWrtShell->LeaveSelFrameMode(); + } + else + { + m_pWrtShell->HideCursor(); + m_pWrtShell->EnterSelFrameMode(); + } + } + } + else if( pMarkAccess->getAllMarksEnd() != (ppMark = pMarkAccess->findMark(sMark))) + { + bRet = m_pWrtShell->GotoMark( *ppMark, false ); + } + else if( nullptr != ( pINet = m_pWrtShell->FindINetAttr( sMark ) )) + bRet = m_pWrtShell->GotoINetAttr( *pINet->GetTextINetFormat() ); + + // make selection visible later + if ( m_aVisArea.IsEmpty() ) + m_bMakeSelectionVisible = true; + + // reset ViewStatus + SetCursorAtTop( bSaveCT, bSaveCC ); + + if( !bHasShFocus ) + m_pWrtShell->ShellLoseFocus(); + } + return bRet; +} + +// #i67305# Undo after insert from file: +// Undo "Insert form file" crashes with documents imported from binary filter (.sdw) => disabled +// Undo "Insert form file" crashes with (.odt) documents crashes if these documents contains +// page styles with active header/footer => disabled for those documents +static size_t lcl_PageDescWithHeader( const SwDoc& rDoc ) +{ + size_t nRet = 0; + size_t nCnt = rDoc.GetPageDescCnt(); + for( size_t i = 0; i < nCnt; ++i ) + { + const SwPageDesc& rPageDesc = rDoc.GetPageDesc( i ); + const SwFrameFormat& rMaster = rPageDesc.GetMaster(); + const SfxPoolItem* pItem; + if( ( SfxItemState::SET == rMaster.GetAttrSet().GetItemState( RES_HEADER, false, &pItem ) && + static_cast<const SwFormatHeader*>(pItem)->IsActive() ) || + ( SfxItemState::SET == rMaster.GetAttrSet().GetItemState( RES_FOOTER, false, &pItem ) && + static_cast<const SwFormatFooter*>(pItem)->IsActive()) ) + ++nRet; + } + return nRet; // number of page styles with active header/footer +} + +void SwView::ExecuteInsertDoc( SfxRequest& rRequest, const SfxPoolItem* pItem ) +{ + m_pViewImpl->InitRequest( rRequest ); + m_pViewImpl->SetParam( pItem ? 1 : 0 ); + const sal_uInt16 nSlot = rRequest.GetSlot(); + + if ( !pItem ) + { + InsertDoc( nSlot, "", "" ); + } + else + { + OUString sFile, sFilter; + sFile = static_cast<const SfxStringItem *>( pItem )->GetValue(); + if ( SfxItemState::SET == rRequest.GetArgs()->GetItemState( FN_PARAM_1, true, &pItem ) ) + sFilter = static_cast<const SfxStringItem *>(pItem )->GetValue(); + + bool bHasFileName = !sFile.isEmpty(); + long nFound = InsertDoc( nSlot, sFile, sFilter ); + + if ( bHasFileName ) + { + rRequest.SetReturnValue( SfxBoolItem( nSlot, nFound != -1 ) ); + rRequest.Done(); + } + } +} + +long SwView::InsertDoc( sal_uInt16 nSlotId, const OUString& rFileName, const OUString& rFilterName, sal_Int16 nVersion ) +{ + std::unique_ptr<SfxMedium> pMed; + SwDocShell* pDocSh = GetDocShell(); + + if( !rFileName.isEmpty() ) + { + SfxObjectFactory& rFact = pDocSh->GetFactory(); + std::shared_ptr<const SfxFilter> pFilter = rFact.GetFilterContainer()->GetFilter4FilterName( rFilterName ); + if ( !pFilter ) + { + pMed.reset(new SfxMedium(rFileName, StreamMode::READ, nullptr, nullptr )); + SfxFilterMatcher aMatcher( rFact.GetFilterContainer()->GetName() ); + pMed->UseInteractionHandler( true ); + ErrCode nErr = aMatcher.GuessFilter(*pMed, pFilter, SfxFilterFlags::NONE); + if ( nErr ) + pMed.reset(); + else + pMed->SetFilter( pFilter ); + } + else + pMed.reset(new SfxMedium(rFileName, StreamMode::READ, pFilter, nullptr)); + } + else + { + m_pViewImpl->StartDocumentInserter( + // tdf#118578 allow inserting any Writer document except GlobalDoc + SwDocShell::Factory().GetFactoryName(), + LINK( this, SwView, DialogClosedHdl ), + nSlotId + ); + return -1; + } + + if( !pMed ) + return -1; + + return InsertMedium( nSlotId, std::move(pMed), nVersion ); +} + +long SwView::InsertMedium( sal_uInt16 nSlotId, std::unique_ptr<SfxMedium> pMedium, sal_Int16 nVersion ) +{ + bool bInsert = false, bCompare = false; + long nFound = 0; + SwDocShell* pDocSh = GetDocShell(); + + switch( nSlotId ) + { + case SID_DOCUMENT_MERGE: break; + case SID_DOCUMENT_COMPARE: bCompare = true; break; + case SID_INSERTDOC: bInsert = true; break; + + default: + OSL_ENSURE( false, "unknown SlotId!" ); + bInsert = true; + break; + } + + if( bInsert ) + { + uno::Reference< frame::XDispatchRecorder > xRecorder = + GetViewFrame()->GetBindings().GetRecorder(); + if ( xRecorder.is() ) + { + SfxRequest aRequest(GetViewFrame(), SID_INSERTDOC); + aRequest.AppendItem(SfxStringItem(SID_INSERTDOC, pMedium->GetOrigURL())); + if(pMedium->GetFilter()) + aRequest.AppendItem(SfxStringItem(FN_PARAM_1, pMedium->GetFilter()->GetName())); + aRequest.Done(); + } + + SfxObjectShellRef aRef( pDocSh ); + + ErrCode nError = SfxObjectShell::HandleFilter( pMedium.get(), pDocSh ); + // #i16722# aborted? + if(nError != ERRCODE_NONE) + { + return -1; + } + + pMedium->Download(); // start download if needed + if( aRef.is() && 1 < aRef->GetRefCount() ) // still a valid ref? + { + SwReaderPtr pRdr; + Reader *pRead = pDocSh->StartConvertFrom(*pMedium, pRdr, m_pWrtShell.get()); + if( pRead || + (pMedium->GetFilter()->GetFilterFlags() & SfxFilterFlags::STARONEFILTER) ) + { + size_t nUndoCheck = 0; + SwDoc *pDoc = pDocSh->GetDoc(); + if( pRead && pDocSh->GetDoc() ) + nUndoCheck = lcl_PageDescWithHeader( *pDoc ); + ErrCode nErrno; + { //Scope for SwWait-Object, to be able to execute slots + //outside this scope. + SwWait aWait( *GetDocShell(), true ); + m_pWrtShell->StartAllAction(); + if ( m_pWrtShell->HasSelection() ) + m_pWrtShell->DelRight(); // delete selections + if( pRead ) + { + nErrno = pRdr->Read( *pRead ); // and insert document + pRdr.reset(); + } + else + { + ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); + uno::Reference<text::XTextRange> const xInsertPosition( + SwXTextRange::CreateXTextRange(*pDoc, + *m_pWrtShell->GetCursor()->GetPoint(), nullptr)); + nErrno = pDocSh->ImportFrom(*pMedium, xInsertPosition) + ? ERRCODE_NONE : ERR_SWG_READ_ERROR; + } + + } + + // update all "table of ..." sections if needed + if( m_pWrtShell->IsUpdateTOX() ) + { + SfxRequest aReq( FN_UPDATE_TOX, SfxCallMode::SLOT, GetPool() ); + Execute( aReq ); + m_pWrtShell->SetUpdateTOX( false ); // reset + } + + if( pDoc ) + { // Disable Undo for .sdw or + // if the number of page styles with header/footer has changed + if( !pRead || nUndoCheck != lcl_PageDescWithHeader( *pDoc ) ) + { + pDoc->GetIDocumentUndoRedo().DelAllUndoObj(); + } + } + + m_pWrtShell->EndAllAction(); + if( nErrno ) + { + ErrorHandler::HandleError( nErrno ); + nFound = nErrno.IsError() ? -1 : 0; + } + else + nFound = 0; + } + } + } + else + { + SfxObjectShellRef xDocSh; + SfxObjectShellLock xLockRef; + + const int nRet = SwFindDocShell( xDocSh, xLockRef, pMedium->GetName(), OUString(), + OUString(), nVersion, pDocSh ); + if( nRet ) + { + SwWait aWait( *GetDocShell(), true ); + m_pWrtShell->StartAllAction(); + + m_pWrtShell->EnterStdMode(); // delete selections + + if( bCompare ) + nFound = m_pWrtShell->CompareDoc( *static_cast<SwDocShell*>( xDocSh.get() )->GetDoc() ); + else + nFound = m_pWrtShell->MergeDoc( *static_cast<SwDocShell*>( xDocSh.get() )->GetDoc() ); + + m_pWrtShell->EndAllAction(); + + if (!bCompare && !nFound) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_NO_MERGE_ENTRY))); + xInfoBox->run(); + } + if( nRet==2 && xDocSh.is() ) + xDocSh->DoClose(); + } + } + + return nFound; +} + +void SwView::EnableMailMerge() +{ + m_bInMailMerge = true; + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate(FN_INSERT_FIELD_DATA_ONLY); + rBind.Update(FN_INSERT_FIELD_DATA_ONLY); +} + +#if HAVE_FEATURE_DBCONNECTIVITY + +namespace +{ + bool lcl_NeedAdditionalDataSource( const uno::Reference< XDatabaseContext >& _rDatasourceContext ) + { + Sequence < OUString > aNames = _rDatasourceContext->getElementNames(); + + return ( !aNames.hasElements() + || ( ( 1 == aNames.getLength() ) + && aNames.getConstArray()[0] == SW_MOD()->GetDBConfig()->GetBibliographySource().sDataSource + ) + ); + } +} + +#endif + +void SwView::GenerateFormLetter(bool bUseCurrentDocument) +{ +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) bUseCurrentDocument; +#else + if(bUseCurrentDocument) + { + if(!GetWrtShell().IsAnyDatabaseFieldInDoc()) + { + //check availability of data sources (except biblio source) + uno::Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference<XDatabaseContext> xDBContext = DatabaseContext::create(xContext); + bool bCallAddressPilot = false; + if ( lcl_NeedAdditionalDataSource( xDBContext ) ) + { + // no data sources are available - create a new one + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/datasourcesunavailabledialog.ui")); + std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("DataSourcesUnavailableDialog")); + // no cancel allowed + if (RET_OK != xQuery->run()) + return; + bCallAddressPilot = true; + } + else + { + //take an existing data source or create a new one? + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractMailMergeFieldConnectionsDlg> pConnectionsDlg(pFact->CreateMailMergeFieldConnectionsDlg(GetFrameWeld())); + if(RET_OK == pConnectionsDlg->Execute()) + bCallAddressPilot = !pConnectionsDlg->IsUseExistingConnections(); + else + return; + + } + if(bCallAddressPilot) + { + GetViewFrame()->GetDispatcher()->Execute( + SID_ADDRESS_DATA_SOURCE, SfxCallMode::SYNCHRON); + if ( lcl_NeedAdditionalDataSource( xDBContext ) ) + // no additional data source has been created + // -> assume that the user has cancelled the pilot + return; + } + + //call insert fields with database field page available, only + SfxViewFrame* pVFrame = GetViewFrame(); + //at first hide the default field dialog if currently visible + pVFrame->SetChildWindow(FN_INSERT_FIELD, false); + //enable the status of the db field dialog - it is disabled in the status method + //to prevent creation of the dialog without mail merge active + EnableMailMerge(); + //then show the "Data base only" field dialog + SfxBoolItem aOn(FN_INSERT_FIELD_DATA_ONLY, true); + pVFrame->GetDispatcher()->ExecuteList(FN_INSERT_FIELD_DATA_ONLY, + SfxCallMode::SYNCHRON, { &aOn }); + return; + } + else + { + OUString sSource; + if(!GetWrtShell().IsFieldDataSourceAvailable(sSource)) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/warndatasourcedialog.ui")); + std::unique_ptr<weld::MessageDialog> xWarning(xBuilder->weld_message_dialog("WarnDataSourceDialog")); + OUString sTmp(xWarning->get_primary_text()); + xWarning->set_primary_text(sTmp.replaceFirst("%1", sSource)); + if (RET_OK == xWarning->run()) + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog( nullptr, SID_OPTIONS_DATABASES )); + pDlg->Execute(); + } + return ; + } + } + SwDBManager* pDBManager = GetWrtShell().GetDBManager(); + + SwDBData aData; + SwWrtShell &rSh = GetWrtShell(); + + std::vector<OUString> aDBNameList; + std::vector<OUString> aAllDBNames; + rSh.GetAllUsedDB( aDBNameList, &aAllDBNames ); + if(!aDBNameList.empty()) + { + OUString sDBName(aDBNameList[0]); + sal_Int32 nIdx {0}; + aData.sDataSource = sDBName.getToken(0, DB_DELIM, nIdx); + aData.sCommand = sDBName.getToken(0, DB_DELIM, nIdx); + aData.nCommandType = sDBName.getToken(0, DB_DELIM, nIdx).toInt32(); + } + rSh.EnterStdMode(); // force change in text shell; necessary for mixing DB fields + AttrChangedNotify(nullptr); + + if (pDBManager) + { + Sequence<PropertyValue> aProperties(3); + PropertyValue* pValues = aProperties.getArray(); + pValues[0].Name = "DataSourceName"; + pValues[1].Name = "Command"; + pValues[2].Name = "CommandType"; + pValues[0].Value <<= aData.sDataSource; + pValues[1].Value <<= aData.sCommand; + pValues[2].Value <<= aData.nCommandType; + pDBManager->ExecuteFormLetter(GetWrtShell(), aProperties); + } + } + else + { + // call documents and template dialog + SfxApplication* pSfxApp = SfxGetpApp(); + vcl::Window* pTopWin = pSfxApp->GetTopWindow(); + + SfxTemplateManagerDlg aDocTemplDlg(GetFrameWeld()); + int nRet = aDocTemplDlg.run(); + bool bNewWin = false; + if ( nRet == RET_OK ) + { + if ( pTopWin != pSfxApp->GetTopWindow() ) + { + // the dialogue opens a document -> a new TopWindow appears + pTopWin = pSfxApp->GetTopWindow(); + bNewWin = true; + } + } + + if ( bNewWin ) + // after the destruction of the dialogue its parent comes to top, + // but we want that the new document is on top + pTopWin->ToTop(); + } +#endif +} + +IMPL_LINK( SwView, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ) +{ + if ( ERRCODE_NONE != _pFileDlg->GetError() ) + return; + + std::unique_ptr<SfxMedium> pMed = m_pViewImpl->CreateMedium(); + if ( !pMed ) + return; + + const sal_uInt16 nSlot = m_pViewImpl->GetRequest()->GetSlot(); + long nFound = InsertMedium( nSlot, std::move(pMed), m_pViewImpl->GetParam() ); + + if ( SID_INSERTDOC == nSlot ) + { + if ( m_pViewImpl->GetParam() == 0 ) + { + m_pViewImpl->GetRequest()->SetReturnValue( SfxBoolItem( nSlot, nFound != -1 ) ); + m_pViewImpl->GetRequest()->Ignore(); + } + else + { + m_pViewImpl->GetRequest()->SetReturnValue( SfxBoolItem( nSlot, nFound != -1 ) ); + m_pViewImpl->GetRequest()->Done(); + } + } + else if ( SID_DOCUMENT_COMPARE == nSlot || SID_DOCUMENT_MERGE == nSlot ) + { + m_pViewImpl->GetRequest()->SetReturnValue( SfxInt32Item( nSlot, nFound ) ); + + if ( nFound > 0 ) // show Redline browser + { + SfxViewFrame* pVFrame = GetViewFrame(); + pVFrame->ShowChildWindow(FN_REDLINE_ACCEPT); + + // re-initialize Redline dialog + sal_uInt16 nId = SwRedlineAcceptChild::GetChildWindowId(); + SwRedlineAcceptChild* pRed = static_cast<SwRedlineAcceptChild*>(pVFrame->GetChildWindow( nId )); + if ( pRed ) + pRed->ReInitDlg( GetDocShell() ); + } + } +} + +void SwView::ExecuteScan( SfxRequest& rReq ) +{ + if (m_pViewImpl) + m_pViewImpl->ExecuteScan(rReq) ; +} + +const OUString& SwView::GetOldGrfCat() +{ + return GetCachedString(OldGrfCat); +} + +void SwView::SetOldGrfCat(const OUString& sStr) +{ + SetCachedString(OldGrfCat, sStr); +} + +const OUString& SwView::GetOldTabCat() +{ + return GetCachedString(OldTabCat); +} + +void SwView::SetOldTabCat(const OUString& sStr) +{ + SetCachedString(OldTabCat, sStr); +} + +const OUString& SwView::GetOldFrameCat() +{ + return GetCachedString(OldFrameCat); +} + +void SwView::SetOldFrameCat(const OUString& sStr) +{ + SetCachedString(OldFrameCat, sStr); +} + +const OUString& SwView::GetOldDrwCat() +{ + return GetCachedString(OldDrwCat); +} + +void SwView::SetOldDrwCat(const OUString& sStr) +{ + SwView::SetCachedString(OldDrwCat, sStr); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewcoll.cxx b/sw/source/uibase/uiview/viewcoll.cxx new file mode 100644 index 000000000..94ad59d1d --- /dev/null +++ b/sw/source/uibase/uiview/viewcoll.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 <cmdid.h> +#include <uiitems.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> +#include <svl/style.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> + +void SwView::ExecColl(SfxRequest const &rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + sal_uInt16 nWhich = rReq.GetSlot(); + switch( nWhich ) + { + case FN_SET_PAGE: + { + OSL_ENSURE(false, "Not implemented"); + } + break; + case FN_SET_PAGE_STYLE: + { + if( pArgs ) + { + if (SfxItemState::SET == pArgs->GetItemState( nWhich , true, &pItem )) + { + if( static_cast<const SfxStringItem*>(pItem)->GetValue() != + GetWrtShell().GetCurPageStyle() ) + { + SfxStringItem aName(SID_STYLE_APPLY, + static_cast<const SfxStringItem*>(pItem)->GetValue()); + SfxUInt16Item aFamItem( SID_STYLE_FAMILY, + sal_uInt16(SfxStyleFamily::Page)); + SwPtrItem aShell(FN_PARAM_WRTSHELL, GetWrtShellPtr()); + SfxRequest aReq(SID_STYLE_APPLY, SfxCallMode::SLOT, GetPool()); + aReq.AppendItem(aName); + aReq.AppendItem(aFamItem); + aReq.AppendItem(aShell); + GetCurShell()->ExecuteSlot(aReq); + } + } + } + else + { + SfxRequest aReq(FN_FORMAT_PAGE_DLG, SfxCallMode::SLOT, GetPool()); + GetCurShell()->ExecuteSlot(aReq); + } + } + break; + default: + OSL_FAIL("wrong CommandProcessor for Dispatch"); + return; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewdlg.cxx b/sw/source/uibase/uiview/viewdlg.cxx new file mode 100644 index 000000000..c6f81808e --- /dev/null +++ b/sw/source/uibase/uiview/viewdlg.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 <sfx2/request.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> + +void SwView::ExecDlg(SfxRequest const &rReq) +{ + // Thus, from the basic no dialogues for background views are called: + const SfxPoolItem* pItem = nullptr; + const SfxItemSet* pArgs = rReq.GetArgs(); + + sal_uInt16 nSlot = rReq.GetSlot(); + if(pArgs) + pArgs->GetItemState( GetPool().GetWhich(nSlot), false, &pItem ); + + switch ( nSlot ) + { + case FN_CHANGE_PAGENUM: + { + if ( pItem ) + { + sal_uInt16 nValue = static_cast<const SfxUInt16Item *>(pItem)->GetValue(); + sal_uInt16 nOldValue = m_pWrtShell->GetPageOffset(); + sal_uInt16 nPage, nLogPage; + m_pWrtShell->GetPageNum( nPage, nLogPage, + m_pWrtShell->IsCursorVisible(), false); + + if(nValue != nOldValue || nValue != nLogPage) + { + if(!nOldValue) + m_pWrtShell->SetNewPageOffset( nValue ); + else + m_pWrtShell->SetPageOffset( nValue ); + } + } + } + break; + + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewdlg2.cxx b/sw/source/uibase/uiview/viewdlg2.cxx new file mode 100644 index 000000000..0283752c9 --- /dev/null +++ b/sw/source/uibase/uiview/viewdlg2.cxx @@ -0,0 +1,283 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/request.hxx> +#include <sfx2/objface.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdview.hxx> +#include <fldmgr.hxx> +#include <expfld.hxx> +#include <modcfg.hxx> + +#include <swmodule.hxx> +#include <view.hxx> +#include <wview.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <caption.hxx> +#include <poolfmt.hxx> +#include <edtwin.hxx> +#include <SwStyleNameMapper.hxx> + +#include <swabstdlg.hxx> + +#include <strings.hrc> + +#include <memory> + +using namespace css; + +void SwView::ExecDlgExt(SfxRequest const &rReq) +{ + switch ( rReq.GetSlot() ) + { + case FN_INSERT_CAPTION: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDialog(pFact->CreateSwCaptionDialog(GetFrameWeld(), *this )); + pDialog->Execute(); + break; + } + case SID_INSERT_SIGNATURELINE: + case SID_EDIT_SIGNATURELINE: + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + const uno::Reference<frame::XModel> xModel(GetCurrentDocument()); + ScopedVclPtr<AbstractSignatureLineDialog> pDialog(pFact->CreateSignatureLineDialog( + GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_SIGNATURELINE)); + pDialog->Execute(); + break; + } + case SID_INSERT_QRCODE: + case SID_EDIT_QRCODE: + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + const uno::Reference<frame::XModel> xModel(GetCurrentDocument()); + ScopedVclPtr<AbstractQrCodeGenDialog> pDialog(pFact->CreateQrCodeGenDialog( + GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_QRCODE)); + pDialog->Execute(); + break; + } + case SID_SIGN_SIGNATURELINE: + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + const uno::Reference<frame::XModel> xModel(GetCurrentDocument()); + ScopedVclPtr<AbstractSignSignatureLineDialog> pDialog( + pFact->CreateSignSignatureLineDialog(GetFrameWeld(), xModel)); + pDialog->Execute(); + break; + } + case FN_EDIT_FOOTNOTE: + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractInsFootNoteDlg> pDlg(pFact->CreateInsFootNoteDlg( + GetFrameWeld(), *m_pWrtShell, true)); + + pDlg->SetHelpId(GetStaticInterface()->GetSlot(FN_EDIT_FOOTNOTE)->GetCommand()); + pDlg->SetText( SwResId(STR_EDIT_FOOTNOTE) ); + pDlg->Execute(); + break; + } + } +} + +bool SwView::isSignatureLineSelected() const +{ + SwWrtShell& rSh = GetWrtShell(); + SdrView* pSdrView = rSh.GetDrawView(); + if (!pSdrView) + return false; + + if (pSdrView->GetMarkedObjectCount() != 1) + return false; + + SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0); + if (!pPickObj) + return false; + + SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj); + if (!pGraphic) + return false; + + return pGraphic->isSignatureLine(); +} + +bool SwView::isSignatureLineSigned() const +{ + SwWrtShell& rSh = GetWrtShell(); + SdrView* pSdrView = rSh.GetDrawView(); + if (!pSdrView) + return false; + + if (pSdrView->GetMarkedObjectCount() != 1) + return false; + + SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0); + if (!pPickObj) + return false; + + SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj); + if (!pGraphic) + return false; + + return pGraphic->isSignatureLineSigned(); +} + +bool SwView::isQRCodeSelected() const +{ + SwWrtShell& rSh = GetWrtShell(); + SdrView* pSdrView = rSh.GetDrawView(); + if (!pSdrView) + return false; + + if (pSdrView->GetMarkedObjectCount() != 1) + return false; + + SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0); + if (!pPickObj) + return false; + + SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj); + if (!pGraphic) + return false; + + return pGraphic->getQrCode() != nullptr; +} + +void SwView::AutoCaption(const sal_uInt16 nType, const SvGlobalName *pOleId) +{ + SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig(); + + bool bWeb = dynamic_cast<SwWebView*>( this ) != nullptr; + if (pModOpt->IsInsWithCaption(bWeb)) + { + const InsCaptionOpt *pOpt = pModOpt->GetCapOption(bWeb, static_cast<SwCapObjType>(nType), pOleId); + if (pOpt && pOpt->UseCaption()) + InsertCaption(pOpt); + } +} + +void SwView::InsertCaption(const InsCaptionOpt *pOpt) +{ + if (!pOpt) + return; + + const OUString &rName = pOpt->GetCategory(); + + // Is there a pool template with the same name? + SwWrtShell &rSh = GetWrtShell(); + if(!rName.isEmpty()) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName(rName, SwGetPoolIdFromName::TxtColl); + if( USHRT_MAX != nPoolId ) + rSh.GetTextCollFromPool(nPoolId); + // Pool template does not exist: Does it exist on the document? + else if( !rSh.GetParaStyle(rName) ) + { + // It also does not exist in the document: generate + SwTextFormatColl* pDerivedFrom = rSh.GetTextCollFromPool(RES_POOLCOLL_LABEL); + rSh.MakeTextFormatColl(rName, pDerivedFrom); + } + } + + SelectionType eType = rSh.GetSelectionType(); + if (eType & SelectionType::Ole) + eType = SelectionType::Graphic; + + const SwLabelType eT = (eType & SelectionType::Table) ? SwLabelType::Table : + (eType & SelectionType::Frame) ? SwLabelType::Fly : + (eType == SelectionType::Text) ? SwLabelType::Fly : + (eType & SelectionType::DrawObject) ? SwLabelType::Draw : + SwLabelType::Object; + + SwFieldMgr aMgr(&rSh); + SwSetExpFieldType* pFieldType = + static_cast<SwSetExpFieldType*>(aMgr.GetFieldType(SwFieldIds::SetExp, rName)); + if (!pFieldType && !rName.isEmpty() ) + { + // Create new field types + SwSetExpFieldType aSwSetExpFieldType(rSh.GetDoc(), rName, nsSwGetSetExpType::GSE_SEQ); + aMgr.InsertFieldType(aSwSetExpFieldType); + pFieldType = static_cast<SwSetExpFieldType*>(aMgr.GetFieldType(SwFieldIds::SetExp, rName)); + } + + if (!pOpt->IgnoreSeqOpts()) + { + if (pFieldType) + { + pFieldType->SetDelimiter(pOpt->GetSeparator()); + pFieldType->SetOutlineLvl( static_cast< sal_uInt8 >(pOpt->GetLevel()) ); + } + } + + sal_uInt16 nID = USHRT_MAX; + SwFieldType* pType = nullptr; + const size_t nCount = aMgr.GetFieldTypeCount(); + if( !rName.isEmpty() ) + { + for (size_t i = 0; i < nCount; ++i) + { + pType = aMgr.GetFieldType(SwFieldIds::Unknown, i); + OUString aTmpName( pType->GetName() ); + if (aTmpName == rName && pType->Which() == SwFieldIds::SetExp) + { + nID = i; + OSL_ENSURE(nID==i, "Downcasting to sal_uInt16 lost information!"); + break; + } + } + } + rSh.StartAllAction(); + + GetWrtShell().InsertLabel( eT, + pOpt->GetCaption(), + !pOpt->IgnoreSeqOpts() ? OUString() : pOpt->GetSeparator(), + pOpt->GetNumSeparator(), + !pOpt->GetPos(), + nID, + pOpt->GetCharacterStyle(), + pOpt->CopyAttributes() ); + // Set Number Format + if(pType) + static_cast<SwSetExpFieldType*>(pType)->SetSeqFormat(pOpt->GetNumType()); + + rSh.UpdateExpFields( true ); + + rSh.EndAllAction(); + + if ( rSh.IsFrameSelected() ) + { + GetEditWin().StopInsFrame(); + rSh.EnterSelFrameMode(); + } + + // remember category + if (eType & SelectionType::Graphic) + SetOldGrfCat(rName); + else if( eType & SelectionType::Table) + SetOldTabCat(rName); + else if( eType & SelectionType::Frame) + SetOldFrameCat(rName); + else if( eType == SelectionType::Text) + SetOldFrameCat(rName); + else if( eType & SelectionType::DrawObject) + SetOldDrwCat(rName); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewdraw.cxx b/sw/source/uibase/uiview/viewdraw.cxx new file mode 100644 index 000000000..281e2d095 --- /dev/null +++ b/sw/source/uibase/uiview/viewdraw.cxx @@ -0,0 +1,749 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/itempool.hxx> +#include <svl/stritem.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdview.hxx> +#include <svx/svdpage.hxx> +#include <editeng/outliner.hxx> +#include <svx/fmview.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <sfx2/viewfrm.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <textboxhelper.hxx> +#include <editeng/langitem.hxx> +#include <svx/fontworkbar.hxx> +#include <svx/fontworkgallery.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdetc.hxx> +#include <editeng/editstat.hxx> +#include <sfx2/request.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdoutl.hxx> +#include <vcl/ptrstyle.hxx> + +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <cmdid.h> +#include <drwbassh.hxx> +#include <beziersh.hxx> +#include <conrect.hxx> +#include <conpoly.hxx> +#include <conarc.hxx> +#include <conform.hxx> +#include <concustomshape.hxx> +#include <dselect.hxx> +#include <edtwin.hxx> + +#include <dcontact.hxx> + +#include <svx/svdpagv.hxx> +#include <svx/extrusionbar.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +using namespace ::com::sun::star; + +// Execute Drawing-Ids + +void SwView::ExecDraw(SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + const SfxStringItem* pStringItem = nullptr; + SdrView *pSdrView = m_pWrtShell->GetDrawView(); + bool bDeselect = false; + + sal_uInt16 nSlotId = rReq.GetSlot(); + if(pArgs && SfxItemState::SET == pArgs->GetItemState(GetPool().GetWhich(nSlotId), false, &pItem)) + pStringItem = dynamic_cast< const SfxStringItem*>(pItem); + + if (nSlotId == SID_OBJECT_SELECT && m_nFormSfxId == nSlotId) + { + bDeselect = true; + } + else if (nSlotId == SID_FM_CREATE_CONTROL) + { + const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER); + if( pIdentifierItem ) + { + sal_uInt16 nNewId = pIdentifierItem->GetValue(); + if (nNewId == m_nFormSfxId) + { + bDeselect = true; + GetViewFrame()->GetDispatcher()->Execute(SID_FM_LEAVE_CREATE); // Button should popping out + } + } + } + else if (nSlotId == SID_FM_CREATE_FIELDCONTROL) + { + FmFormView* pFormView = dynamic_cast<FmFormView*>(pSdrView); + if (pFormView) + { + const SfxUnoAnyItem* pDescriptorItem = rReq.GetArg<SfxUnoAnyItem>(SID_FM_DATACCESS_DESCRIPTOR); + OSL_ENSURE( pDescriptorItem, "SwView::ExecDraw(SID_FM_CREATE_FIELDCONTROL): invalid request args!" ); + if( pDescriptorItem ) + { + svx::ODataAccessDescriptor aDescriptor( pDescriptorItem->GetValue() ); + SdrObjectUniquePtr pObj = pFormView->CreateFieldControl( aDescriptor ); + + if ( pObj ) + { + Size aDocSize(m_pWrtShell->GetDocSize()); + const SwRect& rVisArea = m_pWrtShell->VisArea(); + Point aStartPos = rVisArea.Center(); + if(rVisArea.Width() > aDocSize.Width()) + aStartPos.setX( aDocSize.Width() / 2 + rVisArea.Left() ); + if(rVisArea.Height() > aDocSize.Height()) + aStartPos.setY( aDocSize.Height() / 2 + rVisArea.Top() ); + + //determine the size of the object + if(pObj->IsGroupObject()) + { + const tools::Rectangle& rBoundRect = static_cast<SdrObjGroup*>(pObj.get())->GetCurrentBoundRect(); + aStartPos.AdjustX( -(rBoundRect.GetWidth()/2) ); + aStartPos.AdjustY( -(rBoundRect.GetHeight()/2) ); + } + + // TODO: unmark all other + m_pWrtShell->EnterStdMode(); + m_pWrtShell->SwFEShell::InsertDrawObj( *(pObj.release()), aStartPos ); + } + } + } + } + else if ( nSlotId == SID_FONTWORK_GALLERY_FLOATER ) + { + vcl::Window& rWin = m_pWrtShell->GetView().GetViewFrame()->GetWindow(); + + rWin.EnterWait(); + + if( !m_pWrtShell->HasDrawView() ) + m_pWrtShell->MakeDrawView(); + + pSdrView = m_pWrtShell->GetDrawView(); + if (pSdrView) + { + SdrObject* pObj = nullptr; + svx::FontWorkGalleryDialog aDlg(rWin.GetFrameWeld(), *pSdrView); + aDlg.SetSdrObjectRef( &pObj, pSdrView->GetModel() ); + aDlg.run(); + if ( pObj ) + { + Size aDocSize( m_pWrtShell->GetDocSize() ); + const SwRect& rVisArea = m_pWrtShell->VisArea(); + Point aPos( rVisArea.Center() ); + Size aSize; + Size aPrefSize( pObj->GetSnapRect().GetSize() ); + + if( rVisArea.Width() > aDocSize.Width()) + aPos.setX( aDocSize.Width() / 2 + rVisArea.Left() ); + + if(rVisArea.Height() > aDocSize.Height()) + aPos.setY( aDocSize.Height() / 2 + rVisArea.Top() ); + + if( aPrefSize.Width() && aPrefSize.Height() ) + aSize = rWin.PixelToLogic(aPrefSize, MapMode(MapUnit::MapTwip)); + else + aSize = Size( 2835, 2835 ); + + m_pWrtShell->EnterStdMode(); + m_pWrtShell->SwFEShell::InsertDrawObj( *pObj, aPos ); + rReq.Ignore (); + } + } + rWin.LeaveWait(); + } + else if ( m_nFormSfxId != USHRT_MAX ) + GetViewFrame()->GetDispatcher()->Execute( SID_FM_LEAVE_CREATE ); + + if( nSlotId == SID_DRAW_CS_ID ) + { + //deselect if same custom shape is selected again + SwDrawBase* pFuncPtr = GetDrawFuncPtr(); + if( pFuncPtr && pFuncPtr->GetSlotId() == SID_DRAW_CS_ID ) + { + ConstCustomShape* pConstCustomShape = static_cast<ConstCustomShape*>(pFuncPtr); + OUString aNew = ConstCustomShape::GetShapeTypeFromRequest( rReq ); + const OUString& aOld = pConstCustomShape->GetShapeType(); + if( aNew == aOld ) + { + bDeselect = true; + } + } + } + + //deselect if same shape is selected again (but different custom shapes do have same slot id) + if ( bDeselect || (nSlotId == m_nDrawSfxId && + (!pStringItem || (pStringItem->GetValue() == m_sDrawCustom)) + && (nSlotId != SID_DRAW_CS_ID) ) ) + { + if (GetDrawFuncPtr()) + { + GetDrawFuncPtr()->Deactivate(); + SetDrawFuncPtr(nullptr); + } + + if (m_pWrtShell->IsObjSelected() && !m_pWrtShell->IsSelFrameMode()) + m_pWrtShell->EnterSelFrameMode(); + LeaveDrawCreate(); + + AttrChangedNotify(nullptr); + return; + } + + LeaveDrawCreate(); + + if (m_pWrtShell->IsFrameSelected()) + m_pWrtShell->EnterStdMode(); // because bug #45639 + + std::unique_ptr<SwDrawBase> pFuncPtr; + + // for LibreOfficeKit - choosing a shape should construct it directly + bool bCreateDirectly = false; + + switch (nSlotId) + { + case SID_OBJECT_SELECT: + case SID_DRAW_SELECT: + pFuncPtr.reset( new DrawSelection(m_pWrtShell.get(), m_pEditWin, this) ); + m_nDrawSfxId = m_nFormSfxId = SID_OBJECT_SELECT; + m_sDrawCustom.clear(); + break; + + case SID_LINE_ARROW_END: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_ARROW_START: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + case SID_LINE_ARROWS: + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_DRAW_MEASURELINE: + case SID_DRAW_RECT: + case SID_DRAW_ELLIPSE: + case SID_DRAW_TEXT: + case SID_DRAW_TEXT_VERTICAL: + case SID_DRAW_TEXT_MARQUEE: + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + pFuncPtr.reset( new ConstRectangle(m_pWrtShell.get(), m_pEditWin, this) ); + m_nDrawSfxId = nSlotId; + m_sDrawCustom.clear(); + break; + + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_XPOLYGON: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_POLYGON: + case SID_DRAW_BEZIER_NOFILL: + case SID_DRAW_BEZIER_FILL: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_FREELINE: + pFuncPtr.reset( new ConstPolygon(m_pWrtShell.get(), m_pEditWin, this) ); + m_nDrawSfxId = nSlotId; + m_sDrawCustom.clear(); + break; + + case SID_DRAW_ARC: + case SID_DRAW_PIE: + case SID_DRAW_CIRCLECUT: + pFuncPtr.reset( new ConstArc(m_pWrtShell.get(), m_pEditWin, this) ); + m_nDrawSfxId = nSlotId; + m_sDrawCustom.clear(); + break; + + case SID_FM_CREATE_CONTROL: + { + const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER); + if( pIdentifierItem ) + nSlotId = pIdentifierItem->GetValue(); + pFuncPtr.reset( new ConstFormControl(m_pWrtShell.get(), m_pEditWin, this) ); + m_nFormSfxId = nSlotId; + } + break; + + case SID_DRAWTBX_CS_BASIC : + case SID_DRAWTBX_CS_SYMBOL : + case SID_DRAWTBX_CS_ARROW : + case SID_DRAWTBX_CS_FLOWCHART : + case SID_DRAWTBX_CS_CALLOUT : + case SID_DRAWTBX_CS_STAR : + case SID_DRAW_CS_ID : + { + pFuncPtr.reset( new ConstCustomShape(m_pWrtShell.get(), m_pEditWin, this, rReq ) ); + + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); + + m_nDrawSfxId = nSlotId; + if ( nSlotId != SID_DRAW_CS_ID ) + { + if ( pStringItem ) + { + m_sDrawCustom = pStringItem->GetValue(); + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSlotId ); + rBind.Update( nSlotId ); + } + } + } + break; + + default: + break; + } + + GetViewFrame()->GetBindings().Invalidate(SID_ATTRIBUTES_AREA); + + bool bEndTextEdit = true; + if (pFuncPtr) + { + if (GetDrawFuncPtr()) + { + GetDrawFuncPtr()->Deactivate(); + } + + auto pTempFuncPtr = pFuncPtr.get(); + SetDrawFuncPtr(std::move(pFuncPtr)); + AttrChangedNotify(nullptr); + + pTempFuncPtr->Activate(nSlotId); + NoRotate(); + if(rReq.GetModifier() == KEY_MOD1 || bCreateDirectly) + { + if(SID_OBJECT_SELECT == m_nDrawSfxId ) + { + m_pWrtShell->GotoObj(true); + } + else if (dynamic_cast<ConstCustomShape*>(pTempFuncPtr)) + { + pTempFuncPtr->CreateDefaultObject(); + } + else + { + pTempFuncPtr->CreateDefaultObject(); + pTempFuncPtr->Deactivate(); + SetDrawFuncPtr(nullptr); + LeaveDrawCreate(); + m_pWrtShell->EnterStdMode(); + SdrView *pTmpSdrView = m_pWrtShell->GetDrawView(); + const SdrMarkList& rMarkList = pTmpSdrView->GetMarkedObjectList(); + if(rMarkList.GetMarkCount() == 1 && + (SID_DRAW_TEXT == nSlotId || SID_DRAW_TEXT_VERTICAL == nSlotId || + SID_DRAW_TEXT_MARQUEE == nSlotId )) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + BeginTextEdit(pObj); + bEndTextEdit = false; + } + } + } + } + else + { + if (m_pWrtShell->IsObjSelected() && !m_pWrtShell->IsSelFrameMode()) + m_pWrtShell->EnterSelFrameMode(); + } + + if(bEndTextEdit && pSdrView && pSdrView->IsTextEdit()) + pSdrView->SdrEndTextEdit( true ); + + AttrChangedNotify(nullptr); +} + +// End drawing + +void SwView::ExitDraw() +{ + NoRotate(); + + if(m_pShell) + { + // the shell may be invalid at close/reload/SwitchToViewShell + SfxDispatcher* pDispatch = GetViewFrame()->GetDispatcher(); + sal_uInt16 nIdx = 0; + SfxShell* pTest = nullptr; + do + { + pTest = pDispatch->GetShell(nIdx++); + } + while( pTest && pTest != this && pTest != m_pShell); + if(pTest == m_pShell && + // don't call LeaveSelFrameMode() etc. for the below, + // because objects may still be selected: + dynamic_cast< const SwDrawBaseShell *>( m_pShell ) == nullptr && + dynamic_cast< const SwBezierShell *>( m_pShell ) == nullptr && + dynamic_cast< const svx::ExtrusionBar *>( m_pShell ) == nullptr && + dynamic_cast< const svx::FontworkBar *>( m_pShell ) == nullptr) + { + SdrView *pSdrView = m_pWrtShell->GetDrawView(); + + if (pSdrView && pSdrView->IsGroupEntered()) + { + pSdrView->LeaveOneGroup(); + pSdrView->UnmarkAll(); + GetViewFrame()->GetBindings().Invalidate(SID_ENTER_GROUP); + } + + if (GetDrawFuncPtr()) + { + if (m_pWrtShell->IsSelFrameMode()) + m_pWrtShell->LeaveSelFrameMode(); + GetDrawFuncPtr()->Deactivate(); + + SetDrawFuncPtr(nullptr); + LeaveDrawCreate(); + + GetViewFrame()->GetBindings().Invalidate(SID_INSERT_DRAW); + } + GetEditWin().SetPointer(PointerStyle::Text); + } + } +} + +// Disable rotate mode + +void SwView::NoRotate() +{ + if (IsDrawRotate()) + { + m_pWrtShell->SetDragMode(SdrDragMode::Move); + FlipDrawRotate(); + + const SfxBoolItem aTmp( SID_OBJECT_ROTATE, false ); + GetViewFrame()->GetBindings().SetState( aTmp ); + } +} + +// Enable DrawTextEditMode + +static bool lcl_isTextBox(SdrObject const * pObject) +{ + if (SwDrawContact* pDrawContact = static_cast<SwDrawContact*>(pObject->GetUserCall())) + { + if (SwFrameFormat* pFormat = pDrawContact->GetFormat()) + return SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT); + } + return false; +} + +bool SwView::EnterDrawTextMode(const Point& aDocPos) +{ + SwWrtShell *pSh = &GetWrtShell(); + SdrView *pSdrView = pSh->GetDrawView(); + OSL_ENSURE( pSdrView, "EnterDrawTextMode without DrawView?" ); + + bool bReturn = false; + + sal_uInt16 nOld = pSdrView->GetHitTolerancePixel(); + pSdrView->SetHitTolerancePixel( 2 ); + + SdrObject* pObj = nullptr; + SdrPageView* pPV = nullptr; + if (pSdrView->IsMarkedHit(aDocPos) && !pSdrView->PickHandle(aDocPos) && IsTextTool()) + pObj = pSdrView->PickObj(aDocPos, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::PICKTEXTEDIT); + + if (pObj && + // To allow SwDrawVirtObj text objects to be activated, allow their type, too. + ( dynamic_cast< const SdrTextObj *>( pObj ) != nullptr || + ( dynamic_cast< const SwDrawVirtObj *>( pObj ) != nullptr && + dynamic_cast< const SdrTextObj *>(&static_cast<SwDrawVirtObj*>(pObj)->GetReferencedObj() ) != nullptr ) ) && + + m_pWrtShell->IsSelObjProtected(FlyProtectFlags::Content) == FlyProtectFlags::NONE) + { + // Refuse to edit editeng text of the shape if it has textbox attached. + if (!lcl_isTextBox(pObj)) + bReturn = BeginTextEdit( pObj, pPV, m_pEditWin ); + } + + pSdrView->SetHitTolerancePixel( nOld ); + + return bReturn; +} + +bool SwView::EnterShapeDrawTextMode(SdrObject* pObject) +{ + SdrView* pSdrView = GetWrtShell().GetDrawView(); + SdrPageView* pPageView = pSdrView->GetSdrPageView(); + return BeginTextEdit(pObject, pPageView, m_pEditWin); +} + +// Enable DrawTextEditMode + +bool SwView::BeginTextEdit(SdrObject* pObj, SdrPageView* pPV, vcl::Window* pWin, + bool bIsNewObj, bool bSetSelectionToStart) +{ + SwWrtShell *pSh = &GetWrtShell(); + SdrView *pSdrView = pSh->GetDrawView(); + std::unique_ptr<SdrOutliner> pOutliner = ::SdrMakeOutliner(OutlinerMode::TextObject, *pSdrView->GetModel()); + uno::Reference< linguistic2::XSpellChecker1 > xSpell( ::GetSpellChecker() ); + if (pOutliner) + { + pOutliner->SetRefDevice(pSh->getIDocumentDeviceAccess().getReferenceDevice(false)); + pOutliner->SetSpeller(xSpell); + uno::Reference<linguistic2::XHyphenator> xHyphenator( ::GetHyphenator() ); + pOutliner->SetHyphenator( xHyphenator ); + pSh->SetCalcFieldValueHdl(pOutliner.get()); + + EEControlBits nCntrl = pOutliner->GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + + const SwViewOption *pOpt = pSh->GetViewOptions(); + + if (SwViewOption::IsFieldShadings()) + nCntrl |= EEControlBits::MARKFIELDS; + else + nCntrl &= ~EEControlBits::MARKFIELDS; + + if (pOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + pOutliner->SetControlWord(nCntrl); + const SfxPoolItem& rItem = pSh->GetDoc()->GetDefault(RES_CHRATR_LANGUAGE); + pOutliner->SetDefaultLanguage(static_cast<const SvxLanguageItem&>(rItem).GetLanguage()); + + if( bIsNewObj ) + pOutliner->SetVertical( SID_DRAW_TEXT_VERTICAL == m_nDrawSfxId || + SID_DRAW_CAPTION_VERTICAL == m_nDrawSfxId ); + + // set default horizontal text direction at outliner + EEHorizontalTextDirection aDefHoriTextDir = + pSh->IsShapeDefaultHoriTextDirR2L() ? EEHorizontalTextDirection::R2L : EEHorizontalTextDirection::L2R; + pOutliner->SetDefaultHorizontalTextDirection( aDefHoriTextDir ); + } + + // To allow editing the referenced object from a SwDrawVirtObj here + // the original needs to be fetched eventually. This ATM activates the + // text edit mode for the original object. + SdrObject* pToBeActivated = pObj; + + // Always the original object is edited. To allow the TextEdit to happen + // where the VirtObj is positioned, on demand an occurring offset is set at + // the TextEdit object. That offset is used for creating and managing the + // OutlinerView. + Point aNewTextEditOffset(0, 0); + + if (SwDrawVirtObj* pVirtObj = dynamic_cast<SwDrawVirtObj *>(pObj)) + { + pToBeActivated = &const_cast<SdrObject&>(pVirtObj->GetReferencedObj()); + aNewTextEditOffset = pVirtObj->GetOffset(); + } + + // set in each case, thus it will be correct for all objects + static_cast<SdrTextObj*>(pToBeActivated)->SetTextEditOffset(aNewTextEditOffset); + + bool bRet(pSdrView->SdrBeginTextEdit( pToBeActivated, pPV, pWin, true, pOutliner.release(), nullptr, false, false, false )); + + // #i7672# + // Since SdrBeginTextEdit actually creates the OutlinerView and thus also + // sets the background color, an own background color needs to be set + // after TextEditing was started. This is now done here. + if(bRet) + { + OutlinerView* pView = pSdrView->GetTextEditOutlinerView(); + + if(pView) + { + Color aBackground(pSh->GetShapeBackgrd()); + pView->SetBackgroundColor(aBackground); + } + + // editing should start at the end of text, spell checking at the beginning ... + ESelection aNewSelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND); + if (bSetSelectionToStart) + aNewSelection = ESelection(); + if (pView) + { + pView->SetSelection(aNewSelection); + + if (comphelper::LibreOfficeKit::isActive()) + { + OString sRect = pView->GetOutputArea().toString(); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRect); + } + } + } + + return bRet; +} + +// Is a DrawTextObject selected? + +bool SwView::IsTextTool() const +{ + sal_uInt16 nId; + SdrInventor nInvent; + SdrView *pSdrView = GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "IsTextTool without DrawView?" ); + + if (pSdrView->IsCreateMode()) + pSdrView->SetCreateMode(false); + + pSdrView->TakeCurrentObj(nId,nInvent); + return nInvent == SdrInventor::Default; +} + +SdrView* SwView::GetDrawView() const +{ + return GetWrtShell().GetDrawView(); +} + +bool SwView::IsBezierEditMode() const +{ + return (!IsDrawSelMode() && GetWrtShell().GetDrawView()->HasMarkablePoints()); +} + +bool SwView::IsFormMode() const +{ + if (GetDrawFuncPtr() && GetDrawFuncPtr()->IsCreateObj()) + { + return GetDrawFuncPtr()->IsInsertForm(); + } + + return AreOnlyFormsSelected(); +} + +void SwView::SetDrawFuncPtr(std::unique_ptr<SwDrawBase> pFuncPtr) +{ + m_pDrawActual = std::move(pFuncPtr); +} + +void SwView::SetSelDrawSlot() +{ + m_nDrawSfxId = SID_OBJECT_SELECT; + m_sDrawCustom.clear(); +} + +bool SwView::AreOnlyFormsSelected() const +{ + if ( GetWrtShell().IsFrameSelected() ) + return false; + + bool bForm = true; + + SdrView* pSdrView = GetWrtShell().GetDrawView(); + + const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + if (nCount) + { + for (size_t i = 0; i < nCount; ++i) + { + // Except controls, are still normal draw objects selected? + SdrObject *pSdrObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + if (!pSdrObj) + continue; + + if (!HasOnlyObj(pSdrObj, SdrInventor::FmForm)) + { + bForm = false; + break; + } + } + } + else + bForm = false; + + return bForm; +} + +bool SwView::HasOnlyObj(SdrObject const *pSdrObj, SdrInventor eObjInventor) const +{ + bool bRet = false; + + if (pSdrObj->IsGroupObject()) + { + SdrObjList* pList = pSdrObj->GetSubList(); + const size_t nCnt = pList->GetObjCount(); + + for (size_t i = 0; i < nCnt; ++i) + { + bRet = HasOnlyObj(pList->GetObj(i), eObjInventor); + if (!bRet) + break; + } + } + else if (eObjInventor == pSdrObj->GetObjInventor()) + return true; + + return bRet; +} + +//#i87414# mod +IMPL_LINK(SwView, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void) +{ + if (rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG) + GetViewFrame()->GetDispatcher()->Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON); + else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS) + GetViewFrame()->GetDispatcher()->Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); +} + +bool SwView::ExecDrwTextSpellPopup(const Point& rPt) +{ + bool bRet = false; + SdrView *pSdrView = m_pWrtShell->GetDrawView(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + Point aPos( GetEditWin().LogicToPixel( rPt ) ); + + if (pOLV->IsWrongSpelledWordAtPos( aPos )) + { + bRet = true; + Link<SpellCallbackInfo&,void> aLink = LINK(this, SwView, OnlineSpellCallback); + pOLV->ExecuteSpellPopup( aPos,&aLink ); + } + return bRet; +} + +bool SwView::IsDrawTextHyphenate() +{ + SdrView *pSdrView = m_pWrtShell->GetDrawView(); + bool bHyphenate = false; + + SfxItemSet aNewAttr( pSdrView->GetModel()->GetItemPool(), + svl::Items<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE>{} ); + pSdrView->GetAttributes( aNewAttr ); + if( aNewAttr.GetItemState( EE_PARA_HYPHENATE ) >= SfxItemState::DEFAULT ) + bHyphenate = aNewAttr.Get( EE_PARA_HYPHENATE ).GetValue(); + + return bHyphenate; +} + +void SwView::HyphenateDrawText() +{ + SdrView *pSdrView = m_pWrtShell->GetDrawView(); + bool bHyphenate = IsDrawTextHyphenate(); + + SfxItemSet aSet( GetPool(), svl::Items<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE>{} ); + aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, !bHyphenate ) ); + pSdrView->SetAttributes( aSet ); + GetViewFrame()->GetBindings().Invalidate(FN_HYPHENATE_OPT_DLG); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewfunc.hxx b/sw/source/uibase/uiview/viewfunc.hxx new file mode 100644 index 000000000..c9d300039 --- /dev/null +++ b/sw/source/uibase/uiview/viewfunc.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_UIBASE_UIVIEW_VIEWFUNC_HXX +#define INCLUDED_SW_SOURCE_UIBASE_UIVIEW_VIEWFUNC_HXX + +#include <vcl/outdev.hxx> + +#include <IDocumentDeviceAccess.hxx> + +class ImageButton; +class Point; +class SfxItemSet; +class SfxPrinter; +class SfxTabPage; +class Size; +class SvxRuler; +class SwScrollbar; +class SwViewShell; +namespace vcl { class Window; } + +// The following functions are available in viewprt.cxx +void SetPrinter( IDocumentDeviceAccess*, SfxPrinter const *, bool bWeb ); +void SetAppPrintOptions( SwViewShell* pSh, bool bWeb ); + +// The following functions are available in viewport.cxx +void ViewResizePixel( const vcl::RenderContext &rRef, + const Point &rOfst, + const Size &rSize, + const Size &rEditSz, + SwScrollbar& rVScrollbar, + SwScrollbar& rHScrollbar, + vcl::Window& rScrollBarBox, + SvxRuler* pVRuler = nullptr, + SvxRuler* pHRuler = nullptr, + bool bVRulerRight = false ); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewling.cxx b/sw/source/uibase/uiview/viewling.cxx new file mode 100644 index 000000000..a84474176 --- /dev/null +++ b/sw/source/uibase/uiview/viewling.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 . + */ + +#include <hintids.hxx> + +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/linguistic2/XThesaurus.hpp> +#include <com/sun/star/linguistic2/ProofreadingResult.hpp> +#include <com/sun/star/linguistic2/XLinguProperties.hpp> +#include <com/sun/star/i18n/TextConversionOption.hpp> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/scopeguard.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/weld.hxx> +#include <svtools/ehdl.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/request.hxx> +#include <svx/dialmgr.hxx> +#include <svx/svxerr.hxx> +#include <svx/svxdlg.hxx> +#include <swwait.hxx> +#include <uitool.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <swundo.hxx> +#include <hyp.hxx> +#include <olmenu.hxx> +#include <pam.hxx> +#include <edtwin.hxx> +#include <ndtxt.hxx> +#include <txtfrm.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <hhcwrp.hxx> + +#include <boost/property_tree/json_parser.hpp> + +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XPopupMenuController.hpp> +#include <com/sun/star/awt/PopupMenuDirection.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <vcl/svapp.hxx> +#include <rtl/ustring.hxx> + +#include <cppuhelper/bootstrap.hxx> +#include <svtools/langtab.hxx> + +#include <editeng/editerr.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <memory> + +using namespace sw::mark; +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::linguistic2; + +// Lingu-Dispatcher + +void SwView::ExecLingu(SfxRequest &rReq) +{ + switch(rReq.GetSlot()) + { + case SID_THESAURUS: + StartThesaurus(); + rReq.Ignore(); + break; + case SID_HANGUL_HANJA_CONVERSION: + StartTextConversion( LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, + i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true ); + break; + case SID_CHINESE_CONVERSION: + { + //open ChineseTranslationDialog + Reference< XComponentContext > xContext( + ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one + if(xContext.is()) + { + Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() ); + if(xMCF.is()) + { + Reference< ui::dialogs::XExecutableDialog > xDialog( + xMCF->createInstanceWithContext( + "com.sun.star.linguistic2.ChineseTranslationDialog", xContext), + UNO_QUERY); + Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY ); + if( xInit.is() ) + { + Reference<awt::XWindow> xParentWindow; + if (weld::Window* pParentWindow = rReq.GetFrameWeld()) + xParentWindow = pParentWindow->GetXWindow(); + // initialize dialog + uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(xParentWindow)} + })); + xInit->initialize( aSeq ); + + //execute dialog + sal_Int16 nDialogRet = xDialog->execute(); + if( RET_OK == nDialogRet ) + { + //get some parameters from the dialog + bool bToSimplified = true; + bool bUseVariants = true; + bool bCommonTerms = true; + Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY ); + if( xProp.is() ) + { + try + { + xProp->getPropertyValue( "IsDirectionToSimplified" ) >>= bToSimplified; + xProp->getPropertyValue( "IsUseCharacterVariants" ) >>= bUseVariants; + xProp->getPropertyValue( "IsTranslateCommonTerms" ) >>= bCommonTerms; + } + catch (const Exception&) + { + } + } + + //execute translation + LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; + LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; + sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0; + if( !bCommonTerms ) + nOptions = nOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER; + + vcl::Font aTargetFont = OutputDevice::GetDefaultFont( DefaultFontType::CJK_TEXT, + nTargetLang, GetDefaultFontFlags::OnlyOne ); + + // disallow formatting, updating the view, ... while + // converting the document. (saves time) + // Also remember the current view and cursor position for later + m_pWrtShell->StartAction(); + + // remember cursor position data for later restoration of the cursor + const SwPosition *pPoint = m_pWrtShell->GetCursor()->GetPoint(); + bool bRestoreCursor = pPoint->nNode.GetNode().IsTextNode(); + const SwNodeIndex aPointNodeIndex( pPoint->nNode ); + sal_Int32 nPointIndex = pPoint->nContent.GetIndex(); + + // since this conversion is not interactive the whole converted + // document should be undone in a single undo step. + m_pWrtShell->StartUndo( SwUndoId::OVERWRITE ); + + StartTextConversion( nSourceLang, nTargetLang, &aTargetFont, nOptions, false ); + + m_pWrtShell->EndUndo( SwUndoId::OVERWRITE ); + + if (bRestoreCursor) + { + SwTextNode *pTextNode = aPointNodeIndex.GetNode().GetTextNode(); + // check for unexpected error case + OSL_ENSURE(pTextNode && pTextNode->GetText().getLength() >= nPointIndex, + "text missing: corrupted node?" ); + if (!pTextNode || pTextNode->GetText().getLength() < nPointIndex) + nPointIndex = 0; + // restore cursor to its original position + m_pWrtShell->GetCursor()->GetPoint()->nContent.Assign( pTextNode, nPointIndex ); + } + + // enable all, restore view and cursor position + m_pWrtShell->EndAction(); + } + } + Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); + } + } + break; + } + case FN_HYPHENATE_OPT_DLG: + HyphenateDocument(); + break; + default: + OSL_ENSURE(false, "wrong Dispatcher"); + return; + } +} + +// start language specific text conversion + +void SwView::StartTextConversion( + LanguageType nSourceLang, + LanguageType nTargetLang, + const vcl::Font *pTargetFont, + sal_Int32 nOptions, + bool bIsInteractive ) +{ + // do not do text conversion if it is active elsewhere + if (SwEditShell::HasConvIter()) + { + return; + } + + SpellContext(); + + const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); + const bool bOldIdle = pVOpt->IsIdle(); + pVOpt->SetIdle( false ); + + bool bOldIns = m_pWrtShell->IsInsMode(); + m_pWrtShell->SetInsMode(); + + const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection() || + m_pWrtShell->GetCursor() != m_pWrtShell->GetCursor()->GetNext(); + + const bool bStart = bSelection || m_pWrtShell->IsStartOfDoc(); + const bool bOther = !bSelection && !(m_pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY); + + { + const uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + SwHHCWrapper aWrap( this, xContext, nSourceLang, nTargetLang, pTargetFont, + nOptions, bIsInteractive, + bStart, bOther, bSelection ); + aWrap.Convert(); + } + + m_pWrtShell->SetInsMode( bOldIns ); + pVOpt->SetIdle( bOldIdle ); + SpellContext(false); +} + +// spellcheck and text conversion related stuff + +void SwView::SpellStart( SvxSpellArea eWhich, + bool bStartDone, bool bEndDone, + SwConversionArgs *pConvArgs ) +{ + Reference< XLinguProperties > xProp = ::GetLinguPropertySet(); + bool bIsWrapReverse = !pConvArgs && xProp.is() && xProp->getIsWrapReverse(); + + SwDocPositions eStart = SwDocPositions::Start; + SwDocPositions eEnd = SwDocPositions::End; + SwDocPositions eCurr = SwDocPositions::Curr; + switch ( eWhich ) + { + case SvxSpellArea::Body: + if( bIsWrapReverse ) + eCurr = SwDocPositions::End; + else + eCurr = SwDocPositions::Start; + break; + case SvxSpellArea::BodyEnd: + if( bIsWrapReverse ) + { + if( bStartDone ) + eStart = SwDocPositions::Curr; + eCurr = SwDocPositions::End; + } + else if( bStartDone ) + eCurr = SwDocPositions::Start; + break; + case SvxSpellArea::BodyStart: + if( !bIsWrapReverse ) + { + if( bEndDone ) + eEnd = SwDocPositions::Curr; + eCurr = SwDocPositions::Start; + } + else if( bEndDone ) + eCurr = SwDocPositions::End; + break; + case SvxSpellArea::Other: + if( bIsWrapReverse ) + { + eStart = SwDocPositions::OtherStart; + eEnd = SwDocPositions::OtherEnd; + eCurr = SwDocPositions::OtherEnd; + } + else + { + eStart = SwDocPositions::OtherStart; + eEnd = SwDocPositions::OtherEnd; + eCurr = SwDocPositions::OtherStart; + } + break; + default: + OSL_ENSURE( false, "SpellStart with unknown Area" ); + } + m_pWrtShell->SpellStart( eStart, eEnd, eCurr, pConvArgs ); +} + +// Error message while Spelling + +// The passed pointer nlang is itself the value +void SwView::SpellError(LanguageType eLang) +{ + int nPend = 0; + + if ( m_pWrtShell->ActionPend() ) + { + m_pWrtShell->Push(); + m_pWrtShell->ClearMark(); + do + { + m_pWrtShell->EndAction(); + ++nPend; + } + while( m_pWrtShell->ActionPend() ); + } + OUString aErr(SvtLanguageTable::GetLanguageString( eLang ) ); + + SwEditWin &rEditWin = GetEditWin(); + int nWaitCnt = 0; + while( rEditWin.IsWait() ) + { + rEditWin.LeaveWait(); + ++nWaitCnt; + } + if ( LANGUAGE_NONE == eLang ) + ErrorHandler::HandleError( ERRCODE_SVX_LINGU_NOLANGUAGE ); + else + ErrorHandler::HandleError( *new StringErrorInfo( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) ); + + while( nWaitCnt ) + { + rEditWin.EnterWait(); + --nWaitCnt; + } + + if ( nPend ) + { + while( nPend-- ) + m_pWrtShell->StartAction(); + m_pWrtShell->Combine(); + } +} + +// Finish spelling and restore cursor + +void SwView::SpellEnd( SwConversionArgs const *pConvArgs ) +{ + m_pWrtShell->SpellEnd( pConvArgs ); + if( m_pWrtShell->IsExtMode() ) + m_pWrtShell->SetMark(); +} + +void SwView::HyphStart( SvxSpellArea eWhich ) +{ + switch ( eWhich ) + { + case SvxSpellArea::Body: + m_pWrtShell->HyphStart( SwDocPositions::Start, SwDocPositions::End ); + break; + case SvxSpellArea::BodyEnd: + m_pWrtShell->HyphStart( SwDocPositions::Curr, SwDocPositions::End ); + break; + case SvxSpellArea::BodyStart: + m_pWrtShell->HyphStart( SwDocPositions::Start, SwDocPositions::Curr ); + break; + case SvxSpellArea::Other: + m_pWrtShell->HyphStart( SwDocPositions::OtherStart, SwDocPositions::OtherEnd ); + break; + default: + OSL_ENSURE( false, "HyphStart with unknown Area" ); + } +} + +// Interactive separation + +void SwView::HyphenateDocument() +{ + // do not hyphenate if interactive hyphenation is active elsewhere + if (SwEditShell::HasHyphIter()) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SwResId(STR_MULT_INTERACT_HYPH_WARN))); + xBox->set_title(SwResId(STR_HYPH_TITLE)); + xBox->run(); + return; + } + + SfxErrorContext aContext( ERRCTX_SVX_LINGU_HYPHENATION, OUString(), m_pEditWin->GetFrameWeld(), + RID_SVXERRCTX, SvxResLocale() ); + + Reference< XHyphenator > xHyph( ::GetHyphenator() ); + if (!xHyph.is()) + { + ErrorHandler::HandleError( ERRCODE_SVX_LINGU_LINGUNOTEXISTS ); + return; + } + + if (m_pWrtShell->GetSelectionType() & (SelectionType::DrawObjectEditMode|SelectionType::DrawObject)) + { + // Hyphenation in a Draw object + HyphenateDrawText(); + } + else + { + SwViewOption* pVOpt = const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions()); + bool bOldIdle = pVOpt->IsIdle(); + pVOpt->SetIdle( false ); + + Reference< XLinguProperties > xProp( ::GetLinguPropertySet() ); + + m_pWrtShell->StartUndo(SwUndoId::INSATTR); // valid later + + bool bHyphSpecial = xProp.is() && xProp->getIsHyphSpecial(); + bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection() || + m_pWrtShell->GetCursor() != m_pWrtShell->GetCursor()->GetNext(); + bool bOther = m_pWrtShell->HasOtherCnt() && bHyphSpecial && !bSelection; + bool bStart = bSelection || ( !bOther && m_pWrtShell->IsStartOfDoc() ); + bool bStop = false; + if( !bOther && !(m_pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY) && !bSelection ) + // turned on no special area + { + // I want also in special areas hyphenation + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SwResId(STR_QUERY_SPECIAL_FORCED))); + if (xBox->run() == RET_YES) + { + bOther = true; + if (xProp.is()) + { + xProp->setIsHyphSpecial( true ); + } + } + else + bStop = true; // No hyphenation + } + + if( !bStop ) + { + SwHyphWrapper aWrap( this, xHyph, bStart, bOther, bSelection ); + aWrap.SpellDocument(); + m_pWrtShell->EndUndo(SwUndoId::INSATTR); + } + pVOpt->SetIdle( bOldIdle ); + } +} + +bool SwView::IsValidSelectionForThesaurus() const +{ + // must not be a multi-selection, and if it is a selection it needs + // to be within a single paragraph + + const bool bMultiSel = m_pWrtShell->GetCursor()->IsMultiSelection(); + const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection(); + return !bMultiSel && (!bSelection || m_pWrtShell->IsSelOnePara() ); +} + +OUString SwView::GetThesaurusLookUpText( bool bSelection ) const +{ + return bSelection ? m_pWrtShell->GetSelText() : m_pWrtShell->GetCurWord(); +} + +void SwView::InsertThesaurusSynonym( const OUString &rSynonmText, const OUString &rLookUpText, bool bSelection ) +{ + bool bOldIns = m_pWrtShell->IsInsMode(); + m_pWrtShell->SetInsMode(); + + m_pWrtShell->StartAllAction(); + m_pWrtShell->StartUndo(SwUndoId::DELETE); + + if( !bSelection ) + { + if(m_pWrtShell->IsEndWrd()) + m_pWrtShell->Left(CRSR_SKIP_CELLS, false, 1, false ); + + m_pWrtShell->SelWrd(); + + // 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 = rLookUpText.getStr(); + sal_Int32 nLeft = 0; + while (*pChar++ == CH_TXTATR_INWORD) + ++nLeft; + pChar = rLookUpText.getLength() ? rLookUpText.getStr() + rLookUpText.getLength() - 1 : nullptr; + sal_Int32 nRight = 0; + while (pChar && *pChar-- == CH_TXTATR_INWORD) + ++nRight; + + // adjust existing selection + SwPaM *pCursor = m_pWrtShell->GetCursor(); + pCursor->GetPoint()->nContent -= nRight; + pCursor->GetMark()->nContent += nLeft; + } + + m_pWrtShell->Insert( rSynonmText ); + + m_pWrtShell->EndUndo(SwUndoId::DELETE); + m_pWrtShell->EndAllAction(); + + m_pWrtShell->SetInsMode( bOldIns ); +} + +// Start thesaurus + +void SwView::StartThesaurus() +{ + if (!IsValidSelectionForThesaurus()) + return; + + SfxErrorContext aContext( ERRCTX_SVX_LINGU_THESAURUS, OUString(), m_pEditWin->GetFrameWeld(), + RID_SVXERRCTX, SvxResLocale() ); + + // Determine language + LanguageType eLang = m_pWrtShell->GetCurLang(); + if( LANGUAGE_SYSTEM == eLang ) + eLang = GetAppLanguage(); + + if( eLang == LANGUAGE_DONTKNOW || eLang == LANGUAGE_NONE ) + { + SpellError( LANGUAGE_NONE ); + return; + } + + SwViewOption* pVOpt = const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions()); + const bool bOldIdle = pVOpt->IsIdle(); + pVOpt->SetIdle( false ); + comphelper::ScopeGuard guard([&]() { pVOpt->SetIdle(bOldIdle); }); // restore when leaving scope + + // get initial LookUp text + const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection(); + OUString aTmp = GetThesaurusLookUpText( bSelection ); + + Reference< XThesaurus > xThes( ::GetThesaurus() ); + + if ( !xThes.is() || !xThes->hasLocale( LanguageTag::convertToLocale( eLang ) ) ) + SpellError( eLang ); + else + { + VclPtr<AbstractThesaurusDialog> pDlg; + // create dialog + { //Scope for SwWait-Object + SwWait aWait( *GetDocShell(), true ); + // load library with dialog only on demand ... + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateThesaurusDialog(GetEditWin().GetFrameWeld(), xThes, aTmp, eLang)); + } + + if (pDlg) + { + guard.dismiss(); // ignore, we'll call SetIdle() explicitly after the dialog ends + + pDlg->StartExecuteAsync([aTmp, bSelection, bOldIdle, pDlg, pVOpt, this](sal_Int32 nResult){ + if (nResult == RET_OK ) + InsertThesaurusSynonym(pDlg->GetWord(), aTmp, bSelection); + + pVOpt->SetIdle(bOldIdle); + pDlg->disposeOnce(); + }); + } + } +} + +// Offer online suggestions + +namespace { + +//!! Start of extra code for context menu modifying extensions +struct ExecuteInfo +{ + uno::Reference< frame::XDispatch > xDispatch; + util::URL aTargetURL; + uno::Sequence< PropertyValue > aArgs; +}; + +class AsyncExecute +{ +public: + DECL_STATIC_LINK( AsyncExecute, ExecuteHdl_Impl, void*, void ); +}; + +} + +IMPL_STATIC_LINK( AsyncExecute, ExecuteHdl_Impl, void*, p, void ) +{ + ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); + SolarMutexReleaser aReleaser; + try + { + // Asynchronous execution as this can lead to our own destruction! + // Framework can recycle our current frame and the layout manager disposes all user interface + // elements if a component gets detached from its frame! + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + catch (const Exception&) + { + } + + delete pExecuteInfo; +} +//!! End of extra code for context menu modifying extensions + +bool SwView::ExecSpellPopup(const Point& rPt) +{ + bool bRet = false; + const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); + if( pVOpt->IsOnlineSpell() && + !m_pWrtShell->IsSelection()) + { + if (m_pWrtShell->GetSelectionType() & SelectionType::DrawObjectEditMode) + bRet = ExecDrwTextSpellPopup(rPt); + else if (!m_pWrtShell->IsSelFrameMode()) + { + const bool bOldViewLock = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->Push(); + SwRect aToFill; + + SwCursorShell *pCursorShell = m_pWrtShell.get(); + SwPaM *pCursor = pCursorShell->GetCursor(); + SwPosition aPoint(*pCursor->GetPoint()); + const SwTextNode *pNode = aPoint.nNode.GetNode().GetTextNode(); + + // Spell-check in case the idle jobs haven't had a chance to kick in. + // This makes it possible to suggest spelling corrections for + // wrong words independent of the spell-checking idle job. + if (pNode && pNode->IsWrongDirty() && + !pCursorShell->IsTableMode() && + !pCursor->HasMark() && !pCursor->IsMultiSelection()) + { + std::pair<Point, bool> const tmp(rPt, false); + SwContentFrame *const pContentFrame = pCursor->GetContentNode()->getLayoutFrame( + pCursorShell->GetLayout(), + &aPoint, &tmp); + if (pContentFrame) + { + SwRect aRepaint(static_cast<SwTextFrame*>(pContentFrame)->AutoSpell_( + *pCursor->GetContentNode()->GetTextNode(), 0)); + if (aRepaint.HasArea()) + m_pWrtShell->InvalidateWindows(aRepaint); + } + } + + // decide which variant of the context menu to use... + // if neither spell checking nor grammar checking provides suggestions use the + // default context menu. + bool bUseGrammarContext = false; + Reference< XSpellAlternatives > xAlt( m_pWrtShell->GetCorrection(&rPt, aToFill) ); + ProofreadingResult aGrammarCheckRes; + sal_Int32 nErrorInResult = -1; + uno::Sequence< OUString > aSuggestions; + bool bCorrectionRes = false; + if (!xAlt.is() || !xAlt->getAlternatives().hasElements()) + { + sal_Int32 nErrorPosInText = -1; + bCorrectionRes = m_pWrtShell->GetGrammarCorrection( aGrammarCheckRes, nErrorPosInText, nErrorInResult, aSuggestions, &rPt, aToFill ); + OUString aMessageText; + if (nErrorInResult >= 0) + aMessageText = aGrammarCheckRes.aErrors[ nErrorInResult ].aShortComment; + // we like to use the grammar checking context menu if we either get + // some suggestions or at least a comment about the error found... + bUseGrammarContext = bCorrectionRes && + (aSuggestions.hasElements() || !aMessageText.isEmpty()); + } + + // open respective context menu for spell check or grammar errors with correction suggestions... + if ((!bUseGrammarContext && xAlt.is()) || + (bUseGrammarContext && bCorrectionRes && aGrammarCheckRes.aErrors.hasElements())) + { + // get paragraph text + OUString aParaText; + if (pNode) + aParaText = pNode->GetText(); // this may include hidden text but that should be Ok + else + { + OSL_FAIL("text node expected but not found" ); + } + + bRet = true; + m_pWrtShell->SttSelect(); + std::unique_ptr<SwSpellPopup> xPopup(bUseGrammarContext ? + new SwSpellPopup(m_pWrtShell.get(), aGrammarCheckRes, nErrorInResult, aSuggestions, aParaText) : + new SwSpellPopup(m_pWrtShell.get(), xAlt, aParaText)); + ui::ContextMenuExecuteEvent aEvent; + const Point aPixPos = GetEditWin().LogicToPixel( rPt ); + + aEvent.SourceWindow = VCLUnoHelper::GetInterface( m_pEditWin ); + aEvent.ExecutePosition.X = aPixPos.X(); + aEvent.ExecutePosition.Y = aPixPos.Y(); + ScopedVclPtr<Menu> pMenu; + + OUString sMenuName = bUseGrammarContext ? + OUString("private:resource/GrammarContextMenu") : OUString("private:resource/SpellContextMenu"); + if (TryContextMenuInterception(xPopup->GetMenu(), sMenuName, pMenu, aEvent)) + { + //! happy hacking for context menu modifying extensions of this + //! 'custom made' menu... *sigh* (code copied from sfx2 and framework) + if ( pMenu ) + { + const sal_uInt16 nId = static_cast<PopupMenu*>(pMenu.get())->Execute(m_pEditWin, aPixPos); + OUString aCommand = static_cast<PopupMenu*>(pMenu.get())->GetItemCommand(nId); + if (aCommand.isEmpty() ) + { + if (!ExecuteMenuCommand(dynamic_cast<PopupMenu&>(*pMenu), *GetViewFrame(), nId)) + xPopup->Execute(nId); + } + else + { + SfxViewFrame *pSfxViewFrame = GetViewFrame(); + uno::Reference< frame::XFrame > xFrame; + if ( pSfxViewFrame ) + xFrame = pSfxViewFrame->GetFrame().GetFrameInterface(); + css::util::URL aURL; + uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY ); + + try + { + uno::Reference< frame::XDispatch > xDispatch; + uno::Reference< util::XURLTransformer > xURLTransformer = util::URLTransformer::create(comphelper::getProcessComponentContext()); + + aURL.Complete = aCommand; + xURLTransformer->parseStrict(aURL); + uno::Sequence< beans::PropertyValue > aArgs; + xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 ); + + if (xDispatch.is()) + { + // Execute dispatch asynchronously + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aURL; + pExecuteInfo->aArgs = aArgs; + Application::PostUserEvent( LINK(nullptr, AsyncExecute , ExecuteHdl_Impl), pExecuteInfo ); + } + } + catch (const Exception&) + { + } + } + } + else + { + if (comphelper::LibreOfficeKit::isActive()) + { + if (SfxViewShell* pViewShell = SfxViewShell::Current()) + { + boost::property_tree::ptree aMenu = SfxDispatcher::fillPopupMenu(&xPopup->GetMenu()); + boost::property_tree::ptree aRoot; + aRoot.add_child("menu", aMenu); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aRoot, true); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, aStream.str().c_str()); + } + } + else + { + xPopup->Execute(aToFill.SVRect(), m_pEditWin); + } + } + } + } + + if (!comphelper::LibreOfficeKit::isActive()) + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + m_pWrtShell->LockView( bOldViewLock ); + } + } + return bRet; +} + +/** Function: ExecSmartTagPopup + + This function shows the popup menu for smarttag + actions. +*/ +void SwView::ExecSmartTagPopup( const Point& rPt ) +{ + const bool bOldViewLock = m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->Push(); + + css::uno::Sequence< css::uno::Any > aArgs( 2 ); + aArgs[0] <<= comphelper::makePropertyValue( "Frame", GetDispatcher().GetFrame()->GetFrame().GetFrameInterface() ); + aArgs[1] <<= comphelper::makePropertyValue( "CommandURL", OUString( ".uno:OpenSmartTagMenuOnCursor" ) ); + + css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + css::uno::Reference< css::frame::XPopupMenuController > xPopupController( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.svx.SmartTagMenuController", aArgs, xContext ), css::uno::UNO_QUERY ); + + css::uno::Reference< css::awt::XPopupMenu > xPopupMenu( xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.awt.PopupMenu", xContext ), css::uno::UNO_QUERY ); + + if ( xPopupController.is() && xPopupMenu.is() ) + { + xPopupController->setPopupMenu( xPopupMenu ); + + SwRect aToFill; + m_pWrtShell->GetSmartTagRect( rPt, aToFill ); + m_pWrtShell->SttSelect(); + + if ( aToFill.HasArea() ) + xPopupMenu->execute( m_pEditWin->GetComponentInterface(), + VCLUnoHelper::ConvertToAWTRect( m_pEditWin->LogicToPixel( aToFill.SVRect() ) ), css::awt::PopupMenuDirection::EXECUTE_DOWN ); + + css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + m_pWrtShell->LockView( bOldViewLock ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewmdi.cxx b/sw/source/uibase/uiview/viewmdi.cxx new file mode 100644 index 000000000..12606618f --- /dev/null +++ b/sw/source/uibase/uiview/viewmdi.cxx @@ -0,0 +1,700 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/ruler.hxx> +#include <editeng/lrspitem.hxx> +#include <svl/srchitem.hxx> +#include <svl/stritem.hxx> +#include <sfx2/request.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <frmatr.hxx> +#include <edtwin.hxx> +#include <pagedesc.hxx> +#include <IMark.hxx> +#include <fldbas.hxx> +#include <workctrl.hxx> +#include <usrpref.hxx> +#include <scroll.hxx> +#include <wview.hxx> + +#include <cmdid.h> + +#include <PostItMgr.hxx> +#include <AnnotationWin.hxx> + +#include <svx/srchdlg.hxx> +#include <svx/svdview.hxx> + +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +sal_uInt16 SwView::m_nMoveType = NID_PGE; +sal_Int32 SwView::m_nActMark = 0; + +using namespace ::com::sun::star::uno; + +namespace { + +void collectUIInformation(const OUString& aFactor) +{ + EventDescription aDescription; + aDescription.aID = "writer_edit"; + aDescription.aParameters = {{"ZOOM", aFactor}}; + aDescription.aAction = "SET"; + aDescription.aKeyWord = "SwEditWinUIObject"; + aDescription.aParent = "MainWindow"; + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +void SwView::SetZoom( SvxZoomType eZoomType, short nFactor, bool bViewOnly ) +{ + bool const bCursorIsVisible(m_pWrtShell->IsCursorVisible()); + SetZoom_( GetEditWin().GetOutputSizePixel(), eZoomType, nFactor, bViewOnly ); + // fdo#40465 force the cursor to stay in view whilst zooming + if (bCursorIsVisible) + m_pWrtShell->ShowCursor(); + + collectUIInformation(OUString::number(nFactor)); +} + +void SwView::SetZoom_( const Size &rEditSize, SvxZoomType eZoomType, + short nFactor, bool bViewOnly ) +{ + bool bUnLockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->LockPaint(); + + { // start of SwActContext scope + SwActContext aActContext(m_pWrtShell.get()); + + long nFac = nFactor; + + const bool bWeb = dynamic_cast< const SwWebView *>( this ) != nullptr; + SwMasterUsrPref *pUsrPref = const_cast<SwMasterUsrPref*>(SW_MOD()->GetUsrPref(bWeb)); + + const SwPageDesc &rDesc = m_pWrtShell->GetPageDesc( m_pWrtShell->GetCurPageDesc() ); + const SvxLRSpaceItem &rLRSpace = rDesc.GetMaster().GetLRSpace(); + const SwViewOption *pOpt = m_pWrtShell->GetViewOptions(); + long lLeftMargin = 0; + + if( eZoomType != SvxZoomType::PERCENT ) + { + const bool bAutomaticViewLayout = 0 == pOpt->GetViewLayoutColumns(); + + const SwRect aPageRect( m_pWrtShell->GetAnyCurRect( CurRectType::PageCalc ) ); + const SwRect aRootRect( m_pWrtShell->GetAnyCurRect( CurRectType::PagesArea ) ); + Size aPageSize( aPageRect.SSize() ); + Size aRootSize( aRootRect.SSize() ); + + //mod #i6193# added sidebar width + SwPostItMgr* pPostItMgr = GetPostItMgr(); + if (pPostItMgr->HasNotes() && pPostItMgr->ShowNotes()) + aPageSize.AdjustWidth(pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() ); + + const MapMode aTmpMap( MapUnit::MapTwip ); + const Size aWindowSize( GetEditWin().PixelToLogic( rEditSize, aTmpMap ) ); + + if( UseOnPage::Mirror == rDesc.GetUseOn() ) // mirrored pages + { + const SvxLRSpaceItem &rLeftLRSpace = rDesc.GetLeft().GetLRSpace(); + aPageSize.AdjustWidth(std::abs( rLeftLRSpace.GetLeft() - rLRSpace.GetLeft() ) ); + } + + if( SvxZoomType::OPTIMAL == eZoomType ) + { + if (!pPostItMgr->HasNotes() || !pPostItMgr->ShowNotes()) + aPageSize.AdjustWidth( -( rLRSpace.GetLeft() + rLRSpace.GetRight() + nLeftOfst * 2 ) ); + lLeftMargin = rLRSpace.GetLeft() + DOCUMENTBORDER + nLeftOfst; + nFac = aWindowSize.Width() * 100 / aPageSize.Width(); + } + else if(SvxZoomType::WHOLEPAGE == eZoomType || SvxZoomType::PAGEWIDTH == eZoomType ) + { + const long nOf = DOCUMENTBORDER * 2; + long nTmpWidth = bAutomaticViewLayout ? aPageSize.Width() : aRootSize.Width(); + nTmpWidth += nOf; + aPageSize.AdjustHeight(nOf ); + nFac = aWindowSize.Width() * 100 / nTmpWidth; + + if ( SvxZoomType::WHOLEPAGE == eZoomType ) + { + long nVisPercent = aWindowSize.Height() * 100 / aPageSize.Height(); + nFac = std::min( nFac, nVisPercent ); + } + } + else + { + const long nTmpWidth = bAutomaticViewLayout ? aPageSize.Width() : aRootSize.Width(); + nFac = aWindowSize.Width() * 100 / nTmpWidth; + } + } + + nFac = std::max( long( MINZOOM ), nFac ); + const sal_uInt16 nZoomFac = static_cast<sal_uInt16>(nFac); + + SwViewOption aOpt( *pOpt ); + if ( !GetViewFrame()->GetFrame().IsInPlace() ) + { + //Update MasterUsrPrefs and after that update the ViewOptions of the current View. + if ( !bViewOnly && + (nZoomFac != pUsrPref->GetZoom() || + eZoomType != pUsrPref->GetZoomType()) ) + { + pUsrPref->SetZoom(nZoomFac); + pUsrPref->SetZoomType(eZoomType); + SW_MOD()->ApplyUsrPref(*pUsrPref, nullptr); + pUsrPref->SetModified(); + } + if ( pOpt->GetZoom() != nZoomFac ) + { + aOpt.SetZoom(nZoomFac); + aOpt.SetReadonly(pOpt->IsReadonly()); + m_pWrtShell->ApplyViewOptions( aOpt ); + } + if ( eZoomType != SvxZoomType::PERCENT ) + { + Point aPos; + + if ( eZoomType == SvxZoomType::WHOLEPAGE ) + aPos.setY( m_pWrtShell->GetAnyCurRect(CurRectType::Page).Top() - DOCUMENTBORDER ); + else + { + // Make sure that the cursor is in the visible range, so that + // the scrolling will be performed only once. + aPos.setX( lLeftMargin ); + const SwRect &rCharRect = m_pWrtShell->GetCharRect(); + if ( rCharRect.Top() > GetVisArea().Bottom() || + rCharRect.Bottom() < aPos.Y() ) + aPos.setY( rCharRect.Top() - rCharRect.Height() ); + else + aPos.setY( GetVisArea().Top() ); + } + SetVisArea( aPos ); + } + // Compromise solution - Under certain circumstances SetZoom is called + // in CalcVisAreas again and thus be set wrong values. + const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions())->SetZoomType( eZoomType ); + CalcVisArea( rEditSize ); // for the recalculation of the viewable area + } + else if ( nZoomFac != pOpt->GetZoom() ) + { + aOpt.SetZoom( nZoomFac ); + m_pWrtShell->ApplyViewOptions( aOpt ); + } + + const Fraction aFrac( nFac, 100 ); + m_pVRuler->SetZoom( aFrac ); + m_pVRuler->ForceUpdate(); + m_pHRuler->SetZoom( aFrac ); + m_pHRuler->ForceUpdate(); + const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions())->SetZoomType( eZoomType ); + } // end of SwActContext scope + + m_pWrtShell->UnlockPaint(); + if( bUnLockView ) + m_pWrtShell->LockView( false ); +} + +void SwView::SetViewLayout( sal_uInt16 nColumns, bool bBookMode, bool bViewOnly ) +{ + const bool bUnLockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->LockPaint(); + + { + + SwActContext aActContext(m_pWrtShell.get()); + + if ( !GetViewFrame()->GetFrame().IsInPlace() && !bViewOnly ) + { + const bool bWeb = dynamic_cast< const SwWebView *>( this ) != nullptr; + SwMasterUsrPref *pUsrPref = const_cast<SwMasterUsrPref*>(SW_MOD()->GetUsrPref(bWeb)); + + // Update MasterUsrPrefs and after that update the ViewOptions of the current View. + if ( nColumns != pUsrPref->GetViewLayoutColumns() || + bBookMode != pUsrPref->IsViewLayoutBookMode() ) + { + pUsrPref->SetViewLayoutColumns(nColumns); + pUsrPref->SetViewLayoutBookMode(bBookMode); + SW_MOD()->ApplyUsrPref(*pUsrPref, nullptr); + pUsrPref->SetModified(); + } + } + + const SwViewOption *pOpt = m_pWrtShell->GetViewOptions(); + + if ( nColumns != pOpt->GetViewLayoutColumns() || + bBookMode != pOpt->IsViewLayoutBookMode() ) + { + SwViewOption aOpt( *pOpt ); + aOpt.SetViewLayoutColumns( nColumns ); + aOpt.SetViewLayoutBookMode( bBookMode ); + m_pWrtShell->ApplyViewOptions( aOpt ); + } + + m_pVRuler->ForceUpdate(); + m_pHRuler->ForceUpdate(); + + } + + m_pWrtShell->UnlockPaint(); + if( bUnLockView ) + m_pWrtShell->LockView( false ); + + SfxBindings& rBnd = GetViewFrame()->GetBindings(); + rBnd.Invalidate( SID_ATTR_VIEWLAYOUT ); + rBnd.Invalidate( SID_ATTR_ZOOMSLIDER); +} + +// Scrollbar - Handler + +IMPL_LINK( SwView, WindowChildEventListener, VclWindowEvent&, rEvent, void ) +{ + OSL_ENSURE( rEvent.GetWindow(), "Window???" ); + vcl::Window* pChildWin = static_cast< vcl::Window* >( rEvent.GetData() ); + + switch ( rEvent.GetId() ) + { + case VclEventId::WindowHide: + if( pChildWin == m_pHScrollbar ) + ShowHScrollbar( false ); + else if( pChildWin == m_pVScrollbar ) + ShowVScrollbar( false ); + break; + case VclEventId::WindowShow: + if( pChildWin == m_pHScrollbar ) + ShowHScrollbar( true ); + else if( pChildWin == m_pVScrollbar ) + ShowVScrollbar( true ); + break; + default: break; + } +} + +void SwView::CreateScrollbar( bool bHori ) +{ + vcl::Window *pMDI = &GetViewFrame()->GetWindow(); + VclPtr<SwScrollbar>& ppScrollbar = bHori ? m_pHScrollbar : m_pVScrollbar; + + assert(!ppScrollbar); //check beforehand! + + ppScrollbar = VclPtr<SwScrollbar>::Create( pMDI, bHori ); + UpdateScrollbars(); + if(bHori) + ppScrollbar->SetScrollHdl( LINK( this, SwView, EndScrollHdl )); + else + ppScrollbar->SetScrollHdl( LINK( this, SwView, ScrollHdl )); + ppScrollbar->SetEndScrollHdl( LINK( this, SwView, EndScrollHdl )); + + ppScrollbar->EnableDrag(); + + if(GetWindow()) + InvalidateBorder(); + + if (!m_bShowAtResize) + ppScrollbar->ExtendedShow(); +} + +IMPL_LINK( SwView, MoveNavigationHdl, void*, p, void ) +{ + bool* pbNext = static_cast<bool*>(p); + if ( !pbNext ) + return; + const bool bNext = *pbNext; + SwWrtShell& rSh = GetWrtShell(); + if ( NID_SRCH_REP != m_nMoveType) + { + if ( rSh.GetDrawView()->IsTextEdit() ) + rSh.EndTextEdit(); + if ( IsDrawMode() ) + LeaveDrawCreate(); + } + if ( NID_POSTIT != m_nMoveType && m_pPostItMgr ) + { + sw::annotation::SwAnnotationWin* pActiveSidebarWin = m_pPostItMgr->GetActiveSidebarWin(); + if (pActiveSidebarWin) + pActiveSidebarWin->SwitchToFieldPos(); + } + switch( m_nMoveType ) + { + case NID_PGE: + if ( bNext ) + { + if ( USHRT_MAX == rSh.GetNextPrevPageNum( true ) ) + { + rSh.GotoPage( 1, true ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped ); + } + else + { + PhyPageDown(); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + } + } + else + { + if ( USHRT_MAX == rSh.GetNextPrevPageNum( false ) ) + { + rSh.GotoPage( rSh.GetPageCnt(), true ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped ); + } + else + { + PhyPageUp(); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + } + } + break; + case NID_TBL : + rSh.EnterStdMode(); + if(bNext) + rSh.MoveTable(GotoNextTable, fnTableStart); + else + rSh.MoveTable(GotoPrevTable, fnTableStart); + break; + case NID_FRM : + case NID_GRF: + case NID_OLE: + { + GotoObjFlags eType = GotoObjFlags::FlyFrame; + if(m_nMoveType == NID_GRF) + eType = GotoObjFlags::FlyGrf; + else if(m_nMoveType == NID_OLE) + eType = GotoObjFlags::FlyOLE; + bool bSuccess = bNext ? + rSh.GotoNextFly(eType) : + rSh.GotoPrevFly(eType); + if(bSuccess) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + } + break; + case NID_CTRL: + if (!rSh.GetView().IsDesignMode()) + rSh.GetView().GetFormShell()->SetDesignMode(true); + [[fallthrough]]; + case NID_DRW: + { + bool bSuccess = rSh.GotoObj(bNext, + m_nMoveType == NID_DRW ? + GotoObjFlags::DrawSimple : + GotoObjFlags::DrawControl); + if(bSuccess) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + } + break; + case NID_REG : + rSh.EnterStdMode(); + if(bNext) + rSh.MoveRegion(GotoNextRegion, fnRegionStart); + else + rSh.MoveRegion(GotoPrevRegion, fnRegionStart); + + break; + case NID_BKM : + rSh.EnterStdMode(); + GetViewFrame()->GetDispatcher()->Execute(bNext ? + FN_NEXT_BOOKMARK : + FN_PREV_BOOKMARK); + break; + case NID_OUTL: + rSh.EnterStdMode(); + bNext ? rSh.GotoNextOutline() : rSh.GotoPrevOutline(); + break; + case NID_SEL : + rSh.GoNextPrevCursorSetSearchLabel(bNext); + break; + case NID_FTN: + { + bool bFrameTypeFootnote(rSh.GetFrameType(nullptr, false) & FrameTypeFlags::FOOTNOTE); + + if (bFrameTypeFootnote) + { + rSh.LockView(true); + rSh.GotoFootnoteAnchor(); + } + + rSh.EnterStdMode(); + bNext ? + rSh.GotoNextFootnoteAnchor() : + rSh.GotoPrevFootnoteAnchor(); + + if (bFrameTypeFootnote) + { + rSh.LockView(false); + rSh.GotoFootnoteText(); + } + } + break; + case NID_MARK: + { + // unselect + rSh.MoveCursor(); + rSh.EnterStdMode(); + + // collect and sort navigator reminder names + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + std::vector< OUString > vNavMarkNames; + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); + ppMark != pMarkAccess->getAllMarksEnd(); + ++ppMark) + { + if( IDocumentMarkAccess::GetType(**ppMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER ) + vNavMarkNames.push_back((*ppMark)->GetName()); + } + std::sort(vNavMarkNames.begin(), vNavMarkNames.end()); + + // move + if(!vNavMarkNames.empty()) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + + if(bNext) + { + m_nActMark++; + if (m_nActMark >= MAX_MARKS || m_nActMark >= static_cast<sal_Int32>(vNavMarkNames.size())) + { + m_nActMark = 0; + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::ReminderEndWrapped ); + } + } + else + { + m_nActMark--; + if (m_nActMark < 0 || m_nActMark >= static_cast<sal_Int32>(vNavMarkNames.size())) + { + m_nActMark = vNavMarkNames.size()-1; + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::ReminderStartWrapped ); + } + } + rSh.GotoMark(vNavMarkNames[m_nActMark]); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + } + break; + + case NID_POSTIT: + { + if ( m_pPostItMgr->HasNotes() ) + { + rSh.EnterStdMode(); + sw::annotation::SwAnnotationWin* pPostIt = GetPostItMgr()->GetActiveSidebarWin(); + if (pPostIt) + GetPostItMgr()->SetActiveSidebarWin(nullptr); + SwFieldType* pFieldType = rSh.GetFieldType(0, SwFieldIds::Postit); + if ( !rSh.MoveFieldType( pFieldType, bNext ) ) + { + bNext ? (*(m_pPostItMgr->begin()))->pPostIt->GotoPos() : + (*(m_pPostItMgr->end()-1))->pPostIt->GotoPos(); + SvxSearchDialogWrapper::SetSearchLabel( bNext ? SearchLabel::EndWrapped : SearchLabel::StartWrapped ); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + GetViewFrame()->GetDispatcher()->Execute(FN_POSTIT); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + } + break; + + case NID_SRCH_REP: + if(s_pSrchItem) + { + bool bBackward = s_pSrchItem->GetBackward(); + if (rSh.HasSelection() && bNext != rSh.IsCursorPtAtEnd()) + rSh.SwapPam(); + s_pSrchItem->SetBackward(!bNext); + SfxRequest aReq(FN_REPEAT_SEARCH, SfxCallMode::SLOT, GetPool()); + ExecSearch(aReq); + s_pSrchItem->SetBackward(bBackward); + } + break; + case NID_INDEX_ENTRY: + rSh.GotoNxtPrvTOXMark(bNext); + break; + + case NID_TABLE_FORMULA: + rSh.GotoNxtPrvTableFormula( bNext ); + break; + + case NID_TABLE_FORMULA_ERROR: + rSh.GotoNxtPrvTableFormula( bNext, true ); + break; + } + m_pEditWin->GrabFocus(); + delete pbNext; +} + +void SwView::CreateTab() +{ + m_pHRuler->SetActive(GetFrame() && IsActive()); + + m_pHRuler->Show(); + InvalidateBorder(); +} + +void SwView::KillTab() +{ + m_pHRuler->Hide(); + InvalidateBorder(); +} + +void SwView::ChangeTabMetric( FieldUnit eUnit ) +{ + if(m_pHRuler->GetUnit() != eUnit ) + { + m_pHRuler->SetUnit( eUnit ); + m_pHRuler->Invalidate(); + } +} + +void SwView::ChangeVRulerMetric( FieldUnit eUnit ) +{ + if(m_pVRuler->GetUnit() != eUnit) + { + m_pVRuler->SetUnit( eUnit ); + m_pVRuler->Invalidate(); + } +} + +void SwView::GetVRulerMetric(FieldUnit& eToFill) const +{ + eToFill = m_pVRuler->GetUnit(); +} + +void SwView::GetHRulerMetric(FieldUnit& eToFill) const +{ + eToFill = m_pHRuler->GetUnit(); +} + +void SwView::CreateVRuler() +{ + m_pHRuler->SetBorderPos( m_pVRuler->GetSizePixel().Width()-1 ); + + m_pVRuler->SetActive(GetFrame() && IsActive()); + m_pVRuler->Show(); + InvalidateBorder(); +} + +void SwView::KillVRuler() +{ + m_pVRuler->Hide(); + m_pHRuler->SetBorderPos(); + InvalidateBorder(); +} + +IMPL_LINK( SwView, ExecRulerClick, Ruler *, pRuler, void ) +{ + OUString sDefPage; + sal_uInt16 nDefDlg = SID_PARA_DLG; + switch( pRuler->GetClickType() ) + { + case RulerType::DontKnow: + case RulerType::Outside: + sDefPage="labelTP_BORDER"; + break; + case RulerType::Indent: + sDefPage="labelTP_PARA_STD"; + break; + case RulerType::Margin1: + case RulerType::Margin2: + nDefDlg= FN_FORMAT_PAGE_DLG; + sDefPage = "page"; + break; + default: + sDefPage = "labelTP_TABULATOR"; + + } + + SfxStringItem aDefPage(nDefDlg, sDefPage); + GetViewFrame()->GetDispatcher()->ExecuteList(nDefDlg, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aDefPage }); +} + +sal_uInt16 SwView::GetMoveType() +{ + return m_nMoveType; +} + +void SwView::SetMoveType(sal_uInt16 nSet) +{ + m_nMoveType = nSet; +} + +void SwView::SetActMark(sal_Int32 nSet) +{ + m_nActMark = nSet; +} + +void SwView::ShowHScrollbar(bool bShow) +{ + assert(m_pHScrollbar && "Scrollbar invalid"); + m_pHScrollbar->ExtendedShow(bShow); +} + +bool SwView::IsHScrollbarVisible()const +{ + assert(m_pHScrollbar && "Scrollbar invalid"); + return m_pHScrollbar->IsVisible( false ) || m_pHScrollbar->IsAuto(); +} + +void SwView::ShowVScrollbar(bool bShow) +{ + assert(m_pVScrollbar && "Scrollbar invalid"); + m_pVScrollbar->ExtendedShow(bShow); +} + +bool SwView::IsVScrollbarVisible()const +{ + assert(m_pVScrollbar && "Scrollbar invalid"); + return m_pVScrollbar->IsVisible( false ); +} + +void SwView::EnableHScrollbar(bool bEnable) +{ + if (m_bHScrollbarEnabled != bEnable) + { + m_bHScrollbarEnabled = bEnable; + InvalidateBorder(); + } +} + +void SwView::EnableVScrollbar(bool bEnable) +{ + if (m_bVScrollbarEnabled != bEnable) + { + m_bVScrollbarEnabled = bEnable; + InvalidateBorder(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewport.cxx b/sw/source/uibase/uiview/viewport.cxx new file mode 100644 index 000000000..0dd9a1964 --- /dev/null +++ b/sw/source/uibase/uiview/viewport.cxx @@ -0,0 +1,1235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/commandevent.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> +#include <vcl/syswin.hxx> + +#include <svx/ruler.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <docsh.hxx> +#include <cmdid.h> +#include <edtwin.hxx> +#include <scroll.hxx> + +#include <PostItMgr.hxx> + +#include <basegfx/utils/zoomtools.hxx> +#include <comphelper/lok.hxx> +#include <vcl/weld.hxx> +#include <tools/svborder.hxx> + +#include "viewfunc.hxx" + +// The SetVisArea of the DocShell must not be called from InnerResizePixel. +// But our adjustments must take place. +static bool bProtectDocShellVisArea = false; + +static sal_uInt16 nPgNum = 0; + +bool SwView::IsDocumentBorder() +{ + if (GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + return true; + + if (!m_pWrtShell) + return false; + + return m_pWrtShell->GetViewOptions()->getBrowseMode() || + SvxZoomType::PAGEWIDTH_NOBORDER == m_pWrtShell->GetViewOptions()->GetZoomType(); +} + +static long GetLeftMargin( SwView const &rView ) +{ + SvxZoomType eType = rView.GetWrtShell().GetViewOptions()->GetZoomType(); + long lRet = rView.GetWrtShell().GetAnyCurRect(CurRectType::PagePrt).Left(); + return eType == SvxZoomType::PERCENT ? lRet + DOCUMENTBORDER : + eType == SvxZoomType::PAGEWIDTH || eType == SvxZoomType::PAGEWIDTH_NOBORDER ? 0 : + lRet + DOCUMENTBORDER + nLeftOfst; +} + +static void lcl_GetPos(SwView const * pView, + Point& rPos, + SwScrollbar const * pScrollbar, + bool bBorder) +{ + SwWrtShell &rSh = pView->GetWrtShell(); + const Size aDocSz( rSh.GetDocSize() ); + + const long lBorder = bBorder ? DOCUMENTBORDER : DOCUMENTBORDER * 2; + const bool bHori = pScrollbar->IsHoriScroll(); + + const long lPos = pScrollbar->GetThumbPos() + (bBorder ? DOCUMENTBORDER : 0); + + long lDelta = lPos - (bHori ? rSh.VisArea().Pos().X() : rSh.VisArea().Pos().Y()); + + const long lSize = (bHori ? aDocSz.Width() : aDocSz.Height()) + lBorder; + // Should right or below are too much space, + // then they must be subtracted out of the VisArea! + long nTmp = pView->GetVisArea().Right()+lDelta; + if ( bHori && nTmp > lSize ) + lDelta -= nTmp - lSize; + nTmp = pView->GetVisArea().Bottom()+lDelta; + if ( !bHori && nTmp > lSize ) + lDelta -= nTmp - lSize; + + bHori ? rPos.AdjustX(lDelta) : rPos.AdjustY(lDelta); + if ( bBorder && (bHori ? rPos.X() : rPos.Y()) < DOCUMENTBORDER ) + bHori ? rPos.setX(DOCUMENTBORDER) : rPos.setY(DOCUMENTBORDER); +} + +// Set zero ruler + +void SwView::InvalidateRulerPos() +{ + static sal_uInt16 aInval[] = + { + SID_ATTR_PARA_LRSPACE, SID_RULER_BORDERS, SID_RULER_PAGE_POS, + SID_RULER_LR_MIN_MAX, SID_ATTR_LONG_ULSPACE, SID_ATTR_LONG_LRSPACE, + SID_RULER_BORDER_DISTANCE, + SID_ATTR_PARA_LRSPACE_VERTICAL, SID_RULER_BORDERS_VERTICAL, + SID_RULER_TEXT_RIGHT_TO_LEFT, + SID_RULER_ROWS, SID_RULER_ROWS_VERTICAL, FN_STAT_PAGE, + 0 + }; + + GetViewFrame()->GetBindings().Invalidate(aInval); + + assert(m_pHRuler && "Why is the ruler not there?"); + m_pHRuler->ForceUpdate(); + m_pVRuler->ForceUpdate(); +} + +// Limits the scrolling so far that only a quarter of the +// screen can be scrolled up before the end of the document. + +long SwView::SetHScrollMax( long lMax ) +{ + const long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; + const long lSize = GetDocSz().Width() + lBorder - m_aVisArea.GetWidth(); + + // At negative values the document is completely visible. + // In this case, no scrolling. + return std::max( std::min( lMax, lSize ), 0L ); +} + +long SwView::SetVScrollMax( long lMax ) +{ + const long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; + long lSize = GetDocSz().Height() + lBorder - m_aVisArea.GetHeight(); + return std::max( std::min( lMax, lSize), 0L ); // see horizontal +} + +Point SwView::AlignToPixel(const Point &rPt) const +{ + return GetEditWin().PixelToLogic( GetEditWin().LogicToPixel( rPt ) ); +} + +// Document size has changed. + +void SwView::DocSzChgd(const Size &rSz) +{ + m_aDocSz = rSz; + + if( !m_pWrtShell || m_aVisArea.IsEmpty() ) // no shell -> no change + { + bDocSzUpdated = false; + return; + } + + //If text has been deleted, it may be that the VisArea points behind the visible range. + tools::Rectangle aNewVisArea( m_aVisArea ); + bool bModified = false; + SwTwips lGreenOffset = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER * 2; + SwTwips lTmp = m_aDocSz.Width() + lGreenOffset; + + if ( aNewVisArea.Right() >= lTmp ) + { + lTmp = aNewVisArea.Right() - lTmp; + aNewVisArea.AdjustRight( -lTmp ); + aNewVisArea.AdjustLeft( -lTmp ); + bModified = true; + } + + lTmp = m_aDocSz.Height() + lGreenOffset; + if ( aNewVisArea.Bottom() >= lTmp ) + { + lTmp = aNewVisArea.Bottom() - lTmp; + aNewVisArea.AdjustBottom( -lTmp ); + aNewVisArea.AdjustTop( -lTmp ); + bModified = true; + } + + if ( bModified ) + SetVisArea( aNewVisArea, false ); + + if ( UpdateScrollbars() && !m_bInOuterResizePixel && !m_bInInnerResizePixel && + !GetViewFrame()->GetFrame().IsInPlace()) + OuterResizePixel( Point(), + GetViewFrame()->GetWindow().GetOutputSizePixel() ); +} + +// Set VisArea newly + +void SwView::SetVisArea( const tools::Rectangle &rRect, bool bUpdateScrollbar ) +{ + Size aOldSz( m_aVisArea.GetSize() ); + if (comphelper::LibreOfficeKit::isActive() && m_pWrtShell) + // If m_pWrtShell's visible area is the whole document, do the same here. + aOldSz = m_pWrtShell->VisArea().SSize(); + + if( rRect == m_aVisArea ) + return; + + const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; + + // No negative position, no negative size + tools::Rectangle aLR = rRect; + if( aLR.Top() < lMin ) + { + aLR.AdjustBottom(lMin - aLR.Top() ); + aLR.SetTop( lMin ); + } + if( aLR.Left() < lMin ) + { + aLR.AdjustRight(lMin - aLR.Left() ); + aLR.SetLeft( lMin ); + } + if( aLR.Right() < 0 ) + aLR.SetRight( 0 ); + if( aLR.Bottom() < 0 ) + aLR.SetBottom( 0 ); + + if( aLR == m_aVisArea ) + return; + + const Size aSize( aLR.GetSize() ); + if( aSize.IsEmpty() ) + return; + + // Before the data can be changed, call an update if necessary. This + // ensures that adjacent Paints in document coordinates are converted + // correctly. + // As a precaution, we do this only when an action is running in the + // shell, because then it is not really drawn but the rectangles will + // be only marked (in document coordinates). + if ( m_pWrtShell && m_pWrtShell->ActionPend() ) + m_pWrtShell->GetWin()->PaintImmediately(); + + m_aVisArea = aLR; + + const bool bOuterResize = bUpdateScrollbar && UpdateScrollbars(); + + if ( m_pWrtShell ) + { + m_pWrtShell->VisPortChgd( m_aVisArea ); + if ( aOldSz != m_pWrtShell->VisArea().SSize() && + ( std::abs(aOldSz.Width() - m_pWrtShell->VisArea().Width()) > 2 || + std::abs(aOldSz.Height() - m_pWrtShell->VisArea().Height()) > 2 ) ) + m_pWrtShell->InvalidateLayout( false ); + } + + if ( !bProtectDocShellVisArea ) + { + // If the size of VisArea is unchanged, we extend the size of the VisArea + // InternalObject on. By that the transport of errors shall be avoided. + tools::Rectangle aVis( m_aVisArea ); + if ( aVis.GetSize() == aOldSz ) + aVis.SetSize( GetDocShell()->SfxObjectShell::GetVisArea(ASPECT_CONTENT).GetSize() ); + // TODO/LATER: why casting?! + //GetDocShell()->SfxInPlaceObject::GetVisArea().GetSize() ); + + // With embedded always with modify... + // TODO/LATER: why casting?! + GetDocShell()->SfxObjectShell::SetVisArea( aVis ); + /* + if ( GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + GetDocShell()->SfxInPlaceObject::SetVisArea( aVis ); + else + GetDocShell()->SvEmbeddedObject::SetVisArea( aVis );*/ + } + + SfxViewShell::VisAreaChanged(); + + InvalidateRulerPos(); + + if ( bOuterResize && !m_bInOuterResizePixel && !m_bInInnerResizePixel) + OuterResizePixel( Point(), + GetViewFrame()->GetWindow().GetOutputSizePixel() ); +} + +// Set Pos VisArea + +void SwView::SetVisArea( const Point &rPt, bool bUpdateScrollbar ) +{ + // Align once, so brushes will be inserted correctly. + // This goes wrong in the BrowseView, because the entire document may + // not be visible. Since the content in frames is fitting exactly, + // align is not possible (better idea?!?!) + // (fix: Bild.de, 200%) It does not work completely without alignment + // Let's see how far we get with half BrushSize. + Point aPt = GetEditWin().LogicToPixel( rPt ); +#if HAVE_FEATURE_DESKTOP + const long nTmp = GetWrtShell().IsFrameView() ? 4 : 8; + aPt.AdjustX( -(aPt.X() % nTmp) ); + aPt.AdjustY( -(aPt.Y() % nTmp) ); +#endif + aPt = GetEditWin().PixelToLogic( aPt ); + + if ( aPt == m_aVisArea.TopLeft() ) + return; + + const long lXDiff = m_aVisArea.Left() - aPt.X(); + const long lYDiff = m_aVisArea.Top() - aPt.Y(); + SetVisArea( tools::Rectangle( aPt, + Point( m_aVisArea.Right() - lXDiff, m_aVisArea.Bottom() - lYDiff ) ), + bUpdateScrollbar); +} + +void SwView::CheckVisArea() +{ + m_pHScrollbar->SetAuto( m_pWrtShell->GetViewOptions()->getBrowseMode() && + !GetViewFrame()->GetFrame().IsInPlace() ); + if ( IsDocumentBorder() ) + { + if ( m_aVisArea.Left() != DOCUMENTBORDER || + m_aVisArea.Top() != DOCUMENTBORDER ) + { + tools::Rectangle aNewVisArea( m_aVisArea ); + aNewVisArea.Move( DOCUMENTBORDER - m_aVisArea.Left(), + DOCUMENTBORDER - m_aVisArea.Top() ); + SetVisArea( aNewVisArea ); + } + } +} + +/// Calculate the visible range. + +// OUT Point *pPt: new position of the visible area + +// IN Rectangle &rRect: Rectangle, which should be located +// within the new visible area. +// sal_uInt16 nRange optional accurate indication of the +// range by which to scroll if necessary. + +void SwView::CalcPt( Point *pPt, const tools::Rectangle &rRect, + sal_uInt16 nRangeX, sal_uInt16 nRangeY) +{ + + const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; + + long nYScroll = GetYScroll(); + long nDesHeight = rRect.GetHeight(); + long nCurHeight = m_aVisArea.GetHeight(); + nYScroll = std::min(nYScroll, nCurHeight - nDesHeight); // If it is scarce, then scroll not too much. + if(nDesHeight > nCurHeight) // the height is not sufficient, then nYScroll is no longer of interest + { + pPt->setY( rRect.Top() ); + pPt->setY( std::max( lMin, pPt->Y() ) ); + } + else if ( rRect.Top() < m_aVisArea.Top() ) // Upward shift + { + pPt->setY( rRect.Top() - (nRangeY != USHRT_MAX ? nRangeY : nYScroll) ); + pPt->setY( std::max( lMin, pPt->Y() ) ); + } + else if( rRect.Bottom() > m_aVisArea.Bottom() ) // Downward shift + { + pPt->setY( rRect.Bottom() - + (m_aVisArea.GetHeight()) + ( nRangeY != USHRT_MAX ? + nRangeY : nYScroll ) ); + pPt->setY( SetVScrollMax( pPt->Y() ) ); + } + long nXScroll = GetXScroll(); + if ( rRect.Right() > m_aVisArea.Right() ) // Shift right + { + pPt->setX( rRect.Right() - + (m_aVisArea.GetWidth()) + + (nRangeX != USHRT_MAX ? nRangeX : nXScroll) ); + pPt->setX( SetHScrollMax( pPt->X() ) ); + } + else if ( rRect.Left() < m_aVisArea.Left() ) // Shift left + { + pPt->setX( rRect.Left() - (nRangeX != USHRT_MAX ? nRangeX : nXScroll) ); + pPt->setX( std::max( ::GetLeftMargin( *this ) + nLeftOfst, pPt->X() ) ); + pPt->setX( std::min( rRect.Left() - nScrollX, pPt->X() ) ); + pPt->setX( std::max( 0L, pPt->X() ) ); + } +} + +// Scrolling + +bool SwView::IsScroll( const tools::Rectangle &rRect ) const +{ + return m_bCenterCursor || m_bTopCursor || !m_aVisArea.IsInside(rRect); +} + +void SwView::Scroll( const tools::Rectangle &rRect, sal_uInt16 nRangeX, sal_uInt16 nRangeY ) +{ + if ( m_aVisArea.IsEmpty() ) + return; + + tools::Rectangle aOldVisArea( m_aVisArea ); + long nDiffY = 0; + + weld::Window* pCareDialog = SwViewShell::GetCareDialog(GetWrtShell()); + if (pCareDialog) + { + int x, y, width, height; + tools::Rectangle aDlgRect; + if (pCareDialog->get_extents_relative_to(*GetEditWin().GetFrameWeld(), x, y, width, height)) + { + Point aTopLeft(GetEditWin().GetSystemWindow()->OutputToAbsoluteScreenPixel(Point(x, y))); + aTopLeft = GetEditWin().AbsoluteScreenToOutputPixel(aTopLeft); + aDlgRect = GetEditWin().PixelToLogic(tools::Rectangle(aTopLeft, Size(width, height))); + } + + // Only if the dialogue is not the VisArea right or left: + if ( aDlgRect.Left() < m_aVisArea.Right() && + aDlgRect.Right() > m_aVisArea.Left() ) + { + // If we are not supposed to be centered, lying in the VisArea + // and are not covered by the dialogue ... + if ( !m_bCenterCursor && aOldVisArea.IsInside( rRect ) + && ( rRect.Left() > aDlgRect.Right() + || rRect.Right() < aDlgRect.Left() + || rRect.Top() > aDlgRect.Bottom() + || rRect.Bottom() < aDlgRect.Top() ) ) + return; + + // Is above or below the dialogue more space? + long nTopDiff = aDlgRect.Top() - m_aVisArea.Top(); + long nBottomDiff = m_aVisArea.Bottom() - aDlgRect.Bottom(); + if ( nTopDiff < nBottomDiff ) + { + if ( nBottomDiff > 0 ) // Is there room below at all? + { // then we move the upper edge and we remember this + nDiffY = aDlgRect.Bottom() - m_aVisArea.Top(); + m_aVisArea.AdjustTop(nDiffY ); + } + } + else + { + if ( nTopDiff > 0 ) // Is there room below at all? + m_aVisArea.SetBottom( aDlgRect.Top() ); // Modify the lower edge + } + } + } + + //s.o. !IsScroll() + if( !(m_bCenterCursor || m_bTopCursor) && m_aVisArea.IsInside( rRect ) ) + { + m_aVisArea = aOldVisArea; + return; + } + // If the rectangle is larger than the visible area --> + // upper left corner + Size aSize( rRect.GetSize() ); + const Size aVisSize( m_aVisArea.GetSize() ); + if( !m_aVisArea.IsEmpty() && ( + aSize.Width() + GetXScroll() > aVisSize.Width() || + aSize.Height()+ GetYScroll() > aVisSize.Height() )) + { + Point aPt( m_aVisArea.TopLeft() ); + aSize.setWidth( std::min( aSize.Width(), aVisSize.Width() ) ); + aSize.setHeight( std::min( aSize.Height(),aVisSize.Height()) ); + + CalcPt( &aPt, tools::Rectangle( rRect.TopLeft(), aSize ), + static_cast< sal_uInt16 >((aVisSize.Width() - aSize.Width()) / 2), + static_cast< sal_uInt16 >((aVisSize.Height()- aSize.Height())/ 2) ); + + if( m_bTopCursor ) + { + const long nBorder = IsDocumentBorder() ? DOCUMENTBORDER : 0; + aPt.setY( std::min( std::max( nBorder, rRect.Top() ), + m_aDocSz.Height() + nBorder - + m_aVisArea.GetHeight() ) ); + } + aPt.AdjustY( -nDiffY ); + m_aVisArea = aOldVisArea; + SetVisArea( aPt ); + return; + } + if( !m_bCenterCursor ) + { + Point aPt( m_aVisArea.TopLeft() ); + CalcPt( &aPt, rRect, nRangeX, nRangeY ); + + if( m_bTopCursor ) + { + const long nBorder = IsDocumentBorder() ? DOCUMENTBORDER : 0; + aPt.setY( std::min( std::max( nBorder, rRect.Top() ), + m_aDocSz.Height() + nBorder - + m_aVisArea.GetHeight() ) ); + } + + aPt.AdjustY( -nDiffY ); + m_aVisArea = aOldVisArea; + SetVisArea( aPt ); + return; + } + + //Center cursor + Point aPnt( m_aVisArea.TopLeft() ); + // ... in Y-direction in any case + aPnt.AdjustY(( rRect.Top() + rRect.Bottom() + - m_aVisArea.Top() - m_aVisArea.Bottom() ) / 2 - nDiffY ); + // ... in X-direction, only if the rectangle protrudes over the right or left of the VisArea. + if ( rRect.Right() > m_aVisArea.Right() || rRect.Left() < m_aVisArea.Left() ) + { + aPnt.AdjustX(( rRect.Left() + rRect.Right() + - m_aVisArea.Left() - m_aVisArea.Right() ) / 2 ); + aPnt.setX( SetHScrollMax( aPnt.X() ) ); + const SwTwips lMin = IsDocumentBorder() ? DOCUMENTBORDER : 0; + aPnt.setX( std::max( (GetLeftMargin( *this ) - lMin) + nLeftOfst, aPnt.X() ) ); + } + m_aVisArea = aOldVisArea; + if (pCareDialog) + { + // If we want to avoid only a dialogue, we do + // not want to go beyond the end of the document. + aPnt.setY( SetVScrollMax( aPnt.Y() ) ); + } + SetVisArea( aPnt ); +} + +/// Scroll page by page +// Returns the value by which to be scrolled with PageUp / Down + +bool SwView::GetPageScrollUpOffset( SwTwips &rOff ) const +{ + // in the LOK case, force the value set by the API + if (comphelper::LibreOfficeKit::isActive() && m_nLOKPageUpDownOffset > 0) + { + rOff = -m_nLOKPageUpDownOffset; + return true; + } + + if ( !m_aVisArea.Top() || !m_aVisArea.GetHeight() ) + return false; + long nYScrl = GetYScroll() / 2; + rOff = -(m_aVisArea.GetHeight() - nYScrl); + // Do not scroll before the beginning of the document. + if( m_aVisArea.Top() - rOff < 0 ) + rOff = rOff - m_aVisArea.Top(); + else if( GetWrtShell().GetCharRect().Top() < (m_aVisArea.Top() + nYScrl)) + rOff += nYScrl; + + return true; +} + +bool SwView::GetPageScrollDownOffset( SwTwips &rOff ) const +{ + // in the LOK case, force the value set by the API + if (comphelper::LibreOfficeKit::isActive() && m_nLOKPageUpDownOffset > 0) + { + rOff = m_nLOKPageUpDownOffset; + return true; + } + + if ( !m_aVisArea.GetHeight() || + (m_aVisArea.GetHeight() > m_aDocSz.Height()) ) + return false; + long nYScrl = GetYScroll() / 2; + rOff = m_aVisArea.GetHeight() - nYScrl; + // Do not scroll past the end of the document. + if ( m_aVisArea.Top() + rOff > m_aDocSz.Height() ) + rOff = m_aDocSz.Height() - m_aVisArea.Bottom(); + else if( GetWrtShell().GetCharRect().Bottom() > + ( m_aVisArea.Bottom() - nYScrl )) + rOff -= nYScrl; + + return rOff > 0; +} + +// Scroll page by page +bool SwView::PageUp() +{ + if (!m_aVisArea.GetHeight()) + return false; + + Point aPos(m_aVisArea.TopLeft()); + aPos.AdjustY( -(m_aVisArea.GetHeight() - (GetYScroll() / 2)) ); + aPos.setY( std::max(0L, aPos.Y()) ); + SetVisArea( aPos ); + return true; +} + +bool SwView::PageDown() +{ + if ( !m_aVisArea.GetHeight() ) + return false; + Point aPos( m_aVisArea.TopLeft() ); + aPos.AdjustY(m_aVisArea.GetHeight() - (GetYScroll() / 2) ); + aPos.setY( SetVScrollMax( aPos.Y() ) ); + SetVisArea( aPos ); + return true; +} + +void SwView::PhyPageUp() +{ + // Check for the currently visible page, do not format + sal_uInt16 nActPage = m_pWrtShell->GetNextPrevPageNum( false ); + + if( USHRT_MAX != nActPage ) + { + const Point aPt( m_aVisArea.Left(), + m_pWrtShell->GetPagePos( nActPage ).Y() ); + Point aAlPt( AlignToPixel( aPt ) ); + // If there is a difference, has been truncated --> then add one pixel, + // so that no residue of the previous page is visible. + if( aPt.Y() != aAlPt.Y() ) + aAlPt.AdjustY(3 * GetEditWin().PixelToLogic( Size( 0, 1 ) ).Height() ); + SetVisArea( aAlPt ); + } +} + +void SwView::PhyPageDown() +{ + // Check for the currently visible page, do not format + sal_uInt16 nActPage = m_pWrtShell->GetNextPrevPageNum(); + // If the last page of the document is visible, do nothing. + if( USHRT_MAX != nActPage ) + { + const Point aPt( m_aVisArea.Left(), + m_pWrtShell->GetPagePos( nActPage ).Y() ); + Point aAlPt( AlignToPixel( aPt ) ); + // If there is a difference, has been truncated --> then add one pixel, + // so that no residue of the previous page is visible. + if( aPt.Y() != aAlPt.Y() ) + aAlPt.AdjustY(3 * GetEditWin().PixelToLogic( Size( 0, 1 ) ).Height() ); + SetVisArea( aAlPt ); + } +} + +bool SwView::PageUpCursor( bool bSelect ) +{ + if ( !bSelect ) + { + const FrameTypeFlags eType = m_pWrtShell->GetFrameType(nullptr,true); + if ( eType & FrameTypeFlags::FOOTNOTE ) + { + m_pWrtShell->MoveCursor(); + m_pWrtShell->GotoFootnoteAnchor(); + m_pWrtShell->Right(CRSR_SKIP_CHARS, false, 1, false ); + return true; + } + } + + SwTwips lOff = 0; + if ( GetPageScrollUpOffset( lOff ) && + (m_pWrtShell->IsCursorReadonly() || + !m_pWrtShell->PageCursor( lOff, bSelect )) && + PageUp() ) + { + m_pWrtShell->ResetCursorStack(); + return true; + } + return false; +} + +bool SwView::PageDownCursor(bool bSelect) +{ + SwTwips lOff = 0; + if ( GetPageScrollDownOffset( lOff ) && + (m_pWrtShell->IsCursorReadonly() || + !m_pWrtShell->PageCursor( lOff, bSelect )) && + PageDown() ) + { + m_pWrtShell->ResetCursorStack(); + return true; + } + return false; +} + +// Handler of the scrollbars + +IMPL_LINK( SwView, ScrollHdl, ScrollBar *, p, void ) +{ + SwScrollbar* pScrollbar = static_cast<SwScrollbar*>(p); + if ( GetWrtShell().ActionPend() ) + return; + + if ( pScrollbar->GetType() == ScrollType::Drag ) + m_pWrtShell->EnableSmooth( false ); + + if(!m_pWrtShell->GetViewOptions()->getBrowseMode() && + pScrollbar->GetType() == ScrollType::Drag) + { + // Here comment out again if it is not desired to scroll together: + // The end scrollhandler invalidate the FN_STAT_PAGE, + // so we don't must do it again. + EndScrollHdl(pScrollbar); + + if ( !m_bWheelScrollInProgress && Help::IsQuickHelpEnabled() && + m_pWrtShell->GetViewOptions()->IsShowScrollBarTips()) + { + + Point aPos( m_aVisArea.TopLeft() ); + lcl_GetPos(this, aPos, pScrollbar, IsDocumentBorder()); + + sal_uInt16 nPhNum = 1; + sal_uInt16 nVirtNum = 1; + + OUString sDisplay; + if(m_pWrtShell->GetPageNumber( aPos.Y(), false, nPhNum, nVirtNum, sDisplay )) + { + // The end scrollhandler invalidate the FN_STAT_PAGE, + // so we don't must do it again. + // if(!GetViewFrame()->GetFrame().IsInPlace()) + // S F X_BINDINGS().Update(FN_STAT_PAGE); + + //QuickHelp: + if( m_pWrtShell->GetPageCnt() > 1 ) + { + tools::Rectangle aRect; + aRect.SetLeft( pScrollbar->GetParent()->OutputToScreenPixel( + pScrollbar->GetPosPixel() ).X() -8 ); + aRect.SetTop( pScrollbar->OutputToScreenPixel( + pScrollbar->GetPointerPosPixel() ).Y() ); + aRect.SetRight( aRect.Left() ); + aRect.SetBottom( aRect.Top() ); + + OUString sPageStr( GetPageStr( nPhNum, nVirtNum, sDisplay )); + SwContentAtPos aCnt( IsAttrAtPos::Outline ); + bool bSuccess = m_pWrtShell->GetContentAtPos(aPos, aCnt); + if (bSuccess && !aCnt.sStr.isEmpty()) + { + sPageStr += " - "; + sal_Int32 nChunkLen = std::min<sal_Int32>(aCnt.sStr.getLength(), 80); + OUString sChunk = aCnt.sStr.copy(0, nChunkLen); + sPageStr = sChunk + sPageStr; + sPageStr = sPageStr.replace('\t', ' '); + sPageStr = sPageStr.replace(0x0a, ' '); + } + nPgNum = nPhNum; + } + } + } + } + else + EndScrollHdl(pScrollbar); + + if ( pScrollbar->GetType() == ScrollType::Drag ) + m_pWrtShell->EnableSmooth( true ); +} + +// Handler of the scrollbars + +IMPL_LINK( SwView, EndScrollHdl, ScrollBar *, p, void ) +{ + SwScrollbar* pScrollbar = static_cast<SwScrollbar*>(p); + if ( !GetWrtShell().ActionPend() ) + { + if(nPgNum) + { + nPgNum = 0; + Help::ShowQuickHelp(pScrollbar, tools::Rectangle(), OUString()); + } + Point aPos( m_aVisArea.TopLeft() ); + bool bBorder = IsDocumentBorder(); + lcl_GetPos(this, aPos, pScrollbar, bBorder); + if ( bBorder && aPos == m_aVisArea.TopLeft() ) + UpdateScrollbars(); + else + SetVisArea( aPos, false ); + + GetViewFrame()->GetBindings().Update(FN_STAT_PAGE); + } +} + +// Calculates the size of the m_aVisArea in dependency of the size of +// EditWin on the screen. + +void SwView::CalcVisArea( const Size &rOutPixel ) +{ + Point aTopLeft; + tools::Rectangle aRect( aTopLeft, rOutPixel ); + aTopLeft = GetEditWin().PixelToLogic( aTopLeft ); + Point aBottomRight( GetEditWin().PixelToLogic( aRect.BottomRight() ) ); + + aRect.SetLeft( aTopLeft.X() ); + aRect.SetTop( aTopLeft.Y() ); + aRect.SetRight( aBottomRight.X() ); + aRect.SetBottom( aBottomRight.Y() ); + + // The shifts to the right and/or below can now be incorrect + // (e.g. change zoom level, change view size). + const long lBorder = IsDocumentBorder() ? DOCUMENTBORDER : DOCUMENTBORDER*2; + if ( aRect.Left() ) + { + const long lWidth = GetWrtShell().GetDocSize().Width() + lBorder; + if ( aRect.Right() > lWidth ) + { + long lDelta = aRect.Right() - lWidth; + aRect.AdjustLeft( -lDelta ); + aRect.AdjustRight( -lDelta ); + } + } + if ( aRect.Top() ) + { + const long lHeight = GetWrtShell().GetDocSize().Height() + lBorder; + if ( aRect.Bottom() > lHeight ) + { + long lDelta = aRect.Bottom() - lHeight; + aRect.AdjustTop( -lDelta ); + aRect.AdjustBottom( -lDelta ); + } + } + SetVisArea( aRect ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); // for snapping points +} + +// Rearrange control elements + +void SwView::CalcAndSetBorderPixel( SvBorder &rToFill ) +{ + bool bRightVRuler = m_pWrtShell->GetViewOptions()->IsVRulerRight(); + if ( m_pVRuler->IsVisible() ) + { + long nWidth = m_pVRuler->GetSizePixel().Width(); + if(bRightVRuler) + rToFill.Right() = nWidth; + else + rToFill.Left() = nWidth; + } + + OSL_ENSURE(m_pHRuler, "Why is the ruler not present?"); + if ( m_pHRuler->IsVisible() ) + rToFill.Top() = m_pHRuler->GetSizePixel().Height(); + + const StyleSettings &rSet = GetEditWin().GetSettings().GetStyleSettings(); + const long nTmp = rSet.GetScrollBarSize(); + if( m_pVScrollbar->IsVisible(true) ) + { + if(bRightVRuler) + rToFill.Left() = nTmp; + else + rToFill.Right() = nTmp; + } + if ( m_pHScrollbar->IsVisible(true) ) + rToFill.Bottom() = nTmp; + + SetBorderPixel( rToFill ); +} + +void ViewResizePixel( const vcl::RenderContext &rRef, + const Point &rOfst, + const Size &rSize, + const Size &rEditSz, + SwScrollbar& rVScrollbar, + SwScrollbar& rHScrollbar, + vcl::Window& rScrollBarBox, + SvxRuler* pVRuler, + SvxRuler* pHRuler, + bool bVRulerRight ) +{ +// ViewResizePixel is also used by Preview!!! + + const bool bHRuler = pHRuler && pHRuler->IsVisible(); + const long nHLinSzHeight = bHRuler ? + pHRuler->GetSizePixel().Height() : 0; + const bool bVRuler = pVRuler && pVRuler->IsVisible(); + const long nVLinSzWidth = bVRuler ? + pVRuler->GetSizePixel().Width() : 0; + + const long nScrollBarSize = rRef.GetSettings().GetStyleSettings().GetScrollBarSize(); + const long nHBSzHeight = rHScrollbar.IsVisible(true) ? nScrollBarSize : 0; + const long nVBSzWidth = rVScrollbar.IsVisible(true) ? nScrollBarSize : 0; + + if(pVRuler) + { + WinBits nStyle = pVRuler->GetStyle()&~WB_RIGHT_ALIGNED; + Point aPos( rOfst.X(), rOfst.Y()+nHLinSzHeight ); + if(bVRulerRight) + { + aPos.AdjustX(rSize.Width() - nVLinSzWidth ); + nStyle |= WB_RIGHT_ALIGNED; + } + Size aSize( nVLinSzWidth, rEditSz.Height() ); + if(!aSize.Width()) + aSize.setWidth( pVRuler->GetSizePixel().Width() ); + pVRuler->SetStyle(nStyle); + pVRuler->SetPosSizePixel( aPos, aSize ); + if(!pVRuler->IsVisible()) + pVRuler->Resize(); + } + // Ruler needs a resize, otherwise it will not work in the invisible condition + if(pHRuler) + { + Size aSize( rSize.Width(), nHLinSzHeight ); + if ( nVBSzWidth && !bVRulerRight) + aSize.AdjustWidth( -nVBSzWidth ); + if(!aSize.Height()) + aSize.setHeight( pHRuler->GetSizePixel().Height() ); + pHRuler->SetPosSizePixel( rOfst, aSize ); + // VCL calls no resize on invisible windows + // but that is not a good idea for the ruler + if(!pHRuler->IsVisible()) + pHRuler->Resize(); + } + + // Arrange scrollbars and SizeBox + Point aScrollFillPos; + { + Point aPos( rOfst.X(), + rOfst.Y()+rSize.Height()-nHBSzHeight ); + if(bVRulerRight) + { + aPos.AdjustX(nVBSzWidth ); + } + + Size aSize( rSize.Width(), nHBSzHeight ); + if ( nVBSzWidth ) + aSize.AdjustWidth( -nVBSzWidth ); + rHScrollbar.SetPosSizePixel( aPos, aSize ); + aScrollFillPos.setY( aPos.Y() ); + } + { + Point aPos( rOfst.X()+rSize.Width()-nVBSzWidth, + rOfst.Y() ); + Size aSize( nVBSzWidth, rSize.Height() ); + if(bVRulerRight) + { + aPos.setX( rOfst.X() ); + if(bHRuler) + { + aPos.AdjustY(nHLinSzHeight ); + aSize.AdjustHeight( -nHLinSzHeight ); + } + } + + if ( nHBSzHeight ) + aSize.AdjustHeight( -nHBSzHeight ); + rVScrollbar.SetPosSizePixel( aPos, aSize ); + + aPos.AdjustY(aSize.Height() ); + + aScrollFillPos.setX( aPos.X() ); + } + + rScrollBarBox.SetPosSizePixel(aScrollFillPos, Size(nVBSzWidth, nHBSzHeight)); +} + +void SwView::ShowAtResize() +{ + m_bShowAtResize = false; + if ( m_pWrtShell->GetViewOptions()->IsViewHRuler() ) + m_pHRuler->Show(); +} + +void SwView::InnerResizePixel( const Point &rOfst, const Size &rSize, bool ) +{ + Size aObjSize = GetObjectShell()->GetVisArea().GetSize(); + if ( !aObjSize.IsEmpty() ) + { + SvBorder aBorder( GetBorderPixel() ); + Size aSize( rSize ); + aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); + aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); + Size aObjSizePixel = GetWindow()->LogicToPixel(aObjSize, MapMode(MapUnit::MapTwip)); + SfxViewShell::SetZoomFactor( Fraction( aSize.Width(), aObjSizePixel.Width() ), + Fraction( aSize.Height(), aObjSizePixel.Height() ) ); + } + + m_bInInnerResizePixel = true; + const bool bHScrollVisible = m_pHScrollbar->IsVisible(true); + const bool bVScrollVisible = m_pVScrollbar->IsVisible(true); + bool bRepeat = false; + do + { + Size aSz( rSize ); + SvBorder aBorder; + CalcAndSetBorderPixel( aBorder ); + if ( GetViewFrame()->GetFrame().IsInPlace() ) + { + Size aViewSize( aSz ); + Point aViewPos( rOfst ); + aViewSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); + aViewSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); + aViewPos.AdjustX(aBorder.Left() ); + aViewPos.AdjustY(aBorder.Top() ); + GetEditWin().SetPosSizePixel( aViewPos, aViewSize ); + } + else + { + aSz.AdjustHeight(aBorder.Top() + aBorder.Bottom() ); + aSz.AdjustWidth(aBorder.Left() + aBorder.Right() ); + } + + Size aEditSz( GetEditWin().GetOutputSizePixel() ); + ViewResizePixel( GetEditWin(), rOfst, aSz, aEditSz, *m_pVScrollbar, + *m_pHScrollbar, *m_pScrollFill, m_pVRuler, m_pHRuler, + m_pWrtShell->GetViewOptions()->IsVRulerRight()); + if ( m_bShowAtResize ) + ShowAtResize(); + + if( m_pHRuler->IsVisible() || m_pVRuler->IsVisible() ) + { + const Fraction& rFrac = GetEditWin().GetMapMode().GetScaleX(); + long nZoom = 100; + if (rFrac.IsValid()) + nZoom = long(rFrac * 100); + + const Fraction aFrac( nZoom, 100 ); + m_pVRuler->SetZoom( aFrac ); + m_pHRuler->SetZoom( aFrac ); + InvalidateRulerPos(); // Invalidate content. + } + // Reset the cursor stack because the cursor positions for PageUp/Down + // no longer fit the currently visible area. + m_pWrtShell->ResetCursorStack(); + + // EditWin never set! + + // Set VisArea, but do not call the SetVisArea of the Docshell there! + bProtectDocShellVisArea = true; + CalcVisArea( aEditSz ); + // Visibility changes of the automatic horizontal scrollbar + // require to repeat the ViewResizePixel() call - but only once! + if(bRepeat) + bRepeat = false; + else if(bHScrollVisible != m_pHScrollbar->IsVisible(true) || + bVScrollVisible != m_pVScrollbar->IsVisible(true)) + bRepeat = true; + }while( bRepeat ); + bProtectDocShellVisArea = false; + m_bInInnerResizePixel = false; +} + +void SwView::OuterResizePixel( const Point &rOfst, const Size &rSize ) +{ + // #i16909# return, if no size (caused by minimize window). + if ( m_bInOuterResizePixel || ( !rSize.Width() && !rSize.Height() ) ) + return; + m_bInOuterResizePixel = true; + + // Determine whether scroll bars may be displayed. + bool bShowH = true, + bShowV = true, + bAuto = true, + bHAuto = true; + + const SwViewOption *pVOpt = m_pWrtShell->GetViewOptions(); + if ( !pVOpt->IsReadonly() || pVOpt->IsStarOneSetting() ) + { + bShowH = pVOpt->IsViewHScrollBar(); + bShowV = pVOpt->IsViewVScrollBar(); + } + + if (!m_bHScrollbarEnabled) + { + bHAuto = bShowH = false; + } + if (!m_bVScrollbarEnabled) + { + bAuto = bShowV = false; + } + + SwDocShell* pDocSh = GetDocShell(); + bool bIsPreview = pDocSh->IsPreview(); + if( bIsPreview ) + { + bShowH = bShowV = bHAuto = bAuto = false; + } + if(m_pHScrollbar->IsVisible(false) != bShowH && !bHAuto) + ShowHScrollbar(bShowH); + m_pHScrollbar->SetAuto( bHAuto ); + if(m_pVScrollbar->IsVisible(false) != bShowV && !bAuto) + ShowVScrollbar(bShowV); + m_pVScrollbar->SetAuto(bAuto); + + SET_CURR_SHELL( m_pWrtShell.get() ); + bool bRepeat = false; + long nCnt = 0; + + bool bUnLockView = !m_pWrtShell->IsViewLocked(); + m_pWrtShell->LockView( true ); + m_pWrtShell->LockPaint(); + + do { + ++nCnt; + const bool bScroll1 = m_pVScrollbar->IsVisible(true); + const bool bScroll2 = m_pHScrollbar->IsVisible(true); + SvBorder aBorder; + CalcAndSetBorderPixel( aBorder ); + const Size aEditSz( GetEditWin().GetOutputSizePixel() ); + ViewResizePixel( GetEditWin(), rOfst, rSize, aEditSz, *m_pVScrollbar, + *m_pHScrollbar, *m_pScrollFill, m_pVRuler, m_pHRuler, + m_pWrtShell->GetViewOptions()->IsVRulerRight() ); + if ( m_bShowAtResize ) + ShowAtResize(); + + if( m_pHRuler->IsVisible() || m_pVRuler->IsVisible() ) + InvalidateRulerPos(); // Invalidate content. + + // Reset the cursor stack because the cursor positions for PageUp/Down + // no longer fit the currently visible area. + m_pWrtShell->ResetCursorStack(); + + OSL_ENSURE( !GetEditWin().IsVisible() || + !aEditSz.IsEmpty() || !m_aVisArea.IsEmpty(), "Small world, isn't it?" ); + + // Never set EditWin! + + // Of course the VisArea must also be set. + // Now is the right time to re-calculate the zoom if it is not a simple factor. + m_pWrtShell->StartAction(); + CalcVisArea( aEditSz ); + + //Thus also in the outplace editing the page width will be adjusted immediately. + //TODO/LATER: is that still necessary?! + /* + if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + pDocSh->SetVisArea( + pDocSh->SfxInPlaceObject::GetVisArea() );*/ + if ( m_pWrtShell->GetViewOptions()->GetZoomType() != SvxZoomType::PERCENT && + !m_pWrtShell->GetViewOptions()->getBrowseMode() ) + SetZoom_( aEditSz, m_pWrtShell->GetViewOptions()->GetZoomType(), 100, true ); + m_pWrtShell->EndAction(); + + bRepeat = bScroll1 != m_pVScrollbar->IsVisible(true); + if ( !bRepeat ) + bRepeat = bScroll2 != m_pHScrollbar->IsVisible(true); + + // Do no infinite loops. + // If possible stop when the (auto-) scroll bars are visible. + if ( bRepeat && + ( nCnt > 10 || ( nCnt > 3 && bHAuto && bAuto ) ) + ) + { + bRepeat = false; + } + + } while ( bRepeat ); + + m_pWrtShell->UnlockPaint(); + if( bUnLockView ) + m_pWrtShell->LockView( false ); + + m_bInOuterResizePixel = false; + + if ( m_pPostItMgr ) + { + m_pPostItMgr->CalcRects(); + m_pPostItMgr->LayoutPostIts(); + } +} + +void SwView::SetZoomFactor( const Fraction &rX, const Fraction &rY ) +{ + const Fraction &rFrac = rX < rY ? rX : rY; + SetZoom( SvxZoomType::PERCENT, static_cast<short>(long(rFrac * Fraction( 100, 1 ))) ); + + // To minimize rounding errors we also adjust the odd values + // of the base class if necessary. + SfxViewShell::SetZoomFactor( rX, rY ); +} + +bool SwView::UpdateScrollbars() +{ + bool bRet = false; + if ( !m_aVisArea.IsEmpty() ) + { + const bool bBorder = IsDocumentBorder(); + tools::Rectangle aTmpRect( m_aVisArea ); + if ( bBorder ) + { + Point aPt( DOCUMENTBORDER, DOCUMENTBORDER ); + aPt = AlignToPixel( aPt ); + aTmpRect.Move( -aPt.X(), -aPt.Y() ); + } + + Size aTmpSz( m_aDocSz ); + const long lOfst = bBorder ? 0 : DOCUMENTBORDER * 2; + aTmpSz.AdjustWidth(lOfst ); aTmpSz.AdjustHeight(lOfst ); + + { + const bool bVScrollVisible = m_pVScrollbar->IsVisible(true); + m_pVScrollbar->DocSzChgd( aTmpSz ); + m_pVScrollbar->ViewPortChgd( aTmpRect ); + if ( bVScrollVisible != m_pVScrollbar->IsVisible(true) ) + bRet = true; + } + { + const bool bHScrollVisible = m_pHScrollbar->IsVisible(true); + m_pHScrollbar->DocSzChgd( aTmpSz ); + m_pHScrollbar->ViewPortChgd( aTmpRect ); + if ( bHScrollVisible != m_pHScrollbar->IsVisible(true) ) + bRet = true; + m_pScrollFill->Show(m_pHScrollbar->IsVisible(true) && m_pVScrollbar->IsVisible(true) ); + } + } + return bRet; +} + +void SwView::Move() +{ + if ( GetWrtShell().IsInSelect() ) + GetWrtShell().EndSelect(); + SfxViewShell::Move(); +} + +bool SwView::HandleWheelCommands( const CommandEvent& rCEvt ) +{ + bool bOk = false; + const CommandWheelData* pWData = rCEvt.GetWheelData(); + if (pWData && CommandWheelMode::ZOOM == pWData->GetMode()) + { + long nFact = m_pWrtShell->GetViewOptions()->GetZoom(); + if( 0L > pWData->GetDelta() ) + nFact = std::max( long(20), basegfx::zoomtools::zoomOut( nFact )); + else + nFact = std::min( long(600), basegfx::zoomtools::zoomIn( nFact )); + + SetZoom( SvxZoomType::PERCENT, nFact ); + bOk = true; + } + else + { + if (pWData && pWData->GetMode()==CommandWheelMode::SCROLL) + { + // This influences whether quick help is shown + m_bWheelScrollInProgress=true; + } + + if (pWData && (CommandWheelMode::SCROLL==pWData->GetMode()) && + (COMMAND_WHEEL_PAGESCROLL == pWData->GetScrollLines())) + { + if (pWData->GetDelta()<0) + PhyPageDown(); + else + PhyPageUp(); + bOk = true; + } + else + bOk = m_pEditWin->HandleScrollCommand(rCEvt, m_pHScrollbar, m_pVScrollbar); + + // Restore default state for case when scroll command comes from dragging scrollbar handle + m_bWheelScrollInProgress=false; + } + return bOk; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewprt.cxx b/sw/source/uibase/uiview/viewprt.cxx new file mode 100644 index 000000000..5232c6b6a --- /dev/null +++ b/sw/source/uibase/uiview/viewprt.cxx @@ -0,0 +1,350 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <cmdid.h> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/svapp.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/weld.hxx> +#include <sfx2/printer.hxx> +#include <editeng/paperinf.hxx> +#include <sfx2/dispatch.hxx> +#include <unotools/misccfg.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svl/eitem.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <svl/flagitem.hxx> +#include <sfx2/linkmgr.hxx> + +#include <modcfg.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <prtopt.hxx> +#include <cfgitems.hxx> +#include "viewfunc.hxx" +#include <swmodule.hxx> +#include <wview.hxx> +#include <IDocumentDeviceAccess.hxx> + +#include <globals.hrc> +#include <strings.hrc> +#include <swabstdlg.hxx> + +#include <uivwimp.hxx> + +using namespace ::com::sun::star; + +// Hand over the printer to Sfx + +SfxPrinter* SwView::GetPrinter( bool bCreate ) +{ + const IDocumentDeviceAccess& rIDDA = GetWrtShell().getIDocumentDeviceAccess(); + SfxPrinter *pOld = rIDDA.getPrinter( false ); + SfxPrinter *pPrt = rIDDA.getPrinter( bCreate ); + if ( pOld != pPrt ) + { + bool bWeb = dynamic_cast<SwWebView*>(this) != nullptr; + ::SetAppPrintOptions( &GetWrtShell(), bWeb ); + } + return pPrt; +} + +// Propagate printer change + +void SetPrinter( IDocumentDeviceAccess* pIDDA, SfxPrinter const * pNew, bool bWeb ) +{ + SwPrintOptions* pOpt = SW_MOD()->GetPrtOptions(bWeb); + if( !pOpt) + return; + + // Reading Application own printing options from SfxPrinter + const SfxItemSet& rSet = pNew->GetOptions(); + + const SwAddPrinterItem* pAddPrinterAttr; + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_ADDPRINTER, false, + reinterpret_cast<const SfxPoolItem**>(&pAddPrinterAttr) ) ) + { + if( pIDDA ) + pIDDA->setPrintData( *pAddPrinterAttr ); + if( !pAddPrinterAttr->GetFaxName().isEmpty() ) + pOpt->SetFaxName(pAddPrinterAttr->GetFaxName()); + } +} + +sal_uInt16 SwView::SetPrinter(SfxPrinter* pNew, SfxPrinterChangeFlags nDiffFlags ) +{ + SwWrtShell &rSh = GetWrtShell(); + SfxPrinter* pOld = rSh.getIDocumentDeviceAccess().getPrinter( false ); + if ( pOld && pOld->IsPrinting() ) + return SFX_PRINTERROR_BUSY; + + if ( (SfxPrinterChangeFlags::JOBSETUP | SfxPrinterChangeFlags::PRINTER) & nDiffFlags ) + { + rSh.getIDocumentDeviceAccess().setPrinter( pNew, true, true ); + if ( nDiffFlags & SfxPrinterChangeFlags::PRINTER ) + rSh.SetModified(); + } + bool bWeb = dynamic_cast< const SwWebView *>( this ) != nullptr; + if ( nDiffFlags & SfxPrinterChangeFlags::OPTIONS ) + ::SetPrinter( &rSh.getIDocumentDeviceAccess(), pNew, bWeb ); + + const bool bChgOri = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION); + const bool bChgSize = bool(nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE); + if ( bChgOri || bChgSize ) + { + rSh.StartAllAction(); + if ( bChgOri ) + rSh.ChgAllPageOrientation( pNew->GetOrientation() ); + if ( bChgSize ) + { + Size aSz( SvxPaperInfo::GetPaperSize( pNew ) ); + rSh.ChgAllPageSize( aSz ); + } + rSh.SetModified(); + rSh.EndAllAction(); + InvalidateRulerPos(); + } + return 0; +} + +bool SwView::HasPrintOptionsPage() const +{ + return true; +} + +namespace +{ + class SvxPrtQryBox + { + private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; + public: + SvxPrtQryBox(weld::Window* pParent) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::NONE, SvxResId(RID_SVXSTR_QRY_PRINT_MSG))) + { + m_xQueryBox->set_title(SvxResId(RID_SVXSTR_QRY_PRINT_TITLE)); + + m_xQueryBox->add_button(SvxResId(RID_SVXSTR_QRY_PRINT_SELECTION), RET_OK); + m_xQueryBox->add_button(SvxResId(RID_SVXSTR_QRY_PRINT_ALL), 2); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + m_xQueryBox->set_default_response(RET_OK); + } + short run() { return m_xQueryBox->run(); } + }; +} + +// TabPage for application-specific print options + +std::unique_ptr<SfxTabPage> SwView::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rSet) +{ + return ::CreatePrintOptionsPage(pPage, pController, rSet, false); +} + +// Print dispatcher + +void SwView::ExecutePrint(SfxRequest& rReq) +{ + bool bWeb = dynamic_cast<SwWebView*>( this ) != nullptr; + ::SetAppPrintOptions( &GetWrtShell(), bWeb ); + switch (rReq.GetSlot()) + { + case FN_FAX: + { + SwPrintOptions* pPrintOptions = SW_MOD()->GetPrtOptions(bWeb); + const OUString& sFaxName(pPrintOptions->GetFaxName()); + if (!sFaxName.isEmpty()) + { + SfxStringItem aPrinterName(SID_PRINTER_NAME, sFaxName); + SfxBoolItem aSilent( SID_SILENT, true ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_PRINTDOC, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aPrinterName, &aSilent }); + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_ERR_NO_FAX))); + const char* pResId = bWeb ? STR_WEBOPTIONS : STR_TEXTOPTIONS; + xInfoBox->set_primary_text(xInfoBox->get_primary_text().replaceFirst("%1", SwResId(pResId))); + xInfoBox->run(); + SfxUInt16Item aDefPage(SID_SW_EDITOPTIONS, TP_OPTPRINT_PAGE); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_SW_EDITOPTIONS, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD, + { &aDefPage }); + } + } + break; + case SID_PRINTDOC: + case SID_PRINTDOCDIRECT: + { + SwWrtShell* pSh = &GetWrtShell(); + const SfxBoolItem* pSilentItem = rReq.GetArg<SfxBoolItem>(SID_SILENT); + bool bSilent = pSilentItem && pSilentItem->GetValue(); + const SfxBoolItem* pPrintFromMergeItem = rReq.GetArg<SfxBoolItem>(FN_QRY_MERGE); + if(pPrintFromMergeItem) + rReq.RemoveItem(FN_QRY_MERGE); + bool bFromMerge = pPrintFromMergeItem && pPrintFromMergeItem->GetValue(); + bool bPrintSelection = false; + if(!bSilent && !bFromMerge && + SW_MOD()->GetModuleConfig()->IsAskForMailMerge() && pSh->IsAnyDatabaseFieldInDoc()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetEditWin().GetFrameWeld(), "modules/swriter/ui/printmergedialog.ui")); + std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("PrintMergeDialog")); + short nRet = xBox->run(); + if(RET_NO != nRet) + { + if(RET_YES == nRet) + { + SfxBoolItem aBool(FN_QRY_MERGE, true); + GetViewFrame()->GetDispatcher()->ExecuteList( + FN_QRY_MERGE, SfxCallMode::ASYNCHRON, + { &aBool }); + rReq.Ignore(); + } + return; + } + } + else if( rReq.GetSlot() == SID_PRINTDOCDIRECT && ! bSilent ) + { + if( pSh->IsSelection() || pSh->IsFrameSelected() || pSh->IsObjSelected() ) + { + SvxPrtQryBox aBox(GetEditWin().GetFrameWeld()); + short nBtn = aBox.run(); + if( RET_CANCEL == nBtn ) + return; + + if( RET_OK == nBtn ) + bPrintSelection = true; + } + } + + //#i61455# if master documents are printed silently without loaded links then update the links now + if( bSilent && pSh->IsGlobalDoc() && !pSh->IsGlblDocSaveLinks() ) + { + pSh->GetLinkManager().UpdateAllLinks( false, false, nullptr ); + } + SfxRequest aReq( rReq ); + SfxBoolItem aBool(SID_SELECTION, bPrintSelection); + aReq.AppendItem( aBool ); + SfxViewShell::ExecuteSlot( aReq, SfxViewShell::GetInterface() ); + return; + } + default: + OSL_ENSURE(false, "wrong dispatcher"); + return; + } +} + +int SwView::getPart() const +{ + return 0; +} + +void SwView::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwView")); + SfxViewShell::dumpAsXml(pWriter); + if (m_pWrtShell) + m_pWrtShell->dumpAsXml(pWriter); + xmlTextWriterEndElement(pWriter); +} + +void SwView::SetRedlineAuthor(const OUString& rAuthor) +{ + m_pViewImpl->m_sRedlineAuthor = rAuthor; +} + +const OUString& SwView::GetRedlineAuthor() const +{ + return m_pViewImpl->m_sRedlineAuthor; +} + +void SwView::NotifyCursor(SfxViewShell* pViewShell) const +{ + m_pWrtShell->NotifyCursor(pViewShell); +} + +// Create page printer/additions for SwView and SwPagePreview + +std::unique_ptr<SfxTabPage> CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet &rOptions, + bool bPreview) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + + ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc(TP_OPTPRINT_PAGE); + OSL_ENSURE(pFact, "No Page Creator"); + if (!fnCreatePage) + return nullptr; + + std::unique_ptr<SfxTabPage> xSfxPage = fnCreatePage(pPage, pController, &rOptions); + OSL_ENSURE(xSfxPage, "No page"); + if (!xSfxPage) + return nullptr; + + SfxAllItemSet aSet(*(rOptions.GetPool())); + aSet.Put(SfxBoolItem(SID_PREVIEWFLAG_TYPE, bPreview)); + aSet.Put(SfxBoolItem(SID_FAX_LIST, true)); + xSfxPage->PageCreated(aSet); + return xSfxPage; +} + +void SetAppPrintOptions( SwViewShell* pSh, bool bWeb ) +{ + const IDocumentDeviceAccess& rIDDA = pSh->getIDocumentDeviceAccess(); + const SwPrintData& aPrtData = rIDDA.getPrintData(); + + if( rIDDA.getPrinter( false ) ) + { + // Close application own printing options in SfxPrinter. + SwAddPrinterItem aAddPrinterItem(aPrtData); + SfxItemSet aSet( + pSh->GetAttrPool(), + svl::Items< + 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>{}); + + utl::MiscCfg aMisc; + + if(bWeb) + aSet.Put(SfxUInt16Item(SID_HTML_MODE, + ::GetHtmlMode(static_cast<SwWrtShell*>(pSh)->GetView().GetDocShell()))); + aSet.Put(SfxBoolItem(SID_PRINTER_NOTFOUND_WARN, + aMisc.IsNotFoundWarning() )); + aSet.Put(aAddPrinterItem); + aSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, + static_cast<int>(aMisc.IsPaperSizeWarning() ? SfxPrinterChangeFlags::CHG_SIZE : SfxPrinterChangeFlags::NONE) | + static_cast<int>(aMisc.IsPaperOrientationWarning() ? SfxPrinterChangeFlags::CHG_ORIENTATION : SfxPrinterChangeFlags::NONE ))); + + rIDDA.getPrinter( true )->SetOptions( aSet ); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewsrch.cxx b/sw/source/uibase/uiview/viewsrch.cxx new file mode 100644 index 000000000..440b77ad7 --- /dev/null +++ b/sw/source/uibase/uiview/viewsrch.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 <string> + +#include <memory> +#include <boost/property_tree/json_parser.hpp> + +#include <hintids.hxx> + +#include <sal/log.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <svx/pageitem.hxx> +#include <svl/whiter.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svl/srchitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <svx/srchdlg.hxx> +#include <edtwin.hxx> +#include <swmodule.hxx> +#include <swwait.hxx> +#include <workctrl.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <swundo.hxx> +#include <uitool.hxx> +#include <cmdid.h> +#include <docsh.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <comphelper/string.hxx> + +#include <strings.hrc> +#include <SwRewriter.hxx> + +#include <PostItMgr.hxx> + +using namespace com::sun::star; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; + +//Search Parameter + +struct SwSearchOptions +{ + SwDocPositions eStart, eEnd; + bool bDontWrap; + + SwSearchOptions( SwWrtShell const * pSh, bool bBackward ); +}; + +/// Adds rMatches using rKey as a key to the rTree tree. +static void lcl_addContainerToJson(boost::property_tree::ptree& rTree, const OString& rKey, const std::vector<OString>& rMatches) +{ + boost::property_tree::ptree aChildren; + + for (const OString& rMatch : rMatches) + { + boost::property_tree::ptree aChild; + aChild.put("part", "0"); + aChild.put("rectangles", rMatch.getStr()); + aChildren.push_back(std::make_pair("", aChild)); + } + + rTree.add_child(rKey.getStr(), aChildren); +} + +/// Emits LOK callbacks (count, selection) for search results. +static void lcl_emitSearchResultCallbacks(SvxSearchItem const * pSearchItem, SwWrtShell const * pWrtShell, bool bHighlightAll) +{ + // Emit a callback also about the selection rectangles, grouped by matches. + if (SwPaM* pPaM = pWrtShell->GetCursor()) + { + std::vector<OString> aMatches; + for (SwPaM& rPaM : pPaM->GetRingContainer()) + { + if (SwShellCursor* pShellCursor = dynamic_cast<SwShellCursor*>(&rPaM)) + { + std::vector<OString> aSelectionRectangles; + pShellCursor->SwSelPaintRects::Show(&aSelectionRectangles); + std::vector<OString> aRect; + for (const OString & rSelectionRectangle : aSelectionRectangles) + { + if (rSelectionRectangle.isEmpty()) + continue; + aRect.push_back(rSelectionRectangle); + } + OString sRect = comphelper::string::join("; ", aRect); + aMatches.push_back(sRect); + } + } + boost::property_tree::ptree aTree; + aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr()); + aTree.put("highlightAll", bHighlightAll); + lcl_addContainerToJson(aTree, "searchResultSelection", aMatches); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + OString aPayload = aStream.str().c_str(); + + pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr()); + } +} + +void SwView::ExecSearch(SfxRequest& rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + bool bQuiet = false; + if(pArgs && SfxItemState::SET == pArgs->GetItemState(SID_SEARCH_QUIET, false, &pItem)) + bQuiet = static_cast<const SfxBoolItem*>( pItem)->GetValue(); + + sal_uInt16 nSlot = rReq.GetSlot(); + if (nSlot == FN_REPEAT_SEARCH && !s_pSrchItem) + { + if(bQuiet) + { + rReq.SetReturnValue(SfxBoolItem(nSlot, false)); + nSlot = 0; + } + } + if( m_pWrtShell->IsBlockMode() ) + m_pWrtShell->LeaveBlockMode(); + switch (nSlot) + { + // for now do nothing + case SID_SEARCH_ITEM: + { + delete s_pSrchItem; + s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone(); + } + break; + + case FID_SEARCH_ON: + s_bJustOpened = true; + GetViewFrame()->GetBindings().Invalidate(SID_SEARCH_ITEM); + break; + + case FID_SEARCH_OFF: + if(pArgs) + { + // Unregister dialog + delete s_pSrchItem; + s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone(); + + DELETEZ( s_pSearchList ); + DELETEZ( s_pReplaceList ); + + SvxSearchDialog *const pSrchDlg(GetSearchDialog()); + if (pSrchDlg) + { + // We will remember the search-/replace items. + const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList(); + if( nullptr != pList && pList->Count() ) + s_pSearchList = new SearchAttrItemList( *pList ); + + pList = pSrchDlg->GetReplaceItemList(); + if (nullptr != pList && pList->Count()) + s_pReplaceList = new SearchAttrItemList( *pList ); + } + } + break; + + case FN_REPEAT_SEARCH: + case FID_SEARCH_NOW: + { + sal_uInt16 nMoveType = SwView::GetMoveType(); + { + if(FID_SEARCH_NOW == nSlot && !rReq.IsAPI()) + SwView::SetMoveType(NID_SRCH_REP); + } + + SvxSearchDialog * pSrchDlg(GetSearchDialog()); + if (pSrchDlg) + { + DELETEZ( s_pSearchList ); + DELETEZ( s_pReplaceList ); + + const SearchAttrItemList* pList = pSrchDlg->GetSearchItemList(); + if( nullptr != pList && pList->Count() ) + s_pSearchList = new SearchAttrItemList( *pList ); + + pList = pSrchDlg->GetReplaceItemList(); + if (nullptr != pList && pList->Count()) + s_pReplaceList = new SearchAttrItemList( *pList ); + } + + if (nSlot == FN_REPEAT_SEARCH) + { + OSL_ENSURE(s_pSrchItem, "SearchItem missing"); + if( !s_pSrchItem ) + s_pSrchItem = new SvxSearchItem(SID_SEARCH_ITEM); + } + else + { + // Get SearchItem from request + OSL_ENSURE(pArgs, "Args missing"); + if ( pArgs ) + { + delete s_pSrchItem; + s_pSrchItem = pArgs->Get(SID_SEARCH_ITEM).Clone(); + } + } + SvxSearchCmd eCommand = s_pSrchItem->GetCommand(); + switch (eCommand) + { + case SvxSearchCmd::FIND: + { + bool bRet = SearchAndWrap(bQuiet); + if( bRet ) + { + Scroll(m_pWrtShell->GetCharRect().SVRect()); + if (comphelper::LibreOfficeKit::isActive()) + lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ false); + } + rReq.SetReturnValue(SfxBoolItem(nSlot, bRet)); + } + break; + case SvxSearchCmd::FIND_ALL: + { + // Disable LOK selection notifications during search. + m_pWrtShell->GetSfxViewShell()->setTiledSearching(true); + bool bRet = SearchAll(); + m_pWrtShell->GetSfxViewShell()->setTiledSearching(false); + + if( !bRet ) + { +#if HAVE_FEATURE_DESKTOP + if( !bQuiet ) + { + m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8().getStr()); + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + } +#endif + s_bFound = false; + } + else if (comphelper::LibreOfficeKit::isActive()) + lcl_emitSearchResultCallbacks(s_pSrchItem, m_pWrtShell.get(), /* bHighlightAll = */ true); + rReq.SetReturnValue(SfxBoolItem(nSlot, bRet)); + } + break; + case SvxSearchCmd::REPLACE: + { + + // 1) Replace selection (Not if only attributes should be replaced) +//JP 27.04.95: Why? +// what if you only want to assign attributes to the found?? + + SvxSearchCmd nCmd = SvxSearchCmd::FIND; + if( !s_pSrchItem->GetReplaceString().isEmpty() || + !s_pReplaceList ) + { + // Prevent, that the replaced string will be found again + // if the replacement string is containing the search string. + bool bBack = s_pSrchItem->GetBackward(); + if (bBack) + m_pWrtShell->Push(); + OUString aReplace( s_pSrchItem->GetReplaceString() ); + i18nutil::SearchOptions2 aTmp( s_pSrchItem->GetSearchOptions() ); + std::optional<OUString> xBackRef = sw::ReplaceBackReferences(aTmp, + m_pWrtShell->GetCursor(), m_pWrtShell->GetLayout()); + if( xBackRef ) + s_pSrchItem->SetReplaceString( *xBackRef ); + Replace(); + if( xBackRef ) + { + s_pSrchItem->SetReplaceString( aReplace ); + } + if (bBack) + { + m_pWrtShell->Pop(); + m_pWrtShell->SwapPam(); + } + } + else if( s_pReplaceList ) + nCmd = SvxSearchCmd::REPLACE; + + // 2) Search further (without replacing!) + + SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand(); + s_pSrchItem->SetCommand( nCmd ); + bool bRet = SearchAndWrap(bQuiet); + if( bRet ) + Scroll( m_pWrtShell->GetCharRect().SVRect()); + s_pSrchItem->SetCommand( nOldCmd ); + rReq.SetReturnValue(SfxBoolItem(nSlot, bRet)); + } + break; + + case SvxSearchCmd::REPLACE_ALL: + { + SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() ); + s_bExtra = false; + sal_uLong nFound; + + { //Scope for SwWait-Object + SwWait aWait( *GetDocShell(), true ); + m_pWrtShell->StartAllAction(); + + // i#8288 "replace all" should not change cursor + // position, so save current cursor + m_pWrtShell->Push(); + + if (!s_pSrchItem->GetSelection()) + { + // if we don't want to search in the selection... + m_pWrtShell->KillSelection(nullptr, false); + if (SwDocPositions::Start == aOpts.eEnd) + { + m_pWrtShell->EndOfSection(); + } + else + { + m_pWrtShell->StartOfSection(); + } + } + nFound = FUNC_Search( aOpts ); + // create it just to overwrite it with stack cursor + m_pWrtShell->CreateCursor(); + // i#8288 restore the original cursor position + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + m_pWrtShell->EndAllAction(); + } + + rReq.SetReturnValue(SfxBoolItem(nSlot, nFound != 0 && ULONG_MAX != nFound)); + if( !nFound ) + { +#if HAVE_FEATURE_DESKTOP + if( !bQuiet ) + { + m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8().getStr()); + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + } +#endif + s_bFound = false; + SwView::SetMoveType(nMoveType); + return; + } + + if( !bQuiet && ULONG_MAX != nFound) + { + OUString sText( SwResId( STR_NB_REPLACED ) ); + sText = sText.replaceFirst("XX", OUString::number( nFound )); + SvxSearchDialogWrapper::SetSearchLabel(sText); + } + } + break; + } + + uno::Reference< frame::XDispatchRecorder > xRecorder = + GetViewFrame()->GetBindings().GetRecorder(); + //prevent additional dialogs in recorded macros + if ( xRecorder.is() ) + rReq.AppendItem(SfxBoolItem(SID_SEARCH_QUIET, true)); + + rReq.Done(); + m_eLastSearchCommand = s_pSrchItem->GetCommand(); + SwView::SetMoveType(nMoveType); + } + break; + case FID_SEARCH_SEARCHSET: + case FID_SEARCH_REPLACESET: + { + static const sal_uInt16 aNormalAttr[] = + { +/* 0 */ RES_CHRATR_CASEMAP, RES_CHRATR_CASEMAP, +/* 2 */ RES_CHRATR_COLOR, RES_CHRATR_POSTURE, +/* 4 */ RES_CHRATR_SHADOWED, RES_CHRATR_WORDLINEMODE, +/* 6 */ RES_CHRATR_BLINK, RES_CHRATR_BLINK, +/* 8 */ RES_CHRATR_BACKGROUND, RES_CHRATR_BACKGROUND, +/*10 */ RES_CHRATR_ROTATE, RES_CHRATR_ROTATE, +/*12 */ RES_CHRATR_SCALEW, RES_CHRATR_RELIEF, +/*14 */ RES_CHRATR_OVERLINE, RES_CHRATR_OVERLINE, +/*16 */ RES_PARATR_LINESPACING, RES_PARATR_HYPHENZONE, +/*18 */ RES_PARATR_REGISTER, RES_PARATR_REGISTER, +/*20 */ RES_PARATR_VERTALIGN, RES_PARATR_VERTALIGN, +/*22 */ RES_LR_SPACE, RES_UL_SPACE, +/*24 */ SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP, +/*26 */ 0 + }; + + SfxItemSet aSet(m_pWrtShell->GetAttrPool(), aNormalAttr); + + if( SW_MOD()->GetCTLOptions().IsCTLFontEnabled() ) + { + aSet.MergeRange(RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_WEIGHT); + } + SvtCJKOptions aCJKOpt; + if( aCJKOpt.IsAnyEnabled() ) + { + aSet.MergeRange(RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_WEIGHT); + aSet.MergeRange(RES_CHRATR_EMPHASIS_MARK, RES_CHRATR_TWO_LINES); + aSet.MergeRange(RES_PARATR_SCRIPTSPACE, RES_PARATR_FORBIDDEN_RULES); + } + + sal_uInt16 nWhich = SID_SEARCH_SEARCHSET; + + if ( FID_SEARCH_REPLACESET == nSlot ) + { + nWhich = SID_SEARCH_REPLACESET; + + if ( s_pReplaceList ) + { + s_pReplaceList->Get( aSet ); + DELETEZ( s_pReplaceList ); + } + } + else if ( s_pSearchList ) + { + s_pSearchList->Get( aSet ); + DELETEZ( s_pSearchList ); + } + rReq.SetReturnValue( SvxSetItem( nWhich, aSet ) ); + } + break; + default: + SAL_WARN_IF( nSlot, "sw", "nSlot: " << nSlot << " wrong Dispatcher (viewsrch.cxx)" ); + return; + } +} + +bool SwView::SearchAndWrap(bool bApi) +{ + SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() ); + + // Remember starting position of the search for wraparound + // Start- / EndAction perhaps because existing selections of 'search all' + m_pWrtShell->StartAllAction(); + m_pWrtShell->Push(); + + // After a search all action we place the cursor at the beginning of + // the document so that the single search selects the first matching + // occurrence in the document instead of the second. + if( m_eLastSearchCommand == SvxSearchCmd::FIND_ALL ) + { + if( SwDocPositions::Start == aOpts.eEnd ) + m_pWrtShell->EndOfSection(); + else + m_pWrtShell->StartOfSection(); + } + + // fdo#65014 : Ensure that the point of the cursor is at the extremity of the + // selection closest to the end being searched to as to exclude the selected + // region from the search. (This doesn't work in the case of multiple + // selected regions as the cursor doesn't mark the selection in that case.) + m_pWrtShell->GetCursor()->Normalize( s_pSrchItem->GetBackward() ); + + if (!m_pWrtShell->HasSelection() && (s_pSrchItem->HasStartPoint())) + { + // No selection -> but we have a start point (top left corner of the + // current view), start searching from there, not from the current + // cursor position. + SwEditShell& rShell = GetWrtShell(); + Point aPosition(s_pSrchItem->GetStartPointX(), s_pSrchItem->GetStartPointY()); + rShell.SetCursor(aPosition); + } + + // If you want to search in selected areas, they must not be unselected. + if (!s_pSrchItem->GetSelection()) + m_pWrtShell->KillSelection(nullptr, false); + + std::unique_ptr<SwWait> pWait(new SwWait( *GetDocShell(), true )); + if( FUNC_Search( aOpts ) ) + { + s_bFound = true; + if(m_pWrtShell->IsSelFrameMode()) + { + m_pWrtShell->UnSelectFrame(); + m_pWrtShell->LeaveSelFrameMode(); + } + m_pWrtShell->Pop(); + m_pWrtShell->EndAllAction(); + return true; + } + pWait.reset(); + + // Search in the specialized areas when no search is present in selections. + // When searching selections will already searched in these special areas. + bool bHasSrchInOther = s_bExtra; + if (!s_pSrchItem->GetSelection() && !s_bExtra ) + { + s_bExtra = true; + if( FUNC_Search( aOpts ) ) + { + s_bFound = true; + m_pWrtShell->Pop(); + m_pWrtShell->EndAllAction(); + return true; + } + s_bExtra = false; + } + else + s_bExtra = !s_bExtra; + + // If starting position is at the end or beginning of the document. + if (aOpts.bDontWrap) + { + m_pWrtShell->EndAllAction(); + if( !bApi ) + { +#if HAVE_FEATURE_DESKTOP + m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8().getStr()); + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); +#endif + } + s_bFound = false; + m_pWrtShell->Pop(); + return false; + } + m_pWrtShell->EndAllAction(); + // Try again with WrapAround? + + m_pWrtShell->StartAllAction(); + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + pWait.reset(new SwWait( *GetDocShell(), true )); + + bool bSrchBkwrd = SwDocPositions::Start == aOpts.eEnd; + + aOpts.eEnd = bSrchBkwrd ? SwDocPositions::Start : SwDocPositions::End; + aOpts.eStart = bSrchBkwrd ? SwDocPositions::End : SwDocPositions::Start; + + if (bHasSrchInOther) + { + m_pWrtShell->ClearMark(); + // Select the start or the end of the entire document + if (bSrchBkwrd) + m_pWrtShell->SttEndDoc(false); + else + m_pWrtShell->SttEndDoc(true); + } + + s_bFound = bool(FUNC_Search( aOpts )); + + // If WrapAround found no matches in the body text, search in the special + // sections, too. + if (!s_bFound && !s_pSrchItem->GetSelection() && !s_bExtra) + { + s_bExtra = true; + if (FUNC_Search(aOpts)) + s_bFound = true; + else + s_bExtra = false; + } + + m_pWrtShell->EndAllAction(); + pWait.reset(); +#if HAVE_FEATURE_DESKTOP + if (s_bFound) + { + if (!bSrchBkwrd) + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End); + else + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start); + } + else if(!bApi) + { + m_pWrtShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, s_pSrchItem->GetSearchString().toUtf8().getStr()); + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + } +#endif + return s_bFound; +} + +bool SwView::SearchAll() +{ + SwWait aWait( *GetDocShell(), true ); + m_pWrtShell->StartAllAction(); + + SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() ); + + if (!s_pSrchItem->GetSelection()) + { + // Cancel existing selections, if should not be sought in selected areas. + m_pWrtShell->KillSelection(nullptr, false); + + if( SwDocPositions::Start == aOpts.eEnd ) + m_pWrtShell->EndOfSection(); + else + m_pWrtShell->StartOfSection(); + } + s_bExtra = false; + sal_uInt16 nFound = static_cast<sal_uInt16>(FUNC_Search( aOpts )); + s_bFound = 0 != nFound; + + m_pWrtShell->EndAllAction(); + return s_bFound; +} + +void SwView::Replace() +{ + SwWait aWait( *GetDocShell(), true ); + + m_pWrtShell->StartAllAction(); + + if( s_pSrchItem->GetPattern() ) // Templates? + { + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, s_pSrchItem->GetSearchString()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, s_pSrchItem->GetReplaceString()); + + m_pWrtShell->StartUndo(SwUndoId::UI_REPLACE_STYLE, &aRewriter); + + m_pWrtShell->SetTextFormatColl( m_pWrtShell->GetParaStyle( + s_pSrchItem->GetReplaceString(), + SwWrtShell::GETSTYLE_CREATESOME )); + + m_pWrtShell->EndUndo(); + } + else + { + if (GetPostItMgr()->HasActiveSidebarWin()) + GetPostItMgr()->Replace(s_pSrchItem); + + bool bReqReplace = true; + + if(m_pWrtShell->HasSelection()) + { + /* check that the selection match the search string*/ + //save state + SwPosition aStartPos = * m_pWrtShell->GetSwCursor()->Start(); + SwPosition aEndPos = * m_pWrtShell->GetSwCursor()->End(); + bool bHasSelection = s_pSrchItem->GetSelection(); + SvxSearchCmd nOldCmd = s_pSrchItem->GetCommand(); + + //set state for checking if current selection has a match + s_pSrchItem->SetCommand( SvxSearchCmd::FIND ); + s_pSrchItem->SetSelection(true); + + //check if it matches + SwSearchOptions aOpts( m_pWrtShell.get(), s_pSrchItem->GetBackward() ); + if( ! FUNC_Search(aOpts) ) + { + + //no matching therefore should not replace selection + // => remove selection + + if(! s_pSrchItem->GetBackward() ) + { + (* m_pWrtShell->GetSwCursor()->Start()) = aStartPos; + (* m_pWrtShell->GetSwCursor()->End()) = aEndPos; + } + else + { + (* m_pWrtShell->GetSwCursor()->Start()) = aEndPos; + (* m_pWrtShell->GetSwCursor()->End()) = aStartPos; + } + bReqReplace = false; + } + + //set back old search state + s_pSrchItem->SetCommand( nOldCmd ); + s_pSrchItem->SetSelection(bHasSelection); + } + /* + * remove current selection + * otherwise it is always replaced + * no matter if the search string exists or not in the selection + * Now the selection is removed and the next matching string is selected + */ + + if( bReqReplace ) + { + + bool bReplaced = m_pWrtShell->SwEditShell::Replace( s_pSrchItem->GetReplaceString(), + s_pSrchItem->GetRegExp()); + if( bReplaced && s_pReplaceList && s_pReplaceList->Count() && m_pWrtShell->HasSelection() ) + { + SfxItemSet aReplSet( m_pWrtShell->GetAttrPool(), + aTextFormatCollSetRange ); + if( s_pReplaceList->Get( aReplSet ).Count() ) + { + ::SfxToSwPageDescAttr( *m_pWrtShell, aReplSet ); + m_pWrtShell->SwEditShell::SetAttrSet( aReplSet ); + } + } + } + } + + m_pWrtShell->EndAllAction(); +} + +SwSearchOptions::SwSearchOptions( SwWrtShell const * pSh, bool bBackward ) + : eStart(SwDocPositions::Curr) +{ + if( bBackward ) + { + eEnd = SwDocPositions::Start; + bDontWrap = pSh->IsEndOfDoc(); + } + else + { + eEnd = SwDocPositions::End; + bDontWrap = pSh->IsStartOfDoc(); + } +} + +sal_uLong SwView::FUNC_Search( const SwSearchOptions& rOptions ) +{ +#if HAVE_FEATURE_DESKTOP + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty); +#endif + bool bDoReplace = s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE || + s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL; + + FindRanges eRanges = s_pSrchItem->GetSelection() + ? FindRanges::InSel + : s_bExtra + ? FindRanges::InOther : FindRanges::InBody; + if (s_pSrchItem->GetCommand() == SvxSearchCmd::FIND_ALL || + s_pSrchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL) + eRanges |= FindRanges::InSelAll; + + m_pWrtShell->SttSelect(); + + static const sal_uInt16 aSearchAttrRange[] = { + RES_FRMATR_BEGIN, RES_FRMATR_END-1, + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_PARATR_BEGIN, RES_PARATR_END-1, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP, + 0 }; + + SfxItemSet aSrchSet( m_pWrtShell->GetAttrPool(), aSearchAttrRange); + if( s_pSearchList && s_pSearchList->Count() ) + { + s_pSearchList->Get( aSrchSet ); + + // -- Page break with page template + ::SfxToSwPageDescAttr( *m_pWrtShell, aSrchSet ); + } + + std::unique_ptr<SfxItemSet> pReplSet; + if( bDoReplace && s_pReplaceList && s_pReplaceList->Count() ) + { + pReplSet.reset( new SfxItemSet( m_pWrtShell->GetAttrPool(), + aSearchAttrRange ) ); + s_pReplaceList->Get( *pReplSet ); + + // -- Page break with page template + ::SfxToSwPageDescAttr( *m_pWrtShell, *pReplSet ); + + if( !pReplSet->Count() ) // too bad, we don't know + pReplSet.reset(); // the attributes + } + + // build SearchOptions to be used + + i18nutil::SearchOptions2 aSearchOpt( s_pSrchItem->GetSearchOptions() ); + aSearchOpt.Locale = GetAppLanguageTag().getLocale(); + if( !bDoReplace ) + aSearchOpt.replaceString.clear(); + + sal_uLong nFound; + if( aSrchSet.Count() || ( pReplSet && pReplSet->Count() )) + { + nFound = m_pWrtShell->SearchAttr( + aSrchSet, + !s_pSrchItem->GetPattern(), + rOptions.eStart, + rOptions.eEnd, + eRanges, + !s_pSrchItem->GetSearchString().isEmpty() ? &aSearchOpt : nullptr, + pReplSet.get() ); + } + else if( s_pSrchItem->GetPattern() ) + { + // Searching (and replacing) templates + const OUString& sRplStr( s_pSrchItem->GetReplaceString() ); + nFound = m_pWrtShell->SearchTempl( s_pSrchItem->GetSearchString(), + rOptions.eStart, + rOptions.eEnd, + eRanges, + bDoReplace ? &sRplStr : nullptr ); + } + else + { + // Normal search + nFound = m_pWrtShell->SearchPattern(aSearchOpt, s_pSrchItem->GetNotes(), + rOptions.eStart, + rOptions.eEnd, + eRanges, + bDoReplace ); + } + m_pWrtShell->EndSelect(); + return nFound; +} + +SvxSearchDialog* SwView::GetSearchDialog() +{ +#if HAVE_FEATURE_DESKTOP + const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId(); + SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( SfxViewFrame::Current()->GetChildWindow(nId) ); + auto pSrchDlg = pWrp ? pWrp->getDialog() : nullptr; + return pSrchDlg; +#else + return nullptr; +#endif +} + +void SwView::StateSearch(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + switch(nWhich) + { + case SID_SEARCH_OPTIONS: + { + SearchOptionFlags nOpt = SearchOptionFlags::ALL; + if( GetDocShell()->IsReadOnly() ) + nOpt &= ~SearchOptionFlags( SearchOptionFlags::REPLACE | + SearchOptionFlags::REPLACE_ALL ); + rSet.Put( SfxUInt16Item( SID_SEARCH_OPTIONS, static_cast<sal_uInt16>(nOpt) )); + } + break; + case SID_SEARCH_ITEM: + { + if ( !s_pSrchItem ) + { + s_pSrchItem = new SvxSearchItem( SID_SEARCH_ITEM ); + s_pSrchItem->SetFamily(SfxStyleFamily::Para); + s_pSrchItem->SetSearchString( m_pWrtShell->GetSelText() ); + } + + if( s_bJustOpened && m_pWrtShell->IsSelection() ) + { + OUString aText; + if( 1 == m_pWrtShell->GetCursorCnt() && + !( aText = m_pWrtShell->SwCursorShell::GetSelText() ).isEmpty() ) + { + s_pSrchItem->SetSearchString( aText ); + s_pSrchItem->SetSelection( false ); + } + else + s_pSrchItem->SetSelection( true ); + } + + s_bJustOpened = false; + rSet.Put( *s_pSrchItem ); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewstat.cxx b/sw/source/uibase/uiview/viewstat.cxx new file mode 100644 index 000000000..caa3bd34a --- /dev/null +++ b/sw/source/uibase/uiview/viewstat.cxx @@ -0,0 +1,603 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/linguistic2/XThesaurus.hpp> +#include <svl/whiter.hxx> +#include <svl/cjkoptions.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/imageitm.hxx> +#include <svl/languageoptions.hxx> +#include <sfx2/linkmgr.hxx> +#include <editeng/langitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/tstpitem.hxx> +#include <sfx2/htmlmode.hxx> +#include <swmodule.hxx> +#include <tox.hxx> +#include <sfx2/dispatch.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <uitool.hxx> +#include <viewopt.hxx> +#include <pagedesc.hxx> +#include <wview.hxx> +#include <globdoc.hxx> +#include <svl/stritem.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <svl/visitem.hxx> +#include <redline.hxx> +#include <rootfrm.hxx> +#include <docary.hxx> + +#include <cmdid.h> +#include <IDocumentRedlineAccess.hxx> + +#include <doc.hxx> + +using namespace ::com::sun::star; + +void SwView::GetState(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + FrameTypeFlags eFrameType = FrameTypeFlags::NONE; + bool bGetFrameType = false; + bool bWeb = dynamic_cast<SwWebView*>( this ) != nullptr; + + while(nWhich) + { + switch(nWhich) + { + case FN_NAV_ELEMENT: + // used to update all instances of this control + rSet.InvalidateItem( nWhich ); + break; + case FN_EDIT_LINK_DLG: + if( m_pWrtShell->GetLinkManager().GetLinks().empty() ) + rSet.DisableItem(nWhich); + else if( m_pWrtShell->IsSelFrameMode() && + m_pWrtShell->IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE) + { + rSet.DisableItem(nWhich); + } + break; + + case SID_DRAWTBX_LINES: + if ( bWeb ) + rSet.DisableItem(nWhich); + break; + + case SID_INSERT_GRAPHIC: + if( m_pWrtShell->CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + break; + case SID_INSERT_SIGNATURELINE: + if( !( m_nSelectionType & SelectionType::Text || + m_nSelectionType & SelectionType::NumberList ) ) + { + rSet.DisableItem(nWhich); + } + break; + case SID_EDIT_SIGNATURELINE: + case SID_SIGN_SIGNATURELINE: + if (!isSignatureLineSelected() || isSignatureLineSigned()) + rSet.DisableItem(nWhich); + break; + case SID_INSERT_QRCODE: + if( !( m_nSelectionType & SelectionType::Text || + m_nSelectionType & SelectionType::NumberList ) ) + { + rSet.DisableItem(nWhich); + } + break; + case SID_EDIT_QRCODE: + if (!isQRCodeSelected()) + rSet.DisableItem(nWhich); + break; + case FN_INSERT_CAPTION: + { + // There are captions for graphics, OLE objects, frames and tables + if( !bGetFrameType ) + { + eFrameType = m_pWrtShell->GetFrameType(nullptr, true); + bGetFrameType = true; + } + if (! ( ((eFrameType & FrameTypeFlags::FLY_ANY) && m_nSelectionType != SelectionType::DrawObjectEditMode)|| + m_nSelectionType & SelectionType::Table || + m_nSelectionType & SelectionType::DrawObject) ) + { + rSet.DisableItem(nWhich); + } + else if((m_pWrtShell->IsObjSelected() || m_pWrtShell->IsFrameSelected()) && + (m_pWrtShell->IsSelObjProtected( FlyProtectFlags::Parent) != FlyProtectFlags::NONE || + m_pWrtShell->IsSelObjProtected( FlyProtectFlags::Content ) != FlyProtectFlags::NONE)) + { + rSet.DisableItem(nWhich); + } + else if( m_pWrtShell->IsTableMode() + || isSignatureLineSelected() + || m_pWrtShell->CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + } + break; + + case FN_EDIT_FOOTNOTE: + { + if( !m_pWrtShell->GetCurFootnote() ) + rSet.DisableItem(nWhich); + } + break; + + case FN_CHANGE_PAGENUM: + { + FrameTypeFlags nType = m_pWrtShell->GetFrameType(nullptr,true); + if( ( FrameTypeFlags::FLY_ANY | FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | + FrameTypeFlags::FOOTNOTE | FrameTypeFlags::DRAWOBJ ) & nType ) + rSet.DisableItem(nWhich); + else + rSet.Put(SfxUInt16Item(nWhich, m_pWrtShell->GetPageOffset())); + } + break; + case SID_PRINTDOC: + case SID_PRINTDOCDIRECT: + GetSlotState( nWhich, SfxViewShell::GetInterface(), &rSet ); + break; + case SID_ATTR_PAGE_ORIENTATION: + case SID_ATTR_PAGE: + case SID_ATTR_PAGE_SIZE: + case SID_ATTR_PAGE_PAPERBIN: + case RES_PAPER_BIN: + case FN_PARAM_FTN_INFO: + { + const size_t nCurIdx = m_pWrtShell->GetCurPageDesc(); + const SwPageDesc& rDesc = m_pWrtShell->GetPageDesc( nCurIdx ); + + // set correct parent to get the XFILL_NONE FillStyle as needed + if(!rSet.GetParent()) + { + const SwFrameFormat& rMaster = rDesc.GetMaster(); + + rSet.SetParent(&rMaster.GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + } + + ::PageDescToItemSet( rDesc, rSet); + + if (nWhich == SID_ATTR_PAGE_ORIENTATION && comphelper::LibreOfficeKit::isActive()) + { + OString aPayload = ".uno:Orientation="; + if (rDesc.GetLandscape()) + { + aPayload += "IsLandscape"; + } + else + { + aPayload += "IsPortrait"; + } + libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aPayload.getStr()); + } + } + break; + case RES_BACKGROUND: + case SID_ATTR_BRUSH: + { + const size_t nCurIdx = m_pWrtShell->GetCurPageDesc(); + const SwPageDesc& rDesc = m_pWrtShell->GetPageDesc( nCurIdx ); + const SwFrameFormat& rMaster = rDesc.GetMaster(); + const SvxBrushItem& rBrush = rMaster.GetFormatAttr(RES_BACKGROUND); + rSet.Put(rBrush); + } + break; + case SID_CLEARHISTORY: + { + rSet.Put(SfxBoolItem(nWhich, m_pWrtShell->GetLastUndoInfo(nullptr, nullptr))); + } + break; + case SID_UNDO: + { + // which must not be present, so let them create: + if( !m_pShell ) + SelectShell(); + + const SfxPoolItem* pState = m_pShell->GetSlotState(SID_UNDO); + if(pState) + rSet.Put(*pState); + else + rSet.DisableItem(nWhich); + } + break; + case FN_INSERT_OBJ_CTRL: + if( bWeb + || m_pWrtShell->CursorInsideInputField() ) + { + rSet.DisableItem(nWhich); + } + break; + + case FN_UPDATE_TOX: + if(!m_pWrtShell->GetTOXCount()) + rSet.DisableItem(nWhich); + break; + case FN_EDIT_CURRENT_TOX: + case FN_UPDATE_CUR_TOX: + { + const SwTOXBase* pBase = nullptr; + if(nullptr == (pBase = m_pWrtShell->GetCurTOX()) || + (FN_EDIT_CURRENT_TOX == nWhich && pBase->IsTOXBaseInReadonly())) + rSet.DisableItem(nWhich); + } + break; + case SID_TWAIN_SELECT: + case SID_TWAIN_TRANSFER: +#if defined(_WIN32) || defined UNX + { + if(!SW_MOD()->GetScannerManager().is()) + rSet.DisableItem(nWhich); + } +#endif + break; + case RES_PARATR_TABSTOP: + case SID_ATTR_DEFTABSTOP: + { + const SvxTabStopItem& rDefTabs = m_pWrtShell->GetDefault(RES_PARATR_TABSTOP); + rSet.Put( SfxUInt16Item( nWhich, + static_cast<sal_uInt16>(::GetTabDist(rDefTabs)))); + } + break; + case SID_ATTR_LANGUAGE: + { + rSet.Put(m_pWrtShell->GetDefault(RES_CHRATR_LANGUAGE).CloneSetWhich(SID_ATTR_LANGUAGE)); + } + break; + case RES_CHRATR_CJK_LANGUAGE: + { + rSet.Put(m_pWrtShell->GetDefault(RES_CHRATR_CJK_LANGUAGE) + .CloneSetWhich(RES_CHRATR_CJK_LANGUAGE)); + } + break; + case RES_CHRATR_CTL_LANGUAGE: + { + rSet.Put(m_pWrtShell->GetDefault(RES_CHRATR_CTL_LANGUAGE) + .CloneSetWhich(RES_CHRATR_CTL_LANGUAGE)); + } + break; + case FN_REDLINE_ON: + rSet.Put( SfxBoolItem( nWhich, GetDocShell()->IsChangeRecording() ) ); + break; + case FN_REDLINE_PROTECT : + rSet.Put( SfxBoolItem( nWhich, GetDocShell()->HasChangeRecordProtection() ) ); + break; + case FN_REDLINE_SHOW: + { + rSet.Put(SfxBoolItem(nWhich, !m_pWrtShell->GetLayout()->IsHideRedlines())); + } + break; + case SID_AVMEDIA_PLAYER : + case FN_REDLINE_ACCEPT : + { + SfxViewFrame* pVFrame = GetViewFrame(); + if (pVFrame->KnowsChildWindow(nWhich)) + rSet.Put(SfxBoolItem( nWhich, pVFrame->HasChildWindow(nWhich))); + else + rSet.DisableItem(nWhich); + } + break; + case FN_REDLINE_ACCEPT_DIRECT: + case FN_REDLINE_REJECT_DIRECT: + case FN_REDLINE_ACCEPT_TONEXT: + case FN_REDLINE_REJECT_TONEXT: + { + SwDoc *pDoc = m_pWrtShell->GetDoc(); + SwPaM *pCursor = m_pWrtShell->GetCursor(); + bool bDisable = false; + if (GetDocShell()->HasChangeRecordProtection()) + bDisable = true; + else if (pCursor->HasMark()) + { + // If the selection does not contain redlines, disable accepting/rejecting changes. + SwRedlineTable::size_type index = 0; + const SwRedlineTable& table = pDoc->getIDocumentRedlineAccess().GetRedlineTable(); + const SwRangeRedline* redline = table.FindAtPosition( *pCursor->Start(), index ); + if( redline != nullptr && *redline->Start() == *pCursor->End()) + redline = nullptr; + if( redline == nullptr ) + { + for(; index < table.size(); ++index ) + { + const SwRangeRedline* tmp = table[ index ]; + if( *tmp->Start() >= *pCursor->End()) + break; + if( tmp->HasMark() && tmp->IsVisible()) + { + redline = tmp; + break; + } + } + } + if( redline == nullptr ) + bDisable = true; + } + else + { + // If the cursor position isn't on a redline, disable + // accepting/rejecting changes. + if (nullptr == pDoc->getIDocumentRedlineAccess().GetRedline(*pCursor->Start(), nullptr)) + bDisable = true; + } + + // LibreOfficeKit wants to handle changes by index, so always allow here. + if (bDisable) + rSet.DisableItem(nWhich); + if (comphelper::LibreOfficeKit::isActive()) + { + OString aPayload(".uno:TrackedChangeIndex="); + SwRedlineTable::size_type nRedline = 0; + if (pDoc->getIDocumentRedlineAccess().GetRedline(*pCursor->Start(), &nRedline)) + aPayload += OString::number(nRedline); + libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, aPayload.getStr()); + } + } + break; + + case FN_REDLINE_NEXT_CHANGE: + case FN_REDLINE_PREV_CHANGE: + { + // Enable change navigation if we have any redlines. Ideally we should disable + // "Next Change" if we're at or past the last change, and similarly for + // "Previous Change" + if (0 == m_pWrtShell->GetRedlineCount()) + rSet.DisableItem(nWhich); + } + break; + + case SID_THESAURUS: + { + SwWrtShell &rSh = GetWrtShell(); + if (2 <= rSh.GetCursorCnt()) // multi selection? + rSet.DisableItem(nWhich); + else + { + LanguageType nLang = rSh.GetCurLang(); + + // disable "Thesaurus" (menu entry and key shortcut) if the + // language is not supported (by default it is enabled) + uno::Reference< linguistic2::XThesaurus > xThes( ::GetThesaurus() ); + if (!xThes.is() || nLang == LANGUAGE_NONE || + !xThes->hasLocale( LanguageTag::convertToLocale( nLang ) )) + rSet.DisableItem(nWhich); + } + } + break; + case SID_HANGUL_HANJA_CONVERSION: + case SID_CHINESE_CONVERSION: + { + if (!SvtCJKOptions().IsAnyEnabled()) + { + GetViewFrame()->GetBindings().SetVisibleState( nWhich, false ); + rSet.DisableItem(nWhich); + } + else + GetViewFrame()->GetBindings().SetVisibleState( nWhich, true ); + } + break; + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + const long nBottom = m_pWrtShell->GetDocSize().Height() + DOCUMENTBORDER; + const long nAct = GetVisArea().Bottom(); + rSet.Put(SfxBoolItem(SID_MAIL_SCROLLBODY_PAGEDOWN, nAct < nBottom )); + } + break; + + case SID_DOCUMENT_COMPARE: + case SID_DOCUMENT_MERGE: + if( dynamic_cast<const SwGlobalDocShell* >(GetDocShell()) != nullptr|| + (SID_DOCUMENT_MERGE == nWhich && m_pWrtShell->getIDocumentRedlineAccess().GetRedlinePassword().hasElements())) + rSet.DisableItem(nWhich); + break; + case SID_VIEW_DATA_SOURCE_BROWSER: + if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + rSet.Put( SfxVisibilityItem( nWhich, false ) ); + else + rSet.Put( SfxBoolItem( nWhich, GetViewFrame()->HasChildWindow( SID_BROWSER ) ) ); + break; + case SID_READONLY_MODE: + rSet.Put(SfxBoolItem(nWhich, + m_pWrtShell->HasReadonlySel()||GetDocShell()->IsReadOnly())); + break; + case SID_IMAGE_ORIENTATION: + { + SfxImageItem aImageItem(nWhich); + if(m_pWrtShell->IsInVerticalText()) + aImageItem.SetRotation( 2700 ); + if(m_pWrtShell->IsInRightToLeftText()) + aImageItem.SetMirrored( true ); + rSet.Put(aImageItem); + } + break; + case FN_INSERT_FIELD_DATA_ONLY : + if(!m_bInMailMerge && !GetViewFrame()->HasChildWindow(nWhich)) + rSet.DisableItem(nWhich); + break; + case FN_MAILMERGE_SENDMAIL_CHILDWINDOW: + break; + case SID_ALIGN_ANY_LEFT : + case SID_ALIGN_ANY_HCENTER : + case SID_ALIGN_ANY_RIGHT : + case SID_ALIGN_ANY_JUSTIFIED: + case SID_ALIGN_ANY_TOP : + case SID_ALIGN_ANY_VCENTER : + case SID_ALIGN_ANY_BOTTOM : + case SID_ALIGN_ANY_HDEFAULT : + case SID_ALIGN_ANY_VDEFAULT : + { + if( !m_pShell ) + SelectShell(); + sal_uInt16 nAlias = 0; + if( m_nSelectionType & (SelectionType::DrawObjectEditMode|SelectionType::Text) ) + { + switch( nWhich ) + { + case SID_ALIGN_ANY_LEFT : nAlias = SID_ATTR_PARA_ADJUST_LEFT; break; + case SID_ALIGN_ANY_HCENTER : nAlias = SID_ATTR_PARA_ADJUST_CENTER; break; + case SID_ALIGN_ANY_RIGHT : nAlias = SID_ATTR_PARA_ADJUST_RIGHT; break; + case SID_ALIGN_ANY_JUSTIFIED: nAlias = SID_ATTR_PARA_ADJUST_BLOCK; break; + case SID_ALIGN_ANY_TOP : nAlias = SID_TABLE_VERT_NONE; break; + case SID_ALIGN_ANY_VCENTER : nAlias = SID_TABLE_VERT_CENTER; break; + case SID_ALIGN_ANY_BOTTOM : nAlias = SID_TABLE_VERT_BOTTOM; break; + } + } + else + { + switch( nWhich ) + { + case SID_ALIGN_ANY_LEFT : nAlias = SID_OBJECT_ALIGN_LEFT ; break; + case SID_ALIGN_ANY_HCENTER : nAlias = SID_OBJECT_ALIGN_CENTER ; break; + case SID_ALIGN_ANY_RIGHT : nAlias = SID_OBJECT_ALIGN_RIGHT ; break; + case SID_ALIGN_ANY_TOP : nAlias = SID_OBJECT_ALIGN_UP ; break; + case SID_ALIGN_ANY_VCENTER : nAlias = SID_OBJECT_ALIGN_MIDDLE ; break; + case SID_ALIGN_ANY_BOTTOM : nAlias = SID_OBJECT_ALIGN_DOWN ; break; + } + } + //these slots are either re-mapped to text or object alignment + const SfxPoolItem* pState = nullptr; + if(nAlias) + GetViewFrame()->GetDispatcher()->QueryState( nAlias, pState ); + if(pState) + { + if (!(m_nSelectionType & SelectionType::DrawObject)) + { + rSet.Put(pState->CloneSetWhich(nWhich)); + } + } + else + rSet.DisableItem(nWhich); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SwView::GetDrawState(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + bool bWeb = dynamic_cast<SwWebView*>( this ) != nullptr; + + for( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; + nWhich = aIter.NextWhich() ) + switch(nWhich) + { + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_LINE_ARROW_END: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_ARROW_START: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + case SID_LINE_ARROWS: + case SID_DRAW_MEASURELINE: + case SID_DRAW_RECT: + case SID_DRAW_ELLIPSE: + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_XPOLYGON: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_POLYGON: + case SID_DRAW_BEZIER_NOFILL: + case SID_DRAW_BEZIER_FILL: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_FREELINE: + case SID_DRAW_ARC: + case SID_DRAW_PIE: + case SID_DRAW_CIRCLECUT: + case SID_DRAW_TEXT: + case SID_DRAW_CAPTION: + if ( bWeb ) + rSet.DisableItem( nWhich ); + else + rSet.Put( SfxBoolItem( nWhich, m_nDrawSfxId == nWhich ) ); + break; + + case SID_DRAW_TEXT_VERTICAL: + case SID_DRAW_CAPTION_VERTICAL: + if ( bWeb || !SvtLanguageOptions().IsVerticalTextEnabled() ) + rSet.DisableItem( nWhich ); + else + rSet.Put( SfxBoolItem( nWhich, m_nDrawSfxId == nWhich ) ); + break; + + case SID_DRAW_TEXT_MARQUEE: + if (::GetHtmlMode(GetDocShell()) & HTMLMODE_SOME_STYLES) + rSet.Put( SfxBoolItem(nWhich, m_nDrawSfxId == nWhich)); + else + rSet.DisableItem(nWhich); + break; + case SID_OBJECT_SELECT: + rSet.Put( SfxBoolItem(nWhich, m_nDrawSfxId == nWhich || + m_nFormSfxId == nWhich)); + break; + + case SID_INSERT_DRAW: + case SID_FONTWORK_GALLERY_FLOATER : + case SID_DRAWTBX_ARROWS: + { + if ( bWeb ) + rSet.DisableItem( nWhich ); + } + break; + + case SID_DRAWTBX_CS_BASIC : + case SID_DRAWTBX_CS_SYMBOL : + case SID_DRAWTBX_CS_ARROW : + case SID_DRAWTBX_CS_FLOWCHART : + case SID_DRAWTBX_CS_CALLOUT : + case SID_DRAWTBX_CS_STAR : + { + if ( bWeb ) + rSet.DisableItem( nWhich ); + else + rSet.Put( SfxStringItem( nWhich, m_nDrawSfxId == nWhich ? m_sDrawCustom : OUString() ) ); + } + break; + + } +} + +bool SwView::HasUIFeature(SfxShellFeature nFeature) const +{ + assert((nFeature & ~SfxShellFeature::SwMask) == SfxShellFeature::NONE); + switch(nFeature) + { + case SfxShellFeature::SwChildWindowLabel: + return m_pWrtShell->IsLabelDoc(); + default: + return false; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uiview/viewtab.cxx b/sw/source/uibase/uiview/viewtab.cxx new file mode 100644 index 000000000..b7f6e02dc --- /dev/null +++ b/sw/source/uibase/uiview/viewtab.cxx @@ -0,0 +1,2532 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <uitool.hxx> +#include <svx/rulritem.hxx> +#include <svx/xflclit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xfillit0.hxx> +#include <editeng/tstpitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svl/eitem.hxx> +#include <svl/whiter.hxx> +#include <svx/ruler.hxx> +#include <editeng/protitem.hxx> +#include <svl/rectitem.hxx> +#include <sfx2/bindings.hxx> +#include <fmtfsize.hxx> +#include <fmthdft.hxx> +#include <fmtclds.hxx> +#include <fmtornt.hxx> +#include <frmatr.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <viewopt.hxx> +#include <tabcol.hxx> +#include <frmfmt.hxx> +#include <pagedesc.hxx> +#include <wview.hxx> +#include <fmtcol.hxx> +#include <section.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <comphelper/lok.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <boost/property_tree/json_parser.hpp> + +#include <IDocumentSettingAccess.hxx> + +using namespace ::com::sun::star; + +// Pack columns +static void lcl_FillSvxColumn(const SwFormatCol& rCol, + long nTotalWidth, + SvxColumnItem& rColItem, + long nDistance) +{ + const SwColumns& rCols = rCol.GetColumns(); + + bool bOrtho = rCol.IsOrtho() && !rCols.empty(); + long nInnerWidth = 0; + if( bOrtho ) + { + nInnerWidth = nTotalWidth; + for (const auto & i : rCols) + { + nInnerWidth -= i.GetLeft() + i.GetRight(); + } + if( nInnerWidth < 0 ) + nInnerWidth = 0; + else + nInnerWidth /= rCols.size(); + } + + long nWidth = 0; + for ( size_t i = 0; i < rCols.size(); ++i ) + { + const SwColumn* pCol = &rCols[i]; + const long nStart = pCol->GetLeft() + nWidth + nDistance; + if( bOrtho ) + nWidth += nInnerWidth + pCol->GetLeft() + pCol->GetRight(); + else + nWidth += rCol.CalcColWidth(i, static_cast< sal_uInt16 >(nTotalWidth)); + const long nEnd = nWidth - pCol->GetRight() + nDistance; + + SvxColumnDescription aColDesc(nStart, nEnd, true); + rColItem.Append(aColDesc); + } +} + +// Transfer ColumnItem in ColumnInfo +static void lcl_ConvertToCols(const SvxColumnItem& rColItem, + long nTotalWidth, + SwFormatCol& rCols) +{ + OSL_ENSURE( rCols.GetNumCols() == rColItem.Count(), "Column count mismatch" ); + // ruler executes that change the columns shortly after the selection has changed + // can result in a crash + if(rCols.GetNumCols() != rColItem.Count()) + return; + + sal_uInt16 nLeft = 0; + SwTwips nSumAll= 0; // Sum up all columns and margins + + SwColumns& rArr = rCols.GetColumns(); + + // Tabcols sequentially + for( sal_uInt16 i=0; i < rColItem.Count()-1; ++i ) + { + OSL_ENSURE(rColItem[i+1].nStart >= rColItem[i].nEnd,"overlapping columns" ); + const long nStart = std::max(rColItem[i+1].nStart, rColItem[i].nEnd); + const sal_uInt16 nRight = static_cast<sal_uInt16>((nStart - rColItem[i].nEnd) / 2); + + const long nWidth = rColItem[i].nEnd - rColItem[i].nStart + nLeft + nRight; + + SwColumn* pCol = &rArr[i]; + pCol->SetWishWidth( sal_uInt16(long(rCols.GetWishWidth()) * nWidth / nTotalWidth )); + pCol->SetLeft( nLeft ); + pCol->SetRight( nRight ); + nSumAll += pCol->GetWishWidth(); + + nLeft = nRight; + } + rArr[rColItem.Count()-1].SetLeft( nLeft ); + + // The difference between the total sum of the desired width and the so far + // calculated columns and margins should result in the width of the last column. + rArr[rColItem.Count()-1].SetWishWidth( rCols.GetWishWidth() - static_cast<sal_uInt16>(nSumAll) ); + + rCols.SetOrtho(false, 0, 0 ); +} + +// Delete tabs +static void lcl_EraseDefTabs(SvxTabStopItem& rTabStops) +{ + // Delete DefTabs + for ( sal_uInt16 i = 0; i < rTabStops.Count(); ) + { + // Here also throw out the DefTab to zero + if ( SvxTabAdjust::Default == rTabStops[i].GetAdjustment() || + rTabStops[i].GetTabPos() == 0 ) + { + rTabStops.Remove(i); + continue; + } + ++i; + } +} + +// Flip page margin +void SwView::SwapPageMargin(const SwPageDesc& rDesc, SvxLRSpaceItem& rLRSpace) +{ + sal_uInt16 nPhyPage, nVirPage; + GetWrtShell().GetPageNum( nPhyPage, nVirPage ); + + if ( rDesc.GetUseOn() == UseOnPage::Mirror && (nPhyPage % 2) == 0 ) + { + long nTmp = rLRSpace.GetRight(); + rLRSpace.SetRight( rLRSpace.GetLeft() ); + rLRSpace.SetLeft( nTmp ); + } +} + +// If the frame border is moved, the column separator +// should stay in the same absolute position. +static void lcl_Scale(long& nVal, long nScale) +{ + nVal *= nScale; + nVal >>= 8; +} + +static void ResizeFrameCols(SwFormatCol& rCol, + long nOldWidth, + long nNewWidth, + long nLeftDelta ) +{ + SwColumns& rArr = rCol.GetColumns(); + long nWishSum = static_cast<long>(rCol.GetWishWidth()); + long nWishDiff = (nWishSum * 100/nOldWidth * nNewWidth) / 100 - nWishSum; + long nNewWishWidth = nWishSum + nWishDiff; + if(nNewWishWidth > 0xffffl) + { + // If the desired width is getting too large, then all values + // must be scaled appropriately. + long nScale = (0xffffl << 8)/ nNewWishWidth; + for(SwColumn & i : rArr) + { + SwColumn* pCol = &i; + long nVal = pCol->GetWishWidth(); + lcl_Scale(nVal, nScale); + pCol->SetWishWidth(static_cast<sal_uInt16>(nVal)); + nVal = pCol->GetLeft(); + lcl_Scale(nVal, nScale); + pCol->SetLeft(static_cast<sal_uInt16>(nVal)); + nVal = pCol->GetRight(); + lcl_Scale(nVal, nScale); + pCol->SetRight(static_cast<sal_uInt16>(nVal)); + } + lcl_Scale(nNewWishWidth, nScale); + lcl_Scale(nWishDiff, nScale); + } + rCol.SetWishWidth( static_cast<sal_uInt16>(nNewWishWidth) ); + + if( nLeftDelta >= 2 || nLeftDelta <= -2) + rArr.front().SetWishWidth(rArr.front().GetWishWidth() + static_cast<sal_uInt16>(nWishDiff)); + else + rArr.back().SetWishWidth(rArr.back().GetWishWidth() + static_cast<sal_uInt16>(nWishDiff)); + // Reset auto width + rCol.SetOrtho(false, 0, 0 ); +} + +// Here all changes to the tab bar will be shot again into the model. +void SwView::ExecTabWin( SfxRequest const & rReq ) +{ + SwWrtShell &rSh = GetWrtShell(); + const FrameTypeFlags nFrameType = rSh.IsObjSelected() ? + FrameTypeFlags::DRAWOBJ : + rSh.GetFrameType(nullptr,true); + const bool bFrameSelection = rSh.IsFrameSelected(); + const bool bBrowse = rSh.GetViewOptions()->getBrowseMode(); + + const sal_uInt16 nSlot = rReq.GetSlot(); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + const size_t nDescId = rSh.GetCurPageDesc(); + const SwPageDesc& rDesc = rSh.GetPageDesc( nDescId ); + + const bool bVerticalWriting = rSh.IsInVerticalText(); + const SwFormatHeader& rHeaderFormat = rDesc.GetMaster().GetHeader(); + SwFrameFormat *pHeaderFormat = const_cast<SwFrameFormat*>(rHeaderFormat.GetHeaderFormat()); + + const SwFormatFooter& rFooterFormat = rDesc.GetMaster().GetFooter(); + SwFrameFormat *pFooterFormat = const_cast<SwFrameFormat*>(rFooterFormat.GetFooterFormat()); + + const SwFormatFrameSize &rFrameSize = rDesc.GetMaster().GetFrameSize(); + + const SwRect& rPageRect = rSh.GetAnyCurRect(CurRectType::Page); + const long nPageWidth = bBrowse ? rPageRect.Width() : rFrameSize.GetWidth(); + const long nPageHeight = bBrowse ? rPageRect.Height() : rFrameSize.GetHeight(); + + bool bUnlockView = false; + rSh.StartAllAction(); + bool bSect = bool(nFrameType & FrameTypeFlags::COLSECT); + + switch (nSlot) + { + case SID_ATTR_LONG_LRSPACE: + if ( pReqArgs ) + { + SvxLongLRSpaceItem aLongLR( static_cast<const SvxLongLRSpaceItem&>(pReqArgs-> + Get( SID_ATTR_LONG_LRSPACE )) ); + SvxLRSpaceItem aLR(RES_LR_SPACE); + if ( !bSect && (bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY) ) + { + SwFrameFormat* pFormat = rSh.GetFlyFrameFormat(); + const SwRect &rRect = rSh.GetAnyCurRect(CurRectType::FlyEmbedded); + + bool bVerticalFrame(false); + { + bool bRTL; + bool bVertL2R; + bVerticalFrame = ( bFrameSelection && + rSh.IsFrameVertical(true, bRTL, bVertL2R) ) || + ( !bFrameSelection && bVerticalWriting); + } + long nDeltaX = bVerticalFrame ? + rRect.Right() - rPageRect.Right() + aLongLR.GetRight() : + rPageRect.Left() + aLongLR.GetLeft() - rRect.Left(); + + SfxItemSet aSet( GetPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, + RES_VERT_ORIENT, RES_HORI_ORIENT, + RES_COL, RES_COL>{} ); + + if(bVerticalFrame) + { + SwFormatVertOrient aVertOrient(pFormat->GetVertOrient()); + aVertOrient.SetVertOrient(text::VertOrientation::NONE); + aVertOrient.SetPos(aVertOrient.GetPos() + nDeltaX ); + aSet.Put( aVertOrient ); + } + else + { + SwFormatHoriOrient aHoriOrient( pFormat->GetHoriOrient() ); + aHoriOrient.SetHoriOrient( text::HoriOrientation::NONE ); + aHoriOrient.SetPos( aHoriOrient.GetPos() + nDeltaX ); + aSet.Put( aHoriOrient ); + } + + SwFormatFrameSize aSize( pFormat->GetFrameSize() ); + long nOldWidth = aSize.GetWidth(); + + if(aSize.GetWidthPercent()) + { + SwRect aRect; + rSh.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + long nPrtWidth = aRect.Width(); + aSize.SetWidthPercent(sal_uInt8((nPageWidth - aLongLR.GetLeft() - aLongLR.GetRight()) * 100 /nPrtWidth)); + } + else + aSize.SetWidth( nPageWidth - + (aLongLR.GetLeft() + aLongLR.GetRight())); + + if( nFrameType & FrameTypeFlags::COLUMN ) + { + SwFormatCol aCol(pFormat->GetCol()); + + ::ResizeFrameCols(aCol, nOldWidth, aSize.GetWidth(), nDeltaX ); + aSet.Put(aCol); + } + + aSet.Put( aSize ); + + rSh.StartAction(); + rSh.Push(); + rSh.SetFlyFrameAttr( aSet ); + // Cancel the frame selection + if(!bFrameSelection && rSh.IsFrameSelected()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + rSh.Pop(); + rSh.EndAction(); + } + else if ( nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER )) + { + // Subtract out page margins + long nOld = rDesc.GetMaster().GetLRSpace().GetLeft(); + aLongLR.SetLeft( nOld > aLongLR.GetLeft() ? 0 : aLongLR.GetLeft() - nOld ); + + nOld = rDesc.GetMaster().GetLRSpace().GetRight(); + aLongLR.SetRight( nOld > aLongLR.GetRight() ? 0 : aLongLR.GetRight() - nOld ); + aLR.SetLeft(aLongLR.GetLeft()); + aLR.SetRight(aLongLR.GetRight()); + + if ( nFrameType & FrameTypeFlags::HEADER && pHeaderFormat ) + pHeaderFormat->SetFormatAttr( aLR ); + else if( nFrameType & FrameTypeFlags::FOOTER && pFooterFormat ) + pFooterFormat->SetFormatAttr( aLR ); + } + else if( nFrameType == FrameTypeFlags::DRAWOBJ) + { + SwRect aRect( rSh.GetObjRect() ); + aRect.Left( aLongLR.GetLeft() + rPageRect.Left() ); + aRect.Right( rPageRect.Right() - aLongLR.GetRight()); + rSh.SetObjRect( aRect ); + } + else if(bSect || rSh.IsDirectlyInSection()) + { + //change the section indents and the columns if available + //at first determine the changes + SwRect aSectRect = rSh.GetAnyCurRect(CurRectType::SectionPrt); + const SwRect aTmpRect = rSh.GetAnyCurRect(CurRectType::Section); + aSectRect.Pos() += aTmpRect.Pos(); + long nLeftDiff = aLongLR.GetLeft() - static_cast<long>(aSectRect.Left() - rPageRect.Left() ); + long nRightDiff = aLongLR.GetRight() - static_cast<long>( rPageRect.Right() - aSectRect.Right()); + //change the LRSpaceItem of the section accordingly + const SwSection* pCurrSect = rSh.GetCurrSection(); + const SwSectionFormat* pSectFormat = pCurrSect->GetFormat(); + SvxLRSpaceItem aLRTmp = pSectFormat->GetLRSpace(); + aLRTmp.SetLeft(aLRTmp.GetLeft() + nLeftDiff); + aLRTmp.SetRight(aLRTmp.GetRight() + nRightDiff); + SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE, RES_COL, RES_COL>{}); + aSet.Put(aLRTmp); + //change the first/last column + if(bSect) + { + SwFormatCol aCols( pSectFormat->GetCol() ); + long nDiffWidth = nLeftDiff + nRightDiff; + ::ResizeFrameCols(aCols, aSectRect.Width(), aSectRect.Width() - nDiffWidth, nLeftDiff ); + aSet.Put( aCols ); + } + SwSectionData aData(*pCurrSect); + rSh.UpdateSection(rSh.GetSectionFormatPos(*pSectFormat), aData, &aSet); + } + else + { // Adjust page margins + aLR.SetLeft(aLongLR.GetLeft()); + aLR.SetRight(aLongLR.GetRight()); + SwapPageMargin( rDesc, aLR ); + SwPageDesc aDesc( rDesc ); + aDesc.GetMaster().SetFormatAttr( aLR ); + rSh.ChgPageDesc( nDescId, aDesc ); + } + } + break; + + // apply new left and right margins to current page style + case SID_ATTR_PAGE_LRSPACE: + if ( pReqArgs ) + { + const SvxLongLRSpaceItem& aLongLR( pReqArgs->Get( SID_ATTR_PAGE_LRSPACE ) ); + + SwPageDesc aDesc( rDesc ); + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + aLR.SetLeft(aLongLR.GetLeft()); + aLR.SetRight(aLongLR.GetRight()); + SwapPageMargin( rDesc, aLR ); + aDesc.GetMaster().SetFormatAttr( aLR ); + } + rSh.ChgPageDesc( nDescId, aDesc ); + } + break; + + case SID_ATTR_LONG_ULSPACE: + if ( pReqArgs ) + { + SvxLongULSpaceItem aLongULSpace( static_cast<const SvxLongULSpaceItem&>(pReqArgs-> + Get( SID_ATTR_LONG_ULSPACE ))); + + if( bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY ) + { + SwFrameFormat* pFormat = rSh.GetFlyFrameFormat(); + const SwRect &rRect = rSh.GetAnyCurRect(CurRectType::FlyEmbedded); + const long nDeltaY = rPageRect.Top() + aLongULSpace.GetUpper() - rRect.Top(); + const long nHeight = nPageHeight - (aLongULSpace.GetUpper() + aLongULSpace.GetLower()); + + SfxItemSet aSet( GetPool(), svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, + RES_VERT_ORIENT, RES_HORI_ORIENT>{} ); + //which of the orientation attributes is to be put depends on the frame's environment + bool bRTL; + bool bVertL2R; + if ( ( bFrameSelection && + rSh.IsFrameVertical(true, bRTL, bVertL2R ) ) || + ( !bFrameSelection && bVerticalWriting ) ) + { + SwFormatHoriOrient aHoriOrient(pFormat->GetHoriOrient()); + aHoriOrient.SetHoriOrient(text::HoriOrientation::NONE); + aHoriOrient.SetPos(aHoriOrient.GetPos() + nDeltaY ); + aSet.Put( aHoriOrient ); + } + else + { + SwFormatVertOrient aVertOrient(pFormat->GetVertOrient()); + aVertOrient.SetVertOrient(text::VertOrientation::NONE); + aVertOrient.SetPos(aVertOrient.GetPos() + nDeltaY ); + aSet.Put( aVertOrient ); + } + SwFormatFrameSize aSize(pFormat->GetFrameSize()); + if(aSize.GetHeightPercent()) + { + SwRect aRect; + rSh.CalcBoundRect(aRect, RndStdIds::FLY_AS_CHAR); + long nPrtHeight = aRect.Height(); + aSize.SetHeightPercent(sal_uInt8(nHeight * 100 /nPrtHeight)); + } + else + aSize.SetHeight(nHeight ); + + aSet.Put( aSize ); + rSh.SetFlyFrameAttr( aSet ); + } + else if( nFrameType == FrameTypeFlags::DRAWOBJ ) + { + SwRect aRect( rSh.GetObjRect() ); + aRect.Top( aLongULSpace.GetUpper() + rPageRect.Top() ); + aRect.Bottom( rPageRect.Bottom() - aLongULSpace.GetLower() ); + rSh.SetObjRect( aRect ) ; + } + else if(bVerticalWriting && (bSect || rSh.IsDirectlyInSection())) + { + //change the section indents and the columns if available + //at first determine the changes + SwRect aSectRect = rSh.GetAnyCurRect(CurRectType::SectionPrt); + const SwRect aTmpRect = rSh.GetAnyCurRect(CurRectType::Section); + aSectRect.Pos() += aTmpRect.Pos(); + const long nLeftDiff = aLongULSpace.GetUpper() - static_cast<long>(aSectRect.Top() - rPageRect.Top()); + const long nRightDiff = aLongULSpace.GetLower() - static_cast<long>(nPageHeight - aSectRect.Bottom() + rPageRect.Top()); + //change the LRSpaceItem of the section accordingly + const SwSection* pCurrSect = rSh.GetCurrSection(); + const SwSectionFormat* pSectFormat = pCurrSect->GetFormat(); + SvxLRSpaceItem aLR = pSectFormat->GetLRSpace(); + aLR.SetLeft(aLR.GetLeft() + nLeftDiff); + aLR.SetRight(aLR.GetRight() + nRightDiff); + SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE, RES_COL, RES_COL>{}); + aSet.Put(aLR); + //change the first/last column + if(bSect) + { + SwFormatCol aCols( pSectFormat->GetCol() ); + long nDiffWidth = nLeftDiff + nRightDiff; + ::ResizeFrameCols(aCols, aSectRect.Height(), aSectRect.Height() - nDiffWidth, nLeftDiff ); + aSet.Put( aCols ); + } + SwSectionData aData(*pCurrSect); + rSh.UpdateSection(rSh.GetSectionFormatPos(*pSectFormat), aData, &aSet); + } + else + { SwPageDesc aDesc( rDesc ); + + if ( nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER )) + { + + const bool bHead = bool(nFrameType & FrameTypeFlags::HEADER); + SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + if ( bHead ) + aUL.SetUpper( static_cast<sal_uInt16>(aLongULSpace.GetUpper()) ); + else + aUL.SetLower( static_cast<sal_uInt16>(aLongULSpace.GetLower()) ); + aDesc.GetMaster().SetFormatAttr( aUL ); + + if( (bHead && pHeaderFormat) || (!bHead && pFooterFormat) ) + { + SwFormatFrameSize aSz( bHead ? pHeaderFormat->GetFrameSize() : + pFooterFormat->GetFrameSize() ); + aSz.SetHeightSizeType( SwFrameSize::Fixed ); + aSz.SetHeight(nPageHeight - aLongULSpace.GetLower() - + aLongULSpace.GetUpper() ); + if ( bHead ) + pHeaderFormat->SetFormatAttr( aSz ); + else + pFooterFormat->SetFormatAttr( aSz ); + } + } + else + { + SvxULSpaceItem aUL(RES_UL_SPACE); + aUL.SetUpper(static_cast<sal_uInt16>(aLongULSpace.GetUpper())); + aUL.SetLower(static_cast<sal_uInt16>(aLongULSpace.GetLower())); + aDesc.GetMaster().SetFormatAttr(aUL); + } + + rSh.ChgPageDesc( nDescId, aDesc ); + } + } + break; + + // apply new top and bottom margins to current page style + case SID_ATTR_PAGE_ULSPACE: + if ( pReqArgs ) + { + const SvxLongULSpaceItem& aLongULSpace( pReqArgs->Get( SID_ATTR_PAGE_ULSPACE ) ); + + SwPageDesc aDesc( rDesc ); + { + SvxULSpaceItem aUL(RES_UL_SPACE); + aUL.SetUpper(static_cast<sal_uInt16>(aLongULSpace.GetUpper())); + aUL.SetLower(static_cast<sal_uInt16>(aLongULSpace.GetLower())); + aDesc.GetMaster().SetFormatAttr(aUL); + } + rSh.ChgPageDesc( nDescId, aDesc ); + } + break; + + case SID_ATTR_PAGE_COLUMN: + if ( pReqArgs ) + { + const SfxInt16Item aColumnItem( static_cast<const SfxInt16Item&>(pReqArgs->Get(nSlot)) ); + const sal_uInt16 nPageColumnType = aColumnItem.GetValue(); + + // nPageColumnType = + // 1 - single-columned page + // 2 - two-columned page + // 3 - three-columned page + // 4 - two-columned page with left column width of 2/3 of page width + // 5 - two-columned page with right column width of 2/3 of page width + + sal_uInt16 nCount = 2; + if ( nPageColumnType == 1 ) + { + nCount = 0; + } + else if ( nPageColumnType == 3 ) + { + nCount = 3; + } + + const sal_uInt16 nGutterWidth = 0; + + const SvxLRSpaceItem aLR( rDesc.GetMaster().GetLRSpace() ); + const long nLeft = aLR.GetLeft(); + const long nRight = aLR.GetRight(); + const long nWidth = nPageWidth - nLeft - nRight; + + SwFormatCol aCols( rDesc.GetMaster().GetCol() ); + aCols.Init( nCount, nGutterWidth, nWidth ); + aCols.SetWishWidth( nWidth ); + aCols.SetGutterWidth( nGutterWidth, nWidth ); + aCols.SetOrtho( false, nGutterWidth, nWidth ); + + long nColumnLeft = 0; + long nColumnRight = 0; + if ( nPageColumnType == 4 ) + { + nColumnRight = static_cast<long>(nWidth/3); + nColumnLeft = nWidth - nColumnRight; + aCols.GetColumns()[0].SetWishWidth( nColumnLeft ); + aCols.GetColumns()[1].SetWishWidth( nColumnRight ); + } + else if ( nPageColumnType == 5 ) + { + nColumnLeft = static_cast<long>(nWidth/3); + nColumnRight = nWidth - nColumnLeft; + aCols.GetColumns()[0].SetWishWidth( nColumnLeft ); + aCols.GetColumns()[1].SetWishWidth( nColumnRight ); + } + + SwPageDesc aDesc( rDesc ); + aDesc.GetMaster().SetFormatAttr( aCols ); + rSh.ChgPageDesc( rSh.GetCurPageDesc(), aDesc ); + } + break; + + case SID_ATTR_TABSTOP_VERTICAL: + case SID_ATTR_TABSTOP: + if (pReqArgs) + { + const sal_uInt16 nWhich = GetPool().GetWhich(nSlot); + SvxTabStopItem aTabStops( static_cast<const SvxTabStopItem&>(pReqArgs-> + Get( nWhich ))); + aTabStops.SetWhich(RES_PARATR_TABSTOP); + const SvxTabStopItem& rDefTabs = rSh.GetDefault(RES_PARATR_TABSTOP); + + // Default tab at pos 0 + SfxItemSet aSet( GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + rSh.GetCurAttr( aSet ); + const SvxLRSpaceItem& rLR = aSet.Get(RES_LR_SPACE); + + if ( rLR.GetTextFirstLineOffset() < 0 ) + { + SvxTabStop aSwTabStop( 0, SvxTabAdjust::Default ); + aTabStops.Insert( aSwTabStop ); + } + + // Populate with default tabs. + ::MakeDefTabs( ::GetTabDist( rDefTabs ), aTabStops ); + + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && pColl->IsAutoUpdateFormat() ) + { + SfxItemSet aTmp(GetPool(), svl::Items<RES_PARATR_TABSTOP, RES_PARATR_TABSTOP>{}); + aTmp.Put(aTabStops); + rSh.AutoUpdatePara( pColl, aTmp ); + } + else + rSh.SetAttrItem( aTabStops ); + } + break; + case SID_TABSTOP_ADD_OR_CHANGE: + if (pReqArgs) + { + const auto aIndexItem = static_cast<const SfxInt32Item&>(pReqArgs->Get(SID_TABSTOP_ATTR_INDEX)); + const auto aPositionItem = static_cast<const SfxInt32Item&>(pReqArgs->Get(SID_TABSTOP_ATTR_POSITION)); + const auto aRemoveItem = static_cast<const SfxBoolItem&>(pReqArgs->Get(SID_TABSTOP_ATTR_REMOVE)); + const sal_Int32 nIndex = aIndexItem.GetValue(); + const sal_Int32 nPosition = aPositionItem.GetValue(); + const bool bRemove = aRemoveItem.GetValue(); + + + + SfxItemSet aItemSet(GetPool(), svl::Items<RES_PARATR_TABSTOP, RES_PARATR_TABSTOP>{}); + rSh.GetCurAttr(aItemSet); + SvxTabStopItem aTabStopItem(aItemSet.Get(RES_PARATR_TABSTOP)); + lcl_EraseDefTabs(aTabStopItem); + + if (nIndex < aTabStopItem.Count()) + { + if (nIndex == -1) + { + SvxTabStop aSwTabStop(0, SvxTabAdjust::Default); + aTabStopItem.Insert(aSwTabStop); + + const SvxTabStopItem& rDefaultTabs = rSh.GetDefault(RES_PARATR_TABSTOP); + MakeDefTabs(GetTabDist(rDefaultTabs), aTabStopItem); + + SvxTabStop aTabStop(nPosition); + aTabStopItem.Insert(aTabStop); + } + else + { + SvxTabStop aTabStop = aTabStopItem.At(nIndex); + aTabStopItem.Remove(nIndex); + if (!bRemove) + { + aTabStop.GetTabPos() = nPosition; + aTabStopItem.Insert(aTabStop); + + SvxTabStop aSwTabStop(0, SvxTabAdjust::Default); + aTabStopItem.Insert(aSwTabStop); + } + const SvxTabStopItem& rDefaultTabs = rSh.GetDefault(RES_PARATR_TABSTOP); + MakeDefTabs(GetTabDist(rDefaultTabs), aTabStopItem); + } + rSh.SetAttrItem(aTabStopItem); + } + } + break; + case SID_PARAGRAPH_CHANGE_STATE: + { + const SfxPoolItem *fLineIndent, *pLeftIndent, *pRightIndent; + if (pReqArgs) + { + SfxItemSet aLRSpaceSet( GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + rSh.GetCurAttr( aLRSpaceSet ); + SvxLRSpaceItem aParaMargin( aLRSpaceSet.Get( RES_LR_SPACE ) ); + + if (pReqArgs->GetItemState(SID_PARAGRAPH_FIRST_LINE_INDENT,true,&fLineIndent) == SfxItemState::SET) + { + const OUString ratio = static_cast<const SfxStringItem*>(fLineIndent)->GetValue(); + aParaMargin.SetTextFirstLineOffset(nPageWidth * ratio.toFloat()); + } + else if (pReqArgs->GetItemState(SID_PARAGRAPH_LEFT_INDENT,true,&pLeftIndent) == SfxItemState::SET) + { + const OUString ratio = static_cast<const SfxStringItem*>(pLeftIndent)->GetValue(); + aParaMargin.SetLeft(nPageWidth * ratio.toFloat()); + } + else if (pReqArgs->GetItemState(SID_PARAGRAPH_RIGHT_INDENT,true,&pRightIndent) == SfxItemState::SET) + { + const OUString ratio = static_cast<const SfxStringItem*>(pRightIndent)->GetValue(); + aParaMargin.SetRight(nPageWidth * ratio.toFloat()); + } + rSh.SetAttrItem(aParaMargin); + } + break; + } + case SID_HANGING_INDENT: + { + SfxItemSet aLRSpaceSet( GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + rSh.GetCurAttr( aLRSpaceSet ); + SvxLRSpaceItem aParaMargin( aLRSpaceSet.Get( RES_LR_SPACE ) ); + + SvxLRSpaceItem aNewMargin( RES_LR_SPACE ); + aNewMargin.SetTextLeft( aParaMargin.GetTextLeft() + aParaMargin.GetTextFirstLineOffset() ); + aNewMargin.SetRight( aParaMargin.GetRight() ); + aNewMargin.SetTextFirstLineOffset( (aParaMargin.GetTextFirstLineOffset()) * -1 ); + + rSh.SetAttrItem( aNewMargin ); + break; + } + + case SID_ATTR_PARA_LRSPACE_VERTICAL: + case SID_ATTR_PARA_LRSPACE: + if ( pReqArgs ) + { + SvxLRSpaceItem aParaMargin(static_cast<const SvxLRSpaceItem&>(pReqArgs->Get(nSlot))); + + aParaMargin.SetRight( aParaMargin.GetRight() - m_nRightBorderDistance ); + aParaMargin.SetTextLeft(aParaMargin.GetTextLeft() - m_nLeftBorderDistance ); + + aParaMargin.SetWhich( RES_LR_SPACE ); + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + + // #i23726# + if (m_pNumRuleNodeFromDoc) + { + // --> #i42922# Mouse move of numbering label + // has to consider the left indent of the paragraph + SfxItemSet aSet( GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{} ); + rSh.GetCurAttr( aSet ); + const SvxLRSpaceItem& rLR = aSet.Get(RES_LR_SPACE); + + SwPosition aPos(*m_pNumRuleNodeFromDoc); + // #i90078# + rSh.SetIndent( static_cast< short >(aParaMargin.GetTextLeft() - rLR.GetTextLeft()), aPos); + // #i42921# invalidate state of indent in order to get a ruler update. + aParaMargin.SetWhich( nSlot ); + GetViewFrame()->GetBindings().SetState( aParaMargin ); + } + else if( pColl && pColl->IsAutoUpdateFormat() ) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{}); + aSet.Put(aParaMargin); + rSh.AutoUpdatePara( pColl, aSet); + } + else + rSh.SetAttrItem( aParaMargin ); + + if ( aParaMargin.GetTextFirstLineOffset() < 0 ) + { + SfxItemSet aSet( GetPool(), svl::Items<RES_PARATR_TABSTOP, RES_PARATR_TABSTOP>{} ); + + rSh.GetCurAttr( aSet ); + const SvxTabStopItem& rTabStops = aSet.Get(RES_PARATR_TABSTOP); + + // Do we have a tab at position zero? + sal_uInt16 i; + + for ( i = 0; i < rTabStops.Count(); ++i ) + if ( rTabStops[i].GetTabPos() == 0 ) + break; + + if ( i >= rTabStops.Count() ) + { + // No DefTab + std::unique_ptr<SvxTabStopItem> aTabStops(rTabStops.Clone()); + + ::lcl_EraseDefTabs(*aTabStops); + + SvxTabStop aSwTabStop( 0, SvxTabAdjust::Default ); + aTabStops->Insert(aSwTabStop); + + const SvxTabStopItem& rDefTabs = rSh.GetDefault(RES_PARATR_TABSTOP); + ::MakeDefTabs( ::GetTabDist(rDefTabs), *aTabStops ); + + if( pColl && pColl->IsAutoUpdateFormat()) + { + SfxItemSet aSetTmp(GetPool(), svl::Items<RES_PARATR_TABSTOP, RES_PARATR_TABSTOP>{}); + aSetTmp.Put(*aTabStops); + rSh.AutoUpdatePara( pColl, aSetTmp ); + } + else + rSh.SetAttrItem( *aTabStops ); + } + } + } + break; + + case SID_ATTR_PARA_ULSPACE: + if ( pReqArgs ) + { + SvxULSpaceItem aParaMargin(static_cast<const SvxULSpaceItem&>(pReqArgs->Get(nSlot))); + + aParaMargin.SetUpper( aParaMargin.GetUpper() ); + aParaMargin.SetLower(aParaMargin.GetLower()); + + aParaMargin.SetWhich( RES_UL_SPACE ); + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && pColl->IsAutoUpdateFormat() ) + { + SfxItemSet aSet(GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>{}); + aSet.Put(aParaMargin); + rSh.AutoUpdatePara( pColl, aSet); + } + else + rSh.SetAttrItem( aParaMargin ); + } + break; + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SfxItemSet aULSpaceSet( GetPool(), svl::Items<RES_UL_SPACE, RES_UL_SPACE>{} ); + rSh.GetCurAttr( aULSpaceSet ); + SvxULSpaceItem aULSpace( aULSpaceSet.Get( RES_UL_SPACE ) ); + sal_uInt16 nUpper = aULSpace.GetUpper(); + sal_uInt16 nLower = aULSpace.GetLower(); + + if ( nSlot == SID_PARASPACE_INCREASE ) + { + nUpper = std::min< sal_uInt16 >( nUpper + 57, 5670 ); + nLower = std::min< sal_uInt16 >( nLower + 57, 5670 ); + } + else + { + nUpper = std::max< sal_Int16 >( nUpper - 57, 0 ); + nLower = std::max< sal_Int16 >( nLower - 57, 0 ); + } + + aULSpace.SetUpper( nUpper ); + aULSpace.SetLower( nLower ); + + SwTextFormatColl* pColl = rSh.GetCurTextFormatColl(); + if( pColl && pColl->IsAutoUpdateFormat() ) + { + aULSpaceSet.Put( aULSpace ); + rSh.AutoUpdatePara( pColl, aULSpaceSet ); + } + else + rSh.SetAttrItem( aULSpace, SetAttrMode::DEFAULT, true ); + } + break; + + case SID_RULER_CHANGE_STATE: + { + const SfxPoolItem *pMargin1, *pMargin2; + if ( pReqArgs && + pReqArgs->GetItemState(SID_RULER_MARGIN1,true,&pMargin1) == SfxItemState::SET ) + { + const OUString ratio = static_cast<const SfxStringItem*>(pMargin1)->GetValue(); + GetHRuler().SetValues(RulerChangeType::MARGIN1, GetHRuler().GetPageWidth() * ratio.toFloat()); + } + else if ( pReqArgs && + pReqArgs->GetItemState(SID_RULER_MARGIN2,true,&pMargin2) == SfxItemState::SET ) + { + const OUString ratio = static_cast<const SfxStringItem*>(pMargin2)->GetValue(); + GetHRuler().SetValues(RulerChangeType::MARGIN2, GetHRuler().GetPageWidth() * ratio.toFloat()); + } + } + break; + case SID_RULER_BORDERS_VERTICAL: + case SID_RULER_BORDERS: + if ( pReqArgs ) + { + SvxColumnItem aColItem(static_cast<const SvxColumnItem&>(pReqArgs->Get(nSlot))); + + if( m_bSetTabColFromDoc || (!bSect && rSh.GetTableFormat()) ) + { + OSL_ENSURE(aColItem.Count(), "ColDesc is empty!!"); + + const bool bSingleLine = rReq. + GetArgs()->Get(SID_RULER_ACT_LINE_ONLY).GetValue(); + + SwTabCols aTabCols; + if ( m_bSetTabColFromDoc ) + rSh.GetMouseTabCols( aTabCols, m_aTabColFromDocPos ); + else + rSh.GetTabCols(aTabCols); + + // left table border + long nBorder = static_cast<long>(aColItem.GetLeft() - aTabCols.GetLeftMin()); + aTabCols.SetLeft( nBorder ); + + nBorder = (bVerticalWriting ? nPageHeight : nPageWidth) - aTabCols.GetLeftMin() - aColItem.GetRight(); + + if ( aColItem.GetRight() > 0 ) + aTabCols.SetRight( nBorder ); + + // Tabcols sequentially + // The last column is defined by the edge. + // Columns in right-to-left tables need to be mirrored + bool bIsTableRTL = + IsTabColFromDoc() ? + rSh.IsMouseTableRightToLeft(m_aTabColFromDocPos) + : rSh.IsTableRightToLeft(); + const size_t nColCount = aColItem.Count() - 1; + if(bIsTableRTL) + { + for ( size_t i = 0; i < nColCount && i < aTabCols.Count(); ++i ) + { + const SvxColumnDescription& rCol = aColItem[nColCount - i]; + aTabCols[i] = aTabCols.GetRight() - rCol.nStart; + aTabCols.SetHidden( i, !rCol.bVisible ); + } + } + else + { + for ( size_t i = 0; i < nColCount && i < aTabCols.Count(); ++i ) + { + const SvxColumnDescription& rCol = aColItem[i]; + aTabCols[i] = rCol.nEnd + aTabCols.GetLeft(); + aTabCols.SetHidden( i, !rCol.bVisible ); + } + } + + if ( m_bSetTabColFromDoc ) + { + if( !rSh.IsViewLocked() ) + { + bUnlockView = true; + rSh.LockView( true ); + } + rSh.SetMouseTabCols( aTabCols, bSingleLine, + m_aTabColFromDocPos ); + } + else + rSh.SetTabCols(aTabCols, bSingleLine); + + } + else + { + if ( bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY || bSect) + { + SwSectionFormat *pSectFormat = nullptr; + SfxItemSet aSet( GetPool(), svl::Items<RES_COL, RES_COL>{} ); + if(bSect) + { + SwSection *pSect = rSh.GetAnySection(); + OSL_ENSURE( pSect, "Which section?"); + pSectFormat = pSect->GetFormat(); + } + else + { + rSh.GetFlyFrameAttr( aSet ); + } + SwFormatCol aCols( + bSect ? + pSectFormat->GetCol() : + aSet.Get( RES_COL, false )); + SwRect aCurRect = rSh.GetAnyCurRect(bSect ? CurRectType::SectionPrt : CurRectType::FlyEmbeddedPrt); + const long lWidth = bVerticalWriting ? aCurRect.Height() : aCurRect.Width(); + ::lcl_ConvertToCols( aColItem, lWidth, aCols ); + aSet.Put( aCols ); + if(bSect) + rSh.SetSectionAttr( aSet, pSectFormat ); + else + { + rSh.StartAction(); + rSh.Push(); + rSh.SetFlyFrameAttr( aSet ); + // Cancel the frame selection again + if(!bFrameSelection && rSh.IsFrameSelected()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + rSh.Pop(); + rSh.EndAction(); + } + } + else + { + SwFormatCol aCols( rDesc.GetMaster().GetCol() ); + const SwRect aPrtRect = rSh.GetAnyCurRect(CurRectType::PagePrt); + ::lcl_ConvertToCols( aColItem, + bVerticalWriting ? aPrtRect.Height() : aPrtRect.Width(), + aCols ); + SwPageDesc aDesc( rDesc ); + aDesc.GetMaster().SetFormatAttr( aCols ); + rSh.ChgPageDesc( rSh.GetCurPageDesc(), aDesc ); + } + } + } + break; + + case SID_RULER_ROWS : + case SID_RULER_ROWS_VERTICAL: + if (pReqArgs) + { + SvxColumnItem aColItem(static_cast<const SvxColumnItem&>(pReqArgs->Get(nSlot))); + + if( m_bSetTabColFromDoc || (!bSect && rSh.GetTableFormat()) ) + { + OSL_ENSURE(aColItem.Count(), "ColDesc is empty!!"); + + SwTabCols aTabCols; + if ( m_bSetTabRowFromDoc ) + rSh.GetMouseTabRows( aTabCols, m_aTabColFromDocPos ); + else + rSh.GetTabRows(aTabCols); + + if ( bVerticalWriting ) + { + aTabCols.SetRight(nPageWidth - aColItem.GetRight() - aColItem.GetLeft()); + aTabCols.SetLeftMin(aColItem.GetLeft()); + } + else + { + long nBorder = nPageHeight - aTabCols.GetLeftMin() - aColItem.GetRight(); + aTabCols.SetRight( nBorder ); + } + + const size_t nColItems = aColItem.Count() - 1; + if(bVerticalWriting) + { + for ( size_t i = nColItems; i; --i ) + { + const SvxColumnDescription& rCol = aColItem[i - 1]; + long nColumnPos = aTabCols.GetRight() - rCol.nEnd ; + aTabCols[i - 1] = nColumnPos; + aTabCols.SetHidden( i - 1, !rCol.bVisible ); + } + } + else + { + for ( size_t i = 0; i < nColItems; ++i ) + { + const SvxColumnDescription& rCol = aColItem[i]; + aTabCols[i] = rCol.nEnd + aTabCols.GetLeft(); + aTabCols.SetHidden( i, !rCol.bVisible ); + } + } + bool bSingleLine = false; + const SfxPoolItem* pSingleLine; + if( SfxItemState::SET == rReq.GetArgs()->GetItemState(SID_RULER_ACT_LINE_ONLY, false, &pSingleLine)) + bSingleLine = static_cast<const SfxBoolItem*>(pSingleLine)->GetValue(); + if ( m_bSetTabRowFromDoc ) + { + if( !rSh.IsViewLocked() ) + { + bUnlockView = true; + rSh.LockView( true ); + } + rSh.SetMouseTabRows( aTabCols, bSingleLine, m_aTabColFromDocPos ); + } + else + rSh.SetTabRows(aTabCols, bSingleLine); + } + } + break; + case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION: + { + if (pReqArgs) + { + const SfxPoolItem *pBorderType; + const SfxPoolItem *pIndex; + const SfxPoolItem *pOffset; + constexpr long constDistanceOffset = 40; + + if (pReqArgs->GetItemState(SID_TABLE_BORDER_TYPE, true, &pBorderType) == SfxItemState::SET + && pReqArgs->GetItemState(SID_TABLE_BORDER_INDEX, true, &pIndex) == SfxItemState::SET + && pReqArgs->GetItemState(SID_TABLE_BORDER_OFFSET, true, &pOffset) == SfxItemState::SET) + { + const OUString sType = static_cast<const SfxStringItem*>(pBorderType)->GetValue(); + const sal_uInt16 nIndex = static_cast<const SfxUInt16Item*>(pIndex)->GetValue(); + const sal_Int32 nOffset = static_cast<const SfxInt32Item*>(pOffset)->GetValue(); + + if (sType.startsWith("column")) + { + SwTabCols aTabCols; + rSh.GetTabCols(aTabCols); + + if (sType == "column-left") + { + auto & rEntry = aTabCols.GetEntry(0); + long nNewPosition = aTabCols.GetLeft() + long(nOffset); + long nPosition = std::min(nNewPosition, rEntry.nPos - constDistanceOffset); + aTabCols.SetLeft(nPosition); + } + else if (sType == "column-right") + { + auto & rEntry = aTabCols.GetEntry(aTabCols.Count() - 1); + long nNewPosition = aTabCols.GetRight() + long(nOffset); + long nPosition = std::max(nNewPosition, rEntry.nPos + constDistanceOffset); + aTabCols.SetRight(nPosition); + } + else if (sType == "column-middle" && nIndex < aTabCols.Count()) + { + auto & rEntry = aTabCols.GetEntry(nIndex); + long nNewPosition = rEntry.nPos + long(nOffset); + long nPosition = std::clamp(nNewPosition, rEntry.nMin, rEntry.nMax - constDistanceOffset); + rEntry.nPos = nPosition; + } + + rSh.SetTabCols(aTabCols, false); + } + else if (sType.startsWith("row")) + { + SwTabCols aTabRows; + rSh.GetTabRows(aTabRows); + + if (sType == "row-left") + { + auto & rEntry = aTabRows.GetEntry(0); + long nNewPosition = aTabRows.GetLeft() + long(nOffset); + long nPosition = std::min(nNewPosition, rEntry.nPos - constDistanceOffset); + aTabRows.SetLeft(nPosition); + } + else if (sType == "row-right") + { + auto & rEntry = aTabRows.GetEntry(aTabRows.Count() - 1); + long nNewPosition = aTabRows.GetRight() + long(nOffset); + long nPosition = std::max(nNewPosition, rEntry.nPos + constDistanceOffset); + aTabRows.SetRight(nPosition); + } + else if (sType == "row-middle" && nIndex < aTabRows.Count()) + { + auto & rEntry = aTabRows.GetEntry(nIndex); + long nNewPosition = rEntry.nPos + long(nOffset); + long nPosition = std::clamp(nNewPosition, rEntry.nMin, rEntry.nMax - constDistanceOffset); + rEntry.nPos = nPosition; + } + + rSh.SetTabRows(aTabRows, false); + } + } + } + } + break; + case SID_ATTR_PAGE_HEADER: + { + if ( pReqArgs ) + { + const bool bHeaderOn = static_cast<const SfxBoolItem&>(pReqArgs->Get(SID_ATTR_PAGE_HEADER)).GetValue(); + SwPageDesc aDesc(rDesc); + SwFrameFormat &rMaster = aDesc.GetMaster(); + rMaster.SetFormatAttr( SwFormatHeader( bHeaderOn )); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_HEADER_LRMARGIN: + { + if ( pReqArgs && rDesc.GetMaster().GetHeader().IsActive() ) + { + const SvxLongLRSpaceItem& aLongLR = static_cast<const SvxLongLRSpaceItem&>(pReqArgs->Get(SID_ATTR_PAGE_HEADER_LRMARGIN)); + SvxLRSpaceItem aLR(RES_LR_SPACE); + SwPageDesc aDesc(rDesc); + aLR.SetLeft(aLongLR.GetLeft()); + aLR.SetRight(aLongLR.GetRight()); + SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(aDesc.GetMaster().GetHeader().GetHeaderFormat()); + pFormat->SetFormatAttr( aLR ); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_HEADER_SPACING: + { + if ( pReqArgs && rDesc.GetMaster().GetHeader().IsActive()) + { + const SvxLongULSpaceItem& aLongUL = static_cast<const SvxLongULSpaceItem&>(pReqArgs->Get(SID_ATTR_PAGE_HEADER_SPACING)); + SwPageDesc aDesc(rDesc); + SvxULSpaceItem aUL(0, aLongUL.GetLower(), RES_UL_SPACE ); + SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(aDesc.GetMaster().GetHeader().GetHeaderFormat()); + pFormat->SetFormatAttr( aUL ); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_HEADER_LAYOUT: + { + if ( pReqArgs && rDesc.GetMaster().GetHeader().IsActive()) + { + const SfxInt16Item& aLayoutItem = static_cast<const SfxInt16Item&>(pReqArgs->Get(SID_ATTR_PAGE_HEADER_LAYOUT)); + sal_uInt16 nLayout = aLayoutItem.GetValue(); + SwPageDesc aDesc(rDesc); + aDesc.ChgHeaderShare((nLayout>>1) == 0); + aDesc.ChgFirstShare((nLayout % 2) == 0); // FIXME control changes for both header footer - tdf#100287 + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_FOOTER: + { + if ( pReqArgs ) + { + const bool bFooterOn = static_cast<const SfxBoolItem&>(pReqArgs->Get(SID_ATTR_PAGE_FOOTER)).GetValue(); + SwPageDesc aDesc(rDesc); + SwFrameFormat &rMaster = aDesc.GetMaster(); + rMaster.SetFormatAttr( SwFormatFooter( bFooterOn )); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_FOOTER_LRMARGIN: + { + if ( pReqArgs && rDesc.GetMaster().GetFooter().IsActive() ) + { + const SvxLongLRSpaceItem& aLongLR = static_cast<const SvxLongLRSpaceItem&>(pReqArgs->Get(SID_ATTR_PAGE_FOOTER_LRMARGIN)); + SvxLRSpaceItem aLR(RES_LR_SPACE); + SwPageDesc aDesc(rDesc); + aLR.SetLeft(aLongLR.GetLeft()); + aLR.SetRight(aLongLR.GetRight()); + SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(aDesc.GetMaster().GetFooter().GetFooterFormat()); + pFormat->SetFormatAttr( aLR ); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_FOOTER_SPACING: + { + if ( pReqArgs && rDesc.GetMaster().GetFooter().IsActive()) + { + const SvxLongULSpaceItem& aLongUL = static_cast<const SvxLongULSpaceItem&>(pReqArgs->Get(SID_ATTR_PAGE_FOOTER_SPACING)); + SwPageDesc aDesc(rDesc); + SvxULSpaceItem aUL(aLongUL.GetUpper(), 0, RES_UL_SPACE ); + SwFrameFormat* pFormat = const_cast<SwFrameFormat*>(aDesc.GetMaster().GetFooter().GetFooterFormat()); + pFormat->SetFormatAttr( aUL ); + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + case SID_ATTR_PAGE_FOOTER_LAYOUT: + { + if ( pReqArgs && rDesc.GetMaster().GetFooter().IsActive()) + { + const SfxInt16Item& aLayoutItem = static_cast<const SfxInt16Item&>(pReqArgs->Get(SID_ATTR_PAGE_FOOTER_LAYOUT)); + sal_uInt16 nLayout = aLayoutItem.GetValue(); + SwPageDesc aDesc(rDesc); + aDesc.ChgFooterShare((nLayout>>1) == 0); + aDesc.ChgFirstShare((nLayout % 2) == 0); // FIXME control changes for both header footer - tdf#100287 + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + + case SID_ATTR_PAGE_COLOR: + case SID_ATTR_PAGE_FILLSTYLE: + case SID_ATTR_PAGE_GRADIENT: + case SID_ATTR_PAGE_HATCH: + case SID_ATTR_PAGE_BITMAP: + { + if(pReqArgs) + { + SwPageDesc aDesc(rDesc); + SwFrameFormat &rMaster = aDesc.GetMaster(); + switch (nSlot) + { + case SID_ATTR_PAGE_FILLSTYLE: + { + XFillStyleItem aFSItem( pReqArgs->Get( XATTR_FILLSTYLE ) ); + drawing::FillStyle eXFS = aFSItem.GetValue(); + + if ( eXFS == drawing::FillStyle_NONE ) + rMaster.SetFormatAttr( XFillStyleItem( eXFS ) ); + } + break; + + case SID_ATTR_PAGE_COLOR: + { + XFillColorItem aColorItem( pReqArgs->Get( XATTR_FILLCOLOR ) ); + rMaster.SetFormatAttr( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rMaster.SetFormatAttr( aColorItem ); + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + XFillGradientItem aGradientItem( pReqArgs->Get( XATTR_FILLGRADIENT ) ); + rMaster.SetFormatAttr( XFillStyleItem( drawing::FillStyle_GRADIENT ) ); + rMaster.SetFormatAttr( aGradientItem ); + } + break; + + case SID_ATTR_PAGE_HATCH: + { + XFillHatchItem aHatchItem( pReqArgs->Get( XATTR_FILLHATCH ) ); + rMaster.SetFormatAttr( XFillStyleItem( drawing::FillStyle_HATCH ) ); + rMaster.SetFormatAttr( aHatchItem ); + } + break; + + case SID_ATTR_PAGE_BITMAP: + { + XFillBitmapItem aBitmapItem( pReqArgs->Get( XATTR_FILLBITMAP ) ); + rMaster.SetFormatAttr( XFillStyleItem( drawing::FillStyle_BITMAP ) ); + rMaster.SetFormatAttr( aBitmapItem ); + } + break; + + default: + break; + } + rSh.ChgPageDesc(rSh.GetCurPageDesc(), aDesc); + } + } + break; + + default: + OSL_ENSURE( false, "wrong SlotId"); + } + rSh.EndAllAction(); + + if( bUnlockView ) + rSh.LockView( false ); + + m_bSetTabColFromDoc = m_bSetTabRowFromDoc = m_bTabColFromDoc = m_bTabRowFromDoc = false; + SetNumRuleNodeFromDoc(nullptr); +} + +// Here the status of the tab bar will be determined. +// This means that all relevant attributes at the CursorPos +// will be submitted to the tab bar. +void SwView::StateTabWin(SfxItemSet& rSet) +{ + SwWrtShell &rSh = GetWrtShell(); + + const Point* pPt = IsTabColFromDoc() || IsTabRowFromDoc() ? &m_aTabColFromDocPos : nullptr; + const FrameTypeFlags nFrameType = rSh.IsObjSelected() + ? FrameTypeFlags::DRAWOBJ + : rSh.GetFrameType( pPt, true ); + + const bool bFrameSelection = rSh.IsFrameSelected(); + const bool bBrowse = rSh.GetViewOptions()->getBrowseMode(); + // PageOffset/limiter + const SwRect& rPageRect = rSh.GetAnyCurRect( CurRectType::Page, pPt ); + const SwRect& rPagePrtRect = rSh.GetAnyCurRect( CurRectType::PagePrt, pPt ); + const long nPageWidth = rPageRect.Width(); + const long nPageHeight = rPageRect.Height(); + + const SwPageDesc& rDesc = rSh.GetPageDesc( + IsTabColFromDoc() || m_bTabRowFromDoc ? + rSh.GetMousePageDesc(m_aTabColFromDocPos) : rSh.GetCurPageDesc() ); + + const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir(); + const bool bVerticalWriting = rSh.IsInVerticalText(); + + //enable tab stop display on the rulers depending on the writing direction + WinBits nRulerStyle = m_pHRuler->GetStyle() & ~WB_EXTRAFIELD; + m_pHRuler->SetStyle(bVerticalWriting||bBrowse ? nRulerStyle : nRulerStyle|WB_EXTRAFIELD); + nRulerStyle = m_pVRuler->GetStyle() & ~WB_EXTRAFIELD; + m_pVRuler->SetStyle(bVerticalWriting ? nRulerStyle|WB_EXTRAFIELD : nRulerStyle); + + //#i24363# tab stops relative to indent + bool bRelative = rSh.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT); + m_pHRuler->SetTabsRelativeToIndent( bRelative ); + m_pVRuler->SetTabsRelativeToIndent( bRelative ); + + SvxLRSpaceItem aPageLRSpace( rDesc.GetMaster().GetLRSpace() ); + SwapPageMargin( rDesc, aPageLRSpace ); + + SfxItemSet aCoreSet( GetPool(), svl::Items<RES_PARATR_TABSTOP, RES_PARATR_TABSTOP, + RES_LR_SPACE, RES_UL_SPACE>{} ); + // get also the list level indent values merged as LR-SPACE item, if needed. + rSh.GetCurAttr( aCoreSet, true ); + const SelectionType nSelType = rSh.GetSelectionType(); + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while ( nWhich ) + { + switch ( nWhich ) + { + + case SID_ATTR_PAGE_COLUMN: + { + sal_uInt16 nColumnType = 0; + + const SwFrameFormat& rMaster = rDesc.GetMaster(); + const SwFormatCol& aCol(rMaster.GetCol()); + const sal_uInt16 nCols = aCol.GetNumCols(); + if ( nCols == 0 ) + { + nColumnType = 1; + } + else if ( nCols == 2 ) + { + const sal_uInt16 nColLeft = aCol.CalcPrtColWidth(0, aCol.GetWishWidth()); + const sal_uInt16 nColRight = aCol.CalcPrtColWidth(1, aCol.GetWishWidth()); + + if ( abs(nColLeft - nColRight) <= 10 ) + { + nColumnType = 2; + } + else if( abs(nColLeft - nColRight*2) < 20 ) + { + nColumnType = 4; + } + else if( abs(nColLeft*2 - nColRight) < 20 ) + { + nColumnType = 5; + } + } + else if( nCols == 3 ) + { + nColumnType = 3; + } + else + nColumnType = nCols; + + rSet.Put( SfxInt16Item( SID_ATTR_PAGE_COLUMN, nColumnType ) ); + } + break; + + case SID_ATTR_LONG_LRSPACE: + { + SvxLongLRSpaceItem aLongLR( aPageLRSpace.GetLeft(), + aPageLRSpace.GetRight(), + SID_ATTR_LONG_LRSPACE); + if(bBrowse) + { + aLongLR.SetLeft(rPagePrtRect.Left()); + aLongLR.SetRight(nPageWidth - rPagePrtRect.Right()); + } + if ( ( nFrameType & FrameTypeFlags::HEADER || nFrameType & FrameTypeFlags::FOOTER ) && + !(nFrameType & FrameTypeFlags::COLSECT) ) + { + SwFrameFormat *pFormat = const_cast<SwFrameFormat*>((nFrameType & FrameTypeFlags::HEADER) ? + rDesc.GetMaster().GetHeader().GetHeaderFormat() : + rDesc.GetMaster().GetFooter().GetFooterFormat()); + if( pFormat )// #i80890# if rDesc is not the one belonging to the current page is might crash + { + SwRect aRect( rSh.GetAnyCurRect( CurRectType::HeaderFooter, pPt)); + aRect.Pos() -= rSh.GetAnyCurRect( CurRectType::Page, pPt ).Pos(); + const SvxLRSpaceItem& aLR = pFormat->GetLRSpace(); + aLongLR.SetLeft ( aLR.GetLeft() + aRect.Left() ); + aLongLR.SetRight( nPageWidth - aRect.Right() + aLR.GetRight() ); + } + } + else + { + SwRect aRect; + if( !bFrameSelection && ((nFrameType & FrameTypeFlags::COLSECT) || rSh.IsDirectlyInSection()) ) + { + aRect = rSh.GetAnyCurRect(CurRectType::SectionPrt, pPt); + const SwRect aTmpRect = rSh.GetAnyCurRect(CurRectType::Section, pPt); + aRect.Pos() += aTmpRect.Pos(); + } + + else if ( bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY ) + aRect = rSh.GetAnyCurRect(CurRectType::FlyEmbedded, pPt); + else if( nFrameType & FrameTypeFlags::DRAWOBJ) + aRect = rSh.GetObjRect(); + + if( aRect.Width() ) + { + // make relative to page position: + aLongLR.SetLeft(aRect.Left() - rPageRect.Left()); + aLongLR.SetRight(rPageRect.Right() - aRect.Right()); + } + } + rSet.Put( aLongLR ); + } + break; + + // provide left and right margins of current page style + case SID_ATTR_PAGE_LRSPACE: + { + const SvxLRSpaceItem aTmpPageLRSpace( rDesc.GetMaster().GetLRSpace() ); + const SvxLongLRSpaceItem aLongLR( + aTmpPageLRSpace.GetLeft(), + aTmpPageLRSpace.GetRight(), + SID_ATTR_PAGE_LRSPACE ); + rSet.Put( aLongLR ); + } + break; + + case SID_ATTR_LONG_ULSPACE: + { + // Page margin top bottom + SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + SvxLongULSpaceItem aLongUL( static_cast<long>(aUL.GetUpper()), + static_cast<long>(aUL.GetLower()), + SID_ATTR_LONG_ULSPACE); + + if ( bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY ) + { + // Convert document coordinates into page coordinates. + const SwRect &rRect = rSh.GetAnyCurRect(CurRectType::FlyEmbedded, pPt); + aLongUL.SetUpper(rRect.Top() - rPageRect.Top()); + aLongUL.SetLower(rPageRect.Bottom() - rRect.Bottom()); + } + else if ( nFrameType & FrameTypeFlags::HEADER || nFrameType & FrameTypeFlags::FOOTER ) + { + SwRect aRect( rSh.GetAnyCurRect( CurRectType::HeaderFooter, pPt)); + aRect.Pos() -= rSh.GetAnyCurRect( CurRectType::Page, pPt ).Pos(); + aLongUL.SetUpper( aRect.Top() ); + aLongUL.SetLower( nPageHeight - aRect.Bottom() ); + } + else if( nFrameType & FrameTypeFlags::DRAWOBJ) + { + const SwRect &rRect = rSh.GetObjRect(); + aLongUL.SetUpper(rRect.Top() - rPageRect.Top()); + aLongUL.SetLower(rPageRect.Bottom() - rRect.Bottom()); + } + else if(bBrowse) + { + aLongUL.SetUpper(rPagePrtRect.Top()); + aLongUL.SetLower(nPageHeight - rPagePrtRect.Bottom()); + } + rSet.Put( aLongUL ); + } + break; + + // provide top and bottom margins of current page style + case SID_ATTR_PAGE_ULSPACE: + { + const SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + SvxLongULSpaceItem aLongUL( + static_cast<long>(aUL.GetUpper()), + static_cast<long>(aUL.GetLower()), + SID_ATTR_PAGE_ULSPACE ); + + rSet.Put( aLongUL ); + } + break; + + case SID_ATTR_TABSTOP_VERTICAL : + case RES_PARATR_TABSTOP: + { + if ( dynamic_cast< const SwWebView *>( this ) != nullptr || + IsTabColFromDoc() || + IsTabRowFromDoc() || + ( nSelType & SelectionType::Graphic ) || + ( nSelType & SelectionType::Frame ) || + ( nSelType & SelectionType::Ole ) || + ( SfxItemState::DEFAULT > aCoreSet.GetItemState(RES_LR_SPACE) ) || + (!bVerticalWriting && (SID_ATTR_TABSTOP_VERTICAL == nWhich) ) || + ( bVerticalWriting && (RES_PARATR_TABSTOP == nWhich)) + ) + rSet.DisableItem( nWhich ); + else + { + SvxTabStopItem aTabStops(aCoreSet.Get( RES_PARATR_TABSTOP )); + + const SvxTabStopItem& rDefTabs = rSh.GetDefault(RES_PARATR_TABSTOP); + + OSL_ENSURE(m_pHRuler, "why is there no ruler?"); + const long nDefTabDist = ::GetTabDist(rDefTabs); + m_pHRuler->SetDefTabDist( nDefTabDist ); + m_pVRuler->SetDefTabDist( nDefTabDist ); + ::lcl_EraseDefTabs(aTabStops); + aTabStops.SetWhich(nWhich); + rSet.Put(aTabStops); + + if (comphelper::LibreOfficeKit::isActive() && nWhich == RES_PARATR_TABSTOP) + { + boost::property_tree::ptree aRootTree; + boost::property_tree::ptree aEntries; + + for (sal_uInt16 i = 0; i < aTabStops.Count(); ++i) + { + SvxTabStop const & rTabStop = aTabStops[i]; + boost::property_tree::ptree aEntry; + aEntry.put("position", convertTwipToMm100(rTabStop.GetTabPos())); + aEntry.put("type", sal_uInt16(rTabStop.GetAdjustment())); + aEntry.put("decimal", OUString(rTabStop.GetDecimal())); + aEntry.put("fill", OUString(rTabStop.GetFill())); + aEntries.push_back(std::make_pair("", aEntry)); + } + aRootTree.push_back(std::make_pair("tabstops", aEntries)); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aRootTree); + rSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TAB_STOP_LIST, aStream.str().c_str()); + } + } + break; + } + + case SID_HANGING_INDENT: + { + SfxItemState e = aCoreSet.GetItemState(RES_LR_SPACE); + if( e == SfxItemState::DISABLED ) + rSet.DisableItem(nWhich); + break; + } + + case SID_ATTR_PARA_LRSPACE_VERTICAL: + case SID_ATTR_PARA_LRSPACE: + case SID_ATTR_PARA_LEFTSPACE: + case SID_ATTR_PARA_RIGHTSPACE: + case SID_ATTR_PARA_FIRSTLINESPACE: + { + if ( nSelType & SelectionType::Graphic || + nSelType & SelectionType::Frame || + nSelType & SelectionType::Ole || + nFrameType == FrameTypeFlags::DRAWOBJ || + (!bVerticalWriting && (SID_ATTR_PARA_LRSPACE_VERTICAL == nWhich)) || + ( bVerticalWriting && (SID_ATTR_PARA_LRSPACE == nWhich)) + ) + { + rSet.DisableItem(nWhich); + } + else + { + std::shared_ptr<SvxLRSpaceItem> aLR(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE)); + if ( !IsTabColFromDoc() ) + { + aLR.reset(aCoreSet.Get(RES_LR_SPACE).Clone()); + + // #i23726# + if (m_pNumRuleNodeFromDoc) + { + short nOffset = static_cast< short >(aLR->GetTextLeft() + + // #i42922# Mouse move of numbering label + // has to consider the left indent of the paragraph + m_pNumRuleNodeFromDoc->GetLeftMarginWithNum( true ) ); + + short nFLOffset; + m_pNumRuleNodeFromDoc->GetFirstLineOfsWithNum( nFLOffset ); + + aLR->SetLeft( nOffset + nFLOffset ); + } + } + aLR->SetWhich(nWhich); + rSet.Put(*aLR); + } + break; + } + + case SID_ATTR_PARA_ULSPACE: + case SID_ATTR_PARA_ABOVESPACE: + case SID_ATTR_PARA_BELOWSPACE: + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + SvxULSpaceItem aUL = aCoreSet.Get(RES_UL_SPACE); + SfxItemState e = aCoreSet.GetItemState(RES_UL_SPACE); + if( e >= SfxItemState::DEFAULT ) + { + if ( !aUL.GetUpper() && !aUL.GetLower() ) + rSet.DisableItem( SID_PARASPACE_DECREASE ); + else if ( aUL.GetUpper() >= 5670 && aUL.GetLower() >= 5670 ) + rSet.DisableItem( SID_PARASPACE_INCREASE ); + if ( nWhich == SID_ATTR_PARA_ULSPACE + || nWhich == SID_ATTR_PARA_ABOVESPACE + || nWhich == SID_ATTR_PARA_BELOWSPACE + ) + { + aUL.SetWhich( nWhich ); + rSet.Put( aUL ); + } + } + else + { + rSet.DisableItem( SID_PARASPACE_INCREASE ); + rSet.DisableItem( SID_PARASPACE_DECREASE ); + rSet.InvalidateItem( SID_ATTR_PARA_ULSPACE ); + rSet.InvalidateItem( SID_ATTR_PARA_ABOVESPACE ); + rSet.InvalidateItem( SID_ATTR_PARA_BELOWSPACE ); + } + } + break; + + case SID_RULER_BORDER_DISTANCE: + { + m_nLeftBorderDistance = 0; + m_nRightBorderDistance = 0; + if ( nSelType & SelectionType::Graphic || + nSelType & SelectionType::Frame || + nSelType & SelectionType::Ole || + nFrameType == FrameTypeFlags::DRAWOBJ ) + rSet.DisableItem(SID_RULER_BORDER_DISTANCE); + else + { + SvxLRSpaceItem aDistLR(SID_RULER_BORDER_DISTANCE); + if(nFrameType & FrameTypeFlags::FLY_ANY) + { + if( IsTabColFromDoc() ) + { + const SwRect& rFlyPrtRect = rSh.GetAnyCurRect( CurRectType::FlyEmbeddedPrt, pPt ); + aDistLR.SetLeft(rFlyPrtRect.Left()); + aDistLR.SetRight(rFlyPrtRect.Left()); + } + else + { + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + aCoreSet.Put( aBoxInfo ); + rSh.GetFlyFrameAttr( aCoreSet ); + const SvxBoxItem& rBox = aCoreSet.Get(RES_BOX); + aDistLR.SetLeft(rBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(rBox.GetDistance(SvxBoxItemLine::RIGHT)); + + //add the paragraph border distance + SfxItemSet aCoreSet1( GetPool(), svl::Items<RES_BOX, RES_BOX>{} ); + rSh.GetCurAttr( aCoreSet1 ); + const SvxBoxItem& rParaBox = aCoreSet1.Get(RES_BOX); + aDistLR.SetLeft(aDistLR.GetLeft() + rParaBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(aDistLR.GetRight() + rParaBox.GetDistance(SvxBoxItemLine::RIGHT)); + } + rSet.Put(aDistLR); + m_nLeftBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetLeft()); + m_nRightBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetRight()); + } + else if ( IsTabColFromDoc() || + ( rSh.GetTableFormat() && !bFrameSelection && + !(nFrameType & FrameTypeFlags::COLSECT ) ) ) + { + SfxItemSet aCoreSet2( GetPool(), + svl::Items<RES_BOX, RES_BOX, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>{} ); + SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER ); + aBoxInfo.SetTable(false); + aBoxInfo.SetDist(true); + aCoreSet2.Put(aBoxInfo); + rSh.GetTabBorders( aCoreSet2 ); + const SvxBoxItem& rBox = aCoreSet2.Get(RES_BOX); + aDistLR.SetLeft(rBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(rBox.GetDistance(SvxBoxItemLine::RIGHT)); + + //add the border distance of the paragraph + SfxItemSet aCoreSet1( GetPool(), svl::Items<RES_BOX, RES_BOX>{} ); + rSh.GetCurAttr( aCoreSet1 ); + const SvxBoxItem& rParaBox = aCoreSet1.Get(RES_BOX); + aDistLR.SetLeft(aDistLR.GetLeft() + rParaBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(aDistLR.GetRight() + rParaBox.GetDistance(SvxBoxItemLine::RIGHT)); + rSet.Put(aDistLR); + m_nLeftBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetLeft()); + m_nRightBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetRight()); + } + else if ( !rSh.IsDirectlyInSection() ) + { + //get the page/header/footer border distance + const SwFrameFormat& rMaster = rDesc.GetMaster(); + const SvxBoxItem& rBox = rMaster.GetAttrSet().Get(RES_BOX); + aDistLR.SetLeft(rBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(rBox.GetDistance(SvxBoxItemLine::RIGHT)); + + const SvxBoxItem* pBox = nullptr; + if(nFrameType & FrameTypeFlags::HEADER) + { + rMaster.GetHeader(); + const SwFormatHeader& rHeaderFormat = rMaster.GetHeader(); + SwFrameFormat *pHeaderFormat = const_cast<SwFrameFormat*>(rHeaderFormat.GetHeaderFormat()); + if( pHeaderFormat )// #i80890# if rDesc is not the one belonging to the current page is might crash + pBox = & pHeaderFormat->GetBox(); + } + else if(nFrameType & FrameTypeFlags::FOOTER ) + { + const SwFormatFooter& rFooterFormat = rMaster.GetFooter(); + SwFrameFormat *pFooterFormat = const_cast<SwFrameFormat*>(rFooterFormat.GetFooterFormat()); + if( pFooterFormat )// #i80890# if rDesc is not the one belonging to the current page is might crash + pBox = & pFooterFormat->GetBox(); + } + if(pBox) + { + aDistLR.SetLeft(pBox->GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(pBox->GetDistance(SvxBoxItemLine::RIGHT)); + } + + //add the border distance of the paragraph + SfxItemSet aCoreSetTmp( GetPool(), svl::Items<RES_BOX, RES_BOX>{} ); + rSh.GetCurAttr( aCoreSetTmp ); + const SvxBoxItem& rParaBox = aCoreSetTmp.Get(RES_BOX); + aDistLR.SetLeft(aDistLR.GetLeft() + rParaBox.GetDistance(SvxBoxItemLine::LEFT)); + aDistLR.SetRight(aDistLR.GetRight() + rParaBox.GetDistance(SvxBoxItemLine::RIGHT)); + rSet.Put(aDistLR); + m_nLeftBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetLeft()); + m_nRightBorderDistance = static_cast< sal_uInt16 >(aDistLR.GetRight()); + } + } + } + break; + + case SID_RULER_TEXT_RIGHT_TO_LEFT: + { + if ( nSelType & SelectionType::Graphic || + nSelType & SelectionType::Frame || + nSelType & SelectionType::Ole || + nFrameType == FrameTypeFlags::DRAWOBJ) + rSet.DisableItem(nWhich); + else + { + bool bFlag = rSh.IsInRightToLeftText(); + rSet.Put(SfxBoolItem(nWhich, bFlag)); + } + } + break; + + case SID_RULER_BORDERS_VERTICAL: + case SID_RULER_BORDERS: + { + bool bFrameHasVerticalColumns(false); + { + bool bFrameRTL; + bool bFrameVertL2R; + bFrameHasVerticalColumns = rSh.IsFrameVertical(false, bFrameRTL, bFrameVertL2R) && + bFrameSelection; + } + bool bHasTable = ( IsTabColFromDoc() || + ( rSh.GetTableFormat() && !bFrameSelection && + !(nFrameType & FrameTypeFlags::COLSECT ) ) ); + + bool bTableVertical = bHasTable && rSh.IsTableVertical(); + + if(((SID_RULER_BORDERS_VERTICAL == nWhich) && + ((bHasTable && !bTableVertical) || + (!bVerticalWriting && !bFrameSelection && !bHasTable ) || + ( bFrameSelection && !bFrameHasVerticalColumns))) || + ((SID_RULER_BORDERS == nWhich) && + ((bHasTable && bTableVertical) || + (bVerticalWriting && !bFrameSelection&& !bHasTable) || bFrameHasVerticalColumns))) + rSet.DisableItem(nWhich); + else if ( bHasTable ) + { + SwTabCols aTabCols; + size_t nNum = 0; + m_bSetTabColFromDoc = IsTabColFromDoc(); + if ( m_bSetTabColFromDoc ) + { + rSh.GetMouseTabCols( aTabCols, m_aTabColFromDocPos ); + nNum = rSh.GetCurMouseTabColNum( m_aTabColFromDocPos ); + } + else + { + rSh.GetTabCols( aTabCols ); + nNum = rSh.GetCurTabColNum(); + if(rSh.IsTableRightToLeft()) + nNum = aTabCols.Count() - nNum; + } + + OSL_ENSURE(nNum <= aTabCols.Count(), "TabCol not found"); + const int nLft = aTabCols.GetLeftMin() + aTabCols.GetLeft(); + const int nRgt = (bTableVertical ? nPageHeight : nPageWidth) - + (aTabCols.GetLeftMin() + aTabCols.GetRight()); + + const sal_uInt16 nL = static_cast< sal_uInt16 >(std::max(nLft, 0)); + const sal_uInt16 nR = static_cast< sal_uInt16 >(std::max(nRgt, 0)); + + SvxColumnItem aColItem(nNum, nL, nR); + + long nStart = 0; + long nEnd = 0; + + //columns in right-to-left tables need to be mirrored + bool bIsTableRTL = + IsTabColFromDoc() ? + rSh.IsMouseTableRightToLeft(m_aTabColFromDocPos) + : rSh.IsTableRightToLeft(); + if(bIsTableRTL) + { + for ( size_t i = aTabCols.Count(); i; --i ) + { + const SwTabColsEntry& rEntry = aTabCols.GetEntry( i - 1 ); + nEnd = aTabCols.GetRight() - rEntry.nPos; + SvxColumnDescription aColDesc( nStart, nEnd, + aTabCols.GetRight() - rEntry.nMax, + aTabCols.GetRight() - rEntry.nMin, + !aTabCols.IsHidden(i - 1) ); + aColItem.Append(aColDesc); + nStart = nEnd; + } + SvxColumnDescription aColDesc(nStart, + aTabCols.GetRight() - aTabCols.GetLeft(), true); + aColItem.Append(aColDesc); + } + else + { + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + { + const SwTabColsEntry& rEntry = aTabCols.GetEntry( i ); + nEnd = rEntry.nPos - aTabCols.GetLeft(); + SvxColumnDescription aColDesc( nStart, nEnd, + rEntry.nMin - aTabCols.GetLeft(), rEntry.nMax - aTabCols.GetLeft(), + !aTabCols.IsHidden(i) ); + aColItem.Append(aColDesc); + nStart = nEnd; + } + SvxColumnDescription aColDesc(nStart, aTabCols.GetRight() - aTabCols.GetLeft(), + 0, 0, true); + aColItem.Append(aColDesc); + } + aColItem.SetWhich(nWhich); + rSet.Put(aColItem); + } + else if ( bFrameSelection || nFrameType & ( FrameTypeFlags::COLUMN | FrameTypeFlags::COLSECT ) ) + { + // Out of frame or page? + sal_uInt16 nNum = 0; + if(bFrameSelection) + { + const SwFrameFormat* pFormat = rSh.GetFlyFrameFormat(); + if(pFormat) + nNum = pFormat->GetCol().GetNumCols(); + } + else + nNum = rSh.GetCurColNum(); + + if( + // For that matter FrameTypeFlags::COLSECT should not be included + // if the border is selected! + !bFrameSelection && + nFrameType & FrameTypeFlags::COLSECT ) + { + const SwSection *pSect = rSh.GetAnySection(false, pPt); + OSL_ENSURE( pSect, "Which section?"); + if( pSect ) + { + SwSectionFormat const *pFormat = pSect->GetFormat(); + const SwFormatCol& rCol = pFormat->GetCol(); + if (rSh.IsColRightToLeft()) + nNum = rCol.GetColumns().size() - nNum; + else + --nNum; + SvxColumnItem aColItem(nNum); + SwRect aRect = rSh.GetAnyCurRect(CurRectType::SectionPrt, pPt); + const SwRect aTmpRect = rSh.GetAnyCurRect(CurRectType::Section, pPt); + + ::lcl_FillSvxColumn(rCol, bVerticalWriting ? aRect.Height() : aRect.Width(), aColItem, 0); + + if(bVerticalWriting) + { + aRect.Pos() += Point(aTmpRect.Left(), aTmpRect.Top()); + aRect.Pos().AdjustY( -(rPageRect.Top()) ); + aColItem.SetLeft(aRect.Top()); + aColItem.SetRight(nPageHeight - aRect.Bottom()); + } + else + { + aRect.Pos() += aTmpRect.Pos(); + + // make relative to page position: + aColItem.SetLeft (static_cast<sal_uInt16>( aRect.Left() - rPageRect.Left() )); + aColItem.SetRight(static_cast<sal_uInt16>( rPageRect.Right() - aRect.Right())); + } + aColItem.SetOrtho(aColItem.CalcOrtho()); + + aColItem.SetWhich(nWhich); + rSet.Put(aColItem); + } + } + else if( bFrameSelection || nFrameType & FrameTypeFlags::FLY_ANY ) + { + // Columns in frame + if ( nNum ) + { + const SwFrameFormat* pFormat = rSh.GetFlyFrameFormat() ; + + const SwFormatCol& rCol = pFormat->GetCol(); + if (rSh.IsColRightToLeft()) + nNum = rCol.GetColumns().size() - nNum; + else + nNum--; + SvxColumnItem aColItem(nNum); + const SwRect &rSizeRect = rSh.GetAnyCurRect(CurRectType::FlyEmbeddedPrt, pPt); + + bool bUseVertical = bFrameHasVerticalColumns || (!bFrameSelection && bVerticalWriting); + const long lWidth = bUseVertical ? rSizeRect.Height() : rSizeRect.Width(); + const SwRect &rRect = rSh.GetAnyCurRect(CurRectType::FlyEmbedded, pPt); + long nDist2 = ((bUseVertical ? rRect.Height() : rRect.Width()) - lWidth) /2; + ::lcl_FillSvxColumn(rCol, lWidth, aColItem, nDist2); + + if(bUseVertical) + { + aColItem.SetLeft(rRect.Top()- rPageRect.Top()); + aColItem.SetRight(nPageHeight + rPageRect.Top() - rRect.Bottom()); + } + else + { + aColItem.SetLeft(rRect.Left() - rPageRect.Left()); + aColItem.SetRight(rPageRect.Right() - rRect.Right()); + } + + aColItem.SetOrtho(aColItem.CalcOrtho()); + + aColItem.SetWhich(nWhich); + rSet.Put(aColItem); + } + else + rSet.DisableItem(nWhich); + } + else + { // Columns on the page + const SwFrameFormat& rMaster = rDesc.GetMaster(); + SwFormatCol aCol(rMaster.GetCol()); + if(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB) + nNum = aCol.GetColumns().size() - nNum; + else + nNum--; + + SvxColumnItem aColItem(nNum); + const SwRect aPrtRect = rSh.GetAnyCurRect(CurRectType::PagePrt, pPt); + const SvxBoxItem& rBox = rMaster.GetFormatAttr(RES_BOX); + long nDist = rBox.GetSmallestDistance(); + + lcl_FillSvxColumn( + aCol, + bVerticalWriting ? aPrtRect.Height() : aPrtRect.Width(), + aColItem, nDist); + + if(bBrowse) + { + if (bVerticalWriting) + { + aColItem.SetLeft(static_cast<sal_uInt16>(rPagePrtRect.Top())); + aColItem.SetRight(sal_uInt16(nPageHeight - rPagePrtRect.Bottom())); + } + else + { + aColItem.SetLeft(static_cast<sal_uInt16>(rPagePrtRect.Left())); + aColItem.SetRight(sal_uInt16(nPageWidth - rPagePrtRect.Right())); + } + } + else + { + if (bVerticalWriting) + { + SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + aColItem.SetLeft (aUL.GetUpper()); + aColItem.SetRight(aUL.GetLower()); + } + else + { + aColItem.SetLeft (aPageLRSpace.GetLeft()); + aColItem.SetRight(aPageLRSpace.GetRight()); + } + } + aColItem.SetOrtho(aColItem.CalcOrtho()); + + aColItem.SetWhich(nWhich); + rSet.Put(aColItem); + } + } + else + rSet.DisableItem(nWhich); + break; + } + + case SID_RULER_ROWS : + case SID_RULER_ROWS_VERTICAL: + { + bool bFrameHasVerticalColumns(false); + { + bool bFrameRTL; + bool bFrameVertL2R; + bFrameHasVerticalColumns = rSh.IsFrameVertical(false, bFrameRTL, bFrameVertL2R) && + bFrameSelection; + } + + if(((SID_RULER_ROWS == nWhich) && + ((!bVerticalWriting && !bFrameSelection) || (bFrameSelection && !bFrameHasVerticalColumns))) || + ((SID_RULER_ROWS_VERTICAL == nWhich) && + ((bVerticalWriting && !bFrameSelection) || bFrameHasVerticalColumns))) + rSet.DisableItem(nWhich); + else if ( IsTabRowFromDoc() || + ( rSh.GetTableFormat() && !bFrameSelection && + !(nFrameType & FrameTypeFlags::COLSECT ) ) ) + { + SwTabCols aTabCols; + m_bSetTabRowFromDoc = IsTabRowFromDoc(); + if ( m_bSetTabRowFromDoc ) + { + rSh.GetMouseTabRows( aTabCols, m_aTabColFromDocPos ); + } + else + { + rSh.GetTabRows( aTabCols ); + } + + const int nLft = aTabCols.GetLeftMin(); + const int nRgt = (bVerticalWriting ? nPageWidth : nPageHeight) - + (aTabCols.GetLeftMin() + aTabCols.GetRight()); + + const sal_uInt16 nL = static_cast< sal_uInt16 >(std::max(nLft, 0)); + const sal_uInt16 nR = static_cast< sal_uInt16 >(std::max(nRgt, 0)); + + SvxColumnItem aColItem(0, nL, nR); + + long nStart = 0; + long nEnd = 0; + + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + { + const SwTabColsEntry& rEntry = aTabCols.GetEntry( i ); + if(bVerticalWriting) + { + nEnd = aTabCols.GetRight() - rEntry.nPos; + SvxColumnDescription aColDesc( nStart, nEnd, + std::max(0L, aTabCols.GetRight() - rEntry.nMax), + std::max(0L, aTabCols.GetRight() - rEntry.nMin), + !aTabCols.IsHidden(i) ); + aColItem.Append(aColDesc); + } + else + { + nEnd = rEntry.nPos - aTabCols.GetLeft(); + SvxColumnDescription aColDesc( nStart, nEnd, + rEntry.nMin - aTabCols.GetLeft(), + rEntry.nMax - aTabCols.GetLeft(), + !aTabCols.IsHidden(i) ); + aColItem.Append(aColDesc); + } + nStart = nEnd; + } + if(bVerticalWriting) + nEnd = aTabCols.GetRight(); + else + nEnd = aTabCols.GetLeft(); + + SvxColumnDescription aColDesc( nStart, nEnd, + aTabCols.GetRight(), + aTabCols.GetRight(), + false ); + aColItem.Append(aColDesc); + + aColItem.SetWhich(nWhich); + rSet.Put(aColItem); + } + else + rSet.DisableItem(nWhich); + } + break; + + case SID_RULER_PAGE_POS: + { + SvxPagePosSizeItem aPagePosSize( + Point( rPageRect.Left(), rPageRect.Top()) , nPageWidth, nPageHeight); + + rSet.Put(aPagePosSize); + break; + } + + case SID_RULER_LR_MIN_MAX: + { + tools::Rectangle aRectangle; + if( ( nFrameType & FrameTypeFlags::COLSECT ) && !IsTabColFromDoc() && + ( nFrameType & ( FrameTypeFlags::TABLE|FrameTypeFlags::COLUMN ) ) ) + { + if( nFrameType & FrameTypeFlags::TABLE ) + { + const size_t nNum = rSh.GetCurTabColNum(); + SwTabCols aTabCols; + rSh.GetTabCols( aTabCols ); + + const int nLft = aTabCols.GetLeftMin() + aTabCols.GetLeft(); + const int nRgt = nPageWidth -(aTabCols.GetLeftMin() + aTabCols.GetRight()); + + const sal_uInt16 nL = static_cast< sal_uInt16 >(std::max(nLft, 0)); + const sal_uInt16 nR = static_cast< sal_uInt16 >(std::max(nRgt, 0)); + + aRectangle.SetLeft( nL ); + if(nNum > 1) + aRectangle.AdjustLeft(aTabCols[nNum - 2] ); + if(nNum) + aRectangle.AdjustLeft(MINLAY ); + if(aTabCols.Count() <= nNum + 1 ) + aRectangle.SetRight( nR ); + else + aRectangle.SetRight( nPageWidth - (nL + aTabCols[nNum + 1]) ); + + if(nNum < aTabCols.Count()) + aRectangle.AdjustRight(MINLAY ); + } + else + { + const SwFrameFormat* pFormat = rSh.GetFlyFrameFormat(); + const SwFormatCol* pCols = pFormat ? &pFormat->GetCol(): + &rDesc.GetMaster().GetCol(); + const SwColumns& rCols = pCols->GetColumns(); + sal_uInt16 nNum = rSh.GetCurOutColNum(); + const sal_uInt16 nCount = std::min(sal_uInt16(nNum + 1), sal_uInt16(rCols.size())); + const SwRect aRect( rSh.GetAnyCurRect( pFormat + ? CurRectType::FlyEmbeddedPrt + : CurRectType::PagePrt, pPt )); + const SwRect aAbsRect( rSh.GetAnyCurRect( pFormat + ? CurRectType::FlyEmbedded + : CurRectType::Page, pPt )); + + // The width of the frame or within the page margins. + const sal_uInt16 nTotalWidth = static_cast<sal_uInt16>(aRect.Width()); + // The entire frame width - The difference is twice the distance to the edge. + const sal_uInt16 nOuterWidth = static_cast<sal_uInt16>(aAbsRect.Width()); + int nWidth = 0, + nEnd = 0; + aRectangle.SetLeft( 0 ); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + { + const SwColumn* pCol = &rCols[i]; + const int nStart = pCol->GetLeft() + nWidth; + if(i == nNum - 2) + aRectangle.SetLeft( nStart ); + nWidth += pCols->CalcColWidth( i, nTotalWidth ); + nEnd = nWidth - pCol->GetRight(); + } + aRectangle.SetRight( rPageRect.Right() - nEnd ); + aRectangle.AdjustLeft( -(rPageRect.Left()) ); + + if(nNum > 1) + { + aRectangle.AdjustLeft(MINLAY ); + aRectangle.AdjustLeft(aRect.Left() ); + } + if(pFormat) // Range in frame - here you may up to the edge + { + aRectangle.SetLeft(0); + aRectangle.SetRight(0); + } + else + { + // Move the rectangle to the correct absolute position. + aRectangle.AdjustLeft(aAbsRect.Left() ); + aRectangle.AdjustRight( -(aAbsRect.Left()) ); + // Include distance to the border. + aRectangle.AdjustRight( -((nOuterWidth - nTotalWidth) / 2) ); + } + + if(nNum < rCols.size()) + { + aRectangle.AdjustRight(MINLAY ); + } + else + // Right is only the margin now. + aRectangle.SetRight( 0 ); + + } + } + else if ( ((nFrameType & FrameTypeFlags::TABLE) || IsTabColFromDoc()) && + !bFrameSelection ) + { + bool bColumn; + if ( IsTabColFromDoc() ) + bColumn = rSh.GetCurMouseColNum( m_aTabColFromDocPos ) != 0; + else + bColumn = bool(nFrameType & (FrameTypeFlags::COLUMN|FrameTypeFlags::FLY_ANY|FrameTypeFlags::COLSECTOUTTAB)); + + if ( !bColumn ) + { + if( nFrameType & FrameTypeFlags::FLY_ANY && IsTabColFromDoc() ) + { + SwRect aRect( rSh.GetAnyCurRect( + CurRectType::FlyEmbeddedPrt, pPt ) ); + aRect.Pos() += rSh.GetAnyCurRect( CurRectType::FlyEmbedded, + pPt ).Pos(); + + aRectangle.SetLeft( aRect.Left() - rPageRect.Left() ); + aRectangle.SetRight( rPageRect.Right() - aRect.Right() ); + } + else if( bBrowse ) + { + aRectangle.SetLeft( rPagePrtRect.Left() ); + aRectangle.SetRight( nPageWidth - rPagePrtRect.Right() ); + } + else + { + aRectangle.SetLeft( aPageLRSpace.GetLeft() ); + aRectangle.SetRight( aPageLRSpace.GetRight() ); + } + } + else + { // Here only for table in multi-column pages and borders. + bool bSectOutTable = bool(nFrameType & FrameTypeFlags::TABLE); + bool bFrame = bool(nFrameType & FrameTypeFlags::FLY_ANY); + bool bColSct = bool(nFrameType & ( bSectOutTable + ? FrameTypeFlags::COLSECTOUTTAB + : FrameTypeFlags::COLSECT ) + ); + //So you can also drag with the mouse, without being in the table. + CurRectType eRecType = CurRectType::PagePrt; + size_t nNum = IsTabColFromDoc() ? + rSh.GetCurMouseColNum( m_aTabColFromDocPos ): + rSh.GetCurOutColNum(); + const SwFrameFormat* pFormat = nullptr; + if( bColSct ) + { + eRecType = bSectOutTable ? CurRectType::SectionOutsideTable + : CurRectType::Section; + const SwSection *pSect = rSh.GetAnySection( bSectOutTable, pPt ); + OSL_ENSURE( pSect, "Which section?"); + pFormat = pSect->GetFormat(); + } + else if( bFrame ) + { + pFormat = rSh.GetFlyFrameFormat(); + eRecType = CurRectType::FlyEmbeddedPrt; + } + + const SwFormatCol* pCols = pFormat ? &pFormat->GetCol(): + &rDesc.GetMaster().GetCol(); + const SwColumns& rCols = pCols->GetColumns(); + const sal_uInt16 nBorder = pFormat + ? pFormat->GetBox().GetSmallestDistance() + : rDesc.GetMaster().GetBox().GetSmallestDistance(); + + // RECT_FLY_PRT_EMBEDDED returns the relative position to RECT_FLY_EMBEDDED + // the absolute position must be added here + + SwRect aRect( rSh.GetAnyCurRect( eRecType, pPt ) ); + if(CurRectType::FlyEmbeddedPrt == eRecType) + aRect.Pos() += rSh.GetAnyCurRect( CurRectType::FlyEmbedded, + pPt ).Pos(); + + const sal_uInt16 nTotalWidth = static_cast<sal_uInt16>(aRect.Width()); + // Initialize nStart and nEnd for nNum == 0 + int nWidth = 0, + nStart = 0, + nEnd = nTotalWidth; + + if( nNum > rCols.size() ) + { + OSL_ENSURE( false, "wrong FormatCol is being edited!" ); + nNum = rCols.size(); + } + + for( size_t i = 0; i < nNum; ++i ) + { + const SwColumn* pCol = &rCols[i]; + nStart = pCol->GetLeft() + nWidth; + nWidth += pCols->CalcColWidth( static_cast<sal_uInt16>(i), nTotalWidth ); + nEnd = nWidth - pCol->GetRight(); + } + if( bFrame || bColSct ) + { + aRectangle.SetLeft( aRect.Left() - rPageRect.Left() + nStart ); + aRectangle.SetRight( nPageWidth - aRectangle.Left() - nEnd + nStart ); + } + else if(!bBrowse) + { + aRectangle.SetLeft( aPageLRSpace.GetLeft() + nStart ); + aRectangle.SetRight( nPageWidth - nEnd - aPageLRSpace.GetLeft() ); + } + else + { + long nLeft = rPagePrtRect.Left(); + aRectangle.SetLeft( nStart + nLeft ); + aRectangle.SetRight( nPageWidth - nEnd - nLeft ); + } + if(!bFrame) + { + aRectangle.AdjustLeft(nBorder ); + aRectangle.AdjustRight( -nBorder ); + } + } + } + else if ( nFrameType & ( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER )) + { + aRectangle.SetLeft( aPageLRSpace.GetLeft() ); + aRectangle.SetRight( aPageLRSpace.GetRight() ); + } + else + { + aRectangle.SetLeft(0); + aRectangle.SetRight(0); + } + + SfxRectangleItem aLR( SID_RULER_LR_MIN_MAX , aRectangle); + rSet.Put(aLR); + } + break; + + case SID_RULER_PROTECT: + { + if(bFrameSelection) + { + FlyProtectFlags nProtect = m_pWrtShell->IsSelObjProtected( FlyProtectFlags::Size|FlyProtectFlags::Pos|FlyProtectFlags::Content ); + + SvxProtectItem aProt(SID_RULER_PROTECT); + aProt.SetContentProtect(bool(nProtect & FlyProtectFlags::Content)); + aProt.SetSizeProtect (bool(nProtect & FlyProtectFlags::Size)); + aProt.SetPosProtect (bool(nProtect & FlyProtectFlags::Pos)); + rSet.Put(aProt); + } + else + { + SvxProtectItem aProtect(SID_RULER_PROTECT); + if(bBrowse && !(nFrameType & (FrameTypeFlags::DRAWOBJ|FrameTypeFlags::COLUMN)) && !rSh.GetTableFormat()) + { + aProtect.SetSizeProtect(true); + aProtect.SetPosProtect(true); + } + rSet.Put(aProtect); + } + } + break; + + case SID_ATTR_PAGE_HEADER: + case SID_ATTR_PAGE_HEADER_LRMARGIN: + case SID_ATTR_PAGE_HEADER_SPACING: + case SID_ATTR_PAGE_HEADER_LAYOUT: + { + const SwFormatHeader& rHeader = rDesc.GetMaster().GetHeader(); + bool bHeaderOn = rHeader.IsActive(); + rSet.Put( SfxBoolItem(SID_ATTR_PAGE_HEADER, bHeaderOn ) ); + if(bHeaderOn) + { + const SvxLRSpaceItem* rLR = static_cast<const SvxLRSpaceItem*>( + rHeader.GetHeaderFormat()->GetAttrSet().GetItem(SID_ATTR_LRSPACE)); + const SvxULSpaceItem* rUL = static_cast<const SvxULSpaceItem*>( + rHeader.GetHeaderFormat()->GetAttrSet().GetItem(SID_ATTR_ULSPACE)); + SvxLongLRSpaceItem aLR(rLR->GetLeft(), rLR->GetRight(), SID_ATTR_PAGE_HEADER_LRMARGIN); + rSet.Put(aLR); + SvxLongULSpaceItem aUL( rUL->GetUpper(), rUL->GetLower(), SID_ATTR_PAGE_HEADER_SPACING); + rSet.Put(aUL); + + bool bShared = !rDesc.IsHeaderShared(); + bool bFirst = !rDesc.IsFirstShared(); // FIXME control changes for both header footer - tdf#100287 + sal_uInt16 nLayout = (static_cast<int>(bShared)<<1) + static_cast<int>(bFirst); + SfxInt16Item aLayoutItem(SID_ATTR_PAGE_HEADER_LAYOUT, nLayout); + rSet.Put(aLayoutItem); + } + } + break; + case SID_ATTR_PAGE_FOOTER: + case SID_ATTR_PAGE_FOOTER_LRMARGIN: + case SID_ATTR_PAGE_FOOTER_SPACING: + case SID_ATTR_PAGE_FOOTER_LAYOUT: + { + const SwFormatFooter& rFooter = rDesc.GetMaster().GetFooter(); + bool bFooterOn = rFooter.IsActive(); + rSet.Put( SfxBoolItem(SID_ATTR_PAGE_FOOTER, bFooterOn ) ); + if(bFooterOn) + { + const SvxLRSpaceItem* rLR = rFooter.GetFooterFormat()->GetAttrSet().GetItem<SvxLRSpaceItem>(SID_ATTR_LRSPACE); + const SvxULSpaceItem* rUL = rFooter.GetFooterFormat()->GetAttrSet().GetItem<SvxULSpaceItem>(SID_ATTR_ULSPACE); + SvxLongLRSpaceItem aLR(rLR->GetLeft(), rLR->GetRight(), SID_ATTR_PAGE_FOOTER_LRMARGIN); + rSet.Put(aLR); + SvxLongULSpaceItem aUL( rUL->GetUpper(), rUL->GetLower(), SID_ATTR_PAGE_FOOTER_SPACING); + rSet.Put(aUL); + + bool bShared = !rDesc.IsFooterShared(); + bool bFirst = !rDesc.IsFirstShared(); // FIXME control changes for both header footer - tdf#100287 + sal_uInt16 nLayout = (static_cast<int>(bShared)<<1) + static_cast<int>(bFirst); + SfxInt16Item aLayoutItem(SID_ATTR_PAGE_FOOTER_LAYOUT, nLayout); + rSet.Put(aLayoutItem); + } + } + break; + + case SID_ATTR_PAGE_COLOR: + case SID_ATTR_PAGE_FILLSTYLE: + case SID_ATTR_PAGE_GRADIENT: + case SID_ATTR_PAGE_HATCH: + case SID_ATTR_PAGE_BITMAP: + { + SfxItemSet aSet = rDesc.GetMaster().GetAttrSet(); + if (const auto pFillStyleItem = aSet.GetItem(XATTR_FILLSTYLE)) + { + drawing::FillStyle eXFS = pFillStyleItem->GetValue(); + XFillStyleItem aFillStyleItem( eXFS ); + aFillStyleItem.SetWhich( SID_ATTR_PAGE_FILLSTYLE ); + rSet.Put(aFillStyleItem); + + switch(eXFS) + { + case drawing::FillStyle_SOLID: + { + if (const auto pItem = aSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false)) + { + Color aColor = pItem->GetColorValue(); + XFillColorItem aFillColorItem( OUString(), aColor ); + aFillColorItem.SetWhich( SID_ATTR_PAGE_COLOR ); + rSet.Put( aFillColorItem ); + } + break; + } + + case drawing::FillStyle_GRADIENT: + { + const XGradient& xGradient = aSet.GetItem<XFillGradientItem>( XATTR_FILLGRADIENT )->GetGradientValue(); + XFillGradientItem aFillGradientItem( OUString(), xGradient, SID_ATTR_PAGE_GRADIENT ); + rSet.Put( aFillGradientItem ); + } + break; + + case drawing::FillStyle_HATCH: + { + const XFillHatchItem *pFillHatchItem( aSet.GetItem<XFillHatchItem>( XATTR_FILLHATCH ) ); + XFillHatchItem aFillHatchItem( pFillHatchItem->GetName(), pFillHatchItem->GetHatchValue()); + aFillHatchItem.SetWhich( SID_ATTR_PAGE_HATCH ); + rSet.Put( aFillHatchItem ); + } + break; + + case drawing::FillStyle_BITMAP: + { + const XFillBitmapItem *pFillBitmapItem = aSet.GetItem<XFillBitmapItem>( XATTR_FILLBITMAP ); + XFillBitmapItem aFillBitmapItem( pFillBitmapItem->GetName(), pFillBitmapItem->GetGraphicObject() ); + aFillBitmapItem.SetWhich( SID_ATTR_PAGE_BITMAP ); + rSet.Put( aFillBitmapItem ); + } + break; + case drawing::FillStyle_NONE: + { + } + break; + + default: + break; + } + } + break; + } + + } + nWhich = aIter.NextWhich(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/SwXDocumentSettings.cxx b/sw/source/uibase/uno/SwXDocumentSettings.cxx new file mode 100644 index 000000000..a644a019c --- /dev/null +++ b/sw/source/uibase/uno/SwXDocumentSettings.cxx @@ -0,0 +1,1508 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sal/config.h> +#include <sal/log.hxx> + +#include <utility> + +#include <o3tl/any.hxx> +#include "SwXDocumentSettings.hxx" +#include <comphelper/MasterPropertySetInfo.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/i18n/XForbiddenCharacters.hpp> +#include <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <docsh.hxx> +#include <fldupde.hxx> +#include <linkenum.hxx> +#include <sfx2/printer.hxx> +#include <editsh.hxx> +#include <unotxdoc.hxx> +#include <cmdid.h> +#include <unomod.hxx> +#include <vcl/svapp.hxx> +#include <svl/asiancfg.hxx> +#include <tools/stream.hxx> + +#include <cfgitems.hxx> +#include <dbmgr.hxx> + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; + +namespace { + +enum SwDocumentSettingsPropertyHandles +{ + HANDLE_FORBIDDEN_CHARS, + HANDLE_LINK_UPDATE_MODE, + HANDLE_FIELD_AUTO_UPDATE, + HANDLE_CHART_AUTO_UPDATE, + HANDLE_ADD_PARA_TABLE_SPACING, + HANDLE_ADD_PARA_TABLE_SPACING_AT_START, + HANDLE_ALIGN_TAB_STOP_POSITION, + HANDLE_PRINTER_NAME, + HANDLE_PRINTER_SETUP, + HANDLE_PRINTER_PAPER, + HANDLE_IS_KERN_ASIAN_PUNCTUATION, + HANDLE_CHARACTER_COMPRESSION_TYPE, + HANDLE_APPLY_USER_DATA, + HANDLE_SAVE_THUMBNAIL, + HANDLE_SAVE_GLOBAL_DOCUMENT_LINKS, + HANDLE_CURRENT_DATABASE_DATA_SOURCE, + HANDLE_CURRENT_DATABASE_COMMAND, + HANDLE_CURRENT_DATABASE_COMMAND_TYPE, + HANDLE_EMBEDDED_DATABASE_NAME, + HANDLE_SAVE_VERSION_ON_CLOSE, + HANDLE_UPDATE_FROM_TEMPLATE, + HANDLE_PRINTER_INDEPENDENT_LAYOUT, + HANDLE_IS_LABEL_DOC, + HANDLE_IS_ADD_FLY_OFFSET, + HANDLE_IS_ADD_VERTICAL_FLY_OFFSET, + HANDLE_IS_ADD_EXTERNAL_LEADING, + HANDLE_OLD_NUMBERING, + HANDLE_OUTLINELEVEL_YIELDS_NUMBERING, + /* Stampit It disable the print cancel button of the shown progress dialog. */ + HANDLE_ALLOW_PRINTJOB_CANCEL, + HANDLE_USE_FORMER_LINE_SPACING, + HANDLE_ADD_PARA_SPACING_TO_TABLE_CELLS, + HANDLE_ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, + HANDLE_USE_FORMER_OBJECT_POSITIONING, + HANDLE_USE_FORMER_TEXT_WRAPPING, + HANDLE_CHANGES_PASSWORD, + HANDLE_CONSIDER_WRAP_ON_OBJPOS, + HANDLE_IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, + HANDLE_DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, + HANDLE_DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, + HANDLE_TABLE_ROW_KEEP, + HANDLE_IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, + HANDLE_LOAD_READONLY, + HANDLE_DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, + HANDLE_CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAMES, + HANDLE_UNIX_FORCE_ZERO_EXT_LEADING, + HANDLE_USE_OLD_PRINTER_METRICS, + HANDLE_PROTECT_FORM, + HANDLE_MS_WORD_COMP_TRAILING_BLANKS, + HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, + HANDLE_TABS_RELATIVE_TO_INDENT, + HANDLE_RSID, + HANDLE_RSID_ROOT, + HANDLE_TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST, + HANDLE_MODIFYPASSWORDINFO, + HANDLE_MATH_BASELINE_ALIGNMENT, + HANDLE_INVERT_BORDER_SPACING, + HANDLE_COLLAPSE_EMPTY_CELL_PARA, + HANDLE_SMALL_CAPS_PERCENTAGE_66, + HANDLE_TAB_OVERFLOW, + HANDLE_UNBREAKABLE_NUMBERINGS, + HANDLE_STYLES_NODEFAULT, + HANDLE_FLOATTABLE_NOMARGINS, + HANDLE_CLIPPED_PICTURES, + HANDLE_BACKGROUND_PARA_OVER_DRAWINGS, + HANDLE_EMBED_FONTS, + HANDLE_EMBED_USED_FONTS, + HANDLE_EMBED_LATIN_SCRIPT_FONTS, + HANDLE_EMBED_ASIAN_SCRIPT_FONTS, + HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, + HANDLE_EMBED_SYSTEM_FONTS, + HANDLE_TAB_OVER_MARGIN, + HANDLE_TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK, + HANDLE_SURROUND_TEXT_WRAP_SMALL, + HANDLE_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, + HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE, + HANDLE_SUBTRACT_FLYS, + HANDLE_DISABLE_OFF_PAGE_POSITIONING, + HANDLE_EMPTY_DB_FIELD_HIDES_PARA, + HANDLE_CONTINUOUS_ENDNOTES, + HANDLE_PROTECT_BOOKMARKS, + HANDLE_PROTECT_FIELDS, + HANDLE_HEADER_SPACING_BELOW_LAST_PARA, +}; + +} + +static MasterPropertySetInfo * lcl_createSettingsInfo() +{ + static PropertyInfo const aWriterSettingsInfoMap[] = + { + { OUString("ForbiddenCharacters"), HANDLE_FORBIDDEN_CHARS, cppu::UnoType<css::i18n::XForbiddenCharacters>::get(), 0}, + { OUString("LinkUpdateMode"), HANDLE_LINK_UPDATE_MODE, cppu::UnoType<sal_Int16>::get(), 0}, + { OUString("FieldAutoUpdate"), HANDLE_FIELD_AUTO_UPDATE, cppu::UnoType<bool>::get(), 0}, + { OUString("ChartAutoUpdate"), HANDLE_CHART_AUTO_UPDATE, cppu::UnoType<bool>::get(), 0}, + { OUString("AddParaTableSpacing"), HANDLE_ADD_PARA_TABLE_SPACING, cppu::UnoType<bool>::get(), 0}, + { OUString("AddParaTableSpacingAtStart"), HANDLE_ADD_PARA_TABLE_SPACING_AT_START, cppu::UnoType<bool>::get(), 0}, + { OUString("AlignTabStopPosition"), HANDLE_ALIGN_TAB_STOP_POSITION, cppu::UnoType<bool>::get(), 0}, + { OUString("PrinterName"), HANDLE_PRINTER_NAME, cppu::UnoType<OUString>::get(), 0}, + { OUString("PrinterSetup"), HANDLE_PRINTER_SETUP, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), 0}, + { OUString("PrinterPaperFromSetup"), HANDLE_PRINTER_PAPER, cppu::UnoType<bool>::get(), 0}, + { OUString("IsKernAsianPunctuation"), HANDLE_IS_KERN_ASIAN_PUNCTUATION, cppu::UnoType<bool>::get(), 0}, + { OUString("CharacterCompressionType"), HANDLE_CHARACTER_COMPRESSION_TYPE, cppu::UnoType<sal_Int16>::get(), 0}, + { OUString("ApplyUserData"), HANDLE_APPLY_USER_DATA, cppu::UnoType<bool>::get(), 0 }, + { OUString("SaveThumbnail"), HANDLE_SAVE_THUMBNAIL, cppu::UnoType<bool>::get(), 0 }, + { OUString("SaveGlobalDocumentLinks"), HANDLE_SAVE_GLOBAL_DOCUMENT_LINKS, cppu::UnoType<bool>::get(), 0}, + { OUString("CurrentDatabaseDataSource"), HANDLE_CURRENT_DATABASE_DATA_SOURCE, cppu::UnoType<OUString>::get(), 0}, + { OUString("CurrentDatabaseCommand"), HANDLE_CURRENT_DATABASE_COMMAND, cppu::UnoType<OUString>::get(), 0}, + { OUString("CurrentDatabaseCommandType"), HANDLE_CURRENT_DATABASE_COMMAND_TYPE, cppu::UnoType<sal_Int32>::get(), 0}, + { OUString("EmbeddedDatabaseName"), HANDLE_EMBEDDED_DATABASE_NAME, cppu::UnoType<OUString>::get(), 0}, + { OUString("SaveVersionOnClose"), HANDLE_SAVE_VERSION_ON_CLOSE, cppu::UnoType<bool>::get(), 0}, + { OUString("UpdateFromTemplate"), HANDLE_UPDATE_FROM_TEMPLATE, cppu::UnoType<bool>::get(), 0}, + + { OUString("PrinterIndependentLayout"), HANDLE_PRINTER_INDEPENDENT_LAYOUT, cppu::UnoType<sal_Int16>::get(), 0}, + { OUString("IsLabelDocument"), HANDLE_IS_LABEL_DOC, cppu::UnoType<bool>::get(), 0}, + { OUString("AddFrameOffsets"), HANDLE_IS_ADD_FLY_OFFSET, cppu::UnoType<bool>::get(), 0}, + { OUString("AddVerticalFrameOffsets"), HANDLE_IS_ADD_VERTICAL_FLY_OFFSET, cppu::UnoType<bool>::get(), 0}, + { OUString("AddExternalLeading"), HANDLE_IS_ADD_EXTERNAL_LEADING, cppu::UnoType<bool>::get(), 0}, + { OUString("UseOldNumbering"), HANDLE_OLD_NUMBERING, cppu::UnoType<bool>::get(), 0}, + { OUString("OutlineLevelYieldsNumbering"), HANDLE_OUTLINELEVEL_YIELDS_NUMBERING, cppu::UnoType<bool>::get(), 0}, + /* Stampit It disable the print cancel button of the shown progress dialog. */ + { OUString("AllowPrintJobCancel"), HANDLE_ALLOW_PRINTJOB_CANCEL, cppu::UnoType<bool>::get(), 0}, + { OUString("UseFormerLineSpacing"), HANDLE_USE_FORMER_LINE_SPACING, cppu::UnoType<bool>::get(), 0}, + { OUString("AddParaSpacingToTableCells"), HANDLE_ADD_PARA_SPACING_TO_TABLE_CELLS, cppu::UnoType<bool>::get(), 0}, + { OUString("AddParaLineSpacingToTableCells"), HANDLE_ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, cppu::UnoType<bool>::get(), 0}, + { OUString("UseFormerObjectPositioning"), HANDLE_USE_FORMER_OBJECT_POSITIONING, cppu::UnoType<bool>::get(), 0}, + { OUString("UseFormerTextWrapping"), HANDLE_USE_FORMER_TEXT_WRAPPING, cppu::UnoType<bool>::get(), 0}, + { OUString("RedlineProtectionKey"), HANDLE_CHANGES_PASSWORD, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), 0}, + { OUString("ConsiderTextWrapOnObjPos"), HANDLE_CONSIDER_WRAP_ON_OBJPOS, cppu::UnoType<bool>::get(), 0}, + { OUString("IgnoreFirstLineIndentInNumbering"), HANDLE_IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, cppu::UnoType<bool>::get(), 0}, + { OUString("DoNotJustifyLinesWithManualBreak"), HANDLE_DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, cppu::UnoType<bool>::get(), 0}, + { OUString("DoNotResetParaAttrsForNumFont"), HANDLE_DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, cppu::UnoType<bool>::get(), 0}, + { OUString("TableRowKeep"), HANDLE_TABLE_ROW_KEEP, cppu::UnoType<bool>::get(), 0}, + { OUString("IgnoreTabsAndBlanksForLineCalculation"), HANDLE_IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, cppu::UnoType<bool>::get(), 0}, + { OUString("LoadReadonly"), HANDLE_LOAD_READONLY, cppu::UnoType<bool>::get(), 0}, + { OUString("DoNotCaptureDrawObjsOnPage"), HANDLE_DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, cppu::UnoType<bool>::get(), 0}, + { OUString("ClipAsCharacterAnchoredWriterFlyFrames"), HANDLE_CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAMES, cppu::UnoType<bool>::get(), 0}, + { OUString("UnxForceZeroExtLeading"), HANDLE_UNIX_FORCE_ZERO_EXT_LEADING, cppu::UnoType<bool>::get(), 0}, + { OUString("UseOldPrinterMetrics"), HANDLE_USE_OLD_PRINTER_METRICS, cppu::UnoType<bool>::get(), 0}, + { OUString("TabsRelativeToIndent"), HANDLE_TABS_RELATIVE_TO_INDENT, cppu::UnoType<bool>::get(), 0}, + { OUString("Rsid"), HANDLE_RSID, cppu::UnoType<sal_Int32>::get(), 0}, + { OUString("RsidRoot"), HANDLE_RSID_ROOT, cppu::UnoType<sal_Int32>::get(), 0}, + { OUString("ProtectForm"), HANDLE_PROTECT_FORM, cppu::UnoType<bool>::get(), 0}, + { OUString("MsWordCompTrailingBlanks"), HANDLE_MS_WORD_COMP_TRAILING_BLANKS, cppu::UnoType<bool>::get(), 0 }, + { OUString("MsWordCompMinLineHeightByFly"), HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, cppu::UnoType<bool>::get(), 0 }, + { OUString("TabAtLeftIndentForParagraphsInList"), HANDLE_TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST, cppu::UnoType<bool>::get(), 0}, + { OUString("ModifyPasswordInfo"), HANDLE_MODIFYPASSWORDINFO, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), 0}, + { OUString("MathBaselineAlignment"), HANDLE_MATH_BASELINE_ALIGNMENT, cppu::UnoType<bool>::get(), 0}, + { OUString("InvertBorderSpacing"), HANDLE_INVERT_BORDER_SPACING, cppu::UnoType<bool>::get(), 0}, + { OUString("CollapseEmptyCellPara"), HANDLE_COLLAPSE_EMPTY_CELL_PARA, cppu::UnoType<bool>::get(), 0}, + { OUString("SmallCapsPercentage66"), HANDLE_SMALL_CAPS_PERCENTAGE_66, cppu::UnoType<bool>::get(), 0}, + { OUString("TabOverflow"), HANDLE_TAB_OVERFLOW, cppu::UnoType<bool>::get(), 0}, + { OUString("UnbreakableNumberings"), HANDLE_UNBREAKABLE_NUMBERINGS, cppu::UnoType<bool>::get(), 0}, + { OUString("StylesNoDefault"), HANDLE_STYLES_NODEFAULT, cppu::UnoType<bool>::get(), 0}, + { OUString("FloattableNomargins"), HANDLE_FLOATTABLE_NOMARGINS, cppu::UnoType<bool>::get(), 0}, + { OUString("ClippedPictures"), HANDLE_CLIPPED_PICTURES, cppu::UnoType<bool>::get(), 0}, + { OUString("BackgroundParaOverDrawings"), HANDLE_BACKGROUND_PARA_OVER_DRAWINGS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedFonts"), HANDLE_EMBED_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedOnlyUsedFonts"), HANDLE_EMBED_USED_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedLatinScriptFonts"), HANDLE_EMBED_LATIN_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedAsianScriptFonts"), HANDLE_EMBED_ASIAN_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedComplexScriptFonts"), HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("EmbedSystemFonts"), HANDLE_EMBED_SYSTEM_FONTS, cppu::UnoType<bool>::get(), 0}, + { OUString("TabOverMargin"), HANDLE_TAB_OVER_MARGIN, cppu::UnoType<bool>::get(), 0}, + { OUString("TreatSingleColumnBreakAsPageBreak"), HANDLE_TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK, cppu::UnoType<bool>::get(), 0}, + { OUString("SurroundTextWrapSmall"), HANDLE_SURROUND_TEXT_WRAP_SMALL, cppu::UnoType<bool>::get(), 0}, + { OUString("ApplyParagraphMarkFormatToNumbering"), HANDLE_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, cppu::UnoType<bool>::get(), 0}, + { OUString("PropLineSpacingShrinksFirstLine"), HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE, cppu::UnoType<bool>::get(), 0}, + { OUString("SubtractFlysAnchoredAtFlys"), HANDLE_SUBTRACT_FLYS, cppu::UnoType<bool>::get(), 0}, + { OUString("DisableOffPagePositioning"), HANDLE_DISABLE_OFF_PAGE_POSITIONING, cppu::UnoType<bool>::get(), 0}, + { OUString("EmptyDbFieldHidesPara"), HANDLE_EMPTY_DB_FIELD_HIDES_PARA, cppu::UnoType<bool>::get(), 0 }, + { OUString("ContinuousEndnotes"), HANDLE_CONTINUOUS_ENDNOTES, cppu::UnoType<bool>::get(), 0 }, + { OUString("ProtectBookmarks"), HANDLE_PROTECT_BOOKMARKS, cppu::UnoType<bool>::get(), 0 }, + { OUString("ProtectFields"), HANDLE_PROTECT_FIELDS, cppu::UnoType<bool>::get(), 0 }, + { OUString("HeaderSpacingBelowLastPara"), HANDLE_HEADER_SPACING_BELOW_LAST_PARA, cppu::UnoType<bool>::get(), 0 }, + +/* + * As OS said, we don't have a view when we need to set this, so I have to + * find another solution before adding them to this property set - MTG + { OUString("IsGridVisible"), HANDLE_IS_GRID_VISIBLE, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("IsSnapToGrid"), HANDLE_IS_SNAP_TO_GRID, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("IsSynchroniseAxes"), HANDLE_IS_SYNCHRONISE_AXES, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("HorizontalGridResolution"), HANDLE_HORIZONTAL_GRID_RESOLUTION, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { OUString("HorizontalGridSubdivision"), HANDLE_HORIZONTAL_GRID_SUBDIVISION, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { OUString("VerticalGridResolution"), HANDLE_VERTICAL_GRID_RESOLUTION, cppu::UnoType<sal_Int32>::get(), 0, 0}, + { OUString("VerticalGridSubdivision"), HANDLE_VERTICAL_GRID_SUBDIVISION, cppu::UnoType<sal_Int16>::get(), 0, 0}, + + { OUString("ShowParagraphEnd"), HANDLE_SHOW_PARAGRAPH_END, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowOptionalHyphens"), HANDLE_SHOW_OPTIONAL_HYPHENS, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowSpaces"), HANDLE_SHOW_SPACES, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowTabs"), HANDLE_SHOW_TABS, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowBreaks"), HANDLE_SHOW_BREAKS, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowHiddenText"), HANDLE_SHOW_HIDDEN_TEXT, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowHiddenParagraphs"), HANDLE_SHOW_HIDDEN_PARAGRAPHS, cppu::UnoType<bool>::get(), 0, 0}, + + { OUString("ShowTextLimitGuide"), HANDLE_SHOW_TEXT_LIMIT_GUIDE, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowTableLimitGuide"), HANDLE_SHOW_TABLE_LIMIT_GUIDE, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowSectionLimitGuide"), HANDLE_SHOW_SECTION_LIMIT_GUIDE, cppu::UnoType<bool>::get(), 0, 0}, + { OUString("ShowGuidesWhileMoving"), HANDLE_SHOW_GUIDES_WHILE_MOVING, cppu::UnoType<bool>::get(), 0, 0}, +*/ + { OUString(), 0, css::uno::Type(), 0} + }; + return new MasterPropertySetInfo ( aWriterSettingsInfoMap ); +} + +SwXDocumentSettings::SwXDocumentSettings ( SwXTextDocument * pModel ) +: MasterPropertySet ( lcl_createSettingsInfo (), + &Application::GetSolarMutex () ) +, mpModel ( pModel ) +, mpDocSh ( nullptr ) +, mpDoc ( nullptr ) +, mpPrinter( nullptr ) +, mbPreferPrinterPapersize( false ) +{ + registerSlave ( new SwXPrintSettings ( SwXPrintSettingsType::Document, mpModel->GetDocShell()->GetDoc() ) ); +} + +SwXDocumentSettings::~SwXDocumentSettings() + throw() +{ +} + +Any SAL_CALL SwXDocumentSettings::queryInterface( const Type& rType ) +{ + return ::cppu::queryInterface(rType, + // OWeakObject interfaces + &dynamic_cast<XInterface&>(dynamic_cast<OWeakObject&>(*this)), + &dynamic_cast<XWeak&>(*this), + // my own interfaces + &dynamic_cast<XPropertySet&>(*this), + &dynamic_cast<XPropertyState&>(*this), + &dynamic_cast<XMultiPropertySet&>(*this), + &dynamic_cast<XServiceInfo&>(*this), + &dynamic_cast<XTypeProvider&>(*this)); +} +void SwXDocumentSettings::acquire () + throw () +{ + OWeakObject::acquire(); +} +void SwXDocumentSettings::release () + throw () +{ + OWeakObject::release(); +} + +uno::Sequence< uno::Type > SAL_CALL SwXDocumentSettings::getTypes( ) +{ + static const uno::Sequence< uno::Type > aTypes { + // from MasterPropertySet + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<XPropertyState>::get(), + cppu::UnoType<XMultiPropertySet>::get(), + cppu::UnoType<XServiceInfo>::get(), + cppu::UnoType<XTypeProvider>::get(), + }; + return aTypes; +} + +uno::Sequence< sal_Int8 > SAL_CALL SwXDocumentSettings::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +void SwXDocumentSettings::_preSetValues () +{ + mpDocSh = mpModel->GetDocShell(); + if (nullptr == mpDocSh) + throw UnknownPropertyException(); + + mpDoc = mpDocSh->GetDoc(); + if (nullptr == mpDoc) + throw UnknownPropertyException(); +} + +void SwXDocumentSettings::_setSingleValue( const comphelper::PropertyInfo & rInfo, const uno::Any &rValue ) +{ + if (rInfo.mnAttributes & PropertyAttribute::READONLY) + throw PropertyVetoException ("Property is read-only: " + rInfo.maName, static_cast < cppu::OWeakObject * > ( nullptr ) ); + + switch( rInfo.mnHandle ) + { + case HANDLE_FORBIDDEN_CHARS: + break; + case HANDLE_LINK_UPDATE_MODE: + { + sal_Int16 nMode = 0; + rValue >>= nMode; + switch (nMode) + { + case NEVER: + case MANUAL: + case AUTOMATIC: + case GLOBALSETTING: + break; + default: + throw IllegalArgumentException(); + } + mpDoc->getIDocumentSettingAccess().setLinkUpdateMode(nMode); + } + break; + case HANDLE_FIELD_AUTO_UPDATE: + { + bool bUpdateField = *o3tl::doAccess<bool>(rValue); + SwFieldUpdateFlags nFlag = mpDoc->getIDocumentSettingAccess().getFieldUpdateFlags(true); + mpDoc->getIDocumentSettingAccess().setFieldUpdateFlags( bUpdateField ? + nFlag == AUTOUPD_FIELD_AND_CHARTS ? + AUTOUPD_FIELD_AND_CHARTS : + AUTOUPD_FIELD_ONLY : + AUTOUPD_OFF ); + } + break; + case HANDLE_CHART_AUTO_UPDATE: + { + bool bUpdateChart = *o3tl::doAccess<bool>(rValue); + SwFieldUpdateFlags nFlag = mpDoc->getIDocumentSettingAccess().getFieldUpdateFlags(true); + mpDoc->getIDocumentSettingAccess().setFieldUpdateFlags( (nFlag == AUTOUPD_FIELD_ONLY || nFlag == AUTOUPD_FIELD_AND_CHARTS ) ? + bUpdateChart ? + AUTOUPD_FIELD_AND_CHARTS : + AUTOUPD_FIELD_ONLY : + AUTOUPD_OFF ); + } + break; + case HANDLE_ADD_PARA_TABLE_SPACING: + { + bool bParaSpace = false; + rValue >>= bParaSpace; + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX, bParaSpace ); + } + break; + case HANDLE_ADD_PARA_TABLE_SPACING_AT_START: + { + bool bParaSpacePage = false; + rValue >>= bParaSpacePage; + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, bParaSpacePage ); + } + break; + case HANDLE_ALIGN_TAB_STOP_POSITION: + { + bool bAlignTab = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_COMPAT, bAlignTab); + } + break; + case HANDLE_PRINTER_NAME: + { + //the printer must be created + OUString sPrinterName; + if( !(rValue >>= sPrinterName) ) + throw IllegalArgumentException(); + + if( !mpPrinter && !sPrinterName.isEmpty() && mpDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter* pPrinter = mpDoc->getIDocumentDeviceAccess().getPrinter( true ); + if ( pPrinter->GetName() != sPrinterName ) + { + VclPtrInstance<SfxPrinter> pNewPrinter( pPrinter->GetOptions().Clone(), sPrinterName ); + assert (! pNewPrinter->isDisposed() ); + if( pNewPrinter->IsKnown() ) + { + // set printer only once; in _postSetValues + mpPrinter = pNewPrinter; + } + else + { + pNewPrinter.disposeAndClear(); + } + } + } + + } + break; + case HANDLE_PRINTER_SETUP: + { + Sequence < sal_Int8 > aSequence; + if ( !(rValue >>= aSequence) ) + throw IllegalArgumentException(); + + sal_uInt32 nSize = aSequence.getLength(); + if( nSize > 0 ) + { + SvMemoryStream aStream (aSequence.getArray(), nSize, + StreamMode::READ ); + aStream.Seek ( STREAM_SEEK_TO_BEGIN ); + static sal_uInt16 const nRange[] = + { + FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER, + SID_HTML_MODE, SID_HTML_MODE, + SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + 0 + }; + auto pItemSet = std::make_unique<SfxItemSet>( mpDoc->GetAttrPool(), nRange ); + VclPtr<SfxPrinter> pPrinter = SfxPrinter::Create ( aStream, std::move(pItemSet) ); + assert (! pPrinter->isDisposed() ); + // set printer only once; in _postSetValues + mpPrinter.disposeAndClear(); + mpPrinter = pPrinter; + } + + } + break; + case HANDLE_PRINTER_PAPER: + { + bool bPreferPrinterPapersize = {}; // spurious -Werror=maybe-uninitialized + if(!(rValue >>= bPreferPrinterPapersize)) + throw IllegalArgumentException(); + mbPreferPrinterPapersize = bPreferPrinterPapersize; + } + break; + case HANDLE_IS_KERN_ASIAN_PUNCTUATION: + { + bool bIsKern = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::KERN_ASIAN_PUNCTUATION, bIsKern); + SwEditShell* pEditSh = mpDoc->GetEditShell(); + if(pEditSh) + pEditSh->ChgHyphenation(); + } + break; + case HANDLE_CHARACTER_COMPRESSION_TYPE: + { + sal_Int16 nMode = 0; + rValue >>= nMode; + switch (static_cast<CharCompressType>(nMode)) + { + case CharCompressType::NONE: + case CharCompressType::PunctuationOnly: + case CharCompressType::PunctuationAndKana: + break; + default: + throw IllegalArgumentException(); + } + mpDoc->getIDocumentSettingAccess().setCharacterCompressionType(static_cast<CharCompressType>(nMode)); + } + break; + case HANDLE_APPLY_USER_DATA: + { + mpDocSh->SetUseUserData(*o3tl::doAccess<bool>(rValue)); + } + break; + case HANDLE_SAVE_THUMBNAIL: + { + mpDocSh->SetUseThumbnailSave(*o3tl::doAccess<bool>(rValue)); + } + break; + case HANDLE_SAVE_GLOBAL_DOCUMENT_LINKS: + { + bool bSaveGlobal = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, bSaveGlobal ); + } + break; + case HANDLE_CURRENT_DATABASE_DATA_SOURCE: + { + SwDBData aData = mpDoc->GetDBData(); + if ( rValue >>= aData.sDataSource ) + mpDoc->ChgDBData( aData ); + } + break; + case HANDLE_CURRENT_DATABASE_COMMAND: + { + SwDBData aData = mpDoc->GetDBData(); + + if ( rValue >>= aData.sCommand ) + mpDoc->ChgDBData( aData ); + + SAL_WARN_IF( aData.sDataSource.isEmpty() && !aData.sCommand.isEmpty(), "sw.uno", + "\"CurrentDatabaseCommand\" property possibly set before \"CurrentDatabaseDataSource\"" ); + } + break; + case HANDLE_CURRENT_DATABASE_COMMAND_TYPE: + { + SwDBData aData = mpDoc->GetDBData(); + if ( rValue >>= aData.nCommandType ) + mpDoc->ChgDBData( aData ); + SAL_WARN_IF( aData.nCommandType && aData.sDataSource.isEmpty(), "sw.uno", + "\"CurrentDatabaseCommandType\" property possibly set before \"CurrentDatabaseDataSource\"" ); + SAL_WARN_IF( aData.nCommandType && aData.sCommand.isEmpty(), "sw.uno", + "\"CurrentDatabaseCommandType\" property possibly set before \"CurrentDatabaseCommand\"" ); + } + break; + case HANDLE_EMBEDDED_DATABASE_NAME: + { +#if HAVE_FEATURE_DBCONNECTIVITY + OUString sEmbeddedName; + if (rValue >>= sEmbeddedName) + mpDoc->GetDBManager()->setEmbeddedName(sEmbeddedName, *mpDocSh); +#endif + } + break; + case HANDLE_SAVE_VERSION_ON_CLOSE: + { + mpDocSh->SetSaveVersionOnClose( *o3tl::doAccess<bool>(rValue) ); + } + break; + case HANDLE_UPDATE_FROM_TEMPLATE: + { + mpDocSh->SetQueryLoadTemplate( *o3tl::doAccess<bool>(rValue) ); + } + break; + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + sal_Int16 nTmp = 0; + rValue >>= nTmp; + + bool bUseVirDev = true; + bool bHiResVirDev = true; + if( nTmp == document::PrinterIndependentLayout::DISABLED ) + bUseVirDev = false; + else if ( nTmp == document::PrinterIndependentLayout::LOW_RESOLUTION ) + bHiResVirDev = false; + else if ( nTmp != document::PrinterIndependentLayout::HIGH_RESOLUTION ) + throw IllegalArgumentException(); + + mpDoc->getIDocumentDeviceAccess().setReferenceDeviceType( bUseVirDev, bHiResVirDev ); + } + break; + case HANDLE_IS_LABEL_DOC : + { + bool bSet = false; + if(!(rValue >>= bSet)) + throw IllegalArgumentException(); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::LABEL_DOCUMENT, bSet); + } + break; + case HANDLE_IS_ADD_FLY_OFFSET: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::ADD_FLY_OFFSETS, bTmp); + } + break; + case HANDLE_IS_ADD_VERTICAL_FLY_OFFSET: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS, bTmp); + } + break; + case HANDLE_IS_ADD_EXTERNAL_LEADING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::ADD_EXT_LEADING, bTmp); + } + break; + case HANDLE_OLD_NUMBERING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::OLD_NUMBERING, bTmp); + } + break; + case HANDLE_OUTLINELEVEL_YIELDS_NUMBERING: + { + // ignore - this is a dead property + } + break; + case HANDLE_ALLOW_PRINTJOB_CANCEL: + { + bool bState = false; + if (!(rValue >>= bState)) + throw IllegalArgumentException(); + mpDocSh->Stamp_SetPrintCancelState(bState); + } + break; + case HANDLE_USE_FORMER_LINE_SPACING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::OLD_LINE_SPACING, bTmp); + } + break; + case HANDLE_ADD_PARA_SPACING_TO_TABLE_CELLS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, bTmp); + } + break; + case HANDLE_ADD_PARA_LINE_SPACING_TO_TABLE_CELLS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, bTmp); + } + break; + case HANDLE_USE_FORMER_OBJECT_POSITIONING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_OBJECT_POS, bTmp); + } + break; + case HANDLE_USE_FORMER_TEXT_WRAPPING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, bTmp); + } + break; + case HANDLE_CHANGES_PASSWORD: + { + Sequence <sal_Int8> aNew; + if(rValue >>= aNew) + { + mpDoc->getIDocumentRedlineAccess().SetRedlinePassword(aNew); + if(aNew.hasElements()) + { + RedlineFlags eMode = mpDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + eMode |= RedlineFlags::On; + mpDoc->getIDocumentRedlineAccess().SetRedlineFlags( eMode ); + } + } + } + break; + case HANDLE_CONSIDER_WRAP_ON_OBJPOS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, bTmp ); + } + break; + case HANDLE_IGNORE_FIRST_LINE_INDENT_IN_NUMBERING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING, bTmp); + } + break; + case HANDLE_DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, bTmp); + } + break; + case HANDLE_DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT, bTmp); + } + break; + case HANDLE_TABLE_ROW_KEEP: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TABLE_ROW_KEEP, bTmp); + } + break; + case HANDLE_IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, bTmp); + } + break; + case HANDLE_LOAD_READONLY: + { + mpDocSh->SetLoadReadonly( *o3tl::doAccess<bool>(rValue) ); + } + break; + case HANDLE_DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE, bTmp); + } + break; + case HANDLE_CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAMES: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME, bTmp); + } + break; + case HANDLE_UNIX_FORCE_ZERO_EXT_LEADING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING, bTmp); + } + break; + case HANDLE_USE_OLD_PRINTER_METRICS: + // ignore - this is a dead property + break; + case HANDLE_TABS_RELATIVE_TO_INDENT: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, bTmp); + } + break; + case HANDLE_RSID: + { + sal_uInt32 nTmp = 0; + rValue >>= nTmp; + mpDoc->setRsid( nTmp ); + } + break; + case HANDLE_RSID_ROOT: + { + sal_uInt32 nTmp = 0; + rValue >>= nTmp; + mpDoc->setRsidRoot( nTmp ); + } + break; + case HANDLE_PROTECT_FORM: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FORM, bTmp); + } + break; + case HANDLE_MS_WORD_COMP_TRAILING_BLANKS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, bTmp); + } + break; + case HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY, bTmp); + } + break; + case HANDLE_TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST, bTmp); + } + break; + case HANDLE_MODIFYPASSWORDINFO: + { + uno::Sequence< beans::PropertyValue > aInfo; + if ( !( rValue >>= aInfo ) ) + throw lang::IllegalArgumentException( + "Value of type Sequence<PropertyValue> expected!", + uno::Reference< uno::XInterface >(), + 2 ); + + if ( !mpDocSh->SetModifyPasswordInfo( aInfo ) ) + throw beans::PropertyVetoException( + "The hash is not allowed to be changed now!", + uno::Reference< uno::XInterface >() ); + } + break; + case HANDLE_MATH_BASELINE_ALIGNMENT: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set( DocumentSettingId::MATH_BASELINE_ALIGNMENT, bTmp ); + } + break; + case HANDLE_INVERT_BORDER_SPACING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::INVERT_BORDER_SPACING, bTmp); + } + break; + case HANDLE_COLLAPSE_EMPTY_CELL_PARA: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA, bTmp); + } + break; + case HANDLE_SMALL_CAPS_PERCENTAGE_66: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::SMALL_CAPS_PERCENTAGE_66, bTmp); + } + break; + case HANDLE_TAB_OVERFLOW: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVERFLOW, bTmp); + } + break; + case HANDLE_UNBREAKABLE_NUMBERINGS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::UNBREAKABLE_NUMBERINGS, bTmp); + } + break; + case HANDLE_STYLES_NODEFAULT: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::STYLES_NODEFAULT, bTmp); + } + break; + case HANDLE_FLOATTABLE_NOMARGINS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::FLOATTABLE_NOMARGINS, bTmp); + } + break; + case HANDLE_CLIPPED_PICTURES: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::CLIPPED_PICTURES, bTmp); + } + break; + case HANDLE_BACKGROUND_PARA_OVER_DRAWINGS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS, bTmp); + } + break; + case HANDLE_EMBED_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_FONTS, bTmp); + } + break; + case HANDLE_EMBED_USED_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_USED_FONTS, bTmp); + } + break; + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS, bTmp); + } + break; + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS, bTmp); + } + break; + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS, bTmp); + } + break; + case HANDLE_EMBED_SYSTEM_FONTS: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMBED_SYSTEM_FONTS, bTmp); + } + break; + case HANDLE_TAB_OVER_MARGIN: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TAB_OVER_MARGIN, bTmp); + } + break; + case HANDLE_TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK, bTmp); + } + break; + case HANDLE_SURROUND_TEXT_WRAP_SMALL: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL, bTmp); + } + break; + case HANDLE_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: + { + bool bTmp = *o3tl::doAccess<bool>(rValue); + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING, bTmp); + } + break; + case HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE, bTmp); + } + } + break; + case HANDLE_SUBTRACT_FLYS: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::SUBTRACT_FLYS, bTmp); + } + } + break; + case HANDLE_DISABLE_OFF_PAGE_POSITIONING: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING, bTmp); + } + } + break; + case HANDLE_EMPTY_DB_FIELD_HIDES_PARA: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, + bTmp); + } + } + break; + case HANDLE_CONTINUOUS_ENDNOTES: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::CONTINUOUS_ENDNOTES, + bTmp); + } + } + break; + case HANDLE_PROTECT_BOOKMARKS: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_BOOKMARKS, + bTmp); + } + } + break; + case HANDLE_PROTECT_FIELDS: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set(DocumentSettingId::PROTECT_FIELDS, + bTmp); + } + } + break; + case HANDLE_HEADER_SPACING_BELOW_LAST_PARA: + { + bool bTmp; + if (rValue >>= bTmp) + { + mpDoc->getIDocumentSettingAccess().set( + DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA, bTmp); + } + } + break; + default: + throw UnknownPropertyException(OUString::number(rInfo.mnHandle)); + } +} + +void SwXDocumentSettings::_postSetValues () +{ + // set printer only once, namely here! + if( mpPrinter != nullptr ) + { + // #i86352# the printer is also used as container for options by sfx + // when setting a printer it should have decent default options + SfxItemSet aOptions( mpPrinter->GetOptions() ); + SwPrintData aPrtData( mpDoc->getIDocumentDeviceAccess().getPrintData() ); + SwAddPrinterItem aAddPrinterItem (aPrtData); + aOptions.Put(aAddPrinterItem); + mpPrinter->SetOptions( aOptions ); + mpPrinter->SetPrinterSettingsPreferred( mbPreferPrinterPapersize ); + + mpDoc->getIDocumentDeviceAccess().setPrinter( mpPrinter, true, true ); + } + + mpPrinter = nullptr; + mpDocSh = nullptr; + mpDoc = nullptr; +} + +void SwXDocumentSettings::_preGetValues () +{ + mpDocSh = mpModel->GetDocShell(); + if (nullptr == mpDocSh) + throw UnknownPropertyException(); + mpDoc = mpDocSh->GetDoc(); + if (nullptr == mpDoc) + throw UnknownPropertyException(); +} + +void SwXDocumentSettings::_getSingleValue( const comphelper::PropertyInfo & rInfo, uno::Any & rValue ) +{ + switch( rInfo.mnHandle ) + { + case HANDLE_FORBIDDEN_CHARS: + { + Reference<XForbiddenCharacters> xRet(*mpModel->GetPropertyHelper(), UNO_QUERY); + rValue <<= xRet; + } + break; + case HANDLE_LINK_UPDATE_MODE: + { + rValue <<= static_cast < sal_Int16 > ( mpDoc->getIDocumentSettingAccess().getLinkUpdateMode(true) ); + } + break; + case HANDLE_FIELD_AUTO_UPDATE: + { + SwFieldUpdateFlags nFlags = mpDoc->getIDocumentSettingAccess().getFieldUpdateFlags(true); + rValue <<= nFlags == AUTOUPD_FIELD_ONLY || nFlags == AUTOUPD_FIELD_AND_CHARTS; + } + break; + case HANDLE_CHART_AUTO_UPDATE: + { + SwFieldUpdateFlags nFlags = mpDoc->getIDocumentSettingAccess().getFieldUpdateFlags(true); + rValue <<= nFlags == AUTOUPD_FIELD_AND_CHARTS; + } + break; + case HANDLE_ADD_PARA_TABLE_SPACING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX); + } + break; + case HANDLE_ADD_PARA_TABLE_SPACING_AT_START: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES); + } + break; + case HANDLE_ALIGN_TAB_STOP_POSITION: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT); + } + break; + case HANDLE_PRINTER_NAME: + { + SfxPrinter *pPrinter = mpDoc->getIDocumentDeviceAccess().getPrinter( false ); + rValue <<= pPrinter ? pPrinter->GetName() : OUString(); + } + break; + case HANDLE_PRINTER_SETUP: + { + SfxPrinter *pPrinter = mpDoc->getIDocumentDeviceAccess().getPrinter( false ); + if (pPrinter) + { + SvMemoryStream aStream; + pPrinter->Store( aStream ); + sal_uInt32 nSize = aStream.TellEnd(); + aStream.Seek ( STREAM_SEEK_TO_BEGIN ); + Sequence < sal_Int8 > aSequence( nSize ); + aStream.ReadBytes(aSequence.getArray(), nSize); + rValue <<= aSequence; + } + else + { + Sequence < sal_Int8 > aSequence ( 0 ); + rValue <<= aSequence; + } + } + break; + case HANDLE_PRINTER_PAPER: + { + SfxPrinter *pTempPrinter = mpDoc->getIDocumentDeviceAccess().getPrinter( false ); + rValue <<= pTempPrinter && pTempPrinter->GetPrinterSettingsPreferred(); + } + break; + case HANDLE_IS_KERN_ASIAN_PUNCTUATION: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::KERN_ASIAN_PUNCTUATION); + } + break; + case HANDLE_APPLY_USER_DATA: + { + rValue <<= mpDocSh->IsUseUserData(); + } + break; + case HANDLE_SAVE_THUMBNAIL: + { + rValue <<= mpDocSh->IsUseThumbnailSave(); + } + break; + case HANDLE_CHARACTER_COMPRESSION_TYPE: + { + rValue <<= static_cast < sal_Int16 > (mpDoc->getIDocumentSettingAccess().getCharacterCompressionType()); + } + break; + case HANDLE_SAVE_GLOBAL_DOCUMENT_LINKS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS); + } + break; + case HANDLE_CURRENT_DATABASE_DATA_SOURCE: + { + const SwDBData& rData = mpDoc->GetDBDesc(); + rValue <<= rData.sDataSource; + } + break; + case HANDLE_CURRENT_DATABASE_COMMAND: + { + const SwDBData& rData = mpDoc->GetDBDesc(); + rValue <<= rData.sCommand; + } + break; + case HANDLE_CURRENT_DATABASE_COMMAND_TYPE: + { + const SwDBData& rData = mpDoc->GetDBDesc(); + rValue <<= rData.nCommandType; + } + break; + case HANDLE_EMBEDDED_DATABASE_NAME: + { +#if HAVE_FEATURE_DBCONNECTIVITY + rValue <<= mpDoc->GetDBManager()->getEmbeddedName(); +#else + rValue = uno::Any(); +#endif + } + break; + case HANDLE_SAVE_VERSION_ON_CLOSE: + { + rValue <<= mpDocSh->IsSaveVersionOnClose(); + } + break; + case HANDLE_UPDATE_FROM_TEMPLATE: + { + rValue <<= mpDocSh->IsQueryLoadTemplate(); + } + break; + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + // returns short (see css.document.PrinterIndependentLayout) + sal_Int16 nVirDevType = mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE) ? + ( mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE) ? + document::PrinterIndependentLayout::HIGH_RESOLUTION : + document::PrinterIndependentLayout::LOW_RESOLUTION ) : + document::PrinterIndependentLayout::DISABLED; + rValue <<= nVirDevType; + } + break; + case HANDLE_IS_LABEL_DOC: + { + bool bLabel = mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::LABEL_DOCUMENT); + rValue <<= bLabel; + } + break; + case HANDLE_IS_ADD_FLY_OFFSET: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_FLY_OFFSETS); + } + break; + case HANDLE_IS_ADD_VERTICAL_FLY_OFFSET: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS); + } + break; + case HANDLE_IS_ADD_EXTERNAL_LEADING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING); + } + break; + case HANDLE_OLD_NUMBERING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::OLD_NUMBERING); + } + break; + case HANDLE_OUTLINELEVEL_YIELDS_NUMBERING: + { + rValue <<= false; + } + break; + case HANDLE_ALLOW_PRINTJOB_CANCEL: + { + rValue <<= mpDocSh->Stamp_GetPrintCancelState(); + } + break; + case HANDLE_USE_FORMER_LINE_SPACING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING); + } + break; + case HANDLE_ADD_PARA_SPACING_TO_TABLE_CELLS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS); + } + break; + case HANDLE_ADD_PARA_LINE_SPACING_TO_TABLE_CELLS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS); + } + break; + case HANDLE_USE_FORMER_OBJECT_POSITIONING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS); + } + break; + case HANDLE_USE_FORMER_TEXT_WRAPPING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING); + } + break; + case HANDLE_CHANGES_PASSWORD: + { + rValue <<= mpDoc->getIDocumentRedlineAccess().GetRedlinePassword(); + } + break; + case HANDLE_CONSIDER_WRAP_ON_OBJPOS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION); + } + break; + case HANDLE_IGNORE_FIRST_LINE_INDENT_IN_NUMBERING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING); + } + break; + case HANDLE_DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); + } + break; + case HANDLE_DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT); + } + break; + case HANDLE_TABLE_ROW_KEEP : + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::TABLE_ROW_KEEP); + } + break; + case HANDLE_IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION); + } + break; + case HANDLE_LOAD_READONLY: + { + rValue <<= mpDocSh->IsLoadReadonly(); + } + break; + case HANDLE_DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE); + } + break; + case HANDLE_CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAMES: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME); + } + break; + case HANDLE_UNIX_FORCE_ZERO_EXT_LEADING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING); + } + break; + case HANDLE_USE_OLD_PRINTER_METRICS: + { + rValue <<= false; + } + break; + case HANDLE_TABS_RELATIVE_TO_INDENT: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT); + } + break; + case HANDLE_RSID: + { + rValue <<= static_cast < sal_Int32 > ( mpDoc->getRsid() ); + } + break; + case HANDLE_RSID_ROOT: + { + rValue <<= static_cast < sal_Int32 > ( mpDoc->getRsidRoot() ); + } + break; + case HANDLE_PROTECT_FORM: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM); + } + break; + case HANDLE_MS_WORD_COMP_TRAILING_BLANKS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS); + } + break; + case HANDLE_MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY); + } + break; + case HANDLE_TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST); + } + break; + case HANDLE_MODIFYPASSWORDINFO: + { + rValue <<= mpDocSh->GetModifyPasswordInfo(); + } + break; + case HANDLE_MATH_BASELINE_ALIGNMENT: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ); + } + break; + case HANDLE_INVERT_BORDER_SPACING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::INVERT_BORDER_SPACING); + } + break; + case HANDLE_COLLAPSE_EMPTY_CELL_PARA: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA ); + } + break; + case HANDLE_SMALL_CAPS_PERCENTAGE_66: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::SMALL_CAPS_PERCENTAGE_66 ); + } + break; + case HANDLE_TAB_OVERFLOW: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::TAB_OVERFLOW ); + } + break; + case HANDLE_UNBREAKABLE_NUMBERINGS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::UNBREAKABLE_NUMBERINGS ); + } + break; + case HANDLE_STYLES_NODEFAULT: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::STYLES_NODEFAULT ); + } + break; + case HANDLE_FLOATTABLE_NOMARGINS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::FLOATTABLE_NOMARGINS ); + } + break; + case HANDLE_CLIPPED_PICTURES: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::CLIPPED_PICTURES ); + } + break; + case HANDLE_BACKGROUND_PARA_OVER_DRAWINGS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ); + } + break; + case HANDLE_EMBED_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ); + } + break; + case HANDLE_EMBED_USED_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_USED_FONTS ); + } + break; + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS ); + } + break; + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS ); + } + break; + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS ); + } + break; + case HANDLE_EMBED_SYSTEM_FONTS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_SYSTEM_FONTS ); + } + break; + case HANDLE_TAB_OVER_MARGIN: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::TAB_OVER_MARGIN ); + } + break; + case HANDLE_TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK ); + } + break; + case HANDLE_SURROUND_TEXT_WRAP_SMALL: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::SURROUND_TEXT_WRAP_SMALL ); + } + break; + case HANDLE_APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ); + } + break; + case HANDLE_PROP_LINE_SPACING_SHRINKS_FIRST_LINE: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE); + } + break; + case HANDLE_SUBTRACT_FLYS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS); + } + break; + case HANDLE_DISABLE_OFF_PAGE_POSITIONING: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING); + } + break; + case HANDLE_EMPTY_DB_FIELD_HIDES_PARA: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA); + } + break; + case HANDLE_CONTINUOUS_ENDNOTES: + { + rValue + <<= mpDoc->getIDocumentSettingAccess().get(DocumentSettingId::CONTINUOUS_ENDNOTES); + } + break; + case HANDLE_PROTECT_BOOKMARKS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::PROTECT_BOOKMARKS); + } + break; + case HANDLE_PROTECT_FIELDS: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::PROTECT_FIELDS); + } + break; + case HANDLE_HEADER_SPACING_BELOW_LAST_PARA: + { + rValue <<= mpDoc->getIDocumentSettingAccess().get( + DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA); + } + break; + default: + throw UnknownPropertyException(OUString::number(rInfo.mnHandle)); + } +} + +void SwXDocumentSettings::_postGetValues () +{ + mpDocSh = nullptr; + mpDoc = nullptr; +} + +// XServiceInfo +OUString SAL_CALL SwXDocumentSettings::getImplementationName( ) +{ + return "SwXDocumentSettings"; +} + +sal_Bool SAL_CALL SwXDocumentSettings::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL SwXDocumentSettings::getSupportedServiceNames( ) +{ + return { "com.sun.star.document.Settings", "com.sun.star.text.DocumentSettings", "com.sun.star.text.PrintSettings" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/SwXDocumentSettings.hxx b/sw/source/uibase/uno/SwXDocumentSettings.hxx new file mode 100644 index 000000000..10ef572cb --- /dev/null +++ b/sw/source/uibase/uno/SwXDocumentSettings.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_UNO_SWXDOCUMENTSETTINGS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_UNO_SWXDOCUMENTSETTINGS_HXX + +#include <comphelper/MasterPropertySet.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <cppuhelper/weak.hxx> +#include <vcl/vclptr.hxx> + +class SwXTextDocument; +class SwDocShell; +class SwDoc; +class SfxPrinter; + +class SwXDocumentSettings final : + public comphelper::MasterPropertySet, + public css::lang::XServiceInfo, + public css::lang::XTypeProvider, + public cppu::OWeakObject +{ + SwXTextDocument* mpModel; + SwDocShell* mpDocSh; + SwDoc* mpDoc; + + /** the printer should be set only once; since there are several + * printer-related properties, remember the last printer and set it in + * _postSetValues */ + VclPtr<SfxPrinter> mpPrinter; + bool mbPreferPrinterPapersize; + + virtual void _preSetValues () override; + virtual void _setSingleValue( const comphelper::PropertyInfo & rInfo, const css::uno::Any &rValue ) override; + virtual void _postSetValues () override; + + virtual void _preGetValues () override; + virtual void _getSingleValue( const comphelper::PropertyInfo & rInfo, css::uno::Any & rValue ) override; + virtual void _postGetValues () override; + virtual ~SwXDocumentSettings() + throw() override; +public: + SwXDocumentSettings( SwXTextDocument* pModel ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) + throw () override; + virtual void SAL_CALL release( ) + throw () 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; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; +}; +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/SwXFilterOptions.cxx b/sw/source/uibase/uno/SwXFilterOptions.cxx new file mode 100644 index 000000000..b36472bf5 --- /dev/null +++ b/sw/source/uibase/uno/SwXFilterOptions.cxx @@ -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 . + */ + +#include <SwXFilterOptions.hxx> +#include <shellio.hxx> +#include <swdll.hxx> +#include <vcl/svapp.hxx> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotxdoc.hxx> + +#include <swabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::lang; + +#define FILTER_OPTIONS_NAME "FilterOptions" + +SwXFilterOptions::SwXFilterOptions() +{ +} + +SwXFilterOptions::~SwXFilterOptions() +{ +} + +uno::Sequence< beans::PropertyValue > SwXFilterOptions::getPropertyValues() +{ + return comphelper::InitPropertySequence({ + { FILTER_OPTIONS_NAME, uno::Any(sFilterOptions) } + }); +} + +void SwXFilterOptions::setPropertyValues( const uno::Sequence<beans::PropertyValue >& aProps ) +{ + for (const beans::PropertyValue& rProp : aProps) + { + OUString aPropName = rProp.Name; + + if ( aPropName == FILTER_OPTIONS_NAME ) + rProp.Value >>= sFilterOptions; + else if ( aPropName == "InputStream" ) + rProp.Value >>= xInputStream; + } +} + +void SwXFilterOptions::setTitle( const OUString& /*rTitle*/ ) +{ +} + +sal_Int16 SwXFilterOptions::execute() +{ + sal_Int16 nRet = ui::dialogs::ExecutableDialogResults::CANCEL; + + std::unique_ptr<SvStream> pInStream; + if ( xInputStream.is() ) + pInStream = utl::UcbStreamHelper::CreateStream( xInputStream ); + + SwDocShell* pDocShell = nullptr; + if (auto pXDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(xModel); pXDoc) + pDocShell = pXDoc->GetDocShell(); + + if(pDocShell) + { + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwAsciiFilterDlg> pAsciiDlg(pFact->CreateSwAsciiFilterDlg(Application::GetFrameWeld(xDialogParent), *pDocShell, + pInStream.get())); + if(RET_OK == pAsciiDlg->Execute()) + { + SwAsciiOptions aOptions; + pAsciiDlg->FillOptions( aOptions ); + aOptions.WriteUserData(sFilterOptions); + nRet = ui::dialogs::ExecutableDialogResults::OK; + } + } + + return nRet; +} + +void SwXFilterOptions::setTargetDocument( const uno::Reference< XComponent >& xDoc ) +{ + xModel = xDoc; +} + +void SwXFilterOptions::setSourceDocument( const uno::Reference<XComponent >& xDoc ) +{ + xModel = xDoc; +} + +void SAL_CALL SwXFilterOptions::initialize(const uno::Sequence<uno::Any>& rArguments) +{ + ::comphelper::NamedValueCollection aProperties(rArguments); + if (aProperties.has("ParentWindow")) + aProperties.get("ParentWindow") >>= xDialogParent; +} + +OUString SwXFilterOptions::getImplementationName() +{ + return "com.sun.star.comp.Writer.FilterOptionsDialog"; +} + +sal_Bool SwXFilterOptions::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SwXFilterOptions::getSupportedServiceNames() +{ + return { "com.sun.star.ui.dialogs.FilterOptionsDialog" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_FilterOptionsDialog_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + + //the module may not be loaded + SwGlobals::ensure(); + return cppu::acquire(new SwXFilterOptions()); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/dlelstnr.cxx b/sw/source/uibase/uno/dlelstnr.cxx new file mode 100644 index 000000000..aeb72b540 --- /dev/null +++ b/sw/source/uibase/uno/dlelstnr.cxx @@ -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 . + */ + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/linguistic2/LinguServiceManager.hpp> +#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp> +#include <com/sun/star/linguistic2/XProofreadingIterator.hpp> +#include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp> + +#include <unotools/lingucfg.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <comphelper/processfactory.hxx> +#include <vcl/svapp.hxx> +#include <dlelstnr.hxx> +#include <proofreadingiterator.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::linguistic2::LinguServiceEventFlags; + +SwLinguServiceEventListener::SwLinguServiceEventListener() +{ + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + try + { + xDesktop = frame::Desktop::create(xContext); + xDesktop->addTerminateListener( this ); + + xLngSvcMgr = LinguServiceManager::create(xContext); + xLngSvcMgr->addLinguServiceManagerListener( static_cast<XLinguServiceEventListener *>(this) ); + + if (SvtLinguConfig().HasGrammarChecker()) + { + xGCIterator = sw::proofreadingiterator::get(xContext); + Reference< XLinguServiceEventBroadcaster > xBC( xGCIterator, UNO_QUERY ); + if (xBC.is()) + xBC->addLinguServiceEventListener( static_cast<XLinguServiceEventListener *>(this) ); + } + } + catch (const uno::Exception&) + { + OSL_FAIL("exception caught in SwLinguServiceEventListener c-tor" ); + } +} + +SwLinguServiceEventListener::~SwLinguServiceEventListener() +{ +} + +void SAL_CALL SwLinguServiceEventListener::processLinguServiceEvent( + const LinguServiceEvent& rLngSvcEvent ) +{ + SolarMutexGuard aGuard; + + bool bIsSpellWrong = 0 != (rLngSvcEvent.nEvent & SPELL_WRONG_WORDS_AGAIN); + bool bIsSpellAll = 0 != (rLngSvcEvent.nEvent & SPELL_CORRECT_WORDS_AGAIN); + if (0 != (rLngSvcEvent.nEvent & PROOFREAD_AGAIN)) + bIsSpellWrong = bIsSpellAll = true; // have all spelling and grammar checked... + if (bIsSpellWrong || bIsSpellAll) + { + SwModule::CheckSpellChanges( false, bIsSpellWrong, bIsSpellAll, false ); + } + if (rLngSvcEvent.nEvent & HYPHENATE_AGAIN) + { + SwView *pSwView = SwModule::GetFirstView(); + + //!! since this function may be called within the ctor of + //!! SwView (during formatting) where the WrtShell is not yet + //!! created, we have to check for the WrtShellPtr to see + //!! if it is already available + while (pSwView && pSwView->GetWrtShellPtr()) + { + pSwView->GetWrtShell().ChgHyphenation(); + pSwView = SwModule::GetNextView( pSwView ); + } + } +} + +void SAL_CALL SwLinguServiceEventListener::disposing( + const EventObject& rEventObj ) +{ + SolarMutexGuard aGuard; + + if (xLngSvcMgr.is() && rEventObj.Source == xLngSvcMgr) + xLngSvcMgr = nullptr; + if (xLngSvcMgr.is() && rEventObj.Source == xGCIterator) + xGCIterator = nullptr; +} + +void SAL_CALL SwLinguServiceEventListener::queryTermination( + const EventObject& /*rEventObj*/ ) +{ +} + +void SAL_CALL SwLinguServiceEventListener::notifyTermination( + const EventObject& rEventObj ) +{ + SolarMutexGuard aGuard; + + if (xDesktop.is() && rEventObj.Source == xDesktop) + { + if (xLngSvcMgr.is()) + xLngSvcMgr = nullptr; + if (xGCIterator.is()) + xGCIterator = nullptr; + xDesktop = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unoatxt.cxx b/sw/source/uibase/uno/unoatxt.cxx new file mode 100644 index 000000000..2a13d735b --- /dev/null +++ b/sw/source/uibase/uno/unoatxt.cxx @@ -0,0 +1,1053 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/io/IOException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <vcl/svapp.hxx> +#include <svtools/unoevent.hxx> +#include <sfx2/event.hxx> +#include <glosdoc.hxx> +#include <shellio.hxx> +#include <initui.hxx> +#include <gloslst.hxx> +#include <unoatxt.hxx> +#include <unomap.hxx> +#include <unotextbodyhf.hxx> +#include <unotextrange.hxx> +#include <TextCursorHelper.hxx> +#include <doc.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <docsh.hxx> +#include <swdll.hxx> +#include <svl/hint.hxx> +#include <tools/urlobj.hxx> +#include <svl/macitem.hxx> +#include <editeng/acorrcfg.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <memory> + +using namespace ::com::sun::star; + +SwXAutoTextContainer::SwXAutoTextContainer() +{ + pGlossaries = ::GetGlossaries(); + +} + +SwXAutoTextContainer::~SwXAutoTextContainer() +{ + +} + +sal_Int32 SwXAutoTextContainer::getCount() +{ + OSL_ENSURE(pGlossaries->GetGroupCnt() < o3tl::make_unsigned(SAL_MAX_INT32), + "SwXAutoTextContainer::getCount: too many items"); + return static_cast<sal_Int32>(pGlossaries->GetGroupCnt()); +} + +uno::Any SwXAutoTextContainer::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + const size_t nCount = pGlossaries->GetGroupCnt(); + if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= nCount ) + throw lang::IndexOutOfBoundsException(); + return getByName(pGlossaries->GetGroupName( static_cast<size_t>(nIndex) )); +} + +uno::Type SwXAutoTextContainer::getElementType() +{ + return cppu::UnoType<text::XAutoTextGroup>::get(); + +} + +sal_Bool SwXAutoTextContainer::hasElements() +{ + // At least standard should always exists! + return true; +} + +uno::Any SwXAutoTextContainer::getByName(const OUString& GroupName) +{ + SolarMutexGuard aGuard; + + uno::Reference< text::XAutoTextGroup > xGroup; + if ( pGlossaries && hasByName( GroupName ) ) // group name already known? + // true = create group if not already available + xGroup = pGlossaries->GetAutoTextGroup( GroupName ); + + if ( !xGroup.is() ) + throw container::NoSuchElementException(); + + return makeAny( xGroup ); +} + +uno::Sequence< OUString > SwXAutoTextContainer::getElementNames() +{ + SolarMutexGuard aGuard; + const size_t nCount = pGlossaries->GetGroupCnt(); + OSL_ENSURE(nCount < o3tl::make_unsigned(SAL_MAX_INT32), + "SwXAutoTextContainer::getElementNames: too many groups"); + + uno::Sequence< OUString > aGroupNames(static_cast<sal_Int32>(nCount)); + OUString *pArr = aGroupNames.getArray(); + + for ( size_t i = 0; i < nCount; ++i ) + { + // The names will be passed without a path extension. + pArr[i] = pGlossaries->GetGroupName(i).getToken(0, GLOS_DELIM); + } + return aGroupNames; +} +// Finds group names with or without path index. +sal_Bool SwXAutoTextContainer::hasByName(const OUString& Name) +{ + SolarMutexGuard aGuard; + OUString sGroupName( pGlossaries->GetCompleteGroupName( Name ) ); + if(!sGroupName.isEmpty()) + return true; + return false; +} + +uno::Reference< text::XAutoTextGroup > SwXAutoTextContainer::insertNewByName( + const OUString& aGroupName) +{ + SolarMutexGuard aGuard; + if(hasByName(aGroupName)) + throw container::ElementExistException(); + //check for non-ASCII characters + if(aGroupName.isEmpty()) + { + lang::IllegalArgumentException aIllegal; + aIllegal.Message = "group name must not be empty"; + throw aIllegal; + } + for(sal_Int32 nPos = 0; nPos < aGroupName.getLength(); nPos++) + { + sal_Unicode cChar = aGroupName[nPos]; + if (rtl::isAsciiAlphanumeric(cChar) || + (cChar == '_') || + (cChar == 0x20) || + (cChar == GLOS_DELIM) ) + { + continue; + } + lang::IllegalArgumentException aIllegal; + aIllegal.Message = "group name must contain a-z, A-z, '_', ' ' only"; + throw aIllegal; + } + OUString sGroup(aGroupName); + if (sGroup.indexOf(GLOS_DELIM)<0) + { + sGroup += OUStringChar(GLOS_DELIM) + "0"; + } + pGlossaries->NewGroupDoc(sGroup, sGroup.getToken(0, GLOS_DELIM)); + + uno::Reference< text::XAutoTextGroup > xGroup = pGlossaries->GetAutoTextGroup( sGroup ); + OSL_ENSURE( xGroup.is(), "SwXAutoTextContainer::insertNewByName: no UNO object created? How this?" ); + // We just inserted the group into the glossaries, so why doesn't it exist? + + return xGroup; +} + +void SwXAutoTextContainer::removeByName(const OUString& aGroupName) +{ + SolarMutexGuard aGuard; + // At first find the name with path extension + OUString sGroupName = pGlossaries->GetCompleteGroupName( aGroupName ); + if(sGroupName.isEmpty()) + throw container::NoSuchElementException(); + pGlossaries->DelGroupDoc(sGroupName); +} + +OUString SwXAutoTextContainer::getImplementationName() +{ + return "SwXAutoTextContainer"; +} + +sal_Bool SwXAutoTextContainer::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SwXAutoTextContainer::getSupportedServiceNames() +{ + return { "com.sun.star.text.AutoTextContainer" }; +} + +namespace +{ + class theSwXAutoTextGroupUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextGroupUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXAutoTextGroup::getUnoTunnelId() +{ + return theSwXAutoTextGroupUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXAutoTextGroup::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXAutoTextGroup>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); + } + return 0; +} + +SwXAutoTextGroup::SwXAutoTextGroup(const OUString& rName, + SwGlossaries* pGlos) : + pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_AUTO_TEXT_GROUP)), + pGlossaries(pGlos), + sName(rName), + m_sGroupName(rName) +{ + OSL_ENSURE( -1 != rName.indexOf( GLOS_DELIM ), + "SwXAutoTextGroup::SwXAutoTextGroup: to be constructed with a complete name only!" ); +} + +SwXAutoTextGroup::~SwXAutoTextGroup() +{ +} + +uno::Sequence< OUString > SwXAutoTextGroup::getTitles() +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + const sal_uInt16 nCount = pGlosGroup->GetCount(); + + uno::Sequence< OUString > aEntryTitles(nCount); + OUString *pArr = aEntryTitles.getArray(); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + pArr[i] = pGlosGroup->GetLongName(i); + return aEntryTitles; +} + +void SwXAutoTextGroup::renameByName(const OUString& aElementName, + const OUString& aNewElementName, const OUString& aNewElementTitle) +{ + SolarMutexGuard aGuard; + // throw exception only if the programmatic name is to be changed into an existing name + if(aNewElementName != aElementName && hasByName(aNewElementName)) + throw container::ElementExistException(); + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if(!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + + const sal_uInt16 nIdx = pGlosGroup->GetIndex( aElementName); + if(USHRT_MAX == nIdx) + throw lang::IllegalArgumentException(); + OUString aNewShort(aNewElementName); + OUString aNewName(aNewElementTitle); + sal_uInt16 nOldLongIdx = pGlosGroup->GetLongIndex( aNewShort ); + sal_uInt16 nOldIdx = pGlosGroup->GetIndex( aNewName ); + + if ((nOldLongIdx == USHRT_MAX || nOldLongIdx == nIdx) + && (nOldIdx == USHRT_MAX || nOldIdx == nIdx)) + { + pGlosGroup->Rename( nIdx, &aNewShort, &aNewName ); + if(pGlosGroup->GetError() != ERRCODE_NONE) + throw io::IOException(); + } + +} + +static bool lcl_CopySelToDoc( SwDoc* pInsDoc, OTextCursorHelper* pxCursor, SwXTextRange* pxRange) +{ + OSL_ENSURE( pInsDoc, "no InsDoc"); + + SwNodes& rNds = pInsDoc->GetNodes(); + + SwNodeIndex aIdx( rNds.GetEndOfContent(), -1 ); + SwContentNode * pNd = aIdx.GetNode().GetContentNode(); + SwPosition aPos(aIdx, SwIndex(pNd, pNd ? pNd->Len() : 0)); + + bool bRet = false; + pInsDoc->getIDocumentFieldsAccess().LockExpFields(); + { + SwDoc *const pDoc(pxCursor ? pxCursor->GetDoc() : &pxRange->GetDoc()); + SwPaM aPam(pDoc->GetNodes()); + SwPaM * pPam(nullptr); + if(pxCursor) + { + pPam = pxCursor->GetPaM(); + } + else + { + if (pxRange->GetPositions(aPam)) + { + pPam = & aPam; + } + } + if (!pPam) { return false; } + bRet = pDoc->getIDocumentContentOperations().CopyRange(*pPam, aPos, SwCopyFlags::CheckPosInFly) + || bRet; + } + + pInsDoc->getIDocumentFieldsAccess().UnlockExpFields(); + if( !pInsDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() ) + pInsDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); + + return bRet; +} + +uno::Reference< text::XAutoTextEntry > SwXAutoTextGroup::insertNewByName(const OUString& aName, + const OUString& aTitle, const uno::Reference< text::XTextRange > & xTextRange) +{ + SolarMutexGuard aGuard; + if(hasByName(aName)) + throw container::ElementExistException(); + if(!xTextRange.is()) + throw uno::RuntimeException(); + + std::unique_ptr<SwTextBlocks> pGlosGroup; + if (pGlossaries) + pGlosGroup = pGlossaries->GetGroupDoc(m_sGroupName); + const OUString& sShortName(aName); + const OUString& sLongName(aTitle); + if (pGlosGroup && !pGlosGroup->GetError()) + { + uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY); + SwXTextRange* pxRange = nullptr; + OTextCursorHelper* pxCursor = nullptr; + if(xRangeTunnel.is()) + { + pxRange = reinterpret_cast<SwXTextRange*>(xRangeTunnel->getSomething( + SwXTextRange::getUnoTunnelId())); + pxCursor = reinterpret_cast<OTextCursorHelper*>(xRangeTunnel->getSomething( + OTextCursorHelper::getUnoTunnelId())); + } + + OUString sOnlyText; + OUString* pOnlyText = nullptr; + bool bNoAttr = !pxCursor && !pxRange; + if(bNoAttr) + { + sOnlyText = xTextRange->getString(); + pOnlyText = &sOnlyText; + } + + const SvxAutoCorrCfg& rCfg = SvxAutoCorrCfg::Get(); + + SwDoc* pGDoc = pGlosGroup->GetDoc(); + + // Until there is an option for that, delete base util::URL + if(rCfg.IsSaveRelFile()) + { + INetURLObject aTemp(pGlosGroup->GetFileName()); + pGlosGroup->SetBaseURL( aTemp.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + } + else + pGlosGroup->SetBaseURL( OUString() ); + + sal_uInt16 nRet = USHRT_MAX; + if( pOnlyText ) + nRet = pGlosGroup->PutText( sShortName, sLongName, *pOnlyText ); + else + { + pGlosGroup->ClearDoc(); + if( pGlosGroup->BeginPutDoc( sShortName, sLongName ) ) + { + pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines ); + lcl_CopySelToDoc( pGDoc, pxCursor, pxRange ); + pGDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE); + nRet = pGlosGroup->PutDoc(); + } + } + + if (nRet == USHRT_MAX) + { + throw uno::RuntimeException(); + } + } + pGlosGroup.reset(); + + uno::Reference< text::XAutoTextEntry > xEntry; + + try + { + xEntry = pGlossaries ? + pGlossaries->GetAutoTextEntry( m_sGroupName, sName, sShortName ) : + uno::Reference< text::XAutoTextEntry >(); + OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::insertNewByName: no UNO object created? How this?" ); + // we just inserted the entry into the group, so why doesn't it exist? + } + catch (const container::ElementExistException&) + { + throw; + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "Error Getting AutoText!", + static_cast < OWeakObject * > ( this ), + anyEx ); + } + + return xEntry; +} + +void SwXAutoTextGroup::removeByName(const OUString& aEntryName) +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if(!pGlosGroup || pGlosGroup->GetError()) + throw container::NoSuchElementException(); + + sal_uInt16 nIdx = pGlosGroup->GetIndex(aEntryName); + if ( nIdx == USHRT_MAX ) + throw container::NoSuchElementException(); + + pGlosGroup->Delete(nIdx); +} + +OUString SwXAutoTextGroup::getName() +{ + SolarMutexGuard aGuard; + return sName; +} + +void SwXAutoTextGroup::setName(const OUString& rName) +{ + SolarMutexGuard aGuard; + if( !pGlossaries ) + throw uno::RuntimeException(); + + sal_Int32 nNewDelimPos = rName.lastIndexOf( GLOS_DELIM ); + sal_Int32 nOldDelimPos = sName.lastIndexOf( GLOS_DELIM ); + + OUString aNewSuffix; + if (nNewDelimPos > -1) + aNewSuffix = rName.copy( nNewDelimPos + 1 ); + OUString aOldSuffix; + if (nOldDelimPos > -1) + aOldSuffix = sName.copy( nOldDelimPos + 1 ); + + sal_Int32 nNewNumeric = aNewSuffix.toInt32(); + sal_Int32 nOldNumeric = aOldSuffix.toInt32(); + + OUString aNewPrefix( (nNewDelimPos > 1) ? rName.copy( 0, nNewDelimPos ) : rName ); + OUString aOldPrefix( (nOldDelimPos > 1) ? sName.copy( 0, nOldDelimPos ) : sName ); + + if ( sName == rName || + ( nNewNumeric == nOldNumeric && aNewPrefix == aOldPrefix ) ) + return; + OUString sNewGroup(rName); + if (sNewGroup.indexOf(GLOS_DELIM)<0) + { + sNewGroup += OUStringChar(GLOS_DELIM) + "0"; + } + + //the name must be saved, the group may be invalidated while in RenameGroupDoc() + SwGlossaries* pTempGlossaries = pGlossaries; + + OUString sPreserveTitle( pGlossaries->GetGroupTitle( sName ) ); + if ( !pGlossaries->RenameGroupDoc( sName, sNewGroup, sPreserveTitle ) ) + throw uno::RuntimeException(); + sName = rName; + m_sGroupName = sNewGroup; + pGlossaries = pTempGlossaries; +} + +sal_Int32 SwXAutoTextGroup::getCount() +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + return static_cast<sal_Int32>(pGlosGroup->GetCount()); +} + +uno::Any SwXAutoTextGroup::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + const sal_uInt16 nCount = pGlosGroup->GetCount(); + if (nIndex < 0 || nIndex >= static_cast<sal_Int32>(nCount)) + throw lang::IndexOutOfBoundsException(); + return getByName(pGlosGroup->GetShortName(static_cast<sal_uInt16>(nIndex))); +} + +uno::Type SwXAutoTextGroup::getElementType() +{ + return cppu::UnoType<text::XAutoTextEntry>::get(); + +} + +sal_Bool SwXAutoTextGroup::hasElements() +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + return pGlosGroup->GetCount() > 0; + +} + +uno::Any SwXAutoTextGroup::getByName(const OUString& _rName) +{ + SolarMutexGuard aGuard; + uno::Reference< text::XAutoTextEntry > xEntry = pGlossaries->GetAutoTextEntry( m_sGroupName, sName, _rName ); + OSL_ENSURE( xEntry.is(), "SwXAutoTextGroup::getByName: GetAutoTextEntry is fractious!" ); + // we told it to create the object, so why didn't it? + return makeAny( xEntry ); +} + +uno::Sequence< OUString > SwXAutoTextGroup::getElementNames() +{ + SolarMutexGuard aGuard; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + + const sal_uInt16 nCount = pGlosGroup->GetCount(); + uno::Sequence< OUString > aEntryNames(nCount); + OUString *pArr = aEntryNames.getArray(); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + pArr[i] = pGlosGroup->GetShortName(i); + return aEntryNames; +} + +sal_Bool SwXAutoTextGroup::hasByName(const OUString& rName) +{ + SolarMutexGuard aGuard; + bool bRet = false; + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if (!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + + const sal_uInt16 nCount = pGlosGroup->GetCount(); + for( sal_uInt16 i = 0; i < nCount; ++i ) + { + OUString sCompare(pGlosGroup->GetShortName(i)); + if(sCompare.equalsIgnoreAsciiCase(rName)) + { + bRet = true; + break; + } + } + return bRet; +} + +uno::Reference< beans::XPropertySetInfo > SwXAutoTextGroup::getPropertySetInfo() +{ + static uno::Reference< beans::XPropertySetInfo > xRet = pPropSet->getPropertySetInfo(); + return xRet; +} + +void SwXAutoTextGroup::setPropertyValue( + const OUString& rPropertyName, const uno::Any& aValue) +{ + SolarMutexGuard aGuard; + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName ); + + if(!pEntry) + throw beans::UnknownPropertyException(rPropertyName); + + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if(!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + switch(pEntry->nWID) + { + case WID_GROUP_TITLE: + { + OUString sNewTitle; + aValue >>= sNewTitle; + if(sNewTitle.isEmpty()) + throw lang::IllegalArgumentException(); + bool bChanged = sNewTitle != pGlosGroup->GetName(); + pGlosGroup->SetName(sNewTitle); + if(bChanged && HasGlossaryList()) + GetGlossaryList()->ClearGroups(); + } + break; + } +} + +uno::Any SwXAutoTextGroup::getPropertyValue(const OUString& rPropertyName) +{ + SolarMutexGuard aGuard; + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + + if(!pEntry) + throw beans::UnknownPropertyException(rPropertyName); + std::unique_ptr<SwTextBlocks> pGlosGroup(pGlossaries ? pGlossaries->GetGroupDoc(m_sGroupName) : nullptr); + if(!pGlosGroup || pGlosGroup->GetError()) + throw uno::RuntimeException(); + + uno::Any aAny; + switch(pEntry->nWID) + { + case WID_GROUP_PATH: + aAny <<= pGlosGroup->GetFileName(); + break; + case WID_GROUP_TITLE: + aAny <<= pGlosGroup->GetName(); + break; + } + return aAny; +} + +void SwXAutoTextGroup::addPropertyChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/) +{ +} + +void SwXAutoTextGroup::removePropertyChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/) +{ +} + +void SwXAutoTextGroup::addVetoableChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/) +{ +} + +void SwXAutoTextGroup::removeVetoableChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/) +{ +} + +void SwXAutoTextGroup::Invalidate() +{ + pGlossaries = nullptr; + sName.clear(); + m_sGroupName.clear(); +} + +OUString SwXAutoTextGroup::getImplementationName() +{ + return "SwXAutoTextGroup"; +} + +sal_Bool SwXAutoTextGroup::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SwXAutoTextGroup::getSupportedServiceNames() +{ + uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextGroup" }; + return aRet; +} + +namespace +{ + class theSwXAutoTextEntryUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXAutoTextEntryUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXAutoTextEntry::getUnoTunnelId() +{ + return theSwXAutoTextEntryUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXAutoTextEntry::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXAutoTextEntry>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); + } + return 0; +} + +SwXAutoTextEntry::SwXAutoTextEntry(SwGlossaries* pGlss, const OUString& rGroupName, + const OUString& rEntryName) : + WeakComponentImplHelper(m_aMutex), + pGlossaries(pGlss), + sGroupName(rGroupName), + sEntryName(rEntryName) +{ +} + +SwXAutoTextEntry::~SwXAutoTextEntry() +{ + SolarMutexGuard aGuard; + + // ensure that any pending modifications are written + implFlushDocument( true ); +} + +void SwXAutoTextEntry::implFlushDocument( bool _bCloseDoc ) +{ + if ( xDocSh.is() ) + { + if ( xDocSh->GetDoc()->getIDocumentState().IsModified () ) + xDocSh->Save(); + + if ( _bCloseDoc ) + { + // stop listening at the document + EndListening( *xDocSh ); + + xDocSh->DoClose(); + xDocSh.clear(); + } + } +} + +void SwXAutoTextEntry::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) +{ + if ( &_rBC == xDocSh.get() ) + { // it's our document + if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&_rHint)) + { + if (SfxEventHintId::PrepareCloseDoc == pEventHint->GetEventId()) + { + implFlushDocument(); + mxBodyText.clear(); + EndListening( *xDocSh ); + xDocSh.clear(); + } + } + else + { + if ( SfxHintId::Deinitializing == _rHint.GetId() ) + { + // our document is dying (possibly because we're shutting down, and the document was notified + // earlier than we are?) + // stop listening at the docu + EndListening( *xDocSh ); + // and release our reference + xDocSh.clear(); + } + } + } +} + +void SwXAutoTextEntry::GetBodyText () +{ + SolarMutexGuard aGuard; + + xDocSh = pGlossaries->EditGroupDoc ( sGroupName, sEntryName, false ); + OSL_ENSURE( xDocSh.is(), "SwXAutoTextEntry::GetBodyText: unexpected: no doc returned by EditGroupDoc!" ); + + // start listening at the document + StartListening( *xDocSh ); + + mxBodyText = new SwXBodyText ( xDocSh->GetDoc() ); +} + +void SwXAutoTextEntry::disposing() +{ + SolarMutexGuard g; + implFlushDocument(true); +} + +uno::Reference< text::XTextCursor > SwXAutoTextEntry::createTextCursor() +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + return mxBodyText->createTextCursor(); +} + +uno::Reference< text::XTextCursor > SwXAutoTextEntry::createTextCursorByRange( + const uno::Reference< text::XTextRange > & aTextPosition) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + return mxBodyText->createTextCursorByRange ( aTextPosition ); +} + +void SwXAutoTextEntry::insertString(const uno::Reference< text::XTextRange > & xRange, const OUString& aString, sal_Bool bAbsorb) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + mxBodyText->insertString ( xRange, aString, bAbsorb ); +} + +void SwXAutoTextEntry::insertControlCharacter(const uno::Reference< text::XTextRange > & xRange, + sal_Int16 nControlCharacter, sal_Bool bAbsorb) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + mxBodyText->insertControlCharacter ( xRange, nControlCharacter, bAbsorb ); +} + +void SwXAutoTextEntry::insertTextContent( + const uno::Reference< text::XTextRange > & xRange, + const uno::Reference< text::XTextContent > & xContent, sal_Bool bAbsorb) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + mxBodyText->insertTextContent ( xRange, xContent, bAbsorb ); +} + +void SwXAutoTextEntry::removeTextContent( + const uno::Reference< text::XTextContent > & xContent) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + mxBodyText->removeTextContent ( xContent ); +} + +uno::Reference< text::XText > SwXAutoTextEntry::getText() +{ + SolarMutexGuard aGuard; + uno::Reference< text::XText > xRet = static_cast<text::XText*>(this); + return xRet; +} + +uno::Reference< text::XTextRange > SwXAutoTextEntry::getStart() +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + return mxBodyText->getStart(); +} + +uno::Reference< text::XTextRange > SwXAutoTextEntry::getEnd() +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + return mxBodyText->getEnd(); +} + +OUString SwXAutoTextEntry::getString() +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + return mxBodyText->getString(); +} + +void SwXAutoTextEntry::setString(const OUString& aString) +{ + SolarMutexGuard aGuard; + EnsureBodyText(); + mxBodyText->setString( aString ); +} + +void SwXAutoTextEntry::applyTo(const uno::Reference< text::XTextRange > & xTextRange) +{ + SolarMutexGuard aGuard; + + // ensure that any pending modifications are written + // reason is that we're holding the _copy_ of the auto text, while the real auto text + // is stored somewhere. And below, we're not working with our copy, but only tell the target + // TextRange to work with the stored version. + // #96380# - 2003-03-03 - fs@openoffice.org + implFlushDocument(); + // TODO: think about if we should pass "true" here + // The difference would be that when the next modification is made to this instance here, then + // we would be forced to open the document again, instead of working on our current copy. + // This means that we would reflect any changes which were done to the AutoText by foreign instances + // in the meantime + + uno::Reference<lang::XUnoTunnel> xTunnel( xTextRange, uno::UNO_QUERY); + SwXTextRange* pRange = nullptr; + OTextCursorHelper* pCursor = nullptr; + SwXText *pText = nullptr; + + if(xTunnel.is()) + { + pRange = reinterpret_cast < SwXTextRange* > + ( xTunnel->getSomething( SwXTextRange::getUnoTunnelId() ) ); + pCursor = reinterpret_cast < OTextCursorHelper*> + ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) ); + pText = reinterpret_cast < SwXText* > + ( xTunnel->getSomething( SwXText::getUnoTunnelId() ) ); + } + + SwDoc* pDoc = nullptr; + if (pRange) + pDoc = &pRange->GetDoc(); + else if ( pCursor ) + pDoc = pCursor->GetDoc(); + else if ( pText && pText->GetDoc() ) + { + xTunnel.set(pText->getStart(), uno::UNO_QUERY); + if (xTunnel.is()) + { + pCursor = reinterpret_cast < OTextCursorHelper* > + ( xTunnel->getSomething( OTextCursorHelper::getUnoTunnelId() ) ); + if (pCursor) + pDoc = pText->GetDoc(); + } + } + + if(!pDoc) + throw uno::RuntimeException(); + + SwPaM InsertPaM(pDoc->GetNodes()); + if (pRange) + { + if (!pRange->GetPositions(InsertPaM)) + { + throw uno::RuntimeException(); + } + } + else + { + InsertPaM = *pCursor->GetPaM(); + } + + std::unique_ptr<SwTextBlocks> pBlock(pGlossaries->GetGroupDoc(sGroupName)); + const bool bResult = pBlock && !pBlock->GetError() + && pDoc->InsertGlossary( *pBlock, sEntryName, InsertPaM); + + if(!bResult) + throw uno::RuntimeException(); +} + +OUString SwXAutoTextEntry::getImplementationName() +{ + return "SwXAutoTextEntry"; +} + +sal_Bool SwXAutoTextEntry::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SwXAutoTextEntry::getSupportedServiceNames() +{ + uno::Sequence<OUString> aRet { "com.sun.star.text.AutoTextEntry" }; + return aRet; +} + +uno::Reference< container::XNameReplace > SwXAutoTextEntry::getEvents() +{ + return new SwAutoTextEventDescriptor( *this ); +} + +const struct SvEventDescription aAutotextEvents[] = +{ + { SvMacroItemId::SwStartInsGlossary, "OnInsertStart" }, + { SvMacroItemId::SwEndInsGlossary, "OnInsertDone" }, + { SvMacroItemId::NONE, nullptr } +}; + +SwAutoTextEventDescriptor::SwAutoTextEventDescriptor( + SwXAutoTextEntry& rAutoText ) : + SvBaseEventDescriptor(aAutotextEvents), + rAutoTextEntry(rAutoText) +{ +} + +SwAutoTextEventDescriptor::~SwAutoTextEventDescriptor() +{ +} + +OUString SwAutoTextEventDescriptor::getImplementationName() +{ + return "SwAutoTextEventDescriptor"; +} + +void SwAutoTextEventDescriptor::replaceByName( + const SvMacroItemId nEvent, + const SvxMacro& rMacro) +{ + OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(), + "Strangely enough, the AutoText vanished!" ); + OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) || + (nEvent == SvMacroItemId::SwStartInsGlossary) , + "Unknown event ID" ); + + SwGlossaries *const pGlossaries = + const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries()); + std::unique_ptr<SwTextBlocks> pBlocks( + pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() )); + OSL_ENSURE( pBlocks, + "can't get autotext group; SwAutoTextEntry has illegal name?"); + + if( pBlocks && !pBlocks->GetError()) + { + sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() ); + if( nIndex != USHRT_MAX ) + { + SvxMacroTableDtor aMacroTable; + if( pBlocks->GetMacroTable( nIndex, aMacroTable ) ) + { + aMacroTable.Insert( nEvent, rMacro ); + pBlocks->SetMacroTable( nIndex, aMacroTable ); + } + } + } + // else: ignore +} + +void SwAutoTextEventDescriptor::getByName( + SvxMacro& rMacro, + const SvMacroItemId nEvent ) +{ + OSL_ENSURE( nullptr != rAutoTextEntry.GetGlossaries(), "no AutoText" ); + OSL_ENSURE( (nEvent == SvMacroItemId::SwEndInsGlossary) || + (nEvent == SvMacroItemId::SwStartInsGlossary) , + "Unknown event ID" ); + + SwGlossaries *const pGlossaries = + const_cast<SwGlossaries*>(rAutoTextEntry.GetGlossaries()); + std::unique_ptr<SwTextBlocks> pBlocks( + pGlossaries->GetGroupDoc( rAutoTextEntry.GetGroupName() )); + OSL_ENSURE( pBlocks, + "can't get autotext group; SwAutoTextEntry has illegal name?"); + + // return empty macro, unless macro is found + OUString sEmptyStr; + SvxMacro aEmptyMacro(sEmptyStr, sEmptyStr); + rMacro = aEmptyMacro; + + if ( pBlocks && !pBlocks->GetError()) + { + sal_uInt16 nIndex = pBlocks->GetIndex( rAutoTextEntry.GetEntryName() ); + if( nIndex != USHRT_MAX ) + { + SvxMacroTableDtor aMacroTable; + if( pBlocks->GetMacroTable( nIndex, aMacroTable ) ) + { + SvxMacro *pMacro = aMacroTable.Get( nEvent ); + if( pMacro ) + rMacro = *pMacro; + } + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +SwXAutoTextContainer_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + //the module may not be loaded + SolarMutexGuard aGuard; + SwGlobals::ensure(); + return cppu::acquire(new SwXAutoTextContainer()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unodefaults.cxx b/sw/source/uibase/uno/unodefaults.cxx new file mode 100644 index 000000000..6daaeee4e --- /dev/null +++ b/sw/source/uibase/uno/unodefaults.cxx @@ -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 . + */ + +#include "unodefaults.hxx" +#include <svx/unoprov.hxx> +#include <drawdoc.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> + +SwSvxUnoDrawPool::SwSvxUnoDrawPool(SwDoc* pDoc) + : SvxUnoDrawPool(pDoc->getIDocumentDrawModelAccess().GetDrawModel(), SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS_WRITER) + , m_pDoc(pDoc) +{ +} + +SwSvxUnoDrawPool::~SwSvxUnoDrawPool() throw() +{ +} + +SfxItemPool* SwSvxUnoDrawPool::getModelPool( bool /*bReadOnly*/ ) throw() +{ + if(m_pDoc) + { + + // DVO, OD 01.10.2003 #i18732# - return item pool of writer document; + // it contains draw model item pool as secondary pool. + //SdrModel* pModel = m_pDoc->MakeDrawModel(); + //return &pModel->GetItemPool(); + // #i52858# - method name changed + m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + return &(m_pDoc->GetAttrPool()); + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unodefaults.hxx b/sw/source/uibase/uno/unodefaults.hxx new file mode 100644 index 000000000..23bd6b543 --- /dev/null +++ b/sw/source/uibase/uno/unodefaults.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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_UIBASE_UNO_UNODEFAULTS_HXX +#define INCLUDED_SW_SOURCE_UIBASE_UNO_UNODEFAULTS_HXX + +#include <svx/unopool.hxx> + +class SwDoc; + +class SwSvxUnoDrawPool : public SvxUnoDrawPool +{ + SwDoc* m_pDoc; +public: + SwSvxUnoDrawPool(SwDoc* pDoc); + virtual ~SwSvxUnoDrawPool() throw() override; + + virtual SfxItemPool* getModelPool( bool bReadOnly ) throw() override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unodispatch.cxx b/sw/source/uibase/uno/unodispatch.cxx new file mode 100644 index 000000000..77ef3e4a0 --- /dev/null +++ b/sw/source/uibase/uno/unodispatch.cxx @@ -0,0 +1,393 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <comphelper/servicehelper.hxx> +#include <unodispatch.hxx> +#include <view.hxx> +#include <cmdid.h> +#include <wrtsh.hxx> +#include <dbmgr.hxx> + +using namespace ::com::sun::star; + +static const char cURLFormLetter[] = ".uno:DataSourceBrowser/FormLetter"; +static const char cURLInsertContent[] = ".uno:DataSourceBrowser/InsertContent";//data into fields +static const char cURLInsertColumns[] = ".uno:DataSourceBrowser/InsertColumns";//data into text +static const char cURLDocumentDataSource[] = ".uno:DataSourceBrowser/DocumentDataSource";//current data source of the document +static const char cInternalDBChangeNotification[] = ".uno::Writer/DataSourceChanged"; + +SwXDispatchProviderInterceptor::SwXDispatchProviderInterceptor(SwView& rVw) : + m_pView(&rVw) +{ + uno::Reference< frame::XFrame> xUnoFrame = m_pView->GetViewFrame()->GetFrame().GetFrameInterface(); + m_xIntercepted.set(xUnoFrame, uno::UNO_QUERY); + if(m_xIntercepted.is()) + { + osl_atomic_increment(&m_refCount); + m_xIntercepted->registerDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this)); + // this should make us the top-level dispatch-provider for the component, via a call to our + // setDispatchProvider we should have got a fallback for requests we (i.e. our master) cannot fulfill + uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY); + if (xInterceptedComponent.is()) + xInterceptedComponent->addEventListener(static_cast<lang::XEventListener*>(this)); + osl_atomic_decrement(&m_refCount); + } +} + +SwXDispatchProviderInterceptor::~SwXDispatchProviderInterceptor() +{ +} + +uno::Reference< frame::XDispatch > SwXDispatchProviderInterceptor::queryDispatch( + const util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags ) +{ + DispatchMutexLock_Impl aLock; + uno::Reference< frame::XDispatch> xResult; + // create some dispatch ... + if(m_pView && aURL.Complete.startsWith(".uno:DataSourceBrowser/")) + { + if(aURL.Complete == cURLFormLetter || + aURL.Complete == cURLInsertContent || + aURL.Complete == cURLInsertColumns || + aURL.Complete == cURLDocumentDataSource) + { + if(!m_xDispatch.is()) + m_xDispatch = new SwXDispatch(*m_pView); + xResult = m_xDispatch; + } + } + + // ask our slave provider + if (!xResult.is() && m_xSlaveDispatcher.is()) + xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags); + + return xResult; +} + +uno::Sequence<OUString> SAL_CALL SwXDispatchProviderInterceptor::getInterceptedURLs() +{ + uno::Sequence<OUString> aRet = + { + OUString(".uno:DataSourceBrowser/*") + }; + + return aRet; +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SwXDispatchProviderInterceptor::queryDispatches( + const uno::Sequence< frame::DispatchDescriptor >& aDescripts ) +{ + DispatchMutexLock_Impl aLock; + uno::Sequence< uno::Reference< frame::XDispatch> > aReturn(aDescripts.getLength()); + std::transform(aDescripts.begin(), aDescripts.end(), aReturn.begin(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return aReturn; +} + +uno::Reference< frame::XDispatchProvider > SwXDispatchProviderInterceptor::getSlaveDispatchProvider( ) +{ + DispatchMutexLock_Impl aLock; + return m_xSlaveDispatcher; +} + +void SwXDispatchProviderInterceptor::setSlaveDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider ) +{ + DispatchMutexLock_Impl aLock; + m_xSlaveDispatcher = xNewDispatchProvider; +} + +uno::Reference< frame::XDispatchProvider > SwXDispatchProviderInterceptor::getMasterDispatchProvider( ) +{ + DispatchMutexLock_Impl aLock; + return m_xMasterDispatcher; +} + +void SwXDispatchProviderInterceptor::setMasterDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& xNewSupplier ) +{ + DispatchMutexLock_Impl aLock; + m_xMasterDispatcher = xNewSupplier; +} + +void SwXDispatchProviderInterceptor::disposing( const lang::EventObject& ) +{ + DispatchMutexLock_Impl aLock; + if (m_xIntercepted.is()) + { + m_xIntercepted->releaseDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this)); + uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY); + if (xInterceptedComponent.is()) + xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this)); + m_xDispatch = nullptr; + } + m_xIntercepted = nullptr; +} + +namespace +{ + class theSwXDispatchProviderInterceptorUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXDispatchProviderInterceptorUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXDispatchProviderInterceptor::getUnoTunnelId() +{ + return theSwXDispatchProviderInterceptorUnoTunnelId::get().getSeq(); +} + +sal_Int64 SwXDispatchProviderInterceptor::getSomething( + const uno::Sequence< sal_Int8 >& aIdentifier ) +{ + if( isUnoTunnelId<SwXDispatchProviderInterceptor>(aIdentifier) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); + } + return 0; +} + +void SwXDispatchProviderInterceptor::Invalidate() +{ + DispatchMutexLock_Impl aLock; + if (m_xIntercepted.is()) + { + m_xIntercepted->releaseDispatchProviderInterceptor(static_cast<frame::XDispatchProviderInterceptor*>(this)); + uno::Reference< lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY); + if (xInterceptedComponent.is()) + xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this)); + m_xDispatch = nullptr; + } + m_xIntercepted = nullptr; + m_pView = nullptr; +} + +SwXDispatch::SwXDispatch(SwView& rVw) : + m_pView(&rVw), + m_bOldEnable(false), + m_bListenerAdded(false) +{ +} + +SwXDispatch::~SwXDispatch() +{ + if(m_bListenerAdded && m_pView) + { + uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject(); + uno::Reference<view::XSelectionChangeListener> xThis = this; + xSupplier->removeSelectionChangeListener(xThis); + } +} + +void SwXDispatch::dispatch(const util::URL& aURL, + const uno::Sequence< beans::PropertyValue >& aArgs) +{ + if(!m_pView) + throw uno::RuntimeException(); +#if !HAVE_FEATURE_DBCONNECTIVITY + (void) aArgs; + if (false) + { + } +#else + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwDBManager* pDBManager = rSh.GetDBManager(); + if(aURL.Complete == cURLInsertContent) + { + svx::ODataAccessDescriptor aDescriptor(aArgs); + SwMergeDescriptor aMergeDesc( DBMGR_MERGE, rSh, aDescriptor ); + pDBManager->Merge(aMergeDesc); + } + else if(aURL.Complete == cURLInsertColumns) + { + SwDBManager::InsertText(rSh, aArgs); + } + else if(aURL.Complete == cURLFormLetter) + { + SfxUnoAnyItem aDBProperties(FN_PARAM_DATABASE_PROPERTIES, uno::makeAny(aArgs)); + m_pView->GetViewFrame()->GetDispatcher()->ExecuteList( + FN_MAILMERGE_WIZARD, + SfxCallMode::ASYNCHRON, + { &aDBProperties }); + } +#endif + else if(aURL.Complete == cURLDocumentDataSource) + { + OSL_FAIL("SwXDispatch::dispatch: this URL is not to be dispatched!"); + } + else if(aURL.Complete == cInternalDBChangeNotification) + { + frame::FeatureStateEvent aEvent; + aEvent.Source = *static_cast<cppu::OWeakObject*>(this); + + const SwDBData& rData = m_pView->GetWrtShell().GetDBDesc(); + svx::ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(rData.sDataSource); + aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= rData.sCommand; + aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= rData.nCommandType; + + aEvent.State <<= aDescriptor.createPropertyValueSequence(); + aEvent.IsEnabled = !rData.sDataSource.isEmpty(); + + // calls to statusChanged may call addStatusListener or removeStatusListener + // so copy m_aStatusListenerVector on stack + auto copyStatusListenerVector = m_aStatusListenerVector; + for (auto & status : copyStatusListenerVector) + { + if(status.aURL.Complete == cURLDocumentDataSource) + { + aEvent.FeatureURL = status.aURL; + status.xListener->statusChanged( aEvent ); + } + } + } + else + throw uno::RuntimeException(); + +} + +void SwXDispatch::addStatusListener( + const uno::Reference< frame::XStatusListener >& xControl, const util::URL& aURL ) +{ + if(!m_pView) + throw uno::RuntimeException(); + ShellMode eMode = m_pView->GetShellMode(); + bool bEnable = ShellMode::Text == eMode || + ShellMode::ListText == eMode || + ShellMode::TableText == eMode || + ShellMode::TableListText == eMode; + + m_bOldEnable = bEnable; + frame::FeatureStateEvent aEvent; + aEvent.IsEnabled = bEnable; + aEvent.Source = *static_cast<cppu::OWeakObject*>(this); + aEvent.FeatureURL = aURL; + + // one of the URLs requires a special state... + if (aURL.Complete == cURLDocumentDataSource) + { + const SwDBData& rData = m_pView->GetWrtShell().GetDBDesc(); + + svx::ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(rData.sDataSource); + aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= rData.sCommand; + aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= rData.nCommandType; + + aEvent.State <<= aDescriptor.createPropertyValueSequence(); + aEvent.IsEnabled = !rData.sDataSource.isEmpty(); + } + + xControl->statusChanged( aEvent ); + + StatusStruct_Impl aStatus; + aStatus.xListener = xControl; + aStatus.aURL = aURL; + m_aStatusListenerVector.emplace_back(aStatus); + + if(!m_bListenerAdded) + { + uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject(); + uno::Reference<view::XSelectionChangeListener> xThis = this; + xSupplier->addSelectionChangeListener(xThis); + m_bListenerAdded = true; + } +} + +void SwXDispatch::removeStatusListener( + const uno::Reference< frame::XStatusListener >& xControl, const util::URL& ) +{ + m_aStatusListenerVector.erase( + std::remove_if(m_aStatusListenerVector.begin(), m_aStatusListenerVector.end(), + [&](const StatusStruct_Impl& status) { return status.xListener.get() == xControl.get(); }), + m_aStatusListenerVector.end()); + if(m_aStatusListenerVector.empty() && m_pView) + { + uno::Reference<view::XSelectionSupplier> xSupplier = m_pView->GetUNOObject(); + uno::Reference<view::XSelectionChangeListener> xThis = this; + xSupplier->removeSelectionChangeListener(xThis); + m_bListenerAdded = false; + } +} + +void SwXDispatch::selectionChanged( const lang::EventObject& ) +{ + ShellMode eMode = m_pView->GetShellMode(); + bool bEnable = ShellMode::Text == eMode || + ShellMode::ListText == eMode || + ShellMode::TableText == eMode || + ShellMode::TableListText == eMode; + if(bEnable != m_bOldEnable) + { + m_bOldEnable = bEnable; + frame::FeatureStateEvent aEvent; + aEvent.IsEnabled = bEnable; + aEvent.Source = *static_cast<cppu::OWeakObject*>(this); + + // calls to statusChanged may call addStatusListener or removeStatusListener + // so copy m_aStatusListenerVector on stack + auto copyStatusListenerVector = m_aStatusListenerVector; + for (auto & status : copyStatusListenerVector) + { + aEvent.FeatureURL = status.aURL; + if (status.aURL.Complete != cURLDocumentDataSource) + // the document's data source does not depend on the selection, so it's state does not change here + status.xListener->statusChanged( aEvent ); + } + } +} + +void SwXDispatch::disposing( const lang::EventObject& rSource ) +{ + uno::Reference<view::XSelectionSupplier> xSupplier(rSource.Source, uno::UNO_QUERY); + uno::Reference<view::XSelectionChangeListener> xThis = this; + xSupplier->removeSelectionChangeListener(xThis); + m_bListenerAdded = false; + + lang::EventObject aObject; + aObject.Source = static_cast<cppu::OWeakObject*>(this); + // calls to statusChanged may call addStatusListener or removeStatusListener + // so copy m_aStatusListenerVector on stack + auto copyStatusListenerVector = m_aStatusListenerVector; + for (auto & status : copyStatusListenerVector) + { + status.xListener->disposing(aObject); + } + m_pView = nullptr; +} + +const char* SwXDispatch::GetDBChangeURL() +{ + return cInternalDBChangeNotification; +} + +SwXDispatchProviderInterceptor::DispatchMutexLock_Impl::DispatchMutexLock_Impl() +{ +} + +SwXDispatchProviderInterceptor::DispatchMutexLock_Impl::~DispatchMutexLock_Impl() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unodoc.cxx b/sw/source/uibase/uno/unodoc.cxx new file mode 100644 index 000000000..a61b23778 --- /dev/null +++ b/sw/source/uibase/uno/unodoc.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include <com/sun/star/frame/XModel.hpp> +#include <sfx2/sfxmodelfactory.hxx> +#include <swdll.hxx> +#include <unofreg.hxx> +#include <docsh.hxx> +#include <globdoc.hxx> +#include <wdocsh.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +// com.sun.star.comp.Writer.TextDocument + +uno::Sequence< OUString > SwTextDocument_getSupportedServiceNames() throw() +{ + // return only top level services here! All others must be + // resolved by rtti! + uno::Sequence< OUString > aRet { "com.sun.star.text.TextDocument" }; + return aRet; +} + +OUString SwTextDocument_getImplementationName() throw() +{ + return "com.sun.star.comp.Writer.TextDocument"; +} + +uno::Reference< uno::XInterface > SwTextDocument_createInstance( + const uno::Reference< lang::XMultiServiceFactory >&, SfxModelFlags _nCreationFlags ) +{ + SolarMutexGuard aGuard; + SwGlobals::ensure(); + SfxObjectShell* pShell = new SwDocShell( _nCreationFlags ); + return uno::Reference< uno::XInterface >( pShell->GetModel() ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_WebDocument_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + SwGlobals::ensure(); + SfxObjectShell* pShell = new SwWebDocShell; + uno::Reference< uno::XInterface > model( pShell->GetModel() ); + model->acquire(); + return model.get(); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Writer_GlobalDocument_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + SolarMutexGuard aGuard; + SwGlobals::ensure(); + SfxObjectShell* pShell = new SwGlobalDocShell( SfxObjectCreateMode::STANDARD ); + uno::Reference< uno::XInterface > model( pShell->GetModel() ); + model->acquire(); + return model.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unofreg.cxx b/sw/source/uibase/uno/unofreg.cxx new file mode 100644 index 000000000..a170bb085 --- /dev/null +++ b/sw/source/uibase/uno/unofreg.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <swdll.hxx> +#include <unofreg.hxx> +#include <unomailmerge.hxx> +#include <sal/types.h> +#include <cppuhelper/factory.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <string.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; + +extern "C" +{ + +SAL_DLLPUBLIC_EXPORT void * sw_component_getFactory( + const char * pImplName, + void * pServiceManager, + void * ) +{ + void * pRet = nullptr; + if( pServiceManager ) + { + uno::Reference< XMultiServiceFactory > xMSF( + static_cast< XMultiServiceFactory * >( pServiceManager ) ); + + uno::Reference< XSingleServiceFactory > xFactory; + + const sal_Int32 nImplNameLen = strlen( pImplName ); + if( SwTextDocument_getImplementationName().equalsAsciiL( + pImplName, nImplNameLen ) ) + { + xFactory = ::sfx2::createSfxModelFactory( xMSF, + SwTextDocument_getImplementationName(), + SwTextDocument_createInstance, + SwTextDocument_getSupportedServiceNames() ); + } + + if( xFactory.is()) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + return pRet; +} + +} // extern "C" + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +SwXMailMerge_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ +#if HAVE_FEATURE_DBCONNECTIVITY + SolarMutexGuard aGuard; + + //the module may not be loaded + SwGlobals::ensure(); + return cppu::acquire(new SwXMailMerge()); +#else + return nullptr; +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unomailmerge.cxx b/sw/source/uibase/uno/unomailmerge.cxx new file mode 100644 index 000000000..2e47a54e1 --- /dev/null +++ b/sw/source/uibase/uno/unomailmerge.cxx @@ -0,0 +1,1174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/mutex.hxx> +#include <svl/itemprop.hxx> +#include <svx/dataaccessdescriptor.hxx> +#include <unotools/tempfile.hxx> +#include <sfx2/app.hxx> +#include <sfx2/docfilt.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <vcl/timer.hxx> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/text/MailMergeType.hpp> +#include <com/sun/star/text/MailMergeEvent.hpp> +#include <com/sun/star/text/XMailMergeListener.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/sdbcx/XRowLocate.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/mail/XSmtpService.hpp> +#include <sfx2/viewfrm.hxx> +#include <sfx2/event.hxx> +#include <cppuhelper/implbase.hxx> +#include <printdata.hxx> +#include <swevent.hxx> +#include <unomailmerge.hxx> +#include <unoprnms.hxx> +#include <unomap.hxx> +#include <swunohelper.hxx> +#include <docsh.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <view.hxx> +#include <dbmgr.hxx> +#include <unotxdoc.hxx> +#include <wrtsh.hxx> +#include <mmconfigitem.hxx> +#include <mailmergehelper.hxx> + +#include <iodetect.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; +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 SWUnoHelper; + +typedef ::utl::SharedUNOComponent< XInterface > SharedComponent; + +static osl::Mutex & GetMailMergeMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} + +namespace { + +enum CloseResult +{ + eSuccess, // successfully closed + eVetoed, // vetoed, ownership transferred to the vetoing instance + eFailed // failed for some unknown reason +}; + +} + +static CloseResult CloseModelAndDocSh( + Reference< frame::XModel > const &rxModel, + SfxObjectShellRef &rxDocSh ) +{ + CloseResult eResult = eSuccess; + + rxDocSh = nullptr; + + //! models/documents should never be disposed (they may still be + //! used for printing which is called asynchronously for example) + //! instead call close + Reference< util::XCloseable > xClose( rxModel, UNO_QUERY ); + if (xClose.is()) + { + try + { + //! 'sal_True' -> transfer ownership to vetoing object if vetoed! + //! I.e. now that object is responsible for closing the model and doc shell. + xClose->close( true ); + } + catch (const util::CloseVetoException&) + { + //! here we have the problem that the temporary file that is + //! currently being printed will never be deleted. :-( + eResult = eVetoed; + } + catch (const uno::RuntimeException&) + { + eResult = eFailed; + } + } + return eResult; +} + +/// @throws RuntimeException +static bool LoadFromURL_impl( + Reference< frame::XModel > &rxModel, + SfxObjectShellRef &rxDocSh, + const OUString &rURL, + bool bClose ) +{ + // try to open the document readonly and hidden + Reference< frame::XModel > xTmpModel; + Sequence < PropertyValue > aArgs( 1 ); + aArgs[0].Name = "Hidden"; + aArgs[0].Value <<= true; + try + { + Reference < XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() ); + xTmpModel.set( xDesktop->loadComponentFromURL( rURL, "_blank", 0, aArgs ), UNO_QUERY ); + } + catch (const Exception&) + { + return false; + } + + // try to get the DocShell + SwDocShell *pTmpDocShell = nullptr; + if (auto pTextDoc = comphelper::getUnoTunnelImplementation<SwXTextDocument>(xTmpModel); pTextDoc) + pTmpDocShell = pTextDoc->GetDocShell(); + + bool bRes = false; + if (xTmpModel.is() && pTmpDocShell) // everything available? + { + if (bClose) + CloseModelAndDocSh( rxModel, rxDocSh ); + // set new stuff + rxModel = xTmpModel; + rxDocSh = pTmpDocShell; + bRes = true; + } + else + { + // SfxObjectShellRef is ok here, since the document will be explicitly closed + SfxObjectShellRef xTmpDocSh = pTmpDocShell; + CloseModelAndDocSh( xTmpModel, xTmpDocSh ); + } + + return bRes; +} + +namespace +{ + class DelayedFileDeletion : public ::cppu::WeakImplHelper<util::XCloseListener> + { + protected: + ::osl::Mutex m_aMutex; + Reference< util::XCloseable > m_xDocument; + Timer m_aDeleteTimer; + OUString m_sTemporaryFile; + sal_Int32 m_nPendingDeleteAttempts; + + DelayedFileDeletion(DelayedFileDeletion const&) = delete; + DelayedFileDeletion& operator=(DelayedFileDeletion const&) = delete; + + public: + DelayedFileDeletion( const Reference< XModel >& _rxModel, + const OUString& _rTemporaryFile ); + + protected: + virtual ~DelayedFileDeletion( ) override; + + // XCloseListener + virtual void SAL_CALL queryClosing( const EventObject& _rSource, sal_Bool _bGetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const EventObject& _rSource ) override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + private: + void implTakeOwnership( ); + DECL_LINK( OnTryDeleteFile, Timer*, void ); + }; + + DelayedFileDeletion::DelayedFileDeletion( const Reference< XModel >& _rxModel, const OUString& _rTemporaryFile ) + : + m_xDocument( _rxModel, UNO_QUERY ) + ,m_sTemporaryFile( _rTemporaryFile ) + ,m_nPendingDeleteAttempts( 0 ) + { + osl_atomic_increment( &m_refCount ); + try + { + if ( m_xDocument.is() ) + { + m_xDocument->addCloseListener( this ); + // successfully added -> keep ourself alive + acquire(); + } + else { + OSL_FAIL("DelayedFileDeletion::DelayedFileDeletion: model is no component!" ); + } + } + catch (const Exception&) + { + OSL_FAIL("DelayedFileDeletion::DelayedFileDeletion: could not register as event listener at the model!" ); + } + osl_atomic_decrement( &m_refCount ); + } + + IMPL_LINK_NOARG(DelayedFileDeletion, OnTryDeleteFile, Timer *, void) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + bool bSuccess = false; + try + { + bool bDeliverOwnership = ( 0 == m_nPendingDeleteAttempts ); + // if this is our last attempt, then anybody which vetoes this has to take the consequences + // (means take the ownership) + m_xDocument->close( bDeliverOwnership ); + bSuccess = true; + } + catch (const util::CloseVetoException&) + { + // somebody vetoed -> next try + if ( m_nPendingDeleteAttempts ) + { + // next attempt + --m_nPendingDeleteAttempts; + m_aDeleteTimer.Start(); + } + else + bSuccess = true; // can't do anything here ... + } + catch (const Exception&) + { + OSL_FAIL("DelayedFileDeletion::OnTryDeleteFile: caught a strange exception!" ); + bSuccess = true; + // can't do anything here ... + } + + if ( bSuccess ) + { + SWUnoHelper::UCB_DeleteFile( m_sTemporaryFile ); + aGuard.clear(); + release(); // this should be our last reference, we should be dead after this + } + } + + void DelayedFileDeletion::implTakeOwnership( ) + { + // revoke ourself as listener + try + { + m_xDocument->removeCloseListener( this ); + } + catch (const Exception&) + { + OSL_FAIL("DelayedFileDeletion::implTakeOwnership: could not revoke the listener!" ); + } + + m_aDeleteTimer.SetTimeout( 3000 ); // 3 seconds + m_aDeleteTimer.SetInvokeHandler( LINK( this, DelayedFileDeletion, OnTryDeleteFile ) ); + m_nPendingDeleteAttempts = 3; // try 3 times at most + m_aDeleteTimer.Start( ); + } + + void SAL_CALL DelayedFileDeletion::queryClosing( const EventObject& , sal_Bool _bGetsOwnership ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( _bGetsOwnership ) + implTakeOwnership( ); + + // always veto: We want to take the ownership ourself, as this is the only chance to delete + // the temporary file which the model is based on + throw util::CloseVetoException( ); + } + + void SAL_CALL DelayedFileDeletion::notifyClosing( const EventObject& ) + { + OSL_FAIL("DelayedFileDeletion::notifyClosing: how this?" ); + // this should not happen: + // Either, a foreign instance closes the document, then we should veto this, and take the ownership + // Or, we ourself close the document, then we should not be a listener anymore + } + + void SAL_CALL DelayedFileDeletion::disposing( const EventObject& ) + { + OSL_FAIL("DelayedFileDeletion::disposing: how this?" ); + // this should not happen: + // Either, a foreign instance closes the document, then we should veto this, and take the ownership + // Or, we ourself close the document, then we should not be a listener anymore + } + + DelayedFileDeletion::~DelayedFileDeletion( ) + { + } +} + +static bool DeleteTmpFile_Impl( + Reference< frame::XModel > &rxModel, + SfxObjectShellRef &rxDocSh, + const OUString &rTmpFileURL ) +{ + bool bRes = false; + if (!rTmpFileURL.isEmpty()) + { + bool bDelete = true; + if ( eVetoed == CloseModelAndDocSh( rxModel, rxDocSh ) ) + { + // somebody vetoed the closing, and took the ownership of the document + // -> ensure that the temporary file is deleted later on + new DelayedFileDeletion( rxModel, rTmpFileURL ); + // note: as soon as #106931# is fixed, the whole DelayedFileDeletion is to be superseded by + // a better solution + bDelete = false; + } + + rxModel = nullptr; + rxDocSh = nullptr; // destroy doc shell + + if ( bDelete ) + { + if ( !SWUnoHelper::UCB_DeleteFile( rTmpFileURL ) ) + { + new DelayedFileDeletion( rxModel, rTmpFileURL ); + // same not as above: as soon as #106931#, ... + } + } + else + bRes = true; // file will be deleted delayed + } + return bRes; +} + +SwXMailMerge::SwXMailMerge() : + m_aEvtListeners ( GetMailMergeMutex() ), + m_aMergeListeners ( GetMailMergeMutex() ), + m_aPropListeners ( GetMailMergeMutex() ), + m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_MAILMERGE ) ), + m_nDataCommandType(sdb::CommandType::TABLE), + m_nOutputType(MailMergeType::PRINTER), + m_bEscapeProcessing(true), //!! allow to process properties like "Filter", "Order", ... + m_bSinglePrintJobs(false), + m_bFileNameFromColumn(false), + m_bSendAsHTML(false), + m_bSendAsAttachment(false), + m_bSaveAsSingleFile(false), + m_bDisposing(false), + m_pMgr(nullptr) +{ + // create empty document + // like in: SwModule::InsertEnv (appenv.cxx) + m_xDocSh = new SwDocShell( SfxObjectCreateMode::STANDARD ); + m_xDocSh->DoInitNew(); + SfxViewFrame *pFrame = SfxViewFrame::LoadHiddenDocument( *m_xDocSh, SFX_INTERFACE_NONE ); + SwView *pView = static_cast<SwView*>( pFrame->GetViewShell() ); + pView->AttrChangedNotify(nullptr); //So that SelectShell is called. + m_xModel = m_xDocSh->GetModel(); +} + +SwXMailMerge::~SwXMailMerge() +{ + if (!m_aTmpFileName.isEmpty()) + DeleteTmpFile_Impl( m_xModel, m_xDocSh, m_aTmpFileName ); + else // there was no temporary file in use + { + //! we still need to close the model and doc shell manually + //! because there is no automatism that will do that later. + //! #120086# + if ( eVetoed == CloseModelAndDocSh( m_xModel, m_xDocSh ) ) + OSL_FAIL("ownership transferred to vetoing object!" ); + + m_xModel = nullptr; + m_xDocSh = nullptr; // destroy doc shell + } +} + +// Guarantee object consistence in case of an exception +class MailMergeExecuteFinalizer +{ +public: + explicit MailMergeExecuteFinalizer(SwXMailMerge *mailmerge) + : m_pMailMerge(mailmerge) + { + assert(m_pMailMerge); //mailmerge object missing + } + ~MailMergeExecuteFinalizer() + { + osl::MutexGuard aMgrGuard( GetMailMergeMutex() ); + m_pMailMerge->m_pMgr = nullptr; + } + +private: + MailMergeExecuteFinalizer(MailMergeExecuteFinalizer const&) = delete; + MailMergeExecuteFinalizer& operator=(MailMergeExecuteFinalizer const&) = delete; + + SwXMailMerge *m_pMailMerge; +}; + +uno::Any SAL_CALL SwXMailMerge::execute( + const uno::Sequence< beans::NamedValue >& rArguments ) +{ + SolarMutexGuard aGuard; + MailMergeExecuteFinalizer aFinalizer(this); + + // get property values to be used + // (use values from the service as default and override them with + // the values that are provided as arguments) + + uno::Sequence< uno::Any > aCurSelection = m_aSelection; + uno::Reference< sdbc::XResultSet > xCurResultSet = m_xResultSet; + uno::Reference< sdbc::XConnection > xCurConnection = m_xConnection; + uno::Reference< frame::XModel > xCurModel = m_xModel; + OUString aCurDataSourceName = m_aDataSourceName; + OUString aCurDataCommand = m_aDataCommand; + OUString aCurFilter = m_aFilter; + OUString aCurDocumentURL = m_aDocumentURL; + OUString aCurOutputURL = m_aOutputURL; + OUString aCurFileNamePrefix = m_aFileNamePrefix; + sal_Int32 nCurDataCommandType = m_nDataCommandType; + sal_Int16 nCurOutputType = m_nOutputType; + bool bCurEscapeProcessing = m_bEscapeProcessing; + bool bCurSinglePrintJobs = m_bSinglePrintJobs; + bool bCurFileNameFromColumn = m_bFileNameFromColumn; + + SfxObjectShellRef xCurDocSh = m_xDocSh; // the document + + for (const beans::NamedValue& rArgument : rArguments) + { + const OUString &rName = rArgument.Name; + const Any &rValue = rArgument.Value; + + bool bOK = true; + if (rName == UNO_NAME_SELECTION) + bOK = rValue >>= aCurSelection; + else if (rName == UNO_NAME_RESULT_SET) + bOK = rValue >>= xCurResultSet; + else if (rName == UNO_NAME_CONNECTION) + bOK = rValue >>= xCurConnection; + else if (rName == UNO_NAME_MODEL) + throw PropertyVetoException("Property is read-only: " + rName, static_cast < cppu::OWeakObject * > ( this ) ); + else if (rName == UNO_NAME_DATA_SOURCE_NAME) + bOK = rValue >>= aCurDataSourceName; + else if (rName == UNO_NAME_DAD_COMMAND) + bOK = rValue >>= aCurDataCommand; + else if (rName == UNO_NAME_FILTER) + bOK = rValue >>= aCurFilter; + else if (rName == UNO_NAME_DOCUMENT_URL) + { + bOK = rValue >>= aCurDocumentURL; + if (!aCurDocumentURL.isEmpty() + && !LoadFromURL_impl( xCurModel, xCurDocSh, aCurDocumentURL, false )) + throw RuntimeException("Failed to create document from URL: " + aCurDocumentURL, static_cast < cppu::OWeakObject * > ( this ) ); + } + else if (rName == UNO_NAME_OUTPUT_URL) + { + bOK = rValue >>= aCurOutputURL; + if (!aCurOutputURL.isEmpty()) + { + if (!UCB_IsDirectory(aCurOutputURL)) + throw IllegalArgumentException("URL does not point to a directory: " + aCurOutputURL, static_cast < cppu::OWeakObject * > ( this ), 0 ); + if (UCB_IsReadOnlyFileName(aCurOutputURL)) + throw IllegalArgumentException("URL is read-only: " + aCurOutputURL, static_cast < cppu::OWeakObject * > ( this ), 0 ); + } + } + else if (rName == UNO_NAME_FILE_NAME_PREFIX) + bOK = rValue >>= aCurFileNamePrefix; + else if (rName == UNO_NAME_DAD_COMMAND_TYPE) + bOK = rValue >>= nCurDataCommandType; + else if (rName == UNO_NAME_OUTPUT_TYPE) + bOK = rValue >>= nCurOutputType; + else if (rName == UNO_NAME_ESCAPE_PROCESSING) + bOK = rValue >>= bCurEscapeProcessing; + else if (rName == UNO_NAME_SINGLE_PRINT_JOBS) + bOK = rValue >>= bCurSinglePrintJobs; + else if (rName == UNO_NAME_FILE_NAME_FROM_COLUMN) + bOK = rValue >>= bCurFileNameFromColumn; + else if (rName == UNO_NAME_SUBJECT) + bOK = rValue >>= m_sSubject; + else if (rName == UNO_NAME_ADDRESS_FROM_COLUMN) + bOK = rValue >>= m_sAddressFromColumn; + else if (rName == UNO_NAME_SEND_AS_HTML) + bOK = rValue >>= m_bSendAsHTML; + else if (rName == UNO_NAME_MAIL_BODY) + bOK = rValue >>= m_sMailBody; + else if (rName == UNO_NAME_ATTACHMENT_NAME) + bOK = rValue >>= m_sAttachmentName; + else if (rName == UNO_NAME_ATTACHMENT_FILTER) + bOK = rValue >>= m_sAttachmentFilter; + else if (rName == UNO_NAME_COPIES_TO) + bOK = rValue >>= m_aCopiesTo; + else if (rName == UNO_NAME_BLIND_COPIES_TO) + bOK = rValue >>= m_aBlindCopiesTo; + else if (rName == UNO_NAME_SEND_AS_ATTACHMENT) + bOK = rValue >>= m_bSendAsAttachment; + else if (rName == UNO_NAME_PRINT_OPTIONS) + bOK = rValue >>= m_aPrintSettings; + else if (rName == UNO_NAME_SAVE_AS_SINGLE_FILE) + bOK = rValue >>= m_bSaveAsSingleFile; + else if (rName == UNO_NAME_SAVE_FILTER) + bOK = rValue >>= m_sSaveFilter; + else if (rName == UNO_NAME_SAVE_FILTER_OPTIONS) + bOK = rValue >>= m_sSaveFilterOptions; + else if (rName == UNO_NAME_SAVE_FILTER_DATA) + bOK = rValue >>= m_aSaveFilterData; + else if (rName == UNO_NAME_IN_SERVER_PASSWORD) + bOK = rValue >>= m_sInServerPassword; + else if (rName == UNO_NAME_OUT_SERVER_PASSWORD) + bOK = rValue >>= m_sOutServerPassword; + else + throw UnknownPropertyException( "Property is unknown: " + rName, static_cast < cppu::OWeakObject * > ( this ) ); + + if (!bOK) + throw IllegalArgumentException("Property type mismatch or property not set: " + rName, static_cast < cppu::OWeakObject * > ( this ), 0 ); + } + + // need to translate the selection: the API here requires a sequence of bookmarks, but the Merge + // method we will call below requires a sequence of indices. + if ( aCurSelection.hasElements() ) + { + Sequence< Any > aTranslated( aCurSelection.getLength() ); + + bool bValid = false; + Reference< sdbcx::XRowLocate > xRowLocate( xCurResultSet, UNO_QUERY ); + if ( xRowLocate.is() ) + { + Any* pTranslated = aTranslated.getArray(); + + try + { + bool bEverythingsFine = true; + for ( const Any& rBookmark : std::as_const(aCurSelection) ) + { + bEverythingsFine = xRowLocate->moveToBookmark( rBookmark ); + if ( !bEverythingsFine ) + break; + *pTranslated <<= xCurResultSet->getRow(); + ++pTranslated; + } + if ( bEverythingsFine ) + bValid = true; + } + catch (const Exception&) + { + bValid = false; + } + } + + if ( !bValid ) + { + throw IllegalArgumentException( + "The current 'Selection' does not describe a valid array of bookmarks, relative to the current 'ResultSet'.", + static_cast < cppu::OWeakObject * > ( this ), + 0 + ); + } + + aCurSelection = aTranslated; + } + + SfxViewFrame* pFrame = SfxViewFrame::GetFirst( xCurDocSh.get(), false); + SwView *pView = pFrame ? dynamic_cast<SwView*>( pFrame->GetViewShell() ) : nullptr; + if (!pView) + throw RuntimeException(); + SwWrtShell &rSh = *pView->GetWrtShellPtr(); + + // avoid assertion in 'Update' from Sfx by supplying a shell + // and thus avoiding the SelectShell call in Writers GetState function + // while still in Update of Sfx. + // (GetSelection in Update is not allowed) + if (!aCurDocumentURL.isEmpty()) + pView->AttrChangedNotify(nullptr);//So that SelectShell is called. + + SharedComponent aRowSetDisposeHelper; + if (!xCurResultSet.is()) + { + if (aCurDataSourceName.isEmpty() || aCurDataCommand.isEmpty() ) + { + OSL_FAIL("PropertyValues missing or unset"); + throw IllegalArgumentException("Either the ResultSet or DataSourceName and DataCommand must be set.", static_cast < cppu::OWeakObject * > ( this ), 0 ); + } + + // build ResultSet from DataSourceName, DataCommand and DataCommandType + + Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() ); + if (xMgr.is()) + { + Reference< XInterface > xInstance = xMgr->createInstance( "com.sun.star.sdb.RowSet" ); + aRowSetDisposeHelper.reset( xInstance, SharedComponent::TakeOwnership ); + Reference< XPropertySet > xRowSetPropSet( xInstance, UNO_QUERY ); + OSL_ENSURE( xRowSetPropSet.is(), "failed to get XPropertySet interface from RowSet" ); + if (xRowSetPropSet.is()) + { + if (xCurConnection.is()) + xRowSetPropSet->setPropertyValue( "ActiveConnection", makeAny( xCurConnection ) ); + xRowSetPropSet->setPropertyValue( "DataSourceName", makeAny( aCurDataSourceName ) ); + xRowSetPropSet->setPropertyValue( "Command", makeAny( aCurDataCommand ) ); + xRowSetPropSet->setPropertyValue( "CommandType", makeAny( nCurDataCommandType ) ); + xRowSetPropSet->setPropertyValue( "EscapeProcessing", makeAny( bCurEscapeProcessing ) ); + xRowSetPropSet->setPropertyValue( "ApplyFilter", makeAny( true ) ); + xRowSetPropSet->setPropertyValue( "Filter", makeAny( aCurFilter ) ); + + Reference< sdbc::XRowSet > xRowSet( xInstance, UNO_QUERY ); + if (xRowSet.is()) + xRowSet->execute(); // build ResultSet from properties + if( !xCurConnection.is() ) + xCurConnection.set( xRowSetPropSet->getPropertyValue( "ActiveConnection" ), UNO_QUERY ); + xCurResultSet = xRowSet; + OSL_ENSURE( xCurResultSet.is(), "failed to build ResultSet" ); + } + } + } + + svx::ODataAccessDescriptor aDescriptor; + aDescriptor.setDataSource(aCurDataSourceName); + aDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= xCurConnection; + aDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= aCurDataCommand; + aDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= nCurDataCommandType; + aDescriptor[ svx::DataAccessDescriptorProperty::EscapeProcessing ] <<= bCurEscapeProcessing; + aDescriptor[ svx::DataAccessDescriptorProperty::Cursor ] <<= xCurResultSet; + // aDescriptor[ svx::DataAccessDescriptorProperty::ColumnName ] not used + // aDescriptor[ svx::DataAccessDescriptorProperty::ColumnObject ] not used + aDescriptor[ svx::DataAccessDescriptorProperty::Selection ] <<= aCurSelection; + + DBManagerOptions nMergeType; + switch (nCurOutputType) + { + case MailMergeType::PRINTER : nMergeType = DBMGR_MERGE_PRINTER; break; + case MailMergeType::FILE : nMergeType = DBMGR_MERGE_FILE; break; + case MailMergeType::MAIL : nMergeType = DBMGR_MERGE_EMAIL; break; + case MailMergeType::SHELL : nMergeType = DBMGR_MERGE_SHELL; break; + default: + throw IllegalArgumentException("Invalid value of property: OutputType", static_cast < cppu::OWeakObject * > ( this ), 0 ); + } + + SwDBManager* pMgr = rSh.GetDBManager(); + //force layout creation + rSh.CalcLayout(); + OSL_ENSURE( pMgr, "database manager missing" ); + m_pMgr = pMgr; + + SwMergeDescriptor aMergeDesc( nMergeType, rSh, aDescriptor ); + + std::unique_ptr< SwMailMergeConfigItem > pMMConfigItem; + uno::Reference< mail::XMailService > xInService; + switch (nCurOutputType) + { + case MailMergeType::PRINTER: + { + IDocumentDeviceAccess& rIDDA = rSh.getIDocumentDeviceAccess(); + SwPrintData aPrtData( rIDDA.getPrintData() ); + aPrtData.SetPrintSingleJobs( bCurSinglePrintJobs ); + rIDDA.setPrintData( aPrtData ); + // #i25686# printing should not be done asynchronously to prevent dangling offices + // when mail merge is called as command line macro + aMergeDesc.aPrintOptions = m_aPrintSettings; + aMergeDesc.bCreateSingleFile = true; + } + break; + case MailMergeType::SHELL: + aMergeDesc.bCreateSingleFile = true; + pMMConfigItem.reset(new SwMailMergeConfigItem); + aMergeDesc.pMailMergeConfigItem = pMMConfigItem.get(); + break; + case MailMergeType::FILE: + { + INetURLObject aURLObj; + aURLObj.SetSmartProtocol( INetProtocol::File ); + + if (!aCurDocumentURL.isEmpty()) + { + // if OutputURL or FileNamePrefix are missing get + // them from DocumentURL + aURLObj.SetSmartURL( aCurDocumentURL ); + if (aCurFileNamePrefix.isEmpty()) + aCurFileNamePrefix = aURLObj.GetBase(); // filename without extension + if (aCurOutputURL.isEmpty()) + { + aURLObj.removeSegment(); + aCurOutputURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + } + } + else // default empty document without URL + { + if (aCurOutputURL.isEmpty()) + throw RuntimeException("OutputURL is not set and can not be obtained.", static_cast < cppu::OWeakObject * > ( this ) ); + } + + aURLObj.SetSmartURL( aCurOutputURL ); + OUString aPath = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + + const OUString aDelim( "/" ); + if (!aPath.isEmpty() && !aPath.endsWith(aDelim)) + aPath += aDelim; + if (bCurFileNameFromColumn) + aMergeDesc.sDBcolumn = aCurFileNamePrefix; + else + { + aPath += aCurFileNamePrefix; + } + + aMergeDesc.sPrefix = aPath; + aMergeDesc.sSaveToFilter = m_sSaveFilter; + aMergeDesc.sSaveToFilterOptions = m_sSaveFilterOptions; + aMergeDesc.aSaveToFilterData = m_aSaveFilterData; + aMergeDesc.bCreateSingleFile = m_bSaveAsSingleFile; + } + break; + case MailMergeType::MAIL: + { + aMergeDesc.sDBcolumn = m_sAddressFromColumn; + if(m_sAddressFromColumn.isEmpty()) + throw RuntimeException("Mail address column not set.", static_cast < cppu::OWeakObject * > ( this ) ); + aMergeDesc.sSaveToFilter = m_sAttachmentFilter; + aMergeDesc.sSubject = m_sSubject; + aMergeDesc.sMailBody = m_sMailBody; + aMergeDesc.sAttachmentName = m_sAttachmentName; + aMergeDesc.aCopiesTo = m_aCopiesTo; + aMergeDesc.aBlindCopiesTo = m_aBlindCopiesTo; + aMergeDesc.bSendAsHTML = m_bSendAsHTML; + aMergeDesc.bSendAsAttachment = m_bSendAsAttachment; + + aMergeDesc.bCreateSingleFile = false; + pMMConfigItem.reset(new SwMailMergeConfigItem); + aMergeDesc.pMailMergeConfigItem = pMMConfigItem.get(); + aMergeDesc.xSmtpServer = SwMailMergeHelper::ConnectToSmtpServer( + *pMMConfigItem, + xInService, + m_sInServerPassword, m_sOutServerPassword ); + if( !aMergeDesc.xSmtpServer.is() || !aMergeDesc.xSmtpServer->isConnected()) + throw RuntimeException("Failed to connect to mail server.", static_cast < cppu::OWeakObject * > ( this ) ); + } + break; + } + + // save document with temporary filename + std::shared_ptr<const SfxFilter> pSfxFlt = SwIoSystem::GetFilterOfFormat( + FILTER_XML, + SwDocShell::Factory().GetFilterContainer() ); + OUString aExtension(comphelper::string::stripStart(pSfxFlt->GetDefaultExtension(), '*')); + utl::TempFile aTempFile( "SwMM", true, &aExtension ); + m_aTmpFileName = aTempFile.GetURL(); + + Reference< XStorable > xStorable( xCurModel, UNO_QUERY ); + bool bStoredAsTemporary = false; + if ( xStorable.is() ) + { + try + { + xStorable->storeAsURL( m_aTmpFileName, Sequence< PropertyValue >() ); + bStoredAsTemporary = true; + } + catch (const Exception&) + { + } + } + if ( !bStoredAsTemporary ) + throw RuntimeException("Failed to save temporary file.", static_cast < cppu::OWeakObject * > ( this ) ); + + pMgr->SetMergeSilent( true ); // suppress dialogs, message boxes, etc. + const SwXMailMerge *pOldSrc = pMgr->GetMailMergeEvtSrc(); + OSL_ENSURE( !pOldSrc || pOldSrc == this, "Ooops... different event source already set." ); + pMgr->SetMailMergeEvtSrc( this ); // launch events for listeners + + SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMerge, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), xCurDocSh.get())); + bool bSucc = pMgr->Merge( aMergeDesc ); + SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMergeEnd, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), xCurDocSh.get())); + + pMgr->SetMailMergeEvtSrc( pOldSrc ); + + if ( xCurModel.get() != m_xModel.get() ) + { // in case it was a temporary model -> close it, and delete the file + DeleteTmpFile_Impl( xCurModel, xCurDocSh, m_aTmpFileName ); + m_aTmpFileName.clear(); + } + // (in case it wasn't a temporary model, it will be closed in the dtor, at the latest) + + if (!bSucc) + throw Exception("Mail merge failed. Sorry, no further information available.", static_cast < cppu::OWeakObject * > ( this ) ); + + //de-initialize services + if(xInService.is() && xInService->isConnected()) + xInService->disconnect(); + if(aMergeDesc.xSmtpServer.is() && aMergeDesc.xSmtpServer->isConnected()) + aMergeDesc.xSmtpServer->disconnect(); + + if (DBMGR_MERGE_SHELL == nMergeType) + { + return makeAny( aMergeDesc.pMailMergeConfigItem->GetTargetView()->GetDocShell()->GetBaseModel() ); + } + else + return makeAny( true ); +} + +void SAL_CALL SwXMailMerge::cancel() +{ + // Cancel may be called from a second thread, so this protects from m_pMgr + /// cleanup in the execute function. + osl::MutexGuard aMgrGuard( GetMailMergeMutex() ); + if (m_pMgr) + m_pMgr->MergeCancel(); +} + +void SwXMailMerge::LaunchMailMergeEvent( const MailMergeEvent &rEvt ) const +{ + comphelper::OInterfaceIteratorHelper2 aIt( const_cast<SwXMailMerge *>(this)->m_aMergeListeners ); + while (aIt.hasMoreElements()) + { + Reference< XMailMergeListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->notifyMailMergeEvent( rEvt ); + } +} + +void SwXMailMerge::launchEvent( const PropertyChangeEvent &rEvt ) const +{ + cppu::OInterfaceContainerHelper *pContainer = + m_aPropListeners.getContainer( rEvt.PropertyHandle ); + if (pContainer) + { + cppu::OInterfaceIteratorHelper aIt( *pContainer ); + while (aIt.hasMoreElements()) + { + Reference< XPropertyChangeListener > xRef( aIt.next(), UNO_QUERY ); + if (xRef.is()) + xRef->propertyChange( rEvt ); + } + } +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXMailMerge::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + static Reference< XPropertySetInfo > aRef = m_pPropSet->getPropertySetInfo(); + return aRef; +} + +void SAL_CALL SwXMailMerge::setPropertyValue( + const OUString& rPropertyName, const uno::Any& rValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertySimpleEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pCur) + throw UnknownPropertyException(rPropertyName); + else if (pCur->nFlags & PropertyAttribute::READONLY) + throw PropertyVetoException(); + else + { + void *pData = nullptr; + switch (pCur->nWID) + { + case WID_SELECTION : pData = &m_aSelection; break; + case WID_RESULT_SET : pData = &m_xResultSet; break; + case WID_CONNECTION : pData = &m_xConnection; break; + case WID_MODEL : pData = &m_xModel; break; + case WID_DATA_SOURCE_NAME : pData = &m_aDataSourceName; break; + case WID_DATA_COMMAND : pData = &m_aDataCommand; break; + case WID_FILTER : pData = &m_aFilter; break; + case WID_DOCUMENT_URL : pData = &m_aDocumentURL; break; + case WID_OUTPUT_URL : pData = &m_aOutputURL; break; + case WID_DATA_COMMAND_TYPE : pData = &m_nDataCommandType; break; + case WID_OUTPUT_TYPE : pData = &m_nOutputType; break; + case WID_ESCAPE_PROCESSING : pData = &m_bEscapeProcessing; break; + case WID_SINGLE_PRINT_JOBS : pData = &m_bSinglePrintJobs; break; + case WID_FILE_NAME_FROM_COLUMN : pData = &m_bFileNameFromColumn; break; + case WID_FILE_NAME_PREFIX : pData = &m_aFileNamePrefix; break; + case WID_MAIL_SUBJECT: pData = &m_sSubject; break; + case WID_ADDRESS_FROM_COLUMN: pData = &m_sAddressFromColumn; break; + case WID_SEND_AS_HTML: pData = &m_bSendAsHTML; break; + case WID_SEND_AS_ATTACHMENT: pData = &m_bSendAsAttachment; break; + case WID_MAIL_BODY: pData = &m_sMailBody; break; + case WID_ATTACHMENT_NAME: pData = &m_sAttachmentName; break; + case WID_ATTACHMENT_FILTER: pData = &m_sAttachmentFilter;break; + case WID_PRINT_OPTIONS: pData = &m_aPrintSettings; break; + case WID_SAVE_AS_SINGLE_FILE: pData = &m_bSaveAsSingleFile; break; + case WID_SAVE_FILTER: pData = &m_sSaveFilter; break; + case WID_SAVE_FILTER_OPTIONS: pData = &m_sSaveFilterOptions; break; + case WID_SAVE_FILTER_DATA: pData = &m_aSaveFilterData; break; + case WID_COPIES_TO: pData = &m_aCopiesTo; break; + case WID_BLIND_COPIES_TO: pData = &m_aBlindCopiesTo;break; + case WID_IN_SERVER_PASSWORD: pData = &m_sInServerPassword; break; + case WID_OUT_SERVER_PASSWORD: pData = &m_sOutServerPassword; break; + default : + OSL_FAIL("unknown WID"); + } + Any aOld( pData, pCur->aType ); + + bool bChanged = false; + bool bOK = true; + if (aOld != rValue) + { + if (pData == &m_aSelection) + bOK = rValue >>= m_aSelection; + else if (pData == &m_xResultSet) + bOK = rValue >>= m_xResultSet; + else if (pData == &m_xConnection) + bOK = rValue >>= m_xConnection; + else if (pData == &m_xModel) + bOK = rValue >>= m_xModel; + else if (pData == &m_aDataSourceName) + bOK = rValue >>= m_aDataSourceName; + else if (pData == &m_aDataCommand) + bOK = rValue >>= m_aDataCommand; + else if (pData == &m_aFilter) + bOK = rValue >>= m_aFilter; + else if (pData == &m_aDocumentURL) + { + OUString aText; + bOK = rValue >>= aText; + if (!aText.isEmpty() + && !LoadFromURL_impl( m_xModel, m_xDocSh, aText, true )) + throw RuntimeException("Failed to create document from URL: " + aText, static_cast < cppu::OWeakObject * > ( this ) ); + m_aDocumentURL = aText; + } + else if (pData == &m_aOutputURL) + { + OUString aText; + bOK = rValue >>= aText; + if (!aText.isEmpty()) + { + if (!UCB_IsDirectory(aText)) + throw IllegalArgumentException("URL does not point to a directory: " + aText, static_cast < cppu::OWeakObject * > ( this ), 0 ); + if (UCB_IsReadOnlyFileName(aText)) + throw IllegalArgumentException("URL is read-only: " + aText, static_cast < cppu::OWeakObject * > ( this ), 0 ); + } + m_aOutputURL = aText; + } + else if (pData == &m_nDataCommandType) + bOK = rValue >>= m_nDataCommandType; + else if (pData == &m_nOutputType) + bOK = rValue >>= m_nOutputType; + else if (pData == &m_bEscapeProcessing) + bOK = rValue >>= m_bEscapeProcessing; + else if (pData == &m_bSinglePrintJobs) + bOK = rValue >>= m_bSinglePrintJobs; + else if (pData == &m_bFileNameFromColumn) + bOK = rValue >>= m_bFileNameFromColumn; + else if (pData == &m_aFileNamePrefix) + bOK = rValue >>= m_aFileNamePrefix; + else if (pData == &m_sSubject) + bOK = rValue >>= m_sSubject; + else if (pData == &m_sAddressFromColumn) + bOK = rValue >>= m_sAddressFromColumn; + else if (pData == &m_bSendAsHTML) + bOK = rValue >>= m_bSendAsHTML; + else if (pData == &m_bSendAsAttachment) + bOK = rValue >>= m_bSendAsAttachment; + else if (pData == &m_sMailBody) + bOK = rValue >>= m_sMailBody; + else if (pData == &m_sAttachmentName) + bOK = rValue >>= m_sAttachmentName; + else if (pData == &m_sAttachmentFilter) + bOK = rValue >>= m_sAttachmentFilter; + else if (pData == &m_aPrintSettings) + bOK = rValue >>= m_aPrintSettings; + else if (pData == &m_bSaveAsSingleFile) + bOK = rValue >>= m_bSaveAsSingleFile; + else if (pData == &m_sSaveFilter) + bOK = rValue >>= m_sSaveFilter; + else if (pData == &m_sSaveFilterOptions) + bOK = rValue >>= m_sSaveFilterOptions; + else if (pData == &m_aSaveFilterData) + bOK = rValue >>= m_aSaveFilterData; + else if (pData == &m_aCopiesTo) + bOK = rValue >>= m_aCopiesTo; + else if (pData == &m_aBlindCopiesTo) + bOK = rValue >>= m_aBlindCopiesTo; + else if(pData == &m_sInServerPassword) + bOK = rValue >>= m_sInServerPassword; + else if(pData == &m_sOutServerPassword) + bOK = rValue >>= m_sOutServerPassword; + else { + OSL_FAIL("invalid pointer" ); + } + OSL_ENSURE( bOK, "set value failed" ); + bChanged = true; + } + if (!bOK) + throw IllegalArgumentException("Property type mismatch or property not set: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ), 0 ); + + if (bChanged) + { + PropertyChangeEvent aChgEvt( static_cast<XPropertySet *>(this), rPropertyName, + false, pCur->nWID, aOld, rValue ); + launchEvent( aChgEvt ); + } + } +} + +uno::Any SAL_CALL SwXMailMerge::getPropertyValue( + const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + + Any aRet; + + const SfxItemPropertySimpleEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pCur) + throw UnknownPropertyException(rPropertyName); + + switch (pCur->nWID) + { + case WID_SELECTION : aRet <<= m_aSelection; break; + case WID_RESULT_SET : aRet <<= m_xResultSet; break; + case WID_CONNECTION : aRet <<= m_xConnection; break; + case WID_MODEL : aRet <<= m_xModel; break; + case WID_DATA_SOURCE_NAME : aRet <<= m_aDataSourceName; break; + case WID_DATA_COMMAND : aRet <<= m_aDataCommand; break; + case WID_FILTER : aRet <<= m_aFilter; break; + case WID_DOCUMENT_URL : aRet <<= m_aDocumentURL; break; + case WID_OUTPUT_URL : aRet <<= m_aOutputURL; break; + case WID_DATA_COMMAND_TYPE : aRet <<= m_nDataCommandType; break; + case WID_OUTPUT_TYPE : aRet <<= m_nOutputType; break; + case WID_ESCAPE_PROCESSING : aRet <<= m_bEscapeProcessing; break; + case WID_SINGLE_PRINT_JOBS : aRet <<= m_bSinglePrintJobs; break; + case WID_FILE_NAME_FROM_COLUMN : aRet <<= m_bFileNameFromColumn; break; + case WID_FILE_NAME_PREFIX : aRet <<= m_aFileNamePrefix; break; + case WID_MAIL_SUBJECT: aRet <<= m_sSubject; break; + case WID_ADDRESS_FROM_COLUMN: aRet <<= m_sAddressFromColumn; break; + case WID_SEND_AS_HTML: aRet <<= m_bSendAsHTML; break; + case WID_SEND_AS_ATTACHMENT: aRet <<= m_bSendAsAttachment; break; + case WID_MAIL_BODY: aRet <<= m_sMailBody; break; + case WID_ATTACHMENT_NAME: aRet <<= m_sAttachmentName; break; + case WID_ATTACHMENT_FILTER: aRet <<= m_sAttachmentFilter;break; + case WID_PRINT_OPTIONS: aRet <<= m_aPrintSettings; break; + case WID_SAVE_AS_SINGLE_FILE: aRet <<= m_bSaveAsSingleFile; break; + case WID_SAVE_FILTER: aRet <<= m_sSaveFilter; break; + case WID_SAVE_FILTER_OPTIONS: aRet <<= m_sSaveFilterOptions; break; + case WID_SAVE_FILTER_DATA: aRet <<= m_aSaveFilterData; break; + case WID_COPIES_TO: aRet <<= m_aCopiesTo; break; + case WID_BLIND_COPIES_TO: aRet <<= m_aBlindCopiesTo;break; + case WID_IN_SERVER_PASSWORD: aRet <<= m_sInServerPassword; break; + case WID_OUT_SERVER_PASSWORD: aRet <<= m_sOutServerPassword; break; + default : + OSL_FAIL("unknown WID"); + } + + return aRet; +} + +void SAL_CALL SwXMailMerge::addPropertyChangeListener( + const OUString& rPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& rListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rListener.is()) + { + const SfxItemPropertySimpleEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pCur) + throw UnknownPropertyException(rPropertyName); + m_aPropListeners.addInterface( pCur->nWID, rListener ); + } +} + +void SAL_CALL SwXMailMerge::removePropertyChangeListener( + const OUString& rPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& rListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rListener.is()) + { + const SfxItemPropertySimpleEntry* pCur = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pCur) + throw UnknownPropertyException(rPropertyName); + m_aPropListeners.removeInterface( pCur->nWID, rListener ); + } +} + +void SAL_CALL SwXMailMerge::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ ) +{ + // no vetoable property, thus no support for vetoable change listeners + OSL_FAIL("not implemented"); +} + +void SAL_CALL SwXMailMerge::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*rListener*/ ) +{ + // no vetoable property, thus no support for vetoable change listeners + OSL_FAIL("not implemented"); +} + +void SAL_CALL SwXMailMerge::dispose() +{ + SolarMutexGuard aGuard; + + if (!m_bDisposing) + { + m_bDisposing = true; + + EventObject aEvtObj( static_cast<XPropertySet *>(this) ); + m_aEvtListeners.disposeAndClear( aEvtObj ); + m_aMergeListeners.disposeAndClear( aEvtObj ); + m_aPropListeners.disposeAndClear( aEvtObj ); + } +} + +void SAL_CALL SwXMailMerge::addEventListener( + const Reference< XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rxListener.is()) + m_aEvtListeners.addInterface( rxListener ); +} + +void SAL_CALL SwXMailMerge::removeEventListener( + const Reference< XEventListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rxListener.is()) + m_aEvtListeners.removeInterface( rxListener ); +} + +void SAL_CALL SwXMailMerge::addMailMergeEventListener( + const uno::Reference< XMailMergeListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rxListener.is()) + m_aMergeListeners.addInterface( rxListener ); +} + +void SAL_CALL SwXMailMerge::removeMailMergeEventListener( + const uno::Reference< XMailMergeListener >& rxListener ) +{ + SolarMutexGuard aGuard; + if (!m_bDisposing && rxListener.is()) + m_aMergeListeners.removeInterface( rxListener ); +} + +OUString SAL_CALL SwXMailMerge::getImplementationName() +{ + return "SwXMailMerge"; +} + +sal_Bool SAL_CALL SwXMailMerge::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SAL_CALL SwXMailMerge::getSupportedServiceNames() +{ + return { "com.sun.star.text.MailMerge", "com.sun.star.sdb.DataAccessDescriptor" }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unomod.cxx b/sw/source/uibase/uno/unomod.cxx new file mode 100644 index 000000000..48d84b512 --- /dev/null +++ b/sw/source/uibase/uno/unomod.cxx @@ -0,0 +1,970 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <com/sun/star/view/DocumentZoomType.hpp> +#include <comphelper/ChainablePropertySetInfo.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <svl/itemprop.hxx> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> + +#include <unomod.hxx> +#include <usrpref.hxx> +#include <prtopt.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <viewopt.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <edtwin.hxx> +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::comphelper; + +namespace { + +enum SwViewSettingsPropertyHandles +{ + HANDLE_VIEWSET_ANNOTATIONS, + HANDLE_VIEWSET_BREAKS, + HANDLE_VIEWSET_DRAWINGS, + HANDLE_VIEWSET_FIELD_COMMANDS, + HANDLE_VIEWSET_FOOTNOTE_BACKGROUND, + HANDLE_VIEWSET_GRAPHICS, + HANDLE_VIEWSET_HIDDEN_CHARACTERS, + HANDLE_VIEWSET_HIDDEN_PARAGRAPHS, + HANDLE_VIEWSET_HIDDEN_TEXT, + HANDLE_VIEWSET_HRULER, + HANDLE_VIEWSET_HSCROLL, + HANDLE_VIEWSET_INDEX_MARK_BACKGROUND, + HANDLE_VIEWSET_NONPRINTING_CHARACTERS, + HANDLE_VIEWSET_ONLINE_LAYOUT, + HANDLE_VIEWSET_PARA_BREAKS, + HANDLE_VIEWSET_PROTECTED_SPACES, + HANDLE_VIEWSET_SOFT_HYPHENS, + HANDLE_VIEWSET_SPACES, + HANDLE_VIEWSET_TABLE_BOUNDARIES, + HANDLE_VIEWSET_TABLES, + HANDLE_VIEWSET_TABSTOPS, + HANDLE_VIEWSET_TEXT_BOUNDARIES, + HANDLE_VIEWSET_TEXT_FIELD_BACKGROUND, + HANDLE_VIEWSET_VRULER, + HANDLE_VIEWSET_VSCROLL, + HANDLE_VIEWSET_SMOOTH_SCROLLING, + HANDLE_VIEWSET_ZOOM_TYPE, + HANDLE_VIEWSET_ZOOM, + HANDLE_VIEWSET_SHOW_CONTENT_TIPS, + HANDLE_VIEWSET_HELP_URL, + HANDLE_VIEWSET_VRULER_RIGHT, + HANDLE_VIEWSET_SHOW_RULER, + HANDLE_VIEWSET_IS_RASTER_VISIBLE, + HANDLE_VIEWSET_IS_SNAP_TO_RASTER, + HANDLE_VIEWSET_RASTER_RESOLUTION_X, + HANDLE_VIEWSET_RASTER_RESOLUTION_Y, + HANDLE_VIEWSET_RASTER_SUBDIVISION_X, + HANDLE_VIEWSET_RASTER_SUBDIVISION_Y, + HANDLE_VIEWSET_HORI_RULER_METRIC, + HANDLE_VIEWSET_VERT_RULER_METRIC, + HANDLE_VIEWSET_SCROLLBAR_TIPS, + HANDLE_VIEWSET_INLINECHANGES_TIPS, + HANDLE_VIEWSET_HIDE_WHITESPACE, + HANDLE_VIEWSET_USE_HEADERFOOTERMENU, + HANDLE_VIEWSET_BOOKMARKS, +}; + +enum SwPrintSettingsPropertyHandles +{ + HANDLE_PRINTSET_ANNOTATION_MODE, + HANDLE_PRINTSET_BLACK_FONTS, + HANDLE_PRINTSET_CONTROLS, + HANDLE_PRINTSET_DRAWINGS, + HANDLE_PRINTSET_GRAPHICS, + HANDLE_PRINTSET_LEFT_PAGES, + HANDLE_PRINTSET_PAGE_BACKGROUND, + HANDLE_PRINTSET_PROSPECT, + HANDLE_PRINTSET_REVERSED, + HANDLE_PRINTSET_RIGHT_PAGES, + HANDLE_PRINTSET_FAX_NAME, + HANDLE_PRINTSET_PAPER_FROM_SETUP, + HANDLE_PRINTSET_TABLES, + HANDLE_PRINTSET_SINGLE_JOBS, + HANDLE_PRINTSET_EMPTY_PAGES, + HANDLE_PRINTSET_PROSPECT_RTL, + HANDLE_PRINTSET_PLACEHOLDER, + HANDLE_PRINTSET_HIDDEN_TEXT +}; + +} + +static ChainablePropertySetInfo * lcl_createViewSettingsInfo() +{ + static PropertyInfo const aViewSettingsMap_Impl[] = + { + { OUString( "HelpURL" ), HANDLE_VIEWSET_HELP_URL , cppu::UnoType<OUString>::get(), PROPERTY_NONE}, + { OUString( "HorizontalRulerMetric"),HANDLE_VIEWSET_HORI_RULER_METRIC , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "IsRasterVisible"), HANDLE_VIEWSET_IS_RASTER_VISIBLE, cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "IsSnapToRaster"), HANDLE_VIEWSET_IS_SNAP_TO_RASTER, cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "IsVertRulerRightAligned"),HANDLE_VIEWSET_VRULER_RIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowContentTips" ), HANDLE_VIEWSET_SHOW_CONTENT_TIPS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowInlineTooltips" ), HANDLE_VIEWSET_INLINECHANGES_TIPS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "UseHeaderFooterMenu" ), HANDLE_VIEWSET_USE_HEADERFOOTERMENU , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "RasterResolutionX"), HANDLE_VIEWSET_RASTER_RESOLUTION_X, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "RasterResolutionY"), HANDLE_VIEWSET_RASTER_RESOLUTION_Y, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "RasterSubdivisionX"), HANDLE_VIEWSET_RASTER_SUBDIVISION_X, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "RasterSubdivisionY"), HANDLE_VIEWSET_RASTER_SUBDIVISION_Y, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "ShowAnnotations" ), HANDLE_VIEWSET_ANNOTATIONS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowBookmarks" ), HANDLE_VIEWSET_BOOKMARKS, cppu::UnoType<bool>::get(), PROPERTY_NONE }, + { OUString( "ShowBreaks"), HANDLE_VIEWSET_BREAKS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowDrawings"), HANDLE_VIEWSET_DRAWINGS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowFieldCommands"), HANDLE_VIEWSET_FIELD_COMMANDS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowFootnoteBackground"),HANDLE_VIEWSET_FOOTNOTE_BACKGROUND , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowGraphics"), HANDLE_VIEWSET_GRAPHICS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowHiddenCharacters"), HANDLE_VIEWSET_HIDDEN_CHARACTERS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "HideWhitespace"), HANDLE_VIEWSET_HIDE_WHITESPACE , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowHiddenParagraphs"), HANDLE_VIEWSET_HIDDEN_PARAGRAPHS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowHiddenText"), HANDLE_VIEWSET_HIDDEN_TEXT , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowHoriRuler"), HANDLE_VIEWSET_HRULER , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowHoriScrollBar"), HANDLE_VIEWSET_HSCROLL , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowIndexMarkBackground"),HANDLE_VIEWSET_INDEX_MARK_BACKGROUND, cppu::UnoType<bool>::get(),PROPERTY_NONE}, + { OUString( "ShowNonprintingCharacters"),HANDLE_VIEWSET_NONPRINTING_CHARACTERS, cppu::UnoType<bool>::get(),PROPERTY_NONE}, + { OUString( "ShowOnlineLayout"), HANDLE_VIEWSET_ONLINE_LAYOUT , cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID}, + { OUString( "ShowParaBreaks"), HANDLE_VIEWSET_PARA_BREAKS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowProtectedSpaces"), HANDLE_VIEWSET_PROTECTED_SPACES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowRulers"), HANDLE_VIEWSET_SHOW_RULER , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowScrollBarTips"), HANDLE_VIEWSET_SCROLLBAR_TIPS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowSoftHyphens"), HANDLE_VIEWSET_SOFT_HYPHENS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowSpaces"), HANDLE_VIEWSET_SPACES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowTableBoundaries"), HANDLE_VIEWSET_TABLE_BOUNDARIES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowTables"), HANDLE_VIEWSET_TABLES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowTabstops"), HANDLE_VIEWSET_TABSTOPS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowTextBoundaries"), HANDLE_VIEWSET_TEXT_BOUNDARIES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowTextFieldBackground"),HANDLE_VIEWSET_TEXT_FIELD_BACKGROUND, cppu::UnoType<bool>::get(),PROPERTY_NONE}, + { OUString( "ShowVertRuler"), HANDLE_VIEWSET_VRULER , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "ShowVertScrollBar"), HANDLE_VIEWSET_VSCROLL , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "SmoothScrolling"), HANDLE_VIEWSET_SMOOTH_SCROLLING , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "VerticalRulerMetric"), HANDLE_VIEWSET_VERT_RULER_METRIC , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE}, + { OUString( "ZoomType"), HANDLE_VIEWSET_ZOOM_TYPE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE}, + { OUString( "ZoomValue"), HANDLE_VIEWSET_ZOOM , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE}, + { OUString(), 0, css::uno::Type(), 0 } + }; + return new ChainablePropertySetInfo ( aViewSettingsMap_Impl ); +} + +static ChainablePropertySetInfo * lcl_createPrintSettingsInfo() +{ + static PropertyInfo const aPrintSettingsMap_Impl[] = + { + { OUString( "PrintAnnotationMode" ), HANDLE_PRINTSET_ANNOTATION_MODE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE}, + { OUString( "PrintBlackFonts" ), HANDLE_PRINTSET_BLACK_FONTS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintControls" ), HANDLE_PRINTSET_CONTROLS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintDrawings" ), HANDLE_PRINTSET_DRAWINGS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintGraphics" ), HANDLE_PRINTSET_GRAPHICS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintHiddenText"), HANDLE_PRINTSET_HIDDEN_TEXT , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintLeftPages" ), HANDLE_PRINTSET_LEFT_PAGES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintPageBackground" ), HANDLE_PRINTSET_PAGE_BACKGROUND , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintProspect" ), HANDLE_PRINTSET_PROSPECT , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintProspectRTL" ), HANDLE_PRINTSET_PROSPECT_RTL , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintReversed" ), HANDLE_PRINTSET_REVERSED , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintRightPages" ), HANDLE_PRINTSET_RIGHT_PAGES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintFaxName" ), HANDLE_PRINTSET_FAX_NAME , cppu::UnoType<OUString>::get(), PROPERTY_NONE}, + { OUString( "PrintPaperFromSetup" ), HANDLE_PRINTSET_PAPER_FROM_SETUP , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintTables" ), HANDLE_PRINTSET_TABLES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintTextPlaceholder"), HANDLE_PRINTSET_PLACEHOLDER , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintSingleJobs" ), HANDLE_PRINTSET_SINGLE_JOBS , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString( "PrintEmptyPages" ), HANDLE_PRINTSET_EMPTY_PAGES , cppu::UnoType<bool>::get(), PROPERTY_NONE}, + { OUString(), 0, css::uno::Type(), 0} + }; + return new ChainablePropertySetInfo ( aPrintSettingsMap_Impl ); +} + +SwXModule::SwXModule() : + mxViewSettings(), + mxPrintSettings() +{ +} + +SwXModule::~SwXModule() +{ +} + +Reference< XPropertySet > SwXModule::getViewSettings() +{ + SolarMutexGuard aGuard; + if(!mxViewSettings.is()) + { + OSL_FAIL("Web or Text?"); + mxViewSettings = new SwXViewSettings( nullptr ); + } + return mxViewSettings; +} + +Reference< XPropertySet > SwXModule::getPrintSettings() +{ + SolarMutexGuard aGuard; + if(!mxPrintSettings.is()) + { + OSL_FAIL("Web or Text?"); + mxPrintSettings = new SwXPrintSettings( SwXPrintSettingsType::Module ); + } + return mxPrintSettings; +} + +OUString SwXModule::getImplementationName() +{ + return "SwXModule"; +} + +sal_Bool SwXModule::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXModule::getSupportedServiceNames() +{ + return { "com.sun.star.text.GlobalSettings" }; +} + +SwXPrintSettings::SwXPrintSettings(SwXPrintSettingsType eType, SwDoc* pDoc) +: ChainablePropertySet ( lcl_createPrintSettingsInfo (), &Application::GetSolarMutex() ) +, meType(eType) +, mpPrtOpt ( nullptr ) +, mpDoc ( pDoc ) +{ +} + +SwXPrintSettings::~SwXPrintSettings() + throw() +{ +} + +void SwXPrintSettings::_preSetValues () +{ + switch (meType) + { + case SwXPrintSettingsType::Module: + mpPrtOpt = SW_MOD()->GetPrtOptions( false ); + break; + case SwXPrintSettingsType::Document: + { + if (!mpDoc) + throw IllegalArgumentException (); + mpPrtOpt = const_cast< SwPrintData * >(&mpDoc->getIDocumentDeviceAccess().getPrintData()); + } + break; + } +} + +namespace +{ + bool tryBoolAccess(const uno::Any &rValue) + { + const auto xPrSet = o3tl::tryAccess<bool>(rValue); + if (!xPrSet) + throw lang::IllegalArgumentException(); + return *xPrSet; + } +} + +void SwXPrintSettings::_setSingleValue( const comphelper::PropertyInfo & rInfo, const uno::Any &rValue ) +{ + switch( rInfo.mnHandle ) + { + case HANDLE_PRINTSET_LEFT_PAGES: + { + mpPrtOpt->SetPrintLeftPage(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_RIGHT_PAGES: + { + mpPrtOpt->SetPrintRightPage(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_REVERSED: + { + mpPrtOpt->SetPrintReverse(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_PROSPECT: + { + mpPrtOpt->SetPrintProspect(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_GRAPHICS: + { + mpPrtOpt->SetPrintGraphic(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_TABLES: + { + mpPrtOpt->SetPrintTable(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_DRAWINGS: + { + mpPrtOpt->SetPrintDraw(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_CONTROLS: + { + mpPrtOpt->SetPrintControl(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_PAGE_BACKGROUND: + { + mpPrtOpt->SetPrintPageBackground(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_BLACK_FONTS: + { + mpPrtOpt->SetPrintBlackFont(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_SINGLE_JOBS: + { + mpPrtOpt->SetPrintSingleJobs(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_PAPER_FROM_SETUP: + { + mpPrtOpt->SetPaperFromSetup(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_ANNOTATION_MODE: + { + sal_Int16 nTmp = 0; + rValue >>= nTmp; + SwPostItMode nVal = static_cast<SwPostItMode>(nTmp); + if(nVal > SwPostItMode::EndPage) + throw lang::IllegalArgumentException(); + + mpPrtOpt->SetPrintPostIts(nVal); + } + break; + case HANDLE_PRINTSET_EMPTY_PAGES: + { + mpPrtOpt->SetPrintEmptyPages(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_FAX_NAME: + { + OUString sString; + if ( !(rValue >>= sString)) + throw lang::IllegalArgumentException(); + + mpPrtOpt->SetFaxName(sString); + } + break; + case HANDLE_PRINTSET_PROSPECT_RTL: + { + mpPrtOpt->SetPrintProspect_RTL(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_PLACEHOLDER: + { + mpPrtOpt->SetPrintTextPlaceholder(tryBoolAccess(rValue)); + } + break; + case HANDLE_PRINTSET_HIDDEN_TEXT: + { + mpPrtOpt->SetPrintHiddenText(tryBoolAccess(rValue)); + } + break; + default: + throw UnknownPropertyException(OUString::number(rInfo.mnHandle)); + } +} + +void SwXPrintSettings::_postSetValues() +{ + mpPrtOpt = nullptr; +} + +void SwXPrintSettings::_preGetValues() +{ + switch (meType) + { + case SwXPrintSettingsType::Module: + mpPrtOpt = SW_MOD()->GetPrtOptions( false ); + break; + case SwXPrintSettingsType::Document: + { + if (!mpDoc) + throw IllegalArgumentException (); + mpPrtOpt = const_cast< SwPrintData * >(&mpDoc->getIDocumentDeviceAccess().getPrintData()); + } + break; + } +} + +void SwXPrintSettings::_getSingleValue( const comphelper::PropertyInfo & rInfo, uno::Any & rValue ) +{ + switch( rInfo.mnHandle ) + { + case HANDLE_PRINTSET_LEFT_PAGES: + rValue <<= mpPrtOpt->IsPrintLeftPage(); + break; + case HANDLE_PRINTSET_RIGHT_PAGES: + rValue <<= mpPrtOpt->IsPrintRightPage(); + break; + case HANDLE_PRINTSET_REVERSED: + rValue <<= mpPrtOpt->IsPrintReverse(); + break; + case HANDLE_PRINTSET_PROSPECT: + rValue <<= mpPrtOpt->IsPrintProspect(); + break; + case HANDLE_PRINTSET_GRAPHICS: + rValue <<= mpPrtOpt->IsPrintGraphic(); + break; + case HANDLE_PRINTSET_TABLES: + rValue <<= mpPrtOpt->IsPrintTable(); + break; + case HANDLE_PRINTSET_DRAWINGS: + rValue <<= mpPrtOpt->IsPrintDraw(); + break; + case HANDLE_PRINTSET_CONTROLS: + rValue <<= mpPrtOpt->IsPrintControl(); + break; + case HANDLE_PRINTSET_PAGE_BACKGROUND: + rValue <<= mpPrtOpt->IsPrintPageBackground(); + break; + case HANDLE_PRINTSET_BLACK_FONTS: + rValue <<= mpPrtOpt->IsPrintBlackFont(); + break; + case HANDLE_PRINTSET_SINGLE_JOBS: + rValue <<= mpPrtOpt->IsPrintSingleJobs(); + break; + case HANDLE_PRINTSET_EMPTY_PAGES: + rValue <<= mpPrtOpt->IsPrintEmptyPages(); + break; + case HANDLE_PRINTSET_PAPER_FROM_SETUP: + rValue <<= mpPrtOpt->IsPaperFromSetup(); + break; + case HANDLE_PRINTSET_ANNOTATION_MODE: + { + rValue <<= static_cast < sal_Int16 > ( mpPrtOpt->GetPrintPostIts() ); + } + break; + case HANDLE_PRINTSET_FAX_NAME : + { + rValue <<= mpPrtOpt->GetFaxName(); + } + break; + case HANDLE_PRINTSET_PROSPECT_RTL: + { + rValue <<= mpPrtOpt->IsPrintProspectRTL(); + } + break; + case HANDLE_PRINTSET_PLACEHOLDER: + { + rValue <<= mpPrtOpt->IsPrintTextPlaceholder(); + } + break; + case HANDLE_PRINTSET_HIDDEN_TEXT: + { + rValue <<= mpPrtOpt->IsPrintHiddenText(); + } + break; + default: + throw UnknownPropertyException(OUString::number(rInfo.mnHandle)); + } +} + +void SwXPrintSettings::_postGetValues () +{ + mpPrtOpt = nullptr; +} + +OUString SwXPrintSettings::getImplementationName() +{ + return "SwXPrintSettings"; +} + +sal_Bool SwXPrintSettings::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXPrintSettings::getSupportedServiceNames() +{ + Sequence<OUString> aRet { "com.sun.star.text.PrintSettings" }; + return aRet; +} + +SwXViewSettings::SwXViewSettings(SwView* pVw) + : ChainablePropertySet( lcl_createViewSettingsInfo (), &Application::GetSolarMutex() ) + , pView(pVw) + , mpConstViewOption(nullptr) + , bObjectValid(true) + , mbApplyZoom(false) + , eHRulerUnit(FieldUnit::CM) + , mbApplyHRulerMetric(false) + , eVRulerUnit(FieldUnit::CM) + , mbApplyVRulerMetric(false) +{ + // This property only exists if we have a view (ie, not at the module ) + if ( !pView ) + mxInfo->remove ( "HelpURL" ); + +} + +SwXViewSettings::~SwXViewSettings() + throw() +{ + +} + +void SwXViewSettings::_preSetValues () +{ + const SwViewOption* pVOpt = nullptr; + if(pView) + { + if(!IsValid()) + return; + pVOpt = pView->GetWrtShell().GetViewOptions(); + } + else + pVOpt = SW_MOD()->GetViewOption(false); + + mpViewOption.reset( new SwViewOption (*pVOpt) ); + mbApplyZoom = false; + if(pView) + mpViewOption->SetStarOneSetting(true); +} + +void SwXViewSettings::_setSingleValue( const comphelper::PropertyInfo & rInfo, const uno::Any &rValue ) +{ + // the API flag should not be set to the application's view settings + switch( rInfo.mnHandle ) + { + case HANDLE_VIEWSET_SHOW_RULER : mpViewOption->SetViewAnyRuler(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_HRULER : mpViewOption->SetViewHRuler(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_VRULER : mpViewOption->SetViewVRuler(*o3tl::doAccess<bool>(rValue));break; + case HANDLE_VIEWSET_VRULER_RIGHT : mpViewOption->SetVRulerRight(*o3tl::doAccess<bool>(rValue));break; + case HANDLE_VIEWSET_HSCROLL : mpViewOption->SetViewHScrollBar(*o3tl::doAccess<bool>(rValue));break; + case HANDLE_VIEWSET_VSCROLL : mpViewOption->SetViewVScrollBar(*o3tl::doAccess<bool>(rValue));break; + case HANDLE_VIEWSET_GRAPHICS : mpViewOption->SetGraphic(*o3tl::doAccess<bool>(rValue));break; + case HANDLE_VIEWSET_TABLES : mpViewOption->SetTable(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_DRAWINGS : mpViewOption->SetDraw(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_FIELD_COMMANDS : mpViewOption->SetFieldName(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_ANNOTATIONS : mpViewOption->SetPostIts(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_INDEX_MARK_BACKGROUND : SwViewOption::SetAppearanceFlag(ViewOptFlags::FieldShadings, *o3tl::doAccess<bool>(rValue), true); break; + case HANDLE_VIEWSET_NONPRINTING_CHARACTERS: mpViewOption->SetViewMetaChars( *o3tl::doAccess<bool>(rValue) ); break; + case HANDLE_VIEWSET_FOOTNOTE_BACKGROUND : SwViewOption::SetAppearanceFlag(ViewOptFlags::FieldShadings, *o3tl::doAccess<bool>(rValue), true); break; + case HANDLE_VIEWSET_TEXT_FIELD_BACKGROUND : SwViewOption::SetAppearanceFlag(ViewOptFlags::FieldShadings, *o3tl::doAccess<bool>(rValue), true); break; + case HANDLE_VIEWSET_PARA_BREAKS : mpViewOption->SetParagraph(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_SOFT_HYPHENS : mpViewOption->SetSoftHyph(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_SPACES : mpViewOption->SetBlank(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_PROTECTED_SPACES : mpViewOption->SetHardBlank(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_TABSTOPS : mpViewOption->SetTab(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_BREAKS : mpViewOption->SetLineBreak(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_BOOKMARKS : mpViewOption->SetShowBookmarks(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_HIDDEN_TEXT : mpViewOption->SetShowHiddenField(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_HIDDEN_CHARACTERS : mpViewOption->SetShowHiddenChar(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_HIDDEN_PARAGRAPHS : mpViewOption->SetShowHiddenPara(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_TABLE_BOUNDARIES : SwViewOption::SetAppearanceFlag(ViewOptFlags::TableBoundaries, *o3tl::doAccess<bool>(rValue), true); break; + case HANDLE_VIEWSET_TEXT_BOUNDARIES : SwViewOption::SetDocBoundaries(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_SMOOTH_SCROLLING : mpViewOption->SetSmoothScroll(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_SHOW_CONTENT_TIPS : mpViewOption->SetShowContentTips(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_IS_RASTER_VISIBLE : mpViewOption->SetGridVisible(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_IS_SNAP_TO_RASTER : mpViewOption->SetSnap(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_SCROLLBAR_TIPS : mpViewOption->SetShowScrollBarTips(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_INLINECHANGES_TIPS : mpViewOption->SetShowInlineTooltips(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_USE_HEADERFOOTERMENU : mpViewOption->SetUseHeaderFooterMenu(*o3tl::doAccess<bool>(rValue)); break; + case HANDLE_VIEWSET_RASTER_RESOLUTION_X : + { + sal_Int32 nTmp = 0; + if(!(rValue >>= nTmp) || nTmp < 10) + throw IllegalArgumentException(); + Size aSize( mpViewOption->GetSnapSize() ); + aSize.setWidth( convertMm100ToTwip( nTmp ) ); + mpViewOption->SetSnapSize( aSize ); + } + break; + case HANDLE_VIEWSET_RASTER_RESOLUTION_Y : + { + sal_Int32 nTmp = 0; + if(!(rValue >>= nTmp) || nTmp < 10) + throw IllegalArgumentException(); + Size aSize( mpViewOption->GetSnapSize() ); + aSize.setHeight( convertMm100ToTwip( nTmp ) ); + mpViewOption->SetSnapSize( aSize ); + } + break; + case HANDLE_VIEWSET_RASTER_SUBDIVISION_X : + { + sal_Int32 nTmp = 0; + if(!(rValue >>= nTmp) || !(0 <= nTmp && nTmp < 100)) + throw IllegalArgumentException(); + mpViewOption->SetDivisionX( static_cast<short>(nTmp) ); + } + break; + case HANDLE_VIEWSET_RASTER_SUBDIVISION_Y : + { + sal_Int32 nTmp = 0; + if(!(rValue >>= nTmp) || !(0 <= nTmp && nTmp < 100)) + throw IllegalArgumentException(); + mpViewOption->SetDivisionY( static_cast<short>(nTmp) ); + } + break; + case HANDLE_VIEWSET_ZOOM : + { + sal_Int16 nZoom = 0; + if(!(rValue >>= nZoom) || nZoom > MAXZOOM || nZoom < MINZOOM) + throw lang::IllegalArgumentException(); + mpViewOption->SetZoom(static_cast<sal_uInt16>(nZoom)); + mbApplyZoom = true; + } + break; + case HANDLE_VIEWSET_ZOOM_TYPE: + { + sal_Int16 nZoom = 0; + if(!(rValue >>= nZoom)) + throw IllegalArgumentException(); + SvxZoomType eZoom; + switch (nZoom) + { + case view::DocumentZoomType::OPTIMAL: + eZoom = SvxZoomType::OPTIMAL; + break; + case view::DocumentZoomType::PAGE_WIDTH: + eZoom = SvxZoomType::PAGEWIDTH; + break; + case view::DocumentZoomType::ENTIRE_PAGE: + eZoom = SvxZoomType::WHOLEPAGE; + break; + case view::DocumentZoomType::BY_VALUE: + eZoom = SvxZoomType::PERCENT; + break; + case view::DocumentZoomType::PAGE_WIDTH_EXACT: + eZoom = SvxZoomType::PAGEWIDTH_NOBORDER; + break; + default: + throw IllegalArgumentException( + "SwXViewSettings: invalid zoom type", nullptr, 0); + } + mpViewOption->SetZoomType( eZoom ); + mbApplyZoom = true; + } + break; + case HANDLE_VIEWSET_ONLINE_LAYOUT : + { + if ( pView ) + { + bool bVal = *o3tl::doAccess<bool>(rValue); + SwViewOption aOpt(*pView->GetWrtShell().GetViewOptions()); + if (!bVal != !aOpt.getBrowseMode()) + { + aOpt.setBrowseMode( bVal ); + pView->GetWrtShell().ApplyViewOptions( aOpt ); + + // must be set in mpViewOption as this will overwrite settings in _post! + if(mpViewOption) + mpViewOption->setBrowseMode(bVal); + + pView->GetDocShell()->ToggleLayoutMode(pView); + } + } + } + break; + case HANDLE_VIEWSET_HIDE_WHITESPACE: + { + if ( pView ) + { + bool bVal = *o3tl::doAccess<bool>(rValue); + SwViewOption aOpt(*pView->GetWrtShell().GetViewOptions()); + if (!bVal != !aOpt.IsHideWhitespaceMode()) + { + aOpt.SetHideWhitespaceMode( bVal ); + pView->GetWrtShell().ApplyViewOptions( aOpt ); + + // must be set in mpViewOption as this will overwrite settings in _post! + if(mpViewOption) + mpViewOption->SetHideWhitespaceMode(bVal); + } + } + } + break; + case HANDLE_VIEWSET_HELP_URL: + { + if ( !pView ) + throw UnknownPropertyException(); + + OUString sHelpURL; + if ( ! ( rValue >>= sHelpURL ) ) + throw IllegalArgumentException(); + + INetURLObject aHID( sHelpURL ); + if ( aHID.GetProtocol() != INetProtocol::Hid ) + throw IllegalArgumentException (); + + pView->GetEditWin().SetHelpId( OUStringToOString( aHID.GetURLPath(), RTL_TEXTENCODING_UTF8 ) ); + } + break; + case HANDLE_VIEWSET_HORI_RULER_METRIC: + case HANDLE_VIEWSET_VERT_RULER_METRIC: + { + sal_uInt16 nUnit; + if( rValue >>= nUnit ) + switch (static_cast<FieldUnit>(nUnit)) + { + case FieldUnit::MM: + case FieldUnit::CM: + case FieldUnit::POINT: + case FieldUnit::PICA: + case FieldUnit::INCH: + if( rInfo.mnHandle == HANDLE_VIEWSET_HORI_RULER_METRIC ) + { + eHRulerUnit = static_cast<FieldUnit>(nUnit); + mbApplyHRulerMetric = true; + } + else + { + eVRulerUnit = static_cast<FieldUnit>(nUnit); + mbApplyVRulerMetric = true; + } + break; + default: + throw IllegalArgumentException(); + } + } + break; + default: + throw UnknownPropertyException(OUString::number(rInfo.mnHandle)); + } +} + +void SwXViewSettings::_postSetValues() +{ + if( pView ) + { + if(mbApplyZoom ) + pView->SetZoom( mpViewOption->GetZoomType(), + mpViewOption->GetZoom(), true ); + if(mbApplyHRulerMetric) + pView->ChangeTabMetric(eHRulerUnit); + if(mbApplyVRulerMetric) + pView->ChangeVRulerMetric(eVRulerUnit); + + } + else + { + if(mbApplyHRulerMetric) + SW_MOD()->ApplyRulerMetric( eHRulerUnit, true, false ); + if(mbApplyVRulerMetric) + SW_MOD()->ApplyRulerMetric( eVRulerUnit, false, false ); + } + + SW_MOD()->ApplyUsrPref( *mpViewOption, pView, pView ? SvViewOpt::DestViewOnly + : SvViewOpt::DestText ); + + mpViewOption.reset(); +} + +void SwXViewSettings::_preGetValues () +{ + if(pView) + { + if(!IsValid()) + return; + mpConstViewOption = pView->GetWrtShell().GetViewOptions(); + } + else + mpConstViewOption = SW_MOD()->GetViewOption(false); +} + +void SwXViewSettings::_getSingleValue( const comphelper::PropertyInfo & rInfo, uno::Any & rValue ) +{ + bool bBool = true; + bool bBoolVal = false; + switch( rInfo.mnHandle ) + { + case HANDLE_VIEWSET_SHOW_RULER: bBoolVal = mpConstViewOption->IsViewAnyRuler(); break; + case HANDLE_VIEWSET_HRULER : bBoolVal = mpConstViewOption->IsViewHRuler(true); break; + case HANDLE_VIEWSET_VRULER : bBoolVal = mpConstViewOption->IsViewVRuler(true);break; + case HANDLE_VIEWSET_VRULER_RIGHT : bBoolVal = mpConstViewOption->IsVRulerRight();break; + case HANDLE_VIEWSET_HSCROLL: bBoolVal = mpConstViewOption->IsViewHScrollBar();break; + case HANDLE_VIEWSET_VSCROLL: bBoolVal = mpConstViewOption->IsViewVScrollBar();break; + case HANDLE_VIEWSET_GRAPHICS : bBoolVal = mpConstViewOption->IsGraphic();break; + case HANDLE_VIEWSET_TABLES : bBoolVal = mpConstViewOption->IsTable(); break; + case HANDLE_VIEWSET_DRAWINGS : bBoolVal = mpConstViewOption->IsDraw(); break; + case HANDLE_VIEWSET_FIELD_COMMANDS : bBoolVal = mpConstViewOption->IsFieldName(); break; + case HANDLE_VIEWSET_ANNOTATIONS : bBoolVal = mpConstViewOption->IsPostIts(); break; + case HANDLE_VIEWSET_INDEX_MARK_BACKGROUND : bBoolVal = SwViewOption::IsFieldShadings(); break; + case HANDLE_VIEWSET_NONPRINTING_CHARACTERS: bBoolVal = mpConstViewOption->IsViewMetaChars(); break; + case HANDLE_VIEWSET_FOOTNOTE_BACKGROUND : bBoolVal = SwViewOption::IsFieldShadings(); break; + case HANDLE_VIEWSET_TEXT_FIELD_BACKGROUND : bBoolVal = SwViewOption::IsFieldShadings(); break; + case HANDLE_VIEWSET_PARA_BREAKS : bBoolVal = mpConstViewOption->IsParagraph(true); break; + case HANDLE_VIEWSET_SOFT_HYPHENS : bBoolVal = mpConstViewOption->IsSoftHyph(); break; + case HANDLE_VIEWSET_SPACES : bBoolVal = mpConstViewOption->IsBlank(true); break; + case HANDLE_VIEWSET_PROTECTED_SPACES : bBoolVal = mpConstViewOption->IsHardBlank(); break; + case HANDLE_VIEWSET_TABSTOPS : bBoolVal = mpConstViewOption->IsTab(true); break; + case HANDLE_VIEWSET_BREAKS : bBoolVal = mpConstViewOption->IsLineBreak(true); break; + case HANDLE_VIEWSET_BOOKMARKS : bBoolVal = mpConstViewOption->IsShowBookmarks(true); break; + case HANDLE_VIEWSET_HIDDEN_TEXT : bBoolVal = mpConstViewOption->IsShowHiddenField(); break; + case HANDLE_VIEWSET_HIDDEN_CHARACTERS : bBoolVal = mpConstViewOption->IsShowHiddenChar(true); break; + case HANDLE_VIEWSET_HIDE_WHITESPACE : bBoolVal = mpConstViewOption->IsHideWhitespaceMode(); break; + case HANDLE_VIEWSET_HIDDEN_PARAGRAPHS : bBoolVal = mpConstViewOption->IsShowHiddenPara(); break; + case HANDLE_VIEWSET_TABLE_BOUNDARIES : bBoolVal = SwViewOption::IsTableBoundaries(); break; + case HANDLE_VIEWSET_TEXT_BOUNDARIES : bBoolVal = SwViewOption::IsDocBoundaries(); break; + case HANDLE_VIEWSET_SMOOTH_SCROLLING : bBoolVal = mpConstViewOption->IsSmoothScroll(); break; + case HANDLE_VIEWSET_SHOW_CONTENT_TIPS : bBoolVal = mpConstViewOption->IsShowContentTips(); break; + case HANDLE_VIEWSET_INLINECHANGES_TIPS : bBoolVal = mpConstViewOption->IsShowInlineTooltips(); break; + case HANDLE_VIEWSET_IS_RASTER_VISIBLE : bBoolVal = mpConstViewOption->IsGridVisible(); break; + case HANDLE_VIEWSET_IS_SNAP_TO_RASTER : bBoolVal = mpConstViewOption->IsSnap(); break; + case HANDLE_VIEWSET_SCROLLBAR_TIPS : bBoolVal = mpConstViewOption->IsShowScrollBarTips(); break; + case HANDLE_VIEWSET_RASTER_RESOLUTION_X : + bBool = false; + rValue <<= static_cast<sal_Int32>(convertTwipToMm100(mpConstViewOption->GetSnapSize().Width())); + break; + case HANDLE_VIEWSET_RASTER_RESOLUTION_Y : + bBool = false; + rValue <<= static_cast<sal_Int32>(convertTwipToMm100(mpConstViewOption->GetSnapSize().Height())); + break; + case HANDLE_VIEWSET_RASTER_SUBDIVISION_X : + bBool = false; + rValue <<= static_cast<sal_Int32>(mpConstViewOption->GetDivisionX()); + break; + case HANDLE_VIEWSET_RASTER_SUBDIVISION_Y : + bBool = false; + rValue <<= static_cast<sal_Int32>(mpConstViewOption->GetDivisionY()); + break; + case HANDLE_VIEWSET_ZOOM : + bBool = false; + rValue <<= static_cast<sal_Int16>(mpConstViewOption->GetZoom()); + break; + case HANDLE_VIEWSET_ZOOM_TYPE: + { + bBool = false; + sal_Int16 nRet(0); + switch (mpConstViewOption->GetZoomType()) + { + case SvxZoomType::OPTIMAL: + nRet = view::DocumentZoomType::OPTIMAL; + break; + case SvxZoomType::PAGEWIDTH: + nRet = view::DocumentZoomType::PAGE_WIDTH; + break; + case SvxZoomType::WHOLEPAGE: + nRet = view::DocumentZoomType::ENTIRE_PAGE; + break; + case SvxZoomType::PERCENT: + nRet = view::DocumentZoomType::BY_VALUE; + break; + case SvxZoomType::PAGEWIDTH_NOBORDER: + nRet = view::DocumentZoomType::PAGE_WIDTH_EXACT; + break; + default: + OSL_FAIL("SwXViewSettings: invalid zoom type"); + break; + } + rValue <<= nRet; + } + break; + case HANDLE_VIEWSET_ONLINE_LAYOUT: + if(pView) + bBoolVal = pView->GetWrtShell().GetViewOptions()->getBrowseMode(); + break; + case HANDLE_VIEWSET_HELP_URL : + { + if ( !pView ) + throw UnknownPropertyException(); + + bBool = false; + OUStringBuffer sHelpURL; + sHelpURL.append ( INET_HID_SCHEME ); + SwEditWin &rEditWin = pView->GetEditWin(); + sHelpURL.append( OUString::fromUtf8( rEditWin.GetHelpId() ) ); + rValue <<= sHelpURL.makeStringAndClear(); + } + break; + case HANDLE_VIEWSET_HORI_RULER_METRIC: + { + if ( pView ) + { + FieldUnit eUnit; + pView->GetHRulerMetric( eUnit ); + rValue <<= static_cast<sal_Int32>(eUnit); + } + else + { + const SwMasterUsrPref* pUsrPref = SW_MOD()->GetUsrPref( false ); + rValue <<= static_cast<sal_Int32>(pUsrPref->GetHScrollMetric()); + } + bBool = false; + } + break; + case HANDLE_VIEWSET_VERT_RULER_METRIC: + { + if ( pView ) + { + FieldUnit eUnit; + pView->GetVRulerMetric( eUnit ); + rValue <<= static_cast<sal_Int32>(eUnit); + } + else + { + const SwMasterUsrPref* pUsrPref = SW_MOD()->GetUsrPref( false ); + rValue <<= static_cast<sal_Int32>(pUsrPref->GetVScrollMetric()); + } + bBool = false; + } + break; + default: OSL_FAIL("there is no such ID!"); + } + if( bBool ) + rValue <<= bBoolVal; +} + +void SwXViewSettings::_postGetValues () +{ + mpConstViewOption = nullptr; +} + +OUString SwXViewSettings::getImplementationName() +{ + return "SwXViewSettings"; +} + +sal_Bool SwXViewSettings::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXViewSettings::getSupportedServiceNames() +{ + Sequence<OUString> aRet { "com.sun.star.text.ViewSettings" }; + return aRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +SwXModule_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SwXModule()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unomodule.cxx b/sw/source/uibase/uno/unomodule.cxx new file mode 100644 index 000000000..0d595e773 --- /dev/null +++ b/sw/source/uibase/uno/unomodule.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 <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/Desktop.hpp> + +#include <swmodule.hxx> +#include <swdll.hxx> +#include "unomodule.hxx" +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/processfactory.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/frame.hxx> +#include <vcl/svapp.hxx> + +using namespace css; + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Writer_WriterModule_get_implementation(uno::XComponentContext* /*pCtx*/, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + SolarMutexGuard aGuard; + return cppu::acquire(new SwUnoModule); +} + + // XNotifyingDispatch +void SAL_CALL SwUnoModule::dispatchWithNotification( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs, const uno::Reference< frame::XDispatchResultListener >& xListener ) +{ + // there is no guarantee, that we are holded alive during this method! + // May the outside dispatch container will be updated by a CONTEXT_CHANGED + // asynchronous ... + uno::Reference< uno::XInterface > xThis(static_cast< frame::XNotifyingDispatch* >(this)); + + SolarMutexGuard aGuard; + SwGlobals::ensure(); + const SfxSlot* pSlot = SW_MOD()->GetInterface()->GetSlot( aURL.Complete ); + + sal_Int16 aState = frame::DispatchResultState::DONTKNOW; + if ( !pSlot ) + aState = frame::DispatchResultState::FAILURE; + else + { + SfxRequest aReq( pSlot, aArgs, SfxCallMode::SYNCHRON, SW_MOD()->GetPool() ); + SfxAllItemSet aInternalSet( SfxGetpApp()->GetPool() ); + + css::uno::Reference<css::frame::XDesktop2> xDesktop = css::frame::Desktop::create(::comphelper::getProcessComponentContext()); + css::uno::Reference<css::frame::XFrame> xCurrentFrame = xDesktop->getCurrentFrame(); + if (xCurrentFrame.is()) // an empty set is no problem ... but an empty frame reference can be a problem ! + aInternalSet.Put(SfxUnoFrameItem(SID_FILLFRAME, xCurrentFrame)); + + aReq.SetInternalArgs_Impl(aInternalSet); + const SfxPoolItem* pResult = SW_MOD()->ExecuteSlot( aReq ); + if ( pResult ) + aState = frame::DispatchResultState::SUCCESS; + else + aState = frame::DispatchResultState::FAILURE; + } + + if ( xListener.is() ) + { + xListener->dispatchFinished( + frame::DispatchResultEvent( + xThis, aState, uno::Any())); + } +} + + // XDispatch +void SAL_CALL SwUnoModule::dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs ) +{ + dispatchWithNotification(aURL, aArgs, uno::Reference< frame::XDispatchResultListener >()); +} + +void SAL_CALL SwUnoModule::addStatusListener( + const uno::Reference< frame::XStatusListener > & /*xControl*/, + const util::URL& /*aURL*/) +{ +} + +void SAL_CALL SwUnoModule::removeStatusListener( + const uno::Reference< frame::XStatusListener > & /*xControl*/, + const util::URL& /*aURL*/) +{ +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL SwUnoModule::queryDispatches( + const uno::Sequence< frame::DispatchDescriptor >& seqDescripts ) +{ + sal_Int32 nCount = seqDescripts.getLength(); + uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount ); + + std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.begin(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference< frame::XDispatch > { + return queryDispatch( rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags ); }); + + return lDispatcher; +} + +// XDispatchProvider +uno::Reference< frame::XDispatch > SAL_CALL SwUnoModule::queryDispatch( + const util::URL& aURL, const OUString& /*sTargetFrameName*/, + sal_Int32 /*eSearchFlags*/ ) +{ + uno::Reference< frame::XDispatch > xReturn; + + SolarMutexGuard aGuard; + SwGlobals::ensure(); + const SfxSlot* pSlot = SW_MOD()->GetInterface()->GetSlot( aURL.Complete ); + if ( pSlot ) + xReturn.set(static_cast< frame::XDispatch* >(this), uno::UNO_QUERY); + + return xReturn; +} + +// XServiceInfo +OUString SAL_CALL SwUnoModule::getImplementationName( ) +{ + return "com.sun.star.comp.Writer.WriterModule"; +} + +sal_Bool SAL_CALL SwUnoModule::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString > SAL_CALL SwUnoModule::getSupportedServiceNames( ) +{ + uno::Sequence<OUString> aSeq { "com.sun.star.text.ModuleDispatcher" }; + return aSeq; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unomodule.hxx b/sw/source/uibase/uno/unomodule.hxx new file mode 100644 index 000000000..1d2157eff --- /dev/null +++ b/sw/source/uibase/uno/unomodule.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_UIBASE_UNO_UNOMODULE_HXX +#define INCLUDED_SW_SOURCE_UIBASE_UNO_UNOMODULE_HXX + +#include <rtl/ustring.hxx> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/frame/DispatchDescriptor.hpp> +#include <com/sun/star/uno/Reference.h> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +namespace com +{ + namespace sun + { + namespace star + { + namespace lang + { + class XMultiServiceFactory; + } + namespace beans + { + struct PropertyValue; + } + } + } +} + +class SwUnoModule : public ::cppu::WeakImplHelper< css::frame::XDispatchProvider, css::frame::XNotifyingDispatch, css::lang::XServiceInfo > +{ +public: + SwUnoModule() {} + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs, const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + + // XDispatchProvider + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& seqDescriptor ) override ; + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL & aURL , + const OUString & sTargetFrameName, + sal_Int32 eSearchFlags ) 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/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx new file mode 100644 index 000000000..b65386a7c --- /dev/null +++ b/sw/source/uibase/uno/unotxdoc.cxx @@ -0,0 +1,4475 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <boost/property_tree/json_parser.hpp> + +#include <sal/config.h> + +#include <comphelper/string.hxx> +#include <AnnotationWin.hxx> +#include <o3tl/any.hxx> +#include <osl/mutex.hxx> +#include <vcl/virdev.hxx> +#include <vcl/sysdata.hxx> +#include <vcl/svapp.hxx> +#include <vcl/print.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/lokhelper.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/printer.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <sfx2/lokcharthelper.hxx> +#include <sfx2/ipclient.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/acorrcfg.hxx> +#include <cmdid.h> +#include <swtypes.hxx> +#include <wdocsh.hxx> +#include <wrtsh.hxx> +#include <pview.hxx> +#include <viewsh.hxx> +#include <pvprtdat.hxx> +#include <printdata.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <svl/stritem.hxx> +#include <unotxdoc.hxx> +#include <svl/numuno.hxx> +#include <fldbas.hxx> +#include <unomap.hxx> +#include <unotextbodyhf.hxx> +#include <unotextrange.hxx> +#include <unotextcursor.hxx> +#include <unosett.hxx> +#include <unocoll.hxx> +#include <unoredlines.hxx> +#include <unosrch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/objsh.hxx> +#include <unoprnms.hxx> +#include <unostyle.hxx> +#include <unodraw.hxx> +#include <svl/eitem.hxx> +#include <unotools/datetime.hxx> +#include <unocrsr.hxx> +#include <unofieldcoll.hxx> +#include <unoidxcoll.hxx> +#include <unocrsrhelper.hxx> +#include <globdoc.hxx> +#include <viewopt.hxx> +#include <unochart.hxx> +#include <charatr.hxx> +#include <svx/xmleohlp.hxx> +#include <unotools/printwarningoptions.hxx> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/document/RedlineDisplayType.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <sfx2/linkmgr.hxx> +#include <svx/unofill.hxx> +#include <swmodule.hxx> +#include <docstat.hxx> +#include <modcfg.hxx> +#include <ndtxt.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include "unodefaults.hxx" +#include "SwXDocumentSettings.hxx" +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentChartDataProviderAccess.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentStatistics.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentState.hxx> +#include <svl/zforlist.hxx> +#include <drawdoc.hxx> +#include <SwStyleNameMapper.hxx> +#include <osl/file.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/storagehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/saveopt.hxx> +#include <swruler.hxx> +#include <docufld.hxx> + + +#include <EnhancedPDFExportHelper.hxx> +#include <numrule.hxx> + +#include <editeng/langitem.hxx> +#include <docary.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nutil/searchopt.hxx> + +#include <charfmt.hxx> +#include <fmtcol.hxx> +#include <istyleaccess.hxx> + +#include <swatrset.hxx> +#include <view.hxx> +#include <viscrs.hxx> +#include <srcview.hxx> +#include <edtwin.hxx> +#include <swdtflvr.hxx> +#include <PostItMgr.hxx> + +#include <svtools/langtab.hxx> +#include <map> +#include <set> +#include <vector> + +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdview.hxx> +#include <comphelper/servicehelper.hxx> +#include <memory> +#include <redline.hxx> +#include <DocumentRedlineManager.hxx> +#include <xmloff/odffields.hxx> + +#define TWIPS_PER_PIXEL 15 + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using ::osl::FileBase; + +static std::unique_ptr<SwPrintUIOptions> lcl_GetPrintUIOptions( + SwDocShell * pDocShell, + const SfxViewShell * pView ) +{ + if (!pDocShell) + return nullptr; + + const bool bWebDoc = nullptr != dynamic_cast< const SwWebDocShell * >(pDocShell); + const bool bSwSrcView = nullptr != dynamic_cast< const SwSrcView * >(pView); + const SwView * pSwView = dynamic_cast< const SwView * >(pView); + const bool bHasSelection = pSwView && pSwView->HasSelection( false ); // check for any selection, not just text selection + const bool bHasPostIts = sw_GetPostIts( &pDocShell->GetDoc()->getIDocumentFieldsAccess(), nullptr ); + + // get default values to use in dialog from documents SwPrintData + const SwPrintData &rPrintData = pDocShell->GetDoc()->getIDocumentDeviceAccess().getPrintData(); + + // Get current page number + sal_uInt16 nCurrentPage = 1; + const SwWrtShell* pSh = pDocShell->GetWrtShell(); + const SwRootFrame *pFrame = nullptr; + if (pSh) + { + SwPaM* pShellCursor = pSh->GetCursor(); + nCurrentPage = pShellCursor->GetPageNum(); + pFrame = pSh->GetLayout(); + } + else if (!bSwSrcView) + { + const SwPagePreview* pPreview = dynamic_cast< const SwPagePreview* >(pView); + OSL_ENSURE(pPreview, "Unexpected type of the view shell"); + if (pPreview) + { + nCurrentPage = pPreview->GetSelectedPage(); + pFrame = pPreview->GetViewShell()->GetLayout(); + } + } + + // If blanks are skipped, account for them in initial page range value + if (pFrame && !rPrintData.IsPrintEmptyPages()) + { + sal_uInt16 nMax = nCurrentPage; + const SwPageFrame *pPage = dynamic_cast<const SwPageFrame*>(pFrame->Lower()); + while (pPage && nMax-- > 0) + { + if (pPage->getFrameArea().Height() == 0) + nCurrentPage--; + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + } + } + return std::make_unique<SwPrintUIOptions>( nCurrentPage, bWebDoc, bSwSrcView, bHasSelection, bHasPostIts, rPrintData ); +} + +static SwTextFormatColl *lcl_GetParaStyle(const OUString& rCollName, SwDoc* pDoc) +{ + SwTextFormatColl* pColl = pDoc->FindTextFormatCollByName( rCollName ); + if( !pColl ) + { + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( + rCollName, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId ) + pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nId ); + } + return pColl; +} + +static void lcl_DisposeView( SfxViewFrame* pToClose, SwDocShell const * pDocShell ) +{ + // check if the view frame still exists + SfxViewFrame* pFound = SfxViewFrame::GetFirst( pDocShell, false ); + while(pFound) + { + if( pFound == pToClose) + { + pToClose->DoClose(); + break; + } + pFound = SfxViewFrame::GetNext( *pFound, pDocShell, false ); + } +} + +class SwXTextDocument::Impl +{ +private: + ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 + +public: + ::comphelper::OInterfaceContainerHelper2 m_RefreshListeners; + + Impl() : m_RefreshListeners(m_Mutex) { } + +}; + +namespace +{ + class theSwXTextDocumentUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextDocumentUnoTunnelId > {}; +} + +const Sequence< sal_Int8 > & SwXTextDocument::getUnoTunnelId() +{ + return theSwXTextDocumentUnoTunnelId::get().getSeq(); +} + +sal_Int64 SAL_CALL SwXTextDocument::getSomething( const Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextDocument>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); + } + if( isUnoTunnelId<SfxObjectShell>(rId) ) + { + return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(pDocShell )); + } + + sal_Int64 nRet = SfxBaseModel::getSomething( rId ); + if (nRet) + return nRet; + + GetNumberFormatter(); + if (!xNumFormatAgg.is()) // may happen if not valid or no SwDoc + return 0; + Any aNumTunnel = xNumFormatAgg->queryAggregation(cppu::UnoType<XUnoTunnel>::get()); + Reference<XUnoTunnel> xNumTunnel; + aNumTunnel >>= xNumTunnel; + return (xNumTunnel.is()) ? xNumTunnel->getSomething(rId) : 0; +} + +Any SAL_CALL SwXTextDocument::queryInterface( const uno::Type& rType ) +{ + Any aRet = SwXTextDocumentBaseClass::queryInterface(rType); + if ( !aRet.hasValue() ) + aRet = SfxBaseModel::queryInterface(rType); + if ( !aRet.hasValue() && + rType == cppu::UnoType<lang::XMultiServiceFactory>::get()) + { + Reference<lang::XMultiServiceFactory> xTmp = this; + aRet <<= xTmp; + } + if ( !aRet.hasValue() && + rType == cppu::UnoType<tiledrendering::XTiledRenderable>::get()) + { + Reference<tiledrendering::XTiledRenderable> xTmp = this; + aRet <<= xTmp; + } + + if ( !aRet.hasValue() + && rType != cppu::UnoType<css::document::XDocumentEventBroadcaster>::get() + && rType != cppu::UnoType<css::frame::XController>::get() + && rType != cppu::UnoType<css::frame::XFrame>::get() + && rType != cppu::UnoType<css::script::XInvocation>::get() + && rType != cppu::UnoType<css::beans::XFastPropertySet>::get() + && rType != cppu::UnoType<css::awt::XWindow>::get()) + { + GetNumberFormatter(); + if(xNumFormatAgg.is()) + aRet = xNumFormatAgg->queryAggregation(rType); + } + return aRet; +} + +void SAL_CALL SwXTextDocument::acquire()throw() +{ + SfxBaseModel::acquire(); +} + +void SAL_CALL SwXTextDocument::release()throw() +{ + SfxBaseModel::release(); +} + +Reference< XAdapter > SwXTextDocument::queryAdapter( ) +{ + return SfxBaseModel::queryAdapter(); +} + +Sequence< uno::Type > SAL_CALL SwXTextDocument::getTypes() +{ + Sequence< uno::Type > aNumTypes; + GetNumberFormatter(); + if(xNumFormatAgg.is()) + { + const uno::Type& rProvType = cppu::UnoType<XTypeProvider>::get(); + Any aNumProv = xNumFormatAgg->queryAggregation(rProvType); + Reference<XTypeProvider> xNumProv; + if(aNumProv >>= xNumProv) + { + aNumTypes = xNumProv->getTypes(); + } + } + return comphelper::concatSequences( + SfxBaseModel::getTypes(), + SwXTextDocumentBaseClass::getTypes(), + aNumTypes, + Sequence { + cppu::UnoType<lang::XMultiServiceFactory>::get(), + cppu::UnoType<tiledrendering::XTiledRenderable>::get()}); +} + +SwXTextDocument::SwXTextDocument(SwDocShell* pShell) + : SfxBaseModel(pShell) + , m_pImpl(new Impl) + , + pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_DOCUMENT)), + + pDocShell(pShell), + + bObjectValid(pShell != nullptr), + + pDrawPage(nullptr), + mxXDrawPage(), + pBodyText(nullptr), + mxXNumberingRules(), + mxXFootnotes(), + mxXFootnoteSettings(), + mxXEndnotes(), + mxXEndnoteSettings(), + mxXReferenceMarks(), + mxXTextFieldTypes(), + mxXTextFieldMasters(), + mxXTextSections(), + mxXBookmarks(), + mxXTextTables(), + mxXTextFrames(), + mxXGraphicObjects(), + mxXEmbeddedObjects(), + mxXStyleFamilies(), + mxXAutoStyles(), + mxXChapterNumbering(), + mxXDocumentIndexes(), + + mxXLineNumberingProperties(), + mxLinkTargetSupplier(), + mxXRedlines(), + m_pHiddenViewFrame(nullptr), + // #i117783# + bApplyPagePrintSettingsFromXPagePrintable( false ) +{ +} + +SdrModel& SwXTextDocument::getSdrModelFromUnoModel() const +{ + OSL_ENSURE(pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetOrCreateDrawModel(), "No SdrModel in SwDoc, should not happen"); + return *pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); +} + +SwXTextDocument::~SwXTextDocument() +{ + InitNewDoc(); + if(xNumFormatAgg.is()) + { + Reference< XInterface > x0; + xNumFormatAgg->setDelegator(x0); + xNumFormatAgg = nullptr; + } + m_pPrintUIOptions.reset(); + if (m_pRenderData && m_pRenderData->IsViewOptionAdjust()) + { // rhbz#827695: this can happen if the last page is not printed + // the SwViewShell has been deleted already by SwView::~SwView + // FIXME: replace this awful implementation of XRenderable with + // something less insane that has its own view + m_pRenderData->ViewOptionAdjustCrashPreventionKludge(); + } + m_pRenderData.reset(); +} + +SwXDocumentPropertyHelper * SwXTextDocument::GetPropertyHelper () +{ + if(!mxPropertyHelper.is()) + { + mxPropertyHelper = new SwXDocumentPropertyHelper(*pDocShell->GetDoc()); + } + return mxPropertyHelper.get(); +} + +void SwXTextDocument::GetNumberFormatter() +{ + if(IsValid()) + { + if(!xNumFormatAgg.is()) + { + if ( pDocShell->GetDoc() ) + { + SvNumberFormatsSupplierObj* pNumFormat = new SvNumberFormatsSupplierObj( + pDocShell->GetDoc()->GetNumberFormatter()); + Reference< util::XNumberFormatsSupplier > xTmp = pNumFormat; + xNumFormatAgg.set(xTmp, UNO_QUERY); + } + if(xNumFormatAgg.is()) + xNumFormatAgg->setDelegator(static_cast<cppu::OWeakObject*>(static_cast<SwXTextDocumentBaseClass*>(this))); + } + else + { + const uno::Type& rTunnelType = cppu::UnoType<XUnoTunnel>::get(); + Any aNumTunnel = xNumFormatAgg->queryAggregation(rTunnelType); + SvNumberFormatsSupplierObj* pNumFormat = nullptr; + Reference< XUnoTunnel > xNumTunnel; + if(aNumTunnel >>= xNumTunnel) + { + pNumFormat = reinterpret_cast<SvNumberFormatsSupplierObj*>( + xNumTunnel->getSomething(SvNumberFormatsSupplierObj::getUnoTunnelId())); + + } + OSL_ENSURE(pNumFormat, "No number formatter available"); + if (pNumFormat && !pNumFormat->GetNumberFormatter()) + pNumFormat->SetNumberFormatter(pDocShell->GetDoc()->GetNumberFormatter()); + } + } +} + +Reference< XText > SwXTextDocument::getText() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!xBodyText.is()) + { + pBodyText = new SwXBodyText(pDocShell->GetDoc()); + xBodyText = pBodyText; + } + return xBodyText; +} + +void SwXTextDocument::reformat() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); +} + +void SwXTextDocument::lockControllers() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + maActionArr.emplace_front(new UnoActionContext(pDocShell->GetDoc())); +} + +void SwXTextDocument::unlockControllers() +{ + SolarMutexGuard aGuard; + if(maActionArr.empty()) + throw RuntimeException("Nothing to unlock"); + + maActionArr.pop_front(); +} + +sal_Bool SwXTextDocument::hasControllersLocked() +{ + SolarMutexGuard aGuard; + return !maActionArr.empty(); +} + +Reference< frame::XController > SwXTextDocument::getCurrentController() +{ + return SfxBaseModel::getCurrentController(); +} + +void SwXTextDocument::setCurrentController(const Reference< frame::XController > & xController) +{ + SfxBaseModel::setCurrentController(xController); +} + +Reference< XInterface > SwXTextDocument::getCurrentSelection() +{ + SolarMutexGuard aGuard; + Reference< XInterface > xRef; + if(IsValid()) + { + SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>)); + while(pView && pView->GetObjectShell() != pDocShell) + { + pView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>)); + } + if(pView) + { + Any aRef = pView->GetUNOObject()->getSelection(); + aRef >>= xRef; + } + } + return xRef; +} + +sal_Bool SwXTextDocument::attachResource(const OUString& aURL, const Sequence< beans::PropertyValue >& aArgs) +{ + return SfxBaseModel::attachResource(aURL, aArgs); +} + +OUString SwXTextDocument::getURL() +{ + return SfxBaseModel::getURL(); +} + +Sequence< beans::PropertyValue > SwXTextDocument::getArgs() +{ + return SfxBaseModel::getArgs(); +} + +void SwXTextDocument::connectController(const Reference< frame::XController > & xController) +{ + SfxBaseModel::connectController(xController); +} + +void SwXTextDocument::disconnectController(const Reference< frame::XController > & xController) +{ + SfxBaseModel::disconnectController(xController); +} + +void SwXTextDocument::dispose() +{ + SfxBaseModel::dispose(); +} + +void SwXTextDocument::close( sal_Bool bDeliverOwnership ) +{ + if(pDocShell) + { + uno::Sequence< uno::Any > aArgs; + pDocShell->CallAutomationDocumentEventSinks( "Close", aArgs ); + } + SolarMutexGuard aGuard; + if(IsValid() && m_pHiddenViewFrame) + lcl_DisposeView( m_pHiddenViewFrame, pDocShell); + SfxBaseModel::close(bDeliverOwnership); +} + +void SwXTextDocument::addEventListener(const Reference< lang::XEventListener > & aListener) +{ + SfxBaseModel::addEventListener(aListener); +} + +void SwXTextDocument::removeEventListener(const Reference< lang::XEventListener > & aListener) +{ + SfxBaseModel::removeEventListener(aListener); +} + +Reference< XPropertySet > SwXTextDocument::getLineNumberingProperties() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + if(!mxXLineNumberingProperties.is()) + { + mxXLineNumberingProperties = new SwXLineNumberingProperties(pDocShell->GetDoc()); + } + return mxXLineNumberingProperties; +} + +Reference< XIndexReplace > SwXTextDocument::getChapterNumberingRules() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXChapterNumbering.is()) + { + mxXChapterNumbering = new SwXChapterNumbering(*pDocShell); + } + return mxXChapterNumbering; +} + +Reference< XIndexAccess > SwXTextDocument::getNumberingRules() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXNumberingRules.is() ) + { + mxXNumberingRules = new SwXNumberingRulesCollection( pDocShell->GetDoc() ); + } + return mxXNumberingRules; +} + +Reference< XIndexAccess > SwXTextDocument::getFootnotes() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXFootnotes.is()) + { + mxXFootnotes = new SwXFootnotes(false, pDocShell->GetDoc()); + } + return mxXFootnotes; +} + +Reference< XPropertySet > SAL_CALL + SwXTextDocument::getFootnoteSettings() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXFootnoteSettings.is()) + { + mxXFootnoteSettings = new SwXFootnoteProperties(pDocShell->GetDoc()); + } + return mxXFootnoteSettings; +} + +Reference< XIndexAccess > SwXTextDocument::getEndnotes() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXEndnotes.is()) + { + mxXEndnotes = new SwXFootnotes(true, pDocShell->GetDoc()); + } + return mxXEndnotes; +} + +Reference< XPropertySet > SwXTextDocument::getEndnoteSettings() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXEndnoteSettings.is()) + { + mxXEndnoteSettings = new SwXEndnoteProperties(pDocShell->GetDoc()); + } + return mxXEndnoteSettings; +} + +Reference< util::XReplaceDescriptor > SwXTextDocument::createReplaceDescriptor() +{ + SolarMutexGuard aGuard; + Reference< util::XReplaceDescriptor > xRet = new SwXTextSearch; + return xRet; +} + +SwUnoCursor* SwXTextDocument::CreateCursorForSearch(Reference< XTextCursor > & xCursor) +{ + getText(); + XText *const pText = xBodyText.get(); + SwXBodyText* pBText = static_cast<SwXBodyText*>(pText); + SwXTextCursor *const pXTextCursor = pBText->CreateTextCursor(true); + xCursor.set( static_cast<text::XWordCursor*>(pXTextCursor) ); + + auto& rUnoCursor(pXTextCursor->GetCursor()); + rUnoCursor.SetRemainInSection(false); + return &rUnoCursor; +} + +sal_Int32 SwXTextDocument::replaceAll(const Reference< util::XSearchDescriptor > & xDesc) +{ + SolarMutexGuard aGuard; + Reference< XUnoTunnel > xDescTunnel(xDesc, UNO_QUERY_THROW); + if(!IsValid() || !xDescTunnel->getSomething(SwXTextSearch::getUnoTunnelId())) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + Reference< XTextCursor > xCursor; + auto pUnoCursor(CreateCursorForSearch(xCursor)); + + const SwXTextSearch* pSearch = reinterpret_cast<const SwXTextSearch*>( + xDescTunnel->getSomething(SwXTextSearch::getUnoTunnelId())); + + int eRanges(FindRanges::InBody|FindRanges::InSelAll); + + i18nutil::SearchOptions2 aSearchOpt; + pSearch->FillSearchOptions( aSearchOpt ); + + SwDocPositions eStart = pSearch->m_bBack ? SwDocPositions::End : SwDocPositions::Start; + SwDocPositions eEnd = pSearch->m_bBack ? SwDocPositions::Start : SwDocPositions::End; + + // Search should take place anywhere + pUnoCursor->SetRemainInSection(false); + sal_uInt32 nResult; + UnoActionContext aContext(pDocShell->GetDoc()); + //try attribute search first + if(pSearch->HasSearchAttributes()||pSearch->HasReplaceAttributes()) + { + SfxItemSet aSearch(pDocShell->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>{}); + SfxItemSet aReplace(pDocShell->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>{}); + pSearch->FillSearchItemSet(aSearch); + pSearch->FillReplaceItemSet(aReplace); + bool bCancel; + nResult = static_cast<sal_Int32>(pUnoCursor->FindAttrs(aSearch, !pSearch->m_bStyles, + eStart, eEnd, bCancel, + static_cast<FindRanges>(eRanges), + !pSearch->m_sSearchText.isEmpty() ? &aSearchOpt : nullptr, + &aReplace )); + } + else if(pSearch->m_bStyles) + { + SwTextFormatColl *pSearchColl = lcl_GetParaStyle(pSearch->m_sSearchText, pUnoCursor->GetDoc()); + SwTextFormatColl *pReplaceColl = lcl_GetParaStyle(pSearch->m_sReplaceText, pUnoCursor->GetDoc()); + + bool bCancel; + nResult = pUnoCursor->FindFormat(*pSearchColl, + eStart, eEnd, bCancel, + static_cast<FindRanges>(eRanges), pReplaceColl ); + + } + else + { + //todo/mba: assuming that notes should be omitted + bool bCancel; + nResult = pUnoCursor->Find_Text(aSearchOpt, false/*bSearchInNotes*/, + eStart, eEnd, bCancel, + static_cast<FindRanges>(eRanges), + true ); + } + return static_cast<sal_Int32>(nResult); + +} + +Reference< util::XSearchDescriptor > SwXTextDocument::createSearchDescriptor() +{ + SolarMutexGuard aGuard; + Reference< util::XSearchDescriptor > xRet = new SwXTextSearch; + return xRet; + +} + +// Used for findAll/First/Next + +SwUnoCursor* SwXTextDocument::FindAny(const Reference< util::XSearchDescriptor > & xDesc, + Reference< XTextCursor > & xCursor, + bool bAll, + sal_Int32& nResult, + Reference< XInterface > const & xLastResult) +{ + const auto pSearch = comphelper::getUnoTunnelImplementation<SwXTextSearch>(xDesc); + if(!IsValid() || !pSearch) + return nullptr; + + auto pUnoCursor(CreateCursorForSearch(xCursor)); + + bool bParentInExtra = false; + if(xLastResult.is()) + { + Reference<XUnoTunnel> xCursorTunnel( xLastResult, UNO_QUERY); + OTextCursorHelper* pPosCursor = nullptr; + if(xCursorTunnel.is()) + { + pPosCursor = reinterpret_cast<OTextCursorHelper*>(xCursorTunnel->getSomething( + OTextCursorHelper::getUnoTunnelId())); + } + SwPaM* pCursor = pPosCursor ? pPosCursor->GetPaM() : nullptr; + if(pCursor) + { + *pUnoCursor->GetPoint() = *pCursor->End(); + pUnoCursor->DeleteMark(); + } + else + { + SwXTextRange* pRange = nullptr; + if(xCursorTunnel.is()) + { + pRange = reinterpret_cast<SwXTextRange*>(xCursorTunnel->getSomething( + SwXTextRange::getUnoTunnelId())); + } + if(!pRange) + return nullptr; + pRange->GetPositions(*pUnoCursor); + if(pUnoCursor->HasMark()) + { + if(*pUnoCursor->GetPoint() < *pUnoCursor->GetMark()) + pUnoCursor->Exchange(); + pUnoCursor->DeleteMark(); + } + } + const SwNode& rRangeNode = pUnoCursor->GetNode(); + bParentInExtra = rRangeNode.FindFlyStartNode() || + rRangeNode.FindFootnoteStartNode() || + rRangeNode.FindHeaderStartNode() || + rRangeNode.FindFooterStartNode() ; + } + + i18nutil::SearchOptions2 aSearchOpt; + pSearch->FillSearchOptions( aSearchOpt ); + +/** + * The following combinations are allowed: + * - Search in the body: -> FindRanges::InBody + * - Search all in the body: -> FindRanges::InBodyOnly | FindRanges::InSelAll + * - Search in selections: one / all -> FindRanges::InSel [ | FindRanges::InSelAll ] + * - Search outside the body: one / all -> FindRanges::InOther [ | FindRanges::InSelAll ] + * - Search everywhere all: -> FindRanges::InSelAll + */ + FindRanges eRanges(FindRanges::InBody); + if(bParentInExtra) + eRanges = FindRanges::InOther; + if(bAll) //always - everywhere? + eRanges = FindRanges::InSelAll; + SwDocPositions eStart = !bAll ? SwDocPositions::Curr : pSearch->m_bBack ? SwDocPositions::End : SwDocPositions::Start; + SwDocPositions eEnd = pSearch->m_bBack ? SwDocPositions::Start : SwDocPositions::End; + + nResult = 0; + for (int nSearchProc = 0; nSearchProc < 2; ++nSearchProc) + { + //try attribute search first + if(pSearch->HasSearchAttributes()) + { + SfxItemSet aSearch( + pDocShell->GetDoc()->GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT, + RES_PARATR_BEGIN, RES_PARATR_END - 1, + RES_FRMATR_BEGIN, RES_FRMATR_END - 1>{}); + pSearch->FillSearchItemSet(aSearch); + bool bCancel; + nResult = static_cast<sal_Int32>(pUnoCursor->FindAttrs(aSearch, !pSearch->m_bStyles, + eStart, eEnd, bCancel, + eRanges, + !pSearch->m_sSearchText.isEmpty() ? &aSearchOpt : nullptr )); + } + else if(pSearch->m_bStyles) + { + SwTextFormatColl *pSearchColl = lcl_GetParaStyle(pSearch->m_sSearchText, pUnoCursor->GetDoc()); + //pSearch->sReplaceText + SwTextFormatColl *pReplaceColl = nullptr; + bool bCancel; + nResult = static_cast<sal_Int32>(pUnoCursor->FindFormat(*pSearchColl, + eStart, eEnd, bCancel, + eRanges, pReplaceColl )); + } + else + { + //todo/mba: assuming that notes should be omitted + bool bCancel; + nResult = static_cast<sal_Int32>(pUnoCursor->Find_Text(aSearchOpt, false/*bSearchInNotes*/, + eStart, eEnd, bCancel, + eRanges )); + } + if(nResult || (eRanges&(FindRanges::InSelAll|FindRanges::InOther))) + break; + //second step - find in other + eRanges = FindRanges::InOther; + } + return pUnoCursor; +} + +Reference< XIndexAccess > + SwXTextDocument::findAll(const Reference< util::XSearchDescriptor > & xDesc) +{ + SolarMutexGuard aGuard; + Reference< XInterface > xTmp; + sal_Int32 nResult = 0; + Reference< XTextCursor > xCursor; + auto pResultCursor(FindAny(xDesc, xCursor, true, nResult, xTmp)); + if(!pResultCursor) + throw RuntimeException("No result cursor"); + Reference< XIndexAccess > xRet = SwXTextRanges::Create( nResult ? &(*pResultCursor) : nullptr ); + return xRet; +} + +Reference< XInterface > SwXTextDocument::findFirst(const Reference< util::XSearchDescriptor > & xDesc) +{ + SolarMutexGuard aGuard; + Reference< XInterface > xTmp; + sal_Int32 nResult = 0; + Reference< XTextCursor > xCursor; + auto pResultCursor(FindAny(xDesc, xCursor, false, nResult, xTmp)); + if(!pResultCursor) + throw RuntimeException("No result cursor"); + Reference< XInterface > xRet; + if(nResult) + { + const uno::Reference< text::XText > xParent = + ::sw::CreateParentXText(*pDocShell->GetDoc(), + *pResultCursor->GetPoint()); + xRet = *new SwXTextCursor(xParent, *pResultCursor); + } + return xRet; +} + +Reference< XInterface > SwXTextDocument::findNext(const Reference< XInterface > & xStartAt, + const Reference< util::XSearchDescriptor > & xDesc) +{ + SolarMutexGuard aGuard; + sal_Int32 nResult = 0; + Reference< XTextCursor > xCursor; + if(!xStartAt.is()) + throw RuntimeException("xStartAt missing"); + auto pResultCursor(FindAny(xDesc, xCursor, false, nResult, xStartAt)); + if(!pResultCursor) + throw RuntimeException("No result cursor"); + Reference< XInterface > xRet; + if(nResult) + { + const uno::Reference< text::XText > xParent = + ::sw::CreateParentXText(*pDocShell->GetDoc(), + *pResultCursor->GetPoint()); + + xRet = *new SwXTextCursor(xParent, *pResultCursor); + } + return xRet; +} + +Sequence< beans::PropertyValue > SwXTextDocument::getPagePrintSettings() +{ + SolarMutexGuard aGuard; + Sequence< beans::PropertyValue > aSeq(9); + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + beans::PropertyValue* pArray = aSeq.getArray(); + SwPagePreviewPrtData aData; + const SwPagePreviewPrtData* pData = pDocShell->GetDoc()->GetPreviewPrtData(); + if(pData) + aData = *pData; + Any aVal; + aVal <<= static_cast<sal_Int16>(aData.GetRow()); + pArray[0] = beans::PropertyValue("PageRows", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int16>(aData.GetCol()); + pArray[1] = beans::PropertyValue("PageColumns", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetLeftSpace())); + pArray[2] = beans::PropertyValue("LeftMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetRightSpace())); + pArray[3] = beans::PropertyValue("RightMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetTopSpace())); + pArray[4] = beans::PropertyValue("TopMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetBottomSpace())); + pArray[5] = beans::PropertyValue("BottomMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetHorzSpace())); + pArray[6] = beans::PropertyValue("HoriMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= static_cast<sal_Int32>(convertTwipToMm100(aData.GetVertSpace())); + pArray[7] = beans::PropertyValue("VertMargin", -1, aVal, PropertyState_DIRECT_VALUE); + aVal <<= aData.GetLandscape(); + pArray[8] = beans::PropertyValue("IsLandscape", -1, aVal, PropertyState_DIRECT_VALUE); + + return aSeq; +} + +static sal_uInt32 lcl_Any_To_ULONG(const Any& rValue, bool& bException) +{ + bException = false; + TypeClass eType = rValue.getValueType().getTypeClass(); + + sal_uInt32 nRet = 0; + if( eType == TypeClass_UNSIGNED_LONG ) + rValue >>= nRet; + else + { + sal_Int32 nVal=0; + bException = !(rValue >>= nVal); + if( !bException ) + nRet = static_cast<sal_uInt32>(nVal); + } + + return nRet; +} + +static OUString lcl_CreateOutlineString( size_t nIndex, + const SwOutlineNodes& rOutlineNodes, const SwNumRule* pOutlRule) +{ + OUStringBuffer sEntry; + const SwTextNode * pTextNd = rOutlineNodes[ nIndex ]->GetTextNode(); + SwNumberTree::tNumberVector aNumVector = pTextNd->GetNumberVector(); + if( pOutlRule && pTextNd->GetNumRule()) + for( int nLevel = 0; + nLevel <= pTextNd->GetActualListLevel(); + nLevel++ ) + { + long nVal = aNumVector[nLevel]; + nVal ++; + nVal -= pOutlRule->Get(nLevel).GetStart(); + sEntry.append(OUString::number( nVal )); + sEntry.append("."); + } + sEntry.append( rOutlineNodes[ nIndex ]-> + GetTextNode()->GetExpandText(nullptr) ); + return sEntry.makeStringAndClear(); +} + +void SwXTextDocument::setPagePrintSettings(const Sequence< beans::PropertyValue >& aSettings) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + SwPagePreviewPrtData aData; + //if only a few properties are coming, then use the current settings + const SwPagePreviewPrtData* pData = pDocShell->GetDoc()->GetPreviewPrtData(); + if(pData) + aData = *pData; + for(const beans::PropertyValue& rProperty : aSettings) + { + OUString sName = rProperty.Name; + const Any& rVal = rProperty.Value; + bool bException; + sal_uInt32 nVal = lcl_Any_To_ULONG(rVal, bException); + if( sName == "PageRows" ) + { + if(!nVal || nVal > 0xff) + throw RuntimeException("Invalid value"); + aData.SetRow(static_cast<sal_uInt8>(nVal)); + } + else if(sName == "PageColumns") + { + if(!nVal || nVal > 0xff) + throw RuntimeException("Invalid value"); + aData.SetCol(static_cast<sal_uInt8>(nVal)); + } + else if(sName == "LeftMargin") + { + aData.SetLeftSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "RightMargin") + { + aData.SetRightSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "TopMargin") + { + aData.SetTopSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "BottomMargin") + { + aData.SetBottomSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "HoriMargin") + { + aData.SetHorzSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "VertMargin") + { + aData.SetVertSpace(convertMm100ToTwip(nVal)); + } + else if(sName == "IsLandscape") + { + auto b = o3tl::tryAccess<bool>(rVal); + bException = !b; + if (b) + { + aData.SetLandscape(*b); + } + } + else + bException = true; + if(bException) + throw RuntimeException(); + } + pDocShell->GetDoc()->SetPreviewPrtData(&aData); + +} + +void SwXTextDocument::printPages(const Sequence< beans::PropertyValue >& xOptions) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + SfxViewFrame* pFrame = SfxViewFrame::LoadHiddenDocument( *pDocShell, SfxInterfaceId(7) ); + SfxRequest aReq(FN_PRINT_PAGEPREVIEW, SfxCallMode::SYNCHRON, + pDocShell->GetDoc()->GetAttrPool()); + aReq.AppendItem(SfxBoolItem(FN_PRINT_PAGEPREVIEW, true)); + + for ( const beans::PropertyValue &rProp : xOptions ) + { + // get Property-Value from options + Any aValue( rProp.Value ); + + // FileName-Property? + if ( rProp.Name == UNO_NAME_FILE_NAME ) + { + OUString sFileURL; + if ( rProp.Value >>= sFileURL ) + { + // Convert the File URL into a system dependent path, as the SalPrinter expects + OUString sSystemPath; + FileBase::getSystemPathFromFileURL ( sFileURL, sSystemPath ); + aReq.AppendItem(SfxStringItem( SID_FILE_NAME, sSystemPath ) ); + } + else if ( rProp.Value.getValueType() != cppu::UnoType<void>::get() ) + throw IllegalArgumentException(); + } + + // CopyCount-Property + else if ( rProp.Name == UNO_NAME_COPY_COUNT ) + { + sal_Int32 nCopies = 0; + aValue >>= nCopies; + aReq.AppendItem(SfxInt16Item( SID_PRINT_COPIES, static_cast<sal_Int16>(nCopies) ) ); + } + + // Collate-Property + else if ( rProp.Name == UNO_NAME_COLLATE ) + { + auto b = o3tl::tryAccess<bool>(rProp.Value); + if ( !b ) + throw IllegalArgumentException(); + aReq.AppendItem(SfxBoolItem( SID_PRINT_COLLATE, *b ) ); + + } + + // Sort-Property + else if ( rProp.Name == UNO_NAME_SORT ) + { + auto b = o3tl::tryAccess<bool>(rProp.Value); + if ( !b ) + throw IllegalArgumentException(); + + aReq.AppendItem(SfxBoolItem( SID_PRINT_SORT, *b ) ); + } + + // Pages-Property + else if ( rProp.Name == UNO_NAME_PAGES ) + { + OUString sTmp; + if ( !(rProp.Value >>= sTmp) ) + throw IllegalArgumentException(); + + aReq.AppendItem( SfxStringItem( SID_PRINT_PAGES, sTmp ) ); + + } + } + + // #i117783# + bApplyPagePrintSettingsFromXPagePrintable = true; + pFrame->GetViewShell()->ExecuteSlot(aReq); + // Frame close + pFrame->DoClose(); + +} + +Reference< XNameAccess > SwXTextDocument::getReferenceMarks() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXReferenceMarks.is()) + { + mxXReferenceMarks = new SwXReferenceMarks(pDocShell->GetDoc()); + } + return mxXReferenceMarks; +} + +Reference< XEnumerationAccess > SwXTextDocument::getTextFields() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXTextFieldTypes.is()) + { + mxXTextFieldTypes = new SwXTextFieldTypes(pDocShell->GetDoc()); + } + return mxXTextFieldTypes; +} + +Reference< XNameAccess > SwXTextDocument::getTextFieldMasters() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXTextFieldMasters.is()) + { + mxXTextFieldMasters = new SwXTextFieldMasters(pDocShell->GetDoc()); + } + return mxXTextFieldMasters; +} + +Reference< XNameAccess > SwXTextDocument::getEmbeddedObjects() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXEmbeddedObjects.is()) + { + mxXEmbeddedObjects = new SwXTextEmbeddedObjects(pDocShell->GetDoc()); + } + return mxXEmbeddedObjects; +} + +Reference< XNameAccess > SwXTextDocument::getBookmarks() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXBookmarks.is()) + { + mxXBookmarks = new SwXBookmarks(pDocShell->GetDoc()); + } + return mxXBookmarks; +} + +Reference< XNameAccess > SwXTextDocument::getTextSections() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXTextSections.is()) + { + mxXTextSections = new SwXTextSections(pDocShell->GetDoc()); + } + return mxXTextSections; +} + +Reference< XNameAccess > SwXTextDocument::getTextTables() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXTextTables.is()) + { + mxXTextTables = new SwXTextTables(pDocShell->GetDoc()); + } + return mxXTextTables; +} + +Reference< XNameAccess > SwXTextDocument::getGraphicObjects() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXGraphicObjects.is()) + { + mxXGraphicObjects = new SwXTextGraphicObjects(pDocShell->GetDoc()); + } + return mxXGraphicObjects; +} + +Reference< XNameAccess > SwXTextDocument::getTextFrames() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXTextFrames.is()) + { + mxXTextFrames = new SwXTextFrames(pDocShell->GetDoc()); + } + return mxXTextFrames; +} + +Reference< XNameAccess > SwXTextDocument::getStyleFamilies() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXStyleFamilies.is()) + { + mxXStyleFamilies = new SwXStyleFamilies(*pDocShell); + } + return mxXStyleFamilies; +} + +uno::Reference< style::XAutoStyles > SwXTextDocument::getAutoStyles( ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXAutoStyles.is()) + { + mxXAutoStyles = new SwXAutoStyles(*pDocShell); + } + return mxXAutoStyles; + +} + +Reference< drawing::XDrawPage > SwXTextDocument::getDrawPage() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXDrawPage.is()) + { + pDrawPage = new SwXDrawPage(pDocShell->GetDoc()); + mxXDrawPage = pDrawPage; + // Create a Reference to trigger the complete initialization of the + // object. Otherwise in some corner cases it would get initialized + // at ::InitNewDoc -> which would get called during + // close() or dispose() -> n#681746 + uno::Reference<lang::XComponent> xTriggerInit( mxXDrawPage, uno::UNO_QUERY ); + } + return mxXDrawPage; +} + +namespace { + +class SwDrawPagesObj : public cppu::WeakImplHelper< + css::drawing::XDrawPages, + css::lang::XServiceInfo> +{ +private: + css::uno::Reference< css::drawing::XDrawPageSupplier > m_xDoc; +public: + SwDrawPagesObj(const css::uno::Reference< css::drawing::XDrawPageSupplier >& rxDoc) : m_xDoc(rxDoc) {} + + // XDrawPages + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL + insertNewByIndex(sal_Int32 /*nIndex*/) override { throw css::lang::NoSupportException(); } + + virtual void SAL_CALL remove(const css::uno::Reference< css::drawing::XDrawPage >& /*xPage*/) override + { + throw css::lang::NoSupportException(); + } + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override { return 1; } + + virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 Index) override + { + if (Index != 0) + throw css::lang::IndexOutOfBoundsException("Writer documents have only one DrawPage!"); + return css::uno::Any(m_xDoc->getDrawPage()); + } + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override + { + SolarMutexGuard aGuard; + return cppu::UnoType<drawing::XDrawPage>::get(); + } + + virtual sal_Bool SAL_CALL hasElements() override { return true; } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { + return "SwDrawPagesObj"; + } + + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.drawing.DrawPages" }; + } +}; + +} + +// XDrawPagesSupplier + +uno::Reference<drawing::XDrawPages> SAL_CALL SwXTextDocument::getDrawPages() +{ + SolarMutexGuard aGuard; + return new SwDrawPagesObj(this); +} + +void SwXTextDocument::Invalidate() +{ + bObjectValid = false; + if(xNumFormatAgg.is()) + { + const uno::Type& rTunnelType = cppu::UnoType<XUnoTunnel>::get(); + Any aNumTunnel = xNumFormatAgg->queryAggregation(rTunnelType); + SvNumberFormatsSupplierObj* pNumFormat = nullptr; + Reference< XUnoTunnel > xNumTunnel; + if(aNumTunnel >>= xNumTunnel) + { + pNumFormat = reinterpret_cast<SvNumberFormatsSupplierObj*>( + xNumTunnel->getSomething(SvNumberFormatsSupplierObj::getUnoTunnelId())); + pNumFormat->SetNumberFormatter(nullptr); + } + OSL_ENSURE(pNumFormat, "No number formatter available"); + } + InitNewDoc(); + pDocShell = nullptr; + lang::EventObject const ev(static_cast<SwXTextDocumentBaseClass &>(*this)); + m_pImpl->m_RefreshListeners.disposeAndClear(ev); +} + +void SwXTextDocument::Reactivate(SwDocShell* pNewDocShell) +{ + if(pDocShell && pDocShell != pNewDocShell) + Invalidate(); + pDocShell = pNewDocShell; + bObjectValid = true; +} + +void SwXTextDocument::InitNewDoc() +{ + // first invalidate all collections, then delete references and Set to zero + if(mxXTextTables.is()) + { + XNameAccess* pTables = mxXTextTables.get(); + static_cast<SwXTextTables*>(pTables)->Invalidate(); + mxXTextTables.clear(); + } + + if(mxXTextFrames.is()) + { + XNameAccess* pFrames = mxXTextFrames.get(); + static_cast<SwXTextFrames*>(pFrames)->Invalidate(); + mxXTextFrames.clear(); + } + + if(mxXGraphicObjects.is()) + { + XNameAccess* pFrames = mxXGraphicObjects.get(); + static_cast<SwXTextGraphicObjects*>(pFrames)->Invalidate(); + mxXGraphicObjects.clear(); + } + + if(mxXEmbeddedObjects.is()) + { + XNameAccess* pOLE = mxXEmbeddedObjects.get(); + static_cast<SwXTextEmbeddedObjects*>(pOLE)->Invalidate(); + mxXEmbeddedObjects.clear(); + } + + if(xBodyText.is()) + { + xBodyText = nullptr; + pBodyText = nullptr; + } + + if(xNumFormatAgg.is()) + { + const uno::Type& rTunnelType = cppu::UnoType<XUnoTunnel>::get(); + Any aNumTunnel = xNumFormatAgg->queryAggregation(rTunnelType); + SvNumberFormatsSupplierObj* pNumFormat = nullptr; + Reference< XUnoTunnel > xNumTunnel; + if(aNumTunnel >>= xNumTunnel) + { + pNumFormat = reinterpret_cast<SvNumberFormatsSupplierObj*>( + xNumTunnel->getSomething(SvNumberFormatsSupplierObj::getUnoTunnelId())); + + } + OSL_ENSURE(pNumFormat, "No number formatter available"); + if (pNumFormat) + pNumFormat->SetNumberFormatter(nullptr); + } + + if(mxXTextFieldTypes.is()) + { + XEnumerationAccess* pT = mxXTextFieldTypes.get(); + static_cast<SwXTextFieldTypes*>(pT)->Invalidate(); + mxXTextFieldTypes.clear(); + } + + if(mxXTextFieldMasters.is()) + { + XNameAccess* pT = mxXTextFieldMasters.get(); + static_cast<SwXTextFieldMasters*>(pT)->Invalidate(); + mxXTextFieldMasters.clear(); + } + + if(mxXTextSections.is()) + { + XNameAccess* pSect = mxXTextSections.get(); + static_cast<SwXTextSections*>(pSect)->Invalidate(); + mxXTextSections.clear(); + } + + if(mxXDrawPage.is()) + { + // #i91798#, #i91895# + // dispose XDrawPage here. We are the owner and know that it is no longer in a valid condition. + uno::Reference<lang::XComponent> xComp( mxXDrawPage, uno::UNO_QUERY ); + xComp->dispose(); + pDrawPage->InvalidateSwDoc(); + mxXDrawPage.clear(); + } + + if ( mxXNumberingRules.is() ) + { + XIndexAccess* pNum = mxXNumberingRules.get(); + static_cast<SwXNumberingRulesCollection*>(pNum)->Invalidate(); + mxXNumberingRules.clear(); + } + + if(mxXFootnotes.is()) + { + XIndexAccess* pFootnote = mxXFootnotes.get(); + static_cast<SwXFootnotes*>(pFootnote)->Invalidate(); + mxXFootnotes.clear(); + } + + if(mxXEndnotes.is()) + { + XIndexAccess* pFootnote = mxXEndnotes.get(); + static_cast<SwXFootnotes*>(pFootnote)->Invalidate(); + mxXEndnotes.clear(); + } + + if(mxXDocumentIndexes.is()) + { + XIndexAccess* pIdxs = mxXDocumentIndexes.get(); + static_cast<SwXDocumentIndexes*>(pIdxs)->Invalidate(); + mxXDocumentIndexes.clear(); + } + + if(mxXStyleFamilies.is()) + { + XNameAccess* pStyles = mxXStyleFamilies.get(); + static_cast<SwXStyleFamilies*>(pStyles)->Invalidate(); + mxXStyleFamilies.clear(); + } + if(mxXAutoStyles.is()) + { + XNameAccess* pStyles = mxXAutoStyles.get(); + static_cast<SwXAutoStyles*>(pStyles)->Invalidate(); + mxXAutoStyles.clear(); + } + + if(mxXBookmarks.is()) + { + XNameAccess* pBm = mxXBookmarks.get(); + static_cast<SwXBookmarks*>(pBm)->Invalidate(); + mxXBookmarks.clear(); + } + + if(mxXChapterNumbering.is()) + { + XIndexReplace* pCh = mxXChapterNumbering.get(); + static_cast<SwXChapterNumbering*>(pCh)->Invalidate(); + mxXChapterNumbering.clear(); + } + + if(mxXFootnoteSettings.is()) + { + XPropertySet* pFntSet = mxXFootnoteSettings.get(); + static_cast<SwXFootnoteProperties*>(pFntSet)->Invalidate(); + mxXFootnoteSettings.clear(); + } + + if(mxXEndnoteSettings.is()) + { + XPropertySet* pEndSet = mxXEndnoteSettings.get(); + static_cast<SwXEndnoteProperties*>(pEndSet)->Invalidate(); + mxXEndnoteSettings.clear(); + } + + if(mxXLineNumberingProperties.is()) + { + XPropertySet* pLine = mxXLineNumberingProperties.get(); + static_cast<SwXLineNumberingProperties*>(pLine)->Invalidate(); + mxXLineNumberingProperties.clear(); + } + if(mxXReferenceMarks.is()) + { + XNameAccess* pMarks = mxXReferenceMarks.get(); + static_cast<SwXReferenceMarks*>(pMarks)->Invalidate(); + mxXReferenceMarks.clear(); + } + if(mxLinkTargetSupplier.is()) + { + XNameAccess* pAccess = mxLinkTargetSupplier.get(); + static_cast<SwXLinkTargetSupplier*>(pAccess)->Invalidate(); + mxLinkTargetSupplier.clear(); + } + if(mxXRedlines.is()) + { + XEnumerationAccess* pMarks = mxXRedlines.get(); + static_cast<SwXRedlines*>(pMarks)->Invalidate(); + mxXRedlines.clear(); + } + if(mxPropertyHelper.is()) + { + mxPropertyHelper->Invalidate(); + mxPropertyHelper.clear(); + } +} + +css::uno::Reference<css::uno::XInterface> SwXTextDocument::create( + OUString const & rServiceName, + css::uno::Sequence<css::uno::Any> const * arguments) +{ + SolarMutexGuard aGuard; + if (!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SwServiceType nType = SwXServiceProvider::GetProviderType(rServiceName); + if (nType != SwServiceType::Invalid) + { + return SwXServiceProvider::MakeInstance(nType, *pDocShell->GetDoc()); + } + if (rServiceName == "com.sun.star.drawing.DashTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Dash); + } + if (rServiceName == "com.sun.star.drawing.GradientTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Gradient); + } + if (rServiceName == "com.sun.star.drawing.HatchTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Hatch); + } + if (rServiceName == "com.sun.star.drawing.BitmapTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Bitmap); + } + if (rServiceName == "com.sun.star.drawing.TransparencyGradientTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::TransGradient); + } + if (rServiceName == "com.sun.star.drawing.MarkerTable") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Marker); + } + if (rServiceName == "com.sun.star.drawing.Defaults") + { + return GetPropertyHelper()->GetDrawTable(SwCreateDrawTable::Defaults); + } + if (rServiceName == "com.sun.star.document.Settings") + { + return Reference<XInterface>(*new SwXDocumentSettings(this)); + } + if (rServiceName == "com.sun.star.document.ImportEmbeddedObjectResolver") + { + return static_cast<cppu::OWeakObject *>( + new SvXMLEmbeddedObjectHelper( + *pDocShell, SvXMLEmbeddedObjectHelperMode::Read)); + } + if (rServiceName == "com.sun.star.text.DocumentSettings") + { + return Reference<XInterface>(*new SwXDocumentSettings(this)); + } + if (rServiceName == "com.sun.star.chart2.data.DataProvider") + { + return Reference<XInterface>( + dynamic_cast<chart2::data::XDataProvider *>( + pDocShell->getIDocumentChartDataProviderAccess(). + GetChartDataProvider())); + } + if (!rServiceName.startsWith("com.sun.star.") + || rServiceName.endsWith(".OLE2Shape")) + { + // We do not want to insert OLE2 Shapes (e.g., + // "com.sun.star.drawing.OLE2Shape", ...) like this (by creating them + // with the documents factory and adding the shapes to the draw page); + // for inserting OLE objects the proper way is to use + // "com.sun.star.text.TextEmbeddedObject": + throw ServiceNotRegisteredException(); + } + // The XML import is allowed to create instances of + // "com.sun.star.drawing.OLE2Shape"; thus, a temporary service name is + // introduced to make this possible: + OUString aTmpServiceName(rServiceName); + if (rServiceName == "com.sun.star.drawing.temporaryForXMLImportOLE2Shape") + { + aTmpServiceName = "com.sun.star.drawing.OLE2Shape"; + } + Reference<XInterface> xTmp( + arguments == nullptr + ? SvxFmMSFactory::createInstance(aTmpServiceName) + : SvxFmMSFactory::createInstanceWithArguments( + aTmpServiceName, *arguments)); + if (rServiceName == "com.sun.star.drawing.GroupShape" + || rServiceName == "com.sun.star.drawing.Shape3DSceneObject") + { + return *new SwXGroupShape(xTmp, pDocShell->GetDoc()); + } + if (rServiceName.startsWith("com.sun.star.drawing.")) + { + return *new SwXShape(xTmp, pDocShell->GetDoc()); + } + return xTmp; +} + +Reference< XInterface > SwXTextDocument::createInstance(const OUString& rServiceName) +{ + return create(rServiceName, nullptr); +} + +Reference< XInterface > SwXTextDocument::createInstanceWithArguments( + const OUString& ServiceSpecifier, + const Sequence< Any >& Arguments) +{ + return create(ServiceSpecifier, &Arguments); +} + +Sequence< OUString > SwXTextDocument::getAvailableServiceNames() +{ + static Sequence< OUString > aServices; + if ( !aServices.hasElements() ) + { + Sequence< OUString > aRet = SvxFmMSFactory::getAvailableServiceNames(); + auto i = comphelper::findValue(aRet, "com.sun.star.drawing.OLE2Shape"); + if (i != -1) + { + auto nLength = aRet.getLength(); + aRet[i] = aRet[nLength - 1]; + aRet.realloc( nLength - 1 ); + } + Sequence< OUString > aOwn = SwXServiceProvider::GetAllServiceNames(); + aServices = comphelper::concatSequences(aRet, aOwn); + } + + return aServices; +} + +OUString SwXTextDocument::getImplementationName() +{ + return "SwXTextDocument"; + /* // Matching the .component information: + return dynamic_cast<SwGlobalDocShell*>( pDocShell ) != nullptr + ? OUString("com.sun.star.comp.Writer.GlobalDocument") + : dynamic_cast<SwWebDocShell*>( pDocShell ) != nullptr + ? OUString("com.sun.star.comp.Writer.WebDocument") + : OUString("com.sun.star.comp.Writer.TextDocument"); + */ +} + +sal_Bool SwXTextDocument::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXTextDocument::getSupportedServiceNames() +{ + bool bWebDoc = (dynamic_cast<SwWebDocShell*>( pDocShell) != nullptr ); + bool bGlobalDoc = (dynamic_cast<SwGlobalDocShell*>( pDocShell) != nullptr ); + bool bTextDoc = (!bWebDoc && !bGlobalDoc); + + Sequence< OUString > aRet (3); + OUString* pArray = aRet.getArray(); + + pArray[0] = "com.sun.star.document.OfficeDocument"; + pArray[1] = "com.sun.star.text.GenericTextDocument"; + + if (bTextDoc) + pArray[2] = "com.sun.star.text.TextDocument"; + else if (bWebDoc) + pArray[2] = "com.sun.star.text.WebDocument"; + else if (bGlobalDoc) + pArray[2] = "com.sun.star.text.GlobalDocument"; + + return aRet; +} + +Reference< XIndexAccess > SwXTextDocument::getDocumentIndexes() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + if(!mxXDocumentIndexes.is()) + { + mxXDocumentIndexes = new SwXDocumentIndexes(pDocShell->GetDoc()); + } + return mxXDocumentIndexes; +} + +Reference< XPropertySetInfo > SwXTextDocument::getPropertySetInfo() +{ + static Reference< XPropertySetInfo > xRet = pPropSet->getPropertySetInfo(); + return xRet; +} + +void SwXTextDocument::setPropertyValue(const OUString& rPropertyName, const Any& aValue) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + + if(!pEntry) + throw UnknownPropertyException(rPropertyName); + if(pEntry->nFlags & PropertyAttribute::READONLY) + throw PropertyVetoException(); + switch(pEntry->nWID) + { + case WID_DOC_CHAR_COUNT : + case WID_DOC_PARA_COUNT : + case WID_DOC_WORD_COUNT : + throw RuntimeException( + "bad WID", + static_cast< cppu::OWeakObject * >( + static_cast< SwXTextDocumentBaseClass * >(this))); + case WID_DOC_WORD_SEPARATOR : + { + OUString sDelim; + aValue >>= sDelim; + SW_MOD()->GetModuleConfig()->SetWordDelimiter(sDelim); + } + break; + case WID_DOC_CHANGES_RECORD: + case WID_DOC_CHANGES_SHOW: + { + bool bSet = *o3tl::doAccess<bool>(aValue); + RedlineFlags eMode = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + if(WID_DOC_CHANGES_SHOW == pEntry->nWID) + { + eMode |= RedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); + if( !bSet ) + pDocShell->GetDoc()->GetDocumentRedlineManager().SetHideRedlines(true); + } + else if(WID_DOC_CHANGES_RECORD == pEntry->nWID) + { + eMode = bSet ? eMode|RedlineFlags::On : eMode&~RedlineFlags::On; + } + pDocShell->GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eMode ); + } + break; + case WID_DOC_CHANGES_PASSWORD: + { + Sequence <sal_Int8> aNew; + if(aValue >>= aNew) + { + SwDoc* pDoc = pDocShell->GetDoc(); + pDoc->getIDocumentRedlineAccess().SetRedlinePassword(aNew); + if(aNew.hasElements()) + { + RedlineFlags eMode = pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); + eMode |= RedlineFlags::On; + pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eMode ); + } + } + } + break; + case WID_DOC_AUTO_MARK_URL : + { + OUString sURL; + aValue >>= sURL; + pDocShell->GetDoc()->SetTOIAutoMarkURL(sURL); + } + break; + case WID_DOC_HIDE_TIPS : + SW_MOD()->GetModuleConfig()->SetHideFieldTips(*o3tl::doAccess<bool>(aValue)); + break; + case WID_DOC_REDLINE_DISPLAY: + { + RedlineFlags eRedMode = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + eRedMode = eRedMode & (~RedlineFlags::ShowMask); + sal_Int16 nSet = 0; + aValue >>= nSet; + switch(nSet) + { + case RedlineDisplayType::NONE: break; + case RedlineDisplayType::INSERTED: eRedMode |= RedlineFlags::ShowInsert; break; + case RedlineDisplayType::REMOVED: eRedMode |= RedlineFlags::ShowDelete; break; + case RedlineDisplayType:: + INSERTED_AND_REMOVED: eRedMode |= RedlineFlags::ShowInsert|RedlineFlags::ShowDelete; + break; + default: throw IllegalArgumentException(); + } + pDocShell->GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags(eRedMode); + } + break; + case WID_DOC_TWO_DIGIT_YEAR: + { + sal_Int16 nYear = 0; + aValue >>= nYear; + SfxRequest aRequest ( SID_ATTR_YEAR2000, SfxCallMode::SLOT, pDocShell->GetDoc()->GetAttrPool()); + aRequest.AppendItem(SfxUInt16Item( SID_ATTR_YEAR2000, static_cast < sal_uInt16 > ( nYear ) ) ); + pDocShell->Execute ( aRequest ); + } + break; + case WID_DOC_AUTOMATIC_CONTROL_FOCUS: + { + SwDrawModel * pDrawDoc; + bool bAuto = *o3tl::doAccess<bool>(aValue); + + if ( nullptr != ( pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel() ) ) + pDrawDoc->SetAutoControlFocus( bAuto ); + else if (bAuto) + { + // if setting to true, and we don't have an + // SdrModel, then we are changing the default and + // must thus create an SdrModel, if we don't have an + // SdrModel and we are leaving the default at false, + // we don't need to make an SdrModel and can do nothing + // #i52858# - method name changed + pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + pDrawDoc->SetAutoControlFocus ( bAuto ); + } + } + break; + case WID_DOC_APPLY_FORM_DESIGN_MODE: + { + SwDrawModel * pDrawDoc; + bool bMode = *o3tl::doAccess<bool>(aValue); + + if ( nullptr != ( pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel() ) ) + pDrawDoc->SetOpenInDesignMode( bMode ); + else if (!bMode) + { + // if setting to false, and we don't have an + // SdrModel, then we are changing the default and + // must thus create an SdrModel, if we don't have an + // SdrModel and we are leaving the default at true, + // we don't need to make an SdrModel and can do + // nothing + // #i52858# - method name changed + pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetOrCreateDrawModel(); + pDrawDoc->SetOpenInDesignMode ( bMode ); + } + } + break; + // #i42634# New property to set the bInReading + // flag at the document, used during binary import + case WID_DOC_LOCK_UPDATES : + { + SwDoc* pDoc = pDocShell->GetDoc(); + bool bBool (false); + if( aValue >>= bBool ) + { + pDoc->SetInReading( bBool ); + } + } + break; + case WID_DOC_WRITERFILTER: + { + SwDoc* pDoc = pDocShell->GetDoc(); + bool bBool = {}; + if (aValue >>= bBool) + { // HACK: writerfilter has to use API to set this :( + pDoc->SetInWriterfilterImport(bBool); + } + } + break; + case WID_DOC_BUILDID: + aValue >>= maBuildId; + break; + + case WID_DOC_DEFAULT_PAGE_MODE: + { + bool bDefaultPageMode( false ); + aValue >>= bDefaultPageMode; + pDocShell->GetDoc()->SetDefaultPageMode( bDefaultPageMode ); + } + break; + case WID_DOC_INTEROP_GRAB_BAG: + setGrabBagItem(aValue); + break; + + default: + { + const SfxPoolItem& rItem = pDocShell->GetDoc()->GetDefault(pEntry->nWID); + std::unique_ptr<SfxPoolItem> pNewItem(rItem.Clone()); + pNewItem->PutValue(aValue, pEntry->nMemberId); + pDocShell->GetDoc()->SetDefault(*pNewItem); + } + } +} + +Any SwXTextDocument::getPropertyValue(const OUString& rPropertyName) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + + if(!pEntry) + throw UnknownPropertyException(rPropertyName); + Any aAny; + switch(pEntry->nWID) + { + case WID_DOC_ISTEMPLATEID : + aAny <<= pDocShell->IsTemplate(); + break; + case WID_DOC_CHAR_COUNT : + case WID_DOC_PARA_COUNT : + case WID_DOC_WORD_COUNT : + { + const SwDocStat& rStat(pDocShell->GetDoc()->getIDocumentStatistics().GetUpdatedDocStat( false, true )); + sal_Int32 nValue; + switch(pEntry->nWID) + { + case WID_DOC_CHAR_COUNT :nValue = rStat.nChar;break; + case WID_DOC_PARA_COUNT :nValue = rStat.nPara;break; + case WID_DOC_WORD_COUNT :nValue = rStat.nWord;break; + } + aAny <<= nValue; + } + break; + case WID_DOC_WORD_SEPARATOR : + { + aAny <<= SW_MOD()->GetDocStatWordDelim(); + } + break; + case WID_DOC_CHANGES_RECORD: + case WID_DOC_CHANGES_SHOW: + { + const RedlineFlags eMode = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + bool bSet = false; + if(WID_DOC_CHANGES_SHOW == pEntry->nWID) + { + bSet = IDocumentRedlineAccess::IsShowChanges(eMode); + } + else if(WID_DOC_CHANGES_RECORD == pEntry->nWID) + { + bSet = bool(eMode & RedlineFlags::On); + } + aAny <<= bSet; + } + break; + case WID_DOC_CHANGES_PASSWORD: + { + SwDoc* pDoc = pDocShell->GetDoc(); + aAny <<= pDoc->getIDocumentRedlineAccess().GetRedlinePassword(); + } + break; + case WID_DOC_AUTO_MARK_URL : + aAny <<= pDocShell->GetDoc()->GetTOIAutoMarkURL(); + break; + case WID_DOC_HIDE_TIPS : + aAny <<= SW_MOD()->GetModuleConfig()->IsHideFieldTips(); + break; + case WID_DOC_REDLINE_DISPLAY: + { + RedlineFlags eRedMode = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + eRedMode = eRedMode & RedlineFlags::ShowMask; + sal_Int16 nRet = RedlineDisplayType::NONE; + if(RedlineFlags::ShowInsert == eRedMode) + nRet = RedlineDisplayType::INSERTED; + else if(RedlineFlags::ShowDelete == eRedMode) + nRet = RedlineDisplayType::REMOVED; + else if(RedlineFlags::ShowMask == eRedMode) + nRet = RedlineDisplayType::INSERTED_AND_REMOVED; + aAny <<= nRet; + } + break; + case WID_DOC_FORBIDDEN_CHARS: + { + GetPropertyHelper(); + Reference<XForbiddenCharacters> xRet(static_cast<cppu::OWeakObject*>(mxPropertyHelper.get()), UNO_QUERY); + aAny <<= xRet; + } + break; + case WID_DOC_TWO_DIGIT_YEAR: + { + aAny <<= static_cast < sal_Int16 > (pDocShell->GetDoc()->GetNumberFormatter ()->GetYear2000()); + } + break; + case WID_DOC_AUTOMATIC_CONTROL_FOCUS: + { + SwDrawModel * pDrawDoc; + bool bAuto; + if ( nullptr != ( pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel() ) ) + bAuto = pDrawDoc->GetAutoControlFocus(); + else + bAuto = false; + aAny <<= bAuto; + } + break; + case WID_DOC_APPLY_FORM_DESIGN_MODE: + { + SwDrawModel * pDrawDoc; + bool bMode; + if ( nullptr != ( pDrawDoc = pDocShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel() ) ) + bMode = pDrawDoc->GetOpenInDesignMode(); + else + bMode = true; + aAny <<= bMode; + } + break; + case WID_DOC_BASIC_LIBRARIES: + aAny <<= pDocShell->GetBasicContainer(); + break; + case WID_DOC_DIALOG_LIBRARIES: + aAny <<= pDocShell->GetDialogContainer(); + break; + case WID_DOC_VBA_DOCOBJ: + { + /* #i111553# This property provides the name of the constant that + will be used to store this model in the global Basic manager. + That constant will be equivalent to 'ThisComponent' but for + each application, so e.g. a 'ThisExcelDoc' and a 'ThisWordDoc' + constant can co-exist, as required by VBA. */ + aAny <<= OUString( "ThisWordDoc" ); + } + break; + case WID_DOC_RUNTIME_UID: + aAny <<= getRuntimeUID(); + break; + case WID_DOC_LOCK_UPDATES : + aAny <<= pDocShell->GetDoc()->IsInReading(); + break; + case WID_DOC_BUILDID: + aAny <<= maBuildId; + break; + case WID_DOC_HAS_VALID_SIGNATURES: + aAny <<= hasValidSignatures(); + break; + case WID_DOC_INTEROP_GRAB_BAG: + getGrabBagItem(aAny); + break; + + default: + { + const SfxPoolItem& rItem = pDocShell->GetDoc()->GetDefault(pEntry->nWID); + rItem.QueryValue(aAny, pEntry->nMemberId); + } + } + return aAny; +} + +void SwXTextDocument::addPropertyChangeListener(const OUString& /*PropertyName*/, + const Reference< XPropertyChangeListener > & /*aListener*/) +{ + OSL_FAIL("not implemented"); +} + +void SwXTextDocument::removePropertyChangeListener(const OUString& /*PropertyName*/, + const Reference< XPropertyChangeListener > & /*aListener*/) +{ + OSL_FAIL("not implemented"); +} + +void SwXTextDocument::addVetoableChangeListener(const OUString& /*PropertyName*/, + const Reference< XVetoableChangeListener > & /*aListener*/) +{ + OSL_FAIL("not implemented"); +} + +void SwXTextDocument::removeVetoableChangeListener(const OUString& /*PropertyName*/, + const Reference< XVetoableChangeListener > & /*aListener*/) +{ + OSL_FAIL("not implemented"); +} + +Reference< XNameAccess > SwXTextDocument::getLinks() +{ + if(!mxLinkTargetSupplier.is()) + { + mxLinkTargetSupplier = new SwXLinkTargetSupplier(*this); + } + return mxLinkTargetSupplier; +} + +Reference< XEnumerationAccess > SwXTextDocument::getRedlines( ) +{ + if(!mxXRedlines.is()) + { + mxXRedlines = new SwXRedlines(pDocShell->GetDoc()); + } + return mxXRedlines; +} + +void SwXTextDocument::NotifyRefreshListeners() +{ + // why does SwBaseShell not just call refresh? maybe because it's rSh is + // (sometimes) a different shell than GetWrtShell()? + lang::EventObject const ev(static_cast<SwXTextDocumentBaseClass &>(*this)); + m_pImpl->m_RefreshListeners.notifyEach( + & util::XRefreshListener::refreshed, ev); +} + +void SwXTextDocument::refresh() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + SwViewShell *pViewShell = pDocShell->GetWrtShell(); + NotifyRefreshListeners(); + if(pViewShell) + pViewShell->CalcLayout(); +} + +void SAL_CALL SwXTextDocument::addRefreshListener( + const Reference<util::XRefreshListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_RefreshListeners.addInterface(xListener); +} + +void SAL_CALL SwXTextDocument::removeRefreshListener( + const Reference<util::XRefreshListener> & xListener) +{ + // no need to lock here as m_pImpl is const and container threadsafe + m_pImpl->m_RefreshListeners.removeInterface(xListener); +} + +void SwXTextDocument::updateLinks( ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + SwDoc* pDoc = pDocShell->GetDoc(); + sfx2::LinkManager& rLnkMan = pDoc->getIDocumentLinksAdministration().GetLinkManager(); + if( !rLnkMan.GetLinks().empty() ) + { + UnoActionContext aAction(pDoc); + rLnkMan.UpdateAllLinks( false, true, nullptr ); + } +} + +//XPropertyState +PropertyState SAL_CALL SwXTextDocument::getPropertyState( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + if(!pEntry) + throw UnknownPropertyException(rPropertyName); + return PropertyState_DIRECT_VALUE; +} + +Sequence< PropertyState > SAL_CALL SwXTextDocument::getPropertyStates( const Sequence< OUString >& rPropertyNames ) +{ + const sal_Int32 nCount = rPropertyNames.getLength(); + Sequence < PropertyState > aRet ( nCount ); + + std::transform(rPropertyNames.begin(), rPropertyNames.end(), aRet.begin(), + [this](const OUString& rName) -> PropertyState { return getPropertyState(rName); }); + + return aRet; +} + +void SAL_CALL SwXTextDocument::setPropertyToDefault( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + if(!pEntry) + throw UnknownPropertyException(rPropertyName); + switch(pEntry->nWID) + { + case 0:default:break; + } +} + +Any SAL_CALL SwXTextDocument::getPropertyDefault( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + const SfxItemPropertySimpleEntry* pEntry = pPropSet->getPropertyMap().getByName( rPropertyName); + if(!pEntry) + throw UnknownPropertyException(rPropertyName); + Any aAny; + switch(pEntry->nWID) + { + case 0:default:break; + } + return aAny; +} + +static VclPtr< OutputDevice > lcl_GetOutputDevice( const SwPrintUIOptions &rPrintUIOptions ) +{ + VclPtr< OutputDevice > pOut; + + uno::Any aAny( rPrintUIOptions.getValue( "RenderDevice" )); + uno::Reference< awt::XDevice > xRenderDevice; + aAny >>= xRenderDevice; + if (xRenderDevice.is()) + { + VCLXDevice* pDevice = comphelper::getUnoTunnelImplementation<VCLXDevice>( xRenderDevice ); + pOut = pDevice ? pDevice->GetOutputDevice() : VclPtr< OutputDevice >(); + } + + return pOut; +} + +static bool lcl_SeqHasProperty( + const uno::Sequence< beans::PropertyValue >& rOptions, + const char *pPropName ) +{ + return std::any_of(rOptions.begin(), rOptions.end(), + [&pPropName](const beans::PropertyValue& rProp) { + return rProp.Name.equalsAscii( pPropName ); }); +} + +static bool lcl_GetBoolProperty( + const uno::Sequence< beans::PropertyValue >& rOptions, + const char *pPropName ) +{ + bool bRes = false; + auto pOption = std::find_if(rOptions.begin(), rOptions.end(), + [&pPropName](const beans::PropertyValue& rProp) { + return rProp.Name.equalsAscii( pPropName ); }); + if (pOption != rOptions.end()) + pOption->Value >>= bRes; + return bRes; +} + +SfxViewShell * SwXTextDocument::GetRenderView( + bool &rbIsSwSrcView, + const uno::Sequence< beans::PropertyValue >& rOptions, + bool bIsPDFExport ) +{ + // get view shell to use + SfxViewShell *pView = nullptr; + if (bIsPDFExport) + pView = GuessViewShell( rbIsSwSrcView ); + else + { + uno::Any aTmp; + auto pOption = std::find_if(rOptions.begin(), rOptions.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "View"; }); + if (pOption != rOptions.end()) + aTmp = pOption->Value; + + uno::Reference< frame::XController > xController; + if (aTmp >>= xController) + { + OSL_ENSURE( xController.is(), "controller is empty!" ); + pView = GuessViewShell( rbIsSwSrcView, xController ); + } + } + return pView; +} + +/* + * GetRenderDoc: + * returns the document to be rendered, usually this will be the 'regular' + * document but in case of PDF export of (multi-)selection it will + * be a temporary document that gets created if not already done. + * The rpView variable will be set (if not already done) to the used + * SfxViewShell. +*/ +SwDoc * SwXTextDocument::GetRenderDoc( + SfxViewShell *&rpView, + const uno::Any& rSelection, + bool bIsPDFExport ) +{ + SwDoc *pDoc = nullptr; + + uno::Reference< frame::XModel > xModel; + rSelection >>= xModel; + if (xModel == pDocShell->GetModel()) + pDoc = pDocShell->GetDoc(); + else + { + OSL_ENSURE( !xModel.is(), "unexpected model found" ); + + if (rSelection.hasValue()) // is anything selected ? + { + // this part should only be called when a temporary document needs to be created, + // for example for PDF export or printing of (multi-)selection only. + + if (!rpView) + { + bool bIsSwSrcView = false; + // aside from maybe PDF export the view should always have been provided! + OSL_ENSURE( bIsPDFExport, "view is missing, guessing one..." ); + + rpView = GuessViewShell( bIsSwSrcView ); + } + OSL_ENSURE( rpView, "SwViewShell missing" ); + // the view shell should be SwView for documents PDF export. + // for the page preview no selection should be possible + // (the export dialog does not allow for this option) + if (auto pSwView = dynamic_cast<SwView *>( rpView )) + { + if (!m_pRenderData) + { + OSL_FAIL("GetRenderDoc: no renderdata"); + return nullptr; + } + SfxObjectShellLock xDocSh(m_pRenderData->GetTempDocShell()); + if (!xDocSh.Is()) + { + xDocSh = pSwView->CreateTmpSelectionDoc(); + m_pRenderData->SetTempDocShell(xDocSh); + } + if (xDocSh.Is()) + { + pDoc = static_cast<SwDocShell*>(&xDocSh)->GetDoc(); + rpView = pDoc->GetDocShell()->GetView(); + } + } + else + { + OSL_FAIL("unexpected SwViewShell" ); + } + } + } + return pDoc; +} + +static void lcl_SavePrintUIOptionsToDocumentPrintData( + SwDoc &rDoc, + const SwPrintUIOptions &rPrintUIOptions, + bool bIsPDFEXport ) +{ + SwPrintData aDocPrintData( rDoc.getIDocumentDeviceAccess().getPrintData() ); + + aDocPrintData.SetPrintGraphic( rPrintUIOptions.IsPrintGraphics() ); + aDocPrintData.SetPrintTable( true ); // for now it was decided that tables should always be printed + aDocPrintData.SetPrintDraw( rPrintUIOptions.IsPrintDrawings() ); + aDocPrintData.SetPrintControl( rPrintUIOptions.IsPrintFormControls() ); + aDocPrintData.SetPrintLeftPage( rPrintUIOptions.IsPrintLeftPages() ); + aDocPrintData.SetPrintRightPage( rPrintUIOptions.IsPrintRightPages() ); + aDocPrintData.SetPrintReverse( false ); /*handled by print dialog now*/ + aDocPrintData.SetPaperFromSetup( rPrintUIOptions.IsPaperFromSetup() ); + aDocPrintData.SetPrintEmptyPages( rPrintUIOptions.IsPrintEmptyPages( bIsPDFEXport ) ); + aDocPrintData.SetPrintPostIts( rPrintUIOptions.GetPrintPostItsType() ); + aDocPrintData.SetPrintProspect( rPrintUIOptions.IsPrintProspect() ); + aDocPrintData.SetPrintProspect_RTL( rPrintUIOptions.IsPrintProspectRTL() ); + aDocPrintData.SetPrintPageBackground( rPrintUIOptions.IsPrintPageBackground() ); + aDocPrintData.SetPrintBlackFont( rPrintUIOptions.IsPrintWithBlackTextColor() ); + // aDocPrintData.SetPrintSingleJobs( b ); handled by File/Print dialog itself + // arDocPrintData.SetFaxName( s ); n/a in File/Print dialog + aDocPrintData.SetPrintHiddenText( rPrintUIOptions.IsPrintHiddenText() ); + aDocPrintData.SetPrintTextPlaceholder( rPrintUIOptions.IsPrintTextPlaceholders() ); + + rDoc.getIDocumentDeviceAccess().setPrintData( aDocPrintData ); +} + +sal_Int32 SAL_CALL SwXTextDocument::getRendererCount( + const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + { + throw DisposedException( OUString(), + static_cast< XTextDocument* >(this) ); + } + + const bool bIsPDFExport = !lcl_SeqHasProperty( rxOptions, "IsPrinter" ); + bool bIsSwSrcView = false; + SfxViewShell *pView = GetRenderView( bIsSwSrcView, rxOptions, bIsPDFExport ); + + if (!bIsSwSrcView && !m_pRenderData) + m_pRenderData.reset(new SwRenderData); + if (!m_pPrintUIOptions) + m_pPrintUIOptions = lcl_GetPrintUIOptions( pDocShell, pView ); + bool bFormat = m_pPrintUIOptions->processPropertiesAndCheckFormat( rxOptions ); + + SwDoc *pDoc = GetRenderDoc( pView, rSelection, bIsPDFExport ); + OSL_ENSURE( pDoc && pView, "doc or view shell missing!" ); + if (!pDoc || !pView) + return 0; + + // save current UI options from the print dialog for the next call to that dialog + lcl_SavePrintUIOptionsToDocumentPrintData( *pDoc, *m_pPrintUIOptions, bIsPDFExport ); + + sal_Int32 nRet = 0; + if (bIsSwSrcView) + { + SwSrcView& rSwSrcView = dynamic_cast<SwSrcView&>(*pView); + VclPtr< OutputDevice> pOutDev = lcl_GetOutputDevice( *m_pPrintUIOptions ); + nRet = rSwSrcView.PrintSource( pOutDev, 1 /* dummy */, true /* get page count only */ ); + } + else + { + SwDocShell *pRenderDocShell = pDoc->GetDocShell(); + + // TODO/mba: we really need a generic way to get the SwViewShell! + SwViewShell* pViewShell = nullptr; + SwView* pSwView = dynamic_cast<SwView*>( pView ); + if ( pSwView ) + { + pViewShell = pSwView->GetWrtShellPtr(); + } + else + { + if ( bIsPDFExport && bFormat ) + { + //create a hidden view to be able to export as PDF also in print preview + //pView and pSwView are not changed intentionally! + m_pHiddenViewFrame = SfxViewFrame::LoadHiddenDocument( *pRenderDocShell, SFX_INTERFACE_SFXDOCSH ); + pViewShell = static_cast<SwView*>(m_pHiddenViewFrame->GetViewShell())->GetWrtShellPtr(); + } + else + pViewShell = static_cast<SwPagePreview*>(pView)->GetViewShell(); + } + + if (!pViewShell || !pViewShell->GetLayout()) + return 0; + + if (bFormat) + { + // #i38289 + if( pViewShell->GetViewOptions()->getBrowseMode() || + pViewShell->GetViewOptions()->IsWhitespaceHidden() ) + { + SwViewOption aOpt( *pViewShell->GetViewOptions() ); + aOpt.setBrowseMode( false ); + aOpt.SetHideWhitespaceMode( false ); + pViewShell->ApplyViewOptions( aOpt ); + if (pSwView) + { + pSwView->RecheckBrowseMode(); + } + } + + // reformatting the document for printing will show the changes in the view + // which is likely to produce many unwanted and not nice to view actions. + // We don't want that! Thus we disable updating of the view. + pViewShell->StartAction(); + + if (pSwView) + { + if (m_pRenderData && m_pRenderData->NeedNewViewOptionAdjust( *pViewShell ) ) + m_pRenderData->ViewOptionAdjustStop(); + if (m_pRenderData && !m_pRenderData->IsViewOptionAdjust()) + { + m_pRenderData->ViewOptionAdjustStart( + *pViewShell, *pViewShell->GetViewOptions() ); + } + } + + m_pRenderData->MakeSwPrtOptions( pRenderDocShell, + m_pPrintUIOptions.get(), bIsPDFExport ); + + if (pSwView) + { + // PDF export should not make use of the SwPrtOptions + const SwPrintData *pPrtOptions = bIsPDFExport + ? nullptr : m_pRenderData->GetSwPrtOptions(); + bool setShowPlaceHoldersInPDF = false; + if(bIsPDFExport) + setShowPlaceHoldersInPDF = lcl_GetBoolProperty( rxOptions, "ExportPlaceholders" ); + m_pRenderData->ViewOptionAdjust( pPrtOptions, setShowPlaceHoldersInPDF ); + } + + // since printing now also use the API for PDF export this option + // should be set for printing as well ... + pViewShell->SetPDFExportOption( true ); + + // there is some redundancy between those two function calls, but right now + // there is no time to sort this out. + //TODO: check what exactly needs to be done and make just one function for that + pViewShell->CalcLayout(); + + // #122919# Force field update before PDF export, but after layout init (tdf#121962) + bool bStateChanged = false; + // check configuration: shall update of printing information in DocInfo set the document to "modified"? + if ( pRenderDocShell->IsEnableSetModified() && !SvtPrintWarningOptions().IsModifyDocumentOnPrintingAllowed() ) + { + pRenderDocShell->EnableSetModified( false ); + bStateChanged = true; + } + pViewShell->SwViewShell::UpdateFields(true); + if( bStateChanged ) + pRenderDocShell->EnableSetModified(); + + pViewShell->CalcPagesForPrint( pViewShell->GetPageCount() ); + + pViewShell->SetPDFExportOption( false ); + + // enable view again + pViewShell->EndAction(); + } + + const sal_Int32 nPageCount = pViewShell->GetPageCount(); + + // get number of pages to be rendered + + const bool bPrintProspect = m_pPrintUIOptions->getBoolValue( "PrintProspect" ); + if (bPrintProspect) + { + SwDoc::CalculatePagePairsForProspectPrinting( *pViewShell->GetLayout(), *m_pRenderData, *m_pPrintUIOptions, nPageCount ); + nRet = m_pRenderData->GetPagePairsForProspectPrinting().size(); + } + else + { + const SwPostItMode nPostItMode = static_cast<SwPostItMode>( m_pPrintUIOptions->getIntValue( "PrintAnnotationMode", 0 ) ); + if (nPostItMode != SwPostItMode::NONE) + { + VclPtr< OutputDevice > pOutDev = lcl_GetOutputDevice( *m_pPrintUIOptions ); + m_pRenderData->CreatePostItData( pDoc, pViewShell->GetViewOptions(), pOutDev ); + } + + // get set of valid document pages (according to the current settings) + // and their start frames + SwDoc::CalculatePagesForPrinting( *pViewShell->GetLayout(), *m_pRenderData, *m_pPrintUIOptions, bIsPDFExport, nPageCount ); + + if (nPostItMode != SwPostItMode::NONE) + { + SwDoc::UpdatePagesForPrintingWithPostItData( *m_pRenderData, + *m_pPrintUIOptions, nPageCount ); + } + + nRet = m_pRenderData->GetPagesToPrint().size(); + } + } + OSL_ENSURE( nRet >= 0, "negative number of pages???" ); + + return nRet; +} + +uno::Sequence< beans::PropertyValue > SAL_CALL SwXTextDocument::getRenderer( + sal_Int32 nRenderer, + const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + { + throw DisposedException("", static_cast< XTextDocument* >(this)); + } + + const bool bIsPDFExport = !lcl_SeqHasProperty( rxOptions, "IsPrinter" ); + bool bIsSwSrcView = false; + SfxViewShell *pView = GetRenderView( bIsSwSrcView, rxOptions, bIsPDFExport ); + + // m_pRenderData should NOT be created here! + // That should only be done in getRendererCount. If this function is called before + // getRendererCount was called then the caller will probably just retrieve the extra UI options + // and is not interested in getting valid information about the other data that would + // otherwise be provided here! +// if( ! m_pRenderData ) +// m_pRenderData = new SwRenderData; + if (!m_pPrintUIOptions) + m_pPrintUIOptions = lcl_GetPrintUIOptions( pDocShell, pView ); + m_pPrintUIOptions->processProperties( rxOptions ); + const bool bPrintProspect = m_pPrintUIOptions->getBoolValue( "PrintProspect" ); + const bool bIsSkipEmptyPages = !m_pPrintUIOptions->IsPrintEmptyPages( bIsPDFExport ); + const bool bPrintPaperFromSetup = m_pPrintUIOptions->getBoolValue( "PrintPaperFromSetup" ); + + SwDoc *pDoc = GetRenderDoc( pView, rSelection, bIsPDFExport ); + OSL_ENSURE( pDoc && pView, "doc or view shell missing!" ); + if (!pDoc || !pView) + return uno::Sequence< beans::PropertyValue >(); + + // due to #110067# (document page count changes sometimes during + // PDF export/printing) we can not check for the upper bound properly. + // Thus instead of throwing the exception we silently return. + if (0 > nRenderer) + throw IllegalArgumentException(); + + // TODO/mba: we really need a generic way to get the SwViewShell! + SwViewShell* pVwSh = nullptr; + SwView* pSwView = dynamic_cast<SwView*>( pView ); + if ( pSwView ) + pVwSh = pSwView->GetWrtShellPtr(); + else + pVwSh = static_cast<SwPagePreview*>(pView)->GetViewShell(); + + sal_Int32 nMaxRenderer = 0; + if (!bIsSwSrcView && m_pRenderData) + { + OSL_ENSURE( m_pRenderData, "m_pRenderData missing!!" ); + nMaxRenderer = bPrintProspect? + m_pRenderData->GetPagePairsForProspectPrinting().size() - 1 : + m_pRenderData->GetPagesToPrint().size() - 1; + } + // since SwSrcView::PrintSource is a poor implementation to get the number of pages to print + // we obmit checking of the upper bound in this case. + if (!bIsSwSrcView && m_pRenderData && nRenderer > nMaxRenderer) + return uno::Sequence< beans::PropertyValue >(); + + uno::Sequence< beans::PropertyValue > aRenderer; + if (m_pRenderData) + { + // #i114210# + // determine the correct page number from the renderer index + // #i114875 + // consider brochure print + const sal_Int32 nPage = bPrintProspect + ? nRenderer + 1 + : m_pRenderData->GetPagesToPrint()[ nRenderer ]; + + // get paper tray to use ... + sal_Int32 nPrinterPaperTray = -1; + if (! bPrintPaperFromSetup) + { + // ... from individual page style (see the page tab in Format/Page dialog) + const std::map< sal_Int32, sal_Int32 > &rPaperTrays = m_pRenderData->GetPrinterPaperTrays(); + std::map< sal_Int32, sal_Int32 >::const_iterator aIt( rPaperTrays.find( nPage ) ); + if (aIt != rPaperTrays.end()) + nPrinterPaperTray = aIt->second; + } + + awt::Size aPageSize; + awt::Point aPagePos; + awt::Size aPreferredPageSize; + Size aTmpSize; + if (bIsSwSrcView || bPrintProspect) + { + // for printing of HTML source code and prospect printing we should use + // the printers paper size since + // a) HTML source view has no page size + // b) prospect printing has a different page size from the documents page + // since two document pages will get rendered on one printer page + + // since PageIncludesNonprintableArea will be set to true we can return the + // printers paper size here. + // Sometimes 'getRenderer' is only called to get "ExtraPrintUIOptions", in this + // case we won't get an OutputDevice here, but then the caller also has no need + // for the correct PageSisze right now... + VclPtr< Printer > pPrinter = dynamic_cast< Printer * >(lcl_GetOutputDevice( *m_pPrintUIOptions ).get()); + if (pPrinter) + { + // HTML source view and prospect adapt to the printer's paper size + aTmpSize = pPrinter->GetPaperSize(); + aTmpSize = OutputDevice::LogicToLogic( aTmpSize, + pPrinter->GetMapMode(), MapMode( MapUnit::Map100thMM )); + aPageSize = awt::Size( aTmpSize.Width(), aTmpSize.Height() ); + #if 0 + // #i115048# it seems users didn't like getting double the formatted page size + // revert to "old" behavior scaling to the current paper size of the printer + if (bPrintProspect) + { + // we just state what output size we would need + // which may cause vcl to set that page size on the printer + // (if available and not overridden by the user) + aTmpSize = pVwSh->GetPageSize( nPage, bIsSkipEmptyPages ); + aPreferredPageSize = awt::Size ( convertTwipToMm100( 2 * aTmpSize.Width() ), + convertTwipToMm100( aTmpSize.Height() )); + } + #else + if( bPrintProspect ) + { + // just switch to an appropriate portrait/landscape format + // FIXME: brochure printing with landscape pages puts the + // pages next to each other, so landscape is currently always + // the better choice + if( aPageSize.Width < aPageSize.Height ) + { + aPreferredPageSize.Width = aPageSize.Height; + aPreferredPageSize.Height = aPageSize.Width; + } + } + #endif + } + } + else + { + aTmpSize = pVwSh->GetPageSize( nPage, bIsSkipEmptyPages ); + aPageSize = awt::Size ( convertTwipToMm100( aTmpSize.Width() ), + convertTwipToMm100( aTmpSize.Height() )); + Point aPoint = pVwSh->GetPagePos(nPage); + aPagePos = awt::Point(convertTwipToMm100(aPoint.X()), convertTwipToMm100(aPoint.Y())); + } + + sal_Int32 nLen = 3; + aRenderer.realloc(3); + aRenderer[0].Name = "PageSize"; + aRenderer[0].Value <<= aPageSize; + aRenderer[1].Name = "PageIncludesNonprintableArea"; + aRenderer[1].Value <<= true; + aRenderer[2].Name = "PagePos"; + aRenderer[2].Value <<= aPagePos; + if (aPreferredPageSize.Width && aPreferredPageSize.Height) + { + ++nLen; + aRenderer.realloc( nLen ); + aRenderer[ nLen - 1 ].Name = "PreferredPageSize"; + aRenderer[ nLen - 1 ].Value <<= aPreferredPageSize; + } + if (nPrinterPaperTray >= 0) + { + ++nLen; + aRenderer.realloc( nLen ); + aRenderer[ nLen - 1 ].Name = "PrinterPaperTray"; + aRenderer[ nLen - 1 ].Value <<= nPrinterPaperTray; + } + } + + // #i117783# + if ( bApplyPagePrintSettingsFromXPagePrintable ) + { + const SwPagePreviewPrtData* pPagePrintSettings = + pDocShell->GetDoc()->GetPreviewPrtData(); + if ( pPagePrintSettings && + ( pPagePrintSettings->GetRow() > 1 || + pPagePrintSettings->GetCol() > 1 ) ) + { + // extend render data by page print settings attributes + sal_Int32 nLen = aRenderer.getLength(); + const sal_Int32 nRenderDataIdxStart = nLen; + nLen += 9; + aRenderer.realloc( nLen ); + // put page print settings attribute into render data + const sal_Int32 nRow = pPagePrintSettings->GetRow(); + aRenderer[ nRenderDataIdxStart + 0 ].Name = "NUpRows"; + aRenderer[ nRenderDataIdxStart + 0 ].Value <<= std::max<sal_Int32>( nRow, 1); + const sal_Int32 nCol = pPagePrintSettings->GetCol(); + aRenderer[ nRenderDataIdxStart + 1 ].Name = "NUpColumns"; + aRenderer[ nRenderDataIdxStart + 1 ].Value <<= std::max<sal_Int32>( nCol, 1); + aRenderer[ nRenderDataIdxStart + 2 ].Name = "NUpPageMarginLeft"; + aRenderer[ nRenderDataIdxStart + 2 ].Value <<= pPagePrintSettings->GetLeftSpace(); + aRenderer[ nRenderDataIdxStart + 3 ].Name = "NUpPageMarginRight"; + aRenderer[ nRenderDataIdxStart + 3 ].Value <<= pPagePrintSettings->GetRightSpace(); + aRenderer[ nRenderDataIdxStart + 4 ].Name = "NUpPageMarginTop"; + aRenderer[ nRenderDataIdxStart + 4 ].Value <<= pPagePrintSettings->GetTopSpace(); + aRenderer[ nRenderDataIdxStart + 5 ].Name = "NUpPageMarginBottom"; + aRenderer[ nRenderDataIdxStart + 5 ].Value <<= pPagePrintSettings->GetBottomSpace(); + aRenderer[ nRenderDataIdxStart + 6 ].Name = "NUpHorizontalSpacing"; + aRenderer[ nRenderDataIdxStart + 6 ].Value <<= pPagePrintSettings->GetHorzSpace(); + aRenderer[ nRenderDataIdxStart + 7 ].Name = "NUpVerticalSpacing"; + aRenderer[ nRenderDataIdxStart + 7 ].Value <<= pPagePrintSettings->GetVertSpace(); + { + Printer* pPrinter = pDocShell->GetDoc()->getIDocumentDeviceAccess().getPrinter( false ); + if ( pPrinter ) + { + awt::Size aNewPageSize; + const Size aPageSize = pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ); + aNewPageSize = awt::Size( aPageSize.Width(), aPageSize.Height() ); + if ( ( pPagePrintSettings->GetLandscape() && + aPageSize.Width() < aPageSize.Height() ) || + ( !pPagePrintSettings->GetLandscape() && + aPageSize.Width() > aPageSize.Height() ) ) + { + aNewPageSize = awt::Size( aPageSize.Height(), aPageSize.Width() ); + } + aRenderer[ nRenderDataIdxStart + 8 ].Name = "NUpPaperSize"; + aRenderer[ nRenderDataIdxStart + 8 ].Value <<= aNewPageSize; + } + } + } + + bApplyPagePrintSettingsFromXPagePrintable = false; + } + + m_pPrintUIOptions->appendPrintUIOptions( aRenderer ); + + return aRenderer; +} + +SfxViewShell * SwXTextDocument::GuessViewShell( + /* out */ bool &rbIsSwSrcView, + const uno::Reference< css::frame::XController >& rController ) +{ + // #130810# SfxViewShell::Current() / SfxViewShell::GetObjectShell() + // must not be used (see comment from MBA) + + SfxViewShell *pView = nullptr; + SwView *pSwView = nullptr; + SwPagePreview *pSwPagePreview = nullptr; + SwSrcView *pSwSrcView = nullptr; + SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pDocShell, false ); + + // look for the view shell with the same controller in use, + // otherwise look for a suitable view, preferably a SwView, + // if that one is not found use a SwPagePreview if found. + while (pFrame) + { + pView = pFrame->GetViewShell(); + pSwView = dynamic_cast< SwView * >(pView); + pSwSrcView = dynamic_cast< SwSrcView * >(pView); + if (!pSwPagePreview) + pSwPagePreview = dynamic_cast< SwPagePreview * >(pView); + if (rController.is()) + { + if (pView && pView->GetController() == rController) + break; + } + else if (pSwView || pSwSrcView) + break; + pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell, false ); + } + + OSL_ENSURE( pSwView || pSwPagePreview || pSwSrcView, "failed to get view shell" ); + if (pView) + rbIsSwSrcView = pSwSrcView != nullptr; + return pView; +} + +void SAL_CALL SwXTextDocument::render( + sal_Int32 nRenderer, + const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + { + throw DisposedException( OUString(), + static_cast< XTextDocument* >(this) ); + } + + // due to #110067# (document page count changes sometimes during + // PDF export/printing) we can not check for the upper bound properly. + // Thus instead of throwing the exception we silently return. + if (0 > nRenderer) + throw IllegalArgumentException(); + + const bool bHasPDFExtOutDevData = lcl_SeqHasProperty( rxOptions, "HasPDFExtOutDevData" ); + const bool bIsPDFExport = !lcl_SeqHasProperty( rxOptions, "IsPrinter" ) || bHasPDFExtOutDevData; + bool bIsSwSrcView = false; + SfxViewShell *pView = GetRenderView( bIsSwSrcView, rxOptions, bIsPDFExport ); + + OSL_ENSURE( m_pRenderData, "data should have been created already in getRendererCount..." ); + OSL_ENSURE( m_pPrintUIOptions, "data should have been created already in getRendererCount..." ); + if (!bIsSwSrcView && !m_pRenderData) + m_pRenderData.reset(new SwRenderData); + if (!m_pPrintUIOptions) + m_pPrintUIOptions = lcl_GetPrintUIOptions( pDocShell, pView ); + m_pPrintUIOptions->processProperties( rxOptions ); + const bool bPrintProspect = m_pPrintUIOptions->getBoolValue( "PrintProspect" ); + const bool bLastPage = m_pPrintUIOptions->getBoolValue( "IsLastPage" ); + + SwDoc *pDoc = GetRenderDoc( pView, rSelection, bIsPDFExport ); + OSL_ENSURE( pDoc && pView, "doc or view shell missing!" ); + if (pDoc && pView) + { + sal_Int32 nMaxRenderer = 0; + if (!bIsSwSrcView) + { + OSL_ENSURE( m_pRenderData, "m_pRenderData missing!!" ); + nMaxRenderer = bPrintProspect? + m_pRenderData->GetPagePairsForProspectPrinting().size() - 1 : + m_pRenderData->GetPagesToPrint().size() - 1; + } + // since SwSrcView::PrintSource is a poor implementation to get the number of pages to print + // we obmit checking of the upper bound in this case. + if (bIsSwSrcView || nRenderer <= nMaxRenderer) + { + if (bIsSwSrcView) + { + SwSrcView& rSwSrcView = dynamic_cast<SwSrcView&>(*pView); + VclPtr< OutputDevice > pOutDev = lcl_GetOutputDevice( *m_pPrintUIOptions ); + rSwSrcView.PrintSource(pOutDev, nRenderer + 1, false); + } + else + { + // the view shell should be SwView for documents PDF export + // or SwPagePreview for PDF export of the page preview + SwViewShell* pVwSh = nullptr; + // TODO/mba: we really need a generic way to get the SwViewShell! + const SwView* pSwView = dynamic_cast<const SwView*>(pView); + if (pSwView) + pVwSh = pSwView->GetWrtShellPtr(); + else + pVwSh = static_cast<SwPagePreview*>(pView)->GetViewShell(); + + // get output device to use + VclPtr< OutputDevice > pOut = lcl_GetOutputDevice( *m_pPrintUIOptions ); + + if(pVwSh && pOut && m_pRenderData->HasSwPrtOptions()) + { + const OUString aPageRange = m_pPrintUIOptions->getStringValue( "PageRange" ); + const bool bFirstPage = m_pPrintUIOptions->getBoolValue( "IsFirstPage" ); + bool bIsSkipEmptyPages = !m_pPrintUIOptions->IsPrintEmptyPages( bIsPDFExport ); + + OSL_ENSURE((pSwView && m_pRenderData->IsViewOptionAdjust()) + || (!pSwView && !m_pRenderData->IsViewOptionAdjust()), + "SwView / SwViewOptionAdjust_Impl availability mismatch" ); + + // since printing now also use the API for PDF export this option + // should be set for printing as well ... + pVwSh->SetPDFExportOption( true ); + + // #i12836# enhanced pdf export + + // First, we have to export hyperlinks, notes, and outline to pdf. + // During this process, additional information required for tagging + // the pdf file are collected, which are evaulated during painting. + + SwWrtShell* pWrtShell = pSwView ? pSwView->GetWrtShellPtr() : nullptr; + + SwPrintData const& rSwPrtOptions = + *m_pRenderData->GetSwPrtOptions(); + + if (bIsPDFExport && (bFirstPage || bHasPDFExtOutDevData) && pWrtShell) + { + SwEnhancedPDFExportHelper aHelper( *pWrtShell, *pOut, aPageRange, bIsSkipEmptyPages, false, rSwPrtOptions ); + } + + if (bPrintProspect) + pVwSh->PrintProspect( pOut, rSwPrtOptions, nRenderer ); + else // normal printing and PDF export + pVwSh->PrintOrPDFExport( pOut, rSwPrtOptions, nRenderer, bIsPDFExport ); + + // #i35176# + + // After printing the last page, we take care for the links coming + // from the EditEngine. The links are generated during the painting + // process, but the destinations are still missing. + + if (bIsPDFExport && bLastPage && pWrtShell) + { + SwEnhancedPDFExportHelper aHelper( *pWrtShell, *pOut, aPageRange, bIsSkipEmptyPages, true, rSwPrtOptions ); + } + + pVwSh->SetPDFExportOption( false ); + + // last page to be rendered? (not necessarily the last page of the document) + // -> do clean-up of data + if (bLastPage) + { + // #i96167# haggai: delete ViewOptionsAdjust here because it makes use + // of the shell, which might get destroyed in lcl_DisposeView! + if (m_pRenderData->IsViewOptionAdjust()) + m_pRenderData->ViewOptionAdjustStop(); + + if (m_pRenderData->HasPostItData()) + m_pRenderData->DeletePostItData(); + if (m_pHiddenViewFrame) + { + lcl_DisposeView( m_pHiddenViewFrame, pDocShell ); + m_pHiddenViewFrame = nullptr; + + // prevent crash described in #i108805 + SwDocShell *pRenderDocShell = pDoc->GetDocShell(); + SfxItemSet *pSet = pRenderDocShell->GetMedium()->GetItemSet(); + pSet->Put( SfxBoolItem( SID_HIDDEN, false ) ); + + } + } + } + } + } + } + if( bLastPage ) + { + m_pRenderData.reset(); + m_pPrintUIOptions.reset(); + } +} + +// xforms::XFormsSupplier +Reference<XNameContainer> SAL_CALL SwXTextDocument::getXForms() +{ + SolarMutexGuard aGuard; + if ( !pDocShell ) + throw DisposedException( OUString(), static_cast< XTextDocument* >( this ) ); + SwDoc* pDoc = pDocShell->GetDoc(); + return pDoc->getXForms(); +} + +uno::Reference< text::XFlatParagraphIterator > SAL_CALL SwXTextDocument::getFlatParagraphIterator(::sal_Int32 nTextMarkupType, sal_Bool bAutomatic) +{ + SolarMutexGuard aGuard; + if (!IsValid()) + { + throw DisposedException("SwXTextDocument not valid", + static_cast<XTextDocument*>(this)); + } + + return SwUnoCursorHelper::CreateFlatParagraphIterator( + *pDocShell->GetDoc(), nTextMarkupType, bAutomatic); +} + +uno::Reference< util::XCloneable > SwXTextDocument::createClone( ) +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + + // create a new document - hidden - copy the storage and return it + // SfxObjectShellRef is used here, since the model should control object lifetime after creation + // and thus SfxObjectShellLock is not allowed here + // the model holds reference to the shell, so the shell will not destructed at the end of method + SfxObjectShellRef pShell = pDocShell->GetDoc()->CreateCopy(false, false); + uno::Reference< frame::XModel > xNewModel = pShell->GetModel(); + uno::Reference< embed::XStorage > xNewStorage = ::comphelper::OStorageHelper::GetTemporaryStorage( ); + uno::Sequence< beans::PropertyValue > aTempMediaDescriptor; + storeToStorage( xNewStorage, aTempMediaDescriptor ); + uno::Reference< document::XStorageBasedDocument > xStorageDoc( xNewModel, uno::UNO_QUERY ); + xStorageDoc->loadFromStorage( xNewStorage, aTempMediaDescriptor ); + return uno::Reference< util::XCloneable >( xNewModel, UNO_QUERY ); +} + +void SwXTextDocument::addPasteEventListener(const uno::Reference<text::XPasteListener>& xListener) +{ + SolarMutexGuard aGuard; + + if (IsValid() && xListener.is()) + pDocShell->GetWrtShell()->GetPasteListeners().addInterface(xListener); +} + +void SwXTextDocument::removePasteEventListener( + const uno::Reference<text::XPasteListener>& xListener) +{ + SolarMutexGuard aGuard; + + if (IsValid() && xListener.is()) + pDocShell->GetWrtShell()->GetPasteListeners().removeInterface(xListener); +} + +void SwXTextDocument::paintTile( VirtualDevice &rDevice, + int nOutputWidth, int nOutputHeight, + int nTilePosX, int nTilePosY, + long nTileWidth, long nTileHeight ) +{ + SwViewShell* pViewShell = pDocShell->GetWrtShell(); + pViewShell->PaintTile(rDevice, nOutputWidth, nOutputHeight, + nTilePosX, nTilePosY, nTileWidth, nTileHeight); + + LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight, + nTilePosX, nTilePosY, nTileWidth, nTileHeight); +} + +Size SwXTextDocument::getDocumentSize() +{ + SwViewShell* pViewShell = pDocShell->GetWrtShell(); + Size aDocSize = pViewShell->GetDocSize(); + + return Size(aDocSize.Width() + 2 * DOCUMENTBORDER, + aDocSize.Height() + 2 * DOCUMENTBORDER); +} + +void SwXTextDocument::setPart(int nPart) +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (!pWrtShell) + return; + + pWrtShell->GotoPage(nPart + 1, true); +} + +int SwXTextDocument::getParts() +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (!pWrtShell) + return 0; + + return pWrtShell->GetPageCnt(); +} + +OUString SwXTextDocument::getPartPageRectangles() +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (!pWrtShell) + return OUString(); + + return pWrtShell->getPageRectangles(); +} + +void SwXTextDocument::setClipboard(const uno::Reference<datatransfer::clipboard::XClipboard>& xClipboard) +{ + SolarMutexGuard aGuard; + + SwView* pView = pDocShell->GetView(); + if (pView) + pView->GetEditWin().SetClipboard(xClipboard); +} + +bool SwXTextDocument::isMimeTypeSupported() +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (!pWrtShell) + return false; + + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(&pWrtShell->GetView().GetEditWin())); + if (SdrView* pSdrView = pWrtShell->GetDrawView()) + { + if (pSdrView->GetTextEditObject()) + // Editing shape text + return EditEngine::HasValidData(aDataHelper.GetTransferable()); + } + + return aDataHelper.GetXTransferable().is() && SwTransferable::IsPaste(*pWrtShell, aDataHelper); +} + +void SwXTextDocument::setClientVisibleArea(const tools::Rectangle& rRectangle) +{ + if (SwView* pView = pDocShell->GetView()) + { + // set the PgUp/PgDown offset + pView->ForcePageUpDownOffset(2 * rRectangle.GetHeight() / 3); + } + + if (SwViewShell* pViewShell = pDocShell->GetWrtShell()) + { + pViewShell->setLOKVisibleArea(rRectangle); + } +} + +void SwXTextDocument::setClientZoom(int nTilePixelWidth_, int /*nTilePixelHeight_*/, + int nTileTwipWidth_, int /*nTileTwipHeight_*/) +{ + // Here we set the zoom value as it has been set by the user in the client. + // This value is used in postMouseEvent and setGraphicSelection methods + // for in place chart editing. We assume that x and y scale is roughly + // the same. + SfxInPlaceClient* pIPClient = pDocShell->GetView()->GetIPClient(); + if (pIPClient) + { + SwViewShell* pWrtViewShell = pDocShell->GetWrtShell(); + double fScale = nTilePixelWidth_ * TWIPS_PER_PIXEL / (nTileTwipWidth_ * 1.0); + SwViewOption aOption(*(pWrtViewShell->GetViewOptions())); + if (aOption.GetZoom() != fScale * 100) + { + aOption.SetZoom(fScale * 100); + pWrtViewShell->ApplyViewOptions(aOption); + + // Changing the zoom value doesn't always trigger the updating of + // the client ole object area, so we call it directly. + pIPClient->VisAreaChanged(); + } + } +} + +PointerStyle SwXTextDocument::getPointer() +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (!pWrtShell) + return PointerStyle::Arrow; + + return pWrtShell->GetView().GetEditWin().GetPointer(); +} + +OUString SwXTextDocument::getTrackedChanges() +{ + boost::property_tree::ptree aTrackedChanges; + + // Disable since usability is very low beyond some small number of changes. + static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr; + if (!bDisableRedlineComments) + { + const SwRedlineTable& rRedlineTable + = pDocShell->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable(); + for (SwRedlineTable::size_type i = 0; i < rRedlineTable.size(); ++i) + { + boost::property_tree::ptree aTrackedChange; + aTrackedChange.put("index", rRedlineTable[i]->GetId()); + aTrackedChange.put("author", rRedlineTable[i]->GetAuthorString(1).toUtf8().getStr()); + aTrackedChange.put("type", SwRedlineTypeToOUString( + rRedlineTable[i]->GetRedlineData().GetType()) + .toUtf8() + .getStr()); + aTrackedChange.put("comment", + rRedlineTable[i]->GetRedlineData().GetComment().toUtf8().getStr()); + aTrackedChange.put("description", rRedlineTable[i]->GetDescr().toUtf8().getStr()); + OUString sDateTime = utl::toISO8601( + rRedlineTable[i]->GetRedlineData().GetTimeStamp().GetUNODateTime()); + aTrackedChange.put("dateTime", sDateTime.toUtf8().getStr()); + + SwContentNode* pContentNd = rRedlineTable[i]->GetContentNode(); + SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current()); + if (pView && pContentNd) + { + SwShellCursor aCursor(pView->GetWrtShell(), *(rRedlineTable[i]->Start())); + aCursor.SetMark(); + aCursor.GetMark()->nNode = *pContentNd; + aCursor.GetMark()->nContent.Assign(pContentNd, + rRedlineTable[i]->End()->nContent.GetIndex()); + + 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); + aTrackedChange.put("textRange", sRects.getStr()); + } + + aTrackedChanges.push_back(std::make_pair("", aTrackedChange)); + } + } + + boost::property_tree::ptree aTree; + aTree.add_child("redlines", aTrackedChanges); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + + return OUString::fromUtf8(aStream.str().c_str()); +} + +OUString SwXTextDocument::getTrackedChangeAuthors() +{ + return SW_MOD()->GetRedlineAuthorInfo(); +} + +OUString SwXTextDocument::getRulerState() +{ + SwView* pView = pDocShell->GetView(); + return OUString::fromUtf8(dynamic_cast<SwCommentRuler&>(pView->GetHRuler()).CreateJsonNotification().c_str()); +} + +OUString SwXTextDocument::getPostIts() +{ + SolarMutexGuard aGuard; + boost::property_tree::ptree aAnnotations; + for (auto const& sidebarItem : *pDocShell->GetView()->GetPostItMgr()) + { + sw::annotation::SwAnnotationWin* pWin = sidebarItem->pPostIt.get(); + + const SwPostItField* pField = pWin->GetPostItField(); + const SwRect& aRect = pWin->GetAnchorRect(); + tools::Rectangle aSVRect(aRect.Pos().getX(), + aRect.Pos().getY(), + aRect.Pos().getX() + aRect.SSize().Width(), + aRect.Pos().getY() + aRect.SSize().Height()); + + if (!sidebarItem->maLayoutInfo.mPositionFromCommentAnchor) + { + // Comments on frames: anchor position is the corner position, not the whole frame. + aSVRect.SetSize(Size(0, 0)); + } + + std::vector<OString> aRects; + for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges()) + { + const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight()); + aRects.push_back(rect.SVRect().toString()); + } + const OString sRects = comphelper::string::join("; ", aRects); + + boost::property_tree::ptree aAnnotation; + aAnnotation.put("id", pField->GetPostItId()); + aAnnotation.put("parent", pWin->CalcParent()); + aAnnotation.put("author", pField->GetPar1().toUtf8().getStr()); + aAnnotation.put("text", pField->GetPar2().toUtf8().getStr()); + aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false"); + aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime())); + aAnnotation.put("anchorPos", aSVRect.toString()); + aAnnotation.put("textRange", sRects.getStr()); + + aAnnotations.push_back(std::make_pair("", aAnnotation)); + } + + boost::property_tree::ptree aTree; + aTree.add_child("comments", aAnnotations); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + + return OUString::fromUtf8(aStream.str().c_str()); +} + +void SwXTextDocument::executeFromFieldEvent(const StringMap& aArguments) +{ + auto aIter = aArguments.find("type"); + if (aIter != aArguments.end() && aIter->second == "drop-down") + { + aIter = aArguments.find("cmd"); + if (aIter != aArguments.end() && aIter->second == "selected") + { + aIter = aArguments.find("data"); + if (aIter != aArguments.end()) + { + sal_Int32 nSelection = aIter->second.toInt32(); + SwPosition aPos(*pDocShell->GetWrtShell()->GetCursor()->GetPoint()); + sw::mark::IFieldmark* pFieldBM = pDocShell->GetWrtShell()->getIDocumentMarkAccess()->getFieldmarkFor(aPos); + if ( !pFieldBM ) + { + --aPos.nContent; + pFieldBM = pDocShell->GetWrtShell()->getIDocumentMarkAccess()->getFieldmarkFor(aPos); + } + if (pFieldBM && pFieldBM->GetFieldname() == ODF_FORMDROPDOWN) + { + if (nSelection >= 0) + { + OUString sKey = ODF_FORMDROPDOWN_RESULT; + (*pFieldBM->GetParameters())[sKey] <<= nSelection; + pFieldBM->Invalidate(); + pDocShell->GetWrtShell()->SetModified(); + pDocShell->GetView()->GetEditWin().LogicInvalidate(nullptr); + } + } + } + } + } +} + +int SwXTextDocument::getPart() +{ + SolarMutexGuard aGuard; + + SwView* pView = pDocShell->GetView(); + if (!pView) + return 0; + + return pView->getPart(); +} + +OUString SwXTextDocument::getPartName(int nPart) +{ + SolarMutexGuard aGuard; + + return SwResId(STR_PAGE) + OUString::number(nPart + 1); +} + +OUString SwXTextDocument::getPartHash(int nPart) +{ + SolarMutexGuard aGuard; + OUString sPart(SwResId(STR_PAGE) + OUString::number(nPart + 1)); + + return OUString::number(sPart.hashCode()); +} + +VclPtr<vcl::Window> SwXTextDocument::getDocWindow() +{ + SolarMutexGuard aGuard; + VclPtr<vcl::Window> pWindow; + SwView* pView = pDocShell->GetView(); + if (pView) + pWindow = &(pView->GetEditWin()); + + LokChartHelper aChartHelper(pView); + VclPtr<vcl::Window> pChartWindow = aChartHelper.GetWindow(); + if (pChartWindow) + pWindow = pChartWindow; + + return pWindow; +} + +void SwXTextDocument::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + SolarMutexGuard aGuard; + + SwViewShell* pViewShell = pDocShell->GetWrtShell(); + + SwView* pView = pDocShell->GetView(); + if (!pView) + return; + + pView->SetViewLayout(1/*nColumns*/, false/*bBookMode*/, true); + + // Tiled rendering defaults. + SwViewOption aViewOption(*pViewShell->GetViewOptions()); + aViewOption.SetHardBlank(false); + for (const beans::PropertyValue& rValue : rArguments) + { + if (rValue.Name == ".uno:HideWhitespace" && rValue.Value.has<bool>()) + aViewOption.SetHideWhitespaceMode(rValue.Value.get<bool>()); + else if (rValue.Name == ".uno:ShowBorderShadow" && rValue.Value.has<bool>()) + SwViewOption::SetAppearanceFlag(ViewOptFlags::Shadow , rValue.Value.get<bool>()); + else if (rValue.Name == ".uno:Author" && rValue.Value.has<OUString>()) + { + // Store the author name in the view. + pView->SetRedlineAuthor(rValue.Value.get<OUString>()); + // Let the actual author name pick up the value from the current + // view, which would normally happen only during the next view + // switch. + pDocShell->SetView(pView); + } + } + + // Set the initial zoom value to 1; usually it is set in setClientZoom and + // SwViewShell::PaintTile; zoom value is used for chart in place + // editing, see postMouseEvent and setGraphicSelection methods. + aViewOption.SetZoom(1 * 100); + + aViewOption.SetPostIts(comphelper::LibreOfficeKit::isTiledAnnotations()); + pViewShell->ApplyViewOptions(aViewOption); + + // position the pages again after setting view options. Eg: if postit + // rendering is false, then there would be no sidebar, so width of the + // document needs to be adjusted + pViewShell->GetLayout()->CheckViewLayout( pViewShell->GetViewOptions(), nullptr ); + + // Disable map mode, so that it's possible to send mouse event coordinates + // directly in twips. + SwEditWin& rEditWin = pDocShell->GetView()->GetEditWin(); + rEditWin.EnableMapMode(false); + + // when the "This document may contain formatting or content that cannot + // be saved..." dialog appears, it is auto-cancelled with tiled rendering, + // causing 'Save' being disabled; so let's always save to the original + // format + SvtSaveOptions().SetWarnAlienFormat(false); + + // disable word auto-completion suggestions, the tooltips are not visible, + // and the editeng-like auto-completion is annoying + SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags().bAutoCompleteWords = false; + + // don't change the whitespace at the beginning of paragraphs, this is + // annoying when taking minutes without further formatting + SwEditShell::GetAutoFormatFlags()->bAFormatByInpDelSpacesAtSttEnd = false; +} + +void SwXTextDocument::postKeyEvent(int nType, int nCharCode, int nKeyCode) +{ + SolarMutexGuard aGuard; + SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode); +} + +void SwXTextDocument::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) +{ + SolarMutexGuard aGuard; + + SwViewShell* pWrtViewShell = pDocShell->GetWrtShell(); + SwViewOption aOption(*(pWrtViewShell->GetViewOptions())); + double fScale = aOption.GetZoom() / (TWIPS_PER_PIXEL * 100.0); + + // check if the user hit a chart which is being edited by this view + SfxViewShell* pViewShell = pDocShell->GetView(); + LokChartHelper aChartHelper(pViewShell); + if (aChartHelper.postMouseEvent(nType, nX, nY, + nCount, nButtons, nModifier, + fScale, fScale)) + return; + + // check if the user hit a chart which is being edited by someone else + // and, if so, skip current mouse event + if (nType != LOK_MOUSEEVENT_MOUSEMOVE) + { + if (LokChartHelper::HitAny(Point(nX, nY))) + return; + } + + SwEditWin& rEditWin = pDocShell->GetView()->GetEditWin(); + LokMouseEventData aMouseEventData(nType, Point(nX, nY), nCount, + MouseEventModifiers::SIMPLECLICK, + nButtons, nModifier); + SfxLokHelper::postMouseEventAsync(&rEditWin, aMouseEventData); +} + +void SwXTextDocument::setTextSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + SfxViewShell* pViewShell = pDocShell->GetView(); + LokChartHelper aChartHelper(pViewShell); + if (aChartHelper.setTextSelection(nType, nX, nY)) + return; + + SwEditWin& rEditWin = pDocShell->GetView()->GetEditWin(); + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + rEditWin.SetCursorTwipPosition(Point(nX, nY), /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + rEditWin.SetCursorTwipPosition(Point(nX, nY), /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + rEditWin.SetCursorTwipPosition(Point(nX, nY), /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } +} + +uno::Reference<datatransfer::XTransferable> SwXTextDocument::getSelection() +{ + SolarMutexGuard aGuard; + + uno::Reference<datatransfer::XTransferable> xTransferable; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + if (SdrView* pSdrView = pWrtShell->GetDrawView()) + { + if (pSdrView->GetTextEditObject()) + { + // Editing shape text + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + xTransferable = rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection()); + } + } + + if (SwPostItMgr* pPostItMgr = pDocShell->GetView()->GetPostItMgr()) + { + if (sw::annotation::SwAnnotationWin* pWin = pPostItMgr->GetActiveSidebarWin()) + { + // Editing postit text. + EditView& rEditView = pWin->GetOutlinerView()->GetEditView(); + xTransferable = rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection()); + } + } + + if (!xTransferable.is()) + xTransferable = new SwTransferable(*pWrtShell); + + return xTransferable; +} + +void SwXTextDocument::setGraphicSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + SwViewShell* pWrtViewShell = pDocShell->GetWrtShell(); + SwViewOption aOption(*(pWrtViewShell->GetViewOptions())); + double fScale = aOption.GetZoom() / (TWIPS_PER_PIXEL * 100.0); + + SfxViewShell* pViewShell = pDocShell->GetView(); + LokChartHelper aChartHelper(pViewShell); + if (aChartHelper.setGraphicSelection(nType, nX, nY, fScale, fScale)) + return; + + SwEditWin& rEditWin = pDocShell->GetView()->GetEditWin(); + switch (nType) + { + case LOK_SETGRAPHICSELECTION_START: + rEditWin.SetGraphicTwipPosition(/*bStart=*/true, Point(nX, nY)); + break; + case LOK_SETGRAPHICSELECTION_END: + rEditWin.SetGraphicTwipPosition(/*bStart=*/false, Point(nX, nY)); + break; + default: + assert(false); + break; + } +} + +void SwXTextDocument::resetSelection() +{ + SolarMutexGuard aGuard; + + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->ResetSelect(nullptr, false); +} + +void SAL_CALL SwXTextDocument::paintTile( const ::css::uno::Any& Parent, ::sal_Int32 nOutputWidth, ::sal_Int32 nOutputHeight, ::sal_Int32 nTilePosX, ::sal_Int32 nTilePosY, ::sal_Int32 nTileWidth, ::sal_Int32 nTileHeight ) +{ + SystemGraphicsData aData; + aData.nSize = sizeof(SystemGraphicsData); + #if defined(_WIN32) + sal_Int64 nWindowHandle; + Parent >>= nWindowHandle; + aData.hWnd = reinterpret_cast<HWND>(nWindowHandle); + ScopedVclPtrInstance<VirtualDevice> xDevice(aData, Size(1, 1), DeviceFormat::DEFAULT); + paintTile(*xDevice, nOutputWidth, nOutputHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight); + #else + // TODO: support other platforms + (void)Parent; + (void)nOutputWidth; + (void)nOutputHeight; + (void)nTilePosX; + (void)nTilePosY; + (void)nTileWidth; + (void)nTileHeight; + #endif +} + +/** + * retrieve languages already used in current document + */ +uno::Sequence< lang::Locale > SAL_CALL SwXTextDocument::getDocumentLanguages( + ::sal_Int16 nScriptTypes, + ::sal_Int16 nMaxCount ) +{ + SolarMutexGuard aGuard; + + // possible canonical values for nScriptTypes + // any bit wise combination is allowed + const sal_Int16 nLatin = 0x001; + const sal_Int16 nAsian = 0x002; + const sal_Int16 nComplex = 0x004; + + // script types for which to get the languages + const bool bLatin = 0 != (nScriptTypes & nLatin); + const bool bAsian = 0 != (nScriptTypes & nAsian); + const bool bComplex = 0 != (nScriptTypes & nComplex); + + if (nScriptTypes < nLatin || nScriptTypes > (nLatin | nAsian | nComplex)) + throw IllegalArgumentException("nScriptTypes ranges from 1 to 7!", Reference< XInterface >(), 1); + if (!pDocShell) + throw DisposedException(); + SwDoc* pDoc = pDocShell->GetDoc(); + + // avoid duplicate values + std::set< LanguageType > aAllLangs; + + //USER STYLES + + const SwCharFormats *pFormats = pDoc->GetCharFormats(); + for(size_t i = 0; i < pFormats->size(); ++i) + { + const SwAttrSet &rAttrSet = (*pFormats)[i]->GetAttrSet(); + LanguageType nLang = LANGUAGE_DONTKNOW; + if (bLatin) + { + nLang = rAttrSet.GetLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bAsian) + { + nLang = rAttrSet.GetCJKLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bComplex) + { + nLang = rAttrSet.GetCTLLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + } + + const SwTextFormatColls *pColls = pDoc->GetTextFormatColls(); + for (size_t i = 0; i < pColls->size(); ++i) + { + const SwAttrSet &rAttrSet = (*pColls)[i]->GetAttrSet(); + LanguageType nLang = LANGUAGE_DONTKNOW; + if (bLatin) + { + nLang = rAttrSet.GetLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bAsian) + { + nLang = rAttrSet.GetCJKLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bComplex) + { + nLang = rAttrSet.GetCTLLanguage( false ).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + } + + //AUTO STYLES + const IStyleAccess::SwAutoStyleFamily aFam[2] = + { + IStyleAccess::AUTO_STYLE_CHAR, + IStyleAccess::AUTO_STYLE_PARA + }; + for (IStyleAccess::SwAutoStyleFamily i : aFam) + { + std::vector< std::shared_ptr<SfxItemSet> > rStyles; + pDoc->GetIStyleAccess().getAllStyles(rStyles, i); + while (!rStyles.empty()) + { + std::shared_ptr<SfxItemSet> pStyle = rStyles.back(); + rStyles.pop_back(); + const SfxItemSet *pSet = dynamic_cast< const SfxItemSet * >(pStyle.get()); + + LanguageType nLang = LANGUAGE_DONTKNOW; + if (bLatin) + { + assert(pSet); + nLang = dynamic_cast< const SvxLanguageItem & >(pSet->Get( RES_CHRATR_LANGUAGE, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bAsian) + { + assert(pSet); + nLang = dynamic_cast< const SvxLanguageItem & >(pSet->Get( RES_CHRATR_CJK_LANGUAGE, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bComplex) + { + assert(pSet); + nLang = dynamic_cast< const SvxLanguageItem & >(pSet->Get( RES_CHRATR_CTL_LANGUAGE, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + } + } + + //TODO/mba: it's a strange concept that a view is needed to retrieve core data + SwWrtShell *pWrtSh = pDocShell->GetWrtShell(); + SdrView *pSdrView = pWrtSh->GetDrawView(); + + if( pSdrView ) + { + SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner(); + if(pOutliner) + { + EditEngine& rEditEng = const_cast<EditEngine&>(pOutliner->GetEditEngine()); + sal_Int32 nParCount = pOutliner->GetParagraphCount(); + for (sal_Int32 nPar=0; nPar<nParCount; nPar++) + { + //every paragraph + std::vector<sal_Int32> aPortions; + rEditEng.GetPortions( nPar, aPortions ); + + for ( size_t nPos = aPortions.size(); nPos; ) + { + //every position + --nPos; + sal_Int32 nEnd = aPortions[ nPos ]; + sal_Int32 nStart = nPos ? aPortions[ nPos - 1 ] : 0; + ESelection aSelection( nPar, nStart, nPar, nEnd ); + SfxItemSet aAttr = rEditEng.GetAttribs( aSelection ); + + LanguageType nLang = LANGUAGE_DONTKNOW; + if (bLatin) + { + nLang = dynamic_cast< const SvxLanguageItem & >(aAttr.Get( EE_CHAR_LANGUAGE, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bAsian) + { + nLang = dynamic_cast< const SvxLanguageItem & >(aAttr.Get( EE_CHAR_LANGUAGE_CJK, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + if (bComplex) + { + nLang = dynamic_cast< const SvxLanguageItem & >(aAttr.Get( EE_CHAR_LANGUAGE_CTL, false )).GetLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aAllLangs.insert( nLang ); + } + } + } + } + } + // less than nMaxCount languages + if (nMaxCount > static_cast< sal_Int16 >( aAllLangs.size() )) + nMaxCount = static_cast< sal_Int16 >( aAllLangs.size() ); + + // build return value + uno::Sequence< lang::Locale > aLanguages( nMaxCount ); + lang::Locale* pLanguage = aLanguages.getArray(); + if (nMaxCount > 0) + { + sal_Int32 nCount = 0; + for (const auto& rLang : aAllLangs) + { + if (nCount >= nMaxCount) + break; + if (LANGUAGE_NONE != rLang) + { + pLanguage[nCount] = LanguageTag::convertToLocale( rLang ); + pLanguage[nCount].Language = SvtLanguageTable::GetLanguageString( rLang ); + nCount += 1; + } + } + } + + return aLanguages; +} + +SwXLinkTargetSupplier::SwXLinkTargetSupplier(SwXTextDocument& rxDoc) : + pxDoc(&rxDoc) +{ + sTables = SwResId(STR_CONTENT_TYPE_TABLE); + sFrames = SwResId(STR_CONTENT_TYPE_FRAME); + sGraphics = SwResId(STR_CONTENT_TYPE_GRAPHIC); + sOLEs = SwResId(STR_CONTENT_TYPE_OLE); + sSections = SwResId(STR_CONTENT_TYPE_REGION); + sOutlines = SwResId(STR_CONTENT_TYPE_OUTLINE); + sBookmarks = SwResId(STR_CONTENT_TYPE_BOOKMARK); +} + +SwXLinkTargetSupplier::~SwXLinkTargetSupplier() +{ +} + +Any SwXLinkTargetSupplier::getByName(const OUString& rName) +{ + Any aRet; + if(!pxDoc) + throw RuntimeException("No document available"); + OUString sSuffix("|"); + if(rName == sTables) + { + sSuffix += "table"; + + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + pxDoc->getTextTables(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sFrames) + { + sSuffix += "frame"; + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + pxDoc->getTextFrames(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sSections) + { + sSuffix += "region"; + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + pxDoc->getTextSections(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sGraphics) + { + sSuffix += "graphic"; + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + pxDoc->getGraphicObjects(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sOLEs) + { + sSuffix += "ole"; + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + pxDoc->getEmbeddedObjects(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sOutlines) + { + sSuffix += "outline"; + Reference< XNameAccess > xTables = new SwXLinkNameAccessWrapper( + *pxDoc, rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xTables, UNO_QUERY); + } + else if(rName == sBookmarks) + { + sSuffix.clear(); + Reference< XNameAccess > xBkms = new SwXLinkNameAccessWrapper( + pxDoc->getBookmarks(), rName, sSuffix ); + aRet <<= Reference< XPropertySet >(xBkms, UNO_QUERY); + } + else + throw NoSuchElementException(); + return aRet; +} + +Sequence< OUString > SwXLinkTargetSupplier::getElementNames() +{ + return { sTables, + sFrames, + sGraphics, + sOLEs, + sSections, + sOutlines, + sBookmarks }; +} + +sal_Bool SwXLinkTargetSupplier::hasByName(const OUString& rName) +{ + if( rName == sTables || + rName == sFrames || + rName == sGraphics|| + rName == sOLEs || + rName == sSections || + rName == sOutlines || + rName == sBookmarks ) + return true; + return false; +} + +uno::Type SwXLinkTargetSupplier::getElementType() +{ + return cppu::UnoType<XPropertySet>::get(); + +} + +sal_Bool SwXLinkTargetSupplier::hasElements() +{ + return nullptr != pxDoc; +} + +OUString SwXLinkTargetSupplier::getImplementationName() +{ + return "SwXLinkTargetSupplier"; +} + +sal_Bool SwXLinkTargetSupplier::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXLinkTargetSupplier::getSupportedServiceNames() +{ + Sequence< OUString > aRet { "com.sun.star.document.LinkTargets" }; + return aRet; +} + +SwXLinkNameAccessWrapper::SwXLinkNameAccessWrapper( + Reference< XNameAccess > const & xAccess, const OUString& rLinkDisplayName, const OUString& sSuffix ) : + xRealAccess(xAccess), + pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_LINK_TARGET)), + sLinkSuffix(sSuffix), + sLinkDisplayName(rLinkDisplayName), + pxDoc(nullptr) +{ +} + +SwXLinkNameAccessWrapper::SwXLinkNameAccessWrapper(SwXTextDocument& rxDoc, + const OUString& rLinkDisplayName, const OUString& sSuffix) : + pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_LINK_TARGET)), + sLinkSuffix(sSuffix), + sLinkDisplayName(rLinkDisplayName), + pxDoc(&rxDoc) +{ +} + +SwXLinkNameAccessWrapper::~SwXLinkNameAccessWrapper() +{ +} + +Any SwXLinkNameAccessWrapper::getByName(const OUString& rName) +{ + Any aRet; + bool bFound = false; + //cut link extension and call the real NameAccess + OUString sParam = rName; + OUString sSuffix(sLinkSuffix); + if(sParam.getLength() > sSuffix.getLength() ) + { + OUString sCmp = sParam.copy(sParam.getLength() - sSuffix.getLength(), + sSuffix.getLength()); + if(sCmp == sSuffix) + { + if(pxDoc) + { + sParam = sParam.copy(0, sParam.getLength() - sSuffix.getLength()); + if(!pxDoc->GetDocShell()) + throw RuntimeException("No document shell available"); + SwDoc* pDoc = pxDoc->GetDocShell()->GetDoc(); + const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size(); + + for (size_t i = 0; i < nOutlineCount && !bFound; ++i) + { + const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds(); + const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule(); + if(sParam == lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule)) + { + Reference< XPropertySet > xOutline = new SwXOutlineTarget(sParam); + aRet <<= xOutline; + bFound = true; + } + } + } + else + { + aRet = xRealAccess->getByName(sParam.copy(0, sParam.getLength() - sSuffix.getLength())); + Reference< XInterface > xInt; + if(!(aRet >>= xInt)) + throw RuntimeException("Could not retrieve property"); + Reference< XPropertySet > xProp(xInt, UNO_QUERY); + aRet <<= xProp; + bFound = true; + } + } + } + if(!bFound) + throw NoSuchElementException(); + return aRet; +} + +Sequence< OUString > SwXLinkNameAccessWrapper::getElementNames() +{ + Sequence< OUString > aRet; + if(pxDoc) + { + if(!pxDoc->GetDocShell()) + throw RuntimeException("No document shell available"); + + SwDoc* pDoc = pxDoc->GetDocShell()->GetDoc(); + const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds(); + const size_t nOutlineCount = rOutlineNodes.size(); + aRet.realloc(nOutlineCount); + OUString* pResArr = aRet.getArray(); + const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule(); + for (size_t i = 0; i < nOutlineCount; ++i) + { + OUString sEntry = lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule) + "|outline"; + pResArr[i] = sEntry; + } + } + else + { + Sequence< OUString > aOrg = xRealAccess->getElementNames(); + aRet.realloc(aOrg.getLength()); + std::transform(aOrg.begin(), aOrg.end(), aRet.begin(), + [this](const OUString& rOrg) -> OUString { return rOrg + sLinkSuffix; }); + } + return aRet; +} + +sal_Bool SwXLinkNameAccessWrapper::hasByName(const OUString& rName) +{ + bool bRet = false; + OUString sParam(rName); + if(sParam.getLength() > sLinkSuffix.getLength() ) + { + OUString sCmp = sParam.copy(sParam.getLength() - sLinkSuffix.getLength(), + sLinkSuffix.getLength()); + if(sCmp == sLinkSuffix) + { + sParam = sParam.copy(0, sParam.getLength() - sLinkSuffix.getLength()); + if(pxDoc) + { + if(!pxDoc->GetDocShell()) + throw RuntimeException("No document shell available"); + SwDoc* pDoc = pxDoc->GetDocShell()->GetDoc(); + const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size(); + + for (size_t i = 0; i < nOutlineCount && !bRet; ++i) + { + const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds(); + const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule(); + if(sParam == + lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule)) + { + bRet = true; + } + } + } + else + { + bRet = xRealAccess->hasByName(sParam); + } + } + } + return bRet; +} + +uno::Type SwXLinkNameAccessWrapper::getElementType() +{ + return cppu::UnoType<XPropertySet>::get(); +} + +sal_Bool SwXLinkNameAccessWrapper::hasElements() +{ + bool bRet = false; + if(pxDoc) + { + OSL_FAIL("not implemented"); + } + else + { + bRet = xRealAccess->hasElements(); + } + return bRet; +} + +Reference< XPropertySetInfo > SwXLinkNameAccessWrapper::getPropertySetInfo() +{ + static Reference< XPropertySetInfo > xRet = pPropSet->getPropertySetInfo(); + return xRet; +} + +void SwXLinkNameAccessWrapper::setPropertyValue( + const OUString& rPropName, const Any& ) +{ + throw UnknownPropertyException(rPropName); +} + +static Any lcl_GetDisplayBitmap(const OUString& _sLinkSuffix) +{ + Any aRet; + OUString sLinkSuffix = _sLinkSuffix; + if(!sLinkSuffix.isEmpty()) + sLinkSuffix = sLinkSuffix.copy(1); + OUString sImgId; + + if(sLinkSuffix == "outline") + sImgId = RID_BMP_NAVI_OUTLINE; + else if(sLinkSuffix == "table") + sImgId = RID_BMP_NAVI_TABLE; + else if(sLinkSuffix == "frame") + sImgId = RID_BMP_NAVI_FRAME; + else if(sLinkSuffix == "graphic") + sImgId = RID_BMP_NAVI_GRAPHIC; + else if(sLinkSuffix == "ole") + sImgId = RID_BMP_NAVI_OLE; + else if(sLinkSuffix.isEmpty()) + sImgId = RID_BMP_NAVI_BOOKMARK; + else if(sLinkSuffix == "region") + sImgId = RID_BMP_NAVI_REGION; + + if (!sImgId.isEmpty()) + { + aRet <<= VCLUnoHelper::CreateBitmap(BitmapEx(sImgId)); + } + return aRet; +} + +Any SwXLinkNameAccessWrapper::getPropertyValue(const OUString& rPropertyName) +{ + Any aRet; + if( rPropertyName == UNO_LINK_DISPLAY_NAME ) + { + aRet <<= sLinkDisplayName; + } + else if( rPropertyName == UNO_LINK_DISPLAY_BITMAP ) + { + aRet = lcl_GetDisplayBitmap(sLinkSuffix); + } + else + throw UnknownPropertyException(rPropertyName); + return aRet; +} + +void SwXLinkNameAccessWrapper::addPropertyChangeListener( + const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/) +{} + +void SwXLinkNameAccessWrapper::removePropertyChangeListener( + const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/) +{} + +void SwXLinkNameAccessWrapper::addVetoableChangeListener( + const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/) +{} + +void SwXLinkNameAccessWrapper::removeVetoableChangeListener( + const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/) +{} + +Reference< XNameAccess > SwXLinkNameAccessWrapper::getLinks() +{ + return this; +} + +OUString SwXLinkNameAccessWrapper::getImplementationName() +{ + return "SwXLinkNameAccessWrapper"; +} + +sal_Bool SwXLinkNameAccessWrapper::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXLinkNameAccessWrapper::getSupportedServiceNames() +{ + Sequence< OUString > aRet { "com.sun.star.document.LinkTargets" }; + return aRet; +} + +SwXOutlineTarget::SwXOutlineTarget(const OUString& rOutlineText) : + pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_LINK_TARGET)), + sOutlineText(rOutlineText) +{ +} + +SwXOutlineTarget::~SwXOutlineTarget() +{ +} + +Reference< XPropertySetInfo > SwXOutlineTarget::getPropertySetInfo() +{ + static Reference< XPropertySetInfo > xRet = pPropSet->getPropertySetInfo(); + return xRet; +} + +void SwXOutlineTarget::setPropertyValue( + const OUString& rPropertyName, const Any& /*aValue*/) +{ + throw UnknownPropertyException(rPropertyName); +} + +Any SwXOutlineTarget::getPropertyValue(const OUString& rPropertyName) +{ + if(rPropertyName != UNO_LINK_DISPLAY_NAME) + throw UnknownPropertyException(rPropertyName); + + return Any(sOutlineText); +} + +void SwXOutlineTarget::addPropertyChangeListener( + const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/) +{ +} + +void SwXOutlineTarget::removePropertyChangeListener( + const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/) +{ +} + +void SwXOutlineTarget::addVetoableChangeListener( + const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/) +{ +} + +void SwXOutlineTarget::removeVetoableChangeListener( + const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/) +{ +} + +OUString SwXOutlineTarget::getImplementationName() +{ + return "SwXOutlineTarget"; +} + +sal_Bool SwXOutlineTarget::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SwXOutlineTarget::getSupportedServiceNames() +{ + Sequence<OUString> aRet { "com.sun.star.document.LinkTarget" }; + + return aRet; +} + +SwXDocumentPropertyHelper::SwXDocumentPropertyHelper(SwDoc& rDoc) : +SvxUnoForbiddenCharsTable ( rDoc.getIDocumentSettingAccess().getForbiddenCharacterTable() ) +,m_pDoc(&rDoc) +{ +} + +SwXDocumentPropertyHelper::~SwXDocumentPropertyHelper() +{ +} + +Reference<XInterface> SwXDocumentPropertyHelper::GetDrawTable(SwCreateDrawTable nWhich) +{ + Reference<XInterface> xRet; + if(m_pDoc) + { + switch(nWhich) + { + // #i52858# + // assure that Draw model is created, if it doesn't exist. + case SwCreateDrawTable::Dash : + if(!xDashTable.is()) + xDashTable = SvxUnoDashTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xDashTable; + break; + case SwCreateDrawTable::Gradient : + if(!xGradientTable.is()) + xGradientTable = SvxUnoGradientTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xGradientTable; + break; + case SwCreateDrawTable::Hatch : + if(!xHatchTable.is()) + xHatchTable = SvxUnoHatchTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xHatchTable; + break; + case SwCreateDrawTable::Bitmap : + if(!xBitmapTable.is()) + xBitmapTable = SvxUnoBitmapTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xBitmapTable; + break; + case SwCreateDrawTable::TransGradient: + if(!xTransGradientTable.is()) + xTransGradientTable = SvxUnoTransGradientTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xTransGradientTable; + break; + case SwCreateDrawTable::Marker : + if(!xMarkerTable.is()) + xMarkerTable = SvxUnoMarkerTable_createInstance( m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel() ); + xRet = xMarkerTable; + break; + case SwCreateDrawTable::Defaults: + if(!xDrawDefaults.is()) + xDrawDefaults = static_cast<cppu::OWeakObject*>(new SwSvxUnoDrawPool(m_pDoc)); + xRet = xDrawDefaults; + break; +#if OSL_DEBUG_LEVEL > 0 + default: OSL_FAIL("which table?"); +#endif + } + } + return xRet; +} + +void SwXDocumentPropertyHelper::Invalidate() +{ + xDashTable = nullptr; + xGradientTable = nullptr; + xHatchTable = nullptr; + xBitmapTable = nullptr; + xTransGradientTable = nullptr; + xMarkerTable = nullptr; + xDrawDefaults = nullptr; + m_pDoc = nullptr; + SvxUnoForbiddenCharsTable::mxForbiddenChars.reset(); +} + +void SwXDocumentPropertyHelper::onChange() +{ + if(m_pDoc) + m_pDoc->getIDocumentState().SetModified(); +} + +SwViewOptionAdjust_Impl::SwViewOptionAdjust_Impl( + SwViewShell& rSh, const SwViewOption &rViewOptions) + : m_pShell(&rSh) + , m_aOldViewOptions( rViewOptions ) +{ +} + +SwViewOptionAdjust_Impl::~SwViewOptionAdjust_Impl() +{ + if (m_pShell) + { + m_pShell->ApplyViewOptions( m_aOldViewOptions ); + } +} + +void +SwViewOptionAdjust_Impl::AdjustViewOptions(SwPrintData const*const pPrtOptions, bool setShowPlaceHoldersInPDF) +{ + // to avoid unnecessary reformatting the view options related to the content + // below should only change if necessary, that is if respective content is present + const bool bContainsHiddenChars = m_pShell->GetDoc()->ContainsHiddenChars(); + const SwFieldType* pFieldType = m_pShell->GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ); + const bool bContainsHiddenFields = pFieldType && pFieldType->HasWriterListeners(); + pFieldType = m_pShell->GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara ); + const bool bContainsHiddenParagraphs = pFieldType && pFieldType->HasWriterListeners(); + pFieldType = m_pShell->GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::JumpEdit ); + const bool bContainsPlaceHolders = pFieldType && pFieldType->HasWriterListeners(); + const bool bContainsFields = m_pShell->IsAnyFieldInDoc(); + + SwViewOption aRenderViewOptions( m_aOldViewOptions ); + + // disable anything in the view that should not be printed (or exported to PDF) by default + // (see also dialog "Tools/Options - StarOffice Writer - Formatting Aids" + // in section "Display of ...") + aRenderViewOptions.SetParagraph( false ); // paragraph end + aRenderViewOptions.SetSoftHyph( false ); // aka custom hyphens + aRenderViewOptions.SetBlank( false ); // spaces + aRenderViewOptions.SetHardBlank( false ); // non-breaking spaces + aRenderViewOptions.SetTab( false ); // tabs + aRenderViewOptions.SetShowBookmarks( false ); // bookmarks + aRenderViewOptions.SetLineBreak( false ); // breaks (type 1) + aRenderViewOptions.SetPageBreak( false ); // breaks (type 2) + aRenderViewOptions.SetColumnBreak( false ); // breaks (type 3) + bool bVal = pPrtOptions && pPrtOptions->m_bPrintHiddenText; + if (bContainsHiddenChars) + aRenderViewOptions.SetShowHiddenChar( bVal ); // hidden text + if (bContainsHiddenFields) + aRenderViewOptions.SetShowHiddenField( bVal ); + if (bContainsHiddenParagraphs) + aRenderViewOptions.SetShowHiddenPara( bVal ); + + if (bContainsPlaceHolders) + { + // should always be printed in PDF export! + bVal = !pPrtOptions ? setShowPlaceHoldersInPDF : pPrtOptions->m_bPrintTextPlaceholder; + aRenderViewOptions.SetShowPlaceHolderFields( bVal ); + } + + if (bContainsFields) + aRenderViewOptions.SetFieldName( false ); + + // we need to set this flag in order to get to see the visible effect of + // some of the above settings (needed for correct rendering) + aRenderViewOptions.SetViewMetaChars( true ); + + if (m_aOldViewOptions != aRenderViewOptions) // check if reformatting is necessary + { + aRenderViewOptions.SetPrinting( pPrtOptions != nullptr ); + m_pShell->ApplyViewOptions( aRenderViewOptions ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unotxvw.cxx b/sw/source/uibase/uno/unotxvw.cxx new file mode 100644 index 000000000..b83a52500 --- /dev/null +++ b/sw/source/uibase/uno/unotxvw.cxx @@ -0,0 +1,1756 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <viscrs.hxx> +#include <o3tl/any.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/viewfrm.hxx> +#include <cmdid.h> +#include <docsh.hxx> +#include <rubylist.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <unotxvw.hxx> +#include <unodispatch.hxx> +#include <unomap.hxx> +#include <unoprnms.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <unomod.hxx> +#include <unoframe.hxx> +#include <unocrsr.hxx> +#include <wrtsh.hxx> +#include <unotbl.hxx> +#include <svx/fmshell.hxx> +#include <svx/svdview.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdouno.hxx> +#include <editeng/pbinitem.hxx> +#include <pagedesc.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <frmatr.hxx> +#include <IMark.hxx> +#include <unodraw.hxx> +#include <svx/svdpagv.hxx> +#include <ndtxt.hxx> +#include <SwStyleNameMapper.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <unoparagraph.hxx> +#include <unocrsrhelper.hxx> +#include <unotextrange.hxx> +#include <sfx2/docfile.hxx> +#include <swdtflvr.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/profilezone.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.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::view; +using namespace ::com::sun::star::frame; + +using ::com::sun::star::util::URL; + +SwXTextView::SwXTextView(SwView* pSwView) : + SfxBaseController(pSwView), + m_SelChangedListeners(m_aMutex), + m_pView(pSwView), + m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_VIEW ) ), + mxViewSettings(), + mxTextViewCursor() +{ + +} + +SwXTextView::~SwXTextView() +{ + Invalidate(); +} + +void SwXTextView::Invalidate() +{ + if(mxViewSettings.is()) + { + comphelper::ChainablePropertySet *pSettings = static_cast < comphelper::ChainablePropertySet * > ( mxViewSettings.get() ); + static_cast < SwXViewSettings* > ( pSettings )->Invalidate(); + mxViewSettings.clear(); + } + if(mxTextViewCursor.is()) + { + text::XTextViewCursor* pCursor = mxTextViewCursor.get(); + static_cast<SwXTextViewCursor*>(pCursor)->Invalidate(); + mxTextViewCursor.clear(); + } + + osl_atomic_increment(&m_refCount); //prevent second d'tor call + + { + uno::Reference<uno::XInterface> const xInt(static_cast< + cppu::OWeakObject*>(static_cast<SfxBaseController*>(this))); + lang::EventObject aEvent(xInt); + m_SelChangedListeners.disposeAndClear(aEvent); + } + + osl_atomic_decrement(&m_refCount); + m_pView = nullptr; +} + +Sequence< uno::Type > SAL_CALL SwXTextView::getTypes( ) +{ + return cppu::OTypeCollection( + cppu::UnoType<XSelectionSupplier>::get(), + cppu::UnoType<XServiceInfo>::get(), + cppu::UnoType<XFormLayerAccess>::get(), + cppu::UnoType<XTextViewCursorSupplier>::get(), + cppu::UnoType<XViewSettingsSupplier>::get(), + cppu::UnoType<XRubySelection>::get(), + cppu::UnoType<XPropertySet>::get(), + cppu::UnoType<datatransfer::XTransferableSupplier>::get(), + SfxBaseController::getTypes() + ).getTypes(); +} + +Sequence< sal_Int8 > SAL_CALL SwXTextView::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +void SAL_CALL SwXTextView::acquire( )throw() +{ + SfxBaseController::acquire(); +} + +void SAL_CALL SwXTextView::release( )throw() +{ + SfxBaseController::release(); +} + +uno::Any SAL_CALL SwXTextView::queryInterface( const uno::Type& aType ) +{ + uno::Any aRet; + if(aType == cppu::UnoType<view::XSelectionSupplier>::get()) + { + uno::Reference<view::XSelectionSupplier> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<lang::XServiceInfo>::get()) + { + uno::Reference<lang::XServiceInfo> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<view::XControlAccess>::get()) + { + uno::Reference<view::XControlAccess> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<view::XFormLayerAccess>::get()) + { + uno::Reference<view::XFormLayerAccess> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<text::XTextViewCursorSupplier>::get()) + { + uno::Reference<text::XTextViewCursorSupplier> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<view::XViewSettingsSupplier>::get()) + { + uno::Reference<view::XViewSettingsSupplier> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<XRubySelection>::get()) + { + uno::Reference<XRubySelection> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<XPropertySet>::get()) + { + uno::Reference<XPropertySet> xRet = this; + aRet <<= xRet; + } + else if(aType == cppu::UnoType<datatransfer::XTransferableSupplier>::get()) + { + uno::Reference<datatransfer::XTransferableSupplier> xRet = this; + aRet <<= xRet; + } + else + aRet = SfxBaseController::queryInterface(aType); + return aRet; +} + +sal_Bool SwXTextView::select(const uno::Any& aInterface) +{ + SolarMutexGuard aGuard; + + uno::Reference< uno::XInterface > xInterface; + if (!GetView() || !(aInterface >>= xInterface)) + { + return false; + } + + SwWrtShell& rSh = GetView()->GetWrtShell(); + SwDoc* pDoc = GetView()->GetDocShell()->GetDoc(); + std::vector<SdrObject *> sdrObjects; + uno::Reference<awt::XControlModel> const xCtrlModel(xInterface, + UNO_QUERY); + if (xCtrlModel.is()) + { + uno::Reference<awt::XControl> xControl; + SdrObject *const pSdrObject = GetControl(xCtrlModel, xControl); + if (pSdrObject) // hmm... needs view to verify it's in right doc... + { + sdrObjects.push_back(pSdrObject); + } + } + else + { + SwPaM * pPaM(nullptr); + std::pair<OUString, FlyCntType> frame; + OUString tableName; + SwUnoTableCursor const* pTableCursor(nullptr); + ::sw::mark::IMark const* pMark(nullptr); + SwUnoCursorHelper::GetSelectableFromAny(xInterface, *pDoc, + pPaM, frame, tableName, pTableCursor, pMark, sdrObjects); + if (pPaM) + { + rSh.EnterStdMode(); + rSh.SetSelection(*pPaM); + // the pPaM has been copied - delete it + while (pPaM->GetNext() != pPaM) + delete pPaM->GetNext(); + delete pPaM; + return true; + } + else if (!frame.first.isEmpty()) + { + bool const bSuccess(rSh.GotoFly(frame.first, frame.second)); + if (bSuccess) + { + rSh.HideCursor(); + rSh.EnterSelFrameMode(); + } + return true; + } + else if (!tableName.isEmpty()) + { + rSh.EnterStdMode(); + rSh.GotoTable(tableName); + return true; + } + else if (pTableCursor) + { + UnoActionRemoveContext const aContext(*pTableCursor); + rSh.EnterStdMode(); + rSh.SetSelection(*pTableCursor); + return true; + } + else if (pMark) + { + rSh.EnterStdMode(); + rSh.GotoMark(pMark); + return true; + } + // sdrObjects handled below + } + bool bRet(false); + if (!sdrObjects.empty()) + { + + SdrView *const pDrawView = rSh.GetDrawView(); + SdrPageView *const pPV = pDrawView->GetSdrPageView(); + + pDrawView->SdrEndTextEdit(); + pDrawView->UnmarkAll(); + + for (SdrObject* pSdrObject : sdrObjects) + { + // GetSelectableFromAny did not check pSdrObject is in right doc! + if (pPV && pSdrObject->getSdrPageFromSdrObject() == pPV->GetPage()) + { + pDrawView->MarkObj(pSdrObject, pPV); + bRet = true; + } + } + + // tdf#112696 if we selected every individual element of a group, then + // select that group instead + const SdrMarkList &rMrkList = pDrawView->GetMarkedObjectList(); + size_t nMarkCount = rMrkList.GetMarkCount(); + if (nMarkCount > 1) + { + SdrObject* pObject = rMrkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pGroupParent = pObject->getParentSdrObjectFromSdrObject(); + for (size_t i = 1; i < nMarkCount; ++i) + { + pObject = rMrkList.GetMark(i)->GetMarkedSdrObj(); + SdrObject* pParent = pObject->getParentSdrObjectFromSdrObject(); + if (pParent != pGroupParent) + { + pGroupParent = nullptr; + break; + } + } + + if (pGroupParent && pGroupParent->IsGroupObject() && + pGroupParent->getChildrenOfSdrObject()->GetObjCount() == nMarkCount) + { + pDrawView->UnmarkAll(); + pDrawView->MarkObj(pGroupParent, pPV); + } + } + } + return bRet; +} + +uno::Any SwXTextView::getSelection() +{ + SolarMutexGuard aGuard; + uno::Reference< uno::XInterface > aRef; + if(GetView()) + { + //force immediat shell update + m_pView->StopShellTimer(); + //Generating an interface from the current selection. + SwWrtShell& rSh = m_pView->GetWrtShell(); + ShellMode eSelMode = m_pView->GetShellMode(); + switch(eSelMode) + { + case ShellMode::TableText : + { + if(rSh.GetTableCursor()) + { + OSL_ENSURE(rSh.GetTableFormat(), "not a table format?"); + uno::Reference< text::XTextTableCursor > xCursor = new SwXTextTableCursor(*rSh.GetTableFormat(), + rSh.GetTableCursor()); + aRef.set(xCursor, uno::UNO_QUERY); + break; + } + [[fallthrough]]; + // without a table selection the text will be delivered + } + case ShellMode::ListText : + case ShellMode::TableListText: + case ShellMode::Text : + { + uno::Reference< container::XIndexAccess > xPos = SwXTextRanges::Create(rSh.GetCursor()); + aRef.set(xPos, uno::UNO_QUERY); + } + break; + case ShellMode::Frame : + { + SwFrameFormat *const pFormat = rSh.GetFlyFrameFormat(); + if (pFormat) + { + aRef = SwXTextFrame::CreateXTextFrame( + *pFormat->GetDoc(), pFormat); + } + } + break; + case ShellMode::Graphic : + { + SwFrameFormat *const pFormat = rSh.GetFlyFrameFormat(); + if (pFormat) + { + aRef = SwXTextGraphicObject::CreateXTextGraphicObject( + *pFormat->GetDoc(), pFormat); + } + } + break; + case ShellMode::Object : + { + SwFrameFormat *const pFormat = rSh.GetFlyFrameFormat(); + if (pFormat) + { + aRef = SwXTextEmbeddedObject::CreateXTextEmbeddedObject( + *pFormat->GetDoc(), pFormat); + } + } + break; + case ShellMode::Draw : + case ShellMode::DrawForm : + case ShellMode::DrawText : + case ShellMode::Bezier : + { + uno::Reference< drawing::XShapes > xShCol = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + const SdrMarkList& rMarkList = rSh.GetDrawView()->GetMarkedObjectList(); + for(size_t i = 0; i < rMarkList.GetMarkCount(); ++i) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + uno::Reference<drawing::XShape> xShape = SwFmDrawPage::GetShape( pObj ); + xShCol->add(xShape); + } + aRef.set(xShCol, uno::UNO_QUERY); + } + break; + default:;//prevent warning + } + } + uno::Any aRet(&aRef, cppu::UnoType<uno::XInterface>::get()); + return aRet; +} + +void SwXTextView::addSelectionChangeListener( + const uno::Reference< view::XSelectionChangeListener > & rxListener) +{ + SolarMutexGuard aGuard; + m_SelChangedListeners.addInterface(rxListener); +} + +void SwXTextView::removeSelectionChangeListener( + const uno::Reference< view::XSelectionChangeListener > & rxListener) +{ + SolarMutexGuard aGuard; + m_SelChangedListeners.removeInterface(rxListener); +} + +SdrObject* SwXTextView::GetControl( + const uno::Reference< awt::XControlModel > & xModel, + uno::Reference< awt::XControl >& xToFill ) +{ + SwView* pView2 = GetView(); + FmFormShell* pFormShell = pView2 ? pView2->GetFormShell() : nullptr; + SdrView* pDrawView = pView2 ? pView2->GetDrawView() : nullptr; + vcl::Window* pWindow = pView2 ? pView2->GetWrtShell().GetWin() : nullptr; + + OSL_ENSURE( pFormShell && pDrawView && pWindow, "SwXTextView::GetControl: how could I?" ); + + SdrObject* pControl = nullptr; + if ( pFormShell && pDrawView && pWindow ) + pControl = pFormShell->GetFormControl( xModel, *pDrawView, *pWindow, xToFill ); + return pControl; +} + +uno::Reference< awt::XControl > SwXTextView::getControl(const uno::Reference< awt::XControlModel > & xModel) +{ + SolarMutexGuard aGuard; + uno::Reference< awt::XControl > xRet; + GetControl(xModel, xRet); + return xRet; +} + +uno::Reference< form::runtime::XFormController > SAL_CALL SwXTextView::getFormController( const uno::Reference< form::XForm >& Form ) +{ + SolarMutexGuard aGuard; + + SwView* pView2 = GetView(); + FmFormShell* pFormShell = pView2 ? pView2->GetFormShell() : nullptr; + SdrView* pDrawView = pView2 ? pView2->GetDrawView() : nullptr; + vcl::Window* pWindow = pView2 ? pView2->GetWrtShell().GetWin() : nullptr; + OSL_ENSURE( pFormShell && pDrawView && pWindow, "SwXTextView::getFormController: how could I?" ); + + uno::Reference< form::runtime::XFormController > xController; + if ( pFormShell && pDrawView && pWindow ) + xController = FmFormShell::GetFormController( Form, *pDrawView, *pWindow ); + return xController; +} + +sal_Bool SAL_CALL SwXTextView::isFormDesignMode( ) +{ + SolarMutexGuard aGuard; + SwView* pView2 = GetView(); + FmFormShell* pFormShell = pView2 ? pView2->GetFormShell() : nullptr; + return !pFormShell || pFormShell->IsDesignMode(); +} + +void SAL_CALL SwXTextView::setFormDesignMode( sal_Bool DesignMode ) +{ + SolarMutexGuard aGuard; + SwView* pView2 = GetView(); + FmFormShell* pFormShell = pView2 ? pView2->GetFormShell() : nullptr; + if ( pFormShell ) + pFormShell->SetDesignMode( DesignMode ); +} + +uno::Reference< text::XTextViewCursor > SwXTextView::getViewCursor() +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("getViewCursor"); + if(!GetView()) + throw uno::RuntimeException(); + + if(!mxTextViewCursor.is()) + { + mxTextViewCursor = new SwXTextViewCursor(GetView()); + } + return mxTextViewCursor; +} + +uno::Reference< beans::XPropertySet > SwXTextView::getViewSettings() +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw uno::RuntimeException(); + + if(!mxViewSettings.is()) + { + mxViewSettings = new SwXViewSettings( m_pView ); + } + + return mxViewSettings; +} + +Sequence< Sequence< PropertyValue > > SwXTextView::getRubyList( sal_Bool /*bAutomatic*/ ) +{ + SolarMutexGuard aGuard; + + if(!GetView()) + throw RuntimeException(); + SwWrtShell& rSh = m_pView->GetWrtShell(); + ShellMode eSelMode = m_pView->GetShellMode(); + if (eSelMode != ShellMode::ListText && + eSelMode != ShellMode::TableListText && + eSelMode != ShellMode::TableText && + eSelMode != ShellMode::Text ) + return Sequence< Sequence< PropertyValue > > (); + + SwRubyList aList; + + const sal_uInt16 nCount = SwDoc::FillRubyList( *rSh.GetCursor(), aList ); + Sequence< Sequence< PropertyValue > > aRet(nCount); + Sequence< PropertyValue >* pRet = aRet.getArray(); + OUString aString; + for(sal_uInt16 n = 0; n < nCount; n++) + { + const SwRubyListEntry* pEntry = aList[n].get(); + + const OUString& rEntryText = pEntry->GetText(); + const SwFormatRuby& rAttr = pEntry->GetRubyAttr(); + + pRet[n].realloc(6); + PropertyValue* pValues = pRet[n].getArray(); + pValues[0].Name = UNO_NAME_RUBY_BASE_TEXT; + pValues[0].Value <<= rEntryText; + pValues[1].Name = UNO_NAME_RUBY_TEXT; + pValues[1].Value <<= rAttr.GetText(); + pValues[2].Name = UNO_NAME_RUBY_CHAR_STYLE_NAME; + SwStyleNameMapper::FillProgName(rAttr.GetCharFormatName(), aString, SwGetPoolIdFromName::ChrFmt ); + pValues[2].Value <<= aString; + pValues[3].Name = UNO_NAME_RUBY_ADJUST; + pValues[3].Value <<= static_cast<sal_Int16>(rAttr.GetAdjustment()); + pValues[4].Name = UNO_NAME_RUBY_IS_ABOVE; + pValues[4].Value <<= !rAttr.GetPosition(); + pValues[5].Name = UNO_NAME_RUBY_POSITION; + pValues[5].Value <<= rAttr.GetPosition(); + } + return aRet; +} + +void SAL_CALL SwXTextView::setRubyList( + const Sequence< Sequence< PropertyValue > >& rRubyList, sal_Bool /*bAutomatic*/ ) +{ + SolarMutexGuard aGuard; + + if(!GetView() || !rRubyList.hasElements()) + throw RuntimeException(); + SwWrtShell& rSh = m_pView->GetWrtShell(); + ShellMode eSelMode = m_pView->GetShellMode(); + if (eSelMode != ShellMode::ListText && + eSelMode != ShellMode::TableListText && + eSelMode != ShellMode::TableText && + eSelMode != ShellMode::Text ) + throw RuntimeException(); + + SwRubyList aList; + + for(const Sequence<PropertyValue>& rPropList : rRubyList) + { + std::unique_ptr<SwRubyListEntry> pEntry(new SwRubyListEntry); + OUString sTmp; + for(const PropertyValue& rProperty : rPropList) + { + if(rProperty.Name == UNO_NAME_RUBY_BASE_TEXT) + { + rProperty.Value >>= sTmp; + pEntry->SetText(sTmp); + } + else if(rProperty.Name == UNO_NAME_RUBY_TEXT) + { + rProperty.Value >>= sTmp; + pEntry->GetRubyAttr().SetText(sTmp); + } + else if(rProperty.Name == UNO_NAME_RUBY_CHAR_STYLE_NAME) + { + if(rProperty.Value >>= sTmp) + { + OUString sName; + SwStyleNameMapper::FillUIName(sTmp, sName, SwGetPoolIdFromName::ChrFmt ); + const sal_uInt16 nPoolId = sName.isEmpty() ? 0 + : SwStyleNameMapper::GetPoolIdFromUIName(sName, + SwGetPoolIdFromName::ChrFmt ); + + pEntry->GetRubyAttr().SetCharFormatName( sName ); + pEntry->GetRubyAttr().SetCharFormatId( nPoolId ); + } + } + else if(rProperty.Name == UNO_NAME_RUBY_ADJUST) + { + sal_Int16 nTmp = 0; + if(rProperty.Value >>= nTmp) + pEntry->GetRubyAttr().SetAdjustment(static_cast<css::text::RubyAdjust>(nTmp)); + } + else if(rProperty.Name == UNO_NAME_RUBY_IS_ABOVE) + { + bool bValue = !rProperty.Value.hasValue() || + *o3tl::doAccess<bool>(rProperty.Value); + pEntry->GetRubyAttr().SetPosition(bValue ? 0 : 1); + } + else if(rProperty.Name == UNO_NAME_RUBY_POSITION) + { + sal_Int16 nTmp = 0; + if(rProperty.Value >>= nTmp) + pEntry->GetRubyAttr().SetPosition( nTmp ); + } + } + aList.push_back(std::move(pEntry)); + } + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + pDoc->SetRubyList( *rSh.GetCursor(), aList ); +} + +SfxObjectShellLock SwXTextView::BuildTmpSelectionDoc() +{ + SwWrtShell& rOldSh = m_pView->GetWrtShell(); + SfxPrinter *pPrt = rOldSh.getIDocumentDeviceAccess().getPrinter( false ); + SwDocShell* pDocSh; + SfxObjectShellLock xDocSh( pDocSh = new SwDocShell( /*pPrtDoc, */SfxObjectCreateMode::STANDARD ) ); + xDocSh->DoInitNew(); + SwDoc *const pTempDoc( pDocSh->GetDoc() ); + // #i103634#, #i112425#: do not expand numbering and fields on PDF export + pTempDoc->SetClipBoard(true); + rOldSh.FillPrtDoc(pTempDoc, pPrt); + SfxViewFrame* pDocFrame = SfxViewFrame::LoadHiddenDocument( *xDocSh, SFX_INTERFACE_NONE ); + SwView* pDocView = static_cast<SwView*>( pDocFrame->GetViewShell() ); + pDocView->AttrChangedNotify(nullptr);//So that SelectShell is called. + SwWrtShell* pSh = pDocView->GetWrtShellPtr(); + + IDocumentDeviceAccess& rIDDA = pSh->getIDocumentDeviceAccess(); + SfxPrinter* pTempPrinter = rIDDA.getPrinter( true ); + + const SwPageDesc& rCurPageDesc = rOldSh.GetPageDesc(rOldSh.GetCurPageDesc()); + + IDocumentDeviceAccess& rIDDA_old = rOldSh.getIDocumentDeviceAccess(); + + if( rIDDA_old.getPrinter( false ) ) + { + rIDDA.setJobsetup( *rIDDA_old.getJobsetup() ); + //#69563# if it isn't the same printer then the pointer has been invalidated! + pTempPrinter = rIDDA.getPrinter( true ); + } + + pTempPrinter->SetPaperBin(rCurPageDesc.GetMaster().GetPaperBin().GetValue()); + + return xDocSh; +} + +void SwXTextView::NotifySelChanged() +{ + OSL_ENSURE( m_pView, "view is missing" ); + + uno::Reference<uno::XInterface> const xInt( + static_cast<cppu::OWeakObject*>(static_cast<SfxBaseController*>(this))); + + lang::EventObject const aEvent(xInt); + m_SelChangedListeners.notifyEach( + &view::XSelectionChangeListener::selectionChanged, aEvent); +} + +namespace { + struct DispatchListener + { + URL const & m_rURL; + Sequence<PropertyValue> const& m_rSeq; + explicit DispatchListener(URL const& rURL, + Sequence<PropertyValue> const& rSeq) + : m_rURL(rURL), m_rSeq(rSeq) { } + void operator()(uno::Reference<XDispatch> const & xListener) const + { + xListener->dispatch(m_rURL, m_rSeq); + } + }; +} + +void SwXTextView::NotifyDBChanged() +{ + URL aURL; + aURL.Complete = OUString::createFromAscii(SwXDispatch::GetDBChangeURL()); + + m_SelChangedListeners.forEach<XDispatch>( + DispatchListener(aURL, Sequence<PropertyValue>(0))); +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXTextView::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + static uno::Reference< XPropertySetInfo > aRef = m_pPropSet->getPropertySetInfo(); + return aRef; +} + +void SAL_CALL SwXTextView::setPropertyValue( + const OUString& rPropertyName, const uno::Any& rValue ) +{ + SolarMutexGuard aGuard; + const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pEntry) + throw UnknownPropertyException(rPropertyName); + else if (pEntry->nFlags & PropertyAttribute::READONLY) + throw PropertyVetoException(); + else + { + switch (pEntry->nWID) + { + case WID_IS_HIDE_SPELL_MARKS : + // deprecated #i91949 + break; + case WID_IS_CONSTANT_SPELLCHECK : + { + bool bVal = false; + const SwViewOption *pOpt = m_pView->GetWrtShell().GetViewOptions(); + if (!pOpt || !(rValue >>= bVal)) + throw RuntimeException(); + SwViewOption aNewOpt( *pOpt ); + if (pEntry->nWID == WID_IS_CONSTANT_SPELLCHECK) + aNewOpt.SetOnlineSpell(bVal); + m_pView->GetWrtShell().ApplyViewOptions( aNewOpt ); + } + break; + default : + OSL_FAIL("unknown WID"); + } + } +} + +uno::Any SAL_CALL SwXTextView::getPropertyValue( + const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + + Any aRet; + + const SfxItemPropertySimpleEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName ); + if (!pEntry) + throw UnknownPropertyException(rPropertyName); + + sal_Int16 nWID = pEntry->nWID; + switch (nWID) + { + case WID_PAGE_COUNT : + case WID_LINE_COUNT : + { + // format document completely in order to get meaningful + // values for page count and line count + m_pView->GetWrtShell().CalcLayout(); + + sal_Int32 nCount = -1; + if (nWID == WID_PAGE_COUNT) + nCount = m_pView->GetWrtShell().GetPageCount(); + else // WID_LINE_COUNT + nCount = m_pView->GetWrtShell().GetLineCount(); + aRet <<= nCount; + } + break; + case WID_IS_HIDE_SPELL_MARKS : + // deprecated #i91949 + break; + case WID_IS_CONSTANT_SPELLCHECK : + { + const SwViewOption *pOpt = m_pView->GetWrtShell().GetViewOptions(); + if (!pOpt) + throw RuntimeException(); + aRet <<= bool(pOpt->GetCoreOptions() & ViewOptFlags1::OnlineSpell); + } + break; + default : + OSL_FAIL("unknown WID"); + } + + return aRet; +} + +void SAL_CALL SwXTextView::addPropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*rxListener*/ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL SwXTextView::removePropertyChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*rxListener*/ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL SwXTextView::addVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*rxListener*/ ) +{ + OSL_FAIL("not implemented"); +} + +void SAL_CALL SwXTextView::removeVetoableChangeListener( + const OUString& /*rPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*rxListener*/ ) +{ + OSL_FAIL("not implemented"); +} + +OUString SwXTextView::getImplementationName() +{ + return "SwXTextView"; +} + +sal_Bool SwXTextView::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXTextView::getSupportedServiceNames() +{ + return { "com.sun.star.text.TextDocumentView", "com.sun.star.view.OfficeDocumentView" }; +} + +SwXTextViewCursor::SwXTextViewCursor(SwView* pVw) : + m_pView(pVw), + m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR)) +{ +} + +SwXTextViewCursor::~SwXTextViewCursor() +{ +} + +// used to determine if there is a text selection or not. +// If there is no text selection the functions that need a working +// cursor will be disabled (throw RuntimeException). This will be the case +// for the following interfaces: +// - XViewCursor +// - XTextCursor +// - XTextRange +// - XLineCursor +bool SwXTextViewCursor::IsTextSelection( bool bAllowTables ) const +{ + + bool bRes = false; + OSL_ENSURE(m_pView, "m_pView is NULL ???"); + if(m_pView) + { + //! m_pView->GetShellMode() will only work after the shell + //! has already changed and thus can not be used here! + SelectionType eSelType = m_pView->GetWrtShell().GetSelectionType(); + bRes = ( (SelectionType::Text & eSelType) || + (SelectionType::NumberList & eSelType) ) && + (!(SelectionType::TableCell & eSelType) || bAllowTables); + } + return bRes; +} + +sal_Bool SwXTextViewCursor::isVisible() +{ + OSL_FAIL("not implemented"); + return true; +} + +void SwXTextViewCursor::setVisible(sal_Bool /*bVisible*/) +{ + OSL_FAIL("not implemented"); +} + +awt::Point SwXTextViewCursor::getPosition() +{ + SolarMutexGuard aGuard; + awt::Point aRet; + if(!m_pView) + throw uno::RuntimeException(); + + const SwWrtShell& rSh = m_pView->GetWrtShell(); + const SwRect& aCharRect(rSh.GetCharRect()); + + const SwFrameFormat& rMaster = rSh.GetPageDesc( rSh.GetCurPageDesc() ).GetMaster(); + + const SvxULSpaceItem& rUL = rMaster.GetULSpace(); + const long nY = aCharRect.Top() - (rUL.GetUpper() + DOCUMENTBORDER); + aRet.Y = convertTwipToMm100(nY); + + const SvxLRSpaceItem& rLR = rMaster.GetLRSpace(); + const long nX = aCharRect.Left() - (rLR.GetLeft() + DOCUMENTBORDER); + aRet.X = convertTwipToMm100(nX); + + return aRet; +} + +void SwXTextViewCursor::collapseToStart() +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + if(rSh.HasSelection()) + { + SwPaM* pShellCursor = rSh.GetCursor(); + if(*pShellCursor->GetPoint() > *pShellCursor->GetMark()) + pShellCursor->Exchange(); + pShellCursor->DeleteMark(); + rSh.EnterStdMode(); + rSh.SetSelection(*pShellCursor); + } + +} + +void SwXTextViewCursor::collapseToEnd() +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + if(rSh.HasSelection()) + { + SwPaM* pShellCursor = rSh.GetCursor(); + if(*pShellCursor->GetPoint() < *pShellCursor->GetMark()) + pShellCursor->Exchange(); + pShellCursor->DeleteMark(); + rSh.EnterStdMode(); + rSh.SetSelection(*pShellCursor); + } + +} + +sal_Bool SwXTextViewCursor::isCollapsed() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + const SwWrtShell& rSh = m_pView->GetWrtShell(); + bRet = !rSh.HasSelection(); + + return bRet; + +} + +sal_Bool SwXTextViewCursor::goLeft(sal_Int16 nCount, sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().Left( CRSR_SKIP_CHARS, bExpand, nCount, true ); + + return bRet; +} + +sal_Bool SwXTextViewCursor::goRight(sal_Int16 nCount, sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().Right( CRSR_SKIP_CHARS, bExpand, nCount, true ); + + return bRet; + +} + +void SwXTextViewCursor::gotoRange( + const uno::Reference< text::XTextRange > & xRange, + sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + if(!(m_pView && xRange.is())) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwUnoInternalPaM rDestPam(*m_pView->GetDocShell()->GetDoc()); + if (!::sw::XTextRangeToSwPaM(rDestPam, xRange)) + { + throw uno::RuntimeException(); + } + + ShellMode eSelMode = m_pView->GetShellMode(); + SwWrtShell& rSh = m_pView->GetWrtShell(); + // call EnterStdMode in non-text selections only + if(!bExpand || + (eSelMode != ShellMode::TableText && + eSelMode != ShellMode::ListText && + eSelMode != ShellMode::TableListText && + eSelMode != ShellMode::Text )) + rSh.EnterStdMode(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwPaM aOwnPaM(*pShellCursor->GetPoint()); + if(pShellCursor->HasMark()) + { + aOwnPaM.SetMark(); + *aOwnPaM.GetMark() = *pShellCursor->GetMark(); + } + + uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY); + SwXTextRange* pRange = nullptr; + SwXParagraph* pPara = nullptr; + OTextCursorHelper* pCursor = nullptr; + if(xRangeTunnel.is()) + { + pRange = reinterpret_cast<SwXTextRange*>(xRangeTunnel->getSomething( + SwXTextRange::getUnoTunnelId())); + pCursor = reinterpret_cast<OTextCursorHelper*>(xRangeTunnel->getSomething( + OTextCursorHelper::getUnoTunnelId())); + pPara = reinterpret_cast<SwXParagraph*>(xRangeTunnel->getSomething( + SwXParagraph::getUnoTunnelId())); + } + + const FrameTypeFlags nFrameType = rSh.GetFrameType(nullptr,true); + + SwStartNodeType eSearchNodeType = SwNormalStartNode; + if(nFrameType & FrameTypeFlags::FLY_ANY) + eSearchNodeType = SwFlyStartNode; + else if(nFrameType &FrameTypeFlags::HEADER) + eSearchNodeType = SwHeaderStartNode; + else if(nFrameType & FrameTypeFlags::FOOTER) + eSearchNodeType = SwFooterStartNode; + else if(nFrameType & FrameTypeFlags::TABLE) + eSearchNodeType = SwTableBoxStartNode; + else if(nFrameType & FrameTypeFlags::FOOTNOTE) + eSearchNodeType = SwFootnoteStartNode; + + const SwStartNode* pOwnStartNode = aOwnPaM.GetNode(). + FindSttNodeByType(eSearchNodeType); + + const SwNode* pSrcNode = nullptr; + if(pCursor && pCursor->GetPaM()) + { + pSrcNode = &pCursor->GetPaM()->GetNode(); + } + else if (pRange) + { + SwPaM aPam(pRange->GetDoc().GetNodes()); + if (pRange->GetPositions(aPam)) + { + pSrcNode = &aPam.GetNode(); + } + } + else if (pPara && pPara->GetTextNode()) + { + pSrcNode = pPara->GetTextNode(); + } + const SwStartNode* pTmp = pSrcNode ? pSrcNode->FindSttNodeByType(eSearchNodeType) : nullptr; + + //Skip SectionNodes + while(pTmp && pTmp->IsSectionNode()) + { + pTmp = pTmp->StartOfSectionNode(); + } + while(pOwnStartNode && pOwnStartNode->IsSectionNode()) + { + pOwnStartNode = pOwnStartNode->StartOfSectionNode(); + } + //Without Expand it is allowed to jump out with the ViewCursor everywhere, + //with Expand only in the same environment + if(bExpand && + (pOwnStartNode != pTmp || + (eSelMode != ShellMode::TableText && + eSelMode != ShellMode::ListText && + eSelMode != ShellMode::TableListText && + eSelMode != ShellMode::Text))) + throw uno::RuntimeException(); + + //Now, the selection must be expanded. + if(bExpand) + { + // The cursor should include everything that has been included + // by him and the transferred Range. + SwPosition aOwnLeft(*aOwnPaM.Start()); + SwPosition aOwnRight(*aOwnPaM.End()); + SwPosition* pParamLeft = rDestPam.Start(); + SwPosition* pParamRight = rDestPam.End(); + // Now four SwPositions are there, two of them are needed, but which? + if(aOwnRight > *pParamRight) + *aOwnPaM.GetPoint() = aOwnRight; + else + *aOwnPaM.GetPoint() = *pParamRight; + aOwnPaM.SetMark(); + if(aOwnLeft < *pParamLeft) + *aOwnPaM.GetMark() = aOwnLeft; + else + *aOwnPaM.GetMark() = *pParamLeft; + } + else + { + //The cursor shall match the passed range. + *aOwnPaM.GetPoint() = *rDestPam.GetPoint(); + if(rDestPam.HasMark()) + { + aOwnPaM.SetMark(); + *aOwnPaM.GetMark() = *rDestPam.GetMark(); + } + else + aOwnPaM.DeleteMark(); + } + rSh.SetSelection(aOwnPaM); + + +} + +void SwXTextViewCursor::gotoStart(sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SwXTextViewCursor::gotoStart"); + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + m_pView->GetWrtShell().StartOfSection( bExpand ); + +} + +void SwXTextViewCursor::gotoEnd(sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SwXTextViewCursor::gotoEnd"); + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + m_pView->GetWrtShell().EndOfSection( bExpand ); + +} + +sal_Bool SwXTextViewCursor::jumpToFirstPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + if (rSh.IsSelFrameMode()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + rSh.EnterStdMode(); + bRet = rSh.SttEndDoc(true); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToLastPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + if (rSh.IsSelFrameMode()) + { + rSh.UnSelectFrame(); + rSh.LeaveSelFrameMode(); + } + rSh.EnterStdMode(); + bRet = rSh.SttEndDoc(false); + rSh.SttPg(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToPage(sal_Int16 nPage) +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + bRet = m_pView->GetWrtShell().GotoPage(nPage, true); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToNextPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + bRet = m_pView->GetWrtShell().SttNxtPg(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToPreviousPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + bRet = m_pView->GetWrtShell().EndPrvPg(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToEndOfPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + bRet = m_pView->GetWrtShell().EndPg(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::jumpToStartOfPage() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + bRet = m_pView->GetWrtShell().SttPg(); + + return bRet; +} + +sal_Int16 SwXTextViewCursor::getPage() +{ + SolarMutexGuard aGuard; + sal_Int16 nRet = 0; + if(!m_pView) + throw uno::RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + nRet = static_cast<sal_Int16>(pShellCursor->GetPageNum()); + + return nRet; +} + +sal_Bool SwXTextViewCursor::screenDown() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + SfxRequest aReq(FN_PAGEDOWN, SfxCallMode::SLOT, m_pView->GetPool()); + m_pView->Execute(aReq); + const SfxPoolItem* pRet = aReq.GetReturnValue(); + bRet = pRet && static_cast<const SfxBoolItem*>(pRet)->GetValue(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::screenUp() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + SfxRequest aReq(FN_PAGEUP, SfxCallMode::SLOT, m_pView->GetPool()); + m_pView->Execute(aReq); + const SfxPoolItem* pRet = aReq.GetReturnValue(); + bRet = pRet && static_cast<const SfxBoolItem*>(pRet)->GetValue(); + + return bRet; +} + +uno::Reference< text::XText > SwXTextViewCursor::getText() +{ + SolarMutexGuard aGuard; + uno::Reference< text::XText > xRet; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + xRet = ::sw::CreateParentXText(*pDoc, *pShellCursor->Start()); + + return xRet; +} + +uno::Reference< text::XTextRange > SwXTextViewCursor::getStart() +{ + SolarMutexGuard aGuard; + uno::Reference< text::XTextRange > xRet; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + xRet = SwXTextRange::CreateXTextRange(*pDoc, *pShellCursor->Start(), nullptr); + + return xRet; +} + +uno::Reference< text::XTextRange > SwXTextViewCursor::getEnd() +{ + SolarMutexGuard aGuard; + uno::Reference< text::XTextRange > xRet; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwDoc* pDoc = m_pView->GetDocShell()->GetDoc(); + xRet = SwXTextRange::CreateXTextRange(*pDoc, *pShellCursor->End(), nullptr); + + return xRet; +} + +OUString SwXTextViewCursor::getString() +{ + SolarMutexGuard aGuard; + OUString uRet; + if(m_pView) + { + if (!IsTextSelection( false )) + { + SAL_WARN("sw.uno", "no text selection in getString() " << static_cast<cppu::OWeakObject*>(this)); + return uRet; + } + + ShellMode eSelMode = m_pView->GetShellMode(); + switch(eSelMode) + { + //! since setString for SEL_TABLE_TEXT (with possible + //! multi selection of cells) would not work properly we + //! will ignore this case for both + //! functions (setString AND getString) because of symmetrie. + + case ShellMode::ListText : + case ShellMode::TableListText: + case ShellMode::Text : + { + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwUnoCursorHelper::GetTextFromPam(*pShellCursor, uRet, + rSh.GetLayout()); + break; + } + default:;//prevent warning + } + } + return uRet; +} + +void SwXTextViewCursor::setString(const OUString& aString) +{ + SolarMutexGuard aGuard; + if(m_pView) + { + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + ShellMode eSelMode = m_pView->GetShellMode(); + switch(eSelMode) + { + //! since setString for SEL_TABLE_TEXT (with possible + //! multi selection of cells) would not work properly we + //! will ignore this case for both + //! functions (setString AND getString) because of symmetrie. + + case ShellMode::ListText : + case ShellMode::TableListText : + case ShellMode::Text : + { + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwCursor* pShellCursor = rSh.GetSwCursor(); + SwUnoCursorHelper::SetString(*pShellCursor, aString); + break; + } + default:;//prevent warning + } + } +} + +uno::Reference< XPropertySetInfo > SwXTextViewCursor::getPropertySetInfo( ) +{ + static uno::Reference< XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo(); + return xRef; +} + +void SwXTextViewCursor::setPropertyValue( const OUString& rPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwNode& rNode = pShellCursor->GetNode(); + if (!rNode.IsTextNode()) + throw RuntimeException(); + + SwUnoCursorHelper::SetPropertyValue( + *pShellCursor, *m_pPropSet, rPropertyName, aValue ); + + +} + +Any SwXTextViewCursor::getPropertyValue( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + Any aRet; + if(!m_pView) + throw RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + aRet = SwUnoCursorHelper::GetPropertyValue( + *pShellCursor, *m_pPropSet, rPropertyName); + + return aRet; +} + +void SwXTextViewCursor::addPropertyChangeListener( + const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ ) +{ +} + +void SwXTextViewCursor::removePropertyChangeListener( + const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ ) +{ +} + +void SwXTextViewCursor::addVetoableChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ ) +{ +} + +void SwXTextViewCursor::removeVetoableChangeListener( + const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ ) +{ +} + +PropertyState SwXTextViewCursor::getPropertyState( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + PropertyState eState; + if(!m_pView) + throw RuntimeException(); + + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + eState = SwUnoCursorHelper::GetPropertyState( + *pShellCursor, *m_pPropSet, rPropertyName); + + return eState; +} + +Sequence< PropertyState > SwXTextViewCursor::getPropertyStates( + const Sequence< OUString >& rPropertyNames ) +{ + SolarMutexGuard aGuard; + Sequence< PropertyState > aRet; + if(m_pView) + { + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + aRet = SwUnoCursorHelper::GetPropertyStates( + *pShellCursor, *m_pPropSet, rPropertyNames); + } + return aRet; +} + +void SwXTextViewCursor::setPropertyToDefault( const OUString& rPropertyName ) +{ + SolarMutexGuard aGuard; + if(m_pView) + { + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + SwUnoCursorHelper::SetPropertyToDefault( + *pShellCursor, *m_pPropSet, rPropertyName); + } +} + +Any SwXTextViewCursor::getPropertyDefault( const OUString& rPropertyName ) +{ + Any aRet; + SolarMutexGuard aGuard; + if(m_pView) + { + SwWrtShell& rSh = m_pView->GetWrtShell(); + SwPaM* pShellCursor = rSh.GetCursor(); + aRet = SwUnoCursorHelper::GetPropertyDefault( + *pShellCursor, *m_pPropSet, rPropertyName); + } + return aRet; +} + +sal_Bool SwXTextViewCursor::goDown(sal_Int16 nCount, sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SwXTextViewCursor::goDown"); + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().Down( bExpand, nCount, true ); + + return bRet; +} + +sal_Bool SwXTextViewCursor::goUp(sal_Int16 nCount, sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SwXTextViewCursor::goUp"); + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection()) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().Up( bExpand, nCount, true ); + + return bRet; +} + +sal_Bool SwXTextViewCursor::isAtStartOfLine() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().IsAtLeftMargin(); + + return bRet; +} + +sal_Bool SwXTextViewCursor::isAtEndOfLine() +{ + SolarMutexGuard aGuard; + bool bRet = false; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + bRet = m_pView->GetWrtShell().IsAtRightMargin(); + + return bRet; +} + +void SwXTextViewCursor::gotoEndOfLine(sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + m_pView->GetWrtShell().RightMargin(bExpand, true); + +} + +void SwXTextViewCursor::gotoStartOfLine(sal_Bool bExpand) +{ + SolarMutexGuard aGuard; + if(!m_pView) + throw uno::RuntimeException(); + + if (!IsTextSelection( false )) + throw uno::RuntimeException("no text selection", static_cast < cppu::OWeakObject * > ( this ) ); + + m_pView->GetWrtShell().LeftMargin(bExpand, true); + +} + +OUString SwXTextViewCursor::getImplementationName() +{ + return "SwXTextViewCursor"; +} + +sal_Bool SwXTextViewCursor::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SwXTextViewCursor::getSupportedServiceNames() +{ + return { "com.sun.star.text.TextViewCursor", + "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" }; +} + +namespace +{ + class theSwXTextViewCursorUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextViewCursorUnoTunnelId > {}; +} + +const uno::Sequence< sal_Int8 > & SwXTextViewCursor::getUnoTunnelId() +{ + return theSwXTextViewCursorUnoTunnelId::get().getSeq(); +} + +//XUnoTunnel +sal_Int64 SAL_CALL SwXTextViewCursor::getSomething( + const uno::Sequence< sal_Int8 >& rId ) +{ + if( isUnoTunnelId<SwXTextViewCursor>(rId) ) + { + return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); + } + return OTextCursorHelper::getSomething(rId); +} + +IMPLEMENT_FORWARD_XINTERFACE2(SwXTextViewCursor,SwXTextViewCursor_Base,OTextCursorHelper) +const SwDoc* SwXTextViewCursor::GetDoc() const +{ + SwWrtShell& rSh = m_pView->GetWrtShell(); + return rSh.GetCursor() ? rSh.GetCursor()->GetDoc() : nullptr; +} + +SwDoc* SwXTextViewCursor::GetDoc() +{ + SwWrtShell& rSh = m_pView->GetWrtShell(); + return rSh.GetCursor() ? rSh.GetCursor()->GetDoc() : nullptr; +} + +const SwPaM* SwXTextViewCursor::GetPaM() const +{ + SwWrtShell& rSh = m_pView->GetWrtShell(); + return rSh.GetCursor(); +} + +SwPaM* SwXTextViewCursor::GetPaM() +{ + SwWrtShell& rSh = m_pView->GetWrtShell(); + return rSh.GetCursor(); +} + +uno::Reference< datatransfer::XTransferable > SAL_CALL SwXTextView::getTransferable() +{ + SolarMutexGuard aGuard; + + //force immediat shell update + GetView()->StopShellTimer(); + SwWrtShell& rSh = GetView()->GetWrtShell(); + if ( GetView()->GetShellMode() == ShellMode::DrawText ) + { + SdrView *pSdrView = rSh.GetDrawView(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + return pOLV->GetEditView().GetTransferable(); + } + else + { + SwTransferable* pTransfer = new SwTransferable( rSh ); + const bool bLockedView = rSh.IsViewLocked(); + rSh.LockView( true ); //lock visible section + pTransfer->PrepareForCopy(); + rSh.LockView( bLockedView ); + return uno::Reference< datatransfer::XTransferable >( pTransfer ); + } +} + +void SAL_CALL SwXTextView::insertTransferable( const uno::Reference< datatransfer::XTransferable >& xTrans ) +{ + SolarMutexGuard aGuard; + + //force immediat shell update + GetView()->StopShellTimer(); + SwWrtShell& rSh = GetView()->GetWrtShell(); + if ( GetView()->GetShellMode() == ShellMode::DrawText ) + { + SdrView *pSdrView = rSh.GetDrawView(); + OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView(); + pOLV->GetEditView().InsertText( xTrans, GetView()->GetDocShell()->GetMedium()->GetBaseURL(), false ); + } + else + { + TransferableDataHelper aDataHelper( xTrans ); + if ( SwTransferable::IsPaste( rSh, aDataHelper ) ) + { + SwTransferable::Paste( rSh, aDataHelper ); + if( rSh.IsFrameSelected() || rSh.IsObjSelected() ) + rSh.EnterSelFrameMode(); + GetView()->AttrChangedNotify(nullptr); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/attrdesc.cxx b/sw/source/uibase/utlui/attrdesc.cxx new file mode 100644 index 000000000..a839c436f --- /dev/null +++ b/sw/source/uibase/utlui/attrdesc.cxx @@ -0,0 +1,844 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <i18nutil/unicode.hxx> +#include <svl/itemiter.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/GraphicObject.hxx> + +#include <editeng/itemtype.hxx> +#include <editeng/eerdll.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/syslocale.hxx> +#include <rtl/ustrbuf.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtinfmt.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <fmtsrnd.hxx> +#include <fmtornt.hxx> +#include <fmtlsplt.hxx> +#include <fmtrowsplt.hxx> +#include <fmtpdsc.hxx> +#include <fmtclds.hxx> +#include <fmteiro.hxx> +#include <fmturl.hxx> +#include <fmthdft.hxx> +#include <fmtcnct.hxx> +#include <fmtline.hxx> +#include <tgrditem.hxx> +#include <hfspacingitem.hxx> +#include <fmtruby.hxx> +#include <paratr.hxx> +#include <grfatr.hxx> +#include <pagedesc.hxx> +#include <charfmt.hxx> +#include <strings.hrc> +#include <fmtftntx.hxx> +#include <fmtfollowtextflow.hxx> +#include <libxml/xmlwriter.h> + +using namespace com::sun::star; + + +// query the attribute descriptions +void SwAttrSet::GetPresentation( + SfxItemPresentation ePres, + MapUnit eCoreMetric, + MapUnit ePresMetric, + OUString &rText ) const +{ + rText.clear(); + OUString aStr; + if( Count() ) + { + SfxItemIter aIter( *this ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + const IntlWrapper aInt(SvtSysLocale().GetUILanguageTag()); + do + { + pItem->GetPresentation(ePres, eCoreMetric, ePresMetric, aStr, aInt); + if( rText.getLength() && aStr.getLength() ) + rText += ", "; + rText += aStr; + pItem = aIter.NextItem(); + } while (pItem); + } +} + +bool SwFormatCharFormat::GetPresentation +( + SfxItemPresentation ePres, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const SwCharFormat *pCharFormat = GetCharFormat(); + if ( pCharFormat ) + { + OUString aStr; + pCharFormat->GetPresentation( ePres, eCoreUnit, ePresUnit, aStr ); + rText = SwResId( STR_CHARFMT ) + "(" + aStr + ")"; + } + else + rText = SwResId( STR_NO_CHARFMT ); + return true; +} + +bool SwFormatAutoFormat::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); //TODO + return true; +} + +bool SwFormatINetFormat::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText = GetValue(); + return true; +} + +bool SwFormatRuby::GetPresentation( SfxItemPresentation /*ePres*/, + MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, + OUString &rText, const IntlWrapper& /*rIntl*/ ) const +{ + rText.clear(); + return true; +} + +bool SwFormatDrop::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( GetLines() > 1 ) + { + if ( GetChars() > 1 ) + { + rText = OUString::number( GetChars() ) + " "; + } + rText += SwResId( STR_DROP_OVER ) + + " " + + OUString::number( GetLines() ) + + " " + + SwResId( STR_DROP_LINES ); + } + else + rText = SwResId( STR_NO_DROP_LINES ); + return true; +} + +bool SwRegisterItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = GetValue() ? STR_REGISTER_ON : STR_REGISTER_OFF; + rText = SwResId(pId); + return true; +} + +bool SwNumRuleItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if( !GetValue().isEmpty() ) + rText = SwResId( STR_NUMRULE_ON ) + + "(" + GetValue() + ")"; + else + rText = SwResId( STR_NUMRULE_OFF ); + return true; +} + +bool SwParaConnectBorderItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = GetValue() ? STR_CONNECT_BORDER_ON : STR_CONNECT_BORDER_OFF; + rText = SwResId(pId); + return true; +} + +// Frame attribute + +bool SwFormatFrameSize::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + rText = SwResId( STR_FRM_WIDTH ) + " "; + if ( GetWidthPercent() ) + { + rText += unicode::formatPercent(GetWidthPercent(), + Application::GetSettings().GetUILanguageTag()); + } + else + { + rText += ::GetMetricText( GetWidth(), eCoreUnit, ePresUnit, &rIntl ) + + " " + ::EditResId( ::GetMetricId( ePresUnit ) ); + } + if ( SwFrameSize::Variable != GetHeightSizeType() ) + { + const char* pId = SwFrameSize::Fixed == m_eFrameHeightType ? + STR_FRM_FIXEDHEIGHT : STR_FRM_MINHEIGHT; + rText += ", " + SwResId(pId) + " "; + if ( GetHeightPercent() ) + { + rText += unicode::formatPercent(GetHeightPercent(), + Application::GetSettings().GetUILanguageTag()); + } + else + { + rText = ::GetMetricText( GetHeight(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + } + return true; +} + +//Header for page formats. +//Client of FrameFormat which describes the header. + +bool SwFormatHeader::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = GetHeaderFormat() ? STR_HEADER : STR_NO_HEADER; + rText = SwResId(pId); + return true; +} + +//Footer for page formats. +//Client of FrameFormat which describes the footer. + +bool SwFormatFooter::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = GetFooterFormat() ? STR_FOOTER : STR_NO_FOOTER; + rText = SwResId(pId); + return true; +} + +bool SwFormatSurround::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = nullptr; + switch ( GetValue() ) + { + case css::text::WrapTextMode_NONE: + pId = STR_SURROUND_NONE; + break; + case css::text::WrapTextMode_THROUGH: + pId = STR_SURROUND_THROUGH; + break; + case css::text::WrapTextMode_PARALLEL: + pId = STR_SURROUND_PARALLEL; + break; + case css::text::WrapTextMode_DYNAMIC: + pId = STR_SURROUND_IDEAL; + break; + case css::text::WrapTextMode_LEFT: + pId = STR_SURROUND_LEFT; + break; + case css::text::WrapTextMode_RIGHT: + pId = STR_SURROUND_RIGHT; + break; + default:;//prevent warning + } + if (pId) + rText = SwResId(pId); + + if ( IsAnchorOnly() ) + { + rText += " " + SwResId( STR_SURROUND_ANCHORONLY ); + } + return true; +} + +//VertOrientation, how and by what orientate the FlyFrame in the vertical? + +bool SwFormatVertOrient::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + const char* pId = nullptr; + switch ( GetVertOrient() ) + { + case text::VertOrientation::NONE: + { + rText += SwResId( STR_POS_Y ) + " " + + ::GetMetricText( GetPos(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + break; + case text::VertOrientation::TOP: + pId = STR_VERT_TOP; + break; + case text::VertOrientation::CENTER: + pId = STR_VERT_CENTER; + break; + case text::VertOrientation::BOTTOM: + pId = STR_VERT_BOTTOM; + break; + case text::VertOrientation::LINE_TOP: + pId = STR_LINE_TOP; + break; + case text::VertOrientation::LINE_CENTER: + pId = STR_LINE_CENTER; + break; + case text::VertOrientation::LINE_BOTTOM: + pId = STR_LINE_BOTTOM; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +//HoriOrientation, how and by what orientate the FlyFrame in the horizontal? + +bool SwFormatHoriOrient::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + const char* pId = nullptr; + switch ( GetHoriOrient() ) + { + case text::HoriOrientation::NONE: + { + rText += SwResId( STR_POS_X ) + " " + + ::GetMetricText( GetPos(), eCoreUnit, ePresUnit, &rIntl ) + + " " + EditResId( ::GetMetricId( ePresUnit ) ); + } + break; + case text::HoriOrientation::RIGHT: + pId = STR_HORI_RIGHT; + break; + case text::HoriOrientation::CENTER: + pId = STR_HORI_CENTER; + break; + case text::HoriOrientation::LEFT: + pId = STR_HORI_LEFT; + break; + case text::HoriOrientation::INSIDE: + pId = STR_HORI_INSIDE; + break; + case text::HoriOrientation::OUTSIDE: + pId = STR_HORI_OUTSIDE; + break; + case text::HoriOrientation::FULL: + pId = STR_HORI_FULL; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +// FlyAnchor, Anchor of the free-flying frame + +bool SwFormatAnchor::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = nullptr; + switch ( GetAnchorId() ) + { + case RndStdIds::FLY_AT_PARA: + pId = STR_FLY_AT_PARA; + break; + case RndStdIds::FLY_AS_CHAR: + pId = STR_FLY_AS_CHAR; + break; + case RndStdIds::FLY_AT_PAGE: + pId = STR_FLY_AT_PAGE; + break; + default:;//prevent warning + } + if (pId) + rText += SwResId(pId); + return true; +} + +bool SwFormatPageDesc::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const SwPageDesc *pPageDesc = GetPageDesc(); + if ( pPageDesc ) + rText = pPageDesc->GetName(); + else + rText = SwResId( STR_NO_PAGEDESC ); + return true; +} + +//The ColumnDescriptor + +bool SwFormatCol::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + sal_uInt16 nCnt = GetNumCols(); + if ( nCnt > 1 ) + { + rText = OUString::number(nCnt) + " " + SwResId( STR_COLUMNS ); + if ( COLADJ_NONE != GetLineAdj() ) + { + const long nWdth = static_cast<long>(GetLineWidth()); + rText += " " + SwResId( STR_LINE_WIDTH ) + " " + + ::GetMetricText( nWdth, eCoreUnit, + MapUnit::MapPoint, &rIntl ); + } + } + else + rText.clear(); + return true; +} + +//URL's and maps + +bool SwFormatURL::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( m_pMap ) + rText += "Client-Map"; + if ( !m_sURL.isEmpty() ) + { + if ( m_pMap ) + rText += " - "; + rText += "URL: " + m_sURL; + if ( m_bIsServerMap ) + rText += " (Server-Map)"; + } + if ( !m_sTargetFrameName.isEmpty() ) + { + rText += ", Target: " + m_sTargetFrameName; + } + return true; +} + +bool SwFormatEditInReadonly::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + rText.clear(); + if ( GetValue() ) + rText = SwResId(STR_EDIT_IN_READONLY); + return true; +} + +void SwFormatEditInReadonly::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatEditInReadonly")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr())); + xmlTextWriterEndElement(pWriter); +} + +bool SwFormatLayoutSplit::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( GetValue() ) + rText = SwResId(STR_LAYOUT_SPLIT); + return true; +} + +bool SwFormatRowSplit::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return false; +} + +bool SwFormatFootnoteEndAtTextEnd::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return true; +} + +bool SwFormatChain::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( GetPrev() || GetNext() ) + { + rText = SwResId(STR_CONNECT1); + if ( GetPrev() ) + { + rText += GetPrev()->GetName(); + if ( GetNext() ) + rText += SwResId(STR_CONNECT2); + } + if ( GetNext() ) + rText += GetNext()->GetName(); + } + return true; +} + +bool SwFormatLineNumber::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + if ( IsCount() ) + rText += SwResId(STR_LINECOUNT); + else + rText += SwResId(STR_DONTLINECOUNT); + if ( GetStartValue() ) + { + rText += " " + SwResId(STR_LINCOUNT_START) + + OUString::number( GetStartValue() ); + } + return true; +} + +bool SwTextGridItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, + const IntlWrapper& /*rIntl*/ +) const +{ + const char* pId = nullptr; + + switch ( GetGridType() ) + { + case GRID_NONE : + pId = STR_GRID_NONE; + break; + case GRID_LINES_ONLY : + pId = STR_GRID_LINES_ONLY; + break; + case GRID_LINES_CHARS : + pId = STR_GRID_LINES_CHARS; + break; + } + if (pId) + rText += SwResId(pId); + return true; +} + +bool SwHeaderAndFooterEatSpacingItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rIntl*/ +) const +{ + return false; +} + +// Graphic attributes + +bool SwMirrorGrf::GetPresentation( + SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString& rText, const IntlWrapper& /*rIntl*/ ) const +{ + const char* pId; + switch( GetValue() ) + { + case MirrorGraph::Dont: pId = STR_NO_MIRROR; break; + case MirrorGraph::Vertical: pId = STR_VERT_MIRROR; break; + case MirrorGraph::Horizontal: pId = STR_HORI_MIRROR; break; + case MirrorGraph::Both: pId = STR_BOTH_MIRROR; break; + default: pId = nullptr; break; + } + if (pId) + { + rText = SwResId(pId); + if (m_bGrfToggle) + rText += SwResId( STR_MIRROR_TOGGLE ); + } + return true; +} + +bool SwRotationGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_ROTATION ); + else if( rText.getLength() ) + rText.clear(); + rText += OUString::number( GetValue() ) + "\xB0"; + return true; +} + +bool SwLuminanceGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_LUMINANCE ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwContrastGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_CONTRAST ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwChannelGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + { + const char* pId; + switch ( Which() ) + { + case RES_GRFATR_CHANNELR: pId = STR_CHANNELR; break; + case RES_GRFATR_CHANNELG: pId = STR_CHANNELG; break; + case RES_GRFATR_CHANNELB: pId = STR_CHANNELB; break; + default: pId = nullptr; break; + } + if (pId) + rText = SwResId(pId); + else if( rText.getLength() ) + rText.clear(); + } + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwGammaGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + OUStringBuffer aText; + if( SfxItemPresentation::Complete == ePres ) + aText.append(SwResId(STR_GAMMA)); + aText.append(unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag())); + rText = aText.makeStringAndClear(); + return true; +} + +bool SwInvertGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + const char* pId = GetValue() ? STR_INVERT : STR_INVERT_NOT; + rText = SwResId(pId); + } + return true; +} + +bool SwTransparencyGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + if( SfxItemPresentation::Complete == ePres ) + rText = SwResId( STR_TRANSPARENCY ); + else if( rText.getLength() ) + rText.clear(); + rText += unicode::formatPercent(GetValue(), + Application::GetSettings().GetUILanguageTag()); + return true; +} + +bool SwDrawModeGrf::GetPresentation( + SfxItemPresentation ePres, MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/, + OUString &rText, const IntlWrapper& /*rIntl*/) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + const char* pId; + switch ( GetValue() ) + { + + case GraphicDrawMode::Greys: pId = STR_DRAWMODE_GREY; break; + case GraphicDrawMode::Mono: pId = STR_DRAWMODE_BLACKWHITE; break; + case GraphicDrawMode::Watermark: pId = STR_DRAWMODE_WATERMARK; break; + default: pId = STR_DRAWMODE_STD; break; + } + rText = SwResId( STR_DRAWMODE ) + SwResId(pId); + } + return true; +} + +bool SwFormatFollowTextFlow::GetPresentation( SfxItemPresentation ePres, + MapUnit /*eCoreMetric*/, + MapUnit /*ePresMetric*/, + OUString &rText, + const IntlWrapper& /*rIntl*/ ) const +{ + rText.clear(); + if( SfxItemPresentation::Complete == ePres ) + { + const char* pId = GetValue() ? STR_FOLLOW_TEXT_FLOW : STR_DONT_FOLLOW_TEXT_FLOW; + rText = SwResId(pId); + } + return true; +} + +void SwFormatFollowTextFlow::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFollowTextFlow")); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); + xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr())); + xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/bookctrl.cxx b/sw/source/uibase/utlui/bookctrl.cxx new file mode 100644 index 000000000..ea9c1d742 --- /dev/null +++ b/sw/source/uibase/utlui/bookctrl.cxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <strings.hrc> + +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/status.hxx> +#include <vcl/menu.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <IMark.hxx> +#include <bookctrl.hxx> +#include <map> + +SFX_IMPL_STATUSBAR_CONTROL( SwBookmarkControl, SfxStringItem ); + +namespace { + +class BookmarkPopup_Impl : public PopupMenu +{ +public: + BookmarkPopup_Impl(); + + sal_uInt16 GetCurId() const { return nCurId; } + +private: + sal_uInt16 nCurId; + + virtual void Select() override; +}; + +} + +BookmarkPopup_Impl::BookmarkPopup_Impl() : + PopupMenu(), + nCurId(USHRT_MAX) +{ +} + +void BookmarkPopup_Impl::Select() +{ + nCurId = GetCurItemId(); +} + +SwBookmarkControl::SwBookmarkControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ +} + +SwBookmarkControl::~SwBookmarkControl() +{ +} + +void SwBookmarkControl::StateChanged( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if( eState != SfxItemState::DEFAULT || pState->IsVoidItem() ) + GetStatusBar().SetItemText( GetId(), OUString() ); + else if (const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>(pState)) + { + sPageNumber = pStringItem->GetValue(); + GetStatusBar().SetItemText(GetId(), sPageNumber); + } + else if (const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>(pState)) + { + if (pBoolItem->GetValue()) // Indicates whether to show extended tooltip + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_BOOKCTRL_HINT_EXTENDED)); + else + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_BOOKCTRL_HINT)); + } + +} + +void SwBookmarkControl::Paint( const UserDrawEvent& ) +{ +} + +void SwBookmarkControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu && + !GetStatusBar().GetItemText( GetId() ).isEmpty() ) + { + ScopedVclPtrInstance<BookmarkPopup_Impl> aPop; + SwWrtShell* pWrtShell = ::GetActiveWrtShell(); + if( pWrtShell && pWrtShell->getIDocumentMarkAccess()->getAllMarksCount() > 0 ) + { + IDocumentMarkAccess* const pMarkAccess = pWrtShell->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBookmarkStart = pMarkAccess->getBookmarksBegin(); + sal_uInt16 nPopupId = 1; + std::map<sal_Int32, sal_uInt16> aBookmarkIdx; + for(IDocumentMarkAccess::const_iterator_t ppBookmark = ppBookmarkStart; + ppBookmark != pMarkAccess->getBookmarksEnd(); + ++ppBookmark) + { + if(IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + aPop->InsertItem( nPopupId, (*ppBookmark)->GetName() ); + aBookmarkIdx[nPopupId] = static_cast<sal_uInt16>(ppBookmark - ppBookmarkStart); + nPopupId++; + } + } + aPop->Execute( &GetStatusBar(), rCEvt.GetMousePosPixel()); + sal_uInt16 nCurrId = aPop->GetCurId(); + if( nCurrId != USHRT_MAX) + { + SfxUInt16Item aBookmark( FN_STAT_BOOKMARK, aBookmarkIdx[nCurrId] ); + SfxViewFrame::Current()->GetDispatcher()->ExecuteList(FN_STAT_BOOKMARK, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD, + { &aBookmark }); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/condedit.cxx b/sw/source/uibase/utlui/condedit.cxx new file mode 100644 index 000000000..df50c63ac --- /dev/null +++ b/sw/source/uibase/utlui/condedit.cxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <condedit.hxx> +#include <svx/dbaexchange.hxx> + +using namespace ::svx; +using namespace ::com::sun::star::uno; + +ConditionEdit::ConditionEdit(std::unique_ptr<weld::Entry> xControl) + : m_xControl(std::move(xControl)) + , m_aDropTargetHelper(*this) + , bBrackets(true) + , bEnableDrop(true) +{ +} + +sal_Int8 ConditionEditDropTarget::AcceptDrop( const AcceptDropEvent& /*rEvt*/ ) +{ + return OColumnTransferable::canExtractColumnDescriptor + ( GetDataFlavorExVector(), + ColumnTransferFormatFlags::COLUMN_DESCRIPTOR ) + ? DND_ACTION_COPY + : DND_ACTION_NONE; +} + +ConditionEditDropTarget::ConditionEditDropTarget(ConditionEdit& rEdit) + : DropTargetHelper(rEdit.get_widget().get_drop_target()) + , m_rEdit(rEdit) +{ +} + +sal_Int8 ConditionEditDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + if (m_rEdit.GetDropEnable()) + { + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + + const DataFlavorExVector& rVector = aData.GetDataFlavorExVector(); + if (OColumnTransferable::canExtractColumnDescriptor(rVector, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)) + { + ODataAccessDescriptor aColDesc = OColumnTransferable::extractColumnDescriptor( + aData); + OUString sDBName; + bool bBrackets = m_rEdit.GetBrackets(); + if (bBrackets) + sDBName += "["; + OUString sTmp = aColDesc.getDataSource(); + sDBName += sTmp + "."; + + aColDesc[DataAccessDescriptorProperty::Command] >>= sTmp; + sDBName += sTmp + "."; + + aColDesc[DataAccessDescriptorProperty::ColumnName] >>= sTmp; + sDBName += sTmp; + if (bBrackets) + sDBName += "]"; + + m_rEdit.get_widget().set_text( sDBName ); + nRet = DND_ACTION_COPY; + } + } + return nRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx new file mode 100644 index 000000000..4729818da --- /dev/null +++ b/sw/source/uibase/utlui/content.cxx @@ -0,0 +1,4033 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/string.hxx> +#include <svl/urlbmk.hxx> +#include <osl/thread.h> +#include <sal/log.hxx> +#include <tools/urlobj.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/event.hxx> +#include <sfx2/viewfrm.hxx> +#include <o3tl/enumrange.hxx> +#include <o3tl/sorted_vector.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> +#include <vcl/weldutils.hxx> +#include <sot/formats.hxx> +#include <uiitems.hxx> +#include <fmtinfmt.hxx> +#include <txtinet.hxx> +#include <fmtfld.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <drawdoc.hxx> +#include <content.hxx> +#include <frmfmt.hxx> +#include <fldbas.hxx> +#include <IMark.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <navipi.hxx> +#include <navicont.hxx> +#include <navicfg.hxx> +#include <edtwin.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentOutlineNodes.hxx> +#include <unotxvw.hxx> +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <com/sun/star/text/XTextSectionsSupplier.hpp> +#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp> +#include <com/sun/star/text/XTextTablesSupplier.hpp> +#include <com/sun/star/text/XDocumentIndexesSupplier.hpp> +#include <com/sun/star/text/XDocumentIndex.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> +#include <com/sun/star/text/XTextFramesSupplier.hpp> +#include <dcontact.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdview.hxx> +#include <SwRewriter.hxx> +#include <hints.hxx> +#include <numrule.hxx> +#include <swundo.hxx> +#include <ndtxt.hxx> +#include <PostItMgr.hxx> +#include <postithelper.hxx> + +#include <swabstdlg.hxx> +#include <bitmaps.hlst> + +#include <navmgr.hxx> +#include <AnnotationWin.hxx> +#include <memory> + +#include <fmtcntnt.hxx> +#include <docstat.hxx> + +#define CTYPE_CNT 0 +#define CTYPE_CTT 1 + +using namespace ::std; +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + +namespace { + +constexpr char NAVI_BOOKMARK_DELIM = '\x01'; + +} + +class SwContentArr + : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_uniqueptr_to<SwContent>, + o3tl::find_partialorder_ptrequals> +{ +}; + +namespace +{ + bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView) + { + return reinterpret_cast<const SwTypeNumber*>(rTreeView.get_id(rEntry).toInt64())->GetTypeId() == CTYPE_CNT; + } + + bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView) + { + return reinterpret_cast<const SwTypeNumber*>(rTreeView.get_id(rEntry).toInt64())->GetTypeId() == CTYPE_CTT; + } + + bool lcl_FindShell(SwWrtShell const * pShell) + { + bool bFound = false; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + if(pShell == &pView->GetWrtShell()) + { + bFound = true; + break; + } + pView = SwModule::GetNextView(pView); + } + return bFound; + } + + bool lcl_IsUiVisibleBookmark(const ::sw::mark::IMark* pMark) + { + return IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::BOOKMARK; + } + + size_t lcl_InsertURLFieldContent( + SwContentArr *pMember, + SwWrtShell* pWrtShell, + const SwContentType *pCntType) + { + SwGetINetAttrs aArr; + pWrtShell->GetINetAttrs( aArr ); + const SwGetINetAttrs::size_type nCount {aArr.size()}; + for( SwGetINetAttrs::size_type n = 0; n < nCount; ++n ) + { + SwGetINetAttr* p = &aArr[ n ]; + std::unique_ptr<SwURLFieldContent> pCnt(new SwURLFieldContent( + pCntType, + p->sText, + INetURLObject::decode( + p->rINetAttr.GetINetFormat().GetValue(), + INetURLObject::DecodeMechanism::Unambiguous ), + &p->rINetAttr, + n )); + pMember->insert( std::move(pCnt) ); + } + return nCount; + } +} + +// Content, contains names and reference at the content type. + +SwContent::SwContent(const SwContentType* pCnt, const OUString& rName, long nYPos) : + SwTypeNumber(CTYPE_CNT), + pParent(pCnt), + sContentName(rName), + nYPosition(nYPos), + bInvisible(false) +{ +} + + +SwTypeNumber::~SwTypeNumber() +{ +} + +bool SwContent::IsProtect() const +{ + return false; +} + +bool SwPostItContent::IsProtect() const +{ + return pField->IsProtect(); +} + +bool SwURLFieldContent::IsProtect() const +{ + return pINetAttr->IsProtect(); +} + +SwGraphicContent::~SwGraphicContent() +{ +} + +SwTOXBaseContent::~SwTOXBaseContent() +{ +} + +static const char* STR_CONTENT_TYPE_ARY[] = +{ + STR_CONTENT_TYPE_OUTLINE, + STR_CONTENT_TYPE_TABLE, + STR_CONTENT_TYPE_FRAME, + STR_CONTENT_TYPE_GRAPHIC, + STR_CONTENT_TYPE_OLE, + STR_CONTENT_TYPE_BOOKMARK, + STR_CONTENT_TYPE_REGION, + STR_CONTENT_TYPE_URLFIELD, + STR_CONTENT_TYPE_REFERENCE, + STR_CONTENT_TYPE_INDEX, + STR_CONTENT_TYPE_POSTIT, + STR_CONTENT_TYPE_DRAWOBJECT +}; + +static const char* STR_CONTENT_TYPE_SINGLE_ARY[] = +{ + STR_CONTENT_TYPE_SINGLE_OUTLINE, + STR_CONTENT_TYPE_SINGLE_TABLE, + STR_CONTENT_TYPE_SINGLE_FRAME, + STR_CONTENT_TYPE_SINGLE_GRAPHIC, + STR_CONTENT_TYPE_SINGLE_OLE, + STR_CONTENT_TYPE_SINGLE_BOOKMARK, + STR_CONTENT_TYPE_SINGLE_REGION, + STR_CONTENT_TYPE_SINGLE_URLFIELD, + STR_CONTENT_TYPE_SINGLE_REFERENCE, + STR_CONTENT_TYPE_SINGLE_INDEX, + STR_CONTENT_TYPE_SINGLE_POSTIT, + STR_CONTENT_TYPE_SINGLE_DRAWOBJECT +}; + +namespace +{ + bool checkVisibilityChanged( + const SwContentArr& rSwContentArrA, + const SwContentArr& rSwContentArrB) + { + if(rSwContentArrA.size() != rSwContentArrB.size()) + { + return true; + } + + for(size_t a(0); a < rSwContentArrA.size(); a++) + { + if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible()) + { + return true; + } + } + + return false; + } +} // end of anonymous namespace + +SwContentType::SwContentType(SwWrtShell* pShell, ContentTypeId nType, sal_uInt8 nLevel) : + SwTypeNumber(CTYPE_CTT), + m_pWrtShell(pShell), + m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])), + m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])), + m_nMemberCount(0), + m_nContentType(nType), + m_nOutlineLevel(nLevel), + m_bDataValid(false), + m_bEdit(false), + m_bDelete(true) +{ + Init(); +} + +void SwContentType::Init(bool* pbInvalidateWindow) +{ + // if the MemberCount is changing ... + size_t nOldMemberCount = m_nMemberCount; + m_nMemberCount = 0; + switch(m_nContentType) + { + case ContentTypeId::OUTLINE : + { + m_sTypeToken = "outline"; + m_nMemberCount = m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + if (m_nMemberCount < MAXLEVEL) + { + const size_t nOutlineCount = m_nMemberCount; + for(size_t j = 0; j < nOutlineCount; ++j) + { + if (m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(j) > m_nOutlineLevel + || !m_pWrtShell->getIDocumentOutlineNodesAccess()->isOutlineInLayout(j, *m_pWrtShell->GetLayout())) + { + m_nMemberCount --; + } + } + } + } + break; + + case ContentTypeId::TABLE : + m_sTypeToken = "table"; + m_nMemberCount = m_pWrtShell->GetTableFrameFormatCount(true); + m_bEdit = true; + break; + + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + case ContentTypeId::OLE : + { + FlyCntType eType = FLYCNTTYPE_FRM; + m_sTypeToken = "frame"; + if(m_nContentType == ContentTypeId::OLE) + { + eType = FLYCNTTYPE_OLE; + m_sTypeToken = "ole"; + } + else if(m_nContentType == ContentTypeId::GRAPHIC) + { + eType = FLYCNTTYPE_GRF; + m_sTypeToken = "graphic"; + } + m_nMemberCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true); + m_bEdit = true; + } + break; + case ContentTypeId::BOOKMARK: + { + IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); + m_nMemberCount = count_if( + pMarkAccess->getBookmarksBegin(), + pMarkAccess->getBookmarksEnd(), + &lcl_IsUiVisibleBookmark); + m_sTypeToken.clear(); + const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + m_bEdit = !bProtectedBM; + m_bDelete = !bProtectedBM; + } + break; + case ContentTypeId::REGION : + { + std::unique_ptr<SwContentArr> pOldMember; + if(!m_pMember) + m_pMember.reset( new SwContentArr ); + else if(!m_pMember->empty()) + { + pOldMember = std::move(m_pMember); + m_pMember.reset( new SwContentArr ); + } + const Point aNullPt; + m_nMemberCount = m_pWrtShell->GetSectionFormatCount(); + for(size_t i = 0; i < m_nMemberCount; ++i) + { + const SwSectionFormat* pFormat; + SectionType eTmpType; + if( (pFormat = &m_pWrtShell->GetSectionFormat(i))->IsInNodesArr() && + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + const OUString& rSectionName = + pFormat->GetSection()->GetSectionName(); + sal_uInt8 nLevel = 0; + SwSectionFormat* pParentFormat = pFormat->GetParent(); + while(pParentFormat) + { + nLevel++; + pParentFormat = pParentFormat->GetParent(); + } + + std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, rSectionName, + nLevel, + pFormat->FindLayoutRect( false, &aNullPt ).Top())); + + SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr ); + if( !pFormat->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + } + m_nMemberCount = m_pMember->size(); + m_sTypeToken = "region"; + m_bEdit = true; + m_bDelete = false; + if(pOldMember) + { + if(nullptr != pbInvalidateWindow) + { + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + *pbInvalidateWindow = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + } + break; + case ContentTypeId::INDEX: + { + m_nMemberCount = m_pWrtShell->GetTOXCount(); + m_bEdit = true; + m_bDelete = false; + } + break; + case ContentTypeId::REFERENCE: + { + m_nMemberCount = m_pWrtShell->GetRefMarks(); + m_bDelete = false; + } + break; + case ContentTypeId::URLFIELD: + { + m_nMemberCount = 0; + if(!m_pMember) + m_pMember.reset( new SwContentArr ); + else + m_pMember->clear(); + + m_nMemberCount = lcl_InsertURLFieldContent(m_pMember.get(), m_pWrtShell, this); + + m_bEdit = true; + nOldMemberCount = m_nMemberCount; + m_bDelete = true; + } + break; + case ContentTypeId::POSTIT: + { + m_nMemberCount = 0; + if(!m_pMember) + m_pMember.reset( new SwContentArr ); + else + m_pMember->clear(); + + SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); + if (aMgr) + { + for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i) + { + if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit + { + if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc() && + (*i)->mLayoutStatus!=SwPostItHelper::INVISIBLE ) + { + OUString sEntry = pFormatField->GetField()->GetPar2(); + sEntry = RemoveNewline(sEntry); + std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( + this, + sEntry, + pFormatField, + m_nMemberCount)); + m_pMember->insert(std::move(pCnt)); + m_nMemberCount++; + } + } + } + } + m_sTypeToken.clear(); + m_bEdit = true; + nOldMemberCount = m_nMemberCount; + } + break; + case ContentTypeId::DRAWOBJECT: + { + m_sTypeToken.clear(); + m_bEdit = true; + m_nMemberCount = 0; + SwDrawModel* pModel = m_pWrtShell->getIDocumentDrawModelAccess().GetDrawModel(); + if(pModel) + { + SdrPage* pPage = pModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + // #i51726# - all drawing objects can be named now + if (!pTemp->GetName().isEmpty()) + m_nMemberCount++; + } + } + } + break; + default: break; + } + // ... then, the data can also no longer be valid, + // apart from those which have already been corrected, + // then nOldMemberCount is nevertheless not so old. + if( nOldMemberCount != m_nMemberCount ) + m_bDataValid = false; +} + +SwContentType::~SwContentType() +{ +} + +const SwContent* SwContentType::GetMember(size_t nIndex) +{ + if(!m_bDataValid || !m_pMember) + { + FillMemberList(); + } + if(nIndex < m_pMember->size()) + return (*m_pMember)[nIndex].get(); + + return nullptr; +} + +void SwContentType::Invalidate() +{ + m_bDataValid = false; +} + +void SwContentType::FillMemberList(bool* pbLevelOrVisibilityChanged) +{ + std::unique_ptr<SwContentArr> pOldMember; + size_t nOldMemberCount = 0; + SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr ); + if(m_pMember && pbLevelOrVisibilityChanged) + { + pOldMember = std::move(m_pMember); + nOldMemberCount = pOldMember->size(); + m_pMember.reset( new SwContentArr ); + *pbLevelOrVisibilityChanged = false; + } + else if(!m_pMember) + m_pMember.reset( new SwContentArr ); + else + m_pMember->clear(); + switch(m_nContentType) + { + case ContentTypeId::OUTLINE : + { + const size_t nOutlineCount = m_nMemberCount = + m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + + size_t nPos = 0; + for (size_t i = 0; i < nOutlineCount; ++i) + { + const sal_uInt8 nLevel = m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(i); + if(nLevel >= m_nOutlineLevel ) + m_nMemberCount--; + else + { + if (!m_pWrtShell->getIDocumentOutlineNodesAccess()->isOutlineInLayout(i, *m_pWrtShell->GetLayout())) + { + --m_nMemberCount; + continue; // don't hide it, just skip it + } + OUString aEntry(comphelper::string::stripStart( + m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(i, m_pWrtShell->GetLayout()), ' ')); + aEntry = SwNavigationPI::CleanEntry(aEntry); + std::unique_ptr<SwOutlineContent> pCnt(new SwOutlineContent(this, aEntry, i, nLevel, + m_pWrtShell->IsOutlineMovable( i ), nPos )); + m_pMember->insert(std::move(pCnt)); + // with the same number and existing "pOldMember" the + // old one is compared with the new OutlinePos. + if (nOldMemberCount > nPos && static_cast<SwOutlineContent*>((*pOldMember)[nPos].get())->GetOutlineLevel() != nLevel) + *pbLevelOrVisibilityChanged = true; + + nPos++; + } + } + + } + break; + + case ContentTypeId::TABLE : + { + const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true); + OSL_ENSURE(m_nMemberCount == nCount, "MemberCount differs"); + Point aNullPt; + m_nMemberCount = nCount; + for(size_t i = 0; i < m_nMemberCount; ++i) + { + const SwFrameFormat& rTableFormat = m_pWrtShell->GetTableFrameFormat(i, true); + const OUString& sTableName( rTableFormat.GetName() ); + + SwContent* pCnt = new SwContent(this, sTableName, + rTableFormat.FindLayoutRect(false, &aNullPt).Top() ); + if( !rTableFormat.GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + pCnt->SetInvisible(); + + m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); + + if(nOldMemberCount > i && + (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible()) + *pbLevelOrVisibilityChanged = true; + } + } + break; + case ContentTypeId::OLE : + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + { + FlyCntType eType = FLYCNTTYPE_FRM; + if(m_nContentType == ContentTypeId::OLE) + eType = FLYCNTTYPE_OLE; + else if(m_nContentType == ContentTypeId::GRAPHIC) + eType = FLYCNTTYPE_GRF; + Point aNullPt; + m_nMemberCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true); + std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true)); + SAL_WARN_IF(m_nMemberCount != formats.size(), "sw.ui", "MemberCount differs"); + m_nMemberCount = formats.size(); + for (size_t i = 0; i < m_nMemberCount; ++i) + { + SwFrameFormat const*const pFrameFormat = formats[i]; + const OUString sFrameName = pFrameFormat->GetName(); + + SwContent* pCnt; + if(ContentTypeId::GRAPHIC == m_nContentType) + { + OUString sLink; + m_pWrtShell->GetGrfNms( &sLink, nullptr, static_cast<const SwFlyFrameFormat*>( pFrameFormat)); + pCnt = new SwGraphicContent(this, sFrameName, + INetURLObject::decode( sLink, + INetURLObject::DecodeMechanism::Unambiguous ), + pFrameFormat->FindLayoutRect(false, &aNullPt).Top()); + } + else + { + pCnt = new SwContent(this, sFrameName, + pFrameFormat->FindLayoutRect(false, &aNullPt).Top() ); + } + if( !pFrameFormat->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + pCnt->SetInvisible(); + m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); + if (nOldMemberCount > i && + (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible()) + *pbLevelOrVisibilityChanged = true; + } + } + break; + case ContentTypeId::BOOKMARK: + { + IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); + ++ppBookmark) + { + if(lcl_IsUiVisibleBookmark(*ppBookmark)) + { + const OUString& rBkmName = (*ppBookmark)->GetName(); + //nYPos from 0 -> text::Bookmarks will be sorted alphabetically + std::unique_ptr<SwContent> pCnt(new SwContent(this, rBkmName, 0)); + m_pMember->insert(std::move(pCnt)); + } + } + } + break; + case ContentTypeId::REGION : + { + const Point aNullPt; + m_nMemberCount = m_pWrtShell->GetSectionFormatCount(); + for(size_t i = 0; i < m_nMemberCount; ++i) + { + const SwSectionFormat* pFormat; + SectionType eTmpType; + if( (pFormat = &m_pWrtShell->GetSectionFormat(i))->IsInNodesArr() && + (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent + && SectionType::ToxHeader != eTmpType ) + { + OUString sSectionName = pFormat->GetSection()->GetSectionName(); + + sal_uInt8 nLevel = 0; + SwSectionFormat* pParentFormat = pFormat->GetParent(); + while(pParentFormat) + { + nLevel++; + pParentFormat = pParentFormat->GetParent(); + } + + std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, sSectionName, + nLevel, + pFormat->FindLayoutRect( false, &aNullPt ).Top())); + if( !pFormat->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + pCnt->SetInvisible(); + m_pMember->insert(std::move(pCnt)); + } + + if(nullptr != pbLevelOrVisibilityChanged) + { + assert(pOldMember); + // need to check visibility (and equal entry number) after + // creation due to a sorted list being used here (before, + // entries with same index were compared already at creation + // time what worked before a sorted list was used) + *pbLevelOrVisibilityChanged = checkVisibilityChanged( + *pOldMember, + *m_pMember); + } + } + m_nMemberCount = m_pMember->size(); + } + break; + case ContentTypeId::REFERENCE: + { + std::vector<OUString> aRefMarks; + m_nMemberCount = m_pWrtShell->GetRefMarks( &aRefMarks ); + + for (const auto& rRefMark : aRefMarks) + { + // References sorted alphabetically + m_pMember->insert(std::make_unique<SwContent>(this, rRefMark, 0)); + } + } + break; + case ContentTypeId::URLFIELD: + m_nMemberCount = lcl_InsertURLFieldContent(m_pMember.get(), m_pWrtShell, this); + break; + case ContentTypeId::INDEX: + { + + const sal_uInt16 nCount = m_pWrtShell->GetTOXCount(); + m_nMemberCount = nCount; + for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ ) + { + const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox ); + OUString sTOXNm( pBase->GetTOXName() ); + + SwContent* pCnt = new SwTOXBaseContent( + this, sTOXNm, nTox, *pBase); + + if( !pBase->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + pCnt->SetInvisible(); + + m_pMember->insert( std::unique_ptr<SwContent>(pCnt) ); + const size_t nPos = m_pMember->size() - 1; + if(nOldMemberCount > nPos && + (*pOldMember)[nPos]->IsInvisible() + != pCnt->IsInvisible()) + *pbLevelOrVisibilityChanged = true; + } + } + break; + case ContentTypeId::POSTIT: + { + m_nMemberCount = 0; + m_pMember->clear(); + SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); + if (aMgr) + { + for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i) + { + if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit + { + if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc() && + (*i)->mLayoutStatus!=SwPostItHelper::INVISIBLE ) + { + OUString sEntry = pFormatField->GetField()->GetPar2(); + sEntry = RemoveNewline(sEntry); + std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( + this, + sEntry, + pFormatField, + m_nMemberCount)); + m_pMember->insert(std::move(pCnt)); + m_nMemberCount++; + } + } + } + } + } + break; + case ContentTypeId::DRAWOBJECT: + { + m_nMemberCount = 0; + m_pMember->clear(); + + IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess(); + SwDrawModel* pModel = rIDDMA.GetDrawModel(); + if(pModel) + { + SdrPage* pPage = pModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + // #i51726# - all drawing objects can be named now + if (!pTemp->GetName().isEmpty()) + { + SwContact* pContact = static_cast<SwContact*>(pTemp->GetUserCall()); + long nYPos = 0; + const Point aNullPt; + if(pContact && pContact->GetFormat()) + nYPos = pContact->GetFormat()->FindLayoutRect(false, &aNullPt).Top(); + SwContent* pCnt = new SwContent( + this, + pTemp->GetName(), + nYPos); + if(!rIDDMA.IsVisibleLayerId(pTemp->GetLayer())) + pCnt->SetInvisible(); + m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); + m_nMemberCount++; + if (nOldMemberCount > i && + (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible() ) + *pbLevelOrVisibilityChanged = true; + } + } + } + } + break; + default: break; + } + m_bDataValid = true; +} + +namespace { + +enum STR_CONTEXT_IDX +{ + IDX_STR_OUTLINE_LEVEL = 0, + IDX_STR_DRAGMODE = 1, + IDX_STR_HYPERLINK = 2, + IDX_STR_LINK_REGION = 3, + IDX_STR_COPY_REGION = 4, + IDX_STR_DISPLAY = 5, + IDX_STR_ACTIVE_VIEW = 6, + IDX_STR_HIDDEN = 7, + IDX_STR_ACTIVE = 8, + IDX_STR_INACTIVE = 9, + IDX_STR_EDIT_ENTRY = 10, + IDX_STR_DELETE_ENTRY = 11, + IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 12, + IDX_STR_OUTLINE_TRACKING = 13, + IDX_STR_OUTLINE_TRACKING_DEFAULT = 14, + IDX_STR_OUTLINE_TRACKING_FOCUS = 15, + IDX_STR_OUTLINE_TRACKING_OFF = 16 +}; + +} + +static const char* STR_CONTEXT_ARY[] = +{ + STR_OUTLINE_LEVEL, + STR_DRAGMODE, + STR_HYPERLINK, + STR_LINK_REGION, + STR_COPY_REGION, + STR_DISPLAY, + STR_ACTIVE_VIEW, + STR_HIDDEN, + STR_ACTIVE, + STR_INACTIVE, + STR_EDIT_ENTRY, + STR_DELETE_ENTRY, + STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY, + STR_OUTLINE_TRACKING, + STR_OUTLINE_TRACKING_DEFAULT, + STR_OUTLINE_TRACKING_FOCUS, + STR_OUTLINE_TRACKING_OFF +}; + +SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog) + : m_xTreeView(std::move(xTreeView)) + , m_aDropTargetHelper(*this) + , m_xDialog(pDialog) + , m_sSpace(OUString(" ")) + , m_sInvisible(SwResId(STR_INVISIBLE)) + , m_pHiddenShell(nullptr) + , m_pActiveShell(nullptr) + , m_pConfig(SW_MOD()->GetNavigationConfig()) + , m_nActiveBlock(0) + , m_nHiddenBlock(0) + , m_nEntryCount(0) + , m_nRootType(ContentTypeId::UNKNOWN) + , m_nLastSelType(ContentTypeId::UNKNOWN) + , m_nOutlineLevel(MAXLEVEL) + , m_eState(State::ACTIVE) + , m_bIsRoot(false) + , m_bIsIdleClear(false) + , m_bIsLastReadOnly(false) + , m_bIsOutlineMoveable(true) + , m_bViewHasChanged(false) +{ + Size aSize(m_xDialog->LogicToPixel(Size(110, 112), MapMode(MapUnit::MapAppFont))); + m_xTreeView->set_size_request(aSize.Width(), aSize.Height()); + + m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST); + + m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl)); + m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl)); + m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl)); + m_xTreeView->connect_changed(LINK(this, SwContentTree, SelectHdl)); + m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusHdl)); + m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl)); + m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl)); + m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl)); + m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl)); + + for (ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i] = nullptr; + m_aHiddenContentArr[i] = nullptr; + } + for (int i = 0; i < CONTEXT_COUNT; ++i) + { + m_aContextStrings[i] = SwResId(STR_CONTEXT_ARY[i]); + } + m_nActiveBlock = m_pConfig->GetActiveBlock(); + m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate)); + m_aUpdTimer.SetTimeout(1000); +} + +SwContentTree::~SwContentTree() +{ + clear(); // If applicable erase content types previously. + m_aUpdTimer.Stop(); + SetActiveShell(nullptr); + m_xDialog.clear(); +} + +// Drag&Drop methods +IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = true; + + bool bDisallow = true; + + // don't allow if tree root is selected + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_selected(xEntry.get()); + if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView)) + { + return true; // disallow + } + + rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer; + sal_Int8 nDragMode = DND_ACTION_COPYMOVE | DND_ACTION_LINK; + + if (FillTransferData(*xContainer, nDragMode)) + bDisallow = false; + + if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) + { + // Only move drag entry and continuous selected siblings: + m_aDndOutlinesSelected.clear(); + + std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator()); + + // Find first selected of continuous siblings + while (true) + { + m_xTreeView->copy_iterator(*xEntry, *xScratch); + if (!m_xTreeView->iter_previous_sibling(*xScratch)) + break; + if (!m_xTreeView->is_selected(*xScratch)) + break; + m_xTreeView->copy_iterator(*xScratch, *xEntry); + } + // Record continuous selected siblings + do + { + m_aDndOutlinesSelected.push_back(m_xTreeView->make_iterator(xEntry.get())); + } + while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry)); + bDisallow = false; + } + + if (!bDisallow) + m_xTreeView->enable_drag_source(xContainer, nDragMode); + return bDisallow; +} + +SwContentTreeDropTarget::SwContentTreeDropTarget(SwContentTree& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 SwContentTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt); + + if (nAccept != DND_ACTION_NONE) + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr); + } + + return nAccept; +} + +bool SwContentTree::IsInDrag() const +{ + return m_xTreeView->get_drag_source() == m_xTreeView.get(); +} + +// QueryDrop will be executed in the navigator +sal_Int8 SwContentTree::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nRet = DND_ACTION_NONE; + if( m_bIsRoot ) + { + if( m_bIsOutlineMoveable ) + nRet = rEvt.mnAction; + } + else if (!IsInDrag()) + nRet = GetParentWindow()->AcceptDrop(); + return nRet; +} + +// Drop will be executed in the navigator +static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent) +{ + void* key = nullptr; + if (pContent) + { + SwWrtShell* pShell = rTree.GetWrtShell(); + auto const nPos = pContent->GetOutlinePos(); + + key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + } + return key; +} + +sal_Int8 SwContentTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + return m_rTreeView.ExecuteDrop(rEvt); +} + +sal_Int8 SwContentTree::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get())) + xDropEntry.reset(); + + if (m_nRootType == ContentTypeId::OUTLINE) + { + if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry).toInt64()))); + SwOutlineContent* pOutlineContent = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry).toInt64()); + assert(pOutlineContent); + + void* key = lcl_GetOutlineKey(*this, pOutlineContent); + assert(key); + if (!mOutLineNodeMap[key]) + { + while (m_xTreeView->iter_has_child(*xDropEntry)) + { + std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get())); + bool bChildEntry = m_xTreeView->iter_children(*xChildEntry); + while (bChildEntry) + { + m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry); + bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry); + } + } + } + } + + SwOutlineNodes::size_type nTargetPos = 0; + if (!xDropEntry) + { + // dropped in blank space -> move to bottom + nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1; + } + else if (!lcl_IsContent(*xDropEntry, *m_xTreeView)) + { + // dropped on "heading" parent -> move to start + nTargetPos = SwOutlineNodes::npos; + } + else + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry).toInt64()))); + nTargetPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry).toInt64())->GetOutlinePos(); + } + + if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed. + nTargetPos != SwOutlineNodes::npos) + { + std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get())); + bool bNext = m_xTreeView->iter_next(*xNext); + if (bNext) + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xNext).toInt64()))); + nTargetPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xNext).toInt64())->GetOutlinePos() - 1; + } + else + nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1; + } + + // remove the drop highlight before we change the contents of the tree so we don't + // try and dereference a removed entry in post-processing drop + m_xTreeView->unset_drag_dest_row(); + MoveOutline(nTargetPos); + + } + return IsInDrag() ? DND_ACTION_NONE : GetParentWindow()->ExecuteDrop(rEvt); +} + +namespace +{ + bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry) + { + if (!rContentTree.get_row_expanded(rEntry)) + return false; + + if (!rContentTree.iter_has_child(rEntry)) + return false; + + std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry)); + (void)rContentTree.iter_children(*xChild); + + do + { + if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild)) + { + if (!IsAllExpanded(rContentTree, *xChild)) + return false; + } + } + while (rContentTree.iter_next_sibling(*xChild)); + return true; + } + + void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry) + { + bool bExpand = !IsAllExpanded(rContentTree, rEntry); + bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry); + int nRefDepth = rContentTree.get_iter_depth(rEntry); + while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth) + { + if (rContentTree.iter_has_child(rEntry)) + bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry); + } + } +} + +// Handler for Dragging and ContextMenu +static bool lcl_InsertExpandCollapseAllItem(weld::TreeView& rContentTree, weld::TreeIter& rEntry, weld::Menu& rPop) +{ + if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)) + { + rPop.set_label(OString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL)); + return false; + } + return true; +} + +IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/navigatorcontextmenu.ui")); + std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu"); + + bool bOutline(false); + std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu("outlinelevel"); + std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu("dragmodemenu"); + std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu("displaymenu"); + std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu("outlinetracking"); + + for(int i = 1; i <= 3; ++i) + xSubPopOutlineTracking->append_radio(OUString::number(i + 10), m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]); + xSubPopOutlineTracking->set_active(OString::number(10 + m_nOutlineTracking), true); + + for (int i = 1; i <= MAXLEVEL; ++i) + xSubPop1->append_radio(OUString::number(i + 100), OUString::number(i)); + xSubPop1->set_active(OString::number(100 + m_nOutlineLevel), true); + + for (int i=0; i < 3; ++i) + xSubPop2->append_radio(OUString::number(i + 201), m_aContextStrings[IDX_STR_HYPERLINK + i]); + xSubPop2->set_active(OString::number(201 + static_cast<int>(GetParentWindow()->GetRegionDropMode())), true); + + // Insert the list of the open files + sal_uInt16 nId = 301; + const SwView* pActiveView = ::GetActiveView(); + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + OUString sInsert = pView->GetDocShell()->GetTitle(); + if (pView == pActiveView) + { + sInsert += "(" + + m_aContextStrings[IDX_STR_ACTIVE] + + ")"; + } + xSubPop3->append_radio(OUString::number(nId), sInsert); + if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell()) + xSubPop3->set_active(OString::number(nId), true); + pView = SwModule::GetNextView(pView); + nId++; + } + xSubPop3->append_radio(OUString::number(nId++), m_aContextStrings[IDX_STR_ACTIVE_VIEW]); + if (m_pHiddenShell) + { + OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() + + " ( " + + m_aContextStrings[IDX_STR_HIDDEN] + + " )"; + xSubPop3->append_radio(OUString::number(nId), sHiddenEntry); + } + + if (State::ACTIVE == m_eState) + xSubPop3->set_active(OString::number(--nId), true); + else if (State::HIDDEN == m_eState) + xSubPop3->set_active(OString::number(nId), true); + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + xEntry.reset(); + + if (!xEntry || !lcl_IsContent(*xEntry, *m_xTreeView)) + xPop->remove(OString::number(900)); // go to + + bool bRemovePostItEntries = true; + bool bRemoveIndexEntries = true; + bool bRemoveEditEntry = true; + bool bRemoveUnprotectEntry = true; + bool bRemoveDeleteEntry = true; + bool bRemoveRenameEntry = true; + bool bRemoveSelectEntry = true; + bool bRemoveToggleExpandEntry = true; + bool bRemoveChapterEntries = true; + bool bRemoveSendOutlineEntry = true; + + // Edit only if the shown content is coming from the current view. + if ((State::ACTIVE == m_eState || m_pActiveShell == pActiveView->GetWrtShellPtr()) + && xEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + const SwContentType* pContType = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetParent(); + const ContentTypeId nContentType = pContType->GetType(); + const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); + const bool bVisible = !reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->IsInvisible(); + const bool bProtected = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->IsProtect(); + const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType) + && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + const bool bEditable = pContType->IsEditable() && + ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + const bool bDeletable = pContType->IsDeletable() && + ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + const bool bRenamable = bEditable && !bReadonly && + (ContentTypeId::TABLE == nContentType || + ContentTypeId::FRAME == nContentType || + ContentTypeId::GRAPHIC == nContentType || + ContentTypeId::OLE == nContentType || + (ContentTypeId::BOOKMARK == nContentType && !bProtectBM) || + ContentTypeId::REGION == nContentType || + ContentTypeId::INDEX == nContentType || + ContentTypeId::DRAWOBJECT == nContentType); + + if (!bReadonly && (bEditable || bDeletable)) + { + if(ContentTypeId::INDEX == nContentType) + { + bRemoveIndexEntries = false; + + const SwTOXBase* pBase = reinterpret_cast<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetTOXBase(); + if (!pBase->IsTOXBaseInReadonly()) + bRemoveEditEntry = false; + + xPop->set_active(OString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase)); + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::TABLE == nContentType) + { + bRemoveSelectEntry = false; + bRemoveEditEntry = false; + bRemoveUnprotectEntry = false; + bool bFull = false; + OUString sTableName = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetName(); + bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull ); + xPop->set_sensitive(OString::number(403), !bFull); + xPop->set_sensitive(OString::number(404), bProt); + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::OUTLINE == nContentType) + { + bOutline = true; + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); + bRemoveSelectEntry = false; + bRemoveChapterEntries = false; + } + else if(ContentTypeId::DRAWOBJECT == nContentType) + { + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::REGION == nContentType) + { + bRemoveSelectEntry = false; + bRemoveEditEntry = false; + } + else + { + if (bEditable && bDeletable) + { + bRemoveEditEntry = false; + bRemoveDeleteEntry = false; + } + else if (bEditable) + bRemoveEditEntry = false; + else if (bDeletable) + { + bRemoveDeleteEntry = false; + } + } + //Rename object + if (bRenamable) + bRemoveRenameEntry = false; + } + } + else if (xEntry) + { + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + SwContentType* pType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); + if (ContentTypeId::OUTLINE == pType->GetType()) + { + bOutline = true; + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); + bRemoveSendOutlineEntry = false; + } + if ( (pType->GetType() == ContentTypeId::POSTIT) && (!m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) && ( pType->GetMemberCount() > 0) ) + bRemovePostItEntries = false; + } + + if (bRemoveToggleExpandEntry) + { + xPop->remove("separator3"); + xPop->remove(OString::number(800)); + } + + if (bRemoveSelectEntry) + xPop->remove(OString::number(805)); + + if (bRemoveChapterEntries) + { + xPop->remove("separator2"); + xPop->remove(OString::number(806)); + xPop->remove(OString::number(801)); + xPop->remove(OString::number(802)); + xPop->remove(OString::number(803)); + xPop->remove(OString::number(804)); + } + + if (bRemoveSendOutlineEntry) + xPop->remove(OString::number(700)); + + if (bRemovePostItEntries) + { + xPop->remove(OString::number(600)); + xPop->remove(OString::number(601)); + xPop->remove(OString::number(602)); + } + + if (bRemoveDeleteEntry) + xPop->remove(OString::number(501)); + + if (bRemoveRenameEntry) + xPop->remove(OString::number(502)); + + if (bRemoveIndexEntries) + { + xPop->remove(OString::number(401)); + xPop->remove(OString::number(402)); + xPop->remove(OString::number(405)); + } + + if (bRemoveUnprotectEntry) + xPop->remove(OString::number(404)); + + if (bRemoveEditEntry) + xPop->remove(OString::number(403)); + + if (bRemoveToggleExpandEntry && + bRemoveSelectEntry && + bRemoveChapterEntries && + bRemoveSendOutlineEntry && + bRemovePostItEntries && + bRemoveDeleteEntry && + bRemoveRenameEntry && + bRemoveIndexEntries && + bRemoveUnprotectEntry && + bRemoveEditEntry) + { + xPop->remove("separator1"); + } + + if (!bOutline) + { + xSubPop1.reset(); + xPop->remove(OString::number(1)); // outline level menu + xSubPopOutlineTracking.reset(); + xPop->remove(OString::number(4)); // outline tracking menu + } + + OString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + return true; +} + +void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId, + const OUString* pExpanderName, bool bChildrenOnDemand, weld::TreeIter* pRet) +{ + m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, pExpanderName, bChildrenOnDemand, pRet); + ++m_nEntryCount; +} + +void SwContentTree::remove(const weld::TreeIter& rIter) +{ + if (m_xTreeView->iter_has_child(rIter)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter); + (void)m_xTreeView->iter_children(*xChild); + remove(*xChild); + } + m_xTreeView->remove(rIter); + --m_nEntryCount; +} + +// Content will be integrated into the Box only on demand. +bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent) +{ + bool bChild = m_xTreeView->iter_has_child(rParent); + if (bChild || !m_xTreeView->get_children_on_demand(rParent)) + return bChild; + + // Is this a content type? + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); + SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); + + const size_t nCount = pCntType->GetMemberCount(); + // Add for outline plus/minus + if (pCntType->GetType() == ContentTypeId::OUTLINE) + { + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + if(pCnt) + { + const auto nLevel = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel(); + OUString sEntry = pCnt->GetName(); + if(sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + if (!bChild || (nLevel == 0)) + { + insert(&rParent, sEntry, sId, nullptr, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild)); + bChild = true; + } + else + { + //back search parent. + if(static_cast<const SwOutlineContent*>(pCntType->GetMember(i-1))->GetOutlineLevel() < nLevel) + { + insert(xChild.get(), sEntry, sId, nullptr, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild)); + bChild = true; + } + else + { + bChild = m_xTreeView->iter_previous(*xChild); + assert(!bChild || lcl_IsContentType(*xChild, *m_xTreeView) || dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xChild).toInt64()))); + while (bChild && + lcl_IsContent(*xChild, *m_xTreeView) && + (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xChild).toInt64())->GetOutlineLevel() >= nLevel) + ) + { + bChild = m_xTreeView->iter_previous(*xChild); + } + if (bChild) + { + insert(xChild.get(), sEntry, sId, nullptr, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild)); + } + } + } + } + } + } + else + { + bool bRegion = pCntType->GetType() == ContentTypeId::REGION; + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + if (pCnt) + { + OUString sEntry = pCnt->GetName(); + if (sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + insert(&rParent, sEntry, sId, nullptr, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + if (bRegion) + m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel()); + bChild = true; + } + } + } + } + + return bChild; +} + +SdrObject* SwContentTree::GetDrawingObjectsByContent(const SwContent *pCnt) +{ + SdrObject *pRetObj = nullptr; + switch(pCnt->GetParent()->GetType()) + { + case ContentTypeId::DRAWOBJECT: + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + if( pTemp->GetName() == pCnt->GetName()) + { + pRetObj = pTemp; + break; + } + } + } + break; + } + default: + pRetObj = nullptr; + } + return pRetObj; +} + +void SwContentTree::Expand(const weld::TreeIter& rParent, std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand) +{ + if (!(m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent))) + return; + + if (!m_bIsRoot + || (lcl_IsContentType(rParent, *m_xTreeView) && + reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64())->GetType() == ContentTypeId::OUTLINE) + || (m_nRootType == ContentTypeId::OUTLINE)) + { + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); + const sal_Int32 nOr = 1 << static_cast<int>(pCntType->GetType()); //linear -> Bitposition + if (State::HIDDEN != m_eState) + { + m_nActiveBlock |= nOr; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock |= nOr; + if (pCntType->GetType() == ContentTypeId::OUTLINE) + { + std::map< void*, bool > aCurrOutLineNodeMap; + + SwWrtShell* pShell = GetWrtShell(); + bool bParentHasChild = RequestingChildren(rParent); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent)); + if (bParentHasChild) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent)); + bool bChild = m_xTreeView->iter_next(*xChild); + while (bChild && lcl_IsContent(*xChild, *m_xTreeView)) + { + if (m_xTreeView->iter_has_child(*xChild)) + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xChild).toInt64()))); + auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xChild).toInt64())->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + aCurrOutLineNodeMap.emplace( key, false ); + std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key ); + if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key]) + { + aCurrOutLineNodeMap[key] = true; + RequestingChildren(*xChild); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get())); + m_xTreeView->set_children_on_demand(*xChild, false); + } + } + bChild = m_xTreeView->iter_next(*xChild); + } + } + mOutLineNodeMap = aCurrOutLineNodeMap; + return; + } + } + else + { + if (lcl_IsContent(rParent, *m_xTreeView)) + { + SwWrtShell* pShell = GetWrtShell(); + // paranoid assert now that outline type is checked + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); + auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rParent).toInt64())->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = true; + } + } + } + + RequestingChildren(rParent); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent)); +} + +IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool) +{ + Expand(rParent, nullptr); + return true; +} + +IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool) +{ + if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent)) + return true; + + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + if (m_bIsRoot) + { + // collapse to children of root node + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent)); + if (m_xTreeView->iter_children(*xEntry)) + { + do + { + m_xTreeView->collapse_row(*xEntry); + } + while (m_xTreeView->iter_next(*xEntry)); + } + return false; // return false to notify caller not to do collapse + } + SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); + const sal_Int32 nAnd = ~(1 << static_cast<int>(pCntType->GetType())); + if (State::HIDDEN != m_eState) + { + m_nActiveBlock &= nAnd; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock &= nAnd; + } + else if (lcl_IsContent(rParent, *m_xTreeView)) + { + SwWrtShell* pShell = GetWrtShell(); + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); + auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rParent).toInt64())->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = false; + } + + return true; +} + +// Also on double click will be initially opened only. +IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool) +{ + bool bConsumed = false; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + // Is it a content type? + OSL_ENSURE(bEntry, "no current entry!"); + if (bEntry) + { + if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry)) + { + RequestingChildren(*xEntry); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState)) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop(); + } + //Jump to content type: + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64()); + assert(pCnt && "no UserData"); + GotoContent(pCnt); + const ContentTypeId nActType = pCnt->GetParent()->GetType(); + if (nActType == ContentTypeId::FRAME) + m_pActiveShell->EnterStdMode(); + // fdo#36308 don't expand outlines on double-click + bConsumed = nActType == ContentTypeId::OUTLINE; + } + } + + return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children +} + +namespace +{ + OUString GetImageIdForContentTypeId(ContentTypeId eType) + { + OUString sResId; + + switch (eType) + { + case ContentTypeId::OUTLINE: + sResId = RID_BMP_NAVI_OUTLINE; + break; + case ContentTypeId::TABLE: + sResId = RID_BMP_NAVI_TABLE; + break; + case ContentTypeId::FRAME: + sResId = RID_BMP_NAVI_FRAME; + break; + case ContentTypeId::GRAPHIC: + sResId = RID_BMP_NAVI_GRAPHIC; + break; + case ContentTypeId::OLE: + sResId = RID_BMP_NAVI_OLE; + break; + case ContentTypeId::BOOKMARK: + sResId = RID_BMP_NAVI_BOOKMARK; + break; + case ContentTypeId::REGION: + sResId = RID_BMP_NAVI_REGION; + break; + case ContentTypeId::URLFIELD: + sResId = RID_BMP_NAVI_URLFIELD; + break; + case ContentTypeId::REFERENCE: + sResId = RID_BMP_NAVI_REFERENCE; + break; + case ContentTypeId::INDEX: + sResId = RID_BMP_NAVI_INDEX; + break; + case ContentTypeId::POSTIT: + sResId = RID_BMP_NAVI_POSTIT; + break; + case ContentTypeId::DRAWOBJECT: + sResId = RID_BMP_NAVI_DRAWOBJECT; + break; + case ContentTypeId::UNKNOWN: + SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview"); + break; + } + + return sResId; + }; +} + +size_t SwContentTree::GetAbsPos(const weld::TreeIter& rIter) +{ + return weld::GetAbsPos(*m_xTreeView, rIter); +} + +size_t SwContentTree::GetEntryCount() const +{ + return m_nEntryCount; +} + +size_t SwContentTree::GetChildCount(const weld::TreeIter& rParent) const +{ + if (!m_xTreeView->iter_has_child(rParent)) + return 0; + + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent)); + + size_t nCount = 0; + auto nRefDepth = m_xTreeView->get_iter_depth(*xParent); + auto nActDepth = nRefDepth; + do + { + if (!m_xTreeView->iter_next(*xParent)) + xParent.reset(); + else + nActDepth = m_xTreeView->get_iter_depth(*xParent); + nCount++; + } while(xParent && nRefDepth < nActDepth); + + nCount--; + return nCount; +} + +std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xEntry)) + xEntry.reset(); + + while (nAbsPos && xEntry) + { + if (!m_xTreeView->iter_next(*xEntry)) + xEntry.reset(); + nAbsPos--; + } + return xEntry; +} + +void SwContentTree::Display( bool bActive ) +{ + // First read the selected entry to select it later again if necessary + // -> the user data here are no longer valid! + std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xOldSelEntry.get())) + xOldSelEntry.reset(); + OUString sEntryName; // Name of the entry + size_t nEntryRelPos = 0; // relative position to their parent + size_t nOldEntryCount = GetEntryCount(); + sal_Int32 nOldScrollPos = 0; + if (xOldSelEntry) + { + UpdateLastSelType(); + + nOldScrollPos = m_xTreeView->vadjustment_get_value(); + sEntryName = m_xTreeView->get_text(*xOldSelEntry); + std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get()); + while (m_xTreeView->get_iter_depth(*xParentEntry)) + m_xTreeView->iter_parent(*xParentEntry); + if (m_xTreeView->get_iter_depth(*xOldSelEntry)) + nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry); + } + + clear(); + + if (!bActive) + m_eState = State::HIDDEN; + else if (State::HIDDEN == m_eState) + m_eState = State::ACTIVE; + SwWrtShell* pShell = GetWrtShell(); + const bool bReadOnly = !pShell || pShell->GetView().GetDocShell()->IsReadOnly(); + if(bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + bool bDisable = pShell == nullptr || bReadOnly; + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent3ToolBox->set_item_sensitive("chapterup", !bDisable); + pNavi->m_xContent3ToolBox->set_item_sensitive("chapterdown", !bDisable); + pNavi->m_xContent3ToolBox->set_item_sensitive("promote", !bDisable); + pNavi->m_xContent3ToolBox->set_item_sensitive("demote", !bDisable); + pNavi->m_xContent2ToolBox->set_item_sensitive("reminder", !bDisable); + } + + if (pShell) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + std::unique_ptr<weld::TreeIter> xSelEntry; + // all content navigation view + if(m_nRootType == ContentTypeId::UNKNOWN) + { + m_xTreeView->freeze(); + + std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand; + + for( ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>() ) + { + std::unique_ptr<SwContentType>& rpContentT = bActive ? + m_aActiveContentArr[nCntType] : + m_aHiddenContentArr[nCntType]; + if(!rpContentT) + rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel )); + + OUString sEntry = rpContentT->GetName(); + OUString aImage(GetImageIdForContentTypeId(nCntType)); + bool bChOnDemand = 0 != rpContentT->GetMemberCount(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(rpContentT.get()))); + insert(nullptr, sEntry, sId, &aImage, bChOnDemand, xEntry.get()); + + m_xTreeView->set_sensitive(*xEntry, bChOnDemand); + + if (nCntType == m_nLastSelType) + xSelEntry = m_xTreeView->make_iterator(xEntry.get()); + sal_Int32 nExpandOptions = (State::HIDDEN == m_eState) + ? m_nHiddenBlock + : m_nActiveBlock; + if (nExpandOptions & (1 << static_cast<int>(nCntType))) + { + // fill contents of to-be expanded entries while frozen + Expand(*xEntry, &aNodesToExpand); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + } + + m_xTreeView->thaw(); + + // restore visual expanded tree state + for (const auto& rNode : aNodesToExpand) + m_xTreeView->expand_row(*rNode); + + (void)m_xTreeView->get_iter_first(*xEntry); + for (ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>()) + { + sal_Int32 nExpandOptions = (State::HIDDEN == m_eState) + ? m_nHiddenBlock + : m_nActiveBlock; + if (nExpandOptions & (1 << static_cast<int>(nCntType))) + { + if (nEntryRelPos && nCntType == m_nLastSelType) + { + // reselect the entry + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); + std::unique_ptr<weld::TreeIter> xTemp; + sal_uLong nPos = 1; + while (m_xTreeView->iter_next(*xChild)) + { + // The old text will be slightly favored + if (sEntryName == m_xTreeView->get_text(*xChild) || + nPos == nEntryRelPos) + { + m_xTreeView->copy_iterator(*xChild, *xSelEntry); + break; + } + xTemp = m_xTreeView->make_iterator(xChild.get()); + nPos++; + } + if (!xSelEntry || lcl_IsContentType(*xSelEntry, *m_xTreeView)) + xSelEntry = std::move(xTemp); + } + } + + (void)m_xTreeView->iter_next_sibling(*xEntry); + } + + if (!xSelEntry) + { + nOldScrollPos = 0; + xSelEntry = m_xTreeView->make_iterator(); + if (!m_xTreeView->get_iter_first(*xSelEntry)) + xSelEntry.reset(); + } + + if (xSelEntry) + { + m_xTreeView->set_cursor(*xSelEntry); + Select(); + } + } + // root content navigation view + else + { + m_xTreeView->freeze(); + + std::unique_ptr<SwContentType>& rpRootContentT = bActive ? + m_aActiveContentArr[m_nRootType] : + m_aHiddenContentArr[m_nRootType]; + if(!rpRootContentT) + rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel )); + OUString aImage(GetImageIdForContentTypeId(m_nRootType)); + bool bChOnDemand = m_nRootType == ContentTypeId::OUTLINE; + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(rpRootContentT.get()))); + insert(nullptr, rpRootContentT->GetName(), sId, &aImage, bChOnDemand, xEntry.get()); + + if (!bChOnDemand) + { + bool bRegion = rpRootContentT->GetType() == ContentTypeId::REGION; + + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + for (size_t i = 0; i < rpRootContentT->GetMemberCount(); ++i) + { + const SwContent* pCnt = rpRootContentT->GetMember(i); + if (pCnt) + { + OUString sEntry = pCnt->GetName(); + if(sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + insert(xEntry.get(), sEntry, sSubId, nullptr, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + if (bRegion) + m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel()); + } + } + } + else + { + RequestingChildren(*xEntry); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + + m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry)); + + m_xTreeView->thaw(); + + m_xTreeView->expand_row(*xEntry); + + // reselect the entry + if (nEntryRelPos) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); + sal_uLong nPos = 1; + while (m_xTreeView->iter_next(*xChild)) + { + // The old text will be slightly favored + if (sEntryName == m_xTreeView->get_text(*xChild) || nPos == nEntryRelPos) + { + xSelEntry = std::move(xChild); + break; + } + nPos++; + } + if (xSelEntry) + { + m_xTreeView->set_cursor(*xSelEntry); // unselect all entries, make pSelEntry visible, and select + Select(); + } + } + else + { + m_xTreeView->set_cursor(*xEntry); + Select(); + } + } + } + + if (!m_bIsInPromoteDemote && GetEntryCount() == nOldEntryCount) + { + m_xTreeView->vadjustment_set_value(nOldScrollPos); + } +} + +void SwContentTree::clear() +{ + m_xTreeView->freeze(); + m_xTreeView->clear(); + m_nEntryCount = 0; + m_xTreeView->thaw(); +} + +bool SwContentTree::FillTransferData( TransferDataContainer& rTransfer, + sal_Int8& rDragMode ) +{ + bool bRet = false; + SwWrtShell* pWrtShell = GetWrtShell(); + OSL_ENSURE(pWrtShell, "no Shell!"); + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell) + return false; + OUString sEntry; + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64()); + + const ContentTypeId nActType = pCnt->GetParent()->GetType(); + OUString sUrl; + bool bOutline = false; + OUString sOutlineText; + switch( nActType ) + { + case ContentTypeId::OUTLINE: + { + const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos(); + OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(), + "outlinecnt changed"); + + // make sure outline may actually be copied + if( pWrtShell->IsOutlineCopyable( nPos ) ) + { + const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule(); + const SwTextNode* pTextNd = + pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNode(nPos); + if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout())) + { + SwNumberTree::tNumberVector aNumVector = + pTextNd->GetNumberVector(pWrtShell->GetLayout()); + for( int nLevel = 0; + nLevel <= pTextNd->GetActualListLevel(); + nLevel++ ) + { + const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1; + sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + "."; + } + } + sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false); + sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout()); + m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable(); + bOutline = true; + } + } + break; + case ContentTypeId::POSTIT: + case ContentTypeId::INDEX: + case ContentTypeId::REFERENCE : + // cannot be inserted, neither as URL nor as section + break; + case ContentTypeId::URLFIELD: + sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL(); + [[fallthrough]]; + case ContentTypeId::OLE: + case ContentTypeId::GRAPHIC: + if(GetParentWindow()->GetRegionDropMode() != RegionMode::NONE) + break; + else + rDragMode &= ~( DND_ACTION_MOVE | DND_ACTION_LINK ); + [[fallthrough]]; + default: + sEntry = m_xTreeView->get_text(*xEntry); + } + + if(!sEntry.isEmpty()) + { + const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell(); + if(sUrl.isEmpty()) + { + if(pDocShell->HasName()) + { + SfxMedium* pMedium = pDocShell->GetMedium(); + sUrl = pMedium->GetURLObject().GetURLNoMark(); + // only if a primarily link shall be integrated. + bRet = true; + } + else if ( nActType == ContentTypeId::REGION || nActType == ContentTypeId::BOOKMARK ) + { + // For field and bookmarks a link is also allowed + // without a filename into its own document. + bRet = true; + } + else if (State::CONSTANT == m_eState && + ( !::GetActiveView() || + m_pActiveShell != ::GetActiveView()->GetWrtShellPtr())) + { + // Urls of inactive views cannot dragged without + // file names, also. + bRet = false; + } + else + { + bRet = GetParentWindow()->GetRegionDropMode() == RegionMode::NONE; + rDragMode = DND_ACTION_MOVE; + } + + const OUString& rToken = pCnt->GetParent()->GetTypeToken(); + sUrl += "#" + sEntry; + if(!rToken.isEmpty()) + { + sUrl += OUStringChar(cMarkSeparator) + rToken; + } + } + else + bRet = true; + + if( bRet ) + { + // In Outlines of heading text must match + // the real number into the description. + if(bOutline) + sEntry = sOutlineText; + + { + NaviContentBookmark aBmk( sUrl, sEntry, + GetParentWindow()->GetRegionDropMode(), + pDocShell); + aBmk.Copy( rTransfer ); + } + + // An INetBookmark must a be delivered to foreign DocShells + if( pDocShell->HasName() ) + { + INetBookmark aBkmk( sUrl, sEntry ); + rTransfer.CopyINetBookmark( aBkmk ); + } + } + } + return bRet; +} + +void SwContentTree::ToggleToRoot() +{ + if(!m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + const SwContentType* pCntType; + if (bEntry) + { + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); + } + else + { + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + pCntType = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetParent(); + } + m_nRootType = pCntType->GetType(); + m_bIsRoot = true; + Display(State::HIDDEN != m_eState); + if (m_nRootType == ContentTypeId::OUTLINE) + { + m_xTreeView->set_selection_mode(SelectionMode::Multiple); + } + } + } + else + { + m_xTreeView->set_selection_mode(SelectionMode::Single); + m_nRootType = ContentTypeId::UNKNOWN; + m_bIsRoot = false; + FindActiveTypeAndRemoveUserData(); + Display(State::HIDDEN != m_eState); + } + m_pConfig->SetRootType( m_nRootType ); + weld::Toolbar* pBox = GetParentWindow()->m_xContent2ToolBox.get(); + pBox->set_item_active("root", m_bIsRoot); +} + +bool SwContentTree::HasContentChanged() +{ + bool bContentChanged = false; + +// - Run through the local array and the Treelistbox in parallel. +// - Are the records not expanded, they are discarded only in the array +// and the content type will be set as the new UserData. +// - Is the root mode is active only this will be updated. + +// Valid for the displayed content types is: +// the Memberlist will be erased and the membercount will be updated +// If content will be checked, the memberlists will be replenished +// at the same time. Once a difference occurs it will be only replenished +// no longer checked. Finally, the box is filled again. + + // bVisibilityChanged gets set to true if some element, like a section, + // changed visibility and should have its name rerendered with a new + // grayed-out state + bool bVisibilityChanged = false; + + if (State::HIDDEN == m_eState) + { + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if(m_aActiveContentArr[i]) + m_aActiveContentArr[i]->Invalidate(); + } + } + // root content navigation view + else if(m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xRootEntry)) + bContentChanged = true; + else + { + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry).toInt64()))); + const ContentTypeId nType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xRootEntry).toInt64())->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + if (!pArrType) + bContentChanged = true; + else + { + // start check if first selected outline level has changed + bool bCheckChanged = m_nRootType == ContentTypeId::OUTLINE && !m_xTreeView->has_focus(); + if (bCheckChanged) + { + std::unique_ptr<weld::TreeIter> xFirstSel(m_xTreeView->make_iterator()); + bool bFirstSel = m_xTreeView->get_selected(xFirstSel.get()); + if (bFirstSel && lcl_IsContent(*xFirstSel, *m_xTreeView)) + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xFirstSel).toInt64()))); + const auto nSelLevel = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xFirstSel).toInt64())->GetOutlineLevel(); + SwWrtShell* pSh = GetWrtShell(); + const SwOutlineNodes::size_type nOutlinePos = pSh->GetOutlinePos(MAXLEVEL); + if (nOutlinePos != SwOutlineNodes::npos && pSh->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos) != nSelLevel) + bContentChanged = true; + } + } + // end check if first selected outline level has changed + + pArrType->Init(&bVisibilityChanged); + pArrType->FillMemberList(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pArrType))); + m_xTreeView->set_id(*xRootEntry, sId); + if (!bContentChanged) + { + const size_t nChildCount = GetChildCount(*xRootEntry); + if (nChildCount != pArrType->GetMemberCount()) + bContentChanged = true; + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get())); + for (size_t j = 0; j < nChildCount; ++j) + { + m_xTreeView->iter_next(*xEntry); + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + m_xTreeView->set_id(*xEntry, sSubId); + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + bContentChanged = true; + } + } + } + } + } + } + // all content navigation view + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + while (bEntry) + { + bool bNext = true; // at least a next must be + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); + const size_t nCntCount = pCntType->GetMemberCount(); + const ContentTypeId nType = pCntType->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + if (!pArrType) + bContentChanged = true; + else + { + pArrType->Init(&bVisibilityChanged); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pArrType))); + m_xTreeView->set_id(*xEntry, sId); + if (m_xTreeView->get_row_expanded(*xEntry)) + { + bool bLevelOrVisibilityChanged = false; + // bLevelOrVisibilityChanged is set if outlines have changed their level + // or if the visibility of objects (frames, sections, tables) has changed + // i.e. in header/footer + pArrType->FillMemberList(&bLevelOrVisibilityChanged); + const size_t nChildCount = GetChildCount(*xEntry); + if (bLevelOrVisibilityChanged) + { + if (nType == ContentTypeId::OUTLINE) + bContentChanged = true; + else + bVisibilityChanged = true; + } + + if(nChildCount != pArrType->GetMemberCount()) + bContentChanged = true; + else + { + for(size_t j = 0; j < nChildCount; ++j) + { + bEntry = m_xTreeView->iter_next(*xEntry); + bNext = false; + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + m_xTreeView->set_id(*xEntry, sSubId); + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + bContentChanged = true; + } + } + } + // not expanded and has children + else if (m_xTreeView->iter_has_child(*xEntry)) + { + // was the entry once opened, then must also the + // invisible records be examined. + // At least the user data must be updated. + bool bLevelOrVisibilityChanged = false; + // bLevelOrVisibilityChanged is set if outlines have changed their level + // or if the visibility of objects (frames, sections, tables) has changed + // i.e. in header/footer + pArrType->FillMemberList(&bLevelOrVisibilityChanged); + bool bRemoveChildren = false; + const size_t nOldChildCount = GetChildCount(*xEntry); + const size_t nNewChildCount = pArrType->GetMemberCount(); + if (nOldChildCount != nNewChildCount) + { + bRemoveChildren = true; + } + else + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); + (void)m_xTreeView->iter_children(*xChild); + for (size_t j = 0; j < nOldChildCount; ++j) + { + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); + m_xTreeView->set_id(*xChild, sSubId); + OUString sEntryText = m_xTreeView->get_text(*xChild); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + bRemoveChildren = true; + (void)m_xTreeView->iter_next(*xChild); + } + } + if (bRemoveChildren) + { + std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get())); + while (m_xTreeView->iter_children(*xRemove)) + { + remove(*xRemove); + m_xTreeView->copy_iterator(*xEntry, *xRemove); + } + m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0); + } + } + else if((nCntCount != 0) + != (pArrType->GetMemberCount()!=0)) + { + bContentChanged = true; + } + } + // The Root-Entry has to be found now + while (bEntry && (bNext || m_xTreeView->get_iter_depth(*xEntry))) + { + bEntry = m_xTreeView->iter_next(*xEntry); + bNext = false; + } + } + } + + if (!bContentChanged && bVisibilityChanged) + m_aUpdTimer.Start(); + + return bContentChanged || bVisibilityChanged; +} + +void SwContentTree::UpdateLastSelType() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + sal_Int64 nId = m_xTreeView->get_id(*xEntry).toInt64(); + if (nId && lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(nId))); + m_nLastSelType = reinterpret_cast<SwContentType*>(nId)->GetType(); + } + } +} + +void SwContentTree::FindActiveTypeAndRemoveUserData() +{ + UpdateLastSelType(); + + // If clear is called by TimerUpdate: + // Only for root can the validity of the UserData be guaranteed. + m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){ + m_xTreeView->set_id(rEntry, ""); + return false; + }); +} + +void SwContentTree::SetHiddenShell(SwWrtShell* pSh) +{ + m_pHiddenShell = pSh; + m_eState = State::HIDDEN; + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aHiddenContentArr[i].reset(); + } + Display(false); + + GetParentWindow()->UpdateListBox(); +} + +void SwContentTree::SetActiveShell(SwWrtShell* pSh) +{ + bool bClear = m_pActiveShell != pSh; + if (State::ACTIVE == m_eState && bClear) + { + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + FindActiveTypeAndRemoveUserData(); + clear(); + } + else if (State::CONSTANT == m_eState) + { + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + m_eState = State::ACTIVE; + bClear = true; + } + // Only if it is the active view, the array will be deleted and + // the screen filled new. + if (State::ACTIVE == m_eState && bClear) + { + if (m_pActiveShell) + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); + } +} + +void SwContentTree::SetConstantShell(SwWrtShell* pSh) +{ + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + m_eState = State::CONSTANT; + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); +} + +void SwContentTree::Notify(SfxBroadcaster & rBC, SfxHint const& rHint) +{ + SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint)); + SwXTextView* pDyingShell = nullptr; + if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed") + pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get()); + if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView()) + { + SetActiveShell(nullptr); // our view is dying, clear our pointers to it + } + else + { + SfxListener::Notify(rBC, rHint); + } + switch (rHint.GetId()) + { + case SfxHintId::DocChanged: + if (!m_bIsInPromoteDemote) + { + m_bViewHasChanged = true; + TimerUpdate(&m_aUpdTimer); + } + break; + case SfxHintId::ModeChanged: + if (SwWrtShell* pShell = GetWrtShell()) + { + const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly(); + if (bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + m_xTreeView->select(*xEntry); + Select(); + } + else + m_xTreeView->unselect_all(); + } + } + break; + default: + break; + } +} + +void SwContentTree::ExecCommand(const OString& rCmd, bool bOutlineWithChildren) +{ + const bool bUp = rCmd == "chapterup"; + const bool bUpDown = bUp || rCmd == "chapterdown"; + const bool bLeft = rCmd == "promote"; + const bool bLeftRight = bLeft || rCmd == "demote"; + if (!bUpDown && !bLeftRight) + return; + if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() || + (State::ACTIVE != m_eState && + (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr()))) + { + return; + } + + m_bIsInPromoteDemote = true; + + SwWrtShell *const pShell = GetWrtShell(); + sal_Int8 nActOutlineLevel = m_nOutlineLevel; + SwOutlineNodes::size_type nActPos = pShell->GetOutlinePos(nActOutlineLevel); + + std::vector<SwTextNode*> selectedOutlineNodes; + std::vector<std::unique_ptr<weld::TreeIter>> selected; + + m_xTreeView->selected_foreach([this, pShell, &bLeftRight, &bOutlineWithChildren, &selected, &selectedOutlineNodes](weld::TreeIter& rEntry){ + // it's possible to select the root node too which is a really bad idea + bool bSkip = lcl_IsContentType(rEntry, *m_xTreeView); + // filter out children of selected parents so they don't get promoted + // or moved twice (except if there is Ctrl modifier, since in that + // case children are re-parented) + if ((bLeftRight || bOutlineWithChildren) && !selected.empty()) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); + for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent)) + { + if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0) + { + bSkip = true; + break; + } + } + } + if (!bSkip) + { + selected.emplace_back(m_xTreeView->make_iterator(&rEntry)); + const SwNodes& rNodes = pShell->GetNodes(); + const size_t nPos = GetAbsPos(rEntry) - 1; + if (nPos < rNodes.GetOutLineNds().size()) + { + SwNode* pNode = rNodes.GetOutLineNds()[ nPos ]; + if (pNode) + { + selectedOutlineNodes.push_back(pNode->GetTextNode()); + } + } + } + return false; + }); + + if (bUpDown && !bUp) + { // to move down, start at the end! + std::reverse(selected.begin(), selected.end()); + } + + SwOutlineNodes::difference_type nDirLast = bUp ? -1 : 1; + bool bStartedAction = false; + for (auto const& pCurrentEntry : selected) + { + assert(pCurrentEntry && lcl_IsContent(*pCurrentEntry, *m_xTreeView)); + if (lcl_IsContent(*pCurrentEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry).toInt64()))); + if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || + reinterpret_cast<SwContent*>(m_xTreeView->get_id(*pCurrentEntry).toInt64())->GetParent()->GetType() + == ContentTypeId::OUTLINE) + { + nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*pCurrentEntry).toInt64())->GetOutlinePos(); + } + } + if (nActPos == SwOutlineNodes::npos || (bUpDown && !pShell->IsOutlineMovable(nActPos))) + { + continue; + } + + if (!bStartedAction) + { + pShell->StartAllAction(); + pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD); + bStartedAction = true; + } + pShell->GotoOutline( nActPos); // If text selection != box selection + pShell->Push(); + pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren); + if (bUpDown) + { + const size_t nEntryAbsPos(GetAbsPos(*pCurrentEntry)); + SwOutlineNodes::difference_type nDir = bUp ? -1 : 1; + if (!bOutlineWithChildren && ((nDir == -1 && nActPos > 0) || + (nDir == 1 && nEntryAbsPos < GetEntryCount() - 2))) + { + pShell->MoveOutlinePara( nDir ); + // Set cursor back to the current position + pShell->GotoOutline( nActPos + nDir); + } + else if (bOutlineWithChildren) + { + SwOutlineNodes::size_type nActEndPos = nActPos; + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pCurrentEntry.get())); + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry).toInt64()))); + const auto nActLevel = reinterpret_cast<SwOutlineContent*>( + m_xTreeView->get_id(*pCurrentEntry).toInt64())->GetOutlineLevel(); + bool bEntry = m_xTreeView->iter_next(*xEntry); + while (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlineLevel()) + break; + nActEndPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlinePos(); + bEntry = m_xTreeView->iter_next(*xEntry); + } + if (nDir == 1) // move down + { + std::unique_ptr<weld::TreeIter> xNextSibling(m_xTreeView->make_iterator(pCurrentEntry.get())); + if (m_xTreeView->iter_next_sibling(*xNextSibling) && m_xTreeView->is_selected(*xNextSibling)) + nDir = nDirLast; + else + { + // If the last entry is to be moved we're done + if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + // xEntry now points to the entry following the last + // selected entry. + SwOutlineNodes::size_type nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlinePos(); + // here needs to found the next entry after next. + // The selection must be inserted in front of that. + while (bEntry) + { + bEntry = m_xTreeView->iter_next(*xEntry); + assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView)|| + dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + // nDest++ may only executed if bEntry + if (bEntry) + { + if (!lcl_IsContent(*xEntry, *m_xTreeView)) + break; + else if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlineLevel()) + { + // nDest needs adjusted if there are selected entries (including ancestral lineage) + // immediately before the current moved entry. + std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get())); + bool bTmp = m_xTreeView->iter_previous(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nActLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) + { + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && !m_xTreeView->is_selected(*xTmp) && + nActLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) + { + bTmp = m_xTreeView->iter_parent(*xTmp); + } + if (!bTmp || !m_xTreeView->is_selected(*xTmp)) + break; + bTmp = m_xTreeView->iter_previous(*xTmp); + nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlinePos(); + } + std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(xEntry.get())); + if (!m_xTreeView->iter_previous_sibling(*xPrevSibling) || !m_xTreeView->is_selected(*xPrevSibling)) + break; + } + else + { + nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlinePos(); + } + } + } + nDirLast = nDir = nDest - nActEndPos; + // If no entry was found that allows insertion before + // it, we just move it to the end. + } + else + nDirLast = nDir = 0; + } + } + else // move up + { + std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(pCurrentEntry.get())); + if (m_xTreeView->iter_previous_sibling(*xPrevSibling) && m_xTreeView->is_selected(*xPrevSibling)) + nDir = nDirLast; + else + { + SwOutlineNodes::size_type nDest = nActPos; + bEntry = true; + m_xTreeView->copy_iterator(*pCurrentEntry, *xEntry); + while (bEntry && nDest) + { + bEntry = m_xTreeView->iter_previous(*xEntry); + assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView) || + dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlinePos(); + } + else + { + nDest = 0; // presumably? + } + if (bEntry) + { + if (!lcl_IsContent(*xEntry, *m_xTreeView)) + break; + else if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlineLevel()) + { + // nDest needs adjusted if there are selected entries immediately + // after the level change. + std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get())); + bool bTmp = m_xTreeView->iter_next(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nActLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel() && + m_xTreeView->is_selected(*xTmp)) + { + nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlinePos(); + const auto nLevel = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel(); + // account for selected entries' descendent lineage + bTmp = m_xTreeView->iter_next(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) + { + nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlinePos(); + bTmp = m_xTreeView->iter_next(*xTmp); + } + } + break; + } + } + } + nDirLast = nDir = nDest - nActPos; + } + } + if (nDir) + { + pShell->MoveOutlinePara( nDir ); + // Set cursor back to the current position + pShell->GotoOutline(nActPos + nDir); + } + } + } + else + { + if (!pShell->IsProtectedOutlinePara()) + pShell->OutlineUpDown(bLeft ? -1 : 1); + } + + pShell->ClearMark(); + pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading. + } + + if (bStartedAction) + { + pShell->EndUndo(); + pShell->EndAllAction(); + if (m_aActiveContentArr[ContentTypeId::OUTLINE]) + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + + // clear all selections to prevent the Display function from trying to reselect selected entries + m_xTreeView->unselect_all(); + Display(true); + + // reselect entries + const SwOutlineNodes::size_type nCurrPos = pShell->GetOutlinePos(MAXLEVEL); + std::unique_ptr<weld::TreeIter> xListEntry(m_xTreeView->make_iterator()); + bool bListEntry = m_xTreeView->get_iter_first(*xListEntry); + while ((bListEntry = m_xTreeView->iter_next(*xListEntry)) && lcl_IsContent(*xListEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xListEntry).toInt64()))); + if (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xListEntry).toInt64())->GetOutlinePos() == nCurrPos) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xListEntry.get())); + if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + m_xTreeView->set_cursor(*xListEntry); // unselect all entries, make entry visible, set focus, and select + Select(); + break; + } + } + + if (m_bIsRoot) + { + const SwOutlineNodes& rOutLineNds = pShell->GetNodes().GetOutLineNds(); + for (SwTextNode* pNode : selectedOutlineNodes) + { + SwOutlineNodes::const_iterator aFndIt = rOutLineNds.find(pNode); + if(aFndIt == rOutLineNds.end()) + continue; + const size_t nFndPos = aFndIt - rOutLineNds.begin(); + std::unique_ptr<weld::TreeIter> xEntry = GetEntryAtAbsPos(nFndPos + 1); + if (xEntry) + { + m_xTreeView->select(*xEntry); + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get())); + if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + } + } + } + } + m_bIsInPromoteDemote = false; +} + +void SwContentTree::ShowTree() +{ + m_xTreeView->show(); + m_aUpdTimer.Start(); +} + +void SwContentTree::HideTree() +{ + // folded together will not be idled + m_aUpdTimer.Stop(); + m_xTreeView->hide(); +} + +/** No idle with focus or while dragging */ +IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void) +{ + // No update while focus is not in document. + // No update while drag and drop. + // Query view because the Navigator is cleared too late. + SwView* pView = GetParentWindow()->GetCreateView(); + if(pView && pView->GetWrtShellPtr() && pView->GetWrtShellPtr()->GetWin() && + (pView->GetWrtShellPtr()->GetWin()->HasFocus() || m_bViewHasChanged) && + !IsInDrag() && !pView->GetWrtShellPtr()->ActionPend()) + { + m_bViewHasChanged = false; + m_bIsIdleClear = false; + SwWrtShell* pActShell = pView->GetWrtShellPtr(); + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + GetParentWindow()->UpdateListBox(); + } + + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + { + SetActiveShell(pActShell); + } + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + HasContentChanged()) + { + FindActiveTypeAndRemoveUserData(); + Display(true); + } + + // track document outline position at cursor + if (m_nOutlineTracking == 3) // no outline tracking + return; + + const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); // find out where the cursor is + if (nActPos == SwOutlineNodes::npos) + return; + + // only track if selection is already an outline + std::unique_ptr<weld::TreeIter> xFirstSelected(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xFirstSelected.get())) + xFirstSelected.reset(); + if (xFirstSelected && lcl_IsContent(*xFirstSelected, *m_xTreeView) && + reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetParent()->GetType() != ContentTypeId::OUTLINE) + return; + if (xFirstSelected && lcl_IsContentType(*xFirstSelected, *m_xTreeView) && + reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetType() != ContentTypeId::OUTLINE) + return; + + int nSelectedRows = m_xTreeView->count_selected_rows(); + + // find the outline in the tree and select it + m_xTreeView->all_foreach([this, nSelectedRows, nActPos, &xFirstSelected](weld::TreeIter& rEntry){ + bool bRet = false; + + if (lcl_IsContent(rEntry, *m_xTreeView) && + reinterpret_cast<SwContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE) + { + // might have been scrolled out of view by the user so leave it that way + if (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos() == nActPos) + { + // only select if not already selected or tree has multiple entries selected + if (nSelectedRows != 1 || m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0) + { + if (m_nOutlineTracking == 2) // focused outline tracking + { + // collapse to children of root node + std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_iter_first(*xChildEntry) && m_xTreeView->iter_children(*xChildEntry)) + { + do + { + if (reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xChildEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE) + m_xTreeView->collapse_row(*xChildEntry); + else + break; + } + while (m_xTreeView->iter_next(*xChildEntry)); + } + } + m_xTreeView->set_cursor(rEntry); // unselect all entries, make pEntry visible, and select + Select(); + } + bRet = true; + } + } + else + { + // use of this break assumes outline content type is first in tree + if (lcl_IsContentType(rEntry, *m_xTreeView) && + reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rEntry).toInt64())->GetType() != ContentTypeId::OUTLINE) + bRet = true; + } + + return bRet; + }); + } + else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) + { + if(m_pActiveShell) + { + SetActiveShell(nullptr); + } + clear(); + m_bIsIdleClear = true; + } +} + +void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos) +{ + SwWrtShell *const pShell = GetWrtShell(); + pShell->StartAllAction(); + pShell->StartUndo(SwUndoId::OUTLINE_UD); + + SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos; + SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos; + + bool bFirstMove = true; + + for (const auto& source : m_aDndOutlinesSelected) + { + SwOutlineNodes::size_type nSourcePos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*source).toInt64())->GetOutlinePos(); + + // Done on the first selection move + if (bFirstMove) // only do once + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Up moves + // The first up move sets the up move amount for the remaining selected outlines to be moved + if (nTargetPos != SwOutlineNodes::npos) + nPrevTargetPosOrOffset = nSourcePos - nTargetPos; + else + nPrevTargetPosOrOffset = nSourcePos + 1; + } + else if (nSourcePos < nTargetPos) + { + // Down moves + // The first down move sets the source and target positions for the remaining selected outlines to be moved + nPrevSourcePos = nSourcePos; + nPrevTargetPosOrOffset = nTargetPos; + } + bFirstMove = false; + } + else + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Move up + nTargetPos = nSourcePos - nPrevTargetPosOrOffset; + } + else if (nSourcePos < nTargetPos) + { + // Move down + nSourcePos = nPrevSourcePos; + nTargetPos = nPrevTargetPosOrOffset; + } + } + GetParentWindow()->MoveOutline(nSourcePos, nTargetPos); + } + + pShell->EndUndo(); + pShell->EndAllAction(); + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + Display(true); + m_aDndOutlinesSelected.clear(); +} + +// Update immediately +IMPL_LINK_NOARG(SwContentTree, FocusHdl, weld::Widget&, void) +{ + SwView* pActView = GetParentWindow()->GetCreateView(); + if(pActView) + { + SwWrtShell* pActShell = pActView->GetWrtShellPtr(); + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + } + + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + SetActiveShell(pActShell); + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + HasContentChanged()) + { + Display(true); + } + } + else if (State::ACTIVE == m_eState) + clear(); +} + +IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool) +{ + bool bConsumed = true; + + const vcl::KeyCode aCode = rEvent.GetKeyCode(); + if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + ExpandOrCollapseAll(*m_xTreeView, *xEntry); + } + else if (aCode.GetCode() == KEY_RETURN) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + switch(aCode.GetModifier()) + { + case KEY_MOD2: + // Switch boxes + GetParentWindow()->ToggleTree(); + break; + case KEY_MOD1: + // Switch RootMode + ToggleToRoot(); + break; + case 0: + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry) + : m_xTreeView->expand_row(*xEntry); + } + else + ContentDoubleClickHdl(*m_xTreeView); + break; + } + } + } + else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); + if (reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetParent()->IsDeletable() && + !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) + { + EditEntry(*xEntry, EditEntryMode::DELETE); + } + } + } + //Make KEY_SPACE has same function as DoubleClick , + //and realize multi-selection . + else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + if (State::HIDDEN != m_eState) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop(); + } + + SwContent* pCnt = dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64())); + + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT) + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + pDrawView->SdrEndTextEdit(); + + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + bool hasObjectMarked = false; + + if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt)) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if( pPV ) + { + bool bUnMark = pDrawView->IsObjMarked(pObject); + pDrawView->MarkObj( pObject, pPV, bUnMark); + + } + } + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + bool bMark = pDrawView->IsObjMarked(pTemp); + switch( pTemp->GetObjIdentifier() ) + { + case OBJ_GRUP: + case OBJ_TEXT: + case OBJ_LINE: + case OBJ_RECT: + case OBJ_CIRC: + case OBJ_SECT: + case OBJ_CARC: + case OBJ_CCUT: + case OBJ_POLY: + case OBJ_PLIN: + case OBJ_PATHLINE: + case OBJ_PATHFILL: + case OBJ_FREELINE: + case OBJ_FREEFILL: + case OBJ_PATHPOLY: + case OBJ_PATHPLIN: + case OBJ_CAPTION: + case OBJ_CUSTOMSHAPE: + if( bMark ) + hasObjectMarked = true; + break; + default: + if ( bMark ) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if (pPV) + { + pDrawView->MarkObj(pTemp, pPV, true); + } + } + } + //mod end + } + if ( !hasObjectMarked ) + { + SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin(); + vcl::KeyCode tempKeycode( KEY_ESCAPE ); + KeyEvent rKEvt( 0 , tempKeycode ); + static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt ); + } + } + } + + m_bViewHasChanged = true; + } + } + } + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + SwContent* pCnt = dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64())); + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE) + { + if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0) + { + m_xTreeView->unselect_all(); + bConsumed = false; + } + else if (aCode.IsMod1()) + { + if (aCode.GetCode() == KEY_LEFT) + ExecCommand("promote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_RIGHT) + ExecCommand("demote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_UP) + ExecCommand("chapterup", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_DOWN) + ExecCommand("chapterdown", !aCode.IsShift()); + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + return bConsumed; +} + +IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + ContentTypeId nType; + bool bContent = false; + void* pUserData = reinterpret_cast<void*>(m_xTreeView->get_id(rEntry).toInt64()); + if (lcl_IsContentType(rEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContentType*>(pUserData)->GetType(); + } + else + { + assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType(); + bContent = true; + } + OUString sEntry; + if(bContent) + { + switch( nType ) + { + case ContentTypeId::URLFIELD: + assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL(); + break; + + case ContentTypeId::POSTIT: + assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwPostItContent*>(pUserData)->GetName(); + break; + case ContentTypeId::OUTLINE: + assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwOutlineContent*>(pUserData)->GetName(); + break; + case ContentTypeId::GRAPHIC: + assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink(); + break; + case ContentTypeId::REGION: + { + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwRegionContent*>(pUserData)->GetName(); + const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections(); + for (SwSectionFormats::size_type n = rFormats.size(); n;) + { + const SwNodeIndex* pIdx = nullptr; + const SwSectionFormat* pFormat = rFormats[--n]; + const SwSection* pSect; + if (nullptr != (pSect = pFormat->GetSection()) && + pSect->GetSectionName() == sEntry && + nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) && + pIdx->GetNode().GetNodes().IsDocNodes()) + { + SwDocStat aDocStat; + SwPaM aPaM(*pIdx, *pIdx->GetNode().EndOfSectionNode()); + SwDoc::CountWords(aPaM, aDocStat); + sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" + + SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" + + SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar); + break; + } + } + } + break; + default: break; + } + if(static_cast<SwContent*>(pUserData)->IsInvisible()) + { + if(!sEntry.isEmpty()) + sEntry += ", "; + sEntry += m_sInvisible; + } + } + else + { + const size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount(); + sEntry = OUString::number(nMemberCount) + " " + + (nMemberCount == 1 + ? static_cast<SwContentType*>(pUserData)->GetSingleName() + : static_cast<SwContentType*>(pUserData)->GetName()); + } + + return sEntry; +} + +void SwContentTree::ExecuteContextMenuAction(const OString& rSelectedPopupEntry) +{ + std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xFirst.get())) + xFirst.reset(); + + auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32(); + switch (nSelectedPopupEntry) + { + case 11: + case 12: + case 13: + nSelectedPopupEntry -= 10; + if(m_nOutlineTracking != nSelectedPopupEntry) + m_nOutlineTracking = nSelectedPopupEntry; + break; + //Outlinelevel + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + nSelectedPopupEntry -= 100; + if(m_nOutlineLevel != nSelectedPopupEntry ) + SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry)); + break; + case 201: + case 202: + case 203: + GetParentWindow()->SetRegionDropMode(static_cast<RegionMode>(nSelectedPopupEntry - 201)); + break; + case 401: + case 402: + EditEntry(*xFirst, nSelectedPopupEntry == 401 ? EditEntryMode::RMV_IDX : EditEntryMode::UPD_IDX); + break; + // Edit entry + case 403: + EditEntry(*xFirst, EditEntryMode::EDIT); + break; + case 404: + EditEntry(*xFirst, EditEntryMode::UNPROTECT_TABLE); + break; + case 405 : + { + const SwTOXBase* pBase = reinterpret_cast<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst).toInt64()) + ->GetTOXBase(); + m_pActiveShell->SetTOXBaseReadonly(*pBase, !SwEditShell::IsTOXBaseReadonly(*pBase)); + } + break; + case 4: + break; + case 501: + EditEntry(*xFirst, EditEntryMode::DELETE); + break; + case 502 : + EditEntry(*xFirst, EditEntryMode::RENAME); + break; + case 600: + m_pActiveShell->GetView().GetPostItMgr()->Show(); + break; + case 601: + m_pActiveShell->GetView().GetPostItMgr()->Hide(); + break; + case 602: + { + m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr); + m_pActiveShell->GetView().GetPostItMgr()->Delete(); + break; + } + case 700: + { + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_OUTLINE_TO_CLIPBOARD); + break; + } + case 800: + ExpandOrCollapseAll(*m_xTreeView, *xFirst); + break; + case 801: + ExecCommand("chapterup", true); + break; + case 802: + ExecCommand("chapterdown", true); + break; + case 803: + ExecCommand("promote", true); + break; + case 804: + ExecCommand("demote", true); + break; + case 805: + { + m_pActiveShell->KillPams(); + m_pActiveShell->ClearMark(); + m_pActiveShell->EnterAddMode(); + SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirst).toInt64()); + const ContentTypeId eTypeId = pCnt->GetParent()->GetType(); + if (eTypeId == ContentTypeId::OUTLINE) + { + m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + m_pActiveShell->SttSelect(); + SwOutlineNodes::size_type nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); + m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded + m_pActiveShell->EndSelect(); + return false; + }); + } + else if (eTypeId == ContentTypeId::TABLE) + { + m_pActiveShell->GotoTable(pCnt->GetName()); + m_pActiveShell->SelAll(); + } + else if (eTypeId == ContentTypeId::REGION) + { + m_pActiveShell->EnterStdMode(); + m_pActiveShell->GotoRegion(pCnt->GetName()); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionEnd, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->SttSelect(); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionStart, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->EndSelect(); + m_pActiveShell->UpdateCursor(); + } + m_pActiveShell->LeaveAddMode(); + } + break; + case 806: + // Delete outline selections + EditEntry(*xFirst, EditEntryMode::DELETE); + break; + case 900: + { + SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirst).toInt64()); + GotoContent(pCnt); + } + break; + //Display + default: + if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400) + { + nSelectedPopupEntry -= 300; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + nSelectedPopupEntry --; + if(nSelectedPopupEntry == 0) + { + SetConstantShell(&pView->GetWrtShell()); + break; + } + pView = SwModule::GetNextView(pView); + } + if(nSelectedPopupEntry) + { + m_bViewHasChanged = nSelectedPopupEntry == 1; + m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN; + Display(nSelectedPopupEntry == 1); + } + } + } + GetParentWindow()->UpdateListBox(); +} + +void SwContentTree::DeleteOutlineSelections() +{ + m_pActiveShell->StartAction(); + m_pActiveShell->EnterAddMode(); + auto nChapters(0); + + m_xTreeView->selected_foreach([this, &nChapters](weld::TreeIter& rEntry){ + ++nChapters; + if (m_xTreeView->iter_has_child(rEntry) && + !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded + { + nChapters += m_xTreeView->iter_n_children(rEntry); + } + m_pActiveShell->SttSelect(); + SwOutlineNodes::size_type nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); + m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded + m_pActiveShell->EndSelect(); + return false; + }); + m_pActiveShell->LeaveAddMode(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters)); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->SetTextFormatColl(nullptr); + m_pActiveShell->Delete(); + m_pActiveShell->ClearMark(); + m_pActiveShell->EndUndo(); + m_pActiveShell->EndAction(); +} + +void SwContentTree::SetOutlineLevel(sal_uInt8 nSet) +{ + m_nOutlineLevel = nSet; + m_pConfig->SetOutlineLevel( m_nOutlineLevel ); + std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState) + ? m_aActiveContentArr[ContentTypeId::OUTLINE] + : m_aHiddenContentArr[ContentTypeId::OUTLINE]; + if(rpContentT) + { + rpContentT->SetOutlineLevel(m_nOutlineLevel); + rpContentT->Init(); + } + Display(State::ACTIVE == m_eState); +} + +// Mode Change: Show dropped Doc +void SwContentTree::ShowHiddenShell() +{ + if(m_pHiddenShell) + { + m_eState = State::HIDDEN; + Display(false); + } +} + +// Mode Change: Show active view +void SwContentTree::ShowActualView() +{ + m_eState = State::ACTIVE; + Display(true); + GetParentWindow()->UpdateListBox(); +} + +IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void) +{ + Select(); +} + +// Here the buttons for moving outlines are en-/disabled. +void SwContentTree::Select() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + return; + + bool bEnable = false; + std::unique_ptr<weld::TreeIter> xParentEntry(m_xTreeView->make_iterator(xEntry.get())); + bool bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + while (bParentEntry && (!lcl_IsContentType(*xParentEntry, *m_xTreeView))) + bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + if (!m_bIsLastReadOnly) + { + if (!m_xTreeView->get_visible()) + bEnable = true; + else if (bParentEntry) + { + if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || + (lcl_IsContent(*xEntry, *m_xTreeView) && + reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xParentEntry).toInt64())->GetType() == ContentTypeId::OUTLINE)) + { + bEnable = true; + } + } + } + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent3ToolBox->set_item_sensitive("chapterup", bEnable); + pNavi->m_xContent3ToolBox->set_item_sensitive("chapterdown", bEnable); + pNavi->m_xContent3ToolBox->set_item_sensitive("promote", bEnable); + pNavi->m_xContent3ToolBox->set_item_sensitive("demote", bEnable); +} + +void SwContentTree::SetRootType(ContentTypeId nType) +{ + m_nRootType = nType; + m_bIsRoot = true; + m_pConfig->SetRootType( m_nRootType ); +} + +OUString SwContentType::RemoveNewline(const OUString& rEntry) +{ + if (rEntry.isEmpty()) + return rEntry; + + OUStringBuffer aEntry(rEntry); + for (sal_Int32 i = 0; i < rEntry.getLength(); ++i) + if(aEntry[i] == 10 || aEntry[i] == 13) + aEntry[i] = 0x20; + + return aEntry.makeStringAndClear(); +} + +void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode) +{ + SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(rEntry).toInt64()); + GotoContent(pCnt); + const ContentTypeId nType = pCnt->GetParent()->GetType(); + sal_uInt16 nSlot = 0; + + uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird; + switch(nType) + { + case ContentTypeId::OUTLINE : + if(nMode == EditEntryMode::DELETE) + { + DeleteOutlineSelections(); + } + break; + + case ContentTypeId::TABLE : + if(nMode == EditEntryMode::UNPROTECT_TABLE) + { + m_pActiveShell->GetView().GetDocShell()-> + GetDoc()->UnProtectCells( pCnt->GetName()); + } + else if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->StartAction(); + OUString sTable = SwResId(STR_TABLE_NAME); + SwRewriter aRewriterTableName; + aRewriterTableName.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriterTableName.AddRule(UndoArg2, pCnt->GetName()); + aRewriterTableName.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + sTable = aRewriterTableName.Apply(sTable); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, sTable); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + m_pActiveShell->DeleteRow(); + m_pActiveShell->EndUndo(); + m_pActiveShell->EndAction(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY); + xNameAccess = xTables->getTextTables(); + } + else + nSlot = FN_FORMAT_TABLE_DLG; + break; + + case ContentTypeId::GRAPHIC : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xNameAccess = xGraphics->getGraphicObjects(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + xSecond = xFrames->getTextFrames(); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + xThird = xObjs->getEmbeddedObjects(); + } + else + nSlot = FN_FORMAT_GRAFIC_DLG; + break; + + case ContentTypeId::FRAME : + case ContentTypeId::OLE : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + if(ContentTypeId::FRAME == nType) + { + xNameAccess = xFrames->getTextFrames(); + xSecond = xObjs->getEmbeddedObjects(); + } + else + { + xNameAccess = xObjs->getEmbeddedObjects(); + xSecond = xFrames->getTextFrames(); + } + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xThird = xGraphics->getGraphicObjects(); + } + else + nSlot = FN_FORMAT_FRAME_DLG; + break; + case ContentTypeId::BOOKMARK : + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); + if(nMode == EditEntryMode::DELETE) + { + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + pMarkAccess->deleteMark( pMarkAccess->findMark(pCnt->GetName()) ); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XBookmarksSupplier > xBkms(xModel, uno::UNO_QUERY); + xNameAccess = xBkms->getBookmarks(); + } + else + nSlot = FN_INSERT_BOOKMARK; + break; + + case ContentTypeId::REGION : + if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextSectionsSupplier > xSects(xModel, uno::UNO_QUERY); + xNameAccess = xSects->getTextSections(); + } + else + nSlot = FN_EDIT_REGION; + break; + + case ContentTypeId::URLFIELD: + if (nMode == EditEntryMode::DELETE) + nSlot = SID_REMOVE_HYPERLINK; + else + nSlot = SID_EDIT_HYPERLINK; + break; + case ContentTypeId::REFERENCE: + nSlot = FN_EDIT_FIELD; + break; + + case ContentTypeId::POSTIT: + m_pActiveShell->GetView().GetPostItMgr()->AssureStdModeAtShell(); + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr); + m_pActiveShell->DelRight(); + } + else + { + nSlot = FN_POSTIT; + } + break; + case ContentTypeId::INDEX: + { + const SwTOXBase* pBase = static_cast<SwTOXBaseContent*>(pCnt)->GetTOXBase(); + switch(nMode) + { + case EditEntryMode::EDIT: + if(pBase) + { + SwPtrItem aPtrItem( FN_INSERT_MULTI_TOX, const_cast<SwTOXBase *>(pBase)); + m_pActiveShell->GetView().GetViewFrame()-> + GetDispatcher()->ExecuteList(FN_INSERT_MULTI_TOX, + SfxCallMode::ASYNCHRON, { &aPtrItem }); + + } + break; + case EditEntryMode::RMV_IDX: + case EditEntryMode::DELETE: + { + if( pBase ) + m_pActiveShell->DeleteTOX(*pBase, EditEntryMode::DELETE == nMode); + } + break; + case EditEntryMode::UPD_IDX: + case EditEntryMode::RENAME: + { + Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + Reference< XDocumentIndexesSupplier > xIndexes(xModel, UNO_QUERY); + Reference< XIndexAccess> xIdxAcc(xIndexes->getDocumentIndexes()); + Reference< XNameAccess >xLocalNameAccess(xIdxAcc, UNO_QUERY); + if(EditEntryMode::RENAME == nMode) + xNameAccess = xLocalNameAccess; + else if(xLocalNameAccess.is() && xLocalNameAccess->hasByName(pBase->GetTOXName())) + { + Any aIdx = xLocalNameAccess->getByName(pBase->GetTOXName()); + Reference< XDocumentIndex> xIdx; + if(aIdx >>= xIdx) + xIdx->update(); + } + } + break; + default: break; + } + } + break; + case ContentTypeId::DRAWOBJECT : + if(EditEntryMode::DELETE == nMode) + nSlot = SID_DELETE; + else if(nMode == EditEntryMode::RENAME) + nSlot = FN_NAME_SHAPE; + break; + default: break; + } + if(nSlot) + m_pActiveShell->GetView().GetViewFrame()-> + GetDispatcher()->Execute(nSlot, SfxCallMode::ASYNCHRON); + else if(xNameAccess.is()) + { + uno::Any aObj = xNameAccess->getByName(pCnt->GetName()); + uno::Reference< uno::XInterface > xTmp; + aObj >>= xTmp; + uno::Reference< container::XNamed > xNamed(xTmp, uno::UNO_QUERY); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(GetParentWindow()->GetFrameWeld(), xNamed, xNameAccess)); + if(xSecond.is()) + pDlg->SetAlternativeAccess( xSecond, xThird); + + OUString sForbiddenChars; + if(ContentTypeId::BOOKMARK == nType) + { + sForbiddenChars = "/\\@:*?\";,.#"; + } + else if(ContentTypeId::TABLE == nType) + { + sForbiddenChars = " .<>"; + } + pDlg->SetForbiddenChars(sForbiddenChars); + pDlg->Execute(); + } + if(EditEntryMode::DELETE == nMode) + { + m_bViewHasChanged = true; + GetParentWindow()->UpdateListBox(); + TimerUpdate(&m_aUpdTimer); + grab_focus(); + } +} + +void SwContentTree::GotoContent(const SwContent* pCnt) +{ + m_pActiveShell->EnterStdMode(); + + bool bSel = false; + switch(pCnt->GetParent()->GetType()) + { + case ContentTypeId::OUTLINE : + { + m_pActiveShell->GotoOutline(static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos()); + } + break; + case ContentTypeId::TABLE : + { + m_pActiveShell->GotoTable(pCnt->GetName()); + } + break; + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + case ContentTypeId::OLE : + { + if(m_pActiveShell->GotoFly(pCnt->GetName())) + bSel = true; + } + break; + case ContentTypeId::BOOKMARK: + { + m_pActiveShell->GotoMark(pCnt->GetName()); + } + break; + case ContentTypeId::REGION : + { + m_pActiveShell->GotoRegion(pCnt->GetName()); + } + break; + case ContentTypeId::URLFIELD: + { + if(m_pActiveShell->GotoINetAttr( + *static_cast<const SwURLFieldContent*>(pCnt)->GetINetAttr() )) + { + m_pActiveShell->Right( CRSR_SKIP_CHARS, true, 1, false); + m_pActiveShell->SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true ); + } + + } + break; + case ContentTypeId::REFERENCE: + { + m_pActiveShell->GotoRefMark(pCnt->GetName()); + } + break; + case ContentTypeId::INDEX: + { + const OUString& sName(pCnt->GetName()); + if (!m_pActiveShell->GotoNextTOXBase(&sName)) + m_pActiveShell->GotoPrevTOXBase(&sName); + } + break; + case ContentTypeId::POSTIT: + m_pActiveShell->GetView().GetPostItMgr()->AssureStdModeAtShell(); + m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt()); + break; + case ContentTypeId::DRAWOBJECT: + { + SwPosition aPos = *m_pActiveShell->GetCursor()->GetPoint(); + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + pDrawView->SdrEndTextEdit(); + pDrawView->UnmarkAll(); + SwDrawModel* _pModel = m_pActiveShell->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = _pModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + if (pTemp->GetName() == pCnt->GetName()) + { + SdrPageView* pPV = pDrawView->GetSdrPageView(); + if( pPV ) + { + pDrawView->MarkObj( pTemp, pPV ); + } + } + } + m_pActiveShell->GetNavigationMgr().addEntry(aPos); + m_pActiveShell->EnterStdMode(); + bSel = true; + } + } + break; + default: break; + } + if(bSel) + { + m_pActiveShell->HideCursor(); + m_pActiveShell->EnterSelFrameMode(); + } + SwView& rView = m_pActiveShell->GetView(); + rView.StopShellTimer(); + rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); + rView.GetEditWin().GrabFocus(); + + // force scroll to cursor position when navigating to inactive document + if(!bSel) + { + Point rPoint = m_pActiveShell->GetCursorDocPos(); + rPoint.setX(0); + rView.SetVisArea(rPoint); + } +} + +// Now even the matching text::Bookmark +NaviContentBookmark::NaviContentBookmark() + : + nDocSh(0), + nDefDrag( RegionMode::NONE ) +{ +} + +NaviContentBookmark::NaviContentBookmark( const OUString &rUrl, + const OUString& rDesc, + RegionMode nDragType, + const SwDocShell* pDocSh ) : + aUrl( rUrl ), + aDescr(rDesc), + nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh)), + nDefDrag( nDragType ) +{ +} + +void NaviContentBookmark::Copy( TransferDataContainer& rData ) const +{ + rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding(); + + OString sStrBuf(OUStringToOString(aUrl, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OUStringToOString(aDescr, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(static_cast<int>(nDefDrag)) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(nDocSh)); + rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf); +} + +bool NaviContentBookmark::Paste( TransferableDataHelper& rData ) +{ + OUString sStr; + bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr ); + if( bRet ) + { + sal_Int32 nPos = 0; + aUrl = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + aDescr = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + nDefDrag= static_cast<RegionMode>( sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ).toInt32() ); + nDocSh = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ).toInt32(); + } + return bRet; +} + +SwNavigationPI* SwContentTree::GetParentWindow() +{ + return m_xDialog; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/glbltree.cxx b/sw/source/uibase/utlui/glbltree.cxx new file mode 100644 index 000000000..3261050f3 --- /dev/null +++ b/sw/source/uibase/utlui/glbltree.cxx @@ -0,0 +1,1088 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <o3tl/safeint.hxx> +#include <svl/stritem.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/help.hxx> +#include <sot/filelist.hxx> +#include <svl/eitem.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/settings.hxx> + +#include <sfx2/docinsert.hxx> +#include <sfx2/filedlghelper.hxx> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <docsh.hxx> +#include <edglbldc.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <navipi.hxx> +#include <edtwin.hxx> +#include <toxmgr.hxx> + +#include <cmdid.h> +#include <helpids.h> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <swabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star::uno; + +#define GLOBAL_UPDATE_TIMEOUT 2000 + +const SfxObjectShell* SwGlobalTree::pShowShell = nullptr; + +namespace { + +class SwGlobalFrameListener_Impl : public SfxListener +{ + bool bValid; +public: + explicit SwGlobalFrameListener_Impl(SfxViewFrame& rFrame) + : bValid(true) + { + StartListening(rFrame); + } + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + bool IsValid() const {return bValid;} +}; + +} + +void SwGlobalFrameListener_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if( rHint.GetId() == SfxHintId::Dying) + bValid = false; +} + +namespace { + +enum GLOBAL_CONTEXT_IDX +{ + IDX_STR_UPDATE = 0, + IDX_STR_EDIT_CONTENT = 1, + IDX_STR_EDIT_INSERT = 2, + IDX_STR_INDEX = 3, + IDX_STR_FILE = 4, + IDX_STR_NEW_FILE = 5, + IDX_STR_INSERT_TEXT = 6, + IDX_STR_DELETE = 7, + IDX_STR_UPDATE_SEL = 8, + IDX_STR_UPDATE_INDEX = 9, + IDX_STR_UPDATE_LINK = 10, + IDX_STR_UPDATE_ALL = 11, + IDX_STR_BROKEN_LINK = 12, + IDX_STR_EDIT_LINK = 13 +}; + +} + +static const char* GLOBAL_CONTEXT_ARY[] = +{ + STR_UPDATE, + STR_EDIT_CONTENT, + STR_EDIT_INSERT, + STR_INDEX, + STR_FILE, + STR_NEW_FILE, + STR_INSERT_TEXT, + STR_DELETE, + STR_UPDATE_SEL, + STR_UPDATE_INDEX, + STR_UPDATE_LINK, + STR_UPDATE_ALL, + STR_BROKEN_LINK, + STR_EDIT_LINK +}; + +SwGlobalTree::SwGlobalTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog) + : m_xTreeView(std::move(xTreeView)) + , m_aDropTargetHelper(*this) + , m_xDialog(pDialog) + , m_pActiveShell(nullptr) +{ + Size aSize(m_xDialog->LogicToPixel(Size(110, 112), MapMode(MapUnit::MapAppFont)));; + m_xTreeView->set_size_request(aSize.Width(), aSize.Height()); + + m_aUpdateTimer.SetTimeout(GLOBAL_UPDATE_TIMEOUT); + m_aUpdateTimer.SetInvokeHandler(LINK(this, SwGlobalTree, Timeout)); + m_aUpdateTimer.Start(); + for (sal_uInt16 i = 0; i < GLOBAL_CONTEXT_COUNT; i++) + { + m_aContextStrings[i] = SwResId(GLOBAL_CONTEXT_ARY[i]); + } + m_xTreeView->set_help_id(HID_NAVIGATOR_GLOB_TREELIST); + Select(); + m_xTreeView->connect_row_activated(LINK(this, SwGlobalTree, DoubleClickHdl)); + m_xTreeView->connect_changed(LINK(this, SwGlobalTree, SelectHdl)); + m_xTreeView->connect_focus_in(LINK(this, SwGlobalTree, FocusInHdl)); + m_xTreeView->connect_key_press(LINK(this, SwGlobalTree, KeyInputHdl)); + m_xTreeView->connect_popup_menu(LINK(this, SwGlobalTree, CommandHdl)); + m_xTreeView->connect_query_tooltip(LINK(this, SwGlobalTree, QueryTooltipHdl)); +} + +SwGlobalTree::~SwGlobalTree() +{ + m_pSwGlblDocContents.reset(); + m_pDocInserter.reset(); + m_aUpdateTimer.Stop(); + m_xDialog.clear(); +} + +SwGlobalTreeDropTarget::SwGlobalTreeDropTarget(SwGlobalTree& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 SwGlobalTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + weld::TreeView& rWidget = m_rTreeView.get_widget(); + std::unique_ptr<weld::TreeIter> xDropEntry(rWidget.make_iterator()); + if (!rWidget.get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get())) + xDropEntry.reset(); + + if (rWidget.get_drag_source() == &rWidget) // internal drag + m_rTreeView.MoveSelectionTo(xDropEntry.get()); + else + { + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + + OUString sFileName; + const SwGlblDocContent* pCnt = xDropEntry ? + reinterpret_cast<const SwGlblDocContent*>(rWidget.get_id(*xDropEntry).toInt64()) : + nullptr; + if( aData.HasFormat( SotClipboardFormatId::FILE_LIST )) + { + nRet = rEvt.mnAction; + std::unique_ptr<SwGlblDocContents> pTempContents(new SwGlblDocContents); + int nAbsContPos = xDropEntry ? + rWidget.get_iter_index_in_parent(*xDropEntry): + - 1; + size_t nEntryCount = rWidget.n_children(); + + // Get data + FileList aFileList; + aData.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ); + for ( size_t n = aFileList.Count(); n--; ) + { + sFileName = aFileList.GetFile(n); + m_rTreeView.InsertRegion(pCnt, &sFileName); + // The list of contents must be newly fetched after inserting, + // to not work on an old content. + if(n) + { + m_rTreeView.GetActiveWrtShell()->GetGlobalDocContent(*pTempContents); + // If the file was successfully inserted, + // then the next content must also be fetched. + if(nEntryCount < pTempContents->size()) + { + nEntryCount++; + nAbsContPos++; + pCnt = (*pTempContents)[ nAbsContPos ].get(); + } + } + } + } + else if( !(sFileName = + SwNavigationPI::CreateDropFileName( aData )).isEmpty()) + { + INetURLObject aTemp(sFileName); + GraphicDescriptor aDesc(aTemp); + if( !aDesc.Detect() ) // accept no graphics + { + nRet = rEvt.mnAction; + m_rTreeView.InsertRegion(pCnt, &sFileName); + } + } + } + return nRet; +} + +sal_Int8 SwGlobalTreeDropTarget::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr); + + sal_Int8 nRet = rEvt.mnAction; + + if (rWidget.get_drag_source() == &rWidget) // internal drag + return nRet; + + if (IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE) || + IsDropFormatSupported( SotClipboardFormatId::STRING) || + IsDropFormatSupported( SotClipboardFormatId::FILE_LIST) || + IsDropFormatSupported( SotClipboardFormatId::SOLK) || + IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + IsDropFormatSupported( SotClipboardFormatId::FILECONTENT) || + IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR) || + IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR) || + IsDropFormatSupported( SotClipboardFormatId::FILENAME)) + { + nRet = DND_ACTION_LINK; + } + + return nRet; +} + +IMPL_LINK(SwGlobalTree, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + bool bPop = false; + if (m_pActiveShell && !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/mastercontextmenu.ui")); + std::unique_ptr<weld::Menu> xPopup = xBuilder->weld_menu("navmenu"); + + const MenuEnableFlags nEnableFlags = GetEnableFlags(); + + xPopup->set_sensitive("updatesel", bool(nEnableFlags & MenuEnableFlags::UpdateSel)); + + xPopup->set_sensitive("editlink", bool(nEnableFlags & MenuEnableFlags::EditLink)); + + //disabling if applicable + xPopup->set_sensitive("insertindex", bool(nEnableFlags & MenuEnableFlags::InsertIdx )); + xPopup->set_sensitive("insertfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + xPopup->set_sensitive("insertnewfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + xPopup->set_sensitive("inserttext", bool(nEnableFlags & MenuEnableFlags::InsertText)); + + xPopup->set_sensitive("update", bool(nEnableFlags & MenuEnableFlags::Update)); + xPopup->set_sensitive("insert", bool(nEnableFlags & MenuEnableFlags::InsertIdx)); + xPopup->set_sensitive("editcontent", bool(nEnableFlags & MenuEnableFlags::Edit)); + xPopup->set_sensitive("deleteentry", bool(nEnableFlags & MenuEnableFlags::Delete)); + + OString sCommand = xPopup->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + } + return bPop; +} + +void SwGlobalTree::TbxMenuHdl(const OString& rCommand, weld::Menu& rMenu) +{ + const MenuEnableFlags nEnableFlags = GetEnableFlags(); + if (rCommand == "insert") + { + rMenu.set_sensitive("insertindex", bool(nEnableFlags & MenuEnableFlags::InsertIdx)); + rMenu.set_sensitive("insertfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + rMenu.set_sensitive("insertnewfile", bool(nEnableFlags & MenuEnableFlags::InsertFile)); + rMenu.set_sensitive("inserttext", bool(nEnableFlags & MenuEnableFlags::InsertText)); + } + else if (rCommand == "update") + { + rMenu.set_sensitive("updatesel", bool(nEnableFlags & MenuEnableFlags::UpdateSel)); + } +} + +MenuEnableFlags SwGlobalTree::GetEnableFlags() const +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_selected(xEntry.get()); + + sal_uLong nSelCount = m_xTreeView->count_selected_rows(); + size_t nEntryCount = m_xTreeView->n_children(); + std::unique_ptr<weld::TreeIter> xPrevEntry; + bool bPrevEntry = false; + if (bEntry) + { + xPrevEntry = m_xTreeView->make_iterator(xEntry.get()); + bPrevEntry = m_xTreeView->iter_previous(*xPrevEntry); + } + + MenuEnableFlags nRet = MenuEnableFlags::NONE; + if(nSelCount == 1 || !nEntryCount) + nRet |= MenuEnableFlags::InsertIdx|MenuEnableFlags::InsertFile; + if(nSelCount == 1) + { + nRet |= MenuEnableFlags::Edit; + if (bEntry && reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetType() != GLBLDOC_UNKNOWN && + (!bPrevEntry || reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(*xPrevEntry).toInt64())->GetType() != GLBLDOC_UNKNOWN)) + nRet |= MenuEnableFlags::InsertText; + if (bEntry && GLBLDOC_SECTION == reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetType()) + nRet |= MenuEnableFlags::EditLink; + } + else if(!nEntryCount) + { + nRet |= MenuEnableFlags::InsertText; + } + if(nEntryCount) + nRet |= MenuEnableFlags::Update|MenuEnableFlags::Delete; + if(nSelCount) + nRet |= MenuEnableFlags::UpdateSel; + return nRet; +} + +IMPL_LINK(SwGlobalTree, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + OUString sEntry; + + const SwGlblDocContent* pCont = reinterpret_cast<const SwGlblDocContent*>(m_xTreeView->get_id(rIter).toInt64()); + if (pCont && GLBLDOC_SECTION == pCont->GetType()) + { + const SwSection* pSect = pCont->GetSection(); + sEntry = pSect->GetLinkFileName().getToken(0, sfx2::cTokenSeparator); + if (!pSect->IsConnectFlag()) + sEntry = m_aContextStrings[IDX_STR_BROKEN_LINK] + sEntry; + } + + return sEntry; +} + +IMPL_LINK_NOARG(SwGlobalTree, SelectHdl, weld::TreeView&, void) +{ + Select(); +} + +void SwGlobalTree::Select() +{ + int nSelCount = m_xTreeView->count_selected_rows(); + int nSel = m_xTreeView->get_selected_index(); + int nAbsPos = nSel != -1 ? nSel : 0; + SwNavigationPI* pNavi = GetParentWindow(); + bool bReadonly = !m_pActiveShell || + m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); + pNavi->m_xGlobalToolBox->set_item_sensitive("edit", nSelCount == 1 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("insert", nSelCount <= 1 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("update", m_xTreeView->n_children() > 0 && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("moveup", + nSelCount == 1 && nAbsPos && !bReadonly); + pNavi->m_xGlobalToolBox->set_item_sensitive("movedown", + nSelCount == 1 && nAbsPos < m_xTreeView->n_children() - 1 && !bReadonly); + +} + +void SwGlobalTree::MoveSelectionTo(weld::TreeIter* pDropEntry) +{ + int nSource = m_xTreeView->get_selected_index(); + + int nDest = pDropEntry ? m_xTreeView->get_iter_index_in_parent(*pDropEntry) + : m_pSwGlblDocContents->size(); + + if (m_pActiveShell->MoveGlobalDocContent( + *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && + Update( false )) + Display(); +} + +IMPL_LINK_NOARG(SwGlobalTree, FocusInHdl, weld::Widget&, void) +{ + if (Update(false)) + Display(); +} + +IMPL_LINK(SwGlobalTree, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + const vcl::KeyCode aCode = rKEvt.GetKeyCode(); + if (aCode.GetCode() == KEY_RETURN) + { + switch (aCode.GetModifier()) + { + case KEY_MOD2: + // Switch boxes + GetParentWindow()->ToggleTree(); + bHandled = true; + break; + } + } + return bHandled; +} + +void SwGlobalTree::Display(bool bOnlyUpdateUserData) +{ + size_t nCount = m_pSwGlblDocContents->size(); + size_t nChildren = m_xTreeView->n_children(); + if (bOnlyUpdateUserData && nChildren == m_pSwGlblDocContents->size()) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + for (size_t i = 0; i < nCount && bEntry; i++) + { + const SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i].get(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pCont))); + m_xTreeView->set_id(*xEntry, sId); + if (pCont->GetType() == GLBLDOC_SECTION && !pCont->GetSection()->IsConnectFlag()) + m_xTreeView->set_font_color(*xEntry, COL_LIGHTRED); + else + m_xTreeView->set_font_color(*xEntry, COL_AUTO); + bEntry = m_xTreeView->iter_next(*xEntry); + assert(bEntry || i == nCount - 1); + } + } + else + { + int nOldSelEntry = m_xTreeView->get_selected_index(); + OUString sEntryName; // Name of the entry + int nSelPos = -1; + if (nOldSelEntry != -1) + { + sEntryName = m_xTreeView->get_text(nOldSelEntry); + nSelPos = nOldSelEntry; + } + m_xTreeView->freeze(); + m_xTreeView->clear(); + if (!m_pSwGlblDocContents) + Update( false ); + + int nSelEntry = -1; + for (size_t i = 0; i < nCount; ++i) + { + const SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i].get(); + + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pCont))); + OUString sEntry; + OUString aImage; + switch (pCont->GetType()) + { + case GLBLDOC_UNKNOWN: + sEntry = m_aContextStrings[IDX_STR_INSERT_TEXT]; + break; + case GLBLDOC_TOXBASE: + { + const SwTOXBase* pBase = pCont->GetTOX(); + sEntry = pBase->GetTitle(); + aImage = RID_BMP_NAVI_INDEX; + } + break; + case GLBLDOC_SECTION: + { + const SwSection* pSect = pCont->GetSection(); + sEntry = pSect->GetSectionName(); + aImage = RID_BMP_DROP_REGION; + } + break; + } + + m_xTreeView->append(sId, sEntry); + if (!aImage.isEmpty()) + m_xTreeView->set_image(i, aImage); + + if (pCont->GetType() == GLBLDOC_SECTION && !pCont->GetSection()->IsConnectFlag()) + m_xTreeView->set_font_color(i, COL_LIGHTRED); + + if (sEntry == sEntryName) + nSelEntry = i; + } + m_xTreeView->thaw(); + if (nSelEntry != -1) + m_xTreeView->select(nSelEntry); + else if (nSelPos != -1 && o3tl::make_unsigned(nSelPos) < nCount) + m_xTreeView->select(nSelPos); + else if (nCount) + m_xTreeView->select(0); + Select(); + } +} + +void SwGlobalTree::InsertRegion( const SwGlblDocContent* pCont, const OUString* pFileName ) +{ + Sequence< OUString > aFileNames; + if ( !pFileName ) + { + m_pDocInserter.reset(new ::sfx2::DocumentInserter(GetParentWindow()->GetFrameWeld(), "swriter", sfx2::DocumentInserter::Mode::InsertMulti)); + m_pDocInserter->StartExecuteModal( LINK( this, SwGlobalTree, DialogClosedHdl ) ); + } + else if ( !pFileName->isEmpty() ) + { + aFileNames.realloc(1); + INetURLObject aFileName; + aFileName.SetSmartURL( *pFileName ); + aFileNames.getArray()[0] = aFileName.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + InsertRegion( pCont, aFileNames ); + } +} + +void SwGlobalTree::EditContent(const SwGlblDocContent* pCont ) +{ + sal_uInt16 nSlot = 0; + switch( pCont->GetType() ) + { + case GLBLDOC_UNKNOWN: + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + break; + case GLBLDOC_TOXBASE: + { + const SwTOXBase* pBase = pCont->GetTOX(); + if(pBase) + nSlot = FN_INSERT_MULTI_TOX; + } + break; + case GLBLDOC_SECTION: + { + OpenDoc(pCont); + + nSlot = 0; + pCont = nullptr; + } + break; + } + if(pCont) + GotoContent(pCont); + if(nSlot) + { + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(nSlot); + if(Update( false )) + Display(); + } +} + +void SwGlobalTree::ExecuteContextMenuAction(const OString& rSelectedPopupEntry) +{ + bool bUpdateHard = false; + + int nEntry = m_xTreeView->get_selected_index(); + SwGlblDocContent* pCont = nEntry != -1 ? reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(nEntry).toInt64()) : nullptr; + // If a RequestHelp is called during the dialogue, + // then the content gets lost. Because of that a copy + // is created in which only the DocPos is set correctly. + std::unique_ptr<SwGlblDocContent> pContCopy; + if(pCont) + pContCopy.reset(new SwGlblDocContent(pCont->GetDocPos())); + SfxDispatcher& rDispatch = *m_pActiveShell->GetView().GetViewFrame()->GetDispatcher(); + sal_uInt16 nSlot = 0; + if (rSelectedPopupEntry == "updatesel") + { + // Two passes: first update the areas, then the directories. + m_xTreeView->selected_foreach([this](weld::TreeIter& rSelEntry){ + SwGlblDocContent* pContent = reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(rSelEntry).toInt64()); + if (GLBLDOC_SECTION == pContent->GetType() && + pContent->GetSection()->IsConnected()) + { + const_cast<SwSection*>(pContent->GetSection())->UpdateNow(); + } + return false; + }); + m_xTreeView->selected_foreach([this](weld::TreeIter& rSelEntry){ + SwGlblDocContent* pContent = reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(rSelEntry).toInt64()); + if (GLBLDOC_TOXBASE == pContent->GetType()) + m_pActiveShell->UpdateTableOf(*pContent->GetTOX()); + return false; + }); + + bUpdateHard = true; + } + else if (rSelectedPopupEntry == "updateindex") + { + nSlot = FN_UPDATE_TOX; + bUpdateHard = true; + } + else if (rSelectedPopupEntry == "updatelinks" || rSelectedPopupEntry == "updateall") + { + m_pActiveShell->GetLinkManager().UpdateAllLinks(true, false, nullptr); + if (rSelectedPopupEntry == "updateall") + nSlot = FN_UPDATE_TOX; + pCont = nullptr; + bUpdateHard = true; + } + else if (rSelectedPopupEntry == "edit") + { + OSL_ENSURE(pCont, "edit without entry ? " ); + if (pCont) + { + EditContent(pCont); + } + } + else if (rSelectedPopupEntry == "editlink") + { + OSL_ENSURE(pCont, "edit without entry ? " ); + if (pCont) + { + SfxStringItem aName(FN_EDIT_REGION, + pCont->GetSection()->GetSectionName()); + rDispatch.ExecuteList(FN_EDIT_REGION, SfxCallMode::ASYNCHRON, + { &aName }); + } + } + else if (rSelectedPopupEntry == "deleteentry") + { + // If several entries selected, then after each delete the array + // must be refilled. So you do not have to remember anything, + // deleting begins at the end. + std::vector<int> aRows = m_xTreeView->get_selected_rows(); + std::sort(aRows.begin(), aRows.end()); + + std::unique_ptr<SwGlblDocContents> pTempContents; + m_pActiveShell->StartAction(); + for (auto iter = aRows.rbegin(); iter != aRows.rend(); ++iter) + { + m_pActiveShell->DeleteGlobalDocContent( + pTempContents ? *pTempContents : *m_pSwGlblDocContents, + *iter); + pTempContents.reset(new SwGlblDocContents); + m_pActiveShell->GetGlobalDocContent(*pTempContents); + } + pTempContents.reset(); + m_pActiveShell->EndAction(); + pCont = nullptr; + } + else if (rSelectedPopupEntry == "insertindex") + { + if(pContCopy) + { + SfxItemSet aSet( + m_pActiveShell->GetView().GetPool(), + svl::Items< + RES_FRM_SIZE, RES_FRM_SIZE, + RES_LR_SPACE, RES_LR_SPACE, + RES_BACKGROUND, RES_BACKGROUND, + RES_COL, RES_COL, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + FN_PARAM_TOX_TYPE, FN_PARAM_TOX_TYPE>{}); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractMultiTOXTabDialog> pDlg(pFact->CreateMultiTOXTabDialog( + m_xDialog->GetFrameWeld(), aSet, + *m_pActiveShell, + nullptr, + true)); + if(RET_OK == pDlg->Execute()) + { + SwTOXDescription& rDesc = pDlg->GetTOXDescription( + pDlg->GetCurrentTOXType()); + SwTOXMgr aMgr(m_pActiveShell); + SwTOXBase* pToInsert = nullptr; + if(aMgr.UpdateOrInsertTOX(rDesc, &pToInsert, pDlg->GetOutputItemSet())) + m_pActiveShell->InsertGlobalDocContent( *pContCopy, *pToInsert ); + } + pCont = nullptr; + } + } + else if (rSelectedPopupEntry == "insertfile") + { + m_pDocContent = std::move(pContCopy); + InsertRegion( m_pDocContent.get() ); + pCont = nullptr; + } + else if (rSelectedPopupEntry == "insertnewfile") + { + SfxViewFrame* pGlobFrame = m_pActiveShell->GetView().GetViewFrame(); + SwGlobalFrameListener_Impl aFrameListener(*pGlobFrame); + + // Creating a new doc + SfxStringItem aFactory(SID_NEWDOCDIRECT, + SwDocShell::Factory().GetFilterContainer()->GetName()); + + const SfxFrameItem* pItem = static_cast<const SfxFrameItem*>( + rDispatch.ExecuteList(SID_NEWDOCDIRECT, + SfxCallMode::SYNCHRON, { &aFactory })); + + // save at + SfxFrame* pFrame = pItem ? pItem->GetFrame() : nullptr; + SfxViewFrame* pViewFrame = pFrame ? pFrame->GetCurrentViewFrame() : nullptr; + if (pViewFrame) + { + const SfxBoolItem* pBool = static_cast<const SfxBoolItem*>( + pViewFrame->GetDispatcher()->Execute( + SID_SAVEASDOC, SfxCallMode::SYNCHRON )); + SfxObjectShell& rObj = *pViewFrame->GetObjectShell(); + const SfxMedium* pMedium = rObj.GetMedium(); + OUString sNewFile(pMedium->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri)); + // Insert the area with the Doc-Name + // Bring the own Doc in the foreground + if(aFrameListener.IsValid() && !sNewFile.isEmpty()) + { + pGlobFrame->ToTop(); + // Due to the update the entries are invalid + if (nEntry != -1) + { + Update( false ); + Display(); + m_xTreeView->select(nEntry); + Select(); + nEntry = m_xTreeView->get_selected_index(); + pCont = nEntry != -1 ? reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(nEntry).toInt64()) : nullptr; + } + else + { + nEntry = -1; + pCont = nullptr; + } + if(pBool->GetValue()) + { + InsertRegion(pCont, &sNewFile); + pViewFrame->ToTop(); + } + else + pViewFrame->GetDispatcher()->Execute(SID_CLOSEWIN, SfxCallMode::SYNCHRON); + } + else + { + pViewFrame->ToTop(); + return; + } + } + } + else if (rSelectedPopupEntry == "inserttext") + { + if (pCont) + m_pActiveShell->InsertGlobalDocContent(*pCont); + else + { + m_pActiveShell->SplitNode(); // Empty document + m_pActiveShell->Up( false ); + } + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + } + else if (rSelectedPopupEntry == "update") + pCont = nullptr; + + if (pCont) + GotoContent(pCont); + if (nSlot) + rDispatch.Execute(nSlot); + if (Update(bUpdateHard)) + Display(); +} + +IMPL_LINK_NOARG(SwGlobalTree, Timeout, Timer *, void) +{ + if (!m_xTreeView->has_focus() && Update(false)) + Display(); +} + +void SwGlobalTree::GotoContent(const SwGlblDocContent* pCont) +{ + m_pActiveShell->EnterStdMode(); + + switch( pCont->GetType() ) + { + case GLBLDOC_UNKNOWN: + m_pActiveShell->GotoGlobalDocContent(*pCont); + break; + case GLBLDOC_TOXBASE: + { + const OUString sName = pCont->GetTOX()->GetTOXName(); + if (!m_pActiveShell->GotoNextTOXBase(&sName)) + m_pActiveShell->GotoPrevTOXBase(&sName); + } + break; + case GLBLDOC_SECTION: + break; + } + +} + +void SwGlobalTree::ShowTree() +{ + m_aUpdateTimer.Start(); + m_xTreeView->show(); +} + +void SwGlobalTree::HideTree() +{ + m_aUpdateTimer.Stop(); + m_xTreeView->hide(); +} + +void SwGlobalTree::ExecCommand(const OString &rCmd) +{ + int nEntry = m_xTreeView->get_selected_index(); + if (nEntry == -1) + return; + if (rCmd == "edit") + { + const SwGlblDocContent* pCont = reinterpret_cast<const SwGlblDocContent*>( + m_xTreeView->get_id(nEntry).toInt64()); + EditContent(pCont); + } + else + { + if (m_xTreeView->count_selected_rows() == 1) + { + bool bMove = false; + sal_uLong nSource = nEntry; + sal_uLong nDest = nSource; + if (rCmd == "movedown") + { + size_t nEntryCount = m_xTreeView->n_children(); + bMove = nEntryCount > nSource + 1; + nDest+= 2; + } + else if (rCmd == "moveup") + { + bMove = 0 != nSource; + nDest--; + } + if( bMove && m_pActiveShell->MoveGlobalDocContent( + *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && + Update( false )) + Display(); + } + } +} + +bool SwGlobalTree::Update(bool bHard) +{ + SwView* pActView = GetParentWindow()->GetCreateView(); + bool bRet = false; + if (pActView && pActView->GetWrtShellPtr()) + { + const SwWrtShell* pOldShell = m_pActiveShell; + m_pActiveShell = pActView->GetWrtShellPtr(); + if(m_pActiveShell != pOldShell) + { + m_pSwGlblDocContents.reset(); + } + if(!m_pSwGlblDocContents) + { + m_pSwGlblDocContents.reset(new SwGlblDocContents); + bRet = true; + m_pActiveShell->GetGlobalDocContent(*m_pSwGlblDocContents); + } + else + { + bool bCopy = false; + std::unique_ptr<SwGlblDocContents> pTempContents(new SwGlblDocContents); + m_pActiveShell->GetGlobalDocContent(*pTempContents); + size_t nChildren = m_xTreeView->n_children(); + if (pTempContents->size() != m_pSwGlblDocContents->size() || + pTempContents->size() != nChildren) + { + bRet = true; + bCopy = true; + } + else + { + for(size_t i = 0; i < pTempContents->size() && !bCopy; i++) + { + SwGlblDocContent* pLeft = (*pTempContents)[i].get(); + SwGlblDocContent* pRight = (*m_pSwGlblDocContents)[i].get(); + GlobalDocContentType eType = pLeft->GetType(); + OUString sTemp = m_xTreeView->get_text(i); + if ( + eType != pRight->GetType() || + ( + eType == GLBLDOC_SECTION && + pLeft->GetSection()->GetSectionName() != sTemp + ) || + ( + eType == GLBLDOC_TOXBASE && + pLeft->GetTOX()->GetTitle() != sTemp + ) + ) + { + bCopy = true; + } + } + } + if (bCopy || bHard) + { + *m_pSwGlblDocContents = std::move( *pTempContents ); + bRet = true; + } + } + } + else + { + m_xTreeView->clear(); + if(m_pSwGlblDocContents) + m_pSwGlblDocContents->clear(); + } + // FIXME: Implement a test for changes! + return bRet; +} + +void SwGlobalTree::OpenDoc(const SwGlblDocContent* pCont) +{ + const OUString sFileName(pCont->GetSection()->GetLinkFileName().getToken(0, + sfx2::cTokenSeparator)); + bool bFound = false; + const SfxObjectShell* pCurr = SfxObjectShell::GetFirst(); + while( !bFound && pCurr ) + { + if(pCurr->GetMedium() && + pCurr->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri) == sFileName) + { + bFound = true; + SwGlobalTree::SetShowShell(pCurr); + Application::PostUserEvent(LINK(this, SwGlobalTree, ShowFrameHdl)); + pCurr = nullptr; + } + else + pCurr = SfxObjectShell::GetNext(*pCurr); + } + if(!bFound) + { + SfxStringItem aURL(SID_FILE_NAME, sFileName); + SfxBoolItem aReadOnly(SID_DOC_READONLY, false); + SfxStringItem aTargetFrameName( SID_TARGETNAME, "_blank" ); + SfxStringItem aReferer(SID_REFERER, m_pActiveShell->GetView().GetDocShell()->GetTitle()); + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()-> + ExecuteList(SID_OPENDOC, SfxCallMode::ASYNCHRON, + { &aURL, &aReadOnly, &aReferer, &aTargetFrameName }); + } +} + +IMPL_LINK_NOARG( SwGlobalTree, DoubleClickHdl, weld::TreeView&, bool) +{ + int nEntry = m_xTreeView->get_cursor_index(); + SwGlblDocContent* pCont = reinterpret_cast<SwGlblDocContent*>(m_xTreeView->get_id(nEntry).toInt64()); + if (pCont->GetType() == GLBLDOC_SECTION) + OpenDoc(pCont); + else + { + GotoContent(pCont); + m_pActiveShell->GetView().GetEditWin().GrabFocus(); + } + return false; +} + +SwNavigationPI* SwGlobalTree::GetParentWindow() +{ + return m_xDialog; +} + +IMPL_STATIC_LINK_NOARG(SwGlobalTree, ShowFrameHdl, void*, void) +{ + SfxViewFrame* pFirst = pShowShell ? SfxViewFrame::GetFirst(pShowShell) : nullptr; + if (pFirst) + pFirst->ToTop(); + SwGlobalTree::SetShowShell(nullptr); +} + +void SwGlobalTree::InsertRegion( const SwGlblDocContent* _pContent, const Sequence< OUString >& _rFiles ) +{ + sal_Int32 nFiles = _rFiles.getLength(); + if (!nFiles) + return; + + size_t nEntryCount = m_xTreeView->n_children(); + + bool bMove = _pContent == nullptr; + const OUString* pFileNames = _rFiles.getConstArray(); + SwWrtShell& rSh = GetParentWindow()->GetCreateView()->GetWrtShell(); + rSh.StartAction(); + // after insertion of the first new content the 'pCont' parameter becomes invalid + // find the index of the 'anchor' content to always use a current anchor content + size_t nAnchorContent = m_pSwGlblDocContents->size() - 1; + if (!bMove) + { + for (size_t nContent = 0; nContent < m_pSwGlblDocContents->size(); + ++nContent) + { + if( *_pContent == *(*m_pSwGlblDocContents)[ nContent ] ) + { + nAnchorContent = nContent; + break; + } + } + } + SwGlblDocContents aTempContents; + for ( sal_Int32 nFile = 0; nFile < nFiles; ++nFile ) + { + //update the global document content after each inserted document + rSh.GetGlobalDocContent(aTempContents); + SwGlblDocContent* pAnchorContent = nullptr; + OSL_ENSURE(aTempContents.size() > (nAnchorContent + nFile), "invalid anchor content -> last insertion failed"); + if ( aTempContents.size() > (nAnchorContent + nFile) ) + pAnchorContent = aTempContents[nAnchorContent + nFile].get(); + else + pAnchorContent = aTempContents.back().get(); + OUString sFileName(pFileNames[nFile]); + INetURLObject aFileUrl; + aFileUrl.SetSmartURL( sFileName ); + OUString sSectionName(aFileUrl.GetLastName( + INetURLObject::DecodeMechanism::Unambiguous).getToken(0, sfx2::cTokenSeparator)); + sal_uInt16 nSectCount = rSh.GetSectionFormatCount(); + OUString sTempSectionName(sSectionName); + sal_uInt16 nAddNumber = 0; + sal_uInt16 nCount = 0; + // if applicable: add index if the range name is already in use. + while ( nCount < nSectCount ) + { + const SwSectionFormat& rFormat = rSh.GetSectionFormat(nCount); + if ((rFormat.GetSection()->GetSectionName() == sTempSectionName) + && rFormat.IsInNodesArr()) + { + nCount = 0; + nAddNumber++; + sTempSectionName = sSectionName + ":" + OUString::number( nAddNumber ); + } + else + nCount++; + } + + if ( nAddNumber ) + sSectionName = sTempSectionName; + + SwSectionData aSectionData(SectionType::Content, sSectionName); + aSectionData.SetProtectFlag(true); + aSectionData.SetHidden(false); + + aSectionData.SetLinkFileName(sFileName); + aSectionData.SetType(SectionType::FileLink); + aSectionData.SetLinkFilePassword( OUString() ); + + rSh.InsertGlobalDocContent( *pAnchorContent, aSectionData ); + } + if (bMove) + { + Update( false ); + rSh.MoveGlobalDocContent( + *m_pSwGlblDocContents, nEntryCount, nEntryCount + nFiles, nEntryCount - nFiles ); + } + rSh.EndAction(); + Update( false ); + Display(); + +} + +IMPL_LINK( SwGlobalTree, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ) +{ + if ( ERRCODE_NONE != _pFileDlg->GetError() ) + return; + + SfxMediumList aMedList(m_pDocInserter->CreateMediumList()); + if ( !aMedList.empty() ) + { + Sequence< OUString >aFileNames( aMedList.size() ); + OUString* pFileNames = aFileNames.getArray(); + sal_Int32 nPos = 0; + for (const std::unique_ptr<SfxMedium>& pMed : aMedList) + { + OUString sFileName = pMed->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) + + OUStringChar(sfx2::cTokenSeparator) + + pMed->GetFilter()->GetFilterName() + + OUStringChar(sfx2::cTokenSeparator); + pFileNames[nPos++] = sFileName; + } + InsertRegion( m_pDocContent.get(), aFileNames ); + m_pDocContent.reset(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/gloslst.cxx b/sw/source/uibase/utlui/gloslst.cxx new file mode 100644 index 000000000..b58335850 --- /dev/null +++ b/sw/source/uibase/utlui/gloslst.cxx @@ -0,0 +1,441 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/weld.hxx> +#include <svl/fstathelper.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <shellio.hxx> +#include <initui.hxx> +#include <glosdoc.hxx> +#include <gloslst.hxx> +#include <swunohelper.hxx> +#include <view.hxx> + +#include <vector> + +#define STRING_DELIM char(0x0A) +#define GLOS_TIMEOUT 30000 // update every 30 seconds +#define FIND_MAX_GLOS 20 + +namespace { + +struct TripleString +{ + OUString sGroup; + OUString sBlock; + OUString sShort; +}; + +class SwGlossDecideDlg : public weld::GenericDialogController +{ + std::unique_ptr<weld::Button> m_xOk; + std::unique_ptr<weld::TreeView> m_xListLB; + + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(SelectHdl, weld::TreeView&, void); + +public: + explicit SwGlossDecideDlg(weld::Window* pParent); + + weld::TreeView& GetTreeView() {return *m_xListLB;} +}; + +} + +SwGlossDecideDlg::SwGlossDecideDlg(weld::Window* pParent) + : GenericDialogController(pParent, "modules/swriter/ui/selectautotextdialog.ui", "SelectAutoTextDialog") + , m_xOk(m_xBuilder->weld_button("ok")) + , m_xListLB(m_xBuilder->weld_tree_view("treeview")) +{ + m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 32, + m_xListLB->get_height_rows(8)); + m_xListLB->connect_row_activated(LINK(this, SwGlossDecideDlg, DoubleClickHdl)); + m_xListLB->connect_changed(LINK(this, SwGlossDecideDlg, SelectHdl)); +} + +IMPL_LINK_NOARG(SwGlossDecideDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +IMPL_LINK_NOARG(SwGlossDecideDlg, SelectHdl, weld::TreeView&, void) +{ + m_xOk->set_sensitive(m_xListLB->get_selected_index() != -1); +} + +SwGlossaryList::SwGlossaryList() : + bFilled(false) +{ + SvtPathOptions aPathOpt; + sPath = aPathOpt.GetAutoTextPath(); + SetTimeout(GLOS_TIMEOUT); +} + +SwGlossaryList::~SwGlossaryList() +{ + ClearGroups(); +} + +// If the GroupName is already known, then only rShortName +// will be filled. Otherwise also rGroupName will be set and +// on demand asked for the right group. + +bool SwGlossaryList::GetShortName(const OUString& rLongName, + OUString& rShortName, OUString& rGroupName ) +{ + if(!bFilled) + Update(); + + std::vector<TripleString> aTripleStrings; + + size_t nCount = aGroupArr.size(); + for(size_t i = 0; i < nCount; i++ ) + { + AutoTextGroup* pGroup = aGroupArr[i].get(); + if(!rGroupName.isEmpty() && rGroupName != pGroup->sName) + continue; + + sal_Int32 nPosLong = 0; + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + const OUString sLong = pGroup->sLongNames.getToken(0, STRING_DELIM, nPosLong); + if(rLongName != sLong) + continue; + + TripleString aTriple; + aTriple.sGroup = pGroup->sName; + aTriple.sBlock = sLong; + aTriple.sShort = pGroup->sShortNames.getToken(j, STRING_DELIM); + aTripleStrings.push_back(aTriple); + } + } + + bool bRet = false; + nCount = aTripleStrings.size(); + if(1 == nCount) + { + const TripleString& rTriple(aTripleStrings.front()); + rShortName = rTriple.sShort; + rGroupName = rTriple.sGroup; + bRet = true; + } + else if(1 < nCount) + { + SwView *pView = ::GetActiveView(); + SwGlossDecideDlg aDlg(pView ? pView->GetFrameWeld() : nullptr); + OUString sTitle = aDlg.get_title() + " " + aTripleStrings.front().sBlock; + aDlg.set_title(sTitle); + + weld::TreeView& rLB = aDlg.GetTreeView(); + for (const auto& rTriple : aTripleStrings) + rLB.append_text(rTriple.sGroup.getToken(0, GLOS_DELIM)); + + rLB.select(0); + if (aDlg.run() == RET_OK && rLB.get_selected_index() != -1) + { + const TripleString& rTriple(aTripleStrings[rLB.get_selected_index()]); + rShortName = rTriple.sShort; + rGroupName = rTriple.sGroup; + bRet = true; + } + else + bRet = false; + } + return bRet; +} + +size_t SwGlossaryList::GetGroupCount() +{ + if(!bFilled) + Update(); + return aGroupArr.size(); +} + +OUString SwGlossaryList::GetGroupName(size_t nPos) +{ + OSL_ENSURE(aGroupArr.size() > nPos, "group not available"); + if(nPos < aGroupArr.size()) + { + AutoTextGroup* pGroup = aGroupArr[nPos].get(); + OUString sRet = pGroup->sName; + return sRet; + } + return OUString(); +} + +OUString SwGlossaryList::GetGroupTitle(size_t nPos) +{ + OSL_ENSURE(aGroupArr.size() > nPos, "group not available"); + if(nPos < aGroupArr.size()) + { + AutoTextGroup* pGroup = aGroupArr[nPos].get(); + return pGroup->sTitle; + } + return OUString(); +} + +sal_uInt16 SwGlossaryList::GetBlockCount(size_t nGroup) +{ + OSL_ENSURE(aGroupArr.size() > nGroup, "group not available"); + if(nGroup < aGroupArr.size()) + { + AutoTextGroup* pGroup = aGroupArr[nGroup].get(); + return pGroup->nCount; + } + return 0; +} + +OUString SwGlossaryList::GetBlockLongName(size_t nGroup, sal_uInt16 nBlock) +{ + OSL_ENSURE(aGroupArr.size() > nGroup, "group not available"); + if(nGroup < aGroupArr.size()) + { + AutoTextGroup* pGroup = aGroupArr[nGroup].get(); + return pGroup->sLongNames.getToken(nBlock, STRING_DELIM); + } + return OUString(); +} + +OUString SwGlossaryList::GetBlockShortName(size_t nGroup, sal_uInt16 nBlock) +{ + OSL_ENSURE(aGroupArr.size() > nGroup, "group not available"); + if(nGroup < aGroupArr.size()) + { + AutoTextGroup* pGroup = aGroupArr[nGroup].get(); + return pGroup->sShortNames.getToken(nBlock, STRING_DELIM); + } + return OUString(); +} + +void SwGlossaryList::Update() +{ + if(!IsActive()) + Start(); + + SvtPathOptions aPathOpt; + const OUString& sTemp( aPathOpt.GetAutoTextPath() ); + if(sTemp != sPath) + { + sPath = sTemp; + bFilled = false; + ClearGroups(); + } + SwGlossaries* pGlossaries = ::GetGlossaries(); + const std::vector<OUString> & rPathArr = pGlossaries->GetPathArray(); + const OUString sExt( SwGlossaries::GetExtension() ); + if(!bFilled) + { + const size_t nGroupCount = pGlossaries->GetGroupCnt(); + for(size_t i = 0; i < nGroupCount; ++i) + { + OUString sGrpName = pGlossaries->GetGroupName(i); + const size_t nPath = static_cast<size_t>( + sGrpName.getToken(1, GLOS_DELIM).toInt32()); + if( nPath < rPathArr.size() ) + { + std::unique_ptr<AutoTextGroup> pGroup(new AutoTextGroup); + pGroup->sName = sGrpName; + + FillGroup(pGroup.get(), pGlossaries); + OUString sName = rPathArr[nPath] + "/" + + pGroup->sName.getToken(0, GLOS_DELIM) + sExt; + FStatHelper::GetModifiedDateTimeOfFile( sName, + &pGroup->aDateModified, + &pGroup->aDateModified ); + + aGroupArr.insert( aGroupArr.begin(), std::move(pGroup) ); + } + } + bFilled = true; + } + else + { + for( size_t nPath = 0; nPath < rPathArr.size(); nPath++ ) + { + std::vector<OUString> aFoundGroupNames; + std::vector<OUString> aFiles; + std::vector<DateTime> aDateTimeArr; + + SWUnoHelper::UCB_GetFileListOfFolder( rPathArr[nPath], aFiles, + &sExt, &aDateTimeArr ); + for( size_t nFiles = 0; nFiles < aFiles.size(); ++nFiles ) + { + const OUString aTitle = aFiles[ nFiles ]; + ::DateTime& rDT = aDateTimeArr[ nFiles ]; + + OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() )); + + aFoundGroupNames.push_back(sName); + sName += OUStringChar(GLOS_DELIM) + OUString::number( static_cast<sal_uInt16>(nPath) ); + AutoTextGroup* pFound = FindGroup( sName ); + if( !pFound ) + { + pFound = new AutoTextGroup; + pFound->sName = sName; + FillGroup( pFound, pGlossaries ); + pFound->aDateModified = rDT; + + aGroupArr.push_back(std::unique_ptr<AutoTextGroup>(pFound)); + } + else if( pFound->aDateModified < rDT ) + { + FillGroup(pFound, pGlossaries); + pFound->aDateModified = rDT; + } + } + + for( size_t i = aGroupArr.size(); i>0; ) + { + --i; + // maybe remove deleted groups + AutoTextGroup* pGroup = aGroupArr[i].get(); + const size_t nGroupPath = static_cast<size_t>( + pGroup->sName.getToken( 1, GLOS_DELIM).toInt32()); + // Only the groups will be checked which are registered + // for the current subpath. + if( nGroupPath == nPath ) + { + OUString sCompareGroup = pGroup->sName.getToken(0, GLOS_DELIM); + bool bFound = std::any_of(aFoundGroupNames.begin(), aFoundGroupNames.end(), + [&sCompareGroup](const OUString& rGroupName) { return sCompareGroup == rGroupName; }); + + if(!bFound) + { + aGroupArr.erase(aGroupArr.begin() + i); + } + } + } + } + } +} + +void SwGlossaryList::Invoke() +{ + // Only update automatically if a SwView has the focus. + if(::GetActiveView()) + Update(); +} + +AutoTextGroup* SwGlossaryList::FindGroup(const OUString& rGroupName) +{ + for(const auto & pRet : aGroupArr) + { + if(pRet->sName == rGroupName) + return pRet.get(); + } + return nullptr; +} + +void SwGlossaryList::FillGroup(AutoTextGroup* pGroup, SwGlossaries* pGlossaries) +{ + std::unique_ptr<SwTextBlocks> pBlock = pGlossaries->GetGroupDoc(pGroup->sName); + pGroup->nCount = pBlock ? pBlock->GetCount() : 0; + pGroup->sLongNames.clear(); + pGroup->sShortNames.clear(); + if(pBlock) + pGroup->sTitle = pBlock->GetName(); + + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + pGroup->sLongNames += pBlock->GetLongName(j) + + OUStringChar(STRING_DELIM); + pGroup->sShortNames += pBlock->GetShortName(j) + + OUStringChar(STRING_DELIM); + } +} + +// Give back all (not exceeding FIND_MAX_GLOS) found modules +// with matching beginning. + +void SwGlossaryList::HasLongName(const std::vector<OUString>& rBeginCandidates, + std::vector<std::pair<OUString, sal_uInt16>>& rLongNames) +{ + if(!bFilled) + Update(); + const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); + // We store results for all candidate words in separate lists, so that later + // we can sort them according to the candidate position + std::vector<std::vector<OUString>> aResults(rBeginCandidates.size()); + + // We can't break after FIND_MAX_GLOS items found, since first items may have ended up in + // lower-priority lists, and those from higher-priority lists are yet to come. So process all. + for (const auto& pGroup : aGroupArr) + { + sal_Int32 nIdx{ 0 }; + for(sal_uInt16 j = 0; j < pGroup->nCount; j++) + { + OUString sBlock = pGroup->sLongNames.getToken(0, STRING_DELIM, nIdx); + for (size_t i = 0; i < rBeginCandidates.size(); ++i) + { + const OUString& s = rBeginCandidates[i]; + if (s.getLength() + 1 < sBlock.getLength() + && rSCmp.isEqual(sBlock.copy(0, s.getLength()), s)) + { + aResults[i].push_back(sBlock); + } + } + } + } + + std::vector<std::pair<OUString, sal_uInt16>> aAllResults; + // Sort and concatenate all result lists. See QuickHelpData::SortAndFilter + for (size_t i = 0; i < rBeginCandidates.size(); ++i) + { + std::sort(aResults[i].begin(), aResults[i].end(), + [origWord = rBeginCandidates[i]](const OUString& s1, const OUString& s2) { + int nRet = s1.compareToIgnoreAsciiCase(s2); + if (nRet == 0) + { + // fdo#61251 sort stuff that starts with the exact rOrigWord before + // another ignore-case candidate + int n1StartsWithOrig = s1.startsWith(origWord) ? 0 : 1; + int n2StartsWithOrig = s2.startsWith(origWord) ? 0 : 1; + return n1StartsWithOrig < n2StartsWithOrig; + } + return nRet < 0; + }); + // All suggestions must be accompanied with length of the text they would replace + std::transform(aResults[i].begin(), aResults[i].end(), std::back_inserter(aAllResults), + [nLen = sal_uInt16(rBeginCandidates[i].getLength())](const OUString& s) { + return std::make_pair(s, nLen); + }); + } + + const auto& it = std::unique( + aAllResults.begin(), aAllResults.end(), + [](const std::pair<OUString, sal_uInt16>& s1, const std::pair<OUString, sal_uInt16>& s2) { + return s1.first.equalsIgnoreAsciiCase(s2.first); + }); + if (const auto nCount = std::min<size_t>(std::distance(aAllResults.begin(), it), FIND_MAX_GLOS)) + { + rLongNames.insert(rLongNames.end(), aAllResults.begin(), aAllResults.begin() + nCount); + } +} + +void SwGlossaryList::ClearGroups() +{ + aGroupArr.clear(); + bFilled = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/gotodlg.cxx b/sw/source/uibase/utlui/gotodlg.cxx new file mode 100644 index 000000000..63e7c424c --- /dev/null +++ b/sw/source/uibase/utlui/gotodlg.cxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <gotodlg.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +using namespace com::sun::star; + +SwGotoPageDlg::SwGotoPageDlg(weld::Window* pParent, SfxBindings* _pBindings) + : GenericDialogController(pParent, "modules/swriter/ui/gotopagedialog.ui", "GotoPageDialog") + , m_pCreateView(nullptr) + , m_rBindings(_pBindings) + , mnMaxPageCnt(1) + , mxMtrPageCtrl(m_xBuilder->weld_entry("page")) + , mxPageNumberLbl(m_xBuilder->weld_label("page_count")) +{ + sal_uInt16 nTotalPage = GetPageInfo(); + + if(nTotalPage) + { + OUString sStr = mxPageNumberLbl->get_label(); + mxPageNumberLbl->set_label(sStr.replaceFirst("$1", OUString::number(nTotalPage))); + mnMaxPageCnt = nTotalPage; + } + mxMtrPageCtrl->connect_changed(LINK(this, SwGotoPageDlg, PageModifiedHdl)); + mxMtrPageCtrl->set_position(-1); + mxMtrPageCtrl->select_region(0, -1); +} + +IMPL_LINK_NOARG(SwGotoPageDlg, PageModifiedHdl, weld::Entry&, void) +{ + if (!mxMtrPageCtrl->get_text().isEmpty()) + { + int page_value = mxMtrPageCtrl->get_text().toInt32(); + + if (page_value <= 0) + mxMtrPageCtrl->set_text(OUString::number(1)); + else if(page_value > mnMaxPageCnt) + mxMtrPageCtrl->set_text(OUString::number(mnMaxPageCnt)); + + mxMtrPageCtrl->set_position(-1); + } +} + +SwView* SwGotoPageDlg::GetCreateView() const +{ + if(!m_pCreateView) + { + SwView* pView = SwModule::GetFirstView(); + while(pView) + { + if(&pView->GetViewFrame()->GetBindings() == m_rBindings) + { + const_cast<SwGotoPageDlg*>(this)->m_pCreateView = pView; + break; + } + pView = SwModule::GetNextView(pView); + } + } + + return m_pCreateView; +} + +// If the page can be set here, the maximum is set. + +sal_uInt16 SwGotoPageDlg::GetPageInfo() +{ + SwView *pView = GetCreateView(); + SwWrtShell *pSh = pView ? &pView->GetWrtShell() : nullptr; + mxMtrPageCtrl->set_text(OUString::number(1)); + if (pSh) + { + const sal_uInt16 nPageCnt = pSh->GetPageCnt(); + sal_uInt16 nPhyPage, nVirPage; + pSh->GetPageNum(nPhyPage, nVirPage); + mxMtrPageCtrl->set_text(OUString::number(nPhyPage)); + return nPageCnt; + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/initui.cxx b/sw/source/uibase/utlui/initui.cxx new file mode 100644 index 000000000..f9567ec4a --- /dev/null +++ b/sw/source/uibase/utlui/initui.cxx @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unotools/localedatawrapper.hxx> +#include <viewsh.hxx> +#include <initui.hxx> +#include <edtwin.hxx> +#include <shellres.hxx> +#include <fldbas.hxx> +#include <glosdoc.hxx> +#include <gloslst.hxx> +#include <comcore.hxx> +#include <strings.hrc> +#include <utlui.hrc> +#include <authfld.hxx> +#include <unotools/syslocale.hxx> + +// Global Pointer + +static std::unique_ptr<SwGlossaries> pGlossaries; + +// Provides all needed paths. Is initialized by UI. +static SwGlossaryList* pGlossaryList = nullptr; + +namespace +{ +OUString CurrGlosGroup; +} + +const OUString& GetCurrGlosGroup() +{ + return CurrGlosGroup; +} + +void SetCurrGlosGroup(const OUString& sStr) +{ + CurrGlosGroup = sStr; +} + +namespace +{ + +std::vector<OUString>* pAuthFieldNameList = nullptr; +std::vector<OUString>* pAuthFieldTypeList = nullptr; + +} + +// Finish UI + +void FinitUI() +{ + delete SwViewShell::GetShellRes(); + SwViewShell::SetShellRes( nullptr ); + + SwEditWin::FinitStaticData(); + + pGlossaries.reset(); + + delete SwFieldType::s_pFieldNames; + + delete pGlossaryList; + delete pAuthFieldNameList; + delete pAuthFieldTypeList; + +} + +// Initialise + +void InitUI() +{ + // ShellResource gives the CORE the possibility to work with resources. + SwViewShell::SetShellRes( new ShellResource ); + SwEditWin::InitStaticData(); +} + +static const char* FLD_DOCINFO_ARY[] = +{ + FLD_DOCINFO_TITLE, + FLD_DOCINFO_SUBJECT, + FLD_DOCINFO_KEYS, + FLD_DOCINFO_COMMENT, + FLD_DOCINFO_CREATE, + FLD_DOCINFO_CHANGE, + FLD_DOCINFO_PRINT, + FLD_DOCINFO_DOCNO, + FLD_DOCINFO_EDIT +}; + +ShellResource::ShellResource() + : aPostItAuthor( SwResId( STR_POSTIT_AUTHOR ) ), + aPostItPage( SwResId( STR_POSTIT_PAGE ) ), + aPostItLine( SwResId( STR_POSTIT_LINE ) ), + + aCalc_Syntax( SwResId( STR_CALC_SYNTAX ) ), + aCalc_ZeroDiv( SwResId( STR_CALC_ZERODIV ) ), + aCalc_Brack( SwResId( STR_CALC_BRACK ) ), + aCalc_Pow( SwResId( STR_CALC_POW ) ), + aCalc_Overflow( SwResId( STR_CALC_OVERFLOW ) ), + aCalc_Default( SwResId( STR_CALC_DEFAULT ) ), + aCalc_Error( SwResId( STR_CALC_ERROR ) ), + + // #i81002# + aGetRefField_RefItemNotFound( SwResId( STR_GETREFFLD_REFITEMNOTFOUND ) ), + aStrNone( SwResId( STR_TEMPLATE_NONE )), + aFixedStr( SwResId( STR_FIELD_FIXED )), + sDurationFormat( SwResId( STR_DURATION_FORMAT )), + + aTOXIndexName( SwResId(STR_TOI)), + aTOXUserName( SwResId(STR_TOU)), + aTOXContentName( SwResId(STR_TOC)), + aTOXIllustrationsName( SwResId(STR_TOX_ILL)), + aTOXObjectsName( SwResId(STR_TOX_OBJ)), + aTOXTablesName( SwResId(STR_TOX_TBL)), + aTOXAuthoritiesName( SwResId(STR_TOX_AUTH)), + aTOXCitationName( SwResId(STR_TOX_CITATION)), + sPageDescFirstName( SwResId(STR_PAGEDESC_FIRSTNAME)), + sPageDescFollowName( SwResId(STR_PAGEDESC_FOLLOWNAME)), + sPageDescName( SwResId(STR_PAGEDESC_NAME)) +{ + for (size_t i = 0; i < SAL_N_ELEMENTS(FLD_DOCINFO_ARY); ++i) + aDocInfoLst.push_back(SwResId(FLD_DOCINFO_ARY[i])); +} + +OUString ShellResource::GetPageDescName(sal_uInt16 nNo, PageNameMode eMode) +{ + OUString sRet; + + switch (eMode) + { + case NORMAL_PAGE: + sRet = sPageDescName; + break; + case FIRST_PAGE: + sRet = sPageDescFirstName; + break; + case FOLLOW_PAGE: + sRet = sPageDescFollowName; + break; + } + + return sRet.replaceFirst( "$(ARG1)", OUString::number( nNo )); +} + +SwGlossaries* GetGlossaries() +{ + if (!pGlossaries) + pGlossaries.reset( new SwGlossaries ); + return pGlossaries.get(); +} + +bool HasGlossaryList() +{ + return pGlossaryList != nullptr; +} + +SwGlossaryList* GetGlossaryList() +{ + if(!pGlossaryList) + pGlossaryList = new SwGlossaryList(); + + return pGlossaryList; +} + +void ShellResource::GetAutoFormatNameLst_() const +{ + assert(!pAutoFormatNameLst); + pAutoFormatNameLst.reset( new std::vector<OUString> ); + pAutoFormatNameLst->reserve(STR_AUTOFMTREDL_END); + + assert(SAL_N_ELEMENTS(RID_SHELLRES_AUTOFMTSTRS) == STR_AUTOFMTREDL_END); + for (sal_uInt16 n = 0; n < STR_AUTOFMTREDL_END; ++n) + { + OUString p(SwResId(RID_SHELLRES_AUTOFMTSTRS[n])); + if (STR_AUTOFMTREDL_TYPO == n) + { + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLclD = aSysLocale.GetLocaleData(); + p = p.replaceFirst("%1", rLclD.getDoubleQuotationMarkStart()); + p = p.replaceFirst("%2", rLclD.getDoubleQuotationMarkEnd()); + } + pAutoFormatNameLst->push_back(p); + } +} + +namespace +{ + const char* STR_AUTH_FIELD_ARY[] = + { + STR_AUTH_FIELD_IDENTIFIER, + STR_AUTH_FIELD_AUTHORITY_TYPE, + STR_AUTH_FIELD_ADDRESS, + STR_AUTH_FIELD_ANNOTE, + STR_AUTH_FIELD_AUTHOR, + STR_AUTH_FIELD_BOOKTITLE, + STR_AUTH_FIELD_CHAPTER, + STR_AUTH_FIELD_EDITION, + STR_AUTH_FIELD_EDITOR, + STR_AUTH_FIELD_HOWPUBLISHED, + STR_AUTH_FIELD_INSTITUTION, + STR_AUTH_FIELD_JOURNAL, + STR_AUTH_FIELD_MONTH, + STR_AUTH_FIELD_NOTE, + STR_AUTH_FIELD_NUMBER, + STR_AUTH_FIELD_ORGANIZATIONS, + STR_AUTH_FIELD_PAGES, + STR_AUTH_FIELD_PUBLISHER, + STR_AUTH_FIELD_SCHOOL, + STR_AUTH_FIELD_SERIES, + STR_AUTH_FIELD_TITLE, + STR_AUTH_FIELD_TYPE, + STR_AUTH_FIELD_VOLUME, + STR_AUTH_FIELD_YEAR, + STR_AUTH_FIELD_URL, + STR_AUTH_FIELD_CUSTOM1, + STR_AUTH_FIELD_CUSTOM2, + STR_AUTH_FIELD_CUSTOM3, + STR_AUTH_FIELD_CUSTOM4, + STR_AUTH_FIELD_CUSTOM5, + STR_AUTH_FIELD_ISBN + }; +} + +OUString const & SwAuthorityFieldType::GetAuthFieldName(ToxAuthorityField eType) +{ + if(!pAuthFieldNameList) + { + pAuthFieldNameList = new std::vector<OUString>; + pAuthFieldNameList->reserve(AUTH_FIELD_END); + for (sal_uInt16 i = 0; i < AUTH_FIELD_END; ++i) + pAuthFieldNameList->push_back(SwResId(STR_AUTH_FIELD_ARY[i])); + } + return (*pAuthFieldNameList)[static_cast< sal_uInt16 >(eType)]; +} + +static const char* STR_AUTH_TYPE_ARY[] = +{ + STR_AUTH_TYPE_ARTICLE, + STR_AUTH_TYPE_BOOK, + STR_AUTH_TYPE_BOOKLET, + STR_AUTH_TYPE_CONFERENCE, + STR_AUTH_TYPE_INBOOK, + STR_AUTH_TYPE_INCOLLECTION, + STR_AUTH_TYPE_INPROCEEDINGS, + STR_AUTH_TYPE_JOURNAL, + STR_AUTH_TYPE_MANUAL, + STR_AUTH_TYPE_MASTERSTHESIS, + STR_AUTH_TYPE_MISC, + STR_AUTH_TYPE_PHDTHESIS, + STR_AUTH_TYPE_PROCEEDINGS, + STR_AUTH_TYPE_TECHREPORT, + STR_AUTH_TYPE_UNPUBLISHED, + STR_AUTH_TYPE_EMAIL, + STR_AUTH_TYPE_WWW, + STR_AUTH_TYPE_CUSTOM1, + STR_AUTH_TYPE_CUSTOM2, + STR_AUTH_TYPE_CUSTOM3, + STR_AUTH_TYPE_CUSTOM4, + STR_AUTH_TYPE_CUSTOM5 +}; + +OUString const & SwAuthorityFieldType::GetAuthTypeName(ToxAuthorityType eType) +{ + if(!pAuthFieldTypeList) + { + pAuthFieldTypeList = new std::vector<OUString>; + pAuthFieldTypeList->reserve(AUTH_TYPE_END); + for (sal_uInt16 i = 0; i < AUTH_TYPE_END; ++i) + pAuthFieldTypeList->push_back(SwResId(STR_AUTH_TYPE_ARY[i])); + } + return (*pAuthFieldTypeList)[static_cast< sal_uInt16 >(eType)]; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/navicfg.cxx b/sw/source/uibase/utlui/navicfg.cxx new file mode 100644 index 000000000..f1b4f3983 --- /dev/null +++ b/sw/source/uibase/utlui/navicfg.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <navicfg.hxx> +#include <swcont.hxx> +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +using namespace ::utl; +using namespace ::com::sun::star::uno; + +Sequence<OUString> SwNavigationConfig::GetPropertyNames() +{ + return css::uno::Sequence<OUString>{ + OUString("RootType"), + OUString("SelectedPosition"), + OUString("OutlineLevel"), + OUString("InsertMode"), + OUString("ActiveBlock"), + OUString("ShowListBox"), + OUString("GlobalDocMode")}; +} + +SwNavigationConfig::SwNavigationConfig() : + utl::ConfigItem("Office.Writer/Navigator"), + nRootType(ContentTypeId::UNKNOWN), + nSelectedPos(0), + nOutlineLevel(MAXLEVEL), + nRegionMode(RegionMode::NONE), + nActiveBlock(0), + bIsSmall(false), + bIsGlobalActive(true) +{ + 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()) + { + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + if(pValues[nProp].hasValue()) + { + switch(nProp) + { + case 0: + { + sal_Int32 nTmp = {}; // spurious -Werror=maybe-uninitialized + if (pValues[nProp] >>= nTmp) + { + if (nTmp < sal_Int32(ContentTypeId::UNKNOWN) + || nTmp > sal_Int32(ContentTypeId::LAST)) + { + SAL_WARN( + "sw", + "out-of-bounds ContentTypeId " << nTmp); + nTmp = sal_Int32(ContentTypeId::UNKNOWN); + } + nRootType = static_cast<ContentTypeId>(nTmp); + } + break; + } + case 1: pValues[nProp] >>= nSelectedPos; break; + case 2: pValues[nProp] >>= nOutlineLevel; break; + case 3: + { + sal_uInt16 nTmp; + if (pValues[nProp] >>= nTmp) + nRegionMode = static_cast<RegionMode>(nTmp); + break; + } + case 4: pValues[nProp] >>= nActiveBlock; break; + case 5: bIsSmall = *o3tl::doAccess<bool>(pValues[nProp]); break; + case 6: bIsGlobalActive = *o3tl::doAccess<bool>(pValues[nProp]); break; + } + } + } + } +} + +SwNavigationConfig::~SwNavigationConfig() +{ +} + +void SwNavigationConfig::ImplCommit() +{ + Sequence<OUString> aNames = GetPropertyNames(); + Sequence<Any> aValues(aNames.getLength()); + Any* pValues = aValues.getArray(); + + for(int nProp = 0; nProp < aNames.getLength(); nProp++) + { + switch(nProp) + { + case 0: pValues[nProp] <<= static_cast<sal_Int32>(nRootType); break; + case 1: pValues[nProp] <<= nSelectedPos; break; + case 2: pValues[nProp] <<= nOutlineLevel; break; + case 3: pValues[nProp] <<= static_cast<sal_uInt16>(nRegionMode); break; + case 4: pValues[nProp] <<= nActiveBlock; break; + case 5: pValues[nProp] <<= bIsSmall; break; + case 6: pValues[nProp] <<= bIsGlobalActive; break; + } + } + PutProperties(aNames, aValues); +} + +void SwNavigationConfig::Notify( const css::uno::Sequence< OUString >& ) {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/navipi.cxx b/sw/source/uibase/utlui/navipi.cxx new file mode 100644 index 000000000..577aba8fc --- /dev/null +++ b/sw/source/uibase/utlui/navipi.cxx @@ -0,0 +1,1128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/string.hxx> +#include <svl/urlbmk.hxx> +#include <svl/stritem.hxx> +#include <vcl/graphicfilter.hxx> +#include <sot/formats.hxx> +#include <sot/filelist.hxx> +#include <sfx2/event.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/navigat.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/urlobj.hxx> +#include <vcl/toolbox.hxx> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <navicfg.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <navipi.hxx> +#include <workctrl.hxx> +#include <edtwin.hxx> +#include <sfx2/app.hxx> +#include <cmdid.h> +#include <helpids.h> + +#include <strings.hrc> +#include <bitmaps.hlst> + +#include <memory> + +#include <uiobject.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +//! soon obsolete ! +std::unique_ptr<SfxChildWindowContext> SwNavigationChild::CreateImpl( vcl::Window *pParent, + SfxBindings *pBindings, SfxChildWinInfo* /*pInfo*/ ) +{ + return std::make_unique<SwNavigationChild>(pParent, + /* cast is safe here! */static_cast< sal_uInt16 >(SwView::GetInterfaceId()), + pBindings); +} +void SwNavigationChild::RegisterChildWindowContext(SfxModule* pMod) +{ + auto pFact = std::make_unique<SfxChildWinContextFactory>( + SwNavigationChild::CreateImpl, + /* cast is safe here! */static_cast< sal_uInt16 >(SwView::GetInterfaceId()) ); + SfxChildWindowContext::RegisterChildWindowContext(pMod, SID_NAVIGATOR, std::move(pFact)); +} + + +// Filter the control characters out of the Outline-Entry + +OUString SwNavigationPI::CleanEntry(const OUString& rEntry) +{ + if (rEntry.isEmpty()) + return rEntry; + + OUStringBuffer aEntry(rEntry); + for (sal_Int32 i = 0; i < rEntry.getLength(); ++i) + if(aEntry[i] == 10 || aEntry[i] == 9) + aEntry[i] = 0x20; + + return aEntry.makeStringAndClear(); +} + +// Execution of the drag operation with and without the children. + +void SwNavigationPI::MoveOutline(SwOutlineNodes::size_type nSource, SwOutlineNodes::size_type nTarget) +{ + SwView *pView = GetCreateView(); + SwWrtShell &rSh = pView->GetWrtShell(); + if(nTarget < nSource || nTarget == SwOutlineNodes::npos) + nTarget ++; + if ( rSh.IsOutlineMovable( nSource )) + { + + SwOutlineNodes::difference_type nMove = nTarget-nSource; //( nDir<0 ) ? 1 : 0 ; + rSh.GotoOutline(nSource); + rSh.MakeOutlineSel(nSource, nSource, true); + // While moving, the selected children does not counting. + const SwOutlineNodes::size_type nLastOutlinePos = rSh.GetOutlinePos(MAXLEVEL); + if(nMove > 1 && nLastOutlinePos < nTarget) + { + if(!rSh.IsCursorPtAtEnd()) + rSh.SwapPam(); + nMove -= nLastOutlinePos - nSource; + } + if( nMove < 1 || nLastOutlinePos < nTarget ) + rSh.MoveOutlinePara( nMove ); + rSh.ClearMark(); + rSh.GotoOutline( nSource + nMove); + } + +} + +// After goto cancel the status frame selection +static void lcl_UnSelectFrame(SwWrtShell *pSh) +{ + if (pSh->IsFrameSelected()) + { + pSh->UnSelectFrame(); + pSh->LeaveSelFrameMode(); + } +} + +// Select the document view +IMPL_LINK(SwNavigationPI, DocListBoxSelectHdl, weld::ComboBox&, rBox, void) +{ + int nEntryIdx = rBox.get_active(); + SwView *pView ; + pView = SwModule::GetFirstView(); + while (nEntryIdx-- && pView) + { + pView = SwModule::GetNextView(pView); + } + if(!pView) + { + nEntryIdx == 0 ? + m_xContentTree->ShowHiddenShell(): + m_xContentTree->ShowActualView(); + + } + else + { + m_xContentTree->SetConstantShell(pView->GetWrtShellPtr()); + } +} + +// Filling of the list box for outline view or documents +// The PI will be set to full size +void SwNavigationPI::FillBox() +{ + if(m_pContentWrtShell) + { + m_xContentTree->SetHiddenShell( m_pContentWrtShell ); + m_xContentTree->Display( false ); + } + else + { + SwView *pView = GetCreateView(); + if(!pView) + { + m_xContentTree->SetActiveShell(nullptr); + } + else if( pView != m_pActContView) + { + SwWrtShell* pWrtShell = pView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + } + else + m_xContentTree->Display( true ); + m_pActContView = pView; + } +} + +void SwNavigationPI::UsePage() +{ + SwView *pView = GetCreateView(); + SwWrtShell *pSh = pView ? &pView->GetWrtShell() : nullptr; + m_xEdit->set_value(1); + if (pSh) + { + const sal_uInt16 nPageCnt = pSh->GetPageCnt(); + sal_uInt16 nPhyPage, nVirPage; + pSh->GetPageNum(nPhyPage, nVirPage); + + m_xEdit->set_max(nPageCnt); + m_xEdit->set_width_chars(3); + m_xEdit->set_value(nPhyPage); + } +} + +// Select handler of the toolboxes +IMPL_LINK(SwNavigationPI, ToolBoxSelectHdl, const OString&, rCommand, void) +{ + SwView *pView = GetCreateView(); + if (!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + // Get MouseModifier for Outline-Move + + int nFuncId = 0; + bool bFocusToDoc = false; + if (rCommand == ".uno:ScrollToPrevious") + { + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(FN_SCROLL_PREV, SfxCallMode::ASYNCHRON); + } + else if (rCommand == ".uno:ScrollToNext") + { + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(FN_SCROLL_NEXT, SfxCallMode::ASYNCHRON); + } + else if (rCommand == "root") + { + m_xContentTree->ToggleToRoot(); + } + else if (rCommand == "listbox") + { + if (SfxChildWindowContext::GetFloatingWindow(GetParent())) + { + if (IsZoomedIn()) + { + ZoomOut(); + } + else + { + ZoomIn(); + } + } + return; + } + // Functions that will trigger a direct action. + else if (rCommand == "footer") + { + rSh.MoveCursor(); + const FrameTypeFlags eType = rSh.GetFrameType(nullptr,false); + if (eType & FrameTypeFlags::FOOTER) + { + if (rSh.EndPg()) + nFuncId = FN_END_OF_PAGE; + } + else if (rSh.GotoFooterText()) + nFuncId = FN_TO_FOOTER; + bFocusToDoc = true; + } + else if (rCommand == "header") + { + rSh.MoveCursor(); + const FrameTypeFlags eType = rSh.GetFrameType(nullptr,false); + if (eType & FrameTypeFlags::HEADER) + { + if (rSh.SttPg()) + nFuncId = FN_START_OF_PAGE; + } + else if (rSh.GotoHeaderText()) + nFuncId = FN_TO_HEADER; + bFocusToDoc = true; + } + else if (rCommand == "anchor") + { + rSh.MoveCursor(); + const FrameTypeFlags eFrameType = rSh.GetFrameType(nullptr,false); + // Jump from the footnote to the anchor. + if (eFrameType & FrameTypeFlags::FOOTNOTE) + { + if (rSh.GotoFootnoteAnchor()) + nFuncId = FN_FOOTNOTE_TO_ANCHOR; + } + // Otherwise, jump to the first footnote text; + // go to the next footnote if this is not possible; + // if this is also not possible got to the footnote before. + else + { + if (rSh.GotoFootnoteText()) + nFuncId = FN_FOOTNOTE_TO_ANCHOR; + else if (rSh.GotoNextFootnoteAnchor()) + nFuncId = FN_NEXT_FOOTNOTE; + else if (rSh.GotoPrevFootnoteAnchor()) + nFuncId = FN_PREV_FOOTNOTE; + } + bFocusToDoc = true; + } + else if (rCommand == "reminder") + { + rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(FN_SET_REMINDER, SfxCallMode::ASYNCHRON); + } + else if (rCommand == "chapterdown" || + rCommand == "movedown" || + rCommand == "chapterup" || + rCommand == "moveup" || + rCommand == "promote" || + rCommand == "demote" || + rCommand == "edit") + { + if (IsGlobalMode()) + m_xGlobalTree->ExecCommand(rCommand); + else + { + // Standard: sublevels are taken + // do not take sublevels with Ctrl + bool bOutlineWithChildren = (KEY_MOD1 != m_xContent3ToolBox->get_modifier_state()); + m_xContentTree->ExecCommand(rCommand, bOutlineWithChildren); + } + } + else if (rCommand == "contenttoggle" || rCommand == "globaltoggle") + { + ToggleTree(); + bool bGlobalMode = IsGlobalMode(); + m_pConfig->SetGlobalActive(bGlobalMode); + m_xGlobalToolBox->set_item_active("globaltoggle", bGlobalMode); + m_xContent1ToolBox->set_item_active("contenttoggle", bGlobalMode); + } + else if (rCommand == "save") + { + bool bSave = rSh.IsGlblDocSaveLinks(); + rSh.SetGlblDocSaveLinks( !bSave ); + m_xGlobalToolBox->set_item_active(rCommand, !bSave); + } + else if (rCommand == "dragmode") + m_xContent3ToolBox->set_menu_item_active("dragmode", !m_xContent3ToolBox->get_menu_item_active("dragmode")); + else if (rCommand == "headings") + m_xContent2ToolBox->set_menu_item_active("headings", !m_xContent2ToolBox->get_menu_item_active("headings")); + else if (rCommand == "update") + m_xGlobalToolBox->set_menu_item_active("update", !m_xGlobalToolBox->get_menu_item_active("update")); + else if (rCommand == "insert") + m_xGlobalToolBox->set_menu_item_active("insert", !m_xGlobalToolBox->get_menu_item_active("insert")); + + if (nFuncId) + lcl_UnSelectFrame(&rSh); + if (bFocusToDoc) + pView->GetEditWin().GrabFocus(); +} + +// Click handler of the toolboxes +IMPL_LINK(SwNavigationPI, ToolBoxClickHdl, const OString&, rCommand, void) +{ + if (!m_xGlobalToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "update") + m_xGlobalTree->TbxMenuHdl(rCommand, *m_xUpdateMenu); + else if (rCommand == "insert") + m_xGlobalTree->TbxMenuHdl(rCommand, *m_xInsertMenu); +} + +IMPL_LINK(SwNavigationPI, ToolBox3DropdownClickHdl, const OString&, rCommand, void) +{ + if (!m_xContent3ToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "dragmode") + { + switch (m_nRegionMode) + { + case RegionMode::NONE: + m_xDragModeMenu->set_active("hyperlink", true); + break; + case RegionMode::LINK: + m_xDragModeMenu->set_active("link", true); + break; + case RegionMode::EMBEDDED: + m_xDragModeMenu->set_active("copy", true); + break; + } + } +} + +IMPL_LINK(SwNavigationPI, DropModeMenuSelectHdl, const OString&, rIdent, void) +{ + if (rIdent == "hyperlink") + SetRegionDropMode(RegionMode::NONE); + else if (rIdent == "link") + SetRegionDropMode(RegionMode::LINK); + else if (rIdent == "copy") + SetRegionDropMode(RegionMode::EMBEDDED); +} + +IMPL_LINK(SwNavigationPI, GlobalMenuSelectHdl, const OString&, rIdent, void) +{ + m_xGlobalTree->ExecuteContextMenuAction(rIdent); +} + +IMPL_LINK(SwNavigationPI, ToolBox2DropdownClickHdl, const OString&, rCommand, void) +{ + if (!m_xContent2ToolBox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "headings") + m_xHeadingsMenu->set_active(OString::number(m_xContentTree->GetOutlineLevel()), true); +} + +FactoryFunction SwNavigationPI::GetUITestFactory() const +{ + return SwNavigationPIUIObject::create; +} + +// Action-Handler Edit: +// Switches to the page if the structure view is not turned on. +bool SwNavigationPI::EditAction() +{ + SwView *pView = GetCreateView(); + if (!pView) + return false; + + if (m_aPageChgIdle.IsActive()) + m_aPageChgIdle.Stop(); + + // if the user has clicked into the document, forget about changing the page + if (pView->GetEditWin().HasFocus()) + return false; + + SwWrtShell &rSh = m_pCreateView->GetWrtShell(); + sal_uInt16 nNewPage = m_xEdit->get_value(); + + rSh.GotoPage(nNewPage, true); + m_pCreateView->GetViewFrame()->GetBindings().Invalidate(FN_STAT_PAGE); + + return true; +} + +void SwNavigationPI::ZoomOut() +{ + if (!IsZoomedIn()) + return; + SfxNavigator* pNav = dynamic_cast<SfxNavigator*>(GetParent()); + if (!pNav) + return; + m_bIsZoomedIn = false; + FillBox(); + if (IsGlobalMode()) + { + m_xGlobalBox->show(); + m_xGlobalTree->ShowTree(); + } + else + { + m_xContentBox->show(); + m_xContentTree->ShowTree(); + m_xDocListBox->show(); + } + + Size aOptimalSize(GetOptimalSize()); + Size aNewSize(pNav->GetOutputSizePixel()); + aNewSize.setHeight( m_aExpandedSize.Height() ); + pNav->SetMinOutputSizePixel(aOptimalSize); + pNav->SetOutputSizePixel(aNewSize); + + m_xContentTree->Select(); // Enable toolbox + m_pConfig->SetSmall(false); + m_xContent3ToolBox->set_item_active("listbox", true); +} + +void SwNavigationPI::ZoomIn() +{ + if (IsZoomedIn()) + return; + SfxNavigator* pNav = dynamic_cast<SfxNavigator*>(GetParent()); + if (!pNav) + return; + + m_aExpandedSize = GetSizePixel(); + + m_xContentBox->hide(); + m_xContentTree->HideTree(); + m_xGlobalBox->hide(); + m_xGlobalTree->HideTree(); + m_xDocListBox->hide(); + m_bIsZoomedIn = true; + + Size aOptimalSize(GetOptimalSize()); + Size aNewSize(pNav->GetOutputSizePixel()); + aNewSize.setHeight( aOptimalSize.Height() ); + pNav->SetMinOutputSizePixel(aOptimalSize); + pNav->SetOutputSizePixel(aNewSize); + + m_xContentTree->Select(); // Enable toolbox + + m_pConfig->SetSmall(true); + m_xContent3ToolBox->set_item_active("listbox", false); +} + +namespace { + +enum StatusIndex +{ + IDX_STR_HIDDEN = 0, + IDX_STR_ACTIVE = 1, + IDX_STR_INACTIVE = 2 +}; + +} + +VclPtr<vcl::Window> SwNavigationPI::Create(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) +{ + if( pParent == nullptr ) + throw css::lang::IllegalArgumentException("no parent window given to SwNavigationPI::Create", nullptr, 0); + if( !rxFrame.is() ) + throw css::lang::IllegalArgumentException("no XFrame given to SwNavigationPI::Create", nullptr, 0); + if( pBindings == nullptr ) + throw css::lang::IllegalArgumentException("no SfxBindings given to SwNavigationPI::Create", nullptr, 0); + return VclPtr<SwNavigationPI>::Create(pParent, rxFrame, pBindings); +} + +SwNavigationPI::SwNavigationPI(vcl::Window* pParent, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* _pBindings) + : PanelLayout(pParent, "NavigatorPanel", "modules/swriter/ui/navigatorpanel.ui", rxFrame) + , m_aDocFullName(SID_DOCFULLNAME, *_pBindings, *this) + , m_aPageStats(FN_STAT_PAGE, *_pBindings, *this) + , m_xContent1ToolBox(m_xBuilder->weld_toolbar("content1")) + , m_xContent2ToolBox(m_xBuilder->weld_toolbar("content2")) + , m_xContent3ToolBox(m_xBuilder->weld_toolbar("content3")) + , m_xContent1Dispatch(new ToolbarUnoDispatcher(*m_xContent1ToolBox, *m_xBuilder, rxFrame)) + , m_xHeadingsMenu(m_xBuilder->weld_menu("headingsmenu")) + , m_xDragModeMenu(m_xBuilder->weld_menu("dragmodemenu")) + , m_xUpdateMenu(m_xBuilder->weld_menu("updatemenu")) + , m_xInsertMenu(m_xBuilder->weld_menu("insertmenu")) + , m_xGlobalToolBox(m_xBuilder->weld_toolbar("global")) + , m_xEdit(m_xBuilder->weld_spin_button("spinbutton")) + , m_xContentBox(m_xBuilder->weld_widget("contentbox")) + , m_xContentTree(new SwContentTree(m_xBuilder->weld_tree_view("contenttree"), this)) + , m_xGlobalBox(m_xBuilder->weld_widget("globalbox")) + , m_xGlobalTree(new SwGlobalTree(m_xBuilder->weld_tree_view("globaltree"), this)) + , m_xDocListBox(m_xBuilder->weld_combo_box("documents")) + , m_pContentView(nullptr) + , m_pContentWrtShell(nullptr) + , m_pActContView(nullptr) + , m_pCreateView(nullptr) + , m_pConfig(SW_MOD()->GetNavigationConfig()) + , m_rBindings(*_pBindings) + , m_nRegionMode(RegionMode::NONE) + , m_bIsZoomedIn(false) + , m_bGlobalMode(false) +{ + set_id("NavigatorPanelParent"); // for uitest/writer_tests5/tdf114724.py + + GetCreateView(); + + m_xContent1ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent2ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xContent3ToolBox->set_help_id(HID_NAVIGATOR_TOOLBOX); + m_xGlobalToolBox->set_help_id(HID_NAVIGATOR_GLOBAL_TOOLBOX); + m_xDocListBox->set_help_id(HID_NAVIGATOR_LISTBOX); + m_xDocListBox->set_size_request(42, -1); // set a nominal width so it takes width of surroundings + + // Insert the numeric field in the toolbox. + m_xEdit->set_accessible_name(m_xEdit->get_tooltip_text()); + m_xEdit->set_width_chars(3); + m_xEdit->connect_activate(LINK(this, SwNavigationPI, EditActionHdl)); + m_xEdit->connect_value_changed(LINK(this, SwNavigationPI, PageEditModifyHdl)); + m_xEdit->set_help_id("modules/swriter/ui/navigatorpanel/numericfield"); + + if (!IsGlobalDoc()) + { + m_xContent1ToolBox->set_item_visible("contenttoggle", false); + } + + const char* REGIONNAME_ARY[] = + { + STR_HYPERLINK, + STR_LINK_REGION, + STR_COPY_REGION + }; + + const char* REGIONMODE_ARY[] = + { + STR_HIDDEN, + STR_ACTIVE, + STR_INACTIVE + }; + + static_assert(SAL_N_ELEMENTS(REGIONNAME_ARY) == SAL_N_ELEMENTS(REGIONMODE_ARY), "### unexpected size!"); + static_assert(SAL_N_ELEMENTS(REGIONNAME_ARY) == static_cast<sal_uInt16>(RegionMode::EMBEDDED) + 1, "### unexpected size!"); + + for (sal_uInt16 i = 0; i <= static_cast<sal_uInt16>(RegionMode::EMBEDDED); ++i) + { + m_aStatusArr[i] = SwResId(REGIONMODE_ARY[i]); + } + + m_aStatusArr[3] = SwResId(STR_ACTIVE_VIEW); + + bool bFloatingNavigator = SfxChildWindowContext::GetFloatingWindow(GetParent()) != nullptr; + + m_xContentTree->set_selection_mode(SelectionMode::Single); + m_xContentTree->ShowTree(); + m_xContent3ToolBox->set_item_active("listbox", true); + m_xContent3ToolBox->set_item_sensitive("listbox", bFloatingNavigator); + +// TreeListBox for global document + m_xGlobalTree->set_selection_mode(SelectionMode::Multiple); + +// Handler + Link<const OString&, void> aLk = LINK(this, SwNavigationPI, ToolBoxSelectHdl); + m_xContent1ToolBox->connect_clicked(aLk); + m_xContent2ToolBox->connect_clicked(aLk); + m_xContent3ToolBox->connect_clicked(aLk); + m_xGlobalToolBox->connect_clicked(aLk); + m_xDocListBox->connect_changed(LINK(this, SwNavigationPI, DocListBoxSelectHdl)); + m_xContent2ToolBox->set_item_menu("headings", m_xHeadingsMenu.get()); + m_xHeadingsMenu->connect_activate(LINK(this, SwNavigationPI, HeadingsMenuSelectHdl)); + m_xContent2ToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBox2DropdownClickHdl)); + m_xContent3ToolBox->set_item_menu("dragmode", m_xDragModeMenu.get()); + m_xDragModeMenu->connect_activate(LINK(this, SwNavigationPI, DropModeMenuSelectHdl)); + m_xContent3ToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBox3DropdownClickHdl)); + m_xGlobalToolBox->set_item_menu("update", m_xUpdateMenu.get()); + m_xUpdateMenu->connect_activate(LINK(this, SwNavigationPI, GlobalMenuSelectHdl)); + m_xGlobalToolBox->set_item_menu("insert", m_xInsertMenu.get()); + m_xInsertMenu->connect_activate(LINK(this, SwNavigationPI, GlobalMenuSelectHdl)); + m_xGlobalToolBox->connect_menu_toggled(LINK(this, SwNavigationPI, ToolBoxClickHdl)); + m_xGlobalToolBox->set_item_active("globaltoggle", true); + +// set toolbar of both modes to widest of each + m_xGlobalToolBox->set_size_request(m_xContent1ToolBox->get_preferred_size().Width(), -1); + + StartListening(*SfxGetpApp()); + + if(IsGlobalDoc()) + { + SwView *pActView = GetCreateView(); + m_xGlobalToolBox->set_item_active("save", + pActView->GetWrtShellPtr()->IsGlblDocSaveLinks()); + if (m_pConfig->IsGlobalActive()) + ToggleTree(); + if (bFloatingNavigator) + m_xGlobalTree->grab_focus(); + } + else if (bFloatingNavigator) + m_xContentTree->grab_focus(); + UsePage(); + m_aPageChgIdle.SetInvokeHandler(LINK(this, SwNavigationPI, ChangePageHdl)); + m_aPageChgIdle.SetPriority(TaskPriority::LOWEST); + + m_xContentTree->set_accessible_name(SwResId(STR_ACCESS_TL_CONTENT)); + m_xGlobalTree->set_accessible_name(SwResId(STR_ACCESS_TL_GLOBAL)); + m_xDocListBox->set_accessible_name(m_aStatusArr[3]); + + m_aExpandedSize = GetOptimalSize(); +} + +SwNavigationPI::~SwNavigationPI() +{ + disposeOnce(); +} + +void SwNavigationPI::dispose() +{ + if (IsGlobalDoc() && !IsGlobalMode()) + { + SwView *pView = GetCreateView(); + SwWrtShell &rSh = pView->GetWrtShell(); + if (!rSh.IsAllProtect()) + pView->GetDocShell()->SetReadOnlyUI(false); + } + + EndListening(*SfxGetpApp()); + + if (m_pxObjectShell) + { + if (m_pxObjectShell->Is()) + (*m_pxObjectShell)->DoClose(); + m_pxObjectShell.reset(); + } + + m_xDocListBox.reset(); + m_xGlobalTree.reset(); + m_xGlobalBox.reset(); + m_xContentTree.reset(); + m_xContentBox.reset(); + m_xGlobalToolBox.reset(); + m_xEdit.reset(); + m_xHeadingsMenu.reset(); + m_xDragModeMenu.reset(); + m_xUpdateMenu.reset(); + m_xInsertMenu.reset(); + m_xContent1Dispatch.reset(); + m_xContent1ToolBox.reset(); + m_xContent2ToolBox.reset(); + m_xContent3ToolBox.reset(); + + m_aPageChgIdle.Stop(); + + m_aDocFullName.dispose(); + m_aPageStats.dispose(); + + PanelLayout::dispose(); +} + +void SwNavigationPI::NotifyItemUpdate(sal_uInt16 nSID, SfxItemState /*eState*/, + const SfxPoolItem* /*pState*/) +{ + if (nSID == SID_DOCFULLNAME) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell* pWrtShell = pActView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + bool bGlobal = IsGlobalDoc(); + m_xContent1ToolBox->set_item_visible("contenttoggle", bGlobal); + if ((!bGlobal && IsGlobalMode()) || (!IsGlobalMode() && m_pConfig->IsGlobalActive())) + { + ToggleTree(); + } + if (bGlobal) + { + m_xGlobalToolBox->set_item_active("save", pWrtShell->IsGlblDocSaveLinks()); + } + } + else + { + m_xContentTree->SetActiveShell(nullptr); + } + UpdateListBox(); + } + else if (nSID == FN_STAT_PAGE) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell &rSh = pActView->GetWrtShell(); + m_xEdit->set_max(rSh.GetPageCnt()); + m_xEdit->set_width_chars(3); + } + } +} + +void SwNavigationPI::StateChanged(StateChangedType nStateChange) +{ + PanelLayout::StateChanged(nStateChange); + if (nStateChange == StateChangedType::InitShow) + { + // if the parent isn't a float, then the navigator is displayed in + // the sidebar or is otherwise docked. While the navigator could change + // its size, the sidebar can not, and the navigator would just waste + // space. Therefore disable this button. + m_xContent3ToolBox->set_item_sensitive("listbox", SfxChildWindowContext::GetFloatingWindow(GetParent()) != nullptr); + // show content if docked + if (SfxChildWindowContext::GetFloatingWindow(GetParent()) == nullptr && IsZoomedIn()) + ZoomOut(); + } + else if (nStateChange == StateChangedType::ControlFocus) + { + if (m_xContentTree) + { + // update documents listbox + UpdateListBox(); + } + } +} + +// Notification on modified DocInfo +void SwNavigationPI::Notify( SfxBroadcaster& rBrdc, const SfxHint& rHint ) +{ + if(&rBrdc == m_pCreateView) + { + if (rHint.GetId() == SfxHintId::Dying) + { + EndListening(*m_pCreateView); + m_pCreateView = nullptr; + } + } + else + { + if (const SfxEventHint* pHint = dynamic_cast<const SfxEventHint*>(&rHint)) + { + SfxEventHintId eEventId = pHint->GetEventId(); + if (eEventId == SfxEventHintId::OpenDoc) + { + SwView *pActView = GetCreateView(); + if(pActView) + { + SwWrtShell* pWrtShell = pActView->GetWrtShellPtr(); + m_xContentTree->SetActiveShell(pWrtShell); + if (m_xGlobalTree->get_visible()) + { + bool bUpdateAll = m_xGlobalTree->Update(false); + // If no update is needed, then update the font colors + // at the entries of broken links. + m_xGlobalTree->Display(!bUpdateAll); + } + } + } + } + } +} + +IMPL_LINK( SwNavigationPI, HeadingsMenuSelectHdl, const OString&, rMenuId, void ) +{ + if (!rMenuId.isEmpty()) + m_xContentTree->SetOutlineLevel(rMenuId.toUInt32()); +} + +void SwNavigationPI::UpdateListBox() +{ + if (isDisposed()) + return; + + m_xDocListBox->freeze(); + m_xDocListBox->clear(); + SwView *pActView = GetCreateView(); + bool bDisable = pActView == nullptr; + SwView *pView = SwModule::GetFirstView(); + sal_Int32 nCount = 0; + sal_Int32 nAct = 0; + sal_Int32 nConstPos = 0; + const SwView* pConstView = m_xContentTree->IsConstantView() && + m_xContentTree->GetActiveWrtShell() ? + &m_xContentTree->GetActiveWrtShell()->GetView(): + nullptr; + while (pView) + { + SfxObjectShell* pDoc = pView->GetDocShell(); + // #i53333# don't show help pages here + if ( !pDoc->IsHelpDocument() ) + { + OUString sEntry = pDoc->GetTitle() + " ("; + if (pView == pActView) + { + nAct = nCount; + sEntry += m_aStatusArr[IDX_STR_ACTIVE]; + } + else + sEntry += m_aStatusArr[IDX_STR_INACTIVE]; + sEntry += ")"; + m_xDocListBox->append_text(sEntry); + + if (pConstView && pView == pConstView) + nConstPos = nCount; + + nCount++; + } + pView = SwModule::GetNextView(pView); + } + m_xDocListBox->append_text(m_aStatusArr[3]); // "Active Window" + nCount++; + + if(m_xContentTree->GetHiddenWrtShell()) + { + OUString sEntry = m_xContentTree->GetHiddenWrtShell()->GetView(). + GetDocShell()->GetTitle() + + " (" + + m_aStatusArr[IDX_STR_HIDDEN] + + ")"; + m_xDocListBox->append_text(sEntry); + bDisable = false; + } + + m_xDocListBox->thaw(); + + if(m_xContentTree->IsActiveView()) + { + //Either the name of the current Document or "Active Document". + m_xDocListBox->set_active(pActView ? nAct : --nCount); + } + else if(m_xContentTree->IsHiddenView()) + { + m_xDocListBox->set_active(nCount); + } + else + m_xDocListBox->set_active(nConstPos); + + m_xDocListBox->set_sensitive(!bDisable); +} + +IMPL_LINK(SwNavigationPI, DoneLink, SfxPoolItem const *, pItem, void) +{ + const SfxViewFrameItem* pFrameItem = dynamic_cast<SfxViewFrameItem const *>( pItem ); + if( pFrameItem ) + { + SfxViewFrame* pFrame = pFrameItem->GetFrame(); + if(pFrame) + { + m_xContentTree->clear(); + m_pContentView = dynamic_cast<SwView*>( pFrame->GetViewShell() ); + OSL_ENSURE(m_pContentView, "no SwView"); + if(m_pContentView) + m_pContentWrtShell = m_pContentView->GetWrtShellPtr(); + else + m_pContentWrtShell = nullptr; + m_pxObjectShell.reset( new SfxObjectShellLock(pFrame->GetObjectShell()) ); + FillBox(); + } + } +} + +OUString SwNavigationPI::CreateDropFileName( TransferableDataHelper& rData ) +{ + OUString sFileName; + SotClipboardFormatId nFormat; + if( rData.HasFormat( nFormat = SotClipboardFormatId::FILE_LIST )) + { + FileList aFileList; + rData.GetFileList( nFormat, aFileList ); + sFileName = aFileList.GetFile( 0 ); + } + else if( rData.HasFormat( nFormat = SotClipboardFormatId::STRING ) || + rData.HasFormat( nFormat = SotClipboardFormatId::SIMPLE_FILE ) || + rData.HasFormat( nFormat = SotClipboardFormatId::FILENAME )) + { + (void)rData.GetString(nFormat, sFileName); + } + else if( rData.HasFormat( nFormat = SotClipboardFormatId::SOLK ) || + rData.HasFormat( nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + rData.HasFormat( nFormat = SotClipboardFormatId::FILECONTENT ) || + rData.HasFormat( nFormat = SotClipboardFormatId::FILEGRPDESCRIPTOR ) || + rData.HasFormat( nFormat = SotClipboardFormatId::UNIFORMRESOURCELOCATOR )) + { + INetBookmark aBkmk { OUString(), OUString() }; + if (rData.GetINetBookmark(nFormat, aBkmk)) + sFileName = aBkmk.GetURL(); + } + if( !sFileName.isEmpty() ) + { + sFileName = INetURLObject( sFileName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + return sFileName; +} + +sal_Int8 SwNavigationPI::AcceptDrop() +{ + return ( !m_xContentTree->IsInDrag() && + ( m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::STRING ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::SOLK ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK )|| + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILECONTENT ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) || + m_xContentTree->IsDropFormatSupported( SotClipboardFormatId::FILENAME ))) + ? DND_ACTION_COPY + : DND_ACTION_NONE; +} + +sal_Int8 SwNavigationPI::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); + sal_Int8 nRet = DND_ACTION_NONE; + if (m_xContentTree->IsInDrag()) + return nRet; + + OUString sFileName = SwNavigationPI::CreateDropFileName(aData); + if (sFileName.isEmpty()) + return nRet; + + INetURLObject aTemp(sFileName); + GraphicDescriptor aDesc(aTemp); + if (aDesc.Detect()) // accept no graphics + return nRet; + + if (-1 != sFileName.indexOf('#')) + return nRet; + + if (m_sContentFileName.isEmpty() || m_sContentFileName != sFileName) + { + nRet = rEvt.mnAction; + sFileName = comphelper::string::stripEnd(sFileName, 0); + m_sContentFileName = sFileName; + if(m_pxObjectShell) + { + m_xContentTree->SetHiddenShell( nullptr ); + (*m_pxObjectShell)->DoClose(); + m_pxObjectShell.reset(); + } + SfxStringItem aFileItem(SID_FILE_NAME, sFileName ); + SfxStringItem aOptionsItem( SID_OPTIONS, "HRC" ); + SfxLinkItem aLink( SID_DONELINK, + LINK( this, SwNavigationPI, DoneLink ) ); + GetActiveView()->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON, + { &aFileItem, &aOptionsItem, &aLink }); + } + return nRet; +} + +void SwNavigationPI::SetRegionDropMode(RegionMode nNewMode) +{ + m_nRegionMode = nNewMode; + m_pConfig->SetRegionMode( m_nRegionMode ); + + OUString sImageId; + switch (nNewMode) + { + case RegionMode::NONE: + sImageId = RID_BMP_DROP_REGION; + break; + case RegionMode::LINK: + sImageId = RID_BMP_DROP_LINK; + break; + case RegionMode::EMBEDDED: + sImageId = RID_BMP_DROP_COPY; + break; + } + m_xContent3ToolBox->set_item_icon_name("dragmode", sImageId); +} + +void SwNavigationPI::ToggleTree() +{ + bool bGlobalDoc = IsGlobalDoc(); + if (!IsGlobalMode() && bGlobalDoc) + { + SetUpdateMode(false); + if (IsZoomedIn()) + ZoomOut(); + m_xGlobalBox->show(); + m_xGlobalTree->ShowTree(); + m_xGlobalToolBox->show(); + m_xContentBox->hide(); + m_xContentTree->HideTree(); + m_xContent1ToolBox->hide(); + m_xContent2ToolBox->hide(); + m_xContent3ToolBox->hide(); + m_xDocListBox->hide(); + SetGlobalMode(true); + SetUpdateMode(true); + } + else + { + m_xGlobalBox->hide(); + m_xGlobalTree->HideTree(); + m_xGlobalToolBox->hide(); + if (!IsZoomedIn()) + { + m_xContentBox->show(); + m_xContentTree->ShowTree(); + m_xContent1ToolBox->show(); + m_xContent2ToolBox->show(); + m_xContent3ToolBox->show(); + m_xDocListBox->show(); + } + SetGlobalMode(false); + } +} + +bool SwNavigationPI::IsGlobalDoc() const +{ + bool bRet = false; + SwView *pView = GetCreateView(); + if (pView) + { + SwWrtShell &rSh = pView->GetWrtShell(); + bRet = rSh.IsGlobalDoc(); + } + return bRet; +} + +IMPL_LINK_NOARG(SwNavigationPI, ChangePageHdl, Timer *, void) +{ + if (IsDisposed()) + return; + // tdf#134959 if the SpinButton changed value this Timer was launched, now + // change to the desired page, but we leave focus where it currently is, + // i.e. typically remaining in the spinbutton, or whatever other widget the + // user moved to in the meantime + EditAction(); +} + +IMPL_LINK_NOARG(SwNavigationPI, EditActionHdl, weld::Entry&, bool) +{ + // tdf#134959 if the user presses enter to activate the Entry + // go to the page, and on success we move focus to the document + if (EditAction()) + m_pCreateView->GetEditWin().GrabFocus(); + return true; +} + +IMPL_LINK_NOARG(SwNavigationPI, PageEditModifyHdl, weld::SpinButton&, void) +{ + if (m_aPageChgIdle.IsActive()) + m_aPageChgIdle.Stop(); + m_aPageChgIdle.Start(); +} + +SwView* SwNavigationPI::GetCreateView() const +{ + if (!m_pCreateView) + { + SwView* pView = SwModule::GetFirstView(); + while (pView) + { + if(&pView->GetViewFrame()->GetBindings() == &m_rBindings) + { + const_cast<SwNavigationPI*>(this)->m_pCreateView = pView; + const_cast<SwNavigationPI*>(this)->StartListening(*m_pCreateView); + break; + } + pView = SwModule::GetNextView(pView); + } + } + return m_pCreateView; +} + +SwNavigationChild::SwNavigationChild( vcl::Window* pParent, + sal_uInt16 nId, + SfxBindings* _pBindings ) + : SfxChildWindowContext( nId ) +{ + Reference< XFrame > xFrame = _pBindings->GetActiveFrame(); + VclPtr< SwNavigationPI > pNavi = VclPtr< SwNavigationPI >::Create( pParent, xFrame, _pBindings ); + _pBindings->Invalidate(SID_NAVIGATOR); + + SwNavigationConfig* pNaviConfig = SW_MOD()->GetNavigationConfig(); + + const ContentTypeId nRootType = pNaviConfig->GetRootType(); + if( nRootType != ContentTypeId::UNKNOWN ) + { + pNavi->m_xContentTree->SetRootType(nRootType); + pNavi->m_xContent2ToolBox->set_item_active("root", true); + if (nRootType == ContentTypeId::OUTLINE) + { + pNavi->m_xContentTree->set_selection_mode(SelectionMode::Multiple); + } + } + pNavi->m_xContentTree->SetOutlineLevel( static_cast< sal_uInt8 >( pNaviConfig->GetOutlineLevel() ) ); + pNavi->SetRegionDropMode( pNaviConfig->GetRegionMode() ); + + if (SfxNavigator* pNav = dynamic_cast<SfxNavigator*>(pParent)) + { + pNav->SetMinOutputSizePixel(pNavi->GetOptimalSize()); + if (pNaviConfig->IsSmall()) + pNavi->ZoomIn(); + } + + SetWindow(pNavi); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/numfmtlb.cxx b/sw/source/uibase/utlui/numfmtlb.cxx new file mode 100644 index 000000000..4b4b6acb7 --- /dev/null +++ b/sw/source/uibase/utlui/numfmtlb.cxx @@ -0,0 +1,473 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <svl/zformat.hxx> +#include <svl/eitem.hxx> +#include <svx/svxids.hrc> +#include <svx/numinf.hxx> +#include <svx/flagsdef.hxx> +#include <svl/itemset.hxx> +#include <docsh.hxx> +#include <swtypes.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <numfmtlb.hxx> +#include <strings.hrc> +#include <swabstdlg.hxx> +#include <memory> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +/** + * Description: + * nFormatType: Display the formats of this Type + * nDefaultFormat: Select this format and possibly insert it + */ + +namespace +{ + +bool lcl_isSystemFormat(sal_uInt32 nDefaultFormat, SvNumberFormatter* pFormatter, LanguageType eCurLanguage) +{ + const sal_uInt32 nSysNumFormat = pFormatter->GetFormatIndex(NF_NUMBER_SYSTEM, eCurLanguage); + if (nDefaultFormat == nSysNumFormat) + return true; + const sal_uInt32 nSysShortDateFormat = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, eCurLanguage); + if (nDefaultFormat == nSysShortDateFormat) + return true; + const sal_uInt32 nSysLongDateFormat = pFormatter->GetFormatIndex(NF_DATE_SYSTEM_LONG, eCurLanguage); + if (nDefaultFormat == nSysLongDateFormat) + return true; + + if ( eCurLanguage != GetAppLanguage() ) + return false; + + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysNumFormat, LANGUAGE_SYSTEM)) + return true; + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysShortDateFormat, LANGUAGE_SYSTEM)) + return true; + if (nDefaultFormat == pFormatter->GetFormatForLanguageIfBuiltIn(nSysLongDateFormat, LANGUAGE_SYSTEM)) + return true; + + return false; +} + +} + +double SwNumFormatBase::GetDefValue(const SvNumFormatType nFormatType) +{ + SvxNumValCategory nDefValue = SvxNumValCategory::Standard; + + switch (nFormatType) + { + case SvNumFormatType::DATE: + case SvNumFormatType::DATE|SvNumFormatType::TIME: + nDefValue = SvxNumValCategory::Date; + break; + + case SvNumFormatType::TIME: + nDefValue = SvxNumValCategory::Time; + break; + + case SvNumFormatType::TEXT: + case SvNumFormatType::UNDEFINED: + nDefValue = SvxNumValCategory::Standard; + break; + + case SvNumFormatType::CURRENCY: + nDefValue = SvxNumValCategory::Currency; + break; + + case SvNumFormatType::PERCENT: + nDefValue = SvxNumValCategory::Percent; + break; + + case SvNumFormatType::LOGICAL: + nDefValue = SvxNumValCategory::Boolean; + break; + + default: + nDefValue = SvxNumValCategory::Standard; + break; + } + + return fSvxNumValConst[nDefValue]; +} + +SwNumFormatBase::SwNumFormatBase() + : nCurrFormatType(SvNumFormatType::ALL) + , mbCurrFormatTypeNeedsInit(true) + , nStdEntry(0) + , bOneArea(false) + , nDefFormat(0) + , bShowLanguageControl(false) + , bUseAutomaticLanguage(true) +{ +} + +NumFormatListBox::NumFormatListBox(std::unique_ptr<weld::ComboBox> xControl) + : mxControl(std::move(xControl)) +{ + Init(); +} + +SwNumFormatTreeView::SwNumFormatTreeView(std::unique_ptr<weld::TreeView> xControl) + : mxControl(std::move(xControl)) +{ + Init(); +} + +void SwNumFormatBase::Init() +{ + SwView *pView = GetActiveView(); + + if (pView) + eCurLanguage = pView->GetWrtShell().GetCurLang(); + else + eCurLanguage = SvtSysLocale().GetLanguageTag().getLanguageType(); + + SetFormatType(SvNumFormatType::NUMBER); + SetDefFormat(nDefFormat); +} + +void NumFormatListBox::Init() +{ + SwNumFormatBase::Init(); + + mxControl->connect_changed(LINK(this, NumFormatListBox, SelectHdl)); +} + +void SwNumFormatTreeView::Init() +{ + SwNumFormatBase::Init(); + + mxControl->connect_changed(LINK(this, SwNumFormatTreeView, SelectHdl)); +} + +void SwNumFormatBase::SetFormatType(const SvNumFormatType nFormatType) +{ + if (!mbCurrFormatTypeNeedsInit && + (nCurrFormatType & nFormatType)) // there are mixed formats, like for example DateTime + return; + + SwView *pView = GetActiveView(); + OSL_ENSURE(pView, "no view found"); + if(!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + clear(); // Remove all entries from the Listbox + + NfIndexTableOffset eOffsetStart = NF_NUMBER_START; + NfIndexTableOffset eOffsetEnd = NF_NUMBER_START; + + switch( nFormatType ) + { + case SvNumFormatType::NUMBER: + eOffsetStart=NF_NUMBER_START; + eOffsetEnd=NF_NUMBER_END; + break; + + case SvNumFormatType::PERCENT: + eOffsetStart=NF_PERCENT_START; + eOffsetEnd=NF_PERCENT_END; + break; + + case SvNumFormatType::CURRENCY: + eOffsetStart=NF_CURRENCY_START; + eOffsetEnd=NF_CURRENCY_END; + break; + + case SvNumFormatType::DATETIME: + eOffsetStart=NF_DATE_START; + eOffsetEnd=NF_TIME_END; + break; + + case SvNumFormatType::DATE: + eOffsetStart=NF_DATE_START; + eOffsetEnd=NF_DATE_END; + break; + + case SvNumFormatType::TIME: + eOffsetStart=NF_TIME_START; + eOffsetEnd=NF_TIME_END; + break; + + case SvNumFormatType::SCIENTIFIC: + eOffsetStart=NF_SCIENTIFIC_START; + eOffsetEnd=NF_SCIENTIFIC_END; + break; + + case SvNumFormatType::FRACTION: + eOffsetStart=NF_FRACTION_START; + eOffsetEnd=NF_FRACTION_END; + break; + + case SvNumFormatType::LOGICAL: + eOffsetStart=NF_BOOLEAN; + eOffsetEnd=NF_BOOLEAN; + break; + + case SvNumFormatType::TEXT: + eOffsetStart=NF_TEXT; + eOffsetEnd=NF_TEXT; + break; + + case SvNumFormatType::ALL: + eOffsetStart=NF_NUMERIC_START; + eOffsetEnd = NfIndexTableOffset( NF_INDEX_TABLE_ENTRIES - 1 ); + break; + + default: + OSL_FAIL("what a format?"); + break; + } + + const SvNumberformat* pFormat; + sal_Int32 i = 0; + Color* pCol; + double fVal = SwNumFormatBase::GetDefValue(nFormatType); + OUString sValue; + + const sal_uInt32 nSysNumFormat = pFormatter->GetFormatIndex( + NF_NUMBER_SYSTEM, eCurLanguage ); + const sal_uInt32 nSysShortDateFormat = pFormatter->GetFormatIndex( + NF_DATE_SYSTEM_SHORT, eCurLanguage ); + const sal_uInt32 nSysLongDateFormat = pFormatter->GetFormatIndex( + NF_DATE_SYSTEM_LONG, eCurLanguage ); + + for( long nIndex = eOffsetStart; nIndex <= eOffsetEnd; ++nIndex ) + { + const sal_uInt32 nFormat = pFormatter->GetFormatIndex( + static_cast<NfIndexTableOffset>(nIndex), eCurLanguage ); + pFormat = pFormatter->GetEntry( nFormat ); + + if( nFormat == pFormatter->GetFormatIndex( NF_NUMBER_STANDARD, + eCurLanguage ) + || const_cast<SvNumberformat*>(pFormat)->GetOutputString( fVal, sValue, &pCol ) + || nFormatType == SvNumFormatType::UNDEFINED ) + { + sValue = pFormat->GetFormatstring(); + } + else if( nFormatType == SvNumFormatType::TEXT ) + { + pFormatter->GetOutputString( "\"ABC\"", nFormat, sValue, &pCol); + } + + if (nFormat != nSysNumFormat && + nFormat != nSysShortDateFormat && + nFormat != nSysLongDateFormat) + { + append(OUString::number(nFormat), sValue); + + if( nFormat == pFormatter->GetStandardFormat( + nFormatType, eCurLanguage ) ) + nStdEntry = i; + ++i; + } + } + + append_text(SwResId(STR_DEFINE_NUMBERFORMAT)); + + set_active(nStdEntry); + + nCurrFormatType = nFormatType; + mbCurrFormatTypeNeedsInit = false; + +} + +void SwNumFormatBase::SetDefFormat(const sal_uInt32 nDefaultFormat) +{ + if (nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + nDefFormat = nDefaultFormat; + return; + } + + SwView *pView = GetActiveView(); + OSL_ENSURE(pView, "no view found"); + if(!pView) + return; + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + SvNumFormatType nType = pFormatter->GetType(nDefaultFormat); + + SetFormatType(nType); + + sal_uInt32 nFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nDefaultFormat, eCurLanguage); + + for (sal_Int32 i = 0, nCount = get_count(); i < nCount; ++i) + { + if (nFormat == get_id(i).toUInt32()) + { + set_active(i); + nStdEntry = i; + nDefFormat = GetFormat(); + return; + } + } + + // No entry found: + OUString sValue; + Color* pCol = nullptr; + + if (nType == SvNumFormatType::TEXT) + { + pFormatter->GetOutputString("\"ABC\"", nDefaultFormat, sValue, &pCol); + } + else + { + pFormatter->GetOutputString(SwNumFormatBase::GetDefValue(nType), nDefaultFormat, sValue, &pCol); + } + + sal_Int32 nPos = 0; + while (get_id(nPos).toUInt32() == NUMBERFORMAT_ENTRY_NOT_FOUND) + nPos++; + + if ( lcl_isSystemFormat(nDefaultFormat, pFormatter, eCurLanguage) ) + { + sValue += SwResId(RID_STR_SYSTEM); + } + + insert_text(nPos, sValue); // Insert as first numeric entry + set_id(nPos, OUString::number(nDefaultFormat)); + set_active(nPos); + nDefFormat = GetFormat(); +} + +sal_uInt32 NumFormatListBox::GetFormat() const +{ + return mxControl->get_active_id().toUInt32(); +} + +sal_uInt32 SwNumFormatTreeView::GetFormat() const +{ + return mxControl->get_selected_id().toUInt32(); +} + +void SwNumFormatBase::CallSelectHdl() +{ + const sal_Int32 nPos = get_active(); + OUString sDefine(SwResId( STR_DEFINE_NUMBERFORMAT )); + SwView *pView = GetActiveView(); + + if (!pView || nPos != get_count() - 1 || get_text(nPos) != sDefine) + return; + + SwWrtShell &rSh = pView->GetWrtShell(); + SvNumberFormatter* pFormatter = rSh.GetNumberFormatter(); + + SfxItemSet aCoreSet( + rSh.GetAttrPool(), + svl::Items< + SID_ATTR_NUMBERFORMAT_VALUE, SID_ATTR_NUMBERFORMAT_INFO, + SID_ATTR_NUMBERFORMAT_ONE_AREA, SID_ATTR_NUMBERFORMAT_ONE_AREA, + SID_ATTR_NUMBERFORMAT_NOLANGUAGE, + SID_ATTR_NUMBERFORMAT_NOLANGUAGE, + SID_ATTR_NUMBERFORMAT_ADD_AUTO, + SID_ATTR_NUMBERFORMAT_ADD_AUTO>{}); + + double fValue = SwNumFormatBase::GetDefValue(nCurrFormatType); + + sal_uInt32 nFormat = pFormatter->GetStandardFormat( nCurrFormatType, eCurLanguage); + aCoreSet.Put( SfxUInt32Item( SID_ATTR_NUMBERFORMAT_VALUE, nFormat )); + + aCoreSet.Put( SvxNumberInfoItem( pFormatter, fValue, + SID_ATTR_NUMBERFORMAT_INFO ) ); + + if( (SvNumFormatType::DATE | SvNumFormatType::TIME) & nCurrFormatType ) + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_ONE_AREA, bOneArea)); + + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_NOLANGUAGE, !bShowLanguageControl)); + aCoreSet.Put(SfxBoolItem(SID_ATTR_NUMBERFORMAT_ADD_AUTO, bUseAutomaticLanguage)); + + // force deselect to break mouse lock on selected entry + set_active(-1); + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateNumFormatDialog(&get_widget(), aCoreSet)); + + if (RET_OK == pDlg->Execute()) + { + const SfxPoolItem* pItem = pView->GetDocShell()-> + GetItem( SID_ATTR_NUMBERFORMAT_INFO ); + + if( pItem ) + { + for ( sal_uInt32 key : static_cast<const SvxNumberInfoItem*>(pItem)->GetDelFormats() ) + pFormatter->DeleteEntry( key ); + } + + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + if( SfxItemState::SET == pOutSet->GetItemState( + SID_ATTR_NUMBERFORMAT_VALUE, false, &pItem )) + { + sal_uInt32 nNumberFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + // oj #105473# change order of calls + const SvNumberformat* pFormat = pFormatter->GetEntry(nNumberFormat); + if( pFormat ) + eCurLanguage = pFormat->GetLanguage(); + // SetDefFormat uses eCurLanguage to look for if this format already in the list + SetDefFormat(nNumberFormat); + } + if( bShowLanguageControl && SfxItemState::SET == pOutSet->GetItemState( + SID_ATTR_NUMBERFORMAT_ADD_AUTO, false, &pItem )) + { + bUseAutomaticLanguage = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + } + } + else + SetDefFormat(nFormat); + +} + +IMPL_LINK_NOARG(NumFormatListBox, SelectHdl, weld::ComboBox&, void) +{ + CallSelectHdl(); +} + +IMPL_LINK_NOARG(SwNumFormatTreeView, SelectHdl, weld::TreeView&, void) +{ + CallSelectHdl(); +} + +void SwNumFormatBase::clear() +{ + mbCurrFormatTypeNeedsInit = true; + nCurrFormatType = SvNumFormatType::ALL; +} + +void NumFormatListBox::clear() +{ + mxControl->clear(); + SwNumFormatBase::clear(); +} + +void SwNumFormatTreeView::clear() +{ + mxControl->clear(); + SwNumFormatBase::clear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/prcntfld.cxx b/sw/source/uibase/utlui/prcntfld.cxx new file mode 100644 index 000000000..2cefdc4ff --- /dev/null +++ b/sw/source/uibase/utlui/prcntfld.cxx @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <prcntfld.hxx> +#include <vcl/fieldvalues.hxx> + +SwPercentField::SwPercentField(std::unique_ptr<weld::MetricSpinButton> pControl) + : m_pField(std::move(pControl)) + , nOldMax(0) + , nOldMin(0) + , nLastPercent(-1) + , nLastValue(-1) + , nOldDigits(m_pField->get_digits()) + , eOldUnit(FieldUnit::NONE) + , bLockAutoCalculation(false) +{ + int nMin, nMax; + m_pField->get_range(nMin, nMax, FieldUnit::TWIP); + nRefValue = DenormalizePercent(nMax); + m_pField->get_increments(nOldSpinSize, nOldPageSize, FieldUnit::NONE); +} + +void SwPercentField::SetRefValue(int nValue) +{ + int nRealValue = GetRealValue(eOldUnit); + + nRefValue = nValue; + + if (!bLockAutoCalculation && (m_pField->get_unit() == FieldUnit::PERCENT)) + set_value(nRealValue, eOldUnit); +} + +void SwPercentField::ShowPercent(bool bPercent) +{ + if ((bPercent && m_pField->get_unit() == FieldUnit::PERCENT) || + (!bPercent && m_pField->get_unit() != FieldUnit::PERCENT)) + return; + + int nOldValue; + + if (bPercent) + { + nOldValue = get_value(); + + eOldUnit = m_pField->get_unit(); + nOldDigits = m_pField->get_digits(); + m_pField->get_range(nOldMin, nOldMax, FieldUnit::NONE); + m_pField->get_increments(nOldSpinSize, nOldPageSize, FieldUnit::NONE); + m_pField->set_unit(FieldUnit::PERCENT); + m_pField->set_digits(0); + + int nCurrentWidth = vcl::ConvertValue(nOldMin, 0, nOldDigits, eOldUnit, FieldUnit::TWIP); + // round to 0.5 percent + int nPercent = nRefValue ? (((nCurrentWidth * 10) / nRefValue + 5) / 10) : 0; + + m_pField->set_range(std::max(1, nPercent), 100, FieldUnit::NONE); + m_pField->set_increments(5, 10, FieldUnit::NONE); + if (nOldValue != nLastValue) + { + nCurrentWidth = vcl::ConvertValue(nOldValue, 0, nOldDigits, eOldUnit, FieldUnit::TWIP); + nPercent = nRefValue ? (((nCurrentWidth * 10) / nRefValue + 5) / 10) : 0; + m_pField->set_value(nPercent, FieldUnit::NONE); + nLastPercent = nPercent; + nLastValue = nOldValue; + } + else + m_pField->set_value(nLastPercent, FieldUnit::NONE); + } + else + { + int nOldPercent = get_value(FieldUnit::PERCENT); + + nOldValue = Convert(get_value(), m_pField->get_unit(), eOldUnit); + + m_pField->set_unit(eOldUnit); + m_pField->set_digits(nOldDigits); + m_pField->set_range(nOldMin, nOldMax, FieldUnit::NONE); + m_pField->set_increments(nOldSpinSize, nOldPageSize, FieldUnit::NONE); + + if (nOldPercent != nLastPercent) + { + set_value(nOldValue, eOldUnit); + nLastPercent = nOldPercent; + nLastValue = nOldValue; + } + else + set_value(nLastValue, eOldUnit); + } +} + +void SwPercentField::set_value(int nNewValue, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT || eInUnit == FieldUnit::PERCENT) + m_pField->set_value(Convert(nNewValue, eInUnit, m_pField->get_unit()), FieldUnit::NONE); + else + { + // Overwrite output value, do not restore later + int nPercent, nCurrentWidth; + if(eInUnit == FieldUnit::TWIP) + { + nCurrentWidth = vcl::ConvertValue(nNewValue, 0, nOldDigits, FieldUnit::TWIP, FieldUnit::TWIP); + } + else + { + int nValue = Convert(nNewValue, eInUnit, eOldUnit); + nCurrentWidth = vcl::ConvertValue(nValue, 0, nOldDigits, eOldUnit, FieldUnit::TWIP); + } + nPercent = nRefValue ? (((nCurrentWidth * 10) / nRefValue + 5) / 10) : 0; + m_pField->set_value(nPercent, FieldUnit::NONE); + } +} + +int SwPercentField::get_value(FieldUnit eOutUnit) +{ + return Convert(m_pField->get_value(FieldUnit::NONE), m_pField->get_unit(), eOutUnit); +} + +void SwPercentField::set_min(int nNewMin, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + m_pField->set_min(nNewMin, eInUnit); + else + { + if (eInUnit == FieldUnit::NONE) + eInUnit = eOldUnit; + nOldMin = Convert(nNewMin, eInUnit, eOldUnit); + + int nPercent = Convert(nNewMin, eInUnit, FieldUnit::PERCENT); + m_pField->set_min(std::max(1, nPercent), FieldUnit::NONE); + } +} + +void SwPercentField::set_max(int nNewMax, FieldUnit eInUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + m_pField->set_max(nNewMax, eInUnit); +} + +int SwPercentField::NormalizePercent(int nValue) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + nValue = m_pField->normalize(nValue); + else + nValue = nValue * ImpPower10(nOldDigits); + return nValue; +} + +int SwPercentField::DenormalizePercent(int nValue) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + nValue = m_pField->denormalize(nValue); + else + { + int nFactor = ImpPower10(nOldDigits); + nValue = ((nValue+(nFactor/2)) / nFactor); + } + return nValue; +} + +int SwPercentField::ImpPower10(sal_uInt16 n) +{ + int nValue = 1; + + for (sal_uInt16 i=0; i < n; ++i) + nValue *= 10; + + return nValue; +} + +int SwPercentField::GetRealValue(FieldUnit eOutUnit) +{ + if (m_pField->get_unit() != FieldUnit::PERCENT) + return get_value(eOutUnit); + else + return Convert(get_value(), m_pField->get_unit(), eOutUnit); +} + +int SwPercentField::Convert(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) +{ + if (eInUnit == eOutUnit || + (eInUnit == FieldUnit::NONE && eOutUnit == m_pField->get_unit()) || + (eOutUnit == FieldUnit::NONE && eInUnit == m_pField->get_unit())) + return nValue; + + if (eInUnit == FieldUnit::PERCENT) + { + // Convert to metric + int nTwipValue = (nRefValue * nValue + 50) / 100; + + if (eOutUnit == FieldUnit::TWIP) // Only convert if necessary + return NormalizePercent(nTwipValue); + else + return vcl::ConvertValue(NormalizePercent(nTwipValue), 0, nOldDigits, FieldUnit::TWIP, eOutUnit); + } + + if (eOutUnit == FieldUnit::PERCENT) + { + // Convert to percent + int nCurrentWidth; + nValue = DenormalizePercent(nValue); + + if (eInUnit == FieldUnit::TWIP) // Only convert if necessary + nCurrentWidth = nValue; + else + nCurrentWidth = vcl::ConvertValue(nValue, 0, nOldDigits, eInUnit, FieldUnit::TWIP); + // Round to 0.5 percent + return nRefValue ? (((nCurrentWidth * 1000) / nRefValue + 5) / 10) : 0; + } + + return vcl::ConvertValue(nValue, 0, nOldDigits, eInUnit, eOutUnit); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/shdwcrsr.cxx b/sw/source/uibase/utlui/shdwcrsr.cxx new file mode 100644 index 000000000..dbdf78b49 --- /dev/null +++ b/sw/source/uibase/utlui/shdwcrsr.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 <com/sun/star/text/HoriOrientation.hpp> +#include <vcl/window.hxx> +#include <shdwcrsr.hxx> + +using namespace ::com::sun::star; + +SwShadowCursor::~SwShadowCursor() +{ + if( USHRT_MAX != nOldMode ) + DrawCursor( aOldPt, nOldHeight, nOldMode ); +} + +void SwShadowCursor::SetPos( const Point& rPt, long nHeight, sal_uInt16 nMode ) +{ + Point aPt( pWin->LogicToPixel( rPt )); + nHeight = pWin->LogicToPixel( Size( 0, nHeight )).Height(); + if( aOldPt != aPt || nOldHeight != nHeight || nOldMode != nMode ) + { + if( USHRT_MAX != nOldMode ) + DrawCursor( aOldPt, nOldHeight, nOldMode ); + + DrawCursor( aPt, nHeight, nMode ); + nOldMode = nMode; + nOldHeight = nHeight; + aOldPt = aPt; + } +} + +void SwShadowCursor::DrawTri( const Point& rPt, long nHeight, bool bLeft ) +{ + long nLineDiff = nHeight / 2; + long nLineDiffHalf = nLineDiff / 2; + + // Dot above + Point aPt1( (bLeft ? rPt.X() - 3 : rPt.X() + 3), + rPt.Y() + nLineDiffHalf ); + // Dot below + Point aPt2( aPt1.X(), aPt1.Y() + nHeight - nLineDiff - 1 ); + long nDiff = bLeft ? -1 : 1; + while( aPt1.Y() <= aPt2.Y() ) + { + pWin->DrawLine( aPt1, aPt2 ); + aPt1.AdjustY( 1 ); + aPt2.AdjustY( -1 ); + aPt2.setX( aPt1.AdjustX(nDiff ) ); + } +} + +void SwShadowCursor::DrawCursor( const Point& rPt, long nHeight, sal_uInt16 nMode ) +{ + nHeight = (((nHeight / 4)+1) * 4) + 1; + + pWin->Push(); + + pWin->SetMapMode(MapMode(MapUnit::MapPixel)); + pWin->SetRasterOp( RasterOp::Xor ); + + pWin->SetLineColor( Color( sal_uInt32(aCol) ^ sal_uInt32(COL_WHITE) ) ); + + // 1. The Line: + pWin->DrawLine( Point( rPt.X(), rPt.Y() + 1), + Point( rPt.X(), rPt.Y() - 2 + nHeight )); + + // 2. The Triangle + if( text::HoriOrientation::LEFT == nMode || text::HoriOrientation::CENTER == nMode ) // Arrow to the right + DrawTri( rPt, nHeight, false ); + if( text::HoriOrientation::RIGHT == nMode || text::HoriOrientation::CENTER == nMode ) // Arrow to the left + DrawTri( rPt, nHeight, true ); + + pWin->Pop(); +} + +void SwShadowCursor::Paint() +{ + if( USHRT_MAX != nOldMode ) + DrawCursor( aOldPt, nOldHeight, nOldMode ); +} + +tools::Rectangle SwShadowCursor::GetRect() const +{ + long nH = nOldHeight; + Point aPt( aOldPt ); + + nH = (((nH / 4)+1) * 4) + 1; + long nWidth = nH / 4 + 3 + 1; + + Size aSz( nWidth, nH ); + + if( text::HoriOrientation::RIGHT == nOldMode ) + aPt.AdjustX( -(aSz.Width()) ); + else if( text::HoriOrientation::CENTER == nOldMode ) + { + aPt.AdjustX( -(aSz.Width()) ); + aSz.setWidth( aSz.Width() * 2 ); + } + + return pWin->PixelToLogic( tools::Rectangle( aPt, aSz ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/tmplctrl.cxx b/sw/source/uibase/utlui/tmplctrl.cxx new file mode 100644 index 000000000..2220c776e --- /dev/null +++ b/sw/source/uibase/utlui/tmplctrl.cxx @@ -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 . + */ + +#include <svl/style.hxx> +#include <vcl/menu.hxx> +#include <svl/stritem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/status.hxx> + +#include <swtypes.hxx> +#include <strings.hrc> + +#include <wrtsh.hxx> +#include <view.hxx> +#include <swmodule.hxx> +#include <cmdid.h> +#include <docsh.hxx> +#include <tmplctrl.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwTemplateControl, SfxStringItem ); + +namespace { + +class SwTemplatePopup_Impl : public PopupMenu +{ +public: + SwTemplatePopup_Impl(); + + sal_uInt16 GetCurId() const { return nCurId; } + +private: + sal_uInt16 nCurId; + + virtual void Select() override; +}; + +} + +SwTemplatePopup_Impl::SwTemplatePopup_Impl() : + PopupMenu(), + nCurId(USHRT_MAX) +{ +} + +void SwTemplatePopup_Impl::Select() +{ + nCurId = GetCurItemId(); +} + +SwTemplateControl::SwTemplateControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_TMPLCTRL_HINT)); +} + +SwTemplateControl::~SwTemplateControl() +{ +} + +void SwTemplateControl::StateChanged( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + const SfxStringItem* pItem = nullptr; + if (SfxItemState::DEFAULT == eState && (pItem = dynamic_cast<const SfxStringItem*>(pState))) + { + sTemplate = pItem->GetValue(); + GetStatusBar().SetItemText(GetId(), sTemplate); + } + else + GetStatusBar().SetItemText(GetId(), OUString()); +} + +void SwTemplateControl::Paint( const UserDrawEvent& ) +{ +} + +void SwTemplateControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu && + !GetStatusBar().GetItemText( GetId() ).isEmpty() ) + { + ScopedVclPtrInstance<SwTemplatePopup_Impl> aPop; + { + SwView* pView = ::GetActiveView(); + SwWrtShell *const pWrtShell(pView ? pView->GetWrtShellPtr() : nullptr); + if (nullptr != pWrtShell && + !pWrtShell->SwCursorShell::HasSelection()&& + !pWrtShell->IsSelFrameMode() && + !pWrtShell->IsObjSelected()) + { + SfxStyleSheetBasePool* pPool = pView->GetDocShell()-> + GetStyleSheetPool(); + auto xIter = pPool->CreateIterator(SfxStyleFamily::Page); + if (xIter->Count() > 1) + { + sal_uInt16 nCount = 0; + SfxStyleSheetBase* pStyle = xIter->First(); + while( pStyle ) + { + aPop->InsertItem( ++nCount, pStyle->GetName() ); + pStyle = xIter->Next(); + } + + aPop->Execute( &GetStatusBar(), rCEvt.GetMousePosPixel()); + const sal_uInt16 nCurrId = aPop->GetCurId(); + if( nCurrId != USHRT_MAX) + { + // looks a bit awkward, but another way is not possible + pStyle = xIter->operator[]( nCurrId - 1 ); + SfxStringItem aStyle( FN_SET_PAGE_STYLE, pStyle->GetName() ); + pWrtShell->GetView().GetViewFrame()->GetDispatcher()->ExecuteList( + FN_SET_PAGE_STYLE, + SfxCallMode::SLOT|SfxCallMode::RECORD, + { &aStyle }); + } + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/uiitems.cxx b/sw/source/uibase/utlui/uiitems.cxx new file mode 100644 index 000000000..e6673587c --- /dev/null +++ b/sw/source/uibase/utlui/uiitems.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 <editeng/itemtype.hxx> +#include <unosett.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <uiitems.hxx> + +#include <strings.hrc> +#include <unomid.h> +#include <numrule.hxx> + +#include <editeng/eerdll.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +SwPageFootnoteInfoItem::SwPageFootnoteInfoItem( SwPageFootnoteInfo const & rInfo) : + SfxPoolItem( FN_PARAM_FTN_INFO ), + aFootnoteInfo(rInfo) +{ +} + +SwPageFootnoteInfoItem::~SwPageFootnoteInfoItem() +{ +} + +SwPageFootnoteInfoItem* SwPageFootnoteInfoItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPageFootnoteInfoItem( *this ); +} + +bool SwPageFootnoteInfoItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && aFootnoteInfo == static_cast<const SwPageFootnoteInfoItem&>(rAttr).aFootnoteInfo; +} + +bool SwPageFootnoteInfoItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit eCoreUnit, + MapUnit ePresUnit, + OUString& rText, + const IntlWrapper& rIntl +) const +{ + const SwTwips nHght = GetPageFootnoteInfo().GetHeight(); + if ( nHght ) + { + rText = SwResId( STR_MAX_FTN_HEIGHT ) + " " + + ::GetMetricText( nHght, eCoreUnit, ePresUnit, &rIntl ) + " " + + EditResId( ::GetMetricId( ePresUnit ) ); + } + return true; +} + +bool SwPageFootnoteInfoItem::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const +{ + bool bRet = true; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_FTN_HEIGHT : rVal <<= static_cast<sal_Int32>(convertTwipToMm100(aFootnoteInfo.GetHeight()));break; + case MID_LINE_WEIGHT : rVal <<= static_cast<sal_Int16>(convertTwipToMm100(aFootnoteInfo.GetLineWidth()));break; + case MID_LINE_COLOR : rVal <<= aFootnoteInfo.GetLineColor();break; + case MID_LINE_RELWIDTH : + { + Fraction aTmp( 100, 1 ); + aTmp *= aFootnoteInfo.GetWidth(); + rVal <<= static_cast<sal_Int8>(static_cast<long>(aTmp)); + } + break; + case MID_LINE_ADJUST : rVal <<= static_cast<sal_Int16>(aFootnoteInfo.GetAdj());break;//text::HorizontalAdjust + case MID_LINE_TEXT_DIST : rVal <<= static_cast<sal_Int32>(convertTwipToMm100(aFootnoteInfo.GetTopDist()));break; + case MID_LINE_FOOTNOTE_DIST: rVal <<= static_cast<sal_Int32>(convertTwipToMm100(aFootnoteInfo.GetBottomDist()));break; + case MID_FTN_LINE_STYLE : + { + switch ( aFootnoteInfo.GetLineStyle( ) ) + { + default: + case SvxBorderLineStyle::NONE : rVal <<= sal_Int8(0); break; + case SvxBorderLineStyle::SOLID: rVal <<= sal_Int8(1); break; + case SvxBorderLineStyle::DOTTED: rVal <<= sal_Int8(2); break; + case SvxBorderLineStyle::DASHED: rVal <<= sal_Int8(3); break; + } + break; + } + default: + bRet = false; + } + return bRet; +} + +bool SwPageFootnoteInfoItem::PutValue(const Any& rVal, sal_uInt8 nMemberId) +{ + sal_Int32 nSet32 = 0; + Color aColor; + bool bRet = true; + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_LINE_COLOR : + rVal >>= aColor; + aFootnoteInfo.SetLineColor(aColor); + break; + case MID_FTN_HEIGHT: + case MID_LINE_TEXT_DIST : + case MID_LINE_FOOTNOTE_DIST: + rVal >>= nSet32; + if(nSet32 < 0) + bRet = false; + else + { + nSet32 = convertMm100ToTwip(nSet32); + switch(nMemberId & ~CONVERT_TWIPS) + { + case MID_FTN_HEIGHT: aFootnoteInfo.SetHeight(nSet32); break; + case MID_LINE_TEXT_DIST: aFootnoteInfo.SetTopDist(nSet32);break; + case MID_LINE_FOOTNOTE_DIST: aFootnoteInfo.SetBottomDist(nSet32);break; + } + } + break; + case MID_LINE_WEIGHT : + { + sal_Int16 nSet = 0; + rVal >>= nSet; + if(nSet >= 0) + aFootnoteInfo.SetLineWidth(convertMm100ToTwip(nSet)); + else + bRet = false; + } + break; + case MID_LINE_RELWIDTH : + { + sal_Int8 nSet = 0; + rVal >>= nSet; + if(nSet < 0) + bRet = false; + else + aFootnoteInfo.SetWidth(Fraction(nSet, 100)); + } + break; + case MID_LINE_ADJUST : + { + sal_Int16 nSet = 0; + rVal >>= nSet; + if(nSet >= 0 && nSet < 3) //text::HorizontalAdjust + aFootnoteInfo.SetAdj(static_cast<css::text::HorizontalAdjust>(nSet)); + else + bRet = false; + } + break; + case MID_FTN_LINE_STYLE: + { + SvxBorderLineStyle eStyle = SvxBorderLineStyle::NONE; + sal_Int8 nSet = 0; + rVal >>= nSet; + switch ( nSet ) + { + case 1: eStyle = SvxBorderLineStyle::SOLID; break; + case 2: eStyle = SvxBorderLineStyle::DOTTED; break; + case 3: eStyle = SvxBorderLineStyle::DASHED; break; + default: break; + } + aFootnoteInfo.SetLineStyle( eStyle ); + } + break; + default: + bRet = false; + } + return bRet; +} + +SwPtrItem::SwPtrItem( const sal_uInt16 nId, void* pPtr ) : + SfxPoolItem( nId ), + pMisc(pPtr) +{ +} + +// Cloning + +SwPtrItem* SwPtrItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPtrItem( *this ); +} + +bool SwPtrItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && pMisc == static_cast<const SwPtrItem&>(rAttr).pMisc; +} + +// SwUINumRuleItem for the NumTabPages of the FormatNumRule/Styleists + +SwUINumRuleItem::SwUINumRuleItem( const SwNumRule& rRul ) + : SfxPoolItem( FN_PARAM_ACT_NUMBER ), pRule( new SwNumRule( rRul ) ) +{ +} + +SwUINumRuleItem::SwUINumRuleItem( const SwUINumRuleItem& rItem ) + : SfxPoolItem( rItem ), + pRule( new SwNumRule( *rItem.pRule )) +{ +} + +SwUINumRuleItem::~SwUINumRuleItem() +{ +} + +SwUINumRuleItem* SwUINumRuleItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwUINumRuleItem( *this ); +} + +bool SwUINumRuleItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && *pRule == *static_cast<const SwUINumRuleItem&>(rAttr).pRule; +} + +bool SwUINumRuleItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const +{ + uno::Reference< container::XIndexReplace >xRules = new SwXNumberingRules(*pRule); + rVal <<= xRules; + return true; +} +bool SwUINumRuleItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) +{ + uno::Reference< container::XIndexReplace> xRulesRef; + if(rVal >>= xRulesRef) + { + auto pSwXRules = comphelper::getUnoTunnelImplementation<SwXNumberingRules>(xRulesRef); + if(pSwXRules) + { + *pRule = *pSwXRules->GetNumRule(); + } + } + return true; +} + +SwPaMItem::SwPaMItem( const sal_uInt16 nId, SwPaM* pPaM ) : + SfxPoolItem( nId ), + m_pPaM(pPaM) +{ +} + +SwPaMItem* SwPaMItem::Clone( SfxItemPool * /*pPool*/ ) const +{ + return new SwPaMItem( *this ); +} + +bool SwPaMItem::operator==( const SfxPoolItem& rAttr ) const +{ + return SfxPoolItem::operator==(rAttr) + && m_pPaM == static_cast<const SwPaMItem&>(rAttr).m_pPaM; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/uitool.cxx b/sw/source/uibase/utlui/uitool.cxx new file mode 100644 index 000000000..fb41aaa6f --- /dev/null +++ b/sw/source/uibase/utlui/uitool.cxx @@ -0,0 +1,848 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/datetime.hxx> +#include <vcl/weld.hxx> +#include <unotools/collatorwrapper.hxx> +#include <svl/stritem.hxx> +#include <svl/grabbagitem.hxx> +#include <unotools/syslocale.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <editeng/pmdlitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/brushitem.hxx> +#include <svx/pageitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svl/style.hxx> +#include <unotools/localedatawrapper.hxx> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <comphelper/processfactory.hxx> +#include <sfx2/viewfrm.hxx> +#include <fmtornt.hxx> +#include <tabcol.hxx> +#include <fmtfsize.hxx> +#include <fmthdft.hxx> +#include <fmtpdsc.hxx> +#include <uiitems.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <swmodule.hxx> +#include <view.hxx> +#include <uitool.hxx> +#include <frmatr.hxx> +#include <paratr.hxx> +#include <fmtcol.hxx> +#include <poolfmt.hxx> +#include <usrpref.hxx> + +#include <cmdid.h> +#include <doc.hxx> +#include <docary.hxx> +#include <charfmt.hxx> +#include <SwStyleNameMapper.hxx> +#include <strings.hrc> +// 50 cm 28350 + +#define MAXHEIGHT 28350 +#define MAXWIDTH 28350 + +using namespace ::com::sun::star; + +// General list of string pointer + +// Set boxinfo attribute + +void PrepareBoxInfo(SfxItemSet& rSet, const SwWrtShell& rSh) +{ + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + const SfxPoolItem *pBoxInfo; + + if ( SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER, true, &pBoxInfo)) + { + aBoxInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxInfo->Clone())); + } + + // Table variant: If more than one table cells are selected + rSh.GetCursor(); //So that GetCursorCnt() returns the right thing + aBoxInfo->SetTable (rSh.IsTableMode() && rSh.GetCursorCnt() > 1); + // Always show the distance field + aBoxInfo->SetDist (true); + // Set minimal size in tables and paragraphs + aBoxInfo->SetMinDist (rSh.IsTableMode() || rSh.GetSelectionType() & (SelectionType::Text | SelectionType::Table)); + // Set always the default distance + aBoxInfo->SetDefDist (MIN_BORDER_DIST); + // Single lines can have only in tables DontCare-Status + aBoxInfo->SetValid(SvxBoxInfoItemValidFlags::DISABLE, !rSh.IsTableMode()); + + rSet.Put(*aBoxInfo); +} + +void ConvertAttrCharToGen(SfxItemSet& rSet) +{ + // Background / highlight + { + // Always use the visible background + const SfxPoolItem *pTmpBrush; + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_HIGHLIGHT, true, &pTmpBrush ) ) + { + SvxBrushItem aTmpBrush( *static_cast<const SvxBrushItem*>(pTmpBrush) ); + if( aTmpBrush.GetColor() != COL_TRANSPARENT ) + { + aTmpBrush.SetWhich( RES_CHRATR_BACKGROUND ); + rSet.Put( aTmpBrush ); + } + } + } + + // Tell dialogs to use character-specific slots/whichIds + // tdf#126684: We use RES_PARATR_GRABBAG, because RES_CHRATR_GRABBAG may be overwritten later in + // SwDocStyleSheet::GetItemSet when applying attributes from char format + assert(SfxItemState::SET != rSet.GetItemState(RES_PARATR_GRABBAG, false)); + SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG); + aGrabBag.GetGrabBag()["DialogUseCharAttr"] <<= true; + // Store initial ranges to allow restoring later + const sal_uInt16* pRanges = rSet.GetRanges(); + const sal_uInt16* pEnd = pRanges; + while (*pEnd) + ++pEnd; + const uno::Sequence<sal_uInt16> aOrigRanges(pRanges, pEnd - pRanges + 1); + aGrabBag.GetGrabBag()["OrigItemSetRanges"] <<= aOrigRanges; + rSet.MergeRange(RES_PARATR_GRABBAG, RES_PARATR_GRABBAG); + rSet.Put(aGrabBag); +} + +void ConvertAttrGenToChar(SfxItemSet& rSet, const SfxItemSet& rOrigSet) +{ + // Background / highlighting + const SfxPoolItem *pTmpItem; + if( SfxItemState::SET == rSet.GetItemState( RES_CHRATR_BACKGROUND, false, &pTmpItem ) ) + { + // Highlight is an MS specific thing, so remove it at the first time when LO modifies + // this part of the imported document. + rSet.Put( SvxBrushItem(RES_CHRATR_HIGHLIGHT) ); + + // Remove shading marker + if (SfxItemState::SET == rOrigSet.GetItemState(RES_CHRATR_GRABBAG, false, &pTmpItem)) + { + SfxGrabBagItem aGrabBag(*static_cast<const SfxGrabBagItem*>(pTmpItem)); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if( aIterator != rMap.end() ) + { + aIterator->second <<= false; + } + rSet.Put( aGrabBag ); + } + } + rSet.ClearItem( RES_BACKGROUND ); + + if (SfxItemState::SET == rOrigSet.GetItemState(RES_PARATR_GRABBAG, false, &pTmpItem)) + { + SfxGrabBagItem aGrabBag(*static_cast<const SfxGrabBagItem*>(pTmpItem)); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("OrigItemSetRanges"); + if (aIterator != rMap.end()) + { + if (uno::Sequence<sal_uInt16> aOrigRanges; (aIterator->second >>= aOrigRanges) + && aOrigRanges.getLength() % 2 == 1 + && *(std::cend(aOrigRanges) - 1) == 0) + rSet.SetRanges(aOrigRanges.getConstArray()); + } + } + assert(SfxItemState::SET != rSet.GetItemState(RES_PARATR_GRABBAG, false)); +} + +void ApplyCharBackground(const Color& rBackgroundColor, SwWrtShell& rShell) +{ + rShell.StartUndo(SwUndoId::INSATTR); + + SfxItemSet aCoreSet(rShell.GetView().GetPool(), svl::Items< + RES_CHRATR_GRABBAG, RES_CHRATR_GRABBAG>{}); + + rShell.GetCurAttr(aCoreSet); + + // Set char background + rShell.SetAttrItem(SvxBrushItem(rBackgroundColor, RES_CHRATR_BACKGROUND)); + + // Highlight is an MS specific thing, so remove it at the first time when LO modifies + // this part of the imported document. + rShell.SetAttrItem(SvxBrushItem(RES_CHRATR_HIGHLIGHT)); + + // Remove shading marker + const SfxPoolItem *pTmpItem; + if (SfxItemState::SET == aCoreSet.GetItemState(RES_CHRATR_GRABBAG, false, &pTmpItem)) + { + SfxGrabBagItem aGrabBag(*static_cast<const SfxGrabBagItem*>(pTmpItem)); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if (aIterator != rMap.end()) + { + aIterator->second <<= false; + } + rShell.SetAttrItem(aGrabBag); + } + + rShell.EndUndo(SwUndoId::INSATTR); +} + +// Fill header footer + +static void FillHdFt(SwFrameFormat* pFormat, const SfxItemSet& rSet) +{ + SwAttrSet aSet(pFormat->GetAttrSet()); + aSet.Put(rSet); + + const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE); + const SfxBoolItem& rDynamic = rSet.Get(SID_ATTR_PAGE_DYNAMIC); + + // Convert size + SwFormatFrameSize aFrameSize(rDynamic.GetValue() ? SwFrameSize::Minimum : SwFrameSize::Fixed, + rSize.GetSize().Width(), + rSize.GetSize().Height()); + aSet.Put(aFrameSize); + pFormat->SetFormatAttr(aSet); +} + +/// Convert from UseOnPage to SvxPageUsage. +static SvxPageUsage lcl_convertUseToSvx(UseOnPage nUse) +{ + SvxPageUsage nRet = SvxPageUsage::NONE; + if (nUse & UseOnPage::Left) + nRet = SvxPageUsage::Left; + if (nUse & UseOnPage::Right) + nRet = SvxPageUsage::Right; + if ((nUse & UseOnPage::All) == UseOnPage::All) + nRet = SvxPageUsage::All; + if ((nUse & UseOnPage::Mirror) == UseOnPage::Mirror) + nRet = SvxPageUsage::Mirror; + return nRet; +} + +/// Convert from SvxPageUsage to UseOnPage. +static UseOnPage lcl_convertUseFromSvx(SvxPageUsage nUse) +{ + UseOnPage nRet = UseOnPage::NONE; + if (nUse == SvxPageUsage::Left) + nRet = UseOnPage::Left; + else if (nUse == SvxPageUsage::Right) + nRet = UseOnPage::Right; + else if (nUse == SvxPageUsage::All) + nRet = UseOnPage::All; + else if (nUse == SvxPageUsage::Mirror) + nRet = UseOnPage::Mirror; + return nRet; +} + +// PageDesc <-> convert into sets and back + +void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ) +{ + SwFrameFormat& rMaster = rPageDesc.GetMaster(); + bool bFirstShare = false; + + // Transfer all general frame attributes + rMaster.SetFormatAttr(rSet); + + // PageData + if(rSet.GetItemState(SID_ATTR_PAGE) == SfxItemState::SET) + { + const SvxPageItem& rPageItem = rSet.Get(SID_ATTR_PAGE); + + const SvxPageUsage nUse = rPageItem.GetPageUsage(); + if(nUse != SvxPageUsage::NONE) + rPageDesc.SetUseOn( lcl_convertUseFromSvx(nUse) ); + rPageDesc.SetLandscape(rPageItem.IsLandscape()); + SvxNumberType aNumType; + aNumType.SetNumberingType( rPageItem.GetNumType() ); + rPageDesc.SetNumType(aNumType); + } + // Size + if(rSet.GetItemState(SID_ATTR_PAGE_SIZE) == SfxItemState::SET) + { + const SvxSizeItem& rSizeItem = rSet.Get(SID_ATTR_PAGE_SIZE); + SwFormatFrameSize aSize(SwFrameSize::Fixed); + aSize.SetSize(rSizeItem.GetSize()); + rMaster.SetFormatAttr(aSize); + } + // Evaluate header attributes + const SfxPoolItem* pItem; + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_PAGE_HEADERSET, + false, &pItem ) ) + { + const SfxItemSet& rHeaderSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rHeaderOn = rHeaderSet.Get(SID_ATTR_PAGE_ON); + + if(rHeaderOn.GetValue()) + { + // Take over values + if(!rMaster.GetHeader().IsActive()) + rMaster.SetFormatAttr(SwFormatHeader(true)); + + // Pick out everything and adapt the header format + SwFormatHeader aHeaderFormat(rMaster.GetHeader()); + SwFrameFormat *pHeaderFormat = aHeaderFormat.GetHeaderFormat(); + OSL_ENSURE(pHeaderFormat != nullptr, "no header format"); + + ::FillHdFt(pHeaderFormat, rHeaderSet); + + rPageDesc.ChgHeaderShare(rHeaderSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); + rPageDesc.ChgFirstShare(static_cast<const SfxBoolItem&>( + rHeaderSet.Get(SID_ATTR_PAGE_SHARED_FIRST)).GetValue()); + bFirstShare = true; + } + else + { + // Disable header + if(rMaster.GetHeader().IsActive()) + { + rMaster.SetFormatAttr(SwFormatHeader(false)); + rPageDesc.ChgHeaderShare(false); + } + } + } + + // Evaluate footer attributes + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_PAGE_FOOTERSET, + false, &pItem ) ) + { + const SfxItemSet& rFooterSet = static_cast<const SvxSetItem*>(pItem)->GetItemSet(); + const SfxBoolItem& rFooterOn = rFooterSet.Get(SID_ATTR_PAGE_ON); + + if(rFooterOn.GetValue()) + { + // Take over values + if(!rMaster.GetFooter().IsActive()) + rMaster.SetFormatAttr(SwFormatFooter(true)); + + // Pick out everything and adapt the footer format + SwFormatFooter aFooterFormat(rMaster.GetFooter()); + SwFrameFormat *pFooterFormat = aFooterFormat.GetFooterFormat(); + OSL_ENSURE(pFooterFormat != nullptr, "no footer format"); + + ::FillHdFt(pFooterFormat, rFooterSet); + + rPageDesc.ChgFooterShare(rFooterSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); + if (!bFirstShare) + { + rPageDesc.ChgFirstShare(static_cast<const SfxBoolItem&>( + rFooterSet.Get(SID_ATTR_PAGE_SHARED_FIRST)).GetValue()); + } + } + else + { + // Disable footer + if(rMaster.GetFooter().IsActive()) + { + rMaster.SetFormatAttr(SwFormatFooter(false)); + rPageDesc.ChgFooterShare(false); + } + } + } + + // Footnotes + + if( SfxItemState::SET == rSet.GetItemState( FN_PARAM_FTN_INFO, + false, &pItem ) ) + rPageDesc.SetFootnoteInfo( static_cast<const SwPageFootnoteInfoItem*>(pItem)->GetPageFootnoteInfo() ); + + // Columns + + // Register compliant + + if(SfxItemState::SET == rSet.GetItemState( + SID_SWREGISTER_MODE, false, &pItem)) + { + bool bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue(); + if(!bSet) + rPageDesc.SetRegisterFormatColl(nullptr); + else if(SfxItemState::SET == rSet.GetItemState( + SID_SWREGISTER_COLLECTION, false, &pItem)) + { + const OUString& rColl = static_cast<const SfxStringItem*>(pItem)->GetValue(); + SwDoc& rDoc = *rMaster.GetDoc(); + SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName( rColl ); + if( !pColl ) + { + const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( + rColl, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId ) + pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nId ); + else + pColl = rDoc.MakeTextFormatColl( rColl, + rDoc.GetDfltTextFormatColl() ); + } + if( pColl ) + pColl->SetFormatAttr( SwRegisterItem ( true )); + rPageDesc.SetRegisterFormatColl( pColl ); + } + } +} + +void PageDescToItemSet( const SwPageDesc& rPageDesc, SfxItemSet& rSet) +{ + const SwFrameFormat& rMaster = rPageDesc.GetMaster(); + + // Page data + SvxPageItem aPageItem(SID_ATTR_PAGE); + aPageItem.SetDescName(rPageDesc.GetName()); + aPageItem.SetPageUsage(lcl_convertUseToSvx(rPageDesc.GetUseOn())); + aPageItem.SetLandscape(rPageDesc.GetLandscape()); + aPageItem.SetNumType(rPageDesc.GetNumType().GetNumberingType()); + rSet.Put(aPageItem); + + // Size + SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, rMaster.GetFrameSize().GetSize()); + rSet.Put(aSizeItem); + + // Maximum size + SvxSizeItem aMaxSizeItem(SID_ATTR_PAGE_MAXSIZE, Size(MAXWIDTH, MAXHEIGHT)); + rSet.Put(aMaxSizeItem); + + // Margins, border and the other stuff. + rSet.Put(rMaster.GetAttrSet()); + + std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER)); + const SfxPoolItem *pBoxInfo; + + if ( SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER, true, &pBoxInfo) ) + { + aBoxInfo.reset(static_cast<SvxBoxInfoItem*>(pBoxInfo->Clone())); + } + + aBoxInfo->SetTable( false ); + // Show always the distance field + aBoxInfo->SetDist( true); + // Set minimal size in tables and paragraphs + aBoxInfo->SetMinDist( false ); + // Set always the default distance + aBoxInfo->SetDefDist( MIN_BORDER_DIST ); + // Single lines can have only in tables DontCare-Status + aBoxInfo->SetValid( SvxBoxInfoItemValidFlags::DISABLE ); + rSet.Put( *aBoxInfo ); + + SfxStringItem aFollow(SID_ATTR_PAGE_EXT1, OUString()); + if(rPageDesc.GetFollow()) + aFollow.SetValue(rPageDesc.GetFollow()->GetName()); + rSet.Put(aFollow); + + // Header + if(rMaster.GetHeader().IsActive()) + { + const SwFormatHeader &rHeaderFormat = rMaster.GetHeader(); + const SwFrameFormat *pHeaderFormat = rHeaderFormat.GetHeaderFormat(); + OSL_ENSURE(pHeaderFormat != nullptr, "no header format"); + + // HeaderInfo, margins, background, border + SfxItemSet aHeaderSet(*rSet.GetPool(), + svl::Items<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>{}); + + // set correct parent to get the XFILL_NONE FillStyle as needed + aHeaderSet.SetParent(&rMaster.GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + // Dynamic or fixed height + SfxBoolItem aOn(SID_ATTR_PAGE_ON, true); + aHeaderSet.Put(aOn); + + const SwFormatFrameSize &rFrameSize = pHeaderFormat->GetFrameSize(); + const SwFrameSize eSizeType = rFrameSize.GetHeightSizeType(); + SfxBoolItem aDynamic(SID_ATTR_PAGE_DYNAMIC, eSizeType != SwFrameSize::Fixed); + aHeaderSet.Put(aDynamic); + + // Left equal right + SfxBoolItem aShared(SID_ATTR_PAGE_SHARED, rPageDesc.IsHeaderShared()); + aHeaderSet.Put(aShared); + SfxBoolItem aFirstShared(SID_ATTR_PAGE_SHARED_FIRST, rPageDesc.IsFirstShared()); + aHeaderSet.Put(aFirstShared); + + // Size + SvxSizeItem aSize(SID_ATTR_PAGE_SIZE, rFrameSize.GetSize()); + aHeaderSet.Put(aSize); + + // Shifting frame attributes + aHeaderSet.Put(pHeaderFormat->GetAttrSet()); + aHeaderSet.Put( *aBoxInfo ); + + // Create SetItem + SvxSetItem aSetItem(SID_ATTR_PAGE_HEADERSET, aHeaderSet); + rSet.Put(aSetItem); + } + + // Footer + if(rMaster.GetFooter().IsActive()) + { + const SwFormatFooter &rFooterFormat = rMaster.GetFooter(); + const SwFrameFormat *pFooterFormat = rFooterFormat.GetFooterFormat(); + OSL_ENSURE(pFooterFormat != nullptr, "no footer format"); + + // FooterInfo, margins, background, border + SfxItemSet aFooterSet(*rSet.GetPool(), + svl::Items<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>{}); + + // set correct parent to get the XFILL_NONE FillStyle as needed + aFooterSet.SetParent(&rMaster.GetDoc()->GetDfltFrameFormat()->GetAttrSet()); + + // Dynamic or fixed height + SfxBoolItem aOn(SID_ATTR_PAGE_ON, true); + aFooterSet.Put(aOn); + + const SwFormatFrameSize &rFrameSize = pFooterFormat->GetFrameSize(); + const SwFrameSize eSizeType = rFrameSize.GetHeightSizeType(); + SfxBoolItem aDynamic(SID_ATTR_PAGE_DYNAMIC, eSizeType != SwFrameSize::Fixed); + aFooterSet.Put(aDynamic); + + // Left equal right + SfxBoolItem aShared(SID_ATTR_PAGE_SHARED, rPageDesc.IsFooterShared()); + aFooterSet.Put(aShared); + SfxBoolItem aFirstShared(SID_ATTR_PAGE_SHARED_FIRST, rPageDesc.IsFirstShared()); + aFooterSet.Put(aFirstShared); + + // Size + SvxSizeItem aSize(SID_ATTR_PAGE_SIZE, rFrameSize.GetSize()); + aFooterSet.Put(aSize); + + // Shifting Frame attributes + aFooterSet.Put(pFooterFormat->GetAttrSet()); + aFooterSet.Put( *aBoxInfo ); + + // Create SetItem + SvxSetItem aSetItem(SID_ATTR_PAGE_FOOTERSET, aFooterSet); + rSet.Put(aSetItem); + } + + // Integrate footnotes + SwPageFootnoteInfo& rInfo = const_cast<SwPageFootnoteInfo&>(rPageDesc.GetFootnoteInfo()); + SwPageFootnoteInfoItem aFootnoteItem(rInfo); + rSet.Put(aFootnoteItem); + + // Register compliant + const SwTextFormatColl* pCol = rPageDesc.GetRegisterFormatColl(); + SwRegisterItem aReg(pCol != nullptr); + aReg.SetWhich(SID_SWREGISTER_MODE); + rSet.Put(aReg); + if(pCol) + rSet.Put(SfxStringItem(SID_SWREGISTER_COLLECTION, pCol->GetName())); + +} + +// Set DefaultTabs + +void MakeDefTabs(SwTwips nDefDist, SvxTabStopItem& rTabs) +{ + if( rTabs.Count() ) + return; + { + SvxTabStop aSwTabStop( nDefDist, SvxTabAdjust::Default ); + rTabs.Insert( aSwTabStop ); + } +} + +// Distance between two tabs + +SwTwips GetTabDist(const SvxTabStopItem& rTabs) +{ + return rTabs.Count() ? rTabs[0].GetTabPos() : 1134; // 1134 = 2 cm +} + +// Inquire if in the set is a Sfx-PageDesc combination present and return it. +void SfxToSwPageDescAttr( const SwWrtShell& rShell, SfxItemSet& rSet ) +{ + const SfxPoolItem* pItem; + SwFormatPageDesc aPgDesc; + + bool bChanged = false; + // Page number + switch (rSet.GetItemState(SID_ATTR_PARA_PAGENUM, false, &pItem)) + { + case SfxItemState::SET: + { + aPgDesc.SetNumOffset(static_cast<const SfxUInt16Item*>(pItem)->GetValue()); + bChanged = true; + break; + } + case SfxItemState::DISABLED: + { + bChanged = true; // default initialised aPgDesc clears the number + break; + } + case SfxItemState::UNKNOWN: + case SfxItemState::DEFAULT: + break; + default: + assert(false); // unexpected + break; + } + if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_PARA_MODEL, false, &pItem )) + { + const OUString& rDescName = static_cast<const SvxPageModelItem*>(pItem)->GetValue(); + if( !rDescName.isEmpty() ) // No name -> disable PageDesc! + { + // Delete only, if PageDesc will be enabled! + rSet.ClearItem( RES_BREAK ); + SwPageDesc* pDesc = const_cast<SwWrtShell&>(rShell).FindPageDescByName( + rDescName, true ); + if( pDesc ) + aPgDesc.RegisterToPageDesc( *pDesc ); + } + rSet.ClearItem( SID_ATTR_PARA_MODEL ); + bChanged = true; + } + else + { + SfxItemSet aCoreSet(rShell.GetView().GetPool(), svl::Items<RES_PAGEDESC, RES_PAGEDESC>{} ); + rShell.GetCurAttr( aCoreSet ); + if(SfxItemState::SET == aCoreSet.GetItemState( RES_PAGEDESC, true, &pItem ) ) + { + if( static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc() ) + { + aPgDesc.RegisterToPageDesc( *const_cast<SwFormatPageDesc*>(static_cast<const SwFormatPageDesc*>(pItem))->GetPageDesc() ); + } + } + } + + if(bChanged) + rSet.Put( aPgDesc ); +} + +// Inquire if in the set is a Sfx-PageDesc combination present and return it. +void SwToSfxPageDescAttr( SfxItemSet& rCoreSet ) +{ + const SfxPoolItem* pItem = nullptr; + OUString aName; + ::std::optional<sal_uInt16> oNumOffset; + bool bPut = true; + switch( rCoreSet.GetItemState( RES_PAGEDESC, true, &pItem ) ) + { + case SfxItemState::SET: + { + if( static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc() ) + { + aName = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc()->GetName(); + oNumOffset = static_cast<const SwFormatPageDesc*>(pItem)->GetNumOffset(); + } + rCoreSet.ClearItem( RES_PAGEDESC ); + // Page number + } + break; + + case SfxItemState::DEFAULT: + break; + + default: + bPut = false; + } + + if (oNumOffset) + { + SfxUInt16Item aPageNum( SID_ATTR_PARA_PAGENUM, *oNumOffset ); + rCoreSet.Put( aPageNum ); + } + + if(bPut) + rCoreSet.Put( SvxPageModelItem( aName, true, SID_ATTR_PARA_MODEL ) ); +} + +// Determine metric + +FieldUnit GetDfltMetric(bool bWeb) +{ + return SW_MOD()->GetUsrPref(bWeb)->GetMetric(); +} + +// Determine metric + +void SetDfltMetric( FieldUnit eMetric, bool bWeb ) +{ + SW_MOD()->ApplyUserMetric(eMetric, bWeb); +} + +void InsertStringSorted(const OUString& rId, const OUString& rEntry, weld::ComboBox& rToFill, int nOffset) +{ + CollatorWrapper& rCaseColl = ::GetAppCaseCollator(); + const int nCount = rToFill.get_count(); + while (nOffset < nCount) + { + if (0 < rCaseColl.compareString(rToFill.get_text(nOffset), rEntry)) + break; + ++nOffset; + } + rToFill.insert(nOffset, rEntry, &rId, nullptr, nullptr); +} + +void FillCharStyleListBox(weld::ComboBox& rToFill, SwDocShell* pDocSh, bool bSorted, bool bWithDefault) +{ + const int nOffset = rToFill.get_count() > 0 ? 1 : 0; + rToFill.freeze(); + SfxStyleSheetBasePool* pPool = pDocSh->GetStyleSheetPool(); + SwDoc* pDoc = pDocSh->GetDoc(); + const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Char); + const OUString sStandard(SwResId(STR_POOLCHR_STANDARD)); + while(pBase) + { + if(bWithDefault || pBase->GetName() != sStandard) + { + sal_IntPtr nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( pBase->GetName(), SwGetPoolIdFromName::ChrFmt ); + OUString sId(OUString::number(nPoolId)); + if (bSorted) + InsertStringSorted(sId, pBase->GetName(), rToFill, nOffset); + else + rToFill.append(sId, pBase->GetName()); + } + pBase = pPool->Next(); + } + // non-pool styles + const SwCharFormats* pFormats = pDoc->GetCharFormats(); + for(size_t i = 0; i < pFormats->size(); ++i) + { + const SwCharFormat* pFormat = (*pFormats)[i]; + if(pFormat->IsDefault()) + continue; + const OUString& rName = pFormat->GetName(); + if (rToFill.find_text(rName) == -1) + { + OUString sId(OUString::number(USHRT_MAX)); + if (bSorted) + InsertStringSorted(sId, rName, rToFill, nOffset); + else + rToFill.append(sId, rName); + } + } + rToFill.thaw(); +}; + +SwTwips GetTableWidth( SwFrameFormat const * pFormat, SwTabCols const & rCols, sal_uInt16 *pPercent, + SwWrtShell* pSh ) +{ + // To get the width is slightly more complicated. + SwTwips nWidth = 0; + const sal_Int16 eOri = pFormat->GetHoriOrient().GetHoriOrient(); + switch(eOri) + { + case text::HoriOrientation::FULL: nWidth = rCols.GetRight(); break; + case text::HoriOrientation::LEFT_AND_WIDTH: + case text::HoriOrientation::LEFT: + case text::HoriOrientation::RIGHT: + case text::HoriOrientation::CENTER: + nWidth = pFormat->GetFrameSize().GetWidth(); + break; + default: + { + if(pSh) + { + if ( nullptr == pSh->GetFlyFrameFormat() ) + { + nWidth = pSh->GetAnyCurRect(CurRectType::PagePrt).Width(); + } + else + { + nWidth = pSh->GetAnyCurRect(CurRectType::FlyEmbeddedPrt).Width(); + } + } + else + { + OSL_FAIL("where to get the actual width from?"); + } + const SvxLRSpaceItem& rLRSpace = pFormat->GetLRSpace(); + nWidth -= (rLRSpace.GetRight() + rLRSpace.GetLeft()); + } + } + if (pPercent) + *pPercent = pFormat->GetFrameSize().GetWidthPercent(); + return nWidth; +} + +OUString GetAppLangDateTimeString( const DateTime& rDT ) +{ + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rAppLclData = aSysLocale.GetLocaleData(); + OUString sRet = rAppLclData.getDate( rDT ) + " " + rAppLclData.getTime( rDT ); + return sRet; +} + +// Add a new function which can get and set the current "SID_ATTR_APPLYCHARUNIT" value + +bool HasCharUnit( bool bWeb) +{ + return SW_MOD()->GetUsrPref(bWeb)->IsApplyCharUnit(); +} + +void SetApplyCharUnit(bool bApplyChar, bool bWeb) +{ + SW_MOD()->ApplyUserCharUnit(bApplyChar, bWeb); +} + +bool ExecuteMenuCommand( PopupMenu const & rMenu, SfxViewFrame const & rViewFrame, sal_uInt16 nId ) +{ + bool bRet = false; + const sal_uInt16 nItemCount = rMenu.GetItemCount(); + OUString sCommand; + for( sal_uInt16 nItem = 0; nItem < nItemCount; ++nItem) + { + PopupMenu* pPopup = rMenu.GetPopupMenu( rMenu.GetItemId( nItem ) ); + if(pPopup) + { + sCommand = pPopup->GetItemCommand(nId); + if(!sCommand.isEmpty()) + break; + } + } + if(!sCommand.isEmpty()) + { + uno::Reference< frame::XFrame > xFrame = rViewFrame.GetFrame().GetFrameInterface(); + uno::Reference < frame::XDispatchProvider > xProv( xFrame, uno::UNO_QUERY ); + util::URL aURL; + aURL.Complete = sCommand; + uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create(::comphelper::getProcessComponentContext() ) ); + xTrans->parseStrict( aURL ); + uno::Reference< frame::XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + if( xDisp.is() ) + { + uno::Sequence< beans::PropertyValue > aSeq; + xDisp->dispatch( aURL, aSeq ); + bRet = true; + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/unotools.cxx b/sw/source/uibase/utlui/unotools.cxx new file mode 100644 index 000000000..fbad314f1 --- /dev/null +++ b/sw/source/uibase/utlui/unotools.cxx @@ -0,0 +1,510 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <strings.hrc> +#include <unotools.hxx> +#include <unoprnms.hxx> +#include <i18nutil/unicode.hxx> +#include <svtools/colorcfg.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/jobset.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/text/XTextViewCursorSupplier.hpp> +#include <com/sun/star/view/XScreenCursor.hpp> +#include <com/sun/star/view/DocumentZoomType.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/servicehelper.hxx> +#include <docsh.hxx> +#include <editsh.hxx> +#include <swmodule.hxx> +#include <TextCursorHelper.hxx> +#include <doc.hxx> + +using namespace ::com::sun::star; + +const char cFactory[] = "private:factory/swriter"; + +static void disableScrollBars(uno::Reference< beans::XPropertySet > const & xViewProps, + bool bEnableOnlineMode) +{ + //the scrollbar logic is kind of busted looking in writer, when the hori scrollbar + //property is changed then the hori scrollbar is enabled if the property is + //true or browse (online) mode is enabled. So... + //disable online mode + //turn off scrollbars + //turn back on online mode if that's what we want + //which subverts the (dodgy/buggy) scrollbar setting + + //To reproduce this problem, in edit->autotext and click through + //the examples and see if the preview gets a horizontal scrollbar + uno::Any aFalseSet(uno::makeAny(false)); + xViewProps->setPropertyValue(UNO_NAME_SHOW_ONLINE_LAYOUT, aFalseSet); + + xViewProps->setPropertyValue(UNO_NAME_SHOW_HORI_SCROLL_BAR, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_VERT_SCROLL_BAR, aFalseSet); + + if (bEnableOnlineMode) + { + xViewProps->setPropertyValue(UNO_NAME_SHOW_ONLINE_LAYOUT, uno::makeAny(true)); + } +} + +static const sal_Int16 nZoomValues[] = +{ + 20, + 40, + 50, + 75, + 100 +}; + +SwOneExampleFrame::SwOneExampleFrame(sal_uInt32 nFlags, + const Link<SwOneExampleFrame&,void>* pInitializedLink, + const OUString* pURL) + : m_aLoadedIdle("sw uibase SwOneExampleFrame Loaded") + , m_pModuleView(SW_MOD()->GetView()) + , m_nStyleFlags(nFlags) + , m_bIsInitialized(false) +{ + if (pURL && !pURL->isEmpty()) + m_sArgumentURL = *pURL; + + if( pInitializedLink ) + m_aInitializedLink = *pInitializedLink; + + // the controller is asynchronously set + m_aLoadedIdle.SetInvokeHandler(LINK(this, SwOneExampleFrame, TimeoutHdl)); + m_aLoadedIdle.SetPriority(TaskPriority::HIGH_IDLE); +} + +void SwOneExampleFrame::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + m_xVirDev = VclPtr<VirtualDevice>::Create(); + Size aSize(m_xVirDev->LogicToPixel(Size(150, 188), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + CreateControl(); +} + +bool SwOneExampleFrame::Command(const CommandEvent& rCEvt) +{ + switch (rCEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + //#125881# quickly clicking crashes because the control is not fully initialized + if (m_xController.is()) + return CreatePopup(rCEvt.GetMousePosPixel()); + } + break; + default:; + break; + } + return CustomWidgetController::Command(rCEvt); +} + +void SwOneExampleFrame::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + Size aSize(GetOutputSizePixel()); + // m_xVirDev instead of rRenderContext just to avoid overlays in writer re-triggering + // invalidate on rRenderContext if it is a vcl::Window, which is the "classic" gen mode + m_xVirDev->SetOutputSizePixel(aSize); + + Color aBgColor = SW_MOD()->GetColorConfig().GetColorValue(::svtools::DOCCOLOR).nColor; + m_xVirDev->DrawWallpaper(tools::Rectangle(Point(), aSize), aBgColor); + + auto pCursor = comphelper::getUnoTunnelImplementation<OTextCursorHelper>(m_xCursor); + if (pCursor) + { + uno::Reference<view::XViewSettingsSupplier> xSettings(m_xController, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xViewProps = xSettings->getViewSettings(); + uno::Any aZoom = xViewProps->getPropertyValue(UNO_NAME_ZOOM_VALUE); + sal_Int16 nZoom = 100; + aZoom >>= nZoom; + + double fZoom = 100.0 / nZoom; + + m_xVirDev->Push(PushFlags::ALL); + m_xVirDev->SetMapMode(MapMode(MapUnit::MapTwip)); + SwDoc *pDoc = pCursor->GetDoc(); + SwDocShell* pShell = pDoc->GetDocShell(); + tools::Rectangle aRect(Point(), m_xVirDev->PixelToLogic(aSize)); + pShell->SetVisArea(tools::Rectangle(Point(), Size(aRect.GetWidth() * fZoom, + aRect.GetHeight() * fZoom))); + pShell->DoDraw(m_xVirDev.get(), aRect.TopLeft(), aRect.GetSize(), JobSetup(), ASPECT_CONTENT); + m_xVirDev->Pop(); + } + + rRenderContext.DrawOutDev(Point(), aSize, Point(), aSize, *m_xVirDev); +} + +SwOneExampleFrame::~SwOneExampleFrame() +{ + DisposeControl(); +} + +void SwOneExampleFrame::CreateControl() +{ + // create new doc + OUString sTempURL(cFactory); + if(!m_sArgumentURL.isEmpty()) + sTempURL = m_sArgumentURL; + + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext()); + uno::Sequence<beans::PropertyValue> args( comphelper::InitPropertySequence({ + { "DocumentService", uno::Any(OUString("com.sun.star.text.TextDocument")) }, + { "OpenFlags", uno::Any(OUString("-RB")) }, + { "Referer", uno::Any(OUString("private:user")) }, + { "ReadOnly", uno::Any(true) }, + { "Hidden", uno::Any(true) } + })); + + m_xModel.set(xDesktop->loadComponentFromURL(sTempURL, "_blank", 0, args), uno::UNO_QUERY); + + m_aLoadedIdle.Start(); +} + +void SwOneExampleFrame::DisposeControl() +{ + m_aLoadedIdle.Stop(); + m_xCursor = nullptr; + if (m_xModel) + { + m_xModel->dispose(); + m_xModel = nullptr; + } + m_xController = nullptr; +} + +IMPL_LINK( SwOneExampleFrame, TimeoutHdl, Timer*, pTimer, void ) +{ + if (!m_xModel.is()) + return; + + m_xController = m_xModel->getCurrentController(); + + if (m_xController.is()) + { + uno::Reference<frame::XFrame> xFrame = m_xController->getFrame(); + uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + try + { + uno::Reference< frame::XLayoutManager > xLayoutManager; + uno::Any aValue = xPropSet->getPropertyValue("LayoutManager"); + aValue >>= xLayoutManager; + if ( xLayoutManager.is() ) + xLayoutManager->setVisible( false ); + } + catch (const uno::Exception&) + { + } + } + + //now the ViewOptions should be set properly + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + const uno::Any aTrueSet( true ); + const uno::Any aFalseSet( false ); + + if( !m_bIsInitialized ) + { + xViewProps->setPropertyValue(UNO_NAME_SHOW_BREAKS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_DRAWINGS, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_FIELD_COMMANDS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_GRAPHICS, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_HIDE_WHITESPACE, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HIDDEN_PARAGRAPHS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HIDDEN_TEXT, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_HORI_RULER, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_PARA_BREAKS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_PROTECTED_SPACES, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_SOFT_HYPHENS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_SPACES, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_TABLES, aTrueSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_TABSTOPS, aFalseSet); + xViewProps->setPropertyValue(UNO_NAME_SHOW_VERT_RULER, aFalseSet); + + if(0 ==(m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT)) + { + uno::Any aZoom; + aZoom <<= sal_Int16(view::DocumentZoomType::PAGE_WIDTH_EXACT); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + } + else + { + uno::Any aZoom; + aZoom <<= sal_Int16(view::DocumentZoomType::BY_VALUE); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + + sal_Int16 nZoomValue = 75; + if(EX_SHOW_BUSINESS_CARDS == m_nStyleFlags) + { + nZoomValue = 80; + } + aZoom <<= nZoomValue; + xViewProps->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom); + } + + // set onlinelayout property after setting the zoom + disableScrollBars(xViewProps, (m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT) != 0); + m_bIsInitialized = true; + } + + uno::Reference< text::XTextDocument > xDoc(m_xModel, uno::UNO_QUERY); + uno::Reference< text::XText > xText = xDoc->getText(); + m_xCursor = xText->createTextCursor(); + + //From here, a cursor is defined, which goes through the template, + //and overwrites the template words where it is necessary. + + auto pCursor = comphelper::getUnoTunnelImplementation<OTextCursorHelper>(m_xCursor); + + SwDoc *pDoc = pCursor ? pCursor->GetDoc() : nullptr; + if (pDoc && (m_nStyleFlags & EX_LOCALIZE_TOC_STRINGS)) + { + SwEditShell* pSh = pDoc->GetEditShell(); + + do + { + if (pSh->GetCurWord() == "HEADING1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING1)); + } + else if (pSh->GetCurWord() == "ENTRY1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY1)); + } + else if (pSh->GetCurWord() == "HEADING11") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING11)); + } + else if (pSh->GetCurWord() == "ENTRY11") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY11)); + } + else if (pSh->GetCurWord() == "HEADING12") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_HEADING12)); + } + else if (pSh->GetCurWord() == "ENTRY12") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_ENTRY12)); + } + else if (pSh->GetCurWord() == "TABLE1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_TABLE1)); + } + else if (pSh->GetCurWord() == "IMAGE1") + { + pSh->Overwrite(SwResId(STR_IDXEXAMPLE_IDXTXT_IMAGE1)); + } + } + while(pSh->Right(sal_uInt16(1), sal_uInt16(1), true)); + + TOXTypes eTypes[] = { TOX_INDEX, TOX_USER, TOX_CONTENT }; + for (auto eType : eTypes) + { + const SwTOXType* pTOXType = pDoc->GetTOXType(eType, 0); + SwTOXMarks aMarks; + SwTOXMark::InsertTOXMarks(aMarks, *pTOXType); + for (auto pMark : aMarks) + { + if (pMark->GetAlternativeText() == "Chapter") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_CHAPTER)); + else if (pMark->GetAlternativeText() == "Keyword") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_KEYWORD)); + else if (pMark->GetAlternativeText() == "this") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_THIS)); + else if (pMark->GetAlternativeText() == "User Directory Entry") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_USER_DIR_ENTRY)); + else if (pMark->GetAlternativeText() == "Entry") + pMark->SetAlternativeText(SwResId(STR_IDXEXAMPLE_IDXMARK_ENTRY)); + + if (pMark->GetPrimaryKey() == "Primary key") + pMark->SetPrimaryKey(SwResId(STR_IDXEXAMPLE_IDXMARK_PRIMARY_KEY)); + + if (pMark->GetSecondaryKey() == "Secondary key") + pMark->SetSecondaryKey(SwResId(STR_IDXEXAMPLE_IDXMARK_SECONDARY_KEY)); + } + } + } + + uno::Reference< beans::XPropertySet > xCursorProp(m_xCursor, uno::UNO_QUERY); + uno::Any aPageStyle = xCursorProp->getPropertyValue(UNO_NAME_PAGE_STYLE_NAME); + OUString sPageStyle; + aPageStyle >>= sPageStyle; + + uno::Reference< style::XStyleFamiliesSupplier > xSSupp( xDoc, uno::UNO_QUERY); + uno::Reference< container::XNameAccess > xStyles = xSSupp->getStyleFamilies(); + uno::Any aPFamily = xStyles->getByName( "PageStyles" ); + uno::Reference< container::XNameContainer > xPFamily; + + if( EX_SHOW_DEFAULT_PAGE != m_nStyleFlags + && (aPFamily >>= xPFamily) && !sPageStyle.isEmpty() ) + { + uno::Any aPStyle = xPFamily->getByName( sPageStyle ); + uno::Reference< style::XStyle > xPStyle; + aPStyle >>= xPStyle; + uno::Reference< beans::XPropertySet > xPProp(xPStyle, uno::UNO_QUERY); + uno::Any aSize = xPProp->getPropertyValue(UNO_NAME_SIZE); + awt::Size aPSize; + aSize >>= aPSize; + //TODO: set page width to card width + aPSize.Width = 10000; + aSize <<= aPSize; + xPProp->setPropertyValue(UNO_NAME_SIZE, aSize); + + uno::Any aZero; aZero <<= sal_Int32(0); + xPProp->setPropertyValue(UNO_NAME_LEFT_MARGIN, aZero); + xPProp->setPropertyValue(UNO_NAME_RIGHT_MARGIN, aZero); + } + + uno::Reference<awt::XWindow> xWin = xFrame->getContainerWindow(); + Size aWinSize(GetOutputSizePixel()); + xWin->setPosSize(0, 0, aWinSize.Width(), aWinSize.Height(), awt::PosSize::SIZE); + + // can only be done here - the SFX changes the ScrollBar values + disableScrollBars(xViewProps, (m_nStyleFlags&EX_SHOW_ONLINE_LAYOUT) != 0); + + m_aInitializedLink.Call(*this); + + uno::Reference< text::XTextViewCursorSupplier > xCursorSupp(m_xController, uno::UNO_QUERY); + uno::Reference< view::XScreenCursor > xScrCursor(xCursorSupp->getViewCursor(), uno::UNO_QUERY); + if(xScrCursor.is()) + xScrCursor->screenUp(); + + if (pDoc) + { + SwEditShell* pSh = pDoc->GetEditShell(); + if( pSh->ActionCount() ) + { + pSh->EndAllAction(); + pSh->UnlockPaint(); + } + } + + SW_MOD()->SetView(m_pModuleView); + + Invalidate(); + } + else + pTimer->Start(); +} + +void SwOneExampleFrame::ClearDocument() +{ + uno::Reference< lang::XUnoTunnel> xTunnel( m_xCursor, uno::UNO_QUERY); + if( xTunnel.is() ) + { + OTextCursorHelper* pCursor = reinterpret_cast<OTextCursorHelper*>(xTunnel->getSomething( + OTextCursorHelper::getUnoTunnelId()) ); + if( pCursor ) + { + SwDoc* pDoc = pCursor->GetDoc(); + SwEditShell* pSh = pDoc->GetEditShell(); + pSh->LockPaint(); + pSh->StartAllAction(); + pSh->KillPams(); + pSh->ClearMark(); + pDoc->ClearDoc(); + pSh->ClearUpCursors(); + + if( m_aLoadedIdle.IsActive()) + { + pSh->EndAllAction(); + pSh->UnlockPaint(); + } + m_aLoadedIdle.Start(); + } + else + { + m_xCursor->gotoStart(false); + m_xCursor->gotoEnd(true); + m_xCursor->setString(OUString()); + } + } +} + +bool SwOneExampleFrame::CreatePopup(const Point& rPt) +{ + if (EX_SHOW_ONLINE_LAYOUT != m_nStyleFlags) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/previewmenu.ui")); + std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu("previewmenu")); + + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + uno::Any aZoom = xViewProps->getPropertyValue(UNO_NAME_ZOOM_VALUE); + sal_Int16 nZoom = 0; + aZoom >>= nZoom; + + for (size_t i = 0; i < SAL_N_ELEMENTS(nZoomValues); ++i) + { + OUString sTemp = unicode::formatPercent(nZoomValues[i], + Application::GetSettings().GetUILanguageTag()); + OString sIdent = "zoom" + OString::number(nZoomValues[i]); + xPop->set_label(sIdent, sTemp); + if (nZoom == nZoomValues[i]) + xPop->set_active(sIdent, true); + } + + PopupHdl(xPop->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPt, Size(1, 1)))); + + return true; +} + +void SwOneExampleFrame::PopupHdl(const OString& rId) +{ + OString sZoomValue; + if (rId.startsWith("zoom", &sZoomValue)) + { + sal_Int16 nZoom = sZoomValue.toInt32(); + uno::Reference< view::XViewSettingsSupplier > xSettings(m_xController, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xViewProps = xSettings->getViewSettings(); + + uno::Any aZoom; + aZoom <<= nZoom; + xViewProps->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom); + aZoom <<= sal_Int16(view::DocumentZoomType::BY_VALUE); + xViewProps->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom); + } + Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/viewlayoutctrl.cxx b/sw/source/uibase/utlui/viewlayoutctrl.cxx new file mode 100644 index 000000000..bdb18bbb3 --- /dev/null +++ b/sw/source/uibase/utlui/viewlayoutctrl.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 <viewlayoutctrl.hxx> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <vcl/event.hxx> +#include <vcl/status.hxx> +#include <vcl/image.hxx> +#include <svx/viewlayoutitem.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <swtypes.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwViewLayoutControl, SvxViewLayoutItem ); + +struct SwViewLayoutControl::SwViewLayoutControl_Impl +{ + sal_uInt16 mnState; // 0 = auto, 1= single, 2 = book, 3 = none + Image maImageSingleColumn; + Image maImageSingleColumn_Active; + Image maImageAutomatic; + Image maImageAutomatic_Active; + Image maImageBookMode; + Image maImageBookMode_Active; +}; + +SwViewLayoutControl::SwViewLayoutControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) : + SfxStatusBarControl( _nSlotId, _nId, rStatusBar ), + mpImpl( new SwViewLayoutControl_Impl ) +{ + mpImpl->mnState = 1; + + mpImpl->maImageSingleColumn = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_SINGLECOLUMN); + mpImpl->maImageSingleColumn_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_SINGLECOLUMN_ACTIVE); + mpImpl->maImageAutomatic = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_AUTOMATIC); + mpImpl->maImageAutomatic_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_AUTOMATIC_ACTIVE); + mpImpl->maImageBookMode = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_BOOKMODE); + mpImpl->maImageBookMode_Active = Image(StockImage::Yes, RID_BMP_VIEWLAYOUT_BOOKMODE_ACTIVE); +} + +SwViewLayoutControl::~SwViewLayoutControl() +{ +} + +void SwViewLayoutControl::StateChanged( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if ( SfxItemState::DEFAULT != eState || pState->IsVoidItem() ) + GetStatusBar().SetItemText( GetId(), OUString() ); + else + { + OSL_ENSURE( dynamic_cast< const SvxViewLayoutItem *>( pState ) != nullptr, "invalid item type" ); + const sal_uInt16 nColumns = static_cast<const SvxViewLayoutItem*>( pState )->GetValue(); + const bool bBookMode = static_cast<const SvxViewLayoutItem*>( pState )->IsBookMode(); + + // SingleColumn Mode + if ( 1 == nColumns ) + mpImpl->mnState = 0; + // Automatic Mode + else if ( 0 == nColumns ) + mpImpl->mnState = 1; + // Book Mode + else if ( bBookMode && 2 == nColumns ) + mpImpl->mnState = 2; + else + mpImpl->mnState = 3; + } + + GetStatusBar().SetItemData( GetId(), nullptr ); // force repaint +} + +void SwViewLayoutControl::Paint( const UserDrawEvent& rUsrEvt ) +{ + vcl::RenderContext* pDev = rUsrEvt.GetRenderContext(); + tools::Rectangle aRect(rUsrEvt.GetRect()); + + const tools::Rectangle aControlRect = getControlRect(); + + const bool bSingleColumn = 0 == mpImpl->mnState; + const bool bAutomatic = 1 == mpImpl->mnState; + const bool bBookMode = 2 == mpImpl->mnState; + + const long nImageWidthSum = mpImpl->maImageSingleColumn.GetSizePixel().Width() + + mpImpl->maImageAutomatic.GetSizePixel().Width() + + mpImpl->maImageBookMode.GetSizePixel().Width(); + + const long nXOffset = (aRect.GetWidth() - nImageWidthSum) / 2; + const long nYOffset = (aControlRect.GetHeight() - mpImpl->maImageSingleColumn.GetSizePixel().Height()) / 2; + + aRect.AdjustLeft( nXOffset ); + aRect.AdjustTop( nYOffset ); + + // draw single column image: + pDev->DrawImage( aRect.TopLeft(), bSingleColumn ? mpImpl->maImageSingleColumn_Active : mpImpl->maImageSingleColumn ); + + // draw automatic image: + aRect.AdjustLeft(mpImpl->maImageSingleColumn.GetSizePixel().Width() ); + pDev->DrawImage( aRect.TopLeft(), bAutomatic ? mpImpl->maImageAutomatic_Active : mpImpl->maImageAutomatic ); + + // draw bookmode image: + aRect.AdjustLeft(mpImpl->maImageAutomatic.GetSizePixel().Width() ); + pDev->DrawImage( aRect.TopLeft(), bBookMode ? mpImpl->maImageBookMode_Active : mpImpl->maImageBookMode ); +} + +bool SwViewLayoutControl::MouseButtonDown( const MouseEvent & rEvt ) +{ + const tools::Rectangle aRect = getControlRect(); + const Point aPoint = rEvt.GetPosPixel(); + const long nXDiff = aPoint.X() - aRect.Left(); + + sal_uInt16 nColumns = 1; + bool bBookMode = false; + + const long nImageWidthSingle = mpImpl->maImageSingleColumn.GetSizePixel().Width(); + const long nImageWidthAuto = mpImpl->maImageAutomatic.GetSizePixel().Width(); + const long nImageWidthBook = mpImpl->maImageBookMode.GetSizePixel().Width(); + const long nImageWidthSum = nImageWidthSingle + nImageWidthAuto + nImageWidthBook; + + const long nXOffset = (aRect.GetWidth() - nImageWidthSum)/2; + + if ( nXDiff < nXOffset + nImageWidthSingle ) + { + mpImpl->mnState = 0; // single + nColumns = 1; + } + else if ( nXDiff < nXOffset + nImageWidthSingle + nImageWidthAuto ) + { + mpImpl->mnState = 1; // auto + nColumns = 0; + } + else + { + mpImpl->mnState = 2; // book + nColumns = 2; + bBookMode = true; + } + + // commit state change + SvxViewLayoutItem aViewLayout( nColumns, bBookMode ); + + css::uno::Any a; + aViewLayout.QueryValue( a ); + + css::uno::Sequence< css::beans::PropertyValue > aArgs( 1 ); + aArgs[0].Name = "ViewLayout"; + aArgs[0].Value = a; + + execute( aArgs ); + + return true; +} + +bool SwViewLayoutControl::MouseMove( const MouseEvent & rEvt ) +{ + const tools::Rectangle aRect = getControlRect(); + const Point aPoint = rEvt.GetPosPixel(); + const long nXDiff = aPoint.X() - aRect.Left(); + + const long nImageWidthSingle = mpImpl->maImageSingleColumn.GetSizePixel().Width(); + const long nImageWidthAuto = mpImpl->maImageAutomatic.GetSizePixel().Width(); + const long nImageWidthBook = mpImpl->maImageBookMode.GetSizePixel().Width(); + const long nImageWidthSum = nImageWidthSingle + nImageWidthAuto + nImageWidthBook; + + const long nXOffset = (aRect.GetWidth() - nImageWidthSum)/2; + + if ( nXDiff < nXOffset + nImageWidthSingle ) + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_ONE)); + } + else if ( nXDiff < nXOffset + nImageWidthSingle + nImageWidthAuto ) + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_MULTI)); + } + else + { + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_VIEWLAYOUT_BOOK)); + } + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/wordcountctrl.cxx b/sw/source/uibase/utlui/wordcountctrl.cxx new file mode 100644 index 000000000..93eba3c73 --- /dev/null +++ b/sw/source/uibase/utlui/wordcountctrl.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/. + */ + +#include <swtypes.hxx> +#include <strings.hrc> +#include <wordcountctrl.hxx> +#include <svl/stritem.hxx> +#include <vcl/status.hxx> + +SFX_IMPL_STATUSBAR_CONTROL(SwWordCountStatusBarControl, SfxStringItem); + +SwWordCountStatusBarControl::SwWordCountStatusBarControl( + sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb) : + SfxStatusBarControl(_nSlotId, _nId, rStb) +{ +} + +SwWordCountStatusBarControl::~SwWordCountStatusBarControl() +{ +} + +void SwWordCountStatusBarControl::StateChanged( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if (eState == SfxItemState::DEFAULT) // Can access pState + GetStatusBar().SetItemText( GetId(), static_cast<const SfxStringItem*>(pState)->GetValue() ); + + GetStatusBar().SetQuickHelpText(GetId(), SwResId(STR_WORDCOUNT_HINT)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/utlui/zoomctrl.cxx b/sw/source/uibase/utlui/zoomctrl.cxx new file mode 100644 index 000000000..493b2f9b7 --- /dev/null +++ b/sw/source/uibase/utlui/zoomctrl.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 <vcl/status.hxx> +#include <svl/stritem.hxx> +#include <sfx2/zoomitem.hxx> + +#include <zoomctrl.hxx> + +SFX_IMPL_STATUSBAR_CONTROL( SwZoomControl, SvxZoomItem ); + +SwZoomControl::SwZoomControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SvxZoomStatusBarControl( _nSlotId, _nId, rStb ) +{ +} + +SwZoomControl::~SwZoomControl() +{ +} + +void SwZoomControl::StateChanged( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) +{ + const SfxStringItem* pItem = nullptr; + if (SfxItemState::DEFAULT == eState && (pItem = dynamic_cast<const SfxStringItem*>(pState))) + { + sPreviewZoom = pItem->GetValue(); + GetStatusBar().SetItemText(GetId(), sPreviewZoom); + } + else + { + sPreviewZoom.clear(); + SvxZoomStatusBarControl::StateChanged(nSID, eState, pState); + } +} + +void SwZoomControl::Paint( const UserDrawEvent& ) +{ +} + +void SwZoomControl::Command( const CommandEvent& rCEvt ) +{ + if (sPreviewZoom.isEmpty()) + SvxZoomStatusBarControl::Command(rCEvt); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wdocsh.cxx b/sw/source/uibase/web/wdocsh.cxx new file mode 100644 index 000000000..05242544f --- /dev/null +++ b/sw/source/uibase/web/wdocsh.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <comphelper/fileformat.h> +#include <comphelper/classids.hxx> +#include <sfx2/objface.hxx> +#include <tools/globname.hxx> +#include <osl/diagnose.h> + +#include <sfx2/msg.hxx> +#include <swtypes.hxx> + +#include <wdocsh.hxx> +#include <strings.hrc> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebDocShell +#include <swslots.hxx> + +SFX_IMPL_SUPERCLASS_INTERFACE(SwWebDocShell, SfxObjectShell) + +void SwWebDocShell::InitInterface_Impl() +{ +} + + +SFX_IMPL_OBJECTFACTORY(SwWebDocShell, SvGlobalName(SO3_SWWEB_CLASSID), "swriter/web" ) + +SwWebDocShell::SwWebDocShell() + : SwDocShell(SfxObjectCreateMode::STANDARD) + , m_nSourcePara(0) +{ +} + +SwWebDocShell::~SwWebDocShell() +{ +} + +void SwWebDocShell::FillClass( SvGlobalName * pClassName, + SotClipboardFormatId * pClipFormat, + OUString * pLongUserName, + sal_Int32 nVersion, + bool bTemplate /* = false */) const +{ + OSL_ENSURE( !bTemplate, "No template for Writer Web" ); + + if (nVersion == SOFFICE_FILEFORMAT_60) + { + *pClassName = SvGlobalName( SO3_SWWEB_CLASSID_60 ); + *pClipFormat = SotClipboardFormatId::STARWRITERWEB_60; + *pLongUserName = SwResId(STR_WRITER_WEBDOC_FULLTYPE); + } + else if (nVersion == SOFFICE_FILEFORMAT_8) + { + *pClassName = SvGlobalName( SO3_SWWEB_CLASSID_60 ); + *pClipFormat = SotClipboardFormatId::STARWRITERWEB_8; + *pLongUserName = SwResId(STR_WRITER_WEBDOC_FULLTYPE); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wformsh.cxx b/sw/source/uibase/web/wformsh.cxx new file mode 100644 index 000000000..1d65b62b3 --- /dev/null +++ b/sw/source/uibase/web/wformsh.cxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/msg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/sfxsids.hrc> + +#include <wformsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebDrawFormShell +#include <swslots.hxx> + +SFX_IMPL_SUPERCLASS_INTERFACE(SwWebDrawFormShell, SwDrawFormShell) + +void SwWebDrawFormShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("form"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Text_Toolbox_Sw); +} + + +SwWebDrawFormShell::SwWebDrawFormShell(SwView& rVw) : + SwDrawFormShell(rVw) +{ +} + +SwWebDrawFormShell::~SwWebDrawFormShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wfrmsh.cxx b/sw/source/uibase/web/wfrmsh.cxx new file mode 100644 index 000000000..a9bac1962 --- /dev/null +++ b/sw/source/uibase/web/wfrmsh.cxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/objface.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <wfrmsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebFrameShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebFrameShell, SwFrameShell) + +void SwWebFrameShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("frame"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Webframe_Toolbox); +} + + +SwWebFrameShell::SwWebFrameShell(SwView &_rView) : + SwFrameShell(_rView) +{ +} + +SwWebFrameShell::~SwWebFrameShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wgrfsh.cxx b/sw/source/uibase/web/wgrfsh.cxx new file mode 100644 index 000000000..66fc2235d --- /dev/null +++ b/sw/source/uibase/web/wgrfsh.cxx @@ -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 . + */ + +#include <sfx2/msg.hxx> + +#include <sfx2/objface.hxx> + +#include <cmdid.h> +#include <view.hxx> +#include <wgrfsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebGrfShell +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebGrfShell, SwGrfShell) + +void SwWebGrfShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("graphic"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Webgraphic_Toolbox); +} + + +SwWebGrfShell::SwWebGrfShell(SwView &_rView) : + SwGrfShell(_rView) + +{ + SetName("Graphic"); +} + +SwWebGrfShell::~SwWebGrfShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wlistsh.cxx b/sw/source/uibase/web/wlistsh.cxx new file mode 100644 index 000000000..c7789c1e4 --- /dev/null +++ b/sw/source/uibase/web/wlistsh.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 <sfx2/msg.hxx> +#include <sfx2/objface.hxx> + +#include <cmdid.h> +#include <wlistsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebListShell +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebListShell, SwListShell) + +void SwWebListShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Num_Toolbox); +} + + +SwWebListShell::SwWebListShell(SwView &_rView) : + SwListShell(_rView) +{ + SetName("List"); +} + +SwWebListShell::~SwWebListShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wolesh.cxx b/sw/source/uibase/web/wolesh.cxx new file mode 100644 index 000000000..15bab538b --- /dev/null +++ b/sw/source/uibase/web/wolesh.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 <sfx2/msg.hxx> +#include <sfx2/objface.hxx> + +#include <view.hxx> +#include <wolesh.hxx> + +#define ShellClass_SwWebOleShell +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebOleShell, SwOleShell) + +void SwWebOleShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("oleobject"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Webole_Toolbox); +} + +SwWebOleShell::SwWebOleShell(SwView &_rView) : + SwOleShell(_rView) +{ + SetName("Object"); +} + +SwWebOleShell::~SwWebOleShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wtabsh.cxx b/sw/source/uibase/web/wtabsh.cxx new file mode 100644 index 000000000..88008034c --- /dev/null +++ b/sw/source/uibase/web/wtabsh.cxx @@ -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 . + */ + +#include <sfx2/objface.hxx> + +#include <cmdid.h> +#include <wrtsh.hxx> +#include <wtabsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebTableShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebTableShell, SwTableShell) + +void SwWebTableShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("table"); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Table_Toolbox); +} + + +SwWebTableShell::SwWebTableShell(SwView &_rView) : + SwTableShell(_rView) +{ + GetShell().UpdateTable(); + SetName("Table"); +} + +SwWebTableShell::~SwWebTableShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wtextsh.cxx b/sw/source/uibase/web/wtextsh.cxx new file mode 100644 index 000000000..c655bdf2c --- /dev/null +++ b/sw/source/uibase/web/wtextsh.cxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/objface.hxx> + +#include <cmdid.h> +#include <view.hxx> +#include <wtextsh.hxx> +#include <basesh.hxx> +#include <globals.hrc> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#define ShellClass_SwWebTextShell +#include <sfx2/msg.hxx> +#include <swslots.hxx> + +SFX_IMPL_INTERFACE(SwWebTextShell, SwBaseShell) + +void SwWebTextShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("text"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Text_Toolbox_Sw); + + GetStaticInterface()->RegisterChildWindow(FN_EDIT_FORMULA); + GetStaticInterface()->RegisterChildWindow(FN_INSERT_FIELD); +} + + +SwWebTextShell::SwWebTextShell(SwView &_rView) : + SwTextShell(_rView) +{ +} + +SwWebTextShell::~SwWebTextShell() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/web/wview.cxx b/sw/source/uibase/web/wview.cxx new file mode 100644 index 000000000..2fa09630e --- /dev/null +++ b/sw/source/uibase/web/wview.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/msg.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/infobar.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/srchdlg.hxx> +#include <uivwimp.hxx> +#include <svx/fmshell.hxx> +#include <svx/fmview.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <vcl/inputctx.hxx> + +#include <sfx2/objface.hxx> +#include <swmodule.hxx> +#include <unotxvw.hxx> +#include <cmdid.h> +#include <globals.hrc> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <wgrfsh.hxx> +#include <wfrmsh.hxx> +#include <wolesh.hxx> +#include <wtabsh.hxx> +#include <wlistsh.hxx> +#include <wformsh.hxx> +#include <wtextsh.hxx> +#include <barcfg.hxx> +#include <doc.hxx> + +// TECHNICALLY not possible !! +#include <beziersh.hxx> +#include <drawsh.hxx> +#include <drwtxtsh.hxx> +#include <annotsh.hxx> + +#include <wview.hxx> +#include <wdocsh.hxx> + + // needed for -fsanitize=function visibility of typeinfo for functions of + // type void(SfxShell*,SfxRequest&) defined in swslots.hxx +#include <sfx2/viewfac.hxx> +#define ShellClass_SwWebView +#define ShellClass_Text +#include <swslots.hxx> + +SFX_IMPL_NAMED_VIEWFACTORY(SwWebView, "Default") +{ + SFX_VIEW_REGISTRATION(SwWebDocShell); +} + +SFX_IMPL_INTERFACE(SwWebView, SwView) + +void SwWebView::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard|SfxVisibilityFlags::Server, + ToolbarId::Webtools_Toolbox); +} + + +SwWebView::SwWebView(SfxViewFrame* _pFrame, SfxViewShell* _pShell) : + SwView(_pFrame, _pShell) +{ +} + +SwWebView::~SwWebView() +{ +} + +void SwWebView::SelectShell() +{ +#if HAVE_FEATURE_DESKTOP + // Decision whether UpdateTable must be called + bool bUpdateTable = false; + const SwFrameFormat* pCurTableFormat = GetWrtShell().GetTableFormat(); + if(pCurTableFormat && pCurTableFormat != GetLastTableFrameFormat()) + { + bUpdateTable = true; // can only be executed later + } + SetLastTableFrameFormat(pCurTableFormat); + //SEL_TBL and SEL_TBL_CELLS can be ored! + SelectionType nNewSelectionType = GetWrtShell().GetSelectionType() + & ~SelectionType::TableCell; + + SelectionType _nSelectionType = GetSelectionType(); + if ( nNewSelectionType == _nSelectionType ) + { + GetViewFrame()->GetBindings().InvalidateAll( false ); + if ( _nSelectionType & SelectionType::Ole || + _nSelectionType & SelectionType::Graphic ) + //The verb may of course change for graphics and OLE! + ImpSetVerb( nNewSelectionType ); + } + else + { + SfxDispatcher &rDispatcher = *GetViewFrame()->GetDispatcher(); + SwToolbarConfigItem *pBarCfg = SW_MOD()->GetWebToolbarConfig(); + + if( GetCurShell() ) + { + rDispatcher.Flush(); // really delete all cached shells + + //Additional to the old selection remember which toolbar was visible. + ToolbarId eId = rDispatcher.GetObjectBarId(SFX_OBJECTBAR_OBJECT); + if (eId != ToolbarId::None) + pBarCfg->SetTopToolbar( _nSelectionType, eId ); + + SfxShell *pSfxShell; + sal_uInt16 i; + for ( i = 0; true; ++i ) + { + pSfxShell = rDispatcher.GetShell( i ); + if ( !( dynamic_cast< const SwBaseShell *>( pSfxShell ) != nullptr || + dynamic_cast< const SwDrawTextShell *>( pSfxShell ) || dynamic_cast< const SwAnnotationShell *>( pSfxShell ) != nullptr ) ) + break; + } + if (i) + { + pSfxShell = rDispatcher.GetShell( --i ); + OSL_ENSURE( pSfxShell, "My Shell is lost in space" ); + rDispatcher.Pop( *pSfxShell, SfxDispatcherPopFlags::POP_UNTIL | SfxDispatcherPopFlags::POP_DELETE); + } + } + + bool bInitFormShell = false; + if( !GetFormShell() ) + { + bInitFormShell = true; + SetFormShell( new FmFormShell( this ) ); + rDispatcher.Push( *GetFormShell() ); + } + + bool bSetExtInpCntxt = false; + _nSelectionType = nNewSelectionType; + SetSelectionType( _nSelectionType ); + ShellMode eShellMode; + + if ( _nSelectionType & SelectionType::Ole ) + { + eShellMode = ShellMode::Object; + SetShell( new SwWebOleShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + else if ( _nSelectionType & SelectionType::Frame + || _nSelectionType & SelectionType::Graphic) + { + eShellMode = ShellMode::Frame; + SetShell( new SwWebFrameShell( *this )); + rDispatcher.Push( *GetCurShell() ); + if(_nSelectionType & SelectionType::Graphic ) + { + eShellMode = ShellMode::Graphic; + SetShell( new SwWebGrfShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + } + else if ( _nSelectionType & SelectionType::Frame ) + { + eShellMode = ShellMode::Frame; + SetShell( new SwWebFrameShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + else if ( _nSelectionType & SelectionType::DrawObject ) + { + eShellMode = ShellMode::Draw; + SetShell( new svx::ExtrusionBar( this ) ); + rDispatcher.Push( *GetCurShell() ); + + SetShell( new svx::FontworkBar( this ) ); + rDispatcher.Push( *GetCurShell() ); + + SetShell( new SwDrawShell( *this )); + rDispatcher.Push( *GetCurShell() ); + if ( _nSelectionType & SelectionType::Ornament ) + { + eShellMode = ShellMode::Bezier; + SetShell( new SwBezierShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + + } + else if ( _nSelectionType & SelectionType::DbForm ) + { + eShellMode = ShellMode::DrawForm; + SetShell( new SwWebDrawFormShell( *this )); + + rDispatcher.Push( *GetCurShell() ); + } + else if ( _nSelectionType & SelectionType::DrawObjectEditMode ) + { + eShellMode = ShellMode::DrawText; + rDispatcher.Push( *(new SwBaseShell( *this )) ); + SetShell( new SwDrawTextShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + else if ( _nSelectionType & SelectionType::PostIt ) + { + eShellMode = ShellMode::PostIt; + SetShell( new SwAnnotationShell( *this ) ); + rDispatcher.Push( *GetCurShell() ); + } + else + { + bSetExtInpCntxt = true; + eShellMode = ShellMode::Text; + if ( _nSelectionType & SelectionType::NumberList ) + { + eShellMode = ShellMode::ListText; + SetShell( new SwWebListShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + SetShell( new SwWebTextShell(*this)); + rDispatcher.Push( *GetCurShell() ); + if ( _nSelectionType & SelectionType::Table ) + { + eShellMode = eShellMode == ShellMode::ListText ? ShellMode::TableListText + : ShellMode::TableText; + SetShell( new SwWebTableShell( *this )); + rDispatcher.Push( *GetCurShell() ); + } + } + ImpSetVerb( _nSelectionType ); + GetViewImpl()->SetShellMode(eShellMode); + + if( !GetDocShell()->IsReadOnly() ) + { + if( bSetExtInpCntxt && GetWrtShell().HasReadonlySel() ) + bSetExtInpCntxt = false; + + InputContext aCntxt( GetEditWin().GetInputContext() ); + aCntxt.SetOptions( bSetExtInpCntxt + ? (aCntxt.GetOptions() | + ( InputContextFlags::Text | + InputContextFlags::ExtText )) + : (aCntxt.GetOptions() & ~ + InputContextFlags( InputContextFlags::Text | + InputContextFlags::ExtText )) ); + GetEditWin().SetInputContext( aCntxt ); + } + + //Additional to the selection enable the toolbar, which was + //activated last time + //Before must be a Flush(), but concerns according to MBA not the + //user interface and is not a performance issue. + // TODO/LATER: maybe now the Flush() command is superfluous?! + rDispatcher.Flush(); + + Point aPnt = GetEditWin().GetPointerPosPixel(); + aPnt = GetEditWin().PixelToLogic(aPnt); + GetEditWin().UpdatePointer(aPnt); + + if ( bInitFormShell && GetWrtShell().GetDrawView() ) + GetFormShell()->SetView( dynamic_cast< FmFormView* >( + GetWrtShell().GetDrawView())); + + } + GetViewImpl()->GetUNOObject_Impl()->NotifySelChanged(); + + //Opportune time for the communication with OLE objects? + if ( GetDocShell()->GetDoc()->IsOLEPrtNotifyPending() ) + GetDocShell()->GetDoc()->PrtOLENotify( false ); + + //now the table update + if(bUpdateTable) + GetWrtShell().UpdateTable(); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/delete.cxx b/sw/source/uibase/wrtsh/delete.cxx new file mode 100644 index 000000000..0dbc7eb89 --- /dev/null +++ b/sw/source/uibase/wrtsh/delete.cxx @@ -0,0 +1,631 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <wrtsh.hxx> +#include <swcrsr.hxx> +#include <editeng/lrspitem.hxx> +#include <view.hxx> +#include <drawbase.hxx> +#include <unobaseclass.hxx> +#include <fmtanchr.hxx> +#include <flyfrm.hxx> +#include <ndtxt.hxx> +#include <txtfld.hxx> +#include <docufld.hxx> +#include <IDocumentUndoRedo.hxx> +#include <i18nutil/unicode.hxx> +#include <rtl/character.hxx> +#include <doc.hxx> + +inline void SwWrtShell::OpenMark() +{ + StartAllAction(); + ResetCursorStack(); + KillPams(); + SetMark(); +} + +inline void SwWrtShell::CloseMark( bool bOkFlag ) +{ + if( bOkFlag ) + UpdateAttr(); + else + SwapPam(); + + ClearMark(); + EndAllAction(); +} + + + +// #i23725# +bool SwWrtShell::TryRemoveIndent() +{ + bool bResult = false; + + SfxItemSet aAttrSet(GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{}); + GetCurAttr(aAttrSet); + + SvxLRSpaceItem aItem = aAttrSet.Get(RES_LR_SPACE); + short aOldFirstLineOfst = aItem.GetTextFirstLineOffset(); + + if (aOldFirstLineOfst > 0) + { + aItem.SetTextFirstLineOffset(0); + bResult = true; + } + else if (aOldFirstLineOfst < 0) + { + aItem.SetTextFirstLineOffset(0); + aItem.SetLeft(aItem.GetLeft() + aOldFirstLineOfst); + + bResult = true; + } + else if (aItem.GetLeft() != 0) + { + aItem.SetLeft(0); + bResult = true; + } + + if (bResult) + { + aAttrSet.Put(aItem); + SetAttrSet(aAttrSet); + } + + return bResult; +} + +/** Description: Erase the line. */ + +void SwWrtShell::DelLine() +{ + SwActContext aActContext(this); + ResetCursorStack(); + // remember the old cursor + Push(); + ClearMark(); + SwCursorShell::LeftMargin(); + SetMark(); + SwCursorShell::RightMargin(); + + bool bRet = Delete(); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +void SwWrtShell::DelToStartOfLine() +{ + OpenMark(); + SwCursorShell::LeftMargin(); + bool bRet = Delete(); + CloseMark( bRet ); +} + +void SwWrtShell::DelToEndOfLine() +{ + OpenMark(); + SwCursorShell::RightMargin(); + bool bRet = Delete(); + CloseMark( bRet ); +} + +bool SwWrtShell::DelLeft() +{ + // If it's a Fly, throw it away + SelectionType nSelType = GetSelectionType(); + const SelectionType nCmp = SelectionType::Frame | SelectionType::Graphic | SelectionType::Ole | SelectionType::DrawObject; + if( nCmp & nSelType ) + { + // #108205# Remember object's position. + Point aTmpPt = GetObjRect().TopLeft(); + + DelSelectedObj(); + + // #108205# Set cursor to remembered position. + SetCursor(&aTmpPt); + + LeaveSelFrameMode(); + UnSelectFrame(); + + nSelType = GetSelectionType(); + if ( nCmp & nSelType ) + { + EnterSelFrameMode(); + GotoNextFly(); + } + + return true; + } + + // If a selection exists, erase this + if ( IsSelection() ) + { + if( !IsBlockMode() || HasSelection() ) + { + //OS: Once again Basic: SwActContext must be leaved + //before EnterStdMode! + { + SwActContext aActContext(this); + ResetCursorStack(); + Delete(); + UpdateAttr(); + } + if( IsBlockMode() ) + { + NormalizePam(); + ClearMark(); + EnterBlockMode(); + } + else + EnterStdMode(); + return true; + } + else + EnterStdMode(); + } + + // JP 29.06.95: never erase a table standing in front of it. + bool bSwap = false; + const SwTableNode * pWasInTableNd = SwCursorShell::IsCursorInTable(); + + if( SwCursorShell::IsSttPara()) + { + // Start/EndAllAction to avoid cursor flickering + UnoActionContext c(GetDoc()); + SwCursorShell::Push(); + + // #i4032# Don't actually call a 'delete' if we + // changed the table cell, compare DelRight(). + const SwStartNode * pSNdOld = pWasInTableNd ? + GetSwCursor()->GetNode().FindTableBoxStartNode() : + nullptr; + + // If the cursor is at the beginning of a paragraph, try to step + // backwards. On failure we are done. + bool bDoSomething = SwCursorShell::Left(1,CRSR_SKIP_CHARS); + + if (bDoSomething) + { + // If the cursor entered or left a table (or both) we are done. + const SwTableNode* pIsInTableNd = SwCursorShell::IsCursorInTable(); + bDoSomething = pIsInTableNd == pWasInTableNd; + + if (bDoSomething) + { + const SwStartNode* pSNdNew = pIsInTableNd ? + GetSwCursor()->GetNode().FindTableBoxStartNode() : + nullptr; + + // #i4032# Don't actually call a 'delete' if we + // changed the table cell, compare DelRight(). + bDoSomething = pSNdOld == pSNdNew; + } + } + + if (!bDoSomething) + { + // tdf#115132 Restore previous position and we are done + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteStack); + + OpenMark(); + SwCursorShell::Right(1,CRSR_SKIP_CHARS); + SwCursorShell::SwapPam(); + bSwap = true; + } + else + { + // If we are just to the right to a fieldmark, then remove it completely + const SwPosition* pCurPos = GetCursor()->GetPoint(); + SwPosition aPrevChar(*pCurPos); + --aPrevChar.nContent; + sw::mark::IFieldmark* pFm = getIDocumentMarkAccess()->getFieldmarkAt(aPrevChar); + if (pFm && pFm->GetMarkEnd() == *pCurPos) + { + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm); + getIDocumentMarkAccess()->deleteMark(pFm); + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + return true; + } + + OpenMark(); + SwCursorShell::Left(1, CRSR_SKIP_CHARS); + if (SvtScriptType::ASIAN == GetScriptType()) + { + sal_uInt32 nCode = GetChar(false); + if ( rtl::isSurrogate( nCode ) ) + { + OUString sStr = GetSelText(); + sal_Int32 nIndex = 0; + nCode = sStr.iterateCodePoints( &nIndex ); + } + + if ( unicode::isIVSSelector( nCode ) ) + { + SwCursorShell::Push(); + SwCursorShell::Left(1, CRSR_SKIP_CHARS); + OUString sStr = GetSelText(); + sal_Int32 nIndex = 0; + nCode = sStr.iterateCodePoints( &nIndex ); + if ( unicode::isCJKIVSCharacter( nCode ) ) + SwCursorShell::Pop( SwCursorShell::PopMode::DeleteStack ); + else + SwCursorShell::Pop( SwCursorShell::PopMode::DeleteCurrent ); // For the weak script. + } + } + } + bool bRet = Delete(); + if( !bRet && bSwap ) + SwCursorShell::SwapPam(); + CloseMark( bRet ); + if (!bRet) + { // false indicates HasReadonlySel failed + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xInfo->run(); + } + return bRet; +} + +bool SwWrtShell::DelRight() +{ + // Will be or'ed, if a tableselection exists; + // will here be implemented on SelectionType::Table + bool bRet = false; + SelectionType nSelection = GetSelectionType(); + if(nSelection & SelectionType::TableCell) + nSelection = SelectionType::Table; + if(nSelection & SelectionType::Text) + nSelection = SelectionType::Text; + + switch( nSelection & ~SelectionType::Ornament ) + { + case SelectionType::PostIt: + case SelectionType::Text: + case SelectionType::Table: + case SelectionType::NumberList: + // If a selection exists, erase it. + if( IsSelection() ) + { + if( !IsBlockMode() || HasSelection() ) + { + //OS: And once again Basic: SwActContext must be + //leaved before EnterStdMode ! + { + SwActContext aActContext(this); + ResetCursorStack(); + Delete(); + UpdateAttr(); + } + if( IsBlockMode() ) + { + NormalizePam(); + ClearMark(); + EnterBlockMode(); + } + else + EnterStdMode(); + bRet = true; + break; + } + else + EnterStdMode(); + } + + if (SwCursorShell::IsEndPara()) + { + // Start/EndAllAction to avoid cursor flickering + UnoActionContext c(GetDoc()); + + const SwTableNode* pWasInTableNd = IsCursorInTable(); + // #108049# Save the startnode of the current cell + const SwStartNode* pSNdOld = pWasInTableNd ? + GetSwCursor()->GetNode().FindTableBoxStartNode() : nullptr; + bool bCheckDelFull = SelectionType::Text & nSelection && SwCursorShell::IsSttPara(); + bool bDelFull = false; + bool bDoNothing = false; + + // #i41424# Introduced a couple of + // Push()-Pop() pairs here. The reason for this is that a + // Right()-Left() combination does not make sure, that + // the cursor will be in its initial state, because there + // may be a numbering in front of the next paragraph. + SwCursorShell::Push(); + + if (SwCursorShell::Right(1, CRSR_SKIP_CHARS)) + { + const SwTableNode* pCurrTableNd = IsCursorInTable(); + bDelFull = bCheckDelFull && pCurrTableNd && pCurrTableNd != pWasInTableNd; + if (!bDelFull && (IsCursorInTable() || (pCurrTableNd != pWasInTableNd))) + { + // #108049# Save the startnode of the current cell. + // May be different to pSNdOld as we have moved. + const SwStartNode* pSNdNew = pCurrTableNd ? + GetSwCursor()->GetNode().FindTableBoxStartNode() : nullptr; + + // tdf#115132 Only keep cursor position instead of deleting + // if we have moved to a different cell + bDoNothing = pSNdOld != pSNdNew; + } + } + + // restore cursor + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + + if (bDelFull) + { + DelFullPara(); + UpdateAttr(); + } + if (bDelFull || bDoNothing) + break; + } + + { + // If we are just ahead of a fieldmark, then remove it completely + sw::mark::IFieldmark *const pFm = getIDocumentMarkAccess()->getFieldmarkAt(*GetCursor()->GetPoint()); + if (pFm && pFm->GetMarkStart() == *GetCursor()->GetPoint()) + { + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + IDocumentMarkAccess::DeleteFieldmarkCommand(*pFm); + getIDocumentMarkAccess()->deleteMark(pFm); + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + bRet = true; + break; + } + } + + OpenMark(); + SwCursorShell::Right(1, CRSR_SKIP_CELLS); + bRet = Delete(); + CloseMark( bRet ); + if (!bRet) + { // false indicates HasReadonlySel failed + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetView().GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui")); + std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog")); + xInfo->run(); + } + break; + + case SelectionType::Frame: + case SelectionType::Graphic: + case SelectionType::Ole: + case SelectionType::DrawObject: + case SelectionType::DrawObjectEditMode: + case SelectionType::DbForm: + { + // #108205# Remember object's position. + Point aTmpPt = GetObjRect().TopLeft(); + + // Remember the anchor of the selected object before deletion. + std::unique_ptr<SwPosition> pAnchor; + SwFlyFrame* pFly = GetSelectedFlyFrame(); + if (pFly) + { + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat) + { + RndStdIds eAnchorId = pFormat->GetAnchor().GetAnchorId(); + if ((eAnchorId == RndStdIds::FLY_AS_CHAR || eAnchorId == RndStdIds::FLY_AT_CHAR) + && pFormat->GetAnchor().GetContentAnchor()) + { + pAnchor.reset(new SwPosition(*pFormat->GetAnchor().GetContentAnchor())); + } + } + } + + // Group deletion of the object and its comment together. + mxDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); + + DelSelectedObj(); + + if (pAnchor) + { + SwTextNode* pTextNode = pAnchor->nNode.GetNode().GetTextNode(); + if (pTextNode) + { + const SwTextField* pField( + pTextNode->GetFieldTextAttrAt(pAnchor->nContent.GetIndex(), true)); + if (pField + && dynamic_cast<const SwPostItField*>(pField->GetFormatField().GetField())) + { + // Remove the comment of the deleted object. + *GetCurrentShellCursor().GetPoint() = *pAnchor; + DelRight(); + } + } + } + + mxDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); + + // #108205# Set cursor to remembered position. + SetCursor(&aTmpPt); + + LeaveSelFrameMode(); + UnSelectFrame(); + OSL_ENSURE( !IsFrameSelected(), + "<SwWrtShell::DelRight(..)> - <SwWrtShell::UnSelectFrame()> should unmark all objects" ); + // leave draw mode, if necessary. + { + if (GetView().GetDrawFuncPtr()) + { + GetView().GetDrawFuncPtr()->Deactivate(); + GetView().SetDrawFuncPtr(nullptr); + } + if ( GetView().IsDrawMode() ) + { + GetView().LeaveDrawCreate(); + } + } + } + + // <IsFrameSelected()> can't be true - see above. + { + nSelection = GetSelectionType(); + if ( SelectionType::Frame & nSelection || + SelectionType::Graphic & nSelection || + SelectionType::Ole & nSelection || + SelectionType::DrawObject & nSelection ) + { + EnterSelFrameMode(); + GotoNextFly(); + } + } + bRet = true; + break; + default: break; + } + return bRet; +} + +void SwWrtShell::DelToEndOfPara() +{ + SwActContext aActContext(this); + ResetCursorStack(); + Push(); + SetMark(); + if( !MovePara(GoCurrPara,fnParaEnd)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return; + } + bool bRet = Delete(); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +void SwWrtShell::DelToStartOfPara() +{ + SwActContext aActContext(this); + ResetCursorStack(); + Push(); + SetMark(); + if( !MovePara(GoCurrPara,fnParaStart)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return; + } + bool bRet = Delete(); + Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bRet ) + UpdateAttr(); +} + +// All erase operations should work with Find instead with +// Nxt-/PrvDelim, because the latter works with Wrap Around +// -- that's probably not wished. + +void SwWrtShell::DelToStartOfSentence() +{ + if(IsStartOfDoc()) + return; + OpenMark(); + bool bRet = BwdSentence_() && Delete(); + CloseMark( bRet ); +} + +bool SwWrtShell::DelToEndOfSentence() +{ + if(IsEndOfDoc()) + return false; + OpenMark(); + bool bRet(false); + // fdo#60967: special case that is documented in help: delete + // paragraph following table if cursor is at end of last cell in table + if (IsEndOfTable()) + { + Push(); + ClearMark(); + if (SwCursorShell::Right(1,CRSR_SKIP_CHARS)) + { + SetMark(); + if (!IsEndPara()) // can only be at the end if it's empty + { // for an empty paragraph this would actually select the _next_ + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + } + if (!IsEndOfDoc()) // do not delete last paragraph in body text + { + bRet = DelFullPara(); + } + } + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + else + { + bRet = FwdSentence_() && Delete(); + } + CloseMark( bRet ); + return bRet; +} + +void SwWrtShell::DelNxtWord() +{ + if(IsEndOfDoc()) + return; + SwActContext aActContext(this); + ResetCursorStack(); + EnterStdMode(); + SetMark(); + if(IsEndWrd() && !IsStartWord()) + NxtWrdForDelete(); // #i92468# + if(IsStartWord() || IsEndPara()) + NxtWrdForDelete(); // #i92468# + else + EndWrd(); + + bool bRet = Delete(); + if( bRet ) + UpdateAttr(); + else + SwapPam(); + ClearMark(); +} + +void SwWrtShell::DelPrvWord() +{ + if(IsStartOfDoc()) + return; + SwActContext aActContext(this); + ResetCursorStack(); + EnterStdMode(); + SetMark(); + if ( !IsStartWord() || + !PrvWrdForDelete() ) // #i92468# + { + if (IsEndWrd() || IsSttPara()) + PrvWrdForDelete(); // #i92468# + else + SttWrd(); + } + bool bRet = Delete(); + if( bRet ) + UpdateAttr(); + else + SwapPam(); + ClearMark(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx new file mode 100644 index 000000000..14070c71e --- /dev/null +++ b/sw/source/uibase/wrtsh/move.cxx @@ -0,0 +1,691 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <viewopt.hxx> +#include <drawbase.hxx> + +/** + Always: + - Reset of the cursor stack + - retrigger timer + - if applicable: GCAttr + + on selection + - SttSelect() + + else + - EndSelect() + */ + +const long nReadOnlyScrollOfst = 10; + +namespace { + +class ShellMoveCursor +{ + SwWrtShell* pSh; + bool bAct; +public: + ShellMoveCursor( SwWrtShell* pWrtSh, bool bSel ) + { + bAct = !pWrtSh->ActionPend() && (pWrtSh->GetFrameType(nullptr,false) & FrameTypeFlags::FLY_ANY); + pSh = pWrtSh; + pSh->MoveCursor( bSel ); + pWrtSh->GetView().GetViewFrame()->GetBindings().Invalidate(SID_HYPERLINK_GETLINK); + } + ~ShellMoveCursor() COVERITY_NOEXCEPT_FALSE + { + if( bAct ) + { + // The action is used for scrolling in "single paragraph" + // frames with fixed height. + pSh->StartAllAction(); + pSh->EndAllAction(); + } + } +}; + +} + +void SwWrtShell::MoveCursor( bool bWithSelect ) +{ + ResetCursorStack(); + if ( IsGCAttr() ) + { + GCAttr(); + ClearGCAttr(); + } + if ( bWithSelect ) + SttSelect(); + else + { + EndSelect(); + (this->*m_fnKillSel)( nullptr, false ); + } +} + +bool SwWrtShell::SimpleMove( FNSimpleMove FnSimpleMove, bool bSelect ) +{ + bool bRet; + if( bSelect ) + { + SttCursorMove(); + MoveCursor( true ); + bRet = (this->*FnSimpleMove)(); + EndCursorMove(); + } + else + { + bRet = (this->*FnSimpleMove)(); + if( bRet ) + MoveCursor(); + } + return bRet; +} + +bool SwWrtShell::Left( sal_uInt16 nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustX( -(VisArea().Width() * nReadOnlyScrollOfst / 100) ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Left( nCount, nMode, bVisual ); + } +} + +bool SwWrtShell::Right( sal_uInt16 nMode, bool bSelect, + sal_uInt16 nCount, bool bBasicCall, bool bVisual ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustX(VisArea().Width() * nReadOnlyScrollOfst / 100 ); + aTmp.setX( m_rView.SetHScrollMax( aTmp.X() ) ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Right( nCount, nMode, bVisual ); + } +} + +bool SwWrtShell::Up( bool bSelect, sal_uInt16 nCount, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustY( -(VisArea().Height() * nReadOnlyScrollOfst / 100) ); + m_rView.SetVisArea( aTmp ); + return true; + } + + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Up(nCount); +} + +bool SwWrtShell::Down( bool bSelect, sal_uInt16 nCount, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() && !GetViewOptions()->IsSelectionInReadonly()) + { + Point aTmp( VisArea().Pos() ); + aTmp.AdjustY(VisArea().Height() * nReadOnlyScrollOfst / 100 ); + aTmp.setY( m_rView.SetVScrollMax( aTmp.Y() ) ); + m_rView.SetVisArea( aTmp ); + return true; + } + + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::Down(nCount); +} + +bool SwWrtShell::LeftMargin( bool bSelect, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.setX( DOCUMENTBORDER ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::LeftMargin(); + } +} + +bool SwWrtShell::RightMargin( bool bSelect, bool bBasicCall ) +{ + if ( !bSelect && !bBasicCall && IsCursorReadonly() ) + { + Point aTmp( VisArea().Pos() ); + aTmp.setX( GetDocSize().Width() - VisArea().Width() + DOCUMENTBORDER ); + if( DOCUMENTBORDER > aTmp.X() ) + aTmp.setX( DOCUMENTBORDER ); + m_rView.SetVisArea( aTmp ); + return true; + } + else + { + ShellMoveCursor aTmp( this, bSelect ); + return SwCursorShell::RightMargin(bBasicCall); + } +} + +bool SwWrtShell::GoStart( bool bKeepArea, bool *pMoveTable, + bool bSelect, bool bDontMoveRegion ) +{ + if ( IsCursorInTable() ) + { + const bool bBoxSelection = HasBoxSelection(); + if( !m_bBlockMode ) + { + if ( !bSelect ) + EnterStdMode(); + else + SttSelect(); + } + // Table cell ? + if ( !bBoxSelection && (MoveSection( GoCurrSection, fnSectionStart) + || bDontMoveRegion)) + { + if ( pMoveTable ) + *pMoveTable = false; + return true; + } + if( MoveTable( GotoCurrTable, fnTableStart ) || bDontMoveRegion ) + { + if ( pMoveTable ) + *pMoveTable = true; + return true; + } + else if( bBoxSelection && pMoveTable ) + { + // JP 09.01.96: We have a box selection (or an empty cell) + // and we want select (pMoveTable will be + // set in SelAll). Then the table must not + // be left, otherwise there is no selection + // of the entire table possible! + *pMoveTable = true; + return true; + } + } + + if( !m_bBlockMode ) + { + if ( !bSelect ) + EnterStdMode(); + else + SttSelect(); + } + const FrameTypeFlags nFrameType = GetFrameType(nullptr,false); + if ( FrameTypeFlags::FLY_ANY & nFrameType ) + { + if( MoveSection( GoCurrSection, fnSectionStart ) ) + return true; + else if ( FrameTypeFlags::FLY_FREE & nFrameType || bDontMoveRegion ) + return false; + } + if(( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE ) & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionStart ) ) + return true; + else if ( bKeepArea ) + return true; + } + // Regions ??? + return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionStart ) || + SwCursorShell::SttEndDoc(true); +} + +bool SwWrtShell::GoEnd(bool bKeepArea, const bool *pMoveTable) +{ + if ( pMoveTable && *pMoveTable ) + return MoveTable( GotoCurrTable, fnTableEnd ); + + if ( IsCursorInTable() ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd ) || + MoveTable( GotoCurrTable, fnTableEnd ) ) + return true; + } + else + { + const FrameTypeFlags nFrameType = GetFrameType(nullptr,false); + if ( FrameTypeFlags::FLY_ANY & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd ) ) + return true; + else if ( FrameTypeFlags::FLY_FREE & nFrameType ) + return false; + } + if(( FrameTypeFlags::HEADER | FrameTypeFlags::FOOTER | FrameTypeFlags::FOOTNOTE ) & nFrameType ) + { + if ( MoveSection( GoCurrSection, fnSectionEnd) ) + return true; + else if ( bKeepArea ) + return true; + } + } + // Regions ??? + return SwCursorShell::MoveRegion( GotoCurrRegionAndSkip, fnRegionEnd ) || + SwCursorShell::SttEndDoc(false); +} + +bool SwWrtShell::StartOfSection(bool const bSelect) +{ + ShellMoveCursor aTmp( this, bSelect ); + return GoStart(false, nullptr, bSelect ); +} + +bool SwWrtShell::EndOfSection(bool const bSelect) +{ + ShellMoveCursor aTmp( this, bSelect ); + return GoEnd(); +} + +bool SwWrtShell::SttNxtPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetNextFrame, GetFirstSub ); +} + +void SwWrtShell::SttPrvPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePage( GetPrevFrame, GetFirstSub ); +} + +void SwWrtShell::EndNxtPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePage( GetNextFrame, GetLastSub ); +} + +bool SwWrtShell::EndPrvPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetPrevFrame, GetLastSub ); +} + +bool SwWrtShell::SttPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetThisFrame, GetFirstSub ); +} + +bool SwWrtShell::EndPg( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePage( GetThisFrame, GetLastSub ); +} + +bool SwWrtShell::SttPara( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + return MovePara( GoCurrPara, fnParaStart ); +} + +void SwWrtShell::EndPara( bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + MovePara(GoCurrPara,fnParaEnd); +} + +// Column-by-jumping. +// SSelection with or without +// returns success or failure + +void SwWrtShell::StartOfColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetCurrColumn, GetColumnStt); +} + +void SwWrtShell::EndOfColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetCurrColumn, GetColumnEnd); +} + +void SwWrtShell::StartOfNextColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn( GetNextColumn, GetColumnStt); +} + +void SwWrtShell::EndOfNextColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetNextColumn, GetColumnEnd); +} + +void SwWrtShell::StartOfPrevColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetPrevColumn, GetColumnStt); +} + +void SwWrtShell::EndOfPrevColumn() +{ + ShellMoveCursor aTmp( this, false/*bSelect*/); + MoveColumn(GetPrevColumn, GetColumnEnd); +} + +bool SwWrtShell::PushCursor(SwTwips lOffset, bool bSelect) +{ + bool bDiff = false; + SwRect aOldRect( GetCharRect() ), aTmpArea( VisArea() ); + + // m_bDestOnStack indicates if I could not set the coursor at the current + // position, because in this region is no content. + if( !m_bDestOnStack ) + { + Point aPt( aOldRect.Center() ); + + if( !IsCursorVisible() ) + // set CursorPos to top-/bottom left pos. So the pagescroll is not + // be dependent on the current cursor, but on the visarea. + aPt.setY( aTmpArea.Top() + aTmpArea.Height() / 2 ); + + aPt.AdjustY(lOffset ); + m_aDest = GetContentPos(aPt,lOffset > 0); + m_aDest.setX( aPt.X() ); + m_bDestOnStack = true; + } + + // If we had a frame selection, it must be removed after the m_fnSetCursor + // and we have to remember the position on the stack to return to it later. + bool bIsFrameSel = false; + + //Target position is now within the viewable region --> + //Place the cursor at the target position; remember that no target + //position is longer on the stack. + //The new visible region is to be determined beforehand. + aTmpArea.Pos().AdjustY(lOffset ); + if( aTmpArea.IsInside(m_aDest) ) + { + if( bSelect ) + SttSelect(); + else + EndSelect(); + + bIsFrameSel = IsFrameSelected(); + bool bIsObjSel = 0 != IsObjSelected(); + + // unselect frame + if( bIsFrameSel || bIsObjSel ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + if ( bIsObjSel ) + { + GetView().SetDrawFuncPtr( nullptr ); + GetView().LeaveDrawCreate(); + } + + CallChgLnk(); + } + + (this->*m_fnSetCursor)( &m_aDest, true ); + + bDiff = aOldRect != GetCharRect(); + + if( bIsFrameSel ) + { + // In frames take only the upper corner + // so that it can be re-selected. + aOldRect.SSize( 5, 5 ); + } + + // reset Dest. SPoint Flags + m_bDestOnStack = false; + } + + // Position into the stack; bDiff indicates if there is a + // difference between the old and the new cursor position. + m_pCursorStack.reset( new CursorStack( bDiff, bIsFrameSel, aOldRect.Center(), + lOffset, std::move(m_pCursorStack) ) ); + return !m_bDestOnStack && bDiff; +} + +bool SwWrtShell::PopCursor(bool bUpdate, bool bSelect) +{ + if( nullptr == m_pCursorStack) + return false; + + const bool bValidPos = m_pCursorStack->bValidCurPos; + if( bUpdate && bValidPos ) + { + // If a predecessor is on the stack, + // use the flag for a valid position. + SwRect aTmpArea(VisArea()); + aTmpArea.Pos().AdjustY( -(m_pCursorStack->lOffset) ); + if( aTmpArea.IsInside( m_pCursorStack->aDocPos ) ) + { + if( bSelect ) + SttSelect(); + else + EndSelect(); + + (this->*m_fnSetCursor)(&m_pCursorStack->aDocPos, !m_pCursorStack->bIsFrameSel); + if( m_pCursorStack->bIsFrameSel && IsObjSelectable(m_pCursorStack->aDocPos)) + { + HideCursor(); + SelectObj( m_pCursorStack->aDocPos ); + EnterSelFrameMode( &m_pCursorStack->aDocPos ); + } + } + // If a discrepancy between the visible range and the + // remembered cursor position occurs, all of the remembered + // positions are thrown away. + else + { + ResetCursorStack_(); + return false; + } + } + m_pCursorStack = std::move(m_pCursorStack->pNext); + if( nullptr == m_pCursorStack ) + { + m_ePageMove = MV_NO; + m_bDestOnStack = false; + } + return bValidPos; +} + +// Reset of all pushed cursor positions; these will +// not be displayed ( --> No Start-/EndAction!!) + +void SwWrtShell::ResetCursorStack_() +{ + while(m_pCursorStack) + m_pCursorStack = std::move(m_pCursorStack->pNext); + m_ePageMove = MV_NO; + m_bDestOnStack = false; +} +/** + if no stack exists --> cancel selection + if stack && change of direction + --> pop cursor and return + else + --> push cursor + transpose cursor +*/ + +bool SwWrtShell::PageCursor(SwTwips lOffset, bool bSelect) +{ + // Do nothing if an offset of 0 was indicated + if(!lOffset) return false; + // Was once used to force a reformat of the layout. + // This has not work that way, because the cursor was not set + // because this does not happen within a + // Start-/EndActionParentheses. + // Because only SwViewShell::EndAction() is called at the end, + // no updating of the display of the cursor position takes place. + // The CursorShell-Actionparentheses cannot be used, because it + // always leads to displaying the cursor, thus also, + // if after the scroll scrolled in a region without a valid position. + // SwViewShell::StartAction(); + PageMove eDir = lOffset > 0? MV_PAGE_DOWN: MV_PAGE_UP; + // Change of direction and stack present + if( eDir != m_ePageMove && m_ePageMove != MV_NO && PopCursor( true, bSelect )) + return true; + + const bool bRet = PushCursor(lOffset, bSelect); + m_ePageMove = eDir; + return bRet; +} + +bool SwWrtShell::GotoPage(sal_uInt16 nPage, bool bRecord) +{ + ShellMoveCursor aTmp( this, false); + if( SwCursorShell::GotoPage(nPage) && bRecord) + { + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + return true; + } + return false; +} + +bool SwWrtShell::GotoMark( const ::sw::mark::IMark* const pMark, bool bSelect ) +{ + ShellMoveCursor aTmp( this, bSelect ); + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoMark( pMark, true/*bStart*/ ); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoFly( const OUString& rName, FlyCntType eType, bool bSelFrame ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwFEShell::GotoFly(rName, eType, bSelFrame); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoINetAttr( const SwTextINetFormat& rAttr ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoINetAttr(rAttr); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +void SwWrtShell::GotoOutline( SwOutlineNodes::size_type nIdx ) +{ + addCurrentPosition(); + SwCursorShell::GotoOutline (nIdx); +} + +bool SwWrtShell::GotoOutline( const OUString& rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoOutline (rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoRegion( const OUString& rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoRegion (rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; + } + +bool SwWrtShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType, + sal_uInt16 nSeqNo ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoRefMark(rRefMark, nSubType, nSeqNo); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoNextTOXBase( const OUString* pName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoNextTOXBase(pName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +bool SwWrtShell::GotoTable( const OUString& rName ) +{ + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoTable(rName); + if (bRet) + m_aNavigationMgr.addEntry(aPos); + return bRet; +} + +void SwWrtShell::GotoFormatField( const SwFormatField& rField ) { + SwPosition aPos = *GetCursor()->GetPoint(); + bool bRet = SwCursorShell::GotoFormatField(rField); + if (bRet) + m_aNavigationMgr.addEntry(aPos); +} + +const SwRangeRedline* SwWrtShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect ) { + SwPosition aPos = *GetCursor()->GetPoint(); + const SwRangeRedline *pRedline = SwCursorShell::GotoRedline(nArrPos, bSelect); + if (pRedline) + m_aNavigationMgr.addEntry(aPos); + return pRedline; +} + +bool SwWrtShell::SelectTextAttr( sal_uInt16 nWhich, const SwTextAttr* pAttr ) +{ + bool bRet; + { + SwMvContext aMvContext(this); + SttSelect(); + bRet = SwCursorShell::SelectTextAttr( nWhich, false, pAttr ); + } + EndSelect(); + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/navmgr.cxx b/sw/source/uibase/wrtsh/navmgr.cxx new file mode 100644 index 000000000..c43992662 --- /dev/null +++ b/sw/source/uibase/wrtsh/navmgr.cxx @@ -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/. + */ + +#include <navmgr.hxx> +#include <wrtsh.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/svapp.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <doc.hxx> +#include <unocrsr.hxx> + +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +// This method positions the cursor to the position rPos. + +void SwNavigationMgr::GotoSwPosition(const SwPosition &rPos) { + // EnterStdMode() prevents the cursor to 'block' the current + // shell when it should move from the image back to the normal shell + m_rMyShell.EnterStdMode(); + m_rMyShell.StartAllAction(); + // cursor consists of two SwPositions: Point and Mark. + // Such a pair is called a PaM. SwPaM is derived from SwRing. + // The Ring contains the single regions of a multi-selection. + SwPaM* pPaM = m_rMyShell.GetCursor(); + + if(pPaM->HasMark()) + pPaM->DeleteMark(); // If there was a selection, get rid of it + *pPaM->GetPoint() = rPos; // Position Cursor + + m_rMyShell.EndAllAction(); +} + +// Ctor for the SwNavigationMgr class +// Sets the shell to the current shell +// and the index of the current position to 0 + +SwNavigationMgr::SwNavigationMgr(SwWrtShell & rShell) + : m_nCurrent(0), m_rMyShell(rShell) +{ +} + +SwNavigationMgr::~SwNavigationMgr() +{ + SolarMutexGuard g; + for (auto & it : m_entries) + { + EndListening(it->m_aNotifier); + } + m_entries.clear(); +} + +void SwNavigationMgr::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + // our cursors may now spontaneously self-destruct: remove from + // m_entries if that happens + if (typeid(rHint) == typeid(sw::UnoCursorHint)) + { + auto it = std::find_if(m_entries.begin(), m_entries.end(), + [&rBC](const sw::UnoCursorPointer& rItem) { return !rItem || &rBC == &rItem->m_aNotifier; }); + if (it != m_entries.end()) + { + EndListening(rBC); + m_entries.erase(it); + } + } +} + +// This method is used by the navigation shell - defined in sw/source/uibase/inc/navsh.hxx +// and implemented in sw/source/uibase/shells/navsh.cxx +// It is called when we want to check if the back button should be enabled or not. +// The back button should be enabled only if there are some entries in the navigation history + +bool SwNavigationMgr::backEnabled() { + return (m_nCurrent > 0); +} + +// Similar to backEnabled() method. +// The forward button should be enabled if we ever clicked back +// Due to the implementation of the navigation class, this is when the +// current position within the navigation history entries in not the last one +// i.e. when the m_nCurrent index is not at the end of the m_entries vector + +bool SwNavigationMgr::forwardEnabled() { + return m_nCurrent+1 < m_entries.size(); +} + +// The goBack() method positions the cursor to the previous entry in the navigation history +// If there was no history to go forward to, it adds the current position of the cursor +// to the history so we could go forward to where we came from + +void SwNavigationMgr::goBack() { + + // Although the button should be disabled whenever the backEnabled() returns false, + // the UI is sometimes not as responsive as we would like it to be :) + // this check prevents segmentation faults and in this way the class is not relying on the UI + + if (backEnabled()) { + /* Trying to get the current cursor */ + SwPaM* pPaM = m_rMyShell.GetCursor(); + if (!pPaM) { + return; + } + // This flag will be used to manually refresh the buttons + + bool bForwardWasDisabled = !forwardEnabled(); + + // If we're going backwards in our history, but the current location is not + // in the history then we need to add *here* to it so that we can "go + // forward" to here again. + + if (bForwardWasDisabled) { + + // the cursor consists of two SwPositions: Point and Mark. + // We are adding the current Point to the navigation history + // so we could later navigate forward to it + + // The addEntry() method returns true iff we should decrement + // the index before navigating back + + if (addEntry(*pPaM->GetPoint()) ) { + m_nCurrent--; + } + } + m_nCurrent--; + // Position cursor to appropriate navigation history entry + GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); + // Refresh the buttons + if (bForwardWasDisabled) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); + if (!backEnabled()) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); + } +} + +// The goForward() method positions the cursor to the next entry in the navigation history + +void SwNavigationMgr::goForward() { + + // Although the button should be disabled whenever the backForward() returns false, + // the UI is sometimes not as responsive as we would like it to be :) + // this check prevents segmentation faults and in this way the class is not relying on the UI + + if (forwardEnabled()) { + // This flag will be used to manually refresh the buttons + bool bBackWasDisabled = !backEnabled(); + // The current index is positioned at the current entry in the navigation history + // We have to increment it to go to the next entry + m_nCurrent++; + GotoSwPosition(*m_entries[m_nCurrent]->GetPoint()); + // Refresh the buttons + if (bBackWasDisabled) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); + if (!forwardEnabled()) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); + } +} + +// This method adds the SwPosition rPos to the navigation history +// rPos is usually the current position of the cursor in the document + +bool SwNavigationMgr::addEntry(const SwPosition& rPos) { + // Flags that will be used for refreshing the buttons + bool bBackWasDisabled = !backEnabled(); + bool bForwardWasEnabled = forwardEnabled(); + + bool bRet = false; // return value of the function. + // Indicates whether the index should be decremented before + // jumping back or not + // The navigation history has recency with temporal ordering enhancement, + // as described on http://zing.ncsl.nist.gov/hfweb/proceedings/greenberg/ + // If any forward history exists, twist the tail of the + // list from the current position to the end + if (bForwardWasEnabled) { + + size_t number_ofm_entries = m_entries.size(); // To avoid calling m_entries.size() multiple times + int curr = m_nCurrent; // Index from which we'll twist the tail. + int n = (number_ofm_entries - curr) / 2; // Number of entries that will swap places + for (int i = 0; i < n; i++) { + std::swap(m_entries[curr + i], m_entries[number_ofm_entries -1 - i]); + } + + if (*m_entries.back()->GetPoint() != rPos) + { + sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); + StartListening(pCursor->m_aNotifier); + m_entries.push_back(pCursor); + } + bRet = true; + } + else { + if ( (!m_entries.empty() && *m_entries.back()->GetPoint() != rPos) || m_entries.empty() ) { + sw::UnoCursorPointer pCursor(m_rMyShell.GetDoc()->CreateUnoCursor(rPos)); + StartListening(pCursor->m_aNotifier); + m_entries.push_back(pCursor); + bRet = true; + } + if (m_entries.size() > 1 && *m_entries.back()->GetPoint() == rPos) + bRet = true; + if (m_entries.size() == 1 && *m_entries.back()->GetPoint() == rPos) + bRet = false; + } + m_nCurrent = m_entries.size(); + + // Refresh buttons + if (bBackWasDisabled) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_BACK); + if (bForwardWasEnabled) + m_rMyShell.GetView().GetViewFrame()->GetBindings().Invalidate(FN_NAVIGATION_FORWARD); + + // show the Navigation toolbar + css::uno::Reference< css::frame::XFrame > xFrame = + m_rMyShell.GetView().GetViewFrame()->GetFrame().GetFrameInterface(); + if (xFrame.is()) + { + css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY); + if (xPropSet.is()) + { + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager"); + + aValue >>= xLayoutManager; + if (xLayoutManager.is()) + { + const OUString sResourceURL( "private:resource/toolbar/navigationobjectbar" ); + css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL); + if (!xUIElement.is()) + { + xLayoutManager->createElement( sResourceURL ); + xLayoutManager->showElement( sResourceURL ); + } + } + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/select.cxx b/sw/source/uibase/wrtsh/select.cxx new file mode 100644 index 000000000..a5c1ecbb7 --- /dev/null +++ b/sw/source/uibase/wrtsh/select.cxx @@ -0,0 +1,993 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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 <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svl/macitem.hxx> +#include <unotools/charclass.hxx> +#include <sfx2/event.hxx> +#include <cmdid.h> +#include <view.hxx> +#include <basesh.hxx> +#include <wrtsh.hxx> +#include <frmatr.hxx> +#include <mdiexp.hxx> +#include <fmtcol.hxx> +#include <frmfmt.hxx> +#include <swdtflvr.hxx> +#include <doc.hxx> +#include <wordcountdialog.hxx> +#include <memory> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +namespace com::sun::star::util { + struct SearchOptions2; +} + +using namespace ::com::sun::star::util; + +static long nStartDragX = 0, nStartDragY = 0; +static bool bStartDrag = false; + +void SwWrtShell::Invalidate() +{ + // to avoid making the slot volatile, invalidate it every time if something could have been changed + // this is still much cheaper than asking for the state every 200 ms (and avoid background processing) + GetView().GetViewFrame()->GetBindings().Invalidate( FN_STAT_SELMODE ); + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); +} + +bool SwWrtShell::SelNearestWrd() +{ + SwMvContext aMvContext(this); + if( !IsInWord() && !IsEndWrd() && !IsStartWord() ) + PrvWrd(); + if( IsEndWrd() ) + Left(CRSR_SKIP_CELLS, false, 1, false ); + return SelWrd(); +} + +bool SwWrtShell::SelWrd(const Point *pPt ) +{ + bool bRet; + { + SwMvContext aMvContext(this); + SttSelect(); + bRet = SwCursorShell::SelectWord( pPt ); + } + EndSelect(); + if( bRet ) + { + m_bSelWrd = true; + if(pPt) + m_aStart = *pPt; + } + return bRet; +} + +void SwWrtShell::SelSentence(const Point *pPt ) +{ + { + SwMvContext aMvContext(this); + ClearMark(); + SwCursorShell::GoStartSentence(); + SttSelect(); + SwCursorShell::GoEndSentence(); + } + EndSelect(); + if(pPt) + m_aStart = *pPt; + m_bSelLn = true; + m_bSelWrd = false; // disable SelWord, otherwise no SelLine goes on +} + +void SwWrtShell::SelPara(const Point *pPt ) +{ + { + SwMvContext aMvContext(this); + ClearMark(); + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + SttSelect(); + SwCursorShell::MovePara( GoCurrPara, fnParaEnd ); + } + EndSelect(); + if(pPt) + m_aStart = *pPt; + m_bSelLn = false; + m_bSelWrd = false; // disable SelWord, otherwise no SelLine goes on +} + +void SwWrtShell::SelAll() +{ + const bool bLockedView = IsViewLocked(); + LockView( true ); + { + if(m_bBlockMode) + LeaveBlockMode(); + SwMvContext aMvContext(this); + bool bMoveTable = false; + std::unique_ptr<SwPosition> pStartPos; + std::unique_ptr<SwPosition> pEndPos; + SwShellCursor* pTmpCursor = nullptr; + + // Query these early, before we move the cursor. + bool bHasWholeTabSelection = HasWholeTabSelection(); + bool bIsCursorInTable = IsCursorInTable(); + + if (!bHasWholeTabSelection) + { + if ( IsSelection() && IsCursorPtAtEnd() ) + SwapPam(); + pTmpCursor = getShellCursor( false ); + if( pTmpCursor ) + { + pStartPos.reset(new SwPosition( *pTmpCursor->GetPoint() )); + pEndPos.reset(new SwPosition( *pTmpCursor->GetMark() )); + } + Push(); + bool bIsFullSel = !MoveSection( GoCurrSection, fnSectionStart); + SwapPam(); + bIsFullSel &= !MoveSection( GoCurrSection, fnSectionEnd); + Pop(SwCursorShell::PopMode::DeleteCurrent); + GoStart(true, &bMoveTable, false, !bIsFullSel); + } + else + { + EnterStdMode(); + SttEndDoc(true); + } + SttSelect(); + GoEnd(true, &bMoveTable); + + bool bNeedsExtendedSelectAll = StartsWithTable(); + + // If the cursor was in a table, then we only need the extended select + // all if the whole table is already selected, to still allow selecting + // only a single cell or a single table before selecting the whole + // document. + if (bNeedsExtendedSelectAll && bIsCursorInTable) + bNeedsExtendedSelectAll = bHasWholeTabSelection; + + if (bNeedsExtendedSelectAll) + { + // Disable table cursor to make sure getShellCursor() returns m_pCurrentCursor, not m_pTableCursor. + if (IsTableMode()) + TableCursorToCursor(); + // Do the extended select all on m_pCurrentCursor. + ExtendedSelectAll(/*bFootnotes =*/ false); + } + + SwDoc *pDoc = GetDoc(); + if ( pDoc ) + { + pDoc->SetPrepareSelAll(); + } + + if( pStartPos ) + { + pTmpCursor = getShellCursor( false ); + if( pTmpCursor ) + { + // Some special handling for sections (e.g. TOC) at the beginning of the document body + // to avoid the selection of the first section + // if the last selection was behind the first section or + // if the last selection was already the first section + // In this both cases we select to the end of document + if( ( *pTmpCursor->GetPoint() < *pEndPos || + ( *pStartPos == *pTmpCursor->GetMark() && + *pEndPos == *pTmpCursor->GetPoint() ) ) && !bNeedsExtendedSelectAll) + SwCursorShell::SttEndDoc(false); + } + } + } + EndSelect(); + LockView( bLockedView ); +} + +// Description: Text search + +sal_uLong SwWrtShell::SearchPattern( const i18nutil::SearchOptions2& rSearchOpt, bool bSearchInNotes, + SwDocPositions eStt, SwDocPositions eEnd, + FindRanges eFlags, bool bReplace ) +{ + // no enhancement of existing selections + if(!(eFlags & FindRanges::InSel)) + ClearMark(); + bool bCancel = false; + sal_uLong nRet = Find_Text(rSearchOpt, bSearchInNotes, eStt, eEnd, bCancel, eFlags, bReplace); + if(bCancel) + { + Undo(); + nRet = ULONG_MAX; + } + return nRet; +} + +// Description: search for templates + +sal_uLong SwWrtShell::SearchTempl( const OUString &rTempl, + SwDocPositions eStt, SwDocPositions eEnd, + FindRanges eFlags, const OUString* pReplTempl ) +{ + // no enhancement of existing selections + if(!(eFlags & FindRanges::InSel)) + ClearMark(); + SwTextFormatColl *pColl = GetParaStyle(rTempl, SwWrtShell::GETSTYLE_CREATESOME); + SwTextFormatColl *pReplaceColl = nullptr; + if( pReplTempl ) + pReplaceColl = GetParaStyle(*pReplTempl, SwWrtShell::GETSTYLE_CREATESOME ); + + bool bCancel = false; + sal_uLong nRet = FindFormat(pColl ? *pColl : GetDfltTextFormatColl(), + eStt,eEnd, bCancel, eFlags, pReplaceColl); + if(bCancel) + { + Undo(); + nRet = ULONG_MAX; + } + return nRet; +} + +// search for attributes + +sal_uLong SwWrtShell::SearchAttr( const SfxItemSet& rFindSet, bool bNoColls, + SwDocPositions eStart, SwDocPositions eEnd, + FindRanges eFlags, const i18nutil::SearchOptions2* pSearchOpt, + const SfxItemSet* pReplaceSet ) +{ + // no enhancement of existing selections + if (!(eFlags & FindRanges::InSel)) + ClearMark(); + + // Searching + bool bCancel = false; + sal_uLong nRet = FindAttrs(rFindSet, bNoColls, eStart, eEnd, bCancel, eFlags, pSearchOpt, pReplaceSet); + + if(bCancel) + { + Undo(); + nRet = ULONG_MAX; + } + return nRet; +} + +// Selection modes + +void SwWrtShell::PushMode() +{ + m_pModeStack = new ModeStack( m_pModeStack, m_bIns, m_bExtMode, m_bAddMode, m_bBlockMode ); +} + +void SwWrtShell::PopMode() +{ + if ( nullptr == m_pModeStack ) + return; + + if ( m_bExtMode && !m_pModeStack->bExt ) + LeaveExtMode(); + if ( m_bAddMode && !m_pModeStack->bAdd ) + LeaveAddMode(); + if ( m_bBlockMode && !m_pModeStack->bBlock ) + LeaveBlockMode(); + m_bIns = m_pModeStack->bIns; + + m_pModeStack = std::move(m_pModeStack->pNext); +} + +// Two methods for setting cursors: the first maps at the +// eponymous methods in the CursorShell, the second removes +// all selections at first. + +long SwWrtShell::SetCursor(const Point *pPt, bool bTextOnly) +{ + // Remove a possibly present selection at the position + // of the mouseclick + + if(!IsInSelect() && TestCurrPam(*pPt)) { + ClearMark(); + } + + return SwCursorShell::SetCursor(*pPt, bTextOnly); +} + +long SwWrtShell::SetCursorKillSel(const Point *pPt, bool bTextOnly ) +{ + SwActContext aActContext(this); + ResetSelect(pPt,false); + return SwCursorShell::SetCursor(*pPt, bTextOnly); +} + +void SwWrtShell::UnSelectFrame() +{ + // Remove Frame selection with guaranteed invalid position + Point aPt(LONG_MIN, LONG_MIN); + SelectObj(aPt); + SwTransferable::ClearSelection( *this ); +} + +// Remove of all selections + +long SwWrtShell::ResetSelect(const Point *,bool) +{ + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + else + { + // SwActContext opens an Action - + // to avoid problems in the basic process with the + // shell switching, GetChgLnk().Call() may be called + // after EndAction(). + { + SwActContext aActContext(this); + m_bSelWrd = m_bSelLn = false; + KillPams(); + ClearMark(); + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + } + + // After canceling of all selections an update of Attr-Controls + // could be necessary. + GetChgLnk().Call(nullptr); + + if ( GetEnhancedTableSelection() != SwTable::SEARCH_NONE ) + UnsetEnhancedTableSelection(); + } + Invalidate(); + SwTransferable::ClearSelection( *this ); + return 1; +} + +bool SwWrtShell::IsSplitVerticalByDefault() const +{ + return GetDoc()->IsSplitVerticalByDefault(); +} + +void SwWrtShell::SetSplitVerticalByDefault(bool value) +{ + GetDoc()->SetSplitVerticalByDefault(value); +} + +// Do nothing + +long SwWrtShell::Ignore(const Point *, bool ) { + return 1; +} + +// Begin of a selection process. + +void SwWrtShell::SttSelect() +{ + if(m_bInSelect) + return; + if(!HasMark()) + SetMark(); + if( m_bBlockMode ) + { + SwShellCursor* pTmp = getShellCursor( true ); + if( !pTmp->HasMark() ) + pTmp->SetMark(); + } + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + m_bInSelect = true; + Invalidate(); + SwTransferable::CreateSelection( *this ); +} + +namespace { + +void collectUIInformation(SwShellCursor* pCursor) +{ + EventDescription aDescription; + OUString aSelStart = OUString::number(pCursor->Start()->nContent.GetIndex()); + OUString aSelEnd = OUString::number(pCursor->End()->nContent.GetIndex()); + + aDescription.aParameters = {{"START_POS", aSelStart}, {"END_POS", aSelEnd}}; + aDescription.aAction = "SELECT"; + aDescription.aID = "writer_edit"; + aDescription.aKeyWord = "SwEditWinUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +// End of a selection process. + +void SwWrtShell::EndSelect() +{ + if(m_bInSelect && !m_bExtMode) + { + m_bInSelect = false; + if (m_bAddMode) + { + AddLeaveSelect(); + } + else + { + SttLeaveSelect(); + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + } + SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId())); + if (pWrdCnt) + pWrdCnt->UpdateCounts(); + + collectUIInformation(GetCursor_()); +} + +void SwWrtShell::ExtSelWrd(const Point *pPt, bool ) +{ + SwMvContext aMvContext(this); + if( IsTableMode() ) + return; + + // Bug 66823: actual crsr has in additional mode no selection? + // Then destroy the actual and go to prev, this will be expand + if( !HasMark() && GoPrevCursor() ) + { + bool bHasMark = HasMark(); // that's wrong! + GoNextCursor(); + if( bHasMark ) + { + DestroyCursor(); + GoPrevCursor(); + } + } + + // check the direction of the selection with the new point + bool bMoveCursor = true, bToTop = false; + SwCursorShell::SelectWord( &m_aStart ); // select the startword + SwCursorShell::Push(); // save the cursor + SwCursorShell::SetCursor( *pPt ); // and check the direction + + switch( SwCursorShell::CompareCursorStackMkCurrPt()) + { + case -1: bToTop = false; break; + case 1: bToTop = true; break; + default: bMoveCursor = false; break; + } + + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); // restore the saved cursor + + if( bMoveCursor ) + { + // select to Top but cursor select to Bottom? or + // select to Bottom but cursor select to Top? --> swap the cursor + if( bToTop ) + SwapPam(); + + SwCursorShell::Push(); // save cur cursor + if( SwCursorShell::SelectWord( pPt )) // select the current word + { + if( bToTop ) + SwapPam(); + Combine(); + } + else + { + SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); + if( bToTop ) + SwapPam(); + } + } +} + +void SwWrtShell::ExtSelLn(const Point *pPt, bool ) +{ + SwMvContext aMvContext(this); + SwCursorShell::SetCursor(*pPt); + if( IsTableMode() ) + return; + + // Bug 66823: actual crsr has in additional mode no selection? + // Then destroy the actual and go to prev, this will be expand + if( !HasMark() && GoPrevCursor() ) + { + bool bHasMark = HasMark(); // that's wrong! + GoNextCursor(); + if( bHasMark ) + { + DestroyCursor(); + GoPrevCursor(); + } + } + + // if applicable fit the selection to the "Mark" + bool bToTop = !IsCursorPtAtEnd(); + SwapPam(); + + // The "Mark" has to be at the end or the beginning of the line. + if( bToTop ? !IsEndSentence() : !IsStartSentence() ) + { + if( bToTop ) + { + if( !IsEndPara() ) + SwCursorShell::Right(1,CRSR_SKIP_CHARS); + SwCursorShell::GoEndSentence(); + } + else + SwCursorShell::GoStartSentence(); + } + SwapPam(); + + if (bToTop) + SwCursorShell::GoStartSentence(); + else + SwCursorShell::GoEndSentence(); +} + +// Back into the standard mode: no mode, no selections. + +void SwWrtShell::EnterStdMode() +{ + if(m_bAddMode) + LeaveAddMode(); + if(m_bBlockMode) + LeaveBlockMode(); + m_bBlockMode = false; + m_bExtMode = false; + m_bInSelect = false; + if(IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + else + { + // SwActContext opens and action which has to be + // closed prior to the call of + // GetChgLnk().Call() + SwActContext aActContext(this); + m_bSelWrd = m_bSelLn = false; + if( !IsRetainSelection() ) + KillPams(); + ClearMark(); + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + Invalidate(); + SwTransferable::ClearSelection( *this ); +} + +// Extended Mode + +void SwWrtShell::EnterExtMode() +{ + if(m_bBlockMode) + { + LeaveBlockMode(); + KillPams(); + ClearMark(); + } + m_bExtMode = true; + m_bAddMode = false; + m_bBlockMode = false; + SttSelect(); +} + +void SwWrtShell::LeaveExtMode() +{ + m_bExtMode = false; + EndSelect(); +} + +// End of a selection; if the selection is empty, +// ClearMark(). + +void SwWrtShell::SttLeaveSelect() +{ + if(SwCursorShell::HasSelection() && !IsSelTableCells() && m_bClearMark) { + return; + } + ClearMark(); +} + +// Leaving of the selection mode in additional mode + +void SwWrtShell::AddLeaveSelect() +{ + if(IsTableMode()) LeaveAddMode(); + else if(SwCursorShell::HasSelection()) + CreateCursor(); +} + +// Additional Mode + +void SwWrtShell::EnterAddMode() +{ + if(IsTableMode()) return; + if(m_bBlockMode) + LeaveBlockMode(); + m_fnKillSel = &SwWrtShell::Ignore; + m_fnSetCursor = &SwWrtShell::SetCursor; + m_bAddMode = true; + m_bBlockMode = false; + m_bExtMode = false; + if(SwCursorShell::HasSelection()) + CreateCursor(); + Invalidate(); +} + +void SwWrtShell::LeaveAddMode() +{ + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_bAddMode = false; + Invalidate(); +} + +// Block Mode + +void SwWrtShell::EnterBlockMode() +{ + m_bBlockMode = false; + EnterStdMode(); + m_bBlockMode = true; + CursorToBlockCursor(); + Invalidate(); +} + +void SwWrtShell::LeaveBlockMode() +{ + m_bBlockMode = false; + BlockCursorToCursor(); + EndSelect(); + Invalidate(); +} + +// Insert mode + +void SwWrtShell::SetInsMode( bool bOn ) +{ + m_bIns = bOn; + SwCursorShell::SetOverwriteCursor( !m_bIns ); + const SfxBoolItem aTmp( SID_ATTR_INSERT, m_bIns ); + GetView().GetViewFrame()->GetBindings().SetState( aTmp ); + StartAction(); + EndAction(); + Invalidate(); +} +//Overwrite mode is incompatible with red-lining +void SwWrtShell::SetRedlineFlagsAndCheckInsMode( RedlineFlags eMode ) +{ + SetRedlineFlags( eMode ); + if (IsRedlineOn()) + SetInsMode(); +} + +// Edit frame + +void SwWrtShell::BeginFrameDrag(const Point *pPt, bool bIsShift) +{ + m_fnDrag = &SwFEShell::Drag; + if(bStartDrag) + { + Point aTmp( nStartDragX, nStartDragY ); + SwFEShell::BeginDrag( &aTmp, bIsShift ); + } + else + SwFEShell::BeginDrag( pPt, bIsShift ); +} + +void SwWrtShell::EnterSelFrameMode(const Point *pPos) +{ + if(pPos) + { + nStartDragX = pPos->X(); + nStartDragY = pPos->Y(); + bStartDrag = true; + } + m_bLayoutMode = true; + HideCursor(); + + // equal call of BeginDrag in the SwFEShell + m_fnDrag = &SwWrtShell::BeginFrameDrag; + m_fnEndDrag = &SwWrtShell::UpdateLayoutFrame; + SwBaseShell::SetFrameMode( FLY_DRAG_START, this ); + Invalidate(); +} + +void SwWrtShell::LeaveSelFrameMode() +{ + m_fnDrag = &SwWrtShell::BeginDrag; + m_fnEndDrag = &SwWrtShell::DefaultEndDrag; + m_bLayoutMode = false; + bStartDrag = false; + Edit(); + SwBaseShell::SetFrameMode( FLY_DRAG_END, this ); + Invalidate(); +} + +// Description: execute framebound macro + +IMPL_LINK( SwWrtShell, ExecFlyMac, const SwFlyFrameFormat*, pFlyFormat, void ) +{ + const SwFrameFormat *pFormat = pFlyFormat ? static_cast<const SwFrameFormat*>(pFlyFormat) : GetFlyFrameFormat(); + OSL_ENSURE(pFormat, "no frame format"); + const SvxMacroItem &rFormatMac = pFormat->GetMacro(); + + if(rFormatMac.HasMacro(SvMacroItemId::SwObjectSelect)) + { + const SvxMacro &rMac = rFormatMac.GetMacro(SvMacroItemId::SwObjectSelect); + if( IsFrameSelected() ) + m_bLayoutMode = true; + CallChgLnk(); + ExecMacro( rMac ); + } +} + +void SwWrtShell::UpdateLayoutFrame(const Point *, bool ) +{ + // still a dummy + SwFEShell::EndDrag(); + m_fnDrag = &SwWrtShell::BeginFrameDrag; +} + +// Handler for toggling the modes. Returns back the old mode. + +void SwWrtShell::ToggleAddMode() +{ + m_bAddMode ? LeaveAddMode(): EnterAddMode(); + Invalidate(); +} + +bool SwWrtShell::ToggleBlockMode() +{ + m_bBlockMode ? LeaveBlockMode(): EnterBlockMode(); + Invalidate(); + return !m_bBlockMode; +} + +bool SwWrtShell::ToggleExtMode() +{ + m_bExtMode ? LeaveExtMode() : EnterExtMode(); + Invalidate(); + return !m_bExtMode; +} + +// Dragging in standard mode (Selecting of content) + +void SwWrtShell::BeginDrag(const Point * /*pPt*/, bool ) +{ + if(m_bSelWrd) + { + m_bInSelect = true; + if( !IsCursorPtAtEnd() ) + SwapPam(); + + m_fnDrag = &SwWrtShell::ExtSelWrd; + m_fnSetCursor = &SwWrtShell::Ignore; + } + else if(m_bSelLn) + { + m_bInSelect = true; + m_fnDrag = &SwWrtShell::ExtSelLn; + m_fnSetCursor = &SwWrtShell::Ignore; + } + else + { + m_fnDrag = &SwWrtShell::DefaultDrag; + SttSelect(); + } +} + +void SwWrtShell::DefaultDrag(const Point *, bool ) +{ + if( IsSelTableCells() ) + m_aSelTableLink.Call(*this); +} + +void SwWrtShell::DefaultEndDrag(const Point * /*pPt*/, bool ) +{ + m_fnDrag = &SwWrtShell::BeginDrag; + if( IsExtSel() ) + LeaveExtSel(); + + if( IsSelTableCells() ) + m_aSelTableLink.Call(*this); + EndSelect(); +} + +// #i32329# Enhanced table selection +bool SwWrtShell::SelectTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag ) +{ + SwMvContext aMvContext(this); + SttSelect(); + if(SelTableRowCol( rPt, pEnd, bRowDrag )) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + return true; + } + return false; +} + +// Description: Selection of a table line or column + +void SwWrtShell::SelectTableRow() +{ + if ( SelTableRow() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +void SwWrtShell::SelectTableCol() +{ + if ( SelTableCol() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +void SwWrtShell::SelectTableCell() +{ + if ( SelTableBox() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } +} + +// Description: Check if a word selection is present. +// According to the rules for intelligent cut / paste +// surrounding spaces are cut out. +// Return: Delivers the type of the word selection. + +int SwWrtShell::IntelligentCut(SelectionType nSelection, bool bCut) +{ + // On multiple selection no intelligent drag and drop + // there are multiple cursors, since a second was placed + // already at the target position. + if( IsAddMode() || !(nSelection & SelectionType::Text) ) + return NO_WORD; + + OUString sText; + CharClass& rCC = GetAppCharClass(); + + // If the first character is no word character, + // no word selected. + sal_Unicode cPrev = GetChar(false); + sal_Unicode cNext = GetChar(true, -1); + if( !cPrev || !cNext || + !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) || + !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) ) + return NO_WORD; + + cPrev = GetChar(false, -1); + cNext = GetChar(); + + int cWord = NO_WORD; + // is a word selected? + if (cPrev && cNext && + CH_TXTATR_BREAKWORD != cPrev && CH_TXTATR_INWORD != cPrev && + CH_TXTATR_BREAKWORD != cNext && CH_TXTATR_INWORD != cNext && + !rCC.isLetterNumeric( ( sText = OUString(cPrev) ), 0 ) && + !rCC.isLetterNumeric( ( sText = OUString(cNext) ), 0 ) ) + cWord = WORD_NO_SPACE; + + if(cWord == WORD_NO_SPACE && ' ' == cPrev ) + { + cWord = WORD_SPACE_BEFORE; + // delete the space before + if(bCut) + { + Push(); + if(IsCursorPtAtEnd()) + SwapPam(); + ClearMark(); + SetMark(); + SwCursorShell::Left(1,CRSR_SKIP_CHARS); + SwFEShell::Delete(); + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + else if(cWord == WORD_NO_SPACE && cNext == ' ') + { + cWord = WORD_SPACE_AFTER; + // delete the space behind + if(bCut) { + Push(); + if(!IsCursorPtAtEnd()) SwapPam(); + ClearMark(); + SetMark(); + SwCursorShell::Right(1,CRSR_SKIP_CHARS); + SwFEShell::Delete(); + Pop(SwCursorShell::PopMode::DeleteCurrent); + } + } + return cWord; +} + + // jump to the next / previous hyperlink - inside text and also + // on graphics +void SwWrtShell::SelectNextPrevHyperlink( bool bNext ) +{ + StartAction(); + bool bRet = SwCursorShell::SelectNxtPrvHyperlink( bNext ); + if( !bRet ) // didn't find? wrap and check again + { + SwShellCursor* pCursor = GetCursor_(); + SwCursorSaveState aSaveState(*pCursor); + EnterStdMode(); + if( bNext ) + SttEndDoc(true); + else + SttEndDoc(false); + bRet = SwCursorShell::SelectNxtPrvHyperlink(bNext); + if (!bRet) // didn't find again? restore cursor position and bail + { + pCursor->RestoreSavePos(); + EndAction(true); // don't scroll to restored cursor position + return; + } + } + EndAction(); + + bool bCreateXSelection = false; + const bool bFrameSelected = IsFrameSelected() || IsObjSelected(); + if( IsSelection() ) + { + if ( bFrameSelected ) + UnSelectFrame(); + + // Set the function pointer for the canceling of the selection + // set at cursor + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + bCreateXSelection = true; + } + else if( bFrameSelected ) + { + EnterSelFrameMode(); + bCreateXSelection = true; + } + else if( (CNT_GRF | CNT_OLE ) & GetCntType() ) + { + SelectObj( GetCharRect().Pos() ); + EnterSelFrameMode(); + bCreateXSelection = true; + } + + if( bCreateXSelection ) + SwTransferable::CreateSelection( *this ); +} + +// For the preservation of the selection the cursor will be moved left +// after SetMark(), so that the cursor is not moved by inserting text. +// Because a present selection at the CORE page is cleared at the +// current cursor position, the cursor will be pushed on the stack. +// After moving, they will again resummarized. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx new file mode 100644 index 000000000..44e7391ba --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh1.cxx @@ -0,0 +1,1990 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/container/XChild.hpp> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/util/XModifiable.hpp> + +#include <hintids.hxx> +#include <sot/exchange.hxx> +#include <svx/hdft.hxx> +#include <svx/svdview.hxx> +#include <svl/itemiter.hxx> +#include <tools/bigint.hxx> +#include <svtools/insdlg.hxx> +#include <sfx2/ipclient.hxx> +#include <editeng/formatbreakitem.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/ulspitem.hxx> +#include <vcl/graph.hxx> +#include <unotools/charclass.hxx> +#include <comphelper/storagehelper.hxx> +#include <svx/svxdlg.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <dialoghelp.hxx> +#include <frmfmt.hxx> +#include <fmtftn.hxx> +#include <fmthdft.hxx> +#include <fmtpdsc.hxx> +#include <txtfrm.hxx> +#include <wdocsh.hxx> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <cmdid.h> +#include <pagedesc.hxx> +#include <frmmgr.hxx> +#include <swundo.hxx> +#include <swcli.hxx> +#include <poolfmt.hxx> +#include <edtwin.hxx> +#include <fmtcol.hxx> +#include <swtable.hxx> +#include <viscrs.hxx> +#include <swdtflvr.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <SwCapObjType.hxx> +#include <SwStyleNameMapper.hxx> +#include <sfx2/request.hxx> +#include <paratr.hxx> +#include <ndtxt.hxx> +#include <editeng/acorrcfg.hxx> +#include <IMark.hxx> +#include <sfx2/bindings.hxx> +#include <flyfrm.hxx> + +// -> #111827# +#include <SwRewriter.hxx> +#include <strings.hrc> +// <- #111827# + +#include <toolkit/helper/vclunohelper.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/uitest/logger.hxx> +#include <vcl/uitest/eventdescription.hxx> + +#include <PostItMgr.hxx> +#include <FrameControlsManager.hxx> +#include <fldmgr.hxx> +#include <docufld.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <fmtfld.hxx> + +#include <sfx2/msgpool.hxx> +#include <sfx2/msg.hxx> +#include <svtools/embedhlp.hxx> +#include <svx/postattr.hxx> +#include <comphelper/lok.hxx> +#include <memory> + +using namespace sw::mark; +using namespace com::sun::star; +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); +} + +} + +#define BITFLD_INI_LIST \ + m_bClearMark = \ + m_bIns = true;\ + m_bAddMode = \ + m_bBlockMode = \ + m_bExtMode = \ + m_bInSelect = \ + m_bLayoutMode = \ + m_bSelWrd = \ + m_bSelLn = \ + m_bRetainSelection = false; \ + m_bIsInClickToEdit = false; + +static SvxAutoCorrect* lcl_IsAutoCorr() +{ + 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; + return pACorr; +} + +void SwWrtShell::NoEdit(bool bHideCursor) +{ + if(bHideCursor) + HideCursor(); +} + +void SwWrtShell::Edit() +{ + if (CanInsert()) + { + ShowCursor(); + } +} + +bool SwWrtShell::IsEndWrd() +{ + SwMvContext aMvContext(this); + if(IsEndPara() && !IsSttPara()) + return true; + + return IsEndWord(); +} + +// Insert string +void SwWrtShell::InsertByWord( const OUString & rStr) +{ + if( !rStr.isEmpty() ) + { + bool bDelim = GetAppCharClass().isLetterNumeric( rStr, 0 ); + sal_Int32 nPos = 0, nStt = 0; + for( ; nPos < rStr.getLength(); nPos++ ) + { + bool bTmpDelim = GetAppCharClass().isLetterNumeric( rStr, nPos ); + if( bTmpDelim != bDelim ) + { + Insert( rStr.copy( nStt, nPos - nStt )); + nStt = nPos; + } + } + if( nStt != nPos ) + Insert( rStr.copy( nStt, nPos - nStt )); + } +} + +void SwWrtShell::Insert( const OUString &rStr ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + bool bStarted = false; + bool bHasSel = HasSelection(), + bCallIns = m_bIns /*|| bHasSel*/; + bool bDeleted = false; + + typedef svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_RSID - 1, + RES_CHRATR_RSID + 1, RES_CHRATR_END - 1, + RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> CharItems; + SfxItemSet aCharAttrSet(GetAttrPool(), CharItems{}); + + if( bHasSel || ( !m_bIns && SelectHiddenRange() ) ) + { + // Only here parenthesizing, because the normal + // insert is already in parentheses at Editshell. + StartAllAction(); + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, GetCursorDescr()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + { + OUString aTmpStr = SwResId(STR_START_QUOTE) + + rStr + SwResId(STR_END_QUOTE); + + aRewriter.AddRule(UndoArg3, aTmpStr); + } + + // tdf#79717 Save character formatting of the start of the selection + const SwPosition *pStart = GetCursor()->Start(); + SwPaM aPaM(pStart->nNode.GetNode(), pStart->nContent.GetIndex(), + pStart->nNode.GetNode(), pStart->nContent.GetIndex() + 1); + GetPaMAttr(&aPaM, aCharAttrSet); + + StartUndo(SwUndoId::REPLACE, &aRewriter); + bStarted = true; + Push(); + bDeleted = DelRight(); + Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore selection (if tracking changes) + NormalizePam(false); // tdf#127635 put point at the end of deletion + ClearMark(); + } + + bCallIns ? + SwEditShell::Insert2( rStr, bDeleted ) : SwEditShell::Overwrite( rStr ); + + if( bDeleted ) + { + // tdf#79717 Restore formatting of the deleted selection + SwPosition* pEnd = GetCursor()->Start(); + SwPaM aPaM(pEnd->nNode.GetNode(), pEnd->nContent.GetIndex() - rStr.getLength(), + pEnd->nNode.GetNode(), pEnd->nContent.GetIndex()); + + SetAttrSet(aCharAttrSet, SetAttrMode::DEFAULT, &aPaM); + } + + if( bStarted ) + { + EndUndo(); + EndAllAction(); + } +} + +// Maximum height limit not possible, because the maximum height +// of the current frame can not be obtained. + +void SwWrtShell::Insert( const OUString &rPath, const OUString &rFilter, + const Graphic &rGrf, SwFlyFrameAttrMgr *pFrameMgr, + RndStdIds nAnchorType ) +{ + ResetCursorStack(); + if ( !CanInsert() ) + return; + + StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_GRAPHIC)); + + StartUndo(SwUndoId::INSERT, &aRewriter); + + if ( HasSelection() ) + DelRight(); + // Inserted graphics in its own paragraph, + // if at the end of a non-empty paragraph. + //For i120928,avoid to split node + + EnterSelFrameMode(); + + bool bSetGrfSize = true; + bool bOwnMgr = false; + + if ( !pFrameMgr ) + { + bOwnMgr = true; + pFrameMgr = new SwFlyFrameAttrMgr( true, this, Frmmgr_Type::GRF, nullptr ); + + // CAUTION + // GetAttrSet makes an adjustment + // While pasting is a SwFrameSize present + // because of the DEF-Framesize + // These must be removed explicitly for the optimal size. + pFrameMgr->DelAttr(RES_FRM_SIZE); + + if (nAnchorType != RndStdIds::FLY_AT_PARA) + // Something other than at-para was requested. + pFrameMgr->SetAnchor(nAnchorType); + } + else + { + Size aSz( pFrameMgr->GetSize() ); + if ( !aSz.Width() || !aSz.Height() ) + { + aSz.setWidth(567); + aSz.setHeight( 567); + pFrameMgr->SetSize( aSz ); + } + else if ( aSz.Width() != DFLT_WIDTH && aSz.Height() != DFLT_HEIGHT ) + bSetGrfSize = false; + + pFrameMgr->SetHeightSizeType(SwFrameSize::Fixed); + } + + // Insert the graphic + SwFEShell::Insert(rPath, rFilter, &rGrf, &pFrameMgr->GetAttrSet()); + if ( bOwnMgr ) + pFrameMgr->UpdateAttrMgr(); + + if( bSetGrfSize ) + { + Size aGrfSize, aBound = GetGraphicDefaultSize(); + GetGrfSize( aGrfSize ); + + // Add the margin attributes to GrfSize, + // because these counts at the margin additionally + aGrfSize.AdjustWidth(pFrameMgr->CalcWidthBorder() ); + aGrfSize.AdjustHeight(pFrameMgr->CalcHeightBorder() ); + + const BigInt aTempWidth( aGrfSize.Width() ); + const BigInt aTempHeight( aGrfSize.Height()); + + // Fit width if necessary, scale down the height proportional thereafter. + if( aGrfSize.Width() > aBound.Width() ) + { + aGrfSize.setWidth( aBound.Width() ); + aGrfSize.setHeight( BigInt(aBound.Width()) * aTempHeight / aTempWidth ); + } + // Fit height if necessary, scale down the width proportional thereafter. + if( aGrfSize.Height() > aBound.Height() ) + { + aGrfSize.setHeight( aBound.Height() ); + aGrfSize.setWidth( BigInt(aBound.Height()) * aTempWidth / aTempHeight ); + } + pFrameMgr->SetSize( aGrfSize ); + pFrameMgr->UpdateFlyFrame(); + } + if ( bOwnMgr ) + delete pFrameMgr; + + EndUndo(); + EndAllAction(); +} + +// Insert an OLE-Object into the CORE. +// if no object is transferred, then one will be created. + +void SwWrtShell::InsertObject( const svt::EmbeddedObjectRef& xRef, SvGlobalName const *pName, + sal_uInt16 nSlotId ) +{ + ResetCursorStack(); + if( !CanInsert() ) + return; + + if( !xRef.is() ) + { + // temporary storage + svt::EmbeddedObjectRef xObj; + uno::Reference < embed::XStorage > xStor = comphelper::OStorageHelper::GetTemporaryStorage(); + bool bDoVerb = true; + if ( pName ) + { + comphelper::EmbeddedObjectContainer aCnt( xStor ); + OUString aName; + // TODO/LATER: get aspect? + xObj.Assign( aCnt.CreateEmbeddedObject( pName->GetByteSequence(), aName ), embed::Aspects::MSOLE_CONTENT ); + } + else + { + SvObjectServerList aServerList; + switch (nSlotId) + { + case SID_INSERT_OBJECT: + { + aServerList.FillInsertObjects(); + aServerList.Remove( SwDocShell::Factory().GetClassId() ); + [[fallthrough]]; + } + + // TODO/LATER: recording! Convert properties to items + case SID_INSERT_FLOATINGFRAME: + { + SfxSlotPool* pSlotPool = SW_MOD()->GetSlotPool(); + const SfxSlot* pSlot = pSlotPool->GetSlot(nSlotId); + OString aCmd = OStringLiteral(".uno:") + pSlot->GetUnoName(); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractInsertObjectDialog> pDlg(pFact->CreateInsertObjectDialog(GetFrameWeld(mxDoc->GetDocShell()), + OUString::fromUtf8( aCmd ), xStor, &aServerList)); + if (pDlg) + { + pDlg->Execute(); + bDoVerb = pDlg->IsCreateNew(); + OUString aIconMediaType; + uno::Reference< io::XInputStream > xIconMetaFile = pDlg->GetIconIfIconified( &aIconMediaType ); + xObj.Assign( pDlg->GetObject(), + xIconMetaFile.is() ? embed::Aspects::MSOLE_ICON : embed::Aspects::MSOLE_CONTENT ); + if ( xIconMetaFile.is() ) + xObj.SetGraphicStream( xIconMetaFile, aIconMediaType ); + } + + break; + } + + default: + break; + } + } + + if ( xObj.is() ) + { + if( InsertOleObject( xObj ) && bDoVerb ) + { + SfxInPlaceClient* pClient = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin() ); + if ( !pClient ) + { + pClient = new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + SetCheckForOLEInCaption( true ); + } + + if ( xObj.GetViewAspect() == embed::Aspects::MSOLE_ICON ) + { + SwRect aArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ); + aArea.Pos() += GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, xObj.GetObject() ).Pos(); + MapMode aMapMode( MapUnit::MapTwip ); + Size aSize = xObj.GetSize( &aMapMode ); + aArea.Width( aSize.Width() ); + aArea.Height( aSize.Height() ); + RequestObjectResize( aArea, xObj.GetObject() ); + } + else + CalcAndSetScale( xObj ); + + //#50270# We don't need to handle error, this is handled by the + //DoVerb in the SfxViewShell + pClient->DoVerb(embed::EmbedVerbs::MS_OLEVERB_SHOW); + + // TODO/LATER: set document name - should be done in Client + } + } + } + else + { + if( HasSelection() ) + DelRight(); + InsertOleObject( xRef ); + } +} + +// Insert object into the Core. +// From ClipBoard or Insert + +bool SwWrtShell::InsertOleObject( const svt::EmbeddedObjectRef& xRef, SwFlyFrameFormat **pFlyFrameFormat ) +{ + //tdf#125100 Ensure that ole object is initially shown as pictogram + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = mxDoc->GetDocShell()->getEmbeddedObjectContainer(); + bool bSaveUserAllowsLinkUpdate = rEmbeddedObjectContainer.getUserAllowsLinkUpdate(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + + ResetCursorStack(); + StartAllAction(); + + StartUndo(SwUndoId::INSERT); + + //Some differences between StarMath and any other objects: + //1. Selections should be deleted. For StarMath the Text should be + // passed to the Object + //2. If the cursor is at the end of a non empty paragraph a paragraph + // break should be inserted. StarMath objects are character bound and + // no break should be inserted. + //3. If an selection is passed to a StarMath object, this object should + // not be activated. false should be returned then. + bool bStarMath = true; + bool bActivate = true; + + // set parent to get correct VisArea(in case of object needing parent printer) + uno::Reference < container::XChild > xChild( xRef.GetObject(), uno::UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( mxDoc->GetDocShell()->GetModel() ); + + SvGlobalName aCLSID( xRef->getClassID() ); + bStarMath = ( SotExchange::IsMath( aCLSID ) != 0 ); + if( IsSelection() ) + { + if( bStarMath ) + { + OUString aMathData; + GetSelectedText( aMathData, ParaBreakType::ToOnlyCR ); + + if( !aMathData.isEmpty() && svt::EmbeddedObjectRef::TryRunningState( xRef.GetObject() ) ) + { + uno::Reference < beans::XPropertySet > xSet( xRef->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + try + { + xSet->setPropertyValue("Formula", uno::makeAny( aMathData ) ); + bActivate = false; + } + catch (const uno::Exception&) + { + } + } + } + } + DelRight(); + } + + if ( !bStarMath ) + SwFEShell::SplitNode( false, false ); + + EnterSelFrameMode(); + + const SvGlobalName* pName = nullptr; + SvGlobalName aObjClsId; + if (xRef.is()) + { + aObjClsId = SvGlobalName(xRef.GetObject()->getClassID()); + pName = &aObjClsId; + } + SwFlyFrameAttrMgr aFrameMgr( true, this, Frmmgr_Type::OLE, pName ); + aFrameMgr.SetHeightSizeType(SwFrameSize::Fixed); + + SwRect aBound; + CalcBoundRect( aBound, aFrameMgr.GetAnchor() ); + + //The Size should be suggested by the OLE server + MapMode aMapMode( MapUnit::MapTwip ); + Size aSz = xRef.GetSize( &aMapMode ); + + //Object size can be limited + if ( aSz.Width() > aBound.Width() ) + { + //Always limit proportional. + aSz.setHeight( aSz.Height() * aBound.Width() / aSz.Width() ); + aSz.setWidth( aBound.Width() ); + } + aFrameMgr.SetSize( aSz ); + SwFlyFrameFormat *pFormat = SwFEShell::InsertObject( xRef, &aFrameMgr.GetAttrSet() ); + + // --> #i972# + if ( bStarMath && mxDoc->getIDocumentSettingAccess().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ) ) + AlignFormulaToBaseline( xRef.GetObject() ); + + if (pFlyFrameFormat) + *pFlyFrameFormat = pFormat; + + if ( SotExchange::IsChart( aCLSID ) ) + { + uno::Reference< embed::XEmbeddedObject > xEmbeddedObj = xRef.GetObject(); + if ( xEmbeddedObj.is() ) + { + bool bDisableDataTableDialog = false; + svt::EmbeddedObjectRef::TryRunningState( xEmbeddedObj ); + uno::Reference< beans::XPropertySet > xProps( xEmbeddedObj->getComponent(), uno::UNO_QUERY ); + if ( xProps.is() && + ( xProps->getPropertyValue("DisableDataTableDialog") >>= bDisableDataTableDialog ) && + bDisableDataTableDialog ) + { + xProps->setPropertyValue("DisableDataTableDialog", + uno::makeAny( false ) ); + xProps->setPropertyValue("DisableComplexChartTypes", + uno::makeAny( false ) ); + uno::Reference< util::XModifiable > xModifiable( xProps, uno::UNO_QUERY ); + if ( xModifiable.is() ) + { + xModifiable->setModified( true ); + } + } + } + } + + EndAllAction(); + GetView().AutoCaption(OLE_CAP, &aCLSID); + + SwRewriter aRewriter; + + if ( bStarMath ) + aRewriter.AddRule(UndoArg1, SwResId(STR_MATH_FORMULA)); + else if ( SotExchange::IsChart( aCLSID ) ) + aRewriter.AddRule(UndoArg1, SwResId(STR_CHART)); + else + aRewriter.AddRule(UndoArg1, SwResId(STR_OLE)); + + EndUndo(SwUndoId::INSERT, &aRewriter); + + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(bSaveUserAllowsLinkUpdate); + + return bActivate; +} + +// The current selected OLE object will be loaded with the +// verb into the server. + +void SwWrtShell::LaunchOLEObj( long nVerb ) +{ + if ( GetCntType() == CNT_OLE && + !GetView().GetViewFrame()->GetFrame().IsInPlace() ) + { + svt::EmbeddedObjectRef& xRef = GetOLEObject(); + OSL_ENSURE( xRef.is(), "OLE not found" ); + + // LOK: we don't want to handle any other embedded objects than + // charts, there are too many problems with eg. embedded spreadsheets + // (like it creates a separate view for the calc sheet) + if (comphelper::LibreOfficeKit::isActive() && !SotExchange::IsChart(xRef->getClassID())) + return; + + SfxInPlaceClient* pCli = GetView().FindIPClient( xRef.GetObject(), &GetView().GetEditWin() ); + if ( !pCli ) + pCli = new SwOleClient( &GetView(), &GetView().GetEditWin(), xRef ); + + static_cast<SwOleClient*>(pCli)->SetInDoVerb( true ); + + CalcAndSetScale( xRef ); + pCli->DoVerb( nVerb ); + + static_cast<SwOleClient*>(pCli)->SetInDoVerb( false ); + CalcAndSetScale( xRef ); + } +} + +void SwWrtShell::MoveObjectIfActive( svt::EmbeddedObjectRef& xObj, const Point& rOffset ) +{ + try + { + sal_Int32 nState = xObj->getCurrentState(); + if ( nState == css::embed::EmbedStates::INPLACE_ACTIVE + || nState == css::embed::EmbedStates::UI_ACTIVE ) + { + SfxInPlaceClient* pCli = + GetView().FindIPClient( xObj.GetObject(), &(GetView().GetEditWin()) ); + if ( pCli ) + { + tools::Rectangle aArea = pCli->GetObjArea(); + aArea += rOffset; + pCli->SetObjArea( aArea ); + } + } + } + catch (const uno::Exception&) + { + } +} + +void SwWrtShell::CalcAndSetScale( svt::EmbeddedObjectRef& xObj, + const SwRect *pFlyPrtRect, + const SwRect *pFlyFrameRect, + const bool bNoTextFramePrtAreaChanged ) +{ + // Setting the scale of the client. This arises from the difference + // between the VisArea of the object and the ObjArea. + OSL_ENSURE( xObj.is(), "ObjectRef not valid" ); + + sal_Int64 nAspect = xObj.GetViewAspect(); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + return; // the replacement image is completely controlled by container in this case + + sal_Int64 nMisc = 0; + bool bLinkingChart = false; + + try + { + nMisc = xObj->getStatus( nAspect ); + + // This can surely only be a non-active object, if desired they + // get the new size set as VisArea (StarChart). + if( embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE & nMisc ) + { + // TODO/MBA: testing + SwRect aRect( pFlyPrtRect ? *pFlyPrtRect + : GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() )); + if( !aRect.IsEmpty() ) + { + // TODO/LEAN: getMapUnit can switch object to running state + // xObj.TryRunningState(); + + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + + // TODO/LATER: needs complete VisArea?! + Size aSize( OutputDevice::LogicToLogic(aRect.SVRect(), MapMode(MapUnit::MapTwip), MapMode(aUnit)).GetSize() ); + awt::Size aSz; + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + + // Action 'setVisualAreaSize' doesn't have to turn on the + // modified state of the document, either. + bool bModified = false; + uno::Reference<util::XModifiable> xModifiable(xObj->getComponent(), uno::UNO_QUERY); + if (xModifiable.is()) + bModified = xModifiable->isModified(); + xObj->setVisualAreaSize( nAspect, aSz ); + xModifiable.set(xObj->getComponent(), uno::UNO_QUERY); + if (xModifiable.is() && xModifiable->isModified() && !bModified) + xModifiable->setModified(bModified); + + // #i48419# - action 'UpdateReplacement' doesn't + // have to change the modified state of the document. + // This is only a workaround for the defect, that this action + // modifies a document after load, because unnecessarily the + // replacement graphic is updated, in spite of the fact that + // nothing has been changed. + // If the replacement graphic changes by this action, the document + // will be already modified via other mechanisms. + { + bool bResetEnableSetModified(false); + if ( GetDoc()->GetDocShell()->IsEnableSetModified() ) + { + GetDoc()->GetDocShell()->EnableSetModified( false ); + bResetEnableSetModified = true; + } + + //#i79576# don't destroy chart replacement images on load + //#i79578# don't request a new replacement image for charts to often + //a chart sends a modified call to the framework if it was changed + //thus the replacement update is already handled elsewhere + if ( !SotExchange::IsChart( xObj->getClassID() ) ) + xObj.UpdateReplacement(); + + if ( bResetEnableSetModified ) + { + GetDoc()->GetDocShell()->EnableSetModified(); + } + } + } + + // TODO/LATER: this is only a workaround, + uno::Reference< chart2::XChartDocument > xChartDocument( xObj->getComponent(), uno::UNO_QUERY ); + bLinkingChart = ( xChartDocument.is() && !xChartDocument->hasInternalDataProvider() ); + } + } + catch (const uno::Exception&) + { + // TODO/LATER: handle the error + return; + } + + SfxInPlaceClient* pCli = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin() ); + if ( !pCli ) + { + if ( (embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY & nMisc) + || bLinkingChart + // --> OD #i117189# - refine condition for non-resizable objects + // non-resizable objects need to be set the size back by this method + || ( bNoTextFramePrtAreaChanged && nMisc & embed::EmbedMisc::EMBED_NEVERRESIZE ) ) + { + pCli = new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + } + else + return; + } + + // TODO/LEAN: getMapUnit can switch object to running state + // xObj.TryRunningState(); + + awt::Size aSize; + try + { + aSize = xObj->getVisualAreaSize( nAspect ); + } + catch (const embed::NoVisualAreaSizeException&) + { + OSL_FAIL("Can't get visual area size!" ); + // the scaling will not be done + } + catch (const uno::Exception&) + { + // TODO/LATER: handle the error + OSL_FAIL("Can't get visual area size!" ); + return; + } + + Size _aVisArea( aSize.Width, aSize.Height ); + + Fraction aScaleWidth( 1, 1 ); + Fraction aScaleHeight( 1, 1 ); + + bool bUseObjectSize = false; + + // As long as there comes no reasonable size from the object, + // nothing can be scaled. + if( _aVisArea.Width() && _aVisArea.Height() ) + { + const MapMode aTmp( MapUnit::MapTwip ); + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + _aVisArea = OutputDevice::LogicToLogic(_aVisArea, MapMode(aUnit), aTmp); + Size aObjArea; + if ( pFlyPrtRect ) + aObjArea = pFlyPrtRect->SSize(); + else + aObjArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ).SSize(); + + // differ the aObjArea and _aVisArea by 1 Pixel then set new VisArea + long nX, nY; + SwSelPaintRects::Get1PixelInLogic( *this, &nX, &nY ); + if( !( _aVisArea.Width() - nX <= aObjArea.Width() && + _aVisArea.Width() + nX >= aObjArea.Width() && + _aVisArea.Height()- nY <= aObjArea.Height()&& + _aVisArea.Height()+ nY >= aObjArea.Height() )) + { + if ( nMisc & embed::EmbedMisc::EMBED_NEVERRESIZE ) + { + // the object must not be scaled, + // the size stored in object must be used for restoring + bUseObjectSize = true; + } + else + { + aScaleWidth = Fraction( aObjArea.Width(), _aVisArea.Width() ); + aScaleHeight = Fraction( aObjArea.Height(), _aVisArea.Height()); + } + } + } + + // Now is the favorable time to set the ObjArea. + // The Scaling must be considered. + SwRect aArea; + if ( pFlyPrtRect ) + { + aArea = *pFlyPrtRect; + aArea += pFlyFrameRect->Pos(); + } + else + { + aArea = GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() ); + aArea.Pos() += GetAnyCurRect( CurRectType::FlyEmbedded, nullptr, xObj.GetObject() ).Pos(); + } + + if ( bUseObjectSize ) + { + // --> this moves non-resizable object so that when adding borders the baseline remains the same + const SwFlyFrameFormat *pFlyFrameFormat = dynamic_cast< const SwFlyFrameFormat * >( GetFlyFrameFormat() ); + OSL_ENSURE( pFlyFrameFormat, "Could not find fly frame." ); + if ( pFlyFrameFormat ) + { + const Point &rPoint = pFlyFrameFormat->GetLastFlyFramePrtRectPos(); + SwRect aRect( pFlyPrtRect ? *pFlyPrtRect + : GetAnyCurRect( CurRectType::FlyEmbeddedPrt, nullptr, xObj.GetObject() )); + aArea += rPoint - aRect.Pos(); // adjust area by diff of printing area position in order to keep baseline alignment correct. + } + aArea.Width ( _aVisArea.Width() ); + aArea.Height( _aVisArea.Height() ); + RequestObjectResize( aArea, xObj.GetObject() ); + } + else + { + aArea.Width ( long( aArea.Width() / pCli->GetScaleWidth() ) ); + aArea.Height( long( aArea.Height() / pCli->GetScaleHeight() ) ); + } + + pCli->SetObjAreaAndScale( aArea.SVRect(), aScaleWidth, aScaleHeight ); +} + +void SwWrtShell::ConnectObj( svt::EmbeddedObjectRef& xObj, const SwRect &rPrt, + const SwRect &rFrame ) +{ + SfxInPlaceClient* pCli = GetView().FindIPClient( xObj.GetObject(), &GetView().GetEditWin()); + if ( !pCli ) + new SwOleClient( &GetView(), &GetView().GetEditWin(), xObj ); + CalcAndSetScale( xObj, &rPrt, &rFrame ); +} + +// Insert hard page break; +// Selections will be overwritten +void SwWrtShell::InsertPageBreak(const OUString *pPageDesc, const ::std::optional<sal_uInt16>& oPgNum ) +{ + ResetCursorStack(); + if( CanInsert() ) + { + SwActContext aActContext(this); + StartUndo(SwUndoId::UI_INSERT_PAGE_BREAK); + + if ( !IsCursorInTable() ) + { + if(HasSelection()) + DelRight(); + SwFEShell::SplitNode(); + // delete the numbered attribute of the last line if the last line is empty + GetDoc()->ClearLineNumAttrs( *GetCursor()->GetPoint() ); + } + + const SwPageDesc *pDesc = pPageDesc + ? FindPageDescByName( *pPageDesc, true ) : nullptr; + if( pDesc ) + { + SwFormatPageDesc aDesc( pDesc ); + aDesc.SetNumOffset( oPgNum ); + SetAttrItem( aDesc ); + } + else + SetAttrItem( SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK) ); + EndUndo(SwUndoId::UI_INSERT_PAGE_BREAK); + } + collectUIInformation("BREAK_PAGE", "parameter"); +} + +// Insert hard page break; +// Selections will be overwritten + +void SwWrtShell::InsertLineBreak() +{ + ResetCursorStack(); + if( CanInsert() ) + { + if(HasSelection()) + DelRight(); + + const sal_Unicode cIns = 0x0A; + SvxAutoCorrect* pACorr = lcl_IsAutoCorr(); + if( pACorr ) + AutoCorrect( *pACorr, cIns ); + else + SwWrtShell::Insert( OUString( cIns ) ); + } +} + +// Insert hard column break; +// Selections will be overwritten + +void SwWrtShell::InsertColumnBreak() +{ + SwActContext aActContext(this); + ResetCursorStack(); + if( CanInsert() ) + { + StartUndo(SwUndoId::UI_INSERT_COLUMN_BREAK); + + if ( !IsCursorInTable() ) + { + if(HasSelection()) + DelRight(); + SwFEShell::SplitNode( false, false ); + } + SetAttrItem(SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK)); + + EndUndo(SwUndoId::UI_INSERT_COLUMN_BREAK); + } +} + +// Insert footnote +// rStr - optional footnote mark + +void SwWrtShell::InsertFootnote(const OUString &rStr, bool bEndNote, bool bEdit ) +{ + ResetCursorStack(); + if( CanInsert() ) + { + if(HasSelection()) + { + //collapse cursor to the end + if(!IsCursorPtAtEnd()) + SwapPam(); + ClearMark(); + } + SwPosition aPos = *GetCursor()->GetPoint(); + SwFormatFootnote aFootNote( bEndNote ); + if(!rStr.isEmpty()) + aFootNote.SetNumStr( rStr ); + + SetAttrItem(aFootNote); + + if( bEdit ) + { + // For editing the footnote text. + Left(CRSR_SKIP_CHARS, false, 1, false ); + GotoFootnoteText(); + } + m_aNavigationMgr.addEntry(aPos); + } +} + +// SplitNode; also, because +// - of deleting selected content; +// - of reset of the Cursorstack if necessary. + +void SwWrtShell::SplitNode( bool bAutoFormat ) +{ + ResetCursorStack(); + if( CanInsert() ) + { + SwActContext aActContext(this); + + m_rView.GetEditWin().FlushInBuffer(); + bool bHasSel = HasSelection(); + if( bHasSel ) + { + StartUndo( SwUndoId::INSERT ); + DelRight(); + } + + SwFEShell::SplitNode( bAutoFormat ); + if( bHasSel ) + EndUndo( SwUndoId::INSERT ); + } +} + +// Turn on numbering +// Parameter: Optional specification of a name for the named list; +// this indicates a position if it is possible to convert them +// into a number and less than nMaxRules. + +// To test the CharFormats at the numbering +// external void SetNumChrFormat( SwWrtShell*, SwNumRules& ); + +// -> #i40041# +// Preconditions (as far as OD has figured out): +// - <SwEditShell::HasNumber()> is false, if <bNum> is true +// - <SwEditShell::HasBullet()> is false, if <bNum> is false +// Behavior of method is determined by the current situation at the current +// cursor position in the document. +void SwWrtShell::NumOrBulletOn(bool bNum) +{ + // determine numbering rule found at current cursor position in the document. + const SwNumRule* pCurRule = GetNumRuleAtCurrCursorPos(); + + StartUndo(SwUndoId::NUMORNONUM); + + const SwNumRule * pNumRule = pCurRule; + + // - activate outline rule respectively turning on outline rule for + // current text node. But, only for turning on a numbering (<bNum> == true). + // - overwrite found numbering rule at current cursor position, if + // no numbering rule can be retrieved from the paragraph style. + bool bContinueFoundNumRule( false ); + bool bActivateOutlineRule( false ); + int nActivateOutlineLvl( MAXLEVEL ); // only relevant, if <bActivateOutlineRule> == true + SwTextFormatColl * pColl = GetCurTextFormatColl(); + if ( pColl ) + { + // retrieve numbering rule at paragraph + // style, which is found at current cursor position in the document. + SwNumRule* pCollRule = mxDoc->FindNumRulePtr(pColl->GetNumRule().GetValue()); + // #125993# - The outline numbering rule isn't allowed + // to be derived from a parent paragraph style to a derived one. + // Thus check, if the found outline numbering rule is directly + // set at the paragraph style <pColl>. If not, set <pCollRule> to NULL + if ( pCollRule && pCollRule == GetDoc()->GetOutlineNumRule() ) + { + const SwNumRule* pDirectCollRule = + mxDoc->FindNumRulePtr(pColl->GetNumRule( false ).GetValue()); + if ( !pDirectCollRule ) + { + pCollRule = nullptr; + } + } + + if ( !pCollRule ) + { + pNumRule = pCollRule; + } + // no activation or continuation of outline numbering in Writer/Web document + else if ( bNum && + !dynamic_cast<SwWebDocShell*>(GetDoc()->GetDocShell()) && + pCollRule == GetDoc()->GetOutlineNumRule() ) + { + if ( pNumRule == pCollRule ) + { + // check, if text node at current cursor positioned is counted. + // If not, let it been counted. Then it has to be checked, + // of the outline numbering has to be activated or continued. + SwTextNode const*const pTextNode = sw::GetParaPropsNode( + *GetLayout(), GetCursor()->GetPoint()->nNode); + if ( pTextNode && !pTextNode->IsCountedInList() ) + { + // check, if numbering of the outline level of the paragraph + // style is active. If not, activate this outline level. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + OSL_ENSURE( pColl->IsAssignedToListLevelOfOutlineStyle(), + "<SwWrtShell::NumOrBulletOn(..)> - paragraph style with outline rule, but no outline level" ); + if ( pColl->IsAssignedToListLevelOfOutlineStyle() && + pCollRule->Get( static_cast<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + else + { + // #i101234# + // activate outline numbering, because from the precondition + // it's known, that <SwEdit::HasNumber()> == false + bActivateOutlineRule = true; + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + } + } + else if ( !pNumRule ) + { + // #i101234# + // Check, if corresponding list level of the outline numbering + // has already a numbering format set. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + if ( pCollRule->Get( static_cast<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering, because from the precondition + // it's known, that <SwEdit::HasNumber()> == false + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + else + { + // check, if numbering of the outline level of the paragraph + // style is active. If not, activate this outline level. + nActivateOutlineLvl = pColl->GetAssignedOutlineStyleLevel(); + OSL_ENSURE( pColl->IsAssignedToListLevelOfOutlineStyle(), + "<SwWrtShell::NumOrBulletOn(..)> - paragraph style with outline rule, but no outline level" ); + if ( pColl->IsAssignedToListLevelOfOutlineStyle() && + pCollRule->Get( static_cast<sal_uInt16>(nActivateOutlineLvl) ).GetNumberingType() + == SVX_NUM_NUMBER_NONE ) + { + // activate outline numbering + bActivateOutlineRule = true; + } + else + { + // turning on outline numbering at current cursor position + bContinueFoundNumRule = true; + } + } + pNumRule = pCollRule; + } + } + + // Only automatic numbering/bullet rules should be changed. + // Note: The outline numbering rule is also an automatic one. It's only + // changed, if it has to be activated. + if ( pNumRule ) + { + if ( !pNumRule->IsAutoRule() ) + { + pNumRule = nullptr; + } + else if ( pNumRule == GetDoc()->GetOutlineNumRule() && + !bActivateOutlineRule && !bContinueFoundNumRule ) + { + pNumRule = nullptr; + } + } + + // Search for a previous numbering/bullet rule to continue it. + OUString sContinuedListId; + if ( !pNumRule ) + { + pNumRule = GetDoc()->SearchNumRule( *GetCursor()->GetPoint(), + false, bNum, false, 0, + sContinuedListId, GetLayout() ); + bContinueFoundNumRule = pNumRule != nullptr; + } + + if (pNumRule) + { + SwNumRule aNumRule(*pNumRule); + + // do not change found numbering/bullet rule, if it should only be continued. + if ( !bContinueFoundNumRule ) + { + SwTextNode const*const pTextNode = sw::GetParaPropsNode( + *GetLayout(), GetCursor()->GetPoint()->nNode); + + if (pTextNode) + { + // use above retrieve outline level, if outline numbering has to be activated. + int nLevel = bActivateOutlineRule + ? nActivateOutlineLvl + : pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + SwNumFormat aFormat(aNumRule.Get(static_cast<sal_uInt16>(nLevel))); + + if (bNum) + aFormat.SetNumberingType(SVX_NUM_ARABIC); + else + { + // #i63395# Only apply user defined default bullet font + if ( numfunc::IsDefBulletFontUserDefined() ) + { + const vcl::Font* pFnt = &numfunc::GetDefBulletFont(); + aFormat.SetBulletFont( pFnt ); + } + aFormat.SetBulletChar( numfunc::GetBulletChar(static_cast<sal_uInt8>(nLevel))); + aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFormat.SetPrefix(OUString()); + aFormat.SetSuffix(OUString()); + } + aNumRule.Set(static_cast<sal_uInt16>(nLevel), aFormat); + } + } + + // reset indent attribute on applying list style + SetCurNumRule( aNumRule, false, sContinuedListId, true ); + } + else + { + // #i95907# + const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode( + numfunc::GetDefaultPositionAndSpaceMode() ); + SwNumRule aNumRule( GetUniqueNumRuleName(), ePosAndSpaceMode ); + // Append the character template at the numbering. + SwCharFormat* pChrFormat; + SwDocShell* pDocSh = GetView().GetDocShell(); + // #i63395# + // Only apply user defined default bullet font + const vcl::Font* pFnt = numfunc::IsDefBulletFontUserDefined() + ? &numfunc::GetDefBulletFont() + : nullptr; + + if (bNum) + { + pChrFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL ); + } + else + { + pChrFormat = GetCharFormatFromPool( RES_POOLCHR_BULLET_LEVEL ); + } + + const SwTextNode *const pTextNode = sw::GetParaPropsNode(*GetLayout(), + GetCursor()->GetPoint()->nNode); + const SwTwips nWidthOfTabs = pTextNode + ? pTextNode->GetWidthOfLeadingTabs() + : 0; + GetDoc()->getIDocumentContentOperations().RemoveLeadingWhiteSpace( *GetCursor()->GetPoint() ); + + const bool bHtml = dynamic_cast<SwWebDocShell*>( pDocSh ) != nullptr; + const bool bRightToLeft = IsInRightToLeftText(); + for( sal_uInt8 nLvl = 0; nLvl < MAXLEVEL; ++nLvl ) + { + SwNumFormat aFormat( aNumRule.Get( nLvl ) ); + aFormat.SetCharFormat( pChrFormat ); + + if (! bNum) + { + // #i63395# + // Only apply user defined default bullet font + if ( pFnt ) + { + aFormat.SetBulletFont( pFnt ); + } + aFormat.SetBulletChar( numfunc::GetBulletChar(nLvl) ); + aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFormat.SetPrefix(OUString()); + aFormat.SetSuffix(OUString()); + } + + // #i95907# + if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + if(bHtml && nLvl) + { + // 1/2" for HTML + aFormat.SetAbsLSpace(nLvl * 720); + } + else if ( nWidthOfTabs > 0 ) + { + aFormat.SetAbsLSpace(nWidthOfTabs + nLvl * 720); + } + } + + // #i38904# Default alignment for + // numbering/bullet should be rtl in rtl paragraph: + if ( bRightToLeft ) + { + aFormat.SetNumAdjust( SvxAdjust::Right ); + } + + aNumRule.Set( nLvl, aFormat ); + } + + // #i95907# + if ( pTextNode && + ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT ) + { + + const SwTwips nTextNodeIndent = pTextNode->GetAdditionalIndentForStartingNewList(); + if ( ( nTextNodeIndent + nWidthOfTabs ) != 0 ) + { + // #i111172#/fdo#85666 + // If text node is already inside a list, assure that the indents + // are the same. Thus, adjust the indent change value by subtracting + // indents of to be applied list style. + SwTwips nIndentChange = nTextNodeIndent + nWidthOfTabs; + if ( pTextNode->GetNumRule() ) + { + int nLevel = pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& aFormat( aNumRule.Get( nLevel ) ); + if ( aFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + nIndentChange -= aFormat.GetIndentAt() + aFormat.GetFirstLineIndent(); + } + } + aNumRule.ChangeIndent( nIndentChange ); + } + } + // reset indent attribute on applying list style + // start new list + SetCurNumRule( aNumRule, true, OUString(), true ); + } + + EndUndo(SwUndoId::NUMORNONUM); +} +// <- #i40041# + +void SwWrtShell::NumOn() +{ + NumOrBulletOn(true); +} + +void SwWrtShell::NumOrBulletOff() +{ + const SwNumRule * pCurNumRule = GetNumRuleAtCurrCursorPos(); + + if (pCurNumRule) + { + if (pCurNumRule->IsOutlineRule()) + { + SwNumRule aNumRule(*pCurNumRule); + + SwTextNode * pTextNode = + sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode); + + if (pTextNode) + { + int nLevel = pTextNode->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + SwNumFormat aFormat(aNumRule.Get(static_cast<sal_uInt16>(nLevel))); + + aFormat.SetNumberingType(SVX_NUM_NUMBER_NONE); + aNumRule.Set(nLevel, aFormat); + + // no start or continuation of a list - the outline style is only changed. + SetCurNumRule( aNumRule, false ); + } + } + else + { + DelNumRules(); + } + + // #126346# - Cursor can not be anymore in front of + // a label, because numbering/bullet is switched off. + SetInFrontOfLabel( false ); + } +} +// <- #i29560# + +// Request Default-Bulletlist + +void SwWrtShell::BulletOn() +{ + NumOrBulletOn(false); +} + +SelectionType SwWrtShell::GetSelectionType() const +{ + // ContentType cannot be determined within a Start-/EndAction. + // Because there is no invalid value TEXT will be returned. + // The value does not matter, it may be updated in EndAction anyway. + + if (ActionPend()) + return IsSelFrameMode() ? SelectionType::Frame : SelectionType::Text; + + SwView &_rView = const_cast<SwView&>(GetView()); + if (_rView.GetPostItMgr() && _rView.GetPostItMgr()->HasActiveSidebarWin() ) + return SelectionType::PostIt; + + // Inserting a frame is not a DrawMode + SelectionType nCnt; + if ( !_rView.GetEditWin().IsFrameAction() && + (IsObjSelected() || (_rView.IsDrawMode() && !IsFrameSelected()) )) + { + if (GetDrawView()->IsTextEdit()) + nCnt = SelectionType::DrawObjectEditMode; + else + { + if (GetView().IsFormMode()) // Only Form selected + nCnt = SelectionType::DbForm; + else + nCnt = SelectionType::DrawObject; // Any draw object + + if (_rView.IsBezierEditMode()) + nCnt |= SelectionType::Ornament; + else if( GetDrawView()->GetContext() == SdrViewContext::Media ) + nCnt |= SelectionType::Media; + + if (svx::checkForSelectedCustomShapes( GetDrawView(), true /* bOnlyExtruded */ )) + { + nCnt |= SelectionType::ExtrudedCustomShape; + } + sal_uInt32 nCheckStatus = 0; + if (svx::checkForSelectedFontWork( GetDrawView(), nCheckStatus )) + { + nCnt |= SelectionType::FontWork; + } + } + + return nCnt; + } + + nCnt = static_cast<SelectionType>(GetCntType()); + + if ( IsFrameSelected() ) + { + if (_rView.IsDrawMode()) + _rView.LeaveDrawCreate(); // clean up (Bug #45639) + if ( !(nCnt & (SelectionType::Graphic | SelectionType::Ole)) ) + return SelectionType::Frame; + } + + if ( IsCursorInTable() ) + nCnt |= SelectionType::Table; + + if ( IsTableMode() ) + { + nCnt |= SelectionType::Table | SelectionType::TableCell; + SwTable::SearchType eTableSel = GetEnhancedTableSelection(); + if ( eTableSel == SwTable::SEARCH_ROW ) + nCnt |= SelectionType::TableRow; + else if ( eTableSel == SwTable::SEARCH_COL ) + nCnt |= SelectionType::TableCol; + } + + // Do not pop up numbering toolbar, if the text node has a numbering of type SVX_NUM_NUMBER_NONE. + const SwNumRule* pNumRule = GetNumRuleAtCurrCursorPos(); + if ( pNumRule ) + { + const SwTextNode* pTextNd = + sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode); + + if ( pTextNd && pTextNd->IsInList() ) + { + int nLevel = pTextNd->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat& rFormat = pNumRule->Get(nLevel); + if ( SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ) + nCnt |= SelectionType::NumberList; + } + } + + return nCnt; +} + +// Find the text collection with the name rCollname +// Returns: Pointer at the collection or 0, if no +// text collection with this name exists, or +// this is a default template. + +SwTextFormatColl *SwWrtShell::GetParaStyle(const OUString &rCollName, GetStyle eCreate ) +{ + SwTextFormatColl* pColl = FindTextFormatCollByName( rCollName ); + if( !pColl && GETSTYLE_NOCREATE != eCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rCollName, SwGetPoolIdFromName::TxtColl ); + if( USHRT_MAX != nId || GETSTYLE_CREATEANY == eCreate ) + pColl = GetTextCollFromPool( nId ); + } + return pColl; +} + +// Find the text collection with the name rCollname +// Returns: Pointer at the collection or 0, if no +// character template with this name exists, or +// this is a default template or template is automatic. + +SwCharFormat *SwWrtShell::GetCharStyle(const OUString &rFormatName, GetStyle eCreate ) +{ + SwCharFormat* pFormat = FindCharFormatByName( rFormatName ); + if( !pFormat && GETSTYLE_NOCREATE != eCreate ) + { + sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rFormatName, SwGetPoolIdFromName::ChrFmt ); + if( USHRT_MAX != nId || GETSTYLE_CREATEANY == eCreate ) + pFormat = static_cast<SwCharFormat*>(GetFormatFromPool( nId )); + } + return pFormat; +} + +// Find the table format with the name rFormatname +// Returns: Pointer at the collection or 0, if no +// frame format with this name exists or +// this is a default format or the format is automatic. + +SwFrameFormat *SwWrtShell::GetTableStyle(const OUString &rFormatName) +{ + for( size_t i = GetTableFrameFormatCount(); i; ) + { + SwFrameFormat *pFormat = &GetTableFrameFormat( --i ); + if( !pFormat->IsDefault() && + pFormat->GetName() == rFormatName && IsUsed( *pFormat ) ) + return pFormat; + } + return nullptr; +} + +void SwWrtShell::addCurrentPosition() { + SwPaM* pPaM = GetCursor(); + m_aNavigationMgr.addEntry(*pPaM->GetPoint()); +} + +// Applying templates + +void SwWrtShell::SetPageStyle(const OUString &rCollName) +{ + if( !SwCursorShell::HasSelection() && !IsSelFrameMode() && !IsObjSelected() ) + { + SwPageDesc* pDesc = FindPageDescByName( rCollName, true ); + if( pDesc ) + ChgCurPageDesc( *pDesc ); + } +} + +// Access templates + +OUString const & SwWrtShell::GetCurPageStyle() const +{ + return GetPageDesc(GetCurPageDesc( false/*bCalcFrame*/ )).GetName(); +} + +// Change the current template referring to the existing change. + +void SwWrtShell::QuickUpdateStyle() +{ + SwTextFormatColl *pColl = GetCurTextFormatColl(); + + // Default cannot be changed + if(pColl && !pColl->IsDefault()) + { + FillByEx(pColl); + // Also apply the template to remove hard attribute assignment. + SetTextFormatColl(pColl); + } +} + +void SwWrtShell::AutoUpdatePara(SwTextFormatColl* pColl, const SfxItemSet& rStyleSet, SwPaM* pPaM ) +{ + SwPaM* pCursor = pPaM ? pPaM : GetCursor( ); + SfxItemSet aCoreSet( + GetAttrPool(), + svl::Items< + RES_CHRATR_BEGIN, RES_CHRATR_END - 1, + RES_PARATR_BEGIN, RES_PARATR_END - 1, + RES_FRMATR_BEGIN, RES_FRMATR_END - 1, + SID_ATTR_TABSTOP_DEFAULTS,SID_ATTR_TABSTOP_OFFSET, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_PARA_MODEL, SID_ATTR_PARA_KEEP, + SID_ATTR_PARA_PAGENUM, SID_ATTR_PARA_PAGENUM>{}); + GetPaMAttr( pCursor, aCoreSet ); + bool bReset = false; + SfxItemIter aParaIter( aCoreSet ); + for (auto pParaItem = aParaIter.GetCurItem(); pParaItem; pParaItem = aParaIter.NextItem()) + { + if(!IsInvalidItem(pParaItem)) + { + sal_uInt16 nWhich = pParaItem->Which(); + if(SfxItemState::SET == aCoreSet.GetItemState(nWhich) && + SfxItemState::SET == rStyleSet.GetItemState(nWhich)) + { + aCoreSet.ClearItem(nWhich); + bReset = true; + } + } + } + StartAction(); + if(bReset) + { + ResetAttr( std::set<sal_uInt16>(), pCursor ); + SetAttrSet(aCoreSet, SetAttrMode::DEFAULT, pCursor); + } + mxDoc->ChgFormat(*pColl, rStyleSet ); + EndAction(); +} + +void SwWrtShell::AutoUpdateFrame( SwFrameFormat* pFormat, const SfxItemSet& rStyleSet ) +{ + StartAction(); + + ResetFlyFrameAttr( &rStyleSet ); + pFormat->SetFormatAttr( rStyleSet ); + + EndAction(); +} + +void SwWrtShell::AutoCorrect( SvxAutoCorrect& rACorr, sal_Unicode cChar ) +{ + ResetCursorStack(); + if(CanInsert()) + { + bool bStarted = false; + SwRewriter aRewriter; + + if(HasSelection()) + { + // Only parentheses here, because the regular insert + // is already clipped to the editshell + StartAllAction(); + + OUString aTmpStr1 = SwResId(STR_START_QUOTE) + + GetSelText() + + SwResId(STR_END_QUOTE); + OUString aTmpStr3 = SwResId(STR_START_QUOTE) + + OUStringChar(cChar) + + SwResId(STR_END_QUOTE); + aRewriter.AddRule( UndoArg1, aTmpStr1 ); + aRewriter.AddRule( UndoArg2, SwResId(STR_YIELDS) ); + aRewriter.AddRule( UndoArg3, aTmpStr3 ); + + StartUndo( SwUndoId::REPLACE, &aRewriter ); + bStarted = true; + DelRight(); + } + SwEditShell::AutoCorrect( rACorr, IsInsMode(), cChar ); + + if(bStarted) + { + EndAllAction(); + EndUndo( SwUndoId::REPLACE, &aRewriter ); + } + } +} + +// Some kind of controlled copy ctor + +SwWrtShell::SwWrtShell( SwWrtShell& rSh, vcl::Window *_pWin, SwView &rShell ) + : SwFEShell(rSh, _pWin) + , m_rView(rShell) + , m_aNavigationMgr(*this) +{ + BITFLD_INI_LIST + SET_CURR_SHELL( this ); + + SetSfxViewShell( static_cast<SfxViewShell *>(&rShell) ); + SetFlyMacroLnk( LINK(this, SwWrtShell, ExecFlyMac) ); + + // place the cursor on the first field... + IFieldmark *pBM = nullptr; + if ( IsFormProtected() && ( pBM = GetFieldmarkAfter( ) ) !=nullptr ) { + GotoFieldmark(pBM); + } +} + +SwWrtShell::SwWrtShell( SwDoc& rDoc, vcl::Window *_pWin, SwView &rShell, + const SwViewOption *pViewOpt ) + : SwFEShell(rDoc, _pWin, pViewOpt) + , m_rView(rShell) + , m_aNavigationMgr(*this) +{ + BITFLD_INI_LIST + SET_CURR_SHELL( this ); + SetSfxViewShell( static_cast<SfxViewShell *>(&rShell) ); + SetFlyMacroLnk( LINK(this, SwWrtShell, ExecFlyMac) ); + + // place the cursor on the first field... + IFieldmark *pBM = nullptr; + if ( IsFormProtected() && ( pBM = GetFieldmarkAfter( ) ) !=nullptr ) { + GotoFieldmark(pBM); + } +} + +SwWrtShell::~SwWrtShell() +{ + SET_CURR_SHELL( this ); + while(IsModePushed()) + PopMode(); + while(PopCursor(false)) + ; + SwTransferable::ClearSelection( *this ); +} + +bool SwWrtShell::Pop(SwCursorShell::PopMode const eDelete) +{ + bool bRet = SwCursorShell::Pop(eDelete); + if( bRet && IsSelection() ) + { + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + m_fnKillSel = &SwWrtShell::ResetSelect; + } + return bRet; +} + +bool SwWrtShell::CanInsert() +{ + if(IsSelFrameMode()) + { + return false; + } + + if(IsObjSelected()) + { + return false; + } + + if(GetView().GetDrawFuncPtr()) + { + return false; + } + + if(GetView().GetPostItMgr()->GetActiveSidebarWin()) + { + return false; + } + + return true; +} + +void SwWrtShell::ChgDBData(const SwDBData& aDBData) +{ + SwEditShell::ChgDBData(aDBData); + //notify the db-beamer if available + GetView().NotifyDBChanged(); +} + +OUString SwWrtShell::GetSelDescr() const +{ + OUString aResult; + + SelectionType nSelType = GetSelectionType(); + switch (nSelType) + { + case SelectionType::Graphic: + aResult = SwResId(STR_GRAPHIC); + + break; + case SelectionType::Frame: + { + const SwFrameFormat * pFrameFormat = GetSelectedFrameFormat(); + + if (pFrameFormat) + aResult = pFrameFormat->GetDescription(); + } + break; + case SelectionType::DrawObject: + { + aResult = SwResId(STR_DRAWING_OBJECTS); + } + break; + default: + if (mxDoc) + aResult = GetCursorDescr(); + } + + return aResult; +} + +void SwWrtShell::ApplyViewOptions( const SwViewOption &rOpt ) +{ + SwFEShell::ApplyViewOptions( rOpt ); + //#i115062# invalidate meta character slot + GetView().GetViewFrame()->GetBindings().Invalidate( FN_VIEW_META_CHARS ); +} + +void SwWrtShell::SetReadonlyOption(bool bSet) +{ + GetView().GetEditWin().GetFrameControlsManager().SetReadonlyControls( bSet ); + SwViewShell::SetReadonlyOption( bSet ); +} + +// Switch on/off header or footer of a page style - if an empty name is +// given all styles are changed + +void SwWrtShell::ChangeHeaderOrFooter( + const OUString& rStyleName, bool bHeader, bool bOn, bool bShowWarning) +{ + SdrView *const pSdrView = GetDrawView(); + if (pSdrView && pSdrView->IsTextEdit()) + { // tdf#107474 deleting header may delete active drawing object + pSdrView->SdrEndTextEdit(true); + } + addCurrentPosition(); + StartAllAction(); + StartUndo( SwUndoId::HEADER_FOOTER ); // #i7983# + bool bExecute = true; + bool bCursorSet = false; + for( size_t nFrom = 0, nTo = GetPageDescCnt(); + nFrom < nTo; ++nFrom ) + { + SwPageDesc aDesc( GetPageDesc( nFrom )); + OUString sTmp(aDesc.GetName()); + if( rStyleName.isEmpty() || rStyleName == sTmp ) + { + bool bChgd = false; + + if( bShowWarning && !bOn && GetActiveView() && GetActiveView() == &GetView() && + ( (bHeader && aDesc.GetMaster().GetHeader().IsActive()) || + (!bHeader && aDesc.GetMaster().GetFooter().IsActive()) ) ) + { + bShowWarning = false; + //Actions have to be closed while the dialog is showing + EndAllAction(); + + weld::Window* pParent = GetView().GetFrameWeld(); + short nResult; + if (bHeader) { + nResult = DeleteHeaderDialog(pParent).run(); + } else { + nResult = DeleteFooterDialog(pParent).run(); + } + + bExecute = nResult == RET_YES; + StartAllAction(); + if (nResult == RET_YES) + ToggleHeaderFooterEdit(); + } + if( bExecute ) + { + bChgd = true; + SwFrameFormat &rMaster = aDesc.GetMaster(); + if(bHeader) + rMaster.SetFormatAttr( SwFormatHeader( bOn )); + else + rMaster.SetFormatAttr( SwFormatFooter( bOn )); + if( bOn ) + { + SvxULSpaceItem aUL(bHeader ? 0 : MM50, bHeader ? MM50 : 0, RES_UL_SPACE ); + SwFrameFormat* pFormat = bHeader ? + const_cast<SwFrameFormat*>(rMaster.GetHeader().GetHeaderFormat()) : + const_cast<SwFrameFormat*>(rMaster.GetFooter().GetFooterFormat()); + pFormat->SetFormatAttr( aUL ); + } + } + if( bChgd ) + { + ChgPageDesc( nFrom, aDesc ); + + if( !bCursorSet && bOn ) + { + if ( !IsHeaderFooterEdit() ) + ToggleHeaderFooterEdit(); + bCursorSet = SetCursorInHdFt( + rStyleName.isEmpty() ? SIZE_MAX : nFrom, + bHeader ); + } + } + } + } + EndUndo( SwUndoId::HEADER_FOOTER ); // #i7983# + EndAllAction(); +} + +void SwWrtShell::SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) +{ + SwViewShell::SetShowHeaderFooterSeparator( eControl, bShow ); + if ( !bShow ) + GetView().GetEditWin().GetFrameControlsManager().HideControls( eControl ); +} + +void SwWrtShell::InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq) +{ + SwPostItField* pPostIt = dynamic_cast<SwPostItField*>(rFieldMgr.GetCurField()); + bool bNew = !(pPostIt && pPostIt->GetTyp()->Which() == SwFieldIds::Postit); + if (bNew || GetView().GetPostItMgr()->IsAnswer()) + { + const SvxPostItAuthorItem* pAuthorItem = rReq.GetArg<SvxPostItAuthorItem>(SID_ATTR_POSTIT_AUTHOR); + OUString sAuthor; + if ( pAuthorItem ) + sAuthor = pAuthorItem->GetValue(); + else + { + std::size_t nAuthor = SW_MOD()->GetRedlineAuthor(); + sAuthor = SW_MOD()->GetRedlineAuthor(nAuthor); + } + + const SvxPostItTextItem* pTextItem = rReq.GetArg<SvxPostItTextItem>(SID_ATTR_POSTIT_TEXT); + OUString sText; + if ( pTextItem ) + sText = pTextItem->GetValue(); + + // If we have a text already registered for answer, use that + if (GetView().GetPostItMgr()->IsAnswer() && !GetView().GetPostItMgr()->GetAnswerText().isEmpty()) + { + sText = GetView().GetPostItMgr()->GetAnswerText(); + GetView().GetPostItMgr()->RegisterAnswerText(OUString()); + } + + if ( HasSelection() && !IsTableMode() ) + { + KillPams(); + } + + // #i120513# Inserting a comment into an autocompletion crashes + // --> suggestion has to be removed before + GetView().GetEditWin().StopQuickHelp(); + + SwInsertField_Data aData(SwFieldTypesEnum::Postit, 0, sAuthor, sText, 0); + + if (IsSelFrameMode()) + { + SwFlyFrame* pFly = GetSelectedFlyFrame(); + + // Remember the anchor of the selected object before deletion. + std::unique_ptr<SwPosition> pAnchor; + if (pFly) + { + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat) + { + RndStdIds eAnchorId = pFormat->GetAnchor().GetAnchorId(); + if ((eAnchorId == RndStdIds::FLY_AS_CHAR || eAnchorId == RndStdIds::FLY_AT_CHAR) && pFormat->GetAnchor().GetContentAnchor()) + { + pAnchor.reset(new SwPosition(*pFormat->GetAnchor().GetContentAnchor())); + } + } + } + + // A frame is selected, end frame selection. + EnterStdMode(); + GetView().AttrChangedNotify(nullptr); + + // Set up text selection, so the anchor of the frame will be the anchor of the + // comment. + if (pFly) + { + *GetCurrentShellCursor().GetPoint() = *pAnchor; + SwFrameFormat* pFormat = pFly->GetFormat(); + if (pFormat && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) + { + Right(CRSR_SKIP_CELLS, /*bSelect=*/true, 1, /*bBasicCall=*/false, /*bVisual=*/true); + } + else if (pFormat && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR) + { + aData.m_pAnnotationRange.reset(new SwPaM(*GetCurrentShellCursor().Start(), + *GetCurrentShellCursor().End())); + } + } + } + + rFieldMgr.InsertField( aData ); + + Push(); + SwCursorShell::Left(1, CRSR_SKIP_CHARS); + pPostIt = static_cast<SwPostItField*>(rFieldMgr.GetCurField()); + Pop(SwCursorShell::PopMode::DeleteCurrent); // Restore cursor position + } + + // Client has disabled annotations rendering, no need to + // focus the postit field + if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) + return; + + if (pPostIt) + { + SwFieldType* pType = GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false); + if(auto pFormat = pType->FindFormatForField(pPostIt)) + pFormat->Broadcast( SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::FOCUS, &GetView() ) ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh2.cxx b/sw/source/uibase/wrtsh/wrtsh2.cxx new file mode 100644 index 000000000..5dc536a95 --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh2.cxx @@ -0,0 +1,606 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * 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/macitem.hxx> +#include <sfx2/frame.hxx> +#include <svl/eitem.hxx> +#include <svl/listener.hxx> +#include <svl/stritem.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/viewfrm.hxx> +#include <sot/exchange.hxx> +#include <fmtinfmt.hxx> +#include <wrtsh.hxx> +#include <docsh.hxx> +#include <fldbas.hxx> +#include <expfld.hxx> +#include <docufld.hxx> +#include <reffld.hxx> +#include <swundo.hxx> +#include <doc.hxx> +#include <frmfmt.hxx> +#include <fmtfld.hxx> +#include <view.hxx> +#include <swevent.hxx> +#include <section.hxx> +#include <navicont.hxx> +#include <txtinet.hxx> +#include <cmdid.h> +#include <swabstdlg.hxx> +#include <SwRewriter.hxx> + +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + +#include <memory> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> +#include <sfx2/event.hxx> +#include <sal/log.hxx> + +void SwWrtShell::Insert(SwField const& rField, SwPaM* pAnnotationRange) +{ + ResetCursorStack(); + if(!CanInsert()) + return; + StartAllAction(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, rField.GetDescription()); + + StartUndo(SwUndoId::INSERT, &aRewriter); + + bool bDeleted = false; + std::unique_ptr<SwPaM> pAnnotationTextRange; + if (pAnnotationRange) + { + pAnnotationTextRange.reset(new SwPaM(*pAnnotationRange->Start(), *pAnnotationRange->End())); + } + + if ( HasSelection() ) + { + if ( rField.GetTyp()->Which() == SwFieldIds::Postit ) + { + // for annotation fields: + // - keep the current selection in order to create a corresponding annotation mark + // - collapse cursor to its end + if ( IsTableMode() ) + { + GetTableCrs()->Normalize( false ); + const SwPosition rStartPos( *(GetTableCrs()->GetMark()->nNode.GetNode().GetContentNode()), 0 ); + KillPams(); + if ( !IsEndOfPara() ) + { + EndPara(); + } + const SwPosition rEndPos( *GetCurrentShellCursor().GetPoint() ); + pAnnotationTextRange.reset(new SwPaM( rStartPos, rEndPos )); + } + else + { + NormalizePam( false ); + const SwPaM& rCurrPaM = GetCurrentShellCursor(); + pAnnotationTextRange.reset(new SwPaM( *rCurrPaM.GetPoint(), *rCurrPaM.GetMark() )); + ClearMark(); + } + } + else + { + bDeleted = DelRight(); + } + } + + SwEditShell::Insert2(rField, bDeleted); + + if ( pAnnotationTextRange ) + { + if ( GetDoc() != nullptr ) + { + const SwPaM& rCurrPaM = GetCurrentShellCursor(); + if (*rCurrPaM.Start() == *pAnnotationTextRange->Start() + && *rCurrPaM.End() == *pAnnotationTextRange->End()) + { + // Annotation range was passed in externally, and inserting the postit field shifted + // its start/end positions right by one. Restore the original position for the range + // start. This allows commenting on the placeholder character of the field. + SwIndex& rRangeStart = pAnnotationTextRange->Start()->nContent; + if (rRangeStart.GetIndex() > 0) + --rRangeStart; + } + IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess(); + pMarksAccess->makeAnnotationMark( *pAnnotationTextRange, OUString() ); + } + pAnnotationTextRange.reset(); + } + + EndUndo(); + EndAllAction(); +} + +// Start the field update + +void SwWrtShell::UpdateInputFields( SwInputFieldList* pLst ) +{ + // Go through the list of fields and updating + std::unique_ptr<SwInputFieldList> pTmp; + if (!pLst) + { + pTmp.reset(new SwInputFieldList( this )); + pLst = pTmp.get(); + } + + const size_t nCnt = pLst->Count(); + if(nCnt) + { + pLst->PushCursor(); + + bool bCancel = false; + + size_t nIndex = 0; + FieldDialogPressedButton ePressedButton = FieldDialogPressedButton::NONE; + + SwField* pField = GetCurField(); + if (pField) + { + for (size_t i = 0; i < nCnt; i++) + { + if (pField == pLst->GetField(i)) + { + nIndex = i; + break; + } + } + } + + while (!bCancel) + { + bool bPrev = nIndex > 0; + bool bNext = nIndex < nCnt - 1; + pLst->GotoFieldPos(nIndex); + pField = pLst->GetField(nIndex); + if (pField->GetTyp()->Which() == SwFieldIds::Dropdown) + { + bCancel = StartDropDownFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); + } + else + bCancel = StartInputFieldDlg(pField, bPrev, bNext, GetView().GetFrameWeld(), &ePressedButton); + + if (!bCancel) + { + // Otherwise update error at multi-selection: + pLst->GetField(nIndex)->GetTyp()->UpdateFields(); + + if (ePressedButton == FieldDialogPressedButton::Previous && nIndex > 0) + nIndex--; + else if (ePressedButton == FieldDialogPressedButton::Next && nIndex < nCnt - 1) + nIndex++; + else + bCancel = true; + } + } + + pLst->PopCursor(); + } +} + +namespace { + +// Listener class: will close InputField dialog if input field(s) +// is(are) deleted (for instance, by an extension) after the dialog shows up. +// Otherwise, the for loop in SwWrtShell::UpdateInputFields will crash when doing: +// 'pTmp->GetField( i )->GetTyp()->UpdateFields();' +// on a deleted field. +class FieldDeletionListener : public SvtListener +{ + public: + FieldDeletionListener(AbstractFieldInputDlg* pInputFieldDlg, SwField* pField) + : mpInputFieldDlg(pInputFieldDlg) + , mpFormatField(nullptr) + { + SwInputField *const pInputField(dynamic_cast<SwInputField*>(pField)); + SwSetExpField *const pSetExpField(dynamic_cast<SwSetExpField*>(pField)); + + if (pInputField && pInputField->GetFormatField()) + { + mpFormatField = pInputField->GetFormatField(); + } + else if (pSetExpField && pSetExpField->GetFormatField()) + { + mpFormatField = pSetExpField->GetFormatField(); + } + + // Register for possible field deletion while dialog is open + if (mpFormatField) + StartListening(mpFormatField->GetNotifier()); + } + + virtual ~FieldDeletionListener() override + { + // Dialog closed, remove modification listener + EndListeningAll(); + } + + virtual void Notify(const SfxHint& rHint) override + { + // Input field has been deleted: better to close the dialog + if(rHint.GetId() == SfxHintId::Dying) + { + mpFormatField = nullptr; + mpInputFieldDlg->EndDialog(RET_CANCEL); + } + } + private: + VclPtr<AbstractFieldInputDlg> mpInputFieldDlg; + SwFormatField* mpFormatField; +}; + +} + +// Start input dialog for a specific field +bool SwWrtShell::StartInputFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, + weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) +{ + + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractFieldInputDlg> pDlg(pFact->CreateFieldInputDlg(pParentWin, *this, pField, bPrevButton, bNextButton)); + + bool bRet; + + { + FieldDeletionListener aModify(pDlg.get(), pField); + bRet = RET_CANCEL == pDlg->Execute(); + } + + if (pPressedButton) + { + if (pDlg->PrevButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Previous; + else if (pDlg->NextButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Next; + } + + pDlg.disposeAndClear(); + GetWin()->PaintImmediately(); + return bRet; +} + +bool SwWrtShell::StartDropDownFieldDlg(SwField* pField, bool bPrevButton, bool bNextButton, + weld::Widget* pParentWin, SwWrtShell::FieldDialogPressedButton* pPressedButton) +{ + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractDropDownFieldDialog> pDlg(pFact->CreateDropDownFieldDialog(pParentWin, *this, pField, bPrevButton, bNextButton)); + const short nRet = pDlg->Execute(); + + if (pPressedButton) + { + if (pDlg->PrevButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Previous; + else if (pDlg->NextButtonPressed()) + *pPressedButton = FieldDialogPressedButton::Next; + } + + pDlg.disposeAndClear(); + bool bRet = RET_CANCEL == nRet; + GetWin()->PaintImmediately(); + if(RET_YES == nRet) + { + GetView().GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FIELD, SfxCallMode::SYNCHRON); + } + return bRet; +} + +// Insert directory - remove selection + +void SwWrtShell::InsertTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) +{ + if(!CanInsert()) + return; + + if(HasSelection()) + DelRight(); + + SwEditShell::InsertTableOf(rTOX, pSet); +} + +// Update directory - remove selection + +void SwWrtShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet) +{ + if(CanInsert()) + { + SwEditShell::UpdateTableOf(rTOX, pSet); + } +} + +// handler for click on the field given as parameter. +// the cursor is positioned on the field. + +void SwWrtShell::ClickToField( const SwField& rField ) +{ + // cross reference field must not be selected because it moves the cursor + if (SwFieldIds::GetRef != rField.GetTyp()->Which()) + { + StartAllAction(); + Right( CRSR_SKIP_CHARS, true, 1, false ); // Select the field. + NormalizePam(); + EndAllAction(); + } + + m_bIsInClickToEdit = true; + switch( rField.GetTyp()->Which() ) + { + case SwFieldIds::JumpEdit: + { + sal_uInt16 nSlotId = 0; + switch( rField.GetFormat() ) + { + case JE_FMT_TABLE: + nSlotId = FN_INSERT_TABLE; + break; + + case JE_FMT_FRAME: + nSlotId = FN_INSERT_FRAME; + break; + + case JE_FMT_GRAPHIC: nSlotId = SID_INSERT_GRAPHIC; break; + case JE_FMT_OLE: nSlotId = SID_INSERT_OBJECT; break; + + } + + if( nSlotId ) + { + StartUndo( SwUndoId::START ); + //#97295# immediately select the right shell + GetView().StopShellTimer(); + GetView().GetViewFrame()->GetDispatcher()->Execute( nSlotId, + SfxCallMode::SYNCHRON|SfxCallMode::RECORD ); + EndUndo( SwUndoId::END ); + } + } + break; + + case SwFieldIds::Macro: + { + const SwMacroField *pField = static_cast<const SwMacroField*>(&rField); + const OUString sText( rField.GetPar2() ); + OUString sRet( sText ); + ExecMacro( pField->GetSvxMacro(), &sRet ); + + // return value changed? + if( sRet != sText ) + { + StartAllAction(); + const_cast<SwField&>(rField).SetPar2( sRet ); + rField.GetTyp()->UpdateFields(); + EndAllAction(); + } + } + break; + + case SwFieldIds::GetRef: + StartAllAction(); + SwCursorShell::GotoRefMark( static_cast<const SwGetRefField&>(rField).GetSetRefName(), + static_cast<const SwGetRefField&>(rField).GetSubType(), + static_cast<const SwGetRefField&>(rField).GetSeqNo() ); + EndAllAction(); + break; + + case SwFieldIds::Input: + { + const SwInputField* pInputField = dynamic_cast<const SwInputField*>(&rField); + if ( pInputField == nullptr ) + { + StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + } + } + break; + + case SwFieldIds::SetExp: + if( static_cast<const SwSetExpField&>(rField).GetInputFlag() ) + StartInputFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + break; + case SwFieldIds::Dropdown : + StartDropDownFieldDlg(const_cast<SwField*>(&rField), false, false, GetView().GetFrameWeld()); + break; + default: + SAL_WARN_IF(rField.IsClickable(), "sw", "unhandled clickable field!"); + } + + m_bIsInClickToEdit = false; +} + +void SwWrtShell::ClickToINetAttr( const SwFormatINetFormat& rItem, LoadUrlFlags nFilter ) +{ + if( rItem.GetValue().isEmpty() ) + return ; + + m_bIsInClickToEdit = true; + + // At first run the possibly set ObjectSelect Macro + const SvxMacro* pMac = rItem.GetMacro( SvMacroItemId::OnClick ); + if( pMac ) + { + SwCallMouseEvent aCallEvent; + aCallEvent.Set( &rItem ); + GetDoc()->CallEvent( SvMacroItemId::OnClick, aCallEvent ); + } + + // So that the implementation of templates is displayed immediately + ::LoadURL( *this, rItem.GetValue(), nFilter, rItem.GetTargetFrame() ); + const SwTextINetFormat* pTextAttr = rItem.GetTextINetFormat(); + if( pTextAttr ) + { + const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited( true ); + const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid( true ); + } + + m_bIsInClickToEdit = false; +} + +bool SwWrtShell::ClickToINetGrf( const Point& rDocPt, LoadUrlFlags nFilter ) +{ + bool bRet = false; + OUString sURL; + OUString sTargetFrameName; + const SwFrameFormat* pFnd = IsURLGrfAtPos( rDocPt, &sURL, &sTargetFrameName ); + if( pFnd && !sURL.isEmpty() ) + { + bRet = true; + // At first run the possibly set ObjectSelect Macro + SwCallMouseEvent aCallEvent; + aCallEvent.Set(EVENT_OBJECT_URLITEM, pFnd); + GetDoc()->CallEvent(SvMacroItemId::OnClick, aCallEvent); + + ::LoadURL(*this, sURL, nFilter, sTargetFrameName); + } + return bRet; +} + +void LoadURL( SwViewShell& rVSh, const OUString& rURL, LoadUrlFlags nFilter, + const OUString& rTargetFrameName ) +{ + OSL_ENSURE( !rURL.isEmpty(), "what should be loaded here?" ); + if( rURL.isEmpty() ) + return ; + + // The shell could be 0 also!!!!! + if ( dynamic_cast<const SwCursorShell*>( &rVSh) == nullptr ) + return; + + // We are doing tiledRendering, let the client handles the URL loading, + // unless we are jumping to a TOC mark. + if (comphelper::LibreOfficeKit::isActive() && !rURL.startsWith("#")) + { + rVSh.GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, rURL.toUtf8().getStr()); + return; + } + + //A CursorShell is always a WrtShell + SwWrtShell &rSh = static_cast<SwWrtShell&>(rVSh); + + SwDocShell* pDShell = rSh.GetView().GetDocShell(); + OSL_ENSURE( pDShell, "No DocShell?!"); + OUString sTargetFrame(rTargetFrameName); + if (sTargetFrame.isEmpty() && pDShell) + { + using namespace ::com::sun::star; + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + pDShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference<document::XDocumentProperties> xDocProps + = xDPS->getDocumentProperties(); + sTargetFrame = xDocProps->getDefaultTarget(); + } + + OUString sReferer; + if( pDShell && pDShell->GetMedium() ) + sReferer = pDShell->GetMedium()->GetName(); + SfxViewFrame* pViewFrame = rSh.GetView().GetViewFrame(); + SfxFrameItem aView( SID_DOCFRAME, pViewFrame ); + SfxStringItem aName( SID_FILE_NAME, rURL ); + SfxStringItem aTargetFrameName( SID_TARGETNAME, sTargetFrame ); + SfxStringItem aReferer( SID_REFERER, sReferer ); + + SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false ); + //#39076# Silent can be removed accordingly to SFX. + SfxBoolItem aBrowse( SID_BROWSE, true ); + + if ((nFilter & LoadUrlFlags::NewView) && !comphelper::LibreOfficeKit::isActive()) + aTargetFrameName.SetValue( "_blank" ); + + const SfxPoolItem* aArr[] = { + &aName, + &aNewView, /*&aSilent,*/ + &aReferer, + &aView, &aTargetFrameName, + &aBrowse, + nullptr + }; + + pViewFrame->GetDispatcher()->GetBindings()->Execute( SID_OPENDOC, aArr, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD ); +} + +void SwWrtShell::NavigatorPaste( const NaviContentBookmark& rBkmk, + const sal_uInt16 nAction ) +{ + if( EXCHG_IN_ACTION_COPY == nAction ) + { + // Insert + OUString sURL = rBkmk.GetURL(); + // Is this is a jump within the current Doc? + const SwDocShell* pDocShell = GetView().GetDocShell(); + if(pDocShell->HasName()) + { + const OUString rName = pDocShell->GetMedium()->GetURLObject().GetURLNoMark(); + + if (sURL.startsWith(rName)) + { + if (sURL.getLength()>rName.getLength()) + { + sURL = sURL.copy(rName.getLength()); + } + else + { + sURL.clear(); + } + } + } + SwFormatINetFormat aFormat( sURL, OUString() ); + InsertURL( aFormat, rBkmk.GetDescription() ); + } + else + { + SwSectionData aSection( SectionType::FileLink, GetUniqueSectionName() ); + OUString aLinkFile = rBkmk.GetURL().getToken(0, '#') + + OUStringChar(sfx2::cTokenSeparator) + + OUStringChar(sfx2::cTokenSeparator) + + rBkmk.GetURL().getToken(1, '#'); + aSection.SetLinkFileName( aLinkFile ); + aSection.SetProtectFlag( true ); + const SwSection* pIns = InsertSection( aSection ); + if( EXCHG_IN_ACTION_MOVE == nAction && pIns ) + { + aSection = SwSectionData(*pIns); + aSection.SetLinkFileName( OUString() ); + aSection.SetType( SectionType::Content ); + aSection.SetProtectFlag( false ); + + // the update of content from linked section at time delete + // the undostack. Then the change of the section don't create + // any undoobject. - BUG 69145 + bool bDoesUndo = DoesUndo(); + SwUndoId nLastUndoId(SwUndoId::EMPTY); + if (GetLastUndoInfo(nullptr, & nLastUndoId)) + { + if (SwUndoId::INSSECTION != nLastUndoId) + { + DoUndo(false); + } + } + UpdateSection( GetSectionFormatPos( *pIns->GetFormat() ), aSection ); + DoUndo( bDoesUndo ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx b/sw/source/uibase/wrtsh/wrtsh3.cxx new file mode 100644 index 000000000..1fd1ce2cd --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh3.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 <svx/svxids.hrc> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdview.hxx> +#include <svx/svdouno.hxx> +#include <svx/srchdlg.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <IMark.hxx> +#include <doc.hxx> + +using namespace ::com::sun::star; + +bool SwWrtShell::MoveBookMark( BookMarkMove eFuncId, const ::sw::mark::IMark* const pMark) +{ + addCurrentPosition(); + (this->*m_fnKillSel)( nullptr, false ); + + bool bRet = true; + switch(eFuncId) + { + case BOOKMARK_INDEX:bRet = SwCursorShell::GotoMark( pMark );break; + case BOOKMARK_NEXT: bRet = SwCursorShell::GoNextBookmark();break; + case BOOKMARK_PREV: bRet = SwCursorShell::GoPrevBookmark();break; + default:;//prevent warning + } + + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + return bRet; +} + +bool SwWrtShell::GotoField( const SwFormatField& rField ) +{ + (this->*m_fnKillSel)( nullptr, false ); + + bool bRet = SwCursorShell::GotoFormatField( rField ); + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + + return bRet; +} + +bool SwWrtShell::GotoFieldmark(::sw::mark::IFieldmark const * const pMark) +{ + (this->*m_fnKillSel)( nullptr, false ); + bool bRet = SwCursorShell::GotoFieldmark(pMark); + if( bRet && IsSelFrameMode() ) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + if( IsSelection() ) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + return bRet; +} + +// Invalidate FontWork-Slots + +void SwWrtShell::DrawSelChanged( ) +{ + static sal_uInt16 const aInval[] = + { + SID_ATTR_FILL_STYLE, SID_ATTR_FILL_COLOR, SID_ATTR_LINE_STYLE, + SID_ATTR_LINE_WIDTH, SID_ATTR_LINE_COLOR, + /*AF: these may be needed for the sidebar. + SID_SVX_AREA_TRANSPARENCY, SID_SVX_AREA_TRANSP_GRADIENT, + SID_SVX_AREA_TRANS_TYPE, + */ + 0 + }; + + GetView().GetViewFrame()->GetBindings().Invalidate(aInval); + + bool bOldVal = g_bNoInterrupt; + g_bNoInterrupt = true; // Trick to run AttrChangedNotify by timer. + GetView().AttrChangedNotify(nullptr); + g_bNoInterrupt = bOldVal; +} + +void SwWrtShell::GotoMark( const OUString& rName ) +{ + IDocumentMarkAccess::const_iterator_t ppMark = getIDocumentMarkAccess()->findMark( rName ); + if (ppMark == getIDocumentMarkAccess()->getAllMarksEnd()) + return; + MoveBookMark( BOOKMARK_INDEX, *ppMark ); +} + +void SwWrtShell::GotoMark( const ::sw::mark::IMark* const pMark ) +{ + MoveBookMark( BOOKMARK_INDEX, pMark ); +} + +bool SwWrtShell::GoNextBookmark() +{ + if ( !getIDocumentMarkAccess()->getBookmarksCount() ) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + LockView( true ); + bool bRet = MoveBookMark( BOOKMARK_NEXT ); + if ( !bRet ) + { + MoveBookMark( BOOKMARK_INDEX, *getIDocumentMarkAccess()->getBookmarksBegin() ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped ); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + LockView( false ); + ShowCursor(); + return true; +} + +bool SwWrtShell::GoPrevBookmark() +{ + if ( !getIDocumentMarkAccess()->getBookmarksCount() ) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + LockView( true ); + bool bRet = MoveBookMark( BOOKMARK_PREV ); + if ( !bRet ) + { + MoveBookMark( BOOKMARK_INDEX, *( getIDocumentMarkAccess()->getBookmarksEnd() - 1 ) ); + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped ); + } + else + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + LockView( false ); + ShowCursor(); + return true; +} + +void SwWrtShell::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs ) +{ + // execute macro, if it is allowed. + if ( IsMacroExecAllowed() ) + { + GetDoc()->ExecMacro( rMacro, pRet, pArgs ); + } +} + +sal_uInt16 SwWrtShell::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent, + bool bChkPtr) +{ + return GetDoc()->CallEvent( nEvent, rCallEvent, bChkPtr ); +} + + // If a util::URL-Button is selected, return its util::URL + // otherwise an empty string. +bool SwWrtShell::GetURLFromButton( OUString& rURL, OUString& rDescr ) const +{ + bool bRet = false; + const SdrView *pDView = GetDrawView(); + if( pDView ) + { + // A fly is precisely achievable if it is selected. + const SdrMarkList &rMarkList = pDView->GetMarkedObjectList(); + + if (rMarkList.GetMark(0)) + { + SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference< awt::XControlModel >& xControlModel = pUnoCtrl->GetUnoControlModel(); + + OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" ); + if( !xControlModel.is() ) + return bRet; + + uno::Reference< beans::XPropertySet > xPropSet(xControlModel, uno::UNO_QUERY); + + uno::Any aTmp; + + uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo(); + if(xInfo->hasPropertyByName( "ButtonType" )) + { + aTmp = xPropSet->getPropertyValue( "ButtonType" ); + form::FormButtonType eTmpButtonType; + aTmp >>= eTmpButtonType; + if( form::FormButtonType_URL == eTmpButtonType) + { + // Label + aTmp = xPropSet->getPropertyValue( "Label" ); + OUString uTmp; + if( (aTmp >>= uTmp) && !uTmp.isEmpty()) + { + rDescr = uTmp; + } + + // util::URL + aTmp = xPropSet->getPropertyValue( "TargetURL" ); + if( (aTmp >>= uTmp) && !uTmp.isEmpty()) + { + rURL = uTmp; + } + bRet = true; + } + } + } + } + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtsh4.cxx b/sw/source/uibase/wrtsh/wrtsh4.cxx new file mode 100644 index 000000000..8009ce980 --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtsh4.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 <wrtsh.hxx> + +// Private methods, which move the cursor over search. +// The removal of the selection must be made on the level above. + +// The beginning of a word is the follow of a +// non-delimiter to delimiter. Furthermore, the follow of +// non-sentence separators on sentence separator. +// The begin of paragraph is also the word beginning. + +void SwWrtShell::SttWrd() +{ + if ( IsSttPara() ) + return; + // Create temporary cursor without selection. + Push(); + ClearMark(); + if( !GoStartWord() ) + // not found --> go to the beginning of the paragraph. + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + ClearMark(); + // If Mark was previously set, summarize. + Combine(); +} + +// The end of a word is the follow of separator to nonseparator. +// The end of a word is also the sequence of word separators to +// punctuation marks. +// The end of a paragraph is also the end of a word. + +void SwWrtShell::EndWrd() +{ + if ( IsEndWrd() ) + return; + // Create temporary cursor without selection. + Push(); + ClearMark(); + if( !GoEndWord() ) + // not found --> go to the end of the paragraph. + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + ClearMark(); + // If Mark was previously set, summarize. + Combine(); +} + +bool SwWrtShell::NxtWrd_() +{ + bool bRet = false; + while( IsEndPara() ) // If already at the end, then the next??? + { + if(!SwCursorShell::Right(1,CRSR_SKIP_CHARS)) // Document - end ?? + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return bRet; + } + bRet = IsStartWord(); + } + Push(); + ClearMark(); + while( !bRet ) + { + if( !GoNextWord() ) + { + if( (!IsEndPara() && !SwCursorShell::MovePara( GoCurrPara, fnParaEnd ) ) + || !SwCursorShell::Right(1,CRSR_SKIP_CHARS) ) + break; + bRet = IsStartWord(); + } + else + bRet = true; + } + ClearMark(); + Combine(); + return bRet; +} + +bool SwWrtShell::PrvWrd_() +{ + bool bRet = false; + while( IsSttPara() ) + { // if already at the beginning, then the next??? + if(!SwCursorShell::Left(1,CRSR_SKIP_CHARS)) + { // Document - beginning ?? + Pop(SwCursorShell::PopMode::DeleteCurrent); + return bRet; + } + bRet = IsStartWord() || IsEndPara(); + } + Push(); + ClearMark(); + while( !bRet ) + { + if( !GoPrevWord() ) + { + if( (!IsSttPara() && !SwCursorShell::MovePara( GoCurrPara, fnParaStart ) ) + || !SwCursorShell::Left(1,CRSR_SKIP_CHARS) ) + break; + bRet = IsStartWord(); + } + else + bRet = true; + } + ClearMark(); + Combine(); + return bRet; +} + +// #i92468# +// method code of <SwWrtShell::NxtWrd_()> before fix for issue i72162 +bool SwWrtShell::NxtWrdForDelete() +{ + if ( IsEndPara() ) + { + if ( !SwCursorShell::Right(1,CRSR_SKIP_CHARS) ) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + return true; + } + Push(); + ClearMark(); + if ( !GoNextWord() ) + { + SwCursorShell::MovePara( GoCurrPara, fnParaEnd ); + } + ClearMark(); + Combine(); + return true; +} + +// method code of <SwWrtShell::PrvWrd_()> before fix for issue i72162 +bool SwWrtShell::PrvWrdForDelete() +{ + if ( IsSttPara() ) + { + if ( !SwCursorShell::Left(1,CRSR_SKIP_CHARS) ) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + return true; + } + Push(); + ClearMark(); + if( !GoPrevWord() ) + { + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + } + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::FwdSentence_() +{ + Push(); + ClearMark(); + if(!SwCursorShell::Right(1,CRSR_SKIP_CHARS)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + if( !GoNextSentence() && !IsEndPara() ) + SwCursorShell::MovePara(GoCurrPara, fnParaEnd); + + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::BwdSentence_() +{ + Push(); + ClearMark(); + if(!SwCursorShell::Left(1,CRSR_SKIP_CHARS)) + { + Pop(SwCursorShell::PopMode::DeleteCurrent); + return false; + } + if( !GoStartSentence() && !IsSttPara() ) + // not found --> go to the beginning of the paragraph + SwCursorShell::MovePara( GoCurrPara, fnParaStart ); + ClearMark(); + Combine(); + return true; +} + +bool SwWrtShell::FwdPara_() +{ + Push(); + ClearMark(); + bool bRet = SwCursorShell::MovePara(GoNextPara, fnParaStart); + + ClearMark(); + Combine(); + return bRet; +} + +bool SwWrtShell::BwdPara_() +{ + Push(); + ClearMark(); + + bool bRet = SwCursorShell::MovePara(GoPrevPara, fnParaStart); + if ( !bRet && !IsSttOfPara() ) + { + SttPara(); + } + + ClearMark(); + Combine(); + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/wrtsh/wrtundo.cxx b/sw/source/uibase/wrtsh/wrtundo.cxx new file mode 100644 index 000000000..1e92006a2 --- /dev/null +++ b/sw/source/uibase/wrtsh/wrtundo.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 <svl/slstitm.hxx> +#include <rtl/ustrbuf.hxx> +#include <wrtsh.hxx> +#include <swundo.hxx> +#include <IDocumentUndoRedo.hxx> +#include <swdtflvr.hxx> +#include <svtools/svtresid.hxx> +#include <svtools/strings.hrc> + +// Undo ends all modes. If a selection is emerged by the Undo, +// this must be considered for further action. + +void SwWrtShell::Do( DoType eDoType, sal_uInt16 nCnt ) +{ + // #105332# save current state of DoesUndo() + bool bSaveDoesUndo = DoesUndo(); + + StartAllAction(); + switch (eDoType) + { + case UNDO: + DoUndo(false); // #i21739# + // Reset modes + EnterStdMode(); + SwEditShell::Undo(nCnt); + break; + case REDO: + DoUndo(false); // #i21739# + // Reset modes + EnterStdMode(); + SwEditShell::Redo( nCnt ); + break; + case REPEAT: + // #i21739# do not touch undo flag here !!! + SwEditShell::Repeat( nCnt ); + break; + } + EndAllAction(); + // #105332# restore undo state + DoUndo(bSaveDoesUndo); + + bool bCreateXSelection = false; + const bool bFrameSelected = IsFrameSelected() || IsObjSelected(); + if ( IsSelection() ) + { + if ( bFrameSelected ) + UnSelectFrame(); + + // Set the function pointer for canceling the selection at the + // cursor position. + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + bCreateXSelection = true; + } + else if ( bFrameSelected ) + { + EnterSelFrameMode(); + bCreateXSelection = true; + } + else if( (CNT_GRF | CNT_OLE ) & GetCntType() ) + { + SelectObj( GetCharRect().Pos() ); + EnterSelFrameMode(); + bCreateXSelection = true; + } + + if( bCreateXSelection ) + SwTransferable::CreateSelection( *this ); + + // Bug 32918: After deleting of the numbering the object panel remains. + // Why is not here always a CallChgLink called? + CallChgLnk(); +} + +OUString SwWrtShell::GetDoString( DoType eDoType ) const +{ + OUString aUndoStr; + const char* pResStr = STR_UNDO; + switch( eDoType ) + { + case UNDO: + pResStr = STR_UNDO; + (void)GetLastUndoInfo(&aUndoStr, nullptr, &m_rView); + break; + case REDO: + pResStr = STR_REDO; + (void)GetFirstRedoInfo(&aUndoStr, nullptr, &m_rView); + break; + default:;//prevent warning + } + + return SvtResId(pResStr) + aUndoStr; +} + +void SwWrtShell::GetDoStrings( DoType eDoType, SfxStringListItem& rStrs ) const +{ + SwUndoComments_t comments; + switch( eDoType ) + { + case UNDO: + comments = GetIDocumentUndoRedo().GetUndoComments(); + break; + case REDO: + comments = GetIDocumentUndoRedo().GetRedoComments(); + break; + default:;//prevent warning + } + + OUStringBuffer buf; + for (const OUString & comment : comments) + { + OSL_ENSURE(!comment.isEmpty(), "no Undo/Redo Text set"); + buf.append(comment).append("\n"); + } + rStrs.SetString(buf.makeStringAndClear()); +} + +OUString SwWrtShell::GetRepeatString() const +{ + OUString str; + GetRepeatInfo(& str); + + if (str.isEmpty()) + { + return str; + } + + return SvtResId(STR_REPEAT) + str; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3